summaryrefslogtreecommitdiffstats
path: root/src/pulsecore
diff options
context:
space:
mode:
Diffstat (limited to 'src/pulsecore')
-rw-r--r--src/pulsecore/aupdate.c129
-rw-r--r--src/pulsecore/aupdate.h98
-rw-r--r--src/pulsecore/card.c7
-rw-r--r--src/pulsecore/card.h2
-rw-r--r--src/pulsecore/cli-command.c10
-rw-r--r--src/pulsecore/cli-text.c10
-rw-r--r--src/pulsecore/cli.c13
-rw-r--r--src/pulsecore/core-scache.c30
-rw-r--r--src/pulsecore/core-util.c96
-rw-r--r--src/pulsecore/core-util.h5
-rw-r--r--src/pulsecore/core.h10
-rw-r--r--src/pulsecore/database-gdbm.c246
-rw-r--r--src/pulsecore/database-tdb.c227
-rw-r--r--src/pulsecore/database.h61
-rw-r--r--src/pulsecore/endianmacros.h8
-rw-r--r--src/pulsecore/hashmap.h4
-rw-r--r--src/pulsecore/idxset.h4
-rw-r--r--src/pulsecore/ioline.c48
-rw-r--r--src/pulsecore/ioline.h11
-rw-r--r--src/pulsecore/ipacl.h4
-rw-r--r--src/pulsecore/ltdl-helper.h1
-rw-r--r--src/pulsecore/macro.h15
-rw-r--r--src/pulsecore/memtrap.c129
-rw-r--r--src/pulsecore/memtrap.h2
-rw-r--r--src/pulsecore/mime-type.c182
-rw-r--r--src/pulsecore/mime-type.h37
-rw-r--r--src/pulsecore/modargs.c73
-rw-r--r--src/pulsecore/modargs.h2
-rw-r--r--src/pulsecore/modinfo.c5
-rw-r--r--src/pulsecore/modinfo.h1
-rw-r--r--src/pulsecore/module.c9
-rw-r--r--src/pulsecore/module.h4
-rw-r--r--src/pulsecore/parseaddr.c16
-rw-r--r--src/pulsecore/parseaddr.h8
-rw-r--r--src/pulsecore/proplist-util.c20
-rw-r--r--src/pulsecore/protocol-esound.c4
-rw-r--r--src/pulsecore/protocol-http.c681
-rw-r--r--src/pulsecore/protocol-http.h6
-rw-r--r--src/pulsecore/protocol-native.c20
-rw-r--r--src/pulsecore/protocol-simple.c2
-rw-r--r--src/pulsecore/sample-util.c12
-rw-r--r--src/pulsecore/sconv-s16le.c10
-rw-r--r--src/pulsecore/sconv.c2
-rw-r--r--src/pulsecore/shm.c52
-rw-r--r--src/pulsecore/sink-input.c2
-rw-r--r--src/pulsecore/sink.c204
-rw-r--r--src/pulsecore/sink.h6
-rw-r--r--src/pulsecore/sndfile-util.c462
-rw-r--r--src/pulsecore/sndfile-util.h52
-rw-r--r--src/pulsecore/sound-file-stream.c51
-rw-r--r--src/pulsecore/sound-file.c102
-rw-r--r--src/pulsecore/sound-file.h2
-rw-r--r--src/pulsecore/source.c68
-rw-r--r--src/pulsecore/source.h6
-rw-r--r--src/pulsecore/strbuf.c7
-rw-r--r--src/pulsecore/strbuf.h1
-rw-r--r--src/pulsecore/strlist.c12
-rw-r--r--src/pulsecore/strlist.h6
58 files changed, 2826 insertions, 471 deletions
diff --git a/src/pulsecore/aupdate.c b/src/pulsecore/aupdate.c
new file mode 100644
index 00000000..56ebb8e5
--- /dev/null
+++ b/src/pulsecore/aupdate.c
@@ -0,0 +1,129 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2009 Lennart Poettering
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/semaphore.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/mutex.h>
+
+#include "aupdate.h"
+
+#define MSB (1U << (sizeof(unsigned)*8U-1))
+#define WHICH(n) (!!((n) & MSB))
+#define COUNTER(n) ((n) & ~MSB)
+
+struct pa_aupdate {
+ pa_atomic_t read_lock;
+ pa_mutex *write_lock;
+ pa_semaphore *semaphore;
+};
+
+pa_aupdate *pa_aupdate_new(void) {
+ pa_aupdate *a;
+
+ a = pa_xnew(pa_aupdate, 1);
+ pa_atomic_store(&a->read_lock, 0);
+ a->write_lock = pa_mutex_new(FALSE, FALSE);
+ a->semaphore = pa_semaphore_new(0);
+
+ return a;
+}
+
+void pa_aupdate_free(pa_aupdate *a) {
+ pa_assert(a);
+
+ pa_mutex_free(a->write_lock);
+ pa_semaphore_free(a->semaphore);
+
+ pa_xfree(a);
+}
+
+unsigned pa_aupdate_read_begin(pa_aupdate *a) {
+ unsigned n;
+
+ pa_assert(a);
+
+ /* Increase the lock counter */
+ n = (unsigned) pa_atomic_inc(&a->read_lock);
+
+ /* When n is 0 we have about 2^31 threads running that all try to
+ * access the data at the same time, oh my! */
+ pa_assert(COUNTER(n)+1 > 0);
+
+ /* The uppermost bit tells us which data to look at */
+ return WHICH(n);
+}
+
+void pa_aupdate_read_end(pa_aupdate *a) {
+ unsigned n;
+
+ pa_assert(a);
+
+ /* Decrease the lock counter */
+ n = (unsigned) pa_atomic_dec(&a->read_lock);
+
+ /* Make sure the counter was valid */
+ pa_assert(COUNTER(n) > 0);
+
+ /* Post the semaphore */
+ pa_semaphore_post(a->semaphore);
+}
+
+unsigned pa_aupdate_write_begin(pa_aupdate *a) {
+ unsigned n;
+
+ pa_assert(a);
+
+ pa_mutex_lock(a->write_lock);
+
+ n = (unsigned) pa_atomic_load(&a->read_lock);
+
+ return !WHICH(n);
+}
+
+unsigned pa_aupdate_write_swap(pa_aupdate *a) {
+ unsigned n;
+
+ pa_assert(a);
+
+ for (;;) {
+ n = (unsigned) pa_atomic_load(&a->read_lock);
+
+ /* If the read counter is > 0 wait; if it is 0 try to swap the lists */
+ if (COUNTER(n) > 0)
+ pa_semaphore_wait(a->semaphore);
+ else if (pa_atomic_cmpxchg(&a->read_lock, (int) n, (int) (n ^ MSB)))
+ break;
+ }
+
+ return WHICH(n);
+}
+
+void pa_aupdate_write_end(pa_aupdate *a) {
+ pa_assert(a);
+
+ pa_mutex_unlock(a->write_lock);
+}
diff --git a/src/pulsecore/aupdate.h b/src/pulsecore/aupdate.h
new file mode 100644
index 00000000..072e382d
--- /dev/null
+++ b/src/pulsecore/aupdate.h
@@ -0,0 +1,98 @@
+#ifndef foopulsecoreaupdatehfoo
+#define foopulsecoreaupdatehfoo
+
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2009 Lennart Poettering
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+typedef struct pa_aupdate pa_aupdate;
+
+pa_aupdate *pa_aupdate_new(void);
+void pa_aupdate_free(pa_aupdate *a);
+
+/* Will return 0, or 1, depending on which copy of the data the caller
+ * should look at */
+unsigned pa_aupdate_read_begin(pa_aupdate *a);
+void pa_aupdate_read_end(pa_aupdate *a);
+
+/* Will return 0, or 1, depending which copy of the data the caller
+ * should modify */
+unsigned pa_aupdate_write_begin(pa_aupdate *a);
+void pa_aupdate_write_end(pa_aupdate *a);
+
+/* Will return 0, or 1, depending which copy of the data the caller
+ * should modify. Each time called this will return the opposite of
+ * the previous pa_aupdate_write_begin()/pa_aupdate_write_swap()
+ * call. Should only be called between pa_aupdate_write_begin() and
+ * pa_aupdate_write_end() */
+unsigned pa_aupdate_write_swap(pa_aupdate *a);
+
+/*
+ * This infrastructure allows lock-free updates of arbitrary data
+ * structures in an rcu'ish way: two copies of the data structure
+ * should be exisiting. One side ('the reader') has read access to one
+ * of the two data structure at a time. It does not have to lock it,
+ * however it needs to signal that it is using it/stopped using
+ * it. The other side ('the writer') modifes the second data structure,
+ * and then atomically swaps the two data structures, followed by a
+ * modification of the other one.
+ *
+ * This is intended to be used for cases where the reader side needs
+ * to be fast while the writer side can be slow.
+ *
+ * The reader side is signal handler safe.
+ *
+ * The writer side lock is not recursive. The reader side is.
+ *
+ * There may be multiple readers and multiple writers at the same
+ * time.
+ *
+ * Usage is like this:
+ *
+ * static struct foo bar[2];
+ * static pa_aupdate *a;
+ *
+ * reader() {
+ * unsigned j;
+ *
+ * j = pa_update_read_begin(a);
+ *
+ * ... read the data structure bar[j] ...
+ *
+ * pa_update_read_end(a);
+ * }
+ *
+ * writer() {
+ * unsigned j;
+ *
+ * j = pa_update_write_begin(a);
+ *
+ * ... update the data structure bar[j] ...
+ *
+ * j = pa_update_write_swap(a);
+ *
+ * ... update the data structure bar[j], the same way as above ...
+ *
+ * pa_update_write_end(a)
+ * }
+ *
+ */
+
+#endif
diff --git a/src/pulsecore/card.c b/src/pulsecore/card.c
index 8101a92e..59b8cda6 100644
--- a/src/pulsecore/card.c
+++ b/src/pulsecore/card.c
@@ -244,19 +244,20 @@ int pa_card_set_profile(pa_card *c, const char *name, pa_bool_t save) {
return 0;
}
-int pa_card_suspend(pa_card *c, pa_bool_t suspend) {
+int pa_card_suspend(pa_card *c, pa_bool_t suspend, pa_suspend_cause_t cause) {
pa_sink *sink;
pa_source *source;
uint32_t idx;
int ret = 0;
pa_assert(c);
+ pa_assert(cause != 0);
for (sink = pa_idxset_first(c->sinks, &idx); sink; sink = pa_idxset_next(c->sinks, &idx))
- ret -= pa_sink_suspend(sink, suspend) < 0;
+ ret -= pa_sink_suspend(sink, suspend, cause) < 0;
for (source = pa_idxset_first(c->sources, &idx); source; source = pa_idxset_next(c->sources, &idx))
- ret -= pa_source_suspend(source, suspend) < 0;
+ ret -= pa_source_suspend(source, suspend, cause) < 0;
return ret;
}
diff --git a/src/pulsecore/card.h b/src/pulsecore/card.h
index 3b7608f6..415ab678 100644
--- a/src/pulsecore/card.h
+++ b/src/pulsecore/card.h
@@ -99,6 +99,6 @@ void pa_card_free(pa_card *c);
int pa_card_set_profile(pa_card *c, const char *name, pa_bool_t save);
-int pa_card_suspend(pa_card *c, pa_bool_t suspend);
+int pa_card_suspend(pa_card *c, pa_bool_t suspend, pa_suspend_cause_t cause);
#endif
diff --git a/src/pulsecore/cli-command.c b/src/pulsecore/cli-command.c
index 15fe525c..644de96e 100644
--- a/src/pulsecore/cli-command.c
+++ b/src/pulsecore/cli-command.c
@@ -483,6 +483,8 @@ static int pa_cli_command_describe(pa_core *c, pa_tokenizer *t, pa_strbuf *buf,
if (i->usage)
pa_strbuf_printf(buf, "Usage: %s\n", i->usage);
pa_strbuf_printf(buf, "Load Once: %s\n", pa_yes_no(i->load_once));
+ if (i->deprecated)
+ pa_strbuf_printf(buf, "Warning, deprecated: %s\n", i->deprecated);
}
pa_modinfo_free(i);
@@ -1276,7 +1278,7 @@ static int pa_cli_command_suspend_sink(pa_core *c, pa_tokenizer *t, pa_strbuf *b
return -1;
}
- if ((r = pa_sink_suspend(sink, suspend)) < 0)
+ if ((r = pa_sink_suspend(sink, suspend, PA_SUSPEND_USER)) < 0)
pa_strbuf_printf(buf, "Failed to resume/suspend sink: %s\n", pa_strerror(r));
return 0;
@@ -1312,7 +1314,7 @@ static int pa_cli_command_suspend_source(pa_core *c, pa_tokenizer *t, pa_strbuf
return -1;
}
- if ((r = pa_source_suspend(source, suspend)) < 0)
+ if ((r = pa_source_suspend(source, suspend, PA_SUSPEND_USER)) < 0)
pa_strbuf_printf(buf, "Failed to resume/suspend source: %s\n", pa_strerror(r));
return 0;
@@ -1337,10 +1339,10 @@ static int pa_cli_command_suspend(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, p
return -1;
}
- if ((r = pa_sink_suspend_all(c, suspend)) < 0)
+ if ((r = pa_sink_suspend_all(c, suspend, PA_SUSPEND_USER)) < 0)
pa_strbuf_printf(buf, "Failed to resume/suspend all sinks: %s\n", pa_strerror(r));
- if ((r = pa_source_suspend_all(c, suspend)) < 0)
+ if ((r = pa_source_suspend_all(c, suspend, PA_SUSPEND_USER)) < 0)
pa_strbuf_printf(buf, "Failed to resume/suspend all sources: %s\n", pa_strerror(r));
return 0;
diff --git a/src/pulsecore/cli-text.c b/src/pulsecore/cli-text.c
index 604678be..bc863f05 100644
--- a/src/pulsecore/cli-text.c
+++ b/src/pulsecore/cli-text.c
@@ -232,6 +232,7 @@ char *pa_sink_list_to_string(pa_core *c) {
"\tdriver: <%s>\n"
"\tflags: %s%s%s%s%s%s%s%s\n"
"\tstate: %s\n"
+ "\tsuspend cause: %s%s%s%s\n"
"\tvolume: %s%s%s\n"
"\t balance %0.2f\n"
"\tbase volume: %s%s%s\n"
@@ -258,6 +259,10 @@ char *pa_sink_list_to_string(pa_core *c) {
sink->flags & PA_SINK_FLAT_VOLUME ? "FLAT_VOLUME " : "",
sink->flags & PA_SINK_DYNAMIC_LATENCY ? "DYNAMIC_LATENCY" : "",
sink_state_to_string(pa_sink_get_state(sink)),
+ sink->suspend_cause & PA_SUSPEND_USER ? "USER " : "",
+ sink->suspend_cause & PA_SUSPEND_APPLICATION ? "APPLICATION " : "",
+ sink->suspend_cause & PA_SUSPEND_IDLE ? "IDLE " : "",
+ sink->suspend_cause & PA_SUSPEND_SESSION ? "SESSION" : "",
pa_cvolume_snprint(cv, sizeof(cv), pa_sink_get_volume(sink, FALSE, FALSE)),
sink->flags & PA_SINK_DECIBEL_VOLUME ? "\n\t " : "",
sink->flags & PA_SINK_DECIBEL_VOLUME ? pa_sw_cvolume_snprint_dB(cvdb, sizeof(cvdb), pa_sink_get_volume(sink, FALSE, FALSE)) : "",
@@ -335,6 +340,7 @@ char *pa_source_list_to_string(pa_core *c) {
"\tdriver: <%s>\n"
"\tflags: %s%s%s%s%s%s%s\n"
"\tstate: %s\n"
+ "\tsuspend cause: %s%s%s%s\n"
"\tvolume: %s%s%s\n"
"\t balance %0.2f\n"
"\tbase volume: %s%s%s\n"
@@ -358,6 +364,10 @@ char *pa_source_list_to_string(pa_core *c) {
source->flags & PA_SOURCE_LATENCY ? "LATENCY " : "",
source->flags & PA_SOURCE_DYNAMIC_LATENCY ? "DYNAMIC_LATENCY" : "",
source_state_to_string(pa_source_get_state(source)),
+ source->suspend_cause & PA_SUSPEND_USER ? "USER " : "",
+ source->suspend_cause & PA_SUSPEND_APPLICATION ? "APPLICATION " : "",
+ source->suspend_cause & PA_SUSPEND_IDLE ? "IDLE " : "",
+ source->suspend_cause & PA_SUSPEND_SESSION ? "SESSION" : "",
pa_cvolume_snprint(cv, sizeof(cv), pa_source_get_volume(source, FALSE)),
source->flags & PA_SOURCE_DECIBEL_VOLUME ? "\n\t " : "",
source->flags & PA_SOURCE_DECIBEL_VOLUME ? pa_sw_cvolume_snprint_dB(cvdb, sizeof(cvdb), pa_source_get_volume(source, FALSE)) : "",
diff --git a/src/pulsecore/cli.c b/src/pulsecore/cli.c
index a784f583..54514e7f 100644
--- a/src/pulsecore/cli.c
+++ b/src/pulsecore/cli.c
@@ -59,6 +59,8 @@ struct pa_cli {
pa_bool_t fail, kill_requested;
int defer_kill;
+
+ char *last_line;
};
static void line_callback(pa_ioline *line, const char *s, void *userdata);
@@ -101,6 +103,8 @@ pa_cli* pa_cli_new(pa_core *core, pa_iochannel *io, pa_module *m) {
c->fail = c->kill_requested = FALSE;
c->defer_kill = 0;
+ c->last_line = NULL;
+
return c;
}
@@ -110,6 +114,7 @@ void pa_cli_free(pa_cli *c) {
pa_ioline_close(c->line);
pa_ioline_unref(c->line);
pa_client_free(c->client);
+ pa_xfree(c->last_line);
pa_xfree(c);
}
@@ -144,6 +149,14 @@ static void line_callback(pa_ioline *line, const char *s, void *userdata) {
return;
}
+ /* Magic command, like they had in AT Hayes Modems! Those were the good days! */
+ if (pa_streq(s, "/"))
+ s = c->last_line;
+ else if (s[0]) {
+ pa_xfree(c->last_line);
+ c->last_line = pa_xstrdup(s);
+ }
+
pa_assert_se(buf = pa_strbuf_new());
c->defer_kill++;
pa_cli_command_execute_line(c->core, s, buf, &c->fail);
diff --git a/src/pulsecore/core-scache.c b/src/pulsecore/core-scache.c
index 34d60a8f..086f5fcb 100644
--- a/src/pulsecore/core-scache.c
+++ b/src/pulsecore/core-scache.c
@@ -219,11 +219,14 @@ int pa_scache_add_file(pa_core *c, const char *name, const char *filename, uint3
pa_assert(name);
pa_assert(filename);
- if (pa_sound_file_load(c->mempool, filename, &ss, &map, &chunk) < 0)
- return -1;
-
p = pa_proplist_new();
pa_proplist_sets(p, PA_PROP_MEDIA_FILENAME, filename);
+
+ if (pa_sound_file_load(c->mempool, filename, &ss, &map, &chunk, p) < 0) {
+ pa_proplist_free(p);
+ return -1;
+ }
+
r = pa_scache_add_item(c, name, &ss, &map, &chunk, p, idx);
pa_memblock_unref(chunk.memblock);
pa_proplist_free(p);
@@ -311,11 +314,14 @@ int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, pa_volume_t
if (!(e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE)))
return -1;
+ merged = pa_proplist_new();
+ pa_proplist_setf(merged, PA_PROP_MEDIA_NAME, "Sample %s", name);
+
if (e->lazy && !e->memchunk.memblock) {
pa_channel_map old_channel_map = e->channel_map;
- if (pa_sound_file_load(c->mempool, e->filename, &e->sample_spec, &e->channel_map, &e->memchunk) < 0)
- return -1;
+ if (pa_sound_file_load(c->mempool, e->filename, &e->sample_spec, &e->channel_map, &e->memchunk, merged) < 0)
+ goto fail;
pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE|PA_SUBSCRIPTION_EVENT_CHANGE, e->index);
@@ -328,7 +334,7 @@ int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, pa_volume_t
}
if (!e->memchunk.memblock)
- return -1;
+ goto fail;
pa_log_debug("Playing sample \"%s\" on \"%s\"", name, sink->name);
@@ -344,17 +350,13 @@ int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, pa_volume_t
else
pass_volume = FALSE;
- merged = pa_proplist_new();
- pa_proplist_setf(merged, PA_PROP_MEDIA_NAME, "Sample %s", name);
pa_proplist_update(merged, PA_UPDATE_REPLACE, e->proplist);
if (p)
pa_proplist_update(merged, PA_UPDATE_REPLACE, p);
- if (pa_play_memchunk(sink, &e->sample_spec, &e->channel_map, &e->memchunk, pass_volume ? &r : NULL, merged, sink_input_idx) < 0) {
- pa_proplist_free(merged);
- return -1;
- }
+ if (pa_play_memchunk(sink, &e->sample_spec, &e->channel_map, &e->memchunk, pass_volume ? &r : NULL, merged, sink_input_idx) < 0)
+ goto fail;
pa_proplist_free(merged);
@@ -362,6 +364,10 @@ int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, pa_volume_t
time(&e->last_used_time);
return 0;
+
+fail:
+ pa_proplist_free(merged);
+ return -1;
}
int pa_scache_play_item_by_name(pa_core *c, const char *name, const char*sink_name, pa_volume_t volume, pa_proplist *p, uint32_t *sink_input_idx) {
diff --git a/src/pulsecore/core-util.c b/src/pulsecore/core-util.c
index 294f63cb..b747cd84 100644
--- a/src/pulsecore/core-util.c
+++ b/src/pulsecore/core-util.c
@@ -2235,7 +2235,7 @@ int pa_close_all(int except_fd, ...) {
int pa_close_allv(const int except_fds[]) {
struct rlimit rl;
- int fd;
+ int maxfd, fd;
int saved_errno;
#ifdef __linux__
@@ -2302,10 +2302,12 @@ int pa_close_allv(const int except_fds[]) {
#endif
- if (getrlimit(RLIMIT_NOFILE, &rl) < 0)
- return -1;
+ if (getrlimit(RLIMIT_NOFILE, &rl) >= 0)
+ maxfd = (int) rl.rlim_max;
+ else
+ maxfd = sysconf(_SC_OPEN_MAX);
- for (fd = 3; fd < (int) rl.rlim_max; fd++) {
+ for (fd = 3; fd < maxfd; fd++) {
int i;
pa_bool_t found;
@@ -2467,31 +2469,29 @@ pa_bool_t pa_in_system_mode(void) {
return !!atoi(e);
}
-char *pa_machine_id(void) {
- FILE *f;
- size_t l;
+char *pa_get_user_name_malloc(void) {
+ ssize_t k;
+ char *u;
- /* The returned value is supposed be some kind of ascii identifier
- * that is unique and stable across reboots. */
+#ifdef _SC_LOGIN_NAME_MAX
+ k = (ssize_t) sysconf(_SC_LOGIN_NAME_MAX);
- /* First we try the D-Bus UUID, which is the best option we have,
- * since it fits perfectly our needs and is not as volatile as the
- * hostname which might be set from dhcp. */
-
- if ((f = fopen(PA_MACHINE_ID, "r"))) {
- char ln[34] = "", *r;
+ if (k <= 0)
+#endif
+ k = 32;
- r = fgets(ln, sizeof(ln)-1, f);
- fclose(f);
+ u = pa_xnew(char, k+1);
- pa_strip_nl(ln);
-
- if (r && ln[0])
- return pa_utf8_filter(ln);
+ if (!(pa_get_user_name(u, k))) {
+ pa_xfree(u);
+ return NULL;
}
- /* The we fall back to the host name. It supposed to be somewhat
- * unique, at least in a network, but may change. */
+ return u;
+}
+
+char *pa_get_host_name_malloc(void) {
+ size_t l;
l = 100;
for (;;) {
@@ -2525,6 +2525,35 @@ char *pa_machine_id(void) {
l *= 2;
}
+ return NULL;
+}
+
+char *pa_machine_id(void) {
+ FILE *f;
+ char *h;
+
+ /* The returned value is supposed be some kind of ascii identifier
+ * that is unique and stable across reboots. */
+
+ /* First we try the D-Bus UUID, which is the best option we have,
+ * since it fits perfectly our needs and is not as volatile as the
+ * hostname which might be set from dhcp. */
+
+ if ((f = fopen(PA_MACHINE_ID, "r"))) {
+ char ln[34] = "", *r;
+
+ r = fgets(ln, sizeof(ln)-1, f);
+ fclose(f);
+
+ pa_strip_nl(ln);
+
+ if (r && ln[0])
+ return pa_utf8_filter(ln);
+ }
+
+ if ((h = pa_get_host_name_malloc()))
+ return h;
+
/* If no hostname was set we use the POSIX hostid. It's usually
* the IPv4 address. Might not be that stable. */
return pa_sprintf_malloc("%08lx", (unsigned long) gethostid);
@@ -2682,3 +2711,24 @@ char *pa_realpath(const char *path) {
return t;
}
+
+void pa_disable_sigpipe(void) {
+
+#ifdef SIGPIPE
+ struct sigaction sa;
+
+ pa_zero(sa);
+
+ if (sigaction(SIGPIPE, NULL, &sa) < 0) {
+ pa_log("sigaction(): %s", pa_cstrerror(errno));
+ return;
+ }
+
+ sa.sa_handler = SIG_IGN;
+
+ if (sigaction(SIGPIPE, &sa, NULL) < 0) {
+ pa_log("sigaction(): %s", pa_cstrerror(errno));
+ return;
+ }
+#endif
+}
diff --git a/src/pulsecore/core-util.h b/src/pulsecore/core-util.h
index f96fa443..d073b750 100644
--- a/src/pulsecore/core-util.h
+++ b/src/pulsecore/core-util.h
@@ -201,6 +201,9 @@ pa_bool_t pa_in_system_mode(void);
#define pa_streq(a,b) (!strcmp((a),(b)))
+char *pa_get_host_name_malloc(void);
+char *pa_get_user_name_malloc(void);
+
char *pa_machine_id(void);
char *pa_session_id(void);
char *pa_uname_string(void);
@@ -224,4 +227,6 @@ char *pa_unescape(char *p);
char *pa_realpath(const char *path);
+void pa_disable_sigpipe(void);
+
#endif
diff --git a/src/pulsecore/core.h b/src/pulsecore/core.h
index c6794445..09a880c4 100644
--- a/src/pulsecore/core.h
+++ b/src/pulsecore/core.h
@@ -27,6 +27,16 @@
typedef struct pa_core pa_core;
+/* This is a bitmask that encodes the cause why a sink/source is
+ * suspended. */
+typedef enum pa_suspend_cause {
+ PA_SUSPEND_USER = 1, /* Exposed to the user via some protocol */
+ PA_SUSPEND_APPLICATION = 2, /* Used by the device reservation logic */
+ PA_SUSPEND_IDLE = 4, /* Used by module-suspend-on-idle */
+ PA_SUSPEND_SESSION = 8, /* Used by module-hal for mark inactive sessions */
+ PA_SUSPEND_ALL = 0xFFFF /* Magic cause that can be used to resume forcibly */
+} pa_suspend_cause_t;
+
#include <pulsecore/idxset.h>
#include <pulsecore/hashmap.h>
#include <pulsecore/memblock.h>
diff --git a/src/pulsecore/database-gdbm.c b/src/pulsecore/database-gdbm.c
new file mode 100644
index 00000000..aeaac64b
--- /dev/null
+++ b/src/pulsecore/database-gdbm.c
@@ -0,0 +1,246 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2009 Lennart Poettering
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <gdbm.h>
+
+#include <pulse/xmalloc.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/log.h>
+
+#include "database.h"
+
+#define MAKE_GDBM_FILE(x) ((GDBM_FILE) (x))
+
+static inline datum* datum_to_gdbm(datum *to, const pa_datum *from) {
+ pa_assert(from);
+ pa_assert(to);
+
+ to->dptr = from->data;
+ to->dsize = from->size;
+
+ return to;
+}
+
+static inline pa_datum* datum_from_gdbm(pa_datum *to, const datum *from) {
+ pa_assert(from);
+ pa_assert(to);
+
+ to->data = from->dptr;
+ to->size = from->dsize;
+
+ return to;
+}
+
+void pa_datum_free(pa_datum *d) {
+ pa_assert(d);
+
+ free(d->data); /* gdbm uses raw malloc/free hence we should do that here, too */
+ pa_zero(d);
+}
+
+pa_database* pa_database_open(const char *fn, pa_bool_t for_write) {
+ GDBM_FILE f;
+ int gdbm_cache_size;
+ char *path;
+
+ pa_assert(fn);
+
+ /* We include the host identifier in the file name because gdbm
+ * files are CPU dependant, and we don't want things to go wrong
+ * if we are on a multiarch system. */
+
+ path = pa_sprintf_malloc("%s."CANONICAL_HOST".gdbm", fn);
+ errno = 0;
+ f = gdbm_open((char*) path, 0, GDBM_NOLOCK | (for_write ? GDBM_WRCREAT : GDBM_READER), 0644, NULL);
+
+ if (f)
+ pa_log_debug("Opened GDBM database '%s'", path);
+
+ pa_xfree(path);
+
+ if (!f) {
+ if (errno == 0)
+ errno = EIO;
+ return NULL;
+ }
+
+ /* By default the cache of gdbm is rather large, let's reduce it a bit to save memory */
+ gdbm_cache_size = 10;
+ gdbm_setopt(f, GDBM_CACHESIZE, &gdbm_cache_size, sizeof(gdbm_cache_size));
+
+ return (pa_database*) f;
+}
+
+void pa_database_close(pa_database *db) {
+ pa_assert(db);
+
+ gdbm_close(MAKE_GDBM_FILE(db));
+}
+
+pa_datum* pa_database_get(pa_database *db, const pa_datum *key, pa_datum* data) {
+ datum gdbm_key, gdbm_data;
+
+ pa_assert(db);
+ pa_assert(key);
+ pa_assert(data);
+
+ gdbm_data = gdbm_fetch(MAKE_GDBM_FILE(db), *datum_to_gdbm(&gdbm_key, key));
+
+ return gdbm_data.dptr ?
+ datum_from_gdbm(data, &gdbm_data) :
+ NULL;
+}
+
+int pa_database_set(pa_database *db, const pa_datum *key, const pa_datum* data, pa_bool_t overwrite) {
+ datum gdbm_key, gdbm_data;
+
+ pa_assert(db);
+ pa_assert(key);
+ pa_assert(data);
+
+ return gdbm_store(MAKE_GDBM_FILE(db),
+ *datum_to_gdbm(&gdbm_key, key),
+ *datum_to_gdbm(&gdbm_data, data),
+ overwrite ? GDBM_REPLACE : GDBM_INSERT) != 0 ? -1 : 0;
+}
+
+int pa_database_unset(pa_database *db, const pa_datum *key) {
+ datum gdbm_key;
+
+ pa_assert(db);
+ pa_assert(key);
+
+ return gdbm_delete(MAKE_GDBM_FILE(db), *datum_to_gdbm(&gdbm_key, key)) != 0 ? -1 : 0;
+}
+
+int pa_database_clear(pa_database *db) {
+ datum gdbm_key;
+
+ pa_assert(db);
+
+ gdbm_key = gdbm_firstkey(MAKE_GDBM_FILE(db));
+
+ while (gdbm_key.dptr) {
+ datum next;
+
+ next = gdbm_nextkey(MAKE_GDBM_FILE(db), gdbm_key);
+
+ gdbm_delete(MAKE_GDBM_FILE(db), gdbm_key);
+
+ free(gdbm_key.dptr);
+ gdbm_key = next;
+ }
+
+ return gdbm_reorganize(MAKE_GDBM_FILE(db)) == 0 ? 0 : -1;
+}
+
+signed pa_database_size(pa_database *db) {
+ datum gdbm_key;
+ unsigned n = 0;
+
+ pa_assert(db);
+
+ /* This sucks */
+
+ gdbm_key = gdbm_firstkey(MAKE_GDBM_FILE(db));
+
+ while (gdbm_key.dptr) {
+ datum next;
+
+ n++;
+
+ next = gdbm_nextkey(MAKE_GDBM_FILE(db), gdbm_key);
+ free(gdbm_key.dptr);
+ gdbm_key = next;
+ }
+
+ return (signed) n;
+}
+
+pa_datum* pa_database_first(pa_database *db, pa_datum *key, pa_datum *data) {
+ datum gdbm_key, gdbm_data;
+
+ pa_assert(db);
+ pa_assert(key);
+
+ gdbm_key = gdbm_firstkey(MAKE_GDBM_FILE(db));
+
+ if (!gdbm_key.dptr)
+ return NULL;
+
+ if (data) {
+ gdbm_data = gdbm_fetch(MAKE_GDBM_FILE(db), gdbm_key);
+
+ if (!gdbm_data.dptr) {
+ free(gdbm_key.dptr);
+ return NULL;
+ }
+
+ datum_from_gdbm(data, &gdbm_data);
+ }
+
+ datum_from_gdbm(key, &gdbm_key);
+
+ return key;
+}
+
+pa_datum* pa_database_next(pa_database *db, const pa_datum *key, pa_datum *next, pa_datum *data) {
+ datum gdbm_key, gdbm_data;
+
+ pa_assert(db);
+ pa_assert(key);
+ pa_assert(next);
+
+ if (!key)
+ return pa_database_first(db, next, data);
+
+ gdbm_key = gdbm_nextkey(MAKE_GDBM_FILE(db), *datum_to_gdbm(&gdbm_key, key));
+
+ if (!gdbm_key.dptr)
+ return NULL;
+
+ if (data) {
+ gdbm_data = gdbm_fetch(MAKE_GDBM_FILE(db), gdbm_key);
+
+ if (!gdbm_data.dptr) {
+ free(gdbm_key.dptr);
+ return NULL;
+ }
+
+ datum_from_gdbm(data, &gdbm_data);
+ }
+
+ datum_from_gdbm(next, &gdbm_key);
+
+ return next;
+}
+
+int pa_database_sync(pa_database *db) {
+ pa_assert(db);
+
+ gdbm_sync(MAKE_GDBM_FILE(db));
+ return 0;
+}
diff --git a/src/pulsecore/database-tdb.c b/src/pulsecore/database-tdb.c
new file mode 100644
index 00000000..b79d2837
--- /dev/null
+++ b/src/pulsecore/database-tdb.c
@@ -0,0 +1,227 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2009 Lennart Poettering
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+
+/* Some versions of tdb lack inclusion of signal.h in the header files but use sigatomic_t */
+#include <signal.h>
+#include <tdb.h>
+
+#include <pulse/xmalloc.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/log.h>
+
+#include "database.h"
+
+#define MAKE_TDB_CONTEXT(x) ((struct tdb_context*) (x))
+
+static inline TDB_DATA* datum_to_tdb(TDB_DATA *to, const pa_datum *from) {
+ pa_assert(from);
+ pa_assert(to);
+
+ to->dptr = from->data;
+ to->dsize = from->size;
+
+ return to;
+}
+
+static inline pa_datum* datum_from_tdb(pa_datum *to, const TDB_DATA *from) {
+ pa_assert(from);
+ pa_assert(to);
+
+ to->data = from->dptr;
+ to->size = from->dsize;
+
+ return to;
+}
+
+void pa_datum_free(pa_datum *d) {
+ pa_assert(d);
+
+ free(d->data); /* tdb uses raw malloc/free hence we should do that here, too */
+ pa_zero(d);
+}
+
+pa_database* pa_database_open(const char *fn, pa_bool_t for_write) {
+ struct tdb_context *c;
+ char *path;
+
+ pa_assert(fn);
+
+ path = pa_sprintf_malloc("%s.tdb", fn);
+ errno = 0;
+ c = tdb_open(path, 0, TDB_NOSYNC|TDB_NOLOCK,
+ (for_write ? O_RDWR|O_CREAT : O_RDONLY)|O_NOCTTY
+#ifdef O_CLOEXEC
+ |O_CLOEXEC
+#endif
+ , 0644);
+
+ if (c)
+ pa_log_debug("Opened TDB database '%s'", path);
+
+ pa_xfree(path);
+
+ if (!c) {
+ if (errno == 0)
+ errno = EIO;
+ return NULL;
+ }
+
+ return (pa_database*) c;
+}
+
+void pa_database_close(pa_database *db) {
+ pa_assert(db);
+
+ tdb_close(MAKE_TDB_CONTEXT(db));
+}
+
+pa_datum* pa_database_get(pa_database *db, const pa_datum *key, pa_datum* data) {
+ TDB_DATA tdb_key, tdb_data;
+
+ pa_assert(db);
+ pa_assert(key);
+ pa_assert(data);
+
+ tdb_data = tdb_fetch(MAKE_TDB_CONTEXT(db), *datum_to_tdb(&tdb_key, key));
+
+ return tdb_data.dptr ?
+ datum_from_tdb(data, &tdb_data) :
+ NULL;
+}
+
+int pa_database_set(pa_database *db, const pa_datum *key, const pa_datum* data, pa_bool_t overwrite) {
+ TDB_DATA tdb_key, tdb_data;
+
+ pa_assert(db);
+ pa_assert(key);
+ pa_assert(data);
+
+ return tdb_store(MAKE_TDB_CONTEXT(db),
+ *datum_to_tdb(&tdb_key, key),
+ *datum_to_tdb(&tdb_data, data),
+ overwrite ? TDB_REPLACE : TDB_INSERT) != 0 ? -1 : 0;
+}
+
+int pa_database_unset(pa_database *db, const pa_datum *key) {
+ TDB_DATA tdb_key;
+
+ pa_assert(db);
+ pa_assert(key);
+
+ return tdb_delete(MAKE_TDB_CONTEXT(db), *datum_to_tdb(&tdb_key, key)) != 0 ? -1 : 0;
+}
+
+int pa_database_clear(pa_database *db) {
+ pa_assert(db);
+
+ return tdb_wipe_all(MAKE_TDB_CONTEXT(db)) != 0 ? -1 : 0;
+}
+
+signed pa_database_size(pa_database *db) {
+ TDB_DATA tdb_key;
+ unsigned n = 0;
+
+ pa_assert(db);
+
+ /* This sucks */
+
+ tdb_key = tdb_firstkey(MAKE_TDB_CONTEXT(db));
+
+ while (tdb_key.dptr) {
+ TDB_DATA next;
+
+ n++;
+
+ next = tdb_nextkey(MAKE_TDB_CONTEXT(db), tdb_key);
+ free(tdb_key.dptr);
+ tdb_key = next;
+ }
+
+ return (signed) n;
+}
+
+pa_datum* pa_database_first(pa_database *db, pa_datum *key, pa_datum *data) {
+ TDB_DATA tdb_key, tdb_data;
+
+ pa_assert(db);
+ pa_assert(key);
+
+ tdb_key = tdb_firstkey(MAKE_TDB_CONTEXT(db));
+
+ if (!tdb_key.dptr)
+ return NULL;
+
+ if (data) {
+ tdb_data = tdb_fetch(MAKE_TDB_CONTEXT(db), tdb_key);
+
+ if (!tdb_data.dptr) {
+ free(tdb_key.dptr);
+ return NULL;
+ }
+
+ datum_from_tdb(data, &tdb_data);
+ }
+
+ datum_from_tdb(key, &tdb_key);
+
+ return key;
+}
+
+pa_datum* pa_database_next(pa_database *db, const pa_datum *key, pa_datum *next, pa_datum *data) {
+ TDB_DATA tdb_key, tdb_data;
+
+ pa_assert(db);
+ pa_assert(key);
+
+ tdb_key = tdb_nextkey(MAKE_TDB_CONTEXT(db), *datum_to_tdb(&tdb_key, key));
+
+ if (!tdb_key.dptr)
+ return NULL;
+
+ if (data) {
+ tdb_data = tdb_fetch(MAKE_TDB_CONTEXT(db), tdb_key);
+
+ if (!tdb_data.dptr) {
+ free(tdb_key.dptr);
+ return NULL;
+ }
+
+ datum_from_tdb(data, &tdb_data);
+ }
+
+ datum_from_tdb(next, &tdb_key);
+
+ return next;
+}
+
+int pa_database_sync(pa_database *db) {
+ pa_assert(db);
+
+ return 0;
+}
diff --git a/src/pulsecore/database.h b/src/pulsecore/database.h
new file mode 100644
index 00000000..17455d4c
--- /dev/null
+++ b/src/pulsecore/database.h
@@ -0,0 +1,61 @@
+#ifndef foopulsecoredatabasehfoo
+#define foopulsecoredatabasehfoo
+
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2009 Lennart Poettering
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <sys/types.h>
+
+#include <pulsecore/macro.h>
+
+/* A little abstraction over simple databases, such as gdbm, tdb, and
+ * so on. We only make minimal assumptions about the supported
+ * backend: it does not need to support locking, it does not have to
+ * be arch independant. */
+
+typedef struct pa_database pa_database;
+
+typedef struct pa_datum {
+ void *data;
+ size_t size;
+} pa_datum;
+
+void pa_datum_free(pa_datum *d);
+
+/* This will append a suffix to the filename */
+pa_database* pa_database_open(const char *fn, pa_bool_t for_write);
+void pa_database_close(pa_database *db);
+
+pa_datum* pa_database_get(pa_database *db, const pa_datum *key, pa_datum* data);
+
+int pa_database_set(pa_database *db, const pa_datum *key, const pa_datum* data, pa_bool_t overwrite);
+int pa_database_unset(pa_database *db, const pa_datum *key);
+
+int pa_database_clear(pa_database *db);
+
+signed pa_database_size(pa_database *db);
+
+pa_datum* pa_database_first(pa_database *db, pa_datum *key, pa_datum *data /* may be NULL */);
+pa_datum* pa_database_next(pa_database *db, const pa_datum *key, pa_datum *next, pa_datum *data /* may be NULL */);
+
+int pa_database_sync(pa_database *db);
+
+#endif
diff --git a/src/pulsecore/endianmacros.h b/src/pulsecore/endianmacros.h
index 22579376..2b18cf8d 100644
--- a/src/pulsecore/endianmacros.h
+++ b/src/pulsecore/endianmacros.h
@@ -45,27 +45,27 @@
#define PA_UINT32_SWAP(x) ( (uint32_t) ( ((uint32_t) (x) >> 24) | ((uint32_t) (x) << 24) | (((uint32_t) (x) & 0xFF00) << 8) | ((((uint32_t) (x)) >> 8) & 0xFF00) ) )
#endif
-static inline uint32_t PA_READ24LE(const uint8_t *p) {
+static inline uint32_t PA_READ24BE(const uint8_t *p) {
return
((uint32_t) p[0] << 16) |
((uint32_t) p[1] << 8) |
((uint32_t) p[2]);
}
-static inline uint32_t PA_READ24BE(const uint8_t *p) {
+static inline uint32_t PA_READ24LE(const uint8_t *p) {
return
((uint32_t) p[2] << 16) |
((uint32_t) p[1] << 8) |
((uint32_t) p[0]);
}
-static inline void PA_WRITE24LE(uint8_t *p, uint32_t u) {
+static inline void PA_WRITE24BE(uint8_t *p, uint32_t u) {
p[0] = (uint8_t) (u >> 16);
p[1] = (uint8_t) (u >> 8);
p[2] = (uint8_t) u;
}
-static inline void PA_WRITE24BE(uint8_t *p, uint32_t u) {
+static inline void PA_WRITE24LE(uint8_t *p, uint32_t u) {
p[2] = (uint8_t) (u >> 16);
p[1] = (uint8_t) (u >> 8);
p[0] = (uint8_t) u;
diff --git a/src/pulsecore/hashmap.h b/src/pulsecore/hashmap.h
index 08e18ead..828e2448 100644
--- a/src/pulsecore/hashmap.h
+++ b/src/pulsecore/hashmap.h
@@ -65,4 +65,8 @@ void *pa_hashmap_steal_first(pa_hashmap *h);
/* Return the oldest entry in the hashmap */
void* pa_hashmap_first(pa_hashmap *h);
+/* A macro to ease iteration through all entries */
+#define PA_HASHMAP_FOREACH(e, h, state) \
+ for ((state) = NULL, (e) = pa_hashmap_iterate((h), &(state), NULL); (e); (e) = pa_hashmap_iterate((h), &(state), NULL))
+
#endif
diff --git a/src/pulsecore/idxset.h b/src/pulsecore/idxset.h
index 7531ea32..a6179fcf 100644
--- a/src/pulsecore/idxset.h
+++ b/src/pulsecore/idxset.h
@@ -103,4 +103,8 @@ unsigned pa_idxset_size(pa_idxset*s);
/* Return TRUE of the idxset is empty */
pa_bool_t pa_idxset_isempty(pa_idxset *s);
+/* A macro to ease iteration through all entries */
+#define PA_IDXSET_FOREACH(e, s, idx) \
+ for ((e) = pa_idxset_first((s), &(idx)); (e); (e) = pa_idxset_next((s), &(idx)))
+
#endif
diff --git a/src/pulsecore/ioline.c b/src/pulsecore/ioline.c
index 5c38d6e5..7afdb08c 100644
--- a/src/pulsecore/ioline.c
+++ b/src/pulsecore/ioline.c
@@ -57,6 +57,9 @@ struct pa_ioline {
pa_ioline_cb_t callback;
void *userdata;
+ pa_ioline_drain_cb_t drain_callback;
+ void *drain_userdata;
+
pa_bool_t dead:1;
pa_bool_t defer_close:1;
};
@@ -81,6 +84,9 @@ pa_ioline* pa_ioline_new(pa_iochannel *io) {
l->callback = NULL;
l->userdata = NULL;
+ l->drain_callback = NULL;
+ l->drain_userdata = NULL;
+
l->mainloop = pa_iochannel_get_mainloop_api(io);
l->defer_event = l->mainloop->defer_new(l->mainloop, defer_callback, l);
@@ -202,6 +208,17 @@ void pa_ioline_set_callback(pa_ioline*l, pa_ioline_cb_t callback, void *userdata
l->userdata = userdata;
}
+void pa_ioline_set_drain_callback(pa_ioline*l, pa_ioline_drain_cb_t callback, void *userdata) {
+ pa_assert(l);
+ pa_assert(PA_REFCNT_VALUE(l) >= 1);
+
+ if (l->dead)
+ return;
+
+ l->drain_callback = callback;
+ l->drain_userdata = userdata;
+}
+
static void failure(pa_ioline *l, pa_bool_t process_leftover) {
pa_assert(l);
pa_assert(PA_REFCNT_VALUE(l) >= 1);
@@ -266,7 +283,7 @@ static int do_read(pa_ioline *l) {
pa_assert(l);
pa_assert(PA_REFCNT_VALUE(l) >= 1);
- while (!l->dead && pa_iochannel_is_readable(l->io)) {
+ while (l->io && !l->dead && pa_iochannel_is_readable(l->io)) {
ssize_t r;
size_t len;
@@ -331,12 +348,12 @@ static int do_write(pa_ioline *l) {
pa_assert(l);
pa_assert(PA_REFCNT_VALUE(l) >= 1);
- while (!l->dead && pa_iochannel_is_writable(l->io) && l->wbuf_valid_length) {
+ while (l->io && !l->dead && pa_iochannel_is_writable(l->io) && l->wbuf_valid_length > 0) {
if ((r = pa_iochannel_write(l->io, l->wbuf+l->wbuf_index, l->wbuf_valid_length)) <= 0) {
if (r < 0 && errno == EAGAIN)
- return 0;
+ break;
if (r < 0 && errno != EPIPE)
pa_log("write(): %s", pa_cstrerror(errno));
@@ -354,6 +371,9 @@ static int do_write(pa_ioline *l) {
l->wbuf_index = 0;
}
+ if (l->wbuf_valid_length <= 0 && l->drain_callback)
+ l->drain_callback(l, l->drain_userdata);
+
return 0;
}
@@ -423,3 +443,25 @@ void pa_ioline_printf(pa_ioline *l, const char *format, ...) {
pa_ioline_puts(l, t);
pa_xfree(t);
}
+
+pa_iochannel* pa_ioline_detach_iochannel(pa_ioline *l) {
+ pa_iochannel *r;
+
+ pa_assert(l);
+
+ if (!l->io)
+ return NULL;
+
+ r = l->io;
+ l->io = NULL;
+
+ pa_iochannel_set_callback(r, NULL, NULL);
+
+ return r;
+}
+
+pa_bool_t pa_ioline_is_drained(pa_ioline *l) {
+ pa_assert(l);
+
+ return l->wbuf_valid_length <= 0;
+}
diff --git a/src/pulsecore/ioline.h b/src/pulsecore/ioline.h
index 9f32d60f..d973a3c7 100644
--- a/src/pulsecore/ioline.h
+++ b/src/pulsecore/ioline.h
@@ -32,6 +32,7 @@
typedef struct pa_ioline pa_ioline;
typedef void (*pa_ioline_cb_t)(pa_ioline*io, const char *s, void *userdata);
+typedef void (*pa_ioline_drain_cb_t)(pa_ioline *io, void *userdata);
pa_ioline* pa_ioline_new(pa_iochannel *io);
void pa_ioline_unref(pa_ioline *l);
@@ -47,7 +48,17 @@ void pa_ioline_printf(pa_ioline *s, const char *format, ...) PA_GCC_PRINTF_ATTR(
/* Set the callback function that is called for every recieved line */
void pa_ioline_set_callback(pa_ioline*io, pa_ioline_cb_t callback, void *userdata);
+/* Set the callback function that is called when everything has been written */
+void pa_ioline_set_drain_callback(pa_ioline*io, pa_ioline_drain_cb_t callback, void *userdata);
+
/* Make sure to close the ioline object as soon as the send buffer is emptied */
void pa_ioline_defer_close(pa_ioline *io);
+/* Returns TRUE when everything was written */
+pa_bool_t pa_ioline_is_drained(pa_ioline *io);
+
+/* Detaches from the iochannel and returns it. Data that has already
+ * been read will not be available in the detached iochannel */
+pa_iochannel* pa_ioline_detach_iochannel(pa_ioline *l);
+
#endif
diff --git a/src/pulsecore/ipacl.h b/src/pulsecore/ipacl.h
index 7b7ffa61..a3661397 100644
--- a/src/pulsecore/ipacl.h
+++ b/src/pulsecore/ipacl.h
@@ -1,5 +1,5 @@
-#ifndef fooparseaddrhfoo
-#define fooparseaddrhfoo
+#ifndef foopulsecoreipaclhfoo
+#define foopulsecoreipaclhfoo
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/ltdl-helper.h b/src/pulsecore/ltdl-helper.h
index 4c4f018a..9f346969 100644
--- a/src/pulsecore/ltdl-helper.h
+++ b/src/pulsecore/ltdl-helper.h
@@ -29,4 +29,3 @@ typedef void (*pa_void_func_t)(void);
pa_void_func_t pa_load_sym(lt_dlhandle handle, const char*module, const char *symbol);
#endif
-
diff --git a/src/pulsecore/macro.h b/src/pulsecore/macro.h
index a5ca6964..cf662510 100644
--- a/src/pulsecore/macro.h
+++ b/src/pulsecore/macro.h
@@ -29,6 +29,7 @@
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
#include <pulse/gccmacro.h>
@@ -57,18 +58,27 @@
#define PA_PAGE_SIZE ((size_t) 4096)
#endif
+/* Rounds down */
+static inline void* pa_align_ptr(const void *p) {
+ return (void*) (((size_t) p) & ~(sizeof(void*)-1));
+}
+#define PA_ALIGN_PTR(x) (pa_align_ptr(x))
+
+/* Rounds up */
static inline size_t pa_align(size_t l) {
return (((l + sizeof(void*) - 1) / sizeof(void*)) * sizeof(void*));
}
#define PA_ALIGN(x) (pa_align(x))
+/* Rounds down */
static inline void* pa_page_align_ptr(const void *p) {
return (void*) (((size_t) p) & ~(PA_PAGE_SIZE-1));
}
#define PA_PAGE_ALIGN_PTR(x) (pa_page_align_ptr(x))
+/* Rounds up */
static inline size_t pa_page_align(size_t l) {
- return l & ~(PA_PAGE_SIZE-1);
+ return ((l + PA_PAGE_SIZE - 1) / PA_PAGE_SIZE) * PA_PAGE_SIZE;
}
#define PA_PAGE_ALIGN(x) (pa_page_align(x))
@@ -252,6 +262,9 @@ typedef int pa_bool_t;
#define PA_DEBUG_TRAP raise(SIGTRAP)
#endif
+#define pa_memzero(x,l) (memset((x), 0, (l)))
+#define pa_zero(x) (pa_memzero(&(x), sizeof(x)))
+
/* We include this at the very last place */
#include <pulsecore/log.h>
diff --git a/src/pulsecore/memtrap.c b/src/pulsecore/memtrap.c
index e04e838e..e06f60ca 100644
--- a/src/pulsecore/memtrap.c
+++ b/src/pulsecore/memtrap.c
@@ -26,12 +26,17 @@
#include <signal.h>
#include <sys/mman.h>
+/* This is deprecated on glibc but is still used by FreeBSD */
+#if !defined(MAP_ANONYMOUS) && defined(MAP_ANON)
+# define MAP_ANONYMOUS MAP_ANON
+#endif
+
#include <pulse/xmalloc.h>
-#include <pulsecore/semaphore.h>
-#include <pulsecore/macro.h>
-#include <pulsecore/mutex.h>
#include <pulsecore/core-util.h>
+#include <pulsecore/aupdate.h>
+#include <pulsecore/atomic.h>
+#include <pulsecore/once.h>
#include "memtrap.h"
@@ -43,13 +48,13 @@ struct pa_memtrap {
};
static pa_memtrap *memtraps[2] = { NULL, NULL };
-static pa_atomic_t read_lock = PA_ATOMIC_INIT(0);
-static pa_static_semaphore semaphore = PA_STATIC_SEMAPHORE_INIT;
-static pa_static_mutex write_lock = PA_STATIC_MUTEX_INIT;
+static pa_aupdate *aupdate;
-#define MSB (1U << (sizeof(unsigned)*8U-1))
-#define WHICH(n) (!!((n) & MSB))
-#define COUNTER(n) ((n) & ~MSB)
+static void allocate_aupdate(void) {
+ PA_ONCE_BEGIN {
+ aupdate = pa_aupdate_new();
+ } PA_ONCE_END;
+}
pa_bool_t pa_memtrap_is_good(pa_memtrap *m) {
pa_assert(m);
@@ -62,19 +67,11 @@ static void sigsafe_error(const char *s) {
}
static void signal_handler(int sig, siginfo_t* si, void *data) {
- unsigned n, j;
+ unsigned j;
pa_memtrap *m;
void *r;
- /* Increase the lock counter */
- n = (unsigned) pa_atomic_inc(&read_lock);
-
- /* The uppermost bit tells us which list to look at */
- j = WHICH(n);
-
- /* When n is 0 we have about 2^31 threads running that
- * all got a sigbus at the same time, oh my! */
- pa_assert(COUNTER(n)+1 > 0);
+ j = pa_aupdate_read_begin(aupdate);
for (m = memtraps[j]; m; m = m->next[j])
if (si->si_addr >= m->start &&
@@ -94,33 +91,16 @@ static void signal_handler(int sig, siginfo_t* si, void *data) {
pa_assert(r == m->start);
- pa_atomic_dec(&read_lock);
-
- /* Post the semaphore */
- pa_semaphore_post(pa_static_semaphore_get(&semaphore, 0));
-
+ pa_aupdate_read_end(aupdate);
return;
fail:
+ pa_aupdate_read_end(aupdate);
+
sigsafe_error("Failed to handle SIGBUS.\n");
- pa_atomic_dec(&read_lock);
abort();
}
-static void memtrap_swap(unsigned n) {
-
- for (;;) {
-
- /* If the read counter is > 0 wait; if it is 0 try to swap the lists */
- if (COUNTER(n) > 0)
- pa_semaphore_wait(pa_static_semaphore_get(&semaphore, 0));
- else if (pa_atomic_cmpxchg(&read_lock, (int) n, (int) (n ^ MSB)))
- break;
-
- n = (unsigned) pa_atomic_load(&read_lock);
- }
-}
-
static void memtrap_link(pa_memtrap *m, unsigned j) {
pa_assert(m);
@@ -143,92 +123,76 @@ static void memtrap_unlink(pa_memtrap *m, unsigned j) {
pa_memtrap* pa_memtrap_add(const void *start, size_t size) {
pa_memtrap *m = NULL;
- pa_mutex *lock;
- unsigned n, j;
+ unsigned j;
pa_assert(start);
pa_assert(size > 0);
- pa_assert(PA_PAGE_ALIGN_PTR(start) == start);
- pa_assert(PA_PAGE_ALIGN(size) == size);
- lock = pa_static_mutex_get(&write_lock, FALSE, FALSE);
- pa_mutex_lock(lock);
-
- n = (unsigned) pa_atomic_load(&read_lock);
- j = WHICH(n);
+ start = PA_PAGE_ALIGN_PTR(start);
+ size = PA_PAGE_ALIGN(size);
m = pa_xnew(pa_memtrap, 1);
m->start = (void*) start;
m->size = size;
pa_atomic_store(&m->bad, 0);
- memtrap_link(m, !j);
- memtrap_swap(n);
- memtrap_link(m, j);
+ allocate_aupdate();
- pa_mutex_unlock(lock);
+ j = pa_aupdate_write_begin(aupdate);
+ memtrap_link(m, j);
+ j = pa_aupdate_write_swap(aupdate);
+ memtrap_link(m, j);
+ pa_aupdate_write_end(aupdate);
return m;
}
void pa_memtrap_remove(pa_memtrap *m) {
- unsigned n, j;
- pa_mutex *lock;
+ unsigned j;
pa_assert(m);
- lock = pa_static_mutex_get(&write_lock, FALSE, FALSE);
- pa_mutex_lock(lock);
-
- n = (unsigned) pa_atomic_load(&read_lock);
- j = WHICH(n);
+ allocate_aupdate();
- memtrap_unlink(m, !j);
- memtrap_swap(n);
+ j = pa_aupdate_write_begin(aupdate);
memtrap_unlink(m, j);
+ j = pa_aupdate_write_swap(aupdate);
+ memtrap_unlink(m, j);
+ pa_aupdate_write_end(aupdate);
pa_xfree(m);
-
- pa_mutex_unlock(lock);
}
pa_memtrap *pa_memtrap_update(pa_memtrap *m, const void *start, size_t size) {
- unsigned n, j;
- pa_mutex *lock;
+ unsigned j;
pa_assert(m);
pa_assert(start);
pa_assert(size > 0);
- pa_assert(PA_PAGE_ALIGN_PTR(start) == start);
- pa_assert(PA_PAGE_ALIGN(size) == size);
- lock = pa_static_mutex_get(&write_lock, FALSE, FALSE);
- pa_mutex_lock(lock);
+ start = PA_PAGE_ALIGN_PTR(start);
+ size = PA_PAGE_ALIGN(size);
+
+ allocate_aupdate();
+
+ j = pa_aupdate_write_begin(aupdate);
if (m->start == start && m->size == size)
goto unlock;
- n = (unsigned) pa_atomic_load(&read_lock);
- j = WHICH(n);
-
- memtrap_unlink(m, !j);
- memtrap_swap(n);
memtrap_unlink(m, j);
+ j = pa_aupdate_write_swap(aupdate);
m->start = (void*) start;
m->size = size;
pa_atomic_store(&m->bad, 0);
- n = (unsigned) pa_atomic_load(&read_lock);
- j = WHICH(n);
-
- memtrap_link(m, !j);
- memtrap_swap(n);
+ j = pa_aupdate_write_swap(aupdate);
memtrap_link(m, j);
unlock:
- pa_mutex_unlock(lock);
+ pa_aupdate_write_end(aupdate);
return m;
}
@@ -236,10 +200,7 @@ unlock:
void pa_memtrap_install(void) {
struct sigaction sa;
- /* Before we install the signal handler, make sure the semaphore
- * is valid so that the initialization of the semaphore
- * doesn't have to happen from the signal handler */
- pa_static_semaphore_get(&semaphore, 0);
+ allocate_aupdate();
memset(&sa, 0, sizeof(sa));
sa.sa_sigaction = signal_handler;
diff --git a/src/pulsecore/memtrap.h b/src/pulsecore/memtrap.h
index f7da7083..fa38da58 100644
--- a/src/pulsecore/memtrap.h
+++ b/src/pulsecore/memtrap.h
@@ -34,7 +34,7 @@
* still 'good' i.e. no SIGBUS has happened yet for it.
*
* Intended usage is to handle memory mapped in which is controlled by
- * other processes that might execute ftruncate() or when mapping in
+ * other processes that might execute ftruncate() or when mapping inb
* hardware resources that might get invalidated when unplugged. */
typedef struct pa_memtrap pa_memtrap;
diff --git a/src/pulsecore/mime-type.c b/src/pulsecore/mime-type.c
new file mode 100644
index 00000000..b9fe9444
--- /dev/null
+++ b/src/pulsecore/mime-type.c
@@ -0,0 +1,182 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2005-2009 Lennart Poettering
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2.1 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulse/xmalloc.h>
+#include <pulsecore/core-util.h>
+
+#include "mime-type.h"
+
+pa_bool_t pa_sample_spec_is_mime(const pa_sample_spec *ss, const pa_channel_map *cm) {
+
+ pa_assert(pa_channel_map_compatible(cm, ss));
+
+ switch (ss->format) {
+ case PA_SAMPLE_S16BE:
+ case PA_SAMPLE_S24BE:
+ case PA_SAMPLE_U8:
+
+ if (ss->rate != 8000 &&
+ ss->rate != 11025 &&
+ ss->rate != 16000 &&
+ ss->rate != 22050 &&
+ ss->rate != 24000 &&
+ ss->rate != 32000 &&
+ ss->rate != 44100 &&
+ ss->rate != 48000)
+ return FALSE;
+
+ if (ss->channels != 1 &&
+ ss->channels != 2)
+ return FALSE;
+
+ if ((cm->channels == 1 && cm->map[0] != PA_CHANNEL_POSITION_MONO) ||
+ (cm->channels == 2 && (cm->map[0] != PA_CHANNEL_POSITION_LEFT || cm->map[1] != PA_CHANNEL_POSITION_RIGHT)))
+ return FALSE;
+
+ return TRUE;
+
+ case PA_SAMPLE_ULAW:
+
+ if (ss->rate != 8000)
+ return FALSE;
+
+ if (ss->channels != 1)
+ return FALSE;
+
+ if (cm->map[0] != PA_CHANNEL_POSITION_MONO)
+ return FALSE;
+
+ return TRUE;
+
+ default:
+ return FALSE;
+ }
+}
+
+void pa_sample_spec_mimefy(pa_sample_spec *ss, pa_channel_map *cm) {
+
+ pa_assert(pa_channel_map_compatible(cm, ss));
+
+ /* Turns the sample type passed in into the next 'better' one that
+ * can be encoded for HTTP. If there is no 'better' one we pick
+ * the 'best' one that is 'worse'. */
+
+ if (ss->channels > 2)
+ ss->channels = 2;
+
+ if (ss->rate > 44100)
+ ss->rate = 48000;
+ else if (ss->rate > 32000)
+ ss->rate = 44100;
+ else if (ss->rate > 24000)
+ ss->rate = 32000;
+ else if (ss->rate > 22050)
+ ss->rate = 24000;
+ else if (ss->rate > 16000)
+ ss->rate = 22050;
+ else if (ss->rate > 11025)
+ ss->rate = 16000;
+ else if (ss->rate > 8000)
+ ss->rate = 11025;
+ else
+ ss->rate = 8000;
+
+ switch (ss->format) {
+ case PA_SAMPLE_S24BE:
+ case PA_SAMPLE_S24LE:
+ case PA_SAMPLE_S24_32LE:
+ case PA_SAMPLE_S24_32BE:
+ case PA_SAMPLE_S32LE:
+ case PA_SAMPLE_S32BE:
+ case PA_SAMPLE_FLOAT32LE:
+ case PA_SAMPLE_FLOAT32BE:
+ ss->format = PA_SAMPLE_S24BE;
+ break;
+
+ case PA_SAMPLE_S16BE:
+ case PA_SAMPLE_S16LE:
+ ss->format = PA_SAMPLE_S16BE;
+ break;
+
+ case PA_SAMPLE_ULAW:
+ case PA_SAMPLE_ALAW:
+
+ if (ss->rate == 8000 && ss->channels == 1)
+ ss->format = PA_SAMPLE_ULAW;
+ else
+ ss->format = PA_SAMPLE_S16BE;
+ break;
+
+ case PA_SAMPLE_U8:
+ ss->format = PA_SAMPLE_U8;
+ break;
+
+ case PA_SAMPLE_MAX:
+ case PA_SAMPLE_INVALID:
+ pa_assert_not_reached();
+ }
+
+ pa_channel_map_init_auto(cm, ss->channels, PA_CHANNEL_MAP_DEFAULT);
+
+ pa_assert(pa_sample_spec_is_mime(ss, cm));
+}
+
+char *pa_sample_spec_to_mime_type(const pa_sample_spec *ss, const pa_channel_map *cm) {
+ pa_assert(pa_channel_map_compatible(cm, ss));
+
+ if (!pa_sample_spec_is_mime(ss, cm))
+ return NULL;
+
+ switch (ss->format) {
+
+ case PA_SAMPLE_S16BE:
+ case PA_SAMPLE_S24BE:
+ case PA_SAMPLE_U8:
+ /* Stupid UPnP implementations (PS3...) choke on spaces in
+ * the mime type, that's why we write only ';' here,
+ * instead of '; '. */
+ return pa_sprintf_malloc("audio/%s;rate=%u;channels=%u",
+ ss->format == PA_SAMPLE_S16BE ? "L16" :
+ (ss->format == PA_SAMPLE_S24BE ? "L24" : "L8"),
+ ss->rate, ss->channels);
+
+ case PA_SAMPLE_ULAW:
+ return pa_xstrdup("audio/basic");
+
+ default:
+ pa_assert_not_reached();
+ }
+
+ pa_assert(pa_sample_spec_valid(ss));
+}
+
+char *pa_sample_spec_to_mime_type_mimefy(const pa_sample_spec *_ss, const pa_channel_map *_cm) {
+ pa_sample_spec ss = *_ss;
+ pa_channel_map cm = *_cm;
+
+ pa_sample_spec_mimefy(&ss, &cm);
+
+ return pa_sample_spec_to_mime_type(&ss, &cm);
+}
diff --git a/src/pulsecore/mime-type.h b/src/pulsecore/mime-type.h
new file mode 100644
index 00000000..db77379b
--- /dev/null
+++ b/src/pulsecore/mime-type.h
@@ -0,0 +1,37 @@
+#ifndef foopulsecoremimetypehfoo
+#define foopulsecoremimetypehfoo
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2009 Lennart Poettering
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2.1 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulsecore/macro.h>
+#include <pulse/sample.h>
+#include <pulse/channelmap.h>
+
+pa_bool_t pa_sample_spec_is_mime(const pa_sample_spec *ss, const pa_channel_map *cm);
+void pa_sample_spec_mimefy(pa_sample_spec *ss, pa_channel_map *cm);
+char *pa_sample_spec_to_mime_type(const pa_sample_spec *ss, const pa_channel_map *cm);
+char *pa_sample_spec_to_mime_type_mimefy(const pa_sample_spec *_ss, const pa_channel_map *_cm);
+
+#endif
diff --git a/src/pulsecore/modargs.c b/src/pulsecore/modargs.c
index 73c67a8b..c7d734d9 100644
--- a/src/pulsecore/modargs.c
+++ b/src/pulsecore/modargs.c
@@ -84,8 +84,11 @@ pa_modargs *pa_modargs_new(const char *args, const char* const* valid_keys) {
KEY,
VALUE_START,
VALUE_SIMPLE,
+ VALUE_SIMPLE_ESCAPED,
VALUE_DOUBLE_QUOTES,
- VALUE_TICKS
+ VALUE_DOUBLE_QUOTES_ESCAPED,
+ VALUE_TICKS,
+ VALUE_TICKS_ESCAPED
} state;
const char *p, *key = NULL, *value = NULL;
@@ -131,9 +134,16 @@ pa_modargs *pa_modargs_new(const char *args, const char* const* valid_keys) {
value = p+1;
value_len = 0;
} else if (isspace(*p)) {
- if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrdup(""), valid_keys) < 0)
+ if (add_key_value(map,
+ pa_xstrndup(key, key_len),
+ pa_xstrdup(""),
+ valid_keys) < 0)
goto fail;
state = WHITESPACE;
+ } else if (*p == '\\') {
+ state = VALUE_SIMPLE_ESCAPED;
+ value = p;
+ value_len = 1;
} else {
state = VALUE_SIMPLE;
value = p;
@@ -143,30 +153,63 @@ pa_modargs *pa_modargs_new(const char *args, const char* const* valid_keys) {
case VALUE_SIMPLE:
if (isspace(*p)) {
- if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrndup(value, value_len), valid_keys) < 0)
+ if (add_key_value(map,
+ pa_xstrndup(key, key_len),
+ pa_unescape(pa_xstrndup(value, value_len)),
+ valid_keys) < 0)
goto fail;
state = WHITESPACE;
+ } else if (*p == '\\') {
+ state = VALUE_SIMPLE_ESCAPED;
+ value_len++;
} else
value_len++;
break;
+ case VALUE_SIMPLE_ESCAPED:
+ state = VALUE_SIMPLE;
+ value_len++;
+ break;
+
case VALUE_DOUBLE_QUOTES:
if (*p == '"') {
- if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrndup(value, value_len), valid_keys) < 0)
+ if (add_key_value(map,
+ pa_xstrndup(key, key_len),
+ pa_unescape(pa_xstrndup(value, value_len)),
+ valid_keys) < 0)
goto fail;
state = WHITESPACE;
+ } else if (*p == '\\') {
+ state = VALUE_DOUBLE_QUOTES_ESCAPED;
+ value_len++;
} else
value_len++;
break;
+ case VALUE_DOUBLE_QUOTES_ESCAPED:
+ state = VALUE_DOUBLE_QUOTES;
+ value_len++;
+ break;
+
case VALUE_TICKS:
if (*p == '\'') {
- if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrndup(value, value_len), valid_keys) < 0)
+ if (add_key_value(map,
+ pa_xstrndup(key, key_len),
+ pa_unescape(pa_xstrndup(value, value_len)),
+ valid_keys) < 0)
goto fail;
state = WHITESPACE;
+ } else if (*p == '\\') {
+ state = VALUE_TICKS_ESCAPED;
+ value_len++;
} else
value_len++;
break;
+
+ case VALUE_TICKS_ESCAPED:
+ state = VALUE_TICKS;
+ value_len++;
+ break;
}
}
@@ -352,3 +395,23 @@ int pa_modargs_get_sample_spec_and_channel_map(
return 0;
}
+
+int pa_modargs_get_proplist(pa_modargs *ma, const char *name, pa_proplist *p, pa_update_mode_t m) {
+ const char *v;
+ pa_proplist *n;
+
+ pa_assert(ma);
+ pa_assert(name);
+ pa_assert(p);
+
+ if (!(v = pa_modargs_get_value(ma, name, NULL)))
+ return 0;
+
+ if (!(n = pa_proplist_from_string(v)))
+ return -1;
+
+ pa_proplist_update(p, m, n);
+ pa_proplist_free(n);
+
+ return 0;
+}
diff --git a/src/pulsecore/modargs.h b/src/pulsecore/modargs.h
index 809fb27e..b3125b10 100644
--- a/src/pulsecore/modargs.h
+++ b/src/pulsecore/modargs.h
@@ -58,4 +58,6 @@ structure if no channel_map is found, using pa_channel_map_init_auto() */
int pa_modargs_get_sample_spec_and_channel_map(pa_modargs *ma, pa_sample_spec *ss, pa_channel_map *map, pa_channel_map_def_t def);
+int pa_modargs_get_proplist(pa_modargs *ma, const char *name, pa_proplist *p, pa_update_mode_t m);
+
#endif
diff --git a/src/pulsecore/modinfo.c b/src/pulsecore/modinfo.c
index 00fb9c43..b5ee9f56 100644
--- a/src/pulsecore/modinfo.c
+++ b/src/pulsecore/modinfo.c
@@ -38,6 +38,7 @@
#define PA_SYMBOL_DESCRIPTION "pa__get_description"
#define PA_SYMBOL_USAGE "pa__get_usage"
#define PA_SYMBOL_VERSION "pa__get_version"
+#define PA_SYMBOL_DEPRECATED "pa__get_deprecated"
#define PA_SYMBOL_LOAD_ONCE "pa__load_once"
pa_modinfo *pa_modinfo_get_by_handle(lt_dlhandle dl, const char *module_name) {
@@ -61,6 +62,9 @@ pa_modinfo *pa_modinfo_get_by_handle(lt_dlhandle dl, const char *module_name) {
if ((func = (const char* (*)(void)) pa_load_sym(dl, module_name, PA_SYMBOL_VERSION)))
i->version = pa_xstrdup(func());
+ if ((func = (const char* (*)(void)) pa_load_sym(dl, module_name, PA_SYMBOL_DEPRECATED)))
+ i->deprecated = pa_xstrdup(func());
+
if ((func2 = (pa_bool_t (*)(void)) pa_load_sym(dl, module_name, PA_SYMBOL_LOAD_ONCE)))
i->load_once = func2();
@@ -91,5 +95,6 @@ void pa_modinfo_free(pa_modinfo *i) {
pa_xfree(i->description);
pa_xfree(i->usage);
pa_xfree(i->version);
+ pa_xfree(i->deprecated);
pa_xfree(i);
}
diff --git a/src/pulsecore/modinfo.h b/src/pulsecore/modinfo.h
index 407e602a..baad0de7 100644
--- a/src/pulsecore/modinfo.h
+++ b/src/pulsecore/modinfo.h
@@ -30,6 +30,7 @@ typedef struct pa_modinfo {
char *description;
char *usage;
char *version;
+ char *deprecated;
pa_bool_t load_once;
} pa_modinfo;
diff --git a/src/pulsecore/module.c b/src/pulsecore/module.c
index 42fd912c..5bcdd898 100644
--- a/src/pulsecore/module.c
+++ b/src/pulsecore/module.c
@@ -48,10 +48,12 @@
#define PA_SYMBOL_DONE "pa__done"
#define PA_SYMBOL_LOAD_ONCE "pa__load_once"
#define PA_SYMBOL_GET_N_USED "pa__get_n_used"
+#define PA_SYMBOL_GET_DEPRECATE "pa__get_deprecated"
pa_module* pa_module_load(pa_core *c, const char *name, const char *argument) {
pa_module *m = NULL;
pa_bool_t (*load_once)(void);
+ const char* (*get_deprecated)(void);
pa_modinfo *mi;
pa_assert(c);
@@ -89,6 +91,13 @@ pa_module* pa_module_load(pa_core *c, const char *name, const char *argument) {
}
}
+ if ((get_deprecated = (const char* (*) (void)) pa_load_sym(m->dl, name, PA_SYMBOL_GET_DEPRECATE))) {
+ const char *t;
+
+ if ((t = get_deprecated()))
+ pa_log_warn("%s is deprecated: %s", name, t);
+ }
+
if (!(m->init = (int (*)(pa_module*_m)) pa_load_sym(m->dl, name, PA_SYMBOL_INIT))) {
pa_log("Failed to load module \"%s\": symbol \""PA_SYMBOL_INIT"\" not found.", name);
goto fail;
diff --git a/src/pulsecore/module.h b/src/pulsecore/module.h
index 3f697348..af89d793 100644
--- a/src/pulsecore/module.h
+++ b/src/pulsecore/module.h
@@ -78,6 +78,10 @@ int pa_module_get_n_used(pa_module*m);
const char * pa__get_version(void) { return s; } \
struct __stupid_useless_struct_to_allow_trailing_semicolon
+#define PA_MODULE_DEPRECATED(s) \
+ const char * pa__get_deprecated(void) { return s; } \
+ struct __stupid_useless_struct_to_allow_trailing_semicolon
+
#define PA_MODULE_LOAD_ONCE(b) \
pa_bool_t pa__load_once(void) { return b; } \
struct __stupid_useless_struct_to_allow_trailing_semicolon
diff --git a/src/pulsecore/parseaddr.c b/src/pulsecore/parseaddr.c
index 5b531220..44cd9a05 100644
--- a/src/pulsecore/parseaddr.c
+++ b/src/pulsecore/parseaddr.c
@@ -25,6 +25,8 @@
#include <string.h>
#include <stdlib.h>
+#include <arpa/inet.h>
+#include <sys/socket.h>
#include <pulse/xmalloc.h>
#include <pulse/util.h>
@@ -131,3 +133,17 @@ int pa_parse_address(const char *name, pa_parsed_address *ret_p) {
return 0;
}
+
+pa_bool_t pa_is_ip_address(const char *a) {
+ char buf[INET6_ADDRSTRLEN];
+
+ pa_assert(a);
+
+ if (inet_pton(AF_INET6, a, buf) >= 1)
+ return TRUE;
+
+ if (inet_pton(AF_INET, a, buf) >= 1)
+ return TRUE;
+
+ return FALSE;
+}
diff --git a/src/pulsecore/parseaddr.h b/src/pulsecore/parseaddr.h
index 5fbcb9a7..a1071b0a 100644
--- a/src/pulsecore/parseaddr.h
+++ b/src/pulsecore/parseaddr.h
@@ -1,5 +1,5 @@
-#ifndef fooparseaddrhfoo
-#define fooparseaddrhfoo
+#ifndef foopulsecoreparseaddrhfoo
+#define foopulsecoreparseaddrhfoo
/***
This file is part of PulseAudio.
@@ -24,6 +24,8 @@
#include <inttypes.h>
+#include <pulsecore/macro.h>
+
typedef enum pa_parsed_address_type {
PA_PARSED_ADDRESS_UNIX,
PA_PARSED_ADDRESS_TCP4,
@@ -39,4 +41,6 @@ typedef struct pa_parsed_address {
int pa_parse_address(const char *a, pa_parsed_address *ret_p);
+pa_bool_t pa_is_ip_address(const char *a);
+
#endif
diff --git a/src/pulsecore/proplist-util.c b/src/pulsecore/proplist-util.c
index eac8927c..d9769bc7 100644
--- a/src/pulsecore/proplist-util.c
+++ b/src/pulsecore/proplist-util.c
@@ -168,20 +168,20 @@ void pa_init_proplist(pa_proplist *p) {
}
if (!pa_proplist_contains(p, PA_PROP_APPLICATION_PROCESS_USER)) {
- char t[64];
- if (pa_get_user_name(t, sizeof(t))) {
- char *c = pa_utf8_filter(t);
- pa_proplist_sets(p, PA_PROP_APPLICATION_PROCESS_USER, c);
- pa_xfree(c);
+ char *u;
+
+ if ((u = pa_get_user_name_malloc())) {
+ pa_proplist_sets(p, PA_PROP_APPLICATION_PROCESS_USER, u);
+ pa_xfree(u);
}
}
if (!pa_proplist_contains(p, PA_PROP_APPLICATION_PROCESS_HOST)) {
- char t[64];
- if (pa_get_host_name(t, sizeof(t))) {
- char *c = pa_utf8_filter(t);
- pa_proplist_sets(p, PA_PROP_APPLICATION_PROCESS_HOST, c);
- pa_xfree(c);
+ char *h;
+
+ if ((h = pa_get_host_name_malloc())) {
+ pa_proplist_sets(p, PA_PROP_APPLICATION_PROCESS_HOST, h);
+ pa_xfree(h);
}
}
diff --git a/src/pulsecore/protocol-esound.c b/src/pulsecore/protocol-esound.c
index 7e7126ea..ad7cd045 100644
--- a/src/pulsecore/protocol-esound.c
+++ b/src/pulsecore/protocol-esound.c
@@ -947,10 +947,10 @@ static int esd_proto_standby_or_resume(connection *c, esd_proto_t request, const
connection_write(c, &ok, sizeof(int32_t));
if (request == ESD_PROTO_STANDBY)
- ok = pa_sink_suspend_all(c->protocol->core, TRUE) >= 0;
+ ok = pa_sink_suspend_all(c->protocol->core, TRUE, PA_SUSPEND_USER) >= 0;
else {
pa_assert(request == ESD_PROTO_RESUME);
- ok = pa_sink_suspend_all(c->protocol->core, FALSE) >= 0;
+ ok = pa_sink_suspend_all(c->protocol->core, FALSE, PA_SUSPEND_USER) >= 0;
}
connection_write(c, &ok, sizeof(int32_t));
diff --git a/src/pulsecore/protocol-http.c b/src/pulsecore/protocol-http.c
index f3b93819..5220cc91 100644
--- a/src/pulsecore/protocol-http.c
+++ b/src/pulsecore/protocol-http.c
@@ -1,7 +1,7 @@
/***
This file is part of PulseAudio.
- Copyright 2005-2006 Lennart Poettering
+ Copyright 2005-2009 Lennart Poettering
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
@@ -26,36 +26,68 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
+#include <errno.h>
#include <pulse/util.h>
#include <pulse/xmalloc.h>
+#include <pulse/timeval.h>
#include <pulsecore/ioline.h>
+#include <pulsecore/thread-mq.h>
#include <pulsecore/macro.h>
#include <pulsecore/log.h>
#include <pulsecore/namereg.h>
#include <pulsecore/cli-text.h>
#include <pulsecore/shared.h>
+#include <pulsecore/core-error.h>
+#include <pulsecore/mime-type.h>
#include "protocol-http.h"
/* Don't allow more than this many concurrent connections */
#define MAX_CONNECTIONS 10
-#define internal_server_error(c) http_message((c), 500, "Internal Server Error", NULL)
-
#define URL_ROOT "/"
#define URL_CSS "/style"
#define URL_STATUS "/status"
+#define URL_LISTEN "/listen"
+#define URL_LISTEN_SOURCE "/listen/source/"
+
+#define MIME_HTML "text/html; charset=utf-8"
+#define MIME_TEXT "text/plain; charset=utf-8"
+#define MIME_CSS "text/css"
+
+#define HTML_HEADER(t) \
+ "<?xml version=\"1.0\"?>\n" \
+ "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n" \
+ "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n" \
+ " <head>\n" \
+ " <title>"t"</title>\n" \
+ " <link rel=\"stylesheet\" type=\"text/css\" href=\"style\"/>\n" \
+ " </head>\n" \
+ " <body>\n"
+
+#define HTML_FOOTER \
+ " </body>\n" \
+ "</html>\n"
+
+#define RECORD_BUFFER_SECONDS (5)
+#define DEFAULT_SOURCE_LATENCY (300*PA_USEC_PER_MSEC)
+
+enum state {
+ STATE_REQUEST_LINE,
+ STATE_MIME_HEADER,
+ STATE_DATA
+};
struct connection {
pa_http_protocol *protocol;
+ pa_iochannel *io;
pa_ioline *line;
- enum {
- REQUEST_LINE,
- MIME_HEADER,
- DATA
- } state;
+ pa_memblockq *output_memblockq;
+ pa_source_output *source_output;
+ pa_client *client;
+ enum state state;
char *url;
pa_module *module;
};
@@ -65,58 +97,500 @@ struct pa_http_protocol {
pa_core *core;
pa_idxset *connections;
+
+ pa_strlist *servers;
};
-static void http_response(struct connection *c, int code, const char *msg, const char *mime) {
- char s[256];
+enum {
+ SOURCE_OUTPUT_MESSAGE_POST_DATA = PA_SOURCE_OUTPUT_MESSAGE_MAX
+};
+
+/* Called from main context */
+static void connection_unlink(struct connection *c) {
+ pa_assert(c);
+
+ if (c->source_output) {
+ pa_source_output_unlink(c->source_output);
+ c->source_output->userdata = NULL;
+ pa_source_output_unref(c->source_output);
+ }
+
+ if (c->client)
+ pa_client_free(c->client);
+
+ pa_xfree(c->url);
+
+ if (c->line)
+ pa_ioline_unref(c->line);
+
+ if (c->io)
+ pa_iochannel_free(c->io);
+
+ if (c->output_memblockq)
+ pa_memblockq_free(c->output_memblockq);
+
+ pa_idxset_remove_by_data(c->protocol->connections, c, NULL);
+
+ pa_xfree(c);
+}
+
+/* Called from main context */
+static int do_write(struct connection *c) {
+ pa_memchunk chunk;
+ ssize_t r;
+ void *p;
+
+ pa_assert(c);
+
+ if (pa_memblockq_peek(c->output_memblockq, &chunk) < 0)
+ return 0;
+
+ pa_assert(chunk.memblock);
+ pa_assert(chunk.length > 0);
+
+ p = pa_memblock_acquire(chunk.memblock);
+ r = pa_iochannel_write(c->io, (uint8_t*) p+chunk.index, chunk.length);
+ pa_memblock_release(chunk.memblock);
+
+ pa_memblock_unref(chunk.memblock);
+
+ if (r < 0) {
+
+ if (errno == EINTR || errno == EAGAIN)
+ return 0;
+
+ pa_log("write(): %s", pa_cstrerror(errno));
+ return -1;
+ }
+
+ pa_memblockq_drop(c->output_memblockq, (size_t) r);
+
+ return 0;
+}
+
+/* Called from main context */
+static void do_work(struct connection *c) {
+ pa_assert(c);
+
+ if (pa_iochannel_is_hungup(c->io))
+ goto fail;
+
+ if (pa_iochannel_is_writable(c->io))
+ if (do_write(c) < 0)
+ goto fail;
+
+ return;
+
+fail:
+ connection_unlink(c);
+}
+
+/* Called from thread context, except when it is not */
+static int source_output_process_msg(pa_msgobject *m, int code, void *userdata, int64_t offset, pa_memchunk *chunk) {
+ pa_source_output *o = PA_SOURCE_OUTPUT(m);
+ struct connection *c;
+
+ pa_source_output_assert_ref(o);
+
+ if (!(c = o->userdata))
+ return -1;
+
+ switch (code) {
+
+ case SOURCE_OUTPUT_MESSAGE_POST_DATA:
+ /* While this function is usually called from IO thread
+ * context, this specific command is not! */
+ pa_memblockq_push_align(c->output_memblockq, chunk);
+ do_work(c);
+ break;
+
+ default:
+ return pa_source_output_process_msg(m, code, userdata, offset, chunk);
+ }
+
+ return 0;
+}
+
+/* Called from thread context */
+static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk) {
+ struct connection *c;
+
+ pa_source_output_assert_ref(o);
+ pa_assert_se(c = o->userdata);
+ pa_assert(chunk);
+
+ pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(o), SOURCE_OUTPUT_MESSAGE_POST_DATA, NULL, 0, chunk, NULL);
+}
+
+/* Called from main context */
+static void source_output_kill_cb(pa_source_output *o) {
+ struct connection*c;
+
+ pa_source_output_assert_ref(o);
+ pa_assert_se(c = o->userdata);
+
+ connection_unlink(c);
+}
+
+/* Called from main context */
+static pa_usec_t source_output_get_latency_cb(pa_source_output *o) {
+ struct connection*c;
+
+ pa_source_output_assert_ref(o);
+ pa_assert_se(c = o->userdata);
+
+ return pa_bytes_to_usec(pa_memblockq_get_length(c->output_memblockq), &c->source_output->sample_spec);
+}
+
+/*** client callbacks ***/
+static void client_kill_cb(pa_client *client) {
+ struct connection*c;
+
+ pa_assert(client);
+ pa_assert_se(c = client->userdata);
+
+ connection_unlink(c);
+}
+
+/*** pa_iochannel callbacks ***/
+static void io_callback(pa_iochannel*io, void *userdata) {
+ struct connection *c = userdata;
+
+ pa_assert(c);
+ pa_assert(io);
+
+ do_work(c);
+}
+
+static char *escape_html(const char *t) {
+ pa_strbuf *sb;
+ const char *p, *e;
+
+ sb = pa_strbuf_new();
+
+ for (e = p = t; *p; p++) {
+
+ if (*p == '>' || *p == '<' || *p == '&') {
+
+ if (p > e) {
+ pa_strbuf_putsn(sb, e, p-e);
+ e = p + 1;
+ }
+
+ if (*p == '>')
+ pa_strbuf_puts(sb, "&gt;");
+ else if (*p == '<')
+ pa_strbuf_puts(sb, "&lt;");
+ else
+ pa_strbuf_puts(sb, "&amp;");
+ }
+ }
+
+ if (p > e)
+ pa_strbuf_putsn(sb, e, p-e);
+
+ return pa_strbuf_tostring_free(sb);
+}
+
+static void http_response(
+ struct connection *c,
+ int code,
+ const char *msg,
+ const char *mime) {
+
+ char *s;
pa_assert(c);
pa_assert(msg);
pa_assert(mime);
- pa_snprintf(s, sizeof(s),
- "HTTP/1.0 %i %s\n"
- "Connection: close\n"
- "Content-Type: %s\n"
- "Cache-Control: no-cache\n"
- "Expires: 0\n"
- "Server: "PACKAGE_NAME"/"PACKAGE_VERSION"\n"
- "\n", code, msg, mime);
-
+ s = pa_sprintf_malloc(
+ "HTTP/1.0 %i %s\n"
+ "Connection: close\n"
+ "Content-Type: %s\n"
+ "Cache-Control: no-cache\n"
+ "Expires: 0\n"
+ "Server: "PACKAGE_NAME"/"PACKAGE_VERSION"\n"
+ "\n", code, msg, mime);
pa_ioline_puts(c->line, s);
+ pa_xfree(s);
}
-static void http_message(struct connection *c, int code, const char *msg, const char *text) {
- char s[256];
+static void html_response(
+ struct connection *c,
+ int code,
+ const char *msg,
+ const char *text) {
+
+ char *s;
pa_assert(c);
- http_response(c, code, msg, "text/html");
+ http_response(c, code, msg, MIME_HTML);
if (!text)
text = msg;
- pa_snprintf(s, sizeof(s),
- "<?xml version=\"1.0\"?>\n"
- "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n"
- "<html xmlns=\"http://www.w3.org/1999/xhtml\"><head><title>%s</title></head>\n"
- "<body>%s</body></html>\n",
- text, text);
+ s = pa_sprintf_malloc(
+ HTML_HEADER("%s")
+ "%s"
+ HTML_FOOTER,
+ text, text);
pa_ioline_puts(c->line, s);
+ pa_xfree(s);
+
pa_ioline_defer_close(c->line);
}
+static void html_print_field(pa_ioline *line, const char *left, const char *right) {
+ char *eleft, *eright;
+
+ eleft = escape_html(left);
+ eright = escape_html(right);
+
+ pa_ioline_printf(line,
+ "<tr><td><b>%s</b></td>"
+ "<td>%s</td></tr>\n", eleft, eright);
+
+ pa_xfree(eleft);
+ pa_xfree(eright);
+}
+
+static void handle_root(struct connection *c) {
+ char *t;
-static void connection_unlink(struct connection *c) {
pa_assert(c);
- if (c->url)
- pa_xfree(c->url);
+ http_response(c, 200, "OK", MIME_HTML);
- pa_idxset_remove_by_data(c->protocol->connections, c, NULL);
+ pa_ioline_puts(c->line,
+ HTML_HEADER(PACKAGE_NAME" "PACKAGE_VERSION)
+ "<h1>"PACKAGE_NAME" "PACKAGE_VERSION"</h1>\n"
+ "<table>\n");
+
+ t = pa_get_user_name_malloc();
+ html_print_field(c->line, "User Name:", t);
+ pa_xfree(t);
+
+ t = pa_get_host_name_malloc();
+ html_print_field(c->line, "Host name:", t);
+ pa_xfree(t);
+
+ t = pa_machine_id();
+ html_print_field(c->line, "Machine ID:", t);
+ pa_xfree(t);
+
+ t = pa_uname_string();
+ html_print_field(c->line, "System:", t);
+ pa_xfree(t);
+
+ t = pa_sprintf_malloc("%lu", (unsigned long) getpid());
+ html_print_field(c->line, "Process ID:", t);
+ pa_xfree(t);
+
+ pa_ioline_puts(c->line,
+ "</table>\n"
+ "<p><a href=\"" URL_STATUS "\">Show an extensive server status report</a></p>\n"
+ "<p><a href=\"" URL_LISTEN "\">Monitor sinks and sources</a></p>\n"
+ HTML_FOOTER);
+
+ pa_ioline_defer_close(c->line);
+}
+
+static void handle_css(struct connection *c) {
+ pa_assert(c);
+
+ http_response(c, 200, "OK", MIME_CSS);
+
+ pa_ioline_puts(c->line,
+ "body { color: black; background-color: white; }\n"
+ "a:link, a:visited { color: #900000; }\n"
+ "div.news-date { font-size: 80%; font-style: italic; }\n"
+ "pre { background-color: #f0f0f0; padding: 0.4cm; }\n"
+ ".grey { color: #8f8f8f; font-size: 80%; }"
+ "table { margin-left: 1cm; border:1px solid lightgrey; padding: 0.2cm; }\n"
+ "td { padding-left:10px; padding-right:10px; }\n");
+
+ pa_ioline_defer_close(c->line);
+}
+
+static void handle_status(struct connection *c) {
+ char *r;
+
+ pa_assert(c);
+
+ http_response(c, 200, "OK", MIME_TEXT);
+ r = pa_full_status_string(c->protocol->core);
+ pa_ioline_puts(c->line, r);
+ pa_xfree(r);
+
+ pa_ioline_defer_close(c->line);
+}
+
+static void handle_listen(struct connection *c) {
+ pa_source *source;
+ pa_sink *sink;
+ uint32_t idx;
+
+ http_response(c, 200, "OK", MIME_HTML);
+
+ pa_ioline_puts(c->line,
+ HTML_HEADER("Listen")
+ "<h2>Sinks</h2>\n"
+ "<p>\n");
+
+ PA_IDXSET_FOREACH(sink, c->protocol->core->sinks, idx) {
+ char *t, *m;
+
+ t = escape_html(pa_strna(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION)));
+ m = pa_sample_spec_to_mime_type_mimefy(&sink->sample_spec, &sink->channel_map);
+
+ pa_ioline_printf(c->line,
+ "<a href=\"" URL_LISTEN_SOURCE "%s\" title=\"%s\">%s</a><br/>\n",
+ sink->monitor_source->name, m, t);
+
+ pa_xfree(t);
+ pa_xfree(m);
+ }
+
+ pa_ioline_puts(c->line,
+ "</p>\n"
+ "<h2>Sources</h2>\n"
+ "<p>\n");
+
+ PA_IDXSET_FOREACH(source, c->protocol->core->sources, idx) {
+ char *t, *m;
+
+ if (source->monitor_of)
+ continue;
+
+ t = escape_html(pa_strna(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION)));
+ m = pa_sample_spec_to_mime_type_mimefy(&source->sample_spec, &source->channel_map);
+
+ pa_ioline_printf(c->line,
+ "<a href=\"" URL_LISTEN_SOURCE "%s\" title=\"%s\">%s</a><br/>\n",
+ source->name, m, t);
+
+ pa_xfree(m);
+ pa_xfree(t);
+
+ }
+
+ pa_ioline_puts(c->line,
+ "</p>\n"
+ HTML_FOOTER);
+
+ pa_ioline_defer_close(c->line);
+}
+
+static void line_drain_callback(pa_ioline *l, void *userdata) {
+ struct connection *c;
+
+ pa_assert(l);
+ pa_assert_se(c = userdata);
+
+ /* We don't need the line reader anymore, instead we need a real
+ * binary io channel */
+ pa_assert_se(c->io = pa_ioline_detach_iochannel(c->line));
+ pa_iochannel_set_callback(c->io, io_callback, c);
+
+ pa_iochannel_socket_set_sndbuf(c->io, pa_memblockq_get_length(c->output_memblockq));
pa_ioline_unref(c->line);
- pa_xfree(c);
+ c->line = NULL;
+}
+
+static void handle_listen_prefix(struct connection *c, const char *source_name) {
+ pa_source *source;
+ pa_source_output_new_data data;
+ pa_sample_spec ss;
+ pa_channel_map cm;
+ char *t;
+ size_t l;
+
+ pa_assert(c);
+ pa_assert(source_name);
+
+ pa_assert(c->line);
+ pa_assert(!c->io);
+
+ if (!(source = pa_namereg_get(c->protocol->core, source_name, PA_NAMEREG_SOURCE))) {
+ html_response(c, 404, "Source not found", NULL);
+ return;
+ }
+
+ ss = source->sample_spec;
+ cm = source->channel_map;
+
+ pa_sample_spec_mimefy(&ss, &cm);
+
+ pa_source_output_new_data_init(&data);
+ data.driver = __FILE__;
+ data.module = c->module;
+ data.client = c->client;
+ data.source = source;
+ pa_proplist_update(data.proplist, PA_UPDATE_MERGE, c->client->proplist);
+ pa_source_output_new_data_set_sample_spec(&data, &ss);
+ pa_source_output_new_data_set_channel_map(&data, &cm);
+
+ pa_source_output_new(&c->source_output, c->protocol->core, &data, 0);
+ pa_source_output_new_data_done(&data);
+
+ if (!c->source_output) {
+ html_response(c, 403, "Cannot create source output", NULL);
+ return;
+ }
+
+ c->source_output->parent.process_msg = source_output_process_msg;
+ c->source_output->push = source_output_push_cb;
+ c->source_output->kill = source_output_kill_cb;
+ c->source_output->get_latency = source_output_get_latency_cb;
+ c->source_output->userdata = c;
+
+ pa_source_output_set_requested_latency(c->source_output, DEFAULT_SOURCE_LATENCY);
+
+ l = (size_t) (pa_bytes_per_second(&ss)*RECORD_BUFFER_SECONDS);
+ c->output_memblockq = pa_memblockq_new(
+ 0,
+ l,
+ 0,
+ pa_frame_size(&ss),
+ 1,
+ 0,
+ 0,
+ NULL);
+
+ pa_source_output_put(c->source_output);
+
+ t = pa_sample_spec_to_mime_type(&ss, &cm);
+ http_response(c, 200, "OK", t);
+ pa_xfree(t);
+
+ pa_ioline_set_callback(c->line, NULL, NULL);
+
+ if (pa_ioline_is_drained(c->line))
+ line_drain_callback(c->line, c);
+ else
+ pa_ioline_set_drain_callback(c->line, line_drain_callback, c);
+}
+
+static void handle_url(struct connection *c) {
+ pa_assert(c);
+
+ pa_log_debug("Request for %s", c->url);
+
+ if (pa_streq(c->url, URL_ROOT))
+ handle_root(c);
+ else if (pa_streq(c->url, URL_CSS))
+ handle_css(c);
+ else if (pa_streq(c->url, URL_STATUS))
+ handle_status(c);
+ else if (pa_streq(c->url, URL_LISTEN))
+ handle_listen(c);
+ else if (pa_startswith(c->url, URL_LISTEN_SOURCE))
+ handle_listen_prefix(c, c->url + sizeof(URL_LISTEN_SOURCE)-1);
+ else
+ html_response(c, 404, "Not Found", NULL);
}
static void line_callback(pa_ioline *line, const char *s, void *userdata) {
@@ -131,93 +605,27 @@ static void line_callback(pa_ioline *line, const char *s, void *userdata) {
}
switch (c->state) {
- case REQUEST_LINE: {
- if (memcmp(s, "GET ", 4))
+ case STATE_REQUEST_LINE: {
+ if (!pa_startswith(s, "GET "))
goto fail;
s +=4;
c->url = pa_xstrndup(s, strcspn(s, " \r\n\t?"));
- c->state = MIME_HEADER;
+ c->state = STATE_MIME_HEADER;
break;
-
}
- case MIME_HEADER: {
+ case STATE_MIME_HEADER: {
/* Ignore MIME headers */
if (strcspn(s, " \r\n") != 0)
break;
/* We're done */
- c->state = DATA;
-
- pa_log_info("request for %s", c->url);
-
- if (!strcmp(c->url, URL_ROOT)) {
- char txt[256];
- pa_sink *def_sink;
- pa_source *def_source;
- http_response(c, 200, "OK", "text/html");
-
- pa_ioline_puts(c->line,
- "<?xml version=\"1.0\"?>\n"
- "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n"
- "<html xmlns=\"http://www.w3.org/1999/xhtml\"><title>"PACKAGE_NAME" "PACKAGE_VERSION"</title>\n"
- "<link rel=\"stylesheet\" type=\"text/css\" href=\"style\"/></head><body>\n");
-
- pa_ioline_puts(c->line,
- "<h1>"PACKAGE_NAME" "PACKAGE_VERSION"</h1>\n"
- "<table>");
-
-#define PRINTF_FIELD(a,b) pa_ioline_printf(c->line, "<tr><td><b>%s</b></td><td>%s</td></tr>\n",(a),(b))
-
- PRINTF_FIELD("User Name:", pa_get_user_name(txt, sizeof(txt)));
- PRINTF_FIELD("Host name:", pa_get_host_name(txt, sizeof(txt)));
- PRINTF_FIELD("Default Sample Specification:", pa_sample_spec_snprint(txt, sizeof(txt), &c->protocol->core->default_sample_spec));
-
- def_sink = pa_namereg_get_default_sink(c->protocol->core);
- def_source = pa_namereg_get_default_source(c->protocol->core);
-
- PRINTF_FIELD("Default Sink:", def_sink ? def_sink->name : "n/a");
- PRINTF_FIELD("Default Source:", def_source ? def_source->name : "n/a");
-
- pa_ioline_puts(c->line, "</table>");
-
- pa_ioline_puts(c->line, "<p><a href=\"/status\">Click here</a> for an extensive server status report.</p>");
-
- pa_ioline_puts(c->line, "</body></html>\n");
-
- pa_ioline_defer_close(c->line);
- } else if (!strcmp(c->url, URL_CSS)) {
- http_response(c, 200, "OK", "text/css");
-
- pa_ioline_puts(c->line,
- "body { color: black; background-color: white; margin: 0.5cm; }\n"
- "a:link, a:visited { color: #900000; }\n"
- "p { margin-left: 0.5cm; margin-right: 0.5cm; }\n"
- "h1 { color: #00009F; }\n"
- "h2 { color: #00009F; }\n"
- "ul { margin-left: .5cm; }\n"
- "ol { margin-left: .5cm; }\n"
- "pre { margin-left: .5cm; background-color: #f0f0f0; padding: 0.4cm;}\n"
- ".grey { color: #afafaf; }\n"
- "table { margin-left: 1cm; border:1px solid lightgrey; padding: 0.2cm; }\n"
- "td { padding-left:10px; padding-right:10px; }\n");
-
- pa_ioline_defer_close(c->line);
- } else if (!strcmp(c->url, URL_STATUS)) {
- char *r;
-
- http_response(c, 200, "OK", "text/plain");
- r = pa_full_status_string(c->protocol->core);
- pa_ioline_puts(c->line, r);
- pa_xfree(r);
-
- pa_ioline_defer_close(c->line);
- } else
- http_message(c, 404, "Not Found", NULL);
+ c->state = STATE_DATA;
+ handle_url(c);
break;
}
@@ -228,11 +636,13 @@ static void line_callback(pa_ioline *line, const char *s, void *userdata) {
return;
fail:
- internal_server_error(c);
+ html_response(c, 500, "Internal Server Error", NULL);
}
void pa_http_protocol_connect(pa_http_protocol *p, pa_iochannel *io, pa_module *m) {
struct connection *c;
+ pa_client_new_data client_data;
+ char pname[128];
pa_assert(p);
pa_assert(io);
@@ -244,26 +654,46 @@ void pa_http_protocol_connect(pa_http_protocol *p, pa_iochannel *io, pa_module *
return;
}
- c = pa_xnew(struct connection, 1);
+ c = pa_xnew0(struct connection, 1);
c->protocol = p;
- c->line = pa_ioline_new(io);
- c->state = REQUEST_LINE;
- c->url = NULL;
+ c->state = STATE_REQUEST_LINE;
c->module = m;
+ c->line = pa_ioline_new(io);
pa_ioline_set_callback(c->line, line_callback, c);
+ pa_client_new_data_init(&client_data);
+ client_data.module = c->module;
+ client_data.driver = __FILE__;
+ pa_iochannel_socket_peer_to_string(io, pname, sizeof(pname));
+ pa_proplist_setf(client_data.proplist, PA_PROP_APPLICATION_NAME, "HTTP client (%s)", pname);
+ pa_proplist_sets(client_data.proplist, "http-protocol.peer", pname);
+ c->client = pa_client_new(p->core, &client_data);
+ pa_client_new_data_done(&client_data);
+
+ if (!c->client)
+ goto fail;
+
+ c->client->kill = client_kill_cb;
+ c->client->userdata = c;
+
pa_idxset_put(p->connections, c, NULL);
+
+ return;
+
+fail:
+ if (c)
+ connection_unlink(c);
}
void pa_http_protocol_disconnect(pa_http_protocol *p, pa_module *m) {
struct connection *c;
- void *state = NULL;
+ uint32_t idx;
pa_assert(p);
pa_assert(m);
- while ((c = pa_idxset_iterate(p->connections, &state, NULL)))
+ PA_IDXSET_FOREACH(c, p->connections, idx)
if (c->module == m)
connection_unlink(c);
}
@@ -273,7 +703,7 @@ static pa_http_protocol* http_protocol_new(pa_core *c) {
pa_assert(c);
- p = pa_xnew(pa_http_protocol, 1);
+ p = pa_xnew0(pa_http_protocol, 1);
PA_REFCNT_INIT(p);
p->core = c;
p->connections = pa_idxset_new(NULL, NULL);
@@ -315,7 +745,32 @@ void pa_http_protocol_unref(pa_http_protocol *p) {
pa_idxset_free(p->connections, NULL, NULL);
+ pa_strlist_free(p->servers);
+
pa_assert_se(pa_shared_remove(p->core, "http-protocol") >= 0);
pa_xfree(p);
}
+
+void pa_http_protocol_add_server_string(pa_http_protocol *p, const char *name) {
+ pa_assert(p);
+ pa_assert(PA_REFCNT_VALUE(p) >= 1);
+ pa_assert(name);
+
+ p->servers = pa_strlist_prepend(p->servers, name);
+}
+
+void pa_http_protocol_remove_server_string(pa_http_protocol *p, const char *name) {
+ pa_assert(p);
+ pa_assert(PA_REFCNT_VALUE(p) >= 1);
+ pa_assert(name);
+
+ p->servers = pa_strlist_remove(p->servers, name);
+}
+
+pa_strlist *pa_http_protocol_servers(pa_http_protocol *p) {
+ pa_assert(p);
+ pa_assert(PA_REFCNT_VALUE(p) >= 1);
+
+ return p->servers;
+}
diff --git a/src/pulsecore/protocol-http.h b/src/pulsecore/protocol-http.h
index 40b3d82c..f7517e81 100644
--- a/src/pulsecore/protocol-http.h
+++ b/src/pulsecore/protocol-http.h
@@ -26,7 +26,7 @@
#include <pulsecore/module.h>
#include <pulsecore/modargs.h>
#include <pulsecore/iochannel.h>
-
+#include <pulsecore/strlist.h>
typedef struct pa_http_protocol pa_http_protocol;
@@ -36,4 +36,8 @@ void pa_http_protocol_unref(pa_http_protocol *p);
void pa_http_protocol_connect(pa_http_protocol *p, pa_iochannel *io, pa_module *m);
void pa_http_protocol_disconnect(pa_http_protocol *p, pa_module *m);
+void pa_http_protocol_add_server_string(pa_http_protocol *p, const char *name);
+void pa_http_protocol_remove_server_string(pa_http_protocol *p, const char *name);
+pa_strlist *pa_http_protocol_servers(pa_http_protocol *p);
+
#endif
diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c
index aecaf71c..e9e2d601 100644
--- a/src/pulsecore/protocol-native.c
+++ b/src/pulsecore/protocol-native.c
@@ -3182,10 +3182,10 @@ static void command_get_info_list(pa_pdispatch *pd, uint32_t command, uint32_t t
static void command_get_server_info(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
pa_tagstruct *reply;
- char txt[256];
pa_sink *def_sink;
pa_source *def_source;
pa_sample_spec fixed_ss;
+ char *h, *u;
pa_native_connection_assert_ref(c);
pa_assert(t);
@@ -3200,8 +3200,14 @@ static void command_get_server_info(pa_pdispatch *pd, uint32_t command, uint32_t
reply = reply_new(tag);
pa_tagstruct_puts(reply, PACKAGE_NAME);
pa_tagstruct_puts(reply, PACKAGE_VERSION);
- pa_tagstruct_puts(reply, pa_get_user_name(txt, sizeof(txt)));
- pa_tagstruct_puts(reply, pa_get_host_name(txt, sizeof(txt)));
+
+ u = pa_get_user_name_malloc();
+ pa_tagstruct_puts(reply, u);
+ pa_xfree(u);
+
+ h = pa_get_host_name_malloc();
+ pa_tagstruct_puts(reply, h);
+ pa_xfree(h);
fixup_sample_spec(c, &fixed_ss, &c->protocol->core->default_sample_spec);
pa_tagstruct_put_sample_spec(reply, &fixed_ss);
@@ -4092,7 +4098,7 @@ static void command_suspend(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa
pa_log_debug("%s all sinks", b ? "Suspending" : "Resuming");
- if (pa_sink_suspend_all(c->protocol->core, b) < 0) {
+ if (pa_sink_suspend_all(c->protocol->core, b, PA_SUSPEND_USER) < 0) {
pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID);
return;
}
@@ -4106,7 +4112,7 @@ static void command_suspend(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa
CHECK_VALIDITY(c->pstream, sink, tag, PA_ERR_NOENTITY);
- if (pa_sink_suspend(sink, b) < 0) {
+ if (pa_sink_suspend(sink, b, PA_SUSPEND_USER) < 0) {
pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID);
return;
}
@@ -4119,7 +4125,7 @@ static void command_suspend(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa
pa_log_debug("%s all sources", b ? "Suspending" : "Resuming");
- if (pa_source_suspend_all(c->protocol->core, b) < 0) {
+ if (pa_source_suspend_all(c->protocol->core, b, PA_SUSPEND_USER) < 0) {
pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID);
return;
}
@@ -4134,7 +4140,7 @@ static void command_suspend(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa
CHECK_VALIDITY(c->pstream, source, tag, PA_ERR_NOENTITY);
- if (pa_source_suspend(source, b) < 0) {
+ if (pa_source_suspend(source, b, PA_SUSPEND_USER) < 0) {
pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID);
return;
}
diff --git a/src/pulsecore/protocol-simple.c b/src/pulsecore/protocol-simple.c
index 44fe5973..776d74b6 100644
--- a/src/pulsecore/protocol-simple.c
+++ b/src/pulsecore/protocol-simple.c
@@ -130,7 +130,7 @@ static void connection_unlink(connection *c) {
c->io = NULL;
}
- pa_assert_se(pa_idxset_remove_by_data(c->protocol->connections, c, NULL) == c);
+ pa_idxset_remove_by_data(c->protocol->connections, c, NULL);
c->protocol = NULL;
connection_unref(c);
}
diff --git a/src/pulsecore/sample-util.c b/src/pulsecore/sample-util.c
index 3a9b384d..dda38834 100644
--- a/src/pulsecore/sample-util.c
+++ b/src/pulsecore/sample-util.c
@@ -831,9 +831,9 @@ void pa_volume_memchunk(
calc_linear_integer_volume(linear, volume);
- e = (uint8_t*) ptr + c->length/3;
+ e = (uint8_t*) ptr + c->length;
- for (channel = 0, d = ptr; d < e; d++) {
+ for (channel = 0, d = ptr; d < e; d += 3) {
int64_t t;
t = (int64_t)((int32_t) (PA_READ24NE(d) << 8));
@@ -854,9 +854,9 @@ void pa_volume_memchunk(
calc_linear_integer_volume(linear, volume);
- e = (uint8_t*) ptr + c->length/3;
+ e = (uint8_t*) ptr + c->length;
- for (channel = 0, d = ptr; d < e; d++) {
+ for (channel = 0, d = ptr; d < e; d += 3) {
int64_t t;
t = (int64_t)((int32_t) (PA_READ24RE(d) << 8));
@@ -1181,6 +1181,8 @@ pa_memchunk* pa_silence_memchunk_get(pa_silence_cache *cache, pa_mempool *pool,
case PA_SAMPLE_S32BE:
case PA_SAMPLE_S24LE:
case PA_SAMPLE_S24BE:
+ case PA_SAMPLE_S24_32LE:
+ case PA_SAMPLE_S24_32RE:
case PA_SAMPLE_FLOAT32LE:
case PA_SAMPLE_FLOAT32BE:
cache->blocks[PA_SAMPLE_S16LE] = b = silence_memblock_new(pool, 0);
@@ -1189,6 +1191,8 @@ pa_memchunk* pa_silence_memchunk_get(pa_silence_cache *cache, pa_mempool *pool,
cache->blocks[PA_SAMPLE_S32BE] = pa_memblock_ref(b);
cache->blocks[PA_SAMPLE_S24LE] = pa_memblock_ref(b);
cache->blocks[PA_SAMPLE_S24BE] = pa_memblock_ref(b);
+ cache->blocks[PA_SAMPLE_S24_32LE] = pa_memblock_ref(b);
+ cache->blocks[PA_SAMPLE_S24_32BE] = pa_memblock_ref(b);
cache->blocks[PA_SAMPLE_FLOAT32LE] = pa_memblock_ref(b);
cache->blocks[PA_SAMPLE_FLOAT32BE] = pa_memblock_ref(b);
break;
diff --git a/src/pulsecore/sconv-s16le.c b/src/pulsecore/sconv-s16le.c
index 307ce7b7..43b8cb3e 100644
--- a/src/pulsecore/sconv-s16le.c
+++ b/src/pulsecore/sconv-s16le.c
@@ -370,7 +370,7 @@ void pa_sconv_s24_32le_to_s16ne(unsigned n, const uint32_t *a, int16_t *b) {
pa_assert(b);
for (; n > 0; n--) {
- *b = (int16_t) ((int32_t) (UINT32_FROM(*a) << 8) >> 16);
+ *b = (int16_t) (((int32_t) (UINT32_FROM(*a) << 8)) >> 16);
a++;
b++;
}
@@ -416,8 +416,8 @@ void pa_sconv_s24_32le_to_float32ne(unsigned n, const uint32_t *a, float *b) {
pa_assert(b);
for (; n > 0; n--) {
- int32_t s = (int16_t) ((int32_t) (UINT32_FROM(*a) << 8));
- *b = ((float) s) / 0x7FFFFFFF;
+ int32_t s = (int32_t) (UINT32_FROM(*a) << 8);
+ *b = (float) s / (float) 0x7FFFFFFF;
a ++;
b ++;
}
@@ -428,8 +428,8 @@ void pa_sconv_s24_32le_to_float32re(unsigned n, const uint32_t *a, float *b) {
pa_assert(b);
for (; n > 0; n--) {
- int32_t s = (int16_t) ((int32_t) (UINT32_FROM(*a) << 8));
- float k = ((float) s) / 0x7FFFFFFF;
+ int32_t s = (int32_t) (UINT32_FROM(*a) << 8);
+ float k = (float) s / (float) 0x7FFFFFFF;
*b = PA_FLOAT32_SWAP(k);
a ++;
b ++;
diff --git a/src/pulsecore/sconv.c b/src/pulsecore/sconv.c
index 29a9a453..d89f4283 100644
--- a/src/pulsecore/sconv.c
+++ b/src/pulsecore/sconv.c
@@ -75,7 +75,7 @@ static void u8_from_s16ne(unsigned n, const int16_t *a, uint8_t *b) {
pa_assert(b);
for (; n > 0; n--, a++, b++)
- *b = (uint8_t) (*a / 0x100 + 0x80);
+ *b = (uint8_t) ((uint16_t) *a >> 8) + (uint8_t) 0x80U;
}
/* float32 */
diff --git a/src/pulsecore/shm.c b/src/pulsecore/shm.c
index b8c5f786..fab2b3b6 100644
--- a/src/pulsecore/shm.c
+++ b/src/pulsecore/shm.c
@@ -69,7 +69,10 @@
#define SHM_MARKER ((int) 0xbeefcafe)
/* We now put this SHM marker at the end of each segment. It's
- * optional, to not require a reboot when upgrading, though */
+ * optional, to not require a reboot when upgrading, though. Note that
+ * on multiarch systems 32bit and 64bit processes might access this
+ * region simultaneously. The header fields need to be independant
+ * from the process' word with */
struct shm_marker {
pa_atomic_t marker; /* 0xbeefcafe */
pa_atomic_t pid;
@@ -79,6 +82,8 @@ struct shm_marker {
uint64_t _reserved4;
} PA_GCC_PACKED;
+#define SHM_MARKER_SIZE PA_ALIGN(sizeof(struct shm_marker))
+
static char *segment_name(char *fn, size_t l, unsigned id) {
pa_snprintf(fn, l, "/pulse-shm-%u", id);
return fn;
@@ -97,8 +102,8 @@ int pa_shm_create_rw(pa_shm *m, size_t size, pa_bool_t shared, mode_t mode) {
* ones */
pa_shm_cleanup();
- /* Round up to make it aligned */
- size = PA_ALIGN(size);
+ /* Round up to make it page aligned */
+ size = PA_PAGE_ALIGN(size);
if (!shared) {
m->id = 0;
@@ -136,25 +141,25 @@ int pa_shm_create_rw(pa_shm *m, size_t size, pa_bool_t shared, mode_t mode) {
goto fail;
}
- m->size = size + PA_ALIGN(sizeof(struct shm_marker));
+ m->size = size + SHM_MARKER_SIZE;
if (ftruncate(fd, (off_t) m->size) < 0) {
pa_log("ftruncate() failed: %s", pa_cstrerror(errno));
goto fail;
}
- if ((m->ptr = mmap(NULL, m->size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, (off_t) 0)) == MAP_FAILED) {
+ if ((m->ptr = mmap(NULL, PA_PAGE_ALIGN(m->size), PROT_READ|PROT_WRITE, MAP_SHARED, fd, (off_t) 0)) == MAP_FAILED) {
pa_log("mmap() failed: %s", pa_cstrerror(errno));
goto fail;
}
/* We store our PID at the end of the shm block, so that we
* can check for dead shm segments later */
- marker = (struct shm_marker*) ((uint8_t*) m->ptr + m->size - PA_ALIGN(sizeof(struct shm_marker)));
+ marker = (struct shm_marker*) ((uint8_t*) m->ptr + m->size - SHM_MARKER_SIZE);
pa_atomic_store(&marker->pid, (int) getpid());
pa_atomic_store(&marker->marker, SHM_MARKER);
- pa_assert_se(close(fd) == 0);
+ pa_assert_se(pa_close(fd) == 0);
m->do_unlink = TRUE;
#else
return -1;
@@ -197,7 +202,7 @@ void pa_shm_free(pa_shm *m) {
#endif
} else {
#ifdef HAVE_SHM_OPEN
- if (munmap(m->ptr, m->size) < 0)
+ if (munmap(m->ptr, PA_PAGE_ALIGN(m->size)) < 0)
pa_log("munmap() failed: %s", pa_cstrerror(errno));
if (m->do_unlink) {
@@ -214,12 +219,12 @@ void pa_shm_free(pa_shm *m) {
#endif
}
- memset(m, 0, sizeof(*m));
+ pa_zero(*m);
}
void pa_shm_punch(pa_shm *m, size_t offset, size_t size) {
void *ptr;
- size_t o, ps;
+ size_t o;
pa_assert(m);
pa_assert(m->ptr);
@@ -233,16 +238,19 @@ void pa_shm_punch(pa_shm *m, size_t offset, size_t size) {
/* You're welcome to implement this as NOOP on systems that don't
* support it */
- /* Align this to multiples of the page size */
+ /* Align the pointer up to multiples of the page size */
ptr = (uint8_t*) m->ptr + offset;
o = (size_t) ((uint8_t*) ptr - (uint8_t*) PA_PAGE_ALIGN_PTR(ptr));
if (o > 0) {
- ps = PA_PAGE_SIZE;
- ptr = (uint8_t*) ptr + (ps - o);
- size -= ps - o;
+ size_t delta = PA_PAGE_SIZE - o;
+ ptr = (uint8_t*) ptr + delta;
+ size -= delta;
}
+ /* Align the size down to multiples of page size */
+ size = (size / PA_PAGE_SIZE) * PA_PAGE_SIZE;
+
#ifdef MADV_REMOVE
if (madvise(ptr, size, MADV_REMOVE) >= 0)
return;
@@ -254,9 +262,9 @@ void pa_shm_punch(pa_shm *m, size_t offset, size_t size) {
#endif
#ifdef MADV_DONTNEED
- pa_assert_se(madvise(ptr, size, MADV_DONTNEED) == 0);
+ madvise(ptr, size, MADV_DONTNEED);
#elif defined(POSIX_MADV_DONTNEED)
- pa_assert_se(posix_madvise(ptr, size, POSIX_MADV_DONTNEED) == 0);
+ posix_madvise(ptr, size, POSIX_MADV_DONTNEED);
#endif
}
@@ -283,7 +291,7 @@ int pa_shm_attach_ro(pa_shm *m, unsigned id) {
}
if (st.st_size <= 0 ||
- st.st_size > (off_t) (MAX_SHM_SIZE+PA_ALIGN(sizeof(struct shm_marker))) ||
+ st.st_size > (off_t) (MAX_SHM_SIZE+SHM_MARKER_SIZE) ||
PA_ALIGN((size_t) st.st_size) != (size_t) st.st_size) {
pa_log("Invalid shared memory segment size");
goto fail;
@@ -291,13 +299,13 @@ int pa_shm_attach_ro(pa_shm *m, unsigned id) {
m->size = (size_t) st.st_size;
- if ((m->ptr = mmap(NULL, m->size, PROT_READ, MAP_SHARED, fd, (off_t) 0)) == MAP_FAILED) {
+ if ((m->ptr = mmap(NULL, PA_PAGE_ALIGN(m->size), PROT_READ, MAP_SHARED, fd, (off_t) 0)) == MAP_FAILED) {
pa_log("mmap() failed: %s", pa_cstrerror(errno));
goto fail;
}
- m->do_unlink = 0;
- m->shared = 1;
+ m->do_unlink = FALSE;
+ m->shared = TRUE;
pa_assert_se(pa_close(fd) == 0);
@@ -346,12 +354,12 @@ int pa_shm_cleanup(void) {
if (pa_shm_attach_ro(&seg, id) < 0)
continue;
- if (seg.size < PA_ALIGN(sizeof(struct shm_marker))) {
+ if (seg.size < SHM_MARKER_SIZE) {
pa_shm_free(&seg);
continue;
}
- m = (struct shm_marker*) ((uint8_t*) seg.ptr + seg.size - PA_ALIGN(sizeof(struct shm_marker)));
+ m = (struct shm_marker*) ((uint8_t*) seg.ptr + seg.size - SHM_MARKER_SIZE);
if (pa_atomic_load(&m->marker) != SHM_MARKER) {
pa_shm_free(&seg);
diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c
index 0229918c..0d05b00a 100644
--- a/src/pulsecore/sink-input.c
+++ b/src/pulsecore/sink-input.c
@@ -819,7 +819,7 @@ pa_usec_t pa_sink_input_set_requested_latency_within_thread(pa_sink_input *i, pa
usec = i->sink->fixed_latency;
if (usec != (pa_usec_t) -1)
- usec = PA_CLAMP(usec, i->sink->thread_info.min_latency, i->sink->thread_info.max_latency);
+ usec = PA_CLAMP(usec, i->sink->thread_info.min_latency, i->sink->thread_info.max_latency);
i->thread_info.requested_sink_latency = usec;
pa_sink_invalidate_requested_latency(i->sink);
diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
index 30fa5579..13f0e11e 100644
--- a/src/pulsecore/sink.c
+++ b/src/pulsecore/sink.c
@@ -190,6 +190,7 @@ pa_sink* pa_sink_new(
s->core = core;
s->state = PA_SINK_INIT;
s->flags = flags;
+ s->suspend_cause = 0;
s->name = pa_xstrdup(name);
s->proplist = pa_proplist_copy(data->proplist);
s->driver = pa_xstrdup(pa_path_get_filename(data->driver));
@@ -263,7 +264,9 @@ pa_sink* pa_sink_new(
pa_proplist_setf(source_data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Monitor of %s", dn ? dn : s->name);
pa_proplist_sets(source_data.proplist, PA_PROP_DEVICE_CLASS, "monitor");
- s->monitor_source = pa_source_new(core, &source_data, 0);
+ s->monitor_source = pa_source_new(core, &source_data,
+ ((flags & PA_SINK_LATENCY) ? PA_SOURCE_LATENCY : 0) |
+ ((flags & PA_SINK_DYNAMIC_LATENCY) ? PA_SOURCE_DYNAMIC_LATENCY : 0));
pa_source_new_data_done(&source_data);
@@ -349,31 +352,28 @@ void pa_sink_put(pa_sink* s) {
pa_assert(s->rtpoll);
pa_assert(s->thread_info.min_latency <= s->thread_info.max_latency);
- if (!(s->flags & PA_SINK_HW_VOLUME_CTRL)) {
+ /* Generally, flags should be initialized via pa_sink_new(). As a
+ * special exception we allow volume related flags to be set
+ * between _new() and _put(). */
+
+ if (!(s->flags & PA_SINK_HW_VOLUME_CTRL))
s->flags |= PA_SINK_DECIBEL_VOLUME;
- s->base_volume = PA_VOLUME_NORM;
- }
+
+ if ((s->flags & PA_SINK_DECIBEL_VOLUME) && s->core->flat_volumes)
+ s->flags |= PA_SINK_FLAT_VOLUME;
s->thread_info.soft_volume = s->soft_volume;
s->thread_info.soft_muted = s->muted;
- if (s->flags & PA_SINK_DECIBEL_VOLUME)
- s->n_volume_steps = PA_VOLUME_NORM+1;
-
- if (s->core->flat_volumes)
- if (s->flags & PA_SINK_DECIBEL_VOLUME)
- s->flags |= PA_SINK_FLAT_VOLUME;
-
- if (s->flags & PA_SINK_LATENCY)
- s->monitor_source->flags |= PA_SOURCE_LATENCY;
+ pa_assert((s->flags & PA_SINK_HW_VOLUME_CTRL) || (s->base_volume == PA_VOLUME_NORM && s->flags & PA_SINK_DECIBEL_VOLUME));
+ pa_assert(!(s->flags & PA_SINK_DECIBEL_VOLUME) || s->n_volume_steps == PA_VOLUME_NORM+1);
+ pa_assert(!(s->flags & PA_SINK_DYNAMIC_LATENCY) == (s->fixed_latency != 0));
+ pa_assert(!(s->flags & PA_SINK_LATENCY) == !(s->monitor_source->flags & PA_SOURCE_LATENCY));
+ pa_assert(!(s->flags & PA_SINK_DYNAMIC_LATENCY) == !(s->monitor_source->flags & PA_SOURCE_DYNAMIC_LATENCY));
- if (s->flags & PA_SINK_DYNAMIC_LATENCY) {
- s->monitor_source->flags |= PA_SOURCE_DYNAMIC_LATENCY;
- s->fixed_latency = 0;
- } else if (s->fixed_latency <= 0)
- s->fixed_latency = DEFAULT_FIXED_LATENCY;
-
- s->monitor_source->fixed_latency = s->fixed_latency;
+ pa_assert(s->monitor_source->fixed_latency == s->fixed_latency);
+ pa_assert(s->monitor_source->thread_info.min_latency == s->thread_info.min_latency);
+ pa_assert(s->monitor_source->thread_info.max_latency == s->thread_info.max_latency);
pa_assert_se(sink_set_state(s, PA_SINK_IDLE) == 0);
@@ -500,11 +500,25 @@ int pa_sink_update_status(pa_sink*s) {
}
/* Called from main context */
-int pa_sink_suspend(pa_sink *s, pa_bool_t suspend) {
+int pa_sink_suspend(pa_sink *s, pa_bool_t suspend, pa_suspend_cause_t cause) {
pa_sink_assert_ref(s);
pa_assert(PA_SINK_IS_LINKED(s->state));
+ pa_assert(cause != 0);
- if (suspend)
+ if (suspend) {
+ s->suspend_cause |= cause;
+ s->monitor_source->suspend_cause |= cause;
+ } else {
+ s->suspend_cause &= ~cause;
+ s->monitor_source->suspend_cause &= ~cause;
+ }
+
+ if ((pa_sink_get_state(s) == PA_SINK_SUSPENDED) == !!s->suspend_cause)
+ return 0;
+
+ pa_log_debug("Suspend cause of sink %s is 0x%04x, %s", s->name, s->suspend_cause, s->suspend_cause ? "suspending" : "resuming");
+
+ if (s->suspend_cause)
return sink_set_state(s, PA_SINK_SUSPENDED);
else
return sink_set_state(s, pa_sink_used_by(s) ? PA_SINK_RUNNING : PA_SINK_IDLE);
@@ -788,11 +802,17 @@ void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) {
pa_sw_cvolume_multiply(&volume, &s->thread_info.soft_volume, &info[0].volume);
if (s->thread_info.soft_muted || !pa_cvolume_is_norm(&volume)) {
- pa_memchunk_make_writable(result, 0);
- if (s->thread_info.soft_muted || pa_cvolume_is_muted(&volume))
- pa_silence_memchunk(result, &s->sample_spec);
- else
+ if (s->thread_info.soft_muted || pa_cvolume_is_muted(&volume)) {
+ pa_memblock_unref(result->memblock);
+ pa_silence_memchunk_get(&s->core->silence_cache,
+ s->core->mempool,
+ result,
+ &s->sample_spec,
+ result->length);
+ } else {
+ pa_memchunk_make_writable(result, 0);
pa_volume_memchunk(result, &s->sample_spec, &volume);
+ }
}
} else {
void *ptr;
@@ -933,22 +953,95 @@ void pa_sink_render_into_full(pa_sink *s, pa_memchunk *target) {
/* Called from IO thread context */
void pa_sink_render_full(pa_sink *s, size_t length, pa_memchunk *result) {
+ pa_mix_info info[MAX_MIX_CHANNELS];
+ size_t length1st = length;
+ unsigned n;
+
pa_sink_assert_ref(s);
pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
pa_assert(length > 0);
pa_assert(pa_frame_aligned(length, &s->sample_spec));
pa_assert(result);
+ pa_sink_ref(s);
+
pa_assert(!s->thread_info.rewind_requested);
pa_assert(s->thread_info.rewind_nbytes == 0);
- /*** This needs optimization ***/
+ pa_assert(length > 0);
- result->index = 0;
- result->length = length;
- result->memblock = pa_memblock_new(s->core->mempool, length);
+ n = fill_mix_info(s, &length1st, info, MAX_MIX_CHANNELS);
- pa_sink_render_into_full(s, result);
+ if (n == 0) {
+ pa_silence_memchunk_get(&s->core->silence_cache,
+ s->core->mempool,
+ result,
+ &s->sample_spec,
+ length1st);
+ } else if (n == 1) {
+ pa_cvolume volume;
+
+ *result = info[0].chunk;
+ pa_memblock_ref(result->memblock);
+
+ if (result->length > length)
+ result->length = length;
+
+ pa_sw_cvolume_multiply(&volume, &s->thread_info.soft_volume, &info[0].volume);
+
+ if (s->thread_info.soft_muted || !pa_cvolume_is_norm(&volume)) {
+ if (s->thread_info.soft_muted || pa_cvolume_is_muted(&volume)) {
+ pa_memblock_unref(result->memblock);
+ pa_silence_memchunk_get(&s->core->silence_cache,
+ s->core->mempool,
+ result,
+ &s->sample_spec,
+ result->length);
+ } else {
+ pa_memchunk_make_writable(result, length);
+ pa_volume_memchunk(result, &s->sample_spec, &volume);
+ }
+ }
+ } else {
+ void *ptr;
+
+ result->index = 0;
+ result->memblock = pa_memblock_new(s->core->mempool, length);
+
+ ptr = pa_memblock_acquire(result->memblock);
+
+ result->length = pa_mix(info, n,
+ (uint8_t*) ptr + result->index, length1st,
+ &s->sample_spec,
+ &s->thread_info.soft_volume,
+ s->thread_info.soft_muted);
+
+ pa_memblock_release(result->memblock);
+ }
+
+ inputs_drop(s, info, n, result);
+
+ if (result->length < length) {
+ pa_memchunk chunk;
+ size_t l, d;
+ pa_memchunk_make_writable(result, length);
+
+ l = length - result->length;
+ d = result->index + result->length;
+ while (l > 0) {
+ chunk = *result;
+ chunk.index = d;
+ chunk.length = l;
+
+ pa_sink_render_into(s, &chunk);
+
+ d += chunk.length;
+ l -= chunk.length;
+ }
+ result->length = length;
+ }
+
+ pa_sink_unref(s);
}
/* Called from main thread */
@@ -1272,8 +1365,12 @@ pa_bool_t pa_sink_get_mute(pa_sink *s, pa_bool_t force_refresh) {
pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_MUTE, NULL, 0, NULL) == 0);
- if (old_muted != s->muted)
+ if (old_muted != s->muted) {
pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+
+ /* Make sure the soft mute status stays in sync */
+ pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_MUTE, NULL, 0, NULL) == 0);
+ }
}
return s->muted;
@@ -1744,17 +1841,18 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse
}
/* Called from main thread */
-int pa_sink_suspend_all(pa_core *c, pa_bool_t suspend) {
+int pa_sink_suspend_all(pa_core *c, pa_bool_t suspend, pa_suspend_cause_t cause) {
pa_sink *sink;
uint32_t idx;
int ret = 0;
pa_core_assert_ref(c);
+ pa_assert(cause != 0);
for (sink = PA_SINK(pa_idxset_first(c->sinks, &idx)); sink; sink = PA_SINK(pa_idxset_next(c->sinks, &idx))) {
int r;
- if ((r = pa_sink_suspend(sink, suspend)) < 0)
+ if ((r = pa_sink_suspend(sink, suspend, cause)) < 0)
ret = r;
}
@@ -1863,8 +1961,11 @@ pa_usec_t pa_sink_get_requested_latency_within_thread(pa_sink *s) {
if (result != (pa_usec_t) -1)
result = PA_CLAMP(result, s->thread_info.min_latency, s->thread_info.max_latency);
- s->thread_info.requested_latency = result;
- s->thread_info.requested_latency_valid = TRUE;
+ if (PA_SINK_IS_LINKED(s->thread_info.state)) {
+ /* Only cache if properly initialized */
+ s->thread_info.requested_latency = result;
+ s->thread_info.requested_latency_valid = TRUE;
+ }
return result;
}
@@ -2050,6 +2151,22 @@ void pa_sink_set_latency_range_within_thread(pa_sink *s, pa_usec_t min_latency,
pa_source_set_latency_range_within_thread(s->monitor_source, min_latency, max_latency);
}
+/* Called from main thread, before the sink is put */
+void pa_sink_set_fixed_latency(pa_sink *s, pa_usec_t latency) {
+ pa_sink_assert_ref(s);
+
+ pa_assert(pa_sink_get_state(s) == PA_SINK_INIT);
+
+ if (latency < ABSOLUTE_MIN_LATENCY)
+ latency = ABSOLUTE_MIN_LATENCY;
+
+ if (latency > ABSOLUTE_MAX_LATENCY)
+ latency = ABSOLUTE_MAX_LATENCY;
+
+ s->fixed_latency = latency;
+ pa_source_set_fixed_latency(s->monitor_source, latency);
+}
+
/* Called from main context */
size_t pa_sink_get_max_rewind(pa_sink *s) {
size_t r;
@@ -2099,6 +2216,21 @@ pa_bool_t pa_device_init_icon(pa_proplist *p, pa_bool_t is_sink) {
t = "multimedia-player";
else if (pa_streq(ff, "tv"))
t = "video-display";
+
+ /*
+ * The following icons are not part of the icon naming spec,
+ * because Rodney Dawes sucks as the maintainer of that spec.
+ *
+ * http://lists.freedesktop.org/archives/xdg/2009-May/010397.html
+ */
+ else if (pa_streq(ff, "headset"))
+ t = "audio-headset";
+ else if (pa_streq(ff, "headphone"))
+ t = "audio-headphones";
+ else if (pa_streq(ff, "speaker"))
+ t = "audio-speakers";
+ else if (pa_streq(ff, "hands-free"))
+ t = "audio-handsfree";
}
if (!t)
diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h
index 352282b8..4dce3f93 100644
--- a/src/pulsecore/sink.h
+++ b/src/pulsecore/sink.h
@@ -56,6 +56,7 @@ struct pa_sink {
pa_core *core;
pa_sink_state_t state;
pa_sink_flags_t flags;
+ pa_suspend_cause_t suspend_cause;
char *name;
char *driver; /* may be NULL */
@@ -229,6 +230,7 @@ void pa_sink_set_rtpoll(pa_sink *s, pa_rtpoll *p);
void pa_sink_set_max_rewind(pa_sink *s, size_t max_rewind);
void pa_sink_set_max_request(pa_sink *s, size_t max_request);
void pa_sink_set_latency_range(pa_sink *s, pa_usec_t min_latency, pa_usec_t max_latency);
+void pa_sink_set_fixed_latency(pa_sink *s, pa_usec_t latency);
void pa_sink_detach(pa_sink *s);
void pa_sink_attach(pa_sink *s);
@@ -251,8 +253,8 @@ size_t pa_sink_get_max_rewind(pa_sink *s);
size_t pa_sink_get_max_request(pa_sink *s);
int pa_sink_update_status(pa_sink*s);
-int pa_sink_suspend(pa_sink *s, pa_bool_t suspend);
-int pa_sink_suspend_all(pa_core *c, pa_bool_t suspend);
+int pa_sink_suspend(pa_sink *s, pa_bool_t suspend, pa_suspend_cause_t cause);
+int pa_sink_suspend_all(pa_core *c, pa_bool_t suspend, pa_suspend_cause_t cause);
void pa_sink_update_flat_volume(pa_sink *s, pa_cvolume *new_volume);
void pa_sink_propagate_flat_volume(pa_sink *s);
diff --git a/src/pulsecore/sndfile-util.c b/src/pulsecore/sndfile-util.c
new file mode 100644
index 00000000..032aefca
--- /dev/null
+++ b/src/pulsecore/sndfile-util.c
@@ -0,0 +1,462 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2009 Lennart Poettering
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2.1 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+/* Shared between pacat/parec/paplay and the server */
+
+#include <pulse/xmalloc.h>
+#include <pulse/utf8.h>
+
+#include <pulsecore/macro.h>
+#include <pulsecore/core-util.h>
+
+#include "sndfile-util.h"
+
+int pa_sndfile_read_sample_spec(SNDFILE *sf, pa_sample_spec *ss) {
+ SF_INFO sfi;
+
+ pa_assert(sf);
+ pa_assert(ss);
+
+ pa_zero(sfi);
+ pa_assert_se(sf_command(sf, SFC_GET_CURRENT_SF_INFO, &sfi, sizeof(sfi)) == 0);
+
+ switch (sfi.format & SF_FORMAT_SUBMASK) {
+
+ case SF_FORMAT_PCM_16:
+ case SF_FORMAT_PCM_U8:
+ case SF_FORMAT_PCM_S8:
+ ss->format = PA_SAMPLE_S16NE;
+ break;
+
+ case SF_FORMAT_PCM_24:
+ case SF_FORMAT_PCM_32:
+ ss->format = PA_SAMPLE_S32NE;
+ break;
+
+ case SF_FORMAT_ULAW:
+ ss->format = PA_SAMPLE_ULAW;
+ break;
+
+ case SF_FORMAT_ALAW:
+ ss->format = PA_SAMPLE_ALAW;
+ break;
+
+ case SF_FORMAT_FLOAT:
+ case SF_FORMAT_DOUBLE:
+ default:
+ ss->format = PA_SAMPLE_FLOAT32NE;
+ break;
+ }
+
+ ss->rate = (uint32_t) sfi.samplerate;
+ ss->channels = (uint8_t) sfi.channels;
+
+ if (!pa_sample_spec_valid(ss))
+ return -1;
+
+ return 0;
+}
+
+int pa_sndfile_write_sample_spec(SF_INFO *sfi, pa_sample_spec *ss) {
+ pa_assert(sfi);
+ pa_assert(ss);
+
+ sfi->samplerate = (int) ss->rate;
+ sfi->channels = (int) ss->channels;
+
+ if (pa_sample_format_is_le(ss->format) > 0)
+ sfi->format = SF_ENDIAN_LITTLE;
+ else if (pa_sample_format_is_be(ss->format) > 0)
+ sfi->format = SF_ENDIAN_BIG;
+
+ switch (ss->format) {
+
+ case PA_SAMPLE_U8:
+ ss->format = PA_SAMPLE_S16NE;
+ sfi->format = SF_FORMAT_PCM_U8;
+ break;
+
+ case PA_SAMPLE_S16LE:
+ case PA_SAMPLE_S16BE:
+ ss->format = PA_SAMPLE_S16NE;
+ sfi->format |= SF_FORMAT_PCM_16;
+ break;
+
+ case PA_SAMPLE_S24LE:
+ case PA_SAMPLE_S24BE:
+ case PA_SAMPLE_S24_32LE:
+ case PA_SAMPLE_S24_32BE:
+ ss->format = PA_SAMPLE_S32NE;
+ sfi->format |= SF_FORMAT_PCM_24;
+ break;
+
+ case PA_SAMPLE_S32LE:
+ case PA_SAMPLE_S32RE:
+ ss->format = PA_SAMPLE_S32NE;
+ sfi->format |= SF_FORMAT_PCM_32;
+ break;
+
+ case PA_SAMPLE_ULAW:
+ sfi->format = SF_FORMAT_ULAW;
+ break;
+
+ case PA_SAMPLE_ALAW:
+ sfi->format = SF_FORMAT_ALAW;
+ break;
+
+ case PA_SAMPLE_FLOAT32LE:
+ case PA_SAMPLE_FLOAT32BE:
+ default:
+ ss->format = PA_SAMPLE_FLOAT32NE;
+ sfi->format |= SF_FORMAT_FLOAT;
+ break;
+ }
+
+
+ if (!pa_sample_spec_valid(ss))
+ return -1;
+
+ return 0;
+}
+
+int pa_sndfile_read_channel_map(SNDFILE *sf, pa_channel_map *cm) {
+
+ static const pa_channel_position_t table[] = {
+ [SF_CHANNEL_MAP_MONO] = PA_CHANNEL_POSITION_MONO,
+ [SF_CHANNEL_MAP_LEFT] = PA_CHANNEL_POSITION_FRONT_LEFT, /* libsndfile distuingishes left und front-left, which we don't */
+ [SF_CHANNEL_MAP_RIGHT] = PA_CHANNEL_POSITION_FRONT_RIGHT,
+ [SF_CHANNEL_MAP_CENTER] = PA_CHANNEL_POSITION_FRONT_CENTER,
+ [SF_CHANNEL_MAP_FRONT_LEFT] = PA_CHANNEL_POSITION_FRONT_LEFT,
+ [SF_CHANNEL_MAP_FRONT_RIGHT] = PA_CHANNEL_POSITION_FRONT_RIGHT,
+ [SF_CHANNEL_MAP_FRONT_CENTER] = PA_CHANNEL_POSITION_FRONT_CENTER,
+ [SF_CHANNEL_MAP_REAR_CENTER] = PA_CHANNEL_POSITION_REAR_CENTER,
+ [SF_CHANNEL_MAP_REAR_LEFT] = PA_CHANNEL_POSITION_REAR_LEFT,
+ [SF_CHANNEL_MAP_REAR_RIGHT] = PA_CHANNEL_POSITION_REAR_RIGHT,
+ [SF_CHANNEL_MAP_LFE] = PA_CHANNEL_POSITION_LFE,
+ [SF_CHANNEL_MAP_FRONT_LEFT_OF_CENTER] = PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER,
+ [SF_CHANNEL_MAP_FRONT_RIGHT_OF_CENTER] = PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER,
+ [SF_CHANNEL_MAP_SIDE_LEFT] = PA_CHANNEL_POSITION_SIDE_LEFT,
+ [SF_CHANNEL_MAP_SIDE_RIGHT] = PA_CHANNEL_POSITION_SIDE_RIGHT,
+ [SF_CHANNEL_MAP_TOP_CENTER] = PA_CHANNEL_POSITION_TOP_CENTER,
+ [SF_CHANNEL_MAP_TOP_FRONT_LEFT] = PA_CHANNEL_POSITION_TOP_FRONT_LEFT,
+ [SF_CHANNEL_MAP_TOP_FRONT_RIGHT] = PA_CHANNEL_POSITION_TOP_FRONT_RIGHT,
+ [SF_CHANNEL_MAP_TOP_FRONT_CENTER] = PA_CHANNEL_POSITION_TOP_FRONT_CENTER,
+ [SF_CHANNEL_MAP_TOP_REAR_LEFT] = PA_CHANNEL_POSITION_TOP_REAR_LEFT,
+ [SF_CHANNEL_MAP_TOP_REAR_RIGHT] = PA_CHANNEL_POSITION_TOP_REAR_RIGHT,
+ [SF_CHANNEL_MAP_TOP_REAR_CENTER] = PA_CHANNEL_POSITION_TOP_REAR_CENTER
+ };
+
+ SF_INFO sfi;
+ int *channels;
+ unsigned c;
+
+ pa_assert(sf);
+ pa_assert(cm);
+
+ pa_zero(sfi);
+ pa_assert_se(sf_command(sf, SFC_GET_CURRENT_SF_INFO, &sfi, sizeof(sfi)) == 0);
+
+ channels = pa_xnew(int, sfi.channels);
+ if (!sf_command(sf, SFC_GET_CHANNEL_MAP_INFO,
+ channels, sizeof(channels[0]) * sfi.channels)) {
+
+ pa_xfree(channels);
+ return -1;
+ }
+
+ cm->channels = (uint8_t) sfi.channels;
+ for (c = 0; c < cm->channels; c++) {
+ if (channels[c] <= SF_CHANNEL_MAP_INVALID ||
+ (unsigned) channels[c] >= PA_ELEMENTSOF(table)) {
+ pa_xfree(channels);
+ return -1;
+ }
+
+ cm->map[c] = table[channels[c]];
+ }
+
+ pa_xfree(channels);
+
+ if (!pa_channel_map_valid(cm))
+ return -1;
+
+ return 0;
+}
+
+int pa_sndfile_write_channel_map(SNDFILE *sf, pa_channel_map *cm) {
+ static const int table[PA_CHANNEL_POSITION_MAX] = {
+ [PA_CHANNEL_POSITION_MONO] = SF_CHANNEL_MAP_MONO,
+
+ [PA_CHANNEL_POSITION_FRONT_LEFT] = SF_CHANNEL_MAP_FRONT_LEFT,
+ [PA_CHANNEL_POSITION_FRONT_RIGHT] = SF_CHANNEL_MAP_FRONT_RIGHT,
+ [PA_CHANNEL_POSITION_FRONT_CENTER] = SF_CHANNEL_MAP_FRONT_CENTER,
+
+ [PA_CHANNEL_POSITION_REAR_CENTER] = SF_CHANNEL_MAP_REAR_CENTER,
+ [PA_CHANNEL_POSITION_REAR_LEFT] = SF_CHANNEL_MAP_REAR_LEFT,
+ [PA_CHANNEL_POSITION_REAR_RIGHT] = SF_CHANNEL_MAP_REAR_RIGHT,
+
+ [PA_CHANNEL_POSITION_LFE] = SF_CHANNEL_MAP_LFE,
+
+ [PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER] = SF_CHANNEL_MAP_FRONT_LEFT_OF_CENTER,
+ [PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER] = SF_CHANNEL_MAP_FRONT_RIGHT_OF_CENTER,
+
+ [PA_CHANNEL_POSITION_SIDE_LEFT] = SF_CHANNEL_MAP_SIDE_LEFT,
+ [PA_CHANNEL_POSITION_SIDE_RIGHT] = SF_CHANNEL_MAP_SIDE_RIGHT,
+
+ [PA_CHANNEL_POSITION_AUX0] = -1,
+ [PA_CHANNEL_POSITION_AUX1] = -1,
+ [PA_CHANNEL_POSITION_AUX2] = -1,
+ [PA_CHANNEL_POSITION_AUX3] = -1,
+ [PA_CHANNEL_POSITION_AUX4] = -1,
+ [PA_CHANNEL_POSITION_AUX5] = -1,
+ [PA_CHANNEL_POSITION_AUX6] = -1,
+ [PA_CHANNEL_POSITION_AUX7] = -1,
+ [PA_CHANNEL_POSITION_AUX8] = -1,
+ [PA_CHANNEL_POSITION_AUX9] = -1,
+ [PA_CHANNEL_POSITION_AUX10] = -1,
+ [PA_CHANNEL_POSITION_AUX11] = -1,
+ [PA_CHANNEL_POSITION_AUX12] = -1,
+ [PA_CHANNEL_POSITION_AUX13] = -1,
+ [PA_CHANNEL_POSITION_AUX14] = -1,
+ [PA_CHANNEL_POSITION_AUX15] = -1,
+ [PA_CHANNEL_POSITION_AUX16] = -1,
+ [PA_CHANNEL_POSITION_AUX17] = -1,
+ [PA_CHANNEL_POSITION_AUX18] = -1,
+ [PA_CHANNEL_POSITION_AUX19] = -1,
+ [PA_CHANNEL_POSITION_AUX20] = -1,
+ [PA_CHANNEL_POSITION_AUX21] = -1,
+ [PA_CHANNEL_POSITION_AUX22] = -1,
+ [PA_CHANNEL_POSITION_AUX23] = -1,
+ [PA_CHANNEL_POSITION_AUX24] = -1,
+ [PA_CHANNEL_POSITION_AUX25] = -1,
+ [PA_CHANNEL_POSITION_AUX26] = -1,
+ [PA_CHANNEL_POSITION_AUX27] = -1,
+ [PA_CHANNEL_POSITION_AUX28] = -1,
+ [PA_CHANNEL_POSITION_AUX29] = -1,
+ [PA_CHANNEL_POSITION_AUX30] = -1,
+ [PA_CHANNEL_POSITION_AUX31] = -1,
+
+ [PA_CHANNEL_POSITION_TOP_CENTER] = SF_CHANNEL_MAP_TOP_CENTER,
+
+ [PA_CHANNEL_POSITION_TOP_FRONT_LEFT] = SF_CHANNEL_MAP_TOP_FRONT_LEFT,
+ [PA_CHANNEL_POSITION_TOP_FRONT_RIGHT] = SF_CHANNEL_MAP_TOP_FRONT_RIGHT,
+ [PA_CHANNEL_POSITION_TOP_FRONT_CENTER] = SF_CHANNEL_MAP_TOP_FRONT_CENTER ,
+
+ [PA_CHANNEL_POSITION_TOP_REAR_LEFT] = SF_CHANNEL_MAP_TOP_REAR_LEFT,
+ [PA_CHANNEL_POSITION_TOP_REAR_RIGHT] = SF_CHANNEL_MAP_TOP_REAR_RIGHT,
+ [PA_CHANNEL_POSITION_TOP_REAR_CENTER] = SF_CHANNEL_MAP_TOP_REAR_CENTER,
+ };
+
+ int *channels;
+ unsigned c;
+
+ pa_assert(sf);
+ pa_assert(cm);
+
+ /* Suppress channel mapping for the obvious cases */
+ if (cm->channels == 1 && cm->map[0] == PA_CHANNEL_POSITION_MONO)
+ return 0;
+
+ if (cm->channels == 2 &&
+ cm->map[0] == PA_CHANNEL_POSITION_FRONT_LEFT &&
+ cm->map[1] == PA_CHANNEL_POSITION_FRONT_RIGHT)
+ return 0;
+
+ channels = pa_xnew(int, cm->channels);
+ for (c = 0; c < cm->channels; c++) {
+
+ if (cm->map[c] < 0 ||
+ cm->map[c] >= PA_CHANNEL_POSITION_MAX ||
+ table[cm->map[c]] < 0) {
+ pa_xfree(channels);
+ return -1;
+ }
+
+ channels[c] = table[cm->map[c]];
+ }
+
+ if (!sf_command(sf, SFC_SET_CHANNEL_MAP_INFO,
+ channels, sizeof(channels[0]) * cm->channels)) {
+ pa_xfree(channels);
+ return -1;
+ }
+
+ pa_xfree(channels);
+ return 0;
+}
+
+void pa_sndfile_init_proplist(SNDFILE *sf, pa_proplist *p) {
+
+ static const char* table[] = {
+ [SF_STR_TITLE] = PA_PROP_MEDIA_TITLE,
+ [SF_STR_COPYRIGHT] = PA_PROP_MEDIA_COPYRIGHT,
+ [SF_STR_SOFTWARE] = PA_PROP_MEDIA_SOFTWARE,
+ [SF_STR_ARTIST] = PA_PROP_MEDIA_ARTIST,
+ [SF_STR_COMMENT] = "media.comment",
+ [SF_STR_DATE] = "media.date"
+ };
+
+ SF_INFO sfi;
+ SF_FORMAT_INFO fi;
+ unsigned c;
+
+ pa_assert(sf);
+ pa_assert(p);
+
+ for (c = 0; c < PA_ELEMENTSOF(table); c++) {
+ const char *s;
+ char *t;
+
+ if (!table[c])
+ continue;
+
+ if (!(s = sf_get_string(sf, c)))
+ continue;
+
+ t = pa_utf8_filter(s);
+ pa_proplist_sets(p, table[c], t);
+ pa_xfree(t);
+ }
+
+ pa_zero(sfi);
+ pa_assert_se(sf_command(sf, SFC_GET_CURRENT_SF_INFO, &sfi, sizeof(sfi)) == 0);
+
+ pa_zero(fi);
+ fi.format = sfi.format;
+ if (sf_command(sf, SFC_GET_FORMAT_INFO, &fi, sizeof(fi)) == 0 && fi.name) {
+ char *t;
+
+ t = pa_utf8_filter(fi.name);
+ pa_proplist_sets(p, "media.format", t);
+ pa_xfree(t);
+ }
+}
+
+pa_sndfile_readf_t pa_sndfile_readf_function(const pa_sample_spec *ss) {
+ pa_assert(ss);
+
+ switch (ss->format) {
+ case PA_SAMPLE_S16NE:
+ return (pa_sndfile_readf_t) sf_readf_short;
+
+ case PA_SAMPLE_S32NE:
+ return (pa_sndfile_readf_t) sf_readf_int;
+
+ case PA_SAMPLE_FLOAT32NE:
+ return (pa_sndfile_readf_t) sf_readf_float;
+
+ case PA_SAMPLE_ULAW:
+ case PA_SAMPLE_ALAW:
+ return NULL;
+
+ default:
+ pa_assert_not_reached();
+ }
+}
+
+pa_sndfile_writef_t pa_sndfile_writef_function(const pa_sample_spec *ss) {
+ pa_assert(ss);
+
+ switch (ss->format) {
+ case PA_SAMPLE_S16NE:
+ return (pa_sndfile_writef_t) sf_writef_short;
+
+ case PA_SAMPLE_S32NE:
+ return (pa_sndfile_writef_t) sf_writef_int;
+
+ case PA_SAMPLE_FLOAT32NE:
+ return (pa_sndfile_writef_t) sf_writef_float;
+
+ case PA_SAMPLE_ULAW:
+ case PA_SAMPLE_ALAW:
+ return NULL;
+
+ default:
+ pa_assert_not_reached();
+ }
+}
+
+int pa_sndfile_format_from_string(const char *name) {
+ int i, count = 0;
+
+
+ if (!name[0])
+ return -1;
+
+ pa_assert_se(sf_command(NULL, SFC_GET_FORMAT_MAJOR_COUNT, &count, sizeof(int)) == 0);
+
+ /* First try to match via full type string */
+ for (i = 0; i < count; i++) {
+ SF_FORMAT_INFO fi;
+ pa_zero(fi);
+ fi.format = i;
+
+ pa_assert_se(sf_command(NULL, SFC_GET_FORMAT_MAJOR, &fi, sizeof(fi)) == 0);
+
+ if (strcasecmp(name, fi.name) == 0)
+ return i;
+ }
+
+ /* Then, try to match via the full extension */
+ for (i = 0; i < count; i++) {
+ SF_FORMAT_INFO fi;
+ pa_zero(fi);
+ fi.format = i;
+
+ pa_assert_se(sf_command(NULL, SFC_GET_FORMAT_MAJOR, &fi, sizeof(fi)) == 0);
+
+ if (strcasecmp(name, fi.extension) == 0)
+ return i;
+ }
+
+ /* Then, try to match via the start of the type string */
+ for (i = 0; i < count; i++) {
+ SF_FORMAT_INFO fi;
+ pa_zero(fi);
+ fi.format = i;
+
+ pa_assert_se(sf_command(NULL, SFC_GET_FORMAT_MAJOR, &fi, sizeof(fi)) == 0);
+
+ if (strncasecmp(name, fi.extension, strlen(name)) == 0)
+ return i;
+ }
+
+ return -1;
+}
+
+void pa_sndfile_dump_formats(void) {
+ int i, count = 0;
+
+ pa_assert_se(sf_command(NULL, SFC_GET_FORMAT_MAJOR_COUNT, &count, sizeof(int)) == 0);
+
+ for (i = 0; i < count; i++) {
+ SF_FORMAT_INFO fi;
+ pa_zero(fi);
+ fi.format = i;
+
+ pa_assert_se(sf_command(NULL, SFC_GET_FORMAT_MAJOR, &fi, sizeof(fi)) == 0);
+ printf("%s\t%s\n", fi.extension, fi.name);
+ }
+}
diff --git a/src/pulsecore/sndfile-util.h b/src/pulsecore/sndfile-util.h
new file mode 100644
index 00000000..74021da1
--- /dev/null
+++ b/src/pulsecore/sndfile-util.h
@@ -0,0 +1,52 @@
+#ifndef foopulsecoresndfileutilhfoo
+#define foopulsecoresndfileutilhfoo
+
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2009 Lennart Poettering
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2.1 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <sndfile.h>
+
+#include <pulse/sample.h>
+#include <pulse/channelmap.h>
+#include <pulse/proplist.h>
+
+int pa_sndfile_read_sample_spec(SNDFILE *sf, pa_sample_spec *ss);
+int pa_sndfile_read_channel_map(SNDFILE *sf, pa_channel_map *cm);
+
+int pa_sndfile_write_sample_spec(SF_INFO *sfi, pa_sample_spec *ss);
+int pa_sndfile_write_channel_map(SNDFILE *sf, pa_channel_map *cm);
+
+void pa_sndfile_init_proplist(SNDFILE *sf, pa_proplist *p);
+
+typedef sf_count_t (*pa_sndfile_readf_t)(SNDFILE *sndfile, void *ptr, sf_count_t frames);
+typedef sf_count_t (*pa_sndfile_writef_t)(SNDFILE *sndfile, const void *ptr, sf_count_t frames);
+
+/* Returns NULL if sf_read_raw() shall be used */
+pa_sndfile_readf_t pa_sndfile_readf_function(const pa_sample_spec *ss);
+
+/* Returns NULL if sf_write_raw() shall be used */
+pa_sndfile_writef_t pa_sndfile_writef_function(const pa_sample_spec *ss);
+
+int pa_sndfile_format_from_string(const char *extension);
+
+void pa_sndfile_dump_formats(void);
+
+#endif
diff --git a/src/pulsecore/sound-file-stream.c b/src/pulsecore/sound-file-stream.c
index 3453637f..502e5c69 100644
--- a/src/pulsecore/sound-file-stream.c
+++ b/src/pulsecore/sound-file-stream.c
@@ -41,6 +41,7 @@
#include <pulsecore/thread-mq.h>
#include <pulsecore/core-util.h>
#include <pulsecore/sample-util.h>
+#include <pulsecore/sndfile-util.h>
#include "sound-file-stream.h"
@@ -234,10 +235,11 @@ int pa_play_file(
const pa_cvolume *volume) {
file_stream *u = NULL;
- SF_INFO sfinfo;
pa_sample_spec ss;
+ pa_channel_map cm;
pa_sink_input_new_data data;
int fd;
+ SF_INFO sfi;
pa_assert(sink);
pa_assert(fname);
@@ -251,8 +253,6 @@ int pa_play_file(
u->readf_function = NULL;
u->memblockq = NULL;
- memset(&sfinfo, 0, sizeof(sfinfo));
-
if ((fd = open(fname, O_RDONLY
#ifdef O_NOCTTY
|O_NOCTTY
@@ -281,50 +281,36 @@ int pa_play_file(
pa_log_debug("POSIX_FADV_WILLNEED succeeded.");
#endif
- if (!(u->sndfile = sf_open_fd(fd, SFM_READ, &sfinfo, 1))) {
+ pa_zero(sfi);
+ if (!(u->sndfile = sf_open_fd(fd, SFM_READ, &sfi, 1))) {
pa_log("Failed to open file %s", fname);
- pa_close(fd);
goto fail;
}
- switch (sfinfo.format & 0xFF) {
- case SF_FORMAT_PCM_16:
- case SF_FORMAT_PCM_U8:
- case SF_FORMAT_PCM_S8:
- ss.format = PA_SAMPLE_S16NE;
- u->readf_function = (sf_count_t (*)(SNDFILE *sndfile, void *ptr, sf_count_t frames)) sf_readf_short;
- break;
-
- case SF_FORMAT_ULAW:
- ss.format = PA_SAMPLE_ULAW;
- break;
-
- case SF_FORMAT_ALAW:
- ss.format = PA_SAMPLE_ALAW;
- break;
+ fd = -1;
- case SF_FORMAT_FLOAT:
- default:
- ss.format = PA_SAMPLE_FLOAT32NE;
- u->readf_function = (sf_count_t (*)(SNDFILE *sndfile, void *ptr, sf_count_t frames)) sf_readf_float;
- break;
+ if (pa_sndfile_read_sample_spec(u->sndfile, &ss) < 0) {
+ pa_log("Failed to determine file sample format.");
+ goto fail;
}
- ss.rate = (uint32_t) sfinfo.samplerate;
- ss.channels = (uint8_t) sfinfo.channels;
-
- if (!pa_sample_spec_valid(&ss)) {
- pa_log("Unsupported sample format in file %s", fname);
- goto fail;
+ if (pa_sndfile_read_channel_map(u->sndfile, &cm) < 0) {
+ if (ss.channels > 2)
+ pa_log_info("Failed to determine file channel map, synthesizing one.");
+ pa_channel_map_init_extend(&cm, ss.channels, PA_CHANNEL_MAP_DEFAULT);
}
+ u->readf_function = pa_sndfile_readf_function(&ss);
+
pa_sink_input_new_data_init(&data);
data.sink = sink;
data.driver = __FILE__;
pa_sink_input_new_data_set_sample_spec(&data, &ss);
+ pa_sink_input_new_data_set_channel_map(&data, &cm);
pa_sink_input_new_data_set_volume(&data, volume);
pa_proplist_sets(data.proplist, PA_PROP_MEDIA_NAME, pa_path_get_filename(fname));
pa_proplist_sets(data.proplist, PA_PROP_MEDIA_FILENAME, fname);
+ pa_sndfile_init_proplist(u->sndfile, data.proplist);
pa_sink_input_new(&u->sink_input, sink->core, &data, 0);
pa_sink_input_new_data_done(&data);
@@ -352,5 +338,8 @@ fail:
if (u)
file_stream_unref(u);
+ if (fd >= 0)
+ pa_close(fd);
+
return -1;
}
diff --git a/src/pulsecore/sound-file.c b/src/pulsecore/sound-file.c
index db75ae08..2d9b76ad 100644
--- a/src/pulsecore/sound-file.c
+++ b/src/pulsecore/sound-file.c
@@ -35,19 +35,21 @@
#include <pulsecore/macro.h>
#include <pulsecore/core-error.h>
#include <pulsecore/core-util.h>
+#include <pulsecore/core-scache.h>
+#include <pulsecore/sndfile-util.h>
#include "sound-file.h"
-#include "core-scache.h"
int pa_sound_file_load(
pa_mempool *pool,
const char *fname,
pa_sample_spec *ss,
pa_channel_map *map,
- pa_memchunk *chunk) {
+ pa_memchunk *chunk,
+ pa_proplist *p) {
SNDFILE *sf = NULL;
- SF_INFO sfinfo;
+ SF_INFO sfi;
int ret = -1;
size_t l;
sf_count_t (*readf_function)(SNDFILE *sndfile, void *ptr, sf_count_t frames) = NULL;
@@ -59,7 +61,6 @@ int pa_sound_file_load(
pa_assert(chunk);
pa_memchunk_reset(chunk);
- memset(&sfinfo, 0, sizeof(sfinfo));
if ((fd = open(fname, O_RDONLY
#ifdef O_NOCTTY
@@ -78,48 +79,29 @@ int pa_sound_file_load(
pa_log_debug("POSIX_FADV_SEQUENTIAL succeeded.");
#endif
- if (!(sf = sf_open_fd(fd, SFM_READ, &sfinfo, 1))) {
+ pa_zero(sfi);
+ if (!(sf = sf_open_fd(fd, SFM_READ, &sfi, 1))) {
pa_log("Failed to open file %s", fname);
- pa_close(fd);
goto finish;
}
- switch (sfinfo.format & SF_FORMAT_SUBMASK) {
- case SF_FORMAT_PCM_16:
- case SF_FORMAT_PCM_U8:
- case SF_FORMAT_PCM_S8:
- ss->format = PA_SAMPLE_S16NE;
- readf_function = (sf_count_t (*)(SNDFILE *sndfile, void *_ptr, sf_count_t frames)) sf_readf_short;
- break;
-
- case SF_FORMAT_ULAW:
- ss->format = PA_SAMPLE_ULAW;
- break;
-
- case SF_FORMAT_ALAW:
- ss->format = PA_SAMPLE_ALAW;
- break;
-
- case SF_FORMAT_FLOAT:
- case SF_FORMAT_DOUBLE:
- default:
- ss->format = PA_SAMPLE_FLOAT32NE;
- readf_function = (sf_count_t (*)(SNDFILE *sndfile, void *_ptr, sf_count_t frames)) sf_readf_float;
- break;
- }
-
- ss->rate = (uint32_t) sfinfo.samplerate;
- ss->channels = (uint8_t) sfinfo.channels;
+ fd = -1;
- if (!pa_sample_spec_valid(ss)) {
- pa_log("Unsupported sample format in file %s", fname);
+ if (pa_sndfile_read_sample_spec(sf, ss) < 0) {
+ pa_log("Failed to determine file sample format.");
goto finish;
}
- if (map)
+ if ((map && pa_sndfile_read_channel_map(sf, map) < 0)) {
+ if (ss->channels > 2)
+ pa_log("Failed to determine file channel map, synthesizing one.");
pa_channel_map_init_extend(map, ss->channels, PA_CHANNEL_MAP_DEFAULT);
+ }
- if ((l = pa_frame_size(ss) * (size_t) sfinfo.frames) > PA_SCACHE_ENTRY_SIZE_MAX) {
+ if (p)
+ pa_sndfile_init_proplist(sf, p);
+
+ if ((l = pa_frame_size(ss) * (size_t) sfi.frames) > PA_SCACHE_ENTRY_SIZE_MAX) {
pa_log("File too large");
goto finish;
}
@@ -128,9 +110,11 @@ int pa_sound_file_load(
chunk->index = 0;
chunk->length = l;
+ readf_function = pa_sndfile_readf_function(ss);
+
ptr = pa_memblock_acquire(chunk->memblock);
- if ((readf_function && readf_function(sf, ptr, sfinfo.frames) != sfinfo.frames) ||
+ if ((readf_function && readf_function(sf, ptr, sfi.frames) != sfi.frames) ||
(!readf_function && sf_read_raw(sf, ptr, (sf_count_t) l) != (sf_count_t) l)) {
pa_log("Premature file end");
goto finish;
@@ -149,55 +133,35 @@ finish:
if (ret != 0 && chunk->memblock)
pa_memblock_unref(chunk->memblock);
+ if (fd >= 0)
+ pa_close(fd);
+
return ret;
}
int pa_sound_file_too_big_to_cache(const char *fname) {
SNDFILE*sf = NULL;
- SF_INFO sfinfo;
+ SF_INFO sfi;
pa_sample_spec ss;
pa_assert(fname);
- if (!(sf = sf_open(fname, SFM_READ, &sfinfo))) {
+ pa_zero(sfi);
+ if (!(sf = sf_open(fname, SFM_READ, &sfi))) {
pa_log("Failed to open file %s", fname);
return -1;
}
- sf_close(sf);
-
- switch (sfinfo.format & SF_FORMAT_SUBMASK) {
- case SF_FORMAT_PCM_16:
- case SF_FORMAT_PCM_U8:
- case SF_FORMAT_PCM_S8:
- ss.format = PA_SAMPLE_S16NE;
- break;
-
- case SF_FORMAT_ULAW:
- ss.format = PA_SAMPLE_ULAW;
- break;
-
- case SF_FORMAT_ALAW:
- ss.format = PA_SAMPLE_ALAW;
- break;
-
- case SF_FORMAT_DOUBLE:
- case SF_FORMAT_FLOAT:
- default:
- ss.format = PA_SAMPLE_FLOAT32NE;
- break;
- }
-
- ss.rate = (uint32_t) sfinfo.samplerate;
- ss.channels = (uint8_t) sfinfo.channels;
-
- if (!pa_sample_spec_valid(&ss)) {
- pa_log("Unsupported sample format in file %s", fname);
+ if (pa_sndfile_read_sample_spec(sf, &ss) < 0) {
+ pa_log("Failed to determine file sample format.");
+ sf_close(sf);
return -1;
}
- if ((pa_frame_size(&ss) * (size_t) sfinfo.frames) > PA_SCACHE_ENTRY_SIZE_MAX) {
+ sf_close(sf);
+
+ if ((pa_frame_size(&ss) * (size_t) sfi.frames) > PA_SCACHE_ENTRY_SIZE_MAX) {
pa_log("File too large: %s", fname);
return 1;
}
diff --git a/src/pulsecore/sound-file.h b/src/pulsecore/sound-file.h
index 34e02616..4dc0c7e1 100644
--- a/src/pulsecore/sound-file.h
+++ b/src/pulsecore/sound-file.h
@@ -26,7 +26,7 @@
#include <pulse/channelmap.h>
#include <pulsecore/memchunk.h>
-int pa_sound_file_load(pa_mempool *pool, const char *fname, pa_sample_spec *ss, pa_channel_map *map, pa_memchunk *chunk);
+int pa_sound_file_load(pa_mempool *pool, const char *fname, pa_sample_spec *ss, pa_channel_map *map, pa_memchunk *chunk, pa_proplist *p);
int pa_sound_file_too_big_to_cache(const char *fname);
diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c
index 21902509..53697c57 100644
--- a/src/pulsecore/source.c
+++ b/src/pulsecore/source.c
@@ -180,6 +180,7 @@ pa_source* pa_source_new(
s->core = core;
s->state = PA_SOURCE_INIT;
s->flags = flags;
+ s->suspend_cause = 0;
s->name = pa_xstrdup(name);
s->proplist = pa_proplist_copy(data->proplist);
s->driver = pa_xstrdup(pa_path_get_filename(data->driver));
@@ -308,20 +309,19 @@ void pa_source_put(pa_source *s) {
pa_assert(s->rtpoll);
pa_assert(s->thread_info.min_latency <= s->thread_info.max_latency);
- if (!(s->flags & PA_SOURCE_HW_VOLUME_CTRL)) {
- s->flags |= PA_SOURCE_DECIBEL_VOLUME;
+ /* Generally, flags should be initialized via pa_source_new(). As
+ * a special exception we allow volume related flags to be set
+ * between _new() and _put(). */
- s->thread_info.soft_volume = s->soft_volume;
- s->thread_info.soft_muted = s->muted;
- }
+ if (!(s->flags & PA_SOURCE_HW_VOLUME_CTRL))
+ s->flags |= PA_SOURCE_DECIBEL_VOLUME;
- if (s->flags & PA_SOURCE_DECIBEL_VOLUME)
- s->n_volume_steps = PA_VOLUME_NORM+1;
+ s->thread_info.soft_volume = s->soft_volume;
+ s->thread_info.soft_muted = s->muted;
- if (s->flags & PA_SOURCE_DYNAMIC_LATENCY)
- s->fixed_latency = 0;
- else if (s->fixed_latency <= 0)
- s->fixed_latency = DEFAULT_FIXED_LATENCY;
+ pa_assert((s->flags & PA_SOURCE_HW_VOLUME_CTRL) || (s->base_volume == PA_VOLUME_NORM && s->flags & PA_SOURCE_DECIBEL_VOLUME));
+ pa_assert(!(s->flags & PA_SOURCE_DECIBEL_VOLUME) || s->n_volume_steps == PA_VOLUME_NORM+1);
+ pa_assert(!(s->flags & PA_SOURCE_DYNAMIC_LATENCY) == (s->fixed_latency != 0));
pa_assert_se(source_set_state(s, PA_SOURCE_IDLE) == 0);
@@ -428,14 +428,25 @@ int pa_source_update_status(pa_source*s) {
}
/* Called from main context */
-int pa_source_suspend(pa_source *s, pa_bool_t suspend) {
+int pa_source_suspend(pa_source *s, pa_bool_t suspend, pa_suspend_cause_t cause) {
pa_source_assert_ref(s);
pa_assert(PA_SOURCE_IS_LINKED(s->state));
+ pa_assert(cause != 0);
if (s->monitor_of)
return -PA_ERR_NOTSUPPORTED;
if (suspend)
+ s->suspend_cause |= cause;
+ else
+ s->suspend_cause &= ~cause;
+
+ if ((pa_source_get_state(s) == PA_SOURCE_SUSPENDED) == !!s->suspend_cause)
+ return 0;
+
+ pa_log_debug("Suspend cause of source %s is 0x%04x, %s", s->name, s->suspend_cause, s->suspend_cause ? "suspending" : "resuming");
+
+ if (suspend)
return source_set_state(s, PA_SOURCE_SUSPENDED);
else
return source_set_state(s, pa_source_used_by(s) ? PA_SOURCE_RUNNING : PA_SOURCE_IDLE);
@@ -757,8 +768,12 @@ pa_bool_t pa_source_get_mute(pa_source *s, pa_bool_t force_refresh) {
pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_MUTE, NULL, 0, NULL) == 0);
- if (old_muted != s->muted)
+ if (old_muted != s->muted) {
pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+
+ /* Make sure the soft mute status stays in sync */
+ pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_MUTE, NULL, 0, NULL) == 0);
+ }
}
return s->muted;
@@ -1033,12 +1048,13 @@ int pa_source_process_msg(pa_msgobject *object, int code, void *userdata, int64_
}
/* Called from main thread */
-int pa_source_suspend_all(pa_core *c, pa_bool_t suspend) {
+int pa_source_suspend_all(pa_core *c, pa_bool_t suspend, pa_suspend_cause_t cause) {
uint32_t idx;
pa_source *source;
int ret = 0;
pa_core_assert_ref(c);
+ pa_assert(cause != 0);
for (source = PA_SOURCE(pa_idxset_first(c->sources, &idx)); source; source = PA_SOURCE(pa_idxset_next(c->sources, &idx))) {
int r;
@@ -1046,7 +1062,7 @@ int pa_source_suspend_all(pa_core *c, pa_bool_t suspend) {
if (source->monitor_of)
continue;
- if ((r = pa_source_suspend(source, suspend)) < 0)
+ if ((r = pa_source_suspend(source, suspend, cause)) < 0)
ret = r;
}
@@ -1118,8 +1134,11 @@ pa_usec_t pa_source_get_requested_latency_within_thread(pa_source *s) {
if (result != (pa_usec_t) -1)
result = PA_CLAMP(result, s->thread_info.min_latency, s->thread_info.max_latency);
- s->thread_info.requested_latency = result;
- s->thread_info.requested_latency_valid = TRUE;
+ if (PA_SOURCE_IS_LINKED(s->thread_info.state)) {
+ /* Only cache this if we are fully set up */
+ s->thread_info.requested_latency = result;
+ s->thread_info.requested_latency_valid = TRUE;
+ }
return result;
}
@@ -1276,6 +1295,21 @@ void pa_source_set_latency_range_within_thread(pa_source *s, pa_usec_t min_laten
pa_source_invalidate_requested_latency(s);
}
+/* Called from main thread, before the source is put */
+void pa_source_set_fixed_latency(pa_source *s, pa_usec_t latency) {
+ pa_source_assert_ref(s);
+
+ pa_assert(pa_source_get_state(s) == PA_SOURCE_INIT);
+
+ if (latency < ABSOLUTE_MIN_LATENCY)
+ latency = ABSOLUTE_MIN_LATENCY;
+
+ if (latency > ABSOLUTE_MAX_LATENCY)
+ latency = ABSOLUTE_MAX_LATENCY;
+
+ s->fixed_latency = latency;
+}
+
/* Called from main thread */
size_t pa_source_get_max_rewind(pa_source *s) {
size_t r;
diff --git a/src/pulsecore/source.h b/src/pulsecore/source.h
index b502c228..1fbed70e 100644
--- a/src/pulsecore/source.h
+++ b/src/pulsecore/source.h
@@ -58,6 +58,7 @@ struct pa_source {
pa_core *core;
pa_source_state_t state;
pa_source_flags_t flags;
+ pa_suspend_cause_t suspend_cause;
char *name;
char *driver; /* may be NULL */
@@ -210,6 +211,7 @@ void pa_source_set_rtpoll(pa_source *s, pa_rtpoll *p);
void pa_source_set_max_rewind(pa_source *s, size_t max_rewind);
void pa_source_set_latency_range(pa_source *s, pa_usec_t min_latency, pa_usec_t max_latency);
+void pa_source_set_fixed_latency(pa_source *s, pa_usec_t latency);
void pa_source_detach(pa_source *s);
void pa_source_attach(pa_source *s);
@@ -230,8 +232,8 @@ void pa_source_get_latency_range(pa_source *s, pa_usec_t *min_latency, pa_usec_t
size_t pa_source_get_max_rewind(pa_source *s);
int pa_source_update_status(pa_source*s);
-int pa_source_suspend(pa_source *s, pa_bool_t suspend);
-int pa_source_suspend_all(pa_core *c, pa_bool_t suspend);
+int pa_source_suspend(pa_source *s, pa_bool_t suspend, pa_suspend_cause_t cause);
+int pa_source_suspend_all(pa_core *c, pa_bool_t suspend, pa_suspend_cause_t cause);
void pa_source_set_volume(pa_source *source, const pa_cvolume *volume);
const pa_cvolume *pa_source_get_volume(pa_source *source, pa_bool_t force_refresh);
diff --git a/src/pulsecore/strbuf.c b/src/pulsecore/strbuf.c
index 9f5a84b4..4fc82ded 100644
--- a/src/pulsecore/strbuf.c
+++ b/src/pulsecore/strbuf.c
@@ -113,6 +113,13 @@ void pa_strbuf_puts(pa_strbuf *sb, const char *t) {
pa_strbuf_putsn(sb, t, strlen(t));
}
+/* Append a character to the string buffer */
+void pa_strbuf_putc(pa_strbuf *sb, char c) {
+ pa_assert(sb);
+
+ pa_strbuf_putsn(sb, &c, 1);
+}
+
/* Append a new chunk to the linked list */
static void append(pa_strbuf *sb, struct chunk *c) {
pa_assert(sb);
diff --git a/src/pulsecore/strbuf.h b/src/pulsecore/strbuf.h
index 05e69e03..d71ecb98 100644
--- a/src/pulsecore/strbuf.h
+++ b/src/pulsecore/strbuf.h
@@ -35,6 +35,7 @@ char *pa_strbuf_tostring_free(pa_strbuf *sb);
size_t pa_strbuf_printf(pa_strbuf *sb, const char *format, ...) PA_GCC_PRINTF_ATTR(2,3);
void pa_strbuf_puts(pa_strbuf *sb, const char *t);
void pa_strbuf_putsn(pa_strbuf *sb, const char *t, size_t m);
+void pa_strbuf_putc(pa_strbuf *sb, char c);
pa_bool_t pa_strbuf_isempty(pa_strbuf *sb);
diff --git a/src/pulsecore/strlist.c b/src/pulsecore/strlist.c
index cbafbba6..0f4ca867 100644
--- a/src/pulsecore/strlist.c
+++ b/src/pulsecore/strlist.c
@@ -159,3 +159,15 @@ pa_strlist *pa_strlist_reverse(pa_strlist *l) {
return r;
}
+
+pa_strlist *pa_strlist_next(pa_strlist *s) {
+ pa_assert(s);
+
+ return s->next;
+}
+
+const char *pa_strlist_data(pa_strlist *s) {
+ pa_assert(s);
+
+ return ITEM_TO_TEXT(s);
+}
diff --git a/src/pulsecore/strlist.h b/src/pulsecore/strlist.h
index 2584e86c..e57203c5 100644
--- a/src/pulsecore/strlist.h
+++ b/src/pulsecore/strlist.h
@@ -47,4 +47,10 @@ pa_strlist* pa_strlist_parse(const char *s);
/* Reverse string list */
pa_strlist *pa_strlist_reverse(pa_strlist *l);
+/* Return the next item in the list */
+pa_strlist *pa_strlist_next(pa_strlist *s);
+
+/* Return the string associated to the current item */
+const char *pa_strlist_data(pa_strlist *s);
+
#endif