summaryrefslogtreecommitdiffstats
path: root/src/pulsecore
diff options
context:
space:
mode:
Diffstat (limited to 'src/pulsecore')
-rw-r--r--src/pulsecore/asyncmsgq.c38
-rw-r--r--src/pulsecore/asyncmsgq.h18
-rw-r--r--src/pulsecore/asyncq.c146
-rw-r--r--src/pulsecore/asyncq.h23
-rw-r--r--src/pulsecore/atomic.h250
-rw-r--r--src/pulsecore/authkey-prop.c10
-rw-r--r--src/pulsecore/authkey-prop.h2
-rw-r--r--src/pulsecore/authkey.c14
-rw-r--r--src/pulsecore/authkey.h2
-rw-r--r--src/pulsecore/autoload.c14
-rw-r--r--src/pulsecore/autoload.h4
-rw-r--r--src/pulsecore/avahi-wrap.c2
-rw-r--r--src/pulsecore/avahi-wrap.h2
-rw-r--r--src/pulsecore/cli-command.c356
-rw-r--r--src/pulsecore/cli-command.h13
-rw-r--r--src/pulsecore/cli-text.c238
-rw-r--r--src/pulsecore/cli-text.h2
-rw-r--r--src/pulsecore/cli.c20
-rw-r--r--src/pulsecore/cli.h2
-rw-r--r--src/pulsecore/client.c35
-rw-r--r--src/pulsecore/client.h10
-rw-r--r--src/pulsecore/conf-parser.c11
-rw-r--r--src/pulsecore/conf-parser.h2
-rw-r--r--src/pulsecore/core-error.c4
-rw-r--r--src/pulsecore/core-error.h2
-rw-r--r--src/pulsecore/core-scache.c121
-rw-r--r--src/pulsecore/core-scache.h19
-rw-r--r--src/pulsecore/core-subscribe.c2
-rw-r--r--src/pulsecore/core-subscribe.h2
-rw-r--r--src/pulsecore/core-util.c788
-rw-r--r--src/pulsecore/core-util.h86
-rw-r--r--src/pulsecore/core.c26
-rw-r--r--src/pulsecore/core.h29
-rw-r--r--src/pulsecore/creds.h2
-rw-r--r--src/pulsecore/dllmain.c2
-rw-r--r--src/pulsecore/dynarray.c8
-rw-r--r--src/pulsecore/dynarray.h2
-rw-r--r--src/pulsecore/endianmacros.h28
-rw-r--r--src/pulsecore/envelope.c781
-rw-r--r--src/pulsecore/envelope.h53
-rw-r--r--src/pulsecore/esound.h2
-rw-r--r--src/pulsecore/fdsem.c102
-rw-r--r--src/pulsecore/fdsem.h10
-rw-r--r--src/pulsecore/ffmpeg/avcodec.h2
-rw-r--r--src/pulsecore/flist.c2
-rw-r--r--src/pulsecore/flist.h4
-rw-r--r--src/pulsecore/gccmacro.h80
-rw-r--r--src/pulsecore/hashmap.c46
-rw-r--r--src/pulsecore/hashmap.h9
-rw-r--r--src/pulsecore/hook-list.c39
-rw-r--r--src/pulsecore/hook-list.h21
-rw-r--r--src/pulsecore/idxset.c18
-rw-r--r--src/pulsecore/idxset.h2
-rw-r--r--src/pulsecore/inet_ntop.c3
-rw-r--r--src/pulsecore/inet_pton.c3
-rw-r--r--src/pulsecore/iochannel.c57
-rw-r--r--src/pulsecore/iochannel.h4
-rw-r--r--src/pulsecore/ioline.c52
-rw-r--r--src/pulsecore/ioline.h6
-rw-r--r--src/pulsecore/ipacl.c2
-rw-r--r--src/pulsecore/ipacl.h2
-rw-r--r--src/pulsecore/llist.h2
-rw-r--r--src/pulsecore/log.c40
-rw-r--r--src/pulsecore/log.h4
-rw-r--r--src/pulsecore/ltdl-helper.c12
-rw-r--r--src/pulsecore/ltdl-helper.h2
-rw-r--r--src/pulsecore/macro.h135
-rw-r--r--src/pulsecore/mcalign.c11
-rw-r--r--src/pulsecore/mcalign.h5
-rw-r--r--src/pulsecore/memblock.c120
-rw-r--r--src/pulsecore/memblock.h17
-rw-r--r--src/pulsecore/memblockq.c491
-rw-r--r--src/pulsecore/memblockq.h45
-rw-r--r--src/pulsecore/memchunk.c38
-rw-r--r--src/pulsecore/memchunk.h5
-rw-r--r--src/pulsecore/modargs.c30
-rw-r--r--src/pulsecore/modargs.h5
-rw-r--r--src/pulsecore/modinfo.c11
-rw-r--r--src/pulsecore/modinfo.h4
-rw-r--r--src/pulsecore/module.c32
-rw-r--r--src/pulsecore/module.h29
-rw-r--r--src/pulsecore/msgobject.c2
-rw-r--r--src/pulsecore/msgobject.h2
-rw-r--r--src/pulsecore/mutex-posix.c24
-rw-r--r--src/pulsecore/mutex-win32.c2
-rw-r--r--src/pulsecore/mutex.h3
-rw-r--r--src/pulsecore/namereg.c18
-rw-r--r--src/pulsecore/namereg.h7
-rw-r--r--src/pulsecore/native-common.h44
-rw-r--r--src/pulsecore/object.c6
-rw-r--r--src/pulsecore/object.h2
-rw-r--r--src/pulsecore/once.c6
-rw-r--r--src/pulsecore/once.h14
-rw-r--r--src/pulsecore/packet.c2
-rw-r--r--src/pulsecore/packet.h2
-rw-r--r--src/pulsecore/parseaddr.c13
-rw-r--r--src/pulsecore/parseaddr.h2
-rw-r--r--src/pulsecore/pdispatch.c18
-rw-r--r--src/pulsecore/pdispatch.h2
-rw-r--r--src/pulsecore/pid.c110
-rw-r--r--src/pulsecore/pid.h8
-rw-r--r--src/pulsecore/pipe.c2
-rw-r--r--src/pulsecore/pipe.h2
-rw-r--r--src/pulsecore/play-memblockq.c134
-rw-r--r--src/pulsecore/play-memblockq.h11
-rw-r--r--src/pulsecore/play-memchunk.c160
-rw-r--r--src/pulsecore/play-memchunk.h7
-rw-r--r--src/pulsecore/poll.c2
-rw-r--r--src/pulsecore/poll.h2
-rw-r--r--src/pulsecore/proplist-util.c118
-rw-r--r--src/pulsecore/proplist-util.h (renamed from src/pulsecore/core-def.h)24
-rw-r--r--src/pulsecore/props.c12
-rw-r--r--src/pulsecore/props.h2
-rw-r--r--src/pulsecore/protocol-cli.c10
-rw-r--r--src/pulsecore/protocol-cli.h2
-rw-r--r--src/pulsecore/protocol-esound.c191
-rw-r--r--src/pulsecore/protocol-esound.h2
-rw-r--r--src/pulsecore/protocol-http.c14
-rw-r--r--src/pulsecore/protocol-http.h2
-rw-r--r--src/pulsecore/protocol-native.c1567
-rw-r--r--src/pulsecore/protocol-native.h2
-rw-r--r--src/pulsecore/protocol-simple.c184
-rw-r--r--src/pulsecore/protocol-simple.h2
-rw-r--r--src/pulsecore/pstream-util.c4
-rw-r--r--src/pulsecore/pstream-util.h2
-rw-r--r--src/pulsecore/pstream.c55
-rw-r--r--src/pulsecore/pstream.h15
-rw-r--r--src/pulsecore/queue.c16
-rw-r--r--src/pulsecore/queue.h2
-rw-r--r--src/pulsecore/random.c8
-rw-r--r--src/pulsecore/random.h2
-rw-r--r--src/pulsecore/refcnt.h5
-rw-r--r--src/pulsecore/resampler.c776
-rw-r--r--src/pulsecore/resampler.h18
-rw-r--r--src/pulsecore/rtclock.c23
-rw-r--r--src/pulsecore/rtclock.h4
-rw-r--r--src/pulsecore/rtpoll.c143
-rw-r--r--src/pulsecore/rtpoll.h10
-rw-r--r--src/pulsecore/rtsig.c2
-rw-r--r--src/pulsecore/rtsig.h2
-rw-r--r--src/pulsecore/sample-util.c791
-rw-r--r--src/pulsecore/sample-util.h31
-rw-r--r--src/pulsecore/sconv-s16be.c17
-rw-r--r--src/pulsecore/sconv-s16be.h22
-rw-r--r--src/pulsecore/sconv-s16le.c133
-rw-r--r--src/pulsecore/sconv-s16le.h22
-rw-r--r--src/pulsecore/sconv.c14
-rw-r--r--src/pulsecore/sconv.h2
-rw-r--r--src/pulsecore/semaphore-posix.c2
-rw-r--r--src/pulsecore/semaphore-win32.c2
-rw-r--r--src/pulsecore/semaphore.h2
-rw-r--r--src/pulsecore/shm.c68
-rw-r--r--src/pulsecore/shm.h10
-rw-r--r--src/pulsecore/shmasyncq.c220
-rw-r--r--src/pulsecore/shmasyncq.h60
-rw-r--r--src/pulsecore/sink-input.c977
-rw-r--r--src/pulsecore/sink-input.h170
-rw-r--r--src/pulsecore/sink.c1072
-rw-r--r--src/pulsecore/sink.h150
-rw-r--r--src/pulsecore/sioman.c2
-rw-r--r--src/pulsecore/sioman.h2
-rw-r--r--src/pulsecore/socket-client.c66
-rw-r--r--src/pulsecore/socket-client.h10
-rw-r--r--src/pulsecore/socket-server.c23
-rw-r--r--src/pulsecore/socket-server.h6
-rw-r--r--src/pulsecore/socket-util.c51
-rw-r--r--src/pulsecore/socket-util.h8
-rw-r--r--src/pulsecore/sound-file-stream.c238
-rw-r--r--src/pulsecore/sound-file-stream.h2
-rw-r--r--src/pulsecore/sound-file.c14
-rw-r--r--src/pulsecore/sound-file.h2
-rw-r--r--src/pulsecore/source-output.c467
-rw-r--r--src/pulsecore/source-output.h109
-rw-r--r--src/pulsecore/source.c698
-rw-r--r--src/pulsecore/source.h126
-rw-r--r--src/pulsecore/speex/arch.h62
-rw-r--r--src/pulsecore/speex/fixed_generic.h8
-rw-r--r--src/pulsecore/speex/resample.c123
-rw-r--r--src/pulsecore/speex/speex_resampler.h116
-rw-r--r--src/pulsecore/speexwrap.h4
-rw-r--r--src/pulsecore/start-child.c160
-rw-r--r--src/pulsecore/start-child.h30
-rw-r--r--src/pulsecore/strbuf.c18
-rw-r--r--src/pulsecore/strbuf.h4
-rw-r--r--src/pulsecore/strlist.c21
-rw-r--r--src/pulsecore/strlist.h5
-rw-r--r--src/pulsecore/tagstruct.c134
-rw-r--r--src/pulsecore/tagstruct.h15
-rw-r--r--src/pulsecore/thread-mq.c41
-rw-r--r--src/pulsecore/thread-mq.h7
-rw-r--r--src/pulsecore/thread-posix.c4
-rw-r--r--src/pulsecore/thread-win32.c2
-rw-r--r--src/pulsecore/thread.h2
-rw-r--r--src/pulsecore/time-smoother.c245
-rw-r--r--src/pulsecore/time-smoother.h14
-rw-r--r--src/pulsecore/tokenizer.c8
-rw-r--r--src/pulsecore/tokenizer.h2
-rw-r--r--src/pulsecore/x11prop.c2
-rw-r--r--src/pulsecore/x11prop.h2
-rw-r--r--src/pulsecore/x11wrap.c53
-rw-r--r--src/pulsecore/x11wrap.h12
201 files changed, 11315 insertions, 3867 deletions
diff --git a/src/pulsecore/asyncmsgq.c b/src/pulsecore/asyncmsgq.c
index 96b43a71..5c7af2a8 100644
--- a/src/pulsecore/asyncmsgq.c
+++ b/src/pulsecore/asyncmsgq.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -136,7 +134,7 @@ void pa_asyncmsgq_post(pa_asyncmsgq *a, pa_msgobject *object, int code, const vo
/* This mutex makes the queue multiple-writer safe. This lock is only used on the writing side */
pa_mutex_lock(a->mutex);
- pa_assert_se(pa_asyncq_push(a->asyncq, i, 1) == 0);
+ pa_asyncq_post(a->asyncq, i);
pa_mutex_unlock(a->mutex);
}
@@ -163,7 +161,7 @@ int pa_asyncmsgq_send(pa_asyncmsgq *a, pa_msgobject *object, int code, const voi
/* Thus mutex makes the queue multiple-writer safe. This lock is only used on the writing side */
pa_mutex_lock(a->mutex);
- pa_assert_se(pa_asyncq_push(a->asyncq, &i, 1) == 0);
+ pa_assert_se(pa_asyncq_push(a->asyncq, &i, TRUE) == 0);
pa_mutex_unlock(a->mutex);
pa_semaphore_wait(i.semaphore);
@@ -174,7 +172,7 @@ int pa_asyncmsgq_send(pa_asyncmsgq *a, pa_msgobject *object, int code, const voi
return i.ret;
}
-int pa_asyncmsgq_get(pa_asyncmsgq *a, pa_msgobject **object, int *code, void **userdata, int64_t *offset, pa_memchunk *chunk, int wait) {
+int pa_asyncmsgq_get(pa_asyncmsgq *a, pa_msgobject **object, int *code, void **userdata, int64_t *offset, pa_memchunk *chunk, pa_bool_t wait) {
pa_assert(PA_REFCNT_VALUE(a) > 0);
pa_assert(!a->current);
@@ -276,22 +274,40 @@ int pa_asyncmsgq_process_one(pa_asyncmsgq *a) {
return 1;
}
-int pa_asyncmsgq_get_fd(pa_asyncmsgq *a) {
+int pa_asyncmsgq_read_fd(pa_asyncmsgq *a) {
+ pa_assert(PA_REFCNT_VALUE(a) > 0);
+
+ return pa_asyncq_read_fd(a->asyncq);
+}
+
+int pa_asyncmsgq_read_before_poll(pa_asyncmsgq *a) {
+ pa_assert(PA_REFCNT_VALUE(a) > 0);
+
+ return pa_asyncq_read_before_poll(a->asyncq);
+}
+
+void pa_asyncmsgq_read_after_poll(pa_asyncmsgq *a) {
+ pa_assert(PA_REFCNT_VALUE(a) > 0);
+
+ pa_asyncq_read_after_poll(a->asyncq);
+}
+
+int pa_asyncmsgq_write_fd(pa_asyncmsgq *a) {
pa_assert(PA_REFCNT_VALUE(a) > 0);
- return pa_asyncq_get_fd(a->asyncq);
+ return pa_asyncq_write_fd(a->asyncq);
}
-int pa_asyncmsgq_before_poll(pa_asyncmsgq *a) {
+void pa_asyncmsgq_write_before_poll(pa_asyncmsgq *a) {
pa_assert(PA_REFCNT_VALUE(a) > 0);
- return pa_asyncq_before_poll(a->asyncq);
+ pa_asyncq_write_before_poll(a->asyncq);
}
-void pa_asyncmsgq_after_poll(pa_asyncmsgq *a) {
+void pa_asyncmsgq_write_after_poll(pa_asyncmsgq *a) {
pa_assert(PA_REFCNT_VALUE(a) > 0);
- pa_asyncq_after_poll(a->asyncq);
+ pa_asyncq_write_after_poll(a->asyncq);
}
int pa_asyncmsgq_dispatch(pa_msgobject *object, int code, void *userdata, int64_t offset, pa_memchunk *memchunk) {
diff --git a/src/pulsecore/asyncmsgq.h b/src/pulsecore/asyncmsgq.h
index 5d3867ba..1f38207a 100644
--- a/src/pulsecore/asyncmsgq.h
+++ b/src/pulsecore/asyncmsgq.h
@@ -1,8 +1,6 @@
#ifndef foopulseasyncmsgqhfoo
#define foopulseasyncmsgqhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -56,20 +54,26 @@ typedef struct pa_asyncmsgq pa_asyncmsgq;
pa_asyncmsgq* pa_asyncmsgq_new(unsigned size);
pa_asyncmsgq* pa_asyncmsgq_ref(pa_asyncmsgq *q);
+
void pa_asyncmsgq_unref(pa_asyncmsgq* q);
void pa_asyncmsgq_post(pa_asyncmsgq *q, pa_msgobject *object, int code, const void *userdata, int64_t offset, const pa_memchunk *memchunk, pa_free_cb_t userdata_free_cb);
int pa_asyncmsgq_send(pa_asyncmsgq *q, pa_msgobject *object, int code, const void *userdata, int64_t offset, const pa_memchunk *memchunk);
-int pa_asyncmsgq_get(pa_asyncmsgq *q, pa_msgobject **object, int *code, void **userdata, int64_t *offset, pa_memchunk *memchunk, int wait);
+int pa_asyncmsgq_get(pa_asyncmsgq *q, pa_msgobject **object, int *code, void **userdata, int64_t *offset, pa_memchunk *memchunk, pa_bool_t wait);
int pa_asyncmsgq_dispatch(pa_msgobject *object, int code, void *userdata, int64_t offset, pa_memchunk *memchunk);
void pa_asyncmsgq_done(pa_asyncmsgq *q, int ret);
int pa_asyncmsgq_wait_for(pa_asyncmsgq *a, int code);
int pa_asyncmsgq_process_one(pa_asyncmsgq *a);
-/* Just for the reading side */
-int pa_asyncmsgq_get_fd(pa_asyncmsgq *q);
-int pa_asyncmsgq_before_poll(pa_asyncmsgq *a);
-void pa_asyncmsgq_after_poll(pa_asyncmsgq *a);
+/* For the reading side */
+int pa_asyncmsgq_read_fd(pa_asyncmsgq *q);
+int pa_asyncmsgq_read_before_poll(pa_asyncmsgq *a);
+void pa_asyncmsgq_read_after_poll(pa_asyncmsgq *a);
+
+/* For the write side */
+int pa_asyncmsgq_write_fd(pa_asyncmsgq *q);
+void pa_asyncmsgq_write_before_poll(pa_asyncmsgq *a);
+void pa_asyncmsgq_write_after_poll(pa_asyncmsgq *a);
#endif
diff --git a/src/pulsecore/asyncq.c b/src/pulsecore/asyncq.c
index 22381248..03e9f0df 100644
--- a/src/pulsecore/asyncq.c
+++ b/src/pulsecore/asyncq.c
@@ -1,9 +1,7 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
- Copyright 2006 Lennart Poettering
+ Copyright 2006-2008 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
@@ -33,14 +31,16 @@
#include <pulsecore/thread.h>
#include <pulsecore/macro.h>
#include <pulsecore/core-util.h>
+#include <pulsecore/llist.h>
+#include <pulsecore/flist.h>
#include <pulse/xmalloc.h>
#include "asyncq.h"
#include "fdsem.h"
-#define ASYNCQ_SIZE 128
+#define ASYNCQ_SIZE 256
-/* For debugging purposes we can define _Y to put and extra thread
+/* For debugging purposes we can define _Y to put an extra thread
* yield between each operation. */
/* #define PROFILE */
@@ -51,18 +51,25 @@
#define _Y do { } while(0)
#endif
+struct localq {
+ void *data;
+ PA_LLIST_FIELDS(struct localq);
+};
+
struct pa_asyncq {
unsigned size;
unsigned read_idx;
unsigned write_idx;
pa_fdsem *read_fdsem, *write_fdsem;
+
+ PA_LLIST_HEAD(struct localq, localq);
+ struct localq *last_localq;
+ pa_bool_t waiting_for_post;
};
-#define PA_ASYNCQ_CELLS(x) ((pa_atomic_ptr_t*) ((uint8_t*) (x) + PA_ALIGN(sizeof(struct pa_asyncq))))
+PA_STATIC_FLIST_DECLARE(localq, 0, pa_xfree);
-static int is_power_of_two(unsigned size) {
- return !(size & (size - 1));
-}
+#define PA_ASYNCQ_CELLS(x) ((pa_atomic_ptr_t*) ((uint8_t*) (x) + PA_ALIGN(sizeof(struct pa_asyncq))))
static int reduce(pa_asyncq *l, int value) {
return value & (unsigned) (l->size - 1);
@@ -74,12 +81,16 @@ pa_asyncq *pa_asyncq_new(unsigned size) {
if (!size)
size = ASYNCQ_SIZE;
- pa_assert(is_power_of_two(size));
+ pa_assert(pa_is_power_of_two(size));
l = pa_xmalloc0(PA_ALIGN(sizeof(pa_asyncq)) + (sizeof(pa_atomic_ptr_t) * size));
l->size = size;
+ PA_LLIST_HEAD_INIT(struct localq, l->localq);
+ l->last_localq = NULL;
+ l->waiting_for_post = FALSE;
+
if (!(l->read_fdsem = pa_fdsem_new())) {
pa_xfree(l);
return NULL;
@@ -95,6 +106,7 @@ pa_asyncq *pa_asyncq_new(unsigned size) {
}
void pa_asyncq_free(pa_asyncq *l, pa_free_cb_t free_cb) {
+ struct localq *q;
pa_assert(l);
if (free_cb) {
@@ -104,12 +116,22 @@ void pa_asyncq_free(pa_asyncq *l, pa_free_cb_t free_cb) {
free_cb(p);
}
+ while ((q = l->localq)) {
+ if (free_cb)
+ free_cb(q->data);
+
+ PA_LLIST_REMOVE(struct localq, l->localq, q);
+
+ if (pa_flist_push(PA_STATIC_FLIST_GET(localq), q) < 0)
+ pa_xfree(q);
+ }
+
pa_fdsem_free(l->read_fdsem);
pa_fdsem_free(l->write_fdsem);
pa_xfree(l);
}
-int pa_asyncq_push(pa_asyncq*l, void *p, int wait) {
+static int push(pa_asyncq*l, void *p, pa_bool_t wait) {
int idx;
pa_atomic_ptr_t *cells;
@@ -127,7 +149,7 @@ int pa_asyncq_push(pa_asyncq*l, void *p, int wait) {
return -1;
/* pa_log("sleeping on push"); */
-
+
do {
pa_fdsem_wait(l->read_fdsem);
} while (!pa_atomic_ptr_cmpxchg(&cells[idx], NULL, p));
@@ -141,13 +163,69 @@ int pa_asyncq_push(pa_asyncq*l, void *p, int wait) {
return 0;
}
-void* pa_asyncq_pop(pa_asyncq*l, int wait) {
+static pa_bool_t flush_postq(pa_asyncq *l) {
+ struct localq *q;
+
+ pa_assert(l);
+
+ while ((q = l->last_localq)) {
+
+ if (push(l, q->data, FALSE) < 0)
+ return FALSE;
+
+ l->last_localq = q->prev;
+
+ PA_LLIST_REMOVE(struct localq, l->localq, q);
+
+ if (pa_flist_push(PA_STATIC_FLIST_GET(localq), q) < 0)
+ pa_xfree(q);
+ }
+
+ return TRUE;
+}
+
+int pa_asyncq_push(pa_asyncq*l, void *p, pa_bool_t wait) {
+ pa_assert(l);
+
+ if (!flush_postq(l))
+ return -1;
+
+ return push(l, p, wait);
+}
+
+void pa_asyncq_post(pa_asyncq*l, void *p) {
+ struct localq *q;
+
+ pa_assert(l);
+ pa_assert(p);
+
+ if (pa_asyncq_push(l, p, FALSE) >= 0)
+ return;
+
+ /* OK, we couldn't push anything in the queue. So let's queue it
+ * locally and push it later */
+
+ pa_log("q overrun, queuing locally");
+
+ if (!(q = pa_flist_pop(PA_STATIC_FLIST_GET(localq))))
+ q = pa_xnew(struct localq, 1);
+
+ q->data = p;
+ PA_LLIST_PREPEND(struct localq, l->localq, q);
+
+ if (!l->last_localq)
+ l->last_localq = q;
+
+ return;
+}
+
+void* pa_asyncq_pop(pa_asyncq*l, pa_bool_t wait) {
int idx;
void *ret;
pa_atomic_ptr_t *cells;
pa_assert(l);
-
+
cells = PA_ASYNCQ_CELLS(l);
_Y;
@@ -159,7 +237,7 @@ void* pa_asyncq_pop(pa_asyncq*l, int wait) {
return NULL;
/* pa_log("sleeping on pop"); */
-
+
do {
pa_fdsem_wait(l->write_fdsem);
} while (!(ret = pa_atomic_ptr_load(&cells[idx])));
@@ -174,17 +252,17 @@ void* pa_asyncq_pop(pa_asyncq*l, int wait) {
l->read_idx++;
pa_fdsem_post(l->read_fdsem);
-
+
return ret;
}
-int pa_asyncq_get_fd(pa_asyncq *q) {
+int pa_asyncq_read_fd(pa_asyncq *q) {
pa_assert(q);
return pa_fdsem_get(q->write_fdsem);
}
-int pa_asyncq_before_poll(pa_asyncq *l) {
+int pa_asyncq_read_before_poll(pa_asyncq *l) {
int idx;
pa_atomic_ptr_t *cells;
@@ -206,8 +284,38 @@ int pa_asyncq_before_poll(pa_asyncq *l) {
return 0;
}
-void pa_asyncq_after_poll(pa_asyncq *l) {
+void pa_asyncq_read_after_poll(pa_asyncq *l) {
pa_assert(l);
pa_fdsem_after_poll(l->write_fdsem);
}
+
+int pa_asyncq_write_fd(pa_asyncq *q) {
+ pa_assert(q);
+
+ return pa_fdsem_get(q->read_fdsem);
+}
+
+void pa_asyncq_write_before_poll(pa_asyncq *l) {
+ pa_assert(l);
+
+ for (;;) {
+
+ if (flush_postq(l))
+ break;
+
+ if (pa_fdsem_before_poll(l->read_fdsem) >= 0) {
+ l->waiting_for_post = TRUE;
+ break;
+ }
+ }
+}
+
+void pa_asyncq_write_after_poll(pa_asyncq *l) {
+ pa_assert(l);
+
+ if (l->waiting_for_post) {
+ pa_fdsem_after_poll(l->read_fdsem);
+ l->waiting_for_post = FALSE;
+ }
+}
diff --git a/src/pulsecore/asyncq.h b/src/pulsecore/asyncq.h
index 53d45866..e6847ab8 100644
--- a/src/pulsecore/asyncq.h
+++ b/src/pulsecore/asyncq.h
@@ -1,8 +1,6 @@
#ifndef foopulseasyncqhfoo
#define foopulseasyncqhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -26,6 +24,7 @@
#include <sys/types.h>
#include <pulse/def.h>
+#include <pulsecore/macro.h>
/* A simple, asynchronous, lock-free (if requested also wait-free)
* queue. Not multiple-reader/multiple-writer safe. If that is
@@ -46,11 +45,21 @@ typedef struct pa_asyncq pa_asyncq;
pa_asyncq* pa_asyncq_new(unsigned size);
void pa_asyncq_free(pa_asyncq* q, pa_free_cb_t free_cb);
-void* pa_asyncq_pop(pa_asyncq *q, int wait);
-int pa_asyncq_push(pa_asyncq *q, void *p, int wait);
+void* pa_asyncq_pop(pa_asyncq *q, pa_bool_t wait);
+int pa_asyncq_push(pa_asyncq *q, void *p, pa_bool_t wait);
+
+/* Similar to pa_asyncq_push(), but if the queue is full, postpone it
+ * locally and delay until pa_asyncq_before_poll_post() */
+void pa_asyncq_post(pa_asyncq*l, void *p);
+
+/* For the reading side */
+int pa_asyncq_read_fd(pa_asyncq *q);
+int pa_asyncq_read_before_poll(pa_asyncq *a);
+void pa_asyncq_read_after_poll(pa_asyncq *a);
-int pa_asyncq_get_fd(pa_asyncq *q);
-int pa_asyncq_before_poll(pa_asyncq *a);
-void pa_asyncq_after_poll(pa_asyncq *a);
+/* For the writing side */
+int pa_asyncq_write_fd(pa_asyncq *q);
+void pa_asyncq_write_before_poll(pa_asyncq *a);
+void pa_asyncq_write_after_poll(pa_asyncq *a);
#endif
diff --git a/src/pulsecore/atomic.h b/src/pulsecore/atomic.h
index 25d600c0..a91c4d56 100644
--- a/src/pulsecore/atomic.h
+++ b/src/pulsecore/atomic.h
@@ -1,12 +1,11 @@
#ifndef foopulseatomichfoo
#define foopulseatomichfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
- Copyright 2006 Lennart Poettering
+ Copyright 2006-2008 Lennart Poettering
+ Copyright 2008 Nokia Corporation
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
@@ -28,7 +27,7 @@
* atomic_ops guarantees us that sizeof(AO_t) == sizeof(void*). It is
* not guaranteed however, that sizeof(AO_t) == sizeof(size_t).
* however very likely.
- *
+ *
* For now we do only full memory barriers. Eventually we might want
* to support more elaborate memory barriers, in which case we will add
* suffixes to the function names.
@@ -36,7 +35,7 @@
* On gcc >= 4.1 we use the builtin atomic functions. otherwise we use
* libatomic_ops
*/
-
+#
#ifndef PACKAGE
#error "Please include config.h before including this file!"
#endif
@@ -128,7 +127,7 @@ static inline void pa_atomic_store(pa_atomic_t *a, int i) {
static inline int pa_atomic_add(pa_atomic_t *a, int i) {
int result;
-
+
__asm __volatile ("lock; xaddl %0, %1"
: "=r" (result), "=m" (a->value)
: "0" (i), "m" (a->value));
@@ -150,11 +149,11 @@ static inline int pa_atomic_dec(pa_atomic_t *a) {
static inline int pa_atomic_cmpxchg(pa_atomic_t *a, int old_i, int new_i) {
int result;
-
+
__asm__ __volatile__ ("lock; cmpxchgl %2, %1"
: "=a" (result), "=m" (a->value)
- : "r" (new_i), "m" (a->value), "0" (old_i));
-
+ : "r" (new_i), "m" (a->value), "0" (old_i));
+
return result == oldval;
}
@@ -174,14 +173,243 @@ static inline void pa_atomic_ptr_store(pa_atomic_ptr_t *a, void *p) {
static inline int pa_atomic_ptr_cmpxchg(pa_atomic_ptr_t *a, void *old_p, void* new_p) {
void *result;
-
+
__asm__ __volatile__ ("lock; cmpxchgq %q2, %1"
: "=a" (result), "=m" (a->value)
- : "r" (new_p), "m" (a->value), "0" (old_p));
+ : "r" (new_p), "m" (a->value), "0" (old_p));
return result;
}
+#elif defined(ATOMIC_ARM_INLINE_ASM)
+
+/*
+ These should only be enabled if we have ARMv6 or better.
+*/
+
+typedef struct pa_atomic {
+ volatile int value;
+} pa_atomic_t;
+
+#define PA_ATOMIC_INIT(v) { .value = (v) }
+
+static inline void pa_memory_barrier(void) {
+#ifdef ATOMIC_ARM_MEMORY_BARRIER_ENABLED
+ asm volatile ("mcr p15, 0, r0, c7, c10, 5 @ dmb");
+#endif
+}
+
+static inline int pa_atomic_load(const pa_atomic_t *a) {
+ pa_memory_barrier();
+ return a->value;
+}
+
+static inline void pa_atomic_store(pa_atomic_t *a, int i) {
+ a->value = i;
+ pa_memory_barrier();
+}
+
+/* Returns the previously set value */
+static inline int pa_atomic_add(pa_atomic_t *a, int i) {
+ unsigned long not_exclusive;
+ int new_val, old_val;
+
+ pa_memory_barrier();
+ do {
+ asm volatile ("ldrex %0, [%3]\n"
+ "add %2, %0, %4\n"
+ "strex %1, %2, [%3]\n"
+ : "=&r" (old_val), "=&r" (not_exclusive), "=&r" (new_val)
+ : "r" (&a->value), "Ir" (i)
+ : "cc");
+ } while(not_exclusive);
+ pa_memory_barrier();
+
+ return old_val;
+}
+
+/* Returns the previously set value */
+static inline int pa_atomic_sub(pa_atomic_t *a, int i) {
+ unsigned long not_exclusive;
+ int new_val, old_val;
+
+ pa_memory_barrier();
+ do {
+ asm volatile ("ldrex %0, [%3]\n"
+ "sub %2, %0, %4\n"
+ "strex %1, %2, [%3]\n"
+ : "=&r" (old_val), "=&r" (not_exclusive), "=&r" (new_val)
+ : "r" (&a->value), "Ir" (i)
+ : "cc");
+ } while(not_exclusive);
+ pa_memory_barrier();
+
+ return old_val;
+}
+
+static inline int pa_atomic_inc(pa_atomic_t *a) {
+ return pa_atomic_add(a, 1);
+}
+
+static inline int pa_atomic_dec(pa_atomic_t *a) {
+ return pa_atomic_sub(a, 1);
+}
+
+static inline int pa_atomic_cmpxchg(pa_atomic_t *a, int old_i, int new_i) {
+ unsigned long not_equal, not_exclusive;
+
+ pa_memory_barrier();
+ do {
+ asm volatile ("ldrex %0, [%2]\n"
+ "subs %0, %0, %3\n"
+ "mov %1, %0\n"
+ "strexeq %0, %4, [%2]\n"
+ : "=&r" (not_exclusive), "=&r" (not_equal)
+ : "r" (&a->value), "Ir" (old_i), "r" (new_i)
+ : "cc");
+ } while(not_exclusive && !not_equal);
+ pa_memory_barrier();
+
+ return !not_equal;
+}
+
+typedef struct pa_atomic_ptr {
+ volatile unsigned long value;
+} pa_atomic_ptr_t;
+
+#define PA_ATOMIC_PTR_INIT(v) { .value = (long) (v) }
+
+static inline void* pa_atomic_ptr_load(const pa_atomic_ptr_t *a) {
+ pa_memory_barrier();
+ return (void*) a->value;
+}
+
+static inline void pa_atomic_ptr_store(pa_atomic_ptr_t *a, void *p) {
+ a->value = (unsigned long) p;
+ pa_memory_barrier();
+}
+
+static inline int pa_atomic_ptr_cmpxchg(pa_atomic_ptr_t *a, void *old_p, void* new_p) {
+ unsigned long not_equal, not_exclusive;
+
+ pa_memory_barrier();
+ do {
+ asm volatile ("ldrex %0, [%2]\n"
+ "subs %0, %0, %3\n"
+ "mov %1, %0\n"
+ "strexeq %0, %4, [%2]\n"
+ : "=&r" (not_exclusive), "=&r" (not_equal)
+ : "r" (&a->value), "Ir" (old_p), "r" (new_p)
+ : "cc");
+ } while(not_exclusive && !not_equal);
+ pa_memory_barrier();
+
+ return !not_equal;
+}
+
+#elif defined(ATOMIC_ARM_LINUX_HELPERS)
+
+/* See file arch/arm/kernel/entry-armv.S in your kernel sources for more
+ information about these functions. The arm kernel helper functions first
+ appeared in 2.6.16.
+ Apply --disable-atomic-arm-linux-helpers flag to confugure if you prefere
+ inline asm implementation or you have an obsolete Linux kernel.
+*/
+/* Memory barrier */
+typedef void (__kernel_dmb_t)(void);
+#define __kernel_dmb (*(__kernel_dmb_t *)0xffff0fa0)
+
+static inline void pa_memory_barrier(void) {
+#ifndef ATOMIC_ARM_MEMORY_BARRIER_ENABLED
+ __kernel_dmb();
+#endif
+}
+
+/* Atomic exchange (__kernel_cmpxchg_t contains memory barriers if needed) */
+typedef int (__kernel_cmpxchg_t)(int oldval, int newval, volatile int *ptr);
+#define __kernel_cmpxchg (*(__kernel_cmpxchg_t *)0xffff0fc0)
+
+/* This is just to get rid of all warnings */
+typedef int (__kernel_cmpxchg_u_t)(unsigned long oldval, unsigned long newval, volatile unsigned long *ptr);
+#define __kernel_cmpxchg_u (*(__kernel_cmpxchg_u_t *)0xffff0fc0)
+
+typedef struct pa_atomic {
+ volatile int value;
+} pa_atomic_t;
+
+#define PA_ATOMIC_INIT(v) { .value = (v) }
+
+static inline int pa_atomic_load(const pa_atomic_t *a) {
+ pa_memory_barrier();
+ return a->value;
+}
+
+static inline void pa_atomic_store(pa_atomic_t *a, int i) {
+ a->value = i;
+ pa_memory_barrier();
+}
+
+/* Returns the previously set value */
+static inline int pa_atomic_add(pa_atomic_t *a, int i) {
+ int old_val;
+ do {
+ old_val = a->value;
+ } while(__kernel_cmpxchg(old_val, old_val + i, &a->value));
+ return old_val;
+}
+
+/* Returns the previously set value */
+static inline int pa_atomic_sub(pa_atomic_t *a, int i) {
+ int old_val;
+ do {
+ old_val = a->value;
+ } while(__kernel_cmpxchg(old_val, old_val - i, &a->value));
+ return old_val;
+}
+
+/* Returns the previously set value */
+static inline int pa_atomic_inc(pa_atomic_t *a) {
+ return pa_atomic_add(a, 1);
+}
+
+/* Returns the previously set value */
+static inline int pa_atomic_dec(pa_atomic_t *a) {
+ return pa_atomic_sub(a, 1);
+}
+
+/* Returns non-zero when the operation was successful. */
+static inline int pa_atomic_cmpxchg(pa_atomic_t *a, int old_i, int new_i) {
+ pa_bool_t failed;
+ do {
+ failed = !!__kernel_cmpxchg(old_i, new_i, &a->value);
+ } while(failed && a->value == old_i);
+ return !failed;
+}
+
+typedef struct pa_atomic_ptr {
+ volatile unsigned long value;
+} pa_atomic_ptr_t;
+
+#define PA_ATOMIC_PTR_INIT(v) { .value = (unsigned long) (v) }
+
+static inline void* pa_atomic_ptr_load(const pa_atomic_ptr_t *a) {
+ pa_memory_barrier();
+ return (void*) a->value;
+}
+
+static inline void pa_atomic_ptr_store(pa_atomic_ptr_t *a, void *p) {
+ a->value = (unsigned long) p;
+ pa_memory_barrier();
+}
+
+static inline int pa_atomic_ptr_cmpxchg(pa_atomic_ptr_t *a, void *old_p, void* new_p) {
+ pa_bool_t failed;
+ do {
+ failed = !!__kernel_cmpxchg_u((unsigned long) old_p, (unsigned long) new_p, &a->value);
+ } while(failed && a->value == (unsigned long) old_p);
+ return !failed;
+}
+
#else
/* libatomic_ops based implementation */
diff --git a/src/pulsecore/authkey-prop.c b/src/pulsecore/authkey-prop.c
index 179c16ca..a953bf7d 100644
--- a/src/pulsecore/authkey-prop.c
+++ b/src/pulsecore/authkey-prop.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -43,7 +41,7 @@ struct authkey_data {
int pa_authkey_prop_get(pa_core *c, const char *name, void *data, size_t len) {
struct authkey_data *a;
-
+
pa_assert(c);
pa_assert(name);
pa_assert(data);
@@ -54,13 +52,13 @@ int pa_authkey_prop_get(pa_core *c, const char *name, void *data, size_t len) {
pa_assert(a->length == len);
memcpy(data, (uint8_t*) a + PA_ALIGN(sizeof(struct authkey_data)), len);
-
+
return 0;
}
int pa_authkey_prop_put(pa_core *c, const char *name, const void *data, size_t len) {
struct authkey_data *a;
-
+
pa_assert(c);
pa_assert(name);
@@ -91,7 +89,7 @@ void pa_authkey_prop_ref(pa_core *c, const char *name) {
void pa_authkey_prop_unref(pa_core *c, const char *name) {
struct authkey_data *a;
-
+
pa_assert(c);
pa_assert(name);
diff --git a/src/pulsecore/authkey-prop.h b/src/pulsecore/authkey-prop.h
index 247202f3..de02d2aa 100644
--- a/src/pulsecore/authkey-prop.h
+++ b/src/pulsecore/authkey-prop.h
@@ -1,8 +1,6 @@
#ifndef fooauthkeyprophfoo
#define fooauthkeyprophfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/authkey.c b/src/pulsecore/authkey.c
index d422d6a2..f3f40f80 100644
--- a/src/pulsecore/authkey.c
+++ b/src/pulsecore/authkey.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -49,7 +47,7 @@
/* Generate a new authorization key, store it in file fd and return it in *data */
static int generate(int fd, void *ret_data, size_t length) {
ssize_t r;
-
+
pa_assert(fd >= 0);
pa_assert(ret_data);
pa_assert(length > 0);
@@ -82,13 +80,13 @@ static int load(const char *fn, void *data, size_t length) {
int writable = 1;
int unlock = 0, ret = -1;
ssize_t r;
-
+
pa_assert(fn);
pa_assert(data);
pa_assert(length > 0);
if ((fd = open(fn, O_RDWR|O_CREAT|O_BINARY|O_NOCTTY, S_IRUSR|S_IWUSR)) < 0) {
-
+
if (errno != EACCES || (fd = open(fn, O_RDONLY|O_BINARY|O_NOCTTY)) < 0) {
pa_log("Failed to open cookie file '%s': %s", fn, pa_cstrerror(errno));
goto finish;
@@ -161,7 +159,7 @@ static const char *normalize_path(const char *fn, char *s, size_t l) {
if (strlen(fn) < 3 || !isalpha(fn[0]) || fn[1] != ':' || fn[2] != '\\') {
#endif
char homedir[PATH_MAX];
-
+
if (!pa_get_home_dir(homedir, sizeof(homedir)))
return NULL;
@@ -181,7 +179,7 @@ static const char *normalize_path(const char *fn, char *s, size_t l) {
int pa_authkey_load_auto(const char *fn, void *data, size_t length) {
char path[PATH_MAX];
const char *p;
-
+
pa_assert(fn);
pa_assert(data);
pa_assert(length > 0);
@@ -199,7 +197,7 @@ int pa_authkey_save(const char *fn, const void *data, size_t length) {
ssize_t r;
char path[PATH_MAX];
const char *p;
-
+
pa_assert(fn);
pa_assert(data);
pa_assert(length > 0);
diff --git a/src/pulsecore/authkey.h b/src/pulsecore/authkey.h
index 18e5157d..8301db1e 100644
--- a/src/pulsecore/authkey.h
+++ b/src/pulsecore/authkey.h
@@ -1,8 +1,6 @@
#ifndef fooauthkeyhfoo
#define fooauthkeyhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/autoload.c b/src/pulsecore/autoload.c
index 0caaa295..26c294b2 100644
--- a/src/pulsecore/autoload.c
+++ b/src/pulsecore/autoload.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -61,7 +59,7 @@ static void entry_remove_and_free(pa_autoload_entry *e) {
static pa_autoload_entry* entry_new(pa_core *c, const char *name) {
pa_autoload_entry *e = NULL;
-
+
pa_core_assert_ref(c);
pa_assert(name);
@@ -91,7 +89,7 @@ static pa_autoload_entry* entry_new(pa_core *c, const char *name) {
int pa_autoload_add(pa_core *c, const char*name, pa_namereg_type_t type, const char*module, const char *argument, uint32_t *idx) {
pa_autoload_entry *e = NULL;
-
+
pa_assert(c);
pa_assert(name);
pa_assert(module);
@@ -112,7 +110,7 @@ int pa_autoload_add(pa_core *c, const char*name, pa_namereg_type_t type, const c
int pa_autoload_remove_by_name(pa_core *c, const char*name, pa_namereg_type_t type) {
pa_autoload_entry *e;
-
+
pa_assert(c);
pa_assert(name);
pa_assert(type == PA_NAMEREG_SINK || type == PA_NAMEREG_SOURCE);
@@ -126,7 +124,7 @@ int pa_autoload_remove_by_name(pa_core *c, const char*name, pa_namereg_type_t ty
int pa_autoload_remove_by_index(pa_core *c, uint32_t idx) {
pa_autoload_entry *e;
-
+
pa_assert(c);
pa_assert(idx != PA_IDXSET_INVALID);
@@ -140,7 +138,7 @@ int pa_autoload_remove_by_index(pa_core *c, uint32_t idx) {
void pa_autoload_request(pa_core *c, const char *name, pa_namereg_type_t type) {
pa_autoload_entry *e;
pa_module *m;
-
+
pa_assert(c);
pa_assert(name);
@@ -167,7 +165,7 @@ static void free_func(void *p, PA_GCC_UNUSED void *userdata) {
}
void pa_autoload_free(pa_core *c) {
-
+
if (c->autoload_hashmap) {
pa_hashmap_free(c->autoload_hashmap, free_func, NULL);
c->autoload_hashmap = NULL;
diff --git a/src/pulsecore/autoload.h b/src/pulsecore/autoload.h
index 2899586c..3926351f 100644
--- a/src/pulsecore/autoload.h
+++ b/src/pulsecore/autoload.h
@@ -1,8 +1,6 @@
#ifndef fooautoloadhfoo
#define fooautoloadhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -37,7 +35,7 @@ typedef struct pa_autoload_entry {
uint32_t index;
char *name;
pa_namereg_type_t type; /* Type of the autoload entry */
- int in_action; /* Currently loaded */
+ int in_action; /* The module is currently being loaded */
char *module, *argument;
} pa_autoload_entry;
diff --git a/src/pulsecore/avahi-wrap.c b/src/pulsecore/avahi-wrap.c
index fae54810..d5f40d83 100644
--- a/src/pulsecore/avahi-wrap.c
+++ b/src/pulsecore/avahi-wrap.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/avahi-wrap.h b/src/pulsecore/avahi-wrap.h
index 1e20ec38..7d8995bb 100644
--- a/src/pulsecore/avahi-wrap.h
+++ b/src/pulsecore/avahi-wrap.h
@@ -1,8 +1,6 @@
#ifndef fooavahiwrapperhfoo
#define fooavahiwrapperhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/cli-command.c b/src/pulsecore/cli-command.c
index 2e674cc2..cbdb602a 100644
--- a/src/pulsecore/cli-command.c
+++ b/src/pulsecore/cli-command.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -31,6 +29,7 @@
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
+#include <ltdl.h>
#include <pulse/xmalloc.h>
@@ -58,7 +57,7 @@
struct command {
const char *name;
- int (*proc) (pa_core *c, pa_tokenizer*t, pa_strbuf *buf, int *fail);
+ int (*proc) (pa_core *c, pa_tokenizer*t, pa_strbuf *buf, pa_bool_t *fail);
const char *help;
unsigned args;
};
@@ -77,46 +76,47 @@ enum {
};
/* Prototypes for all available commands */
-static int pa_cli_command_exit(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_help(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_modules(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_clients(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_sinks(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_sources(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_sink_inputs(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_source_outputs(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_stat(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_info(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_load(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_unload(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_sink_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_sink_input_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_source_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_sink_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_source_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_sink_input_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_sink_default(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_source_default(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_kill_client(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_kill_sink_input(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_kill_source_output(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_scache_play(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_scache_remove(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_scache_list(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_scache_load(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_scache_load_dir(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_play_file(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_autoload_list(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_autoload_add(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_autoload_remove(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_list_props(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_move_sink_input(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_move_source_output(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_vacuum(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_suspend_sink(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_suspend_source(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
-static int pa_cli_command_suspend(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
+static int pa_cli_command_exit(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_help(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_modules(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_clients(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_sinks(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_sources(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_sink_inputs(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_source_outputs(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_stat(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_info(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_load(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_unload(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_describe(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_sink_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_sink_input_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_source_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_sink_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_source_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_sink_input_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_sink_default(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_source_default(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_kill_client(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_kill_sink_input(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_kill_source_output(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_scache_play(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_scache_remove(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_scache_list(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_scache_load(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_scache_load_dir(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_play_file(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_autoload_list(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_autoload_add(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_autoload_remove(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_list_props(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_move_sink_input(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_move_source_output(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_vacuum(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_suspend_sink(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_suspend_source(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_suspend(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
/* A method table for all available commands */
@@ -135,6 +135,7 @@ static const struct command commands[] = {
{ "list", pa_cli_command_info, NULL, 1 },
{ "load-module", pa_cli_command_load, "Load a module (args: name, arguments)", 3},
{ "unload-module", pa_cli_command_unload, "Unload a module (args: index)", 2},
+ { "describe-module", pa_cli_command_describe, "Describe a module (arg: name)", 2},
{ "set-sink-volume", pa_cli_command_sink_volume, "Set the volume of a sink (args: index|name, volume)", 3},
{ "set-sink-input-volume", pa_cli_command_sink_input_volume, "Set the volume of a sink input (args: index, volume)", 3},
{ "set-source-volume", pa_cli_command_source_volume, "Set the volume of a source (args: index|name, volume)", 3},
@@ -154,10 +155,10 @@ static const struct command commands[] = {
{ "load-sample-dir-lazy", pa_cli_command_scache_load_dir, "Lazily load all files in a directory into the sample cache (args: pathname)", 2},
{ "play-file", pa_cli_command_play_file, "Play a sound file (args: filename, sink|index)", 3},
{ "list-autoload", pa_cli_command_autoload_list, "List autoload entries", 1},
- { "add-autoload-sink", pa_cli_command_autoload_add, "Add autoload entry for a sink (args: sink, module name, arguments)", 4},
- { "add-autoload-source", pa_cli_command_autoload_add, "Add autoload entry for a source (args: source, module name, arguments)", 4},
- { "remove-autoload-sink", pa_cli_command_autoload_remove, "Remove autoload entry for a sink (args: name)", 2},
- { "remove-autoload-source", pa_cli_command_autoload_remove, "Remove autoload entry for a source (args: name)", 2},
+ { "add-autoload-sink", pa_cli_command_autoload_add, NULL /*"Add autoload entry for a sink (args: sink, module name, arguments)"*/, 4},
+ { "add-autoload-source", pa_cli_command_autoload_add, NULL /*"Add autoload entry for a source (args: source, module name, arguments)"*/, 4},
+ { "remove-autoload-sink", pa_cli_command_autoload_remove, NULL /*"Remove autoload entry for a sink (args: name)"*/, 2},
+ { "remove-autoload-source", pa_cli_command_autoload_remove, NULL /*"Remove autoload entry for a source (args: name)"*/, 2},
{ "dump", pa_cli_command_dump, "Dump daemon configuration", 1},
{ "list-props", pa_cli_command_list_props, NULL, 1},
{ "move-sink-input", pa_cli_command_move_sink_input, "Move sink input to another sink (args: index, sink)", 3},
@@ -181,7 +182,7 @@ static uint32_t parse_index(const char *n) {
return idx;
}
-static int pa_cli_command_exit(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
+static int pa_cli_command_exit(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
pa_core_assert_ref(c);
pa_assert(t);
pa_assert(buf);
@@ -191,7 +192,7 @@ static int pa_cli_command_exit(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int
return 0;
}
-static int pa_cli_command_help(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
+static int pa_cli_command_help(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
const struct command*command;
pa_core_assert_ref(c);
@@ -207,7 +208,7 @@ static int pa_cli_command_help(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int
return 0;
}
-static int pa_cli_command_modules(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
+static int pa_cli_command_modules(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
char *s;
pa_core_assert_ref(c);
@@ -221,7 +222,7 @@ static int pa_cli_command_modules(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, i
return 0;
}
-static int pa_cli_command_clients(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
+static int pa_cli_command_clients(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
char *s;
pa_core_assert_ref(c);
@@ -235,7 +236,7 @@ static int pa_cli_command_clients(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, i
return 0;
}
-static int pa_cli_command_sinks(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
+static int pa_cli_command_sinks(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
char *s;
pa_core_assert_ref(c);
@@ -249,7 +250,7 @@ static int pa_cli_command_sinks(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int
return 0;
}
-static int pa_cli_command_sources(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
+static int pa_cli_command_sources(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
char *s;
pa_core_assert_ref(c);
@@ -263,7 +264,7 @@ static int pa_cli_command_sources(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, i
return 0;
}
-static int pa_cli_command_sink_inputs(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
+static int pa_cli_command_sink_inputs(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
char *s;
pa_core_assert_ref(c);
@@ -277,7 +278,7 @@ static int pa_cli_command_sink_inputs(pa_core *c, pa_tokenizer *t, pa_strbuf *bu
return 0;
}
-static int pa_cli_command_source_outputs(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
+static int pa_cli_command_source_outputs(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
char *s;
pa_core_assert_ref(c);
@@ -291,7 +292,7 @@ static int pa_cli_command_source_outputs(pa_core *c, pa_tokenizer *t, pa_strbuf
return 0;
}
-static int pa_cli_command_stat(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
+static int pa_cli_command_stat(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
char s[256];
const pa_mempool_stat *stat;
unsigned k;
@@ -352,7 +353,7 @@ static int pa_cli_command_stat(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int
return 0;
}
-static int pa_cli_command_info(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
+static int pa_cli_command_info(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
pa_core_assert_ref(c);
pa_assert(t);
pa_assert(buf);
@@ -366,14 +367,14 @@ static int pa_cli_command_info(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int
pa_cli_command_sink_inputs(c, t, buf, fail);
pa_cli_command_source_outputs(c, t, buf, fail);
pa_cli_command_scache_list(c, t, buf, fail);
- pa_cli_command_autoload_list(c, t, buf, fail);
+/* pa_cli_command_autoload_list(c, t, buf, fail); */
return 0;
}
-static int pa_cli_command_load(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
+static int pa_cli_command_load(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
pa_module *m;
const char *name;
-
+
pa_core_assert_ref(c);
pa_assert(t);
pa_assert(buf);
@@ -392,12 +393,12 @@ static int pa_cli_command_load(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int
return 0;
}
-static int pa_cli_command_unload(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
+static int pa_cli_command_unload(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
pa_module *m;
uint32_t idx;
const char *i;
char *e;
-
+
pa_core_assert_ref(c);
pa_assert(t);
pa_assert(buf);
@@ -418,7 +419,46 @@ static int pa_cli_command_unload(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, in
return 0;
}
-static int pa_cli_command_sink_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
+static int pa_cli_command_describe(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
+ const char *name;
+ pa_modinfo *i;
+
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
+
+ if (!(name = pa_tokenizer_get(t, 1))) {
+ pa_strbuf_puts(buf, "You need to specify the module name.\n");
+ return -1;
+ }
+
+ if ((i = pa_modinfo_get_by_name(name))) {
+
+ pa_strbuf_printf(buf, "Name: %s\n", name);
+
+ if (!i->description && !i->version && !i->author && !i->usage)
+ pa_strbuf_printf(buf, "No module information available\n");
+ else {
+ if (i->version)
+ pa_strbuf_printf(buf, "Version: %s\n", i->version);
+ if (i->description)
+ pa_strbuf_printf(buf, "Description: %s\n", i->description);
+ if (i->author)
+ pa_strbuf_printf(buf, "Author: %s\n", i->author);
+ 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));
+ }
+
+ pa_modinfo_free(i);
+ } else
+ pa_strbuf_puts(buf, "Failed to open module.\n");
+
+ return 0;
+}
+
+static int pa_cli_command_sink_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
const char *n, *v;
pa_sink *sink;
uint32_t volume;
@@ -435,7 +475,7 @@ static int pa_cli_command_sink_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *bu
}
if (!(v = pa_tokenizer_get(t, 2))) {
- pa_strbuf_puts(buf, "You need to specify a volume >= 0. (0 is muted, 0x100 is normal volume)\n");
+ pa_strbuf_puts(buf, "You need to specify a volume >= 0. (0 is muted, 0x10000 is normal volume)\n");
return -1;
}
@@ -454,7 +494,7 @@ static int pa_cli_command_sink_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *bu
return 0;
}
-static int pa_cli_command_sink_input_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
+static int pa_cli_command_sink_input_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
const char *n, *v;
pa_sink_input *si;
pa_volume_t volume;
@@ -477,7 +517,7 @@ static int pa_cli_command_sink_input_volume(pa_core *c, pa_tokenizer *t, pa_strb
}
if (!(v = pa_tokenizer_get(t, 2))) {
- pa_strbuf_puts(buf, "You need to specify a volume >= 0. (0 is muted, 0x100 is normal volume)\n");
+ pa_strbuf_puts(buf, "You need to specify a volume >= 0. (0 is muted, 0x10000 is normal volume)\n");
return -1;
}
@@ -496,7 +536,7 @@ static int pa_cli_command_sink_input_volume(pa_core *c, pa_tokenizer *t, pa_strb
return 0;
}
-static int pa_cli_command_source_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
+static int pa_cli_command_source_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
const char *n, *v;
pa_source *source;
uint32_t volume;
@@ -513,7 +553,7 @@ static int pa_cli_command_source_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *
}
if (!(v = pa_tokenizer_get(t, 2))) {
- pa_strbuf_puts(buf, "You need to specify a volume >= 0. (0 is muted, 0x100 is normal volume)\n");
+ pa_strbuf_puts(buf, "You need to specify a volume >= 0. (0 is muted, 0x10000 is normal volume)\n");
return -1;
}
@@ -532,7 +572,7 @@ static int pa_cli_command_source_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *
return 0;
}
-static int pa_cli_command_sink_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
+static int pa_cli_command_sink_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
const char *n, *m;
pa_sink *sink;
int mute;
@@ -552,7 +592,7 @@ static int pa_cli_command_sink_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf,
return -1;
}
- if (pa_atoi(m, &mute) < 0) {
+ if ((mute = pa_parse_boolean(m)) < 0) {
pa_strbuf_puts(buf, "Failed to parse mute switch.\n");
return -1;
}
@@ -566,7 +606,7 @@ static int pa_cli_command_sink_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf,
return 0;
}
-static int pa_cli_command_source_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
+static int pa_cli_command_source_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
const char *n, *m;
pa_source *source;
int mute;
@@ -586,7 +626,7 @@ static int pa_cli_command_source_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *bu
return -1;
}
- if (pa_atoi(m, &mute) < 0) {
+ if ((mute = pa_parse_boolean(m)) < 0) {
pa_strbuf_puts(buf, "Failed to parse mute switch.\n");
return -1;
}
@@ -600,7 +640,7 @@ static int pa_cli_command_source_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *bu
return 0;
}
-static int pa_cli_command_sink_input_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
+static int pa_cli_command_sink_input_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
const char *n, *v;
pa_sink_input *si;
uint32_t idx;
@@ -622,11 +662,11 @@ static int pa_cli_command_sink_input_mute(pa_core *c, pa_tokenizer *t, pa_strbuf
}
if (!(v = pa_tokenizer_get(t, 2))) {
- pa_strbuf_puts(buf, "You need to specify a volume >= 0. (0 is muted, 0x100 is normal volume)\n");
+ pa_strbuf_puts(buf, "You need to specify a mute switch setting (0/1).\n");
return -1;
}
- if (pa_atoi(v, &mute) < 0) {
+ if ((mute = pa_parse_boolean(v)) < 0) {
pa_strbuf_puts(buf, "Failed to parse mute switch.\n");
return -1;
}
@@ -640,7 +680,7 @@ static int pa_cli_command_sink_input_mute(pa_core *c, pa_tokenizer *t, pa_strbuf
return 0;
}
-static int pa_cli_command_sink_default(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
+static int pa_cli_command_sink_default(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
const char *n;
pa_core_assert_ref(c);
@@ -657,7 +697,7 @@ static int pa_cli_command_sink_default(pa_core *c, pa_tokenizer *t, pa_strbuf *b
return 0;
}
-static int pa_cli_command_source_default(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
+static int pa_cli_command_source_default(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
const char *n;
pa_core_assert_ref(c);
@@ -674,7 +714,7 @@ static int pa_cli_command_source_default(pa_core *c, pa_tokenizer *t, pa_strbuf
return 0;
}
-static int pa_cli_command_kill_client(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
+static int pa_cli_command_kill_client(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
const char *n;
pa_client *client;
uint32_t idx;
@@ -703,7 +743,7 @@ static int pa_cli_command_kill_client(pa_core *c, pa_tokenizer *t, pa_strbuf *bu
return 0;
}
-static int pa_cli_command_kill_sink_input(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
+static int pa_cli_command_kill_sink_input(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
const char *n;
pa_sink_input *sink_input;
uint32_t idx;
@@ -732,7 +772,7 @@ static int pa_cli_command_kill_sink_input(pa_core *c, pa_tokenizer *t, pa_strbuf
return 0;
}
-static int pa_cli_command_kill_source_output(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
+static int pa_cli_command_kill_source_output(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
const char *n;
pa_source_output *source_output;
uint32_t idx;
@@ -761,7 +801,7 @@ static int pa_cli_command_kill_source_output(pa_core *c, pa_tokenizer *t, pa_str
return 0;
}
-static int pa_cli_command_scache_list(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
+static int pa_cli_command_scache_list(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
char *s;
pa_core_assert_ref(c);
@@ -772,13 +812,14 @@ static int pa_cli_command_scache_list(pa_core *c, pa_tokenizer *t, pa_strbuf *bu
pa_assert_se(s = pa_scache_list_to_string(c));
pa_strbuf_puts(buf, s);
pa_xfree(s);
-
+
return 0;
}
-static int pa_cli_command_scache_play(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
+static int pa_cli_command_scache_play(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
const char *n, *sink_name;
pa_sink *sink;
+ uint32_t idx;
pa_core_assert_ref(c);
pa_assert(t);
@@ -795,15 +836,17 @@ static int pa_cli_command_scache_play(pa_core *c, pa_tokenizer *t, pa_strbuf *bu
return -1;
}
- if (pa_scache_play_item(c, n, sink, PA_VOLUME_NORM) < 0) {
+ if (pa_scache_play_item(c, n, sink, PA_VOLUME_NORM, NULL, &idx) < 0) {
pa_strbuf_puts(buf, "Failed to play sample.\n");
return -1;
}
+ pa_strbuf_printf(buf, "Playing on sink input #%i\n", idx);
+
return 0;
}
-static int pa_cli_command_scache_remove(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
+static int pa_cli_command_scache_remove(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
const char *n;
pa_core_assert_ref(c);
@@ -824,7 +867,7 @@ static int pa_cli_command_scache_remove(pa_core *c, pa_tokenizer *t, pa_strbuf *
return 0;
}
-static int pa_cli_command_scache_load(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
+static int pa_cli_command_scache_load(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
const char *fname, *n;
int r;
@@ -849,7 +892,7 @@ static int pa_cli_command_scache_load(pa_core *c, pa_tokenizer *t, pa_strbuf *bu
return 0;
}
-static int pa_cli_command_scache_load_dir(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
+static int pa_cli_command_scache_load_dir(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
const char *pname;
pa_core_assert_ref(c);
@@ -870,10 +913,10 @@ static int pa_cli_command_scache_load_dir(pa_core *c, pa_tokenizer *t, pa_strbuf
return 0;
}
-static int pa_cli_command_play_file(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
+static int pa_cli_command_play_file(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
const char *fname, *sink_name;
pa_sink *sink;
-
+
pa_core_assert_ref(c);
pa_assert(t);
pa_assert(buf);
@@ -893,7 +936,7 @@ static int pa_cli_command_play_file(pa_core *c, pa_tokenizer *t, pa_strbuf *buf,
return pa_play_file(sink, fname, NULL);
}
-static int pa_cli_command_autoload_add(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
+static int pa_cli_command_autoload_add(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
const char *a, *b;
pa_core_assert_ref(c);
@@ -901,6 +944,8 @@ static int pa_cli_command_autoload_add(pa_core *c, pa_tokenizer *t, pa_strbuf *b
pa_assert(buf);
pa_assert(fail);
+ pa_log_warn("Autoload will no longer be implemented by future versions of the PulseAudio server.");
+
if (!(a = pa_tokenizer_get(t, 1)) || !(b = pa_tokenizer_get(t, 2))) {
pa_strbuf_puts(buf, "You need to specify a device name, a filename or a module name and optionally module arguments\n");
return -1;
@@ -911,7 +956,7 @@ static int pa_cli_command_autoload_add(pa_core *c, pa_tokenizer *t, pa_strbuf *b
return 0;
}
-static int pa_cli_command_autoload_remove(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
+static int pa_cli_command_autoload_remove(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
const char *name;
pa_core_assert_ref(c);
@@ -919,6 +964,8 @@ static int pa_cli_command_autoload_remove(pa_core *c, pa_tokenizer *t, pa_strbuf
pa_assert(buf);
pa_assert(fail);
+ pa_log_warn("Autoload will no longer be implemented by future versions of the PulseAudio server.");
+
if (!(name = pa_tokenizer_get(t, 1))) {
pa_strbuf_puts(buf, "You need to specify a device name\n");
return -1;
@@ -932,7 +979,7 @@ static int pa_cli_command_autoload_remove(pa_core *c, pa_tokenizer *t, pa_strbuf
return 0;
}
-static int pa_cli_command_autoload_list(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
+static int pa_cli_command_autoload_list(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
char *s;
pa_core_assert_ref(c);
@@ -940,14 +987,16 @@ static int pa_cli_command_autoload_list(pa_core *c, pa_tokenizer *t, pa_strbuf *
pa_assert(buf);
pa_assert(fail);
+ pa_log_warn("Autoload will no longer be implemented by future versions of the PulseAudio server.");
+
pa_assert_se(s = pa_autoload_list_to_string(c));
pa_strbuf_puts(buf, s);
pa_xfree(s);
-
+
return 0;
}
-static int pa_cli_command_list_props(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
+static int pa_cli_command_list_props(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
pa_core_assert_ref(c);
pa_assert(t);
pa_assert(buf);
@@ -957,7 +1006,7 @@ static int pa_cli_command_list_props(pa_core *c, pa_tokenizer *t, pa_strbuf *buf
return 0;
}
-static int pa_cli_command_vacuum(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
+static int pa_cli_command_vacuum(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
pa_core_assert_ref(c);
pa_assert(t);
pa_assert(buf);
@@ -968,7 +1017,7 @@ static int pa_cli_command_vacuum(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, in
return 0;
}
-static int pa_cli_command_move_sink_input(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
+static int pa_cli_command_move_sink_input(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
const char *n, *k;
pa_sink_input *si;
pa_sink *sink;
@@ -1004,14 +1053,14 @@ static int pa_cli_command_move_sink_input(pa_core *c, pa_tokenizer *t, pa_strbuf
return -1;
}
- if (pa_sink_input_move_to(si, sink, 0) < 0) {
+ if (pa_sink_input_move_to(si, sink) < 0) {
pa_strbuf_puts(buf, "Moved failed.\n");
return -1;
}
return 0;
}
-static int pa_cli_command_move_source_output(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
+static int pa_cli_command_move_source_output(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
const char *n, *k;
pa_source_output *so;
pa_source *source;
@@ -1054,7 +1103,7 @@ static int pa_cli_command_move_source_output(pa_core *c, pa_tokenizer *t, pa_str
return 0;
}
-static int pa_cli_command_suspend_sink(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
+static int pa_cli_command_suspend_sink(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
const char *n, *m;
pa_sink *sink;
int suspend;
@@ -1074,7 +1123,7 @@ static int pa_cli_command_suspend_sink(pa_core *c, pa_tokenizer *t, pa_strbuf *b
return -1;
}
- if (pa_atoi(m, &suspend) < 0) {
+ if ((suspend = pa_parse_boolean(m)) < 0) {
pa_strbuf_puts(buf, "Failed to parse suspend switch.\n");
return -1;
}
@@ -1088,7 +1137,7 @@ static int pa_cli_command_suspend_sink(pa_core *c, pa_tokenizer *t, pa_strbuf *b
return 0;
}
-static int pa_cli_command_suspend_source(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
+static int pa_cli_command_suspend_source(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
const char *n, *m;
pa_source *source;
int suspend;
@@ -1108,7 +1157,7 @@ static int pa_cli_command_suspend_source(pa_core *c, pa_tokenizer *t, pa_strbuf
return -1;
}
- if (pa_atoi(m, &suspend) < 0) {
+ if ((suspend = pa_parse_boolean(m)) < 0) {
pa_strbuf_puts(buf, "Failed to parse suspend switch.\n");
return -1;
}
@@ -1122,7 +1171,7 @@ static int pa_cli_command_suspend_source(pa_core *c, pa_tokenizer *t, pa_strbuf
return 0;
}
-static int pa_cli_command_suspend(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
+static int pa_cli_command_suspend(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
const char *m;
int suspend;
int ret;
@@ -1137,7 +1186,7 @@ static int pa_cli_command_suspend(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, i
return -1;
}
- if (pa_atoi(m, &suspend) < 0) {
+ if ((suspend = pa_parse_boolean(m)) < 0) {
pa_strbuf_puts(buf, "Failed to parse suspend switch.\n");
return -1;
}
@@ -1148,11 +1197,11 @@ static int pa_cli_command_suspend(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, i
if (ret < 0)
pa_strbuf_puts(buf, "Failed to resume/suspend all sinks/sources.\n");
-
+
return 0;
}
-static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
+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;
pa_source *source;
@@ -1201,7 +1250,8 @@ static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int
}
pa_strbuf_printf(buf, "set-sink-volume %s 0x%03x\n", sink->name, pa_cvolume_avg(pa_sink_get_volume(sink)));
- pa_strbuf_printf(buf, "set-sink-mute %s %d\n", sink->name, pa_sink_get_mute(sink));
+ pa_strbuf_printf(buf, "set-sink-mute %s %s\n", sink->name, pa_yes_no(pa_sink_get_mute(sink)));
+ pa_strbuf_printf(buf, "suspend-sink %s %s\n", sink->name, pa_yes_no(pa_sink_get_state(sink) == PA_SINK_SUSPENDED));
}
for (source = pa_idxset_first(c->sources, &idx); source; source = pa_idxset_next(c->sources, &idx)) {
@@ -1214,7 +1264,8 @@ static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int
}
pa_strbuf_printf(buf, "set-source-volume %s 0x%03x\n", source->name, pa_cvolume_avg(pa_source_get_volume(source)));
- pa_strbuf_printf(buf, "set-source-mute %s %d\n", source->name, pa_source_get_mute(source));
+ pa_strbuf_printf(buf, "set-source-mute %s %s\n", source->name, pa_yes_no(pa_source_get_mute(source)));
+ pa_strbuf_printf(buf, "suspend-source %s %s\n", source->name, pa_yes_no(pa_source_get_state(source) == PA_SOURCE_SUSPENDED));
}
@@ -1261,7 +1312,7 @@ static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int
return 0;
}
-int pa_cli_command_execute_line_stateful(pa_core *c, const char *s, pa_strbuf *buf, int *fail, int *ifstate) {
+int pa_cli_command_execute_line_stateful(pa_core *c, const char *s, pa_strbuf *buf, pa_bool_t *fail, int *ifstate) {
const char *cs;
pa_assert(c);
@@ -1293,9 +1344,9 @@ int pa_cli_command_execute_line_stateful(pa_core *c, const char *s, pa_strbuf *b
if (ifstate && *ifstate == IFSTATE_FALSE)
return 0;
if (!strcmp(cs, META_FAIL))
- *fail = 1;
+ *fail = TRUE;
else if (!strcmp(cs, META_NOFAIL))
- *fail = 0;
+ *fail = FALSE;
else {
size_t l;
l = strcspn(cs, whitespace);
@@ -1315,8 +1366,35 @@ int pa_cli_command_execute_line_stateful(pa_core *c, const char *s, pa_strbuf *b
} else {
const char *filename = cs+l+strspn(cs+l, whitespace);
- *ifstate = access(filename, F_OK) == 0 ? IFSTATE_TRUE : IFSTATE_FALSE;
- pa_log_debug("Checking for existance of '%s': %s", filename, *ifstate == IFSTATE_TRUE ? "success" : "failure");
+ /* Search DL_SEARCH_PATH unless the filename is absolute */
+ if (filename[0] == PA_PATH_SEP_CHAR) {
+
+ *ifstate = access(filename, F_OK) == 0 ? IFSTATE_TRUE : IFSTATE_FALSE;
+ pa_log_debug("Checking for existance of '%s': %s", filename, *ifstate == IFSTATE_TRUE ? "success" : "failure");
+
+ } else {
+ const char *paths, *state = NULL;
+ char *p;
+
+ if (!(paths = lt_dlgetsearchpath()))
+ return -1;
+
+ while ((p = pa_split(paths, ":", &state))) {
+ char *pathname;
+
+ pathname = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", p, filename);
+ pa_xfree(p);
+
+ *ifstate = access(pathname, F_OK) == 0 ? IFSTATE_TRUE : IFSTATE_FALSE;
+ pa_log_debug("Checking for existance of '%s': %s", pathname, *ifstate == IFSTATE_TRUE ? "success" : "failure");
+
+ pa_xfree(pathname);
+
+ if (*ifstate == IFSTATE_TRUE)
+ break;
+ }
+ }
+
}
} else {
pa_strbuf_printf(buf, "Invalid meta command: %s\n", cs);
@@ -1358,20 +1436,49 @@ int pa_cli_command_execute_line_stateful(pa_core *c, const char *s, pa_strbuf *b
return 0;
}
-int pa_cli_command_execute_line(pa_core *c, const char *s, pa_strbuf *buf, int *fail) {
+int pa_cli_command_execute_line(pa_core *c, const char *s, pa_strbuf *buf, pa_bool_t *fail) {
return pa_cli_command_execute_line_stateful(c, s, buf, fail, NULL);
}
-int pa_cli_command_execute_file(pa_core *c, const char *fn, pa_strbuf *buf, int *fail) {
- char line[256];
- FILE *f = NULL;
+int pa_cli_command_execute_file_stream(pa_core *c, FILE *f, pa_strbuf *buf, pa_bool_t *fail) {
+ char line[1024];
int ifstate = IFSTATE_NONE;
int ret = -1;
+ pa_bool_t _fail = TRUE;
+
+ pa_assert(c);
+ pa_assert(f);
+ pa_assert(buf);
+
+ if (!fail)
+ fail = &_fail;
+
+ while (fgets(line, sizeof(line), f)) {
+ pa_strip_nl(line);
+
+ if (pa_cli_command_execute_line_stateful(c, line, buf, fail, &ifstate) < 0 && *fail)
+ goto fail;
+ }
+
+ ret = 0;
+
+fail:
+
+ return ret;
+}
+
+int pa_cli_command_execute_file(pa_core *c, const char *fn, pa_strbuf *buf, pa_bool_t *fail) {
+ FILE *f = NULL;
+ int ret = -1;
+ pa_bool_t _fail = TRUE;
pa_assert(c);
pa_assert(fn);
pa_assert(buf);
+ if (!fail)
+ fail = &_fail;
+
if (!(f = fopen(fn, "r"))) {
pa_strbuf_printf(buf, "open('%s') failed: %s\n", fn, pa_cstrerror(errno));
if (!*fail)
@@ -1379,13 +1486,7 @@ int pa_cli_command_execute_file(pa_core *c, const char *fn, pa_strbuf *buf, int
goto fail;
}
- while (fgets(line, sizeof(line), f)) {
- char *e = line + strcspn(line, linebreak);
- *e = 0;
-
- if (pa_cli_command_execute_line_stateful(c, line, buf, fail, &ifstate) < 0 && *fail)
- goto fail;
- }
+ ret = pa_cli_command_execute_file_stream(c, f, buf, fail);
ret = 0;
@@ -1396,14 +1497,18 @@ fail:
return ret;
}
-int pa_cli_command_execute(pa_core *c, const char *s, pa_strbuf *buf, int *fail) {
+int pa_cli_command_execute(pa_core *c, const char *s, pa_strbuf *buf, pa_bool_t *fail) {
const char *p;
int ifstate = IFSTATE_NONE;
+ pa_bool_t _fail = TRUE;
pa_assert(c);
pa_assert(s);
pa_assert(buf);
+ if (!fail)
+ fail = &_fail;
+
p = s;
while (*p) {
size_t l = strcspn(p, linebreak);
@@ -1421,4 +1526,3 @@ int pa_cli_command_execute(pa_core *c, const char *s, pa_strbuf *buf, int *fail)
return 0;
}
-
diff --git a/src/pulsecore/cli-command.h b/src/pulsecore/cli-command.h
index 01bca8be..9bf35dc3 100644
--- a/src/pulsecore/cli-command.h
+++ b/src/pulsecore/cli-command.h
@@ -1,8 +1,6 @@
#ifndef fooclicommandhfoo
#define fooclicommandhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -31,15 +29,18 @@
* buffer *buf. If *fail is non-zero the function will return -1 when
* one or more of the executed commands failed. *fail
* may be modified by the function call. */
-int pa_cli_command_execute_line(pa_core *c, const char *s, pa_strbuf *buf, int *fail);
+int pa_cli_command_execute_line(pa_core *c, const char *s, pa_strbuf *buf, pa_bool_t *fail);
+
+/* Execute a whole file of CLI commands */
+int pa_cli_command_execute_file(pa_core *c, const char *fn, pa_strbuf *buf, pa_bool_t *fail);
/* Execute a whole file of CLI commands */
-int pa_cli_command_execute_file(pa_core *c, const char *fn, pa_strbuf *buf, int *fail);
+int pa_cli_command_execute_file_stream(pa_core *c, FILE *f, pa_strbuf *buf, pa_bool_t *fail);
/* Split the specified string into lines and run pa_cli_command_execute_line() for each. */
-int pa_cli_command_execute(pa_core *c, const char *s, pa_strbuf *buf, int *fail);
+int pa_cli_command_execute(pa_core *c, const char *s, pa_strbuf *buf, pa_bool_t *fail);
/* Same as pa_cli_command_execute_line() but also take ifstate var. */
-int pa_cli_command_execute_line_stateful(pa_core *c, const char *s, pa_strbuf *buf, int *fail, int *ifstate);
+int pa_cli_command_execute_line_stateful(pa_core *c, const char *s, pa_strbuf *buf, pa_bool_t *fail, int *ifstate);
#endif
diff --git a/src/pulsecore/cli-text.c b/src/pulsecore/cli-text.c
index 6683e697..c92fca20 100644
--- a/src/pulsecore/cli-text.c
+++ b/src/pulsecore/cli-text.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -29,6 +27,7 @@
#include <pulse/volume.h>
#include <pulse/xmalloc.h>
+#include <pulse/timeval.h>
#include <pulsecore/module.h>
#include <pulsecore/client.h>
@@ -41,6 +40,7 @@
#include <pulsecore/core-scache.h>
#include <pulsecore/autoload.h>
#include <pulsecore/macro.h>
+#include <pulsecore/core-util.h>
#include "cli-text.h"
@@ -56,12 +56,12 @@ char *pa_module_list_to_string(pa_core *c) {
for (m = pa_idxset_first(c->modules, &idx); m; m = pa_idxset_next(c->modules, &idx)) {
pa_strbuf_printf(s, " index: %u\n"
- "\tname: <%s>\n"
- "\targument: <%s>\n"
- "\tused: %i\n"
- "\tauto unload: %s\n",
- m->index, m->name, m->argument ? m->argument : "", m->n_used,
- m->auto_unload ? "yes" : "no");
+ "\tname: <%s>\n"
+ "\targument: <%s>\n"
+ "\tused: %i\n"
+ "\tauto unload: %s\n",
+ m->index, m->name, m->argument ? m->argument : "", m->n_used,
+ pa_yes_no(m->auto_unload));
}
return pa_strbuf_tostring_free(s);
@@ -78,10 +78,20 @@ char *pa_client_list_to_string(pa_core *c) {
pa_strbuf_printf(s, "%u client(s) logged in.\n", pa_idxset_size(c->clients));
for (client = pa_idxset_first(c->clients, &idx); client; client = pa_idxset_next(c->clients, &idx)) {
- pa_strbuf_printf(s, " index: %u\n\tname: <%s>\n\tdriver: <%s>\n", client->index, client->name, client->driver);
+ char *t;
+ pa_strbuf_printf(
+ s,
+ " index: %u\n"
+ "\tdriver: <%s>\n",
+ client->index,
+ client->driver);
- if (client->owner)
- pa_strbuf_printf(s, "\towner module: <%u>\n", client->owner->index);
+ if (client->module)
+ pa_strbuf_printf(s, "\towner module: %u\n", client->module->index);
+
+ t = pa_proplist_to_string(client->proplist);
+ pa_strbuf_printf(s, "\tproperties:\n%s", t);
+ pa_xfree(t);
}
return pa_strbuf_tostring_free(s);
@@ -92,6 +102,7 @@ char *pa_sink_list_to_string(pa_core *c) {
pa_sink *sink;
uint32_t idx = PA_IDXSET_INVALID;
static const char* const state_table[] = {
+ [PA_SINK_INIT] = "INIT",
[PA_SINK_RUNNING] = "RUNNING",
[PA_SINK_SUSPENDED] = "SUSPENDED",
[PA_SINK_IDLE] = "IDLE",
@@ -104,34 +115,46 @@ char *pa_sink_list_to_string(pa_core *c) {
pa_strbuf_printf(s, "%u sink(s) available.\n", pa_idxset_size(c->sinks));
for (sink = pa_idxset_first(c->sinks, &idx); sink; sink = pa_idxset_next(c->sinks, &idx)) {
- char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
+ char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX], *t;
+ pa_usec_t min_latency, max_latency;
+
+ 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\n"
+ "\tflags: %s%s%s%s%s%s\n"
"\tstate: %s\n"
- "\tvolume: <%s>\n"
- "\tmute: <%i>\n"
- "\tlatency: <%0.0f usec>\n"
- "\tmonitor source: <%u>\n"
- "\tsample spec: <%s>\n"
- "\tchannel map: <%s>\n"
- "\tused by: <%u>\n"
- "\tlinked by: <%u>\n",
+ "\tvolume: %s\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"
+ "\tsample spec: %s\n"
+ "\tchannel map: %s\n"
+ "\tused by: %u\n"
+ "\tlinked by: %u\n",
c->default_sink_name && !strcmp(sink->name, c->default_sink_name) ? '*' : ' ',
sink->index,
sink->name,
sink->driver,
+ sink->flags & PA_SINK_HARDWARE ? "HARDWARE " : "",
+ sink->flags & PA_SINK_NETWORK ? "NETWORK " : "",
+ sink->flags & PA_SINK_HW_MUTE_CTRL ? "HW_MUTE_CTRL " : "",
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_HARDWARE ? "HARDWARE " : "",
state_table[pa_sink_get_state(sink)],
pa_cvolume_snprint(cv, sizeof(cv), pa_sink_get_volume(sink)),
- !!pa_sink_get_mute(sink),
- (double) pa_sink_get_latency(sink),
+ pa_yes_no(pa_sink_get_mute(sink)),
+ (double) pa_sink_get_latency(sink) / PA_USEC_PER_MSEC,
+ (double) pa_sink_get_requested_latency(sink) / 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,
pa_sample_spec_snprint(ss, sizeof(ss), &sink->sample_spec),
pa_channel_map_snprint(cm, sizeof(cm), &sink->channel_map),
@@ -139,9 +162,11 @@ char *pa_sink_list_to_string(pa_core *c) {
pa_sink_linked_by(sink));
if (sink->module)
- pa_strbuf_printf(s, "\tmodule: <%u>\n", sink->module->index);
- if (sink->description)
- pa_strbuf_printf(s, "\tdescription: <%s>\n", sink->description);
+ pa_strbuf_printf(s, "\tmodule: %u\n", sink->module->index);
+
+ t = pa_proplist_to_string(sink->proplist);
+ pa_strbuf_printf(s, "\tproperties:\n%s", t);
+ pa_xfree(t);
}
return pa_strbuf_tostring_free(s);
@@ -152,6 +177,7 @@ char *pa_source_list_to_string(pa_core *c) {
pa_source *source;
uint32_t idx = PA_IDXSET_INVALID;
static const char* const state_table[] = {
+ [PA_SOURCE_INIT] = "INIT",
[PA_SOURCE_RUNNING] = "RUNNING",
[PA_SOURCE_SUSPENDED] = "SUSPENDED",
[PA_SOURCE_IDLE] = "IDLE",
@@ -164,45 +190,56 @@ char *pa_source_list_to_string(pa_core *c) {
pa_strbuf_printf(s, "%u source(s) available.\n", pa_idxset_size(c->sources));
for (source = pa_idxset_first(c->sources, &idx); source; source = pa_idxset_next(c->sources, &idx)) {
- char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX];
+ char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], *t;
+ pa_usec_t min_latency, max_latency;
+ 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\n"
+ "\tflags: %s%s%s%s%s%s\n"
"\tstate: %s\n"
- "\tvolume: <%s>\n"
- "\tmute: <%u>\n"
- "\tlatency: <%0.0f usec>\n"
- "\tsample spec: <%s>\n"
- "\tchannel map: <%s>\n"
- "\tused by: <%u>\n"
- "\tlinked by: <%u>\n",
+ "\tvolume: %s\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\n"
+ "\tused by: %u\n"
+ "\tlinked by: %u\n",
c->default_source_name && !strcmp(source->name, c->default_source_name) ? '*' : ' ',
source->index,
source->name,
source->driver,
+ source->flags & PA_SOURCE_HARDWARE ? "HARDWARE " : "",
+ source->flags & PA_SOURCE_NETWORK ? "NETWORK " : "",
+ source->flags & PA_SOURCE_HW_MUTE_CTRL ? "HW_MUTE_CTRL " : "",
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_HARDWARE ? "HARDWARE " : "",
state_table[pa_source_get_state(source)],
pa_cvolume_snprint(cv, sizeof(cv), pa_source_get_volume(source)),
- !!pa_source_get_mute(source),
- (double) pa_source_get_latency(source),
+ pa_yes_no(pa_source_get_mute(source)),
+ (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),
pa_source_used_by(source),
pa_source_linked_by(source));
if (source->monitor_of)
- pa_strbuf_printf(s, "\tmonitor_of: <%u>\n", source->monitor_of->index);
+ pa_strbuf_printf(s, "\tmonitor_of: %u\n", source->monitor_of->index);
if (source->module)
- pa_strbuf_printf(s, "\tmodule: <%u>\n", source->module->index);
- if (source->description)
- pa_strbuf_printf(s, "\tdescription: <%s>\n", source->description);
+ pa_strbuf_printf(s, "\tmodule: %u\n", source->module->index);
+
+ t = pa_proplist_to_string(source->proplist);
+ pa_strbuf_printf(s, "\tproperties:\n%s", t);
+ pa_xfree(t);
}
return pa_strbuf_tostring_free(s);
@@ -214,6 +251,7 @@ char *pa_source_output_list_to_string(pa_core *c) {
pa_source_output *o;
uint32_t idx = PA_IDXSET_INVALID;
static const char* const state_table[] = {
+ [PA_SOURCE_OUTPUT_INIT] = "INIT",
[PA_SOURCE_OUTPUT_RUNNING] = "RUNNING",
[PA_SOURCE_OUTPUT_CORKED] = "CORKED",
[PA_SOURCE_OUTPUT_UNLINKED] = "UNLINKED"
@@ -225,37 +263,55 @@ char *pa_source_output_list_to_string(pa_core *c) {
pa_strbuf_printf(s, "%u source outputs(s) available.\n", pa_idxset_size(c->source_outputs));
for (o = pa_idxset_first(c->source_outputs, &idx); o; o = pa_idxset_next(c->source_outputs, &idx)) {
- char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
+ char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX], *t, clt[28];
+ pa_usec_t cl;
+
+ if ((cl = pa_source_output_get_requested_latency(o)) == (pa_usec_t) -1)
+ pa_snprintf(clt, sizeof(clt), "n/a");
+ else
+ pa_snprintf(clt, sizeof(clt), "%0.2f ms", (double) cl / PA_USEC_PER_MSEC);
pa_assert(o->source);
pa_strbuf_printf(
s,
" index: %u\n"
- "\tname: '%s'\n"
"\tdriver: <%s>\n"
- "\tflags: %s%s\n"
+ "\tflags: %s%s%s%s%s%s%s%s\n"
"\tstate: %s\n"
- "\tsource: <%u> '%s'\n"
- "\tlatency: <%0.0f usec>\n"
- "\tsample spec: <%s>\n"
- "\tchannel map: <%s>\n"
+ "\tsource: %u <%s>\n"
+ "\tcurrent latency: %0.2f ms\n"
+ "\trequested latency: %s\n"
+ "\tsample spec: %s\n"
+ "\tchannel map: %s\n"
"\tresample method: %s\n",
o->index,
- o->name,
o->driver,
o->flags & PA_SOURCE_OUTPUT_VARIABLE_RATE ? "VARIABLE_RATE " : "",
o->flags & PA_SOURCE_OUTPUT_DONT_MOVE ? "DONT_MOVE " : "",
+ o->flags & PA_SOURCE_OUTPUT_START_CORKED ? "START_CORKED " : "",
+ o->flags & PA_SOURCE_OUTPUT_NO_REMAP ? "NO_REMAP " : "",
+ o->flags & PA_SOURCE_OUTPUT_NO_REMIX ? "NO_REMIX " : "",
+ o->flags & PA_SOURCE_OUTPUT_FIX_FORMAT ? "FIX_FORMAT " : "",
+ o->flags & PA_SOURCE_OUTPUT_FIX_RATE ? "FIX_RATE " : "",
+ o->flags & PA_SOURCE_OUTPUT_FIX_CHANNELS ? "FIX_CHANNELS " : "",
state_table[pa_source_output_get_state(o)],
o->source->index, o->source->name,
- (double) pa_source_output_get_latency(o),
+ (double) pa_source_output_get_latency(o, NULL) / PA_USEC_PER_MSEC,
+ clt,
pa_sample_spec_snprint(ss, sizeof(ss), &o->sample_spec),
pa_channel_map_snprint(cm, sizeof(cm), &o->channel_map),
pa_resample_method_to_string(pa_source_output_get_resample_method(o)));
if (o->module)
- pa_strbuf_printf(s, "\towner module: <%u>\n", o->module->index);
+ pa_strbuf_printf(s, "\towner module: %u\n", o->module->index);
if (o->client)
- pa_strbuf_printf(s, "\tclient: <%u> '%s'\n", o->client->index, o->client->name);
+ pa_strbuf_printf(s, "\tclient: %u <%s>\n", o->client->index, pa_strnull(pa_proplist_gets(o->client->proplist, PA_PROP_APPLICATION_NAME)));
+ if (o->direct_on_input)
+ pa_strbuf_printf(s, "\tdirect on input: %u\n", o->direct_on_input->index);
+
+ t = pa_proplist_to_string(o->proplist);
+ pa_strbuf_printf(s, "\tproperties:\n%s", t);
+ pa_xfree(t);
}
return pa_strbuf_tostring_free(s);
@@ -266,6 +322,7 @@ char *pa_sink_input_list_to_string(pa_core *c) {
pa_sink_input *i;
uint32_t idx = PA_IDXSET_INVALID;
static const char* const state_table[] = {
+ [PA_SINK_INPUT_INIT] = "INIT",
[PA_SINK_INPUT_RUNNING] = "RUNNING",
[PA_SINK_INPUT_DRAINED] = "DRAINED",
[PA_SINK_INPUT_CORKED] = "CORKED",
@@ -278,42 +335,58 @@ char *pa_sink_input_list_to_string(pa_core *c) {
pa_strbuf_printf(s, "%u sink input(s) available.\n", pa_idxset_size(c->sink_inputs));
for (i = pa_idxset_first(c->sink_inputs, &idx); i; i = pa_idxset_next(c->sink_inputs, &idx)) {
- char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
+ char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX], *t, clt[28];
+ pa_usec_t cl;
+
+ if ((cl = pa_sink_input_get_requested_latency(i)) == (pa_usec_t) -1)
+ pa_snprintf(clt, sizeof(clt), "n/a");
+ else
+ pa_snprintf(clt, sizeof(clt), "%0.2f ms", (double) cl / PA_USEC_PER_MSEC);
pa_assert(i->sink);
pa_strbuf_printf(
s,
" index: %u\n"
- "\tname: <%s>\n"
"\tdriver: <%s>\n"
- "\tflags: %s%s\n"
+ "\tflags: %s%s%s%s%s%s%s%s\n"
"\tstate: %s\n"
- "\tsink: <%u> '%s'\n"
- "\tvolume: <%s>\n"
- "\tmute: <%i>\n"
- "\tlatency: <%0.0f usec>\n"
- "\tsample spec: <%s>\n"
- "\tchannel map: <%s>\n"
+ "\tsink: %u <%s>\n"
+ "\tvolume: %s\n"
+ "\tmuted: %s\n"
+ "\tcurrent latency: %0.2f ms\n"
+ "\trequested latency: %s\n"
+ "\tsample spec: %s\n"
+ "\tchannel map: %s\n"
"\tresample method: %s\n",
i->index,
- i->name,
i->driver,
i->flags & PA_SINK_INPUT_VARIABLE_RATE ? "VARIABLE_RATE " : "",
i->flags & PA_SINK_INPUT_DONT_MOVE ? "DONT_MOVE " : "",
+ i->flags & PA_SINK_INPUT_START_CORKED ? "START_CORKED " : "",
+ i->flags & PA_SINK_INPUT_NO_REMAP ? "NO_REMAP " : "",
+ i->flags & PA_SINK_INPUT_NO_REMIX ? "NO_REMIX " : "",
+ i->flags & PA_SINK_INPUT_FIX_FORMAT ? "FIX_FORMAT " : "",
+ i->flags & PA_SINK_INPUT_FIX_RATE ? "FIX_RATE " : "",
+ i->flags & PA_SINK_INPUT_FIX_CHANNELS ? "FIX_CHANNELS " : "",
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_sink_input_get_mute(i),
- (double) pa_sink_input_get_latency(i),
+ pa_yes_no(pa_sink_input_get_mute(i)),
+ (double) pa_sink_input_get_latency(i, NULL) / PA_USEC_PER_MSEC,
+ clt,
pa_sample_spec_snprint(ss, sizeof(ss), &i->sample_spec),
pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map),
pa_resample_method_to_string(pa_sink_input_get_resample_method(i)));
if (i->module)
- pa_strbuf_printf(s, "\tmodule: <%u>\n", i->module->index);
+ pa_strbuf_printf(s, "\tmodule: %u\n", i->module->index);
if (i->client)
- pa_strbuf_printf(s, "\tclient: <%u> '%s'\n", i->client->index, i->client->name);
+ pa_strbuf_printf(s, "\tclient: %u <%s>\n", i->client->index, pa_strnull(pa_proplist_gets(i->client->proplist, PA_PROP_APPLICATION_NAME)));
+
+ t = pa_proplist_to_string(i->proplist);
+ pa_strbuf_printf(s, "\tproperties:\n%s", t);
+ pa_xfree(t);
}
return pa_strbuf_tostring_free(s);
@@ -333,7 +406,7 @@ char *pa_scache_list_to_string(pa_core *c) {
for (e = pa_idxset_first(c->scache, &idx); e; e = pa_idxset_next(c->scache, &idx)) {
double l = 0;
- char ss[PA_SAMPLE_SPEC_SNPRINT_MAX] = "n/a", cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX] = "n/a";
+ char ss[PA_SAMPLE_SPEC_SNPRINT_MAX] = "n/a", cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX] = "n/a", *t;
if (e->memchunk.memblock) {
pa_sample_spec_snprint(ss, sizeof(ss), &e->sample_spec);
@@ -344,14 +417,14 @@ char *pa_scache_list_to_string(pa_core *c) {
pa_strbuf_printf(
s,
" name: <%s>\n"
- "\tindex: <%u>\n"
- "\tsample spec: <%s>\n"
- "\tchannel map: <%s>\n"
- "\tlength: <%lu>\n"
- "\tduration: <%0.1fs>\n"
- "\tvolume: <%s>\n"
+ "\tindex: %u\n"
+ "\tsample spec: %s\n"
+ "\tchannel map: %s\n"
+ "\tlength: %lu\n"
+ "\tduration: %0.1f s\n"
+ "\tvolume: %s\n"
"\tlazy: %s\n"
- "\tfilename: %s\n",
+ "\tfilename: <%s>\n",
e->name,
e->index,
ss,
@@ -359,8 +432,12 @@ char *pa_scache_list_to_string(pa_core *c) {
(long unsigned)(e->memchunk.memblock ? e->memchunk.length : 0),
l,
pa_cvolume_snprint(cv, sizeof(cv), &e->volume),
- e->lazy ? "yes" : "no",
+ pa_yes_no(e->lazy),
e->filename ? e->filename : "n/a");
+
+ t = pa_proplist_to_string(e->proplist);
+ pa_strbuf_printf(s, "\tproperties:\n%s", t);
+ pa_xfree(t);
}
}
@@ -381,7 +458,12 @@ char *pa_autoload_list_to_string(pa_core *c) {
while ((e = pa_hashmap_iterate(c->autoload_hashmap, &state, NULL))) {
pa_strbuf_printf(
- s, " name: <%s>\n\ttype: <%s>\n\tindex: <%u>\n\tmodule_name: <%s>\n\targuments: <%s>\n",
+ s,
+ " name: <%s>\n"
+ "\ttype: %s\n"
+ "\tindex: %u\n"
+ "\tmodule_name: <%s>\n"
+ "\targuments: <%s>\n",
e->name,
e->type == PA_NAMEREG_SOURCE ? "source" : "sink",
e->index,
diff --git a/src/pulsecore/cli-text.h b/src/pulsecore/cli-text.h
index 9e5bf081..f4cb97a5 100644
--- a/src/pulsecore/cli-text.h
+++ b/src/pulsecore/cli-text.h
@@ -1,8 +1,6 @@
#ifndef fooclitexthfoo
#define fooclitexthfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/cli.c b/src/pulsecore/cli.c
index 9fff7347..b3c639f8 100644
--- a/src/pulsecore/cli.c
+++ b/src/pulsecore/cli.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -59,7 +57,8 @@ struct pa_cli {
pa_client *client;
- int fail, kill_requested, defer_kill;
+ pa_bool_t fail, kill_requested;
+ int defer_kill;
};
static void line_callback(pa_ioline *line, const char *s, void *userdata);
@@ -81,19 +80,20 @@ pa_cli* pa_cli_new(pa_core *core, pa_iochannel *io, pa_module *m) {
pa_assert_se(c->client = pa_client_new(core, __FILE__, cname));
c->client->kill = client_kill;
c->client->userdata = c;
- c->client->owner = m;
+ c->client->module = m;
pa_ioline_set_callback(c->line, line_callback, c);
pa_ioline_puts(c->line, "Welcome to PulseAudio! Use \"help\" for usage information.\n"PROMPT);
- c->fail = c->kill_requested = c->defer_kill = 0;
+ c->fail = c->kill_requested = FALSE;
+ c->defer_kill = 0;
return c;
}
void pa_cli_free(pa_cli *c) {
pa_assert(c);
-
+
pa_ioline_close(c->line);
pa_ioline_unref(c->line);
pa_client_free(c->client);
@@ -102,13 +102,13 @@ void pa_cli_free(pa_cli *c) {
static void client_kill(pa_client *client) {
pa_cli *c;
-
+
pa_assert(client);
pa_assert_se(c = client->userdata);
pa_log_debug("CLI client killed.");
if (c->defer_kill)
- c->kill_requested = 1;
+ c->kill_requested = TRUE;
else {
if (c->eof_callback)
c->eof_callback(c, c->userdata);
@@ -119,7 +119,7 @@ static void line_callback(pa_ioline *line, const char *s, void *userdata) {
pa_strbuf *buf;
pa_cli *c = userdata;
char *p;
-
+
pa_assert(line);
pa_assert(c);
@@ -147,7 +147,7 @@ static void line_callback(pa_ioline *line, const char *s, void *userdata) {
void pa_cli_set_eof_callback(pa_cli *c, void (*cb)(pa_cli*c, void *userdata), void *userdata) {
pa_assert(c);
-
+
c->eof_callback = cb;
c->userdata = userdata;
}
diff --git a/src/pulsecore/cli.h b/src/pulsecore/cli.h
index 2b58d458..6077a8e8 100644
--- a/src/pulsecore/cli.h
+++ b/src/pulsecore/cli.h
@@ -1,8 +1,6 @@
#ifndef fooclihfoo
#define fooclihfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/client.c b/src/pulsecore/client.c
index 99cbeb83..0ffd2330 100644
--- a/src/pulsecore/client.c
+++ b/src/pulsecore/client.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -35,26 +33,29 @@
#include <pulsecore/core-subscribe.h>
#include <pulsecore/log.h>
#include <pulsecore/macro.h>
+#include <pulsecore/core-util.h>
#include "client.h"
pa_client *pa_client_new(pa_core *core, const char *driver, const char *name) {
pa_client *c;
-
+
pa_core_assert_ref(core);
c = pa_xnew(pa_client, 1);
- c->name = pa_xstrdup(name);
- c->driver = pa_xstrdup(driver);
- c->owner = NULL;
c->core = core;
+ c->proplist = pa_proplist_new();
+ if (name)
+ pa_proplist_sets(c->proplist, PA_PROP_APPLICATION_NAME, name);
+ c->driver = pa_xstrdup(driver);
+ c->module = NULL;
c->kill = NULL;
c->userdata = NULL;
pa_assert_se(pa_idxset_put(core->clients, c, &c->index) >= 0);
- pa_log_info("Created %u \"%s\"", c->index, c->name);
+ pa_log_info("Created %u \"%s\"", c->index, pa_strnull(name));
pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_NEW, c->index);
pa_core_check_quit(core);
@@ -63,23 +64,26 @@ pa_client *pa_client_new(pa_core *core, const char *driver, const char *name) {
}
void pa_client_free(pa_client *c) {
+ pa_core *core;
+
pa_assert(c);
pa_assert(c->core);
+ core = c->core;
pa_idxset_remove_by_data(c->core->clients, c, NULL);
- pa_core_check_quit(c->core);
-
- pa_log_info("Freed %u \"%s\"", c->index, c->name);
+ pa_log_info("Freed %u \"%s\"", c->index, pa_strnull(pa_proplist_gets(c->proplist, PA_PROP_APPLICATION_NAME)));
pa_subscription_post(c->core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_REMOVE, c->index);
- pa_xfree(c->name);
+ pa_proplist_free(c->proplist);
pa_xfree(c->driver);
pa_xfree(c);
+
+ pa_core_check_quit(core);
}
void pa_client_kill(pa_client *c) {
pa_assert(c);
-
+
if (!c->kill) {
pa_log_warn("kill() operation not implemented for client %u", c->index);
return;
@@ -91,10 +95,7 @@ void pa_client_kill(pa_client *c) {
void pa_client_set_name(pa_client *c, const char *name) {
pa_assert(c);
- pa_log_info("Client %u changed name from \"%s\" to \"%s\"", c->index, c->name, name);
-
- pa_xfree(c->name);
- c->name = pa_xstrdup(name);
-
+ pa_log_info("Client %u changed name from \"%s\" to \"%s\"", c->index, pa_strnull(pa_proplist_gets(c->proplist, PA_PROP_APPLICATION_NAME)), name);
+ pa_proplist_sets(c->proplist, PA_PROP_APPLICATION_NAME, name);
pa_subscription_post(c->core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_CHANGE, c->index);
}
diff --git a/src/pulsecore/client.h b/src/pulsecore/client.h
index 6d09b999..28d1fe5f 100644
--- a/src/pulsecore/client.h
+++ b/src/pulsecore/client.h
@@ -1,8 +1,6 @@
#ifndef foopulseclienthfoo
#define foopulseclienthfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -28,6 +26,7 @@
typedef struct pa_client pa_client;
+#include <pulse/proplist.h>
#include <pulsecore/core.h>
#include <pulsecore/module.h>
@@ -37,11 +36,12 @@ typedef struct pa_client pa_client;
struct pa_client {
uint32_t index;
-
- pa_module *owner;
- char *name, *driver;
pa_core *core;
+ pa_proplist *proplist;
+ pa_module *module;
+ char *driver;
+
void (*kill)(pa_client *c);
void *userdata;
};
diff --git a/src/pulsecore/conf-parser.c b/src/pulsecore/conf-parser.c
index ee43b05a..4aec45d7 100644
--- a/src/pulsecore/conf-parser.c
+++ b/src/pulsecore/conf-parser.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -110,7 +108,7 @@ int pa_config_parse(const char *filename, FILE *f, const pa_config_item *t, void
int r = -1;
unsigned line = 0;
int do_close = !f;
-
+
pa_assert(filename);
pa_assert(t);
@@ -153,7 +151,7 @@ finish:
int pa_config_parse_int(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, PA_GCC_UNUSED void *userdata) {
int *i = data;
int32_t k;
-
+
pa_assert(filename);
pa_assert(lvalue);
pa_assert(rvalue);
@@ -169,7 +167,8 @@ int pa_config_parse_int(const char *filename, unsigned line, const char *lvalue,
}
int pa_config_parse_bool(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, PA_GCC_UNUSED void *userdata) {
- int *b = data, k;
+ int k;
+ pa_bool_t *b = data;
pa_assert(filename);
pa_assert(lvalue);
@@ -181,7 +180,7 @@ int pa_config_parse_bool(const char *filename, unsigned line, const char *lvalue
return -1;
}
- *b = k;
+ *b = !!k;
return 0;
}
diff --git a/src/pulsecore/conf-parser.h b/src/pulsecore/conf-parser.h
index b56d979e..7eb1fae2 100644
--- a/src/pulsecore/conf-parser.h
+++ b/src/pulsecore/conf-parser.h
@@ -1,8 +1,6 @@
#ifndef fooconfparserhfoo
#define fooconfparserhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/core-error.c b/src/pulsecore/core-error.c
index 64464132..3d6c2c3b 100644
--- a/src/pulsecore/core-error.c
+++ b/src/pulsecore/core-error.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -51,7 +49,7 @@ const char* pa_cstrerror(int errnum) {
if ((t = PA_STATIC_TLS_GET(cstrerror)))
pa_xfree(t);
-
+
#if defined(HAVE_STRERROR_R) && defined(__GLIBC__)
original = strerror_r(errnum, errbuf, sizeof(errbuf));
#elif defined(HAVE_STRERROR_R)
diff --git a/src/pulsecore/core-error.h b/src/pulsecore/core-error.h
index 443c4883..b0c306c7 100644
--- a/src/pulsecore/core-error.h
+++ b/src/pulsecore/core-error.h
@@ -1,8 +1,6 @@
#ifndef foocoreerrorhfoo
#define foocoreerrorhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/core-scache.c b/src/pulsecore/core-scache.c
index 1cd24aa2..75fa2ff1 100644
--- a/src/pulsecore/core-scache.c
+++ b/src/pulsecore/core-scache.c
@@ -1,9 +1,7 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
- Copyright 2004-2006 Lennart Poettering
+ Copyright 2004-2008 Lennart Poettering
Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
PulseAudio is free software; you can redistribute it and/or modify
@@ -63,12 +61,12 @@
#include "core-scache.h"
-#define UNLOAD_POLL_TIME 2
+#define UNLOAD_POLL_TIME 60
static void timeout_callback(pa_mainloop_api *m, pa_time_event*e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) {
pa_core *c = userdata;
struct timeval ntv;
-
+
pa_assert(c);
pa_assert(c->mainloop == m);
pa_assert(c->scache_auto_unload_event == e);
@@ -82,19 +80,21 @@ static void timeout_callback(pa_mainloop_api *m, pa_time_event*e, PA_GCC_UNUSED
static void free_entry(pa_scache_entry *e) {
pa_assert(e);
-
+
pa_namereg_unregister(e->core, e->name);
pa_subscription_post(e->core, PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE|PA_SUBSCRIPTION_EVENT_REMOVE, e->index);
pa_xfree(e->name);
pa_xfree(e->filename);
if (e->memchunk.memblock)
pa_memblock_unref(e->memchunk.memblock);
+ if (e->proplist)
+ pa_proplist_free(e->proplist);
pa_xfree(e);
}
static pa_scache_entry* scache_add_item(pa_core *c, const char *name) {
pa_scache_entry *e;
-
+
pa_assert(c);
pa_assert(name);
@@ -103,6 +103,7 @@ static pa_scache_entry* scache_add_item(pa_core *c, const char *name) {
pa_memblock_unref(e->memchunk.memblock);
pa_xfree(e->filename);
+ pa_proplist_clear(e->proplist);
pa_assert(e->core == c);
@@ -117,11 +118,10 @@ static pa_scache_entry* scache_add_item(pa_core *c, const char *name) {
e->name = pa_xstrdup(name);
e->core = c;
+ e->proplist = pa_proplist_new();
- if (!c->scache) {
+ if (!c->scache)
c->scache = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
- pa_assert(c->scache);
- }
pa_idxset_put(c->scache, e, &e->index);
@@ -129,25 +129,40 @@ static pa_scache_entry* scache_add_item(pa_core *c, const char *name) {
}
e->last_used_time = 0;
- e->memchunk.memblock = NULL;
- e->memchunk.index = e->memchunk.length = 0;
+ pa_memchunk_reset(&e->memchunk);
e->filename = NULL;
- e->lazy = 0;
+ e->lazy = FALSE;
e->last_used_time = 0;
memset(&e->sample_spec, 0, sizeof(e->sample_spec));
pa_channel_map_init(&e->channel_map);
pa_cvolume_reset(&e->volume, PA_CHANNELS_MAX);
+ pa_proplist_sets(e->proplist, PA_PROP_MEDIA_ROLE, "event");
+
return e;
}
-int pa_scache_add_item(pa_core *c, const char *name, const pa_sample_spec *ss, const pa_channel_map *map, const pa_memchunk *chunk, uint32_t *idx) {
+int pa_scache_add_item(
+ pa_core *c,
+ const char *name,
+ const pa_sample_spec *ss,
+ const pa_channel_map *map,
+ const pa_memchunk *chunk,
+ pa_proplist *p,
+ uint32_t *idx) {
+
pa_scache_entry *e;
char st[PA_SAMPLE_SPEC_SNPRINT_MAX];
-
+ pa_channel_map tmap;
+
pa_assert(c);
pa_assert(name);
+ pa_assert(!ss || pa_sample_spec_valid(ss));
+ pa_assert(!map || (pa_channel_map_valid(map) && ss && ss->channels == map->channels));
+
+ if (ss && !map)
+ pa_channel_map_init_extend(&tmap, ss->channels, PA_CHANNEL_MAP_DEFAULT);
if (chunk && chunk->length > PA_SCACHE_ENTRY_SIZE_MAX)
return -1;
@@ -155,9 +170,11 @@ int pa_scache_add_item(pa_core *c, const char *name, const pa_sample_spec *ss, c
if (!(e = scache_add_item(c, name)))
return -1;
+ memset(&e->sample_spec, 0, sizeof(e->sample_spec));
+ pa_channel_map_init(&e->channel_map);
+
if (ss) {
e->sample_spec = *ss;
- pa_channel_map_init_auto(&e->channel_map, ss->channels, PA_CHANNEL_MAP_DEFAULT);
e->volume.channels = e->sample_spec.channels;
}
@@ -169,6 +186,9 @@ int pa_scache_add_item(pa_core *c, const char *name, const pa_sample_spec *ss, c
pa_memblock_ref(e->memchunk.memblock);
}
+ if (p)
+ pa_proplist_update(e->proplist, PA_UPDATE_REPLACE, p);
+
if (idx)
*idx = e->index;
@@ -184,6 +204,7 @@ int pa_scache_add_file(pa_core *c, const char *name, const char *filename, uint3
pa_channel_map map;
pa_memchunk chunk;
int r;
+ pa_proplist *p;
#ifdef OS_IS_WIN32
char buf[MAX_PATH];
@@ -195,12 +216,15 @@ int pa_scache_add_file(pa_core *c, const char *name, const char *filename, uint3
pa_assert(c);
pa_assert(name);
pa_assert(filename);
-
+
if (pa_sound_file_load(c->mempool, filename, &ss, &map, &chunk) < 0)
return -1;
- r = pa_scache_add_item(c, name, &ss, &map, &chunk, idx);
+ p = pa_proplist_new();
+ pa_proplist_sets(p, PA_PROP_MEDIA_FILENAME, filename);
+ r = pa_scache_add_item(c, name, &ss, &map, &chunk, p, idx);
pa_memblock_unref(chunk.memblock);
+ pa_proplist_free(p);
return r;
}
@@ -222,9 +246,11 @@ int pa_scache_add_file_lazy(pa_core *c, const char *name, const char *filename,
if (!(e = scache_add_item(c, name)))
return -1;
- e->lazy = 1;
+ e->lazy = TRUE;
e->filename = pa_xstrdup(filename);
+ pa_proplist_sets(e->proplist, PA_PROP_MEDIA_FILENAME, filename);
+
if (!c->scache_auto_unload_event) {
struct timeval ntv;
pa_gettimeofday(&ntv);
@@ -240,15 +266,14 @@ int pa_scache_add_file_lazy(pa_core *c, const char *name, const char *filename,
int pa_scache_remove_item(pa_core *c, const char *name) {
pa_scache_entry *e;
-
+
pa_assert(c);
pa_assert(name);
if (!(e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE, 0)))
return -1;
- if (pa_idxset_remove_by_data(c->scache, e, NULL) != e)
- pa_assert(0);
+ pa_assert_se(pa_idxset_remove_by_data(c->scache, e, NULL) == e);
pa_log_debug("Removed sample \"%s\"", name);
@@ -260,7 +285,7 @@ int pa_scache_remove_item(pa_core *c, const char *name) {
static void free_cb(void *p, PA_GCC_UNUSED void *userdata) {
pa_scache_entry *e = p;
pa_assert(e);
-
+
free_entry(e);
}
@@ -276,10 +301,10 @@ void pa_scache_free(pa_core *c) {
c->mainloop->time_free(c->scache_auto_unload_event);
}
-int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, pa_volume_t volume) {
+int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, pa_volume_t volume, pa_proplist *p, uint32_t *sink_input_idx) {
pa_scache_entry *e;
- char *t;
pa_cvolume r;
+ pa_proplist *merged;
pa_assert(c);
pa_assert(name);
@@ -303,17 +328,24 @@ int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, pa_volume_t
pa_log_debug("Playing sample \"%s\" on \"%s\"", name, sink->name);
- t = pa_sprintf_malloc("sample:%s", name);
-
pa_cvolume_set(&r, e->volume.channels, volume);
pa_sw_cvolume_multiply(&r, &r, &e->volume);
- if (pa_play_memchunk(sink, t, &e->sample_spec, &e->channel_map, &e->memchunk, &r) < 0) {
- pa_xfree(t);
+ 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, &r, merged, sink_input_idx) < 0) {
+ pa_proplist_free(merged);
return -1;
}
- pa_xfree(t);
+ pa_proplist_free(merged);
if (e->lazy)
time(&e->last_used_time);
@@ -321,21 +353,21 @@ int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, pa_volume_t
return 0;
}
-int pa_scache_play_item_by_name(pa_core *c, const char *name, const char*sink_name, pa_volume_t volume, int autoload) {
+int pa_scache_play_item_by_name(pa_core *c, const char *name, const char*sink_name, pa_bool_t autoload, pa_volume_t volume, pa_proplist *p, uint32_t *sink_input_idx) {
pa_sink *sink;
-
+
pa_assert(c);
pa_assert(name);
if (!(sink = pa_namereg_get(c, sink_name, PA_NAMEREG_SINK, autoload)))
return -1;
- return pa_scache_play_item(c, name, sink, volume);
+ return pa_scache_play_item(c, name, sink, volume, p, sink_input_idx);
}
-const char * pa_scache_get_name_by_id(pa_core *c, uint32_t id) {
+const char *pa_scache_get_name_by_id(pa_core *c, uint32_t id) {
pa_scache_entry *e;
-
+
pa_assert(c);
pa_assert(id != PA_IDXSET_INVALID);
@@ -347,7 +379,7 @@ const char * pa_scache_get_name_by_id(pa_core *c, uint32_t id) {
uint32_t pa_scache_get_id_by_name(pa_core *c, const char *name) {
pa_scache_entry *e;
-
+
pa_assert(c);
pa_assert(name);
@@ -357,10 +389,11 @@ uint32_t pa_scache_get_id_by_name(pa_core *c, const char *name) {
return e->index;
}
-uint32_t pa_scache_total_size(pa_core *c) {
+size_t pa_scache_total_size(pa_core *c) {
pa_scache_entry *e;
- uint32_t idx, sum = 0;
-
+ uint32_t idx;
+ size_t sum = 0;
+
pa_assert(c);
if (!c->scache || !pa_idxset_size(c->scache))
@@ -394,8 +427,7 @@ void pa_scache_unload_unused(pa_core *c) {
continue;
pa_memblock_unref(e->memchunk.memblock);
- e->memchunk.memblock = NULL;
- e->memchunk.index = e->memchunk.length = 0;
+ pa_memchunk_reset(&e->memchunk);
pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE|PA_SUBSCRIPTION_EVENT_CHANGE, e->index);
}
@@ -407,7 +439,7 @@ static void add_file(pa_core *c, const char *pathname) {
pa_core_assert_ref(c);
pa_assert(pathname);
-
+
e = pa_path_get_filename(pathname);
if (stat(pathname, &st) < 0) {
@@ -423,7 +455,7 @@ static void add_file(pa_core *c, const char *pathname) {
int pa_scache_add_directory_lazy(pa_core *c, const char *pathname) {
DIR *dir;
-
+
pa_core_assert_ref(c);
pa_assert(pathname);
@@ -458,8 +490,9 @@ int pa_scache_add_directory_lazy(pa_core *c, const char *pathname) {
pa_snprintf(p, sizeof(p), "%s/%s", pathname, e->d_name);
add_file(c, p);
}
+
+ closedir(dir);
}
- closedir(dir);
return 0;
}
diff --git a/src/pulsecore/core-scache.h b/src/pulsecore/core-scache.h
index ab7ec0ef..80e0fd04 100644
--- a/src/pulsecore/core-scache.h
+++ b/src/pulsecore/core-scache.h
@@ -1,8 +1,6 @@
#ifndef foocorescachehfoo
#define foocorescachehfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -29,11 +27,12 @@
#include <pulsecore/memchunk.h>
#include <pulsecore/sink.h>
-#define PA_SCACHE_ENTRY_SIZE_MAX (1024*1024*2)
+#define PA_SCACHE_ENTRY_SIZE_MAX (1024*1024*16)
typedef struct pa_scache_entry {
- pa_core *core;
uint32_t index;
+ pa_core *core;
+
char *name;
pa_cvolume volume;
@@ -43,25 +42,27 @@ typedef struct pa_scache_entry {
char *filename;
- int lazy;
+ pa_bool_t lazy;
time_t last_used_time;
+
+ pa_proplist *proplist;
} pa_scache_entry;
-int pa_scache_add_item(pa_core *c, const char *name, const pa_sample_spec *ss, const pa_channel_map *map, const pa_memchunk *chunk, uint32_t *idx);
+int pa_scache_add_item(pa_core *c, const char *name, const pa_sample_spec *ss, const pa_channel_map *map, const pa_memchunk *chunk, pa_proplist *p, uint32_t *idx);
int pa_scache_add_file(pa_core *c, const char *name, const char *filename, uint32_t *idx);
int pa_scache_add_file_lazy(pa_core *c, const char *name, const char *filename, uint32_t *idx);
int pa_scache_add_directory_lazy(pa_core *c, const char *pathname);
int pa_scache_remove_item(pa_core *c, const char *name);
-int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, pa_volume_t volume);
-int pa_scache_play_item_by_name(pa_core *c, const char *name, const char*sink_name, pa_volume_t volume, int autoload);
+int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, pa_volume_t volume, pa_proplist *p, uint32_t *sink_input_idx);
+int pa_scache_play_item_by_name(pa_core *c, const char *name, const char*sink_name, pa_bool_t autoload, pa_volume_t volume, pa_proplist *p, uint32_t *sink_input_idx);
void pa_scache_free(pa_core *c);
const char *pa_scache_get_name_by_id(pa_core *c, uint32_t id);
uint32_t pa_scache_get_id_by_name(pa_core *c, const char *name);
-uint32_t pa_scache_total_size(pa_core *c);
+size_t pa_scache_total_size(pa_core *c);
void pa_scache_unload_unused(pa_core *c);
diff --git a/src/pulsecore/core-subscribe.c b/src/pulsecore/core-subscribe.c
index 06c5a4ad..6107002b 100644
--- a/src/pulsecore/core-subscribe.c
+++ b/src/pulsecore/core-subscribe.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/core-subscribe.h b/src/pulsecore/core-subscribe.h
index 2b6863f9..2f9730d9 100644
--- a/src/pulsecore/core-subscribe.h
+++ b/src/pulsecore/core-subscribe.h
@@ -1,8 +1,6 @@
#ifndef foocoresubscribehfoo
#define foocoresubscribehfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/core-util.c b/src/pulsecore/core-util.c
index a644b664..d259fb16 100644
--- a/src/pulsecore/core-util.c
+++ b/src/pulsecore/core-util.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -41,6 +39,7 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
+#include <dirent.h>
#ifdef HAVE_STRTOF_L
#include <locale.h>
@@ -103,12 +102,6 @@
#define MSG_NOSIGNAL 0
#endif
-#ifndef OS_IS_WIN32
-#define PA_USER_RUNTIME_PATH_PREFIX "/tmp/pulse-"
-#else
-#define PA_USER_RUNTIME_PATH_PREFIX "%TEMP%\\pulse-"
-#endif
-
#ifdef OS_IS_WIN32
#define PULSE_ROOTENV "PULSE_ROOT"
@@ -221,7 +214,7 @@ int pa_make_secure_dir(const char* dir, mode_t m, uid_t uid, gid_t gid) {
goto fail;
}
#else
- pa_log_warn("secure directory creation not supported on Win32.");
+ pa_log_warn("Secure directory creation not supported on Win32.");
#endif
return 0;
@@ -512,8 +505,10 @@ char *pa_strlcpy(char *b, const char *s, size_t l) {
return b;
}
-/* Make the current thread a realtime thread*/
-void pa_make_realtime(void) {
+/* 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
struct sched_param sp;
@@ -524,55 +519,169 @@ void pa_make_realtime(void) {
if ((r = pthread_getschedparam(pthread_self(), &policy, &sp)) != 0) {
pa_log("pthread_getschedgetparam(): %s", pa_cstrerror(r));
- return;
+ return -1;
+ }
+
+ if (policy == SCHED_FIFO && sp.sched_priority >= rtprio) {
+ pa_log_info("Thread already being scheduled with SCHED_FIFO with priority %i.", sp.sched_priority);
+ return 0;
}
- sp.sched_priority = 1;
+ sp.sched_priority = rtprio;
if ((r = pthread_setschedparam(pthread_self(), SCHED_FIFO, &sp)) != 0) {
+
+ while (sp.sched_priority > 1) {
+ sp.sched_priority --;
+
+ 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;
+ }
+ }
+
pa_log_warn("pthread_setschedparam(): %s", pa_cstrerror(r));
- return;
+ return -1;
+ }
+
+ pa_log_info("Successfully enabled SCHED_FIFO scheduling for thread, with priority %i.", sp.sched_priority);
+ return 0;
+#else
+ 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;
+
+#if defined(HAVE_SYS_RESOURCE_H) && defined(RLIMIT_RTPRIO)
+ {
+ struct rlimit rl;
+
+ if (getrlimit(RLIMIT_RTPRIO, &rl) >= 0)
+ if (rl.rlim_cur > 0 || rl.rlim_cur == RLIM_INFINITY)
+ return TRUE;
}
+#endif
+
+#if defined(HAVE_SYS_CAPABILITY_H) && defined(CAP_SYS_NICE)
+ {
+ cap_t cap;
- pa_log_info("Successfully enabled SCHED_FIFO scheduling for thread.");
+ if ((cap = cap_get_proc())) {
+ cap_flag_value_t flag = CAP_CLEAR;
+
+ if (cap_get_flag(cap, CAP_SYS_NICE, CAP_EFFECTIVE, &flag) >= 0)
+ if (flag == CAP_SET) {
+ cap_free(cap);
+ return TRUE;
+ }
+
+ cap_free(cap);
+ }
+ }
#endif
+ return FALSE;
}
-#define NICE_LEVEL (-11)
+/* 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;
+
+#if defined(HAVE_SYS_RESOURCE_H) && defined(RLIMIT_RTPRIO)
+ {
+ struct rlimit rl;
-/* Raise the priority of the current process as much as possible and
-sensible: set the nice level to -15.*/
-void pa_raise_priority(void) {
+ if (getrlimit(RLIMIT_NICE, &rl) >= 0)
+ if (rl.rlim_cur >= 21 || rl.rlim_cur == RLIM_INFINITY)
+ return TRUE;
+ }
+#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;
+
+ if (cap_get_flag(cap, CAP_SYS_NICE, CAP_EFFECTIVE, &flag) >= 0)
+ if (flag == CAP_SET) {
+ cap_free(cap);
+ return TRUE;
+ }
+
+ cap_free(cap);
+ }
+ }
+#endif
+
+ return FALSE;
+}
+
+/* Raise the priority of the current process as much as possible that
+ * is <= the specified nice level..*/
+int pa_raise_priority(int nice_level) {
#ifdef HAVE_SYS_RESOURCE_H
- if (setpriority(PRIO_PROCESS, 0, NICE_LEVEL) < 0)
+ if (setpriority(PRIO_PROCESS, 0, nice_level) < 0) {
+ int n;
+
+ for (n = nice_level+1; n < 0; n++) {
+
+ 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;
+ }
+ }
+
pa_log_warn("setpriority(): %s", pa_cstrerror(errno));
- else
- pa_log_info("Successfully gained nice level %i.", NICE_LEVEL);
+ return -1;
+ }
+
+ pa_log_info("Successfully gained nice level %i.", nice_level);
#endif
#ifdef OS_IS_WIN32
- if (!SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS))
- pa_log_warn("SetPriorityClass() failed: 0x%08X", GetLastError());
- else
- pa_log_info("Successfully gained high priority class.");
+ if (nice_level < 0) {
+ if (!SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS)) {
+ pa_log_warn("SetPriorityClass() failed: 0x%08X", GetLastError());
+ return .-1;
+ } else
+ pa_log_info("Successfully gained high priority class.");
+ }
#endif
+
+ return 0;
}
/* Reset the priority to normal, inverting the changes made by
- * pa_raise_priority() */
+ * pa_raise_priority() and pa_make_realtime()*/
void pa_reset_priority(void) {
-#ifdef OS_IS_WIN32
- SetPriorityClass(GetCurrentProcess(), NORMAL_PRIORITY_CLASS);
-#endif
-
#ifdef HAVE_SYS_RESOURCE_H
+ struct sched_param sp;
+
setpriority(PRIO_PROCESS, 0, 0);
+
+ memset(&sp, 0, sizeof(sp));
+ pa_assert_se(pthread_setschedparam(pthread_self(), SCHED_OTHER, &sp) == 0);
+#endif
+
+#ifdef OS_IS_WIN32
+ SetPriorityClass(GetCurrentProcess(), NORMAL_PRIORITY_CLASS);
#endif
}
/* Try to parse a boolean string value.*/
int pa_parse_boolean(const char *v) {
+ pa_assert(v);
if (!strcmp(v, "1") || v[0] == 'y' || v[0] == 'Y' || v[0] == 't' || v[0] == 'T' || !strcasecmp(v, "on"))
return 1;
@@ -1033,12 +1142,13 @@ fail:
/* Unlock a temporary lcok file */
int pa_unlock_lockfile(const char *fn, int fd) {
int r = 0;
- pa_assert(fn);
pa_assert(fd >= 0);
- if (unlink(fn) < 0) {
- pa_log_warn("Unable to remove lock file '%s': %s", fn, pa_cstrerror(errno));
- r = -1;
+ if (fn) {
+ if (unlink(fn) < 0) {
+ pa_log_warn("Unable to remove lock file '%s': %s", fn, pa_cstrerror(errno));
+ r = -1;
+ }
}
if (pa_lock_fd(fd, 0) < 0) {
@@ -1054,16 +1164,58 @@ int pa_unlock_lockfile(const char *fn, int fd) {
return r;
}
+static char *get_dir(mode_t m, const char *env_name) {
+ const char *e;
+ char *d;
+
+ if ((e = getenv(env_name)))
+ d = pa_xstrdup(e);
+ else {
+ char h[PATH_MAX];
+ struct stat st;
+
+ if (!pa_get_home_dir(h, sizeof(h))) {
+ pa_log_error("Failed to get home directory.");
+ return NULL;
+ }
+
+ if (stat(h, &st) < 0) {
+ pa_log_error("Failed to stat home directory %s: %s", h, pa_cstrerror(errno));
+ return NULL;
+ }
+
+ if (st.st_uid != getuid()) {
+ pa_log_error("Home directory %s not ours.", d);
+ return NULL;
+ }
+
+ d = pa_sprintf_malloc("%s" PA_PATH_SEP ".pulse", h);
+ }
+
+ if (pa_make_secure_dir(d, m, (pid_t) -1, (pid_t) -1) < 0) {
+ pa_log_error("Failed to create secure directory: %s", pa_cstrerror(errno));
+ return NULL;
+ }
+
+ return d;
+}
+
+char *pa_get_runtime_dir(void) {
+ return get_dir(pa_in_system_mode() ? 0755 : 0700, "PULSE_RUNTIME_PATH");
+}
+
+char *pa_get_state_dir(void) {
+ return get_dir(0700, "PULSE_STATE_PATH");
+}
+
/* Try to open a configuration file. If "env" is specified, open the
* value of the specified environment variable. Otherwise look for a
* file "local" in the home directory or a file "global" in global
* file system. If "result" is non-NULL, a pointer to a newly
* allocated buffer containing the used configuration file is
* stored there.*/
-FILE *pa_open_config_file(const char *global, const char *local, const char *env, char **result, const char *mode) {
+FILE *pa_open_config_file(const char *global, const char *local, const char *env, char **result) {
const char *fn;
- char h[PATH_MAX];
-
#ifdef OS_IS_WIN32
char buf[PATH_MAX];
@@ -1072,68 +1224,152 @@ FILE *pa_open_config_file(const char *global, const char *local, const char *env
#endif
if (env && (fn = getenv(env))) {
+ FILE *f;
+
#ifdef OS_IS_WIN32
if (!ExpandEnvironmentStrings(fn, buf, PATH_MAX))
return NULL;
fn = buf;
#endif
- if (result)
- *result = pa_xstrdup(fn);
+ if ((f = fopen(fn, "r"))) {
+ if (result)
+ *result = pa_xstrdup(fn);
- return fopen(fn, mode);
+ return f;
+ }
+
+ pa_log_warn("Failed to open configuration file '%s': %s", fn, pa_cstrerror(errno));
+ return NULL;
}
if (local) {
const char *e;
- char *lfn = NULL;
+ char *lfn;
+ char h[PATH_MAX];
+ FILE *f;
if ((e = getenv("PULSE_CONFIG_PATH")))
- fn = lfn = pa_sprintf_malloc("%s/%s", e, local);
+ fn = lfn = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", e, local);
else if (pa_get_home_dir(h, sizeof(h)))
- fn = lfn = pa_sprintf_malloc("%s/.pulse/%s", h, local);
-
- if (lfn) {
- FILE *f;
+ fn = lfn = pa_sprintf_malloc("%s" PA_PATH_SEP ".pulse" PA_PATH_SEP "%s", h, local);
#ifdef OS_IS_WIN32
- if (!ExpandEnvironmentStrings(lfn, buf, PATH_MAX))
- return NULL;
- fn = buf;
+ if (!ExpandEnvironmentStrings(lfn, buf, PATH_MAX)) {
+ pa_xfree(lfn);
+ return NULL;
+ }
+ fn = buf;
#endif
- f = fopen(fn, mode);
- if (f != NULL) {
- if (result)
- *result = pa_xstrdup(fn);
- pa_xfree(lfn);
- return f;
- }
+ if ((f = fopen(fn, "r"))) {
+ if (result)
+ *result = pa_xstrdup(fn);
- if (errno != ENOENT)
- pa_log_warn("Failed to open configuration file '%s': %s", lfn, pa_cstrerror(errno));
+ pa_xfree(lfn);
+ return f;
+ }
+ if (errno != ENOENT) {
+ pa_log_warn("Failed to open configuration file '%s': %s", fn, pa_cstrerror(errno));
pa_xfree(lfn);
+ return NULL;
}
+
+ pa_xfree(lfn);
}
- if (!global) {
- if (result)
- *result = NULL;
+ if (global) {
+ FILE *f;
+
+#ifdef OS_IS_WIN32
+ if (!ExpandEnvironmentStrings(global, buf, PATH_MAX))
+ return NULL;
+ global = buf;
+#endif
+
+ if ((f = fopen(global, "r"))) {
+
+ if (result)
+ *result = pa_xstrdup(global);
+
+ return f;
+ }
+ } else
errno = ENOENT;
+
+ return NULL;
+}
+
+char *pa_find_config_file(const char *global, const char *local, const char *env) {
+ const char *fn;
+#ifdef OS_IS_WIN32
+ char buf[PATH_MAX];
+
+ if (!getenv(PULSE_ROOTENV))
+ pa_set_root(NULL);
+#endif
+
+ if (env && (fn = getenv(env))) {
+#ifdef OS_IS_WIN32
+ if (!ExpandEnvironmentStrings(fn, buf, PATH_MAX))
+ return NULL;
+ fn = buf;
+#endif
+
+ if (access(fn, R_OK) == 0)
+ return pa_xstrdup(fn);
+
+ pa_log_warn("Failed to access configuration file '%s': %s", fn, pa_cstrerror(errno));
return NULL;
}
+ if (local) {
+ const char *e;
+ char *lfn;
+ char h[PATH_MAX];
+
+ if ((e = getenv("PULSE_CONFIG_PATH")))
+ fn = lfn = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", e, local);
+ else if (pa_get_home_dir(h, sizeof(h)))
+ fn = lfn = pa_sprintf_malloc("%s" PA_PATH_SEP ".pulse" PA_PATH_SEP "%s", h, local);
+
#ifdef OS_IS_WIN32
- if (!ExpandEnvironmentStrings(global, buf, PATH_MAX))
- return NULL;
- global = buf;
+ if (!ExpandEnvironmentStrings(lfn, buf, PATH_MAX)) {
+ pa_xfree(lfn);
+ return NULL;
+ }
+ fn = buf;
#endif
- if (result)
- *result = pa_xstrdup(global);
+ if (access(fn, R_OK) == 0) {
+ char *r = pa_xstrdup(fn);
+ pa_xfree(lfn);
+ return r;
+ }
+
+ if (errno != ENOENT) {
+ pa_log_warn("Failed to access configuration file '%s': %s", fn, pa_cstrerror(errno));
+ pa_xfree(lfn);
+ return NULL;
+ }
+
+ pa_xfree(lfn);
+ }
+
+ if (global) {
+#ifdef OS_IS_WIN32
+ if (!ExpandEnvironmentStrings(global, buf, PATH_MAX))
+ return NULL;
+ global = buf;
+#endif
- return fopen(global, mode);
+ if (access(fn, R_OK) == 0)
+ return pa_xstrdup(global);
+ } else
+ errno = ENOENT;
+
+ return NULL;
}
/* Format the specified data as a hexademical string */
@@ -1200,7 +1436,7 @@ size_t pa_parsehex(const char *p, uint8_t *d, size_t dlength) {
}
/* Returns nonzero when *s starts with *pfx */
-int pa_startswith(const char *s, const char *pfx) {
+pa_bool_t pa_startswith(const char *s, const char *pfx) {
size_t l;
pa_assert(s);
@@ -1212,7 +1448,7 @@ int pa_startswith(const char *s, const char *pfx) {
}
/* Returns nonzero when *s ends with *sfx */
-int pa_endswith(const char *s, const char *sfx) {
+pa_bool_t pa_endswith(const char *s, const char *sfx) {
size_t l1, l2;
pa_assert(s);
@@ -1224,45 +1460,62 @@ int pa_endswith(const char *s, const char *sfx) {
return l1 >= l2 && strcmp(s+l1-l2, sfx) == 0;
}
-/* if fn is null return the PulseAudio run time path in s (/tmp/pulse)
- * if fn is non-null and starts with / return fn in s
- * otherwise append fn to the run time path and return it in s */
-char *pa_runtime_path(const char *fn, char *s, size_t l) {
- const char *e;
+pa_bool_t pa_is_path_absolute(const char *fn) {
+ pa_assert(fn);
#ifndef OS_IS_WIN32
- if (fn && *fn == '/')
+ return *fn == '/';
#else
- if (fn && strlen(fn) >= 3 && isalpha(fn[0]) && fn[1] == ':' && fn[2] == '\\')
+ return strlen(fn) >= 3 && isalpha(fn[0]) && fn[1] == ':' && fn[2] == '\\';
#endif
- return pa_strlcpy(s, fn, l);
+}
- if ((e = getenv("PULSE_RUNTIME_PATH"))) {
+char *pa_make_path_absolute(const char *p) {
+ char *r;
+ char *cwd;
- if (fn)
- pa_snprintf(s, l, "%s%c%s", e, PA_PATH_SEP_CHAR, fn);
- else
- pa_snprintf(s, l, "%s", e);
+ pa_assert(p);
- } else {
- char u[256];
+ if (pa_is_path_absolute(p))
+ return pa_xstrdup(p);
- if (fn)
- pa_snprintf(s, l, "%s%s%c%s", PA_USER_RUNTIME_PATH_PREFIX, pa_get_user_name(u, sizeof(u)), PA_PATH_SEP_CHAR, fn);
- else
- pa_snprintf(s, l, "%s%s", PA_USER_RUNTIME_PATH_PREFIX, pa_get_user_name(u, sizeof(u)));
- }
+ if (!(cwd = pa_getcwd()))
+ return pa_xstrdup(p);
+
+ r = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", cwd, p);
+ pa_xfree(cwd);
+ return r;
+}
+/* if fn is null return the PulseAudio run time path in s (~/.pulse)
+ * if fn is non-null and starts with / return fn
+ * otherwise append fn to the run time path and return it */
+static char *get_path(const char *fn, pa_bool_t rt) {
+ char *rtp;
-#ifdef OS_IS_WIN32
- {
- char buf[l];
- strcpy(buf, s);
- ExpandEnvironmentStrings(buf, s, l);
- }
-#endif
+ if (pa_is_path_absolute(fn))
+ return pa_xstrdup(fn);
- return s;
+ rtp = rt ? pa_get_runtime_dir() : pa_get_state_dir();
+
+ if (!rtp)
+ return NULL;
+
+ if (fn) {
+ char *r;
+ r = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", rtp, fn);
+ pa_xfree(rtp);
+ return r;
+ } else
+ return rtp;
+}
+
+char *pa_runtime_path(const char *fn) {
+ return get_path(fn, 1);
+}
+
+char *pa_state_path(const char *fn) {
+ return get_path(fn, 0);
}
/* Convert the string s to a signed integer in *ret_i */
@@ -1317,13 +1570,13 @@ static void c_locale_destroy(void) {
}
#endif
-int pa_atof(const char *s, float *ret_f) {
+int pa_atod(const char *s, double *ret_d) {
char *x = NULL;
- float f;
+ double f;
int r = 0;
pa_assert(s);
- pa_assert(ret_f);
+ pa_assert(ret_d);
/* This should be locale independent */
@@ -1338,22 +1591,18 @@ int pa_atof(const char *s, float *ret_f) {
if (c_locale) {
errno = 0;
- f = strtof_l(s, &x, c_locale);
+ f = strtod_l(s, &x, c_locale);
} else
#endif
{
errno = 0;
-#ifdef HAVE_STRTOF
- f = strtof(s, &x);
-#else
f = strtod(s, &x);
-#endif
}
if (!x || *x || errno != 0)
r = -1;
else
- *ret_f = f;
+ *ret_d = f;
return r;
}
@@ -1368,12 +1617,28 @@ int pa_snprintf(char *str, size_t size, const char *format, ...) {
pa_assert(format);
va_start(ap, format);
- ret = vsnprintf(str, size, format, ap);
+ ret = pa_vsnprintf(str, size, format, ap);
va_end(ap);
+ return ret;
+}
+
+/* Same as vsnprintf, but guarantees NUL-termination on every platform */
+int pa_vsnprintf(char *str, size_t size, const char *format, va_list ap) {
+ int ret;
+
+ pa_assert(str);
+ pa_assert(size > 0);
+ pa_assert(format);
+
+ ret = vsnprintf(str, size, format, ap);
+
str[size-1] = 0;
- return ret;
+ if (ret < 0)
+ ret = strlen(str);
+
+ return PA_MIN((int) size-1, ret);
}
/* Truncate the specified string, but guarantee that the string
@@ -1409,23 +1674,6 @@ char *pa_getcwd(void) {
}
}
-char *pa_make_path_absolute(const char *p) {
- char *r;
- char *cwd;
-
- pa_assert(p);
-
- if (p[0] == '/')
- return pa_xstrdup(p);
-
- if (!(cwd = pa_getcwd()))
- return pa_xstrdup(p);
-
- r = pa_sprintf_malloc("%s/%s", cwd, p);
- pa_xfree(cwd);
- return r;
-}
-
void *pa_will_need(const void *p, size_t l) {
#ifdef RLIMIT_MEMLOCK
struct rlimit rlim;
@@ -1507,3 +1755,291 @@ void pa_close_pipe(int fds[2]) {
fds[0] = fds[1] = -1;
}
+
+char *pa_readlink(const char *p) {
+ size_t l = 100;
+
+ for (;;) {
+ char *c;
+ ssize_t n;
+
+ c = pa_xnew(char, l);
+
+ if ((n = readlink(p, c, l-1)) < 0) {
+ pa_xfree(c);
+ return NULL;
+ }
+
+ if ((size_t) n < l-1) {
+ c[n] = 0;
+ return c;
+ }
+
+ pa_xfree(c);
+ l *= 2;
+ }
+}
+
+int pa_close_all(int except_fd, ...) {
+ va_list ap;
+ int n = 0, i, r;
+ int *p;
+
+ va_start(ap, except_fd);
+
+ if (except_fd >= 0)
+ for (n = 1; va_arg(ap, int) >= 0; n++)
+ ;
+
+ va_end(ap);
+
+ p = pa_xnew(int, n+1);
+
+ va_start(ap, except_fd);
+
+ i = 0;
+ if (except_fd >= 0) {
+ int fd;
+ p[i++] = except_fd;
+
+ while ((fd = va_arg(ap, int)) >= 0)
+ p[i++] = fd;
+ }
+ p[i] = -1;
+
+ va_end(ap);
+
+ r = pa_close_allv(p);
+ free(p);
+
+ return r;
+}
+
+int pa_close_allv(const int except_fds[]) {
+ struct rlimit rl;
+ int fd;
+ int saved_errno;
+
+#ifdef __linux__
+
+ DIR *d;
+
+ if ((d = opendir("/proc/self/fd"))) {
+
+ struct dirent *de;
+
+ while ((de = readdir(d))) {
+ pa_bool_t found;
+ long l;
+ char *e = NULL;
+ int i;
+
+ if (de->d_name[0] == '.')
+ continue;
+
+ errno = 0;
+ l = strtol(de->d_name, &e, 10);
+ if (errno != 0 || !e || *e) {
+ closedir(d);
+ errno = EINVAL;
+ return -1;
+ }
+
+ fd = (int) l;
+
+ if ((long) fd != l) {
+ closedir(d);
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (fd < 3)
+ continue;
+
+ if (fd == dirfd(d))
+ continue;
+
+ found = FALSE;
+ for (i = 0; except_fds[i] >= 0; i++)
+ if (except_fds[i] == fd) {
+ found = TRUE;
+ break;
+ }
+
+ if (found)
+ continue;
+
+ if (pa_close(fd) < 0) {
+ saved_errno = errno;
+ closedir(d);
+ errno = saved_errno;
+
+ return -1;
+ }
+ }
+
+ closedir(d);
+ return 0;
+ }
+
+#endif
+
+ if (getrlimit(RLIMIT_NOFILE, &rl) < 0)
+ return -1;
+
+ for (fd = 0; fd < (int) rl.rlim_max; fd++) {
+ int i;
+
+ if (fd <= 3)
+ continue;
+
+ for (i = 0; except_fds[i] >= 0; i++)
+ if (except_fds[i] == fd)
+ continue;
+
+ if (close(fd) < 0 && errno != EBADF)
+ return -1;
+ }
+
+ return 0;
+}
+
+int pa_unblock_sigs(int except, ...) {
+ va_list ap;
+ int n = 0, i, r;
+ int *p;
+
+ va_start(ap, except);
+
+ if (except >= 1)
+ for (n = 1; va_arg(ap, int) >= 0; n++)
+ ;
+
+ va_end(ap);
+
+ p = pa_xnew(int, n+1);
+
+ va_start(ap, except);
+
+ i = 0;
+ if (except >= 1) {
+ int sig;
+ p[i++] = except;
+
+ while ((sig = va_arg(ap, int)) >= 0)
+ p[i++] = sig;
+ }
+ p[i] = -1;
+
+ va_end(ap);
+
+ r = pa_unblock_sigsv(p);
+ pa_xfree(p);
+
+ return r;
+}
+
+int pa_unblock_sigsv(const int except[]) {
+ int i;
+ sigset_t ss;
+
+ if (sigemptyset(&ss) < 0)
+ return -1;
+
+ for (i = 0; except[i] > 0; i++)
+ if (sigaddset(&ss, except[i]) < 0)
+ return -1;
+
+ return sigprocmask(SIG_SETMASK, &ss, NULL);
+}
+
+int pa_reset_sigs(int except, ...) {
+ va_list ap;
+ int n = 0, i, r;
+ int *p;
+
+ va_start(ap, except);
+
+ if (except >= 1)
+ for (n = 1; va_arg(ap, int) >= 0; n++)
+ ;
+
+ va_end(ap);
+
+ p = pa_xnew(int, n+1);
+
+ va_start(ap, except);
+
+ i = 0;
+ if (except >= 1) {
+ p[i++] = except;
+
+ while ((p[i++] = va_arg(ap, int)) >= 0)
+ ;
+ }
+ p[i] = -1;
+
+ va_end(ap);
+
+ r = pa_reset_sigsv(p);
+ pa_xfree(p);
+
+ return r;
+}
+
+int pa_reset_sigsv(const int except[]) {
+ int sig;
+
+ for (sig = 1; sig < _NSIG; sig++) {
+ pa_bool_t reset = TRUE;
+
+ switch (sig) {
+ case SIGKILL:
+ case SIGSTOP:
+ reset = FALSE;
+ break;
+
+ default: {
+ int i;
+
+ for (i = 0; except[i] > 0; i++) {
+ if (sig == except[i]) {
+ reset = FALSE;
+ break;
+ }
+ }
+ }
+ }
+
+ if (reset) {
+ struct sigaction sa;
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = SIG_DFL;
+
+ /* On Linux the first two RT signals are reserved by
+ * glibc, and sigaction() will return EINVAL for them. */
+ if ((sigaction(sig, &sa, NULL) < 0))
+ if (errno != EINVAL)
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+void pa_set_env(const char *key, const char *value) {
+ pa_assert(key);
+ pa_assert(value);
+
+ putenv(pa_sprintf_malloc("%s=%s", key, value));
+}
+
+pa_bool_t pa_in_system_mode(void) {
+ const char *e;
+
+ if (!(e = getenv("PULSE_SYSTEM")))
+ return FALSE;
+
+ return !!atoi(e);
+}
diff --git a/src/pulsecore/core-util.h b/src/pulsecore/core-util.h
index 44c7574e..2ed81fc5 100644
--- a/src/pulsecore/core-util.h
+++ b/src/pulsecore/core-util.h
@@ -1,8 +1,6 @@
#ifndef foocoreutilhfoo
#define foocoreutilhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -30,10 +28,27 @@
#include <stdarg.h>
#include <stdio.h>
-#include <pulsecore/gccmacro.h>
+#ifdef HAVE_SYS_RESOURCE_H
+#include <sys/resource.h>
+#endif
+
+#include <pulse/gccmacro.h>
+#include <pulsecore/macro.h>
struct timeval;
+/* These resource limits are pretty new on Linux, let's define them
+ * here manually, in case the kernel is newer than the glibc */
+#if !defined(RLIMIT_NICE) && defined(__linux__)
+#define RLIMIT_NICE 13
+#endif
+#if !defined(RLIMIT_RTPRIO) && defined(__linux__)
+#define RLIMIT_RTPRIO 14
+#endif
+#if !defined(RLIMIT_RTTIME) && defined(__linux__)
+#define RLIMIT_RTTIME 15
+#endif
+
void pa_make_fd_nonblock(int fd);
void pa_make_fd_cloexec(int fd);
@@ -56,12 +71,27 @@ char *pa_strlcpy(char *b, const char *s, size_t l);
char *pa_parent_dir(const char *fn);
-void pa_make_realtime(void);
-void pa_raise_priority(void);
+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) {
+ return b ? "yes" : "no";
+}
+
+static inline const char *pa_strnull(const char *x) {
+ return x ? x : "(null)";
+}
+
+static inline const char *pa_strempty(const char *x) {
+ return x ? x : "";
+}
+
char *pa_split(const char *c, const char*delimiters, const char **state);
char *pa_split_spaces(const char *c, const char **state);
@@ -79,26 +109,32 @@ int pa_lock_fd(int fd, int b);
int pa_lock_lockfile(const char *fn);
int pa_unlock_lockfile(const char *fn, int fd);
-FILE *pa_open_config_file(const char *global, const char *local, const char *env, char **result, const char *mode);
-
char *pa_hexstr(const uint8_t* d, size_t dlength, char *s, size_t slength);
size_t pa_parsehex(const char *p, uint8_t *d, size_t dlength);
-int pa_startswith(const char *s, const char *pfx) PA_GCC_PURE;
-int pa_endswith(const char *s, const char *sfx) PA_GCC_PURE;
+pa_bool_t pa_startswith(const char *s, const char *pfx) PA_GCC_PURE;
+pa_bool_t pa_endswith(const char *s, const char *sfx) PA_GCC_PURE;
+
+FILE *pa_open_config_file(const char *global, const char *local, const char *env, char **result);
+char* pa_find_config_file(const char *global, const char *local, const char *env);
-char *pa_runtime_path(const char *fn, char *s, size_t l);
+char *pa_get_runtime_dir(void);
+char *pa_get_state_dir(void);
+char *pa_runtime_path(const char *fn);
+char *pa_state_path(const char *fn);
int pa_atoi(const char *s, int32_t *ret_i);
int pa_atou(const char *s, uint32_t *ret_u);
-int pa_atof(const char *s, float *ret_f);
+int pa_atod(const char *s, double *ret_d);
int pa_snprintf(char *str, size_t size, const char *format, ...);
+int pa_vsnprintf(char *str, size_t size, const char *format, va_list ap);
char *pa_truncate_utf8(char *c, size_t l);
char *pa_getcwd(void);
char *pa_make_path_absolute(const char *p);
+pa_bool_t pa_is_path_absolute(const char *p);
void *pa_will_need(const void *p, size_t l);
@@ -111,7 +147,7 @@ static inline unsigned pa_make_power_of_two(unsigned n) {
if (pa_is_power_of_two(n))
return n;
-
+
while (j) {
j = j >> 1;
n = n | j;
@@ -120,6 +156,32 @@ static inline unsigned pa_make_power_of_two(unsigned n) {
return n + 1;
}
+static inline unsigned pa_ulog2(unsigned n) {
+ unsigned r = 0;
+
+ while (n) {
+ r++;
+ n = n >> 1;
+ }
+
+ return r;
+}
+
void pa_close_pipe(int fds[2]);
+char *pa_readlink(const char *p);
+
+int pa_close_all(int except_fd, ...);
+int pa_close_allv(const int except_fds[]);
+int pa_unblock_sigs(int except, ...);
+int pa_unblock_sigsv(const int except[]);
+int pa_reset_sigs(int except, ...);
+int pa_reset_sigsv(const int except[]);
+
+void pa_set_env(const char *key, const char *value);
+
+pa_bool_t pa_in_system_mode(void);
+
+#define pa_streq(a,b) (!strcmp((a),(b)))
+
#endif
diff --git a/src/pulsecore/core.c b/src/pulsecore/core.c
index c5386522..b2638b10 100644
--- a/src/pulsecore/core.c
+++ b/src/pulsecore/core.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -72,7 +70,7 @@ pa_core* pa_core_new(pa_mainloop_api *m, int shared) {
pa_core* c;
pa_mempool *pool;
int j;
-
+
pa_assert(m);
if (shared) {
@@ -107,7 +105,7 @@ pa_core* pa_core_new(pa_mainloop_api *m, int shared) {
c->scache = NULL;
c->autoload_idxset = NULL;
c->autoload_hashmap = NULL;
- c->running_as_daemon = 0;
+ c->running_as_daemon = FALSE;
c->default_sample_spec.format = PA_SAMPLE_S16NE;
c->default_sample_spec.rate = 44100;
@@ -125,6 +123,7 @@ pa_core* pa_core_new(pa_mainloop_api *m, int shared) {
c->subscription_event_last = NULL;
c->mempool = pool;
+ pa_silence_cache_init(&c->silence_cache);
c->quit_event = NULL;
@@ -132,12 +131,12 @@ pa_core* pa_core_new(pa_mainloop_api *m, int shared) {
c->module_idle_time = 20;
c->scache_idle_time = 20;
- c->resample_method = PA_RESAMPLER_SPEEX_FLOAT_BASE;
-
- c->is_system_instance = 0;
- c->disallow_module_loading = 0;
- c->high_priority = 0;
+ c->resample_method = PA_RESAMPLER_SPEEX_FLOAT_BASE + 3;
+ c->disallow_module_loading = FALSE;
+ c->realtime_scheduling = FALSE;
+ c->realtime_priority = 5;
+ c->disable_remixing = FALSE;
for (j = 0; j < PA_CORE_HOOK_MAX; j++)
pa_hook_init(&c->hooks[j], c);
@@ -187,6 +186,7 @@ static void core_free(pa_object *o) {
pa_xfree(c->default_source_name);
pa_xfree(c->default_sink_name);
+ pa_silence_cache_done(&c->silence_cache);
pa_mempool_free(c->mempool);
pa_property_cleanup(c);
@@ -207,14 +207,18 @@ static void quit_callback(pa_mainloop_api*m, pa_time_event *e, PA_GCC_UNUSED con
void pa_core_check_quit(pa_core *c) {
pa_assert(c);
- if (!c->quit_event && c->exit_idle_time >= 0 && pa_idxset_size(c->clients) == 0) {
+ if (!c->quit_event &&
+ 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->quit_event = c->mainloop->time_new(c->mainloop, &tv, quit_callback, c);
+
} else if (c->quit_event && pa_idxset_size(c->clients) > 0) {
c->mainloop->time_free(c->quit_event);
c->quit_event = NULL;
}
}
-
diff --git a/src/pulsecore/core.h b/src/pulsecore/core.h
index dfa80f8d..d9ed46f6 100644
--- a/src/pulsecore/core.h
+++ b/src/pulsecore/core.h
@@ -1,8 +1,6 @@
#ifndef foocorehfoo
#define foocorehfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -35,6 +33,7 @@
#include <pulsecore/llist.h>
#include <pulsecore/hook-list.h>
#include <pulsecore/asyncmsgq.h>
+#include <pulsecore/sample-util.h>
typedef struct pa_core pa_core;
@@ -43,32 +42,38 @@ typedef struct pa_core pa_core;
#include <pulsecore/msgobject.h>
typedef enum pa_core_hook {
- PA_CORE_HOOK_SINK_NEW_POST,
+ PA_CORE_HOOK_SINK_NEW,
+ PA_CORE_HOOK_SINK_FIXATE,
+ PA_CORE_HOOK_SINK_PUT,
PA_CORE_HOOK_SINK_UNLINK,
PA_CORE_HOOK_SINK_UNLINK_POST,
PA_CORE_HOOK_SINK_STATE_CHANGED,
- PA_CORE_HOOK_SINK_DESCRIPTION_CHANGED,
- PA_CORE_HOOK_SOURCE_NEW_POST,
+ PA_CORE_HOOK_SINK_PROPLIST_CHANGED,
+ PA_CORE_HOOK_SOURCE_NEW,
+ PA_CORE_HOOK_SOURCE_FIXATE,
+ PA_CORE_HOOK_SOURCE_PUT,
PA_CORE_HOOK_SOURCE_UNLINK,
PA_CORE_HOOK_SOURCE_UNLINK_POST,
PA_CORE_HOOK_SOURCE_STATE_CHANGED,
- PA_CORE_HOOK_SOURCE_DESCRIPTION_CHANGED,
+ PA_CORE_HOOK_SOURCE_PROPLIST_CHANGED,
PA_CORE_HOOK_SINK_INPUT_NEW,
+ PA_CORE_HOOK_SINK_INPUT_FIXATE,
PA_CORE_HOOK_SINK_INPUT_PUT,
PA_CORE_HOOK_SINK_INPUT_UNLINK,
PA_CORE_HOOK_SINK_INPUT_UNLINK_POST,
PA_CORE_HOOK_SINK_INPUT_MOVE,
PA_CORE_HOOK_SINK_INPUT_MOVE_POST,
- PA_CORE_HOOK_SINK_INPUT_NAME_CHANGED,
PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED,
+ PA_CORE_HOOK_SINK_INPUT_PROPLIST_CHANGED,
PA_CORE_HOOK_SOURCE_OUTPUT_NEW,
+ PA_CORE_HOOK_SOURCE_OUTPUT_FIXATE,
PA_CORE_HOOK_SOURCE_OUTPUT_PUT,
PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK,
PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK_POST,
PA_CORE_HOOK_SOURCE_OUTPUT_MOVE,
PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_POST,
- PA_CORE_HOOK_SOURCE_OUTPUT_NAME_CHANGED,
PA_CORE_HOOK_SOURCE_OUTPUT_STATE_CHANGED,
+ PA_CORE_HOOK_SOURCE_OUTPUT_PROPLIST_CHANGED,
PA_CORE_HOOK_MAX
} pa_core_hook_t;
@@ -106,6 +111,7 @@ struct pa_core {
pa_subscription_event *subscription_event_last;
pa_mempool *mempool;
+ pa_silence_cache silence_cache;
int exit_idle_time, module_idle_time, scache_idle_time;
@@ -113,10 +119,11 @@ struct pa_core {
pa_time_event *scache_auto_unload_event;
- int disallow_module_loading, running_as_daemon;
+ pa_bool_t disallow_module_loading, running_as_daemon;
pa_resample_method_t resample_method;
- int is_system_instance;
- int high_priority;
+ pa_bool_t realtime_scheduling;
+ int realtime_priority;
+ pa_bool_t disable_remixing;
/* hooks */
pa_hook hooks[PA_CORE_HOOK_MAX];
diff --git a/src/pulsecore/creds.h b/src/pulsecore/creds.h
index 51dfc33d..c15c469b 100644
--- a/src/pulsecore/creds.h
+++ b/src/pulsecore/creds.h
@@ -1,8 +1,6 @@
#ifndef foocredshfoo
#define foocredshfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/dllmain.c b/src/pulsecore/dllmain.c
index 52cbf9e2..269de604 100644
--- a/src/pulsecore/dllmain.c
+++ b/src/pulsecore/dllmain.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/dynarray.c b/src/pulsecore/dynarray.c
index f5e73ed0..a1fcd8a3 100644
--- a/src/pulsecore/dynarray.c
+++ b/src/pulsecore/dynarray.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -88,7 +86,7 @@ unsigned pa_dynarray_append(pa_dynarray*a, void *p) {
unsigned i;
pa_assert(a);
-
+
i = a->n_entries;
pa_dynarray_put(a, i, p);
return i;
@@ -96,7 +94,7 @@ unsigned pa_dynarray_append(pa_dynarray*a, void *p) {
void *pa_dynarray_get(pa_dynarray*a, unsigned i) {
pa_assert(a);
-
+
if (i >= a->n_entries)
return NULL;
@@ -106,6 +104,6 @@ void *pa_dynarray_get(pa_dynarray*a, unsigned i) {
unsigned pa_dynarray_size(pa_dynarray*a) {
pa_assert(a);
-
+
return a->n_entries;
}
diff --git a/src/pulsecore/dynarray.h b/src/pulsecore/dynarray.h
index 0f222e10..82b42082 100644
--- a/src/pulsecore/dynarray.h
+++ b/src/pulsecore/dynarray.h
@@ -1,8 +1,6 @@
#ifndef foodynarrayhfoo
#define foodynarrayhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/endianmacros.h b/src/pulsecore/endianmacros.h
index 8f7cfade..26336918 100644
--- a/src/pulsecore/endianmacros.h
+++ b/src/pulsecore/endianmacros.h
@@ -1,8 +1,6 @@
#ifndef fooendianmacroshfoo
#define fooendianmacroshfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -47,12 +45,20 @@
#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 float PA_FLOAT32_SWAP(float x) {
+ uint32_t i = *(uint32_t*) &x;
+ i = PA_UINT32_SWAP(i);
+ return *(float*) &i;
+}
+
#define PA_MAYBE_INT16_SWAP(c,x) ((c) ? PA_INT32_SWAP(x) : x)
#define PA_MAYBE_UINT16_SWAP(c,x) ((c) ? PA_UINT32_SWAP(x) : x)
#define PA_MAYBE_INT32_SWAP(c,x) ((c) ? PA_INT32_SWAP(x) : x)
#define PA_MAYBE_UINT32_SWAP(c,x) ((c) ? PA_UINT32_SWAP(x) : x)
+#define PA_MAYBE_FLOAT32_SWAP(c,x) ((c) ? PA_FLOAT32_SWAP(x) : x)
+
#ifdef WORDS_BIGENDIAN
#define PA_INT16_FROM_LE(x) PA_INT16_SWAP(x)
#define PA_INT16_FROM_BE(x) ((int16_t)(x))
@@ -63,14 +69,23 @@
#define PA_UINT16_FROM_LE(x) PA_UINT16_SWAP(x)
#define PA_UINT16_FROM_BE(x) ((uint16_t)(x))
+ #define PA_UINT16_TO_LE(x) PA_UINT16_SWAP(x)
+ #define PA_UINT16_TO_BE(x) ((uint16_t)(x))
+
#define PA_INT32_FROM_LE(x) PA_INT32_SWAP(x)
#define PA_INT32_FROM_BE(x) ((int32_t)(x))
+ #define PA_INT32_TO_LE(x) PA_INT32_SWAP(x)
+ #define PA_INT32_TO_BE(x) ((int32_t)(x))
+
#define PA_UINT32_FROM_LE(x) PA_UINT32_SWAP(x)
#define PA_UINT32_FROM_BE(x) ((uint32_t)(x))
#define PA_UINT32_TO_LE(x) PA_UINT32_SWAP(x)
#define PA_UINT32_TO_BE(x) ((uint32_t)(x))
+
+ #define PA_FLOAT32_TO_LE(x) PA_FLOAT32_SWAP(x)
+ #define PA_FLOAT32_TO_BE(x) ((float) (x))
#else
#define PA_INT16_FROM_LE(x) ((int16_t)(x))
#define PA_INT16_FROM_BE(x) PA_INT16_SWAP(x)
@@ -81,14 +96,23 @@
#define PA_UINT16_FROM_LE(x) ((uint16_t)(x))
#define PA_UINT16_FROM_BE(x) PA_UINT16_SWAP(x)
+ #define PA_UINT16_TO_LE(x) ((uint16_t)(x))
+ #define PA_UINT16_TO_BE(x) PA_UINT16_SWAP(x)
+
#define PA_INT32_FROM_LE(x) ((int32_t)(x))
#define PA_INT32_FROM_BE(x) PA_INT32_SWAP(x)
+ #define PA_INT32_TO_LE(x) ((int32_t)(x))
+ #define PA_INT32_TO_BE(x) PA_INT32_SWAP(x)
+
#define PA_UINT32_FROM_LE(x) ((uint32_t)(x))
#define PA_UINT32_FROM_BE(x) PA_UINT32_SWAP(x)
#define PA_UINT32_TO_LE(x) ((uint32_t)(x))
#define PA_UINT32_TO_BE(x) PA_UINT32_SWAP(x)
+
+ #define PA_FLOAT32_TO_LE(x) ((float) (x))
+ #define PA_FLOAT32_TO_BE(x) PA_FLOAT32_SWAP(x)
#endif
#endif
diff --git a/src/pulsecore/envelope.c b/src/pulsecore/envelope.c
new file mode 100644
index 00000000..e2691611
--- /dev/null
+++ b/src/pulsecore/envelope.c
@@ -0,0 +1,781 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2007 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 <stdio.h>
+
+#include <pulse/sample.h>
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/endianmacros.h>
+#include <pulsecore/memchunk.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/flist.h>
+#include <pulsecore/semaphore.h>
+#include <pulsecore/g711.h>
+
+#include "envelope.h"
+
+/*
+ Envelope subsystem for applying linear interpolated volume
+ envelopes on audio data. If multiple enevelopes shall be applied
+ at the same time, the "minimum" envelope is determined and
+ applied.
+
+ Envelopes are defined in a statically allocated constant structure
+ pa_envelope_def. It may be activated using pa_envelope_add(). And
+ already active envelope may be replaced with pa_envelope_replace()
+ and removed with pa_envelope_remove().The combined "minimum"
+ envelope can be applied to audio data with pa_envelope_apply().
+
+ _apply() on one hand and _add()/_replace()/_remove() on the other
+ can be executed in seperate threads, in which case no locking is
+ used.
+*/
+
+PA_STATIC_FLIST_DECLARE(items, 0, pa_xfree);
+
+struct pa_envelope_item {
+ PA_LLIST_FIELDS(pa_envelope_item);
+ const pa_envelope_def *def;
+ pa_usec_t start_x;
+ union {
+ int32_t i;
+ float f;
+ } start_y;
+ unsigned j;
+};
+
+enum envelope_state {
+ STATE_VALID0,
+ STATE_VALID1,
+ STATE_READ0,
+ STATE_READ1,
+ STATE_WAIT0,
+ STATE_WAIT1,
+ STATE_WRITE0,
+ STATE_WRITE1
+};
+
+struct pa_envelope {
+ pa_sample_spec sample_spec;
+
+ PA_LLIST_HEAD(pa_envelope_item, items);
+
+ pa_atomic_t state;
+
+ size_t x;
+
+ struct {
+ unsigned n_points, n_allocated, n_current;
+
+ size_t *x;
+ union {
+ int32_t *i;
+ float *f;
+ } y;
+
+ size_t cached_dx;
+ int32_t cached_dy_i;
+ float cached_dy_dx;
+ pa_bool_t cached_valid;
+ } points[2];
+
+ pa_bool_t is_float;
+
+ pa_semaphore *semaphore;
+};
+
+pa_envelope *pa_envelope_new(const pa_sample_spec *ss) {
+ pa_envelope *e;
+ pa_assert(ss);
+
+ e = pa_xnew(pa_envelope, 1);
+
+ e->sample_spec = *ss;
+ PA_LLIST_HEAD_INIT(pa_envelope_item, e->items);
+
+ e->x = 0;
+
+ e->points[0].n_points = e->points[1].n_points = 0;
+ e->points[0].n_allocated = e->points[1].n_allocated = 0;
+ e->points[0].n_current = e->points[1].n_current = 0;
+ e->points[0].x = e->points[1].x = NULL;
+ e->points[0].y.i = e->points[1].y.i = NULL;
+ e->points[0].cached_valid = e->points[1].cached_valid = FALSE;
+
+ pa_atomic_store(&e->state, STATE_VALID0);
+
+ e->is_float =
+ ss->format == PA_SAMPLE_FLOAT32LE ||
+ ss->format == PA_SAMPLE_FLOAT32BE;
+
+ e->semaphore = pa_semaphore_new(0);
+
+ return e;
+}
+
+void pa_envelope_free(pa_envelope *e) {
+ pa_assert(e);
+
+ while (e->items)
+ pa_envelope_remove(e, e->items);
+
+ pa_xfree(e->points[0].x);
+ pa_xfree(e->points[1].x);
+ pa_xfree(e->points[0].y.i);
+ pa_xfree(e->points[1].y.i);
+
+ pa_semaphore_free(e->semaphore);
+
+ pa_xfree(e);
+}
+
+static int32_t linear_interpolate_int(pa_usec_t x1, int32_t _y1, pa_usec_t x2, int32_t y2, pa_usec_t x3) {
+ return (int32_t) (_y1 + (x3 - x1) * (float) (y2 - _y1) / (float) (x2 - x1));
+}
+
+static float linear_interpolate_float(pa_usec_t x1, float _y1, pa_usec_t x2, float y2, pa_usec_t x3) {
+ return _y1 + (x3 - x1) * (y2 - _y1) / (x2 - x1);
+}
+
+static int32_t item_get_int(pa_envelope_item *i, pa_usec_t x) {
+ pa_assert(i);
+
+ if (x <= i->start_x)
+ return i->start_y.i;
+
+ x -= i->start_x;
+
+ if (x <= i->def->points_x[0])
+ return linear_interpolate_int(0, i->start_y.i,
+ i->def->points_x[0], i->def->points_y.i[0], x);
+
+ if (x >= i->def->points_x[i->def->n_points-1])
+ return i->def->points_y.i[i->def->n_points-1];
+
+ pa_assert(i->j > 0);
+ pa_assert(i->def->points_x[i->j-1] <= x);
+ pa_assert(x < i->def->points_x[i->j]);
+
+ return linear_interpolate_int(i->def->points_x[i->j-1], i->def->points_y.i[i->j-1],
+ i->def->points_x[i->j], i->def->points_y.i[i->j], x);
+}
+
+static float item_get_float(pa_envelope_item *i, pa_usec_t x) {
+ pa_assert(i);
+
+ if (x <= i->start_x)
+ return i->start_y.f;
+
+ x -= i->start_x;
+
+ if (x <= i->def->points_x[0])
+ return linear_interpolate_float(0, i->start_y.f,
+ i->def->points_x[0], i->def->points_y.f[0], x);
+
+ if (x >= i->def->points_x[i->def->n_points-1])
+ return i->def->points_y.f[i->def->n_points-1];
+
+ pa_assert(i->j > 0);
+ pa_assert(i->def->points_x[i->j-1] <= x);
+ pa_assert(x < i->def->points_x[i->j]);
+
+ return linear_interpolate_float(i->def->points_x[i->j-1], i->def->points_y.f[i->j-1],
+ i->def->points_x[i->j], i->def->points_y.f[i->j], x);
+}
+
+static void envelope_begin_write(pa_envelope *e, int *v) {
+ enum envelope_state new_state, old_state;
+ pa_bool_t wait_sem;
+
+ pa_assert(e);
+ pa_assert(v);
+
+ for (;;) {
+ do {
+ wait_sem = FALSE;
+ old_state = pa_atomic_load(&e->state);
+
+ switch (old_state) {
+ case STATE_VALID0:
+ *v = 1;
+ new_state = STATE_WRITE0;
+ break;
+ case STATE_VALID1:
+ *v = 0;
+ new_state = STATE_WRITE1;
+ break;
+ case STATE_READ0:
+ new_state = STATE_WAIT0;
+ wait_sem = TRUE;
+ break;
+ case STATE_READ1:
+ new_state = STATE_WAIT1;
+ wait_sem = TRUE;
+ break;
+ default:
+ pa_assert_not_reached();
+ }
+ } while (!pa_atomic_cmpxchg(&e->state, old_state, new_state));
+
+ if (!wait_sem)
+ break;
+
+ pa_semaphore_wait(e->semaphore);
+ }
+}
+
+static pa_bool_t envelope_commit_write(pa_envelope *e, int v) {
+ enum envelope_state new_state, old_state;
+
+ pa_assert(e);
+
+ do {
+ old_state = pa_atomic_load(&e->state);
+
+ switch (old_state) {
+ case STATE_WRITE0:
+ pa_assert(v == 1);
+ new_state = STATE_VALID1;
+ break;
+ case STATE_WRITE1:
+ pa_assert(v == 0);
+ new_state = STATE_VALID0;
+ break;
+ case STATE_VALID0:
+ case STATE_VALID1:
+ case STATE_READ0:
+ case STATE_READ1:
+ return FALSE;
+ default:
+ pa_assert_not_reached();
+ }
+ } while (!pa_atomic_cmpxchg(&e->state, old_state, new_state));
+
+ return TRUE;
+}
+
+static void envelope_begin_read(pa_envelope *e, int *v) {
+ enum envelope_state new_state, old_state;
+ pa_assert(e);
+ pa_assert(v);
+
+ do {
+ old_state = pa_atomic_load(&e->state);
+
+ switch (old_state) {
+ case STATE_VALID0:
+ case STATE_WRITE0:
+ *v = 0;
+ new_state = STATE_READ0;
+ break;
+ case STATE_VALID1:
+ case STATE_WRITE1:
+ *v = 1;
+ new_state = STATE_READ1;
+ break;
+ default:
+ pa_assert_not_reached();
+ }
+ } while (!pa_atomic_cmpxchg(&e->state, old_state, new_state));
+}
+
+static void envelope_commit_read(pa_envelope *e, int v) {
+ enum envelope_state new_state, old_state;
+ pa_bool_t post_sem;
+
+ pa_assert(e);
+
+ do {
+ post_sem = FALSE;
+ old_state = pa_atomic_load(&e->state);
+
+ switch (old_state) {
+ case STATE_READ0:
+ pa_assert(v == 0);
+ new_state = STATE_VALID0;
+ break;
+ case STATE_READ1:
+ pa_assert(v == 1);
+ new_state = STATE_VALID1;
+ break;
+ case STATE_WAIT0:
+ pa_assert(v == 0);
+ new_state = STATE_VALID0;
+ post_sem = TRUE;
+ break;
+ case STATE_WAIT1:
+ pa_assert(v == 1);
+ new_state = STATE_VALID1;
+ post_sem = TRUE;
+ break;
+ default:
+ pa_assert_not_reached();
+ }
+ } while (!pa_atomic_cmpxchg(&e->state, old_state, new_state));
+
+ if (post_sem)
+ pa_semaphore_post(e->semaphore);
+}
+
+static void envelope_merge(pa_envelope *e, int v) {
+
+ e->points[v].n_points = 0;
+
+ if (e->items) {
+ pa_envelope_item *i;
+ pa_usec_t x = (pa_usec_t) -1;
+
+ for (i = e->items; i; i = i->next)
+ i->j = 0;
+
+ for (;;) {
+ pa_bool_t min_is_set;
+ pa_envelope_item *s = NULL;
+
+ /* Let's find the next spot on the X axis to analyze */
+ for (i = e->items; i; i = i->next) {
+
+ for (;;) {
+
+ if (i->j >= i->def->n_points)
+ break;
+
+ if ((x != (pa_usec_t) -1) && i->start_x + i->def->points_x[i->j] <= x) {
+ i->j++;
+ continue;
+ }
+
+ if (!s || (i->start_x + i->def->points_x[i->j] < s->start_x + s->def->points_x[s->j]))
+ s = i;
+
+ break;
+ }
+ }
+
+ if (!s)
+ break;
+
+ if (e->points[v].n_points >= e->points[v].n_allocated) {
+ e->points[v].n_allocated = PA_MAX(e->points[v].n_points*2, PA_ENVELOPE_POINTS_MAX);
+
+ e->points[v].x = pa_xrealloc(e->points[v].x, sizeof(size_t) * e->points[v].n_allocated);
+ e->points[v].y.i = pa_xrealloc(e->points[v].y.i, sizeof(int32_t) * e->points[v].n_allocated);
+ }
+
+ x = s->start_x + s->def->points_x[s->j];
+ e->points[v].x[e->points[v].n_points] = pa_usec_to_bytes(x, &e->sample_spec);
+
+ min_is_set = FALSE;
+
+ /* Now let's find the lowest value */
+ if (e->is_float) {
+ float min_f;
+
+ for (i = e->items; i; i = i->next) {
+ float f = item_get_float(i, x);
+ if (!min_is_set || f < min_f) {
+ min_f = f;
+ min_is_set = TRUE;
+ }
+ }
+
+ e->points[v].y.f[e->points[v].n_points] = min_f;
+ } else {
+ int32_t min_k;
+
+ for (i = e->items; i; i = i->next) {
+ int32_t k = item_get_int(i, x);
+ if (!min_is_set || k < min_k) {
+ min_k = k;
+ min_is_set = TRUE;
+ }
+ }
+
+ e->points[v].y.i[e->points[v].n_points] = min_k;
+ }
+
+ pa_assert_se(min_is_set);
+ e->points[v].n_points++;
+ }
+ }
+
+ e->points[v].n_current = 0;
+ e->points[v].cached_valid = FALSE;
+}
+
+pa_envelope_item *pa_envelope_add(pa_envelope *e, const pa_envelope_def *def) {
+ pa_envelope_item *i;
+ int v;
+
+ pa_assert(e);
+ pa_assert(def);
+ pa_assert(def->n_points > 0);
+
+ if (!(i = pa_flist_pop(PA_STATIC_FLIST_GET(items))))
+ i = pa_xnew(pa_envelope_item, 1);
+
+ i->def = def;
+
+ if (e->is_float)
+ i->start_y.f = def->points_y.f[0];
+ else
+ i->start_y.i = def->points_y.i[0];
+
+ PA_LLIST_PREPEND(pa_envelope_item, e->items, i);
+
+ envelope_begin_write(e, &v);
+
+ do {
+
+ i->start_x = pa_bytes_to_usec(e->x, &e->sample_spec);
+ envelope_merge(e, v);
+
+ } while (!envelope_commit_write(e, v));
+
+ return i;
+}
+
+pa_envelope_item *pa_envelope_replace(pa_envelope *e, pa_envelope_item *i, const pa_envelope_def *def) {
+ pa_usec_t x;
+ int v;
+
+ pa_assert(e);
+ pa_assert(i);
+ pa_assert(def->n_points > 0);
+
+ envelope_begin_write(e, &v);
+
+ for (;;) {
+ float saved_f;
+ int32_t saved_i;
+ uint64_t saved_start_x;
+ const pa_envelope_def *saved_def;
+
+ x = pa_bytes_to_usec(e->x, &e->sample_spec);
+
+ if (e->is_float) {
+ saved_f = i->start_y.f;
+ i->start_y.f = item_get_float(i, x);
+ } else {
+ saved_i = i->start_y.i;
+ i->start_y.i = item_get_int(i, x);
+ }
+
+ saved_start_x = i->start_x;
+ saved_def = i->def;
+
+ i->start_x = x;
+ i->def = def;
+
+ envelope_merge(e, v);
+
+ if (envelope_commit_write(e, v))
+ break;
+
+ i->start_x = saved_start_x;
+ i->def = saved_def;
+
+ if (e->is_float)
+ i->start_y.f = saved_f;
+ else
+ i->start_y.i = saved_i;
+ }
+
+ return i;
+}
+
+void pa_envelope_remove(pa_envelope *e, pa_envelope_item *i) {
+ int v;
+
+ pa_assert(e);
+ pa_assert(i);
+
+ PA_LLIST_REMOVE(pa_envelope_item, e->items, i);
+
+ if (pa_flist_push(PA_STATIC_FLIST_GET(items), i) < 0)
+ pa_xfree(i);
+
+ envelope_begin_write(e, &v);
+ do {
+ envelope_merge(e, v);
+ } while (!envelope_commit_write(e, v));
+}
+
+static int32_t linear_get_int(pa_envelope *e, int v) {
+ pa_assert(e);
+
+ /* The repeated division could be replaced by Bresenham, as an
+ * optimization */
+
+ if (e->x < e->points[v].x[0])
+ return e->points[v].y.i[0];
+
+ for (;;) {
+ if (e->points[v].n_current+1 >= e->points[v].n_points)
+ return e->points[v].y.i[e->points[v].n_points-1];
+
+ if (e->x < e->points[v].x[e->points[v].n_current+1])
+ break;
+
+ e->points[v].n_current++;
+ e->points[v].cached_valid = FALSE;
+ }
+
+ if (!e->points[v].cached_valid) {
+ e->points[v].cached_dx = e->points[v].x[e->points[v].n_current+1] - e->points[v].x[e->points[v].n_current];
+ e->points[v].cached_dy_i = e->points[v].y.i[e->points[v].n_current+1] - e->points[v].y.i[e->points[v].n_current];
+ e->points[v].cached_valid = TRUE;
+ }
+
+ return e->points[v].y.i[e->points[v].n_current] + (e->points[v].cached_dy_i * (int32_t) (e->x - e->points[v].x[e->points[v].n_current])) / (int32_t) e->points[v].cached_dx;
+}
+
+static float linear_get_float(pa_envelope *e, int v) {
+ pa_assert(e);
+
+ if (e->x < e->points[v].x[0])
+ return e->points[v].y.f[0];
+
+ for (;;) {
+ if (e->points[v].n_current+1 >= e->points[v].n_points)
+ return e->points[v].y.f[e->points[v].n_points-1];
+
+ if (e->x < e->points[v].x[e->points[v].n_current+1])
+ break;
+
+ e->points[v].n_current++;
+ e->points[v].cached_valid = FALSE;
+ }
+
+ if (!e->points[v].cached_valid) {
+ e->points[v].cached_dy_dx =
+ (e->points[v].y.f[e->points[v].n_current+1] - e->points[v].y.f[e->points[v].n_current]) /
+ (e->points[v].x[e->points[v].n_current+1] - e->points[v].x[e->points[v].n_current]);
+ e->points[v].cached_valid = TRUE;
+ }
+
+ return e->points[v].y.f[e->points[v].n_current] + (e->x - e->points[v].x[e->points[v].n_current]) * e->points[v].cached_dy_dx;
+}
+
+void pa_envelope_apply(pa_envelope *e, pa_memchunk *chunk) {
+ int v;
+
+ pa_assert(e);
+ pa_assert(chunk);
+
+ envelope_begin_read(e, &v);
+
+ if (e->points[v].n_points > 0) {
+ void *p;
+ size_t fs, n;
+
+ pa_memchunk_make_writable(chunk, 0);
+ p = (uint8_t*) pa_memblock_acquire(chunk->memblock) + chunk->index;
+ fs = pa_frame_size(&e->sample_spec);
+ n = chunk->length;
+
+ switch (e->sample_spec.format) {
+
+
+
+ case PA_SAMPLE_U8: {
+ uint8_t *t;
+
+ for (t = p; n > 0; n -= fs) {
+ int16_t factor = linear_get_int(e, v);
+ unsigned c;
+ e->x += fs;
+
+ for (c = 0; c < e->sample_spec.channels; c++, t++)
+ *t = (uint8_t) (((factor * ((int16_t) *t - 0x80)) / 0x10000) + 0x80);
+ }
+
+ break;
+ }
+
+ case PA_SAMPLE_ULAW: {
+ uint8_t *t;
+
+ for (t = p; n > 0; n -= fs) {
+ int16_t factor = linear_get_int(e, v);
+ unsigned c;
+ e->x += fs;
+
+ for (c = 0; c < e->sample_spec.channels; c++, t++) {
+ int16_t k = st_ulaw2linear16(*t);
+ *t = (uint8_t) st_14linear2ulaw(((factor * k) / 0x10000) >> 2);
+ }
+ }
+
+ break;
+ }
+
+ case PA_SAMPLE_ALAW: {
+ uint8_t *t;
+
+ for (t = p; n > 0; n -= fs) {
+ int16_t factor = linear_get_int(e, v);
+ unsigned c;
+ e->x += fs;
+
+ for (c = 0; c < e->sample_spec.channels; c++, t++) {
+ int16_t k = st_alaw2linear16(*t);
+ *t = (uint8_t) st_13linear2alaw(((factor * k) / 0x10000) >> 3);
+ }
+ }
+
+ break;
+ }
+
+ case PA_SAMPLE_S16NE: {
+ int16_t *t;
+
+ for (t = p; n > 0; n -= fs) {
+ int32_t factor = linear_get_int(e, v);
+ unsigned c;
+ e->x += fs;
+
+ for (c = 0; c < e->sample_spec.channels; c++, t++)
+ *t = (factor * *t) / 0x10000;
+ }
+
+ break;
+ }
+
+ case PA_SAMPLE_S16RE: {
+ int16_t *t;
+
+ for (t = p; n > 0; n -= fs) {
+ int32_t factor = linear_get_int(e, v);
+ unsigned c;
+ e->x += fs;
+
+ for (c = 0; c < e->sample_spec.channels; c++, t++) {
+ int16_t r = (factor * PA_INT16_SWAP(*t)) / 0x10000;
+ *t = PA_INT16_SWAP(r);
+ }
+ }
+
+ break;
+ }
+
+ case PA_SAMPLE_S32NE: {
+ int32_t *t;
+
+ for (t = p; n > 0; n -= fs) {
+ int32_t factor = linear_get_int(e, v);
+ unsigned c;
+ e->x += fs;
+
+ for (c = 0; c < e->sample_spec.channels; c++, t++)
+ *t = (int32_t) (((int64_t) factor * (int64_t) *t) / 0x10000);
+ }
+
+ break;
+ }
+
+ case PA_SAMPLE_S32RE: {
+ int32_t *t;
+
+ for (t = p; n > 0; n -= fs) {
+ int32_t factor = linear_get_int(e, v);
+ unsigned c;
+ e->x += fs;
+
+ for (c = 0; c < e->sample_spec.channels; c++, t++) {
+ int32_t r = (int32_t) (((int64_t) factor * (int64_t) PA_INT32_SWAP(*t)) / 0x10000);
+ *t = PA_INT32_SWAP(r);
+ }
+ }
+
+ break;
+ }
+
+ case PA_SAMPLE_FLOAT32NE: {
+ float *t;
+
+ for (t = p; n > 0; n -= fs) {
+ float factor = linear_get_float(e, v);
+ unsigned c;
+ e->x += fs;
+
+ for (c = 0; c < e->sample_spec.channels; c++, t++)
+ *t = *t * factor;
+ }
+
+ break;
+ }
+
+ case PA_SAMPLE_FLOAT32RE: {
+ float *t;
+
+ for (t = p; n > 0; n -= fs) {
+ float factor = linear_get_float(e, v);
+ unsigned c;
+ e->x += fs;
+
+ for (c = 0; c < e->sample_spec.channels; c++, t++) {
+ float r = PA_FLOAT32_SWAP(*t) * factor;
+ *t = PA_FLOAT32_SWAP(r);
+ }
+ }
+
+ break;
+ }
+
+ case PA_SAMPLE_MAX:
+ case PA_SAMPLE_INVALID:
+ pa_assert_not_reached();
+ }
+
+ pa_memblock_release(chunk->memblock);
+
+ e->x += chunk->length;
+ } else {
+ /* When we have no envelope to apply we reset our origin */
+ e->x = 0;
+ }
+
+ envelope_commit_read(e, v);
+}
+
+void pa_envelope_rewind(pa_envelope *e, size_t n_bytes) {
+ int v;
+
+ pa_assert(e);
+
+ envelope_begin_read(e, &v);
+
+ if (n_bytes < e->x)
+ e->x -= n_bytes;
+ else
+ e->x = 0;
+
+ e->points[v].n_current = 0;
+ e->points[v].cached_valid = FALSE;
+
+ envelope_commit_read(e, v);
+}
diff --git a/src/pulsecore/envelope.h b/src/pulsecore/envelope.h
new file mode 100644
index 00000000..5296415a
--- /dev/null
+++ b/src/pulsecore/envelope.h
@@ -0,0 +1,53 @@
+#ifndef foopulseenvelopehfoo
+#define foopulseenvelopehfoo
+
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2007 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 <pulsecore/memchunk.h>
+
+#include <pulse/sample.h>
+
+#define PA_ENVELOPE_POINTS_MAX 4U
+
+typedef struct pa_envelope pa_envelope;
+typedef struct pa_envelope_item pa_envelope_item;
+
+typedef struct pa_envelope_def {
+ unsigned n_points;
+
+ pa_usec_t points_x[PA_ENVELOPE_POINTS_MAX];
+ struct {
+ int32_t i[PA_ENVELOPE_POINTS_MAX];
+ float f[PA_ENVELOPE_POINTS_MAX];
+ } points_y;
+} pa_envelope_def;
+
+pa_envelope *pa_envelope_new(const pa_sample_spec *ss);
+void pa_envelope_free(pa_envelope *e);
+pa_envelope_item *pa_envelope_add(pa_envelope *e, const pa_envelope_def *def);
+pa_envelope_item *pa_envelope_replace(pa_envelope *e, pa_envelope_item *i, const pa_envelope_def *def);
+void pa_envelope_remove(pa_envelope *e, pa_envelope_item *i);
+void pa_envelope_apply(pa_envelope *e, pa_memchunk *chunk);
+void pa_envelope_rewind(pa_envelope *e, size_t n_bytes);
+
+#endif
diff --git a/src/pulsecore/esound.h b/src/pulsecore/esound.h
index ea6a5665..79322ae4 100644
--- a/src/pulsecore/esound.h
+++ b/src/pulsecore/esound.h
@@ -1,8 +1,6 @@
#ifndef fooesoundhfoo
#define fooesoundhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/fdsem.c b/src/pulsecore/fdsem.c
index 927bf00c..1531e3db 100644
--- a/src/pulsecore/fdsem.c
+++ b/src/pulsecore/fdsem.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -53,6 +51,10 @@
#define __NR_eventfd 284
#endif
+#if !defined(__NR_eventfd) && defined(__arm__)
+#define __NR_eventfd (__NR_SYSCALL_BASE+351)
+#endif
+
#if !defined(SYS_eventfd) && defined(__NR_eventfd)
#define SYS_eventfd __NR_eventfd
#endif
@@ -74,21 +76,19 @@ struct pa_fdsem {
#ifdef HAVE_EVENTFD
int efd;
#endif
- pa_atomic_t waiting;
- pa_atomic_t signalled;
- pa_atomic_t in_pipe;
+
+ pa_fdsem_data *data;
};
pa_fdsem *pa_fdsem_new(void) {
pa_fdsem *f;
- f = pa_xnew(pa_fdsem, 1);
+ f = pa_xmalloc(PA_ALIGN(sizeof(pa_fdsem)) + PA_ALIGN(sizeof(pa_fdsem_data)));
#ifdef HAVE_EVENTFD
if ((f->efd = eventfd(0)) >= 0) {
pa_make_fd_cloexec(f->efd);
f->fds[0] = f->fds[1] = -1;
-
} else
#endif
{
@@ -101,9 +101,57 @@ pa_fdsem *pa_fdsem_new(void) {
pa_make_fd_cloexec(f->fds[1]);
}
- pa_atomic_store(&f->waiting, 0);
- pa_atomic_store(&f->signalled, 0);
- pa_atomic_store(&f->in_pipe, 0);
+ f->data = (pa_fdsem_data*) ((uint8_t*) f + PA_ALIGN(sizeof(pa_fdsem)));
+
+ pa_atomic_store(&f->data->waiting, 0);
+ pa_atomic_store(&f->data->signalled, 0);
+ pa_atomic_store(&f->data->in_pipe, 0);
+
+ return f;
+}
+
+pa_fdsem *pa_fdsem_open_shm(pa_fdsem_data *data, int event_fd) {
+ pa_fdsem *f = NULL;
+
+ pa_assert(data);
+ pa_assert(event_fd >= 0);
+
+#ifdef HAVE_EVENTFD
+ f = pa_xnew(pa_fdsem, 1);
+
+ f->efd = event_fd;
+ pa_make_fd_cloexec(f->efd);
+ f->fds[0] = f->fds[1] = -1;
+ f->data = data;
+#endif
+
+ return f;
+}
+
+pa_fdsem *pa_fdsem_new_shm(pa_fdsem_data *data, int* event_fd) {
+ pa_fdsem *f = NULL;
+
+ pa_assert(data);
+ pa_assert(event_fd);
+
+#ifdef HAVE_EVENTFD
+
+ f = pa_xnew(pa_fdsem, 1);
+
+ if ((f->efd = eventfd(0)) < 0) {
+ pa_xfree(f);
+ return NULL;
+ }
+
+ pa_make_fd_cloexec(f->efd);
+ f->fds[0] = f->fds[1] = -1;
+ f->data = data;
+
+ pa_atomic_store(&f->data->waiting, 0);
+ pa_atomic_store(&f->data->signalled, 0);
+ pa_atomic_store(&f->data->in_pipe, 0);
+
+#endif
return f;
}
@@ -124,7 +172,7 @@ static void flush(pa_fdsem *f) {
ssize_t r;
pa_assert(f);
- if (pa_atomic_load(&f->in_pipe) <= 0)
+ if (pa_atomic_load(&f->data->in_pipe) <= 0)
return;
do {
@@ -147,19 +195,19 @@ static void flush(pa_fdsem *f) {
continue;
}
- } while (pa_atomic_sub(&f->in_pipe, r) > r);
+ } while (pa_atomic_sub(&f->data->in_pipe, r) > r);
}
void pa_fdsem_post(pa_fdsem *f) {
pa_assert(f);
- if (pa_atomic_cmpxchg(&f->signalled, 0, 1)) {
+ if (pa_atomic_cmpxchg(&f->data->signalled, 0, 1)) {
- if (pa_atomic_load(&f->waiting)) {
+ if (pa_atomic_load(&f->data->waiting)) {
ssize_t r;
char x = 'x';
- pa_atomic_inc(&f->in_pipe);
+ pa_atomic_inc(&f->data->in_pipe);
for (;;) {
@@ -190,12 +238,12 @@ void pa_fdsem_wait(pa_fdsem *f) {
flush(f);
- if (pa_atomic_cmpxchg(&f->signalled, 1, 0))
+ if (pa_atomic_cmpxchg(&f->data->signalled, 1, 0))
return;
- pa_atomic_inc(&f->waiting);
+ pa_atomic_inc(&f->data->waiting);
- while (!pa_atomic_cmpxchg(&f->signalled, 1, 0)) {
+ while (!pa_atomic_cmpxchg(&f->data->signalled, 1, 0)) {
char x[10];
ssize_t r;
@@ -217,10 +265,10 @@ void pa_fdsem_wait(pa_fdsem *f) {
continue;
}
- pa_atomic_sub(&f->in_pipe, r);
+ pa_atomic_sub(&f->data->in_pipe, r);
}
- pa_assert_se(pa_atomic_dec(&f->waiting) >= 1);
+ pa_assert_se(pa_atomic_dec(&f->data->waiting) >= 1);
}
int pa_fdsem_try(pa_fdsem *f) {
@@ -228,7 +276,7 @@ int pa_fdsem_try(pa_fdsem *f) {
flush(f);
- if (pa_atomic_cmpxchg(&f->signalled, 1, 0))
+ if (pa_atomic_cmpxchg(&f->data->signalled, 1, 0))
return 1;
return 0;
@@ -250,13 +298,13 @@ int pa_fdsem_before_poll(pa_fdsem *f) {
flush(f);
- if (pa_atomic_cmpxchg(&f->signalled, 1, 0))
+ if (pa_atomic_cmpxchg(&f->data->signalled, 1, 0))
return -1;
- pa_atomic_inc(&f->waiting);
+ pa_atomic_inc(&f->data->waiting);
- if (pa_atomic_cmpxchg(&f->signalled, 1, 0)) {
- pa_assert_se(pa_atomic_dec(&f->waiting) >= 1);
+ if (pa_atomic_cmpxchg(&f->data->signalled, 1, 0)) {
+ pa_assert_se(pa_atomic_dec(&f->data->waiting) >= 1);
return -1;
}
return 0;
@@ -265,11 +313,11 @@ int pa_fdsem_before_poll(pa_fdsem *f) {
int pa_fdsem_after_poll(pa_fdsem *f) {
pa_assert(f);
- pa_assert_se(pa_atomic_dec(&f->waiting) >= 1);
+ pa_assert_se(pa_atomic_dec(&f->data->waiting) >= 1);
flush(f);
- if (pa_atomic_cmpxchg(&f->signalled, 1, 0))
+ if (pa_atomic_cmpxchg(&f->data->signalled, 1, 0))
return 1;
return 0;
diff --git a/src/pulsecore/fdsem.h b/src/pulsecore/fdsem.h
index f38ef205..48a77c49 100644
--- a/src/pulsecore/fdsem.h
+++ b/src/pulsecore/fdsem.h
@@ -1,8 +1,6 @@
#ifndef foopulsefdsemhfoo
#define foopulsefdsemhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -33,7 +31,15 @@
typedef struct pa_fdsem pa_fdsem;
+typedef struct pa_fdsem_data {
+ pa_atomic_t waiting;
+ pa_atomic_t signalled;
+ pa_atomic_t in_pipe;
+} pa_fdsem_data;
+
pa_fdsem *pa_fdsem_new(void);
+pa_fdsem *pa_fdsem_open_shm(pa_fdsem_data *data, int event_fd);
+pa_fdsem *pa_fdsem_new_shm(pa_fdsem_data *data, int* event_fd);
void pa_fdsem_free(pa_fdsem *f);
void pa_fdsem_post(pa_fdsem *f);
diff --git a/src/pulsecore/ffmpeg/avcodec.h b/src/pulsecore/ffmpeg/avcodec.h
index bf8b84ea..696fc986 100644
--- a/src/pulsecore/ffmpeg/avcodec.h
+++ b/src/pulsecore/ffmpeg/avcodec.h
@@ -43,7 +43,7 @@
static inline void av_freep(void *k) {
void **p = k;
-
+
if (p) {
free(*p);
*p = NULL;
diff --git a/src/pulsecore/flist.c b/src/pulsecore/flist.c
index d9740777..f166ee33 100644
--- a/src/pulsecore/flist.c
+++ b/src/pulsecore/flist.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/flist.h b/src/pulsecore/flist.h
index daf0fec4..c040667d 100644
--- a/src/pulsecore/flist.h
+++ b/src/pulsecore/flist.h
@@ -1,8 +1,6 @@
#ifndef foopulseflisthfoo
#define foopulseflisthfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -25,9 +23,9 @@
***/
#include <pulse/def.h>
+#include <pulse/gccmacro.h>
#include <pulsecore/once.h>
-#include <pulsecore/gccmacro.h>
/* A multiple-reader multipler-write lock-free free list implementation */
diff --git a/src/pulsecore/gccmacro.h b/src/pulsecore/gccmacro.h
deleted file mode 100644
index e9f0d093..00000000
--- a/src/pulsecore/gccmacro.h
+++ /dev/null
@@ -1,80 +0,0 @@
-#ifndef foopulsegccmacrohfoo
-#define foopulsegccmacrohfoo
-
-/* $Id$ */
-
-/***
- 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 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 __GNUC__
-#define PA_GCC_PRINTF_ATTR(a,b) __attribute__ ((format (printf, a, b)))
-#else
-/** If we're in GNU C, use some magic for detecting invalid format strings */
-#define PA_GCC_PRINTF_ATTR(a,b)
-#endif
-
-#if defined(__GNUC__) && (__GNUC__ >= 4)
-#define PA_GCC_SENTINEL __attribute__ ((sentinel))
-#else
-/** Macro for usage of GCC's sentinel compilation warnings */
-#define PA_GCC_SENTINEL
-#endif
-
-#ifdef __GNUC__
-#define PA_GCC_NORETURN __attribute__((noreturn))
-#else
-/** Macro for no-return functions */
-#define PA_GCC_NORETURN
-#endif
-
-#ifdef __GNUC__
-#define PA_GCC_UNUSED __attribute__ ((unused))
-#else
-/** Macro for not used parameter */
-#define PA_GCC_UNUSED
-#endif
-
-#ifdef __GNUC__
-#define PA_GCC_DESTRUCTOR __attribute__ ((destructor))
-#else
-/** Call this function when process terminates */
-#define PA_GCC_DESTRUCTOR
-#endif
-
-#ifndef PA_GCC_PURE
-#ifdef __GNUCC__
-#define PA_GCC_PURE __attribute__ ((pure))
-#else
-/** This function's return value depends only the arguments list and global state **/
-#define PA_GCC_PURE
-#endif
-#endif
-
-#ifndef PA_GCC_CONST
-#ifdef __GNUCC__
-#define PA_GCC_CONST __attribute__ ((const))
-#else
-/** This function's return value depends only the arguments list (stricter version of PA_GCC_PURE) **/
-#define PA_GCC_CONST
-#endif
-#endif
-
-#endif
diff --git a/src/pulsecore/hashmap.c b/src/pulsecore/hashmap.c
index 8366181c..b7f4109b 100644
--- a/src/pulsecore/hashmap.c
+++ b/src/pulsecore/hashmap.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -71,7 +69,7 @@ pa_hashmap *pa_hashmap_new(pa_hash_func_t hash_func, pa_compare_func_t compare_f
return h;
}
-static void remove(pa_hashmap *h, struct hashmap_entry *e) {
+static void remove_entry(pa_hashmap *h, struct hashmap_entry *e) {
pa_assert(h);
pa_assert(e);
@@ -93,7 +91,7 @@ static void remove(pa_hashmap *h, struct hashmap_entry *e) {
if (pa_flist_push(PA_STATIC_FLIST_GET(entries), e) < 0)
pa_xfree(e);
-
+
h->n_entries--;
}
@@ -103,7 +101,7 @@ void pa_hashmap_free(pa_hashmap*h, void (*free_func)(void *p, void *userdata), v
while (h->first_entry) {
if (free_func)
free_func(h->first_entry->value, userdata);
- remove(h, h->first_entry);
+ remove_entry(h, h->first_entry);
}
pa_xfree(h->data);
@@ -134,7 +132,7 @@ int pa_hashmap_put(pa_hashmap *h, const void *key, void *value) {
if (!(e = pa_flist_pop(PA_STATIC_FLIST_GET(entries))))
e = pa_xnew(struct hashmap_entry, 1);
-
+
e->hash = hash;
e->key = key;
e->value = value;
@@ -182,7 +180,7 @@ void* pa_hashmap_remove(pa_hashmap *h, const void *key) {
return NULL;
data = e->value;
- remove(h, e);
+ remove_entry(h, e);
return data;
}
@@ -191,24 +189,36 @@ unsigned pa_hashmap_size(pa_hashmap *h) {
}
void *pa_hashmap_iterate(pa_hashmap *h, void **state, const void **key) {
+ struct hashmap_entry *e;
+
pa_assert(h);
pa_assert(state);
- if (!*state)
- *state = h->first_entry;
+ if (*state == (void*) -1)
+ goto at_end;
+
+ if ((!*state && !h->first_entry))
+ goto at_end;
+
+ e = *state ? *state : h->first_entry;
+
+ if (e->next)
+ *state = e->next;
else
- *state = ((struct hashmap_entry*) *state)->next;
+ *state = (void*) -1;
- if (!*state) {
- if (key)
- *key = NULL;
- return NULL;
- }
+ if (key)
+ *key = e->key;
+
+ return e->value;
+
+at_end:
+ *state = (void *) -1;
if (key)
- *key = ((struct hashmap_entry*) *state)->key;
+ *key = NULL;
- return ((struct hashmap_entry*) *state)->value;
+ return NULL;
}
void* pa_hashmap_steal_first(pa_hashmap *h) {
@@ -220,7 +230,7 @@ void* pa_hashmap_steal_first(pa_hashmap *h) {
return NULL;
data = h->first_entry->value;
- remove(h, h->first_entry);
+ remove_entry(h, h->first_entry);
return data;
}
diff --git a/src/pulsecore/hashmap.h b/src/pulsecore/hashmap.h
index 98df4502..a4505c4c 100644
--- a/src/pulsecore/hashmap.h
+++ b/src/pulsecore/hashmap.h
@@ -1,8 +1,6 @@
#ifndef foohashmaphfoo
#define foohashmaphfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -51,9 +49,10 @@ unsigned pa_hashmap_size(pa_hashmap *h);
/* May be used to iterate through the hashmap. Initially the opaque
pointer *state has to be set to NULL. The hashmap may not be
- modified during iteration. The key of the entry is returned in
- *key, if key is non-NULL. After the last entry in the hashmap NULL
- is returned. */
+ modified during iteration -- except for deleting the current entry
+ via pa_hashmap_remove(). The key of the entry is returned in *key,
+ if key is non-NULL. After the last entry in the hashmap NULL is
+ returned. */
void *pa_hashmap_iterate(pa_hashmap *h, void **state, const void**key);
void *pa_hashmap_steal_first(pa_hashmap *h);
diff --git a/src/pulsecore/hook-list.c b/src/pulsecore/hook-list.c
index 3a6874c4..0aac4759 100644
--- a/src/pulsecore/hook-list.c
+++ b/src/pulsecore/hook-list.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -33,8 +31,7 @@ void pa_hook_init(pa_hook *hook, void *data) {
pa_assert(hook);
PA_LLIST_HEAD_INIT(pa_hook_slot, hook->slots);
- hook->last = NULL;
- hook->n_dead = hook->firing = 0;
+ hook->n_dead = hook->n_firing = 0;
hook->data = data;
}
@@ -42,9 +39,6 @@ static void slot_free(pa_hook *hook, pa_hook_slot *slot) {
pa_assert(hook);
pa_assert(slot);
- if (hook->last == slot)
- hook->last = slot->prev;
-
PA_LLIST_REMOVE(pa_hook_slot, hook->slots, slot);
pa_xfree(slot);
@@ -52,7 +46,7 @@ static void slot_free(pa_hook *hook, pa_hook_slot *slot) {
void pa_hook_free(pa_hook *hook) {
pa_assert(hook);
- pa_assert(!hook->firing);
+ pa_assert(hook->n_firing == 0);
while (hook->slots)
slot_free(hook, hook->slots);
@@ -60,19 +54,26 @@ void pa_hook_free(pa_hook *hook) {
pa_hook_init(hook, NULL);
}
-pa_hook_slot* pa_hook_connect(pa_hook *hook, pa_hook_cb_t cb, void *data) {
- pa_hook_slot *slot;
+pa_hook_slot* pa_hook_connect(pa_hook *hook, pa_hook_priority_t prio, pa_hook_cb_t cb, void *data) {
+ pa_hook_slot *slot, *where, *prev;
pa_assert(cb);
slot = pa_xnew(pa_hook_slot, 1);
slot->hook = hook;
- slot->dead = 0;
+ slot->dead = FALSE;
slot->callback = cb;
slot->data = data;
+ slot->priority = prio;
- PA_LLIST_INSERT_AFTER(pa_hook_slot, hook->slots, hook->last, slot);
- hook->last = slot;
+ prev = NULL;
+ for (where = hook->slots; where; where = where->next) {
+ if (prio < where->priority)
+ break;
+ prev = where;
+ }
+
+ PA_LLIST_INSERT_AFTER(pa_hook_slot, hook->slots, prev, slot);
return slot;
}
@@ -81,8 +82,8 @@ void pa_hook_slot_free(pa_hook_slot *slot) {
pa_assert(slot);
pa_assert(!slot->dead);
- if (slot->hook->firing > 0) {
- slot->dead = 1;
+ if (slot->hook->n_firing > 0) {
+ slot->dead = TRUE;
slot->hook->n_dead++;
} else
slot_free(slot->hook, slot);
@@ -94,7 +95,7 @@ pa_hook_result_t pa_hook_fire(pa_hook *hook, void *data) {
pa_assert(hook);
- hook->firing ++;
+ hook->n_firing ++;
for (slot = hook->slots; slot; slot = slot->next) {
if (slot->dead)
@@ -104,7 +105,8 @@ pa_hook_result_t pa_hook_fire(pa_hook *hook, void *data) {
break;
}
- hook->firing --;
+ hook->n_firing --;
+ pa_assert(hook->n_firing >= 0);
for (slot = hook->slots; hook->n_dead > 0 && slot; slot = next) {
next = slot->next;
@@ -115,6 +117,7 @@ pa_hook_result_t pa_hook_fire(pa_hook *hook, void *data) {
}
}
+ pa_assert(hook->n_dead == 0);
+
return result;
}
-
diff --git a/src/pulsecore/hook-list.h b/src/pulsecore/hook-list.h
index b3bd600a..cf85aca5 100644
--- a/src/pulsecore/hook-list.h
+++ b/src/pulsecore/hook-list.h
@@ -1,8 +1,6 @@
#ifndef foohooklistfoo
#define foohooklistfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -24,9 +22,10 @@
USA.
***/
-#include <pulsecore/llist.h>
#include <pulse/xmalloc.h>
-#include <pulsecore/gccmacro.h>
+#include <pulse/gccmacro.h>
+
+#include <pulsecore/llist.h>
typedef struct pa_hook_slot pa_hook_slot;
typedef struct pa_hook pa_hook;
@@ -37,14 +36,21 @@ typedef enum pa_hook_result {
PA_HOOK_CANCEL = -1
} pa_hook_result_t;
+typedef enum pa_hook_priority {
+ PA_HOOK_EARLY = -100,
+ PA_HOOK_NORMAL = 0,
+ PA_HOOK_LATE = 100
+} pa_hook_priority_t;
+
typedef pa_hook_result_t (*pa_hook_cb_t)(
void *hook_data,
void *call_data,
void *slot_data);
struct pa_hook_slot {
- int dead;
+ pa_bool_t dead;
pa_hook *hook;
+ pa_hook_priority_t priority;
pa_hook_cb_t callback;
void *data;
PA_LLIST_FIELDS(pa_hook_slot);
@@ -52,8 +58,7 @@ struct pa_hook_slot {
struct pa_hook {
PA_LLIST_HEAD(pa_hook_slot, slots);
- pa_hook_slot *last;
- int firing, n_dead;
+ int n_firing, n_dead;
void *data;
};
@@ -61,7 +66,7 @@ struct pa_hook {
void pa_hook_init(pa_hook *hook, void *data);
void pa_hook_free(pa_hook *hook);
-pa_hook_slot* pa_hook_connect(pa_hook *hook, pa_hook_cb_t, void *data);
+pa_hook_slot* pa_hook_connect(pa_hook *hook, pa_hook_priority_t prio, pa_hook_cb_t cb, void *data);
void pa_hook_slot_free(pa_hook_slot *slot);
pa_hook_result_t pa_hook_fire(pa_hook *hook, void *data);
diff --git a/src/pulsecore/idxset.c b/src/pulsecore/idxset.c
index 773d7d3a..7c9520a4 100644
--- a/src/pulsecore/idxset.c
+++ b/src/pulsecore/idxset.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -156,7 +154,7 @@ static void extend_array(pa_idxset *s, uint32_t idx) {
static struct idxset_entry** array_index(pa_idxset*s, uint32_t idx) {
pa_assert(s);
-
+
if (idx >= s->start_index + s->array_size)
return NULL;
@@ -240,7 +238,7 @@ void* pa_idxset_get_by_index(pa_idxset*s, uint32_t idx) {
void* pa_idxset_get_by_data(pa_idxset*s, const void *p, uint32_t *idx) {
unsigned h;
struct idxset_entry *e;
-
+
pa_assert(s);
pa_assert(p);
@@ -259,7 +257,7 @@ void* pa_idxset_get_by_data(pa_idxset*s, const void *p, uint32_t *idx) {
static void remove_entry(pa_idxset *s, struct idxset_entry *e) {
struct idxset_entry **a;
-
+
pa_assert(s);
pa_assert(e);
@@ -338,7 +336,7 @@ void* pa_idxset_remove_by_data(pa_idxset*s, const void *data, uint32_t *idx) {
void* pa_idxset_rrobin(pa_idxset *s, uint32_t *idx) {
struct idxset_entry **a, *e = NULL;
-
+
pa_assert(s);
pa_assert(idx);
@@ -368,7 +366,7 @@ void* pa_idxset_first(pa_idxset *s, uint32_t *idx) {
void *pa_idxset_next(pa_idxset *s, uint32_t *idx) {
struct idxset_entry **a, *e = NULL;
-
+
pa_assert(s);
pa_assert(idx);
@@ -386,7 +384,7 @@ void *pa_idxset_next(pa_idxset *s, uint32_t *idx) {
int pa_idxset_foreach(pa_idxset*s, int (*func)(void *p, uint32_t idx, int *del, void*userdata), void *userdata) {
struct idxset_entry *e;
-
+
pa_assert(s);
pa_assert(func);
@@ -411,13 +409,13 @@ int pa_idxset_foreach(pa_idxset*s, int (*func)(void *p, uint32_t idx, int *del,
unsigned pa_idxset_size(pa_idxset*s) {
pa_assert(s);
-
+
return s->n_entries;
}
int pa_idxset_isempty(pa_idxset *s) {
pa_assert(s);
-
+
return s->n_entries == 0;
}
diff --git a/src/pulsecore/idxset.h b/src/pulsecore/idxset.h
index 5b55cec2..0a4e528e 100644
--- a/src/pulsecore/idxset.h
+++ b/src/pulsecore/idxset.h
@@ -1,8 +1,6 @@
#ifndef fooidxsethfoo
#define fooidxsethfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/inet_ntop.c b/src/pulsecore/inet_ntop.c
index 041bc09b..87551232 100644
--- a/src/pulsecore/inet_ntop.c
+++ b/src/pulsecore/inet_ntop.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -27,7 +25,6 @@
#include <stdio.h>
#include <errno.h>
-#include <assert.h>
#ifndef HAVE_INET_NTOP
diff --git a/src/pulsecore/inet_pton.c b/src/pulsecore/inet_pton.c
index 7272e459..d191e550 100644
--- a/src/pulsecore/inet_pton.c
+++ b/src/pulsecore/inet_pton.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -27,7 +25,6 @@
#include <stdio.h>
#include <errno.h>
-#include <assert.h>
#ifndef HAVE_INET_PTON
diff --git a/src/pulsecore/iochannel.c b/src/pulsecore/iochannel.c
index 01f17ab3..b40c9815 100644
--- a/src/pulsecore/iochannel.c
+++ b/src/pulsecore/iochannel.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -267,9 +265,11 @@ ssize_t pa_iochannel_write_with_creds(pa_iochannel*io, const void*data, size_t l
ssize_t r;
struct msghdr mh;
struct iovec iov;
- uint8_t cmsg_data[CMSG_SPACE(sizeof(struct ucred))];
+ union {
+ struct cmsghdr hdr;
+ uint8_t data[CMSG_SPACE(sizeof(struct ucred))];
+ } cmsg;
struct ucred *u;
- struct cmsghdr *cmsg;
pa_assert(io);
pa_assert(data);
@@ -280,13 +280,12 @@ ssize_t pa_iochannel_write_with_creds(pa_iochannel*io, const void*data, size_t l
iov.iov_base = (void*) data;
iov.iov_len = l;
- memset(cmsg_data, 0, sizeof(cmsg_data));
- cmsg = (struct cmsghdr*) cmsg_data;
- cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred));
- cmsg->cmsg_level = SOL_SOCKET;
- cmsg->cmsg_type = SCM_CREDENTIALS;
+ memset(&cmsg, 0, sizeof(cmsg));
+ cmsg.hdr.cmsg_len = CMSG_LEN(sizeof(struct ucred));
+ cmsg.hdr.cmsg_level = SOL_SOCKET;
+ cmsg.hdr.cmsg_type = SCM_CREDENTIALS;
- u = (struct ucred*) CMSG_DATA(cmsg);
+ u = (struct ucred*) CMSG_DATA(&cmsg.hdr);
u->pid = getpid();
if (ucred) {
@@ -302,8 +301,8 @@ ssize_t pa_iochannel_write_with_creds(pa_iochannel*io, const void*data, size_t l
mh.msg_namelen = 0;
mh.msg_iov = &iov;
mh.msg_iovlen = 1;
- mh.msg_control = cmsg_data;
- mh.msg_controllen = sizeof(cmsg_data);
+ mh.msg_control = &cmsg;
+ mh.msg_controllen = sizeof(cmsg);
mh.msg_flags = 0;
if ((r = sendmsg(io->ofd, &mh, MSG_NOSIGNAL)) >= 0) {
@@ -318,7 +317,10 @@ ssize_t pa_iochannel_read_with_creds(pa_iochannel*io, void*data, size_t l, pa_cr
ssize_t r;
struct msghdr mh;
struct iovec iov;
- uint8_t cmsg_data[CMSG_SPACE(sizeof(struct ucred))];
+ union {
+ struct cmsghdr hdr;
+ uint8_t data[CMSG_SPACE(sizeof(struct ucred))];
+ } cmsg;
pa_assert(io);
pa_assert(data);
@@ -331,28 +333,28 @@ ssize_t pa_iochannel_read_with_creds(pa_iochannel*io, void*data, size_t l, pa_cr
iov.iov_base = data;
iov.iov_len = l;
- memset(cmsg_data, 0, sizeof(cmsg_data));
+ memset(&cmsg, 0, sizeof(cmsg));
memset(&mh, 0, sizeof(mh));
mh.msg_name = NULL;
mh.msg_namelen = 0;
mh.msg_iov = &iov;
mh.msg_iovlen = 1;
- mh.msg_control = cmsg_data;
- mh.msg_controllen = sizeof(cmsg_data);
+ mh.msg_control = &cmsg;
+ mh.msg_controllen = sizeof(cmsg);
mh.msg_flags = 0;
if ((r = recvmsg(io->ifd, &mh, 0)) >= 0) {
- struct cmsghdr *cmsg;
+ struct cmsghdr *cmh;
*creds_valid = 0;
- for (cmsg = CMSG_FIRSTHDR(&mh); cmsg; cmsg = CMSG_NXTHDR(&mh, cmsg)) {
+ for (cmh = CMSG_FIRSTHDR(&mh); cmh; cmh = CMSG_NXTHDR(&mh, cmh)) {
- if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_CREDENTIALS) {
+ if (cmh->cmsg_level == SOL_SOCKET && cmh->cmsg_type == SCM_CREDENTIALS) {
struct ucred u;
- pa_assert(cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred)));
- memcpy(&u, CMSG_DATA(cmsg), sizeof(struct ucred));
+ pa_assert(cmh->cmsg_len == CMSG_LEN(sizeof(struct ucred)));
+ memcpy(&u, CMSG_DATA(cmh), sizeof(struct ucred));
creds->gid = u.gid;
creds->uid = u.uid;
@@ -420,3 +422,16 @@ int pa_iochannel_get_send_fd(pa_iochannel *io) {
return io->ofd;
}
+
+pa_bool_t pa_iochannel_socket_is_local(pa_iochannel *io) {
+ pa_assert(io);
+
+ if (pa_socket_is_local(io->ifd))
+ return TRUE;
+
+ if (io->ifd != io->ofd)
+ if (pa_socket_is_local(io->ofd))
+ return TRUE;
+
+ return FALSE;
+}
diff --git a/src/pulsecore/iochannel.h b/src/pulsecore/iochannel.h
index c9794d99..9050df90 100644
--- a/src/pulsecore/iochannel.h
+++ b/src/pulsecore/iochannel.h
@@ -1,8 +1,6 @@
#ifndef fooiochannelhfoo
#define fooiochannelhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -85,6 +83,8 @@ void pa_iochannel_socket_peer_to_string(pa_iochannel*io, char*s, size_t l);
int pa_iochannel_socket_set_rcvbuf(pa_iochannel*io, size_t l);
int pa_iochannel_socket_set_sndbuf(pa_iochannel*io, size_t l);
+pa_bool_t pa_iochannel_socket_is_local(pa_iochannel *io);
+
pa_mainloop_api* pa_iochannel_get_mainloop_api(pa_iochannel *io);
int pa_iochannel_get_recv_fd(pa_iochannel *io);
diff --git a/src/pulsecore/ioline.c b/src/pulsecore/ioline.c
index efb50722..90afaafd 100644
--- a/src/pulsecore/ioline.c
+++ b/src/pulsecore/ioline.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -45,11 +43,10 @@
struct pa_ioline {
PA_REFCNT_DECLARE;
-
+
pa_iochannel *io;
pa_defer_event *defer_event;
pa_mainloop_api *mainloop;
- int dead;
char *wbuf;
size_t wbuf_length, wbuf_index, wbuf_valid_length;
@@ -57,10 +54,11 @@ struct pa_ioline {
char *rbuf;
size_t rbuf_length, rbuf_index, rbuf_valid_length;
- void (*callback)(pa_ioline*io, const char *s, void *userdata);
+ pa_ioline_cb_t callback;
void *userdata;
- int defer_close;
+ pa_bool_t dead:1;
+ pa_bool_t defer_close:1;
};
static void io_callback(pa_iochannel*io, void *userdata);
@@ -73,7 +71,6 @@ pa_ioline* pa_ioline_new(pa_iochannel *io) {
l = pa_xnew(pa_ioline, 1);
PA_REFCNT_INIT(l);
l->io = io;
- l->dead = 0;
l->wbuf = NULL;
l->wbuf_length = l->wbuf_index = l->wbuf_valid_length = 0;
@@ -89,7 +86,8 @@ pa_ioline* pa_ioline_new(pa_iochannel *io) {
l->defer_event = l->mainloop->defer_new(l->mainloop, defer_callback, l);
l->mainloop->defer_enable(l->defer_event, 0);
- l->defer_close = 0;
+ l->dead = FALSE;
+ l->defer_close = FALSE;
pa_iochannel_set_callback(io, io_callback, l);
@@ -130,7 +128,7 @@ void pa_ioline_close(pa_ioline *l) {
pa_assert(l);
pa_assert(PA_REFCNT_VALUE(l) >= 1);
- l->dead = 1;
+ l->dead = TRUE;
if (l->io) {
pa_iochannel_free(l->io);
@@ -166,11 +164,13 @@ void pa_ioline_puts(pa_ioline *l, const char *c) {
/* In case the allocated buffer is too small, enlarge it. */
if (l->wbuf_valid_length + len > l->wbuf_length) {
size_t n = l->wbuf_valid_length+len;
- char *new = pa_xmalloc(n);
+ char *new = pa_xnew(char, n);
+
if (l->wbuf) {
memcpy(new, l->wbuf+l->wbuf_index, l->wbuf_valid_length);
pa_xfree(l->wbuf);
}
+
l->wbuf = new;
l->wbuf_length = n;
l->wbuf_index = 0;
@@ -191,15 +191,18 @@ void pa_ioline_puts(pa_ioline *l, const char *c) {
}
}
-void pa_ioline_set_callback(pa_ioline*l, void (*callback)(pa_ioline*io, const char *s, void *userdata), void *userdata) {
+void pa_ioline_set_callback(pa_ioline*l, pa_ioline_cb_t callback, void *userdata) {
pa_assert(l);
pa_assert(PA_REFCNT_VALUE(l) >= 1);
+ if (l->dead)
+ return;
+
l->callback = callback;
l->userdata = userdata;
}
-static void failure(pa_ioline *l, int process_leftover) {
+static void failure(pa_ioline *l, pa_bool_t process_leftover) {
pa_assert(l);
pa_assert(PA_REFCNT_VALUE(l) >= 1);
pa_assert(!l->dead);
@@ -247,7 +250,7 @@ static void scan_for_lines(pa_ioline *l, size_t skip) {
l->rbuf_index = 0;
if (l->callback)
- l->callback(l, p, l->userdata);
+ l->callback(l, pa_strip_nl(p), l->userdata);
skip = 0;
}
@@ -282,7 +285,7 @@ static int do_read(pa_ioline *l) {
memmove(l->rbuf, l->rbuf+l->rbuf_index, l->rbuf_valid_length);
} else {
/* Enlarge the buffer */
- char *new = pa_xmalloc(n);
+ char *new = pa_xnew(char, n);
if (l->rbuf_valid_length)
memcpy(new, l->rbuf+l->rbuf_index, l->rbuf_valid_length);
pa_xfree(l->rbuf);
@@ -299,11 +302,15 @@ static int do_read(pa_ioline *l) {
/* Read some data */
if ((r = pa_iochannel_read(l->io, l->rbuf+l->rbuf_index+l->rbuf_valid_length, len)) <= 0) {
+
+ if (r < 0 && errno == EAGAIN)
+ return 0;
+
if (r < 0 && errno != ECONNRESET) {
pa_log("read(): %s", pa_cstrerror(errno));
- failure(l, 0);
+ failure(l, FALSE);
} else
- failure(l, 1);
+ failure(l, TRUE);
return -1;
}
@@ -328,11 +335,14 @@ static int do_write(pa_ioline *l) {
if ((r = pa_iochannel_write(l->io, l->wbuf+l->wbuf_index, l->wbuf_valid_length)) <= 0) {
+ if (r < 0 && errno == EAGAIN)
+ return 0;
+
if (r < 0 && errno != EPIPE)
pa_log("write(): %s", pa_cstrerror(errno));
- failure(l, 0);
-
+ failure(l, FALSE);
+
return -1;
}
@@ -363,14 +373,14 @@ static void do_work(pa_ioline *l) {
do_write(l);
if (l->defer_close && !l->wbuf_valid_length)
- failure(l, 1);
+ failure(l, TRUE);
pa_ioline_unref(l);
}
static void io_callback(pa_iochannel*io, void *userdata) {
pa_ioline *l = userdata;
-
+
pa_assert(io);
pa_assert(l);
pa_assert(PA_REFCNT_VALUE(l) >= 1);
@@ -393,7 +403,7 @@ void pa_ioline_defer_close(pa_ioline *l) {
pa_assert(l);
pa_assert(PA_REFCNT_VALUE(l) >= 1);
- l->defer_close = 1;
+ l->defer_close = TRUE;
if (!l->wbuf_valid_length)
l->mainloop->defer_enable(l->defer_event, 1);
diff --git a/src/pulsecore/ioline.h b/src/pulsecore/ioline.h
index 8475b798..b9a3d9f4 100644
--- a/src/pulsecore/ioline.h
+++ b/src/pulsecore/ioline.h
@@ -1,8 +1,6 @@
#ifndef fooiolinehfoo
#define fooiolinehfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -33,6 +31,8 @@
typedef struct pa_ioline pa_ioline;
+typedef void (*pa_ioline_cb_t)(pa_ioline*io, const char *s, void *userdata);
+
pa_ioline* pa_ioline_new(pa_iochannel *io);
void pa_ioline_unref(pa_ioline *l);
pa_ioline* pa_ioline_ref(pa_ioline *l);
@@ -45,7 +45,7 @@ void pa_ioline_puts(pa_ioline *s, const char *c);
void pa_ioline_printf(pa_ioline *s, const char *format, ...) PA_GCC_PRINTF_ATTR(2,3);
/* Set the callback function that is called for every recieved line */
-void pa_ioline_set_callback(pa_ioline*io, void (*callback)(pa_ioline*io, const char *s, void *userdata), void *userdata);
+void pa_ioline_set_callback(pa_ioline*io, pa_ioline_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);
diff --git a/src/pulsecore/ipacl.c b/src/pulsecore/ipacl.c
index 9b22e8f5..7b5f865b 100644
--- a/src/pulsecore/ipacl.c
+++ b/src/pulsecore/ipacl.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/ipacl.h b/src/pulsecore/ipacl.h
index 175f54e0..7b7ffa61 100644
--- a/src/pulsecore/ipacl.h
+++ b/src/pulsecore/ipacl.h
@@ -1,8 +1,6 @@
#ifndef fooparseaddrhfoo
#define fooparseaddrhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/llist.h b/src/pulsecore/llist.h
index e62f15b4..46b54eb3 100644
--- a/src/pulsecore/llist.h
+++ b/src/pulsecore/llist.h
@@ -1,8 +1,6 @@
#ifndef foollistfoo
#define foollistfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/log.c b/src/pulsecore/log.c
index 53f8974c..5eda4f65 100644
--- a/src/pulsecore/log.c
+++ b/src/pulsecore/log.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -30,6 +28,7 @@
#include <stdio.h>
#include <unistd.h>
#include <string.h>
+#include <errno.h>
#ifdef HAVE_SYSLOG_H
#include <syslog.h>
@@ -80,7 +79,7 @@ void pa_log_set_ident(const char *p) {
}
/* To make valgrind shut up. */
-static void ident_destructor(void) PA_GCC_DESTRUCTOR;
+static void ident_destructor(void) PA_GCC_DESTRUCTOR;
static void ident_destructor(void) {
pa_xfree(log_ident);
pa_xfree(log_ident_local);
@@ -88,13 +87,13 @@ static void ident_destructor(void) {
void pa_log_set_maximal_level(pa_log_level_t l) {
pa_assert(l < PA_LOG_LEVEL_MAX);
-
+
maximal_level = l;
}
void pa_log_set_target(pa_log_target_t t, void (*func)(pa_log_level_t l, const char*s)) {
pa_assert(t == PA_LOG_USER || !func);
-
+
log_target = t;
user_log_func = func;
}
@@ -108,7 +107,12 @@ void pa_log_levelv_meta(
va_list ap) {
const char *e;
- char *text, *t, *n, *location;
+ char *t, *n;
+ int saved_errno = errno;
+
+ /* We don't use dynamic memory allocation here to minimize the hit
+ * in RT threads */
+ char text[1024], location[128];
pa_assert(level < PA_LOG_LEVEL_MAX);
pa_assert(format);
@@ -116,17 +120,19 @@ void pa_log_levelv_meta(
if ((e = getenv(ENV_LOGLEVEL)))
maximal_level = atoi(e);
- if (level > maximal_level)
+ if (level > maximal_level) {
+ errno = saved_errno;
return;
+ }
- text = pa_vsprintf_malloc(format, ap);
+ pa_vsnprintf(text, sizeof(text), format, ap);
if (getenv(ENV_LOGMETA) && file && line > 0 && func)
- location = pa_sprintf_malloc("[%s:%i %s()] ", file, line, func);
+ pa_snprintf(location, sizeof(location), "[%s:%i %s()] ", file, line, func);
else if (file)
- location = pa_sprintf_malloc("%s: ", pa_path_get_filename(file));
+ pa_snprintf(location, sizeof(location), "%s: ", pa_path_get_filename(file));
else
- location = pa_xstrdup("");
+ location[0] = 0;
if (!pa_utf8_valid(text))
pa_log_level(level, __FILE__": invalid UTF-8 string following below:");
@@ -158,6 +164,8 @@ void pa_log_levelv_meta(
}
#endif
+ /* We shouldn't be using dynamic allocation here to
+ * minimize the hit in RT threads */
local_t = pa_utf8_to_locale(t);
if (!local_t)
fprintf(stderr, "%c: %s%s%s%s\n", level_to_char[level], location, prefix, t, suffix);
@@ -189,11 +197,10 @@ void pa_log_levelv_meta(
#endif
case PA_LOG_USER: {
- char *x;
+ char x[1024];
- x = pa_sprintf_malloc("%s%s", location, t);
+ pa_snprintf(x, sizeof(x), "%s%s", location, t);
user_log_func(level, x);
- pa_xfree(x);
break;
}
@@ -204,8 +211,7 @@ void pa_log_levelv_meta(
}
}
- pa_xfree(text);
- pa_xfree(location);
+ errno = saved_errno;
}
void pa_log_level_meta(
@@ -227,7 +233,7 @@ void pa_log_levelv(pa_log_level_t level, const char *format, va_list ap) {
void pa_log_level(pa_log_level_t level, const char *format, ...) {
va_list ap;
-
+
va_start(ap, format);
pa_log_levelv_meta(level, NULL, 0, NULL, format, ap);
va_end(ap);
diff --git a/src/pulsecore/log.h b/src/pulsecore/log.h
index b0711dca..2047696e 100644
--- a/src/pulsecore/log.h
+++ b/src/pulsecore/log.h
@@ -1,8 +1,6 @@
#ifndef foologhfoo
#define foologhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -27,7 +25,7 @@
#include <stdarg.h>
#include <stdlib.h>
-#include <pulsecore/gccmacro.h>
+#include <pulse/gccmacro.h>
/* A simple logging subsystem */
diff --git a/src/pulsecore/ltdl-helper.c b/src/pulsecore/ltdl-helper.c
index bdce18ef..0d4c22f8 100644
--- a/src/pulsecore/ltdl-helper.c
+++ b/src/pulsecore/ltdl-helper.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -42,12 +40,14 @@ pa_void_func_t pa_load_sym(lt_dlhandle handle, const char *module, const char *s
pa_void_func_t f;
pa_assert(handle);
- pa_assert(module);
pa_assert(symbol);
-
- if ((f = ((pa_void_func_t) (long) lt_dlsym(handle, symbol))))
+
+ if ((f = ((pa_void_func_t) (size_t) lt_dlsym(handle, symbol))))
return f;
+ if (!module)
+ return NULL;
+
/* As the .la files might have been cleansed from the system, we should
* try with the ltdl prefix as well. */
@@ -57,7 +57,7 @@ pa_void_func_t pa_load_sym(lt_dlhandle handle, const char *module, const char *s
if (!isalnum(*c))
*c = '_';
- f = (pa_void_func_t) (long) lt_dlsym(handle, sn);
+ f = (pa_void_func_t) (size_t) lt_dlsym(handle, sn);
pa_xfree(sn);
return f;
diff --git a/src/pulsecore/ltdl-helper.h b/src/pulsecore/ltdl-helper.h
index 5c7388a1..ea73de54 100644
--- a/src/pulsecore/ltdl-helper.h
+++ b/src/pulsecore/ltdl-helper.h
@@ -1,8 +1,6 @@
#ifndef foopulsecoreltdlhelperhfoo
#define foopulsecoreltdlhelperhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/macro.h b/src/pulsecore/macro.h
index c6bba437..fd33b7bb 100644
--- a/src/pulsecore/macro.h
+++ b/src/pulsecore/macro.h
@@ -1,8 +1,6 @@
#ifndef foopulsemacrohfoo
#define foopulsemacrohfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -29,13 +27,26 @@
#include <assert.h>
#include <limits.h>
#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
#include <pulsecore/log.h>
+#include <pulse/gccmacro.h>
#ifndef PACKAGE
#error "Please include config.h before including this file!"
#endif
+#ifndef PA_LIKELY
+#ifdef __GNUC__
+#define PA_LIKELY(x) (__builtin_expect(!!(x),1))
+#define PA_UNLIKELY(x) (__builtin_expect((x),0))
+#else
+#define PA_LIKELY(x) (x)
+#define PA_UNLIKELY(x) (x)
+#endif
+#endif
+
#if defined(PAGE_SIZE)
#define PA_PAGE_SIZE ((size_t) PAGE_SIZE)
#elif defined(PAGESIZE)
@@ -64,18 +75,57 @@ static inline size_t pa_page_align(size_t l) {
#define PA_ELEMENTSOF(x) (sizeof(x)/sizeof((x)[0]))
-#ifndef MAX
-#define MAX(a, b) ((a) > (b) ? (a) : (b))
+/* The users of PA_MIN and PA_MAX should be aware that these macros on
+ * non-GCC executed code with side effects twice. It is thus
+ * considered misuse to use code with side effects as arguments to MIN
+ * and MAX. */
+
+#ifdef __GNUC__
+#define PA_MAX(a,b) \
+ __extension__ ({ typeof(a) _a = (a); \
+ typeof(b) _b = (b); \
+ _a > _b ? _a : _b; \
+ })
+#else
+#define PA_MAX(a, b) ((a) > (b) ? (a) : (b))
#endif
-#ifndef MIN
-#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#ifdef __GNUC__
+#define PA_MIN(a,b) \
+ __extension__ ({ typeof(a) _a = (a); \
+ typeof(b) _b = (b); \
+ _a < _b ? _a : _b; \
+ })
+#else
+#define PA_MIN(a, b) ((a) < (b) ? (a) : (b))
+#endif
+
+#ifdef __GNUC__
+#define PA_CLAMP(x, low, high) \
+ __extension__ ({ typeof(x) _x = (x); \
+ typeof(low) _low = (low); \
+ typeof(high) _high = (high); \
+ ((_x > _high) ? _high : ((_x < _low) ? _low : _x)); \
+ })
+#else
+#define PA_CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x)))
#endif
-#ifndef CLAMP
-#define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x)))
+#ifdef __GNUC__
+#define PA_CLAMP_UNLIKELY(x, low, high) \
+ __extension__ ({ typeof(x) _x = (x); \
+ typeof(low) _low = (low); \
+ typeof(high) _high = (high); \
+ (PA_UNLIKELY(_x > _high) ? _high : (PA_UNLIKELY(_x < _low) ? _low : _x)); \
+ })
+#else
+#define PA_CLAMP_UNLIKELY(x, low, high) (PA_UNLIKELY((x) > (high)) ? (high) : (PA_UNLIKELY((x) < (low)) ? (low) : (x)))
#endif
+/* We don't define a PA_CLAMP_LIKELY here, because it doesn't really
+ * make sense: we cannot know if it is more likely that the values is
+ * lower or greater than the boundaries.*/
+
/* This type is not intended to be used in exported APIs! Use classic "int" there! */
#ifdef HAVE_STD_BOOL
typedef _Bool pa_bool_t;
@@ -97,35 +147,47 @@ typedef int pa_bool_t;
#define PA_PRETTY_FUNCTION ""
#endif
-#define pa_return_if_fail(expr) \
- do { \
- if (!(expr)) { \
- pa_log_debug("%s: Assertion <%s> failed.\n", PA_PRETTY_FUNCTION, #expr ); \
- return; \
- } \
- } while(0)
-
-#define pa_return_val_if_fail(expr, val) \
- do { \
- if (!(expr)) { \
- pa_log_debug("%s: Assertion <%s> failed.\n", PA_PRETTY_FUNCTION, #expr ); \
- return (val); \
- } \
- } while(0)
+#define pa_return_if_fail(expr) \
+ do { \
+ if (PA_UNLIKELY(!(expr))) { \
+ pa_log_debug("Assertion '%s' failed at %s:%u, function %s.\n", #expr , __FILE__, __LINE__, PA_PRETTY_FUNCTION); \
+ return; \
+ } \
+ } while(FALSE)
+
+#define pa_return_val_if_fail(expr, val) \
+ do { \
+ if (PA_UNLIKELY(!(expr))) { \
+ pa_log_debug("Assertion '%s' failed at %s:%u, function %s.\n", #expr , __FILE__, __LINE__, PA_PRETTY_FUNCTION); \
+ return (val); \
+ } \
+ } while(FALSE)
#define pa_return_null_if_fail(expr) pa_return_val_if_fail(expr, NULL)
-#define pa_assert assert
-
-#define pa_assert_not_reached() pa_assert(!"Should not be reached.")
-
-/* An assert which guarantees side effects of x */
+/* An assert which guarantees side effects of x, i.e. is never
+ * optimized away */
+#define pa_assert_se(expr) \
+ do { \
+ if (PA_UNLIKELY(!(expr))) { \
+ pa_log_error("Assertion '%s' failed at %s:%u, function %s(). Aborting.", #expr , __FILE__, __LINE__, PA_PRETTY_FUNCTION); \
+ abort(); \
+ } \
+ } while (FALSE)
+
+/* An assert that may be optimized away by defining NDEBUG */
#ifdef NDEBUG
-#define pa_assert_se(x) x
+#define pa_assert(expr) do {} while (FALSE)
#else
-#define pa_assert_se(x) pa_assert(x)
+#define pa_assert(expr) pa_assert_se(expr)
#endif
+#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)
+
#define PA_PTR_TO_UINT(p) ((unsigned int) (unsigned long) (p))
#define PA_UINT_TO_PTR(u) ((void*) (unsigned long) (u))
@@ -146,4 +208,17 @@ typedef int pa_bool_t;
#define PA_PATH_SEP_CHAR '/'
#endif
+#ifdef __GNUC__
+
+#define PA_WARN_REFERENCE(sym, msg) \
+ __asm__(".section .gnu.warning." #sym); \
+ __asm__(".asciz \"" msg "\""); \
+ __asm__(".previous")
+
+#else
+
+#define PA_WARN_REFERENCE(sym, msg)
+
+#endif
+
#endif
diff --git a/src/pulsecore/mcalign.c b/src/pulsecore/mcalign.c
index 8ca7c962..a03d5ae7 100644
--- a/src/pulsecore/mcalign.c
+++ b/src/pulsecore/mcalign.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -197,7 +195,6 @@ int pa_mcalign_pop(pa_mcalign *m, pa_memchunk *c) {
/* There's simply nothing */
return -1;
-
}
size_t pa_mcalign_csize(pa_mcalign *m, size_t l) {
@@ -211,3 +208,11 @@ size_t pa_mcalign_csize(pa_mcalign *m, size_t l) {
return (l/m->base)*m->base;
}
+
+void pa_mcalign_flush(pa_mcalign *m) {
+ pa_memchunk chunk;
+ pa_assert(m);
+
+ while (pa_mcalign_pop(m, &chunk) >= 0)
+ pa_memblock_unref(chunk.memblock);
+}
diff --git a/src/pulsecore/mcalign.h b/src/pulsecore/mcalign.h
index 6ff8f94e..e82eb007 100644
--- a/src/pulsecore/mcalign.h
+++ b/src/pulsecore/mcalign.h
@@ -1,8 +1,6 @@
#ifndef foomcalignhfoo
#define foomcalignhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -79,4 +77,7 @@ int pa_mcalign_pop(pa_mcalign *m, pa_memchunk *c);
/* If we pass l bytes in now, how many bytes would we get out? */
size_t pa_mcalign_csize(pa_mcalign *m, size_t l);
+/* Flush what's still stored in the aligner */
+void pa_mcalign_flush(pa_mcalign *m);
+
#endif
diff --git a/src/pulsecore/memblock.c b/src/pulsecore/memblock.c
index e05304ba..c2ee1360 100644
--- a/src/pulsecore/memblock.c
+++ b/src/pulsecore/memblock.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -46,8 +44,12 @@
#include "memblock.h"
-#define PA_MEMPOOL_SLOTS_MAX 128
-#define PA_MEMPOOL_SLOT_SIZE (16*1024)
+/* We can allocate 64*1024*1024 bytes at maximum. That's 64MB. Please
+ * note that the footprint is usually much smaller, since the data is
+ * stored in SHM and our OS does not commit the memory before we use
+ * it for the first time. */
+#define PA_MEMPOOL_SLOTS_MAX 1024
+#define PA_MEMPOOL_SLOT_SIZE (64*1024)
#define PA_MEMEXPORT_SLOTS_MAX 128
@@ -59,7 +61,9 @@ struct pa_memblock {
pa_mempool *pool;
pa_memblock_type_t type;
- int read_only; /* boolean */
+
+ pa_bool_t read_only:1;
+ pa_bool_t is_silence:1;
pa_atomic_ptr_t data;
size_t length;
@@ -125,11 +129,6 @@ struct pa_memexport {
PA_LLIST_FIELDS(pa_memexport);
};
-struct mempool_slot {
- PA_LLIST_FIELDS(struct mempool_slot);
- /* the actual data follows immediately hereafter */
-};
-
struct pa_mempool {
pa_semaphore *semaphore;
pa_mutex *mutex;
@@ -202,7 +201,7 @@ pa_memblock *pa_memblock_new(pa_mempool *p, size_t length) {
pa_memblock *b;
pa_assert(p);
- pa_assert(length > 0);
+ pa_assert(length);
if (!(b = pa_memblock_new_pool(p, length)))
b = memblock_new_appended(p, length);
@@ -215,18 +214,18 @@ static pa_memblock *memblock_new_appended(pa_mempool *p, size_t length) {
pa_memblock *b;
pa_assert(p);
- pa_assert(length > 0);
+ pa_assert(length);
/* If -1 is passed as length we choose the size for the caller. */
-
+
if (length == (size_t) -1)
- length = p->block_size - PA_ALIGN(sizeof(struct mempool_slot)) - PA_ALIGN(sizeof(pa_memblock));
-
+ length = p->block_size - PA_ALIGN(sizeof(pa_memblock));
+
b = pa_xmalloc(PA_ALIGN(sizeof(pa_memblock)) + length);
PA_REFCNT_INIT(b);
b->pool = p;
b->type = PA_MEMBLOCK_APPENDED;
- b->read_only = 0;
+ b->read_only = b->is_silence = FALSE;
pa_atomic_ptr_store(&b->data, (uint8_t*) b + PA_ALIGN(sizeof(pa_memblock)));
b->length = length;
pa_atomic_store(&b->n_acquired, 0);
@@ -252,7 +251,7 @@ static struct mempool_slot* mempool_allocate_slot(pa_mempool *p) {
slot = (struct mempool_slot*) ((uint8_t*) p->memory.ptr + (p->block_size * idx));
if (!slot) {
- pa_log_debug("Pool full");
+ pa_log_info("Pool full");
pa_atomic_inc(&p->stat.n_pool_full);
return NULL;
}
@@ -261,11 +260,9 @@ static struct mempool_slot* mempool_allocate_slot(pa_mempool *p) {
return slot;
}
-/* No lock necessary */
-static void* mempool_slot_data(struct mempool_slot *slot) {
- pa_assert(slot);
-
- return (uint8_t*) slot + PA_ALIGN(sizeof(struct mempool_slot));
+/* No lock necessary, totally redundant anyway */
+static inline void* mempool_slot_data(struct mempool_slot *slot) {
+ return slot;
}
/* No lock necessary */
@@ -294,15 +291,15 @@ pa_memblock *pa_memblock_new_pool(pa_mempool *p, size_t length) {
struct mempool_slot *slot;
pa_assert(p);
- pa_assert(length > 0);
+ pa_assert(length);
/* If -1 is passed as length we choose the size for the caller: we
* take the largest size that fits in one of our slots. */
-
+
if (length == (size_t) -1)
length = pa_mempool_block_size_max(p);
-
- if (p->block_size - PA_ALIGN(sizeof(struct mempool_slot)) >= PA_ALIGN(sizeof(pa_memblock)) + length) {
+
+ if (p->block_size >= PA_ALIGN(sizeof(pa_memblock)) + length) {
if (!(slot = mempool_allocate_slot(p)))
return NULL;
@@ -311,26 +308,26 @@ pa_memblock *pa_memblock_new_pool(pa_mempool *p, size_t length) {
b->type = PA_MEMBLOCK_POOL;
pa_atomic_ptr_store(&b->data, (uint8_t*) b + PA_ALIGN(sizeof(pa_memblock)));
- } else if (p->block_size - PA_ALIGN(sizeof(struct mempool_slot)) >= length) {
+ } else if (p->block_size >= length) {
if (!(slot = mempool_allocate_slot(p)))
return NULL;
if (!(b = pa_flist_pop(PA_STATIC_FLIST_GET(unused_memblocks))))
b = pa_xnew(pa_memblock, 1);
-
+
b->type = PA_MEMBLOCK_POOL_EXTERNAL;
pa_atomic_ptr_store(&b->data, mempool_slot_data(slot));
} else {
- pa_log_debug("Memory block too large for pool: %lu > %lu", (unsigned long) length, (unsigned long) (p->block_size - PA_ALIGN(sizeof(struct mempool_slot))));
+ pa_log_debug("Memory block too large for pool: %lu > %lu", (unsigned long) length, (unsigned long) p->block_size);
pa_atomic_inc(&p->stat.n_too_large_for_pool);
return NULL;
}
PA_REFCNT_INIT(b);
b->pool = p;
- b->read_only = 0;
+ b->read_only = b->is_silence = FALSE;
b->length = length;
pa_atomic_store(&b->n_acquired, 0);
pa_atomic_store(&b->please_signal, 0);
@@ -340,13 +337,13 @@ pa_memblock *pa_memblock_new_pool(pa_mempool *p, size_t length) {
}
/* No lock necessary */
-pa_memblock *pa_memblock_new_fixed(pa_mempool *p, void *d, size_t length, int read_only) {
+pa_memblock *pa_memblock_new_fixed(pa_mempool *p, void *d, size_t length, pa_bool_t read_only) {
pa_memblock *b;
pa_assert(p);
pa_assert(d);
pa_assert(length != (size_t) -1);
- pa_assert(length > 0);
+ pa_assert(length);
if (!(b = pa_flist_pop(PA_STATIC_FLIST_GET(unused_memblocks))))
b = pa_xnew(pa_memblock, 1);
@@ -354,6 +351,7 @@ pa_memblock *pa_memblock_new_fixed(pa_mempool *p, void *d, size_t length, int re
b->pool = p;
b->type = PA_MEMBLOCK_FIXED;
b->read_only = read_only;
+ b->is_silence = FALSE;
pa_atomic_ptr_store(&b->data, d);
b->length = length;
pa_atomic_store(&b->n_acquired, 0);
@@ -364,12 +362,12 @@ pa_memblock *pa_memblock_new_fixed(pa_mempool *p, void *d, size_t length, int re
}
/* No lock necessary */
-pa_memblock *pa_memblock_new_user(pa_mempool *p, void *d, size_t length, void (*free_cb)(void *p), int read_only) {
+pa_memblock *pa_memblock_new_user(pa_mempool *p, void *d, size_t length, pa_free_cb_t free_cb, pa_bool_t read_only) {
pa_memblock *b;
pa_assert(p);
pa_assert(d);
- pa_assert(length > 0);
+ pa_assert(length);
pa_assert(length != (size_t) -1);
pa_assert(free_cb);
@@ -379,6 +377,7 @@ pa_memblock *pa_memblock_new_user(pa_mempool *p, void *d, size_t length, void (*
b->pool = p;
b->type = PA_MEMBLOCK_USER;
b->read_only = read_only;
+ b->is_silence = FALSE;
pa_atomic_ptr_store(&b->data, d);
b->length = length;
pa_atomic_store(&b->n_acquired, 0);
@@ -391,7 +390,7 @@ pa_memblock *pa_memblock_new_user(pa_mempool *p, void *d, size_t length, void (*
}
/* No lock necessary */
-int pa_memblock_is_read_only(pa_memblock *b) {
+pa_bool_t pa_memblock_is_read_only(pa_memblock *b) {
pa_assert(b);
pa_assert(PA_REFCNT_VALUE(b) > 0);
@@ -399,13 +398,27 @@ int pa_memblock_is_read_only(pa_memblock *b) {
}
/* No lock necessary */
-int pa_memblock_ref_is_one(pa_memblock *b) {
+pa_bool_t pa_memblock_is_silence(pa_memblock *b) {
+ pa_assert(b);
+ pa_assert(PA_REFCNT_VALUE(b) > 0);
+
+ return b->is_silence;
+}
+
+/* No lock necessary */
+void pa_memblock_set_is_silence(pa_memblock *b, pa_bool_t v) {
+ pa_assert(b);
+ pa_assert(PA_REFCNT_VALUE(b) > 0);
+
+ b->is_silence = v;
+}
+
+/* No lock necessary */
+pa_bool_t pa_memblock_ref_is_one(pa_memblock *b) {
int r;
-
pa_assert(b);
- r = PA_REFCNT_VALUE(b);
- pa_assert(r > 0);
+ pa_assert_se((r = PA_REFCNT_VALUE(b)) > 0);
return r == 1;
}
@@ -475,7 +488,7 @@ static void memblock_free(pa_memblock *b) {
case PA_MEMBLOCK_APPENDED :
if (pa_flist_push(PA_STATIC_FLIST_GET(unused_memblocks), b) < 0)
pa_xfree(b);
-
+
break;
case PA_MEMBLOCK_IMPORTED : {
@@ -567,7 +580,7 @@ static void memblock_make_local(pa_memblock *b) {
pa_atomic_dec(&b->pool->stat.n_allocated_by_type[b->type]);
- if (b->length <= b->pool->block_size - PA_ALIGN(sizeof(struct mempool_slot))) {
+ if (b->length <= b->pool->block_size) {
struct mempool_slot *slot;
if ((slot = mempool_allocate_slot(b->pool))) {
@@ -579,7 +592,7 @@ static void memblock_make_local(pa_memblock *b) {
pa_atomic_ptr_store(&b->data, new_data);
b->type = PA_MEMBLOCK_POOL_EXTERNAL;
- b->read_only = 0;
+ b->read_only = FALSE;
goto finish;
}
@@ -590,7 +603,7 @@ static void memblock_make_local(pa_memblock *b) {
pa_atomic_ptr_store(&b->data, pa_xmemdup(pa_atomic_ptr_load(&b->data), b->length));
b->type = PA_MEMBLOCK_USER;
- b->read_only = 0;
+ b->read_only = FALSE;
finish:
pa_atomic_inc(&b->pool->stat.n_allocated_by_type[b->type]);
@@ -613,7 +626,7 @@ void pa_memblock_unref_fixed(pa_memblock *b) {
/* No lock necessary. */
pa_memblock *pa_memblock_will_need(pa_memblock *b) {
void *p;
-
+
pa_assert(b);
pa_assert(PA_REFCNT_VALUE(b) > 0);
@@ -651,11 +664,11 @@ static void memblock_replace_import(pa_memblock *b) {
if (-- seg->n_blocks <= 0) {
pa_mutex_unlock(seg->import->mutex);
segment_detach(seg);
- } else
+ } else
pa_mutex_unlock(seg->import->mutex);
}
-pa_mempool* pa_mempool_new(int shared) {
+pa_mempool* pa_mempool_new(pa_bool_t shared) {
pa_mempool *p;
p = pa_xnew(pa_mempool, 1);
@@ -669,8 +682,6 @@ pa_mempool* pa_mempool_new(int shared) {
p->n_blocks = PA_MEMPOOL_SLOTS_MAX;
- pa_assert(p->block_size > PA_ALIGN(sizeof(struct mempool_slot)));
-
if (pa_shm_create_rw(&p->memory, p->n_blocks * p->block_size, shared, 0700) < 0) {
pa_xfree(p);
return NULL;
@@ -726,7 +737,7 @@ const pa_mempool_stat* pa_mempool_get_stat(pa_mempool *p) {
size_t pa_mempool_block_size_max(pa_mempool *p) {
pa_assert(p);
- return p->block_size - PA_ALIGN(sizeof(struct mempool_slot)) - PA_ALIGN(sizeof(pa_memblock));
+ return p->block_size - PA_ALIGN(sizeof(pa_memblock));
}
/* No lock necessary */
@@ -743,9 +754,7 @@ void pa_mempool_vacuum(pa_mempool *p) {
;
while ((slot = pa_flist_pop(list))) {
- pa_shm_punch(&p->memory,
- (uint8_t*) slot - (uint8_t*) p->memory.ptr + PA_ALIGN(sizeof(struct mempool_slot)),
- p->block_size - PA_ALIGN(sizeof(struct mempool_slot)));
+ pa_shm_punch(&p->memory, (uint8_t*) slot - (uint8_t*) p->memory.ptr, p->block_size);
while (pa_flist_push(p->free_slots, slot))
;
@@ -767,7 +776,7 @@ int pa_mempool_get_shm_id(pa_mempool *p, uint32_t *id) {
}
/* No lock necessary */
-int pa_mempool_is_shared(pa_mempool *p) {
+pa_bool_t pa_mempool_is_shared(pa_mempool *p) {
pa_assert(p);
return !!p->memory.shared;
@@ -882,11 +891,12 @@ pa_memblock* pa_memimport_get(pa_memimport *i, uint32_t block_id, uint32_t shm_i
if (!(b = pa_flist_pop(PA_STATIC_FLIST_GET(unused_memblocks))))
b = pa_xnew(pa_memblock, 1);
-
+
PA_REFCNT_INIT(b);
b->pool = i->pool;
b->type = PA_MEMBLOCK_IMPORTED;
- b->read_only = 1;
+ b->read_only = TRUE;
+ b->is_silence = FALSE;
pa_atomic_ptr_store(&b->data, (uint8_t*) seg->memory.ptr + offset);
b->length = size;
pa_atomic_store(&b->n_acquired, 0);
diff --git a/src/pulsecore/memblock.h b/src/pulsecore/memblock.h
index c704014a..efe55b02 100644
--- a/src/pulsecore/memblock.h
+++ b/src/pulsecore/memblock.h
@@ -1,8 +1,6 @@
#ifndef foopulsememblockhfoo
#define foopulsememblockhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -87,13 +85,13 @@ pa_memblock *pa_memblock_new(pa_mempool *, size_t length);
pa_memblock *pa_memblock_new_pool(pa_mempool *, size_t length);
/* Allocate a new memory block of type PA_MEMBLOCK_USER */
-pa_memblock *pa_memblock_new_user(pa_mempool *, void *data, size_t length, void (*free_cb)(void *p), int read_only);
+pa_memblock *pa_memblock_new_user(pa_mempool *, void *data, size_t length, pa_free_cb_t free_cb, pa_bool_t read_only);
/* A special case of pa_memblock_new_user: take a memory buffer previously allocated with pa_xmalloc() */
#define pa_memblock_new_malloced(p,data,length) pa_memblock_new_user(p, data, length, pa_xfree, 0)
/* Allocate a new memory block of type PA_MEMBLOCK_FIXED */
-pa_memblock *pa_memblock_new_fixed(pa_mempool *, void *data, size_t length, int read_only);
+pa_memblock *pa_memblock_new_fixed(pa_mempool *, void *data, size_t length, pa_bool_t read_only);
void pa_memblock_unref(pa_memblock*b);
pa_memblock* pa_memblock_ref(pa_memblock*b);
@@ -106,8 +104,11 @@ function is not multiple caller safe, i.e. needs to be locked
manually if called from more than one thread at the same time. */
void pa_memblock_unref_fixed(pa_memblock*b);
-int pa_memblock_is_read_only(pa_memblock *b);
-int pa_memblock_ref_is_one(pa_memblock *b);
+pa_bool_t pa_memblock_is_read_only(pa_memblock *b);
+pa_bool_t pa_memblock_is_silence(pa_memblock *b);
+pa_bool_t pa_memblock_ref_is_one(pa_memblock *b);
+void pa_memblock_set_is_silence(pa_memblock *b, pa_bool_t v);
+
void* pa_memblock_acquire(pa_memblock *b);
void pa_memblock_release(pa_memblock *b);
size_t pa_memblock_get_length(pa_memblock *b);
@@ -116,12 +117,12 @@ pa_mempool * pa_memblock_get_pool(pa_memblock *b);
pa_memblock *pa_memblock_will_need(pa_memblock *b);
/* The memory block manager */
-pa_mempool* pa_mempool_new(int shared);
+pa_mempool* pa_mempool_new(pa_bool_t shared);
void pa_mempool_free(pa_mempool *p);
const pa_mempool_stat* pa_mempool_get_stat(pa_mempool *p);
void pa_mempool_vacuum(pa_mempool *p);
int pa_mempool_get_shm_id(pa_mempool *p, uint32_t *id);
-int pa_mempool_is_shared(pa_mempool *p);
+pa_bool_t pa_mempool_is_shared(pa_mempool *p);
size_t pa_mempool_block_size_max(pa_mempool *p);
/* For recieving blocks from other nodes */
diff --git a/src/pulsecore/memblockq.c b/src/pulsecore/memblockq.c
index a46155a9..a9f28a07 100644
--- a/src/pulsecore/memblockq.c
+++ b/src/pulsecore/memblockq.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -50,11 +48,12 @@ PA_STATIC_FLIST_DECLARE(list_items, 0, pa_xfree);
struct pa_memblockq {
struct list_item *blocks, *blocks_tail;
+ struct list_item *current_read, *current_write;
unsigned n_blocks;
- size_t maxlength, tlength, base, prebuf, minreq;
+ size_t maxlength, tlength, base, prebuf, minreq, maxrewind;
int64_t read_index, write_index;
pa_bool_t in_prebuf;
- pa_memblock *silence;
+ pa_memchunk silence;
pa_mcalign *mcalign;
int64_t missing;
size_t requested;
@@ -67,52 +66,43 @@ pa_memblockq* pa_memblockq_new(
size_t base,
size_t prebuf,
size_t minreq,
- pa_memblock *silence) {
+ size_t maxrewind,
+ pa_memchunk *silence) {
pa_memblockq* bq;
pa_assert(base > 0);
- pa_assert(maxlength >= base);
bq = pa_xnew(pa_memblockq, 1);
bq->blocks = bq->blocks_tail = NULL;
+ bq->current_read = bq->current_write = NULL;
bq->n_blocks = 0;
bq->base = base;
bq->read_index = bq->write_index = idx;
- pa_log_debug("memblockq requested: maxlength=%lu, tlength=%lu, base=%lu, prebuf=%lu, minreq=%lu",
- (unsigned long) maxlength, (unsigned long) tlength, (unsigned long) base, (unsigned long) prebuf, (unsigned long) minreq);
-
- bq->maxlength = ((maxlength+base-1)/base)*base;
- pa_assert(bq->maxlength >= base);
-
- bq->tlength = ((tlength+base-1)/base)*base;
- if (bq->tlength <= 0 || bq->tlength > bq->maxlength)
- bq->tlength = bq->maxlength;
-
- bq->prebuf = (prebuf == (size_t) -1) ? bq->tlength/2 : prebuf;
- bq->prebuf = ((bq->prebuf+base-1)/base)*base;
- if (bq->prebuf > bq->maxlength)
- bq->prebuf = bq->maxlength;
-
- bq->minreq = (minreq/base)*base;
+ pa_log_debug("memblockq requested: maxlength=%lu, tlength=%lu, base=%lu, prebuf=%lu, minreq=%lu maxrewind=%lu",
+ (unsigned long) maxlength, (unsigned long) tlength, (unsigned long) base, (unsigned long) prebuf, (unsigned long) minreq, (unsigned long) maxrewind);
- if (bq->minreq > bq->tlength - bq->prebuf)
- bq->minreq = bq->tlength - bq->prebuf;
+ bq->missing = bq->requested = bq->maxlength = bq->tlength = bq->prebuf = bq->minreq = bq->maxrewind = 0;
+ bq->in_prebuf = TRUE;
- if (!bq->minreq)
- bq->minreq = 1;
+ 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_maxrewind(bq, maxrewind);
- pa_log_debug("memblockq sanitized: maxlength=%lu, tlength=%lu, base=%lu, prebuf=%lu, minreq=%lu",
- (unsigned long)bq->maxlength, (unsigned long)bq->tlength, (unsigned long)bq->base, (unsigned long)bq->prebuf, (unsigned long)bq->minreq);
+ pa_log_debug("memblockq sanitized: maxlength=%lu, tlength=%lu, base=%lu, prebuf=%lu, minreq=%lu maxrewind=%lu",
+ (unsigned long) bq->maxlength, (unsigned long) bq->tlength, (unsigned long) bq->base, (unsigned long) bq->prebuf, (unsigned long) bq->minreq, (unsigned long) bq->maxrewind);
- bq->in_prebuf = bq->prebuf > 0;
- bq->silence = silence ? pa_memblock_ref(silence) : NULL;
- bq->mcalign = NULL;
+ if (silence) {
+ bq->silence = *silence;
+ pa_memblock_ref(bq->silence.memblock);
+ } else
+ pa_memchunk_reset(&bq->silence);
- bq->missing = bq->tlength;
- bq->requested = 0;
+ bq->mcalign = pa_mcalign_new(bq->base);
return bq;
}
@@ -122,8 +112,8 @@ void pa_memblockq_free(pa_memblockq* bq) {
pa_memblockq_flush(bq);
- if (bq->silence)
- pa_memblock_unref(bq->silence);
+ if (bq->silence.memblock)
+ pa_memblock_unref(bq->silence.memblock);
if (bq->mcalign)
pa_mcalign_free(bq->mcalign);
@@ -131,6 +121,62 @@ void pa_memblockq_free(pa_memblockq* bq) {
pa_xfree(bq);
}
+static void fix_current_read(pa_memblockq *bq) {
+ pa_assert(bq);
+
+ if (PA_UNLIKELY(!bq->blocks)) {
+ bq->current_read = NULL;
+ return;
+ }
+
+ if (PA_UNLIKELY(!bq->current_read))
+ bq->current_read = bq->blocks;
+
+ /* Scan left */
+ while (PA_UNLIKELY(bq->current_read->index > bq->read_index))
+
+ if (bq->current_read->prev)
+ bq->current_read = bq->current_read->prev;
+ else
+ break;
+
+ /* Scan right */
+ while (PA_LIKELY(bq->current_read != NULL) && PA_UNLIKELY(bq->current_read->index + (int64_t) bq->current_read->chunk.length <= bq->read_index))
+ bq->current_read = bq->current_read->next;
+
+ /* At this point current_read will either point at or left of the
+ next block to play. It may be NULL in case everything in
+ the queue was already played */
+}
+
+static void fix_current_write(pa_memblockq *bq) {
+ pa_assert(bq);
+
+ if (PA_UNLIKELY(!bq->blocks)) {
+ bq->current_write = NULL;
+ return;
+ }
+
+ if (PA_UNLIKELY(!bq->current_write))
+ bq->current_write = bq->blocks_tail;
+
+ /* Scan right */
+ while (PA_UNLIKELY(bq->current_write->index + (int64_t) bq->current_write->chunk.length <= bq->write_index))
+
+ if (bq->current_write->next)
+ bq->current_write = bq->current_write->next;
+ else
+ break;
+
+ /* Scan left */
+ while (PA_LIKELY(bq->current_write != NULL) && PA_UNLIKELY(bq->current_write->index > bq->write_index))
+ bq->current_write = bq->current_write->prev;
+
+ /* At this point current_write will either point at or right of
+ the next block to write data to. It may be NULL in case
+ everything in the queue is still to be played */
+}
+
static void drop_block(pa_memblockq *bq, struct list_item *q) {
pa_assert(bq);
pa_assert(q);
@@ -139,13 +185,23 @@ static void drop_block(pa_memblockq *bq, struct list_item *q) {
if (q->prev)
q->prev->next = q->next;
- else
+ else {
+ pa_assert(bq->blocks == q);
bq->blocks = q->next;
+ }
if (q->next)
q->next->prev = q->prev;
- else
+ else {
+ pa_assert(bq->blocks_tail == q);
bq->blocks_tail = q->prev;
+ }
+
+ if (bq->current_write == q)
+ bq->current_write = q->prev;
+
+ if (bq->current_read == q)
+ bq->current_read = q->next;
pa_memblock_unref(q->chunk.memblock);
@@ -155,6 +211,16 @@ static void drop_block(pa_memblockq *bq, struct list_item *q) {
bq->n_blocks--;
}
+static void drop_backlog(pa_memblockq *bq) {
+ int64_t boundary;
+ pa_assert(bq);
+
+ boundary = bq->read_index - bq->maxrewind;
+
+ while (bq->blocks && (bq->blocks->index + (int64_t) bq->blocks->chunk.length <= boundary))
+ drop_block(bq, bq->blocks);
+}
+
static pa_bool_t can_push(pa_memblockq *bq, size_t l) {
int64_t end;
@@ -169,10 +235,10 @@ static pa_bool_t can_push(pa_memblockq *bq, size_t l) {
return TRUE;
}
- end = bq->blocks_tail ? bq->blocks_tail->index + bq->blocks_tail->chunk.length : 0;
+ end = bq->blocks_tail ? bq->blocks_tail->index + (int64_t) bq->blocks_tail->chunk.length : bq->write_index;
/* Make sure that the list doesn't get too long */
- if (bq->write_index + (int64_t)l > end)
+ if (bq->write_index + (int64_t) l > end)
if (bq->write_index + l - bq->read_index > bq->maxlength)
return FALSE;
@@ -199,28 +265,26 @@ int pa_memblockq_push(pa_memblockq* bq, const pa_memchunk *uchunk) {
old = bq->write_index;
chunk = *uchunk;
- if (bq->read_index > bq->write_index) {
-
- /* We currently have a buffer underflow, we need to drop some
- * incoming data */
+ fix_current_write(bq);
+ q = bq->current_write;
- size_t d = bq->read_index - bq->write_index;
+ /* First we advance the q pointer right of where we want to
+ * write to */
- if (chunk.length > d) {
- chunk.index += d;
- chunk.length -= d;
- bq->write_index += d;
- } else {
- /* We drop the incoming data completely */
- bq->write_index += chunk.length;
- goto finish;
- }
+ if (q) {
+ while (bq->write_index + (int64_t) chunk.length > q->index)
+ if (q->next)
+ q = q->next;
+ else
+ break;
}
+ if (!q)
+ q = bq->blocks_tail;
+
/* We go from back to front to look for the right place to add
* this new entry. Drop data we will overwrite on the way */
- q = bq->blocks_tail;
while (q) {
if (bq->write_index >= q->index + (int64_t) q->chunk.length)
@@ -346,7 +410,7 @@ finish:
delta = bq->write_index - old;
- if (delta >= bq->requested) {
+ if (delta >= (int64_t) bq->requested) {
delta -= bq->requested;
bq->requested = 0;
} else {
@@ -359,7 +423,16 @@ finish:
return 0;
}
-static pa_bool_t memblockq_check_prebuf(pa_memblockq *bq) {
+pa_bool_t pa_memblockq_prebuf_active(pa_memblockq *bq) {
+ pa_assert(bq);
+
+ if (bq->in_prebuf)
+ return pa_memblockq_get_length(bq) < bq->prebuf;
+ else
+ return bq->prebuf > 0 && bq->read_index >= bq->write_index;
+}
+
+static pa_bool_t update_prebuf(pa_memblockq *bq) {
pa_assert(bq);
if (bq->in_prebuf) {
@@ -381,34 +454,42 @@ static pa_bool_t memblockq_check_prebuf(pa_memblockq *bq) {
}
int pa_memblockq_peek(pa_memblockq* bq, pa_memchunk *chunk) {
+ int64_t d;
pa_assert(bq);
pa_assert(chunk);
/* We need to pre-buffer */
- if (memblockq_check_prebuf(bq))
+ if (update_prebuf(bq))
return -1;
+ fix_current_read(bq);
+
/* Do we need to spit out silence? */
- if (!bq->blocks || bq->blocks->index > bq->read_index) {
+ if (!bq->current_read || bq->current_read->index > bq->read_index) {
size_t length;
/* How much silence shall we return? */
- length = bq->blocks ? bq->blocks->index - bq->read_index : 0;
+ if (bq->current_read)
+ length = bq->current_read->index - bq->read_index;
+ else if (bq->write_index > bq->read_index)
+ length = (size_t) (bq->write_index - bq->read_index);
+ else
+ length = 0;
/* We need to return silence, since no data is yet available */
- if (bq->silence) {
- chunk->memblock = pa_memblock_ref(bq->silence);
+ if (bq->silence.memblock) {
+ *chunk = bq->silence;
+ pa_memblock_ref(chunk->memblock);
- if (!length || length > pa_memblock_get_length(chunk->memblock))
- length = pa_memblock_get_length(chunk->memblock);
+ if (length > 0 && length < chunk->length)
+ chunk->length = length;
- chunk->length = length;
} else {
/* If the memblockq is empty, return -1, otherwise return
* the time to sleep */
- if (!bq->blocks)
+ if (length <= 0)
return -1;
chunk->memblock = NULL;
@@ -420,11 +501,14 @@ int pa_memblockq_peek(pa_memblockq* bq, pa_memchunk *chunk) {
}
/* Ok, let's pass real data to the caller */
- pa_assert(bq->blocks->index == bq->read_index);
-
- *chunk = bq->blocks->chunk;
+ *chunk = bq->current_read->chunk;
pa_memblock_ref(chunk->memblock);
+ pa_assert(bq->read_index >= bq->current_read->index);
+ d = bq->read_index - bq->current_read->index;
+ chunk->index += d;
+ chunk->length -= d;
+
return 0;
}
@@ -438,45 +522,26 @@ void pa_memblockq_drop(pa_memblockq *bq, size_t length) {
while (length > 0) {
/* Do not drop any data when we are in prebuffering mode */
- if (memblockq_check_prebuf(bq))
+ if (update_prebuf(bq))
break;
- if (bq->blocks) {
- size_t d;
-
- pa_assert(bq->blocks->index >= bq->read_index);
-
- d = (size_t) (bq->blocks->index - bq->read_index);
-
- if (d >= length) {
- /* The first block is too far in the future */
-
- bq->read_index += length;
- break;
- } else {
+ fix_current_read(bq);
- length -= d;
- bq->read_index += d;
- }
-
- pa_assert(bq->blocks->index == bq->read_index);
+ if (bq->current_read) {
+ int64_t p, d;
- if (bq->blocks->chunk.length <= length) {
- /* We need to drop the full block */
+ /* We go through this piece by piece to make sure we don't
+ * drop more than allowed by prebuf */
- length -= bq->blocks->chunk.length;
- bq->read_index += bq->blocks->chunk.length;
+ p = bq->current_read->index + bq->current_read->chunk.length;
+ pa_assert(p >= bq->read_index);
+ d = p - bq->read_index;
- drop_block(bq, bq->blocks);
- } else {
- /* Only the start of this block needs to be dropped */
+ if (d > (int64_t) length)
+ d = length;
- bq->blocks->chunk.index += length;
- bq->blocks->chunk.length -= length;
- bq->blocks->index += length;
- bq->read_index += length;
- break;
- }
+ bq->read_index += d;
+ length -= d;
} else {
@@ -486,20 +551,32 @@ void pa_memblockq_drop(pa_memblockq *bq, size_t length) {
}
}
+ drop_backlog(bq);
+
delta = bq->read_index - old;
bq->missing += delta;
}
-int pa_memblockq_is_readable(pa_memblockq *bq) {
+void pa_memblockq_rewind(pa_memblockq *bq, size_t length) {
pa_assert(bq);
+ pa_assert(length % bq->base == 0);
- if (memblockq_check_prebuf(bq))
- return 0;
+ /* This is kind of the inverse of pa_memblockq_drop() */
+
+ bq->read_index -= length;
+ bq->missing -= length;
+}
+
+pa_bool_t pa_memblockq_is_readable(pa_memblockq *bq) {
+ pa_assert(bq);
+
+ if (pa_memblockq_prebuf_active(bq))
+ return FALSE;
if (pa_memblockq_get_length(bq) <= 0)
- return 0;
+ return FALSE;
- return 1;
+ return TRUE;
}
size_t pa_memblockq_get_length(pa_memblockq *bq) {
@@ -523,12 +600,6 @@ size_t pa_memblockq_missing(pa_memblockq *bq) {
return l >= bq->minreq ? l : 0;
}
-size_t pa_memblockq_get_minreq(pa_memblockq *bq) {
- pa_assert(bq);
-
- return bq->minreq;
-}
-
void pa_memblockq_seek(pa_memblockq *bq, int64_t offset, pa_seek_mode_t seek) {
int64_t old, delta;
pa_assert(bq);
@@ -552,9 +623,11 @@ void pa_memblockq_seek(pa_memblockq *bq, int64_t offset, pa_seek_mode_t seek) {
pa_assert_not_reached();
}
+ drop_backlog(bq);
+
delta = bq->write_index - old;
- if (delta >= bq->requested) {
+ if (delta >= (int64_t) bq->requested) {
delta -= bq->requested;
bq->requested = 0;
} else if (delta >= 0) {
@@ -569,10 +642,7 @@ void pa_memblockq_flush(pa_memblockq *bq) {
int64_t old, delta;
pa_assert(bq);
- while (bq->blocks)
- drop_block(bq, bq->blocks);
-
- pa_assert(bq->n_blocks == 0);
+ pa_memblockq_silence(bq);
old = bq->write_index;
bq->write_index = bq->read_index;
@@ -581,7 +651,7 @@ void pa_memblockq_flush(pa_memblockq *bq) {
delta = bq->write_index - old;
- if (delta > bq->requested) {
+ if (delta >= (int64_t) bq->requested) {
delta -= bq->requested;
bq->requested = 0;
} else if (delta >= 0) {
@@ -598,13 +668,21 @@ size_t pa_memblockq_get_tlength(pa_memblockq *bq) {
return bq->tlength;
}
+size_t pa_memblockq_get_minreq(pa_memblockq *bq) {
+ pa_assert(bq);
+
+ return bq->minreq;
+}
+
int64_t pa_memblockq_get_read_index(pa_memblockq *bq) {
pa_assert(bq);
+
return bq->read_index;
}
int64_t pa_memblockq_get_write_index(pa_memblockq *bq) {
pa_assert(bq);
+
return bq->write_index;
}
@@ -617,9 +695,6 @@ int pa_memblockq_push_align(pa_memblockq* bq, const pa_memchunk *chunk) {
if (bq->base == 1)
return pa_memblockq_push(bq, chunk);
- if (!bq->mcalign)
- bq->mcalign = pa_mcalign_new(bq->base);
-
if (!can_push(bq, pa_mcalign_csize(bq->mcalign, chunk->length)))
return -1;
@@ -630,23 +705,15 @@ int pa_memblockq_push_align(pa_memblockq* bq, const pa_memchunk *chunk) {
r = pa_memblockq_push(bq, &rchunk);
pa_memblock_unref(rchunk.memblock);
- if (r < 0)
+ if (r < 0) {
+ pa_mcalign_flush(bq->mcalign);
return -1;
+ }
}
return 0;
}
-void pa_memblockq_shorten(pa_memblockq *bq, size_t length) {
- size_t l;
- pa_assert(bq);
-
- l = pa_memblockq_get_length(bq);
-
- if (l > length)
- pa_memblockq_drop(bq, l - length);
-}
-
void pa_memblockq_prebuf_disable(pa_memblockq *bq) {
pa_assert(bq);
@@ -656,7 +723,7 @@ void pa_memblockq_prebuf_disable(pa_memblockq *bq) {
void pa_memblockq_prebuf_force(pa_memblockq *bq) {
pa_assert(bq);
- if (!bq->in_prebuf && bq->prebuf > 0)
+ if (bq->prebuf > 0)
bq->in_prebuf = TRUE;
}
@@ -688,3 +755,163 @@ size_t pa_memblockq_pop_missing(pa_memblockq *bq) {
return l;
}
+
+void pa_memblockq_set_maxlength(pa_memblockq *bq, size_t maxlength) {
+ pa_assert(bq);
+
+ bq->maxlength = ((maxlength+bq->base-1)/bq->base)*bq->base;
+
+ if (bq->maxlength < bq->base)
+ bq->maxlength = bq->base;
+
+ 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)
+ tlength = bq->maxlength;
+
+ old_tlength = bq->tlength;
+ bq->tlength = ((tlength+bq->base-1)/bq->base)*bq->base;
+
+ 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);
+
+ bq->missing += (int64_t) bq->tlength - (int64_t) old_tlength;
+}
+
+void pa_memblockq_set_prebuf(pa_memblockq *bq, size_t prebuf) {
+ pa_assert(bq);
+
+ if (prebuf == (size_t) -1)
+ prebuf = bq->tlength;
+
+ 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 <= 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) {
+ pa_assert(bq);
+
+ bq->minreq = (minreq/bq->base)*bq->base;
+
+ if (bq->minreq > bq->tlength)
+ bq->minreq = bq->tlength;
+
+ if (bq->minreq > bq->prebuf)
+ bq->minreq = bq->prebuf;
+
+ if (bq->minreq < bq->base)
+ bq->minreq = bq->base;
+}
+
+void pa_memblockq_set_maxrewind(pa_memblockq *bq, size_t maxrewind) {
+ pa_assert(bq);
+
+ bq->maxrewind = (maxrewind/bq->base)*bq->base;
+}
+
+int pa_memblockq_splice(pa_memblockq *bq, pa_memblockq *source) {
+
+ pa_assert(bq);
+ pa_assert(source);
+
+ pa_memblockq_prebuf_disable(bq);
+
+ for (;;) {
+ pa_memchunk chunk;
+
+ if (pa_memblockq_peek(source, &chunk) < 0)
+ return 0;
+
+ pa_assert(chunk.length > 0);
+
+ if (chunk.memblock) {
+
+ if (pa_memblockq_push_align(bq, &chunk) < 0) {
+ pa_memblock_unref(chunk.memblock);
+ return -1;
+ }
+
+ pa_memblock_unref(chunk.memblock);
+ } else
+ pa_memblockq_seek(bq, chunk.length, PA_SEEK_RELATIVE);
+
+ pa_memblockq_drop(bq, chunk.length);
+ }
+}
+
+void pa_memblockq_willneed(pa_memblockq *bq) {
+ struct list_item *q;
+
+ pa_assert(bq);
+
+ fix_current_read(bq);
+
+ for (q = bq->current_read; q; q = q->next)
+ pa_memchunk_will_need(&q->chunk);
+}
+
+void pa_memblockq_set_silence(pa_memblockq *bq, pa_memchunk *silence) {
+ pa_assert(bq);
+
+ if (bq->silence.memblock)
+ pa_memblock_unref(bq->silence.memblock);
+
+ if (silence) {
+ bq->silence = *silence;
+ pa_memblock_ref(bq->silence.memblock);
+ } else
+ pa_memchunk_reset(&bq->silence);
+}
+
+pa_bool_t pa_memblockq_is_empty(pa_memblockq *bq) {
+ pa_assert(bq);
+
+ return !bq->blocks;
+}
+
+void pa_memblockq_silence(pa_memblockq *bq) {
+ pa_assert(bq);
+
+ while (bq->blocks)
+ drop_block(bq, bq->blocks);
+
+ pa_assert(bq->n_blocks == 0);
+}
+
+unsigned pa_memblockq_get_nblocks(pa_memblockq *bq) {
+ pa_assert(bq);
+
+ return bq->n_blocks;
+}
+
+size_t pa_memblockq_get_base(pa_memblockq *bq) {
+ pa_assert(bq);
+
+ return bq->base;
+}
diff --git a/src/pulsecore/memblockq.h b/src/pulsecore/memblockq.h
index 8c3e70fc..81f7cbb8 100644
--- a/src/pulsecore/memblockq.h
+++ b/src/pulsecore/memblockq.h
@@ -1,8 +1,6 @@
#ifndef foomemblockqhfoo
#define foomemblockqhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -62,7 +60,9 @@ typedef struct pa_memblockq pa_memblockq;
- minreq: pa_memblockq_missing() will only return values greater
than this value. Pass 0 for the default.
- - silence: return this memblock when reading unitialized data
+ - maxrewind: how many bytes of history to keep in the queue
+
+ - silence: return this memchunk when reading unitialized data
*/
pa_memblockq* pa_memblockq_new(
int64_t idx,
@@ -71,7 +71,8 @@ pa_memblockq* pa_memblockq_new(
size_t base,
size_t prebuf,
size_t minreq,
- pa_memblock *silence);
+ size_t maxrewind,
+ pa_memchunk *silence);
void pa_memblockq_free(pa_memblockq*bq);
@@ -95,7 +96,7 @@ int pa_memblockq_peek(pa_memblockq* bq, pa_memchunk *chunk);
void pa_memblockq_drop(pa_memblockq *bq, size_t length);
/* Test if the pa_memblockq is currently readable, that is, more data than base */
-int pa_memblockq_is_readable(pa_memblockq *bq);
+pa_bool_t pa_memblockq_is_readable(pa_memblockq *bq);
/* Return the length of the queue in bytes */
size_t pa_memblockq_get_length(pa_memblockq *bq);
@@ -107,6 +108,9 @@ size_t pa_memblockq_missing(pa_memblockq *bq);
* this function, reset the internal counter to 0. */
size_t pa_memblockq_pop_missing(pa_memblockq *bq);
+/* Directly moves the data from the source memblockq into bq */
+int pa_memblockq_splice(pa_memblockq *bq, pa_memblockq *source);
+
/* Returns the minimal request value */
size_t pa_memblockq_get_minreq(pa_memblockq *bq);
@@ -125,10 +129,8 @@ int64_t pa_memblockq_get_read_index(pa_memblockq *bq);
/* Return the current write index */
int64_t pa_memblockq_get_write_index(pa_memblockq *bq);
-/* Shorten the pa_memblockq to the specified length by dropping data
- * at the read end of the queue. The read index is increased until the
- * queue has the specified length */
-void pa_memblockq_shorten(pa_memblockq *bq, size_t length);
+/* Rewind the read index. If the history is shorter than the specified length we'll point to silence afterwards. */
+void pa_memblockq_rewind(pa_memblockq *bq, size_t length);
/* Ignore prebuf for now */
void pa_memblockq_prebuf_disable(pa_memblockq *bq);
@@ -142,4 +144,29 @@ size_t pa_memblockq_get_maxlength(pa_memblockq *bq);
/* Return the prebuffer length in bytes */
size_t pa_memblockq_get_prebuf(pa_memblockq *bq);
+/* Change metrics. Always call in order. */
+void pa_memblockq_set_maxlength(pa_memblockq *memblockq, size_t maxlength); /* might modify tlength, prebuf, minreq too */
+void pa_memblockq_set_tlength(pa_memblockq *memblockq, size_t tlength); /* might modify minreq, too */
+void pa_memblockq_set_prebuf(pa_memblockq *memblockq, size_t prebuf); /* might modify minreq, too */
+void pa_memblockq_set_minreq(pa_memblockq *memblockq, size_t minreq);
+void pa_memblockq_set_maxrewind(pa_memblockq *memblockq, size_t rewind); /* Set the maximum history size */
+void pa_memblockq_set_silence(pa_memblockq *memblockq, pa_memchunk *silence);
+
+/* 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);
+
+/* Check whether the memblockq is completely empty, i.e. no data
+ * neither left nor right of the read pointer, and hence no buffered
+ * data for the future nor data in the backlog. */
+pa_bool_t pa_memblockq_is_empty(pa_memblockq *bq);
+
+void pa_memblockq_silence(pa_memblockq *bq);
+
+/* Check whether we currently are in prebuf state */
+pa_bool_t pa_memblockq_prebuf_active(pa_memblockq *bq);
+
+unsigned pa_memblockq_get_nblocks(pa_memblockq *bq);
+
+size_t pa_memblockq_get_base(pa_memblockq *bq);
+
#endif
diff --git a/src/pulsecore/memchunk.c b/src/pulsecore/memchunk.c
index 4e73b636..0bbf8590 100644
--- a/src/pulsecore/memchunk.c
+++ b/src/pulsecore/memchunk.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -49,17 +47,20 @@ pa_memchunk* pa_memchunk_make_writable(pa_memchunk *c, size_t min) {
pa_memblock_get_length(c->memblock) >= c->index+min)
return c;
- l = c->length;
- if (l < min)
- l = min;
+ l = PA_MAX(c->length, min);
n = pa_memblock_new(pa_memblock_get_pool(c->memblock), l);
- tdata = pa_memblock_acquire(n);
+
sdata = pa_memblock_acquire(c->memblock);
+ tdata = pa_memblock_acquire(n);
+
memcpy(tdata, (uint8_t*) sdata + c->index, c->length);
- pa_memblock_release(n);
+
pa_memblock_release(c->memblock);
+ pa_memblock_release(n);
+
pa_memblock_unref(c->memblock);
+
c->memblock = n;
c->index = 0;
@@ -69,8 +70,7 @@ pa_memchunk* pa_memchunk_make_writable(pa_memchunk *c, size_t min) {
pa_memchunk* pa_memchunk_reset(pa_memchunk *c) {
pa_assert(c);
- c->memblock = NULL;
- c->length = c->index = 0;
+ memset(c, 0, sizeof(*c));
return c;
}
@@ -90,3 +90,23 @@ pa_memchunk *pa_memchunk_will_need(const pa_memchunk *c) {
return (pa_memchunk*) c;
}
+
+pa_memchunk* pa_memchunk_memcpy(pa_memchunk *dst, pa_memchunk *src) {
+ void *p, *q;
+
+ pa_assert(dst);
+ pa_assert(src);
+ pa_assert(dst->length == src->length);
+
+ p = pa_memblock_acquire(dst->memblock);
+ q = pa_memblock_acquire(src->memblock);
+
+ memmove((uint8_t*) p + dst->index,
+ (uint8_t*) q + src->index,
+ dst->length);
+
+ pa_memblock_release(dst->memblock);
+ pa_memblock_release(src->memblock);
+
+ return dst;
+}
diff --git a/src/pulsecore/memchunk.h b/src/pulsecore/memchunk.h
index e6105ace..9458f4ff 100644
--- a/src/pulsecore/memchunk.h
+++ b/src/pulsecore/memchunk.h
@@ -1,8 +1,6 @@
#ifndef foomemchunkhfoo
#define foomemchunkhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -49,4 +47,7 @@ pa_memchunk* pa_memchunk_reset(pa_memchunk *c);
/* Map a memory chunk back into memory if it was swapped out */
pa_memchunk *pa_memchunk_will_need(const pa_memchunk *c);
+/* Copy the data in the src memchunk to the dst memchunk */
+pa_memchunk* pa_memchunk_memcpy(pa_memchunk *dst, pa_memchunk *src);
+
#endif
diff --git a/src/pulsecore/modargs.c b/src/pulsecore/modargs.c
index 7ce3dd08..5f5902c9 100644
--- a/src/pulsecore/modargs.c
+++ b/src/pulsecore/modargs.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -48,11 +46,17 @@ struct entry {
static int add_key_value(pa_hashmap *map, char *key, char *value, const char* const valid_keys[]) {
struct entry *e;
-
+
pa_assert(map);
pa_assert(key);
pa_assert(value);
+ if (pa_hashmap_get(map, key)) {
+ pa_xfree(key);
+ pa_xfree(value);
+ return -1;
+ }
+
if (valid_keys) {
const char*const* v;
for (v = valid_keys; *v; v++)
@@ -70,7 +74,7 @@ static int add_key_value(pa_hashmap *map, char *key, char *value, const char* co
e->key = key;
e->value = value;
pa_hashmap_put(map, key, e);
-
+
return 0;
}
@@ -80,7 +84,15 @@ pa_modargs *pa_modargs_new(const char *args, const char* const* valid_keys) {
map = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
if (args) {
- enum { WHITESPACE, KEY, VALUE_START, VALUE_SIMPLE, VALUE_DOUBLE_QUOTES, VALUE_TICKS } state;
+ enum {
+ WHITESPACE,
+ KEY,
+ VALUE_START,
+ VALUE_SIMPLE,
+ VALUE_DOUBLE_QUOTES,
+ VALUE_TICKS
+ } state;
+
const char *p, *key, *value;
size_t key_len = 0, value_len = 0;
@@ -100,6 +112,8 @@ pa_modargs *pa_modargs_new(const char *args, const char* const* valid_keys) {
case KEY:
if (*p == '=')
state = VALUE_START;
+ else if (isspace(*p))
+ goto fail;
else
key_len++;
break;
@@ -172,7 +186,7 @@ fail:
static void free_func(void *p, PA_GCC_UNUSED void*userdata) {
struct entry *e = p;
pa_assert(e);
-
+
pa_xfree(e->key);
pa_xfree(e->value);
pa_xfree(e);
@@ -225,7 +239,7 @@ int pa_modargs_get_value_s32(pa_modargs *ma, const char *key, int32_t *value) {
return 0;
}
-int pa_modargs_get_value_boolean(pa_modargs *ma, const char *key, int *value) {
+int pa_modargs_get_value_boolean(pa_modargs *ma, const char *key, pa_bool_t *value) {
const char *v;
int r;
@@ -250,7 +264,7 @@ int pa_modargs_get_sample_spec(pa_modargs *ma, pa_sample_spec *rss) {
const char *format;
uint32_t channels;
pa_sample_spec ss;
-
+
pa_assert(ma);
pa_assert(rss);
diff --git a/src/pulsecore/modargs.h b/src/pulsecore/modargs.h
index aa175885..23766cfc 100644
--- a/src/pulsecore/modargs.h
+++ b/src/pulsecore/modargs.h
@@ -1,8 +1,6 @@
#ifndef foomodargshfoo
#define foomodargshfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -28,6 +26,7 @@
#include <pulse/sample.h>
#include <pulse/channelmap.h>
#include <pulsecore/core.h>
+#include <pulsecore/macro.h>
typedef struct pa_modargs pa_modargs;
@@ -44,7 +43,7 @@ const char *pa_modargs_get_value(pa_modargs *ma, const char *key, const char *de
/* Return a module argument as unsigned 32bit value in *value */
int pa_modargs_get_value_u32(pa_modargs *ma, const char *key, uint32_t *value);
int pa_modargs_get_value_s32(pa_modargs *ma, const char *key, int32_t *value);
-int pa_modargs_get_value_boolean(pa_modargs *ma, const char *key, int *value);
+int pa_modargs_get_value_boolean(pa_modargs *ma, const char *key, pa_bool_t *value);
/* Return sample spec data from the three arguments "rate", "format" and "channels" */
int pa_modargs_get_sample_spec(pa_modargs *ma, pa_sample_spec *ss);
diff --git a/src/pulsecore/modinfo.c b/src/pulsecore/modinfo.c
index 9f53887b..ac4ca88a 100644
--- a/src/pulsecore/modinfo.c
+++ b/src/pulsecore/modinfo.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -40,10 +38,12 @@
#define PA_SYMBOL_DESCRIPTION "pa__get_description"
#define PA_SYMBOL_USAGE "pa__get_usage"
#define PA_SYMBOL_VERSION "pa__get_version"
+#define PA_SYMBOL_LOAD_ONCE "pa__load_once"
pa_modinfo *pa_modinfo_get_by_handle(lt_dlhandle dl, const char *module_name) {
pa_modinfo *i;
const char* (*func)(void);
+ pa_bool_t (*func2) (void);
pa_assert(dl);
@@ -61,13 +61,16 @@ 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 ((func2 = (pa_bool_t (*)(void)) pa_load_sym(dl, module_name, PA_SYMBOL_LOAD_ONCE)))
+ i->load_once = func2();
+
return i;
}
pa_modinfo *pa_modinfo_get_by_name(const char *name) {
lt_dlhandle dl;
pa_modinfo *i;
-
+
pa_assert(name);
if (!(dl = lt_dlopenext(name))) {
@@ -83,7 +86,7 @@ pa_modinfo *pa_modinfo_get_by_name(const char *name) {
void pa_modinfo_free(pa_modinfo *i) {
pa_assert(i);
-
+
pa_xfree(i->author);
pa_xfree(i->description);
pa_xfree(i->usage);
diff --git a/src/pulsecore/modinfo.h b/src/pulsecore/modinfo.h
index 02e536c6..605637c4 100644
--- a/src/pulsecore/modinfo.h
+++ b/src/pulsecore/modinfo.h
@@ -1,8 +1,6 @@
#ifndef foomodinfohfoo
#define foomodinfohfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -25,12 +23,14 @@
***/
/* Some functions for reading module meta data from PulseAudio modules */
+#include <pulsecore/macro.h>
typedef struct pa_modinfo {
char *author;
char *description;
char *usage;
char *version;
+ pa_bool_t load_once;
} pa_modinfo;
/* Read meta data from an libtool handle */
diff --git a/src/pulsecore/module.c b/src/pulsecore/module.c
index e6d7fcaf..f1eeb762 100644
--- a/src/pulsecore/module.c
+++ b/src/pulsecore/module.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -46,6 +44,7 @@
#define PA_SYMBOL_INIT "pa__init"
#define PA_SYMBOL_DONE "pa__done"
+#define PA_SYMBOL_LOAD_ONCE "pa__load_once"
#define UNLOAD_POLL_TIME 2
@@ -66,6 +65,7 @@ static void timeout_callback(pa_mainloop_api *m, pa_time_event*e, PA_GCC_UNUSED
pa_module* pa_module_load(pa_core *c, const char *name, const char *argument) {
pa_module *m = NULL;
+ pa_bool_t (*load_once)(void);
pa_assert(c);
pa_assert(name);
@@ -82,6 +82,22 @@ pa_module* pa_module_load(pa_core *c, const char *name, const char *argument) {
goto fail;
}
+ if ((load_once = (pa_bool_t (*)(void)) pa_load_sym(m->dl, name, PA_SYMBOL_LOAD_ONCE))) {
+
+ if (load_once() && c->modules) {
+ pa_module *i;
+ uint32_t idx;
+ /* OK, the module only wants to be loaded once, let's make sure it is */
+
+ for (i = pa_idxset_first(c->modules, &idx); i; i = pa_idxset_next(c->modules, &idx)) {
+ if (strcmp(name, i->name) == 0) {
+ pa_log("Module \"%s\" should be loaded once at most. Refusing to load.", name);
+ goto fail;
+ }
+ }
+ }
+ }
+
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;
@@ -91,8 +107,8 @@ pa_module* pa_module_load(pa_core *c, const char *name, const char *argument) {
m->userdata = NULL;
m->core = c;
m->n_used = -1;
- m->auto_unload = 0;
- m->unload_requested = 0;
+ m->auto_unload = FALSE;
+ m->unload_requested = FALSE;
if (m->init(m) < 0) {
pa_log_error("Failed to load module \"%s\" (argument: \"%s\"): initialization failed.", name, argument ? argument : "");
@@ -102,7 +118,7 @@ pa_module* pa_module_load(pa_core *c, const char *name, const char *argument) {
if (!c->modules)
c->modules = pa_idxset_new(NULL, NULL);
- if (!c->module_auto_unload_event) {
+ if (m->auto_unload && !c->module_auto_unload_event) {
struct timeval ntv;
pa_gettimeofday(&ntv);
pa_timeval_add(&ntv, UNLOAD_POLL_TIME*1000000);
@@ -186,7 +202,7 @@ static void free_callback(void *p, PA_GCC_UNUSED void *userdata) {
void pa_module_unload_all(pa_core *c) {
pa_module *m;
-
+
pa_assert(c);
if (!c->modules)
@@ -212,7 +228,7 @@ void pa_module_unload_all(pa_core *c) {
static int unused_callback(void *p, PA_GCC_UNUSED uint32_t idx, int *del, void *userdata) {
pa_module *m = p;
time_t *now = userdata;
-
+
pa_assert(m);
pa_assert(del);
pa_assert(now);
@@ -263,7 +279,7 @@ static void defer_cb(pa_mainloop_api*api, pa_defer_event *e, void *userdata) {
void pa_module_unload_request(pa_module *m) {
pa_assert(m);
- m->unload_requested = 1;
+ m->unload_requested = TRUE;
if (!m->core->module_defer_unload_event)
m->core->module_defer_unload_event = m->core->mainloop->defer_new(m->core->mainloop, defer_cb, m->core);
diff --git a/src/pulsecore/module.h b/src/pulsecore/module.h
index 7a93a071..ec582f25 100644
--- a/src/pulsecore/module.h
+++ b/src/pulsecore/module.h
@@ -1,8 +1,6 @@
#ifndef foomodulehfoo
#define foomodulehfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -45,10 +43,10 @@ struct pa_module {
void *userdata;
int n_used;
- int auto_unload;
+ pa_bool_t auto_unload;
time_t last_used_time;
- int unload_requested;
+ pa_bool_t unload_requested;
};
pa_module* pa_module_load(pa_core *c, const char *name, const char*argument);
@@ -62,10 +60,25 @@ void pa_module_unload_request(pa_module *m);
void pa_module_set_used(pa_module*m, int used);
-#define PA_MODULE_AUTHOR(s) const char *pa__get_author(void) { return s; }
-#define PA_MODULE_DESCRIPTION(s) const char *pa__get_description(void) { return s; }
-#define PA_MODULE_USAGE(s) const char *pa__get_usage(void) { return s; }
-#define PA_MODULE_VERSION(s) const char * pa__get_version(void) { return s; }
+#define PA_MODULE_AUTHOR(s) \
+ const char *pa__get_author(void) { return s; } \
+ struct __stupid_useless_struct_to_allow_trailing_semicolon
+
+#define PA_MODULE_DESCRIPTION(s) \
+ const char *pa__get_description(void) { return s; } \
+ struct __stupid_useless_struct_to_allow_trailing_semicolon
+
+#define PA_MODULE_USAGE(s) \
+ const char *pa__get_usage(void) { return s; } \
+ struct __stupid_useless_struct_to_allow_trailing_semicolon
+
+#define PA_MODULE_VERSION(s) \
+ const char * pa__get_version(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
pa_modinfo *pa_module_get_info(pa_module *m);
diff --git a/src/pulsecore/msgobject.c b/src/pulsecore/msgobject.c
index f54e69f2..81417ea4 100644
--- a/src/pulsecore/msgobject.c
+++ b/src/pulsecore/msgobject.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/msgobject.h b/src/pulsecore/msgobject.h
index 8221cc33..1a43fa35 100644
--- a/src/pulsecore/msgobject.h
+++ b/src/pulsecore/msgobject.h
@@ -1,8 +1,6 @@
#ifndef foopulsemsgobjecthfoo
#define foopulsemsgobjecthfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/mutex-posix.c b/src/pulsecore/mutex-posix.c
index 805f11de..35465b7b 100644
--- a/src/pulsecore/mutex-posix.c
+++ b/src/pulsecore/mutex-posix.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -49,7 +47,7 @@ pa_mutex* pa_mutex_new(pa_bool_t recursive, pa_bool_t inherit_priority) {
int r;
pa_assert_se(pthread_mutexattr_init(&attr) == 0);
-
+
if (recursive)
pa_assert_se(pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE) == 0);
@@ -60,9 +58,9 @@ pa_mutex* pa_mutex_new(pa_bool_t recursive, pa_bool_t inherit_priority) {
m = pa_xnew(pa_mutex, 1);
-#ifndef HAVE_PTHREAD_PRIO_INHERIT
+#ifndef HAVE_PTHREAD_PRIO_INHERIT
pa_assert_se(pthread_mutex_init(&m->mutex, &attr) == 0);
-
+
#else
if ((r = pthread_mutex_init(&m->mutex, &attr))) {
@@ -74,8 +72,8 @@ pa_mutex* pa_mutex_new(pa_bool_t recursive, pa_bool_t inherit_priority) {
pa_assert_se(pthread_mutexattr_setprotocol(&attr, PTHREAD_PRIO_NONE) == 0);
pa_assert_se(pthread_mutex_init(&m->mutex, &attr) == 0);
}
-#endif
-
+#endif
+
return m;
}
@@ -92,6 +90,18 @@ void pa_mutex_lock(pa_mutex *m) {
pa_assert_se(pthread_mutex_lock(&m->mutex) == 0);
}
+pa_bool_t pa_mutex_try_lock(pa_mutex *m) {
+ int r;
+ pa_assert(m);
+
+ if ((r = pthread_mutex_trylock(&m->mutex)) != 0) {
+ pa_assert(r == EBUSY);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
void pa_mutex_unlock(pa_mutex *m) {
pa_assert(m);
diff --git a/src/pulsecore/mutex-win32.c b/src/pulsecore/mutex-win32.c
index 77d63d15..5e884e7f 100644
--- a/src/pulsecore/mutex-win32.c
+++ b/src/pulsecore/mutex-win32.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/mutex.h b/src/pulsecore/mutex.h
index 72e88781..36e1d635 100644
--- a/src/pulsecore/mutex.h
+++ b/src/pulsecore/mutex.h
@@ -1,8 +1,6 @@
#ifndef foopulsemutexhfoo
#define foopulsemutexhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -37,6 +35,7 @@ pa_mutex* pa_mutex_new(pa_bool_t recursive, pa_bool_t inherit_priority);
void pa_mutex_free(pa_mutex *m);
void pa_mutex_lock(pa_mutex *m);
+pa_bool_t pa_mutex_try_lock(pa_mutex *m);
void pa_mutex_unlock(pa_mutex *m);
typedef struct pa_cond pa_cond;
diff --git a/src/pulsecore/namereg.c b/src/pulsecore/namereg.c
index fe2be467..cc18adab 100644
--- a/src/pulsecore/namereg.c
+++ b/src/pulsecore/namereg.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -56,20 +54,20 @@ static int is_valid_char(char c) {
c == '_';
}
-static int is_valid_name(const char *name) {
+pa_bool_t pa_namereg_is_valid_name(const char *name) {
const char *c;
if (*name == 0)
- return 0;
+ return FALSE;
for (c = name; *c && (c-name < PA_NAME_MAX); c++)
if (!is_valid_char(*c))
- return 0;
+ return FALSE;
if (*c)
- return 0;
+ return FALSE;
- return 1;
+ return TRUE;
}
static char* cleanup_name(const char *name) {
@@ -111,7 +109,7 @@ const char *pa_namereg_register(pa_core *c, const char *name, pa_namereg_type_t
return NULL;
if ((type == PA_NAMEREG_SINK || type == PA_NAMEREG_SOURCE) &&
- !is_valid_name(name) ) {
+ !pa_namereg_is_valid_name(name) ) {
if (fail)
return NULL;
@@ -179,7 +177,7 @@ void pa_namereg_unregister(pa_core *c, const char *name) {
pa_xfree(e);
}
-void* pa_namereg_get(pa_core *c, const char *name, pa_namereg_type_t type, int autoload) {
+void* pa_namereg_get(pa_core *c, const char *name, pa_namereg_type_t type, pa_bool_t autoload) {
struct namereg_entry *e;
uint32_t idx;
pa_assert(c);
@@ -253,7 +251,7 @@ int pa_namereg_set_default(pa_core*c, const char *name, pa_namereg_type_t type)
if (name && *s && !strcmp(name, *s))
return 0;
- if (!is_valid_name(name))
+ if (!pa_namereg_is_valid_name(name))
return -1;
pa_xfree(*s);
diff --git a/src/pulsecore/namereg.h b/src/pulsecore/namereg.h
index 350ba0f6..af0153ec 100644
--- a/src/pulsecore/namereg.h
+++ b/src/pulsecore/namereg.h
@@ -1,8 +1,6 @@
#ifndef foonamereghfoo
#define foonamereghfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -25,6 +23,7 @@
***/
#include <pulsecore/core.h>
+#include <pulsecore/macro.h>
#define PA_NAME_MAX 128
@@ -38,10 +37,12 @@ void pa_namereg_free(pa_core *c);
const char *pa_namereg_register(pa_core *c, const char *name, pa_namereg_type_t type, void *data, int fail);
void pa_namereg_unregister(pa_core *c, const char *name);
-void* pa_namereg_get(pa_core *c, const char *name, pa_namereg_type_t type, int autoload);
+void* pa_namereg_get(pa_core *c, const char *name, pa_namereg_type_t type, pa_bool_t autoload);
int pa_namereg_set_default(pa_core*c, const char *name, pa_namereg_type_t type);
const char *pa_namereg_get_default_sink_name(pa_core *c);
const char *pa_namereg_get_default_source_name(pa_core *c);
+pa_bool_t pa_namereg_is_valid_name(const char *name);
+
#endif
diff --git a/src/pulsecore/native-common.h b/src/pulsecore/native-common.h
index 9defc4a5..809d6c75 100644
--- a/src/pulsecore/native-common.h
+++ b/src/pulsecore/native-common.h
@@ -1,8 +1,6 @@
#ifndef foonativecommonhfoo
#define foonativecommonhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -36,10 +34,10 @@ enum {
PA_COMMAND_TIMEOUT, /* pseudo command */
PA_COMMAND_REPLY,
- /* Commands from client to server */
- PA_COMMAND_CREATE_PLAYBACK_STREAM,
+ /* CLIENT->SERVER */
+ PA_COMMAND_CREATE_PLAYBACK_STREAM, /* Payload changed in v9, v12 (0.9.0, 0.9.8) */
PA_COMMAND_DELETE_PLAYBACK_STREAM,
- PA_COMMAND_CREATE_RECORD_STREAM,
+ PA_COMMAND_CREATE_RECORD_STREAM, /* Payload changed in v9, v12 (0.9.0, 0.9.8) */
PA_COMMAND_DELETE_RECORD_STREAM,
PA_COMMAND_EXIT,
PA_COMMAND_AUTH,
@@ -64,8 +62,8 @@ enum {
PA_COMMAND_GET_MODULE_INFO_LIST,
PA_COMMAND_GET_CLIENT_INFO,
PA_COMMAND_GET_CLIENT_INFO_LIST,
- PA_COMMAND_GET_SINK_INPUT_INFO,
- PA_COMMAND_GET_SINK_INPUT_INFO_LIST,
+ PA_COMMAND_GET_SINK_INPUT_INFO, /* Payload changed in v11 (0.9.7) */
+ PA_COMMAND_GET_SINK_INPUT_INFO_LIST, /* Payload changed in v11 (0.9.7) */
PA_COMMAND_GET_SOURCE_OUTPUT_INFO,
PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST,
PA_COMMAND_GET_SAMPLE_INFO,
@@ -92,18 +90,21 @@ enum {
PA_COMMAND_KILL_CLIENT,
PA_COMMAND_KILL_SINK_INPUT,
PA_COMMAND_KILL_SOURCE_OUTPUT,
+
PA_COMMAND_LOAD_MODULE,
PA_COMMAND_UNLOAD_MODULE,
+
PA_COMMAND_ADD_AUTOLOAD,
PA_COMMAND_REMOVE_AUTOLOAD,
PA_COMMAND_GET_AUTOLOAD_INFO,
PA_COMMAND_GET_AUTOLOAD_INFO_LIST,
+
PA_COMMAND_GET_RECORD_LATENCY,
PA_COMMAND_CORK_RECORD_STREAM,
PA_COMMAND_FLUSH_RECORD_STREAM,
PA_COMMAND_PREBUF_PLAYBACK_STREAM,
- /* Commands from server to client */
+ /* SERVER->CLIENT */
PA_COMMAND_REQUEST,
PA_COMMAND_OVERFLOW,
PA_COMMAND_UNDERFLOW,
@@ -112,14 +113,41 @@ enum {
PA_COMMAND_SUBSCRIBE_EVENT,
/* A few more client->server commands */
+
+ /* Supported since protocol v10 (0.9.5) */
PA_COMMAND_MOVE_SINK_INPUT,
PA_COMMAND_MOVE_SOURCE_OUTPUT,
+ /* Supported since protocol v11 (0.9.7) */
PA_COMMAND_SET_SINK_INPUT_MUTE,
PA_COMMAND_SUSPEND_SINK,
PA_COMMAND_SUSPEND_SOURCE,
+ /* Supported since protocol v12 (0.9.8) */
+ PA_COMMAND_SET_PLAYBACK_STREAM_BUFFER_ATTR,
+ PA_COMMAND_SET_RECORD_STREAM_BUFFER_ATTR,
+
+ PA_COMMAND_UPDATE_PLAYBACK_STREAM_SAMPLE_RATE,
+ PA_COMMAND_UPDATE_RECORD_STREAM_SAMPLE_RATE,
+
+ /* SERVER->CLIENT */
+ PA_COMMAND_PLAYBACK_STREAM_SUSPENDED,
+ PA_COMMAND_RECORD_STREAM_SUSPENDED,
+ PA_COMMAND_PLAYBACK_STREAM_MOVED,
+ PA_COMMAND_RECORD_STREAM_MOVED,
+
+ /* Supported since protocol v13 (0.9.10) */
+ PA_COMMAND_UPDATE_RECORD_STREAM_PROPLIST,
+ PA_COMMAND_UPDATE_PLAYBACK_STREAM_PROPLIST,
+ PA_COMMAND_UPDATE_CLIENT_PROPLIST,
+ PA_COMMAND_REMOVE_RECORD_STREAM_PROPLIST,
+ PA_COMMAND_REMOVE_PLAYBACK_STREAM_PROPLIST,
+ PA_COMMAND_REMOVE_CLIENT_PROPLIST,
+
+ /* SERVER->CLIENT */
+ PA_COMMAND_STARTED,
+
PA_COMMAND_MAX
};
diff --git a/src/pulsecore/object.c b/src/pulsecore/object.c
index 6680d078..9a2f28f3 100644
--- a/src/pulsecore/object.c
+++ b/src/pulsecore/object.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -39,7 +37,7 @@ pa_object *pa_object_new_internal(size_t size, const char *type_name, int (*chec
pa_assert(check_type(type_name));
pa_assert(check_type("pa_object"));
-
+
o = pa_xmalloc(size);
PA_REFCNT_INIT(o);
o->type_name = type_name;
@@ -67,6 +65,6 @@ 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;
}
diff --git a/src/pulsecore/object.h b/src/pulsecore/object.h
index 562fd113..7dcfa2eb 100644
--- a/src/pulsecore/object.h
+++ b/src/pulsecore/object.h
@@ -1,8 +1,6 @@
#ifndef foopulseobjecthfoo
#define foopulseobjecthfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/once.c b/src/pulsecore/once.c
index 198bd41e..989741dc 100644
--- a/src/pulsecore/once.c
+++ b/src/pulsecore/once.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -43,7 +41,7 @@ int pa_once_begin(pa_once *control) {
/* Caveat: We have to make sure that the once func has completed
* before returning, even if the once func is not actually
* executed by us. Hence the awkward locking. */
-
+
for (;;) {
if ((m = pa_atomic_ptr_load(&control->mutex))) {
@@ -69,7 +67,7 @@ int pa_once_begin(pa_once *control) {
void pa_once_end(pa_once *control) {
pa_mutex *m;
-
+
pa_assert(control);
pa_atomic_store(&control->done, 1);
diff --git a/src/pulsecore/once.h b/src/pulsecore/once.h
index a4a0b239..576d40fa 100644
--- a/src/pulsecore/once.h
+++ b/src/pulsecore/once.h
@@ -1,8 +1,6 @@
#ifndef foopulseoncehfoo
#define foopulseoncehfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -55,18 +53,18 @@ void pa_once_end(pa_once *o);
} while(0)
/*
-
+
Usage of these macros is like this:
-
+
void foo() {
-
+
PA_ONCE_BEGIN {
-
+
... stuff to be called just once ...
-
+
} PA_ONCE_END;
}
-
+
*/
/* Same API but calls a function */
diff --git a/src/pulsecore/packet.c b/src/pulsecore/packet.c
index 2706efea..cee468bd 100644
--- a/src/pulsecore/packet.c
+++ b/src/pulsecore/packet.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/packet.h b/src/pulsecore/packet.h
index bcac4a7f..5989b1fa 100644
--- a/src/pulsecore/packet.h
+++ b/src/pulsecore/packet.h
@@ -1,8 +1,6 @@
#ifndef foopackethfoo
#define foopackethfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/parseaddr.c b/src/pulsecore/parseaddr.c
index 0df37f4e..f2b6b2cf 100644
--- a/src/pulsecore/parseaddr.c
+++ b/src/pulsecore/parseaddr.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -47,7 +45,7 @@
static char *parse_host(const char *s, uint16_t *ret_port) {
pa_assert(s);
pa_assert(ret_port);
-
+
if (*s == '[') {
char *e;
if (!(e = strchr(s+1, ']')))
@@ -72,10 +70,10 @@ static char *parse_host(const char *s, uint16_t *ret_port) {
int pa_parse_address(const char *name, pa_parsed_address *ret_p) {
const char *p;
-
+
pa_assert(name);
pa_assert(ret_p);
-
+
memset(ret_p, 0, sizeof(pa_parsed_address));
ret_p->type = PA_PARSED_ADDRESS_TCP_AUTO;
@@ -103,9 +101,12 @@ int pa_parse_address(const char *name, pa_parsed_address *ret_p) {
else if (pa_startswith(p, "unix:")) {
ret_p->type = PA_PARSED_ADDRESS_UNIX;
p += sizeof("unix:")-1;
- } else if (pa_startswith(p, "tcp:") || pa_startswith(p, "tcp4:")) {
+ } else if (pa_startswith(p, "tcp:")) {
ret_p->type = PA_PARSED_ADDRESS_TCP4;
p += sizeof("tcp:")-1;
+ } else if (pa_startswith(p, "tcp4:")) {
+ ret_p->type = PA_PARSED_ADDRESS_TCP4;
+ p += sizeof("tcp4:")-1;
} else if (pa_startswith(p, "tcp6:")) {
ret_p->type = PA_PARSED_ADDRESS_TCP6;
p += sizeof("tcp6:")-1;
diff --git a/src/pulsecore/parseaddr.h b/src/pulsecore/parseaddr.h
index fd7cad3b..5fbcb9a7 100644
--- a/src/pulsecore/parseaddr.h
+++ b/src/pulsecore/parseaddr.h
@@ -1,8 +1,6 @@
#ifndef fooparseaddrhfoo
#define fooparseaddrhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/pdispatch.c b/src/pulsecore/pdispatch.c
index 737b1b98..e6a6ae4d 100644
--- a/src/pulsecore/pdispatch.c
+++ b/src/pulsecore/pdispatch.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -38,6 +36,7 @@
#include <pulsecore/core-util.h>
#include <pulsecore/macro.h>
#include <pulsecore/refcnt.h>
+#include <pulsecore/flist.h>
#include "pdispatch.h"
@@ -98,6 +97,8 @@ static const char *command_names[PA_COMMAND_MAX] = {
#endif
+PA_STATIC_FLIST_DECLARE(reply_infos, 0, pa_xfree);
+
struct reply_info {
pa_pdispatch *pdispatch;
PA_LLIST_FIELDS(struct reply_info);
@@ -129,7 +130,8 @@ static void reply_info_free(struct reply_info *r) {
PA_LLIST_REMOVE(struct reply_info, r->pdispatch->replies, r);
- pa_xfree(r);
+ if (pa_flist_push(PA_STATIC_FLIST_GET(reply_infos), r) < 0)
+ pa_xfree(r);
}
pa_pdispatch* pa_pdispatch_new(pa_mainloop_api *mainloop, const pa_pdispatch_cb_t*table, unsigned entries) {
@@ -190,7 +192,7 @@ int pa_pdispatch_run(pa_pdispatch *pd, pa_packet*packet, const pa_creds *creds,
uint32_t tag, command;
pa_tagstruct *ts = NULL;
int ret = -1;
-
+
pa_assert(pd);
pa_assert(PA_REFCNT_VALUE(pd) >= 1);
pa_assert(packet);
@@ -255,7 +257,7 @@ finish:
static void timeout_callback(pa_mainloop_api*m, pa_time_event*e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) {
struct reply_info*r = userdata;
-
+
pa_assert(r);
pa_assert(r->time_event == e);
pa_assert(r->pdispatch);
@@ -268,12 +270,14 @@ static void timeout_callback(pa_mainloop_api*m, pa_time_event*e, PA_GCC_UNUSED c
void pa_pdispatch_register_reply(pa_pdispatch *pd, uint32_t tag, int timeout, pa_pdispatch_cb_t cb, void *userdata, pa_free_cb_t free_cb) {
struct reply_info *r;
struct timeval tv;
-
+
pa_assert(pd);
pa_assert(PA_REFCNT_VALUE(pd) >= 1);
pa_assert(cb);
- r = pa_xnew(struct reply_info, 1);
+ if (!(r = pa_flist_pop(PA_STATIC_FLIST_GET(reply_infos))))
+ r = pa_xnew(struct reply_info, 1);
+
r->pdispatch = pd;
r->callback = cb;
r->userdata = userdata;
diff --git a/src/pulsecore/pdispatch.h b/src/pulsecore/pdispatch.h
index de0aa3ec..5c31d80e 100644
--- a/src/pulsecore/pdispatch.h
+++ b/src/pulsecore/pdispatch.h
@@ -1,8 +1,6 @@
#ifndef foopdispatchhfoo
#define foopdispatchhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/pid.c b/src/pulsecore/pid.c
index 55ff2088..addb17cc 100644
--- a/src/pulsecore/pid.c
+++ b/src/pulsecore/pid.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -42,6 +40,7 @@
#endif
#include <pulse/xmalloc.h>
+#include <pulse/util.h>
#include <pulsecore/core-error.h>
#include <pulsecore/core-util.h>
@@ -139,20 +138,64 @@ fail:
return -1;
}
+static int proc_name_ours(pid_t pid, const char *procname) {
+#ifdef __linux__
+ char bn[PATH_MAX];
+ FILE *f;
+
+ pa_snprintf(bn, sizeof(bn), "/proc/%lu/stat", (unsigned long) pid);
+
+ if (!(f = fopen(bn, "r"))) {
+ pa_log_info("Failed to open %s: %s", bn, pa_cstrerror(errno));
+ return -1;
+ } else {
+ char *expected;
+ pa_bool_t good;
+ char stored[64];
+
+ if (!(fgets(stored, sizeof(stored), f))) {
+ pa_log_info("Failed to read from %s: %s", bn, feof(f) ? "EOF" : pa_cstrerror(errno));
+ fclose(f);
+ return -1;
+ }
+
+ fclose(f);
+
+ expected = pa_sprintf_malloc("%lu (%s)", (unsigned long) pid, procname);
+ good = pa_startswith(stored, expected);
+ pa_xfree(expected);
+
+#if !defined(__OPTIMIZE__)
+ if (!good) {
+ /* libtool likes to rename our binary names ... */
+ expected = pa_sprintf_malloc("%lu (lt-%s)", (unsigned long) pid, procname);
+ good = pa_startswith(stored, expected);
+ pa_xfree(expected);
+ }
+#endif
+
+ return !!good;
+ }
+#endif
+
+ return 1;
+}
+
/* Create a new PID file for the current process. */
-int pa_pid_file_create(void) {
+int pa_pid_file_create(const char *procname) {
int fd = -1;
int ret = -1;
- char fn[PATH_MAX];
char t[20];
pid_t pid;
size_t l;
+ char *fn;
#ifdef OS_IS_WIN32
HANDLE process;
#endif
- pa_runtime_path("pid", fn, sizeof(fn));
+ if (!(fn = pa_runtime_path("pid")))
+ goto fail;
if ((fd = open_pid_file(fn, O_CREAT|O_RDWR)) < 0)
goto fail;
@@ -160,14 +203,24 @@ int pa_pid_file_create(void) {
if ((pid = read_pid(fn, fd)) == (pid_t) -1)
pa_log_warn("Corrupt PID file, overwriting.");
else if (pid > 0) {
+
#ifdef OS_IS_WIN32
if ((process = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid)) != NULL) {
CloseHandle(process);
#else
if (kill(pid, 0) >= 0 || errno != ESRCH) {
#endif
- pa_log("Daemon already running.");
- goto fail;
+ int ours = 1;
+
+ if (procname)
+ if ((ours = proc_name_ours(pid, procname)) < 0)
+ goto fail;
+
+ if (ours) {
+ pa_log("Daemon already running.");
+ ret = 1;
+ goto fail;
+ }
}
pa_log_warn("Stale PID file, overwriting.");
@@ -199,17 +252,20 @@ fail:
}
}
+ pa_xfree(fn);
+
return ret;
}
/* Remove the PID file, if it is ours */
int pa_pid_file_remove(void) {
int fd = -1;
- char fn[PATH_MAX];
+ char *fn;
int ret = -1;
pid_t pid;
- pa_runtime_path("pid", fn, sizeof(fn));
+ if (!(fn = pa_runtime_path("pid")))
+ goto fail;
if ((fd = open_pid_file(fn, O_RDWR)) < 0) {
pa_log_warn("Failed to open PID file '%s': %s", fn, pa_cstrerror(errno));
@@ -231,7 +287,7 @@ int pa_pid_file_remove(void) {
#ifdef OS_IS_WIN32
pa_lock_fd(fd, 0);
- close(fd);
+ pa_close(fd);
fd = -1;
#endif
@@ -253,6 +309,8 @@ fail:
}
}
+ pa_xfree(fn);
+
return ret;
}
@@ -260,8 +318,8 @@ fail:
* exists and the PID therein too. Returns 0 on succcess, -1
* otherwise. If pid is non-NULL and a running daemon was found,
* return its PID therein */
-int pa_pid_file_check_running(pid_t *pid) {
- return pa_pid_file_kill(0, pid);
+int pa_pid_file_check_running(pid_t *pid, const char *procname) {
+ return pa_pid_file_kill(0, pid, procname);
}
#ifndef OS_IS_WIN32
@@ -269,16 +327,20 @@ int pa_pid_file_check_running(pid_t *pid) {
/* Kill a current running daemon. Return non-zero on success, -1
* otherwise. If successful *pid contains the PID of the daemon
* process. */
-int pa_pid_file_kill(int sig, pid_t *pid) {
+int pa_pid_file_kill(int sig, pid_t *pid, const char *procname) {
int fd = -1;
- char fn[PATH_MAX];
+ char *fn;
int ret = -1;
pid_t _pid;
+#ifdef __linux__
+ char *e = NULL;
+#endif
if (!pid)
pid = &_pid;
- pa_runtime_path("pid", fn, sizeof(fn));
+ if (!(fn = pa_runtime_path("pid")))
+ goto fail;
if ((fd = open_pid_file(fn, O_RDONLY)) < 0)
goto fail;
@@ -286,6 +348,16 @@ int pa_pid_file_kill(int sig, pid_t *pid) {
if ((*pid = read_pid(fn, fd)) == (pid_t) -1)
goto fail;
+ if (procname) {
+ int ours;
+
+ if ((ours = proc_name_ours(*pid, procname)) < 0)
+ goto fail;
+
+ if (!ours)
+ goto fail;
+ }
+
ret = kill(*pid, sig);
fail:
@@ -295,13 +367,19 @@ fail:
pa_close(fd);
}
+#ifdef __linux__
+ pa_xfree(e);
+#endif
+
+ pa_xfree(fn);
+
return ret;
}
#else /* OS_IS_WIN32 */
-int pa_pid_file_kill(int sig, pid_t *pid) {
+int pa_pid_file_kill(int sig, pid_t *pid, const char *exe_name) {
return -1;
}
diff --git a/src/pulsecore/pid.h b/src/pulsecore/pid.h
index 0f25d1c8..3c8a9de3 100644
--- a/src/pulsecore/pid.h
+++ b/src/pulsecore/pid.h
@@ -1,8 +1,6 @@
#ifndef foopidhfoo
#define foopidhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -24,9 +22,9 @@
USA.
***/
-int pa_pid_file_create(void);
+int pa_pid_file_create(const char *procname);
int pa_pid_file_remove(void);
-int pa_pid_file_check_running(pid_t *pid);
-int pa_pid_file_kill(int sig, pid_t *pid);
+int pa_pid_file_check_running(pid_t *pid, const char *procname);
+int pa_pid_file_kill(int sig, pid_t *pid, const char *procname);
#endif
diff --git a/src/pulsecore/pipe.c b/src/pulsecore/pipe.c
index e614c9c6..93d78a22 100644
--- a/src/pulsecore/pipe.c
+++ b/src/pulsecore/pipe.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/pipe.h b/src/pulsecore/pipe.h
index e013a2e7..9a7e62c8 100644
--- a/src/pulsecore/pipe.h
+++ b/src/pulsecore/pipe.h
@@ -1,8 +1,6 @@
#ifndef foopipehfoo
#define foopipehfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/play-memblockq.c b/src/pulsecore/play-memblockq.c
index 5652ac2b..8b3e79b9 100644
--- a/src/pulsecore/play-memblockq.c
+++ b/src/pulsecore/play-memblockq.c
@@ -1,9 +1,7 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
- Copyright 2006 Lennart Poettering
+ Copyright 2006-2008 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
@@ -30,10 +28,11 @@
#include <string.h>
#include <pulse/xmalloc.h>
+#include <pulse/gccmacro.h>
#include <pulsecore/sink-input.h>
-#include <pulsecore/gccmacro.h>
#include <pulsecore/thread-mq.h>
+#include <pulsecore/sample-util.h>
#include "play-memblockq.h"
@@ -59,10 +58,9 @@ static void memblockq_stream_unlink(memblockq_stream *u) {
return;
pa_sink_input_unlink(u->sink_input);
-
pa_sink_input_unref(u->sink_input);
u->sink_input = NULL;
-
+
memblockq_stream_unref(u);
}
@@ -70,8 +68,6 @@ static void memblockq_stream_free(pa_object *o) {
memblockq_stream *u = MEMBLOCKQ_STREAM(o);
pa_assert(u);
- memblockq_stream_unlink(u);
-
if (u->memblockq)
pa_memblockq_free(u->memblockq);
@@ -81,7 +77,7 @@ static void memblockq_stream_free(pa_object *o) {
static int memblockq_stream_process_msg(pa_msgobject *o, int code, void*userdata, int64_t offset, pa_memchunk *chunk) {
memblockq_stream *u = MEMBLOCKQ_STREAM(o);
memblockq_stream_assert_ref(u);
-
+
switch (code) {
case MEMBLOCKQ_STREAM_MESSAGE_UNLINK:
memblockq_stream_unlink(u);
@@ -92,15 +88,34 @@ static int memblockq_stream_process_msg(pa_msgobject *o, int code, void*userdata
}
static void sink_input_kill_cb(pa_sink_input *i) {
+ memblockq_stream *u;
+
+ pa_sink_input_assert_ref(i);
+ u = MEMBLOCKQ_STREAM(i->userdata);
+ memblockq_stream_assert_ref(u);
+
+ memblockq_stream_unlink(u);
+}
+
+/* Called from IO thread context */
+static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t state) {
+ memblockq_stream *u;
+
pa_sink_input_assert_ref(i);
+ u = MEMBLOCKQ_STREAM(i->userdata);
+ memblockq_stream_assert_ref(u);
- memblockq_stream_unlink(MEMBLOCKQ_STREAM(i->userdata));
+ /* If we are added for the first time, ask for a rewinding so that
+ * we are heard right-away. */
+ if (PA_SINK_INPUT_IS_LINKED(state) &&
+ i->thread_info.state == PA_SINK_INPUT_INIT)
+ pa_sink_input_request_rewind(i, 0, FALSE, TRUE);
}
-static int sink_input_peek_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) {
+static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) {
memblockq_stream *u;
- pa_assert(i);
+ pa_sink_input_assert_ref(i);
pa_assert(chunk);
u = MEMBLOCKQ_STREAM(i->userdata);
memblockq_stream_assert_ref(u);
@@ -109,36 +124,58 @@ static int sink_input_peek_cb(pa_sink_input *i, size_t length, pa_memchunk *chun
return -1;
if (pa_memblockq_peek(u->memblockq, chunk) < 0) {
- pa_memblockq_free(u->memblockq);
- u->memblockq = NULL;
- pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(u), MEMBLOCKQ_STREAM_MESSAGE_UNLINK, NULL, 0, NULL, NULL);
+
+ if (pa_sink_input_safe_to_remove(i)) {
+
+ pa_memblockq_free(u->memblockq);
+ u->memblockq = NULL;
+
+ pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(u), MEMBLOCKQ_STREAM_MESSAGE_UNLINK, NULL, 0, NULL, NULL);
+ }
+
return -1;
}
+ chunk->length = PA_MIN(chunk->length, nbytes);
+ pa_memblockq_drop(u->memblockq, chunk->length);
+
return 0;
}
-static void sink_input_drop_cb(pa_sink_input *i, size_t length) {
+static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
memblockq_stream *u;
- pa_assert(i);
- pa_assert(length > 0);
+ pa_sink_input_assert_ref(i);
+ pa_assert(nbytes > 0);
u = MEMBLOCKQ_STREAM(i->userdata);
memblockq_stream_assert_ref(u);
if (!u->memblockq)
return;
-
- pa_memblockq_drop(u->memblockq, length);
+
+ pa_memblockq_rewind(u->memblockq, nbytes);
+}
+
+static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) {
+ memblockq_stream *u;
+
+ pa_sink_input_assert_ref(i);
+ u = MEMBLOCKQ_STREAM(i->userdata);
+ memblockq_stream_assert_ref(u);
+
+ if (!u->memblockq)
+ return;
+
+ pa_memblockq_set_maxrewind(u->memblockq, nbytes);
}
pa_sink_input* pa_memblockq_sink_input_new(
pa_sink *sink,
- const char *name,
const pa_sample_spec *ss,
const pa_channel_map *map,
pa_memblockq *q,
- pa_cvolume *volume) {
+ pa_cvolume *volume,
+ pa_proplist *p) {
memblockq_stream *u = NULL;
pa_sink_input_new_data data;
@@ -149,48 +186,43 @@ pa_sink_input* pa_memblockq_sink_input_new(
/* We allow creating this stream with no q set, so that it can be
* filled in later */
- if (q && pa_memblockq_get_length(q) <= 0) {
- pa_memblockq_free(q);
- return NULL;
- }
-
- if (volume && pa_cvolume_is_muted(volume)) {
- pa_memblockq_free(q);
- return NULL;
- }
-
u = pa_msgobject_new(memblockq_stream);
u->parent.parent.free = memblockq_stream_free;
u->parent.process_msg = memblockq_stream_process_msg;
u->core = sink->core;
u->sink_input = NULL;
- u->memblockq = q;
+ u->memblockq = NULL;
pa_sink_input_new_data_init(&data);
data.sink = sink;
- data.name = name;
data.driver = __FILE__;
pa_sink_input_new_data_set_sample_spec(&data, ss);
pa_sink_input_new_data_set_channel_map(&data, map);
pa_sink_input_new_data_set_volume(&data, volume);
+ pa_proplist_update(data.proplist, PA_UPDATE_REPLACE, p);
+
+ u->sink_input = pa_sink_input_new(sink->core, &data, 0);
+ pa_sink_input_new_data_done(&data);
- if (!(u->sink_input = pa_sink_input_new(sink->core, &data, 0)))
+ if (!u->sink_input)
goto fail;
-
- u->sink_input->peek = sink_input_peek_cb;
- u->sink_input->drop = sink_input_drop_cb;
+
+ u->sink_input->pop = sink_input_pop_cb;
+ u->sink_input->process_rewind = sink_input_process_rewind_cb;
+ u->sink_input->update_max_rewind = sink_input_update_max_rewind_cb;
u->sink_input->kill = sink_input_kill_cb;
+ u->sink_input->state_change = sink_input_state_change_cb;
u->sink_input->userdata = u;
if (q)
- pa_memblockq_prebuf_disable(q);
-
+ pa_memblockq_sink_input_set_queue(u->sink_input, q);
+
/* The reference to u is dangling here, because we want
* to keep this stream around until it is fully played. */
/* This sink input is not "put" yet, i.e. pa_sink_input_put() has
* not been called! */
-
+
return pa_sink_input_ref(u->sink_input);
fail:
@@ -202,11 +234,12 @@ fail:
int pa_play_memblockq(
pa_sink *sink,
- const char *name,
const pa_sample_spec *ss,
const pa_channel_map *map,
pa_memblockq *q,
- pa_cvolume *volume) {
+ pa_cvolume *volume,
+ pa_proplist *p,
+ uint32_t *sink_input_index) {
pa_sink_input *i;
@@ -214,10 +247,14 @@ int pa_play_memblockq(
pa_assert(ss);
pa_assert(q);
- if (!(i = pa_memblockq_sink_input_new(sink, name, ss, map, q, volume)))
+ if (!(i = pa_memblockq_sink_input_new(sink, ss, map, q, volume, p)))
return -1;
pa_sink_input_put(i);
+
+ if (sink_input_index)
+ *sink_input_index = i->index;
+
pa_sink_input_unref(i);
return 0;
@@ -225,12 +262,17 @@ int pa_play_memblockq(
void pa_memblockq_sink_input_set_queue(pa_sink_input *i, pa_memblockq *q) {
memblockq_stream *u;
-
+
pa_sink_input_assert_ref(i);
u = MEMBLOCKQ_STREAM(i->userdata);
memblockq_stream_assert_ref(u);
if (u->memblockq)
pa_memblockq_free(u->memblockq);
- u->memblockq = q;
+
+ if ((u->memblockq = q)) {
+ pa_memblockq_set_prebuf(q, 0);
+ pa_memblockq_set_silence(q, NULL);
+ pa_memblockq_willneed(q);
+ }
}
diff --git a/src/pulsecore/play-memblockq.h b/src/pulsecore/play-memblockq.h
index d8790316..1a42867b 100644
--- a/src/pulsecore/play-memblockq.h
+++ b/src/pulsecore/play-memblockq.h
@@ -1,8 +1,6 @@
#ifndef fooplaymemblockqhfoo
#define fooplaymemblockqhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -29,20 +27,21 @@
pa_sink_input* pa_memblockq_sink_input_new(
pa_sink *sink,
- const char *name,
const pa_sample_spec *ss,
const pa_channel_map *map,
pa_memblockq *q,
- pa_cvolume *volume);
+ pa_cvolume *volume,
+ pa_proplist *p);
void pa_memblockq_sink_input_set_queue(pa_sink_input *i, pa_memblockq *q);
int pa_play_memblockq(
pa_sink *sink,
- const char *name,
const pa_sample_spec *ss,
const pa_channel_map *map,
pa_memblockq *q,
- pa_cvolume *cvolume);
+ pa_cvolume *cvolume,
+ pa_proplist *p,
+ uint32_t *sink_input_index);
#endif
diff --git a/src/pulsecore/play-memchunk.c b/src/pulsecore/play-memchunk.c
index fd931a24..0dd48251 100644
--- a/src/pulsecore/play-memchunk.c
+++ b/src/pulsecore/play-memchunk.c
@@ -1,9 +1,7 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
- Copyright 2004-2006 Lennart Poettering
+ Copyright 2004-2008 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
@@ -30,167 +28,37 @@
#include <string.h>
#include <pulse/xmalloc.h>
+#include <pulse/gccmacro.h>
#include <pulsecore/sink-input.h>
-#include <pulsecore/gccmacro.h>
#include <pulsecore/thread-mq.h>
+#include <pulsecore/play-memblockq.h>
#include "play-memchunk.h"
-typedef struct memchunk_stream {
- pa_msgobject parent;
- pa_core *core;
- pa_sink_input *sink_input;
- pa_memchunk memchunk;
-} memchunk_stream;
-
-enum {
- MEMCHUNK_STREAM_MESSAGE_UNLINK,
-};
-
-PA_DECLARE_CLASS(memchunk_stream);
-#define MEMCHUNK_STREAM(o) (memchunk_stream_cast(o))
-static PA_DEFINE_CHECK_TYPE(memchunk_stream, pa_msgobject);
-
-static void memchunk_stream_unlink(memchunk_stream *u) {
- pa_assert(u);
-
- if (!u->sink_input)
- return;
-
- pa_sink_input_unlink(u->sink_input);
-
- pa_sink_input_unref(u->sink_input);
- u->sink_input = NULL;
-
- memchunk_stream_unref(u);
-}
-
-static void memchunk_stream_free(pa_object *o) {
- memchunk_stream *u = MEMCHUNK_STREAM(o);
- pa_assert(u);
-
- memchunk_stream_unlink(u);
-
- if (u->memchunk.memblock)
- pa_memblock_unref(u->memchunk.memblock);
-
- pa_xfree(u);
-}
-
-static int memchunk_stream_process_msg(pa_msgobject *o, int code, void*userdata, int64_t offset, pa_memchunk *chunk) {
- memchunk_stream *u = MEMCHUNK_STREAM(o);
- memchunk_stream_assert_ref(u);
-
- switch (code) {
- case MEMCHUNK_STREAM_MESSAGE_UNLINK:
- memchunk_stream_unlink(u);
- break;
- }
-
- return 0;
-}
-
-static void sink_input_kill_cb(pa_sink_input *i) {
- pa_sink_input_assert_ref(i);
-
- memchunk_stream_unlink(MEMCHUNK_STREAM(i->userdata));
-}
-
-static int sink_input_peek_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) {
- memchunk_stream *u;
-
- pa_assert(i);
- pa_assert(chunk);
- u = MEMCHUNK_STREAM(i->userdata);
- memchunk_stream_assert_ref(u);
-
- if (!u->memchunk.memblock)
- return -1;
-
- if (u->memchunk.length <= 0) {
- pa_memblock_unref(u->memchunk.memblock);
- u->memchunk.memblock = NULL;
- pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(u), MEMCHUNK_STREAM_MESSAGE_UNLINK, NULL, 0, NULL, NULL);
- return -1;
- }
-
- pa_assert(u->memchunk.memblock);
- *chunk = u->memchunk;
- pa_memblock_ref(chunk->memblock);
-
- return 0;
-}
-
-static void sink_input_drop_cb(pa_sink_input *i, size_t length) {
- memchunk_stream *u;
-
- pa_assert(i);
- pa_assert(length > 0);
- u = MEMCHUNK_STREAM(i->userdata);
- memchunk_stream_assert_ref(u);
-
- if (length < u->memchunk.length) {
- u->memchunk.length -= length;
- u->memchunk.index += length;
- } else
- u->memchunk.length = 0;
-}
-
int pa_play_memchunk(
pa_sink *sink,
- const char *name,
const pa_sample_spec *ss,
const pa_channel_map *map,
const pa_memchunk *chunk,
- pa_cvolume *volume) {
+ pa_cvolume *volume,
+ pa_proplist *p,
+ uint32_t *sink_input_index) {
- memchunk_stream *u = NULL;
- pa_sink_input_new_data data;
+ pa_memblockq *q;
+ int r;
pa_assert(sink);
pa_assert(ss);
pa_assert(chunk);
- if (volume && pa_cvolume_is_muted(volume))
- return 0;
-
- pa_memchunk_will_need(chunk);
-
- u = pa_msgobject_new(memchunk_stream);
- u->parent.parent.free = memchunk_stream_free;
- u->parent.process_msg = memchunk_stream_process_msg;
- u->core = sink->core;
- u->memchunk = *chunk;
- pa_memblock_ref(u->memchunk.memblock);
-
- pa_sink_input_new_data_init(&data);
- data.sink = sink;
- data.driver = __FILE__;
- data.name = name;
- pa_sink_input_new_data_set_sample_spec(&data, ss);
- pa_sink_input_new_data_set_channel_map(&data, map);
- pa_sink_input_new_data_set_volume(&data, volume);
+ q = pa_memblockq_new(0, chunk->length, 0, pa_frame_size(ss), 1, 1, 0, NULL);
+ pa_assert_se(pa_memblockq_push(q, chunk) >= 0);
- if (!(u->sink_input = pa_sink_input_new(sink->core, &data, 0)))
- goto fail;
-
- u->sink_input->peek = sink_input_peek_cb;
- u->sink_input->drop = sink_input_drop_cb;
- u->sink_input->kill = sink_input_kill_cb;
- u->sink_input->userdata = u;
-
- pa_sink_input_put(u->sink_input);
+ if ((r = pa_play_memblockq(sink, ss, map, q, volume, p, sink_input_index)) < 0) {
+ pa_memblockq_free(q);
+ return r;
+ }
- /* The reference to u is dangling here, because we want to keep
- * this stream around until it is fully played. */
-
return 0;
-
-fail:
- if (u)
- memchunk_stream_unref(u);
-
- return -1;
}
-
diff --git a/src/pulsecore/play-memchunk.h b/src/pulsecore/play-memchunk.h
index 5afb094c..c312ae82 100644
--- a/src/pulsecore/play-memchunk.h
+++ b/src/pulsecore/play-memchunk.h
@@ -1,8 +1,6 @@
#ifndef fooplaychunkhfoo
#define fooplaychunkhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -29,10 +27,11 @@
int pa_play_memchunk(
pa_sink *sink,
- const char *name,
const pa_sample_spec *ss,
const pa_channel_map *map,
const pa_memchunk *chunk,
- pa_cvolume *cvolume);
+ pa_cvolume *cvolume,
+ pa_proplist *p,
+ uint32_t *sink_input_index);
#endif
diff --git a/src/pulsecore/poll.c b/src/pulsecore/poll.c
index 288f7dfb..88ac21e4 100644
--- a/src/pulsecore/poll.c
+++ b/src/pulsecore/poll.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/poll.h b/src/pulsecore/poll.h
index 6be6069b..86c37a08 100644
--- a/src/pulsecore/poll.h
+++ b/src/pulsecore/poll.h
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/proplist-util.c b/src/pulsecore/proplist-util.c
new file mode 100644
index 00000000..6005775e
--- /dev/null
+++ b/src/pulsecore/proplist-util.c
@@ -0,0 +1,118 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2008 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 <string.h>
+#include <locale.h>
+
+#include <pulse/proplist.h>
+#include <pulse/utf8.h>
+#include <pulse/xmalloc.h>
+#include <pulse/util.h>
+
+#include <pulsecore/core-util.h>
+
+#include "proplist-util.h"
+
+void pa_init_proplist(pa_proplist *p) {
+ int a, b;
+#ifndef HAVE_DECL_ENVIRON
+ extern char **environ;
+#endif
+ char **e;
+
+ pa_assert(p);
+
+ for (e = environ; *e; e++) {
+
+ if (pa_startswith(*e, "PULSE_PROP_")) {
+ size_t kl = strcspn(*e+11, "=");
+ char *k;
+
+ if ((*e)[11+kl] != '=')
+ continue;
+
+ if (!pa_utf8_valid(*e+11+kl+1))
+ continue;
+
+ k = pa_xstrndup(*e+11, kl);
+
+ if (pa_proplist_contains(p, k)) {
+ pa_xfree(k);
+ continue;
+ }
+
+ pa_proplist_sets(p, k, *e+11+kl+1);
+ pa_xfree(k);
+ }
+ }
+
+ if (!pa_proplist_contains(p, PA_PROP_APPLICATION_PROCESS_ID)) {
+ char t[32];
+ pa_snprintf(t, sizeof(t), "%lu", (unsigned long) getpid());
+ pa_proplist_sets(p, PA_PROP_APPLICATION_PROCESS_ID, t);
+ }
+
+ 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);
+ }
+ }
+
+ 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);
+ }
+ }
+
+ a = pa_proplist_contains(p, PA_PROP_APPLICATION_PROCESS_BINARY);
+ b = pa_proplist_contains(p, PA_PROP_APPLICATION_NAME);
+
+ if (!a || !b) {
+ char t[PATH_MAX];
+ if (pa_get_binary_name(t, sizeof(t))) {
+ char *c = pa_utf8_filter(t);
+
+ if (!a)
+ pa_proplist_sets(p, PA_PROP_APPLICATION_PROCESS_BINARY, c);
+ if (!b)
+ pa_proplist_sets(p, PA_PROP_APPLICATION_NAME, c);
+
+ pa_xfree(c);
+ }
+ }
+
+ if (!pa_proplist_contains(p, PA_PROP_APPLICATION_LANGUAGE)) {
+ const char *l;
+
+ if ((l = setlocale(LC_MESSAGES, NULL)))
+ pa_proplist_sets(p, PA_PROP_APPLICATION_LANGUAGE, l);
+ }
+}
diff --git a/src/pulsecore/core-def.h b/src/pulsecore/proplist-util.h
index 4bc05137..c6bdc103 100644
--- a/src/pulsecore/core-def.h
+++ b/src/pulsecore/proplist-util.h
@@ -1,29 +1,29 @@
-#ifndef foocoredefhfoo
-#define foocoredefhfoo
-
-/* $Id$ */
+#ifndef fooproplistutilutilhfoo
+#define fooproplistutilutilhfoo
/***
This file is part of PulseAudio.
- Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+ Copyright 2008 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 of the License,
- or (at your option) any later version.
+ 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.
+ 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
+ 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.
***/
-/* FIXME: Remove this shit */
+#include <pulse/proplist.h>
+
+void pa_init_proplist(pa_proplist *p);
#endif
diff --git a/src/pulsecore/props.c b/src/pulsecore/props.c
index fc3dce9e..23d432ec 100644
--- a/src/pulsecore/props.c
+++ b/src/pulsecore/props.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -39,7 +37,7 @@ typedef struct pa_property {
/* Allocate a new property object */
static pa_property* property_new(const char *name, void *data) {
pa_property* p;
-
+
pa_assert(name);
pa_assert(data);
@@ -60,7 +58,7 @@ static void property_free(pa_property *p) {
void* pa_property_get(pa_core *c, const char *name) {
pa_property *p;
-
+
pa_assert(c);
pa_assert(name);
pa_assert(c->properties);
@@ -73,7 +71,7 @@ void* pa_property_get(pa_core *c, const char *name) {
int pa_property_set(pa_core *c, const char *name, void *data) {
pa_property *p;
-
+
pa_assert(c);
pa_assert(name);
pa_assert(data);
@@ -89,7 +87,7 @@ int pa_property_set(pa_core *c, const char *name, void *data) {
int pa_property_remove(pa_core *c, const char *name) {
pa_property *p;
-
+
pa_assert(c);
pa_assert(name);
pa_assert(c->properties);
@@ -123,7 +121,7 @@ void pa_property_cleanup(pa_core *c) {
void pa_property_dump(pa_core *c, pa_strbuf *s) {
void *state = NULL;
pa_property *p;
-
+
pa_assert(c);
pa_assert(s);
diff --git a/src/pulsecore/props.h b/src/pulsecore/props.h
index 880325f6..95db229b 100644
--- a/src/pulsecore/props.h
+++ b/src/pulsecore/props.h
@@ -1,8 +1,6 @@
#ifndef foopropshfoo
#define foopropshfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/protocol-cli.c b/src/pulsecore/protocol-cli.c
index d9d00734..30cb475d 100644
--- a/src/pulsecore/protocol-cli.c
+++ b/src/pulsecore/protocol-cli.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -48,7 +46,7 @@ struct pa_protocol_cli {
static void cli_eof_cb(pa_cli*c, void*userdata) {
pa_protocol_cli *p = userdata;
pa_assert(p);
-
+
pa_idxset_remove_by_data(p->connections, c, NULL);
pa_cli_free(c);
}
@@ -56,7 +54,7 @@ static void cli_eof_cb(pa_cli*c, void*userdata) {
static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata) {
pa_protocol_cli *p = userdata;
pa_cli *c;
-
+
pa_assert(s);
pa_assert(io);
pa_assert(p);
@@ -82,7 +80,7 @@ pa_protocol_cli* pa_protocol_cli_new(pa_core *core, pa_socket_server *server, pa
p = pa_xnew(pa_protocol_cli, 1);
p->module = m;
p->core = core;
- p->server = server;
+ p->server = pa_socket_server_ref(server);
p->connections = pa_idxset_new(NULL, NULL);
pa_socket_server_set_callback(p->server, on_connection, p);
@@ -92,7 +90,7 @@ pa_protocol_cli* pa_protocol_cli_new(pa_core *core, pa_socket_server *server, pa
static void free_connection(void *p, PA_GCC_UNUSED void *userdata) {
pa_assert(p);
-
+
pa_cli_free(p);
}
diff --git a/src/pulsecore/protocol-cli.h b/src/pulsecore/protocol-cli.h
index 3870def3..8922ac62 100644
--- a/src/pulsecore/protocol-cli.h
+++ b/src/pulsecore/protocol-cli.h
@@ -1,8 +1,6 @@
#ifndef fooprotocolclihfoo
#define fooprotocolclihfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/protocol-esound.c b/src/pulsecore/protocol-esound.c
index 76ba9dd0..db1b4305 100644
--- a/src/pulsecore/protocol-esound.c
+++ b/src/pulsecore/protocol-esound.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -70,9 +68,11 @@
#define PLAYBACK_BUFFER_SECONDS (.25)
#define PLAYBACK_BUFFER_FRAGMENTS (10)
#define RECORD_BUFFER_SECONDS (5)
-#define RECORD_BUFFER_FRAGMENTS (100)
-#define MAX_CACHE_SAMPLE_SIZE (1024000)
+#define MAX_CACHE_SAMPLE_SIZE (2048000)
+
+#define DEFAULT_SINK_LATENCY (150*PA_USEC_PER_MSEC)
+#define DEFAULT_SOURCE_LATENCY (150*PA_USEC_PER_MSEC)
#define SCACHE_PREFIX "esound."
@@ -102,8 +102,9 @@ typedef struct connection {
struct {
pa_memblock *current_memblock;
- size_t memblock_index, fragment_size;
+ size_t memblock_index;
pa_atomic_t missing;
+ pa_bool_t underrun;
} playback;
struct {
@@ -122,7 +123,7 @@ static PA_DEFINE_CHECK_TYPE(connection, pa_msgobject);
struct pa_protocol_esound {
pa_module *module;
pa_core *core;
- int public;
+ pa_bool_t public;
pa_socket_server *server;
pa_idxset *connections;
@@ -149,8 +150,9 @@ typedef struct proto_handler {
const char *description;
} esd_proto_handler_info_t;
-static void sink_input_drop_cb(pa_sink_input *i, size_t length);
-static int sink_input_peek_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk);
+static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk);
+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_kill_cb(pa_sink_input *i);
static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk);
static pa_usec_t source_output_get_latency_cb(pa_source_output *o);
@@ -398,8 +400,7 @@ static int esd_proto_stream_play(connection *c, PA_GCC_UNUSED esd_proto_t reques
CHECK_VALIDITY(sink, "No such sink: %s", c->protocol->sink_name);
}
- strncpy(name, data, sizeof(name));
- name[sizeof(name)-1] = 0;
+ pa_strlcpy(name, data, sizeof(name));
utf8_name = pa_utf8_filter(name);
pa_client_set_name(c->client, utf8_name);
@@ -410,34 +411,39 @@ static int esd_proto_stream_play(connection *c, PA_GCC_UNUSED esd_proto_t reques
pa_assert(!c->sink_input && !c->input_memblockq);
pa_sink_input_new_data_init(&sdata);
- sdata.sink = sink;
sdata.driver = __FILE__;
- sdata.name = c->client->name;
- pa_sink_input_new_data_set_sample_spec(&sdata, &ss);
sdata.module = c->protocol->module;
sdata.client = c->client;
+ sdata.sink = sink;
+ pa_proplist_update(sdata.proplist, PA_UPDATE_MERGE, c->client->proplist);
+ pa_sink_input_new_data_set_sample_spec(&sdata, &ss);
c->sink_input = pa_sink_input_new(c->protocol->core, &sdata, 0);
+ pa_sink_input_new_data_done(&sdata);
+
CHECK_VALIDITY(c->sink_input, "Failed to create sink input.");
l = (size_t) (pa_bytes_per_second(&ss)*PLAYBACK_BUFFER_SECONDS);
c->input_memblockq = pa_memblockq_new(
0,
l,
- 0,
+ l,
pa_frame_size(&ss),
(size_t) -1,
l/PLAYBACK_BUFFER_FRAGMENTS,
+ 0,
NULL);
- pa_iochannel_socket_set_rcvbuf(c->io, l/PLAYBACK_BUFFER_FRAGMENTS*2);
- c->playback.fragment_size = l/PLAYBACK_BUFFER_FRAGMENTS;
+ pa_iochannel_socket_set_rcvbuf(c->io, l);
c->sink_input->parent.process_msg = sink_input_process_msg;
- c->sink_input->peek = sink_input_peek_cb;
- c->sink_input->drop = sink_input_drop_cb;
+ c->sink_input->pop = sink_input_pop_cb;
+ c->sink_input->process_rewind = sink_input_process_rewind_cb;
+ c->sink_input->update_max_rewind = sink_input_update_max_rewind_cb;
c->sink_input->kill = sink_input_kill_cb;
c->sink_input->userdata = c;
+ pa_sink_input_set_requested_latency(c->sink_input, DEFAULT_SINK_LATENCY);
+
c->state = ESD_STREAMING_DATA;
c->protocol->n_player++;
@@ -497,8 +503,7 @@ static int esd_proto_stream_record(connection *c, esd_proto_t request, const voi
}
}
- strncpy(name, data, sizeof(name));
- name[sizeof(name)-1] = 0;
+ pa_strlcpy(name, data, sizeof(name));
utf8_name = pa_utf8_filter(name);
pa_client_set_name(c->client, utf8_name);
@@ -509,32 +514,37 @@ static int esd_proto_stream_record(connection *c, esd_proto_t request, const voi
pa_assert(!c->output_memblockq && !c->source_output);
pa_source_output_new_data_init(&sdata);
- sdata.source = source;
sdata.driver = __FILE__;
- sdata.name = c->client->name;
- pa_source_output_new_data_set_sample_spec(&sdata, &ss);
sdata.module = c->protocol->module;
sdata.client = c->client;
+ sdata.source = source;
+ pa_proplist_update(sdata.proplist, PA_UPDATE_MERGE, c->client->proplist);
+ pa_source_output_new_data_set_sample_spec(&sdata, &ss);
+
+ c->source_output = pa_source_output_new(c->protocol->core, &sdata, 0);
+ pa_source_output_new_data_done(&sdata);
- c->source_output = pa_source_output_new(c->protocol->core, &sdata, 9);
- CHECK_VALIDITY(c->source_output, "Failed to create source_output.");
+ CHECK_VALIDITY(c->source_output, "Failed to create source output.");
l = (size_t) (pa_bytes_per_second(&ss)*RECORD_BUFFER_SECONDS);
c->output_memblockq = pa_memblockq_new(
0,
l,
- 0,
+ l,
pa_frame_size(&ss),
1,
0,
+ 0,
NULL);
- pa_iochannel_socket_set_sndbuf(c->io, l/RECORD_BUFFER_FRAGMENTS*2);
+ pa_iochannel_socket_set_sndbuf(c->io, l);
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);
+
c->state = ESD_STREAMING_DATA;
c->protocol->n_player++;
@@ -638,8 +648,8 @@ static int esd_proto_all_info(connection *c, esd_proto_t request, const void *da
memset(name, 0, ESD_NAME_MAX); /* don't leak old data */
if (conn->original_name)
strncpy(name, conn->original_name, ESD_NAME_MAX);
- else if (conn->client && conn->client->name)
- strncpy(name, conn->client->name, ESD_NAME_MAX);
+ else if (conn->client && pa_proplist_gets(conn->client->proplist, PA_PROP_APPLICATION_NAME))
+ strncpy(name, pa_proplist_gets(conn->client->proplist, PA_PROP_APPLICATION_NAME), ESD_NAME_MAX);
connection_write(c, name, ESD_NAME_MAX);
/* rate */
@@ -785,8 +795,7 @@ static int esd_proto_sample_cache(connection *c, PA_GCC_UNUSED esd_proto_t reque
CHECK_VALIDITY(sc_length <= MAX_CACHE_SAMPLE_SIZE, "Sample too large (%d bytes).", (int)sc_length);
strcpy(name, SCACHE_PREFIX);
- strncpy(name+sizeof(SCACHE_PREFIX)-1, data, ESD_NAME_MAX);
- name[sizeof(name)-1] = 0;
+ pa_strlcpy(name+sizeof(SCACHE_PREFIX)-1, data, ESD_NAME_MAX);
CHECK_VALIDITY(pa_utf8_valid(name), "Invalid UTF8 in sample name.");
@@ -800,7 +809,7 @@ static int esd_proto_sample_cache(connection *c, PA_GCC_UNUSED esd_proto_t reque
c->state = ESD_CACHING_SAMPLE;
- pa_scache_add_item(c->protocol->core, c->scache.name, NULL, NULL, NULL, &idx);
+ pa_scache_add_item(c->protocol->core, c->scache.name, NULL, NULL, NULL, c->client->proplist, &idx);
idx += 1;
connection_write(c, &idx, sizeof(uint32_t));
@@ -818,8 +827,7 @@ static int esd_proto_sample_get_id(connection *c, PA_GCC_UNUSED esd_proto_t requ
pa_assert(length == ESD_NAME_MAX);
strcpy(name, SCACHE_PREFIX);
- strncpy(name+sizeof(SCACHE_PREFIX)-1, data, ESD_NAME_MAX);
- name[sizeof(name)-1] = 0;
+ pa_strlcpy(name+sizeof(SCACHE_PREFIX)-1, data, ESD_NAME_MAX);
CHECK_VALIDITY(pa_utf8_valid(name), "Invalid UTF8 in sample name.");
@@ -851,7 +859,7 @@ static int esd_proto_sample_free_or_play(connection *c, esd_proto_t request, con
pa_sink *sink;
if ((sink = pa_namereg_get(c->protocol->core, c->protocol->sink_name, PA_NAMEREG_SINK, 1)))
- if (pa_scache_play_item(c->protocol->core, name, sink, PA_VOLUME_NORM) >= 0)
+ if (pa_scache_play_item(c->protocol->core, name, sink, PA_VOLUME_NORM, c->client->proplist, NULL) >= 0)
ok = idx + 1;
} else {
pa_assert(request == ESD_PROTO_SAMPLE_FREE);
@@ -992,7 +1000,7 @@ static int do_read(connection *c) {
uint32_t idx;
c->scache.memchunk.index = 0;
- pa_scache_add_item(c->protocol->core, c->scache.name, &c->scache.sample_spec, NULL, &c->scache.memchunk, &idx);
+ 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;
@@ -1012,6 +1020,7 @@ static int do_read(connection *c) {
ssize_t r;
size_t l;
void *p;
+ size_t space;
pa_assert(c->input_memblockq);
@@ -1020,21 +1029,26 @@ static int do_read(connection *c) {
if (!(l = pa_atomic_load(&c->playback.missing)))
return 0;
- if (l > c->playback.fragment_size)
- l = c->playback.fragment_size;
+ if (c->playback.current_memblock) {
+
+ space = pa_memblock_get_length(c->playback.current_memblock) - c->playback.memblock_index;
- if (c->playback.current_memblock)
- if (pa_memblock_get_length(c->playback.current_memblock) - c->playback.memblock_index < l) {
+ if (space <= 0) {
pa_memblock_unref(c->playback.current_memblock);
c->playback.current_memblock = NULL;
- c->playback.memblock_index = 0;
}
+ }
if (!c->playback.current_memblock) {
- pa_assert_se(c->playback.current_memblock = pa_memblock_new(c->protocol->core->mempool, c->playback.fragment_size*2));
+ pa_assert_se(c->playback.current_memblock = pa_memblock_new(c->protocol->core->mempool, (size_t) -1));
c->playback.memblock_index = 0;
+
+ space = pa_memblock_get_length(c->playback.current_memblock);
}
+ if (l > space)
+ l = space;
+
p = pa_memblock_acquire(c->playback.current_memblock);
r = pa_iochannel_read(c->io, (uint8_t*) p+c->playback.memblock_index, l);
pa_memblock_release(c->playback.current_memblock);
@@ -1122,12 +1136,11 @@ static void do_work(connection *c) {
if (c->dead)
return;
- if (pa_iochannel_is_readable(c->io)) {
+ if (pa_iochannel_is_readable(c->io))
if (do_read(c) < 0)
goto fail;
- }
- if (c->state == ESD_STREAMING_DATA && c->source_output && pa_iochannel_is_hungup(c->io))
+ if (c->state == ESD_STREAMING_DATA && !c->sink_input && pa_iochannel_is_hungup(c->io))
/* In case we are in capture mode we will never call read()
* on the socket, hence we need to detect the hangup manually
* here, instead of simply waiting for read() to return 0. */
@@ -1212,15 +1225,19 @@ static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int
/* New data from the main loop */
pa_memblockq_push_align(c->input_memblockq, chunk);
+ if (pa_memblockq_is_readable(c->input_memblockq) && c->playback.underrun) {
+ pa_log_debug("Requesting rewind due to end of underrun.");
+ pa_sink_input_request_rewind(c->sink_input, 0, FALSE, TRUE);
+ }
+
/* pa_log("got data, %u", pa_memblockq_get_length(c->input_memblockq)); */
return 0;
}
- case SINK_INPUT_MESSAGE_DISABLE_PREBUF: {
+ case SINK_INPUT_MESSAGE_DISABLE_PREBUF:
pa_memblockq_prebuf_disable(c->input_memblockq);
return 0;
- }
case PA_SINK_INPUT_MESSAGE_GET_LATENCY: {
pa_usec_t *r = userdata;
@@ -1237,41 +1254,64 @@ static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int
}
/* Called from thread context */
-static int sink_input_peek_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) {
+static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) {
connection*c;
- int r;
- pa_assert(i);
+ pa_sink_input_assert_ref(i);
c = CONNECTION(i->userdata);
connection_assert_ref(c);
pa_assert(chunk);
- if ((r = pa_memblockq_peek(c->input_memblockq, chunk)) < 0 && c->dead)
- pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(c), CONNECTION_MESSAGE_UNLINK_CONNECTION, NULL, 0, NULL, NULL);
+ if (pa_memblockq_peek(c->input_memblockq, chunk) < 0) {
+
+ c->playback.underrun = TRUE;
+
+ if (c->dead && pa_sink_input_safe_to_remove(i))
+ pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(c), CONNECTION_MESSAGE_UNLINK_CONNECTION, NULL, 0, NULL, NULL);
+
+ return -1;
+ } else {
+ size_t m;
+
+ chunk->length = PA_MIN(length, chunk->length);
+
+ c->playback.underrun = FALSE;
+
+ pa_memblockq_drop(c->input_memblockq, chunk->length);
+ m = pa_memblockq_pop_missing(c->input_memblockq);
+
+ if (m > 0)
+ if (pa_atomic_add(&c->playback.missing, m) <= 0)
+ pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(c), CONNECTION_MESSAGE_REQUEST_DATA, NULL, 0, NULL, NULL);
- return r;
+ return 0;
+ }
}
/* Called from thread context */
-static void sink_input_drop_cb(pa_sink_input *i, size_t length) {
- connection*c;
- size_t old, new;
+static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
+ connection *c;
- pa_assert(i);
+ pa_sink_input_assert_ref(i);
c = CONNECTION(i->userdata);
connection_assert_ref(c);
- pa_assert(length);
- /* pa_log("DROP"); */
+ /* If we are in an underrun, then we don't rewind */
+ if (i->thread_info.underrun_for > 0)
+ return;
- old = pa_memblockq_missing(c->input_memblockq);
- pa_memblockq_drop(c->input_memblockq, length);
- new = pa_memblockq_missing(c->input_memblockq);
+ pa_memblockq_rewind(c->input_memblockq, nbytes);
+}
- if (new > old) {
- if (pa_atomic_add(&c->playback.missing, new - old) <= 0)
- pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(c), CONNECTION_MESSAGE_REQUEST_DATA, NULL, 0, NULL, NULL);
- }
+/* Called from thread context */
+static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) {
+ connection *c;
+
+ pa_sink_input_assert_ref(i);
+ c = CONNECTION(i->userdata);
+ connection_assert_ref(c);
+
+ pa_memblockq_set_maxrewind(c->input_memblockq, nbytes);
}
static void sink_input_kill_cb(pa_sink_input *i) {
@@ -1286,7 +1326,7 @@ static void sink_input_kill_cb(pa_sink_input *i) {
static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk) {
connection *c;
- pa_assert(o);
+ pa_source_output_assert_ref(o);
c = CONNECTION(o->userdata);
pa_assert(c);
pa_assert(chunk);
@@ -1303,7 +1343,7 @@ static void source_output_kill_cb(pa_source_output *o) {
static pa_usec_t source_output_get_latency_cb(pa_source_output *o) {
connection*c;
- pa_assert(o);
+ pa_source_output_assert_ref(o);
c = CONNECTION(o->userdata);
pa_assert(c);
@@ -1349,7 +1389,8 @@ static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata)
pa_iochannel_socket_peer_to_string(io, pname, sizeof(pname));
pa_snprintf(cname, sizeof(cname), "EsounD client (%s)", pname);
c->client = pa_client_new(p->core, __FILE__, cname);
- c->client->owner = p->module;
+ pa_proplist_sets(c->client->proplist, "esound-protocol.peer", pname);
+ c->client->module = p->module;
c->client->kill = client_kill_cb;
c->client->userdata = c;
@@ -1374,11 +1415,10 @@ static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata)
c->playback.current_memblock = NULL;
c->playback.memblock_index = 0;
- c->playback.fragment_size = 0;
+ c->playback.underrun = TRUE;
pa_atomic_store(&c->playback.missing, 0);
- c->scache.memchunk.length = c->scache.memchunk.index = 0;
- c->scache.memchunk.memblock = NULL;
+ pa_memchunk_reset(&c->scache.memchunk);
c->scache.name = NULL;
c->original_name = NULL;
@@ -1406,7 +1446,7 @@ static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata)
pa_protocol_esound* pa_protocol_esound_new(pa_core*core, pa_socket_server *server, pa_module *m, pa_modargs *ma) {
pa_protocol_esound *p = NULL;
- int public = 0;
+ pa_bool_t public = FALSE;
const char *acl;
pa_assert(core);
@@ -1436,7 +1476,7 @@ pa_protocol_esound* pa_protocol_esound_new(pa_core*core, pa_socket_server *serve
p->core = core;
p->module = m;
p->public = public;
- p->server = server;
+ p->server = pa_socket_server_ref(server);
pa_socket_server_set_callback(p->server, on_connection, p);
p->connections = pa_idxset_new(NULL, NULL);
@@ -1459,7 +1499,8 @@ void pa_protocol_esound_free(pa_protocol_esound *p) {
connection_unlink(c);
pa_idxset_free(p->connections, NULL, NULL);
- pa_socket_server_unref(p->server);
+ if (p->server)
+ pa_socket_server_unref(p->server);
if (p->auth_ip_acl)
pa_ip_acl_free(p->auth_ip_acl);
diff --git a/src/pulsecore/protocol-esound.h b/src/pulsecore/protocol-esound.h
index 868ef5d2..0c9447d3 100644
--- a/src/pulsecore/protocol-esound.h
+++ b/src/pulsecore/protocol-esound.h
@@ -1,8 +1,6 @@
#ifndef fooprotocolesoundhfoo
#define fooprotocolesoundhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/protocol-http.c b/src/pulsecore/protocol-http.c
index 4a2836e7..03990435 100644
--- a/src/pulsecore/protocol-http.c
+++ b/src/pulsecore/protocol-http.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -65,7 +63,7 @@ struct pa_protocol_http {
static void http_response(struct connection *c, int code, const char *msg, const char *mime) {
char s[256];
-
+
pa_assert(c);
pa_assert(msg);
pa_assert(mime);
@@ -111,7 +109,7 @@ static void connection_free(struct connection *c, int del) {
if (del)
pa_idxset_remove_by_data(c->protocol->connections, c, NULL);
-
+
pa_ioline_unref(c->line);
pa_xfree(c);
}
@@ -168,7 +166,7 @@ static void line_callback(pa_ioline *line, const char *s, void *userdata) {
#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("Fully Qualified Domain Name:", pa_get_fqdn(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));
PRINTF_FIELD("Default Sink:", pa_namereg_get_default_sink_name(c->protocol->core));
PRINTF_FIELD("Default Source:", pa_namereg_get_default_source_name(c->protocol->core));
@@ -225,7 +223,7 @@ fail:
static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata) {
pa_protocol_http *p = userdata;
struct connection *c;
-
+
pa_assert(s);
pa_assert(io);
pa_assert(p);
@@ -248,14 +246,14 @@ static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata)
pa_protocol_http* pa_protocol_http_new(pa_core *core, pa_socket_server *server, pa_module *m, PA_GCC_UNUSED pa_modargs *ma) {
pa_protocol_http* p;
-
+
pa_core_assert_ref(core);
pa_assert(server);
p = pa_xnew(pa_protocol_http, 1);
p->module = m;
p->core = core;
- p->server = server;
+ p->server = pa_socket_server_ref(server);
p->connections = pa_idxset_new(NULL, NULL);
pa_socket_server_set_callback(p->server, on_connection, p);
diff --git a/src/pulsecore/protocol-http.h b/src/pulsecore/protocol-http.h
index cf952476..e3372335 100644
--- a/src/pulsecore/protocol-http.h
+++ b/src/pulsecore/protocol-http.h
@@ -1,8 +1,6 @@
#ifndef fooprotocolhttphfoo
#define fooprotocolhttphfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c
index 9ae0f083..923a5665 100644
--- a/src/pulsecore/protocol-native.c
+++ b/src/pulsecore/protocol-native.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -71,6 +69,9 @@
#define MAX_CONNECTIONS 64
#define MAX_MEMBLOCKQ_LENGTH (4*1024*1024) /* 4MB */
+#define DEFAULT_TLENGTH_MSEC 2000 /* 2s */
+#define DEFAULT_PROCESS_MSEC 20 /* 20ms */
+#define DEFAULT_FRAGSIZE_MSEC DEFAULT_TLENGTH_MSEC
typedef struct connection connection;
struct pa_protocol_native;
@@ -84,6 +85,7 @@ typedef struct record_stream {
pa_source_output *source_output;
pa_memblockq *memblockq;
size_t fragment_size;
+ pa_usec_t source_latency;
} record_stream;
typedef struct output_stream {
@@ -98,17 +100,17 @@ typedef struct playback_stream {
pa_sink_input *sink_input;
pa_memblockq *memblockq;
- int drain_request;
+ pa_bool_t drain_request;
uint32_t drain_tag;
uint32_t syncid;
- int underrun;
pa_atomic_t missing;
size_t minreq;
+ pa_usec_t sink_latency;
/* Only updated after SINK_INPUT_MESSAGE_UPDATE_LATENCY */
int64_t read_index, write_index;
- size_t resampled_chunk_length;
+ size_t render_memblockq_length;
} playback_stream;
typedef struct upload_stream {
@@ -122,12 +124,14 @@ typedef struct upload_stream {
char *name;
pa_sample_spec sample_spec;
pa_channel_map channel_map;
+ pa_proplist *proplist;
} upload_stream;
struct connection {
pa_msgobject parent;
- int authorized;
+ pa_bool_t authorized:1;
+ pa_bool_t is_local:1;
uint32_t version;
pa_protocol_native *protocol;
pa_client *client;
@@ -162,11 +166,11 @@ static PA_DEFINE_CHECK_TYPE(connection, pa_msgobject);
struct pa_protocol_native {
pa_module *module;
pa_core *core;
- int public;
+ pa_bool_t public;
pa_socket_server *server;
pa_idxset *connections;
uint8_t auth_cookie[PA_NATIVE_COOKIE_LENGTH];
- int auth_cookie_in_property;
+ pa_bool_t auth_cookie_in_property;
#ifdef HAVE_CREDS
char *auth_group;
#endif
@@ -187,7 +191,8 @@ enum {
PLAYBACK_STREAM_MESSAGE_REQUEST_DATA, /* data requested from sink input from the main loop */
PLAYBACK_STREAM_MESSAGE_UNDERFLOW,
PLAYBACK_STREAM_MESSAGE_OVERFLOW,
- PLAYBACK_STREAM_MESSAGE_DRAIN_ACK
+ PLAYBACK_STREAM_MESSAGE_DRAIN_ACK,
+ PLAYBACK_STREAM_MESSAGE_STARTED
};
enum {
@@ -199,15 +204,21 @@ enum {
CONNECTION_MESSAGE_REVOKE
};
-static int sink_input_peek_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk);
-static void sink_input_drop_cb(pa_sink_input *i, size_t length);
+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_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 send_memblock(connection *c);
static void 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 pa_usec_t source_output_get_latency_cb(pa_source_output *o);
static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk);
@@ -248,6 +259,10 @@ static void command_cork_record_stream(pa_pdispatch *pd, uint32_t command, uint3
static void command_flush_record_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
static void command_move_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
static void command_suspend(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_set_stream_buffer_attr(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_update_stream_sample_rate(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_update_proplist(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_remove_proplist(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,
@@ -323,7 +338,21 @@ static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {
[PA_COMMAND_REMOVE_AUTOLOAD] = command_remove_autoload,
[PA_COMMAND_MOVE_SINK_INPUT] = command_move_stream,
- [PA_COMMAND_MOVE_SOURCE_OUTPUT] = command_move_stream
+ [PA_COMMAND_MOVE_SOURCE_OUTPUT] = command_move_stream,
+
+ [PA_COMMAND_SET_PLAYBACK_STREAM_BUFFER_ATTR] = command_set_stream_buffer_attr,
+ [PA_COMMAND_SET_RECORD_STREAM_BUFFER_ATTR] = command_set_stream_buffer_attr,
+
+ [PA_COMMAND_UPDATE_PLAYBACK_STREAM_SAMPLE_RATE] = command_update_stream_sample_rate,
+ [PA_COMMAND_UPDATE_RECORD_STREAM_SAMPLE_RATE] = command_update_stream_sample_rate,
+
+ [PA_COMMAND_UPDATE_RECORD_STREAM_PROPLIST] = command_update_proplist,
+ [PA_COMMAND_UPDATE_PLAYBACK_STREAM_PROPLIST] = command_update_proplist,
+ [PA_COMMAND_UPDATE_CLIENT_PROPLIST] = command_update_proplist,
+
+ [PA_COMMAND_REMOVE_RECORD_STREAM_PROPLIST] = command_remove_proplist,
+ [PA_COMMAND_REMOVE_PLAYBACK_STREAM_PROPLIST] = command_remove_proplist,
+ [PA_COMMAND_REMOVE_CLIENT_PROPLIST] = command_remove_proplist,
};
/* structure management */
@@ -347,6 +376,9 @@ static void upload_stream_free(pa_object *o) {
pa_xfree(s->name);
+ if (s->proplist)
+ pa_proplist_free(s->proplist);
+
if (s->memchunk.memblock)
pa_memblock_unref(s->memchunk.memblock);
@@ -357,7 +389,9 @@ static upload_stream* upload_stream_new(
connection *c,
const pa_sample_spec *ss,
const pa_channel_map *map,
- const char *name, size_t length) {
+ const char *name,
+ size_t length,
+ pa_proplist *p) {
upload_stream *s;
@@ -365,6 +399,7 @@ static upload_stream* upload_stream_new(
pa_assert(ss);
pa_assert(name);
pa_assert(length > 0);
+ pa_assert(p);
s = pa_msgobject_new(upload_stream);
s->parent.parent.parent.free = upload_stream_free;
@@ -374,6 +409,8 @@ static upload_stream* upload_stream_new(
s->name = pa_xstrdup(name);
pa_memchunk_reset(&s->memchunk);
s->length = length;
+ s->proplist = pa_proplist_copy(p);
+ pa_proplist_update(s->proplist, PA_UPDATE_MERGE, c->client->proplist);
pa_idxset_put(c->output_streams, s, &s->index);
@@ -432,15 +469,72 @@ 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, uint32_t *maxlength, uint32_t *fragsize) {
+ pa_assert(s);
+ pa_assert(maxlength);
+ pa_assert(fragsize);
+
+ if (*maxlength <= 0 || *maxlength > MAX_MEMBLOCKQ_LENGTH)
+ *maxlength = MAX_MEMBLOCKQ_LENGTH;
+
+ if (*fragsize <= 0)
+ *fragsize = pa_usec_to_bytes(DEFAULT_FRAGSIZE_MSEC*PA_USEC_PER_MSEC, &s->source_output->sample_spec);
+
+ if (adjust_latency) {
+ pa_usec_t fragsize_usec;
+
+ /* So, the user asked us to adjust the latency according to
+ * what the source can provide. Half the latency will be
+ * spent on the hw buffer, half of it in the async buffer
+ * queue we maintain for each client. */
+
+ fragsize_usec = pa_bytes_to_usec(*fragsize, &s->source_output->sample_spec);
+
+ s->source_latency = pa_source_output_set_requested_latency(s->source_output, fragsize_usec/2);
+
+ if (fragsize_usec >= s->source_latency*2)
+ fragsize_usec -= s->source_latency;
+ else
+ fragsize_usec = s->source_latency;
+
+ *fragsize = pa_usec_to_bytes(fragsize_usec, &s->source_output->sample_spec);
+ } else
+ s->source_latency = 0;
+}
+
+static void fix_record_buffer_attr_post(record_stream *s, uint32_t *maxlength, uint32_t *fragsize) {
+ size_t base;
+
+ pa_assert(s);
+ pa_assert(maxlength);
+ pa_assert(fragsize);
+
+ *maxlength = pa_memblockq_get_maxlength(s->memblockq);
+
+ 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;
+
+ *fragsize = s->fragment_size;
+}
+
static record_stream* record_stream_new(
connection *c,
pa_source *source,
- const pa_sample_spec *ss,
- const pa_channel_map *map,
- const char *name,
+ pa_sample_spec *ss,
+ pa_channel_map *map,
+ pa_bool_t peak_detect,
uint32_t *maxlength,
- uint32_t fragment_size,
- int corked) {
+ uint32_t *fragsize,
+ pa_source_output_flags_t flags,
+ pa_proplist *p,
+ pa_bool_t adjust_latency,
+ pa_sink_input *direct_on_input) {
record_stream *s;
pa_source_output *source_output;
@@ -449,20 +543,28 @@ static record_stream* record_stream_new(
pa_assert(c);
pa_assert(ss);
- pa_assert(name);
pa_assert(maxlength);
- pa_assert(*maxlength > 0);
+ pa_assert(p);
pa_source_output_new_data_init(&data);
+
+ pa_proplist_update(data.proplist, PA_UPDATE_REPLACE, p);
+ pa_proplist_update(data.proplist, PA_UPDATE_MERGE, c->client->proplist);
+ data.driver = __FILE__;
data.module = c->protocol->module;
data.client = c->client;
data.source = source;
- data.driver = __FILE__;
- data.name = name;
+ data.direct_on_input = direct_on_input;
pa_source_output_new_data_set_sample_spec(&data, ss);
pa_source_output_new_data_set_channel_map(&data, map);
+ if (peak_detect)
+ data.resample_method = PA_RESAMPLER_PEAKS;
+
+ source_output = pa_source_output_new(c->protocol->core, &data, flags);
- if (!(source_output = pa_source_output_new(c->protocol->core, &data, corked ? PA_SOURCE_OUTPUT_START_CORKED : 0)))
+ pa_source_output_new_data_done(&data);
+
+ if (!source_output)
return NULL;
s = pa_msgobject_new(record_stream);
@@ -470,27 +572,38 @@ static record_stream* record_stream_new(
s->parent.process_msg = record_stream_process_msg;
s->connection = c;
s->source_output = source_output;
+
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->suspend = source_output_suspend_cb;
s->source_output->userdata = s;
+ fix_record_buffer_attr_pre(s, adjust_latency, maxlength, fragsize);
+
s->memblockq = pa_memblockq_new(
0,
*maxlength,
0,
- base = pa_frame_size(ss),
+ base = pa_frame_size(&source_output->sample_spec),
1,
0,
+ 0,
NULL);
- s->fragment_size = (fragment_size/base)*base;
- if (s->fragment_size <= 0)
- s->fragment_size = base;
- *maxlength = pa_memblockq_get_maxlength(s->memblockq);
+ fix_record_buffer_attr_post(s, maxlength, fragsize);
+
+ *ss = s->source_output->sample_spec;
+ *map = s->source_output->channel_map;
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);
+
pa_source_output_put(s->source_output);
return s;
}
@@ -538,21 +651,14 @@ static int playback_stream_process_msg(pa_msgobject *o, int code, void*userdata,
uint32_t l = 0;
for (;;) {
- int32_t k;
-
- if ((k = pa_atomic_load(&s->missing)) <= 0)
+ if ((l = pa_atomic_load(&s->missing)) <= 0)
break;
- l += k;
-
- if (l < s->minreq)
- break;
-
- if (pa_atomic_sub(&s->missing, k) <= k)
+ if (pa_atomic_cmpxchg(&s->missing, l, 0))
break;
}
- if (l < s->minreq)
+ if (l <= 0)
break;
t = pa_tagstruct_new(NULL, 0);
@@ -562,7 +668,7 @@ static int playback_stream_process_msg(pa_msgobject *o, int code, void*userdata,
pa_tagstruct_putu32(t, l);
pa_pstream_send_tagstruct(s->connection->pstream, t);
-/* pa_log("Requesting %u bytes", l); */
+/* pa_log("Requesting %lu bytes", (unsigned long) l); */
break;
}
@@ -590,41 +696,172 @@ static int playback_stream_process_msg(pa_msgobject *o, int code, void*userdata,
break;
}
+ case PLAYBACK_STREAM_MESSAGE_STARTED:
+
+ if (s->connection->version >= 13) {
+ pa_tagstruct *t;
+
+ /* Notify the user we're overflowed*/
+ t = pa_tagstruct_new(NULL, 0);
+ pa_tagstruct_putu32(t, PA_COMMAND_STARTED);
+ pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
+ pa_tagstruct_putu32(t, s->index);
+ pa_pstream_send_tagstruct(s->connection->pstream, t);
+ }
+
+ break;
+
case PLAYBACK_STREAM_MESSAGE_DRAIN_ACK:
pa_pstream_send_simple_ack(s->connection->pstream, PA_PTR_TO_UINT(userdata));
break;
-
}
return 0;
}
+static void fix_playback_buffer_attr_pre(playback_stream *s, pa_bool_t adjust_latency, uint32_t *maxlength, uint32_t *tlength, uint32_t* prebuf, uint32_t* minreq) {
+ size_t frame_size;
+ pa_usec_t tlength_usec, minreq_usec, sink_usec;
+
+ pa_assert(s);
+ pa_assert(maxlength);
+ pa_assert(tlength);
+ pa_assert(prebuf);
+ pa_assert(minreq);
+
+ if (*maxlength <= 0 || *maxlength > MAX_MEMBLOCKQ_LENGTH)
+ *maxlength = MAX_MEMBLOCKQ_LENGTH;
+ if (*tlength <= 0)
+ *tlength = pa_usec_to_bytes(DEFAULT_TLENGTH_MSEC*PA_USEC_PER_MSEC, &s->sink_input->sample_spec);
+ if (*minreq <= 0)
+ *minreq = pa_usec_to_bytes(DEFAULT_PROCESS_MSEC*PA_USEC_PER_MSEC, &s->sink_input->sample_spec);
+
+ frame_size = pa_frame_size(&s->sink_input->sample_spec);
+ if (*minreq <= 0)
+ *minreq = frame_size;
+ if (*tlength < *minreq+frame_size)
+ *tlength = *minreq+frame_size;
+
+ tlength_usec = pa_bytes_to_usec(*tlength, &s->sink_input->sample_spec);
+ minreq_usec = pa_bytes_to_usec(*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 (adjust_latency) {
+
+ /* So, the user asked us to adjust the latency of the stream
+ * buffer according to the what the sink can provide. The
+ * tlength passed in shall be the overall latency. Roughly
+ * half the latency will be spent on the hw buffer, the other
+ * half of it in the async buffer queue we maintain for each
+ * client. In between we'll have a safety space of size
+ * 2*minreq. Why the 2*minreq? When the hw buffer is completey
+ * empty and needs to be filled, then our buffer must have
+ * enough data to fulfill this request immediatly and thus
+ * have at least the same tlength as the size of the hw
+ * buffer. It additionally needs space for 2 times minreq
+ * because if the buffer ran empty and a partial fillup
+ * happens immediately on the next iteration we need to be
+ * able to fulfill it and give the application also minreq
+ * time to fill it up again for the next request Makes 2 times
+ * minreq in plus.. */
+
+ if (tlength_usec > minreq_usec*2)
+ sink_usec = (tlength_usec - minreq_usec*2)/2;
+ else
+ sink_usec = 0;
+
+ } else {
+
+ /* Ok, the user didn't ask us to adjust the latency, but we
+ * still need to make sure that the parameters from the user
+ * do make sense. */
+
+ if (tlength_usec > minreq_usec*2)
+ sink_usec = (tlength_usec - minreq_usec*2);
+ else
+ sink_usec = 0;
+ }
+
+ s->sink_latency = pa_sink_input_set_requested_latency(s->sink_input, sink_usec);
+
+ if (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->sink_latency + 2*minreq_usec)
+ tlength_usec = s->sink_latency + 2*minreq_usec;
+
+ *tlength = pa_usec_to_bytes(tlength_usec, &s->sink_input->sample_spec);
+ *minreq = pa_usec_to_bytes(minreq_usec, &s->sink_input->sample_spec);
+
+ if (*minreq <= 0) {
+ *minreq += frame_size;
+ *tlength += frame_size*2;
+ }
+
+ if (*tlength <= *minreq)
+ *tlength = *minreq*2 + frame_size;
+
+ if (*prebuf <= 0 || *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);
+
+ *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);
+
+ s->minreq = *minreq;
+}
+
static playback_stream* playback_stream_new(
connection *c,
pa_sink *sink,
- const pa_sample_spec *ss,
- const pa_channel_map *map,
- const char *name,
+ pa_sample_spec *ss,
+ pa_channel_map *map,
uint32_t *maxlength,
uint32_t *tlength,
uint32_t *prebuf,
uint32_t *minreq,
pa_cvolume *volume,
+ pa_bool_t muted,
uint32_t syncid,
- int corked,
- uint32_t *missing) {
+ uint32_t *missing,
+ pa_sink_input_flags_t flags,
+ pa_proplist *p,
+ pa_bool_t adjust_latency) {
playback_stream *s, *ssync;
pa_sink_input *sink_input;
- pa_memblock *silence;
+ pa_memchunk silence;
uint32_t idx;
int64_t start_index;
pa_sink_input_new_data data;
pa_assert(c);
pa_assert(ss);
- pa_assert(name);
pa_assert(maxlength);
+ pa_assert(tlength);
+ pa_assert(prebuf);
+ pa_assert(minreq);
+ pa_assert(volume);
+ pa_assert(missing);
+ pa_assert(p);
/* Find syncid group */
for (ssync = pa_idxset_first(c->output_streams, &idx); ssync; ssync = pa_idxset_next(c->output_streams, &idx)) {
@@ -646,17 +883,24 @@ static playback_stream* playback_stream_new(
}
pa_sink_input_new_data_init(&data);
- data.sink = sink;
+
+ pa_proplist_update(data.proplist, PA_UPDATE_REPLACE, p);
+ pa_proplist_update(data.proplist, PA_UPDATE_MERGE, c->client->proplist);
data.driver = __FILE__;
- data.name = name;
+ data.module = c->protocol->module;
+ data.client = c->client;
+ data.sink = sink;
pa_sink_input_new_data_set_sample_spec(&data, ss);
pa_sink_input_new_data_set_channel_map(&data, map);
pa_sink_input_new_data_set_volume(&data, volume);
- data.module = c->protocol->module;
- data.client = c->client;
+ pa_sink_input_new_data_set_muted(&data, muted);
data.sync_base = ssync ? ssync->sink_input : NULL;
- if (!(sink_input = pa_sink_input_new(c->protocol->core, &data, corked ? PA_SINK_INPUT_START_CORKED : 0)))
+ sink_input = pa_sink_input_new(c->protocol->core, &data, flags);
+
+ pa_sink_input_new_data_done(&data);
+
+ if (!sink_input)
return NULL;
s = pa_msgobject_new(playback_stream);
@@ -665,43 +909,51 @@ static playback_stream* playback_stream_new(
s->connection = c;
s->syncid = syncid;
s->sink_input = sink_input;
- s->underrun = 1;
s->sink_input->parent.process_msg = sink_input_process_msg;
- s->sink_input->peek = sink_input_peek_cb;
- s->sink_input->drop = sink_input_drop_cb;
+ s->sink_input->pop = sink_input_pop_cb;
+ s->sink_input->process_rewind = sink_input_process_rewind_cb;
+ s->sink_input->update_max_rewind = sink_input_update_max_rewind_cb;
s->sink_input->kill = sink_input_kill_cb;
+ s->sink_input->moved = sink_input_moved_cb;
+ s->sink_input->suspend = sink_input_suspend_cb;
s->sink_input->userdata = s;
start_index = ssync ? pa_memblockq_get_read_index(ssync->memblockq) : 0;
- silence = pa_silence_memblock_new(c->protocol->core->mempool, ss, 0);
+ fix_playback_buffer_attr_pre(s, adjust_latency, maxlength, tlength, prebuf, minreq);
+ pa_sink_input_get_silence(sink_input, &silence);
s->memblockq = pa_memblockq_new(
start_index,
*maxlength,
*tlength,
- pa_frame_size(ss),
+ pa_frame_size(&sink_input->sample_spec),
*prebuf,
*minreq,
- silence);
+ 0,
+ &silence);
- pa_memblock_unref(silence);
+ pa_memblock_unref(silence.memblock);
+ fix_playback_buffer_attr_post(s, maxlength, tlength, prebuf, minreq);
- *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);
*missing = (uint32_t) pa_memblockq_pop_missing(s->memblockq);
- s->minreq = pa_memblockq_get_minreq(s->memblockq);
+ *ss = s->sink_input->sample_spec;
+ *map = s->sink_input->channel_map;
+
pa_atomic_store(&s->missing, 0);
- s->drain_request = 0;
+ s->drain_request = FALSE;
pa_idxset_put(c->output_streams, s, &s->index);
- pa_sink_input_put(s->sink_input);
+ 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);
+ pa_sink_input_put(s->sink_input);
return s;
}
@@ -788,13 +1040,13 @@ static void request_bytes(playback_stream *s) {
if (m <= 0)
return;
-/* pa_log("request_bytes(%u)", m); */
+/* pa_log("request_bytes(%lu)", (unsigned long) m); */
previous_missing = pa_atomic_add(&s->missing, m);
- if (previous_missing < s->minreq && previous_missing+m >= s->minreq) {
- pa_assert(pa_thread_mq_get());
+
+ if (pa_memblockq_prebuf_active(s->memblockq) ||
+ (previous_missing < s->minreq && previous_missing+m >= s->minreq))
pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_REQUEST_DATA, NULL, 0, NULL, NULL);
- }
}
static void send_memblock(connection *c) {
@@ -853,6 +1105,43 @@ static void send_record_stream_killed(record_stream *r) {
/*** sink input callbacks ***/
+static void handle_seek(playback_stream *s, int64_t indexw) {
+ playback_stream_assert_ref(s);
+
+/* pa_log("handle_seek: %llu -- %i", (unsigned long long) s->sink_input->thread_info.underrun_for, pa_memblockq_is_readable(s->memblockq)); */
+
+ if (s->sink_input->thread_info.underrun_for > 0) {
+
+/* pa_log("%lu vs. %lu", (unsigned long) pa_memblockq_get_length(s->memblockq), (unsigned long) pa_memblockq_get_prebuf(s->memblockq)); */
+
+ if (pa_memblockq_is_readable(s->memblockq)) {
+
+ /* We just ended an underrun, let's ask the sink
+ * for a complete rewind rewrite */
+
+ pa_log_debug("Requesting rewind due to end of underrun.");
+ pa_sink_input_request_rewind(s->sink_input,
+ s->sink_input->thread_info.underrun_for == (size_t) -1 ? 0 : s->sink_input->thread_info.underrun_for,
+ FALSE, TRUE);
+ }
+
+ } else {
+ int64_t indexr;
+
+ indexr = pa_memblockq_get_read_index(s->memblockq);
+
+ if (indexw < indexr) {
+ /* OK, the sink already asked for this data, so
+ * let's have it usk us again */
+
+ pa_log_debug("Requesting rewind due to rewrite.");
+ pa_sink_input_request_rewind(s->sink_input, indexr - indexw, TRUE, FALSE);
+ }
+ }
+
+ request_bytes(s);
+}
+
/* Called from thread context */
static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk) {
pa_sink_input *i = PA_SINK_INPUT(o);
@@ -864,48 +1153,44 @@ static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int
switch (code) {
- case SINK_INPUT_MESSAGE_SEEK:
+ case SINK_INPUT_MESSAGE_SEEK: {
+ int64_t windex;
+
+ windex = pa_memblockq_get_write_index(s->memblockq);
pa_memblockq_seek(s->memblockq, offset, PA_PTR_TO_UINT(userdata));
- request_bytes(s);
+
+ handle_seek(s, windex);
return 0;
+ }
case SINK_INPUT_MESSAGE_POST_DATA: {
+ int64_t windex;
+
pa_assert(chunk);
-/* pa_log("sink input post: %u", chunk->length); */
+ windex = pa_memblockq_get_write_index(s->memblockq);
- if (pa_memblockq_push_align(s->memblockq, chunk) < 0) {
+/* pa_log("sink input post: %lu %lli", (unsigned long) chunk->length, (long long) windex); */
+ if (pa_memblockq_push_align(s->memblockq, chunk) < 0) {
pa_log_warn("Failed to push data into queue");
pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_OVERFLOW, NULL, 0, NULL, NULL);
pa_memblockq_seek(s->memblockq, chunk->length, PA_SEEK_RELATIVE);
}
- request_bytes(s);
-
- s->underrun = 0;
- return 0;
- }
-
- case SINK_INPUT_MESSAGE_DRAIN: {
+ handle_seek(s, windex);
- pa_memblockq_prebuf_disable(s->memblockq);
-
- if (!pa_memblockq_is_readable(s->memblockq))
- pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_DRAIN_ACK, userdata, 0, NULL, NULL);
- else {
- s->drain_tag = PA_PTR_TO_UINT(userdata);
- s->drain_request = 1;
- }
- request_bytes(s);
+/* pa_log("sink input post2: %lu", (unsigned long) pa_memblockq_get_length(s->memblockq)); */
return 0;
}
+ case SINK_INPUT_MESSAGE_DRAIN:
case SINK_INPUT_MESSAGE_FLUSH:
case SINK_INPUT_MESSAGE_PREBUF_FORCE:
case SINK_INPUT_MESSAGE_TRIGGER: {
+ int64_t windex;
pa_sink_input *isync;
void (*func)(pa_memblockq *bq);
@@ -918,6 +1203,7 @@ static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int
func = pa_memblockq_prebuf_force;
break;
+ case SINK_INPUT_MESSAGE_DRAIN:
case SINK_INPUT_MESSAGE_TRIGGER:
func = pa_memblockq_prebuf_disable;
break;
@@ -926,23 +1212,32 @@ static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int
pa_assert_not_reached();
}
+ windex = pa_memblockq_get_write_index(s->memblockq);
func(s->memblockq);
- s->underrun = 0;
- request_bytes(s);
+ handle_seek(s, windex);
/* Do the same for all other members in the sync group */
for (isync = i->sync_prev; isync; isync = isync->sync_prev) {
playback_stream *ssync = PLAYBACK_STREAM(isync->userdata);
+ windex = pa_memblockq_get_write_index(ssync->memblockq);
func(ssync->memblockq);
- ssync->underrun = 0;
- request_bytes(ssync);
+ handle_seek(ssync, windex);
}
for (isync = i->sync_next; isync; isync = isync->sync_next) {
playback_stream *ssync = PLAYBACK_STREAM(isync->userdata);
+ windex = pa_memblockq_get_write_index(ssync->memblockq);
func(ssync->memblockq);
- ssync->underrun = 0;
- request_bytes(ssync);
+ handle_seek(ssync, windex);
+ }
+
+ if (code == SINK_INPUT_MESSAGE_DRAIN) {
+ if (!pa_memblockq_is_readable(s->memblockq))
+ pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_DRAIN_ACK, userdata, 0, NULL, NULL);
+ else {
+ s->drain_tag = PA_PTR_TO_UINT(userdata);
+ s->drain_request = TRUE;
+ }
}
return 0;
@@ -952,14 +1247,21 @@ static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int
s->read_index = pa_memblockq_get_read_index(s->memblockq);
s->write_index = pa_memblockq_get_write_index(s->memblockq);
- s->resampled_chunk_length = s->sink_input->thread_info.resampled_chunk.memblock ? s->sink_input->thread_info.resampled_chunk.length : 0;
+ s->render_memblockq_length = pa_memblockq_get_length(s->sink_input->thread_info.render_memblockq);
return 0;
- case PA_SINK_INPUT_MESSAGE_SET_STATE:
+ case PA_SINK_INPUT_MESSAGE_SET_STATE: {
+ int64_t windex;
+
+ windex = pa_memblockq_get_write_index(s->memblockq);
pa_memblockq_prebuf_force(s->memblockq);
- request_bytes(s);
+
+ handle_seek(s, windex);
+
+ /* Fall through to the default handler */
break;
+ }
case PA_SINK_INPUT_MESSAGE_GET_LATENCY: {
pa_usec_t *r = userdata;
@@ -976,7 +1278,7 @@ static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int
}
/* Called from thread context */
-static int sink_input_peek_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) {
+static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) {
playback_stream *s;
pa_sink_input_assert_ref(i);
@@ -984,44 +1286,61 @@ static int sink_input_peek_cb(pa_sink_input *i, size_t length, pa_memchunk *chun
playback_stream_assert_ref(s);
pa_assert(chunk);
- if (pa_memblockq_get_length(s->memblockq) <= 0 && !s->underrun) {
- pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_UNDERFLOW, NULL, 0, NULL, NULL);
- s->underrun = 1;
- }
-
if (pa_memblockq_peek(s->memblockq, chunk) < 0) {
-/* pa_log("peek: failure"); */
+
+/* pa_log("UNDERRUN: %lu", (unsigned long) pa_memblockq_get_length(s->memblockq)); */
+
+ if (s->drain_request && pa_sink_input_safe_to_remove(i)) {
+ s->drain_request = FALSE;
+ pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_DRAIN_ACK, PA_UINT_TO_PTR(s->drain_tag), 0, NULL, NULL);
+ } else if (i->thread_info.playing_for > 0)
+ pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_UNDERFLOW, NULL, 0, NULL, NULL);
+
+/* pa_log("adding %llu bytes", (unsigned long long) nbytes); */
+
+ request_bytes(s);
+
return -1;
}
-/* pa_log("peek: %u", chunk->length); */
+/* pa_log("NOTUNDERRUN %lu", (unsigned long) chunk->length); */
+
+ chunk->length = PA_MIN(nbytes, chunk->length);
+ if (i->thread_info.underrun_for > 0)
+ pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_STARTED, NULL, 0, NULL, NULL);
+
+ pa_memblockq_drop(s->memblockq, chunk->length);
request_bytes(s);
return 0;
}
-/* Called from thread context */
-static void sink_input_drop_cb(pa_sink_input *i, size_t length) {
+static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
playback_stream *s;
pa_sink_input_assert_ref(i);
s = PLAYBACK_STREAM(i->userdata);
playback_stream_assert_ref(s);
- pa_assert(length > 0);
- pa_memblockq_drop(s->memblockq, length);
+ /* If we are in an underrun, then we don't rewind */
+ if (i->thread_info.underrun_for > 0)
+ return;
- if (s->drain_request && !pa_memblockq_is_readable(s->memblockq)) {
- s->drain_request = 0;
- pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_DRAIN_ACK, PA_UINT_TO_PTR(s->drain_tag), 0, NULL, NULL);
- }
+ pa_memblockq_rewind(s->memblockq, nbytes);
+}
- request_bytes(s);
+static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) {
+ playback_stream *s;
-/* pa_log("after_drop: %u %u", pa_memblockq_get_length(s->memblockq), pa_memblockq_is_readable(s->memblockq)); */
+ pa_sink_input_assert_ref(i);
+ s = PLAYBACK_STREAM(i->userdata);
+ playback_stream_assert_ref(s);
+
+ pa_memblockq_set_maxrewind(s->memblockq, nbytes);
}
+/* Called from main context */
static void sink_input_kill_cb(pa_sink_input *i) {
playback_stream *s;
@@ -1033,6 +1352,70 @@ static void sink_input_kill_cb(pa_sink_input *i) {
playback_stream_unlink(s);
}
+/* Called from main context */
+static void sink_input_suspend_cb(pa_sink_input *i, pa_bool_t suspend) {
+ playback_stream *s;
+ pa_tagstruct *t;
+
+ pa_sink_input_assert_ref(i);
+ s = PLAYBACK_STREAM(i->userdata);
+ playback_stream_assert_ref(s);
+
+ if (s->connection->version < 12)
+ return;
+
+ t = pa_tagstruct_new(NULL, 0);
+ pa_tagstruct_putu32(t, PA_COMMAND_PLAYBACK_STREAM_SUSPENDED);
+ pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
+ pa_tagstruct_putu32(t, s->index);
+ pa_tagstruct_put_boolean(t, suspend);
+ pa_pstream_send_tagstruct(s->connection->pstream, t);
+}
+
+/* Called from main context */
+static void sink_input_moved_cb(pa_sink_input *i) {
+ 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, &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);
+
+ if (s->connection->version < 12)
+ return;
+
+ t = pa_tagstruct_new(NULL, 0);
+ 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);
+
+ 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_pstream_send_tagstruct(s->connection->pstream, t);
+}
+
/*** source_output callbacks ***/
/* Called from thread context */
@@ -1070,6 +1453,63 @@ static pa_usec_t source_output_get_latency_cb(pa_source_output *o) {
return pa_bytes_to_usec(pa_memblockq_get_length(s->memblockq), &o->sample_spec);
}
+/* Called from main context */
+static void source_output_suspend_cb(pa_source_output *o, pa_bool_t suspend) {
+ record_stream *s;
+ pa_tagstruct *t;
+
+ pa_source_output_assert_ref(o);
+ s = RECORD_STREAM(o->userdata);
+ record_stream_assert_ref(s);
+
+ if (s->connection->version < 12)
+ return;
+
+ t = pa_tagstruct_new(NULL, 0);
+ pa_tagstruct_putu32(t, PA_COMMAND_RECORD_STREAM_SUSPENDED);
+ pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
+ pa_tagstruct_putu32(t, s->index);
+ pa_tagstruct_put_boolean(t, suspend);
+ pa_pstream_send_tagstruct(s->connection->pstream, t);
+}
+
+/* Called from main context */
+static void source_output_moved_cb(pa_source_output *o) {
+ 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, &maxlength, &fragsize);
+ pa_memblockq_set_maxlength(s->memblockq, maxlength);
+ fix_record_buffer_attr_post(s, &maxlength, &fragsize);
+
+ if (s->connection->version < 12)
+ return;
+
+ t = pa_tagstruct_new(NULL, 0);
+ 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);
+
+ if (s->connection->version >= 13) {
+ pa_tagstruct_putu32(t, maxlength);
+ pa_tagstruct_putu32(t, fragsize);
+ pa_tagstruct_put_usec(t, s->source_latency);
+ }
+
+ pa_pstream_send_tagstruct(s->connection->pstream, t);
+}
+
/*** pdispatch callbacks ***/
static void protocol_error(connection *c) {
@@ -1097,57 +1537,126 @@ static void command_create_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GC
connection *c = CONNECTION(userdata);
playback_stream *s;
uint32_t maxlength, tlength, prebuf, minreq, sink_index, syncid, missing;
- const char *name, *sink_name;
+ const char *name = NULL, *sink_name;
pa_sample_spec ss;
pa_channel_map map;
pa_tagstruct *reply;
pa_sink *sink = NULL;
pa_cvolume volume;
- int corked;
+ pa_bool_t
+ corked = FALSE,
+ no_remap = FALSE,
+ no_remix = FALSE,
+ fix_format = FALSE,
+ fix_rate = FALSE,
+ fix_channels = FALSE,
+ no_move = FALSE,
+ variable_rate = FALSE,
+ muted = FALSE,
+ adjust_latency = FALSE;
+
+ pa_sink_input_flags_t flags = 0;
+ pa_proplist *p;
connection_assert_ref(c);
pa_assert(t);
- if (pa_tagstruct_get(
- t,
- PA_TAG_STRING, &name,
- PA_TAG_SAMPLE_SPEC, &ss,
- PA_TAG_CHANNEL_MAP, &map,
- PA_TAG_U32, &sink_index,
- PA_TAG_STRING, &sink_name,
- PA_TAG_U32, &maxlength,
- PA_TAG_BOOLEAN, &corked,
- PA_TAG_U32, &tlength,
- PA_TAG_U32, &prebuf,
- PA_TAG_U32, &minreq,
- PA_TAG_U32, &syncid,
- PA_TAG_CVOLUME, &volume,
- PA_TAG_INVALID) < 0 ||
- !pa_tagstruct_eof(t) ||
- !name) {
+ if ((c->version < 13 && (pa_tagstruct_gets(t, &name) < 0 || !name)) ||
+ pa_tagstruct_get(
+ t,
+ PA_TAG_SAMPLE_SPEC, &ss,
+ PA_TAG_CHANNEL_MAP, &map,
+ PA_TAG_U32, &sink_index,
+ PA_TAG_STRING, &sink_name,
+ PA_TAG_U32, &maxlength,
+ PA_TAG_BOOLEAN, &corked,
+ PA_TAG_U32, &tlength,
+ PA_TAG_U32, &prebuf,
+ PA_TAG_U32, &minreq,
+ PA_TAG_U32, &syncid,
+ PA_TAG_CVOLUME, &volume,
+ PA_TAG_INVALID) < 0) {
+
protocol_error(c);
return;
}
CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
- CHECK_VALIDITY(c->pstream, name && pa_utf8_valid(name), tag, PA_ERR_INVALID);
- CHECK_VALIDITY(c->pstream, sink_index != PA_INVALID_INDEX || !sink_name || (*sink_name && pa_utf8_valid(name)), tag, PA_ERR_INVALID);
+ CHECK_VALIDITY(c->pstream, sink_index != PA_INVALID_INDEX || !sink_name || (*sink_name && pa_utf8_valid(sink_name)), tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, pa_channel_map_valid(&map), tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, pa_sample_spec_valid(&ss), tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, pa_cvolume_valid(&volume), tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, map.channels == ss.channels && volume.channels == ss.channels, tag, PA_ERR_INVALID);
- CHECK_VALIDITY(c->pstream, maxlength > 0 && maxlength <= MAX_MEMBLOCKQ_LENGTH, tag, PA_ERR_INVALID);
- CHECK_VALIDITY(c->pstream, maxlength >= pa_frame_size(&ss), tag, PA_ERR_INVALID);
+
+ p = pa_proplist_new();
+
+ if (name)
+ pa_proplist_sets(p, PA_PROP_MEDIA_NAME, name);
+
+ if (c->version >= 12) {
+ /* Since 0.9.8 the user can ask for a couple of additional flags */
+
+ if (pa_tagstruct_get_boolean(t, &no_remap) < 0 ||
+ pa_tagstruct_get_boolean(t, &no_remix) < 0 ||
+ pa_tagstruct_get_boolean(t, &fix_format) < 0 ||
+ pa_tagstruct_get_boolean(t, &fix_rate) < 0 ||
+ pa_tagstruct_get_boolean(t, &fix_channels) < 0 ||
+ pa_tagstruct_get_boolean(t, &no_move) < 0 ||
+ pa_tagstruct_get_boolean(t, &variable_rate) < 0) {
+
+ protocol_error(c);
+ pa_proplist_free(p);
+ return;
+ }
+ }
+
+ if (c->version >= 13) {
+
+ if (pa_tagstruct_get_boolean(t, &muted) < 0 ||
+ pa_tagstruct_get_boolean(t, &adjust_latency) < 0 ||
+ pa_tagstruct_get_proplist(t, p) < 0) {
+ protocol_error(c);
+ pa_proplist_free(p);
+ return;
+ }
+ }
+
+ if (!pa_tagstruct_eof(t)) {
+ protocol_error(c);
+ pa_proplist_free(p);
+ return;
+ }
if (sink_index != PA_INVALID_INDEX) {
- sink = pa_idxset_get_by_index(c->protocol->core->sinks, sink_index);
- CHECK_VALIDITY(c->pstream, sink, tag, PA_ERR_NOENTITY);
+
+ if (!(sink = pa_idxset_get_by_index(c->protocol->core->sinks, sink_index))) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY);
+ pa_proplist_free(p);
+ return;
+ }
+
} else if (sink_name) {
- sink = pa_namereg_get(c->protocol->core, sink_name, PA_NAMEREG_SINK, 1);
- CHECK_VALIDITY(c->pstream, sink, tag, PA_ERR_NOENTITY);
+
+ if (!(sink = pa_namereg_get(c->protocol->core, sink_name, PA_NAMEREG_SINK, 1))) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY);
+ pa_proplist_free(p);
+ return;
+ }
}
- s = playback_stream_new(c, sink, &ss, &map, name, &maxlength, &tlength, &prebuf, &minreq, &volume, syncid, corked, &missing);
+ flags =
+ (corked ? PA_SINK_INPUT_START_CORKED : 0) |
+ (no_remap ? PA_SINK_INPUT_NO_REMAP : 0) |
+ (no_remix ? PA_SINK_INPUT_NO_REMIX : 0) |
+ (fix_format ? PA_SINK_INPUT_FIX_FORMAT : 0) |
+ (fix_rate ? PA_SINK_INPUT_FIX_RATE : 0) |
+ (fix_channels ? PA_SINK_INPUT_FIX_CHANNELS : 0) |
+ (no_move ? PA_SINK_INPUT_DONT_MOVE : 0) |
+ (variable_rate ? PA_SINK_INPUT_VARIABLE_RATE : 0);
+
+ s = playback_stream_new(c, sink, &ss, &map, &maxlength, &tlength, &prebuf, &minreq, &volume, muted, syncid, &missing, flags, p, adjust_latency);
+ pa_proplist_free(p);
+
CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_INVALID);
reply = reply_new(tag);
@@ -1159,7 +1668,7 @@ static void command_create_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GC
/* pa_log("initial request is %u", missing); */
if (c->version >= 9) {
- /* Since 0.9 we support sending the buffer metrics back to the client */
+ /* 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);
@@ -1167,6 +1676,23 @@ static void command_create_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GC
pa_tagstruct_putu32(reply, (uint32_t) minreq);
}
+ if (c->version >= 12) {
+ /* Since 0.9.8 we support sending the chosen sample
+ * spec/channel map/device/suspend status back to the
+ * client */
+
+ pa_tagstruct_put_sample_spec(reply, &ss);
+ pa_tagstruct_put_channel_map(reply, &map);
+
+ pa_tagstruct_putu32(reply, s->sink_input->sink->index);
+ pa_tagstruct_puts(reply, s->sink_input->sink->name);
+
+ pa_tagstruct_put_boolean(reply, pa_sink_get_state(s->sink_input->sink) == PA_SINK_SUSPENDED);
+ }
+
+ if (c->version >= 13)
+ pa_tagstruct_put_usec(reply, s->sink_latency);
+
pa_pstream_send_tagstruct(c->pstream, reply);
}
@@ -1233,46 +1759,127 @@ static void command_create_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_
record_stream *s;
uint32_t maxlength, fragment_size;
uint32_t source_index;
- const char *name, *source_name;
+ const char *name = NULL, *source_name;
pa_sample_spec ss;
pa_channel_map map;
pa_tagstruct *reply;
pa_source *source = NULL;
- int corked;
+ pa_bool_t
+ corked = FALSE,
+ no_remap = FALSE,
+ no_remix = FALSE,
+ fix_format = FALSE,
+ fix_rate = FALSE,
+ fix_channels = FALSE,
+ no_move = FALSE,
+ variable_rate = FALSE,
+ adjust_latency = FALSE,
+ peak_detect = FALSE;
+ pa_source_output_flags_t flags = 0;
+ pa_proplist *p;
+ uint32_t direct_on_input_idx = PA_INVALID_INDEX;
+ pa_sink_input *direct_on_input = NULL;
connection_assert_ref(c);
pa_assert(t);
- if (pa_tagstruct_gets(t, &name) < 0 ||
+ 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_get_boolean(t, &corked) < 0 ||
- pa_tagstruct_getu32(t, &fragment_size) < 0 ||
- !pa_tagstruct_eof(t)) {
+ pa_tagstruct_getu32(t, &fragment_size) < 0) {
protocol_error(c);
return;
}
CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
- CHECK_VALIDITY(c->pstream, name && pa_utf8_valid(name), tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, pa_sample_spec_valid(&ss), tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, pa_channel_map_valid(&map), tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, source_index != PA_INVALID_INDEX || !source_name || (*source_name && pa_utf8_valid(source_name)), tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, map.channels == ss.channels, tag, PA_ERR_INVALID);
- CHECK_VALIDITY(c->pstream, maxlength <= MAX_MEMBLOCKQ_LENGTH, tag, PA_ERR_INVALID);
+
+ p = pa_proplist_new();
+
+ if (name)
+ pa_proplist_sets(p, PA_PROP_MEDIA_NAME, name);
+
+ if (c->version >= 12) {
+ /* Since 0.9.8 the user can ask for a couple of additional flags */
+
+ if (pa_tagstruct_get_boolean(t, &no_remap) < 0 ||
+ pa_tagstruct_get_boolean(t, &no_remix) < 0 ||
+ pa_tagstruct_get_boolean(t, &fix_format) < 0 ||
+ pa_tagstruct_get_boolean(t, &fix_rate) < 0 ||
+ pa_tagstruct_get_boolean(t, &fix_channels) < 0 ||
+ pa_tagstruct_get_boolean(t, &no_move) < 0 ||
+ pa_tagstruct_get_boolean(t, &variable_rate) < 0) {
+
+ protocol_error(c);
+ pa_proplist_free(p);
+ return;
+ }
+ }
+
+ if (c->version >= 13) {
+
+ if (pa_tagstruct_get_boolean(t, &peak_detect) < 0 ||
+ pa_tagstruct_get_boolean(t, &adjust_latency) < 0 ||
+ pa_tagstruct_get_proplist(t, p) < 0 ||
+ pa_tagstruct_getu32(t, &direct_on_input_idx) < 0) {
+ protocol_error(c);
+ pa_proplist_free(p);
+ return;
+ }
+ }
+
+ if (!pa_tagstruct_eof(t)) {
+ protocol_error(c);
+ pa_proplist_free(p);
+ return;
+ }
if (source_index != PA_INVALID_INDEX) {
- source = pa_idxset_get_by_index(c->protocol->core->sources, source_index);
- CHECK_VALIDITY(c->pstream, source, tag, PA_ERR_NOENTITY);
+
+ if (!(source = pa_idxset_get_by_index(c->protocol->core->sources, source_index))) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY);
+ pa_proplist_free(p);
+ return;
+ }
+
} else if (source_name) {
- source = pa_namereg_get(c->protocol->core, source_name, PA_NAMEREG_SOURCE, 1);
- CHECK_VALIDITY(c->pstream, source, tag, PA_ERR_NOENTITY);
+
+ if (!(source = pa_namereg_get(c->protocol->core, source_name, PA_NAMEREG_SOURCE, 1))) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY);
+ pa_proplist_free(p);
+ return;
+ }
}
- s = record_stream_new(c, source, &ss, &map, name, &maxlength, fragment_size, corked);
+ if (direct_on_input_idx != PA_INVALID_INDEX) {
+
+ if (!(direct_on_input = pa_idxset_get_by_index(c->protocol->core->sink_inputs, direct_on_input_idx))) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY);
+ pa_proplist_free(p);
+ return;
+ }
+ }
+
+ flags =
+ (corked ? PA_SOURCE_OUTPUT_START_CORKED : 0) |
+ (no_remap ? PA_SOURCE_OUTPUT_NO_REMAP : 0) |
+ (no_remix ? PA_SOURCE_OUTPUT_NO_REMIX : 0) |
+ (fix_format ? PA_SOURCE_OUTPUT_FIX_FORMAT : 0) |
+ (fix_rate ? PA_SOURCE_OUTPUT_FIX_RATE : 0) |
+ (fix_channels ? PA_SOURCE_OUTPUT_FIX_CHANNELS : 0) |
+ (no_move ? PA_SOURCE_OUTPUT_DONT_MOVE : 0) |
+ (variable_rate ? PA_SOURCE_OUTPUT_VARIABLE_RATE : 0);
+
+ s = record_stream_new(c, source, &ss, &map, peak_detect, &maxlength, &fragment_size, flags, p, adjust_latency, direct_on_input);
+ pa_proplist_free(p);
+
CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_INVALID);
reply = reply_new(tag);
@@ -1284,9 +1891,26 @@ static void command_create_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_
/* 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) s->fragment_size);
+ pa_tagstruct_putu32(reply, (uint32_t) fragment_size);
+ }
+
+ if (c->version >= 12) {
+ /* Since 0.9.8 we support sending the chosen sample
+ * spec/channel map/device/suspend status back to the
+ * client */
+
+ pa_tagstruct_put_sample_spec(reply, &ss);
+ pa_tagstruct_put_channel_map(reply, &map);
+
+ pa_tagstruct_putu32(reply, s->source_output->source->index);
+ pa_tagstruct_puts(reply, s->source_output->source->name);
+
+ pa_tagstruct_put_boolean(reply, pa_source_get_state(s->source_output->source) == PA_SOURCE_SUSPENDED);
}
+ if (c->version >= 13)
+ pa_tagstruct_put_usec(reply, s->source_latency);
+
pa_pstream_send_tagstruct(c->pstream, reply);
}
@@ -1311,6 +1935,7 @@ static void command_auth(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t
connection *c = CONNECTION(userdata);
const void*cookie;
pa_tagstruct *reply;
+ pa_bool_t shm_on_remote, do_shm;
connection_assert_ref(c);
pa_assert(t);
@@ -1328,50 +1953,53 @@ static void command_auth(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t
return;
}
+ /* Starting with protocol version 13 the MSB of the version tag
+ reflects if shm is available for this connection or
+ not. */
+ if (c->version >= 13) {
+ shm_on_remote = !!(c->version & 0x80000000U);
+ c->version &= 0x7FFFFFFFU;
+ }
+
+ pa_log_debug("Protocol version: remote %u, local %u", c->version, PA_PROTOCOL_VERSION);
+
+ pa_proplist_setf(c->client->proplist, "native-protocol.version", "%u", c->version);
+
if (!c->authorized) {
- int success = 0;
+ pa_bool_t success = FALSE;
#ifdef HAVE_CREDS
const pa_creds *creds;
if ((creds = pa_pdispatch_creds(pd))) {
if (creds->uid == getuid())
- success = 1;
+ success = TRUE;
else if (c->protocol->auth_group) {
int r;
gid_t gid;
if ((gid = pa_get_gid_of_group(c->protocol->auth_group)) == (gid_t) -1)
- pa_log_warn("failed to get GID of group '%s'", c->protocol->auth_group);
+ pa_log_warn("Failed to get GID of group '%s'", c->protocol->auth_group);
else if (gid == creds->gid)
- success = 1;
+ success = TRUE;
if (!success) {
if ((r = pa_uid_in_group(creds->uid, c->protocol->auth_group)) < 0)
- pa_log_warn("failed to check group membership.");
+ pa_log_warn("Failed to check group membership.");
else if (r > 0)
- success = 1;
+ success = TRUE;
}
}
pa_log_info("Got credentials: uid=%lu gid=%lu success=%i",
(unsigned long) creds->uid,
(unsigned long) creds->gid,
- success);
-
- if (c->version >= 10 &&
- pa_mempool_is_shared(c->protocol->core->mempool) &&
- creds->uid == getuid()) {
-
- pa_pstream_use_shm(c->pstream, 1);
- pa_log_info("Enabled SHM for new connection");
- }
-
+ (int) success);
}
#endif
if (!success && memcmp(c->protocol->auth_cookie, cookie, PA_NATIVE_COOKIE_LENGTH) == 0)
- success = 1;
+ success = TRUE;
if (!success) {
pa_log_warn("Denied access to client with invalid authorization data.");
@@ -1379,15 +2007,39 @@ static void command_auth(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t
return;
}
- c->authorized = 1;
+ c->authorized = TRUE;
if (c->auth_timeout_event) {
c->protocol->core->mainloop->time_free(c->auth_timeout_event);
c->auth_timeout_event = NULL;
}
}
+ /* Enable shared memory support if possible */
+ do_shm =
+ pa_mempool_is_shared(c->protocol->core->mempool) &&
+ c->is_local;
+
+ pa_log_debug("SHM possible: %s", pa_yes_no(do_shm));
+
+ if (do_shm)
+ if (c->version < 10 || (c->version >= 13 && !shm_on_remote))
+ do_shm = FALSE;
+
+ if (do_shm) {
+ /* Only enable SHM if both sides are owned by the same
+ * user. This is a security measure because otherwise data
+ * private to the user might leak. */
+
+ const pa_creds *creds;
+ if (!(creds = pa_pdispatch_creds(pd)) || getuid() != creds->uid)
+ do_shm = FALSE;
+ }
+
+ pa_log_debug("Negotiated SHM: %s", pa_yes_no(do_shm));
+ pa_pstream_enable_shm(c->pstream, do_shm);
+
reply = reply_new(tag);
- pa_tagstruct_putu32(reply, PA_PROTOCOL_VERSION);
+ pa_tagstruct_putu32(reply, PA_PROTOCOL_VERSION | (do_shm ? 0x80000000 : 0));
#ifdef HAVE_CREDS
{
@@ -1407,21 +2059,42 @@ static void command_auth(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t
static void command_set_client_name(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
connection *c = CONNECTION(userdata);
- const char *name;
+ const char *name = NULL;
+ pa_proplist *p;
+ pa_tagstruct *reply;
connection_assert_ref(c);
pa_assert(t);
- if (pa_tagstruct_gets(t, &name) < 0 ||
+ p = pa_proplist_new();
+
+ if ((c->version < 13 && pa_tagstruct_gets(t, &name) < 0) ||
+ (c->version >= 13 && pa_tagstruct_get_proplist(t, p) < 0) ||
!pa_tagstruct_eof(t)) {
+
protocol_error(c);
+ pa_proplist_free(p);
return;
}
- CHECK_VALIDITY(c->pstream, name && pa_utf8_valid(name), tag, PA_ERR_INVALID);
+ if (name)
+ if (pa_proplist_sets(p, PA_PROP_APPLICATION_NAME, name) < 0) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID);
+ pa_proplist_free(p);
+ return;
+ }
- pa_client_set_name(c->client, name);
- pa_pstream_send_simple_ack(c->pstream, tag);
+ pa_proplist_update(c->client->proplist, PA_UPDATE_REPLACE, p);
+ pa_proplist_free(p);
+
+ pa_subscription_post(c->protocol->core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_CHANGE, c->client->index);
+
+ reply = reply_new(tag);
+
+ if (c->version >= 13)
+ pa_tagstruct_putu32(reply, c->client->index);
+
+ pa_pstream_send_tagstruct(c->pstream, reply);
}
static void command_lookup(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
@@ -1537,16 +2210,22 @@ static void command_get_playback_latency(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_
reply = reply_new(tag);
latency = pa_sink_get_latency(s->sink_input->sink);
- latency += pa_bytes_to_usec(s->resampled_chunk_length, &s->sink_input->sample_spec);
+ latency += pa_bytes_to_usec(s->render_memblockq_length, &s->sink_input->sample_spec);
pa_tagstruct_put_usec(reply, latency);
pa_tagstruct_put_usec(reply, 0);
- pa_tagstruct_put_boolean(reply, pa_sink_input_get_state(s->sink_input) == PA_SINK_INPUT_RUNNING);
+ pa_tagstruct_put_boolean(reply, s->sink_input->thread_info.playing_for > 0);
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_pstream_send_tagstruct(c->pstream, reply);
}
@@ -1574,7 +2253,7 @@ static void command_get_record_latency(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UN
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, 0);
+ pa_tagstruct_put_boolean(reply, pa_source_get_state(s->source_output->source) == PA_SOURCE_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));
@@ -1586,10 +2265,11 @@ static void command_create_upload_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_
connection *c = CONNECTION(userdata);
upload_stream *s;
uint32_t length;
- const char *name;
+ const char *name = NULL;
pa_sample_spec ss;
pa_channel_map map;
pa_tagstruct *reply;
+ pa_proplist *p;
connection_assert_ref(c);
pa_assert(t);
@@ -1597,8 +2277,7 @@ static void command_create_upload_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_
if (pa_tagstruct_gets(t, &name) < 0 ||
pa_tagstruct_get_sample_spec(t, &ss) < 0 ||
pa_tagstruct_get_channel_map(t, &map) < 0 ||
- pa_tagstruct_getu32(t, &length) < 0 ||
- !pa_tagstruct_eof(t)) {
+ pa_tagstruct_getu32(t, &length) < 0) {
protocol_error(c);
return;
}
@@ -1609,9 +2288,26 @@ static void command_create_upload_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_
CHECK_VALIDITY(c->pstream, map.channels == ss.channels, tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, (length % pa_frame_size(&ss)) == 0 && length > 0, tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, length <= PA_SCACHE_ENTRY_SIZE_MAX, tag, PA_ERR_TOOLARGE);
+
+ p = pa_proplist_new();
+
+ if (c->version >= 13 && pa_tagstruct_get_proplist(t, p) < 0) {
+ protocol_error(c);
+ pa_proplist_free(p);
+ return;
+ }
+
+ if (c->version < 13)
+ pa_proplist_sets(p, PA_PROP_MEDIA_NAME, name);
+ else if (!name)
+ if (!(name = pa_proplist_gets(p, PA_PROP_EVENT_ID)))
+ name = pa_proplist_gets(p, PA_PROP_MEDIA_NAME);
+
CHECK_VALIDITY(c->pstream, name && *name && pa_utf8_valid(name), tag, PA_ERR_INVALID);
- s = upload_stream_new(c, &ss, &map, name, length);
+ s = upload_stream_new(c, &ss, &map, name, length, p);
+ pa_proplist_free(p);
+
CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_INVALID);
reply = reply_new(tag);
@@ -1641,7 +2337,7 @@ static void command_finish_upload_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_
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, &idx) < 0)
+ 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);
@@ -1655,20 +2351,23 @@ static void command_play_sample(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED ui
pa_volume_t volume;
pa_sink *sink;
const char *name, *sink_name;
+ uint32_t idx;
+ pa_proplist *p;
+ pa_tagstruct *reply;
connection_assert_ref(c);
pa_assert(t);
+ CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+
if (pa_tagstruct_getu32(t, &sink_index) < 0 ||
pa_tagstruct_gets(t, &sink_name) < 0 ||
pa_tagstruct_getu32(t, &volume) < 0 ||
- pa_tagstruct_gets(t, &name) < 0 ||
- !pa_tagstruct_eof(t)) {
+ pa_tagstruct_gets(t, &name) < 0) {
protocol_error(c);
return;
}
- CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
CHECK_VALIDITY(c->pstream, sink_index != PA_INVALID_INDEX || !sink_name || (*sink_name && pa_utf8_valid(name)), tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, name && *name && pa_utf8_valid(name), tag, PA_ERR_INVALID);
@@ -1679,12 +2378,29 @@ static void command_play_sample(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED ui
CHECK_VALIDITY(c->pstream, sink, tag, PA_ERR_NOENTITY);
- if (pa_scache_play_item(c->protocol->core, name, sink, volume) < 0) {
+ p = pa_proplist_new();
+
+ if ((c->version >= 13 && pa_tagstruct_get_proplist(t, p) < 0) ||
+ !pa_tagstruct_eof(t)) {
+ protocol_error(c);
+ pa_proplist_free(p);
+ return;
+ }
+
+ if (pa_scache_play_item(c->protocol->core, name, sink, volume, p, &idx) < 0) {
pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY);
+ pa_proplist_free(p);
return;
}
- pa_pstream_send_simple_ack(c->pstream, tag);
+ pa_proplist_free(p);
+
+ reply = reply_new(tag);
+
+ if (c->version >= 13)
+ pa_tagstruct_putu32(reply, idx);
+
+ pa_pstream_send_tagstruct(c->pstream, reply);
}
static void command_remove_sample(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
@@ -1711,16 +2427,38 @@ static void command_remove_sample(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED
pa_pstream_send_simple_ack(c->pstream, tag);
}
-static void sink_fill_tagstruct(pa_tagstruct *t, pa_sink *sink) {
+static void fixup_sample_spec(connection *c, pa_sample_spec *fixed, const pa_sample_spec *original) {
+ pa_assert(c);
+ pa_assert(fixed);
+ pa_assert(original);
+
+ *fixed = *original;
+
+ if (c->version < 12) {
+ /* Before protocol version 12 we didn't support S32 samples,
+ * so we need to lie about this to the client */
+
+ if (fixed->format == PA_SAMPLE_S32LE)
+ fixed->format = PA_SAMPLE_FLOAT32LE;
+ if (fixed->format == PA_SAMPLE_S32BE)
+ fixed->format = PA_SAMPLE_FLOAT32BE;
+ }
+}
+
+static void sink_fill_tagstruct(connection *c, pa_tagstruct *t, pa_sink *sink) {
+ pa_sample_spec fixed_ss;
+
pa_assert(t);
pa_sink_assert_ref(sink);
+ fixup_sample_spec(c, &fixed_ss, &sink->sample_spec);
+
pa_tagstruct_put(
t,
PA_TAG_U32, sink->index,
PA_TAG_STRING, sink->name,
- PA_TAG_STRING, sink->description,
- PA_TAG_SAMPLE_SPEC, &sink->sample_spec,
+ PA_TAG_STRING, pa_strnull(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION)),
+ 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),
@@ -1731,18 +2469,27 @@ static void sink_fill_tagstruct(pa_tagstruct *t, pa_sink *sink) {
PA_TAG_STRING, sink->driver,
PA_TAG_U32, sink->flags,
PA_TAG_INVALID);
+
+ if (c->version >= 13) {
+ pa_tagstruct_put_proplist(t, sink->proplist);
+ pa_tagstruct_put_usec(t, pa_sink_get_requested_latency(sink));
+ }
}
-static void source_fill_tagstruct(pa_tagstruct *t, pa_source *source) {
+static void source_fill_tagstruct(connection *c, pa_tagstruct *t, pa_source *source) {
+ pa_sample_spec fixed_ss;
+
pa_assert(t);
pa_source_assert_ref(source);
+ fixup_sample_spec(c, &fixed_ss, &source->sample_spec);
+
pa_tagstruct_put(
t,
PA_TAG_U32, source->index,
PA_TAG_STRING, source->name,
- PA_TAG_STRING, source->description,
- PA_TAG_SAMPLE_SPEC, &source->sample_spec,
+ PA_TAG_STRING, pa_strnull(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION)),
+ PA_TAG_SAMPLE_SPEC, &fixed_ss,
PA_TAG_CHANNEL_MAP, &source->channel_map,
PA_TAG_U32, source->module ? source->module->index : PA_INVALID_INDEX,
PA_TAG_CVOLUME, pa_source_get_volume(source),
@@ -1753,16 +2500,26 @@ static void source_fill_tagstruct(pa_tagstruct *t, pa_source *source) {
PA_TAG_STRING, source->driver,
PA_TAG_U32, source->flags,
PA_TAG_INVALID);
+
+ if (c->version >= 13) {
+ pa_tagstruct_put_proplist(t, source->proplist);
+ pa_tagstruct_put_usec(t, pa_source_get_requested_latency(source));
+ }
}
-static void client_fill_tagstruct(pa_tagstruct *t, pa_client *client) {
+
+static void client_fill_tagstruct(connection *c, pa_tagstruct *t, pa_client *client) {
pa_assert(t);
pa_assert(client);
pa_tagstruct_putu32(t, client->index);
- pa_tagstruct_puts(t, client->name);
- pa_tagstruct_putu32(t, client->owner ? client->owner->index : PA_INVALID_INDEX);
+ pa_tagstruct_puts(t, pa_strnull(pa_proplist_gets(client->proplist, PA_PROP_APPLICATION_NAME)));
+ pa_tagstruct_putu32(t, client->module ? client->module->index : PA_INVALID_INDEX);
pa_tagstruct_puts(t, client->driver);
+
+ if (c->version >= 13)
+ pa_tagstruct_put_proplist(t, client->proplist);
+
}
static void module_fill_tagstruct(pa_tagstruct *t, pa_module *module) {
@@ -1777,55 +2534,80 @@ static void module_fill_tagstruct(pa_tagstruct *t, pa_module *module) {
}
static void sink_input_fill_tagstruct(connection *c, pa_tagstruct *t, pa_sink_input *s) {
+ pa_sample_spec fixed_ss;
+ pa_usec_t sink_latency;
+
pa_assert(t);
pa_sink_input_assert_ref(s);
+ fixup_sample_spec(c, &fixed_ss, &s->sample_spec);
+
pa_tagstruct_putu32(t, s->index);
- pa_tagstruct_puts(t, s->name);
+ pa_tagstruct_puts(t, pa_strnull(pa_proplist_gets(s->proplist, PA_PROP_MEDIA_NAME)));
pa_tagstruct_putu32(t, s->module ? s->module->index : PA_INVALID_INDEX);
pa_tagstruct_putu32(t, s->client ? s->client->index : PA_INVALID_INDEX);
pa_tagstruct_putu32(t, s->sink->index);
- pa_tagstruct_put_sample_spec(t, &s->sample_spec);
+ pa_tagstruct_put_sample_spec(t, &fixed_ss);
pa_tagstruct_put_channel_map(t, &s->channel_map);
pa_tagstruct_put_cvolume(t, &s->volume);
- pa_tagstruct_put_usec(t, pa_sink_input_get_latency(s));
- pa_tagstruct_put_usec(t, pa_sink_get_latency(s->sink));
+ 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)));
pa_tagstruct_puts(t, s->driver);
if (c->version >= 11)
pa_tagstruct_put_boolean(t, pa_sink_input_get_mute(s));
+ if (c->version >= 13)
+ pa_tagstruct_put_proplist(t, s->proplist);
}
-static void source_output_fill_tagstruct(pa_tagstruct *t, pa_source_output *s) {
+static void source_output_fill_tagstruct(connection *c, pa_tagstruct *t, pa_source_output *s) {
+ pa_sample_spec fixed_ss;
+ pa_usec_t source_latency;
+
pa_assert(t);
pa_source_output_assert_ref(s);
+ fixup_sample_spec(c, &fixed_ss, &s->sample_spec);
+
pa_tagstruct_putu32(t, s->index);
- pa_tagstruct_puts(t, s->name);
+ pa_tagstruct_puts(t, pa_strnull(pa_proplist_gets(s->proplist, PA_PROP_MEDIA_NAME)));
pa_tagstruct_putu32(t, s->module ? s->module->index : PA_INVALID_INDEX);
pa_tagstruct_putu32(t, s->client ? s->client->index : PA_INVALID_INDEX);
pa_tagstruct_putu32(t, s->source->index);
- pa_tagstruct_put_sample_spec(t, &s->sample_spec);
+ pa_tagstruct_put_sample_spec(t, &fixed_ss);
pa_tagstruct_put_channel_map(t, &s->channel_map);
- pa_tagstruct_put_usec(t, pa_source_output_get_latency(s));
- pa_tagstruct_put_usec(t, pa_source_get_latency(s->source));
+ pa_tagstruct_put_usec(t, pa_source_output_get_latency(s, &source_latency));
+ pa_tagstruct_put_usec(t, source_latency);
pa_tagstruct_puts(t, pa_resample_method_to_string(pa_source_output_get_resample_method(s)));
pa_tagstruct_puts(t, s->driver);
+
+ if (c->version >= 13)
+ pa_tagstruct_put_proplist(t, s->proplist);
}
-static void scache_fill_tagstruct(pa_tagstruct *t, pa_scache_entry *e) {
+static void scache_fill_tagstruct(connection *c, pa_tagstruct *t, pa_scache_entry *e) {
+ pa_sample_spec fixed_ss;
+
pa_assert(t);
pa_assert(e);
+ if (e->memchunk.memblock)
+ fixup_sample_spec(c, &fixed_ss, &e->sample_spec);
+ else
+ memset(&fixed_ss, 0, sizeof(fixed_ss));
+
pa_tagstruct_putu32(t, e->index);
pa_tagstruct_puts(t, e->name);
pa_tagstruct_put_cvolume(t, &e->volume);
- pa_tagstruct_put_usec(t, pa_bytes_to_usec(e->memchunk.length, &e->sample_spec));
- pa_tagstruct_put_sample_spec(t, &e->sample_spec);
+ pa_tagstruct_put_usec(t, e->memchunk.memblock ? pa_bytes_to_usec(e->memchunk.length, &e->sample_spec) : 0);
+ pa_tagstruct_put_sample_spec(t, &fixed_ss);
pa_tagstruct_put_channel_map(t, &e->channel_map);
pa_tagstruct_putu32(t, e->memchunk.length);
pa_tagstruct_put_boolean(t, e->lazy);
pa_tagstruct_puts(t, e->filename);
+
+ if (c->version >= 13)
+ pa_tagstruct_put_proplist(t, e->proplist);
}
static void command_get_info(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
@@ -1891,19 +2673,19 @@ static void command_get_info(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, u
reply = reply_new(tag);
if (sink)
- sink_fill_tagstruct(reply, sink);
+ sink_fill_tagstruct(c, reply, sink);
else if (source)
- source_fill_tagstruct(reply, source);
+ source_fill_tagstruct(c, reply, source);
else if (client)
- client_fill_tagstruct(reply, client);
+ client_fill_tagstruct(c, reply, client);
else if (module)
module_fill_tagstruct(reply, module);
else if (si)
sink_input_fill_tagstruct(c, reply, si);
else if (so)
- source_output_fill_tagstruct(reply, so);
+ source_output_fill_tagstruct(c, reply, so);
else
- scache_fill_tagstruct(reply, sce);
+ scache_fill_tagstruct(c, reply, sce);
pa_pstream_send_tagstruct(c->pstream, reply);
}
@@ -1946,20 +2728,20 @@ static void command_get_info_list(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t comma
if (i) {
for (p = pa_idxset_first(i, &idx); p; p = pa_idxset_next(i, &idx)) {
if (command == PA_COMMAND_GET_SINK_INFO_LIST)
- sink_fill_tagstruct(reply, p);
+ sink_fill_tagstruct(c, reply, p);
else if (command == PA_COMMAND_GET_SOURCE_INFO_LIST)
- source_fill_tagstruct(reply, p);
+ source_fill_tagstruct(c, reply, p);
else if (command == PA_COMMAND_GET_CLIENT_INFO_LIST)
- client_fill_tagstruct(reply, p);
+ client_fill_tagstruct(c, reply, p);
else if (command == PA_COMMAND_GET_MODULE_INFO_LIST)
module_fill_tagstruct(reply, p);
else if (command == PA_COMMAND_GET_SINK_INPUT_INFO_LIST)
sink_input_fill_tagstruct(c, reply, p);
else if (command == PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST)
- source_output_fill_tagstruct(reply, p);
+ source_output_fill_tagstruct(c, reply, p);
else {
pa_assert(command == PA_COMMAND_GET_SAMPLE_INFO_LIST);
- scache_fill_tagstruct(reply, p);
+ scache_fill_tagstruct(c, reply, p);
}
}
}
@@ -1972,6 +2754,7 @@ static void command_get_server_info(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSE
pa_tagstruct *reply;
char txt[256];
const char *n;
+ pa_sample_spec fixed_ss;
connection_assert_ref(c);
pa_assert(t);
@@ -1987,8 +2770,10 @@ static void command_get_server_info(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSE
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_fqdn(txt, sizeof(txt)));
- pa_tagstruct_put_sample_spec(reply, &c->protocol->core->default_sample_spec);
+ pa_tagstruct_puts(reply, pa_get_host_name(txt, sizeof(txt)));
+
+ fixup_sample_spec(c, &fixed_ss, &c->protocol->core->default_sample_spec);
+ pa_tagstruct_put_sample_spec(reply, &fixed_ss);
n = pa_namereg_get_default_sink_name(c->protocol->core);
pa_tagstruct_puts(reply, n);
@@ -2118,7 +2903,7 @@ static void command_set_mute(
connection *c = CONNECTION(userdata);
uint32_t idx;
- int mute;
+ pa_bool_t mute;
pa_sink *sink = NULL;
pa_source *source = NULL;
pa_sink_input *si = NULL;
@@ -2181,7 +2966,7 @@ static void command_set_mute(
static void command_cork_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
connection *c = CONNECTION(userdata);
uint32_t idx;
- int b;
+ pa_bool_t b;
playback_stream *s;
connection_assert_ref(c);
@@ -2248,7 +3033,7 @@ static void command_cork_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UN
connection *c = CONNECTION(userdata);
uint32_t idx;
record_stream *s;
- int b;
+ pa_bool_t b;
connection_assert_ref(c);
pa_assert(t);
@@ -2291,6 +3076,295 @@ static void command_flush_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_U
pa_pstream_send_simple_ack(c->pstream, tag);
}
+static void command_set_stream_buffer_attr(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ connection *c = CONNECTION(userdata);
+ uint32_t idx;
+ uint32_t maxlength, tlength, prebuf, minreq, fragsize;
+ pa_tagstruct *reply;
+
+ connection_assert_ref(c);
+ pa_assert(t);
+
+ if (pa_tagstruct_getu32(t, &idx) < 0) {
+ protocol_error(c);
+ return;
+ }
+
+ CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+
+ if (command == PA_COMMAND_SET_PLAYBACK_STREAM_BUFFER_ATTR) {
+ playback_stream *s;
+ pa_bool_t adjust_latency = FALSE;
+
+ 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);
+
+ if (pa_tagstruct_get(
+ t,
+ PA_TAG_U32, &maxlength,
+ PA_TAG_U32, &tlength,
+ PA_TAG_U32, &prebuf,
+ PA_TAG_U32, &minreq,
+ PA_TAG_INVALID) < 0 ||
+ (c->version >= 13 && pa_tagstruct_get_boolean(t, &adjust_latency) < 0) ||
+ !pa_tagstruct_eof(t)) {
+ protocol_error(c);
+ return;
+ }
+
+ fix_playback_buffer_attr_pre(s, adjust_latency, &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);
+
+ reply = reply_new(tag);
+ pa_tagstruct_putu32(reply, maxlength);
+ pa_tagstruct_putu32(reply, tlength);
+ pa_tagstruct_putu32(reply, prebuf);
+ pa_tagstruct_putu32(reply, minreq);
+
+ if (c->version >= 13)
+ pa_tagstruct_put_usec(reply, s->sink_latency);
+
+ } else {
+ record_stream *s;
+ pa_bool_t adjust_latency = FALSE;
+ pa_assert(command == PA_COMMAND_SET_RECORD_STREAM_BUFFER_ATTR);
+
+ s = pa_idxset_get_by_index(c->record_streams, idx);
+ CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
+
+ if (pa_tagstruct_get(
+ t,
+ PA_TAG_U32, &maxlength,
+ PA_TAG_U32, &fragsize,
+ PA_TAG_INVALID) < 0 ||
+ (c->version >= 13 && pa_tagstruct_get_boolean(t, &adjust_latency) < 0) ||
+ !pa_tagstruct_eof(t)) {
+ protocol_error(c);
+ return;
+ }
+
+ fix_record_buffer_attr_pre(s, adjust_latency, &maxlength, &fragsize);
+ pa_memblockq_set_maxlength(s->memblockq, maxlength);
+ fix_record_buffer_attr_post(s, &maxlength, &fragsize);
+
+ reply = reply_new(tag);
+ pa_tagstruct_putu32(reply, maxlength);
+ pa_tagstruct_putu32(reply, fragsize);
+
+ if (c->version >= 13)
+ pa_tagstruct_put_usec(reply, s->source_latency);
+ }
+
+ pa_pstream_send_tagstruct(c->pstream, reply);
+}
+
+static void command_update_stream_sample_rate(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ connection *c = CONNECTION(userdata);
+ uint32_t idx;
+ uint32_t rate;
+
+ connection_assert_ref(c);
+ pa_assert(t);
+
+ if (pa_tagstruct_getu32(t, &idx) < 0 ||
+ pa_tagstruct_getu32(t, &rate) < 0 ||
+ !pa_tagstruct_eof(t)) {
+ protocol_error(c);
+ return;
+ }
+
+ CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+ CHECK_VALIDITY(c->pstream, rate > 0 && rate <= PA_RATE_MAX, tag, PA_ERR_INVALID);
+
+ if (command == PA_COMMAND_UPDATE_PLAYBACK_STREAM_SAMPLE_RATE) {
+ playback_stream *s;
+
+ 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);
+
+ pa_sink_input_set_rate(s->sink_input, rate);
+
+ } else {
+ record_stream *s;
+ pa_assert(command == PA_COMMAND_UPDATE_RECORD_STREAM_SAMPLE_RATE);
+
+ s = pa_idxset_get_by_index(c->record_streams, idx);
+ CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
+
+ pa_source_output_set_rate(s->source_output, rate);
+ }
+
+ pa_pstream_send_simple_ack(c->pstream, tag);
+}
+
+static void command_update_proplist(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ connection *c = CONNECTION(userdata);
+ uint32_t idx;
+ uint32_t mode;
+ pa_proplist *p;
+
+ connection_assert_ref(c);
+ pa_assert(t);
+
+ CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+
+ p = pa_proplist_new();
+
+ if (command == PA_COMMAND_UPDATE_CLIENT_PROPLIST) {
+
+ if (pa_tagstruct_getu32(t, &mode) < 0 ||
+ pa_tagstruct_get_proplist(t, p) < 0 ||
+ !pa_tagstruct_eof(t)) {
+ protocol_error(c);
+ pa_proplist_free(p);
+ return;
+ }
+
+ } else {
+
+ if (pa_tagstruct_getu32(t, &idx) < 0 ||
+ pa_tagstruct_getu32(t, &mode) < 0 ||
+ pa_tagstruct_get_proplist(t, p) < 0 ||
+ !pa_tagstruct_eof(t)) {
+ protocol_error(c);
+ pa_proplist_free(p);
+ return;
+ }
+ }
+
+ CHECK_VALIDITY(c->pstream, mode == PA_UPDATE_SET || mode == PA_UPDATE_MERGE || mode == PA_UPDATE_REPLACE, tag, PA_ERR_INVALID);
+
+ if (command == PA_COMMAND_UPDATE_PLAYBACK_STREAM_PROPLIST) {
+ playback_stream *s;
+
+ 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);
+
+ pa_proplist_update(s->sink_input->proplist, mode, p);
+ pa_subscription_post(c->protocol->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, s->sink_input->index);
+
+ } else if (command == PA_COMMAND_UPDATE_RECORD_STREAM_PROPLIST) {
+ record_stream *s;
+
+ s = pa_idxset_get_by_index(c->record_streams, idx);
+ CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
+
+ pa_proplist_update(s->source_output->proplist, mode, p);
+ pa_subscription_post(c->protocol->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, s->source_output->index);
+ } else {
+ pa_assert(command == PA_COMMAND_UPDATE_CLIENT_PROPLIST);
+
+ pa_proplist_update(c->client->proplist, mode, p);
+ pa_subscription_post(c->protocol->core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_CHANGE, c->client->index);
+ }
+
+ pa_pstream_send_simple_ack(c->pstream, tag);
+}
+
+static void command_remove_proplist(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ connection *c = CONNECTION(userdata);
+ uint32_t idx;
+ unsigned changed = 0;
+ pa_proplist *p;
+ pa_strlist *l = NULL;
+
+ connection_assert_ref(c);
+ pa_assert(t);
+
+ CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+
+ if (command != PA_COMMAND_REMOVE_CLIENT_PROPLIST) {
+
+ if (pa_tagstruct_getu32(t, &idx) < 0) {
+ protocol_error(c);
+ return;
+ }
+ }
+
+ if (command == PA_COMMAND_REMOVE_PLAYBACK_STREAM_PROPLIST) {
+ playback_stream *s;
+
+ 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);
+
+ p = s->sink_input->proplist;
+
+ } else if (command == PA_COMMAND_REMOVE_RECORD_STREAM_PROPLIST) {
+ record_stream *s;
+
+ s = pa_idxset_get_by_index(c->record_streams, idx);
+ CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
+
+ p = s->source_output->proplist;
+ } else {
+ pa_assert(command == PA_COMMAND_REMOVE_CLIENT_PROPLIST);
+
+ p = c->client->proplist;
+ }
+
+ for (;;) {
+ const char *k;
+
+ if (pa_tagstruct_gets(t, &k) < 0) {
+ protocol_error(c);
+ pa_strlist_free(l);
+ return;
+ }
+
+ if (!k)
+ break;
+
+ l = pa_strlist_prepend(l, k);
+ }
+
+ if (!pa_tagstruct_eof(t)) {
+ protocol_error(c);
+ pa_strlist_free(l);
+ return;
+ }
+
+ for (;;) {
+ char *z;
+
+ l = pa_strlist_pop(l, &z);
+
+ if (!z)
+ break;
+
+ changed += pa_proplist_unset(p, z) >= 0;
+ pa_xfree(z);
+ }
+
+ pa_pstream_send_simple_ack(c->pstream, tag);
+
+ if (changed) {
+ if (command == PA_COMMAND_REMOVE_PLAYBACK_STREAM_PROPLIST) {
+ playback_stream *s;
+
+ s = pa_idxset_get_by_index(c->output_streams, idx);
+ pa_subscription_post(c->protocol->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, s->sink_input->index);
+
+ } else if (command == PA_COMMAND_REMOVE_RECORD_STREAM_PROPLIST) {
+ record_stream *s;
+
+ s = pa_idxset_get_by_index(c->record_streams, idx);
+ pa_subscription_post(c->protocol->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, s->source_output->index);
+
+ } else {
+ pa_assert(command == PA_COMMAND_REMOVE_CLIENT_PROPLIST);
+ pa_subscription_post(c->protocol->core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_CHANGE, c->client->index);
+ }
+ }
+}
+
static void command_set_default_sink_or_source(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
connection *c = CONNECTION(userdata);
const char *s;
@@ -2340,6 +3414,7 @@ static void command_set_stream_name(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t com
} else {
record_stream *s;
+ pa_assert(command == PA_COMMAND_SET_RECORD_STREAM_NAME);
s = pa_idxset_get_by_index(c->record_streams, idx);
CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
@@ -2620,7 +3695,7 @@ static void command_move_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag
CHECK_VALIDITY(c->pstream, si && sink, tag, PA_ERR_NOENTITY);
- if (pa_sink_input_move_to(si, sink, 0) < 0) {
+ if (pa_sink_input_move_to(si, sink) < 0) {
pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID);
return;
}
@@ -2652,7 +3727,7 @@ static void command_suspend(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa
connection *c = CONNECTION(userdata);
uint32_t idx = PA_INVALID_INDEX;
const char *name = NULL;
- int b;
+ pa_bool_t b;
connection_assert_ref(c);
pa_assert(t);
@@ -2862,7 +3937,6 @@ static void on_connection(PA_GCC_UNUSED pa_socket_server*s, pa_iochannel *io, vo
connection *c;
char cname[256], pname[128];
- pa_assert(s);
pa_assert(io);
pa_assert(p);
@@ -2876,11 +3950,11 @@ static void on_connection(PA_GCC_UNUSED pa_socket_server*s, pa_iochannel *io, vo
c->parent.parent.free = connection_free;
c->parent.process_msg = connection_process_msg;
- c->authorized = !!p->public;
+ c->authorized = p->public;
if (!c->authorized && p->auth_ip_acl && pa_ip_acl_check(p->auth_ip_acl, pa_iochannel_get_recv_fd(io)) > 0) {
pa_log_info("Client authenticated by IP ACL.");
- c->authorized = 1;
+ c->authorized = TRUE;
}
if (!c->authorized) {
@@ -2891,14 +3965,16 @@ static void on_connection(PA_GCC_UNUSED pa_socket_server*s, pa_iochannel *io, vo
} else
c->auth_timeout_event = NULL;
+ c->is_local = pa_iochannel_socket_is_local(io);
c->version = 8;
c->protocol = p;
pa_iochannel_socket_peer_to_string(io, pname, sizeof(pname));
pa_snprintf(cname, sizeof(cname), "Native client (%s)", pname);
c->client = pa_client_new(p->core, __FILE__, cname);
+ pa_proplist_sets(c->client->proplist, "native-protocol.peer", pname);
c->client->kill = client_kill_cb;
c->client->userdata = c;
- c->client->owner = p->module;
+ c->client->module = p->module;
c->pstream = pa_pstream_new(p->core->mainloop, io, p->core->mempool);
@@ -2922,7 +3998,6 @@ static void on_connection(PA_GCC_UNUSED pa_socket_server*s, pa_iochannel *io, vo
#ifdef HAVE_CREDS
if (pa_iochannel_creds_supported(io))
pa_iochannel_creds_enable(io);
-
#endif
}
@@ -2931,12 +4006,12 @@ static void on_connection(PA_GCC_UNUSED pa_socket_server*s, pa_iochannel *io, vo
static int load_key(pa_protocol_native*p, const char*fn) {
pa_assert(p);
- p->auth_cookie_in_property = 0;
+ p->auth_cookie_in_property = FALSE;
if (!fn && pa_authkey_prop_get(p->core, PA_NATIVE_COOKIE_PROPERTY_NAME, p->auth_cookie, sizeof(p->auth_cookie)) >= 0) {
pa_log_info("using already loaded auth cookie.");
pa_authkey_prop_ref(p->core, PA_NATIVE_COOKIE_PROPERTY_NAME);
- p->auth_cookie_in_property = 1;
+ p->auth_cookie_in_property = TRUE;
return 0;
}
@@ -2949,14 +4024,14 @@ static int load_key(pa_protocol_native*p, const char*fn) {
pa_log_info("loading cookie from disk.");
if (pa_authkey_prop_put(p->core, PA_NATIVE_COOKIE_PROPERTY_NAME, p->auth_cookie, sizeof(p->auth_cookie)) >= 0)
- p->auth_cookie_in_property = 1;
+ p->auth_cookie_in_property = TRUE;
return 0;
}
static pa_protocol_native* protocol_new_internal(pa_core *c, pa_module *m, pa_modargs *ma) {
pa_protocol_native *p;
- int public = 0;
+ pa_bool_t public = FALSE;
const char *acl;
pa_assert(c);
@@ -2976,12 +4051,12 @@ static pa_protocol_native* protocol_new_internal(pa_core *c, pa_module *m, pa_mo
#ifdef HAVE_CREDS
{
- int a = 1;
+ pa_bool_t a = TRUE;
if (pa_modargs_get_value_boolean(ma, "auth-group-enabled", &a) < 0) {
pa_log("auth-group-enabled= expects a boolean argument.");
return NULL;
}
- p->auth_group = a ? pa_xstrdup(pa_modargs_get_value(ma, "auth-group", c->is_system_instance ? PA_ACCESS_GROUP : NULL)) : NULL;
+ p->auth_group = a ? pa_xstrdup(pa_modargs_get_value(ma, "auth-group", pa_in_system_mode() ? PA_ACCESS_GROUP : NULL)) : NULL;
if (p->auth_group)
pa_log_info("Allowing access to group '%s'.", p->auth_group);
@@ -3021,7 +4096,7 @@ pa_protocol_native* pa_protocol_native_new(pa_core *core, pa_socket_server *serv
if (!(p = protocol_new_internal(core, m, ma)))
return NULL;
- p->server = server;
+ p->server = pa_socket_server_ref(server);
pa_socket_server_set_callback(p->server, on_connection, p);
if (pa_socket_server_get_address(p->server, t, sizeof(t))) {
diff --git a/src/pulsecore/protocol-native.h b/src/pulsecore/protocol-native.h
index bf05f937..a52fa8cf 100644
--- a/src/pulsecore/protocol-native.h
+++ b/src/pulsecore/protocol-native.h
@@ -1,8 +1,6 @@
#ifndef fooprotocolnativehfoo
#define fooprotocolnativehfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/protocol-simple.c b/src/pulsecore/protocol-simple.c
index 64e2a81c..020a281d 100644
--- a/src/pulsecore/protocol-simple.c
+++ b/src/pulsecore/protocol-simple.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -32,6 +30,7 @@
#include <string.h>
#include <pulse/xmalloc.h>
+#include <pulse/timeval.h>
#include <pulsecore/sink-input.h>
#include <pulsecore/source-output.h>
@@ -42,6 +41,7 @@
#include <pulsecore/core-error.h>
#include <pulsecore/atomic.h>
#include <pulsecore/thread-mq.h>
+#include <pulsecore/core-util.h>
#include "protocol-simple.h"
@@ -57,12 +57,13 @@ typedef struct connection {
pa_client *client;
pa_memblockq *input_memblockq, *output_memblockq;
- int dead;
+ pa_bool_t dead;
struct {
pa_memblock *current_memblock;
- size_t memblock_index, fragment_size;
+ size_t memblock_index;
pa_atomic_t missing;
+ pa_bool_t underrun;
} playback;
} connection;
@@ -101,7 +102,8 @@ enum {
#define PLAYBACK_BUFFER_SECONDS (.5)
#define PLAYBACK_BUFFER_FRAGMENTS (10)
#define RECORD_BUFFER_SECONDS (5)
-#define RECORD_BUFFER_FRAGMENTS (100)
+#define DEFAULT_SINK_LATENCY (300*PA_USEC_PER_MSEC)
+#define DEFAULT_SOURCE_LATENCY (300*PA_USEC_PER_MSEC)
static void connection_unlink(connection *c) {
pa_assert(c);
@@ -140,8 +142,6 @@ static void connection_free(pa_object *o) {
connection *c = CONNECTION(o);
pa_assert(c);
- connection_unref(c);
-
if (c->playback.current_memblock)
pa_memblock_unref(c->playback.current_memblock);
@@ -158,27 +158,33 @@ static int do_read(connection *c) {
ssize_t r;
size_t l;
void *p;
+ size_t space;
connection_assert_ref(c);
if (!c->sink_input || (l = pa_atomic_load(&c->playback.missing)) <= 0)
return 0;
- if (l > c->playback.fragment_size)
- l = c->playback.fragment_size;
+ if (c->playback.current_memblock) {
- if (c->playback.current_memblock)
- if (pa_memblock_get_length(c->playback.current_memblock) - c->playback.memblock_index < l) {
+ space = pa_memblock_get_length(c->playback.current_memblock) - c->playback.memblock_index;
+
+ if (space <= 0) {
pa_memblock_unref(c->playback.current_memblock);
c->playback.current_memblock = NULL;
- c->playback.memblock_index = 0;
}
+ }
if (!c->playback.current_memblock) {
- pa_assert_se(c->playback.current_memblock = pa_memblock_new(c->protocol->core->mempool, l));
+ pa_assert_se(c->playback.current_memblock = pa_memblock_new(c->protocol->core->mempool, 0));
c->playback.memblock_index = 0;
+
+ space = pa_memblock_get_length(c->playback.current_memblock);
}
+ if (l > space)
+ l = space;
+
p = pa_memblock_acquire(c->playback.current_memblock);
r = pa_iochannel_read(c->io, (uint8_t*) p + c->playback.memblock_index, l);
pa_memblock_release(c->playback.current_memblock);
@@ -248,16 +254,16 @@ static void do_work(connection *c) {
if (c->dead)
return;
- if (pa_iochannel_is_readable(c->io)) {
+ if (pa_iochannel_is_readable(c->io))
if (do_read(c) < 0)
goto fail;
- } else if (pa_iochannel_is_hungup(c->io))
+
+ if (!c->sink_input && pa_iochannel_is_hungup(c->io))
goto fail;
- if (pa_iochannel_is_writable(c->io)) {
+ if (pa_iochannel_is_writable(c->io))
if (do_write(c) < 0)
goto fail;
- }
return;
@@ -266,7 +272,7 @@ fail:
if (c->sink_input) {
/* If there is a sink input, we first drain what we already have read before shutting down the connection */
- c->dead = 1;
+ c->dead = TRUE;
pa_iochannel_free(c->io);
c->io = NULL;
@@ -318,15 +324,19 @@ static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int
/* New data from the main loop */
pa_memblockq_push_align(c->input_memblockq, chunk);
+ if (pa_memblockq_is_readable(c->input_memblockq) && c->playback.underrun) {
+ pa_log_debug("Requesting rewind due to end of underrun.");
+ pa_sink_input_request_rewind(c->sink_input, 0, FALSE, TRUE);
+ }
+
/* pa_log("got data, %u", pa_memblockq_get_length(c->input_memblockq)); */
return 0;
}
- case SINK_INPUT_MESSAGE_DISABLE_PREBUF: {
+ case SINK_INPUT_MESSAGE_DISABLE_PREBUF:
pa_memblockq_prebuf_disable(c->input_memblockq);
return 0;
- }
case PA_SINK_INPUT_MESSAGE_GET_LATENCY: {
pa_usec_t *r = userdata;
@@ -343,43 +353,64 @@ static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int
}
/* Called from thread context */
-static int sink_input_peek_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) {
+static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) {
connection *c;
- int r;
- pa_assert(i);
+ pa_sink_input_assert_ref(i);
c = CONNECTION(i->userdata);
connection_assert_ref(c);
pa_assert(chunk);
- r = pa_memblockq_peek(c->input_memblockq, chunk);
+ if (pa_memblockq_peek(c->input_memblockq, chunk) < 0) {
-/* pa_log("peeked %u %i", r >= 0 ? chunk->length: 0, r); */
+ c->playback.underrun = TRUE;
- if (c->dead && r < 0)
- pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(c), CONNECTION_MESSAGE_UNLINK_CONNECTION, NULL, 0, NULL, NULL);
+ if (c->dead && pa_sink_input_safe_to_remove(i))
+ pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(c), CONNECTION_MESSAGE_UNLINK_CONNECTION, NULL, 0, NULL, NULL);
- return r;
+ return -1;
+ } else {
+ size_t m;
+
+ chunk->length = PA_MIN(length, chunk->length);
+
+ c->playback.underrun = FALSE;
+
+ pa_memblockq_drop(c->input_memblockq, chunk->length);
+ m = pa_memblockq_pop_missing(c->input_memblockq);
+
+ if (m > 0)
+ if (pa_atomic_add(&c->playback.missing, m) <= 0)
+ pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(c), CONNECTION_MESSAGE_REQUEST_DATA, NULL, 0, NULL, NULL);
+
+ return 0;
+ }
}
/* Called from thread context */
-static void sink_input_drop_cb(pa_sink_input *i, size_t length) {
+static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
connection *c;
- size_t old, new;
- pa_assert(i);
+ pa_sink_input_assert_ref(i);
c = CONNECTION(i->userdata);
connection_assert_ref(c);
- pa_assert(length);
- old = pa_memblockq_missing(c->input_memblockq);
- pa_memblockq_drop(c->input_memblockq, length);
- new = pa_memblockq_missing(c->input_memblockq);
+ /* If we are in an underrun, then we don't rewind */
+ if (i->thread_info.underrun_for > 0)
+ return;
- if (new > old) {
- if (pa_atomic_add(&c->playback.missing, new - old) <= 0)
- pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(c), CONNECTION_MESSAGE_REQUEST_DATA, NULL, 0, NULL, NULL);
- }
+ pa_memblockq_rewind(c->input_memblockq, nbytes);
+}
+
+/* Called from thread context */
+static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) {
+ connection *c;
+
+ pa_sink_input_assert_ref(i);
+ c = CONNECTION(i->userdata);
+ connection_assert_ref(c);
+
+ pa_memblockq_set_maxrewind(c->input_memblockq, nbytes);
}
/* Called from main context */
@@ -395,7 +426,7 @@ static void sink_input_kill_cb(pa_sink_input *i) {
static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk) {
connection *c;
- pa_assert(o);
+ pa_source_output_assert_ref(o);
c = CONNECTION(o->userdata);
pa_assert(c);
pa_assert(chunk);
@@ -414,7 +445,7 @@ static void source_output_kill_cb(pa_source_output *o) {
static pa_usec_t source_output_get_latency_cb(pa_source_output *o) {
connection*c;
- pa_assert(o);
+ pa_source_output_assert_ref(o);
c = CONNECTION(o->userdata);
pa_assert(c);
@@ -449,7 +480,7 @@ static void io_callback(pa_iochannel*io, void *userdata) {
static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata) {
pa_protocol_simple *p = userdata;
connection *c = NULL;
- char cname[256];
+ char cname[256], pname[128];
pa_assert(s);
pa_assert(io);
@@ -471,49 +502,64 @@ static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata)
c->protocol = p;
c->playback.current_memblock = NULL;
c->playback.memblock_index = 0;
- c->playback.fragment_size = 0;
- c->dead = 0;
+ c->dead = FALSE;
+ c->playback.underrun = TRUE;
pa_atomic_store(&c->playback.missing, 0);
- pa_iochannel_socket_peer_to_string(io, cname, sizeof(cname));
+ pa_iochannel_socket_peer_to_string(io, pname, sizeof(pname));
+ pa_snprintf(cname, sizeof(cname), "Simple client (%s)", pname);
pa_assert_se(c->client = pa_client_new(p->core, __FILE__, cname));
- c->client->owner = p->module;
+ pa_proplist_sets(c->client->proplist, "simple-protocol.peer", pname);
+ c->client->module = p->module;
c->client->kill = client_kill_cb;
c->client->userdata = c;
if (p->mode & PLAYBACK) {
pa_sink_input_new_data data;
size_t l;
+ pa_sink *sink;
+
+ if (!(sink = pa_namereg_get(c->protocol->core, c->protocol->sink_name, PA_NAMEREG_SINK, TRUE))) {
+ pa_log("Failed to get sink.");
+ goto fail;
+ }
pa_sink_input_new_data_init(&data);
data.driver = __FILE__;
- data.name = c->client->name;
- pa_sink_input_new_data_set_sample_spec(&data, &p->sample_spec);
data.module = p->module;
data.client = c->client;
+ data.sink = sink;
+ pa_proplist_update(data.proplist, PA_UPDATE_MERGE, c->client->proplist);
+ pa_sink_input_new_data_set_sample_spec(&data, &p->sample_spec);
- if (!(c->sink_input = pa_sink_input_new(p->core, &data, 0))) {
+ c->sink_input = pa_sink_input_new(p->core, &data, 0);
+ pa_sink_input_new_data_done(&data);
+
+ if (!c->sink_input) {
pa_log("Failed to create sink input.");
goto fail;
}
c->sink_input->parent.process_msg = sink_input_process_msg;
- c->sink_input->peek = sink_input_peek_cb;
- c->sink_input->drop = sink_input_drop_cb;
+ c->sink_input->pop = sink_input_pop_cb;
+ c->sink_input->process_rewind = sink_input_process_rewind_cb;
+ c->sink_input->update_max_rewind = sink_input_update_max_rewind_cb;
c->sink_input->kill = sink_input_kill_cb;
c->sink_input->userdata = c;
+ pa_sink_input_set_requested_latency(c->sink_input, DEFAULT_SINK_LATENCY);
+
l = (size_t) (pa_bytes_per_second(&p->sample_spec)*PLAYBACK_BUFFER_SECONDS);
c->input_memblockq = pa_memblockq_new(
0,
l,
- 0,
+ l,
pa_frame_size(&p->sample_spec),
(size_t) -1,
l/PLAYBACK_BUFFER_FRAGMENTS,
+ 0,
NULL);
- pa_iochannel_socket_set_rcvbuf(io, l/PLAYBACK_BUFFER_FRAGMENTS*5);
- c->playback.fragment_size = l/PLAYBACK_BUFFER_FRAGMENTS;
+ pa_iochannel_socket_set_rcvbuf(io, l);
pa_atomic_store(&c->playback.missing, pa_memblockq_missing(c->input_memblockq));
@@ -523,15 +569,25 @@ static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata)
if (p->mode & RECORD) {
pa_source_output_new_data data;
size_t l;
+ pa_source *source;
+
+ if (!(source = pa_namereg_get(c->protocol->core, c->protocol->source_name, PA_NAMEREG_SOURCE, TRUE))) {
+ pa_log("Failed to get source.");
+ goto fail;
+ }
pa_source_output_new_data_init(&data);
data.driver = __FILE__;
- data.name = c->client->name;
- pa_source_output_new_data_set_sample_spec(&data, &p->sample_spec);
data.module = p->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, &p->sample_spec);
- if (!(c->source_output = pa_source_output_new(p->core, &data, 0))) {
+ c->source_output = pa_source_output_new(p->core, &data, 0);
+ pa_source_output_new_data_done(&data);
+
+ if (!c->source_output) {
pa_log("Failed to create source output.");
goto fail;
}
@@ -540,6 +596,8 @@ static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata)
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(&p->sample_spec)*RECORD_BUFFER_SECONDS);
c->output_memblockq = pa_memblockq_new(
0,
@@ -548,8 +606,9 @@ static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata)
pa_frame_size(&p->sample_spec),
1,
0,
+ 0,
NULL);
- pa_iochannel_socket_set_sndbuf(io, l/RECORD_BUFFER_FRAGMENTS*2);
+ pa_iochannel_socket_set_sndbuf(io, l);
pa_source_output_put(c->source_output);
}
@@ -566,16 +625,17 @@ fail:
pa_protocol_simple* pa_protocol_simple_new(pa_core *core, pa_socket_server *server, pa_module *m, pa_modargs *ma) {
pa_protocol_simple* p = NULL;
- int enable;
+ pa_bool_t enable;
pa_assert(core);
pa_assert(server);
+ pa_assert(m);
pa_assert(ma);
p = pa_xnew0(pa_protocol_simple, 1);
p->module = m;
p->core = core;
- p->server = server;
+ p->server = pa_socket_server_ref(server);
p->connections = pa_idxset_new(NULL, NULL);
p->sample_spec = core->default_sample_spec;
@@ -587,14 +647,14 @@ pa_protocol_simple* pa_protocol_simple_new(pa_core *core, pa_socket_server *serv
p->source_name = pa_xstrdup(pa_modargs_get_value(ma, "source", NULL));
p->sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL));
- enable = 0;
+ enable = FALSE;
if (pa_modargs_get_value_boolean(ma, "record", &enable) < 0) {
pa_log("record= expects a numeric argument.");
goto fail;
}
p->mode = enable ? RECORD : 0;
- enable = 1;
+ enable = TRUE;
if (pa_modargs_get_value_boolean(ma, "playback", &enable) < 0) {
pa_log("playback= expects a numeric argument.");
goto fail;
diff --git a/src/pulsecore/protocol-simple.h b/src/pulsecore/protocol-simple.h
index 3b02c88e..e1b3143b 100644
--- a/src/pulsecore/protocol-simple.h
+++ b/src/pulsecore/protocol-simple.h
@@ -1,8 +1,6 @@
#ifndef fooprotocolsimplehfoo
#define fooprotocolsimplehfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/pstream-util.c b/src/pulsecore/pstream-util.c
index 1b513782..f84f486a 100644
--- a/src/pulsecore/pstream-util.c
+++ b/src/pulsecore/pstream-util.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -56,7 +54,7 @@ void pa_pstream_send_error(pa_pstream *p, uint32_t tag, uint32_t error) {
void pa_pstream_send_simple_ack(pa_pstream *p, uint32_t tag) {
pa_tagstruct *t;
-
+
pa_assert_se(t = pa_tagstruct_new(NULL, 0));
pa_tagstruct_putu32(t, PA_COMMAND_REPLY);
pa_tagstruct_putu32(t, tag);
diff --git a/src/pulsecore/pstream-util.h b/src/pulsecore/pstream-util.h
index 67759f2a..ae0d79c3 100644
--- a/src/pulsecore/pstream-util.h
+++ b/src/pulsecore/pstream-util.h
@@ -1,8 +1,6 @@
#ifndef foopstreamutilhfoo
#define foopstreamutilhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/pstream.c b/src/pulsecore/pstream.c
index 9d32a363..e26ca473 100644
--- a/src/pulsecore/pstream.c
+++ b/src/pulsecore/pstream.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -98,7 +96,7 @@ struct item_info {
/* packet info */
pa_packet *packet;
#ifdef HAVE_CREDS
- int with_creds;
+ pa_bool_t with_creds;
pa_creds creds;
#endif
@@ -121,7 +119,7 @@ struct pa_pstream {
pa_queue *send_queue;
- int dead;
+ pa_bool_t dead;
struct {
pa_pstream_descriptor descriptor;
@@ -141,7 +139,7 @@ struct pa_pstream {
size_t index;
} read;
- int use_shm;
+ pa_bool_t use_shm;
pa_memimport *import;
pa_memexport *export;
@@ -167,7 +165,7 @@ struct pa_pstream {
#ifdef HAVE_CREDS
pa_creds read_creds, write_creds;
- int read_creds_valid, send_creds_now;
+ pa_bool_t read_creds_valid, send_creds_now;
#endif
};
@@ -239,7 +237,7 @@ pa_pstream *pa_pstream_new(pa_mainloop_api *m, pa_iochannel *io, pa_mempool *poo
PA_REFCNT_INIT(p);
p->io = io;
pa_iochannel_set_callback(io, io_callback, p);
- p->dead = 0;
+ p->dead = FALSE;
p->mainloop = m;
p->defer_event = m->defer_new(m, defer_callback, p);
@@ -269,18 +267,18 @@ pa_pstream *pa_pstream_new(pa_mainloop_api *m, pa_iochannel *io, pa_mempool *poo
p->mempool = pool;
- p->use_shm = 0;
+ p->use_shm = FALSE;
p->export = NULL;
/* We do importing unconditionally */
p->import = pa_memimport_new(p->mempool, memimport_release_cb, p);
- pa_iochannel_socket_set_rcvbuf(io, 1024*8);
- pa_iochannel_socket_set_sndbuf(io, 1024*8);
+ pa_iochannel_socket_set_rcvbuf(io, pa_mempool_block_size_max(p->mempool));
+ pa_iochannel_socket_set_sndbuf(io, pa_mempool_block_size_max(p->mempool));
#ifdef HAVE_CREDS
- p->send_creds_now = 0;
- p->read_creds_valid = 0;
+ p->send_creds_now = FALSE;
+ p->read_creds_valid = FALSE;
#endif
return p;
}
@@ -374,7 +372,7 @@ void pa_pstream_send_memblock(pa_pstream*p, uint32_t channel, int64_t offset, pa
i = pa_xnew(struct item_info, 1);
i->type = PA_PSTREAM_ITEM_MEMBLOCK;
- n = MIN(length, bsm);
+ n = PA_MIN(length, bsm);
i->chunk.index = chunk->index + idx;
i->chunk.length = n;
i->chunk.memblock = pa_memblock_ref(chunk->memblock);
@@ -383,7 +381,7 @@ void pa_pstream_send_memblock(pa_pstream*p, uint32_t channel, int64_t offset, pa
i->offset = offset;
i->seek_mode = seek_mode;
#ifdef HAVE_CREDS
- i->with_creds = 0;
+ i->with_creds = FALSE;
#endif
pa_queue_push(p->send_queue, i);
@@ -410,7 +408,7 @@ void pa_pstream_send_release(pa_pstream *p, uint32_t block_id) {
item->type = PA_PSTREAM_ITEM_SHMRELEASE;
item->block_id = block_id;
#ifdef HAVE_CREDS
- item->with_creds = 0;
+ item->with_creds = FALSE;
#endif
pa_queue_push(p->send_queue, item);
@@ -447,7 +445,7 @@ void pa_pstream_send_revoke(pa_pstream *p, uint32_t block_id) {
item->type = PA_PSTREAM_ITEM_SHMREVOKE;
item->block_id = block_id;
#ifdef HAVE_CREDS
- item->with_creds = 0;
+ item->with_creds = FALSE;
#endif
pa_queue_push(p->send_queue, item);
@@ -504,7 +502,7 @@ static void prepare_next_write_item(pa_pstream *p) {
} else {
uint32_t flags;
- int send_payload = 1;
+ pa_bool_t send_payload = TRUE;
pa_assert(p->write.current->type == PA_PSTREAM_ITEM_MEMBLOCK);
pa_assert(p->write.current->chunk.memblock);
@@ -529,7 +527,7 @@ static void prepare_next_write_item(pa_pstream *p) {
&length) >= 0) {
flags |= PA_FLAG_SHMDATA;
- send_payload = 0;
+ send_payload = FALSE;
p->write.shm_info[PA_PSTREAM_SHM_BLOCKID] = htonl(block_id);
p->write.shm_info[PA_PSTREAM_SHM_SHMID] = htonl(shm_id);
@@ -599,7 +597,7 @@ static int do_write(pa_pstream *p) {
if ((r = pa_iochannel_write_with_creds(p->io, d, l, &p->write_creds)) < 0)
goto fail;
- p->send_creds_now = 0;
+ p->send_creds_now = FALSE;
} else
#endif
@@ -875,7 +873,7 @@ frame_done:
p->read.data = NULL;
#ifdef HAVE_CREDS
- p->read_creds_valid = 0;
+ p->read_creds_valid = FALSE;
#endif
return 0;
@@ -935,14 +933,14 @@ void pa_pstream_set_revoke_callback(pa_pstream *p, pa_pstream_block_id_cb_t cb,
p->release_callback_userdata = userdata;
}
-int pa_pstream_is_pending(pa_pstream *p) {
- int b;
+pa_bool_t pa_pstream_is_pending(pa_pstream *p) {
+ pa_bool_t b;
pa_assert(p);
pa_assert(PA_REFCNT_VALUE(p) > 0);
if (p->dead)
- b = 0;
+ b = FALSE;
else
b = p->write.current || !pa_queue_is_empty(p->send_queue);
@@ -971,7 +969,7 @@ void pa_pstream_unlink(pa_pstream *p) {
if (p->dead)
return;
- p->dead = 1;
+ p->dead = TRUE;
if (p->import) {
pa_memimport_free(p->import);
@@ -999,7 +997,7 @@ void pa_pstream_unlink(pa_pstream *p) {
p->recieve_memblock_callback = NULL;
}
-void pa_pstream_use_shm(pa_pstream *p, int enable) {
+void pa_pstream_enable_shm(pa_pstream *p, pa_bool_t enable) {
pa_assert(p);
pa_assert(PA_REFCNT_VALUE(p) > 0);
@@ -1018,3 +1016,10 @@ void pa_pstream_use_shm(pa_pstream *p, int enable) {
}
}
}
+
+pa_bool_t pa_pstream_get_shm(pa_pstream *p) {
+ pa_assert(p);
+ pa_assert(PA_REFCNT_VALUE(p) > 0);
+
+ return p->use_shm;
+}
diff --git a/src/pulsecore/pstream.h b/src/pulsecore/pstream.h
index 72babea9..a528b25c 100644
--- a/src/pulsecore/pstream.h
+++ b/src/pulsecore/pstream.h
@@ -1,8 +1,6 @@
#ifndef foopstreamhfoo
#define foopstreamhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -35,6 +33,7 @@
#include <pulsecore/iochannel.h>
#include <pulsecore/memchunk.h>
#include <pulsecore/creds.h>
+#include <pulsecore/macro.h>
typedef struct pa_pstream pa_pstream;
@@ -44,8 +43,11 @@ typedef void (*pa_pstream_notify_cb_t)(pa_pstream *p, void *userdata);
typedef void (*pa_pstream_block_id_cb_t)(pa_pstream *p, uint32_t block_id, void *userdata);
pa_pstream* pa_pstream_new(pa_mainloop_api *m, pa_iochannel *io, pa_mempool *p);
-void pa_pstream_unref(pa_pstream*p);
+
pa_pstream* pa_pstream_ref(pa_pstream*p);
+void pa_pstream_unref(pa_pstream*p);
+
+void pa_pstream_unlink(pa_pstream *p);
void pa_pstream_send_packet(pa_pstream*p, pa_packet *packet, const pa_creds *creds);
void pa_pstream_send_memblock(pa_pstream*p, uint32_t channel, int64_t offset, pa_seek_mode_t seek, const pa_memchunk *chunk);
@@ -59,10 +61,9 @@ void pa_pstream_set_die_callback(pa_pstream *p, pa_pstream_notify_cb_t cb, void
void pa_pstream_set_release_callback(pa_pstream *p, pa_pstream_block_id_cb_t cb, void *userdata);
void pa_pstream_set_revoke_callback(pa_pstream *p, pa_pstream_block_id_cb_t cb, void *userdata);
-int pa_pstream_is_pending(pa_pstream *p);
+pa_bool_t pa_pstream_is_pending(pa_pstream *p);
-void pa_pstream_use_shm(pa_pstream *p, int enable);
-
-void pa_pstream_unlink(pa_pstream *p);
+void pa_pstream_enable_shm(pa_pstream *p, pa_bool_t enable);
+pa_bool_t pa_pstream_get_shm(pa_pstream *p);
#endif
diff --git a/src/pulsecore/queue.c b/src/pulsecore/queue.c
index a88876f6..08fd1426 100644
--- a/src/pulsecore/queue.c
+++ b/src/pulsecore/queue.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -47,10 +45,10 @@ struct pa_queue {
pa_queue* pa_queue_new(void) {
pa_queue *q = pa_xnew(pa_queue, 1);
-
+
q->front = q->back = NULL;
q->length = 0;
-
+
return q;
}
@@ -65,7 +63,7 @@ void pa_queue_free(pa_queue* q, void (*destroy)(void *p, void *userdata), void *
pa_assert(!q->front);
pa_assert(!q->back);
pa_assert(q->length == 0);
-
+
pa_xfree(q);
}
@@ -74,10 +72,10 @@ void pa_queue_push(pa_queue *q, void *p) {
pa_assert(q);
pa_assert(p);
-
+
if (!(e = pa_flist_pop(PA_STATIC_FLIST_GET(entries))))
e = pa_xnew(struct queue_entry, 1);
-
+
e->data = p;
e->next = NULL;
@@ -112,7 +110,7 @@ void* pa_queue_pop(pa_queue *q) {
if (pa_flist_push(PA_STATIC_FLIST_GET(entries), e) < 0)
pa_xfree(e);
-
+
q->length--;
return p;
@@ -120,6 +118,6 @@ void* pa_queue_pop(pa_queue *q) {
int pa_queue_is_empty(pa_queue *q) {
pa_assert(q);
-
+
return q->length == 0;
}
diff --git a/src/pulsecore/queue.h b/src/pulsecore/queue.h
index cd767364..b09216cf 100644
--- a/src/pulsecore/queue.h
+++ b/src/pulsecore/queue.h
@@ -1,8 +1,6 @@
#ifndef fooqueuehfoo
#define fooqueuehfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/random.c b/src/pulsecore/random.c
index 87afebfa..5deac37b 100644
--- a/src/pulsecore/random.c
+++ b/src/pulsecore/random.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -64,7 +62,11 @@ static int random_proper(void *ret_data, size_t length) {
while (*device) {
ret = 0;
- if ((fd = open(*device, O_RDONLY)) >= 0) {
+ if ((fd = open(*device, O_RDONLY
+#ifdef O_NOCTTY
+ | O_NOCTTY
+#endif
+ )) >= 0) {
if ((r = pa_loop_read(fd, ret_data, length, NULL)) < 0 || (size_t) r != length)
ret = -1;
diff --git a/src/pulsecore/random.h b/src/pulsecore/random.h
index 01b7d746..36d7f9df 100644
--- a/src/pulsecore/random.h
+++ b/src/pulsecore/random.h
@@ -1,8 +1,6 @@
#ifndef foorandomhfoo
#define foorandomhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/refcnt.h b/src/pulsecore/refcnt.h
index 64271ab2..291f4504 100644
--- a/src/pulsecore/refcnt.h
+++ b/src/pulsecore/refcnt.h
@@ -1,8 +1,6 @@
#ifndef foopulserefcnthfoo
#define foopulserefcnthfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -32,6 +30,9 @@
#define PA_REFCNT_INIT(p) \
pa_atomic_store(&(p)->_ref, 1)
+#define PA_REFCNT_INIT_ZERO(p) \
+ pa_atomic_store(&(p)->_ref, 0)
+
#define PA_REFCNT_INC(p) \
pa_atomic_inc(&(p)->_ref)
diff --git a/src/pulsecore/resampler.c b/src/pulsecore/resampler.c
index 5bbc6bf4..00dc794c 100644
--- a/src/pulsecore/resampler.c
+++ b/src/pulsecore/resampler.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -38,6 +36,7 @@
#include <pulsecore/sconv.h>
#include <pulsecore/log.h>
#include <pulsecore/macro.h>
+#include <pulsecore/strbuf.h>
#include "speexwrap.h"
@@ -46,10 +45,12 @@
#include "resampler.h"
/* Number of samples of extra space we allow the resamplers to return */
-#define EXTRA_SAMPLES 128
+#define EXTRA_FRAMES 128
struct pa_resampler {
- pa_resample_method_t resample_method;
+ pa_resample_method_t method;
+ pa_resample_flags_t flags;
+
pa_sample_spec i_ss, o_ss;
pa_channel_map i_cm, o_cm;
size_t i_fz, o_fz, w_sz;
@@ -63,18 +64,28 @@ struct pa_resampler {
pa_convert_func_t to_work_format_func;
pa_convert_func_t from_work_format_func;
- int map_table[PA_CHANNELS_MAX][PA_CHANNELS_MAX];
- int map_required;
+ float map_table[PA_CHANNELS_MAX][PA_CHANNELS_MAX];
+ pa_bool_t map_required;
void (*impl_free)(pa_resampler *r);
void (*impl_update_rates)(pa_resampler *r);
void (*impl_resample)(pa_resampler *r, const pa_memchunk *in, unsigned in_samples, pa_memchunk *out, unsigned *out_samples);
+ void (*impl_reset)(pa_resampler *r);
struct { /* data specific to the trivial resampler */
unsigned o_counter;
unsigned i_counter;
} trivial;
+ struct { /* data specific to the peak finder pseudo resampler */
+ unsigned o_counter;
+ unsigned i_counter;
+
+ float max_f[PA_CHANNELS_MAX];
+ int16_t max_i[PA_CHANNELS_MAX];
+
+ } peaks;
+
#ifdef HAVE_LIBSAMPLERATE
struct { /* data specific to libsamplerate */
SRC_STATE *state;
@@ -95,6 +106,7 @@ static int copy_init(pa_resampler *r);
static int trivial_init(pa_resampler*r);
static int speex_init(pa_resampler*r);
static int ffmpeg_init(pa_resampler*r);
+static int peaks_init(pa_resampler*r);
#ifdef HAVE_LIBSAMPLERATE
static int libsamplerate_init(pa_resampler*r);
#endif
@@ -140,7 +152,8 @@ static int (* const init_table[])(pa_resampler*r) = {
[PA_RESAMPLER_SPEEX_FIXED_BASE+10] = speex_init,
[PA_RESAMPLER_FFMPEG] = ffmpeg_init,
[PA_RESAMPLER_AUTO] = NULL,
- [PA_RESAMPLER_COPY] = copy_init
+ [PA_RESAMPLER_COPY] = copy_init,
+ [PA_RESAMPLER_PEAKS] = peaks_init,
};
static inline size_t sample_size(pa_sample_format_t f) {
@@ -159,8 +172,8 @@ pa_resampler* pa_resampler_new(
const pa_channel_map *am,
const pa_sample_spec *b,
const pa_channel_map *bm,
- pa_resample_method_t resample_method,
- int variable_rate) {
+ pa_resample_method_t method,
+ pa_resample_flags_t flags) {
pa_resampler *r = NULL;
@@ -169,41 +182,43 @@ pa_resampler* pa_resampler_new(
pa_assert(b);
pa_assert(pa_sample_spec_valid(a));
pa_assert(pa_sample_spec_valid(b));
- pa_assert(resample_method >= 0);
- pa_assert(resample_method < PA_RESAMPLER_MAX);
+ pa_assert(method >= 0);
+ pa_assert(method < PA_RESAMPLER_MAX);
/* Fix method */
- if (!variable_rate && a->rate == b->rate) {
+ if (!(flags & PA_RESAMPLER_VARIABLE_RATE) && a->rate == b->rate) {
pa_log_info("Forcing resampler 'copy', because of fixed, identical sample rates.");
- resample_method = PA_RESAMPLER_COPY;
+ method = PA_RESAMPLER_COPY;
}
- if (!pa_resample_method_supported(resample_method)) {
- pa_log_warn("Support for resampler '%s' not compiled in, reverting to 'auto'.", pa_resample_method_to_string(resample_method));
- resample_method = PA_RESAMPLER_AUTO;
+ if (!pa_resample_method_supported(method)) {
+ pa_log_warn("Support for resampler '%s' not compiled in, reverting to 'auto'.", pa_resample_method_to_string(method));
+ method = PA_RESAMPLER_AUTO;
}
- if (resample_method == PA_RESAMPLER_FFMPEG && variable_rate) {
+ if (method == PA_RESAMPLER_FFMPEG && (flags & PA_RESAMPLER_VARIABLE_RATE)) {
pa_log_info("Resampler 'ffmpeg' cannot do variable rate, reverting to resampler 'auto'.");
- resample_method = PA_RESAMPLER_AUTO;
+ method = PA_RESAMPLER_AUTO;
}
- if (resample_method == PA_RESAMPLER_COPY && (variable_rate || a->rate != b->rate)) {
+ if (method == PA_RESAMPLER_COPY && ((flags & PA_RESAMPLER_VARIABLE_RATE) || a->rate != b->rate)) {
pa_log_info("Resampler 'copy' cannot change sampling rate, reverting to resampler 'auto'.");
- resample_method = PA_RESAMPLER_AUTO;
+ method = PA_RESAMPLER_AUTO;
}
- if (resample_method == PA_RESAMPLER_AUTO)
- resample_method = PA_RESAMPLER_SPEEX_FLOAT_BASE + 0;
+ if (method == PA_RESAMPLER_AUTO)
+ method = PA_RESAMPLER_SPEEX_FLOAT_BASE + 3;
r = pa_xnew(pa_resampler, 1);
r->mempool = pool;
- r->resample_method = resample_method;
+ r->method = method;
+ r->flags = flags;
r->impl_free = NULL;
r->impl_update_rates = NULL;
r->impl_resample = NULL;
+ r->impl_reset = NULL;
/* Fill sample specs */
r->i_ss = *a;
@@ -211,13 +226,13 @@ pa_resampler* pa_resampler_new(
if (am)
r->i_cm = *am;
- else
- pa_channel_map_init_auto(&r->i_cm, r->i_ss.channels, PA_CHANNEL_MAP_DEFAULT);
+ else if (!pa_channel_map_init_auto(&r->i_cm, r->i_ss.channels, PA_CHANNEL_MAP_DEFAULT))
+ goto fail;
if (bm)
r->o_cm = *bm;
- else
- pa_channel_map_init_auto(&r->o_cm, r->o_ss.channels, PA_CHANNEL_MAP_DEFAULT);
+ else if (!pa_channel_map_init_auto(&r->o_cm, r->o_ss.channels, PA_CHANNEL_MAP_DEFAULT))
+ goto fail;
r->i_fz = pa_frame_size(a);
r->o_fz = pa_frame_size(b);
@@ -231,16 +246,18 @@ pa_resampler* pa_resampler_new(
calc_map_table(r);
- pa_log_info("Using resampler '%s'", pa_resample_method_to_string(resample_method));
+ pa_log_info("Using resampler '%s'", pa_resample_method_to_string(method));
- if ((resample_method >= PA_RESAMPLER_SPEEX_FIXED_BASE && resample_method <= PA_RESAMPLER_SPEEX_FIXED_MAX) ||
- (resample_method == PA_RESAMPLER_FFMPEG))
+ if ((method >= PA_RESAMPLER_SPEEX_FIXED_BASE && method <= PA_RESAMPLER_SPEEX_FIXED_MAX) ||
+ (method == PA_RESAMPLER_FFMPEG))
r->work_format = PA_SAMPLE_S16NE;
- else if (resample_method == PA_RESAMPLER_TRIVIAL || resample_method == PA_RESAMPLER_COPY) {
+ else if (method == PA_RESAMPLER_TRIVIAL || method == PA_RESAMPLER_COPY || method == PA_RESAMPLER_PEAKS) {
- if (r->map_required || a->format != b->format) {
+ if (r->map_required || a->format != b->format || method == PA_RESAMPLER_PEAKS) {
- if (a->format == PA_SAMPLE_FLOAT32NE || a->format == PA_SAMPLE_FLOAT32RE ||
+ if (a->format == PA_SAMPLE_S32NE || a->format == PA_SAMPLE_S32RE ||
+ a->format == PA_SAMPLE_FLOAT32NE || a->format == PA_SAMPLE_FLOAT32RE ||
+ b->format == PA_SAMPLE_S32NE || b->format == PA_SAMPLE_S32RE ||
b->format == PA_SAMPLE_FLOAT32NE || b->format == PA_SAMPLE_FLOAT32RE)
r->work_format = PA_SAMPLE_FLOAT32NE;
else
@@ -279,7 +296,7 @@ pa_resampler* pa_resampler_new(
}
/* initialize implementation */
- if (init_table[resample_method](r) < 0)
+ if (init_table[method](r) < 0)
goto fail;
return r;
@@ -339,6 +356,12 @@ size_t pa_resampler_request(pa_resampler *r, size_t out_length) {
return (((out_length / r->o_fz)*r->i_ss.rate)/r->o_ss.rate) * r->i_fz;
}
+size_t pa_resampler_result(pa_resampler *r, size_t in_length) {
+ pa_assert(r);
+
+ return (((in_length / r->i_fz)*r->o_ss.rate)/r->i_ss.rate) * r->o_fz;
+}
+
size_t pa_resampler_max_block_size(pa_resampler *r) {
size_t block_size_max;
pa_sample_spec ss;
@@ -350,28 +373,30 @@ size_t pa_resampler_max_block_size(pa_resampler *r) {
/* We deduce the "largest" sample spec we're using during the
* conversion */
- ss = r->i_ss;
- if (r->o_ss.channels > ss.channels)
- ss.channels = r->o_ss.channels;
+ ss.channels = PA_MAX(r->i_ss.channels, r->o_ss.channels);
/* We silently assume that the format enum is ordered by size */
- if (r->o_ss.format > ss.format)
- ss.format = r->o_ss.format;
- if (r->work_format > ss.format)
- ss.format = r->work_format;
+ ss.format = PA_MAX(r->i_ss.format, r->o_ss.format);
+ ss.format = PA_MAX(ss.format, r->work_format);
- if (r->o_ss.rate > ss.rate)
- ss.rate = r->o_ss.rate;
+ ss.rate = PA_MAX(r->i_ss.rate, r->o_ss.rate);
fs = pa_frame_size(&ss);
- return (((block_size_max/fs + EXTRA_SAMPLES)*r->i_ss.rate)/ss.rate)*r->i_fz;
+ return (((block_size_max/fs - EXTRA_FRAMES)*r->i_ss.rate)/ss.rate)*r->i_fz;
+}
+
+void pa_resampler_reset(pa_resampler *r) {
+ pa_assert(r);
+
+ if (r->impl_reset)
+ r->impl_reset(r);
}
pa_resample_method_t pa_resampler_get_method(pa_resampler *r) {
pa_assert(r);
- return r->resample_method;
+ return r->method;
}
static const char * const resample_methods[] = {
@@ -405,7 +430,8 @@ static const char * const resample_methods[] = {
"speex-fixed-10",
"ffmpeg",
"auto",
- "copy"
+ "copy",
+ "peaks"
};
const char *pa_resample_method_to_string(pa_resample_method_t m) {
@@ -439,44 +465,431 @@ pa_resample_method_t pa_parse_resample_method(const char *string) {
return m;
if (!strcmp(string, "speex-fixed"))
- return PA_RESAMPLER_SPEEX_FIXED_BASE + 0;
+ return PA_RESAMPLER_SPEEX_FIXED_BASE + 3;
if (!strcmp(string, "speex-float"))
- return PA_RESAMPLER_SPEEX_FLOAT_BASE + 0;
+ return PA_RESAMPLER_SPEEX_FLOAT_BASE + 3;
return PA_RESAMPLER_INVALID;
}
+static pa_bool_t on_left(pa_channel_position_t p) {
+
+ return
+ p == PA_CHANNEL_POSITION_FRONT_LEFT ||
+ p == PA_CHANNEL_POSITION_REAR_LEFT ||
+ p == PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER ||
+ p == PA_CHANNEL_POSITION_SIDE_LEFT ||
+ p == PA_CHANNEL_POSITION_TOP_FRONT_LEFT ||
+ p == PA_CHANNEL_POSITION_TOP_REAR_LEFT;
+}
+
+static pa_bool_t on_right(pa_channel_position_t p) {
+
+ return
+ p == PA_CHANNEL_POSITION_FRONT_RIGHT ||
+ p == PA_CHANNEL_POSITION_REAR_RIGHT ||
+ p == PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER ||
+ p == PA_CHANNEL_POSITION_SIDE_RIGHT ||
+ p == PA_CHANNEL_POSITION_TOP_FRONT_RIGHT ||
+ p == PA_CHANNEL_POSITION_TOP_REAR_RIGHT;
+}
+
+static pa_bool_t on_center(pa_channel_position_t p) {
+
+ return
+ p == PA_CHANNEL_POSITION_FRONT_CENTER ||
+ p == PA_CHANNEL_POSITION_REAR_CENTER ||
+ p == PA_CHANNEL_POSITION_TOP_CENTER ||
+ p == PA_CHANNEL_POSITION_TOP_FRONT_CENTER ||
+ p == PA_CHANNEL_POSITION_TOP_REAR_CENTER;
+}
+
+static pa_bool_t on_lfe(pa_channel_position_t p) {
+ return
+ p == PA_CHANNEL_POSITION_LFE;
+}
+
static void calc_map_table(pa_resampler *r) {
- unsigned oc;
+ unsigned oc, ic;
+ pa_bool_t ic_connected[PA_CHANNELS_MAX];
+ pa_bool_t remix;
+ pa_strbuf *s;
+ char *t;
pa_assert(r);
- if (!(r->map_required = (r->i_ss.channels != r->o_ss.channels || !pa_channel_map_equal(&r->i_cm, &r->o_cm))))
+ if (!(r->map_required = (r->i_ss.channels != r->o_ss.channels || (!(r->flags & PA_RESAMPLER_NO_REMAP) && !pa_channel_map_equal(&r->i_cm, &r->o_cm)))))
return;
+ memset(r->map_table, 0, sizeof(r->map_table));
+ memset(ic_connected, 0, sizeof(ic_connected));
+ remix = (r->flags & (PA_RESAMPLER_NO_REMAP|PA_RESAMPLER_NO_REMIX)) == 0;
+
for (oc = 0; oc < r->o_ss.channels; oc++) {
- unsigned ic, i = 0;
+ pa_bool_t oc_connected = FALSE;
+ pa_channel_position_t b = r->o_cm.map[oc];
for (ic = 0; ic < r->i_ss.channels; ic++) {
- pa_channel_position_t a, b;
+ pa_channel_position_t a = r->i_cm.map[ic];
+
+ if (r->flags & PA_RESAMPLER_NO_REMAP) {
+ /* We shall not do any remapping. Hence, just check by index */
+
+ if (ic == oc)
+ r->map_table[oc][ic] = 1.0;
+
+ continue;
+ }
- a = r->i_cm.map[ic];
- b = r->o_cm.map[oc];
+ if (r->flags & PA_RESAMPLER_NO_REMIX) {
+ /* We shall not do any remixing. Hence, just check by name */
- if (a == b ||
- (a == PA_CHANNEL_POSITION_MONO && b == PA_CHANNEL_POSITION_LEFT) ||
- (a == PA_CHANNEL_POSITION_MONO && b == PA_CHANNEL_POSITION_RIGHT) ||
- (a == PA_CHANNEL_POSITION_LEFT && b == PA_CHANNEL_POSITION_MONO) ||
- (a == PA_CHANNEL_POSITION_RIGHT && b == PA_CHANNEL_POSITION_MONO))
+ if (a == b)
+ r->map_table[oc][ic] = 1.0;
- r->map_table[oc][i++] = ic;
+ continue;
+ }
+
+ pa_assert(remix);
+
+ /* OK, we shall do the full monty: upmixing and
+ * downmixing. Our algorithm is relatively simple, does
+ * not do spacialization, delay elements or apply lowpass
+ * filters for LFE. Patches are always welcome,
+ * though. Oh, and it doesn't do any matrix
+ * decoding. (Which probably wouldn't make any sense
+ * anyway.)
+ *
+ * This code is not idempotent: downmixing an upmixed
+ * stereo stream is not identical to the original. The
+ * volume will not match, and the two channels will be a
+ * linear combination of both.
+ *
+ * This is losely based on random suggestions found on the
+ * Internet, such as this:
+ * http://www.halfgaar.net/surround-sound-in-linux and the
+ * alsa upmix plugin.
+ *
+ * The algorithm works basically like this:
+ *
+ * 1) Connect all channels with matching names.
+ *
+ * 2) Mono Handling:
+ * S:Mono: Copy into all D:channels
+ * D:Mono: Copy in all S:channels
+ *
+ * 3) Mix D:Left, D:Right:
+ * D:Left: If not connected, avg all S:Left
+ * D:Right: If not connected, avg all S:Right
+ *
+ * 4) Mix D:Center
+ * If not connected, avg all S:Center
+ * If still not connected, avg all S:Left, S:Right
+ *
+ * 5) Mix D:LFE
+ * If not connected, avg all S:*
+ *
+ * 6) Make sure S:Left/S:Right is used: S:Left/S:Right: If
+ * not connected, mix into all D:left and all D:right
+ * channels. Gain is 0.1, the current left and right
+ * should be multiplied by 0.9.
+ *
+ * 7) Make sure S:Center, S:LFE is used:
+ *
+ * S:Center, S:LFE: If not connected, mix into all
+ * D:left, all D:right, all D:center channels, gain is
+ * 0.375. The current (as result of 1..6) factors
+ * should be multiplied by 0.75. (Alt. suggestion: 0.25
+ * vs. 0.5)
+ *
+ * S: and D: shall relate to the source resp. destination channels.
+ *
+ * Rationale: 1, 2 are probably obvious. For 3: this
+ * copies front to rear if needed. For 4: we try to find
+ * some suitable C source for C, if we don't find any, we
+ * avg L and R. For 5: LFE is mixed from all channels. For
+ * 6: the rear channels should not be dropped entirely,
+ * however have only minimal impact. For 7: movies usually
+ * encode speech on the center channel. Thus we have to
+ * make sure this channel is distributed to L and R if not
+ * available in the output. Also, LFE is used to achieve a
+ * greater dynamic range, and thus we should try to do our
+ * best to pass it to L+R.
+ */
+
+ if (a == b || a == PA_CHANNEL_POSITION_MONO || b == PA_CHANNEL_POSITION_MONO) {
+ r->map_table[oc][ic] = 1.0;
+
+ oc_connected = TRUE;
+ ic_connected[ic] = TRUE;
+ }
}
- /* Add an end marker */
- if (i < PA_CHANNELS_MAX)
- r->map_table[oc][i] = -1;
+ if (!oc_connected && remix) {
+ /* OK, we shall remix */
+
+ if (on_left(b)) {
+ unsigned n = 0;
+
+ /* We are not connected and on the left side, let's
+ * average all left side input channels. */
+
+ for (ic = 0; ic < r->i_ss.channels; ic++)
+ if (on_left(r->i_cm.map[ic]))
+ n++;
+
+ if (n > 0)
+ for (ic = 0; ic < r->i_ss.channels; ic++)
+ if (on_left(r->i_cm.map[ic])) {
+ r->map_table[oc][ic] = 1.0 / n;
+ ic_connected[ic] = TRUE;
+ }
+
+ /* We ignore the case where there is no left input
+ * channel. Something is really wrong in this case
+ * anyway. */
+
+ } else if (on_right(b)) {
+ unsigned n = 0;
+
+ /* We are not connected and on the right side, let's
+ * average all right side input channels. */
+
+ for (ic = 0; ic < r->i_ss.channels; ic++)
+ if (on_right(r->i_cm.map[ic]))
+ n++;
+
+ if (n > 0)
+ for (ic = 0; ic < r->i_ss.channels; ic++)
+ if (on_right(r->i_cm.map[ic])) {
+ r->map_table[oc][ic] = 1.0 / n;
+ ic_connected[ic] = TRUE;
+ }
+
+ /* We ignore the case where there is no right input
+ * channel. Something is really wrong in this case
+ * anyway. */
+
+ } else if (on_center(b)) {
+ unsigned n = 0;
+
+ /* We are not connected and at the center. Let's
+ * average all center input channels. */
+
+ for (ic = 0; ic < r->i_ss.channels; ic++)
+ if (on_center(r->i_cm.map[ic]))
+ n++;
+
+ if (n > 0) {
+ for (ic = 0; ic < r->i_ss.channels; ic++)
+ if (on_center(r->i_cm.map[ic])) {
+ r->map_table[oc][ic] = 1.0 / n;
+ ic_connected[ic] = TRUE;
+ }
+ } else {
+
+ /* Hmm, no center channel around, let's synthesize
+ * it by mixing L and R.*/
+
+ n = 0;
+
+ for (ic = 0; ic < r->i_ss.channels; ic++)
+ if (on_left(r->i_cm.map[ic]) || on_right(r->i_cm.map[ic]))
+ n++;
+
+ if (n > 0)
+ for (ic = 0; ic < r->i_ss.channels; ic++)
+ if (on_left(r->i_cm.map[ic]) || on_right(r->i_cm.map[ic])) {
+ r->map_table[oc][ic] = 1.0 / n;
+ ic_connected[ic] = TRUE;
+ }
+
+ /* We ignore the case where there is not even a
+ * left or right input channel. Something is
+ * really wrong in this case anyway. */
+ }
+
+ } else if (on_lfe(b)) {
+
+ /* We are not connected and an LFE. Let's average all
+ * channels for LFE. */
+
+ for (ic = 0; ic < r->i_ss.channels; ic++) {
+ r->map_table[oc][ic] = 1.0 / r->i_ss.channels;
+
+ /* Please note that a channel connected to LFE
+ * doesn't really count as connected. */
+ }
+ }
+ }
}
+
+ if (remix) {
+ unsigned
+ ic_unconnected_left = 0,
+ ic_unconnected_right = 0,
+ ic_unconnected_center = 0,
+ ic_unconnected_lfe = 0;
+
+ for (ic = 0; ic < r->i_ss.channels; ic++) {
+ pa_channel_position_t a = r->i_cm.map[ic];
+
+ if (ic_connected[ic])
+ continue;
+
+ if (on_left(a))
+ ic_unconnected_left++;
+ else if (on_right(a))
+ ic_unconnected_right++;
+ else if (on_center(a))
+ ic_unconnected_center++;
+ else if (on_lfe(a))
+ ic_unconnected_lfe++;
+ }
+
+ if (ic_unconnected_left > 0) {
+
+ /* OK, so there are unconnected input channels on the
+ * left. Let's multiply all already connected channels on
+ * the left side by .9 and add in our averaged unconnected
+ * channels multplied by .1 */
+
+ for (oc = 0; oc < r->o_ss.channels; oc++) {
+
+ if (!on_left(r->o_cm.map[oc]))
+ continue;
+
+ for (ic = 0; ic < r->i_ss.channels; ic++) {
+
+ if (ic_connected[ic]) {
+ r->map_table[oc][ic] *= .9;
+ continue;
+ }
+
+ if (on_left(r->i_cm.map[ic]))
+ r->map_table[oc][ic] = .1 / ic_unconnected_left;
+ }
+ }
+ }
+
+ if (ic_unconnected_right > 0) {
+
+ /* OK, so there are unconnected input channels on the
+ * right. Let's multiply all already connected channels on
+ * the right side by .9 and add in our averaged unconnected
+ * channels multplied by .1 */
+
+ for (oc = 0; oc < r->o_ss.channels; oc++) {
+
+ if (!on_right(r->o_cm.map[oc]))
+ continue;
+
+ for (ic = 0; ic < r->i_ss.channels; ic++) {
+
+ if (ic_connected[ic]) {
+ r->map_table[oc][ic] *= .9;
+ continue;
+ }
+
+ if (on_right(r->i_cm.map[ic]))
+ r->map_table[oc][ic] = .1 / ic_unconnected_right;
+ }
+ }
+ }
+
+ if (ic_unconnected_center > 0) {
+ pa_bool_t mixed_in = FALSE;
+
+ /* OK, so there are unconnected input channels on the
+ * center. Let's multiply all already connected channels on
+ * the center side by .9 and add in our averaged unconnected
+ * channels multplied by .1 */
+
+ for (oc = 0; oc < r->o_ss.channels; oc++) {
+
+ if (!on_center(r->o_cm.map[oc]))
+ continue;
+
+ for (ic = 0; ic < r->i_ss.channels; ic++) {
+
+ if (ic_connected[ic]) {
+ r->map_table[oc][ic] *= .9;
+ continue;
+ }
+
+ if (on_center(r->i_cm.map[ic])) {
+ r->map_table[oc][ic] = .1 / ic_unconnected_center;
+ mixed_in = TRUE;
+ }
+ }
+ }
+
+ if (!mixed_in) {
+
+ /* Hmm, as it appears there was no center channel we
+ could mix our center channel in. In this case, mix
+ it into left and right. Using .375 and 0.75 as
+ factors. */
+
+ for (oc = 0; oc < r->o_ss.channels; oc++) {
+
+ if (!on_left(r->o_cm.map[oc]) && !on_right(r->o_cm.map[oc]))
+ continue;
+
+ for (ic = 0; ic < r->i_ss.channels; ic++) {
+
+ if (ic_connected[ic]) {
+ r->map_table[oc][ic] *= .75;
+ continue;
+ }
+
+ if (on_center(r->i_cm.map[ic]))
+ r->map_table[oc][ic] = .375 / ic_unconnected_center;
+ }
+ }
+ }
+ }
+
+ if (ic_unconnected_lfe > 0) {
+
+ /* OK, so there is an unconnected LFE channel. Let's mix
+ * it into all channels, with factor 0.375 */
+
+ for (ic = 0; ic < r->i_ss.channels; ic++) {
+
+ if (!on_lfe(r->i_cm.map[ic]))
+ continue;
+
+ for (oc = 0; oc < r->o_ss.channels; oc++)
+ r->map_table[oc][ic] = 0.375 / ic_unconnected_lfe;
+ }
+ }
+ }
+
+
+ s = pa_strbuf_new();
+
+ pa_strbuf_printf(s, " ");
+ for (ic = 0; ic < r->i_ss.channels; ic++)
+ pa_strbuf_printf(s, " I%02u ", ic);
+ pa_strbuf_puts(s, "\n +");
+
+ for (ic = 0; ic < r->i_ss.channels; ic++)
+ pa_strbuf_printf(s, "------");
+ pa_strbuf_puts(s, "\n");
+
+ for (oc = 0; oc < r->o_ss.channels; oc++) {
+ pa_strbuf_printf(s, "O%02u |", oc);
+
+ for (ic = 0; ic < r->i_ss.channels; ic++)
+ pa_strbuf_printf(s, " %1.3f", r->map_table[oc][ic]);
+
+ pa_strbuf_puts(s, "\n");
+ }
+
+ pa_log_debug("Channel matrix:\n%s", t = pa_strbuf_tostring_free(s));
+ pa_xfree(t);
}
static pa_memchunk* convert_to_work_format(pa_resampler *r, pa_memchunk *input) {
@@ -516,6 +929,36 @@ static pa_memchunk* convert_to_work_format(pa_resampler *r, pa_memchunk *input)
return &r->buf1;
}
+static void vectoradd_s16_with_fraction(
+ int16_t *d, int dstr,
+ const int16_t *s1, int sstr1,
+ const int16_t *s2, int sstr2,
+ int n,
+ float s3, float s4) {
+
+ int32_t i3, i4;
+
+ i3 = (int32_t) (s3 * 0x10000);
+ i4 = (int32_t) (s4 * 0x10000);
+
+ for (; n > 0; n--) {
+ int32_t a, b;
+
+ a = *s1;
+ b = *s2;
+
+ a = (a * i3) / 0x10000;
+ b = (b * i4) / 0x10000;
+
+ *d = (int16_t) (a + b);
+
+ s1 = (const int16_t*) ((const uint8_t*) s1 + sstr1);
+ s2 = (const int16_t*) ((const uint8_t*) s2 + sstr2);
+ d = (int16_t*) ((uint8_t*) d + dstr);
+
+ }
+}
+
static pa_memchunk *remap_channels(pa_resampler *r, pa_memchunk *input) {
unsigned in_n_samples, out_n_samples, n_frames;
int i_skip, o_skip;
@@ -558,16 +1001,21 @@ static pa_memchunk *remap_channels(pa_resampler *r, pa_memchunk *input) {
case PA_SAMPLE_FLOAT32NE:
for (oc = 0; oc < r->o_ss.channels; oc++) {
- unsigned i;
+ unsigned ic;
static const float one = 1.0;
- for (i = 0; i < PA_CHANNELS_MAX && r->map_table[oc][i] >= 0; i++)
+ for (ic = 0; ic < r->i_ss.channels; ic++) {
+
+ if (r->map_table[oc][ic] <= 0.0)
+ continue;
+
oil_vectoradd_f32(
(float*) dst + oc, o_skip,
(float*) dst + oc, o_skip,
- (float*) src + r->map_table[oc][i], i_skip,
+ (float*) src + ic, i_skip,
n_frames,
- &one, &one);
+ &one, &r->map_table[oc][ic]);
+ }
}
break;
@@ -575,16 +1023,32 @@ static pa_memchunk *remap_channels(pa_resampler *r, pa_memchunk *input) {
case PA_SAMPLE_S16NE:
for (oc = 0; oc < r->o_ss.channels; oc++) {
- unsigned i;
- static const int16_t one = 1;
-
- for (i = 0; i < PA_CHANNELS_MAX && r->map_table[oc][i] >= 0; i++)
- oil_vectoradd_s16(
- (int16_t*) dst + oc, o_skip,
- (int16_t*) dst + oc, o_skip,
- (int16_t*) src + r->map_table[oc][i], i_skip,
- n_frames,
- &one, &one);
+ unsigned ic;
+
+ for (ic = 0; ic < r->i_ss.channels; ic++) {
+
+ if (r->map_table[oc][ic] <= 0.0)
+ continue;
+
+ if (r->map_table[oc][ic] >= 1.0) {
+ static const int16_t one = 1;
+
+ oil_vectoradd_s16(
+ (int16_t*) dst + oc, o_skip,
+ (int16_t*) dst + oc, o_skip,
+ (int16_t*) src + ic, i_skip,
+ n_frames,
+ &one, &one);
+
+ } else
+
+ vectoradd_s16_with_fraction(
+ (int16_t*) dst + oc, o_skip,
+ (int16_t*) dst + oc, o_skip,
+ (int16_t*) src + ic, i_skip,
+ n_frames,
+ 1.0, r->map_table[oc][ic]);
+ }
}
break;
@@ -616,7 +1080,7 @@ static pa_memchunk *resample(pa_resampler *r, pa_memchunk *input) {
in_n_samples = input->length / r->w_sz;
in_n_frames = in_n_samples / r->o_ss.channels;
- out_n_frames = ((in_n_frames*r->o_ss.rate)/r->i_ss.rate)+EXTRA_SAMPLES;
+ out_n_frames = ((in_n_frames*r->o_ss.rate)/r->i_ss.rate)+EXTRA_FRAMES;
out_n_samples = out_n_frames * r->o_ss.channels;
r->buf3.index = 0;
@@ -737,6 +1201,12 @@ static void libsamplerate_update_rates(pa_resampler *r) {
pa_assert_se(src_set_ratio(r->src.state, (double) r->o_ss.rate / r->i_ss.rate) == 0);
}
+static void libsamplerate_reset(pa_resampler *r) {
+ pa_assert(r);
+
+ pa_assert_se(src_reset(r->src.state) == 0);
+}
+
static void libsamplerate_free(pa_resampler *r) {
pa_assert(r);
@@ -749,12 +1219,13 @@ static int libsamplerate_init(pa_resampler *r) {
pa_assert(r);
- if (!(r->src.state = src_new(r->resample_method, r->o_ss.channels, &err)))
+ if (!(r->src.state = src_new(r->method, r->o_ss.channels, &err)))
return -1;
r->impl_free = libsamplerate_free;
r->impl_update_rates = libsamplerate_update_rates;
r->impl_resample = libsamplerate_resample;
+ r->impl_reset = libsamplerate_reset;
return 0;
}
@@ -807,24 +1278,35 @@ static void speex_resample_int(pa_resampler *r, const pa_memchunk *input, unsign
static void speex_update_rates(pa_resampler *r) {
pa_assert(r);
- if (r->resample_method >= PA_RESAMPLER_SPEEX_FIXED_BASE && r->resample_method <= PA_RESAMPLER_SPEEX_FIXED_MAX)
+ if (r->method >= PA_RESAMPLER_SPEEX_FIXED_BASE && r->method <= PA_RESAMPLER_SPEEX_FIXED_MAX)
pa_assert_se(paspfx_resampler_set_rate(r->speex.state, r->i_ss.rate, r->o_ss.rate) == 0);
else {
- pa_assert(r->resample_method >= PA_RESAMPLER_SPEEX_FLOAT_BASE && r->resample_method <= PA_RESAMPLER_SPEEX_FLOAT_MAX);
+ pa_assert(r->method >= PA_RESAMPLER_SPEEX_FLOAT_BASE && r->method <= PA_RESAMPLER_SPEEX_FLOAT_MAX);
pa_assert_se(paspfl_resampler_set_rate(r->speex.state, r->i_ss.rate, r->o_ss.rate) == 0);
}
}
+static void speex_reset(pa_resampler *r) {
+ pa_assert(r);
+
+ if (r->method >= PA_RESAMPLER_SPEEX_FIXED_BASE && r->method <= PA_RESAMPLER_SPEEX_FIXED_MAX)
+ pa_assert_se(paspfx_resampler_reset_mem(r->speex.state) == 0);
+ else {
+ pa_assert(r->method >= PA_RESAMPLER_SPEEX_FLOAT_BASE && r->method <= PA_RESAMPLER_SPEEX_FLOAT_MAX);
+ pa_assert_se(paspfl_resampler_reset_mem(r->speex.state) == 0);
+ }
+}
+
static void speex_free(pa_resampler *r) {
pa_assert(r);
if (!r->speex.state)
return;
- if (r->resample_method >= PA_RESAMPLER_SPEEX_FIXED_BASE && r->resample_method <= PA_RESAMPLER_SPEEX_FIXED_MAX)
+ if (r->method >= PA_RESAMPLER_SPEEX_FIXED_BASE && r->method <= PA_RESAMPLER_SPEEX_FIXED_MAX)
paspfx_resampler_destroy(r->speex.state);
else {
- pa_assert(r->resample_method >= PA_RESAMPLER_SPEEX_FLOAT_BASE && r->resample_method <= PA_RESAMPLER_SPEEX_FLOAT_MAX);
+ pa_assert(r->method >= PA_RESAMPLER_SPEEX_FLOAT_BASE && r->method <= PA_RESAMPLER_SPEEX_FLOAT_MAX);
paspfl_resampler_destroy(r->speex.state);
}
}
@@ -836,9 +1318,10 @@ static int speex_init(pa_resampler *r) {
r->impl_free = speex_free;
r->impl_update_rates = speex_update_rates;
+ r->impl_reset = speex_reset;
- if (r->resample_method >= PA_RESAMPLER_SPEEX_FIXED_BASE && r->resample_method <= PA_RESAMPLER_SPEEX_FIXED_MAX) {
- q = r->resample_method - PA_RESAMPLER_SPEEX_FIXED_BASE;
+ if (r->method >= PA_RESAMPLER_SPEEX_FIXED_BASE && r->method <= PA_RESAMPLER_SPEEX_FIXED_MAX) {
+ q = r->method - PA_RESAMPLER_SPEEX_FIXED_BASE;
pa_log_info("Choosing speex quality setting %i.", q);
@@ -847,8 +1330,8 @@ static int speex_init(pa_resampler *r) {
r->impl_resample = speex_resample_int;
} else {
- pa_assert(r->resample_method >= PA_RESAMPLER_SPEEX_FLOAT_BASE && r->resample_method <= PA_RESAMPLER_SPEEX_FLOAT_MAX);
- q = r->resample_method - PA_RESAMPLER_SPEEX_FLOAT_BASE;
+ pa_assert(r->method >= PA_RESAMPLER_SPEEX_FLOAT_BASE && r->method <= PA_RESAMPLER_SPEEX_FLOAT_MAX);
+ q = r->method - PA_RESAMPLER_SPEEX_FLOAT_BASE;
pa_log_info("Choosing speex quality setting %i.", q);
@@ -909,7 +1392,7 @@ static void trivial_resample(pa_resampler *r, const pa_memchunk *input, unsigned
}
}
-static void trivial_update_rates(pa_resampler *r) {
+static void trivial_update_rates_or_reset(pa_resampler *r) {
pa_assert(r);
r->trivial.i_counter = 0;
@@ -922,8 +1405,117 @@ static int trivial_init(pa_resampler*r) {
r->trivial.o_counter = r->trivial.i_counter = 0;
r->impl_resample = trivial_resample;
- r->impl_update_rates = trivial_update_rates;
- r->impl_free = NULL;
+ r->impl_update_rates = trivial_update_rates_or_reset;
+ r->impl_reset = trivial_update_rates_or_reset;
+
+ return 0;
+}
+
+/* Peak finder implementation */
+
+static void peaks_resample(pa_resampler *r, const pa_memchunk *input, unsigned in_n_frames, pa_memchunk *output, unsigned *out_n_frames) {
+ size_t fz;
+ unsigned o_index;
+ void *src, *dst;
+ unsigned start = 0;
+
+ pa_assert(r);
+ pa_assert(input);
+ pa_assert(output);
+ pa_assert(out_n_frames);
+
+ fz = r->w_sz * r->o_ss.channels;
+
+ src = (uint8_t*) pa_memblock_acquire(input->memblock) + input->index;
+ dst = (uint8_t*) pa_memblock_acquire(output->memblock) + output->index;
+
+ for (o_index = 0;; o_index++, r->peaks.o_counter++) {
+ unsigned j;
+
+ j = ((r->peaks.o_counter * r->i_ss.rate) / r->o_ss.rate);
+ j = j > r->peaks.i_counter ? j - r->peaks.i_counter : 0;
+
+ if (j >= in_n_frames)
+ break;
+
+ pa_assert(o_index * fz < pa_memblock_get_length(output->memblock));
+
+ if (r->work_format == PA_SAMPLE_S16NE) {
+ unsigned i, c;
+ int16_t *s = (int16_t*) ((uint8_t*) src + fz * j);
+ int16_t *d = (int16_t*) ((uint8_t*) dst + fz * o_index);
+
+ for (i = start; i <= j; i++)
+ for (c = 0; c < r->o_ss.channels; c++, s++) {
+ int16_t n;
+
+ n = *s < 0 ? -*s : *s;
+
+ if (n > r->peaks.max_i[c])
+ r->peaks.max_i[c] = n;
+ }
+
+ for (c = 0; c < r->o_ss.channels; c++, d++) {
+ *d = r->peaks.max_i[c];
+ r->peaks.max_i[c] = 0;
+ }
+ } else {
+ unsigned i, c;
+ float *s = (float*) ((uint8_t*) src + fz * j);
+ float *d = (float*) ((uint8_t*) dst + fz * o_index);
+
+ pa_assert(r->work_format == PA_SAMPLE_FLOAT32NE);
+
+ for (i = start; i <= j; i++)
+ for (c = 0; c < r->o_ss.channels; c++, s++) {
+ float n = fabsf(*s);
+
+ if (n > r->peaks.max_f[c])
+ r->peaks.max_f[c] = n;
+ }
+
+ for (c = 0; c < r->o_ss.channels; c++, d++) {
+ *d = r->peaks.max_f[c];
+ r->peaks.max_f[c] = 0;
+ }
+ }
+
+ start = j+1;
+ }
+
+ pa_memblock_release(input->memblock);
+ pa_memblock_release(output->memblock);
+
+ *out_n_frames = o_index;
+
+ r->peaks.i_counter += in_n_frames;
+
+ /* Normalize counters */
+ while (r->peaks.i_counter >= r->i_ss.rate) {
+ pa_assert(r->peaks.o_counter >= r->o_ss.rate);
+
+ r->peaks.i_counter -= r->i_ss.rate;
+ r->peaks.o_counter -= r->o_ss.rate;
+ }
+}
+
+static void peaks_update_rates_or_reset(pa_resampler *r) {
+ pa_assert(r);
+
+ r->peaks.i_counter = 0;
+ r->peaks.o_counter = 0;
+}
+
+static int peaks_init(pa_resampler*r) {
+ pa_assert(r);
+
+ r->peaks.o_counter = r->peaks.i_counter = 0;
+ memset(r->peaks.max_i, 0, sizeof(r->peaks.max_i));
+ memset(r->peaks.max_f, 0, sizeof(r->peaks.max_f));
+
+ r->impl_resample = peaks_resample;
+ r->impl_update_rates = peaks_update_rates_or_reset;
+ r->impl_reset = peaks_update_rates_or_reset;
return 0;
}
@@ -1051,9 +1643,5 @@ static int copy_init(pa_resampler *r) {
pa_assert(r->o_ss.rate == r->i_ss.rate);
- r->impl_free = NULL;
- r->impl_resample = NULL;
- r->impl_update_rates = NULL;
-
return 0;
}
diff --git a/src/pulsecore/resampler.h b/src/pulsecore/resampler.h
index 23e1acb7..5e302a9b 100644
--- a/src/pulsecore/resampler.h
+++ b/src/pulsecore/resampler.h
@@ -1,8 +1,6 @@
#ifndef fooresamplerhfoo
#define fooresamplerhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -46,9 +44,16 @@ typedef enum pa_resample_method {
PA_RESAMPLER_FFMPEG,
PA_RESAMPLER_AUTO, /* automatic select based on sample format */
PA_RESAMPLER_COPY,
+ PA_RESAMPLER_PEAKS,
PA_RESAMPLER_MAX
} pa_resample_method_t;
+typedef enum pa_resample_flags {
+ PA_RESAMPLER_VARIABLE_RATE = 1,
+ PA_RESAMPLER_NO_REMAP = 2, /* implies NO_REMIX */
+ PA_RESAMPLER_NO_REMIX = 4
+} pa_resample_flags_t;
+
pa_resampler* pa_resampler_new(
pa_mempool *pool,
const pa_sample_spec *a,
@@ -56,13 +61,16 @@ pa_resampler* pa_resampler_new(
const pa_sample_spec *b,
const pa_channel_map *bm,
pa_resample_method_t resample_method,
- int variable_rate);
+ pa_resample_flags_t flags);
void pa_resampler_free(pa_resampler *r);
/* Returns the size of an input memory block which is required to return the specified amount of output data */
size_t pa_resampler_request(pa_resampler *r, size_t out_length);
+/* Inverse of pa_resampler_request() */
+size_t pa_resampler_result(pa_resampler *r, size_t in_length);
+
/* Returns the maximum size of input blocks we can process without needing bounce buffers larger than the mempool tile size. */
size_t pa_resampler_max_block_size(pa_resampler *r);
@@ -75,6 +83,9 @@ void pa_resampler_set_input_rate(pa_resampler *r, uint32_t rate);
/* Change the output rate of the resampler object */
void pa_resampler_set_output_rate(pa_resampler *r, uint32_t rate);
+/* Reinitialize state of the resampler, possibly due to seeking or other discontinuities */
+void pa_resampler_reset(pa_resampler *r);
+
/* Return the resampling method of the resampler object */
pa_resample_method_t pa_resampler_get_method(pa_resampler *r);
@@ -87,4 +98,5 @@ const char *pa_resample_method_to_string(pa_resample_method_t m);
/* Return 1 when the specified resampling method is supported */
int pa_resample_method_supported(pa_resample_method_t m);
+
#endif
diff --git a/src/pulsecore/rtclock.c b/src/pulsecore/rtclock.c
index 07d776e4..f33de830 100644
--- a/src/pulsecore/rtclock.c
+++ b/src/pulsecore/rtclock.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -96,3 +94,24 @@ pa_usec_t pa_rtclock_usec(void) {
return pa_timeval_load(pa_rtclock_get(&tv));
}
+
+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;
+}
diff --git a/src/pulsecore/rtclock.h b/src/pulsecore/rtclock.h
index f0360af3..705de48f 100644
--- a/src/pulsecore/rtclock.h
+++ b/src/pulsecore/rtclock.h
@@ -1,8 +1,6 @@
#ifndef foopulsertclockhfoo
#define foopulsertclockhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -40,4 +38,6 @@ pa_bool_t pa_rtclock_hrtimer(void);
/* timer with a resolution better than this are considered high-resolution */
#define PA_HRTIMER_THRESHOLD_USEC 10
+struct timeval* pa_rtclock_from_wallclock(struct timeval *tv);
+
#endif
diff --git a/src/pulsecore/rtpoll.c b/src/pulsecore/rtpoll.c
index 354c4c0e..a67a5516 100644
--- a/src/pulsecore/rtpoll.c
+++ b/src/pulsecore/rtpoll.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -63,7 +61,6 @@ struct pa_rtpoll {
pa_bool_t timer_enabled;
struct timeval next_elapse;
- pa_usec_t period;
pa_bool_t scan_for_dead;
pa_bool_t running, installed, rebuild_needed, quit;
@@ -72,6 +69,7 @@ struct pa_rtpoll {
int rtsig;
sigset_t sigset_unblocked;
timer_t timer;
+ pa_bool_t timer_armed;
#ifdef __linux__
pa_bool_t dont_use_ppoll;
#endif
@@ -99,7 +97,7 @@ struct pa_rtpoll_item {
PA_STATIC_FLIST_DECLARE(items, 0, pa_xfree);
-static void signal_handler_noop(int s) { }
+static void signal_handler_noop(int s) { /* write(2, "signal\n", 7); */ }
pa_rtpoll *pa_rtpoll_new(void) {
pa_rtpoll *p;
@@ -131,6 +129,7 @@ pa_rtpoll *pa_rtpoll_new(void) {
p->rtsig = -1;
sigemptyset(&p->sigset_unblocked);
p->timer = (timer_t) -1;
+ p->timer_armed = FALSE;
#endif
@@ -139,7 +138,6 @@ pa_rtpoll *pa_rtpoll_new(void) {
p->pollfd2 = pa_xnew(struct pollfd, p->n_pollfd_alloc);
p->n_pollfd_used = 0;
- p->period = 0;
memset(&p->next_elapse, 0, sizeof(p->next_elapse));
p->timer_enabled = FALSE;
@@ -161,8 +159,10 @@ void pa_rtpoll_install(pa_rtpoll *p) {
p->installed = 1;
#ifdef HAVE_PPOLL
+# ifdef __linux__
if (p->dont_use_ppoll)
return;
+# endif
if ((p->rtsig = pa_rtsig_get_for_thread()) < 0) {
pa_log_warn("Failed to reserve POSIX realtime signal.");
@@ -366,15 +366,13 @@ int pa_rtpoll_run(pa_rtpoll *p, pa_bool_t wait) {
if (p->rebuild_needed)
rtpoll_rebuild(p);
+ memset(&timeout, 0, sizeof(timeout));
+
/* Calculate timeout */
- if (!wait || p->quit) {
- timeout.tv_sec = 0;
- timeout.tv_usec = 0;
- } else if (p->timer_enabled) {
+ if (wait && !p->quit && p->timer_enabled) {
struct timeval now;
pa_rtclock_get(&now);
- memset(&timeout, 0, sizeof(timeout));
if (pa_timeval_cmp(&p->next_elapse, &now) > 0)
pa_timeval_add(&timeout, pa_timeval_diff(&p->next_elapse, &now));
}
@@ -389,14 +387,14 @@ int pa_rtpoll_run(pa_rtpoll *p, pa_bool_t wait) {
struct timespec ts;
ts.tv_sec = timeout.tv_sec;
ts.tv_nsec = timeout.tv_usec * 1000;
- r = ppoll(p->pollfd, p->n_pollfd_used, p->timer_enabled ? &ts : NULL, p->rtsig < 0 ? NULL : &p->sigset_unblocked);
+ r = ppoll(p->pollfd, p->n_pollfd_used, (!wait || p->quit || p->timer_enabled) ? &ts : NULL, p->rtsig < 0 ? NULL : &p->sigset_unblocked);
}
#ifdef __linux__
else
#endif
#endif
- r = poll(p->pollfd, p->n_pollfd_used, p->timer_enabled ? (timeout.tv_sec*1000) + (timeout.tv_usec / 1000) : -1);
+ r = poll(p->pollfd, p->n_pollfd_used, (!wait || p->quit || p->timer_enabled) ? (timeout.tv_sec*1000) + (timeout.tv_usec / 1000) : -1);
if (r < 0) {
if (errno == EAGAIN || errno == EINTR)
@@ -407,21 +405,6 @@ int pa_rtpoll_run(pa_rtpoll *p, pa_bool_t wait) {
reset_all_revents(p);
}
- if (p->timer_enabled) {
- if (p->period > 0) {
- struct timeval now;
- pa_rtclock_get(&now);
-
- pa_timeval_add(&p->next_elapse, p->period);
-
- /* Guarantee that the next timeout will happen in the future */
- if (pa_timeval_cmp(&p->next_elapse, &now) < 0)
- pa_timeval_add(&p->next_elapse, (pa_timeval_diff(&now, &p->next_elapse) / p->period + 1) * p->period);
-
- } else
- p->timer_enabled = FALSE;
- }
-
/* Let's tell everyone that we left the sleep */
for (i = p->items; i && i->priority < PA_RTPOLL_NEVER; i = i->next) {
@@ -479,26 +462,35 @@ static void update_timer(pa_rtpoll *p) {
if (p->timer != (timer_t) -1) {
struct itimerspec its;
- memset(&its, 0, sizeof(its));
+ struct timespec ts = { .tv_sec = 0, .tv_nsec = 0 };
+ sigset_t ss;
+
+ 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 (p->timer_enabled) {
+ memset(&its, 0, sizeof(its));
+
its.it_value.tv_sec = p->next_elapse.tv_sec;
its.it_value.tv_nsec = p->next_elapse.tv_usec*1000;
/* Make sure that 0,0 is not understood as
* "disarming" */
- if (its.it_value.tv_sec == 0)
+ if (its.it_value.tv_sec == 0 && its.it_value.tv_nsec == 0)
its.it_value.tv_nsec = 1;
-
- if (p->period > 0) {
- struct timeval tv;
- pa_timeval_store(&tv, p->period);
- its.it_interval.tv_sec = tv.tv_sec;
- its.it_interval.tv_nsec = tv.tv_usec*1000;
- }
+ pa_assert_se(timer_settime(p->timer, TIMER_ABSTIME, &its, NULL) == 0);
}
- pa_assert_se(timer_settime(p->timer, TIMER_ABSTIME, &its, NULL) == 0);
+ p->timer_armed = p->timer_enabled;
}
#ifdef __linux__
@@ -508,23 +500,10 @@ static void update_timer(pa_rtpoll *p) {
#endif
}
-void pa_rtpoll_set_timer_absolute(pa_rtpoll *p, const struct timeval *ts) {
+void pa_rtpoll_set_timer_absolute(pa_rtpoll *p, pa_usec_t usec) {
pa_assert(p);
- pa_assert(ts);
- p->next_elapse = *ts;
- p->period = 0;
- p->timer_enabled = TRUE;
-
- update_timer(p);
-}
-
-void pa_rtpoll_set_timer_periodic(pa_rtpoll *p, pa_usec_t usec) {
- pa_assert(p);
-
- p->period = usec;
- pa_rtclock_get(&p->next_elapse);
- pa_timeval_add(&p->next_elapse, usec);
+ pa_timeval_store(&p->next_elapse, usec);
p->timer_enabled = TRUE;
update_timer(p);
@@ -533,7 +512,9 @@ void pa_rtpoll_set_timer_periodic(pa_rtpoll *p, pa_usec_t usec) {
void pa_rtpoll_set_timer_relative(pa_rtpoll *p, pa_usec_t usec) {
pa_assert(p);
- p->period = 0;
+ /* Scheduling a timeout for more than an hour is very very suspicious */
+ pa_assert(usec <= PA_USEC_PER_SEC*60ULL*60ULL);
+
pa_rtclock_get(&p->next_elapse);
pa_timeval_add(&p->next_elapse, usec);
p->timer_enabled = TRUE;
@@ -544,7 +525,6 @@ void pa_rtpoll_set_timer_relative(pa_rtpoll *p, pa_usec_t usec) {
void pa_rtpoll_set_timer_disabled(pa_rtpoll *p) {
pa_assert(p);
- p->period = 0;
memset(&p->next_elapse, 0, sizeof(p->next_elapse));
p->timer_enabled = FALSE;
@@ -681,23 +661,23 @@ pa_rtpoll_item *pa_rtpoll_item_new_fdsem(pa_rtpoll *p, pa_rtpoll_priority_t prio
return i;
}
-static int asyncmsgq_before(pa_rtpoll_item *i) {
+static int asyncmsgq_read_before(pa_rtpoll_item *i) {
pa_assert(i);
- if (pa_asyncmsgq_before_poll(i->userdata) < 0)
+ if (pa_asyncmsgq_read_before_poll(i->userdata) < 0)
return 1; /* 1 means immediate restart of the loop */
return 0;
}
-static void asyncmsgq_after(pa_rtpoll_item *i) {
+static void asyncmsgq_read_after(pa_rtpoll_item *i) {
pa_assert(i);
pa_assert((i->pollfd[0].revents & ~POLLIN) == 0);
- pa_asyncmsgq_after_poll(i->userdata);
+ pa_asyncmsgq_read_after_poll(i->userdata);
}
-static int asyncmsgq_work(pa_rtpoll_item *i) {
+static int asyncmsgq_read_work(pa_rtpoll_item *i) {
pa_msgobject *object;
int code;
void *data;
@@ -723,7 +703,7 @@ static int asyncmsgq_work(pa_rtpoll_item *i) {
return 0;
}
-pa_rtpoll_item *pa_rtpoll_item_new_asyncmsgq(pa_rtpoll *p, pa_rtpoll_priority_t prio, pa_asyncmsgq *q) {
+pa_rtpoll_item *pa_rtpoll_item_new_asyncmsgq_read(pa_rtpoll *p, pa_rtpoll_priority_t prio, pa_asyncmsgq *q) {
pa_rtpoll_item *i;
struct pollfd *pollfd;
@@ -733,12 +713,47 @@ pa_rtpoll_item *pa_rtpoll_item_new_asyncmsgq(pa_rtpoll *p, pa_rtpoll_priority_t
i = pa_rtpoll_item_new(p, prio, 1);
pollfd = pa_rtpoll_item_get_pollfd(i, NULL);
- pollfd->fd = pa_asyncmsgq_get_fd(q);
+ pollfd->fd = pa_asyncmsgq_read_fd(q);
pollfd->events = POLLIN;
- i->before_cb = asyncmsgq_before;
- i->after_cb = asyncmsgq_after;
- i->work_cb = asyncmsgq_work;
+ i->before_cb = asyncmsgq_read_before;
+ i->after_cb = asyncmsgq_read_after;
+ i->work_cb = asyncmsgq_read_work;
+ i->userdata = q;
+
+ return i;
+}
+
+static int asyncmsgq_write_before(pa_rtpoll_item *i) {
+ pa_assert(i);
+
+ pa_asyncmsgq_write_before_poll(i->userdata);
+ return 0;
+}
+
+static void asyncmsgq_write_after(pa_rtpoll_item *i) {
+ pa_assert(i);
+
+ pa_assert((i->pollfd[0].revents & ~POLLIN) == 0);
+ pa_asyncmsgq_write_after_poll(i->userdata);
+}
+
+pa_rtpoll_item *pa_rtpoll_item_new_asyncmsgq_write(pa_rtpoll *p, pa_rtpoll_priority_t prio, pa_asyncmsgq *q) {
+ pa_rtpoll_item *i;
+ struct pollfd *pollfd;
+
+ pa_assert(p);
+ pa_assert(q);
+
+ i = pa_rtpoll_item_new(p, prio, 1);
+
+ pollfd = pa_rtpoll_item_get_pollfd(i, NULL);
+ pollfd->fd = pa_asyncmsgq_write_fd(q);
+ pollfd->events = POLLIN;
+
+ i->before_cb = asyncmsgq_write_before;
+ i->after_cb = asyncmsgq_write_after;
+ i->work_cb = NULL;
i->userdata = q;
return i;
diff --git a/src/pulsecore/rtpoll.h b/src/pulsecore/rtpoll.h
index d38d0864..08776ef0 100644
--- a/src/pulsecore/rtpoll.h
+++ b/src/pulsecore/rtpoll.h
@@ -1,8 +1,6 @@
#ifndef foopulsertpollhfoo
#define foopulsertpollhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -58,7 +56,7 @@ typedef enum pa_rtpoll_priority {
PA_RTPOLL_EARLY = -100, /* For veeery important stuff, like handling control messages */
PA_RTPOLL_NORMAL = 0, /* For normal stuff */
PA_RTPOLL_LATE = +100, /* For housekeeping */
- PA_RTPOLL_NEVER = INT_MAX, /* For stuff that doesn't register any callbacks, but only fds to listen on */
+ PA_RTPOLL_NEVER = INT_MAX, /* For stuff that doesn't register any callbacks, but only fds to listen on */
} pa_rtpoll_priority_t;
pa_rtpoll *pa_rtpoll_new(void);
@@ -74,8 +72,7 @@ void pa_rtpoll_install(pa_rtpoll *p);
* cleanly. */
int pa_rtpoll_run(pa_rtpoll *f, pa_bool_t wait);
-void pa_rtpoll_set_timer_absolute(pa_rtpoll *p, const struct timeval *ts);
-void pa_rtpoll_set_timer_periodic(pa_rtpoll *p, pa_usec_t usec);
+void pa_rtpoll_set_timer_absolute(pa_rtpoll *p, pa_usec_t usec);
void pa_rtpoll_set_timer_relative(pa_rtpoll *p, pa_usec_t usec);
void pa_rtpoll_set_timer_disabled(pa_rtpoll *p);
@@ -107,7 +104,8 @@ void pa_rtpoll_item_set_userdata(pa_rtpoll_item *i, void *userdata);
void* pa_rtpoll_item_get_userdata(pa_rtpoll_item *i);
pa_rtpoll_item *pa_rtpoll_item_new_fdsem(pa_rtpoll *p, pa_rtpoll_priority_t prio, pa_fdsem *s);
-pa_rtpoll_item *pa_rtpoll_item_new_asyncmsgq(pa_rtpoll *p, pa_rtpoll_priority_t prio, pa_asyncmsgq *q);
+pa_rtpoll_item *pa_rtpoll_item_new_asyncmsgq_read(pa_rtpoll *p, pa_rtpoll_priority_t prio, pa_asyncmsgq *q);
+pa_rtpoll_item *pa_rtpoll_item_new_asyncmsgq_write(pa_rtpoll *p, pa_rtpoll_priority_t prio, pa_asyncmsgq *q);
/* Requests the loop to exit. Will cause the next iteration of
* pa_rtpoll_run() to return 0 */
diff --git a/src/pulsecore/rtsig.c b/src/pulsecore/rtsig.c
index bfc49c88..4df217c3 100644
--- a/src/pulsecore/rtsig.c
+++ b/src/pulsecore/rtsig.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/rtsig.h b/src/pulsecore/rtsig.h
index 7830d272..e414122d 100644
--- a/src/pulsecore/rtsig.h
+++ b/src/pulsecore/rtsig.h
@@ -1,8 +1,6 @@
#ifndef foopulsertsighfoo
#define foopulsertsighfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/sample-util.c b/src/pulsecore/sample-util.c
index 21771302..b42b79d1 100644
--- a/src/pulsecore/sample-util.c
+++ b/src/pulsecore/sample-util.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -35,35 +33,13 @@
#include <pulsecore/log.h>
#include <pulsecore/macro.h>
+#include <pulsecore/g711.h>
#include "sample-util.h"
#include "endianmacros.h"
#define PA_SILENCE_MAX (PA_PAGE_SIZE*16)
-pa_memblock *pa_silence_memblock_new(pa_mempool *pool, const pa_sample_spec *spec, size_t length) {
- size_t fs;
- pa_assert(pool);
- pa_assert(spec);
-
- if (length <= 0)
- length = pa_bytes_per_second(spec)/20; /* 50 ms */
-
- if (length > PA_SILENCE_MAX)
- length = PA_SILENCE_MAX;
-
- fs = pa_frame_size(spec);
-
- length = (length+fs-1)/fs;
-
- if (length <= 0)
- length = 1;
-
- length *= fs;
-
- return pa_silence_memblock(pa_memblock_new(pool, length), spec);
-}
-
pa_memblock *pa_silence_memblock(pa_memblock* b, const pa_sample_spec *spec) {
void *data;
@@ -73,10 +49,11 @@ pa_memblock *pa_silence_memblock(pa_memblock* b, const pa_sample_spec *spec) {
data = pa_memblock_acquire(b);
pa_silence_memory(data, pa_memblock_get_length(b), spec);
pa_memblock_release(b);
+
return b;
}
-void pa_silence_memchunk(pa_memchunk *c, const pa_sample_spec *spec) {
+pa_memchunk* pa_silence_memchunk(pa_memchunk *c, const pa_sample_spec *spec) {
void *data;
pa_assert(c);
@@ -86,35 +63,90 @@ void pa_silence_memchunk(pa_memchunk *c, const pa_sample_spec *spec) {
data = pa_memblock_acquire(c->memblock);
pa_silence_memory((uint8_t*) data+c->index, c->length, spec);
pa_memblock_release(c->memblock);
-}
-void pa_silence_memory(void *p, size_t length, const pa_sample_spec *spec) {
- uint8_t c = 0;
- pa_assert(p);
- pa_assert(length > 0);
- pa_assert(spec);
+ return c;
+}
- switch (spec->format) {
+static uint8_t silence_byte(pa_sample_format_t format) {
+ switch (format) {
case PA_SAMPLE_U8:
- c = 0x80;
- break;
+ return 0x80;
case PA_SAMPLE_S16LE:
case PA_SAMPLE_S16BE:
- case PA_SAMPLE_FLOAT32:
- case PA_SAMPLE_FLOAT32RE:
- c = 0;
- break;
+ case PA_SAMPLE_S32LE:
+ case PA_SAMPLE_S32BE:
+ case PA_SAMPLE_FLOAT32LE:
+ case PA_SAMPLE_FLOAT32BE:
+ return 0;
case PA_SAMPLE_ALAW:
- c = 0xd5;
- break;
+ return 0xd5;
case PA_SAMPLE_ULAW:
- c = 0xff;
- break;
+ return 0xff;
default:
pa_assert_not_reached();
}
+ return 0;
+}
+
+void* pa_silence_memory(void *p, size_t length, const pa_sample_spec *spec) {
+ pa_assert(p);
+ pa_assert(length > 0);
+ pa_assert(spec);
- memset(p, c, length);
+ memset(p, silence_byte(spec->format), length);
+ return p;
+}
+
+static void calc_linear_integer_stream_volumes(pa_mix_info streams[], unsigned nstreams, const pa_sample_spec *spec) {
+ unsigned k;
+
+ pa_assert(streams);
+ pa_assert(spec);
+
+ for (k = 0; k < nstreams; k++) {
+ unsigned channel;
+
+ for (channel = 0; channel < spec->channels; channel++) {
+ pa_mix_info *m = streams + k;
+ m->linear[channel].i = (int32_t) (pa_sw_volume_to_linear(m->volume.values[channel]) * 0x10000);
+ }
+ }
+}
+
+static void calc_linear_integer_volume(int32_t linear[], const pa_cvolume *volume) {
+ unsigned channel;
+
+ pa_assert(linear);
+ pa_assert(volume);
+
+ for (channel = 0; channel < volume->channels; channel++)
+ linear[channel] = (int32_t) (pa_sw_volume_to_linear(volume->values[channel]) * 0x10000);
+}
+
+static void calc_linear_float_stream_volumes(pa_mix_info streams[], unsigned nstreams, const pa_sample_spec *spec) {
+ unsigned k;
+
+ pa_assert(streams);
+ pa_assert(spec);
+
+ for (k = 0; k < nstreams; k++) {
+ unsigned channel;
+
+ for (channel = 0; channel < spec->channels; channel++) {
+ pa_mix_info *m = streams + k;
+ m->linear[channel].f = pa_sw_volume_to_linear(m->volume.values[channel]);
+ }
+ }
+}
+
+static void calc_linear_float_volume(float linear[], const pa_cvolume *volume) {
+ unsigned channel;
+
+ pa_assert(linear);
+ pa_assert(volume);
+
+ for (channel = 0; channel < volume->channels; channel++)
+ linear[channel] = pa_sw_volume_to_linear(volume->values[channel]);
}
size_t pa_mix(
@@ -124,11 +156,11 @@ size_t pa_mix(
size_t length,
const pa_sample_spec *spec,
const pa_cvolume *volume,
- int mute) {
+ pa_bool_t mute) {
pa_cvolume full_volume;
- size_t d = 0;
unsigned k;
+ size_t d = 0;
pa_assert(streams);
pa_assert(data);
@@ -139,50 +171,49 @@ size_t pa_mix(
volume = pa_cvolume_reset(&full_volume, spec->channels);
for (k = 0; k < nstreams; k++)
- streams[k].internal = pa_memblock_acquire(streams[k].chunk.memblock);
+ streams[k].ptr = (uint8_t*) pa_memblock_acquire(streams[k].chunk.memblock) + streams[k].chunk.index;
switch (spec->format) {
+
case PA_SAMPLE_S16NE:{
unsigned channel = 0;
+ int32_t linear[PA_CHANNELS_MAX];
+
+ calc_linear_integer_stream_volumes(streams, nstreams, spec);
+ calc_linear_integer_volume(linear, volume);
for (d = 0;; d += sizeof(int16_t)) {
int32_t sum = 0;
+ unsigned i;
- if (d >= length)
+ if (PA_UNLIKELY(d >= length))
goto finish;
- if (!mute && volume->values[channel] != PA_VOLUME_MUTED) {
- unsigned i;
-
- for (i = 0; i < nstreams; i++) {
- int32_t v;
- pa_volume_t cvolume = streams[i].volume.values[channel];
-
- if (d >= streams[i].chunk.length)
- goto finish;
+ for (i = 0; i < nstreams; i++) {
+ pa_mix_info *m = streams + i;
+ int32_t v, cv = m->linear[channel].i;
- if (cvolume == PA_VOLUME_MUTED)
- v = 0;
- else {
- v = *((int16_t*) ((uint8_t*) streams[i].internal + streams[i].chunk.index + d));
+ if (PA_UNLIKELY(d >= m->chunk.length))
+ goto finish;
- if (cvolume != PA_VOLUME_NORM)
- v = (int32_t) (v * pa_sw_volume_to_linear(cvolume));
- }
-
- sum += v;
+ if (PA_UNLIKELY(cv <= 0) || PA_UNLIKELY(!!mute) || PA_UNLIKELY(linear[channel] <= 0))
+ v = 0;
+ else {
+ v = *((int16_t*) m->ptr);
+ v = (v * cv) / 0x10000;
}
- if (volume->values[channel] != PA_VOLUME_NORM)
- sum = (int32_t) (sum * pa_sw_volume_to_linear(volume->values[channel]));
-
- sum = CLAMP(sum, -0x8000, 0x7FFF);
+ sum += v;
+ m->ptr = (uint8_t*) m->ptr + sizeof(int16_t);
}
+ sum = PA_CLAMP_UNLIKELY(sum, -0x8000, 0x7FFF);
+ sum = (sum * linear[channel]) / 0x10000;
*((int16_t*) data) = (int16_t) sum;
+
data = (uint8_t*) data + sizeof(int16_t);
- if (++channel >= spec->channels)
+ if (PA_UNLIKELY(++channel >= spec->channels))
channel = 0;
}
@@ -191,45 +222,135 @@ size_t pa_mix(
case PA_SAMPLE_S16RE:{
unsigned channel = 0;
+ int32_t linear[PA_CHANNELS_MAX];
+
+ calc_linear_integer_stream_volumes(streams, nstreams, spec);
+ calc_linear_integer_volume(linear, volume);
for (d = 0;; d += sizeof(int16_t)) {
int32_t sum = 0;
+ unsigned i;
- if (d >= length)
+ if (PA_UNLIKELY(d >= length))
goto finish;
- if (!mute && volume->values[channel] != PA_VOLUME_MUTED) {
- unsigned i;
+ for (i = 0; i < nstreams; i++) {
+ pa_mix_info *m = streams + i;
+ int32_t v, cv = m->linear[channel].i;
+
+ if (PA_UNLIKELY(d >= m->chunk.length))
+ goto finish;
+
+ if (PA_UNLIKELY(cv <= 0) || PA_UNLIKELY(!!mute) || PA_UNLIKELY(linear[channel] <= 0))
+ v = 0;
+ else {
+ v = PA_INT16_SWAP(*((int16_t*) m->ptr));
+ v = (v * cv) / 0x10000;
+ }
+
+ sum += v;
+ m->ptr = (uint8_t*) m->ptr + sizeof(int16_t);
+ }
- for (i = 0; i < nstreams; i++) {
- int32_t v;
- pa_volume_t cvolume = streams[i].volume.values[channel];
+ sum = PA_CLAMP_UNLIKELY(sum, -0x8000, 0x7FFF);
+ sum = (sum * linear[channel]) / 0x10000;
+ *((int16_t*) data) = PA_INT16_SWAP((int16_t) sum);
- if (d >= streams[i].chunk.length)
- goto finish;
+ data = (uint8_t*) data + sizeof(int16_t);
+
+ if (PA_UNLIKELY(++channel >= spec->channels))
+ channel = 0;
+ }
- if (cvolume == PA_VOLUME_MUTED)
- v = 0;
- else {
- v = PA_INT16_SWAP(*((int16_t*) ((uint8_t*) streams[i].internal + streams[i].chunk.index + d)));
+ break;
+ }
- if (cvolume != PA_VOLUME_NORM)
- v = (int32_t) (v * pa_sw_volume_to_linear(cvolume));
- }
+ case PA_SAMPLE_S32NE:{
+ unsigned channel = 0;
+ int32_t linear[PA_CHANNELS_MAX];
- sum += v;
+ calc_linear_integer_stream_volumes(streams, nstreams, spec);
+ calc_linear_integer_volume(linear, volume);
+
+ for (d = 0;; d += sizeof(int32_t)) {
+ int64_t sum = 0;
+ unsigned i;
+
+ if (PA_UNLIKELY(d >= length))
+ goto finish;
+
+ for (i = 0; i < nstreams; i++) {
+ pa_mix_info *m = streams + i;
+ int64_t v;
+ int32_t cv = m->linear[channel].i;
+
+ if (PA_UNLIKELY(d >= m->chunk.length))
+ goto finish;
+
+ if (PA_UNLIKELY(cv <= 0) || PA_UNLIKELY(!!mute) || PA_UNLIKELY(linear[channel] <= 0))
+ v = 0;
+ else {
+ v = *((int32_t*) m->ptr);
+ v = (v * cv) / 0x10000;
}
- if (volume->values[channel] != PA_VOLUME_NORM)
- sum = (int32_t) (sum * pa_sw_volume_to_linear(volume->values[channel]));
+ sum += v;
+ m->ptr = (uint8_t*) m->ptr + sizeof(int32_t);
+ }
+
+ sum = PA_CLAMP_UNLIKELY(sum, -0x80000000LL, 0x7FFFFFFFLL);
+ sum = (sum * linear[channel]) / 0x10000;
+ *((int32_t*) data) = (int32_t) sum;
+
+ data = (uint8_t*) data + sizeof(int32_t);
- sum = CLAMP(sum, -0x8000, 0x7FFF);
+ if (PA_UNLIKELY(++channel >= spec->channels))
+ channel = 0;
+ }
+
+ break;
+ }
+
+ case PA_SAMPLE_S32RE:{
+ unsigned channel = 0;
+ int32_t linear[PA_CHANNELS_MAX];
+
+ calc_linear_integer_stream_volumes(streams, nstreams, spec);
+ calc_linear_integer_volume(linear, volume);
+
+ for (d = 0;; d += sizeof(int32_t)) {
+ int64_t sum = 0;
+ unsigned i;
+
+ if (PA_UNLIKELY(d >= length))
+ goto finish;
+
+ for (i = 0; i < nstreams; i++) {
+ pa_mix_info *m = streams + i;
+ int64_t v;
+ int32_t cv = m->linear[channel].i;
+
+ if (PA_UNLIKELY(d >= m->chunk.length))
+ goto finish;
+
+ if (PA_UNLIKELY(cv <= 0) || PA_UNLIKELY(!!mute) || PA_UNLIKELY(linear[channel] <= 0))
+ v = 0;
+ else {
+ v = PA_INT32_SWAP(*((int32_t*) m->ptr));
+ v = (v * cv) / 0x10000;
+ }
+
+ sum += v;
+ m->ptr = (uint8_t*) m->ptr + sizeof(int32_t);
}
- *((int16_t*) data) = PA_INT16_SWAP((int16_t) sum);
- data = (uint8_t*) data + sizeof(int16_t);
+ sum = PA_CLAMP_UNLIKELY(sum, -0x80000000LL, 0x7FFFFFFFLL);
+ sum = (sum * linear[channel]) / 0x10000;
+ *((int32_t*) data) = PA_INT32_SWAP((int32_t) sum);
- if (++channel >= spec->channels)
+ data = (uint8_t*) data + sizeof(int32_t);
+
+ if (PA_UNLIKELY(++channel >= spec->channels))
channel = 0;
}
@@ -238,45 +359,133 @@ size_t pa_mix(
case PA_SAMPLE_U8: {
unsigned channel = 0;
+ int32_t linear[PA_CHANNELS_MAX];
+
+ calc_linear_integer_stream_volumes(streams, nstreams, spec);
+ calc_linear_integer_volume(linear, volume);
for (d = 0;; d ++) {
int32_t sum = 0;
+ unsigned i;
- if (d >= length)
+ if (PA_UNLIKELY(d >= length))
goto finish;
- if (!mute && volume->values[channel] != PA_VOLUME_MUTED) {
- unsigned i;
+ for (i = 0; i < nstreams; i++) {
+ pa_mix_info *m = streams + i;
+ int32_t v, cv = m->linear[channel].i;
- for (i = 0; i < nstreams; i++) {
- int32_t v;
- pa_volume_t cvolume = streams[i].volume.values[channel];
+ if (PA_UNLIKELY(d >= m->chunk.length))
+ goto finish;
+
+ if (PA_UNLIKELY(cv <= 0) || PA_UNLIKELY(!!mute) || PA_UNLIKELY(linear[channel] <= 0))
+ v = 0;
+ else {
+ v = (int32_t) *((uint8_t*) m->ptr) - 0x80;
+ v = (v * cv) / 0x10000;
+ }
- if (d >= streams[i].chunk.length)
- goto finish;
+ sum += v;
+ m->ptr = (uint8_t*) m->ptr + 1;
+ }
- if (cvolume == PA_VOLUME_MUTED)
- v = 0;
- else {
- v = (int32_t) *((uint8_t*) streams[i].internal + streams[i].chunk.index + d) - 0x80;
+ sum = (sum * linear[channel]) / 0x10000;
+ sum = PA_CLAMP_UNLIKELY(sum, -0x80, 0x7F);
+ *((uint8_t*) data) = (uint8_t) (sum + 0x80);
- if (cvolume != PA_VOLUME_NORM)
- v = (int32_t) (v * pa_sw_volume_to_linear(cvolume));
- }
+ data = (uint8_t*) data + 1;
- sum += v;
+ if (PA_UNLIKELY(++channel >= spec->channels))
+ channel = 0;
+ }
+
+ break;
+ }
+
+ case PA_SAMPLE_ULAW: {
+ unsigned channel = 0;
+ int32_t linear[PA_CHANNELS_MAX];
+
+ calc_linear_integer_stream_volumes(streams, nstreams, spec);
+ calc_linear_integer_volume(linear, volume);
+
+ for (d = 0;; d ++) {
+ int32_t sum = 0;
+ unsigned i;
+
+ if (PA_UNLIKELY(d >= length))
+ goto finish;
+
+ for (i = 0; i < nstreams; i++) {
+ pa_mix_info *m = streams + i;
+ int32_t v, cv = m->linear[channel].i;
+
+ if (PA_UNLIKELY(d >= m->chunk.length))
+ goto finish;
+
+ if (PA_UNLIKELY(cv <= 0) || PA_UNLIKELY(!!mute) || PA_UNLIKELY(linear[channel] <= 0))
+ v = 0;
+ else {
+ v = (int32_t) st_ulaw2linear16(*((uint8_t*) m->ptr));
+ v = (v * cv) / 0x10000;
}
- if (volume->values[channel] != PA_VOLUME_NORM)
- sum = (int32_t) (sum * pa_sw_volume_to_linear(volume->values[channel]));
+ sum += v;
+ m->ptr = (uint8_t*) m->ptr + 1;
+ }
+
+ sum = PA_CLAMP_UNLIKELY(sum, -0x8000, 0x7FFF);
+ sum = (sum * linear[channel]) / 0x10000;
+ *((uint8_t*) data) = (uint8_t) st_14linear2ulaw(sum >> 2);
+
+ data = (uint8_t*) data + 1;
+
+ if (PA_UNLIKELY(++channel >= spec->channels))
+ channel = 0;
+ }
+
+ break;
+ }
+
+ case PA_SAMPLE_ALAW: {
+ unsigned channel = 0;
+ int32_t linear[PA_CHANNELS_MAX];
+
+ calc_linear_integer_stream_volumes(streams, nstreams, spec);
+ calc_linear_integer_volume(linear, volume);
+
+ for (d = 0;; d ++) {
+ int32_t sum = 0;
+ unsigned i;
+
+ if (PA_UNLIKELY(d >= length))
+ goto finish;
+
+ for (i = 0; i < nstreams; i++) {
+ pa_mix_info *m = streams + i;
+ int32_t v, cv = m->linear[channel].i;
- sum = CLAMP(sum, -0x80, 0x7F);
+ if (PA_UNLIKELY(d >= m->chunk.length))
+ goto finish;
+
+ if (PA_UNLIKELY(cv <= 0) || PA_UNLIKELY(!!mute) || PA_UNLIKELY(linear[channel] <= 0))
+ v = 0;
+ else {
+ v = (int32_t) st_alaw2linear16(*((uint8_t*) m->ptr));
+ v = (v * cv) / 0x10000;
+ }
+
+ sum += v;
+ m->ptr = (uint8_t*) m->ptr + 1;
}
- *((uint8_t*) data) = (uint8_t) (sum + 0x80);
+ sum = PA_CLAMP_UNLIKELY(sum, -0x8000, 0x7FFF);
+ sum = (sum * linear[channel]) / 0x10000;
+ *((uint8_t*) data) = (uint8_t) st_13linear2alaw(sum >> 3);
+
data = (uint8_t*) data + 1;
- if (++channel >= spec->channels)
+ if (PA_UNLIKELY(++channel >= spec->channels))
channel = 0;
}
@@ -285,43 +494,88 @@ size_t pa_mix(
case PA_SAMPLE_FLOAT32NE: {
unsigned channel = 0;
+ float linear[PA_CHANNELS_MAX];
+
+ calc_linear_float_stream_volumes(streams, nstreams, spec);
+ calc_linear_float_volume(linear, volume);
for (d = 0;; d += sizeof(float)) {
float sum = 0;
+ unsigned i;
- if (d >= length)
+ if (PA_UNLIKELY(d >= length))
goto finish;
- if (!mute && volume->values[channel] != PA_VOLUME_MUTED) {
- unsigned i;
+ for (i = 0; i < nstreams; i++) {
+ pa_mix_info *m = streams + i;
+ float v, cv = m->linear[channel].f;
+
+ if (PA_UNLIKELY(d >= m->chunk.length))
+ goto finish;
+
+ if (PA_UNLIKELY(cv <= 0) || PA_UNLIKELY(!!mute) || PA_UNLIKELY(linear[channel] <= 0))
+ v = 0;
+ else {
+ v = *((float*) m->ptr);
+ v *= cv;
+ }
+
+ sum += v;
+ m->ptr = (uint8_t*) m->ptr + sizeof(float);
+ }
+
+ sum *= linear[channel];
+ *((float*) data) = sum;
- for (i = 0; i < nstreams; i++) {
- float v;
- pa_volume_t cvolume = streams[i].volume.values[channel];
+ data = (uint8_t*) data + sizeof(float);
+
+ if (PA_UNLIKELY(++channel >= spec->channels))
+ channel = 0;
+ }
- if (d >= streams[i].chunk.length)
- goto finish;
+ break;
+ }
- if (cvolume == PA_VOLUME_MUTED)
- v = 0;
- else {
- v = *((float*) ((uint8_t*) streams[i].internal + streams[i].chunk.index + d));
+ case PA_SAMPLE_FLOAT32RE: {
+ unsigned channel = 0;
+ float linear[PA_CHANNELS_MAX];
- if (cvolume != PA_VOLUME_NORM)
- v *= pa_sw_volume_to_linear(cvolume);
- }
+ calc_linear_float_stream_volumes(streams, nstreams, spec);
+ calc_linear_float_volume(linear, volume);
- sum += v;
+ for (d = 0;; d += sizeof(float)) {
+ float sum = 0;
+ unsigned i;
+
+ if (PA_UNLIKELY(d >= length))
+ goto finish;
+
+ for (i = 0; i < nstreams; i++) {
+ pa_mix_info *m = streams + i;
+ float v, cv = m->linear[channel].f;
+
+ if (PA_UNLIKELY(d >= m->chunk.length))
+ goto finish;
+
+ if (PA_UNLIKELY(cv <= 0) || PA_UNLIKELY(!!mute) || PA_UNLIKELY(linear[channel] <= 0))
+ v = 0;
+ else {
+ uint32_t z = *(uint32_t*) m->ptr;
+ z = PA_UINT32_SWAP(z);
+ v = *((float*) &z);
+ v *= cv;
}
- if (volume->values[channel] != PA_VOLUME_NORM)
- sum *= pa_sw_volume_to_linear(volume->values[channel]);
+ sum += v;
+ m->ptr = (uint8_t*) m->ptr + sizeof(float);
}
- *((float*) data) = sum;
+ sum *= linear[channel];
+ *((uint32_t*) data) = PA_UINT32_SWAP(*(uint32_t*) &sum);
+
data = (uint8_t*) data + sizeof(float);
- if (++channel >= spec->channels)
+ if (PA_UNLIKELY(++channel >= spec->channels))
channel = 0;
}
@@ -330,7 +584,7 @@ size_t pa_mix(
default:
pa_log_error("ERROR: Unable to mix audio data of format %s.", pa_sample_format_to_string(spec->format));
- abort();
+ pa_assert_not_reached();
}
finish:
@@ -354,6 +608,9 @@ void pa_volume_memchunk(
pa_assert(c->length % pa_frame_size(spec) == 0);
pa_assert(volume);
+ if (pa_memblock_is_silence(c->memblock))
+ return;
+
if (pa_cvolume_channels_equal_to(volume, PA_VOLUME_NORM))
return;
@@ -362,7 +619,7 @@ void pa_volume_memchunk(
return;
}
- ptr = pa_memblock_acquire(c->memblock);
+ ptr = (uint8_t*) pa_memblock_acquire(c->memblock) + c->index;
switch (spec->format) {
@@ -372,18 +629,17 @@ void pa_volume_memchunk(
unsigned channel;
int32_t linear[PA_CHANNELS_MAX];
- for (channel = 0; channel < spec->channels; channel++)
- linear[channel] = (int32_t) (pa_sw_volume_to_linear(volume->values[channel]) * 0x10000);
+ calc_linear_integer_volume(linear, volume);
- for (channel = 0, d = (int16_t*) ((uint8_t*) ptr + c->index), n = c->length/sizeof(int16_t); n > 0; d++, n--) {
+ for (channel = 0, d = ptr, n = c->length/sizeof(int16_t); n > 0; d++, n--) {
int32_t t;
t = (int32_t)(*d);
t = (t * linear[channel]) / 0x10000;
- t = CLAMP(t, -0x8000, 0x7FFF);
+ t = PA_CLAMP_UNLIKELY(t, -0x8000, 0x7FFF);
*d = (int16_t) t;
- if (++channel >= spec->channels)
+ if (PA_UNLIKELY(++channel >= spec->channels))
channel = 0;
}
break;
@@ -395,18 +651,62 @@ void pa_volume_memchunk(
unsigned channel;
int32_t linear[PA_CHANNELS_MAX];
- for (channel = 0; channel < spec->channels; channel++)
- linear[channel] = (int32_t) (pa_sw_volume_to_linear(volume->values[channel]) * 0x10000);
+ calc_linear_integer_volume(linear, volume);
- for (channel = 0, d = (int16_t*) ((uint8_t*) ptr + c->index), n = c->length/sizeof(int16_t); n > 0; d++, n--) {
+ for (channel = 0, d = ptr, n = c->length/sizeof(int16_t); n > 0; d++, n--) {
int32_t t;
t = (int32_t)(PA_INT16_SWAP(*d));
t = (t * linear[channel]) / 0x10000;
- t = CLAMP(t, -0x8000, 0x7FFF);
+ t = PA_CLAMP_UNLIKELY(t, -0x8000, 0x7FFF);
*d = PA_INT16_SWAP((int16_t) t);
- if (++channel >= spec->channels)
+ if (PA_UNLIKELY(++channel >= spec->channels))
+ channel = 0;
+ }
+
+ break;
+ }
+
+ case PA_SAMPLE_S32NE: {
+ int32_t *d;
+ size_t n;
+ unsigned channel;
+ int32_t linear[PA_CHANNELS_MAX];
+
+ calc_linear_integer_volume(linear, volume);
+
+ for (channel = 0, d = ptr, n = c->length/sizeof(int32_t); n > 0; d++, n--) {
+ int64_t t;
+
+ t = (int64_t)(*d);
+ t = (t * linear[channel]) / 0x10000;
+ t = PA_CLAMP_UNLIKELY(t, -0x80000000LL, 0x7FFFFFFFLL);
+ *d = (int32_t) t;
+
+ if (PA_UNLIKELY(++channel >= spec->channels))
+ channel = 0;
+ }
+ break;
+ }
+
+ case PA_SAMPLE_S32RE: {
+ int32_t *d;
+ size_t n;
+ unsigned channel;
+ int32_t linear[PA_CHANNELS_MAX];
+
+ calc_linear_integer_volume(linear, volume);
+
+ for (channel = 0, d = ptr, n = c->length/sizeof(int32_t); n > 0; d++, n--) {
+ int64_t t;
+
+ t = (int64_t)(PA_INT32_SWAP(*d));
+ t = (t * linear[channel]) / 0x10000;
+ t = PA_CLAMP_UNLIKELY(t, -0x80000000LL, 0x7FFFFFFFLL);
+ *d = PA_INT32_SWAP((int32_t) t);
+
+ if (PA_UNLIKELY(++channel >= spec->channels))
channel = 0;
}
@@ -419,18 +719,61 @@ void pa_volume_memchunk(
unsigned channel;
int32_t linear[PA_CHANNELS_MAX];
- for (channel = 0; channel < spec->channels; channel++)
- linear[channel] = (int32_t) (pa_sw_volume_to_linear(volume->values[channel]) * 0x10000);
+ calc_linear_integer_volume(linear, volume);
- for (channel = 0, d = (uint8_t*) ptr + c->index, n = c->length; n > 0; d++, n--) {
+ for (channel = 0, d = ptr, n = c->length; n > 0; d++, n--) {
int32_t t;
t = (int32_t) *d - 0x80;
t = (t * linear[channel]) / 0x10000;
- t = CLAMP(t, -0x80, 0x7F);
+ t = PA_CLAMP_UNLIKELY(t, -0x80, 0x7F);
*d = (uint8_t) (t + 0x80);
- if (++channel >= spec->channels)
+ if (PA_UNLIKELY(++channel >= spec->channels))
+ channel = 0;
+ }
+ break;
+ }
+
+ case PA_SAMPLE_ULAW: {
+ uint8_t *d;
+ size_t n;
+ unsigned channel;
+ int32_t linear[PA_CHANNELS_MAX];
+
+ calc_linear_integer_volume(linear, volume);
+
+ for (channel = 0, d = ptr, n = c->length; n > 0; d++, n--) {
+ int32_t t;
+
+ t = (int32_t) st_ulaw2linear16(*d);
+ t = (t * linear[channel]) / 0x10000;
+ t = PA_CLAMP_UNLIKELY(t, -0x8000, 0x7FFF);
+ *d = (uint8_t) st_14linear2ulaw(t >> 2);
+
+ if (PA_UNLIKELY(++channel >= spec->channels))
+ channel = 0;
+ }
+ break;
+ }
+
+ case PA_SAMPLE_ALAW: {
+ uint8_t *d;
+ size_t n;
+ unsigned channel;
+ int32_t linear[PA_CHANNELS_MAX];
+
+ calc_linear_integer_volume(linear, volume);
+
+ for (channel = 0, d = ptr, n = c->length; n > 0; d++, n--) {
+ int32_t t;
+
+ t = (int32_t) st_alaw2linear16(*d);
+ t = (t * linear[channel]) / 0x10000;
+ t = PA_CLAMP_UNLIKELY(t, -0x8000, 0x7FFF);
+ *d = (uint8_t) st_13linear2alaw(t >> 3);
+
+ if (PA_UNLIKELY(++channel >= spec->channels))
channel = 0;
}
break;
@@ -442,14 +785,14 @@ void pa_volume_memchunk(
unsigned n;
unsigned channel;
- d = (float*) ((uint8_t*) ptr + c->index);
+ d = ptr;
skip = spec->channels * sizeof(float);
n = c->length/sizeof(float)/spec->channels;
- for (channel = 0; channel < spec->channels ; channel ++) {
+ for (channel = 0; channel < spec->channels; channel ++) {
float v, *t;
- if (volume->values[channel] == PA_VOLUME_NORM)
+ if (PA_UNLIKELY(volume->values[channel] == PA_VOLUME_NORM))
continue;
v = (float) pa_sw_volume_to_linear(volume->values[channel]);
@@ -459,6 +802,32 @@ void pa_volume_memchunk(
break;
}
+ case PA_SAMPLE_FLOAT32RE: {
+ uint32_t *d;
+ size_t n;
+ unsigned channel;
+ float linear[PA_CHANNELS_MAX];
+
+ calc_linear_float_volume(linear, volume);
+
+ for (channel = 0, d = ptr, n = c->length/sizeof(float); n > 0; d++, n--) {
+ float t;
+ uint32_t z;
+
+ z = PA_UINT32_SWAP(*d);
+ t = *(float*) &z;
+ t *= linear[channel];
+ z = *(uint32_t*) &t;
+ *d = PA_UINT32_SWAP(z);
+
+ if (PA_UNLIKELY(++channel >= spec->channels))
+ channel = 0;
+ }
+
+ break;
+ }
+
+
default:
pa_log_warn(" Unable to change volume of format %s.", pa_sample_format_to_string(spec->format));
/* If we cannot change the volume, we just don't do it */
@@ -542,3 +911,117 @@ void pa_deinterleave(const void *src, void *dst[], unsigned channels, size_t ss,
}
}
}
+
+static pa_memblock *silence_memblock_new(pa_mempool *pool, uint8_t c) {
+ pa_memblock *b;
+ size_t length;
+ void *data;
+
+ pa_assert(pool);
+
+ length = PA_MIN(pa_mempool_block_size_max(pool), PA_SILENCE_MAX);
+
+ b = pa_memblock_new(pool, length);
+
+ data = pa_memblock_acquire(b);
+ memset(data, c, length);
+ pa_memblock_release(b);
+
+ pa_memblock_set_is_silence(b, TRUE);
+
+ return b;
+}
+
+void pa_silence_cache_init(pa_silence_cache *cache) {
+ pa_assert(cache);
+
+ memset(cache, 0, sizeof(pa_silence_cache));
+}
+
+void pa_silence_cache_done(pa_silence_cache *cache) {
+ pa_sample_format_t f;
+ pa_assert(cache);
+
+ for (f = 0; f < PA_SAMPLE_MAX; f++)
+ if (cache->blocks[f])
+ pa_memblock_unref(cache->blocks[f]);
+
+ memset(cache, 0, sizeof(pa_silence_cache));
+}
+
+pa_memchunk* pa_silence_memchunk_get(pa_silence_cache *cache, pa_mempool *pool, pa_memchunk* ret, const pa_sample_spec *spec, size_t length) {
+ pa_memblock *b;
+ size_t l;
+
+ pa_assert(cache);
+ pa_assert(pa_sample_spec_valid(spec));
+
+ if (!(b = cache->blocks[spec->format]))
+
+ switch (spec->format) {
+ case PA_SAMPLE_U8:
+ cache->blocks[PA_SAMPLE_U8] = b = silence_memblock_new(pool, 0x80);
+ break;
+ case PA_SAMPLE_S16LE:
+ case PA_SAMPLE_S16BE:
+ case PA_SAMPLE_S32LE:
+ case PA_SAMPLE_S32BE:
+ case PA_SAMPLE_FLOAT32LE:
+ case PA_SAMPLE_FLOAT32BE:
+ cache->blocks[PA_SAMPLE_S16LE] = b = silence_memblock_new(pool, 0);
+ cache->blocks[PA_SAMPLE_S16BE] = pa_memblock_ref(b);
+ cache->blocks[PA_SAMPLE_S32LE] = pa_memblock_ref(b);
+ cache->blocks[PA_SAMPLE_S32BE] = pa_memblock_ref(b);
+ cache->blocks[PA_SAMPLE_FLOAT32LE] = pa_memblock_ref(b);
+ cache->blocks[PA_SAMPLE_FLOAT32BE] = pa_memblock_ref(b);
+ break;
+ case PA_SAMPLE_ALAW:
+ cache->blocks[PA_SAMPLE_ALAW] = b = silence_memblock_new(pool, 0xd5);
+ break;
+ case PA_SAMPLE_ULAW:
+ cache->blocks[PA_SAMPLE_ULAW] = b = silence_memblock_new(pool, 0xff);
+ break;
+ default:
+ pa_assert_not_reached();
+ }
+
+ pa_assert(b);
+
+ ret->memblock = pa_memblock_ref(b);
+
+ l = pa_memblock_get_length(b);
+ if (length > l || length == 0)
+ length = l;
+
+ ret->length = pa_frame_align(length, spec);
+ ret->index = 0;
+
+ return ret;
+}
+
+void pa_sample_clamp(pa_sample_format_t format, void *dst, size_t dstr, const void *src, size_t sstr, unsigned n) {
+ const float *s;
+ float *d;
+
+ s = src; d = dst;
+
+ if (format == PA_SAMPLE_FLOAT32NE) {
+
+ float minus_one = -1.0, plus_one = 1.0;
+ oil_clip_f32(d, dstr, s, sstr, n, &minus_one, &plus_one);
+
+ } else {
+ pa_assert(format == PA_SAMPLE_FLOAT32RE);
+
+ for (; n > 0; n--) {
+ float f;
+
+ f = PA_FLOAT32_SWAP(*s);
+ f = PA_CLAMP_UNLIKELY(f, -1.0, 1.0);
+ *d = PA_FLOAT32_SWAP(f);
+
+ s = (const float*) ((const uint8_t*) s + sstr);
+ d = (float*) ((uint8_t*) d + dstr);
+ }
+ }
+}
diff --git a/src/pulsecore/sample-util.h b/src/pulsecore/sample-util.h
index 0a39d5ca..cef70750 100644
--- a/src/pulsecore/sample-util.h
+++ b/src/pulsecore/sample-util.h
@@ -1,8 +1,6 @@
#ifndef foosampleutilhfoo
#define foosampleutilhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -30,16 +28,31 @@
#include <pulsecore/memblock.h>
#include <pulsecore/memchunk.h>
-pa_memblock *pa_silence_memblock(pa_memblock* b, const pa_sample_spec *spec);
-pa_memblock *pa_silence_memblock_new(pa_mempool *pool, const pa_sample_spec *spec, size_t length);
-void pa_silence_memchunk(pa_memchunk *c, const pa_sample_spec *spec);
-void pa_silence_memory(void *p, size_t length, const pa_sample_spec *spec);
+typedef struct pa_silence_cache {
+ pa_memblock* blocks[PA_SAMPLE_MAX];
+} pa_silence_cache;
+
+void pa_silence_cache_init(pa_silence_cache *cache);
+void pa_silence_cache_done(pa_silence_cache *cache);
+
+void *pa_silence_memory(void *p, size_t length, const pa_sample_spec *spec);
+pa_memchunk* pa_silence_memchunk(pa_memchunk *c, const pa_sample_spec *spec);
+pa_memblock* pa_silence_memblock(pa_memblock *b, const pa_sample_spec *spec);
+
+pa_memchunk* pa_silence_memchunk_get(pa_silence_cache *cache, pa_mempool *pool, pa_memchunk* ret, const pa_sample_spec *spec, size_t length);
typedef struct pa_mix_info {
pa_memchunk chunk;
pa_cvolume volume;
void *userdata;
- void *internal; /* Used internally by pa_mix(), should not be initialised when calling pa_mix() */
+
+ /* The following fields are used internally by pa_mix(), should
+ * not be initialised by the caller of pa_mix(). */
+ void *ptr;
+ union {
+ int32_t i;
+ float f;
+ } linear[PA_CHANNELS_MAX];
} pa_mix_info;
size_t pa_mix(
@@ -49,7 +62,7 @@ size_t pa_mix(
size_t length,
const pa_sample_spec *spec,
const pa_cvolume *volume,
- int mute);
+ pa_bool_t mute);
void pa_volume_memchunk(
pa_memchunk*c,
@@ -63,4 +76,6 @@ int pa_frame_aligned(size_t l, const pa_sample_spec *ss) PA_GCC_PURE;
void pa_interleave(const void *src[], unsigned channels, void *dst, size_t ss, unsigned n);
void pa_deinterleave(const void *src, void *dst[], unsigned channels, size_t ss, unsigned n);
+void pa_sample_clamp(pa_sample_format_t format, void *dst, size_t dstr, const void *src, size_t sstr, unsigned n);
+
#endif
diff --git a/src/pulsecore/sconv-s16be.c b/src/pulsecore/sconv-s16be.c
index f74d0282..e7e1449a 100644
--- a/src/pulsecore/sconv-s16be.c
+++ b/src/pulsecore/sconv-s16be.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -30,12 +28,27 @@
#define INT16_FROM PA_INT16_FROM_BE
#define INT16_TO PA_INT16_TO_BE
+#define INT32_FROM PA_INT32_FROM_BE
+#define INT32_TO PA_INT32_TO_BE
+
#define pa_sconv_s16le_to_float32ne pa_sconv_s16be_to_float32ne
#define pa_sconv_s16le_from_float32ne pa_sconv_s16be_from_float32ne
#define pa_sconv_s16le_to_float32re pa_sconv_s16be_to_float32re
#define pa_sconv_s16le_from_float32re pa_sconv_s16be_from_float32re
+#define pa_sconv_s32le_to_float32ne pa_sconv_s32be_to_float32ne
+#define pa_sconv_s32le_from_float32ne pa_sconv_s32be_from_float32ne
+
+#define pa_sconv_s32le_to_float32re pa_sconv_s32be_to_float32re
+#define pa_sconv_s32le_from_float32re pa_sconv_s32be_from_float32re
+
+#define pa_sconv_s32le_to_s16ne pa_sconv_s32be_to_s16ne
+#define pa_sconv_s32le_from_s16ne pa_sconv_s32be_from_s16ne
+
+#define pa_sconv_s32le_to_s16re pa_sconv_s32be_to_s16re
+#define pa_sconv_s32le_from_s16re pa_sconv_s32be_from_s16re
+
#ifdef WORDS_BIGENDIAN
#define SWAP_WORDS 0
#else
diff --git a/src/pulsecore/sconv-s16be.h b/src/pulsecore/sconv-s16be.h
index ad034489..60580815 100644
--- a/src/pulsecore/sconv-s16be.h
+++ b/src/pulsecore/sconv-s16be.h
@@ -1,8 +1,6 @@
#ifndef foosconv_s16befoo
#define foosconv_s16befoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -31,11 +29,31 @@ void pa_sconv_s16be_from_float32ne(unsigned n, const float *a, int16_t *b);
void pa_sconv_s16be_to_float32re(unsigned n, const int16_t *a, float *b);
void pa_sconv_s16be_from_float32re(unsigned n, const float *a, int16_t *b);
+void pa_sconv_s32be_to_float32ne(unsigned n, const int32_t *a, float *b);
+void pa_sconv_s32be_from_float32ne(unsigned n, const float *a, int32_t *b);
+void pa_sconv_s32be_to_float32re(unsigned n, const int32_t *a, float *b);
+void pa_sconv_s32be_from_float32re(unsigned n, const float *a, int32_t *b);
+
+void pa_sconv_s32be_to_s16ne(unsigned n, const int32_t *a, int16_t *b);
+void pa_sconv_s32be_from_s16ne(unsigned n, const int16_t *a, int32_t *b);
+void pa_sconv_s32be_to_s16re(unsigned n, const int32_t *a, int16_t *b);
+void pa_sconv_s32be_from_s16re(unsigned n, const int16_t *a, int32_t *b);
+
#ifdef WORDS_BIGENDIAN
#define pa_sconv_float32be_to_s16ne pa_sconv_s16be_from_float32ne
#define pa_sconv_float32be_from_s16ne pa_sconv_s16be_to_float32ne
#define pa_sconv_float32le_to_s16ne pa_sconv_s16be_from_float32re
#define pa_sconv_float32le_from_s16ne pa_sconv_s16be_to_float32re
+
+#define pa_sconv_float32be_to_s32ne pa_sconv_s32be_from_float32ne
+#define pa_sconv_float32be_from_s32ne pa_sconv_s32be_to_float32ne
+#define pa_sconv_float32le_to_s32ne pa_sconv_s32be_from_float32re
+#define pa_sconv_float32le_from_s32ne pa_sconv_s32be_to_float32re
+
+#define pa_sconv_s16be_to_s32ne pa_sconv_s32be_from_s16ne
+#define pa_sconv_s16be_from_s32ne pa_sconv_s32be_to_s16ne
+#define pa_sconv_s16le_to_s32ne pa_sconv_s32be_from_s16re
+#define pa_sconv_s16le_from_s32ne pa_sconv_s32be_to_s16re
#endif
#endif
diff --git a/src/pulsecore/sconv-s16le.c b/src/pulsecore/sconv-s16le.c
index 6925052c..41670f27 100644
--- a/src/pulsecore/sconv-s16le.c
+++ b/src/pulsecore/sconv-s16le.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -25,7 +23,10 @@
#include <config.h>
#endif
+/* Despite the name of this file we implement S32 handling here, too. */
+
#include <inttypes.h>
+#include <stdio.h>
#include <liboil/liboilfuncs.h>
@@ -45,6 +46,14 @@
#define INT16_TO PA_INT16_TO_LE
#endif
+#ifndef INT32_FROM
+#define INT32_FROM PA_INT32_FROM_LE
+#endif
+
+#ifndef INT32_TO
+#define INT32_TO PA_INT32_TO_LE
+#endif
+
#ifndef SWAP_WORDS
#ifdef WORDS_BIGENDIAN
#define SWAP_WORDS 1
@@ -72,6 +81,25 @@ void pa_sconv_s16le_to_float32ne(unsigned n, const int16_t *a, float *b) {
#endif
}
+void pa_sconv_s32le_to_float32ne(unsigned n, const int32_t *a, float *b) {
+ pa_assert(a);
+ pa_assert(b);
+
+#if SWAP_WORDS == 1
+
+ for (; n > 0; n--) {
+ int32_t s = *(a++);
+ *(b++) = (float) (((double) INT32_FROM(s))/0x7FFFFFFF);
+ }
+
+#else
+{
+ static const double add = 0, factor = 1.0/0x7FFFFFFF;
+ oil_scaleconv_f32_s32(b, a, n, &add, &factor);
+}
+#endif
+}
+
void pa_sconv_s16le_from_float32ne(unsigned n, const float *a, int16_t *b) {
pa_assert(a);
pa_assert(b);
@@ -82,7 +110,7 @@ void pa_sconv_s16le_from_float32ne(unsigned n, const float *a, int16_t *b) {
int16_t s;
float v = *(a++);
- v = CLAMP(v, -1, 1);
+ v = PA_CLAMP_UNLIKELY(v, -1, 1);
s = (int16_t) (v * 0x7FFF);
*(b++) = INT16_TO(s);
}
@@ -95,6 +123,29 @@ void pa_sconv_s16le_from_float32ne(unsigned n, const float *a, int16_t *b) {
#endif
}
+void pa_sconv_s32le_from_float32ne(unsigned n, const float *a, int32_t *b) {
+ pa_assert(a);
+ pa_assert(b);
+
+#if SWAP_WORDS == 1
+
+ for (; n > 0; n--) {
+ int32_t s;
+ float v = *(a++);
+
+ v = PA_CLAMP_UNLIKELY(v, -1, 1);
+ s = (int32_t) ((double) v * (double) 0x7FFFFFFF);
+ *(b++) = INT32_TO(s);
+ }
+
+#else
+{
+ static const double add = 0, factor = 0x7FFFFFFF;
+ oil_scaleconv_s32_f32(b, a, n, &add, &factor);
+}
+#endif
+}
+
void pa_sconv_s16le_to_float32re(unsigned n, const int16_t *a, float *b) {
pa_assert(a);
pa_assert(b);
@@ -108,6 +159,19 @@ void pa_sconv_s16le_to_float32re(unsigned n, const int16_t *a, float *b) {
}
}
+void pa_sconv_s32le_to_float32re(unsigned n, const int32_t *a, float *b) {
+ pa_assert(a);
+ pa_assert(b);
+
+ for (; n > 0; n--) {
+ int32_t s = *(a++);
+ float k = (float) (((double) INT32_FROM(s))/0x7FFFFFFF);
+ uint32_t *j = (uint32_t*) &k;
+ *j = PA_UINT32_SWAP(*j);
+ *(b++) = k;
+ }
+}
+
void pa_sconv_s16le_from_float32re(unsigned n, const float *a, int16_t *b) {
pa_assert(a);
pa_assert(b);
@@ -117,8 +181,69 @@ void pa_sconv_s16le_from_float32re(unsigned n, const float *a, int16_t *b) {
float v = *(a++);
uint32_t *j = (uint32_t*) &v;
*j = PA_UINT32_SWAP(*j);
- v = CLAMP(v, -1, 1);
+ v = PA_CLAMP_UNLIKELY(v, -1, 1);
s = (int16_t) (v * 0x7FFF);
*(b++) = INT16_TO(s);
}
}
+
+void pa_sconv_s32le_from_float32re(unsigned n, const float *a, int32_t *b) {
+ pa_assert(a);
+ pa_assert(b);
+
+ for (; n > 0; n--) {
+ int32_t s;
+ float v = *(a++);
+ uint32_t *j = (uint32_t*) &v;
+ *j = PA_UINT32_SWAP(*j);
+ v = PA_CLAMP_UNLIKELY(v, -1, 1);
+ s = (int32_t) ((double) v * 0x7FFFFFFF);
+ *(b++) = INT32_TO(s);
+ }
+}
+
+void pa_sconv_s32le_to_s16ne(unsigned n, const int32_t*a, int16_t *b) {
+ pa_assert(a);
+ pa_assert(b);
+
+ for (; n > 0; n--) {
+ *b = (int16_t) (INT32_FROM(*a) >> 16);
+ a++;
+ b++;
+ }
+}
+
+void pa_sconv_s32le_to_s16re(unsigned n, const int32_t*a, int16_t *b) {
+ pa_assert(a);
+ pa_assert(b);
+
+ for (; n > 0; n--) {
+ int16_t s = (int16_t) (INT32_FROM(*a) >> 16);
+ *b = PA_UINT32_SWAP(s);
+ a++;
+ b++;
+ }
+}
+
+void pa_sconv_s32le_from_s16ne(unsigned n, const int16_t *a, int32_t *b) {
+ pa_assert(a);
+ pa_assert(b);
+
+ for (; n > 0; n--) {
+ *b = INT32_TO(((int32_t) *a) << 16);
+ a++;
+ b++;
+ }
+}
+
+void pa_sconv_s32le_from_s16re(unsigned n, const int16_t *a, int32_t *b) {
+ pa_assert(a);
+ pa_assert(b);
+
+ for (; n > 0; n--) {
+ int32_t s = ((int32_t) PA_UINT16_SWAP(*a)) << 16;
+ *b = INT32_TO(s);
+ a++;
+ b++;
+ }
+}
diff --git a/src/pulsecore/sconv-s16le.h b/src/pulsecore/sconv-s16le.h
index 4203315a..8d4c87c3 100644
--- a/src/pulsecore/sconv-s16le.h
+++ b/src/pulsecore/sconv-s16le.h
@@ -1,8 +1,6 @@
#ifndef foosconv_s16lefoo
#define foosconv_s16lefoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -31,11 +29,31 @@ void pa_sconv_s16le_from_float32ne(unsigned n, const float *a, int16_t *b);
void pa_sconv_s16le_to_float32re(unsigned n, const int16_t *a, float *b);
void pa_sconv_s16le_from_float32re(unsigned n, const float *a, int16_t *b);
+void pa_sconv_s32le_to_float32ne(unsigned n, const int32_t *a, float *b);
+void pa_sconv_s32le_from_float32ne(unsigned n, const float *a, int32_t *b);
+void pa_sconv_s32le_to_float32re(unsigned n, const int32_t *a, float *b);
+void pa_sconv_s32le_from_float32re(unsigned n, const float *a, int32_t *b);
+
+void pa_sconv_s32le_to_s16ne(unsigned n, const int32_t *a, int16_t *b);
+void pa_sconv_s32le_from_s16ne(unsigned n, const int16_t *a, int32_t *b);
+void pa_sconv_s32le_to_s16re(unsigned n, const int32_t *a, int16_t *b);
+void pa_sconv_s32le_from_s16re(unsigned n, const int16_t *a, int32_t *b);
+
#ifndef WORDS_BIGENDIAN
#define pa_sconv_float32be_to_s16ne pa_sconv_s16le_from_float32re
#define pa_sconv_float32be_from_s16ne pa_sconv_s16le_to_float32re
#define pa_sconv_float32le_to_s16ne pa_sconv_s16le_from_float32ne
#define pa_sconv_float32le_from_s16ne pa_sconv_s16le_to_float32ne
+
+#define pa_sconv_float32be_to_s32ne pa_sconv_s32le_from_float32re
+#define pa_sconv_float32be_from_s32ne pa_sconv_s32le_to_float32re
+#define pa_sconv_float32le_to_s32ne pa_sconv_s32le_from_float32ne
+#define pa_sconv_float32le_from_s32ne pa_sconv_s32le_to_float32ne
+
+#define pa_sconv_s16be_to_s32ne pa_sconv_s32le_from_s16re
+#define pa_sconv_s16be_from_s32ne pa_sconv_s32le_to_s16re
+#define pa_sconv_s16le_to_s32ne pa_sconv_s32le_from_s16ne
+#define pa_sconv_s16le_from_s32ne pa_sconv_s32le_to_s16ne
#endif
#endif
diff --git a/src/pulsecore/sconv.c b/src/pulsecore/sconv.c
index 7f5da63f..581e4681 100644
--- a/src/pulsecore/sconv.c
+++ b/src/pulsecore/sconv.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -130,7 +128,7 @@ static void ulaw_from_float32ne(unsigned n, const float *a, uint8_t *b) {
for (; n > 0; n--) {
float v = *(a++);
- v = CLAMP(v, -1, 1);
+ v = PA_CLAMP_UNLIKELY(v, -1, 1);
v *= 0x1FFF;
*(b++) = st_14linear2ulaw((int16_t) v);
}
@@ -168,7 +166,7 @@ static void alaw_from_float32ne(unsigned n, const float *a, uint8_t *b) {
for (; n > 0; n--, a++, b++) {
float v = *a;
- v = CLAMP(v, -1, 1);
+ v = PA_CLAMP_UNLIKELY(v, -1, 1);
v *= 0xFFF;
*b = st_13linear2alaw((int16_t) v);
}
@@ -198,6 +196,8 @@ pa_convert_func_t pa_get_convert_to_float32ne_function(pa_sample_format_t f) {
[PA_SAMPLE_ULAW] = (pa_convert_func_t) ulaw_to_float32ne,
[PA_SAMPLE_S16LE] = (pa_convert_func_t) pa_sconv_s16le_to_float32ne,
[PA_SAMPLE_S16BE] = (pa_convert_func_t) pa_sconv_s16be_to_float32ne,
+ [PA_SAMPLE_S32LE] = (pa_convert_func_t) pa_sconv_s32le_to_float32ne,
+ [PA_SAMPLE_S32BE] = (pa_convert_func_t) pa_sconv_s32be_to_float32ne,
[PA_SAMPLE_FLOAT32NE] = (pa_convert_func_t) float32ne_to_float32ne,
[PA_SAMPLE_FLOAT32RE] = (pa_convert_func_t) float32re_to_float32ne,
};
@@ -214,6 +214,8 @@ pa_convert_func_t pa_get_convert_from_float32ne_function(pa_sample_format_t f) {
[PA_SAMPLE_U8] = (pa_convert_func_t) u8_from_float32ne,
[PA_SAMPLE_S16LE] = (pa_convert_func_t) pa_sconv_s16le_from_float32ne,
[PA_SAMPLE_S16BE] = (pa_convert_func_t) pa_sconv_s16be_from_float32ne,
+ [PA_SAMPLE_S32LE] = (pa_convert_func_t) pa_sconv_s32le_from_float32ne,
+ [PA_SAMPLE_S32BE] = (pa_convert_func_t) pa_sconv_s32be_from_float32ne,
[PA_SAMPLE_FLOAT32NE] = (pa_convert_func_t) float32ne_to_float32ne,
[PA_SAMPLE_FLOAT32RE] = (pa_convert_func_t) float32re_to_float32ne,
[PA_SAMPLE_ALAW] = (pa_convert_func_t) alaw_from_float32ne,
@@ -234,6 +236,8 @@ pa_convert_func_t pa_get_convert_to_s16ne_function(pa_sample_format_t f) {
[PA_SAMPLE_S16RE] = (pa_convert_func_t) s16re_to_s16ne,
[PA_SAMPLE_FLOAT32BE] = (pa_convert_func_t) pa_sconv_float32be_to_s16ne,
[PA_SAMPLE_FLOAT32LE] = (pa_convert_func_t) pa_sconv_float32le_to_s16ne,
+ [PA_SAMPLE_S32BE] = (pa_convert_func_t) pa_sconv_s32be_to_s16ne,
+ [PA_SAMPLE_S32LE] = (pa_convert_func_t) pa_sconv_s32le_to_s16ne,
[PA_SAMPLE_ALAW] = (pa_convert_func_t) alaw_to_s16ne,
[PA_SAMPLE_ULAW] = (pa_convert_func_t) ulaw_to_s16ne
};
@@ -252,6 +256,8 @@ pa_convert_func_t pa_get_convert_from_s16ne_function(pa_sample_format_t f) {
[PA_SAMPLE_S16RE] = (pa_convert_func_t) s16re_to_s16ne,
[PA_SAMPLE_FLOAT32BE] = (pa_convert_func_t) pa_sconv_float32be_from_s16ne,
[PA_SAMPLE_FLOAT32LE] = (pa_convert_func_t) pa_sconv_float32le_from_s16ne,
+ [PA_SAMPLE_S32BE] = (pa_convert_func_t) pa_sconv_s32be_from_s16ne,
+ [PA_SAMPLE_S32LE] = (pa_convert_func_t) pa_sconv_s32le_from_s16ne,
[PA_SAMPLE_ALAW] = (pa_convert_func_t) alaw_from_s16ne,
[PA_SAMPLE_ULAW] = (pa_convert_func_t) ulaw_from_s16ne,
};
diff --git a/src/pulsecore/sconv.h b/src/pulsecore/sconv.h
index 901f50a3..59710369 100644
--- a/src/pulsecore/sconv.h
+++ b/src/pulsecore/sconv.h
@@ -1,8 +1,6 @@
#ifndef foosconvhfoo
#define foosconvhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/semaphore-posix.c b/src/pulsecore/semaphore-posix.c
index 750c2afc..7c9f859d 100644
--- a/src/pulsecore/semaphore-posix.c
+++ b/src/pulsecore/semaphore-posix.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/semaphore-win32.c b/src/pulsecore/semaphore-win32.c
index f6576348..41e0b8d4 100644
--- a/src/pulsecore/semaphore-win32.c
+++ b/src/pulsecore/semaphore-win32.c
@@ -1,5 +1,3 @@
-/* $Id: mutex-win32.c 1426 2007-02-13 15:35:19Z ossman $ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/semaphore.h b/src/pulsecore/semaphore.h
index c394e0f2..850ae817 100644
--- a/src/pulsecore/semaphore.h
+++ b/src/pulsecore/semaphore.h
@@ -1,8 +1,6 @@
#ifndef foopulsesemaphorehfoo
#define foopulsesemaphorehfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/shm.c b/src/pulsecore/shm.c
index 02f6a7bd..298bf716 100644
--- a/src/pulsecore/shm.c
+++ b/src/pulsecore/shm.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -42,6 +40,7 @@
#endif
#include <pulse/xmalloc.h>
+#include <pulse/gccmacro.h>
#include <pulsecore/core-error.h>
#include <pulsecore/log.h>
@@ -56,7 +55,7 @@
#define MADV_REMOVE 9
#endif
-#define MAX_SHM_SIZE (PA_ALIGN(1024*1024*20))
+#define MAX_SHM_SIZE (PA_ALIGN(1024*1024*64))
#ifdef __linux__
/* On Linux we know that the shared memory blocks are files in
@@ -69,37 +68,38 @@
#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 */
-struct shm_marker {
+/* We now put this SHM marker at the end of each segment. It's
+ * optional, to not require a reboot when upgrading, though */
+struct shm_marker PA_GCC_PACKED {
pa_atomic_t marker; /* 0xbeefcafe */
pa_atomic_t pid;
- void *_reserverd1;
- void *_reserverd2;
- void *_reserverd3;
- void *_reserverd4;
+ uint64_t *_reserverd1;
+ uint64_t *_reserverd2;
+ uint64_t *_reserverd3;
+ uint64_t *_reserverd4;
};
-
+
static char *segment_name(char *fn, size_t l, unsigned id) {
pa_snprintf(fn, l, "/pulse-shm-%u", id);
return fn;
}
-int pa_shm_create_rw(pa_shm *m, size_t size, int shared, mode_t mode) {
+int pa_shm_create_rw(pa_shm *m, size_t size, pa_bool_t shared, mode_t mode) {
char fn[32];
int fd = -1;
pa_assert(m);
pa_assert(size > 0);
- pa_assert(size < MAX_SHM_SIZE);
+ pa_assert(size <= MAX_SHM_SIZE);
pa_assert(mode >= 0600);
/* Each time we create a new SHM area, let's first drop all stale
* ones */
pa_shm_cleanup();
-
+
/* Round up to make it aligned */
size = PA_ALIGN(size);
-
+
if (!shared) {
m->id = 0;
m->size = size;
@@ -122,12 +122,12 @@ int pa_shm_create_rw(pa_shm *m, size_t size, int shared, mode_t mode) {
m->ptr = pa_xmalloc(m->size);
#endif
- m->do_unlink = 0;
+ m->do_unlink = FALSE;
} else {
#ifdef HAVE_SHM_OPEN
struct shm_marker *marker;
-
+
pa_random(&m->id, sizeof(m->id));
segment_name(fn, sizeof(fn), m->id);
@@ -153,9 +153,9 @@ int pa_shm_create_rw(pa_shm *m, size_t size, int shared, mode_t mode) {
marker = (struct shm_marker*) ((uint8_t*) m->ptr + m->size - PA_ALIGN(sizeof(struct shm_marker)));
pa_atomic_store(&marker->pid, (int) getpid());
pa_atomic_store(&marker->marker, SHM_MARKER);
-
+
pa_assert_se(close(fd) == 0);
- m->do_unlink = 1;
+ m->do_unlink = TRUE;
#else
return -1;
#endif
@@ -185,7 +185,7 @@ void pa_shm_free(pa_shm *m) {
#ifdef MAP_FAILED
pa_assert(m->ptr != MAP_FAILED);
#endif
-
+
if (!m->shared) {
#ifdef MAP_ANONYMOUS
if (munmap(m->ptr, m->size) < 0)
@@ -199,12 +199,12 @@ void pa_shm_free(pa_shm *m) {
#ifdef HAVE_SHM_OPEN
if (munmap(m->ptr, m->size) < 0)
pa_log("munmap() failed: %s", pa_cstrerror(errno));
-
+
if (m->do_unlink) {
char fn[32];
-
+
segment_name(fn, sizeof(fn), m->id);
-
+
if (shm_unlink(fn) < 0)
pa_log(" shm_unlink(%s) failed: %s", fn, pa_cstrerror(errno));
}
@@ -236,7 +236,7 @@ void pa_shm_punch(pa_shm *m, size_t offset, size_t size) {
/* Align this to multiples of the page size */
ptr = (uint8_t*) m->ptr + offset;
o = (uint8_t*) ptr - (uint8_t*) PA_PAGE_ALIGN_PTR(ptr);
-
+
if (o > 0) {
ps = PA_PAGE_SIZE;
ptr = (uint8_t*) ptr + (ps - o);
@@ -282,7 +282,9 @@ int pa_shm_attach_ro(pa_shm *m, unsigned id) {
goto fail;
}
- if (st.st_size <= 0 || st.st_size > MAX_SHM_SIZE+PA_ALIGN(sizeof(struct shm_marker)) || PA_ALIGN(st.st_size) != st.st_size) {
+ if (st.st_size <= 0 ||
+ st.st_size > (off_t) (MAX_SHM_SIZE+PA_ALIGN(sizeof(struct shm_marker))) ||
+ PA_ALIGN((size_t) st.st_size) != (size_t) st.st_size) {
pa_log("Invalid shared memory segment size");
goto fail;
}
@@ -318,6 +320,7 @@ int pa_shm_attach_ro(pa_shm *m, unsigned id) {
int pa_shm_cleanup(void) {
+#ifdef HAVE_SHM_OPEN
#ifdef SHM_PATH
DIR *d;
struct dirent *de;
@@ -333,13 +336,13 @@ int pa_shm_cleanup(void) {
pid_t pid;
char fn[128];
struct shm_marker *m;
-
+
if (strncmp(de->d_name, "pulse-shm-", 10))
continue;
if (pa_atou(de->d_name + 10, &id) < 0)
continue;
-
+
if (pa_shm_attach_ro(&seg, id) < 0)
continue;
@@ -347,14 +350,14 @@ int pa_shm_cleanup(void) {
pa_shm_free(&seg);
continue;
}
-
+
m = (struct shm_marker*) ((uint8_t*) seg.ptr + seg.size - PA_ALIGN(sizeof(struct shm_marker)));
-
+
if (pa_atomic_load(&m->marker) != SHM_MARKER) {
pa_shm_free(&seg);
continue;
}
-
+
if (!(pid = (pid_t) pa_atomic_load(&m->pid))) {
pa_shm_free(&seg);
continue;
@@ -366,16 +369,17 @@ int pa_shm_cleanup(void) {
}
pa_shm_free(&seg);
-
+
/* Ok, the owner of this shms segment is dead, so, let's remove the segment */
segment_name(fn, sizeof(fn), id);
- if (shm_unlink(fn) < 0 && errno != EACCES)
+ if (shm_unlink(fn) < 0 && errno != EACCES && errno != ENOENT)
pa_log_warn("Failed to remove SHM segment %s: %s\n", fn, pa_cstrerror(errno));
}
closedir(d);
-#endif
+#endif /* SHM_PATH */
+#endif /* HAVE_SHM_OPEN */
return 0;
}
diff --git a/src/pulsecore/shm.h b/src/pulsecore/shm.h
index 270591de..c2adbd07 100644
--- a/src/pulsecore/shm.h
+++ b/src/pulsecore/shm.h
@@ -1,8 +1,6 @@
#ifndef foopulseshmhfoo
#define foopulseshmhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -26,15 +24,17 @@
#include <sys/types.h>
+#include <pulsecore/macro.h>
+
typedef struct pa_shm {
unsigned id;
void *ptr;
size_t size;
- int do_unlink;
- int shared;
+ pa_bool_t do_unlink:1;
+ pa_bool_t shared:1;
} pa_shm;
-int pa_shm_create_rw(pa_shm *m, size_t size, int shared, mode_t mode);
+int pa_shm_create_rw(pa_shm *m, size_t size, pa_bool_t shared, mode_t mode);
int pa_shm_attach_ro(pa_shm *m, unsigned id);
void pa_shm_punch(pa_shm *m, size_t offset, size_t size);
diff --git a/src/pulsecore/shmasyncq.c b/src/pulsecore/shmasyncq.c
new file mode 100644
index 00000000..eac56adb
--- /dev/null
+++ b/src/pulsecore/shmasyncq.c
@@ -0,0 +1,220 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 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.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <unistd.h>
+#include <errno.h>
+
+#include <pulsecore/atomic.h>
+#include <pulsecore/log.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/core-util.h>
+#include <pulse/xmalloc.h>
+
+#include "fdsem.h"
+
+/* For debugging purposes we can define _Y to put and extra thread
+ * yield between each operation. */
+
+/* #define PROFILE */
+
+#ifdef PROFILE
+#define _Y pa_thread_yield()
+#else
+#define _Y do { } while(0)
+#endif
+
+
+struct pa_shmasyncq {
+ pa_fdsem *read_fdsem, *write_fdsem;
+ pa_shmasyncq_data *data;
+};
+
+static int is_power_of_two(unsigned size) {
+ return !(size & (size - 1));
+}
+
+static int reduce(pa_shmasyncq *l, int value) {
+ return value & (unsigned) (l->n_elements - 1);
+}
+
+static pa_atomic_t* get_cell(pa_shmasyncq *l, unsigned i) {
+ pa_assert(i < l->data->n_elements);
+
+ return (pa_atomic_t*) ((uint8*t) l->data + PA_ALIGN(sizeof(pa_shmasyncq_data)) + i * (PA_ALIGN(sizeof(pa_atomic_t)) + PA_ALIGN(element_size)))
+}
+
+static void *get_cell_data(pa_atomic_t *a) {
+ return (uint8_t*) a + PA_ALIGN(sizeof(atomic_t));
+}
+
+pa_shmasyncq *pa_shmasyncq_new(unsigned n_elements, size_t element_size, void *data, int fd[2]) {
+ pa_shmasyncq *l;
+
+ pa_assert(n_elements > 0);
+ pa_assert(is_power_of_two(n_elements));
+ pa_assert(element_size > 0);
+ pa_assert(data);
+ pa_assert(fd);
+
+ l = pa_xnew(pa_shmasyncq, 1);
+
+ l->data = data;
+ memset(data, 0, PA_SHMASYNCQ_SIZE(n_elements, element_size));
+
+ l->data->n_elements = n_elements;
+ l->data->element_size = element_size;
+
+ if (!(l->read_fdsem = pa_fdsem_new_shm(&d->read_fdsem_data, &fd[0]))) {
+ pa_xfree(l);
+ return NULL;
+ }
+
+ if (!(l->write_fdsem = pa_fdsem_new(&d->write_fdsem_data, &fd[1]))) {
+ pa_fdsem_free(l->read_fdsem);
+ pa_xfree(l);
+ return NULL;
+ }
+
+ return l;
+}
+
+void pa_shmasyncq_free(pa_shmasyncq *l, pa_free_cb_t free_cb) {
+ pa_assert(l);
+
+ if (free_cb) {
+ void *p;
+
+ while ((p = pa_shmasyncq_pop(l, 0)))
+ free_cb(p);
+ }
+
+ pa_fdsem_free(l->read_fdsem);
+ pa_fdsem_free(l->write_fdsem);
+ pa_xfree(l);
+}
+
+int pa_shmasyncq_push(pa_shmasyncq*l, void *p, int wait) {
+ int idx;
+ pa_atomic_ptr_t *cells;
+
+ pa_assert(l);
+ pa_assert(p);
+
+ cells = PA_SHMASYNCQ_CELLS(l);
+
+ _Y;
+ idx = reduce(l, l->write_idx);
+
+ if (!pa_atomic_ptr_cmpxchg(&cells[idx], NULL, p)) {
+
+ if (!wait)
+ return -1;
+
+/* pa_log("sleeping on push"); */
+
+ do {
+ pa_fdsem_wait(l->read_fdsem);
+ } while (!pa_atomic_ptr_cmpxchg(&cells[idx], NULL, p));
+ }
+
+ _Y;
+ l->write_idx++;
+
+ pa_fdsem_post(l->write_fdsem);
+
+ return 0;
+}
+
+void* pa_shmasyncq_pop(pa_shmasyncq*l, int wait) {
+ int idx;
+ void *ret;
+ pa_atomic_ptr_t *cells;
+
+ pa_assert(l);
+
+ cells = PA_SHMASYNCQ_CELLS(l);
+
+ _Y;
+ idx = reduce(l, l->read_idx);
+
+ if (!(ret = pa_atomic_ptr_load(&cells[idx]))) {
+
+ if (!wait)
+ return NULL;
+
+/* pa_log("sleeping on pop"); */
+
+ do {
+ pa_fdsem_wait(l->write_fdsem);
+ } while (!(ret = pa_atomic_ptr_load(&cells[idx])));
+ }
+
+ pa_assert(ret);
+
+ /* Guaranteed to succeed if we only have a single reader */
+ pa_assert_se(pa_atomic_ptr_cmpxchg(&cells[idx], ret, NULL));
+
+ _Y;
+ l->read_idx++;
+
+ pa_fdsem_post(l->read_fdsem);
+
+ return ret;
+}
+
+int pa_shmasyncq_get_fd(pa_shmasyncq *q) {
+ pa_assert(q);
+
+ return pa_fdsem_get(q->write_fdsem);
+}
+
+int pa_shmasyncq_before_poll(pa_shmasyncq *l) {
+ int idx;
+ pa_atomic_ptr_t *cells;
+
+ pa_assert(l);
+
+ cells = PA_SHMASYNCQ_CELLS(l);
+
+ _Y;
+ idx = reduce(l, l->read_idx);
+
+ for (;;) {
+ if (pa_atomic_ptr_load(&cells[idx]))
+ return -1;
+
+ if (pa_fdsem_before_poll(l->write_fdsem) >= 0)
+ return 0;
+ }
+
+ return 0;
+}
+
+void pa_shmasyncq_after_poll(pa_shmasyncq *l) {
+ pa_assert(l);
+
+ pa_fdsem_after_poll(l->write_fdsem);
+}
diff --git a/src/pulsecore/shmasyncq.h b/src/pulsecore/shmasyncq.h
new file mode 100644
index 00000000..9845c391
--- /dev/null
+++ b/src/pulsecore/shmasyncq.h
@@ -0,0 +1,60 @@
+#ifndef foopulseshmasyncqhfoo
+#define foopulseshmasyncqhfoo
+
+/***
+ 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 <sys/types.h>
+
+#include <pulse/def.h>
+#include <pulsecore/macro.h>
+
+/* Similar to pa_asyncq, but stores data in a shared memory segment */
+
+
+struct pa_shmasyncq_data {
+ unsigned n_elements;
+ size_t element_size;
+ unsigned read_idx;
+ unsigned write_idx;
+ pa_fdsem_data read_fdsem_data, write_fdsem_data;
+};
+
+#define PA_SHMASYNCQ_DEFAULT_N_ELEMENTS 128
+#define PA_SHMASYNCQ_SIZE(n_elements, element_size) (PA_ALIGN(sizeof(pa_shmasyncq_data)) + (((n_elements) * (PA_ALIGN(sizeof(pa_atomic_t)) + PA_ALIGN(element_size)))))
+#define PA_SHMASYNCQ_DEFAULT_SIZE(element_size) PA_SHMASYNCQ_SIZE(PA_SHMASYNCQ_DEFAULT_N_ELEMENTS, element_size)
+
+typedef struct pa_shmasyncq pa_shmasyncq;
+
+pa_shmasyncq *pa_shmasyncq_new(unsigned n_elements, size_t element_size, void *data, int fd[2]);
+void pa_shmasyncq_free(pa_shmasyncq* q, pa_free_cb_t free_cb);
+
+void* pa_shmasyncq_pop_begin(pa_shmasyncq *q, pa_bool_t wait);
+void pa_shmasyncq_pop_commit(pa_shmasyncq *q);
+
+int* pa_shmasyncq_push_begin(pa_shmasyncq *q, pa_bool_t wait);
+void pa_shmasyncq_push_commit(pa_shmasyncq *q);
+
+int pa_shmasyncq_get_fd(pa_shmasyncq *q);
+int pa_shmasyncq_before_poll(pa_shmasyncq *a);
+void pa_shmasyncq_after_poll(pa_shmasyncq *a);
+
+#endif
diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c
index 6f654b61..43e7bb21 100644
--- a/src/pulsecore/sink-input.c
+++ b/src/pulsecore/sink-input.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -38,12 +36,12 @@
#include <pulsecore/log.h>
#include <pulsecore/play-memblockq.h>
#include <pulsecore/namereg.h>
+#include <pulsecore/core-util.h>
#include "sink-input.h"
+#define MEMBLOCKQ_MAXLENGTH (32*1024*1024)
#define CONVERT_BUFFER_LENGTH (PA_PAGE_SIZE)
-#define SILENCE_BUFFER_LENGTH (PA_PAGE_SIZE*12)
-#define MOVE_BUFFER_LENGTH (PA_PAGE_SIZE*256)
static PA_DEFINE_CHECK_TYPE(pa_sink_input, pa_msgobject);
@@ -54,10 +52,18 @@ pa_sink_input_new_data* pa_sink_input_new_data_init(pa_sink_input_new_data *data
memset(data, 0, sizeof(*data));
data->resample_method = PA_RESAMPLER_INVALID;
+ data->proplist = pa_proplist_new();
return data;
}
+void pa_sink_input_new_data_set_sample_spec(pa_sink_input_new_data *data, const pa_sample_spec *spec) {
+ pa_assert(data);
+
+ if ((data->sample_spec_is_set = !!spec))
+ data->sample_spec = *spec;
+}
+
void pa_sink_input_new_data_set_channel_map(pa_sink_input_new_data *data, const pa_channel_map *map) {
pa_assert(data);
@@ -72,20 +78,39 @@ void pa_sink_input_new_data_set_volume(pa_sink_input_new_data *data, const pa_cv
data->volume = *volume;
}
-void pa_sink_input_new_data_set_sample_spec(pa_sink_input_new_data *data, const pa_sample_spec *spec) {
+void pa_sink_input_new_data_set_muted(pa_sink_input_new_data *data, pa_bool_t mute) {
pa_assert(data);
- if ((data->sample_spec_is_set = !!spec))
- data->sample_spec = *spec;
+ data->muted_is_set = TRUE;
+ data->muted = !!mute;
}
-void pa_sink_input_new_data_set_muted(pa_sink_input_new_data *data, pa_bool_t mute) {
+void pa_sink_input_new_data_done(pa_sink_input_new_data *data) {
pa_assert(data);
- data->muted_is_set = TRUE;
- data->muted = !!mute;
+ pa_proplist_free(data->proplist);
}
+/* Called from main context */
+static void reset_callbacks(pa_sink_input *i) {
+ pa_assert(i);
+
+ i->pop = NULL;
+ i->process_rewind = NULL;
+ i->update_max_rewind = NULL;
+ i->update_max_request = NULL;
+ i->update_sink_requested_latency = NULL;
+ i->update_sink_latency_range = NULL;
+ i->attach = NULL;
+ i->detach = NULL;
+ i->suspend = NULL;
+ i->moved = NULL;
+ i->kill = NULL;
+ i->get_latency = NULL;
+ i->state_change = NULL;
+}
+
+/* Called from main context */
pa_sink_input* pa_sink_input_new(
pa_core *core,
pa_sink_input_new_data *data,
@@ -93,7 +118,7 @@ pa_sink_input* pa_sink_input_new(
pa_sink_input *i;
pa_resampler *resampler = NULL;
- char st[PA_SAMPLE_SPEC_SNPRINT_MAX];
+ char st[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
pa_assert(core);
pa_assert(data);
@@ -102,10 +127,9 @@ pa_sink_input* pa_sink_input_new(
return NULL;
pa_return_null_if_fail(!data->driver || pa_utf8_valid(data->driver));
- pa_return_null_if_fail(!data->name || pa_utf8_valid(data->name));
if (!data->sink)
- data->sink = pa_namereg_get(core, NULL, PA_NAMEREG_SINK, 1);
+ data->sink = pa_namereg_get(core, NULL, PA_NAMEREG_SINK, TRUE);
pa_return_null_if_fail(data->sink);
pa_return_null_if_fail(pa_sink_get_state(data->sink) != PA_SINK_UNLINKED);
@@ -120,7 +144,7 @@ pa_sink_input* pa_sink_input_new(
if (data->sink->channel_map.channels == data->sample_spec.channels)
data->channel_map = data->sink->channel_map;
else
- pa_channel_map_init_auto(&data->channel_map, data->sample_spec.channels, PA_CHANNEL_MAP_DEFAULT);
+ pa_return_null_if_fail(pa_channel_map_init_auto(&data->channel_map, data->sample_spec.channels, PA_CHANNEL_MAP_DEFAULT));
}
pa_return_null_if_fail(pa_channel_map_valid(&data->channel_map));
@@ -133,13 +157,34 @@ pa_sink_input* pa_sink_input_new(
pa_return_null_if_fail(data->volume.channels == data->sample_spec.channels);
if (!data->muted_is_set)
- data->muted = 0;
+ data->muted = FALSE;
+
+ if (flags & PA_SINK_INPUT_FIX_FORMAT)
+ data->sample_spec.format = data->sink->sample_spec.format;
+
+ if (flags & PA_SINK_INPUT_FIX_RATE)
+ data->sample_spec.rate = data->sink->sample_spec.rate;
+
+ if (flags & PA_SINK_INPUT_FIX_CHANNELS) {
+ data->sample_spec.channels = data->sink->sample_spec.channels;
+ data->channel_map = data->sink->channel_map;
+ }
+
+ pa_assert(pa_sample_spec_valid(&data->sample_spec));
+ pa_assert(pa_channel_map_valid(&data->channel_map));
+
+ /* Due to the fixing of the sample spec the volume might not match anymore */
+ if (data->volume.channels != data->sample_spec.channels)
+ pa_cvolume_set(&data->volume, data->sample_spec.channels, pa_cvolume_avg(&data->volume));
if (data->resample_method == PA_RESAMPLER_INVALID)
data->resample_method = core->resample_method;
pa_return_null_if_fail(data->resample_method < PA_RESAMPLER_MAX);
+ if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SINK_INPUT_FIXATE], data) < 0)
+ return NULL;
+
if (pa_idxset_size(data->sink->inputs) >= PA_MAX_INPUTS_PER_SINK) {
pa_log_warn("Failed to create sink input: too many inputs per sink.");
return NULL;
@@ -154,7 +199,9 @@ pa_sink_input* pa_sink_input_new(
&data->sample_spec, &data->channel_map,
&data->sink->sample_spec, &data->sink->channel_map,
data->resample_method,
- !!(flags & PA_SINK_INPUT_VARIABLE_RATE)))) {
+ ((flags & PA_SINK_INPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0) |
+ ((flags & PA_SINK_INPUT_NO_REMAP) ? PA_RESAMPLER_NO_REMAP : 0) |
+ (core->disable_remixing || (flags & PA_SINK_INPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0)))) {
pa_log_warn("Unsupported resampling operation.");
return NULL;
}
@@ -169,7 +216,7 @@ pa_sink_input* pa_sink_input_new(
i->core = core;
i->state = PA_SINK_INPUT_INIT;
i->flags = flags;
- i->name = pa_xstrdup(data->name);
+ i->proplist = pa_proplist_copy(data->proplist);
i->driver = pa_xstrdup(data->driver);
i->module = data->module;
i->sink = data->sink;
@@ -192,40 +239,51 @@ pa_sink_input* pa_sink_input_new(
} else
i->sync_next = i->sync_prev = NULL;
- i->peek = NULL;
- i->drop = NULL;
- i->kill = NULL;
- i->get_latency = NULL;
- i->attach = NULL;
- i->detach = NULL;
- i->suspend = NULL;
+ i->direct_outputs = pa_idxset_new(NULL, NULL);
+
+ reset_callbacks(i);
i->userdata = NULL;
i->thread_info.state = i->state;
+ i->thread_info.attached = FALSE;
pa_atomic_store(&i->thread_info.drained, 1);
i->thread_info.sample_spec = i->sample_spec;
- i->thread_info.silence_memblock = NULL;
- i->thread_info.move_silence = 0;
- pa_memchunk_reset(&i->thread_info.resampled_chunk);
i->thread_info.resampler = resampler;
i->thread_info.volume = i->volume;
i->thread_info.muted = i->muted;
- i->thread_info.attached = FALSE;
+ i->thread_info.requested_sink_latency = (pa_usec_t) -1;
+ i->thread_info.rewrite_nbytes = 0;
+ i->thread_info.rewrite_flush = FALSE;
+ i->thread_info.underrun_for = (uint64_t) -1;
+ i->thread_info.playing_for = 0;
+ i->thread_info.direct_outputs = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+
+ i->thread_info.render_memblockq = pa_memblockq_new(
+ 0,
+ MEMBLOCKQ_MAXLENGTH,
+ 0,
+ pa_frame_size(&i->sink->sample_spec),
+ 0,
+ 1,
+ 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_log_info("Created input %u \"%s\" on %s with sample spec %s",
+ pa_log_info("Created input %u \"%s\" on %s with sample spec %s and channel map %s",
i->index,
- i->name,
+ pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_MEDIA_NAME)),
i->sink->name,
- pa_sample_spec_snprint(st, sizeof(st), &i->sample_spec));
+ pa_sample_spec_snprint(st, sizeof(st), &i->sample_spec),
+ pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map));
/* Don't forget to call pa_sink_input_put! */
return i;
}
+/* Called from main context */
static void update_n_corked(pa_sink_input *i, pa_sink_input_state_t state) {
pa_assert(i);
@@ -237,6 +295,7 @@ static void update_n_corked(pa_sink_input *i, pa_sink_input_state_t state) {
pa_sink_update_status(i->sink);
}
+/* Called from main context */
static int sink_input_set_state(pa_sink_input *i, pa_sink_input_state_t state) {
pa_sink_input *ssync;
pa_assert(i);
@@ -247,8 +306,7 @@ static int sink_input_set_state(pa_sink_input *i, pa_sink_input_state_t state) {
if (i->state == state)
return 0;
- if (pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_STATE, PA_UINT_TO_PTR(state), 0, NULL) < 0)
- return -1;
+ pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_STATE, PA_UINT_TO_PTR(state), 0, NULL) == 0);
update_n_corked(i, state);
i->state = state;
@@ -262,14 +320,23 @@ static int sink_input_set_state(pa_sink_input *i, pa_sink_input_state_t state) {
ssync->state = state;
}
- if (state != PA_SINK_INPUT_UNLINKED)
+ if (state != PA_SINK_INPUT_UNLINKED) {
pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED], i);
+ for (ssync = i->sync_prev; ssync; ssync = ssync->sync_prev)
+ pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED], ssync);
+
+ for (ssync = i->sync_next; ssync; ssync = ssync->sync_next)
+ pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED], ssync);
+ }
+
return 0;
}
+/* Called from main context */
void pa_sink_input_unlink(pa_sink_input *i) {
pa_bool_t linked;
+ pa_source_output *o, *p = NULL;
pa_assert(i);
/* See pa_sink_unlink() for a couple of comments how this function
@@ -277,7 +344,7 @@ void pa_sink_input_unlink(pa_sink_input *i) {
pa_sink_input_ref(i);
- linked = PA_SINK_INPUT_LINKED(i->state);
+ linked = PA_SINK_INPUT_IS_LINKED(i->state);
if (linked)
pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_UNLINK], i);
@@ -293,20 +360,20 @@ void pa_sink_input_unlink(pa_sink_input *i) {
if (pa_idxset_remove_by_data(i->sink->inputs, i, NULL))
pa_sink_input_unref(i);
- if (linked) {
- pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_REMOVE_INPUT, i, 0, NULL);
- sink_input_set_state(i, PA_SINK_INPUT_UNLINKED);
- pa_sink_update_status(i->sink);
- } else
- i->state = PA_SINK_INPUT_UNLINKED;
+ while ((o = pa_idxset_first(i->direct_outputs, NULL))) {
+ pa_assert(o != p);
+ pa_source_output_kill(o);
+ p = o;
+ }
- i->peek = NULL;
- i->drop = NULL;
- i->kill = NULL;
- i->get_latency = NULL;
- i->attach = NULL;
- i->detach = NULL;
- i->suspend = NULL;
+ update_n_corked(i, PA_SINK_INPUT_UNLINKED);
+ i->state = PA_SINK_INPUT_UNLINKED;
+
+ if (linked)
+ if (i->sink->asyncmsgq)
+ pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_REMOVE_INPUT, i, 0, NULL) == 0);
+
+ reset_callbacks(i);
if (linked) {
pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_REMOVE, i->index);
@@ -317,329 +384,440 @@ void pa_sink_input_unlink(pa_sink_input *i) {
pa_sink_input_unref(i);
}
+/* Called from main context */
static void sink_input_free(pa_object *o) {
pa_sink_input* i = PA_SINK_INPUT(o);
pa_assert(i);
pa_assert(pa_sink_input_refcnt(i) == 0);
- if (PA_SINK_INPUT_LINKED(i->state))
+ if (PA_SINK_INPUT_IS_LINKED(i->state))
pa_sink_input_unlink(i);
- pa_log_info("Freeing output %u \"%s\"", i->index, i->name);
+ pa_log_info("Freeing input %u \"%s\"", i->index, pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_MEDIA_NAME)));
pa_assert(!i->thread_info.attached);
- if (i->thread_info.resampled_chunk.memblock)
- pa_memblock_unref(i->thread_info.resampled_chunk.memblock);
+ if (i->thread_info.render_memblockq)
+ pa_memblockq_free(i->thread_info.render_memblockq);
if (i->thread_info.resampler)
pa_resampler_free(i->thread_info.resampler);
- if (i->thread_info.silence_memblock)
- pa_memblock_unref(i->thread_info.silence_memblock);
+ if (i->proplist)
+ pa_proplist_free(i->proplist);
+
+ if (i->direct_outputs)
+ pa_idxset_free(i->direct_outputs, NULL, NULL);
+
+ if (i->thread_info.direct_outputs)
+ pa_hashmap_free(i->thread_info.direct_outputs, NULL, NULL);
- pa_xfree(i->name);
pa_xfree(i->driver);
pa_xfree(i);
}
+/* Called from main context */
void pa_sink_input_put(pa_sink_input *i) {
+ pa_sink_input_state_t state;
pa_sink_input_assert_ref(i);
pa_assert(i->state == PA_SINK_INPUT_INIT);
- pa_assert(i->peek);
- pa_assert(i->drop);
- i->thread_info.state = i->state = i->flags & PA_SINK_INPUT_START_CORKED ? PA_SINK_INPUT_CORKED : PA_SINK_INPUT_RUNNING;
+ /* The following fields must be initialized properly */
+ pa_assert(i->pop);
+ pa_assert(i->process_rewind);
+ pa_assert(i->kill);
+
i->thread_info.volume = i->volume;
i->thread_info.muted = i->muted;
- if (i->state == PA_SINK_INPUT_CORKED)
- i->sink->n_corked++;
+ state = i->flags & PA_SINK_INPUT_START_CORKED ? PA_SINK_INPUT_CORKED : PA_SINK_INPUT_RUNNING;
- pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_ADD_INPUT, i, 0, NULL);
- pa_sink_update_status(i->sink);
+ update_n_corked(i, state);
+ i->state = state;
+
+ pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_ADD_INPUT, i, 0, NULL) == 0);
pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW, i->index);
pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_PUT], i);
-
- /* Please note that if you change something here, you have to
- change something in pa_sink_input_move() with the ghost stream
- registration too. */
}
+/* Called from main context */
void pa_sink_input_kill(pa_sink_input*i) {
pa_sink_input_assert_ref(i);
- pa_assert(PA_SINK_INPUT_LINKED(i->state));
+ pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
- if (i->kill)
- i->kill(i);
+ i->kill(i);
}
-pa_usec_t pa_sink_input_get_latency(pa_sink_input *i) {
- pa_usec_t r = 0;
+/* Called from main context */
+pa_usec_t pa_sink_input_get_latency(pa_sink_input *i, pa_usec_t *sink_latency) {
+ pa_usec_t r[2] = { 0, 0 };
pa_sink_input_assert_ref(i);
- pa_assert(PA_SINK_INPUT_LINKED(i->state));
+ pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
- if (pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_GET_LATENCY, &r, 0, NULL) < 0)
- r = 0;
+ pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_GET_LATENCY, r, 0, NULL) == 0);
if (i->get_latency)
- r += i->get_latency(i);
+ r[0] += i->get_latency(i);
+
+ if (sink_latency)
+ *sink_latency = r[1];
- return r;
+ return r[0];
}
/* Called from thread context */
-int pa_sink_input_peek(pa_sink_input *i, size_t length, pa_memchunk *chunk, pa_cvolume *volume) {
- int ret = -1;
- int do_volume_adj_here;
- int volume_is_norm;
- size_t block_size_max;
+int pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink frames */, pa_memchunk *chunk, pa_cvolume *volume) {
+ pa_bool_t do_volume_adj_here;
+ pa_bool_t volume_is_norm;
+ size_t block_size_max_sink, block_size_max_sink_input;
+ size_t ilength;
pa_sink_input_assert_ref(i);
- pa_assert(PA_SINK_INPUT_LINKED(i->thread_info.state));
- pa_assert(pa_frame_aligned(length, &i->sink->sample_spec));
+ pa_assert(PA_SINK_INPUT_IS_LINKED(i->thread_info.state));
+ pa_assert(pa_frame_aligned(slength, &i->sink->sample_spec));
pa_assert(chunk);
pa_assert(volume);
- if (!i->peek || !i->drop || i->thread_info.state == PA_SINK_INPUT_CORKED)
- goto finish;
+/* pa_log_debug("peek"); */
+
+ if (!i->pop)
+ return -1;
+
+ pa_assert(i->thread_info.state == PA_SINK_INPUT_RUNNING ||
+ i->thread_info.state == PA_SINK_INPUT_CORKED ||
+ i->thread_info.state == PA_SINK_INPUT_DRAINED);
+
+ /* If there's still some rewrite request the handle, but the sink
+ didn't do this for us, we do it here. However, since the sink
+ apparently doesn't support rewinding, we pass 0 here. This still
+ allows rewinding through the render buffer. */
+ pa_sink_input_process_rewind(i, 0);
- pa_assert(i->thread_info.state == PA_SINK_INPUT_RUNNING || i->thread_info.state == PA_SINK_INPUT_DRAINED);
+ block_size_max_sink_input = i->thread_info.resampler ?
+ pa_resampler_max_block_size(i->thread_info.resampler) :
+ pa_frame_align(pa_mempool_block_size_max(i->sink->core->mempool), &i->sample_spec);
+
+ block_size_max_sink = pa_frame_align(pa_mempool_block_size_max(i->sink->core->mempool), &i->sink->sample_spec);
/* Default buffer size */
- if (length <= 0)
- length = pa_frame_align(CONVERT_BUFFER_LENGTH, &i->sink->sample_spec);
-
- /* Make sure the buffer fits in the mempool tile */
- block_size_max = pa_mempool_block_size_max(i->sink->core->mempool);
- if (length > block_size_max)
- length = pa_frame_align(block_size_max, &i->sink->sample_spec);
-
- if (i->thread_info.move_silence > 0) {
- size_t l;
-
- /* We have just been moved and shall play some silence for a
- * while until the old sink has drained its playback buffer */
-
- if (!i->thread_info.silence_memblock)
- i->thread_info.silence_memblock = pa_silence_memblock_new(
- i->sink->core->mempool,
- &i->sink->sample_spec,
- pa_frame_align(SILENCE_BUFFER_LENGTH, &i->sink->sample_spec));
-
- chunk->memblock = pa_memblock_ref(i->thread_info.silence_memblock);
- chunk->index = 0;
- l = pa_memblock_get_length(chunk->memblock);
- chunk->length = i->thread_info.move_silence < l ? i->thread_info.move_silence : l;
-
- ret = 0;
- do_volume_adj_here = 1;
- goto finish;
- }
+ if (slength <= 0)
+ slength = pa_frame_align(CONVERT_BUFFER_LENGTH, &i->sink->sample_spec);
- if (!i->thread_info.resampler) {
- do_volume_adj_here = 0; /* FIXME??? */
- ret = i->peek(i, length, chunk);
- goto finish;
- }
+ if (slength > block_size_max_sink)
+ slength = block_size_max_sink;
+
+ if (i->thread_info.resampler) {
+ ilength = pa_resampler_request(i->thread_info.resampler, slength);
+
+ if (ilength <= 0)
+ ilength = pa_frame_align(CONVERT_BUFFER_LENGTH, &i->sample_spec);
+ } else
+ ilength = slength;
+
+ if (ilength > block_size_max_sink_input)
+ ilength = block_size_max_sink_input;
+
+ /* If the channel maps of the sink and this stream differ, we need
+ * to adjust the volume *before* we resample. Otherwise we can do
+ * it after and leave it for the sink code */
do_volume_adj_here = !pa_channel_map_equal(&i->channel_map, &i->sink->channel_map);
volume_is_norm = pa_cvolume_is_norm(&i->thread_info.volume) && !i->thread_info.muted;
- while (!i->thread_info.resampled_chunk.memblock) {
+ while (!pa_memblockq_is_readable(i->thread_info.render_memblockq)) {
pa_memchunk tchunk;
- size_t l, rmbs;
- l = pa_resampler_request(i->thread_info.resampler, length);
+ /* There's nothing in our render queue. We need to fill it up
+ * with data from the implementor. */
- if (l <= 0)
- l = pa_frame_align(CONVERT_BUFFER_LENGTH, &i->sample_spec);
+ if (i->thread_info.state == PA_SINK_INPUT_CORKED ||
+ i->pop(i, ilength, &tchunk) < 0) {
- rmbs = pa_resampler_max_block_size(i->thread_info.resampler);
- if (l > rmbs)
- l = rmbs;
+ /* OK, we're corked or the implementor didn't give us any
+ * data, so let's just hand out silence */
+ pa_atomic_store(&i->thread_info.drained, 1);
+
+ pa_memblockq_seek(i->thread_info.render_memblockq, slength, PA_SEEK_RELATIVE);
+ i->thread_info.playing_for = 0;
+ if (i->thread_info.underrun_for != (uint64_t) -1)
+ i->thread_info.underrun_for += ilength;
+ break;
+ }
- if ((ret = i->peek(i, l, &tchunk)) < 0)
- goto finish;
+ pa_atomic_store(&i->thread_info.drained, 0);
pa_assert(tchunk.length > 0);
+ pa_assert(tchunk.memblock);
+
+ i->thread_info.underrun_for = 0;
+ i->thread_info.playing_for += tchunk.length;
+
+ while (tchunk.length > 0) {
+ pa_memchunk wchunk;
+
+ wchunk = tchunk;
+ pa_memblock_ref(wchunk.memblock);
- if (tchunk.length > l)
- tchunk.length = l;
+ if (wchunk.length > block_size_max_sink_input)
+ wchunk.length = block_size_max_sink_input;
- i->drop(i, tchunk.length);
+ /* It might be necessary to adjust the volume here */
+ if (do_volume_adj_here && !volume_is_norm) {
+ pa_memchunk_make_writable(&wchunk, 0);
+
+ if (i->thread_info.muted)
+ pa_silence_memchunk(&wchunk, &i->thread_info.sample_spec);
+ else
+ pa_volume_memchunk(&wchunk, &i->thread_info.sample_spec, &i->thread_info.volume);
+ }
+
+ if (!i->thread_info.resampler)
+ pa_memblockq_push_align(i->thread_info.render_memblockq, &wchunk);
+ else {
+ pa_memchunk rchunk;
+ pa_resampler_run(i->thread_info.resampler, &wchunk, &rchunk);
+
+/* pa_log_debug("pushing %lu", (unsigned long) rchunk.length); */
+
+ if (rchunk.memblock) {
+ pa_memblockq_push_align(i->thread_info.render_memblockq, &rchunk);
+ pa_memblock_unref(rchunk.memblock);
+ }
+ }
- /* It might be necessary to adjust the volume here */
- if (do_volume_adj_here && !volume_is_norm) {
- pa_memchunk_make_writable(&tchunk, 0);
+ pa_memblock_unref(wchunk.memblock);
- if (i->thread_info.muted)
- pa_silence_memchunk(&tchunk, &i->thread_info.sample_spec);
- else
- pa_volume_memchunk(&tchunk, &i->thread_info.sample_spec, &i->thread_info.volume);
+ tchunk.index += wchunk.length;
+ tchunk.length -= wchunk.length;
}
- pa_resampler_run(i->thread_info.resampler, &tchunk, &i->thread_info.resampled_chunk);
pa_memblock_unref(tchunk.memblock);
}
- pa_assert(i->thread_info.resampled_chunk.memblock);
- pa_assert(i->thread_info.resampled_chunk.length > 0);
+ pa_assert_se(pa_memblockq_peek(i->thread_info.render_memblockq, chunk) >= 0);
- *chunk = i->thread_info.resampled_chunk;
- pa_memblock_ref(i->thread_info.resampled_chunk.memblock);
+ pa_assert(chunk->length > 0);
+ pa_assert(chunk->memblock);
- ret = 0;
+/* pa_log_debug("peeking %lu", (unsigned long) chunk->length); */
-finish:
+ if (chunk->length > block_size_max_sink)
+ chunk->length = block_size_max_sink;
- if (ret >= 0)
- pa_atomic_store(&i->thread_info.drained, 0);
- else if (ret < 0)
- pa_atomic_store(&i->thread_info.drained, 1);
+ /* Let's see if we had to apply the volume adjustment ourselves,
+ * or if this can be done by the sink for us */
- if (ret >= 0) {
- /* Let's see if we had to apply the volume adjustment
- * ourselves, or if this can be done by the sink for us */
-
- if (do_volume_adj_here)
- /* We had different channel maps, so we already did the adjustment */
- pa_cvolume_reset(volume, i->sink->sample_spec.channels);
- else if (i->thread_info.muted)
- /* We've both the same channel map, so let's have the sink do the adjustment for us*/
- pa_cvolume_mute(volume, i->sink->sample_spec.channels);
- else
- *volume = i->thread_info.volume;
- }
+ if (do_volume_adj_here)
+ /* We had different channel maps, so we already did the adjustment */
+ pa_cvolume_reset(volume, i->sink->sample_spec.channels);
+ else if (i->thread_info.muted)
+ /* We've both the same channel map, so let's have the sink do the adjustment for us*/
+ pa_cvolume_mute(volume, i->sink->sample_spec.channels);
+ else
+ *volume = i->thread_info.volume;
- return ret;
+ return 0;
}
/* Called from thread context */
-void pa_sink_input_drop(pa_sink_input *i, size_t length) {
+void pa_sink_input_drop(pa_sink_input *i, size_t nbytes /* in sink sample spec */) {
pa_sink_input_assert_ref(i);
- pa_assert(PA_SINK_INPUT_LINKED(i->thread_info.state));
- pa_assert(pa_frame_aligned(length, &i->sink->sample_spec));
- pa_assert(length > 0);
- if (!i->peek || !i->drop || i->thread_info.state == PA_SINK_INPUT_CORKED)
- return;
+ pa_assert(PA_SINK_INPUT_IS_LINKED(i->thread_info.state));
+ pa_assert(pa_frame_aligned(nbytes, &i->sink->sample_spec));
+ pa_assert(nbytes > 0);
- if (i->thread_info.move_silence > 0) {
+/* pa_log_debug("dropping %lu", (unsigned long) nbytes); */
- if (i->thread_info.move_silence >= length) {
- i->thread_info.move_silence -= length;
- length = 0;
- } else {
- length -= i->thread_info.move_silence;
- i->thread_info.move_silence = 0;
- }
+ /* If there's still some rewrite request the handle, but the sink
+ didn't do this for us, we do it here. However, since the sink
+ apparently doesn't support rewinding, we pass 0 here. This still
+ allows rewinding through the render buffer. */
+ pa_sink_input_process_rewind(i, 0);
- if (i->thread_info.move_silence <= 0) {
- if (i->thread_info.silence_memblock) {
- pa_memblock_unref(i->thread_info.silence_memblock);
- i->thread_info.silence_memblock = NULL;
- }
- }
+ pa_memblockq_drop(i->thread_info.render_memblockq, nbytes);
+}
+
+/* Called from thread context */
+void pa_sink_input_process_rewind(pa_sink_input *i, size_t nbytes /* in sink sample spec */) {
+ size_t lbq;
+ pa_sink_input_assert_ref(i);
+
+ pa_assert(PA_SINK_INPUT_IS_LINKED(i->thread_info.state));
+ pa_assert(pa_frame_aligned(nbytes, &i->sink->sample_spec));
+
+/* pa_log_debug("rewind(%lu, %lu)", (unsigned long) nbytes, (unsigned long) i->thread_info.rewrite_nbytes); */
+
+ lbq = pa_memblockq_get_length(i->thread_info.render_memblockq);
- if (length <= 0)
- return;
+ if (nbytes > 0) {
+ pa_log_debug("Have to rewind %lu bytes on render memblockq.", (unsigned long) nbytes);
+ pa_memblockq_rewind(i->thread_info.render_memblockq, nbytes);
}
- if (i->thread_info.resampled_chunk.memblock) {
- size_t l = length;
+ if (i->thread_info.rewrite_nbytes == (size_t) -1) {
- if (l > i->thread_info.resampled_chunk.length)
- l = i->thread_info.resampled_chunk.length;
+ /* We were asked to drop all buffered data, and rerequest new
+ * data from implementor the next time push() is called */
- i->thread_info.resampled_chunk.index += l;
- i->thread_info.resampled_chunk.length -= l;
+ pa_memblockq_flush(i->thread_info.render_memblockq);
- if (i->thread_info.resampled_chunk.length <= 0) {
- pa_memblock_unref(i->thread_info.resampled_chunk.memblock);
- pa_memchunk_reset(&i->thread_info.resampled_chunk);
- }
+ } else if (i->thread_info.rewrite_nbytes > 0) {
+ size_t max_rewrite, amount;
+
+ /* Calculate how much make sense to rewrite at most */
+ max_rewrite = nbytes + lbq;
+
+ /* Transform into local domain */
+ if (i->thread_info.resampler)
+ max_rewrite = pa_resampler_request(i->thread_info.resampler, max_rewrite);
- length -= l;
+ /* Calculate how much of the rewinded data should actually be rewritten */
+ amount = PA_MIN(i->thread_info.rewrite_nbytes, max_rewrite);
+
+ if (amount > 0) {
+ pa_log_debug("Have to rewind %lu bytes on implementor.", (unsigned long) amount);
+
+ /* Tell the implementor */
+ if (i->process_rewind)
+ i->process_rewind(i, amount);
+
+ /* Convert back to to sink domain */
+ if (i->thread_info.resampler)
+ amount = pa_resampler_result(i->thread_info.resampler, amount);
+
+ if (amount > 0)
+ /* Ok, now update the write pointer */
+ pa_memblockq_seek(i->thread_info.render_memblockq, - ((int64_t) amount), PA_SEEK_RELATIVE);
+
+ if (i->thread_info.rewrite_flush)
+ pa_memblockq_silence(i->thread_info.render_memblockq);
+
+ /* And reset the resampler */
+ if (i->thread_info.resampler)
+ pa_resampler_reset(i->thread_info.resampler);
+ }
}
- if (length > 0) {
+ i->thread_info.rewrite_nbytes = 0;
+ i->thread_info.rewrite_flush = FALSE;
+}
- if (i->thread_info.resampler) {
- /* So, we have a resampler. To avoid discontinuities we
- * have to actually read all data that could be read and
- * pass it through the resampler. */
+/* Called from thread context */
+void pa_sink_input_update_max_rewind(pa_sink_input *i, size_t nbytes /* in the sink's sample spec */) {
+ pa_sink_input_assert_ref(i);
+ pa_assert(PA_SINK_INPUT_IS_LINKED(i->thread_info.state));
+ pa_assert(pa_frame_aligned(nbytes, &i->sink->sample_spec));
- while (length > 0) {
- pa_memchunk chunk;
- pa_cvolume volume;
+ pa_memblockq_set_maxrewind(i->thread_info.render_memblockq, nbytes);
- if (pa_sink_input_peek(i, length, &chunk, &volume) >= 0) {
- size_t l;
+ if (i->update_max_rewind)
+ i->update_max_rewind(i, i->thread_info.resampler ? pa_resampler_request(i->thread_info.resampler, nbytes) : nbytes);
+}
- pa_memblock_unref(chunk.memblock);
+/* Called from thread context */
+void pa_sink_input_update_max_request(pa_sink_input *i, size_t nbytes /* in the sink's sample spec */) {
+ pa_sink_input_assert_ref(i);
+ pa_assert(PA_SINK_INPUT_IS_LINKED(i->thread_info.state));
+ pa_assert(pa_frame_aligned(nbytes, &i->sink->sample_spec));
- l = chunk.length;
- if (l > length)
- l = length;
+ if (i->update_max_request)
+ i->update_max_request(i, i->thread_info.resampler ? pa_resampler_request(i->thread_info.resampler, nbytes) : nbytes);
+}
- pa_sink_input_drop(i, l);
- length -= l;
+/* Called from thread context */
+static pa_usec_t fixup_latency(pa_sink *s, pa_usec_t usec) {
+ pa_sink_assert_ref(s);
- } else {
- size_t l;
+ if (usec == (pa_usec_t) -1)
+ return usec;
- l = pa_resampler_request(i->thread_info.resampler, length);
+ if (s->thread_info.max_latency > 0 && usec > s->thread_info.max_latency)
+ usec = s->thread_info.max_latency;
- /* Hmmm, peeking failed, so let's at least drop
- * the right amount of data */
+ if (s->thread_info.min_latency > 0 && usec < s->thread_info.min_latency)
+ usec = s->thread_info.min_latency;
- if (l > 0)
- if (i->drop)
- i->drop(i, l);
+ return usec;
+}
- break;
- }
- }
+/* 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);
- } else {
+ usec = fixup_latency(i->sink, usec);
+ i->thread_info.requested_sink_latency = usec;
+ pa_sink_invalidate_requested_latency(i->sink);
- /* We have no resampler, hence let's just drop the data */
+ return usec;
+}
- if (i->drop)
- i->drop(i, length);
- }
+/* Called from main context */
+pa_usec_t pa_sink_input_set_requested_latency(pa_sink_input *i, pa_usec_t usec) {
+ pa_sink_input_assert_ref(i);
+
+ if (PA_SINK_INPUT_IS_LINKED(i->state))
+ 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 */
+
+ usec = fixup_latency(i->sink, usec);
+ i->thread_info.requested_sink_latency = usec;
+ i->sink->thread_info.requested_latency_valid = FALSE;
}
+
+ 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))
+ 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;
+}
+
+/* Called from main context */
void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume) {
pa_sink_input_assert_ref(i);
- pa_assert(PA_SINK_INPUT_LINKED(i->state));
+ pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
+ pa_assert(volume);
if (pa_cvolume_equal(&i->volume, volume))
return;
i->volume = *volume;
- pa_asyncmsgq_post(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_VOLUME, pa_xnewdup(struct pa_cvolume, volume, 1), 0, NULL, pa_xfree);
+ pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_VOLUME, &i->volume, 0, NULL) == 0);
pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
}
+/* Called from main context */
const pa_cvolume *pa_sink_input_get_volume(pa_sink_input *i) {
pa_sink_input_assert_ref(i);
- pa_assert(PA_SINK_INPUT_LINKED(i->state));
+ pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
return &i->volume;
}
+/* Called from main context */
void pa_sink_input_set_mute(pa_sink_input *i, pa_bool_t mute) {
pa_assert(i);
pa_sink_input_assert_ref(i);
- pa_assert(PA_SINK_INPUT_LINKED(i->state));
+ pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
if (!i->muted == !mute)
return;
@@ -650,23 +828,26 @@ void pa_sink_input_set_mute(pa_sink_input *i, pa_bool_t mute) {
pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
}
-int pa_sink_input_get_mute(pa_sink_input *i) {
+/* Called from main context */
+pa_bool_t pa_sink_input_get_mute(pa_sink_input *i) {
pa_sink_input_assert_ref(i);
- pa_assert(PA_SINK_INPUT_LINKED(i->state));
+ pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
- return !!i->muted;
+ return i->muted;
}
+/* Called from main context */
void pa_sink_input_cork(pa_sink_input *i, pa_bool_t b) {
pa_sink_input_assert_ref(i);
- pa_assert(PA_SINK_INPUT_LINKED(i->state));
+ pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
sink_input_set_state(i, b ? PA_SINK_INPUT_CORKED : PA_SINK_INPUT_RUNNING);
}
+/* Called from main context */
int pa_sink_input_set_rate(pa_sink_input *i, uint32_t rate) {
pa_sink_input_assert_ref(i);
- pa_assert(PA_SINK_INPUT_LINKED(i->state));
+ pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
pa_return_val_if_fail(i->thread_info.resampler, -1);
if (i->sample_spec.rate == rate)
@@ -680,38 +861,46 @@ int pa_sink_input_set_rate(pa_sink_input *i, uint32_t rate) {
return 0;
}
+/* Called from main context */
void pa_sink_input_set_name(pa_sink_input *i, const char *name) {
+ const char *old;
pa_sink_input_assert_ref(i);
- if (!i->name && !name)
+ if (!name && !pa_proplist_contains(i->proplist, PA_PROP_MEDIA_NAME))
return;
- if (i->name && name && !strcmp(i->name, name))
+ old = pa_proplist_gets(i->proplist, PA_PROP_MEDIA_NAME);
+
+ if (old && name && !strcmp(old, name))
return;
- pa_xfree(i->name);
- i->name = pa_xstrdup(name);
+ if (name)
+ pa_proplist_sets(i->proplist, PA_PROP_MEDIA_NAME, name);
+ else
+ pa_proplist_unset(i->proplist, PA_PROP_MEDIA_NAME);
- if (PA_SINK_INPUT_LINKED(i->state)) {
- pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_NAME_CHANGED], i);
+ if (PA_SINK_INPUT_IS_LINKED(i->state)) {
+ pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_PROPLIST_CHANGED], i);
pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
}
}
+/* Called from main context */
pa_resample_method_t pa_sink_input_get_resample_method(pa_sink_input *i) {
pa_sink_input_assert_ref(i);
return i->resample_method;
}
-int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, int immediately) {
+/* Called from main context */
+int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest) {
pa_resampler *new_resampler;
pa_sink *origin;
- pa_usec_t silence_usec = 0;
- pa_sink_input_move_info info;
+ pa_sink_input_move_hook_data hook_data;
+ pa_source_output *o, *p = NULL;
pa_sink_input_assert_ref(i);
- pa_assert(PA_SINK_INPUT_LINKED(i->state));
+ pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
pa_sink_assert_ref(dest);
origin = i->sink;
@@ -732,6 +921,13 @@ int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, int immediately) {
return -1;
}
+ /* Kill directly connected outputs */
+ while ((o = pa_idxset_first(i->direct_outputs, NULL))) {
+ pa_assert(o != p);
+ pa_source_output_kill(o);
+ p = o;
+ }
+
if (i->thread_info.resampler &&
pa_sample_spec_equal(&origin->sample_spec, &dest->sample_spec) &&
pa_channel_map_equal(&origin->channel_map, &dest->channel_map))
@@ -750,80 +946,20 @@ int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, int immediately) {
&i->sample_spec, &i->channel_map,
&dest->sample_spec, &dest->channel_map,
i->resample_method,
- !!(i->flags & PA_SINK_INPUT_VARIABLE_RATE)))) {
+ ((i->flags & PA_SINK_INPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0) |
+ ((i->flags & PA_SINK_INPUT_NO_REMAP) ? PA_RESAMPLER_NO_REMAP : 0) |
+ (i->core->disable_remixing || (i->flags & PA_SINK_INPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0)))) {
pa_log_warn("Unsupported resampling operation.");
return -1;
}
} else
new_resampler = NULL;
- pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE], i);
+ hook_data.sink_input = i;
+ hook_data.destination = dest;
+ pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE], &hook_data);
- memset(&info, 0, sizeof(info));
- info.sink_input = i;
-
- if (!immediately) {
- pa_usec_t old_latency, new_latency;
-
- /* Let's do a little bit of Voodoo for compensating latency
- * differences. We assume that the accuracy for our
- * estimations is still good enough, even though we do these
- * operations non-atomic. */
-
- old_latency = pa_sink_get_latency(origin);
- new_latency = pa_sink_get_latency(dest);
-
- /* The already resampled data should go to the old sink */
-
- if (old_latency >= new_latency) {
-
- /* The latency of the old sink is larger than the latency
- * of the new sink. Therefore to compensate for the
- * difference we to play silence on the new one for a
- * while */
-
- silence_usec = old_latency - new_latency;
-
- } else {
-
- /* The latency of new sink is larger than the latency of
- * the old sink. Therefore we have to precompute a little
- * and make sure that this is still played on the old
- * sink, until we can play the first sample on the new
- * sink.*/
-
- info.buffer_bytes = pa_usec_to_bytes(new_latency - old_latency, &origin->sample_spec);
- }
-
- /* Okey, let's move it */
-
- if (info.buffer_bytes > 0) {
-
- info.ghost_sink_input = pa_memblockq_sink_input_new(
- origin,
- "Ghost Stream",
- &origin->sample_spec,
- &origin->channel_map,
- NULL,
- NULL);
-
- info.ghost_sink_input->thread_info.state = info.ghost_sink_input->state = PA_SINK_INPUT_RUNNING;
- info.ghost_sink_input->thread_info.volume = info.ghost_sink_input->volume;
- info.ghost_sink_input->thread_info.muted = info.ghost_sink_input->muted;
-
- info.buffer = pa_memblockq_new(0, MOVE_BUFFER_LENGTH, 0, pa_frame_size(&origin->sample_spec), 0, 0, NULL);
- }
- }
-
- pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_REMOVE_INPUT_AND_BUFFER, &info, 0, NULL);
-
- if (info.ghost_sink_input) {
- /* Basically, do what pa_sink_input_put() does ...*/
-
- pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW, info.ghost_sink_input->index);
- pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_PUT], info.ghost_sink_input);
- pa_sink_input_unref(info.ghost_sink_input);
- }
+ pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_START_MOVE, i, 0, NULL) == 0);
pa_idxset_remove_by_data(origin->inputs, i, NULL);
pa_idxset_put(dest->inputs, i, NULL);
@@ -834,42 +970,34 @@ int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, int immediately) {
dest->n_corked++;
}
- /* Replace resampler */
+ /* Replace resampler and render queue */
if (new_resampler != i->thread_info.resampler) {
+
if (i->thread_info.resampler)
pa_resampler_free(i->thread_info.resampler);
i->thread_info.resampler = new_resampler;
- /* if the resampler changed, the silence memblock is
- * probably invalid now, too */
- if (i->thread_info.silence_memblock) {
- pa_memblock_unref(i->thread_info.silence_memblock);
- i->thread_info.silence_memblock = NULL;
- }
- }
-
- /* Dump already resampled data */
- if (i->thread_info.resampled_chunk.memblock) {
- /* Hmm, this data has already been added to the ghost queue, presumably, hence let's sleep a little bit longer */
- silence_usec += pa_bytes_to_usec(i->thread_info.resampled_chunk.length, &origin->sample_spec);
- pa_memblock_unref(i->thread_info.resampled_chunk.memblock);
- pa_memchunk_reset(&i->thread_info.resampled_chunk);
+ pa_memblockq_free(i->thread_info.render_memblockq);
+
+ i->thread_info.render_memblockq = pa_memblockq_new(
+ 0,
+ MEMBLOCKQ_MAXLENGTH,
+ 0,
+ pa_frame_size(&i->sink->sample_spec),
+ 0,
+ 1,
+ 0,
+ &i->sink->silence);
}
- /* Calculate the new sleeping time */
- if (immediately)
- i->thread_info.move_silence = 0;
- else
- i->thread_info.move_silence = pa_usec_to_bytes(
- pa_bytes_to_usec(i->thread_info.move_silence, &origin->sample_spec) +
- silence_usec,
- &dest->sample_spec);
-
- pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_ADD_INPUT, i, 0, NULL);
-
pa_sink_update_status(origin);
pa_sink_update_status(dest);
+ pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_FINISH_MOVE, i, 0, NULL) == 0);
+
+ if (i->moved)
+ i->moved(i);
+
pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_POST], i);
pa_log_debug("Successfully moved sink input %i from %s to %s.", i->index, origin->name, dest->name);
@@ -880,30 +1008,61 @@ int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, int immediately) {
return 0;
}
-/* Called from thread context */
+/* Called from IO thread context */
+void pa_sink_input_set_state_within_thread(pa_sink_input *i, pa_sink_input_state_t state) {
+ pa_sink_input_assert_ref(i);
+
+ if (state == i->thread_info.state)
+ return;
+
+ if ((state == PA_SINK_INPUT_DRAINED || state == PA_SINK_INPUT_RUNNING) &&
+ !(i->thread_info.state == PA_SINK_INPUT_DRAINED || i->thread_info.state != PA_SINK_INPUT_RUNNING))
+ pa_atomic_store(&i->thread_info.drained, 1);
+
+ if (state == PA_SINK_INPUT_CORKED && i->thread_info.state != PA_SINK_INPUT_CORKED) {
+
+ /* This will tell the implementing sink input driver to rewind
+ * so that the unplayed already mixed data is not lost */
+ pa_sink_input_request_rewind(i, 0, TRUE, TRUE);
+
+ } else if (i->thread_info.state == PA_SINK_INPUT_CORKED && state != PA_SINK_INPUT_CORKED) {
+
+ /* OK, we're being uncorked. Make sure we're not rewound when
+ * the hw buffer is remixed and request a remix. */
+ pa_sink_input_request_rewind(i, 0, FALSE, TRUE);
+ }
+
+ if (i->state_change)
+ i->state_change(i, state);
+
+ i->thread_info.state = state;
+}
+
+/* Called from thread context, except when it is not. */
int pa_sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk) {
pa_sink_input *i = PA_SINK_INPUT(o);
-
pa_sink_input_assert_ref(i);
- pa_assert(PA_SINK_INPUT_LINKED(i->thread_info.state));
switch (code) {
+
case PA_SINK_INPUT_MESSAGE_SET_VOLUME:
i->thread_info.volume = *((pa_cvolume*) userdata);
+ pa_sink_input_request_rewind(i, 0, TRUE, FALSE);
return 0;
case PA_SINK_INPUT_MESSAGE_SET_MUTE:
i->thread_info.muted = PA_PTR_TO_UINT(userdata);
+ pa_sink_input_request_rewind(i, 0, TRUE, FALSE);
return 0;
case PA_SINK_INPUT_MESSAGE_GET_LATENCY: {
pa_usec_t *r = userdata;
+ pa_usec_t sink_usec = 0;
- if (i->thread_info.resampled_chunk.memblock)
- *r += pa_bytes_to_usec(i->thread_info.resampled_chunk.length, &i->sink->sample_spec);
+ r[0] += pa_bytes_to_usec(pa_memblockq_get_length(i->thread_info.render_memblockq), &i->sink->sample_spec);
- if (i->thread_info.move_silence)
- *r += pa_bytes_to_usec(i->thread_info.move_silence, &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;
return 0;
}
@@ -918,26 +1077,28 @@ int pa_sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t
case PA_SINK_INPUT_MESSAGE_SET_STATE: {
pa_sink_input *ssync;
- if ((PA_PTR_TO_UINT(userdata) == PA_SINK_INPUT_DRAINED || PA_PTR_TO_UINT(userdata) == PA_SINK_INPUT_RUNNING) &&
- (i->thread_info.state != PA_SINK_INPUT_DRAINED) && (i->thread_info.state != PA_SINK_INPUT_RUNNING))
- pa_atomic_store(&i->thread_info.drained, 1);
+ pa_sink_input_set_state_within_thread(i, PA_PTR_TO_UINT(userdata));
- i->thread_info.state = PA_PTR_TO_UINT(userdata);
+ for (ssync = i->thread_info.sync_prev; ssync; ssync = ssync->thread_info.sync_prev)
+ pa_sink_input_set_state_within_thread(ssync, PA_PTR_TO_UINT(userdata));
- for (ssync = i->thread_info.sync_prev; ssync; ssync = ssync->thread_info.sync_prev) {
- if ((PA_PTR_TO_UINT(userdata) == PA_SINK_INPUT_DRAINED || PA_PTR_TO_UINT(userdata) == PA_SINK_INPUT_RUNNING) &&
- (ssync->thread_info.state != PA_SINK_INPUT_DRAINED) && (ssync->thread_info.state != PA_SINK_INPUT_RUNNING))
- pa_atomic_store(&ssync->thread_info.drained, 1);
- ssync->thread_info.state = PA_PTR_TO_UINT(userdata);
- }
+ for (ssync = i->thread_info.sync_next; ssync; ssync = ssync->thread_info.sync_next)
+ pa_sink_input_set_state_within_thread(ssync, PA_PTR_TO_UINT(userdata));
- for (ssync = i->thread_info.sync_next; ssync; ssync = ssync->thread_info.sync_next) {
- if ((PA_PTR_TO_UINT(userdata) == PA_SINK_INPUT_DRAINED || PA_PTR_TO_UINT(userdata) == PA_SINK_INPUT_RUNNING) &&
- (ssync->thread_info.state != PA_SINK_INPUT_DRAINED) && (ssync->thread_info.state != PA_SINK_INPUT_RUNNING))
- pa_atomic_store(&ssync->thread_info.drained, 1);
- ssync->thread_info.state = PA_PTR_TO_UINT(userdata);
- }
+ return 0;
+ }
+ case PA_SINK_INPUT_MESSAGE_SET_REQUESTED_LATENCY: {
+ pa_usec_t *usec = userdata;
+
+ *usec = pa_sink_input_set_requested_latency_within_thread(i, *usec);
+ return 0;
+ }
+
+ case PA_SINK_INPUT_MESSAGE_GET_REQUESTED_LATENCY: {
+ pa_usec_t *r = userdata;
+
+ *r = i->thread_info.requested_sink_latency;
return 0;
}
}
@@ -945,6 +1106,7 @@ int pa_sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t
return -1;
}
+/* Called from main thread */
pa_sink_input_state_t pa_sink_input_get_state(pa_sink_input *i) {
pa_sink_input_assert_ref(i);
@@ -953,3 +1115,88 @@ pa_sink_input_state_t pa_sink_input_get_state(pa_sink_input *i) {
return i->state;
}
+
+/* Called from IO context */
+pa_bool_t pa_sink_input_safe_to_remove(pa_sink_input *i) {
+ pa_sink_input_assert_ref(i);
+
+ if (PA_SINK_INPUT_IS_LINKED(i->thread_info.state))
+ return pa_memblockq_is_empty(i->thread_info.render_memblockq);
+
+ return TRUE;
+}
+
+/* Called from IO context */
+void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes /* in our sample spec */, pa_bool_t rewrite, pa_bool_t flush) {
+ size_t lbq;
+
+ /* If 'rewrite' is TRUE the sink is rewound as far as requested
+ * and possible and the exact value of this is passed back the
+ * implementor via process_rewind(). If 'flush' is also TRUE all
+ * already rendered data is also dropped.
+ *
+ * If 'rewrite' is FALSE the sink is rewound as far as requested
+ * and possible and the already rendered data is dropped so that
+ * in the next iteration we read new data from the
+ * implementor. This implies 'flush' is TRUE. */
+
+ pa_sink_input_assert_ref(i);
+ pa_assert(i->thread_info.rewrite_nbytes == 0);
+
+ /* We don't take rewind requests while we are corked */
+ if (i->thread_info.state == PA_SINK_INPUT_CORKED)
+ return;
+
+ pa_assert(rewrite || flush);
+
+ /* Calculate how much we can rewind locally without having to
+ * touch the sink */
+ if (rewrite)
+ lbq = pa_memblockq_get_length(i->thread_info.render_memblockq);
+ else
+ lbq = 0;
+
+ /* Check if rewinding for the maximum is requested, and if so, fix up */
+ if (nbytes <= 0) {
+
+ /* Calculate maximum number of bytes that could be rewound in theory */
+ nbytes = i->sink->thread_info.max_rewind + lbq;
+
+ /* Transform from sink domain */
+ if (i->thread_info.resampler)
+ nbytes = pa_resampler_request(i->thread_info.resampler, nbytes);
+ }
+
+ if (rewrite) {
+ /* Make sure to not overwrite over underruns */
+ if (nbytes > i->thread_info.playing_for)
+ nbytes = (size_t) i->thread_info.playing_for;
+
+ i->thread_info.rewrite_nbytes = nbytes;
+ } else
+ i->thread_info.rewrite_nbytes = (size_t) -1;
+
+ i->thread_info.rewrite_flush = flush && i->thread_info.rewrite_nbytes != 0;
+
+ /* Transform to sink domain */
+ if (i->thread_info.resampler)
+ nbytes = pa_resampler_result(i->thread_info.resampler, nbytes);
+
+ if (nbytes > lbq)
+ pa_sink_request_rewind(i->sink, nbytes - lbq);
+}
+
+/* Called from main context */
+pa_memchunk* pa_sink_input_get_silence(pa_sink_input *i, pa_memchunk *ret) {
+ pa_sink_input_assert_ref(i);
+ pa_assert(ret);
+
+ pa_silence_memchunk_get(
+ &i->sink->core->silence_cache,
+ i->sink->core->mempool,
+ ret,
+ &i->sample_spec,
+ i->thread_info.resampler ? pa_resampler_max_block_size(i->thread_info.resampler) : 0);
+
+ return ret;
+}
diff --git a/src/pulsecore/sink-input.h b/src/pulsecore/sink-input.h
index fec431f0..c07a7404 100644
--- a/src/pulsecore/sink-input.h
+++ b/src/pulsecore/sink-input.h
@@ -1,8 +1,6 @@
#ifndef foopulsesinkinputhfoo
#define foopulsesinkinputhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -46,14 +44,19 @@ typedef enum pa_sink_input_state {
PA_SINK_INPUT_UNLINKED /*< The stream is dead */
} pa_sink_input_state_t;
-static inline pa_bool_t PA_SINK_INPUT_LINKED(pa_sink_input_state_t x) {
+static inline pa_bool_t PA_SINK_INPUT_IS_LINKED(pa_sink_input_state_t x) {
return x == PA_SINK_INPUT_DRAINED || x == PA_SINK_INPUT_RUNNING || x == PA_SINK_INPUT_CORKED;
}
typedef enum pa_sink_input_flags {
PA_SINK_INPUT_VARIABLE_RATE = 1,
PA_SINK_INPUT_DONT_MOVE = 2,
- PA_SINK_INPUT_START_CORKED = 4
+ PA_SINK_INPUT_START_CORKED = 4,
+ PA_SINK_INPUT_NO_REMAP = 8,
+ PA_SINK_INPUT_NO_REMIX = 16,
+ PA_SINK_INPUT_FIX_FORMAT = 32,
+ PA_SINK_INPUT_FIX_RATE = 64,
+ PA_SINK_INPUT_FIX_CHANNELS = 128
} pa_sink_input_flags_t;
struct pa_sink_input {
@@ -65,41 +68,69 @@ struct pa_sink_input {
/* Please note that this state should only be read with
* pa_sink_input_get_state(). That function will transparently
* merge the thread_info.drained value in. */
- pa_sink_input_state_t state;
+ pa_sink_input_state_t state;
pa_sink_input_flags_t flags;
- char *name, *driver; /* may be NULL */
+ char *driver; /* may be NULL */
+ pa_proplist *proplist;
+
pa_module *module; /* may be NULL */
pa_client *client; /* may be NULL */
pa_sink *sink;
+ /* A sink input may be connected to multiple source outputs
+ * directly, so that they don't get mixed data of the entire
+ * source. */
+ pa_idxset *direct_outputs;
+
pa_sample_spec sample_spec;
pa_channel_map channel_map;
pa_sink_input *sync_prev, *sync_next;
-
+
pa_cvolume volume;
pa_bool_t muted;
- /* Returns the chunk of audio data (but doesn't drop it
- * yet!). Returns -1 on failure. Called from IO thread context. If
- * data needs to be generated from scratch then please in the
- * specified length. This is an optimization only. If less data is
- * available, it's fine to return a smaller block. If more data is
- * already ready, it is better to return the full block.*/
- int (*peek) (pa_sink_input *i, size_t length, pa_memchunk *chunk);
+ pa_resample_method_t resample_method;
- /* Drops the specified number of bytes, usually called right after
- * peek(), but not necessarily. Called from IO thread context. */
- void (*drop) (pa_sink_input *i, size_t length);
+ /* Returns the chunk of audio data and drops it from the
+ * queue. Returns -1 on failure. Called from IO thread context. If
+ * data needs to be generated from scratch then please in the
+ * specified length request_nbytes. This is an optimization
+ * only. If less data is available, it's fine to return a smaller
+ * block. If more data is already ready, it is better to return
+ * the full block. */
+ int (*pop) (pa_sink_input *i, size_t request_nbytes, pa_memchunk *chunk); /* may NOT be NULL */
+
+ /* Rewind the queue by the specified number of bytes. Called just
+ * before peek() if it is called at all. Only called if the sink
+ * input driver ever plans to call
+ * pa_sink_input_request_rewind(). Called from IO context. */
+ void (*process_rewind) (pa_sink_input *i, size_t nbytes); /* may NOT be NULL */
+
+ /* Called whenever the maximum rewindable size of the sink
+ * 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
+ * changes. Called from IO context. */
+ void (*update_max_request) (pa_sink_input *i, size_t nbytes); /* may be NULL */
+
+ /* Called whenever the configured latency of the sink
+ * changes. Called from IO context. */
+ void (*update_sink_requested_latency) (pa_sink_input *i); /* may be NULL */
+
+ /* Called whenver the latency range of the sink changes. Called
+ * from IO context. */
+ void (*update_sink_latency_range) (pa_sink_input *i); /* may be NULL */
/* If non-NULL this function is called when the input is first
* connected to a sink or when the rtpoll/asyncmsgq fields
* change. You usually don't need to implement this function
* unless you rewrite a sink that is piggy-backed onto
* another. Called from IO thread context */
- void (*attach) (pa_sink_input *i); /* may be NULL */
+ void (*attach) (pa_sink_input *i); /* may be NULL */
/* If non-NULL this function is called when the output is
* disconnected from its sink. Called from IO thread context */
@@ -107,41 +138,52 @@ struct pa_sink_input {
/* If non-NULL called whenever the the sink this input is attached
* to suspends or resumes. Called from main context */
- void (*suspend) (pa_sink_input *i, int b); /* may be NULL */
-
+ 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 */
+
/* Supposed to unlink and destroy this stream. Called from main
* context. */
- void (*kill) (pa_sink_input *i); /* may be NULL */
+ void (*kill) (pa_sink_input *i); /* may NOT be NULL */
/* Return the current latency (i.e. length of bufferd audio) of
- this stream. Called from main context. If NULL a
- PA_SINK_INPUT_MESSAGE_GET_LATENCY message is sent to the IO thread
- instead. */
+ this stream. Called from main context. This is added to what the
+ PA_SINK_INPUT_MESSAGE_GET_LATENCY message sent to the IO thread
+ returns */
pa_usec_t (*get_latency) (pa_sink_input *i); /* may be NULL */
- pa_resample_method_t resample_method;
+ /* If non_NULL this function is called from thread context if the
+ * state changes. The old state is found in thread_info.state. */
+ void (*state_change) (pa_sink_input *i, pa_sink_input_state_t state); /* may be NULL */
struct {
pa_sink_input_state_t state;
pa_atomic_t drained;
pa_bool_t attached; /* True only between ->attach() and ->detach() calls */
-
+
pa_sample_spec sample_spec;
- pa_memchunk resampled_chunk;
pa_resampler *resampler; /* may be NULL */
- /* Some silence to play before the actual data. This is used to
- * compensate for latency differences when moving a sink input
- * "hot" between sinks. */
- size_t move_silence;
- pa_memblock *silence_memblock; /* may be NULL */
+ /* We maintain a history of resampled audio data here. */
+ pa_memblockq *render_memblockq;
+
+ size_t rewrite_nbytes;
+ pa_bool_t rewrite_flush;
+ uint64_t underrun_for, playing_for;
pa_sink_input *sync_prev, *sync_next;
-
+
pa_cvolume volume;
pa_bool_t muted;
+
+ /* The requested latency for the sink */
+ pa_usec_t requested_sink_latency;
+
+ pa_hashmap *direct_outputs;
} thread_info;
void *userdata;
@@ -156,11 +198,15 @@ enum {
PA_SINK_INPUT_MESSAGE_GET_LATENCY,
PA_SINK_INPUT_MESSAGE_SET_RATE,
PA_SINK_INPUT_MESSAGE_SET_STATE,
+ PA_SINK_INPUT_MESSAGE_SET_REQUESTED_LATENCY,
+ PA_SINK_INPUT_MESSAGE_GET_REQUESTED_LATENCY,
PA_SINK_INPUT_MESSAGE_MAX
};
typedef struct pa_sink_input_new_data {
- const char *name, *driver;
+ pa_proplist *proplist;
+
+ const char *driver;
pa_module *module;
pa_client *client;
@@ -170,7 +216,7 @@ typedef struct pa_sink_input_new_data {
pa_bool_t sample_spec_is_set;
pa_channel_map channel_map;
pa_bool_t channel_map_is_set;
-
+
pa_cvolume volume;
pa_bool_t volume_is_set;
pa_bool_t muted;
@@ -186,6 +232,12 @@ void pa_sink_input_new_data_set_sample_spec(pa_sink_input_new_data *data, const
void pa_sink_input_new_data_set_channel_map(pa_sink_input_new_data *data, const pa_channel_map *map);
void pa_sink_input_new_data_set_volume(pa_sink_input_new_data *data, const pa_cvolume *volume);
void pa_sink_input_new_data_set_muted(pa_sink_input_new_data *data, pa_bool_t mute);
+void pa_sink_input_new_data_done(pa_sink_input_new_data *data);
+
+typedef struct pa_sink_input_move_hook_data {
+ pa_sink_input *sink_input;
+ pa_sink *destination;
+} pa_sink_input_move_hook_data;
/* To be called by the implementing module only */
@@ -199,39 +251,57 @@ void pa_sink_input_unlink(pa_sink_input* i);
void pa_sink_input_set_name(pa_sink_input *i, const char *name);
-/* Callable by everyone */
+pa_usec_t pa_sink_input_set_requested_latency(pa_sink_input *i, pa_usec_t usec);
+
+/* Request that the specified number of bytes already written out to
+the hw device is rewritten, if possible. Please note that this is
+only a kind request. The sink driver may not be able to fulfill it
+fully -- or at all. If the request for a rewrite was successful, the
+sink driver will call ->rewind() and pass the number of bytes that
+could be rewound in the HW device. This functionality is required for
+implementing the "zero latency" write-through functionality. */
+void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes, pa_bool_t rewrite, pa_bool_t flush);
+
+void pa_sink_input_cork(pa_sink_input *i, pa_bool_t b);
+
+int pa_sink_input_set_rate(pa_sink_input *i, uint32_t rate);
+
+/* Callable by everyone from main thread*/
/* External code may request disconnection with this function */
void pa_sink_input_kill(pa_sink_input*i);
-pa_usec_t pa_sink_input_get_latency(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);
const pa_cvolume *pa_sink_input_get_volume(pa_sink_input *i);
void pa_sink_input_set_mute(pa_sink_input *i, pa_bool_t mute);
-int pa_sink_input_get_mute(pa_sink_input *i);
-
-void pa_sink_input_cork(pa_sink_input *i, pa_bool_t b);
-
-int pa_sink_input_set_rate(pa_sink_input *i, uint32_t rate);
+pa_bool_t pa_sink_input_get_mute(pa_sink_input *i);
pa_resample_method_t pa_sink_input_get_resample_method(pa_sink_input *i);
-int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, int immediately);
+int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest);
pa_sink_input_state_t pa_sink_input_get_state(pa_sink_input *i);
-/* To be used exclusively by the sink driver thread */
+pa_usec_t pa_sink_input_get_requested_latency(pa_sink_input *i);
+
+/* To be used exclusively by the sink driver IO thread */
int pa_sink_input_peek(pa_sink_input *i, size_t length, pa_memchunk *chunk, pa_cvolume *volume);
void pa_sink_input_drop(pa_sink_input *i, size_t length);
+void pa_sink_input_process_rewind(pa_sink_input *i, size_t nbytes /* in the sink's sample spec */);
+void pa_sink_input_update_max_rewind(pa_sink_input *i, size_t nbytes /* in the sink's sample spec */);
+void pa_sink_input_update_max_request(pa_sink_input *i, size_t nbytes /* in the sink's sample spec */);
+
+void pa_sink_input_set_state_within_thread(pa_sink_input *i, pa_sink_input_state_t state);
+
int pa_sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk);
-typedef struct pa_sink_input_move_info {
- pa_sink_input *sink_input;
- pa_sink_input *ghost_sink_input;
- pa_memblockq *buffer;
- size_t buffer_bytes;
-} pa_sink_input_move_info;
+pa_usec_t pa_sink_input_set_requested_latency_within_thread(pa_sink_input *i, pa_usec_t usec);
+
+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);
#endif
diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
index dccb34cc..edb023b2 100644
--- a/src/pulsecore/sink.c
+++ b/src/pulsecore/sink.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -33,6 +31,7 @@
#include <pulse/introspect.h>
#include <pulse/utf8.h>
#include <pulse/xmalloc.h>
+#include <pulse/timeval.h>
#include <pulsecore/sink-input.h>
#include <pulsecore/namereg.h>
@@ -47,43 +46,130 @@
#define MAX_MIX_CHANNELS 32
#define MIX_BUFFER_LENGTH (PA_PAGE_SIZE)
-#define SILENCE_BUFFER_LENGTH (PA_PAGE_SIZE*12)
+#define DEFAULT_MIN_LATENCY (4*PA_USEC_PER_MSEC)
static PA_DEFINE_CHECK_TYPE(pa_sink, pa_msgobject);
static void sink_free(pa_object *s);
+pa_sink_new_data* pa_sink_new_data_init(pa_sink_new_data *data) {
+ pa_assert(data);
+
+ memset(data, 0, sizeof(*data));
+ data->proplist = pa_proplist_new();
+
+ return data;
+}
+
+void pa_sink_new_data_set_name(pa_sink_new_data *data, const char *name) {
+ pa_assert(data);
+
+ pa_xfree(data->name);
+ data->name = pa_xstrdup(name);
+}
+
+void pa_sink_new_data_set_sample_spec(pa_sink_new_data *data, const pa_sample_spec *spec) {
+ pa_assert(data);
+
+ if ((data->sample_spec_is_set = !!spec))
+ data->sample_spec = *spec;
+}
+
+void pa_sink_new_data_set_channel_map(pa_sink_new_data *data, const pa_channel_map *map) {
+ pa_assert(data);
+
+ if ((data->channel_map_is_set = !!map))
+ data->channel_map = *map;
+}
+
+void pa_sink_new_data_set_volume(pa_sink_new_data *data, const pa_cvolume *volume) {
+ pa_assert(data);
+
+ if ((data->volume_is_set = !!volume))
+ data->volume = *volume;
+}
+
+void pa_sink_new_data_set_muted(pa_sink_new_data *data, pa_bool_t mute) {
+ pa_assert(data);
+
+ data->muted_is_set = TRUE;
+ data->muted = !!mute;
+}
+
+void pa_sink_new_data_done(pa_sink_new_data *data) {
+ pa_assert(data);
+
+ pa_xfree(data->name);
+ pa_proplist_free(data->proplist);
+}
+
+/* Called from main context */
+static void reset_callbacks(pa_sink *s) {
+ pa_assert(s);
+
+ s->set_state = NULL;
+ s->get_volume = NULL;
+ s->set_volume = NULL;
+ s->get_mute = NULL;
+ s->set_mute = NULL;
+ s->request_rewind = NULL;
+ s->update_requested_latency = NULL;
+}
+
+/* Called from main context */
pa_sink* pa_sink_new(
pa_core *core,
- const char *driver,
- const char *name,
- int fail,
- const pa_sample_spec *spec,
- const pa_channel_map *map) {
+ pa_sink_new_data *data,
+ pa_sink_flags_t flags) {
pa_sink *s;
- char *n = NULL;
- char st[256];
- pa_channel_map tmap;
+ const char *name;
+ char st[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
+ pa_source_new_data source_data;
+ const char *dn;
pa_assert(core);
- pa_assert(name);
- pa_assert(spec);
+ pa_assert(data);
+ pa_assert(data->name);
- pa_return_null_if_fail(pa_sample_spec_valid(spec));
+ s = pa_msgobject_new(pa_sink);
- if (!map)
- map = pa_channel_map_init_auto(&tmap, spec->channels, PA_CHANNEL_MAP_DEFAULT);
+ if (!(name = pa_namereg_register(core, data->name, PA_NAMEREG_SINK, s, data->namereg_fail))) {
+ pa_xfree(s);
+ return NULL;
+ }
- pa_return_null_if_fail(map && pa_channel_map_valid(map));
- pa_return_null_if_fail(map->channels == spec->channels);
- pa_return_null_if_fail(!driver || pa_utf8_valid(driver));
- pa_return_null_if_fail(name && pa_utf8_valid(name) && *name);
+ pa_sink_new_data_set_name(data, name);
- s = pa_msgobject_new(pa_sink);
+ if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SINK_NEW], data) < 0) {
+ pa_xfree(s);
+ pa_namereg_unregister(core, name);
+ return NULL;
+ }
+
+ 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]);
+
+ pa_return_null_if_fail(data->sample_spec_is_set && pa_sample_spec_valid(&data->sample_spec));
+
+ if (!data->channel_map_is_set)
+ pa_return_null_if_fail(pa_channel_map_init_auto(&data->channel_map, data->sample_spec.channels, PA_CHANNEL_MAP_DEFAULT));
+
+ pa_return_null_if_fail(pa_channel_map_valid(&data->channel_map));
+ pa_return_null_if_fail(data->channel_map.channels == data->sample_spec.channels);
+
+ if (!data->volume_is_set)
+ pa_cvolume_reset(&data->volume, data->sample_spec.channels);
+
+ pa_return_null_if_fail(pa_cvolume_valid(&data->volume));
+ pa_return_null_if_fail(data->volume.channels == data->sample_spec.channels);
- if (!(name = pa_namereg_register(core, name, PA_NAMEREG_SINK, s, fail))) {
+ if (!data->muted_is_set)
+ data->muted = FALSE;
+
+ if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SINK_FIXATE], data) < 0) {
pa_xfree(s);
+ pa_namereg_unregister(core, name);
return NULL;
}
@@ -92,71 +178,108 @@ pa_sink* pa_sink_new(
s->core = core;
s->state = PA_SINK_INIT;
- s->flags = 0;
+ s->flags = flags;
s->name = pa_xstrdup(name);
- s->description = NULL;
- s->driver = pa_xstrdup(driver);
- s->module = NULL;
+ s->proplist = pa_proplist_copy(data->proplist);
+ s->driver = pa_xstrdup(data->driver);
+ s->module = data->module;
- s->sample_spec = *spec;
- s->channel_map = *map;
+ s->sample_spec = data->sample_spec;
+ s->channel_map = data->channel_map;
s->inputs = pa_idxset_new(NULL, NULL);
s->n_corked = 0;
- pa_cvolume_reset(&s->volume, spec->channels);
- s->muted = FALSE;
- s->refresh_volume = s->refresh_mute = FALSE;
+ s->volume = data->volume;
+ s->muted = data->muted;
+ s->refresh_volume = s->refresh_muted = FALSE;
- s->get_latency = NULL;
- s->set_volume = NULL;
- s->get_volume = NULL;
- s->set_mute = NULL;
- s->get_mute = NULL;
- s->set_state = NULL;
+ reset_callbacks(s);
s->userdata = NULL;
s->asyncmsgq = NULL;
s->rtpoll = NULL;
- s->silence = NULL;
+
+ pa_silence_memchunk_get(
+ &core->silence_cache,
+ core->mempool,
+ &s->silence,
+ &s->sample_spec,
+ 0);
+
+ s->thread_info.inputs = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+ pa_cvolume_reset(&s->thread_info.soft_volume, s->sample_spec.channels);
+ s->thread_info.soft_muted = FALSE;
+ s->thread_info.state = s->state;
+ s->thread_info.rewind_nbytes = 0;
+ s->thread_info.max_rewind = 0;
+ 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;
pa_assert_se(pa_idxset_put(core->sinks, s, &s->index) >= 0);
- pa_sample_spec_snprint(st, sizeof(st), spec);
- pa_log_info("Created sink %u \"%s\" with sample spec \"%s\"", s->index, s->name, st);
+ pa_log_info("Created sink %u \"%s\" with sample spec %s and channel map %s",
+ s->index,
+ s->name,
+ pa_sample_spec_snprint(st, sizeof(st), &s->sample_spec),
+ pa_channel_map_snprint(cm, sizeof(cm), &s->channel_map));
+
+ pa_source_new_data_init(&source_data);
+ pa_source_new_data_set_sample_spec(&source_data, &s->sample_spec);
+ pa_source_new_data_set_channel_map(&source_data, &s->channel_map);
+ source_data.name = pa_sprintf_malloc("%s.monitor", name);
+ source_data.driver = data->driver;
+ source_data.module = data->module;
+
+ dn = pa_proplist_gets(s->proplist, PA_PROP_DEVICE_DESCRIPTION);
+ 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");
- n = pa_sprintf_malloc("%s.monitor", name);
+ s->monitor_source = pa_source_new(core, &source_data, 0);
- if (!(s->monitor_source = pa_source_new(core, driver, n, 0, spec, map)))
- pa_log_warn("Failed to create monitor source.");
- else {
- char *d;
- s->monitor_source->monitor_of = s;
- d = pa_sprintf_malloc("Monitor Source of %s", s->name);
- pa_source_set_description(s->monitor_source, d);
- pa_xfree(d);
+ pa_source_new_data_done(&source_data);
+
+ if (!s->monitor_source) {
+ pa_sink_unlink(s);
+ pa_sink_unref(s);
+ return NULL;
}
- pa_xfree(n);
+ s->monitor_source->monitor_of = s;
- s->thread_info.inputs = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
- s->thread_info.soft_volume = s->volume;
- s->thread_info.soft_muted = s->muted;
- s->thread_info.state = s->state;
+ pa_source_set_latency_range(s->monitor_source, s->thread_info.min_latency, s->thread_info.max_latency);
+ pa_source_set_max_rewind(s->monitor_source, s->thread_info.max_rewind);
return s;
}
+/* Called from main context */
static int sink_set_state(pa_sink *s, pa_sink_state_t state) {
int ret;
+ pa_bool_t suspend_change;
pa_assert(s);
if (s->state == state)
return 0;
- if ((s->state == PA_SINK_SUSPENDED && PA_SINK_OPENED(state)) ||
- (PA_SINK_OPENED(s->state) && state == PA_SINK_SUSPENDED)) {
+ suspend_change =
+ (s->state == PA_SINK_SUSPENDED && PA_SINK_IS_OPENED(state)) ||
+ (PA_SINK_IS_OPENED(s->state) && state == PA_SINK_SUSPENDED);
+
+ if (s->set_state)
+ if ((ret = s->set_state(s, state)) < 0)
+ return -1;
+
+ if (s->asyncmsgq)
+ pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_STATE, PA_UINT_TO_PTR(state), 0, NULL) == 0);
+
+ s->state = state;
+
+ if (suspend_change) {
pa_sink_input *i;
uint32_t idx;
@@ -167,35 +290,40 @@ static int sink_set_state(pa_sink *s, pa_sink_state_t state) {
i->suspend(i, state == PA_SINK_SUSPENDED);
}
- if (s->set_state)
- if ((ret = s->set_state(s, state)) < 0)
- return -1;
-
- if (pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_STATE, PA_UINT_TO_PTR(state), 0, NULL) < 0)
- return -1;
-
- s->state = state;
-
if (state != PA_SINK_UNLINKED) /* if we enter UNLINKED state pa_sink_unlink() will fire the apropriate events */
pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_STATE_CHANGED], s);
+
return 0;
}
+/* Called from main context */
void pa_sink_put(pa_sink* s) {
pa_sink_assert_ref(s);
pa_assert(s->state == PA_SINK_INIT);
+
+ /* The following fields must be initialized properly when calling _put() */
pa_assert(s->asyncmsgq);
pa_assert(s->rtpoll);
+ pa_assert(!s->thread_info.min_latency || !s->thread_info.max_latency ||
+ s->thread_info.min_latency <= s->thread_info.max_latency);
+
+ if (!(s->flags & PA_SINK_HW_VOLUME_CTRL)) {
+ s->flags |= PA_SINK_DECIBEL_VOLUME;
+
+ s->thread_info.soft_volume = s->volume;
+ s->thread_info.soft_muted = s->muted;
+ }
pa_assert_se(sink_set_state(s, PA_SINK_IDLE) == 0);
pa_source_put(s->monitor_source);
pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK | PA_SUBSCRIPTION_EVENT_NEW, s->index);
- pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_NEW_POST], s);
+ pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_PUT], s);
}
+/* Called from main context */
void pa_sink_unlink(pa_sink* s) {
pa_bool_t linked;
pa_sink_input *i, *j = NULL;
@@ -210,7 +338,7 @@ void pa_sink_unlink(pa_sink* s) {
* may be called multiple times on the same sink without bad
* effects. */
- linked = PA_SINK_LINKED(s->state);
+ linked = PA_SINK_IS_LINKED(s->state);
if (linked)
pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_UNLINK], s);
@@ -230,12 +358,7 @@ void pa_sink_unlink(pa_sink* s) {
else
s->state = PA_SINK_UNLINKED;
- s->get_latency = NULL;
- s->get_volume = NULL;
- s->set_volume = NULL;
- s->set_mute = NULL;
- s->get_mute = NULL;
- s->set_state = NULL;
+ reset_callbacks(s);
if (s->monitor_source)
pa_source_unlink(s->monitor_source);
@@ -246,6 +369,7 @@ void pa_sink_unlink(pa_sink* s) {
}
}
+/* Called from main context */
static void sink_free(pa_object *o) {
pa_sink *s = PA_SINK(o);
pa_sink_input *i;
@@ -253,7 +377,7 @@ static void sink_free(pa_object *o) {
pa_assert(s);
pa_assert(pa_sink_refcnt(s) == 0);
- if (PA_SINK_LINKED(s->state))
+ if (PA_SINK_IS_LINKED(s->state))
pa_sink_unlink(s);
pa_log_info("Freeing sink %u \"%s\"", s->index, s->name);
@@ -270,18 +394,21 @@ static void sink_free(pa_object *o) {
pa_hashmap_free(s->thread_info.inputs, NULL, NULL);
- if (s->silence)
- pa_memblock_unref(s->silence);
+ if (s->silence.memblock)
+ pa_memblock_unref(s->silence.memblock);
pa_xfree(s->name);
- pa_xfree(s->description);
pa_xfree(s->driver);
+
+ if (s->proplist)
+ pa_proplist_free(s->proplist);
+
pa_xfree(s);
}
+/* Called from main context */
void pa_sink_set_asyncmsgq(pa_sink *s, pa_asyncmsgq *q) {
pa_sink_assert_ref(s);
- pa_assert(q);
s->asyncmsgq = q;
@@ -289,18 +416,19 @@ void pa_sink_set_asyncmsgq(pa_sink *s, pa_asyncmsgq *q) {
pa_source_set_asyncmsgq(s->monitor_source, q);
}
+/* Called from main context */
void pa_sink_set_rtpoll(pa_sink *s, pa_rtpoll *p) {
pa_sink_assert_ref(s);
- pa_assert(p);
s->rtpoll = p;
if (s->monitor_source)
pa_source_set_rtpoll(s->monitor_source, p);
}
+/* Called from main context */
int pa_sink_update_status(pa_sink*s) {
pa_sink_assert_ref(s);
- pa_assert(PA_SINK_LINKED(s->state));
+ pa_assert(PA_SINK_IS_LINKED(s->state));
if (s->state == PA_SINK_SUSPENDED)
return 0;
@@ -308,9 +436,10 @@ int pa_sink_update_status(pa_sink*s) {
return sink_set_state(s, pa_sink_used_by(s) ? PA_SINK_RUNNING : PA_SINK_IDLE);
}
+/* Called from main context */
int pa_sink_suspend(pa_sink *s, pa_bool_t suspend) {
pa_sink_assert_ref(s);
- pa_assert(PA_SINK_LINKED(s->state));
+ pa_assert(PA_SINK_IS_LINKED(s->state));
if (suspend)
return sink_set_state(s, PA_SINK_SUSPENDED);
@@ -318,17 +447,36 @@ int pa_sink_suspend(pa_sink *s, pa_bool_t suspend) {
return sink_set_state(s, pa_sink_used_by(s) ? PA_SINK_RUNNING : PA_SINK_IDLE);
}
-void pa_sink_ping(pa_sink *s) {
+/* Called from IO thread context */
+void pa_sink_process_rewind(pa_sink *s, size_t nbytes) {
+ pa_sink_input *i;
+ void *state = NULL;
pa_sink_assert_ref(s);
- pa_assert(PA_SINK_LINKED(s->state));
+ pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
- pa_asyncmsgq_post(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_PING, NULL, 0, NULL, NULL);
+ /* Make sure the sink code already reset the counter! */
+ pa_assert(s->thread_info.rewind_nbytes <= 0);
+
+ if (nbytes <= 0)
+ return;
+
+ pa_log_debug("Processing rewind...");
+
+ while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL))) {
+ pa_sink_input_assert_ref(i);
+ pa_sink_input_process_rewind(i, nbytes);
+ }
+
+ if (s->monitor_source && PA_SOURCE_IS_OPENED(s->monitor_source->thread_info.state))
+ pa_source_process_rewind(s->monitor_source, nbytes);
}
-static unsigned fill_mix_info(pa_sink *s, size_t length, pa_mix_info *info, unsigned maxinfo) {
+/* Called from IO thread context */
+static unsigned fill_mix_info(pa_sink *s, size_t *length, pa_mix_info *info, unsigned maxinfo) {
pa_sink_input *i;
unsigned n = 0;
void *state = NULL;
+ size_t mixlength = *length;
pa_sink_assert_ref(s);
pa_assert(info);
@@ -336,8 +484,16 @@ static unsigned fill_mix_info(pa_sink *s, size_t length, pa_mix_info *info, unsi
while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)) && maxinfo > 0) {
pa_sink_input_assert_ref(i);
- if (pa_sink_input_peek(i, length, &info->chunk, &info->volume) < 0)
+ if (pa_sink_input_peek(i, *length, &info->chunk, &info->volume) < 0)
+ continue;
+
+ if (mixlength == 0 || info->chunk.length < mixlength)
+ mixlength = info->chunk.length;
+
+ if (pa_memblock_is_silence(info->chunk.memblock)) {
+ pa_memblock_unref(info->chunk.memblock);
continue;
+ }
info->userdata = pa_sink_input_ref(i);
@@ -349,27 +505,32 @@ static unsigned fill_mix_info(pa_sink *s, size_t length, pa_mix_info *info, unsi
maxinfo--;
}
+ if (mixlength > 0)
+ *length = mixlength;
+
return n;
}
-static void inputs_drop(pa_sink *s, pa_mix_info *info, unsigned n, size_t length) {
+/* Called from IO thread context */
+static void inputs_drop(pa_sink *s, pa_mix_info *info, unsigned n, pa_memchunk *result) {
pa_sink_input *i;
void *state = NULL;
unsigned p = 0;
unsigned n_unreffed = 0;
pa_sink_assert_ref(s);
+ pa_assert(result);
+ pa_assert(result->memblock);
+ pa_assert(result->length > 0);
/* We optimize for the case where the order of the inputs has not changed */
while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL))) {
unsigned j;
- pa_mix_info* m;
+ pa_mix_info* m = NULL;
pa_sink_input_assert_ref(i);
- m = NULL;
-
/* Let's try to find the matching entry info the pa_mix_info array */
for (j = 0; j < n; j ++) {
@@ -384,14 +545,47 @@ static void inputs_drop(pa_sink *s, pa_mix_info *info, unsigned n, size_t length
}
/* Drop read data */
- pa_sink_input_drop(i, length);
+ pa_sink_input_drop(i, result->length);
+
+ if (s->monitor_source && PA_SOURCE_IS_OPENED(pa_source_get_state(s->monitor_source))) {
+
+ if (pa_hashmap_size(i->thread_info.direct_outputs) > 0) {
+ void *ostate = NULL;
+ pa_source_output *o;
+ pa_memchunk c;
+
+ if (m && m->chunk.memblock) {
+ c = m->chunk;
+ pa_memblock_ref(c.memblock);
+ pa_assert(result->length <= c.length);
+ c.length = result->length;
+
+ pa_memchunk_make_writable(&c, 0);
+ pa_volume_memchunk(&c, &s->sample_spec, &m->volume);
+ } else {
+ c = s->silence;
+ pa_memblock_ref(c.memblock);
+ pa_assert(result->length <= c.length);
+ c.length = result->length;
+ }
+
+ while ((o = pa_hashmap_iterate(i->thread_info.direct_outputs, &ostate, NULL))) {
+ pa_source_output_assert_ref(o);
+ pa_assert(o->direct_on_input == i);
+ pa_source_post_direct(s->monitor_source, o, &c);
+ }
+
+ pa_memblock_unref(c.memblock);
+ }
+ }
if (m) {
- pa_sink_input_unref(m->userdata);
- m->userdata = NULL;
if (m->chunk.memblock)
pa_memblock_unref(m->chunk.memblock);
- pa_memchunk_reset(&m->chunk);
+ pa_memchunk_reset(&m->chunk);
+
+ pa_sink_input_unref(m->userdata);
+ m->userdata = NULL;
n_unreffed += 1;
}
@@ -408,20 +602,26 @@ static void inputs_drop(pa_sink *s, pa_mix_info *info, unsigned n, size_t length
pa_memblock_unref(info->chunk.memblock);
}
}
+
+ if (s->monitor_source && PA_SOURCE_IS_OPENED(pa_source_get_state(s->monitor_source)))
+ pa_source_post(s->monitor_source, result);
}
+/* Called from IO thread context */
void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) {
pa_mix_info info[MAX_MIX_CHANNELS];
unsigned n;
size_t block_size_max;
pa_sink_assert_ref(s);
- pa_assert(PA_SINK_OPENED(s->thread_info.state));
+ pa_assert(PA_SINK_IS_OPENED(s->thread_info.state));
pa_assert(pa_frame_aligned(length, &s->sample_spec));
pa_assert(result);
pa_sink_ref(s);
+ s->thread_info.rewind_nbytes = 0;
+
if (length <= 0)
length = pa_frame_align(MIX_BUFFER_LENGTH, &s->sample_spec);
@@ -431,24 +631,15 @@ void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) {
pa_assert(length > 0);
- n = s->thread_info.state == PA_SINK_RUNNING ? fill_mix_info(s, length, info, MAX_MIX_CHANNELS) : 0;
+ n = s->thread_info.state == PA_SINK_RUNNING ? fill_mix_info(s, &length, info, MAX_MIX_CHANNELS) : 0;
if (n == 0) {
- if (length > SILENCE_BUFFER_LENGTH)
- length = pa_frame_align(SILENCE_BUFFER_LENGTH, &s->sample_spec);
-
- pa_assert(length > 0);
-
- if (!s->silence || pa_memblock_get_length(s->silence) < length) {
- if (s->silence)
- pa_memblock_unref(s->silence);
- s->silence = pa_silence_memblock_new(s->core->mempool, &s->sample_spec, length);
- }
+ *result = s->silence;
+ pa_memblock_ref(result->memblock);
- result->memblock = pa_memblock_ref(s->silence);
- result->length = length;
- result->index = 0;
+ if (result->length > length)
+ result->length = length;
} else if (n == 1) {
pa_cvolume volume;
@@ -462,6 +653,7 @@ 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_log("adjusting 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);
@@ -473,27 +665,30 @@ void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) {
result->memblock = pa_memblock_new(s->core->mempool, length);
ptr = pa_memblock_acquire(result->memblock);
- result->length = pa_mix(info, n, ptr, length, &s->sample_spec, &s->thread_info.soft_volume, s->thread_info.soft_muted);
+ result->length = pa_mix(info, n,
+ ptr, length,
+ &s->sample_spec,
+ &s->thread_info.soft_volume,
+ s->thread_info.soft_muted);
pa_memblock_release(result->memblock);
result->index = 0;
}
if (s->thread_info.state == PA_SINK_RUNNING)
- inputs_drop(s, info, n, result->length);
-
- if (s->monitor_source && PA_SOURCE_OPENED(pa_source_get_state(s->monitor_source)))
- pa_source_post(s->monitor_source, result);
+ inputs_drop(s, info, n, result);
pa_sink_unref(s);
}
+/* Called from IO thread context */
void pa_sink_render_into(pa_sink*s, pa_memchunk *target) {
pa_mix_info info[MAX_MIX_CHANNELS];
unsigned n;
+ size_t length, block_size_max;
pa_sink_assert_ref(s);
- pa_assert(PA_SINK_OPENED(s->thread_info.state));
+ pa_assert(PA_SINK_IS_OPENED(s->thread_info.state));
pa_assert(target);
pa_assert(target->memblock);
pa_assert(target->length > 0);
@@ -501,34 +696,48 @@ void pa_sink_render_into(pa_sink*s, pa_memchunk *target) {
pa_sink_ref(s);
- n = s->thread_info.state == PA_SINK_RUNNING ? fill_mix_info(s, target->length, info, MAX_MIX_CHANNELS) : 0;
+ s->thread_info.rewind_nbytes = 0;
+
+ length = target->length;
+ block_size_max = pa_mempool_block_size_max(s->core->mempool);
+ if (length > block_size_max)
+ length = pa_frame_align(block_size_max, &s->sample_spec);
+
+ pa_assert(length > 0);
+
+ n = s->thread_info.state == PA_SINK_RUNNING ? fill_mix_info(s, &length, info, MAX_MIX_CHANNELS) : 0;
if (n == 0) {
+ if (target->length > length)
+ target->length = length;
+
pa_silence_memchunk(target, &s->sample_spec);
} else if (n == 1) {
- if (target->length > info[0].chunk.length)
- target->length = info[0].chunk.length;
+ pa_cvolume volume;
+
+ if (target->length > length)
+ target->length = length;
+
+ pa_sw_cvolume_multiply(&volume, &s->thread_info.soft_volume, &info[0].volume);
- if (s->thread_info.soft_muted)
+ if (s->thread_info.soft_muted || pa_cvolume_is_muted(&volume))
pa_silence_memchunk(target, &s->sample_spec);
else {
- void *src, *ptr;
- pa_cvolume volume;
-
- ptr = pa_memblock_acquire(target->memblock);
- src = pa_memblock_acquire(info[0].chunk.memblock);
+ pa_memchunk vchunk;
- memcpy((uint8_t*) ptr + target->index,
- (uint8_t*) src + info[0].chunk.index,
- target->length);
+ vchunk = info[0].chunk;
+ pa_memblock_ref(vchunk.memblock);
- pa_memblock_release(target->memblock);
- pa_memblock_release(info[0].chunk.memblock);
+ if (vchunk.length > length)
+ vchunk.length = length;
- pa_sw_cvolume_multiply(&volume, &s->thread_info.soft_volume, &info[0].volume);
+ if (!pa_cvolume_is_norm(&volume)) {
+ pa_memchunk_make_writable(&vchunk, 0);
+ pa_volume_memchunk(&vchunk, &s->sample_spec, &volume);
+ }
- if (!pa_cvolume_is_norm(&volume))
- pa_volume_memchunk(target, &s->sample_spec, &volume);
+ pa_memchunk_memcpy(target, &vchunk);
+ pa_memblock_unref(vchunk.memblock);
}
} else {
@@ -537,8 +746,7 @@ void pa_sink_render_into(pa_sink*s, pa_memchunk *target) {
ptr = pa_memblock_acquire(target->memblock);
target->length = pa_mix(info, n,
- (uint8_t*) ptr + target->index,
- target->length,
+ (uint8_t*) ptr + target->index, length,
&s->sample_spec,
&s->thread_info.soft_volume,
s->thread_info.soft_muted);
@@ -547,20 +755,18 @@ void pa_sink_render_into(pa_sink*s, pa_memchunk *target) {
}
if (s->thread_info.state == PA_SINK_RUNNING)
- inputs_drop(s, info, n, target->length);
-
- if (s->monitor_source && PA_SOURCE_OPENED(pa_source_get_state(s->monitor_source)))
- pa_source_post(s->monitor_source, target);
+ inputs_drop(s, info, n, target);
pa_sink_unref(s);
}
+/* Called from IO thread context */
void pa_sink_render_into_full(pa_sink *s, pa_memchunk *target) {
pa_memchunk chunk;
size_t l, d;
pa_sink_assert_ref(s);
- pa_assert(PA_SINK_OPENED(s->thread_info.state));
+ pa_assert(PA_SINK_IS_OPENED(s->thread_info.state));
pa_assert(target);
pa_assert(target->memblock);
pa_assert(target->length > 0);
@@ -568,6 +774,8 @@ void pa_sink_render_into_full(pa_sink *s, pa_memchunk *target) {
pa_sink_ref(s);
+ s->thread_info.rewind_nbytes = 0;
+
l = target->length;
d = 0;
while (l > 0) {
@@ -584,13 +792,16 @@ void pa_sink_render_into_full(pa_sink *s, pa_memchunk *target) {
pa_sink_unref(s);
}
+/* Called from IO thread context */
void pa_sink_render_full(pa_sink *s, size_t length, pa_memchunk *result) {
pa_sink_assert_ref(s);
- pa_assert(PA_SINK_OPENED(s->thread_info.state));
+ pa_assert(PA_SINK_IS_OPENED(s->thread_info.state));
pa_assert(length > 0);
pa_assert(pa_frame_aligned(length, &s->sample_spec));
pa_assert(result);
+ s->thread_info.rewind_nbytes = 0;
+
/*** This needs optimization ***/
result->index = 0;
@@ -600,62 +811,29 @@ void pa_sink_render_full(pa_sink *s, size_t length, pa_memchunk *result) {
pa_sink_render_into_full(s, result);
}
-void pa_sink_skip(pa_sink *s, size_t length) {
- pa_sink_input *i;
- void *state = NULL;
-
- pa_sink_assert_ref(s);
- pa_assert(PA_SINK_OPENED(s->thread_info.state));
- pa_assert(length > 0);
- pa_assert(pa_frame_aligned(length, &s->sample_spec));
-
- if (pa_source_used_by(s->monitor_source)) {
- pa_memchunk chunk;
-
- /* If something is connected to our monitor source, we have to
- * pass valid data to it */
-
- while (length > 0) {
- pa_sink_render(s, length, &chunk);
- pa_memblock_unref(chunk.memblock);
-
- pa_assert(chunk.length <= length);
- length -= chunk.length;
- }
-
- } else {
- /* Ok, noone cares about the rendered data, so let's not even render it */
-
- while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL))) {
- pa_sink_input_assert_ref(i);
- pa_sink_input_drop(i, length);
- }
- }
-}
-
+/* Called from main thread */
pa_usec_t pa_sink_get_latency(pa_sink *s) {
pa_usec_t usec = 0;
pa_sink_assert_ref(s);
- pa_assert(PA_SINK_LINKED(s->state));
-
- if (!PA_SINK_OPENED(s->state))
- return 0;
+ pa_assert(PA_SINK_IS_LINKED(s->state));
- if (s->get_latency)
- return s->get_latency(s);
+ /* The returned value is supposed to be in the time domain of the sound card! */
- if (pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0)
+ if (!PA_SINK_IS_OPENED(s->state))
return 0;
+ pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) == 0);
+
return usec;
}
+/* Called from main thread */
void pa_sink_set_volume(pa_sink *s, const pa_cvolume *volume) {
- int changed;
+ pa_bool_t changed;
pa_sink_assert_ref(s);
- pa_assert(PA_SINK_LINKED(s->state));
+ pa_assert(PA_SINK_IS_LINKED(s->state));
pa_assert(volume);
changed = !pa_cvolume_equal(volume, &s->volume);
@@ -665,37 +843,39 @@ void pa_sink_set_volume(pa_sink *s, const pa_cvolume *volume) {
s->set_volume = NULL;
if (!s->set_volume)
- pa_asyncmsgq_post(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_VOLUME, pa_xnewdup(struct pa_cvolume, volume, 1), 0, NULL, pa_xfree);
+ pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_VOLUME, volume, 0, NULL);
if (changed)
pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
}
+/* Called from main thread */
const pa_cvolume *pa_sink_get_volume(pa_sink *s) {
- struct pa_cvolume old_volume;
-
pa_sink_assert_ref(s);
- pa_assert(PA_SINK_LINKED(s->state));
+ pa_assert(PA_SINK_IS_LINKED(s->state));
- old_volume = s->volume;
+ if (s->refresh_volume) {
+ struct pa_cvolume old_volume = s->volume;
- if (s->get_volume && s->get_volume(s) < 0)
- s->get_volume = NULL;
+ if (s->get_volume && s->get_volume(s) < 0)
+ s->get_volume = NULL;
- if (!s->get_volume && s->refresh_volume)
- pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_VOLUME, &s->volume, 0, NULL);
+ if (!s->get_volume)
+ pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_VOLUME, &s->volume, 0, NULL);
- if (!pa_cvolume_equal(&old_volume, &s->volume))
- pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+ if (!pa_cvolume_equal(&old_volume, &s->volume))
+ pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+ }
return &s->volume;
}
+/* Called from main thread */
void pa_sink_set_mute(pa_sink *s, pa_bool_t mute) {
- int changed;
+ pa_bool_t changed;
pa_sink_assert_ref(s);
- pa_assert(PA_SINK_LINKED(s->state));
+ pa_assert(PA_SINK_IS_LINKED(s->state));
changed = s->muted != mute;
s->muted = mute;
@@ -710,71 +890,66 @@ void pa_sink_set_mute(pa_sink *s, pa_bool_t mute) {
pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
}
+/* Called from main thread */
pa_bool_t pa_sink_get_mute(pa_sink *s) {
- pa_bool_t old_muted;
pa_sink_assert_ref(s);
- pa_assert(PA_SINK_LINKED(s->state));
+ pa_assert(PA_SINK_IS_LINKED(s->state));
- old_muted = s->muted;
+ if (s->refresh_muted) {
+ pa_bool_t old_muted = s->muted;
- if (s->get_mute && s->get_mute(s) < 0)
- s->get_mute = NULL;
+ if (s->get_mute && s->get_mute(s) < 0)
+ s->get_mute = NULL;
- if (!s->get_mute && s->refresh_mute)
- pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_MUTE, &s->muted, 0, NULL);
+ if (!s->get_mute)
+ pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_MUTE, &s->muted, 0, NULL);
- if (old_muted != s->muted)
- pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+ if (old_muted != s->muted)
+ pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+ }
return s->muted;
}
-void pa_sink_set_module(pa_sink *s, pa_module *m) {
- pa_sink_assert_ref(s);
-
- if (s->module == m)
- return;
-
- s->module = m;
-
- if (s->monitor_source)
- pa_source_set_module(s->monitor_source, m);
-
- pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
-}
-
+/* Called from main thread */
void pa_sink_set_description(pa_sink *s, const char *description) {
+ const char *old;
pa_sink_assert_ref(s);
- if (!description && !s->description)
+ if (!description && !pa_proplist_contains(s->proplist, PA_PROP_DEVICE_DESCRIPTION))
return;
- if (description && s->description && !strcmp(description, s->description))
+ old = pa_proplist_gets(s->proplist, PA_PROP_DEVICE_DESCRIPTION);
+
+ if (old && description && !strcmp(old, description))
return;
- pa_xfree(s->description);
- s->description = pa_xstrdup(description);
+ if (description)
+ pa_proplist_sets(s->proplist, PA_PROP_DEVICE_DESCRIPTION, description);
+ else
+ pa_proplist_unset(s->proplist, PA_PROP_DEVICE_DESCRIPTION);
if (s->monitor_source) {
char *n;
- n = pa_sprintf_malloc("Monitor Source of %s", s->description? s->description : s->name);
+ n = pa_sprintf_malloc("Monitor Source of %s", description ? description : s->name);
pa_source_set_description(s->monitor_source, n);
pa_xfree(n);
}
- if (PA_SINK_LINKED(s->state)) {
+ if (PA_SINK_IS_LINKED(s->state)) {
pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
- pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_DESCRIPTION_CHANGED], s);
+ pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_PROPLIST_CHANGED], s);
}
}
+/* Called from main thread */
unsigned pa_sink_linked_by(pa_sink *s) {
unsigned ret;
pa_sink_assert_ref(s);
- pa_assert(PA_SINK_LINKED(s->state));
+ pa_assert(PA_SINK_IS_LINKED(s->state));
ret = pa_idxset_size(s->inputs);
@@ -787,31 +962,36 @@ unsigned pa_sink_linked_by(pa_sink *s) {
return ret;
}
+/* Called from main thread */
unsigned pa_sink_used_by(pa_sink *s) {
unsigned ret;
pa_sink_assert_ref(s);
- pa_assert(PA_SINK_LINKED(s->state));
+ pa_assert(PA_SINK_IS_LINKED(s->state));
ret = pa_idxset_size(s->inputs);
pa_assert(ret >= s->n_corked);
- ret -= s->n_corked;
/* Streams connected to our monitor source do not matter for
* pa_sink_used_by()!.*/
- return ret;
+ return ret - s->n_corked;
}
+/* Called from IO thread, except when it is not */
int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk) {
pa_sink *s = PA_SINK(o);
pa_sink_assert_ref(s);
- pa_assert(s->thread_info.state != PA_SINK_UNLINKED);
switch ((pa_sink_message_t) code) {
case PA_SINK_MESSAGE_ADD_INPUT: {
pa_sink_input *i = PA_SINK_INPUT(userdata);
+
+ /* If you change anything here, make sure to change the
+ * sink input handling a few lines down at
+ * PA_SINK_MESSAGE_FINISH_MOVE, too. */
+
pa_hashmap_put(s->thread_info.inputs, PA_UINT32_TO_PTR(i->index), pa_sink_input_ref(i));
/* Since the caller sleeps in pa_sink_input_put(), we can
@@ -836,9 +1016,17 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse
if (i->attach)
i->attach(i);
- /* If you change anything here, make sure to change the
- * ghost sink input handling a few lines down at
- * PA_SINK_MESSAGE_REMOVE_INPUT_AND_BUFFER, too. */
+ pa_sink_input_set_state_within_thread(i, i->state);
+
+ pa_sink_input_update_max_rewind(i, s->thread_info.max_rewind);
+ pa_sink_input_update_max_request(i, s->thread_info.max_request);
+
+ pa_sink_invalidate_requested_latency(s);
+
+ /* We don't rewind here automatically. This is left to the
+ * sink input implementor because some sink inputs need a
+ * slow start, i.e. need some time to buffer client
+ * samples before beginning streaming. */
return 0;
}
@@ -848,11 +1036,13 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse
/* If you change anything here, make sure to change the
* sink input handling a few lines down at
- * PA_SINK_MESSAGE_REMOVE_INPUT_AND_BUFFER, too. */
+ * PA_SINK_MESSAGE_PREPAPRE_MOVE, too. */
if (i->detach)
i->detach(i);
+ pa_sink_input_set_state_within_thread(i, i->state);
+
pa_assert(i->thread_info.attached);
i->thread_info.attached = FALSE;
@@ -876,82 +1066,94 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse
if (pa_hashmap_remove(s->thread_info.inputs, PA_UINT32_TO_PTR(i->index)))
pa_sink_input_unref(i);
+ pa_sink_invalidate_requested_latency(s);
+ pa_sink_request_rewind(s, 0);
+
return 0;
}
- case PA_SINK_MESSAGE_REMOVE_INPUT_AND_BUFFER: {
- pa_sink_input_move_info *info = userdata;
- int volume_is_norm;
+ case PA_SINK_MESSAGE_START_MOVE: {
+ pa_sink_input *i = PA_SINK_INPUT(userdata);
/* We don't support moving synchronized streams. */
- pa_assert(!info->sink_input->sync_prev);
- pa_assert(!info->sink_input->sync_next);
- pa_assert(!info->sink_input->thread_info.sync_next);
- pa_assert(!info->sink_input->thread_info.sync_prev);
+ pa_assert(!i->sync_prev);
+ pa_assert(!i->sync_next);
+ pa_assert(!i->thread_info.sync_next);
+ pa_assert(!i->thread_info.sync_prev);
+
+ if (i->thread_info.state != PA_SINK_INPUT_CORKED) {
+ pa_usec_t usec = 0;
+ size_t sink_nbytes, total_nbytes;
+
+ /* Get the latency of the sink */
+ if (PA_MSGOBJECT(s)->process_msg(PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0)
+ usec = 0;
+
+ sink_nbytes = pa_usec_to_bytes(usec, &s->sample_spec);
+ total_nbytes = sink_nbytes + pa_memblockq_get_length(i->thread_info.render_memblockq);
+
+ if (total_nbytes > 0) {
+ i->thread_info.rewrite_nbytes = i->thread_info.resampler ? pa_resampler_request(i->thread_info.resampler, total_nbytes) : total_nbytes;
+ i->thread_info.rewrite_flush = TRUE;
+ pa_sink_input_process_rewind(i, sink_nbytes);
+ }
+ }
- if (info->sink_input->detach)
- info->sink_input->detach(info->sink_input);
+ if (i->detach)
+ i->detach(i);
- pa_assert(info->sink_input->thread_info.attached);
- info->sink_input->thread_info.attached = FALSE;
+ pa_assert(i->thread_info.attached);
+ i->thread_info.attached = FALSE;
- if (info->ghost_sink_input) {
- pa_assert(info->buffer_bytes > 0);
- pa_assert(info->buffer);
+ /* Let's remove the sink input ...*/
+ if (pa_hashmap_remove(s->thread_info.inputs, PA_UINT32_TO_PTR(i->index)))
+ pa_sink_input_unref(i);
- volume_is_norm = pa_cvolume_is_norm(&info->sink_input->thread_info.volume);
+ pa_sink_invalidate_requested_latency(s);
- pa_log_debug("Buffering %lu bytes ...", (unsigned long) info->buffer_bytes);
+ pa_log_debug("Requesting rewind due to started move");
+ pa_sink_request_rewind(s, 0);
- while (info->buffer_bytes > 0) {
- pa_memchunk memchunk;
- pa_cvolume volume;
- size_t n;
+ return 0;
+ }
- if (pa_sink_input_peek(info->sink_input, info->buffer_bytes, &memchunk, &volume) < 0)
- break;
+ case PA_SINK_MESSAGE_FINISH_MOVE: {
+ pa_sink_input *i = PA_SINK_INPUT(userdata);
- n = memchunk.length > info->buffer_bytes ? info->buffer_bytes : memchunk.length;
- pa_sink_input_drop(info->sink_input, n);
- memchunk.length = n;
+ /* We don't support moving synchronized streams. */
+ pa_assert(!i->sync_prev);
+ pa_assert(!i->sync_next);
+ pa_assert(!i->thread_info.sync_next);
+ pa_assert(!i->thread_info.sync_prev);
- if (!volume_is_norm) {
- pa_memchunk_make_writable(&memchunk, 0);
- pa_volume_memchunk(&memchunk, &s->sample_spec, &volume);
- }
+ pa_hashmap_put(s->thread_info.inputs, PA_UINT32_TO_PTR(i->index), pa_sink_input_ref(i));
- if (pa_memblockq_push(info->buffer, &memchunk) < 0) {
- pa_memblock_unref(memchunk.memblock);
- break;
- }
+ pa_assert(!i->thread_info.attached);
+ i->thread_info.attached = TRUE;
- pa_memblock_unref(memchunk.memblock);
- info->buffer_bytes -= n;
- }
+ if (i->attach)
+ i->attach(i);
- /* Add the remaining already resampled chunk to the buffer */
- if (info->sink_input->thread_info.resampled_chunk.memblock)
- pa_memblockq_push(info->buffer, &info->sink_input->thread_info.resampled_chunk);
+ pa_sink_input_update_max_rewind(i, s->thread_info.max_rewind);
+ pa_sink_input_update_max_request(i, s->thread_info.max_request);
- pa_memblockq_sink_input_set_queue(info->ghost_sink_input, info->buffer);
+ pa_sink_input_set_requested_latency_within_thread(i, i->thread_info.requested_sink_latency);
- pa_log_debug("Buffered %lu bytes ...", (unsigned long) pa_memblockq_get_length(info->buffer));
- }
+ if (i->thread_info.state != PA_SINK_INPUT_CORKED) {
+ pa_usec_t usec = 0;
+ size_t nbytes;
- /* Let's remove the sink input ...*/
- if (pa_hashmap_remove(s->thread_info.inputs, PA_UINT32_TO_PTR(info->sink_input->index)))
- pa_sink_input_unref(info->sink_input);
+ /* Get the latency of the sink */
+ if (PA_MSGOBJECT(s)->process_msg(PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0)
+ usec = 0;
- /* .. and add the ghost sink input instead */
- if (info->ghost_sink_input) {
- pa_hashmap_put(s->thread_info.inputs, PA_UINT32_TO_PTR(info->ghost_sink_input->index), pa_sink_input_ref(info->ghost_sink_input));
- info->ghost_sink_input->thread_info.sync_prev = info->ghost_sink_input->thread_info.sync_next = NULL;
+ nbytes = pa_usec_to_bytes(usec, &s->sample_spec);
- pa_assert(!info->ghost_sink_input->thread_info.attached);
- info->ghost_sink_input->thread_info.attached = TRUE;
+ if (nbytes > 0)
+ pa_sink_input_drop(i, nbytes);
- if (info->ghost_sink_input->attach)
- info->ghost_sink_input->attach(info->ghost_sink_input);
+ pa_log_debug("Requesting rewind due to finished move");
+ pa_sink_request_rewind(s, nbytes);
}
return 0;
@@ -959,10 +1161,14 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse
case PA_SINK_MESSAGE_SET_VOLUME:
s->thread_info.soft_volume = *((pa_cvolume*) userdata);
+
+ pa_sink_request_rewind(s, 0);
return 0;
case PA_SINK_MESSAGE_SET_MUTE:
s->thread_info.soft_muted = PA_PTR_TO_UINT(userdata);
+
+ pa_sink_request_rewind(s, 0);
return 0;
case PA_SINK_MESSAGE_GET_VOLUME:
@@ -973,9 +1179,6 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse
*((pa_bool_t*) userdata) = s->thread_info.soft_muted;
return 0;
- case PA_SINK_MESSAGE_PING:
- return 0;
-
case PA_SINK_MESSAGE_SET_STATE:
s->thread_info.state = PA_PTR_TO_UINT(userdata);
@@ -983,17 +1186,53 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse
case PA_SINK_MESSAGE_DETACH:
- /* We're detaching all our input streams so that the
- * asyncmsgq and rtpoll fields can be changed without
- * problems */
+ /* Detach all streams */
pa_sink_detach_within_thread(s);
- break;
+ return 0;
case PA_SINK_MESSAGE_ATTACH:
/* Reattach all streams */
pa_sink_attach_within_thread(s);
- break;
+ return 0;
+
+ case PA_SINK_MESSAGE_GET_REQUESTED_LATENCY: {
+
+ pa_usec_t *usec = userdata;
+ *usec = pa_sink_get_requested_latency_within_thread(s);
+
+ if (*usec == (pa_usec_t) -1)
+ *usec = s->thread_info.max_latency;
+
+ return 0;
+ }
+
+ case PA_SINK_MESSAGE_SET_LATENCY_RANGE: {
+ pa_usec_t *r = userdata;
+
+ pa_sink_update_latency_range(s, r[0], r[1]);
+
+ return 0;
+ }
+
+ case PA_SINK_MESSAGE_GET_LATENCY_RANGE: {
+ pa_usec_t *r = userdata;
+
+ r[0] = s->thread_info.min_latency;
+ r[1] = s->thread_info.max_latency;
+
+ return 0;
+ }
+
+ case PA_SINK_MESSAGE_GET_MAX_REWIND:
+
+ *((size_t*) userdata) = s->thread_info.max_rewind;
+ return 0;
+
+ case PA_SINK_MESSAGE_GET_MAX_REQUEST:
+
+ *((size_t*) userdata) = s->thread_info.max_request;
+ return 0;
case PA_SINK_MESSAGE_GET_LATENCY:
case PA_SINK_MESSAGE_MAX:
@@ -1003,6 +1242,7 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse
return -1;
}
+/* Called from main thread */
int pa_sink_suspend_all(pa_core *c, pa_bool_t suspend) {
pa_sink *sink;
uint32_t idx;
@@ -1016,26 +1256,29 @@ int pa_sink_suspend_all(pa_core *c, pa_bool_t suspend) {
return ret;
}
+/* Called from main thread */
void pa_sink_detach(pa_sink *s) {
pa_sink_assert_ref(s);
- pa_assert(PA_SINK_LINKED(s->state));
+ pa_assert(PA_SINK_IS_LINKED(s->state));
- pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_DETACH, NULL, 0, NULL);
+ pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_DETACH, NULL, 0, NULL) == 0);
}
+/* Called from main thread */
void pa_sink_attach(pa_sink *s) {
pa_sink_assert_ref(s);
- pa_assert(PA_SINK_LINKED(s->state));
+ pa_assert(PA_SINK_IS_LINKED(s->state));
- pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_ATTACH, NULL, 0, NULL);
+ pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_ATTACH, NULL, 0, NULL) == 0);
}
+/* Called from IO thread */
void pa_sink_detach_within_thread(pa_sink *s) {
pa_sink_input *i;
void *state = NULL;
pa_sink_assert_ref(s);
- pa_assert(PA_SINK_LINKED(s->thread_info.state));
+ pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
if (i->detach)
@@ -1045,12 +1288,13 @@ void pa_sink_detach_within_thread(pa_sink *s) {
pa_source_detach_within_thread(s->monitor_source);
}
+/* Called from IO thread */
void pa_sink_attach_within_thread(pa_sink *s) {
pa_sink_input *i;
void *state = NULL;
pa_sink_assert_ref(s);
- pa_assert(PA_SINK_LINKED(s->thread_info.state));
+ pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
if (i->attach)
@@ -1059,3 +1303,229 @@ void pa_sink_attach_within_thread(pa_sink *s) {
if (s->monitor_source)
pa_source_attach_within_thread(s->monitor_source);
}
+
+/* Called from IO thread */
+void pa_sink_request_rewind(pa_sink*s, size_t nbytes) {
+ pa_sink_assert_ref(s);
+ pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
+
+ if (nbytes <= 0)
+ nbytes = s->thread_info.max_rewind;
+
+ nbytes = PA_MIN(nbytes, s->thread_info.max_rewind);
+
+ if (nbytes <= s->thread_info.rewind_nbytes)
+ return;
+
+ s->thread_info.rewind_nbytes = nbytes;
+
+ if (s->request_rewind)
+ s->request_rewind(s);
+}
+
+/* Called from IO thread */
+pa_usec_t pa_sink_get_requested_latency_within_thread(pa_sink *s) {
+ pa_usec_t result = (pa_usec_t) -1;
+ pa_sink_input *i;
+ void *state = NULL;
+ pa_usec_t monitor_latency;
+
+ pa_sink_assert_ref(s);
+
+ if (s->thread_info.requested_latency_valid)
+ return s->thread_info.requested_latency;
+
+ while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
+
+ if (i->thread_info.requested_sink_latency != (pa_usec_t) -1 &&
+ (result == (pa_usec_t) -1 || result > i->thread_info.requested_sink_latency))
+ result = i->thread_info.requested_sink_latency;
+
+ monitor_latency = pa_source_get_requested_latency_within_thread(s->monitor_source);
+
+ if (monitor_latency != (pa_usec_t) -1 &&
+ (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 (s->thread_info.min_latency > 0 && result < s->thread_info.min_latency)
+ result = s->thread_info.min_latency;
+ }
+
+ s->thread_info.requested_latency = result;
+ s->thread_info.requested_latency_valid = TRUE;
+
+ return result;
+}
+
+/* Called from main thread */
+pa_usec_t pa_sink_get_requested_latency(pa_sink *s) {
+ pa_usec_t usec = 0;
+
+ pa_sink_assert_ref(s);
+ pa_assert(PA_SINK_IS_LINKED(s->state));
+
+ if (!PA_SINK_IS_OPENED(s->state))
+ return 0;
+
+ pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_REQUESTED_LATENCY, &usec, 0, NULL) == 0);
+ return usec;
+}
+
+/* Called from IO thread */
+void pa_sink_set_max_rewind(pa_sink *s, size_t max_rewind) {
+ pa_sink_input *i;
+ void *state = NULL;
+
+ pa_sink_assert_ref(s);
+
+ if (max_rewind == s->thread_info.max_rewind)
+ return;
+
+ s->thread_info.max_rewind = max_rewind;
+
+ if (PA_SINK_IS_LINKED(s->thread_info.state)) {
+ while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
+ pa_sink_input_update_max_rewind(i, s->thread_info.max_rewind);
+ }
+
+ if (s->monitor_source)
+ pa_source_set_max_rewind(s->monitor_source, s->thread_info.max_rewind);
+}
+
+/* Called from IO thread */
+void pa_sink_set_max_request(pa_sink *s, size_t max_request) {
+ pa_sink_input *i;
+ void *state = NULL;
+
+ pa_sink_assert_ref(s);
+
+ if (max_request == s->thread_info.max_request)
+ return;
+
+ s->thread_info.max_request = max_request;
+
+ if (PA_SINK_IS_LINKED(s->thread_info.state)) {
+ while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
+ pa_sink_input_update_max_request(i, s->thread_info.max_request);
+ }
+}
+
+/* Called from IO thread */
+void pa_sink_invalidate_requested_latency(pa_sink *s) {
+ pa_sink_input *i;
+ void *state = NULL;
+
+ pa_sink_assert_ref(s);
+
+ s->thread_info.requested_latency_valid = FALSE;
+
+ 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 */
+void pa_sink_set_latency_range(pa_sink *s, pa_usec_t min_latency, pa_usec_t max_latency) {
+ 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 (max_latency == (pa_usec_t) -1)
+ max_latency = min_latency;
+
+ pa_assert(!min_latency || !max_latency ||
+ min_latency <= max_latency);
+
+ if (PA_SINK_IS_LINKED(s->state)) {
+ pa_usec_t r[2];
+
+ r[0] = min_latency;
+ 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;
+ }
+}
+
+/* Called from main thread */
+void pa_sink_get_latency_range(pa_sink *s, pa_usec_t *min_latency, pa_usec_t *max_latency) {
+ pa_sink_assert_ref(s);
+ pa_assert(min_latency);
+ pa_assert(max_latency);
+
+ if (PA_SINK_IS_LINKED(s->state)) {
+ pa_usec_t r[2] = { 0, 0 };
+
+ pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_LATENCY_RANGE, r, 0, NULL) == 0);
+
+ *min_latency = r[0];
+ *max_latency = r[1];
+ } else {
+ *min_latency = s->thread_info.min_latency;
+ *max_latency = s->thread_info.max_latency;
+ }
+}
+
+/* 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 *state = NULL;
+
+ pa_sink_assert_ref(s);
+
+ 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);
+
+ pa_sink_invalidate_requested_latency(s);
+
+ pa_source_update_latency_range(s->monitor_source, min_latency, max_latency);
+}
+
+size_t pa_sink_get_max_rewind(pa_sink *s) {
+ size_t r;
+ pa_sink_assert_ref(s);
+
+ if (!PA_SINK_IS_LINKED(s->state))
+ return s->thread_info.max_rewind;
+
+ pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_MAX_REWIND, &r, 0, NULL) == 0);
+
+ return r;
+}
+
+size_t pa_sink_get_max_request(pa_sink *s) {
+ size_t r;
+ pa_sink_assert_ref(s);
+
+ if (!PA_SINK_IS_LINKED(s->state))
+ return s->thread_info.max_request;
+
+ pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_MAX_REQUEST, &r, 0, NULL) == 0);
+
+ return r;
+}
diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h
index ce1d12c3..b73944e8 100644
--- a/src/pulsecore/sink.h
+++ b/src/pulsecore/sink.h
@@ -1,8 +1,6 @@
#ifndef foopulsesinkhfoo
#define foopulsesinkhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -33,7 +31,6 @@ typedef struct pa_sink pa_sink;
#include <pulse/channelmap.h>
#include <pulse/volume.h>
-#include <pulsecore/core-def.h>
#include <pulsecore/core.h>
#include <pulsecore/idxset.h>
#include <pulsecore/source.h>
@@ -52,11 +49,11 @@ typedef enum pa_sink_state {
PA_SINK_UNLINKED
} pa_sink_state_t;
-static inline pa_bool_t PA_SINK_OPENED(pa_sink_state_t x) {
+static inline pa_bool_t PA_SINK_IS_OPENED(pa_sink_state_t x) {
return x == PA_SINK_RUNNING || x == PA_SINK_IDLE;
}
-static inline pa_bool_t PA_SINK_LINKED(pa_sink_state_t x) {
+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;
}
@@ -69,7 +66,8 @@ struct pa_sink {
pa_sink_flags_t flags;
char *name;
- char *description, *driver; /* may be NULL */
+ char *driver; /* may be NULL */
+ pa_proplist *proplist;
pa_module *module; /* may be NULL */
@@ -82,29 +80,75 @@ struct pa_sink {
pa_cvolume volume;
pa_bool_t muted;
- pa_bool_t refresh_volume;
- pa_bool_t refresh_mute;
- int (*set_state)(pa_sink *s, pa_sink_state_t state); /* may be NULL */
- int (*set_volume)(pa_sink *s); /* dito */
- int (*get_volume)(pa_sink *s); /* dito */
- int (*get_mute)(pa_sink *s); /* dito */
- int (*set_mute)(pa_sink *s); /* dito */
- pa_usec_t (*get_latency)(pa_sink *s); /* dito */
+ pa_bool_t refresh_volume:1;
+ pa_bool_t refresh_muted:1;
pa_asyncmsgq *asyncmsgq;
pa_rtpoll *rtpoll;
+ pa_memchunk silence;
+
+ /* Called when the main loop requests a state change. Called from
+ * main loop context. If returns -1 the state change will be
+ * inhibited */
+ int (*set_state)(pa_sink *s, pa_sink_state_t state); /* may be NULL */
+
+ /* Callled when the volume is queried. Called from main loop
+ * context. If this is NULL a PA_SINK_MESSAGE_GET_VOLUME message
+ * will be sent to the IO thread instead. If refresh_volume is
+ * FALSE neither this function is called nor a message is sent. */
+ int (*get_volume)(pa_sink *s); /* may be NULL */
+
+ /* Called when the volume shall be changed. Called from main loop
+ * context. If this is NULL a PA_SINK_MESSAGE_SET_VOLUME message
+ * will be sent to the IO thread instead. */
+ int (*set_volume)(pa_sink *s); /* dito */
+
+ /* Called when the mute setting is queried. Called from main loop
+ * context. If this is NULL a PA_SINK_MESSAGE_GET_MUTE message
+ * will be sent to the IO thread instead. If refresh_mute is
+ * FALSE neither this function is called nor a message is sent.*/
+ int (*get_mute)(pa_sink *s); /* dito */
+
+ /* Called when the mute setting shall be changed. Called from main
+ * loop context. If this is NULL a PA_SINK_MESSAGE_SET_MUTE
+ * message will be sent to the IO thread instead. */
+ int (*set_mute)(pa_sink *s); /* dito */
+
+ /* Called when a rewind request is issued. Called from IO thread
+ * context. */
+ void (*request_rewind)(pa_sink *s); /* dito */
+
+ /* Called when a the requested latency is changed. Called from IO
+ * thread context. */
+ void (*update_requested_latency)(pa_sink *s); /* dito */
+
/* Contains copies of the above data so that the real-time worker
* thread can work without access locking */
struct {
pa_sink_state_t state;
pa_hashmap *inputs;
pa_cvolume soft_volume;
- pa_bool_t soft_muted;
- } thread_info;
+ pa_bool_t soft_muted:1;
+
+ pa_bool_t requested_latency_valid:1;
+ pa_usec_t requested_latency;
+
+ /* The number of bytes streams need to keep around as history to
+ * be able to satisfy every DMA buffer rewrite */
+ size_t max_rewind;
- pa_memblock *silence;
+ /* The number of bytes streams need to keep around to satisfy
+ * every DMA write request */
+ size_t max_request;
+
+ /* Maximum of what clients requested to rewind in this cycle */
+ size_t rewind_nbytes;
+
+ pa_usec_t min_latency; /* we won't go below this latency */
+ pa_usec_t max_latency; /* An upper limit for the latencies */
+ } thread_info;
void *userdata;
};
@@ -120,48 +164,79 @@ typedef enum pa_sink_message {
PA_SINK_MESSAGE_GET_MUTE,
PA_SINK_MESSAGE_SET_MUTE,
PA_SINK_MESSAGE_GET_LATENCY,
+ PA_SINK_MESSAGE_GET_REQUESTED_LATENCY,
PA_SINK_MESSAGE_SET_STATE,
- PA_SINK_MESSAGE_PING,
- PA_SINK_MESSAGE_REMOVE_INPUT_AND_BUFFER,
+ PA_SINK_MESSAGE_START_MOVE,
+ PA_SINK_MESSAGE_FINISH_MOVE,
PA_SINK_MESSAGE_ATTACH,
PA_SINK_MESSAGE_DETACH,
+ PA_SINK_MESSAGE_SET_LATENCY_RANGE,
+ PA_SINK_MESSAGE_GET_LATENCY_RANGE,
+ PA_SINK_MESSAGE_GET_MAX_REWIND,
+ PA_SINK_MESSAGE_GET_MAX_REQUEST,
PA_SINK_MESSAGE_MAX
} pa_sink_message_t;
+typedef struct pa_sink_new_data {
+ char *name;
+ pa_bool_t namereg_fail;
+ pa_proplist *proplist;
+
+ const char *driver;
+ pa_module *module;
+
+ pa_sample_spec sample_spec;
+ pa_bool_t sample_spec_is_set;
+ pa_channel_map channel_map;
+ pa_bool_t channel_map_is_set;
+
+ pa_cvolume volume;
+ pa_bool_t volume_is_set;
+ pa_bool_t muted;
+ pa_bool_t muted_is_set;
+} pa_sink_new_data;
+
+pa_sink_new_data* pa_sink_new_data_init(pa_sink_new_data *data);
+void pa_sink_new_data_set_name(pa_sink_new_data *data, const char *name);
+void pa_sink_new_data_set_sample_spec(pa_sink_new_data *data, const pa_sample_spec *spec);
+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_done(pa_sink_new_data *data);
+
/* To be called exclusively by the sink driver, from main context */
pa_sink* pa_sink_new(
pa_core *core,
- const char *driver,
- const char *name,
- int namereg_fail,
- const pa_sample_spec *spec,
- const pa_channel_map *map);
+ pa_sink_new_data *data,
+ pa_sink_flags_t flags);
void pa_sink_put(pa_sink *s);
void pa_sink_unlink(pa_sink* s);
-void pa_sink_set_module(pa_sink *sink, pa_module *m);
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_latency_range(pa_sink *s, pa_usec_t min_latency, pa_usec_t max_latency);
+
void pa_sink_detach(pa_sink *s);
void pa_sink_attach(pa_sink *s);
/* May be called by everyone, from main context */
+/* The returned value is supposed to be in the time domain of the sound card! */
pa_usec_t pa_sink_get_latency(pa_sink *s);
+pa_usec_t pa_sink_get_requested_latency(pa_sink *s);
+void pa_sink_get_latency_range(pa_sink *s, pa_usec_t *min_latency, pa_usec_t *max_latency);
+
+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);
-/* Sends a ping message to the sink thread, to make it wake up and
- * check for data to process even if there is no real message is
- * sent */
-void pa_sink_ping(pa_sink *s);
-
void pa_sink_set_volume(pa_sink *sink, const pa_cvolume *volume);
const pa_cvolume *pa_sink_get_volume(pa_sink *sink);
void pa_sink_set_mute(pa_sink *sink, pa_bool_t mute);
@@ -178,11 +253,24 @@ void pa_sink_render_full(pa_sink *s, size_t length, pa_memchunk *result);
void pa_sink_render_into(pa_sink*s, pa_memchunk *target);
void pa_sink_render_into_full(pa_sink *s, pa_memchunk *target);
-void pa_sink_skip(pa_sink *s, size_t length);
+void pa_sink_process_rewind(pa_sink *s, size_t nbytes);
int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk);
void pa_sink_attach_within_thread(pa_sink *s);
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_update_latency_range(pa_sink *s, pa_usec_t min_latency, pa_usec_t max_latency);
+
+/* To be called exclusively by sink input drivers, from IO context */
+
+void pa_sink_request_rewind(pa_sink*s, size_t nbytes);
+
+void pa_sink_invalidate_requested_latency(pa_sink *s);
+
#endif
diff --git a/src/pulsecore/sioman.c b/src/pulsecore/sioman.c
index 8d4c6fa7..7e5b186c 100644
--- a/src/pulsecore/sioman.c
+++ b/src/pulsecore/sioman.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/sioman.h b/src/pulsecore/sioman.h
index 49fffb34..d0cacc9b 100644
--- a/src/pulsecore/sioman.h
+++ b/src/pulsecore/sioman.h
@@ -1,8 +1,6 @@
#ifndef foosiomanhfoo
#define foosiomanhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/socket-client.c b/src/pulsecore/socket-client.c
index 5b5bc5ca..e69a63da 100644
--- a/src/pulsecore/socket-client.c
+++ b/src/pulsecore/socket-client.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -61,6 +59,7 @@
#include <pulsecore/core-error.h>
#include <pulsecore/socket-util.h>
#include <pulsecore/core-util.h>
+#include <pulsecore/socket-util.h>
#include <pulsecore/log.h>
#include <pulsecore/parseaddr.h>
#include <pulsecore/macro.h>
@@ -77,9 +76,9 @@ struct pa_socket_client {
pa_io_event *io_event;
pa_time_event *timeout_event;
pa_defer_event *defer_event;
- void (*callback)(pa_socket_client*c, pa_iochannel *io, void *userdata);
+ pa_socket_client_cb_t callback;
void *userdata;
- int local;
+ pa_bool_t local;
#ifdef HAVE_LIBASYNCNS
asyncns_t *asyncns;
asyncns_query_t * asyncns_query;
@@ -87,7 +86,7 @@ struct pa_socket_client {
#endif
};
-static pa_socket_client*pa_socket_client_new(pa_mainloop_api *m) {
+static pa_socket_client* socket_client_new(pa_mainloop_api *m) {
pa_socket_client *c;
pa_assert(m);
@@ -96,11 +95,11 @@ static pa_socket_client*pa_socket_client_new(pa_mainloop_api *m) {
c->mainloop = m;
c->fd = -1;
c->io_event = NULL;
- c->defer_event = NULL;
c->timeout_event = NULL;
+ c->defer_event = NULL;
c->callback = NULL;
c->userdata = NULL;
- c->local = 0;
+ c->local = FALSE;
#ifdef HAVE_LIBASYNCNS
c->asyncns = NULL;
@@ -119,15 +118,15 @@ static void free_events(pa_socket_client *c) {
c->io_event = NULL;
}
- if (c->defer_event) {
- c->mainloop->defer_free(c->defer_event);
- c->defer_event = NULL;
- }
-
if (c->timeout_event) {
c->mainloop->time_free(c->timeout_event);
c->timeout_event = NULL;
}
+
+ if (c->defer_event) {
+ c->mainloop->defer_free(c->defer_event);
+ c->defer_event = NULL;
+ }
}
static void do_call(pa_socket_client *c) {
@@ -177,7 +176,7 @@ finish:
pa_socket_client_unref(c);
}
-static void connect_fixed_cb(pa_mainloop_api *m, pa_defer_event *e, void *userdata) {
+static void connect_defer_cb(pa_mainloop_api *m, pa_defer_event *e, void *userdata) {
pa_socket_client *c = userdata;
pa_assert(m);
@@ -223,7 +222,7 @@ static int do_connect(pa_socket_client *c, const struct sockaddr *sa, socklen_t
pa_assert_se(c->io_event = c->mainloop->io_new(c->mainloop, c->fd, PA_IO_EVENT_OUTPUT, connect_io_cb, c));
} else
- pa_assert_se(c->defer_event = c->mainloop->defer_new(c->mainloop, connect_fixed_cb, c));
+ pa_assert_se(c->defer_event = c->mainloop->defer_new(c->mainloop, connect_defer_cb, c));
return 0;
}
@@ -252,8 +251,7 @@ pa_socket_client* pa_socket_client_new_unix(pa_mainloop_api *m, const char *file
memset(&sa, 0, sizeof(sa));
sa.sun_family = AF_UNIX;
- strncpy(sa.sun_path, filename, sizeof(sa.sun_path)-1);
- sa.sun_path[sizeof(sa.sun_path) - 1] = 0;
+ pa_strlcpy(sa.sun_path, filename, sizeof(sa.sun_path));
return pa_socket_client_new_sockaddr(m, (struct sockaddr*) &sa, sizeof(sa));
}
@@ -271,22 +269,7 @@ static int sockaddr_prepare(pa_socket_client *c, const struct sockaddr *sa, size
pa_assert(sa);
pa_assert(salen);
- switch (sa->sa_family) {
- case AF_UNIX:
- c->local = 1;
- break;
-
- case AF_INET:
- c->local = ((const struct sockaddr_in*) sa)->sin_addr.s_addr == INADDR_LOOPBACK;
- break;
-
- case AF_INET6:
- c->local = memcmp(&((const struct sockaddr_in6*) sa)->sin6_addr, &in6addr_loopback, sizeof(struct in6_addr)) == 0;
- break;
-
- default:
- c->local = 0;
- }
+ c->local = pa_socket_address_is_local(sa);
if ((c->fd = socket(sa->sa_family, SOCK_STREAM, 0)) < 0) {
pa_log("socket(): %s", pa_cstrerror(errno));
@@ -294,6 +277,7 @@ static int sockaddr_prepare(pa_socket_client *c, const struct sockaddr *sa, size
}
pa_make_fd_cloexec(c->fd);
+
if (sa->sa_family == AF_INET || sa->sa_family == AF_INET6)
pa_make_tcp_socket_low_delay(c->fd);
else
@@ -312,7 +296,7 @@ pa_socket_client* pa_socket_client_new_sockaddr(pa_mainloop_api *m, const struct
pa_assert(sa);
pa_assert(salen > 0);
- pa_assert_se(c = pa_socket_client_new(m));
+ pa_assert_se(c = socket_client_new(m));
if (sockaddr_prepare(c, sa, salen) < 0)
goto fail;
@@ -361,7 +345,7 @@ pa_socket_client* pa_socket_client_ref(pa_socket_client *c) {
return c;
}
-void pa_socket_client_set_callback(pa_socket_client *c, void (*on_connection)(pa_socket_client *c, pa_iochannel*io, void *userdata), void *userdata) {
+void pa_socket_client_set_callback(pa_socket_client *c, pa_socket_client_cb_t on_connection, void *userdata) {
pa_assert(c);
pa_assert(PA_REFCNT_VALUE(c) >= 1);
@@ -489,23 +473,22 @@ pa_socket_client* pa_socket_client_new_string(pa_mainloop_api *m, const char*nam
hints.ai_family = a.type == PA_PARSED_ADDRESS_TCP4 ? PF_INET : (a.type == PA_PARSED_ADDRESS_TCP6 ? PF_INET6 : PF_UNSPEC);
hints.ai_socktype = SOCK_STREAM;
-#ifdef HAVE_LIBASYNCNS
+#if defined(HAVE_LIBASYNCNS)
{
asyncns_t *asyncns;
if (!(asyncns = asyncns_new(1)))
goto finish;
- c = pa_socket_client_new(m);
+ pa_assert_se(c = socket_client_new(m));
c->asyncns = asyncns;
c->asyncns_io_event = m->io_new(m, asyncns_fd(c->asyncns), PA_IO_EVENT_INPUT, asyncns_cb, c);
c->asyncns_query = asyncns_getaddrinfo(c->asyncns, a.path_or_host, port, &hints);
pa_assert(c->asyncns_query);
start_timeout(c);
}
-#else /* HAVE_LIBASYNCNS */
+#elif defined(HAVE_GETADDRINFO)
{
-#ifdef HAVE_GETADDRINFO
int ret;
struct addrinfo *res = NULL;
@@ -520,7 +503,9 @@ pa_socket_client* pa_socket_client_new_string(pa_mainloop_api *m, const char*nam
}
freeaddrinfo(res);
-#else /* HAVE_GETADDRINFO */
+ }
+#else
+ {
struct hostent *host = NULL;
struct sockaddr_in s;
@@ -546,7 +531,6 @@ pa_socket_client* pa_socket_client_new_string(pa_mainloop_api *m, const char*nam
if ((c = pa_socket_client_new_sockaddr(m, (struct sockaddr*)&s, sizeof(s))))
start_timeout(c);
-#endif /* HAVE_GETADDRINFO */
}
#endif /* HAVE_LIBASYNCNS */
}
@@ -561,7 +545,7 @@ finish:
/* Return non-zero when the target sockaddr is considered
local. "local" means UNIX socket or TCP socket on localhost. Other
local IP addresses are not considered local. */
-int pa_socket_client_is_local(pa_socket_client *c) {
+pa_bool_t pa_socket_client_is_local(pa_socket_client *c) {
pa_assert(c);
pa_assert(PA_REFCNT_VALUE(c) >= 1);
diff --git a/src/pulsecore/socket-client.h b/src/pulsecore/socket-client.h
index b1d58eff..9ceeaddc 100644
--- a/src/pulsecore/socket-client.h
+++ b/src/pulsecore/socket-client.h
@@ -1,8 +1,6 @@
#ifndef foosocketclienthfoo
#define foosocketclienthfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -34,17 +32,19 @@ struct sockaddr;
typedef struct pa_socket_client pa_socket_client;
+typedef void (*pa_socket_client_cb_t)(pa_socket_client *c, pa_iochannel*io, void *userdata);
+
pa_socket_client* pa_socket_client_new_ipv4(pa_mainloop_api *m, uint32_t address, uint16_t port);
pa_socket_client* pa_socket_client_new_ipv6(pa_mainloop_api *m, uint8_t address[16], uint16_t port);
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);
-void pa_socket_client_unref(pa_socket_client *c);
pa_socket_client* pa_socket_client_ref(pa_socket_client *c);
+void pa_socket_client_unref(pa_socket_client *c);
-void pa_socket_client_set_callback(pa_socket_client *c, void (*on_connection)(pa_socket_client *c, pa_iochannel*io, void *userdata), void *userdata);
+void pa_socket_client_set_callback(pa_socket_client *c, pa_socket_client_cb_t on_connection, void *userdata);
-int pa_socket_client_is_local(pa_socket_client *c);
+pa_bool_t pa_socket_client_is_local(pa_socket_client *c);
#endif
diff --git a/src/pulsecore/socket-server.c b/src/pulsecore/socket-server.c
index e0d2f164..9885a02b 100644
--- a/src/pulsecore/socket-server.c
+++ b/src/pulsecore/socket-server.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -83,7 +81,7 @@ struct pa_socket_server {
char *filename;
char *tcpwrap_service;
- void (*on_connection)(pa_socket_server*s, pa_iochannel *io, void *userdata);
+ pa_socket_server_on_connection_cb_t on_connection;
void *userdata;
pa_io_event *io_event;
@@ -91,11 +89,11 @@ struct pa_socket_server {
enum { SOCKET_SERVER_GENERIC, SOCKET_SERVER_IPV4, SOCKET_SERVER_UNIX, SOCKET_SERVER_IPV6 } type;
};
-static void callback(pa_mainloop_api *mainloop, pa_io_event *e, int fd, PA_GCC_UNUSED pa_io_event_flags_t f, void *userdata) {
+static void callback(pa_mainloop_api *mainloop, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata) {
pa_socket_server *s = userdata;
pa_iochannel *io;
int nfd;
-
+
pa_assert(s);
pa_assert(PA_REFCNT_VALUE(s) >= 1);
pa_assert(s->mainloop == mainloop);
@@ -150,7 +148,7 @@ finish:
pa_socket_server* pa_socket_server_new(pa_mainloop_api *m, int fd) {
pa_socket_server *s;
-
+
pa_assert(m);
pa_assert(fd >= 0);
@@ -173,7 +171,7 @@ pa_socket_server* pa_socket_server_new(pa_mainloop_api *m, int fd) {
pa_socket_server* pa_socket_server_ref(pa_socket_server *s) {
pa_assert(s);
pa_assert(PA_REFCNT_VALUE(s) >= 1);
-
+
PA_REFCNT_INC(s);
return s;
}
@@ -195,9 +193,9 @@ pa_socket_server* pa_socket_server_new_unix(pa_mainloop_api *m, const char *file
pa_make_fd_cloexec(fd);
+ memset(&sa, 0, sizeof(sa));
sa.sun_family = AF_UNIX;
- strncpy(sa.sun_path, filename, sizeof(sa.sun_path)-1);
- sa.sun_path[sizeof(sa.sun_path) - 1] = 0;
+ pa_strlcpy(sa.sun_path, filename, sizeof(sa.sun_path));
pa_make_socket_low_delay(fd);
@@ -295,7 +293,7 @@ pa_socket_server* pa_socket_server_new_ipv6(pa_mainloop_api *m, const uint8_t ad
pa_socket_server *ss;
int fd = -1;
struct sockaddr_in6 sa;
- int on = 1;
+ int on;
pa_assert(m);
pa_assert(port > 0);
@@ -308,11 +306,13 @@ pa_socket_server* pa_socket_server_new_ipv6(pa_mainloop_api *m, const uint8_t ad
pa_make_fd_cloexec(fd);
#ifdef IPV6_V6ONLY
+ on = 1;
if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) < 0)
pa_log("setsockopt(IPPROTO_IPV6, IPV6_V6ONLY): %s", pa_cstrerror(errno));
#endif
#ifdef SO_REUSEADDR
+ on = 1;
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
pa_log("setsockopt(SOL_SOCKET, SO_REUSEADDR, 1): %s", pa_cstrerror(errno));
#endif
@@ -426,7 +426,7 @@ void pa_socket_server_unref(pa_socket_server *s) {
socket_server_free(s);
}
-void pa_socket_server_set_callback(pa_socket_server*s, void (*on_connection)(pa_socket_server*s, pa_iochannel *io, void *userdata), void *userdata) {
+void pa_socket_server_set_callback(pa_socket_server*s, pa_socket_server_on_connection_cb_t on_connection, void *userdata) {
pa_assert(s);
pa_assert(PA_REFCNT_VALUE(s) >= 1);
@@ -507,7 +507,6 @@ char *pa_socket_server_get_address(pa_socket_server *s, char *c, size_t l) {
}
pa_snprintf(c, l, "tcp:[%s]:%u", ip, (unsigned) ntohs(sa.sin_port));
-
}
return c;
diff --git a/src/pulsecore/socket-server.h b/src/pulsecore/socket-server.h
index 777599e5..1edfb432 100644
--- a/src/pulsecore/socket-server.h
+++ b/src/pulsecore/socket-server.h
@@ -1,8 +1,6 @@
#ifndef foosocketserverhfoo
#define foosocketserverhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -47,7 +45,9 @@ pa_socket_server* pa_socket_server_new_ipv6_string(pa_mainloop_api *m, const cha
void pa_socket_server_unref(pa_socket_server*s);
pa_socket_server* pa_socket_server_ref(pa_socket_server *s);
-void pa_socket_server_set_callback(pa_socket_server*s, void (*on_connection)(pa_socket_server*s, pa_iochannel *io, void *userdata), void *userdata);
+typedef void (*pa_socket_server_on_connection_cb_t)(pa_socket_server*s, pa_iochannel *io, void *userdata);
+
+void pa_socket_server_set_callback(pa_socket_server*s, pa_socket_server_on_connection_cb_t connection_cb, void *userdata);
char *pa_socket_server_get_address(pa_socket_server *s, char *c, size_t l);
diff --git a/src/pulsecore/socket-util.c b/src/pulsecore/socket-util.c
index 085edeb8..f721f699 100644
--- a/src/pulsecore/socket-util.c
+++ b/src/pulsecore/socket-util.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -129,8 +127,8 @@ void pa_socket_peer_to_string(int fd, char *c, size_t l) {
return;
#endif
}
-
}
+
#ifndef OS_IS_WIN32
pa_snprintf(c, l, "Unknown network client");
return;
@@ -144,7 +142,7 @@ void pa_socket_peer_to_string(int fd, char *c, size_t l) {
}
void pa_make_socket_low_delay(int fd) {
-
+
#ifdef SO_PRIORITY
int priority;
pa_assert(fd >= 0);
@@ -157,7 +155,7 @@ void pa_make_socket_low_delay(int fd) {
void pa_make_tcp_socket_low_delay(int fd) {
pa_assert(fd >= 0);
-
+
pa_make_socket_low_delay(fd);
#if defined(SOL_TCP) || defined(IPPROTO_TCP)
@@ -171,7 +169,7 @@ void pa_make_tcp_socket_low_delay(int fd) {
pa_log_warn("TCP_NODELAY failed: %s", pa_cstrerror(errno));
}
#endif
-
+
#if defined(IPTOS_LOWDELAY) && defined(IP_TOS) && (defined(SOL_IP) || defined(IPPROTO_IP))
{
int tos = IPTOS_LOWDELAY;
@@ -187,9 +185,9 @@ void pa_make_tcp_socket_low_delay(int fd) {
void pa_make_udp_socket_low_delay(int fd) {
pa_assert(fd >= 0);
-
+
pa_make_socket_low_delay(fd);
-
+
#if defined(IPTOS_LOWDELAY) && defined(IP_TOS) && (defined(SOL_IP) || defined(IPPROTO_IP))
{
int tos = IPTOS_LOWDELAY;
@@ -284,3 +282,40 @@ int pa_unix_socket_remove_stale(const char *fn) {
}
#endif /* HAVE_SYS_UN_H */
+
+
+pa_bool_t pa_socket_address_is_local(const struct sockaddr *sa) {
+ pa_assert(sa);
+
+ switch (sa->sa_family) {
+ case AF_UNIX:
+ return TRUE;
+
+ case AF_INET:
+ return ((const struct sockaddr_in*) sa)->sin_addr.s_addr == INADDR_LOOPBACK;
+
+ case AF_INET6:
+ return memcmp(&((const struct sockaddr_in6*) sa)->sin6_addr, &in6addr_loopback, sizeof(struct in6_addr)) == 0;
+
+ default:
+ return FALSE;
+ }
+}
+
+pa_bool_t pa_socket_is_local(int fd) {
+
+ union {
+ struct sockaddr sa;
+ struct sockaddr_in in;
+ struct sockaddr_in6 in6;
+#ifdef HAVE_SYS_UN_H
+ struct sockaddr_un un;
+#endif
+ } sa;
+ socklen_t sa_len = sizeof(sa);
+
+ if (getpeername(fd, &sa.sa, &sa_len) < 0)
+ return FALSE;
+
+ return pa_socket_address_is_local(&sa.sa);
+}
diff --git a/src/pulsecore/socket-util.h b/src/pulsecore/socket-util.h
index a0344c68..7a40285a 100644
--- a/src/pulsecore/socket-util.h
+++ b/src/pulsecore/socket-util.h
@@ -1,8 +1,6 @@
#ifndef foosocketutilhfoo
#define foosocketutilhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -26,6 +24,9 @@
***/
#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <pulsecore/macro.h>
void pa_socket_peer_to_string(int fd, char *c, size_t l);
@@ -39,4 +40,7 @@ int pa_socket_set_rcvbuf(int fd, size_t l);
int pa_unix_socket_is_stale(const char *fn);
int pa_unix_socket_remove_stale(const char *fn);
+pa_bool_t pa_socket_address_is_local(const struct sockaddr *sa);
+pa_bool_t pa_socket_is_local(int fd);
+
#endif
diff --git a/src/pulsecore/sound-file-stream.c b/src/pulsecore/sound-file-stream.c
index d9f11a26..8eedf830 100644
--- a/src/pulsecore/sound-file-stream.c
+++ b/src/pulsecore/sound-file-stream.c
@@ -1,9 +1,7 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
- Copyright 2004-2006 Lennart Poettering
+ Copyright 2004-2008 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
@@ -35,23 +33,30 @@
#include <sndfile.h>
#include <pulse/xmalloc.h>
+#include <pulse/util.h>
#include <pulsecore/core-error.h>
#include <pulsecore/sink-input.h>
#include <pulsecore/log.h>
#include <pulsecore/thread-mq.h>
#include <pulsecore/core-util.h>
+#include <pulsecore/sample-util.h>
#include "sound-file-stream.h"
+#define MEMBLOCKQ_MAXLENGTH (16*1024*1024)
+
typedef struct file_stream {
pa_msgobject parent;
pa_core *core;
- SNDFILE *sndfile;
pa_sink_input *sink_input;
- pa_memchunk memchunk;
+
+ SNDFILE *sndfile;
sf_count_t (*readf_function)(SNDFILE *sndfile, void *ptr, sf_count_t frames);
- size_t drop;
+
+ /* We need this memblockq here to easily fulfill rewind requests
+ * (even beyond the file start!) */
+ pa_memblockq *memblockq;
} file_stream;
enum {
@@ -62,29 +67,28 @@ PA_DECLARE_CLASS(file_stream);
#define FILE_STREAM(o) (file_stream_cast(o))
static PA_DEFINE_CHECK_TYPE(file_stream, pa_msgobject);
+/* Called from main context */
static void file_stream_unlink(file_stream *u) {
pa_assert(u);
if (!u->sink_input)
return;
-
+
pa_sink_input_unlink(u->sink_input);
-
pa_sink_input_unref(u->sink_input);
u->sink_input = NULL;
-
+
/* Make sure we don't decrease the ref count twice. */
file_stream_unref(u);
}
+/* Called from main context */
static void file_stream_free(pa_object *o) {
file_stream *u = FILE_STREAM(o);
pa_assert(u);
- file_stream_unlink(u);
-
- if (u->memchunk.memblock)
- pa_memblock_unref(u->memchunk.memblock);
+ if (u->memblockq)
+ pa_memblockq_free(u->memblockq);
if (u->sndfile)
sf_close(u->sndfile);
@@ -92,10 +96,11 @@ static void file_stream_free(pa_object *o) {
pa_xfree(u);
}
+/* Called from main context */
static int file_stream_process_msg(pa_msgobject *o, int code, void*userdata, int64_t offset, pa_memchunk *chunk) {
file_stream *u = FILE_STREAM(o);
file_stream_assert_ref(u);
-
+
switch (code) {
case FILE_STREAM_MESSAGE_UNLINK:
file_stream_unlink(u);
@@ -105,117 +110,125 @@ static int file_stream_process_msg(pa_msgobject *o, int code, void*userdata, int
return 0;
}
+/* Called from main context */
static void sink_input_kill_cb(pa_sink_input *i) {
+ file_stream *u;
+
pa_sink_input_assert_ref(i);
-
- file_stream_unlink(FILE_STREAM(i->userdata));
+ u = FILE_STREAM(i->userdata);
+ file_stream_assert_ref(u);
+
+ file_stream_unlink(u);
}
-static int sink_input_peek_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) {
+/* Called from IO thread context */
+static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t state) {
file_stream *u;
-
- pa_assert(i);
+
+ pa_sink_input_assert_ref(i);
+ u = FILE_STREAM(i->userdata);
+ file_stream_assert_ref(u);
+
+ /* If we are added for the first time, ask for a rewinding so that
+ * we are heard right-away. */
+ if (PA_SINK_INPUT_IS_LINKED(state) &&
+ i->thread_info.state == PA_SINK_INPUT_INIT)
+ pa_sink_input_request_rewind(i, 0, FALSE, TRUE);
+}
+
+/* Called from IO thread context */
+static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) {
+ file_stream *u;
+
+ pa_sink_input_assert_ref(i);
pa_assert(chunk);
u = FILE_STREAM(i->userdata);
file_stream_assert_ref(u);
- if (!u->sndfile)
+ if (!u->memblockq)
return -1;
-
+
for (;;) {
-
- if (!u->memchunk.memblock) {
-
- u->memchunk.memblock = pa_memblock_new(i->sink->core->mempool, length);
- u->memchunk.index = 0;
-
- if (u->readf_function) {
- sf_count_t n;
- void *p;
- size_t fs = pa_frame_size(&i->sample_spec);
-
- p = pa_memblock_acquire(u->memchunk.memblock);
- n = u->readf_function(u->sndfile, p, length/fs);
- pa_memblock_release(u->memchunk.memblock);
-
- if (n <= 0)
- n = 0;
-
- u->memchunk.length = n * fs;
- } else {
- sf_count_t n;
- void *p;
-
- p = pa_memblock_acquire(u->memchunk.memblock);
- n = sf_read_raw(u->sndfile, p, length);
- pa_memblock_release(u->memchunk.memblock);
-
- if (n <= 0)
- n = 0;
-
- u->memchunk.length = n;
- }
-
- if (u->memchunk.length <= 0) {
-
- pa_memblock_unref(u->memchunk.memblock);
- pa_memchunk_reset(&u->memchunk);
-
- pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(u), FILE_STREAM_MESSAGE_UNLINK, NULL, 0, NULL, NULL);
-
- sf_close(u->sndfile);
- u->sndfile = NULL;
-
- return -1;
- }
+ pa_memchunk tchunk;
+ size_t fs;
+ void *p;
+ sf_count_t n;
+
+ if (pa_memblockq_peek(u->memblockq, chunk) >= 0) {
+ chunk->length = PA_MIN(chunk->length, length);
+ pa_memblockq_drop(u->memblockq, chunk->length);
+ return 0;
+ }
+
+ if (!u->sndfile)
+ break;
+
+ tchunk.memblock = pa_memblock_new(i->sink->core->mempool, length);
+ tchunk.index = 0;
+
+ p = pa_memblock_acquire(tchunk.memblock);
+
+ if (u->readf_function) {
+ fs = pa_frame_size(&i->sample_spec);
+ n = u->readf_function(u->sndfile, p, length/fs);
+ } else {
+ fs = 1;
+ n = sf_read_raw(u->sndfile, p, length);
}
- pa_assert(u->memchunk.memblock);
- pa_assert(u->memchunk.length > 0);
+ pa_memblock_release(tchunk.memblock);
- if (u->drop < u->memchunk.length) {
- u->memchunk.index += u->drop;
- u->memchunk.length -= u->drop;
- u->drop = 0;
+ if (n <= 0) {
+ pa_memblock_unref(tchunk.memblock);
+
+ sf_close(u->sndfile);
+ u->sndfile = NULL;
break;
}
-
- u->drop -= u->memchunk.length;
- pa_memblock_unref(u->memchunk.memblock);
- pa_memchunk_reset(&u->memchunk);
+
+ tchunk.length = n * fs;
+
+ pa_memblockq_push(u->memblockq, &tchunk);
+ pa_memblock_unref(tchunk.memblock);
}
- *chunk = u->memchunk;
- pa_memblock_ref(chunk->memblock);
-
- pa_assert(chunk->length > 0);
- pa_assert(u->drop <= 0);
-
- return 0;
+ if (pa_sink_input_safe_to_remove(i)) {
+ pa_memblockq_free(u->memblockq);
+ u->memblockq = NULL;
+
+ pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(u), FILE_STREAM_MESSAGE_UNLINK, NULL, 0, NULL, NULL);
+ }
+
+ return -1;
+ }
+
+static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
+ file_stream *u;
+
+ pa_sink_input_assert_ref(i);
+ pa_assert(nbytes > 0);
+ u = FILE_STREAM(i->userdata);
+ file_stream_assert_ref(u);
+
+ pa_log("backwards %lu", (unsigned long) nbytes);
+
+ if (!u->memblockq)
+ return;
+
+ pa_memblockq_rewind(u->memblockq, nbytes);
}
-static void sink_input_drop_cb(pa_sink_input *i, size_t length) {
+static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) {
file_stream *u;
- pa_assert(i);
- pa_assert(length > 0);
+ pa_sink_input_assert_ref(i);
u = FILE_STREAM(i->userdata);
file_stream_assert_ref(u);
-
- if (u->memchunk.memblock) {
- if (length < u->memchunk.length) {
- u->memchunk.index += length;
- u->memchunk.length -= length;
- return;
- }
+ if (!u->memblockq)
+ return;
- length -= u->memchunk.length;
- pa_memblock_unref(u->memchunk.memblock);
- pa_memchunk_reset(&u->memchunk);
- }
-
- u->drop += length;
+ pa_memblockq_set_maxrewind(u->memblockq, nbytes);
}
int pa_play_file(
@@ -228,7 +241,7 @@ int pa_play_file(
pa_sample_spec ss;
pa_sink_input_new_data data;
int fd;
-
+
pa_assert(sink);
pa_assert(fname);
@@ -237,10 +250,9 @@ int pa_play_file(
u->parent.process_msg = file_stream_process_msg;
u->core = sink->core;
u->sink_input = NULL;
- pa_memchunk_reset(&u->memchunk);
u->sndfile = NULL;
u->readf_function = NULL;
- u->drop = 0;
+ u->memblockq = NULL;
memset(&sfinfo, 0, sizeof(sfinfo));
@@ -264,14 +276,14 @@ int pa_play_file(
goto fail;
} else
pa_log_debug("POSIX_FADV_SEQUENTIAL succeeded.");
-
+
if (posix_fadvise(fd, 0, 0, POSIX_FADV_WILLNEED) < 0) {
pa_log_warn("POSIX_FADV_WILLNEED failed: %s", pa_cstrerror(errno));
goto fail;
} else
pa_log_debug("POSIX_FADV_WILLNEED succeeded.");
#endif
-
+
if (!(u->sndfile = sf_open_fd(fd, SFM_READ, &sfinfo, 1))) {
pa_log("Failed to open file %s", fname);
pa_close(fd);
@@ -312,23 +324,31 @@ int pa_play_file(
pa_sink_input_new_data_init(&data);
data.sink = sink;
data.driver = __FILE__;
- data.name = fname;
pa_sink_input_new_data_set_sample_spec(&data, &ss);
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);
+
+ u->sink_input = pa_sink_input_new(sink->core, &data, 0);
+ pa_sink_input_new_data_done(&data);
- if (!(u->sink_input = pa_sink_input_new(sink->core, &data, 0)))
+ if (!u->sink_input)
goto fail;
- u->sink_input->peek = sink_input_peek_cb;
- u->sink_input->drop = sink_input_drop_cb;
+ u->sink_input->pop = sink_input_pop_cb;
+ u->sink_input->process_rewind = sink_input_process_rewind_cb;
+ u->sink_input->update_max_rewind = sink_input_update_max_rewind_cb;
u->sink_input->kill = sink_input_kill_cb;
+ u->sink_input->state_change = sink_input_state_change_cb;
u->sink_input->userdata = u;
+ u->memblockq = pa_memblockq_new(0, MEMBLOCKQ_MAXLENGTH, 0, pa_frame_size(&ss), 1, 1, 0, NULL);
+
pa_sink_input_put(u->sink_input);
/* The reference to u is dangling here, because we want to keep
* this stream around until it is fully played. */
-
+
return 0;
fail:
diff --git a/src/pulsecore/sound-file-stream.h b/src/pulsecore/sound-file-stream.h
index 189e242d..4cc69146 100644
--- a/src/pulsecore/sound-file-stream.h
+++ b/src/pulsecore/sound-file-stream.h
@@ -1,8 +1,6 @@
#ifndef foosoundfilestreamhfoo
#define foosoundfilestreamhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/sound-file.c b/src/pulsecore/sound-file.c
index 50352930..3183ede6 100644
--- a/src/pulsecore/sound-file.c
+++ b/src/pulsecore/sound-file.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -47,7 +45,7 @@ int pa_sound_file_load(
pa_sample_spec *ss,
pa_channel_map *map,
pa_memchunk *chunk) {
-
+
SNDFILE *sf = NULL;
SF_INFO sfinfo;
int ret = -1;
@@ -79,7 +77,7 @@ int pa_sound_file_load(
} else
pa_log_debug("POSIX_FADV_SEQUENTIAL succeeded.");
#endif
-
+
if (!(sf = sf_open_fd(fd, SFM_READ, &sfinfo, 1))) {
pa_log("Failed to open file %s", fname);
pa_close(fd);
@@ -119,7 +117,7 @@ int pa_sound_file_load(
}
if (map)
- pa_channel_map_init_auto(map, ss->channels, PA_CHANNEL_MAP_DEFAULT);
+ pa_channel_map_init_extend(map, ss->channels, PA_CHANNEL_MAP_DEFAULT);
if ((l = pa_frame_size(ss) * sfinfo.frames) > PA_SCACHE_ENTRY_SIZE_MAX) {
pa_log("File too large");
@@ -155,13 +153,13 @@ finish:
}
int pa_sound_file_too_big_to_cache(const char *fname) {
-
+
SNDFILE*sf = NULL;
SF_INFO sfinfo;
pa_sample_spec ss;
pa_assert(fname);
-
+
if (!(sf = sf_open(fname, SFM_READ, &sfinfo))) {
pa_log("Failed to open file %s", fname);
return -1;
@@ -198,7 +196,7 @@ int pa_sound_file_too_big_to_cache(const char *fname) {
pa_log("Unsupported sample format in file %s", fname);
return -1;
}
-
+
if ((pa_frame_size(&ss) * sfinfo.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 46763bd8..e4d703d3 100644
--- a/src/pulsecore/sound-file.h
+++ b/src/pulsecore/sound-file.h
@@ -1,8 +1,6 @@
#ifndef soundfilehfoo
#define soundfilehfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c
index 2a902dc2..3d1abe30 100644
--- a/src/pulsecore/source-output.c
+++ b/src/pulsecore/source-output.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -32,12 +30,16 @@
#include <pulse/utf8.h>
#include <pulse/xmalloc.h>
+#include <pulsecore/sample-util.h>
#include <pulsecore/core-subscribe.h>
#include <pulsecore/log.h>
#include <pulsecore/namereg.h>
+#include <pulsecore/core-util.h>
#include "source-output.h"
+#define MEMBLOCKQ_MAXLENGTH (32*1024*1024)
+
static PA_DEFINE_CHECK_TYPE(pa_source_output, pa_msgobject);
static void source_output_free(pa_object* mo);
@@ -47,9 +49,18 @@ pa_source_output_new_data* pa_source_output_new_data_init(pa_source_output_new_d
memset(data, 0, sizeof(*data));
data->resample_method = PA_RESAMPLER_INVALID;
+ data->proplist = pa_proplist_new();
+
return data;
}
+void pa_source_output_new_data_set_sample_spec(pa_source_output_new_data *data, const pa_sample_spec *spec) {
+ pa_assert(data);
+
+ if ((data->sample_spec_is_set = !!spec))
+ data->sample_spec = *spec;
+}
+
void pa_source_output_new_data_set_channel_map(pa_source_output_new_data *data, const pa_channel_map *map) {
pa_assert(data);
@@ -57,13 +68,31 @@ void pa_source_output_new_data_set_channel_map(pa_source_output_new_data *data,
data->channel_map = *map;
}
-void pa_source_output_new_data_set_sample_spec(pa_source_output_new_data *data, const pa_sample_spec *spec) {
+void pa_source_output_new_data_done(pa_source_output_new_data *data) {
pa_assert(data);
- if ((data->sample_spec_is_set = !!spec))
- data->sample_spec = *spec;
+ pa_proplist_free(data->proplist);
}
+/* Called from main context */
+static void reset_callbacks(pa_source_output *o) {
+ pa_assert(o);
+
+ o->push = NULL;
+ o->process_rewind = NULL;
+ o->update_max_rewind = NULL;
+ o->update_source_requested_latency = NULL;
+ o->update_source_latency_range = NULL;
+ o->attach = NULL;
+ o->detach = NULL;
+ o->suspend = NULL;
+ o->moved = NULL;
+ o->kill = NULL;
+ o->get_latency = NULL;
+ o->state_change = NULL;
+}
+
+/* Called from main context */
pa_source_output* pa_source_output_new(
pa_core *core,
pa_source_output_new_data *data,
@@ -71,7 +100,7 @@ pa_source_output* pa_source_output_new(
pa_source_output *o;
pa_resampler *resampler = NULL;
- char st[PA_SAMPLE_SPEC_SNPRINT_MAX];
+ char st[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
pa_assert(core);
pa_assert(data);
@@ -80,14 +109,15 @@ pa_source_output* pa_source_output_new(
return NULL;
pa_return_null_if_fail(!data->driver || pa_utf8_valid(data->driver));
- pa_return_null_if_fail(!data->name || pa_utf8_valid(data->name));
if (!data->source)
- data->source = pa_namereg_get(core, NULL, PA_NAMEREG_SOURCE, 1);
+ data->source = pa_namereg_get(core, NULL, PA_NAMEREG_SOURCE, TRUE);
pa_return_null_if_fail(data->source);
pa_return_null_if_fail(pa_source_get_state(data->source) != PA_SOURCE_UNLINKED);
+ pa_return_null_if_fail(!data->direct_on_input || data->direct_on_input->sink == data->source->monitor_of);
+
if (!data->sample_spec_is_set)
data->sample_spec = data->source->sample_spec;
@@ -97,17 +127,34 @@ pa_source_output* pa_source_output_new(
if (data->source->channel_map.channels == data->sample_spec.channels)
data->channel_map = data->source->channel_map;
else
- pa_channel_map_init_auto(&data->channel_map, data->sample_spec.channels, PA_CHANNEL_MAP_DEFAULT);
+ pa_return_null_if_fail(pa_channel_map_init_auto(&data->channel_map, data->sample_spec.channels, PA_CHANNEL_MAP_DEFAULT));
}
pa_return_null_if_fail(pa_channel_map_valid(&data->channel_map));
pa_return_null_if_fail(data->channel_map.channels == data->sample_spec.channels);
+ if (flags & PA_SOURCE_OUTPUT_FIX_FORMAT)
+ data->sample_spec.format = data->source->sample_spec.format;
+
+ if (flags & PA_SOURCE_OUTPUT_FIX_RATE)
+ data->sample_spec.rate = data->source->sample_spec.rate;
+
+ if (flags & PA_SOURCE_OUTPUT_FIX_CHANNELS) {
+ data->sample_spec.channels = data->source->sample_spec.channels;
+ data->channel_map = data->source->channel_map;
+ }
+
+ pa_assert(pa_sample_spec_valid(&data->sample_spec));
+ pa_assert(pa_channel_map_valid(&data->channel_map));
+
if (data->resample_method == PA_RESAMPLER_INVALID)
data->resample_method = core->resample_method;
pa_return_null_if_fail(data->resample_method < PA_RESAMPLER_MAX);
+ if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_FIXATE], data) < 0)
+ return NULL;
+
if (pa_idxset_size(data->source->outputs) >= PA_MAX_OUTPUTS_PER_SOURCE) {
pa_log("Failed to create source output: too many outputs per source.");
return NULL;
@@ -122,7 +169,9 @@ pa_source_output* pa_source_output_new(
&data->source->sample_spec, &data->source->channel_map,
&data->sample_spec, &data->channel_map,
data->resample_method,
- !!(flags & PA_SOURCE_OUTPUT_VARIABLE_RATE)))) {
+ ((flags & PA_SOURCE_OUTPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0) |
+ ((flags & PA_SOURCE_OUTPUT_NO_REMAP) ? PA_RESAMPLER_NO_REMAP : 0) |
+ (core->disable_remixing || (flags & PA_SOURCE_OUTPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0)))) {
pa_log_warn("Unsupported resampling operation.");
return NULL;
}
@@ -137,7 +186,7 @@ pa_source_output* pa_source_output_new(
o->core = core;
o->state = PA_SOURCE_OUTPUT_INIT;
o->flags = flags;
- o->name = pa_xstrdup(data->name);
+ o->proplist = pa_proplist_copy(data->proplist);
o->driver = pa_xstrdup(data->driver);
o->module = data->module;
o->source = data->source;
@@ -147,49 +196,68 @@ pa_source_output* pa_source_output_new(
o->sample_spec = data->sample_spec;
o->channel_map = data->channel_map;
- o->push = NULL;
- o->kill = NULL;
- o->get_latency = NULL;
- o->detach = NULL;
- o->attach = NULL;
- o->suspend = NULL;
+ o->direct_on_input = data->direct_on_input;
+
+ reset_callbacks(o);
o->userdata = NULL;
o->thread_info.state = o->state;
o->thread_info.attached = FALSE;
o->thread_info.sample_spec = o->sample_spec;
o->thread_info.resampler = resampler;
+ o->thread_info.requested_source_latency = (pa_usec_t) -1;
+ o->thread_info.direct_on_input = o->direct_on_input;
+
+ o->thread_info.delay_memblockq = pa_memblockq_new(
+ 0,
+ MEMBLOCKQ_MAXLENGTH,
+ 0,
+ pa_frame_size(&o->source->sample_spec),
+ 0,
+ 1,
+ 0,
+ &o->source->silence);
pa_assert_se(pa_idxset_put(core->source_outputs, o, &o->index) == 0);
pa_assert_se(pa_idxset_put(o->source->outputs, pa_source_output_ref(o), NULL) == 0);
- pa_log_info("Created output %u \"%s\" on %s with sample spec %s",
+ if (o->direct_on_input)
+ pa_assert_se(pa_idxset_put(o->direct_on_input->direct_outputs, o, NULL) == 0);
+
+ pa_log_info("Created output %u \"%s\" on %s with sample spec %s and channel map %s",
o->index,
- o->name,
+ pa_strnull(pa_proplist_gets(o->proplist, PA_PROP_MEDIA_NAME)),
o->source->name,
- pa_sample_spec_snprint(st, sizeof(st), &o->sample_spec));
+ pa_sample_spec_snprint(st, sizeof(st), &o->sample_spec),
+ pa_channel_map_snprint(cm, sizeof(cm), &o->channel_map));
/* Don't forget to call pa_source_output_put! */
return o;
}
-static int source_output_set_state(pa_source_output *o, pa_source_output_state_t state) {
+/* Called from main context */
+static void update_n_corked(pa_source_output *o, pa_source_output_state_t state) {
pa_assert(o);
- if (o->state == state)
- return 0;
-
- if (pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_SET_STATE, PA_UINT_TO_PTR(state), 0, NULL) < 0)
- return -1;
-
if (o->state == PA_SOURCE_OUTPUT_CORKED && state != PA_SOURCE_OUTPUT_CORKED)
pa_assert_se(o->source->n_corked -- >= 1);
else if (o->state != PA_SOURCE_OUTPUT_CORKED && state == PA_SOURCE_OUTPUT_CORKED)
o->source->n_corked++;
pa_source_update_status(o->source);
+}
+
+/* Called from main context */
+static int source_output_set_state(pa_source_output *o, pa_source_output_state_t state) {
+ pa_assert(o);
+
+ if (o->state == state)
+ return 0;
+
+ pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_SET_STATE, PA_UINT_TO_PTR(state), 0, NULL) == 0);
+ update_n_corked(o, state);
o->state = state;
if (state != PA_SOURCE_OUTPUT_UNLINKED)
@@ -198,6 +266,7 @@ static int source_output_set_state(pa_source_output *o, pa_source_output_state_t
return 0;
}
+/* Called from main context */
void pa_source_output_unlink(pa_source_output*o) {
pa_bool_t linked;
pa_assert(o);
@@ -207,28 +276,25 @@ void pa_source_output_unlink(pa_source_output*o) {
pa_source_output_ref(o);
- linked = PA_SOURCE_OUTPUT_LINKED(o->state);
+ linked = PA_SOURCE_OUTPUT_IS_LINKED(o->state);
if (linked)
pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK], o);
+ if (o->direct_on_input)
+ pa_idxset_remove_by_data(o->direct_on_input->direct_outputs, o, NULL);
pa_idxset_remove_by_data(o->source->core->source_outputs, o, NULL);
if (pa_idxset_remove_by_data(o->source->outputs, o, NULL))
pa_source_output_unref(o);
- if (linked) {
- pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_REMOVE_OUTPUT, o, 0, NULL);
- source_output_set_state(o, PA_SOURCE_OUTPUT_UNLINKED);
- pa_source_update_status(o->source);
- } else
- o->state = PA_SOURCE_OUTPUT_UNLINKED;
+ update_n_corked(o, PA_SOURCE_OUTPUT_UNLINKED);
+ o->state = PA_SOURCE_OUTPUT_UNLINKED;
- o->push = NULL;
- o->kill = NULL;
- o->get_latency = NULL;
- o->attach = NULL;
- o->detach = NULL;
- o->suspend = NULL;
+ if (linked)
+ if (o->source->asyncmsgq)
+ pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_REMOVE_OUTPUT, o, 0, NULL) == 0);
+
+ reset_callbacks(o);
if (linked) {
pa_subscription_post(o->source->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_REMOVE, o->index);
@@ -239,106 +305,252 @@ void pa_source_output_unlink(pa_source_output*o) {
pa_source_output_unref(o);
}
+/* Called from main context */
static void source_output_free(pa_object* mo) {
pa_source_output *o = PA_SOURCE_OUTPUT(mo);
+ pa_assert(o);
pa_assert(pa_source_output_refcnt(o) == 0);
- if (PA_SOURCE_OUTPUT_LINKED(o->state))
+ if (PA_SOURCE_OUTPUT_IS_LINKED(o->state))
pa_source_output_unlink(o);
- pa_log_info("Freeing output %u \"%s\"", o->index, o->name);
+ pa_log_info("Freeing output %u \"%s\"", o->index, pa_strnull(pa_proplist_gets(o->proplist, PA_PROP_MEDIA_NAME)));
pa_assert(!o->thread_info.attached);
+ if (o->thread_info.delay_memblockq)
+ pa_memblockq_free(o->thread_info.delay_memblockq);
+
if (o->thread_info.resampler)
pa_resampler_free(o->thread_info.resampler);
- pa_xfree(o->name);
+ if (o->proplist)
+ pa_proplist_free(o->proplist);
+
pa_xfree(o->driver);
pa_xfree(o);
}
+/* Called from main context */
void pa_source_output_put(pa_source_output *o) {
+ pa_source_output_state_t state;
pa_source_output_assert_ref(o);
pa_assert(o->state == PA_SOURCE_OUTPUT_INIT);
+
+ /* The following fields must be initialized properly */
pa_assert(o->push);
+ pa_assert(o->kill);
- o->thread_info.state = o->state = o->flags & PA_SOURCE_OUTPUT_START_CORKED ? PA_SOURCE_OUTPUT_CORKED : PA_SOURCE_OUTPUT_RUNNING;
+ state = o->flags & PA_SOURCE_OUTPUT_START_CORKED ? PA_SOURCE_OUTPUT_CORKED : PA_SOURCE_OUTPUT_RUNNING;
- if (o->state == PA_SOURCE_OUTPUT_CORKED)
- o->source->n_corked++;
+ update_n_corked(o, state);
+ o->state = state;
- pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_ADD_OUTPUT, o, 0, NULL);
- pa_source_update_status(o->source);
+ pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_ADD_OUTPUT, o, 0, NULL) == 0);
pa_subscription_post(o->source->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_NEW, o->index);
-
pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PUT], o);
}
+/* Called from main context */
void pa_source_output_kill(pa_source_output*o) {
pa_source_output_assert_ref(o);
- pa_assert(PA_SOURCE_OUTPUT_LINKED(o->state));
+ pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
- if (o->kill)
- o->kill(o);
+ o->kill(o);
}
-pa_usec_t pa_source_output_get_latency(pa_source_output *o) {
- pa_usec_t r = 0;
+/* Called from main context */
+pa_usec_t pa_source_output_get_latency(pa_source_output *o, pa_usec_t *source_latency) {
+ pa_usec_t r[2] = { 0, 0 };
pa_source_output_assert_ref(o);
- pa_assert(PA_SOURCE_OUTPUT_LINKED(o->state));
+ pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
- if (pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_GET_LATENCY, &r, 0, NULL) < 0)
- r = 0;
+ pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_GET_LATENCY, r, 0, NULL) == 0);
if (o->get_latency)
- r += o->get_latency(o);
+ r[0] += o->get_latency(o);
- return r;
+ if (source_latency)
+ *source_latency = r[1];
+
+ return r[0];
}
/* Called from thread context */
void pa_source_output_push(pa_source_output *o, const pa_memchunk *chunk) {
- pa_memchunk rchunk;
+ size_t length;
+ size_t limit, mbs = 0;
pa_source_output_assert_ref(o);
- pa_assert(PA_SOURCE_OUTPUT_LINKED(o->thread_info.state));
+ pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->thread_info.state));
pa_assert(chunk);
- pa_assert(chunk->length);
+ pa_assert(pa_frame_aligned(chunk->length, &o->source->sample_spec));
- if (!o->push || o->state == PA_SOURCE_OUTPUT_CORKED)
+ if (!o->push || o->thread_info.state == PA_SOURCE_OUTPUT_CORKED)
return;
- pa_assert(o->state == PA_SOURCE_OUTPUT_RUNNING);
+ pa_assert(o->thread_info.state == PA_SOURCE_OUTPUT_RUNNING);
- if (!o->thread_info.resampler) {
- o->push(o, chunk);
- return;
+ 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, chunk->length, PA_SEEK_RELATIVE);
+ }
+
+ limit = o->process_rewind ? 0 : o->source->thread_info.max_rewind;
+
+ /* Implement the delay queue */
+ while ((length = pa_memblockq_get_length(o->thread_info.delay_memblockq)) > limit) {
+ pa_memchunk qchunk;
+
+ length -= limit;
+
+ pa_assert_se(pa_memblockq_peek(o->thread_info.delay_memblockq, &qchunk) >= 0);
+
+ if (qchunk.length > length)
+ qchunk.length = length;
+
+ pa_assert(qchunk.length > 0);
+
+ if (!o->thread_info.resampler)
+ o->push(o, &qchunk);
+ else {
+ pa_memchunk rchunk;
+
+ if (mbs == 0)
+ mbs = pa_resampler_max_block_size(o->thread_info.resampler);
+
+ if (qchunk.length > mbs)
+ qchunk.length = mbs;
+
+ pa_resampler_run(o->thread_info.resampler, &qchunk, &rchunk);
+
+ if (rchunk.length > 0)
+ o->push(o, &rchunk);
+
+ if (rchunk.memblock)
+ pa_memblock_unref(rchunk.memblock);
+ }
+
+ pa_memblock_unref(qchunk.memblock);
+ pa_memblockq_drop(o->thread_info.delay_memblockq, qchunk.length);
}
+}
+
+/* Called from thread context */
+void pa_source_output_process_rewind(pa_source_output *o, size_t nbytes /* in sink sample spec */) {
+ pa_source_output_assert_ref(o);
- pa_resampler_run(o->thread_info.resampler, chunk, &rchunk);
- if (!rchunk.length)
+ pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->thread_info.state));
+ pa_assert(pa_frame_aligned(nbytes, &o->source->sample_spec));
+
+ if (nbytes <= 0)
return;
- pa_assert(rchunk.memblock);
- o->push(o, &rchunk);
- pa_memblock_unref(rchunk.memblock);
+ if (o->process_rewind) {
+ pa_assert(pa_memblockq_get_length(o->thread_info.delay_memblockq) == 0);
+
+ if (o->thread_info.resampler)
+ nbytes = pa_resampler_result(o->thread_info.resampler, nbytes);
+
+ pa_log_debug("Have to rewind %lu bytes on implementor.", (unsigned long) nbytes);
+
+ if (nbytes > 0)
+ o->process_rewind(o, nbytes);
+
+ if (o->thread_info.resampler)
+ pa_resampler_reset(o->thread_info.resampler);
+
+ } else
+ pa_memblockq_rewind(o->thread_info.delay_memblockq, nbytes);
}
+/* Called from thread context */
+void pa_source_output_update_max_rewind(pa_source_output *o, size_t nbytes /* in the source's sample spec */) {
+ pa_source_output_assert_ref(o);
+ pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->thread_info.state));
+ pa_assert(pa_frame_aligned(nbytes, &o->source->sample_spec));
+
+ if (o->update_max_rewind)
+ o->update_max_rewind(o, o->thread_info.resampler ? pa_resampler_result(o->thread_info.resampler, nbytes) : nbytes);
+}
+
+/* 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);
+ o->thread_info.requested_source_latency = usec;
+ pa_source_invalidate_requested_latency(o->source);
+
+ return usec;
+}
+
+/* Called from main context */
+pa_usec_t pa_source_output_set_requested_latency(pa_source_output *o, pa_usec_t usec) {
+ pa_source_output_assert_ref(o);
+
+ if (PA_SOURCE_OUTPUT_IS_LINKED(o->state))
+ 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 sink input is not realized yet, we have to touch
+ * the thread info data directly */
+
+ usec = fixup_latency(o->source, usec);
+ o->thread_info.requested_source_latency = usec;
+ o->source->thread_info.requested_latency_valid = FALSE;
+ }
+
+ 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))
+ 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 sink input is not realized yet, we have to touch
+ * the thread info data directly */
+ usec = o->thread_info.requested_source_latency;
+
+ return usec;
+}
+
+/* Called from main context */
void pa_source_output_cork(pa_source_output *o, pa_bool_t b) {
pa_source_output_assert_ref(o);
- pa_assert(PA_SOURCE_OUTPUT_LINKED(o->state));
+ pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
source_output_set_state(o, b ? PA_SOURCE_OUTPUT_CORKED : PA_SOURCE_OUTPUT_RUNNING);
}
+/* Called from main context */
int pa_source_output_set_rate(pa_source_output *o, uint32_t rate) {
pa_source_output_assert_ref(o);
- pa_assert(PA_SOURCE_OUTPUT_LINKED(o->state));
+ pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
pa_return_val_if_fail(o->thread_info.resampler, -1);
if (o->sample_spec.rate == rate)
@@ -352,36 +564,45 @@ int pa_source_output_set_rate(pa_source_output *o, uint32_t rate) {
return 0;
}
+/* Called from main context */
void pa_source_output_set_name(pa_source_output *o, const char *name) {
+ const char *old;
pa_source_output_assert_ref(o);
- if (!o->name && !name)
+ if (!name && !pa_proplist_contains(o->proplist, PA_PROP_MEDIA_NAME))
return;
- if (o->name && name && !strcmp(o->name, name))
+ old = pa_proplist_gets(o->proplist, PA_PROP_MEDIA_NAME);
+
+ if (old && name && !strcmp(old, name))
return;
- pa_xfree(o->name);
- o->name = pa_xstrdup(name);
+ if (name)
+ pa_proplist_sets(o->proplist, PA_PROP_MEDIA_NAME, name);
+ else
+ pa_proplist_unset(o->proplist, PA_PROP_MEDIA_NAME);
- if (PA_SOURCE_OUTPUT_LINKED(o->state)) {
- pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_NAME_CHANGED], o);
+ if (PA_SOURCE_OUTPUT_IS_LINKED(o->state)) {
+ pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PROPLIST_CHANGED], o);
pa_subscription_post(o->source->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
}
}
+/* Called from main context */
pa_resample_method_t pa_source_output_get_resample_method(pa_source_output *o) {
pa_source_output_assert_ref(o);
return o->resample_method;
}
+/* Called from main context */
int pa_source_output_move_to(pa_source_output *o, pa_source *dest) {
pa_source *origin;
- pa_resampler *new_resampler = NULL;
+ pa_resampler *new_resampler;
+ pa_source_output_move_hook_data hook_data;
pa_source_output_assert_ref(o);
- pa_assert(PA_SOURCE_OUTPUT_LINKED(o->state));
+ pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
pa_source_assert_ref(dest);
origin = o->source;
@@ -392,6 +613,9 @@ int pa_source_output_move_to(pa_source_output *o, pa_source *dest) {
if (o->flags & PA_SOURCE_OUTPUT_DONT_MOVE)
return -1;
+ if (o->direct_on_input)
+ return -1;
+
if (pa_idxset_size(dest->outputs) >= PA_MAX_OUTPUTS_PER_SOURCE) {
pa_log_warn("Failed to move source output: too many outputs per source.");
return -1;
@@ -415,16 +639,21 @@ int pa_source_output_move_to(pa_source_output *o, pa_source *dest) {
&dest->sample_spec, &dest->channel_map,
&o->sample_spec, &o->channel_map,
o->resample_method,
- !!(o->flags & PA_SOURCE_OUTPUT_VARIABLE_RATE)))) {
+ ((o->flags & PA_SOURCE_OUTPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0) |
+ ((o->flags & PA_SOURCE_OUTPUT_NO_REMAP) ? PA_RESAMPLER_NO_REMAP : 0) |
+ (o->core->disable_remixing || (o->flags & PA_SOURCE_OUTPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0)))) {
pa_log_warn("Unsupported resampling operation.");
return -1;
}
- }
+ } else
+ new_resampler = NULL;
- pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE], o);
+ hook_data.source_output = o;
+ hook_data.destination = dest;
+ pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE], &hook_data);
/* Okey, let's move it */
- pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_REMOVE_OUTPUT, o, 0, NULL);
+ pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_REMOVE_OUTPUT, o, 0, NULL) == 0);
pa_idxset_remove_by_data(origin->outputs, o, NULL);
pa_idxset_put(dest->outputs, o, NULL);
@@ -440,13 +669,28 @@ int pa_source_output_move_to(pa_source_output *o, pa_source *dest) {
if (o->thread_info.resampler)
pa_resampler_free(o->thread_info.resampler);
o->thread_info.resampler = new_resampler;
- }
- pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_ADD_OUTPUT, o, 0, NULL);
+ pa_memblockq_free(o->thread_info.delay_memblockq);
+
+ o->thread_info.delay_memblockq = pa_memblockq_new(
+ 0,
+ MEMBLOCKQ_MAXLENGTH,
+ 0,
+ pa_frame_size(&o->source->sample_spec),
+ 0,
+ 1,
+ 0,
+ &o->source->silence);
+ }
pa_source_update_status(origin);
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);
+
+ if (o->moved)
+ o->moved(o);
+
pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_POST], o);
pa_log_debug("Successfully moved source output %i from %s to %s.", o->index, origin->name, dest->name);
@@ -457,26 +701,61 @@ int pa_source_output_move_to(pa_source_output *o, pa_source *dest) {
return 0;
}
-/* Called from thread context */
+/* Called from IO thread context */
+void pa_source_output_set_state_within_thread(pa_source_output *o, pa_source_output_state_t state) {
+ pa_source_output_assert_ref(o);
+
+ if (state == o->thread_info.state)
+ return;
+
+ if (o->state_change)
+ o->state_change(o, state);
+
+ o->thread_info.state = state;
+}
+
+/* Called from IO thread context, except when it is not */
int pa_source_output_process_msg(pa_msgobject *mo, int code, void *userdata, int64_t offset, pa_memchunk* chunk) {
pa_source_output *o = PA_SOURCE_OUTPUT(mo);
-
pa_source_output_assert_ref(o);
- pa_assert(PA_SOURCE_OUTPUT_LINKED(o->thread_info.state));
switch (code) {
- case PA_SOURCE_OUTPUT_MESSAGE_SET_RATE: {
+ 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;
+
+ return 0;
+ }
+
+ case PA_SOURCE_OUTPUT_MESSAGE_SET_RATE:
o->thread_info.sample_spec.rate = PA_PTR_TO_UINT(userdata);
pa_resampler_set_output_rate(o->thread_info.resampler, PA_PTR_TO_UINT(userdata));
+ return 0;
+
+ case PA_SOURCE_OUTPUT_MESSAGE_SET_STATE:
+
+ pa_source_output_set_state_within_thread(o, PA_PTR_TO_UINT(userdata));
+ return 0;
+
+ case PA_SOURCE_OUTPUT_MESSAGE_SET_REQUESTED_LATENCY: {
+ pa_usec_t *usec = userdata;
+
+ *usec = pa_source_output_set_requested_latency_within_thread(o, *usec);
return 0;
}
- case PA_SOURCE_OUTPUT_MESSAGE_SET_STATE: {
- o->thread_info.state = PA_PTR_TO_UINT(userdata);
+ case PA_SINK_INPUT_MESSAGE_GET_REQUESTED_LATENCY: {
+ pa_usec_t *r = userdata;
+ *r = o->thread_info.requested_source_latency;
return 0;
}
}
diff --git a/src/pulsecore/source-output.h b/src/pulsecore/source-output.h
index 60552d43..61825b22 100644
--- a/src/pulsecore/source-output.h
+++ b/src/pulsecore/source-output.h
@@ -1,8 +1,6 @@
#ifndef foopulsesourceoutputhfoo
#define foopulsesourceoutputhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -42,14 +40,19 @@ typedef enum pa_source_output_state {
PA_SOURCE_OUTPUT_UNLINKED
} pa_source_output_state_t;
-static inline pa_bool_t PA_SOURCE_OUTPUT_LINKED(pa_source_output_state_t x) {
+static inline pa_bool_t PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_state_t x) {
return x == PA_SOURCE_OUTPUT_RUNNING || x == PA_SOURCE_OUTPUT_CORKED;
}
typedef enum pa_source_output_flags {
PA_SOURCE_OUTPUT_VARIABLE_RATE = 1,
PA_SOURCE_OUTPUT_DONT_MOVE = 2,
- PA_SOURCE_OUTPUT_START_CORKED = 4
+ PA_SOURCE_OUTPUT_START_CORKED = 4,
+ PA_SOURCE_OUTPUT_NO_REMAP = 8,
+ PA_SOURCE_OUTPUT_NO_REMIX = 16,
+ PA_SOURCE_OUTPUT_FIX_FORMAT = 32,
+ PA_SOURCE_OUTPUT_FIX_RATE = 64,
+ PA_SOURCE_OUTPUT_FIX_CHANNELS = 128
} pa_source_output_flags_t;
struct pa_source_output {
@@ -57,54 +60,93 @@ struct pa_source_output {
uint32_t index;
pa_core *core;
+
pa_source_output_state_t state;
pa_source_output_flags_t flags;
- char *name, *driver; /* may be NULL */
+ char *driver; /* may be NULL */
+ pa_proplist *proplist;
+
pa_module *module; /* may be NULL */
pa_client *client; /* may be NULL */
pa_source *source;
+ /* A source output can monitor just a single input of a sink, in which case we find it here */
+ pa_sink_input *direct_on_input; /* may be NULL */
+
pa_sample_spec sample_spec;
pa_channel_map channel_map;
+ pa_resample_method_t resample_method;
+
/* Pushes a new memchunk into the output. Called from IO thread
* context. */
- void (*push)(pa_source_output *o, const pa_memchunk *chunk);
+ void (*push)(pa_source_output *o, const pa_memchunk *chunk); /* may NOT be NULL */
+
+ /* Only relevant for monitor sources right now: called when the
+ * recorded stream is rewound. Called from IO context */
+ void (*process_rewind)(pa_source_output *o, size_t nbytes); /* may be NULL */
+
+ /* Called whenever the maximum rewindable size of the source
+ * changes. Called from IO thread context. */
+ void (*update_max_rewind) (pa_source_output *o, size_t nbytes); /* may be NULL */
+
+ /* Called whenever the configured latency of the source
+ * changes. Called from IO context. */
+ void (*update_source_requested_latency) (pa_source_output *o); /* may be NULL */
+
+ /* Called whenver the latency range of the source changes. Called
+ * from IO context. */
+ void (*update_source_latency_range) (pa_source_output *o); /* may be NULL */
/* If non-NULL this function is called when the output is first
* connected to a source. Called from IO thread context */
- void (*attach) (pa_source_output *o); /* may be NULL */
+ void (*attach) (pa_source_output *o); /* may be NULL */
/* If non-NULL this function is called when the output is
* disconnected from its source. Called from IO thread context */
- void (*detach) (pa_source_output *o); /* may be NULL */
-
+ void (*detach) (pa_source_output *o); /* may be NULL */
+
/* If non-NULL called whenever the the source this output is attached
* to suspends or resumes. Called from main context */
- void (*suspend) (pa_source_output *o, int b); /* may be NULL */
+ 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 */
/* Supposed to unlink and destroy this stream. Called from main
* context. */
- void (*kill)(pa_source_output* o); /* may be NULL */
+ void (*kill)(pa_source_output* o); /* may NOT be NULL */
/* Return the current latency (i.e. length of bufferd audio) of
- this stream. Called from main context. If NULL a
- PA_SOURCE_OUTPUT_MESSAGE_GET_LATENCY message is sent to the IO
- thread instead. */
+ this stream. Called from main context. This is added to what the
+ PA_SOURCE_OUTPUT_MESSAGE_GET_LATENCY message sent to the IO thread
+ returns */
pa_usec_t (*get_latency) (pa_source_output *o); /* may be NULL */
- pa_resample_method_t resample_method;
+ /* If non_NULL this function is called from thread context if the
+ * state changes. The old state is found in thread_info.state. */
+ void (*state_change) (pa_source_output *o, pa_source_output_state_t state); /* may be NULL */
struct {
pa_source_output_state_t state;
pa_bool_t attached; /* True only between ->attach() and ->detach() calls */
-
+
pa_sample_spec sample_spec;
pa_resampler* resampler; /* may be NULL */
+
+ /* We maintain a delay memblockq here for source outputs that
+ * don't implement rewind() */
+ pa_memblockq *delay_memblockq;
+
+ /* The requested latency for the source */
+ pa_usec_t requested_source_latency;
+
+ pa_sink_input *direct_on_input; /* may be NULL */
} thread_info;
void *userdata;
@@ -117,11 +159,16 @@ enum {
PA_SOURCE_OUTPUT_MESSAGE_GET_LATENCY,
PA_SOURCE_OUTPUT_MESSAGE_SET_RATE,
PA_SOURCE_OUTPUT_MESSAGE_SET_STATE,
+ PA_SOURCE_OUTPUT_MESSAGE_SET_REQUESTED_LATENCY,
+ PA_SOURCE_OUTPUT_MESSAGE_GET_REQUESTED_LATENCY,
PA_SOURCE_OUTPUT_MESSAGE_MAX
};
typedef struct pa_source_output_new_data {
- const char *name, *driver;
+ pa_proplist *proplist;
+ pa_sink_input *direct_on_input;
+
+ const char *driver;
pa_module *module;
pa_client *client;
@@ -138,7 +185,12 @@ typedef struct pa_source_output_new_data {
pa_source_output_new_data* pa_source_output_new_data_init(pa_source_output_new_data *data);
void pa_source_output_new_data_set_sample_spec(pa_source_output_new_data *data, const pa_sample_spec *spec);
void pa_source_output_new_data_set_channel_map(pa_source_output_new_data *data, const pa_channel_map *map);
-void pa_source_output_new_data_set_volume(pa_source_output_new_data *data, const pa_cvolume *volume);
+void pa_source_output_new_data_done(pa_source_output_new_data *data);
+
+typedef struct pa_source_output_move_hook_data {
+ pa_source_output *source_output;
+ pa_source *destination;
+} pa_source_output_move_hook_data;
/* To be called by the implementing module only */
@@ -152,16 +204,18 @@ void pa_source_output_unlink(pa_source_output*o);
void pa_source_output_set_name(pa_source_output *i, const char *name);
+pa_usec_t pa_source_output_set_requested_latency(pa_source_output *i, pa_usec_t usec);
+
+void pa_source_output_cork(pa_source_output *i, pa_bool_t b);
+
+int pa_source_output_set_rate(pa_source_output *o, uint32_t rate);
+
/* Callable by everyone */
/* External code may request disconnection with this funcion */
void pa_source_output_kill(pa_source_output*o);
-pa_usec_t pa_source_output_get_latency(pa_source_output *i);
-
-void pa_source_output_cork(pa_source_output *i, pa_bool_t b);
-
-int pa_source_output_set_rate(pa_source_output *o, uint32_t rate);
+pa_usec_t pa_source_output_get_latency(pa_source_output *i, pa_usec_t *source_latency);
pa_resample_method_t pa_source_output_get_resample_method(pa_source_output *o);
@@ -169,9 +223,18 @@ int pa_source_output_move_to(pa_source_output *o, pa_source *dest);
#define pa_source_output_get_state(o) ((o)->state)
+pa_usec_t pa_source_output_get_requested_latency(pa_source_output *o);
+
/* To be used exclusively by the source driver thread */
void pa_source_output_push(pa_source_output *o, const pa_memchunk *chunk);
+void pa_source_output_process_rewind(pa_source_output *o, size_t nbytes);
+void pa_source_output_update_max_rewind(pa_source_output *o, size_t nbytes);
+
+void pa_source_output_set_state_within_thread(pa_source_output *o, pa_source_output_state_t state);
+
int pa_source_output_process_msg(pa_msgobject *mo, int code, void *userdata, int64_t offset, pa_memchunk *chunk);
+pa_usec_t pa_source_output_set_requested_latency_within_thread(pa_source_output *o, pa_usec_t usec);
+
#endif
diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c
index 9a6902ae..8256a988 100644
--- a/src/pulsecore/source.c
+++ b/src/pulsecore/source.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -32,6 +30,7 @@
#include <pulse/utf8.h>
#include <pulse/xmalloc.h>
+#include <pulse/timeval.h>
#include <pulsecore/source-output.h>
#include <pulsecore/namereg.h>
@@ -41,40 +40,127 @@
#include "source.h"
+#define DEFAULT_MIN_LATENCY (4*PA_USEC_PER_MSEC)
+
static PA_DEFINE_CHECK_TYPE(pa_source, pa_msgobject);
static void source_free(pa_object *o);
+pa_source_new_data* pa_source_new_data_init(pa_source_new_data *data) {
+ pa_assert(data);
+
+ memset(data, 0, sizeof(*data));
+ data->proplist = pa_proplist_new();
+
+ return data;
+}
+
+void pa_source_new_data_set_name(pa_source_new_data *data, const char *name) {
+ pa_assert(data);
+
+ pa_xfree(data->name);
+ data->name = pa_xstrdup(name);
+}
+
+void pa_source_new_data_set_sample_spec(pa_source_new_data *data, const pa_sample_spec *spec) {
+ pa_assert(data);
+
+ if ((data->sample_spec_is_set = !!spec))
+ data->sample_spec = *spec;
+}
+
+void pa_source_new_data_set_channel_map(pa_source_new_data *data, const pa_channel_map *map) {
+ pa_assert(data);
+
+ if ((data->channel_map_is_set = !!map))
+ data->channel_map = *map;
+}
+
+void pa_source_new_data_set_volume(pa_source_new_data *data, const pa_cvolume *volume) {
+ pa_assert(data);
+
+ if ((data->volume_is_set = !!volume))
+ data->volume = *volume;
+}
+
+void pa_source_new_data_set_muted(pa_source_new_data *data, pa_bool_t mute) {
+ pa_assert(data);
+
+ data->muted_is_set = TRUE;
+ data->muted = !!mute;
+}
+
+void pa_source_new_data_done(pa_source_new_data *data) {
+ pa_assert(data);
+
+ pa_xfree(data->name);
+ pa_proplist_free(data->proplist);
+}
+
+/* Called from main context */
+static void reset_callbacks(pa_source *s) {
+ pa_assert(s);
+
+ s->set_state = NULL;
+ s->get_volume = NULL;
+ s->set_volume = NULL;
+ s->get_mute = NULL;
+ s->set_mute = NULL;
+ s->update_requested_latency = NULL;
+}
+
+/* Called from main context */
pa_source* pa_source_new(
pa_core *core,
- const char *driver,
- const char *name,
- int fail,
- const pa_sample_spec *spec,
- const pa_channel_map *map) {
+ pa_source_new_data *data,
+ pa_source_flags_t flags) {
pa_source *s;
- char st[256];
- pa_channel_map tmap;
+ const char *name;
+ char st[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
pa_assert(core);
- pa_assert(name);
- pa_assert(spec);
+ pa_assert(data);
+ pa_assert(data->name);
+
+ s = pa_msgobject_new(pa_source);
- pa_return_null_if_fail(pa_sample_spec_valid(spec));
+ if (!(name = pa_namereg_register(core, data->name, PA_NAMEREG_SOURCE, s, data->namereg_fail))) {
+ pa_xfree(s);
+ return NULL;
+ }
- if (!map)
- map = pa_channel_map_init_auto(&tmap, spec->channels, PA_CHANNEL_MAP_DEFAULT);
+ pa_source_new_data_set_name(data, name);
- pa_return_null_if_fail(map && pa_channel_map_valid(map));
- pa_return_null_if_fail(map->channels == spec->channels);
- pa_return_null_if_fail(!driver || pa_utf8_valid(driver));
- pa_return_null_if_fail(pa_utf8_valid(name) && *name);
+ if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SOURCE_NEW], data) < 0) {
+ pa_xfree(s);
+ pa_namereg_unregister(core, name);
+ return NULL;
+ }
- s = pa_msgobject_new(pa_source);
+ 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]);
+
+ pa_return_null_if_fail(data->sample_spec_is_set && pa_sample_spec_valid(&data->sample_spec));
+
+ if (!data->channel_map_is_set)
+ pa_return_null_if_fail(pa_channel_map_init_auto(&data->channel_map, data->sample_spec.channels, PA_CHANNEL_MAP_DEFAULT));
- if (!(name = pa_namereg_register(core, name, PA_NAMEREG_SOURCE, s, fail))) {
+ pa_return_null_if_fail(pa_channel_map_valid(&data->channel_map));
+ pa_return_null_if_fail(data->channel_map.channels == data->sample_spec.channels);
+
+ if (!data->volume_is_set)
+ pa_cvolume_reset(&data->volume, data->sample_spec.channels);
+
+ pa_return_null_if_fail(pa_cvolume_valid(&data->volume));
+ pa_return_null_if_fail(data->volume.channels == data->sample_spec.channels);
+
+ if (!data->muted_is_set)
+ data->muted = FALSE;
+
+ if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SOURCE_FIXATE], data) < 0) {
pa_xfree(s);
+ pa_namereg_unregister(core, name);
return NULL;
}
@@ -83,57 +169,81 @@ pa_source* pa_source_new(
s->core = core;
s->state = PA_SOURCE_INIT;
- s->flags = 0;
+ s->flags = flags;
s->name = pa_xstrdup(name);
- s->description = NULL;
- s->driver = pa_xstrdup(driver);
- s->module = NULL;
+ s->proplist = pa_proplist_copy(data->proplist);
+ s->driver = pa_xstrdup(data->driver);
+ s->module = data->module;
- s->sample_spec = *spec;
- s->channel_map = *map;
+ s->sample_spec = data->sample_spec;
+ s->channel_map = data->channel_map;
s->outputs = pa_idxset_new(NULL, NULL);
s->n_corked = 0;
s->monitor_of = NULL;
- pa_cvolume_reset(&s->volume, spec->channels);
- s->muted = FALSE;
+ s->volume = data->volume;
+ s->muted = data->muted;
s->refresh_volume = s->refresh_muted = FALSE;
- s->get_latency = NULL;
- s->set_volume = NULL;
- s->get_volume = NULL;
- s->set_mute = NULL;
- s->get_mute = NULL;
- s->set_state = NULL;
+ reset_callbacks(s);
s->userdata = NULL;
s->asyncmsgq = NULL;
s->rtpoll = NULL;
- pa_assert_se(pa_idxset_put(core->sources, s, &s->index) >= 0);
-
- pa_sample_spec_snprint(st, sizeof(st), spec);
- pa_log_info("Created source %u \"%s\" with sample spec \"%s\"", s->index, s->name, st);
+ pa_silence_memchunk_get(
+ &core->silence_cache,
+ core->mempool,
+ &s->silence,
+ &s->sample_spec,
+ 0);
s->thread_info.outputs = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
- s->thread_info.soft_volume = s->volume;
- s->thread_info.soft_muted = s->muted;
+ pa_cvolume_reset(&s->thread_info.soft_volume, s->sample_spec.channels);
+ s->thread_info.soft_muted = FALSE;
s->thread_info.state = s->state;
+ 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;
+
+ pa_assert_se(pa_idxset_put(core->sources, s, &s->index) >= 0);
+
+ pa_log_info("Created source %u \"%s\" with sample spec %s and channel map %s",
+ s->index,
+ s->name,
+ pa_sample_spec_snprint(st, sizeof(st), &s->sample_spec),
+ pa_channel_map_snprint(cm, sizeof(cm), &s->channel_map));
return s;
}
+/* Called from main context */
static int source_set_state(pa_source *s, pa_source_state_t state) {
int ret;
+ pa_bool_t suspend_change;
pa_assert(s);
if (s->state == state)
return 0;
- if ((s->state == PA_SOURCE_SUSPENDED && PA_SOURCE_OPENED(state)) ||
- (PA_SOURCE_OPENED(s->state) && state == PA_SOURCE_SUSPENDED)) {
+ suspend_change =
+ (s->state == PA_SOURCE_SUSPENDED && PA_SOURCE_IS_OPENED(state)) ||
+ (PA_SOURCE_IS_OPENED(s->state) && state == PA_SOURCE_SUSPENDED);
+
+ if (s->set_state)
+ if ((ret = s->set_state(s, state)) < 0)
+ return -1;
+
+ if (s->asyncmsgq)
+ pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_STATE, PA_UINT_TO_PTR(state), 0, NULL) == 0);
+
+ s->state = state;
+
+ if (suspend_change) {
pa_source_output *o;
uint32_t idx;
@@ -144,33 +254,38 @@ static int source_set_state(pa_source *s, pa_source_state_t state) {
o->suspend(o, state == PA_SINK_SUSPENDED);
}
- if (s->set_state)
- if ((ret = s->set_state(s, state)) < 0)
- return -1;
-
- if (pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_STATE, PA_UINT_TO_PTR(state), 0, NULL) < 0)
- return -1;
-
- s->state = state;
-
if (state != PA_SOURCE_UNLINKED) /* if we enter UNLINKED state pa_source_unlink() will fire the apropriate events */
pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_STATE_CHANGED], s);
+
return 0;
}
+/* Called from main context */
void pa_source_put(pa_source *s) {
pa_source_assert_ref(s);
pa_assert(s->state == PA_SINK_INIT);
- pa_assert(s->rtpoll);
+
+ /* 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);
+
+ if (!(s->flags & PA_SOURCE_HW_VOLUME_CTRL)) {
+ s->flags |= PA_SOURCE_DECIBEL_VOLUME;
+
+ s->thread_info.soft_volume = s->volume;
+ s->thread_info.soft_muted = s->muted;
+ }
pa_assert_se(source_set_state(s, PA_SOURCE_IDLE) == 0);
pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE | PA_SUBSCRIPTION_EVENT_NEW, s->index);
- pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_NEW_POST], s);
+ pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_PUT], s);
}
+/* Called from main context */
void pa_source_unlink(pa_source *s) {
pa_bool_t linked;
pa_source_output *o, *j = NULL;
@@ -180,7 +295,7 @@ void pa_source_unlink(pa_source *s) {
/* See pa_sink_unlink() for a couple of comments how this function
* works. */
- linked = PA_SOURCE_LINKED(s->state);
+ linked = PA_SOURCE_IS_LINKED(s->state);
if (linked)
pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK], s);
@@ -200,12 +315,7 @@ void pa_source_unlink(pa_source *s) {
else
s->state = PA_SOURCE_UNLINKED;
- s->get_latency = NULL;
- s->get_volume = NULL;
- s->set_volume = NULL;
- s->set_mute = NULL;
- s->get_mute = NULL;
- s->set_state = NULL;
+ reset_callbacks(s);
if (linked) {
pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE | PA_SUBSCRIPTION_EVENT_REMOVE, s->index);
@@ -213,6 +323,7 @@ void pa_source_unlink(pa_source *s) {
}
}
+/* Called from main context */
static void source_free(pa_object *o) {
pa_source_output *so;
pa_source *s = PA_SOURCE(o);
@@ -220,7 +331,7 @@ static void source_free(pa_object *o) {
pa_assert(s);
pa_assert(pa_source_refcnt(s) == 0);
- if (PA_SOURCE_LINKED(s->state))
+ if (PA_SOURCE_IS_LINKED(s->state))
pa_source_unlink(s);
pa_log_info("Freeing source %u \"%s\"", s->index, s->name);
@@ -232,15 +343,36 @@ static void source_free(pa_object *o) {
pa_hashmap_free(s->thread_info.outputs, NULL, NULL);
+ if (s->silence.memblock)
+ pa_memblock_unref(s->silence.memblock);
+
pa_xfree(s->name);
- pa_xfree(s->description);
pa_xfree(s->driver);
+
+ if (s->proplist)
+ pa_proplist_free(s->proplist);
+
pa_xfree(s);
}
+/* Called from main context */
+void pa_source_set_asyncmsgq(pa_source *s, pa_asyncmsgq *q) {
+ pa_source_assert_ref(s);
+
+ s->asyncmsgq = q;
+}
+
+/* Called from main context */
+void pa_source_set_rtpoll(pa_source *s, pa_rtpoll *p) {
+ pa_source_assert_ref(s);
+
+ s->rtpoll = p;
+}
+
+/* Called from main context */
int pa_source_update_status(pa_source*s) {
pa_source_assert_ref(s);
- pa_assert(PA_SOURCE_LINKED(s->state));
+ pa_assert(PA_SOURCE_IS_LINKED(s->state));
if (s->state == PA_SOURCE_SUSPENDED)
return 0;
@@ -248,9 +380,10 @@ int pa_source_update_status(pa_source*s) {
return source_set_state(s, pa_source_used_by(s) ? PA_SOURCE_RUNNING : PA_SOURCE_IDLE);
}
+/* Called from main context */
int pa_source_suspend(pa_source *s, pa_bool_t suspend) {
pa_source_assert_ref(s);
- pa_assert(PA_SOURCE_LINKED(s->state));
+ pa_assert(PA_SOURCE_IS_LINKED(s->state));
if (suspend)
return source_set_state(s, PA_SOURCE_SUSPENDED);
@@ -258,19 +391,32 @@ int pa_source_suspend(pa_source *s, pa_bool_t suspend) {
return source_set_state(s, pa_source_used_by(s) ? PA_SOURCE_RUNNING : PA_SOURCE_IDLE);
}
-void pa_source_ping(pa_source *s) {
+/* Called from IO thread context */
+void pa_source_process_rewind(pa_source *s, size_t nbytes) {
+ pa_source_output *o;
+ void *state = NULL;
+
pa_source_assert_ref(s);
- pa_assert(PA_SOURCE_LINKED(s->state));
+ pa_assert(PA_SOURCE_IS_OPENED(s->thread_info.state));
+
+ if (nbytes <= 0)
+ return;
- pa_asyncmsgq_post(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_PING, NULL, 0, NULL, NULL);
+ pa_log_debug("Processing rewind...");
+
+ while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL))) {
+ pa_source_output_assert_ref(o);
+ pa_source_output_process_rewind(o, nbytes);
+ }
}
+/* Called from IO thread context */
void pa_source_post(pa_source*s, const pa_memchunk *chunk) {
pa_source_output *o;
void *state = NULL;
pa_source_assert_ref(s);
- pa_assert(PA_SOURCE_OPENED(s->thread_info.state));
+ pa_assert(PA_SOURCE_IS_OPENED(s->thread_info.state));
pa_assert(chunk);
if (s->thread_info.state != PA_SOURCE_RUNNING)
@@ -287,40 +433,75 @@ void pa_source_post(pa_source*s, const pa_memchunk *chunk) {
else
pa_volume_memchunk(&vchunk, &s->sample_spec, &s->thread_info.soft_volume);
- while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL)))
- pa_source_output_push(o, &vchunk);
+ while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL))) {
+ pa_source_output_assert_ref(o);
+
+ if (!o->thread_info.direct_on_input)
+ pa_source_output_push(o, &vchunk);
+ }
pa_memblock_unref(vchunk.memblock);
} else {
- while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL)))
- pa_source_output_push(o, chunk);
+ while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL))) {
+ pa_source_output_assert_ref(o);
+
+ if (!o->thread_info.direct_on_input)
+ pa_source_output_push(o, chunk);
+ }
}
}
+/* Called from IO thread context */
+void pa_source_post_direct(pa_source*s, pa_source_output *o, const pa_memchunk *chunk) {
+ pa_source_assert_ref(s);
+ pa_assert(PA_SOURCE_IS_OPENED(s->thread_info.state));
+ pa_source_output_assert_ref(o);
+ pa_assert(o->thread_info.direct_on_input);
+ pa_assert(chunk);
+
+ if (s->thread_info.state != PA_SOURCE_RUNNING)
+ return;
+
+ if (s->thread_info.soft_muted || !pa_cvolume_is_norm(&s->thread_info.soft_volume)) {
+ pa_memchunk vchunk = *chunk;
+
+ pa_memblock_ref(vchunk.memblock);
+ pa_memchunk_make_writable(&vchunk, 0);
+
+ if (s->thread_info.soft_muted || pa_cvolume_is_muted(&s->thread_info.soft_volume))
+ pa_silence_memchunk(&vchunk, &s->sample_spec);
+ else
+ pa_volume_memchunk(&vchunk, &s->sample_spec, &s->thread_info.soft_volume);
+
+ pa_source_output_push(o, &vchunk);
+
+ pa_memblock_unref(vchunk.memblock);
+ } else
+ pa_source_output_push(o, chunk);
+}
+
+/* Called from main thread */
pa_usec_t pa_source_get_latency(pa_source *s) {
pa_usec_t usec;
pa_source_assert_ref(s);
- pa_assert(PA_SOURCE_LINKED(s->state));
+ pa_assert(PA_SOURCE_IS_LINKED(s->state));
- if (!PA_SOURCE_OPENED(s->state))
+ if (!PA_SOURCE_IS_OPENED(s->state))
return 0;
- if (s->get_latency)
- return s->get_latency(s);
-
- if (pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0)
- return 0;
+ pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_LATENCY, &usec, 0, NULL) == 0);
return usec;
}
+/* Called from main thread */
void pa_source_set_volume(pa_source *s, const pa_cvolume *volume) {
- int changed;
+ pa_bool_t changed;
pa_source_assert_ref(s);
- pa_assert(PA_SOURCE_LINKED(s->state));
+ pa_assert(PA_SOURCE_IS_LINKED(s->state));
pa_assert(volume);
changed = !pa_cvolume_equal(volume, &s->volume);
@@ -330,37 +511,39 @@ void pa_source_set_volume(pa_source *s, const pa_cvolume *volume) {
s->set_volume = NULL;
if (!s->set_volume)
- pa_asyncmsgq_post(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_VOLUME, pa_xnewdup(struct pa_cvolume, volume, 1), 0, NULL, pa_xfree);
+ pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_VOLUME, volume, 0, NULL);
if (changed)
pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
}
+/* Called from main thread */
const pa_cvolume *pa_source_get_volume(pa_source *s) {
- pa_cvolume old_volume;
-
pa_source_assert_ref(s);
- pa_assert(PA_SOURCE_LINKED(s->state));
+ pa_assert(PA_SOURCE_IS_LINKED(s->state));
- old_volume = s->volume;
+ if (s->refresh_volume) {
+ pa_cvolume old_volume = s->volume;
- if (s->get_volume && s->get_volume(s) < 0)
- s->get_volume = NULL;
+ if (s->get_volume && s->get_volume(s) < 0)
+ s->get_volume = NULL;
- if (!s->get_volume && s->refresh_volume)
- pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_VOLUME, &s->volume, 0, NULL);
+ if (!s->get_volume)
+ pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_VOLUME, &s->volume, 0, NULL);
- if (!pa_cvolume_equal(&old_volume, &s->volume))
- pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+ if (!pa_cvolume_equal(&old_volume, &s->volume))
+ pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+ }
return &s->volume;
}
+/* Called from main thread */
void pa_source_set_mute(pa_source *s, pa_bool_t mute) {
- int changed;
+ pa_bool_t changed;
pa_source_assert_ref(s);
- pa_assert(PA_SOURCE_LINKED(s->state));
+ pa_assert(PA_SOURCE_IS_LINKED(s->state));
changed = s->muted != mute;
s->muted = mute;
@@ -375,81 +558,66 @@ void pa_source_set_mute(pa_source *s, pa_bool_t mute) {
pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
}
+/* Called from main thread */
pa_bool_t pa_source_get_mute(pa_source *s) {
- pa_bool_t old_muted;
pa_source_assert_ref(s);
- pa_assert(PA_SOURCE_LINKED(s->state));
+ pa_assert(PA_SOURCE_IS_LINKED(s->state));
- old_muted = s->muted;
+ if (s->refresh_muted) {
+ pa_bool_t old_muted = s->muted;
- if (s->get_mute && s->get_mute(s) < 0)
- s->get_mute = NULL;
+ if (s->get_mute && s->get_mute(s) < 0)
+ s->get_mute = NULL;
- if (!s->get_mute && s->refresh_muted)
- pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_MUTE, &s->muted, 0, NULL);
+ if (!s->get_mute)
+ pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_MUTE, &s->muted, 0, NULL);
- if (old_muted != s->muted)
- pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+ if (old_muted != s->muted)
+ pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+ }
return s->muted;
}
-void pa_source_set_module(pa_source *s, pa_module *m) {
- pa_source_assert_ref(s);
-
- if (m == s->module)
- return;
-
- s->module = m;
-
- pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
-}
-
+/* Called from main thread */
void pa_source_set_description(pa_source *s, const char *description) {
+ const char *old;
pa_source_assert_ref(s);
- if (!description && !s->description)
+ if (!description && !pa_proplist_contains(s->proplist, PA_PROP_DEVICE_DESCRIPTION))
return;
- if (description && s->description && !strcmp(description, s->description))
+ old = pa_proplist_gets(s->proplist, PA_PROP_DEVICE_DESCRIPTION);
+
+ if (old && description && !strcmp(old, description))
return;
- pa_xfree(s->description);
- s->description = pa_xstrdup(description);
+ if (description)
+ pa_proplist_sets(s->proplist, PA_PROP_DEVICE_DESCRIPTION, description);
+ else
+ pa_proplist_unset(s->proplist, PA_PROP_DEVICE_DESCRIPTION);
- if (PA_SOURCE_LINKED(s->state)) {
- pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_DESCRIPTION_CHANGED], s);
+ if (PA_SOURCE_IS_LINKED(s->state)) {
pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+ pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_PROPLIST_CHANGED], s);
}
}
-void pa_source_set_asyncmsgq(pa_source *s, pa_asyncmsgq *q) {
- pa_source_assert_ref(s);
- pa_assert(q);
-
- s->asyncmsgq = q;
-}
-
-void pa_source_set_rtpoll(pa_source *s, pa_rtpoll *p) {
- pa_source_assert_ref(s);
- pa_assert(p);
-
- s->rtpoll = p;
-}
-
+/* Called from main thread */
unsigned pa_source_linked_by(pa_source *s) {
pa_source_assert_ref(s);
- pa_assert(PA_SOURCE_LINKED(s->state));
+ pa_assert(PA_SOURCE_IS_LINKED(s->state));
return pa_idxset_size(s->outputs);
}
+/* Called from main thread */
unsigned pa_source_used_by(pa_source *s) {
unsigned ret;
pa_source_assert_ref(s);
- pa_assert(PA_SOURCE_LINKED(s->state));
+ pa_assert(PA_SOURCE_IS_LINKED(s->state));
ret = pa_idxset_size(s->outputs);
pa_assert(ret >= s->n_corked);
@@ -457,37 +625,62 @@ unsigned pa_source_used_by(pa_source *s) {
return ret - s->n_corked;
}
+/* Called from IO thread, except when it is not */
int pa_source_process_msg(pa_msgobject *object, int code, void *userdata, int64_t offset, pa_memchunk *chunk) {
pa_source *s = PA_SOURCE(object);
pa_source_assert_ref(s);
- pa_assert(s->thread_info.state != PA_SOURCE_UNLINKED);
switch ((pa_source_message_t) code) {
+
case PA_SOURCE_MESSAGE_ADD_OUTPUT: {
pa_source_output *o = PA_SOURCE_OUTPUT(userdata);
+
pa_hashmap_put(s->thread_info.outputs, PA_UINT32_TO_PTR(o->index), pa_source_output_ref(o));
+ if (o->direct_on_input) {
+ o->thread_info.direct_on_input = o->direct_on_input;
+ pa_hashmap_put(o->thread_info.direct_on_input->thread_info.direct_outputs, PA_UINT32_TO_PTR(o->index), o);
+ }
+
pa_assert(!o->thread_info.attached);
o->thread_info.attached = TRUE;
if (o->attach)
o->attach(o);
+ pa_source_output_set_state_within_thread(o, o->state);
+
+ pa_source_output_update_max_rewind(o, s->thread_info.max_rewind);
+
+ /* We don't just invalidate the requested latency here,
+ * because if we are in a move we might need to fix up the
+ * requested latency. */
+ pa_source_output_set_requested_latency_within_thread(o, o->thread_info.requested_source_latency);
+
return 0;
}
case PA_SOURCE_MESSAGE_REMOVE_OUTPUT: {
pa_source_output *o = PA_SOURCE_OUTPUT(userdata);
+ pa_source_output_set_state_within_thread(o, o->state);
+
if (o->detach)
o->detach(o);
pa_assert(o->thread_info.attached);
o->thread_info.attached = FALSE;
+ if (o->thread_info.direct_on_input) {
+ pa_hashmap_remove(o->thread_info.direct_on_input->thread_info.direct_outputs, PA_UINT32_TO_PTR(o->index));
+ o->thread_info.direct_on_input = NULL;
+ }
+
if (pa_hashmap_remove(s->thread_info.outputs, PA_UINT32_TO_PTR(o->index)))
pa_source_output_unref(o);
+ pa_source_invalidate_requested_latency(s);
+
return 0;
}
@@ -507,28 +700,65 @@ int pa_source_process_msg(pa_msgobject *object, int code, void *userdata, int64_
*((pa_bool_t*) userdata) = s->thread_info.soft_muted;
return 0;
- case PA_SOURCE_MESSAGE_PING:
- return 0;
-
case PA_SOURCE_MESSAGE_SET_STATE:
s->thread_info.state = PA_PTR_TO_UINT(userdata);
return 0;
case PA_SOURCE_MESSAGE_DETACH:
- /* We're detaching all our output streams so that the
- * asyncmsgq and rtpoll fields can be changed without
- * problems */
+ /* Detach all streams */
pa_source_detach_within_thread(s);
- break;
+ return 0;
case PA_SOURCE_MESSAGE_ATTACH:
/* Reattach all streams */
pa_source_attach_within_thread(s);
- break;
+ return 0;
+
+ case PA_SOURCE_MESSAGE_GET_REQUESTED_LATENCY: {
+
+ pa_usec_t *usec = userdata;
+ *usec = pa_source_get_requested_latency_within_thread(s);
+
+ if (*usec == (pa_usec_t) -1)
+ *usec = s->thread_info.max_latency;
+
+ return 0;
+ }
+
+ case PA_SOURCE_MESSAGE_SET_LATENCY_RANGE: {
+ pa_usec_t *r = userdata;
+
+ pa_source_update_latency_range(s, r[0], r[1]);
+
+ return 0;
+ }
+
+ case PA_SOURCE_MESSAGE_GET_LATENCY_RANGE: {
+ pa_usec_t *r = userdata;
+
+ r[0] = s->thread_info.min_latency;
+ r[1] = s->thread_info.max_latency;
+
+ return 0;
+ }
+
+ case PA_SOURCE_MESSAGE_GET_MAX_REWIND:
+
+ *((size_t*) userdata) = s->thread_info.max_rewind;
+ return 0;
case PA_SOURCE_MESSAGE_GET_LATENCY:
+
+ if (s->monitor_of) {
+ *((pa_usec_t*) userdata) = 0;
+ return 0;
+ }
+
+ /* Implementors need to overwrite this implementation! */
+ return -1;
+
case PA_SOURCE_MESSAGE_MAX:
;
}
@@ -536,6 +766,7 @@ int pa_source_process_msg(pa_msgobject *object, int code, void *userdata, int64_
return -1;
}
+/* Called from main thread */
int pa_source_suspend_all(pa_core *c, pa_bool_t suspend) {
uint32_t idx;
pa_source *source;
@@ -549,41 +780,206 @@ int pa_source_suspend_all(pa_core *c, pa_bool_t suspend) {
return ret;
}
+/* Called from main thread */
void pa_source_detach(pa_source *s) {
pa_source_assert_ref(s);
- pa_assert(PA_SOURCE_LINKED(s->state));
+ pa_assert(PA_SOURCE_IS_LINKED(s->state));
- pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_DETACH, NULL, 0, NULL);
+ pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_DETACH, NULL, 0, NULL) == 0);
}
+/* Called from main thread */
void pa_source_attach(pa_source *s) {
pa_source_assert_ref(s);
- pa_assert(PA_SOURCE_LINKED(s->state));
+ pa_assert(PA_SOURCE_IS_LINKED(s->state));
- pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_ATTACH, NULL, 0, NULL);
+ pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_ATTACH, NULL, 0, NULL) == 0);
}
+/* Called from IO thread */
void pa_source_detach_within_thread(pa_source *s) {
pa_source_output *o;
void *state = NULL;
pa_source_assert_ref(s);
- pa_assert(PA_SOURCE_LINKED(s->thread_info.state));
+ pa_assert(PA_SOURCE_IS_LINKED(s->thread_info.state));
while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL)))
if (o->detach)
o->detach(o);
}
+/* Called from IO thread */
void pa_source_attach_within_thread(pa_source *s) {
pa_source_output *o;
void *state = NULL;
pa_source_assert_ref(s);
- pa_assert(PA_SOURCE_LINKED(s->thread_info.state));
+ pa_assert(PA_SOURCE_IS_LINKED(s->thread_info.state));
while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL)))
if (o->attach)
o->attach(o);
+}
+
+/* Called from IO thread */
+pa_usec_t pa_source_get_requested_latency_within_thread(pa_source *s) {
+ pa_usec_t result = (pa_usec_t) -1;
+ pa_source_output *o;
+ void *state = NULL;
+
+ pa_source_assert_ref(s);
+
+ if (s->thread_info.requested_latency_valid)
+ return s->thread_info.requested_latency;
+
+ while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL)))
+
+ if (o->thread_info.requested_source_latency != (pa_usec_t) -1 &&
+ (result == (pa_usec_t) -1 || result > o->thread_info.requested_source_latency))
+ result = o->thread_info.requested_source_latency;
+
+ 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 (s->thread_info.min_latency > 0 && result < s->thread_info.min_latency)
+ result = s->thread_info.min_latency;
+ }
+
+ 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_source_assert_ref(s);
+ pa_assert(PA_SOURCE_IS_LINKED(s->state));
+
+ if (!PA_SOURCE_IS_OPENED(s->state))
+ return 0;
+
+ pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_REQUESTED_LATENCY, &usec, 0, NULL) == 0);
+
+ return usec;
+}
+
+/* Called from IO thread */
+void pa_source_set_max_rewind(pa_source *s, size_t max_rewind) {
+ pa_source_output *o;
+ void *state = NULL;
+
+ pa_source_assert_ref(s);
+
+ if (max_rewind == s->thread_info.max_rewind)
+ return;
+
+ s->thread_info.max_rewind = max_rewind;
+
+ if (PA_SOURCE_IS_LINKED(s->thread_info.state)) {
+ while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL)))
+ pa_source_output_update_max_rewind(o, s->thread_info.max_rewind);
+ }
+}
+
+void pa_source_invalidate_requested_latency(pa_source *s) {
+ pa_source_output *o;
+ void *state = NULL;
+
+ pa_source_assert_ref(s);
+
+ s->thread_info.requested_latency_valid = FALSE;
+
+ if (s->update_requested_latency)
+ s->update_requested_latency(s);
+
+ while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL)))
+ o->update_source_requested_latency(o);
+
+ if (s->monitor_of)
+ pa_sink_invalidate_requested_latency(s->monitor_of);
+}
+
+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 (max_latency == (pa_usec_t) -1)
+ max_latency = min_latency;
+
+ pa_assert(!min_latency || !max_latency ||
+ min_latency <= max_latency);
+
+ if (PA_SINK_IS_LINKED(s->state)) {
+ pa_usec_t r[2];
+
+ r[0] = min_latency;
+ 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;
+ }
+}
+
+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);
+ pa_assert(max_latency);
+
+ if (PA_SOURCE_IS_LINKED(s->state)) {
+ pa_usec_t r[2] = { 0, 0 };
+
+ pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_LATENCY_RANGE, r, 0, NULL) == 0);
+
+ *min_latency = r[0];
+ *max_latency = r[1];
+ } else {
+ *min_latency = s->thread_info.min_latency;
+ *max_latency = s->thread_info.max_latency;
+ }
+}
+
+/* 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;
+ void *state = NULL;
+
+ pa_source_assert_ref(s);
+
+ 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);
+
+ pa_source_invalidate_requested_latency(s);
+}
+
+size_t pa_source_get_max_rewind(pa_source *s) {
+ size_t r;
+ pa_source_assert_ref(s);
+
+ if (!PA_SOURCE_IS_LINKED(s->state))
+ return s->thread_info.max_rewind;
+
+ pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_MAX_REWIND, &r, 0, NULL) == 0);
+ return r;
}
diff --git a/src/pulsecore/source.h b/src/pulsecore/source.h
index bd0a9122..f4a17e8d 100644
--- a/src/pulsecore/source.h
+++ b/src/pulsecore/source.h
@@ -1,8 +1,6 @@
#ifndef foopulsesourcehfoo
#define foopulsesourcehfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -33,7 +31,6 @@ typedef struct pa_source pa_source;
#include <pulse/channelmap.h>
#include <pulse/volume.h>
-#include <pulsecore/core-def.h>
#include <pulsecore/core.h>
#include <pulsecore/idxset.h>
#include <pulsecore/memblock.h>
@@ -43,6 +40,7 @@ typedef struct pa_source pa_source;
#include <pulsecore/asyncmsgq.h>
#include <pulsecore/msgobject.h>
#include <pulsecore/rtpoll.h>
+#include <pulsecore/source-output.h>
#define PA_MAX_OUTPUTS_PER_SOURCE 32
@@ -54,11 +52,11 @@ typedef enum pa_source_state {
PA_SOURCE_UNLINKED
} pa_source_state_t;
-static inline pa_bool_t PA_SOURCE_OPENED(pa_source_state_t x) {
+static inline pa_bool_t PA_SOURCE_IS_OPENED(pa_source_state_t x) {
return x == PA_SOURCE_RUNNING || x == PA_SOURCE_IDLE;
}
-static inline pa_bool_t PA_SOURCE_LINKED(pa_source_state_t x) {
+static inline pa_bool_t PA_SOURCE_IS_LINKED(pa_source_state_t x) {
return x == PA_SOURCE_RUNNING || x == PA_SOURCE_IDLE || x == PA_SOURCE_SUSPENDED;
}
@@ -71,7 +69,8 @@ struct pa_source {
pa_source_flags_t flags;
char *name;
- char *description, *driver; /* may be NULL */
+ char *driver; /* may be NULL */
+ pa_proplist *proplist;
pa_module *module; /* may be NULL */
@@ -84,18 +83,45 @@ struct pa_source {
pa_cvolume volume;
pa_bool_t muted;
- pa_bool_t refresh_volume;
- pa_bool_t refresh_muted;
+ pa_bool_t refresh_volume:1;
+ pa_bool_t refresh_muted:1;
+
+ pa_asyncmsgq *asyncmsgq;
+ pa_rtpoll *rtpoll;
+
+ pa_memchunk silence;
+
+ /* Called when the main loop requests a state change. Called from
+ * main loop context. If returns -1 the state change will be
+ * inhibited */
int (*set_state)(pa_source*source, pa_source_state_t state); /* may be NULL */
- int (*set_volume)(pa_source *s); /* dito */
+
+ /* Callled when the volume is queried. Called from main loop
+ * context. If this is NULL a PA_SOURCE_MESSAGE_GET_VOLUME message
+ * will be sent to the IO thread instead. If refresh_volume is
+ * FALSE neither this function is called nor a message is sent. */
int (*get_volume)(pa_source *s); /* dito */
- int (*set_mute)(pa_source *s); /* dito */
+
+ /* Called when the volume shall be changed. Called from main loop
+ * context. If this is NULL a PA_SOURCE_MESSAGE_SET_VOLUME message
+ * will be sent to the IO thread instead. */
+ int (*set_volume)(pa_source *s); /* dito */
+
+ /* Called when the mute setting is queried. Called from main loop
+ * context. If this is NULL a PA_SOURCE_MESSAGE_GET_MUTE message
+ * will be sent to the IO thread instead. If refresh_mute is
+ * FALSE neither this function is called nor a message is sent.*/
int (*get_mute)(pa_source *s); /* dito */
- pa_usec_t (*get_latency)(pa_source *s); /* dito */
- pa_asyncmsgq *asyncmsgq;
- pa_rtpoll *rtpoll;
+ /* Called when the mute setting shall be changed. Called from main
+ * loop context. If this is NULL a PA_SOURCE_MESSAGE_SET_MUTE
+ * message will be sent to the IO thread instead. */
+ int (*set_mute)(pa_source *s); /* dito */
+
+ /* Called when a the requested latency is changed. Called from IO
+ * thread context. */
+ void (*update_requested_latency)(pa_source *s); /* dito */
/* Contains copies of the above data so that the real-time worker
* thread can work without access locking */
@@ -103,7 +129,17 @@ struct pa_source {
pa_source_state_t state;
pa_hashmap *outputs;
pa_cvolume soft_volume;
- pa_bool_t soft_muted;
+ pa_bool_t soft_muted:1;
+
+ pa_bool_t requested_latency_valid:1;
+ pa_usec_t requested_latency;
+
+ /* Then number of bytes this source will be rewound for at
+ * max. (Only used on monitor sources) */
+ size_t max_rewind;
+
+ pa_usec_t min_latency; /* we won't go below this latency */
+ pa_usec_t max_latency; /* An upper limit for the latencies */
} thread_info;
void *userdata;
@@ -120,44 +156,75 @@ typedef enum pa_source_message {
PA_SOURCE_MESSAGE_GET_MUTE,
PA_SOURCE_MESSAGE_SET_MUTE,
PA_SOURCE_MESSAGE_GET_LATENCY,
+ PA_SOURCE_MESSAGE_GET_REQUESTED_LATENCY,
PA_SOURCE_MESSAGE_SET_STATE,
- PA_SOURCE_MESSAGE_PING,
PA_SOURCE_MESSAGE_ATTACH,
PA_SOURCE_MESSAGE_DETACH,
+ PA_SOURCE_MESSAGE_SET_LATENCY_RANGE,
+ PA_SOURCE_MESSAGE_GET_LATENCY_RANGE,
+ PA_SOURCE_MESSAGE_GET_MAX_REWIND,
PA_SOURCE_MESSAGE_MAX
} pa_source_message_t;
+typedef struct pa_source_new_data {
+ char *name;
+ pa_bool_t namereg_fail;
+ pa_proplist *proplist;
+
+ const char *driver;
+ pa_module *module;
+
+ pa_sample_spec sample_spec;
+ pa_bool_t sample_spec_is_set;
+ pa_channel_map channel_map;
+ pa_bool_t channel_map_is_set;
+
+ pa_cvolume volume;
+ pa_bool_t volume_is_set;
+ pa_bool_t muted;
+ pa_bool_t muted_is_set;
+} pa_source_new_data;
+
+pa_source_new_data* pa_source_new_data_init(pa_source_new_data *data);
+void pa_source_new_data_set_name(pa_source_new_data *data, const char *name);
+void pa_source_new_data_set_sample_spec(pa_source_new_data *data, const pa_sample_spec *spec);
+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_done(pa_source_new_data *data);
+
/* To be called exclusively by the source driver, from main context */
pa_source* pa_source_new(
pa_core *core,
- const char *driver,
- const char *name,
- int namereg_fail,
- const pa_sample_spec *spec,
- const pa_channel_map *map);
+ pa_source_new_data *data,
+ pa_source_flags_t flags);
void pa_source_put(pa_source *s);
void pa_source_unlink(pa_source *s);
-void pa_source_set_module(pa_source *s, pa_module *m);
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_latency_range(pa_source *s, pa_usec_t min_latency, pa_usec_t max_latency);
+
void pa_source_detach(pa_source *s);
void pa_source_attach(pa_source *s);
/* May be called by everyone, from main context */
+/* The returned value is supposed to be in the time domain of the sound card! */
pa_usec_t pa_source_get_latency(pa_source *s);
+pa_usec_t pa_source_get_requested_latency(pa_source *s);
+void pa_source_get_latency_range(pa_source *s, pa_usec_t *min_latency, pa_usec_t *max_latency);
+
+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);
-void pa_source_ping(pa_source *s);
-
void pa_source_set_volume(pa_source *source, const pa_cvolume *volume);
const pa_cvolume *pa_source_get_volume(pa_source *source);
void pa_source_set_mute(pa_source *source, pa_bool_t mute);
@@ -169,11 +236,22 @@ unsigned pa_source_used_by(pa_source *s); /* Number of connected streams that ar
/* To be called exclusively by the source driver, from IO context */
-void pa_source_post(pa_source*s, const pa_memchunk *b);
+void pa_source_post(pa_source*s, const pa_memchunk *chunk);
+void pa_source_post_direct(pa_source*s, pa_source_output *o, const pa_memchunk *chunk);
+void pa_source_process_rewind(pa_source *s, size_t nbytes);
int pa_source_process_msg(pa_msgobject *o, int code, void *userdata, int64_t, pa_memchunk *chunk);
void pa_source_attach_within_thread(pa_source *s);
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);
+
+/* To be called exclusively by source output drivers, from IO context */
+
+void pa_source_invalidate_requested_latency(pa_source *s);
+
#endif
diff --git a/src/pulsecore/speex/arch.h b/src/pulsecore/speex/arch.h
index e2d731ac..9987c8fb 100644
--- a/src/pulsecore/speex/arch.h
+++ b/src/pulsecore/speex/arch.h
@@ -7,18 +7,18 @@
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
-
+
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
-
+
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
-
+
- Neither the name of the Xiph.org Foundation nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
-
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
@@ -35,6 +35,45 @@
#ifndef ARCH_H
#define ARCH_H
+#ifndef SPEEX_VERSION
+#define SPEEX_MAJOR_VERSION 1 /**< Major Speex version. */
+#define SPEEX_MINOR_VERSION 1 /**< Minor Speex version. */
+#define SPEEX_MICRO_VERSION 15 /**< Micro Speex version. */
+#define SPEEX_EXTRA_VERSION "" /**< Extra Speex version. */
+#define SPEEX_VERSION "speex-1.2beta3" /**< Speex version string. */
+#endif
+
+/* A couple test to catch stupid option combinations */
+#ifdef FIXED_POINT
+
+#ifdef FLOATING_POINT
+#error You cannot compile as floating point and fixed point at the same time
+#endif
+#ifdef _USE_SSE
+#error SSE is only for floating-point
+#endif
+#if ((defined (ARM4_ASM)||defined (ARM4_ASM)) && defined(BFIN_ASM)) || (defined (ARM4_ASM)&&defined(ARM5E_ASM))
+#error Make up your mind. What CPU do you have?
+#endif
+#ifdef VORBIS_PSYCHO
+#error Vorbis-psy model currently not implemented in fixed-point
+#endif
+
+#else
+
+#ifndef FLOATING_POINT
+#error You now need to define either FIXED_POINT or FLOATING_POINT
+#endif
+#if defined (ARM4_ASM) || defined(ARM5E_ASM) || defined(BFIN_ASM)
+#error I suppose you can have a [ARM4/ARM5E/Blackfin] that has float instructions?
+#endif
+#ifdef FIXED_POINT_DEBUG
+#error "Don't you think enabling fixed-point is a good thing to do if you want to debug that?"
+#endif
+
+
+#endif
+
#ifndef OUTSIDE_SPEEX
#include "speex/speex_types.h"
#endif
@@ -68,6 +107,7 @@ typedef spx_word32_t spx_sig_t;
#define LPC_SHIFT 13
#define LSP_SHIFT 13
#define SIG_SHIFT 14
+#define GAIN_SHIFT 6
#define VERY_SMALL 0
#define VERY_LARGE32 ((spx_word32_t)2147483647)
@@ -111,9 +151,6 @@ typedef float spx_word32_t;
#define GAIN_SCALING 1.f
#define GAIN_SCALING_1 1.f
-#define LPC_SHIFT 0
-#define LSP_SHIFT 0
-#define SIG_SHIFT 0
#define VERY_SMALL 1e-15f
#define VERY_LARGE32 1e15f
@@ -182,11 +219,11 @@ typedef float spx_word32_t;
#if defined (CONFIG_TI_C54X) || defined (CONFIG_TI_C55X)
/* 2 on TI C5x DSP */
-#define BYTES_PER_CHAR 2
+#define BYTES_PER_CHAR 2
#define BITS_PER_CHAR 16
#define LOG2_BITS_PER_CHAR 4
-#else
+#else
#define BYTES_PER_CHAR 1
#define BITS_PER_CHAR 8
@@ -194,4 +231,11 @@ typedef float spx_word32_t;
#endif
+
+
+#ifdef FIXED_DEBUG
+long long spx_mips=0;
+#endif
+
+
#endif
diff --git a/src/pulsecore/speex/fixed_generic.h b/src/pulsecore/speex/fixed_generic.h
index 2948177c..547e22c7 100644
--- a/src/pulsecore/speex/fixed_generic.h
+++ b/src/pulsecore/speex/fixed_generic.h
@@ -7,18 +7,18 @@
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
-
+
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
-
+
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
-
+
- Neither the name of the Xiph.org Foundation nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
-
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
diff --git a/src/pulsecore/speex/resample.c b/src/pulsecore/speex/resample.c
index 1cc4d490..1e592002 100644
--- a/src/pulsecore/speex/resample.c
+++ b/src/pulsecore/speex/resample.c
@@ -1,5 +1,5 @@
/* Copyright (C) 2007 Jean-Marc Valin
-
+
File: resample.c
Arbitrary resampling code
@@ -37,17 +37,23 @@
- Low memory requirement
- Good *perceptual* quality (and not best SNR)
- The code is working, but it's in a very early stage, so it may have
- artifacts, noise or subliminal messages from satan. Also, the API
- isn't stable and I can actually promise that I *will* change the API
- some time in the future.
-
-TODO list:
- - Variable calculation resolution depending on quality setting
- - Single vs double in float mode
- - 16-bit vs 32-bit (sinc only) in fixed-point mode
- - Make sure the filter update works even when changing params
- after only a few samples procesed
+ Warning: This resampler is relatively new. Although I think I got rid of
+ all the major bugs and I don't expect the API to change anymore, there
+ may be something I've missed. So use with caution.
+
+ This algorithm is based on this original resampling algorithm:
+ Smith, Julius O. Digital Audio Resampling Home Page
+ Center for Computer Research in Music and Acoustics (CCRMA),
+ Stanford University, 2007.
+ Web published at http://www-ccrma.stanford.edu/~jos/resample/.
+
+ There is one main difference, though. This resampler uses cubic
+ interpolation instead of linear interpolation in the above paper. This
+ makes the table much smaller and makes it possible to compute that table
+ on a per-stream basis. In turn, being able to tweak the table for each
+ stream makes it possible to both reduce complexity on simple ratios
+ (e.g. 2/3), and get rid of the rounding operations in the inner loop.
+ The latter both reduces CPU time and makes the algorithm more SIMD-friendly.
*/
#ifdef HAVE_CONFIG_H
@@ -62,9 +68,10 @@ static void speex_free (void *ptr) {free(ptr);}
#include "speex_resampler.h"
#include "arch.h"
#else /* OUTSIDE_SPEEX */
-
+
#include "speex/speex_resampler.h"
-#include "misc.h"
+#include "arch.h"
+#include "os_support.h"
#endif /* OUTSIDE_SPEEX */
#include <math.h>
@@ -74,11 +81,11 @@ static void speex_free (void *ptr) {free(ptr);}
#endif
#ifdef FIXED_POINT
-#define WORD2INT(x) ((x) < -32767 ? -32768 : ((x) > 32766 ? 32767 : (x)))
+#define WORD2INT(x) ((x) < -32767 ? -32768 : ((x) > 32766 ? 32767 : (x)))
#else
-#define WORD2INT(x) ((x) < -32767.5f ? -32768 : ((x) > 32766.5f ? 32767 : floor(.5+(x))))
+#define WORD2INT(x) ((x) < -32767.5f ? -32768 : ((x) > 32766.5f ? 32767 : floor(.5+(x))))
#endif
-
+
/*#define float double*/
#define FILTER_SIZE 64
#define OVERSAMPLE 8
@@ -97,7 +104,7 @@ struct SpeexResamplerState_ {
spx_uint32_t out_rate;
spx_uint32_t num_rate;
spx_uint32_t den_rate;
-
+
int quality;
spx_uint32_t nb_channels;
spx_uint32_t filt_len;
@@ -108,17 +115,17 @@ struct SpeexResamplerState_ {
spx_uint32_t oversample;
int initialised;
int started;
-
+
/* These are per-channel */
spx_int32_t *last_sample;
spx_uint32_t *samp_frac_num;
spx_uint32_t *magic_samples;
-
+
spx_word16_t *mem;
spx_word16_t *sinc_table;
spx_uint32_t sinc_table_length;
resampler_basic_func resampler_ptr;
-
+
int in_stride;
int out_stride;
} ;
@@ -160,7 +167,7 @@ static double kaiser8_table[36] = {
0.32108304, 0.27619388, 0.23465776, 0.19672670, 0.16255380, 0.13219758,
0.10562887, 0.08273982, 0.06335451, 0.04724088, 0.03412321, 0.02369490,
0.01563093, 0.00959968, 0.00527363, 0.00233883, 0.00050000, 0.00000000};
-
+
static double kaiser6_table[36] = {
0.99733006, 1.00000000, 0.99733006, 0.98935595, 0.97618418, 0.95799003,
0.93501423, 0.90755855, 0.87598009, 0.84068475, 0.80211977, 0.76076565,
@@ -173,7 +180,7 @@ struct FuncDef {
double *table;
int oversample;
};
-
+
static struct FuncDef _KAISER12 = {kaiser12_table, 64};
#define KAISER12 (&_KAISER12)
/*static struct FuncDef _KAISER12 = {kaiser12_table, 32};
@@ -195,7 +202,7 @@ struct QualityMapping {
/* This table maps conversion quality to internal parameters. There are two
- reasons that explain why the up-sampling bandwidth is larger than the
+ reasons that explain why the up-sampling bandwidth is larger than the
down-sampling bandwidth:
1) When up-sampling, we can assume that the spectrum is already attenuated
close to the Nyquist rate (from an A/D or a previous resampling filter)
@@ -221,7 +228,7 @@ static double compute_func(float x, struct FuncDef *func)
{
float y, frac;
double interp[4];
- int ind;
+ int ind;
y = x*func->oversample;
ind = (int)floor(y);
frac = (y-ind);
@@ -232,7 +239,7 @@ static double compute_func(float x, struct FuncDef *func)
interp[0] = -0.3333333333*frac + 0.5*(frac*frac) - 0.1666666667*(frac*frac*frac);
/* Just to make sure we don't have rounding problems */
interp[1] = 1.f-interp[3]-interp[2]-interp[0];
-
+
/*sum = frac*accum[1] + (1-frac)*accum[2];*/
return interp[0]*func->table[ind] + interp[1]*func->table[ind+1] + interp[2]*func->table[ind+2] + interp[3]*func->table[ind+3];
}
@@ -320,7 +327,7 @@ static int resampler_basic_direct_single(SpeexResamplerState *st, spx_uint32_t c
{
int j;
spx_word32_t sum=0;
-
+
/* We already have all the filter coefficients pre-computed in the table */
const spx_word16_t *ptr;
/* Do the memory part */
@@ -328,7 +335,7 @@ static int resampler_basic_direct_single(SpeexResamplerState *st, spx_uint32_t c
{
sum += MULT16_16(mem[last_sample+j],st->sinc_table[samp_frac_num*st->filt_len+j]);
}
-
+
/* Do the new part */
ptr = in+st->in_stride*(last_sample-N+1+j);
for (;j<N;j++)
@@ -336,7 +343,7 @@ static int resampler_basic_direct_single(SpeexResamplerState *st, spx_uint32_t c
sum += MULT16_16(*ptr,st->sinc_table[samp_frac_num*st->filt_len+j]);
ptr += st->in_stride;
}
-
+
*out = PSHR32(sum,15);
out += st->out_stride;
out_sample++;
@@ -368,7 +375,7 @@ static int resampler_basic_direct_double(SpeexResamplerState *st, spx_uint32_t c
{
int j;
double sum=0;
-
+
/* We already have all the filter coefficients pre-computed in the table */
const spx_word16_t *ptr;
/* Do the memory part */
@@ -376,7 +383,7 @@ static int resampler_basic_direct_double(SpeexResamplerState *st, spx_uint32_t c
{
sum += MULT16_16(mem[last_sample+j],(double)st->sinc_table[samp_frac_num*st->filt_len+j]);
}
-
+
/* Do the new part */
ptr = in+st->in_stride*(last_sample-N+1+j);
for (;j<N;j++)
@@ -384,7 +391,7 @@ static int resampler_basic_direct_double(SpeexResamplerState *st, spx_uint32_t c
sum += MULT16_16(*ptr,(double)st->sinc_table[samp_frac_num*st->filt_len+j]);
ptr += st->in_stride;
}
-
+
*out = sum;
out += st->out_stride;
out_sample++;
@@ -414,7 +421,7 @@ static int resampler_basic_interpolate_single(SpeexResamplerState *st, spx_uint3
{
int j;
spx_word32_t sum=0;
-
+
/* We need to interpolate the sinc filter */
spx_word32_t accum[4] = {0.f,0.f, 0.f, 0.f};
spx_word16_t interp[4];
@@ -428,7 +435,7 @@ static int resampler_basic_interpolate_single(SpeexResamplerState *st, spx_uint3
frac = ((float)((samp_frac_num*st->oversample) % st->den_rate))/st->den_rate;
#endif
/* This code is written like this to make it easy to optimise with SIMD.
- For most DSPs, it would be best to split the loops in two because most DSPs
+ For most DSPs, it would be best to split the loops in two because most DSPs
have only two accumulators */
for (j=0;last_sample-N+1+j < 0;j++)
{
@@ -451,7 +458,7 @@ static int resampler_basic_interpolate_single(SpeexResamplerState *st, spx_uint3
}
cubic_coef(frac, interp);
sum = MULT16_32_Q15(interp[0],accum[0]) + MULT16_32_Q15(interp[1],accum[1]) + MULT16_32_Q15(interp[2],accum[2]) + MULT16_32_Q15(interp[3],accum[3]);
-
+
*out = PSHR32(sum,15);
out += st->out_stride;
out_sample++;
@@ -483,7 +490,7 @@ static int resampler_basic_interpolate_double(SpeexResamplerState *st, spx_uint3
{
int j;
spx_word32_t sum=0;
-
+
/* We need to interpolate the sinc filter */
double accum[4] = {0.f,0.f, 0.f, 0.f};
float interp[4];
@@ -492,7 +499,7 @@ static int resampler_basic_interpolate_double(SpeexResamplerState *st, spx_uint3
int offset = samp_frac_num*st->oversample/st->den_rate;
float frac = alpha*st->oversample - offset;
/* This code is written like this to make it easy to optimise with SIMD.
- For most DSPs, it would be best to split the loops in two because most DSPs
+ For most DSPs, it would be best to split the loops in two because most DSPs
have only two accumulators */
for (j=0;last_sample-N+1+j < 0;j++)
{
@@ -515,7 +522,7 @@ static int resampler_basic_interpolate_double(SpeexResamplerState *st, spx_uint3
}
cubic_coef(frac, interp);
sum = interp[0]*accum[0] + interp[1]*accum[1] + interp[2]*accum[2] + interp[3]*accum[3];
-
+
*out = PSHR32(sum,15);
out += st->out_stride;
out_sample++;
@@ -536,11 +543,11 @@ static int resampler_basic_interpolate_double(SpeexResamplerState *st, spx_uint3
static void update_filter(SpeexResamplerState *st)
{
spx_uint32_t old_length;
-
+
old_length = st->filt_len;
st->oversample = quality_map[st->quality].oversample;
st->filt_len = quality_map[st->quality].base_length;
-
+
if (st->num_rate > st->den_rate)
{
/* down-sampling */
@@ -616,7 +623,7 @@ static void update_filter(SpeexResamplerState *st)
st->int_advance = st->num_rate/st->den_rate;
st->frac_advance = st->num_rate%st->den_rate;
-
+
/* Here's the place where we update the filter memory to take into account
the change in filter length. It's probably the messiest part of the code
due to handling of lots of corner cases. */
@@ -654,7 +661,7 @@ static void update_filter(SpeexResamplerState *st)
/*if (st->magic_samples[i])*/
{
/* Try and remove the magic samples as if nothing had happened */
-
+
/* FIXME: This is wrong but for now we need it to avoid going over the array bounds */
olen = old_length + 2*st->magic_samples[i];
for (j=old_length-2+st->magic_samples[i];j>=0;j--)
@@ -729,12 +736,12 @@ SpeexResamplerState *speex_resampler_init_frac(spx_uint32_t nb_channels, spx_uin
st->filt_len = 0;
st->mem = 0;
st->resampler_ptr = 0;
-
+
st->cutoff = 1.f;
st->nb_channels = nb_channels;
st->in_stride = 1;
st->out_stride = 1;
-
+
/* Per channel data */
st->last_sample = (spx_int32_t*)speex_alloc(nb_channels*sizeof(int));
st->magic_samples = (spx_uint32_t*)speex_alloc(nb_channels*sizeof(int));
@@ -749,9 +756,9 @@ SpeexResamplerState *speex_resampler_init_frac(spx_uint32_t nb_channels, spx_uin
speex_resampler_set_quality(st, quality);
speex_resampler_set_rate_frac(st, ratio_num, ratio_den, in_rate, out_rate);
-
+
update_filter(st);
-
+
st->initialised = 1;
if (err)
*err = RESAMPLER_ERR_SUCCESS;
@@ -780,14 +787,14 @@ static int speex_resampler_process_native(SpeexResamplerState *st, spx_uint32_t
spx_uint32_t tmp_out_len = 0;
mem = st->mem + channel_index * st->mem_alloc_size;
st->started = 1;
-
+
/* Handle the case where we have samples left from a reduction in filter length */
if (st->magic_samples[channel_index])
{
int istride_save;
spx_uint32_t tmp_in_len;
spx_uint32_t tmp_magic;
-
+
istride_save = st->in_stride;
tmp_in_len = st->magic_samples[channel_index];
tmp_out_len = *out_len;
@@ -809,20 +816,20 @@ static int speex_resampler_process_native(SpeexResamplerState *st, spx_uint32_t
out += tmp_out_len*st->out_stride;
*out_len -= tmp_out_len;
}
-
+
/* Call the right resampler through the function ptr */
out_sample = st->resampler_ptr(st, channel_index, in, in_len, out, out_len);
-
+
if (st->last_sample[channel_index] < (spx_int32_t)*in_len)
*in_len = st->last_sample[channel_index];
*out_len = out_sample+tmp_out_len;
st->last_sample[channel_index] -= *in_len;
-
+
for (j=0;j<N-1-(spx_int32_t)*in_len;j++)
mem[j] = mem[j+*in_len];
for (;j<N-1;j++)
mem[j] = in[st->in_stride*(j+*in_len-N+1)];
-
+
return RESAMPLER_ERR_SUCCESS;
}
@@ -879,7 +886,7 @@ int speex_resampler_process_float(SpeexResamplerState *st, spx_uint32_t channel_
olen -= ochunk;
}
*in_len -= ilen;
- *out_len -= olen;
+ *out_len -= olen;
#endif
return RESAMPLER_ERR_SUCCESS;
}
@@ -942,7 +949,7 @@ int speex_resampler_process_int(SpeexResamplerState *st, spx_uint32_t channel_in
olen -= ochunk;
}
*in_len -= ilen;
- *out_len -= olen;
+ *out_len -= olen;
#endif
return RESAMPLER_ERR_SUCCESS;
}
@@ -966,7 +973,7 @@ int speex_resampler_process_interleaved_float(SpeexResamplerState *st, const flo
return RESAMPLER_ERR_SUCCESS;
}
-
+
int speex_resampler_process_interleaved_int(SpeexResamplerState *st, const spx_int16_t *in, spx_uint32_t *in_len, spx_int16_t *out, spx_uint32_t *out_len)
{
spx_uint32_t i;
@@ -1003,7 +1010,7 @@ int speex_resampler_set_rate_frac(SpeexResamplerState *st, spx_uint32_t ratio_nu
spx_uint32_t i;
if (st->in_rate == in_rate && st->out_rate == out_rate && st->num_rate == ratio_num && st->den_rate == ratio_den)
return RESAMPLER_ERR_SUCCESS;
-
+
old_den = st->den_rate;
st->in_rate = in_rate;
st->out_rate = out_rate;
@@ -1018,7 +1025,7 @@ int speex_resampler_set_rate_frac(SpeexResamplerState *st, spx_uint32_t ratio_nu
st->den_rate /= fact;
}
}
-
+
if (old_den > 0)
{
for (i=0;i<st->nb_channels;i++)
@@ -1029,7 +1036,7 @@ int speex_resampler_set_rate_frac(SpeexResamplerState *st, spx_uint32_t ratio_nu
st->samp_frac_num[i] = st->den_rate-1;
}
}
-
+
if (st->initialised)
update_filter(st);
return RESAMPLER_ERR_SUCCESS;
diff --git a/src/pulsecore/speex/speex_resampler.h b/src/pulsecore/speex/speex_resampler.h
index c44fbcd0..8629eeb3 100644
--- a/src/pulsecore/speex/speex_resampler.h
+++ b/src/pulsecore/speex/speex_resampler.h
@@ -1,8 +1,8 @@
/* Copyright (C) 2007 Jean-Marc Valin
-
+
File: speex_resampler.h
Resampling code
-
+
The design goals of this code are:
- Very fast algorithm
- Low memory requirement
@@ -43,7 +43,7 @@
/********* WARNING: MENTAL SANITY ENDS HERE *************/
-/* If the resampler is defined outside of Speex, we change the symbol names so that
+/* If the resampler is defined outside of Speex, we change the symbol names so that
there won't be any clash if linking with Speex later on. */
/* #define RANDOM_PREFIX your software name here */
@@ -53,7 +53,7 @@
#define CAT_PREFIX2(a,b) a ## b
#define CAT_PREFIX(a,b) CAT_PREFIX2(a, b)
-
+
#define speex_resampler_init CAT_PREFIX(RANDOM_PREFIX,_resampler_init)
#define speex_resampler_init_frac CAT_PREFIX(RANDOM_PREFIX,_resampler_init_frac)
#define speex_resampler_destroy CAT_PREFIX(RANDOM_PREFIX,_resampler_destroy)
@@ -79,7 +79,7 @@
#define spx_int32_t int
#define spx_uint16_t unsigned short
#define spx_uint32_t unsigned int
-
+
#else /* OUTSIDE_SPEEX */
#include "speex/speex_types.h"
@@ -102,7 +102,7 @@ enum {
RESAMPLER_ERR_BAD_STATE = 2,
RESAMPLER_ERR_INVALID_ARG = 3,
RESAMPLER_ERR_PTR_OVERLAP = 4,
-
+
RESAMPLER_ERR_MAX_ERROR
};
@@ -118,14 +118,14 @@ typedef struct SpeexResamplerState_ SpeexResamplerState;
* @return Newly created resampler state
* @retval NULL Error: not enough memory
*/
-SpeexResamplerState *speex_resampler_init(spx_uint32_t nb_channels,
- spx_uint32_t in_rate,
- spx_uint32_t out_rate,
+SpeexResamplerState *speex_resampler_init(spx_uint32_t nb_channels,
+ spx_uint32_t in_rate,
+ spx_uint32_t out_rate,
int quality,
int *err);
-/** Create a new resampler with fractional input/output rates. The sampling
- * rate ratio is an arbitrary rational number with both the numerator and
+/** Create a new resampler with fractional input/output rates. The sampling
+ * rate ratio is an arbitrary rational number with both the numerator and
* denominator being 32-bit integers.
* @param nb_channels Number of channels to be processed
* @param ratio_num Numerator of the sampling rate ratio
@@ -137,11 +137,11 @@ SpeexResamplerState *speex_resampler_init(spx_uint32_t nb_channels,
* @return Newly created resampler state
* @retval NULL Error: not enough memory
*/
-SpeexResamplerState *speex_resampler_init_frac(spx_uint32_t nb_channels,
- spx_uint32_t ratio_num,
- spx_uint32_t ratio_den,
- spx_uint32_t in_rate,
- spx_uint32_t out_rate,
+SpeexResamplerState *speex_resampler_init_frac(spx_uint32_t nb_channels,
+ spx_uint32_t ratio_num,
+ spx_uint32_t ratio_den,
+ spx_uint32_t in_rate,
+ spx_uint32_t out_rate,
int quality,
int *err);
@@ -152,24 +152,24 @@ void speex_resampler_destroy(SpeexResamplerState *st);
/** Resample a float array. The input and output buffers must *not* overlap.
* @param st Resampler state
- * @param channel_index Index of the channel to process for the multi-channel
+ * @param channel_index Index of the channel to process for the multi-channel
* base (0 otherwise)
* @param in Input buffer
- * @param in_len Number of input samples in the input buffer. Returns the
+ * @param in_len Number of input samples in the input buffer. Returns the
* number of samples processed
* @param out Output buffer
* @param out_len Size of the output buffer. Returns the number of samples written
*/
-int speex_resampler_process_float(SpeexResamplerState *st,
- spx_uint32_t channel_index,
- const float *in,
- spx_uint32_t *in_len,
- float *out,
+int speex_resampler_process_float(SpeexResamplerState *st,
+ spx_uint32_t channel_index,
+ const float *in,
+ spx_uint32_t *in_len,
+ float *out,
spx_uint32_t *out_len);
/** Resample an int array. The input and output buffers must *not* overlap.
* @param st Resampler state
- * @param channel_index Index of the channel to process for the multi-channel
+ * @param channel_index Index of the channel to process for the multi-channel
* base (0 otherwise)
* @param in Input buffer
* @param in_len Number of input samples in the input buffer. Returns the number
@@ -177,11 +177,11 @@ int speex_resampler_process_float(SpeexResamplerState *st,
* @param out Output buffer
* @param out_len Size of the output buffer. Returns the number of samples written
*/
-int speex_resampler_process_int(SpeexResamplerState *st,
- spx_uint32_t channel_index,
- const spx_int16_t *in,
- spx_uint32_t *in_len,
- spx_int16_t *out,
+int speex_resampler_process_int(SpeexResamplerState *st,
+ spx_uint32_t channel_index,
+ const spx_int16_t *in,
+ spx_uint32_t *in_len,
+ spx_int16_t *out,
spx_uint32_t *out_len);
/** Resample an interleaved float array. The input and output buffers must *not* overlap.
@@ -193,10 +193,10 @@ int speex_resampler_process_int(SpeexResamplerState *st,
* @param out_len Size of the output buffer. Returns the number of samples written.
* This is all per-channel.
*/
-int speex_resampler_process_interleaved_float(SpeexResamplerState *st,
- const float *in,
- spx_uint32_t *in_len,
- float *out,
+int speex_resampler_process_interleaved_float(SpeexResamplerState *st,
+ const float *in,
+ spx_uint32_t *in_len,
+ float *out,
spx_uint32_t *out_len);
/** Resample an interleaved int array. The input and output buffers must *not* overlap.
@@ -208,10 +208,10 @@ int speex_resampler_process_interleaved_float(SpeexResamplerState *st,
* @param out_len Size of the output buffer. Returns the number of samples written.
* This is all per-channel.
*/
-int speex_resampler_process_interleaved_int(SpeexResamplerState *st,
- const spx_int16_t *in,
- spx_uint32_t *in_len,
- spx_int16_t *out,
+int speex_resampler_process_interleaved_int(SpeexResamplerState *st,
+ const spx_int16_t *in,
+ spx_uint32_t *in_len,
+ spx_int16_t *out,
spx_uint32_t *out_len);
/** Set (change) the input/output sampling rates (integer value).
@@ -219,8 +219,8 @@ int speex_resampler_process_interleaved_int(SpeexResamplerState *st,
* @param in_rate Input sampling rate (integer number of Hz).
* @param out_rate Output sampling rate (integer number of Hz).
*/
-int speex_resampler_set_rate(SpeexResamplerState *st,
- spx_uint32_t in_rate,
+int speex_resampler_set_rate(SpeexResamplerState *st,
+ spx_uint32_t in_rate,
spx_uint32_t out_rate);
/** Get the current input/output sampling rates (integer value).
@@ -228,11 +228,11 @@ int speex_resampler_set_rate(SpeexResamplerState *st,
* @param in_rate Input sampling rate (integer number of Hz) copied.
* @param out_rate Output sampling rate (integer number of Hz) copied.
*/
-void speex_resampler_get_rate(SpeexResamplerState *st,
- spx_uint32_t *in_rate,
+void speex_resampler_get_rate(SpeexResamplerState *st,
+ spx_uint32_t *in_rate,
spx_uint32_t *out_rate);
-/** Set (change) the input/output sampling rates and resampling ratio
+/** Set (change) the input/output sampling rates and resampling ratio
* (fractional values in Hz supported).
* @param st Resampler state
* @param ratio_num Numerator of the sampling rate ratio
@@ -240,10 +240,10 @@ void speex_resampler_get_rate(SpeexResamplerState *st,
* @param in_rate Input sampling rate rounded to the nearest integer (in Hz).
* @param out_rate Output sampling rate rounded to the nearest integer (in Hz).
*/
-int speex_resampler_set_rate_frac(SpeexResamplerState *st,
- spx_uint32_t ratio_num,
- spx_uint32_t ratio_den,
- spx_uint32_t in_rate,
+int speex_resampler_set_rate_frac(SpeexResamplerState *st,
+ spx_uint32_t ratio_num,
+ spx_uint32_t ratio_den,
+ spx_uint32_t in_rate,
spx_uint32_t out_rate);
/** Get the current resampling ratio. This will be reduced to the least
@@ -252,56 +252,56 @@ int speex_resampler_set_rate_frac(SpeexResamplerState *st,
* @param ratio_num Numerator of the sampling rate ratio copied
* @param ratio_den Denominator of the sampling rate ratio copied
*/
-void speex_resampler_get_ratio(SpeexResamplerState *st,
- spx_uint32_t *ratio_num,
+void speex_resampler_get_ratio(SpeexResamplerState *st,
+ spx_uint32_t *ratio_num,
spx_uint32_t *ratio_den);
/** Set (change) the conversion quality.
* @param st Resampler state
- * @param quality Resampling quality between 0 and 10, where 0 has poor
+ * @param quality Resampling quality between 0 and 10, where 0 has poor
* quality and 10 has very high quality.
*/
-int speex_resampler_set_quality(SpeexResamplerState *st,
+int speex_resampler_set_quality(SpeexResamplerState *st,
int quality);
/** Get the conversion quality.
* @param st Resampler state
- * @param quality Resampling quality between 0 and 10, where 0 has poor
+ * @param quality Resampling quality between 0 and 10, where 0 has poor
* quality and 10 has very high quality.
*/
-void speex_resampler_get_quality(SpeexResamplerState *st,
+void speex_resampler_get_quality(SpeexResamplerState *st,
int *quality);
/** Set (change) the input stride.
* @param st Resampler state
* @param stride Input stride
*/
-void speex_resampler_set_input_stride(SpeexResamplerState *st,
+void speex_resampler_set_input_stride(SpeexResamplerState *st,
spx_uint32_t stride);
/** Get the input stride.
* @param st Resampler state
* @param stride Input stride copied
*/
-void speex_resampler_get_input_stride(SpeexResamplerState *st,
+void speex_resampler_get_input_stride(SpeexResamplerState *st,
spx_uint32_t *stride);
/** Set (change) the output stride.
* @param st Resampler state
* @param stride Output stride
*/
-void speex_resampler_set_output_stride(SpeexResamplerState *st,
+void speex_resampler_set_output_stride(SpeexResamplerState *st,
spx_uint32_t stride);
/** Get the output stride.
* @param st Resampler state copied
* @param stride Output stride
*/
-void speex_resampler_get_output_stride(SpeexResamplerState *st,
+void speex_resampler_get_output_stride(SpeexResamplerState *st,
spx_uint32_t *stride);
-/** Make sure that the first samples to go out of the resamplers don't have
- * leading zeros. This is only useful before starting to use a newly created
+/** Make sure that the first samples to go out of the resamplers don't have
+ * leading zeros. This is only useful before starting to use a newly created
* resampler. It is recommended to use that when resampling an audio file, as
* it will generate a file with the same length. For real-time processing,
* it is probably easier not to use this call (so that the output duration
diff --git a/src/pulsecore/speexwrap.h b/src/pulsecore/speexwrap.h
index c0d5c0c0..617e4afb 100644
--- a/src/pulsecore/speexwrap.h
+++ b/src/pulsecore/speexwrap.h
@@ -1,8 +1,6 @@
#ifndef foopulsespeexwraphfoo
#define foopulsespeexwraphfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -39,10 +37,12 @@ SpeexResamplerState *paspfx_resampler_init(spx_uint32_t nb_channels, spx_uint32_
void paspfx_resampler_destroy(SpeexResamplerState *st);
int paspfx_resampler_process_interleaved_int(SpeexResamplerState *st, const spx_int16_t *in, spx_uint32_t *in_len, spx_int16_t *out, spx_uint32_t *out_len);
int paspfx_resampler_set_rate(SpeexResamplerState *st, spx_uint32_t in_rate, spx_uint32_t out_rate);
+int paspfx_resampler_reset_mem(SpeexResamplerState *st);
SpeexResamplerState *paspfl_resampler_init(spx_uint32_t nb_channels, spx_uint32_t in_rate, spx_uint32_t out_rate, int quality, int *err);
void paspfl_resampler_destroy(SpeexResamplerState *st);
int paspfl_resampler_process_interleaved_float(SpeexResamplerState *st, const float *in, spx_uint32_t *in_len, float *out, spx_uint32_t *out_len);
int paspfl_resampler_set_rate(SpeexResamplerState *st, spx_uint32_t in_rate, spx_uint32_t out_rate);
+int paspfl_resampler_reset_mem(SpeexResamplerState *st);
#endif
diff --git a/src/pulsecore/start-child.c b/src/pulsecore/start-child.c
new file mode 100644
index 00000000..1661383d
--- /dev/null
+++ b/src/pulsecore/start-child.c
@@ -0,0 +1,160 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2007 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 <stdio.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <signal.h>
+
+#ifdef HAVE_SYS_PRCTL_H
+#include <sys/prctl.h>
+#endif
+#ifdef HAVE_SYS_RESOURCE_H
+#include <sys/resource.h>
+#endif
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/core-error.h>
+
+#include "start-child.h"
+
+int pa_start_child_for_read(const char *name, const char *argv1, pid_t *pid) {
+ pid_t child;
+ int pipe_fds[2] = { -1, -1 };
+
+ if (pipe(pipe_fds) < 0) {
+ pa_log("pipe() failed: %s", pa_cstrerror(errno));
+ goto fail;
+ }
+
+ if ((child = fork()) == (pid_t) -1) {
+ pa_log("fork() failed: %s", pa_cstrerror(errno));
+ goto fail;
+
+ } else if (child != 0) {
+
+ /* Parent */
+ pa_assert_se(pa_close(pipe_fds[1]) == 0);
+
+ if (pid)
+ *pid = child;
+
+ return pipe_fds[0];
+ } else {
+#ifdef __linux__
+ DIR* d;
+#endif
+ int max_fd, i;
+
+ /* child */
+
+ pa_reset_priority();
+
+ pa_assert_se(pa_close(pipe_fds[0]) == 0);
+ pa_assert_se(dup2(pipe_fds[1], 1) == 1);
+
+ if (pipe_fds[1] != 1)
+ pa_assert_se(pa_close(pipe_fds[1]) == 0);
+
+ pa_close(0);
+ pa_assert_se(open("/dev/null", O_RDONLY) == 0);
+
+ pa_close(2);
+ pa_assert_se(open("/dev/null", O_WRONLY) == 2);
+
+#ifdef __linux__
+
+ if ((d = opendir("/proc/self/fd/"))) {
+
+ struct dirent *de;
+
+ while ((de = readdir(d))) {
+ char *e = NULL;
+ int fd;
+
+ if (de->d_name[0] == '.')
+ continue;
+
+ errno = 0;
+ fd = strtol(de->d_name, &e, 10);
+ pa_assert(errno == 0 && e && *e == 0);
+
+ if (fd >= 3 && dirfd(d) != fd)
+ pa_close(fd);
+ }
+
+ closedir(d);
+ } else {
+
+#endif
+
+ max_fd = 1024;
+
+#ifdef HAVE_SYS_RESOURCE_H
+ {
+ struct rlimit r;
+ if (getrlimit(RLIMIT_NOFILE, &r) == 0)
+ max_fd = r.rlim_max;
+ }
+#endif
+
+ for (i = 3; i < max_fd; i++)
+ pa_close(i);
+
+#ifdef __linux__
+ }
+#endif
+
+#ifdef PR_SET_PDEATHSIG
+ /* On Linux we can use PR_SET_PDEATHSIG to have the helper
+ process killed when the daemon dies abnormally. On non-Linux
+ machines the client will die as soon as it writes data to
+ stdout again (SIGPIPE) */
+
+ prctl(PR_SET_PDEATHSIG, SIGTERM, 0, 0, 0);
+#endif
+
+#ifdef SIGPIPE
+ /* Make sure that SIGPIPE kills the child process */
+ signal(SIGPIPE, SIG_DFL);
+#endif
+
+#ifdef SIGTERM
+ /* Make sure that SIGTERM kills the child process */
+ signal(SIGTERM, SIG_DFL);
+#endif
+
+ execl(name, name, argv1, NULL);
+ _exit(1);
+ }
+
+fail:
+ pa_close_pipe(pipe_fds);
+
+ return -1;
+}
diff --git a/src/pulsecore/start-child.h b/src/pulsecore/start-child.h
new file mode 100644
index 00000000..0b5ff660
--- /dev/null
+++ b/src/pulsecore/start-child.h
@@ -0,0 +1,30 @@
+#ifndef foopulsestartchildhfoo
+#define foopulsestartchildhfoo
+
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2007 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 <unistd.h>
+
+int pa_start_child_for_read(const char *name, const char *argv1, pid_t *pid);
+
+#endif
diff --git a/src/pulsecore/strbuf.c b/src/pulsecore/strbuf.c
index ea8389f5..b59b6f49 100644
--- a/src/pulsecore/strbuf.c
+++ b/src/pulsecore/strbuf.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -55,13 +53,13 @@ pa_strbuf *pa_strbuf_new(void) {
sb = pa_xnew(pa_strbuf, 1);
sb->length = 0;
sb->head = sb->tail = NULL;
-
+
return sb;
}
void pa_strbuf_free(pa_strbuf *sb) {
pa_assert(sb);
-
+
while (sb->head) {
struct chunk *c = sb->head;
sb->head = sb->head->next;
@@ -76,7 +74,7 @@ void pa_strbuf_free(pa_strbuf *sb) {
char *pa_strbuf_tostring(pa_strbuf *sb) {
char *t, *e;
struct chunk *c;
-
+
pa_assert(sb);
e = t = pa_xnew(char, sb->length+1);
@@ -98,20 +96,20 @@ char *pa_strbuf_tostring(pa_strbuf *sb) {
/* Combination of pa_strbuf_free() and pa_strbuf_tostring() */
char *pa_strbuf_tostring_free(pa_strbuf *sb) {
char *t;
-
+
pa_assert(sb);
t = pa_strbuf_tostring(sb);
pa_strbuf_free(sb);
-
+
return t;
}
/* Append a string to the string buffer */
void pa_strbuf_puts(pa_strbuf *sb, const char *t) {
-
+
pa_assert(sb);
pa_assert(t);
-
+
pa_strbuf_putsn(sb, t, strlen(t));
}
@@ -136,7 +134,7 @@ static void append(pa_strbuf *sb, struct chunk *c) {
/* Append up to l bytes of a string to the string buffer */
void pa_strbuf_putsn(pa_strbuf *sb, const char *t, size_t l) {
struct chunk *c;
-
+
pa_assert(sb);
pa_assert(t);
diff --git a/src/pulsecore/strbuf.h b/src/pulsecore/strbuf.h
index 1c0850b1..24c876d5 100644
--- a/src/pulsecore/strbuf.h
+++ b/src/pulsecore/strbuf.h
@@ -1,8 +1,6 @@
#ifndef foostrbufhfoo
#define foostrbufhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -24,7 +22,7 @@
USA.
***/
-#include <pulsecore/gccmacro.h>
+#include <pulse/gccmacro.h>
typedef struct pa_strbuf pa_strbuf;
diff --git a/src/pulsecore/strlist.c b/src/pulsecore/strlist.c
index 1fcb0451..f587a2f8 100644
--- a/src/pulsecore/strlist.c
+++ b/src/pulsecore/strlist.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -44,7 +42,7 @@ struct pa_strlist {
pa_strlist* pa_strlist_prepend(pa_strlist *l, const char *s) {
pa_strlist *n;
size_t size;
-
+
pa_assert(s);
size = strlen(s);
n = pa_xmalloc(PA_ALIGN(sizeof(pa_strlist)) + size + 1);
@@ -108,7 +106,7 @@ void pa_strlist_free(pa_strlist *l) {
pa_strlist* pa_strlist_pop(pa_strlist *l, char **s) {
pa_strlist *r;
-
+
pa_assert(s);
if (!l) {
@@ -146,3 +144,18 @@ pa_strlist* pa_strlist_parse(const char *s) {
return head;
}
+
+pa_strlist *pa_strlist_reverse(pa_strlist *l) {
+ pa_strlist *r = NULL;
+
+ while (l) {
+ pa_strlist *n;
+
+ n = l->next;
+ l->next = r;
+ r = l;
+ l = n;
+ }
+
+ return r;
+}
diff --git a/src/pulsecore/strlist.h b/src/pulsecore/strlist.h
index 96ad47e2..1cb7537a 100644
--- a/src/pulsecore/strlist.h
+++ b/src/pulsecore/strlist.h
@@ -1,8 +1,6 @@
#ifndef foostrlisthfoo
#define foostrlisthfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -46,4 +44,7 @@ pa_strlist* pa_strlist_pop(pa_strlist *l, char **s);
/* Parse a whitespace separated server list */
pa_strlist* pa_strlist_parse(const char *s);
+/* Reverse string list */
+pa_strlist *pa_strlist_reverse(pa_strlist *l);
+
#endif
diff --git a/src/pulsecore/tagstruct.c b/src/pulsecore/tagstruct.c
index 8cf542b6..b0ed59ef 100644
--- a/src/pulsecore/tagstruct.c
+++ b/src/pulsecore/tagstruct.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -42,12 +40,14 @@
#include "tagstruct.h"
+#define MAX_TAG_SIZE (64*1024)
+
struct pa_tagstruct {
uint8_t *data;
size_t length, allocated;
size_t rindex;
- int dynamic;
+ pa_bool_t dynamic;
};
pa_tagstruct *pa_tagstruct_new(const uint8_t* data, size_t length) {
@@ -60,7 +60,7 @@ pa_tagstruct *pa_tagstruct_new(const uint8_t* data, size_t length) {
t->allocated = t->length = data ? length : 0;
t->rindex = 0;
t->dynamic = !data;
-
+
return t;
}
@@ -74,11 +74,11 @@ void pa_tagstruct_free(pa_tagstruct*t) {
uint8_t* pa_tagstruct_free_data(pa_tagstruct*t, size_t *l) {
uint8_t *p;
-
+
pa_assert(t);
pa_assert(t->dynamic);
pa_assert(l);
-
+
p = t->data;
*l = t->length;
pa_xfree(t);
@@ -98,7 +98,7 @@ static void extend(pa_tagstruct*t, size_t l) {
void pa_tagstruct_puts(pa_tagstruct*t, const char *s) {
size_t l;
pa_assert(t);
-
+
if (s) {
l = strlen(s)+2;
extend(t, l);
@@ -114,7 +114,7 @@ void pa_tagstruct_puts(pa_tagstruct*t, const char *s) {
void pa_tagstruct_putu32(pa_tagstruct*t, uint32_t i) {
pa_assert(t);
-
+
extend(t, 5);
t->data[t->length] = PA_TAG_U32;
i = htonl(i);
@@ -124,7 +124,7 @@ void pa_tagstruct_putu32(pa_tagstruct*t, uint32_t i) {
void pa_tagstruct_putu8(pa_tagstruct*t, uint8_t c) {
pa_assert(t);
-
+
extend(t, 2);
t->data[t->length] = PA_TAG_U8;
*(t->data+t->length+1) = c;
@@ -133,10 +133,10 @@ void pa_tagstruct_putu8(pa_tagstruct*t, uint8_t c) {
void pa_tagstruct_put_sample_spec(pa_tagstruct *t, const pa_sample_spec *ss) {
uint32_t rate;
-
+
pa_assert(t);
pa_assert(ss);
-
+
extend(t, 7);
t->data[t->length] = PA_TAG_SAMPLE_SPEC;
t->data[t->length+1] = (uint8_t) ss->format;
@@ -148,7 +148,7 @@ void pa_tagstruct_put_sample_spec(pa_tagstruct *t, const pa_sample_spec *ss) {
void pa_tagstruct_put_arbitrary(pa_tagstruct *t, const void *p, size_t length) {
uint32_t tmp;
-
+
pa_assert(t);
pa_assert(p);
@@ -161,7 +161,7 @@ void pa_tagstruct_put_arbitrary(pa_tagstruct *t, const void *p, size_t length) {
t->length += 5+length;
}
-void pa_tagstruct_put_boolean(pa_tagstruct*t, int b) {
+void pa_tagstruct_put_boolean(pa_tagstruct*t, pa_bool_t b) {
pa_assert(t);
extend(t, 1);
@@ -184,7 +184,7 @@ void pa_tagstruct_put_timeval(pa_tagstruct*t, const struct timeval *tv) {
void pa_tagstruct_put_usec(pa_tagstruct*t, pa_usec_t u) {
uint32_t tmp;
-
+
pa_assert(t);
extend(t, 9);
@@ -198,7 +198,7 @@ void pa_tagstruct_put_usec(pa_tagstruct*t, pa_usec_t u) {
void pa_tagstruct_putu64(pa_tagstruct*t, uint64_t u) {
uint32_t tmp;
-
+
pa_assert(t);
extend(t, 9);
@@ -212,7 +212,7 @@ void pa_tagstruct_putu64(pa_tagstruct*t, uint64_t u) {
void pa_tagstruct_puts64(pa_tagstruct*t, int64_t u) {
uint32_t tmp;
-
+
pa_assert(t);
extend(t, 9);
@@ -254,11 +254,37 @@ void pa_tagstruct_put_cvolume(pa_tagstruct *t, const pa_cvolume *cvolume) {
}
}
+void pa_tagstruct_put_proplist(pa_tagstruct *t, pa_proplist *p) {
+ void *state = NULL;
+ pa_assert(t);
+ pa_assert(p);
+
+ extend(t, 1);
+
+ t->data[t->length++] = PA_TAG_PROPLIST;
+
+ for (;;) {
+ const char *k;
+ const void *d;
+ size_t l;
+
+ if (!(k = pa_proplist_iterate(p, &state)))
+ break;
+
+ pa_tagstruct_puts(t, k);
+ pa_assert_se(pa_proplist_get(p, k, &d, &l) >= 0);
+ pa_tagstruct_putu32(t, (uint32_t) l);
+ pa_tagstruct_put_arbitrary(t, d, l);
+ }
+
+ pa_tagstruct_puts(t, NULL);
+}
+
int pa_tagstruct_gets(pa_tagstruct*t, const char **s) {
int error = 0;
size_t n;
char *c;
-
+
pa_assert(t);
pa_assert(s);
@@ -345,7 +371,7 @@ int pa_tagstruct_get_sample_spec(pa_tagstruct *t, pa_sample_spec *ss) {
int pa_tagstruct_get_arbitrary(pa_tagstruct *t, const void **p, size_t length) {
uint32_t len;
-
+
pa_assert(t);
pa_assert(p);
@@ -366,7 +392,7 @@ int pa_tagstruct_get_arbitrary(pa_tagstruct *t, const void **p, size_t length) {
int pa_tagstruct_eof(pa_tagstruct*t) {
pa_assert(t);
-
+
return t->rindex >= t->length;
}
@@ -374,22 +400,22 @@ const uint8_t* pa_tagstruct_data(pa_tagstruct*t, size_t *l) {
pa_assert(t);
pa_assert(t->dynamic);
pa_assert(l);
-
+
*l = t->length;
return t->data;
}
-int pa_tagstruct_get_boolean(pa_tagstruct*t, int *b) {
+int pa_tagstruct_get_boolean(pa_tagstruct*t, pa_bool_t *b) {
pa_assert(t);
pa_assert(b);
-
+
if (t->rindex+1 > t->length)
return -1;
if (t->data[t->rindex] == PA_TAG_BOOLEAN_TRUE)
- *b = 1;
+ *b = TRUE;
else if (t->data[t->rindex] == PA_TAG_BOOLEAN_FALSE)
- *b = 0;
+ *b = FALSE;
else
return -1;
@@ -401,7 +427,7 @@ int pa_tagstruct_get_timeval(pa_tagstruct*t, struct timeval *tv) {
pa_assert(t);
pa_assert(tv);
-
+
if (t->rindex+9 > t->length)
return -1;
@@ -458,7 +484,7 @@ int pa_tagstruct_getu64(pa_tagstruct*t, uint64_t *u) {
int pa_tagstruct_gets64(pa_tagstruct*t, int64_t *u) {
uint32_t tmp;
-
+
pa_assert(t);
pa_assert(u);
@@ -529,6 +555,52 @@ int pa_tagstruct_get_cvolume(pa_tagstruct *t, pa_cvolume *cvolume) {
return 0;
}
+int pa_tagstruct_get_proplist(pa_tagstruct *t, pa_proplist *p) {
+ size_t saved_rindex;
+
+ pa_assert(t);
+ pa_assert(p);
+
+ if (t->rindex+1 > t->length)
+ return -1;
+
+ if (t->data[t->rindex] != PA_TAG_PROPLIST)
+ return -1;
+
+ saved_rindex = t->rindex;
+ t->rindex++;
+
+ for (;;) {
+ const char *k;
+ const void *d;
+ uint32_t length;
+
+ if (pa_tagstruct_gets(t, &k) < 0)
+ goto fail;
+
+ if (!k)
+ break;
+
+ if (pa_tagstruct_getu32(t, &length) < 0)
+ goto fail;
+
+ if (length > MAX_TAG_SIZE)
+ goto fail;
+
+ if (pa_tagstruct_get_arbitrary(t, &d, length) < 0)
+ goto fail;
+
+ if (pa_proplist_set(p, k, d, length) < 0)
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ t->rindex = saved_rindex;
+ return -1;
+}
+
void pa_tagstruct_put(pa_tagstruct *t, ...) {
va_list va;
pa_assert(t);
@@ -591,6 +663,10 @@ void pa_tagstruct_put(pa_tagstruct *t, ...) {
pa_tagstruct_put_cvolume(t, va_arg(va, pa_cvolume *));
break;
+ case PA_TAG_PROPLIST:
+ pa_tagstruct_put_proplist(t, va_arg(va, pa_proplist *));
+ break;
+
default:
pa_assert_not_reached();
}
@@ -643,7 +719,7 @@ int pa_tagstruct_get(pa_tagstruct *t, ...) {
case PA_TAG_BOOLEAN_TRUE:
case PA_TAG_BOOLEAN_FALSE:
- ret = pa_tagstruct_get_boolean(t, va_arg(va, int*));
+ ret = pa_tagstruct_get_boolean(t, va_arg(va, pa_bool_t*));
break;
case PA_TAG_TIMEVAL:
@@ -662,6 +738,10 @@ int pa_tagstruct_get(pa_tagstruct *t, ...) {
ret = pa_tagstruct_get_cvolume(t, va_arg(va, pa_cvolume *));
break;
+ case PA_TAG_PROPLIST:
+ ret = pa_tagstruct_get_proplist(t, va_arg(va, pa_proplist *));
+ break;
+
default:
pa_assert_not_reached();
}
diff --git a/src/pulsecore/tagstruct.h b/src/pulsecore/tagstruct.h
index e9bb9ac8..e7d07054 100644
--- a/src/pulsecore/tagstruct.h
+++ b/src/pulsecore/tagstruct.h
@@ -1,8 +1,6 @@
#ifndef footagstructhfoo
#define footagstructhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -32,6 +30,10 @@
#include <pulse/sample.h>
#include <pulse/channelmap.h>
#include <pulse/volume.h>
+#include <pulse/proplist.h>
+#include <pulse/gccmacro.h>
+
+#include <pulsecore/macro.h>
typedef struct pa_tagstruct pa_tagstruct;
@@ -51,7 +53,8 @@ enum {
PA_TAG_TIMEVAL = 'T',
PA_TAG_USEC = 'U' /* 64bit unsigned */,
PA_TAG_CHANNEL_MAP = 'm',
- PA_TAG_CVOLUME = 'v'
+ PA_TAG_CVOLUME = 'v',
+ PA_TAG_PROPLIST = 'P'
};
pa_tagstruct *pa_tagstruct_new(const uint8_t* data, size_t length);
@@ -70,11 +73,12 @@ void pa_tagstruct_putu64(pa_tagstruct*t, uint64_t i);
void pa_tagstruct_puts64(pa_tagstruct*t, int64_t i);
void pa_tagstruct_put_sample_spec(pa_tagstruct *t, const pa_sample_spec *ss);
void pa_tagstruct_put_arbitrary(pa_tagstruct*t, const void *p, size_t length);
-void pa_tagstruct_put_boolean(pa_tagstruct*t, int b);
+void pa_tagstruct_put_boolean(pa_tagstruct*t, pa_bool_t b);
void pa_tagstruct_put_timeval(pa_tagstruct*t, const struct timeval *tv);
void pa_tagstruct_put_usec(pa_tagstruct*t, pa_usec_t u);
void pa_tagstruct_put_channel_map(pa_tagstruct *t, const pa_channel_map *map);
void pa_tagstruct_put_cvolume(pa_tagstruct *t, const pa_cvolume *cvolume);
+void pa_tagstruct_put_proplist(pa_tagstruct *t, pa_proplist *p);
int pa_tagstruct_get(pa_tagstruct *t, ...);
@@ -85,11 +89,12 @@ int pa_tagstruct_getu64(pa_tagstruct*t, uint64_t *i);
int pa_tagstruct_gets64(pa_tagstruct*t, int64_t *i);
int pa_tagstruct_get_sample_spec(pa_tagstruct *t, pa_sample_spec *ss);
int pa_tagstruct_get_arbitrary(pa_tagstruct *t, const void **p, size_t length);
-int pa_tagstruct_get_boolean(pa_tagstruct *t, int *b);
+int pa_tagstruct_get_boolean(pa_tagstruct *t, pa_bool_t *b);
int pa_tagstruct_get_timeval(pa_tagstruct*t, struct timeval *tv);
int pa_tagstruct_get_usec(pa_tagstruct*t, pa_usec_t *u);
int pa_tagstruct_get_channel_map(pa_tagstruct *t, pa_channel_map *map);
int pa_tagstruct_get_cvolume(pa_tagstruct *t, pa_cvolume *v);
+int pa_tagstruct_get_proplist(pa_tagstruct *t, pa_proplist *p);
#endif
diff --git a/src/pulsecore/thread-mq.c b/src/pulsecore/thread-mq.c
index d572f6e0..34f92a7e 100644
--- a/src/pulsecore/thread-mq.c
+++ b/src/pulsecore/thread-mq.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -43,15 +41,15 @@
PA_STATIC_TLS_DECLARE_NO_FREE(thread_mq);
-static void asyncmsgq_cb(pa_mainloop_api*api, pa_io_event* e, int fd, pa_io_event_flags_t events, void *userdata) {
+static void asyncmsgq_read_cb(pa_mainloop_api*api, pa_io_event* e, int fd, pa_io_event_flags_t events, void *userdata) {
pa_thread_mq *q = userdata;
pa_asyncmsgq *aq;
- pa_assert(pa_asyncmsgq_get_fd(q->outq) == fd);
+ pa_assert(pa_asyncmsgq_read_fd(q->outq) == fd);
pa_assert(events == PA_IO_EVENT_INPUT);
pa_asyncmsgq_ref(aq = q->outq);
- pa_asyncmsgq_after_poll(aq);
+ pa_asyncmsgq_write_after_poll(aq);
for (;;) {
pa_msgobject *object;
@@ -68,35 +66,52 @@ static void asyncmsgq_cb(pa_mainloop_api*api, pa_io_event* e, int fd, pa_io_even
pa_asyncmsgq_done(aq, ret);
}
- if (pa_asyncmsgq_before_poll(aq) == 0)
+ if (pa_asyncmsgq_read_before_poll(aq) == 0)
break;
}
pa_asyncmsgq_unref(aq);
}
-void pa_thread_mq_init(pa_thread_mq *q, pa_mainloop_api *mainloop) {
+static void asyncmsgq_write_cb(pa_mainloop_api*api, pa_io_event* e, int fd, pa_io_event_flags_t events, void *userdata) {
+ pa_thread_mq *q = userdata;
+
+ pa_assert(pa_asyncmsgq_write_fd(q->inq) == fd);
+ pa_assert(events == PA_IO_EVENT_INPUT);
+
+ pa_asyncmsgq_write_after_poll(q->inq);
+ pa_asyncmsgq_write_before_poll(q->inq);
+}
+
+void pa_thread_mq_init(pa_thread_mq *q, pa_mainloop_api *mainloop, pa_rtpoll *rtpoll) {
pa_assert(q);
pa_assert(mainloop);
q->mainloop = mainloop;
pa_assert_se(q->inq = pa_asyncmsgq_new(0));
pa_assert_se(q->outq = pa_asyncmsgq_new(0));
-
- pa_assert_se(pa_asyncmsgq_before_poll(q->outq) == 0);
- pa_assert_se(q->io_event = mainloop->io_new(mainloop, pa_asyncmsgq_get_fd(q->outq), PA_IO_EVENT_INPUT, asyncmsgq_cb, q));
+
+ pa_assert_se(pa_asyncmsgq_read_before_poll(q->outq) == 0);
+ pa_assert_se(q->read_event = mainloop->io_new(mainloop, pa_asyncmsgq_read_fd(q->outq), PA_IO_EVENT_INPUT, asyncmsgq_read_cb, q));
+
+ pa_asyncmsgq_write_before_poll(q->inq);
+ pa_assert_se(q->write_event = mainloop->io_new(mainloop, pa_asyncmsgq_write_fd(q->inq), PA_IO_EVENT_INPUT, asyncmsgq_write_cb, q));
+
+ pa_rtpoll_item_new_asyncmsgq_read(rtpoll, PA_RTPOLL_EARLY, q->inq);
+ pa_rtpoll_item_new_asyncmsgq_write(rtpoll, PA_RTPOLL_LATE, q->outq);
}
void pa_thread_mq_done(pa_thread_mq *q) {
pa_assert(q);
- q->mainloop->io_free(q->io_event);
- q->io_event = NULL;
+ q->mainloop->io_free(q->read_event);
+ q->mainloop->io_free(q->write_event);
+ q->read_event = q->write_event = NULL;
pa_asyncmsgq_unref(q->inq);
pa_asyncmsgq_unref(q->outq);
q->inq = q->outq = NULL;
-
+
q->mainloop = NULL;
}
diff --git a/src/pulsecore/thread-mq.h b/src/pulsecore/thread-mq.h
index 13b6e01f..3b5e0e78 100644
--- a/src/pulsecore/thread-mq.h
+++ b/src/pulsecore/thread-mq.h
@@ -1,8 +1,6 @@
#ifndef foopulsethreadmqhfoo
#define foopulsethreadmqhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -26,6 +24,7 @@
#include <pulse/mainloop-api.h>
#include <pulsecore/asyncmsgq.h>
+#include <pulsecore/rtpoll.h>
/* Two way communication between a thread and a mainloop. Before the
* thread is started a pa_pthread_mq should be initialized and than
@@ -34,10 +33,10 @@
typedef struct pa_thread_mq {
pa_mainloop_api *mainloop;
pa_asyncmsgq *inq, *outq;
- pa_io_event *io_event;
+ pa_io_event *read_event, *write_event;
} pa_thread_mq;
-void pa_thread_mq_init(pa_thread_mq *q, pa_mainloop_api *mainloop);
+void pa_thread_mq_init(pa_thread_mq *q, pa_mainloop_api *mainloop, pa_rtpoll *rtpoll);
void pa_thread_mq_done(pa_thread_mq *q);
/* Install the specified pa_thread_mq object for the current thread */
diff --git a/src/pulsecore/thread-posix.c b/src/pulsecore/thread-posix.c
index 3c69adfb..20ed16d9 100644
--- a/src/pulsecore/thread-posix.c
+++ b/src/pulsecore/thread-posix.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -126,7 +124,7 @@ pa_thread* pa_thread_self(void) {
if ((t = PA_STATIC_TLS_GET(current_thread)))
return t;
-
+
/* This is a foreign thread, let's create a pthread structure to
* make sure that we can always return a sensible pointer */
diff --git a/src/pulsecore/thread-win32.c b/src/pulsecore/thread-win32.c
index cad1420a..c40d3342 100644
--- a/src/pulsecore/thread-win32.c
+++ b/src/pulsecore/thread-win32.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/thread.h b/src/pulsecore/thread.h
index 54ef320e..f3aca13e 100644
--- a/src/pulsecore/thread.h
+++ b/src/pulsecore/thread.h
@@ -1,8 +1,6 @@
#ifndef foopulsethreadhfoo
#define foopulsethreadhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/time-smoother.c b/src/pulsecore/time-smoother.c
index 6bda3df0..fe5a4f18 100644
--- a/src/pulsecore/time-smoother.c
+++ b/src/pulsecore/time-smoother.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -34,7 +32,7 @@
#include "time-smoother.h"
-#define HISTORY_MAX 50
+#define HISTORY_MAX 64
/*
* Implementation of a time smoothing algorithm to synchronize remote
@@ -61,7 +59,6 @@
struct pa_smoother {
pa_usec_t adjust_time, history_time;
- pa_bool_t monotonic;
pa_usec_t time_offset;
@@ -70,27 +67,34 @@ struct pa_smoother {
pa_usec_t ex, ey; /* Point e, which we estimated before and need to smooth to */
double de; /* Gradient we estimated for point e */
+ pa_usec_t ry; /* The original y value for ex */
/* History of last measurements */
pa_usec_t history_x[HISTORY_MAX], history_y[HISTORY_MAX];
unsigned history_idx, n_history;
/* To even out for monotonicity */
- pa_usec_t last_y;
+ pa_usec_t last_y, last_x;
/* Cached parameters for our interpolation polynomial y=ax^3+b^2+cx */
double a, b, c;
pa_bool_t abc_valid;
- pa_bool_t paused;
+ pa_bool_t monotonic:1;
+ pa_bool_t paused:1;
+
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) {
+pa_smoother* pa_smoother_new(pa_usec_t adjust_time, pa_usec_t history_time, pa_bool_t monotonic, unsigned min_history) {
pa_smoother *s;
pa_assert(adjust_time > 0);
pa_assert(history_time > 0);
+ pa_assert(min_history >= 2);
+ pa_assert(min_history <= HISTORY_MAX);
s = pa_xnew(pa_smoother, 1);
s->adjust_time = adjust_time;
@@ -101,18 +105,20 @@ pa_smoother* pa_smoother_new(pa_usec_t adjust_time, pa_usec_t history_time, pa_b
s->px = s->py = 0;
s->dp = 1;
- s->ex = s->ey = 0;
+ s->ex = s->ey = s->ry = 0;
s->de = 1;
s->history_idx = 0;
s->n_history = 0;
- s->last_y = 0;
+ s->last_y = s->last_x = 0;
s->abc_valid = FALSE;
s->paused = FALSE;
+ s->min_history = min_history;
+
return s;
}
@@ -122,39 +128,58 @@ void pa_smoother_free(pa_smoother* s) {
pa_xfree(s);
}
+#define REDUCE(x) \
+ do { \
+ x = (x) % HISTORY_MAX; \
+ } while(FALSE)
+
+#define REDUCE_INC(x) \
+ do { \
+ x = ((x)+1) % HISTORY_MAX; \
+ } while(FALSE)
+
+
static void drop_old(pa_smoother *s, pa_usec_t x) {
- unsigned j;
- /* First drop items from history which are too old, but make sure
- * to always keep two entries in the history */
+ /* Drop items from history which are too old, but make sure to
+ * always keep min_history in the history */
- for (j = s->n_history; j > 2; j--) {
+ while (s->n_history > s->min_history) {
- if (s->history_x[s->history_idx] + s->history_time >= x) {
+ if (s->history_x[s->history_idx] + s->history_time >= x)
/* This item is still valid, and thus all following ones
* are too, so let's quit this loop */
break;
- }
/* Item is too old, let's drop it */
- s->history_idx ++;
- while (s->history_idx >= HISTORY_MAX)
- s->history_idx -= HISTORY_MAX;
+ REDUCE_INC(s->history_idx);
s->n_history --;
}
}
static void add_to_history(pa_smoother *s, pa_usec_t x, pa_usec_t y) {
- unsigned j;
+ unsigned j, i;
pa_assert(s);
+ /* First try to update an existing history entry */
+ i = s->history_idx;
+ for (j = s->n_history; j > 0; j--) {
+
+ if (s->history_x[i] == x) {
+ s->history_y[i] = y;
+ return;
+ }
+
+ REDUCE_INC(i);
+ }
+
+ /* Drop old entries */
drop_old(s, x);
/* Calculate position for new entry */
j = s->history_idx + s->n_history;
- while (j >= HISTORY_MAX)
- j -= HISTORY_MAX;
+ REDUCE(j);
/* Fill in entry */
s->history_x[j] = x;
@@ -164,8 +189,9 @@ static void add_to_history(pa_smoother *s, pa_usec_t x, pa_usec_t y) {
s->n_history ++;
/* And make sure we don't store more entries than fit in */
- if (s->n_history >= HISTORY_MAX) {
+ if (s->n_history > HISTORY_MAX) {
s->history_idx += s->n_history - HISTORY_MAX;
+ REDUCE(s->history_idx);
s->n_history = HISTORY_MAX;
}
}
@@ -175,7 +201,9 @@ static double avg_gradient(pa_smoother *s, pa_usec_t x) {
int64_t ax = 0, ay = 0, k, t;
double r;
- drop_old(s, x);
+ /* Too few measurements, assume gradient of 1 */
+ if (s->n_history < s->min_history)
+ return 1;
/* First, calculate average of all measurements */
i = s->history_idx;
@@ -185,15 +213,10 @@ static double avg_gradient(pa_smoother *s, pa_usec_t x) {
ay += s->history_y[i];
c++;
- i++;
- while (i >= HISTORY_MAX)
- i -= HISTORY_MAX;
+ REDUCE_INC(i);
}
- /* Too few measurements, assume gradient of 1 */
- if (c < 2)
- return 1;
-
+ pa_assert(c >= s->min_history);
ax /= c;
ay /= c;
@@ -210,14 +233,45 @@ static double avg_gradient(pa_smoother *s, pa_usec_t x) {
k += dx*dy;
t += dx*dx;
- i++;
- while (i >= HISTORY_MAX)
- i -= HISTORY_MAX;
+ REDUCE_INC(i);
}
r = (double) k / t;
- return s->monotonic && r < 0 ? 0 : r;
+ return (s->monotonic && r < 0) ? 0 : r;
+}
+
+static void calc_abc(pa_smoother *s) {
+ pa_usec_t ex, ey, px, py;
+ int64_t kx, ky;
+ double de, dp;
+
+ pa_assert(s);
+
+ if (s->abc_valid)
+ return;
+
+ /* We have two points: (ex|ey) and (px|py) with two gradients at
+ * these points de and dp. We do a polynomial
+ * interpolation of degree 3 with these 6 values */
+
+ ex = s->ex; ey = s->ey;
+ px = s->px; py = s->py;
+ de = s->de; dp = s->dp;
+
+ pa_assert(ex < px);
+
+ /* To increase the dynamic range and symplify calculation, we
+ * move these values to the origin */
+ kx = (int64_t) px - (int64_t) ex;
+ ky = (int64_t) py - (int64_t) ey;
+
+ /* Calculate a, b, c for y=ax^3+bx^2+cx */
+ s->c = de;
+ s->b = (((double) (3*ky)/kx - dp - 2*de)) / kx;
+ s->a = (dp/kx - 2*s->b - de/kx) / (3*kx);
+
+ s->abc_valid = TRUE;
}
static void estimate(pa_smoother *s, pa_usec_t x, pa_usec_t *y, double *deriv) {
@@ -242,36 +296,10 @@ static void estimate(pa_smoother *s, pa_usec_t x, pa_usec_t *y, double *deriv) {
} else {
- if (!s->abc_valid) {
- pa_usec_t ex, ey, px, py;
- int64_t kx, ky;
- double de, dp;
-
- /* Ok, we're not yet on track, thus let's interpolate, and
- * make sure that the first derivative is smooth */
-
- /* We have two points: (ex|ey) and (px|py) with two gradients
- * at these points de and dp. We do a polynomial interpolation
- * of degree 3 with these 6 values */
-
- ex = s->ex; ey = s->ey;
- px = s->px; py = s->py;
- de = s->de; dp = s->dp;
+ /* Ok, we're not yet on track, thus let's interpolate, and
+ * make sure that the first derivative is smooth */
- pa_assert(ex < px);
-
- /* To increase the dynamic range and symplify calculation, we
- * move these values to the origin */
- kx = (int64_t) px - (int64_t) ex;
- ky = (int64_t) py - (int64_t) ey;
-
- /* Calculate a, b, c for y=ax^3+b^2+cx */
- s->c = de;
- s->b = (((double) (3*ky)/kx - dp - 2*de)) / kx;
- s->a = (dp/kx - 2*s->b - de/kx) / (3*kx);
-
- s->abc_valid = TRUE;
- }
+ calc_abc(s);
/* Move to origin */
x -= s->ex;
@@ -290,11 +318,6 @@ static void estimate(pa_smoother *s, pa_usec_t x, pa_usec_t *y, double *deriv) {
/* Guarantee monotonicity */
if (s->monotonic) {
- if (*y < s->last_y)
- *y = s->last_y;
- else
- s->last_y = *y;
-
if (deriv && *deriv < 0)
*deriv = 0;
}
@@ -303,22 +326,26 @@ static void estimate(pa_smoother *s, pa_usec_t x, pa_usec_t *y, double *deriv) {
void pa_smoother_put(pa_smoother *s, pa_usec_t x, pa_usec_t y) {
pa_usec_t ney;
double nde;
+ pa_bool_t is_new;
pa_assert(s);
- pa_assert(x >= s->time_offset);
/* Fix up x value */
if (s->paused)
x = s->pause_time;
- else
- x -= s->time_offset;
- pa_assert(x >= s->ex);
+ x = PA_LIKELY(x >= s->time_offset) ? x - s->time_offset : 0;
- /* First, we calculate the position we'd estimate for x, so that
- * we can adjust our position smoothly from this one */
- estimate(s, x, &ney, &nde);
- s->ex = x; s->ey = ney; s->de = nde;
+ is_new = x >= s->ex;
+
+ if (is_new) {
+ /* First, we calculate the position we'd estimate for x, so that
+ * 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;
+ }
/* Then, we add the new measurement to our history */
add_to_history(s, x, y);
@@ -327,27 +354,41 @@ 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 = x + s->adjust_time;
- s->py = y + s->dp *s->adjust_time;
+ s->px = s->ex + s->adjust_time;
+ s->py = s->ry + s->dp *s->adjust_time;
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); */
}
pa_usec_t pa_smoother_get(pa_smoother *s, pa_usec_t x) {
pa_usec_t y;
pa_assert(s);
- pa_assert(x >= s->time_offset);
/* Fix up x value */
if (s->paused)
x = s->pause_time;
- else
- x -= s->time_offset;
- pa_assert(x >= s->ex);
+ x = PA_LIKELY(x >= s->time_offset) ? x - s->time_offset : 0;
estimate(s, x, &y, NULL);
+
+ if (s->monotonic) {
+
+ /* Make sure the querier doesn't jump forth and back. */
+ pa_assert(x >= s->last_x);
+ s->last_x = x;
+
+ if (y < s->last_y)
+ y = s->last_y;
+ else
+ 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); */
+
return y;
}
@@ -355,6 +396,8 @@ void pa_smoother_set_time_offset(pa_smoother *s, pa_usec_t offset) {
pa_assert(s);
s->time_offset = offset;
+
+/* pa_log_debug("offset(%llu)", (unsigned long long) offset); */
}
void pa_smoother_pause(pa_smoother *s, pa_usec_t x) {
@@ -363,6 +406,8 @@ void pa_smoother_pause(pa_smoother *s, pa_usec_t x) {
if (s->paused)
return;
+/* pa_log_debug("pause(%llu)", (unsigned long long) x); */
+
s->paused = TRUE;
s->pause_time = x;
}
@@ -373,6 +418,42 @@ void pa_smoother_resume(pa_smoother *s, pa_usec_t x) {
if (!s->paused)
return;
+ pa_assert(x >= s->pause_time);
+
+/* pa_log_debug("resume(%llu)", (unsigned long long) x); */
+
s->paused = FALSE;
s->time_offset += x - s->pause_time;
}
+
+pa_usec_t pa_smoother_translate(pa_smoother *s, pa_usec_t x, pa_usec_t y_delay) {
+ pa_usec_t ney;
+ double nde;
+
+ pa_assert(s);
+
+ /* Fix up x value */
+ if (s->paused)
+ x = s->pause_time;
+
+ x = PA_LIKELY(x >= s->time_offset) ? x - s->time_offset : 0;
+
+ estimate(s, x, &ney, &nde);
+
+ /* Play safe and take the larger gradient, so that we wakeup
+ * earlier when this is used for sleeping */
+ 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); */
+
+ return (pa_usec_t) ((double) y_delay / nde);
+}
+
+void pa_smoother_reset(pa_smoother *s) {
+ pa_assert(s);
+
+ s->n_history = 0;
+ s->abc_valid = FALSE;
+
+}
diff --git a/src/pulsecore/time-smoother.h b/src/pulsecore/time-smoother.h
index 8b8512e2..2051e640 100644
--- a/src/pulsecore/time-smoother.h
+++ b/src/pulsecore/time-smoother.h
@@ -1,8 +1,6 @@
#ifndef foopulsetimesmootherhfoo
#define foopulsetimesmootherhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -29,15 +27,23 @@
typedef struct pa_smoother pa_smoother;
-pa_smoother* pa_smoother_new(pa_usec_t adjust_time, pa_usec_t history_time, pa_bool_t monotonic);
+pa_smoother* pa_smoother_new(pa_usec_t x_adjust_time, pa_usec_t x_history_time, pa_bool_t monotonic, unsigned min_history);
void pa_smoother_free(pa_smoother* s);
+/* Adds a new value to our dataset. x = local/system time, y = remote time */
void pa_smoother_put(pa_smoother *s, pa_usec_t x, pa_usec_t y);
+
+/* Returns an interpolated value based on the dataset. x = local/system time, return value = remote time */
pa_usec_t pa_smoother_get(pa_smoother *s, pa_usec_t x);
-void pa_smoother_set_time_offset(pa_smoother *s, pa_usec_t offset);
+/* Translates a time span from the remote time domain to the local one. x = local/system time when to estimate, y_delay = remote time span */
+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_reset(pa_smoother *s);
+
#endif
diff --git a/src/pulsecore/tokenizer.c b/src/pulsecore/tokenizer.c
index ed54a86e..d1e0836b 100644
--- a/src/pulsecore/tokenizer.c
+++ b/src/pulsecore/tokenizer.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -29,9 +27,9 @@
#include <stdlib.h>
#include <pulse/xmalloc.h>
+#include <pulse/gccmacro.h>
#include <pulsecore/dynarray.h>
-#include <pulsecore/gccmacro.h>
#include <pulsecore/macro.h>
#include "tokenizer.h"
@@ -44,7 +42,7 @@ static void parse(pa_dynarray*a, const char *s, unsigned args) {
int infty = 0;
const char delimiter[] = " \t\n\r";
const char *p;
-
+
pa_assert(a);
pa_assert(s);
@@ -77,7 +75,7 @@ pa_tokenizer* pa_tokenizer_new(const char *s, unsigned args) {
void pa_tokenizer_free(pa_tokenizer *t) {
pa_dynarray *a = (pa_dynarray*) t;
-
+
pa_assert(a);
pa_dynarray_free(a, token_free, NULL);
}
diff --git a/src/pulsecore/tokenizer.h b/src/pulsecore/tokenizer.h
index 68a8db49..d51cd73e 100644
--- a/src/pulsecore/tokenizer.h
+++ b/src/pulsecore/tokenizer.h
@@ -1,8 +1,6 @@
#ifndef footokenizerhfoo
#define footokenizerhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/x11prop.c b/src/pulsecore/x11prop.c
index a740e39b..9e75f63a 100644
--- a/src/pulsecore/x11prop.c
+++ b/src/pulsecore/x11prop.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/x11prop.h b/src/pulsecore/x11prop.h
index 388c5a34..c5998d3e 100644
--- a/src/pulsecore/x11prop.h
+++ b/src/pulsecore/x11prop.h
@@ -1,8 +1,6 @@
#ifndef foox11prophfoo
#define foox11prophfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/x11wrap.c b/src/pulsecore/x11wrap.c
index 1cb1ce84..00b6a157 100644
--- a/src/pulsecore/x11wrap.c
+++ b/src/pulsecore/x11wrap.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -63,7 +61,8 @@ struct pa_x11_wrapper {
struct pa_x11_client {
PA_LLIST_FIELDS(pa_x11_client);
pa_x11_wrapper *wrapper;
- int (*callback)(pa_x11_wrapper *w, XEvent *e, void *userdata);
+ pa_x11_event_cb_t event_cb;
+ pa_x11_kill_cb_t kill_cb;
void *userdata;
};
@@ -72,17 +71,23 @@ static void work(pa_x11_wrapper *w) {
pa_assert(w);
pa_assert(PA_REFCNT_VALUE(w) >= 1);
+ pa_x11_wrapper_ref(w);
+
while (XPending(w->display)) {
- pa_x11_client *c;
+ pa_x11_client *c, *n;
XEvent e;
XNextEvent(w->display, &e);
- for (c = w->clients; c; c = c->next) {
- pa_assert(c->callback);
- if (c->callback(w, &e, c->userdata) != 0)
- break;
+ for (c = w->clients; c; c = n) {
+ n = c->next;
+
+ if (c->event_cb)
+ if (c->event_cb(w, &e, c->userdata) != 0)
+ break;
}
}
+
+ pa_x11_wrapper_unref(w);
}
/* IO notification event for the X11 display connection */
@@ -115,7 +120,7 @@ static void defer_event(pa_mainloop_api *m, pa_defer_event *e, void *userdata) {
/* IO notification event for X11 internal connections */
static void internal_io_event(pa_mainloop_api *m, pa_io_event *e, int fd, PA_GCC_UNUSED pa_io_event_flags_t f, void *userdata) {
pa_x11_wrapper *w = userdata;
-
+
pa_assert(m);
pa_assert(e);
pa_assert(fd >= 0);
@@ -153,7 +158,7 @@ static void x11_internal_remove(pa_x11_wrapper *w, pa_x11_internal *i) {
/* Implementation of XConnectionWatchProc */
static void x11_watch(Display *display, XPointer userdata, int fd, Bool opening, XPointer *watch_data) {
pa_x11_wrapper *w = (pa_x11_wrapper*) userdata;
-
+
pa_assert(display);
pa_assert(w);
pa_assert(fd >= 0);
@@ -215,7 +220,7 @@ static void x11_wrapper_free(pa_x11_wrapper*w) {
pa_x11_wrapper* pa_x11_wrapper_get(pa_core *c, const char *name) {
char t[256];
pa_x11_wrapper *w;
-
+
pa_core_assert_ref(c);
pa_snprintf(t, sizeof(t), "x11-wrapper%s%s", name ? "-" : "", name ? name : "");
@@ -251,15 +256,33 @@ Display *pa_x11_wrapper_get_display(pa_x11_wrapper *w) {
return w->display;
}
-pa_x11_client* pa_x11_client_new(pa_x11_wrapper *w, int (*cb)(pa_x11_wrapper *w, XEvent *e, void *userdata), void *userdata) {
+void pa_x11_wrapper_kill(pa_x11_wrapper *w) {
+ pa_x11_client *c, *n;
+
+ pa_assert(w);
+
+ pa_x11_wrapper_ref(w);
+
+ for (c = w->clients; c; c = n) {
+ n = c->next;
+
+ if (c->kill_cb)
+ c->kill_cb(w, c->userdata);
+ }
+
+ pa_x11_wrapper_unref(w);
+}
+
+pa_x11_client* pa_x11_client_new(pa_x11_wrapper *w, pa_x11_event_cb_t event_cb, pa_x11_kill_cb_t kill_cb, void *userdata) {
pa_x11_client *c;
-
+
pa_assert(w);
pa_assert(PA_REFCNT_VALUE(w) >= 1);
c = pa_xnew(pa_x11_client, 1);
c->wrapper = w;
- c->callback = cb;
+ c->event_cb = event_cb;
+ c->kill_cb = kill_cb;
c->userdata = userdata;
PA_LLIST_PREPEND(pa_x11_client, w->clients, c);
@@ -271,7 +294,7 @@ void pa_x11_client_free(pa_x11_client *c) {
pa_assert(c);
pa_assert(c->wrapper);
pa_assert(PA_REFCNT_VALUE(c->wrapper) >= 1);
-
+
PA_LLIST_REMOVE(pa_x11_client, c->wrapper->clients, c);
pa_xfree(c);
}
diff --git a/src/pulsecore/x11wrap.h b/src/pulsecore/x11wrap.h
index 9bed2fce..badc3a1f 100644
--- a/src/pulsecore/x11wrap.h
+++ b/src/pulsecore/x11wrap.h
@@ -1,8 +1,6 @@
#ifndef foox11wraphfoo
#define foox11wraphfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -30,6 +28,11 @@
typedef struct pa_x11_wrapper pa_x11_wrapper;
+typedef struct pa_x11_client pa_x11_client;
+
+typedef int (*pa_x11_event_cb_t)(pa_x11_wrapper *w, XEvent *e, void *userdata);
+typedef void (*pa_x11_kill_cb_t)(pa_x11_wrapper *w, void *userdata);
+
/* Return the X11 wrapper for this core. In case no wrapper was
existant before, allocate a new one */
pa_x11_wrapper* pa_x11_wrapper_get(pa_core *c, const char *name);
@@ -43,10 +46,11 @@ void pa_x11_wrapper_unref(pa_x11_wrapper* w);
/* Return the X11 display object for this connection */
Display *pa_x11_wrapper_get_display(pa_x11_wrapper *w);
-typedef struct pa_x11_client pa_x11_client;
+/* Kill the connection to the X11 display */
+void pa_x11_wrapper_kill(pa_x11_wrapper *w);
/* Register an X11 client, that is called for each X11 event */
-pa_x11_client* pa_x11_client_new(pa_x11_wrapper *w, int (*cb)(pa_x11_wrapper *w, XEvent *e, void *userdata), void *userdata);
+pa_x11_client* pa_x11_client_new(pa_x11_wrapper *w, pa_x11_event_cb_t event_cb, pa_x11_kill_cb_t kill_cb, void *userdata);
/* Free an X11 client object */
void pa_x11_client_free(pa_x11_client *c);