summaryrefslogtreecommitdiffstats
path: root/src/pulsecore
diff options
context:
space:
mode:
Diffstat (limited to 'src/pulsecore')
-rw-r--r--src/pulsecore/asyncmsgq.c42
-rw-r--r--src/pulsecore/asyncmsgq.h4
-rw-r--r--src/pulsecore/aupdate.c8
-rw-r--r--src/pulsecore/aupdate.h4
-rw-r--r--src/pulsecore/authkey.c10
-rw-r--r--src/pulsecore/cli-command.c60
-rw-r--r--src/pulsecore/cli-text.c24
-rw-r--r--src/pulsecore/conf-parser.c28
-rw-r--r--src/pulsecore/conf-parser.h1
-rw-r--r--src/pulsecore/core-rtclock.c76
-rw-r--r--src/pulsecore/core-rtclock.h1
-rw-r--r--src/pulsecore/core-scache.c4
-rw-r--r--src/pulsecore/core-util.c505
-rw-r--r--src/pulsecore/core-util.h29
-rw-r--r--src/pulsecore/core.c2
-rw-r--r--src/pulsecore/core.h12
-rw-r--r--src/pulsecore/cpu-arm.c139
-rw-r--r--src/pulsecore/cpu-arm.h42
-rw-r--r--src/pulsecore/cpu-x86.c125
-rw-r--r--src/pulsecore/cpu-x86.h71
-rw-r--r--src/pulsecore/database-simple.c510
-rw-r--r--src/pulsecore/database-tdb.c43
-rw-r--r--src/pulsecore/dbus-util.c352
-rw-r--r--src/pulsecore/dbus-util.h43
-rw-r--r--src/pulsecore/fdsem.c13
-rw-r--r--src/pulsecore/flist.c22
-rw-r--r--src/pulsecore/hook-list.c2
-rw-r--r--src/pulsecore/llist.h3
-rw-r--r--src/pulsecore/lock-autospawn.c5
-rw-r--r--src/pulsecore/macro.h91
-rw-r--r--src/pulsecore/memblock.c32
-rw-r--r--src/pulsecore/memblockq.c6
-rw-r--r--src/pulsecore/memblockq.h3
-rw-r--r--src/pulsecore/memtrap.c9
-rw-r--r--src/pulsecore/modargs.c10
-rw-r--r--src/pulsecore/modargs.h9
-rw-r--r--src/pulsecore/msgobject.c14
-rw-r--r--src/pulsecore/msgobject.h8
-rw-r--r--src/pulsecore/namereg.c88
-rw-r--r--src/pulsecore/namereg.h1
-rw-r--r--src/pulsecore/object.c18
-rw-r--r--src/pulsecore/object.h51
-rw-r--r--src/pulsecore/pdispatch.c36
-rw-r--r--src/pulsecore/pdispatch.h6
-rw-r--r--src/pulsecore/pid.c11
-rw-r--r--src/pulsecore/play-memblockq.c5
-rw-r--r--src/pulsecore/protocol-dbus.c1134
-rw-r--r--src/pulsecore/protocol-dbus.h217
-rw-r--r--src/pulsecore/protocol-esound.c11
-rw-r--r--src/pulsecore/protocol-http.c2
-rw-r--r--src/pulsecore/protocol-native.c181
-rw-r--r--src/pulsecore/protocol-native.h2
-rw-r--r--src/pulsecore/protocol-simple.c12
-rw-r--r--src/pulsecore/random.c6
-rw-r--r--src/pulsecore/ratelimit.h18
-rw-r--r--src/pulsecore/remap.c204
-rw-r--r--src/pulsecore/remap.h48
-rw-r--r--src/pulsecore/remap_mmx.c161
-rw-r--r--src/pulsecore/remap_sse.c159
-rw-r--r--src/pulsecore/resampler.c240
-rw-r--r--src/pulsecore/rtpoll.c28
-rw-r--r--src/pulsecore/rtpoll.h4
-rw-r--r--src/pulsecore/sample-util.c412
-rw-r--r--src/pulsecore/sample-util.h7
-rw-r--r--src/pulsecore/sconv-s16le.c42
-rw-r--r--src/pulsecore/sconv.c188
-rw-r--r--src/pulsecore/sconv.h6
-rw-r--r--src/pulsecore/sconv_sse.c235
-rw-r--r--src/pulsecore/semaphore-osx.c63
-rw-r--r--src/pulsecore/shm.c3
-rw-r--r--src/pulsecore/sink-input.c395
-rw-r--r--src/pulsecore/sink-input.h61
-rw-r--r--src/pulsecore/sink.c825
-rw-r--r--src/pulsecore/sink.h51
-rw-r--r--src/pulsecore/socket-client.c68
-rw-r--r--src/pulsecore/socket-client.h2
-rw-r--r--src/pulsecore/socket-server.c16
-rw-r--r--src/pulsecore/socket-util.c30
-rw-r--r--src/pulsecore/sound-file-stream.c14
-rw-r--r--src/pulsecore/sound-file.c6
-rw-r--r--src/pulsecore/source-output.c100
-rw-r--r--src/pulsecore/source-output.h24
-rw-r--r--src/pulsecore/source.c286
-rw-r--r--src/pulsecore/source.h33
-rw-r--r--src/pulsecore/start-child.c20
-rw-r--r--src/pulsecore/svolume_arm.c195
-rw-r--r--src/pulsecore/svolume_c.c335
-rw-r--r--src/pulsecore/svolume_mmx.c319
-rw-r--r--src/pulsecore/svolume_sse.c321
-rw-r--r--src/pulsecore/thread-mq.c11
-rw-r--r--src/pulsecore/thread-mq.h8
-rw-r--r--src/pulsecore/time-smoother.c7
-rw-r--r--src/pulsecore/usergroup.c372
-rw-r--r--src/pulsecore/usergroup.h51
-rw-r--r--src/pulsecore/vector.h3
-rw-r--r--src/pulsecore/x11prop.c20
96 files changed, 7830 insertions, 1704 deletions
diff --git a/src/pulsecore/asyncmsgq.c b/src/pulsecore/asyncmsgq.c
index 083d9de2..b0804f79 100644
--- a/src/pulsecore/asyncmsgq.c
+++ b/src/pulsecore/asyncmsgq.c
@@ -26,14 +26,16 @@
#include <unistd.h>
#include <errno.h>
+#include <pulse/xmalloc.h>
+
#include <pulsecore/atomic.h>
+#include <pulsecore/macro.h>
#include <pulsecore/log.h>
#include <pulsecore/thread.h>
#include <pulsecore/semaphore.h>
#include <pulsecore/macro.h>
#include <pulsecore/core-util.h>
#include <pulsecore/flist.h>
-#include <pulse/xmalloc.h>
#include "asyncmsgq.h"
@@ -76,7 +78,7 @@ static void asyncmsgq_free(pa_asyncmsgq *a) {
struct asyncmsgq_item *i;
pa_assert(a);
- while ((i = pa_asyncq_pop(a->asyncq, 0))) {
+ while ((i = pa_asyncq_pop(a->asyncq, FALSE))) {
pa_assert(!i->semaphore);
@@ -246,7 +248,7 @@ int pa_asyncmsgq_wait_for(pa_asyncmsgq *a, int code) {
pa_memchunk chunk;
int ret;
- if (pa_asyncmsgq_get(a, &o, &c, &data, &offset, &chunk, 1) < 0)
+ if (pa_asyncmsgq_get(a, &o, &c, &data, &offset, &chunk, TRUE) < 0)
return -1;
ret = pa_asyncmsgq_dispatch(o, c, data, offset, &chunk);
@@ -269,7 +271,7 @@ int pa_asyncmsgq_process_one(pa_asyncmsgq *a) {
pa_assert(PA_REFCNT_VALUE(a) > 0);
- if (pa_asyncmsgq_get(a, &object, &code, &data, &offset, &chunk, 0) < 0)
+ if (pa_asyncmsgq_get(a, &object, &code, &data, &offset, &chunk, FALSE) < 0)
return 0;
pa_asyncmsgq_ref(a);
@@ -323,3 +325,35 @@ int pa_asyncmsgq_dispatch(pa_msgobject *object, int code, void *userdata, int64_
return 0;
}
+
+void pa_asyncmsgq_flush(pa_asyncmsgq *a, pa_bool_t run) {
+ pa_assert(PA_REFCNT_VALUE(a) > 0);
+
+ for (;;) {
+ pa_msgobject *object;
+ int code;
+ void *data;
+ int64_t offset;
+ pa_memchunk chunk;
+ int ret;
+
+ if (pa_asyncmsgq_get(a, &object, &code, &data, &offset, &chunk, FALSE) < 0)
+ return;
+
+ if (!run) {
+ pa_asyncmsgq_done(a, -1);
+ continue;
+ }
+
+ pa_asyncmsgq_ref(a);
+ ret = pa_asyncmsgq_dispatch(object, code, data, offset, &chunk);
+ pa_asyncmsgq_done(a, ret);
+ pa_asyncmsgq_unref(a);
+ }
+}
+
+pa_bool_t pa_asyncmsgq_dispatching(pa_asyncmsgq *a) {
+ pa_assert(PA_REFCNT_VALUE(a) > 0);
+
+ return !!a->current;
+}
diff --git a/src/pulsecore/asyncmsgq.h b/src/pulsecore/asyncmsgq.h
index 1f38207a..1085c2f0 100644
--- a/src/pulsecore/asyncmsgq.h
+++ b/src/pulsecore/asyncmsgq.h
@@ -66,6 +66,8 @@ void pa_asyncmsgq_done(pa_asyncmsgq *q, int ret);
int pa_asyncmsgq_wait_for(pa_asyncmsgq *a, int code);
int pa_asyncmsgq_process_one(pa_asyncmsgq *a);
+void pa_asyncmsgq_flush(pa_asyncmsgq *a, pa_bool_t run);
+
/* For the reading side */
int pa_asyncmsgq_read_fd(pa_asyncmsgq *q);
int pa_asyncmsgq_read_before_poll(pa_asyncmsgq *a);
@@ -76,4 +78,6 @@ int pa_asyncmsgq_write_fd(pa_asyncmsgq *q);
void pa_asyncmsgq_write_before_poll(pa_asyncmsgq *a);
void pa_asyncmsgq_write_after_poll(pa_asyncmsgq *a);
+pa_bool_t pa_asyncmsgq_dispatching(pa_asyncmsgq *a);
+
#endif
diff --git a/src/pulsecore/aupdate.c b/src/pulsecore/aupdate.c
index 56ebb8e5..85b6e00e 100644
--- a/src/pulsecore/aupdate.c
+++ b/src/pulsecore/aupdate.c
@@ -39,6 +39,7 @@ struct pa_aupdate {
pa_atomic_t read_lock;
pa_mutex *write_lock;
pa_semaphore *semaphore;
+ pa_bool_t swapped;
};
pa_aupdate *pa_aupdate_new(void) {
@@ -101,6 +102,8 @@ unsigned pa_aupdate_write_begin(pa_aupdate *a) {
n = (unsigned) pa_atomic_load(&a->read_lock);
+ a->swapped = FALSE;
+
return !WHICH(n);
}
@@ -119,11 +122,16 @@ unsigned pa_aupdate_write_swap(pa_aupdate *a) {
break;
}
+ a->swapped = TRUE;
+
return WHICH(n);
}
void pa_aupdate_write_end(pa_aupdate *a) {
pa_assert(a);
+ if (!a->swapped)
+ pa_aupdate_write_swap(a);
+
pa_mutex_unlock(a->write_lock);
}
diff --git a/src/pulsecore/aupdate.h b/src/pulsecore/aupdate.h
index 072e382d..fb38ffa2 100644
--- a/src/pulsecore/aupdate.h
+++ b/src/pulsecore/aupdate.h
@@ -93,6 +93,10 @@ unsigned pa_aupdate_write_swap(pa_aupdate *a);
* pa_update_write_end(a)
* }
*
+ * In some cases keeping both structures up-to-date might not be
+ * necessary, since they are fully rebuilt on each iteration
+ * anyway. In that case you may leave the _write_swap() call out, it
+ * will then be done implicitly in the _write_end() invocation.
*/
#endif
diff --git a/src/pulsecore/authkey.c b/src/pulsecore/authkey.c
index 15613e27..d671e367 100644
--- a/src/pulsecore/authkey.c
+++ b/src/pulsecore/authkey.c
@@ -70,10 +70,6 @@ static int generate(int fd, void *ret_data, size_t length) {
#define O_BINARY 0
#endif
-#ifndef O_NOCTTY
-#define O_NOCTTY 0
-#endif
-
/* Load an euthorization cookie from file fn and store it in data. If
* the cookie file doesn't exist, create it */
static int load(const char *fn, void *data, size_t length) {
@@ -86,9 +82,9 @@ static int load(const char *fn, void *data, size_t length) {
pa_assert(data);
pa_assert(length > 0);
- if ((fd = open(fn, O_RDWR|O_CREAT|O_BINARY|O_NOCTTY, S_IRUSR|S_IWUSR)) < 0) {
+ if ((fd = pa_open_cloexec(fn, O_RDWR|O_CREAT|O_BINARY, S_IRUSR|S_IWUSR)) < 0) {
- if (errno != EACCES || (fd = open(fn, O_RDONLY|O_BINARY|O_NOCTTY)) < 0) {
+ if (errno != EACCES || (fd = open(fn, O_RDONLY|O_BINARY)) < 0) {
pa_log_warn("Failed to open cookie file '%s': %s", fn, pa_cstrerror(errno));
goto finish;
} else
@@ -204,7 +200,7 @@ int pa_authkey_save(const char *fn, const void *data, size_t length) {
if (!(p = normalize_path(fn)))
return -2;
- if ((fd = open(p, O_RDWR|O_CREAT|O_NOCTTY, S_IRUSR|S_IWUSR)) < 0) {
+ if ((fd = pa_open_cloexec(p, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR)) < 0) {
pa_log_warn("Failed to open cookie file '%s': %s", fn, pa_cstrerror(errno));
goto finish;
}
diff --git a/src/pulsecore/cli-command.c b/src/pulsecore/cli-command.c
index e2c3c066..82a44d84 100644
--- a/src/pulsecore/cli-command.c
+++ b/src/pulsecore/cli-command.c
@@ -328,7 +328,7 @@ static int pa_cli_command_source_outputs(pa_core *c, pa_tokenizer *t, pa_strbuf
static int pa_cli_command_stat(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
char ss[PA_SAMPLE_SPEC_SNPRINT_MAX];
char cm[PA_CHANNEL_MAP_SNPRINT_MAX];
- char s[256];
+ char bytes[PA_BYTES_SNPRINT_MAX];
const pa_mempool_stat *stat;
unsigned k;
pa_sink *def_sink;
@@ -352,22 +352,22 @@ static int pa_cli_command_stat(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_b
pa_strbuf_printf(buf, "Memory blocks currently allocated: %u, size: %s.\n",
(unsigned) pa_atomic_load(&stat->n_allocated),
- pa_bytes_snprint(s, sizeof(s), (unsigned) pa_atomic_load(&stat->allocated_size)));
+ pa_bytes_snprint(bytes, sizeof(bytes), (unsigned) pa_atomic_load(&stat->allocated_size)));
pa_strbuf_printf(buf, "Memory blocks allocated during the whole lifetime: %u, size: %s.\n",
(unsigned) pa_atomic_load(&stat->n_accumulated),
- pa_bytes_snprint(s, sizeof(s), (unsigned) pa_atomic_load(&stat->accumulated_size)));
+ pa_bytes_snprint(bytes, sizeof(bytes), (unsigned) pa_atomic_load(&stat->accumulated_size)));
pa_strbuf_printf(buf, "Memory blocks imported from other processes: %u, size: %s.\n",
(unsigned) pa_atomic_load(&stat->n_imported),
- pa_bytes_snprint(s, sizeof(s), (unsigned) pa_atomic_load(&stat->imported_size)));
+ pa_bytes_snprint(bytes, sizeof(bytes), (unsigned) pa_atomic_load(&stat->imported_size)));
pa_strbuf_printf(buf, "Memory blocks exported to other processes: %u, size: %s.\n",
(unsigned) pa_atomic_load(&stat->n_exported),
- pa_bytes_snprint(s, sizeof(s), (unsigned) pa_atomic_load(&stat->exported_size)));
+ pa_bytes_snprint(bytes, sizeof(bytes), (unsigned) pa_atomic_load(&stat->exported_size)));
pa_strbuf_printf(buf, "Total sample cache size: %s.\n",
- pa_bytes_snprint(s, sizeof(s), (unsigned) pa_scache_total_size(c)));
+ pa_bytes_snprint(bytes, sizeof(bytes), (unsigned) pa_scache_total_size(c)));
pa_strbuf_printf(buf, "Default sample spec: %s\n",
pa_sample_spec_snprint(ss, sizeof(ss), &c->default_sample_spec));
@@ -529,8 +529,8 @@ static int pa_cli_command_sink_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *bu
return -1;
}
- pa_cvolume_set(&cvolume, sink->sample_spec.channels, volume);
- pa_sink_set_volume(sink, &cvolume, TRUE, TRUE, TRUE, TRUE);
+ pa_cvolume_set(&cvolume, 1, volume);
+ pa_sink_set_volume(sink, &cvolume, TRUE, TRUE);
return 0;
}
@@ -571,7 +571,7 @@ static int pa_cli_command_sink_input_volume(pa_core *c, pa_tokenizer *t, pa_strb
return -1;
}
- pa_cvolume_set(&cvolume, si->sample_spec.channels, volume);
+ pa_cvolume_set(&cvolume, 1, volume);
pa_sink_input_set_volume(si, &cvolume, TRUE, TRUE);
return 0;
}
@@ -607,7 +607,7 @@ static int pa_cli_command_source_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *
return -1;
}
- pa_cvolume_set(&cvolume, source->sample_spec.channels, volume);
+ pa_cvolume_set(&cvolume, 1, volume);
pa_source_set_volume(source, &cvolume, TRUE);
return 0;
}
@@ -1549,7 +1549,7 @@ static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_b
pa_sink *sink;
pa_source *source;
pa_card *card;
- int nl;
+ pa_bool_t nl;
uint32_t idx;
char txt[256];
time_t now;
@@ -1567,7 +1567,7 @@ static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_b
pa_strbuf_printf(buf, "### Configuration dump generated at %s\n", ctime(&now));
#endif
- for (m = pa_idxset_first(c->modules, &idx); m; m = pa_idxset_next(c->modules, &idx)) {
+ PA_IDXSET_FOREACH(m, c->modules, idx) {
pa_strbuf_printf(buf, "load-module %s", m->name);
@@ -1577,58 +1577,58 @@ static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_b
pa_strbuf_puts(buf, "\n");
}
- nl = 0;
-
- for (sink = pa_idxset_first(c->sinks, &idx); sink; sink = pa_idxset_next(c->sinks, &idx)) {
+ nl = FALSE;
+ PA_IDXSET_FOREACH(sink, c->sinks, idx) {
if (!nl) {
pa_strbuf_puts(buf, "\n");
- nl = 1;
+ nl = TRUE;
}
- pa_strbuf_printf(buf, "set-sink-volume %s 0x%03x\n", sink->name, pa_cvolume_avg(pa_sink_get_volume(sink, FALSE, TRUE)));
+ pa_strbuf_printf(buf, "set-sink-volume %s 0x%03x\n", sink->name, pa_cvolume_max(pa_sink_get_volume(sink, FALSE)));
pa_strbuf_printf(buf, "set-sink-mute %s %s\n", sink->name, pa_yes_no(pa_sink_get_mute(sink, FALSE)));
pa_strbuf_printf(buf, "suspend-sink %s %s\n", sink->name, pa_yes_no(pa_sink_get_state(sink) == PA_SINK_SUSPENDED));
}
- for (source = pa_idxset_first(c->sources, &idx); source; source = pa_idxset_next(c->sources, &idx)) {
+ nl = FALSE;
+ PA_IDXSET_FOREACH(source, c->sources, idx) {
if (!nl) {
pa_strbuf_puts(buf, "\n");
- nl = 1;
+ nl = TRUE;
}
- pa_strbuf_printf(buf, "set-source-volume %s 0x%03x\n", source->name, pa_cvolume_avg(pa_source_get_volume(source, FALSE)));
+ pa_strbuf_printf(buf, "set-source-volume %s 0x%03x\n", source->name, pa_cvolume_max(pa_source_get_volume(source, FALSE)));
pa_strbuf_printf(buf, "set-source-mute %s %s\n", source->name, pa_yes_no(pa_source_get_mute(source, FALSE)));
pa_strbuf_printf(buf, "suspend-source %s %s\n", source->name, pa_yes_no(pa_source_get_state(source) == PA_SOURCE_SUSPENDED));
}
- for (card = pa_idxset_first(c->cards, &idx); card; card = pa_idxset_next(c->cards, &idx)) {
+ nl = FALSE;
+ PA_IDXSET_FOREACH(card, c->cards, idx) {
if (!nl) {
pa_strbuf_puts(buf, "\n");
- nl = 1;
+ nl = TRUE;
}
if (card->active_profile)
pa_strbuf_printf(buf, "set-card-profile %s %s\n", card->name, card->active_profile->name);
}
- nl = 0;
-
+ nl = FALSE;
if ((sink = pa_namereg_get_default_sink(c))) {
if (!nl) {
pa_strbuf_puts(buf, "\n");
- nl = 1;
+ nl = TRUE;
}
+
pa_strbuf_printf(buf, "set-default-sink %s\n", sink->name);
}
if ((source = pa_namereg_get_default_source(c))) {
- if (!nl) {
+ if (!nl)
pa_strbuf_puts(buf, "\n");
- nl = 1;
- }
+
pa_strbuf_printf(buf, "set-default-source %s\n", source->name);
}
@@ -1804,7 +1804,7 @@ int pa_cli_command_execute_file(pa_core *c, const char *fn, pa_strbuf *buf, pa_b
if (!fail)
fail = &_fail;
- if (!(f = fopen(fn, "r"))) {
+ if (!(f = pa_fopen_cloexec(fn, "r"))) {
pa_strbuf_printf(buf, "open('%s') failed: %s\n", fn, pa_cstrerror(errno));
if (!*fail)
ret = 0;
@@ -1813,8 +1813,6 @@ int pa_cli_command_execute_file(pa_core *c, const char *fn, pa_strbuf *buf, pa_b
ret = pa_cli_command_execute_file_stream(c, f, buf, fail);
- ret = 0;
-
fail:
if (f)
fclose(f);
diff --git a/src/pulsecore/cli-text.c b/src/pulsecore/cli-text.c
index 9395513d..23a57d37 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) {
"\tflags: %s%s%s%s%s%s%s%s\n"
"\tstate: %s\n"
"\tsuspend cause: %s%s%s%s\n"
+ "\tpriority: %u\n"
"\tvolume: %s%s%s\n"
"\t balance %0.2f\n"
"\tbase volume: %s%s%s\n"
@@ -262,10 +263,11 @@ char *pa_sink_list_to_string(pa_core *c) {
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->priority,
+ pa_cvolume_snprint(cv, sizeof(cv), pa_sink_get_volume(sink, 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)) : "",
- pa_cvolume_get_balance(pa_sink_get_volume(sink, FALSE, FALSE), &sink->channel_map),
+ sink->flags & PA_SINK_DECIBEL_VOLUME ? pa_sw_cvolume_snprint_dB(cvdb, sizeof(cvdb), pa_sink_get_volume(sink, FALSE)) : "",
+ pa_cvolume_get_balance(pa_sink_get_volume(sink, FALSE), &sink->channel_map),
pa_volume_snprint(v, sizeof(v), sink->base_volume),
sink->flags & PA_SINK_DECIBEL_VOLUME ? "\n\t " : "",
sink->flags & PA_SINK_DECIBEL_VOLUME ? pa_sw_volume_snprint_dB(vdb, sizeof(vdb), sink->base_volume) : "",
@@ -296,7 +298,7 @@ char *pa_sink_list_to_string(pa_core *c) {
pa_strbuf_printf(
s,
"\tfixed latency: %0.2f ms\n",
- (double) pa_sink_get_requested_latency(sink) / PA_USEC_PER_MSEC);
+ (double) pa_sink_get_fixed_latency(sink) / PA_USEC_PER_MSEC);
if (sink->card)
pa_strbuf_printf(s, "\tcard: %u <%s>\n", sink->card->index, sink->card->name);
@@ -356,6 +358,7 @@ char *pa_source_list_to_string(pa_core *c) {
"\tflags: %s%s%s%s%s%s%s\n"
"\tstate: %s\n"
"\tsuspend cause: %s%s%s%s\n"
+ "\tpriority: %u\n"
"\tvolume: %s%s%s\n"
"\t balance %0.2f\n"
"\tbase volume: %s%s%s\n"
@@ -383,6 +386,7 @@ char *pa_source_list_to_string(pa_core *c) {
source->suspend_cause & PA_SUSPEND_APPLICATION ? "APPLICATION " : "",
source->suspend_cause & PA_SUSPEND_IDLE ? "IDLE " : "",
source->suspend_cause & PA_SUSPEND_SESSION ? "SESSION" : "",
+ source->priority,
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)) : "",
@@ -415,7 +419,7 @@ char *pa_source_list_to_string(pa_core *c) {
pa_strbuf_printf(
s,
"\tfixed latency: %0.2f ms\n",
- (double) pa_source_get_requested_latency(source) / PA_USEC_PER_MSEC);
+ (double) pa_source_get_fixed_latency(source) / PA_USEC_PER_MSEC);
if (source->monitor_of)
pa_strbuf_printf(s, "\tmonitor_of: %u\n", source->monitor_of->index);
@@ -482,7 +486,7 @@ char *pa_source_output_list_to_string(pa_core *c) {
s,
" index: %u\n"
"\tdriver: <%s>\n"
- "\tflags: %s%s%s%s%s%s%s%s%s%s\n"
+ "\tflags: %s%s%s%s%s%s%s%s%s%s%s\n"
"\tstate: %s\n"
"\tsource: %u <%s>\n"
"\tcurrent latency: %0.2f ms\n"
@@ -501,7 +505,8 @@ char *pa_source_output_list_to_string(pa_core *c) {
o->flags & PA_SOURCE_OUTPUT_FIX_RATE ? "FIX_RATE " : "",
o->flags & PA_SOURCE_OUTPUT_FIX_CHANNELS ? "FIX_CHANNELS " : "",
o->flags & PA_SOURCE_OUTPUT_DONT_INHIBIT_AUTO_SUSPEND ? "DONT_INHIBIT_AUTO_SUSPEND " : "",
- o->flags & PA_SOURCE_OUTPUT_FAIL_ON_SUSPEND ? "FAIL_ON_SUSPEND " : "",
+ o->flags & PA_SOURCE_OUTPUT_NO_CREATE_ON_SUSPEND ? "NO_CREATE_ON_SUSPEND " : "",
+ o->flags & PA_SOURCE_OUTPUT_KILL_ON_SUSPEND ? "KILL_ON_SUSPEND " : "",
state_table[pa_source_output_get_state(o)],
o->source->index, o->source->name,
(double) pa_source_output_get_latency(o, NULL) / PA_USEC_PER_MSEC,
@@ -564,7 +569,7 @@ char *pa_sink_input_list_to_string(pa_core *c) {
s,
" index: %u\n"
"\tdriver: <%s>\n"
- "\tflags: %s%s%s%s%s%s%s%s%s%s\n"
+ "\tflags: %s%s%s%s%s%s%s%s%s%s%s\n"
"\tstate: %s\n"
"\tsink: %u <%s>\n"
"\tvolume: %s\n"
@@ -587,7 +592,8 @@ char *pa_sink_input_list_to_string(pa_core *c) {
i->flags & PA_SINK_INPUT_FIX_RATE ? "FIX_RATE " : "",
i->flags & PA_SINK_INPUT_FIX_CHANNELS ? "FIX_CHANNELS " : "",
i->flags & PA_SINK_INPUT_DONT_INHIBIT_AUTO_SUSPEND ? "DONT_INHIBIT_AUTO_SUSPEND " : "",
- i->flags & PA_SINK_INPUT_FAIL_ON_SUSPEND ? "FAIL_ON_SUSPEND " : "",
+ i->flags & PA_SINK_INPUT_NO_CREATE_ON_SUSPEND ? "NO_CREATE_SUSPEND " : "",
+ i->flags & PA_SINK_INPUT_KILL_ON_SUSPEND ? "KILL_ON_SUSPEND " : "",
state_table[pa_sink_input_get_state(i)],
i->sink->index, i->sink->name,
pa_cvolume_snprint(cv, sizeof(cv), &v),
diff --git a/src/pulsecore/conf-parser.c b/src/pulsecore/conf-parser.c
index 2dc9a223..34b4d6fe 100644
--- a/src/pulsecore/conf-parser.c
+++ b/src/pulsecore/conf-parser.c
@@ -113,7 +113,7 @@ static int parse_line(const char *filename, unsigned line, char **section, const
return 0;
if (pa_startswith(b, ".include ")) {
- char *path, *fn;
+ char *path = NULL, *fn;
int r;
fn = strip(b+9);
@@ -168,7 +168,7 @@ int pa_config_parse(const char *filename, FILE *f, const pa_config_item *t, void
pa_assert(filename);
pa_assert(t);
- if (!f && !(f = fopen(filename, "r"))) {
+ if (!f && !(f = pa_fopen_cloexec(filename, "r"))) {
if (errno == ENOENT) {
pa_log_debug("Failed to open configuration file '%s': %s", filename, pa_cstrerror(errno));
r = 0;
@@ -278,6 +278,30 @@ int pa_config_parse_bool(const char *filename, unsigned line, const char *sectio
return 0;
}
+int pa_config_parse_not_bool(
+ const char *filename, unsigned line,
+ const char *section,
+ const char *lvalue, const char *rvalue,
+ void *data, void *userdata) {
+
+ int k;
+ pa_bool_t *b = data;
+
+ pa_assert(filename);
+ pa_assert(lvalue);
+ pa_assert(rvalue);
+ pa_assert(data);
+
+ if ((k = pa_parse_boolean(rvalue)) < 0) {
+ pa_log("[%s:%u] Failed to parse boolean value: %s", filename, line, rvalue);
+ return -1;
+ }
+
+ *b = !k;
+
+ return 0;
+}
+
int pa_config_parse_string(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata) {
char **s = data;
diff --git a/src/pulsecore/conf-parser.h b/src/pulsecore/conf-parser.h
index 08e17ca7..c6c8a148 100644
--- a/src/pulsecore/conf-parser.h
+++ b/src/pulsecore/conf-parser.h
@@ -47,6 +47,7 @@ int pa_config_parse_int(const char *filename, unsigned line, const char *section
int pa_config_parse_unsigned(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata);
int pa_config_parse_size(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata);
int pa_config_parse_bool(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata);
+int pa_config_parse_not_bool(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata);
int pa_config_parse_string(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata);
#endif
diff --git a/src/pulsecore/core-rtclock.c b/src/pulsecore/core-rtclock.c
index 3b3e3a64..4fe0a47b 100644
--- a/src/pulsecore/core-rtclock.c
+++ b/src/pulsecore/core-rtclock.c
@@ -33,6 +33,12 @@
#include <sys/prctl.h>
#endif
+#ifdef OS_IS_DARWIN
+#include <CoreServices/CoreServices.h>
+#include <mach/mach.h>
+#include <mach/mach_time.h>
+#endif
+
#include <pulse/timeval.h>
#include <pulsecore/macro.h>
#include <pulsecore/core-error.h>
@@ -47,7 +53,8 @@ pa_usec_t pa_rtclock_age(const struct timeval *tv) {
}
struct timeval *pa_rtclock_get(struct timeval *tv) {
-#ifdef HAVE_CLOCK_GETTIME
+
+#if defined(HAVE_CLOCK_GETTIME)
struct timespec ts;
#ifdef CLOCK_MONOTONIC
@@ -59,7 +66,7 @@ struct timeval *pa_rtclock_get(struct timeval *tv) {
no_monotonic = TRUE;
if (no_monotonic)
-#endif
+#endif /* CLOCK_MONOTONIC */
pa_assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0);
pa_assert(tv);
@@ -69,7 +76,30 @@ struct timeval *pa_rtclock_get(struct timeval *tv) {
return tv;
-#else /* HAVE_CLOCK_GETTIME */
+#elif defined(OS_IS_DARWIN)
+ static mach_timebase_info_data_t tbi;
+ uint64_t nticks;
+ uint64_t time_nsec;
+
+ /* Refer Apple ADC QA1398
+ Also: http://devworld.apple.com/documentation/Darwin/Conceptual/KernelProgramming/services/services.html
+
+ Note: argument is timespec NOT timeval (timespec uses nsec, timeval uses usec)
+ */
+
+ /* try and be a mite efficient - maybe I should keep the N/D as a float !? */
+ if (tbi.denom == 0)
+ mach_timebase_info(&tbi);
+
+ nticks = mach_absolute_time();
+ time_nsec = nticks * tbi.numer / tbi.denom; // see above
+
+ tv->tv_sec = time_nsec / PA_NSEC_PER_SEC;
+ tv->tv_usec = time_nsec / PA_NSEC_PER_USEC;
+
+ return tv;
+
+#else /* OS_IS_DARWIN */
return pa_gettimeofday(tv);
@@ -77,19 +107,30 @@ struct timeval *pa_rtclock_get(struct timeval *tv) {
}
pa_bool_t pa_rtclock_hrtimer(void) {
-#ifdef HAVE_CLOCK_GETTIME
+
+#if defined(HAVE_CLOCK_GETTIME)
struct timespec ts;
#ifdef CLOCK_MONOTONIC
+
if (clock_getres(CLOCK_MONOTONIC, &ts) >= 0)
return ts.tv_sec == 0 && ts.tv_nsec <= (long) (PA_HRTIMER_THRESHOLD_USEC*PA_NSEC_PER_USEC);
-#endif
+#endif /* CLOCK_MONOTONIC */
pa_assert_se(clock_getres(CLOCK_REALTIME, &ts) == 0);
return ts.tv_sec == 0 && ts.tv_nsec <= (long) (PA_HRTIMER_THRESHOLD_USEC*PA_NSEC_PER_USEC);
-#else /* HAVE_CLOCK_GETTIME */
+#elif defined (OS_IS_DARWIN)
+ mach_timebase_info_data_t tbi;
+ uint64_t time_nsec;
+
+ mach_timebase_info(&tbi);
+ /* nsec = nticks * (N/D) - we want 1 tick == resolution !? */
+ time_nsec = tbi.numer / tbi.denom;
+ return time_nsec <= (long) (PA_HRTIMER_THRESHOLD_USEC*PA_NSEC_PER_USEC);
+
+#else /* OS_IS_DARWIN */
return FALSE;
#endif
@@ -98,6 +139,7 @@ pa_bool_t pa_rtclock_hrtimer(void) {
#define TIMER_SLACK_NS (int) ((500 * PA_NSEC_PER_USEC))
void pa_rtclock_hrtimer_enable(void) {
+
#ifdef PR_SET_TIMERSLACK
int slack_ns;
@@ -132,6 +174,8 @@ struct timeval* pa_rtclock_from_wallclock(struct timeval *tv) {
pa_assert(tv);
+ /* pa_timeval_sub() saturates on underflow! */
+
if (pa_timeval_cmp(&wc_now, tv) < 0)
pa_timeval_add(&rt_now, pa_timeval_diff(tv, &wc_now));
else
@@ -144,13 +188,29 @@ struct timeval* pa_rtclock_from_wallclock(struct timeval *tv) {
}
pa_usec_t pa_timespec_load(const struct timespec *ts) {
- pa_assert(ts);
+
+ if (PA_UNLIKELY(!ts))
+ return PA_USEC_INVALID;
return
(pa_usec_t) ts->tv_sec * PA_USEC_PER_SEC +
(pa_usec_t) ts->tv_nsec / PA_NSEC_PER_USEC;
}
+struct timespec* pa_timespec_store(struct timespec *ts, pa_usec_t v) {
+ pa_assert(ts);
+
+ if (PA_UNLIKELY(v == PA_USEC_INVALID)) {
+ ts->tv_sec = PA_INT_TYPE_MAX(time_t);
+ ts->tv_nsec = (long) (PA_NSEC_PER_SEC-1);
+ return NULL;
+ }
+
+ ts->tv_sec = (time_t) (v / PA_USEC_PER_SEC);
+ ts->tv_nsec = (long) ((v % PA_USEC_PER_SEC) * PA_NSEC_PER_USEC);
+
+ return ts;
+}
static struct timeval* wallclock_from_rtclock(struct timeval *tv) {
@@ -162,6 +222,8 @@ static struct timeval* wallclock_from_rtclock(struct timeval *tv) {
pa_assert(tv);
+ /* pa_timeval_sub() saturates on underflow! */
+
if (pa_timeval_cmp(&rt_now, tv) < 0)
pa_timeval_add(&wc_now, pa_timeval_diff(tv, &rt_now));
else
diff --git a/src/pulsecore/core-rtclock.h b/src/pulsecore/core-rtclock.h
index 9f5ae2dd..3b393ed5 100644
--- a/src/pulsecore/core-rtclock.h
+++ b/src/pulsecore/core-rtclock.h
@@ -44,6 +44,7 @@ void pa_rtclock_hrtimer_enable(void);
struct timeval* pa_rtclock_from_wallclock(struct timeval *tv);
pa_usec_t pa_timespec_load(const struct timespec *ts);
+struct timespec* pa_timespec_store(struct timespec *ts, pa_usec_t v);
struct timeval* pa_timeval_rtstore(struct timeval *tv, pa_usec_t v, pa_bool_t rtclock);
diff --git a/src/pulsecore/core-scache.c b/src/pulsecore/core-scache.c
index fde12ecf..1fb81d0d 100644
--- a/src/pulsecore/core-scache.c
+++ b/src/pulsecore/core-scache.c
@@ -335,12 +335,12 @@ int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, pa_volume_t
pass_volume = TRUE;
- if (e->volume_is_set && volume != (pa_volume_t) -1) {
+ if (e->volume_is_set && volume != PA_VOLUME_INVALID) {
pa_cvolume_set(&r, e->sample_spec.channels, volume);
pa_sw_cvolume_multiply(&r, &r, &e->volume);
} else if (e->volume_is_set)
r = e->volume;
- else if (volume != (pa_volume_t) -1)
+ else if (volume != PA_VOLUME_INVALID)
pa_cvolume_set(&r, e->sample_spec.channels, volume);
else
pass_volume = FALSE;
diff --git a/src/pulsecore/core-util.c b/src/pulsecore/core-util.c
index d4baf697..93ddf301 100644
--- a/src/pulsecore/core-util.c
+++ b/src/pulsecore/core-util.c
@@ -101,6 +101,10 @@
#include "rtkit.h"
#endif
+#ifdef __linux__
+#include <sys/personality.h>
+#endif
+
#include <pulse/xmalloc.h>
#include <pulse/util.h>
#include <pulse/utf8.h>
@@ -111,6 +115,8 @@
#include <pulsecore/macro.h>
#include <pulsecore/thread.h>
#include <pulsecore/strbuf.h>
+#include <pulsecore/usergroup.h>
+#include <pulsecore/strlist.h>
#include "core-util.h"
@@ -119,6 +125,8 @@
#define MSG_NOSIGNAL 0
#endif
+static pa_strlist *recorded_env = NULL;
+
#ifdef OS_IS_WIN32
#define PULSE_ROOTENV "PULSE_ROOT"
@@ -583,13 +591,13 @@ static int set_scheduler(int rtprio) {
sp.sched_priority = rtprio;
#ifdef SCHED_RESET_ON_FORK
- if ((r = pthread_setschedparam(pthread_self(), SCHED_RR|SCHED_RESET_ON_FORK, &sp)) == 0) {
+ if (pthread_setschedparam(pthread_self(), SCHED_RR|SCHED_RESET_ON_FORK, &sp) == 0) {
pa_log_debug("SCHED_RR|SCHED_RESET_ON_FORK worked.");
return 0;
}
#endif
- if ((r = pthread_setschedparam(pthread_self(), SCHED_RR, &sp)) == 0) {
+ if (pthread_setschedparam(pthread_self(), SCHED_RR, &sp) == 0) {
pa_log_debug("SCHED_RR worked.");
return 0;
}
@@ -604,6 +612,11 @@ static int set_scheduler(int rtprio) {
return -1;
}
+ /* We need to disable exit on disconnect because otherwise
+ * dbus_shutdown will kill us. See
+ * https://bugs.freedesktop.org/show_bug.cgi?id=16924 */
+ dbus_connection_set_exit_on_disconnect(bus, FALSE);
+
r = rtkit_make_realtime(bus, 0, rtprio);
dbus_connection_unref(bus);
@@ -672,6 +685,11 @@ static int set_nice(int nice_level) {
return -1;
}
+ /* We need to disable exit on disconnect because otherwise
+ * dbus_shutdown will kill us. See
+ * https://bugs.freedesktop.org/show_bug.cgi?id=16924 */
+ dbus_connection_set_exit_on_disconnect(bus, FALSE);
+
r = rtkit_make_high_priority(bus, 0, nice_level);
dbus_connection_unref(bus);
@@ -768,7 +786,6 @@ int pa_match(const char *expr, const char *v) {
/* Try to parse a boolean string value.*/
int pa_parse_boolean(const char *v) {
const char *expr;
- int r;
pa_assert(v);
/* First we check language independant */
@@ -780,12 +797,12 @@ int pa_parse_boolean(const char *v) {
/* And then we check language dependant */
if ((expr = nl_langinfo(YESEXPR)))
if (expr[0])
- if ((r = pa_match(expr, v)) > 0)
+ if (pa_match(expr, v) > 0)
return 1;
if ((expr = nl_langinfo(NOEXPR)))
if (expr[0])
- if ((r = pa_match(expr, v)) > 0)
+ if (pa_match(expr, v) > 0)
return 0;
errno = EINVAL;
@@ -965,54 +982,24 @@ fail:
/* Check whether the specified GID and the group name match */
static int is_group(gid_t gid, const char *name) {
- struct group group, *result = NULL;
- long n;
- void *data;
+ struct group *group = NULL;
int r = -1;
-#ifdef HAVE_GETGRGID_R
-#ifdef _SC_GETGR_R_SIZE_MAX
- n = sysconf(_SC_GETGR_R_SIZE_MAX);
-#else
- n = -1;
-#endif
- if (n <= 0)
- n = 512;
-
- data = pa_xmalloc((size_t) n);
-
errno = 0;
- if (getgrgid_r(gid, &group, data, (size_t) n, &result) < 0 || !result) {
- pa_log("getgrgid_r(%u): %s", (unsigned) gid, pa_cstrerror(errno));
-
+ if (!(group = pa_getgrgid_malloc(gid)))
+ {
if (!errno)
errno = ENOENT;
- goto finish;
- }
-
- r = strcmp(name, result->gr_name) == 0;
-
-finish:
- pa_xfree(data);
-#else
- /* XXX Not thread-safe, but needed on OSes (e.g. FreeBSD 4.X) that do not
- * support getgrgid_r. */
-
- errno = 0;
- if (!(result = getgrgid(gid))) {
- pa_log("getgrgid(%u): %s", gid, pa_cstrerror(errno));
-
- if (!errno)
- errno = ENOENT;
+ pa_log("pa_getgrgid_malloc(%u): %s", gid, pa_cstrerror(errno));
goto finish;
}
- r = strcmp(name, result->gr_name) == 0;
+ r = strcmp(name, group->gr_name) == 0;
finish:
-#endif
+ pa_getgrgid_free(group);
return r;
}
@@ -1061,38 +1048,12 @@ finish:
/* Check whether the specifc user id is a member of the specified group */
int pa_uid_in_group(uid_t uid, const char *name) {
- char *g_buf, *p_buf;
- long g_n, p_n;
- struct group grbuf, *gr;
+ struct group *group = NULL;
char **i;
int r = -1;
-#ifdef _SC_GETGR_R_SIZE_MAX
- g_n = sysconf(_SC_GETGR_R_SIZE_MAX);
-#else
- g_n = -1;
-#endif
- if (g_n <= 0)
- g_n = 512;
-
- g_buf = pa_xmalloc((size_t) g_n);
-
-#ifdef _SC_GETPW_R_SIZE_MAX
- p_n = sysconf(_SC_GETPW_R_SIZE_MAX);
-#else
- p_n = -1;
-#endif
- if (p_n <= 0)
- p_n = 512;
-
- p_buf = pa_xmalloc((size_t) p_n);
-
errno = 0;
-#ifdef HAVE_GETGRNAM_R
- if (getgrnam_r(name, &grbuf, g_buf, (size_t) g_n, &gr) != 0 || !gr)
-#else
- if (!(gr = getgrnam(name)))
-#endif
+ if (!(group = pa_getgrnam_malloc(name)))
{
if (!errno)
errno = ENOENT;
@@ -1100,25 +1061,24 @@ int pa_uid_in_group(uid_t uid, const char *name) {
}
r = 0;
- for (i = gr->gr_mem; *i; i++) {
- struct passwd pwbuf, *pw;
+ for (i = group->gr_mem; *i; i++) {
+ struct passwd *pw = NULL;
-#ifdef HAVE_GETPWNAM_R
- if (getpwnam_r(*i, &pwbuf, p_buf, (size_t) p_n, &pw) != 0 || !pw)
-#else
- if (!(pw = getpwnam(*i)))
-#endif
+ errno = 0;
+ if (!(pw = pa_getpwnam_malloc(*i)))
continue;
- if (pw->pw_uid == uid) {
+ if (pw->pw_uid == uid)
r = 1;
+
+ pa_getpwnam_free(pw);
+
+ if (r == 1)
break;
- }
}
finish:
- pa_xfree(g_buf);
- pa_xfree(p_buf);
+ pa_getgrnam_free(group);
return r;
}
@@ -1126,26 +1086,10 @@ finish:
/* Get the GID of a gfiven group, return (gid_t) -1 on failure. */
gid_t pa_get_gid_of_group(const char *name) {
gid_t ret = (gid_t) -1;
- char *g_buf;
- long g_n;
- struct group grbuf, *gr;
-
-#ifdef _SC_GETGR_R_SIZE_MAX
- g_n = sysconf(_SC_GETGR_R_SIZE_MAX);
-#else
- g_n = -1;
-#endif
- if (g_n <= 0)
- g_n = 512;
-
- g_buf = pa_xmalloc((size_t) g_n);
+ struct group *gr = NULL;
errno = 0;
-#ifdef HAVE_GETGRNAM_R
- if (getgrnam_r(name, &grbuf, g_buf, (size_t) g_n, &gr) != 0 || !gr)
-#else
- if (!(gr = getgrnam(name)))
-#endif
+ if (!(gr = pa_getgrnam_malloc(name)))
{
if (!errno)
errno = ENOENT;
@@ -1155,7 +1099,7 @@ gid_t pa_get_gid_of_group(const char *name) {
ret = gr->gr_gid;
finish:
- pa_xfree(g_buf);
+ pa_getgrnam_free(gr);
return ret;
}
@@ -1250,16 +1194,13 @@ char* pa_strip_nl(char *s) {
/* Create a temporary lock file and lock it. */
int pa_lock_lockfile(const char *fn) {
- int fd = -1;
+ int fd;
pa_assert(fn);
for (;;) {
struct stat st;
- if ((fd = open(fn, O_CREAT|O_RDWR
-#ifdef O_NOCTTY
- |O_NOCTTY
-#endif
+ if ((fd = pa_open_cloexec(fn, O_CREAT|O_RDWR
#ifdef O_NOFOLLOW
|O_NOFOLLOW
#endif
@@ -1293,8 +1234,6 @@ int pa_lock_lockfile(const char *fn) {
fd = -1;
goto fail;
}
-
- fd = -1;
}
return fd;
@@ -1436,19 +1375,10 @@ static char* make_random_dir(mode_t m) {
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"0123456789";
- const char *tmpdir;
char *fn;
size_t pathlen;
- if (!(tmpdir = getenv("TMPDIR")))
- if (!(tmpdir = getenv("TMP")))
- if (!(tmpdir = getenv("TEMP")))
- tmpdir = getenv("TEMPDIR");
-
- if (!tmpdir || !pa_is_path_absolute(tmpdir))
- tmpdir = "/tmp";
-
- fn = pa_sprintf_malloc("%s/pulse-XXXXXXXXXXXX", tmpdir);
+ fn = pa_sprintf_malloc("%s" PA_PATH_SEP "pulse-XXXXXXXXXXXX", pa_get_temp_dir());
pathlen = strlen(fn);
for (;;) {
@@ -1670,7 +1600,7 @@ FILE *pa_open_config_file(const char *global, const char *local, const char *env
fn = buf;
#endif
- if ((f = fopen(fn, "r"))) {
+ if ((f = pa_fopen_cloexec(fn, "r"))) {
if (result)
*result = pa_xstrdup(fn);
@@ -1704,7 +1634,7 @@ FILE *pa_open_config_file(const char *global, const char *local, const char *env
fn = buf;
#endif
- if ((f = fopen(fn, "r"))) {
+ if ((f = pa_fopen_cloexec(fn, "r"))) {
if (result)
*result = pa_xstrdup(fn);
@@ -1731,7 +1661,7 @@ FILE *pa_open_config_file(const char *global, const char *local, const char *env
global = buf;
#endif
- if ((f = fopen(global, "r"))) {
+ if ((f = pa_fopen_cloexec(global, "r"))) {
if (result)
*result = pa_xstrdup(global);
@@ -1945,17 +1875,17 @@ char *pa_make_path_absolute(const char *p) {
static char *get_path(const char *fn, pa_bool_t prependmid, pa_bool_t rt) {
char *rtp;
- if (pa_is_path_absolute(fn))
- return pa_xstrdup(fn);
-
rtp = rt ? pa_get_runtime_dir() : pa_get_state_dir();
- if (!rtp)
- return NULL;
-
if (fn) {
char *r;
+ if (pa_is_path_absolute(fn))
+ return pa_xstrdup(fn);
+
+ if (!rtp)
+ return NULL;
+
if (prependmid) {
char *mid;
@@ -2291,7 +2221,7 @@ int pa_close_all(int except_fd, ...) {
va_end(ap);
r = pa_close_allv(p);
- free(p);
+ pa_xfree(p);
return r;
}
@@ -2462,7 +2392,7 @@ int pa_reset_sigs(int except, ...) {
p[i++] = except;
while ((sig = va_arg(ap, int)) >= 0)
- sig = p[i++];
+ p[i++] = sig;
}
p[i] = -1;
@@ -2519,9 +2449,38 @@ void pa_set_env(const char *key, const char *value) {
pa_assert(key);
pa_assert(value);
+ /* This is not thread-safe */
+
putenv(pa_sprintf_malloc("%s=%s", key, value));
}
+void pa_set_env_and_record(const char *key, const char *value) {
+ pa_assert(key);
+ pa_assert(value);
+
+ /* This is not thread-safe */
+
+ pa_set_env(key, value);
+ recorded_env = pa_strlist_prepend(recorded_env, key);
+}
+
+void pa_unset_env_recorded(void) {
+
+ /* This is not thread-safe */
+
+ for (;;) {
+ char *s;
+
+ recorded_env = pa_strlist_pop(recorded_env, &s);
+
+ if (!s)
+ break;
+
+ unsetenv(s);
+ pa_xfree(s);
+ }
+}
+
pa_bool_t pa_in_system_mode(void) {
const char *e;
@@ -2601,7 +2560,7 @@ char *pa_machine_id(void) {
* 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"))) {
+ if ((f = pa_fopen_cloexec(PA_MACHINE_ID, "r"))) {
char ln[34] = "", *r;
r = fgets(ln, sizeof(ln)-1, f);
@@ -2716,6 +2675,28 @@ char *pa_replace(const char*s, const char*a, const char *b) {
return pa_strbuf_tostring_free(sb);
}
+char *pa_escape(const char *p, const char *chars) {
+ const char *s;
+ const char *c;
+ pa_strbuf *buf = pa_strbuf_new();
+
+ for (s = p; *s; ++s) {
+ if (*s == '\\')
+ pa_strbuf_putc(buf, '\\');
+ else if (chars) {
+ for (c = chars; *c; ++c) {
+ if (*s == *c) {
+ pa_strbuf_putc(buf, '\\');
+ break;
+ }
+ }
+ }
+ pa_strbuf_putc(buf, *s);
+ }
+
+ return pa_strbuf_tostring_free(buf);
+}
+
char *pa_unescape(char *p) {
char *s, *d;
pa_bool_t escaped = FALSE;
@@ -2855,3 +2836,271 @@ size_t pa_pipe_buf(int fd) {
return 4096;
#endif
}
+
+void pa_reset_personality(void) {
+
+#ifdef __linux__
+ if (personality(PER_LINUX) < 0)
+ pa_log_warn("Uh, personality() failed: %s", pa_cstrerror(errno));
+#endif
+
+}
+
+#if defined(__linux__) && !defined(__OPTIMIZE__)
+
+pa_bool_t pa_run_from_build_tree(void) {
+ char *rp;
+ pa_bool_t b = FALSE;
+
+ /* We abuse __OPTIMIZE__ as a check whether we are a debug build
+ * or not. */
+
+ if ((rp = pa_readlink("/proc/self/exe"))) {
+ b = pa_startswith(rp, PA_BUILDDIR);
+ pa_xfree(rp);
+ }
+
+ return b;
+}
+
+#endif
+
+const char *pa_get_temp_dir(void) {
+ const char *t;
+
+ if ((t = getenv("TMPDIR")) &&
+ pa_is_path_absolute(t))
+ return t;
+
+ if ((t = getenv("TMP")) &&
+ pa_is_path_absolute(t))
+ return t;
+
+ if ((t = getenv("TEMP")) &&
+ pa_is_path_absolute(t))
+ return t;
+
+ if ((t = getenv("TEMPDIR")) &&
+ pa_is_path_absolute(t))
+ return t;
+
+ return "/tmp";
+}
+
+int pa_open_cloexec(const char *fn, int flags, mode_t mode) {
+ int fd;
+
+#ifdef O_NOCTTY
+ flags |= O_NOCTTY;
+#endif
+
+#ifdef O_CLOEXEC
+ if ((fd = open(fn, flags|O_CLOEXEC, mode)) >= 0)
+ goto finish;
+
+ if (errno != EINVAL)
+ return fd;
+#endif
+
+ if ((fd = open(fn, flags, mode)) < 0)
+ return fd;
+
+finish:
+ /* Some implementations might simply ignore O_CLOEXEC if it is not
+ * understood, make sure FD_CLOEXEC is enabled anyway */
+
+ pa_make_fd_cloexec(fd);
+ return fd;
+}
+
+int pa_socket_cloexec(int domain, int type, int protocol) {
+ int fd;
+
+#ifdef SOCK_CLOEXEC
+ if ((fd = socket(domain, type | SOCK_CLOEXEC, protocol)) >= 0)
+ goto finish;
+
+ if (errno != EINVAL)
+ return fd;
+#endif
+
+ if ((fd = socket(domain, type, protocol)) < 0)
+ return fd;
+
+finish:
+ /* Some implementations might simply ignore SOCK_CLOEXEC if it is
+ * not understood, make sure FD_CLOEXEC is enabled anyway */
+
+ pa_make_fd_cloexec(fd);
+ return fd;
+}
+
+int pa_pipe_cloexec(int pipefd[2]) {
+ int r;
+
+#ifdef HAVE_PIPE2
+ if ((r = pipe2(pipefd, O_CLOEXEC)) >= 0)
+ goto finish;
+
+ if (errno != EINVAL && errno != ENOSYS)
+ return r;
+
+#endif
+
+ if ((r = pipe(pipefd)) < 0)
+ return r;
+
+finish:
+ pa_make_fd_cloexec(pipefd[0]);
+ pa_make_fd_cloexec(pipefd[1]);
+
+ return 0;
+}
+
+int pa_accept_cloexec(int sockfd, struct sockaddr *addr, socklen_t *addrlen) {
+ int fd;
+
+#ifdef HAVE_ACCEPT4
+ if ((fd = accept4(sockfd, addr, addrlen, SOCK_CLOEXEC)) >= 0)
+ goto finish;
+
+ if (errno != EINVAL && errno != ENOSYS)
+ return fd;
+
+#endif
+
+ if ((fd = accept(sockfd, addr, addrlen)) < 0)
+ return fd;
+
+finish:
+ pa_make_fd_cloexec(fd);
+ return fd;
+}
+
+FILE* pa_fopen_cloexec(const char *path, const char *mode) {
+ FILE *f;
+ char *m;
+
+ m = pa_sprintf_malloc("%se", mode);
+
+ errno = 0;
+ if ((f = fopen(path, m))) {
+ pa_xfree(m);
+ goto finish;
+ }
+
+ pa_xfree(m);
+
+ if (errno != EINVAL)
+ return NULL;
+
+ if (!(f = fopen(path, mode)))
+ return NULL;
+
+finish:
+ pa_make_fd_cloexec(fileno(f));
+ return f;
+}
+
+void pa_nullify_stdfds(void) {
+
+#ifndef OS_IS_WIN32
+ pa_close(STDIN_FILENO);
+ pa_close(STDOUT_FILENO);
+ pa_close(STDERR_FILENO);
+
+ pa_assert_se(open("/dev/null", O_RDONLY) == STDIN_FILENO);
+ pa_assert_se(open("/dev/null", O_WRONLY) == STDOUT_FILENO);
+ pa_assert_se(open("/dev/null", O_WRONLY) == STDERR_FILENO);
+#else
+ FreeConsole();
+#endif
+
+}
+
+char *pa_read_line_from_file(const char *fn) {
+ FILE *f;
+ char ln[256] = "", *r;
+
+ if (!(f = pa_fopen_cloexec(fn, "r")))
+ return NULL;
+
+ r = fgets(ln, sizeof(ln)-1, f);
+ fclose(f);
+
+ if (!r) {
+ errno = EIO;
+ return NULL;
+ }
+
+ pa_strip_nl(ln);
+ return pa_xstrdup(ln);
+}
+
+pa_bool_t pa_running_in_vm(void) {
+
+#if defined(__i386__) || defined(__x86_64__)
+
+ /* Both CPUID and DMI are x86 specific interfaces... */
+
+ uint32_t eax = 0x40000000;
+ union {
+ uint32_t sig32[3];
+ char text[13];
+ } sig;
+
+#ifdef __linux__
+ const char *const dmi_vendors[] = {
+ "/sys/class/dmi/id/sys_vendor",
+ "/sys/class/dmi/id/board_vendor",
+ "/sys/class/dmi/id/bios_vendor"
+ };
+
+ unsigned i;
+
+ for (i = 0; i < PA_ELEMENTSOF(dmi_vendors); i++) {
+ char *s;
+
+ if ((s = pa_read_line_from_file(dmi_vendors[i]))) {
+
+ if (pa_startswith(s, "QEMU") ||
+ /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
+ pa_startswith(s, "VMware") ||
+ pa_startswith(s, "VMW") ||
+ pa_startswith(s, "Microsoft Corporation") ||
+ pa_startswith(s, "innotek GmbH") ||
+ pa_startswith(s, "Xen")) {
+
+ pa_xfree(s);
+ return TRUE;
+ }
+
+ pa_xfree(s);
+ }
+ }
+
+#endif
+
+ /* http://lwn.net/Articles/301888/ */
+ pa_zero(sig);
+
+ __asm__ __volatile__ (
+ " xor %%ebx, %%ebx \n\t"
+ " cpuid \n\t"
+
+ : "=a" (eax), "=b" (sig.sig32[0]), "=c" (sig.sig32[1]), "=d" (sig.sig32[2])
+ : "0" (eax)
+ );
+
+ if (pa_streq(sig.text, "XenVMMXenVMM") ||
+ pa_streq(sig.text, "KVMKVMKVM") ||
+ /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
+ pa_streq(sig.text, "VMwareVMware") ||
+ /* http://msdn.microsoft.com/en-us/library/bb969719.aspx */
+ pa_streq(sig.text, "Microsoft Hv"))
+ return TRUE;
+
+#endif
+
+ return FALSE;
+}
diff --git a/src/pulsecore/core-util.h b/src/pulsecore/core-util.h
index 6de4b771..31a83bcc 100644
--- a/src/pulsecore/core-util.h
+++ b/src/pulsecore/core-util.h
@@ -28,6 +28,7 @@
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
+#include <sys/socket.h>
#ifdef HAVE_SYS_RESOURCE_H
#include <sys/resource.h>
@@ -195,6 +196,8 @@ int pa_reset_sigs(int except, ...);
int pa_reset_sigsv(const int except[]);
void pa_set_env(const char *key, const char *value);
+void pa_set_env_and_record(const char *key, const char *value);
+void pa_unset_env_recorded(void);
pa_bool_t pa_in_system_mode(void);
@@ -222,6 +225,13 @@ unsigned pa_ncpus(void);
char *pa_replace(const char*s, const char*a, const char *b);
+/* Escapes p by inserting backslashes in front of backslashes. chars is a
+ * regular (ie. NULL-terminated) string containing additional characters that
+ * should be escaped. chars can be NULL. The caller has to free the returned
+ * string. */
+char *pa_escape(const char *p, const char *chars);
+
+/* Does regular backslash unescaping. Returns the argument p. */
char *pa_unescape(char *p);
char *pa_realpath(const char *path);
@@ -241,4 +251,23 @@ char* pa_maybe_prefix_path(const char *path, const char *prefix);
/* Returns size of the specified pipe or 4096 on failure */
size_t pa_pipe_buf(int fd);
+void pa_reset_personality(void);
+
+#if defined(__linux__) && !defined(__OPTIMIZE__)
+pa_bool_t pa_run_from_build_tree(void);
+#endif
+
+const char *pa_get_temp_dir(void);
+
+int pa_open_cloexec(const char *fn, int flags, mode_t mode);
+int pa_socket_cloexec(int domain, int type, int protocol);
+int pa_pipe_cloexec(int pipefd[2]);
+int pa_accept_cloexec(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
+FILE* pa_fopen_cloexec(const char *path, const char *mode);
+
+void pa_nullify_stdfds(void);
+
+char *pa_read_line_from_file(const char *fn);
+pa_bool_t pa_running_in_vm(void);
+
#endif
diff --git a/src/pulsecore/core.c b/src/pulsecore/core.c
index f5eb8352..f0726453 100644
--- a/src/pulsecore/core.c
+++ b/src/pulsecore/core.c
@@ -47,7 +47,7 @@
#include "core.h"
-static PA_DEFINE_CHECK_TYPE(pa_core, pa_msgobject);
+PA_DEFINE_PUBLIC_CLASS(pa_core, pa_msgobject);
static int core_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk) {
pa_core *c = PA_CORE(o);
diff --git a/src/pulsecore/core.h b/src/pulsecore/core.h
index e7abd61b..bfcea4f6 100644
--- a/src/pulsecore/core.h
+++ b/src/pulsecore/core.h
@@ -52,6 +52,13 @@ typedef enum pa_suspend_cause {
#include <pulsecore/sink-input.h>
#include <pulsecore/msgobject.h>
+typedef enum pa_server_type {
+ PA_SERVER_TYPE_UNSET,
+ PA_SERVER_TYPE_USER,
+ PA_SERVER_TYPE_SYSTEM,
+ PA_SERVER_TYPE_NONE
+} pa_server_type_t;
+
typedef enum pa_core_state {
PA_CORE_STARTUP,
PA_CORE_RUNNING,
@@ -83,7 +90,6 @@ typedef enum pa_core_hook {
PA_CORE_HOOK_SINK_INPUT_MOVE_FAIL,
PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED,
PA_CORE_HOOK_SINK_INPUT_PROPLIST_CHANGED,
- PA_CORE_HOOK_SINK_INPUT_SET_VOLUME,
PA_CORE_HOOK_SINK_INPUT_SEND_EVENT,
PA_CORE_HOOK_SOURCE_OUTPUT_NEW,
PA_CORE_HOOK_SOURCE_OUTPUT_FIXATE,
@@ -162,11 +168,13 @@ struct pa_core {
pa_resample_method_t resample_method;
int realtime_priority;
+ pa_server_type_t server_type;
+
/* hooks */
pa_hook hooks[PA_CORE_HOOK_MAX];
};
-PA_DECLARE_CLASS(pa_core);
+PA_DECLARE_PUBLIC_CLASS(pa_core);
#define PA_CORE(o) pa_core_cast(o)
enum {
diff --git a/src/pulsecore/cpu-arm.c b/src/pulsecore/cpu-arm.c
new file mode 100644
index 00000000..6bb2eadd
--- /dev/null
+++ b/src/pulsecore/cpu-arm.c
@@ -0,0 +1,139 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2004-2006 Lennart Poettering
+ Copyright 2009 Wim Taymans <wim.taymans@collabora.co.uk>
+
+ 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 <stdint.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <pulse/xmalloc.h>
+#include <pulsecore/log.h>
+
+#include "cpu-arm.h"
+
+#if defined (__arm__) && defined (__linux__)
+
+#define MAX_BUFFER 4096
+static char *
+get_cpuinfo_line (char *cpuinfo, const char *tag) {
+ char *line, *end, *colon;
+
+ if (!(line = strstr (cpuinfo, tag)))
+ return NULL;
+
+ if (!(end = strchr (line, '\n')))
+ return NULL;
+
+ if (!(colon = strchr (line, ':')))
+ return NULL;
+
+ if (++colon >= end)
+ return NULL;
+
+ return pa_xstrndup (colon, end - colon);
+}
+
+static char *get_cpuinfo(void) {
+ char *cpuinfo;
+ int n, fd;
+
+ cpuinfo = pa_xmalloc(MAX_BUFFER);
+
+ if ((fd = pa_open_cloexec("/proc/cpuinfo", O_RDONLY, 0)) < 0) {
+ pa_xfree(cpuinfo);
+ return NULL;
+ }
+
+ if ((n = pa_read(fd, cpuinfo, MAX_BUFFER-1)) < 0) {
+ pa_xfree(cpuinfo);
+ pa_close(fd);
+ return NULL;
+ }
+ cpuinfo[n] = 0;
+ pa_close(fd);
+
+ return cpuinfo;
+}
+#endif /* defined (__arm__) && defined (__linux__) */
+
+void pa_cpu_init_arm (void) {
+#if defined (__arm__)
+#if defined (__linux__)
+ char *cpuinfo, *line;
+ int arch;
+ pa_cpu_arm_flag_t flags = 0;
+
+ /* We need to read the CPU flags from /proc/cpuinfo because there is no user
+ * space support to get the CPU features. This only works on linux AFAIK. */
+ if (!(cpuinfo = get_cpuinfo ())) {
+ pa_log ("Can't read cpuinfo");
+ return;
+ }
+
+ /* get the CPU architecture */
+ if ((line = get_cpuinfo_line (cpuinfo, "CPU architecture"))) {
+ arch = strtoul (line, NULL, 0);
+ if (arch >= 6)
+ flags |= PA_CPU_ARM_V6;
+ if (arch >= 7)
+ flags |= PA_CPU_ARM_V7;
+
+ pa_xfree(line);
+ }
+ /* get the CPU features */
+ if ((line = get_cpuinfo_line (cpuinfo, "Features"))) {
+ char *state = NULL, *current;
+
+ while ((current = pa_split_spaces (line, &state))) {
+ if (!strcmp (current, "vfp"))
+ flags |= PA_CPU_ARM_VFP;
+ else if (!strcmp (current, "edsp"))
+ flags |= PA_CPU_ARM_EDSP;
+ else if (!strcmp (current, "neon"))
+ flags |= PA_CPU_ARM_NEON;
+ else if (!strcmp (current, "vfpv3"))
+ flags |= PA_CPU_ARM_VFPV3;
+
+ pa_xfree(current);
+ }
+ }
+ pa_xfree(cpuinfo);
+
+ pa_log_info ("CPU flags: %s%s%s%s%s%s",
+ (flags & PA_CPU_ARM_V6) ? "V6 " : "",
+ (flags & PA_CPU_ARM_V7) ? "V7 " : "",
+ (flags & PA_CPU_ARM_VFP) ? "VFP " : "",
+ (flags & PA_CPU_ARM_EDSP) ? "EDSP " : "",
+ (flags & PA_CPU_ARM_NEON) ? "NEON " : "",
+ (flags & PA_CPU_ARM_VFPV3) ? "VFPV3 " : "");
+#else /* defined (__linux__) */
+ pa_log ("ARM cpu features not yet supported on this OS");
+#endif /* defined (__linux__) */
+
+ if (flags & PA_CPU_ARM_V6)
+ pa_volume_func_init_arm (flags);
+#endif /* defined (__arm__) */
+}
diff --git a/src/pulsecore/cpu-arm.h b/src/pulsecore/cpu-arm.h
new file mode 100644
index 00000000..a87cb63b
--- /dev/null
+++ b/src/pulsecore/cpu-arm.h
@@ -0,0 +1,42 @@
+#ifndef foocpuarmhfoo
+#define foocpuarmhfoo
+
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2004-2006 Lennart Poettering
+ Copyright 2009 Wim Taymans <wim.taymans@collabora.co.uk>
+
+ 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 <stdint.h>
+
+typedef enum pa_cpu_arm_flag {
+ PA_CPU_ARM_V6 = (1 << 0),
+ PA_CPU_ARM_V7 = (1 << 1),
+ PA_CPU_ARM_VFP = (1 << 2),
+ PA_CPU_ARM_EDSP = (1 << 3),
+ PA_CPU_ARM_NEON = (1 << 4),
+ PA_CPU_ARM_VFPV3 = (1 << 5)
+} pa_cpu_arm_flag_t;
+
+void pa_cpu_init_arm (void);
+
+/* some optimized functions */
+void pa_volume_func_init_arm(pa_cpu_arm_flag_t flags);
+
+#endif /* foocpuarmhfoo */
diff --git a/src/pulsecore/cpu-x86.c b/src/pulsecore/cpu-x86.c
new file mode 100644
index 00000000..f194a608
--- /dev/null
+++ b/src/pulsecore/cpu-x86.c
@@ -0,0 +1,125 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2004-2006 Lennart Poettering
+ Copyright 2009 Wim Taymans <wim.taymans@collabora.co.uk>
+
+ 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 <stdint.h>
+
+#include <pulsecore/log.h>
+
+#include "cpu-x86.h"
+
+#if defined (__i386__) || defined (__amd64__)
+static void
+get_cpuid (uint32_t op, uint32_t *a, uint32_t *b, uint32_t *c, uint32_t *d)
+{
+ __asm__ __volatile__ (
+ " push %%"PA_REG_b" \n\t"
+ " cpuid \n\t"
+ " mov %%ebx, %%esi \n\t"
+ " pop %%"PA_REG_b" \n\t"
+
+ : "=a" (*a), "=S" (*b), "=c" (*c), "=d" (*d)
+ : "0" (op)
+ );
+}
+#endif
+
+void pa_cpu_init_x86 (void) {
+#if defined (__i386__) || defined (__amd64__)
+ uint32_t eax, ebx, ecx, edx;
+ uint32_t level;
+ pa_cpu_x86_flag_t flags = 0;
+
+ /* get standard level */
+ get_cpuid (0x00000000, &level, &ebx, &ecx, &edx);
+ if (level >= 1) {
+ get_cpuid (0x00000001, &eax, &ebx, &ecx, &edx);
+
+ if (edx & (1<<23))
+ flags |= PA_CPU_X86_MMX;
+
+ if (edx & (1<<25))
+ flags |= PA_CPU_X86_SSE;
+
+ if (edx & (1<<26))
+ flags |= PA_CPU_X86_SSE2;
+
+ if (ecx & (1<<0))
+ flags |= PA_CPU_X86_SSE3;
+
+ if (ecx & (1<<9))
+ flags |= PA_CPU_X86_SSSE3;
+
+ if (ecx & (1<<19))
+ flags |= PA_CPU_X86_SSE4_1;
+
+ if (ecx & (1<<20))
+ flags |= PA_CPU_X86_SSE4_2;
+ }
+
+ /* get extended level */
+ get_cpuid (0x80000000, &level, &ebx, &ecx, &edx);
+ if (level >= 0x80000001) {
+ get_cpuid (0x80000001, &eax, &ebx, &ecx, &edx);
+
+ if (edx & (1<<22))
+ flags |= PA_CPU_X86_MMXEXT;
+
+ if (edx & (1<<23))
+ flags |= PA_CPU_X86_MMX;
+
+ if (edx & (1<<30))
+ flags |= PA_CPU_X86_3DNOWEXT;
+
+ if (edx & (1<<31))
+ flags |= PA_CPU_X86_3DNOW;
+ }
+
+ pa_log_info ("CPU flags: %s%s%s%s%s%s%s%s%s%s",
+ (flags & PA_CPU_X86_MMX) ? "MMX " : "",
+ (flags & PA_CPU_X86_SSE) ? "SSE " : "",
+ (flags & PA_CPU_X86_SSE2) ? "SSE2 " : "",
+ (flags & PA_CPU_X86_SSE3) ? "SSE3 " : "",
+ (flags & PA_CPU_X86_SSSE3) ? "SSSE3 " : "",
+ (flags & PA_CPU_X86_SSE4_1) ? "SSE4_1 " : "",
+ (flags & PA_CPU_X86_SSE4_2) ? "SSE4_2 " : "",
+ (flags & PA_CPU_X86_MMXEXT) ? "MMXEXT " : "",
+ (flags & PA_CPU_X86_3DNOW) ? "3DNOW " : "",
+ (flags & PA_CPU_X86_3DNOWEXT) ? "3DNOWEXT " : "");
+
+ /* activate various optimisations */
+ if (flags & PA_CPU_X86_MMX) {
+ pa_volume_func_init_mmx (flags);
+ pa_remap_func_init_mmx (flags);
+ }
+
+ if (flags & (PA_CPU_X86_SSE | PA_CPU_X86_SSE2)) {
+ pa_volume_func_init_sse (flags);
+ pa_remap_func_init_sse (flags);
+ pa_convert_func_init_sse (flags);
+ }
+
+#endif /* defined (__i386__) || defined (__amd64__) */
+}
diff --git a/src/pulsecore/cpu-x86.h b/src/pulsecore/cpu-x86.h
new file mode 100644
index 00000000..f6484c59
--- /dev/null
+++ b/src/pulsecore/cpu-x86.h
@@ -0,0 +1,71 @@
+#ifndef foocpux86hfoo
+#define foocpux86hfoo
+
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2004-2006 Lennart Poettering
+ Copyright 2009 Wim Taymans <wim.taymans@collabora.co.uk>
+
+ 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 <stdint.h>
+
+typedef enum pa_cpu_x86_flag {
+ PA_CPU_X86_MMX = (1 << 0),
+ PA_CPU_X86_MMXEXT = (1 << 1),
+ PA_CPU_X86_SSE = (1 << 2),
+ PA_CPU_X86_SSE2 = (1 << 3),
+ PA_CPU_X86_SSE3 = (1 << 4),
+ PA_CPU_X86_SSSE3 = (1 << 5),
+ PA_CPU_X86_SSE4_1 = (1 << 6),
+ PA_CPU_X86_SSE4_2 = (1 << 7),
+ PA_CPU_X86_3DNOW = (1 << 8),
+ PA_CPU_X86_3DNOWEXT = (1 << 9)
+} pa_cpu_x86_flag_t;
+
+void pa_cpu_init_x86 (void);
+
+
+#if defined (__i386__)
+typedef int32_t pa_reg_x86;
+#define PA_REG_a "eax"
+#define PA_REG_b "ebx"
+#define PA_REG_c "ecx"
+#define PA_REG_d "edx"
+#define PA_REG_D "edi"
+#define PA_REG_S "esi"
+#elif defined (__amd64__)
+typedef int64_t pa_reg_x86;
+#define PA_REG_a "rax"
+#define PA_REG_b "rbx"
+#define PA_REG_c "rcx"
+#define PA_REG_d "rdx"
+#define PA_REG_D "rdi"
+#define PA_REG_S "rsi"
+#endif
+
+/* some optimized functions */
+void pa_volume_func_init_mmx(pa_cpu_x86_flag_t flags);
+void pa_volume_func_init_sse(pa_cpu_x86_flag_t flags);
+
+void pa_remap_func_init_mmx(pa_cpu_x86_flag_t flags);
+void pa_remap_func_init_sse(pa_cpu_x86_flag_t flags);
+
+void pa_convert_func_init_sse (pa_cpu_x86_flag_t flags);
+
+#endif /* foocpux86hfoo */
diff --git a/src/pulsecore/database-simple.c b/src/pulsecore/database-simple.c
new file mode 100644
index 00000000..754930db
--- /dev/null
+++ b/src/pulsecore/database-simple.c
@@ -0,0 +1,510 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2009 Nokia Corporation
+ Contact: Maemo Multimedia <multimedia@maemo.org>
+
+ 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 <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdio.h>
+
+#include <pulse/xmalloc.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/log.h>
+#include <pulsecore/core-error.h>
+#include <pulsecore/hashmap.h>
+
+#include "database.h"
+
+
+typedef struct simple_data {
+ char *filename;
+ char *tmp_filename;
+ pa_hashmap *map;
+ pa_bool_t read_only;
+} simple_data;
+
+typedef struct entry {
+ pa_datum key;
+ pa_datum data;
+} entry;
+
+void pa_datum_free(pa_datum *d) {
+ pa_assert(d);
+
+ pa_xfree(d->data);
+ d->data = NULL;
+ d->size = 0;
+}
+
+static int compare_func(const void *a, const void *b) {
+ const pa_datum *aa, *bb;
+
+ aa = (const pa_datum*)a;
+ bb = (const pa_datum*)b;
+
+ if (aa->size != bb->size)
+ return aa->size > bb->size ? 1 : -1;
+
+ return memcmp(aa->data, bb->data, aa->size);
+}
+
+/* pa_idxset_string_hash_func modified for our use */
+static unsigned hash_func(const void *p) {
+ const pa_datum *d;
+ unsigned hash = 0;
+ const char *c;
+ unsigned i;
+
+ d = (const pa_datum*)p;
+ c = d->data;
+
+ for (i = 0; i < d->size; i++) {
+ hash = 31 * hash + (unsigned) *c;
+ c++;
+ }
+
+ return hash;
+}
+
+static entry* new_entry(const pa_datum *key, const pa_datum *data) {
+ entry *e;
+
+ pa_assert(key);
+ pa_assert(data);
+
+ e = pa_xnew0(entry, 1);
+ e->key.data = key->size > 0 ? pa_xmemdup(key->data, key->size) : NULL;
+ e->key.size = key->size;
+ e->data.data = data->size > 0 ? pa_xmemdup(data->data, data->size) : NULL;
+ e->data.size = data->size;
+ return e;
+}
+
+static void free_entry(entry *e) {
+ if (e) {
+ if (e->key.data)
+ pa_xfree(e->key.data);
+ if (e->data.data)
+ pa_xfree(e->data.data);
+ pa_xfree(e);
+ }
+}
+
+static int read_uint(FILE *f, uint32_t *res) {
+ size_t items = 0;
+ uint8_t values[4];
+ uint32_t tmp;
+ int i;
+
+ items = fread(&values, sizeof(values), sizeof(uint8_t), f);
+
+ if (feof(f)) /* EOF */
+ return 0;
+
+ if (ferror(f))
+ return -1;
+
+ for (i = 0; i < 4; ++i) {
+ tmp = values[i];
+ *res += (tmp << (i*8));
+ }
+
+ return items;
+}
+
+static int read_data(FILE *f, void **data, ssize_t *length) {
+ size_t items = 0;
+ uint32_t data_len = 0;
+
+ pa_assert(f);
+
+ *data = NULL;
+ *length = 0;
+
+ if ((items = read_uint(f, &data_len)) <= 0)
+ return -1;
+
+ if (data_len > 0) {
+ *data = pa_xmalloc0(data_len);
+ items = fread(*data, data_len, 1, f);
+
+ if (feof(f)) /* EOF */
+ goto reset;
+
+ if (ferror(f))
+ goto reset;
+
+ *length = data_len;
+
+ } else { /* no data? */
+ return -1;
+ }
+
+ return 0;
+
+reset:
+ pa_xfree(*data);
+ *data = NULL;
+ *length = 0;
+ return -1;
+}
+
+static int fill_data(simple_data *db, FILE *f) {
+ pa_datum key;
+ pa_datum data;
+ void *d = NULL;
+ ssize_t l = 0;
+ pa_bool_t append = FALSE;
+ enum { FIELD_KEY = 0, FIELD_DATA } field = FIELD_KEY;
+
+ pa_assert(db);
+ pa_assert(db->map);
+
+ errno = 0;
+
+ key.size = 0;
+ key.data = NULL;
+
+ while (!read_data(f, &d, &l)) {
+
+ switch (field) {
+ case FIELD_KEY:
+ key.data = d;
+ key.size = l;
+ field = FIELD_DATA;
+ break;
+ case FIELD_DATA:
+ data.data = d;
+ data.size = l;
+ append = TRUE;
+ break;
+ }
+
+ if (append) {
+ entry *e = pa_xnew0(entry, 1);
+ e->key.data = key.data;
+ e->key.size = key.size;
+ e->data.data = data.data;
+ e->data.size = data.size;
+ pa_hashmap_put(db->map, &e->key, e);
+ append = FALSE;
+ field = FIELD_KEY;
+ }
+ }
+
+ if (ferror(f)) {
+ pa_log_warn("read error. %s", pa_cstrerror(errno));
+ pa_database_clear((pa_database*)db);
+ }
+
+ if (field == FIELD_DATA && d)
+ pa_xfree(d);
+
+ return pa_hashmap_size(db->map);
+}
+
+pa_database* pa_database_open(const char *fn, pa_bool_t for_write) {
+ FILE *f;
+ char *path;
+ simple_data *db;
+
+ pa_assert(fn);
+
+ path = pa_sprintf_malloc("%s."CANONICAL_HOST".simple", fn);
+ errno = 0;
+
+ f = pa_fopen_cloexec(path, "r");
+
+ if (f || errno == ENOENT) { /* file not found is ok */
+ db = pa_xnew0(simple_data, 1);
+ db->map = pa_hashmap_new(hash_func, compare_func);
+ db->filename = pa_xstrdup(path);
+ db->tmp_filename = pa_sprintf_malloc(".%s.tmp", db->filename);
+ db->read_only = !for_write;
+
+ if (f) {
+ fill_data(db, f);
+ fclose(f);
+ }
+ } else {
+ if (errno == 0)
+ errno = EIO;
+ db = NULL;
+ }
+
+ pa_xfree(path);
+
+ return (pa_database*) db;
+}
+
+void pa_database_close(pa_database *database) {
+ simple_data *db = (simple_data*)database;
+ pa_assert(db);
+
+ pa_database_sync(database);
+ pa_database_clear(database);
+ pa_xfree(db->filename);
+ pa_xfree(db->tmp_filename);
+ pa_hashmap_free(db->map, NULL, NULL);
+ pa_xfree(db);
+}
+
+pa_datum* pa_database_get(pa_database *database, const pa_datum *key, pa_datum* data) {
+ simple_data *db = (simple_data*)database;
+ entry *e;
+
+ pa_assert(db);
+ pa_assert(key);
+ pa_assert(data);
+
+ e = pa_hashmap_get(db->map, key);
+
+ if (!e)
+ return NULL;
+
+ data->data = e->data.size > 0 ? pa_xmemdup(e->data.data, e->data.size) : NULL;
+ data->size = e->data.size;
+
+ return data;
+}
+
+int pa_database_set(pa_database *database, const pa_datum *key, const pa_datum* data, pa_bool_t overwrite) {
+ simple_data *db = (simple_data*)database;
+ entry *e;
+ int ret = 0;
+
+ pa_assert(db);
+ pa_assert(key);
+ pa_assert(data);
+
+ if (db->read_only)
+ return -1;
+
+ e = new_entry(key, data);
+
+ if (pa_hashmap_put(db->map, &e->key, e) < 0) {
+ /* entry with same key exists in hashmap */
+ entry *r;
+ if (overwrite) {
+ r = pa_hashmap_remove(db->map, key);
+ pa_hashmap_put(db->map, &e->key, e);
+ } else {
+ /* wont't overwrite, so clean new entry */
+ r = e;
+ ret = -1;
+ }
+
+ free_entry(r);
+ }
+
+ return ret;
+}
+
+int pa_database_unset(pa_database *database, const pa_datum *key) {
+ simple_data *db = (simple_data*)database;
+ entry *e;
+
+ pa_assert(db);
+ pa_assert(key);
+
+ e = pa_hashmap_remove(db->map, key);
+ if (!e)
+ return -1;
+
+ free_entry(e);
+
+ return 0;
+}
+
+int pa_database_clear(pa_database *database) {
+ simple_data *db = (simple_data*)database;
+ entry *e;
+
+ pa_assert(db);
+
+ while ((e = pa_hashmap_steal_first(db->map)))
+ free_entry(e);
+
+ return 0;
+}
+
+signed pa_database_size(pa_database *database) {
+ simple_data *db = (simple_data*)database;
+ pa_assert(db);
+
+ return (signed) pa_hashmap_size(db->map);
+}
+
+pa_datum* pa_database_first(pa_database *database, pa_datum *key, pa_datum *data) {
+ simple_data *db = (simple_data*)database;
+ entry *e;
+
+ pa_assert(db);
+ pa_assert(key);
+
+ e = pa_hashmap_first(db->map);
+
+ if (!e)
+ return NULL;
+
+ key->data = e->key.size > 0 ? pa_xmemdup(e->key.data, e->key.size) : NULL;
+ key->size = e->key.size;
+
+ if (data) {
+ data->data = e->data.size > 0 ? pa_xmemdup(e->data.data, e->data.size) : NULL;
+ data->size = e->data.size;
+ }
+
+ return key;
+}
+
+pa_datum* pa_database_next(pa_database *database, const pa_datum *key, pa_datum *next, pa_datum *data) {
+ simple_data *db = (simple_data*)database;
+ entry *e;
+ entry *search;
+ void *state;
+ pa_bool_t pick_now;
+
+ pa_assert(db);
+ pa_assert(next);
+
+ if (!key)
+ return pa_database_first(database, next, data);
+
+ search = pa_hashmap_get(db->map, key);
+
+ state = NULL;
+ pick_now = FALSE;
+
+ while ((e = pa_hashmap_iterate(db->map, &state, NULL))) {
+ if (pick_now)
+ break;
+
+ if (search == e)
+ pick_now = TRUE;
+ }
+
+ if (!pick_now || !e)
+ return NULL;
+
+ next->data = e->key.size > 0 ? pa_xmemdup(e->key.data, e->key.size) : NULL;
+ next->size = e->key.size;
+
+ if (data) {
+ data->data = e->data.size > 0 ? pa_xmemdup(e->data.data, e->data.size) : NULL;
+ data->size = e->data.size;
+ }
+
+ return next;
+}
+
+static int write_uint(FILE *f, const uint32_t num) {
+ size_t items;
+ uint8_t values[4];
+ int i;
+ errno = 0;
+
+ for (i = 0; i < 4; i++)
+ values[i] = (num >> (i*8)) & 0xFF;
+
+ items = fwrite(&values, sizeof(values), sizeof(uint8_t), f);
+
+ if (ferror(f))
+ return -1;
+
+ return items;
+}
+
+static int write_data(FILE *f, void *data, const size_t length) {
+ size_t items;
+ uint32_t len;
+
+ len = length;
+ if ((items = write_uint(f, len)) <= 0)
+ return -1;
+
+ items = fwrite(data, length, 1, f);
+
+ if (ferror(f) || items != 1)
+ return -1;
+
+ return 0;
+}
+
+static int write_entry(FILE *f, const entry *e) {
+ pa_assert(f);
+ pa_assert(e);
+
+ if (write_data(f, e->key.data, e->key.size) < 0)
+ return -1;
+ if (write_data(f, e->data.data, e->data.size) < 0)
+ return -1;
+
+ return 0;
+}
+
+int pa_database_sync(pa_database *database) {
+ simple_data *db = (simple_data*)database;
+ FILE *f;
+ void *state;
+ entry *e;
+
+ pa_assert(db);
+
+ if (db->read_only)
+ return 0;
+
+ errno = 0;
+
+ f = pa_fopen_cloexec(db->tmp_filename, "w");
+
+ if (!f)
+ goto fail;
+
+ state = NULL;
+ while((e = pa_hashmap_iterate(db->map, &state, NULL))) {
+ if (write_entry(f, e) < 0) {
+ pa_log_warn("error while writing to file. %s", pa_cstrerror(errno));
+ goto fail;
+ }
+ }
+
+ fclose(f);
+ f = NULL;
+
+ if (rename(db->tmp_filename, db->filename) < 0) {
+ pa_log_warn("error while renaming file. %s", pa_cstrerror(errno));
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ if (f)
+ fclose(f);
+ return -1;
+}
diff --git a/src/pulsecore/database-tdb.c b/src/pulsecore/database-tdb.c
index b79d2837..4e782d65 100644
--- a/src/pulsecore/database-tdb.c
+++ b/src/pulsecore/database-tdb.c
@@ -66,6 +66,39 @@ void pa_datum_free(pa_datum *d) {
pa_zero(d);
}
+static struct tdb_context *tdb_open_cloexec(
+ const char *name,
+ int hash_size,
+ int tdb_flags,
+ int open_flags,
+ mode_t mode) {
+
+ /* Mimics pa_open_cloexec() */
+
+ struct tdb_context *c;
+
+#ifdef O_NOCTTY
+ open_flags |= O_NOCTTY;
+#endif
+
+#ifdef O_CLOEXEC
+ errno = 0;
+ if ((c = tdb_open(name, hash_size, tdb_flags, open_flags | O_CLOEXEC, mode)))
+ goto finish;
+
+ if (errno != EINVAL)
+ return NULL;
+#endif
+
+ errno = 0;
+ if (!(c = tdb_open(name, hash_size, tdb_flags, open_flags, mode)))
+ return NULL;
+
+finish:
+ pa_make_fd_cloexec(tdb_fd(c));
+ return c;
+}
+
pa_database* pa_database_open(const char *fn, pa_bool_t for_write) {
struct tdb_context *c;
char *path;
@@ -73,15 +106,7 @@ pa_database* pa_database_open(const char *fn, pa_bool_t for_write) {
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)
+ if ((c = tdb_open_cloexec(path, 0, TDB_NOSYNC|TDB_NOLOCK, (for_write ? O_RDWR|O_CREAT : O_RDONLY), 0644)))
pa_log_debug("Opened TDB database '%s'", path);
pa_xfree(path);
diff --git a/src/pulsecore/dbus-util.c b/src/pulsecore/dbus-util.c
index 4e6148f0..e3700ea5 100644
--- a/src/pulsecore/dbus-util.c
+++ b/src/pulsecore/dbus-util.c
@@ -28,6 +28,7 @@
#include <pulse/rtclock.h>
#include <pulse/timeval.h>
+#include <pulse/utf8.h>
#include <pulse/xmalloc.h>
#include <pulsecore/core-rtclock.h>
@@ -290,6 +291,31 @@ pa_dbus_wrap_connection* pa_dbus_wrap_connection_new(pa_mainloop_api *m, pa_bool
return pconn;
}
+pa_dbus_wrap_connection* pa_dbus_wrap_connection_new_from_existing(
+ pa_mainloop_api *m,
+ pa_bool_t use_rtclock,
+ DBusConnection *conn) {
+ pa_dbus_wrap_connection *pconn;
+
+ pa_assert(m);
+ pa_assert(conn);
+
+ pconn = pa_xnew(pa_dbus_wrap_connection, 1);
+ pconn->mainloop = m;
+ pconn->connection = dbus_connection_ref(conn);
+ pconn->use_rtclock = use_rtclock;
+
+ dbus_connection_set_exit_on_disconnect(conn, FALSE);
+ dbus_connection_set_dispatch_status_function(conn, dispatch_status, pconn, NULL);
+ dbus_connection_set_watch_functions(conn, add_watch, remove_watch, toggle_watch, pconn, NULL);
+ dbus_connection_set_timeout_functions(conn, add_timeout, remove_timeout, toggle_timeout, pconn, NULL);
+ dbus_connection_set_wakeup_main_function(conn, wakeup_main, pconn, NULL);
+
+ pconn->dispatch_event = pconn->mainloop->defer_new(pconn->mainloop, dispatch_cb, conn);
+
+ return pconn;
+}
+
void pa_dbus_wrap_connection_free(pa_dbus_wrap_connection* c) {
pa_assert(c);
@@ -422,3 +448,329 @@ void pa_dbus_free_pending_list(pa_dbus_pending **p) {
pa_dbus_pending_free(i);
}
}
+
+void pa_dbus_send_error(DBusConnection *c, DBusMessage *in_reply_to, const char *name, const char *format, ...) {
+ va_list ap;
+ char *message;
+ DBusMessage *reply = NULL;
+
+ pa_assert(c);
+ pa_assert(in_reply_to);
+ pa_assert(name);
+ pa_assert(format);
+
+ va_start(ap, format);
+ message = pa_vsprintf_malloc(format, ap);
+ va_end(ap);
+ pa_assert_se((reply = dbus_message_new_error(in_reply_to, name, message)));
+ pa_assert_se(dbus_connection_send(c, reply, NULL));
+
+ dbus_message_unref(reply);
+
+ pa_xfree(message);
+}
+
+void pa_dbus_send_empty_reply(DBusConnection *c, DBusMessage *in_reply_to) {
+ DBusMessage *reply = NULL;
+
+ pa_assert(c);
+ pa_assert(in_reply_to);
+
+ pa_assert_se((reply = dbus_message_new_method_return(in_reply_to)));
+ pa_assert_se(dbus_connection_send(c, reply, NULL));
+ dbus_message_unref(reply);
+}
+
+void pa_dbus_send_basic_value_reply(DBusConnection *c, DBusMessage *in_reply_to, int type, void *data) {
+ DBusMessage *reply = NULL;
+
+ pa_assert(c);
+ pa_assert(in_reply_to);
+ pa_assert(dbus_type_is_basic(type));
+ pa_assert(data);
+
+ pa_assert_se((reply = dbus_message_new_method_return(in_reply_to)));
+ pa_assert_se(dbus_message_append_args(reply, type, data, DBUS_TYPE_INVALID));
+ pa_assert_se(dbus_connection_send(c, reply, NULL));
+ dbus_message_unref(reply);
+}
+
+static const char *signature_from_basic_type(int type) {
+ switch (type) {
+ case DBUS_TYPE_BOOLEAN: return DBUS_TYPE_BOOLEAN_AS_STRING;
+ case DBUS_TYPE_BYTE: return DBUS_TYPE_BYTE_AS_STRING;
+ case DBUS_TYPE_INT16: return DBUS_TYPE_INT16_AS_STRING;
+ case DBUS_TYPE_UINT16: return DBUS_TYPE_UINT16_AS_STRING;
+ case DBUS_TYPE_INT32: return DBUS_TYPE_INT32_AS_STRING;
+ case DBUS_TYPE_UINT32: return DBUS_TYPE_UINT32_AS_STRING;
+ case DBUS_TYPE_INT64: return DBUS_TYPE_INT64_AS_STRING;
+ case DBUS_TYPE_UINT64: return DBUS_TYPE_UINT64_AS_STRING;
+ case DBUS_TYPE_DOUBLE: return DBUS_TYPE_DOUBLE_AS_STRING;
+ case DBUS_TYPE_STRING: return DBUS_TYPE_STRING_AS_STRING;
+ case DBUS_TYPE_OBJECT_PATH: return DBUS_TYPE_OBJECT_PATH_AS_STRING;
+ case DBUS_TYPE_SIGNATURE: return DBUS_TYPE_SIGNATURE_AS_STRING;
+ default: pa_assert_not_reached();
+ }
+}
+
+void pa_dbus_send_basic_variant_reply(DBusConnection *c, DBusMessage *in_reply_to, int type, void *data) {
+ DBusMessage *reply = NULL;
+ DBusMessageIter msg_iter;
+ DBusMessageIter variant_iter;
+
+ pa_assert(c);
+ pa_assert(in_reply_to);
+ pa_assert(dbus_type_is_basic(type));
+ pa_assert(data);
+
+ pa_assert_se((reply = dbus_message_new_method_return(in_reply_to)));
+ dbus_message_iter_init_append(reply, &msg_iter);
+ pa_assert_se(dbus_message_iter_open_container(&msg_iter,
+ DBUS_TYPE_VARIANT,
+ signature_from_basic_type(type),
+ &variant_iter));
+ pa_assert_se(dbus_message_iter_append_basic(&variant_iter, type, data));
+ pa_assert_se(dbus_message_iter_close_container(&msg_iter, &variant_iter));
+ pa_assert_se(dbus_connection_send(c, reply, NULL));
+ dbus_message_unref(reply);
+}
+
+/* Note: returns sizeof(char*) for strings, object paths and signatures. */
+static unsigned basic_type_size(int type) {
+ switch (type) {
+ case DBUS_TYPE_BOOLEAN: return sizeof(dbus_bool_t);
+ case DBUS_TYPE_BYTE: return 1;
+ case DBUS_TYPE_INT16: return sizeof(dbus_int16_t);
+ case DBUS_TYPE_UINT16: return sizeof(dbus_uint16_t);
+ case DBUS_TYPE_INT32: return sizeof(dbus_int32_t);
+ case DBUS_TYPE_UINT32: return sizeof(dbus_uint32_t);
+ case DBUS_TYPE_INT64: return sizeof(dbus_int64_t);
+ case DBUS_TYPE_UINT64: return sizeof(dbus_uint64_t);
+ case DBUS_TYPE_DOUBLE: return sizeof(double);
+ case DBUS_TYPE_STRING:
+ case DBUS_TYPE_OBJECT_PATH:
+ case DBUS_TYPE_SIGNATURE: return sizeof(char*);
+ default: pa_assert_not_reached();
+ }
+}
+
+void pa_dbus_send_basic_array_variant_reply(
+ DBusConnection *c,
+ DBusMessage *in_reply_to,
+ int item_type,
+ void *array,
+ unsigned n) {
+ DBusMessage *reply = NULL;
+ DBusMessageIter msg_iter;
+
+ pa_assert(c);
+ pa_assert(in_reply_to);
+ pa_assert(dbus_type_is_basic(item_type));
+ pa_assert(array || n == 0);
+
+ pa_assert_se((reply = dbus_message_new_method_return(in_reply_to)));
+ dbus_message_iter_init_append(reply, &msg_iter);
+ pa_dbus_append_basic_array_variant(&msg_iter, item_type, array, n);
+ pa_assert_se(dbus_connection_send(c, reply, NULL));
+ dbus_message_unref(reply);
+}
+
+void pa_dbus_send_proplist_variant_reply(DBusConnection *c, DBusMessage *in_reply_to, pa_proplist *proplist) {
+ DBusMessage *reply = NULL;
+ DBusMessageIter msg_iter;
+
+ pa_assert(c);
+ pa_assert(in_reply_to);
+ pa_assert(proplist);
+
+ pa_assert_se((reply = dbus_message_new_method_return(in_reply_to)));
+ dbus_message_iter_init_append(reply, &msg_iter);
+ pa_dbus_append_proplist_variant(&msg_iter, proplist);
+ pa_assert_se(dbus_connection_send(c, reply, NULL));
+ dbus_message_unref(reply);
+}
+
+void pa_dbus_append_basic_array(DBusMessageIter *iter, int item_type, const void *array, unsigned n) {
+ DBusMessageIter array_iter;
+ unsigned i;
+ unsigned item_size;
+
+ pa_assert(iter);
+ pa_assert(dbus_type_is_basic(item_type));
+ pa_assert(array || n == 0);
+
+ item_size = basic_type_size(item_type);
+
+ pa_assert_se(dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, signature_from_basic_type(item_type), &array_iter));
+
+ for (i = 0; i < n; ++i)
+ pa_assert_se(dbus_message_iter_append_basic(&array_iter, item_type, &((uint8_t*) array)[i * item_size]));
+
+ pa_assert_se(dbus_message_iter_close_container(iter, &array_iter));
+};
+
+void pa_dbus_append_basic_variant(DBusMessageIter *iter, int type, void *data) {
+ DBusMessageIter variant_iter;
+
+ pa_assert(iter);
+ pa_assert(dbus_type_is_basic(type));
+ pa_assert(data);
+
+ pa_assert_se(dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, signature_from_basic_type(type), &variant_iter));
+ pa_assert_se(dbus_message_iter_append_basic(&variant_iter, type, data));
+ pa_assert_se(dbus_message_iter_close_container(iter, &variant_iter));
+}
+
+void pa_dbus_append_basic_array_variant(DBusMessageIter *iter, int item_type, const void *array, unsigned n) {
+ DBusMessageIter variant_iter;
+ char *array_signature;
+
+ pa_assert(iter);
+ pa_assert(dbus_type_is_basic(item_type));
+ pa_assert(array || n == 0);
+
+ array_signature = pa_sprintf_malloc("a%c", *signature_from_basic_type(item_type));
+
+ pa_assert_se(dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, array_signature, &variant_iter));
+ pa_dbus_append_basic_array(&variant_iter, item_type, array, n);
+ pa_assert_se(dbus_message_iter_close_container(iter, &variant_iter));
+
+ pa_xfree(array_signature);
+}
+
+void pa_dbus_append_basic_variant_dict_entry(DBusMessageIter *dict_iter, const char *key, int type, void *data) {
+ DBusMessageIter dict_entry_iter;
+
+ pa_assert(dict_iter);
+ pa_assert(key);
+ pa_assert(dbus_type_is_basic(type));
+ pa_assert(data);
+
+ pa_assert_se(dbus_message_iter_open_container(dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry_iter));
+ pa_assert_se(dbus_message_iter_append_basic(&dict_entry_iter, DBUS_TYPE_STRING, &key));
+ pa_dbus_append_basic_variant(&dict_entry_iter, type, data);
+ pa_assert_se(dbus_message_iter_close_container(dict_iter, &dict_entry_iter));
+}
+
+void pa_dbus_append_basic_array_variant_dict_entry(
+ DBusMessageIter *dict_iter,
+ const char *key,
+ int item_type,
+ const void *array,
+ unsigned n) {
+ DBusMessageIter dict_entry_iter;
+
+ pa_assert(dict_iter);
+ pa_assert(key);
+ pa_assert(dbus_type_is_basic(item_type));
+ pa_assert(array || n == 0);
+
+ pa_assert_se(dbus_message_iter_open_container(dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry_iter));
+ pa_assert_se(dbus_message_iter_append_basic(&dict_entry_iter, DBUS_TYPE_STRING, &key));
+ pa_dbus_append_basic_array_variant(&dict_entry_iter, item_type, array, n);
+ pa_assert_se(dbus_message_iter_close_container(dict_iter, &dict_entry_iter));
+}
+
+void pa_dbus_append_proplist(DBusMessageIter *iter, pa_proplist *proplist) {
+ DBusMessageIter dict_iter;
+ DBusMessageIter dict_entry_iter;
+ DBusMessageIter array_iter;
+ void *state = NULL;
+ const char *key;
+
+ pa_assert(iter);
+ pa_assert(proplist);
+
+ pa_assert_se(dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "{say}", &dict_iter));
+
+ while ((key = pa_proplist_iterate(proplist, &state))) {
+ const void *value = NULL;
+ size_t nbytes;
+
+ pa_assert_se(pa_proplist_get(proplist, key, &value, &nbytes) >= 0);
+
+ pa_assert_se(dbus_message_iter_open_container(&dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry_iter));
+
+ pa_assert_se(dbus_message_iter_append_basic(&dict_entry_iter, DBUS_TYPE_STRING, &key));
+
+ pa_assert_se(dbus_message_iter_open_container(&dict_entry_iter, DBUS_TYPE_ARRAY, "y", &array_iter));
+ pa_assert_se(dbus_message_iter_append_fixed_array(&array_iter, DBUS_TYPE_BYTE, &value, nbytes));
+ pa_assert_se(dbus_message_iter_close_container(&dict_entry_iter, &array_iter));
+
+ pa_assert_se(dbus_message_iter_close_container(&dict_iter, &dict_entry_iter));
+ }
+
+ pa_assert_se(dbus_message_iter_close_container(iter, &dict_iter));
+}
+
+void pa_dbus_append_proplist_variant(DBusMessageIter *iter, pa_proplist *proplist) {
+ DBusMessageIter variant_iter;
+
+ pa_assert(iter);
+ pa_assert(proplist);
+
+ pa_assert_se(dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, "a{say}", &variant_iter));
+ pa_dbus_append_proplist(&variant_iter, proplist);
+ pa_assert_se(dbus_message_iter_close_container(iter, &variant_iter));
+}
+
+void pa_dbus_append_proplist_variant_dict_entry(DBusMessageIter *dict_iter, const char *key, pa_proplist *proplist) {
+ DBusMessageIter dict_entry_iter;
+
+ pa_assert(dict_iter);
+ pa_assert(key);
+ pa_assert(proplist);
+
+ pa_assert_se(dbus_message_iter_open_container(dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry_iter));
+ pa_assert_se(dbus_message_iter_append_basic(&dict_entry_iter, DBUS_TYPE_STRING, &key));
+ pa_dbus_append_proplist_variant(&dict_entry_iter, proplist);
+ pa_assert_se(dbus_message_iter_close_container(dict_iter, &dict_entry_iter));
+}
+
+pa_proplist *pa_dbus_get_proplist_arg(DBusConnection *c, DBusMessage *msg, DBusMessageIter *iter) {
+ DBusMessageIter dict_iter;
+ DBusMessageIter dict_entry_iter;
+ pa_proplist *proplist = NULL;
+ const char *key = NULL;
+ const uint8_t *value = NULL;
+ int value_length = 0;
+
+ pa_assert(c);
+ pa_assert(msg);
+ pa_assert(iter);
+ pa_assert(pa_streq(dbus_message_iter_get_signature(iter), "a{say}"));
+
+ proplist = pa_proplist_new();
+
+ dbus_message_iter_recurse(iter, &dict_iter);
+
+ while (dbus_message_iter_get_arg_type(&dict_iter) != DBUS_TYPE_INVALID) {
+ dbus_message_iter_recurse(&dict_iter, &dict_entry_iter);
+
+ dbus_message_iter_get_basic(&dict_entry_iter, &key);
+ dbus_message_iter_next(&dict_entry_iter);
+
+ if (strlen(key) <= 0 || !pa_ascii_valid(key)) {
+ pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Invalid property list key: '%s'.", key);
+ goto fail;
+ }
+
+ dbus_message_iter_get_fixed_array(&dict_entry_iter, &value, &value_length);
+
+ pa_assert(value_length >= 0);
+
+ pa_assert_se(pa_proplist_set(proplist, key, value, value_length) >= 0);
+
+ dbus_message_iter_next(&dict_iter);
+ }
+
+ dbus_message_iter_next(iter);
+
+ return proplist;
+
+fail:
+ if (proplist)
+ pa_proplist_free(proplist);
+
+ return NULL;
+}
diff --git a/src/pulsecore/dbus-util.h b/src/pulsecore/dbus-util.h
index 9ff298d8..f35e66cb 100644
--- a/src/pulsecore/dbus-util.h
+++ b/src/pulsecore/dbus-util.h
@@ -26,11 +26,16 @@
#include <pulsecore/llist.h>
#include <pulse/mainloop-api.h>
+#include <pulse/proplist.h>
/* A wrap connection is not shared or refcounted, it is available in client side */
typedef struct pa_dbus_wrap_connection pa_dbus_wrap_connection;
pa_dbus_wrap_connection* pa_dbus_wrap_connection_new(pa_mainloop_api *mainloop, pa_bool_t use_rtclock, DBusBusType type, DBusError *error);
+pa_dbus_wrap_connection* pa_dbus_wrap_connection_new_from_existing(
+ pa_mainloop_api *mainloop,
+ pa_bool_t use_rtclock,
+ DBusConnection *conn);
void pa_dbus_wrap_connection_free(pa_dbus_wrap_connection* conn);
DBusConnection* pa_dbus_wrap_connection_get(pa_dbus_wrap_connection *conn);
@@ -60,4 +65,42 @@ void pa_dbus_sync_pending_list(pa_dbus_pending **p);
/* Free up a list of pa_dbus_pending_call objects */
void pa_dbus_free_pending_list(pa_dbus_pending **p);
+/* Sends an error message as the reply to the given message. */
+void pa_dbus_send_error(
+ DBusConnection *c,
+ DBusMessage *in_reply_to,
+ const char *name,
+ const char *format, ...) PA_GCC_PRINTF_ATTR(4, 5);
+
+void pa_dbus_send_empty_reply(DBusConnection *c, DBusMessage *in_reply_to);
+void pa_dbus_send_basic_value_reply(DBusConnection *c, DBusMessage *in_reply_to, int type, void *data);
+void pa_dbus_send_basic_variant_reply(DBusConnection *c, DBusMessage *in_reply_to, int type, void *data);
+void pa_dbus_send_basic_array_variant_reply(
+ DBusConnection *c,
+ DBusMessage *in_reply_to,
+ int item_type,
+ void *array,
+ unsigned n);
+void pa_dbus_send_proplist_variant_reply(DBusConnection *c, DBusMessage *in_reply_to, pa_proplist *proplist);
+
+void pa_dbus_append_basic_array(DBusMessageIter *iter, int item_type, const void *array, unsigned n);
+void pa_dbus_append_basic_array_variant(DBusMessageIter *iter, int item_type, const void *array, unsigned n);
+void pa_dbus_append_basic_variant(DBusMessageIter *iter, int type, void *data);
+void pa_dbus_append_basic_variant_dict_entry(DBusMessageIter *dict_iter, const char *key, int type, void *data);
+void pa_dbus_append_basic_array_variant_dict_entry(
+ DBusMessageIter *dict_iter,
+ const char *key,
+ int item_type,
+ const void *array,
+ unsigned n);
+void pa_dbus_append_proplist(DBusMessageIter *iter, pa_proplist *proplist);
+void pa_dbus_append_proplist_variant(DBusMessageIter *iter, pa_proplist *proplist);
+void pa_dbus_append_proplist_variant_dict_entry(DBusMessageIter *dict_iter, const char *key, pa_proplist *proplist);
+
+/* Returns a new proplist that the caller has to free. If the proplist contains
+ * invalid keys, an error reply is sent and NULL is returned. The iterator must
+ * point to "a{say}" element. This function calls dbus_message_iter_next(iter)
+ * before returning. */
+pa_proplist *pa_dbus_get_proplist_arg(DBusConnection *c, DBusMessage *msg, DBusMessageIter *iter);
+
#endif
diff --git a/src/pulsecore/fdsem.c b/src/pulsecore/fdsem.c
index 380f34f5..ea14e8a7 100644
--- a/src/pulsecore/fdsem.c
+++ b/src/pulsecore/fdsem.c
@@ -62,19 +62,15 @@ pa_fdsem *pa_fdsem_new(void) {
f = pa_xmalloc(PA_ALIGN(sizeof(pa_fdsem)) + PA_ALIGN(sizeof(pa_fdsem_data)));
#ifdef HAVE_SYS_EVENTFD_H
- if ((f->efd = eventfd(0, 0)) >= 0) {
- pa_make_fd_cloexec(f->efd);
+ if ((f->efd = eventfd(0, EFD_CLOEXEC)) >= 0)
f->fds[0] = f->fds[1] = -1;
- } else
+ else
#endif
{
- if (pipe(f->fds) < 0) {
+ if (pa_pipe_cloexec(f->fds) < 0) {
pa_xfree(f);
return NULL;
}
-
- pa_make_fd_cloexec(f->fds[0]);
- pa_make_fd_cloexec(f->fds[1]);
}
f->data = (pa_fdsem_data*) ((uint8_t*) f + PA_ALIGN(sizeof(pa_fdsem)));
@@ -114,12 +110,11 @@ pa_fdsem *pa_fdsem_new_shm(pa_fdsem_data *data, int* event_fd) {
f = pa_xnew(pa_fdsem, 1);
- if ((f->efd = eventfd(0, 0)) < 0) {
+ if ((f->efd = eventfd(0, EFD_CLOEXEC)) < 0) {
pa_xfree(f);
return NULL;
}
- pa_make_fd_cloexec(f->efd);
f->fds[0] = f->fds[1] = -1;
f->data = data;
diff --git a/src/pulsecore/flist.c b/src/pulsecore/flist.c
index 6fb944f9..7e5ee244 100644
--- a/src/pulsecore/flist.c
+++ b/src/pulsecore/flist.c
@@ -130,15 +130,22 @@ void pa_flist_free(pa_flist *l, pa_free_cb_t free_cb) {
}
int pa_flist_push(pa_flist*l, void *p) {
- unsigned idx, n, len;
+ unsigned idx, n;
pa_atomic_ptr_t*cells;
+#ifdef PROFILE
+ unsigned len;
+#endif
pa_assert(l);
pa_assert(p);
cells = PA_FLIST_CELLS(l);
- n = len = l->size + N_EXTRA_SCAN - (unsigned) pa_atomic_load(&l->length);
+ n = l->size + N_EXTRA_SCAN - (unsigned) pa_atomic_load(&l->length);
+
+#ifdef PROFILE
+ len = n;
+#endif
_Y;
idx = reduce(l, (unsigned) pa_atomic_load(&l->write_idx));
@@ -171,14 +178,21 @@ int pa_flist_push(pa_flist*l, void *p) {
}
void* pa_flist_pop(pa_flist*l) {
- unsigned idx, len, n;
+ unsigned idx, n;
pa_atomic_ptr_t *cells;
+#ifdef PROFILE
+ unsigned len;
+#endif
pa_assert(l);
cells = PA_FLIST_CELLS(l);
- n = len = (unsigned) pa_atomic_load(&l->length) + N_EXTRA_SCAN;
+ n = (unsigned) pa_atomic_load(&l->length) + N_EXTRA_SCAN;
+
+#ifdef PROFILE
+ len = n;
+#endif
_Y;
idx = reduce(l, (unsigned) pa_atomic_load(&l->read_idx));
diff --git a/src/pulsecore/hook-list.c b/src/pulsecore/hook-list.c
index a00116d1..d9b9917d 100644
--- a/src/pulsecore/hook-list.c
+++ b/src/pulsecore/hook-list.c
@@ -97,7 +97,7 @@ pa_hook_result_t pa_hook_fire(pa_hook *hook, void *data) {
hook->n_firing ++;
- for (slot = hook->slots; slot; slot = slot->next) {
+ PA_LLIST_FOREACH(slot, hook->slots) {
if (slot->dead)
continue;
diff --git a/src/pulsecore/llist.h b/src/pulsecore/llist.h
index 58b51c68..27f174a9 100644
--- a/src/pulsecore/llist.h
+++ b/src/pulsecore/llist.h
@@ -107,4 +107,7 @@
#define PA_LLIST_FOREACH(i,head) \
for (i = (head); i; i = i->next)
+#define PA_LLIST_FOREACH_SAFE(i,n,head) \
+ for (i = (head); i && ((n = i->next), 1); i = n)
+
#endif
diff --git a/src/pulsecore/lock-autospawn.c b/src/pulsecore/lock-autospawn.c
index c0df7938..65e35634 100644
--- a/src/pulsecore/lock-autospawn.c
+++ b/src/pulsecore/lock-autospawn.c
@@ -87,12 +87,9 @@ static int ref(void) {
pa_assert(pipe_fd[0] < 0);
pa_assert(pipe_fd[1] < 0);
- if (pipe(pipe_fd) < 0)
+ if (pa_pipe_cloexec(pipe_fd) < 0)
return -1;
- pa_make_fd_cloexec(pipe_fd[0]);
- pa_make_fd_cloexec(pipe_fd[1]);
-
pa_make_fd_nonblock(pipe_fd[1]);
pa_make_fd_nonblock(pipe_fd[0]);
diff --git a/src/pulsecore/macro.h b/src/pulsecore/macro.h
index cf662510..9a5a2670 100644
--- a/src/pulsecore/macro.h
+++ b/src/pulsecore/macro.h
@@ -59,39 +59,42 @@
#endif
/* Rounds down */
-static inline void* pa_align_ptr(const void *p) {
- return (void*) (((size_t) p) & ~(sizeof(void*)-1));
+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*));
+static inline size_t PA_ALIGN(size_t l) {
+ return ((l + sizeof(void*) - 1) & ~(sizeof(void*) - 1));
}
-#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));
+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) / PA_PAGE_SIZE) * PA_PAGE_SIZE;
+static inline size_t PA_PAGE_ALIGN(size_t l) {
+ return (l + PA_PAGE_SIZE - 1) & ~(PA_PAGE_SIZE - 1);
}
-#define PA_PAGE_ALIGN(x) (pa_page_align(x))
#define PA_ELEMENTSOF(x) (sizeof(x)/sizeof((x)[0]))
-/* The users of PA_MIN and PA_MAX should be aware that these macros on
- * non-GCC executed code with side effects twice. It is thus
- * considered misuse to use code with side effects as arguments to MIN
- * and MAX. */
+#if defined(__GNUC__)
+ #define PA_DECLARE_ALIGNED(n,t,v) t v __attribute__ ((aligned (n)))
+#else
+ #define PA_DECLARE_ALIGNED(n,t,v) t v
+#endif
+
+/* The users of PA_MIN and PA_MAX, PA_CLAMP, PA_ROUND_UP should be
+ * aware that these macros on non-GCC executed code with side effects
+ * twice. It is thus considered misuse to use code with side effects
+ * as arguments to MIN and MAX. */
#ifdef __GNUC__
#define PA_MAX(a,b) \
- __extension__ ({ typeof(a) _a = (a); \
+ __extension__ ({ \
+ typeof(a) _a = (a); \
typeof(b) _b = (b); \
_a > _b ? _a : _b; \
})
@@ -101,7 +104,8 @@ static inline size_t pa_page_align(size_t l) {
#ifdef __GNUC__
#define PA_MIN(a,b) \
- __extension__ ({ typeof(a) _a = (a); \
+ __extension__ ({ \
+ typeof(a) _a = (a); \
typeof(b) _b = (b); \
_a < _b ? _a : _b; \
})
@@ -111,7 +115,8 @@ static inline size_t pa_page_align(size_t l) {
#ifdef __GNUC__
#define PA_CLAMP(x, low, high) \
- __extension__ ({ typeof(x) _x = (x); \
+ __extension__ ({ \
+ typeof(x) _x = (x); \
typeof(low) _low = (low); \
typeof(high) _high = (high); \
((_x > _high) ? _high : ((_x < _low) ? _low : _x)); \
@@ -122,7 +127,8 @@ static inline size_t pa_page_align(size_t l) {
#ifdef __GNUC__
#define PA_CLAMP_UNLIKELY(x, low, high) \
- __extension__ ({ typeof(x) _x = (x); \
+ __extension__ ({ \
+ typeof(x) _x = (x); \
typeof(low) _low = (low); \
typeof(high) _high = (high); \
(PA_UNLIKELY(_x > _high) ? _high : (PA_UNLIKELY(_x < _low) ? _low : _x)); \
@@ -135,6 +141,39 @@ static inline size_t pa_page_align(size_t l) {
* make sense: we cannot know if it is more likely that the values is
* lower or greater than the boundaries.*/
+#ifdef __GNUC__
+#define PA_ROUND_UP(a, b) \
+ __extension__ ({ \
+ typeof(a) _a = (a); \
+ typeof(b) _b = (b); \
+ ((_a + _b - 1) / _b) * _b; \
+ })
+#else
+#define PA_ROUND_UP(a, b) ((((a) + (b) - 1) / (b)) * (b))
+#endif
+
+#ifdef __GNUC__
+#define PA_ROUND_DOWN(a, b) \
+ __extension__ ({ \
+ typeof(a) _a = (a); \
+ typeof(b) _b = (b); \
+ (_a / _b) * _b; \
+ })
+#else
+#define PA_ROUND_DOWN(a, b) (((a) / (b)) * (b))
+#endif
+
+#ifdef __GNUC__
+#define PA_CLIP_SUB(a, b) \
+ __extension__ ({ \
+ typeof(a) _a = (a); \
+ typeof(b) _b = (b); \
+ _a > _b ? _a - _b : 0; \
+ })
+#else
+#define PA_CLIP_SUB(a, b) ((a) > (b) ? (a) - (b) : 0)
+#endif
+
/* This type is not intended to be used in exported APIs! Use classic "int" there! */
#ifdef HAVE_STD_BOOL
typedef _Bool pa_bool_t;
@@ -265,6 +304,18 @@ typedef int pa_bool_t;
#define pa_memzero(x,l) (memset((x), 0, (l)))
#define pa_zero(x) (pa_memzero(&(x), sizeof(x)))
+#define PA_INT_TYPE_SIGNED(type) (!!((type) 0 > (type) -1))
+
+#define PA_INT_TYPE_MAX(type) \
+ ((type) (PA_INT_TYPE_SIGNED(type) \
+ ? ~(~(type) 0 << (8*sizeof(type)-1)) \
+ : (type) -1))
+
+#define PA_INT_TYPE_MIN(type) \
+ ((type) (PA_INT_TYPE_SIGNED(type) \
+ ? (~(type) 0 << (8*sizeof(type)-1)) \
+ : (type) 0))
+
/* We include this at the very last place */
#include <pulsecore/log.h>
diff --git a/src/pulsecore/memblock.c b/src/pulsecore/memblock.c
index 3bc10de5..f38b17c6 100644
--- a/src/pulsecore/memblock.c
+++ b/src/pulsecore/memblock.c
@@ -258,7 +258,8 @@ static struct mempool_slot* mempool_allocate_slot(pa_mempool *p) {
slot = (struct mempool_slot*) ((uint8_t*) p->memory.ptr + (p->block_size * (size_t) idx));
if (!slot) {
- pa_log_debug("Pool full");
+ if (pa_log_ratelimit())
+ pa_log_debug("Pool full");
pa_atomic_inc(&p->stat.n_pool_full);
return NULL;
}
@@ -302,10 +303,17 @@ static struct mempool_slot* mempool_slot_by_ptr(pa_mempool *p, void *ptr) {
pa_memblock *pa_memblock_new_pool(pa_mempool *p, size_t length) {
pa_memblock *b = NULL;
struct mempool_slot *slot;
+ static int mempool_disable = 0;
pa_assert(p);
pa_assert(length);
+ if (mempool_disable == 0)
+ mempool_disable = getenv("PULSE_MEMPOOL_DISABLE") ? 1 : -1;
+
+ if (mempool_disable > 0)
+ return NULL;
+
/* If -1 is passed as length we choose the size for the caller: we
* take the largest size that fits in one of our slots. */
@@ -360,6 +368,7 @@ pa_memblock *pa_memblock_new_fixed(pa_mempool *p, void *d, size_t length, pa_boo
if (!(b = pa_flist_pop(PA_STATIC_FLIST_GET(unused_memblocks))))
b = pa_xnew(pa_memblock, 1);
+
PA_REFCNT_INIT(b);
b->pool = p;
b->type = PA_MEMBLOCK_FIXED;
@@ -386,6 +395,7 @@ pa_memblock *pa_memblock_new_user(pa_mempool *p, void *d, size_t length, pa_free
if (!(b = pa_flist_pop(PA_STATIC_FLIST_GET(unused_memblocks))))
b = pa_xnew(pa_memblock, 1);
+
PA_REFCNT_INIT(b);
b->pool = p;
b->type = PA_MEMBLOCK_USER;
@@ -498,13 +508,19 @@ static void memblock_free(pa_memblock *b) {
/* Fall through */
case PA_MEMBLOCK_FIXED:
- case PA_MEMBLOCK_APPENDED :
if (pa_flist_push(PA_STATIC_FLIST_GET(unused_memblocks), b) < 0)
pa_xfree(b);
break;
- case PA_MEMBLOCK_IMPORTED : {
+ case PA_MEMBLOCK_APPENDED:
+
+ /* We could attached it unused_memblocks, but that would
+ * probably waste some considerable memory */
+ pa_xfree(b);
+ break;
+
+ case PA_MEMBLOCK_IMPORTED: {
pa_memimport_segment *segment;
pa_memimport *import;
@@ -538,8 +554,7 @@ static void memblock_free(pa_memblock *b) {
struct mempool_slot *slot;
pa_bool_t call_free;
- slot = mempool_slot_by_ptr(b->pool, pa_atomic_ptr_load(&b->data));
- pa_assert(slot);
+ pa_assert_se(slot = mempool_slot_by_ptr(b->pool, pa_atomic_ptr_load(&b->data)));
call_free = b->type == PA_MEMBLOCK_POOL_EXTERNAL;
@@ -693,7 +708,7 @@ static void memblock_replace_import(pa_memblock *b) {
pa_mempool* pa_mempool_new(pa_bool_t shared, size_t size) {
pa_mempool *p;
- char t1[64], t2[64];
+ char t1[PA_BYTES_SNPRINT_MAX], t2[PA_BYTES_SNPRINT_MAX];
p = pa_xnew(pa_mempool, 1);
@@ -890,7 +905,7 @@ static pa_memimport_segment* segment_attach(pa_memimport *i, uint32_t shm_id) {
if (pa_hashmap_size(i->segments) >= PA_MEMIMPORT_SEGMENTS_MAX)
return NULL;
- seg = pa_xnew(pa_memimport_segment, 1);
+ seg = pa_xnew0(pa_memimport_segment, 1);
if (pa_shm_attach_ro(&seg->memory, shm_id) < 0) {
pa_xfree(seg);
@@ -898,10 +913,9 @@ static pa_memimport_segment* segment_attach(pa_memimport *i, uint32_t shm_id) {
}
seg->import = i;
- seg->n_blocks = 0;
seg->trap = pa_memtrap_add(seg->memory.ptr, seg->memory.size);
- pa_hashmap_put(i->segments, PA_UINT32_TO_PTR(shm_id), seg);
+ pa_hashmap_put(i->segments, PA_UINT32_TO_PTR(seg->memory.id), seg);
return seg;
}
diff --git a/src/pulsecore/memblockq.c b/src/pulsecore/memblockq.c
index 77f9efc9..32758be3 100644
--- a/src/pulsecore/memblockq.c
+++ b/src/pulsecore/memblockq.c
@@ -692,6 +692,12 @@ size_t pa_memblockq_get_minreq(pa_memblockq *bq) {
return bq->minreq;
}
+size_t pa_memblockq_get_maxrewind(pa_memblockq *bq) {
+ pa_assert(bq);
+
+ return bq->maxrewind;
+}
+
int64_t pa_memblockq_get_read_index(pa_memblockq *bq) {
pa_assert(bq);
diff --git a/src/pulsecore/memblockq.h b/src/pulsecore/memblockq.h
index 146d261b..587c364b 100644
--- a/src/pulsecore/memblockq.h
+++ b/src/pulsecore/memblockq.h
@@ -141,6 +141,9 @@ size_t pa_memblockq_get_prebuf(pa_memblockq *bq);
/* Returns the minimal request value */
size_t pa_memblockq_get_minreq(pa_memblockq *bq);
+/* Returns the maximal rewind value */
+size_t pa_memblockq_get_maxrewind(pa_memblockq *bq);
+
/* Return the base unit in bytes */
size_t pa_memblockq_get_base(pa_memblockq *bq);
diff --git a/src/pulsecore/memtrap.c b/src/pulsecore/memtrap.c
index c647e507..4fc18210 100644
--- a/src/pulsecore/memtrap.c
+++ b/src/pulsecore/memtrap.c
@@ -107,7 +107,10 @@ static void memtrap_link(pa_memtrap *m, unsigned j) {
pa_assert(m);
m->prev[j] = NULL;
- m->next[j] = memtraps[j];
+
+ if ((m->next[j] = memtraps[j]))
+ m->next[j]->prev[j] = m;
+
memtraps[j] = m;
}
@@ -200,13 +203,13 @@ pa_memtrap *pa_memtrap_update(pa_memtrap *m, const void *start, size_t size) {
goto unlock;
memtrap_unlink(m, j);
- j = pa_aupdate_write_swap(aupdate);
+ pa_aupdate_write_swap(aupdate);
m->start = (void*) start;
m->size = size;
pa_atomic_store(&m->bad, 0);
- j = pa_aupdate_write_swap(aupdate);
+ pa_assert_se(pa_aupdate_write_swap(aupdate) == j);
memtrap_link(m, j);
unlock:
diff --git a/src/pulsecore/modargs.c b/src/pulsecore/modargs.c
index c7d734d9..e78cdb9a 100644
--- a/src/pulsecore/modargs.c
+++ b/src/pulsecore/modargs.c
@@ -415,3 +415,13 @@ int pa_modargs_get_proplist(pa_modargs *ma, const char *name, pa_proplist *p, pa
return 0;
}
+
+const char *pa_modargs_iterate(pa_modargs *ma, void **state) {
+ pa_hashmap *map = (pa_hashmap*) ma;
+ struct entry *e;
+
+ if (!(e = pa_hashmap_iterate(map, state, NULL)))
+ return NULL;
+
+ return e->key;
+}
diff --git a/src/pulsecore/modargs.h b/src/pulsecore/modargs.h
index b3125b10..1ed66e9a 100644
--- a/src/pulsecore/modargs.h
+++ b/src/pulsecore/modargs.h
@@ -60,4 +60,13 @@ int pa_modargs_get_sample_spec_and_channel_map(pa_modargs *ma, pa_sample_spec *s
int pa_modargs_get_proplist(pa_modargs *ma, const char *name, pa_proplist *p, pa_update_mode_t m);
+/* Iterate through the module argument list. The user should allocate a
+ * state variable of type void* and initialize it with NULL. A pointer
+ * to this variable should then be passed to pa_modargs_iterate()
+ * which should be called in a loop until it returns NULL which
+ * signifies EOL. On each invication this function will return the
+ * key string for the next entry. The keys in the argument list do not
+ * have any particular order. */
+const char *pa_modargs_iterate(pa_modargs *ma, void **state);
+
#endif
diff --git a/src/pulsecore/msgobject.c b/src/pulsecore/msgobject.c
index 6a2a612d..075a28c5 100644
--- a/src/pulsecore/msgobject.c
+++ b/src/pulsecore/msgobject.c
@@ -26,22 +26,22 @@
#include "msgobject.h"
-PA_DEFINE_CHECK_TYPE(pa_msgobject, pa_object);
+PA_DEFINE_PUBLIC_CLASS(pa_msgobject, pa_object);
-pa_msgobject *pa_msgobject_new_internal(size_t size, const char *type_name, int (*check_type)(const char *type_name)) {
+pa_msgobject *pa_msgobject_new_internal(size_t size, const char *type_id, pa_bool_t (*check_type)(const char *type_name)) {
pa_msgobject *o;
pa_assert(size > sizeof(pa_msgobject));
- pa_assert(type_name);
+ pa_assert(type_id);
if (!check_type)
check_type = pa_msgobject_check_type;
- pa_assert(check_type(type_name));
- pa_assert(check_type("pa_object"));
- pa_assert(check_type("pa_msgobject"));
+ pa_assert(check_type(type_id));
+ pa_assert(check_type(pa_object_type_id));
+ pa_assert(check_type(pa_msgobject_type_id));
- o = PA_MSGOBJECT(pa_object_new_internal(size, type_name, check_type));
+ o = PA_MSGOBJECT(pa_object_new_internal(size, type_id, check_type));
o->process_msg = NULL;
return o;
}
diff --git a/src/pulsecore/msgobject.h b/src/pulsecore/msgobject.h
index a35a23b5..ee0ec1ed 100644
--- a/src/pulsecore/msgobject.h
+++ b/src/pulsecore/msgobject.h
@@ -38,15 +38,13 @@ struct pa_msgobject {
int (*process_msg)(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk);
};
-pa_msgobject *pa_msgobject_new_internal(size_t size, const char *type_name, int (*check_type)(const char *type_name));
+pa_msgobject *pa_msgobject_new_internal(size_t size, const char *type_id, pa_bool_t (*check_type)(const char *type_name));
-int pa_msgobject_check_type(const char *type);
-
-#define pa_msgobject_new(type) ((type*) pa_msgobject_new_internal(sizeof(type), #type, type##_check_type))
+#define pa_msgobject_new(type) ((type*) pa_msgobject_new_internal(sizeof(type), type##_type_id, type##_check_type))
#define pa_msgobject_free ((void (*) (pa_msgobject* o)) pa_object_free)
#define PA_MSGOBJECT(o) pa_msgobject_cast(o)
-PA_DECLARE_CLASS(pa_msgobject);
+PA_DECLARE_PUBLIC_CLASS(pa_msgobject);
#endif
diff --git a/src/pulsecore/namereg.c b/src/pulsecore/namereg.c
index 9df2f583..d9821876 100644
--- a/src/pulsecore/namereg.c
+++ b/src/pulsecore/namereg.c
@@ -57,6 +57,8 @@ static pa_bool_t is_valid_char(char c) {
pa_bool_t pa_namereg_is_valid_name(const char *name) {
const char *c;
+ pa_assert(name);
+
if (*name == 0)
return FALSE;
@@ -70,6 +72,25 @@ pa_bool_t pa_namereg_is_valid_name(const char *name) {
return TRUE;
}
+pa_bool_t pa_namereg_is_valid_name_or_wildcard(const char *name, pa_namereg_type_t type) {
+
+ pa_assert(name);
+
+ if (pa_namereg_is_valid_name(name))
+ return TRUE;
+
+ if (type == PA_NAMEREG_SINK &&
+ pa_streq(name, "@DEFAULT_SINK@"))
+ return TRUE;
+
+ if (type == PA_NAMEREG_SOURCE &&
+ (pa_streq(name, "@DEFAULT_SOURCE@") ||
+ pa_streq(name, "@DEFAULT_MONITOR@")))
+ return TRUE;
+
+ return FALSE;
+}
+
char* pa_namereg_make_valid_name(const char *name) {
const char *a;
char *b, *n;
@@ -77,7 +98,7 @@ char* pa_namereg_make_valid_name(const char *name) {
if (*name == 0)
return NULL;
- n = pa_xmalloc(strlen(name)+1);
+ n = pa_xnew(char, strlen(name)+1);
for (a = name, b = n; *a && (a-name < PA_NAME_MAX); a++, b++)
*b = (char) (is_valid_char(*a) ? *a : '_');
@@ -149,6 +170,17 @@ const char *pa_namereg_register(pa_core *c, const char *name, pa_namereg_type_t
pa_assert_se(pa_hashmap_put(c->namereg, e->name, e) >= 0);
+ /* If a sink or source is registered and there was none registered
+ * before we inform the clients which then can ask for the default
+ * sink/source which is then assigned. We don't adjust the default
+ * sink/source here right away to give the module the chance to
+ * register more sinks/sources before we choose a new default
+ * sink/source. */
+
+ if ((!c->default_sink && type == PA_NAMEREG_SINK) ||
+ (!c->default_source && type == PA_NAMEREG_SOURCE))
+ pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SERVER|PA_SUBSCRIPTION_EVENT_CHANGE, PA_INVALID_INDEX);
+
return e->name;
}
@@ -191,7 +223,6 @@ void* pa_namereg_get(pa_core *c, const char *name, pa_namereg_type_t type) {
if ((s = pa_namereg_get(c, NULL, PA_NAMEREG_SINK)))
return s->monitor_source;
-
}
if (!name)
@@ -223,6 +254,9 @@ void* pa_namereg_get(pa_core *c, const char *name, pa_namereg_type_t type) {
pa_sink* pa_namereg_set_default_sink(pa_core*c, pa_sink *s) {
pa_assert(c);
+ if (s && !PA_SINK_IS_LINKED(pa_sink_get_state(s)))
+ return NULL;
+
if (c->default_sink != s) {
c->default_sink = s;
pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SERVER|PA_SUBSCRIPTION_EVENT_CHANGE, PA_INVALID_INDEX);
@@ -234,6 +268,9 @@ pa_sink* pa_namereg_set_default_sink(pa_core*c, pa_sink *s) {
pa_source* pa_namereg_set_default_source(pa_core*c, pa_source *s) {
pa_assert(c);
+ if (s && !PA_SOURCE_IS_LINKED(pa_source_get_state(s)))
+ return NULL;
+
if (c->default_source != s) {
c->default_source = s;
pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SERVER|PA_SUBSCRIPTION_EVENT_CHANGE, PA_INVALID_INDEX);
@@ -243,34 +280,57 @@ pa_source* pa_namereg_set_default_source(pa_core*c, pa_source *s) {
}
pa_sink *pa_namereg_get_default_sink(pa_core *c) {
- pa_sink *s;
+ pa_sink *s, *best = NULL;
+ uint32_t idx;
pa_assert(c);
- if (c->default_sink)
+ if (c->default_sink && PA_SINK_IS_LINKED(pa_sink_get_state(c->default_sink)))
return c->default_sink;
- if ((s = pa_idxset_first(c->sinks, NULL)))
- return pa_namereg_set_default_sink(c, s);
+ PA_IDXSET_FOREACH(s, c->sinks, idx)
+ if (PA_SINK_IS_LINKED(pa_sink_get_state(s)))
+ if (!best || s->priority > best->priority)
+ best = s;
+
+ if (best)
+ return pa_namereg_set_default_sink(c, best);
return NULL;
}
pa_source *pa_namereg_get_default_source(pa_core *c) {
- pa_source *s;
+ pa_source *s, *best = NULL;
uint32_t idx;
pa_assert(c);
- if (c->default_source)
+ if (c->default_source && PA_SOURCE_IS_LINKED(pa_source_get_state(c->default_source)))
return c->default_source;
- for (s = PA_SOURCE(pa_idxset_first(c->sources, &idx)); s; s = PA_SOURCE(pa_idxset_next(c->sources, &idx)))
- if (!s->monitor_of)
- return pa_namereg_set_default_source(c, s);
-
- if ((s = pa_idxset_first(c->sources, NULL)))
- return pa_namereg_set_default_source(c, s);
+ /* First, try to find one that isn't a monitor */
+ PA_IDXSET_FOREACH(s, c->sources, idx)
+ if (!s->monitor_of && PA_SOURCE_IS_LINKED(pa_source_get_state(s)))
+ if (!best ||
+ s->priority > best->priority)
+ best = s;
+
+ if (best)
+ return pa_namereg_set_default_source(c, best);
+
+ /* Then, fallback to a monitor */
+ PA_IDXSET_FOREACH(s, c->sources, idx)
+ if (PA_SOURCE_IS_LINKED(pa_source_get_state(s)))
+ if (!best ||
+ s->priority > best->priority ||
+ (s->priority == best->priority &&
+ s->monitor_of &&
+ best->monitor_of &&
+ s->monitor_of->priority > best->monitor_of->priority))
+ best = s;
+
+ if (best)
+ return pa_namereg_set_default_source(c, best);
return NULL;
}
diff --git a/src/pulsecore/namereg.h b/src/pulsecore/namereg.h
index 38fae6f5..b5a976d7 100644
--- a/src/pulsecore/namereg.h
+++ b/src/pulsecore/namereg.h
@@ -45,6 +45,7 @@ pa_sink *pa_namereg_get_default_sink(pa_core *c);
pa_source *pa_namereg_get_default_source(pa_core *c);
pa_bool_t pa_namereg_is_valid_name(const char *name);
+pa_bool_t pa_namereg_is_valid_name_or_wildcard(const char *name, pa_namereg_type_t type);
char* pa_namereg_make_valid_name(const char *name);
#endif
diff --git a/src/pulsecore/object.c b/src/pulsecore/object.c
index f3ead9c5..099d50d9 100644
--- a/src/pulsecore/object.c
+++ b/src/pulsecore/object.c
@@ -28,21 +28,23 @@
#include "object.h"
-pa_object *pa_object_new_internal(size_t size, const char *type_name, int (*check_type)(const char *type_name)) {
+const char pa_object_type_id[] = "pa_object";
+
+pa_object *pa_object_new_internal(size_t size, const char *type_id, pa_bool_t (*check_type)(const char *type_id)) {
pa_object *o;
pa_assert(size > sizeof(pa_object));
- pa_assert(type_name);
+ pa_assert(type_id);
if (!check_type)
check_type = pa_object_check_type;
- pa_assert(check_type(type_name));
- pa_assert(check_type("pa_object"));
+ pa_assert(check_type(type_id));
+ pa_assert(check_type(pa_object_type_id));
o = pa_xmalloc(size);
PA_REFCNT_INIT(o);
- o->type_name = type_name;
+ o->type_id = type_id;
o->free = pa_object_free;
o->check_type = check_type;
@@ -65,8 +67,8 @@ void pa_object_unref(pa_object *o) {
}
}
-int pa_object_check_type(const char *type_name) {
- pa_assert(type_name);
+pa_bool_t pa_object_check_type(const char *type_id) {
+ pa_assert(type_id);
- return pa_streq(type_name, "pa_object");
+ return type_id == pa_object_type_id;
}
diff --git a/src/pulsecore/object.h b/src/pulsecore/object.h
index 43e79327..4c120cd5 100644
--- a/src/pulsecore/object.h
+++ b/src/pulsecore/object.h
@@ -34,21 +34,23 @@ typedef struct pa_object pa_object;
struct pa_object {
PA_REFCNT_DECLARE;
- const char *type_name;
+ const char *type_id;
void (*free)(pa_object *o);
- int (*check_type)(const char *type_name);
+ pa_bool_t (*check_type)(const char *type_name);
};
-pa_object *pa_object_new_internal(size_t size, const char *type_name, int (*check_type)(const char *type_name));
-#define pa_object_new(type) ((type*) pa_object_new_internal(sizeof(type), #type, type##_check_type)
+pa_object *pa_object_new_internal(size_t size, const char *type_id, pa_bool_t (*check_type)(const char *type_id));
+#define pa_object_new(type) ((type*) pa_object_new_internal(sizeof(type), type##_type_id, type##_check_type)
#define pa_object_free ((void (*) (pa_object* _obj)) pa_xfree)
-int pa_object_check_type(const char *type);
+pa_bool_t pa_object_check_type(const char *type_id);
-static inline int pa_object_isinstance(void *o) {
+extern const char pa_object_type_id[];
+
+static inline pa_bool_t pa_object_isinstance(void *o) {
pa_object *obj = (pa_object*) o;
- return obj ? obj->check_type("pa_object") : 0;
+ return obj ? obj->check_type(pa_object_type_id) : TRUE;
}
pa_object *pa_object_ref(pa_object *o);
@@ -60,7 +62,7 @@ static inline int pa_object_refcnt(pa_object *o) {
static inline pa_object* pa_object_cast(void *o) {
pa_object *obj = (pa_object*) o;
- pa_assert(!obj || obj->check_type("pa_object"));
+ pa_assert(!obj || obj->check_type(pa_object_type_id));
return obj;
}
@@ -68,10 +70,10 @@ static inline pa_object* pa_object_cast(void *o) {
#define PA_OBJECT(o) pa_object_cast(o)
-#define PA_DECLARE_CLASS(c) \
- static inline int c##_isinstance(void *o) { \
+#define PA_DECLARE_CLASS_COMMON(c) \
+ static inline pa_bool_t c##_isinstance(void *o) { \
pa_object *obj = (pa_object*) o; \
- return obj ? obj->check_type(#c) : 1; \
+ return obj ? obj->check_type(c##_type_id) : TRUE; \
} \
static inline c* c##_cast(void *o) { \
pa_assert(c##_isinstance(o)); \
@@ -91,12 +93,27 @@ static inline pa_object* pa_object_cast(void *o) {
} \
struct __stupid_useless_struct_to_allow_trailing_semicolon
-#define PA_DEFINE_CHECK_TYPE(c, parent) \
- int c##_check_type(const char *type) { \
- pa_assert(type); \
- if (strcmp(type, #c) == 0) \
- return 1; \
- return parent##_check_type(type); \
+#define PA_DECLARE_PUBLIC_CLASS(c) \
+ extern const char c##_type_id[]; \
+ PA_DECLARE_CLASS_COMMON(c); \
+ pa_bool_t c##_check_type(const char *type_id)
+
+#define PA_DEFINE_PUBLIC_CLASS(c, parent) \
+ const char c##_type_id[] = #c; \
+ pa_bool_t c##_check_type(const char *type_id) { \
+ if (type_id == c##_type_id) \
+ return TRUE; \
+ return parent##_check_type(type_id); \
+ } \
+ struct __stupid_useless_struct_to_allow_trailing_semicolon
+
+#define PA_DEFINE_PRIVATE_CLASS(c, parent) \
+ static const char c##_type_id[] = #c; \
+ PA_DECLARE_CLASS_COMMON(c); \
+ static pa_bool_t c##_check_type(const char *type_id) { \
+ if (type_id == c##_type_id) \
+ return TRUE; \
+ return parent##_check_type(type_id); \
} \
struct __stupid_useless_struct_to_allow_trailing_semicolon
diff --git a/src/pulsecore/pdispatch.c b/src/pulsecore/pdispatch.c
index fc8ce76f..f15466ce 100644
--- a/src/pulsecore/pdispatch.c
+++ b/src/pulsecore/pdispatch.c
@@ -169,7 +169,7 @@ static const char *command_names[PA_COMMAND_MAX] = {
/* Supported since protocol v14 (0.9.12) */
[PA_COMMAND_EXTENSION] = "EXTENSION",
-
+ /* Supported since protocol v15 (0.9.15) */
[PA_COMMAND_GET_CARD_INFO] = "GET_CARD_INFO",
[PA_COMMAND_GET_CARD_INFO_LIST] = "GET_CARD_INFO_LIST",
[PA_COMMAND_SET_CARD_PROFILE] = "SET_CARD_PROFILE",
@@ -180,7 +180,11 @@ static const char *command_names[PA_COMMAND_MAX] = {
/* SERVER->CLIENT */
[PA_COMMAND_PLAYBACK_BUFFER_ATTR_CHANGED] = "PLAYBACK_BUFFER_ATTR_CHANGED",
- [PA_COMMAND_RECORD_BUFFER_ATTR_CHANGED] = "RECORD_BUFFER_ATTR_CHANGED"
+ [PA_COMMAND_RECORD_BUFFER_ATTR_CHANGED] = "RECORD_BUFFER_ATTR_CHANGED",
+
+ /* Supported since protocol v16 (0.9.16) */
+ [PA_COMMAND_SET_SINK_PORT] = "SET_SINK_PORT",
+ [PA_COMMAND_SET_SOURCE_PORT] = "SET_SOURCE_PORT"
};
#endif
@@ -203,10 +207,10 @@ struct pa_pdispatch {
const pa_pdispatch_cb_t *callback_table;
unsigned n_commands;
PA_LLIST_HEAD(struct reply_info, replies);
- pa_pdispatch_drain_callback drain_callback;
+ pa_pdispatch_drain_cb_t drain_callback;
void *drain_userdata;
const pa_creds *creds;
- pa_bool_t use_rtclock:1;
+ pa_bool_t use_rtclock;
};
static void reply_info_free(struct reply_info *r) {
@@ -225,19 +229,16 @@ static void reply_info_free(struct reply_info *r) {
pa_pdispatch* pa_pdispatch_new(pa_mainloop_api *mainloop, pa_bool_t use_rtclock, const pa_pdispatch_cb_t *table, unsigned entries) {
pa_pdispatch *pd;
- pa_assert(mainloop);
+ pa_assert(mainloop);
pa_assert((entries && table) || (!entries && !table));
- pd = pa_xnew(pa_pdispatch, 1);
+ pd = pa_xnew0(pa_pdispatch, 1);
PA_REFCNT_INIT(pd);
pd->mainloop = mainloop;
pd->callback_table = table;
pd->n_commands = entries;
PA_LLIST_HEAD_INIT(struct reply_info, pd->replies);
- pd->drain_callback = NULL;
- pd->drain_userdata = NULL;
- pd->creds = NULL;
pd->use_rtclock = use_rtclock;
return pd;
@@ -317,7 +318,7 @@ int pa_pdispatch_run(pa_pdispatch *pd, pa_packet*packet, const pa_creds *creds,
if (command == PA_COMMAND_ERROR || command == PA_COMMAND_REPLY) {
struct reply_info *r;
- for (r = pd->replies; r; r = r->next)
+ PA_LLIST_FOREACH(r, pd->replies)
if (r->tag == tag)
break;
@@ -325,9 +326,9 @@ int pa_pdispatch_run(pa_pdispatch *pd, pa_packet*packet, const pa_creds *creds,
run_action(pd, r, command, ts);
} else if (pd->callback_table && (command < pd->n_commands) && pd->callback_table[command]) {
- const pa_pdispatch_cb_t *c = pd->callback_table+command;
+ const pa_pdispatch_cb_t *cb = pd->callback_table+command;
- (*c)(pd, command, tag, ts, userdata);
+ (*cb)(pd, command, tag, ts, userdata);
} else {
pa_log("Received unsupported command %u", command);
goto finish;
@@ -375,7 +376,9 @@ void pa_pdispatch_register_reply(pa_pdispatch *pd, uint32_t tag, int timeout, pa
r->free_cb = free_cb;
r->tag = tag;
- pa_assert_se(r->time_event = pd->mainloop->time_new(pd->mainloop, pa_timeval_rtstore(&tv, pa_rtclock_now() + timeout * PA_USEC_PER_SEC, pd->use_rtclock), timeout_callback, r));
+ pa_assert_se(r->time_event = pd->mainloop->time_new(pd->mainloop,
+ pa_timeval_rtstore(&tv, pa_rtclock_now() + timeout * PA_USEC_PER_SEC, pd->use_rtclock),
+ timeout_callback, r));
PA_LLIST_PREPEND(struct reply_info, pd->replies, r);
}
@@ -387,7 +390,7 @@ int pa_pdispatch_is_pending(pa_pdispatch *pd) {
return !!pd->replies;
}
-void pa_pdispatch_set_drain_callback(pa_pdispatch *pd, void (*cb)(pa_pdispatch *pd, void *userdata), void *userdata) {
+void pa_pdispatch_set_drain_callback(pa_pdispatch *pd, pa_pdispatch_drain_cb_t cb, void *userdata) {
pa_assert(pd);
pa_assert(PA_REFCNT_VALUE(pd) >= 1);
pa_assert(!cb || pa_pdispatch_is_pending(pd));
@@ -402,12 +405,9 @@ void pa_pdispatch_unregister_reply(pa_pdispatch *pd, void *userdata) {
pa_assert(pd);
pa_assert(PA_REFCNT_VALUE(pd) >= 1);
- for (r = pd->replies; r; r = n) {
- n = r->next;
-
+ PA_LLIST_FOREACH_SAFE(r, n, pd->replies)
if (r->userdata == userdata)
reply_info_free(r);
- }
}
void pa_pdispatch_unref(pa_pdispatch *pd) {
diff --git a/src/pulsecore/pdispatch.h b/src/pulsecore/pdispatch.h
index dae475af..c5431c2e 100644
--- a/src/pulsecore/pdispatch.h
+++ b/src/pulsecore/pdispatch.h
@@ -35,9 +35,9 @@
typedef struct pa_pdispatch pa_pdispatch;
typedef void (*pa_pdispatch_cb_t)(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
-typedef void (*pa_pdispatch_drain_callback)(pa_pdispatch *pd, void *userdata);
+typedef void (*pa_pdispatch_drain_cb_t)(pa_pdispatch *pd, void *userdata);
-pa_pdispatch* pa_pdispatch_new(pa_mainloop_api *m, pa_bool_t use_rtclock, const pa_pdispatch_cb_t*table, unsigned entries);
+pa_pdispatch* pa_pdispatch_new(pa_mainloop_api *m, pa_bool_t use_rtclock, const pa_pdispatch_cb_t *table, unsigned entries);
void pa_pdispatch_unref(pa_pdispatch *pd);
pa_pdispatch* pa_pdispatch_ref(pa_pdispatch *pd);
@@ -47,7 +47,7 @@ void pa_pdispatch_register_reply(pa_pdispatch *pd, uint32_t tag, int timeout, pa
int pa_pdispatch_is_pending(pa_pdispatch *pd);
-void pa_pdispatch_set_drain_callback(pa_pdispatch *pd, pa_pdispatch_drain_callback callback, void *userdata);
+void pa_pdispatch_set_drain_callback(pa_pdispatch *pd, pa_pdispatch_drain_cb_t callback, void *userdata);
/* Remove all reply slots with the give userdata parameter */
void pa_pdispatch_unregister_reply(pa_pdispatch *pd, void *userdata);
diff --git a/src/pulsecore/pid.c b/src/pulsecore/pid.c
index 00878462..213e7983 100644
--- a/src/pulsecore/pid.c
+++ b/src/pulsecore/pid.c
@@ -81,17 +81,14 @@ static pid_t read_pid(const char *fn, int fd) {
}
static int open_pid_file(const char *fn, int mode) {
- int fd = -1;
+ int fd;
pa_assert(fn);
for (;;) {
struct stat st;
- if ((fd = open(fn, mode
-#ifdef O_NOCTTY
- |O_NOCTTY
-#endif
+ if ((fd = pa_open_cloexec(fn, mode
#ifdef O_NOFOLLOW
|O_NOFOLLOW
#endif
@@ -123,8 +120,6 @@ static int open_pid_file(const char *fn, int mode) {
fd = -1;
goto fail;
}
-
- fd = -1;
}
return fd;
@@ -148,7 +143,7 @@ static int proc_name_ours(pid_t pid, const char *procname) {
pa_snprintf(bn, sizeof(bn), "/proc/%lu/stat", (unsigned long) pid);
- if (!(f = fopen(bn, "r"))) {
+ if (!(f = pa_fopen_cloexec(bn, "r"))) {
pa_log_info("Failed to open %s: %s", bn, pa_cstrerror(errno));
return -1;
} else {
diff --git a/src/pulsecore/play-memblockq.c b/src/pulsecore/play-memblockq.c
index fceb2ca1..f528c496 100644
--- a/src/pulsecore/play-memblockq.c
+++ b/src/pulsecore/play-memblockq.c
@@ -47,9 +47,8 @@ enum {
MEMBLOCKQ_STREAM_MESSAGE_UNLINK,
};
-PA_DECLARE_CLASS(memblockq_stream);
+PA_DEFINE_PRIVATE_CLASS(memblockq_stream, pa_msgobject);
#define MEMBLOCKQ_STREAM(o) (memblockq_stream_cast(o))
-static PA_DEFINE_CHECK_TYPE(memblockq_stream, pa_msgobject);
static void memblockq_stream_unlink(memblockq_stream *u) {
pa_assert(u);
@@ -200,7 +199,7 @@ pa_sink_input* pa_memblockq_sink_input_new(
pa_sink_input_new_data_set_volume(&data, volume);
pa_proplist_update(data.proplist, PA_UPDATE_REPLACE, p);
- pa_sink_input_new(&u->sink_input, sink->core, &data, 0);
+ pa_sink_input_new(&u->sink_input, sink->core, &data);
pa_sink_input_new_data_done(&data);
if (!u->sink_input)
diff --git a/src/pulsecore/protocol-dbus.c b/src/pulsecore/protocol-dbus.c
new file mode 100644
index 00000000..5c1127be
--- /dev/null
+++ b/src/pulsecore/protocol-dbus.c
@@ -0,0 +1,1134 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2009 Tanu Kaskinen
+
+ 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 <dbus/dbus.h>
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/dbus-util.h>
+#include <pulsecore/hashmap.h>
+#include <pulsecore/idxset.h>
+#include <pulsecore/shared.h>
+#include <pulsecore/strbuf.h>
+
+#include "protocol-dbus.h"
+
+struct pa_dbus_protocol {
+ PA_REFCNT_DECLARE;
+
+ pa_core *core;
+ pa_hashmap *objects; /* Object path -> struct object_entry */
+ pa_hashmap *connections; /* DBusConnection -> struct connection_entry */
+ pa_idxset *extensions; /* Strings */
+
+ pa_hook hooks[PA_DBUS_PROTOCOL_HOOK_MAX];
+};
+
+struct object_entry {
+ char *path;
+ pa_hashmap *interfaces; /* Interface name -> struct interface_entry */
+ char *introspection;
+};
+
+struct connection_entry {
+ DBusConnection *connection;
+ pa_client *client;
+
+ pa_bool_t listening_for_all_signals;
+
+ /* Contains object paths. If this is empty, then signals from all objects
+ * are accepted. Only used when listening_for_all_signals == TRUE. */
+ pa_idxset *all_signals_objects;
+
+ /* Signal name -> idxset. The idxsets contain object paths. If an idxset is
+ * empty, then that signal is accepted from all objects. Only used when
+ * listening_for_all_signals == FALSE. */
+ pa_hashmap *listening_signals;
+};
+
+struct interface_entry {
+ char *name;
+ pa_hashmap *method_handlers;
+ pa_hashmap *method_signatures; /* Derived from method_handlers. Contains only "in" arguments. */
+ pa_hashmap *property_handlers;
+ pa_dbus_receive_cb_t get_all_properties_cb;
+ pa_dbus_signal_info *signals;
+ unsigned n_signals;
+ void *userdata;
+};
+
+char *pa_get_dbus_address_from_server_type(pa_server_type_t server_type) {
+ char *address = NULL;
+ char *runtime_path = NULL;
+ char *escaped_path = NULL;
+
+ switch (server_type) {
+ case PA_SERVER_TYPE_USER:
+ pa_assert_se((runtime_path = pa_runtime_path(PA_DBUS_SOCKET_NAME)));
+ pa_assert_se((escaped_path = dbus_address_escape_value(runtime_path)));
+ address = pa_sprintf_malloc("unix:path=%s", escaped_path);
+ break;
+
+ case PA_SERVER_TYPE_SYSTEM:
+ pa_assert_se((escaped_path = dbus_address_escape_value(PA_DBUS_SYSTEM_SOCKET_PATH)));
+ address = pa_sprintf_malloc("unix:path=%s", escaped_path);
+ break;
+
+ case PA_SERVER_TYPE_NONE:
+ address = pa_xnew0(char, 1);
+ break;
+
+ default:
+ pa_assert_not_reached();
+ }
+
+ pa_xfree(runtime_path);
+ pa_xfree(escaped_path);
+
+ return address;
+}
+
+static pa_dbus_protocol *dbus_protocol_new(pa_core *c) {
+ pa_dbus_protocol *p;
+ unsigned i;
+
+ pa_assert(c);
+
+ p = pa_xnew(pa_dbus_protocol, 1);
+ PA_REFCNT_INIT(p);
+ p->core = pa_core_ref(c);
+ p->objects = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+ p->connections = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+ p->extensions = pa_idxset_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+
+ for (i = 0; i < PA_DBUS_PROTOCOL_HOOK_MAX; ++i)
+ pa_hook_init(&p->hooks[i], p);
+
+ pa_assert_se(pa_shared_set(c, "dbus-protocol", p) >= 0);
+
+ return p;
+}
+
+pa_dbus_protocol* pa_dbus_protocol_get(pa_core *c) {
+ pa_dbus_protocol *p;
+
+ if ((p = pa_shared_get(c, "dbus-protocol")))
+ return pa_dbus_protocol_ref(p);
+
+ return dbus_protocol_new(c);
+}
+
+pa_dbus_protocol* pa_dbus_protocol_ref(pa_dbus_protocol *p) {
+ pa_assert(p);
+ pa_assert(PA_REFCNT_VALUE(p) >= 1);
+
+ PA_REFCNT_INC(p);
+
+ return p;
+}
+
+void pa_dbus_protocol_unref(pa_dbus_protocol *p) {
+ unsigned i;
+
+ pa_assert(p);
+ pa_assert(PA_REFCNT_VALUE(p) >= 1);
+
+ if (PA_REFCNT_DEC(p) > 0)
+ return;
+
+ pa_assert(pa_hashmap_isempty(p->objects));
+ pa_assert(pa_hashmap_isempty(p->connections));
+ pa_assert(pa_idxset_isempty(p->extensions));
+
+ pa_hashmap_free(p->objects, NULL, NULL);
+ pa_hashmap_free(p->connections, NULL, NULL);
+ pa_idxset_free(p->extensions, NULL, NULL);
+
+ for (i = 0; i < PA_DBUS_PROTOCOL_HOOK_MAX; ++i)
+ pa_hook_done(&p->hooks[i]);
+
+ pa_assert_se(pa_shared_remove(p->core, "dbus-protocol") >= 0);
+
+ pa_core_unref(p->core);
+
+ pa_xfree(p);
+}
+
+static void update_introspection(struct object_entry *oe) {
+ pa_strbuf *buf;
+ void *interfaces_state = NULL;
+ struct interface_entry *iface_entry = NULL;
+
+ pa_assert(oe);
+
+ buf = pa_strbuf_new();
+ pa_strbuf_puts(buf, DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE);
+ pa_strbuf_puts(buf, "<node>\n");
+
+ PA_HASHMAP_FOREACH(iface_entry, oe->interfaces, interfaces_state) {
+ pa_dbus_method_handler *method_handler;
+ pa_dbus_property_handler *property_handler;
+ void *handlers_state = NULL;
+ unsigned i;
+ unsigned j;
+
+ pa_strbuf_printf(buf, " <interface name=\"%s\">\n", iface_entry->name);
+
+ PA_HASHMAP_FOREACH(method_handler, iface_entry->method_handlers, handlers_state) {
+ pa_strbuf_printf(buf, " <method name=\"%s\">\n", method_handler->method_name);
+
+ for (i = 0; i < method_handler->n_arguments; ++i)
+ pa_strbuf_printf(buf, " <arg name=\"%s\" type=\"%s\" direction=\"%s\"/>\n",
+ method_handler->arguments[i].name,
+ method_handler->arguments[i].type,
+ method_handler->arguments[i].direction);
+
+ pa_strbuf_puts(buf, " </method>\n");
+ }
+
+ handlers_state = NULL;
+
+ PA_HASHMAP_FOREACH(property_handler, iface_entry->property_handlers, handlers_state)
+ pa_strbuf_printf(buf, " <property name=\"%s\" type=\"%s\" access=\"%s\"/>\n",
+ property_handler->property_name,
+ property_handler->type,
+ property_handler->get_cb ? (property_handler->set_cb ? "readwrite" : "read") : "write");
+
+ for (i = 0; i < iface_entry->n_signals; ++i) {
+ pa_strbuf_printf(buf, " <signal name=\"%s\">\n", iface_entry->signals[i].name);
+
+ for (j = 0; j < iface_entry->signals[i].n_arguments; ++j)
+ pa_strbuf_printf(buf, " <arg name=\"%s\" type=\"%s\"/>\n", iface_entry->signals[i].arguments[j].name,
+ iface_entry->signals[i].arguments[j].type);
+
+ pa_strbuf_puts(buf, " </signal>\n");
+ }
+
+ pa_strbuf_puts(buf, " </interface>\n");
+ }
+
+ pa_strbuf_puts(buf, " <interface name=\"" DBUS_INTERFACE_INTROSPECTABLE "\">\n"
+ " <method name=\"Introspect\">\n"
+ " <arg name=\"data\" type=\"s\" direction=\"out\"/>\n"
+ " </method>\n"
+ " </interface>\n"
+ " <interface name=\"" DBUS_INTERFACE_PROPERTIES "\">\n"
+ " <method name=\"Get\">\n"
+ " <arg name=\"interface_name\" type=\"s\" direction=\"in\"/>\n"
+ " <arg name=\"property_name\" type=\"s\" direction=\"in\"/>\n"
+ " <arg name=\"value\" type=\"v\" direction=\"out\"/>\n"
+ " </method>\n"
+ " <method name=\"Set\">\n"
+ " <arg name=\"interface_name\" type=\"s\" direction=\"in\"/>\n"
+ " <arg name=\"property_name\" type=\"s\" direction=\"in\"/>\n"
+ " <arg name=\"value\" type=\"v\" direction=\"in\"/>\n"
+ " </method>\n"
+ " <method name=\"GetAll\">\n"
+ " <arg name=\"interface_name\" type=\"s\" direction=\"in\"/>\n"
+ " <arg name=\"props\" type=\"a{sv}\" direction=\"out\"/>\n"
+ " </method>\n"
+ " </interface>\n");
+
+ pa_strbuf_puts(buf, "</node>\n");
+
+ pa_xfree(oe->introspection);
+ oe->introspection = pa_strbuf_tostring_free(buf);
+}
+
+/* Return value of find_handler() and its subfunctions. */
+enum find_result_t {
+ /* The received message is a valid .Get call. */
+ FOUND_GET_PROPERTY,
+
+ /* The received message is a valid .Set call. */
+ FOUND_SET_PROPERTY,
+
+ /* The received message is a valid .GetAll call. */
+ FOUND_GET_ALL,
+
+ /* The received message is a valid method call. */
+ FOUND_METHOD,
+
+ /* The interface of the received message hasn't been registered for the
+ * destination object. */
+ NO_SUCH_INTERFACE,
+
+ /* No property handler was found for the received .Get or .Set call. */
+ NO_SUCH_PROPERTY,
+
+ /* The interface argument of a property call didn't match any registered
+ * interface. */
+ NO_SUCH_PROPERTY_INTERFACE,
+
+ /* The received message called .Get or .Set for a property whose access
+ * mode doesn't match the call. */
+ PROPERTY_ACCESS_DENIED,
+
+ /* The new value signature of a .Set call didn't match the expexted
+ * signature. */
+ INVALID_PROPERTY_SIG,
+
+ /* No method handler was found for the received message. */
+ NO_SUCH_METHOD,
+
+ /* The signature of the received message didn't match the expected
+ * signature. Despite the name, this can also be returned for a property
+ * call if its message signature is invalid. */
+ INVALID_METHOD_SIG
+};
+
+/* Data for resolving the correct reaction to a received message. */
+struct call_info {
+ DBusMessage *message; /* The received message. */
+ struct object_entry *obj_entry;
+ const char *interface; /* Destination interface name (extracted from the message). */
+ struct interface_entry *iface_entry;
+
+ const char *property; /* Property name (extracted from the message). */
+ const char *property_interface; /* The interface argument of a property call is stored here. */
+ pa_dbus_property_handler *property_handler;
+ const char *expected_property_sig; /* Property signature from the introspection data. */
+ const char *property_sig; /* The signature of the new value in the received .Set message. */
+ DBusMessageIter variant_iter; /* Iterator pointing to the beginning of the new value variant of a .Set call. */
+
+ const char *method; /* Method name (extracted from the message). */
+ pa_dbus_method_handler *method_handler;
+ const char *expected_method_sig; /* Method signature from the introspection data. */
+ const char *method_sig; /* The signature of the received message. */
+};
+
+/* Called when call_info->property has been set and the property interface has
+ * not been given. In case of a Set call, call_info->property_sig is also set,
+ * which is checked against the expected value in this function. */
+static enum find_result_t find_handler_by_property(struct call_info *call_info) {
+ void *state = NULL;
+
+ pa_assert(call_info);
+
+ PA_HASHMAP_FOREACH(call_info->iface_entry, call_info->obj_entry->interfaces, state) {
+ if ((call_info->property_handler = pa_hashmap_get(call_info->iface_entry->property_handlers, call_info->property))) {
+ if (pa_streq(call_info->method, "Get"))
+ return call_info->property_handler->get_cb ? FOUND_GET_PROPERTY : PROPERTY_ACCESS_DENIED;
+
+ else if (pa_streq(call_info->method, "Set")) {
+ call_info->expected_property_sig = call_info->property_handler->type;
+
+ if (pa_streq(call_info->property_sig, call_info->expected_property_sig))
+ return call_info->property_handler->set_cb ? FOUND_SET_PROPERTY : PROPERTY_ACCESS_DENIED;
+ else
+ return INVALID_PROPERTY_SIG;
+
+ } else
+ pa_assert_not_reached();
+ }
+ }
+
+ return NO_SUCH_PROPERTY;
+}
+
+static enum find_result_t find_handler_by_method(struct call_info *call_info) {
+ void *state = NULL;
+
+ pa_assert(call_info);
+
+ PA_HASHMAP_FOREACH(call_info->iface_entry, call_info->obj_entry->interfaces, state) {
+ if ((call_info->method_handler = pa_hashmap_get(call_info->iface_entry->method_handlers, call_info->method))) {
+ call_info->expected_method_sig = pa_hashmap_get(call_info->iface_entry->method_signatures, call_info->method);
+
+ if (pa_streq(call_info->method_sig, call_info->expected_method_sig))
+ return FOUND_METHOD;
+ else
+ return INVALID_METHOD_SIG;
+ }
+ }
+
+ return NO_SUCH_METHOD;
+}
+
+static enum find_result_t find_handler_from_properties_call(struct call_info *call_info) {
+ pa_assert(call_info);
+
+ if (pa_streq(call_info->method, "GetAll")) {
+ call_info->expected_method_sig = "s";
+ if (!pa_streq(call_info->method_sig, call_info->expected_method_sig))
+ return INVALID_METHOD_SIG;
+
+ pa_assert_se(dbus_message_get_args(call_info->message, NULL,
+ DBUS_TYPE_STRING, &call_info->property_interface,
+ DBUS_TYPE_INVALID));
+
+ if (*call_info->property_interface) {
+ if ((call_info->iface_entry = pa_hashmap_get(call_info->obj_entry->interfaces, call_info->property_interface)))
+ return FOUND_GET_ALL;
+ else
+ return NO_SUCH_PROPERTY_INTERFACE;
+
+ } else {
+ pa_assert_se(call_info->iface_entry = pa_hashmap_first(call_info->obj_entry->interfaces));
+ return FOUND_GET_ALL;
+ }
+
+ } else if (pa_streq(call_info->method, "Get")) {
+ call_info->expected_method_sig = "ss";
+ if (!pa_streq(call_info->method_sig, call_info->expected_method_sig))
+ return INVALID_METHOD_SIG;
+
+ pa_assert_se(dbus_message_get_args(call_info->message, NULL,
+ DBUS_TYPE_STRING, &call_info->property_interface,
+ DBUS_TYPE_STRING, &call_info->property,
+ DBUS_TYPE_INVALID));
+
+ if (*call_info->property_interface) {
+ if (!(call_info->iface_entry = pa_hashmap_get(call_info->obj_entry->interfaces, call_info->property_interface)))
+ return NO_SUCH_PROPERTY_INTERFACE;
+ else if ((call_info->property_handler =
+ pa_hashmap_get(call_info->iface_entry->property_handlers, call_info->property)))
+ return FOUND_GET_PROPERTY;
+ else
+ return NO_SUCH_PROPERTY;
+
+ } else
+ return find_handler_by_property(call_info);
+
+ } else if (pa_streq(call_info->method, "Set")) {
+ DBusMessageIter msg_iter;
+
+ call_info->expected_method_sig = "ssv";
+ if (!pa_streq(call_info->method_sig, call_info->expected_method_sig))
+ return INVALID_METHOD_SIG;
+
+ pa_assert_se(dbus_message_iter_init(call_info->message, &msg_iter));
+
+ dbus_message_iter_get_basic(&msg_iter, &call_info->property_interface);
+ pa_assert_se(dbus_message_iter_next(&msg_iter));
+ dbus_message_iter_get_basic(&msg_iter, &call_info->property);
+ pa_assert_se(dbus_message_iter_next(&msg_iter));
+
+ dbus_message_iter_recurse(&msg_iter, &call_info->variant_iter);
+
+ call_info->property_sig = dbus_message_iter_get_signature(&call_info->variant_iter);
+
+ if (*call_info->property_interface) {
+ if (!(call_info->iface_entry = pa_hashmap_get(call_info->obj_entry->interfaces, call_info->property_interface)))
+ return NO_SUCH_PROPERTY_INTERFACE;
+
+ else if ((call_info->property_handler =
+ pa_hashmap_get(call_info->iface_entry->property_handlers, call_info->property))) {
+ call_info->expected_property_sig = call_info->property_handler->type;
+
+ if (pa_streq(call_info->property_sig, call_info->expected_property_sig))
+ return FOUND_SET_PROPERTY;
+ else
+ return INVALID_PROPERTY_SIG;
+
+ } else
+ return NO_SUCH_PROPERTY;
+
+ } else
+ return find_handler_by_property(call_info);
+
+ } else
+ pa_assert_not_reached();
+}
+
+static enum find_result_t find_handler(struct call_info *call_info) {
+ pa_assert(call_info);
+
+ if (call_info->interface) {
+ if (pa_streq(call_info->interface, DBUS_INTERFACE_PROPERTIES))
+ return find_handler_from_properties_call(call_info);
+
+ else if (!(call_info->iface_entry = pa_hashmap_get(call_info->obj_entry->interfaces, call_info->interface)))
+ return NO_SUCH_INTERFACE;
+
+ else if ((call_info->method_handler = pa_hashmap_get(call_info->iface_entry->method_handlers, call_info->method)))
+ return FOUND_METHOD;
+
+ else
+ return NO_SUCH_METHOD;
+
+ } else { /* The method call doesn't contain an interface. */
+ if (pa_streq(call_info->method, "Get") || pa_streq(call_info->method, "Set") || pa_streq(call_info->method, "GetAll")) {
+ if (find_handler_by_method(call_info) == FOUND_METHOD)
+ /* The object has a method named Get, Set or GetAll in some other interface than .Properties. */
+ return FOUND_METHOD;
+ else
+ /* Assume this is a .Properties call. */
+ return find_handler_from_properties_call(call_info);
+
+ } else /* This is not a .Properties call. */
+ return find_handler_by_method(call_info);
+ }
+}
+
+static DBusHandlerResult handle_message_cb(DBusConnection *connection, DBusMessage *message, void *user_data) {
+ pa_dbus_protocol *p = user_data;
+ struct call_info call_info;
+
+ pa_assert(connection);
+ pa_assert(message);
+ pa_assert(p);
+ pa_assert(p->objects);
+
+ if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_METHOD_CALL)
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+ pa_log_debug("Received message: destination = %s, interface = %s, member = %s",
+ dbus_message_get_path(message),
+ dbus_message_get_interface(message),
+ dbus_message_get_member(message));
+
+ call_info.message = message;
+ pa_assert_se(call_info.obj_entry = pa_hashmap_get(p->objects, dbus_message_get_path(message)));
+ call_info.interface = dbus_message_get_interface(message);
+ pa_assert_se(call_info.method = dbus_message_get_member(message));
+ pa_assert_se(call_info.method_sig = dbus_message_get_signature(message));
+
+ if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect") ||
+ (!dbus_message_get_interface(message) && dbus_message_has_member(message, "Introspect"))) {
+ pa_dbus_send_basic_value_reply(connection, message, DBUS_TYPE_STRING, &call_info.obj_entry->introspection);
+ goto finish;
+ }
+
+ switch (find_handler(&call_info)) {
+ case FOUND_GET_PROPERTY:
+ call_info.property_handler->get_cb(connection, message, call_info.iface_entry->userdata);
+ break;
+
+ case FOUND_SET_PROPERTY:
+ call_info.property_handler->set_cb(connection, message, &call_info.variant_iter, call_info.iface_entry->userdata);
+ break;
+
+ case FOUND_METHOD:
+ call_info.method_handler->receive_cb(connection, message, call_info.iface_entry->userdata);
+ break;
+
+ case FOUND_GET_ALL:
+ if (call_info.iface_entry->get_all_properties_cb)
+ call_info.iface_entry->get_all_properties_cb(connection, message, call_info.iface_entry->userdata);
+ else {
+ DBusMessage *dummy_reply = NULL;
+ DBusMessageIter msg_iter;
+ DBusMessageIter dict_iter;
+
+ pa_assert_se(dummy_reply = dbus_message_new_method_return(message));
+ dbus_message_iter_init_append(dummy_reply, &msg_iter);
+ pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter));
+ pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter));
+ pa_assert_se(dbus_connection_send(connection, dummy_reply, NULL));
+ dbus_message_unref(dummy_reply);
+ }
+ break;
+
+ case PROPERTY_ACCESS_DENIED:
+ pa_dbus_send_error(connection, message, DBUS_ERROR_ACCESS_DENIED,
+ "%s access denied for property %s", call_info.method, call_info.property);
+ break;
+
+ case NO_SUCH_METHOD:
+ pa_dbus_send_error(connection, message, DBUS_ERROR_UNKNOWN_METHOD, "No such method: %s", call_info.method);
+ break;
+
+ case NO_SUCH_PROPERTY:
+ pa_dbus_send_error(connection, message, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "No such property: %s", call_info.property);
+ break;
+
+ case INVALID_METHOD_SIG:
+ pa_dbus_send_error(connection, message, DBUS_ERROR_INVALID_ARGS,
+ "Invalid signature for method %s: '%s'. Expected '%s'.",
+ call_info.method, call_info.method_sig, call_info.expected_method_sig);
+ break;
+
+ case INVALID_PROPERTY_SIG:
+ pa_dbus_send_error(connection, message, DBUS_ERROR_INVALID_ARGS,
+ "Invalid signature for property %s: '%s'. Expected '%s'.",
+ call_info.property, call_info.property_sig, call_info.expected_property_sig);
+
+ default:
+ pa_assert_not_reached();
+ }
+
+finish:
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static DBusObjectPathVTable vtable = {
+ .unregister_function = NULL,
+ .message_function = handle_message_cb,
+ .dbus_internal_pad1 = NULL,
+ .dbus_internal_pad2 = NULL,
+ .dbus_internal_pad3 = NULL,
+ .dbus_internal_pad4 = NULL
+};
+
+static void register_object(pa_dbus_protocol *p, struct object_entry *obj_entry) {
+ struct connection_entry *conn_entry;
+ void *state = NULL;
+
+ pa_assert(p);
+ pa_assert(obj_entry);
+
+ PA_HASHMAP_FOREACH(conn_entry, p->connections, state)
+ pa_assert_se(dbus_connection_register_object_path(conn_entry->connection, obj_entry->path, &vtable, p));
+}
+
+static pa_dbus_arg_info *copy_args(const pa_dbus_arg_info *src, unsigned n) {
+ pa_dbus_arg_info *dst;
+ unsigned i;
+
+ if (n == 0)
+ return NULL;
+
+ pa_assert(src);
+
+ dst = pa_xnew0(pa_dbus_arg_info, n);
+
+ for (i = 0; i < n; ++i) {
+ dst[i].name = pa_xstrdup(src[i].name);
+ dst[i].type = pa_xstrdup(src[i].type);
+ dst[i].direction = pa_xstrdup(src[i].direction);
+ }
+
+ return dst;
+}
+
+static pa_hashmap *create_method_handlers(const pa_dbus_interface_info *info) {
+ pa_hashmap *handlers;
+ unsigned i;
+
+ pa_assert(info);
+ pa_assert(info->method_handlers || info->n_method_handlers == 0);
+
+ handlers = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+
+ for (i = 0; i < info->n_method_handlers; ++i) {
+ pa_dbus_method_handler *h = pa_xnew(pa_dbus_method_handler, 1);
+ h->method_name = pa_xstrdup(info->method_handlers[i].method_name);
+ h->arguments = copy_args(info->method_handlers[i].arguments, info->method_handlers[i].n_arguments);
+ h->n_arguments = info->method_handlers[i].n_arguments;
+ h->receive_cb = info->method_handlers[i].receive_cb;
+
+ pa_hashmap_put(handlers, h->method_name, h);
+ }
+
+ return handlers;
+}
+
+static pa_hashmap *extract_method_signatures(pa_hashmap *method_handlers) {
+ pa_hashmap *signatures = NULL;
+ pa_dbus_method_handler *handler = NULL;
+ void *state = NULL;
+ pa_strbuf *sig_buf = NULL;
+ unsigned i = 0;
+
+ pa_assert(method_handlers);
+
+ signatures = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+
+ PA_HASHMAP_FOREACH(handler, method_handlers, state) {
+ sig_buf = pa_strbuf_new();
+
+ for (i = 0; i < handler->n_arguments; ++i) {
+ if (pa_streq(handler->arguments[i].direction, "in"))
+ pa_strbuf_puts(sig_buf, handler->arguments[i].type);
+ }
+
+ pa_hashmap_put(signatures, handler->method_name, pa_strbuf_tostring_free(sig_buf));
+ }
+
+ return signatures;
+}
+
+static pa_hashmap *create_property_handlers(const pa_dbus_interface_info *info) {
+ pa_hashmap *handlers;
+ unsigned i = 0;
+
+ pa_assert(info);
+ pa_assert(info->property_handlers || info->n_property_handlers == 0);
+
+ handlers = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+
+ for (i = 0; i < info->n_property_handlers; ++i) {
+ pa_dbus_property_handler *h = pa_xnew(pa_dbus_property_handler, 1);
+ h->property_name = pa_xstrdup(info->property_handlers[i].property_name);
+ h->type = pa_xstrdup(info->property_handlers[i].type);
+ h->get_cb = info->property_handlers[i].get_cb;
+ h->set_cb = info->property_handlers[i].set_cb;
+
+ pa_hashmap_put(handlers, h->property_name, h);
+ }
+
+ return handlers;
+}
+
+static pa_dbus_signal_info *copy_signals(const pa_dbus_interface_info *info) {
+ pa_dbus_signal_info *dst;
+ unsigned i;
+
+ pa_assert(info);
+
+ if (info->n_signals == 0)
+ return NULL;
+
+ pa_assert(info->signals);
+
+ dst = pa_xnew(pa_dbus_signal_info, info->n_signals);
+
+ for (i = 0; i < info->n_signals; ++i) {
+ dst[i].name = pa_xstrdup(info->signals[i].name);
+ dst[i].arguments = copy_args(info->signals[i].arguments, info->signals[i].n_arguments);
+ dst[i].n_arguments = info->signals[i].n_arguments;
+ }
+
+ return dst;
+}
+
+int pa_dbus_protocol_add_interface(pa_dbus_protocol *p,
+ const char *path,
+ const pa_dbus_interface_info *info,
+ void *userdata) {
+ struct object_entry *obj_entry;
+ struct interface_entry *iface_entry;
+ pa_bool_t obj_entry_created = FALSE;
+
+ pa_assert(p);
+ pa_assert(path);
+ pa_assert(info);
+ pa_assert(info->name);
+ pa_assert(info->method_handlers || info->n_method_handlers == 0);
+ pa_assert(info->property_handlers || info->n_property_handlers == 0);
+ pa_assert(info->get_all_properties_cb || info->n_property_handlers == 0);
+ pa_assert(info->signals || info->n_signals == 0);
+
+ if (!(obj_entry = pa_hashmap_get(p->objects, path))) {
+ obj_entry = pa_xnew(struct object_entry, 1);
+ obj_entry->path = pa_xstrdup(path);
+ obj_entry->interfaces = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+ obj_entry->introspection = NULL;
+
+ pa_hashmap_put(p->objects, path, obj_entry);
+ obj_entry_created = TRUE;
+ }
+
+ if (pa_hashmap_get(obj_entry->interfaces, info->name) != NULL)
+ goto fail; /* The interface was already registered. */
+
+ iface_entry = pa_xnew(struct interface_entry, 1);
+ iface_entry->name = pa_xstrdup(info->name);
+ iface_entry->method_handlers = create_method_handlers(info);
+ iface_entry->method_signatures = extract_method_signatures(iface_entry->method_handlers);
+ iface_entry->property_handlers = create_property_handlers(info);
+ iface_entry->get_all_properties_cb = info->get_all_properties_cb;
+ iface_entry->signals = copy_signals(info);
+ iface_entry->n_signals = info->n_signals;
+ iface_entry->userdata = userdata;
+ pa_hashmap_put(obj_entry->interfaces, iface_entry->name, iface_entry);
+
+ update_introspection(obj_entry);
+
+ if (obj_entry_created)
+ register_object(p, obj_entry);
+
+ pa_log_debug("Interface %s added for object %s", iface_entry->name, obj_entry->path);
+
+ return 0;
+
+fail:
+ if (obj_entry_created) {
+ pa_hashmap_remove(p->objects, path);
+ pa_xfree(obj_entry);
+ }
+
+ return -1;
+}
+
+static void unregister_object(pa_dbus_protocol *p, struct object_entry *obj_entry) {
+ struct connection_entry *conn_entry;
+ void *state = NULL;
+
+ pa_assert(p);
+ pa_assert(obj_entry);
+
+ PA_HASHMAP_FOREACH(conn_entry, p->connections, state)
+ pa_assert_se(dbus_connection_unregister_object_path(conn_entry->connection, obj_entry->path));
+}
+
+static void method_handler_free_cb(void *p, void *userdata) {
+ pa_dbus_method_handler *h = p;
+ unsigned i;
+
+ pa_assert(h);
+
+ pa_xfree((char *) h->method_name);
+
+ for (i = 0; i < h->n_arguments; ++i) {
+ pa_xfree((char *) h->arguments[i].name);
+ pa_xfree((char *) h->arguments[i].type);
+ pa_xfree((char *) h->arguments[i].direction);
+ }
+
+ pa_xfree((pa_dbus_arg_info *) h->arguments);
+}
+
+static void method_signature_free_cb(void *p, void *userdata) {
+ pa_assert(p);
+
+ pa_xfree(p);
+}
+
+static void property_handler_free_cb(void *p, void *userdata) {
+ pa_dbus_property_handler *h = p;
+
+ pa_assert(h);
+
+ pa_xfree((char *) h->property_name);
+ pa_xfree((char *) h->type);
+
+ pa_xfree(h);
+}
+
+int pa_dbus_protocol_remove_interface(pa_dbus_protocol *p, const char* path, const char* interface) {
+ struct object_entry *obj_entry;
+ struct interface_entry *iface_entry;
+ unsigned i;
+
+ pa_assert(p);
+ pa_assert(path);
+ pa_assert(interface);
+
+ if (!(obj_entry = pa_hashmap_get(p->objects, path)))
+ return -1;
+
+ if (!(iface_entry = pa_hashmap_remove(obj_entry->interfaces, interface)))
+ return -1;
+
+ update_introspection(obj_entry);
+
+ pa_log_debug("Interface %s removed from object %s", iface_entry->name, obj_entry->path);
+
+ pa_xfree(iface_entry->name);
+ pa_hashmap_free(iface_entry->method_handlers, method_handler_free_cb, NULL);
+ pa_hashmap_free(iface_entry->method_signatures, method_signature_free_cb, NULL);
+ pa_hashmap_free(iface_entry->property_handlers, property_handler_free_cb, NULL);
+
+ for (i = 0; i < iface_entry->n_signals; ++i) {
+ unsigned j;
+
+ pa_xfree((char *) iface_entry->signals[i].name);
+
+ for (j = 0; j < iface_entry->signals[i].n_arguments; ++j) {
+ pa_xfree((char *) iface_entry->signals[i].arguments[j].name);
+ pa_xfree((char *) iface_entry->signals[i].arguments[j].type);
+ pa_assert(iface_entry->signals[i].arguments[j].direction == NULL);
+ }
+
+ pa_xfree((pa_dbus_arg_info *) iface_entry->signals[i].arguments);
+ }
+
+ pa_xfree(iface_entry->signals);
+ pa_xfree(iface_entry);
+
+ if (pa_hashmap_isempty(obj_entry->interfaces)) {
+ unregister_object(p, obj_entry);
+
+ pa_hashmap_remove(p->objects, path);
+ pa_xfree(obj_entry->path);
+ pa_hashmap_free(obj_entry->interfaces, NULL, NULL);
+ pa_xfree(obj_entry->introspection);
+ pa_xfree(obj_entry);
+ }
+
+ return 0;
+}
+
+static void register_all_objects(pa_dbus_protocol *p, DBusConnection *conn) {
+ struct object_entry *obj_entry;
+ void *state = NULL;
+
+ pa_assert(p);
+ pa_assert(conn);
+
+ PA_HASHMAP_FOREACH(obj_entry, p->objects, state)
+ pa_assert_se(dbus_connection_register_object_path(conn, obj_entry->path, &vtable, p));
+}
+
+int pa_dbus_protocol_register_connection(pa_dbus_protocol *p, DBusConnection *conn, pa_client *client) {
+ struct connection_entry *conn_entry;
+
+ pa_assert(p);
+ pa_assert(conn);
+ pa_assert(client);
+
+ if (pa_hashmap_get(p->connections, conn))
+ return -1; /* The connection was already registered. */
+
+ register_all_objects(p, conn);
+
+ conn_entry = pa_xnew(struct connection_entry, 1);
+ conn_entry->connection = dbus_connection_ref(conn);
+ conn_entry->client = client;
+ conn_entry->listening_for_all_signals = FALSE;
+ conn_entry->all_signals_objects = pa_idxset_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+ conn_entry->listening_signals = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+
+ pa_hashmap_put(p->connections, conn, conn_entry);
+
+ return 0;
+}
+
+static void unregister_all_objects(pa_dbus_protocol *p, DBusConnection *conn) {
+ struct object_entry *obj_entry;
+ void *state = NULL;
+
+ pa_assert(p);
+ pa_assert(conn);
+
+ PA_HASHMAP_FOREACH(obj_entry, p->objects, state)
+ pa_assert_se(dbus_connection_unregister_object_path(conn, obj_entry->path));
+}
+
+static void free_listened_object_name_cb(void *p, void *userdata) {
+ pa_assert(p);
+
+ pa_xfree(p);
+}
+
+static void free_listening_signals_idxset_cb(void *p, void *userdata) {
+ pa_idxset *set = p;
+
+ pa_assert(set);
+
+ pa_idxset_free(set, free_listened_object_name_cb, NULL);
+}
+
+int pa_dbus_protocol_unregister_connection(pa_dbus_protocol *p, DBusConnection *conn) {
+ struct connection_entry *conn_entry;
+
+ pa_assert(p);
+ pa_assert(conn);
+
+ if (!(conn_entry = pa_hashmap_remove(p->connections, conn)))
+ return -1;
+
+ unregister_all_objects(p, conn);
+
+ dbus_connection_unref(conn_entry->connection);
+ pa_idxset_free(conn_entry->all_signals_objects, free_listened_object_name_cb, NULL);
+ pa_hashmap_free(conn_entry->listening_signals, free_listening_signals_idxset_cb, NULL);
+ pa_xfree(conn_entry);
+
+ return 0;
+}
+
+pa_client *pa_dbus_protocol_get_client(pa_dbus_protocol *p, DBusConnection *conn) {
+ struct connection_entry *conn_entry;
+
+ pa_assert(p);
+ pa_assert(conn);
+
+ if (!(conn_entry = pa_hashmap_get(p->connections, conn)))
+ return NULL;
+
+ return conn_entry->client;
+}
+
+void pa_dbus_protocol_add_signal_listener(
+ pa_dbus_protocol *p,
+ DBusConnection *conn,
+ const char *signal,
+ char **objects,
+ unsigned n_objects) {
+ struct connection_entry *conn_entry;
+ pa_idxset *object_set;
+ char *object_path;
+ unsigned i;
+
+ pa_assert(p);
+ pa_assert(conn);
+ pa_assert(objects || n_objects == 0);
+
+ pa_assert_se((conn_entry = pa_hashmap_get(p->connections, conn)));
+
+ /* all_signals_objects will either be emptied or replaced with new objects,
+ * so we empty it here unconditionally. If listening_for_all_signals is
+ * currently FALSE, the idxset is empty already. */
+ while ((object_path = pa_idxset_steal_first(conn_entry->all_signals_objects, NULL)))
+ pa_xfree(object_path);
+
+ if (signal) {
+ conn_entry->listening_for_all_signals = FALSE;
+
+ /* Replace the old object list with a new one. */
+ if ((object_set = pa_hashmap_remove(conn_entry->listening_signals, signal)))
+ pa_idxset_free(object_set, free_listened_object_name_cb, NULL);
+ object_set = pa_idxset_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+
+ for (i = 0; i < n_objects; ++i)
+ pa_idxset_put(object_set, pa_xstrdup(objects[i]), NULL);
+
+ pa_hashmap_put(conn_entry->listening_signals, signal, object_set);
+
+ } else {
+ conn_entry->listening_for_all_signals = TRUE;
+
+ /* We're not interested in individual signals anymore, so let's empty
+ * listening_signals. */
+ while ((object_set = pa_hashmap_steal_first(conn_entry->listening_signals)))
+ pa_idxset_free(object_set, free_listened_object_name_cb, NULL);
+
+ for (i = 0; i < n_objects; ++i)
+ pa_idxset_put(conn_entry->all_signals_objects, pa_xstrdup(objects[i]), NULL);
+ }
+}
+
+void pa_dbus_protocol_remove_signal_listener(pa_dbus_protocol *p, DBusConnection *conn, const char *signal) {
+ struct connection_entry *conn_entry;
+ pa_idxset *object_set;
+
+ pa_assert(p);
+ pa_assert(conn);
+
+ pa_assert_se((conn_entry = pa_hashmap_get(p->connections, conn)));
+
+ if (signal) {
+ if ((object_set = pa_hashmap_get(conn_entry->listening_signals, signal)))
+ pa_idxset_free(object_set, free_listened_object_name_cb, NULL);
+
+ } else {
+ char *object_path;
+
+ conn_entry->listening_for_all_signals = FALSE;
+
+ while ((object_path = pa_idxset_steal_first(conn_entry->all_signals_objects, NULL)))
+ pa_xfree(object_path);
+
+ while ((object_set = pa_hashmap_steal_first(conn_entry->listening_signals)))
+ pa_idxset_free(object_set, free_listened_object_name_cb, NULL);
+ }
+}
+
+void pa_dbus_protocol_send_signal(pa_dbus_protocol *p, DBusMessage *signal) {
+ struct connection_entry *conn_entry;
+ void *state = NULL;
+ pa_idxset *object_set;
+ DBusMessage *signal_copy;
+ char *signal_string;
+
+ pa_assert(p);
+ pa_assert(signal);
+ pa_assert(dbus_message_get_type(signal) == DBUS_MESSAGE_TYPE_SIGNAL);
+ pa_assert_se(dbus_message_get_interface(signal));
+ pa_assert_se(dbus_message_get_member(signal));
+
+ signal_string = pa_sprintf_malloc("%s.%s", dbus_message_get_interface(signal), dbus_message_get_member(signal));
+
+ PA_HASHMAP_FOREACH(conn_entry, p->connections, state) {
+ if ((conn_entry->listening_for_all_signals /* Case 1: listening for all signals */
+ && (pa_idxset_get_by_data(conn_entry->all_signals_objects, dbus_message_get_path(signal), NULL)
+ || pa_idxset_isempty(conn_entry->all_signals_objects)))
+
+ || (!conn_entry->listening_for_all_signals /* Case 2: not listening for all signals */
+ && (object_set = pa_hashmap_get(conn_entry->listening_signals, signal_string))
+ && (pa_idxset_get_by_data(object_set, dbus_message_get_path(signal), NULL)
+ || pa_idxset_isempty(object_set)))) {
+
+ pa_assert_se(signal_copy = dbus_message_copy(signal));
+ pa_assert_se(dbus_connection_send(conn_entry->connection, signal_copy, NULL));
+ dbus_message_unref(signal_copy);
+ }
+ }
+
+ pa_xfree(signal_string);
+}
+
+const char **pa_dbus_protocol_get_extensions(pa_dbus_protocol *p, unsigned *n) {
+ const char **extensions;
+ const char *ext_name;
+ void *state = NULL;
+ unsigned i = 0;
+
+ pa_assert(p);
+ pa_assert(n);
+
+ *n = pa_idxset_size(p->extensions);
+
+ if (*n <= 0)
+ return NULL;
+
+ extensions = pa_xnew(const char *, *n);
+
+ while ((ext_name = pa_idxset_iterate(p->extensions, &state, NULL)))
+ extensions[i++] = ext_name;
+
+ return extensions;
+}
+
+int pa_dbus_protocol_register_extension(pa_dbus_protocol *p, const char *name) {
+ char *internal_name;
+
+ pa_assert(p);
+ pa_assert(name);
+
+ internal_name = pa_xstrdup(name);
+
+ if (pa_idxset_put(p->extensions, internal_name, NULL) < 0) {
+ pa_xfree(internal_name);
+ return -1;
+ }
+
+ pa_hook_fire(&p->hooks[PA_DBUS_PROTOCOL_HOOK_EXTENSION_REGISTERED], internal_name);
+
+ return 0;
+}
+
+int pa_dbus_protocol_unregister_extension(pa_dbus_protocol *p, const char *name) {
+ char *internal_name;
+
+ pa_assert(p);
+ pa_assert(name);
+
+ if (!(internal_name = pa_idxset_remove_by_data(p->extensions, name, NULL)))
+ return -1;
+
+ pa_hook_fire(&p->hooks[PA_DBUS_PROTOCOL_HOOK_EXTENSION_UNREGISTERED], internal_name);
+
+ pa_xfree(internal_name);
+
+ return 0;
+}
+
+pa_hook_slot *pa_dbus_protocol_hook_connect(
+ pa_dbus_protocol *p,
+ pa_dbus_protocol_hook_t hook,
+ pa_hook_priority_t prio,
+ pa_hook_cb_t cb,
+ void *data) {
+ pa_assert(p);
+ pa_assert(hook < PA_DBUS_PROTOCOL_HOOK_MAX);
+ pa_assert(cb);
+
+ return pa_hook_connect(&p->hooks[hook], prio, cb, data);
+}
diff --git a/src/pulsecore/protocol-dbus.h b/src/pulsecore/protocol-dbus.h
new file mode 100644
index 00000000..6d100f7c
--- /dev/null
+++ b/src/pulsecore/protocol-dbus.h
@@ -0,0 +1,217 @@
+#ifndef fooprotocoldbushfoo
+#define fooprotocoldbushfoo
+
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2009 Tanu Kaskinen
+
+ 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 <dbus/dbus.h>
+
+#include <pulsecore/core.h>
+#include <pulsecore/macro.h>
+
+#define PA_DBUS_DEFAULT_PORT 24883
+#define PA_DBUS_SOCKET_NAME "dbus-socket"
+
+#define PA_DBUS_SYSTEM_SOCKET_PATH PA_SYSTEM_RUNTIME_PATH PA_PATH_SEP PA_DBUS_SOCKET_NAME
+
+#define PA_DBUS_CORE_INTERFACE "org.PulseAudio.Core1"
+#define PA_DBUS_CORE_OBJECT_PATH "/org/pulseaudio/core1"
+
+#define PA_DBUS_ERROR_NO_SUCH_PROPERTY PA_DBUS_CORE_INTERFACE ".NoSuchPropertyError"
+#define PA_DBUS_ERROR_NOT_FOUND PA_DBUS_CORE_INTERFACE ".NotFoundError"
+
+/* Returns the default address of the server type in the escaped form. For
+ * PA_SERVER_TYPE_NONE an empty string is returned. The caller frees the
+ * string. */
+char *pa_get_dbus_address_from_server_type(pa_server_type_t server_type);
+
+typedef struct pa_dbus_protocol pa_dbus_protocol;
+
+/* This function either creates a new pa_dbus_protocol object, or if one
+ * already exists, increases the reference count. */
+pa_dbus_protocol* pa_dbus_protocol_get(pa_core *c);
+
+pa_dbus_protocol* pa_dbus_protocol_ref(pa_dbus_protocol *p);
+void pa_dbus_protocol_unref(pa_dbus_protocol *p);
+
+/* Called when a received message needs handling. Completely ignoring the
+ * message isn't a good idea; if you can't handle the message, reply with an
+ * error.
+ *
+ * The message signature is already checked against the introspection data, so
+ * you don't have to do that yourself.
+ *
+ * All messages are method calls. */
+typedef void (*pa_dbus_receive_cb_t)(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+/* A specialized version of pa_dbus_receive_cb_t: the additional iterator
+ * argument points to the element inside the new value variant.
+ *
+ * The new value signature is checked against the introspection data, so you
+ * don't have to do that yourself. */
+typedef void (*pa_dbus_set_property_cb_t)(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata);
+
+typedef struct pa_dbus_arg_info {
+ const char *name;
+ const char *type;
+ const char *direction; /* NULL for signal arguments. */
+} pa_dbus_arg_info;
+
+typedef struct pa_dbus_signal_info {
+ const char *name;
+ const pa_dbus_arg_info *arguments; /* NULL, if the signal has no args. */
+ unsigned n_arguments;
+} pa_dbus_signal_info;
+
+typedef struct pa_dbus_method_handler {
+ const char *method_name;
+ const pa_dbus_arg_info *arguments; /* NULL, if the method has no args. */
+ unsigned n_arguments;
+ pa_dbus_receive_cb_t receive_cb;
+} pa_dbus_method_handler;
+
+typedef struct pa_dbus_property_handler {
+ const char *property_name;
+ const char *type;
+
+ /* The access mode for the property is determined by checking whether
+ * get_cb or set_cb is NULL. */
+ pa_dbus_receive_cb_t get_cb;
+ pa_dbus_set_property_cb_t set_cb;
+} pa_dbus_property_handler;
+
+typedef struct pa_dbus_interface_info {
+ const char* name;
+ const pa_dbus_method_handler *method_handlers; /* NULL, if the interface has no methods. */
+ unsigned n_method_handlers;
+ const pa_dbus_property_handler *property_handlers; /* NULL, if the interface has no properties. */
+ unsigned n_property_handlers;
+ const pa_dbus_receive_cb_t get_all_properties_cb; /* May be NULL, in which case GetAll returns an error. */
+ const pa_dbus_signal_info *signals; /* NULL, if the interface has no signals. */
+ unsigned n_signals;
+} pa_dbus_interface_info;
+
+
+/* The following functions may only be called from the main thread. */
+
+/* Registers the given interface to the given object path. It doesn't matter
+ * whether or not the object has already been registered; if it is, then its
+ * interface set is extended.
+ *
+ * Introspection requests are handled automatically.
+ *
+ * Userdata is passed to all the callbacks.
+ *
+ * Fails and returns a negative number if the object already has the interface
+ * registered. */
+int pa_dbus_protocol_add_interface(pa_dbus_protocol *p, const char *path, const pa_dbus_interface_info *info, void *userdata);
+
+/* Returns a negative number if the given object doesn't have the given
+ * interface registered. */
+int pa_dbus_protocol_remove_interface(pa_dbus_protocol *p, const char* path, const char* interface);
+
+/* Fails and returns a negative number if the connection is already
+ * registered. */
+int pa_dbus_protocol_register_connection(pa_dbus_protocol *p, DBusConnection *conn, pa_client *client);
+
+/* Returns a negative number if the connection isn't registered. */
+int pa_dbus_protocol_unregister_connection(pa_dbus_protocol *p, DBusConnection *conn);
+
+/* Returns NULL if the connection isn't registered. */
+pa_client *pa_dbus_protocol_get_client(pa_dbus_protocol *p, DBusConnection *conn);
+
+/* Enables signal receiving for the given connection. The connection must have
+ * been registered earlier. The signal string must contain both the signal
+ * interface and the signal name, concatenated using a period as the separator.
+ *
+ * If the signal argument is NULL, all signals will be sent to the connection,
+ * otherwise calling this function only adds the given signal to the list of
+ * signals that will be delivered to the connection.
+ *
+ * The objects argument is a list of object paths. If the list is not empty,
+ * only signals from the given objects are delivered. If this function is
+ * called multiple time for the same connection and signal, the latest call
+ * always replaces the previous object list. */
+void pa_dbus_protocol_add_signal_listener(
+ pa_dbus_protocol *p,
+ DBusConnection *conn,
+ const char *signal,
+ char **objects,
+ unsigned n_objects);
+
+/* Disables the delivery of the signal for the given connection. The connection
+ * must have been registered. If signal is NULL, all signals are disabled. If
+ * signal is non-NULL and _add_signal_listener() was previously called with
+ * NULL signal (causing all signals to be enabled), this function doesn't do
+ * anything. Also, if the signal wasn't enabled before, this function doesn't
+ * do anything in that case either. */
+void pa_dbus_protocol_remove_signal_listener(pa_dbus_protocol *p, DBusConnection *conn, const char *signal);
+
+/* Sends the given signal to all interested clients. By default no signals are
+ * sent - clients have to explicitly to request signals by calling
+ * .Core1.ListenForSignal. That method's handler then calls
+ * pa_dbus_protocol_add_signal_listener(). */
+void pa_dbus_protocol_send_signal(pa_dbus_protocol *p, DBusMessage *signal);
+
+/* Returns an array of extension identifier strings. The strings pointers point
+ * to the internal copies, so don't free the strings. The caller must free the
+ * array, however. Also, do not save the returned pointer or any of the string
+ * pointers, because the contained strings may be freed at any time. If you
+ * need to save the array, copy it. */
+const char **pa_dbus_protocol_get_extensions(pa_dbus_protocol *p, unsigned *n);
+
+/* Modules that want to provide a D-Bus interface for clients should register
+ * an identifier that the clients can use to check whether the additional
+ * functionality is available.
+ *
+ * This function registers the extension with the given name. It is recommended
+ * that the name follows the D-Bus interface naming convention, so that the
+ * names remain unique in case there will be at some point in the future
+ * extensions that aren't included with the main PulseAudio source tree. For
+ * in-tree extensions the convention is to use the org.PulseAudio.Ext
+ * namespace.
+ *
+ * It is suggested that the name contains a version number, and whenever the
+ * extension interface is modified in non-backwards compatible way, the version
+ * number is incremented.
+ *
+ * Fails and returns a negative number if the extension is already registered.
+ */
+int pa_dbus_protocol_register_extension(pa_dbus_protocol *p, const char *name);
+
+/* Returns a negative number if the extension isn't registered. */
+int pa_dbus_protocol_unregister_extension(pa_dbus_protocol *p, const char *name);
+
+/* All hooks have the pa_dbus_protocol object as hook data. */
+typedef enum pa_dbus_protocol_hook {
+ PA_DBUS_PROTOCOL_HOOK_EXTENSION_REGISTERED, /* Extension name as call data. */
+ PA_DBUS_PROTOCOL_HOOK_EXTENSION_UNREGISTERED, /* Extension name as call data. */
+ PA_DBUS_PROTOCOL_HOOK_MAX
+} pa_dbus_protocol_hook_t;
+
+pa_hook_slot *pa_dbus_protocol_hook_connect(
+ pa_dbus_protocol *p,
+ pa_dbus_protocol_hook_t hook,
+ pa_hook_priority_t prio,
+ pa_hook_cb_t cb,
+ void *data);
+
+#endif
diff --git a/src/pulsecore/protocol-esound.c b/src/pulsecore/protocol-esound.c
index f64552aa..2326eb3a 100644
--- a/src/pulsecore/protocol-esound.c
+++ b/src/pulsecore/protocol-esound.c
@@ -120,9 +120,8 @@ typedef struct connection {
pa_time_event *auth_timeout_event;
} connection;
-PA_DECLARE_CLASS(connection);
+PA_DEFINE_PRIVATE_CLASS(connection, pa_msgobject);
#define CONNECTION(o) (connection_cast(o))
-static PA_DEFINE_CHECK_TYPE(connection, pa_msgobject);
struct pa_esound_protocol {
PA_REFCNT_DECLARE;
@@ -430,7 +429,7 @@ static int esd_proto_stream_play(connection *c, esd_proto_t request, const void
sdata.sink = sink;
pa_sink_input_new_data_set_sample_spec(&sdata, &ss);
- pa_sink_input_new(&c->sink_input, c->protocol->core, &sdata, 0);
+ pa_sink_input_new(&c->sink_input, c->protocol->core, &sdata);
pa_sink_input_new_data_done(&sdata);
CHECK_VALIDITY(c->sink_input, "Failed to create sink input.");
@@ -526,7 +525,7 @@ static int esd_proto_stream_record(connection *c, esd_proto_t request, const voi
sdata.source = source;
pa_source_output_new_data_set_sample_spec(&sdata, &ss);
- pa_source_output_new(&c->source_output, c->protocol->core, &sdata, 0);
+ pa_source_output_new(&c->source_output, c->protocol->core, &sdata);
pa_source_output_new_data_done(&sdata);
CHECK_VALIDITY(c->source_output, "Failed to create source output.");
@@ -772,7 +771,6 @@ static int esd_proto_stream_pan(connection *c, esd_proto_t request, const void *
memcpy(&rvolume, data, sizeof(uint32_t));
rvolume = PA_MAYBE_UINT32_SWAP(c->swap_byte_order, rvolume);
- data = (const char*)data + sizeof(uint32_t);
if ((conn = pa_idxset_get_by_index(c->protocol->connections, idx)) && conn->sink_input) {
pa_cvolume volume;
@@ -810,7 +808,6 @@ static int esd_proto_sample_pan(connection *c, esd_proto_t request, const void *
memcpy(&rvolume, data, sizeof(uint32_t));
rvolume = PA_MAYBE_UINT32_SWAP(c->swap_byte_order, rvolume);
- data = (const char*)data + sizeof(uint32_t);
volume.values[0] = (lvolume*PA_VOLUME_NORM)/ESD_VOLUME_BASE;
volume.values[1] = (rvolume*PA_VOLUME_NORM)/ESD_VOLUME_BASE;
@@ -1124,7 +1121,7 @@ static int do_read(connection *c) {
ssize_t r;
size_t l;
void *p;
- size_t space;
+ size_t space = 0;
pa_assert(c->input_memblockq);
diff --git a/src/pulsecore/protocol-http.c b/src/pulsecore/protocol-http.c
index 5220cc91..c09e5348 100644
--- a/src/pulsecore/protocol-http.c
+++ b/src/pulsecore/protocol-http.c
@@ -533,7 +533,7 @@ static void handle_listen_prefix(struct connection *c, const char *source_name)
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(&c->source_output, c->protocol->core, &data);
pa_source_output_new_data_done(&data);
if (!c->source_output) {
diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c
index 9a37c565..bb29a196 100644
--- a/src/pulsecore/protocol-native.c
+++ b/src/pulsecore/protocol-native.c
@@ -98,17 +98,15 @@ typedef struct record_stream {
pa_usec_t current_source_latency;
} record_stream;
-PA_DECLARE_CLASS(record_stream);
#define RECORD_STREAM(o) (record_stream_cast(o))
-static PA_DEFINE_CHECK_TYPE(record_stream, pa_msgobject);
+PA_DEFINE_PRIVATE_CLASS(record_stream, pa_msgobject);
typedef struct output_stream {
pa_msgobject parent;
} output_stream;
-PA_DECLARE_CLASS(output_stream);
#define OUTPUT_STREAM(o) (output_stream_cast(o))
-static PA_DEFINE_CHECK_TYPE(output_stream, pa_msgobject);
+PA_DEFINE_PRIVATE_CLASS(output_stream, pa_msgobject);
typedef struct playback_stream {
output_stream parent;
@@ -138,9 +136,8 @@ typedef struct playback_stream {
uint64_t playing_for, underrun_for;
} playback_stream;
-PA_DECLARE_CLASS(playback_stream);
#define PLAYBACK_STREAM(o) (playback_stream_cast(o))
-static PA_DEFINE_CHECK_TYPE(playback_stream, output_stream);
+PA_DEFINE_PRIVATE_CLASS(playback_stream, output_stream);
typedef struct upload_stream {
output_stream parent;
@@ -156,9 +153,8 @@ typedef struct upload_stream {
pa_proplist *proplist;
} upload_stream;
-PA_DECLARE_CLASS(upload_stream);
#define UPLOAD_STREAM(o) (upload_stream_cast(o))
-static PA_DEFINE_CHECK_TYPE(upload_stream, output_stream);
+PA_DEFINE_PRIVATE_CLASS(upload_stream, output_stream);
struct pa_native_connection {
pa_msgobject parent;
@@ -176,9 +172,8 @@ struct pa_native_connection {
pa_time_event *auth_timeout_event;
};
-PA_DECLARE_CLASS(pa_native_connection);
#define PA_NATIVE_CONNECTION(o) (pa_native_connection_cast(o))
-static PA_DEFINE_CHECK_TYPE(pa_native_connection, pa_msgobject);
+PA_DEFINE_PRIVATE_CLASS(pa_native_connection, pa_msgobject);
struct pa_native_protocol {
PA_REFCNT_DECLARE;
@@ -633,7 +628,6 @@ static record_stream* record_stream_new(
record_stream *s;
pa_source_output *source_output = NULL;
- size_t base;
pa_source_output_new_data data;
pa_assert(c);
@@ -653,8 +647,9 @@ static record_stream* record_stream_new(
pa_source_output_new_data_set_channel_map(&data, map);
if (peak_detect)
data.resample_method = PA_RESAMPLER_PEAKS;
+ data.flags = flags;
- *ret = -pa_source_output_new(&source_output, c->protocol->core, &data, flags);
+ *ret = -pa_source_output_new(&source_output, c->protocol->core, &data);
pa_source_output_new_data_done(&data);
@@ -686,7 +681,7 @@ static record_stream* record_stream_new(
0,
s->buffer_attr.maxlength,
0,
- base = pa_frame_size(&source_output->sample_spec),
+ pa_frame_size(&source_output->sample_spec),
1,
0,
0,
@@ -762,6 +757,7 @@ static int playback_stream_process_msg(pa_msgobject *o, int code, void*userdata,
return -1;
switch (code) {
+
case PLAYBACK_STREAM_MESSAGE_REQUEST_DATA: {
pa_tagstruct *t;
int l = 0;
@@ -830,24 +826,26 @@ static int playback_stream_process_msg(pa_msgobject *o, int code, void*userdata,
pa_pstream_send_simple_ack(s->connection->pstream, PA_PTR_TO_UINT(userdata));
break;
- case PLAYBACK_STREAM_MESSAGE_UPDATE_TLENGTH: {
- pa_tagstruct *t;
+ case PLAYBACK_STREAM_MESSAGE_UPDATE_TLENGTH:
s->buffer_attr.tlength = (uint32_t) offset;
- t = pa_tagstruct_new(NULL, 0);
- pa_tagstruct_putu32(t, PA_COMMAND_PLAYBACK_BUFFER_ATTR_CHANGED);
- pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
- pa_tagstruct_putu32(t, s->index);
- pa_tagstruct_putu32(t, s->buffer_attr.maxlength);
- pa_tagstruct_putu32(t, s->buffer_attr.tlength);
- pa_tagstruct_putu32(t, s->buffer_attr.prebuf);
- pa_tagstruct_putu32(t, s->buffer_attr.minreq);
- pa_tagstruct_put_usec(t, s->configured_sink_latency);
- pa_pstream_send_tagstruct(s->connection->pstream, t);
+ if (s->connection->version >= 15) {
+ pa_tagstruct *t;
+
+ t = pa_tagstruct_new(NULL, 0);
+ pa_tagstruct_putu32(t, PA_COMMAND_PLAYBACK_BUFFER_ATTR_CHANGED);
+ pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
+ pa_tagstruct_putu32(t, s->index);
+ pa_tagstruct_putu32(t, s->buffer_attr.maxlength);
+ pa_tagstruct_putu32(t, s->buffer_attr.tlength);
+ pa_tagstruct_putu32(t, s->buffer_attr.prebuf);
+ pa_tagstruct_putu32(t, s->buffer_attr.minreq);
+ pa_tagstruct_put_usec(t, s->configured_sink_latency);
+ pa_pstream_send_tagstruct(s->connection->pstream, t);
+ }
break;
- }
}
return 0;
@@ -1004,6 +1002,7 @@ static playback_stream* playback_stream_new(
pa_proplist *p,
pa_bool_t adjust_latency,
pa_bool_t early_requests,
+ pa_bool_t relative_volume,
int *ret) {
playback_stream *s, *ssync;
@@ -1046,16 +1045,25 @@ static playback_stream* playback_stream_new(
data.driver = __FILE__;
data.module = c->options->module;
data.client = c->client;
- data.sink = sink;
+ if (sink) {
+ data.sink = sink;
+ data.save_sink = TRUE;
+ }
pa_sink_input_new_data_set_sample_spec(&data, ss);
pa_sink_input_new_data_set_channel_map(&data, map);
- if (volume)
+ if (volume) {
pa_sink_input_new_data_set_volume(&data, volume);
- if (muted_set)
+ data.volume_is_absolute = !relative_volume;
+ data.save_volume = TRUE;
+ }
+ if (muted_set) {
pa_sink_input_new_data_set_muted(&data, muted);
+ data.save_muted = TRUE;
+ }
data.sync_base = ssync ? ssync->sink_input : NULL;
+ data.flags = flags;
- *ret = -pa_sink_input_new(&sink_input, c->protocol->core, &data, flags);
+ *ret = -pa_sink_input_new(&sink_input, c->protocol->core, &data);
pa_sink_input_new_data_done(&data);
@@ -1130,6 +1138,12 @@ static void playback_stream_request_bytes(playback_stream *s) {
m = pa_memblockq_pop_missing(s->memblockq);
+ /* pa_log("request_bytes(%lu) (tlength=%lu minreq=%lu length=%lu)", */
+ /* (unsigned long) m, */
+ /* pa_memblockq_get_tlength(s->memblockq), */
+ /* pa_memblockq_get_minreq(s->memblockq), */
+ /* pa_memblockq_get_length(s->memblockq)); */
+
if (m <= 0)
return;
@@ -1143,7 +1157,6 @@ static void playback_stream_request_bytes(playback_stream *s) {
pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_REQUEST_DATA, NULL, 0, NULL, NULL);
}
-
/* Called from main context */
static void playback_stream_send_killed(playback_stream *p) {
pa_tagstruct *t;
@@ -1288,7 +1301,8 @@ static void handle_seek(playback_stream *s, int64_t indexw) {
pa_log_debug("Requesting rewind due to end of underrun.");
pa_sink_input_request_rewind(s->sink_input,
- (size_t) (s->sink_input->thread_info.underrun_for == (size_t) -1 ? 0 : s->sink_input->thread_info.underrun_for),
+ (size_t) (s->sink_input->thread_info.underrun_for == (uint64_t) -1 ? 0 :
+ s->sink_input->thread_info.underrun_for),
FALSE, TRUE, FALSE);
}
@@ -1345,7 +1359,9 @@ static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int
/* pa_log("sink input post: %lu %lli", (unsigned long) chunk->length, (long long) windex); */
if (pa_memblockq_push_align(s->memblockq, chunk) < 0) {
- pa_log_warn("Failed to push data into queue");
+
+ if (pa_log_ratelimit())
+ pa_log_warn("Failed to push data into queue");
pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_OVERFLOW, NULL, 0, NULL, NULL);
pa_memblockq_seek(s->memblockq, (int64_t) chunk->length, PA_SEEK_RELATIVE, TRUE);
}
@@ -1617,6 +1633,9 @@ static void sink_input_moving_cb(pa_sink_input *i, pa_sink *dest) {
s = PLAYBACK_STREAM(i->userdata);
playback_stream_assert_ref(s);
+ if (!dest)
+ return;
+
fix_playback_buffer_attr(s);
pa_memblockq_apply_attr(s->memblockq, &s->buffer_attr);
pa_memblockq_get_attr(s->memblockq, &s->buffer_attr);
@@ -1752,6 +1771,9 @@ static void source_output_moving_cb(pa_source_output *o, pa_source *dest) {
s = RECORD_STREAM(o->userdata);
record_stream_assert_ref(s);
+ if (!dest)
+ return;
+
fix_record_buffer_attr_pre(s);
pa_memblockq_set_maxlength(s->memblockq, s->buffer_attr.maxlength);
pa_memblockq_get_attr(s->memblockq, &s->buffer_attr);
@@ -1825,7 +1847,8 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
early_requests = FALSE,
dont_inhibit_auto_suspend = FALSE,
muted_set = FALSE,
- fail_on_suspend = FALSE;
+ fail_on_suspend = FALSE,
+ relative_volume = FALSE;
pa_sink_input_flags_t flags = 0;
pa_proplist *p;
pa_bool_t volume_set = TRUE;
@@ -1856,7 +1879,7 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
}
CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
- CHECK_VALIDITY(c->pstream, !sink_name || pa_namereg_is_valid_name(sink_name), tag, PA_ERR_INVALID);
+ CHECK_VALIDITY(c->pstream, !sink_name || pa_namereg_is_valid_name_or_wildcard(sink_name, PA_NAMEREG_SINK), tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, sink_index == PA_INVALID_INDEX || !sink_name, tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, !sink_name || sink_index == PA_INVALID_INDEX, tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, pa_channel_map_valid(&map), tag, PA_ERR_INVALID);
@@ -1918,6 +1941,15 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
}
}
+ if (c->version >= 17) {
+
+ if (pa_tagstruct_get_boolean(t, &relative_volume) < 0) {
+ protocol_error(c);
+ pa_proplist_free(p);
+ return;
+ }
+ }
+
if (!pa_tagstruct_eof(t)) {
protocol_error(c);
pa_proplist_free(p);
@@ -1951,13 +1983,13 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
(no_move ? PA_SINK_INPUT_DONT_MOVE : 0) |
(variable_rate ? PA_SINK_INPUT_VARIABLE_RATE : 0) |
(dont_inhibit_auto_suspend ? PA_SINK_INPUT_DONT_INHIBIT_AUTO_SUSPEND : 0) |
- (fail_on_suspend ? PA_SINK_INPUT_FAIL_ON_SUSPEND : 0);
+ (fail_on_suspend ? PA_SINK_INPUT_NO_CREATE_ON_SUSPEND|PA_SINK_INPUT_KILL_ON_SUSPEND : 0);
/* Only since protocol version 15 there's a seperate muted_set
* flag. For older versions we synthesize it here */
muted_set = muted_set || muted;
- s = playback_stream_new(c, sink, &ss, &map, &attr, volume_set ? &volume : NULL, muted, muted_set, syncid, &missing, flags, p, adjust_latency, early_requests, &ret);
+ s = playback_stream_new(c, sink, &ss, &map, &attr, volume_set ? &volume : NULL, muted, muted_set, syncid, &missing, flags, p, adjust_latency, early_requests, relative_volume, &ret);
pa_proplist_free(p);
CHECK_VALIDITY(c->pstream, s, tag, ret);
@@ -2105,7 +2137,7 @@ static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uin
}
CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
- CHECK_VALIDITY(c->pstream, !source_name || pa_namereg_is_valid_name(source_name), tag, PA_ERR_INVALID);
+ CHECK_VALIDITY(c->pstream, !source_name || pa_namereg_is_valid_name_or_wildcard(source_name, PA_NAMEREG_SOURCE), tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, source_index == PA_INVALID_INDEX || !source_name, tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, !source_name || source_index == PA_INVALID_INDEX, tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, pa_sample_spec_valid(&ss), tag, PA_ERR_INVALID);
@@ -2207,7 +2239,7 @@ static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uin
(no_move ? PA_SOURCE_OUTPUT_DONT_MOVE : 0) |
(variable_rate ? PA_SOURCE_OUTPUT_VARIABLE_RATE : 0) |
(dont_inhibit_auto_suspend ? PA_SOURCE_OUTPUT_DONT_INHIBIT_AUTO_SUSPEND : 0) |
- (fail_on_suspend ? PA_SOURCE_OUTPUT_FAIL_ON_SUSPEND : 0);
+ (fail_on_suspend ? PA_SOURCE_OUTPUT_NO_CREATE_ON_SUSPEND|PA_SOURCE_OUTPUT_KILL_ON_SUSPEND : 0);
s = record_stream_new(c, source, &ss, &map, peak_detect, &attr, flags, p, adjust_latency, direct_on_input, early_requests, &ret);
pa_proplist_free(p);
@@ -2262,6 +2294,8 @@ static void command_exit(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_ta
ret = pa_core_exit(c->protocol->core, FALSE, 0);
CHECK_VALIDITY(c->pstream, ret >= 0, tag, PA_ERR_ACCESS);
+ pa_log_debug("Client %s asks us to terminate.", pa_strnull(pa_proplist_gets(c->client->proplist, PA_PROP_APPLICATION_PROCESS_BINARY)));
+
pa_pstream_send_simple_ack(c->pstream, tag); /* nonsense */
}
@@ -2451,7 +2485,7 @@ static void command_lookup(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_
}
CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
- CHECK_VALIDITY(c->pstream, name && pa_namereg_is_valid_name(name), tag, PA_ERR_INVALID);
+ CHECK_VALIDITY(c->pstream, name && pa_namereg_is_valid_name_or_wildcard(name, command == PA_COMMAND_LOOKUP_SINK ? PA_NAMEREG_SINK : PA_NAMEREG_SOURCE), tag, PA_ERR_INVALID);
if (command == PA_COMMAND_LOOKUP_SINK) {
pa_sink *sink;
@@ -2722,7 +2756,7 @@ static void command_play_sample(pa_pdispatch *pd, uint32_t command, uint32_t tag
return;
}
- CHECK_VALIDITY(c->pstream, !sink_name || pa_namereg_is_valid_name(sink_name), tag, PA_ERR_INVALID);
+ CHECK_VALIDITY(c->pstream, !sink_name || pa_namereg_is_valid_name_or_wildcard(sink_name, PA_NAMEREG_SINK), tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, sink_index == PA_INVALID_INDEX || !sink_name, tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, !sink_name || sink_index == PA_INVALID_INDEX, tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, name && pa_namereg_is_valid_name(name), tag, PA_ERR_INVALID);
@@ -2826,7 +2860,7 @@ static void sink_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_sin
PA_TAG_SAMPLE_SPEC, &fixed_ss,
PA_TAG_CHANNEL_MAP, &sink->channel_map,
PA_TAG_U32, sink->module ? sink->module->index : PA_INVALID_INDEX,
- PA_TAG_CVOLUME, pa_sink_get_volume(sink, FALSE, FALSE),
+ PA_TAG_CVOLUME, pa_sink_get_volume(sink, FALSE),
PA_TAG_BOOLEAN, pa_sink_get_mute(sink, FALSE),
PA_TAG_U32, sink->monitor_source ? sink->monitor_source->index : PA_INVALID_INDEX,
PA_TAG_STRING, sink->monitor_source ? sink->monitor_source->name : NULL,
@@ -3096,7 +3130,12 @@ static void command_get_info(pa_pdispatch *pd, uint32_t command, uint32_t tag, p
}
CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
- CHECK_VALIDITY(c->pstream, !name || pa_namereg_is_valid_name(name), tag, PA_ERR_INVALID);
+ CHECK_VALIDITY(c->pstream, !name ||
+ (command == PA_COMMAND_GET_SINK_INFO &&
+ pa_namereg_is_valid_name_or_wildcard(name, PA_NAMEREG_SINK)) ||
+ (command == PA_COMMAND_GET_SOURCE_INFO &&
+ pa_namereg_is_valid_name_or_wildcard(name, PA_NAMEREG_SOURCE)) ||
+ pa_namereg_is_valid_name(name), tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, idx != PA_INVALID_INDEX || name, tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, idx == PA_INVALID_INDEX || !name, tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, !name || idx == PA_INVALID_INDEX, tag, PA_ERR_INVALID);
@@ -3323,6 +3362,7 @@ static void command_set_volume(
pa_source *source = NULL;
pa_sink_input *si = NULL;
const char *name = NULL;
+ const char *client_name;
pa_native_connection_assert_ref(c);
pa_assert(t);
@@ -3337,7 +3377,7 @@ static void command_set_volume(
}
CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
- CHECK_VALIDITY(c->pstream, !name || pa_namereg_is_valid_name(name), tag, PA_ERR_INVALID);
+ CHECK_VALIDITY(c->pstream, !name || pa_namereg_is_valid_name_or_wildcard(name, command == PA_COMMAND_SET_SINK_VOLUME ? PA_NAMEREG_SINK : PA_NAMEREG_SOURCE), tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, idx != PA_INVALID_INDEX || name, tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, idx == PA_INVALID_INDEX || !name, tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, !name || idx == PA_INVALID_INDEX, tag, PA_ERR_INVALID);
@@ -3369,12 +3409,26 @@ static void command_set_volume(
CHECK_VALIDITY(c->pstream, si || sink || source, tag, PA_ERR_NOENTITY);
- if (sink)
- pa_sink_set_volume(sink, &volume, TRUE, TRUE, TRUE, TRUE);
- else if (source)
+ client_name = pa_strnull(pa_proplist_gets(c->client->proplist, PA_PROP_APPLICATION_PROCESS_BINARY));
+
+ if (sink) {
+ CHECK_VALIDITY(c->pstream, volume.channels == 1 || pa_cvolume_compatible(&volume, &sink->sample_spec), tag, PA_ERR_INVALID);
+
+ pa_log_debug("Client %s changes volume of sink %s.", client_name, sink->name);
+ pa_sink_set_volume(sink, &volume, TRUE, TRUE);
+ } else if (source) {
+ CHECK_VALIDITY(c->pstream, volume.channels == 1 || pa_cvolume_compatible(&volume, &source->sample_spec), tag, PA_ERR_INVALID);
+
+ pa_log_debug("Client %s changes volume of source %s.", client_name, source->name);
pa_source_set_volume(source, &volume, TRUE);
- else if (si)
+ } else if (si) {
+ CHECK_VALIDITY(c->pstream, volume.channels == 1 || pa_cvolume_compatible(&volume, &si->sample_spec), tag, PA_ERR_INVALID);
+
+ pa_log_debug("Client %s changes volume of sink input %s.",
+ client_name,
+ pa_strnull(pa_proplist_gets(si->proplist, PA_PROP_MEDIA_NAME)));
pa_sink_input_set_volume(si, &volume, TRUE, TRUE);
+ }
pa_pstream_send_simple_ack(c->pstream, tag);
}
@@ -3392,7 +3446,7 @@ static void command_set_mute(
pa_sink *sink = NULL;
pa_source *source = NULL;
pa_sink_input *si = NULL;
- const char *name = NULL;
+ const char *name = NULL, *client_name;
pa_native_connection_assert_ref(c);
pa_assert(t);
@@ -3407,7 +3461,7 @@ static void command_set_mute(
}
CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
- CHECK_VALIDITY(c->pstream, !name || pa_namereg_is_valid_name(name), tag, PA_ERR_INVALID);
+ CHECK_VALIDITY(c->pstream, !name || pa_namereg_is_valid_name_or_wildcard(name, command == PA_COMMAND_SET_SINK_MUTE ? PA_NAMEREG_SINK : PA_NAMEREG_SOURCE), tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, idx != PA_INVALID_INDEX || name, tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, idx == PA_INVALID_INDEX || !name, tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, !name || idx == PA_INVALID_INDEX, tag, PA_ERR_INVALID);
@@ -3415,7 +3469,6 @@ static void command_set_mute(
switch (command) {
case PA_COMMAND_SET_SINK_MUTE:
-
if (idx != PA_INVALID_INDEX)
sink = pa_idxset_get_by_index(c->protocol->core->sinks, idx);
else
@@ -3441,12 +3494,20 @@ static void command_set_mute(
CHECK_VALIDITY(c->pstream, si || sink || source, tag, PA_ERR_NOENTITY);
- if (sink)
+ client_name = pa_strnull(pa_proplist_gets(c->client->proplist, PA_PROP_APPLICATION_PROCESS_BINARY));
+
+ if (sink) {
+ pa_log_debug("Client %s changes mute of sink %s.", client_name, sink->name);
pa_sink_set_mute(sink, mute, TRUE);
- else if (source)
+ } else if (source) {
+ pa_log_debug("Client %s changes mute of source %s.", client_name, source->name);
pa_source_set_mute(source, mute, TRUE);
- else if (si)
+ } else if (si) {
+ pa_log_debug("Client %s changes mute of sink input %s.",
+ client_name,
+ pa_strnull(pa_proplist_gets(si->proplist, PA_PROP_MEDIA_NAME)));
pa_sink_input_set_mute(si, mute, TRUE);
+ }
pa_pstream_send_simple_ack(c->pstream, tag);
}
@@ -4065,7 +4126,7 @@ static void command_move_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag
CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
CHECK_VALIDITY(c->pstream, idx != PA_INVALID_INDEX, tag, PA_ERR_INVALID);
- CHECK_VALIDITY(c->pstream, !name_device || pa_namereg_is_valid_name(name_device), tag, PA_ERR_INVALID);
+ CHECK_VALIDITY(c->pstream, !name_device || pa_namereg_is_valid_name_or_wildcard(name_device, command == PA_COMMAND_MOVE_SINK_INPUT ? PA_NAMEREG_SINK : PA_NAMEREG_SOURCE), tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, idx_device != PA_INVALID_INDEX || name_device, tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, idx_device == PA_INVALID_INDEX || !name_device, tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, !name_device || idx_device == PA_INVALID_INDEX, tag, PA_ERR_INVALID);
@@ -4129,7 +4190,7 @@ static void command_suspend(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa
}
CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
- CHECK_VALIDITY(c->pstream, !name || pa_namereg_is_valid_name(name) || *name == 0, tag, PA_ERR_INVALID);
+ CHECK_VALIDITY(c->pstream, !name || pa_namereg_is_valid_name_or_wildcard(name, command == PA_COMMAND_SUSPEND_SINK ? PA_NAMEREG_SINK : PA_NAMEREG_SOURCE) || *name == 0, tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, idx != PA_INVALID_INDEX || name, tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, idx == PA_INVALID_INDEX || !name, tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, !name || idx == PA_INVALID_INDEX, tag, PA_ERR_INVALID);
@@ -4289,7 +4350,7 @@ static void command_set_sink_or_source_port(pa_pdispatch *pd, uint32_t command,
}
CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
- CHECK_VALIDITY(c->pstream, !name || pa_namereg_is_valid_name(name), tag, PA_ERR_INVALID);
+ CHECK_VALIDITY(c->pstream, !name || pa_namereg_is_valid_name_or_wildcard(name, command == PA_COMMAND_SET_SINK_PORT ? PA_NAMEREG_SINK : PA_NAMEREG_SOURCE), tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, idx != PA_INVALID_INDEX || name, tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, idx == PA_INVALID_INDEX || !name, tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, !name || idx == PA_INVALID_INDEX, tag, PA_ERR_INVALID);
@@ -4824,3 +4885,9 @@ pa_pstream* pa_native_connection_get_pstream(pa_native_connection *c) {
return c->pstream;
}
+
+pa_client* pa_native_connection_get_client(pa_native_connection *c) {
+ pa_native_connection_assert_ref(c);
+
+ return c->client;
+}
diff --git a/src/pulsecore/protocol-native.h b/src/pulsecore/protocol-native.h
index 8a8d601c..97126274 100644
--- a/src/pulsecore/protocol-native.h
+++ b/src/pulsecore/protocol-native.h
@@ -47,7 +47,6 @@ typedef struct pa_native_options {
char *auth_group;
pa_ip_acl *auth_ip_acl;
pa_auth_cookie *auth_cookie;
-
} pa_native_options;
typedef enum pa_native_hook {
@@ -80,6 +79,7 @@ int pa_native_protocol_install_ext(pa_native_protocol *p, pa_module *m, pa_nativ
void pa_native_protocol_remove_ext(pa_native_protocol *p, pa_module *m);
pa_pstream* pa_native_connection_get_pstream(pa_native_connection *c);
+pa_client* pa_native_connection_get_client(pa_native_connection *c);
pa_native_options* pa_native_options_new(void);
pa_native_options* pa_native_options_ref(pa_native_options *o);
diff --git a/src/pulsecore/protocol-simple.c b/src/pulsecore/protocol-simple.c
index 776d74b6..a9f73896 100644
--- a/src/pulsecore/protocol-simple.c
+++ b/src/pulsecore/protocol-simple.c
@@ -69,9 +69,8 @@ typedef struct connection {
} playback;
} connection;
-PA_DECLARE_CLASS(connection);
+PA_DEFINE_PRIVATE_CLASS(connection, pa_msgobject);
#define CONNECTION(o) (connection_cast(o))
-static PA_DEFINE_CHECK_TYPE(connection, pa_msgobject);
struct pa_simple_protocol {
PA_REFCNT_DECLARE;
@@ -155,7 +154,7 @@ static int do_read(connection *c) {
ssize_t r;
size_t l;
void *p;
- size_t space;
+ size_t space = 0;
connection_assert_ref(c);
@@ -542,7 +541,7 @@ void pa_simple_protocol_connect(pa_simple_protocol *p, pa_iochannel *io, pa_simp
pa_proplist_update(data.proplist, PA_UPDATE_MERGE, c->client->proplist);
pa_sink_input_new_data_set_sample_spec(&data, &o->sample_spec);
- pa_sink_input_new(&c->sink_input, p->core, &data, 0);
+ pa_sink_input_new(&c->sink_input, p->core, &data);
pa_sink_input_new_data_done(&data);
if (!c->sink_input) {
@@ -594,7 +593,7 @@ void pa_simple_protocol_connect(pa_simple_protocol *p, pa_iochannel *io, pa_simp
pa_proplist_update(data.proplist, PA_UPDATE_MERGE, c->client->proplist);
pa_source_output_new_data_set_sample_spec(&data, &o->sample_spec);
- pa_source_output_new(&c->source_output, p->core, &data, 0);
+ pa_source_output_new(&c->source_output, p->core, &data);
pa_source_output_new_data_done(&data);
if (!c->source_output) {
@@ -628,8 +627,7 @@ void pa_simple_protocol_connect(pa_simple_protocol *p, pa_iochannel *io, pa_simp
return;
fail:
- if (c)
- connection_unlink(c);
+ connection_unlink(c);
}
void pa_simple_protocol_disconnect(pa_simple_protocol *p, pa_module *m) {
diff --git a/src/pulsecore/random.c b/src/pulsecore/random.c
index 518c281a..a87d24e3 100644
--- a/src/pulsecore/random.c
+++ b/src/pulsecore/random.c
@@ -62,11 +62,7 @@ static int random_proper(void *ret_data, size_t length) {
while (*device) {
ret = 0;
- if ((fd = open(*device, O_RDONLY
-#ifdef O_NOCTTY
- | O_NOCTTY
-#endif
- )) >= 0) {
+ if ((fd = pa_open_cloexec(*device, O_RDONLY, 0)) >= 0) {
if ((r = pa_loop_read(fd, ret_data, length, NULL)) < 0 || (size_t) r != length)
ret = -1;
diff --git a/src/pulsecore/ratelimit.h b/src/pulsecore/ratelimit.h
index ec3b5a38..9857a291 100644
--- a/src/pulsecore/ratelimit.h
+++ b/src/pulsecore/ratelimit.h
@@ -26,21 +26,31 @@
#include <pulsecore/macro.h>
typedef struct pa_ratelimit {
- const pa_usec_t interval;
- const unsigned burst;
+ pa_usec_t interval;
+ unsigned burst;
unsigned n_printed, n_missed;
pa_usec_t begin;
} pa_ratelimit;
#define PA_DEFINE_RATELIMIT(_name, _interval, _burst) \
pa_ratelimit _name = { \
- .interval = _interval, \
- .burst = _burst, \
+ .interval = (_interval), \
+ .burst = (_burst), \
.n_printed = 0, \
.n_missed = 0, \
.begin = 0 \
}
+#define PA_INIT_RATELIMIT(v, _interval, _burst) \
+ do { \
+ pa_ratelimit *r = &(v); \
+ r->interval = (_interval); \
+ r->burst = (_burst); \
+ r->n_printed = 0; \
+ r->n_missed = 0; \
+ r->begin = 0; \
+ } while (FALSE);
+
pa_bool_t pa_ratelimit_test(pa_ratelimit *r);
#endif
diff --git a/src/pulsecore/remap.c b/src/pulsecore/remap.c
new file mode 100644
index 00000000..a0fc85b9
--- /dev/null
+++ b/src/pulsecore/remap.c
@@ -0,0 +1,204 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2004-2006 Lennart Poettering
+ Copyright 2009 Wim Taymans <wim.taymans@collabora.co.uk.com>
+
+ 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 <string.h>
+
+#include <pulse/sample.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+
+#include "remap.h"
+
+static void remap_mono_to_stereo_c (pa_remap_t *m, void *dst, const void *src, unsigned n) {
+ unsigned i;
+
+ switch (*m->format) {
+ case PA_SAMPLE_FLOAT32NE:
+ {
+ float *d, *s;
+
+ d = (float *) dst;
+ s = (float *) src;
+
+ for (i = n >> 2; i; i--) {
+ d[0] = d[1] = s[0];
+ d[2] = d[3] = s[1];
+ d[4] = d[5] = s[2];
+ d[6] = d[7] = s[3];
+ s += 4;
+ d += 8;
+ }
+ for (i = n & 3; i; i--) {
+ d[0] = d[1] = s[0];
+ s++;
+ d += 2;
+ }
+ break;
+ }
+ case PA_SAMPLE_S16NE:
+ {
+ int16_t *d, *s;
+
+ d = (int16_t *) dst;
+ s = (int16_t *) src;
+
+ for (i = n >> 2; i; i--) {
+ d[0] = d[1] = s[0];
+ d[2] = d[3] = s[1];
+ d[4] = d[5] = s[2];
+ d[6] = d[7] = s[3];
+ s += 4;
+ d += 8;
+ }
+ for (i = n & 3; i; i--) {
+ d[0] = d[1] = s[0];
+ s++;
+ d += 2;
+ }
+ break;
+ }
+ default:
+ pa_assert_not_reached();
+ }
+}
+
+static void remap_channels_matrix_c (pa_remap_t *m, void *dst, const void *src, unsigned n) {
+ unsigned oc, ic, i;
+ unsigned n_ic, n_oc;
+
+ n_ic = m->i_ss->channels;
+ n_oc = m->o_ss->channels;
+
+ switch (*m->format) {
+ case PA_SAMPLE_FLOAT32NE:
+ {
+ float *d, *s;
+
+ memset(dst, 0, n * sizeof (float) * n_oc);
+
+ for (oc = 0; oc < n_oc; oc++) {
+
+ for (ic = 0; ic < n_ic; ic++) {
+ float vol;
+
+ vol = m->map_table_f[oc][ic];
+
+ if (vol <= 0.0)
+ continue;
+
+ d = (float *)dst + oc;
+ s = (float *)src + ic;
+
+ if (vol >= 1.0) {
+ for (i = n; i > 0; i--, s += n_ic, d += n_oc)
+ *d += *s;
+ } else {
+ for (i = n; i > 0; i--, s += n_ic, d += n_oc)
+ *d += *s * vol;
+ }
+ }
+ }
+
+ break;
+ }
+ case PA_SAMPLE_S16NE:
+ {
+ int16_t *d, *s;
+
+ memset(dst, 0, n * sizeof (int16_t) * n_oc);
+
+ for (oc = 0; oc < n_oc; oc++) {
+
+ for (ic = 0; ic < n_ic; ic++) {
+ int32_t vol;
+
+ vol = m->map_table_i[oc][ic];
+
+ if (vol <= 0)
+ continue;
+
+ d = (int16_t *)dst + oc;
+ s = (int16_t *)src + ic;
+
+ if (vol >= 0x10000) {
+ for (i = n; i > 0; i--, s += n_ic, d += n_oc)
+ *d += *s;
+ } else {
+ for (i = n; i > 0; i--, s += n_ic, d += n_oc)
+ *d += (int16_t) (((int32_t)*s * vol) >> 16);
+ }
+ }
+ }
+ break;
+ }
+ default:
+ pa_assert_not_reached();
+ }
+}
+
+/* set the function that will execute the remapping based on the matrices */
+static void init_remap_c (pa_remap_t *m) {
+ unsigned n_oc, n_ic;
+
+ n_oc = m->o_ss->channels;
+ n_ic = m->i_ss->channels;
+
+ /* find some common channel remappings, fall back to full matrix operation. */
+ if (n_ic == 1 && n_oc == 2 &&
+ m->map_table_f[0][0] >= 1.0 && m->map_table_f[1][0] >= 1.0) {
+ m->do_remap = (pa_do_remap_func_t) remap_mono_to_stereo_c;
+ pa_log_info("Using mono to stereo remapping");
+ } else {
+ m->do_remap = (pa_do_remap_func_t) remap_channels_matrix_c;
+ pa_log_info("Using generic matrix remapping");
+ }
+}
+
+
+/* default C implementation */
+static pa_init_remap_func_t remap_func = init_remap_c;
+
+void pa_init_remap (pa_remap_t *m) {
+ pa_assert (remap_func);
+
+ m->do_remap = NULL;
+
+ /* call the installed remap init function */
+ remap_func (m);
+
+ if (m->do_remap == NULL) {
+ /* nothing was installed, fallback to C version */
+ init_remap_c (m);
+ }
+}
+
+pa_init_remap_func_t pa_get_init_remap_func(void) {
+ return remap_func;
+}
+
+void pa_set_init_remap_func(pa_init_remap_func_t func) {
+ remap_func = func;
+}
diff --git a/src/pulsecore/remap.h b/src/pulsecore/remap.h
new file mode 100644
index 00000000..32a67cdd
--- /dev/null
+++ b/src/pulsecore/remap.h
@@ -0,0 +1,48 @@
+#ifndef fooremapfoo
+#define fooremapfoo
+
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2004-2006 Lennart Poettering
+ Copyright 2009 Wim Taymans <wim.taymans@collabora.co.uk.com>
+
+ 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 <pulse/sample.h>
+
+typedef struct pa_remap pa_remap_t;
+
+typedef void (*pa_do_remap_func_t) (pa_remap_t *m, void *d, const void *s, unsigned n);
+
+struct pa_remap {
+ pa_sample_format_t *format;
+ pa_sample_spec *i_ss, *o_ss;
+ float map_table_f[PA_CHANNELS_MAX][PA_CHANNELS_MAX];
+ int32_t map_table_i[PA_CHANNELS_MAX][PA_CHANNELS_MAX];
+ pa_do_remap_func_t do_remap;
+};
+
+void pa_init_remap (pa_remap_t *m);
+
+/* custom installation of init functions */
+typedef void (*pa_init_remap_func_t) (pa_remap_t *m);
+
+pa_init_remap_func_t pa_get_init_remap_func(void);
+void pa_set_init_remap_func(pa_init_remap_func_t func);
+
+#endif /* fooremapfoo */
diff --git a/src/pulsecore/remap_mmx.c b/src/pulsecore/remap_mmx.c
new file mode 100644
index 00000000..d358a58b
--- /dev/null
+++ b/src/pulsecore/remap_mmx.c
@@ -0,0 +1,161 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2004-2006 Lennart Poettering
+ Copyright 2009 Wim Taymans <wim.taymans@collabora.co.uk.com>
+
+ 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 <string.h>
+
+#include <pulse/sample.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+
+#include "cpu-x86.h"
+#include "remap.h"
+
+#define LOAD_SAMPLES \
+ " movq (%1), %%mm0 \n\t" \
+ " movq 8(%1), %%mm2 \n\t" \
+ " movq 16(%1), %%mm4 \n\t" \
+ " movq 24(%1), %%mm6 \n\t" \
+ " movq %%mm0, %%mm1 \n\t" \
+ " movq %%mm2, %%mm3 \n\t" \
+ " movq %%mm4, %%mm5 \n\t" \
+ " movq %%mm6, %%mm7 \n\t"
+
+#define UNPACK_SAMPLES(s) \
+ " punpckl"#s" %%mm0, %%mm0 \n\t" \
+ " punpckh"#s" %%mm1, %%mm1 \n\t" \
+ " punpckl"#s" %%mm2, %%mm2 \n\t" \
+ " punpckh"#s" %%mm3, %%mm3 \n\t" \
+ " punpckl"#s" %%mm4, %%mm4 \n\t" \
+ " punpckh"#s" %%mm5, %%mm5 \n\t" \
+ " punpckl"#s" %%mm6, %%mm6 \n\t" \
+ " punpckh"#s" %%mm7, %%mm7 \n\t"
+
+#define STORE_SAMPLES \
+ " movq %%mm0, (%0) \n\t" \
+ " movq %%mm1, 8(%0) \n\t" \
+ " movq %%mm2, 16(%0) \n\t" \
+ " movq %%mm3, 24(%0) \n\t" \
+ " movq %%mm4, 32(%0) \n\t" \
+ " movq %%mm5, 40(%0) \n\t" \
+ " movq %%mm6, 48(%0) \n\t" \
+ " movq %%mm7, 56(%0) \n\t" \
+ " add $32, %1 \n\t" \
+ " add $64, %0 \n\t"
+
+#define HANDLE_SINGLE_dq() \
+ " movd (%1), %%mm0 \n\t" \
+ " punpckldq %%mm0, %%mm0 \n\t" \
+ " movq %%mm0, (%0) \n\t" \
+ " add $4, %1 \n\t" \
+ " add $8, %0 \n\t"
+
+#define HANDLE_SINGLE_wd() \
+ " movw (%1), %w3 \n\t" \
+ " movd %3, %%mm0 \n\t" \
+ " punpcklwd %%mm0, %%mm0 \n\t" \
+ " movd %%mm0, (%0) \n\t" \
+ " add $2, %1 \n\t" \
+ " add $4, %0 \n\t"
+
+#define MONO_TO_STEREO(s,shift,mask) \
+ " mov %4, %2 \n\t" \
+ " sar $"#shift", %2 \n\t" \
+ " cmp $0, %2 \n\t" \
+ " je 2f \n\t" \
+ "1: \n\t" \
+ LOAD_SAMPLES \
+ UNPACK_SAMPLES(s) \
+ STORE_SAMPLES \
+ " dec %2 \n\t" \
+ " jne 1b \n\t" \
+ "2: \n\t" \
+ " mov %4, %2 \n\t" \
+ " and $"#mask", %2 \n\t" \
+ " je 4f \n\t" \
+ "3: \n\t" \
+ HANDLE_SINGLE_##s() \
+ " dec %2 \n\t" \
+ " jne 3b \n\t" \
+ "4: \n\t" \
+ " emms \n\t"
+
+#if defined (__i386__) || defined (__amd64__)
+static void remap_mono_to_stereo_mmx (pa_remap_t *m, void *dst, const void *src, unsigned n) {
+ pa_reg_x86 temp, temp2;
+
+ switch (*m->format) {
+ case PA_SAMPLE_FLOAT32NE:
+ {
+ __asm__ __volatile__ (
+ MONO_TO_STEREO(dq,3,7) /* do doubles to quads */
+ : "+r" (dst), "+r" (src), "=&r" (temp), "=&r" (temp2)
+ : "r" ((pa_reg_x86)n)
+ : "cc"
+ );
+ break;
+ }
+ case PA_SAMPLE_S16NE:
+ {
+ __asm__ __volatile__ (
+ MONO_TO_STEREO(wd,4,15) /* do words to doubles */
+ : "+r" (dst), "+r" (src), "=&r" (temp), "=&r" (temp2)
+ : "r" ((pa_reg_x86)n)
+ : "cc"
+ );
+ break;
+ }
+ default:
+ pa_assert_not_reached();
+ }
+}
+
+/* set the function that will execute the remapping based on the matrices */
+static void init_remap_mmx (pa_remap_t *m) {
+ unsigned n_oc, n_ic;
+
+ n_oc = m->o_ss->channels;
+ n_ic = m->i_ss->channels;
+
+ /* find some common channel remappings, fall back to full matrix operation. */
+ if (n_ic == 1 && n_oc == 2 &&
+ m->map_table_f[0][0] >= 1.0 && m->map_table_f[1][0] >= 1.0) {
+ m->do_remap = (pa_do_remap_func_t) remap_mono_to_stereo_mmx;
+ pa_log_info("Using MMX mono to stereo remapping");
+ }
+}
+#endif /* defined (__i386__) || defined (__amd64__) */
+
+void pa_remap_func_init_mmx (pa_cpu_x86_flag_t flags) {
+#if defined (__i386__) || defined (__amd64__)
+
+ if (flags & PA_CPU_X86_MMX) {
+ pa_log_info("Initialising MMX optimized remappers.");
+
+ pa_set_init_remap_func ((pa_init_remap_func_t) init_remap_mmx);
+ }
+
+#endif /* defined (__i386__) || defined (__amd64__) */
+}
diff --git a/src/pulsecore/remap_sse.c b/src/pulsecore/remap_sse.c
new file mode 100644
index 00000000..0ccf3161
--- /dev/null
+++ b/src/pulsecore/remap_sse.c
@@ -0,0 +1,159 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2004-2006 Lennart Poettering
+ Copyright 2009 Wim Taymans <wim.taymans@collabora.co.uk.com>
+
+ 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 <string.h>
+
+#include <pulse/sample.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+
+#include "cpu-x86.h"
+#include "remap.h"
+
+#define LOAD_SAMPLES \
+ " movdqu (%1), %%xmm0 \n\t" \
+ " movdqu 16(%1), %%xmm2 \n\t" \
+ " movdqu 32(%1), %%xmm4 \n\t" \
+ " movdqu 48(%1), %%xmm6 \n\t" \
+ " movdqa %%xmm0, %%xmm1 \n\t" \
+ " movdqa %%xmm2, %%xmm3 \n\t" \
+ " movdqa %%xmm4, %%xmm5 \n\t" \
+ " movdqa %%xmm6, %%xmm7 \n\t"
+
+#define UNPACK_SAMPLES(s) \
+ " punpckl"#s" %%xmm0, %%xmm0 \n\t" \
+ " punpckh"#s" %%xmm1, %%xmm1 \n\t" \
+ " punpckl"#s" %%xmm2, %%xmm2 \n\t" \
+ " punpckh"#s" %%xmm3, %%xmm3 \n\t" \
+ " punpckl"#s" %%xmm4, %%xmm4 \n\t" \
+ " punpckh"#s" %%xmm5, %%xmm5 \n\t" \
+ " punpckl"#s" %%xmm6, %%xmm6 \n\t" \
+ " punpckh"#s" %%xmm7, %%xmm7 \n\t"
+
+#define STORE_SAMPLES \
+ " movdqu %%xmm0, (%0) \n\t" \
+ " movdqu %%xmm1, 16(%0) \n\t" \
+ " movdqu %%xmm2, 32(%0) \n\t" \
+ " movdqu %%xmm3, 48(%0) \n\t" \
+ " movdqu %%xmm4, 64(%0) \n\t" \
+ " movdqu %%xmm5, 80(%0) \n\t" \
+ " movdqu %%xmm6, 96(%0) \n\t" \
+ " movdqu %%xmm7, 112(%0) \n\t" \
+ " add $64, %1 \n\t" \
+ " add $128, %0 \n\t"
+
+#define HANDLE_SINGLE_dq() \
+ " movd (%1), %%xmm0 \n\t" \
+ " punpckldq %%xmm0, %%xmm0 \n\t" \
+ " movq %%xmm0, (%0) \n\t" \
+ " add $4, %1 \n\t" \
+ " add $8, %0 \n\t"
+
+#define HANDLE_SINGLE_wd() \
+ " movw (%1), %w3 \n\t" \
+ " movd %3, %%xmm0 \n\t" \
+ " punpcklwd %%xmm0, %%xmm0 \n\t" \
+ " movd %%xmm0, (%0) \n\t" \
+ " add $2, %1 \n\t" \
+ " add $4, %0 \n\t"
+
+#define MONO_TO_STEREO(s,shift,mask) \
+ " mov %4, %2 \n\t" \
+ " sar $"#shift", %2 \n\t" \
+ " cmp $0, %2 \n\t" \
+ " je 2f \n\t" \
+ "1: \n\t" \
+ LOAD_SAMPLES \
+ UNPACK_SAMPLES(s) \
+ STORE_SAMPLES \
+ " dec %2 \n\t" \
+ " jne 1b \n\t" \
+ "2: \n\t" \
+ " mov %4, %2 \n\t" \
+ " and $"#mask", %2 \n\t" \
+ " je 4f \n\t" \
+ "3: \n\t" \
+ HANDLE_SINGLE_##s() \
+ " dec %2 \n\t" \
+ " jne 3b \n\t" \
+ "4: \n\t"
+
+#if defined (__i386__) || defined (__amd64__)
+static void remap_mono_to_stereo_sse2 (pa_remap_t *m, void *dst, const void *src, unsigned n) {
+ pa_reg_x86 temp, temp2;
+
+ switch (*m->format) {
+ case PA_SAMPLE_FLOAT32NE:
+ {
+ __asm__ __volatile__ (
+ MONO_TO_STEREO(dq, 4, 15) /* do doubles to quads */
+ : "+r" (dst), "+r" (src), "=&r" (temp), "=&r" (temp2)
+ : "r" ((pa_reg_x86)n)
+ : "cc"
+ );
+ break;
+ }
+ case PA_SAMPLE_S16NE:
+ {
+ __asm__ __volatile__ (
+ MONO_TO_STEREO(wd, 5, 31) /* do words to doubles */
+ : "+r" (dst), "+r" (src), "=&r" (temp), "=&r" (temp2)
+ : "r" ((pa_reg_x86)n)
+ : "cc"
+ );
+ break;
+ }
+ default:
+ pa_assert_not_reached();
+ }
+}
+
+/* set the function that will execute the remapping based on the matrices */
+static void init_remap_sse2 (pa_remap_t *m) {
+ unsigned n_oc, n_ic;
+
+ n_oc = m->o_ss->channels;
+ n_ic = m->i_ss->channels;
+
+ /* find some common channel remappings, fall back to full matrix operation. */
+ if (n_ic == 1 && n_oc == 2 &&
+ m->map_table_f[0][0] >= 1.0 && m->map_table_f[1][0] >= 1.0) {
+ m->do_remap = (pa_do_remap_func_t) remap_mono_to_stereo_sse2;
+ pa_log_info("Using SSE mono to stereo remapping");
+ }
+}
+#endif /* defined (__i386__) || defined (__amd64__) */
+
+void pa_remap_func_init_sse (pa_cpu_x86_flag_t flags) {
+#if defined (__i386__) || defined (__amd64__)
+
+ if (flags & PA_CPU_X86_SSE2) {
+ pa_log_info("Initialising SSE2 optimized remappers.");
+ pa_set_init_remap_func ((pa_init_remap_func_t) init_remap_sse2);
+ }
+
+#endif /* defined (__i386__) || defined (__amd64__) */
+}
diff --git a/src/pulsecore/resampler.c b/src/pulsecore/resampler.c
index 17fb8480..bed5a20d 100644
--- a/src/pulsecore/resampler.c
+++ b/src/pulsecore/resampler.c
@@ -31,9 +31,6 @@
#include <speex/speex_resampler.h>
-#include <liboil/liboilfuncs.h>
-#include <liboil/liboil.h>
-
#include <pulse/xmalloc.h>
#include <pulsecore/sconv.h>
#include <pulsecore/log.h>
@@ -43,6 +40,7 @@
#include "ffmpeg/avcodec.h"
#include "resampler.h"
+#include "remap.h"
/* Number of samples of extra space we allow the resamplers to return */
#define EXTRA_FRAMES 128
@@ -64,7 +62,7 @@ struct pa_resampler {
pa_convert_func_t to_work_format_func;
pa_convert_func_t from_work_format_func;
- float map_table[PA_CHANNELS_MAX][PA_CHANNELS_MAX];
+ pa_remap_t remap;
pa_bool_t map_required;
void (*impl_free)(pa_resampler *r);
@@ -214,6 +212,11 @@ pa_resampler* pa_resampler_new(
r->i_ss = *a;
r->o_ss = *b;
+ /* set up the remap structure */
+ r->remap.i_ss = &r->i_ss;
+ r->remap.o_ss = &r->o_ss;
+ r->remap.format = &r->work_format;
+
if (am)
r->i_cm = *am;
else if (!pa_channel_map_init_auto(&r->i_cm, r->i_ss.channels, PA_CHANNEL_MAP_DEFAULT))
@@ -296,8 +299,7 @@ pa_resampler* pa_resampler_new(
return r;
fail:
- if (r)
- pa_xfree(r);
+ pa_xfree(r);
return NULL;
}
@@ -347,13 +349,17 @@ void pa_resampler_set_output_rate(pa_resampler *r, uint32_t rate) {
size_t pa_resampler_request(pa_resampler *r, size_t out_length) {
pa_assert(r);
- return (((out_length / r->o_fz)*r->i_ss.rate)/r->o_ss.rate) * r->i_fz;
+ /* Let's round up here */
+
+ return (((((out_length + r->o_fz-1) / r->o_fz) * r->i_ss.rate) + r->o_ss.rate-1) / r->o_ss.rate) * r->i_fz;
}
size_t pa_resampler_result(pa_resampler *r, size_t in_length) {
pa_assert(r);
- return (((in_length / r->i_fz)*r->o_ss.rate)/r->i_ss.rate) * r->o_fz;
+ /* Let's round up here */
+
+ return (((((in_length + r->i_fz-1) / r->i_fz) * r->o_ss.rate) + r->i_ss.rate-1) / r->i_ss.rate) * r->o_fz;
}
size_t pa_resampler_max_block_size(pa_resampler *r) {
@@ -576,32 +582,41 @@ static int front_rear_side(pa_channel_position_t p) {
static void calc_map_table(pa_resampler *r) {
unsigned oc, ic;
+ unsigned n_oc, n_ic;
pa_bool_t ic_connected[PA_CHANNELS_MAX];
pa_bool_t remix;
pa_strbuf *s;
char *t;
+ pa_remap_t *m;
pa_assert(r);
if (!(r->map_required = (r->i_ss.channels != r->o_ss.channels || (!(r->flags & PA_RESAMPLER_NO_REMAP) && !pa_channel_map_equal(&r->i_cm, &r->o_cm)))))
return;
- memset(r->map_table, 0, sizeof(r->map_table));
+ m = &r->remap;
+
+ n_oc = r->o_ss.channels;
+ n_ic = r->i_ss.channels;
+
+ memset(m->map_table_f, 0, sizeof(m->map_table_f));
+ memset(m->map_table_i, 0, sizeof(m->map_table_i));
+
memset(ic_connected, 0, sizeof(ic_connected));
remix = (r->flags & (PA_RESAMPLER_NO_REMAP|PA_RESAMPLER_NO_REMIX)) == 0;
- for (oc = 0; oc < r->o_ss.channels; oc++) {
+ for (oc = 0; oc < n_oc; oc++) {
pa_bool_t oc_connected = FALSE;
pa_channel_position_t b = r->o_cm.map[oc];
- for (ic = 0; ic < r->i_ss.channels; ic++) {
+ for (ic = 0; ic < n_ic; ic++) {
pa_channel_position_t a = r->i_cm.map[ic];
if (r->flags & PA_RESAMPLER_NO_REMAP) {
/* We shall not do any remapping. Hence, just check by index */
if (ic == oc)
- r->map_table[oc][ic] = 1.0;
+ m->map_table_f[oc][ic] = 1.0;
continue;
}
@@ -610,7 +625,7 @@ static void calc_map_table(pa_resampler *r) {
/* We shall not do any remixing. Hence, just check by name */
if (a == b)
- r->map_table[oc][ic] = 1.0;
+ m->map_table_f[oc][ic] = 1.0;
continue;
}
@@ -685,7 +700,7 @@ static void calc_map_table(pa_resampler *r) {
*/
if (a == b || a == PA_CHANNEL_POSITION_MONO || b == PA_CHANNEL_POSITION_MONO) {
- r->map_table[oc][ic] = 1.0;
+ m->map_table_f[oc][ic] = 1.0;
oc_connected = TRUE;
ic_connected[ic] = TRUE;
@@ -703,14 +718,14 @@ static void calc_map_table(pa_resampler *r) {
/* We are not connected and on the left side, let's
* average all left side input channels. */
- for (ic = 0; ic < r->i_ss.channels; ic++)
+ for (ic = 0; ic < n_ic; ic++)
if (on_left(r->i_cm.map[ic]))
n++;
if (n > 0)
- for (ic = 0; ic < r->i_ss.channels; ic++)
+ for (ic = 0; ic < n_ic; ic++)
if (on_left(r->i_cm.map[ic])) {
- r->map_table[oc][ic] = 1.0f / (float) n;
+ m->map_table_f[oc][ic] = 1.0f / (float) n;
ic_connected[ic] = TRUE;
}
@@ -724,14 +739,14 @@ static void calc_map_table(pa_resampler *r) {
/* We are not connected and on the right side, let's
* average all right side input channels. */
- for (ic = 0; ic < r->i_ss.channels; ic++)
+ for (ic = 0; ic < n_ic; ic++)
if (on_right(r->i_cm.map[ic]))
n++;
if (n > 0)
- for (ic = 0; ic < r->i_ss.channels; ic++)
+ for (ic = 0; ic < n_ic; ic++)
if (on_right(r->i_cm.map[ic])) {
- r->map_table[oc][ic] = 1.0f / (float) n;
+ m->map_table_f[oc][ic] = 1.0f / (float) n;
ic_connected[ic] = TRUE;
}
@@ -745,14 +760,14 @@ static void calc_map_table(pa_resampler *r) {
/* We are not connected and at the center. Let's
* average all center input channels. */
- for (ic = 0; ic < r->i_ss.channels; ic++)
+ for (ic = 0; ic < n_ic; ic++)
if (on_center(r->i_cm.map[ic]))
n++;
if (n > 0) {
- for (ic = 0; ic < r->i_ss.channels; ic++)
+ for (ic = 0; ic < n_ic; ic++)
if (on_center(r->i_cm.map[ic])) {
- r->map_table[oc][ic] = 1.0f / (float) n;
+ m->map_table_f[oc][ic] = 1.0f / (float) n;
ic_connected[ic] = TRUE;
}
} else {
@@ -762,14 +777,14 @@ static void calc_map_table(pa_resampler *r) {
n = 0;
- for (ic = 0; ic < r->i_ss.channels; ic++)
+ for (ic = 0; ic < n_ic; ic++)
if (on_left(r->i_cm.map[ic]) || on_right(r->i_cm.map[ic]))
n++;
if (n > 0)
- for (ic = 0; ic < r->i_ss.channels; ic++)
+ for (ic = 0; ic < n_ic; ic++)
if (on_left(r->i_cm.map[ic]) || on_right(r->i_cm.map[ic])) {
- r->map_table[oc][ic] = 1.0f / (float) n;
+ m->map_table_f[oc][ic] = 1.0f / (float) n;
ic_connected[ic] = TRUE;
}
@@ -783,12 +798,12 @@ static void calc_map_table(pa_resampler *r) {
/* We are not connected and an LFE. Let's average all
* channels for LFE. */
- for (ic = 0; ic < r->i_ss.channels; ic++) {
+ for (ic = 0; ic < n_ic; ic++) {
if (!(r->flags & PA_RESAMPLER_NO_LFE))
- r->map_table[oc][ic] = 1.0f / (float) r->i_ss.channels;
+ m->map_table_f[oc][ic] = 1.0f / (float) n_ic;
else
- r->map_table[oc][ic] = 0;
+ m->map_table_f[oc][ic] = 0;
/* Please note that a channel connected to LFE
* doesn't really count as connected. */
@@ -804,7 +819,7 @@ static void calc_map_table(pa_resampler *r) {
ic_unconnected_center = 0,
ic_unconnected_lfe = 0;
- for (ic = 0; ic < r->i_ss.channels; ic++) {
+ for (ic = 0; ic < n_ic; ic++) {
pa_channel_position_t a = r->i_cm.map[ic];
if (ic_connected[ic])
@@ -827,20 +842,20 @@ static void calc_map_table(pa_resampler *r) {
* the left side by .9 and add in our averaged unconnected
* channels multplied by .1 */
- for (oc = 0; oc < r->o_ss.channels; oc++) {
+ for (oc = 0; oc < n_oc; oc++) {
if (!on_left(r->o_cm.map[oc]))
continue;
- for (ic = 0; ic < r->i_ss.channels; ic++) {
+ for (ic = 0; ic < n_ic; ic++) {
if (ic_connected[ic]) {
- r->map_table[oc][ic] *= .9f;
+ m->map_table_f[oc][ic] *= .9f;
continue;
}
if (on_left(r->i_cm.map[ic]))
- r->map_table[oc][ic] = .1f / (float) ic_unconnected_left;
+ m->map_table_f[oc][ic] = .1f / (float) ic_unconnected_left;
}
}
}
@@ -852,20 +867,20 @@ static void calc_map_table(pa_resampler *r) {
* the right side by .9 and add in our averaged unconnected
* channels multplied by .1 */
- for (oc = 0; oc < r->o_ss.channels; oc++) {
+ for (oc = 0; oc < n_oc; oc++) {
if (!on_right(r->o_cm.map[oc]))
continue;
- for (ic = 0; ic < r->i_ss.channels; ic++) {
+ for (ic = 0; ic < n_ic; ic++) {
if (ic_connected[ic]) {
- r->map_table[oc][ic] *= .9f;
+ m->map_table_f[oc][ic] *= .9f;
continue;
}
if (on_right(r->i_cm.map[ic]))
- r->map_table[oc][ic] = .1f / (float) ic_unconnected_right;
+ m->map_table_f[oc][ic] = .1f / (float) ic_unconnected_right;
}
}
}
@@ -878,20 +893,20 @@ static void calc_map_table(pa_resampler *r) {
* the center side by .9 and add in our averaged unconnected
* channels multplied by .1 */
- for (oc = 0; oc < r->o_ss.channels; oc++) {
+ for (oc = 0; oc < n_oc; oc++) {
if (!on_center(r->o_cm.map[oc]))
continue;
- for (ic = 0; ic < r->i_ss.channels; ic++) {
+ for (ic = 0; ic < n_ic; ic++) {
if (ic_connected[ic]) {
- r->map_table[oc][ic] *= .9f;
+ m->map_table_f[oc][ic] *= .9f;
continue;
}
if (on_center(r->i_cm.map[ic])) {
- r->map_table[oc][ic] = .1f / (float) ic_unconnected_center;
+ m->map_table_f[oc][ic] = .1f / (float) ic_unconnected_center;
mixed_in = TRUE;
}
}
@@ -909,7 +924,7 @@ static void calc_map_table(pa_resampler *r) {
it into left and right. Using .375 and 0.75 as
factors. */
- for (ic = 0; ic < r->i_ss.channels; ic++) {
+ for (ic = 0; ic < n_ic; ic++) {
if (ic_connected[ic])
continue;
@@ -917,7 +932,7 @@ static void calc_map_table(pa_resampler *r) {
if (!on_center(r->i_cm.map[ic]))
continue;
- for (oc = 0; oc < r->o_ss.channels; oc++) {
+ for (oc = 0; oc < n_oc; oc++) {
if (!on_left(r->o_cm.map[oc]) && !on_right(r->o_cm.map[oc]))
continue;
@@ -928,7 +943,7 @@ static void calc_map_table(pa_resampler *r) {
}
}
- for (oc = 0; oc < r->o_ss.channels; oc++) {
+ for (oc = 0; oc < n_oc; oc++) {
if (!on_left(r->o_cm.map[oc]) && !on_right(r->o_cm.map[oc]))
continue;
@@ -938,7 +953,7 @@ static void calc_map_table(pa_resampler *r) {
}
}
- for (oc = 0; oc < r->o_ss.channels; oc++) {
+ for (oc = 0; oc < n_oc; oc++) {
if (!on_left(r->o_cm.map[oc]) && !on_right(r->o_cm.map[oc]))
continue;
@@ -946,10 +961,10 @@ static void calc_map_table(pa_resampler *r) {
if (ncenter[oc] <= 0)
continue;
- for (ic = 0; ic < r->i_ss.channels; ic++) {
+ for (ic = 0; ic < n_ic; ic++) {
if (ic_connected[ic]) {
- r->map_table[oc][ic] *= .75f;
+ m->map_table_f[oc][ic] *= .75f;
continue;
}
@@ -957,7 +972,7 @@ static void calc_map_table(pa_resampler *r) {
continue;
if (!found_frs[ic] || front_rear_side(r->i_cm.map[ic]) == front_rear_side(r->o_cm.map[oc]))
- r->map_table[oc][ic] = .375f / (float) ncenter[oc];
+ m->map_table_f[oc][ic] = .375f / (float) ncenter[oc];
}
}
}
@@ -968,40 +983,46 @@ static void calc_map_table(pa_resampler *r) {
/* OK, so there is an unconnected LFE channel. Let's mix
* it into all channels, with factor 0.375 */
- for (ic = 0; ic < r->i_ss.channels; ic++) {
+ for (ic = 0; ic < n_ic; ic++) {
if (!on_lfe(r->i_cm.map[ic]))
continue;
- for (oc = 0; oc < r->o_ss.channels; oc++)
- r->map_table[oc][ic] = 0.375f / (float) ic_unconnected_lfe;
+ for (oc = 0; oc < n_oc; oc++)
+ m->map_table_f[oc][ic] = 0.375f / (float) ic_unconnected_lfe;
}
}
}
-
+ /* make an 16:16 int version of the matrix */
+ for (oc = 0; oc < n_oc; oc++)
+ for (ic = 0; ic < n_ic; ic++)
+ m->map_table_i[oc][ic] = (int32_t) (m->map_table_f[oc][ic] * 0x10000);
s = pa_strbuf_new();
pa_strbuf_printf(s, " ");
- for (ic = 0; ic < r->i_ss.channels; ic++)
+ for (ic = 0; ic < n_ic; ic++)
pa_strbuf_printf(s, " I%02u ", ic);
pa_strbuf_puts(s, "\n +");
- for (ic = 0; ic < r->i_ss.channels; ic++)
+ for (ic = 0; ic < n_ic; ic++)
pa_strbuf_printf(s, "------");
pa_strbuf_puts(s, "\n");
- for (oc = 0; oc < r->o_ss.channels; oc++) {
+ for (oc = 0; oc < n_oc; oc++) {
pa_strbuf_printf(s, "O%02u |", oc);
- for (ic = 0; ic < r->i_ss.channels; ic++)
- pa_strbuf_printf(s, " %1.3f", r->map_table[oc][ic]);
+ for (ic = 0; ic < n_ic; ic++)
+ pa_strbuf_printf(s, " %1.3f", m->map_table_f[oc][ic]);
pa_strbuf_puts(s, "\n");
}
pa_log_debug("Channel matrix:\n%s", t = pa_strbuf_tostring_free(s));
pa_xfree(t);
+
+ /* initialize the remapping function */
+ pa_init_remap (m);
}
static pa_memchunk* convert_to_work_format(pa_resampler *r, pa_memchunk *input) {
@@ -1041,41 +1062,10 @@ static pa_memchunk* convert_to_work_format(pa_resampler *r, pa_memchunk *input)
return &r->buf1;
}
-static void vectoradd_s16_with_fraction(
- int16_t *d, int dstr,
- const int16_t *s1, int sstr1,
- const int16_t *s2, int sstr2,
- int n,
- float s3, float s4) {
-
- int32_t i3, i4;
-
- i3 = (int32_t) (s3 * 0x10000);
- i4 = (int32_t) (s4 * 0x10000);
-
- for (; n > 0; n--) {
- int32_t a, b;
-
- a = *s1;
- b = *s2;
-
- a = (a * i3) / 0x10000;
- b = (b * i4) / 0x10000;
-
- *d = (int16_t) (a + b);
-
- s1 = (const int16_t*) ((const uint8_t*) s1 + sstr1);
- s2 = (const int16_t*) ((const uint8_t*) s2 + sstr2);
- d = (int16_t*) ((uint8_t*) d + dstr);
-
- }
-}
-
static pa_memchunk *remap_channels(pa_resampler *r, pa_memchunk *input) {
unsigned in_n_samples, out_n_samples, n_frames;
- int i_skip, o_skip;
- unsigned oc;
void *src, *dst;
+ pa_remap_t *remap;
pa_assert(r);
pa_assert(input);
@@ -1104,76 +1094,14 @@ static pa_memchunk *remap_channels(pa_resampler *r, pa_memchunk *input) {
src = ((uint8_t*) pa_memblock_acquire(input->memblock) + input->index);
dst = pa_memblock_acquire(r->buf2.memblock);
- memset(dst, 0, r->buf2.length);
-
- o_skip = (int) (r->w_sz * r->o_ss.channels);
- i_skip = (int) (r->w_sz * r->i_ss.channels);
-
- switch (r->work_format) {
- case PA_SAMPLE_FLOAT32NE:
-
- for (oc = 0; oc < r->o_ss.channels; oc++) {
- unsigned ic;
- static const float one = 1.0;
+ remap = &r->remap;
- for (ic = 0; ic < r->i_ss.channels; ic++) {
-
- if (r->map_table[oc][ic] <= 0.0)
- continue;
-
- oil_vectoradd_f32(
- (float*) dst + oc, o_skip,
- (float*) dst + oc, o_skip,
- (float*) src + ic, i_skip,
- (int) n_frames,
- &one, &r->map_table[oc][ic]);
- }
- }
-
- break;
-
- case PA_SAMPLE_S16NE:
-
- for (oc = 0; oc < r->o_ss.channels; oc++) {
- unsigned ic;
-
- for (ic = 0; ic < r->i_ss.channels; ic++) {
-
- if (r->map_table[oc][ic] <= 0.0)
- continue;
-
- if (r->map_table[oc][ic] >= 1.0) {
- static const int16_t one = 1;
-
- oil_vectoradd_s16(
- (int16_t*) dst + oc, o_skip,
- (int16_t*) dst + oc, o_skip,
- (int16_t*) src + ic, i_skip,
- (int) n_frames,
- &one, &one);
-
- } else
-
- vectoradd_s16_with_fraction(
- (int16_t*) dst + oc, o_skip,
- (int16_t*) dst + oc, o_skip,
- (int16_t*) src + ic, i_skip,
- (int) n_frames,
- 1.0f, r->map_table[oc][ic]);
- }
- }
-
- break;
-
- default:
- pa_assert_not_reached();
- }
+ pa_assert (remap->do_remap);
+ remap->do_remap (remap, dst, src, n_frames);
pa_memblock_release(input->memblock);
pa_memblock_release(r->buf2.memblock);
- r->buf2.length = out_n_samples * r->w_sz;
-
return &r->buf2;
}
@@ -1465,7 +1393,7 @@ static void trivial_resample(pa_resampler *r, const pa_memchunk *input, unsigned
pa_assert(o_index * fz < pa_memblock_get_length(output->memblock));
- oil_memcpy((uint8_t*) dst + fz * o_index,
+ memcpy((uint8_t*) dst + fz * o_index,
(uint8_t*) src + fz * j, (int) fz);
}
diff --git a/src/pulsecore/rtpoll.c b/src/pulsecore/rtpoll.c
index 42708a8a..666cbc98 100644
--- a/src/pulsecore/rtpoll.c
+++ b/src/pulsecore/rtpoll.c
@@ -63,6 +63,7 @@ struct pa_rtpoll {
pa_bool_t running:1;
pa_bool_t rebuild_needed:1;
pa_bool_t quit:1;
+ pa_bool_t timer_elapsed:1;
#ifdef DEBUG_TIMING
pa_usec_t timestamp;
@@ -94,26 +95,14 @@ PA_STATIC_FLIST_DECLARE(items, 0, pa_xfree);
pa_rtpoll *pa_rtpoll_new(void) {
pa_rtpoll *p;
- p = pa_xnew(pa_rtpoll, 1);
+ p = pa_xnew0(pa_rtpoll, 1);
p->n_pollfd_alloc = 32;
p->pollfd = pa_xnew(struct pollfd, p->n_pollfd_alloc);
p->pollfd2 = pa_xnew(struct pollfd, p->n_pollfd_alloc);
- p->n_pollfd_used = 0;
-
- pa_zero(p->next_elapse);
- p->timer_enabled = FALSE;
-
- p->running = FALSE;
- p->scan_for_dead = FALSE;
- p->rebuild_needed = FALSE;
- p->quit = FALSE;
-
- PA_LLIST_HEAD_INIT(pa_rtpoll_item, p->items);
#ifdef DEBUG_TIMING
p->timestamp = pa_rtclock_now();
- p->slept = p->awake = 0;
#endif
return p;
@@ -229,6 +218,7 @@ int pa_rtpoll_run(pa_rtpoll *p, pa_bool_t wait_op) {
pa_assert(!p->running);
p->running = TRUE;
+ p->timer_elapsed = FALSE;
/* First, let's do some work */
for (i = p->items; i && i->priority < PA_RTPOLL_NEVER; i = i->next) {
@@ -286,7 +276,7 @@ int pa_rtpoll_run(pa_rtpoll *p, pa_bool_t wait_op) {
if (p->rebuild_needed)
rtpoll_rebuild(p);
- memset(&timeout, 0, sizeof(timeout));
+ pa_zero(timeout);
/* Calculate timeout */
if (wait_op && !p->quit && p->timer_enabled) {
@@ -314,9 +304,11 @@ int pa_rtpoll_run(pa_rtpoll *p, pa_bool_t wait_op) {
r = ppoll(p->pollfd, p->n_pollfd_used, (!wait_op || p->quit || p->timer_enabled) ? &ts : NULL, NULL);
}
#else
- r = poll(p->pollfd, p->n_pollfd_used, (!wait_op || p->quit || p->timer_enabled) ? (int) ((timeout.tv_sec*1000) + (timeout.tv_usec / 1000)) : -1);
+ r = poll(p->pollfd, p->n_pollfd_used, (!wait_op || p->quit || p->timer_enabled) ? (int) ((timeout.tv_sec*1000) + (timeout.tv_usec / 1000)) : -1);
#endif
+ p->timer_elapsed = r == 0;
+
#ifdef DEBUG_TIMING
{
pa_usec_t now = pa_rtclock_now();
@@ -628,3 +620,9 @@ void pa_rtpoll_quit(pa_rtpoll *p) {
p->quit = TRUE;
}
+
+pa_bool_t pa_rtpoll_timer_elapsed(pa_rtpoll *p) {
+ pa_assert(p);
+
+ return p->timer_elapsed;
+}
diff --git a/src/pulsecore/rtpoll.h b/src/pulsecore/rtpoll.h
index d2d69cad..b2a87fca 100644
--- a/src/pulsecore/rtpoll.h
+++ b/src/pulsecore/rtpoll.h
@@ -73,6 +73,10 @@ void pa_rtpoll_set_timer_absolute(pa_rtpoll *p, pa_usec_t usec);
void pa_rtpoll_set_timer_relative(pa_rtpoll *p, pa_usec_t usec);
void pa_rtpoll_set_timer_disabled(pa_rtpoll *p);
+/* Return TRUE when the elapsed timer was the reason for
+ * the last pa_rtpoll_run() invocation to finish */
+pa_bool_t pa_rtpoll_timer_elapsed(pa_rtpoll *p);
+
/* A new fd wakeup item for pa_rtpoll */
pa_rtpoll_item *pa_rtpoll_item_new(pa_rtpoll *p, pa_rtpoll_priority_t prio, unsigned n_fds);
void pa_rtpoll_item_free(pa_rtpoll_item *i);
diff --git a/src/pulsecore/sample-util.c b/src/pulsecore/sample-util.c
index 5b8ccf59..74600dec 100644
--- a/src/pulsecore/sample-util.c
+++ b/src/pulsecore/sample-util.c
@@ -30,9 +30,6 @@
#include <stdio.h>
#include <errno.h>
-#include <liboil/liboilfuncs.h>
-#include <liboil/liboil.h>
-
#include <pulse/timeval.h>
#include <pulsecore/log.h>
@@ -106,29 +103,41 @@ void* pa_silence_memory(void *p, size_t length, const pa_sample_spec *spec) {
return p;
}
+#define VOLUME_PADDING 32
+
static void calc_linear_integer_volume(int32_t linear[], const pa_cvolume *volume) {
- unsigned channel;
+ unsigned channel, nchannels, padding;
pa_assert(linear);
pa_assert(volume);
- for (channel = 0; channel < volume->channels; channel++)
+ nchannels = volume->channels;
+
+ for (channel = 0; channel < nchannels; channel++)
linear[channel] = (int32_t) lrint(pa_sw_volume_to_linear(volume->values[channel]) * 0x10000);
+
+ for (padding = 0; padding < VOLUME_PADDING; padding++, channel++)
+ linear[channel] = linear[padding];
}
static void calc_linear_float_volume(float linear[], const pa_cvolume *volume) {
- unsigned channel;
+ unsigned channel, nchannels, padding;
pa_assert(linear);
pa_assert(volume);
- for (channel = 0; channel < volume->channels; channel++)
+ nchannels = volume->channels;
+
+ for (channel = 0; channel < nchannels; channel++)
linear[channel] = (float) pa_sw_volume_to_linear(volume->values[channel]);
+
+ for (padding = 0; padding < VOLUME_PADDING; padding++, channel++)
+ linear[channel] = linear[padding];
}
static void calc_linear_integer_stream_volumes(pa_mix_info streams[], unsigned nstreams, const pa_cvolume *volume, const pa_sample_spec *spec) {
unsigned k, channel;
- float linear[PA_CHANNELS_MAX];
+ float linear[PA_CHANNELS_MAX + VOLUME_PADDING];
pa_assert(streams);
pa_assert(spec);
@@ -147,7 +156,7 @@ static void calc_linear_integer_stream_volumes(pa_mix_info streams[], unsigned n
static void calc_linear_float_stream_volumes(pa_mix_info streams[], unsigned nstreams, const pa_cvolume *volume, const pa_sample_spec *spec) {
unsigned k, channel;
- float linear[PA_CHANNELS_MAX];
+ float linear[PA_CHANNELS_MAX + VOLUME_PADDING];
pa_assert(streams);
pa_assert(spec);
@@ -690,6 +699,28 @@ size_t pa_mix(
return length;
}
+typedef union {
+ float f;
+ uint32_t i;
+} volume_val;
+
+typedef void (*pa_calc_volume_func_t) (void *volumes, const pa_cvolume *volume);
+
+static const pa_calc_volume_func_t calc_volume_table[] = {
+ [PA_SAMPLE_U8] = (pa_calc_volume_func_t) calc_linear_integer_volume,
+ [PA_SAMPLE_ALAW] = (pa_calc_volume_func_t) calc_linear_integer_volume,
+ [PA_SAMPLE_ULAW] = (pa_calc_volume_func_t) calc_linear_integer_volume,
+ [PA_SAMPLE_S16LE] = (pa_calc_volume_func_t) calc_linear_integer_volume,
+ [PA_SAMPLE_S16BE] = (pa_calc_volume_func_t) calc_linear_integer_volume,
+ [PA_SAMPLE_FLOAT32LE] = (pa_calc_volume_func_t) calc_linear_float_volume,
+ [PA_SAMPLE_FLOAT32BE] = (pa_calc_volume_func_t) calc_linear_float_volume,
+ [PA_SAMPLE_S32LE] = (pa_calc_volume_func_t) calc_linear_integer_volume,
+ [PA_SAMPLE_S32BE] = (pa_calc_volume_func_t) calc_linear_integer_volume,
+ [PA_SAMPLE_S24LE] = (pa_calc_volume_func_t) calc_linear_integer_volume,
+ [PA_SAMPLE_S24BE] = (pa_calc_volume_func_t) calc_linear_integer_volume,
+ [PA_SAMPLE_S24_32LE] = (pa_calc_volume_func_t) calc_linear_integer_volume,
+ [PA_SAMPLE_S24_32BE] = (pa_calc_volume_func_t) calc_linear_integer_volume
+};
void pa_volume_memchunk(
pa_memchunk*c,
@@ -697,6 +728,8 @@ void pa_volume_memchunk(
const pa_cvolume *volume) {
void *ptr;
+ volume_val linear[PA_CHANNELS_MAX + VOLUME_PADDING];
+ pa_do_volume_func_t do_volume;
pa_assert(c);
pa_assert(spec);
@@ -714,337 +747,19 @@ void pa_volume_memchunk(
return;
}
- ptr = (uint8_t*) pa_memblock_acquire(c->memblock) + c->index;
-
- switch (spec->format) {
-
- case PA_SAMPLE_S16NE: {
- int16_t *d, *e;
- unsigned channel;
- int32_t linear[PA_CHANNELS_MAX];
-
- calc_linear_integer_volume(linear, volume);
-
- e = (int16_t*) ptr + c->length/sizeof(int16_t);
-
- for (channel = 0, d = ptr; d < e; d++) {
- int32_t t, hi, lo;
-
- /* Multiplying the 32bit volume factor with the 16bit
- * sample might result in an 48bit value. We want to
- * do without 64 bit integers and hence do the
- * multiplication independantly for the HI and LO part
- * of the volume. */
-
- hi = linear[channel] >> 16;
- lo = linear[channel] & 0xFFFF;
-
- t = (int32_t)(*d);
- t = ((t * lo) >> 16) + (t * hi);
- t = PA_CLAMP_UNLIKELY(t, -0x8000, 0x7FFF);
- *d = (int16_t) t;
-
- if (PA_UNLIKELY(++channel >= spec->channels))
- channel = 0;
- }
-
- break;
- }
-
- case PA_SAMPLE_S16RE: {
- int16_t *d, *e;
- unsigned channel;
- int32_t linear[PA_CHANNELS_MAX];
-
- calc_linear_integer_volume(linear, volume);
-
- e = (int16_t*) ptr + c->length/sizeof(int16_t);
-
- for (channel = 0, d = ptr; d < e; d++) {
- int32_t t, hi, lo;
-
- hi = linear[channel] >> 16;
- lo = linear[channel] & 0xFFFF;
-
- t = (int32_t) PA_INT16_SWAP(*d);
- t = ((t * lo) >> 16) + (t * hi);
- t = PA_CLAMP_UNLIKELY(t, -0x8000, 0x7FFF);
- *d = PA_INT16_SWAP((int16_t) t);
-
- if (PA_UNLIKELY(++channel >= spec->channels))
- channel = 0;
- }
-
- break;
- }
-
- case PA_SAMPLE_S32NE: {
- int32_t *d, *e;
- unsigned channel;
- int32_t linear[PA_CHANNELS_MAX];
-
- calc_linear_integer_volume(linear, volume);
-
- e = (int32_t*) ptr + c->length/sizeof(int32_t);
-
- for (channel = 0, d = ptr; d < e; d++) {
- int64_t t;
-
- t = (int64_t)(*d);
- t = (t * linear[channel]) >> 16;
- t = PA_CLAMP_UNLIKELY(t, -0x80000000LL, 0x7FFFFFFFLL);
- *d = (int32_t) t;
-
- if (PA_UNLIKELY(++channel >= spec->channels))
- channel = 0;
- }
- break;
- }
-
- case PA_SAMPLE_S32RE: {
- int32_t *d, *e;
- unsigned channel;
- int32_t linear[PA_CHANNELS_MAX];
-
- calc_linear_integer_volume(linear, volume);
-
- e = (int32_t*) ptr + c->length/sizeof(int32_t);
-
- for (channel = 0, d = ptr; d < e; d++) {
- int64_t t;
-
- t = (int64_t) PA_INT32_SWAP(*d);
- t = (t * linear[channel]) >> 16;
- t = PA_CLAMP_UNLIKELY(t, -0x80000000LL, 0x7FFFFFFFLL);
- *d = PA_INT32_SWAP((int32_t) t);
-
- if (PA_UNLIKELY(++channel >= spec->channels))
- channel = 0;
- }
- break;
- }
-
- case PA_SAMPLE_S24NE: {
- uint8_t *d, *e;
- unsigned channel;
- int32_t linear[PA_CHANNELS_MAX];
-
- calc_linear_integer_volume(linear, volume);
-
- e = (uint8_t*) ptr + c->length;
-
- for (channel = 0, d = ptr; d < e; d += 3) {
- int64_t t;
-
- t = (int64_t)((int32_t) (PA_READ24NE(d) << 8));
- t = (t * linear[channel]) >> 16;
- t = PA_CLAMP_UNLIKELY(t, -0x80000000LL, 0x7FFFFFFFLL);
- PA_WRITE24NE(d, ((uint32_t) (int32_t) t) >> 8);
-
- if (PA_UNLIKELY(++channel >= spec->channels))
- channel = 0;
- }
- break;
- }
-
- case PA_SAMPLE_S24RE: {
- uint8_t *d, *e;
- unsigned channel;
- int32_t linear[PA_CHANNELS_MAX];
-
- calc_linear_integer_volume(linear, volume);
-
- e = (uint8_t*) ptr + c->length;
-
- for (channel = 0, d = ptr; d < e; d += 3) {
- int64_t t;
-
- t = (int64_t)((int32_t) (PA_READ24RE(d) << 8));
- t = (t * linear[channel]) >> 16;
- t = PA_CLAMP_UNLIKELY(t, -0x80000000LL, 0x7FFFFFFFLL);
- PA_WRITE24RE(d, ((uint32_t) (int32_t) t) >> 8);
-
- if (PA_UNLIKELY(++channel >= spec->channels))
- channel = 0;
- }
- break;
- }
-
- case PA_SAMPLE_S24_32NE: {
- uint32_t *d, *e;
- unsigned channel;
- int32_t linear[PA_CHANNELS_MAX];
-
- calc_linear_integer_volume(linear, volume);
-
- e = (uint32_t*) ptr + c->length/sizeof(uint32_t);
-
- for (channel = 0, d = ptr; d < e; d++) {
- int64_t t;
-
- t = (int64_t) ((int32_t) (*d << 8));
- t = (t * linear[channel]) >> 16;
- t = PA_CLAMP_UNLIKELY(t, -0x80000000LL, 0x7FFFFFFFLL);
- *d = ((uint32_t) ((int32_t) t)) >> 8;
-
- if (PA_UNLIKELY(++channel >= spec->channels))
- channel = 0;
- }
- break;
- }
-
- case PA_SAMPLE_S24_32RE: {
- uint32_t *d, *e;
- unsigned channel;
- int32_t linear[PA_CHANNELS_MAX];
-
- calc_linear_integer_volume(linear, volume);
-
- e = (uint32_t*) ptr + c->length/sizeof(uint32_t);
-
- for (channel = 0, d = ptr; d < e; d++) {
- int64_t t;
-
- t = (int64_t) ((int32_t) (PA_UINT32_SWAP(*d) << 8));
- t = (t * linear[channel]) >> 16;
- t = PA_CLAMP_UNLIKELY(t, -0x80000000LL, 0x7FFFFFFFLL);
- *d = PA_UINT32_SWAP(((uint32_t) ((int32_t) t)) >> 8);
-
- if (PA_UNLIKELY(++channel >= spec->channels))
- channel = 0;
- }
- break;
- }
-
- case PA_SAMPLE_U8: {
- uint8_t *d, *e;
- unsigned channel;
- int32_t linear[PA_CHANNELS_MAX];
-
- calc_linear_integer_volume(linear, volume);
-
- e = (uint8_t*) ptr + c->length;
-
- for (channel = 0, d = ptr; d < e; d++) {
- int32_t t, hi, lo;
-
- hi = linear[channel] >> 16;
- lo = linear[channel] & 0xFFFF;
-
- t = (int32_t) *d - 0x80;
- t = ((t * lo) >> 16) + (t * hi);
- t = PA_CLAMP_UNLIKELY(t, -0x80, 0x7F);
- *d = (uint8_t) (t + 0x80);
-
- if (PA_UNLIKELY(++channel >= spec->channels))
- channel = 0;
- }
- break;
- }
-
- case PA_SAMPLE_ULAW: {
- uint8_t *d, *e;
- unsigned channel;
- int32_t linear[PA_CHANNELS_MAX];
-
- calc_linear_integer_volume(linear, volume);
-
- e = (uint8_t*) ptr + c->length;
-
- for (channel = 0, d = ptr; d < e; d++) {
- int32_t t, hi, lo;
-
- hi = linear[channel] >> 16;
- lo = linear[channel] & 0xFFFF;
-
- t = (int32_t) st_ulaw2linear16(*d);
- t = ((t * lo) >> 16) + (t * hi);
- t = PA_CLAMP_UNLIKELY(t, -0x8000, 0x7FFF);
- *d = (uint8_t) st_14linear2ulaw((int16_t) t >> 2);
-
- if (PA_UNLIKELY(++channel >= spec->channels))
- channel = 0;
- }
- break;
- }
-
- case PA_SAMPLE_ALAW: {
- uint8_t *d, *e;
- unsigned channel;
- int32_t linear[PA_CHANNELS_MAX];
-
- calc_linear_integer_volume(linear, volume);
-
- e = (uint8_t*) ptr + c->length;
-
- for (channel = 0, d = ptr; d < e; d++) {
- int32_t t, hi, lo;
-
- hi = linear[channel] >> 16;
- lo = linear[channel] & 0xFFFF;
-
- t = (int32_t) st_alaw2linear16(*d);
- t = ((t * lo) >> 16) + (t * hi);
- t = PA_CLAMP_UNLIKELY(t, -0x8000, 0x7FFF);
- *d = (uint8_t) st_13linear2alaw((int16_t) t >> 3);
-
- if (PA_UNLIKELY(++channel >= spec->channels))
- channel = 0;
- }
- break;
- }
-
- case PA_SAMPLE_FLOAT32NE: {
- float *d;
- int skip;
- unsigned n;
- unsigned channel;
-
- d = ptr;
- skip = (int) (spec->channels * sizeof(float));
- n = (unsigned) (c->length/sizeof(float)/spec->channels);
-
- for (channel = 0; channel < spec->channels; channel ++) {
- float v, *t;
-
- if (PA_UNLIKELY(volume->values[channel] == PA_VOLUME_NORM))
- continue;
-
- v = (float) pa_sw_volume_to_linear(volume->values[channel]);
- t = d + channel;
- oil_scalarmult_f32(t, skip, t, skip, &v, (int) n);
- }
- break;
- }
-
- case PA_SAMPLE_FLOAT32RE: {
- float *d, *e;
- unsigned channel;
- float linear[PA_CHANNELS_MAX];
-
- calc_linear_float_volume(linear, volume);
-
- e = (float*) ptr + c->length/sizeof(float);
-
- for (channel = 0, d = ptr; d < e; d++) {
- float t;
+ if (spec->format < 0 || spec->format > PA_SAMPLE_MAX) {
+ pa_log_warn(" Unable to change volume of format %s.", pa_sample_format_to_string(spec->format));
+ return;
+ }
- t = PA_FLOAT32_SWAP(*d);
- t *= linear[channel];
- *d = PA_FLOAT32_SWAP(t);
+ do_volume = pa_get_volume_func (spec->format);
+ pa_assert(do_volume);
- if (PA_UNLIKELY(++channel >= spec->channels))
- channel = 0;
- }
-
- break;
- }
+ calc_volume_table[spec->format] ((void *)linear, volume);
+ ptr = (uint8_t*) pa_memblock_acquire(c->memblock) + c->index;
- default:
- pa_log_warn(" Unable to change volume of format %s.", pa_sample_format_to_string(spec->format));
- /* If we cannot change the volume, we just don't do it */
- }
+ do_volume (ptr, (void *)linear, spec->channels, c->length);
pa_memblock_release(c->memblock);
}
@@ -1090,7 +805,7 @@ void pa_interleave(const void *src[], unsigned channels, void *dst, size_t ss, u
d = (uint8_t*) dst + c * ss;
for (j = 0; j < n; j ++) {
- oil_memcpy(d, s, (int) ss);
+ memcpy(d, s, (int) ss);
s = (uint8_t*) s + ss;
d = (uint8_t*) d + fs;
}
@@ -1118,7 +833,7 @@ void pa_deinterleave(const void *src, void *dst[], unsigned channels, size_t ss,
d = dst[c];
for (j = 0; j < n; j ++) {
- oil_memcpy(d, s, (int) ss);
+ memcpy(d, s, (int) ss);
s = (uint8_t*) s + fs;
d = (uint8_t*) d + ss;
}
@@ -1227,10 +942,15 @@ void pa_sample_clamp(pa_sample_format_t format, void *dst, size_t dstr, const vo
s = src; d = dst;
if (format == PA_SAMPLE_FLOAT32NE) {
+ for (; n > 0; n--) {
+ float f;
- float minus_one = -1.0, plus_one = 1.0;
- oil_clip_f32(d, (int) dstr, s, (int) sstr, (int) n, &minus_one, &plus_one);
+ f = *s;
+ *d = PA_CLAMP_UNLIKELY(f, -1.0f, 1.0f);
+ s = (const float*) ((const uint8_t*) s + sstr);
+ d = (float*) ((uint8_t*) d + dstr);
+ }
} else {
pa_assert(format == PA_SAMPLE_FLOAT32RE);
@@ -1287,7 +1007,7 @@ void pa_memchunk_dump_to_file(pa_memchunk *c, const char *fn) {
/* Only for debugging purposes */
- f = fopen(fn, "a");
+ f = pa_fopen_cloexec(fn, "a");
if (!f) {
pa_log_warn("Failed to open '%s': %s", fn, pa_cstrerror(errno));
@@ -1336,3 +1056,13 @@ void pa_memchunk_sine(pa_memchunk *c, pa_mempool *pool, unsigned rate, unsigned
calc_sine(p, c->length, freq * l / rate);
pa_memblock_release(c->memblock);
}
+
+size_t pa_convert_size(size_t size, const pa_sample_spec *from, const pa_sample_spec *to) {
+ pa_usec_t usec;
+
+ pa_assert(from);
+ pa_assert(to);
+
+ usec = pa_bytes_to_usec_round_up(size, from);
+ return pa_usec_to_bytes_round_up(usec, to);
+}
diff --git a/src/pulsecore/sample-util.h b/src/pulsecore/sample-util.h
index 6a306c11..d0235d60 100644
--- a/src/pulsecore/sample-util.h
+++ b/src/pulsecore/sample-util.h
@@ -86,6 +86,13 @@ void pa_memchunk_dump_to_file(pa_memchunk *c, const char *fn);
void pa_memchunk_sine(pa_memchunk *c, pa_mempool *pool, unsigned rate, unsigned freq);
+typedef void (*pa_do_volume_func_t) (void *samples, void *volumes, unsigned channels, unsigned length);
+
+pa_do_volume_func_t pa_get_volume_func(pa_sample_format_t f);
+void pa_set_volume_func(pa_sample_format_t f, pa_do_volume_func_t func);
+
+size_t pa_convert_size(size_t size, const pa_sample_spec *from, const pa_sample_spec *to);
+
#define PA_CHANNEL_POSITION_MASK_LEFT \
(PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_LEFT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_REAR_LEFT) \
diff --git a/src/pulsecore/sconv-s16le.c b/src/pulsecore/sconv-s16le.c
index 43b8cb3e..0fefdf1c 100644
--- a/src/pulsecore/sconv-s16le.c
+++ b/src/pulsecore/sconv-s16le.c
@@ -28,8 +28,6 @@
#include <inttypes.h>
#include <stdio.h>
-#include <liboil/liboilfuncs.h>
-
#include <pulsecore/sconv.h>
#include <pulsecore/macro.h>
#include <pulsecore/log.h>
@@ -86,17 +84,13 @@ void pa_sconv_s16le_to_float32ne(unsigned n, const int16_t *a, float *b) {
pa_assert(b);
#if SWAP_WORDS == 1
-
for (; n > 0; n--) {
int16_t s = *(a++);
*(b++) = ((float) INT16_FROM(s))/(float) 0x7FFF;
}
-
#else
-{
- static const double add = 0, factor = 1.0/0x7FFF;
- oil_scaleconv_f32_s16(b, a, (int) n, &add, &factor);
-}
+ for (; n > 0; n--)
+ *(b++) = ((float) (*(a++)))/(float) 0x7FFF;
#endif
}
@@ -105,17 +99,13 @@ void pa_sconv_s32le_to_float32ne(unsigned n, const int32_t *a, float *b) {
pa_assert(b);
#if SWAP_WORDS == 1
-
for (; n > 0; n--) {
int32_t s = *(a++);
*(b++) = (float) (((double) INT32_FROM(s))/0x7FFFFFFF);
}
-
#else
-{
- static const double add = 0, factor = 1.0/0x7FFFFFFF;
- oil_scaleconv_f32_s32(b, a, (int) n, &add, &factor);
-}
+ for (; n > 0; n--)
+ *(b++) = (float) (((double) (*(a++)))/0x7FFFFFFF);
#endif
}
@@ -124,7 +114,6 @@ void pa_sconv_s16le_from_float32ne(unsigned n, const float *a, int16_t *b) {
pa_assert(b);
#if SWAP_WORDS == 1
-
for (; n > 0; n--) {
int16_t s;
float v = *(a++);
@@ -133,12 +122,13 @@ void pa_sconv_s16le_from_float32ne(unsigned n, const float *a, int16_t *b) {
s = (int16_t) lrintf(v * 0x7FFF);
*(b++) = INT16_TO(s);
}
-
#else
-{
- static const double add = 0, factor = 0x7FFF;
- oil_scaleconv_s16_f32(b, a, (int) n, &add, &factor);
-}
+ for (; n > 0; n--) {
+ float v = *(a++);
+
+ v = PA_CLAMP_UNLIKELY(v, -1.0f, 1.f);
+ *(b++) = (int16_t) lrintf(v * 0x7FFF);
+ }
#endif
}
@@ -147,7 +137,6 @@ void pa_sconv_s32le_from_float32ne(unsigned n, const float *a, int32_t *b) {
pa_assert(b);
#if SWAP_WORDS == 1
-
for (; n > 0; n--) {
int32_t s;
float v = *(a++);
@@ -156,12 +145,13 @@ void pa_sconv_s32le_from_float32ne(unsigned n, const float *a, int32_t *b) {
s = (int32_t) lrint((double) v * (double) 0x7FFFFFFF);
*(b++) = INT32_TO(s);
}
-
#else
-{
- static const double add = 0, factor = 0x7FFFFFFF;
- oil_scaleconv_s32_f32(b, a, (int) n, &add, &factor);
-}
+ for (; n > 0; n--) {
+ float v = *(a++);
+
+ v = PA_CLAMP_UNLIKELY(v, -1.0f, 1.0f);
+ *(b++) = (int32_t) lrint((double) v * (double) 0x7FFFFFFF);
+ }
#endif
}
diff --git a/src/pulsecore/sconv.c b/src/pulsecore/sconv.c
index d89f4283..301f08b4 100644
--- a/src/pulsecore/sconv.c
+++ b/src/pulsecore/sconv.c
@@ -27,9 +27,6 @@
#include <stdio.h>
#include <stdlib.h>
-#include <liboil/liboilfuncs.h>
-#include <liboil/liboil.h>
-
#include <pulsecore/g711.h>
#include <pulsecore/macro.h>
@@ -41,32 +38,31 @@
/* u8 */
static void u8_to_float32ne(unsigned n, const uint8_t *a, float *b) {
- static const double add = -1, factor = 1.0/128.0;
-
pa_assert(a);
pa_assert(b);
- oil_scaleconv_f32_u8(b, a, (int) n, &add, &factor);
+ for (; n > 0; n--, a++, b++)
+ *b = (*a * 1.0/128.0) - 1.0;
}
static void u8_from_float32ne(unsigned n, const float *a, uint8_t *b) {
- static const double add = 128, factor = 127.0;
-
pa_assert(a);
pa_assert(b);
- oil_scaleconv_u8_f32(b, a, (int) n, &add, &factor);
+ for (; n > 0; n--, a++, b++) {
+ float v;
+ v = (*a * 127.0) + 128.0;
+ v = PA_CLAMP_UNLIKELY (v, 0.0, 255.0);
+ *b = rint (v);
+ }
}
static void u8_to_s16ne(unsigned n, const uint8_t *a, int16_t *b) {
- static const int16_t add = -0x80, factor = 0x100;
-
pa_assert(a);
pa_assert(b);
- oil_conv_s16_u8(b, 2, a, 1, (int) n);
- oil_scalaradd_s16(b, 2, b, 2, &add, (int) n);
- oil_scalarmult_s16(b, 2, b, 2, &factor, (int) n);
+ for (; n > 0; n--, a++, b++)
+ *b = (((int16_t)*a) - 128) << 8;
}
static void u8_from_s16ne(unsigned n, const int16_t *a, uint8_t *b) {
@@ -84,7 +80,7 @@ static void float32ne_to_float32ne(unsigned n, const float *a, float *b) {
pa_assert(a);
pa_assert(b);
- oil_memcpy(b, a, (int) (sizeof(float) * n));
+ memcpy(b, a, (int) (sizeof(float) * n));
}
static void float32re_to_float32ne(unsigned n, const float *a, float *b) {
@@ -101,7 +97,7 @@ static void s16ne_to_s16ne(unsigned n, const int16_t *a, int16_t *b) {
pa_assert(a);
pa_assert(b);
- oil_memcpy(b, a, (int) (sizeof(int16_t) * n));
+ memcpy(b, a, (int) (sizeof(int16_t) * n));
}
static void s16re_to_s16ne(unsigned n, const int16_t *a, int16_t *b) {
@@ -188,98 +184,130 @@ static void alaw_from_s16ne(unsigned n, const int16_t *a, uint8_t *b) {
*b = st_13linear2alaw(*a >> 3);
}
+static pa_convert_func_t to_float32ne_table[] = {
+ [PA_SAMPLE_U8] = (pa_convert_func_t) u8_to_float32ne,
+ [PA_SAMPLE_ALAW] = (pa_convert_func_t) alaw_to_float32ne,
+ [PA_SAMPLE_ULAW] = (pa_convert_func_t) ulaw_to_float32ne,
+ [PA_SAMPLE_S16LE] = (pa_convert_func_t) pa_sconv_s16le_to_float32ne,
+ [PA_SAMPLE_S16BE] = (pa_convert_func_t) pa_sconv_s16be_to_float32ne,
+ [PA_SAMPLE_S32LE] = (pa_convert_func_t) pa_sconv_s32le_to_float32ne,
+ [PA_SAMPLE_S32BE] = (pa_convert_func_t) pa_sconv_s32be_to_float32ne,
+ [PA_SAMPLE_S24LE] = (pa_convert_func_t) pa_sconv_s24le_to_float32ne,
+ [PA_SAMPLE_S24BE] = (pa_convert_func_t) pa_sconv_s24be_to_float32ne,
+ [PA_SAMPLE_S24_32LE] = (pa_convert_func_t) pa_sconv_s24_32le_to_float32ne,
+ [PA_SAMPLE_S24_32BE] = (pa_convert_func_t) pa_sconv_s24_32be_to_float32ne,
+ [PA_SAMPLE_FLOAT32NE] = (pa_convert_func_t) float32ne_to_float32ne,
+ [PA_SAMPLE_FLOAT32RE] = (pa_convert_func_t) float32re_to_float32ne,
+};
+
pa_convert_func_t pa_get_convert_to_float32ne_function(pa_sample_format_t f) {
- static const pa_convert_func_t table[] = {
- [PA_SAMPLE_U8] = (pa_convert_func_t) u8_to_float32ne,
- [PA_SAMPLE_ALAW] = (pa_convert_func_t) alaw_to_float32ne,
- [PA_SAMPLE_ULAW] = (pa_convert_func_t) ulaw_to_float32ne,
- [PA_SAMPLE_S16LE] = (pa_convert_func_t) pa_sconv_s16le_to_float32ne,
- [PA_SAMPLE_S16BE] = (pa_convert_func_t) pa_sconv_s16be_to_float32ne,
- [PA_SAMPLE_S32LE] = (pa_convert_func_t) pa_sconv_s32le_to_float32ne,
- [PA_SAMPLE_S32BE] = (pa_convert_func_t) pa_sconv_s32be_to_float32ne,
- [PA_SAMPLE_S24LE] = (pa_convert_func_t) pa_sconv_s24le_to_float32ne,
- [PA_SAMPLE_S24BE] = (pa_convert_func_t) pa_sconv_s24be_to_float32ne,
- [PA_SAMPLE_S24_32LE] = (pa_convert_func_t) pa_sconv_s24_32le_to_float32ne,
- [PA_SAMPLE_S24_32BE] = (pa_convert_func_t) pa_sconv_s24_32be_to_float32ne,
- [PA_SAMPLE_FLOAT32NE] = (pa_convert_func_t) float32ne_to_float32ne,
- [PA_SAMPLE_FLOAT32RE] = (pa_convert_func_t) float32re_to_float32ne,
- };
+ pa_assert(f >= 0);
+ pa_assert(f < PA_SAMPLE_MAX);
+
+ return to_float32ne_table[f];
+}
+
+void pa_set_convert_to_float32ne_function(pa_sample_format_t f, pa_convert_func_t func) {
pa_assert(f >= 0);
pa_assert(f < PA_SAMPLE_MAX);
- return table[f];
+ to_float32ne_table[f] = func;
}
+static pa_convert_func_t from_float32ne_table[] = {
+ [PA_SAMPLE_U8] = (pa_convert_func_t) u8_from_float32ne,
+ [PA_SAMPLE_S16LE] = (pa_convert_func_t) pa_sconv_s16le_from_float32ne,
+ [PA_SAMPLE_S16BE] = (pa_convert_func_t) pa_sconv_s16be_from_float32ne,
+ [PA_SAMPLE_S32LE] = (pa_convert_func_t) pa_sconv_s32le_from_float32ne,
+ [PA_SAMPLE_S32BE] = (pa_convert_func_t) pa_sconv_s32be_from_float32ne,
+ [PA_SAMPLE_S24LE] = (pa_convert_func_t) pa_sconv_s24le_from_float32ne,
+ [PA_SAMPLE_S24BE] = (pa_convert_func_t) pa_sconv_s24be_from_float32ne,
+ [PA_SAMPLE_S24_32LE] = (pa_convert_func_t) pa_sconv_s24_32le_from_float32ne,
+ [PA_SAMPLE_S24_32BE] = (pa_convert_func_t) pa_sconv_s24_32be_from_float32ne,
+ [PA_SAMPLE_FLOAT32NE] = (pa_convert_func_t) float32ne_to_float32ne,
+ [PA_SAMPLE_FLOAT32RE] = (pa_convert_func_t) float32re_to_float32ne,
+ [PA_SAMPLE_ALAW] = (pa_convert_func_t) alaw_from_float32ne,
+ [PA_SAMPLE_ULAW] = (pa_convert_func_t) ulaw_from_float32ne
+};
+
pa_convert_func_t pa_get_convert_from_float32ne_function(pa_sample_format_t f) {
- static const pa_convert_func_t table[] = {
- [PA_SAMPLE_U8] = (pa_convert_func_t) u8_from_float32ne,
- [PA_SAMPLE_S16LE] = (pa_convert_func_t) pa_sconv_s16le_from_float32ne,
- [PA_SAMPLE_S16BE] = (pa_convert_func_t) pa_sconv_s16be_from_float32ne,
- [PA_SAMPLE_S32LE] = (pa_convert_func_t) pa_sconv_s32le_from_float32ne,
- [PA_SAMPLE_S32BE] = (pa_convert_func_t) pa_sconv_s32be_from_float32ne,
- [PA_SAMPLE_S24LE] = (pa_convert_func_t) pa_sconv_s24le_from_float32ne,
- [PA_SAMPLE_S24BE] = (pa_convert_func_t) pa_sconv_s24be_from_float32ne,
- [PA_SAMPLE_S24_32LE] = (pa_convert_func_t) pa_sconv_s24_32le_from_float32ne,
- [PA_SAMPLE_S24_32BE] = (pa_convert_func_t) pa_sconv_s24_32be_from_float32ne,
- [PA_SAMPLE_FLOAT32NE] = (pa_convert_func_t) float32ne_to_float32ne,
- [PA_SAMPLE_FLOAT32RE] = (pa_convert_func_t) float32re_to_float32ne,
- [PA_SAMPLE_ALAW] = (pa_convert_func_t) alaw_from_float32ne,
- [PA_SAMPLE_ULAW] = (pa_convert_func_t) ulaw_from_float32ne
- };
+ pa_assert(f >= 0);
+ pa_assert(f < PA_SAMPLE_MAX);
+
+ return from_float32ne_table[f];
+}
+
+void pa_set_convert_from_float32ne_function(pa_sample_format_t f, pa_convert_func_t func) {
pa_assert(f >= 0);
pa_assert(f < PA_SAMPLE_MAX);
- return table[f];
+ from_float32ne_table[f] = func;
}
+static pa_convert_func_t to_s16ne_table[] = {
+ [PA_SAMPLE_U8] = (pa_convert_func_t) u8_to_s16ne,
+ [PA_SAMPLE_S16NE] = (pa_convert_func_t) s16ne_to_s16ne,
+ [PA_SAMPLE_S16RE] = (pa_convert_func_t) s16re_to_s16ne,
+ [PA_SAMPLE_FLOAT32BE] = (pa_convert_func_t) pa_sconv_float32be_to_s16ne,
+ [PA_SAMPLE_FLOAT32LE] = (pa_convert_func_t) pa_sconv_float32le_to_s16ne,
+ [PA_SAMPLE_S32BE] = (pa_convert_func_t) pa_sconv_s32be_to_s16ne,
+ [PA_SAMPLE_S32LE] = (pa_convert_func_t) pa_sconv_s32le_to_s16ne,
+ [PA_SAMPLE_S24BE] = (pa_convert_func_t) pa_sconv_s24be_to_s16ne,
+ [PA_SAMPLE_S24LE] = (pa_convert_func_t) pa_sconv_s24le_to_s16ne,
+ [PA_SAMPLE_S24_32BE] = (pa_convert_func_t) pa_sconv_s24_32be_to_s16ne,
+ [PA_SAMPLE_S24_32LE] = (pa_convert_func_t) pa_sconv_s24_32le_to_s16ne,
+ [PA_SAMPLE_ALAW] = (pa_convert_func_t) alaw_to_s16ne,
+ [PA_SAMPLE_ULAW] = (pa_convert_func_t) ulaw_to_s16ne
+};
+
pa_convert_func_t pa_get_convert_to_s16ne_function(pa_sample_format_t f) {
- static const pa_convert_func_t table[] = {
- [PA_SAMPLE_U8] = (pa_convert_func_t) u8_to_s16ne,
- [PA_SAMPLE_S16NE] = (pa_convert_func_t) s16ne_to_s16ne,
- [PA_SAMPLE_S16RE] = (pa_convert_func_t) s16re_to_s16ne,
- [PA_SAMPLE_FLOAT32BE] = (pa_convert_func_t) pa_sconv_float32be_to_s16ne,
- [PA_SAMPLE_FLOAT32LE] = (pa_convert_func_t) pa_sconv_float32le_to_s16ne,
- [PA_SAMPLE_S32BE] = (pa_convert_func_t) pa_sconv_s32be_to_s16ne,
- [PA_SAMPLE_S32LE] = (pa_convert_func_t) pa_sconv_s32le_to_s16ne,
- [PA_SAMPLE_S24BE] = (pa_convert_func_t) pa_sconv_s24be_to_s16ne,
- [PA_SAMPLE_S24LE] = (pa_convert_func_t) pa_sconv_s24le_to_s16ne,
- [PA_SAMPLE_S24_32BE] = (pa_convert_func_t) pa_sconv_s24_32be_to_s16ne,
- [PA_SAMPLE_S24_32LE] = (pa_convert_func_t) pa_sconv_s24_32le_to_s16ne,
- [PA_SAMPLE_ALAW] = (pa_convert_func_t) alaw_to_s16ne,
- [PA_SAMPLE_ULAW] = (pa_convert_func_t) ulaw_to_s16ne
- };
+ pa_assert(f >= 0);
+ pa_assert(f < PA_SAMPLE_MAX);
+
+ return to_s16ne_table[f];
+}
+
+void pa_set_convert_to_s16ne_function(pa_sample_format_t f, pa_convert_func_t func) {
pa_assert(f >= 0);
pa_assert(f < PA_SAMPLE_MAX);
- return table[f];
+ to_s16ne_table[f] = func;
}
+static pa_convert_func_t from_s16ne_table[] = {
+ [PA_SAMPLE_U8] = (pa_convert_func_t) u8_from_s16ne,
+ [PA_SAMPLE_S16NE] = (pa_convert_func_t) s16ne_to_s16ne,
+ [PA_SAMPLE_S16RE] = (pa_convert_func_t) s16re_to_s16ne,
+ [PA_SAMPLE_FLOAT32BE] = (pa_convert_func_t) pa_sconv_float32be_from_s16ne,
+ [PA_SAMPLE_FLOAT32LE] = (pa_convert_func_t) pa_sconv_float32le_from_s16ne,
+ [PA_SAMPLE_S32BE] = (pa_convert_func_t) pa_sconv_s32be_from_s16ne,
+ [PA_SAMPLE_S32LE] = (pa_convert_func_t) pa_sconv_s32le_from_s16ne,
+ [PA_SAMPLE_S24BE] = (pa_convert_func_t) pa_sconv_s24be_from_s16ne,
+ [PA_SAMPLE_S24LE] = (pa_convert_func_t) pa_sconv_s24le_from_s16ne,
+ [PA_SAMPLE_S24_32BE] = (pa_convert_func_t) pa_sconv_s24_32be_from_s16ne,
+ [PA_SAMPLE_S24_32LE] = (pa_convert_func_t) pa_sconv_s24_32le_from_s16ne,
+ [PA_SAMPLE_ALAW] = (pa_convert_func_t) alaw_from_s16ne,
+ [PA_SAMPLE_ULAW] = (pa_convert_func_t) ulaw_from_s16ne,
+};
+
pa_convert_func_t pa_get_convert_from_s16ne_function(pa_sample_format_t f) {
- static const pa_convert_func_t table[] = {
- [PA_SAMPLE_U8] = (pa_convert_func_t) u8_from_s16ne,
- [PA_SAMPLE_S16NE] = (pa_convert_func_t) s16ne_to_s16ne,
- [PA_SAMPLE_S16RE] = (pa_convert_func_t) s16re_to_s16ne,
- [PA_SAMPLE_FLOAT32BE] = (pa_convert_func_t) pa_sconv_float32be_from_s16ne,
- [PA_SAMPLE_FLOAT32LE] = (pa_convert_func_t) pa_sconv_float32le_from_s16ne,
- [PA_SAMPLE_S32BE] = (pa_convert_func_t) pa_sconv_s32be_from_s16ne,
- [PA_SAMPLE_S32LE] = (pa_convert_func_t) pa_sconv_s32le_from_s16ne,
- [PA_SAMPLE_S24BE] = (pa_convert_func_t) pa_sconv_s24be_from_s16ne,
- [PA_SAMPLE_S24LE] = (pa_convert_func_t) pa_sconv_s24le_from_s16ne,
- [PA_SAMPLE_S24_32BE] = (pa_convert_func_t) pa_sconv_s24_32be_from_s16ne,
- [PA_SAMPLE_S24_32LE] = (pa_convert_func_t) pa_sconv_s24_32le_from_s16ne,
- [PA_SAMPLE_ALAW] = (pa_convert_func_t) alaw_from_s16ne,
- [PA_SAMPLE_ULAW] = (pa_convert_func_t) ulaw_from_s16ne,
- };
+ pa_assert(f >= 0);
+ pa_assert(f < PA_SAMPLE_MAX);
+
+ return from_s16ne_table[f];
+}
+
+void pa_set_convert_from_s16ne_function(pa_sample_format_t f, pa_convert_func_t func) {
pa_assert(f >= 0);
pa_assert(f < PA_SAMPLE_MAX);
- return table[f];
+ from_s16ne_table[f] = func;
}
diff --git a/src/pulsecore/sconv.h b/src/pulsecore/sconv.h
index b00a16a4..cd937559 100644
--- a/src/pulsecore/sconv.h
+++ b/src/pulsecore/sconv.h
@@ -33,4 +33,10 @@ pa_convert_func_t pa_get_convert_from_float32ne_function(pa_sample_format_t f) P
pa_convert_func_t pa_get_convert_to_s16ne_function(pa_sample_format_t f) PA_GCC_PURE;
pa_convert_func_t pa_get_convert_from_s16ne_function(pa_sample_format_t f) PA_GCC_PURE;
+void pa_set_convert_to_float32ne_function(pa_sample_format_t f, pa_convert_func_t func);
+void pa_set_convert_from_float32ne_function(pa_sample_format_t f, pa_convert_func_t func);
+
+void pa_set_convert_to_s16ne_function(pa_sample_format_t f, pa_convert_func_t func);
+void pa_set_convert_from_s16ne_function(pa_sample_format_t f, pa_convert_func_t func);
+
#endif
diff --git a/src/pulsecore/sconv_sse.c b/src/pulsecore/sconv_sse.c
new file mode 100644
index 00000000..3737af2a
--- /dev/null
+++ b/src/pulsecore/sconv_sse.c
@@ -0,0 +1,235 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2004-2006 Lennart Poettering
+ Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+ 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 <stdio.h>
+#include <stdlib.h>
+
+#include <pulsecore/g711.h>
+#include <pulsecore/macro.h>
+
+#include "endianmacros.h"
+
+#include "cpu-x86.h"
+#include "sconv.h"
+
+#if defined (__i386__) || defined (__amd64__)
+
+static const PA_DECLARE_ALIGNED (16, float, one[4]) = { 1.0, 1.0, 1.0, 1.0 };
+static const PA_DECLARE_ALIGNED (16, float, mone[4]) = { -1.0, -1.0, -1.0, -1.0 };
+static const PA_DECLARE_ALIGNED (16, float, scale[4]) = { 0x7fff, 0x7fff, 0x7fff, 0x7fff };
+
+static void pa_sconv_s16le_from_f32ne_sse(unsigned n, const float *a, int16_t *b) {
+ pa_reg_x86 temp, i;
+
+ __asm__ __volatile__ (
+ " movaps %5, %%xmm5 \n\t"
+ " movaps %6, %%xmm6 \n\t"
+ " movaps %7, %%xmm7 \n\t"
+ " xor %0, %0 \n\t"
+
+ " mov %4, %1 \n\t"
+ " sar $3, %1 \n\t" /* 8 floats at a time */
+ " cmp $0, %1 \n\t"
+ " je 2f \n\t"
+
+ "1: \n\t"
+ " movups (%2, %0, 2), %%xmm0 \n\t" /* read 8 floats */
+ " movups 16(%2, %0, 2), %%xmm2 \n\t"
+ " minps %%xmm5, %%xmm0 \n\t" /* clamp to 1.0 */
+ " minps %%xmm5, %%xmm2 \n\t"
+ " maxps %%xmm6, %%xmm0 \n\t" /* clamp to -1.0 */
+ " maxps %%xmm6, %%xmm2 \n\t"
+ " mulps %%xmm7, %%xmm0 \n\t" /* *= 0x7fff */
+ " mulps %%xmm7, %%xmm2 \n\t"
+
+ " cvtps2pi %%xmm0, %%mm0 \n\t" /* low part to int */
+ " cvtps2pi %%xmm2, %%mm2 \n\t"
+ " movhlps %%xmm0, %%xmm0 \n\t" /* bring high part in position */
+ " movhlps %%xmm2, %%xmm2 \n\t"
+ " cvtps2pi %%xmm0, %%mm1 \n\t" /* high part to int */
+ " cvtps2pi %%xmm2, %%mm3 \n\t"
+
+ " packssdw %%mm1, %%mm0 \n\t" /* pack parts */
+ " packssdw %%mm3, %%mm2 \n\t"
+ " movq %%mm0, (%3, %0) \n\t"
+ " movq %%mm2, 8(%3, %0) \n\t"
+
+ " add $16, %0 \n\t"
+ " dec %1 \n\t"
+ " jne 1b \n\t"
+
+ "2: \n\t"
+ " mov %4, %1 \n\t" /* prepare for leftovers */
+ " and $7, %1 \n\t"
+ " je 4f \n\t"
+
+ "3: \n\t"
+ " movss (%2, %0, 2), %%xmm0 \n\t"
+ " minss %%xmm5, %%xmm0 \n\t"
+ " maxss %%xmm6, %%xmm0 \n\t"
+ " mulss %%xmm7, %%xmm0 \n\t"
+ " cvtss2si %%xmm0, %4 \n\t"
+ " movw %w4, (%3, %0) \n\t"
+ " add $2, %0 \n\t"
+ " dec %1 \n\t"
+ " jne 3b \n\t"
+
+ "4: \n\t"
+ " emms \n\t"
+
+ : "=&r" (i), "=&r" (temp)
+ : "r" (a), "r" (b), "r" ((pa_reg_x86)n), "m" (*one), "m" (*mone), "m" (*scale)
+ : "cc", "memory"
+ );
+}
+
+static void pa_sconv_s16le_from_f32ne_sse2(unsigned n, const float *a, int16_t *b) {
+ pa_reg_x86 temp, i;
+
+ __asm__ __volatile__ (
+ " movaps %5, %%xmm5 \n\t"
+ " movaps %6, %%xmm6 \n\t"
+ " movaps %7, %%xmm7 \n\t"
+ " xor %0, %0 \n\t"
+
+ " mov %4, %1 \n\t"
+ " sar $3, %1 \n\t" /* 8 floats at a time */
+ " cmp $0, %1 \n\t"
+ " je 2f \n\t"
+
+ "1: \n\t"
+ " movups (%2, %0, 2), %%xmm0 \n\t" /* read 8 floats */
+ " movups 16(%2, %0, 2), %%xmm2 \n\t"
+ " minps %%xmm5, %%xmm0 \n\t" /* clamp to 1.0 */
+ " minps %%xmm5, %%xmm2 \n\t"
+ " maxps %%xmm6, %%xmm0 \n\t" /* clamp to -1.0 */
+ " maxps %%xmm6, %%xmm2 \n\t"
+ " mulps %%xmm7, %%xmm0 \n\t" /* *= 0x7fff */
+ " mulps %%xmm7, %%xmm2 \n\t"
+
+ " cvtps2dq %%xmm0, %%xmm0 \n\t"
+ " cvtps2dq %%xmm2, %%xmm2 \n\t"
+
+ " packssdw %%xmm2, %%xmm0 \n\t"
+ " movdqu %%xmm0, (%3, %0) \n\t"
+
+ " add $16, %0 \n\t"
+ " dec %1 \n\t"
+ " jne 1b \n\t"
+
+ "2: \n\t"
+ " mov %4, %1 \n\t" /* prepare for leftovers */
+ " and $7, %1 \n\t"
+ " je 4f \n\t"
+
+ "3: \n\t"
+ " movss (%2, %0, 2), %%xmm0 \n\t"
+ " minss %%xmm5, %%xmm0 \n\t"
+ " maxss %%xmm6, %%xmm0 \n\t"
+ " mulss %%xmm7, %%xmm0 \n\t"
+ " cvtss2si %%xmm0, %4 \n\t"
+ " movw %w4, (%3, %0) \n\t"
+ " add $2, %0 \n\t"
+ " dec %1 \n\t"
+ " jne 3b \n\t"
+
+ "4: \n\t"
+
+ : "=&r" (i), "=&r" (temp)
+ : "r" (a), "r" (b), "r" ((pa_reg_x86)n), "m" (*one), "m" (*mone), "m" (*scale)
+ : "cc", "memory"
+ );
+}
+
+#undef RUN_TEST
+
+#ifdef RUN_TEST
+#define SAMPLES 1019
+#define TIMES 1000
+
+static void run_test (void) {
+ int16_t samples[SAMPLES];
+ int16_t samples_ref[SAMPLES];
+ float floats[SAMPLES];
+ int i;
+ pa_usec_t start, stop;
+ pa_convert_func_t func;
+
+ printf ("checking SSE %zd\n", sizeof (samples));
+
+ memset (samples_ref, 0, sizeof (samples_ref));
+ memset (samples, 0, sizeof (samples));
+
+ for (i = 0; i < SAMPLES; i++) {
+ floats[i] = (rand()/(RAND_MAX+2.2)) - 1.1;
+ }
+
+ func = pa_get_convert_from_float32ne_function (PA_SAMPLE_S16LE);
+ func (SAMPLES, floats, samples_ref);
+ pa_sconv_s16le_from_f32ne_sse2 (SAMPLES, floats, samples);
+
+ for (i = 0; i < SAMPLES; i++) {
+ if (samples[i] != samples_ref[i]) {
+ printf ("%d: %04x != %04x (%f)\n", i, samples[i], samples_ref[i],
+ floats[i]);
+ }
+ }
+
+ start = pa_rtclock_now();
+ for (i = 0; i < TIMES; i++) {
+ pa_sconv_s16le_from_f32ne_sse2 (SAMPLES, floats, samples);
+ }
+ stop = pa_rtclock_now();
+ pa_log_info("SSE: %llu usec.", (long long unsigned int)(stop - start));
+
+ start = pa_rtclock_now();
+ for (i = 0; i < TIMES; i++) {
+ func (SAMPLES, floats, samples_ref);
+ }
+ stop = pa_rtclock_now();
+ pa_log_info("ref: %llu usec.", (long long unsigned int)(stop - start));
+}
+#endif
+#endif /* defined (__i386__) || defined (__amd64__) */
+
+
+void pa_convert_func_init_sse (pa_cpu_x86_flag_t flags) {
+#if defined (__i386__) || defined (__amd64__)
+
+#ifdef RUN_TEST
+ run_test ();
+#endif
+
+ if (flags & PA_CPU_X86_SSE2) {
+ pa_log_info("Initialising SSE2 optimized conversions.");
+ pa_set_convert_from_float32ne_function (PA_SAMPLE_S16LE, (pa_convert_func_t) pa_sconv_s16le_from_f32ne_sse2);
+ } else {
+ pa_log_info("Initialising SSE optimized conversions.");
+ pa_set_convert_from_float32ne_function (PA_SAMPLE_S16LE, (pa_convert_func_t) pa_sconv_s16le_from_f32ne_sse);
+ }
+
+#endif /* defined (__i386__) || defined (__amd64__) */
+}
diff --git a/src/pulsecore/semaphore-osx.c b/src/pulsecore/semaphore-osx.c
new file mode 100644
index 00000000..73f43559
--- /dev/null
+++ b/src/pulsecore/semaphore-osx.c
@@ -0,0 +1,63 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2009 Kim Lester <kim@dfusion.com.au>
+
+ 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 <Multiprocessing.h>
+
+#include <pulse/xmalloc.h>
+#include <pulsecore/macro.h>
+
+#include "semaphore.h"
+
+struct pa_semaphore
+{
+ MPSemaphoreID sema;
+};
+
+pa_semaphore* pa_semaphore_new(unsigned int value) {
+ /* NOTE: Can't assume boolean - ie value = 0,1, so use UINT_MAX (boolean more efficient ?) */
+ pa_semaphore *s;
+
+ s = pa_xnew(pa_semaphore, 1);
+ pa_assert_se(MPCreateSemaphore(UINT_MAX, value, &s->sema) == 0);
+
+ return s;
+}
+
+void pa_semaphore_free(pa_semaphore *s) {
+ pa_assert(s);
+ pa_assert_se(MPDeleteSemaphore(s->sema) == 0);
+ pa_xfree(s);
+}
+
+void pa_semaphore_post(pa_semaphore *s) {
+ pa_assert(s);
+ pa_assert_se(MPSignalSemaphore(s->sema) == 0);
+}
+
+void pa_semaphore_wait(pa_semaphore *s) {
+ pa_assert(s);
+ /* should probably check return value (-ve is error), noErr is ok. */
+ pa_assert_se(MPWaitOnSemaphore(s->sema, kDurationForever) == 0);
+}
diff --git a/src/pulsecore/shm.c b/src/pulsecore/shm.c
index 6e428426..fbf777a4 100644
--- a/src/pulsecore/shm.c
+++ b/src/pulsecore/shm.c
@@ -60,7 +60,8 @@
#define MADV_REMOVE 9
#endif
-#define MAX_SHM_SIZE (PA_ALIGN(1024*1024*64))
+/* 1 GiB at max */
+#define MAX_SHM_SIZE (PA_ALIGN(1024*1024*1024))
#ifdef __linux__
/* On Linux we know that the shared memory blocks are files in
diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c
index 2acaa08f..216edd41 100644
--- a/src/pulsecore/sink-input.c
+++ b/src/pulsecore/sink-input.c
@@ -45,9 +45,10 @@
#define MEMBLOCKQ_MAXLENGTH (32*1024*1024)
#define CONVERT_BUFFER_LENGTH (PA_PAGE_SIZE)
-static PA_DEFINE_CHECK_TYPE(pa_sink_input, pa_msgobject);
+PA_DEFINE_PUBLIC_CLASS(pa_sink_input, pa_msgobject);
static void sink_input_free(pa_object *o);
+static void set_real_ratio(pa_sink_input *i, const pa_cvolume *v);
static void sink_input_set_ramping_info(pa_sink_input* i, pa_volume_t pre_virtual_volume, pa_volume_t target_virtual_volume, pa_usec_t t);
static void sink_input_set_ramping_info_for_mute(pa_sink_input* i, pa_bool_t mute, pa_usec_t t);
static void sink_input_volume_ramping(pa_sink_input* i, pa_memchunk* chunk);
@@ -57,7 +58,7 @@ static void sink_input_release_envelope(pa_sink_input *i);
pa_sink_input_new_data* pa_sink_input_new_data_init(pa_sink_input_new_data *data) {
pa_assert(data);
- memset(data, 0, sizeof(*data));
+ pa_zero(*data);
data->resample_method = PA_RESAMPLER_INVALID;
data->proplist = pa_proplist_new();
@@ -97,6 +98,18 @@ void pa_sink_input_new_data_apply_volume_factor(pa_sink_input_new_data *data, co
}
}
+void pa_sink_input_new_data_apply_volume_factor_sink(pa_sink_input_new_data *data, const pa_cvolume *volume_factor) {
+ pa_assert(data);
+ pa_assert(volume_factor);
+
+ if (data->volume_factor_sink_is_set)
+ pa_sw_cvolume_multiply(&data->volume_factor_sink, &data->volume_factor_sink, volume_factor);
+ else {
+ data->volume_factor_sink_is_set = TRUE;
+ data->volume_factor_sink = *volume_factor;
+ }
+}
+
void pa_sink_input_new_data_set_muted(pa_sink_input_new_data *data, pa_bool_t mute) {
pa_assert(data);
@@ -120,6 +133,7 @@ static void reset_callbacks(pa_sink_input *i) {
i->update_max_request = NULL;
i->update_sink_requested_latency = NULL;
i->update_sink_latency_range = NULL;
+ i->update_sink_fixed_latency = NULL;
i->attach = NULL;
i->detach = NULL;
i->suspend = NULL;
@@ -130,24 +144,27 @@ static void reset_callbacks(pa_sink_input *i) {
i->state_change = NULL;
i->may_move_to = NULL;
i->send_event = NULL;
+ i->volume_changed = NULL;
+ i->mute_changed = NULL;
}
/* Called from main context */
int pa_sink_input_new(
pa_sink_input **_i,
pa_core *core,
- pa_sink_input_new_data *data,
- pa_sink_input_flags_t flags) {
+ pa_sink_input_new_data *data) {
pa_sink_input *i;
pa_resampler *resampler = NULL;
char st[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
pa_channel_map original_cm;
int r;
+ char *pt;
pa_assert(_i);
pa_assert(core);
pa_assert(data);
+ pa_assert_ctl_context();
if (data->client)
pa_proplist_update(data->proplist, PA_UPDATE_MERGE, data->client->proplist);
@@ -178,7 +195,6 @@ int pa_sink_input_new(
pa_channel_map_init_extend(&data->channel_map, data->sample_spec.channels, PA_CHANNEL_MAP_DEFAULT);
}
- pa_return_val_if_fail(pa_channel_map_valid(&data->channel_map), -PA_ERR_INVALID);
pa_return_val_if_fail(pa_channel_map_compatible(&data->channel_map, &data->sample_spec), -PA_ERR_INVALID);
if (!data->volume_is_set) {
@@ -187,27 +203,30 @@ int pa_sink_input_new(
data->save_volume = FALSE;
}
- pa_return_val_if_fail(pa_cvolume_valid(&data->volume), -PA_ERR_INVALID);
pa_return_val_if_fail(pa_cvolume_compatible(&data->volume, &data->sample_spec), -PA_ERR_INVALID);
if (!data->volume_factor_is_set)
pa_cvolume_reset(&data->volume_factor, data->sample_spec.channels);
- pa_return_val_if_fail(pa_cvolume_valid(&data->volume_factor), -PA_ERR_INVALID);
pa_return_val_if_fail(pa_cvolume_compatible(&data->volume_factor, &data->sample_spec), -PA_ERR_INVALID);
+ if (!data->volume_factor_sink_is_set)
+ pa_cvolume_reset(&data->volume_factor_sink, data->sink->sample_spec.channels);
+
+ pa_return_val_if_fail(pa_cvolume_compatible(&data->volume_factor_sink, &data->sink->sample_spec), -PA_ERR_INVALID);
+
if (!data->muted_is_set)
data->muted = FALSE;
- if (flags & PA_SINK_INPUT_FIX_FORMAT)
+ if (data->flags & PA_SINK_INPUT_FIX_FORMAT)
data->sample_spec.format = data->sink->sample_spec.format;
- if (flags & PA_SINK_INPUT_FIX_RATE)
+ if (data->flags & PA_SINK_INPUT_FIX_RATE)
data->sample_spec.rate = data->sink->sample_spec.rate;
original_cm = data->channel_map;
- if (flags & PA_SINK_INPUT_FIX_CHANNELS) {
+ if (data->flags & PA_SINK_INPUT_FIX_CHANNELS) {
data->sample_spec.channels = data->sink->sample_spec.channels;
data->channel_map = data->sink->channel_map;
}
@@ -226,7 +245,7 @@ int pa_sink_input_new(
if ((r = pa_hook_fire(&core->hooks[PA_CORE_HOOK_SINK_INPUT_FIXATE], data)) < 0)
return r;
- if ((flags & PA_SINK_INPUT_FAIL_ON_SUSPEND) &&
+ if ((data->flags & PA_SINK_INPUT_NO_CREATE_ON_SUSPEND) &&
pa_sink_get_state(data->sink) == PA_SINK_SUSPENDED) {
pa_log_warn("Failed to create sink input: sink is suspended.");
return -PA_ERR_BADSTATE;
@@ -237,7 +256,7 @@ int pa_sink_input_new(
return -PA_ERR_TOOLARGE;
}
- if ((flags & PA_SINK_INPUT_VARIABLE_RATE) ||
+ if ((data->flags & PA_SINK_INPUT_VARIABLE_RATE) ||
!pa_sample_spec_equal(&data->sample_spec, &data->sink->sample_spec) ||
!pa_channel_map_equal(&data->channel_map, &data->sink->channel_map)) {
@@ -246,9 +265,9 @@ int pa_sink_input_new(
&data->sample_spec, &data->channel_map,
&data->sink->sample_spec, &data->sink->channel_map,
data->resample_method,
- ((flags & PA_SINK_INPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0) |
- ((flags & PA_SINK_INPUT_NO_REMAP) ? PA_RESAMPLER_NO_REMAP : 0) |
- (core->disable_remixing || (flags & PA_SINK_INPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0) |
+ ((data->flags & PA_SINK_INPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0) |
+ ((data->flags & PA_SINK_INPUT_NO_REMAP) ? PA_RESAMPLER_NO_REMAP : 0) |
+ (core->disable_remixing || (data->flags & PA_SINK_INPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0) |
(core->disable_lfe_remixing ? PA_RESAMPLER_NO_LFE : 0)))) {
pa_log_warn("Unsupported resampling operation.");
return -PA_ERR_NOTSUPPORTED;
@@ -261,7 +280,7 @@ int pa_sink_input_new(
i->core = core;
i->state = PA_SINK_INPUT_INIT;
- i->flags = flags;
+ i->flags = data->flags;
i->proplist = pa_proplist_copy(data->proplist);
i->driver = pa_xstrdup(pa_path_get_filename(data->driver));
i->module = data->module;
@@ -274,18 +293,21 @@ int pa_sink_input_new(
i->channel_map = data->channel_map;
if ((i->sink->flags & PA_SINK_FLAT_VOLUME) && !data->volume_is_absolute) {
+ pa_cvolume remapped;
+
/* When the 'absolute' bool is not set then we'll treat the volume
* as relative to the sink volume even in flat volume mode */
-
- pa_cvolume v = data->sink->reference_volume;
- pa_cvolume_remap(&v, &data->sink->channel_map, &data->channel_map);
- pa_sw_cvolume_multiply(&i->virtual_volume, &data->volume, &v);
+ remapped = data->sink->reference_volume;
+ pa_cvolume_remap(&remapped, &data->sink->channel_map, &data->channel_map);
+ pa_sw_cvolume_multiply(&i->volume, &data->volume, &remapped);
} else
- i->virtual_volume = data->volume;
+ i->volume = data->volume;
i->volume_factor = data->volume_factor;
- pa_cvolume_init(&i->soft_volume);
- memset(i->relative_volume, 0, sizeof(i->relative_volume));
+ i->volume_factor_sink = data->volume_factor_sink;
+ i->real_ratio = i->reference_ratio = data->volume;
+ pa_cvolume_reset(&i->soft_volume, i->sample_spec.channels);
+ pa_cvolume_reset(&i->real_ratio, i->sample_spec.channels);
i->save_volume = data->save_volume;
i->save_sink = data->save_sink;
i->save_muted = data->save_muted;
@@ -348,12 +370,15 @@ int pa_sink_input_new(
if (i->client)
pa_assert_se(pa_idxset_put(i->client->sink_inputs, i, NULL) >= 0);
- pa_log_info("Created input %u \"%s\" on %s with sample spec %s and channel map %s",
+ pt = pa_proplist_to_string_sep(i->proplist, "\n ");
+ pa_log_info("Created input %u \"%s\" on %s with sample spec %s and channel map %s\n %s",
i->index,
pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_MEDIA_NAME)),
i->sink->name,
pa_sample_spec_snprint(st, sizeof(st), &i->sample_spec),
- pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map));
+ pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map),
+ pt);
+ pa_xfree(pt);
/* Don't forget to call pa_sink_input_put! */
@@ -364,6 +389,7 @@ int pa_sink_input_new(
/* Called from main context */
static void update_n_corked(pa_sink_input *i, pa_sink_input_state_t state) {
pa_assert(i);
+ pa_assert_ctl_context();
if (!i->sink)
return;
@@ -378,6 +404,7 @@ static void update_n_corked(pa_sink_input *i, pa_sink_input_state_t state) {
static void sink_input_set_state(pa_sink_input *i, pa_sink_input_state_t state) {
pa_sink_input *ssync;
pa_assert(i);
+ pa_assert_ctl_context();
if (state == PA_SINK_INPUT_DRAINED)
state = PA_SINK_INPUT_RUNNING;
@@ -416,7 +443,9 @@ static void sink_input_set_state(pa_sink_input *i, pa_sink_input_state_t state)
void pa_sink_input_unlink(pa_sink_input *i) {
pa_bool_t linked;
pa_source_output *o, *p = NULL;
+
pa_assert(i);
+ pa_assert_ctl_context();
/* See pa_sink_unlink() for a couple of comments how this function
* works */
@@ -455,11 +484,8 @@ void pa_sink_input_unlink(pa_sink_input *i) {
if (linked && i->sink) {
/* We might need to update the sink's volume if we are in flat volume mode. */
- if (i->sink->flags & PA_SINK_FLAT_VOLUME) {
- pa_cvolume new_volume;
- pa_sink_update_flat_volume(i->sink, &new_volume);
- pa_sink_set_volume(i->sink, &new_volume, FALSE, FALSE, FALSE, FALSE);
- }
+ if (i->sink->flags & PA_SINK_FLAT_VOLUME)
+ pa_sink_set_volume(i->sink, NULL, FALSE, FALSE);
if (i->sink->asyncmsgq)
pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_REMOVE_INPUT, i, 0, NULL) == 0);
@@ -487,6 +513,7 @@ static void sink_input_free(pa_object *o) {
pa_sink_input* i = PA_SINK_INPUT(o);
pa_assert(i);
+ pa_assert_ctl_context();
pa_assert(pa_sink_input_refcnt(i) == 0);
if (PA_SINK_INPUT_IS_LINKED(i->state))
@@ -494,7 +521,10 @@ static void sink_input_free(pa_object *o) {
pa_log_info("Freeing input %u \"%s\"", i->index, pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_MEDIA_NAME)));
- pa_assert(!i->thread_info.attached);
+ /* Side note: this function must be able to destruct properly any
+ * kind of sink input in any state, even those which are
+ * "half-moved" or are connected to sinks that have no asyncmsgq
+ * and are hence half-destructed themselves! */
if (i->thread_info.ramp_info.envelope) {
pa_log_debug ("Freeing envelope\n");
@@ -524,7 +554,9 @@ static void sink_input_free(pa_object *o) {
/* Called from main context */
void pa_sink_input_put(pa_sink_input *i) {
pa_sink_input_state_t state;
+
pa_sink_input_assert_ref(i);
+ pa_assert_ctl_context();
pa_assert(i->state == PA_SINK_INPUT_INIT);
@@ -539,12 +571,10 @@ void pa_sink_input_put(pa_sink_input *i) {
i->state = state;
/* We might need to update the sink's volume if we are in flat volume mode. */
- if (i->sink->flags & PA_SINK_FLAT_VOLUME) {
- pa_cvolume new_volume;
- pa_sink_update_flat_volume(i->sink, &new_volume);
- pa_sink_set_volume(i->sink, &new_volume, FALSE, FALSE, FALSE, FALSE);
- } else
- pa_sink_input_set_relative_volume(i, &i->virtual_volume);
+ if (i->sink->flags & PA_SINK_FLAT_VOLUME)
+ pa_sink_set_volume(i->sink, NULL, FALSE, i->save_volume);
+ else
+ set_real_ratio(i, &i->volume);
i->thread_info.soft_volume = i->soft_volume;
i->thread_info.muted = i->muted;
@@ -560,6 +590,7 @@ void pa_sink_input_put(pa_sink_input *i) {
/* Called from main context */
void pa_sink_input_kill(pa_sink_input*i) {
pa_sink_input_assert_ref(i);
+ pa_assert_ctl_context();
pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
i->kill(i);
@@ -570,6 +601,7 @@ pa_usec_t pa_sink_input_get_latency(pa_sink_input *i, pa_usec_t *sink_latency) {
pa_usec_t r[2] = { 0, 0 };
pa_sink_input_assert_ref(i);
+ pa_assert_ctl_context();
pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_GET_LATENCY, r, 0, NULL) == 0);
@@ -585,13 +617,14 @@ pa_usec_t pa_sink_input_get_latency(pa_sink_input *i, pa_usec_t *sink_latency) {
/* Called from thread context */
void pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink frames */, pa_memchunk *chunk, pa_cvolume *volume) {
- pa_bool_t do_volume_adj_here;
+ pa_bool_t do_volume_adj_here, need_volume_factor_sink;
pa_bool_t volume_is_norm;
pa_bool_t ramping;
size_t block_size_max_sink, block_size_max_sink_input;
size_t ilength;
pa_sink_input_assert_ref(i);
+ pa_sink_input_assert_io_context(i);
pa_assert(PA_SINK_INPUT_IS_LINKED(i->thread_info.state));
pa_assert(pa_frame_aligned(slength, &i->sink->sample_spec));
pa_assert(chunk);
@@ -633,6 +666,7 @@ void pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink frames */, p
do_volume_adj_here = !pa_channel_map_equal(&i->channel_map, &i->sink->channel_map) || i->thread_info.ramp_info.is_ramping;
volume_is_norm = pa_cvolume_is_norm(&i->thread_info.soft_volume) && !i->thread_info.muted;
+ need_volume_factor_sink = !pa_cvolume_is_norm(&i->volume_factor_sink);
while (!pa_memblockq_is_readable(i->thread_info.render_memblockq)) {
pa_memchunk tchunk;
@@ -664,6 +698,7 @@ void pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink frames */, p
while (tchunk.length > 0) {
pa_memchunk wchunk;
+ pa_bool_t nvfs = need_volume_factor_sink;
wchunk = tchunk;
pa_memblock_ref(wchunk.memblock);
@@ -675,18 +710,41 @@ void pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink frames */, p
if (do_volume_adj_here && !volume_is_norm && !i->thread_info.ramp_info.is_ramping) {
pa_memchunk_make_writable(&wchunk, 0);
- if (i->thread_info.muted)
+ if (i->thread_info.muted) {
pa_silence_memchunk(&wchunk, &i->thread_info.sample_spec);
- else
+ nvfs = FALSE;
+
+ } else if (!i->thread_info.resampler && nvfs) {
+ pa_cvolume v;
+
+ /* If we don't need a resampler we can merge the
+ * post and the pre volume adjustment into one */
+
+ pa_sw_cvolume_multiply(&v, &i->thread_info.soft_volume, &i->volume_factor_sink);
+ pa_volume_memchunk(&wchunk, &i->thread_info.sample_spec, &v);
+ nvfs = FALSE;
+
+ } else
pa_volume_memchunk(&wchunk, &i->thread_info.sample_spec, &i->thread_info.soft_volume);
}
- if (!i->thread_info.resampler)
+ if (!i->thread_info.resampler) {
+
+ if (nvfs) {
+ pa_memchunk_make_writable(&wchunk, 0);
+ pa_volume_memchunk(&wchunk, &i->sink->sample_spec, &i->volume_factor_sink);
+ }
+
pa_memblockq_push_align(i->thread_info.render_memblockq, &wchunk);
- else {
+ } else {
pa_memchunk rchunk;
pa_resampler_run(i->thread_info.resampler, &wchunk, &rchunk);
+ if (nvfs) {
+ pa_memchunk_make_writable(&rchunk, 0);
+ pa_volume_memchunk(&rchunk, &i->sink->sample_spec, &i->volume_factor_sink);
+ }
+
/* pa_log_debug("pushing %lu", (unsigned long) rchunk.length); */
if (rchunk.memblock) {
@@ -746,8 +804,9 @@ void pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink frames */, p
/* Called from thread context */
void pa_sink_input_drop(pa_sink_input *i, size_t nbytes /* in sink sample spec */) {
- pa_sink_input_assert_ref(i);
+ pa_sink_input_assert_ref(i);
+ pa_sink_input_assert_io_context(i);
pa_assert(PA_SINK_INPUT_IS_LINKED(i->thread_info.state));
pa_assert(pa_frame_aligned(nbytes, &i->sink->sample_spec));
pa_assert(nbytes > 0);
@@ -761,8 +820,9 @@ void pa_sink_input_drop(pa_sink_input *i, size_t nbytes /* in sink sample spec *
void pa_sink_input_process_rewind(pa_sink_input *i, size_t nbytes /* in sink sample spec */) {
size_t lbq;
pa_bool_t called = FALSE;
- pa_sink_input_assert_ref(i);
+ pa_sink_input_assert_ref(i);
+ pa_sink_input_assert_io_context(i);
pa_assert(PA_SINK_INPUT_IS_LINKED(i->thread_info.state));
pa_assert(pa_frame_aligned(nbytes, &i->sink->sample_spec));
@@ -831,8 +891,28 @@ void pa_sink_input_process_rewind(pa_sink_input *i, size_t nbytes /* in sink sam
}
/* Called from thread context */
+size_t pa_sink_input_get_max_rewind(pa_sink_input *i) {
+ pa_sink_input_assert_ref(i);
+ pa_sink_input_assert_io_context(i);
+
+ return i->thread_info.resampler ? pa_resampler_request(i->thread_info.resampler, i->sink->thread_info.max_rewind) : i->sink->thread_info.max_rewind;
+}
+
+/* Called from thread context */
+size_t pa_sink_input_get_max_request(pa_sink_input *i) {
+ pa_sink_input_assert_ref(i);
+ pa_sink_input_assert_io_context(i);
+
+ /* We're not verifying the status here, to allow this to be called
+ * in the state change handler between _INIT and _RUNNING */
+
+ return i->thread_info.resampler ? pa_resampler_request(i->thread_info.resampler, i->sink->thread_info.max_request) : i->sink->thread_info.max_request;
+}
+
+/* Called from thread context */
void pa_sink_input_update_max_rewind(pa_sink_input *i, size_t nbytes /* in the sink's sample spec */) {
pa_sink_input_assert_ref(i);
+ pa_sink_input_assert_io_context(i);
pa_assert(PA_SINK_INPUT_IS_LINKED(i->thread_info.state));
pa_assert(pa_frame_aligned(nbytes, &i->sink->sample_spec));
@@ -845,6 +925,7 @@ void pa_sink_input_update_max_rewind(pa_sink_input *i, size_t nbytes /* in the
/* Called from thread context */
void pa_sink_input_update_max_request(pa_sink_input *i, size_t nbytes /* in the sink's sample spec */) {
pa_sink_input_assert_ref(i);
+ pa_sink_input_assert_io_context(i);
pa_assert(PA_SINK_INPUT_IS_LINKED(i->thread_info.state));
pa_assert(pa_frame_aligned(nbytes, &i->sink->sample_spec));
@@ -855,15 +936,16 @@ void pa_sink_input_update_max_request(pa_sink_input *i, size_t nbytes /* in the
/* Called from thread context */
pa_usec_t pa_sink_input_set_requested_latency_within_thread(pa_sink_input *i, pa_usec_t usec) {
pa_sink_input_assert_ref(i);
+ pa_sink_input_assert_io_context(i);
if (!(i->sink->flags & PA_SINK_DYNAMIC_LATENCY))
- usec = i->sink->fixed_latency;
+ usec = i->sink->thread_info.fixed_latency;
if (usec != (pa_usec_t) -1)
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);
+ pa_sink_invalidate_requested_latency(i->sink, TRUE);
return usec;
}
@@ -871,6 +953,7 @@ pa_usec_t pa_sink_input_set_requested_latency_within_thread(pa_sink_input *i, pa
/* Called from main context */
pa_usec_t pa_sink_input_set_requested_latency(pa_sink_input *i, pa_usec_t usec) {
pa_sink_input_assert_ref(i);
+ pa_assert_ctl_context();
if (PA_SINK_INPUT_IS_LINKED(i->state) && i->sink) {
pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_REQUESTED_LATENCY, &usec, 0, NULL) == 0);
@@ -882,7 +965,7 @@ pa_usec_t pa_sink_input_set_requested_latency(pa_sink_input *i, pa_usec_t usec)
if (i->sink) {
if (!(i->sink->flags & PA_SINK_DYNAMIC_LATENCY))
- usec = i->sink->fixed_latency;
+ usec = pa_sink_get_fixed_latency(i->sink);
if (usec != (pa_usec_t) -1) {
pa_usec_t min_latency, max_latency;
@@ -899,6 +982,7 @@ pa_usec_t pa_sink_input_set_requested_latency(pa_sink_input *i, pa_usec_t usec)
/* Called from main context */
pa_usec_t pa_sink_input_get_requested_latency(pa_sink_input *i) {
pa_sink_input_assert_ref(i);
+ pa_assert_ctl_context();
if (PA_SINK_INPUT_IS_LINKED(i->state) && i->sink) {
pa_usec_t usec = 0;
@@ -913,73 +997,44 @@ pa_usec_t pa_sink_input_get_requested_latency(pa_sink_input *i) {
}
/* Called from main context */
-void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume, pa_bool_t save, pa_bool_t absolute) {
- /* test ramping -> return pa_sink_input_set_volume_with_ramping(i, volume, save, absolute, 2000 * PA_USEC_PER_MSEC); */
- return pa_sink_input_set_volume_with_ramping(i, volume, save, absolute, 0);
-}
-
-/* Called from main context */
-pa_cvolume *pa_sink_input_get_volume(pa_sink_input *i, pa_cvolume *volume, pa_bool_t absolute) {
+static void set_real_ratio(pa_sink_input *i, const pa_cvolume *v) {
pa_sink_input_assert_ref(i);
+ pa_assert_ctl_context();
pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
+ pa_assert(!v || pa_cvolume_compatible(v, &i->sample_spec));
- if ((i->sink->flags & PA_SINK_FLAT_VOLUME) && !absolute) {
- pa_cvolume v = i->sink->reference_volume;
- pa_cvolume_remap(&v, &i->sink->channel_map, &i->channel_map);
- pa_sw_cvolume_divide(volume, &i->virtual_volume, &v);
- } else
- *volume = i->virtual_volume;
+ /* This basically calculates:
+ *
+ * i->real_ratio := v
+ * i->soft_volume := i->real_ratio * i->volume_factor */
- return volume;
+ if (v)
+ i->real_ratio = *v;
+ else
+ pa_cvolume_reset(&i->real_ratio, i->sample_spec.channels);
+
+ pa_sw_cvolume_multiply(&i->soft_volume, &i->real_ratio, &i->volume_factor);
+ /* We don't copy the data to the thread_info data. That's left for someone else to do */
}
/* Called from main context */
-pa_cvolume *pa_sink_input_get_relative_volume(pa_sink_input *i, pa_cvolume *v) {
- unsigned c;
-
- pa_sink_input_assert_ref(i);
- pa_assert(v);
- pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
-
- /* This always returns the relative volume. Converts the float
- * version into a pa_cvolume */
-
- v->channels = i->sample_spec.channels;
-
- for (c = 0; c < v->channels; c++)
- v->values[c] = pa_sw_volume_from_linear(i->relative_volume[c]);
-
- return v;
+void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume, pa_bool_t save, pa_bool_t absolute) {
+ /* test ramping -> return pa_sink_input_set_volume_with_ramping(i, volume, save, absolute, 2000 * PA_USEC_PER_MSEC); */
+ return pa_sink_input_set_volume_with_ramping(i, volume, save, absolute, 0);
}
/* Called from main context */
-void pa_sink_input_set_relative_volume(pa_sink_input *i, const pa_cvolume *v) {
- unsigned c;
- pa_cvolume _v;
-
+pa_cvolume *pa_sink_input_get_volume(pa_sink_input *i, pa_cvolume *volume, pa_bool_t absolute) {
pa_sink_input_assert_ref(i);
+ pa_assert_ctl_context();
pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
- pa_assert(!v || pa_cvolume_compatible(v, &i->sample_spec));
- if (!v)
- v = pa_cvolume_reset(&_v, i->sample_spec.channels);
-
- /* This basically calculates:
- *
- * i->relative_volume := v
- * i->soft_volume := i->relative_volume * i->volume_factor */
-
- i->soft_volume.channels = i->sample_spec.channels;
-
- for (c = 0; c < i->sample_spec.channels; c++) {
- i->relative_volume[c] = pa_sw_volume_to_linear(v->values[c]);
-
- i->soft_volume.values[c] = pa_sw_volume_from_linear(
- i->relative_volume[c] *
- pa_sw_volume_to_linear(i->volume_factor.values[c]));
- }
+ if (absolute || !(i->sink->flags & PA_SINK_FLAT_VOLUME))
+ *volume = i->volume;
+ else
+ *volume = i->reference_ratio;
- /* We don't copy the data to the thread_info data. That's left for someone else to do */
+ return volume;
}
/* Called from main context */
@@ -991,6 +1046,7 @@ void pa_sink_input_set_mute(pa_sink_input *i, pa_bool_t mute, pa_bool_t save) {
/* Called from main context */
pa_bool_t pa_sink_input_get_mute(pa_sink_input *i) {
pa_sink_input_assert_ref(i);
+ pa_assert_ctl_context();
pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
return i->muted;
@@ -999,6 +1055,7 @@ pa_bool_t pa_sink_input_get_mute(pa_sink_input *i) {
/* Called from main thread */
void pa_sink_input_update_proplist(pa_sink_input *i, pa_update_mode_t mode, pa_proplist *p) {
pa_sink_input_assert_ref(i);
+ pa_assert_ctl_context();
if (p)
pa_proplist_update(i->proplist, mode, p);
@@ -1012,6 +1069,7 @@ void pa_sink_input_update_proplist(pa_sink_input *i, pa_update_mode_t mode, pa_p
/* Called from main context */
void pa_sink_input_cork(pa_sink_input *i, pa_bool_t b) {
pa_sink_input_assert_ref(i);
+ pa_assert_ctl_context();
pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
sink_input_set_state(i, b ? PA_SINK_INPUT_CORKED : PA_SINK_INPUT_RUNNING);
@@ -1020,6 +1078,7 @@ void pa_sink_input_cork(pa_sink_input *i, pa_bool_t b) {
/* Called from main context */
int pa_sink_input_set_rate(pa_sink_input *i, uint32_t rate) {
pa_sink_input_assert_ref(i);
+ pa_assert_ctl_context();
pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
pa_return_val_if_fail(i->thread_info.resampler, -PA_ERR_BADSTATE);
@@ -1038,13 +1097,14 @@ int pa_sink_input_set_rate(pa_sink_input *i, uint32_t rate) {
void pa_sink_input_set_name(pa_sink_input *i, const char *name) {
const char *old;
pa_sink_input_assert_ref(i);
+ pa_assert_ctl_context();
if (!name && !pa_proplist_contains(i->proplist, PA_PROP_MEDIA_NAME))
return;
old = pa_proplist_gets(i->proplist, PA_PROP_MEDIA_NAME);
- if (old && name && !strcmp(old, name))
+ if (old && name && pa_streq(old, name))
return;
if (name)
@@ -1061,6 +1121,7 @@ void pa_sink_input_set_name(pa_sink_input *i, const char *name) {
/* Called from main context */
pa_resample_method_t pa_sink_input_get_resample_method(pa_sink_input *i) {
pa_sink_input_assert_ref(i);
+ pa_assert_ctl_context();
return i->actual_resample_method;
}
@@ -1068,6 +1129,7 @@ pa_resample_method_t pa_sink_input_get_resample_method(pa_sink_input *i) {
/* Called from main context */
pa_bool_t pa_sink_input_may_move(pa_sink_input *i) {
pa_sink_input_assert_ref(i);
+ pa_assert_ctl_context();
pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
if (i->flags & PA_SINK_INPUT_DONT_MOVE)
@@ -1084,6 +1146,7 @@ pa_bool_t pa_sink_input_may_move(pa_sink_input *i) {
/* Called from main context */
pa_bool_t pa_sink_input_may_move_to(pa_sink_input *i, pa_sink *dest) {
pa_sink_input_assert_ref(i);
+ pa_assert_ctl_context();
pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
pa_sink_assert_ref(dest);
@@ -1108,10 +1171,10 @@ pa_bool_t pa_sink_input_may_move_to(pa_sink_input *i, pa_sink *dest) {
/* Called from main context */
int pa_sink_input_start_move(pa_sink_input *i) {
pa_source_output *o, *p = NULL;
- pa_sink *origin;
int r;
pa_sink_input_assert_ref(i);
+ pa_assert_ctl_context();
pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
pa_assert(i->sink);
@@ -1121,8 +1184,6 @@ int pa_sink_input_start_move(pa_sink_input *i) {
if ((r = pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_START], i)) < 0)
return r;
- origin = i->sink;
-
/* Kill directly connected outputs */
while ((o = pa_idxset_first(i->direct_outputs, NULL))) {
pa_assert(o != p);
@@ -1136,24 +1197,15 @@ int pa_sink_input_start_move(pa_sink_input *i) {
if (pa_sink_input_get_state(i) == PA_SINK_INPUT_CORKED)
pa_assert_se(i->sink->n_corked-- >= 1);
- if (i->sink->flags & PA_SINK_FLAT_VOLUME) {
- pa_cvolume new_volume;
-
- /* Make the virtual volume relative */
- pa_sink_input_get_relative_volume(i, &i->virtual_volume);
-
- /* And reset the the relative volume */
- pa_sink_input_set_relative_volume(i, NULL);
-
+ if (i->sink->flags & PA_SINK_FLAT_VOLUME)
/* We might need to update the sink's volume if we are in flat
* volume mode. */
- pa_sink_update_flat_volume(i->sink, &new_volume);
- pa_sink_set_volume(i->sink, &new_volume, FALSE, FALSE, FALSE, FALSE);
- }
+ pa_sink_set_volume(i->sink, NULL, FALSE, FALSE);
pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_START_MOVE, i, 0, NULL) == 0);
pa_sink_update_status(i->sink);
+ pa_cvolume_remap(&i->volume_factor_sink, &i->sink->channel_map, &i->channel_map);
i->sink = NULL;
pa_sink_input_unref(i);
@@ -1166,6 +1218,7 @@ int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest, pa_bool_t save) {
pa_resampler *new_resampler;
pa_sink_input_assert_ref(i);
+ pa_assert_ctl_context();
pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
pa_assert(!i->sink);
pa_sink_assert_ref(dest);
@@ -1207,6 +1260,8 @@ int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest, pa_bool_t save) {
i->save_sink = save;
pa_idxset_put(dest->inputs, pa_sink_input_ref(i), NULL);
+ pa_cvolume_remap(&i->volume_factor_sink, &i->channel_map, &i->sink->channel_map);
+
if (pa_sink_input_get_state(i) == PA_SINK_INPUT_CORKED)
i->sink->n_corked++;
@@ -1232,16 +1287,15 @@ int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest, pa_bool_t save) {
pa_sink_update_status(dest);
if (i->sink->flags & PA_SINK_FLAT_VOLUME) {
- pa_cvolume new_volume;
+ pa_cvolume remapped;
- /* Make relative volume absolute again */
- pa_cvolume t = dest->reference_volume;
- pa_cvolume_remap(&t, &dest->channel_map, &i->channel_map);
- pa_sw_cvolume_multiply(&i->virtual_volume, &i->virtual_volume, &t);
+ /* Make relative volumes absolute */
+ remapped = dest->reference_volume;
+ pa_cvolume_remap(&remapped, &dest->channel_map, &i->channel_map);
+ pa_sw_cvolume_multiply(&i->volume, &i->reference_ratio, &remapped);
/* We might need to update the sink's volume if we are in flat volume mode. */
- pa_sink_update_flat_volume(i->sink, &new_volume);
- pa_sink_set_volume(i->sink, &new_volume, FALSE, FALSE, FALSE, FALSE);
+ pa_sink_set_volume(i->sink, NULL, FALSE, i->save_volume);
}
pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_FINISH_MOVE, i, 0, NULL) == 0);
@@ -1250,16 +1304,39 @@ int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest, pa_bool_t save) {
/* Notify everyone */
pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_FINISH], i);
+
+ if (i->volume_changed)
+ i->volume_changed(i);
+
pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
return 0;
}
/* Called from main context */
+void pa_sink_input_fail_move(pa_sink_input *i) {
+
+ pa_sink_input_assert_ref(i);
+ pa_assert_ctl_context();
+ pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
+ pa_assert(!i->sink);
+
+ /* Check if someone wants this sink input? */
+ if (pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_FAIL], i) == PA_HOOK_STOP)
+ return;
+
+ if (i->moving)
+ i->moving(i, NULL);
+
+ pa_sink_input_kill(i);
+}
+
+/* Called from main context */
int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, pa_bool_t save) {
int r;
pa_sink_input_assert_ref(i);
+ pa_assert_ctl_context();
pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
pa_assert(i->sink);
pa_sink_assert_ref(dest);
@@ -1278,6 +1355,7 @@ int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, pa_bool_t save) {
}
if ((r = pa_sink_input_finish_move(i, dest, save)) < 0) {
+ pa_sink_input_fail_move(i);
pa_sink_input_unref(i);
return r;
}
@@ -1290,7 +1368,9 @@ int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, pa_bool_t save) {
/* Called from IO thread context */
void pa_sink_input_set_state_within_thread(pa_sink_input *i, pa_sink_input_state_t state) {
pa_bool_t corking, uncorking;
+
pa_sink_input_assert_ref(i);
+ pa_sink_input_assert_io_context(i);
if (state == i->thread_info.state)
return;
@@ -1428,6 +1508,7 @@ int pa_sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t
/* Called from main thread */
pa_sink_input_state_t pa_sink_input_get_state(pa_sink_input *i) {
pa_sink_input_assert_ref(i);
+ pa_assert_ctl_context();
if (i->state == PA_SINK_INPUT_RUNNING || i->state == PA_SINK_INPUT_DRAINED)
return pa_atomic_load(&i->thread_info.drained) ? PA_SINK_INPUT_DRAINED : PA_SINK_INPUT_RUNNING;
@@ -1438,6 +1519,7 @@ pa_sink_input_state_t pa_sink_input_get_state(pa_sink_input *i) {
/* Called from IO context */
pa_bool_t pa_sink_input_safe_to_remove(pa_sink_input *i) {
pa_sink_input_assert_ref(i);
+ pa_sink_input_assert_io_context(i);
if (PA_SINK_INPUT_IS_LINKED(i->thread_info.state))
return pa_memblockq_is_empty(i->thread_info.render_memblockq);
@@ -1446,7 +1528,13 @@ pa_bool_t pa_sink_input_safe_to_remove(pa_sink_input *i) {
}
/* Called from IO context */
-void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes /* in our sample spec */, pa_bool_t rewrite, pa_bool_t flush, pa_bool_t dont_rewind_render) {
+void pa_sink_input_request_rewind(
+ pa_sink_input *i,
+ size_t nbytes /* in our sample spec */,
+ pa_bool_t rewrite,
+ pa_bool_t flush,
+ pa_bool_t dont_rewind_render) {
+
size_t lbq;
/* If 'rewrite' is TRUE the sink is rewound as far as requested
@@ -1461,18 +1549,20 @@ void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes /* in our sam
* dont_rewind_render is TRUE then the render memblockq is not
* rewound. */
- pa_sink_input_assert_ref(i);
-
- nbytes = PA_MAX(i->thread_info.rewrite_nbytes, nbytes);
+ /* nbytes = 0 means maximum rewind request */
-/* pa_log_debug("request rewrite %lu", (unsigned long) nbytes); */
+ pa_sink_input_assert_ref(i);
+ pa_sink_input_assert_io_context(i);
+ pa_assert(rewrite || flush);
+ pa_assert(!dont_rewind_render || !rewrite);
/* We don't take rewind requests while we are corked */
if (i->thread_info.state == PA_SINK_INPUT_CORKED)
return;
- pa_assert(rewrite || flush);
- pa_assert(!dont_rewind_render || !rewrite);
+ nbytes = PA_MAX(i->thread_info.rewrite_nbytes, nbytes);
+
+ /* pa_log_debug("request rewrite %zu", nbytes); */
/* Calculate how much we can rewind locally without having to
* touch the sink */
@@ -1492,6 +1582,7 @@ void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes /* in our sam
nbytes = pa_resampler_request(i->thread_info.resampler, nbytes);
}
+ /* Remember how much we actually want to rewrite */
if (i->thread_info.rewrite_nbytes != (size_t) -1) {
if (rewrite) {
/* Make sure to not overwrite over underruns */
@@ -1528,8 +1619,11 @@ void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes /* in our sam
/* Called from main context */
pa_memchunk* pa_sink_input_get_silence(pa_sink_input *i, pa_memchunk *ret) {
pa_sink_input_assert_ref(i);
+ pa_assert_ctl_context();
pa_assert(ret);
+ /* FIXME: Shouldn't access resampler object from main context! */
+
pa_silence_memchunk_get(
&i->core->silence_cache,
i->core->mempool,
@@ -1546,6 +1640,7 @@ void pa_sink_input_send_event(pa_sink_input *i, const char *event, pa_proplist *
pa_sink_input_send_event_hook_data hook_data;
pa_sink_input_assert_ref(i);
+ pa_assert_ctl_context();
pa_assert(event);
if (!i->send_event)
@@ -1701,29 +1796,42 @@ static void sink_input_rewind_ramp_info(pa_sink_input *i, size_t nbytes) {
void pa_sink_input_set_volume_with_ramping(pa_sink_input *i, const pa_cvolume *volume, pa_bool_t save, pa_bool_t absolute, pa_usec_t t){
pa_cvolume v;
pa_volume_t previous_virtual_volume, target_virtual_volume;
- pa_sink_input_assert_ref(i);
+ pa_sink_input_assert_ref(i);
+ pa_assert_ctl_context();
pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
pa_assert(volume);
pa_assert(pa_cvolume_valid(volume));
- pa_assert(pa_cvolume_compatible(volume, &i->sample_spec));
+ pa_assert(volume->channels == 1 || pa_cvolume_compatible(volume, &i->sample_spec));
if ((i->sink->flags & PA_SINK_FLAT_VOLUME) && !absolute) {
v = i->sink->reference_volume;
pa_cvolume_remap(&v, &i->sink->channel_map, &i->channel_map);
- volume = pa_sw_cvolume_multiply(&v, &v, volume);
+
+ if (pa_cvolume_compatible(volume, &i->sample_spec))
+ volume = pa_sw_cvolume_multiply(&v, &v, volume);
+ else
+ volume = pa_sw_cvolume_multiply_scalar(&v, &v, pa_cvolume_max(volume));
+ } else {
+ if (!pa_cvolume_compatible(volume, &i->sample_spec)) {
+ v = i->volume;
+ volume = pa_cvolume_scale(&v, pa_cvolume_max(volume));
+ }
}
- if (pa_cvolume_equal(volume, &i->virtual_volume))
+ if (pa_cvolume_equal(volume, &i->volume)) {
+ i->save_volume = i->save_volume || save;
return;
+ }
- previous_virtual_volume = pa_cvolume_avg(&i->virtual_volume);
+ previous_virtual_volume = pa_cvolume_avg(&i->volume);
target_virtual_volume = pa_cvolume_avg(volume);
+
if (t > 0 && target_virtual_volume > 0)
pa_log_debug("SetVolumeWithRamping: Virtual Volume From %u=%f to %u=%f\n", previous_virtual_volume, pa_sw_volume_to_linear(previous_virtual_volume),
target_virtual_volume, pa_sw_volume_to_linear(target_virtual_volume));
- i->virtual_volume = *volume;
+ i->volume = *volume;
i->save_volume = save;
/* Set this flag before the following code modify i->thread_info.soft_volume */
@@ -1731,22 +1839,15 @@ void pa_sink_input_set_volume_with_ramping(pa_sink_input *i, const pa_cvolume *v
pa_atomic_store(&i->before_ramping_v, 1);
if (i->sink->flags & PA_SINK_FLAT_VOLUME) {
- pa_cvolume new_volume;
-
/* We are in flat volume mode, so let's update all sink input
* volumes and update the flat volume of the sink */
- pa_sink_update_flat_volume(i->sink, &new_volume);
- pa_sink_set_volume(i->sink, &new_volume, FALSE, TRUE, FALSE, FALSE);
+ pa_sink_set_volume(i->sink, NULL, TRUE, save);
} else {
-
/* OK, we are in normal volume mode. The volume only affects
* ourselves */
- pa_sink_input_set_relative_volume(i, volume);
-
- /* Hooks have the ability to play games with i->soft_volume */
- pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_SET_VOLUME], i);
+ set_real_ratio(i, volume);
/* Copy the new soft_volume to the thread_info struct */
pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_SOFT_VOLUME, NULL, 0, NULL) == 0);
@@ -1755,6 +1856,10 @@ void pa_sink_input_set_volume_with_ramping(pa_sink_input *i, const pa_cvolume *v
if (t > 0 && target_virtual_volume > 0)
sink_input_set_ramping_info(i, previous_virtual_volume, target_virtual_volume, t);
+ /* The volume changed, let's tell people so */
+ if (i->volume_changed)
+ i->volume_changed(i);
+
/* The virtual volume changed, let's tell people so */
pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
}
diff --git a/src/pulsecore/sink-input.h b/src/pulsecore/sink-input.h
index 036806fc..56ac3d60 100644
--- a/src/pulsecore/sink-input.h
+++ b/src/pulsecore/sink-input.h
@@ -43,6 +43,7 @@ typedef enum pa_sink_input_state {
PA_SINK_INPUT_RUNNING, /*< The stream is alive and kicking */
PA_SINK_INPUT_CORKED, /*< The stream was corked on user request */
PA_SINK_INPUT_UNLINKED /*< The stream is dead */
+ /* FIXME: we need a state for MOVING here */
} pa_sink_input_state_t;
static inline pa_bool_t PA_SINK_INPUT_IS_LINKED(pa_sink_input_state_t x) {
@@ -59,7 +60,8 @@ typedef enum pa_sink_input_flags {
PA_SINK_INPUT_FIX_RATE = 64,
PA_SINK_INPUT_FIX_CHANNELS = 128,
PA_SINK_INPUT_DONT_INHIBIT_AUTO_SUSPEND = 256,
- PA_SINK_INPUT_FAIL_ON_SUSPEND = 512
+ PA_SINK_INPUT_NO_CREATE_ON_SUSPEND = 512,
+ PA_SINK_INPUT_KILL_ON_SUSPEND = 1024
} pa_sink_input_flags_t;
struct pa_sink_input {
@@ -93,10 +95,14 @@ struct pa_sink_input {
pa_sink_input *sync_prev, *sync_next;
/* Also see http://pulseaudio.org/wiki/InternalVolumes */
- pa_cvolume virtual_volume; /* The volume clients are informed about */
- pa_cvolume volume_factor; /* An internally used volume factor that can be used by modules to apply effects and suchlike without having that visible to the outside */
- double relative_volume[PA_CHANNELS_MAX]; /* The calculated volume relative to the sink volume as linear factors. */
- pa_cvolume soft_volume; /* The internal software volume we apply to all PCM data while it passes through. Usually calculated as relative_volume * volume_factor */
+ pa_cvolume volume; /* The volume clients are informed about */
+ pa_cvolume reference_ratio; /* The ratio of the stream's volume to the sink's reference volume */
+ pa_cvolume real_ratio; /* The ratio of the stream's volume to the sink's real volume */
+ pa_cvolume volume_factor; /* An internally used volume factor that can be used by modules to apply effects and suchlike without having that visible to the outside */
+ pa_cvolume soft_volume; /* The internal software volume we apply to all PCM data while it passes through. Usually calculated as real_ratio * volume_factor */
+
+ pa_cvolume volume_factor_sink; /* A second volume factor in format of the sink this stream is connected to */
+
pa_bool_t muted:1;
/* if TRUE then the source we are connected to and/or the volume
@@ -138,6 +144,10 @@ struct pa_sink_input {
* from IO context. */
void (*update_sink_latency_range) (pa_sink_input *i); /* may be NULL */
+ /* Called whenver the fixed latency of the sink changes, if there
+ * is one. Called from IO context. */
+ void (*update_sink_fixed_latency) (pa_sink_input *i); /* may be NULL */
+
/* If non-NULL this function is called when the input is first
* connected to a sink or when the rtpoll/asyncmsgq fields
* change. You usually don't need to implement this function
@@ -160,7 +170,9 @@ struct pa_sink_input {
/* If non-NULL called whenever the sink input is moved to a new
* sink. Called from main context after the sink input has been
* detached from the old sink and before it has been attached to
- * the new sink. */
+ * the new sink. If dest is NULL the move was executed in two
+ * phases and the second one failed; the stream will be destroyed
+ * after this call. */
void (*moving) (pa_sink_input *i, pa_sink *dest); /* may be NULL */
/* Supposed to unlink and destroy this stream. Called from main
@@ -183,8 +195,16 @@ struct pa_sink_input {
pa_bool_t (*may_move_to) (pa_sink_input *i, pa_sink *s); /* may be NULL */
/* If non-NULL this function is used to dispatch asynchronous
- * control events. */
- void (*send_event)(pa_sink_input *i, const char *event, pa_proplist* data);
+ * control events. Called from main context. */
+ void (*send_event)(pa_sink_input *i, const char *event, pa_proplist* data); /* may be NULL */
+
+ /* If non-NULL this function is called whenever the sink input
+ * volume changes. Called from main context */
+ void (*volume_changed)(pa_sink_input *i); /* may be NULL */
+
+ /* If non-NULL this function is called whenever the sink input
+ * mute status changes. Called from main context */
+ void (*mute_changed)(pa_sink_input *i); /* may be NULL */
struct {
pa_sink_input_state_t state;
@@ -195,7 +215,7 @@ struct pa_sink_input {
pa_bool_t attached:1; /* True only between ->attach() and ->detach() calls */
- /* 0: rewrite nothing, (size_t) -1: rewrite everything, otherwise how many bytes to rewrite */
+ /* rewrite_nbytes: 0: rewrite nothing, (size_t) -1: rewrite everything, otherwise how many bytes to rewrite */
pa_bool_t rewrite_flush:1, dont_rewind_render:1;
size_t rewrite_nbytes;
uint64_t underrun_for, playing_for;
@@ -233,7 +253,7 @@ struct pa_sink_input {
void *userdata;
};
-PA_DECLARE_CLASS(pa_sink_input);
+PA_DECLARE_PUBLIC_CLASS(pa_sink_input);
#define PA_SINK_INPUT(o) pa_sink_input_cast(o)
enum {
@@ -255,6 +275,8 @@ typedef struct pa_sink_input_send_event_hook_data {
} pa_sink_input_send_event_hook_data;
typedef struct pa_sink_input_new_data {
+ pa_sink_input_flags_t flags;
+
pa_proplist *proplist;
const char *driver;
@@ -270,13 +292,13 @@ typedef struct pa_sink_input_new_data {
pa_sample_spec sample_spec;
pa_channel_map channel_map;
- pa_cvolume volume, volume_factor;
+ pa_cvolume volume, volume_factor, volume_factor_sink;
pa_bool_t muted:1;
pa_bool_t sample_spec_is_set:1;
pa_bool_t channel_map_is_set:1;
- pa_bool_t volume_is_set:1, volume_factor_is_set:1;
+ pa_bool_t volume_is_set:1, volume_factor_is_set:1, volume_factor_sink_is_set:1;
pa_bool_t muted_is_set:1;
pa_bool_t volume_is_absolute:1;
@@ -289,6 +311,7 @@ void pa_sink_input_new_data_set_sample_spec(pa_sink_input_new_data *data, const
void pa_sink_input_new_data_set_channel_map(pa_sink_input_new_data *data, const pa_channel_map *map);
void pa_sink_input_new_data_set_volume(pa_sink_input_new_data *data, const pa_cvolume *volume);
void pa_sink_input_new_data_apply_volume_factor(pa_sink_input_new_data *data, const pa_cvolume *volume_factor);
+void pa_sink_input_new_data_apply_volume_factor_sink(pa_sink_input_new_data *data, const pa_cvolume *volume_factor);
void pa_sink_input_new_data_set_muted(pa_sink_input_new_data *data, pa_bool_t mute);
void pa_sink_input_new_data_done(pa_sink_input_new_data *data);
@@ -297,8 +320,7 @@ void pa_sink_input_new_data_done(pa_sink_input_new_data *data);
int pa_sink_input_new(
pa_sink_input **i,
pa_core *core,
- pa_sink_input_new_data *data,
- pa_sink_input_flags_t flags);
+ pa_sink_input_new_data *data);
void pa_sink_input_put(pa_sink_input *i);
void pa_sink_input_unlink(pa_sink_input* i);
@@ -320,6 +342,10 @@ void pa_sink_input_cork(pa_sink_input *i, pa_bool_t b);
int pa_sink_input_set_rate(pa_sink_input *i, uint32_t rate);
+/* This returns the sink's fields converted into out sample type */
+size_t pa_sink_input_get_max_rewind(pa_sink_input *i);
+size_t pa_sink_input_get_max_request(pa_sink_input *i);
+
/* Callable by everyone from main thread*/
/* External code may request disconnection with this function */
@@ -330,8 +356,6 @@ pa_usec_t pa_sink_input_get_latency(pa_sink_input *i, pa_usec_t *sink_latency);
void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume, pa_bool_t save, pa_bool_t absolute);
pa_cvolume *pa_sink_input_get_volume(pa_sink_input *i, pa_cvolume *volume, pa_bool_t absolute);
-pa_cvolume *pa_sink_input_get_relative_volume(pa_sink_input *i, pa_cvolume *v);
-
void pa_sink_input_set_mute(pa_sink_input *i, pa_bool_t mute, pa_bool_t save);
pa_bool_t pa_sink_input_get_mute(pa_sink_input *i);
@@ -350,6 +374,7 @@ pa_bool_t pa_sink_input_may_move_to(pa_sink_input *i, pa_sink *dest); /* may thi
* new sink */
int pa_sink_input_start_move(pa_sink_input *i);
int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest, pa_bool_t save);
+void pa_sink_input_fail_move(pa_sink_input *i);
pa_sink_input_state_t pa_sink_input_get_state(pa_sink_input *i);
@@ -373,8 +398,8 @@ pa_bool_t pa_sink_input_safe_to_remove(pa_sink_input *i);
pa_memchunk* pa_sink_input_get_silence(pa_sink_input *i, pa_memchunk *ret);
-/* To be used by sink.c only */
-void pa_sink_input_set_relative_volume(pa_sink_input *i, const pa_cvolume *v);
+#define pa_sink_input_assert_io_context(s) \
+ pa_assert(pa_thread_mq_get() || !PA_SINK_INPUT_IS_LINKED((s)->state))
/* Volume ramping*/
void pa_sink_input_set_volume_with_ramping(pa_sink_input *i, const pa_cvolume *volume, pa_bool_t save, pa_bool_t absolute, pa_usec_t t);
diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
index 29500718..24fad34d 100644
--- a/src/pulsecore/sink.c
+++ b/src/pulsecore/sink.c
@@ -52,14 +52,14 @@
#define ABSOLUTE_MAX_LATENCY (10*PA_USEC_PER_SEC)
#define DEFAULT_FIXED_LATENCY (250*PA_USEC_PER_MSEC)
-static PA_DEFINE_CHECK_TYPE(pa_sink, pa_msgobject);
+PA_DEFINE_PUBLIC_CLASS(pa_sink, pa_msgobject);
static void sink_free(pa_object *s);
pa_sink_new_data* pa_sink_new_data_init(pa_sink_new_data *data) {
pa_assert(data);
- memset(data, 0, sizeof(*data));
+ pa_zero(*data);
data->proplist = pa_proplist_new();
return data;
@@ -177,6 +177,7 @@ pa_sink* pa_sink_new(
pa_assert(core);
pa_assert(data);
pa_assert(data->name);
+ pa_assert_ctl_context();
s = pa_msgobject_new(pa_sink);
@@ -211,7 +212,7 @@ pa_sink* pa_sink_new(
pa_cvolume_reset(&data->volume, data->sample_spec.channels);
pa_return_null_if_fail(pa_cvolume_valid(&data->volume));
- pa_return_null_if_fail(data->volume.channels == data->sample_spec.channels);
+ pa_return_null_if_fail(pa_cvolume_compatible(&data->volume, &data->sample_spec));
if (!data->muted_is_set)
data->muted = FALSE;
@@ -235,6 +236,7 @@ pa_sink* pa_sink_new(
s->core = core;
s->state = PA_SINK_INIT;
s->flags = flags;
+ s->priority = 0;
s->suspend_cause = 0;
s->name = pa_xstrdup(name);
s->proplist = pa_proplist_copy(data->proplist);
@@ -242,26 +244,25 @@ pa_sink* pa_sink_new(
s->module = data->module;
s->card = data->card;
+ s->priority = pa_device_init_priority(s->proplist);
+
s->sample_spec = data->sample_spec;
s->channel_map = data->channel_map;
s->inputs = pa_idxset_new(NULL, NULL);
s->n_corked = 0;
- s->reference_volume = s->virtual_volume = data->volume;
+ s->reference_volume = s->real_volume = data->volume;
pa_cvolume_reset(&s->soft_volume, s->sample_spec.channels);
s->base_volume = PA_VOLUME_NORM;
s->n_volume_steps = PA_VOLUME_NORM+1;
s->muted = data->muted;
s->refresh_volume = s->refresh_muted = FALSE;
- s->fixed_latency = flags & PA_SINK_DYNAMIC_LATENCY ? 0 : DEFAULT_FIXED_LATENCY;
-
reset_callbacks(s);
s->userdata = NULL;
s->asyncmsgq = NULL;
- s->rtpoll = NULL;
/* As a minor optimization we just steal the list instead of
* copying it here */
@@ -294,6 +295,7 @@ pa_sink* pa_sink_new(
&s->sample_spec,
0);
+ s->thread_info.rtpoll = NULL;
s->thread_info.inputs = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
s->thread_info.soft_volume = s->soft_volume;
s->thread_info.soft_muted = s->muted;
@@ -306,7 +308,9 @@ pa_sink* pa_sink_new(
s->thread_info.requested_latency = 0;
s->thread_info.min_latency = ABSOLUTE_MIN_LATENCY;
s->thread_info.max_latency = ABSOLUTE_MAX_LATENCY;
+ s->thread_info.fixed_latency = flags & PA_SINK_DYNAMIC_LATENCY ? 0 : DEFAULT_FIXED_LATENCY;
+ /* FIXME: This should probably be moved to pa_sink_put() */
pa_assert_se(pa_idxset_put(core->sinks, s, &s->index) >= 0);
if (s->card)
@@ -348,6 +352,7 @@ pa_sink* pa_sink_new(
s->monitor_source->monitor_of = s;
pa_source_set_latency_range(s->monitor_source, s->thread_info.min_latency, s->thread_info.max_latency);
+ pa_source_set_fixed_latency(s->monitor_source, s->thread_info.fixed_latency);
pa_source_set_max_rewind(s->monitor_source, s->thread_info.max_rewind);
return s;
@@ -360,6 +365,7 @@ static int sink_set_state(pa_sink *s, pa_sink_state_t state) {
pa_sink_state_t original_state;
pa_assert(s);
+ pa_assert_ctl_context();
if (s->state == state)
return 0;
@@ -396,9 +402,9 @@ static int sink_set_state(pa_sink *s, pa_sink_state_t state) {
/* We're suspending or resuming, tell everyone about it */
- for (i = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); i; i = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx)))
+ PA_IDXSET_FOREACH(i, s->inputs, idx)
if (s->state == PA_SINK_SUSPENDED &&
- (i->flags & PA_SINK_INPUT_FAIL_ON_SUSPEND))
+ (i->flags & PA_SINK_INPUT_KILL_ON_SUSPEND))
pa_sink_input_kill(i);
else if (i->suspend)
i->suspend(i, state == PA_SINK_SUSPENDED);
@@ -413,12 +419,12 @@ static int sink_set_state(pa_sink *s, pa_sink_state_t state) {
/* Called from main context */
void pa_sink_put(pa_sink* s) {
pa_sink_assert_ref(s);
+ pa_assert_ctl_context();
pa_assert(s->state == PA_SINK_INIT);
/* The following fields must be initialized properly when calling _put() */
pa_assert(s->asyncmsgq);
- pa_assert(s->rtpoll);
pa_assert(s->thread_info.min_latency <= s->thread_info.max_latency);
/* Generally, flags should be initialized via pa_sink_new(). As a
@@ -431,16 +437,21 @@ void pa_sink_put(pa_sink* s) {
if ((s->flags & PA_SINK_DECIBEL_VOLUME) && s->core->flat_volumes)
s->flags |= PA_SINK_FLAT_VOLUME;
+ /* We assume that if the sink implementor changed the default
+ * volume he did so in real_volume, because that is the usual
+ * place where he is supposed to place his changes. */
+ s->reference_volume = s->real_volume;
+
s->thread_info.soft_volume = s->soft_volume;
s->thread_info.soft_muted = s->muted;
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_DYNAMIC_LATENCY) == (s->thread_info.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));
- pa_assert(s->monitor_source->fixed_latency == s->fixed_latency);
+ pa_assert(s->monitor_source->thread_info.fixed_latency == s->thread_info.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);
@@ -458,6 +469,7 @@ void pa_sink_unlink(pa_sink* s) {
pa_sink_input *i, *j = NULL;
pa_assert(s);
+ pa_assert_ctl_context();
/* Please note that pa_sink_unlink() does more than simply
* reversing pa_sink_put(). It also undoes the registrations
@@ -507,6 +519,7 @@ static void sink_free(pa_object *o) {
pa_sink_input *i;
pa_assert(s);
+ pa_assert_ctl_context();
pa_assert(pa_sink_refcnt(s) == 0);
if (PA_SINK_IS_LINKED(s->state))
@@ -547,9 +560,10 @@ static void sink_free(pa_object *o) {
pa_xfree(s);
}
-/* Called from main context */
+/* Called from main context, and not while the IO thread is active, please */
void pa_sink_set_asyncmsgq(pa_sink *s, pa_asyncmsgq *q) {
pa_sink_assert_ref(s);
+ pa_assert_ctl_context();
s->asyncmsgq = q;
@@ -557,11 +571,32 @@ void pa_sink_set_asyncmsgq(pa_sink *s, pa_asyncmsgq *q) {
pa_source_set_asyncmsgq(s->monitor_source, q);
}
-/* Called from main context */
+/* Called from main context, and not while the IO thread is active, please */
+void pa_sink_update_flags(pa_sink *s, pa_sink_flags_t mask, pa_sink_flags_t value) {
+ pa_sink_assert_ref(s);
+ pa_assert_ctl_context();
+
+ if (mask == 0)
+ return;
+
+ /* For now, allow only a minimal set of flags to be changed. */
+ pa_assert((mask & ~(PA_SINK_DYNAMIC_LATENCY|PA_SINK_LATENCY)) == 0);
+
+ s->flags = (s->flags & ~mask) | (value & mask);
+
+ pa_source_update_flags(s->monitor_source,
+ ((mask & PA_SINK_LATENCY) ? PA_SOURCE_LATENCY : 0) |
+ ((mask & PA_SINK_DYNAMIC_LATENCY) ? PA_SOURCE_DYNAMIC_LATENCY : 0),
+ ((value & PA_SINK_LATENCY) ? PA_SOURCE_LATENCY : 0) |
+ ((value & PA_SINK_DYNAMIC_LATENCY) ? PA_SINK_DYNAMIC_LATENCY : 0));
+}
+
+/* Called from IO context, or before _put() from main context */
void pa_sink_set_rtpoll(pa_sink *s, pa_rtpoll *p) {
pa_sink_assert_ref(s);
+ pa_sink_assert_io_context(s);
- s->rtpoll = p;
+ s->thread_info.rtpoll = p;
if (s->monitor_source)
pa_source_set_rtpoll(s->monitor_source, p);
@@ -570,6 +605,7 @@ void pa_sink_set_rtpoll(pa_sink *s, pa_rtpoll *p) {
/* Called from main context */
int pa_sink_update_status(pa_sink*s) {
pa_sink_assert_ref(s);
+ pa_assert_ctl_context();
pa_assert(PA_SINK_IS_LINKED(s->state));
if (s->state == PA_SINK_SUSPENDED)
@@ -581,6 +617,7 @@ int pa_sink_update_status(pa_sink*s) {
/* Called from main context */
int pa_sink_suspend(pa_sink *s, pa_bool_t suspend, pa_suspend_cause_t cause) {
pa_sink_assert_ref(s);
+ pa_assert_ctl_context();
pa_assert(PA_SINK_IS_LINKED(s->state));
pa_assert(cause != 0);
@@ -609,6 +646,7 @@ pa_queue *pa_sink_move_all_start(pa_sink *s, pa_queue *q) {
uint32_t idx;
pa_sink_assert_ref(s);
+ pa_assert_ctl_context();
pa_assert(PA_SINK_IS_LINKED(s->state));
if (!q)
@@ -633,12 +671,13 @@ void pa_sink_move_all_finish(pa_sink *s, pa_queue *q, pa_bool_t save) {
pa_sink_input *i;
pa_sink_assert_ref(s);
+ pa_assert_ctl_context();
pa_assert(PA_SINK_IS_LINKED(s->state));
pa_assert(q);
while ((i = PA_SINK_INPUT(pa_queue_pop(q)))) {
if (pa_sink_input_finish_move(i, s, save) < 0)
- pa_sink_input_kill(i);
+ pa_sink_input_fail_move(i);
pa_sink_input_unref(i);
}
@@ -649,13 +688,13 @@ void pa_sink_move_all_finish(pa_sink *s, pa_queue *q, pa_bool_t save) {
/* Called from main context */
void pa_sink_move_all_fail(pa_queue *q) {
pa_sink_input *i;
+
+ pa_assert_ctl_context();
pa_assert(q);
while ((i = PA_SINK_INPUT(pa_queue_pop(q)))) {
- if (pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_FAIL], i) == PA_HOOK_OK) {
- pa_sink_input_kill(i);
- pa_sink_input_unref(i);
- }
+ pa_sink_input_fail_move(i);
+ pa_sink_input_unref(i);
}
pa_queue_free(q, NULL, NULL);
@@ -665,11 +704,15 @@ void pa_sink_move_all_fail(pa_queue *q) {
void pa_sink_process_rewind(pa_sink *s, size_t nbytes) {
pa_sink_input *i;
void *state = NULL;
+
pa_sink_assert_ref(s);
+ pa_sink_assert_io_context(s);
pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
/* If nobody requested this and this is actually no real rewind
- * then we can short cut this */
+ * then we can short cut this. Please note that this means that
+ * not all rewind requests triggered upstream will always be
+ * translated in actual requests! */
if (!s->thread_info.rewind_requested && nbytes <= 0)
return;
@@ -682,7 +725,7 @@ void pa_sink_process_rewind(pa_sink *s, size_t nbytes) {
if (nbytes > 0)
pa_log_debug("Processing rewind...");
- while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL))) {
+ PA_HASHMAP_FOREACH(i, s->thread_info.inputs, state) {
pa_sink_input_assert_ref(i);
pa_sink_input_process_rewind(i, nbytes);
}
@@ -700,6 +743,7 @@ static unsigned fill_mix_info(pa_sink *s, size_t *length, pa_mix_info *info, uns
size_t mixlength = *length;
pa_sink_assert_ref(s);
+ pa_sink_assert_io_context(s);
pa_assert(info);
while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)) && maxinfo > 0) {
@@ -734,18 +778,19 @@ static unsigned fill_mix_info(pa_sink *s, size_t *length, pa_mix_info *info, uns
/* Called from IO thread context */
static void inputs_drop(pa_sink *s, pa_mix_info *info, unsigned n, pa_memchunk *result) {
pa_sink_input *i;
- void *state = NULL;
+ void *state;
unsigned p = 0;
unsigned n_unreffed = 0;
pa_sink_assert_ref(s);
+ pa_sink_assert_io_context(s);
pa_assert(result);
pa_assert(result->memblock);
pa_assert(result->length > 0);
/* We optimize for the case where the order of the inputs has not changed */
- while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL))) {
+ PA_HASHMAP_FOREACH(i, s->thread_info.inputs, state) {
unsigned j;
pa_mix_info* m = NULL;
@@ -834,12 +879,11 @@ void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) {
size_t block_size_max;
pa_sink_assert_ref(s);
+ pa_sink_assert_io_context(s);
pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
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);
@@ -850,6 +894,8 @@ void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) {
return;
}
+ pa_sink_ref(s);
+
if (length <= 0)
length = pa_frame_align(MIX_BUFFER_LENGTH, &s->sample_spec);
@@ -880,18 +926,16 @@ 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)) {
- 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);
- }
+ 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 if (!pa_cvolume_is_norm(&volume)) {
+ pa_memchunk_make_writable(result, 0);
+ pa_volume_memchunk(result, &s->sample_spec, &volume);
}
} else {
void *ptr;
@@ -920,14 +964,13 @@ void pa_sink_render_into(pa_sink*s, pa_memchunk *target) {
size_t length, block_size_max;
pa_sink_assert_ref(s);
+ pa_sink_assert_io_context(s);
pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
pa_assert(target);
pa_assert(target->memblock);
pa_assert(target->length > 0);
pa_assert(pa_frame_aligned(target->length, &s->sample_spec));
- pa_sink_ref(s);
-
pa_assert(!s->thread_info.rewind_requested);
pa_assert(s->thread_info.rewind_nbytes == 0);
@@ -936,6 +979,8 @@ void pa_sink_render_into(pa_sink*s, pa_memchunk *target) {
return;
}
+ pa_sink_ref(s);
+
length = target->length;
block_size_max = pa_mempool_block_size_max(s->core->mempool);
if (length > block_size_max)
@@ -1003,17 +1048,23 @@ void pa_sink_render_into_full(pa_sink *s, pa_memchunk *target) {
size_t l, d;
pa_sink_assert_ref(s);
+ pa_sink_assert_io_context(s);
pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
pa_assert(target);
pa_assert(target->memblock);
pa_assert(target->length > 0);
pa_assert(pa_frame_aligned(target->length, &s->sample_spec));
- pa_sink_ref(s);
-
pa_assert(!s->thread_info.rewind_requested);
pa_assert(s->thread_info.rewind_nbytes == 0);
+ if (s->thread_info.state == PA_SINK_SUSPENDED) {
+ pa_silence_memchunk(target, &s->sample_spec);
+ return;
+ }
+
+ pa_sink_ref(s);
+
l = target->length;
d = 0;
while (l > 0) {
@@ -1032,91 +1083,31 @@ 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_sink_assert_io_context(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);
- pa_assert(length > 0);
-
- n = fill_mix_info(s, &length1st, info, MAX_MIX_CHANNELS);
-
- 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_sink_ref(s);
- pa_memblock_release(result->memblock);
- }
-
- inputs_drop(s, info, n, result);
+ pa_sink_render(s, length, 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;
+ chunk.memblock = result->memblock;
+ chunk.index = result->index + result->length;
+ chunk.length = length - result->length;
- pa_sink_render_into(s, &chunk);
+ pa_sink_render_into_full(s, &chunk);
- d += chunk.length;
- l -= chunk.length;
- }
result->length = length;
}
@@ -1128,6 +1119,7 @@ pa_usec_t pa_sink_get_latency(pa_sink *s) {
pa_usec_t usec = 0;
pa_sink_assert_ref(s);
+ pa_assert_ctl_context();
pa_assert(PA_SINK_IS_LINKED(s->state));
/* The returned value is supposed to be in the time domain of the sound card! */
@@ -1149,6 +1141,7 @@ pa_usec_t pa_sink_get_latency_within_thread(pa_sink *s) {
pa_msgobject *o;
pa_sink_assert_ref(s);
+ pa_sink_assert_io_context(s);
pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
/* The returned value is supposed to be in the time domain of the sound card! */
@@ -1161,7 +1154,7 @@ pa_usec_t pa_sink_get_latency_within_thread(pa_sink *s) {
o = PA_MSGOBJECT(s);
- /* We probably should make this a proper vtable callback instead of going through process_msg() */
+ /* FIXME: We probably should make this a proper vtable callback instead of going through process_msg() */
if (o->process_msg(o, PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0)
return -1;
@@ -1169,108 +1162,149 @@ pa_usec_t pa_sink_get_latency_within_thread(pa_sink *s) {
return usec;
}
-static void compute_new_soft_volume(pa_sink_input *i, const pa_cvolume *new_volume) {
- unsigned c;
+/* Called from main context */
+static void compute_reference_ratios(pa_sink *s) {
+ uint32_t idx;
+ pa_sink_input *i;
- pa_sink_input_assert_ref(i);
- pa_assert(new_volume->channels == i->sample_spec.channels);
+ pa_sink_assert_ref(s);
+ pa_assert_ctl_context();
+ pa_assert(PA_SINK_IS_LINKED(s->state));
+ pa_assert(s->flags & PA_SINK_FLAT_VOLUME);
- /*
- * This basically calculates:
- *
- * i->relative_volume := i->virtual_volume / new_volume
- * i->soft_volume := i->relative_volume * i->volume_factor
- */
+ PA_IDXSET_FOREACH(i, s->inputs, idx) {
+ unsigned c;
+ pa_cvolume remapped;
- /* The new sink volume passed in here must already be remapped to
- * the sink input's channel map! */
+ /*
+ * Calculates the reference volume from the sink's reference
+ * volume. This basically calculates:
+ *
+ * i->reference_ratio = i->volume / s->reference_volume
+ */
- i->soft_volume.channels = i->sample_spec.channels;
+ remapped = s->reference_volume;
+ pa_cvolume_remap(&remapped, &s->channel_map, &i->channel_map);
- for (c = 0; c < i->sample_spec.channels; c++)
+ i->reference_ratio.channels = i->sample_spec.channels;
- if (new_volume->values[c] <= PA_VOLUME_MUTED)
- /* We leave i->relative_volume untouched */
- i->soft_volume.values[c] = PA_VOLUME_MUTED;
- else {
- i->relative_volume[c] =
- pa_sw_volume_to_linear(i->virtual_volume.values[c]) /
- pa_sw_volume_to_linear(new_volume->values[c]);
+ for (c = 0; c < i->sample_spec.channels; c++) {
- i->soft_volume.values[c] = pa_sw_volume_from_linear(
- i->relative_volume[c] *
- pa_sw_volume_to_linear(i->volume_factor.values[c]));
+ /* We don't update when the sink volume is 0 anyway */
+ if (remapped.values[c] <= PA_VOLUME_MUTED)
+ continue;
+
+ /* Don't update the reference ratio unless necessary */
+ if (pa_sw_volume_multiply(
+ i->reference_ratio.values[c],
+ remapped.values[c]) == i->volume.values[c])
+ continue;
+
+ i->reference_ratio.values[c] = pa_sw_volume_divide(
+ i->volume.values[c],
+ remapped.values[c]);
}
+ }
+}
+
+/* Called from main context */
+static void compute_real_ratios(pa_sink *s) {
+ pa_sink_input *i;
+ uint32_t idx;
- /* Hooks have the ability to play games with i->soft_volume */
- pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_SET_VOLUME], i);
+ pa_sink_assert_ref(s);
+ pa_assert_ctl_context();
+ pa_assert(PA_SINK_IS_LINKED(s->state));
+ pa_assert(s->flags & PA_SINK_FLAT_VOLUME);
+
+ PA_IDXSET_FOREACH(i, s->inputs, idx) {
+ unsigned c;
+ pa_cvolume remapped;
+
+ /*
+ * This basically calculates:
+ *
+ * i->real_ratio := i->volume / s->real_volume
+ * i->soft_volume := i->real_ratio * i->volume_factor
+ */
+
+ remapped = s->real_volume;
+ pa_cvolume_remap(&remapped, &s->channel_map, &i->channel_map);
+
+ i->real_ratio.channels = i->sample_spec.channels;
+ i->soft_volume.channels = i->sample_spec.channels;
+
+ for (c = 0; c < i->sample_spec.channels; c++) {
+
+ if (remapped.values[c] <= PA_VOLUME_MUTED) {
+ /* We leave i->real_ratio untouched */
+ i->soft_volume.values[c] = PA_VOLUME_MUTED;
+ continue;
+ }
+
+ /* Don't lose accuracy unless necessary */
+ if (pa_sw_volume_multiply(
+ i->real_ratio.values[c],
+ remapped.values[c]) != i->volume.values[c])
+
+ i->real_ratio.values[c] = pa_sw_volume_divide(
+ i->volume.values[c],
+ remapped.values[c]);
- /* We don't copy the soft_volume to the thread_info data
- * here. That must be done by the caller */
+ i->soft_volume.values[c] = pa_sw_volume_multiply(
+ i->real_ratio.values[c],
+ i->volume_factor.values[c]);
+ }
+
+ /* We don't copy the soft_volume to the thread_info data
+ * here. That must be done by the caller */
+ }
}
/* Called from main thread */
-void pa_sink_update_flat_volume(pa_sink *s, pa_cvolume *new_volume) {
+static void compute_real_volume(pa_sink *s) {
pa_sink_input *i;
uint32_t idx;
pa_sink_assert_ref(s);
- pa_assert(new_volume);
+ pa_assert_ctl_context();
pa_assert(PA_SINK_IS_LINKED(s->state));
pa_assert(s->flags & PA_SINK_FLAT_VOLUME);
- /* This is called whenever a sink input volume changes or a sink
- * input is added/removed and we might need to fix up the sink
- * volume accordingly. Please note that we don't actually update
- * the sinks volume here, we only return how it needs to be
- * updated. The caller should then call pa_sink_set_volume().*/
+ /* This determines the maximum volume of all streams and sets
+ * s->real_volume accordingly. */
if (pa_idxset_isempty(s->inputs)) {
/* In the special case that we have no sink input we leave the
* volume unmodified. */
- *new_volume = s->reference_volume;
+ s->real_volume = s->reference_volume;
return;
}
- pa_cvolume_mute(new_volume, s->channel_map.channels);
+ pa_cvolume_mute(&s->real_volume, s->channel_map.channels);
/* First let's determine the new maximum volume of all inputs
* connected to this sink */
- for (i = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); i; i = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx))) {
- unsigned c;
- pa_cvolume remapped_volume;
-
- remapped_volume = i->virtual_volume;
- pa_cvolume_remap(&remapped_volume, &i->channel_map, &s->channel_map);
+ PA_IDXSET_FOREACH(i, s->inputs, idx) {
+ pa_cvolume remapped;
- for (c = 0; c < new_volume->channels; c++)
- if (remapped_volume.values[c] > new_volume->values[c])
- new_volume->values[c] = remapped_volume.values[c];
+ remapped = i->volume;
+ pa_cvolume_remap(&remapped, &i->channel_map, &s->channel_map);
+ pa_cvolume_merge(&s->real_volume, &s->real_volume, &remapped);
}
- /* Then, let's update the soft volumes of all inputs connected
- * to this sink */
- for (i = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); i; i = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx))) {
- pa_cvolume remapped_new_volume;
-
- remapped_new_volume = *new_volume;
- pa_cvolume_remap(&remapped_new_volume, &s->channel_map, &i->channel_map);
- compute_new_soft_volume(i, &remapped_new_volume);
-
- /* We don't copy soft_volume to the thread_info data here
- * (i.e. issue PA_SINK_INPUT_MESSAGE_SET_VOLUME) because we
- * want the update to be atomically with the sink volume
- * update, hence we do it within the pa_sink_set_volume() call
- * below */
- }
+ /* Then, let's update the real ratios/soft volumes of all inputs
+ * connected to this sink */
+ compute_real_ratios(s);
}
/* Called from main thread */
-void pa_sink_propagate_flat_volume(pa_sink *s) {
+static void propagate_reference_volume(pa_sink *s) {
pa_sink_input *i;
uint32_t idx;
pa_sink_assert_ref(s);
+ pa_assert_ctl_context();
pa_assert(PA_SINK_IS_LINKED(s->state));
pa_assert(s->flags & PA_SINK_FLAT_VOLUME);
@@ -1278,63 +1312,88 @@ void pa_sink_propagate_flat_volume(pa_sink *s) {
* caused by a sink input volume change. We need to fix up the
* sink input volumes accordingly */
- for (i = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); i; i = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx))) {
- pa_cvolume sink_volume, new_virtual_volume;
- unsigned c;
-
- /* This basically calculates i->virtual_volume := i->relative_volume * s->virtual_volume */
+ PA_IDXSET_FOREACH(i, s->inputs, idx) {
+ pa_cvolume old_volume, remapped;
- sink_volume = s->virtual_volume;
- pa_cvolume_remap(&sink_volume, &s->channel_map, &i->channel_map);
+ old_volume = i->volume;
- for (c = 0; c < i->sample_spec.channels; c++)
- new_virtual_volume.values[c] = pa_sw_volume_from_linear(
- i->relative_volume[c] *
- pa_sw_volume_to_linear(sink_volume.values[c]));
+ /* This basically calculates:
+ *
+ * i->volume := s->reference_volume * i->reference_ratio */
- new_virtual_volume.channels = i->sample_spec.channels;
+ remapped = s->reference_volume;
+ pa_cvolume_remap(&remapped, &s->channel_map, &i->channel_map);
+ pa_sw_cvolume_multiply(&i->volume, &remapped, &i->reference_ratio);
- if (!pa_cvolume_equal(&new_virtual_volume, &i->virtual_volume)) {
- i->virtual_volume = new_virtual_volume;
+ /* The volume changed, let's tell people so */
+ if (!pa_cvolume_equal(&old_volume, &i->volume)) {
- /* Hmm, the soft volume might no longer actually match
- * what has been chosen as new virtual volume here,
- * especially when the old volume was
- * PA_VOLUME_MUTED. Hence let's recalculate the soft
- * volumes here. */
- compute_new_soft_volume(i, &sink_volume);
+ if (i->volume_changed)
+ i->volume_changed(i);
- /* The virtual volume changed, let's tell people so */
pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
}
}
-
- /* If the soft_volume of any of the sink inputs got changed, let's
- * make sure the thread copies are synced up. */
- pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SYNC_VOLUMES, NULL, 0, NULL) == 0);
}
/* Called from main thread */
-void pa_sink_set_volume(pa_sink *s, const pa_cvolume *volume, pa_bool_t propagate, pa_bool_t sendmsg, pa_bool_t become_reference, pa_bool_t save) {
- pa_bool_t virtual_volume_changed;
+void pa_sink_set_volume(
+ pa_sink *s,
+ const pa_cvolume *volume,
+ pa_bool_t send_msg,
+ pa_bool_t save) {
+
+ pa_cvolume old_reference_volume;
+ pa_bool_t reference_changed;
pa_sink_assert_ref(s);
+ pa_assert_ctl_context();
pa_assert(PA_SINK_IS_LINKED(s->state));
- pa_assert(volume);
- pa_assert(pa_cvolume_valid(volume));
- pa_assert(pa_cvolume_compatible(volume, &s->sample_spec));
+ pa_assert(!volume || pa_cvolume_valid(volume));
+ pa_assert(volume || (s->flags & PA_SINK_FLAT_VOLUME));
+ pa_assert(!volume || volume->channels == 1 || pa_cvolume_compatible(volume, &s->sample_spec));
- virtual_volume_changed = !pa_cvolume_equal(volume, &s->virtual_volume);
- s->virtual_volume = *volume;
- s->save_volume = (!virtual_volume_changed && s->save_volume) || save;
+ /* As a special exception we accept mono volumes on all sinks --
+ * even on those with more complex channel maps */
- if (become_reference)
- s->reference_volume = s->virtual_volume;
+ /* If volume is NULL we synchronize the sink's real and reference
+ * volumes with the stream volumes. If it is not NULL we update
+ * the reference_volume with it. */
- /* Propagate this volume change back to the inputs */
- if (virtual_volume_changed)
- if (propagate && (s->flags & PA_SINK_FLAT_VOLUME))
- pa_sink_propagate_flat_volume(s);
+ old_reference_volume = s->reference_volume;
+
+ if (volume) {
+
+ if (pa_cvolume_compatible(volume, &s->sample_spec))
+ s->reference_volume = *volume;
+ else
+ pa_cvolume_scale(&s->reference_volume, pa_cvolume_max(volume));
+
+ if (s->flags & PA_SINK_FLAT_VOLUME) {
+ /* OK, propagate this volume change back to the inputs */
+ propagate_reference_volume(s);
+
+ /* And now recalculate the real volume */
+ compute_real_volume(s);
+ } else
+ s->real_volume = s->reference_volume;
+
+ } else {
+ pa_assert(s->flags & PA_SINK_FLAT_VOLUME);
+
+ /* Ok, let's determine the new real volume */
+ compute_real_volume(s);
+
+ /* Let's 'push' the reference volume if necessary */
+ pa_cvolume_merge(&s->reference_volume, &s->reference_volume, &s->real_volume);
+
+ /* We need to fix the reference ratios of all streams now that
+ * we changed the reference volume */
+ compute_reference_ratios(s);
+ }
+
+ reference_changed = !pa_cvolume_equal(&old_reference_volume, &s->reference_volume);
+ s->save_volume = (!reference_changed && s->save_volume) || save;
if (s->set_volume) {
/* If we have a function set_volume(), then we do not apply a
@@ -1347,72 +1406,133 @@ void pa_sink_set_volume(pa_sink *s, const pa_cvolume *volume, pa_bool_t propagat
} else
/* If we have no function set_volume(), then the soft volume
* becomes the virtual volume */
- s->soft_volume = s->virtual_volume;
+ s->soft_volume = s->real_volume;
/* This tells the sink that soft and/or virtual volume changed */
- if (sendmsg)
+ if (send_msg)
pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_VOLUME, NULL, 0, NULL) == 0);
- if (virtual_volume_changed)
+ if (reference_changed)
pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
}
/* Called from main thread. Only to be called by sink implementor */
void pa_sink_set_soft_volume(pa_sink *s, const pa_cvolume *volume) {
pa_sink_assert_ref(s);
- pa_assert(volume);
+ pa_assert_ctl_context();
- s->soft_volume = *volume;
+ if (!volume)
+ pa_cvolume_reset(&s->soft_volume, s->sample_spec.channels);
+ else
+ s->soft_volume = *volume;
if (PA_SINK_IS_LINKED(s->state))
pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_VOLUME, NULL, 0, NULL) == 0);
else
- s->thread_info.soft_volume = *volume;
+ s->thread_info.soft_volume = s->soft_volume;
}
-/* Called from main thread */
-const pa_cvolume *pa_sink_get_volume(pa_sink *s, pa_bool_t force_refresh, pa_bool_t reference) {
+static void propagate_real_volume(pa_sink *s, const pa_cvolume *old_real_volume) {
+ pa_sink_input *i;
+ uint32_t idx;
+ pa_cvolume old_reference_volume;
+
pa_sink_assert_ref(s);
+ pa_assert_ctl_context();
+ pa_assert(PA_SINK_IS_LINKED(s->state));
- if (s->refresh_volume || force_refresh) {
- struct pa_cvolume old_virtual_volume = s->virtual_volume;
+ /* This is called when the hardware's real volume changes due to
+ * some external event. We copy the real volume into our
+ * reference volume and then rebuild the stream volumes based on
+ * i->real_ratio which should stay fixed. */
- if (s->get_volume)
- s->get_volume(s);
+ if (pa_cvolume_equal(old_real_volume, &s->real_volume))
+ return;
- pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_VOLUME, NULL, 0, NULL) == 0);
+ old_reference_volume = s->reference_volume;
- if (!pa_cvolume_equal(&old_virtual_volume, &s->virtual_volume)) {
+ /* 1. Make the real volume the reference volume */
+ s->reference_volume = s->real_volume;
- s->reference_volume = s->virtual_volume;
+ if (s->flags & PA_SINK_FLAT_VOLUME) {
- if (s->flags & PA_SINK_FLAT_VOLUME)
- pa_sink_propagate_flat_volume(s);
+ PA_IDXSET_FOREACH(i, s->inputs, idx) {
+ pa_cvolume old_volume, remapped;
- pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+ old_volume = i->volume;
+
+ /* 2. Since the sink's reference and real volumes are equal
+ * now our ratios should be too. */
+ i->reference_ratio = i->real_ratio;
+
+ /* 3. Recalculate the new stream reference volume based on the
+ * reference ratio and the sink's reference volume.
+ *
+ * This basically calculates:
+ *
+ * i->volume = s->reference_volume * i->reference_ratio
+ *
+ * This is identical to propagate_reference_volume() */
+ remapped = s->reference_volume;
+ pa_cvolume_remap(&remapped, &s->channel_map, &i->channel_map);
+ pa_sw_cvolume_multiply(&i->volume, &remapped, &i->reference_ratio);
+
+ /* Notify if something changed */
+ if (!pa_cvolume_equal(&old_volume, &i->volume)) {
+
+ if (i->volume_changed)
+ i->volume_changed(i);
+
+ pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
+ }
}
}
- return reference ? &s->reference_volume : &s->virtual_volume;
+ /* Something got changed in the hardware. It probably makes sense
+ * to save changed hw settings given that hw volume changes not
+ * triggered by PA are almost certainly done by the user. */
+ s->save_volume = TRUE;
+
+ if (!pa_cvolume_equal(&old_reference_volume, &s->reference_volume))
+ pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
}
/* Called from main thread */
-void pa_sink_volume_changed(pa_sink *s, const pa_cvolume *new_volume, pa_bool_t save) {
+const pa_cvolume *pa_sink_get_volume(pa_sink *s, pa_bool_t force_refresh) {
pa_sink_assert_ref(s);
+ pa_assert_ctl_context();
+ pa_assert(PA_SINK_IS_LINKED(s->state));
- /* The sink implementor may call this if the volume changed to make sure everyone is notified */
- if (pa_cvolume_equal(&s->virtual_volume, new_volume)) {
- s->save_volume = s->save_volume || save;
- return;
+ if (s->refresh_volume || force_refresh) {
+ struct pa_cvolume old_real_volume;
+
+ old_real_volume = s->real_volume;
+
+ if (s->get_volume)
+ s->get_volume(s);
+
+ pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_VOLUME, NULL, 0, NULL) == 0);
+
+ propagate_real_volume(s, &old_real_volume);
}
- s->reference_volume = s->virtual_volume = *new_volume;
- s->save_volume = save;
+ return &s->reference_volume;
+}
+
+/* Called from main thread */
+void pa_sink_volume_changed(pa_sink *s, const pa_cvolume *new_real_volume) {
+ pa_cvolume old_real_volume;
- if (s->flags & PA_SINK_FLAT_VOLUME)
- pa_sink_propagate_flat_volume(s);
+ pa_sink_assert_ref(s);
+ pa_assert_ctl_context();
+ pa_assert(PA_SINK_IS_LINKED(s->state));
- pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+ /* The sink implementor may call this if the volume changed to make sure everyone is notified */
+
+ old_real_volume = s->real_volume;
+ s->real_volume = *new_real_volume;
+
+ propagate_real_volume(s, &old_real_volume);
}
/* Called from main thread */
@@ -1420,6 +1540,7 @@ void pa_sink_set_mute(pa_sink *s, pa_bool_t mute, pa_bool_t save) {
pa_bool_t old_muted;
pa_sink_assert_ref(s);
+ pa_assert_ctl_context();
pa_assert(PA_SINK_IS_LINKED(s->state));
old_muted = s->muted;
@@ -1439,6 +1560,8 @@ void pa_sink_set_mute(pa_sink *s, pa_bool_t mute, pa_bool_t save) {
pa_bool_t pa_sink_get_mute(pa_sink *s, pa_bool_t force_refresh) {
pa_sink_assert_ref(s);
+ pa_assert_ctl_context();
+ pa_assert(PA_SINK_IS_LINKED(s->state));
if (s->refresh_muted || force_refresh) {
pa_bool_t old_muted = s->muted;
@@ -1449,6 +1572,8 @@ 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) {
+ s->save_muted = TRUE;
+
pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
/* Make sure the soft mute status stays in sync */
@@ -1460,18 +1585,18 @@ pa_bool_t pa_sink_get_mute(pa_sink *s, pa_bool_t force_refresh) {
}
/* Called from main thread */
-void pa_sink_mute_changed(pa_sink *s, pa_bool_t new_muted, pa_bool_t save) {
+void pa_sink_mute_changed(pa_sink *s, pa_bool_t new_muted) {
pa_sink_assert_ref(s);
+ pa_assert_ctl_context();
+ pa_assert(PA_SINK_IS_LINKED(s->state));
/* The sink implementor may call this if the volume changed to make sure everyone is notified */
- if (s->muted == new_muted) {
- s->save_muted = s->save_muted || save;
+ if (s->muted == new_muted)
return;
- }
s->muted = new_muted;
- s->save_muted = save;
+ s->save_muted = TRUE;
pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
}
@@ -1479,6 +1604,7 @@ void pa_sink_mute_changed(pa_sink *s, pa_bool_t new_muted, pa_bool_t save) {
/* Called from main thread */
pa_bool_t pa_sink_update_proplist(pa_sink *s, pa_update_mode_t mode, pa_proplist *p) {
pa_sink_assert_ref(s);
+ pa_assert_ctl_context();
if (p)
pa_proplist_update(s->proplist, mode, p);
@@ -1492,16 +1618,18 @@ pa_bool_t pa_sink_update_proplist(pa_sink *s, pa_update_mode_t mode, pa_proplist
}
/* Called from main thread */
+/* FIXME -- this should be dropped and be merged into pa_sink_update_proplist() */
void pa_sink_set_description(pa_sink *s, const char *description) {
const char *old;
pa_sink_assert_ref(s);
+ pa_assert_ctl_context();
if (!description && !pa_proplist_contains(s->proplist, PA_PROP_DEVICE_DESCRIPTION))
return;
old = pa_proplist_gets(s->proplist, PA_PROP_DEVICE_DESCRIPTION);
- if (old && description && !strcmp(old, description))
+ if (old && description && pa_streq(old, description))
return;
if (description)
@@ -1528,6 +1656,7 @@ unsigned pa_sink_linked_by(pa_sink *s) {
unsigned ret;
pa_sink_assert_ref(s);
+ pa_assert_ctl_context();
pa_assert(PA_SINK_IS_LINKED(s->state));
ret = pa_idxset_size(s->inputs);
@@ -1546,6 +1675,7 @@ unsigned pa_sink_used_by(pa_sink *s) {
unsigned ret;
pa_sink_assert_ref(s);
+ pa_assert_ctl_context();
pa_assert(PA_SINK_IS_LINKED(s->state));
ret = pa_idxset_size(s->inputs);
@@ -1564,6 +1694,7 @@ unsigned pa_sink_check_suspend(pa_sink *s) {
uint32_t idx;
pa_sink_assert_ref(s);
+ pa_assert_ctl_context();
if (!PA_SINK_IS_LINKED(s->state))
return 0;
@@ -1597,8 +1728,9 @@ static void sync_input_volumes_within_thread(pa_sink *s) {
void *state = NULL;
pa_sink_assert_ref(s);
+ pa_sink_assert_io_context(s);
- while ((i = PA_SINK_INPUT(pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))) {
+ PA_HASHMAP_FOREACH(i, s->thread_info.inputs, state) {
if (pa_atomic_load(&i->before_ramping_v))
i->thread_info.future_soft_volume = i->soft_volume;
@@ -1705,7 +1837,7 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse
if (pa_hashmap_remove(s->thread_info.inputs, PA_UINT32_TO_PTR(i->index)))
pa_sink_input_unref(i);
- pa_sink_invalidate_requested_latency(s);
+ pa_sink_invalidate_requested_latency(s, TRUE);
pa_sink_request_rewind(s, (size_t) -1);
/* In flat volume mode we need to update the volume as
@@ -1727,10 +1859,7 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse
size_t sink_nbytes, total_nbytes;
/* Get the latency of the sink */
- if (!(s->flags & PA_SINK_LATENCY) ||
- PA_MSGOBJECT(s)->process_msg(PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0)
- usec = 0;
-
+ usec = pa_sink_get_latency_within_thread(s);
sink_nbytes = pa_usec_to_bytes(usec, &s->sample_spec);
total_nbytes = sink_nbytes + pa_memblockq_get_length(i->thread_info.render_memblockq);
@@ -1751,7 +1880,7 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse
if (pa_hashmap_remove(s->thread_info.inputs, PA_UINT32_TO_PTR(i->index)))
pa_sink_input_unref(i);
- pa_sink_invalidate_requested_latency(s);
+ pa_sink_invalidate_requested_latency(s, TRUE);
pa_log_debug("Requesting rewind due to started move");
pa_sink_request_rewind(s, (size_t) -1);
@@ -1789,10 +1918,7 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse
size_t nbytes;
/* Get the latency of the sink */
- if (!(s->flags & PA_SINK_LATENCY) ||
- PA_MSGOBJECT(s)->process_msg(PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0)
- usec = 0;
-
+ usec = pa_sink_get_latency_within_thread(s);
nbytes = pa_usec_to_bytes(usec, &s->sample_spec);
if (nbytes > 0)
@@ -1880,6 +2006,9 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse
pa_usec_t *usec = userdata;
*usec = pa_sink_get_requested_latency_within_thread(s);
+ /* Yes, that's right, the IO thread will see -1 when no
+ * explicit requested latency is configured, the main
+ * thread will see max_latency */
if (*usec == (pa_usec_t) -1)
*usec = s->thread_info.max_latency;
@@ -1903,6 +2032,16 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse
return 0;
}
+ case PA_SINK_MESSAGE_GET_FIXED_LATENCY:
+
+ *((pa_usec_t*) userdata) = s->thread_info.fixed_latency;
+ return 0;
+
+ case PA_SINK_MESSAGE_SET_FIXED_LATENCY:
+
+ pa_sink_set_fixed_latency_within_thread(s, (pa_usec_t) offset);
+ return 0;
+
case PA_SINK_MESSAGE_GET_MAX_REWIND:
*((size_t*) userdata) = s->thread_info.max_rewind;
@@ -1938,9 +2077,10 @@ int pa_sink_suspend_all(pa_core *c, pa_bool_t suspend, pa_suspend_cause_t cause)
int ret = 0;
pa_core_assert_ref(c);
+ pa_assert_ctl_context();
pa_assert(cause != 0);
- for (sink = PA_SINK(pa_idxset_first(c->sinks, &idx)); sink; sink = PA_SINK(pa_idxset_next(c->sinks, &idx))) {
+ PA_IDXSET_FOREACH(sink, c->sinks, idx) {
int r;
if ((r = pa_sink_suspend(sink, suspend, cause)) < 0)
@@ -1953,6 +2093,7 @@ int pa_sink_suspend_all(pa_core *c, pa_bool_t suspend, pa_suspend_cause_t cause)
/* Called from main thread */
void pa_sink_detach(pa_sink *s) {
pa_sink_assert_ref(s);
+ pa_assert_ctl_context();
pa_assert(PA_SINK_IS_LINKED(s->state));
pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_DETACH, NULL, 0, NULL) == 0);
@@ -1961,6 +2102,7 @@ void pa_sink_detach(pa_sink *s) {
/* Called from main thread */
void pa_sink_attach(pa_sink *s) {
pa_sink_assert_ref(s);
+ pa_assert_ctl_context();
pa_assert(PA_SINK_IS_LINKED(s->state));
pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_ATTACH, NULL, 0, NULL) == 0);
@@ -1972,9 +2114,10 @@ void pa_sink_detach_within_thread(pa_sink *s) {
void *state = NULL;
pa_sink_assert_ref(s);
+ pa_sink_assert_io_context(s);
pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
- while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
+ PA_HASHMAP_FOREACH(i, s->thread_info.inputs, state)
if (i->detach)
i->detach(i);
@@ -1988,9 +2131,10 @@ void pa_sink_attach_within_thread(pa_sink *s) {
void *state = NULL;
pa_sink_assert_ref(s);
+ pa_sink_assert_io_context(s);
pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
- while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
+ PA_HASHMAP_FOREACH(i, s->thread_info.inputs, state)
if (i->attach)
i->attach(i);
@@ -2001,6 +2145,7 @@ void pa_sink_attach_within_thread(pa_sink *s) {
/* Called from IO thread */
void pa_sink_request_rewind(pa_sink*s, size_t nbytes) {
pa_sink_assert_ref(s);
+ pa_sink_assert_io_context(s);
pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
if (s->thread_info.state == PA_SINK_SUSPENDED)
@@ -2030,15 +2175,15 @@ pa_usec_t pa_sink_get_requested_latency_within_thread(pa_sink *s) {
pa_usec_t monitor_latency;
pa_sink_assert_ref(s);
+ pa_sink_assert_io_context(s);
if (!(s->flags & PA_SINK_DYNAMIC_LATENCY))
- return PA_CLAMP(s->fixed_latency, s->thread_info.min_latency, s->thread_info.max_latency);
+ return PA_CLAMP(s->thread_info.fixed_latency, s->thread_info.min_latency, s->thread_info.max_latency);
if (s->thread_info.requested_latency_valid)
return s->thread_info.requested_latency;
- while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
-
+ PA_HASHMAP_FOREACH(i, s->thread_info.inputs, state)
if (i->thread_info.requested_sink_latency != (pa_usec_t) -1 &&
(result == (pa_usec_t) -1 || result > i->thread_info.requested_sink_latency))
result = i->thread_info.requested_sink_latency;
@@ -2066,6 +2211,7 @@ pa_usec_t pa_sink_get_requested_latency(pa_sink *s) {
pa_usec_t usec = 0;
pa_sink_assert_ref(s);
+ pa_assert_ctl_context();
pa_assert(PA_SINK_IS_LINKED(s->state));
if (s->state == PA_SINK_SUSPENDED)
@@ -2081,16 +2227,16 @@ void pa_sink_set_max_rewind_within_thread(pa_sink *s, size_t max_rewind) {
void *state = NULL;
pa_sink_assert_ref(s);
+ pa_sink_assert_io_context(s);
if (max_rewind == s->thread_info.max_rewind)
return;
s->thread_info.max_rewind = max_rewind;
- if (PA_SINK_IS_LINKED(s->thread_info.state)) {
- while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
+ if (PA_SINK_IS_LINKED(s->thread_info.state))
+ PA_HASHMAP_FOREACH(i, s->thread_info.inputs, state)
pa_sink_input_update_max_rewind(i, s->thread_info.max_rewind);
- }
if (s->monitor_source)
pa_source_set_max_rewind_within_thread(s->monitor_source, s->thread_info.max_rewind);
@@ -2099,6 +2245,7 @@ void pa_sink_set_max_rewind_within_thread(pa_sink *s, size_t max_rewind) {
/* Called from main thread */
void pa_sink_set_max_rewind(pa_sink *s, size_t max_rewind) {
pa_sink_assert_ref(s);
+ pa_assert_ctl_context();
if (PA_SINK_IS_LINKED(s->state))
pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_MAX_REWIND, NULL, max_rewind, NULL) == 0);
@@ -2111,6 +2258,7 @@ void pa_sink_set_max_request_within_thread(pa_sink *s, size_t max_request) {
void *state = NULL;
pa_sink_assert_ref(s);
+ pa_sink_assert_io_context(s);
if (max_request == s->thread_info.max_request)
return;
@@ -2120,7 +2268,7 @@ void pa_sink_set_max_request_within_thread(pa_sink *s, size_t max_request) {
if (PA_SINK_IS_LINKED(s->thread_info.state)) {
pa_sink_input *i;
- while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
+ PA_HASHMAP_FOREACH(i, s->thread_info.inputs, state)
pa_sink_input_update_max_request(i, s->thread_info.max_request);
}
}
@@ -2128,6 +2276,7 @@ void pa_sink_set_max_request_within_thread(pa_sink *s, size_t max_request) {
/* Called from main thread */
void pa_sink_set_max_request(pa_sink *s, size_t max_request) {
pa_sink_assert_ref(s);
+ pa_assert_ctl_context();
if (PA_SINK_IS_LINKED(s->state))
pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_MAX_REQUEST, NULL, max_request, NULL) == 0);
@@ -2136,23 +2285,24 @@ void pa_sink_set_max_request(pa_sink *s, size_t max_request) {
}
/* Called from IO thread */
-void pa_sink_invalidate_requested_latency(pa_sink *s) {
+void pa_sink_invalidate_requested_latency(pa_sink *s, pa_bool_t dynamic) {
pa_sink_input *i;
void *state = NULL;
pa_sink_assert_ref(s);
+ pa_sink_assert_io_context(s);
- if (!(s->flags & PA_SINK_DYNAMIC_LATENCY))
+ if ((s->flags & PA_SINK_DYNAMIC_LATENCY))
+ s->thread_info.requested_latency_valid = FALSE;
+ else if (dynamic)
return;
- s->thread_info.requested_latency_valid = FALSE;
-
if (PA_SINK_IS_LINKED(s->thread_info.state)) {
if (s->update_requested_latency)
s->update_requested_latency(s);
- while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
+ PA_HASHMAP_FOREACH(i, s->thread_info.inputs, state)
if (i->update_sink_requested_latency)
i->update_sink_requested_latency(i);
}
@@ -2161,6 +2311,7 @@ void pa_sink_invalidate_requested_latency(pa_sink *s) {
/* Called from main thread */
void pa_sink_set_latency_range(pa_sink *s, pa_usec_t min_latency, pa_usec_t max_latency) {
pa_sink_assert_ref(s);
+ pa_assert_ctl_context();
/* min_latency == 0: no limit
* min_latency anything else: specified limit
@@ -2195,6 +2346,7 @@ void pa_sink_set_latency_range(pa_sink *s, pa_usec_t min_latency, pa_usec_t max_
/* Called from main thread */
void pa_sink_get_latency_range(pa_sink *s, pa_usec_t *min_latency, pa_usec_t *max_latency) {
pa_sink_assert_ref(s);
+ pa_assert_ctl_context();
pa_assert(min_latency);
pa_assert(max_latency);
@@ -2213,9 +2365,8 @@ void pa_sink_get_latency_range(pa_sink *s, pa_usec_t *min_latency, pa_usec_t *ma
/* Called from IO thread */
void pa_sink_set_latency_range_within_thread(pa_sink *s, pa_usec_t min_latency, pa_usec_t max_latency) {
- void *state = NULL;
-
pa_sink_assert_ref(s);
+ pa_sink_assert_io_context(s);
pa_assert(min_latency >= ABSOLUTE_MIN_LATENCY);
pa_assert(max_latency <= ABSOLUTE_MAX_LATENCY);
@@ -2226,27 +2377,36 @@ void pa_sink_set_latency_range_within_thread(pa_sink *s, pa_usec_t min_latency,
max_latency == ABSOLUTE_MAX_LATENCY) ||
(s->flags & PA_SINK_DYNAMIC_LATENCY));
+ if (s->thread_info.min_latency == min_latency &&
+ s->thread_info.max_latency == max_latency)
+ return;
+
s->thread_info.min_latency = min_latency;
s->thread_info.max_latency = max_latency;
if (PA_SINK_IS_LINKED(s->thread_info.state)) {
pa_sink_input *i;
+ void *state = NULL;
- while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
+ PA_HASHMAP_FOREACH(i, s->thread_info.inputs, state)
if (i->update_sink_latency_range)
i->update_sink_latency_range(i);
}
- pa_sink_invalidate_requested_latency(s);
+ pa_sink_invalidate_requested_latency(s, FALSE);
pa_source_set_latency_range_within_thread(s->monitor_source, min_latency, max_latency);
}
-/* Called from main thread, before the sink is put */
+/* Called from main thread */
void pa_sink_set_fixed_latency(pa_sink *s, pa_usec_t latency) {
pa_sink_assert_ref(s);
+ pa_assert_ctl_context();
- pa_assert(pa_sink_get_state(s) == PA_SINK_INIT);
+ if (s->flags & PA_SINK_DYNAMIC_LATENCY) {
+ pa_assert(latency == 0);
+ return;
+ }
if (latency < ABSOLUTE_MIN_LATENCY)
latency = ABSOLUTE_MIN_LATENCY;
@@ -2254,14 +2414,69 @@ void pa_sink_set_fixed_latency(pa_sink *s, pa_usec_t latency) {
if (latency > ABSOLUTE_MAX_LATENCY)
latency = ABSOLUTE_MAX_LATENCY;
- s->fixed_latency = latency;
+ if (PA_SINK_IS_LINKED(s->state))
+ pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_FIXED_LATENCY, NULL, (int64_t) latency, NULL) == 0);
+ else
+ s->thread_info.fixed_latency = latency;
+
pa_source_set_fixed_latency(s->monitor_source, latency);
}
+/* Called from main thread */
+pa_usec_t pa_sink_get_fixed_latency(pa_sink *s) {
+ pa_usec_t latency;
+
+ pa_sink_assert_ref(s);
+ pa_assert_ctl_context();
+
+ if (s->flags & PA_SINK_DYNAMIC_LATENCY)
+ return 0;
+
+ if (PA_SINK_IS_LINKED(s->state))
+ pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_FIXED_LATENCY, &latency, 0, NULL) == 0);
+ else
+ latency = s->thread_info.fixed_latency;
+
+ return latency;
+}
+
+/* Called from IO thread */
+void pa_sink_set_fixed_latency_within_thread(pa_sink *s, pa_usec_t latency) {
+ pa_sink_assert_ref(s);
+ pa_sink_assert_io_context(s);
+
+ if (s->flags & PA_SINK_DYNAMIC_LATENCY) {
+ pa_assert(latency == 0);
+ return;
+ }
+
+ pa_assert(latency >= ABSOLUTE_MIN_LATENCY);
+ pa_assert(latency <= ABSOLUTE_MAX_LATENCY);
+
+ if (s->thread_info.fixed_latency == latency)
+ return;
+
+ s->thread_info.fixed_latency = latency;
+
+ if (PA_SINK_IS_LINKED(s->thread_info.state)) {
+ pa_sink_input *i;
+ void *state = NULL;
+
+ PA_HASHMAP_FOREACH(i, s->thread_info.inputs, state)
+ if (i->update_sink_fixed_latency)
+ i->update_sink_fixed_latency(i);
+ }
+
+ pa_sink_invalidate_requested_latency(s, FALSE);
+
+ pa_source_set_fixed_latency_within_thread(s->monitor_source, latency);
+}
+
/* Called from main context */
size_t pa_sink_get_max_rewind(pa_sink *s) {
size_t r;
pa_sink_assert_ref(s);
+ pa_assert_ctl_context();
if (!PA_SINK_IS_LINKED(s->state))
return s->thread_info.max_rewind;
@@ -2275,6 +2490,7 @@ size_t pa_sink_get_max_rewind(pa_sink *s) {
size_t pa_sink_get_max_request(pa_sink *s) {
size_t r;
pa_sink_assert_ref(s);
+ pa_assert_ctl_context();
if (!PA_SINK_IS_LINKED(s->state))
return s->thread_info.max_request;
@@ -2288,7 +2504,8 @@ size_t pa_sink_get_max_request(pa_sink *s) {
int pa_sink_set_port(pa_sink *s, const char *name, pa_bool_t save) {
pa_device_port *port;
- pa_assert(s);
+ pa_sink_assert_ref(s);
+ pa_assert_ctl_context();
if (!s->set_port) {
pa_log_debug("set_port() operation not implemented for sink %u \"%s\"", s->index, s->name);
@@ -2319,7 +2536,6 @@ int pa_sink_set_port(pa_sink *s, const char *name, pa_bool_t save) {
return 0;
}
-/* Called from main context */
pa_bool_t pa_device_init_icon(pa_proplist *p, pa_bool_t is_sink) {
const char *ff, *c, *t = NULL, *s = "", *profile, *bus;
@@ -2434,3 +2650,48 @@ pa_bool_t pa_device_init_intended_roles(pa_proplist *p) {
return FALSE;
}
+
+unsigned pa_device_init_priority(pa_proplist *p) {
+ const char *s;
+ unsigned priority = 0;
+
+ pa_assert(p);
+
+ if ((s = pa_proplist_gets(p, PA_PROP_DEVICE_CLASS))) {
+
+ if (pa_streq(s, "sound"))
+ priority += 9000;
+ else if (!pa_streq(s, "modem"))
+ priority += 1000;
+ }
+
+ if ((s = pa_proplist_gets(p, PA_PROP_DEVICE_FORM_FACTOR))) {
+
+ if (pa_streq(s, "internal"))
+ priority += 900;
+ else if (pa_streq(s, "speaker"))
+ priority += 500;
+ else if (pa_streq(s, "headphone"))
+ priority += 400;
+ }
+
+ if ((s = pa_proplist_gets(p, PA_PROP_DEVICE_BUS))) {
+
+ if (pa_streq(s, "pci"))
+ priority += 50;
+ else if (pa_streq(s, "usb"))
+ priority += 40;
+ else if (pa_streq(s, "bluetooth"))
+ priority += 30;
+ }
+
+ if ((s = pa_proplist_gets(p, PA_PROP_DEVICE_PROFILE_NAME))) {
+
+ if (pa_startswith(s, "analog-"))
+ priority += 9;
+ else if (pa_startswith(s, "iec958-"))
+ priority += 8;
+ }
+
+ return priority;
+}
diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h
index d16fcc01..ba547fc3 100644
--- a/src/pulsecore/sink.h
+++ b/src/pulsecore/sink.h
@@ -42,6 +42,7 @@ typedef struct pa_device_port pa_device_port;
#include <pulsecore/rtpoll.h>
#include <pulsecore/card.h>
#include <pulsecore/queue.h>
+#include <pulsecore/thread-mq.h>
#define PA_MAX_INPUTS_PER_SINK 32
@@ -89,9 +90,10 @@ struct pa_sink {
unsigned n_volume_steps; /* shall be constant */
/* Also see http://pulseaudio.org/wiki/InternalVolumes */
- pa_cvolume virtual_volume; /* The volume clients are informed about */
- pa_cvolume reference_volume; /* The volume taken as refernce base for relative sink input volumes */
+ pa_cvolume reference_volume; /* The volume exported and taken as reference base for relative sink input volumes */
+ pa_cvolume real_volume; /* The volume that the hardware is configured to */
pa_cvolume soft_volume; /* The internal software volume we apply to all PCM data while it passes through */
+
pa_bool_t muted:1;
pa_bool_t refresh_volume:1;
@@ -101,15 +103,14 @@ struct pa_sink {
pa_bool_t save_muted:1;
pa_asyncmsgq *asyncmsgq;
- pa_rtpoll *rtpoll;
pa_memchunk silence;
- pa_usec_t fixed_latency; /* for sinks with PA_SINK_DYNAMIC_LATENCY this is 0 */
-
pa_hashmap *ports;
pa_device_port *active_port;
+ unsigned priority;
+
/* Called when the main loop requests a state change. Called from
* main loop context. If returns -1 the state change will be
* inhibited */
@@ -155,9 +156,14 @@ struct pa_sink {
pa_sink_state_t state;
pa_hashmap *inputs;
+ pa_rtpoll *rtpoll;
+
pa_cvolume soft_volume;
pa_bool_t soft_muted:1;
+ /* The requested latency is used for dynamic latency
+ * sinks. For fixed latency sinks it is always identical to
+ * the fixed_latency. See below. */
pa_bool_t requested_latency_valid:1;
pa_usec_t requested_latency;
@@ -173,14 +179,21 @@ struct pa_sink {
size_t rewind_nbytes;
pa_bool_t rewind_requested;
+ /* Both dynamic and fixed latencies will be clamped to this
+ * range. */
pa_usec_t min_latency; /* we won't go below this latency */
pa_usec_t max_latency; /* An upper limit for the latencies */
+
+ /* 'Fixed' simply means that the latency is exclusively
+ * decided on by the sink, and the clients have no influence
+ * in changing it */
+ pa_usec_t fixed_latency; /* for sinks with PA_SINK_DYNAMIC_LATENCY this is 0 */
} thread_info;
void *userdata;
};
-PA_DECLARE_CLASS(pa_sink);
+PA_DECLARE_PUBLIC_CLASS(pa_sink);
#define PA_SINK(s) (pa_sink_cast(s))
typedef enum pa_sink_message {
@@ -200,6 +213,8 @@ typedef enum pa_sink_message {
PA_SINK_MESSAGE_DETACH,
PA_SINK_MESSAGE_SET_LATENCY_RANGE,
PA_SINK_MESSAGE_GET_LATENCY_RANGE,
+ PA_SINK_MESSAGE_SET_FIXED_LATENCY,
+ PA_SINK_MESSAGE_GET_FIXED_LATENCY,
PA_SINK_MESSAGE_GET_MAX_REWIND,
PA_SINK_MESSAGE_GET_MAX_REQUEST,
PA_SINK_MESSAGE_SET_MAX_REWIND,
@@ -267,12 +282,15 @@ void pa_sink_detach(pa_sink *s);
void pa_sink_attach(pa_sink *s);
void pa_sink_set_soft_volume(pa_sink *s, const pa_cvolume *volume);
-void pa_sink_volume_changed(pa_sink *s, const pa_cvolume *new_volume, pa_bool_t save);
-void pa_sink_mute_changed(pa_sink *s, pa_bool_t new_muted, pa_bool_t save);
+void pa_sink_volume_changed(pa_sink *s, const pa_cvolume *new_volume);
+void pa_sink_mute_changed(pa_sink *s, pa_bool_t new_muted);
+
+void pa_sink_update_flags(pa_sink *s, pa_sink_flags_t mask, pa_sink_flags_t value);
pa_bool_t pa_device_init_description(pa_proplist *p);
pa_bool_t pa_device_init_icon(pa_proplist *p, pa_bool_t is_sink);
pa_bool_t pa_device_init_intended_roles(pa_proplist *p);
+unsigned pa_device_init_priority(pa_proplist *p);
/**** May be called by everyone, from main context */
@@ -280,6 +298,7 @@ pa_bool_t pa_device_init_intended_roles(pa_proplist *p);
pa_usec_t pa_sink_get_latency(pa_sink *s);
pa_usec_t pa_sink_get_requested_latency(pa_sink *s);
void pa_sink_get_latency_range(pa_sink *s, pa_usec_t *min_latency, pa_usec_t *max_latency);
+pa_usec_t pa_sink_get_fixed_latency(pa_sink *s);
size_t pa_sink_get_max_rewind(pa_sink *s);
size_t pa_sink_get_max_request(pa_sink *s);
@@ -288,11 +307,8 @@ int pa_sink_update_status(pa_sink*s);
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);
-
-void pa_sink_set_volume(pa_sink *sink, const pa_cvolume *volume, pa_bool_t propagate, pa_bool_t sendmsg, pa_bool_t become_reference, pa_bool_t save);
-const pa_cvolume *pa_sink_get_volume(pa_sink *sink, pa_bool_t force_refresh, pa_bool_t reference);
+void pa_sink_set_volume(pa_sink *sink, const pa_cvolume *volume, pa_bool_t sendmsg, pa_bool_t save);
+const pa_cvolume *pa_sink_get_volume(pa_sink *sink, pa_bool_t force_refresh);
void pa_sink_set_mute(pa_sink *sink, pa_bool_t mute, pa_bool_t save);
pa_bool_t pa_sink_get_mute(pa_sink *sink, pa_bool_t force_refresh);
@@ -331,16 +347,23 @@ void pa_sink_set_max_rewind_within_thread(pa_sink *s, size_t max_rewind);
void pa_sink_set_max_request_within_thread(pa_sink *s, size_t max_request);
void pa_sink_set_latency_range_within_thread(pa_sink *s, pa_usec_t min_latency, pa_usec_t max_latency);
+void pa_sink_set_fixed_latency_within_thread(pa_sink *s, pa_usec_t latency);
/*** To be called exclusively by sink input drivers, from IO context */
void pa_sink_request_rewind(pa_sink*s, size_t nbytes);
-void pa_sink_invalidate_requested_latency(pa_sink *s);
+void pa_sink_invalidate_requested_latency(pa_sink *s, pa_bool_t dynamic);
pa_usec_t pa_sink_get_latency_within_thread(pa_sink *s);
pa_device_port *pa_device_port_new(const char *name, const char *description, size_t extra);
void pa_device_port_free(pa_device_port *p);
+/* Verify that we called in IO context (aka 'thread context), or that
+ * the sink is not yet set up, i.e. the thread not set up yet. See
+ * pa_assert_io_context() in thread-mq.h for more information. */
+#define pa_sink_assert_io_context(s) \
+ pa_assert(pa_thread_mq_get() || !PA_SINK_IS_LINKED((s)->state))
+
#endif
diff --git a/src/pulsecore/socket-client.c b/src/pulsecore/socket-client.c
index 24535157..ef3c29ee 100644
--- a/src/pulsecore/socket-client.c
+++ b/src/pulsecore/socket-client.c
@@ -73,14 +73,18 @@
struct pa_socket_client {
PA_REFCNT_DECLARE;
- pa_mainloop_api *mainloop;
int fd;
+
+ pa_mainloop_api *mainloop;
pa_io_event *io_event;
pa_time_event *timeout_event;
pa_defer_event *defer_event;
+
pa_socket_client_cb_t callback;
void *userdata;
+
pa_bool_t local;
+
#ifdef HAVE_LIBASYNCNS
asyncns_t *asyncns;
asyncns_query_t * asyncns_query;
@@ -92,22 +96,10 @@ static pa_socket_client* socket_client_new(pa_mainloop_api *m) {
pa_socket_client *c;
pa_assert(m);
- c = pa_xnew(pa_socket_client, 1);
+ c = pa_xnew0(pa_socket_client, 1);
PA_REFCNT_INIT(c);
c->mainloop = m;
c->fd = -1;
- c->io_event = NULL;
- c->timeout_event = NULL;
- c->defer_event = NULL;
- c->callback = NULL;
- c->userdata = NULL;
- c->local = FALSE;
-
-#ifdef HAVE_LIBASYNCNS
- c->asyncns = NULL;
- c->asyncns_io_event = NULL;
- c->asyncns_query = NULL;
-#endif
return c;
}
@@ -163,7 +155,6 @@ static void do_call(pa_socket_client *c) {
}
io = pa_iochannel_new(c->mainloop, c->fd, c->fd);
- pa_assert(io);
finish:
if (!io && c->fd >= 0)
@@ -172,7 +163,6 @@ finish:
free_events(c);
- pa_assert(c->callback);
c->callback(c, io, c->userdata);
pa_socket_client_unref(c);
@@ -202,8 +192,6 @@ static void connect_io_cb(pa_mainloop_api*m, pa_io_event *e, int fd, pa_io_event
}
static int do_connect(pa_socket_client *c, const struct sockaddr *sa, socklen_t len) {
- int r;
-
pa_assert(c);
pa_assert(PA_REFCNT_VALUE(c) >= 1);
pa_assert(sa);
@@ -211,7 +199,7 @@ static int do_connect(pa_socket_client *c, const struct sockaddr *sa, socklen_t
pa_make_fd_nonblock(c->fd);
- if ((r = connect(c->fd, sa, len)) < 0) {
+ if (connect(c->fd, sa, len) < 0) {
#ifdef OS_IS_WIN32
if (WSAGetLastError() != EWOULDBLOCK) {
pa_log_debug("connect(): %d", WSAGetLastError());
@@ -222,9 +210,9 @@ static int do_connect(pa_socket_client *c, const struct sockaddr *sa, socklen_t
return -1;
}
- pa_assert_se(c->io_event = c->mainloop->io_new(c->mainloop, c->fd, PA_IO_EVENT_OUTPUT, connect_io_cb, c));
+ c->io_event = c->mainloop->io_new(c->mainloop, c->fd, PA_IO_EVENT_OUTPUT, connect_io_cb, c);
} else
- pa_assert_se(c->defer_event = c->mainloop->defer_new(c->mainloop, connect_defer_cb, c));
+ c->defer_event = c->mainloop->defer_new(c->mainloop, connect_defer_cb, c);
return 0;
}
@@ -235,7 +223,7 @@ pa_socket_client* pa_socket_client_new_ipv4(pa_mainloop_api *m, uint32_t address
pa_assert(m);
pa_assert(port > 0);
- memset(&sa, 0, sizeof(sa));
+ pa_zero(sa);
sa.sin_family = AF_INET;
sa.sin_port = htons(port);
sa.sin_addr.s_addr = htonl(address);
@@ -243,28 +231,24 @@ pa_socket_client* pa_socket_client_new_ipv4(pa_mainloop_api *m, uint32_t address
return pa_socket_client_new_sockaddr(m, (struct sockaddr*) &sa, sizeof(sa));
}
-#ifdef HAVE_SYS_UN_H
pa_socket_client* pa_socket_client_new_unix(pa_mainloop_api *m, const char *filename) {
+#ifdef HAVE_SYS_UN_H
struct sockaddr_un sa;
pa_assert(m);
pa_assert(filename);
- memset(&sa, 0, sizeof(sa));
+ pa_zero(sa);
sa.sun_family = AF_UNIX;
pa_strlcpy(sa.sun_path, filename, sizeof(sa.sun_path));
return pa_socket_client_new_sockaddr(m, (struct sockaddr*) &sa, sizeof(sa));
-}
-
#else /* HAVE_SYS_UN_H */
-pa_socket_client* pa_socket_client_new_unix(pa_mainloop_api *m, const char *filename) {
return NULL;
-}
-
#endif /* HAVE_SYS_UN_H */
+}
static int sockaddr_prepare(pa_socket_client *c, const struct sockaddr *sa, size_t salen) {
pa_assert(c);
@@ -273,13 +257,11 @@ static int sockaddr_prepare(pa_socket_client *c, const struct sockaddr *sa, size
c->local = pa_socket_address_is_local(sa);
- if ((c->fd = socket(sa->sa_family, SOCK_STREAM, 0)) < 0) {
+ if ((c->fd = pa_socket_cloexec(sa->sa_family, SOCK_STREAM, 0)) < 0) {
pa_log("socket(): %s", pa_cstrerror(errno));
return -1;
}
- pa_make_fd_cloexec(c->fd);
-
#ifdef HAVE_IPV6
if (sa->sa_family == AF_INET || sa->sa_family == AF_INET6)
#else
@@ -302,7 +284,7 @@ pa_socket_client* pa_socket_client_new_sockaddr(pa_mainloop_api *m, const struct
pa_assert(sa);
pa_assert(salen > 0);
- pa_assert_se(c = socket_client_new(m));
+ c = socket_client_new(m);
if (sockaddr_prepare(c, sa, salen) < 0)
goto fail;
@@ -359,22 +341,25 @@ void pa_socket_client_set_callback(pa_socket_client *c, pa_socket_client_cb_t on
c->userdata = userdata;
}
-#ifdef HAVE_IPV6
pa_socket_client* pa_socket_client_new_ipv6(pa_mainloop_api *m, uint8_t address[16], uint16_t port) {
+#ifdef HAVE_IPV6
struct sockaddr_in6 sa;
pa_assert(m);
pa_assert(address);
pa_assert(port > 0);
- memset(&sa, 0, sizeof(sa));
+ pa_zero(sa);
sa.sin6_family = AF_INET6;
sa.sin6_port = htons(port);
memcpy(&sa.sin6_addr, address, sizeof(sa.sin6_addr));
return pa_socket_client_new_sockaddr(m, (struct sockaddr*) &sa, sizeof(sa));
-}
+
+#else
+ return NULL;
#endif
+}
#ifdef HAVE_LIBASYNCNS
@@ -468,14 +453,13 @@ pa_socket_client* pa_socket_client_new_string(pa_mainloop_api *m, pa_bool_t use_
case PA_PARSED_ADDRESS_TCP4: /* Fallthrough */
case PA_PARSED_ADDRESS_TCP6: /* Fallthrough */
- case PA_PARSED_ADDRESS_TCP_AUTO:{
-
+ case PA_PARSED_ADDRESS_TCP_AUTO: {
struct addrinfo hints;
char port[12];
pa_snprintf(port, sizeof(port), "%u", (unsigned) a.port);
- memset(&hints, 0, sizeof(hints));
+ pa_zero(hints);
if (a.type == PA_PARSED_ADDRESS_TCP4)
hints.ai_family = PF_INET;
#ifdef HAVE_IPV6
@@ -494,11 +478,10 @@ pa_socket_client* pa_socket_client_new_string(pa_mainloop_api *m, pa_bool_t use_
if (!(asyncns = asyncns_new(1)))
goto finish;
- pa_assert_se(c = socket_client_new(m));
+ c = socket_client_new(m);
c->asyncns = asyncns;
c->asyncns_io_event = m->io_new(m, asyncns_fd(c->asyncns), PA_IO_EVENT_INPUT, asyncns_cb, c);
- c->asyncns_query = asyncns_getaddrinfo(c->asyncns, a.path_or_host, port, &hints);
- pa_assert(c->asyncns_query);
+ pa_assert_se(c->asyncns_query = asyncns_getaddrinfo(c->asyncns, a.path_or_host, port, &hints));
start_timeout(c, use_rtclock);
}
#elif defined(HAVE_GETADDRINFO)
@@ -541,6 +524,7 @@ pa_socket_client* pa_socket_client_new_string(pa_mainloop_api *m, pa_bool_t use_
if (!host)
goto finish;
+ pa_zero(sa);
s.sin_family = AF_INET;
memcpy(&s.sin_addr, host->h_addr, sizeof(struct in_addr));
s.sin_port = htons(a.port);
diff --git a/src/pulsecore/socket-client.h b/src/pulsecore/socket-client.h
index b896afa9..138f658c 100644
--- a/src/pulsecore/socket-client.h
+++ b/src/pulsecore/socket-client.h
@@ -35,9 +35,7 @@ typedef struct pa_socket_client pa_socket_client;
typedef void (*pa_socket_client_cb_t)(pa_socket_client *c, pa_iochannel*io, void *userdata);
pa_socket_client* pa_socket_client_new_ipv4(pa_mainloop_api *m, uint32_t address, uint16_t port);
-#ifdef HAVE_IPV6
pa_socket_client* pa_socket_client_new_ipv6(pa_mainloop_api *m, uint8_t address[16], uint16_t port);
-#endif
pa_socket_client* pa_socket_client_new_unix(pa_mainloop_api *m, const char *filename);
pa_socket_client* pa_socket_client_new_sockaddr(pa_mainloop_api *m, const struct sockaddr *sa, size_t salen);
pa_socket_client* pa_socket_client_new_string(pa_mainloop_api *m, pa_bool_t use_rtclock, const char *a, uint16_t default_port);
diff --git a/src/pulsecore/socket-server.c b/src/pulsecore/socket-server.c
index e660700c..5d55de3e 100644
--- a/src/pulsecore/socket-server.c
+++ b/src/pulsecore/socket-server.c
@@ -104,13 +104,11 @@ static void callback(pa_mainloop_api *mainloop, pa_io_event *e, int fd, pa_io_ev
pa_socket_server_ref(s);
- if ((nfd = accept(fd, NULL, NULL)) < 0) {
+ if ((nfd = pa_accept_cloexec(fd, NULL, NULL)) < 0) {
pa_log("accept(): %s", pa_cstrerror(errno));
goto finish;
}
- pa_make_fd_cloexec(nfd);
-
if (!s->on_connection) {
pa_close(nfd);
goto finish;
@@ -186,13 +184,11 @@ pa_socket_server* pa_socket_server_new_unix(pa_mainloop_api *m, const char *file
pa_assert(m);
pa_assert(filename);
- if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) {
+ if ((fd = pa_socket_cloexec(PF_UNIX, SOCK_STREAM, 0)) < 0) {
pa_log("socket(): %s", pa_cstrerror(errno));
goto fail;
}
- pa_make_fd_cloexec(fd);
-
memset(&sa, 0, sizeof(sa));
sa.sun_family = AF_UNIX;
pa_strlcpy(sa.sun_path, filename, sizeof(sa.sun_path));
@@ -246,13 +242,11 @@ pa_socket_server* pa_socket_server_new_ipv4(pa_mainloop_api *m, uint32_t address
pa_assert(m);
pa_assert(port);
- if ((fd = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
+ if ((fd = pa_socket_cloexec(PF_INET, SOCK_STREAM, 0)) < 0) {
pa_log("socket(PF_INET): %s", pa_cstrerror(errno));
goto fail;
}
- pa_make_fd_cloexec(fd);
-
#ifdef SO_REUSEADDR
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
pa_log("setsockopt(): %s", pa_cstrerror(errno));
@@ -299,13 +293,11 @@ pa_socket_server* pa_socket_server_new_ipv6(pa_mainloop_api *m, const uint8_t ad
pa_assert(m);
pa_assert(port > 0);
- if ((fd = socket(PF_INET6, SOCK_STREAM, 0)) < 0) {
+ if ((fd = pa_socket_cloexec(PF_INET6, SOCK_STREAM, 0)) < 0) {
pa_log("socket(PF_INET6): %s", pa_cstrerror(errno));
goto fail;
}
- pa_make_fd_cloexec(fd);
-
#ifdef IPV6_V6ONLY
on = 1;
if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) < 0)
diff --git a/src/pulsecore/socket-util.c b/src/pulsecore/socket-util.c
index 5fd5dd67..2cc9882a 100644
--- a/src/pulsecore/socket-util.c
+++ b/src/pulsecore/socket-util.c
@@ -85,12 +85,11 @@ void pa_socket_peer_to_string(int fd, char *c, size_t l) {
#ifndef OS_IS_WIN32
pa_assert_se(fstat(fd, &st) == 0);
-#endif
-#ifndef OS_IS_WIN32
if (S_ISSOCK(st.st_mode)) {
#endif
union {
+ struct sockaddr_storage storage;
struct sockaddr sa;
struct sockaddr_in in;
#ifdef HAVE_IPV6
@@ -152,7 +151,7 @@ void pa_make_socket_low_delay(int fd) {
pa_assert(fd >= 0);
priority = 6;
- if (setsockopt(fd, SOL_SOCKET, SO_PRIORITY, (void*)&priority, sizeof(priority)) < 0)
+ if (setsockopt(fd, SOL_SOCKET, SO_PRIORITY, &priority, sizeof(priority)) < 0)
pa_log_warn("SO_PRIORITY failed: %s", pa_cstrerror(errno));
#endif
}
@@ -166,9 +165,9 @@ void pa_make_tcp_socket_low_delay(int fd) {
{
int on = 1;
#if defined(SOL_TCP)
- if (setsockopt(fd, SOL_TCP, TCP_NODELAY, (void*)&on, sizeof(on)) < 0)
+ if (setsockopt(fd, SOL_TCP, TCP_NODELAY, &on, sizeof(on)) < 0)
#else
- if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (void*)&on, sizeof(on)) < 0)
+ if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)) < 0)
#endif
pa_log_warn("TCP_NODELAY failed: %s", pa_cstrerror(errno));
}
@@ -178,9 +177,9 @@ void pa_make_tcp_socket_low_delay(int fd) {
{
int tos = IPTOS_LOWDELAY;
#ifdef SOL_IP
- if (setsockopt(fd, SOL_IP, IP_TOS, (void*)&tos, sizeof(tos)) < 0)
+ if (setsockopt(fd, SOL_IP, IP_TOS, &tos, sizeof(tos)) < 0)
#else
- if (setsockopt(fd, IPPROTO_IP, IP_TOS, (void*)&tos, sizeof(tos)) < 0)
+ if (setsockopt(fd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)) < 0)
#endif
pa_log_warn("IP_TOS failed: %s", pa_cstrerror(errno));
}
@@ -196,9 +195,9 @@ void pa_make_udp_socket_low_delay(int fd) {
{
int tos = IPTOS_LOWDELAY;
#ifdef SOL_IP
- if (setsockopt(fd, SOL_IP, IP_TOS, (void*)&tos, sizeof(tos)) < 0)
+ if (setsockopt(fd, SOL_IP, IP_TOS, &tos, sizeof(tos)) < 0)
#else
- if (setsockopt(fd, IPPROTO_IP, IP_TOS, (void*)&tos, sizeof(tos)) < 0)
+ if (setsockopt(fd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)) < 0)
#endif
pa_log_warn("IP_TOS failed: %s", pa_cstrerror(errno));
}
@@ -206,11 +205,11 @@ void pa_make_udp_socket_low_delay(int fd) {
}
int pa_socket_set_rcvbuf(int fd, size_t l) {
- int bufsz = (int)l;
+ int bufsz = (int) l;
pa_assert(fd >= 0);
- if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (void*)&bufsz, sizeof(bufsz)) < 0) {
+ if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &bufsz, sizeof(bufsz)) < 0) {
pa_log_warn("SO_RCVBUF: %s", pa_cstrerror(errno));
return -1;
}
@@ -219,12 +218,12 @@ int pa_socket_set_rcvbuf(int fd, size_t l) {
}
int pa_socket_set_sndbuf(int fd, size_t l) {
- int bufsz = (int)l;
+ int bufsz = (int) l;
pa_assert(fd >= 0);
- if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (void*)&bufsz, sizeof(bufsz)) < 0) {
- pa_log("SO_SNDBUF: %s", pa_cstrerror(errno));
+ if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &bufsz, sizeof(bufsz)) < 0) {
+ pa_log_warn("SO_SNDBUF: %s", pa_cstrerror(errno));
return -1;
}
@@ -239,7 +238,7 @@ int pa_unix_socket_is_stale(const char *fn) {
pa_assert(fn);
- if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) {
+ if ((fd = pa_socket_cloexec(PF_UNIX, SOCK_STREAM, 0)) < 0) {
pa_log("socket(): %s", pa_cstrerror(errno));
goto finish;
}
@@ -315,6 +314,7 @@ pa_bool_t pa_socket_address_is_local(const struct sockaddr *sa) {
pa_bool_t pa_socket_is_local(int fd) {
union {
+ struct sockaddr_storage storage;
struct sockaddr sa;
struct sockaddr_in in;
#ifdef HAVE_IPV6
diff --git a/src/pulsecore/sound-file-stream.c b/src/pulsecore/sound-file-stream.c
index 502e5c69..53674ba1 100644
--- a/src/pulsecore/sound-file-stream.c
+++ b/src/pulsecore/sound-file-stream.c
@@ -64,9 +64,8 @@ enum {
FILE_STREAM_MESSAGE_UNLINK
};
-PA_DECLARE_CLASS(file_stream);
+PA_DEFINE_PRIVATE_CLASS(file_stream, pa_msgobject);
#define FILE_STREAM(o) (file_stream_cast(o))
-static PA_DEFINE_CHECK_TYPE(file_stream, pa_msgobject);
/* Called from main context */
static void file_stream_unlink(file_stream *u) {
@@ -253,11 +252,7 @@ int pa_play_file(
u->readf_function = NULL;
u->memblockq = NULL;
- if ((fd = open(fname, O_RDONLY
-#ifdef O_NOCTTY
- |O_NOCTTY
-#endif
- )) < 0) {
+ if ((fd = pa_open_cloexec(fname, O_RDONLY, 0)) < 0) {
pa_log("Failed to open file %s: %s", fname, pa_cstrerror(errno));
goto fail;
}
@@ -312,7 +307,7 @@ int pa_play_file(
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(&u->sink_input, sink->core, &data);
pa_sink_input_new_data_done(&data);
if (!u->sink_input)
@@ -335,8 +330,7 @@ int pa_play_file(
return 0;
fail:
- if (u)
- file_stream_unref(u);
+ file_stream_unref(u);
if (fd >= 0)
pa_close(fd);
diff --git a/src/pulsecore/sound-file.c b/src/pulsecore/sound-file.c
index 2d9b76ad..d8c10b1e 100644
--- a/src/pulsecore/sound-file.c
+++ b/src/pulsecore/sound-file.c
@@ -62,11 +62,7 @@ int pa_sound_file_load(
pa_memchunk_reset(chunk);
- if ((fd = open(fname, O_RDONLY
-#ifdef O_NOCTTY
- |O_NOCTTY
-#endif
- )) < 0) {
+ if ((fd = pa_open_cloexec(fname, O_RDONLY, 0)) < 0) {
pa_log("Failed to open file %s: %s", fname, pa_cstrerror(errno));
goto finish;
}
diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c
index 4ba25ae4..57ccc067 100644
--- a/src/pulsecore/source-output.c
+++ b/src/pulsecore/source-output.c
@@ -41,14 +41,14 @@
#define MEMBLOCKQ_MAXLENGTH (32*1024*1024)
-static PA_DEFINE_CHECK_TYPE(pa_source_output, pa_msgobject);
+PA_DEFINE_PUBLIC_CLASS(pa_source_output, pa_msgobject);
static void source_output_free(pa_object* mo);
pa_source_output_new_data* pa_source_output_new_data_init(pa_source_output_new_data *data) {
pa_assert(data);
- memset(data, 0, sizeof(*data));
+ pa_zero(*data);
data->resample_method = PA_RESAMPLER_INVALID;
data->proplist = pa_proplist_new();
@@ -84,6 +84,7 @@ static void reset_callbacks(pa_source_output *o) {
o->update_max_rewind = NULL;
o->update_source_requested_latency = NULL;
o->update_source_latency_range = NULL;
+ o->update_source_fixed_latency = NULL;
o->attach = NULL;
o->detach = NULL;
o->suspend = NULL;
@@ -100,17 +101,18 @@ static void reset_callbacks(pa_source_output *o) {
int pa_source_output_new(
pa_source_output**_o,
pa_core *core,
- pa_source_output_new_data *data,
- pa_source_output_flags_t flags) {
+ pa_source_output_new_data *data) {
pa_source_output *o;
pa_resampler *resampler = NULL;
char st[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
int r;
+ char *pt;
pa_assert(_o);
pa_assert(core);
pa_assert(data);
+ pa_assert_ctl_context();
if (data->client)
pa_proplist_update(data->proplist, PA_UPDATE_MERGE, data->client->proplist);
@@ -144,13 +146,13 @@ int pa_source_output_new(
pa_return_val_if_fail(pa_channel_map_valid(&data->channel_map), -PA_ERR_INVALID);
pa_return_val_if_fail(pa_channel_map_compatible(&data->channel_map, &data->sample_spec), -PA_ERR_INVALID);
- if (flags & PA_SOURCE_OUTPUT_FIX_FORMAT)
+ if (data->flags & PA_SOURCE_OUTPUT_FIX_FORMAT)
data->sample_spec.format = data->source->sample_spec.format;
- if (flags & PA_SOURCE_OUTPUT_FIX_RATE)
+ if (data->flags & PA_SOURCE_OUTPUT_FIX_RATE)
data->sample_spec.rate = data->source->sample_spec.rate;
- if (flags & PA_SOURCE_OUTPUT_FIX_CHANNELS) {
+ if (data->flags & PA_SOURCE_OUTPUT_FIX_CHANNELS) {
data->sample_spec.channels = data->source->sample_spec.channels;
data->channel_map = data->source->channel_map;
}
@@ -166,7 +168,7 @@ int pa_source_output_new(
if ((r = pa_hook_fire(&core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_FIXATE], data)) < 0)
return r;
- if ((flags & PA_SOURCE_OUTPUT_FAIL_ON_SUSPEND) &&
+ if ((data->flags & PA_SOURCE_OUTPUT_NO_CREATE_ON_SUSPEND) &&
pa_source_get_state(data->source) == PA_SOURCE_SUSPENDED) {
pa_log("Failed to create source output: source is suspended.");
return -PA_ERR_BADSTATE;
@@ -177,7 +179,7 @@ int pa_source_output_new(
return -PA_ERR_TOOLARGE;
}
- if ((flags & PA_SOURCE_OUTPUT_VARIABLE_RATE) ||
+ if ((data->flags & PA_SOURCE_OUTPUT_VARIABLE_RATE) ||
!pa_sample_spec_equal(&data->sample_spec, &data->source->sample_spec) ||
!pa_channel_map_equal(&data->channel_map, &data->source->channel_map)) {
@@ -186,9 +188,9 @@ int pa_source_output_new(
&data->source->sample_spec, &data->source->channel_map,
&data->sample_spec, &data->channel_map,
data->resample_method,
- ((flags & PA_SOURCE_OUTPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0) |
- ((flags & PA_SOURCE_OUTPUT_NO_REMAP) ? PA_RESAMPLER_NO_REMAP : 0) |
- (core->disable_remixing || (flags & PA_SOURCE_OUTPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0) |
+ ((data->flags & PA_SOURCE_OUTPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0) |
+ ((data->flags & PA_SOURCE_OUTPUT_NO_REMAP) ? PA_RESAMPLER_NO_REMAP : 0) |
+ (core->disable_remixing || (data->flags & PA_SOURCE_OUTPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0) |
(core->disable_lfe_remixing ? PA_RESAMPLER_NO_LFE : 0)))) {
pa_log_warn("Unsupported resampling operation.");
return -PA_ERR_NOTSUPPORTED;
@@ -201,7 +203,7 @@ int pa_source_output_new(
o->core = core;
o->state = PA_SOURCE_OUTPUT_INIT;
- o->flags = flags;
+ o->flags = data->flags;
o->proplist = pa_proplist_copy(data->proplist);
o->driver = pa_xstrdup(pa_path_get_filename(data->driver));
o->module = data->module;
@@ -246,12 +248,15 @@ int pa_source_output_new(
if (o->direct_on_input)
pa_assert_se(pa_idxset_put(o->direct_on_input->direct_outputs, o, NULL) == 0);
- pa_log_info("Created output %u \"%s\" on %s with sample spec %s and channel map %s",
+ pt = pa_proplist_to_string_sep(o->proplist, "\n ");
+ pa_log_info("Created output %u \"%s\" on %s with sample spec %s and channel map %s\n %s",
o->index,
pa_strnull(pa_proplist_gets(o->proplist, PA_PROP_MEDIA_NAME)),
o->source->name,
pa_sample_spec_snprint(st, sizeof(st), &o->sample_spec),
- pa_channel_map_snprint(cm, sizeof(cm), &o->channel_map));
+ pa_channel_map_snprint(cm, sizeof(cm), &o->channel_map),
+ pt);
+ pa_xfree(pt);
/* Don't forget to call pa_source_output_put! */
@@ -262,6 +267,7 @@ int pa_source_output_new(
/* Called from main context */
static void update_n_corked(pa_source_output *o, pa_source_output_state_t state) {
pa_assert(o);
+ pa_assert_ctl_context();
if (!o->source)
return;
@@ -275,6 +281,7 @@ static void update_n_corked(pa_source_output *o, pa_source_output_state_t state)
/* Called from main context */
static void source_output_set_state(pa_source_output *o, pa_source_output_state_t state) {
pa_assert(o);
+ pa_assert_ctl_context();
if (o->state == state)
return;
@@ -294,6 +301,7 @@ static void source_output_set_state(pa_source_output *o, pa_source_output_state_
void pa_source_output_unlink(pa_source_output*o) {
pa_bool_t linked;
pa_assert(o);
+ pa_assert_ctl_context();
/* See pa_sink_unlink() for a couple of comments how this function
* works */
@@ -346,6 +354,7 @@ static void source_output_free(pa_object* mo) {
pa_source_output *o = PA_SOURCE_OUTPUT(mo);
pa_assert(o);
+ pa_assert_ctl_context();
pa_assert(pa_source_output_refcnt(o) == 0);
if (PA_SOURCE_OUTPUT_IS_LINKED(o->state))
@@ -353,8 +362,6 @@ static void source_output_free(pa_object* mo) {
pa_log_info("Freeing output %u \"%s\"", o->index, pa_strnull(pa_proplist_gets(o->proplist, PA_PROP_MEDIA_NAME)));
- pa_assert(!o->thread_info.attached);
-
if (o->thread_info.delay_memblockq)
pa_memblockq_free(o->thread_info.delay_memblockq);
@@ -371,7 +378,9 @@ static void source_output_free(pa_object* mo) {
/* Called from main context */
void pa_source_output_put(pa_source_output *o) {
pa_source_output_state_t state;
+
pa_source_output_assert_ref(o);
+ pa_assert_ctl_context();
pa_assert(o->state == PA_SOURCE_OUTPUT_INIT);
@@ -395,6 +404,7 @@ void pa_source_output_put(pa_source_output *o) {
/* Called from main context */
void pa_source_output_kill(pa_source_output*o) {
pa_source_output_assert_ref(o);
+ pa_assert_ctl_context();
pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
o->kill(o);
@@ -405,6 +415,7 @@ pa_usec_t pa_source_output_get_latency(pa_source_output *o, pa_usec_t *source_la
pa_usec_t r[2] = { 0, 0 };
pa_source_output_assert_ref(o);
+ pa_assert_ctl_context();
pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_GET_LATENCY, r, 0, NULL) == 0);
@@ -424,6 +435,7 @@ void pa_source_output_push(pa_source_output *o, const pa_memchunk *chunk) {
size_t limit, mbs = 0;
pa_source_output_assert_ref(o);
+ pa_source_output_assert_io_context(o);
pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->thread_info.state));
pa_assert(chunk);
pa_assert(pa_frame_aligned(chunk->length, &o->source->sample_spec));
@@ -499,8 +511,9 @@ void pa_source_output_push(pa_source_output *o, const pa_memchunk *chunk) {
/* Called from thread context */
void pa_source_output_process_rewind(pa_source_output *o, size_t nbytes /* in source sample spec */) {
- pa_source_output_assert_ref(o);
+ pa_source_output_assert_ref(o);
+ pa_source_output_assert_io_context(o);
pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->thread_info.state));
pa_assert(pa_frame_aligned(nbytes, &o->source->sample_spec));
@@ -526,8 +539,17 @@ void pa_source_output_process_rewind(pa_source_output *o, size_t nbytes /* in so
}
/* Called from thread context */
+size_t pa_source_output_get_max_rewind(pa_source_output *o) {
+ pa_source_output_assert_ref(o);
+ pa_source_output_assert_io_context(o);
+
+ return o->thread_info.resampler ? pa_resampler_request(o->thread_info.resampler, o->source->thread_info.max_rewind) : o->source->thread_info.max_rewind;
+}
+
+/* Called from thread context */
void pa_source_output_update_max_rewind(pa_source_output *o, size_t nbytes /* in the source's sample spec */) {
pa_source_output_assert_ref(o);
+ pa_source_output_assert_io_context(o);
pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->thread_info.state));
pa_assert(pa_frame_aligned(nbytes, &o->source->sample_spec));
@@ -538,15 +560,16 @@ void pa_source_output_update_max_rewind(pa_source_output *o, size_t nbytes /* i
/* Called from thread context */
pa_usec_t pa_source_output_set_requested_latency_within_thread(pa_source_output *o, pa_usec_t usec) {
pa_source_output_assert_ref(o);
+ pa_source_output_assert_io_context(o);
if (!(o->source->flags & PA_SOURCE_DYNAMIC_LATENCY))
- usec = o->source->fixed_latency;
+ usec = o->source->thread_info.fixed_latency;
if (usec != (pa_usec_t) -1)
usec = PA_CLAMP(usec, o->source->thread_info.min_latency, o->source->thread_info.max_latency);
o->thread_info.requested_source_latency = usec;
- pa_source_invalidate_requested_latency(o->source);
+ pa_source_invalidate_requested_latency(o->source, TRUE);
return usec;
}
@@ -554,6 +577,7 @@ pa_usec_t pa_source_output_set_requested_latency_within_thread(pa_source_output
/* Called from main context */
pa_usec_t pa_source_output_set_requested_latency(pa_source_output *o, pa_usec_t usec) {
pa_source_output_assert_ref(o);
+ pa_assert_ctl_context();
if (PA_SOURCE_OUTPUT_IS_LINKED(o->state) && o->source) {
pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_SET_REQUESTED_LATENCY, &usec, 0, NULL) == 0);
@@ -565,7 +589,7 @@ pa_usec_t pa_source_output_set_requested_latency(pa_source_output *o, pa_usec_t
if (o->source) {
if (!(o->source->flags & PA_SOURCE_DYNAMIC_LATENCY))
- usec = o->source->fixed_latency;
+ usec = pa_source_get_fixed_latency(o->source);
if (usec != (pa_usec_t) -1) {
pa_usec_t min_latency, max_latency;
@@ -582,6 +606,7 @@ pa_usec_t pa_source_output_set_requested_latency(pa_source_output *o, pa_usec_t
/* Called from main context */
pa_usec_t pa_source_output_get_requested_latency(pa_source_output *o) {
pa_source_output_assert_ref(o);
+ pa_assert_ctl_context();
if (PA_SOURCE_OUTPUT_IS_LINKED(o->state) && o->source) {
pa_usec_t usec = 0;
@@ -598,6 +623,7 @@ pa_usec_t pa_source_output_get_requested_latency(pa_source_output *o) {
/* Called from main context */
void pa_source_output_cork(pa_source_output *o, pa_bool_t b) {
pa_source_output_assert_ref(o);
+ pa_assert_ctl_context();
pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
source_output_set_state(o, b ? PA_SOURCE_OUTPUT_CORKED : PA_SOURCE_OUTPUT_RUNNING);
@@ -606,6 +632,7 @@ void pa_source_output_cork(pa_source_output *o, pa_bool_t b) {
/* Called from main context */
int pa_source_output_set_rate(pa_source_output *o, uint32_t rate) {
pa_source_output_assert_ref(o);
+ pa_assert_ctl_context();
pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
pa_return_val_if_fail(o->thread_info.resampler, -PA_ERR_BADSTATE);
@@ -623,6 +650,7 @@ int pa_source_output_set_rate(pa_source_output *o, uint32_t rate) {
/* Called from main context */
void pa_source_output_set_name(pa_source_output *o, const char *name) {
const char *old;
+ pa_assert_ctl_context();
pa_source_output_assert_ref(o);
if (!name && !pa_proplist_contains(o->proplist, PA_PROP_MEDIA_NAME))
@@ -647,11 +675,12 @@ void pa_source_output_set_name(pa_source_output *o, const char *name) {
/* Called from main thread */
void pa_source_output_update_proplist(pa_source_output *o, pa_update_mode_t mode, pa_proplist *p) {
pa_source_output_assert_ref(o);
+ pa_assert_ctl_context();
if (p)
pa_proplist_update(o->proplist, mode, p);
- if (PA_SINK_IS_LINKED(o->state)) {
+ if (PA_SOURCE_OUTPUT_IS_LINKED(o->state)) {
pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PROPLIST_CHANGED], o);
pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
}
@@ -660,6 +689,7 @@ void pa_source_output_update_proplist(pa_source_output *o, pa_update_mode_t mode
/* Called from main context */
pa_resample_method_t pa_source_output_get_resample_method(pa_source_output *o) {
pa_source_output_assert_ref(o);
+ pa_assert_ctl_context();
return o->actual_resample_method;
}
@@ -667,6 +697,7 @@ pa_resample_method_t pa_source_output_get_resample_method(pa_source_output *o) {
/* Called from main context */
pa_bool_t pa_source_output_may_move(pa_source_output *o) {
pa_source_output_assert_ref(o);
+ pa_assert_ctl_context();
pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
if (o->flags & PA_SOURCE_OUTPUT_DONT_MOVE)
@@ -708,6 +739,7 @@ int pa_source_output_start_move(pa_source_output *o) {
int r;
pa_source_output_assert_ref(o);
+ pa_assert_ctl_context();
pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
pa_assert(o->source);
@@ -739,6 +771,7 @@ int pa_source_output_finish_move(pa_source_output *o, pa_source *dest, pa_bool_t
pa_resampler *new_resampler;
pa_source_output_assert_ref(o);
+ pa_assert_ctl_context();
pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
pa_assert(!o->source);
pa_source_assert_ref(dest);
@@ -816,10 +849,29 @@ int pa_source_output_finish_move(pa_source_output *o, pa_source *dest, pa_bool_t
}
/* Called from main context */
+void pa_source_output_fail_move(pa_source_output *o) {
+
+ pa_source_output_assert_ref(o);
+ pa_assert_ctl_context();
+ pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
+ pa_assert(!o->source);
+
+ /* Check if someone wants this source output? */
+ if (pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_FAIL], o) == PA_HOOK_STOP)
+ return;
+
+ if (o->moving)
+ o->moving(o, NULL);
+
+ pa_source_output_kill(o);
+}
+
+/* Called from main context */
int pa_source_output_move_to(pa_source_output *o, pa_source *dest, pa_bool_t save) {
int r;
pa_source_output_assert_ref(o);
+ pa_assert_ctl_context();
pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
pa_assert(o->source);
pa_source_assert_ref(dest);
@@ -838,6 +890,7 @@ int pa_source_output_move_to(pa_source_output *o, pa_source *dest, pa_bool_t sav
}
if ((r = pa_source_output_finish_move(o, dest, save)) < 0) {
+ pa_source_output_fail_move(o);
pa_source_output_unref(o);
return r;
}
@@ -850,6 +903,7 @@ int pa_source_output_move_to(pa_source_output *o, pa_source *dest, pa_bool_t sav
/* Called from IO thread context */
void pa_source_output_set_state_within_thread(pa_source_output *o, pa_source_output_state_t state) {
pa_source_output_assert_ref(o);
+ pa_source_output_assert_io_context(o);
if (state == o->thread_info.state)
return;
@@ -906,11 +960,13 @@ int pa_source_output_process_msg(pa_msgobject *mo, int code, void *userdata, int
return -PA_ERR_NOTIMPLEMENTED;
}
+/* Called from main context */
void pa_source_output_send_event(pa_source_output *o, const char *event, pa_proplist *data) {
pa_proplist *pl = NULL;
pa_source_output_send_event_hook_data hook_data;
pa_source_output_assert_ref(o);
+ pa_assert_ctl_context();
pa_assert(event);
if (!o->send_event)
diff --git a/src/pulsecore/source-output.h b/src/pulsecore/source-output.h
index 9824e160..273b78fc 100644
--- a/src/pulsecore/source-output.h
+++ b/src/pulsecore/source-output.h
@@ -55,7 +55,8 @@ typedef enum pa_source_output_flags {
PA_SOURCE_OUTPUT_FIX_RATE = 64,
PA_SOURCE_OUTPUT_FIX_CHANNELS = 128,
PA_SOURCE_OUTPUT_DONT_INHIBIT_AUTO_SUSPEND = 256,
- PA_SOURCE_OUTPUT_FAIL_ON_SUSPEND = 512
+ PA_SOURCE_OUTPUT_NO_CREATE_ON_SUSPEND = 512,
+ PA_SOURCE_OUTPUT_KILL_ON_SUSPEND = 1024
} pa_source_output_flags_t;
struct pa_source_output {
@@ -108,6 +109,10 @@ struct pa_source_output {
* from IO context. */
void (*update_source_latency_range) (pa_source_output *o); /* may be NULL */
+ /* Called whenver the fixed latency of the source changes, if there
+ * is one. Called from IO context. */
+ void (*update_source_fixed_latency) (pa_source_output *i); /* may be NULL */
+
/* If non-NULL this function is called when the output is first
* connected to a source. Called from IO thread context */
void (*attach) (pa_source_output *o); /* may be NULL */
@@ -127,7 +132,9 @@ struct pa_source_output {
/* If non-NULL called whenever the source output is moved to a new
* source. Called from main context after the stream was detached
* from the old source and before it is attached to the new
- * source. */
+ * source. If dest is NULL the move was executed in two
+ * phases and the second one failed; the stream will be destroyed
+ * after this call. */
void (*moving) (pa_source_output *o, pa_source *dest); /* may be NULL */
/* Supposed to unlink and destroy this stream. Called from main
@@ -175,7 +182,7 @@ struct pa_source_output {
void *userdata;
};
-PA_DECLARE_CLASS(pa_source_output);
+PA_DECLARE_PUBLIC_CLASS(pa_source_output);
#define PA_SOURCE_OUTPUT(o) pa_source_output_cast(o)
enum {
@@ -194,6 +201,8 @@ typedef struct pa_source_output_send_event_hook_data {
} pa_source_output_send_event_hook_data;
typedef struct pa_source_output_new_data {
+ pa_source_output_flags_t flags;
+
pa_proplist *proplist;
pa_sink_input *direct_on_input;
@@ -224,8 +233,7 @@ void pa_source_output_new_data_done(pa_source_output_new_data *data);
int pa_source_output_new(
pa_source_output**o,
pa_core *core,
- pa_source_output_new_data *data,
- pa_source_output_flags_t flags);
+ pa_source_output_new_data *data);
void pa_source_output_put(pa_source_output *o);
void pa_source_output_unlink(pa_source_output*o);
@@ -238,6 +246,8 @@ void pa_source_output_cork(pa_source_output *o, pa_bool_t b);
int pa_source_output_set_rate(pa_source_output *o, uint32_t rate);
+size_t pa_source_output_get_max_rewind(pa_source_output *o);
+
/* Callable by everyone */
/* External code may request disconnection with this funcion */
@@ -260,6 +270,7 @@ int pa_source_output_move_to(pa_source_output *o, pa_source *dest, pa_bool_t sav
* new source */
int pa_source_output_start_move(pa_source_output *o);
int pa_source_output_finish_move(pa_source_output *o, pa_source *dest, pa_bool_t save);
+void pa_source_output_fail_move(pa_source_output *o);
#define pa_source_output_get_state(o) ((o)->state)
@@ -277,4 +288,7 @@ int pa_source_output_process_msg(pa_msgobject *mo, int code, void *userdata, int
pa_usec_t pa_source_output_set_requested_latency_within_thread(pa_source_output *o, pa_usec_t usec);
+#define pa_source_output_assert_io_context(s) \
+ pa_assert(pa_thread_mq_get() || !PA_SOURCE_OUTPUT_IS_LINKED((s)->state))
+
#endif
diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c
index 74f38bc5..415c54bc 100644
--- a/src/pulsecore/source.c
+++ b/src/pulsecore/source.c
@@ -33,6 +33,7 @@
#include <pulse/timeval.h>
#include <pulse/util.h>
+#include <pulsecore/core-util.h>
#include <pulsecore/source-output.h>
#include <pulsecore/namereg.h>
#include <pulsecore/core-subscribe.h>
@@ -45,14 +46,14 @@
#define ABSOLUTE_MAX_LATENCY (10*PA_USEC_PER_SEC)
#define DEFAULT_FIXED_LATENCY (250*PA_USEC_PER_MSEC)
-static PA_DEFINE_CHECK_TYPE(pa_source, pa_msgobject);
+PA_DEFINE_PUBLIC_CLASS(pa_source, pa_msgobject);
static void source_free(pa_object *o);
pa_source_new_data* pa_source_new_data_init(pa_source_new_data *data) {
pa_assert(data);
- memset(data, 0, sizeof(*data));
+ pa_zero(*data);
data->proplist = pa_proplist_new();
return data;
@@ -145,6 +146,7 @@ pa_source* pa_source_new(
pa_assert(core);
pa_assert(data);
pa_assert(data->name);
+ pa_assert_ctl_context();
s = pa_msgobject_new(pa_source);
@@ -179,7 +181,7 @@ pa_source* pa_source_new(
pa_cvolume_reset(&data->volume, data->sample_spec.channels);
pa_return_null_if_fail(pa_cvolume_valid(&data->volume));
- pa_return_null_if_fail(data->volume.channels == data->sample_spec.channels);
+ pa_return_null_if_fail(pa_cvolume_compatible(&data->volume, &data->sample_spec));
if (!data->muted_is_set)
data->muted = FALSE;
@@ -203,6 +205,7 @@ pa_source* pa_source_new(
s->core = core;
s->state = PA_SOURCE_INIT;
s->flags = flags;
+ s->priority = 0;
s->suspend_cause = 0;
s->name = pa_xstrdup(name);
s->proplist = pa_proplist_copy(data->proplist);
@@ -210,6 +213,8 @@ pa_source* pa_source_new(
s->module = data->module;
s->card = data->card;
+ s->priority = pa_device_init_priority(s->proplist);
+
s->sample_spec = data->sample_spec;
s->channel_map = data->channel_map;
@@ -217,20 +222,17 @@ pa_source* pa_source_new(
s->n_corked = 0;
s->monitor_of = NULL;
- s->virtual_volume = data->volume;
+ s->volume = data->volume;
pa_cvolume_reset(&s->soft_volume, s->sample_spec.channels);
s->base_volume = PA_VOLUME_NORM;
s->n_volume_steps = PA_VOLUME_NORM+1;
s->muted = data->muted;
s->refresh_volume = s->refresh_muted = FALSE;
- s->fixed_latency = flags & PA_SOURCE_DYNAMIC_LATENCY ? 0 : DEFAULT_FIXED_LATENCY;
-
reset_callbacks(s);
s->userdata = NULL;
s->asyncmsgq = NULL;
- s->rtpoll = NULL;
/* As a minor optimization we just steal the list instead of
* copying it here */
@@ -263,6 +265,7 @@ pa_source* pa_source_new(
&s->sample_spec,
0);
+ s->thread_info.rtpoll = NULL;
s->thread_info.outputs = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
s->thread_info.soft_volume = s->soft_volume;
s->thread_info.soft_muted = s->muted;
@@ -272,6 +275,7 @@ pa_source* pa_source_new(
s->thread_info.requested_latency = 0;
s->thread_info.min_latency = ABSOLUTE_MIN_LATENCY;
s->thread_info.max_latency = ABSOLUTE_MAX_LATENCY;
+ s->thread_info.fixed_latency = flags & PA_SOURCE_DYNAMIC_LATENCY ? 0 : DEFAULT_FIXED_LATENCY;
pa_assert_se(pa_idxset_put(core->sources, s, &s->index) >= 0);
@@ -297,6 +301,7 @@ static int source_set_state(pa_source *s, pa_source_state_t state) {
pa_source_state_t original_state;
pa_assert(s);
+ pa_assert_ctl_context();
if (s->state == state)
return 0;
@@ -333,27 +338,26 @@ static int source_set_state(pa_source *s, pa_source_state_t state) {
/* We're suspending or resuming, tell everyone about it */
- for (o = PA_SOURCE_OUTPUT(pa_idxset_first(s->outputs, &idx)); o; o = PA_SOURCE_OUTPUT(pa_idxset_next(s->outputs, &idx)))
+ PA_IDXSET_FOREACH(o, s->outputs, idx)
if (s->state == PA_SOURCE_SUSPENDED &&
- (o->flags & PA_SOURCE_OUTPUT_FAIL_ON_SUSPEND))
+ (o->flags & PA_SOURCE_OUTPUT_KILL_ON_SUSPEND))
pa_source_output_kill(o);
else if (o->suspend)
o->suspend(o, state == PA_SOURCE_SUSPENDED);
}
-
return 0;
}
/* Called from main context */
void pa_source_put(pa_source *s) {
pa_source_assert_ref(s);
+ pa_assert_ctl_context();
pa_assert(s->state == PA_SOURCE_INIT);
/* The following fields must be initialized properly when calling _put() */
pa_assert(s->asyncmsgq);
- pa_assert(s->rtpoll);
pa_assert(s->thread_info.min_latency <= s->thread_info.max_latency);
/* Generally, flags should be initialized via pa_source_new(). As
@@ -368,7 +372,7 @@ void pa_source_put(pa_source *s) {
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(!(s->flags & PA_SOURCE_DYNAMIC_LATENCY) == (s->thread_info.fixed_latency != 0));
pa_assert_se(source_set_state(s, PA_SOURCE_IDLE) == 0);
@@ -382,6 +386,7 @@ void pa_source_unlink(pa_source *s) {
pa_source_output *o, *j = NULL;
pa_assert(s);
+ pa_assert_ctl_context();
/* See pa_sink_unlink() for a couple of comments how this function
* works. */
@@ -423,6 +428,7 @@ static void source_free(pa_object *o) {
pa_source *s = PA_SOURCE(o);
pa_assert(s);
+ pa_assert_ctl_context();
pa_assert(pa_source_refcnt(s) == 0);
if (PA_SOURCE_IS_LINKED(s->state))
@@ -458,23 +464,40 @@ static void source_free(pa_object *o) {
pa_xfree(s);
}
-/* Called from main context */
+/* Called from main context, and not while the IO thread is active, please */
void pa_source_set_asyncmsgq(pa_source *s, pa_asyncmsgq *q) {
pa_source_assert_ref(s);
+ pa_assert_ctl_context();
s->asyncmsgq = q;
}
-/* Called from main context */
+/* Called from main context, and not while the IO thread is active, please */
+void pa_source_update_flags(pa_source *s, pa_source_flags_t mask, pa_source_flags_t value) {
+ pa_source_assert_ref(s);
+ pa_assert_ctl_context();
+
+ if (mask == 0)
+ return;
+
+ /* For now, allow only a minimal set of flags to be changed. */
+ pa_assert((mask & ~(PA_SOURCE_DYNAMIC_LATENCY|PA_SOURCE_LATENCY)) == 0);
+
+ s->flags = (s->flags & ~mask) | (value & mask);
+}
+
+/* Called from IO context, or before _put() from main context */
void pa_source_set_rtpoll(pa_source *s, pa_rtpoll *p) {
pa_source_assert_ref(s);
+ pa_source_assert_io_context(s);
- s->rtpoll = p;
+ s->thread_info.rtpoll = p;
}
/* Called from main context */
int pa_source_update_status(pa_source*s) {
pa_source_assert_ref(s);
+ pa_assert_ctl_context();
pa_assert(PA_SOURCE_IS_LINKED(s->state));
if (s->state == PA_SOURCE_SUSPENDED)
@@ -486,6 +509,7 @@ int pa_source_update_status(pa_source*s) {
/* Called from main context */
int pa_source_suspend(pa_source *s, pa_bool_t suspend, pa_suspend_cause_t cause) {
pa_source_assert_ref(s);
+ pa_assert_ctl_context();
pa_assert(PA_SOURCE_IS_LINKED(s->state));
pa_assert(cause != 0);
@@ -513,6 +537,7 @@ int pa_source_sync_suspend(pa_source *s) {
pa_sink_state_t state;
pa_source_assert_ref(s);
+ pa_assert_ctl_context();
pa_assert(PA_SOURCE_IS_LINKED(s->state));
pa_assert(s->monitor_of);
@@ -532,6 +557,7 @@ pa_queue *pa_source_move_all_start(pa_source *s, pa_queue *q) {
uint32_t idx;
pa_source_assert_ref(s);
+ pa_assert_ctl_context();
pa_assert(PA_SOURCE_IS_LINKED(s->state));
if (!q)
@@ -556,12 +582,13 @@ void pa_source_move_all_finish(pa_source *s, pa_queue *q, pa_bool_t save) {
pa_source_output *o;
pa_source_assert_ref(s);
+ pa_assert_ctl_context();
pa_assert(PA_SOURCE_IS_LINKED(s->state));
pa_assert(q);
while ((o = PA_SOURCE_OUTPUT(pa_queue_pop(q)))) {
if (pa_source_output_finish_move(o, s, save) < 0)
- pa_source_output_kill(o);
+ pa_source_output_fail_move(o);
pa_source_output_unref(o);
}
@@ -572,13 +599,13 @@ void pa_source_move_all_finish(pa_source *s, pa_queue *q, pa_bool_t save) {
/* Called from main context */
void pa_source_move_all_fail(pa_queue *q) {
pa_source_output *o;
+
+ pa_assert_ctl_context();
pa_assert(q);
while ((o = PA_SOURCE_OUTPUT(pa_queue_pop(q)))) {
- if (pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_FAIL], o) == PA_HOOK_OK) {
- pa_source_output_kill(o);
- pa_source_output_unref(o);
- }
+ pa_source_output_fail_move(o);
+ pa_source_output_unref(o);
}
pa_queue_free(q, NULL, NULL);
@@ -590,17 +617,18 @@ void pa_source_process_rewind(pa_source *s, size_t nbytes) {
void *state = NULL;
pa_source_assert_ref(s);
+ pa_source_assert_io_context(s);
pa_assert(PA_SOURCE_IS_LINKED(s->thread_info.state));
- if (s->thread_info.state == PA_SOURCE_SUSPENDED)
+ if (nbytes <= 0)
return;
- if (nbytes <= 0)
+ if (s->thread_info.state == PA_SOURCE_SUSPENDED)
return;
pa_log_debug("Processing rewind...");
- while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL))) {
+ PA_HASHMAP_FOREACH(o, s->thread_info.outputs, state) {
pa_source_output_assert_ref(o);
pa_source_output_process_rewind(o, nbytes);
}
@@ -612,6 +640,7 @@ void pa_source_post(pa_source*s, const pa_memchunk *chunk) {
void *state = NULL;
pa_source_assert_ref(s);
+ pa_source_assert_io_context(s);
pa_assert(PA_SOURCE_IS_LINKED(s->thread_info.state));
pa_assert(chunk);
@@ -651,6 +680,7 @@ void pa_source_post(pa_source*s, const pa_memchunk *chunk) {
/* Called from IO thread context */
void pa_source_post_direct(pa_source*s, pa_source_output *o, const pa_memchunk *chunk) {
pa_source_assert_ref(s);
+ pa_source_assert_io_context(s);
pa_assert(PA_SOURCE_IS_LINKED(s->thread_info.state));
pa_source_output_assert_ref(o);
pa_assert(o->thread_info.direct_on_input);
@@ -682,6 +712,7 @@ pa_usec_t pa_source_get_latency(pa_source *s) {
pa_usec_t usec;
pa_source_assert_ref(s);
+ pa_assert_ctl_context();
pa_assert(PA_SOURCE_IS_LINKED(s->state));
if (s->state == PA_SOURCE_SUSPENDED)
@@ -701,6 +732,7 @@ pa_usec_t pa_source_get_latency_within_thread(pa_source *s) {
pa_msgobject *o;
pa_source_assert_ref(s);
+ pa_source_assert_io_context(s);
pa_assert(PA_SOURCE_IS_LINKED(s->thread_info.state));
/* The returned value is supposed to be in the time domain of the sound card! */
@@ -722,77 +754,96 @@ pa_usec_t pa_source_get_latency_within_thread(pa_source *s) {
}
/* Called from main thread */
-void pa_source_set_volume(pa_source *s, const pa_cvolume *volume, pa_bool_t save) {
- pa_cvolume old_virtual_volume;
- pa_bool_t virtual_volume_changed;
+void pa_source_set_volume(
+ pa_source *s,
+ const pa_cvolume *volume,
+ pa_bool_t save) {
+
+ pa_bool_t real_changed;
+ pa_cvolume old_volume;
pa_source_assert_ref(s);
+ pa_assert_ctl_context();
pa_assert(PA_SOURCE_IS_LINKED(s->state));
- pa_assert(volume);
pa_assert(pa_cvolume_valid(volume));
- pa_assert(pa_cvolume_compatible(volume, &s->sample_spec));
+ pa_assert(volume->channels == 1 || pa_cvolume_compatible(volume, &s->sample_spec));
- old_virtual_volume = s->virtual_volume;
- s->virtual_volume = *volume;
- virtual_volume_changed = !pa_cvolume_equal(&old_virtual_volume, &s->virtual_volume);
- s->save_volume = (!virtual_volume_changed && s->save_volume) || save;
+ old_volume = s->volume;
+
+ if (pa_cvolume_compatible(volume, &s->sample_spec))
+ s->volume = *volume;
+ else
+ pa_cvolume_scale(&s->volume, pa_cvolume_max(volume));
+
+ real_changed = !pa_cvolume_equal(&old_volume, &s->volume);
+ s->save_volume = (!real_changed && s->save_volume) || save;
if (s->set_volume) {
pa_cvolume_reset(&s->soft_volume, s->sample_spec.channels);
s->set_volume(s);
} else
- s->soft_volume = s->virtual_volume;
+ s->soft_volume = s->volume;
pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_VOLUME, NULL, 0, NULL) == 0);
- if (virtual_volume_changed)
+ if (real_changed)
pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
}
/* Called from main thread. Only to be called by source implementor */
void pa_source_set_soft_volume(pa_source *s, const pa_cvolume *volume) {
pa_source_assert_ref(s);
- pa_assert(volume);
+ pa_assert_ctl_context();
+
+ if (!volume)
+ pa_cvolume_reset(&s->soft_volume, s->sample_spec.channels);
+ else
+ s->soft_volume = *volume;
if (PA_SOURCE_IS_LINKED(s->state))
pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_VOLUME, NULL, 0, NULL) == 0);
else
- s->thread_info.soft_volume = *volume;
+ s->thread_info.soft_volume = s->soft_volume;
}
/* Called from main thread */
const pa_cvolume *pa_source_get_volume(pa_source *s, pa_bool_t force_refresh) {
pa_source_assert_ref(s);
+ pa_assert_ctl_context();
pa_assert(PA_SOURCE_IS_LINKED(s->state));
if (s->refresh_volume || force_refresh) {
- pa_cvolume old_virtual_volume = s->virtual_volume;
+ pa_cvolume old_volume;
+
+ old_volume = s->volume;
if (s->get_volume)
s->get_volume(s);
pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_VOLUME, NULL, 0, NULL) == 0);
- if (!pa_cvolume_equal(&old_virtual_volume, &s->virtual_volume))
+ if (!pa_cvolume_equal(&old_volume, &s->volume)) {
+ s->save_volume = TRUE;
pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+ }
}
- return &s->virtual_volume;
+ return &s->volume;
}
/* Called from main thread */
-void pa_source_volume_changed(pa_source *s, const pa_cvolume *new_volume, pa_bool_t save) {
+void pa_source_volume_changed(pa_source *s, const pa_cvolume *new_volume) {
pa_source_assert_ref(s);
+ pa_assert_ctl_context();
+ pa_assert(PA_SOURCE_IS_LINKED(s->state));
/* The source implementor may call this if the volume changed to make sure everyone is notified */
- if (pa_cvolume_equal(&s->virtual_volume, new_volume)) {
- s->save_volume = s->save_volume || save;
+ if (pa_cvolume_equal(&s->volume, new_volume))
return;
- }
- s->virtual_volume = *new_volume;
- s->save_volume = save;
+ s->volume = *new_volume;
+ s->save_volume = TRUE;
pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
}
@@ -802,6 +853,7 @@ void pa_source_set_mute(pa_source *s, pa_bool_t mute, pa_bool_t save) {
pa_bool_t old_muted;
pa_source_assert_ref(s);
+ pa_assert_ctl_context();
pa_assert(PA_SOURCE_IS_LINKED(s->state));
old_muted = s->muted;
@@ -820,6 +872,7 @@ void pa_source_set_mute(pa_source *s, pa_bool_t mute, pa_bool_t save) {
/* Called from main thread */
pa_bool_t pa_source_get_mute(pa_source *s, pa_bool_t force_refresh) {
pa_source_assert_ref(s);
+ pa_assert_ctl_context();
pa_assert(PA_SOURCE_IS_LINKED(s->state));
if (s->refresh_muted || force_refresh) {
@@ -831,6 +884,8 @@ 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) {
+ s->save_muted = TRUE;
+
pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
/* Make sure the soft mute status stays in sync */
@@ -842,18 +897,18 @@ pa_bool_t pa_source_get_mute(pa_source *s, pa_bool_t force_refresh) {
}
/* Called from main thread */
-void pa_source_mute_changed(pa_source *s, pa_bool_t new_muted, pa_bool_t save) {
+void pa_source_mute_changed(pa_source *s, pa_bool_t new_muted) {
pa_source_assert_ref(s);
+ pa_assert_ctl_context();
+ pa_assert(PA_SOURCE_IS_LINKED(s->state));
/* The source implementor may call this if the mute state changed to make sure everyone is notified */
- if (s->muted == new_muted) {
- s->save_muted = s->save_muted || save;
+ if (s->muted == new_muted)
return;
- }
s->muted = new_muted;
- s->save_muted = save;
+ s->save_muted = TRUE;
pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
}
@@ -861,6 +916,7 @@ void pa_source_mute_changed(pa_source *s, pa_bool_t new_muted, pa_bool_t save) {
/* Called from main thread */
pa_bool_t pa_source_update_proplist(pa_source *s, pa_update_mode_t mode, pa_proplist *p) {
pa_source_assert_ref(s);
+ pa_assert_ctl_context();
if (p)
pa_proplist_update(s->proplist, mode, p);
@@ -874,16 +930,18 @@ pa_bool_t pa_source_update_proplist(pa_source *s, pa_update_mode_t mode, pa_prop
}
/* Called from main thread */
+/* FIXME -- this should be dropped and be merged into pa_source_update_proplist() */
void pa_source_set_description(pa_source *s, const char *description) {
const char *old;
pa_source_assert_ref(s);
+ pa_assert_ctl_context();
if (!description && !pa_proplist_contains(s->proplist, PA_PROP_DEVICE_DESCRIPTION))
return;
old = pa_proplist_gets(s->proplist, PA_PROP_DEVICE_DESCRIPTION);
- if (old && description && !strcmp(old, description))
+ if (old && description && pa_streq(old, description))
return;
if (description)
@@ -901,6 +959,7 @@ void pa_source_set_description(pa_source *s, const char *description) {
unsigned pa_source_linked_by(pa_source *s) {
pa_source_assert_ref(s);
pa_assert(PA_SOURCE_IS_LINKED(s->state));
+ pa_assert_ctl_context();
return pa_idxset_size(s->outputs);
}
@@ -911,6 +970,7 @@ unsigned pa_source_used_by(pa_source *s) {
pa_source_assert_ref(s);
pa_assert(PA_SOURCE_IS_LINKED(s->state));
+ pa_assert_ctl_context();
ret = pa_idxset_size(s->outputs);
pa_assert(ret >= s->n_corked);
@@ -925,6 +985,7 @@ unsigned pa_source_check_suspend(pa_source *s) {
uint32_t idx;
pa_source_assert_ref(s);
+ pa_assert_ctl_context();
if (!PA_SOURCE_IS_LINKED(s->state))
return 0;
@@ -1006,7 +1067,7 @@ int pa_source_process_msg(pa_msgobject *object, int code, void *userdata, int64_
if (pa_hashmap_remove(s->thread_info.outputs, PA_UINT32_TO_PTR(o->index)))
pa_source_output_unref(o);
- pa_source_invalidate_requested_latency(s);
+ pa_source_invalidate_requested_latency(s, TRUE);
return 0;
}
@@ -1086,6 +1147,16 @@ int pa_source_process_msg(pa_msgobject *object, int code, void *userdata, int64_
return 0;
}
+ case PA_SOURCE_MESSAGE_GET_FIXED_LATENCY:
+
+ *((pa_usec_t*) userdata) = s->thread_info.fixed_latency;
+ return 0;
+
+ case PA_SOURCE_MESSAGE_SET_FIXED_LATENCY:
+
+ pa_source_set_fixed_latency_within_thread(s, (pa_usec_t) offset);
+ return 0;
+
case PA_SOURCE_MESSAGE_GET_MAX_REWIND:
*((size_t*) userdata) = s->thread_info.max_rewind;
@@ -1120,6 +1191,7 @@ int pa_source_suspend_all(pa_core *c, pa_bool_t suspend, pa_suspend_cause_t caus
int ret = 0;
pa_core_assert_ref(c);
+ pa_assert_ctl_context();
pa_assert(cause != 0);
for (source = PA_SOURCE(pa_idxset_first(c->sources, &idx)); source; source = PA_SOURCE(pa_idxset_next(c->sources, &idx))) {
@@ -1138,6 +1210,7 @@ int pa_source_suspend_all(pa_core *c, pa_bool_t suspend, pa_suspend_cause_t caus
/* Called from main thread */
void pa_source_detach(pa_source *s) {
pa_source_assert_ref(s);
+ pa_assert_ctl_context();
pa_assert(PA_SOURCE_IS_LINKED(s->state));
pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_DETACH, NULL, 0, NULL) == 0);
@@ -1146,6 +1219,7 @@ void pa_source_detach(pa_source *s) {
/* Called from main thread */
void pa_source_attach(pa_source *s) {
pa_source_assert_ref(s);
+ pa_assert_ctl_context();
pa_assert(PA_SOURCE_IS_LINKED(s->state));
pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_ATTACH, NULL, 0, NULL) == 0);
@@ -1157,9 +1231,10 @@ void pa_source_detach_within_thread(pa_source *s) {
void *state = NULL;
pa_source_assert_ref(s);
+ pa_source_assert_io_context(s);
pa_assert(PA_SOURCE_IS_LINKED(s->thread_info.state));
- while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL)))
+ PA_HASHMAP_FOREACH(o, s->thread_info.outputs, state)
if (o->detach)
o->detach(o);
}
@@ -1170,9 +1245,10 @@ void pa_source_attach_within_thread(pa_source *s) {
void *state = NULL;
pa_source_assert_ref(s);
+ pa_source_assert_io_context(s);
pa_assert(PA_SOURCE_IS_LINKED(s->thread_info.state));
- while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL)))
+ PA_HASHMAP_FOREACH(o, s->thread_info.outputs, state)
if (o->attach)
o->attach(o);
}
@@ -1184,15 +1260,15 @@ pa_usec_t pa_source_get_requested_latency_within_thread(pa_source *s) {
void *state = NULL;
pa_source_assert_ref(s);
+ pa_source_assert_io_context(s);
if (!(s->flags & PA_SOURCE_DYNAMIC_LATENCY))
- return PA_CLAMP(s->fixed_latency, s->thread_info.min_latency, s->thread_info.max_latency);
+ return PA_CLAMP(s->thread_info.fixed_latency, s->thread_info.min_latency, s->thread_info.max_latency);
if (s->thread_info.requested_latency_valid)
return s->thread_info.requested_latency;
- while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL)))
-
+ PA_HASHMAP_FOREACH(o, s->thread_info.outputs, state)
if (o->thread_info.requested_source_latency != (pa_usec_t) -1 &&
(result == (pa_usec_t) -1 || result > o->thread_info.requested_source_latency))
result = o->thread_info.requested_source_latency;
@@ -1214,6 +1290,7 @@ pa_usec_t pa_source_get_requested_latency(pa_source *s) {
pa_usec_t usec = 0;
pa_source_assert_ref(s);
+ pa_assert_ctl_context();
pa_assert(PA_SOURCE_IS_LINKED(s->state));
if (s->state == PA_SOURCE_SUSPENDED)
@@ -1230,21 +1307,22 @@ void pa_source_set_max_rewind_within_thread(pa_source *s, size_t max_rewind) {
void *state = NULL;
pa_source_assert_ref(s);
+ pa_source_assert_io_context(s);
if (max_rewind == s->thread_info.max_rewind)
return;
s->thread_info.max_rewind = max_rewind;
- if (PA_SOURCE_IS_LINKED(s->thread_info.state)) {
- while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL)))
+ if (PA_SOURCE_IS_LINKED(s->thread_info.state))
+ PA_HASHMAP_FOREACH(o, s->thread_info.outputs, state)
pa_source_output_update_max_rewind(o, s->thread_info.max_rewind);
- }
}
/* Called from main thread */
void pa_source_set_max_rewind(pa_source *s, size_t max_rewind) {
pa_source_assert_ref(s);
+ pa_assert_ctl_context();
if (PA_SOURCE_IS_LINKED(s->state))
pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_MAX_REWIND, NULL, max_rewind, NULL) == 0);
@@ -1253,17 +1331,18 @@ void pa_source_set_max_rewind(pa_source *s, size_t max_rewind) {
}
/* Called from IO thread */
-void pa_source_invalidate_requested_latency(pa_source *s) {
+void pa_source_invalidate_requested_latency(pa_source *s, pa_bool_t dynamic) {
pa_source_output *o;
void *state = NULL;
pa_source_assert_ref(s);
+ pa_source_assert_io_context(s);
- if (!(s->flags & PA_SOURCE_DYNAMIC_LATENCY))
+ if ((s->flags & PA_SOURCE_DYNAMIC_LATENCY))
+ s->thread_info.requested_latency_valid = FALSE;
+ else if (dynamic)
return;
- s->thread_info.requested_latency_valid = FALSE;
-
if (PA_SOURCE_IS_LINKED(s->thread_info.state)) {
if (s->update_requested_latency)
@@ -1275,12 +1354,13 @@ void pa_source_invalidate_requested_latency(pa_source *s) {
}
if (s->monitor_of)
- pa_sink_invalidate_requested_latency(s->monitor_of);
+ pa_sink_invalidate_requested_latency(s->monitor_of, dynamic);
}
/* Called from main thread */
void pa_source_set_latency_range(pa_source *s, pa_usec_t min_latency, pa_usec_t max_latency) {
pa_source_assert_ref(s);
+ pa_assert_ctl_context();
/* min_latency == 0: no limit
* min_latency anything else: specified limit
@@ -1315,6 +1395,7 @@ void pa_source_set_latency_range(pa_source *s, pa_usec_t min_latency, pa_usec_t
/* Called from main thread */
void pa_source_get_latency_range(pa_source *s, pa_usec_t *min_latency, pa_usec_t *max_latency) {
pa_source_assert_ref(s);
+ pa_assert_ctl_context();
pa_assert(min_latency);
pa_assert(max_latency);
@@ -1333,9 +1414,8 @@ void pa_source_get_latency_range(pa_source *s, pa_usec_t *min_latency, pa_usec_t
/* Called from IO thread, and from main thread before pa_source_put() is called */
void pa_source_set_latency_range_within_thread(pa_source *s, pa_usec_t min_latency, pa_usec_t max_latency) {
- void *state = NULL;
-
pa_source_assert_ref(s);
+ pa_source_assert_io_context(s);
pa_assert(min_latency >= ABSOLUTE_MIN_LATENCY);
pa_assert(max_latency <= ABSOLUTE_MAX_LATENCY);
@@ -1347,25 +1427,34 @@ void pa_source_set_latency_range_within_thread(pa_source *s, pa_usec_t min_laten
(s->flags & PA_SOURCE_DYNAMIC_LATENCY) ||
s->monitor_of);
+ if (s->thread_info.min_latency == min_latency &&
+ s->thread_info.max_latency == max_latency)
+ return;
+
s->thread_info.min_latency = min_latency;
s->thread_info.max_latency = max_latency;
if (PA_SOURCE_IS_LINKED(s->thread_info.state)) {
pa_source_output *o;
+ void *state = NULL;
- while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL)))
+ PA_HASHMAP_FOREACH(o, s->thread_info.outputs, state)
if (o->update_source_latency_range)
o->update_source_latency_range(o);
}
- pa_source_invalidate_requested_latency(s);
+ pa_source_invalidate_requested_latency(s, FALSE);
}
/* 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_ctl_context();
- pa_assert(pa_source_get_state(s) == PA_SOURCE_INIT);
+ if (s->flags & PA_SOURCE_DYNAMIC_LATENCY) {
+ pa_assert(latency == 0);
+ return;
+ }
if (latency < ABSOLUTE_MIN_LATENCY)
latency = ABSOLUTE_MIN_LATENCY;
@@ -1373,12 +1462,64 @@ void pa_source_set_fixed_latency(pa_source *s, pa_usec_t latency) {
if (latency > ABSOLUTE_MAX_LATENCY)
latency = ABSOLUTE_MAX_LATENCY;
- s->fixed_latency = latency;
+ if (PA_SOURCE_IS_LINKED(s->state))
+ pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_FIXED_LATENCY, NULL, (int64_t) latency, NULL) == 0);
+ else
+ s->thread_info.fixed_latency = latency;
+}
+
+/* Called from main thread */
+pa_usec_t pa_source_get_fixed_latency(pa_source *s) {
+ pa_usec_t latency;
+
+ pa_source_assert_ref(s);
+ pa_assert_ctl_context();
+
+ if (s->flags & PA_SOURCE_DYNAMIC_LATENCY)
+ return 0;
+
+ if (PA_SOURCE_IS_LINKED(s->state))
+ pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_FIXED_LATENCY, &latency, 0, NULL) == 0);
+ else
+ latency = s->thread_info.fixed_latency;
+
+ return latency;
+}
+
+/* Called from IO thread */
+void pa_source_set_fixed_latency_within_thread(pa_source *s, pa_usec_t latency) {
+ pa_source_assert_ref(s);
+ pa_source_assert_io_context(s);
+
+ if (s->flags & PA_SOURCE_DYNAMIC_LATENCY) {
+ pa_assert(latency == 0);
+ return;
+ }
+
+ pa_assert(latency >= ABSOLUTE_MIN_LATENCY);
+ pa_assert(latency <= ABSOLUTE_MAX_LATENCY);
+
+ if (s->thread_info.fixed_latency == latency)
+ return;
+
+ s->thread_info.fixed_latency = latency;
+
+ if (PA_SOURCE_IS_LINKED(s->thread_info.state)) {
+ pa_source_output *o;
+ void *state = NULL;
+
+ PA_HASHMAP_FOREACH(o, s->thread_info.outputs, state)
+ if (o->update_source_fixed_latency)
+ o->update_source_fixed_latency(o);
+ }
+
+ pa_source_invalidate_requested_latency(s, FALSE);
}
/* Called from main thread */
size_t pa_source_get_max_rewind(pa_source *s) {
size_t r;
+ pa_assert_ctl_context();
pa_source_assert_ref(s);
if (!PA_SOURCE_IS_LINKED(s->state))
@@ -1394,9 +1535,10 @@ int pa_source_set_port(pa_source *s, const char *name, pa_bool_t save) {
pa_device_port *port;
pa_assert(s);
+ pa_assert_ctl_context();
if (!s->set_port) {
- pa_log_debug("set_port() operation not implemented for sink %u \"%s\"", s->index, s->name);
+ pa_log_debug("set_port() operation not implemented for source %u \"%s\"", s->index, s->name);
return -PA_ERR_NOTIMPLEMENTED;
}
diff --git a/src/pulsecore/source.h b/src/pulsecore/source.h
index 7e9fd8b7..e3e56bc4 100644
--- a/src/pulsecore/source.h
+++ b/src/pulsecore/source.h
@@ -43,6 +43,7 @@ typedef struct pa_source pa_source;
#include <pulsecore/source-output.h>
#include <pulsecore/card.h>
#include <pulsecore/queue.h>
+#include <pulsecore/thread-mq.h>
#define PA_MAX_OUTPUTS_PER_SOURCE 32
@@ -78,7 +79,7 @@ struct pa_source {
pa_volume_t base_volume; /* shall be constant */
unsigned n_volume_steps; /* shall be constant */
- pa_cvolume virtual_volume, soft_volume;
+ pa_cvolume volume, soft_volume;
pa_bool_t muted:1;
pa_bool_t refresh_volume:1;
@@ -89,15 +90,14 @@ struct pa_source {
pa_bool_t save_muted:1;
pa_asyncmsgq *asyncmsgq;
- pa_rtpoll *rtpoll;
pa_memchunk silence;
- pa_usec_t fixed_latency; /* for sources with PA_SOURCE_DYNAMIC_LATENCY this is 0 */
-
pa_hashmap *ports;
pa_device_port *active_port;
+ unsigned priority;
+
/* Called when the main loop requests a state change. Called from
* main loop context. If returns -1 the state change will be
* inhibited */
@@ -139,6 +139,8 @@ struct pa_source {
pa_source_state_t state;
pa_hashmap *outputs;
+ pa_rtpoll *rtpoll;
+
pa_cvolume soft_volume;
pa_bool_t soft_muted:1;
@@ -151,12 +153,14 @@ struct pa_source {
pa_usec_t min_latency; /* we won't go below this latency */
pa_usec_t max_latency; /* An upper limit for the latencies */
- } thread_info;
+
+ pa_usec_t fixed_latency; /* for sources with PA_SOURCE_DYNAMIC_LATENCY this is 0 */
+ } thread_info;
void *userdata;
};
-PA_DECLARE_CLASS(pa_source);
+PA_DECLARE_PUBLIC_CLASS(pa_source);
#define PA_SOURCE(s) pa_source_cast(s)
typedef enum pa_source_message {
@@ -173,6 +177,8 @@ typedef enum pa_source_message {
PA_SOURCE_MESSAGE_DETACH,
PA_SOURCE_MESSAGE_SET_LATENCY_RANGE,
PA_SOURCE_MESSAGE_GET_LATENCY_RANGE,
+ PA_SOURCE_MESSAGE_SET_FIXED_LATENCY,
+ PA_SOURCE_MESSAGE_GET_FIXED_LATENCY,
PA_SOURCE_MESSAGE_GET_MAX_REWIND,
PA_SOURCE_MESSAGE_SET_MAX_REWIND,
PA_SOURCE_MESSAGE_MAX
@@ -237,17 +243,20 @@ void pa_source_detach(pa_source *s);
void pa_source_attach(pa_source *s);
void pa_source_set_soft_volume(pa_source *s, const pa_cvolume *volume);
-void pa_source_volume_changed(pa_source *s, const pa_cvolume *new_volume, pa_bool_t save);
-void pa_source_mute_changed(pa_source *s, pa_bool_t new_muted, pa_bool_t save);
+void pa_source_volume_changed(pa_source *s, const pa_cvolume *new_volume);
+void pa_source_mute_changed(pa_source *s, pa_bool_t new_muted);
int pa_source_sync_suspend(pa_source *s);
+void pa_source_update_flags(pa_source *s, pa_source_flags_t mask, pa_source_flags_t value);
+
/*** May be called by everyone, from main context */
/* The returned value is supposed to be in the time domain of the sound card! */
pa_usec_t pa_source_get_latency(pa_source *s);
pa_usec_t pa_source_get_requested_latency(pa_source *s);
void pa_source_get_latency_range(pa_source *s, pa_usec_t *min_latency, pa_usec_t *max_latency);
+pa_usec_t pa_source_get_fixed_latency(pa_source *s);
size_t pa_source_get_max_rewind(pa_source *s);
@@ -257,6 +266,7 @@ int pa_source_suspend_all(pa_core *c, pa_bool_t suspend, pa_suspend_cause_t caus
void pa_source_set_volume(pa_source *source, const pa_cvolume *volume, pa_bool_t save);
const pa_cvolume *pa_source_get_volume(pa_source *source, pa_bool_t force_refresh);
+
void pa_source_set_mute(pa_source *source, pa_bool_t mute, pa_bool_t save);
pa_bool_t pa_source_get_mute(pa_source *source, pa_bool_t force_refresh);
@@ -288,11 +298,16 @@ void pa_source_detach_within_thread(pa_source *s);
pa_usec_t pa_source_get_requested_latency_within_thread(pa_source *s);
void pa_source_set_max_rewind_within_thread(pa_source *s, size_t max_rewind);
+
void pa_source_set_latency_range_within_thread(pa_source *s, pa_usec_t min_latency, pa_usec_t max_latency);
+void pa_source_set_fixed_latency_within_thread(pa_source *s, pa_usec_t latency);
/*** To be called exclusively by source output drivers, from IO context */
-void pa_source_invalidate_requested_latency(pa_source *s);
+void pa_source_invalidate_requested_latency(pa_source *s, pa_bool_t dynamic);
pa_usec_t pa_source_get_latency_within_thread(pa_source *s);
+#define pa_source_assert_io_context(s) \
+ pa_assert(pa_thread_mq_get() || !PA_SOURCE_IS_LINKED((s)->state))
+
#endif
diff --git a/src/pulsecore/start-child.c b/src/pulsecore/start-child.c
index 7774bde6..4a70aea1 100644
--- a/src/pulsecore/start-child.c
+++ b/src/pulsecore/start-child.c
@@ -68,23 +68,29 @@ int pa_start_child_for_read(const char *name, const char *argv1, pid_t *pid) {
} else {
/* child */
- pa_reset_priority();
+ pa_reset_personality();
pa_assert_se(pa_close(pipe_fds[0]) == 0);
- pa_assert_se(dup2(pipe_fds[1], 1) == 1);
+ pa_assert_se(dup2(pipe_fds[1], STDOUT_FILENO) == STDOUT_FILENO);
- if (pipe_fds[1] != 1)
+ if (pipe_fds[1] != STDOUT_FILENO)
pa_assert_se(pa_close(pipe_fds[1]) == 0);
- pa_close(0);
- pa_assert_se(open("/dev/null", O_RDONLY) == 0);
+ pa_close(STDIN_FILENO);
+ pa_assert_se(open("/dev/null", O_RDONLY) == STDIN_FILENO);
- pa_close(2);
- pa_assert_se(open("/dev/null", O_WRONLY) == 2);
+ pa_close(STDERR_FILENO);
+ pa_assert_se(open("/dev/null", O_WRONLY) == STDERR_FILENO);
pa_close_all(-1);
pa_reset_sigs(-1);
pa_unblock_sigs(-1);
+ pa_reset_priority();
+ pa_unset_env_recorded();
+
+ /* Make sure our children are not influenced by the
+ * LD_BIND_NOW we set for ourselves. */
+ unsetenv("LD_BIND_NOW");
#ifdef PR_SET_PDEATHSIG
/* On Linux we can use PR_SET_PDEATHSIG to have the helper
diff --git a/src/pulsecore/svolume_arm.c b/src/pulsecore/svolume_arm.c
new file mode 100644
index 00000000..5bd1448f
--- /dev/null
+++ b/src/pulsecore/svolume_arm.c
@@ -0,0 +1,195 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2004-2006 Lennart Poettering
+ Copyright 2009 Wim Taymans <wim.taymans@collabora.co.uk>
+
+ 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/timeval.h>
+#include <pulsecore/random.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/g711.h>
+#include <pulsecore/core-util.h>
+
+#include "cpu-arm.h"
+
+#include "sample-util.h"
+#include "endianmacros.h"
+
+#if defined (__arm__)
+
+#define MOD_INC() \
+ " subs r0, r6, %2 \n\t" \
+ " addcs r0, %1 \n\t" \
+ " movcs r6, r0 \n\t"
+
+static void
+pa_volume_s16ne_arm (int16_t *samples, int32_t *volumes, unsigned channels, unsigned length)
+{
+ int32_t *ve;
+
+ channels = PA_MAX (4U, channels);
+ ve = volumes + channels;
+
+ __asm__ __volatile__ (
+ " mov r6, %1 \n\t"
+ " mov %3, %3, LSR #1 \n\t" /* length /= sizeof (int16_t) */
+ " tst %3, #1 \n\t" /* check for odd samples */
+ " beq 2f \n\t"
+
+ "1: \n\t"
+ " ldr r0, [r6], #4 \n\t" /* odd samples volumes */
+ " ldrh r2, [%0] \n\t"
+
+ " smulwb r0, r0, r2 \n\t"
+ " ssat r0, #16, r0 \n\t"
+
+ " strh r0, [%0], #2 \n\t"
+
+ MOD_INC()
+
+ "2: \n\t"
+ " mov %3, %3, LSR #1 \n\t"
+ " tst %3, #1 \n\t" /* check for odd samples */
+ " beq 4f \n\t"
+
+ "3: \n\t"
+ " ldrd r2, [r6], #8 \n\t" /* 2 samples at a time */
+ " ldr r0, [%0] \n\t"
+
+ " smulwt r2, r2, r0 \n\t"
+ " smulwb r3, r3, r0 \n\t"
+
+ " ssat r2, #16, r2 \n\t"
+ " ssat r3, #16, r3 \n\t"
+
+ " pkhbt r0, r3, r2, LSL #16 \n\t"
+ " str r0, [%0], #4 \n\t"
+
+ MOD_INC()
+
+ "4: \n\t"
+ " movs %3, %3, LSR #1 \n\t"
+ " beq 6f \n\t"
+
+ "5: \n\t"
+ " ldrd r2, [r6], #8 \n\t" /* 4 samples at a time */
+ " ldrd r4, [r6], #8 \n\t"
+ " ldrd r0, [%0] \n\t"
+
+ " smulwt r2, r2, r0 \n\t"
+ " smulwb r3, r3, r0 \n\t"
+ " smulwt r4, r4, r1 \n\t"
+ " smulwb r5, r5, r1 \n\t"
+
+ " ssat r2, #16, r2 \n\t"
+ " ssat r3, #16, r3 \n\t"
+ " ssat r4, #16, r4 \n\t"
+ " ssat r5, #16, r5 \n\t"
+
+ " pkhbt r0, r3, r2, LSL #16 \n\t"
+ " pkhbt r1, r5, r4, LSL #16 \n\t"
+ " strd r0, [%0], #8 \n\t"
+
+ MOD_INC()
+
+ " subs %3, %3, #1 \n\t"
+ " bne 5b \n\t"
+ "6: \n\t"
+
+ : "+r" (samples), "+r" (volumes), "+r" (ve), "+r" (length)
+ :
+ : "r6", "r5", "r4", "r3", "r2", "r1", "r0", "cc"
+ );
+}
+
+#undef RUN_TEST
+
+#ifdef RUN_TEST
+#define CHANNELS 2
+#define SAMPLES 1023
+#define TIMES 1000
+#define PADDING 16
+
+static void run_test (void) {
+ int16_t samples[SAMPLES];
+ int16_t samples_ref[SAMPLES];
+ int16_t samples_orig[SAMPLES];
+ int32_t volumes[CHANNELS + PADDING];
+ int i, j, padding;
+ pa_do_volume_func_t func;
+ pa_usec_t start, stop;
+
+ func = pa_get_volume_func (PA_SAMPLE_S16NE);
+
+ printf ("checking ARM %zd\n", sizeof (samples));
+
+ pa_random (samples, sizeof (samples));
+ memcpy (samples_ref, samples, sizeof (samples));
+ memcpy (samples_orig, samples, sizeof (samples));
+
+ for (i = 0; i < CHANNELS; i++)
+ volumes[i] = rand() >> 1;
+ for (padding = 0; padding < PADDING; padding++, i++)
+ volumes[i] = volumes[padding];
+
+ func (samples_ref, volumes, CHANNELS, sizeof (samples));
+ pa_volume_s16ne_arm (samples, volumes, CHANNELS, sizeof (samples));
+ for (i = 0; i < SAMPLES; i++) {
+ if (samples[i] != samples_ref[i]) {
+ printf ("%d: %04x != %04x (%04x * %04x)\n", i, samples[i], samples_ref[i],
+ samples_orig[i], volumes[i % CHANNELS]);
+ }
+ }
+
+ start = pa_rtclock_now();
+ for (j = 0; j < TIMES; j++) {
+ memcpy (samples, samples_orig, sizeof (samples));
+ pa_volume_s16ne_arm (samples, volumes, CHANNELS, sizeof (samples));
+ }
+ stop = pa_rtclock_now();
+ pa_log_info("ARM: %llu usec.", (long long unsigned int) (stop - start));
+
+ start = pa_rtclock_now();
+ for (j = 0; j < TIMES; j++) {
+ memcpy (samples_ref, samples_orig, sizeof (samples));
+ func (samples_ref, volumes, CHANNELS, sizeof (samples));
+ }
+ stop = pa_rtclock_now();
+ pa_log_info("ref: %llu usec.", (long long unsigned int) (stop - start));
+}
+#endif
+
+#endif /* defined (__arm__) */
+
+
+void pa_volume_func_init_arm (pa_cpu_arm_flag_t flags) {
+#if defined (__arm__)
+ pa_log_info("Initialising ARM optimized functions.");
+
+#ifdef RUN_TEST
+ run_test ();
+#endif
+
+ pa_set_volume_func (PA_SAMPLE_S16NE, (pa_do_volume_func_t) pa_volume_s16ne_arm);
+#endif /* defined (__arm__) */
+}
diff --git a/src/pulsecore/svolume_c.c b/src/pulsecore/svolume_c.c
new file mode 100644
index 00000000..5fc052b8
--- /dev/null
+++ b/src/pulsecore/svolume_c.c
@@ -0,0 +1,335 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2004-2006 Lennart Poettering
+ Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+ 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 <pulsecore/g711.h>
+#include <pulsecore/core-util.h>
+
+#include "sample-util.h"
+#include "endianmacros.h"
+
+static void
+pa_volume_u8_c (uint8_t *samples, int32_t *volumes, unsigned channels, unsigned length)
+{
+ unsigned channel;
+
+ for (channel = 0; length; length--) {
+ int32_t t, hi, lo;
+
+ hi = volumes[channel] >> 16;
+ lo = volumes[channel] & 0xFFFF;
+
+ t = (int32_t) *samples - 0x80;
+ t = ((t * lo) >> 16) + (t * hi);
+ t = PA_CLAMP_UNLIKELY(t, -0x80, 0x7F);
+ *samples++ = (uint8_t) (t + 0x80);
+
+ if (PA_UNLIKELY(++channel >= channels))
+ channel = 0;
+ }
+}
+
+static void
+pa_volume_alaw_c (uint8_t *samples, int32_t *volumes, unsigned channels, unsigned length)
+{
+ unsigned channel;
+
+ for (channel = 0; length; length--) {
+ int32_t t, hi, lo;
+
+ hi = volumes[channel] >> 16;
+ lo = volumes[channel] & 0xFFFF;
+
+ t = (int32_t) st_alaw2linear16(*samples);
+ t = ((t * lo) >> 16) + (t * hi);
+ t = PA_CLAMP_UNLIKELY(t, -0x8000, 0x7FFF);
+ *samples++ = (uint8_t) st_13linear2alaw((int16_t) t >> 3);
+
+ if (PA_UNLIKELY(++channel >= channels))
+ channel = 0;
+ }
+}
+
+static void
+pa_volume_ulaw_c (uint8_t *samples, int32_t *volumes, unsigned channels, unsigned length)
+{
+ unsigned channel;
+
+ for (channel = 0; length; length--) {
+ int32_t t, hi, lo;
+
+ hi = volumes[channel] >> 16;
+ lo = volumes[channel] & 0xFFFF;
+
+ t = (int32_t) st_ulaw2linear16(*samples);
+ t = ((t * lo) >> 16) + (t * hi);
+ t = PA_CLAMP_UNLIKELY(t, -0x8000, 0x7FFF);
+ *samples++ = (uint8_t) st_14linear2ulaw((int16_t) t >> 2);
+
+ if (PA_UNLIKELY(++channel >= channels))
+ channel = 0;
+ }
+}
+
+static void
+pa_volume_s16ne_c (int16_t *samples, int32_t *volumes, unsigned channels, unsigned length)
+{
+ unsigned channel;
+
+ length /= sizeof (int16_t);
+
+ for (channel = 0; length; length--) {
+ int32_t t, hi, lo;
+
+ /* Multiplying the 32bit volume factor with the 16bit
+ * sample might result in an 48bit value. We want to
+ * do without 64 bit integers and hence do the
+ * multiplication independantly for the HI and LO part
+ * of the volume. */
+
+ hi = volumes[channel] >> 16;
+ lo = volumes[channel] & 0xFFFF;
+
+ t = (int32_t)(*samples);
+ t = ((t * lo) >> 16) + (t * hi);
+ t = PA_CLAMP_UNLIKELY(t, -0x8000, 0x7FFF);
+ *samples++ = (int16_t) t;
+
+ if (PA_UNLIKELY(++channel >= channels))
+ channel = 0;
+ }
+}
+
+static void
+pa_volume_s16re_c (int16_t *samples, int32_t *volumes, unsigned channels, unsigned length)
+{
+ unsigned channel;
+
+ length /= sizeof (int16_t);
+
+ for (channel = 0; length; length--) {
+ int32_t t, hi, lo;
+
+ hi = volumes[channel] >> 16;
+ lo = volumes[channel] & 0xFFFF;
+
+ t = (int32_t) PA_INT16_SWAP(*samples);
+ t = ((t * lo) >> 16) + (t * hi);
+ t = PA_CLAMP_UNLIKELY(t, -0x8000, 0x7FFF);
+ *samples++ = PA_INT16_SWAP((int16_t) t);
+
+ if (PA_UNLIKELY(++channel >= channels))
+ channel = 0;
+ }
+}
+
+static void
+pa_volume_float32ne_c (float *samples, float *volumes, unsigned channels, unsigned length)
+{
+ unsigned channel;
+
+ length /= sizeof (float);
+
+ for (channel = 0; length; length--) {
+ *samples++ *= volumes[channel];
+
+ if (PA_UNLIKELY(++channel >= channels))
+ channel = 0;
+ }
+}
+
+static void
+pa_volume_float32re_c (float *samples, float *volumes, unsigned channels, unsigned length)
+{
+ unsigned channel;
+
+ length /= sizeof (float);
+
+ for (channel = 0; length; length--) {
+ float t;
+
+ t = PA_FLOAT32_SWAP(*samples);
+ t *= volumes[channel];
+ *samples++ = PA_FLOAT32_SWAP(t);
+
+ if (PA_UNLIKELY(++channel >= channels))
+ channel = 0;
+ }
+}
+
+static void
+pa_volume_s32ne_c (int32_t *samples, int32_t *volumes, unsigned channels, unsigned length)
+{
+ unsigned channel;
+
+ length /= sizeof (int32_t);
+
+ for (channel = 0; length; length--) {
+ int64_t t;
+
+ t = (int64_t)(*samples);
+ t = (t * volumes[channel]) >> 16;
+ t = PA_CLAMP_UNLIKELY(t, -0x80000000LL, 0x7FFFFFFFLL);
+ *samples++ = (int32_t) t;
+
+ if (PA_UNLIKELY(++channel >= channels))
+ channel = 0;
+ }
+}
+
+static void
+pa_volume_s32re_c (int32_t *samples, int32_t *volumes, unsigned channels, unsigned length)
+{
+ unsigned channel;
+
+ length /= sizeof (int32_t);
+
+ for (channel = 0; length; length--) {
+ int64_t t;
+
+ t = (int64_t) PA_INT32_SWAP(*samples);
+ t = (t * volumes[channel]) >> 16;
+ t = PA_CLAMP_UNLIKELY(t, -0x80000000LL, 0x7FFFFFFFLL);
+ *samples++ = PA_INT32_SWAP((int32_t) t);
+
+ if (PA_UNLIKELY(++channel >= channels))
+ channel = 0;
+ }
+}
+
+static void
+pa_volume_s24ne_c (uint8_t *samples, int32_t *volumes, unsigned channels, unsigned length)
+{
+ unsigned channel;
+ uint8_t *e;
+
+ e = samples + length;
+
+ for (channel = 0; samples < e; samples += 3) {
+ int64_t t;
+
+ t = (int64_t)((int32_t) (PA_READ24NE(samples) << 8));
+ t = (t * volumes[channel]) >> 16;
+ t = PA_CLAMP_UNLIKELY(t, -0x80000000LL, 0x7FFFFFFFLL);
+ PA_WRITE24NE(samples, ((uint32_t) (int32_t) t) >> 8);
+
+ if (PA_UNLIKELY(++channel >= channels))
+ channel = 0;
+ }
+}
+
+static void
+pa_volume_s24re_c (uint8_t *samples, int32_t *volumes, unsigned channels, unsigned length)
+{
+ unsigned channel;
+ uint8_t *e;
+
+ e = samples + length;
+
+ for (channel = 0; samples < e; samples += 3) {
+ int64_t t;
+
+ t = (int64_t)((int32_t) (PA_READ24RE(samples) << 8));
+ t = (t * volumes[channel]) >> 16;
+ t = PA_CLAMP_UNLIKELY(t, -0x80000000LL, 0x7FFFFFFFLL);
+ PA_WRITE24RE(samples, ((uint32_t) (int32_t) t) >> 8);
+
+ if (PA_UNLIKELY(++channel >= channels))
+ channel = 0;
+ }
+}
+
+static void
+pa_volume_s24_32ne_c (uint32_t *samples, int32_t *volumes, unsigned channels, unsigned length)
+{
+ unsigned channel;
+
+ length /= sizeof (uint32_t);
+
+ for (channel = 0; length; length--) {
+ int64_t t;
+
+ t = (int64_t) ((int32_t) (*samples << 8));
+ t = (t * volumes[channel]) >> 16;
+ t = PA_CLAMP_UNLIKELY(t, -0x80000000LL, 0x7FFFFFFFLL);
+ *samples++ = ((uint32_t) ((int32_t) t)) >> 8;
+
+ if (PA_UNLIKELY(++channel >= channels))
+ channel = 0;
+ }
+}
+
+static void
+pa_volume_s24_32re_c (uint32_t *samples, int32_t *volumes, unsigned channels, unsigned length)
+{
+ unsigned channel;
+
+ length /= sizeof (uint32_t);
+
+ for (channel = 0; length; length--) {
+ int64_t t;
+
+ t = (int64_t) ((int32_t) (PA_UINT32_SWAP(*samples) << 8));
+ t = (t * volumes[channel]) >> 16;
+ t = PA_CLAMP_UNLIKELY(t, -0x80000000LL, 0x7FFFFFFFLL);
+ *samples++ = PA_UINT32_SWAP(((uint32_t) ((int32_t) t)) >> 8);
+
+ if (PA_UNLIKELY(++channel >= channels))
+ channel = 0;
+ }
+}
+
+static pa_do_volume_func_t do_volume_table[] =
+{
+ [PA_SAMPLE_U8] = (pa_do_volume_func_t) pa_volume_u8_c,
+ [PA_SAMPLE_ALAW] = (pa_do_volume_func_t) pa_volume_alaw_c,
+ [PA_SAMPLE_ULAW] = (pa_do_volume_func_t) pa_volume_ulaw_c,
+ [PA_SAMPLE_S16NE] = (pa_do_volume_func_t) pa_volume_s16ne_c,
+ [PA_SAMPLE_S16RE] = (pa_do_volume_func_t) pa_volume_s16re_c,
+ [PA_SAMPLE_FLOAT32NE] = (pa_do_volume_func_t) pa_volume_float32ne_c,
+ [PA_SAMPLE_FLOAT32RE] = (pa_do_volume_func_t) pa_volume_float32re_c,
+ [PA_SAMPLE_S32NE] = (pa_do_volume_func_t) pa_volume_s32ne_c,
+ [PA_SAMPLE_S32RE] = (pa_do_volume_func_t) pa_volume_s32re_c,
+ [PA_SAMPLE_S24NE] = (pa_do_volume_func_t) pa_volume_s24ne_c,
+ [PA_SAMPLE_S24RE] = (pa_do_volume_func_t) pa_volume_s24re_c,
+ [PA_SAMPLE_S24_32NE] = (pa_do_volume_func_t) pa_volume_s24_32ne_c,
+ [PA_SAMPLE_S24_32RE] = (pa_do_volume_func_t) pa_volume_s24_32re_c
+};
+
+pa_do_volume_func_t pa_get_volume_func(pa_sample_format_t f) {
+ pa_assert(f >= 0);
+ pa_assert(f < PA_SAMPLE_MAX);
+
+ return do_volume_table[f];
+}
+
+void pa_set_volume_func(pa_sample_format_t f, pa_do_volume_func_t func) {
+ pa_assert(f >= 0);
+ pa_assert(f < PA_SAMPLE_MAX);
+
+ do_volume_table[f] = func;
+}
diff --git a/src/pulsecore/svolume_mmx.c b/src/pulsecore/svolume_mmx.c
new file mode 100644
index 00000000..a011789c
--- /dev/null
+++ b/src/pulsecore/svolume_mmx.c
@@ -0,0 +1,319 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2004-2006 Lennart Poettering
+ Copyright 2009 Wim Taymans <wim.taymans@collabora.co.uk>
+
+ 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/timeval.h>
+#include <pulse/rtclock.h>
+
+#include <pulsecore/random.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/g711.h>
+#include <pulsecore/core-util.h>
+
+#include "cpu-x86.h"
+
+#include "sample-util.h"
+#include "endianmacros.h"
+
+#if defined (__i386__) || defined (__amd64__)
+/* in s: 2 int16_t samples
+ * in v: 2 int32_t volumes, fixed point 16:16
+ * out s: contains scaled and clamped int16_t samples.
+ *
+ * We calculate the high 32 bits of a 32x16 multiply which we then
+ * clamp to 16 bits. The calulcation is:
+ *
+ * vl = (v & 0xffff)
+ * vh = (v >> 16)
+ * s = ((s * vl) >> 16) + (s * vh);
+ *
+ * For the first multiply we have to do a sign correction as we need to
+ * multiply a signed int with an unsigned int. Hacker's delight 8-3 gives a
+ * simple formula to correct the sign of the high word after the signed
+ * multiply.
+ */
+#define VOLUME_32x16(s,v) /* .. | vh | vl | */ \
+ " pxor %%mm4, %%mm4 \n\t" /* .. | 0 | 0 | */ \
+ " punpcklwd %%mm4, "#s" \n\t" /* .. | 0 | p0 | */ \
+ " pcmpgtw "#v", %%mm4 \n\t" /* .. | 0 | s(vl) | */ \
+ " pand "#s", %%mm4 \n\t" /* .. | 0 | (p0) | (vl >> 15) & p */ \
+ " movq "#s", %%mm5 \n\t" \
+ " pmulhw "#v", "#s" \n\t" /* .. | 0 | vl*p0 | */ \
+ " paddw %%mm4, "#s" \n\t" /* .. | 0 | vl*p0 | + sign correct */ \
+ " pslld $16, "#s" \n\t" /* .. | vl*p0 | 0 | */ \
+ " psrld $16, "#v" \n\t" /* .. | 0 | vh | */ \
+ " psrad $16, "#s" \n\t" /* .. | vl*p0 | sign extend */ \
+ " pmaddwd %%mm5, "#v" \n\t" /* .. | p0 * vh | */ \
+ " paddd "#s", "#v" \n\t" /* .. | p0 * v0 | */ \
+ " packssdw "#v", "#v" \n\t" /* .. | p1*v1 | p0*v0 | */
+
+/* approximately advances %3 = (%3 + a) % b. This function requires that
+ * a <= b. */
+#define MOD_ADD(a,b) \
+ " add "#a", %3 \n\t" \
+ " mov %3, %4 \n\t" \
+ " sub "#b", %4 \n\t" \
+ " cmovae %4, %3 \n\t"
+
+/* swap 16 bits */
+#define SWAP_16(s) \
+ " movq "#s", %%mm4 \n\t" /* .. | h l | */ \
+ " psrlw $8, %%mm4 \n\t" /* .. | 0 h | */ \
+ " psllw $8, "#s" \n\t" /* .. | l 0 | */ \
+ " por %%mm4, "#s" \n\t" /* .. | l h | */
+
+/* swap 2 registers 16 bits for better pairing */
+#define SWAP_16_2(s1,s2) \
+ " movq "#s1", %%mm4 \n\t" /* .. | h l | */ \
+ " movq "#s2", %%mm5 \n\t" \
+ " psrlw $8, %%mm4 \n\t" /* .. | 0 h | */ \
+ " psrlw $8, %%mm5 \n\t" \
+ " psllw $8, "#s1" \n\t" /* .. | l 0 | */ \
+ " psllw $8, "#s2" \n\t" \
+ " por %%mm4, "#s1" \n\t" /* .. | l h | */ \
+ " por %%mm5, "#s2" \n\t"
+
+static void
+pa_volume_s16ne_mmx (int16_t *samples, int32_t *volumes, unsigned channels, unsigned length)
+{
+ pa_reg_x86 channel, temp;
+
+ /* the max number of samples we process at a time, this is also the max amount
+ * we overread the volume array, which should have enough padding. */
+ channels = PA_MAX (4U, channels);
+
+ __asm__ __volatile__ (
+ " xor %3, %3 \n\t"
+ " sar $1, %2 \n\t" /* length /= sizeof (int16_t) */
+
+ " test $1, %2 \n\t" /* check for odd samples */
+ " je 2f \n\t"
+
+ " movd (%1, %3, 4), %%mm0 \n\t" /* | v0h | v0l | */
+ " movw (%0), %w4 \n\t" /* .. | p0 | */
+ " movd %4, %%mm1 \n\t"
+ VOLUME_32x16 (%%mm1, %%mm0)
+ " movd %%mm0, %4 \n\t" /* .. | p0*v0 | */
+ " movw %w4, (%0) \n\t"
+ " add $2, %0 \n\t"
+ MOD_ADD ($1, %5)
+
+ "2: \n\t"
+ " sar $1, %2 \n\t" /* prepare for processing 2 samples at a time */
+ " test $1, %2 \n\t" /* check for odd samples */
+ " je 4f \n\t"
+
+ "3: \n\t" /* do samples in groups of 2 */
+ " movq (%1, %3, 4), %%mm0 \n\t" /* | v1h | v1l | v0h | v0l | */
+ " movd (%0), %%mm1 \n\t" /* .. | p1 | p0 | */
+ VOLUME_32x16 (%%mm1, %%mm0)
+ " movd %%mm0, (%0) \n\t" /* .. | p1*v1 | p0*v0 | */
+ " add $4, %0 \n\t"
+ MOD_ADD ($2, %5)
+
+ "4: \n\t"
+ " sar $1, %2 \n\t" /* prepare for processing 4 samples at a time */
+ " cmp $0, %2 \n\t"
+ " je 6f \n\t"
+
+ "5: \n\t" /* do samples in groups of 4 */
+ " movq (%1, %3, 4), %%mm0 \n\t" /* | v1h | v1l | v0h | v0l | */
+ " movq 8(%1, %3, 4), %%mm2 \n\t" /* | v3h | v3l | v2h | v2l | */
+ " movd (%0), %%mm1 \n\t" /* .. | p1 | p0 | */
+ " movd 4(%0), %%mm3 \n\t" /* .. | p3 | p2 | */
+ VOLUME_32x16 (%%mm1, %%mm0)
+ VOLUME_32x16 (%%mm3, %%mm2)
+ " movd %%mm0, (%0) \n\t" /* .. | p1*v1 | p0*v0 | */
+ " movd %%mm2, 4(%0) \n\t" /* .. | p3*v3 | p2*v2 | */
+ " add $8, %0 \n\t"
+ MOD_ADD ($4, %5)
+ " dec %2 \n\t"
+ " jne 5b \n\t"
+
+ "6: \n\t"
+ " emms \n\t"
+
+ : "+r" (samples), "+r" (volumes), "+r" (length), "=D" ((pa_reg_x86)channel), "=&r" (temp)
+ : "rm" ((pa_reg_x86)channels)
+ : "cc"
+ );
+}
+
+static void
+pa_volume_s16re_mmx (int16_t *samples, int32_t *volumes, unsigned channels, unsigned length)
+{
+ pa_reg_x86 channel, temp;
+
+ /* the max number of samples we process at a time, this is also the max amount
+ * we overread the volume array, which should have enough padding. */
+ channels = PA_MAX (4U, channels);
+
+ __asm__ __volatile__ (
+ " xor %3, %3 \n\t"
+ " sar $1, %2 \n\t" /* length /= sizeof (int16_t) */
+ " pcmpeqw %%mm6, %%mm6 \n\t" /* .. | ffff | ffff | */
+ " pcmpeqw %%mm7, %%mm7 \n\t" /* .. | ffff | ffff | */
+ " pslld $16, %%mm6 \n\t" /* .. | ffff | 0 | */
+ " psrld $31, %%mm7 \n\t" /* .. | 0 | 1 | */
+
+ " test $1, %2 \n\t" /* check for odd samples */
+ " je 2f \n\t"
+
+ " movd (%1, %3, 4), %%mm0 \n\t" /* | v0h | v0l | */
+ " movw (%0), %w4 \n\t" /* .. | p0 | */
+ " rorw $8, %w4 \n\t"
+ " movd %4, %%mm1 \n\t"
+ VOLUME_32x16 (%%mm1, %%mm0)
+ " movd %%mm0, %4 \n\t" /* .. | p0*v0 | */
+ " rorw $8, %w4 \n\t"
+ " movw %w4, (%0) \n\t"
+ " add $2, %0 \n\t"
+ MOD_ADD ($1, %5)
+
+ "2: \n\t"
+ " sar $1, %2 \n\t" /* prepare for processing 2 samples at a time */
+ " test $1, %2 \n\t" /* check for odd samples */
+ " je 4f \n\t"
+
+ "3: \n\t" /* do samples in groups of 2 */
+ " movq (%1, %3, 4), %%mm0 \n\t" /* | v1h | v1l | v0h | v0l | */
+ " movd (%0), %%mm1 \n\t" /* .. | p1 | p0 | */
+ SWAP_16 (%%mm1)
+ VOLUME_32x16 (%%mm1, %%mm0)
+ SWAP_16 (%%mm0)
+ " movd %%mm0, (%0) \n\t" /* .. | p1*v1 | p0*v0 | */
+ " add $4, %0 \n\t"
+ MOD_ADD ($2, %5)
+
+ "4: \n\t"
+ " sar $1, %2 \n\t" /* prepare for processing 4 samples at a time */
+ " cmp $0, %2 \n\t"
+ " je 6f \n\t"
+
+ "5: \n\t" /* do samples in groups of 4 */
+ " movq (%1, %3, 4), %%mm0 \n\t" /* | v1h | v1l | v0h | v0l | */
+ " movq 8(%1, %3, 4), %%mm2 \n\t" /* | v3h | v3l | v2h | v2l | */
+ " movd (%0), %%mm1 \n\t" /* .. | p1 | p0 | */
+ " movd 4(%0), %%mm3 \n\t" /* .. | p3 | p2 | */
+ SWAP_16_2 (%%mm1, %%mm3)
+ VOLUME_32x16 (%%mm1, %%mm0)
+ VOLUME_32x16 (%%mm3, %%mm2)
+ SWAP_16_2 (%%mm0, %%mm2)
+ " movd %%mm0, (%0) \n\t" /* .. | p1*v1 | p0*v0 | */
+ " movd %%mm2, 4(%0) \n\t" /* .. | p3*v3 | p2*v2 | */
+ " add $8, %0 \n\t"
+ MOD_ADD ($4, %5)
+ " dec %2 \n\t"
+ " jne 5b \n\t"
+
+ "6: \n\t"
+ " emms \n\t"
+
+ : "+r" (samples), "+r" (volumes), "+r" (length), "=D" ((pa_reg_x86)channel), "=&r" (temp)
+ : "rm" ((pa_reg_x86)channels)
+ : "cc"
+ );
+}
+
+#undef RUN_TEST
+
+#ifdef RUN_TEST
+#define CHANNELS 2
+#define SAMPLES 1021
+#define TIMES 1000
+#define PADDING 16
+
+static void run_test (void) {
+ int16_t samples[SAMPLES];
+ int16_t samples_ref[SAMPLES];
+ int16_t samples_orig[SAMPLES];
+ int32_t volumes[CHANNELS + PADDING];
+ int i, j, padding;
+ pa_do_volume_func_t func;
+ pa_usec_t start, stop;
+
+ func = pa_get_volume_func (PA_SAMPLE_S16NE);
+
+ printf ("checking MMX %zd\n", sizeof (samples));
+
+ pa_random (samples, sizeof (samples));
+ /* for (i = 0; i < SAMPLES; i++)
+ samples[i] = -1; */
+ memcpy (samples_ref, samples, sizeof (samples));
+ memcpy (samples_orig, samples, sizeof (samples));
+
+ for (i = 0; i < CHANNELS; i++)
+ volumes[i] = rand() >> 1;
+ /* volumes[i] = 0x0000ffff; */
+ for (padding = 0; padding < PADDING; padding++, i++)
+ volumes[i] = volumes[padding];
+
+ func (samples_ref, volumes, CHANNELS, sizeof (samples));
+ pa_volume_s16ne_mmx (samples, volumes, CHANNELS, sizeof (samples));
+ for (i = 0; i < SAMPLES; i++) {
+ if (samples[i] != samples_ref[i]) {
+ printf ("%d: %04x != %04x (%04x * %08x)\n", i, samples[i], samples_ref[i],
+ samples_orig[i], volumes[i % CHANNELS]);
+ }
+ }
+
+ start = pa_rtclock_now();
+ for (j = 0; j < TIMES; j++) {
+ memcpy (samples, samples_orig, sizeof (samples));
+ pa_volume_s16ne_mmx (samples, volumes, CHANNELS, sizeof (samples));
+ }
+ stop = pa_rtclock_now();
+ pa_log_info("MMX: %llu usec.", (long long unsigned int)(stop - start));
+
+ start = pa_rtclock_now();
+ for (j = 0; j < TIMES; j++) {
+ memcpy (samples_ref, samples_orig, sizeof (samples));
+ func (samples_ref, volumes, CHANNELS, sizeof (samples));
+ }
+ stop = pa_rtclock_now();
+ pa_log_info("ref: %llu usec.", (long long unsigned int)(stop - start));
+
+ pa_assert_se(memcmp(samples_ref, samples, sizeof(samples)) == 0);
+}
+#endif
+
+#endif /* defined (__i386__) || defined (__amd64__) */
+
+
+void pa_volume_func_init_mmx (pa_cpu_x86_flag_t flags) {
+#if defined (__i386__) || defined (__amd64__)
+
+#ifdef RUN_TEST
+ run_test ();
+#endif
+
+ if (flags & PA_CPU_X86_MMX) {
+ pa_log_info("Initialising MMX optimized functions.");
+
+ pa_set_volume_func (PA_SAMPLE_S16NE, (pa_do_volume_func_t) pa_volume_s16ne_mmx);
+ pa_set_volume_func (PA_SAMPLE_S16RE, (pa_do_volume_func_t) pa_volume_s16re_mmx);
+ }
+#endif /* defined (__i386__) || defined (__amd64__) */
+}
diff --git a/src/pulsecore/svolume_sse.c b/src/pulsecore/svolume_sse.c
new file mode 100644
index 00000000..620524fa
--- /dev/null
+++ b/src/pulsecore/svolume_sse.c
@@ -0,0 +1,321 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2004-2006 Lennart Poettering
+ Copyright 2009 Wim Taymans <wim.taymans@collabora.co.uk>
+
+ 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/timeval.h>
+#include <pulse/rtclock.h>
+
+#include <pulsecore/random.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/g711.h>
+#include <pulsecore/core-util.h>
+
+#include "cpu-x86.h"
+
+#include "sample-util.h"
+#include "endianmacros.h"
+
+#if defined (__i386__) || defined (__amd64__)
+
+#define VOLUME_32x16(s,v) /* .. | vh | vl | */ \
+ " pxor %%xmm4, %%xmm4 \n\t" /* .. | 0 | 0 | */ \
+ " punpcklwd %%xmm4, "#s" \n\t" /* .. | 0 | p0 | */ \
+ " pcmpgtw "#s", %%xmm4 \n\t" /* .. | 0 | s(p0) | */ \
+ " pand "#v", %%xmm4 \n\t" /* .. | 0 | (vl) | */ \
+ " movdqa "#s", %%xmm5 \n\t" \
+ " pmulhuw "#v", "#s" \n\t" /* .. | 0 | vl*p0 | */ \
+ " psubd %%xmm4, "#s" \n\t" /* .. | 0 | vl*p0 | + sign correct */ \
+ " psrld $16, "#v" \n\t" /* .. | 0 | vh | */ \
+ " pmaddwd %%xmm5, "#v" \n\t" /* .. | p0 * vh | */ \
+ " paddd "#s", "#v" \n\t" /* .. | p0 * v0 | */ \
+ " packssdw "#v", "#v" \n\t" /* .. | p1*v1 | p0*v0 | */
+
+#define MOD_ADD(a,b) \
+ " add "#a", %3 \n\t" /* channel += inc */ \
+ " mov %3, %4 \n\t" \
+ " sub "#b", %4 \n\t" /* tmp = channel - channels */ \
+ " cmovae %4, %3 \n\t" /* if (tmp >= 0) channel = tmp */
+
+/* swap 16 bits */
+#define SWAP_16(s) \
+ " movdqa "#s", %%xmm4 \n\t" /* .. | h l | */ \
+ " psrlw $8, %%xmm4 \n\t" /* .. | 0 h | */ \
+ " psllw $8, "#s" \n\t" /* .. | l 0 | */ \
+ " por %%xmm4, "#s" \n\t" /* .. | l h | */
+
+/* swap 2 registers 16 bits for better pairing */
+#define SWAP_16_2(s1,s2) \
+ " movdqa "#s1", %%xmm4 \n\t" /* .. | h l | */ \
+ " movdqa "#s2", %%xmm5 \n\t" \
+ " psrlw $8, %%xmm4 \n\t" /* .. | 0 h | */ \
+ " psrlw $8, %%xmm5 \n\t" \
+ " psllw $8, "#s1" \n\t" /* .. | l 0 | */ \
+ " psllw $8, "#s2" \n\t" \
+ " por %%xmm4, "#s1" \n\t" /* .. | l h | */ \
+ " por %%xmm5, "#s2" \n\t"
+
+static void
+pa_volume_s16ne_sse2 (int16_t *samples, int32_t *volumes, unsigned channels, unsigned length)
+{
+ pa_reg_x86 channel, temp;
+
+ /* the max number of samples we process at a time, this is also the max amount
+ * we overread the volume array, which should have enough padding. */
+ channels = PA_MAX (8U, channels);
+
+ __asm__ __volatile__ (
+ " xor %3, %3 \n\t"
+ " sar $1, %2 \n\t" /* length /= sizeof (int16_t) */
+
+ " test $1, %2 \n\t" /* check for odd samples */
+ " je 2f \n\t"
+
+ " movd (%1, %3, 4), %%xmm0 \n\t" /* | v0h | v0l | */
+ " movw (%0), %w4 \n\t" /* .. | p0 | */
+ " movd %4, %%xmm1 \n\t"
+ VOLUME_32x16 (%%xmm1, %%xmm0)
+ " movd %%xmm0, %4 \n\t" /* .. | p0*v0 | */
+ " movw %w4, (%0) \n\t"
+ " add $2, %0 \n\t"
+ MOD_ADD ($1, %5)
+
+ "2: \n\t"
+ " sar $1, %2 \n\t" /* prepare for processing 2 samples at a time */
+ " test $1, %2 \n\t"
+ " je 4f \n\t"
+
+ "3: \n\t" /* do samples in groups of 2 */
+ " movq (%1, %3, 4), %%xmm0 \n\t" /* | v1h | v1l | v0h | v0l | */
+ " movd (%0), %%xmm1 \n\t" /* .. | p1 | p0 | */
+ VOLUME_32x16 (%%xmm1, %%xmm0)
+ " movd %%xmm0, (%0) \n\t" /* .. | p1*v1 | p0*v0 | */
+ " add $4, %0 \n\t"
+ MOD_ADD ($2, %5)
+
+ "4: \n\t"
+ " sar $1, %2 \n\t" /* prepare for processing 4 samples at a time */
+ " test $1, %2 \n\t"
+ " je 6f \n\t"
+
+ /* FIXME, we can do aligned access of the volume values if we can guarantee
+ * that the array is 16 bytes aligned, we probably have to do the odd values
+ * after this then. */
+ "5: \n\t" /* do samples in groups of 4 */
+ " movdqu (%1, %3, 4), %%xmm0 \n\t" /* | v3h | v3l .. v0h | v0l | */
+ " movq (%0), %%xmm1 \n\t" /* .. | p3 .. p0 | */
+ VOLUME_32x16 (%%xmm1, %%xmm0)
+ " movq %%xmm0, (%0) \n\t" /* .. | p3*v3 .. p0*v0 | */
+ " add $8, %0 \n\t"
+ MOD_ADD ($4, %5)
+
+ "6: \n\t"
+ " sar $1, %2 \n\t" /* prepare for processing 8 samples at a time */
+ " cmp $0, %2 \n\t"
+ " je 8f \n\t"
+
+ "7: \n\t" /* do samples in groups of 8 */
+ " movdqu (%1, %3, 4), %%xmm0 \n\t" /* | v3h | v3l .. v0h | v0l | */
+ " movdqu 16(%1, %3, 4), %%xmm2 \n\t" /* | v7h | v7l .. v4h | v4l | */
+ " movq (%0), %%xmm1 \n\t" /* .. | p3 .. p0 | */
+ " movq 8(%0), %%xmm3 \n\t" /* .. | p7 .. p4 | */
+ VOLUME_32x16 (%%xmm1, %%xmm0)
+ VOLUME_32x16 (%%xmm3, %%xmm2)
+ " movq %%xmm0, (%0) \n\t" /* .. | p3*v3 .. p0*v0 | */
+ " movq %%xmm2, 8(%0) \n\t" /* .. | p7*v7 .. p4*v4 | */
+ " add $16, %0 \n\t"
+ MOD_ADD ($8, %5)
+ " dec %2 \n\t"
+ " jne 7b \n\t"
+ "8: \n\t"
+
+ : "+r" (samples), "+r" (volumes), "+r" (length), "=D" (channel), "=&r" (temp)
+ : "rm" ((pa_reg_x86)channels)
+ : "cc"
+ );
+}
+
+static void
+pa_volume_s16re_sse2 (int16_t *samples, int32_t *volumes, unsigned channels, unsigned length)
+{
+ pa_reg_x86 channel, temp;
+
+ /* the max number of samples we process at a time, this is also the max amount
+ * we overread the volume array, which should have enough padding. */
+ channels = PA_MAX (8U, channels);
+
+ __asm__ __volatile__ (
+ " xor %3, %3 \n\t"
+ " sar $1, %2 \n\t" /* length /= sizeof (int16_t) */
+
+ " test $1, %2 \n\t" /* check for odd samples */
+ " je 2f \n\t"
+
+ " movd (%1, %3, 4), %%xmm0 \n\t" /* | v0h | v0l | */
+ " movw (%0), %w4 \n\t" /* .. | p0 | */
+ " rorw $8, %w4 \n\t"
+ " movd %4, %%xmm1 \n\t"
+ VOLUME_32x16 (%%xmm1, %%xmm0)
+ " movd %%xmm0, %4 \n\t" /* .. | p0*v0 | */
+ " rorw $8, %w4 \n\t"
+ " movw %w4, (%0) \n\t"
+ " add $2, %0 \n\t"
+ MOD_ADD ($1, %5)
+
+ "2: \n\t"
+ " sar $1, %2 \n\t" /* prepare for processing 2 samples at a time */
+ " test $1, %2 \n\t"
+ " je 4f \n\t"
+
+ "3: \n\t" /* do samples in groups of 2 */
+ " movq (%1, %3, 4), %%xmm0 \n\t" /* | v1h | v1l | v0h | v0l | */
+ " movd (%0), %%xmm1 \n\t" /* .. | p1 | p0 | */
+ SWAP_16 (%%xmm1)
+ VOLUME_32x16 (%%xmm1, %%xmm0)
+ SWAP_16 (%%xmm0)
+ " movd %%xmm0, (%0) \n\t" /* .. | p1*v1 | p0*v0 | */
+ " add $4, %0 \n\t"
+ MOD_ADD ($2, %5)
+
+ "4: \n\t"
+ " sar $1, %2 \n\t" /* prepare for processing 4 samples at a time */
+ " test $1, %2 \n\t"
+ " je 6f \n\t"
+
+ /* FIXME, we can do aligned access of the volume values if we can guarantee
+ * that the array is 16 bytes aligned, we probably have to do the odd values
+ * after this then. */
+ "5: \n\t" /* do samples in groups of 4 */
+ " movdqu (%1, %3, 4), %%xmm0 \n\t" /* | v3h | v3l .. v0h | v0l | */
+ " movq (%0), %%xmm1 \n\t" /* .. | p3 .. p0 | */
+ SWAP_16 (%%xmm1)
+ VOLUME_32x16 (%%xmm1, %%xmm0)
+ SWAP_16 (%%xmm0)
+ " movq %%xmm0, (%0) \n\t" /* .. | p3*v3 .. p0*v0 | */
+ " add $8, %0 \n\t"
+ MOD_ADD ($4, %5)
+
+ "6: \n\t"
+ " sar $1, %2 \n\t" /* prepare for processing 8 samples at a time */
+ " cmp $0, %2 \n\t"
+ " je 8f \n\t"
+
+ "7: \n\t" /* do samples in groups of 8 */
+ " movdqu (%1, %3, 4), %%xmm0 \n\t" /* | v3h | v3l .. v0h | v0l | */
+ " movdqu 16(%1, %3, 4), %%xmm2 \n\t" /* | v7h | v7l .. v4h | v4l | */
+ " movq (%0), %%xmm1 \n\t" /* .. | p3 .. p0 | */
+ " movq 8(%0), %%xmm3 \n\t" /* .. | p7 .. p4 | */
+ SWAP_16_2 (%%xmm1, %%xmm3)
+ VOLUME_32x16 (%%xmm1, %%xmm0)
+ VOLUME_32x16 (%%xmm3, %%xmm2)
+ SWAP_16_2 (%%xmm0, %%xmm2)
+ " movq %%xmm0, (%0) \n\t" /* .. | p3*v3 .. p0*v0 | */
+ " movq %%xmm2, 8(%0) \n\t" /* .. | p7*v7 .. p4*v4 | */
+ " add $16, %0 \n\t"
+ MOD_ADD ($8, %5)
+ " dec %2 \n\t"
+ " jne 7b \n\t"
+ "8: \n\t"
+
+ : "+r" (samples), "+r" (volumes), "+r" (length), "=D" (channel), "=&r" (temp)
+ : "rm" ((pa_reg_x86)channels)
+ : "cc"
+ );
+}
+
+#undef RUN_TEST
+
+#ifdef RUN_TEST
+#define CHANNELS 2
+#define SAMPLES 1021
+#define TIMES 1000
+#define PADDING 16
+
+static void run_test (void) {
+ int16_t samples[SAMPLES];
+ int16_t samples_ref[SAMPLES];
+ int16_t samples_orig[SAMPLES];
+ int32_t volumes[CHANNELS + PADDING];
+ int i, j, padding;
+ pa_do_volume_func_t func;
+ pa_usec_t start, stop;
+
+ func = pa_get_volume_func (PA_SAMPLE_S16NE);
+
+ printf ("checking SSE2 %zd\n", sizeof (samples));
+
+ pa_random (samples, sizeof (samples));
+ memcpy (samples_ref, samples, sizeof (samples));
+ memcpy (samples_orig, samples, sizeof (samples));
+
+ for (i = 0; i < CHANNELS; i++)
+ volumes[i] = rand() >> 1;
+ for (padding = 0; padding < PADDING; padding++, i++)
+ volumes[i] = volumes[padding];
+
+ func (samples_ref, volumes, CHANNELS, sizeof (samples));
+ pa_volume_s16ne_sse2 (samples, volumes, CHANNELS, sizeof (samples));
+ for (i = 0; i < SAMPLES; i++) {
+ if (samples[i] != samples_ref[i]) {
+ printf ("%d: %04x != %04x (%04x * %04x)\n", i, samples[i], samples_ref[i],
+ samples_orig[i], volumes[i % CHANNELS]);
+ }
+ }
+
+ start = pa_rtclock_now();
+ for (j = 0; j < TIMES; j++) {
+ memcpy (samples, samples_orig, sizeof (samples));
+ pa_volume_s16ne_sse2 (samples, volumes, CHANNELS, sizeof (samples));
+ }
+ stop = pa_rtclock_now();
+ pa_log_info("SSE: %llu usec.", (long long unsigned int)(stop - start));
+
+ start = pa_rtclock_now();
+ for (j = 0; j < TIMES; j++) {
+ memcpy (samples_ref, samples_orig, sizeof (samples));
+ func (samples_ref, volumes, CHANNELS, sizeof (samples));
+ }
+ stop = pa_rtclock_now();
+ pa_log_info("ref: %llu usec.", (long long unsigned int)(stop - start));
+
+ pa_assert_se(memcmp(samples_ref, samples, sizeof(samples)) == 0);
+}
+#endif
+#endif /* defined (__i386__) || defined (__amd64__) */
+
+void pa_volume_func_init_sse (pa_cpu_x86_flag_t flags) {
+#if defined (__i386__) || defined (__amd64__)
+
+#ifdef RUN_TEST
+ run_test ();
+#endif
+
+ if (flags & PA_CPU_X86_SSE2) {
+ pa_log_info("Initialising SSE2 optimized functions.");
+
+ pa_set_volume_func (PA_SAMPLE_S16NE, (pa_do_volume_func_t) pa_volume_s16ne_sse2);
+ pa_set_volume_func (PA_SAMPLE_S16RE, (pa_do_volume_func_t) pa_volume_s16re_sse2);
+ }
+#endif /* defined (__i386__) || defined (__amd64__) */
+}
diff --git a/src/pulsecore/thread-mq.c b/src/pulsecore/thread-mq.c
index 34f92a7e..73997a74 100644
--- a/src/pulsecore/thread-mq.c
+++ b/src/pulsecore/thread-mq.c
@@ -59,7 +59,7 @@ static void asyncmsgq_read_cb(pa_mainloop_api*api, pa_io_event* e, int fd, pa_io
pa_memchunk chunk;
/* Check whether there is a message for us to process */
- while (pa_asyncmsgq_get(aq, &object, &code, &data, &offset, &chunk, 0) == 0) {
+ while (pa_asyncmsgq_get(aq, &object, &code, &data, &offset, &chunk, 0) >= 0) {
int ret;
ret = pa_asyncmsgq_dispatch(object, code, data, offset, &chunk);
@@ -104,6 +104,15 @@ void pa_thread_mq_init(pa_thread_mq *q, pa_mainloop_api *mainloop, pa_rtpoll *rt
void pa_thread_mq_done(pa_thread_mq *q) {
pa_assert(q);
+ /* Since we are called from main context we can be sure that the
+ * inq is empty. However, the outq might still contain messages
+ * for the main loop, which we need to dispatch (e.g. release
+ * msgs, other stuff). Hence do so if we aren't currently
+ * dispatching anyway. */
+
+ if (!pa_asyncmsgq_dispatching(q->outq))
+ pa_asyncmsgq_flush(q->outq, TRUE);
+
q->mainloop->io_free(q->read_event);
q->mainloop->io_free(q->write_event);
q->read_event = q->write_event = NULL;
diff --git a/src/pulsecore/thread-mq.h b/src/pulsecore/thread-mq.h
index 3b5e0e78..96839d25 100644
--- a/src/pulsecore/thread-mq.h
+++ b/src/pulsecore/thread-mq.h
@@ -45,4 +45,12 @@ void pa_thread_mq_install(pa_thread_mq *q);
/* Return the pa_thread_mq object that is set for the current thread */
pa_thread_mq *pa_thread_mq_get(void);
+/* Verify that we are in control context (aka 'main context'). */
+#define pa_assert_ctl_context(s) \
+ pa_assert(!pa_thread_mq_get())
+
+/* Verify that we are in IO context (aka 'thread context'). */
+#define pa_assert_io_context(s) \
+ pa_assert(pa_thread_mq_get())
+
#endif
diff --git a/src/pulsecore/time-smoother.c b/src/pulsecore/time-smoother.c
index d6c37878..1371ad56 100644
--- a/src/pulsecore/time-smoother.c
+++ b/src/pulsecore/time-smoother.c
@@ -196,6 +196,13 @@ static double avg_gradient(pa_smoother *s, pa_usec_t x) {
int64_t ax = 0, ay = 0, k, t;
double r;
+ /* FIXME: Optimization: Jason Newton suggested that instead of
+ * going through the history on each iteration we could calculated
+ * avg_gradient() as we go.
+ *
+ * Second idea: it might make sense to weight history entries:
+ * more recent entries should matter more than old ones. */
+
/* Too few measurements, assume gradient of 1 */
if (s->n_history < s->min_history)
return 1;
diff --git a/src/pulsecore/usergroup.c b/src/pulsecore/usergroup.c
new file mode 100644
index 00000000..71b13bca
--- /dev/null
+++ b/src/pulsecore/usergroup.c
@@ -0,0 +1,372 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2009 Ted Percival
+
+ 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 <sys/types.h>
+#include <errno.h>
+
+#ifdef HAVE_PWD_H
+#include <pwd.h>
+#endif
+
+#ifdef HAVE_GRP_H
+#include <grp.h>
+#endif
+
+#include <pulse/xmalloc.h>
+#include <pulsecore/macro.h>
+
+#include "usergroup.h"
+
+#ifdef HAVE_GRP_H
+
+/* Returns a suitable starting size for a getgrnam_r() or getgrgid_r() buffer,
+ plus the size of a struct group.
+ */
+static size_t starting_getgr_buflen(void) {
+ size_t full_size;
+ long n;
+#ifdef _SC_GETGR_R_SIZE_MAX
+ n = sysconf(_SC_GETGR_R_SIZE_MAX);
+#else
+ n = -1;
+#endif
+ if (n <= 0)
+ n = 512;
+
+ full_size = (size_t) n + sizeof(struct group);
+
+ if (full_size < (size_t) n) /* check for integer overflow */
+ return (size_t) n;
+
+ return full_size;
+}
+
+/* Returns a suitable starting size for a getpwnam_r() or getpwuid_r() buffer,
+ plus the size of a struct passwd.
+ */
+static size_t starting_getpw_buflen(void) {
+ long n;
+ size_t full_size;
+
+#ifdef _SC_GETPW_R_SIZE_MAX
+ n = sysconf(_SC_GETPW_R_SIZE_MAX);
+#else
+ n = -1;
+#endif
+ if (n <= 0)
+ n = 512;
+
+ full_size = (size_t) n + sizeof(struct passwd);
+
+ if (full_size < (size_t) n) /* check for integer overflow */
+ return (size_t) n;
+
+ return full_size;
+}
+
+/* Given a memory allocation (*bufptr) and its length (*buflenptr),
+ double the size of the allocation, updating the given buffer and length
+ arguments. This function should be used in conjunction with the pa_*alloc
+ and pa_xfree functions.
+
+ Unlike realloc(), this function does *not* retain the original buffer's
+ contents.
+
+ Returns 0 on success, nonzero on error. The error cause is indicated by
+ errno.
+ */
+static int expand_buffer_trashcontents(void **bufptr, size_t *buflenptr) {
+ size_t newlen;
+
+ if (!bufptr || !*bufptr || !buflenptr) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ newlen = *buflenptr * 2;
+
+ if (newlen < *buflenptr) {
+ errno = EOVERFLOW;
+ return -1;
+ }
+
+ /* Don't bother retaining memory contents; free & alloc anew */
+ pa_xfree(*bufptr);
+
+ *bufptr = pa_xmalloc(newlen);
+ *buflenptr = newlen;
+
+ return 0;
+}
+
+#ifdef HAVE_GETGRGID_R
+/* Thread-safe getgrgid() replacement.
+ Returned value should be freed using pa_getgrgid_free() when the caller is
+ finished with the returned group data.
+
+ API is the same as getgrgid(), errors are indicated by a NULL return;
+ consult errno for the error cause (zero it before calling).
+ */
+struct group *pa_getgrgid_malloc(gid_t gid) {
+ size_t buflen, getgr_buflen;
+ int err;
+ void *buf;
+ void *getgr_buf;
+ struct group *result = NULL;
+
+ buflen = starting_getgr_buflen();
+ buf = pa_xmalloc(buflen);
+
+ getgr_buflen = buflen - sizeof(struct group);
+ getgr_buf = (char *)buf + sizeof(struct group);
+
+ while ((err = getgrgid_r(gid, (struct group *)buf, getgr_buf,
+ getgr_buflen, &result)) == ERANGE)
+ {
+ if (expand_buffer_trashcontents(&buf, &buflen))
+ break;
+
+ getgr_buflen = buflen - sizeof(struct group);
+ getgr_buf = (char *)buf + sizeof(struct group);
+ }
+
+ if (err || !result) {
+ result = NULL;
+ if (buf) {
+ pa_xfree(buf);
+ buf = NULL;
+ }
+ }
+
+ pa_assert(result == buf || result == NULL);
+
+ return result;
+}
+
+void pa_getgrgid_free(struct group *grp) {
+ pa_xfree(grp);
+}
+
+#else /* !HAVE_GETGRGID_R */
+
+struct group *pa_getgrgid_malloc(gid_t gid) {
+ return getgrgid(gid);
+}
+
+void pa_getgrgid_free(struct group *grp) {
+ /* nothing */
+ return;
+}
+
+#endif /* !HAVE_GETGRGID_R */
+
+#ifdef HAVE_GETGRNAM_R
+/* Thread-safe getgrnam() function.
+ Returned value should be freed using pa_getgrnam_free() when the caller is
+ finished with the returned group data.
+
+ API is the same as getgrnam(), errors are indicated by a NULL return;
+ consult errno for the error cause (zero it before calling).
+ */
+struct group *pa_getgrnam_malloc(const char *name) {
+ size_t buflen, getgr_buflen;
+ int err;
+ void *buf;
+ void *getgr_buf;
+ struct group *result = NULL;
+
+ buflen = starting_getgr_buflen();
+ buf = pa_xmalloc(buflen);
+
+ getgr_buflen = buflen - sizeof(struct group);
+ getgr_buf = (char *)buf + sizeof(struct group);
+
+ while ((err = getgrnam_r(name, (struct group *)buf, getgr_buf,
+ getgr_buflen, &result)) == ERANGE)
+ {
+ if (expand_buffer_trashcontents(&buf, &buflen))
+ break;
+
+ getgr_buflen = buflen - sizeof(struct group);
+ getgr_buf = (char *)buf + sizeof(struct group);
+ }
+
+ if (err || !result) {
+ result = NULL;
+ if (buf) {
+ pa_xfree(buf);
+ buf = NULL;
+ }
+ }
+
+ pa_assert(result == buf || result == NULL);
+
+ return result;
+}
+
+void pa_getgrnam_free(struct group *group) {
+ pa_xfree(group);
+}
+
+#else /* !HAVE_GETGRNAM_R */
+
+struct group *pa_getgrnam_malloc(const char *name) {
+ return getgrnam(name);
+}
+
+void pa_getgrnam_free(struct group *group) {
+ /* nothing */
+ return;
+}
+
+#endif /* HAVE_GETGRNAM_R */
+
+#endif /* HAVE_GRP_H */
+
+#ifdef HAVE_PWD_H
+
+#ifdef HAVE_GETPWNAM_R
+/* Thread-safe getpwnam() function.
+ Returned value should be freed using pa_getpwnam_free() when the caller is
+ finished with the returned passwd data.
+
+ API is the same as getpwnam(), errors are indicated by a NULL return;
+ consult errno for the error cause (zero it before calling).
+ */
+struct passwd *pa_getpwnam_malloc(const char *name) {
+ size_t buflen, getpw_buflen;
+ int err;
+ void *buf;
+ void *getpw_buf;
+ struct passwd *result = NULL;
+
+ buflen = starting_getpw_buflen();
+ buf = pa_xmalloc(buflen);
+
+ getpw_buflen = buflen - sizeof(struct passwd);
+ getpw_buf = (char *)buf + sizeof(struct passwd);
+
+ while ((err = getpwnam_r(name, (struct passwd *)buf, getpw_buf,
+ getpw_buflen, &result)) == ERANGE)
+ {
+ if (expand_buffer_trashcontents(&buf, &buflen))
+ break;
+
+ getpw_buflen = buflen - sizeof(struct passwd);
+ getpw_buf = (char *)buf + sizeof(struct passwd);
+ }
+
+ if (err || !result) {
+ result = NULL;
+ if (buf) {
+ pa_xfree(buf);
+ buf = NULL;
+ }
+ }
+
+ pa_assert(result == buf || result == NULL);
+
+ return result;
+}
+
+void pa_getpwnam_free(struct passwd *passwd) {
+ pa_xfree(passwd);
+}
+
+#else /* !HAVE_GETPWNAM_R */
+
+struct passwd *pa_getpwnam_malloc(const char *name) {
+ return getpwnam(name);
+}
+
+void pa_getpwnam_free(struct passwd *passwd) {
+ /* nothing */
+ return;
+}
+
+#endif /* !HAVE_GETPWNAM_R */
+
+#ifdef HAVE_GETPWUID_R
+/* Thread-safe getpwuid() function.
+ Returned value should be freed using pa_getpwuid_free() when the caller is
+ finished with the returned group data.
+
+ API is the same as getpwuid(), errors are indicated by a NULL return;
+ consult errno for the error cause (zero it before calling).
+ */
+struct passwd *pa_getpwuid_malloc(uid_t uid) {
+ size_t buflen, getpw_buflen;
+ int err;
+ void *buf;
+ void *getpw_buf;
+ struct passwd *result = NULL;
+
+ buflen = starting_getpw_buflen();
+ buf = pa_xmalloc(buflen);
+
+ getpw_buflen = buflen - sizeof(struct passwd);
+ getpw_buf = (char *)buf + sizeof(struct passwd);
+
+ while ((err = getpwuid_r(uid, (struct passwd *)buf, getpw_buf,
+ getpw_buflen, &result)) == ERANGE)
+ {
+ if (expand_buffer_trashcontents(&buf, &buflen))
+ break;
+
+ getpw_buflen = buflen - sizeof(struct passwd);
+ getpw_buf = (char *)buf + sizeof(struct passwd);
+ }
+
+ if (err || !result) {
+ result = NULL;
+ if (buf) {
+ pa_xfree(buf);
+ buf = NULL;
+ }
+ }
+
+ pa_assert(result == buf || result == NULL);
+
+ return result;
+}
+
+void pa_getpwuid_free(struct passwd *passwd) {
+ pa_xfree(passwd);
+}
+
+#else /* !HAVE_GETPWUID_R */
+
+struct passwd *pa_getpwuid_malloc(uid_t uid) {
+ return getpwuid(uid);
+}
+
+void pa_getpwuid_free(struct passwd *passwd) {
+ /* nothing */
+ return;
+}
+
+#endif /* !HAVE_GETPWUID_R */
+
+#endif /* HAVE_PWD_H */
diff --git a/src/pulsecore/usergroup.h b/src/pulsecore/usergroup.h
new file mode 100644
index 00000000..1c091638
--- /dev/null
+++ b/src/pulsecore/usergroup.h
@@ -0,0 +1,51 @@
+#ifndef foousergrouphfoo
+#define foousergrouphfoo
+
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2009 Ted Percival
+
+ 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>
+
+#ifndef PACKAGE
+#error "Please include config.h before including this file!"
+#endif
+
+#ifdef HAVE_GRP_H
+
+struct group *pa_getgrgid_malloc(gid_t gid);
+void pa_getgrgid_free(struct group *grp);
+
+struct group *pa_getgrnam_malloc(const char *name);
+void pa_getgrnam_free(struct group *group);
+
+#endif /* HAVE_GRP_H */
+
+#ifdef HAVE_PWD_H
+
+struct passwd *pa_getpwuid_malloc(uid_t uid);
+void pa_getpwuid_free(struct passwd *passwd);
+
+struct passwd *pa_getpwnam_malloc(const char *name);
+void pa_getpwnam_free(struct passwd *passwd);
+
+#endif /* HAVE_PWD_H */
+
+#endif /* foousergrouphfoo */
diff --git a/src/pulsecore/vector.h b/src/pulsecore/vector.h
index 924e3cb8..9de3b8cd 100644
--- a/src/pulsecore/vector.h
+++ b/src/pulsecore/vector.h
@@ -23,7 +23,8 @@
#include <inttypes.h>
/* First, define HAVE_VECTOR if we have the gcc vector extensions at all */
-#if defined(__SSE2__) || defined(__ALTIVEC__)
+#if defined(__SSE2__)
+ /* || defined(__ALTIVEC__)*/
#define HAVE_VECTOR
diff --git a/src/pulsecore/x11prop.c b/src/pulsecore/x11prop.c
index 873a76e7..dc8ec294 100644
--- a/src/pulsecore/x11prop.c
+++ b/src/pulsecore/x11prop.c
@@ -32,12 +32,12 @@
void pa_x11_set_prop(Display *d, const char *name, const char *data) {
Atom a = XInternAtom(d, name, False);
- XChangeProperty(d, RootWindow(d, 0), a, XA_STRING, 8, PropModeReplace, (const unsigned char*) data, (int) (strlen(data)+1));
+ XChangeProperty(d, DefaultRootWindow(d), a, XA_STRING, 8, PropModeReplace, (const unsigned char*) data, (int) (strlen(data)+1));
}
void pa_x11_del_prop(Display *d, const char *name) {
Atom a = XInternAtom(d, name, False);
- XDeleteProperty(d, RootWindow(d, 0), a);
+ XDeleteProperty(d, DefaultRootWindow(d), a);
}
char* pa_x11_get_prop(Display *d, const char *name, char *p, size_t l) {
@@ -47,13 +47,21 @@ char* pa_x11_get_prop(Display *d, const char *name, char *p, size_t l) {
unsigned long nbytes_after;
unsigned char *prop = NULL;
char *ret = NULL;
+ int window_ret;
Atom a = XInternAtom(d, name, False);
- if (XGetWindowProperty(d, RootWindow(d, 0), a, 0, (long) ((l+2)/4), False, XA_STRING, &actual_type, &actual_format, &nitems, &nbytes_after, &prop) != Success)
- goto finish;
- if (actual_type != XA_STRING)
- goto finish;
+ window_ret = XGetWindowProperty(d, DefaultRootWindow(d), a, 0, (long) ((l+2)/4), False, XA_STRING, &actual_type, &actual_format, &nitems, &nbytes_after, &prop);
+
+ if (window_ret != Success || actual_type != XA_STRING) {
+ if (DefaultScreen(d) != 0) {
+ window_ret = XGetWindowProperty(d, RootWindow(d, 0), a, 0, (long) ((l+2)/4), False, XA_STRING, &actual_type, &actual_format, &nitems, &nbytes_after, &prop);
+
+ if (window_ret != Success || actual_type != XA_STRING)
+ goto finish;
+ } else
+ goto finish;
+ }
memcpy(p, prop, nitems);
p[nitems] = 0;