summaryrefslogtreecommitdiffstats
path: root/src/pulsecore
diff options
context:
space:
mode:
Diffstat (limited to 'src/pulsecore')
-rw-r--r--src/pulsecore/asyncq.c18
-rw-r--r--src/pulsecore/aupdate.c129
-rw-r--r--src/pulsecore/aupdate.h98
-rw-r--r--src/pulsecore/authkey.c2
-rw-r--r--src/pulsecore/avahi-wrap.c9
-rw-r--r--src/pulsecore/card.c60
-rw-r--r--src/pulsecore/card.h11
-rw-r--r--src/pulsecore/cli-command.c116
-rw-r--r--src/pulsecore/cli-text.c114
-rw-r--r--src/pulsecore/cli.c13
-rw-r--r--src/pulsecore/conf-parser.c50
-rw-r--r--src/pulsecore/core-rtclock.c199
-rw-r--r--src/pulsecore/core-rtclock.h51
-rw-r--r--src/pulsecore/core-scache.c49
-rw-r--r--src/pulsecore/core-util.c454
-rw-r--r--src/pulsecore/core-util.h19
-rw-r--r--src/pulsecore/core.c28
-rw-r--r--src/pulsecore/core.h14
-rw-r--r--src/pulsecore/database-gdbm.c249
-rw-r--r--src/pulsecore/database-tdb.c227
-rw-r--r--src/pulsecore/database.h61
-rw-r--r--src/pulsecore/dbus-shared.c107
-rw-r--r--src/pulsecore/dbus-shared.h42
-rw-r--r--src/pulsecore/dbus-util.c424
-rw-r--r--src/pulsecore/dbus-util.h63
-rw-r--r--src/pulsecore/endianmacros.h8
-rw-r--r--src/pulsecore/hashmap.c42
-rw-r--r--src/pulsecore/hashmap.h17
-rw-r--r--src/pulsecore/hook-list.c6
-rw-r--r--src/pulsecore/hook-list.h2
-rw-r--r--src/pulsecore/idxset.c14
-rw-r--r--src/pulsecore/idxset.h7
-rw-r--r--src/pulsecore/ioline.c48
-rw-r--r--src/pulsecore/ioline.h11
-rw-r--r--src/pulsecore/ipacl.h4
-rw-r--r--src/pulsecore/llist.h6
-rw-r--r--src/pulsecore/log.c48
-rw-r--r--src/pulsecore/log.h4
-rw-r--r--src/pulsecore/ltdl-helper.h1
-rw-r--r--src/pulsecore/macro.h51
-rw-r--r--src/pulsecore/memblock.c88
-rw-r--r--src/pulsecore/memblockq.c82
-rw-r--r--src/pulsecore/memblockq.h7
-rw-r--r--src/pulsecore/memtrap.c230
-rw-r--r--src/pulsecore/memtrap.h51
-rw-r--r--src/pulsecore/mime-type.c182
-rw-r--r--src/pulsecore/mime-type.h37
-rw-r--r--src/pulsecore/modargs.c73
-rw-r--r--src/pulsecore/modargs.h2
-rw-r--r--src/pulsecore/modinfo.c5
-rw-r--r--src/pulsecore/modinfo.h1
-rw-r--r--src/pulsecore/module.c9
-rw-r--r--src/pulsecore/module.h4
-rw-r--r--src/pulsecore/mutex-posix.c2
-rw-r--r--src/pulsecore/mutex.h4
-rw-r--r--src/pulsecore/native-common.h10
-rw-r--r--src/pulsecore/object.c4
-rw-r--r--src/pulsecore/parseaddr.c26
-rw-r--r--src/pulsecore/parseaddr.h8
-rw-r--r--src/pulsecore/pdispatch.c32
-rw-r--r--src/pulsecore/pdispatch.h2
-rw-r--r--src/pulsecore/proplist-util.c29
-rw-r--r--src/pulsecore/protocol-esound.c34
-rw-r--r--src/pulsecore/protocol-http.c681
-rw-r--r--src/pulsecore/protocol-http.h6
-rw-r--r--src/pulsecore/protocol-native.c794
-rw-r--r--src/pulsecore/protocol-simple.c2
-rw-r--r--src/pulsecore/pstream.c8
-rw-r--r--src/pulsecore/ratelimit.c7
-rw-r--r--src/pulsecore/refcnt.h44
-rw-r--r--src/pulsecore/rtkit.c189
-rw-r--r--src/pulsecore/rtkit.h62
-rw-r--r--src/pulsecore/rtpoll.c222
-rw-r--r--src/pulsecore/rtpoll.h3
-rw-r--r--src/pulsecore/sample-util.c12
-rw-r--r--src/pulsecore/sample-util.h59
-rw-r--r--src/pulsecore/sconv-s16le.c10
-rw-r--r--src/pulsecore/sconv.c2
-rw-r--r--src/pulsecore/semaphore-posix.c22
-rw-r--r--src/pulsecore/semaphore.h15
-rw-r--r--src/pulsecore/shm.c57
-rw-r--r--src/pulsecore/sink-input.c216
-rw-r--r--src/pulsecore/sink-input.h33
-rw-r--r--src/pulsecore/sink.c729
-rw-r--r--src/pulsecore/sink.h76
-rw-r--r--src/pulsecore/sndfile-util.c462
-rw-r--r--src/pulsecore/sndfile-util.h52
-rw-r--r--src/pulsecore/socket-client.c22
-rw-r--r--src/pulsecore/socket-client.h2
-rw-r--r--src/pulsecore/socket-server.c23
-rw-r--r--src/pulsecore/sound-file-stream.c51
-rw-r--r--src/pulsecore/sound-file.c102
-rw-r--r--src/pulsecore/sound-file.h2
-rw-r--r--src/pulsecore/source-output.c114
-rw-r--r--src/pulsecore/source-output.h14
-rw-r--r--src/pulsecore/source.c376
-rw-r--r--src/pulsecore/source.h45
-rw-r--r--src/pulsecore/strbuf.c7
-rw-r--r--src/pulsecore/strbuf.h1
-rw-r--r--src/pulsecore/strlist.c12
-rw-r--r--src/pulsecore/strlist.h6
-rw-r--r--src/pulsecore/time-smoother.c79
-rw-r--r--src/pulsecore/time-smoother.h14
103 files changed, 6884 insertions, 1547 deletions
diff --git a/src/pulsecore/asyncq.c b/src/pulsecore/asyncq.c
index 67f661fe..072ef02c 100644
--- a/src/pulsecore/asyncq.c
+++ b/src/pulsecore/asyncq.c
@@ -131,7 +131,7 @@ void pa_asyncq_free(pa_asyncq *l, pa_free_cb_t free_cb) {
pa_xfree(l);
}
-static int push(pa_asyncq*l, void *p, pa_bool_t wait) {
+static int push(pa_asyncq*l, void *p, pa_bool_t wait_op) {
unsigned idx;
pa_atomic_ptr_t *cells;
@@ -145,7 +145,7 @@ static int push(pa_asyncq*l, void *p, pa_bool_t wait) {
if (!pa_atomic_ptr_cmpxchg(&cells[idx], NULL, p)) {
- if (!wait)
+ if (!wait_op)
return -1;
/* pa_log("sleeping on push"); */
@@ -163,14 +163,14 @@ static int push(pa_asyncq*l, void *p, pa_bool_t wait) {
return 0;
}
-static pa_bool_t flush_postq(pa_asyncq *l, pa_bool_t wait) {
+static pa_bool_t flush_postq(pa_asyncq *l, pa_bool_t wait_op) {
struct localq *q;
pa_assert(l);
while ((q = l->last_localq)) {
- if (push(l, q->data, wait) < 0)
+ if (push(l, q->data, wait_op) < 0)
return FALSE;
l->last_localq = q->prev;
@@ -184,13 +184,13 @@ static pa_bool_t flush_postq(pa_asyncq *l, pa_bool_t wait) {
return TRUE;
}
-int pa_asyncq_push(pa_asyncq*l, void *p, pa_bool_t wait) {
+int pa_asyncq_push(pa_asyncq*l, void *p, pa_bool_t wait_op) {
pa_assert(l);
- if (!flush_postq(l, wait))
+ if (!flush_postq(l, wait_op))
return -1;
- return push(l, p, wait);
+ return push(l, p, wait_op);
}
void pa_asyncq_post(pa_asyncq*l, void *p) {
@@ -221,7 +221,7 @@ void pa_asyncq_post(pa_asyncq*l, void *p) {
return;
}
-void* pa_asyncq_pop(pa_asyncq*l, pa_bool_t wait) {
+void* pa_asyncq_pop(pa_asyncq*l, pa_bool_t wait_op) {
unsigned idx;
void *ret;
pa_atomic_ptr_t *cells;
@@ -235,7 +235,7 @@ void* pa_asyncq_pop(pa_asyncq*l, pa_bool_t wait) {
if (!(ret = pa_atomic_ptr_load(&cells[idx]))) {
- if (!wait)
+ if (!wait_op)
return NULL;
/* pa_log("sleeping on pop"); */
diff --git a/src/pulsecore/aupdate.c b/src/pulsecore/aupdate.c
new file mode 100644
index 00000000..56ebb8e5
--- /dev/null
+++ b/src/pulsecore/aupdate.c
@@ -0,0 +1,129 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2009 Lennart Poettering
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/semaphore.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/mutex.h>
+
+#include "aupdate.h"
+
+#define MSB (1U << (sizeof(unsigned)*8U-1))
+#define WHICH(n) (!!((n) & MSB))
+#define COUNTER(n) ((n) & ~MSB)
+
+struct pa_aupdate {
+ pa_atomic_t read_lock;
+ pa_mutex *write_lock;
+ pa_semaphore *semaphore;
+};
+
+pa_aupdate *pa_aupdate_new(void) {
+ pa_aupdate *a;
+
+ a = pa_xnew(pa_aupdate, 1);
+ pa_atomic_store(&a->read_lock, 0);
+ a->write_lock = pa_mutex_new(FALSE, FALSE);
+ a->semaphore = pa_semaphore_new(0);
+
+ return a;
+}
+
+void pa_aupdate_free(pa_aupdate *a) {
+ pa_assert(a);
+
+ pa_mutex_free(a->write_lock);
+ pa_semaphore_free(a->semaphore);
+
+ pa_xfree(a);
+}
+
+unsigned pa_aupdate_read_begin(pa_aupdate *a) {
+ unsigned n;
+
+ pa_assert(a);
+
+ /* Increase the lock counter */
+ n = (unsigned) pa_atomic_inc(&a->read_lock);
+
+ /* When n is 0 we have about 2^31 threads running that all try to
+ * access the data at the same time, oh my! */
+ pa_assert(COUNTER(n)+1 > 0);
+
+ /* The uppermost bit tells us which data to look at */
+ return WHICH(n);
+}
+
+void pa_aupdate_read_end(pa_aupdate *a) {
+ unsigned n;
+
+ pa_assert(a);
+
+ /* Decrease the lock counter */
+ n = (unsigned) pa_atomic_dec(&a->read_lock);
+
+ /* Make sure the counter was valid */
+ pa_assert(COUNTER(n) > 0);
+
+ /* Post the semaphore */
+ pa_semaphore_post(a->semaphore);
+}
+
+unsigned pa_aupdate_write_begin(pa_aupdate *a) {
+ unsigned n;
+
+ pa_assert(a);
+
+ pa_mutex_lock(a->write_lock);
+
+ n = (unsigned) pa_atomic_load(&a->read_lock);
+
+ return !WHICH(n);
+}
+
+unsigned pa_aupdate_write_swap(pa_aupdate *a) {
+ unsigned n;
+
+ pa_assert(a);
+
+ for (;;) {
+ n = (unsigned) pa_atomic_load(&a->read_lock);
+
+ /* If the read counter is > 0 wait; if it is 0 try to swap the lists */
+ if (COUNTER(n) > 0)
+ pa_semaphore_wait(a->semaphore);
+ else if (pa_atomic_cmpxchg(&a->read_lock, (int) n, (int) (n ^ MSB)))
+ break;
+ }
+
+ return WHICH(n);
+}
+
+void pa_aupdate_write_end(pa_aupdate *a) {
+ pa_assert(a);
+
+ pa_mutex_unlock(a->write_lock);
+}
diff --git a/src/pulsecore/aupdate.h b/src/pulsecore/aupdate.h
new file mode 100644
index 00000000..072e382d
--- /dev/null
+++ b/src/pulsecore/aupdate.h
@@ -0,0 +1,98 @@
+#ifndef foopulsecoreaupdatehfoo
+#define foopulsecoreaupdatehfoo
+
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2009 Lennart Poettering
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+typedef struct pa_aupdate pa_aupdate;
+
+pa_aupdate *pa_aupdate_new(void);
+void pa_aupdate_free(pa_aupdate *a);
+
+/* Will return 0, or 1, depending on which copy of the data the caller
+ * should look at */
+unsigned pa_aupdate_read_begin(pa_aupdate *a);
+void pa_aupdate_read_end(pa_aupdate *a);
+
+/* Will return 0, or 1, depending which copy of the data the caller
+ * should modify */
+unsigned pa_aupdate_write_begin(pa_aupdate *a);
+void pa_aupdate_write_end(pa_aupdate *a);
+
+/* Will return 0, or 1, depending which copy of the data the caller
+ * should modify. Each time called this will return the opposite of
+ * the previous pa_aupdate_write_begin()/pa_aupdate_write_swap()
+ * call. Should only be called between pa_aupdate_write_begin() and
+ * pa_aupdate_write_end() */
+unsigned pa_aupdate_write_swap(pa_aupdate *a);
+
+/*
+ * This infrastructure allows lock-free updates of arbitrary data
+ * structures in an rcu'ish way: two copies of the data structure
+ * should be exisiting. One side ('the reader') has read access to one
+ * of the two data structure at a time. It does not have to lock it,
+ * however it needs to signal that it is using it/stopped using
+ * it. The other side ('the writer') modifes the second data structure,
+ * and then atomically swaps the two data structures, followed by a
+ * modification of the other one.
+ *
+ * This is intended to be used for cases where the reader side needs
+ * to be fast while the writer side can be slow.
+ *
+ * The reader side is signal handler safe.
+ *
+ * The writer side lock is not recursive. The reader side is.
+ *
+ * There may be multiple readers and multiple writers at the same
+ * time.
+ *
+ * Usage is like this:
+ *
+ * static struct foo bar[2];
+ * static pa_aupdate *a;
+ *
+ * reader() {
+ * unsigned j;
+ *
+ * j = pa_update_read_begin(a);
+ *
+ * ... read the data structure bar[j] ...
+ *
+ * pa_update_read_end(a);
+ * }
+ *
+ * writer() {
+ * unsigned j;
+ *
+ * j = pa_update_write_begin(a);
+ *
+ * ... update the data structure bar[j] ...
+ *
+ * j = pa_update_write_swap(a);
+ *
+ * ... update the data structure bar[j], the same way as above ...
+ *
+ * pa_update_write_end(a)
+ * }
+ *
+ */
+
+#endif
diff --git a/src/pulsecore/authkey.c b/src/pulsecore/authkey.c
index b122feee..1e31d076 100644
--- a/src/pulsecore/authkey.c
+++ b/src/pulsecore/authkey.c
@@ -190,7 +190,7 @@ int pa_authkey_load_auto(const char *fn, void *data, size_t length) {
return pa_authkey_load(p, data, length);
}
-/* Store the specified cookie in the speicified cookie file */
+/* Store the specified cookie in the specified cookie file */
int pa_authkey_save(const char *fn, const void *data, size_t length) {
int fd = -1;
int unlock = 0, ret = -1;
diff --git a/src/pulsecore/avahi-wrap.c b/src/pulsecore/avahi-wrap.c
index 56d9d3dd..f1f08bcc 100644
--- a/src/pulsecore/avahi-wrap.c
+++ b/src/pulsecore/avahi-wrap.c
@@ -23,6 +23,7 @@
#include <config.h>
#endif
+#include <pulse/timeval.h>
#include <pulse/xmalloc.h>
#include <pulsecore/log.h>
@@ -116,14 +117,13 @@ struct AvahiTimeout {
void *userdata;
};
-static void timeout_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *tv, void *userdata) {
- AvahiTimeout *t = userdata;
+static void timeout_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *t, void *userdata) {
+ AvahiTimeout *to = userdata;
pa_assert(a);
pa_assert(e);
- pa_assert(t);
- t->callback(t, t->userdata);
+ to->callback(to, to->userdata);
}
static AvahiTimeout* timeout_new(const AvahiPoll *api, const struct timeval *tv, AvahiTimeoutCallback callback, void *userdata) {
@@ -145,6 +145,7 @@ static AvahiTimeout* timeout_new(const AvahiPoll *api, const struct timeval *tv,
}
static void timeout_update(AvahiTimeout *t, const struct timeval *tv) {
+
pa_assert(t);
if (t->time_event && tv)
diff --git a/src/pulsecore/card.c b/src/pulsecore/card.c
index 6419c234..2f0a3af0 100644
--- a/src/pulsecore/card.c
+++ b/src/pulsecore/card.c
@@ -141,20 +141,19 @@ pa_card *pa_card_new(pa_core *core, pa_card_new_data *data) {
data->profiles = NULL;
c->active_profile = NULL;
+ c->save_profile = FALSE;
if (data->active_profile && c->profiles)
- c->active_profile = pa_hashmap_get(c->profiles, data->active_profile);
+ if ((c->active_profile = pa_hashmap_get(c->profiles, data->active_profile)))
+ c->save_profile = data->save_profile;
if (!c->active_profile && c->profiles) {
- void *state = NULL;
+ void *state;
pa_card_profile *p;
- while ((p = pa_hashmap_iterate(c->profiles, &state, NULL))) {
- if (!c->active_profile ||
- p->priority > c->active_profile->priority)
-
+ PA_HASHMAP_FOREACH(p, c->profiles, state)
+ if (!c->active_profile || p->priority > c->active_profile->priority)
c->active_profile = p;
- }
}
c->userdata = NULL;
@@ -162,6 +161,7 @@ pa_card *pa_card_new(pa_core *core, pa_card_new_data *data) {
pa_device_init_description(c->proplist);
pa_device_init_icon(c->proplist, TRUE);
+ pa_device_init_intended_roles(c->proplist);
pa_assert_se(pa_idxset_put(core->cards, c, &c->index) >= 0);
@@ -174,7 +174,6 @@ pa_card *pa_card_new(pa_core *core, pa_card_new_data *data) {
void pa_card_free(pa_card *c) {
pa_core *core;
- pa_card_profile *profile;
pa_assert(c);
pa_assert(c->core);
@@ -197,8 +196,10 @@ void pa_card_free(pa_card *c) {
pa_idxset_free(c->sources, NULL, NULL);
if (c->profiles) {
- while ((profile = pa_hashmap_steal_first(c->profiles)))
- pa_card_profile_free(profile);
+ pa_card_profile *p;
+
+ while ((p = pa_hashmap_steal_first(c->profiles)))
+ pa_card_profile_free(p);
pa_hashmap_free(c->profiles, NULL, NULL);
}
@@ -209,49 +210,62 @@ void pa_card_free(pa_card *c) {
pa_xfree(c);
}
-int pa_card_set_profile(pa_card *c, const char *name) {
+int pa_card_set_profile(pa_card *c, const char *name, pa_bool_t save) {
pa_card_profile *profile;
+ int r;
pa_assert(c);
if (!c->set_profile) {
- pa_log_warn("set_profile() operation not implemented for card %u \"%s\"", c->index, c->name);
- return -1;
+ pa_log_debug("set_profile() operation not implemented for card %u \"%s\"", c->index, c->name);
+ return -PA_ERR_NOTIMPLEMENTED;
}
if (!c->profiles)
- return -1;
+ return -PA_ERR_NOENTITY;
if (!(profile = pa_hashmap_get(c->profiles, name)))
- return -1;
+ return -PA_ERR_NOENTITY;
- if (c->active_profile == profile)
+ if (c->active_profile == profile) {
+ c->save_profile = c->save_profile || save;
return 0;
+ }
- if (c->set_profile(c, profile) < 0)
- return -1;
+ if ((r = c->set_profile(c, profile)) < 0)
+ return r;
pa_subscription_post(c->core, PA_SUBSCRIPTION_EVENT_CARD|PA_SUBSCRIPTION_EVENT_CHANGE, c->index);
pa_log_info("Changed profile of card %u \"%s\" to %s", c->index, c->name, profile->name);
c->active_profile = profile;
+ c->save_profile = save;
return 0;
}
-int pa_card_suspend(pa_card *c, pa_bool_t suspend) {
+int pa_card_suspend(pa_card *c, pa_bool_t suspend, pa_suspend_cause_t cause) {
pa_sink *sink;
pa_source *source;
uint32_t idx;
int ret = 0;
pa_assert(c);
+ pa_assert(cause != 0);
- for (sink = pa_idxset_first(c->sinks, &idx); sink; sink = pa_idxset_next(c->sinks, &idx))
- ret -= pa_sink_suspend(sink, suspend) < 0;
+ for (sink = pa_idxset_first(c->sinks, &idx); sink; sink = pa_idxset_next(c->sinks, &idx)) {
+ int r;
- for (source = pa_idxset_first(c->sources, &idx); source; source = pa_idxset_next(c->sources, &idx))
- ret -= pa_source_suspend(source, suspend) < 0;
+ if ((r = pa_sink_suspend(sink, suspend, cause)) < 0)
+ ret = r;
+ }
+
+ for (source = pa_idxset_first(c->sources, &idx); source; source = pa_idxset_next(c->sources, &idx)) {
+ int r;
+
+ if ((r = pa_source_suspend(source, suspend, cause)) < 0)
+ ret = r;
+ }
return ret;
}
diff --git a/src/pulsecore/card.h b/src/pulsecore/card.h
index c80d4e2e..2d691b67 100644
--- a/src/pulsecore/card.h
+++ b/src/pulsecore/card.h
@@ -63,6 +63,8 @@ struct pa_card {
pa_hashmap *profiles;
pa_card_profile *active_profile;
+ pa_bool_t save_profile:1;
+
void *userdata;
int (*set_profile)(pa_card *c, pa_card_profile *profile);
@@ -70,9 +72,8 @@ struct pa_card {
typedef struct pa_card_new_data {
char *name;
- char *description;
-
pa_proplist *proplist;
+
const char *driver;
pa_module *module;
@@ -80,6 +81,8 @@ typedef struct pa_card_new_data {
char *active_profile;
pa_bool_t namereg_fail:1;
+
+ pa_bool_t save_profile:1;
} pa_card_new_data;
pa_card_profile *pa_card_profile_new(const char *name, const char *description, size_t extra);
@@ -93,8 +96,8 @@ void pa_card_new_data_done(pa_card_new_data *data);
pa_card *pa_card_new(pa_core *c, pa_card_new_data *data);
void pa_card_free(pa_card *c);
-int pa_card_set_profile(pa_card *c, const char *name);
+int pa_card_set_profile(pa_card *c, const char *name, pa_bool_t save);
-int pa_card_suspend(pa_card *c, pa_bool_t suspend);
+int pa_card_suspend(pa_card *c, pa_bool_t suspend, pa_suspend_cause_t cause);
#endif
diff --git a/src/pulsecore/cli-command.c b/src/pulsecore/cli-command.c
index b5f7e7f5..e2c3c066 100644
--- a/src/pulsecore/cli-command.c
+++ b/src/pulsecore/cli-command.c
@@ -125,6 +125,8 @@ static int pa_cli_command_update_source_proplist(pa_core *c, pa_tokenizer *t, pa
static int pa_cli_command_update_sink_input_proplist(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
static int pa_cli_command_update_source_output_proplist(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
static int pa_cli_command_card_profile(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_sink_port(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_source_port(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
/* A method table for all available commands */
@@ -175,11 +177,13 @@ static const struct command commands[] = {
{ "suspend-sink", pa_cli_command_suspend_sink, "Suspend sink (args: index|name, bool)", 3},
{ "suspend-source", pa_cli_command_suspend_source, "Suspend source (args: index|name, bool)", 3},
{ "suspend", pa_cli_command_suspend, "Suspend all sinks and all sources (args: bool)", 2},
- { "set-card-profile", pa_cli_command_card_profile, "Change the profile of a card (aargs: index, name)", 3},
+ { "set-card-profile", pa_cli_command_card_profile, "Change the profile of a card (args: index, name)", 3},
+ { "set-sink-port", pa_cli_command_sink_port, "Change the port of a sink (args: index, name)", 3},
+ { "set-source-port", pa_cli_command_source_port, "Change the port of a source (args: index, name)", 3},
{ "set-log-level", pa_cli_command_log_level, "Change the log level (args: numeric level)", 2},
{ "set-log-meta", pa_cli_command_log_meta, "Show source code location in log messages (args: bool)", 2},
{ "set-log-time", pa_cli_command_log_time, "Show timestamps in log messages (args: bool)", 2},
- { "set-log-backtrace", pa_cli_command_log_backtrace, "Show bakctrace in log messages (args: frames)", 2},
+ { "set-log-backtrace", pa_cli_command_log_backtrace, "Show backtrace in log messages (args: frames)", 2},
{ NULL, NULL, NULL, 0 }
};
@@ -483,6 +487,8 @@ static int pa_cli_command_describe(pa_core *c, pa_tokenizer *t, pa_strbuf *buf,
if (i->usage)
pa_strbuf_printf(buf, "Usage: %s\n", i->usage);
pa_strbuf_printf(buf, "Load Once: %s\n", pa_yes_no(i->load_once));
+ if (i->deprecated)
+ pa_strbuf_printf(buf, "Warning, deprecated: %s\n", i->deprecated);
}
pa_modinfo_free(i);
@@ -524,7 +530,7 @@ static int pa_cli_command_sink_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *bu
}
pa_cvolume_set(&cvolume, sink->sample_spec.channels, volume);
- pa_sink_set_volume(sink, &cvolume, TRUE, TRUE);
+ pa_sink_set_volume(sink, &cvolume, TRUE, TRUE, TRUE, TRUE);
return 0;
}
@@ -566,7 +572,7 @@ static int pa_cli_command_sink_input_volume(pa_core *c, pa_tokenizer *t, pa_strb
}
pa_cvolume_set(&cvolume, si->sample_spec.channels, volume);
- pa_sink_input_set_volume(si, &cvolume, TRUE);
+ pa_sink_input_set_volume(si, &cvolume, TRUE, TRUE);
return 0;
}
@@ -602,7 +608,7 @@ static int pa_cli_command_source_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *
}
pa_cvolume_set(&cvolume, source->sample_spec.channels, volume);
- pa_source_set_volume(source, &cvolume);
+ pa_source_set_volume(source, &cvolume, TRUE);
return 0;
}
@@ -636,7 +642,7 @@ static int pa_cli_command_sink_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf,
return -1;
}
- pa_sink_set_mute(sink, mute);
+ pa_sink_set_mute(sink, mute, TRUE);
return 0;
}
@@ -670,7 +676,7 @@ static int pa_cli_command_source_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *bu
return -1;
}
- pa_source_set_mute(source, mute);
+ pa_source_set_mute(source, mute, TRUE);
return 0;
}
@@ -699,7 +705,10 @@ static int pa_cli_command_update_sink_proplist(pa_core *c, pa_tokenizer *t, pa_s
return -1;
}
- p = pa_proplist_from_string(s);
+ if (!(p = pa_proplist_from_string(s))) {
+ pa_strbuf_puts(buf, "Failed to parse proplist.\n");
+ return -1;
+ }
pa_sink_update_proplist(sink, PA_UPDATE_REPLACE, p);
@@ -733,7 +742,10 @@ static int pa_cli_command_update_source_proplist(pa_core *c, pa_tokenizer *t, pa
return -1;
}
- p = pa_proplist_from_string(s);
+ if (!(p = pa_proplist_from_string(s))) {
+ pa_strbuf_puts(buf, "Failed to parse proplist.\n");
+ return -1;
+ }
pa_source_update_proplist(source, PA_UPDATE_REPLACE, p);
@@ -773,7 +785,10 @@ static int pa_cli_command_update_sink_input_proplist(pa_core *c, pa_tokenizer *t
return -1;
}
- p = pa_proplist_from_string(s);
+ if (!(p = pa_proplist_from_string(s))) {
+ pa_strbuf_puts(buf, "Failed to parse proplist.\n");
+ return -1;
+ }
pa_sink_input_update_proplist(si, PA_UPDATE_REPLACE, p);
@@ -813,7 +828,10 @@ static int pa_cli_command_update_source_output_proplist(pa_core *c, pa_tokenizer
return -1;
}
- p = pa_proplist_from_string(s);
+ if (!(p = pa_proplist_from_string(s))) {
+ pa_strbuf_puts(buf, "Failed to parse proplist.\n");
+ return -1;
+ }
pa_source_output_update_proplist(so, PA_UPDATE_REPLACE, p);
@@ -1264,7 +1282,7 @@ static int pa_cli_command_suspend_sink(pa_core *c, pa_tokenizer *t, pa_strbuf *b
return -1;
}
- if ((r = pa_sink_suspend(sink, suspend)) < 0)
+ if ((r = pa_sink_suspend(sink, suspend, PA_SUSPEND_USER)) < 0)
pa_strbuf_printf(buf, "Failed to resume/suspend sink: %s\n", pa_strerror(r));
return 0;
@@ -1300,7 +1318,7 @@ static int pa_cli_command_suspend_source(pa_core *c, pa_tokenizer *t, pa_strbuf
return -1;
}
- if ((r = pa_source_suspend(source, suspend)) < 0)
+ if ((r = pa_source_suspend(source, suspend, PA_SUSPEND_USER)) < 0)
pa_strbuf_printf(buf, "Failed to resume/suspend source: %s\n", pa_strerror(r));
return 0;
@@ -1325,10 +1343,10 @@ static int pa_cli_command_suspend(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, p
return -1;
}
- if ((r = pa_sink_suspend_all(c, suspend)) < 0)
+ if ((r = pa_sink_suspend_all(c, suspend, PA_SUSPEND_USER)) < 0)
pa_strbuf_printf(buf, "Failed to resume/suspend all sinks: %s\n", pa_strerror(r));
- if ((r = pa_source_suspend_all(c, suspend)) < 0)
+ if ((r = pa_source_suspend_all(c, suspend, PA_SUSPEND_USER)) < 0)
pa_strbuf_printf(buf, "Failed to resume/suspend all sources: %s\n", pa_strerror(r));
return 0;
@@ -1454,7 +1472,7 @@ static int pa_cli_command_card_profile(pa_core *c, pa_tokenizer *t, pa_strbuf *b
return -1;
}
- if (pa_card_set_profile(card, p) < 0) {
+ if (pa_card_set_profile(card, p, TRUE) < 0) {
pa_strbuf_printf(buf, "Failed to set card profile to '%s'.\n", p);
return -1;
}
@@ -1462,6 +1480,70 @@ static int pa_cli_command_card_profile(pa_core *c, pa_tokenizer *t, pa_strbuf *b
return 0;
}
+static int pa_cli_command_sink_port(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
+ const char *n, *p;
+ pa_sink *sink;
+
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
+
+ if (!(n = pa_tokenizer_get(t, 1))) {
+ pa_strbuf_puts(buf, "You need to specify a sink either by its name or its index.\n");
+ return -1;
+ }
+
+ if (!(p = pa_tokenizer_get(t, 2))) {
+ pa_strbuf_puts(buf, "You need to specify a profile by its name.\n");
+ return -1;
+ }
+
+ if (!(sink = pa_namereg_get(c, n, PA_NAMEREG_SINK))) {
+ pa_strbuf_puts(buf, "No sink found by this name or index.\n");
+ return -1;
+ }
+
+ if (pa_sink_set_port(sink, p, TRUE) < 0) {
+ pa_strbuf_printf(buf, "Failed to set sink port to '%s'.\n", p);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int pa_cli_command_source_port(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
+ const char *n, *p;
+ pa_source *source;
+
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
+
+ if (!(n = pa_tokenizer_get(t, 1))) {
+ pa_strbuf_puts(buf, "You need to specify a source either by its name or its index.\n");
+ return -1;
+ }
+
+ if (!(p = pa_tokenizer_get(t, 2))) {
+ pa_strbuf_puts(buf, "You need to specify a profile by its name.\n");
+ return -1;
+ }
+
+ if (!(source = pa_namereg_get(c, n, PA_NAMEREG_SOURCE))) {
+ pa_strbuf_puts(buf, "No source found by this name or index.\n");
+ return -1;
+ }
+
+ if (pa_source_set_port(source, p, TRUE) < 0) {
+ pa_strbuf_printf(buf, "Failed to set source port to '%s'.\n", p);
+ return -1;
+ }
+
+ return 0;
+}
+
static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
pa_module *m;
pa_sink *sink;
@@ -1504,7 +1586,7 @@ static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_b
nl = 1;
}
- pa_strbuf_printf(buf, "set-sink-volume %s 0x%03x\n", sink->name, pa_cvolume_avg(pa_sink_get_volume(sink, FALSE)));
+ 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-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));
}
diff --git a/src/pulsecore/cli-text.c b/src/pulsecore/cli-text.c
index 76adc4dd..9395513d 100644
--- a/src/pulsecore/cli-text.c
+++ b/src/pulsecore/cli-text.c
@@ -139,11 +139,10 @@ char *pa_card_list_to_string(pa_core *c) {
if (card->profiles) {
pa_card_profile *p;
- void *state = NULL;
+ void *state;
pa_strbuf_puts(s, "\tprofiles:\n");
-
- while ((p = pa_hashmap_iterate(card->profiles, &state, NULL)))
+ PA_HASHMAP_FOREACH(p, card->profiles, state)
pa_strbuf_printf(s, "\t\t%s: %s (priority %u)\n", p->name, p->description, p->priority);
}
@@ -220,27 +219,25 @@ char *pa_sink_list_to_string(pa_core *c) {
v[PA_VOLUME_SNPRINT_MAX],
vdb[PA_SW_VOLUME_SNPRINT_DB_MAX],
cm[PA_CHANNEL_MAP_SNPRINT_MAX], *t;
- pa_usec_t min_latency, max_latency;
const char *cmn;
cmn = pa_channel_map_to_pretty_name(&sink->channel_map);
- pa_sink_get_latency_range(sink, &min_latency, &max_latency);
pa_strbuf_printf(
s,
" %c index: %u\n"
"\tname: <%s>\n"
"\tdriver: <%s>\n"
- "\tflags: %s%s%s%s%s%s%s\n"
+ "\tflags: %s%s%s%s%s%s%s%s\n"
"\tstate: %s\n"
+ "\tsuspend cause: %s%s%s%s\n"
"\tvolume: %s%s%s\n"
"\t balance %0.2f\n"
"\tbase volume: %s%s%s\n"
"\tvolume steps: %u\n"
"\tmuted: %s\n"
"\tcurrent latency: %0.2f ms\n"
- "\tconfigured latency: %0.2f ms; range is %0.2f .. %0.2f ms\n"
"\tmax request: %lu KiB\n"
"\tmax rewind: %lu KiB\n"
"\tmonitor source: %u\n"
@@ -258,21 +255,23 @@ char *pa_sink_list_to_string(pa_core *c) {
sink->flags & PA_SINK_HW_VOLUME_CTRL ? "HW_VOLUME_CTRL " : "",
sink->flags & PA_SINK_DECIBEL_VOLUME ? "DECIBEL_VOLUME " : "",
sink->flags & PA_SINK_LATENCY ? "LATENCY " : "",
- sink->flags & PA_SINK_FLAT_VOLUME ? "FLAT_VOLUME" : "",
+ sink->flags & PA_SINK_FLAT_VOLUME ? "FLAT_VOLUME " : "",
+ sink->flags & PA_SINK_DYNAMIC_LATENCY ? "DYNAMIC_LATENCY" : "",
sink_state_to_string(pa_sink_get_state(sink)),
- pa_cvolume_snprint(cv, sizeof(cv), pa_sink_get_volume(sink, FALSE)),
+ sink->suspend_cause & PA_SUSPEND_USER ? "USER " : "",
+ sink->suspend_cause & PA_SUSPEND_APPLICATION ? "APPLICATION " : "",
+ sink->suspend_cause & PA_SUSPEND_IDLE ? "IDLE " : "",
+ sink->suspend_cause & PA_SUSPEND_SESSION ? "SESSION" : "",
+ pa_cvolume_snprint(cv, sizeof(cv), pa_sink_get_volume(sink, FALSE, FALSE)),
sink->flags & PA_SINK_DECIBEL_VOLUME ? "\n\t " : "",
- sink->flags & PA_SINK_DECIBEL_VOLUME ? pa_sw_cvolume_snprint_dB(cvdb, sizeof(cvdb), pa_sink_get_volume(sink, FALSE)) : "",
- pa_cvolume_get_balance(pa_sink_get_volume(sink, FALSE), &sink->channel_map),
+ 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),
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) : "",
sink->n_volume_steps,
pa_yes_no(pa_sink_get_mute(sink, FALSE)),
(double) pa_sink_get_latency(sink) / (double) PA_USEC_PER_MSEC,
- (double) pa_sink_get_requested_latency(sink) / (double) PA_USEC_PER_MSEC,
- (double) min_latency / PA_USEC_PER_MSEC,
- (double) max_latency / PA_USEC_PER_MSEC,
(unsigned long) pa_sink_get_max_request(sink) / 1024,
(unsigned long) pa_sink_get_max_rewind(sink) / 1024,
sink->monitor_source ? sink->monitor_source->index : PA_INVALID_INDEX,
@@ -283,6 +282,22 @@ char *pa_sink_list_to_string(pa_core *c) {
pa_sink_used_by(sink),
pa_sink_linked_by(sink));
+ if (sink->flags & PA_SINK_DYNAMIC_LATENCY) {
+ pa_usec_t min_latency, max_latency;
+ pa_sink_get_latency_range(sink, &min_latency, &max_latency);
+
+ pa_strbuf_printf(
+ s,
+ "\tconfigured latency: %0.2f ms; range is %0.2f .. %0.2f ms\n",
+ (double) pa_sink_get_requested_latency(sink) / (double) PA_USEC_PER_MSEC,
+ (double) min_latency / PA_USEC_PER_MSEC,
+ (double) max_latency / PA_USEC_PER_MSEC);
+ } else
+ pa_strbuf_printf(
+ s,
+ "\tfixed latency: %0.2f ms\n",
+ (double) pa_sink_get_requested_latency(sink) / PA_USEC_PER_MSEC);
+
if (sink->card)
pa_strbuf_printf(s, "\tcard: %u <%s>\n", sink->card->index, sink->card->name);
if (sink->module)
@@ -291,6 +306,22 @@ char *pa_sink_list_to_string(pa_core *c) {
t = pa_proplist_to_string_sep(sink->proplist, "\n\t\t");
pa_strbuf_printf(s, "\tproperties:\n\t\t%s\n", t);
pa_xfree(t);
+
+ if (sink->ports) {
+ pa_device_port *p;
+ void *state;
+
+ pa_strbuf_puts(s, "\tports:\n");
+ PA_HASHMAP_FOREACH(p, sink->ports, state)
+ pa_strbuf_printf(s, "\t\t%s: %s (priority %u)\n", p->name, p->description, p->priority);
+ }
+
+
+ if (sink->active_port)
+ pa_strbuf_printf(
+ s,
+ "\tactive port: <%s>\n",
+ sink->active_port->name);
}
return pa_strbuf_tostring_free(s);
@@ -313,27 +344,24 @@ char *pa_source_list_to_string(pa_core *c) {
v[PA_VOLUME_SNPRINT_MAX],
vdb[PA_SW_VOLUME_SNPRINT_DB_MAX],
cm[PA_CHANNEL_MAP_SNPRINT_MAX], *t;
- pa_usec_t min_latency, max_latency;
const char *cmn;
cmn = pa_channel_map_to_pretty_name(&source->channel_map);
- pa_source_get_latency_range(source, &min_latency, &max_latency);
-
pa_strbuf_printf(
s,
" %c index: %u\n"
"\tname: <%s>\n"
"\tdriver: <%s>\n"
- "\tflags: %s%s%s%s%s%s\n"
+ "\tflags: %s%s%s%s%s%s%s\n"
"\tstate: %s\n"
+ "\tsuspend cause: %s%s%s%s\n"
"\tvolume: %s%s%s\n"
"\t balance %0.2f\n"
"\tbase volume: %s%s%s\n"
"\tvolume steps: %u\n"
"\tmuted: %s\n"
"\tcurrent latency: %0.2f ms\n"
- "\tconfigured latency: %0.2f ms; range is %0.2f .. %0.2f ms\n"
"\tmax rewind: %lu KiB\n"
"\tsample spec: %s\n"
"\tchannel map: %s%s%s\n"
@@ -349,7 +377,12 @@ char *pa_source_list_to_string(pa_core *c) {
source->flags & PA_SOURCE_HW_VOLUME_CTRL ? "HW_VOLUME_CTRL " : "",
source->flags & PA_SOURCE_DECIBEL_VOLUME ? "DECIBEL_VOLUME " : "",
source->flags & PA_SOURCE_LATENCY ? "LATENCY " : "",
+ source->flags & PA_SOURCE_DYNAMIC_LATENCY ? "DYNAMIC_LATENCY" : "",
source_state_to_string(pa_source_get_state(source)),
+ source->suspend_cause & PA_SUSPEND_USER ? "USER " : "",
+ source->suspend_cause & PA_SUSPEND_APPLICATION ? "APPLICATION " : "",
+ source->suspend_cause & PA_SUSPEND_IDLE ? "IDLE " : "",
+ source->suspend_cause & PA_SUSPEND_SESSION ? "SESSION" : "",
pa_cvolume_snprint(cv, sizeof(cv), pa_source_get_volume(source, FALSE)),
source->flags & PA_SOURCE_DECIBEL_VOLUME ? "\n\t " : "",
source->flags & PA_SOURCE_DECIBEL_VOLUME ? pa_sw_cvolume_snprint_dB(cvdb, sizeof(cvdb), pa_source_get_volume(source, FALSE)) : "",
@@ -360,9 +393,6 @@ char *pa_source_list_to_string(pa_core *c) {
source->n_volume_steps,
pa_yes_no(pa_source_get_mute(source, FALSE)),
(double) pa_source_get_latency(source) / PA_USEC_PER_MSEC,
- (double) pa_source_get_requested_latency(source) / PA_USEC_PER_MSEC,
- (double) min_latency / PA_USEC_PER_MSEC,
- (double) max_latency / PA_USEC_PER_MSEC,
(unsigned long) pa_source_get_max_rewind(source) / 1024,
pa_sample_spec_snprint(ss, sizeof(ss), &source->sample_spec),
pa_channel_map_snprint(cm, sizeof(cm), &source->channel_map),
@@ -371,6 +401,22 @@ char *pa_source_list_to_string(pa_core *c) {
pa_source_used_by(source),
pa_source_linked_by(source));
+ if (source->flags & PA_SOURCE_DYNAMIC_LATENCY) {
+ pa_usec_t min_latency, max_latency;
+ pa_source_get_latency_range(source, &min_latency, &max_latency);
+
+ pa_strbuf_printf(
+ s,
+ "\tconfigured latency: %0.2f ms; range is %0.2f .. %0.2f ms\n",
+ (double) pa_source_get_requested_latency(source) / PA_USEC_PER_MSEC,
+ (double) min_latency / PA_USEC_PER_MSEC,
+ (double) max_latency / PA_USEC_PER_MSEC);
+ } else
+ pa_strbuf_printf(
+ s,
+ "\tfixed latency: %0.2f ms\n",
+ (double) pa_source_get_requested_latency(source) / PA_USEC_PER_MSEC);
+
if (source->monitor_of)
pa_strbuf_printf(s, "\tmonitor_of: %u\n", source->monitor_of->index);
if (source->card)
@@ -381,6 +427,21 @@ char *pa_source_list_to_string(pa_core *c) {
t = pa_proplist_to_string_sep(source->proplist, "\n\t\t");
pa_strbuf_printf(s, "\tproperties:\n\t\t%s\n", t);
pa_xfree(t);
+
+ if (source->ports) {
+ pa_device_port *p;
+ void *state;
+
+ pa_strbuf_puts(s, "\tports:\n");
+ PA_HASHMAP_FOREACH(p, source->ports, state)
+ pa_strbuf_printf(s, "\t\t%s: %s (priority %u)\n", p->name, p->description, p->priority);
+ }
+
+ if (source->active_port)
+ pa_strbuf_printf(
+ s,
+ "\tactive port: <%s>\n",
+ source->active_port->name);
}
return pa_strbuf_tostring_free(s);
@@ -486,6 +547,9 @@ char *pa_sink_input_list_to_string(pa_core *c) {
char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cvdb[PA_SW_CVOLUME_SNPRINT_DB_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX], *t, clt[28];
pa_usec_t cl;
const char *cmn;
+ pa_cvolume v;
+
+ pa_sink_input_get_volume(i, &v, TRUE);
cmn = pa_channel_map_to_pretty_name(&i->channel_map);
@@ -526,9 +590,9 @@ char *pa_sink_input_list_to_string(pa_core *c) {
i->flags & PA_SINK_INPUT_FAIL_ON_SUSPEND ? "FAIL_ON_SUSPEND " : "",
state_table[pa_sink_input_get_state(i)],
i->sink->index, i->sink->name,
- pa_cvolume_snprint(cv, sizeof(cv), pa_sink_input_get_volume(i)),
- pa_sw_cvolume_snprint_dB(cvdb, sizeof(cvdb), pa_sink_input_get_volume(i)),
- pa_cvolume_get_balance(pa_sink_input_get_volume(i), &i->channel_map),
+ pa_cvolume_snprint(cv, sizeof(cv), &v),
+ pa_sw_cvolume_snprint_dB(cvdb, sizeof(cvdb), &v),
+ pa_cvolume_get_balance(&v, &i->channel_map),
pa_yes_no(pa_sink_input_get_mute(i)),
(double) pa_sink_input_get_latency(i, NULL) / PA_USEC_PER_MSEC,
clt,
diff --git a/src/pulsecore/cli.c b/src/pulsecore/cli.c
index a784f583..54514e7f 100644
--- a/src/pulsecore/cli.c
+++ b/src/pulsecore/cli.c
@@ -59,6 +59,8 @@ struct pa_cli {
pa_bool_t fail, kill_requested;
int defer_kill;
+
+ char *last_line;
};
static void line_callback(pa_ioline *line, const char *s, void *userdata);
@@ -101,6 +103,8 @@ pa_cli* pa_cli_new(pa_core *core, pa_iochannel *io, pa_module *m) {
c->fail = c->kill_requested = FALSE;
c->defer_kill = 0;
+ c->last_line = NULL;
+
return c;
}
@@ -110,6 +114,7 @@ void pa_cli_free(pa_cli *c) {
pa_ioline_close(c->line);
pa_ioline_unref(c->line);
pa_client_free(c->client);
+ pa_xfree(c->last_line);
pa_xfree(c);
}
@@ -144,6 +149,14 @@ static void line_callback(pa_ioline *line, const char *s, void *userdata) {
return;
}
+ /* Magic command, like they had in AT Hayes Modems! Those were the good days! */
+ if (pa_streq(s, "/"))
+ s = c->last_line;
+ else if (s[0]) {
+ pa_xfree(c->last_line);
+ c->last_line = pa_xstrdup(s);
+ }
+
pa_assert_se(buf = pa_strbuf_new());
c->defer_kill++;
pa_cli_command_execute_line(c->core, s, buf, &c->fail);
diff --git a/src/pulsecore/conf-parser.c b/src/pulsecore/conf-parser.c
index a6eb581c..2dc9a223 100644
--- a/src/pulsecore/conf-parser.c
+++ b/src/pulsecore/conf-parser.c
@@ -40,19 +40,35 @@
#define COMMENTS "#;\n"
/* Run the user supplied parser for an assignment */
-static int next_assignment(const char *filename, unsigned line, const char *section, const pa_config_item *t, const char *lvalue, const char *rvalue, void *userdata) {
+static int next_assignment(
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const pa_config_item *t,
+ const char *lvalue,
+ const char *rvalue,
+ void *userdata) {
+
pa_assert(filename);
pa_assert(t);
pa_assert(lvalue);
pa_assert(rvalue);
- for (; t->parse; t++)
- if (!t->lvalue ||
- (pa_streq(lvalue, t->lvalue) &&
- ((!section && !t->section) || pa_streq(section, t->section))))
- return t->parse(filename, line, section, lvalue, rvalue, t->data, userdata);
+ for (; t->parse; t++) {
+
+ if (t->lvalue && !pa_streq(lvalue, t->lvalue))
+ continue;
+
+ if (t->section && !section)
+ continue;
+
+ if (t->section && !pa_streq(section, t->section))
+ continue;
+
+ return t->parse(filename, line, section, lvalue, rvalue, t->data, userdata);
+ }
- pa_log("[%s:%u] Unknown lvalue '%s' in section '%s'.", filename, line, lvalue, pa_strnull(section));
+ pa_log("[%s:%u] Unknown lvalue '%s' in section '%s'.", filename, line, lvalue, pa_strna(section));
return -1;
}
@@ -96,6 +112,25 @@ static int parse_line(const char *filename, unsigned line, char **section, const
if (!*b)
return 0;
+ if (pa_startswith(b, ".include ")) {
+ char *path, *fn;
+ int r;
+
+ fn = strip(b+9);
+ if (!pa_is_path_absolute(fn)) {
+ const char *k;
+ if ((k = strrchr(filename, '/'))) {
+ char *dir = pa_xstrndup(filename, k-filename);
+ fn = path = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", dir, fn);
+ pa_xfree(dir);
+ }
+ }
+
+ r = pa_config_parse(fn, NULL, t, userdata);
+ pa_xfree(path);
+ return r;
+ }
+
if (*b == '[') {
size_t k;
@@ -135,6 +170,7 @@ int pa_config_parse(const char *filename, FILE *f, const pa_config_item *t, void
if (!f && !(f = fopen(filename, "r"))) {
if (errno == ENOENT) {
+ pa_log_debug("Failed to open configuration file '%s': %s", filename, pa_cstrerror(errno));
r = 0;
goto finish;
}
diff --git a/src/pulsecore/core-rtclock.c b/src/pulsecore/core-rtclock.c
new file mode 100644
index 00000000..3bc9e830
--- /dev/null
+++ b/src/pulsecore/core-rtclock.c
@@ -0,0 +1,199 @@
+/***
+ 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
+ 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 <stddef.h>
+#include <time.h>
+#include <sys/time.h>
+#include <errno.h>
+
+#ifdef HAVE_SYS_PRCTL_H
+#include <sys/prctl.h>
+#endif
+
+#include <pulse/timeval.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/core-error.h>
+
+#include "core-rtclock.h"
+
+pa_usec_t pa_rtclock_age(const struct timeval *tv) {
+ struct timeval now;
+ pa_assert(tv);
+
+ return pa_timeval_diff(pa_rtclock_get(&now), tv);
+}
+
+struct timeval *pa_rtclock_get(struct timeval *tv) {
+#ifdef HAVE_CLOCK_GETTIME
+ struct timespec ts;
+
+#ifdef CLOCK_MONOTONIC
+ /* No locking or atomic ops for no_monotonic here */
+ static pa_bool_t no_monotonic = FALSE;
+
+ if (!no_monotonic)
+ if (clock_gettime(CLOCK_MONOTONIC, &ts) < 0)
+ no_monotonic = TRUE;
+
+ if (no_monotonic)
+#endif
+ pa_assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0);
+
+ pa_assert(tv);
+
+ tv->tv_sec = ts.tv_sec;
+ tv->tv_usec = ts.tv_nsec / PA_NSEC_PER_USEC;
+
+ return tv;
+
+#else /* HAVE_CLOCK_GETTIME */
+
+ return pa_gettimeofday(tv);
+
+#endif
+}
+
+pa_bool_t pa_rtclock_hrtimer(void) {
+#ifdef 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
+
+ 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 */
+
+ return FALSE;
+
+#endif
+}
+
+#define TIMER_SLACK_NS (int) ((500 * PA_NSEC_PER_USEC))
+
+void pa_rtclock_hrtimer_enable(void) {
+#ifdef PR_SET_TIMERSLACK
+ int slack_ns;
+
+ if ((slack_ns = prctl(PR_GET_TIMERSLACK, 0, 0, 0, 0)) < 0) {
+ pa_log_info("PR_GET_TIMERSLACK/PR_SET_TIMERSLACK not supported.");
+ return;
+ }
+
+ pa_log_debug("Timer slack is set to %i us.", (int) (slack_ns/PA_NSEC_PER_USEC));
+
+ if (slack_ns > TIMER_SLACK_NS) {
+ slack_ns = TIMER_SLACK_NS;
+
+ pa_log_debug("Setting timer slack to %i us.", (int) (slack_ns/PA_NSEC_PER_USEC));
+
+ if (prctl(PR_SET_TIMERSLACK, slack_ns, 0, 0, 0) < 0) {
+ pa_log_warn("PR_SET_TIMERSLACK failed: %s", pa_cstrerror(errno));
+ return;
+ }
+ }
+
+#endif
+}
+
+struct timeval* pa_rtclock_from_wallclock(struct timeval *tv) {
+
+#ifdef HAVE_CLOCK_GETTIME
+ struct timeval wc_now, rt_now;
+
+ pa_gettimeofday(&wc_now);
+ pa_rtclock_get(&rt_now);
+
+ pa_assert(tv);
+
+ if (pa_timeval_cmp(&wc_now, tv) < 0)
+ pa_timeval_add(&rt_now, pa_timeval_diff(tv, &wc_now));
+ else
+ pa_timeval_sub(&rt_now, pa_timeval_diff(&wc_now, tv));
+
+ *tv = rt_now;
+#endif
+
+ return tv;
+}
+
+struct timespec *pa_timespec_store(struct timespec *ts, pa_usec_t v) {
+ pa_assert(ts);
+
+ 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;
+}
+
+pa_usec_t pa_timespec_load(const struct timespec *ts) {
+ pa_assert(ts);
+
+ return
+ (pa_usec_t) ts->tv_sec * PA_USEC_PER_SEC +
+ (pa_usec_t) ts->tv_nsec / PA_NSEC_PER_USEC;
+}
+
+
+static struct timeval* wallclock_from_rtclock(struct timeval *tv) {
+
+#ifdef HAVE_CLOCK_GETTIME
+ struct timeval wc_now, rt_now;
+
+ pa_gettimeofday(&wc_now);
+ pa_rtclock_get(&rt_now);
+
+ pa_assert(tv);
+
+ if (pa_timeval_cmp(&rt_now, tv) < 0)
+ pa_timeval_add(&wc_now, pa_timeval_diff(tv, &rt_now));
+ else
+ pa_timeval_sub(&wc_now, pa_timeval_diff(&rt_now, tv));
+
+ *tv = wc_now;
+#endif
+
+ return tv;
+}
+
+struct timeval* pa_timeval_rtstore(struct timeval *tv, pa_usec_t v, pa_bool_t rtclock) {
+ pa_assert(tv);
+
+ if (v == PA_USEC_INVALID)
+ return NULL;
+
+ pa_timeval_store(tv, v);
+
+ if (rtclock)
+ tv->tv_usec |= PA_TIMEVAL_RTCLOCK;
+ else
+ wallclock_from_rtclock(tv);
+
+ return tv;
+}
diff --git a/src/pulsecore/core-rtclock.h b/src/pulsecore/core-rtclock.h
new file mode 100644
index 00000000..152a3397
--- /dev/null
+++ b/src/pulsecore/core-rtclock.h
@@ -0,0 +1,51 @@
+#ifndef foopulsertclockhfoo
+#define foopulsertclockhfoo
+
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2004-2006 Lennart Poettering
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <pulsecore/macro.h>
+#include <pulse/sample.h>
+
+struct timeval;
+
+/* Something like pulse/timeval.h but based on CLOCK_MONOTONIC */
+
+struct timeval *pa_rtclock_get(struct timeval *ts);
+
+pa_usec_t pa_rtclock_age(const struct timeval *tv);
+pa_bool_t pa_rtclock_hrtimer(void);
+void pa_rtclock_hrtimer_enable(void);
+
+/* timer with a resolution better than this are considered high-resolution */
+#define PA_HRTIMER_THRESHOLD_USEC 10
+
+/* bit to set in tv.tv_usec to mark that the timeval is in monotonic time */
+#define PA_TIMEVAL_RTCLOCK ((time_t) (1LU << 30))
+
+struct timeval* pa_rtclock_from_wallclock(struct timeval *tv);
+
+struct timespec *pa_timespec_store(struct timespec *ts, pa_usec_t v);
+pa_usec_t pa_timespec_load(const struct timespec *ts);
+
+struct timeval* pa_timeval_rtstore(struct timeval *tv, pa_usec_t v, pa_bool_t rtclock);
+
+#endif
diff --git a/src/pulsecore/core-scache.c b/src/pulsecore/core-scache.c
index 34d60a8f..4c5a4b26 100644
--- a/src/pulsecore/core-scache.c
+++ b/src/pulsecore/core-scache.c
@@ -47,6 +47,7 @@
#include <pulse/util.h>
#include <pulse/volume.h>
#include <pulse/xmalloc.h>
+#include <pulse/rtclock.h>
#include <pulsecore/sink-input.h>
#include <pulsecore/sample-util.h>
@@ -54,6 +55,7 @@
#include <pulsecore/core-subscribe.h>
#include <pulsecore/namereg.h>
#include <pulsecore/sound-file.h>
+#include <pulsecore/core-rtclock.h>
#include <pulsecore/core-util.h>
#include <pulsecore/log.h>
#include <pulsecore/core-error.h>
@@ -61,11 +63,10 @@
#include "core-scache.h"
-#define UNLOAD_POLL_TIME 60
+#define UNLOAD_POLL_TIME (60 * PA_USEC_PER_SEC)
-static void timeout_callback(pa_mainloop_api *m, pa_time_event*e, const struct timeval *tv, void *userdata) {
+static void timeout_callback(pa_mainloop_api *m, pa_time_event *e, const struct timeval *t, void *userdata) {
pa_core *c = userdata;
- struct timeval ntv;
pa_assert(c);
pa_assert(c->mainloop == m);
@@ -73,9 +74,7 @@ static void timeout_callback(pa_mainloop_api *m, pa_time_event*e, const struct t
pa_scache_unload_unused(c);
- pa_gettimeofday(&ntv);
- ntv.tv_sec += UNLOAD_POLL_TIME;
- m->time_restart(e, &ntv);
+ pa_core_rttime_restart(c, e, pa_rtclock_now() + UNLOAD_POLL_TIME);
}
static void free_entry(pa_scache_entry *e) {
@@ -219,11 +218,14 @@ int pa_scache_add_file(pa_core *c, const char *name, const char *filename, uint3
pa_assert(name);
pa_assert(filename);
- if (pa_sound_file_load(c->mempool, filename, &ss, &map, &chunk) < 0)
- return -1;
-
p = pa_proplist_new();
pa_proplist_sets(p, PA_PROP_MEDIA_FILENAME, filename);
+
+ if (pa_sound_file_load(c->mempool, filename, &ss, &map, &chunk, p) < 0) {
+ pa_proplist_free(p);
+ return -1;
+ }
+
r = pa_scache_add_item(c, name, &ss, &map, &chunk, p, idx);
pa_memblock_unref(chunk.memblock);
pa_proplist_free(p);
@@ -253,12 +255,8 @@ int pa_scache_add_file_lazy(pa_core *c, const char *name, const char *filename,
pa_proplist_sets(e->proplist, PA_PROP_MEDIA_FILENAME, filename);
- if (!c->scache_auto_unload_event) {
- struct timeval ntv;
- pa_gettimeofday(&ntv);
- ntv.tv_sec += UNLOAD_POLL_TIME;
- c->scache_auto_unload_event = c->mainloop->time_new(c->mainloop, &ntv, timeout_callback, c);
- }
+ if (!c->scache_auto_unload_event)
+ c->scache_auto_unload_event = pa_core_rttime_new(c, pa_rtclock_now() + UNLOAD_POLL_TIME, timeout_callback, c);
if (idx)
*idx = e->index;
@@ -311,11 +309,14 @@ int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, pa_volume_t
if (!(e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE)))
return -1;
+ merged = pa_proplist_new();
+ pa_proplist_setf(merged, PA_PROP_MEDIA_NAME, "Sample %s", name);
+
if (e->lazy && !e->memchunk.memblock) {
pa_channel_map old_channel_map = e->channel_map;
- if (pa_sound_file_load(c->mempool, e->filename, &e->sample_spec, &e->channel_map, &e->memchunk) < 0)
- return -1;
+ if (pa_sound_file_load(c->mempool, e->filename, &e->sample_spec, &e->channel_map, &e->memchunk, merged) < 0)
+ goto fail;
pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE|PA_SUBSCRIPTION_EVENT_CHANGE, e->index);
@@ -328,7 +329,7 @@ int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, pa_volume_t
}
if (!e->memchunk.memblock)
- return -1;
+ goto fail;
pa_log_debug("Playing sample \"%s\" on \"%s\"", name, sink->name);
@@ -344,17 +345,13 @@ int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, pa_volume_t
else
pass_volume = FALSE;
- merged = pa_proplist_new();
- pa_proplist_setf(merged, PA_PROP_MEDIA_NAME, "Sample %s", name);
pa_proplist_update(merged, PA_UPDATE_REPLACE, e->proplist);
if (p)
pa_proplist_update(merged, PA_UPDATE_REPLACE, p);
- if (pa_play_memchunk(sink, &e->sample_spec, &e->channel_map, &e->memchunk, pass_volume ? &r : NULL, merged, sink_input_idx) < 0) {
- pa_proplist_free(merged);
- return -1;
- }
+ if (pa_play_memchunk(sink, &e->sample_spec, &e->channel_map, &e->memchunk, pass_volume ? &r : NULL, merged, sink_input_idx) < 0)
+ goto fail;
pa_proplist_free(merged);
@@ -362,6 +359,10 @@ int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, pa_volume_t
time(&e->last_used_time);
return 0;
+
+fail:
+ pa_proplist_free(merged);
+ return -1;
}
int pa_scache_play_item_by_name(pa_core *c, const char *name, const char*sink_name, pa_volume_t volume, pa_proplist *p, uint32_t *sink_input_idx) {
diff --git a/src/pulsecore/core-util.c b/src/pulsecore/core-util.c
index ba3a9b21..ec83fffd 100644
--- a/src/pulsecore/core-util.c
+++ b/src/pulsecore/core-util.c
@@ -43,6 +43,7 @@
#include <regex.h>
#include <langinfo.h>
#include <sys/utsname.h>
+#include <sys/socket.h>
#ifdef HAVE_STRTOF_L
#include <locale.h>
@@ -50,6 +51,10 @@
#ifdef HAVE_SCHED_H
#include <sched.h>
+
+#if defined(__linux__) && !defined(SCHED_RESET_ON_FORK)
+#define SCHED_RESET_ON_FORK 0x40000000
+#endif
#endif
#ifdef HAVE_SYS_RESOURCE_H
@@ -92,6 +97,10 @@
#include <xlocale.h>
#endif
+#ifdef HAVE_DBUS
+#include "rtkit.h"
+#endif
+
#include <pulse/xmalloc.h>
#include <pulse/util.h>
#include <pulse/utf8.h>
@@ -296,7 +305,15 @@ ssize_t pa_read(int fd, void *buf, size_t count, int *type) {
#endif
- return read(fd, buf, count);
+ for (;;) {
+ ssize_t r;
+
+ if ((r = read(fd, buf, count)) < 0)
+ if (errno == EINTR)
+ continue;
+
+ return r;
+ }
}
/** Similar to pa_read(), but handles writes */
@@ -305,8 +322,17 @@ ssize_t pa_write(int fd, const void *buf, size_t count, int *type) {
if (!type || *type == 0) {
ssize_t r;
- if ((r = send(fd, buf, count, MSG_NOSIGNAL)) >= 0)
+ for (;;) {
+ if ((r = send(fd, buf, count, MSG_NOSIGNAL)) < 0) {
+
+ if (errno == EINTR)
+ continue;
+
+ break;
+ }
+
return r;
+ }
#ifdef OS_IS_WIN32
if (WSAGetLastError() != WSAENOTSOCK) {
@@ -322,11 +348,19 @@ ssize_t pa_write(int fd, const void *buf, size_t count, int *type) {
*type = 1;
}
- return write(fd, buf, count);
+ for (;;) {
+ ssize_t r;
+
+ if ((r = write(fd, buf, count)) < 0)
+ if (errno == EINTR)
+ continue;
+
+ return r;
+ }
}
/** Calls read() in a loop. Makes sure that as much as 'size' bytes,
- * unless EOF is reached or an error occured */
+ * unless EOF is reached or an error occurred */
ssize_t pa_loop_read(int fd, void*data, size_t size, int *type) {
ssize_t ret = 0;
int _type;
@@ -407,11 +441,11 @@ int pa_close(int fd) {
for (;;) {
int r;
- if ((r = close(fd)) >= 0)
- return r;
+ if ((r = close(fd)) < 0)
+ if (errno == EINTR)
+ continue;
- if (errno != EINTR)
- return r;
+ return r;
}
}
@@ -527,127 +561,121 @@ char *pa_strlcpy(char *b, const char *s, size_t l) {
return b;
}
-/* Make the current thread a realtime thread, and acquire the highest
- * rtprio we can get that is less or equal the specified parameter. If
- * the thread is already realtime, don't do anything. */
-int pa_make_realtime(int rtprio) {
-
-#ifdef _POSIX_PRIORITY_SCHEDULING
+static int set_scheduler(int rtprio) {
struct sched_param sp;
- int r, policy;
+ int r;
+#ifdef HAVE_DBUS
+ DBusError error;
+ DBusConnection *bus;
- memset(&sp, 0, sizeof(sp));
- policy = 0;
+ dbus_error_init(&error);
+#endif
- if ((r = pthread_getschedparam(pthread_self(), &policy, &sp)) != 0) {
- pa_log("pthread_getschedgetparam(): %s", pa_cstrerror(r));
- return -1;
+ pa_zero(sp);
+ sp.sched_priority = rtprio;
+
+#ifdef SCHED_RESET_ON_FORK
+ if ((r = 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 (policy == SCHED_FIFO && sp.sched_priority >= rtprio) {
- pa_log_info("Thread already being scheduled with SCHED_FIFO with priority %i.", sp.sched_priority);
+ if ((r = pthread_setschedparam(pthread_self(), SCHED_RR, &sp)) == 0) {
+ pa_log_debug("SCHED_RR worked.");
return 0;
}
- sp.sched_priority = rtprio;
- if ((r = pthread_setschedparam(pthread_self(), SCHED_FIFO, &sp)) != 0) {
+#ifdef HAVE_DBUS
+ /* Try to talk to RealtimeKit */
- while (sp.sched_priority > 1) {
- sp.sched_priority --;
+ if (!(bus = dbus_bus_get(DBUS_BUS_SYSTEM, &error))) {
+ pa_log("Failed to connect to system bus: %s\n", error.message);
+ dbus_error_free(&error);
+ errno = -EIO;
+ return -1;
+ }
- if ((r = pthread_setschedparam(pthread_self(), SCHED_FIFO, &sp)) == 0) {
- pa_log_info("Successfully enabled SCHED_FIFO scheduling for thread, with priority %i, which is lower than the requested %i.", sp.sched_priority, rtprio);
- return 0;
- }
- }
+ r = rtkit_make_realtime(bus, 0, rtprio);
+ dbus_connection_unref(bus);
- pa_log_warn("pthread_setschedparam(): %s", pa_cstrerror(r));
- return -1;
+ if (r >= 0) {
+ pa_log_debug("RealtimeKit worked.");
+ return 0;
}
- pa_log_info("Successfully enabled SCHED_FIFO scheduling for thread, with priority %i.", sp.sched_priority);
- return 0;
+ errno = -r;
#else
+ errno = r;
+#endif
- errno = ENOTSUP;
return -1;
-#endif
}
-/* This is merely used for giving the user a hint. This is not correct
- * for anything security related */
-pa_bool_t pa_can_realtime(void) {
-
- if (geteuid() == 0)
- return TRUE;
+/* Make the current thread a realtime thread, and acquire the highest
+ * rtprio we can get that is less or equal the specified parameter. If
+ * the thread is already realtime, don't do anything. */
+int pa_make_realtime(int rtprio) {
-#if defined(HAVE_SYS_RESOURCE_H) && defined(RLIMIT_RTPRIO)
- {
- struct rlimit rl;
+#ifdef _POSIX_PRIORITY_SCHEDULING
+ int p;
- if (getrlimit(RLIMIT_RTPRIO, &rl) >= 0)
- if (rl.rlim_cur > 0 || rl.rlim_cur == RLIM_INFINITY)
- return TRUE;
+ if (set_scheduler(rtprio) >= 0) {
+ pa_log_info("Successfully enabled SCHED_RR scheduling for thread, with priority %i.", rtprio);
+ return 0;
}
-#endif
-
-#if defined(HAVE_SYS_CAPABILITY_H) && defined(CAP_SYS_NICE)
- {
- cap_t cap;
- if ((cap = cap_get_proc())) {
- cap_flag_value_t flag = CAP_CLEAR;
+ for (p = rtprio-1; p >= 1; p--)
+ if (set_scheduler(p)) {
+ pa_log_info("Successfully enabled SCHED_RR scheduling for thread, with priority %i, which is lower than the requested %i.", p, rtprio);
+ return 0;
+ }
- if (cap_get_flag(cap, CAP_SYS_NICE, CAP_EFFECTIVE, &flag) >= 0)
- if (flag == CAP_SET) {
- cap_free(cap);
- return TRUE;
- }
+ pa_log_info("Failed to acquire real-time scheduling: %s", pa_cstrerror(errno));
+ return -1;
+#else
- cap_free(cap);
- }
- }
+ errno = ENOTSUP;
+ return -1;
#endif
-
- return FALSE;
}
-/* This is merely used for giving the user a hint. This is not correct
- * for anything security related */
-pa_bool_t pa_can_high_priority(void) {
-
- if (geteuid() == 0)
- return TRUE;
+static int set_nice(int nice_level) {
+#ifdef HAVE_DBUS
+ DBusError error;
+ DBusConnection *bus;
+ int r;
-#if defined(HAVE_SYS_RESOURCE_H) && defined(RLIMIT_RTPRIO)
- {
- struct rlimit rl;
+ dbus_error_init(&error);
+#endif
- if (getrlimit(RLIMIT_NICE, &rl) >= 0)
- if (rl.rlim_cur >= 21 || rl.rlim_cur == RLIM_INFINITY)
- return TRUE;
+ if (setpriority(PRIO_PROCESS, 0, nice_level) >= 0) {
+ pa_log_debug("setpriority() worked.");
+ return 0;
}
-#endif
-#if defined(HAVE_SYS_CAPABILITY_H) && defined(CAP_SYS_NICE)
- {
- cap_t cap;
+#ifdef HAVE_DBUS
+ /* Try to talk to RealtimeKit */
- if ((cap = cap_get_proc())) {
- cap_flag_value_t flag = CAP_CLEAR;
+ if (!(bus = dbus_bus_get(DBUS_BUS_SYSTEM, &error))) {
+ pa_log("Failed to connect to system bus: %s\n", error.message);
+ dbus_error_free(&error);
+ errno = -EIO;
+ return -1;
+ }
- if (cap_get_flag(cap, CAP_SYS_NICE, CAP_EFFECTIVE, &flag) >= 0)
- if (flag == CAP_SET) {
- cap_free(cap);
- return TRUE;
- }
+ r = rtkit_make_high_priority(bus, 0, nice_level);
+ dbus_connection_unref(bus);
- cap_free(cap);
- }
+ if (r >= 0) {
+ pa_log_debug("RealtimeKit worked.");
+ return 0;
}
+
+ errno = -r;
#endif
- return FALSE;
+ return -1;
}
/* Raise the priority of the current process as much as possible that
@@ -655,22 +683,21 @@ pa_bool_t pa_can_high_priority(void) {
int pa_raise_priority(int nice_level) {
#ifdef HAVE_SYS_RESOURCE_H
- if (setpriority(PRIO_PROCESS, 0, nice_level) < 0) {
- int n;
+ int n;
- for (n = nice_level+1; n < 0; n++) {
+ if (set_nice(nice_level) >= 0) {
+ pa_log_info("Successfully gained nice level %i.", nice_level);
+ return 0;
+ }
- if (setpriority(PRIO_PROCESS, 0, n) == 0) {
- pa_log_info("Successfully acquired nice level %i, which is lower than the requested %i.", n, nice_level);
- return 0;
- }
+ for (n = nice_level+1; n < 0; n++)
+ if (set_nice(n) > 0) {
+ pa_log_info("Successfully acquired nice level %i, which is lower than the requested %i.", n, nice_level);
+ return 0;
}
- pa_log_warn("setpriority(): %s", pa_cstrerror(errno));
- return -1;
- }
-
- pa_log_info("Successfully gained nice level %i.", nice_level);
+ pa_log_info("Failed to acquire high-priority scheduling: %s", pa_cstrerror(errno));
+ return -1;
#endif
#ifdef OS_IS_WIN32
@@ -678,9 +705,10 @@ int pa_raise_priority(int nice_level) {
if (!SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS)) {
pa_log_warn("SetPriorityClass() failed: 0x%08X", GetLastError());
errno = EPERM;
- return .-1;
- } else
- pa_log_info("Successfully gained high priority class.");
+ return -1;
+ }
+
+ pa_log_info("Successfully gained high priority class.");
}
#endif
@@ -695,8 +723,8 @@ void pa_reset_priority(void) {
setpriority(PRIO_PROCESS, 0, 0);
- memset(&sp, 0, sizeof(sp));
- pa_assert_se(pthread_setschedparam(pthread_self(), SCHED_OTHER, &sp) == 0);
+ pa_zero(sp);
+ pthread_setschedparam(pthread_self(), SCHED_OTHER, &sp);
#endif
#ifdef OS_IS_WIN32
@@ -1166,22 +1194,22 @@ int pa_check_in_group(gid_t g) {
(advisory on UNIX, mandatory on Windows) */
int pa_lock_fd(int fd, int b) {
#ifdef F_SETLKW
- struct flock flock;
+ struct flock f_lock;
/* Try a R/W lock first */
- flock.l_type = (short) (b ? F_WRLCK : F_UNLCK);
- flock.l_whence = SEEK_SET;
- flock.l_start = 0;
- flock.l_len = 0;
+ f_lock.l_type = (short) (b ? F_WRLCK : F_UNLCK);
+ f_lock.l_whence = SEEK_SET;
+ f_lock.l_start = 0;
+ f_lock.l_len = 0;
- if (fcntl(fd, F_SETLKW, &flock) >= 0)
+ if (fcntl(fd, F_SETLKW, &f_lock) >= 0)
return 0;
/* Perhaps the file descriptor qas opened for read only, than try again with a read lock. */
if (b && errno == EBADF) {
- flock.l_type = F_RDLCK;
- if (fcntl(fd, F_SETLKW, &flock) >= 0)
+ f_lock.l_type = F_RDLCK;
+ if (fcntl(fd, F_SETLKW, &f_lock) >= 0)
return 0;
}
@@ -1242,7 +1270,7 @@ int pa_lock_lockfile(const char *fn) {
goto fail;
}
- /* Check wheter the file has been removed meanwhile. When yes,
+ /* Check whether the file has been removed meanwhile. When yes,
* restart this loop, otherwise, we're done */
if (st.st_nlink >= 1)
break;
@@ -2210,11 +2238,10 @@ int pa_close_all(int except_fd, ...) {
int pa_close_allv(const int except_fds[]) {
struct rlimit rl;
- int fd;
- int saved_errno;
+ int maxfd, fd;
#ifdef __linux__
-
+ int saved_errno;
DIR *d;
if ((d = opendir("/proc/self/fd"))) {
@@ -2277,10 +2304,12 @@ int pa_close_allv(const int except_fds[]) {
#endif
- if (getrlimit(RLIMIT_NOFILE, &rl) < 0)
- return -1;
+ if (getrlimit(RLIMIT_NOFILE, &rl) >= 0)
+ maxfd = (int) rl.rlim_max;
+ else
+ maxfd = sysconf(_SC_OPEN_MAX);
- for (fd = 3; fd < (int) rl.rlim_max; fd++) {
+ for (fd = 3; fd < maxfd; fd++) {
int i;
pa_bool_t found;
@@ -2442,31 +2471,29 @@ pa_bool_t pa_in_system_mode(void) {
return !!atoi(e);
}
-char *pa_machine_id(void) {
- FILE *f;
- size_t l;
-
- /* The returned value is supposed be some kind of ascii identifier
- * that is unique and stable across reboots. */
-
- /* First we try the D-Bus UUID, which is the best option we have,
- * since it fits perfectly our needs and is not as volatile as the
- * hostname which might be set from dhcp. */
+char *pa_get_user_name_malloc(void) {
+ ssize_t k;
+ char *u;
- if ((f = fopen(PA_MACHINE_ID, "r"))) {
- char ln[34] = "", *r;
+#ifdef _SC_LOGIN_NAME_MAX
+ k = (ssize_t) sysconf(_SC_LOGIN_NAME_MAX);
- r = fgets(ln, sizeof(ln)-1, f);
- fclose(f);
+ if (k <= 0)
+#endif
+ k = 32;
- pa_strip_nl(ln);
+ u = pa_xnew(char, k+1);
- if (r && ln[0])
- return pa_xstrdup(ln);
+ if (!(pa_get_user_name(u, k))) {
+ pa_xfree(u);
+ return NULL;
}
- /* The we fall back to the host name. It supposed to be somewhat
- * unique, at least in a network, but may change. */
+ return u;
+}
+
+char *pa_get_host_name_malloc(void) {
+ size_t l;
l = 100;
for (;;) {
@@ -2480,13 +2507,16 @@ char *pa_machine_id(void) {
break;
} else if (strlen(c) < l-1) {
+ char *u;
if (*c == 0) {
pa_xfree(c);
break;
}
- return c;
+ u = pa_utf8_filter(c);
+ pa_xfree(c);
+ return u;
}
/* Hmm, the hostname is as long the space we offered the
@@ -2497,11 +2527,49 @@ char *pa_machine_id(void) {
l *= 2;
}
+ return NULL;
+}
+
+char *pa_machine_id(void) {
+ FILE *f;
+ char *h;
+
+ /* The returned value is supposed be some kind of ascii identifier
+ * that is unique and stable across reboots. */
+
+ /* First we try the D-Bus UUID, which is the best option we have,
+ * since it fits perfectly our needs and is not as volatile as the
+ * hostname which might be set from dhcp. */
+
+ if ((f = fopen(PA_MACHINE_ID, "r"))) {
+ char ln[34] = "", *r;
+
+ r = fgets(ln, sizeof(ln)-1, f);
+ fclose(f);
+
+ pa_strip_nl(ln);
+
+ if (r && ln[0])
+ return pa_utf8_filter(ln);
+ }
+
+ if ((h = pa_get_host_name_malloc()))
+ return h;
+
/* If no hostname was set we use the POSIX hostid. It's usually
- * the IPv4 address. Mit not be that stable. */
+ * the IPv4 address. Might not be that stable. */
return pa_sprintf_malloc("%08lx", (unsigned long) gethostid);
}
+char *pa_session_id(void) {
+ const char *e;
+
+ if (!(e = getenv("XDG_SESSION_COOKIE")))
+ return NULL;
+
+ return pa_utf8_filter(e);
+}
+
char *pa_uname_string(void) {
struct utsname u;
@@ -2608,7 +2676,7 @@ char *pa_unescape(char *p) {
}
char *pa_realpath(const char *path) {
- char *r, *t;
+ char *t;
pa_assert(path);
/* We want only abolsute paths */
@@ -2617,18 +2685,89 @@ char *pa_realpath(const char *path) {
return NULL;
}
-#ifndef __GLIBC__
+#if defined(__GLIBC__) || defined(__APPLE__)
+ {
+ char *r;
+
+ if (!(r = realpath(path, NULL)))
+ return NULL;
+
+ /* We copy this here in case our pa_xmalloc() is not
+ * implemented on top of libc malloc() */
+ t = pa_xstrdup(r);
+ pa_xfree(r);
+ }
+#elif defined(PATH_MAX)
+ {
+ char *path_buf;
+ path_buf = pa_xmalloc(PATH_MAX);
+
+ if (!(t = realpath(path, path_buf))) {
+ pa_xfree(path_buf);
+ return NULL;
+ }
+ }
+#else
#error "It's not clear whether this system supports realpath(..., NULL) like GNU libc does. If it doesn't we need a private version of realpath() here."
#endif
- if (!(r = realpath(path, NULL)))
- return NULL;
+ return t;
+}
+
+void pa_disable_sigpipe(void) {
+
+#ifdef SIGPIPE
+ struct sigaction sa;
+
+ pa_zero(sa);
+
+ if (sigaction(SIGPIPE, NULL, &sa) < 0) {
+ pa_log("sigaction(): %s", pa_cstrerror(errno));
+ return;
+ }
- /* We copy this here in case our pa_xmalloc() is not implemented
- * on top of libc malloc() */
- t = pa_xstrdup(r);
- pa_xfree(r);
+ sa.sa_handler = SIG_IGN;
+ if (sigaction(SIGPIPE, &sa, NULL) < 0) {
+ pa_log("sigaction(): %s", pa_cstrerror(errno));
+ return;
+ }
+#endif
+}
+
+void pa_xfreev(void**a) {
+ void **p;
+
+ if (!a)
+ return;
+
+ for (p = a; *p; p++)
+ pa_xfree(*p);
+
+ pa_xfree(a);
+}
+
+char **pa_split_spaces_strv(const char *s) {
+ char **t, *e;
+ unsigned i = 0, n = 8;
+ const char *state = NULL;
+
+ t = pa_xnew(char*, n);
+ while ((e = pa_split_spaces(s, &state))) {
+ t[i++] = e;
+
+ if (i >= n) {
+ n *= 2;
+ t = pa_xrenew(char*, t, n);
+ }
+ }
+
+ if (i <= 0) {
+ pa_xfree(t);
+ return NULL;
+ }
+
+ t[i] = NULL;
return t;
}
@@ -2652,3 +2791,12 @@ pa_bool_t pa_linux_newer_than(unsigned major, unsigned minor, unsigned micro) {
return FALSE;
}
+
+char* pa_maybe_prefix_path(const char *path, const char *prefix) {
+ pa_assert(path);
+
+ if (pa_is_path_absolute(path))
+ return pa_xstrdup(path);
+
+ return pa_sprintf_malloc("%s" PA_PATH_SEP "%s", prefix, path);
+}
diff --git a/src/pulsecore/core-util.h b/src/pulsecore/core-util.h
index 9ab84b4c..3a1586c9 100644
--- a/src/pulsecore/core-util.h
+++ b/src/pulsecore/core-util.h
@@ -80,9 +80,6 @@ int pa_make_realtime(int rtprio);
int pa_raise_priority(int nice_level);
void pa_reset_priority(void);
-pa_bool_t pa_can_realtime(void);
-pa_bool_t pa_can_high_priority(void);
-
int pa_parse_boolean(const char *s) PA_GCC_PURE;
static inline const char *pa_yes_no(pa_bool_t b) {
@@ -201,7 +198,11 @@ pa_bool_t pa_in_system_mode(void);
#define pa_streq(a,b) (!strcmp((a),(b)))
+char *pa_get_host_name_malloc(void);
+char *pa_get_user_name_malloc(void);
+
char *pa_machine_id(void);
+char *pa_session_id(void);
char *pa_uname_string(void);
#ifdef HAVE_VALGRIND_MEMCHECK_H
@@ -225,4 +226,16 @@ char *pa_realpath(const char *path);
pa_bool_t pa_linux_newer_than(unsigned major, unsigned minor, unsigned micro);
+void pa_disable_sigpipe(void);
+
+void pa_xfreev(void**a);
+
+static inline void pa_xstrfreev(char **a) {
+ pa_xfreev((void**) a);
+}
+
+char **pa_split_spaces_strv(const char *s);
+
+char* pa_maybe_prefix_path(const char *path, const char *prefix);
+
#endif
diff --git a/src/pulsecore/core.c b/src/pulsecore/core.c
index 06573f17..f5eb8352 100644
--- a/src/pulsecore/core.c
+++ b/src/pulsecore/core.c
@@ -28,6 +28,7 @@
#include <stdio.h>
#include <signal.h>
+#include <pulse/rtclock.h>
#include <pulse/timeval.h>
#include <pulse/xmalloc.h>
@@ -35,6 +36,7 @@
#include <pulsecore/sink.h>
#include <pulsecore/source.h>
#include <pulsecore/namereg.h>
+#include <pulsecore/core-rtclock.h>
#include <pulsecore/core-util.h>
#include <pulsecore/core-scache.h>
#include <pulsecore/core-subscribe.h>
@@ -214,7 +216,7 @@ static void core_free(pa_object *o) {
pa_xfree(c);
}
-static void exit_callback(pa_mainloop_api*m, pa_time_event *e, const struct timeval *tv, void *userdata) {
+static void exit_callback(pa_mainloop_api *m, pa_time_event *e, const struct timeval *t, void *userdata) {
pa_core *c = userdata;
pa_assert(c->exit_event == e);
@@ -229,11 +231,7 @@ void pa_core_check_idle(pa_core *c) {
c->exit_idle_time >= 0 &&
pa_idxset_size(c->clients) == 0) {
- struct timeval tv;
- pa_gettimeofday(&tv);
- tv.tv_sec+= c->exit_idle_time;
-
- c->exit_event = c->mainloop->time_new(c->mainloop, &tv, exit_callback, c);
+ c->exit_event = pa_core_rttime_new(c, pa_rtclock_now() + c->exit_idle_time * PA_USEC_PER_SEC, exit_callback, c);
} else if (c->exit_event && pa_idxset_size(c->clients) > 0) {
c->mainloop->time_free(c->exit_event);
@@ -261,3 +259,21 @@ void pa_core_maybe_vacuum(pa_core *c) {
pa_log_debug("Hmm, no streams around, trying to vacuum.");
pa_mempool_vacuum(c->mempool);
}
+
+pa_time_event* pa_core_rttime_new(pa_core *c, pa_usec_t usec, pa_time_event_cb_t cb, void *userdata) {
+ struct timeval tv;
+
+ pa_assert(c);
+ pa_assert(c->mainloop);
+
+ return c->mainloop->time_new(c->mainloop, pa_timeval_rtstore(&tv, usec, TRUE), cb, userdata);
+}
+
+void pa_core_rttime_restart(pa_core *c, pa_time_event *e, pa_usec_t usec) {
+ struct timeval tv;
+
+ pa_assert(c);
+ pa_assert(c->mainloop);
+
+ c->mainloop->time_restart(e, pa_timeval_rtstore(&tv, usec, TRUE));
+}
diff --git a/src/pulsecore/core.h b/src/pulsecore/core.h
index c6794445..e7abd61b 100644
--- a/src/pulsecore/core.h
+++ b/src/pulsecore/core.h
@@ -27,6 +27,16 @@
typedef struct pa_core pa_core;
+/* This is a bitmask that encodes the cause why a sink/source is
+ * suspended. */
+typedef enum pa_suspend_cause {
+ PA_SUSPEND_USER = 1, /* Exposed to the user via some protocol */
+ PA_SUSPEND_APPLICATION = 2, /* Used by the device reservation logic */
+ PA_SUSPEND_IDLE = 4, /* Used by module-suspend-on-idle */
+ PA_SUSPEND_SESSION = 8, /* Used by module-hal for mark inactive sessions */
+ PA_SUSPEND_ALL = 0xFFFF /* Magic cause that can be used to resume forcibly */
+} pa_suspend_cause_t;
+
#include <pulsecore/idxset.h>
#include <pulsecore/hashmap.h>
#include <pulsecore/memblock.h>
@@ -173,4 +183,8 @@ int pa_core_exit(pa_core *c, pa_bool_t force, int retval);
void pa_core_maybe_vacuum(pa_core *c);
+/* wrapper for c->mainloop->time_*() RT time events */
+pa_time_event* pa_core_rttime_new(pa_core *c, pa_usec_t usec, pa_time_event_cb_t cb, void *userdata);
+void pa_core_rttime_restart(pa_core *c, pa_time_event *e, pa_usec_t usec);
+
#endif
diff --git a/src/pulsecore/database-gdbm.c b/src/pulsecore/database-gdbm.c
new file mode 100644
index 00000000..e65125d3
--- /dev/null
+++ b/src/pulsecore/database-gdbm.c
@@ -0,0 +1,249 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2009 Lennart Poettering
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <gdbm.h>
+
+#include <pulse/xmalloc.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/log.h>
+
+#include "database.h"
+
+#define MAKE_GDBM_FILE(x) ((GDBM_FILE) (x))
+
+static inline datum* datum_to_gdbm(datum *to, const pa_datum *from) {
+ pa_assert(from);
+ pa_assert(to);
+
+ to->dptr = from->data;
+ to->dsize = from->size;
+
+ return to;
+}
+
+static inline pa_datum* datum_from_gdbm(pa_datum *to, const datum *from) {
+ pa_assert(from);
+ pa_assert(to);
+
+ to->data = from->dptr;
+ to->size = from->dsize;
+
+ return to;
+}
+
+void pa_datum_free(pa_datum *d) {
+ pa_assert(d);
+
+ free(d->data); /* gdbm uses raw malloc/free hence we should do that here, too */
+ pa_zero(d);
+}
+
+pa_database* pa_database_open(const char *fn, pa_bool_t for_write) {
+ GDBM_FILE f;
+ int gdbm_cache_size;
+ char *path;
+
+ pa_assert(fn);
+
+ /* We include the host identifier in the file name because gdbm
+ * files are CPU dependant, and we don't want things to go wrong
+ * if we are on a multiarch system. */
+ path = pa_sprintf_malloc("%s."CANONICAL_HOST".gdbm", fn);
+ errno = 0;
+
+ /* We need to set the block size explicitly here, since otherwise
+ * gdbm takes the native block size of the underlying file system
+ * which might be incredibly large. */
+ f = gdbm_open((char*) path, 1024, GDBM_NOLOCK | (for_write ? GDBM_WRCREAT : GDBM_READER), 0644, NULL);
+
+ if (f)
+ pa_log_debug("Opened GDBM database '%s'", path);
+
+ pa_xfree(path);
+
+ if (!f) {
+ if (errno == 0)
+ errno = EIO;
+ return NULL;
+ }
+
+ /* By default the cache of gdbm is rather large, let's reduce it a bit to save memory */
+ gdbm_cache_size = 10;
+ gdbm_setopt(f, GDBM_CACHESIZE, &gdbm_cache_size, sizeof(gdbm_cache_size));
+
+ return (pa_database*) f;
+}
+
+void pa_database_close(pa_database *db) {
+ pa_assert(db);
+
+ gdbm_close(MAKE_GDBM_FILE(db));
+}
+
+pa_datum* pa_database_get(pa_database *db, const pa_datum *key, pa_datum* data) {
+ datum gdbm_key, gdbm_data;
+
+ pa_assert(db);
+ pa_assert(key);
+ pa_assert(data);
+
+ gdbm_data = gdbm_fetch(MAKE_GDBM_FILE(db), *datum_to_gdbm(&gdbm_key, key));
+
+ return gdbm_data.dptr ?
+ datum_from_gdbm(data, &gdbm_data) :
+ NULL;
+}
+
+int pa_database_set(pa_database *db, const pa_datum *key, const pa_datum* data, pa_bool_t overwrite) {
+ datum gdbm_key, gdbm_data;
+
+ pa_assert(db);
+ pa_assert(key);
+ pa_assert(data);
+
+ return gdbm_store(MAKE_GDBM_FILE(db),
+ *datum_to_gdbm(&gdbm_key, key),
+ *datum_to_gdbm(&gdbm_data, data),
+ overwrite ? GDBM_REPLACE : GDBM_INSERT) != 0 ? -1 : 0;
+}
+
+int pa_database_unset(pa_database *db, const pa_datum *key) {
+ datum gdbm_key;
+
+ pa_assert(db);
+ pa_assert(key);
+
+ return gdbm_delete(MAKE_GDBM_FILE(db), *datum_to_gdbm(&gdbm_key, key)) != 0 ? -1 : 0;
+}
+
+int pa_database_clear(pa_database *db) {
+ datum gdbm_key;
+
+ pa_assert(db);
+
+ gdbm_key = gdbm_firstkey(MAKE_GDBM_FILE(db));
+
+ while (gdbm_key.dptr) {
+ datum next;
+
+ next = gdbm_nextkey(MAKE_GDBM_FILE(db), gdbm_key);
+
+ gdbm_delete(MAKE_GDBM_FILE(db), gdbm_key);
+
+ free(gdbm_key.dptr);
+ gdbm_key = next;
+ }
+
+ return gdbm_reorganize(MAKE_GDBM_FILE(db)) == 0 ? 0 : -1;
+}
+
+signed pa_database_size(pa_database *db) {
+ datum gdbm_key;
+ unsigned n = 0;
+
+ pa_assert(db);
+
+ /* This sucks */
+
+ gdbm_key = gdbm_firstkey(MAKE_GDBM_FILE(db));
+
+ while (gdbm_key.dptr) {
+ datum next;
+
+ n++;
+
+ next = gdbm_nextkey(MAKE_GDBM_FILE(db), gdbm_key);
+ free(gdbm_key.dptr);
+ gdbm_key = next;
+ }
+
+ return (signed) n;
+}
+
+pa_datum* pa_database_first(pa_database *db, pa_datum *key, pa_datum *data) {
+ datum gdbm_key, gdbm_data;
+
+ pa_assert(db);
+ pa_assert(key);
+
+ gdbm_key = gdbm_firstkey(MAKE_GDBM_FILE(db));
+
+ if (!gdbm_key.dptr)
+ return NULL;
+
+ if (data) {
+ gdbm_data = gdbm_fetch(MAKE_GDBM_FILE(db), gdbm_key);
+
+ if (!gdbm_data.dptr) {
+ free(gdbm_key.dptr);
+ return NULL;
+ }
+
+ datum_from_gdbm(data, &gdbm_data);
+ }
+
+ datum_from_gdbm(key, &gdbm_key);
+
+ return key;
+}
+
+pa_datum* pa_database_next(pa_database *db, const pa_datum *key, pa_datum *next, pa_datum *data) {
+ datum gdbm_key, gdbm_data;
+
+ pa_assert(db);
+ pa_assert(key);
+ pa_assert(next);
+
+ if (!key)
+ return pa_database_first(db, next, data);
+
+ gdbm_key = gdbm_nextkey(MAKE_GDBM_FILE(db), *datum_to_gdbm(&gdbm_key, key));
+
+ if (!gdbm_key.dptr)
+ return NULL;
+
+ if (data) {
+ gdbm_data = gdbm_fetch(MAKE_GDBM_FILE(db), gdbm_key);
+
+ if (!gdbm_data.dptr) {
+ free(gdbm_key.dptr);
+ return NULL;
+ }
+
+ datum_from_gdbm(data, &gdbm_data);
+ }
+
+ datum_from_gdbm(next, &gdbm_key);
+
+ return next;
+}
+
+int pa_database_sync(pa_database *db) {
+ pa_assert(db);
+
+ gdbm_sync(MAKE_GDBM_FILE(db));
+ return 0;
+}
diff --git a/src/pulsecore/database-tdb.c b/src/pulsecore/database-tdb.c
new file mode 100644
index 00000000..b79d2837
--- /dev/null
+++ b/src/pulsecore/database-tdb.c
@@ -0,0 +1,227 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2009 Lennart Poettering
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+
+/* Some versions of tdb lack inclusion of signal.h in the header files but use sigatomic_t */
+#include <signal.h>
+#include <tdb.h>
+
+#include <pulse/xmalloc.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/log.h>
+
+#include "database.h"
+
+#define MAKE_TDB_CONTEXT(x) ((struct tdb_context*) (x))
+
+static inline TDB_DATA* datum_to_tdb(TDB_DATA *to, const pa_datum *from) {
+ pa_assert(from);
+ pa_assert(to);
+
+ to->dptr = from->data;
+ to->dsize = from->size;
+
+ return to;
+}
+
+static inline pa_datum* datum_from_tdb(pa_datum *to, const TDB_DATA *from) {
+ pa_assert(from);
+ pa_assert(to);
+
+ to->data = from->dptr;
+ to->size = from->dsize;
+
+ return to;
+}
+
+void pa_datum_free(pa_datum *d) {
+ pa_assert(d);
+
+ free(d->data); /* tdb uses raw malloc/free hence we should do that here, too */
+ pa_zero(d);
+}
+
+pa_database* pa_database_open(const char *fn, pa_bool_t for_write) {
+ struct tdb_context *c;
+ char *path;
+
+ pa_assert(fn);
+
+ path = pa_sprintf_malloc("%s.tdb", fn);
+ errno = 0;
+ c = tdb_open(path, 0, TDB_NOSYNC|TDB_NOLOCK,
+ (for_write ? O_RDWR|O_CREAT : O_RDONLY)|O_NOCTTY
+#ifdef O_CLOEXEC
+ |O_CLOEXEC
+#endif
+ , 0644);
+
+ if (c)
+ pa_log_debug("Opened TDB database '%s'", path);
+
+ pa_xfree(path);
+
+ if (!c) {
+ if (errno == 0)
+ errno = EIO;
+ return NULL;
+ }
+
+ return (pa_database*) c;
+}
+
+void pa_database_close(pa_database *db) {
+ pa_assert(db);
+
+ tdb_close(MAKE_TDB_CONTEXT(db));
+}
+
+pa_datum* pa_database_get(pa_database *db, const pa_datum *key, pa_datum* data) {
+ TDB_DATA tdb_key, tdb_data;
+
+ pa_assert(db);
+ pa_assert(key);
+ pa_assert(data);
+
+ tdb_data = tdb_fetch(MAKE_TDB_CONTEXT(db), *datum_to_tdb(&tdb_key, key));
+
+ return tdb_data.dptr ?
+ datum_from_tdb(data, &tdb_data) :
+ NULL;
+}
+
+int pa_database_set(pa_database *db, const pa_datum *key, const pa_datum* data, pa_bool_t overwrite) {
+ TDB_DATA tdb_key, tdb_data;
+
+ pa_assert(db);
+ pa_assert(key);
+ pa_assert(data);
+
+ return tdb_store(MAKE_TDB_CONTEXT(db),
+ *datum_to_tdb(&tdb_key, key),
+ *datum_to_tdb(&tdb_data, data),
+ overwrite ? TDB_REPLACE : TDB_INSERT) != 0 ? -1 : 0;
+}
+
+int pa_database_unset(pa_database *db, const pa_datum *key) {
+ TDB_DATA tdb_key;
+
+ pa_assert(db);
+ pa_assert(key);
+
+ return tdb_delete(MAKE_TDB_CONTEXT(db), *datum_to_tdb(&tdb_key, key)) != 0 ? -1 : 0;
+}
+
+int pa_database_clear(pa_database *db) {
+ pa_assert(db);
+
+ return tdb_wipe_all(MAKE_TDB_CONTEXT(db)) != 0 ? -1 : 0;
+}
+
+signed pa_database_size(pa_database *db) {
+ TDB_DATA tdb_key;
+ unsigned n = 0;
+
+ pa_assert(db);
+
+ /* This sucks */
+
+ tdb_key = tdb_firstkey(MAKE_TDB_CONTEXT(db));
+
+ while (tdb_key.dptr) {
+ TDB_DATA next;
+
+ n++;
+
+ next = tdb_nextkey(MAKE_TDB_CONTEXT(db), tdb_key);
+ free(tdb_key.dptr);
+ tdb_key = next;
+ }
+
+ return (signed) n;
+}
+
+pa_datum* pa_database_first(pa_database *db, pa_datum *key, pa_datum *data) {
+ TDB_DATA tdb_key, tdb_data;
+
+ pa_assert(db);
+ pa_assert(key);
+
+ tdb_key = tdb_firstkey(MAKE_TDB_CONTEXT(db));
+
+ if (!tdb_key.dptr)
+ return NULL;
+
+ if (data) {
+ tdb_data = tdb_fetch(MAKE_TDB_CONTEXT(db), tdb_key);
+
+ if (!tdb_data.dptr) {
+ free(tdb_key.dptr);
+ return NULL;
+ }
+
+ datum_from_tdb(data, &tdb_data);
+ }
+
+ datum_from_tdb(key, &tdb_key);
+
+ return key;
+}
+
+pa_datum* pa_database_next(pa_database *db, const pa_datum *key, pa_datum *next, pa_datum *data) {
+ TDB_DATA tdb_key, tdb_data;
+
+ pa_assert(db);
+ pa_assert(key);
+
+ tdb_key = tdb_nextkey(MAKE_TDB_CONTEXT(db), *datum_to_tdb(&tdb_key, key));
+
+ if (!tdb_key.dptr)
+ return NULL;
+
+ if (data) {
+ tdb_data = tdb_fetch(MAKE_TDB_CONTEXT(db), tdb_key);
+
+ if (!tdb_data.dptr) {
+ free(tdb_key.dptr);
+ return NULL;
+ }
+
+ datum_from_tdb(data, &tdb_data);
+ }
+
+ datum_from_tdb(next, &tdb_key);
+
+ return next;
+}
+
+int pa_database_sync(pa_database *db) {
+ pa_assert(db);
+
+ return 0;
+}
diff --git a/src/pulsecore/database.h b/src/pulsecore/database.h
new file mode 100644
index 00000000..17455d4c
--- /dev/null
+++ b/src/pulsecore/database.h
@@ -0,0 +1,61 @@
+#ifndef foopulsecoredatabasehfoo
+#define foopulsecoredatabasehfoo
+
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2009 Lennart Poettering
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <sys/types.h>
+
+#include <pulsecore/macro.h>
+
+/* A little abstraction over simple databases, such as gdbm, tdb, and
+ * so on. We only make minimal assumptions about the supported
+ * backend: it does not need to support locking, it does not have to
+ * be arch independant. */
+
+typedef struct pa_database pa_database;
+
+typedef struct pa_datum {
+ void *data;
+ size_t size;
+} pa_datum;
+
+void pa_datum_free(pa_datum *d);
+
+/* This will append a suffix to the filename */
+pa_database* pa_database_open(const char *fn, pa_bool_t for_write);
+void pa_database_close(pa_database *db);
+
+pa_datum* pa_database_get(pa_database *db, const pa_datum *key, pa_datum* data);
+
+int pa_database_set(pa_database *db, const pa_datum *key, const pa_datum* data, pa_bool_t overwrite);
+int pa_database_unset(pa_database *db, const pa_datum *key);
+
+int pa_database_clear(pa_database *db);
+
+signed pa_database_size(pa_database *db);
+
+pa_datum* pa_database_first(pa_database *db, pa_datum *key, pa_datum *data /* may be NULL */);
+pa_datum* pa_database_next(pa_database *db, const pa_datum *key, pa_datum *next, pa_datum *data /* may be NULL */);
+
+int pa_database_sync(pa_database *db);
+
+#endif
diff --git a/src/pulsecore/dbus-shared.c b/src/pulsecore/dbus-shared.c
new file mode 100644
index 00000000..20ef9b1e
--- /dev/null
+++ b/src/pulsecore/dbus-shared.c
@@ -0,0 +1,107 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2004-2006, 2009 Lennart Poettering
+ Copyright 2006 Shams E. King
+
+ 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 <stdarg.h>
+
+#include <pulse/xmalloc.h>
+#include <pulse/timeval.h>
+#include <pulsecore/log.h>
+#include <pulsecore/shared.h>
+
+#include "dbus-shared.h"
+
+struct pa_dbus_connection {
+ PA_REFCNT_DECLARE;
+
+ pa_dbus_wrap_connection *connection;
+ pa_core *core;
+ const char *property_name;
+};
+
+static pa_dbus_connection* dbus_connection_new(pa_core *c, pa_dbus_wrap_connection *conn, const char *name) {
+ pa_dbus_connection *pconn;
+
+ pconn = pa_xnew(pa_dbus_connection, 1);
+ PA_REFCNT_INIT(pconn);
+ pconn->core = c;
+ pconn->property_name = name;
+ pconn->connection = conn;
+
+ pa_shared_set(c, name, pconn);
+
+ return pconn;
+}
+
+pa_dbus_connection* pa_dbus_bus_get(pa_core *c, DBusBusType type, DBusError *error) {
+
+ static const char *const prop_name[] = {
+ [DBUS_BUS_SESSION] = "dbus-connection-session",
+ [DBUS_BUS_SYSTEM] = "dbus-connection-system",
+ [DBUS_BUS_STARTER] = "dbus-connection-starter"
+ };
+ pa_dbus_wrap_connection *conn;
+ pa_dbus_connection *pconn;
+
+ pa_assert(type == DBUS_BUS_SYSTEM || type == DBUS_BUS_SESSION || type == DBUS_BUS_STARTER);
+
+ if ((pconn = pa_shared_get(c, prop_name[type])))
+ return pa_dbus_connection_ref(pconn);
+
+ if (!(conn = pa_dbus_wrap_connection_new(c->mainloop, TRUE, type, error)))
+ return NULL;
+
+ return dbus_connection_new(c, conn, prop_name[type]);
+}
+
+DBusConnection* pa_dbus_connection_get(pa_dbus_connection *c){
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) > 0);
+ pa_assert(c->connection);
+
+ return pa_dbus_wrap_connection_get(c->connection);
+}
+
+void pa_dbus_connection_unref(pa_dbus_connection *c) {
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) > 0);
+
+ if (PA_REFCNT_DEC(c) > 0)
+ return;
+
+ pa_dbus_wrap_connection_free(c->connection);
+
+ pa_shared_remove(c->core, c->property_name);
+ pa_xfree(c);
+}
+
+pa_dbus_connection* pa_dbus_connection_ref(pa_dbus_connection *c) {
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) > 0);
+
+ PA_REFCNT_INC(c);
+
+ return c;
+}
diff --git a/src/pulsecore/dbus-shared.h b/src/pulsecore/dbus-shared.h
new file mode 100644
index 00000000..4c154552
--- /dev/null
+++ b/src/pulsecore/dbus-shared.h
@@ -0,0 +1,42 @@
+#ifndef foodbussharedhfoo
+#define foodbussharedhfoo
+
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2004-2006, 2009 Lennart Poettering
+ Copyright 2006 Shams E. King
+
+ 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/dbus-util.h>
+
+typedef struct pa_dbus_connection pa_dbus_connection;
+
+/* return a pa_dbus_connection of the specified type for the given core,
+ * like dbus_bus_get(), but integrates the connection with the pa_core */
+pa_dbus_connection* pa_dbus_bus_get(pa_core *c, DBusBusType type, DBusError *error);
+
+DBusConnection* pa_dbus_connection_get(pa_dbus_connection *conn);
+
+pa_dbus_connection* pa_dbus_connection_ref(pa_dbus_connection *conn);
+void pa_dbus_connection_unref(pa_dbus_connection *conn);
+
+#endif
diff --git a/src/pulsecore/dbus-util.c b/src/pulsecore/dbus-util.c
new file mode 100644
index 00000000..4e6148f0
--- /dev/null
+++ b/src/pulsecore/dbus-util.c
@@ -0,0 +1,424 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2006 Lennart Poettering
+ Copyright 2006 Shams E. King
+
+ 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 <stdarg.h>
+
+#include <pulse/rtclock.h>
+#include <pulse/timeval.h>
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/core-rtclock.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/log.h>
+
+#include "dbus-util.h"
+
+struct pa_dbus_wrap_connection {
+ pa_mainloop_api *mainloop;
+ DBusConnection *connection;
+ pa_defer_event* dispatch_event;
+ pa_bool_t use_rtclock:1;
+};
+
+struct timeout_data {
+ pa_dbus_wrap_connection *c;
+ DBusTimeout *timeout;
+};
+
+static void dispatch_cb(pa_mainloop_api *ea, pa_defer_event *ev, void *userdata) {
+ DBusConnection *conn = userdata;
+
+ if (dbus_connection_dispatch(conn) == DBUS_DISPATCH_COMPLETE) {
+ /* no more data to process, disable the deferred */
+ ea->defer_enable(ev, 0);
+ }
+}
+
+/* DBusDispatchStatusFunction callback for the pa mainloop */
+static void dispatch_status(DBusConnection *conn, DBusDispatchStatus status, void *userdata) {
+ pa_dbus_wrap_connection *c = userdata;
+
+ pa_assert(c);
+
+ switch(status) {
+
+ case DBUS_DISPATCH_COMPLETE:
+ c->mainloop->defer_enable(c->dispatch_event, 0);
+ break;
+
+ case DBUS_DISPATCH_DATA_REMAINS:
+ case DBUS_DISPATCH_NEED_MEMORY:
+ default:
+ c->mainloop->defer_enable(c->dispatch_event, 1);
+ break;
+ }
+}
+
+static pa_io_event_flags_t get_watch_flags(DBusWatch *watch) {
+ unsigned int flags;
+ pa_io_event_flags_t events = 0;
+
+ pa_assert(watch);
+
+ flags = dbus_watch_get_flags(watch);
+
+ /* no watch flags for disabled watches */
+ if (!dbus_watch_get_enabled(watch))
+ return PA_IO_EVENT_NULL;
+
+ if (flags & DBUS_WATCH_READABLE)
+ events |= PA_IO_EVENT_INPUT;
+ if (flags & DBUS_WATCH_WRITABLE)
+ events |= PA_IO_EVENT_OUTPUT;
+
+ return events | PA_IO_EVENT_HANGUP | PA_IO_EVENT_ERROR;
+}
+
+/* pa_io_event_cb_t IO event handler */
+static void handle_io_event(pa_mainloop_api *ea, pa_io_event *e, int fd, pa_io_event_flags_t events, void *userdata) {
+ unsigned int flags = 0;
+ DBusWatch *watch = userdata;
+
+#if HAVE_DBUS_WATCH_GET_UNIX_FD
+ pa_assert(fd == dbus_watch_get_unix_fd(watch));
+#else
+ pa_assert(fd == dbus_watch_get_fd(watch));
+#endif
+
+ if (!dbus_watch_get_enabled(watch)) {
+ pa_log_warn("Asked to handle disabled watch: %p %i", (void*) watch, fd);
+ return;
+ }
+
+ if (events & PA_IO_EVENT_INPUT)
+ flags |= DBUS_WATCH_READABLE;
+ if (events & PA_IO_EVENT_OUTPUT)
+ flags |= DBUS_WATCH_WRITABLE;
+ if (events & PA_IO_EVENT_HANGUP)
+ flags |= DBUS_WATCH_HANGUP;
+ if (events & PA_IO_EVENT_ERROR)
+ flags |= DBUS_WATCH_ERROR;
+
+ dbus_watch_handle(watch, flags);
+}
+
+/* pa_time_event_cb_t timer event handler */
+static void handle_time_event(pa_mainloop_api *ea, pa_time_event* e, const struct timeval *t, void *userdata) {
+ struct timeval tv;
+ struct timeout_data *d = userdata;
+
+ pa_assert(d);
+ pa_assert(d->c);
+
+ if (dbus_timeout_get_enabled(d->timeout)) {
+ dbus_timeout_handle(d->timeout);
+
+ /* restart it for the next scheduled time */
+ ea->time_restart(e, pa_timeval_rtstore(&tv, pa_timeval_load(t) + dbus_timeout_get_interval(d->timeout) * PA_USEC_PER_MSEC, d->c->use_rtclock));
+ }
+}
+
+/* DBusAddWatchFunction callback for pa mainloop */
+static dbus_bool_t add_watch(DBusWatch *watch, void *data) {
+ pa_dbus_wrap_connection *c = data;
+ pa_io_event *ev;
+
+ pa_assert(watch);
+ pa_assert(c);
+
+ ev = c->mainloop->io_new(
+ c->mainloop,
+#if HAVE_DBUS_WATCH_GET_UNIX_FD
+ dbus_watch_get_unix_fd(watch),
+#else
+ dbus_watch_get_fd(watch),
+#endif
+ get_watch_flags(watch), handle_io_event, watch);
+
+ dbus_watch_set_data(watch, ev, NULL);
+
+ return TRUE;
+}
+
+/* DBusRemoveWatchFunction callback for pa mainloop */
+static void remove_watch(DBusWatch *watch, void *data) {
+ pa_dbus_wrap_connection *c = data;
+ pa_io_event *ev;
+
+ pa_assert(watch);
+ pa_assert(c);
+
+ if ((ev = dbus_watch_get_data(watch)))
+ c->mainloop->io_free(ev);
+}
+
+/* DBusWatchToggledFunction callback for pa mainloop */
+static void toggle_watch(DBusWatch *watch, void *data) {
+ pa_dbus_wrap_connection *c = data;
+ pa_io_event *ev;
+
+ pa_assert(watch);
+ pa_assert(c);
+
+ pa_assert_se(ev = dbus_watch_get_data(watch));
+
+ /* get_watch_flags() checks if the watch is enabled */
+ c->mainloop->io_enable(ev, get_watch_flags(watch));
+}
+
+static void time_event_destroy_cb(pa_mainloop_api *a, pa_time_event *e, void *userdata) {
+ pa_xfree(userdata);
+}
+
+/* DBusAddTimeoutFunction callback for pa mainloop */
+static dbus_bool_t add_timeout(DBusTimeout *timeout, void *data) {
+ pa_dbus_wrap_connection *c = data;
+ pa_time_event *ev;
+ struct timeval tv;
+ struct timeout_data *d;
+
+ pa_assert(timeout);
+ pa_assert(c);
+
+ if (!dbus_timeout_get_enabled(timeout))
+ return FALSE;
+
+ d = pa_xnew(struct timeout_data, 1);
+ d->c = c;
+ d->timeout = timeout;
+ ev = c->mainloop->time_new(c->mainloop, pa_timeval_rtstore(&tv, pa_rtclock_now() + dbus_timeout_get_interval(timeout) * PA_USEC_PER_MSEC, c->use_rtclock), handle_time_event, d);
+ c->mainloop->time_set_destroy(ev, time_event_destroy_cb);
+
+ dbus_timeout_set_data(timeout, ev, NULL);
+
+ return TRUE;
+}
+
+/* DBusRemoveTimeoutFunction callback for pa mainloop */
+static void remove_timeout(DBusTimeout *timeout, void *data) {
+ pa_dbus_wrap_connection *c = data;
+ pa_time_event *ev;
+
+ pa_assert(timeout);
+ pa_assert(c);
+
+ if ((ev = dbus_timeout_get_data(timeout)))
+ c->mainloop->time_free(ev);
+}
+
+/* DBusTimeoutToggledFunction callback for pa mainloop */
+static void toggle_timeout(DBusTimeout *timeout, void *data) {
+ struct timeout_data *d = data;
+ pa_time_event *ev;
+ struct timeval tv;
+
+ pa_assert(d);
+ pa_assert(d->c);
+ pa_assert(timeout);
+
+ pa_assert_se(ev = dbus_timeout_get_data(timeout));
+
+ if (dbus_timeout_get_enabled(timeout)) {
+ d->c->mainloop->time_restart(ev, pa_timeval_rtstore(&tv, pa_rtclock_now() + dbus_timeout_get_interval(timeout) * PA_USEC_PER_MSEC, d->c->use_rtclock));
+ } else
+ d->c->mainloop->time_restart(ev, pa_timeval_rtstore(&tv, PA_USEC_INVALID, d->c->use_rtclock));
+}
+
+static void wakeup_main(void *userdata) {
+ pa_dbus_wrap_connection *c = userdata;
+
+ pa_assert(c);
+
+ /* this will wakeup the mainloop and dispatch events, although
+ * it may not be the cleanest way of accomplishing it */
+ c->mainloop->defer_enable(c->dispatch_event, 1);
+}
+
+pa_dbus_wrap_connection* pa_dbus_wrap_connection_new(pa_mainloop_api *m, pa_bool_t use_rtclock, DBusBusType type, DBusError *error) {
+ DBusConnection *conn;
+ pa_dbus_wrap_connection *pconn;
+ char *id;
+
+ pa_assert(type == DBUS_BUS_SYSTEM || type == DBUS_BUS_SESSION || type == DBUS_BUS_STARTER);
+
+ if (!(conn = dbus_bus_get_private(type, error)))
+ return NULL;
+
+ pconn = pa_xnew(pa_dbus_wrap_connection, 1);
+ pconn->mainloop = m;
+ pconn->connection = 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);
+
+ pa_log_debug("Successfully connected to D-Bus %s bus %s as %s",
+ type == DBUS_BUS_SYSTEM ? "system" : (type == DBUS_BUS_SESSION ? "session" : "starter"),
+ pa_strnull((id = dbus_connection_get_server_id(conn))),
+ pa_strnull(dbus_bus_get_unique_name(conn)));
+
+ dbus_free(id);
+
+ return pconn;
+}
+
+void pa_dbus_wrap_connection_free(pa_dbus_wrap_connection* c) {
+ pa_assert(c);
+
+ if (dbus_connection_get_is_connected(c->connection)) {
+ dbus_connection_close(c->connection);
+ /* must process remaining messages, bit of a kludge to handle
+ * both unload and shutdown */
+ while (dbus_connection_read_write_dispatch(c->connection, -1))
+ ;
+ }
+
+ c->mainloop->defer_free(c->dispatch_event);
+ dbus_connection_unref(c->connection);
+ pa_xfree(c);
+}
+
+DBusConnection* pa_dbus_wrap_connection_get(pa_dbus_wrap_connection *c) {
+ pa_assert(c);
+ pa_assert(c->connection);
+
+ return c->connection;
+}
+
+int pa_dbus_add_matches(DBusConnection *c, DBusError *error, ...) {
+ const char *t;
+ va_list ap;
+ unsigned k = 0;
+
+ pa_assert(c);
+ pa_assert(error);
+
+ va_start(ap, error);
+ while ((t = va_arg(ap, const char*))) {
+ dbus_bus_add_match(c, t, error);
+
+ if (dbus_error_is_set(error))
+ goto fail;
+
+ k++;
+ }
+ va_end(ap);
+ return 0;
+
+fail:
+
+ va_end(ap);
+ va_start(ap, error);
+ for (; k > 0; k--) {
+ DBusError e;
+
+ pa_assert_se(t = va_arg(ap, const char*));
+
+ dbus_error_init(&e);
+ dbus_bus_remove_match(c, t, &e);
+ dbus_error_free(&e);
+ }
+ va_end(ap);
+
+ return -1;
+}
+
+void pa_dbus_remove_matches(DBusConnection *c, ...) {
+ const char *t;
+ va_list ap;
+ DBusError error;
+
+ pa_assert(c);
+
+ dbus_error_init(&error);
+
+ va_start(ap, c);
+ while ((t = va_arg(ap, const char*))) {
+ dbus_bus_remove_match(c, t, &error);
+ dbus_error_free(&error);
+ }
+ va_end(ap);
+}
+
+pa_dbus_pending *pa_dbus_pending_new(
+ DBusConnection *c,
+ DBusMessage *m,
+ DBusPendingCall *pending,
+ void *context_data,
+ void *call_data) {
+
+ pa_dbus_pending *p;
+
+ pa_assert(pending);
+
+ p = pa_xnew(pa_dbus_pending, 1);
+ p->connection = c;
+ p->message = m;
+ p->pending = pending;
+ p->context_data = context_data;
+ p->call_data = call_data;
+
+ PA_LLIST_INIT(pa_dbus_pending, p);
+
+ return p;
+}
+
+void pa_dbus_pending_free(pa_dbus_pending *p) {
+ pa_assert(p);
+
+ if (p->pending) {
+ dbus_pending_call_cancel(p->pending);
+ dbus_pending_call_unref(p->pending);
+ }
+
+ if (p->message)
+ dbus_message_unref(p->message);
+
+ pa_xfree(p);
+}
+
+void pa_dbus_sync_pending_list(pa_dbus_pending **p) {
+ pa_assert(p);
+
+ while (*p && dbus_connection_read_write_dispatch((*p)->connection, -1))
+ ;
+}
+
+void pa_dbus_free_pending_list(pa_dbus_pending **p) {
+ pa_dbus_pending *i;
+
+ pa_assert(p);
+
+ while ((i = *p)) {
+ PA_LLIST_REMOVE(pa_dbus_pending, *p, i);
+ pa_dbus_pending_free(i);
+ }
+}
diff --git a/src/pulsecore/dbus-util.h b/src/pulsecore/dbus-util.h
new file mode 100644
index 00000000..9ff298d8
--- /dev/null
+++ b/src/pulsecore/dbus-util.h
@@ -0,0 +1,63 @@
+#ifndef foodbusutilhfoo
+#define foodbusutilhfoo
+
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2006 Shams E. King
+
+ 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/llist.h>
+#include <pulse/mainloop-api.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);
+void pa_dbus_wrap_connection_free(pa_dbus_wrap_connection* conn);
+
+DBusConnection* pa_dbus_wrap_connection_get(pa_dbus_wrap_connection *conn);
+
+int pa_dbus_add_matches(DBusConnection *c, DBusError *error, ...) PA_GCC_SENTINEL;
+void pa_dbus_remove_matches(DBusConnection *c, ...) PA_GCC_SENTINEL;
+
+typedef struct pa_dbus_pending pa_dbus_pending;
+
+struct pa_dbus_pending {
+ DBusConnection *connection;
+ DBusMessage *message;
+ DBusPendingCall *pending;
+
+ void *context_data;
+ void *call_data;
+
+ PA_LLIST_FIELDS(pa_dbus_pending);
+};
+
+pa_dbus_pending *pa_dbus_pending_new(DBusConnection *c, DBusMessage *m, DBusPendingCall *pending, void *context_data, void *call_data);
+void pa_dbus_pending_free(pa_dbus_pending *p);
+
+/* Sync up a list of pa_dbus_pending_call objects */
+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);
+
+#endif
diff --git a/src/pulsecore/endianmacros.h b/src/pulsecore/endianmacros.h
index 22579376..2b18cf8d 100644
--- a/src/pulsecore/endianmacros.h
+++ b/src/pulsecore/endianmacros.h
@@ -45,27 +45,27 @@
#define PA_UINT32_SWAP(x) ( (uint32_t) ( ((uint32_t) (x) >> 24) | ((uint32_t) (x) << 24) | (((uint32_t) (x) & 0xFF00) << 8) | ((((uint32_t) (x)) >> 8) & 0xFF00) ) )
#endif
-static inline uint32_t PA_READ24LE(const uint8_t *p) {
+static inline uint32_t PA_READ24BE(const uint8_t *p) {
return
((uint32_t) p[0] << 16) |
((uint32_t) p[1] << 8) |
((uint32_t) p[2]);
}
-static inline uint32_t PA_READ24BE(const uint8_t *p) {
+static inline uint32_t PA_READ24LE(const uint8_t *p) {
return
((uint32_t) p[2] << 16) |
((uint32_t) p[1] << 8) |
((uint32_t) p[0]);
}
-static inline void PA_WRITE24LE(uint8_t *p, uint32_t u) {
+static inline void PA_WRITE24BE(uint8_t *p, uint32_t u) {
p[0] = (uint8_t) (u >> 16);
p[1] = (uint8_t) (u >> 8);
p[2] = (uint8_t) u;
}
-static inline void PA_WRITE24BE(uint8_t *p, uint32_t u) {
+static inline void PA_WRITE24LE(uint8_t *p, uint32_t u) {
p[2] = (uint8_t) (u >> 16);
p[1] = (uint8_t) (u >> 8);
p[0] = (uint8_t) u;
diff --git a/src/pulsecore/hashmap.c b/src/pulsecore/hashmap.c
index e957c5ba..1fac97eb 100644
--- a/src/pulsecore/hashmap.c
+++ b/src/pulsecore/hashmap.c
@@ -237,6 +237,39 @@ at_end:
return NULL;
}
+void *pa_hashmap_iterate_backwards(pa_hashmap *h, void **state, const void **key) {
+ struct hashmap_entry *e;
+
+ pa_assert(h);
+ pa_assert(state);
+
+ if (*state == (void*) -1)
+ goto at_beginning;
+
+ if (!*state && !h->iterate_list_tail)
+ goto at_beginning;
+
+ e = *state ? *state : h->iterate_list_tail;
+
+ if (e->iterate_previous)
+ *state = e->iterate_previous;
+ else
+ *state = (void*) -1;
+
+ if (key)
+ *key = e->key;
+
+ return e->value;
+
+at_beginning:
+ *state = (void *) -1;
+
+ if (key)
+ *key = NULL;
+
+ return NULL;
+}
+
void* pa_hashmap_first(pa_hashmap *h) {
pa_assert(h);
@@ -246,6 +279,15 @@ void* pa_hashmap_first(pa_hashmap *h) {
return h->iterate_list_head->value;
}
+void* pa_hashmap_last(pa_hashmap *h) {
+ pa_assert(h);
+
+ if (!h->iterate_list_tail)
+ return NULL;
+
+ return h->iterate_list_tail->value;
+}
+
void* pa_hashmap_steal_first(pa_hashmap *h) {
void *data;
diff --git a/src/pulsecore/hashmap.h b/src/pulsecore/hashmap.h
index 08e18ead..ac2092a6 100644
--- a/src/pulsecore/hashmap.h
+++ b/src/pulsecore/hashmap.h
@@ -26,7 +26,8 @@
/* Simple Implementation of a hash table. Memory management is the
* user's job. It's a good idea to have the key pointer point to a
- * string in the value data. */
+ * string in the value data. The insertion order is preserved when
+ * iterating. */
typedef struct pa_hashmap pa_hashmap;
@@ -59,10 +60,24 @@ pa_bool_t pa_hashmap_isempty(pa_hashmap *h);
returned. */
void *pa_hashmap_iterate(pa_hashmap *h, void **state, const void**key);
+/* Same as pa_hashmap_iterate() but goes backwards */
+void *pa_hashmap_iterate_backwards(pa_hashmap *h, void **state, const void**key);
+
/* Remove the oldest entry in the hashmap and return it */
void *pa_hashmap_steal_first(pa_hashmap *h);
/* Return the oldest entry in the hashmap */
void* pa_hashmap_first(pa_hashmap *h);
+/* Return the newest entry in the hashmap */
+void* pa_hashmap_last(pa_hashmap *h);
+
+/* A macro to ease iteration through all entries */
+#define PA_HASHMAP_FOREACH(e, h, state) \
+ for ((state) = NULL, (e) = pa_hashmap_iterate((h), &(state), NULL); (e); (e) = pa_hashmap_iterate((h), &(state), NULL))
+
+/* A macro to ease iteration through all entries, backwards */
+#define PA_HASHMAP_FOREACH_BACKWARDS(e, h, state) \
+ for ((state) = NULL, (e) = pa_hashmap_iterate_backwards((h), &(state), NULL); (e); (e) = pa_hashmap_iterate_backwards((h), &(state), NULL))
+
#endif
diff --git a/src/pulsecore/hook-list.c b/src/pulsecore/hook-list.c
index 5f7a8665..a00116d1 100644
--- a/src/pulsecore/hook-list.c
+++ b/src/pulsecore/hook-list.c
@@ -121,3 +121,9 @@ pa_hook_result_t pa_hook_fire(pa_hook *hook, void *data) {
return result;
}
+
+pa_bool_t pa_hook_is_firing(pa_hook *hook) {
+ pa_assert(hook);
+
+ return hook->n_firing > 0;
+}
diff --git a/src/pulsecore/hook-list.h b/src/pulsecore/hook-list.h
index 8514cced..86ce9d25 100644
--- a/src/pulsecore/hook-list.h
+++ b/src/pulsecore/hook-list.h
@@ -71,4 +71,6 @@ void pa_hook_slot_free(pa_hook_slot *slot);
pa_hook_result_t pa_hook_fire(pa_hook *hook, void *data);
+pa_bool_t pa_hook_is_firing(pa_hook *hook);
+
#endif
diff --git a/src/pulsecore/idxset.c b/src/pulsecore/idxset.c
index 352ac977..408011f6 100644
--- a/src/pulsecore/idxset.c
+++ b/src/pulsecore/idxset.c
@@ -453,3 +453,17 @@ pa_bool_t pa_idxset_isempty(pa_idxset *s) {
return s->n_entries == 0;
}
+
+pa_idxset *pa_idxset_copy(pa_idxset *s) {
+ pa_idxset *copy;
+ struct idxset_entry *i;
+
+ pa_assert(s);
+
+ copy = pa_idxset_new(s->hash_func, s->compare_func);
+
+ for (i = s->iterate_list_head; i; i = i->iterate_next)
+ pa_idxset_put(copy, i->data, NULL);
+
+ return copy;
+}
diff --git a/src/pulsecore/idxset.h b/src/pulsecore/idxset.h
index 7531ea32..d1e68c5c 100644
--- a/src/pulsecore/idxset.h
+++ b/src/pulsecore/idxset.h
@@ -103,4 +103,11 @@ unsigned pa_idxset_size(pa_idxset*s);
/* Return TRUE of the idxset is empty */
pa_bool_t pa_idxset_isempty(pa_idxset *s);
+/* Duplicate the idxset. This will not copy the actual indexes */
+pa_idxset *pa_idxset_copy(pa_idxset *s);
+
+/* A macro to ease iteration through all entries */
+#define PA_IDXSET_FOREACH(e, s, idx) \
+ for ((e) = pa_idxset_first((s), &(idx)); (e); (e) = pa_idxset_next((s), &(idx)))
+
#endif
diff --git a/src/pulsecore/ioline.c b/src/pulsecore/ioline.c
index 5c38d6e5..7afdb08c 100644
--- a/src/pulsecore/ioline.c
+++ b/src/pulsecore/ioline.c
@@ -57,6 +57,9 @@ struct pa_ioline {
pa_ioline_cb_t callback;
void *userdata;
+ pa_ioline_drain_cb_t drain_callback;
+ void *drain_userdata;
+
pa_bool_t dead:1;
pa_bool_t defer_close:1;
};
@@ -81,6 +84,9 @@ pa_ioline* pa_ioline_new(pa_iochannel *io) {
l->callback = NULL;
l->userdata = NULL;
+ l->drain_callback = NULL;
+ l->drain_userdata = NULL;
+
l->mainloop = pa_iochannel_get_mainloop_api(io);
l->defer_event = l->mainloop->defer_new(l->mainloop, defer_callback, l);
@@ -202,6 +208,17 @@ void pa_ioline_set_callback(pa_ioline*l, pa_ioline_cb_t callback, void *userdata
l->userdata = userdata;
}
+void pa_ioline_set_drain_callback(pa_ioline*l, pa_ioline_drain_cb_t callback, void *userdata) {
+ pa_assert(l);
+ pa_assert(PA_REFCNT_VALUE(l) >= 1);
+
+ if (l->dead)
+ return;
+
+ l->drain_callback = callback;
+ l->drain_userdata = userdata;
+}
+
static void failure(pa_ioline *l, pa_bool_t process_leftover) {
pa_assert(l);
pa_assert(PA_REFCNT_VALUE(l) >= 1);
@@ -266,7 +283,7 @@ static int do_read(pa_ioline *l) {
pa_assert(l);
pa_assert(PA_REFCNT_VALUE(l) >= 1);
- while (!l->dead && pa_iochannel_is_readable(l->io)) {
+ while (l->io && !l->dead && pa_iochannel_is_readable(l->io)) {
ssize_t r;
size_t len;
@@ -331,12 +348,12 @@ static int do_write(pa_ioline *l) {
pa_assert(l);
pa_assert(PA_REFCNT_VALUE(l) >= 1);
- while (!l->dead && pa_iochannel_is_writable(l->io) && l->wbuf_valid_length) {
+ while (l->io && !l->dead && pa_iochannel_is_writable(l->io) && l->wbuf_valid_length > 0) {
if ((r = pa_iochannel_write(l->io, l->wbuf+l->wbuf_index, l->wbuf_valid_length)) <= 0) {
if (r < 0 && errno == EAGAIN)
- return 0;
+ break;
if (r < 0 && errno != EPIPE)
pa_log("write(): %s", pa_cstrerror(errno));
@@ -354,6 +371,9 @@ static int do_write(pa_ioline *l) {
l->wbuf_index = 0;
}
+ if (l->wbuf_valid_length <= 0 && l->drain_callback)
+ l->drain_callback(l, l->drain_userdata);
+
return 0;
}
@@ -423,3 +443,25 @@ void pa_ioline_printf(pa_ioline *l, const char *format, ...) {
pa_ioline_puts(l, t);
pa_xfree(t);
}
+
+pa_iochannel* pa_ioline_detach_iochannel(pa_ioline *l) {
+ pa_iochannel *r;
+
+ pa_assert(l);
+
+ if (!l->io)
+ return NULL;
+
+ r = l->io;
+ l->io = NULL;
+
+ pa_iochannel_set_callback(r, NULL, NULL);
+
+ return r;
+}
+
+pa_bool_t pa_ioline_is_drained(pa_ioline *l) {
+ pa_assert(l);
+
+ return l->wbuf_valid_length <= 0;
+}
diff --git a/src/pulsecore/ioline.h b/src/pulsecore/ioline.h
index 9f32d60f..d973a3c7 100644
--- a/src/pulsecore/ioline.h
+++ b/src/pulsecore/ioline.h
@@ -32,6 +32,7 @@
typedef struct pa_ioline pa_ioline;
typedef void (*pa_ioline_cb_t)(pa_ioline*io, const char *s, void *userdata);
+typedef void (*pa_ioline_drain_cb_t)(pa_ioline *io, void *userdata);
pa_ioline* pa_ioline_new(pa_iochannel *io);
void pa_ioline_unref(pa_ioline *l);
@@ -47,7 +48,17 @@ void pa_ioline_printf(pa_ioline *s, const char *format, ...) PA_GCC_PRINTF_ATTR(
/* Set the callback function that is called for every recieved line */
void pa_ioline_set_callback(pa_ioline*io, pa_ioline_cb_t callback, void *userdata);
+/* Set the callback function that is called when everything has been written */
+void pa_ioline_set_drain_callback(pa_ioline*io, pa_ioline_drain_cb_t callback, void *userdata);
+
/* Make sure to close the ioline object as soon as the send buffer is emptied */
void pa_ioline_defer_close(pa_ioline *io);
+/* Returns TRUE when everything was written */
+pa_bool_t pa_ioline_is_drained(pa_ioline *io);
+
+/* Detaches from the iochannel and returns it. Data that has already
+ * been read will not be available in the detached iochannel */
+pa_iochannel* pa_ioline_detach_iochannel(pa_ioline *l);
+
#endif
diff --git a/src/pulsecore/ipacl.h b/src/pulsecore/ipacl.h
index 7b7ffa61..a3661397 100644
--- a/src/pulsecore/ipacl.h
+++ b/src/pulsecore/ipacl.h
@@ -1,5 +1,5 @@
-#ifndef fooparseaddrhfoo
-#define fooparseaddrhfoo
+#ifndef foopulsecoreipaclhfoo
+#define foopulsecoreipaclhfoo
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/llist.h b/src/pulsecore/llist.h
index 77a1749f..43d1fbee 100644
--- a/src/pulsecore/llist.h
+++ b/src/pulsecore/llist.h
@@ -104,4 +104,10 @@
} \
} while (0)
+#define PA_LLIST_FOREACH(i,head) \
+ for (i = (head); i; i = i->next)
+
+#define PA_LLIST_FOREACH_FOR_DELETE(i,n,head) \
+ for (i = (head), n = i ? i->next : NULL; i; i = n, n = i ? i ->next : NULL)
+
#endif
diff --git a/src/pulsecore/log.c b/src/pulsecore/log.c
index d4d3b76e..8c21ee6c 100644
--- a/src/pulsecore/log.c
+++ b/src/pulsecore/log.c
@@ -38,6 +38,7 @@
#include <syslog.h>
#endif
+#include <pulse/rtclock.h>
#include <pulse/utf8.h>
#include <pulse/xmalloc.h>
#include <pulse/util.h>
@@ -45,7 +46,7 @@
#include <pulsecore/macro.h>
#include <pulsecore/core-util.h>
-#include <pulsecore/rtclock.h>
+#include <pulsecore/core-rtclock.h>
#include <pulsecore/once.h>
#include <pulsecore/ratelimit.h>
@@ -59,12 +60,13 @@
#define ENV_LOG_PRINT_META "PULSE_LOG_META"
#define ENV_LOG_PRINT_LEVEL "PULSE_LOG_LEVEL"
#define ENV_LOG_BACKTRACE "PULSE_LOG_BACKTRACE"
+#define ENV_LOG_BACKTRACE_SKIP "PULSE_LOG_BACKTRACE_SKIP"
static char *ident = NULL; /* in local charset format */
static pa_log_target_t target = PA_LOG_STDERR, target_override;
static pa_bool_t target_override_set = FALSE;
static pa_log_level_t maximum_level = PA_LOG_ERROR, maximum_level_override = PA_LOG_ERROR;
-static unsigned show_backtrace = 0, show_backtrace_override = 0;
+static unsigned show_backtrace = 0, show_backtrace_override = 0, skip_backtrace = 0;
static pa_log_flags_t flags = 0, flags_override = 0;
#ifdef HAVE_SYSLOG_H
@@ -128,13 +130,17 @@ void pa_log_set_show_backtrace(unsigned nlevels) {
show_backtrace = nlevels;
}
+void pa_log_set_skip_backtrace(unsigned nlevels) {
+ skip_backtrace = nlevels;
+}
+
#ifdef HAVE_EXECINFO_H
static char* get_backtrace(unsigned show_nframes) {
void* trace[32];
int n_frames;
char **symbols, *e, *r;
- unsigned j, n;
+ unsigned j, n, s;
size_t a;
pa_assert(show_nframes > 0);
@@ -149,14 +155,15 @@ static char* get_backtrace(unsigned show_nframes) {
if (!symbols)
return NULL;
- n = PA_MIN((unsigned) n_frames, show_nframes);
+ s = skip_backtrace;
+ n = PA_MIN((unsigned) n_frames, s + show_nframes);
a = 4;
- for (j = 0; j < n; j++) {
- if (j > 0)
+ for (j = s; j < n; j++) {
+ if (j > s)
a += 2;
- a += strlen(symbols[j]);
+ a += strlen(pa_path_get_filename(symbols[j]));
}
r = pa_xnew(char, a);
@@ -164,14 +171,18 @@ static char* get_backtrace(unsigned show_nframes) {
strcpy(r, " (");
e = r + 2;
- for (j = 0; j < n; j++) {
- if (j > 0) {
+ for (j = s; j < n; j++) {
+ const char *sym;
+
+ if (j > s) {
strcpy(e, "<<");
e += 2;
}
- strcpy(e, symbols[j]);
- e += strlen(symbols[j]);
+ sym = pa_path_get_filename(symbols[j]);
+
+ strcpy(e, sym);
+ e += strlen(sym);
}
strcpy(e, ")");
@@ -225,6 +236,13 @@ static void init_defaults(void) {
if (show_backtrace_override <= 0)
show_backtrace_override = 0;
}
+
+ if ((e = getenv(ENV_LOG_BACKTRACE_SKIP))) {
+ skip_backtrace = (unsigned) atoi(e);
+
+ if (skip_backtrace <= 0)
+ skip_backtrace = 0;
+ }
}
void pa_log_levelv_meta(
@@ -245,7 +263,7 @@ void pa_log_levelv_meta(
/* We don't use dynamic memory allocation here to minimize the hit
* in RT threads */
- char text[4096], location[128], timestamp[32];
+ char text[16*1024], location[128], timestamp[32];
pa_assert(level < PA_LOG_LEVEL_MAX);
pa_assert(format);
@@ -268,7 +286,7 @@ void pa_log_levelv_meta(
if ((_flags & PA_LOG_PRINT_META) && file && line > 0 && func)
pa_snprintf(location, sizeof(location), "[%s:%i %s()] ", file, line, func);
- else if (_flags & (PA_LOG_PRINT_META|PA_LOG_PRINT_FILE))
+ else if ((_flags & (PA_LOG_PRINT_META|PA_LOG_PRINT_FILE)) && file)
pa_snprintf(location, sizeof(location), "%s: ", pa_path_get_filename(file));
else
location[0] = 0;
@@ -277,7 +295,7 @@ void pa_log_levelv_meta(
static pa_usec_t start, last;
pa_usec_t u, a, r;
- u = pa_rtclock_usec();
+ u = pa_rtclock_now();
PA_ONCE_BEGIN {
start = u;
@@ -306,7 +324,7 @@ void pa_log_levelv_meta(
#endif
if (!pa_utf8_valid(text))
- pa_log_level(level, __FILE__": invalid UTF-8 string following below:");
+ pa_logl(level, "Invalid UTF-8 string following below:");
for (t = text; t; t = n) {
if ((n = strchr(t, '\n'))) {
diff --git a/src/pulsecore/log.h b/src/pulsecore/log.h
index 153e11e8..2f379f68 100644
--- a/src/pulsecore/log.h
+++ b/src/pulsecore/log.h
@@ -77,6 +77,9 @@ void pa_log_set_flags(pa_log_flags_t flags, pa_log_merge_t merge);
/* Enable backtrace */
void pa_log_set_show_backtrace(unsigned nlevels);
+/* Skip the first backtrace frames */
+void pa_log_set_skip_backtrace(unsigned nlevels);
+
void pa_log_level_meta(
pa_log_level_t level,
const char*file,
@@ -110,6 +113,7 @@ void pa_log_levelv(
#define pa_log_notice(...) pa_log_level_meta(PA_LOG_NOTICE, __FILE__, __LINE__, __func__, __VA_ARGS__)
#define pa_log_warn(...) pa_log_level_meta(PA_LOG_WARN, __FILE__, __LINE__, __func__, __VA_ARGS__)
#define pa_log_error(...) pa_log_level_meta(PA_LOG_ERROR, __FILE__, __LINE__, __func__, __VA_ARGS__)
+#define pa_logl(level, ...) pa_log_level_meta(level, __FILE__, __LINE__, __func__, __VA_ARGS__)
#else
diff --git a/src/pulsecore/ltdl-helper.h b/src/pulsecore/ltdl-helper.h
index 4c4f018a..9f346969 100644
--- a/src/pulsecore/ltdl-helper.h
+++ b/src/pulsecore/ltdl-helper.h
@@ -29,4 +29,3 @@ typedef void (*pa_void_func_t)(void);
pa_void_func_t pa_load_sym(lt_dlhandle handle, const char*module, const char *symbol);
#endif
-
diff --git a/src/pulsecore/macro.h b/src/pulsecore/macro.h
index 301d1d16..f1fd7d7f 100644
--- a/src/pulsecore/macro.h
+++ b/src/pulsecore/macro.h
@@ -29,6 +29,7 @@
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
#include <pulse/gccmacro.h>
@@ -57,18 +58,27 @@
#define PA_PAGE_SIZE ((size_t) 4096)
#endif
+/* Rounds down */
+static inline void* pa_align_ptr(const void *p) {
+ return (void*) (((size_t) p) & ~(sizeof(void*)-1));
+}
+#define PA_ALIGN_PTR(x) (pa_align_ptr(x))
+
+/* Rounds up */
static inline size_t pa_align(size_t l) {
return (((l + sizeof(void*) - 1) / sizeof(void*)) * sizeof(void*));
}
#define PA_ALIGN(x) (pa_align(x))
+/* Rounds down */
static inline void* pa_page_align_ptr(const void *p) {
return (void*) (((size_t) p) & ~(PA_PAGE_SIZE-1));
}
#define PA_PAGE_ALIGN_PTR(x) (pa_page_align_ptr(x))
+/* Rounds up */
static inline size_t pa_page_align(size_t l) {
- return l & ~(PA_PAGE_SIZE-1);
+ return ((l + PA_PAGE_SIZE - 1) / PA_PAGE_SIZE) * PA_PAGE_SIZE;
}
#define PA_PAGE_ALIGN(x) (pa_page_align(x))
@@ -164,8 +174,8 @@ typedef int pa_bool_t;
#define pa_return_null_if_fail(expr) pa_return_val_if_fail(expr, NULL)
-/* An assert which guarantees side effects of x, i.e. is never
- * optimized away */
+/* pa_assert_se() is an assert which guarantees side effects of x,
+ * i.e. is never optimized away, regardless of NDEBUG or FASTPATH. */
#define pa_assert_se(expr) \
do { \
if (PA_UNLIKELY(!(expr))) { \
@@ -174,18 +184,44 @@ typedef int pa_bool_t;
} \
} while (FALSE)
-/* An assert that may be optimized away by defining NDEBUG */
+/* Does exactly nothing */
+#define pa_nop() do {} while (FALSE)
+
+/* pa_assert() is an assert that may be optimized away by defining
+ * NDEBUG. pa_assert_fp() is an assert that may be optimized away by
+ * defining FASTPATH. It is supposed to be used in inner loops. It's
+ * there for extra paranoia checking and should probably be removed in
+ * production builds. */
#ifdef NDEBUG
-#define pa_assert(expr) do {} while (FALSE)
+#define pa_assert(expr) pa_nop()
+#define pa_assert_fp(expr) pa_nop()
+#elif defined (FASTPATH)
+#define pa_assert(expr) pa_assert_se(expr)
+#define pa_assert_fp(expr) pa_nop()
#else
#define pa_assert(expr) pa_assert_se(expr)
+#define pa_assert_fp(expr) pa_assert_se(expr)
#endif
+#ifdef NDEBUG
+#define pa_assert_not_reached() pa_nop()
+#else
#define pa_assert_not_reached() \
do { \
pa_log_error("Code should not be reached at %s:%u, function %s(). Aborting.", __FILE__, __LINE__, PA_PRETTY_FUNCTION); \
abort(); \
} while (FALSE)
+#endif
+
+/* A compile time assertion */
+#define pa_assert_cc(expr) \
+ do { \
+ switch (0) { \
+ case 0: \
+ case !!(expr): \
+ ; \
+ } \
+ } while (FALSE)
#define PA_PTR_TO_UINT(p) ((unsigned int) ((uintptr_t) (p)))
#define PA_UINT_TO_PTR(u) ((void*) ((uintptr_t) (u)))
@@ -226,7 +262,10 @@ typedef int pa_bool_t;
#define PA_DEBUG_TRAP raise(SIGTRAP)
#endif
-typedef void (*pa_function_t) (...);
+typedef void (*pa_function_t) (void);
+
+#define pa_memzero(x,l) (memset((x), 0, (l)))
+#define pa_zero(x) (pa_memzero(&(x), sizeof(x)))
/* We include this at the very last place */
#include <pulsecore/log.h>
diff --git a/src/pulsecore/memblock.c b/src/pulsecore/memblock.c
index fbf0a470..2c3f98a5 100644
--- a/src/pulsecore/memblock.c
+++ b/src/pulsecore/memblock.c
@@ -45,6 +45,7 @@
#include <pulsecore/macro.h>
#include <pulsecore/flist.h>
#include <pulsecore/core-util.h>
+#include <pulsecore/memtrap.h>
#include "memblock.h"
@@ -91,6 +92,7 @@ struct pa_memblock {
struct pa_memimport_segment {
pa_memimport *import;
pa_shm memory;
+ pa_memtrap *trap;
unsigned n_blocks;
};
@@ -255,7 +257,7 @@ 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_info("Pool full");
+ pa_log_debug("Pool full");
pa_atomic_inc(&p->stat.n_pool_full);
return NULL;
}
@@ -507,13 +509,16 @@ static void memblock_free(pa_memblock *b) {
/* FIXME! This should be implemented lock-free */
- segment = b->per_type.imported.segment;
- pa_assert(segment);
- import = segment->import;
- pa_assert(import);
+ pa_assert_se(segment = b->per_type.imported.segment);
+ pa_assert_se(import = segment->import);
pa_mutex_lock(import->mutex);
- pa_hashmap_remove(import->blocks, PA_UINT32_TO_PTR(b->per_type.imported.id));
+
+ pa_hashmap_remove(
+ import->blocks,
+ PA_UINT32_TO_PTR(b->per_type.imported.id));
+
+ pa_assert(segment->n_blocks >= 1);
if (-- segment->n_blocks <= 0)
segment_detach(segment);
@@ -523,6 +528,7 @@ static void memblock_free(pa_memblock *b) {
if (pa_flist_push(PA_STATIC_FLIST_GET(unused_memblocks), b) < 0)
pa_xfree(b);
+
break;
}
@@ -655,7 +661,8 @@ pa_memblock *pa_memblock_will_need(pa_memblock *b) {
/* Self-locked. This function is not multiple-caller safe */
static void memblock_replace_import(pa_memblock *b) {
- pa_memimport_segment *seg;
+ pa_memimport_segment *segment;
+ pa_memimport *import;
pa_assert(b);
pa_assert(b->type == PA_MEMBLOCK_IMPORTED);
@@ -665,23 +672,22 @@ static void memblock_replace_import(pa_memblock *b) {
pa_atomic_dec(&b->pool->stat.n_imported);
pa_atomic_sub(&b->pool->stat.imported_size, (int) b->length);
- seg = b->per_type.imported.segment;
- pa_assert(seg);
- pa_assert(seg->import);
+ pa_assert_se(segment = b->per_type.imported.segment);
+ pa_assert_se(import = segment->import);
- pa_mutex_lock(seg->import->mutex);
+ pa_mutex_lock(import->mutex);
pa_hashmap_remove(
- seg->import->blocks,
+ import->blocks,
PA_UINT32_TO_PTR(b->per_type.imported.id));
memblock_make_local(b);
- if (-- seg->n_blocks <= 0) {
- pa_mutex_unlock(seg->import->mutex);
- segment_detach(seg);
- } else
- pa_mutex_unlock(seg->import->mutex);
+ pa_assert(segment->n_blocks >= 1);
+ if (-- segment->n_blocks <= 0)
+ segment_detach(segment);
+
+ pa_mutex_unlock(import->mutex);
}
pa_mempool* pa_mempool_new(pa_bool_t shared, size_t size) {
@@ -745,8 +751,47 @@ void pa_mempool_free(pa_mempool *p) {
pa_flist_free(p->free_slots, NULL);
if (pa_atomic_load(&p->stat.n_allocated) > 0) {
-/* raise(SIGTRAP); */
- pa_log_warn("Memory pool destroyed but not all memory blocks freed! %u remain.", pa_atomic_load(&p->stat.n_allocated));
+
+ /* Ouch, somebody is retaining a memory block reference! */
+
+#ifdef DEBUG_REF
+ unsigned i;
+ pa_flist *list;
+
+ /* Let's try to find at least one of those leaked memory blocks */
+
+ list = pa_flist_new(p->n_blocks);
+
+ for (i = 0; i < (unsigned) pa_atomic_load(&p->n_init); i++) {
+ struct mempool_slot *slot;
+ pa_memblock *b, *k;
+
+ slot = (struct mempool_slot*) ((uint8_t*) p->memory.ptr + (p->block_size * (size_t) i));
+ b = mempool_slot_data(slot);
+
+ while ((k = pa_flist_pop(p->free_slots))) {
+ while (pa_flist_push(list, k) < 0)
+ ;
+
+ if (b == k)
+ break;
+ }
+
+ if (!k)
+ pa_log("REF: Leaked memory block %p", b);
+
+ while ((k = pa_flist_pop(list)))
+ while (pa_flist_push(p->free_slots, k) < 0)
+ ;
+ }
+
+ pa_flist_free(list, NULL);
+
+#endif
+
+ pa_log_error("Memory pool destroyed but not all memory blocks freed! %u remain.", pa_atomic_load(&p->stat.n_allocated));
+
+/* PA_DEBUG_TRAP; */
}
pa_shm_free(&p->memory);
@@ -853,6 +898,7 @@ 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);
return seg;
@@ -864,6 +910,10 @@ static void segment_detach(pa_memimport_segment *seg) {
pa_hashmap_remove(seg->import->segments, PA_UINT32_TO_PTR(seg->memory.id));
pa_shm_free(&seg->memory);
+
+ if (seg->trap)
+ pa_memtrap_remove(seg->trap);
+
pa_xfree(seg);
}
diff --git a/src/pulsecore/memblockq.c b/src/pulsecore/memblockq.c
index e2be42b3..77f9efc9 100644
--- a/src/pulsecore/memblockq.c
+++ b/src/pulsecore/memblockq.c
@@ -90,8 +90,8 @@ pa_memblockq* pa_memblockq_new(
pa_memblockq_set_maxlength(bq, maxlength);
pa_memblockq_set_tlength(bq, tlength);
- pa_memblockq_set_prebuf(bq, prebuf);
pa_memblockq_set_minreq(bq, minreq);
+ pa_memblockq_set_prebuf(bq, prebuf);
pa_memblockq_set_maxrewind(bq, maxrewind);
pa_log_debug("memblockq sanitized: maxlength=%lu, tlength=%lu, base=%lu, prebuf=%lu, minreq=%lu maxrewind=%lu",
@@ -601,7 +601,7 @@ size_t pa_memblockq_missing(pa_memblockq *bq) {
return l >= bq->minreq ? l : 0;
}
-void pa_memblockq_seek(pa_memblockq *bq, int64_t offset, pa_seek_mode_t seek) {
+void pa_memblockq_seek(pa_memblockq *bq, int64_t offset, pa_seek_mode_t seek, pa_bool_t account) {
int64_t old, delta;
pa_assert(bq);
@@ -628,12 +628,14 @@ void pa_memblockq_seek(pa_memblockq *bq, int64_t offset, pa_seek_mode_t seek) {
delta = bq->write_index - old;
- if (delta >= (int64_t) bq->requested) {
- delta -= (int64_t) bq->requested;
- bq->requested = 0;
- } else if (delta >= 0) {
- bq->requested -= (size_t) delta;
- delta = 0;
+ if (account) {
+ if (delta >= (int64_t) bq->requested) {
+ delta -= (int64_t) bq->requested;
+ bq->requested = 0;
+ } else if (delta >= 0) {
+ bq->requested -= (size_t) delta;
+ delta = 0;
+ }
}
bq->missing -= delta;
@@ -782,16 +784,13 @@ void pa_memblockq_set_maxlength(pa_memblockq *bq, size_t maxlength) {
if (bq->tlength > bq->maxlength)
pa_memblockq_set_tlength(bq, bq->maxlength);
-
- if (bq->prebuf > bq->maxlength)
- pa_memblockq_set_prebuf(bq, bq->maxlength);
}
void pa_memblockq_set_tlength(pa_memblockq *bq, size_t tlength) {
size_t old_tlength;
pa_assert(bq);
- if (tlength <= 0)
+ if (tlength <= 0 || tlength == (size_t) -1)
tlength = bq->maxlength;
old_tlength = bq->tlength;
@@ -800,55 +799,72 @@ void pa_memblockq_set_tlength(pa_memblockq *bq, size_t tlength) {
if (bq->tlength > bq->maxlength)
bq->tlength = bq->maxlength;
- if (bq->prebuf > bq->tlength)
- pa_memblockq_set_prebuf(bq, bq->tlength);
-
if (bq->minreq > bq->tlength)
pa_memblockq_set_minreq(bq, bq->tlength);
+ if (bq->prebuf > bq->tlength+bq->base-bq->minreq)
+ pa_memblockq_set_prebuf(bq, bq->tlength+bq->base-bq->minreq);
+
bq->missing += (int64_t) bq->tlength - (int64_t) old_tlength;
}
+void pa_memblockq_set_minreq(pa_memblockq *bq, size_t minreq) {
+ pa_assert(bq);
+
+ bq->minreq = (minreq/bq->base)*bq->base;
+
+ if (bq->minreq > bq->tlength)
+ bq->minreq = bq->tlength;
+
+ if (bq->minreq < bq->base)
+ bq->minreq = bq->base;
+
+ if (bq->prebuf > bq->tlength+bq->base-bq->minreq)
+ pa_memblockq_set_prebuf(bq, bq->tlength+bq->base-bq->minreq);
+}
+
void pa_memblockq_set_prebuf(pa_memblockq *bq, size_t prebuf) {
pa_assert(bq);
if (prebuf == (size_t) -1)
- prebuf = bq->tlength;
+ prebuf = bq->tlength+bq->base-bq->minreq;
bq->prebuf = ((prebuf+bq->base-1)/bq->base)*bq->base;
if (prebuf > 0 && bq->prebuf < bq->base)
bq->prebuf = bq->base;
- if (bq->prebuf > bq->tlength)
- bq->prebuf = bq->tlength;
+ if (bq->prebuf > bq->tlength+bq->base-bq->minreq)
+ bq->prebuf = bq->tlength+bq->base-bq->minreq;
if (bq->prebuf <= 0 || pa_memblockq_get_length(bq) >= bq->prebuf)
bq->in_prebuf = FALSE;
-
- if (bq->minreq > bq->prebuf)
- pa_memblockq_set_minreq(bq, bq->prebuf);
}
-void pa_memblockq_set_minreq(pa_memblockq *bq, size_t minreq) {
+void pa_memblockq_set_maxrewind(pa_memblockq *bq, size_t maxrewind) {
pa_assert(bq);
- bq->minreq = (minreq/bq->base)*bq->base;
-
- if (bq->minreq > bq->tlength)
- bq->minreq = bq->tlength;
+ bq->maxrewind = (maxrewind/bq->base)*bq->base;
+}
- if (bq->minreq > bq->prebuf)
- bq->minreq = bq->prebuf;
+void pa_memblockq_apply_attr(pa_memblockq *bq, const pa_buffer_attr *a) {
+ pa_assert(bq);
+ pa_assert(a);
- if (bq->minreq < bq->base)
- bq->minreq = bq->base;
+ pa_memblockq_set_maxlength(bq, a->maxlength);
+ pa_memblockq_set_tlength(bq, a->tlength);
+ pa_memblockq_set_prebuf(bq, a->prebuf);
+ pa_memblockq_set_minreq(bq, a->minreq);
}
-void pa_memblockq_set_maxrewind(pa_memblockq *bq, size_t maxrewind) {
+void pa_memblockq_get_attr(pa_memblockq *bq, pa_buffer_attr *a) {
pa_assert(bq);
+ pa_assert(a);
- bq->maxrewind = (maxrewind/bq->base)*bq->base;
+ a->maxlength = (uint32_t) pa_memblockq_get_maxlength(bq);
+ a->tlength = (uint32_t) pa_memblockq_get_tlength(bq);
+ a->prebuf = (uint32_t) pa_memblockq_get_prebuf(bq);
+ a->minreq = (uint32_t) pa_memblockq_get_minreq(bq);
}
int pa_memblockq_splice(pa_memblockq *bq, pa_memblockq *source) {
@@ -875,7 +891,7 @@ int pa_memblockq_splice(pa_memblockq *bq, pa_memblockq *source) {
pa_memblock_unref(chunk.memblock);
} else
- pa_memblockq_seek(bq, (int64_t) chunk.length, PA_SEEK_RELATIVE);
+ pa_memblockq_seek(bq, (int64_t) chunk.length, PA_SEEK_RELATIVE, TRUE);
pa_memblockq_drop(bq, chunk.length);
}
diff --git a/src/pulsecore/memblockq.h b/src/pulsecore/memblockq.h
index 0a74aa37..146d261b 100644
--- a/src/pulsecore/memblockq.h
+++ b/src/pulsecore/memblockq.h
@@ -85,7 +85,7 @@ int pa_memblockq_push(pa_memblockq* bq, const pa_memchunk *chunk);
int pa_memblockq_push_align(pa_memblockq* bq, const pa_memchunk *chunk);
/* Manipulate the write pointer */
-void pa_memblockq_seek(pa_memblockq *bq, int64_t offset, pa_seek_mode_t seek);
+void pa_memblockq_seek(pa_memblockq *bq, int64_t offset, pa_seek_mode_t seek, pa_bool_t account);
/* Return a copy of the next memory chunk in the queue. It is not
* removed from the queue. There are two reasons this function might
@@ -158,6 +158,10 @@ void pa_memblockq_set_minreq(pa_memblockq *memblockq, size_t minreq);
void pa_memblockq_set_maxrewind(pa_memblockq *memblockq, size_t maxrewind); /* Set the maximum history size */
void pa_memblockq_set_silence(pa_memblockq *memblockq, pa_memchunk *silence);
+/* Apply the data from pa_buffer_attr */
+void pa_memblockq_apply_attr(pa_memblockq *memblockq, const pa_buffer_attr *a);
+void pa_memblockq_get_attr(pa_memblockq *bq, pa_buffer_attr *a);
+
/* Call pa_memchunk_willneed() for every chunk in the queue from the current read pointer to the end */
void pa_memblockq_willneed(pa_memblockq *bq);
@@ -175,5 +179,4 @@ pa_bool_t pa_memblockq_prebuf_active(pa_memblockq *bq);
/* Return how many items are currently stored in the queue */
unsigned pa_memblockq_get_nblocks(pa_memblockq *bq);
-
#endif
diff --git a/src/pulsecore/memtrap.c b/src/pulsecore/memtrap.c
new file mode 100644
index 00000000..c647e507
--- /dev/null
+++ b/src/pulsecore/memtrap.c
@@ -0,0 +1,230 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2009 Lennart Poettering
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <signal.h>
+#include <sys/mman.h>
+
+/* This is deprecated on glibc but is still used by FreeBSD */
+#if !defined(MAP_ANONYMOUS) && defined(MAP_ANON)
+# define MAP_ANONYMOUS MAP_ANON
+#endif
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/aupdate.h>
+#include <pulsecore/atomic.h>
+#include <pulsecore/once.h>
+#include <pulsecore/mutex.h>
+
+#include "memtrap.h"
+
+struct pa_memtrap {
+ void *start;
+ size_t size;
+ pa_atomic_t bad;
+ pa_memtrap *next[2], *prev[2];
+};
+
+static pa_memtrap *memtraps[2] = { NULL, NULL };
+static pa_aupdate *aupdate;
+static pa_static_mutex mutex = PA_STATIC_MUTEX_INIT; /* only required to serialize access to the write side */
+
+static void allocate_aupdate(void) {
+ PA_ONCE_BEGIN {
+ aupdate = pa_aupdate_new();
+ } PA_ONCE_END;
+}
+
+pa_bool_t pa_memtrap_is_good(pa_memtrap *m) {
+ pa_assert(m);
+
+ return !pa_atomic_load(&m->bad);
+}
+
+static void sigsafe_error(const char *s) {
+ (void) write(STDERR_FILENO, s, strlen(s));
+}
+
+static void signal_handler(int sig, siginfo_t* si, void *data) {
+ unsigned j;
+ pa_memtrap *m;
+ void *r;
+
+ j = pa_aupdate_read_begin(aupdate);
+
+ for (m = memtraps[j]; m; m = m->next[j])
+ if (si->si_addr >= m->start &&
+ (uint8_t*) si->si_addr < (uint8_t*) m->start + m->size)
+ break;
+
+ if (!m)
+ goto fail;
+
+ pa_atomic_store(&m->bad, 1);
+
+ /* Remap anonymous memory into the bad segment */
+ if ((r = mmap(m->start, m->size, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_FIXED|MAP_PRIVATE, -1, 0)) == MAP_FAILED) {
+ sigsafe_error("mmap() failed.\n");
+ goto fail;
+ }
+
+ pa_assert(r == m->start);
+
+ pa_aupdate_read_end(aupdate);
+ return;
+
+fail:
+ pa_aupdate_read_end(aupdate);
+
+ sigsafe_error("Failed to handle SIGBUS.\n");
+ abort();
+}
+
+static void memtrap_link(pa_memtrap *m, unsigned j) {
+ pa_assert(m);
+
+ m->prev[j] = NULL;
+ m->next[j] = memtraps[j];
+ memtraps[j] = m;
+}
+
+static void memtrap_unlink(pa_memtrap *m, unsigned j) {
+ pa_assert(m);
+
+ if (m->next[j])
+ m->next[j]->prev[j] = m->prev[j];
+
+ if (m->prev[j])
+ m->prev[j]->next[j] = m->next[j];
+ else
+ memtraps[j] = m->next[j];
+}
+
+pa_memtrap* pa_memtrap_add(const void *start, size_t size) {
+ pa_memtrap *m = NULL;
+ unsigned j;
+ pa_mutex *mx;
+
+ pa_assert(start);
+ pa_assert(size > 0);
+
+ start = PA_PAGE_ALIGN_PTR(start);
+ size = PA_PAGE_ALIGN(size);
+
+ m = pa_xnew(pa_memtrap, 1);
+ m->start = (void*) start;
+ m->size = size;
+ pa_atomic_store(&m->bad, 0);
+
+ allocate_aupdate();
+
+ mx = pa_static_mutex_get(&mutex, FALSE, TRUE);
+ pa_mutex_lock(mx);
+
+ j = pa_aupdate_write_begin(aupdate);
+ memtrap_link(m, j);
+ j = pa_aupdate_write_swap(aupdate);
+ memtrap_link(m, j);
+ pa_aupdate_write_end(aupdate);
+
+ pa_mutex_unlock(mx);
+
+ return m;
+}
+
+void pa_memtrap_remove(pa_memtrap *m) {
+ unsigned j;
+ pa_mutex *mx;
+
+ pa_assert(m);
+
+ allocate_aupdate();
+
+ mx = pa_static_mutex_get(&mutex, FALSE, TRUE);
+ pa_mutex_lock(mx);
+
+ j = pa_aupdate_write_begin(aupdate);
+ memtrap_unlink(m, j);
+ j = pa_aupdate_write_swap(aupdate);
+ memtrap_unlink(m, j);
+ pa_aupdate_write_end(aupdate);
+
+ pa_mutex_unlock(mx);
+
+ pa_xfree(m);
+}
+
+pa_memtrap *pa_memtrap_update(pa_memtrap *m, const void *start, size_t size) {
+ unsigned j;
+ pa_mutex *mx;
+
+ pa_assert(m);
+
+ pa_assert(start);
+ pa_assert(size > 0);
+
+ start = PA_PAGE_ALIGN_PTR(start);
+ size = PA_PAGE_ALIGN(size);
+
+ allocate_aupdate();
+
+ mx = pa_static_mutex_get(&mutex, FALSE, TRUE);
+ pa_mutex_lock(mx);
+
+ j = pa_aupdate_write_begin(aupdate);
+
+ if (m->start == start && m->size == size)
+ goto unlock;
+
+ memtrap_unlink(m, j);
+ j = pa_aupdate_write_swap(aupdate);
+
+ m->start = (void*) start;
+ m->size = size;
+ pa_atomic_store(&m->bad, 0);
+
+ j = pa_aupdate_write_swap(aupdate);
+ memtrap_link(m, j);
+
+unlock:
+ pa_aupdate_write_end(aupdate);
+
+ pa_mutex_unlock(mx);
+
+ return m;
+}
+
+void pa_memtrap_install(void) {
+ struct sigaction sa;
+
+ allocate_aupdate();
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_sigaction = signal_handler;
+ sa.sa_flags = SA_RESTART|SA_SIGINFO;
+
+ pa_assert_se(sigaction(SIGBUS, &sa, NULL) == 0);
+}
diff --git a/src/pulsecore/memtrap.h b/src/pulsecore/memtrap.h
new file mode 100644
index 00000000..fa38da58
--- /dev/null
+++ b/src/pulsecore/memtrap.h
@@ -0,0 +1,51 @@
+#ifndef foopulsecorememtraphfoo
+#define foopulsecorememtraphfoo
+
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2009 Lennart Poettering
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <sys/types.h>
+
+#include <pulsecore/macro.h>
+
+/* This subsystem will trap SIGBUS on specific memory regions. The
+ * regions will be remapped to anonymous memory (i.e. writable NUL
+ * bytes) on SIGBUS, so that execution of the main program can
+ * continue though with memory having changed beneath its hands. With
+ * pa_memtrap_is_good() it is possible to query if a memory region is
+ * still 'good' i.e. no SIGBUS has happened yet for it.
+ *
+ * Intended usage is to handle memory mapped in which is controlled by
+ * other processes that might execute ftruncate() or when mapping inb
+ * hardware resources that might get invalidated when unplugged. */
+
+typedef struct pa_memtrap pa_memtrap;
+
+pa_memtrap* pa_memtrap_add(const void *start, size_t size);
+pa_memtrap *pa_memtrap_update(pa_memtrap *m, const void *start, size_t size);
+
+void pa_memtrap_remove(pa_memtrap *m);
+
+pa_bool_t pa_memtrap_is_good(pa_memtrap *m);
+
+void pa_memtrap_install(void);
+
+#endif
diff --git a/src/pulsecore/mime-type.c b/src/pulsecore/mime-type.c
new file mode 100644
index 00000000..b9fe9444
--- /dev/null
+++ b/src/pulsecore/mime-type.c
@@ -0,0 +1,182 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2005-2009 Lennart Poettering
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2.1 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulse/xmalloc.h>
+#include <pulsecore/core-util.h>
+
+#include "mime-type.h"
+
+pa_bool_t pa_sample_spec_is_mime(const pa_sample_spec *ss, const pa_channel_map *cm) {
+
+ pa_assert(pa_channel_map_compatible(cm, ss));
+
+ switch (ss->format) {
+ case PA_SAMPLE_S16BE:
+ case PA_SAMPLE_S24BE:
+ case PA_SAMPLE_U8:
+
+ if (ss->rate != 8000 &&
+ ss->rate != 11025 &&
+ ss->rate != 16000 &&
+ ss->rate != 22050 &&
+ ss->rate != 24000 &&
+ ss->rate != 32000 &&
+ ss->rate != 44100 &&
+ ss->rate != 48000)
+ return FALSE;
+
+ if (ss->channels != 1 &&
+ ss->channels != 2)
+ return FALSE;
+
+ if ((cm->channels == 1 && cm->map[0] != PA_CHANNEL_POSITION_MONO) ||
+ (cm->channels == 2 && (cm->map[0] != PA_CHANNEL_POSITION_LEFT || cm->map[1] != PA_CHANNEL_POSITION_RIGHT)))
+ return FALSE;
+
+ return TRUE;
+
+ case PA_SAMPLE_ULAW:
+
+ if (ss->rate != 8000)
+ return FALSE;
+
+ if (ss->channels != 1)
+ return FALSE;
+
+ if (cm->map[0] != PA_CHANNEL_POSITION_MONO)
+ return FALSE;
+
+ return TRUE;
+
+ default:
+ return FALSE;
+ }
+}
+
+void pa_sample_spec_mimefy(pa_sample_spec *ss, pa_channel_map *cm) {
+
+ pa_assert(pa_channel_map_compatible(cm, ss));
+
+ /* Turns the sample type passed in into the next 'better' one that
+ * can be encoded for HTTP. If there is no 'better' one we pick
+ * the 'best' one that is 'worse'. */
+
+ if (ss->channels > 2)
+ ss->channels = 2;
+
+ if (ss->rate > 44100)
+ ss->rate = 48000;
+ else if (ss->rate > 32000)
+ ss->rate = 44100;
+ else if (ss->rate > 24000)
+ ss->rate = 32000;
+ else if (ss->rate > 22050)
+ ss->rate = 24000;
+ else if (ss->rate > 16000)
+ ss->rate = 22050;
+ else if (ss->rate > 11025)
+ ss->rate = 16000;
+ else if (ss->rate > 8000)
+ ss->rate = 11025;
+ else
+ ss->rate = 8000;
+
+ switch (ss->format) {
+ case PA_SAMPLE_S24BE:
+ case PA_SAMPLE_S24LE:
+ case PA_SAMPLE_S24_32LE:
+ case PA_SAMPLE_S24_32BE:
+ case PA_SAMPLE_S32LE:
+ case PA_SAMPLE_S32BE:
+ case PA_SAMPLE_FLOAT32LE:
+ case PA_SAMPLE_FLOAT32BE:
+ ss->format = PA_SAMPLE_S24BE;
+ break;
+
+ case PA_SAMPLE_S16BE:
+ case PA_SAMPLE_S16LE:
+ ss->format = PA_SAMPLE_S16BE;
+ break;
+
+ case PA_SAMPLE_ULAW:
+ case PA_SAMPLE_ALAW:
+
+ if (ss->rate == 8000 && ss->channels == 1)
+ ss->format = PA_SAMPLE_ULAW;
+ else
+ ss->format = PA_SAMPLE_S16BE;
+ break;
+
+ case PA_SAMPLE_U8:
+ ss->format = PA_SAMPLE_U8;
+ break;
+
+ case PA_SAMPLE_MAX:
+ case PA_SAMPLE_INVALID:
+ pa_assert_not_reached();
+ }
+
+ pa_channel_map_init_auto(cm, ss->channels, PA_CHANNEL_MAP_DEFAULT);
+
+ pa_assert(pa_sample_spec_is_mime(ss, cm));
+}
+
+char *pa_sample_spec_to_mime_type(const pa_sample_spec *ss, const pa_channel_map *cm) {
+ pa_assert(pa_channel_map_compatible(cm, ss));
+
+ if (!pa_sample_spec_is_mime(ss, cm))
+ return NULL;
+
+ switch (ss->format) {
+
+ case PA_SAMPLE_S16BE:
+ case PA_SAMPLE_S24BE:
+ case PA_SAMPLE_U8:
+ /* Stupid UPnP implementations (PS3...) choke on spaces in
+ * the mime type, that's why we write only ';' here,
+ * instead of '; '. */
+ return pa_sprintf_malloc("audio/%s;rate=%u;channels=%u",
+ ss->format == PA_SAMPLE_S16BE ? "L16" :
+ (ss->format == PA_SAMPLE_S24BE ? "L24" : "L8"),
+ ss->rate, ss->channels);
+
+ case PA_SAMPLE_ULAW:
+ return pa_xstrdup("audio/basic");
+
+ default:
+ pa_assert_not_reached();
+ }
+
+ pa_assert(pa_sample_spec_valid(ss));
+}
+
+char *pa_sample_spec_to_mime_type_mimefy(const pa_sample_spec *_ss, const pa_channel_map *_cm) {
+ pa_sample_spec ss = *_ss;
+ pa_channel_map cm = *_cm;
+
+ pa_sample_spec_mimefy(&ss, &cm);
+
+ return pa_sample_spec_to_mime_type(&ss, &cm);
+}
diff --git a/src/pulsecore/mime-type.h b/src/pulsecore/mime-type.h
new file mode 100644
index 00000000..db77379b
--- /dev/null
+++ b/src/pulsecore/mime-type.h
@@ -0,0 +1,37 @@
+#ifndef foopulsecoremimetypehfoo
+#define foopulsecoremimetypehfoo
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2009 Lennart Poettering
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2.1 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulsecore/macro.h>
+#include <pulse/sample.h>
+#include <pulse/channelmap.h>
+
+pa_bool_t pa_sample_spec_is_mime(const pa_sample_spec *ss, const pa_channel_map *cm);
+void pa_sample_spec_mimefy(pa_sample_spec *ss, pa_channel_map *cm);
+char *pa_sample_spec_to_mime_type(const pa_sample_spec *ss, const pa_channel_map *cm);
+char *pa_sample_spec_to_mime_type_mimefy(const pa_sample_spec *_ss, const pa_channel_map *_cm);
+
+#endif
diff --git a/src/pulsecore/modargs.c b/src/pulsecore/modargs.c
index 73c67a8b..c7d734d9 100644
--- a/src/pulsecore/modargs.c
+++ b/src/pulsecore/modargs.c
@@ -84,8 +84,11 @@ pa_modargs *pa_modargs_new(const char *args, const char* const* valid_keys) {
KEY,
VALUE_START,
VALUE_SIMPLE,
+ VALUE_SIMPLE_ESCAPED,
VALUE_DOUBLE_QUOTES,
- VALUE_TICKS
+ VALUE_DOUBLE_QUOTES_ESCAPED,
+ VALUE_TICKS,
+ VALUE_TICKS_ESCAPED
} state;
const char *p, *key = NULL, *value = NULL;
@@ -131,9 +134,16 @@ pa_modargs *pa_modargs_new(const char *args, const char* const* valid_keys) {
value = p+1;
value_len = 0;
} else if (isspace(*p)) {
- if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrdup(""), valid_keys) < 0)
+ if (add_key_value(map,
+ pa_xstrndup(key, key_len),
+ pa_xstrdup(""),
+ valid_keys) < 0)
goto fail;
state = WHITESPACE;
+ } else if (*p == '\\') {
+ state = VALUE_SIMPLE_ESCAPED;
+ value = p;
+ value_len = 1;
} else {
state = VALUE_SIMPLE;
value = p;
@@ -143,30 +153,63 @@ pa_modargs *pa_modargs_new(const char *args, const char* const* valid_keys) {
case VALUE_SIMPLE:
if (isspace(*p)) {
- if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrndup(value, value_len), valid_keys) < 0)
+ if (add_key_value(map,
+ pa_xstrndup(key, key_len),
+ pa_unescape(pa_xstrndup(value, value_len)),
+ valid_keys) < 0)
goto fail;
state = WHITESPACE;
+ } else if (*p == '\\') {
+ state = VALUE_SIMPLE_ESCAPED;
+ value_len++;
} else
value_len++;
break;
+ case VALUE_SIMPLE_ESCAPED:
+ state = VALUE_SIMPLE;
+ value_len++;
+ break;
+
case VALUE_DOUBLE_QUOTES:
if (*p == '"') {
- if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrndup(value, value_len), valid_keys) < 0)
+ if (add_key_value(map,
+ pa_xstrndup(key, key_len),
+ pa_unescape(pa_xstrndup(value, value_len)),
+ valid_keys) < 0)
goto fail;
state = WHITESPACE;
+ } else if (*p == '\\') {
+ state = VALUE_DOUBLE_QUOTES_ESCAPED;
+ value_len++;
} else
value_len++;
break;
+ case VALUE_DOUBLE_QUOTES_ESCAPED:
+ state = VALUE_DOUBLE_QUOTES;
+ value_len++;
+ break;
+
case VALUE_TICKS:
if (*p == '\'') {
- if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrndup(value, value_len), valid_keys) < 0)
+ if (add_key_value(map,
+ pa_xstrndup(key, key_len),
+ pa_unescape(pa_xstrndup(value, value_len)),
+ valid_keys) < 0)
goto fail;
state = WHITESPACE;
+ } else if (*p == '\\') {
+ state = VALUE_TICKS_ESCAPED;
+ value_len++;
} else
value_len++;
break;
+
+ case VALUE_TICKS_ESCAPED:
+ state = VALUE_TICKS;
+ value_len++;
+ break;
}
}
@@ -352,3 +395,23 @@ int pa_modargs_get_sample_spec_and_channel_map(
return 0;
}
+
+int pa_modargs_get_proplist(pa_modargs *ma, const char *name, pa_proplist *p, pa_update_mode_t m) {
+ const char *v;
+ pa_proplist *n;
+
+ pa_assert(ma);
+ pa_assert(name);
+ pa_assert(p);
+
+ if (!(v = pa_modargs_get_value(ma, name, NULL)))
+ return 0;
+
+ if (!(n = pa_proplist_from_string(v)))
+ return -1;
+
+ pa_proplist_update(p, m, n);
+ pa_proplist_free(n);
+
+ return 0;
+}
diff --git a/src/pulsecore/modargs.h b/src/pulsecore/modargs.h
index 809fb27e..b3125b10 100644
--- a/src/pulsecore/modargs.h
+++ b/src/pulsecore/modargs.h
@@ -58,4 +58,6 @@ structure if no channel_map is found, using pa_channel_map_init_auto() */
int pa_modargs_get_sample_spec_and_channel_map(pa_modargs *ma, pa_sample_spec *ss, pa_channel_map *map, pa_channel_map_def_t def);
+int pa_modargs_get_proplist(pa_modargs *ma, const char *name, pa_proplist *p, pa_update_mode_t m);
+
#endif
diff --git a/src/pulsecore/modinfo.c b/src/pulsecore/modinfo.c
index 00fb9c43..b5ee9f56 100644
--- a/src/pulsecore/modinfo.c
+++ b/src/pulsecore/modinfo.c
@@ -38,6 +38,7 @@
#define PA_SYMBOL_DESCRIPTION "pa__get_description"
#define PA_SYMBOL_USAGE "pa__get_usage"
#define PA_SYMBOL_VERSION "pa__get_version"
+#define PA_SYMBOL_DEPRECATED "pa__get_deprecated"
#define PA_SYMBOL_LOAD_ONCE "pa__load_once"
pa_modinfo *pa_modinfo_get_by_handle(lt_dlhandle dl, const char *module_name) {
@@ -61,6 +62,9 @@ pa_modinfo *pa_modinfo_get_by_handle(lt_dlhandle dl, const char *module_name) {
if ((func = (const char* (*)(void)) pa_load_sym(dl, module_name, PA_SYMBOL_VERSION)))
i->version = pa_xstrdup(func());
+ if ((func = (const char* (*)(void)) pa_load_sym(dl, module_name, PA_SYMBOL_DEPRECATED)))
+ i->deprecated = pa_xstrdup(func());
+
if ((func2 = (pa_bool_t (*)(void)) pa_load_sym(dl, module_name, PA_SYMBOL_LOAD_ONCE)))
i->load_once = func2();
@@ -91,5 +95,6 @@ void pa_modinfo_free(pa_modinfo *i) {
pa_xfree(i->description);
pa_xfree(i->usage);
pa_xfree(i->version);
+ pa_xfree(i->deprecated);
pa_xfree(i);
}
diff --git a/src/pulsecore/modinfo.h b/src/pulsecore/modinfo.h
index 407e602a..baad0de7 100644
--- a/src/pulsecore/modinfo.h
+++ b/src/pulsecore/modinfo.h
@@ -30,6 +30,7 @@ typedef struct pa_modinfo {
char *description;
char *usage;
char *version;
+ char *deprecated;
pa_bool_t load_once;
} pa_modinfo;
diff --git a/src/pulsecore/module.c b/src/pulsecore/module.c
index 42fd912c..5bcdd898 100644
--- a/src/pulsecore/module.c
+++ b/src/pulsecore/module.c
@@ -48,10 +48,12 @@
#define PA_SYMBOL_DONE "pa__done"
#define PA_SYMBOL_LOAD_ONCE "pa__load_once"
#define PA_SYMBOL_GET_N_USED "pa__get_n_used"
+#define PA_SYMBOL_GET_DEPRECATE "pa__get_deprecated"
pa_module* pa_module_load(pa_core *c, const char *name, const char *argument) {
pa_module *m = NULL;
pa_bool_t (*load_once)(void);
+ const char* (*get_deprecated)(void);
pa_modinfo *mi;
pa_assert(c);
@@ -89,6 +91,13 @@ pa_module* pa_module_load(pa_core *c, const char *name, const char *argument) {
}
}
+ if ((get_deprecated = (const char* (*) (void)) pa_load_sym(m->dl, name, PA_SYMBOL_GET_DEPRECATE))) {
+ const char *t;
+
+ if ((t = get_deprecated()))
+ pa_log_warn("%s is deprecated: %s", name, t);
+ }
+
if (!(m->init = (int (*)(pa_module*_m)) pa_load_sym(m->dl, name, PA_SYMBOL_INIT))) {
pa_log("Failed to load module \"%s\": symbol \""PA_SYMBOL_INIT"\" not found.", name);
goto fail;
diff --git a/src/pulsecore/module.h b/src/pulsecore/module.h
index 3f697348..af89d793 100644
--- a/src/pulsecore/module.h
+++ b/src/pulsecore/module.h
@@ -78,6 +78,10 @@ int pa_module_get_n_used(pa_module*m);
const char * pa__get_version(void) { return s; } \
struct __stupid_useless_struct_to_allow_trailing_semicolon
+#define PA_MODULE_DEPRECATED(s) \
+ const char * pa__get_deprecated(void) { return s; } \
+ struct __stupid_useless_struct_to_allow_trailing_semicolon
+
#define PA_MODULE_LOAD_ONCE(b) \
pa_bool_t pa__load_once(void) { return b; } \
struct __stupid_useless_struct_to_allow_trailing_semicolon
diff --git a/src/pulsecore/mutex-posix.c b/src/pulsecore/mutex-posix.c
index b3e5256a..0ff4bee6 100644
--- a/src/pulsecore/mutex-posix.c
+++ b/src/pulsecore/mutex-posix.c
@@ -153,6 +153,8 @@ pa_mutex* pa_static_mutex_get(pa_static_mutex *s, pa_bool_t recursive, pa_bool_t
if ((pa_atomic_ptr_cmpxchg(&s->ptr, NULL, m)))
return m;
+ pa_mutex_free(m);
+
/* Him, filling in failed, so someone else must have filled in
* already */
pa_assert_se(m = pa_atomic_ptr_load(&s->ptr));
diff --git a/src/pulsecore/mutex.h b/src/pulsecore/mutex.h
index a4dd6738..b1edc132 100644
--- a/src/pulsecore/mutex.h
+++ b/src/pulsecore/mutex.h
@@ -46,10 +46,14 @@ void pa_cond_free(pa_cond *c);
void pa_cond_signal(pa_cond *c, int broadcast);
int pa_cond_wait(pa_cond *c, pa_mutex *m);
+/* Static mutexes are basically just atomically updated pointers to pa_mutex objects */
+
typedef struct pa_static_mutex {
pa_atomic_ptr_t ptr;
} pa_static_mutex;
+#define PA_STATIC_MUTEX_INIT { PA_ATOMIC_PTR_INIT(NULL) }
+
pa_mutex* pa_static_mutex_get(pa_static_mutex *m, pa_bool_t recursive, pa_bool_t inherit_priority);
#endif
diff --git a/src/pulsecore/native-common.h b/src/pulsecore/native-common.h
index 6951e10a..f49abb09 100644
--- a/src/pulsecore/native-common.h
+++ b/src/pulsecore/native-common.h
@@ -152,7 +152,7 @@ enum {
/* Supported since protocol v14 (0.9.12) */
PA_COMMAND_EXTENSION,
- /* Supported since protocol v15 (0.9.15*/
+ /* Supported since protocol v15 (0.9.15) */
PA_COMMAND_GET_CARD_INFO,
PA_COMMAND_GET_CARD_INFO_LIST,
PA_COMMAND_SET_CARD_PROFILE,
@@ -161,6 +161,14 @@ enum {
PA_COMMAND_PLAYBACK_STREAM_EVENT,
PA_COMMAND_RECORD_STREAM_EVENT,
+ /* SERVER->CLIENT */
+ PA_COMMAND_PLAYBACK_BUFFER_ATTR_CHANGED,
+ PA_COMMAND_RECORD_BUFFER_ATTR_CHANGED,
+
+ /* Supported since protocol v16 (0.9.16) */
+ PA_COMMAND_SET_SINK_PORT,
+ PA_COMMAND_SET_SOURCE_PORT,
+
PA_COMMAND_MAX
};
diff --git a/src/pulsecore/object.c b/src/pulsecore/object.c
index 8fd05fb6..f3ead9c5 100644
--- a/src/pulsecore/object.c
+++ b/src/pulsecore/object.c
@@ -24,6 +24,8 @@
#include <config.h>
#endif
+#include <pulsecore/core-util.h>
+
#include "object.h"
pa_object *pa_object_new_internal(size_t size, const char *type_name, int (*check_type)(const char *type_name)) {
@@ -66,5 +68,5 @@ void pa_object_unref(pa_object *o) {
int pa_object_check_type(const char *type_name) {
pa_assert(type_name);
- return strcmp(type_name, "pa_object") == 0;
+ return pa_streq(type_name, "pa_object");
}
diff --git a/src/pulsecore/parseaddr.c b/src/pulsecore/parseaddr.c
index c5cd7fe7..44cd9a05 100644
--- a/src/pulsecore/parseaddr.c
+++ b/src/pulsecore/parseaddr.c
@@ -25,6 +25,8 @@
#include <string.h>
#include <stdlib.h>
+#include <arpa/inet.h>
+#include <sys/socket.h>
#include <pulse/xmalloc.h>
#include <pulse/util.h>
@@ -87,13 +89,15 @@ int pa_parse_address(const char *name, pa_parsed_address *ret_p) {
ret_p->type = PA_PARSED_ADDRESS_TCP_AUTO;
if (*name == '{') {
- char hn[256], *pfx;
- /* The URL starts with a host specification for detecting local connections */
+ char *id, *pfx;
- if (!pa_get_host_name(hn, sizeof(hn)))
+ /* The URL starts with a host id for detecting local connections */
+ if (!(id = pa_machine_id()))
return -1;
- pfx = pa_sprintf_malloc("{%s}", hn);
+ pfx = pa_sprintf_malloc("{%s}", id);
+ pa_xfree(id);
+
if (!pa_startswith(name, pfx)) {
pa_xfree(pfx);
/* Not local */
@@ -129,3 +133,17 @@ int pa_parse_address(const char *name, pa_parsed_address *ret_p) {
return 0;
}
+
+pa_bool_t pa_is_ip_address(const char *a) {
+ char buf[INET6_ADDRSTRLEN];
+
+ pa_assert(a);
+
+ if (inet_pton(AF_INET6, a, buf) >= 1)
+ return TRUE;
+
+ if (inet_pton(AF_INET, a, buf) >= 1)
+ return TRUE;
+
+ return FALSE;
+}
diff --git a/src/pulsecore/parseaddr.h b/src/pulsecore/parseaddr.h
index 5fbcb9a7..a1071b0a 100644
--- a/src/pulsecore/parseaddr.h
+++ b/src/pulsecore/parseaddr.h
@@ -1,5 +1,5 @@
-#ifndef fooparseaddrhfoo
-#define fooparseaddrhfoo
+#ifndef foopulsecoreparseaddrhfoo
+#define foopulsecoreparseaddrhfoo
/***
This file is part of PulseAudio.
@@ -24,6 +24,8 @@
#include <inttypes.h>
+#include <pulsecore/macro.h>
+
typedef enum pa_parsed_address_type {
PA_PARSED_ADDRESS_UNIX,
PA_PARSED_ADDRESS_TCP4,
@@ -39,4 +41,6 @@ typedef struct pa_parsed_address {
int pa_parse_address(const char *a, pa_parsed_address *ret_p);
+pa_bool_t pa_is_ip_address(const char *a);
+
#endif
diff --git a/src/pulsecore/pdispatch.c b/src/pulsecore/pdispatch.c
index 305941a3..fc8ce76f 100644
--- a/src/pulsecore/pdispatch.c
+++ b/src/pulsecore/pdispatch.c
@@ -27,6 +27,7 @@
#include <stdio.h>
#include <stdlib.h>
+#include <pulse/rtclock.h>
#include <pulse/timeval.h>
#include <pulse/xmalloc.h>
@@ -37,6 +38,7 @@
#include <pulsecore/macro.h>
#include <pulsecore/refcnt.h>
#include <pulsecore/flist.h>
+#include <pulsecore/core-rtclock.h>
#include "pdispatch.h"
@@ -165,7 +167,20 @@ static const char *command_names[PA_COMMAND_MAX] = {
[PA_COMMAND_STARTED] = "STARTED",
/* Supported since protocol v14 (0.9.12) */
- [PA_COMMAND_EXTENSION] = "EXTENSION"
+ [PA_COMMAND_EXTENSION] = "EXTENSION",
+
+
+ [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",
+
+ [PA_COMMAND_CLIENT_EVENT] = "GET_CLIENT_EVENT",
+ [PA_COMMAND_PLAYBACK_STREAM_EVENT] = "PLAYBACK_STREAM_EVENT",
+ [PA_COMMAND_RECORD_STREAM_EVENT] = "RECORD_STREAM_EVENT",
+
+ /* SERVER->CLIENT */
+ [PA_COMMAND_PLAYBACK_BUFFER_ATTR_CHANGED] = "PLAYBACK_BUFFER_ATTR_CHANGED",
+ [PA_COMMAND_RECORD_BUFFER_ATTR_CHANGED] = "RECORD_BUFFER_ATTR_CHANGED"
};
#endif
@@ -191,6 +206,7 @@ struct pa_pdispatch {
pa_pdispatch_drain_callback drain_callback;
void *drain_userdata;
const pa_creds *creds;
+ pa_bool_t use_rtclock:1;
};
static void reply_info_free(struct reply_info *r) {
@@ -207,7 +223,7 @@ static void reply_info_free(struct reply_info *r) {
pa_xfree(r);
}
-pa_pdispatch* pa_pdispatch_new(pa_mainloop_api *mainloop, const pa_pdispatch_cb_t*table, unsigned entries) {
+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);
@@ -222,6 +238,7 @@ pa_pdispatch* pa_pdispatch_new(pa_mainloop_api *mainloop, const pa_pdispatch_cb_
pd->drain_callback = NULL;
pd->drain_userdata = NULL;
pd->creds = NULL;
+ pd->use_rtclock = use_rtclock;
return pd;
}
@@ -291,7 +308,7 @@ int pa_pdispatch_run(pa_pdispatch *pd, pa_packet*packet, const pa_creds *creds,
if (command >= PA_COMMAND_MAX || !(p = command_names[command]))
pa_snprintf((char*) (p = t), sizeof(t), "%u", command);
- pa_log("[%p] Recieved opcode <%s>", pd, p);
+ pa_log("[%p] Received opcode <%s>", pd, p);
}
#endif
@@ -312,7 +329,7 @@ int pa_pdispatch_run(pa_pdispatch *pd, pa_packet*packet, const pa_creds *creds,
(*c)(pd, command, tag, ts, userdata);
} else {
- pa_log("Recieved unsupported command %u", command);
+ pa_log("Received unsupported command %u", command);
goto finish;
}
@@ -329,7 +346,7 @@ finish:
return ret;
}
-static void timeout_callback(pa_mainloop_api*m, pa_time_event*e, const struct timeval *tv, void *userdata) {
+static void timeout_callback(pa_mainloop_api*m, pa_time_event*e, const struct timeval *t, void *userdata) {
struct reply_info*r = userdata;
pa_assert(r);
@@ -358,10 +375,7 @@ void pa_pdispatch_register_reply(pa_pdispatch *pd, uint32_t tag, int timeout, pa
r->free_cb = free_cb;
r->tag = tag;
- pa_gettimeofday(&tv);
- tv.tv_sec += timeout;
-
- pa_assert_se(r->time_event = pd->mainloop->time_new(pd->mainloop, &tv, 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);
}
diff --git a/src/pulsecore/pdispatch.h b/src/pulsecore/pdispatch.h
index 5c31d80e..dae475af 100644
--- a/src/pulsecore/pdispatch.h
+++ b/src/pulsecore/pdispatch.h
@@ -37,7 +37,7 @@ 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);
-pa_pdispatch* pa_pdispatch_new(pa_mainloop_api *m, 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);
diff --git a/src/pulsecore/proplist-util.c b/src/pulsecore/proplist-util.c
index af5f0aa6..d9769bc7 100644
--- a/src/pulsecore/proplist-util.c
+++ b/src/pulsecore/proplist-util.c
@@ -168,20 +168,20 @@ void pa_init_proplist(pa_proplist *p) {
}
if (!pa_proplist_contains(p, PA_PROP_APPLICATION_PROCESS_USER)) {
- char t[64];
- if (pa_get_user_name(t, sizeof(t))) {
- char *c = pa_utf8_filter(t);
- pa_proplist_sets(p, PA_PROP_APPLICATION_PROCESS_USER, c);
- pa_xfree(c);
+ char *u;
+
+ if ((u = pa_get_user_name_malloc())) {
+ pa_proplist_sets(p, PA_PROP_APPLICATION_PROCESS_USER, u);
+ pa_xfree(u);
}
}
if (!pa_proplist_contains(p, PA_PROP_APPLICATION_PROCESS_HOST)) {
- char t[64];
- if (pa_get_host_name(t, sizeof(t))) {
- char *c = pa_utf8_filter(t);
- pa_proplist_sets(p, PA_PROP_APPLICATION_PROCESS_HOST, c);
- pa_xfree(c);
+ char *h;
+
+ if ((h = pa_get_host_name_malloc())) {
+ pa_proplist_sets(p, PA_PROP_APPLICATION_PROCESS_HOST, h);
+ pa_xfree(h);
}
}
@@ -231,12 +231,11 @@ void pa_init_proplist(pa_proplist *p) {
}
if (!pa_proplist_contains(p, PA_PROP_APPLICATION_PROCESS_SESSION_ID)) {
- const char *t;
+ char *s;
- if ((t = getenv("XDG_SESSION_COOKIE"))) {
- char *c = pa_utf8_filter(t);
- pa_proplist_sets(p, PA_PROP_APPLICATION_PROCESS_SESSION_ID, c);
- pa_xfree(c);
+ if ((s = pa_session_id())) {
+ pa_proplist_sets(p, PA_PROP_APPLICATION_PROCESS_SESSION_ID, s);
+ pa_xfree(s);
}
}
}
diff --git a/src/pulsecore/protocol-esound.c b/src/pulsecore/protocol-esound.c
index 2b80c65f..f64552aa 100644
--- a/src/pulsecore/protocol-esound.c
+++ b/src/pulsecore/protocol-esound.c
@@ -30,6 +30,7 @@
#include <stdlib.h>
#include <limits.h>
+#include <pulse/rtclock.h>
#include <pulse/sample.h>
#include <pulse/timeval.h>
#include <pulse/utf8.h>
@@ -63,7 +64,7 @@
#define MAX_CONNECTIONS 64
/* Kick a client if it doesn't authenticate within this time */
-#define AUTH_TIMEOUT 5
+#define AUTH_TIMEOUT (5*PA_USEC_PER_SEC)
#define DEFAULT_COOKIE_FILE ".esd_auth"
@@ -562,7 +563,7 @@ static int esd_proto_get_latency(connection *c, esd_proto_t request, const void
pa_sink *sink;
int32_t latency;
- connection_ref(c);
+ connection_assert_ref(c);
pa_assert(!data);
pa_assert(length == 0);
@@ -575,6 +576,7 @@ static int esd_proto_get_latency(connection *c, esd_proto_t request, const void
latency = PA_MAYBE_INT32_SWAP(c->swap_byte_order, latency);
connection_write(c, &latency, sizeof(int32_t));
+
return 0;
}
@@ -583,7 +585,7 @@ static int esd_proto_server_info(connection *c, esd_proto_t request, const void
int32_t response;
pa_sink *sink;
- connection_ref(c);
+ connection_assert_ref(c);
pa_assert(data);
pa_assert(length == sizeof(int32_t));
@@ -611,7 +613,7 @@ static int esd_proto_all_info(connection *c, esd_proto_t request, const void *da
unsigned nsamples;
char terminator[sizeof(int32_t)*6+ESD_NAME_MAX];
- connection_ref(c);
+ connection_assert_ref(c);
pa_assert(data);
pa_assert(length == sizeof(int32_t));
@@ -637,7 +639,8 @@ static int esd_proto_all_info(connection *c, esd_proto_t request, const void *da
pa_assert(t >= k*2+s);
if (conn->sink_input) {
- pa_cvolume volume = *pa_sink_input_get_volume(conn->sink_input);
+ pa_cvolume volume;
+ pa_sink_input_get_volume(conn->sink_input, &volume, TRUE);
rate = (int32_t) conn->sink_input->sample_spec.rate;
lvolume = (int32_t) ((volume.values[0]*ESD_VOLUME_BASE)/PA_VOLUME_NORM);
rvolume = (int32_t) ((volume.values[volume.channels == 2 ? 1 : 0]*ESD_VOLUME_BASE)/PA_VOLUME_NORM);
@@ -777,7 +780,7 @@ static int esd_proto_stream_pan(connection *c, esd_proto_t request, const void *
volume.values[1] = (rvolume*PA_VOLUME_NORM)/ESD_VOLUME_BASE;
volume.channels = conn->sink_input->sample_spec.channels;
- pa_sink_input_set_volume(conn->sink_input, &volume, TRUE);
+ pa_sink_input_set_volume(conn->sink_input, &volume, TRUE, TRUE);
ok = 1;
} else
ok = 0;
@@ -945,10 +948,10 @@ static int esd_proto_standby_or_resume(connection *c, esd_proto_t request, const
connection_write(c, &ok, sizeof(int32_t));
if (request == ESD_PROTO_STANDBY)
- ok = pa_sink_suspend_all(c->protocol->core, TRUE) >= 0;
+ ok = pa_sink_suspend_all(c->protocol->core, TRUE, PA_SUSPEND_USER) >= 0;
else {
pa_assert(request == ESD_PROTO_RESUME);
- ok = pa_sink_suspend_all(c->protocol->core, FALSE) >= 0;
+ ok = pa_sink_suspend_all(c->protocol->core, FALSE, PA_SUSPEND_USER) >= 0;
}
connection_write(c, &ok, sizeof(int32_t));
@@ -1105,8 +1108,7 @@ static int do_read(connection *c) {
pa_scache_add_item(c->protocol->core, c->scache.name, &c->scache.sample_spec, NULL, &c->scache.memchunk, c->client->proplist, &idx);
pa_memblock_unref(c->scache.memchunk.memblock);
- c->scache.memchunk.memblock = NULL;
- c->scache.memchunk.index = c->scache.memchunk.length = 0;
+ pa_memchunk_reset(&c->scache.memchunk);
pa_xfree(c->scache.name);
c->scache.name = NULL;
@@ -1458,11 +1460,10 @@ static pa_usec_t source_output_get_latency_cb(pa_source_output *o) {
/*** entry points ***/
-static void auth_timeout(pa_mainloop_api*m, pa_time_event *e, const struct timeval *tv, void *userdata) {
+static void auth_timeout(pa_mainloop_api *m, pa_time_event *e, const struct timeval *t, void *userdata) {
connection *c = CONNECTION(userdata);
pa_assert(m);
- pa_assert(tv);
connection_assert_ref(c);
pa_assert(c->auth_timeout_event == e);
@@ -1552,12 +1553,9 @@ void pa_esound_protocol_connect(pa_esound_protocol *p, pa_iochannel *io, pa_esou
c->authorized = TRUE;
}
- if (!c->authorized) {
- struct timeval tv;
- pa_gettimeofday(&tv);
- tv.tv_sec += AUTH_TIMEOUT;
- c->auth_timeout_event = p->core->mainloop->time_new(p->core->mainloop, &tv, auth_timeout, c);
- } else
+ if (!c->authorized)
+ c->auth_timeout_event = pa_core_rttime_new(p->core, pa_rtclock_now() + AUTH_TIMEOUT, auth_timeout, c);
+ else
c->auth_timeout_event = NULL;
c->defer_event = p->core->mainloop->defer_new(p->core->mainloop, defer_callback, c);
diff --git a/src/pulsecore/protocol-http.c b/src/pulsecore/protocol-http.c
index f3b93819..5220cc91 100644
--- a/src/pulsecore/protocol-http.c
+++ b/src/pulsecore/protocol-http.c
@@ -1,7 +1,7 @@
/***
This file is part of PulseAudio.
- Copyright 2005-2006 Lennart Poettering
+ Copyright 2005-2009 Lennart Poettering
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
@@ -26,36 +26,68 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
+#include <errno.h>
#include <pulse/util.h>
#include <pulse/xmalloc.h>
+#include <pulse/timeval.h>
#include <pulsecore/ioline.h>
+#include <pulsecore/thread-mq.h>
#include <pulsecore/macro.h>
#include <pulsecore/log.h>
#include <pulsecore/namereg.h>
#include <pulsecore/cli-text.h>
#include <pulsecore/shared.h>
+#include <pulsecore/core-error.h>
+#include <pulsecore/mime-type.h>
#include "protocol-http.h"
/* Don't allow more than this many concurrent connections */
#define MAX_CONNECTIONS 10
-#define internal_server_error(c) http_message((c), 500, "Internal Server Error", NULL)
-
#define URL_ROOT "/"
#define URL_CSS "/style"
#define URL_STATUS "/status"
+#define URL_LISTEN "/listen"
+#define URL_LISTEN_SOURCE "/listen/source/"
+
+#define MIME_HTML "text/html; charset=utf-8"
+#define MIME_TEXT "text/plain; charset=utf-8"
+#define MIME_CSS "text/css"
+
+#define HTML_HEADER(t) \
+ "<?xml version=\"1.0\"?>\n" \
+ "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n" \
+ "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n" \
+ " <head>\n" \
+ " <title>"t"</title>\n" \
+ " <link rel=\"stylesheet\" type=\"text/css\" href=\"style\"/>\n" \
+ " </head>\n" \
+ " <body>\n"
+
+#define HTML_FOOTER \
+ " </body>\n" \
+ "</html>\n"
+
+#define RECORD_BUFFER_SECONDS (5)
+#define DEFAULT_SOURCE_LATENCY (300*PA_USEC_PER_MSEC)
+
+enum state {
+ STATE_REQUEST_LINE,
+ STATE_MIME_HEADER,
+ STATE_DATA
+};
struct connection {
pa_http_protocol *protocol;
+ pa_iochannel *io;
pa_ioline *line;
- enum {
- REQUEST_LINE,
- MIME_HEADER,
- DATA
- } state;
+ pa_memblockq *output_memblockq;
+ pa_source_output *source_output;
+ pa_client *client;
+ enum state state;
char *url;
pa_module *module;
};
@@ -65,58 +97,500 @@ struct pa_http_protocol {
pa_core *core;
pa_idxset *connections;
+
+ pa_strlist *servers;
};
-static void http_response(struct connection *c, int code, const char *msg, const char *mime) {
- char s[256];
+enum {
+ SOURCE_OUTPUT_MESSAGE_POST_DATA = PA_SOURCE_OUTPUT_MESSAGE_MAX
+};
+
+/* Called from main context */
+static void connection_unlink(struct connection *c) {
+ pa_assert(c);
+
+ if (c->source_output) {
+ pa_source_output_unlink(c->source_output);
+ c->source_output->userdata = NULL;
+ pa_source_output_unref(c->source_output);
+ }
+
+ if (c->client)
+ pa_client_free(c->client);
+
+ pa_xfree(c->url);
+
+ if (c->line)
+ pa_ioline_unref(c->line);
+
+ if (c->io)
+ pa_iochannel_free(c->io);
+
+ if (c->output_memblockq)
+ pa_memblockq_free(c->output_memblockq);
+
+ pa_idxset_remove_by_data(c->protocol->connections, c, NULL);
+
+ pa_xfree(c);
+}
+
+/* Called from main context */
+static int do_write(struct connection *c) {
+ pa_memchunk chunk;
+ ssize_t r;
+ void *p;
+
+ pa_assert(c);
+
+ if (pa_memblockq_peek(c->output_memblockq, &chunk) < 0)
+ return 0;
+
+ pa_assert(chunk.memblock);
+ pa_assert(chunk.length > 0);
+
+ p = pa_memblock_acquire(chunk.memblock);
+ r = pa_iochannel_write(c->io, (uint8_t*) p+chunk.index, chunk.length);
+ pa_memblock_release(chunk.memblock);
+
+ pa_memblock_unref(chunk.memblock);
+
+ if (r < 0) {
+
+ if (errno == EINTR || errno == EAGAIN)
+ return 0;
+
+ pa_log("write(): %s", pa_cstrerror(errno));
+ return -1;
+ }
+
+ pa_memblockq_drop(c->output_memblockq, (size_t) r);
+
+ return 0;
+}
+
+/* Called from main context */
+static void do_work(struct connection *c) {
+ pa_assert(c);
+
+ if (pa_iochannel_is_hungup(c->io))
+ goto fail;
+
+ if (pa_iochannel_is_writable(c->io))
+ if (do_write(c) < 0)
+ goto fail;
+
+ return;
+
+fail:
+ connection_unlink(c);
+}
+
+/* Called from thread context, except when it is not */
+static int source_output_process_msg(pa_msgobject *m, int code, void *userdata, int64_t offset, pa_memchunk *chunk) {
+ pa_source_output *o = PA_SOURCE_OUTPUT(m);
+ struct connection *c;
+
+ pa_source_output_assert_ref(o);
+
+ if (!(c = o->userdata))
+ return -1;
+
+ switch (code) {
+
+ case SOURCE_OUTPUT_MESSAGE_POST_DATA:
+ /* While this function is usually called from IO thread
+ * context, this specific command is not! */
+ pa_memblockq_push_align(c->output_memblockq, chunk);
+ do_work(c);
+ break;
+
+ default:
+ return pa_source_output_process_msg(m, code, userdata, offset, chunk);
+ }
+
+ return 0;
+}
+
+/* Called from thread context */
+static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk) {
+ struct connection *c;
+
+ pa_source_output_assert_ref(o);
+ pa_assert_se(c = o->userdata);
+ pa_assert(chunk);
+
+ pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(o), SOURCE_OUTPUT_MESSAGE_POST_DATA, NULL, 0, chunk, NULL);
+}
+
+/* Called from main context */
+static void source_output_kill_cb(pa_source_output *o) {
+ struct connection*c;
+
+ pa_source_output_assert_ref(o);
+ pa_assert_se(c = o->userdata);
+
+ connection_unlink(c);
+}
+
+/* Called from main context */
+static pa_usec_t source_output_get_latency_cb(pa_source_output *o) {
+ struct connection*c;
+
+ pa_source_output_assert_ref(o);
+ pa_assert_se(c = o->userdata);
+
+ return pa_bytes_to_usec(pa_memblockq_get_length(c->output_memblockq), &c->source_output->sample_spec);
+}
+
+/*** client callbacks ***/
+static void client_kill_cb(pa_client *client) {
+ struct connection*c;
+
+ pa_assert(client);
+ pa_assert_se(c = client->userdata);
+
+ connection_unlink(c);
+}
+
+/*** pa_iochannel callbacks ***/
+static void io_callback(pa_iochannel*io, void *userdata) {
+ struct connection *c = userdata;
+
+ pa_assert(c);
+ pa_assert(io);
+
+ do_work(c);
+}
+
+static char *escape_html(const char *t) {
+ pa_strbuf *sb;
+ const char *p, *e;
+
+ sb = pa_strbuf_new();
+
+ for (e = p = t; *p; p++) {
+
+ if (*p == '>' || *p == '<' || *p == '&') {
+
+ if (p > e) {
+ pa_strbuf_putsn(sb, e, p-e);
+ e = p + 1;
+ }
+
+ if (*p == '>')
+ pa_strbuf_puts(sb, "&gt;");
+ else if (*p == '<')
+ pa_strbuf_puts(sb, "&lt;");
+ else
+ pa_strbuf_puts(sb, "&amp;");
+ }
+ }
+
+ if (p > e)
+ pa_strbuf_putsn(sb, e, p-e);
+
+ return pa_strbuf_tostring_free(sb);
+}
+
+static void http_response(
+ struct connection *c,
+ int code,
+ const char *msg,
+ const char *mime) {
+
+ char *s;
pa_assert(c);
pa_assert(msg);
pa_assert(mime);
- pa_snprintf(s, sizeof(s),
- "HTTP/1.0 %i %s\n"
- "Connection: close\n"
- "Content-Type: %s\n"
- "Cache-Control: no-cache\n"
- "Expires: 0\n"
- "Server: "PACKAGE_NAME"/"PACKAGE_VERSION"\n"
- "\n", code, msg, mime);
-
+ s = pa_sprintf_malloc(
+ "HTTP/1.0 %i %s\n"
+ "Connection: close\n"
+ "Content-Type: %s\n"
+ "Cache-Control: no-cache\n"
+ "Expires: 0\n"
+ "Server: "PACKAGE_NAME"/"PACKAGE_VERSION"\n"
+ "\n", code, msg, mime);
pa_ioline_puts(c->line, s);
+ pa_xfree(s);
}
-static void http_message(struct connection *c, int code, const char *msg, const char *text) {
- char s[256];
+static void html_response(
+ struct connection *c,
+ int code,
+ const char *msg,
+ const char *text) {
+
+ char *s;
pa_assert(c);
- http_response(c, code, msg, "text/html");
+ http_response(c, code, msg, MIME_HTML);
if (!text)
text = msg;
- pa_snprintf(s, sizeof(s),
- "<?xml version=\"1.0\"?>\n"
- "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n"
- "<html xmlns=\"http://www.w3.org/1999/xhtml\"><head><title>%s</title></head>\n"
- "<body>%s</body></html>\n",
- text, text);
+ s = pa_sprintf_malloc(
+ HTML_HEADER("%s")
+ "%s"
+ HTML_FOOTER,
+ text, text);
pa_ioline_puts(c->line, s);
+ pa_xfree(s);
+
pa_ioline_defer_close(c->line);
}
+static void html_print_field(pa_ioline *line, const char *left, const char *right) {
+ char *eleft, *eright;
+
+ eleft = escape_html(left);
+ eright = escape_html(right);
+
+ pa_ioline_printf(line,
+ "<tr><td><b>%s</b></td>"
+ "<td>%s</td></tr>\n", eleft, eright);
+
+ pa_xfree(eleft);
+ pa_xfree(eright);
+}
+
+static void handle_root(struct connection *c) {
+ char *t;
-static void connection_unlink(struct connection *c) {
pa_assert(c);
- if (c->url)
- pa_xfree(c->url);
+ http_response(c, 200, "OK", MIME_HTML);
- pa_idxset_remove_by_data(c->protocol->connections, c, NULL);
+ pa_ioline_puts(c->line,
+ HTML_HEADER(PACKAGE_NAME" "PACKAGE_VERSION)
+ "<h1>"PACKAGE_NAME" "PACKAGE_VERSION"</h1>\n"
+ "<table>\n");
+
+ t = pa_get_user_name_malloc();
+ html_print_field(c->line, "User Name:", t);
+ pa_xfree(t);
+
+ t = pa_get_host_name_malloc();
+ html_print_field(c->line, "Host name:", t);
+ pa_xfree(t);
+
+ t = pa_machine_id();
+ html_print_field(c->line, "Machine ID:", t);
+ pa_xfree(t);
+
+ t = pa_uname_string();
+ html_print_field(c->line, "System:", t);
+ pa_xfree(t);
+
+ t = pa_sprintf_malloc("%lu", (unsigned long) getpid());
+ html_print_field(c->line, "Process ID:", t);
+ pa_xfree(t);
+
+ pa_ioline_puts(c->line,
+ "</table>\n"
+ "<p><a href=\"" URL_STATUS "\">Show an extensive server status report</a></p>\n"
+ "<p><a href=\"" URL_LISTEN "\">Monitor sinks and sources</a></p>\n"
+ HTML_FOOTER);
+
+ pa_ioline_defer_close(c->line);
+}
+
+static void handle_css(struct connection *c) {
+ pa_assert(c);
+
+ http_response(c, 200, "OK", MIME_CSS);
+
+ pa_ioline_puts(c->line,
+ "body { color: black; background-color: white; }\n"
+ "a:link, a:visited { color: #900000; }\n"
+ "div.news-date { font-size: 80%; font-style: italic; }\n"
+ "pre { background-color: #f0f0f0; padding: 0.4cm; }\n"
+ ".grey { color: #8f8f8f; font-size: 80%; }"
+ "table { margin-left: 1cm; border:1px solid lightgrey; padding: 0.2cm; }\n"
+ "td { padding-left:10px; padding-right:10px; }\n");
+
+ pa_ioline_defer_close(c->line);
+}
+
+static void handle_status(struct connection *c) {
+ char *r;
+
+ pa_assert(c);
+
+ http_response(c, 200, "OK", MIME_TEXT);
+ r = pa_full_status_string(c->protocol->core);
+ pa_ioline_puts(c->line, r);
+ pa_xfree(r);
+
+ pa_ioline_defer_close(c->line);
+}
+
+static void handle_listen(struct connection *c) {
+ pa_source *source;
+ pa_sink *sink;
+ uint32_t idx;
+
+ http_response(c, 200, "OK", MIME_HTML);
+
+ pa_ioline_puts(c->line,
+ HTML_HEADER("Listen")
+ "<h2>Sinks</h2>\n"
+ "<p>\n");
+
+ PA_IDXSET_FOREACH(sink, c->protocol->core->sinks, idx) {
+ char *t, *m;
+
+ t = escape_html(pa_strna(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION)));
+ m = pa_sample_spec_to_mime_type_mimefy(&sink->sample_spec, &sink->channel_map);
+
+ pa_ioline_printf(c->line,
+ "<a href=\"" URL_LISTEN_SOURCE "%s\" title=\"%s\">%s</a><br/>\n",
+ sink->monitor_source->name, m, t);
+
+ pa_xfree(t);
+ pa_xfree(m);
+ }
+
+ pa_ioline_puts(c->line,
+ "</p>\n"
+ "<h2>Sources</h2>\n"
+ "<p>\n");
+
+ PA_IDXSET_FOREACH(source, c->protocol->core->sources, idx) {
+ char *t, *m;
+
+ if (source->monitor_of)
+ continue;
+
+ t = escape_html(pa_strna(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION)));
+ m = pa_sample_spec_to_mime_type_mimefy(&source->sample_spec, &source->channel_map);
+
+ pa_ioline_printf(c->line,
+ "<a href=\"" URL_LISTEN_SOURCE "%s\" title=\"%s\">%s</a><br/>\n",
+ source->name, m, t);
+
+ pa_xfree(m);
+ pa_xfree(t);
+
+ }
+
+ pa_ioline_puts(c->line,
+ "</p>\n"
+ HTML_FOOTER);
+
+ pa_ioline_defer_close(c->line);
+}
+
+static void line_drain_callback(pa_ioline *l, void *userdata) {
+ struct connection *c;
+
+ pa_assert(l);
+ pa_assert_se(c = userdata);
+
+ /* We don't need the line reader anymore, instead we need a real
+ * binary io channel */
+ pa_assert_se(c->io = pa_ioline_detach_iochannel(c->line));
+ pa_iochannel_set_callback(c->io, io_callback, c);
+
+ pa_iochannel_socket_set_sndbuf(c->io, pa_memblockq_get_length(c->output_memblockq));
pa_ioline_unref(c->line);
- pa_xfree(c);
+ c->line = NULL;
+}
+
+static void handle_listen_prefix(struct connection *c, const char *source_name) {
+ pa_source *source;
+ pa_source_output_new_data data;
+ pa_sample_spec ss;
+ pa_channel_map cm;
+ char *t;
+ size_t l;
+
+ pa_assert(c);
+ pa_assert(source_name);
+
+ pa_assert(c->line);
+ pa_assert(!c->io);
+
+ if (!(source = pa_namereg_get(c->protocol->core, source_name, PA_NAMEREG_SOURCE))) {
+ html_response(c, 404, "Source not found", NULL);
+ return;
+ }
+
+ ss = source->sample_spec;
+ cm = source->channel_map;
+
+ pa_sample_spec_mimefy(&ss, &cm);
+
+ pa_source_output_new_data_init(&data);
+ data.driver = __FILE__;
+ data.module = c->module;
+ data.client = c->client;
+ data.source = source;
+ pa_proplist_update(data.proplist, PA_UPDATE_MERGE, c->client->proplist);
+ pa_source_output_new_data_set_sample_spec(&data, &ss);
+ pa_source_output_new_data_set_channel_map(&data, &cm);
+
+ pa_source_output_new(&c->source_output, c->protocol->core, &data, 0);
+ pa_source_output_new_data_done(&data);
+
+ if (!c->source_output) {
+ html_response(c, 403, "Cannot create source output", NULL);
+ return;
+ }
+
+ c->source_output->parent.process_msg = source_output_process_msg;
+ c->source_output->push = source_output_push_cb;
+ c->source_output->kill = source_output_kill_cb;
+ c->source_output->get_latency = source_output_get_latency_cb;
+ c->source_output->userdata = c;
+
+ pa_source_output_set_requested_latency(c->source_output, DEFAULT_SOURCE_LATENCY);
+
+ l = (size_t) (pa_bytes_per_second(&ss)*RECORD_BUFFER_SECONDS);
+ c->output_memblockq = pa_memblockq_new(
+ 0,
+ l,
+ 0,
+ pa_frame_size(&ss),
+ 1,
+ 0,
+ 0,
+ NULL);
+
+ pa_source_output_put(c->source_output);
+
+ t = pa_sample_spec_to_mime_type(&ss, &cm);
+ http_response(c, 200, "OK", t);
+ pa_xfree(t);
+
+ pa_ioline_set_callback(c->line, NULL, NULL);
+
+ if (pa_ioline_is_drained(c->line))
+ line_drain_callback(c->line, c);
+ else
+ pa_ioline_set_drain_callback(c->line, line_drain_callback, c);
+}
+
+static void handle_url(struct connection *c) {
+ pa_assert(c);
+
+ pa_log_debug("Request for %s", c->url);
+
+ if (pa_streq(c->url, URL_ROOT))
+ handle_root(c);
+ else if (pa_streq(c->url, URL_CSS))
+ handle_css(c);
+ else if (pa_streq(c->url, URL_STATUS))
+ handle_status(c);
+ else if (pa_streq(c->url, URL_LISTEN))
+ handle_listen(c);
+ else if (pa_startswith(c->url, URL_LISTEN_SOURCE))
+ handle_listen_prefix(c, c->url + sizeof(URL_LISTEN_SOURCE)-1);
+ else
+ html_response(c, 404, "Not Found", NULL);
}
static void line_callback(pa_ioline *line, const char *s, void *userdata) {
@@ -131,93 +605,27 @@ static void line_callback(pa_ioline *line, const char *s, void *userdata) {
}
switch (c->state) {
- case REQUEST_LINE: {
- if (memcmp(s, "GET ", 4))
+ case STATE_REQUEST_LINE: {
+ if (!pa_startswith(s, "GET "))
goto fail;
s +=4;
c->url = pa_xstrndup(s, strcspn(s, " \r\n\t?"));
- c->state = MIME_HEADER;
+ c->state = STATE_MIME_HEADER;
break;
-
}
- case MIME_HEADER: {
+ case STATE_MIME_HEADER: {
/* Ignore MIME headers */
if (strcspn(s, " \r\n") != 0)
break;
/* We're done */
- c->state = DATA;
-
- pa_log_info("request for %s", c->url);
-
- if (!strcmp(c->url, URL_ROOT)) {
- char txt[256];
- pa_sink *def_sink;
- pa_source *def_source;
- http_response(c, 200, "OK", "text/html");
-
- pa_ioline_puts(c->line,
- "<?xml version=\"1.0\"?>\n"
- "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n"
- "<html xmlns=\"http://www.w3.org/1999/xhtml\"><title>"PACKAGE_NAME" "PACKAGE_VERSION"</title>\n"
- "<link rel=\"stylesheet\" type=\"text/css\" href=\"style\"/></head><body>\n");
-
- pa_ioline_puts(c->line,
- "<h1>"PACKAGE_NAME" "PACKAGE_VERSION"</h1>\n"
- "<table>");
-
-#define PRINTF_FIELD(a,b) pa_ioline_printf(c->line, "<tr><td><b>%s</b></td><td>%s</td></tr>\n",(a),(b))
-
- PRINTF_FIELD("User Name:", pa_get_user_name(txt, sizeof(txt)));
- PRINTF_FIELD("Host name:", pa_get_host_name(txt, sizeof(txt)));
- PRINTF_FIELD("Default Sample Specification:", pa_sample_spec_snprint(txt, sizeof(txt), &c->protocol->core->default_sample_spec));
-
- def_sink = pa_namereg_get_default_sink(c->protocol->core);
- def_source = pa_namereg_get_default_source(c->protocol->core);
-
- PRINTF_FIELD("Default Sink:", def_sink ? def_sink->name : "n/a");
- PRINTF_FIELD("Default Source:", def_source ? def_source->name : "n/a");
-
- pa_ioline_puts(c->line, "</table>");
-
- pa_ioline_puts(c->line, "<p><a href=\"/status\">Click here</a> for an extensive server status report.</p>");
-
- pa_ioline_puts(c->line, "</body></html>\n");
-
- pa_ioline_defer_close(c->line);
- } else if (!strcmp(c->url, URL_CSS)) {
- http_response(c, 200, "OK", "text/css");
-
- pa_ioline_puts(c->line,
- "body { color: black; background-color: white; margin: 0.5cm; }\n"
- "a:link, a:visited { color: #900000; }\n"
- "p { margin-left: 0.5cm; margin-right: 0.5cm; }\n"
- "h1 { color: #00009F; }\n"
- "h2 { color: #00009F; }\n"
- "ul { margin-left: .5cm; }\n"
- "ol { margin-left: .5cm; }\n"
- "pre { margin-left: .5cm; background-color: #f0f0f0; padding: 0.4cm;}\n"
- ".grey { color: #afafaf; }\n"
- "table { margin-left: 1cm; border:1px solid lightgrey; padding: 0.2cm; }\n"
- "td { padding-left:10px; padding-right:10px; }\n");
-
- pa_ioline_defer_close(c->line);
- } else if (!strcmp(c->url, URL_STATUS)) {
- char *r;
-
- http_response(c, 200, "OK", "text/plain");
- r = pa_full_status_string(c->protocol->core);
- pa_ioline_puts(c->line, r);
- pa_xfree(r);
-
- pa_ioline_defer_close(c->line);
- } else
- http_message(c, 404, "Not Found", NULL);
+ c->state = STATE_DATA;
+ handle_url(c);
break;
}
@@ -228,11 +636,13 @@ static void line_callback(pa_ioline *line, const char *s, void *userdata) {
return;
fail:
- internal_server_error(c);
+ html_response(c, 500, "Internal Server Error", NULL);
}
void pa_http_protocol_connect(pa_http_protocol *p, pa_iochannel *io, pa_module *m) {
struct connection *c;
+ pa_client_new_data client_data;
+ char pname[128];
pa_assert(p);
pa_assert(io);
@@ -244,26 +654,46 @@ void pa_http_protocol_connect(pa_http_protocol *p, pa_iochannel *io, pa_module *
return;
}
- c = pa_xnew(struct connection, 1);
+ c = pa_xnew0(struct connection, 1);
c->protocol = p;
- c->line = pa_ioline_new(io);
- c->state = REQUEST_LINE;
- c->url = NULL;
+ c->state = STATE_REQUEST_LINE;
c->module = m;
+ c->line = pa_ioline_new(io);
pa_ioline_set_callback(c->line, line_callback, c);
+ pa_client_new_data_init(&client_data);
+ client_data.module = c->module;
+ client_data.driver = __FILE__;
+ pa_iochannel_socket_peer_to_string(io, pname, sizeof(pname));
+ pa_proplist_setf(client_data.proplist, PA_PROP_APPLICATION_NAME, "HTTP client (%s)", pname);
+ pa_proplist_sets(client_data.proplist, "http-protocol.peer", pname);
+ c->client = pa_client_new(p->core, &client_data);
+ pa_client_new_data_done(&client_data);
+
+ if (!c->client)
+ goto fail;
+
+ c->client->kill = client_kill_cb;
+ c->client->userdata = c;
+
pa_idxset_put(p->connections, c, NULL);
+
+ return;
+
+fail:
+ if (c)
+ connection_unlink(c);
}
void pa_http_protocol_disconnect(pa_http_protocol *p, pa_module *m) {
struct connection *c;
- void *state = NULL;
+ uint32_t idx;
pa_assert(p);
pa_assert(m);
- while ((c = pa_idxset_iterate(p->connections, &state, NULL)))
+ PA_IDXSET_FOREACH(c, p->connections, idx)
if (c->module == m)
connection_unlink(c);
}
@@ -273,7 +703,7 @@ static pa_http_protocol* http_protocol_new(pa_core *c) {
pa_assert(c);
- p = pa_xnew(pa_http_protocol, 1);
+ p = pa_xnew0(pa_http_protocol, 1);
PA_REFCNT_INIT(p);
p->core = c;
p->connections = pa_idxset_new(NULL, NULL);
@@ -315,7 +745,32 @@ void pa_http_protocol_unref(pa_http_protocol *p) {
pa_idxset_free(p->connections, NULL, NULL);
+ pa_strlist_free(p->servers);
+
pa_assert_se(pa_shared_remove(p->core, "http-protocol") >= 0);
pa_xfree(p);
}
+
+void pa_http_protocol_add_server_string(pa_http_protocol *p, const char *name) {
+ pa_assert(p);
+ pa_assert(PA_REFCNT_VALUE(p) >= 1);
+ pa_assert(name);
+
+ p->servers = pa_strlist_prepend(p->servers, name);
+}
+
+void pa_http_protocol_remove_server_string(pa_http_protocol *p, const char *name) {
+ pa_assert(p);
+ pa_assert(PA_REFCNT_VALUE(p) >= 1);
+ pa_assert(name);
+
+ p->servers = pa_strlist_remove(p->servers, name);
+}
+
+pa_strlist *pa_http_protocol_servers(pa_http_protocol *p) {
+ pa_assert(p);
+ pa_assert(PA_REFCNT_VALUE(p) >= 1);
+
+ return p->servers;
+}
diff --git a/src/pulsecore/protocol-http.h b/src/pulsecore/protocol-http.h
index 40b3d82c..f7517e81 100644
--- a/src/pulsecore/protocol-http.h
+++ b/src/pulsecore/protocol-http.h
@@ -26,7 +26,7 @@
#include <pulsecore/module.h>
#include <pulsecore/modargs.h>
#include <pulsecore/iochannel.h>
-
+#include <pulsecore/strlist.h>
typedef struct pa_http_protocol pa_http_protocol;
@@ -36,4 +36,8 @@ void pa_http_protocol_unref(pa_http_protocol *p);
void pa_http_protocol_connect(pa_http_protocol *p, pa_iochannel *io, pa_module *m);
void pa_http_protocol_disconnect(pa_http_protocol *p, pa_module *m);
+void pa_http_protocol_add_server_string(pa_http_protocol *p, const char *name);
+void pa_http_protocol_remove_server_string(pa_http_protocol *p, const char *name);
+pa_strlist *pa_http_protocol_servers(pa_http_protocol *p);
+
#endif
diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c
index 50a9191b..96184bd2 100644
--- a/src/pulsecore/protocol-native.c
+++ b/src/pulsecore/protocol-native.c
@@ -29,6 +29,7 @@
#include <stdlib.h>
#include <unistd.h>
+#include <pulse/rtclock.h>
#include <pulse/timeval.h>
#include <pulse/version.h>
#include <pulse/utf8.h>
@@ -61,7 +62,7 @@
#include "protocol-native.h"
/* Kick a client if it doesn't authenticate within this time */
-#define AUTH_TIMEOUT 60
+#define AUTH_TIMEOUT (60 * PA_USEC_PER_SEC)
/* Don't accept more connection than this */
#define MAX_CONNECTIONS 64
@@ -81,8 +82,20 @@ typedef struct record_stream {
pa_source_output *source_output;
pa_memblockq *memblockq;
- size_t fragment_size;
- pa_usec_t source_latency;
+
+ pa_bool_t adjust_latency:1;
+ pa_bool_t early_requests:1;
+
+ pa_buffer_attr buffer_attr;
+
+ pa_atomic_t on_the_fly;
+ pa_usec_t configured_source_latency;
+ size_t drop_initial;
+
+ /* Only updated after SOURCE_OUTPUT_MESSAGE_UPDATE_LATENCY */
+ size_t on_the_fly_snapshot;
+ pa_usec_t current_monitor_latency;
+ pa_usec_t current_source_latency;
} record_stream;
PA_DECLARE_CLASS(record_stream);
@@ -105,18 +118,24 @@ typedef struct playback_stream {
pa_sink_input *sink_input;
pa_memblockq *memblockq;
+
+ pa_bool_t adjust_latency:1;
+ pa_bool_t early_requests:1;
+
pa_bool_t is_underrun:1;
pa_bool_t drain_request:1;
uint32_t drain_tag;
uint32_t syncid;
pa_atomic_t missing;
- size_t minreq;
- pa_usec_t sink_latency;
+ pa_usec_t configured_sink_latency;
+ pa_buffer_attr buffer_attr;
/* Only updated after SINK_INPUT_MESSAGE_UPDATE_LATENCY */
int64_t read_index, write_index;
size_t render_memblockq_length;
+ pa_usec_t current_sink_latency;
+ uint64_t playing_for, underrun_for;
} playback_stream;
PA_DECLARE_CLASS(playback_stream);
@@ -174,13 +193,18 @@ struct pa_native_protocol {
};
enum {
+ SOURCE_OUTPUT_MESSAGE_UPDATE_LATENCY = PA_SOURCE_OUTPUT_MESSAGE_MAX
+};
+
+enum {
SINK_INPUT_MESSAGE_POST_DATA = PA_SINK_INPUT_MESSAGE_MAX, /* data from main loop to sink input */
SINK_INPUT_MESSAGE_DRAIN, /* disabled prebuf, get playback started. */
SINK_INPUT_MESSAGE_FLUSH,
SINK_INPUT_MESSAGE_TRIGGER,
SINK_INPUT_MESSAGE_SEEK,
SINK_INPUT_MESSAGE_PREBUF_FORCE,
- SINK_INPUT_MESSAGE_UPDATE_LATENCY
+ SINK_INPUT_MESSAGE_UPDATE_LATENCY,
+ SINK_INPUT_MESSAGE_UPDATE_BUFFER_ATTR
};
enum {
@@ -188,7 +212,8 @@ enum {
PLAYBACK_STREAM_MESSAGE_UNDERFLOW,
PLAYBACK_STREAM_MESSAGE_OVERFLOW,
PLAYBACK_STREAM_MESSAGE_DRAIN_ACK,
- PLAYBACK_STREAM_MESSAGE_STARTED
+ PLAYBACK_STREAM_MESSAGE_STARTED,
+ PLAYBACK_STREAM_MESSAGE_UPDATE_TLENGTH
};
enum {
@@ -203,7 +228,7 @@ enum {
static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk);
static void sink_input_kill_cb(pa_sink_input *i);
static void sink_input_suspend_cb(pa_sink_input *i, pa_bool_t suspend);
-static void sink_input_moved_cb(pa_sink_input *i);
+static void sink_input_moving_cb(pa_sink_input *i, pa_sink *dest);
static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes);
static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes);
static void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes);
@@ -215,11 +240,12 @@ static void playback_stream_request_bytes(struct playback_stream*s);
static void source_output_kill_cb(pa_source_output *o);
static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk);
static void source_output_suspend_cb(pa_source_output *o, pa_bool_t suspend);
-static void source_output_moved_cb(pa_source_output *o);
+static void source_output_moving_cb(pa_source_output *o, pa_source *dest);
static pa_usec_t source_output_get_latency_cb(pa_source_output *o);
static void source_output_send_event_cb(pa_source_output *o, const char *event, pa_proplist *pl);
static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk);
+static int source_output_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk);
static void command_exit(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
@@ -259,6 +285,7 @@ static void command_update_proplist(pa_pdispatch *pd, uint32_t command, uint32_t
static void command_remove_proplist(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
static void command_extension(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
static void command_set_card_profile(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_set_sink_or_source_port(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {
[PA_COMMAND_ERROR] = NULL,
@@ -355,11 +382,15 @@ static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {
[PA_COMMAND_SET_CARD_PROFILE] = command_set_card_profile,
+ [PA_COMMAND_SET_SINK_PORT] = command_set_sink_or_source_port,
+ [PA_COMMAND_SET_SOURCE_PORT] = command_set_sink_or_source_port,
+
[PA_COMMAND_EXTENSION] = command_extension
};
/* structure management */
+/* Called from main context */
static void upload_stream_unlink(upload_stream *s) {
pa_assert(s);
@@ -371,6 +402,7 @@ static void upload_stream_unlink(upload_stream *s) {
upload_stream_unref(s);
}
+/* Called from main context */
static void upload_stream_free(pa_object *o) {
upload_stream *s = UPLOAD_STREAM(o);
pa_assert(s);
@@ -388,6 +420,7 @@ static void upload_stream_free(pa_object *o) {
pa_xfree(s);
}
+/* Called from main context */
static upload_stream* upload_stream_new(
pa_native_connection *c,
const pa_sample_spec *ss,
@@ -420,6 +453,7 @@ static upload_stream* upload_stream_new(
return s;
}
+/* Called from main context */
static void record_stream_unlink(record_stream *s) {
pa_assert(s);
@@ -437,6 +471,7 @@ static void record_stream_unlink(record_stream *s) {
record_stream_unref(s);
}
+/* Called from main context */
static void record_stream_free(pa_object *o) {
record_stream *s = RECORD_STREAM(o);
pa_assert(s);
@@ -447,6 +482,7 @@ static void record_stream_free(pa_object *o) {
pa_xfree(s);
}
+/* Called from main context */
static int record_stream_process_msg(pa_msgobject *o, int code, void*userdata, int64_t offset, pa_memchunk *chunk) {
record_stream *s = RECORD_STREAM(o);
record_stream_assert_ref(s);
@@ -458,6 +494,10 @@ static int record_stream_process_msg(pa_msgobject *o, int code, void*userdata, i
case RECORD_STREAM_MESSAGE_POST_DATA:
+ /* We try to keep up to date with how many bytes are
+ * currently on the fly */
+ pa_atomic_sub(&s->on_the_fly, chunk->length);
+
if (pa_memblockq_push_align(s->memblockq, chunk) < 0) {
/* pa_log_warn("Failed to push data into output queue."); */
return -1;
@@ -472,35 +512,34 @@ static int record_stream_process_msg(pa_msgobject *o, int code, void*userdata, i
return 0;
}
-static void fix_record_buffer_attr_pre(
- record_stream *s,
- pa_bool_t adjust_latency,
- pa_bool_t early_requests,
- uint32_t *maxlength,
- uint32_t *fragsize) {
+/* Called from main context */
+static void fix_record_buffer_attr_pre(record_stream *s) {
size_t frame_size;
pa_usec_t orig_fragsize_usec, fragsize_usec, source_usec;
pa_assert(s);
- pa_assert(maxlength);
- pa_assert(fragsize);
+
+ /* This function will be called from the main thread, before as
+ * well as after the source output has been activated using
+ * pa_source_output_put()! That means it may not touch any
+ * ->thread_info data! */
frame_size = pa_frame_size(&s->source_output->sample_spec);
- if (*maxlength == (uint32_t) -1 || *maxlength > MAX_MEMBLOCKQ_LENGTH)
- *maxlength = MAX_MEMBLOCKQ_LENGTH;
- if (*maxlength <= 0)
- *maxlength = (uint32_t) frame_size;
+ if (s->buffer_attr.maxlength == (uint32_t) -1 || s->buffer_attr.maxlength > MAX_MEMBLOCKQ_LENGTH)
+ s->buffer_attr.maxlength = MAX_MEMBLOCKQ_LENGTH;
+ if (s->buffer_attr.maxlength <= 0)
+ s->buffer_attr.maxlength = (uint32_t) frame_size;
- if (*fragsize == (uint32_t) -1)
- *fragsize = (uint32_t) pa_usec_to_bytes(DEFAULT_FRAGSIZE_MSEC*PA_USEC_PER_MSEC, &s->source_output->sample_spec);
- if (*fragsize <= 0)
- *fragsize = (uint32_t) frame_size;
+ if (s->buffer_attr.fragsize == (uint32_t) -1)
+ s->buffer_attr.fragsize = (uint32_t) pa_usec_to_bytes(DEFAULT_FRAGSIZE_MSEC*PA_USEC_PER_MSEC, &s->source_output->sample_spec);
+ if (s->buffer_attr.fragsize <= 0)
+ s->buffer_attr.fragsize = (uint32_t) frame_size;
- orig_fragsize_usec = fragsize_usec = pa_bytes_to_usec(*fragsize, &s->source_output->sample_spec);
+ orig_fragsize_usec = fragsize_usec = pa_bytes_to_usec(s->buffer_attr.fragsize, &s->source_output->sample_spec);
- if (early_requests) {
+ if (s->early_requests) {
/* In early request mode we need to emulate the classic
* fragment-based playback model. We do this setting the source
@@ -508,7 +547,7 @@ static void fix_record_buffer_attr_pre(
source_usec = fragsize_usec;
- } else if (adjust_latency) {
+ } else if (s->adjust_latency) {
/* So, the user asked us to adjust the latency according to
* what the source can provide. Half the latency will be
@@ -522,73 +561,69 @@ static void fix_record_buffer_attr_pre(
/* Ok, the user didn't ask us to adjust the latency, hence we
* don't */
- source_usec = 0;
+ source_usec = (pa_usec_t) -1;
}
- if (source_usec > 0)
- s->source_latency = pa_source_output_set_requested_latency(s->source_output, source_usec);
+ if (source_usec != (pa_usec_t) -1)
+ s->configured_source_latency = pa_source_output_set_requested_latency(s->source_output, source_usec);
else
- s->source_latency = 0;
+ s->configured_source_latency = 0;
- if (early_requests) {
+ if (s->early_requests) {
/* Ok, we didn't necessarily get what we were asking for, so
* let's tell the user */
- fragsize_usec = s->source_latency;
+ fragsize_usec = s->configured_source_latency;
- } else if (adjust_latency) {
+ } else if (s->adjust_latency) {
/* Now subtract what we actually got */
- if (fragsize_usec >= s->source_latency*2)
- fragsize_usec -= s->source_latency;
+ if (fragsize_usec >= s->configured_source_latency*2)
+ fragsize_usec -= s->configured_source_latency;
else
- fragsize_usec = s->source_latency;
+ fragsize_usec = s->configured_source_latency;
}
if (pa_usec_to_bytes(orig_fragsize_usec, &s->source_output->sample_spec) !=
pa_usec_to_bytes(fragsize_usec, &s->source_output->sample_spec))
- *fragsize = (uint32_t) pa_usec_to_bytes(fragsize_usec, &s->source_output->sample_spec);
+ s->buffer_attr.fragsize = (uint32_t) pa_usec_to_bytes(fragsize_usec, &s->source_output->sample_spec);
- if (*fragsize <= 0)
- *fragsize = (uint32_t) frame_size;
+ if (s->buffer_attr.fragsize <= 0)
+ s->buffer_attr.fragsize = (uint32_t) frame_size;
}
-static void fix_record_buffer_attr_post(
- record_stream *s,
- uint32_t *maxlength,
- uint32_t *fragsize) {
-
+/* Called from main context */
+static void fix_record_buffer_attr_post(record_stream *s) {
size_t base;
pa_assert(s);
- pa_assert(maxlength);
- pa_assert(fragsize);
- *maxlength = (uint32_t) pa_memblockq_get_maxlength(s->memblockq);
+ /* This function will be called from the main thread, before as
+ * well as after the source output has been activated using
+ * pa_source_output_put()! That means it may not touch and
+ * ->thread_info data! */
base = pa_frame_size(&s->source_output->sample_spec);
- s->fragment_size = (*fragsize/base)*base;
- if (s->fragment_size <= 0)
- s->fragment_size = base;
-
- if (s->fragment_size > *maxlength)
- s->fragment_size = *maxlength;
+ s->buffer_attr.fragsize = (s->buffer_attr.fragsize/base)*base;
+ if (s->buffer_attr.fragsize <= 0)
+ s->buffer_attr.fragsize = base;
- *fragsize = (uint32_t) s->fragment_size;
+ if (s->buffer_attr.fragsize > s->buffer_attr.maxlength)
+ s->buffer_attr.fragsize = s->buffer_attr.maxlength;
}
+/* Called from main context */
static record_stream* record_stream_new(
pa_native_connection *c,
pa_source *source,
pa_sample_spec *ss,
pa_channel_map *map,
pa_bool_t peak_detect,
- uint32_t *maxlength,
- uint32_t *fragsize,
+ pa_buffer_attr *attr,
pa_source_output_flags_t flags,
pa_proplist *p,
pa_bool_t adjust_latency,
@@ -603,7 +638,6 @@ static record_stream* record_stream_new(
pa_assert(c);
pa_assert(ss);
- pa_assert(maxlength);
pa_assert(p);
pa_assert(ret);
@@ -632,20 +666,25 @@ static record_stream* record_stream_new(
s->parent.process_msg = record_stream_process_msg;
s->connection = c;
s->source_output = source_output;
+ s->buffer_attr = *attr;
+ s->adjust_latency = adjust_latency;
+ s->early_requests = early_requests;
+ pa_atomic_store(&s->on_the_fly, 0);
+ s->source_output->parent.process_msg = source_output_process_msg;
s->source_output->push = source_output_push_cb;
s->source_output->kill = source_output_kill_cb;
s->source_output->get_latency = source_output_get_latency_cb;
- s->source_output->moved = source_output_moved_cb;
+ s->source_output->moving = source_output_moving_cb;
s->source_output->suspend = source_output_suspend_cb;
s->source_output->send_event = source_output_send_event_cb;
s->source_output->userdata = s;
- fix_record_buffer_attr_pre(s, adjust_latency, early_requests, maxlength, fragsize);
+ fix_record_buffer_attr_pre(s);
s->memblockq = pa_memblockq_new(
0,
- *maxlength,
+ s->buffer_attr.maxlength,
0,
base = pa_frame_size(&source_output->sample_spec),
1,
@@ -653,7 +692,8 @@ static record_stream* record_stream_new(
0,
NULL);
- fix_record_buffer_attr_post(s, maxlength, fragsize);
+ pa_memblockq_get_attr(s->memblockq, &s->buffer_attr);
+ fix_record_buffer_attr_post(s);
*ss = s->source_output->sample_spec;
*map = s->source_output->channel_map;
@@ -661,14 +701,15 @@ static record_stream* record_stream_new(
pa_idxset_put(c->record_streams, s, &s->index);
pa_log_info("Final latency %0.2f ms = %0.2f ms + %0.2f ms",
- ((double) pa_bytes_to_usec(s->fragment_size, &source_output->sample_spec) + (double) s->source_latency) / PA_USEC_PER_MSEC,
- (double) pa_bytes_to_usec(s->fragment_size, &source_output->sample_spec) / PA_USEC_PER_MSEC,
- (double) s->source_latency / PA_USEC_PER_MSEC);
+ ((double) pa_bytes_to_usec(s->buffer_attr.fragsize, &source_output->sample_spec) + (double) s->configured_source_latency) / PA_USEC_PER_MSEC,
+ (double) pa_bytes_to_usec(s->buffer_attr.fragsize, &source_output->sample_spec) / PA_USEC_PER_MSEC,
+ (double) s->configured_source_latency / PA_USEC_PER_MSEC);
pa_source_output_put(s->source_output);
return s;
}
+/* Called from main context */
static void record_stream_send_killed(record_stream *r) {
pa_tagstruct *t;
record_stream_assert_ref(r);
@@ -680,6 +721,7 @@ static void record_stream_send_killed(record_stream *r) {
pa_pstream_send_tagstruct(r->connection->pstream, t);
}
+/* Called from main context */
static void playback_stream_unlink(playback_stream *s) {
pa_assert(s);
@@ -700,6 +742,7 @@ static void playback_stream_unlink(playback_stream *s) {
playback_stream_unref(s);
}
+/* Called from main context */
static void playback_stream_free(pa_object* o) {
playback_stream *s = PLAYBACK_STREAM(o);
pa_assert(s);
@@ -710,6 +753,7 @@ static void playback_stream_free(pa_object* o) {
pa_xfree(s);
}
+/* Called from main context */
static int playback_stream_process_msg(pa_msgobject *o, int code, void*userdata, int64_t offset, pa_memchunk *chunk) {
playback_stream *s = PLAYBACK_STREAM(o);
playback_stream_assert_ref(s);
@@ -720,24 +764,21 @@ static int playback_stream_process_msg(pa_msgobject *o, int code, void*userdata,
switch (code) {
case PLAYBACK_STREAM_MESSAGE_REQUEST_DATA: {
pa_tagstruct *t;
- uint32_t l = 0;
+ int l = 0;
for (;;) {
- if ((l = (uint32_t) pa_atomic_load(&s->missing)) <= 0)
- break;
+ if ((l = pa_atomic_load(&s->missing)) <= 0)
+ return 0;
- if (pa_atomic_cmpxchg(&s->missing, (int) l, 0))
+ if (pa_atomic_cmpxchg(&s->missing, l, 0))
break;
}
- if (l <= 0)
- break;
-
t = pa_tagstruct_new(NULL, 0);
pa_tagstruct_putu32(t, PA_COMMAND_REQUEST);
pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
pa_tagstruct_putu32(t, s->index);
- pa_tagstruct_putu32(t, l);
+ pa_tagstruct_putu32(t, (uint32_t) l);
pa_pstream_send_tagstruct(s->connection->pstream, t);
/* pa_log("Requesting %lu bytes", (unsigned long) l); */
@@ -775,7 +816,7 @@ static int playback_stream_process_msg(pa_msgobject *o, int code, void*userdata,
if (s->connection->version >= 13) {
pa_tagstruct *t;
- /* Notify the user we're overflowed*/
+ /* Notify the user we started playback */
t = pa_tagstruct_new(NULL, 0);
pa_tagstruct_putu32(t, PA_COMMAND_STARTED);
pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
@@ -788,67 +829,79 @@ static int playback_stream_process_msg(pa_msgobject *o, int code, void*userdata,
case PLAYBACK_STREAM_MESSAGE_DRAIN_ACK:
pa_pstream_send_simple_ack(s->connection->pstream, PA_PTR_TO_UINT(userdata));
break;
+
+ case PLAYBACK_STREAM_MESSAGE_UPDATE_TLENGTH: {
+ pa_tagstruct *t;
+
+ 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);
+
+ break;
+ }
}
return 0;
}
-static void fix_playback_buffer_attr_pre(
- playback_stream *s,
- pa_bool_t adjust_latency,
- pa_bool_t early_requests,
- uint32_t *maxlength,
- uint32_t *tlength,
- uint32_t* prebuf,
- uint32_t* minreq) {
-
- size_t frame_size;
+/* Called from main context */
+static void fix_playback_buffer_attr(playback_stream *s) {
+ size_t frame_size, max_prebuf;
pa_usec_t orig_tlength_usec, tlength_usec, orig_minreq_usec, minreq_usec, sink_usec;
pa_assert(s);
- pa_assert(maxlength);
- pa_assert(tlength);
- pa_assert(prebuf);
- pa_assert(minreq);
+
+ /* This function will be called from the main thread, before as
+ * well as after the sink input has been activated using
+ * pa_sink_input_put()! That means it may not touch any
+ * ->thread_info data, such as the memblockq! */
frame_size = pa_frame_size(&s->sink_input->sample_spec);
- if (*maxlength == (uint32_t) -1 || *maxlength > MAX_MEMBLOCKQ_LENGTH)
- *maxlength = MAX_MEMBLOCKQ_LENGTH;
- if (*maxlength <= 0)
- *maxlength = (uint32_t) frame_size;
+ if (s->buffer_attr.maxlength == (uint32_t) -1 || s->buffer_attr.maxlength > MAX_MEMBLOCKQ_LENGTH)
+ s->buffer_attr.maxlength = MAX_MEMBLOCKQ_LENGTH;
+ if (s->buffer_attr.maxlength <= 0)
+ s->buffer_attr.maxlength = (uint32_t) frame_size;
- if (*tlength == (uint32_t) -1)
- *tlength = (uint32_t) pa_usec_to_bytes_round_up(DEFAULT_TLENGTH_MSEC*PA_USEC_PER_MSEC, &s->sink_input->sample_spec);
- if (*tlength <= 0)
- *tlength = (uint32_t) frame_size;
+ if (s->buffer_attr.tlength == (uint32_t) -1)
+ s->buffer_attr.tlength = (uint32_t) pa_usec_to_bytes_round_up(DEFAULT_TLENGTH_MSEC*PA_USEC_PER_MSEC, &s->sink_input->sample_spec);
+ if (s->buffer_attr.tlength <= 0)
+ s->buffer_attr.tlength = (uint32_t) frame_size;
- if (*minreq == (uint32_t) -1)
- *minreq = (uint32_t) pa_usec_to_bytes_round_up(DEFAULT_PROCESS_MSEC*PA_USEC_PER_MSEC, &s->sink_input->sample_spec);
- if (*minreq <= 0)
- *minreq = (uint32_t) frame_size;
+ if (s->buffer_attr.minreq == (uint32_t) -1)
+ s->buffer_attr.minreq = (uint32_t) pa_usec_to_bytes_round_up(DEFAULT_PROCESS_MSEC*PA_USEC_PER_MSEC, &s->sink_input->sample_spec);
+ if (s->buffer_attr.minreq <= 0)
+ s->buffer_attr.minreq = (uint32_t) frame_size;
- if (*tlength < *minreq+frame_size)
- *tlength = *minreq+(uint32_t) frame_size;
+ if (s->buffer_attr.tlength < s->buffer_attr.minreq+frame_size)
+ s->buffer_attr.tlength = s->buffer_attr.minreq+(uint32_t) frame_size;
- orig_tlength_usec = tlength_usec = pa_bytes_to_usec(*tlength, &s->sink_input->sample_spec);
- orig_minreq_usec = minreq_usec = pa_bytes_to_usec(*minreq, &s->sink_input->sample_spec);
+ orig_tlength_usec = tlength_usec = pa_bytes_to_usec(s->buffer_attr.tlength, &s->sink_input->sample_spec);
+ orig_minreq_usec = minreq_usec = pa_bytes_to_usec(s->buffer_attr.minreq, &s->sink_input->sample_spec);
pa_log_info("Requested tlength=%0.2f ms, minreq=%0.2f ms",
(double) tlength_usec / PA_USEC_PER_MSEC,
(double) minreq_usec / PA_USEC_PER_MSEC);
- if (early_requests) {
+ if (s->early_requests) {
/* In early request mode we need to emulate the classic
* fragment-based playback model. We do this setting the sink
* latency to the fragment size. */
sink_usec = minreq_usec;
-
pa_log_debug("Early requests mode enabled, configuring sink latency to minreq.");
- } else if (adjust_latency) {
+ } else if (s->adjust_latency) {
/* So, the user asked us to adjust the latency of the stream
* buffer according to the what the sink can provide. The
@@ -888,80 +941,60 @@ static void fix_playback_buffer_attr_pre(
pa_log_debug("Traditional mode enabled, modifying sink usec only for compat with minreq.");
}
- s->sink_latency = pa_sink_input_set_requested_latency(s->sink_input, sink_usec);
+ s->configured_sink_latency = pa_sink_input_set_requested_latency(s->sink_input, sink_usec);
- if (early_requests) {
+ if (s->early_requests) {
/* Ok, we didn't necessarily get what we were asking for, so
* let's tell the user */
- minreq_usec = s->sink_latency;
+ minreq_usec = s->configured_sink_latency;
- } else if (adjust_latency) {
+ } else if (s->adjust_latency) {
/* Ok, we didn't necessarily get what we were asking for, so
* let's subtract from what we asked for for the remaining
* buffer space */
- if (tlength_usec >= s->sink_latency)
- tlength_usec -= s->sink_latency;
+ if (tlength_usec >= s->configured_sink_latency)
+ tlength_usec -= s->configured_sink_latency;
}
/* FIXME: This is actually larger than necessary, since not all of
* the sink latency is actually rewritable. */
- if (tlength_usec < s->sink_latency + 2*minreq_usec)
- tlength_usec = s->sink_latency + 2*minreq_usec;
+ if (tlength_usec < s->configured_sink_latency + 2*minreq_usec)
+ tlength_usec = s->configured_sink_latency + 2*minreq_usec;
if (pa_usec_to_bytes_round_up(orig_tlength_usec, &s->sink_input->sample_spec) !=
pa_usec_to_bytes_round_up(tlength_usec, &s->sink_input->sample_spec))
- *tlength = (uint32_t) pa_usec_to_bytes_round_up(tlength_usec, &s->sink_input->sample_spec);
+ s->buffer_attr.tlength = (uint32_t) pa_usec_to_bytes_round_up(tlength_usec, &s->sink_input->sample_spec);
if (pa_usec_to_bytes(orig_minreq_usec, &s->sink_input->sample_spec) !=
pa_usec_to_bytes(minreq_usec, &s->sink_input->sample_spec))
- *minreq = (uint32_t) pa_usec_to_bytes(minreq_usec, &s->sink_input->sample_spec);
+ s->buffer_attr.minreq = (uint32_t) pa_usec_to_bytes(minreq_usec, &s->sink_input->sample_spec);
- if (*minreq <= 0) {
- *minreq = (uint32_t) frame_size;
- *tlength += (uint32_t) frame_size*2;
+ if (s->buffer_attr.minreq <= 0) {
+ s->buffer_attr.minreq = (uint32_t) frame_size;
+ s->buffer_attr.tlength += (uint32_t) frame_size*2;
}
- if (*tlength <= *minreq)
- *tlength = *minreq*2 + (uint32_t) frame_size;
-
- if (*prebuf == (uint32_t) -1 || *prebuf > *tlength)
- *prebuf = *tlength;
-}
-
-static void fix_playback_buffer_attr_post(
- playback_stream *s,
- uint32_t *maxlength,
- uint32_t *tlength,
- uint32_t* prebuf,
- uint32_t* minreq) {
-
- pa_assert(s);
- pa_assert(maxlength);
- pa_assert(tlength);
- pa_assert(prebuf);
- pa_assert(minreq);
+ if (s->buffer_attr.tlength <= s->buffer_attr.minreq)
+ s->buffer_attr.tlength = s->buffer_attr.minreq*2 + (uint32_t) frame_size;
- *maxlength = (uint32_t) pa_memblockq_get_maxlength(s->memblockq);
- *tlength = (uint32_t) pa_memblockq_get_tlength(s->memblockq);
- *prebuf = (uint32_t) pa_memblockq_get_prebuf(s->memblockq);
- *minreq = (uint32_t) pa_memblockq_get_minreq(s->memblockq);
+ max_prebuf = s->buffer_attr.tlength + (uint32_t)frame_size - s->buffer_attr.minreq;
- s->minreq = *minreq;
+ if (s->buffer_attr.prebuf == (uint32_t) -1 ||
+ s->buffer_attr.prebuf > max_prebuf)
+ s->buffer_attr.prebuf = max_prebuf;
}
+/* Called from main context */
static playback_stream* playback_stream_new(
pa_native_connection *c,
pa_sink *sink,
pa_sample_spec *ss,
pa_channel_map *map,
- uint32_t *maxlength,
- uint32_t *tlength,
- uint32_t *prebuf,
- uint32_t *minreq,
+ pa_buffer_attr *a,
pa_cvolume *volume,
pa_bool_t muted,
pa_bool_t muted_set,
@@ -982,10 +1015,6 @@ static playback_stream* playback_stream_new(
pa_assert(c);
pa_assert(ss);
- pa_assert(maxlength);
- pa_assert(tlength);
- pa_assert(prebuf);
- pa_assert(minreq);
pa_assert(missing);
pa_assert(p);
pa_assert(ret);
@@ -1042,6 +1071,9 @@ static playback_stream* playback_stream_new(
s->is_underrun = TRUE;
s->drain_request = FALSE;
pa_atomic_store(&s->missing, 0);
+ s->buffer_attr = *a;
+ s->adjust_latency = adjust_latency;
+ s->early_requests = early_requests;
s->sink_input->parent.process_msg = sink_input_process_msg;
s->sink_input->pop = sink_input_pop_cb;
@@ -1049,28 +1081,28 @@ static playback_stream* playback_stream_new(
s->sink_input->update_max_rewind = sink_input_update_max_rewind_cb;
s->sink_input->update_max_request = sink_input_update_max_request_cb;
s->sink_input->kill = sink_input_kill_cb;
- s->sink_input->moved = sink_input_moved_cb;
+ s->sink_input->moving = sink_input_moving_cb;
s->sink_input->suspend = sink_input_suspend_cb;
s->sink_input->send_event = sink_input_send_event_cb;
s->sink_input->userdata = s;
start_index = ssync ? pa_memblockq_get_read_index(ssync->memblockq) : 0;
- fix_playback_buffer_attr_pre(s, adjust_latency, early_requests, maxlength, tlength, prebuf, minreq);
- pa_sink_input_get_silence(sink_input, &silence);
+ fix_playback_buffer_attr(s);
+ pa_sink_input_get_silence(sink_input, &silence);
s->memblockq = pa_memblockq_new(
start_index,
- *maxlength,
- *tlength,
+ s->buffer_attr.maxlength,
+ s->buffer_attr.tlength,
pa_frame_size(&sink_input->sample_spec),
- *prebuf,
- *minreq,
+ s->buffer_attr.prebuf,
+ s->buffer_attr.minreq,
0,
&silence);
-
pa_memblock_unref(silence.memblock);
- fix_playback_buffer_attr_post(s, maxlength, tlength, prebuf, minreq);
+
+ pa_memblockq_get_attr(s->memblockq, &s->buffer_attr);
*missing = (uint32_t) pa_memblockq_pop_missing(s->memblockq);
@@ -1080,18 +1112,19 @@ static playback_stream* playback_stream_new(
pa_idxset_put(c->output_streams, s, &s->index);
pa_log_info("Final latency %0.2f ms = %0.2f ms + 2*%0.2f ms + %0.2f ms",
- ((double) pa_bytes_to_usec(*tlength, &sink_input->sample_spec) + (double) s->sink_latency) / PA_USEC_PER_MSEC,
- (double) pa_bytes_to_usec(*tlength-*minreq*2, &sink_input->sample_spec) / PA_USEC_PER_MSEC,
- (double) pa_bytes_to_usec(*minreq, &sink_input->sample_spec) / PA_USEC_PER_MSEC,
- (double) s->sink_latency / PA_USEC_PER_MSEC);
+ ((double) pa_bytes_to_usec(s->buffer_attr.tlength, &sink_input->sample_spec) + (double) s->configured_sink_latency) / PA_USEC_PER_MSEC,
+ (double) pa_bytes_to_usec(s->buffer_attr.tlength-s->buffer_attr.minreq*2, &sink_input->sample_spec) / PA_USEC_PER_MSEC,
+ (double) pa_bytes_to_usec(s->buffer_attr.minreq, &sink_input->sample_spec) / PA_USEC_PER_MSEC,
+ (double) s->configured_sink_latency / PA_USEC_PER_MSEC);
pa_sink_input_put(s->sink_input);
return s;
}
-/* Called from thread context */
+/* Called from IO context */
static void playback_stream_request_bytes(playback_stream *s) {
- size_t m, previous_missing;
+ size_t m, minreq;
+ int previous_missing;
playback_stream_assert_ref(s);
@@ -1102,14 +1135,16 @@ static void playback_stream_request_bytes(playback_stream *s) {
/* pa_log("request_bytes(%lu)", (unsigned long) m); */
- previous_missing = (size_t) pa_atomic_add(&s->missing, (int) m);
+ previous_missing = pa_atomic_add(&s->missing, (int) m);
+ minreq = pa_memblockq_get_minreq(s->memblockq);
if (pa_memblockq_prebuf_active(s->memblockq) ||
- (previous_missing < s->minreq && previous_missing+m >= s->minreq))
+ (previous_missing < (int) minreq && previous_missing + (int) m >= (int) minreq))
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;
playback_stream_assert_ref(p);
@@ -1121,6 +1156,7 @@ static void playback_stream_send_killed(playback_stream *p) {
pa_pstream_send_tagstruct(p->connection->pstream, t);
}
+/* Called from main context */
static int native_connection_process_msg(pa_msgobject *o, int code, void*userdata, int64_t offset, pa_memchunk *chunk) {
pa_native_connection *c = PA_NATIVE_CONNECTION(o);
pa_native_connection_assert_ref(c);
@@ -1142,6 +1178,7 @@ static int native_connection_process_msg(pa_msgobject *o, int code, void*userdat
return 0;
}
+/* Called from main context */
static void native_connection_unlink(pa_native_connection *c) {
record_stream *r;
output_stream *o;
@@ -1181,6 +1218,7 @@ static void native_connection_unlink(pa_native_connection *c) {
pa_native_connection_unref(c);
}
+/* Called from main context */
static void native_connection_free(pa_object *o) {
pa_native_connection *c = PA_NATIVE_CONNECTION(o);
@@ -1198,6 +1236,7 @@ static void native_connection_free(pa_object *o) {
pa_xfree(c);
}
+/* Called from main context */
static void native_connection_send_memblock(pa_native_connection *c) {
uint32_t start;
record_stream *r;
@@ -1217,8 +1256,8 @@ static void native_connection_send_memblock(pa_native_connection *c) {
if (pa_memblockq_peek(r->memblockq, &chunk) >= 0) {
pa_memchunk schunk = chunk;
- if (schunk.length > r->fragment_size)
- schunk.length = r->fragment_size;
+ if (schunk.length > r->buffer_attr.fragsize)
+ schunk.length = r->buffer_attr.fragsize;
pa_pstream_send_memblock(c->pstream, r->index, 0, PA_SEEK_RELATIVE, &schunk);
@@ -1232,6 +1271,7 @@ static void native_connection_send_memblock(pa_native_connection *c) {
/*** sink input callbacks ***/
+/* Called from thread context */
static void handle_seek(playback_stream *s, int64_t indexw) {
playback_stream_assert_ref(s);
@@ -1284,7 +1324,12 @@ static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int
int64_t windex;
windex = pa_memblockq_get_write_index(s->memblockq);
- pa_memblockq_seek(s->memblockq, offset, PA_PTR_TO_UINT(userdata));
+
+ /* The client side is incapable of accounting correctly
+ * for seeks of a type != PA_SEEK_RELATIVE. We need to be
+ * able to deal with that. */
+
+ pa_memblockq_seek(s->memblockq, offset, PA_PTR_TO_UINT(userdata), PA_PTR_TO_UINT(userdata) == PA_SEEK_RELATIVE);
handle_seek(s, windex);
return 0;
@@ -1302,7 +1347,7 @@ static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int
if (pa_memblockq_push_align(s->memblockq, chunk) < 0) {
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);
+ pa_memblockq_seek(s->memblockq, (int64_t) chunk->length, PA_SEEK_RELATIVE, TRUE);
}
handle_seek(s, windex);
@@ -1371,10 +1416,14 @@ static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int
}
case SINK_INPUT_MESSAGE_UPDATE_LATENCY:
-
+ /* Atomically get a snapshot of all timing parameters... */
s->read_index = pa_memblockq_get_read_index(s->memblockq);
s->write_index = pa_memblockq_get_write_index(s->memblockq);
s->render_memblockq_length = pa_memblockq_get_length(s->sink_input->thread_info.render_memblockq);
+ s->current_sink_latency = pa_sink_get_latency_within_thread(s->sink_input->sink);
+ s->underrun_for = s->sink_input->thread_info.underrun_for;
+ s->playing_for = s->sink_input->thread_info.playing_for;
+
return 0;
case PA_SINK_INPUT_MESSAGE_SET_STATE: {
@@ -1399,6 +1448,12 @@ static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int
* latency added by the resampler */
break;
}
+
+ case SINK_INPUT_MESSAGE_UPDATE_BUFFER_ATTR: {
+ pa_memblockq_apply_attr(s->memblockq, &s->buffer_attr);
+ pa_memblockq_get_attr(s->memblockq, &s->buffer_attr);
+ return 0;
+ }
}
return pa_sink_input_process_msg(o, code, userdata, offset, chunk);
@@ -1418,7 +1473,8 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk
if (pa_memblockq_is_readable(s->memblockq))
s->is_underrun = FALSE;
else {
-/* pa_log("%s, UNDERRUN: %lu", pa_proplist_gets(i->proplist, PA_PROP_MEDIA_NAME), (unsigned long) pa_memblockq_get_length(s->memblockq)); */
+ if (!s->is_underrun)
+ pa_log_debug("Underrun on '%s', %lu bytes in queue.", pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_MEDIA_NAME)), (unsigned long) pa_memblockq_get_length(s->memblockq));
if (s->drain_request && pa_sink_input_safe_to_remove(i)) {
s->drain_request = FALSE;
@@ -1447,6 +1503,7 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk
return 0;
}
+/* Called from thread context */
static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
playback_stream *s;
@@ -1461,6 +1518,7 @@ static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
pa_memblockq_rewind(s->memblockq, nbytes);
}
+/* Called from thread context */
static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) {
playback_stream *s;
@@ -1471,18 +1529,30 @@ static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) {
pa_memblockq_set_maxrewind(s->memblockq, nbytes);
}
+/* Called from thread context */
static void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes) {
playback_stream *s;
- size_t tlength;
+ size_t new_tlength, old_tlength;
pa_sink_input_assert_ref(i);
s = PLAYBACK_STREAM(i->userdata);
playback_stream_assert_ref(s);
- tlength = nbytes+2*pa_memblockq_get_minreq(s->memblockq);
+ old_tlength = pa_memblockq_get_tlength(s->memblockq);
+ new_tlength = nbytes+2*pa_memblockq_get_minreq(s->memblockq);
+
+ if (old_tlength < new_tlength) {
+ pa_log_debug("max_request changed, trying to update from %zu to %zu.", old_tlength, new_tlength);
+ pa_memblockq_set_tlength(s->memblockq, new_tlength);
+ new_tlength = pa_memblockq_get_tlength(s->memblockq);
- if (pa_memblockq_get_tlength(s->memblockq) < tlength)
- pa_memblockq_set_tlength(s->memblockq, tlength);
+ if (new_tlength == old_tlength)
+ pa_log_debug("Failed to increase tlength");
+ else {
+ pa_log_debug("Notifying client about increased tlength");
+ pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_UPDATE_TLENGTH, NULL, pa_memblockq_get_tlength(s->memblockq), NULL, NULL);
+ }
+ }
}
/* Called from main context */
@@ -1539,26 +1609,17 @@ static void sink_input_suspend_cb(pa_sink_input *i, pa_bool_t suspend) {
}
/* Called from main context */
-static void sink_input_moved_cb(pa_sink_input *i) {
+static void sink_input_moving_cb(pa_sink_input *i, pa_sink *dest) {
playback_stream *s;
pa_tagstruct *t;
- uint32_t maxlength, tlength, prebuf, minreq;
pa_sink_input_assert_ref(i);
s = PLAYBACK_STREAM(i->userdata);
playback_stream_assert_ref(s);
- maxlength = (uint32_t) pa_memblockq_get_maxlength(s->memblockq);
- tlength = (uint32_t) pa_memblockq_get_tlength(s->memblockq);
- prebuf = (uint32_t) pa_memblockq_get_prebuf(s->memblockq);
- minreq = (uint32_t) pa_memblockq_get_minreq(s->memblockq);
-
- fix_playback_buffer_attr_pre(s, TRUE, FALSE, &maxlength, &tlength, &prebuf, &minreq);
- pa_memblockq_set_maxlength(s->memblockq, maxlength);
- pa_memblockq_set_tlength(s->memblockq, tlength);
- pa_memblockq_set_prebuf(s->memblockq, prebuf);
- pa_memblockq_set_minreq(s->memblockq, minreq);
- fix_playback_buffer_attr_post(s, &maxlength, &tlength, &prebuf, &minreq);
+ fix_playback_buffer_attr(s);
+ pa_memblockq_apply_attr(s->memblockq, &s->buffer_attr);
+ pa_memblockq_get_attr(s->memblockq, &s->buffer_attr);
if (s->connection->version < 12)
return;
@@ -1567,16 +1628,16 @@ static void sink_input_moved_cb(pa_sink_input *i) {
pa_tagstruct_putu32(t, PA_COMMAND_PLAYBACK_STREAM_MOVED);
pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
pa_tagstruct_putu32(t, s->index);
- pa_tagstruct_putu32(t, i->sink->index);
- pa_tagstruct_puts(t, i->sink->name);
- pa_tagstruct_put_boolean(t, pa_sink_get_state(i->sink) == PA_SINK_SUSPENDED);
+ pa_tagstruct_putu32(t, dest->index);
+ pa_tagstruct_puts(t, dest->name);
+ pa_tagstruct_put_boolean(t, pa_sink_get_state(dest) == PA_SINK_SUSPENDED);
if (s->connection->version >= 13) {
- pa_tagstruct_putu32(t, maxlength);
- pa_tagstruct_putu32(t, tlength);
- pa_tagstruct_putu32(t, prebuf);
- pa_tagstruct_putu32(t, minreq);
- pa_tagstruct_put_usec(t, s->sink_latency);
+ 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);
@@ -1585,6 +1646,27 @@ static void sink_input_moved_cb(pa_sink_input *i) {
/*** source_output callbacks ***/
/* Called from thread context */
+static int source_output_process_msg(pa_msgobject *_o, int code, void *userdata, int64_t offset, pa_memchunk *chunk) {
+ pa_source_output *o = PA_SOURCE_OUTPUT(_o);
+ record_stream *s;
+
+ pa_source_output_assert_ref(o);
+ s = RECORD_STREAM(o->userdata);
+ record_stream_assert_ref(s);
+
+ switch (code) {
+ case SOURCE_OUTPUT_MESSAGE_UPDATE_LATENCY:
+ /* Atomically get a snapshot of all timing parameters... */
+ s->current_monitor_latency = o->source->monitor_of ? pa_sink_get_latency_within_thread(o->source->monitor_of) : 0;
+ s->current_source_latency = pa_source_get_latency_within_thread(o->source);
+ s->on_the_fly_snapshot = pa_atomic_load(&s->on_the_fly);
+ return 0;
+ }
+
+ return pa_source_output_process_msg(_o, code, userdata, offset, chunk);
+}
+
+/* Called from thread context */
static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk) {
record_stream *s;
@@ -1593,6 +1675,7 @@ static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk)
record_stream_assert_ref(s);
pa_assert(chunk);
+ pa_atomic_add(&s->on_the_fly, chunk->length);
pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), RECORD_STREAM_MESSAGE_POST_DATA, NULL, 0, chunk, NULL);
}
@@ -1661,21 +1744,18 @@ static void source_output_suspend_cb(pa_source_output *o, pa_bool_t suspend) {
}
/* Called from main context */
-static void source_output_moved_cb(pa_source_output *o) {
+static void source_output_moving_cb(pa_source_output *o, pa_source *dest) {
record_stream *s;
pa_tagstruct *t;
- uint32_t maxlength, fragsize;
pa_source_output_assert_ref(o);
s = RECORD_STREAM(o->userdata);
record_stream_assert_ref(s);
- fragsize = (uint32_t) s->fragment_size;
- maxlength = (uint32_t) pa_memblockq_get_length(s->memblockq);
-
- fix_record_buffer_attr_pre(s, TRUE, FALSE, &maxlength, &fragsize);
- pa_memblockq_set_maxlength(s->memblockq, maxlength);
- fix_record_buffer_attr_post(s, &maxlength, &fragsize);
+ 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);
+ fix_record_buffer_attr_post(s);
if (s->connection->version < 12)
return;
@@ -1684,14 +1764,14 @@ static void source_output_moved_cb(pa_source_output *o) {
pa_tagstruct_putu32(t, PA_COMMAND_RECORD_STREAM_MOVED);
pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
pa_tagstruct_putu32(t, s->index);
- pa_tagstruct_putu32(t, o->source->index);
- pa_tagstruct_puts(t, o->source->name);
- pa_tagstruct_put_boolean(t, pa_source_get_state(o->source) == PA_SOURCE_SUSPENDED);
+ pa_tagstruct_putu32(t, dest->index);
+ pa_tagstruct_puts(t, dest->name);
+ pa_tagstruct_put_boolean(t, pa_source_get_state(dest) == PA_SOURCE_SUSPENDED);
if (s->connection->version >= 13) {
- pa_tagstruct_putu32(t, maxlength);
- pa_tagstruct_putu32(t, fragsize);
- pa_tagstruct_put_usec(t, s->source_latency);
+ pa_tagstruct_putu32(t, s->buffer_attr.maxlength);
+ pa_tagstruct_putu32(t, s->buffer_attr.fragsize);
+ pa_tagstruct_put_usec(t, s->configured_source_latency);
}
pa_pstream_send_tagstruct(s->connection->pstream, t);
@@ -1723,7 +1803,8 @@ static pa_tagstruct *reply_new(uint32_t tag) {
static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
playback_stream *s;
- uint32_t maxlength, tlength, prebuf, minreq, sink_index, syncid, missing;
+ uint32_t sink_index, syncid, missing;
+ pa_buffer_attr attr;
const char *name = NULL, *sink_name;
pa_sample_spec ss;
pa_channel_map map;
@@ -1752,6 +1833,7 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
pa_native_connection_assert_ref(c);
pa_assert(t);
+ memset(&attr, 0, sizeof(attr));
if ((c->version < 13 && (pa_tagstruct_gets(t, &name) < 0 || !name)) ||
pa_tagstruct_get(
@@ -1760,11 +1842,11 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
PA_TAG_CHANNEL_MAP, &map,
PA_TAG_U32, &sink_index,
PA_TAG_STRING, &sink_name,
- PA_TAG_U32, &maxlength,
+ PA_TAG_U32, &attr.maxlength,
PA_TAG_BOOLEAN, &corked,
- PA_TAG_U32, &tlength,
- PA_TAG_U32, &prebuf,
- PA_TAG_U32, &minreq,
+ PA_TAG_U32, &attr.tlength,
+ PA_TAG_U32, &attr.prebuf,
+ PA_TAG_U32, &attr.minreq,
PA_TAG_U32, &syncid,
PA_TAG_CVOLUME, &volume,
PA_TAG_INVALID) < 0) {
@@ -1875,7 +1957,7 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
* flag. For older versions we synthesize it here */
muted_set = muted_set || muted;
- s = playback_stream_new(c, sink, &ss, &map, &maxlength, &tlength, &prebuf, &minreq, 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, &ret);
pa_proplist_free(p);
CHECK_VALIDITY(c->pstream, s, tag, ret);
@@ -1891,10 +1973,10 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
if (c->version >= 9) {
/* Since 0.9.0 we support sending the buffer metrics back to the client */
- pa_tagstruct_putu32(reply, (uint32_t) maxlength);
- pa_tagstruct_putu32(reply, (uint32_t) tlength);
- pa_tagstruct_putu32(reply, (uint32_t) prebuf);
- pa_tagstruct_putu32(reply, (uint32_t) minreq);
+ pa_tagstruct_putu32(reply, (uint32_t) s->buffer_attr.maxlength);
+ pa_tagstruct_putu32(reply, (uint32_t) s->buffer_attr.tlength);
+ pa_tagstruct_putu32(reply, (uint32_t) s->buffer_attr.prebuf);
+ pa_tagstruct_putu32(reply, (uint32_t) s->buffer_attr.minreq);
}
if (c->version >= 12) {
@@ -1912,7 +1994,7 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
}
if (c->version >= 13)
- pa_tagstruct_put_usec(reply, s->sink_latency);
+ pa_tagstruct_put_usec(reply, s->configured_sink_latency);
pa_pstream_send_tagstruct(c->pstream, reply);
}
@@ -1978,7 +2060,7 @@ static void command_delete_stream(pa_pdispatch *pd, uint32_t command, uint32_t t
static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
record_stream *s;
- uint32_t maxlength, fragment_size;
+ pa_buffer_attr attr;
uint32_t source_index;
const char *name = NULL, *source_name;
pa_sample_spec ss;
@@ -2008,14 +2090,16 @@ static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uin
pa_native_connection_assert_ref(c);
pa_assert(t);
+ memset(&attr, 0, sizeof(attr));
+
if ((c->version < 13 && (pa_tagstruct_gets(t, &name) < 0 || !name)) ||
pa_tagstruct_get_sample_spec(t, &ss) < 0 ||
pa_tagstruct_get_channel_map(t, &map) < 0 ||
pa_tagstruct_getu32(t, &source_index) < 0 ||
pa_tagstruct_gets(t, &source_name) < 0 ||
- pa_tagstruct_getu32(t, &maxlength) < 0 ||
+ pa_tagstruct_getu32(t, &attr.maxlength) < 0 ||
pa_tagstruct_get_boolean(t, &corked) < 0 ||
- pa_tagstruct_getu32(t, &fragment_size) < 0) {
+ pa_tagstruct_getu32(t, &attr.fragsize) < 0) {
protocol_error(c);
return;
}
@@ -2125,7 +2209,7 @@ static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uin
(dont_inhibit_auto_suspend ? PA_SOURCE_OUTPUT_DONT_INHIBIT_AUTO_SUSPEND : 0) |
(fail_on_suspend ? PA_SOURCE_OUTPUT_FAIL_ON_SUSPEND : 0);
- s = record_stream_new(c, source, &ss, &map, peak_detect, &maxlength, &fragment_size, flags, p, adjust_latency, direct_on_input, early_requests, &ret);
+ 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);
CHECK_VALIDITY(c->pstream, s, tag, ret);
@@ -2138,8 +2222,8 @@ static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uin
if (c->version >= 9) {
/* Since 0.9 we support sending the buffer metrics back to the client */
- pa_tagstruct_putu32(reply, (uint32_t) maxlength);
- pa_tagstruct_putu32(reply, (uint32_t) fragment_size);
+ pa_tagstruct_putu32(reply, (uint32_t) s->buffer_attr.maxlength);
+ pa_tagstruct_putu32(reply, (uint32_t) s->buffer_attr.fragsize);
}
if (c->version >= 12) {
@@ -2157,7 +2241,7 @@ static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uin
}
if (c->version >= 13)
- pa_tagstruct_put_usec(reply, s->source_latency);
+ pa_tagstruct_put_usec(reply, s->configured_source_latency);
pa_pstream_send_tagstruct(c->pstream, reply);
}
@@ -2444,7 +2528,6 @@ static void command_get_playback_latency(pa_pdispatch *pd, uint32_t command, uin
playback_stream *s;
struct timeval tv, now;
uint32_t idx;
- pa_usec_t latency;
pa_native_connection_assert_ref(c);
pa_assert(t);
@@ -2460,25 +2543,27 @@ static void command_get_playback_latency(pa_pdispatch *pd, uint32_t command, uin
s = pa_idxset_get_by_index(c->output_streams, idx);
CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
CHECK_VALIDITY(c->pstream, playback_stream_isinstance(s), tag, PA_ERR_NOENTITY);
- CHECK_VALIDITY(c->pstream, pa_asyncmsgq_send(s->sink_input->sink->asyncmsgq, PA_MSGOBJECT(s->sink_input), SINK_INPUT_MESSAGE_UPDATE_LATENCY, s, 0, NULL) == 0, tag, PA_ERR_NOENTITY)
-
- reply = reply_new(tag);
- latency = pa_sink_get_latency(s->sink_input->sink);
- latency += pa_bytes_to_usec(s->render_memblockq_length, &s->sink_input->sample_spec);
-
- pa_tagstruct_put_usec(reply, latency);
+ /* Get an atomic snapshot of all timing parameters */
+ pa_assert_se(pa_asyncmsgq_send(s->sink_input->sink->asyncmsgq, PA_MSGOBJECT(s->sink_input), SINK_INPUT_MESSAGE_UPDATE_LATENCY, s, 0, NULL) == 0);
+ reply = reply_new(tag);
+ pa_tagstruct_put_usec(reply,
+ s->current_sink_latency +
+ pa_bytes_to_usec(s->render_memblockq_length, &s->sink_input->sample_spec));
pa_tagstruct_put_usec(reply, 0);
- pa_tagstruct_put_boolean(reply, s->sink_input->thread_info.playing_for > 0);
+ pa_tagstruct_put_boolean(reply,
+ s->playing_for > 0 &&
+ pa_sink_get_state(s->sink_input->sink) == PA_SINK_RUNNING &&
+ pa_sink_input_get_state(s->sink_input) == PA_SINK_INPUT_RUNNING);
pa_tagstruct_put_timeval(reply, &tv);
pa_tagstruct_put_timeval(reply, pa_gettimeofday(&now));
pa_tagstruct_puts64(reply, s->write_index);
pa_tagstruct_puts64(reply, s->read_index);
if (c->version >= 13) {
- pa_tagstruct_putu64(reply, s->sink_input->thread_info.underrun_for);
- pa_tagstruct_putu64(reply, s->sink_input->thread_info.playing_for);
+ pa_tagstruct_putu64(reply, s->underrun_for);
+ pa_tagstruct_putu64(reply, s->playing_for);
}
pa_pstream_send_tagstruct(c->pstream, reply);
@@ -2505,10 +2590,17 @@ static void command_get_record_latency(pa_pdispatch *pd, uint32_t command, uint3
s = pa_idxset_get_by_index(c->record_streams, idx);
CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
+ /* Get an atomic snapshot of all timing parameters */
+ pa_assert_se(pa_asyncmsgq_send(s->source_output->source->asyncmsgq, PA_MSGOBJECT(s->source_output), SOURCE_OUTPUT_MESSAGE_UPDATE_LATENCY, s, 0, NULL) == 0);
+
reply = reply_new(tag);
- pa_tagstruct_put_usec(reply, s->source_output->source->monitor_of ? pa_sink_get_latency(s->source_output->source->monitor_of) : 0);
- pa_tagstruct_put_usec(reply, pa_source_get_latency(s->source_output->source));
- pa_tagstruct_put_boolean(reply, pa_source_get_state(s->source_output->source) == PA_SOURCE_RUNNING);
+ pa_tagstruct_put_usec(reply, s->current_monitor_latency);
+ pa_tagstruct_put_usec(reply,
+ s->current_source_latency +
+ pa_bytes_to_usec(s->on_the_fly_snapshot, &s->source_output->sample_spec));
+ pa_tagstruct_put_boolean(reply,
+ pa_source_get_state(s->source_output->source) == PA_SOURCE_RUNNING &&
+ pa_source_output_get_state(s->source_output) == PA_SOURCE_OUTPUT_RUNNING);
pa_tagstruct_put_timeval(reply, &tv);
pa_tagstruct_put_timeval(reply, pa_gettimeofday(&now));
pa_tagstruct_puts64(reply, pa_memblockq_get_write_index(s->memblockq));
@@ -2597,7 +2689,9 @@ static void command_finish_upload_stream(pa_pdispatch *pd, uint32_t command, uin
CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
CHECK_VALIDITY(c->pstream, upload_stream_isinstance(s), tag, PA_ERR_NOENTITY);
- if (pa_scache_add_item(c->protocol->core, s->name, &s->sample_spec, &s->channel_map, &s->memchunk, s->proplist, &idx) < 0)
+ if (!s->memchunk.memblock)
+ pa_pstream_send_error(c->pstream, tag, PA_ERR_TOOLARGE);
+ else if (pa_scache_add_item(c->protocol->core, s->name, &s->sample_spec, &s->channel_map, &s->memchunk, s->proplist, &idx) < 0)
pa_pstream_send_error(c->pstream, tag, PA_ERR_INTERNAL);
else
pa_pstream_send_simple_ack(c->pstream, tag);
@@ -2732,7 +2826,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),
+ PA_TAG_CVOLUME, pa_sink_get_volume(sink, FALSE, 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,
@@ -2754,6 +2848,23 @@ static void sink_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_sin
pa_tagstruct_putu32(t, sink->n_volume_steps);
pa_tagstruct_putu32(t, sink->card ? sink->card->index : PA_INVALID_INDEX);
}
+
+ if (c->version >= 16) {
+ pa_tagstruct_putu32(t, sink->ports ? pa_hashmap_size(sink->ports) : 0);
+
+ if (sink->ports) {
+ void *state;
+ pa_device_port *p;
+
+ PA_HASHMAP_FOREACH(p, sink->ports, state) {
+ pa_tagstruct_puts(t, p->name);
+ pa_tagstruct_puts(t, p->description);
+ pa_tagstruct_putu32(t, p->priority);
+ }
+ }
+
+ pa_tagstruct_puts(t, sink->active_port ? sink->active_port->name : NULL);
+ }
}
static void source_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_source *source) {
@@ -2794,6 +2905,24 @@ static void source_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_s
pa_tagstruct_putu32(t, source->n_volume_steps);
pa_tagstruct_putu32(t, source->card ? source->card->index : PA_INVALID_INDEX);
}
+
+ if (c->version >= 16) {
+
+ pa_tagstruct_putu32(t, source->ports ? pa_hashmap_size(source->ports) : 0);
+
+ if (source->ports) {
+ void *state;
+ pa_device_port *p;
+
+ PA_HASHMAP_FOREACH(p, source->ports, state) {
+ pa_tagstruct_puts(t, p->name);
+ pa_tagstruct_puts(t, p->description);
+ pa_tagstruct_putu32(t, p->priority);
+ }
+ }
+
+ pa_tagstruct_puts(t, source->active_port ? source->active_port->name : NULL);
+ }
}
static void client_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_client *client) {
@@ -2856,6 +2985,7 @@ static void module_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_m
static void sink_input_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_sink_input *s) {
pa_sample_spec fixed_ss;
pa_usec_t sink_latency;
+ pa_cvolume v;
pa_assert(t);
pa_sink_input_assert_ref(s);
@@ -2869,7 +2999,7 @@ static void sink_input_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t,
pa_tagstruct_putu32(t, s->sink->index);
pa_tagstruct_put_sample_spec(t, &fixed_ss);
pa_tagstruct_put_channel_map(t, &s->channel_map);
- pa_tagstruct_put_cvolume(t, pa_sink_input_get_volume(s));
+ pa_tagstruct_put_cvolume(t, pa_sink_input_get_volume(s, &v, TRUE));
pa_tagstruct_put_usec(t, pa_sink_input_get_latency(s, &sink_latency));
pa_tagstruct_put_usec(t, sink_latency);
pa_tagstruct_puts(t, pa_resample_method_to_string(pa_sink_input_get_resample_method(s)));
@@ -3094,10 +3224,10 @@ static void command_get_info_list(pa_pdispatch *pd, uint32_t command, uint32_t t
static void command_get_server_info(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
pa_tagstruct *reply;
- char txt[256];
pa_sink *def_sink;
pa_source *def_source;
pa_sample_spec fixed_ss;
+ char *h, *u;
pa_native_connection_assert_ref(c);
pa_assert(t);
@@ -3112,8 +3242,14 @@ static void command_get_server_info(pa_pdispatch *pd, uint32_t command, uint32_t
reply = reply_new(tag);
pa_tagstruct_puts(reply, PACKAGE_NAME);
pa_tagstruct_puts(reply, PACKAGE_VERSION);
- pa_tagstruct_puts(reply, pa_get_user_name(txt, sizeof(txt)));
- pa_tagstruct_puts(reply, pa_get_host_name(txt, sizeof(txt)));
+
+ u = pa_get_user_name_malloc();
+ pa_tagstruct_puts(reply, u);
+ pa_xfree(u);
+
+ h = pa_get_host_name_malloc();
+ pa_tagstruct_puts(reply, h);
+ pa_xfree(h);
fixup_sample_spec(c, &fixed_ss, &c->protocol->core->default_sample_spec);
pa_tagstruct_put_sample_spec(reply, &fixed_ss);
@@ -3234,11 +3370,11 @@ 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);
+ pa_sink_set_volume(sink, &volume, TRUE, TRUE, TRUE, TRUE);
else if (source)
- pa_source_set_volume(source, &volume);
+ pa_source_set_volume(source, &volume, TRUE);
else if (si)
- pa_sink_input_set_volume(si, &volume, TRUE);
+ pa_sink_input_set_volume(si, &volume, TRUE, TRUE);
pa_pstream_send_simple_ack(c->pstream, tag);
}
@@ -3306,9 +3442,9 @@ static void command_set_mute(
CHECK_VALIDITY(c->pstream, si || sink || source, tag, PA_ERR_NOENTITY);
if (sink)
- pa_sink_set_mute(sink, mute);
+ pa_sink_set_mute(sink, mute, TRUE);
else if (source)
- pa_source_set_mute(source, mute);
+ pa_source_set_mute(source, mute, TRUE);
else if (si)
pa_sink_input_set_mute(si, mute, TRUE);
@@ -3435,12 +3571,14 @@ static void command_flush_record_stream(pa_pdispatch *pd, uint32_t command, uint
static void command_set_stream_buffer_attr(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
uint32_t idx;
- uint32_t maxlength, tlength, prebuf, minreq, fragsize;
+ pa_buffer_attr a;
pa_tagstruct *reply;
pa_native_connection_assert_ref(c);
pa_assert(t);
+ memset(&a, 0, sizeof(a));
+
if (pa_tagstruct_getu32(t, &idx) < 0) {
protocol_error(c);
return;
@@ -3458,10 +3596,10 @@ static void command_set_stream_buffer_attr(pa_pdispatch *pd, uint32_t command, u
if (pa_tagstruct_get(
t,
- PA_TAG_U32, &maxlength,
- PA_TAG_U32, &tlength,
- PA_TAG_U32, &prebuf,
- PA_TAG_U32, &minreq,
+ PA_TAG_U32, &a.maxlength,
+ PA_TAG_U32, &a.tlength,
+ PA_TAG_U32, &a.prebuf,
+ PA_TAG_U32, &a.minreq,
PA_TAG_INVALID) < 0 ||
(c->version >= 13 && pa_tagstruct_get_boolean(t, &adjust_latency) < 0) ||
(c->version >= 14 && pa_tagstruct_get_boolean(t, &early_requests) < 0) ||
@@ -3470,21 +3608,21 @@ static void command_set_stream_buffer_attr(pa_pdispatch *pd, uint32_t command, u
return;
}
- fix_playback_buffer_attr_pre(s, adjust_latency, early_requests, &maxlength, &tlength, &prebuf, &minreq);
- pa_memblockq_set_maxlength(s->memblockq, maxlength);
- pa_memblockq_set_tlength(s->memblockq, tlength);
- pa_memblockq_set_prebuf(s->memblockq, prebuf);
- pa_memblockq_set_minreq(s->memblockq, minreq);
- fix_playback_buffer_attr_post(s, &maxlength, &tlength, &prebuf, &minreq);
+ s->adjust_latency = adjust_latency;
+ s->early_requests = early_requests;
+ s->buffer_attr = a;
+
+ fix_playback_buffer_attr(s);
+ pa_assert_se(pa_asyncmsgq_send(s->sink_input->sink->asyncmsgq, PA_MSGOBJECT(s->sink_input), SINK_INPUT_MESSAGE_UPDATE_BUFFER_ATTR, NULL, 0, NULL) == 0);
reply = reply_new(tag);
- pa_tagstruct_putu32(reply, maxlength);
- pa_tagstruct_putu32(reply, tlength);
- pa_tagstruct_putu32(reply, prebuf);
- pa_tagstruct_putu32(reply, minreq);
+ pa_tagstruct_putu32(reply, s->buffer_attr.maxlength);
+ pa_tagstruct_putu32(reply, s->buffer_attr.tlength);
+ pa_tagstruct_putu32(reply, s->buffer_attr.prebuf);
+ pa_tagstruct_putu32(reply, s->buffer_attr.minreq);
if (c->version >= 13)
- pa_tagstruct_put_usec(reply, s->sink_latency);
+ pa_tagstruct_put_usec(reply, s->configured_sink_latency);
} else {
record_stream *s;
@@ -3496,8 +3634,8 @@ static void command_set_stream_buffer_attr(pa_pdispatch *pd, uint32_t command, u
if (pa_tagstruct_get(
t,
- PA_TAG_U32, &maxlength,
- PA_TAG_U32, &fragsize,
+ PA_TAG_U32, &a.maxlength,
+ PA_TAG_U32, &a.fragsize,
PA_TAG_INVALID) < 0 ||
(c->version >= 13 && pa_tagstruct_get_boolean(t, &adjust_latency) < 0) ||
(c->version >= 14 && pa_tagstruct_get_boolean(t, &early_requests) < 0) ||
@@ -3506,16 +3644,21 @@ static void command_set_stream_buffer_attr(pa_pdispatch *pd, uint32_t command, u
return;
}
- fix_record_buffer_attr_pre(s, adjust_latency, early_requests, &maxlength, &fragsize);
- pa_memblockq_set_maxlength(s->memblockq, maxlength);
- fix_record_buffer_attr_post(s, &maxlength, &fragsize);
+ s->adjust_latency = adjust_latency;
+ s->early_requests = early_requests;
+ s->buffer_attr = a;
+
+ 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);
+ fix_record_buffer_attr_post(s);
reply = reply_new(tag);
- pa_tagstruct_putu32(reply, maxlength);
- pa_tagstruct_putu32(reply, fragsize);
+ pa_tagstruct_putu32(reply, s->buffer_attr.maxlength);
+ pa_tagstruct_putu32(reply, s->buffer_attr.fragsize);
if (c->version >= 13)
- pa_tagstruct_put_usec(reply, s->source_latency);
+ pa_tagstruct_put_usec(reply, s->configured_source_latency);
}
pa_pstream_send_tagstruct(c->pstream, reply);
@@ -3997,7 +4140,7 @@ static void command_suspend(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa
pa_log_debug("%s all sinks", b ? "Suspending" : "Resuming");
- if (pa_sink_suspend_all(c->protocol->core, b) < 0) {
+ if (pa_sink_suspend_all(c->protocol->core, b, PA_SUSPEND_USER) < 0) {
pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID);
return;
}
@@ -4011,7 +4154,7 @@ static void command_suspend(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa
CHECK_VALIDITY(c->pstream, sink, tag, PA_ERR_NOENTITY);
- if (pa_sink_suspend(sink, b) < 0) {
+ if (pa_sink_suspend(sink, b, PA_SUSPEND_USER) < 0) {
pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID);
return;
}
@@ -4024,7 +4167,7 @@ static void command_suspend(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa
pa_log_debug("%s all sources", b ? "Suspending" : "Resuming");
- if (pa_source_suspend_all(c->protocol->core, b) < 0) {
+ if (pa_source_suspend_all(c->protocol->core, b, PA_SUSPEND_USER) < 0) {
pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID);
return;
}
@@ -4039,7 +4182,7 @@ static void command_suspend(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa
CHECK_VALIDITY(c->pstream, source, tag, PA_ERR_NOENTITY);
- if (pa_source_suspend(source, b) < 0) {
+ if (pa_source_suspend(source, b, PA_SUSPEND_USER) < 0) {
pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID);
return;
}
@@ -4094,6 +4237,7 @@ static void command_set_card_profile(pa_pdispatch *pd, uint32_t command, uint32_
uint32_t idx = PA_INVALID_INDEX;
const char *name = NULL, *profile = NULL;
pa_card *card = NULL;
+ int ret;
pa_native_connection_assert_ref(c);
pa_assert(t);
@@ -4119,11 +4263,69 @@ static void command_set_card_profile(pa_pdispatch *pd, uint32_t command, uint32_
CHECK_VALIDITY(c->pstream, card, tag, PA_ERR_NOENTITY);
- if (pa_card_set_profile(card, profile) < 0) {
- pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID);
+ if ((ret = pa_card_set_profile(card, profile, TRUE)) < 0) {
+ pa_pstream_send_error(c->pstream, tag, -ret);
+ return;
+ }
+
+ pa_pstream_send_simple_ack(c->pstream, tag);
+}
+
+static void command_set_sink_or_source_port(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
+ uint32_t idx = PA_INVALID_INDEX;
+ const char *name = NULL, *port = NULL;
+ int ret;
+
+ pa_native_connection_assert_ref(c);
+ pa_assert(t);
+
+ if (pa_tagstruct_getu32(t, &idx) < 0 ||
+ pa_tagstruct_gets(t, &name) < 0 ||
+ pa_tagstruct_gets(t, &port) < 0 ||
+ !pa_tagstruct_eof(t)) {
+ protocol_error(c);
return;
}
+ 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, 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);
+
+ if (command == PA_COMMAND_SET_SINK_PORT) {
+ pa_sink *sink;
+
+ if (idx != PA_INVALID_INDEX)
+ sink = pa_idxset_get_by_index(c->protocol->core->sinks, idx);
+ else
+ sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK);
+
+ CHECK_VALIDITY(c->pstream, sink, tag, PA_ERR_NOENTITY);
+
+ if ((ret = pa_sink_set_port(sink, port, TRUE)) < 0) {
+ pa_pstream_send_error(c->pstream, tag, -ret);
+ return;
+ }
+ } else {
+ pa_source *source;
+
+ pa_assert(command = PA_COMMAND_SET_SOURCE_PORT);
+
+ if (idx != PA_INVALID_INDEX)
+ source = pa_idxset_get_by_index(c->protocol->core->sources, idx);
+ else
+ source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE);
+
+ CHECK_VALIDITY(c->pstream, source, tag, PA_ERR_NOENTITY);
+
+ if ((ret = pa_source_set_port(source, port, TRUE)) < 0) {
+ pa_pstream_send_error(c->pstream, tag, -ret);
+ return;
+ }
+ }
+
pa_pstream_send_simple_ack(c->pstream, tag);
}
@@ -4151,7 +4353,7 @@ static void pstream_memblock_callback(pa_pstream *p, uint32_t channel, int64_t o
pa_native_connection_assert_ref(c);
if (!(stream = OUTPUT_STREAM(pa_idxset_get_by_index(c->output_streams, channel)))) {
- pa_log("client sent block for invalid stream.");
+ pa_log_debug("Client sent block for invalid stream.");
/* Ignoring */
return;
}
@@ -4280,11 +4482,10 @@ static void client_send_event_cb(pa_client *client, const char*event, pa_proplis
/*** module entry points ***/
-static void auth_timeout(pa_mainloop_api*m, pa_time_event *e, const struct timeval *tv, void *userdata) {
+static void auth_timeout(pa_mainloop_api*m, pa_time_event *e, const struct timeval *t, void *userdata) {
pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
pa_assert(m);
- pa_assert(tv);
pa_native_connection_assert_ref(c);
pa_assert(c->auth_timeout_event == e);
@@ -4342,12 +4543,9 @@ void pa_native_protocol_connect(pa_native_protocol *p, pa_iochannel *io, pa_nati
c->authorized = TRUE;
}
- if (!c->authorized) {
- struct timeval tv;
- pa_gettimeofday(&tv);
- tv.tv_sec += AUTH_TIMEOUT;
- c->auth_timeout_event = p->core->mainloop->time_new(p->core->mainloop, &tv, auth_timeout, c);
- } else
+ if (!c->authorized)
+ c->auth_timeout_event = pa_core_rttime_new(p->core, pa_rtclock_now() + AUTH_TIMEOUT, auth_timeout, c);
+ else
c->auth_timeout_event = NULL;
c->is_local = pa_iochannel_socket_is_local(io);
@@ -4366,7 +4564,7 @@ void pa_native_protocol_connect(pa_native_protocol *p, pa_iochannel *io, pa_nati
pa_pstream_set_revoke_callback(c->pstream, pstream_revoke_callback, c);
pa_pstream_set_release_callback(c->pstream, pstream_release_callback, c);
- c->pdispatch = pa_pdispatch_new(p->core->mainloop, command_table, PA_COMMAND_MAX);
+ c->pdispatch = pa_pdispatch_new(p->core->mainloop, TRUE, command_table, PA_COMMAND_MAX);
c->record_streams = pa_idxset_new(NULL, NULL);
c->output_streams = pa_idxset_new(NULL, NULL);
diff --git a/src/pulsecore/protocol-simple.c b/src/pulsecore/protocol-simple.c
index 44fe5973..776d74b6 100644
--- a/src/pulsecore/protocol-simple.c
+++ b/src/pulsecore/protocol-simple.c
@@ -130,7 +130,7 @@ static void connection_unlink(connection *c) {
c->io = NULL;
}
- pa_assert_se(pa_idxset_remove_by_data(c->protocol->connections, c, NULL) == c);
+ pa_idxset_remove_by_data(c->protocol->connections, c, NULL);
c->protocol = NULL;
connection_unref(c);
}
diff --git a/src/pulsecore/pstream.c b/src/pulsecore/pstream.c
index c92b5baf..34b9eaad 100644
--- a/src/pulsecore/pstream.c
+++ b/src/pulsecore/pstream.c
@@ -683,7 +683,7 @@ static int do_read(pa_pstream *p) {
flags = ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_FLAGS]);
if (!p->use_shm && (flags & PA_FLAG_SHMMASK) != 0) {
- pa_log_warn("Recieved SHM frame on a socket where SHM is disabled.");
+ pa_log_warn("Received SHM frame on a socket where SHM is disabled.");
return -1;
}
@@ -713,7 +713,7 @@ static int do_read(pa_pstream *p) {
length = ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH]);
if (length > FRAME_SIZE_MAX_ALLOW || length <= 0) {
- pa_log_warn("Recieved invalid frame size: %lu", (unsigned long) length);
+ pa_log_warn("Received invalid frame size: %lu", (unsigned long) length);
return -1;
}
@@ -742,7 +742,7 @@ static int do_read(pa_pstream *p) {
if ((flags & PA_FLAG_SHMMASK) == PA_FLAG_SHMDATA) {
if (length != sizeof(p->read.shm_info)) {
- pa_log_warn("Recieved SHM memblock frame with Invalid frame length.");
+ pa_log_warn("Received SHM memblock frame with Invalid frame length.");
return -1;
}
@@ -757,7 +757,7 @@ static int do_read(pa_pstream *p) {
p->read.data = NULL;
} else {
- pa_log_warn("Recieved memblock frame with invalid flags value.");
+ pa_log_warn("Received memblock frame with invalid flags value.");
return -1;
}
}
diff --git a/src/pulsecore/ratelimit.c b/src/pulsecore/ratelimit.c
index 29e6fb10..844dd77d 100644
--- a/src/pulsecore/ratelimit.c
+++ b/src/pulsecore/ratelimit.c
@@ -23,13 +23,14 @@
#include <config.h>
#endif
-#include <pulsecore/rtclock.h>
+#include <pulse/rtclock.h>
+
#include <pulsecore/log.h>
#include <pulsecore/mutex.h>
#include "ratelimit.h"
-static pa_static_mutex mutex;
+static pa_static_mutex mutex = PA_STATIC_MUTEX_INIT;
/* Modelled after Linux' lib/ratelimit.c by Dave Young
* <hidave.darkstar@gmail.com>, which is licensed GPLv2. */
@@ -38,7 +39,7 @@ pa_bool_t pa_ratelimit_test(pa_ratelimit *r) {
pa_usec_t now;
pa_mutex *m;
- now = pa_rtclock_usec();
+ now = pa_rtclock_now();
m = pa_static_mutex_get(&mutex, FALSE, FALSE);
pa_mutex_lock(m);
diff --git a/src/pulsecore/refcnt.h b/src/pulsecore/refcnt.h
index 1e988326..782436b5 100644
--- a/src/pulsecore/refcnt.h
+++ b/src/pulsecore/refcnt.h
@@ -23,23 +23,59 @@
***/
#include <pulsecore/atomic.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/log.h>
+
+/* #define DEBUG_REF */
#define PA_REFCNT_DECLARE \
pa_atomic_t _ref
-#define PA_REFCNT_INIT(p) \
- pa_atomic_store(&(p)->_ref, 1)
+#define PA_REFCNT_VALUE(p) \
+ pa_atomic_load(&(p)->_ref)
#define PA_REFCNT_INIT_ZERO(p) \
pa_atomic_store(&(p)->_ref, 0)
+#ifndef DEBUG_REF
+
+#define PA_REFCNT_INIT(p) \
+ pa_atomic_store(&(p)->_ref, 1)
+
#define PA_REFCNT_INC(p) \
pa_atomic_inc(&(p)->_ref)
#define PA_REFCNT_DEC(p) \
(pa_atomic_dec(&(p)->_ref)-1)
-#define PA_REFCNT_VALUE(p) \
- pa_atomic_load(&(p)->_ref)
+#else
+
+/* If you need to debug ref counting problems define DEBUG_REF and
+ * set $PULSE_LOG_BACKTRACE=5 or suchlike in the shell when running
+ * PA */
+
+#define PA_REFCNT_INIT(p) \
+ do { \
+ pa_atomic_store(&(p)->_ref, 1); \
+ pa_log("REF: Init %p", p); \
+ } while (FALSE)
+
+#define PA_REFCNT_INC(p) \
+ do { \
+ pa_atomic_inc(&(p)->_ref); \
+ pa_log("REF: Inc %p", p); \
+ } while (FALSE) \
+
+#define PA_REFCNT_DEC(p) \
+ ({ \
+ int _j = (pa_atomic_dec(&(p)->_ref)-1); \
+ if (_j <= 0) \
+ pa_log("REF: Done %p", p); \
+ else \
+ pa_log("REF: Dec %p", p); \
+ _j; \
+ })
+
+#endif
#endif
diff --git a/src/pulsecore/rtkit.c b/src/pulsecore/rtkit.c
new file mode 100644
index 00000000..aecc4e32
--- /dev/null
+++ b/src/pulsecore/rtkit.c
@@ -0,0 +1,189 @@
+/*-*- Mode: C; c-basic-offset: 8 -*-*/
+
+/***
+ Copyright 2009 Lennart Poettering
+
+ Permission is hereby granted, free of charge, to any person
+ obtaining a copy of this software and associated documentation files
+ (the "Software"), to deal in the Software without restriction,
+ including without limitation the rights to use, copy, modify, merge,
+ publish, distribute, sublicense, and/or sell copies of the Software,
+ and to permit persons to whom the Software is furnished to do so,
+ subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+***/
+
+#include <errno.h>
+
+#include "rtkit.h"
+
+#ifdef __linux__
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/syscall.h>
+
+static pid_t _gettid(void) {
+ return (pid_t) syscall(SYS_gettid);
+}
+
+static int translate_error(const char *name) {
+ if (strcmp(name, DBUS_ERROR_NO_MEMORY) == 0)
+ return -ENOMEM;
+ if (strcmp(name, DBUS_ERROR_SERVICE_UNKNOWN) == 0 ||
+ strcmp(name, DBUS_ERROR_NAME_HAS_NO_OWNER) == 0)
+ return -ENOENT;
+ if (strcmp(name, DBUS_ERROR_ACCESS_DENIED) == 0 ||
+ strcmp(name, DBUS_ERROR_AUTH_FAILED) == 0)
+ return -EACCES;
+
+ return -EIO;
+}
+
+int rtkit_make_realtime(DBusConnection *connection, pid_t thread, int priority) {
+ DBusMessage *m = NULL, *r = NULL;
+ dbus_uint64_t u64;
+ dbus_uint32_t u32;
+ DBusError error;
+ int ret;
+
+ dbus_error_init(&error);
+
+ if (thread == 0)
+ thread = _gettid();
+
+ if (!(m = dbus_message_new_method_call(
+ RTKIT_SERVICE_NAME,
+ RTKIT_OBJECT_PATH,
+ "org.freedesktop.RealtimeKit1",
+ "MakeThreadRealtime"))) {
+ ret = -ENOMEM;
+ goto finish;
+ }
+
+ u64 = (dbus_uint64_t) thread;
+ u32 = (dbus_uint32_t) priority;
+
+ if (!dbus_message_append_args(
+ m,
+ DBUS_TYPE_UINT64, &u64,
+ DBUS_TYPE_UINT32, &u32,
+ DBUS_TYPE_INVALID)) {
+ ret = -ENOMEM;
+ goto finish;
+ }
+
+ if (!(r = dbus_connection_send_with_reply_and_block(connection, m, -1, &error))) {
+ ret = translate_error(error.name);
+ goto finish;
+ }
+
+
+ if (dbus_set_error_from_message(&error, r)) {
+ ret = translate_error(error.name);
+ goto finish;
+ }
+
+ ret = 0;
+
+finish:
+
+ if (m)
+ dbus_message_unref(m);
+
+ if (r)
+ dbus_message_unref(r);
+
+ dbus_error_free(&error);
+
+ return ret;
+}
+
+int rtkit_make_high_priority(DBusConnection *connection, pid_t thread, int nice_level) {
+ DBusMessage *m = NULL, *r = NULL;
+ dbus_uint64_t u64;
+ dbus_int32_t s32;
+ DBusError error;
+ int ret;
+
+ dbus_error_init(&error);
+
+ if (thread == 0)
+ thread = _gettid();
+
+ if (!(m = dbus_message_new_method_call(
+ RTKIT_SERVICE_NAME,
+ RTKIT_OBJECT_PATH,
+ "org.freedesktop.RealtimeKit1",
+ "MakeThreadHighPriority"))) {
+ ret = -ENOMEM;
+ goto finish;
+ }
+
+ u64 = (dbus_uint64_t) thread;
+ s32 = (dbus_int32_t) nice_level;
+
+ if (!dbus_message_append_args(
+ m,
+ DBUS_TYPE_UINT64, &u64,
+ DBUS_TYPE_INT32, &s32,
+ DBUS_TYPE_INVALID)) {
+ ret = -ENOMEM;
+ goto finish;
+ }
+
+
+
+ if (!(r = dbus_connection_send_with_reply_and_block(connection, m, -1, &error))) {
+ ret = translate_error(error.name);
+ goto finish;
+ }
+
+
+ if (dbus_set_error_from_message(&error, r)) {
+ ret = translate_error(error.name);
+ goto finish;
+ }
+
+ ret = 0;
+
+finish:
+
+ if (m)
+ dbus_message_unref(m);
+
+ if (r)
+ dbus_message_unref(r);
+
+ dbus_error_free(&error);
+
+ return ret;
+}
+
+#else
+
+int rtkit_make_realtime(DBusConnection *connection, pid_t thread, int priority) {
+ return -ENOTSUP;
+}
+
+int rtkit_make_high_priority(DBusConnection *connection, pid_t thread, int nice_level) {
+ return -ENOTSUP;
+}
+
+#endif
diff --git a/src/pulsecore/rtkit.h b/src/pulsecore/rtkit.h
new file mode 100644
index 00000000..2081b4e9
--- /dev/null
+++ b/src/pulsecore/rtkit.h
@@ -0,0 +1,62 @@
+/*-*- Mode: C; c-basic-offset: 8 -*-*/
+
+#ifndef foortkithfoo
+#define foortkithfoo
+
+/***
+ Copyright 2009 Lennart Poettering
+
+ Permission is hereby granted, free of charge, to any person
+ obtaining a copy of this software and associated documentation files
+ (the "Software"), to deal in the Software without restriction,
+ including without limitation the rights to use, copy, modify, merge,
+ publish, distribute, sublicense, and/or sell copies of the Software,
+ and to permit persons to whom the Software is furnished to do so,
+ subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+***/
+
+#include <sys/types.h>
+#include <dbus/dbus.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* This is the reference implementation for a client for
+ * RealtimeKit. You don't have to use this, but if do, just copy these
+ * sources into your repository */
+
+#define RTKIT_SERVICE_NAME "org.freedesktop.RealtimeKit1"
+#define RTKIT_OBJECT_PATH "/org/freedesktop/RealtimeKit1"
+
+/* This is mostly equivalent to sched_setparam(thread, SCHED_RR, {
+ * .sched_priority = priority }). 'thread' needs to be a kernel thread
+ * id as returned by gettid(), not a pthread_t! If 'thread' is 0 the
+ * current thread is used. The returned value is a negative errno
+ * style error code, or 0 on success. */
+int rtkit_make_realtime(DBusConnection *system_bus, pid_t thread, int priority);
+
+/* This is mostly equivalent to setpriority(PRIO_PROCESS, thread,
+ * nice_level). 'thread' needs to be a kernel thread id as returned by
+ * gettid(), not a pthread_t! If 'thread' is 0 the current thread is
+ * used. The returned value is a negative errno style error code, or 0
+ * on success.*/
+int rtkit_make_high_priority(DBusConnection *system_bus, pid_t thread, int nice_level);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/pulsecore/rtpoll.c b/src/pulsecore/rtpoll.c
index dd3e240a..c6f1ef8c 100644
--- a/src/pulsecore/rtpoll.c
+++ b/src/pulsecore/rtpoll.c
@@ -30,10 +30,6 @@
#include <string.h>
#include <errno.h>
-#ifdef __linux__
-#include <sys/utsname.h>
-#endif
-
#ifdef HAVE_POLL_H
#include <poll.h>
#else
@@ -42,12 +38,12 @@
#include <pulse/xmalloc.h>
#include <pulse/timeval.h>
+#include <pulse/rtclock.h>
#include <pulsecore/core-error.h>
-#include <pulsecore/rtclock.h>
+#include <pulsecore/core-rtclock.h>
#include <pulsecore/macro.h>
#include <pulsecore/llist.h>
-#include <pulsecore/rtsig.h>
#include <pulsecore/flist.h>
#include <pulsecore/core-util.h>
#include <pulsecore/winsock.h>
@@ -59,6 +55,8 @@
/* #define DEBUG_TIMING */
struct pa_rtpoll {
+ void *userdata;
+
struct pollfd *pollfd, *pollfd2;
unsigned n_pollfd_alloc, n_pollfd_used;
@@ -67,24 +65,11 @@ struct pa_rtpoll {
pa_usec_t elapse;
pa_bool_t timer_enabled:1;
- pa_bool_t scan_for_dead:1;
pa_bool_t running:1;
- pa_bool_t installed:1;
+ pa_bool_t scan_for_dead:1;
pa_bool_t rebuild_needed:1;
pa_bool_t quit:1;
-#if defined(HAVE_PPOLL) && defined(__linux__)
- pa_bool_t use_ppoll:1;
- pa_bool_t use_signals:1;
-
- pa_bool_t timer_armed:1;
- int rtsig;
- sigset_t sigset_unblocked;
- timer_t timer;
-#endif
-
- void *userdata;
-
#ifdef DEBUG_TIMING
pa_usec_t timestamp;
pa_usec_t slept, awake;
@@ -117,8 +102,6 @@ struct pa_rtpoll_item {
PA_STATIC_FLIST_DECLARE(items, 0, pa_xfree);
-static void signal_handler_noop(int s) { /* write(2, "signal\n", 7); */ }
-
static int item_compare(const void *_a, const void *_b) {
const pa_rtpoll_item *a = _a, *b = _b;
@@ -140,21 +123,6 @@ pa_rtpoll *pa_rtpoll_new(void) {
p->userdata = NULL;
-#if defined(HAVE_PPOLL) && defined(__linux__)
- /* ppoll() is broken on Linux < 2.6.16. Don't use it. */
- p->use_ppoll = pa_linux_newer_than(2, 6, 16);
-
- /* Starting with Linux 2.6.28 ppoll() does no longer round up
- * timeouts to multiple of HZ, hence using signal based timers is
- * no longer necessary. */
- p->use_signals = p->use_ppoll && !pa_linux_newer_than(2, 6, 28);
-
- p->rtsig = -1;
- sigemptyset(&p->sigset_unblocked);
- p->timer = (timer_t) -1;
- p->timer_armed = FALSE;
-#endif
-
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);
@@ -166,58 +134,18 @@ pa_rtpoll *pa_rtpoll_new(void) {
p->timer_enabled = FALSE;
p->running = FALSE;
- p->installed = 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_usec();
+ p->timestamp = pa_rtclock_now();
p->slept = p->awake = 0;
#endif
- return p;
-}
-
-void pa_rtpoll_install(pa_rtpoll *p) {
- pa_assert(p);
- pa_assert(!p->installed);
-
- p->installed = TRUE;
-
-#if defined(HAVE_PPOLL) && defined(__linux__)
-
- if (!p->use_signals)
- return;
-
- if ((p->rtsig = pa_rtsig_get_for_thread()) < 0) {
- pa_log_warn("Failed to reserve POSIX realtime signal.");
- return;
- }
-
- pa_log_debug("Acquired POSIX realtime signal %s", pa_sig2str(p->rtsig));
-
- {
- sigset_t ss;
- struct sigaction sa;
-
- pa_assert_se(sigemptyset(&ss) == 0);
- pa_assert_se(sigaddset(&ss, p->rtsig) == 0);
- pa_assert_se(pthread_sigmask(SIG_BLOCK, &ss, &p->sigset_unblocked) == 0);
- pa_assert_se(sigdelset(&p->sigset_unblocked, p->rtsig) == 0);
-
- memset(&sa, 0, sizeof(sa));
- sa.sa_handler = signal_handler_noop;
- pa_assert_se(sigemptyset(&sa.sa_mask) == 0);
-
- pa_assert_se(sigaction(p->rtsig, &sa, NULL) == 0);
-
- /* We never reset the signal handler. Why should we? */
- }
+ PA_LLIST_HEAD_INIT(pa_rtpoll_item, p->items);
-#endif
+ return p;
}
static void rtpoll_rebuild(pa_rtpoll *p) {
@@ -295,11 +223,6 @@ void pa_rtpoll_free(pa_rtpoll *p) {
pa_xfree(p->pollfd);
pa_xfree(p->pollfd2);
-#if defined(HAVE_PPOLL) && defined(__linux__)
- if (p->timer != (timer_t) -1)
- timer_delete(p->timer);
-#endif
-
if (p->prioq)
pa_prioq_free(p->prioq, NULL, NULL);
@@ -358,7 +281,7 @@ static pa_bool_t next_elapse(pa_rtpoll *p, pa_usec_t *usec) {
return FALSE;
}
-int pa_rtpoll_run(pa_rtpoll *p, pa_bool_t wait) {
+int pa_rtpoll_run(pa_rtpoll *p, pa_bool_t wait_op) {
pa_rtpoll_item *i;
int r = 0;
pa_usec_t timeout;
@@ -366,14 +289,16 @@ int pa_rtpoll_run(pa_rtpoll *p, pa_bool_t wait) {
pa_assert(p);
pa_assert(!p->running);
- pa_assert(p->installed);
p->running = TRUE;
/* First, let's do some work */
- for (i = p->items; i && i->priority < PA_RTPOLL_NEVER; i = i->next) {
+ PA_LLIST_FOREACH(i, p->items) {
int k;
+ if (i->priority >= PA_RTPOLL_NEVER)
+ break;
+
if (i->dead)
continue;
@@ -392,9 +317,12 @@ int pa_rtpoll_run(pa_rtpoll *p, pa_bool_t wait) {
}
/* Now let's prepare for entering the sleep */
- for (i = p->items; i && i->priority < PA_RTPOLL_NEVER; i = i->next) {
+ PA_LLIST_FOREACH(i, p->items) {
int k = 0;
+ if (i->priority >= PA_RTPOLL_NEVER)
+ break;
+
if (i->dead)
continue;
@@ -430,13 +358,13 @@ int pa_rtpoll_run(pa_rtpoll *p, pa_bool_t wait) {
timeout_valid = FALSE;
/* Calculate timeout */
- if (wait && !p->quit) {
+ if (wait_op && !p->quit) {
pa_usec_t elapse;
if (next_elapse(p, &elapse)) {
pa_usec_t now;
- now = pa_rtclock_usec();
+ now = pa_rtclock_now();
timeout = now >= elapse ? 0 : elapse - now;
timeout_valid = TRUE;
}
@@ -444,36 +372,29 @@ int pa_rtpoll_run(pa_rtpoll *p, pa_bool_t wait) {
#ifdef DEBUG_TIMING
{
- pa_usec_t now = pa_rtclock_usec();
+ pa_usec_t now = pa_rtclock_now();
p->awake = now - p->timestamp;
p->timestamp = now;
}
#endif
/* OK, now let's sleep */
-#ifdef HAVE_PPOLL
-
-#ifdef __linux__
- if (p->use_ppoll)
-#endif
{
+#ifdef HAVE_PPOLL
struct timespec ts;
- pa_timespec_store(&ts, timeout);
r = ppoll(p->pollfd, p->n_pollfd_used,
- (!wait || p->quit || timeout_valid) ? &ts : NULL,
- p->rtsig < 0 ? NULL : &p->sigset_unblocked);
- }
-#ifdef __linux__
- else
-#endif
-
-#endif
+ (!wait_op || p->quit || timeout_valid) ?
+ pa_timespec_store(&ts, timeout) : NULL, NULL);
+#else
r = poll(p->pollfd, p->n_pollfd_used,
- (!wait || p->quit || timeout_valid) ? (int) (timeout / PA_USEC_PER_MSEC) : -1);
+ (!wait_op || p->quit || timeout_valid) ?
+ (int) (timeout / PA_USEC_PER_MSEC) : -1);
+#endif
+ }
#ifdef DEBUG_TIMING
{
- pa_usec_t now = pa_rtclock_usec();
+ pa_usec_t now = pa_rtclock_now();
p->slept = now - p->timestamp;
p->timestamp = now;
@@ -493,7 +414,9 @@ int pa_rtpoll_run(pa_rtpoll *p, pa_bool_t wait) {
}
/* Let's tell everyone that we left the sleep */
- for (i = p->items; i && i->priority < PA_RTPOLL_NEVER; i = i->next) {
+ PA_LLIST_FOREACH(i, p->items) {
+ if (i->priority >= PA_RTPOLL_NEVER)
+ break;
if (i->dead)
continue;
@@ -513,86 +436,22 @@ finish:
p->scan_for_dead = FALSE;
- for (i = p->items; i; i = n) {
- n = i->next;
-
+ PA_LLIST_FOREACH_FOR_DELETE(i, n, p->items)
if (i->dead)
rtpoll_item_destroy(i);
- }
}
return r < 0 ? r : !p->quit;
}
-static void update_timer(pa_rtpoll *p) {
- pa_assert(p);
-
-#if defined(HAVE_PPOLL) && defined(__linux__)
-
- if (!p->use_signals)
- return;
-
- if (p->timer == (timer_t) -1) {
- struct sigevent se;
-
- memset(&se, 0, sizeof(se));
- se.sigev_notify = SIGEV_SIGNAL;
- se.sigev_signo = p->rtsig;
-
- if (timer_create(CLOCK_MONOTONIC, &se, &p->timer) < 0)
- if (timer_create(CLOCK_REALTIME, &se, &p->timer) < 0) {
- pa_log_warn("Failed to allocate POSIX timer: %s", pa_cstrerror(errno));
- p->timer = (timer_t) -1;
- }
- }
-
- if (p->timer != (timer_t) -1) {
- struct itimerspec its;
- struct timespec ts = { .tv_sec = 0, .tv_nsec = 0 };
- sigset_t ss;
- pa_usec_t elapse;
-
- if (p->timer_armed) {
- /* First disarm timer */
- memset(&its, 0, sizeof(its));
- pa_assert_se(timer_settime(p->timer, TIMER_ABSTIME, &its, NULL) == 0);
-
- /* Remove a signal that might be waiting in the signal q */
- pa_assert_se(sigemptyset(&ss) == 0);
- pa_assert_se(sigaddset(&ss, p->rtsig) == 0);
- sigtimedwait(&ss, NULL, &ts);
- }
-
- /* And install the new timer */
- if (next_elapse(p, &elapse)) {
- memset(&its, 0, sizeof(its));
-
- pa_timespec_store(&its.it_value, elapse);
-
- /* Make sure that 0,0 is not understood as
- * "disarming" */
- if (its.it_value.tv_sec == 0 && its.it_value.tv_nsec == 0)
- its.it_value.tv_nsec = 1;
-
- pa_assert_se(timer_settime(p->timer, TIMER_ABSTIME, &its, NULL) == 0);
- }
-
- p->timer_armed = p->timer_enabled;
- }
-
-#endif
-}
-
void pa_rtpoll_set_timer_absolute(pa_rtpoll *p, pa_usec_t usec) {
pa_assert(p);
if (p->timer_enabled && p->elapse == usec)
- return
+ return;
p->elapse = usec;
p->timer_enabled = TRUE;
-
- update_timer(p);
}
void pa_rtpoll_set_timer_relative(pa_rtpoll *p, pa_usec_t usec) {
@@ -601,7 +460,7 @@ void pa_rtpoll_set_timer_relative(pa_rtpoll *p, pa_usec_t usec) {
/* Scheduling a timeout for more than an hour is very very suspicious */
pa_assert(usec <= PA_USEC_PER_SEC*60ULL*60ULL);
- pa_rtpoll_set_timer_absolute(p, pa_rtclock_usec() + usec);
+ pa_rtpoll_set_timer_absolute(p, pa_rtclock_now() + usec);
}
void pa_rtpoll_disable_timer(pa_rtpoll *p) {
@@ -612,11 +471,8 @@ void pa_rtpoll_disable_timer(pa_rtpoll *p) {
p->elapse = 0;
p->timer_enabled = FALSE;
-
- update_timer(p);
}
-
void pa_rtpoll_set_userdata(pa_rtpoll *p, void *userdata) {
pa_assert(p);
@@ -651,7 +507,7 @@ pa_rtpoll_item *pa_rtpoll_item_new(pa_rtpoll *p, pa_rtpoll_priority_t prio, unsi
i->after_cb = NULL;
i->work_cb = NULL;
- for (j = p->items; j; j = j->next) {
+ PA_LLIST_FOREACH(j, p->items) {
if (prio <= j->priority)
break;
@@ -720,8 +576,6 @@ void pa_rtpoll_item_set_timer_absolute(pa_rtpoll_item *i, pa_usec_t usec){
pa_prioq_reshuffle(i->rtpoll->prioq, i->prioq_item);
else
i->prioq_item = pa_prioq_put(i->rtpoll->prioq, i);
-
- update_timer(i->rtpoll);
}
void pa_rtpoll_item_set_timer_relative(pa_rtpoll_item *i, pa_usec_t usec) {
@@ -730,7 +584,7 @@ void pa_rtpoll_item_set_timer_relative(pa_rtpoll_item *i, pa_usec_t usec) {
/* Scheduling a timeout for more than an hour is very very suspicious */
pa_assert(usec <= PA_USEC_PER_SEC*60ULL*60ULL);
- pa_rtpoll_item_set_timer_absolute(i, pa_rtclock_usec() + usec);
+ pa_rtpoll_item_set_timer_absolute(i, pa_rtclock_now() + usec);
}
void pa_rtpoll_item_disable_timer(pa_rtpoll_item *i) {
@@ -746,8 +600,6 @@ void pa_rtpoll_item_disable_timer(pa_rtpoll_item *i) {
pa_prioq_remove(i->rtpoll->prioq, i->prioq_item);
i->prioq_item = NULL;
}
-
- update_timer(i->rtpoll);
}
void pa_rtpoll_item_set_before_callback(pa_rtpoll_item *i, int (*before_cb)(pa_rtpoll_item *i)) {
diff --git a/src/pulsecore/rtpoll.h b/src/pulsecore/rtpoll.h
index 8c3cc70d..0373d0cb 100644
--- a/src/pulsecore/rtpoll.h
+++ b/src/pulsecore/rtpoll.h
@@ -62,9 +62,6 @@ typedef enum pa_rtpoll_priority {
pa_rtpoll *pa_rtpoll_new(void);
void pa_rtpoll_free(pa_rtpoll *p);
-/* Install the rtpoll in the current thread */
-void pa_rtpoll_install(pa_rtpoll *p);
-
/* Sleep on the rtpoll until the time event, or any of the fd events
* is triggered. If "wait" is 0 we don't sleep but only update the
* struct pollfd. Returns negative on error, positive if the loop
diff --git a/src/pulsecore/sample-util.c b/src/pulsecore/sample-util.c
index 3a9b384d..5b8ccf59 100644
--- a/src/pulsecore/sample-util.c
+++ b/src/pulsecore/sample-util.c
@@ -831,9 +831,9 @@ void pa_volume_memchunk(
calc_linear_integer_volume(linear, volume);
- e = (uint8_t*) ptr + c->length/3;
+ e = (uint8_t*) ptr + c->length;
- for (channel = 0, d = ptr; d < e; d++) {
+ for (channel = 0, d = ptr; d < e; d += 3) {
int64_t t;
t = (int64_t)((int32_t) (PA_READ24NE(d) << 8));
@@ -854,9 +854,9 @@ void pa_volume_memchunk(
calc_linear_integer_volume(linear, volume);
- e = (uint8_t*) ptr + c->length/3;
+ e = (uint8_t*) ptr + c->length;
- for (channel = 0, d = ptr; d < e; d++) {
+ for (channel = 0, d = ptr; d < e; d += 3) {
int64_t t;
t = (int64_t)((int32_t) (PA_READ24RE(d) << 8));
@@ -1181,6 +1181,8 @@ pa_memchunk* pa_silence_memchunk_get(pa_silence_cache *cache, pa_mempool *pool,
case PA_SAMPLE_S32BE:
case PA_SAMPLE_S24LE:
case PA_SAMPLE_S24BE:
+ case PA_SAMPLE_S24_32LE:
+ case PA_SAMPLE_S24_32BE:
case PA_SAMPLE_FLOAT32LE:
case PA_SAMPLE_FLOAT32BE:
cache->blocks[PA_SAMPLE_S16LE] = b = silence_memblock_new(pool, 0);
@@ -1189,6 +1191,8 @@ pa_memchunk* pa_silence_memchunk_get(pa_silence_cache *cache, pa_mempool *pool,
cache->blocks[PA_SAMPLE_S32BE] = pa_memblock_ref(b);
cache->blocks[PA_SAMPLE_S24LE] = pa_memblock_ref(b);
cache->blocks[PA_SAMPLE_S24BE] = pa_memblock_ref(b);
+ cache->blocks[PA_SAMPLE_S24_32LE] = pa_memblock_ref(b);
+ cache->blocks[PA_SAMPLE_S24_32BE] = pa_memblock_ref(b);
cache->blocks[PA_SAMPLE_FLOAT32LE] = pa_memblock_ref(b);
cache->blocks[PA_SAMPLE_FLOAT32BE] = pa_memblock_ref(b);
break;
diff --git a/src/pulsecore/sample-util.h b/src/pulsecore/sample-util.h
index 79af9efc..6a306c11 100644
--- a/src/pulsecore/sample-util.h
+++ b/src/pulsecore/sample-util.h
@@ -25,6 +25,7 @@
#include <pulse/sample.h>
#include <pulse/volume.h>
+#include <pulse/channelmap.h>
#include <pulsecore/memblock.h>
#include <pulsecore/memchunk.h>
@@ -85,4 +86,62 @@ 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);
+#define PA_CHANNEL_POSITION_MASK_LEFT \
+ (PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_LEFT) \
+ | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_REAR_LEFT) \
+ | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER) \
+ | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_SIDE_LEFT) \
+ | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_LEFT) \
+ | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_LEFT)) \
+
+#define PA_CHANNEL_POSITION_MASK_RIGHT \
+ (PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_RIGHT) \
+ | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_REAR_RIGHT) \
+ | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER) \
+ | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_SIDE_RIGHT) \
+ | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_RIGHT) \
+ | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_RIGHT))
+
+#define PA_CHANNEL_POSITION_MASK_CENTER \
+ (PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_CENTER) \
+ | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_REAR_CENTER) \
+ | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_CENTER) \
+ | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_CENTER) \
+ | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_CENTER))
+
+#define PA_CHANNEL_POSITION_MASK_FRONT \
+ (PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_LEFT) \
+ | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_RIGHT) \
+ | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_CENTER) \
+ | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER) \
+ | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER) \
+ | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_LEFT) \
+ | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_RIGHT) \
+ | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_CENTER))
+
+#define PA_CHANNEL_POSITION_MASK_REAR \
+ (PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_REAR_LEFT) \
+ | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_REAR_RIGHT) \
+ | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_REAR_CENTER) \
+ | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_LEFT) \
+ | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_RIGHT) \
+ | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_CENTER))
+
+#define PA_CHANNEL_POSITION_MASK_SIDE_OR_TOP_CENTER \
+ (PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_SIDE_LEFT) \
+ | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_SIDE_RIGHT) \
+ | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_CENTER))
+
+#define PA_CHANNEL_POSITION_MASK_TOP \
+ (PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_CENTER) \
+ | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_LEFT) \
+ | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_RIGHT) \
+ | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_CENTER) \
+ | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_LEFT) \
+ | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_RIGHT) \
+ | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_CENTER))
+
+#define PA_CHANNEL_POSITION_MASK_ALL \
+ ((pa_channel_position_mask_t) (PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_MAX)-1))
+
#endif
diff --git a/src/pulsecore/sconv-s16le.c b/src/pulsecore/sconv-s16le.c
index 307ce7b7..43b8cb3e 100644
--- a/src/pulsecore/sconv-s16le.c
+++ b/src/pulsecore/sconv-s16le.c
@@ -370,7 +370,7 @@ void pa_sconv_s24_32le_to_s16ne(unsigned n, const uint32_t *a, int16_t *b) {
pa_assert(b);
for (; n > 0; n--) {
- *b = (int16_t) ((int32_t) (UINT32_FROM(*a) << 8) >> 16);
+ *b = (int16_t) (((int32_t) (UINT32_FROM(*a) << 8)) >> 16);
a++;
b++;
}
@@ -416,8 +416,8 @@ void pa_sconv_s24_32le_to_float32ne(unsigned n, const uint32_t *a, float *b) {
pa_assert(b);
for (; n > 0; n--) {
- int32_t s = (int16_t) ((int32_t) (UINT32_FROM(*a) << 8));
- *b = ((float) s) / 0x7FFFFFFF;
+ int32_t s = (int32_t) (UINT32_FROM(*a) << 8);
+ *b = (float) s / (float) 0x7FFFFFFF;
a ++;
b ++;
}
@@ -428,8 +428,8 @@ void pa_sconv_s24_32le_to_float32re(unsigned n, const uint32_t *a, float *b) {
pa_assert(b);
for (; n > 0; n--) {
- int32_t s = (int16_t) ((int32_t) (UINT32_FROM(*a) << 8));
- float k = ((float) s) / 0x7FFFFFFF;
+ int32_t s = (int32_t) (UINT32_FROM(*a) << 8);
+ float k = (float) s / (float) 0x7FFFFFFF;
*b = PA_FLOAT32_SWAP(k);
a ++;
b ++;
diff --git a/src/pulsecore/sconv.c b/src/pulsecore/sconv.c
index 29a9a453..d89f4283 100644
--- a/src/pulsecore/sconv.c
+++ b/src/pulsecore/sconv.c
@@ -75,7 +75,7 @@ static void u8_from_s16ne(unsigned n, const int16_t *a, uint8_t *b) {
pa_assert(b);
for (; n > 0; n--, a++, b++)
- *b = (uint8_t) (*a / 0x100 + 0x80);
+ *b = (uint8_t) ((uint16_t) *a >> 8) + (uint8_t) 0x80U;
}
/* float32 */
diff --git a/src/pulsecore/semaphore-posix.c b/src/pulsecore/semaphore-posix.c
index 616d897d..2aa1bce9 100644
--- a/src/pulsecore/semaphore-posix.c
+++ b/src/pulsecore/semaphore-posix.c
@@ -65,3 +65,25 @@ void pa_semaphore_wait(pa_semaphore *s) {
pa_assert(ret == 0);
}
+
+pa_semaphore* pa_static_semaphore_get(pa_static_semaphore *s, unsigned value) {
+ pa_semaphore *m;
+
+ pa_assert(s);
+
+ /* First, check if already initialized and short cut */
+ if ((m = pa_atomic_ptr_load(&s->ptr)))
+ return m;
+
+ /* OK, not initialized, so let's allocate, and fill in */
+ m = pa_semaphore_new(value);
+ if ((pa_atomic_ptr_cmpxchg(&s->ptr, NULL, m)))
+ return m;
+
+ pa_semaphore_free(m);
+
+ /* Him, filling in failed, so someone else must have filled in
+ * already */
+ pa_assert_se(m = pa_atomic_ptr_load(&s->ptr));
+ return m;
+}
diff --git a/src/pulsecore/semaphore.h b/src/pulsecore/semaphore.h
index dc3ca6a5..2bf81496 100644
--- a/src/pulsecore/semaphore.h
+++ b/src/pulsecore/semaphore.h
@@ -22,6 +22,9 @@
USA.
***/
+#include <pulsecore/macro.h>
+#include <pulsecore/atomic.h>
+
typedef struct pa_semaphore pa_semaphore;
pa_semaphore* pa_semaphore_new(unsigned value);
@@ -30,4 +33,16 @@ void pa_semaphore_free(pa_semaphore *m);
void pa_semaphore_post(pa_semaphore *m);
void pa_semaphore_wait(pa_semaphore *m);
+/* Static semaphores are basically just atomically updated pointers to
+ * pa_semaphore objects */
+
+typedef struct pa_static_semaphore {
+ pa_atomic_ptr_t ptr;
+} pa_static_semaphore;
+
+#define PA_STATIC_SEMAPHORE_INIT { PA_ATOMIC_PTR_INIT(NULL) }
+
+/* When you call this make sure to pass always the same value parameter! */
+pa_semaphore* pa_static_semaphore_get(pa_static_semaphore *m, unsigned value);
+
#endif
diff --git a/src/pulsecore/shm.c b/src/pulsecore/shm.c
index b8c5f786..6e428426 100644
--- a/src/pulsecore/shm.c
+++ b/src/pulsecore/shm.c
@@ -39,6 +39,11 @@
#include <sys/mman.h>
#endif
+/* This is deprecated on glibc but is still used by FreeBSD */
+#if !defined(MAP_ANONYMOUS) && defined(MAP_ANON)
+# define MAP_ANONYMOUS MAP_ANON
+#endif
+
#include <pulse/xmalloc.h>
#include <pulse/gccmacro.h>
@@ -69,7 +74,10 @@
#define SHM_MARKER ((int) 0xbeefcafe)
/* We now put this SHM marker at the end of each segment. It's
- * optional, to not require a reboot when upgrading, though */
+ * optional, to not require a reboot when upgrading, though. Note that
+ * on multiarch systems 32bit and 64bit processes might access this
+ * region simultaneously. The header fields need to be independant
+ * from the process' word with */
struct shm_marker {
pa_atomic_t marker; /* 0xbeefcafe */
pa_atomic_t pid;
@@ -79,6 +87,8 @@ struct shm_marker {
uint64_t _reserved4;
} PA_GCC_PACKED;
+#define SHM_MARKER_SIZE PA_ALIGN(sizeof(struct shm_marker))
+
static char *segment_name(char *fn, size_t l, unsigned id) {
pa_snprintf(fn, l, "/pulse-shm-%u", id);
return fn;
@@ -97,8 +107,8 @@ int pa_shm_create_rw(pa_shm *m, size_t size, pa_bool_t shared, mode_t mode) {
* ones */
pa_shm_cleanup();
- /* Round up to make it aligned */
- size = PA_ALIGN(size);
+ /* Round up to make it page aligned */
+ size = PA_PAGE_ALIGN(size);
if (!shared) {
m->id = 0;
@@ -136,25 +146,25 @@ int pa_shm_create_rw(pa_shm *m, size_t size, pa_bool_t shared, mode_t mode) {
goto fail;
}
- m->size = size + PA_ALIGN(sizeof(struct shm_marker));
+ m->size = size + SHM_MARKER_SIZE;
if (ftruncate(fd, (off_t) m->size) < 0) {
pa_log("ftruncate() failed: %s", pa_cstrerror(errno));
goto fail;
}
- if ((m->ptr = mmap(NULL, m->size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, (off_t) 0)) == MAP_FAILED) {
+ if ((m->ptr = mmap(NULL, PA_PAGE_ALIGN(m->size), PROT_READ|PROT_WRITE, MAP_SHARED, fd, (off_t) 0)) == MAP_FAILED) {
pa_log("mmap() failed: %s", pa_cstrerror(errno));
goto fail;
}
/* We store our PID at the end of the shm block, so that we
* can check for dead shm segments later */
- marker = (struct shm_marker*) ((uint8_t*) m->ptr + m->size - PA_ALIGN(sizeof(struct shm_marker)));
+ marker = (struct shm_marker*) ((uint8_t*) m->ptr + m->size - SHM_MARKER_SIZE);
pa_atomic_store(&marker->pid, (int) getpid());
pa_atomic_store(&marker->marker, SHM_MARKER);
- pa_assert_se(close(fd) == 0);
+ pa_assert_se(pa_close(fd) == 0);
m->do_unlink = TRUE;
#else
return -1;
@@ -197,7 +207,7 @@ void pa_shm_free(pa_shm *m) {
#endif
} else {
#ifdef HAVE_SHM_OPEN
- if (munmap(m->ptr, m->size) < 0)
+ if (munmap(m->ptr, PA_PAGE_ALIGN(m->size)) < 0)
pa_log("munmap() failed: %s", pa_cstrerror(errno));
if (m->do_unlink) {
@@ -214,12 +224,12 @@ void pa_shm_free(pa_shm *m) {
#endif
}
- memset(m, 0, sizeof(*m));
+ pa_zero(*m);
}
void pa_shm_punch(pa_shm *m, size_t offset, size_t size) {
void *ptr;
- size_t o, ps;
+ size_t o;
pa_assert(m);
pa_assert(m->ptr);
@@ -233,16 +243,19 @@ void pa_shm_punch(pa_shm *m, size_t offset, size_t size) {
/* You're welcome to implement this as NOOP on systems that don't
* support it */
- /* Align this to multiples of the page size */
+ /* Align the pointer up to multiples of the page size */
ptr = (uint8_t*) m->ptr + offset;
o = (size_t) ((uint8_t*) ptr - (uint8_t*) PA_PAGE_ALIGN_PTR(ptr));
if (o > 0) {
- ps = PA_PAGE_SIZE;
- ptr = (uint8_t*) ptr + (ps - o);
- size -= ps - o;
+ size_t delta = PA_PAGE_SIZE - o;
+ ptr = (uint8_t*) ptr + delta;
+ size -= delta;
}
+ /* Align the size down to multiples of page size */
+ size = (size / PA_PAGE_SIZE) * PA_PAGE_SIZE;
+
#ifdef MADV_REMOVE
if (madvise(ptr, size, MADV_REMOVE) >= 0)
return;
@@ -254,9 +267,9 @@ void pa_shm_punch(pa_shm *m, size_t offset, size_t size) {
#endif
#ifdef MADV_DONTNEED
- pa_assert_se(madvise(ptr, size, MADV_DONTNEED) == 0);
+ madvise(ptr, size, MADV_DONTNEED);
#elif defined(POSIX_MADV_DONTNEED)
- pa_assert_se(posix_madvise(ptr, size, POSIX_MADV_DONTNEED) == 0);
+ posix_madvise(ptr, size, POSIX_MADV_DONTNEED);
#endif
}
@@ -283,7 +296,7 @@ int pa_shm_attach_ro(pa_shm *m, unsigned id) {
}
if (st.st_size <= 0 ||
- st.st_size > (off_t) (MAX_SHM_SIZE+PA_ALIGN(sizeof(struct shm_marker))) ||
+ st.st_size > (off_t) (MAX_SHM_SIZE+SHM_MARKER_SIZE) ||
PA_ALIGN((size_t) st.st_size) != (size_t) st.st_size) {
pa_log("Invalid shared memory segment size");
goto fail;
@@ -291,13 +304,13 @@ int pa_shm_attach_ro(pa_shm *m, unsigned id) {
m->size = (size_t) st.st_size;
- if ((m->ptr = mmap(NULL, m->size, PROT_READ, MAP_SHARED, fd, (off_t) 0)) == MAP_FAILED) {
+ if ((m->ptr = mmap(NULL, PA_PAGE_ALIGN(m->size), PROT_READ, MAP_SHARED, fd, (off_t) 0)) == MAP_FAILED) {
pa_log("mmap() failed: %s", pa_cstrerror(errno));
goto fail;
}
- m->do_unlink = 0;
- m->shared = 1;
+ m->do_unlink = FALSE;
+ m->shared = TRUE;
pa_assert_se(pa_close(fd) == 0);
@@ -346,12 +359,12 @@ int pa_shm_cleanup(void) {
if (pa_shm_attach_ro(&seg, id) < 0)
continue;
- if (seg.size < PA_ALIGN(sizeof(struct shm_marker))) {
+ if (seg.size < SHM_MARKER_SIZE) {
pa_shm_free(&seg);
continue;
}
- m = (struct shm_marker*) ((uint8_t*) seg.ptr + seg.size - PA_ALIGN(sizeof(struct shm_marker)));
+ m = (struct shm_marker*) ((uint8_t*) seg.ptr + seg.size - SHM_MARKER_SIZE);
if (pa_atomic_load(&m->marker) != SHM_MARKER) {
pa_shm_free(&seg);
diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c
index 53e727bb..a5f96351 100644
--- a/src/pulsecore/sink-input.c
+++ b/src/pulsecore/sink-input.c
@@ -117,7 +117,8 @@ static void reset_callbacks(pa_sink_input *i) {
i->attach = NULL;
i->detach = NULL;
i->suspend = NULL;
- i->moved = NULL;
+ i->suspend_within_thread = NULL;
+ i->moving = NULL;
i->kill = NULL;
i->get_latency = NULL;
i->state_change = NULL;
@@ -175,16 +176,8 @@ int pa_sink_input_new(
pa_return_val_if_fail(pa_channel_map_compatible(&data->channel_map, &data->sample_spec), -PA_ERR_INVALID);
if (!data->volume_is_set) {
-
- if (data->sink->flags & PA_SINK_FLAT_VOLUME) {
- data->volume = *pa_sink_get_volume(data->sink, FALSE);
- pa_cvolume_remap(&data->volume, &data->sink->channel_map, &data->channel_map);
- data->volume_is_absolute = TRUE;
- } else {
- pa_cvolume_reset(&data->volume, data->sample_spec.channels);
- data->volume_is_absolute = FALSE;
- }
-
+ pa_cvolume_reset(&data->volume, data->sample_spec.channels);
+ data->volume_is_absolute = FALSE;
data->save_volume = FALSE;
}
@@ -278,15 +271,15 @@ int pa_sink_input_new(
/* 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 t = *pa_sink_get_volume(data->sink, FALSE);
- pa_cvolume_remap(&t, &data->sink->channel_map, &data->channel_map);
-
- pa_sw_cvolume_multiply(&i->virtual_volume, &data->volume, &t);
+ 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);
} else
i->virtual_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->save_volume = data->save_volume;
i->save_sink = data->save_sink;
i->save_muted = data->save_muted;
@@ -333,8 +326,8 @@ int pa_sink_input_new(
0,
&i->sink->silence);
- pa_assert_se(pa_idxset_put(core->sink_inputs, pa_sink_input_ref(i), &i->index) == 0);
- pa_assert_se(pa_idxset_put(i->sink->inputs, i, NULL) == 0);
+ pa_assert_se(pa_idxset_put(core->sink_inputs, i, &i->index) == 0);
+ pa_assert_se(pa_idxset_put(i->sink->inputs, pa_sink_input_ref(i), NULL) == 0);
if (i->client)
pa_assert_se(pa_idxset_put(i->client->sink_inputs, i, NULL) >= 0);
@@ -449,7 +442,7 @@ void pa_sink_input_unlink(pa_sink_input *i) {
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);
+ pa_sink_set_volume(i->sink, &new_volume, FALSE, FALSE, FALSE, FALSE);
}
if (i->sink->asyncmsgq)
@@ -527,9 +520,9 @@ void pa_sink_input_put(pa_sink_input *i) {
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);
+ pa_sink_set_volume(i->sink, &new_volume, FALSE, FALSE, FALSE, FALSE);
} else
- pa_sw_cvolume_multiply(&i->soft_volume, &i->virtual_volume, &i->volume_factor);
+ pa_sink_input_set_relative_volume(i, &i->virtual_volume);
i->thread_info.soft_volume = i->soft_volume;
i->thread_info.muted = i->muted;
@@ -631,7 +624,7 @@ void pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink frames */, p
* data, so let's just hand out silence */
pa_atomic_store(&i->thread_info.drained, 1);
- pa_memblockq_seek(i->thread_info.render_memblockq, (int64_t) slength, PA_SEEK_RELATIVE);
+ pa_memblockq_seek(i->thread_info.render_memblockq, (int64_t) slength, PA_SEEK_RELATIVE, TRUE);
i->thread_info.playing_for = 0;
if (i->thread_info.underrun_for != (uint64_t) -1)
i->thread_info.underrun_for += ilength;
@@ -776,7 +769,7 @@ void pa_sink_input_process_rewind(pa_sink_input *i, size_t nbytes /* in sink sam
if (amount > 0)
/* Ok, now update the write pointer */
- pa_memblockq_seek(i->thread_info.render_memblockq, - ((int64_t) amount), PA_SEEK_RELATIVE);
+ pa_memblockq_seek(i->thread_info.render_memblockq, - ((int64_t) amount), PA_SEEK_RELATIVE, TRUE);
if (i->thread_info.rewrite_flush)
pa_memblockq_silence(i->thread_info.render_memblockq);
@@ -819,26 +812,15 @@ void pa_sink_input_update_max_request(pa_sink_input *i, size_t nbytes /* in the
}
/* Called from thread context */
-static pa_usec_t fixup_latency(pa_sink *s, pa_usec_t usec) {
- pa_sink_assert_ref(s);
-
- if (usec == (pa_usec_t) -1)
- return usec;
-
- if (s->thread_info.max_latency > 0 && usec > s->thread_info.max_latency)
- usec = s->thread_info.max_latency;
-
- if (s->thread_info.min_latency > 0 && usec < s->thread_info.min_latency)
- usec = s->thread_info.min_latency;
-
- return usec;
-}
-
-/* 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);
- usec = fixup_latency(i->sink, usec);
+ if (!(i->sink->flags & PA_SINK_DYNAMIC_LATENCY))
+ usec = i->sink->fixed_latency;
+
+ if (usec != (pa_usec_t) -1)
+ usec = PA_CLAMP(usec, i->sink->thread_info.min_latency, i->sink->thread_info.max_latency);
+
i->thread_info.requested_sink_latency = usec;
pa_sink_invalidate_requested_latency(i->sink);
@@ -849,41 +831,62 @@ pa_usec_t pa_sink_input_set_requested_latency_within_thread(pa_sink_input *i, pa
pa_usec_t pa_sink_input_set_requested_latency(pa_sink_input *i, pa_usec_t usec) {
pa_sink_input_assert_ref(i);
- if (PA_SINK_INPUT_IS_LINKED(i->state))
+ 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);
- else
- /* If this sink input is not realized yet, we have to touch
- * the thread info data directly */
+ return usec;
+ }
- i->thread_info.requested_sink_latency = usec;
+ /* If this sink input is not realized yet or we are being moved,
+ * we have to touch the thread info data directly */
+
+ if (i->sink) {
+ if (!(i->sink->flags & PA_SINK_DYNAMIC_LATENCY))
+ usec = i->sink->fixed_latency;
+
+ if (usec != (pa_usec_t) -1) {
+ pa_usec_t min_latency, max_latency;
+ pa_sink_get_latency_range(i->sink, &min_latency, &max_latency);
+ usec = PA_CLAMP(usec, min_latency, max_latency);
+ }
+ }
+
+ i->thread_info.requested_sink_latency = usec;
return usec;
}
/* Called from main context */
pa_usec_t pa_sink_input_get_requested_latency(pa_sink_input *i) {
- pa_usec_t usec = 0;
-
pa_sink_input_assert_ref(i);
- if (PA_SINK_INPUT_IS_LINKED(i->state))
+ if (PA_SINK_INPUT_IS_LINKED(i->state) && i->sink) {
+ pa_usec_t usec = 0;
pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_GET_REQUESTED_LATENCY, &usec, 0, NULL) == 0);
- else
- /* If this sink input is not realized yet, we have to touch
- * the thread info data directly */
- usec = i->thread_info.requested_sink_latency;
+ return usec;
+ }
- return usec;
+ /* If this sink input is not realized yet or we are being moved,
+ * we have to touch the thread info data directly */
+
+ return i->thread_info.requested_sink_latency;
}
/* Called from main context */
-void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume, pa_bool_t save) {
+void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume, pa_bool_t save, pa_bool_t absolute) {
+ pa_cvolume v;
+
pa_sink_input_assert_ref(i);
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));
+ 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_equal(volume, &i->virtual_volume))
return;
@@ -897,17 +900,18 @@ void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume, pa_boo
* 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);
+ pa_sink_set_volume(i->sink, &new_volume, FALSE, TRUE, FALSE, FALSE);
} else {
/* OK, we are in normal volume mode. The volume only affects
* ourselves */
- pa_sw_cvolume_multiply(&i->soft_volume, volume, &i->volume_factor);
+ 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);
+ /* 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);
}
@@ -916,11 +920,67 @@ void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume, pa_boo
}
/* Called from main context */
-const pa_cvolume *pa_sink_input_get_volume(pa_sink_input *i) {
+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(PA_SINK_INPUT_IS_LINKED(i->state));
- return &i->virtual_volume;
+ 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;
+
+ return volume;
+}
+
+/* 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;
+}
+
+/* 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_sink_input_assert_ref(i);
+ 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]));
+ }
+
+ /* We don't copy the data to the thread_info data. That's left for someone else to do */
}
/* Called from main context */
@@ -1090,14 +1150,16 @@ int pa_sink_input_start_move(pa_sink_input *i) {
if (i->sink->flags & PA_SINK_FLAT_VOLUME) {
pa_cvolume new_volume;
- /* Make the absolute volume relative */
- i->virtual_volume = i->soft_volume;
- i->soft_volume = i->volume_factor;
+ /* 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);
/* 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);
+ pa_sink_set_volume(i->sink, &new_volume, FALSE, FALSE, FALSE, FALSE);
}
pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_START_MOVE, i, 0, NULL) == 0);
@@ -1105,6 +1167,8 @@ int pa_sink_input_start_move(pa_sink_input *i) {
pa_sink_update_status(i->sink);
i->sink = NULL;
+ pa_sink_input_unref(i);
+
return 0;
}
@@ -1147,9 +1211,12 @@ int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest, pa_bool_t save) {
} else
new_resampler = NULL;
+ if (i->moving)
+ i->moving(i, dest);
+
i->sink = dest;
i->save_sink = save;
- pa_idxset_put(dest->inputs, i, NULL);
+ pa_idxset_put(dest->inputs, pa_sink_input_ref(i), NULL);
if (pa_sink_input_get_state(i) == PA_SINK_INPUT_CORKED)
i->sink->n_corked++;
@@ -1173,20 +1240,19 @@ int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest, pa_bool_t save) {
0,
&i->sink->silence);
}
-
pa_sink_update_status(dest);
if (i->sink->flags & PA_SINK_FLAT_VOLUME) {
pa_cvolume new_volume;
/* Make relative volume absolute again */
- pa_cvolume t = dest->virtual_volume;
+ 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);
/* 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);
+ pa_sink_set_volume(i->sink, &new_volume, FALSE, FALSE, FALSE, FALSE);
}
pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_FINISH_MOVE, i, 0, NULL) == 0);
@@ -1194,9 +1260,6 @@ int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest, pa_bool_t save) {
pa_log_debug("Successfully moved sink input %i to %s.", i->index, dest->name);
/* Notify everyone */
- if (i->moved)
- i->moved(i);
-
pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_FINISH], i);
pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
@@ -1218,11 +1281,19 @@ int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, pa_bool_t save) {
if (!pa_sink_input_may_move_to(i, dest))
return -PA_ERR_NOTSUPPORTED;
- if ((r = pa_sink_input_start_move(i)) < 0)
+ pa_sink_input_ref(i);
+
+ if ((r = pa_sink_input_start_move(i)) < 0) {
+ pa_sink_input_unref(i);
return r;
+ }
- if ((r = pa_sink_input_finish_move(i, dest, save)) < 0)
+ if ((r = pa_sink_input_finish_move(i, dest, save)) < 0) {
+ pa_sink_input_unref(i);
return r;
+ }
+
+ pa_sink_input_unref(i);
return 0;
}
@@ -1291,12 +1362,9 @@ int pa_sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t
case PA_SINK_INPUT_MESSAGE_GET_LATENCY: {
pa_usec_t *r = userdata;
- pa_usec_t sink_usec = 0;
r[0] += pa_bytes_to_usec(pa_memblockq_get_length(i->thread_info.render_memblockq), &i->sink->sample_spec);
-
- if (i->sink->parent.process_msg(PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_GET_LATENCY, &sink_usec, 0, NULL) >= 0)
- r[1] += sink_usec;
+ r[1] += pa_sink_get_latency_within_thread(i->sink);
return 0;
}
diff --git a/src/pulsecore/sink-input.h b/src/pulsecore/sink-input.h
index e3801687..98144d41 100644
--- a/src/pulsecore/sink-input.h
+++ b/src/pulsecore/sink-input.h
@@ -91,7 +91,11 @@ struct pa_sink_input {
pa_sink_input *sync_prev, *sync_next;
- pa_cvolume virtual_volume, soft_volume, volume_factor;
+ /* 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_bool_t muted:1;
/* if TRUE then the source we are connected to and/or the volume
@@ -121,7 +125,7 @@ struct pa_sink_input {
* changes. Called from IO context. */
void (*update_max_rewind) (pa_sink_input *i, size_t nbytes); /* may be NULL */
- /* Called whenever the maxiumum request size of the sink
+ /* Called whenever the maximum request size of the sink
* changes. Called from IO context. */
void (*update_max_request) (pa_sink_input *i, size_t nbytes); /* may be NULL */
@@ -144,13 +148,19 @@ struct pa_sink_input {
* disconnected from its sink. Called from IO thread context */
void (*detach) (pa_sink_input *i); /* may be NULL */
- /* If non-NULL called whenever the the sink this input is attached
+ /* If non-NULL called whenever the sink this input is attached
* to suspends or resumes. Called from main context */
void (*suspend) (pa_sink_input *i, pa_bool_t b); /* may be NULL */
- /* If non-NULL called whenever the the sink this input is attached
- * to changes. Called from main context */
- void (*moved) (pa_sink_input *i); /* may be NULL */
+ /* If non-NULL called whenever the sink this input is attached
+ * to suspends or resumes. Called from IO context */
+ void (*suspend_within_thread) (pa_sink_input *i, pa_bool_t b); /* may be NULL */
+
+ /* 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. */
+ void (*moving) (pa_sink_input *i, pa_sink *dest); /* may be NULL */
/* Supposed to unlink and destroy this stream. Called from main
* context. */
@@ -300,10 +310,14 @@ void pa_sink_input_kill(pa_sink_input*i);
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);
-const pa_cvolume *pa_sink_input_get_volume(pa_sink_input *i);
+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);
+
void pa_sink_input_update_proplist(pa_sink_input *i, pa_update_mode_t mode, pa_proplist *p);
pa_resample_method_t pa_sink_input_get_resample_method(pa_sink_input *i);
@@ -342,4 +356,7 @@ 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);
+
#endif
diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
index c725595f..d8f3c7d1 100644
--- a/src/pulsecore/sink.c
+++ b/src/pulsecore/sink.c
@@ -48,7 +48,9 @@
#define MAX_MIX_CHANNELS 32
#define MIX_BUFFER_LENGTH (PA_PAGE_SIZE)
-#define DEFAULT_MIN_LATENCY (4*PA_USEC_PER_MSEC)
+#define ABSOLUTE_MIN_LATENCY (500)
+#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);
@@ -98,11 +100,51 @@ void pa_sink_new_data_set_muted(pa_sink_new_data *data, pa_bool_t mute) {
data->muted = !!mute;
}
+void pa_sink_new_data_set_port(pa_sink_new_data *data, const char *port) {
+ pa_assert(data);
+
+ pa_xfree(data->active_port);
+ data->active_port = pa_xstrdup(port);
+}
+
void pa_sink_new_data_done(pa_sink_new_data *data) {
pa_assert(data);
- pa_xfree(data->name);
pa_proplist_free(data->proplist);
+
+ if (data->ports) {
+ pa_device_port *p;
+
+ while ((p = pa_hashmap_steal_first(data->ports)))
+ pa_device_port_free(p);
+
+ pa_hashmap_free(data->ports, NULL, NULL);
+ }
+
+ pa_xfree(data->name);
+ pa_xfree(data->active_port);
+}
+
+pa_device_port *pa_device_port_new(const char *name, const char *description, size_t extra) {
+ pa_device_port *p;
+
+ pa_assert(name);
+
+ p = pa_xmalloc(PA_ALIGN(sizeof(pa_device_port)) + extra);
+ p->name = pa_xstrdup(name);
+ p->description = pa_xstrdup(description);
+
+ p->priority = 0;
+
+ return p;
+}
+
+void pa_device_port_free(pa_device_port *p) {
+ pa_assert(p);
+
+ pa_xfree(p->name);
+ pa_xfree(p->description);
+ pa_xfree(p);
}
/* Called from main context */
@@ -116,6 +158,7 @@ static void reset_callbacks(pa_sink *s) {
s->set_mute = NULL;
s->request_rewind = NULL;
s->update_requested_latency = NULL;
+ s->set_port = NULL;
}
/* Called from main context */
@@ -138,6 +181,7 @@ pa_sink* pa_sink_new(
s = pa_msgobject_new(pa_sink);
if (!(name = pa_namereg_register(core, data->name, PA_NAMEREG_SINK, s, data->namereg_fail))) {
+ pa_log_debug("Failed to register name %s.", data->name);
pa_xfree(s);
return NULL;
}
@@ -150,6 +194,8 @@ pa_sink* pa_sink_new(
return NULL;
}
+ /* FIXME, need to free s here on failure */
+
pa_return_null_if_fail(!data->driver || pa_utf8_valid(data->driver));
pa_return_null_if_fail(data->name && pa_utf8_valid(data->name) && data->name[0]);
@@ -175,6 +221,7 @@ pa_sink* pa_sink_new(
pa_device_init_description(data->proplist);
pa_device_init_icon(data->proplist, TRUE);
+ pa_device_init_intended_roles(data->proplist);
if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SINK_FIXATE], data) < 0) {
pa_xfree(s);
@@ -188,6 +235,7 @@ pa_sink* pa_sink_new(
s->core = core;
s->state = PA_SINK_INIT;
s->flags = flags;
+ s->suspend_cause = 0;
s->name = pa_xstrdup(name);
s->proplist = pa_proplist_copy(data->proplist);
s->driver = pa_xstrdup(pa_path_get_filename(data->driver));
@@ -200,19 +248,45 @@ pa_sink* pa_sink_new(
s->inputs = pa_idxset_new(NULL, NULL);
s->n_corked = 0;
- s->virtual_volume = data->volume;
+ s->reference_volume = s->virtual_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 */
+ s->ports = data->ports;
+ data->ports = NULL;
+
+ s->active_port = NULL;
+ s->save_port = FALSE;
+
+ if (data->active_port && s->ports)
+ if ((s->active_port = pa_hashmap_get(s->ports, data->active_port)))
+ s->save_port = data->save_port;
+
+ if (!s->active_port && s->ports) {
+ void *state;
+ pa_device_port *p;
+
+ PA_HASHMAP_FOREACH(p, s->ports, state)
+ if (!s->active_port || p->priority > s->active_port->priority)
+ s->active_port = p;
+ }
+
+ s->save_volume = data->save_volume;
+ s->save_muted = data->save_muted;
+
pa_silence_memchunk_get(
&core->silence_cache,
core->mempool,
@@ -230,8 +304,8 @@ pa_sink* pa_sink_new(
s->thread_info.max_request = 0;
s->thread_info.requested_latency_valid = FALSE;
s->thread_info.requested_latency = 0;
- s->thread_info.min_latency = DEFAULT_MIN_LATENCY;
- s->thread_info.max_latency = 0;
+ s->thread_info.min_latency = ABSOLUTE_MIN_LATENCY;
+ s->thread_info.max_latency = ABSOLUTE_MAX_LATENCY;
pa_assert_se(pa_idxset_put(core->sinks, s, &s->index) >= 0);
@@ -259,7 +333,9 @@ pa_sink* pa_sink_new(
pa_proplist_setf(source_data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Monitor of %s", dn ? dn : s->name);
pa_proplist_sets(source_data.proplist, PA_PROP_DEVICE_CLASS, "monitor");
- s->monitor_source = pa_source_new(core, &source_data, PA_SOURCE_LATENCY);
+ s->monitor_source = pa_source_new(core, &source_data,
+ ((flags & PA_SINK_LATENCY) ? PA_SOURCE_LATENCY : 0) |
+ ((flags & PA_SINK_DYNAMIC_LATENCY) ? PA_SOURCE_DYNAMIC_LATENCY : 0));
pa_source_new_data_done(&source_data);
@@ -343,22 +419,30 @@ void pa_sink_put(pa_sink* s) {
/* 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 ||
- s->thread_info.min_latency <= s->thread_info.max_latency);
+ pa_assert(s->thread_info.min_latency <= s->thread_info.max_latency);
+
+ /* Generally, flags should be initialized via pa_sink_new(). As a
+ * special exception we allow volume related flags to be set
+ * between _new() and _put(). */
- if (!(s->flags & PA_SINK_HW_VOLUME_CTRL)) {
+ if (!(s->flags & PA_SINK_HW_VOLUME_CTRL))
s->flags |= PA_SINK_DECIBEL_VOLUME;
- s->thread_info.soft_volume = s->soft_volume;
- s->thread_info.soft_muted = s->muted;
- }
+ if ((s->flags & PA_SINK_DECIBEL_VOLUME) && s->core->flat_volumes)
+ s->flags |= PA_SINK_FLAT_VOLUME;
+
+ s->thread_info.soft_volume = s->soft_volume;
+ s->thread_info.soft_muted = s->muted;
- if (s->flags & PA_SINK_DECIBEL_VOLUME)
- s->n_volume_steps = PA_VOLUME_NORM+1;
+ pa_assert((s->flags & PA_SINK_HW_VOLUME_CTRL) || (s->base_volume == PA_VOLUME_NORM && s->flags & PA_SINK_DECIBEL_VOLUME));
+ pa_assert(!(s->flags & PA_SINK_DECIBEL_VOLUME) || s->n_volume_steps == PA_VOLUME_NORM+1);
+ pa_assert(!(s->flags & PA_SINK_DYNAMIC_LATENCY) == (s->fixed_latency != 0));
+ pa_assert(!(s->flags & PA_SINK_LATENCY) == !(s->monitor_source->flags & PA_SOURCE_LATENCY));
+ pa_assert(!(s->flags & PA_SINK_DYNAMIC_LATENCY) == !(s->monitor_source->flags & PA_SOURCE_DYNAMIC_LATENCY));
- if (s->core->flat_volumes)
- if (s->flags & PA_SINK_DECIBEL_VOLUME)
- s->flags |= PA_SINK_FLAT_VOLUME;
+ pa_assert(s->monitor_source->fixed_latency == s->fixed_latency);
+ pa_assert(s->monitor_source->thread_info.min_latency == s->thread_info.min_latency);
+ pa_assert(s->monitor_source->thread_info.max_latency == s->thread_info.max_latency);
pa_assert_se(sink_set_state(s, PA_SINK_IDLE) == 0);
@@ -451,6 +535,15 @@ static void sink_free(pa_object *o) {
if (s->proplist)
pa_proplist_free(s->proplist);
+ if (s->ports) {
+ pa_device_port *p;
+
+ while ((p = pa_hashmap_steal_first(s->ports)))
+ pa_device_port_free(p);
+
+ pa_hashmap_free(s->ports, NULL, NULL);
+ }
+
pa_xfree(s);
}
@@ -469,6 +562,7 @@ void pa_sink_set_rtpoll(pa_sink *s, pa_rtpoll *p) {
pa_sink_assert_ref(s);
s->rtpoll = p;
+
if (s->monitor_source)
pa_source_set_rtpoll(s->monitor_source, p);
}
@@ -485,32 +579,50 @@ int pa_sink_update_status(pa_sink*s) {
}
/* Called from main context */
-int pa_sink_suspend(pa_sink *s, pa_bool_t suspend) {
+int pa_sink_suspend(pa_sink *s, pa_bool_t suspend, pa_suspend_cause_t cause) {
pa_sink_assert_ref(s);
pa_assert(PA_SINK_IS_LINKED(s->state));
+ pa_assert(cause != 0);
+
+ if (suspend) {
+ s->suspend_cause |= cause;
+ s->monitor_source->suspend_cause |= cause;
+ } else {
+ s->suspend_cause &= ~cause;
+ s->monitor_source->suspend_cause &= ~cause;
+ }
- if (suspend)
+ if ((pa_sink_get_state(s) == PA_SINK_SUSPENDED) == !!s->suspend_cause)
+ return 0;
+
+ pa_log_debug("Suspend cause of sink %s is 0x%04x, %s", s->name, s->suspend_cause, s->suspend_cause ? "suspending" : "resuming");
+
+ if (s->suspend_cause)
return sink_set_state(s, PA_SINK_SUSPENDED);
else
return sink_set_state(s, pa_sink_used_by(s) ? PA_SINK_RUNNING : PA_SINK_IDLE);
}
/* Called from main context */
-pa_queue *pa_sink_move_all_start(pa_sink *s) {
- pa_queue *q;
+pa_queue *pa_sink_move_all_start(pa_sink *s, pa_queue *q) {
pa_sink_input *i, *n;
uint32_t idx;
pa_sink_assert_ref(s);
pa_assert(PA_SINK_IS_LINKED(s->state));
- q = pa_queue_new();
+ if (!q)
+ q = pa_queue_new();
for (i = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); i; i = n) {
n = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx));
+ pa_sink_input_ref(i);
+
if (pa_sink_input_start_move(i) >= 0)
- pa_queue_push(q, pa_sink_input_ref(i));
+ pa_queue_push(q, i);
+ else
+ pa_sink_input_unref(i);
}
return q;
@@ -769,11 +881,17 @@ void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) {
pa_sw_cvolume_multiply(&volume, &s->thread_info.soft_volume, &info[0].volume);
if (s->thread_info.soft_muted || !pa_cvolume_is_norm(&volume)) {
- pa_memchunk_make_writable(result, 0);
- if (s->thread_info.soft_muted || pa_cvolume_is_muted(&volume))
- pa_silence_memchunk(result, &s->sample_spec);
- else
+ if (s->thread_info.soft_muted || pa_cvolume_is_muted(&volume)) {
+ pa_memblock_unref(result->memblock);
+ pa_silence_memchunk_get(&s->core->silence_cache,
+ s->core->mempool,
+ result,
+ &s->sample_spec,
+ result->length);
+ } else {
+ pa_memchunk_make_writable(result, 0);
pa_volume_memchunk(result, &s->sample_spec, &volume);
+ }
}
} else {
void *ptr;
@@ -914,22 +1032,95 @@ void pa_sink_render_into_full(pa_sink *s, pa_memchunk *target) {
/* Called from IO thread context */
void pa_sink_render_full(pa_sink *s, size_t length, pa_memchunk *result) {
+ pa_mix_info info[MAX_MIX_CHANNELS];
+ size_t length1st = length;
+ unsigned n;
+
pa_sink_assert_ref(s);
pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
pa_assert(length > 0);
pa_assert(pa_frame_aligned(length, &s->sample_spec));
pa_assert(result);
+ pa_sink_ref(s);
+
pa_assert(!s->thread_info.rewind_requested);
pa_assert(s->thread_info.rewind_nbytes == 0);
- /*** This needs optimization ***/
+ pa_assert(length > 0);
+
+ 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);
- result->index = 0;
- result->length = length;
- result->memblock = pa_memblock_new(s->core->mempool, length);
+ if (result->length > length)
+ result->length = length;
- pa_sink_render_into_full(s, 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, length);
+ pa_volume_memchunk(result, &s->sample_spec, &volume);
+ }
+ }
+ } else {
+ void *ptr;
+
+ result->index = 0;
+ result->memblock = pa_memblock_new(s->core->mempool, length);
+
+ ptr = pa_memblock_acquire(result->memblock);
+
+ result->length = pa_mix(info, n,
+ (uint8_t*) ptr + result->index, length1st,
+ &s->sample_spec,
+ &s->thread_info.soft_volume,
+ s->thread_info.soft_muted);
+
+ pa_memblock_release(result->memblock);
+ }
+
+ inputs_drop(s, info, n, result);
+
+ if (result->length < length) {
+ pa_memchunk chunk;
+ size_t l, d;
+ pa_memchunk_make_writable(result, length);
+
+ l = length - result->length;
+ d = result->index + result->length;
+ while (l > 0) {
+ chunk = *result;
+ chunk.index = d;
+ chunk.length = l;
+
+ pa_sink_render_into(s, &chunk);
+
+ d += chunk.length;
+ l -= chunk.length;
+ }
+ result->length = length;
+ }
+
+ pa_sink_unref(s);
}
/* Called from main thread */
@@ -952,6 +1143,72 @@ pa_usec_t pa_sink_get_latency(pa_sink *s) {
return usec;
}
+/* Called from IO thread */
+pa_usec_t pa_sink_get_latency_within_thread(pa_sink *s) {
+ pa_usec_t usec = 0;
+ pa_msgobject *o;
+
+ pa_sink_assert_ref(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! */
+
+ if (s->thread_info.state == PA_SINK_SUSPENDED)
+ return 0;
+
+ if (!(s->flags & PA_SINK_LATENCY))
+ return 0;
+
+ o = PA_MSGOBJECT(s);
+
+ /* 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;
+
+ return usec;
+}
+
+static void compute_new_soft_volume(pa_sink_input *i, const pa_cvolume *new_volume) {
+ unsigned c;
+
+ pa_sink_input_assert_ref(i);
+ pa_assert(new_volume->channels == i->sample_spec.channels);
+
+ /*
+ * This basically calculates:
+ *
+ * i->relative_volume := i->virtual_volume / new_volume
+ * i->soft_volume := i->relative_volume * i->volume_factor
+ */
+
+ /* The new sink volume passed in here must already be remapped to
+ * the sink input's channel map! */
+
+ i->soft_volume.channels = i->sample_spec.channels;
+
+ for (c = 0; c < i->sample_spec.channels; c++)
+
+ 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]);
+
+ i->soft_volume.values[c] = pa_sw_volume_from_linear(
+ i->relative_volume[c] *
+ pa_sw_volume_to_linear(i->volume_factor.values[c]));
+ }
+
+ /* 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);
+
+ /* 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) {
pa_sink_input *i;
@@ -962,16 +1219,16 @@ void pa_sink_update_flat_volume(pa_sink *s, pa_cvolume *new_volume) {
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 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_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().*/
if (pa_idxset_isempty(s->inputs)) {
/* In the special case that we have no sink input we leave the
* volume unmodified. */
- *new_volume = s->virtual_volume;
+ *new_volume = s->reference_volume;
return;
}
@@ -998,26 +1255,22 @@ void pa_sink_update_flat_volume(pa_sink *s, pa_cvolume *new_volume) {
remapped_new_volume = *new_volume;
pa_cvolume_remap(&remapped_new_volume, &s->channel_map, &i->channel_map);
- pa_sw_cvolume_divide(&i->soft_volume, &i->virtual_volume, &remapped_new_volume);
- pa_sw_cvolume_multiply(&i->soft_volume, &i->soft_volume, &i->volume_factor);
+ compute_new_soft_volume(i, &remapped_new_volume);
- /* Hooks have the ability to play games with i->soft_volume */
- pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_INPUT_SET_VOLUME], i);
-
- /* We don't issue PA_SINK_INPUT_MESSAGE_SET_VOLUME because
- * we want the update to have atomically with the sink
- * volume update, hence we do it within the
- * pa_sink_set_flat_volume() call below*/
+ /* 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 */
}
}
/* Called from main thread */
-void pa_sink_propagate_flat_volume(pa_sink *s, const pa_cvolume *old_volume) {
+void pa_sink_propagate_flat_volume(pa_sink *s) {
pa_sink_input *i;
uint32_t idx;
pa_sink_assert_ref(s);
- pa_assert(old_volume);
pa_assert(PA_SINK_IS_LINKED(s->state));
pa_assert(s->flags & PA_SINK_FLAT_VOLUME);
@@ -1026,39 +1279,43 @@ void pa_sink_propagate_flat_volume(pa_sink *s, const pa_cvolume *old_volume) {
* 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 remapped_old_volume, remapped_new_volume, fixed_volume;
+ pa_cvolume sink_volume, new_virtual_volume;
unsigned c;
- remapped_new_volume = s->virtual_volume;
- pa_cvolume_remap(&remapped_new_volume, &s->channel_map, &i->channel_map);
+ /* This basically calculates i->virtual_volume := i->relative_volume * s->virtual_volume */
- remapped_old_volume = *old_volume;
- pa_cvolume_remap(&remapped_old_volume, &s->channel_map, &i->channel_map);
+ sink_volume = s->virtual_volume;
+ pa_cvolume_remap(&sink_volume, &s->channel_map, &i->channel_map);
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]));
- if (remapped_old_volume.values[c] == PA_VOLUME_MUTED)
- fixed_volume.values[c] = PA_VOLUME_MUTED;
- else
- fixed_volume.values[c] = (pa_volume_t)
- ((uint64_t) i->virtual_volume.values[c] *
- (uint64_t) remapped_new_volume.values[c] /
- (uint64_t) remapped_old_volume.values[c]);
+ new_virtual_volume.channels = i->sample_spec.channels;
- fixed_volume.channels = i->virtual_volume.channels;
+ if (!pa_cvolume_equal(&new_virtual_volume, &i->virtual_volume)) {
+ i->virtual_volume = new_virtual_volume;
- if (!pa_cvolume_equal(&fixed_volume, &i->virtual_volume)) {
- i->virtual_volume = fixed_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);
/* 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_cvolume old_virtual_volume;
+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;
pa_sink_assert_ref(s);
@@ -1067,19 +1324,22 @@ void pa_sink_set_volume(pa_sink *s, const pa_cvolume *volume, pa_bool_t propagat
pa_assert(pa_cvolume_valid(volume));
pa_assert(pa_cvolume_compatible(volume, &s->sample_spec));
- old_virtual_volume = s->virtual_volume;
+ virtual_volume_changed = !pa_cvolume_equal(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;
+
+ if (become_reference)
+ s->reference_volume = s->virtual_volume;
/* 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_virtual_volume);
+ pa_sink_propagate_flat_volume(s);
if (s->set_volume) {
/* If we have a function set_volume(), then we do not apply a
- * soft volume by default. However, set_volume() is apply one
- * to s->soft_volume */
+ * soft volume by default. However, set_volume() is free to
+ * apply one to s->soft_volume */
pa_cvolume_reset(&s->soft_volume, s->sample_spec.channels);
s->set_volume(s);
@@ -1111,7 +1371,7 @@ void pa_sink_set_soft_volume(pa_sink *s, const pa_cvolume *volume) {
}
/* Called from main thread */
-const pa_cvolume *pa_sink_get_volume(pa_sink *s, pa_bool_t force_refresh) {
+const pa_cvolume *pa_sink_get_volume(pa_sink *s, pa_bool_t force_refresh, pa_bool_t reference) {
pa_sink_assert_ref(s);
if (s->refresh_volume || force_refresh) {
@@ -1124,18 +1384,39 @@ const pa_cvolume *pa_sink_get_volume(pa_sink *s, pa_bool_t force_refresh) {
if (!pa_cvolume_equal(&old_virtual_volume, &s->virtual_volume)) {
+ s->reference_volume = s->virtual_volume;
+
if (s->flags & PA_SINK_FLAT_VOLUME)
- pa_sink_propagate_flat_volume(s, &old_virtual_volume);
+ pa_sink_propagate_flat_volume(s);
pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
}
}
- return &s->virtual_volume;
+ return reference ? &s->reference_volume : &s->virtual_volume;
}
/* Called from main thread */
-void pa_sink_set_mute(pa_sink *s, pa_bool_t mute) {
+void pa_sink_volume_changed(pa_sink *s, const pa_cvolume *new_volume, pa_bool_t save) {
+ pa_sink_assert_ref(s);
+
+ /* 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;
+ }
+
+ s->reference_volume = s->virtual_volume = *new_volume;
+ s->save_volume = save;
+
+ if (s->flags & PA_SINK_FLAT_VOLUME)
+ pa_sink_propagate_flat_volume(s);
+
+ pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+}
+
+/* Called from main thread */
+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);
@@ -1143,6 +1424,7 @@ void pa_sink_set_mute(pa_sink *s, pa_bool_t mute) {
old_muted = s->muted;
s->muted = mute;
+ s->save_muted = (old_muted == s->muted && s->save_muted) || save;
if (s->set_mute)
s->set_mute(s);
@@ -1166,20 +1448,40 @@ pa_bool_t pa_sink_get_mute(pa_sink *s, pa_bool_t force_refresh) {
pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_MUTE, NULL, 0, NULL) == 0);
- if (old_muted != s->muted)
+ if (old_muted != s->muted) {
pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+
+ /* Make sure the soft mute status stays in sync */
+ pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_MUTE, NULL, 0, NULL) == 0);
+ }
}
return s->muted;
}
/* Called from main thread */
-pa_bool_t pa_sink_update_proplist(pa_sink *s, pa_update_mode_t mode, pa_proplist *p) {
+void pa_sink_mute_changed(pa_sink *s, pa_bool_t new_muted, pa_bool_t save) {
+ pa_sink_assert_ref(s);
+
+ /* 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;
+ return;
+ }
+
+ s->muted = new_muted;
+ s->save_muted = save;
+
+ pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+}
+/* 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(p);
- pa_proplist_update(s->proplist, mode, p);
+ if (p)
+ pa_proplist_update(s->proplist, mode, p);
if (PA_SINK_IS_LINKED(s->state)) {
pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_PROPLIST_CHANGED], s);
@@ -1268,7 +1570,7 @@ unsigned pa_sink_check_suspend(pa_sink *s) {
ret = 0;
- 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) {
pa_sink_input_state_t st;
st = pa_sink_input_get_state(i);
@@ -1508,9 +1810,13 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse
pa_sink_request_rewind(s, (size_t) -1);
}
- if (s->flags & PA_SINK_FLAT_VOLUME)
- sync_input_volumes_within_thread(s);
+ if (!(s->flags & PA_SINK_FLAT_VOLUME))
+ return 0;
+ /* Fall through ... */
+
+ case PA_SINK_MESSAGE_SYNC_VOLUMES:
+ sync_input_volumes_within_thread(s);
return 0;
case PA_SINK_MESSAGE_GET_VOLUME:
@@ -1528,7 +1834,11 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse
case PA_SINK_MESSAGE_GET_MUTE:
return 0;
- case PA_SINK_MESSAGE_SET_STATE:
+ case PA_SINK_MESSAGE_SET_STATE: {
+
+ pa_bool_t suspend_change =
+ (s->thread_info.state == PA_SINK_SUSPENDED && PA_SINK_IS_OPENED(PA_PTR_TO_UINT(userdata))) ||
+ (PA_SINK_IS_OPENED(s->thread_info.state) && PA_PTR_TO_UINT(userdata) == PA_SINK_SUSPENDED);
s->thread_info.state = PA_PTR_TO_UINT(userdata);
@@ -1537,7 +1847,17 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse
s->thread_info.rewind_requested = FALSE;
}
+ if (suspend_change) {
+ pa_sink_input *i;
+ void *state = NULL;
+
+ while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
+ if (i->suspend_within_thread)
+ i->suspend_within_thread(i, s->thread_info.state == PA_SINK_SUSPENDED);
+ }
+
return 0;
+ }
case PA_SINK_MESSAGE_DETACH:
@@ -1565,7 +1885,7 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse
case PA_SINK_MESSAGE_SET_LATENCY_RANGE: {
pa_usec_t *r = userdata;
- pa_sink_update_latency_range(s, r[0], r[1]);
+ pa_sink_set_latency_range_within_thread(s, r[0], r[1]);
return 0;
}
@@ -1589,6 +1909,16 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse
*((size_t*) userdata) = s->thread_info.max_request;
return 0;
+ case PA_SINK_MESSAGE_SET_MAX_REWIND:
+
+ pa_sink_set_max_rewind_within_thread(s, (size_t) offset);
+ return 0;
+
+ case PA_SINK_MESSAGE_SET_MAX_REQUEST:
+
+ pa_sink_set_max_request_within_thread(s, (size_t) offset);
+ return 0;
+
case PA_SINK_MESSAGE_GET_LATENCY:
case PA_SINK_MESSAGE_MAX:
;
@@ -1598,17 +1928,18 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse
}
/* Called from main thread */
-int pa_sink_suspend_all(pa_core *c, pa_bool_t suspend) {
+int pa_sink_suspend_all(pa_core *c, pa_bool_t suspend, pa_suspend_cause_t cause) {
pa_sink *sink;
uint32_t idx;
int ret = 0;
pa_core_assert_ref(c);
+ pa_assert(cause != 0);
for (sink = PA_SINK(pa_idxset_first(c->sinks, &idx)); sink; sink = PA_SINK(pa_idxset_next(c->sinks, &idx))) {
int r;
- if ((r = pa_sink_suspend(sink, suspend)) < 0)
+ if ((r = pa_sink_suspend(sink, suspend, cause)) < 0)
ret = r;
}
@@ -1696,6 +2027,9 @@ pa_usec_t pa_sink_get_requested_latency_within_thread(pa_sink *s) {
pa_sink_assert_ref(s);
+ if (!(s->flags & PA_SINK_DYNAMIC_LATENCY))
+ return PA_CLAMP(s->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;
@@ -1711,17 +2045,15 @@ pa_usec_t pa_sink_get_requested_latency_within_thread(pa_sink *s) {
(result == (pa_usec_t) -1 || result > monitor_latency))
result = monitor_latency;
- if (result != (pa_usec_t) -1) {
- if (s->thread_info.max_latency > 0 && result > s->thread_info.max_latency)
- result = s->thread_info.max_latency;
+ if (result != (pa_usec_t) -1)
+ result = PA_CLAMP(result, s->thread_info.min_latency, s->thread_info.max_latency);
- if (s->thread_info.min_latency > 0 && result < s->thread_info.min_latency)
- result = s->thread_info.min_latency;
+ if (PA_SINK_IS_LINKED(s->thread_info.state)) {
+ /* Only cache if properly initialized */
+ s->thread_info.requested_latency = result;
+ s->thread_info.requested_latency_valid = TRUE;
}
- s->thread_info.requested_latency = result;
- s->thread_info.requested_latency_valid = TRUE;
-
return result;
}
@@ -1740,7 +2072,7 @@ pa_usec_t pa_sink_get_requested_latency(pa_sink *s) {
}
/* Called from IO as well as the main thread -- the latter only before the IO thread started up */
-void pa_sink_set_max_rewind(pa_sink *s, size_t max_rewind) {
+void pa_sink_set_max_rewind_within_thread(pa_sink *s, size_t max_rewind) {
pa_sink_input *i;
void *state = NULL;
@@ -1757,11 +2089,21 @@ void pa_sink_set_max_rewind(pa_sink *s, size_t max_rewind) {
}
if (s->monitor_source)
- pa_source_set_max_rewind(s->monitor_source, s->thread_info.max_rewind);
+ pa_source_set_max_rewind_within_thread(s->monitor_source, s->thread_info.max_rewind);
+}
+
+/* Called from main thread */
+void pa_sink_set_max_rewind(pa_sink *s, size_t max_rewind) {
+ pa_sink_assert_ref(s);
+
+ 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);
+ else
+ pa_sink_set_max_rewind_within_thread(s, max_rewind);
}
/* Called from IO as well as the main thread -- the latter only before the IO thread started up */
-void pa_sink_set_max_request(pa_sink *s, size_t max_request) {
+void pa_sink_set_max_request_within_thread(pa_sink *s, size_t max_request) {
void *state = NULL;
pa_sink_assert_ref(s);
@@ -1779,6 +2121,16 @@ void pa_sink_set_max_request(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);
+
+ 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);
+ else
+ pa_sink_set_max_request_within_thread(s, max_request);
+}
+
/* Called from IO thread */
void pa_sink_invalidate_requested_latency(pa_sink *s) {
pa_sink_input *i;
@@ -1786,14 +2138,20 @@ void pa_sink_invalidate_requested_latency(pa_sink *s) {
pa_sink_assert_ref(s);
+ if (!(s->flags & PA_SINK_DYNAMIC_LATENCY))
+ return;
+
s->thread_info.requested_latency_valid = FALSE;
- if (s->update_requested_latency)
- s->update_requested_latency(s);
+ if (PA_SINK_IS_LINKED(s->thread_info.state)) {
- while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
- if (i->update_sink_requested_latency)
- i->update_sink_requested_latency(i);
+ if (s->update_requested_latency)
+ s->update_requested_latency(s);
+
+ while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
+ if (i->update_sink_requested_latency)
+ i->update_sink_requested_latency(i);
+ }
}
/* Called from main thread */
@@ -1801,19 +2159,23 @@ void pa_sink_set_latency_range(pa_sink *s, pa_usec_t min_latency, pa_usec_t max_
pa_sink_assert_ref(s);
/* min_latency == 0: no limit
- * min_latency == (size_t) -1: default limit
* min_latency anything else: specified limit
*
* Similar for max_latency */
- if (min_latency == (pa_usec_t) -1)
- min_latency = DEFAULT_MIN_LATENCY;
+ if (min_latency < ABSOLUTE_MIN_LATENCY)
+ min_latency = ABSOLUTE_MIN_LATENCY;
- if (max_latency == (pa_usec_t) -1)
- max_latency = min_latency;
+ if (max_latency <= 0 ||
+ max_latency > ABSOLUTE_MAX_LATENCY)
+ max_latency = ABSOLUTE_MAX_LATENCY;
- pa_assert(!min_latency || !max_latency ||
- min_latency <= max_latency);
+ pa_assert(min_latency <= max_latency);
+
+ /* Hmm, let's see if someone forgot to set PA_SINK_DYNAMIC_LATENCY here... */
+ pa_assert((min_latency == ABSOLUTE_MIN_LATENCY &&
+ max_latency == ABSOLUTE_MAX_LATENCY) ||
+ (s->flags & PA_SINK_DYNAMIC_LATENCY));
if (PA_SINK_IS_LINKED(s->state)) {
pa_usec_t r[2];
@@ -1822,15 +2184,8 @@ void pa_sink_set_latency_range(pa_sink *s, pa_usec_t min_latency, pa_usec_t max_
r[1] = max_latency;
pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_LATENCY_RANGE, r, 0, NULL) == 0);
- } else {
- s->thread_info.min_latency = min_latency;
- s->thread_info.max_latency = max_latency;
-
- s->monitor_source->thread_info.min_latency = min_latency;
- s->monitor_source->thread_info.max_latency = max_latency;
-
- s->thread_info.requested_latency_valid = s->monitor_source->thread_info.requested_latency_valid = FALSE;
- }
+ } else
+ pa_sink_set_latency_range_within_thread(s, min_latency, max_latency);
}
/* Called from main thread */
@@ -1853,25 +2208,50 @@ 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_update_latency_range(pa_sink *s, pa_usec_t min_latency, pa_usec_t max_latency) {
- pa_sink_input *i;
+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_assert(!min_latency || !max_latency ||
- min_latency <= max_latency);
+ pa_assert(min_latency >= ABSOLUTE_MIN_LATENCY);
+ pa_assert(max_latency <= ABSOLUTE_MAX_LATENCY);
+ pa_assert(min_latency <= max_latency);
+
+ /* Hmm, let's see if someone forgot to set PA_SINK_DYNAMIC_LATENCY here... */
+ pa_assert((min_latency == ABSOLUTE_MIN_LATENCY &&
+ max_latency == ABSOLUTE_MAX_LATENCY) ||
+ (s->flags & PA_SINK_DYNAMIC_LATENCY));
s->thread_info.min_latency = min_latency;
s->thread_info.max_latency = max_latency;
- while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
- if (i->update_sink_latency_range)
- i->update_sink_latency_range(i);
+ if (PA_SINK_IS_LINKED(s->thread_info.state)) {
+ pa_sink_input *i;
+
+ while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
+ if (i->update_sink_latency_range)
+ i->update_sink_latency_range(i);
+ }
pa_sink_invalidate_requested_latency(s);
- pa_source_update_latency_range(s->monitor_source, min_latency, max_latency);
+ pa_source_set_latency_range_within_thread(s->monitor_source, min_latency, max_latency);
+}
+
+/* Called from main thread, before the sink is put */
+void pa_sink_set_fixed_latency(pa_sink *s, pa_usec_t latency) {
+ pa_sink_assert_ref(s);
+
+ pa_assert(pa_sink_get_state(s) == PA_SINK_INIT);
+
+ if (latency < ABSOLUTE_MIN_LATENCY)
+ latency = ABSOLUTE_MIN_LATENCY;
+
+ if (latency > ABSOLUTE_MAX_LATENCY)
+ latency = ABSOLUTE_MAX_LATENCY;
+
+ s->fixed_latency = latency;
+ pa_source_set_fixed_latency(s->monitor_source, latency);
}
/* Called from main context */
@@ -1901,6 +2281,41 @@ size_t pa_sink_get_max_request(pa_sink *s) {
}
/* Called from main context */
+int pa_sink_set_port(pa_sink *s, const char *name, pa_bool_t save) {
+ pa_device_port *port;
+
+ pa_assert(s);
+
+ if (!s->set_port) {
+ pa_log_debug("set_port() operation not implemented for sink %u \"%s\"", s->index, s->name);
+ return -PA_ERR_NOTIMPLEMENTED;
+ }
+
+ if (!s->ports)
+ return -PA_ERR_NOENTITY;
+
+ if (!(port = pa_hashmap_get(s->ports, name)))
+ return -PA_ERR_NOENTITY;
+
+ if (s->active_port == port) {
+ s->save_port = s->save_port || save;
+ return 0;
+ }
+
+ if ((s->set_port(s, port)) < 0)
+ return -PA_ERR_NOENTITY;
+
+ pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+
+ pa_log_info("Changed port of sink %u \"%s\" to %s", s->index, s->name, port->name);
+
+ s->active_port = port;
+ s->save_port = 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;
@@ -1923,6 +2338,21 @@ pa_bool_t pa_device_init_icon(pa_proplist *p, pa_bool_t is_sink) {
t = "multimedia-player";
else if (pa_streq(ff, "tv"))
t = "video-display";
+
+ /*
+ * The following icons are not part of the icon naming spec,
+ * because Rodney Dawes sucks as the maintainer of that spec.
+ *
+ * http://lists.freedesktop.org/archives/xdg/2009-May/010397.html
+ */
+ else if (pa_streq(ff, "headset"))
+ t = "audio-headset";
+ else if (pa_streq(ff, "headphone"))
+ t = "audio-headphones";
+ else if (pa_streq(ff, "speaker"))
+ t = "audio-speakers";
+ else if (pa_streq(ff, "hands-free"))
+ t = "audio-handsfree";
}
if (!t)
@@ -1954,28 +2384,49 @@ pa_bool_t pa_device_init_icon(pa_proplist *p, pa_bool_t is_sink) {
}
pa_bool_t pa_device_init_description(pa_proplist *p) {
- const char *s;
+ const char *s, *d = NULL, *k;
pa_assert(p);
if (pa_proplist_contains(p, PA_PROP_DEVICE_DESCRIPTION))
return TRUE;
if ((s = pa_proplist_gets(p, PA_PROP_DEVICE_FORM_FACTOR)))
- if (pa_streq(s, "internal")) {
- pa_proplist_sets(p, PA_PROP_DEVICE_DESCRIPTION, _("Internal Audio"));
- return TRUE;
- }
+ if (pa_streq(s, "internal"))
+ d = _("Internal Audio");
- if ((s = pa_proplist_gets(p, PA_PROP_DEVICE_CLASS)))
- if (pa_streq(s, "modem")) {
- pa_proplist_sets(p, PA_PROP_DEVICE_DESCRIPTION, _("Modem"));
- return TRUE;
- }
+ if (!d)
+ if ((s = pa_proplist_gets(p, PA_PROP_DEVICE_CLASS)))
+ if (pa_streq(s, "modem"))
+ d = _("Modem");
+
+ if (!d)
+ d = pa_proplist_gets(p, PA_PROP_DEVICE_PRODUCT_NAME);
+
+ if (!d)
+ return FALSE;
+
+ k = pa_proplist_gets(p, PA_PROP_DEVICE_PROFILE_DESCRIPTION);
+
+ if (d && k)
+ pa_proplist_setf(p, PA_PROP_DEVICE_DESCRIPTION, _("%s %s"), d, k);
+ else if (d)
+ pa_proplist_sets(p, PA_PROP_DEVICE_DESCRIPTION, d);
+
+ return TRUE;
+}
- if ((s = pa_proplist_gets(p, PA_PROP_DEVICE_PRODUCT_NAME))) {
- pa_proplist_sets(p, PA_PROP_DEVICE_DESCRIPTION, s);
+pa_bool_t pa_device_init_intended_roles(pa_proplist *p) {
+ const char *s;
+ pa_assert(p);
+
+ if (pa_proplist_contains(p, PA_PROP_DEVICE_INTENDED_ROLES))
return TRUE;
- }
+
+ if ((s = pa_proplist_gets(p, PA_PROP_DEVICE_FORM_FACTOR)))
+ if (pa_streq(s, "handset") || pa_streq(s, "hands-free")) {
+ pa_proplist_sets(p, PA_PROP_DEVICE_INTENDED_ROLES, "phone");
+ return TRUE;
+ }
return FALSE;
}
diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h
index 0d33679f..d16fcc01 100644
--- a/src/pulsecore/sink.h
+++ b/src/pulsecore/sink.h
@@ -24,6 +24,7 @@
***/
typedef struct pa_sink pa_sink;
+typedef struct pa_device_port pa_device_port;
#include <inttypes.h>
@@ -49,13 +50,26 @@ static inline pa_bool_t PA_SINK_IS_LINKED(pa_sink_state_t x) {
return x == PA_SINK_RUNNING || x == PA_SINK_IDLE || x == PA_SINK_SUSPENDED;
}
+struct pa_device_port {
+ char *name;
+ char *description;
+
+ unsigned priority;
+
+ /* .. followed by some implementation specific data */
+};
+
+#define PA_DEVICE_PORT_DATA(d) ((void*) ((uint8_t*) d + PA_ALIGN(sizeof(pa_device_port))))
+
struct pa_sink {
pa_msgobject parent;
uint32_t index;
pa_core *core;
+
pa_sink_state_t state;
pa_sink_flags_t flags;
+ pa_suspend_cause_t suspend_cause;
char *name;
char *driver; /* may be NULL */
@@ -74,17 +88,28 @@ struct pa_sink {
pa_volume_t base_volume; /* shall be constant */
unsigned n_volume_steps; /* shall be constant */
- pa_cvolume virtual_volume, soft_volume;
+ /* 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 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;
pa_bool_t refresh_muted:1;
+ pa_bool_t save_port:1;
+ pa_bool_t save_volume:1;
+ 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;
+
/* Called when the main loop requests a state change. Called from
* main loop context. If returns -1 the state change will be
* inhibited */
@@ -120,6 +145,10 @@ struct pa_sink {
* thread context. */
void (*update_requested_latency)(pa_sink *s); /* dito */
+ /* Called whenever the port shall be changed. Called from main
+ * thread. */
+ int (*set_port)(pa_sink *s, pa_device_port *port); /* dito */
+
/* Contains copies of the above data so that the real-time worker
* thread can work without access locking */
struct {
@@ -159,6 +188,7 @@ typedef enum pa_sink_message {
PA_SINK_MESSAGE_REMOVE_INPUT,
PA_SINK_MESSAGE_GET_VOLUME,
PA_SINK_MESSAGE_SET_VOLUME,
+ PA_SINK_MESSAGE_SYNC_VOLUMES,
PA_SINK_MESSAGE_GET_MUTE,
PA_SINK_MESSAGE_SET_MUTE,
PA_SINK_MESSAGE_GET_LATENCY,
@@ -172,6 +202,8 @@ typedef enum pa_sink_message {
PA_SINK_MESSAGE_GET_LATENCY_RANGE,
PA_SINK_MESSAGE_GET_MAX_REWIND,
PA_SINK_MESSAGE_GET_MAX_REQUEST,
+ PA_SINK_MESSAGE_SET_MAX_REWIND,
+ PA_SINK_MESSAGE_SET_MAX_REQUEST,
PA_SINK_MESSAGE_MAX
} pa_sink_message_t;
@@ -183,6 +215,9 @@ typedef struct pa_sink_new_data {
pa_module *module;
pa_card *card;
+ pa_hashmap *ports;
+ char *active_port;
+
pa_sample_spec sample_spec;
pa_channel_map channel_map;
pa_cvolume volume;
@@ -194,6 +229,10 @@ typedef struct pa_sink_new_data {
pa_bool_t muted_is_set:1;
pa_bool_t namereg_fail:1;
+
+ pa_bool_t save_port:1;
+ pa_bool_t save_volume:1;
+ pa_bool_t save_muted:1;
} pa_sink_new_data;
pa_sink_new_data* pa_sink_new_data_init(pa_sink_new_data *data);
@@ -202,6 +241,7 @@ void pa_sink_new_data_set_sample_spec(pa_sink_new_data *data, const pa_sample_sp
void pa_sink_new_data_set_channel_map(pa_sink_new_data *data, const pa_channel_map *map);
void pa_sink_new_data_set_volume(pa_sink_new_data *data, const pa_cvolume *volume);
void pa_sink_new_data_set_muted(pa_sink_new_data *data, pa_bool_t mute);
+void pa_sink_new_data_set_port(pa_sink_new_data *data, const char *port);
void pa_sink_new_data_done(pa_sink_new_data *data);
/*** To be called exclusively by the sink driver, from main context */
@@ -218,15 +258,21 @@ void pa_sink_set_description(pa_sink *s, const char *description);
void pa_sink_set_asyncmsgq(pa_sink *s, pa_asyncmsgq *q);
void pa_sink_set_rtpoll(pa_sink *s, pa_rtpoll *p);
+void pa_sink_set_max_rewind(pa_sink *s, size_t max_rewind);
+void pa_sink_set_max_request(pa_sink *s, size_t max_request);
void pa_sink_set_latency_range(pa_sink *s, pa_usec_t min_latency, pa_usec_t max_latency);
+void pa_sink_set_fixed_latency(pa_sink *s, pa_usec_t latency);
void pa_sink_detach(pa_sink *s);
void pa_sink_attach(pa_sink *s);
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);
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);
/**** May be called by everyone, from main context */
@@ -239,26 +285,29 @@ size_t pa_sink_get_max_rewind(pa_sink *s);
size_t pa_sink_get_max_request(pa_sink *s);
int pa_sink_update_status(pa_sink*s);
-int pa_sink_suspend(pa_sink *s, pa_bool_t suspend);
-int pa_sink_suspend_all(pa_core *c, pa_bool_t suspend);
+int pa_sink_suspend(pa_sink *s, pa_bool_t suspend, pa_suspend_cause_t cause);
+int pa_sink_suspend_all(pa_core *c, pa_bool_t suspend, pa_suspend_cause_t cause);
void pa_sink_update_flat_volume(pa_sink *s, pa_cvolume *new_volume);
-void pa_sink_propagate_flat_volume(pa_sink *s, const pa_cvolume *old_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 propagate, pa_bool_t sendmsg);
-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);
+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);
pa_bool_t pa_sink_update_proplist(pa_sink *s, pa_update_mode_t mode, pa_proplist *p);
+int pa_sink_set_port(pa_sink *s, const char *name, pa_bool_t save);
+
unsigned pa_sink_linked_by(pa_sink *s); /* Number of connected streams */
unsigned pa_sink_used_by(pa_sink *s); /* Number of connected streams which are not corked */
unsigned pa_sink_check_suspend(pa_sink *s); /* Returns how many streams are active that don't allow suspensions */
#define pa_sink_get_state(s) ((s)->state)
/* Moves all inputs away, and stores them in pa_queue */
-pa_queue *pa_sink_move_all_start(pa_sink *s);
+pa_queue *pa_sink_move_all_start(pa_sink *s, pa_queue *q);
void pa_sink_move_all_finish(pa_sink *s, pa_queue *q, pa_bool_t save);
void pa_sink_move_all_fail(pa_queue *q);
@@ -278,10 +327,10 @@ void pa_sink_detach_within_thread(pa_sink *s);
pa_usec_t pa_sink_get_requested_latency_within_thread(pa_sink *s);
-void pa_sink_set_max_rewind(pa_sink *s, size_t max_rewind);
-void pa_sink_set_max_request(pa_sink *s, size_t max_request);
+void pa_sink_set_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_update_latency_range(pa_sink *s, pa_usec_t min_latency, pa_usec_t max_latency);
+void pa_sink_set_latency_range_within_thread(pa_sink *s, pa_usec_t min_latency, pa_usec_t max_latency);
/*** To be called exclusively by sink input drivers, from IO context */
@@ -289,4 +338,9 @@ void pa_sink_request_rewind(pa_sink*s, size_t nbytes);
void pa_sink_invalidate_requested_latency(pa_sink *s);
+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);
+
#endif
diff --git a/src/pulsecore/sndfile-util.c b/src/pulsecore/sndfile-util.c
new file mode 100644
index 00000000..4f7f8bdb
--- /dev/null
+++ b/src/pulsecore/sndfile-util.c
@@ -0,0 +1,462 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2009 Lennart Poettering
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2.1 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+/* Shared between pacat/parec/paplay and the server */
+
+#include <pulse/xmalloc.h>
+#include <pulse/utf8.h>
+
+#include <pulsecore/macro.h>
+#include <pulsecore/core-util.h>
+
+#include "sndfile-util.h"
+
+int pa_sndfile_read_sample_spec(SNDFILE *sf, pa_sample_spec *ss) {
+ SF_INFO sfi;
+
+ pa_assert(sf);
+ pa_assert(ss);
+
+ pa_zero(sfi);
+ pa_assert_se(sf_command(sf, SFC_GET_CURRENT_SF_INFO, &sfi, sizeof(sfi)) == 0);
+
+ switch (sfi.format & SF_FORMAT_SUBMASK) {
+
+ case SF_FORMAT_PCM_16:
+ case SF_FORMAT_PCM_U8:
+ case SF_FORMAT_PCM_S8:
+ ss->format = PA_SAMPLE_S16NE;
+ break;
+
+ case SF_FORMAT_PCM_24:
+ case SF_FORMAT_PCM_32:
+ ss->format = PA_SAMPLE_S32NE;
+ break;
+
+ case SF_FORMAT_ULAW:
+ ss->format = PA_SAMPLE_ULAW;
+ break;
+
+ case SF_FORMAT_ALAW:
+ ss->format = PA_SAMPLE_ALAW;
+ break;
+
+ case SF_FORMAT_FLOAT:
+ case SF_FORMAT_DOUBLE:
+ default:
+ ss->format = PA_SAMPLE_FLOAT32NE;
+ break;
+ }
+
+ ss->rate = (uint32_t) sfi.samplerate;
+ ss->channels = (uint8_t) sfi.channels;
+
+ if (!pa_sample_spec_valid(ss))
+ return -1;
+
+ return 0;
+}
+
+int pa_sndfile_write_sample_spec(SF_INFO *sfi, pa_sample_spec *ss) {
+ pa_assert(sfi);
+ pa_assert(ss);
+
+ sfi->samplerate = (int) ss->rate;
+ sfi->channels = (int) ss->channels;
+
+ if (pa_sample_format_is_le(ss->format) > 0)
+ sfi->format = SF_ENDIAN_LITTLE;
+ else if (pa_sample_format_is_be(ss->format) > 0)
+ sfi->format = SF_ENDIAN_BIG;
+
+ switch (ss->format) {
+
+ case PA_SAMPLE_U8:
+ ss->format = PA_SAMPLE_S16NE;
+ sfi->format = SF_FORMAT_PCM_U8;
+ break;
+
+ case PA_SAMPLE_S16LE:
+ case PA_SAMPLE_S16BE:
+ ss->format = PA_SAMPLE_S16NE;
+ sfi->format |= SF_FORMAT_PCM_16;
+ break;
+
+ case PA_SAMPLE_S24LE:
+ case PA_SAMPLE_S24BE:
+ case PA_SAMPLE_S24_32LE:
+ case PA_SAMPLE_S24_32BE:
+ ss->format = PA_SAMPLE_S32NE;
+ sfi->format |= SF_FORMAT_PCM_24;
+ break;
+
+ case PA_SAMPLE_S32LE:
+ case PA_SAMPLE_S32BE:
+ ss->format = PA_SAMPLE_S32NE;
+ sfi->format |= SF_FORMAT_PCM_32;
+ break;
+
+ case PA_SAMPLE_ULAW:
+ sfi->format = SF_FORMAT_ULAW;
+ break;
+
+ case PA_SAMPLE_ALAW:
+ sfi->format = SF_FORMAT_ALAW;
+ break;
+
+ case PA_SAMPLE_FLOAT32LE:
+ case PA_SAMPLE_FLOAT32BE:
+ default:
+ ss->format = PA_SAMPLE_FLOAT32NE;
+ sfi->format |= SF_FORMAT_FLOAT;
+ break;
+ }
+
+
+ if (!pa_sample_spec_valid(ss))
+ return -1;
+
+ return 0;
+}
+
+int pa_sndfile_read_channel_map(SNDFILE *sf, pa_channel_map *cm) {
+
+ static const pa_channel_position_t table[] = {
+ [SF_CHANNEL_MAP_MONO] = PA_CHANNEL_POSITION_MONO,
+ [SF_CHANNEL_MAP_LEFT] = PA_CHANNEL_POSITION_FRONT_LEFT, /* libsndfile distuingishes left und front-left, which we don't */
+ [SF_CHANNEL_MAP_RIGHT] = PA_CHANNEL_POSITION_FRONT_RIGHT,
+ [SF_CHANNEL_MAP_CENTER] = PA_CHANNEL_POSITION_FRONT_CENTER,
+ [SF_CHANNEL_MAP_FRONT_LEFT] = PA_CHANNEL_POSITION_FRONT_LEFT,
+ [SF_CHANNEL_MAP_FRONT_RIGHT] = PA_CHANNEL_POSITION_FRONT_RIGHT,
+ [SF_CHANNEL_MAP_FRONT_CENTER] = PA_CHANNEL_POSITION_FRONT_CENTER,
+ [SF_CHANNEL_MAP_REAR_CENTER] = PA_CHANNEL_POSITION_REAR_CENTER,
+ [SF_CHANNEL_MAP_REAR_LEFT] = PA_CHANNEL_POSITION_REAR_LEFT,
+ [SF_CHANNEL_MAP_REAR_RIGHT] = PA_CHANNEL_POSITION_REAR_RIGHT,
+ [SF_CHANNEL_MAP_LFE] = PA_CHANNEL_POSITION_LFE,
+ [SF_CHANNEL_MAP_FRONT_LEFT_OF_CENTER] = PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER,
+ [SF_CHANNEL_MAP_FRONT_RIGHT_OF_CENTER] = PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER,
+ [SF_CHANNEL_MAP_SIDE_LEFT] = PA_CHANNEL_POSITION_SIDE_LEFT,
+ [SF_CHANNEL_MAP_SIDE_RIGHT] = PA_CHANNEL_POSITION_SIDE_RIGHT,
+ [SF_CHANNEL_MAP_TOP_CENTER] = PA_CHANNEL_POSITION_TOP_CENTER,
+ [SF_CHANNEL_MAP_TOP_FRONT_LEFT] = PA_CHANNEL_POSITION_TOP_FRONT_LEFT,
+ [SF_CHANNEL_MAP_TOP_FRONT_RIGHT] = PA_CHANNEL_POSITION_TOP_FRONT_RIGHT,
+ [SF_CHANNEL_MAP_TOP_FRONT_CENTER] = PA_CHANNEL_POSITION_TOP_FRONT_CENTER,
+ [SF_CHANNEL_MAP_TOP_REAR_LEFT] = PA_CHANNEL_POSITION_TOP_REAR_LEFT,
+ [SF_CHANNEL_MAP_TOP_REAR_RIGHT] = PA_CHANNEL_POSITION_TOP_REAR_RIGHT,
+ [SF_CHANNEL_MAP_TOP_REAR_CENTER] = PA_CHANNEL_POSITION_TOP_REAR_CENTER
+ };
+
+ SF_INFO sfi;
+ int *channels;
+ unsigned c;
+
+ pa_assert(sf);
+ pa_assert(cm);
+
+ pa_zero(sfi);
+ pa_assert_se(sf_command(sf, SFC_GET_CURRENT_SF_INFO, &sfi, sizeof(sfi)) == 0);
+
+ channels = pa_xnew(int, sfi.channels);
+ if (!sf_command(sf, SFC_GET_CHANNEL_MAP_INFO,
+ channels, sizeof(channels[0]) * sfi.channels)) {
+
+ pa_xfree(channels);
+ return -1;
+ }
+
+ cm->channels = (uint8_t) sfi.channels;
+ for (c = 0; c < cm->channels; c++) {
+ if (channels[c] <= SF_CHANNEL_MAP_INVALID ||
+ (unsigned) channels[c] >= PA_ELEMENTSOF(table)) {
+ pa_xfree(channels);
+ return -1;
+ }
+
+ cm->map[c] = table[channels[c]];
+ }
+
+ pa_xfree(channels);
+
+ if (!pa_channel_map_valid(cm))
+ return -1;
+
+ return 0;
+}
+
+int pa_sndfile_write_channel_map(SNDFILE *sf, pa_channel_map *cm) {
+ static const int table[PA_CHANNEL_POSITION_MAX] = {
+ [PA_CHANNEL_POSITION_MONO] = SF_CHANNEL_MAP_MONO,
+
+ [PA_CHANNEL_POSITION_FRONT_LEFT] = SF_CHANNEL_MAP_FRONT_LEFT,
+ [PA_CHANNEL_POSITION_FRONT_RIGHT] = SF_CHANNEL_MAP_FRONT_RIGHT,
+ [PA_CHANNEL_POSITION_FRONT_CENTER] = SF_CHANNEL_MAP_FRONT_CENTER,
+
+ [PA_CHANNEL_POSITION_REAR_CENTER] = SF_CHANNEL_MAP_REAR_CENTER,
+ [PA_CHANNEL_POSITION_REAR_LEFT] = SF_CHANNEL_MAP_REAR_LEFT,
+ [PA_CHANNEL_POSITION_REAR_RIGHT] = SF_CHANNEL_MAP_REAR_RIGHT,
+
+ [PA_CHANNEL_POSITION_LFE] = SF_CHANNEL_MAP_LFE,
+
+ [PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER] = SF_CHANNEL_MAP_FRONT_LEFT_OF_CENTER,
+ [PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER] = SF_CHANNEL_MAP_FRONT_RIGHT_OF_CENTER,
+
+ [PA_CHANNEL_POSITION_SIDE_LEFT] = SF_CHANNEL_MAP_SIDE_LEFT,
+ [PA_CHANNEL_POSITION_SIDE_RIGHT] = SF_CHANNEL_MAP_SIDE_RIGHT,
+
+ [PA_CHANNEL_POSITION_AUX0] = -1,
+ [PA_CHANNEL_POSITION_AUX1] = -1,
+ [PA_CHANNEL_POSITION_AUX2] = -1,
+ [PA_CHANNEL_POSITION_AUX3] = -1,
+ [PA_CHANNEL_POSITION_AUX4] = -1,
+ [PA_CHANNEL_POSITION_AUX5] = -1,
+ [PA_CHANNEL_POSITION_AUX6] = -1,
+ [PA_CHANNEL_POSITION_AUX7] = -1,
+ [PA_CHANNEL_POSITION_AUX8] = -1,
+ [PA_CHANNEL_POSITION_AUX9] = -1,
+ [PA_CHANNEL_POSITION_AUX10] = -1,
+ [PA_CHANNEL_POSITION_AUX11] = -1,
+ [PA_CHANNEL_POSITION_AUX12] = -1,
+ [PA_CHANNEL_POSITION_AUX13] = -1,
+ [PA_CHANNEL_POSITION_AUX14] = -1,
+ [PA_CHANNEL_POSITION_AUX15] = -1,
+ [PA_CHANNEL_POSITION_AUX16] = -1,
+ [PA_CHANNEL_POSITION_AUX17] = -1,
+ [PA_CHANNEL_POSITION_AUX18] = -1,
+ [PA_CHANNEL_POSITION_AUX19] = -1,
+ [PA_CHANNEL_POSITION_AUX20] = -1,
+ [PA_CHANNEL_POSITION_AUX21] = -1,
+ [PA_CHANNEL_POSITION_AUX22] = -1,
+ [PA_CHANNEL_POSITION_AUX23] = -1,
+ [PA_CHANNEL_POSITION_AUX24] = -1,
+ [PA_CHANNEL_POSITION_AUX25] = -1,
+ [PA_CHANNEL_POSITION_AUX26] = -1,
+ [PA_CHANNEL_POSITION_AUX27] = -1,
+ [PA_CHANNEL_POSITION_AUX28] = -1,
+ [PA_CHANNEL_POSITION_AUX29] = -1,
+ [PA_CHANNEL_POSITION_AUX30] = -1,
+ [PA_CHANNEL_POSITION_AUX31] = -1,
+
+ [PA_CHANNEL_POSITION_TOP_CENTER] = SF_CHANNEL_MAP_TOP_CENTER,
+
+ [PA_CHANNEL_POSITION_TOP_FRONT_LEFT] = SF_CHANNEL_MAP_TOP_FRONT_LEFT,
+ [PA_CHANNEL_POSITION_TOP_FRONT_RIGHT] = SF_CHANNEL_MAP_TOP_FRONT_RIGHT,
+ [PA_CHANNEL_POSITION_TOP_FRONT_CENTER] = SF_CHANNEL_MAP_TOP_FRONT_CENTER ,
+
+ [PA_CHANNEL_POSITION_TOP_REAR_LEFT] = SF_CHANNEL_MAP_TOP_REAR_LEFT,
+ [PA_CHANNEL_POSITION_TOP_REAR_RIGHT] = SF_CHANNEL_MAP_TOP_REAR_RIGHT,
+ [PA_CHANNEL_POSITION_TOP_REAR_CENTER] = SF_CHANNEL_MAP_TOP_REAR_CENTER,
+ };
+
+ int *channels;
+ unsigned c;
+
+ pa_assert(sf);
+ pa_assert(cm);
+
+ /* Suppress channel mapping for the obvious cases */
+ if (cm->channels == 1 && cm->map[0] == PA_CHANNEL_POSITION_MONO)
+ return 0;
+
+ if (cm->channels == 2 &&
+ cm->map[0] == PA_CHANNEL_POSITION_FRONT_LEFT &&
+ cm->map[1] == PA_CHANNEL_POSITION_FRONT_RIGHT)
+ return 0;
+
+ channels = pa_xnew(int, cm->channels);
+ for (c = 0; c < cm->channels; c++) {
+
+ if (cm->map[c] < 0 ||
+ cm->map[c] >= PA_CHANNEL_POSITION_MAX ||
+ table[cm->map[c]] < 0) {
+ pa_xfree(channels);
+ return -1;
+ }
+
+ channels[c] = table[cm->map[c]];
+ }
+
+ if (!sf_command(sf, SFC_SET_CHANNEL_MAP_INFO,
+ channels, sizeof(channels[0]) * cm->channels)) {
+ pa_xfree(channels);
+ return -1;
+ }
+
+ pa_xfree(channels);
+ return 0;
+}
+
+void pa_sndfile_init_proplist(SNDFILE *sf, pa_proplist *p) {
+
+ static const char* table[] = {
+ [SF_STR_TITLE] = PA_PROP_MEDIA_TITLE,
+ [SF_STR_COPYRIGHT] = PA_PROP_MEDIA_COPYRIGHT,
+ [SF_STR_SOFTWARE] = PA_PROP_MEDIA_SOFTWARE,
+ [SF_STR_ARTIST] = PA_PROP_MEDIA_ARTIST,
+ [SF_STR_COMMENT] = "media.comment",
+ [SF_STR_DATE] = "media.date"
+ };
+
+ SF_INFO sfi;
+ SF_FORMAT_INFO fi;
+ unsigned c;
+
+ pa_assert(sf);
+ pa_assert(p);
+
+ for (c = 0; c < PA_ELEMENTSOF(table); c++) {
+ const char *s;
+ char *t;
+
+ if (!table[c])
+ continue;
+
+ if (!(s = sf_get_string(sf, c)))
+ continue;
+
+ t = pa_utf8_filter(s);
+ pa_proplist_sets(p, table[c], t);
+ pa_xfree(t);
+ }
+
+ pa_zero(sfi);
+ pa_assert_se(sf_command(sf, SFC_GET_CURRENT_SF_INFO, &sfi, sizeof(sfi)) == 0);
+
+ pa_zero(fi);
+ fi.format = sfi.format;
+ if (sf_command(sf, SFC_GET_FORMAT_INFO, &fi, sizeof(fi)) == 0 && fi.name) {
+ char *t;
+
+ t = pa_utf8_filter(fi.name);
+ pa_proplist_sets(p, "media.format", t);
+ pa_xfree(t);
+ }
+}
+
+pa_sndfile_readf_t pa_sndfile_readf_function(const pa_sample_spec *ss) {
+ pa_assert(ss);
+
+ switch (ss->format) {
+ case PA_SAMPLE_S16NE:
+ return (pa_sndfile_readf_t) sf_readf_short;
+
+ case PA_SAMPLE_S32NE:
+ return (pa_sndfile_readf_t) sf_readf_int;
+
+ case PA_SAMPLE_FLOAT32NE:
+ return (pa_sndfile_readf_t) sf_readf_float;
+
+ case PA_SAMPLE_ULAW:
+ case PA_SAMPLE_ALAW:
+ return NULL;
+
+ default:
+ pa_assert_not_reached();
+ }
+}
+
+pa_sndfile_writef_t pa_sndfile_writef_function(const pa_sample_spec *ss) {
+ pa_assert(ss);
+
+ switch (ss->format) {
+ case PA_SAMPLE_S16NE:
+ return (pa_sndfile_writef_t) sf_writef_short;
+
+ case PA_SAMPLE_S32NE:
+ return (pa_sndfile_writef_t) sf_writef_int;
+
+ case PA_SAMPLE_FLOAT32NE:
+ return (pa_sndfile_writef_t) sf_writef_float;
+
+ case PA_SAMPLE_ULAW:
+ case PA_SAMPLE_ALAW:
+ return NULL;
+
+ default:
+ pa_assert_not_reached();
+ }
+}
+
+int pa_sndfile_format_from_string(const char *name) {
+ int i, count = 0;
+
+
+ if (!name[0])
+ return -1;
+
+ pa_assert_se(sf_command(NULL, SFC_GET_FORMAT_MAJOR_COUNT, &count, sizeof(int)) == 0);
+
+ /* First try to match via full type string */
+ for (i = 0; i < count; i++) {
+ SF_FORMAT_INFO fi;
+ pa_zero(fi);
+ fi.format = i;
+
+ pa_assert_se(sf_command(NULL, SFC_GET_FORMAT_MAJOR, &fi, sizeof(fi)) == 0);
+
+ if (strcasecmp(name, fi.name) == 0)
+ return i;
+ }
+
+ /* Then, try to match via the full extension */
+ for (i = 0; i < count; i++) {
+ SF_FORMAT_INFO fi;
+ pa_zero(fi);
+ fi.format = i;
+
+ pa_assert_se(sf_command(NULL, SFC_GET_FORMAT_MAJOR, &fi, sizeof(fi)) == 0);
+
+ if (strcasecmp(name, fi.extension) == 0)
+ return i;
+ }
+
+ /* Then, try to match via the start of the type string */
+ for (i = 0; i < count; i++) {
+ SF_FORMAT_INFO fi;
+ pa_zero(fi);
+ fi.format = i;
+
+ pa_assert_se(sf_command(NULL, SFC_GET_FORMAT_MAJOR, &fi, sizeof(fi)) == 0);
+
+ if (strncasecmp(name, fi.extension, strlen(name)) == 0)
+ return i;
+ }
+
+ return -1;
+}
+
+void pa_sndfile_dump_formats(void) {
+ int i, count = 0;
+
+ pa_assert_se(sf_command(NULL, SFC_GET_FORMAT_MAJOR_COUNT, &count, sizeof(int)) == 0);
+
+ for (i = 0; i < count; i++) {
+ SF_FORMAT_INFO fi;
+ pa_zero(fi);
+ fi.format = i;
+
+ pa_assert_se(sf_command(NULL, SFC_GET_FORMAT_MAJOR, &fi, sizeof(fi)) == 0);
+ printf("%s\t%s\n", fi.extension, fi.name);
+ }
+}
diff --git a/src/pulsecore/sndfile-util.h b/src/pulsecore/sndfile-util.h
new file mode 100644
index 00000000..74021da1
--- /dev/null
+++ b/src/pulsecore/sndfile-util.h
@@ -0,0 +1,52 @@
+#ifndef foopulsecoresndfileutilhfoo
+#define foopulsecoresndfileutilhfoo
+
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2009 Lennart Poettering
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2.1 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <sndfile.h>
+
+#include <pulse/sample.h>
+#include <pulse/channelmap.h>
+#include <pulse/proplist.h>
+
+int pa_sndfile_read_sample_spec(SNDFILE *sf, pa_sample_spec *ss);
+int pa_sndfile_read_channel_map(SNDFILE *sf, pa_channel_map *cm);
+
+int pa_sndfile_write_sample_spec(SF_INFO *sfi, pa_sample_spec *ss);
+int pa_sndfile_write_channel_map(SNDFILE *sf, pa_channel_map *cm);
+
+void pa_sndfile_init_proplist(SNDFILE *sf, pa_proplist *p);
+
+typedef sf_count_t (*pa_sndfile_readf_t)(SNDFILE *sndfile, void *ptr, sf_count_t frames);
+typedef sf_count_t (*pa_sndfile_writef_t)(SNDFILE *sndfile, const void *ptr, sf_count_t frames);
+
+/* Returns NULL if sf_read_raw() shall be used */
+pa_sndfile_readf_t pa_sndfile_readf_function(const pa_sample_spec *ss);
+
+/* Returns NULL if sf_write_raw() shall be used */
+pa_sndfile_writef_t pa_sndfile_writef_function(const pa_sample_spec *ss);
+
+int pa_sndfile_format_from_string(const char *extension);
+
+void pa_sndfile_dump_formats(void);
+
+#endif
diff --git a/src/pulsecore/socket-client.c b/src/pulsecore/socket-client.c
index dc23bff6..24535157 100644
--- a/src/pulsecore/socket-client.c
+++ b/src/pulsecore/socket-client.c
@@ -52,12 +52,14 @@
#include <asyncns.h>
#endif
+#include <pulse/rtclock.h>
#include <pulse/timeval.h>
#include <pulse/xmalloc.h>
#include <pulsecore/winsock.h>
#include <pulsecore/core-error.h>
#include <pulsecore/socket-util.h>
+#include <pulsecore/core-rtclock.h>
#include <pulsecore/core-util.h>
#include <pulsecore/socket-util.h>
#include <pulsecore/log.h>
@@ -420,12 +422,11 @@ fail:
#endif
-static void timeout_cb(pa_mainloop_api *m, pa_time_event *e, const struct timeval *tv, void *userdata) {
+static void timeout_cb(pa_mainloop_api *m, pa_time_event *e, const struct timeval *t, void *userdata) {
pa_socket_client *c = userdata;
pa_assert(m);
pa_assert(e);
- pa_assert(tv);
pa_assert(c);
if (c->fd >= 0) {
@@ -437,17 +438,16 @@ static void timeout_cb(pa_mainloop_api *m, pa_time_event *e, const struct timeva
do_call(c);
}
-static void start_timeout(pa_socket_client *c) {
+static void start_timeout(pa_socket_client *c, pa_bool_t use_rtclock) {
struct timeval tv;
+
pa_assert(c);
pa_assert(!c->timeout_event);
- pa_gettimeofday(&tv);
- pa_timeval_add(&tv, CONNECT_TIMEOUT * PA_USEC_PER_SEC);
- c->timeout_event = c->mainloop->time_new(c->mainloop, &tv, timeout_cb, c);
+ c->timeout_event = c->mainloop->time_new(c->mainloop, pa_timeval_rtstore(&tv, pa_rtclock_now() + CONNECT_TIMEOUT * PA_USEC_PER_SEC, use_rtclock), timeout_cb, c);
}
-pa_socket_client* pa_socket_client_new_string(pa_mainloop_api *m, const char*name, uint16_t default_port) {
+pa_socket_client* pa_socket_client_new_string(pa_mainloop_api *m, pa_bool_t use_rtclock, const char*name, uint16_t default_port) {
pa_socket_client *c = NULL;
pa_parsed_address a;
@@ -463,7 +463,7 @@ pa_socket_client* pa_socket_client_new_string(pa_mainloop_api *m, const char*nam
switch (a.type) {
case PA_PARSED_ADDRESS_UNIX:
if ((c = pa_socket_client_new_unix(m, a.path_or_host)))
- start_timeout(c);
+ start_timeout(c, use_rtclock);
break;
case PA_PARSED_ADDRESS_TCP4: /* Fallthrough */
@@ -499,7 +499,7 @@ pa_socket_client* pa_socket_client_new_string(pa_mainloop_api *m, const char*nam
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);
- start_timeout(c);
+ start_timeout(c, use_rtclock);
}
#elif defined(HAVE_GETADDRINFO)
{
@@ -513,7 +513,7 @@ pa_socket_client* pa_socket_client_new_string(pa_mainloop_api *m, const char*nam
if (res->ai_addr) {
if ((c = pa_socket_client_new_sockaddr(m, res->ai_addr, res->ai_addrlen)))
- start_timeout(c);
+ start_timeout(c, use_rtclock);
}
freeaddrinfo(res);
@@ -546,7 +546,7 @@ pa_socket_client* pa_socket_client_new_string(pa_mainloop_api *m, const char*nam
s.sin_port = htons(a.port);
if ((c = pa_socket_client_new_sockaddr(m, (struct sockaddr*)&s, sizeof(s))))
- start_timeout(c);
+ start_timeout(c, use_rtclock);
}
#endif /* HAVE_LIBASYNCNS */
}
diff --git a/src/pulsecore/socket-client.h b/src/pulsecore/socket-client.h
index ed36400c..b896afa9 100644
--- a/src/pulsecore/socket-client.h
+++ b/src/pulsecore/socket-client.h
@@ -40,7 +40,7 @@ pa_socket_client* pa_socket_client_new_ipv6(pa_mainloop_api *m, uint8_t address[
#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, const char *a, uint16_t default_port);
+pa_socket_client* pa_socket_client_new_string(pa_mainloop_api *m, pa_bool_t use_rtclock, const char *a, uint16_t default_port);
pa_socket_client* pa_socket_client_ref(pa_socket_client *c);
void pa_socket_client_unref(pa_socket_client *c);
diff --git a/src/pulsecore/socket-server.c b/src/pulsecore/socket-server.c
index 6a4405e3..e660700c 100644
--- a/src/pulsecore/socket-server.c
+++ b/src/pulsecore/socket-server.c
@@ -467,11 +467,13 @@ char *pa_socket_server_get_address(pa_socket_server *s, char *c, size_t l) {
pa_snprintf(c, l, "tcp6:%s:%u", fqdn, (unsigned) ntohs(sa.sin6_port));
} else if (memcmp(&in6addr_loopback, &sa.sin6_addr, sizeof(in6addr_loopback)) == 0) {
- char hn[256];
- if (!pa_get_host_name(hn, sizeof(hn)))
+ char *id;
+
+ if (!(id = pa_machine_id()))
return NULL;
- pa_snprintf(c, l, "{%s}tcp6:localhost:%u", hn, (unsigned) ntohs(sa.sin6_port));
+ pa_snprintf(c, l, "{%s}tcp6:localhost:%u", id, (unsigned) ntohs(sa.sin6_port));
+ pa_xfree(id);
} else {
char ip[INET6_ADDRSTRLEN];
@@ -503,11 +505,13 @@ char *pa_socket_server_get_address(pa_socket_server *s, char *c, size_t l) {
pa_snprintf(c, l, "tcp:%s:%u", fqdn, (unsigned) ntohs(sa.sin_port));
} else if (sa.sin_addr.s_addr == INADDR_LOOPBACK) {
- char hn[256];
- if (!pa_get_host_name(hn, sizeof(hn)))
+ char *id;
+
+ if (!(id = pa_machine_id()))
return NULL;
- pa_snprintf(c, l, "{%s}tcp:localhost:%u", hn, (unsigned) ntohs(sa.sin_port));
+ pa_snprintf(c, l, "{%s}tcp:localhost:%u", id, (unsigned) ntohs(sa.sin_port));
+ pa_xfree(id);
} else {
char ip[INET_ADDRSTRLEN];
@@ -523,15 +527,16 @@ char *pa_socket_server_get_address(pa_socket_server *s, char *c, size_t l) {
}
case SOCKET_SERVER_UNIX: {
- char hn[256];
+ char *id;
if (!s->filename)
return NULL;
- if (!pa_get_host_name(hn, sizeof(hn)))
+ if (!(id = pa_machine_id()))
return NULL;
- pa_snprintf(c, l, "{%s}unix:%s", hn, s->filename);
+ pa_snprintf(c, l, "{%s}unix:%s", id, s->filename);
+ pa_xfree(id);
return c;
}
diff --git a/src/pulsecore/sound-file-stream.c b/src/pulsecore/sound-file-stream.c
index 3453637f..502e5c69 100644
--- a/src/pulsecore/sound-file-stream.c
+++ b/src/pulsecore/sound-file-stream.c
@@ -41,6 +41,7 @@
#include <pulsecore/thread-mq.h>
#include <pulsecore/core-util.h>
#include <pulsecore/sample-util.h>
+#include <pulsecore/sndfile-util.h>
#include "sound-file-stream.h"
@@ -234,10 +235,11 @@ int pa_play_file(
const pa_cvolume *volume) {
file_stream *u = NULL;
- SF_INFO sfinfo;
pa_sample_spec ss;
+ pa_channel_map cm;
pa_sink_input_new_data data;
int fd;
+ SF_INFO sfi;
pa_assert(sink);
pa_assert(fname);
@@ -251,8 +253,6 @@ int pa_play_file(
u->readf_function = NULL;
u->memblockq = NULL;
- memset(&sfinfo, 0, sizeof(sfinfo));
-
if ((fd = open(fname, O_RDONLY
#ifdef O_NOCTTY
|O_NOCTTY
@@ -281,50 +281,36 @@ int pa_play_file(
pa_log_debug("POSIX_FADV_WILLNEED succeeded.");
#endif
- if (!(u->sndfile = sf_open_fd(fd, SFM_READ, &sfinfo, 1))) {
+ pa_zero(sfi);
+ if (!(u->sndfile = sf_open_fd(fd, SFM_READ, &sfi, 1))) {
pa_log("Failed to open file %s", fname);
- pa_close(fd);
goto fail;
}
- switch (sfinfo.format & 0xFF) {
- case SF_FORMAT_PCM_16:
- case SF_FORMAT_PCM_U8:
- case SF_FORMAT_PCM_S8:
- ss.format = PA_SAMPLE_S16NE;
- u->readf_function = (sf_count_t (*)(SNDFILE *sndfile, void *ptr, sf_count_t frames)) sf_readf_short;
- break;
-
- case SF_FORMAT_ULAW:
- ss.format = PA_SAMPLE_ULAW;
- break;
-
- case SF_FORMAT_ALAW:
- ss.format = PA_SAMPLE_ALAW;
- break;
+ fd = -1;
- case SF_FORMAT_FLOAT:
- default:
- ss.format = PA_SAMPLE_FLOAT32NE;
- u->readf_function = (sf_count_t (*)(SNDFILE *sndfile, void *ptr, sf_count_t frames)) sf_readf_float;
- break;
+ if (pa_sndfile_read_sample_spec(u->sndfile, &ss) < 0) {
+ pa_log("Failed to determine file sample format.");
+ goto fail;
}
- ss.rate = (uint32_t) sfinfo.samplerate;
- ss.channels = (uint8_t) sfinfo.channels;
-
- if (!pa_sample_spec_valid(&ss)) {
- pa_log("Unsupported sample format in file %s", fname);
- goto fail;
+ if (pa_sndfile_read_channel_map(u->sndfile, &cm) < 0) {
+ if (ss.channels > 2)
+ pa_log_info("Failed to determine file channel map, synthesizing one.");
+ pa_channel_map_init_extend(&cm, ss.channels, PA_CHANNEL_MAP_DEFAULT);
}
+ u->readf_function = pa_sndfile_readf_function(&ss);
+
pa_sink_input_new_data_init(&data);
data.sink = sink;
data.driver = __FILE__;
pa_sink_input_new_data_set_sample_spec(&data, &ss);
+ pa_sink_input_new_data_set_channel_map(&data, &cm);
pa_sink_input_new_data_set_volume(&data, volume);
pa_proplist_sets(data.proplist, PA_PROP_MEDIA_NAME, pa_path_get_filename(fname));
pa_proplist_sets(data.proplist, PA_PROP_MEDIA_FILENAME, fname);
+ pa_sndfile_init_proplist(u->sndfile, data.proplist);
pa_sink_input_new(&u->sink_input, sink->core, &data, 0);
pa_sink_input_new_data_done(&data);
@@ -352,5 +338,8 @@ fail:
if (u)
file_stream_unref(u);
+ if (fd >= 0)
+ pa_close(fd);
+
return -1;
}
diff --git a/src/pulsecore/sound-file.c b/src/pulsecore/sound-file.c
index db75ae08..2d9b76ad 100644
--- a/src/pulsecore/sound-file.c
+++ b/src/pulsecore/sound-file.c
@@ -35,19 +35,21 @@
#include <pulsecore/macro.h>
#include <pulsecore/core-error.h>
#include <pulsecore/core-util.h>
+#include <pulsecore/core-scache.h>
+#include <pulsecore/sndfile-util.h>
#include "sound-file.h"
-#include "core-scache.h"
int pa_sound_file_load(
pa_mempool *pool,
const char *fname,
pa_sample_spec *ss,
pa_channel_map *map,
- pa_memchunk *chunk) {
+ pa_memchunk *chunk,
+ pa_proplist *p) {
SNDFILE *sf = NULL;
- SF_INFO sfinfo;
+ SF_INFO sfi;
int ret = -1;
size_t l;
sf_count_t (*readf_function)(SNDFILE *sndfile, void *ptr, sf_count_t frames) = NULL;
@@ -59,7 +61,6 @@ int pa_sound_file_load(
pa_assert(chunk);
pa_memchunk_reset(chunk);
- memset(&sfinfo, 0, sizeof(sfinfo));
if ((fd = open(fname, O_RDONLY
#ifdef O_NOCTTY
@@ -78,48 +79,29 @@ int pa_sound_file_load(
pa_log_debug("POSIX_FADV_SEQUENTIAL succeeded.");
#endif
- if (!(sf = sf_open_fd(fd, SFM_READ, &sfinfo, 1))) {
+ pa_zero(sfi);
+ if (!(sf = sf_open_fd(fd, SFM_READ, &sfi, 1))) {
pa_log("Failed to open file %s", fname);
- pa_close(fd);
goto finish;
}
- switch (sfinfo.format & SF_FORMAT_SUBMASK) {
- case SF_FORMAT_PCM_16:
- case SF_FORMAT_PCM_U8:
- case SF_FORMAT_PCM_S8:
- ss->format = PA_SAMPLE_S16NE;
- readf_function = (sf_count_t (*)(SNDFILE *sndfile, void *_ptr, sf_count_t frames)) sf_readf_short;
- break;
-
- case SF_FORMAT_ULAW:
- ss->format = PA_SAMPLE_ULAW;
- break;
-
- case SF_FORMAT_ALAW:
- ss->format = PA_SAMPLE_ALAW;
- break;
-
- case SF_FORMAT_FLOAT:
- case SF_FORMAT_DOUBLE:
- default:
- ss->format = PA_SAMPLE_FLOAT32NE;
- readf_function = (sf_count_t (*)(SNDFILE *sndfile, void *_ptr, sf_count_t frames)) sf_readf_float;
- break;
- }
-
- ss->rate = (uint32_t) sfinfo.samplerate;
- ss->channels = (uint8_t) sfinfo.channels;
+ fd = -1;
- if (!pa_sample_spec_valid(ss)) {
- pa_log("Unsupported sample format in file %s", fname);
+ if (pa_sndfile_read_sample_spec(sf, ss) < 0) {
+ pa_log("Failed to determine file sample format.");
goto finish;
}
- if (map)
+ if ((map && pa_sndfile_read_channel_map(sf, map) < 0)) {
+ if (ss->channels > 2)
+ pa_log("Failed to determine file channel map, synthesizing one.");
pa_channel_map_init_extend(map, ss->channels, PA_CHANNEL_MAP_DEFAULT);
+ }
- if ((l = pa_frame_size(ss) * (size_t) sfinfo.frames) > PA_SCACHE_ENTRY_SIZE_MAX) {
+ if (p)
+ pa_sndfile_init_proplist(sf, p);
+
+ if ((l = pa_frame_size(ss) * (size_t) sfi.frames) > PA_SCACHE_ENTRY_SIZE_MAX) {
pa_log("File too large");
goto finish;
}
@@ -128,9 +110,11 @@ int pa_sound_file_load(
chunk->index = 0;
chunk->length = l;
+ readf_function = pa_sndfile_readf_function(ss);
+
ptr = pa_memblock_acquire(chunk->memblock);
- if ((readf_function && readf_function(sf, ptr, sfinfo.frames) != sfinfo.frames) ||
+ if ((readf_function && readf_function(sf, ptr, sfi.frames) != sfi.frames) ||
(!readf_function && sf_read_raw(sf, ptr, (sf_count_t) l) != (sf_count_t) l)) {
pa_log("Premature file end");
goto finish;
@@ -149,55 +133,35 @@ finish:
if (ret != 0 && chunk->memblock)
pa_memblock_unref(chunk->memblock);
+ if (fd >= 0)
+ pa_close(fd);
+
return ret;
}
int pa_sound_file_too_big_to_cache(const char *fname) {
SNDFILE*sf = NULL;
- SF_INFO sfinfo;
+ SF_INFO sfi;
pa_sample_spec ss;
pa_assert(fname);
- if (!(sf = sf_open(fname, SFM_READ, &sfinfo))) {
+ pa_zero(sfi);
+ if (!(sf = sf_open(fname, SFM_READ, &sfi))) {
pa_log("Failed to open file %s", fname);
return -1;
}
- sf_close(sf);
-
- switch (sfinfo.format & SF_FORMAT_SUBMASK) {
- case SF_FORMAT_PCM_16:
- case SF_FORMAT_PCM_U8:
- case SF_FORMAT_PCM_S8:
- ss.format = PA_SAMPLE_S16NE;
- break;
-
- case SF_FORMAT_ULAW:
- ss.format = PA_SAMPLE_ULAW;
- break;
-
- case SF_FORMAT_ALAW:
- ss.format = PA_SAMPLE_ALAW;
- break;
-
- case SF_FORMAT_DOUBLE:
- case SF_FORMAT_FLOAT:
- default:
- ss.format = PA_SAMPLE_FLOAT32NE;
- break;
- }
-
- ss.rate = (uint32_t) sfinfo.samplerate;
- ss.channels = (uint8_t) sfinfo.channels;
-
- if (!pa_sample_spec_valid(&ss)) {
- pa_log("Unsupported sample format in file %s", fname);
+ if (pa_sndfile_read_sample_spec(sf, &ss) < 0) {
+ pa_log("Failed to determine file sample format.");
+ sf_close(sf);
return -1;
}
- if ((pa_frame_size(&ss) * (size_t) sfinfo.frames) > PA_SCACHE_ENTRY_SIZE_MAX) {
+ sf_close(sf);
+
+ if ((pa_frame_size(&ss) * (size_t) sfi.frames) > PA_SCACHE_ENTRY_SIZE_MAX) {
pa_log("File too large: %s", fname);
return 1;
}
diff --git a/src/pulsecore/sound-file.h b/src/pulsecore/sound-file.h
index 34e02616..4dc0c7e1 100644
--- a/src/pulsecore/sound-file.h
+++ b/src/pulsecore/sound-file.h
@@ -26,7 +26,7 @@
#include <pulse/channelmap.h>
#include <pulsecore/memchunk.h>
-int pa_sound_file_load(pa_mempool *pool, const char *fname, pa_sample_spec *ss, pa_channel_map *map, pa_memchunk *chunk);
+int pa_sound_file_load(pa_mempool *pool, const char *fname, pa_sample_spec *ss, pa_channel_map *map, pa_memchunk *chunk, pa_proplist *p);
int pa_sound_file_too_big_to_cache(const char *fname);
diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c
index 373d5637..4ba25ae4 100644
--- a/src/pulsecore/source-output.c
+++ b/src/pulsecore/source-output.c
@@ -87,7 +87,8 @@ static void reset_callbacks(pa_source_output *o) {
o->attach = NULL;
o->detach = NULL;
o->suspend = NULL;
- o->moved = NULL;
+ o->suspend_within_thread = NULL;
+ o->moving = NULL;
o->kill = NULL;
o->get_latency = NULL;
o->state_change = NULL;
@@ -434,11 +435,30 @@ void pa_source_output_push(pa_source_output *o, const pa_memchunk *chunk) {
if (pa_memblockq_push(o->thread_info.delay_memblockq, chunk) < 0) {
pa_log_debug("Delay queue overflow!");
- pa_memblockq_seek(o->thread_info.delay_memblockq, (int64_t) chunk->length, PA_SEEK_RELATIVE);
+ pa_memblockq_seek(o->thread_info.delay_memblockq, (int64_t) chunk->length, PA_SEEK_RELATIVE, TRUE);
}
limit = o->process_rewind ? 0 : o->source->thread_info.max_rewind;
+ if (limit > 0 && o->source->monitor_of) {
+ pa_usec_t latency;
+ size_t n;
+
+ /* Hmm, check the latency for knowing how much of the buffered
+ * data is actually still unplayed and might hence still
+ * change. This is suboptimal. Ideally we'd have a call like
+ * pa_sink_get_changeable_size() or so that tells us how much
+ * of the queued data is actually still changeable. Hence
+ * FIXME! */
+
+ latency = pa_sink_get_latency_within_thread(o->source->monitor_of);
+
+ n = pa_usec_to_bytes(latency, &o->source->sample_spec);
+
+ if (n < limit)
+ limit = n;
+ }
+
/* Implement the delay queue */
while ((length = pa_memblockq_get_length(o->thread_info.delay_memblockq)) > limit) {
pa_memchunk qchunk;
@@ -516,26 +536,15 @@ void pa_source_output_update_max_rewind(pa_source_output *o, size_t nbytes /* i
}
/* Called from thread context */
-static pa_usec_t fixup_latency(pa_source *s, pa_usec_t usec) {
- pa_source_assert_ref(s);
-
- if (usec == (pa_usec_t) -1)
- return usec;
-
- if (s->thread_info.max_latency > 0 && usec > s->thread_info.max_latency)
- usec = s->thread_info.max_latency;
-
- if (s->thread_info.min_latency > 0 && usec < s->thread_info.min_latency)
- usec = s->thread_info.min_latency;
-
- return usec;
-}
-
-/* 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);
- usec = fixup_latency(o->source, usec);
+ if (!(o->source->flags & PA_SOURCE_DYNAMIC_LATENCY))
+ usec = o->source->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);
@@ -546,31 +555,44 @@ pa_usec_t pa_source_output_set_requested_latency_within_thread(pa_source_output
pa_usec_t pa_source_output_set_requested_latency(pa_source_output *o, pa_usec_t usec) {
pa_source_output_assert_ref(o);
- if (PA_SOURCE_OUTPUT_IS_LINKED(o->state))
+ 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);
- else
- /* If this source output is not realized yet, we have to touch
- * the thread info data directly */
+ return usec;
+ }
- o->thread_info.requested_source_latency = usec;
+ /* If this source output is not realized yet or is being moved, we
+ * have to touch the thread info data directly */
+
+ if (o->source) {
+ if (!(o->source->flags & PA_SOURCE_DYNAMIC_LATENCY))
+ usec = o->source->fixed_latency;
+
+ if (usec != (pa_usec_t) -1) {
+ pa_usec_t min_latency, max_latency;
+ pa_source_get_latency_range(o->source, &min_latency, &max_latency);
+ usec = PA_CLAMP(usec, min_latency, max_latency);
+ }
+ }
+
+ o->thread_info.requested_source_latency = usec;
return usec;
}
/* Called from main context */
pa_usec_t pa_source_output_get_requested_latency(pa_source_output *o) {
- pa_usec_t usec = 0;
-
pa_source_output_assert_ref(o);
- if (PA_SOURCE_OUTPUT_IS_LINKED(o->state))
+ if (PA_SOURCE_OUTPUT_IS_LINKED(o->state) && o->source) {
+ pa_usec_t usec = 0;
pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_GET_REQUESTED_LATENCY, &usec, 0, NULL) == 0);
- else
- /* If this source output is not realized yet, we have to touch
- * the thread info data directly */
- usec = o->thread_info.requested_source_latency;
+ return usec;
+ }
- return usec;
+ /* If this source output is not realized yet or is being moved, we
+ * have to touch the thread info data directly */
+
+ return o->thread_info.requested_source_latency;
}
/* Called from main context */
@@ -707,6 +729,8 @@ int pa_source_output_start_move(pa_source_output *o) {
pa_source_update_status(o->source);
o->source = NULL;
+ pa_source_output_unref(o);
+
return 0;
}
@@ -749,9 +773,12 @@ int pa_source_output_finish_move(pa_source_output *o, pa_source *dest, pa_bool_t
} else
new_resampler = NULL;
+ if (o->moving)
+ o->moving(o, dest);
+
o->source = dest;
o->save_source = save;
- pa_idxset_put(o->source->outputs, o, NULL);
+ pa_idxset_put(o->source->outputs, pa_source_output_ref(o), NULL);
if (pa_source_output_get_state(o) == PA_SOURCE_OUTPUT_CORKED)
o->source->n_corked++;
@@ -776,14 +803,12 @@ int pa_source_output_finish_move(pa_source_output *o, pa_source *dest, pa_bool_t
}
pa_source_update_status(dest);
+
pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_ADD_OUTPUT, o, 0, NULL) == 0);
pa_log_debug("Successfully moved source output %i to %s.", o->index, dest->name);
/* Notify everyone */
- if (o->moved)
- o->moved(o);
-
pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_FINISH], o);
pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
@@ -805,11 +830,19 @@ int pa_source_output_move_to(pa_source_output *o, pa_source *dest, pa_bool_t sav
if (!pa_source_output_may_move_to(o, dest))
return -PA_ERR_NOTSUPPORTED;
- if ((r = pa_source_output_start_move(o)) < 0)
+ pa_source_output_ref(o);
+
+ if ((r = pa_source_output_start_move(o)) < 0) {
+ pa_source_output_unref(o);
return r;
+ }
- if ((r = pa_source_output_finish_move(o, dest, save)) < 0)
+ if ((r = pa_source_output_finish_move(o, dest, save)) < 0) {
+ pa_source_output_unref(o);
return r;
+ }
+
+ pa_source_output_unref(o);
return 0;
}
@@ -836,12 +869,9 @@ int pa_source_output_process_msg(pa_msgobject *mo, int code, void *userdata, int
case PA_SOURCE_OUTPUT_MESSAGE_GET_LATENCY: {
pa_usec_t *r = userdata;
- pa_usec_t source_usec = 0;
r[0] += pa_bytes_to_usec(pa_memblockq_get_length(o->thread_info.delay_memblockq), &o->source->sample_spec);
-
- if (o->source->parent.process_msg(PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_GET_LATENCY, &source_usec, 0, NULL) >= 0)
- r[1] += source_usec;
+ r[1] += pa_source_get_latency_within_thread(o->source);
return 0;
}
diff --git a/src/pulsecore/source-output.h b/src/pulsecore/source-output.h
index 018ec886..9824e160 100644
--- a/src/pulsecore/source-output.h
+++ b/src/pulsecore/source-output.h
@@ -116,13 +116,19 @@ struct pa_source_output {
* disconnected from its source. Called from IO thread context */
void (*detach) (pa_source_output *o); /* may be NULL */
- /* If non-NULL called whenever the the source this output is attached
+ /* If non-NULL called whenever the source this output is attached
* to suspends or resumes. Called from main context */
void (*suspend) (pa_source_output *o, pa_bool_t b); /* may be NULL */
- /* If non-NULL called whenever the the source this output is attached
- * to changes. Called from main context */
- void (*moved) (pa_source_output *o); /* may be NULL */
+ /* If non-NULL called whenever the source this output is attached
+ * to suspends or resumes. Called from IO context */
+ void (*suspend_within_thread) (pa_source_output *o, pa_bool_t b); /* may be NULL */
+
+ /* 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. */
+ void (*moving) (pa_source_output *o, pa_source *dest); /* may be NULL */
/* Supposed to unlink and destroy this stream. Called from main
* context. */
diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c
index cc6dfc40..74f38bc5 100644
--- a/src/pulsecore/source.c
+++ b/src/pulsecore/source.c
@@ -41,7 +41,9 @@
#include "source.h"
-#define DEFAULT_MIN_LATENCY (4*PA_USEC_PER_MSEC)
+#define ABSOLUTE_MIN_LATENCY (500)
+#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);
@@ -91,11 +93,29 @@ void pa_source_new_data_set_muted(pa_source_new_data *data, pa_bool_t mute) {
data->muted = !!mute;
}
+void pa_source_new_data_set_port(pa_source_new_data *data, const char *port) {
+ pa_assert(data);
+
+ pa_xfree(data->active_port);
+ data->active_port = pa_xstrdup(port);
+}
+
void pa_source_new_data_done(pa_source_new_data *data) {
pa_assert(data);
- pa_xfree(data->name);
pa_proplist_free(data->proplist);
+
+ if (data->ports) {
+ pa_device_port *p;
+
+ while ((p = pa_hashmap_steal_first(data->ports)))
+ pa_device_port_free(p);
+
+ pa_hashmap_free(data->ports, NULL, NULL);
+ }
+
+ pa_xfree(data->name);
+ pa_xfree(data->active_port);
}
/* Called from main context */
@@ -108,6 +128,7 @@ static void reset_callbacks(pa_source *s) {
s->get_mute = NULL;
s->set_mute = NULL;
s->update_requested_latency = NULL;
+ s->set_port = NULL;
}
/* Called from main context */
@@ -128,6 +149,7 @@ pa_source* pa_source_new(
s = pa_msgobject_new(pa_source);
if (!(name = pa_namereg_register(core, data->name, PA_NAMEREG_SOURCE, s, data->namereg_fail))) {
+ pa_log_debug("Failed to register name %s.", data->name);
pa_xfree(s);
return NULL;
}
@@ -140,6 +162,8 @@ pa_source* pa_source_new(
return NULL;
}
+ /* FIXME, need to free s here on failure */
+
pa_return_null_if_fail(!data->driver || pa_utf8_valid(data->driver));
pa_return_null_if_fail(data->name && pa_utf8_valid(data->name) && data->name[0]);
@@ -165,6 +189,7 @@ pa_source* pa_source_new(
pa_device_init_description(data->proplist);
pa_device_init_icon(data->proplist, FALSE);
+ pa_device_init_intended_roles(data->proplist);
if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SOURCE_FIXATE], data) < 0) {
pa_xfree(s);
@@ -178,6 +203,7 @@ pa_source* pa_source_new(
s->core = core;
s->state = PA_SOURCE_INIT;
s->flags = flags;
+ s->suspend_cause = 0;
s->name = pa_xstrdup(name);
s->proplist = pa_proplist_copy(data->proplist);
s->driver = pa_xstrdup(pa_path_get_filename(data->driver));
@@ -198,12 +224,38 @@ pa_source* pa_source_new(
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 */
+ s->ports = data->ports;
+ data->ports = NULL;
+
+ s->active_port = NULL;
+ s->save_port = FALSE;
+
+ if (data->active_port && s->ports)
+ if ((s->active_port = pa_hashmap_get(s->ports, data->active_port)))
+ s->save_port = data->save_port;
+
+ if (!s->active_port && s->ports) {
+ void *state;
+ pa_device_port *p;
+
+ PA_HASHMAP_FOREACH(p, s->ports, state)
+ if (!s->active_port || p->priority > s->active_port->priority)
+ s->active_port = p;
+ }
+
+ s->save_volume = data->save_volume;
+ s->save_muted = data->save_muted;
+
pa_silence_memchunk_get(
&core->silence_cache,
core->mempool,
@@ -218,8 +270,8 @@ pa_source* pa_source_new(
s->thread_info.max_rewind = 0;
s->thread_info.requested_latency_valid = FALSE;
s->thread_info.requested_latency = 0;
- s->thread_info.min_latency = DEFAULT_MIN_LATENCY;
- s->thread_info.max_latency = 0;
+ s->thread_info.min_latency = ABSOLUTE_MIN_LATENCY;
+ s->thread_info.max_latency = ABSOLUTE_MAX_LATENCY;
pa_assert_se(pa_idxset_put(core->sources, s, &s->index) >= 0);
@@ -302,18 +354,21 @@ void pa_source_put(pa_source *s) {
/* 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 ||
- s->thread_info.min_latency <= s->thread_info.max_latency);
+ pa_assert(s->thread_info.min_latency <= s->thread_info.max_latency);
+
+ /* Generally, flags should be initialized via pa_source_new(). As
+ * a special exception we allow volume related flags to be set
+ * between _new() and _put(). */
- if (!(s->flags & PA_SOURCE_HW_VOLUME_CTRL)) {
+ if (!(s->flags & PA_SOURCE_HW_VOLUME_CTRL))
s->flags |= PA_SOURCE_DECIBEL_VOLUME;
- s->thread_info.soft_volume = s->soft_volume;
- s->thread_info.soft_muted = s->muted;
- }
+ s->thread_info.soft_volume = s->soft_volume;
+ s->thread_info.soft_muted = s->muted;
- if (s->flags & PA_SOURCE_DECIBEL_VOLUME)
- s->n_volume_steps = PA_VOLUME_NORM+1;
+ pa_assert((s->flags & PA_SOURCE_HW_VOLUME_CTRL) || (s->base_volume == PA_VOLUME_NORM && s->flags & PA_SOURCE_DECIBEL_VOLUME));
+ pa_assert(!(s->flags & PA_SOURCE_DECIBEL_VOLUME) || s->n_volume_steps == PA_VOLUME_NORM+1);
+ pa_assert(!(s->flags & PA_SOURCE_DYNAMIC_LATENCY) == (s->fixed_latency != 0));
pa_assert_se(source_set_state(s, PA_SOURCE_IDLE) == 0);
@@ -391,6 +446,15 @@ static void source_free(pa_object *o) {
if (s->proplist)
pa_proplist_free(s->proplist);
+ if (s->ports) {
+ pa_device_port *p;
+
+ while ((p = pa_hashmap_steal_first(s->ports)))
+ pa_device_port_free(p);
+
+ pa_hashmap_free(s->ports, NULL, NULL);
+ }
+
pa_xfree(s);
}
@@ -420,14 +484,25 @@ int pa_source_update_status(pa_source*s) {
}
/* Called from main context */
-int pa_source_suspend(pa_source *s, pa_bool_t suspend) {
+int pa_source_suspend(pa_source *s, pa_bool_t suspend, pa_suspend_cause_t cause) {
pa_source_assert_ref(s);
pa_assert(PA_SOURCE_IS_LINKED(s->state));
+ pa_assert(cause != 0);
if (s->monitor_of)
return -PA_ERR_NOTSUPPORTED;
if (suspend)
+ s->suspend_cause |= cause;
+ else
+ s->suspend_cause &= ~cause;
+
+ if ((pa_source_get_state(s) == PA_SOURCE_SUSPENDED) == !!s->suspend_cause)
+ return 0;
+
+ pa_log_debug("Suspend cause of source %s is 0x%04x, %s", s->name, s->suspend_cause, s->suspend_cause ? "suspending" : "resuming");
+
+ if (suspend)
return source_set_state(s, PA_SOURCE_SUSPENDED);
else
return source_set_state(s, pa_source_used_by(s) ? PA_SOURCE_RUNNING : PA_SOURCE_IDLE);
@@ -452,21 +527,25 @@ int pa_source_sync_suspend(pa_source *s) {
}
/* Called from main context */
-pa_queue *pa_source_move_all_start(pa_source *s) {
- pa_queue *q;
+pa_queue *pa_source_move_all_start(pa_source *s, pa_queue *q) {
pa_source_output *o, *n;
uint32_t idx;
pa_source_assert_ref(s);
pa_assert(PA_SOURCE_IS_LINKED(s->state));
- q = pa_queue_new();
+ if (!q)
+ q = pa_queue_new();
for (o = PA_SOURCE_OUTPUT(pa_idxset_first(s->outputs, &idx)); o; o = n) {
n = PA_SOURCE_OUTPUT(pa_idxset_next(s->outputs, &idx));
+ pa_source_output_ref(o);
+
if (pa_source_output_start_move(o) >= 0)
- pa_queue_push(q, pa_source_output_ref(o));
+ pa_queue_push(q, o);
+ else
+ pa_source_output_unref(o);
}
return q;
@@ -616,8 +695,34 @@ pa_usec_t pa_source_get_latency(pa_source *s) {
return usec;
}
+/* Called from IO thread */
+pa_usec_t pa_source_get_latency_within_thread(pa_source *s) {
+ pa_usec_t usec = 0;
+ pa_msgobject *o;
+
+ pa_source_assert_ref(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! */
+
+ if (s->thread_info.state == PA_SOURCE_SUSPENDED)
+ return 0;
+
+ if (!(s->flags & PA_SOURCE_LATENCY))
+ return 0;
+
+ o = PA_MSGOBJECT(s);
+
+ /* We probably should make this a proper vtable callback instead of going through process_msg() */
+
+ if (o->process_msg(o, PA_SOURCE_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0)
+ return -1;
+
+ return usec;
+}
+
/* Called from main thread */
-void pa_source_set_volume(pa_source *s, const pa_cvolume *volume) {
+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;
@@ -630,6 +735,7 @@ void pa_source_set_volume(pa_source *s, const pa_cvolume *volume) {
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;
if (s->set_volume) {
pa_cvolume_reset(&s->soft_volume, s->sample_spec.channels);
@@ -675,7 +781,24 @@ const pa_cvolume *pa_source_get_volume(pa_source *s, pa_bool_t force_refresh) {
}
/* Called from main thread */
-void pa_source_set_mute(pa_source *s, pa_bool_t mute) {
+void pa_source_volume_changed(pa_source *s, const pa_cvolume *new_volume, pa_bool_t save) {
+ pa_source_assert_ref(s);
+
+ /* 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;
+ return;
+ }
+
+ s->virtual_volume = *new_volume;
+ s->save_volume = save;
+
+ pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+}
+
+/* Called from main thread */
+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);
@@ -683,6 +806,7 @@ void pa_source_set_mute(pa_source *s, pa_bool_t mute) {
old_muted = s->muted;
s->muted = mute;
+ s->save_muted = (old_muted == s->muted && s->save_muted) || save;
if (s->set_mute)
s->set_mute(s);
@@ -695,7 +819,6 @@ void pa_source_set_mute(pa_source *s, pa_bool_t mute) {
/* 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(PA_SOURCE_IS_LINKED(s->state));
@@ -707,19 +830,40 @@ pa_bool_t pa_source_get_mute(pa_source *s, pa_bool_t force_refresh) {
pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_MUTE, NULL, 0, NULL) == 0);
- if (old_muted != s->muted)
+ if (old_muted != s->muted) {
pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+
+ /* Make sure the soft mute status stays in sync */
+ pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_MUTE, NULL, 0, NULL) == 0);
+ }
}
return s->muted;
}
/* Called from main thread */
+void pa_source_mute_changed(pa_source *s, pa_bool_t new_muted, pa_bool_t save) {
+ pa_source_assert_ref(s);
+
+ /* 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;
+ return;
+ }
+
+ s->muted = new_muted;
+ s->save_muted = save;
+
+ pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+}
+
+/* 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(p);
- pa_proplist_update(s->proplist, mode, p);
+ if (p)
+ pa_proplist_update(s->proplist, mode, p);
if (PA_SOURCE_IS_LINKED(s->state)) {
pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_PROPLIST_CHANGED], s);
@@ -787,7 +931,7 @@ unsigned pa_source_check_suspend(pa_source *s) {
ret = 0;
- 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) {
pa_source_output_state_t st;
st = pa_source_output_get_state(o);
@@ -881,9 +1025,26 @@ int pa_source_process_msg(pa_msgobject *object, int code, void *userdata, int64_
case PA_SOURCE_MESSAGE_GET_MUTE:
return 0;
- case PA_SOURCE_MESSAGE_SET_STATE:
+ case PA_SOURCE_MESSAGE_SET_STATE: {
+
+ pa_bool_t suspend_change =
+ (s->thread_info.state == PA_SOURCE_SUSPENDED && PA_SOURCE_IS_OPENED(PA_PTR_TO_UINT(userdata))) ||
+ (PA_SOURCE_IS_OPENED(s->thread_info.state) && PA_PTR_TO_UINT(userdata) == PA_SOURCE_SUSPENDED);
+
s->thread_info.state = PA_PTR_TO_UINT(userdata);
+
+ if (suspend_change) {
+ pa_source_output *o;
+ void *state = NULL;
+
+ while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL)))
+ if (o->suspend_within_thread)
+ o->suspend_within_thread(o, s->thread_info.state == PA_SOURCE_SUSPENDED);
+ }
+
+
return 0;
+ }
case PA_SOURCE_MESSAGE_DETACH:
@@ -911,7 +1072,7 @@ int pa_source_process_msg(pa_msgobject *object, int code, void *userdata, int64_
case PA_SOURCE_MESSAGE_SET_LATENCY_RANGE: {
pa_usec_t *r = userdata;
- pa_source_update_latency_range(s, r[0], r[1]);
+ pa_source_set_latency_range_within_thread(s, r[0], r[1]);
return 0;
}
@@ -930,6 +1091,11 @@ int pa_source_process_msg(pa_msgobject *object, int code, void *userdata, int64_
*((size_t*) userdata) = s->thread_info.max_rewind;
return 0;
+ case PA_SOURCE_MESSAGE_SET_MAX_REWIND:
+
+ pa_source_set_max_rewind_within_thread(s, (size_t) offset);
+ return 0;
+
case PA_SOURCE_MESSAGE_GET_LATENCY:
if (s->monitor_of) {
@@ -948,12 +1114,13 @@ int pa_source_process_msg(pa_msgobject *object, int code, void *userdata, int64_
}
/* Called from main thread */
-int pa_source_suspend_all(pa_core *c, pa_bool_t suspend) {
+int pa_source_suspend_all(pa_core *c, pa_bool_t suspend, pa_suspend_cause_t cause) {
uint32_t idx;
pa_source *source;
int ret = 0;
pa_core_assert_ref(c);
+ pa_assert(cause != 0);
for (source = PA_SOURCE(pa_idxset_first(c->sources, &idx)); source; source = PA_SOURCE(pa_idxset_next(c->sources, &idx))) {
int r;
@@ -961,7 +1128,7 @@ int pa_source_suspend_all(pa_core *c, pa_bool_t suspend) {
if (source->monitor_of)
continue;
- if ((r = pa_source_suspend(source, suspend)) < 0)
+ if ((r = pa_source_suspend(source, suspend, cause)) < 0)
ret = r;
}
@@ -1018,6 +1185,9 @@ pa_usec_t pa_source_get_requested_latency_within_thread(pa_source *s) {
pa_source_assert_ref(s);
+ if (!(s->flags & PA_SOURCE_DYNAMIC_LATENCY))
+ return PA_CLAMP(s->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;
@@ -1027,23 +1197,21 @@ pa_usec_t pa_source_get_requested_latency_within_thread(pa_source *s) {
(result == (pa_usec_t) -1 || result > o->thread_info.requested_source_latency))
result = o->thread_info.requested_source_latency;
- if (result != (pa_usec_t) -1) {
- if (s->thread_info.max_latency > 0 && result > s->thread_info.max_latency)
- result = s->thread_info.max_latency;
+ if (result != (pa_usec_t) -1)
+ result = PA_CLAMP(result, s->thread_info.min_latency, s->thread_info.max_latency);
- if (s->thread_info.min_latency > 0 && result < s->thread_info.min_latency)
- result = s->thread_info.min_latency;
+ if (PA_SOURCE_IS_LINKED(s->thread_info.state)) {
+ /* Only cache this if we are fully set up */
+ s->thread_info.requested_latency = result;
+ s->thread_info.requested_latency_valid = TRUE;
}
- s->thread_info.requested_latency = result;
- s->thread_info.requested_latency_valid = TRUE;
-
return result;
}
/* Called from main thread */
pa_usec_t pa_source_get_requested_latency(pa_source *s) {
- pa_usec_t usec;
+ pa_usec_t usec = 0;
pa_source_assert_ref(s);
pa_assert(PA_SOURCE_IS_LINKED(s->state));
@@ -1057,7 +1225,7 @@ pa_usec_t pa_source_get_requested_latency(pa_source *s) {
}
/* Called from IO thread */
-void pa_source_set_max_rewind(pa_source *s, size_t max_rewind) {
+void pa_source_set_max_rewind_within_thread(pa_source *s, size_t max_rewind) {
pa_source_output *o;
void *state = NULL;
@@ -1074,42 +1242,64 @@ void pa_source_set_max_rewind(pa_source *s, size_t max_rewind) {
}
}
+/* Called from main thread */
+void pa_source_set_max_rewind(pa_source *s, size_t max_rewind) {
+ pa_source_assert_ref(s);
+
+ 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);
+ else
+ pa_source_set_max_rewind_within_thread(s, max_rewind);
+}
+
+/* Called from IO thread */
void pa_source_invalidate_requested_latency(pa_source *s) {
pa_source_output *o;
void *state = NULL;
pa_source_assert_ref(s);
+ if (!(s->flags & PA_SOURCE_DYNAMIC_LATENCY))
+ return;
+
s->thread_info.requested_latency_valid = FALSE;
- if (s->update_requested_latency)
- s->update_requested_latency(s);
+ if (PA_SOURCE_IS_LINKED(s->thread_info.state)) {
- while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL)))
- if (o->update_source_requested_latency)
- o->update_source_requested_latency(o);
+ if (s->update_requested_latency)
+ s->update_requested_latency(s);
+
+ while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL)))
+ if (o->update_source_requested_latency)
+ o->update_source_requested_latency(o);
+ }
if (s->monitor_of)
pa_sink_invalidate_requested_latency(s->monitor_of);
}
+/* 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);
/* min_latency == 0: no limit
- * min_latency == (size_t) -1: default limit
* min_latency anything else: specified limit
*
* Similar for max_latency */
- if (min_latency == (pa_usec_t) -1)
- min_latency = DEFAULT_MIN_LATENCY;
+ if (min_latency < ABSOLUTE_MIN_LATENCY)
+ min_latency = ABSOLUTE_MIN_LATENCY;
+
+ if (max_latency <= 0 ||
+ max_latency > ABSOLUTE_MAX_LATENCY)
+ max_latency = ABSOLUTE_MAX_LATENCY;
- if (max_latency == (pa_usec_t) -1)
- max_latency = min_latency;
+ pa_assert(min_latency <= max_latency);
- pa_assert(!min_latency || !max_latency ||
- min_latency <= max_latency);
+ /* Hmm, let's see if someone forgot to set PA_SOURCE_DYNAMIC_LATENCY here... */
+ pa_assert((min_latency == ABSOLUTE_MIN_LATENCY &&
+ max_latency == ABSOLUTE_MAX_LATENCY) ||
+ (s->flags & PA_SOURCE_DYNAMIC_LATENCY));
if (PA_SOURCE_IS_LINKED(s->state)) {
pa_usec_t r[2];
@@ -1118,14 +1308,11 @@ void pa_source_set_latency_range(pa_source *s, pa_usec_t min_latency, pa_usec_t
r[1] = max_latency;
pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_LATENCY_RANGE, r, 0, NULL) == 0);
- } else {
- s->thread_info.min_latency = min_latency;
- s->thread_info.max_latency = max_latency;
-
- s->thread_info.requested_latency_valid = FALSE;
- }
+ } else
+ pa_source_set_latency_range_within_thread(s, min_latency, max_latency);
}
+/* 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(min_latency);
@@ -1144,26 +1331,52 @@ void pa_source_get_latency_range(pa_source *s, pa_usec_t *min_latency, pa_usec_t
}
}
-/* Called from IO thread */
-void pa_source_update_latency_range(pa_source *s, pa_usec_t min_latency, pa_usec_t max_latency) {
- pa_source_output *o;
+/* 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_assert(!min_latency || !max_latency ||
- min_latency <= max_latency);
+ pa_assert(min_latency >= ABSOLUTE_MIN_LATENCY);
+ pa_assert(max_latency <= ABSOLUTE_MAX_LATENCY);
+ pa_assert(min_latency <= max_latency);
+
+ /* Hmm, let's see if someone forgot to set PA_SOURCE_DYNAMIC_LATENCY here... */
+ pa_assert((min_latency == ABSOLUTE_MIN_LATENCY &&
+ max_latency == ABSOLUTE_MAX_LATENCY) ||
+ (s->flags & PA_SOURCE_DYNAMIC_LATENCY) ||
+ s->monitor_of);
s->thread_info.min_latency = min_latency;
s->thread_info.max_latency = max_latency;
- while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL)))
- if (o->update_source_latency_range)
- o->update_source_latency_range(o);
+ if (PA_SOURCE_IS_LINKED(s->thread_info.state)) {
+ pa_source_output *o;
+
+ while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL)))
+ if (o->update_source_latency_range)
+ o->update_source_latency_range(o);
+ }
pa_source_invalidate_requested_latency(s);
}
+/* Called from main thread, before the source is put */
+void pa_source_set_fixed_latency(pa_source *s, pa_usec_t latency) {
+ pa_source_assert_ref(s);
+
+ pa_assert(pa_source_get_state(s) == PA_SOURCE_INIT);
+
+ if (latency < ABSOLUTE_MIN_LATENCY)
+ latency = ABSOLUTE_MIN_LATENCY;
+
+ if (latency > ABSOLUTE_MAX_LATENCY)
+ latency = ABSOLUTE_MAX_LATENCY;
+
+ s->fixed_latency = latency;
+}
+
+/* Called from main thread */
size_t pa_source_get_max_rewind(pa_source *s) {
size_t r;
pa_source_assert_ref(s);
@@ -1175,3 +1388,38 @@ size_t pa_source_get_max_rewind(pa_source *s) {
return r;
}
+
+/* Called from main context */
+int pa_source_set_port(pa_source *s, const char *name, pa_bool_t save) {
+ pa_device_port *port;
+
+ pa_assert(s);
+
+ if (!s->set_port) {
+ pa_log_debug("set_port() operation not implemented for sink %u \"%s\"", s->index, s->name);
+ return -PA_ERR_NOTIMPLEMENTED;
+ }
+
+ if (!s->ports)
+ return -PA_ERR_NOENTITY;
+
+ if (!(port = pa_hashmap_get(s->ports, name)))
+ return -PA_ERR_NOENTITY;
+
+ if (s->active_port == port) {
+ s->save_port = s->save_port || save;
+ return 0;
+ }
+
+ if ((s->set_port(s, port)) < 0)
+ return -PA_ERR_NOENTITY;
+
+ pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+
+ pa_log_info("Changed port of source %u \"%s\" to %s", s->index, s->name, port->name);
+
+ s->active_port = port;
+ s->save_port = save;
+
+ return 0;
+}
diff --git a/src/pulsecore/source.h b/src/pulsecore/source.h
index 26471de0..7e9fd8b7 100644
--- a/src/pulsecore/source.h
+++ b/src/pulsecore/source.h
@@ -56,8 +56,10 @@ struct pa_source {
uint32_t index;
pa_core *core;
+
pa_source_state_t state;
pa_source_flags_t flags;
+ pa_suspend_cause_t suspend_cause;
char *name;
char *driver; /* may be NULL */
@@ -82,11 +84,20 @@ struct pa_source {
pa_bool_t refresh_volume:1;
pa_bool_t refresh_muted:1;
+ pa_bool_t save_port:1;
+ pa_bool_t save_volume:1;
+ 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;
+
/* Called when the main loop requests a state change. Called from
* main loop context. If returns -1 the state change will be
* inhibited */
@@ -118,6 +129,10 @@ struct pa_source {
* thread context. */
void (*update_requested_latency)(pa_source *s); /* dito */
+ /* Called whenever the port shall be changed. Called from main
+ * thread. */
+ int (*set_port)(pa_source *s, pa_device_port *port); /*dito */
+
/* Contains copies of the above data so that the real-time worker
* thread can work without access locking */
struct {
@@ -159,6 +174,7 @@ typedef enum pa_source_message {
PA_SOURCE_MESSAGE_SET_LATENCY_RANGE,
PA_SOURCE_MESSAGE_GET_LATENCY_RANGE,
PA_SOURCE_MESSAGE_GET_MAX_REWIND,
+ PA_SOURCE_MESSAGE_SET_MAX_REWIND,
PA_SOURCE_MESSAGE_MAX
} pa_source_message_t;
@@ -170,6 +186,9 @@ typedef struct pa_source_new_data {
pa_module *module;
pa_card *card;
+ pa_hashmap *ports;
+ char *active_port;
+
pa_sample_spec sample_spec;
pa_channel_map channel_map;
pa_cvolume volume;
@@ -181,6 +200,10 @@ typedef struct pa_source_new_data {
pa_bool_t channel_map_is_set:1;
pa_bool_t namereg_fail:1;
+
+ pa_bool_t save_port:1;
+ pa_bool_t save_volume:1;
+ pa_bool_t save_muted:1;
} pa_source_new_data;
pa_source_new_data* pa_source_new_data_init(pa_source_new_data *data);
@@ -189,6 +212,7 @@ void pa_source_new_data_set_sample_spec(pa_source_new_data *data, const pa_sampl
void pa_source_new_data_set_channel_map(pa_source_new_data *data, const pa_channel_map *map);
void pa_source_new_data_set_volume(pa_source_new_data *data, const pa_cvolume *volume);
void pa_source_new_data_set_muted(pa_source_new_data *data, pa_bool_t mute);
+void pa_source_new_data_set_port(pa_source_new_data *data, const char *port);
void pa_source_new_data_done(pa_source_new_data *data);
/*** To be called exclusively by the source driver, from main context */
@@ -205,12 +229,16 @@ void pa_source_set_description(pa_source *s, const char *description);
void pa_source_set_asyncmsgq(pa_source *s, pa_asyncmsgq *q);
void pa_source_set_rtpoll(pa_source *s, pa_rtpoll *p);
+void pa_source_set_max_rewind(pa_source *s, size_t max_rewind);
void pa_source_set_latency_range(pa_source *s, pa_usec_t min_latency, pa_usec_t max_latency);
+void pa_source_set_fixed_latency(pa_source *s, pa_usec_t latency);
void pa_source_detach(pa_source *s);
void pa_source_attach(pa_source *s);
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);
int pa_source_sync_suspend(pa_source *s);
@@ -224,23 +252,25 @@ void pa_source_get_latency_range(pa_source *s, pa_usec_t *min_latency, pa_usec_t
size_t pa_source_get_max_rewind(pa_source *s);
int pa_source_update_status(pa_source*s);
-int pa_source_suspend(pa_source *s, pa_bool_t suspend);
-int pa_source_suspend_all(pa_core *c, pa_bool_t suspend);
+int pa_source_suspend(pa_source *s, pa_bool_t suspend, pa_suspend_cause_t cause);
+int pa_source_suspend_all(pa_core *c, pa_bool_t suspend, pa_suspend_cause_t cause);
-void pa_source_set_volume(pa_source *source, const pa_cvolume *volume);
+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);
+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);
pa_bool_t pa_source_update_proplist(pa_source *s, pa_update_mode_t mode, pa_proplist *p);
+int pa_source_set_port(pa_source *s, const char *name, pa_bool_t save);
+
unsigned pa_source_linked_by(pa_source *s); /* Number of connected streams */
unsigned pa_source_used_by(pa_source *s); /* Number of connected streams that are not corked */
unsigned pa_source_check_suspend(pa_source *s); /* Returns how many streams are active that don't allow suspensions */
#define pa_source_get_state(s) ((pa_source_state_t) (s)->state)
/* Moves all inputs away, and stores them in pa_queue */
-pa_queue *pa_source_move_all_start(pa_source *s);
+pa_queue *pa_source_move_all_start(pa_source *s, pa_queue *q);
void pa_source_move_all_finish(pa_source *s, pa_queue *q, pa_bool_t save);
void pa_source_move_all_fail(pa_queue *q);
@@ -257,11 +287,12 @@ 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(pa_source *s, size_t max_rewind);
-void pa_source_update_latency_range(pa_source *s, pa_usec_t min_latency, pa_usec_t max_latency);
+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);
/*** To be called exclusively by source output drivers, from IO context */
void pa_source_invalidate_requested_latency(pa_source *s);
+pa_usec_t pa_source_get_latency_within_thread(pa_source *s);
#endif
diff --git a/src/pulsecore/strbuf.c b/src/pulsecore/strbuf.c
index 9f5a84b4..4fc82ded 100644
--- a/src/pulsecore/strbuf.c
+++ b/src/pulsecore/strbuf.c
@@ -113,6 +113,13 @@ void pa_strbuf_puts(pa_strbuf *sb, const char *t) {
pa_strbuf_putsn(sb, t, strlen(t));
}
+/* Append a character to the string buffer */
+void pa_strbuf_putc(pa_strbuf *sb, char c) {
+ pa_assert(sb);
+
+ pa_strbuf_putsn(sb, &c, 1);
+}
+
/* Append a new chunk to the linked list */
static void append(pa_strbuf *sb, struct chunk *c) {
pa_assert(sb);
diff --git a/src/pulsecore/strbuf.h b/src/pulsecore/strbuf.h
index 05e69e03..d71ecb98 100644
--- a/src/pulsecore/strbuf.h
+++ b/src/pulsecore/strbuf.h
@@ -35,6 +35,7 @@ char *pa_strbuf_tostring_free(pa_strbuf *sb);
size_t pa_strbuf_printf(pa_strbuf *sb, const char *format, ...) PA_GCC_PRINTF_ATTR(2,3);
void pa_strbuf_puts(pa_strbuf *sb, const char *t);
void pa_strbuf_putsn(pa_strbuf *sb, const char *t, size_t m);
+void pa_strbuf_putc(pa_strbuf *sb, char c);
pa_bool_t pa_strbuf_isempty(pa_strbuf *sb);
diff --git a/src/pulsecore/strlist.c b/src/pulsecore/strlist.c
index cbafbba6..0f4ca867 100644
--- a/src/pulsecore/strlist.c
+++ b/src/pulsecore/strlist.c
@@ -159,3 +159,15 @@ pa_strlist *pa_strlist_reverse(pa_strlist *l) {
return r;
}
+
+pa_strlist *pa_strlist_next(pa_strlist *s) {
+ pa_assert(s);
+
+ return s->next;
+}
+
+const char *pa_strlist_data(pa_strlist *s) {
+ pa_assert(s);
+
+ return ITEM_TO_TEXT(s);
+}
diff --git a/src/pulsecore/strlist.h b/src/pulsecore/strlist.h
index 2584e86c..e57203c5 100644
--- a/src/pulsecore/strlist.h
+++ b/src/pulsecore/strlist.h
@@ -47,4 +47,10 @@ pa_strlist* pa_strlist_parse(const char *s);
/* Reverse string list */
pa_strlist *pa_strlist_reverse(pa_strlist *l);
+/* Return the next item in the list */
+pa_strlist *pa_strlist_next(pa_strlist *s);
+
+/* Return the string associated to the current item */
+const char *pa_strlist_data(pa_strlist *s);
+
#endif
diff --git a/src/pulsecore/time-smoother.c b/src/pulsecore/time-smoother.c
index 65621948..9d5a0705 100644
--- a/src/pulsecore/time-smoother.c
+++ b/src/pulsecore/time-smoother.c
@@ -78,17 +78,26 @@ struct pa_smoother {
/* Cached parameters for our interpolation polynomial y=ax^3+b^2+cx */
double a, b, c;
- pa_bool_t abc_valid;
+ pa_bool_t abc_valid:1;
pa_bool_t monotonic:1;
pa_bool_t paused:1;
+ pa_bool_t smoothing:1; /* If FALSE we skip the polonyomial interpolation step */
pa_usec_t pause_time;
unsigned min_history;
};
-pa_smoother* pa_smoother_new(pa_usec_t adjust_time, pa_usec_t history_time, pa_bool_t monotonic, unsigned min_history) {
+pa_smoother* pa_smoother_new(
+ pa_usec_t adjust_time,
+ pa_usec_t history_time,
+ pa_bool_t monotonic,
+ pa_bool_t smoothing,
+ unsigned min_history,
+ pa_usec_t time_offset,
+ pa_bool_t paused) {
+
pa_smoother *s;
pa_assert(adjust_time > 0);
@@ -116,9 +125,13 @@ pa_smoother* pa_smoother_new(pa_usec_t adjust_time, pa_usec_t history_time, pa_b
s->abc_valid = FALSE;
s->paused = FALSE;
+ s->smoothing = smoothing;
s->min_history = min_history;
+ s->paused = paused;
+ s->time_offset = s->pause_time = time_offset;
+
return s;
}
@@ -279,6 +292,7 @@ static void estimate(pa_smoother *s, pa_usec_t x, pa_usec_t *y, double *deriv) {
pa_assert(y);
if (x >= s->px) {
+ /* Linear interpolation right from px */
int64_t t;
/* The requested point is right of the point where we wanted
@@ -294,7 +308,22 @@ static void estimate(pa_smoother *s, pa_usec_t x, pa_usec_t *y, double *deriv) {
if (deriv)
*deriv = s->dp;
+ } else if (x <= s->ex) {
+ /* Linear interpolation left from ex */
+ int64_t t;
+
+ t = (int64_t) s->ey - (int64_t) llrint(s->de * (double) (s->ex - x));
+
+ if (t < 0)
+ t = 0;
+
+ *y = (pa_usec_t) t;
+
+ if (deriv)
+ *deriv = s->de;
+
} else {
+ /* Spline interpolation between ex and px */
double tx, ty;
/* Ok, we're not yet on track, thus let's interpolate, and
@@ -348,7 +377,6 @@ void pa_smoother_put(pa_smoother *s, pa_usec_t x, pa_usec_t y) {
* we can adjust our position smoothly from this one */
estimate(s, x, &ney, &nde);
s->ex = x; s->ey = ney; s->de = nde;
-
s->ry = y;
}
@@ -359,12 +387,19 @@ void pa_smoother_put(pa_smoother *s, pa_usec_t x, pa_usec_t y) {
s->dp = avg_gradient(s, x);
/* And calculate when we want to be on track again */
- s->px = s->ex + s->adjust_time;
- s->py = s->ry + (pa_usec_t) llrint(s->dp * (double) s->adjust_time);
+ if (s->smoothing) {
+ s->px = s->ex + s->adjust_time;
+ s->py = s->ry + (pa_usec_t) llrint(s->dp * (double) s->adjust_time);
+ } else {
+ s->px = s->ex;
+ s->py = s->ry;
+ }
s->abc_valid = FALSE;
-/* pa_log_debug("put(%llu | %llu) = %llu", (unsigned long long) (x + s->time_offset), (unsigned long long) x, (unsigned long long) y); */
+#ifdef DEBUG_DATA
+ pa_log_debug("%p, put(%llu | %llu) = %llu", s, (unsigned long long) (x + s->time_offset), (unsigned long long) x, (unsigned long long) y);
+#endif
}
pa_usec_t pa_smoother_get(pa_smoother *s, pa_usec_t x) {
@@ -395,7 +430,9 @@ pa_usec_t pa_smoother_get(pa_smoother *s, pa_usec_t x) {
s->last_y = y;
}
-/* pa_log_debug("get(%llu | %llu) = %llu", (unsigned long long) (x + s->time_offset), (unsigned long long) x, (unsigned long long) y); */
+#ifdef DEBUG_DATA
+ pa_log_debug("%p, get(%llu | %llu) = %llu", s, (unsigned long long) (x + s->time_offset), (unsigned long long) x, (unsigned long long) y);
+#endif
return y;
}
@@ -405,7 +442,9 @@ void pa_smoother_set_time_offset(pa_smoother *s, pa_usec_t offset) {
s->time_offset = offset;
-/* pa_log_debug("offset(%llu)", (unsigned long long) offset); */
+#ifdef DEBUG_DATA
+ pa_log_debug("offset(%llu)", (unsigned long long) offset);
+#endif
}
void pa_smoother_pause(pa_smoother *s, pa_usec_t x) {
@@ -414,13 +453,15 @@ void pa_smoother_pause(pa_smoother *s, pa_usec_t x) {
if (s->paused)
return;
-/* pa_log_debug("pause(%llu)", (unsigned long long) x); */
+#ifdef DEBUG_DATA
+ pa_log_debug("pause(%llu)", (unsigned long long) x);
+#endif
s->paused = TRUE;
s->pause_time = x;
}
-void pa_smoother_resume(pa_smoother *s, pa_usec_t x) {
+void pa_smoother_resume(pa_smoother *s, pa_usec_t x, pa_bool_t fix_now) {
pa_assert(s);
if (!s->paused)
@@ -429,10 +470,22 @@ void pa_smoother_resume(pa_smoother *s, pa_usec_t x) {
if (x < s->pause_time)
x = s->pause_time;
-/* pa_log_debug("resume(%llu)", (unsigned long long) x); */
+#ifdef DEBUG_DATA
+ pa_log_debug("resume(%llu)", (unsigned long long) x);
+#endif
s->paused = FALSE;
s->time_offset += x - s->pause_time;
+
+ if (fix_now)
+ pa_smoother_fix_now(s);
+}
+
+void pa_smoother_fix_now(pa_smoother *s) {
+ pa_assert(s);
+
+ s->px = s->ex;
+ s->py = s->ry;
}
pa_usec_t pa_smoother_translate(pa_smoother *s, pa_usec_t x, pa_usec_t y_delay) {
@@ -454,7 +507,9 @@ pa_usec_t pa_smoother_translate(pa_smoother *s, pa_usec_t x, pa_usec_t y_delay)
if (s->dp > nde)
nde = s->dp;
-/* pa_log_debug("translate(%llu) = %llu (%0.2f)", (unsigned long long) y_delay, (unsigned long long) ((double) y_delay / nde), nde); */
+#ifdef DEBUG_DATA
+ pa_log_debug("translate(%llu) = %llu (%0.2f)", (unsigned long long) y_delay, (unsigned long long) ((double) y_delay / nde), nde);
+#endif
return (pa_usec_t) llrint((double) y_delay / nde);
}
diff --git a/src/pulsecore/time-smoother.h b/src/pulsecore/time-smoother.h
index 2051e640..5244a7e7 100644
--- a/src/pulsecore/time-smoother.h
+++ b/src/pulsecore/time-smoother.h
@@ -27,7 +27,15 @@
typedef struct pa_smoother pa_smoother;
-pa_smoother* pa_smoother_new(pa_usec_t x_adjust_time, pa_usec_t x_history_time, pa_bool_t monotonic, unsigned min_history);
+pa_smoother* pa_smoother_new(
+ pa_usec_t x_adjust_time,
+ pa_usec_t x_history_time,
+ pa_bool_t monotonic,
+ pa_bool_t smoothing,
+ unsigned min_history,
+ pa_usec_t x_offset,
+ pa_bool_t paused);
+
void pa_smoother_free(pa_smoother* s);
/* Adds a new value to our dataset. x = local/system time, y = remote time */
@@ -42,8 +50,10 @@ pa_usec_t pa_smoother_translate(pa_smoother *s, pa_usec_t x, pa_usec_t y_delay);
void pa_smoother_set_time_offset(pa_smoother *s, pa_usec_t x_offset);
void pa_smoother_pause(pa_smoother *s, pa_usec_t x);
-void pa_smoother_resume(pa_smoother *s, pa_usec_t x);
+void pa_smoother_resume(pa_smoother *s, pa_usec_t x, pa_bool_t abrupt);
void pa_smoother_reset(pa_smoother *s);
+void pa_smoother_fix_now(pa_smoother *s);
+
#endif