summaryrefslogtreecommitdiff
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.c148
-rw-r--r--src/pulsecore/asyncq.h23
-rw-r--r--src/pulsecore/atomic.h61
-rw-r--r--src/pulsecore/auth-cookie.c109
-rw-r--r--src/pulsecore/auth-cookie.h (renamed from src/pulsecore/core-def.h)18
-rw-r--r--src/pulsecore/authkey-prop.c108
-rw-r--r--src/pulsecore/authkey-prop.h45
-rw-r--r--src/pulsecore/authkey.c14
-rw-r--r--src/pulsecore/authkey.h2
-rw-r--r--src/pulsecore/autoload.c4
-rw-r--r--src/pulsecore/autoload.h2
-rw-r--r--src/pulsecore/avahi-wrap.c2
-rw-r--r--src/pulsecore/avahi-wrap.h2
-rw-r--r--src/pulsecore/cli-command.c167
-rw-r--r--src/pulsecore/cli-command.h5
-rw-r--r--src/pulsecore/cli-text.c240
-rw-r--r--src/pulsecore/cli-text.h2
-rw-r--r--src/pulsecore/cli.c22
-rw-r--r--src/pulsecore/cli.h8
-rw-r--r--src/pulsecore/client.c33
-rw-r--r--src/pulsecore/client.h10
-rw-r--r--src/pulsecore/conf-parser.c26
-rw-r--r--src/pulsecore/conf-parser.h5
-rw-r--r--src/pulsecore/core-error.c2
-rw-r--r--src/pulsecore/core-error.h2
-rw-r--r--src/pulsecore/core-scache.c123
-rw-r--r--src/pulsecore/core-scache.h19
-rw-r--r--src/pulsecore/core-subscribe.c14
-rw-r--r--src/pulsecore/core-subscribe.h2
-rw-r--r--src/pulsecore/core-util.c1216
-rw-r--r--src/pulsecore/core-util.h111
-rw-r--r--src/pulsecore/core.c67
-rw-r--r--src/pulsecore/core.h41
-rw-r--r--src/pulsecore/creds.h2
-rw-r--r--src/pulsecore/dllmain.c2
-rw-r--r--src/pulsecore/dynarray.c17
-rw-r--r--src/pulsecore/dynarray.h12
-rw-r--r--src/pulsecore/endianmacros.h13
-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.c142
-rw-r--r--src/pulsecore/fdsem.h10
-rw-r--r--src/pulsecore/ffmpeg/resample2.c4
-rw-r--r--src/pulsecore/flist.c97
-rw-r--r--src/pulsecore/flist.h9
-rw-r--r--src/pulsecore/gccmacro.h90
-rw-r--r--src/pulsecore/hashmap.c181
-rw-r--r--src/pulsecore/hashmap.h29
-rw-r--r--src/pulsecore/hook-list.c41
-rw-r--r--src/pulsecore/hook-list.h23
-rw-r--r--src/pulsecore/idxset.c392
-rw-r--r--src/pulsecore/idxset.h36
-rw-r--r--src/pulsecore/inet_ntop.c2
-rw-r--r--src/pulsecore/inet_pton.c2
-rw-r--r--src/pulsecore/iochannel.c15
-rw-r--r--src/pulsecore/iochannel.h4
-rw-r--r--src/pulsecore/ioline.c54
-rw-r--r--src/pulsecore/ioline.h6
-rw-r--r--src/pulsecore/ipacl.c4
-rw-r--r--src/pulsecore/ipacl.h2
-rw-r--r--src/pulsecore/llist.h2
-rw-r--r--src/pulsecore/lock-autospawn.c330
-rw-r--r--src/pulsecore/lock-autospawn.h32
-rw-r--r--src/pulsecore/log.c80
-rw-r--r--src/pulsecore/log.h8
-rw-r--r--src/pulsecore/ltdl-helper.c10
-rw-r--r--src/pulsecore/ltdl-helper.h2
-rw-r--r--src/pulsecore/macro.h75
-rw-r--r--src/pulsecore/mcalign.c11
-rw-r--r--src/pulsecore/mcalign.h5
-rw-r--r--src/pulsecore/memblock.c168
-rw-r--r--src/pulsecore/memblock.h17
-rw-r--r--src/pulsecore/memblockq.c488
-rw-r--r--src/pulsecore/memblockq.h84
-rw-r--r--src/pulsecore/memchunk.c38
-rw-r--r--src/pulsecore/memchunk.h5
-rw-r--r--src/pulsecore/modargs.c25
-rw-r--r--src/pulsecore/modargs.h2
-rw-r--r--src/pulsecore/modinfo.c2
-rw-r--r--src/pulsecore/modinfo.h2
-rw-r--r--src/pulsecore/module.c113
-rw-r--r--src/pulsecore/module.h16
-rw-r--r--src/pulsecore/msgobject.c2
-rw-r--r--src/pulsecore/msgobject.h2
-rw-r--r--src/pulsecore/mutex-posix.c14
-rw-r--r--src/pulsecore/mutex-win32.c2
-rw-r--r--src/pulsecore/mutex.h3
-rw-r--r--src/pulsecore/namereg.c21
-rw-r--r--src/pulsecore/namereg.h7
-rw-r--r--src/pulsecore/native-common.h19
-rw-r--r--src/pulsecore/object.c2
-rw-r--r--src/pulsecore/object.h4
-rw-r--r--src/pulsecore/once.c2
-rw-r--r--src/pulsecore/once.h2
-rw-r--r--src/pulsecore/packet.c2
-rw-r--r--src/pulsecore/packet.h2
-rw-r--r--src/pulsecore/parseaddr.c23
-rw-r--r--src/pulsecore/parseaddr.h2
-rw-r--r--src/pulsecore/pdispatch.c4
-rw-r--r--src/pulsecore/pdispatch.h2
-rw-r--r--src/pulsecore/pid.c137
-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.c119
-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/prioq.c256
-rw-r--r--src/pulsecore/prioq.h64
-rw-r--r--src/pulsecore/proplist-util.c118
-rw-r--r--src/pulsecore/proplist-util.h29
-rw-r--r--src/pulsecore/protocol-cli.c89
-rw-r--r--src/pulsecore/protocol-cli.h11
-rw-r--r--src/pulsecore/protocol-esound.c528
-rw-r--r--src/pulsecore/protocol-esound.h32
-rw-r--r--src/pulsecore/protocol-http.c100
-rw-r--r--src/pulsecore/protocol-http.h14
-rw-r--r--src/pulsecore/protocol-native.c2325
-rw-r--r--src/pulsecore/protocol-native.h63
-rw-r--r--src/pulsecore/protocol-simple.c402
-rw-r--r--src/pulsecore/protocol-simple.h30
-rw-r--r--src/pulsecore/pstream-util.c2
-rw-r--r--src/pulsecore/pstream-util.h2
-rw-r--r--src/pulsecore/pstream.c71
-rw-r--r--src/pulsecore/pstream.h15
-rw-r--r--src/pulsecore/queue.c13
-rw-r--r--src/pulsecore/queue.h17
-rw-r--r--src/pulsecore/random.c23
-rw-r--r--src/pulsecore/random.h2
-rw-r--r--src/pulsecore/refcnt.h5
-rw-r--r--src/pulsecore/resampler.c326
-rw-r--r--src/pulsecore/resampler.h17
-rw-r--r--src/pulsecore/rtclock.c23
-rw-r--r--src/pulsecore/rtclock.h5
-rw-r--r--src/pulsecore/rtpoll.c141
-rw-r--r--src/pulsecore/rtpoll.h8
-rw-r--r--src/pulsecore/rtsig.c4
-rw-r--r--src/pulsecore/rtsig.h2
-rw-r--r--src/pulsecore/sample-util.c594
-rw-r--r--src/pulsecore/sample-util.h23
-rw-r--r--src/pulsecore/sconv-s16be.c2
-rw-r--r--src/pulsecore/sconv-s16be.h2
-rw-r--r--src/pulsecore/sconv-s16le.c44
-rw-r--r--src/pulsecore/sconv-s16le.h2
-rw-r--r--src/pulsecore/sconv.c28
-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/shared.c (renamed from src/pulsecore/props.c)78
-rw-r--r--src/pulsecore/shared.h (renamed from src/pulsecore/props.h)43
-rw-r--r--src/pulsecore/shm.c44
-rw-r--r--src/pulsecore/shm.h10
-rw-r--r--src/pulsecore/sink-input.c949
-rw-r--r--src/pulsecore/sink-input.h166
-rw-r--r--src/pulsecore/sink.c1092
-rw-r--r--src/pulsecore/sink.h157
-rw-r--r--src/pulsecore/sioman.c2
-rw-r--r--src/pulsecore/sioman.h2
-rw-r--r--src/pulsecore/socket-client.c74
-rw-r--r--src/pulsecore/socket-client.h10
-rw-r--r--src/pulsecore/socket-server.c19
-rw-r--r--src/pulsecore/socket-server.h6
-rw-r--r--src/pulsecore/socket-util.c41
-rw-r--r--src/pulsecore/socket-util.h8
-rw-r--r--src/pulsecore/sound-file-stream.c201
-rw-r--r--src/pulsecore/sound-file-stream.h2
-rw-r--r--src/pulsecore/sound-file.c25
-rw-r--r--src/pulsecore/sound-file.h2
-rw-r--r--src/pulsecore/source-output.c427
-rw-r--r--src/pulsecore/source-output.h106
-rw-r--r--src/pulsecore/source.c711
-rw-r--r--src/pulsecore/source.h132
-rw-r--r--src/pulsecore/speex/arch.h241
-rw-r--r--src/pulsecore/speex/fixed_generic.h106
-rw-r--r--src/pulsecore/speex/resample.c1121
-rw-r--r--src/pulsecore/speex/speex_resampler.h328
-rw-r--r--src/pulsecore/speexwrap.h48
-rw-r--r--src/pulsecore/start-child.c62
-rw-r--r--src/pulsecore/start-child.h2
-rw-r--r--src/pulsecore/strbuf.c16
-rw-r--r--src/pulsecore/strbuf.h6
-rw-r--r--src/pulsecore/strlist.c2
-rw-r--r--src/pulsecore/strlist.h2
-rw-r--r--src/pulsecore/tagstruct.c112
-rw-r--r--src/pulsecore/tagstruct.h15
-rw-r--r--src/pulsecore/thread-mq.c37
-rw-r--r--src/pulsecore/thread-mq.h7
-rw-r--r--src/pulsecore/thread-posix.c11
-rw-r--r--src/pulsecore/thread-win32.c2
-rw-r--r--src/pulsecore/thread.h7
-rw-r--r--src/pulsecore/time-smoother.c265
-rw-r--r--src/pulsecore/time-smoother.h14
-rw-r--r--src/pulsecore/tokenizer.c6
-rw-r--r--src/pulsecore/tokenizer.h2
-rw-r--r--src/pulsecore/x11prop.c6
-rw-r--r--src/pulsecore/x11prop.h2
-rw-r--r--src/pulsecore/x11wrap.c64
-rw-r--r--src/pulsecore/x11wrap.h12
205 files changed, 12027 insertions, 7030 deletions
diff --git a/src/pulsecore/asyncmsgq.c b/src/pulsecore/asyncmsgq.c
index f8ddda1..5c7af2a 100644
--- a/src/pulsecore/asyncmsgq.c
+++ b/src/pulsecore/asyncmsgq.c
@@ -1,5 +1,3 @@
-/* $Id: asyncmsgq.c 1971 2007-10-28 19:13:50Z lennart $ */
-
/***
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 932a5b1..1f38207 100644
--- a/src/pulsecore/asyncmsgq.h
+++ b/src/pulsecore/asyncmsgq.h
@@ -1,8 +1,6 @@
#ifndef foopulseasyncmsgqhfoo
#define foopulseasyncmsgqhfoo
-/* $Id: asyncmsgq.h 1971 2007-10-28 19:13:50Z lennart $ */
-
/***
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 4724c1b..f64931a 100644
--- a/src/pulsecore/asyncq.c
+++ b/src/pulsecore/asyncq.c
@@ -1,9 +1,7 @@
-/* $Id: asyncq.c 1971 2007-10-28 19:13:50Z lennart $ */
-
/***
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,20 +51,27 @@
#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) {
+static unsigned reduce(pa_asyncq *l, unsigned 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,13 +116,23 @@ 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) {
- int idx;
+static int push(pa_asyncq*l, void *p, pa_bool_t wait) {
+ unsigned idx;
pa_atomic_ptr_t *cells;
pa_assert(l);
@@ -141,8 +163,64 @@ int pa_asyncq_push(pa_asyncq*l, void *p, int wait) {
return 0;
}
-void* pa_asyncq_pop(pa_asyncq*l, int wait) {
- int idx;
+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) {
+ unsigned idx;
void *ret;
pa_atomic_ptr_t *cells;
@@ -178,14 +256,14 @@ void* pa_asyncq_pop(pa_asyncq*l, int wait) {
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 idx;
+int pa_asyncq_read_before_poll(pa_asyncq *l) {
+ unsigned idx;
pa_atomic_ptr_t *cells;
pa_assert(l);
@@ -202,12 +280,40 @@ int pa_asyncq_before_poll(pa_asyncq *l) {
if (pa_fdsem_before_poll(l->write_fdsem) >= 0)
return 0;
}
-
- 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 d47f472..e6847ab 100644
--- a/src/pulsecore/asyncq.h
+++ b/src/pulsecore/asyncq.h
@@ -1,8 +1,6 @@
#ifndef foopulseasyncqhfoo
#define foopulseasyncqhfoo
-/* $Id: asyncq.h 1971 2007-10-28 19:13:50Z lennart $ */
-
/***
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 efe68d1..9c58c66 100644
--- a/src/pulsecore/atomic.h
+++ b/src/pulsecore/atomic.h
@@ -1,12 +1,11 @@
#ifndef foopulseatomichfoo
#define foopulseatomichfoo
-/* $Id: atomic.h 2170 2008-03-27 23:37:23Z lennart $ */
-
/***
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
@@ -24,6 +23,8 @@
USA.
***/
+#include <pulsecore/macro.h>
+
/*
* atomic_ops guarantees us that sizeof(AO_t) == sizeof(void*). It is
* not guaranteed however, that sizeof(AO_t) == sizeof(size_t).
@@ -36,7 +37,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
@@ -81,8 +82,8 @@ 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) {
+/* Returns TRUE when the operation was successful. */
+static inline pa_bool_t pa_atomic_cmpxchg(pa_atomic_t *a, int old_i, int new_i) {
return __sync_bool_compare_and_swap(&a->value, old_i, new_i);
}
@@ -102,13 +103,13 @@ static inline void pa_atomic_ptr_store(pa_atomic_ptr_t *a, void *p) {
__sync_synchronize();
}
-static inline int pa_atomic_ptr_cmpxchg(pa_atomic_ptr_t *a, void *old_p, void* new_p) {
+static inline pa_bool_t pa_atomic_ptr_cmpxchg(pa_atomic_ptr_t *a, void *old_p, void* new_p) {
return __sync_bool_compare_and_swap(&a->value, (long) old_p, (long) new_p);
}
#elif defined(__GNUC__) && (defined(__amd64__) || defined(__x86_64__))
-#error "The native atomic operations implementation for AMD64 has not been tested. libatomic_ops is known to not work properly on AMD64 and your gcc version is too old for the gcc-builtin atomic ops support. You have three options now: make the native atomic operations implementation for AMD64 work, fix libatomic_ops, or upgrade your GCC."
+#warn "The native atomic operations implementation for AMD64 has not been tested thoroughly. libatomic_ops is known to not work properly on AMD64 and your gcc version is too old for the gcc-builtin atomic ops support. You have three options now: test the native atomic operations implementation for AMD64, fix libatomic_ops, or upgrade your GCC."
/* Addapted from glibc */
@@ -148,14 +149,14 @@ 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) {
+static inline pa_bool_t 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));
- return result == oldval;
+ return result == old_i;
}
typedef struct pa_atomic_ptr {
@@ -172,14 +173,14 @@ static inline void pa_atomic_ptr_store(pa_atomic_ptr_t *a, void *p) {
a->value = (unsigned long) p;
}
-static inline int pa_atomic_ptr_cmpxchg(pa_atomic_ptr_t *a, void *old_p, void* new_p) {
+static inline pa_bool_t 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));
- return result;
+ return result == old_p;
}
#elif defined(ATOMIC_ARM_INLINE_ASM)
@@ -256,7 +257,7 @@ 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) {
+static inline pa_bool_t pa_atomic_cmpxchg(pa_atomic_t *a, int old_i, int new_i) {
unsigned long not_equal, not_exclusive;
pa_memory_barrier();
@@ -290,7 +291,7 @@ static inline void pa_atomic_ptr_store(pa_atomic_ptr_t *a, void *p) {
pa_memory_barrier();
}
-static inline int pa_atomic_ptr_cmpxchg(pa_atomic_ptr_t *a, void *old_p, void* new_p) {
+static inline pa_bool_t pa_atomic_ptr_cmpxchg(pa_atomic_ptr_t *a, void *old_p, void* new_p) {
unsigned long not_equal, not_exclusive;
pa_memory_barrier();
@@ -378,11 +379,11 @@ 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) {
- int failed = 1;
+/* Returns TRUE when the operation was successful. */
+static inline pa_bool_t 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);
+ failed = !!__kernel_cmpxchg(old_i, new_i, &a->value);
} while(failed && a->value == old_i);
return !failed;
}
@@ -403,11 +404,11 @@ static inline void pa_atomic_ptr_store(pa_atomic_ptr_t *a, void *p) {
pa_memory_barrier();
}
-static inline int pa_atomic_ptr_cmpxchg(pa_atomic_ptr_t *a, void *old_p, void* new_p) {
- int failed = 1;
+static inline pa_bool_t 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 == old_p);
+ failed = !!__kernel_cmpxchg_u((unsigned long) old_p, (unsigned long) new_p, &a->value);
+ } while(failed && a->value == (unsigned long) old_p);
return !failed;
}
@@ -421,7 +422,7 @@ typedef struct pa_atomic {
volatile AO_t value;
} pa_atomic_t;
-#define PA_ATOMIC_INIT(v) { .value = (v) }
+#define PA_ATOMIC_INIT(v) { .value = (AO_t) (v) }
static inline int pa_atomic_load(const pa_atomic_t *a) {
return (int) AO_load_full((AO_t*) &a->value);
@@ -432,23 +433,23 @@ static inline void pa_atomic_store(pa_atomic_t *a, int i) {
}
static inline int pa_atomic_add(pa_atomic_t *a, int i) {
- return AO_fetch_and_add_full(&a->value, (AO_t) i);
+ return (int) AO_fetch_and_add_full(&a->value, (AO_t) i);
}
static inline int pa_atomic_sub(pa_atomic_t *a, int i) {
- return AO_fetch_and_add_full(&a->value, (AO_t) -i);
+ return (int) AO_fetch_and_add_full(&a->value, (AO_t) -i);
}
static inline int pa_atomic_inc(pa_atomic_t *a) {
- return AO_fetch_and_add1_full(&a->value);
+ return (int) AO_fetch_and_add1_full(&a->value);
}
static inline int pa_atomic_dec(pa_atomic_t *a) {
- return AO_fetch_and_sub1_full(&a->value);
+ return (int) AO_fetch_and_sub1_full(&a->value);
}
-static inline int pa_atomic_cmpxchg(pa_atomic_t *a, int old_i, int new_i) {
- return AO_compare_and_swap_full(&a->value, old_i, new_i);
+static inline pa_bool_t pa_atomic_cmpxchg(pa_atomic_t *a, int old_i, int new_i) {
+ return AO_compare_and_swap_full(&a->value, (unsigned long) old_i, (unsigned long) new_i);
}
typedef struct pa_atomic_ptr {
@@ -465,7 +466,7 @@ static inline void pa_atomic_ptr_store(pa_atomic_ptr_t *a, void *p) {
AO_store_full(&a->value, (AO_t) p);
}
-static inline int pa_atomic_ptr_cmpxchg(pa_atomic_ptr_t *a, void *old_p, void* new_p) {
+static inline pa_bool_t pa_atomic_ptr_cmpxchg(pa_atomic_ptr_t *a, void *old_p, void* new_p) {
return AO_compare_and_swap_full(&a->value, (AO_t) old_p, (AO_t) new_p);
}
diff --git a/src/pulsecore/auth-cookie.c b/src/pulsecore/auth-cookie.c
new file mode 100644
index 0000000..68b0147
--- /dev/null
+++ b/src/pulsecore/auth-cookie.c
@@ -0,0 +1,109 @@
+/***
+ 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 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/types.h>
+
+#include <pulse/xmalloc.h>
+#include <pulse/util.h>
+
+#include <pulsecore/refcnt.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/shared.h>
+#include <pulsecore/authkey.h>
+
+#include "auth-cookie.h"
+
+struct pa_auth_cookie {
+ PA_REFCNT_DECLARE;
+ pa_core *core;
+ char *name;
+ size_t size;
+};
+
+pa_auth_cookie* pa_auth_cookie_get(pa_core *core, const char *cn, size_t size) {
+ pa_auth_cookie *c;
+ char *t;
+
+ pa_assert(core);
+ pa_assert(size > 0);
+
+ t = pa_sprintf_malloc("auth-cookie%s%s", cn ? "@" : "", cn ? cn : "");
+
+ if ((c = pa_shared_get(core, t))) {
+
+ pa_xfree(t);
+
+ if (c->size != size)
+ return NULL;
+
+ return pa_auth_cookie_ref(c);
+ }
+
+ c = pa_xmalloc(PA_ALIGN(sizeof(pa_auth_cookie)) + size);
+ PA_REFCNT_INIT(c);
+ c->core = core;
+ c->name = t;
+ c->size = size;
+
+ pa_assert_se(pa_shared_set(core, t, c) >= 0);
+
+ if (pa_authkey_load_auto(cn, (uint8_t*) c + PA_ALIGN(sizeof(pa_auth_cookie)), size) < 0) {
+ pa_auth_cookie_unref(c);
+ return NULL;
+ }
+
+ return c;
+}
+
+pa_auth_cookie* pa_auth_cookie_ref(pa_auth_cookie *c) {
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+ PA_REFCNT_INC(c);
+
+ return c;
+}
+
+void pa_auth_cookie_unref(pa_auth_cookie *c) {
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+ if (PA_REFCNT_DEC(c) > 0)
+ return;
+
+ pa_assert_se(pa_shared_remove(c->core, c->name) >= 0);
+
+ pa_xfree(c->name);
+ pa_xfree(c);
+}
+
+const uint8_t* pa_auth_cookie_read(pa_auth_cookie *c, size_t size) {
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+ pa_assert(c->size == size);
+
+ return (const uint8_t*) c + PA_ALIGN(sizeof(pa_auth_cookie));
+}
diff --git a/src/pulsecore/core-def.h b/src/pulsecore/auth-cookie.h
index 24a17d7..c08cbd8 100644
--- a/src/pulsecore/core-def.h
+++ b/src/pulsecore/auth-cookie.h
@@ -1,12 +1,10 @@
-#ifndef foocoredefhfoo
-#define foocoredefhfoo
-
-/* $Id: core-def.h 1971 2007-10-28 19:13:50Z lennart $ */
+#ifndef fooauthcookiehfoo
+#define fooauthcookiehfoo
/***
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
@@ -24,6 +22,14 @@
USA.
***/
-/* FIXME: Remove this shit */
+#include <pulsecore/core.h>
+
+typedef struct pa_auth_cookie pa_auth_cookie;
+
+pa_auth_cookie* pa_auth_cookie_get(pa_core *c, const char *cn, size_t size);
+pa_auth_cookie* pa_auth_cookie_ref(pa_auth_cookie *c);
+void pa_auth_cookie_unref(pa_auth_cookie *c);
+
+const uint8_t* pa_auth_cookie_read(pa_auth_cookie *, size_t size);
#endif
diff --git a/src/pulsecore/authkey-prop.c b/src/pulsecore/authkey-prop.c
deleted file mode 100644
index fa90fbd..0000000
--- a/src/pulsecore/authkey-prop.c
+++ /dev/null
@@ -1,108 +0,0 @@
-/* $Id: authkey-prop.c 1971 2007-10-28 19:13:50Z lennart $ */
-
-/***
- 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 HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <string.h>
-
-#include <pulse/xmalloc.h>
-
-#include <pulsecore/props.h>
-#include <pulsecore/macro.h>
-#include <pulsecore/log.h>
-#include <pulsecore/refcnt.h>
-
-#include "authkey-prop.h"
-
-struct authkey_data {
- PA_REFCNT_DECLARE;
- size_t length;
-};
-
-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);
- pa_assert(len > 0);
-
- if (!(a = pa_property_get(c, name)))
- return -1;
-
- 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);
-
- if (pa_property_get(c, name))
- return -1;
-
- a = pa_xmalloc(PA_ALIGN(sizeof(struct authkey_data)) + len);
- PA_REFCNT_INIT(a);
- a->length = len;
- memcpy((uint8_t*) a + PA_ALIGN(sizeof(struct authkey_data)), data, len);
-
- pa_property_set(c, name, a);
-
- return 0;
-}
-
-void pa_authkey_prop_ref(pa_core *c, const char *name) {
- struct authkey_data *a;
-
- pa_assert(c);
- pa_assert(name);
-
- a = pa_property_get(c, name);
- pa_assert(a);
- pa_assert(PA_REFCNT_VALUE(a) >= 1);
- PA_REFCNT_INC(a);
-}
-
-void pa_authkey_prop_unref(pa_core *c, const char *name) {
- struct authkey_data *a;
-
- pa_assert(c);
- pa_assert(name);
-
- a = pa_property_get(c, name);
- pa_assert(a);
- pa_assert(PA_REFCNT_VALUE(a) >= 1);
-
- if (PA_REFCNT_DEC(a) <= 0) {
- pa_property_remove(c, name);
- pa_xfree(a);
- }
-}
-
-
diff --git a/src/pulsecore/authkey-prop.h b/src/pulsecore/authkey-prop.h
deleted file mode 100644
index 5316668..0000000
--- a/src/pulsecore/authkey-prop.h
+++ /dev/null
@@ -1,45 +0,0 @@
-#ifndef fooauthkeyprophfoo
-#define fooauthkeyprophfoo
-
-/* $Id: authkey-prop.h 1426 2007-02-13 15:35:19Z ossman $ */
-
-/***
- 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.
-***/
-
-#include <pulsecore/core.h>
-
-/* The authkey-prop uses a central property to store a previously
- * loaded cookie in memory. Useful for sharing the same cookie between
- * several modules. */
-
-/* Return the data of the specified authorization key property. Doesn't alter the refernce count of the key */
-int pa_authkey_prop_get(pa_core *c, const char *name, void *data, size_t len);
-
-/* Store data in the specified authorization key property. The initial reference count is set to 1 */
-int pa_authkey_prop_put(pa_core *c, const char *name, const void *data, size_t len);
-
-/* Increase the reference count of the specified authorization key */
-void pa_authkey_prop_ref(pa_core *c, const char *name);
-
-/* Decrease the reference count of the specified authorization key */
-void pa_authkey_prop_unref(pa_core *c, const char *name);
-
-#endif
diff --git a/src/pulsecore/authkey.c b/src/pulsecore/authkey.c
index 5a99d9e..b122fee 100644
--- a/src/pulsecore/authkey.c
+++ b/src/pulsecore/authkey.c
@@ -1,5 +1,3 @@
-/* $Id: authkey.c 1971 2007-10-28 19:13:50Z lennart $ */
-
/***
This file is part of PulseAudio.
@@ -56,8 +54,8 @@ static int generate(int fd, void *ret_data, size_t length) {
pa_random(ret_data, length);
- lseek(fd, 0, SEEK_SET);
- (void) ftruncate(fd, 0);
+ lseek(fd, (off_t) 0, SEEK_SET);
+ (void) ftruncate(fd, (off_t) 0);
if ((r = pa_loop_write(fd, ret_data, length, NULL)) < 0 || (size_t) r != length) {
pa_log("Failed to write cookie file: %s", pa_cstrerror(errno));
@@ -90,7 +88,7 @@ static int load(const char *fn, void *data, size_t length) {
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));
+ pa_log_warn("Failed to open cookie file '%s': %s", fn, pa_cstrerror(errno));
goto finish;
} else
writable = 0;
@@ -107,7 +105,7 @@ static int load(const char *fn, void *data, size_t length) {
pa_log_debug("Got %d bytes from cookie file '%s', expected %d", (int) r, fn, (int) length);
if (!writable) {
- pa_log("Unable to write cookie to read only file");
+ pa_log_warn("Unable to write cookie to read-only file");
goto finish;
}
@@ -142,7 +140,7 @@ int pa_authkey_load(const char *path, void *data, size_t length) {
pa_assert(length > 0);
if ((ret = load(path, data, length)) < 0)
- pa_log("Failed to load authorization key '%s': %s", path, (ret < 0) ? pa_cstrerror(errno) : "File corrupt");
+ pa_log_warn("Failed to load authorization key '%s': %s", path, (ret < 0) ? pa_cstrerror(errno) : "File corrupt");
return ret;
}
@@ -208,7 +206,7 @@ int pa_authkey_save(const char *fn, const void *data, size_t length) {
return -2;
if ((fd = open(p, O_RDWR|O_CREAT|O_NOCTTY, S_IRUSR|S_IWUSR)) < 0) {
- pa_log("Failed to open cookie file '%s': %s", fn, pa_cstrerror(errno));
+ pa_log_warn("Failed to open cookie file '%s': %s", fn, pa_cstrerror(errno));
goto finish;
}
diff --git a/src/pulsecore/authkey.h b/src/pulsecore/authkey.h
index 71d4ab4..8301db1 100644
--- a/src/pulsecore/authkey.h
+++ b/src/pulsecore/authkey.h
@@ -1,8 +1,6 @@
#ifndef fooauthkeyhfoo
#define fooauthkeyhfoo
-/* $Id: authkey.h 1426 2007-02-13 15:35:19Z ossman $ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/autoload.c b/src/pulsecore/autoload.c
index f8abbe6..8c84cee 100644
--- a/src/pulsecore/autoload.c
+++ b/src/pulsecore/autoload.c
@@ -1,5 +1,3 @@
-/* $Id: autoload.c 1971 2007-10-28 19:13:50Z lennart $ */
-
/***
This file is part of PulseAudio.
@@ -160,7 +158,7 @@ void pa_autoload_request(pa_core *c, const char *name, pa_namereg_type_t type) {
e->in_action = 0;
}
-static void free_func(void *p, PA_GCC_UNUSED void *userdata) {
+static void free_func(void *p, void *userdata) {
pa_autoload_entry *e = p;
pa_idxset_remove_by_data(e->core->autoload_idxset, e, NULL);
entry_free(e);
diff --git a/src/pulsecore/autoload.h b/src/pulsecore/autoload.h
index 0095c20..3926351 100644
--- a/src/pulsecore/autoload.h
+++ b/src/pulsecore/autoload.h
@@ -1,8 +1,6 @@
#ifndef fooautoloadhfoo
#define fooautoloadhfoo
-/* $Id: autoload.h 2166 2008-03-27 23:33:40Z lennart $ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/avahi-wrap.c b/src/pulsecore/avahi-wrap.c
index 32b0b92..d5f40d8 100644
--- a/src/pulsecore/avahi-wrap.c
+++ b/src/pulsecore/avahi-wrap.c
@@ -1,5 +1,3 @@
-/* $Id: avahi-wrap.c 1971 2007-10-28 19:13:50Z lennart $ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/avahi-wrap.h b/src/pulsecore/avahi-wrap.h
index 20f9505..7d8995b 100644
--- a/src/pulsecore/avahi-wrap.h
+++ b/src/pulsecore/avahi-wrap.h
@@ -1,8 +1,6 @@
#ifndef fooavahiwrapperhfoo
#define fooavahiwrapperhfoo
-/* $Id: avahi-wrap.h 1426 2007-02-13 15:35:19Z ossman $ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/cli-command.c b/src/pulsecore/cli-command.c
index 3cf565c..1624165 100644
--- a/src/pulsecore/cli-command.c
+++ b/src/pulsecore/cli-command.c
@@ -1,5 +1,3 @@
-/* $Id: cli-command.c 2175 2008-03-27 23:39:10Z lennart $ */
-
/***
This file is part of PulseAudio.
@@ -51,7 +49,7 @@
#include <pulsecore/play-memchunk.h>
#include <pulsecore/autoload.h>
#include <pulsecore/sound-file-stream.h>
-#include <pulsecore/props.h>
+#include <pulsecore/shared.h>
#include <pulsecore/core-util.h>
#include <pulsecore/core-error.h>
@@ -90,6 +88,7 @@ static int pa_cli_command_stat(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_b
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);
@@ -111,7 +110,7 @@ static int pa_cli_command_autoload_list(pa_core *c, pa_tokenizer *t, pa_strbuf *
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_list_shared_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);
@@ -136,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},
@@ -155,12 +155,12 @@ 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},
+ { "shared", pa_cli_command_list_shared_props, NULL, 1},
{ "move-sink-input", pa_cli_command_move_sink_input, "Move sink input to another sink (args: index, sink)", 3},
{ "move-source-output", pa_cli_command_move_source_output, "Move source output to another source (args: index, source)", 3},
{ "vacuum", pa_cli_command_vacuum, NULL, 1},
@@ -188,7 +188,9 @@ static int pa_cli_command_exit(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_b
pa_assert(buf);
pa_assert(fail);
- c->mainloop->quit(c->mainloop, 0);
+ if (pa_core_exit(c, FALSE, 0) < 0)
+ pa_strbuf_puts(buf, "Not allowed to terminate daemon.\n");
+
return 0;
}
@@ -316,22 +318,22 @@ static int pa_cli_command_stat(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_b
pa_strbuf_printf(buf, "Memory blocks currently allocated: %u, size: %s.\n",
(unsigned) pa_atomic_load(&stat->n_allocated),
- pa_bytes_snprint(s, sizeof(s), (size_t) pa_atomic_load(&stat->allocated_size)));
+ pa_bytes_snprint(s, sizeof(s), (unsigned) pa_atomic_load(&stat->allocated_size)));
pa_strbuf_printf(buf, "Memory blocks allocated during the whole lifetime: %u, size: %s.\n",
(unsigned) pa_atomic_load(&stat->n_accumulated),
- pa_bytes_snprint(s, sizeof(s), (size_t) pa_atomic_load(&stat->accumulated_size)));
+ pa_bytes_snprint(s, sizeof(s), (unsigned) pa_atomic_load(&stat->accumulated_size)));
pa_strbuf_printf(buf, "Memory blocks imported from other processes: %u, size: %s.\n",
(unsigned) pa_atomic_load(&stat->n_imported),
- pa_bytes_snprint(s, sizeof(s), (size_t) pa_atomic_load(&stat->imported_size)));
+ pa_bytes_snprint(s, sizeof(s), (unsigned) pa_atomic_load(&stat->imported_size)));
pa_strbuf_printf(buf, "Memory blocks exported to other processes: %u, size: %s.\n",
(unsigned) pa_atomic_load(&stat->n_exported),
- pa_bytes_snprint(s, sizeof(s), (size_t) pa_atomic_load(&stat->exported_size)));
+ pa_bytes_snprint(s, sizeof(s), (unsigned) pa_atomic_load(&stat->exported_size)));
pa_strbuf_printf(buf, "Total sample cache size: %s.\n",
- pa_bytes_snprint(s, sizeof(s), pa_scache_total_size(c)));
+ pa_bytes_snprint(s, sizeof(s), (unsigned) pa_scache_total_size(c)));
pa_strbuf_printf(buf, "Default sample spec: %s\n",
pa_sample_spec_snprint(s, sizeof(s), &c->default_sample_spec));
@@ -367,7 +369,7 @@ static int pa_cli_command_info(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_b
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;
}
@@ -415,7 +417,46 @@ static int pa_cli_command_unload(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa
return -1;
}
- pa_module_unload_request(m);
+ pa_module_unload_request(m, FALSE);
+ return 0;
+}
+
+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;
}
@@ -436,7 +477,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;
}
@@ -478,7 +519,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;
}
@@ -514,7 +555,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;
}
@@ -553,7 +594,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;
}
@@ -587,7 +628,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;
}
@@ -623,11 +664,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;
}
@@ -780,6 +821,7 @@ static int pa_cli_command_scache_list(pa_core *c, pa_tokenizer *t, pa_strbuf *bu
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);
@@ -796,11 +838,13 @@ 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;
}
@@ -902,6 +946,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;
@@ -920,6 +966,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;
@@ -941,6 +989,8 @@ 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);
@@ -948,13 +998,13 @@ static int pa_cli_command_autoload_list(pa_core *c, pa_tokenizer *t, pa_strbuf *
return 0;
}
-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_list_shared_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);
pa_assert(fail);
- pa_property_dump(c, buf);
+ pa_shared_dump(c, buf);
return 0;
}
@@ -1005,7 +1055,7 @@ 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;
}
@@ -1075,7 +1125,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;
}
@@ -1109,7 +1159,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;
}
@@ -1138,7 +1188,7 @@ static int pa_cli_command_suspend(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, p
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;
}
@@ -1201,8 +1251,9 @@ static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_b
nl = 1;
}
- pa_strbuf_printf(buf, "set-sink-volume %s 0x%03x\n", sink->name, pa_cvolume_avg(pa_sink_get_volume(sink)));
- pa_strbuf_printf(buf, "set-sink-mute %s %d\n", sink->name, pa_sink_get_mute(sink));
+ pa_strbuf_printf(buf, "set-sink-volume %s 0x%03x\n", sink->name, pa_cvolume_avg(pa_sink_get_volume(sink, FALSE)));
+ pa_strbuf_printf(buf, "set-sink-mute %s %s\n", sink->name, pa_yes_no(pa_sink_get_mute(sink, FALSE)));
+ pa_strbuf_printf(buf, "suspend-sink %s %s\n", sink->name, pa_yes_no(pa_sink_get_state(sink) == PA_SINK_SUSPENDED));
}
for (source = pa_idxset_first(c->sources, &idx); source; source = pa_idxset_next(c->sources, &idx)) {
@@ -1214,8 +1265,9 @@ static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_b
nl = 1;
}
- 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-volume %s 0x%03x\n", source->name, pa_cvolume_avg(pa_source_get_volume(source, FALSE)));
+ pa_strbuf_printf(buf, "set-source-mute %s %s\n", source->name, pa_yes_no(pa_source_get_mute(source, FALSE)));
+ pa_strbuf_printf(buf, "suspend-source %s %s\n", source->name, pa_yes_no(pa_source_get_state(source) == PA_SOURCE_SUSPENDED));
}
@@ -1390,16 +1442,45 @@ int pa_cli_command_execute_line(pa_core *c, const char *s, pa_strbuf *buf, pa_bo
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, pa_bool_t *fail) {
+int pa_cli_command_execute_file_stream(pa_core *c, FILE *f, pa_strbuf *buf, pa_bool_t *fail) {
char line[1024];
- FILE *f = NULL;
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)
@@ -1407,13 +1488,7 @@ int pa_cli_command_execute_file(pa_core *c, const char *fn, pa_strbuf *buf, pa_b
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;
@@ -1427,11 +1502,15 @@ 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);
diff --git a/src/pulsecore/cli-command.h b/src/pulsecore/cli-command.h
index 6ea8365..9bf35dc 100644
--- a/src/pulsecore/cli-command.h
+++ b/src/pulsecore/cli-command.h
@@ -1,8 +1,6 @@
#ifndef fooclicommandhfoo
#define fooclicommandhfoo
-/* $Id: cli-command.h 2008 2007-11-01 00:32:45Z lennart $ */
-
/***
This file is part of PulseAudio.
@@ -36,6 +34,9 @@ int pa_cli_command_execute_line(pa_core *c, const char *s, pa_strbuf *buf, pa_bo
/* 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_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, pa_bool_t *fail);
diff --git a/src/pulsecore/cli-text.c b/src/pulsecore/cli-text.c
index 9a8c1d5..7bbc266 100644
--- a/src/pulsecore/cli-text.c
+++ b/src/pulsecore/cli-text.c
@@ -1,5 +1,3 @@
-/* $Id: cli-text.c 2067 2007-11-21 01:30:40Z lennart $ */
-
/***
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,35 +115,48 @@ 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%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_HW_VOLUME_CTRL ? "HW_VOLUME_CTRL " : "",
- sink->flags & PA_SINK_LATENCY ? "LATENCY " : "",
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 " : "",
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_cvolume_snprint(cv, sizeof(cv), pa_sink_get_volume(sink, FALSE)),
+ pa_yes_no(pa_sink_get_mute(sink, FALSE)),
+ (double) pa_sink_get_latency(sink) / (double) PA_USEC_PER_MSEC,
+ (double) pa_sink_get_requested_latency(sink) / (double) PA_USEC_PER_MSEC,
+ (double) min_latency / PA_USEC_PER_MSEC,
+ (double) max_latency / PA_USEC_PER_MSEC,
+ (unsigned long) pa_sink_get_max_request(sink) / 1024,
+ (unsigned long) pa_sink_get_max_rewind(sink) / 1024,
sink->monitor_source ? sink->monitor_source->index : PA_INVALID_INDEX,
pa_sample_spec_snprint(ss, sizeof(ss), &sink->sample_spec),
pa_channel_map_snprint(cm, sizeof(cm), &sink->channel_map),
@@ -140,9 +164,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);
@@ -153,6 +179,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",
@@ -165,46 +192,58 @@ 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%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_HW_VOLUME_CTRL ? "HW_VOLUME_CTRL " : "",
- source->flags & PA_SOURCE_LATENCY ? "LATENCY " : "",
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 " : "",
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_cvolume_snprint(cv, sizeof(cv), pa_source_get_volume(source, FALSE)),
+ pa_yes_no(pa_source_get_mute(source, FALSE)),
+ (double) pa_source_get_latency(source) / PA_USEC_PER_MSEC,
+ (double) pa_source_get_requested_latency(source) / PA_USEC_PER_MSEC,
+ (double) min_latency / PA_USEC_PER_MSEC,
+ (double) max_latency / PA_USEC_PER_MSEC,
+ (unsigned long) pa_source_get_max_rewind(source) / 1024,
pa_sample_spec_snprint(ss, sizeof(ss), &source->sample_spec),
pa_channel_map_snprint(cm, sizeof(cm), &source->channel_map),
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);
@@ -216,6 +255,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"
@@ -227,27 +267,33 @@ 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%s%s%s%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 " : "",
@@ -255,14 +301,21 @@ char *pa_source_output_list_to_string(pa_core *c) {
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);
@@ -273,6 +326,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",
@@ -285,29 +339,35 @@ 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%s%s%s%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 " : "",
@@ -316,16 +376,21 @@ char *pa_sink_input_list_to_string(pa_core *c) {
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);
@@ -345,25 +410,25 @@ 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);
pa_channel_map_snprint(cm, sizeof(cm), &e->channel_map);
- l = (double) e->memchunk.length / pa_bytes_per_second(&e->sample_spec);
+ l = (double) e->memchunk.length / (double) pa_bytes_per_second(&e->sample_spec);
}
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,
@@ -371,8 +436,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);
}
}
@@ -393,7 +462,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 0c71cd7..f4cb97a 100644
--- a/src/pulsecore/cli-text.h
+++ b/src/pulsecore/cli-text.h
@@ -1,8 +1,6 @@
#ifndef fooclitexthfoo
#define fooclitexthfoo
-/* $Id: cli-text.h 1426 2007-02-13 15:35:19Z ossman $ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/cli.c b/src/pulsecore/cli.c
index 7bff329..67bf1e7 100644
--- a/src/pulsecore/cli.c
+++ b/src/pulsecore/cli.c
@@ -1,5 +1,3 @@
-/* $Id: cli.c 2008 2007-11-01 00:32:45Z lennart $ */
-
/***
This file is part of PulseAudio.
@@ -54,7 +52,7 @@ struct pa_cli {
pa_core *core;
pa_ioline *line;
- void (*eof_callback)(pa_cli *c, void *userdata);
+ pa_cli_eof_cb_t eof_callback;
void *userdata;
pa_client *client;
@@ -82,7 +80,7 @@ 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);
@@ -109,12 +107,11 @@ static void client_kill(pa_client *client) {
pa_assert_se(c = client->userdata);
pa_log_debug("CLI client killed.");
+
if (c->defer_kill)
c->kill_requested = TRUE;
- else {
- if (c->eof_callback)
- c->eof_callback(c, c->userdata);
- }
+ else if (c->eof_callback)
+ c->eof_callback(c, c->userdata);
}
static void line_callback(pa_ioline *line, const char *s, void *userdata) {
@@ -127,6 +124,7 @@ static void line_callback(pa_ioline *line, const char *s, void *userdata) {
if (!s) {
pa_log_debug("CLI got EOF from user.");
+
if (c->eof_callback)
c->eof_callback(c, c->userdata);
@@ -147,9 +145,15 @@ static void line_callback(pa_ioline *line, const char *s, void *userdata) {
pa_ioline_puts(line, PROMPT);
}
-void pa_cli_set_eof_callback(pa_cli *c, void (*cb)(pa_cli*c, void *userdata), void *userdata) {
+void pa_cli_set_eof_callback(pa_cli *c, pa_cli_eof_cb_t cb, void *userdata) {
pa_assert(c);
c->eof_callback = cb;
c->userdata = userdata;
}
+
+pa_module *pa_cli_get_module(pa_cli *c) {
+ pa_assert(c);
+
+ return c->client->module;
+}
diff --git a/src/pulsecore/cli.h b/src/pulsecore/cli.h
index 991dc35..d860461 100644
--- a/src/pulsecore/cli.h
+++ b/src/pulsecore/cli.h
@@ -1,8 +1,6 @@
#ifndef fooclihfoo
#define fooclihfoo
-/* $Id: cli.h 1426 2007-02-13 15:35:19Z ossman $ */
-
/***
This file is part of PulseAudio.
@@ -30,11 +28,15 @@
typedef struct pa_cli pa_cli;
+typedef void (*pa_cli_eof_cb_t)(pa_cli *c, void *userdata);
+
/* Create a new command line session on the specified io channel owned by the specified module */
pa_cli* pa_cli_new(pa_core *core, pa_iochannel *io, pa_module *m);
void pa_cli_free(pa_cli *cli);
/* Set a callback function that is called whenever the command line session is terminated */
-void pa_cli_set_eof_callback(pa_cli *cli, void (*cb)(pa_cli*c, void *userdata), void *userdata);
+void pa_cli_set_eof_callback(pa_cli *cli, pa_cli_eof_cb_t cb, void *userdata);
+
+pa_module *pa_cli_get_module(pa_cli *c);
#endif
diff --git a/src/pulsecore/client.c b/src/pulsecore/client.c
index c3a174c..ab6e5df 100644
--- a/src/pulsecore/client.c
+++ b/src/pulsecore/client.c
@@ -1,5 +1,3 @@
-/* $Id: client.c 1971 2007-10-28 19:13:50Z lennart $ */
-
/***
This file is part of PulseAudio.
@@ -35,6 +33,7 @@
#include <pulsecore/core-subscribe.h>
#include <pulsecore/log.h>
#include <pulsecore/macro.h>
+#include <pulsecore/core-util.h>
#include "client.h"
@@ -44,37 +43,42 @@ pa_client *pa_client_new(pa_core *core, const char *driver, const char *name) {
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);
+ pa_core_check_idle(core);
return c;
}
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_idle(core);
}
void pa_client_kill(pa_client *c) {
@@ -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 79cf322..28d1fe5 100644
--- a/src/pulsecore/client.h
+++ b/src/pulsecore/client.h
@@ -1,8 +1,6 @@
#ifndef foopulseclienthfoo
#define foopulseclienthfoo
-/* $Id: client.h 1426 2007-02-13 15:35:19Z ossman $ */
-
/***
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 554de82..58ceab9 100644
--- a/src/pulsecore/conf-parser.c
+++ b/src/pulsecore/conf-parser.c
@@ -1,5 +1,3 @@
-/* $Id: conf-parser.c 2007 2007-11-01 00:31:59Z lennart $ */
-
/***
This file is part of PulseAudio.
@@ -150,7 +148,7 @@ finish:
return r;
}
-int pa_config_parse_int(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, PA_GCC_UNUSED void *userdata) {
+int pa_config_parse_int(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata) {
int *i = data;
int32_t k;
@@ -168,7 +166,25 @@ int pa_config_parse_int(const char *filename, unsigned line, const char *lvalue,
return 0;
}
-int pa_config_parse_bool(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, PA_GCC_UNUSED void *userdata) {
+int pa_config_parse_size(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata) {
+ size_t *i = data;
+ uint32_t k;
+
+ pa_assert(filename);
+ pa_assert(lvalue);
+ pa_assert(rvalue);
+ pa_assert(data);
+
+ if (pa_atou(rvalue, &k) < 0) {
+ pa_log("[%s:%u] Failed to parse numeric value: %s", filename, line, rvalue);
+ return -1;
+ }
+
+ *i = (size_t) k;
+ return 0;
+}
+
+int pa_config_parse_bool(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata) {
int k;
pa_bool_t *b = data;
@@ -187,7 +203,7 @@ int pa_config_parse_bool(const char *filename, unsigned line, const char *lvalue
return 0;
}
-int pa_config_parse_string(const char *filename, PA_GCC_UNUSED unsigned line, const char *lvalue, const char *rvalue, void *data, PA_GCC_UNUSED void *userdata) {
+int pa_config_parse_string(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata) {
char **s = data;
pa_assert(filename);
diff --git a/src/pulsecore/conf-parser.h b/src/pulsecore/conf-parser.h
index 19a4636..a5174fc 100644
--- a/src/pulsecore/conf-parser.h
+++ b/src/pulsecore/conf-parser.h
@@ -1,8 +1,6 @@
#ifndef fooconfparserhfoo
#define fooconfparserhfoo
-/* $Id: conf-parser.h 1426 2007-02-13 15:35:19Z ossman $ */
-
/***
This file is part of PulseAudio.
@@ -41,8 +39,9 @@ typedef struct pa_config_item {
* NULL */
int pa_config_parse(const char *filename, FILE *f, const pa_config_item *t, void *userdata);
-/* Generic parsers for integers, booleans and strings */
+/* Generic parsers for integers, size_t, booleans and strings */
int pa_config_parse_int(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata);
+int pa_config_parse_size(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata);
int pa_config_parse_bool(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata);
int pa_config_parse_string(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata);
diff --git a/src/pulsecore/core-error.c b/src/pulsecore/core-error.c
index dfe0611..3d6c2c3 100644
--- a/src/pulsecore/core-error.c
+++ b/src/pulsecore/core-error.c
@@ -1,5 +1,3 @@
-/* $Id: core-error.c 1971 2007-10-28 19:13:50Z lennart $ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/core-error.h b/src/pulsecore/core-error.h
index 473f174..b0c306c 100644
--- a/src/pulsecore/core-error.h
+++ b/src/pulsecore/core-error.h
@@ -1,8 +1,6 @@
#ifndef foocoreerrorhfoo
#define foocoreerrorhfoo
-/* $Id: core-error.h 1426 2007-02-13 15:35:19Z ossman $ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/core-scache.c b/src/pulsecore/core-scache.c
index b475cac..1d080e1 100644
--- a/src/pulsecore/core-scache.c
+++ b/src/pulsecore/core-scache.c
@@ -1,9 +1,7 @@
-/* $Id: core-scache.c 2159 2008-03-27 23:29:32Z lennart $ */
-
/***
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,9 +61,9 @@
#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) {
+static void timeout_callback(pa_mainloop_api *m, pa_time_event*e, const struct timeval *tv, void *userdata) {
pa_core *c = userdata;
struct timeval ntv;
@@ -89,6 +87,8 @@ static void free_entry(pa_scache_entry *e) {
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);
}
@@ -98,11 +98,12 @@ static pa_scache_entry* scache_add_item(pa_core *c, const char *name) {
pa_assert(c);
pa_assert(name);
- if ((e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE, 0))) {
+ if ((e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE, FALSE))) {
if (e->memchunk.memblock)
pa_memblock_unref(e->memchunk.memblock);
pa_xfree(e->filename);
+ pa_proplist_clear(e->proplist);
pa_assert(e->core == c);
@@ -110,18 +111,17 @@ static pa_scache_entry* scache_add_item(pa_core *c, const char *name) {
} else {
e = pa_xnew(pa_scache_entry, 1);
- if (!pa_namereg_register(c, name, PA_NAMEREG_SAMPLE, e, 1)) {
+ if (!pa_namereg_register(c, name, PA_NAMEREG_SAMPLE, e, TRUE)) {
pa_xfree(e);
return NULL;
}
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,20 +129,29 @@ 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_sample_spec_init(&e->sample_spec);
pa_channel_map_init(&e->channel_map);
- pa_cvolume_reset(&e->volume, PA_CHANNELS_MAX);
+ pa_cvolume_init(&e->volume);
+
+ 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;
@@ -150,11 +159,12 @@ int pa_scache_add_item(pa_core *c, const char *name, const pa_sample_spec *ss, c
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));
+ pa_assert(!map || (pa_channel_map_valid(map) && ss && pa_channel_map_compatible(map, ss)));
- if (ss && !map)
- if (!(map = pa_channel_map_init_auto(&tmap, ss->channels, PA_CHANNEL_MAP_DEFAULT)))
- return -1;
+ if (ss && !map) {
+ pa_channel_map_init_extend(&tmap, ss->channels, PA_CHANNEL_MAP_DEFAULT);
+ map = &tmap;
+ }
if (chunk && chunk->length > PA_SCACHE_ENTRY_SIZE_MAX)
return -1;
@@ -162,12 +172,13 @@ 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_sample_spec_init(&e->sample_spec);
pa_channel_map_init(&e->channel_map);
+ pa_cvolume_init(&e->volume);
if (ss) {
e->sample_spec = *ss;
- e->volume.channels = e->sample_spec.channels;
+ pa_cvolume_reset(&e->volume, ss->channels);
}
if (map)
@@ -178,6 +189,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;
@@ -193,6 +207,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];
@@ -208,8 +223,11 @@ int pa_scache_add_file(pa_core *c, const char *name, const char *filename, uint3
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;
}
@@ -231,9 +249,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);
@@ -256,8 +276,7 @@ int pa_scache_remove_item(pa_core *c, const char *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);
@@ -266,7 +285,7 @@ int pa_scache_remove_item(pa_core *c, const char *name) {
return 0;
}
-static void free_cb(void *p, PA_GCC_UNUSED void *userdata) {
+static void free_cb(void *p, void *userdata) {
pa_scache_entry *e = p;
pa_assert(e);
@@ -285,26 +304,30 @@ 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);
pa_assert(sink);
- if (!(e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE, 1)))
+ if (!(e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE, FALSE)))
return -1;
if (e->lazy && !e->memchunk.memblock) {
+ pa_channel_map old_channel_map = e->channel_map;
+
if (pa_sound_file_load(c->mempool, e->filename, &e->sample_spec, &e->channel_map, &e->memchunk) < 0)
return -1;
pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE|PA_SUBSCRIPTION_EVENT_CHANGE, e->index);
- if (e->volume.channels > e->sample_spec.channels)
- e->volume.channels = e->sample_spec.channels;
+ if (pa_cvolume_valid(&e->volume))
+ pa_cvolume_remap(&e->volume, &old_channel_map, &e->channel_map);
+ else
+ pa_cvolume_reset(&e->volume, e->sample_spec.channels);
}
if (!e->memchunk.memblock)
@@ -312,17 +335,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);
@@ -330,7 +360,7 @@ 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);
@@ -339,10 +369,10 @@ int pa_scache_play_item_by_name(pa_core *c, const char *name, const char*sink_na
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);
@@ -360,15 +390,16 @@ uint32_t pa_scache_get_id_by_name(pa_core *c, const char *name) {
pa_assert(c);
pa_assert(name);
- if (!(e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE, 0)))
+ if (!(e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE, FALSE)))
return PA_IDXSET_INVALID;
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);
@@ -403,8 +434,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);
}
@@ -467,8 +497,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 7ec76e4..80e0fd0 100644
--- a/src/pulsecore/core-scache.h
+++ b/src/pulsecore/core-scache.h
@@ -1,8 +1,6 @@
#ifndef foocorescachehfoo
#define foocorescachehfoo
-/* $Id: core-scache.h 1971 2007-10-28 19:13:50Z lennart $ */
-
/***
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 6b54853..c70d8ad 100644
--- a/src/pulsecore/core-subscribe.c
+++ b/src/pulsecore/core-subscribe.c
@@ -1,5 +1,3 @@
-/* $Id: core-subscribe.c 1971 2007-10-28 19:13:50Z lennart $ */
-
/***
This file is part of PulseAudio.
@@ -44,7 +42,7 @@
struct pa_subscription {
pa_core *core;
- int dead;
+ pa_bool_t dead;
pa_subscription_cb_t callback;
void *userdata;
@@ -74,7 +72,7 @@ pa_subscription* pa_subscription_new(pa_core *c, pa_subscription_mask_t m, pa_su
s = pa_xnew(pa_subscription, 1);
s->core = c;
- s->dead = 0;
+ s->dead = FALSE;
s->callback = callback;
s->userdata = userdata;
s->mask = m;
@@ -88,7 +86,7 @@ void pa_subscription_free(pa_subscription*s) {
pa_assert(s);
pa_assert(!s->dead);
- s->dead = 1;
+ s->dead = TRUE;
sched_event(s->core);
}
@@ -147,7 +145,7 @@ static void dump_event(const char * prefix, pa_subscription_event*e) {
[PA_SUBSCRIPTION_EVENT_REMOVE] = "REMOVE"
};
- pa_log("%s event (%s|%s|%u)",
+ pa_log_debug("%s event (%s|%s|%u)",
prefix,
fac_table[e->type & PA_SUBSCRIPTION_EVENT_FACILITY_MASK],
type_table[e->type & PA_SUBSCRIPTION_EVENT_TYPE_MASK],
@@ -236,7 +234,7 @@ void pa_subscription_post(pa_core *c, pa_subscription_event_type_t t, uint32_t i
* entry in the queue. */
free_event(i);
- pa_log_debug("dropped redundant event.");
+ pa_log_debug("Dropped redundant event due to remove event.");
continue;
}
@@ -244,7 +242,7 @@ void pa_subscription_post(pa_core *c, pa_subscription_event_type_t t, uint32_t i
/* This object has changed. If a "new" or "change" event for
* this object is still in the queue we can exit. */
- pa_log_debug("dropped redundant event.");
+ pa_log_debug("Dropped redundant event due to change event.");
return;
}
}
diff --git a/src/pulsecore/core-subscribe.h b/src/pulsecore/core-subscribe.h
index c043f46..2f9730d 100644
--- a/src/pulsecore/core-subscribe.h
+++ b/src/pulsecore/core-subscribe.h
@@ -1,8 +1,6 @@
#ifndef foocoresubscribehfoo
#define foocoresubscribehfoo
-/* $Id: core-subscribe.h 1426 2007-02-13 15:35:19Z ossman $ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/core-util.c b/src/pulsecore/core-util.c
index 97c591d..dde34d7 100644
--- a/src/pulsecore/core-util.c
+++ b/src/pulsecore/core-util.c
@@ -1,5 +1,3 @@
-/* $Id: core-util.c 2137 2008-03-27 21:20:07Z lennart $ */
-
/***
This file is part of PulseAudio.
@@ -41,6 +39,10 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
+#include <dirent.h>
+#include <regex.h>
+#include <langinfo.h>
+#include <sys/utsname.h>
#ifdef HAVE_STRTOF_L
#include <locale.h>
@@ -103,12 +105,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"
@@ -118,6 +114,8 @@ int pa_set_root(HANDLE handle) {
strcpy(library_path, PULSE_ROOTENV "=");
+ /* FIXME: Needs to set errno */
+
if (!GetModuleFileName(handle, library_path + sizeof(PULSE_ROOTENV), MAX_PATH))
return 0;
@@ -175,7 +173,7 @@ void pa_make_fd_cloexec(int fd) {
/** Creates a directory securely */
int pa_make_secure_dir(const char* dir, mode_t m, uid_t uid, gid_t gid) {
struct stat st;
- int r;
+ int r, saved_errno;
pa_assert(dir);
@@ -184,7 +182,7 @@ int pa_make_secure_dir(const char* dir, mode_t m, uid_t uid, gid_t gid) {
#else
{
mode_t u;
- u = umask(~m);
+ u = umask((~m) & 0777);
r = mkdir(dir, m);
umask(u);
}
@@ -221,13 +219,16 @@ 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;
fail:
+ saved_errno = errno;
rmdir(dir);
+ errno = saved_errno;
+
return -1;
}
@@ -237,6 +238,7 @@ char *pa_parent_dir(const char *fn) {
if ((slash = (char*) pa_path_get_filename(dir)) == dir) {
pa_xfree(dir);
+ errno = ENOENT;
return NULL;
}
@@ -344,7 +346,7 @@ ssize_t pa_loop_read(int fd, void*data, size_t size, int *type) {
ret += r;
data = (uint8_t*) data + r;
- size -= r;
+ size -= (size_t) r;
}
return ret;
@@ -375,7 +377,7 @@ ssize_t pa_loop_write(int fd, const void*data, size_t size, int *type) {
ret += r;
data = (const uint8_t*) data + r;
- size -= r;
+ size -= (size_t) r;
}
return ret;
@@ -397,7 +399,15 @@ int pa_close(int fd) {
}
#endif
- return close(fd);
+ for (;;) {
+ int r;
+
+ if ((r = close(fd)) >= 0)
+ return r;
+
+ if (errno != EINTR)
+ return r;
+ }
}
/* Print a warning messages in case that the given signal is not
@@ -444,7 +454,7 @@ void pa_check_signal_is_blocked(int sig) {
/* The following function is based on an example from the GNU libc
* documentation. This function is similar to GNU's asprintf(). */
char *pa_sprintf_malloc(const char *format, ...) {
- int size = 100;
+ size_t size = 100;
char *c = NULL;
pa_assert(format);
@@ -461,11 +471,11 @@ char *pa_sprintf_malloc(const char *format, ...) {
c[size-1] = 0;
- if (r > -1 && r < size)
+ if (r > -1 && (size_t) r < size)
return c;
if (r > -1) /* glibc 2.1 */
- size = r+1;
+ size = (size_t) r+1;
else /* glibc 2.0 */
size *= 2;
}
@@ -474,7 +484,7 @@ char *pa_sprintf_malloc(const char *format, ...) {
/* Same as the previous function, but use a va_list instead of an
* ellipsis */
char *pa_vsprintf_malloc(const char *format, va_list ap) {
- int size = 100;
+ size_t size = 100;
char *c = NULL;
pa_assert(format);
@@ -491,11 +501,11 @@ char *pa_vsprintf_malloc(const char *format, va_list ap) {
c[size-1] = 0;
- if (r > -1 && r < size)
+ if (r > -1 && (size_t) r < size)
return c;
if (r > -1) /* glibc 2.1 */
- size = r+1;
+ size = (size_t) r+1;
else /* glibc 2.0 */
size *= 2;
}
@@ -553,10 +563,88 @@ int pa_make_realtime(int rtprio) {
pa_log_info("Successfully enabled SCHED_FIFO scheduling for thread, with priority %i.", sp.sched_priority);
return 0;
#else
+
+ errno = ENOTSUP;
return -1;
#endif
}
+/* This is merely used for giving the user a hint. This is not correct
+ * for anything security related */
+pa_bool_t pa_can_realtime(void) {
+
+ if (geteuid() == 0)
+ return TRUE;
+
+#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;
+
+ 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;
+}
+
+/* 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;
+
+ 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) {
@@ -584,6 +672,7 @@ int pa_raise_priority(int nice_level) {
if (nice_level < 0) {
if (!SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS)) {
pa_log_warn("SetPriorityClass() failed: 0x%08X", GetLastError());
+ errno = EPERM;
return .-1;
} else
pa_log_info("Successfully gained high priority class.");
@@ -610,14 +699,55 @@ void pa_reset_priority(void) {
#endif
}
+static int match(const char *expr, const char *v) {
+ int k;
+ regex_t re;
+ int r;
+
+ if (regcomp(&re, expr, REG_NOSUB|REG_EXTENDED) != 0) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if ((k = regexec(&re, v, 0, NULL, 0)) == 0)
+ r = 1;
+ else if (k == REG_NOMATCH)
+ r = 0;
+ else
+ r = -1;
+
+ regfree(&re);
+
+ if (r < 0)
+ errno = EINVAL;
+
+ return r;
+}
+
/* Try to parse a boolean string value.*/
int pa_parse_boolean(const char *v) {
+ const char *expr;
+ int r;
+ pa_assert(v);
+ /* First we check language independant */
if (!strcmp(v, "1") || v[0] == 'y' || v[0] == 'Y' || v[0] == 't' || v[0] == 'T' || !strcasecmp(v, "on"))
return 1;
else if (!strcmp(v, "0") || v[0] == 'n' || v[0] == 'N' || v[0] == 'f' || v[0] == 'F' || !strcasecmp(v, "off"))
return 0;
+ /* And then we check language dependant */
+ if ((expr = nl_langinfo(YESEXPR)))
+ if (expr[0])
+ if ((r = match(expr, v)) > 0)
+ return 1;
+
+ if ((expr = nl_langinfo(NOEXPR)))
+ if (expr[0])
+ if ((r = match(expr, v)) > 0)
+ return 0;
+
+ errno = EINVAL;
return -1;
}
@@ -805,11 +935,18 @@ static int is_group(gid_t gid, const char *name) {
#else
n = -1;
#endif
- if (n < 0) n = 512;
- data = pa_xmalloc(n);
+ if (n < 0)
+ n = 512;
+
+ data = pa_xmalloc((size_t) n);
+
+ errno = 0;
+ if (getgrgid_r(gid, &group, data, (size_t) n, &result) < 0 || !result) {
+ pa_log("getgrgid_r(%u): %s", (unsigned) gid, pa_cstrerror(errno));
+
+ if (!errno)
+ errno = ENOENT;
- if (getgrgid_r(gid, &group, data, n, &result) < 0 || !result) {
- pa_log("getgrgid_r(%u): %s", (unsigned)gid, pa_cstrerror(errno));
goto finish;
}
@@ -820,8 +957,14 @@ finish:
#else
/* XXX Not thread-safe, but needed on OSes (e.g. FreeBSD 4.X) that do not
* support getgrgid_r. */
+
+ errno = 0;
if ((result = getgrgid(gid)) == NULL) {
pa_log("getgrgid(%u): %s", gid, pa_cstrerror(errno));
+
+ if (!errno)
+ errno = ENOENT;
+
goto finish;
}
@@ -836,27 +979,32 @@ finish:
/* Check the current user is member of the specified group */
int pa_own_uid_in_group(const char *name, gid_t *gid) {
GETGROUPS_T *gids, tgid;
- int n = sysconf(_SC_NGROUPS_MAX);
- int r = -1, i;
+ long n = sysconf(_SC_NGROUPS_MAX);
+ int r = -1, i, k;
pa_assert(n > 0);
- gids = pa_xmalloc(sizeof(GETGROUPS_T)*n);
+ gids = pa_xmalloc(sizeof(GETGROUPS_T) * (size_t) n);
- if ((n = getgroups(n, gids)) < 0) {
+ if ((n = getgroups((int) n, gids)) < 0) {
pa_log("getgroups(): %s", pa_cstrerror(errno));
goto finish;
}
for (i = 0; i < n; i++) {
- if (is_group(gids[i], name) > 0) {
+
+ if ((k = is_group(gids[i], name)) < 0)
+ goto finish;
+ else if (k > 0) {
*gid = gids[i];
r = 1;
goto finish;
}
}
- if (is_group(tgid = getgid(), name) > 0) {
+ if ((k = is_group(tgid = getgid(), name)) < 0)
+ goto finish;
+ else if (k > 0) {
*gid = tgid;
r = 1;
goto finish;
@@ -879,18 +1027,25 @@ int pa_uid_in_group(uid_t uid, const char *name) {
int r = -1;
g_n = sysconf(_SC_GETGR_R_SIZE_MAX);
- g_buf = pa_xmalloc(g_n);
+ g_buf = pa_xmalloc((size_t) g_n);
p_n = sysconf(_SC_GETPW_R_SIZE_MAX);
- p_buf = pa_xmalloc(p_n);
+ p_buf = pa_xmalloc((size_t) p_n);
+
+ errno = 0;
+ if (getgrnam_r(name, &grbuf, g_buf, (size_t) g_n, &gr) != 0 || !gr) {
+
+ if (!errno)
+ errno = ENOENT;
- if (getgrnam_r(name, &grbuf, g_buf, (size_t) g_n, &gr) != 0 || !gr)
goto finish;
+ }
r = 0;
for (i = gr->gr_mem; *i; i++) {
struct passwd pwbuf, *pw;
+ errno = 0;
if (getpwnam_r(*i, &pwbuf, p_buf, (size_t) p_n, &pw) != 0 || !pw)
continue;
@@ -915,10 +1070,16 @@ gid_t pa_get_gid_of_group(const char *name) {
struct group grbuf, *gr;
g_n = sysconf(_SC_GETGR_R_SIZE_MAX);
- g_buf = pa_xmalloc(g_n);
+ g_buf = pa_xmalloc((size_t) g_n);
+
+ errno = 0;
+ if (getgrnam_r(name, &grbuf, g_buf, (size_t) g_n, &gr) != 0 || !gr) {
+
+ if (!errno)
+ errno = ENOENT;
- if (getgrnam_r(name, &grbuf, g_buf, (size_t) g_n, &gr) != 0 || !gr)
goto finish;
+ }
ret = gr->gr_gid;
@@ -944,19 +1105,23 @@ int pa_check_in_group(gid_t g) {
#else /* HAVE_GRP_H */
int pa_own_uid_in_group(const char *name, gid_t *gid) {
+ errno = ENOSUP;
return -1;
}
int pa_uid_in_group(uid_t uid, const char *name) {
+ errno = ENOSUP;
return -1;
}
gid_t pa_get_gid_of_group(const char *name) {
+ errno = ENOSUP;
return (gid_t) -1;
}
int pa_check_in_group(gid_t g) {
+ errno = ENOSUP;
return -1;
}
@@ -970,7 +1135,7 @@ int pa_lock_fd(int fd, int b) {
/* Try a R/W lock first */
- flock.l_type = b ? F_WRLCK : F_UNLCK;
+ flock.l_type = (short) (b ? F_WRLCK : F_UNLCK);
flock.l_whence = SEEK_SET;
flock.l_start = 0;
flock.l_len = 0;
@@ -997,6 +1162,8 @@ int pa_lock_fd(int fd, int b) {
return 0;
pa_log("%slock failed: 0x%08X", !b ? "un" : "", GetLastError());
+
+ /* FIXME: Needs to set errno! */
#endif
return -1;
@@ -1063,8 +1230,11 @@ int pa_lock_lockfile(const char *fn) {
fail:
- if (fd >= 0)
+ if (fd >= 0) {
+ int saved_errno = errno;
pa_close(fd);
+ errno = saved_errno;
+ }
return -1;
}
@@ -1072,12 +1242,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) {
@@ -1093,16 +1264,272 @@ int pa_unlock_lockfile(const char *fn, int fd) {
return r;
}
+static char *get_pulse_home(void) {
+ 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.", h);
+ errno = EACCES;
+ return NULL;
+ }
+
+ return pa_sprintf_malloc("%s" PA_PATH_SEP ".pulse", h);
+}
+
+char *pa_get_state_dir(void) {
+ char *d;
+
+ /* The state directory shall contain dynamic data that should be
+ * kept across reboots, and is private to this user */
+
+ if (!(d = pa_xstrdup(getenv("PULSE_STATE_PATH"))))
+ if (!(d = get_pulse_home()))
+ return NULL;
+
+ /* If PULSE_STATE_PATH and PULSE_RUNTIME_PATH point to the same
+ * dir then this will break. */
+
+ if (pa_make_secure_dir(d, 0700U, (uid_t) -1, (gid_t) -1) < 0) {
+ pa_log_error("Failed to create secure directory: %s", pa_cstrerror(errno));
+ pa_xfree(d);
+ return NULL;
+ }
+
+ return d;
+}
+
+static char* make_random_dir(mode_t m) {
+ static const char table[] =
+ "abcdefghijklmnopqrstuvwxyz"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "0123456789";
+
+ const char *tmpdir;
+ char *fn;
+ size_t pathlen;
+
+ if (!(tmpdir = getenv("TMPDIR")))
+ if (!(tmpdir = getenv("TMP")))
+ if (!(tmpdir = getenv("TEMP")))
+ tmpdir = getenv("TEMPDIR");
+
+ if (!tmpdir || !pa_is_path_absolute(tmpdir))
+ tmpdir = "/tmp";
+
+ fn = pa_sprintf_malloc("%s/pulse-XXXXXXXXXXXX", tmpdir);
+ pathlen = strlen(fn);
+
+ for (;;) {
+ size_t i;
+ int r;
+ mode_t u;
+ int saved_errno;
+
+ for (i = pathlen - 12; i < pathlen; i++)
+ fn[i] = table[rand() % (sizeof(table)-1)];
+
+ u = umask((~m) & 0777);
+ r = mkdir(fn, m);
+
+ saved_errno = errno;
+ umask(u);
+ errno = saved_errno;
+
+ if (r >= 0)
+ return fn;
+
+ if (errno != EEXIST) {
+ pa_log_error("Failed to create random directory %s: %s", fn, pa_cstrerror(errno));
+ pa_xfree(fn);
+ return NULL;
+ }
+ }
+}
+
+static int make_random_dir_and_link(mode_t m, const char *k) {
+ char *p;
+
+ if (!(p = make_random_dir(m)))
+ return -1;
+
+ if (symlink(p, k) < 0) {
+ int saved_errno = errno;
+
+ if (errno != EEXIST)
+ pa_log_error("Failed to symlink %s to %s: %s", k, p, pa_cstrerror(errno));
+
+ rmdir(p);
+ pa_xfree(p);
+
+ errno = saved_errno;
+ return -1;
+ }
+
+ return 0;
+}
+
+char *pa_get_runtime_dir(void) {
+ char *d, *k = NULL, *p = NULL, *t = NULL, *mid;
+ struct stat st;
+ mode_t m;
+
+ /* The runtime directory shall contain dynamic data that needs NOT
+ * to be kept accross reboots and is usuallly private to the user,
+ * except in system mode, where it might be accessible by other
+ * users, too. Since we need POSIX locking and UNIX sockets in
+ * this directory, we link it to a random subdir in /tmp, if it
+ * was not explicitly configured. */
+
+ m = pa_in_system_mode() ? 0755U : 0700U;
+
+ if ((d = getenv("PULSE_RUNTIME_PATH"))) {
+
+ if (pa_make_secure_dir(d, m, (uid_t) -1, (gid_t) -1) < 0) {
+ pa_log_error("Failed to create secure directory: %s", pa_cstrerror(errno));
+ goto fail;
+ }
+
+ return pa_xstrdup(d);
+ }
+
+ if (!(d = get_pulse_home()))
+ goto fail;
+
+ if (pa_make_secure_dir(d, m, (uid_t) -1, (gid_t) -1) < 0) {
+ pa_log_error("Failed to create secure directory: %s", pa_cstrerror(errno));
+ goto fail;
+ }
+
+ if (!(mid = pa_machine_id())) {
+ pa_xfree(d);
+ goto fail;
+ }
+
+ k = pa_sprintf_malloc("%s" PA_PATH_SEP "%s:runtime", d, mid);
+ pa_xfree(d);
+ pa_xfree(mid);
+
+ for (;;) {
+ /* OK, first let's check if the "runtime" symlink is already
+ * existant */
+
+ if (!(p = pa_readlink(k))) {
+
+ if (errno != ENOENT) {
+ pa_log_error("Failed to stat runtime directory %s: %s", k, pa_cstrerror(errno));
+ goto fail;
+ }
+
+ /* Hmm, so the runtime directory didn't exist yet, so let's
+ * create one in /tmp and symlink that to it */
+
+ if (make_random_dir_and_link(0700, k) < 0) {
+
+ /* Mhmm, maybe another process was quicker than us,
+ * let's check if that was valid */
+ if (errno == EEXIST)
+ continue;
+
+ goto fail;
+ }
+
+ return k;
+ }
+
+ /* Make sure that this actually makes sense */
+ if (!pa_is_path_absolute(p)) {
+ pa_log_error("Path %s in link %s is not absolute.", p, k);
+ errno = ENOENT;
+ goto fail;
+ }
+
+ /* Hmm, so this symlink is still around, make sure nobody fools
+ * us */
+
+ if (lstat(p, &st) < 0) {
+
+ if (errno != ENOENT) {
+ pa_log_error("Failed to stat runtime directory %s: %s", p, pa_cstrerror(errno));
+ goto fail;
+ }
+
+ } else {
+
+ if (S_ISDIR(st.st_mode) &&
+ (st.st_uid == getuid()) &&
+ ((st.st_mode & 0777) == 0700)) {
+
+ pa_xfree(p);
+ return k;
+ }
+
+ pa_log_info("Hmm, runtime path exists, but points to an invalid directory. Changing runtime directory.");
+ }
+
+ pa_xfree(p);
+ p = NULL;
+
+ /* Hmm, so the link points to some nonexisting or invalid
+ * dir. Let's replace it by a new link. We first create a
+ * temporary link and then rename that to allow concurrent
+ * execution of this function. */
+
+ t = pa_sprintf_malloc("%s.tmp", k);
+
+ if (make_random_dir_and_link(0700, t) < 0) {
+
+ if (errno != EEXIST) {
+ pa_log_error("Failed to symlink %s: %s", t, pa_cstrerror(errno));
+ goto fail;
+ }
+
+ pa_xfree(t);
+ t = NULL;
+
+ /* Hmm, someone lese was quicker then us. Let's give
+ * him some time to finish, and retry. */
+ pa_msleep(10);
+ continue;
+ }
+
+ /* OK, we succeeded in creating the temporary symlink, so
+ * let's rename it */
+ if (rename(t, k) < 0) {
+ pa_log_error("Failed to rename %s to %s: %s", t, k, pa_cstrerror(errno));
+ goto fail;
+ }
+
+ pa_xfree(t);
+ return k;
+ }
+
+fail:
+ pa_xfree(p);
+ pa_xfree(k);
+ pa_xfree(t);
+
+ return NULL;
+}
+
/* 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];
@@ -1111,75 +1538,164 @@ 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))
+ /* FIXME: Needs to set errno! */
return NULL;
fn = buf;
#endif
- if (result)
- *result = pa_xstrdup(fn);
+ if ((f = fopen(fn, "r"))) {
+ if (result)
+ *result = pa_xstrdup(fn);
+
+ return f;
+ }
- return fopen(fn, mode);
+ 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);
- else if (pa_get_home_dir(h, sizeof(h))) {
- char *d;
+ 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);
+ else
+ return NULL;
+
+#ifdef OS_IS_WIN32
+ if (!ExpandEnvironmentStrings(lfn, buf, PATH_MAX)) {
+ /* FIXME: Needs to set errno! */
+ pa_xfree(lfn);
+ return NULL;
+ }
+ fn = buf;
+#endif
- d = pa_sprintf_malloc("%s/.pulse", h);
- mkdir(d, 0755);
- pa_xfree(d);
+ if ((f = fopen(fn, "r"))) {
+ if (result)
+ *result = pa_xstrdup(fn);
- fn = lfn = pa_sprintf_malloc("%s/.pulse/%s", h, local);
+ pa_xfree(lfn);
+ return f;
}
- if (lfn) {
- FILE *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) {
+ FILE *f;
#ifdef OS_IS_WIN32
- if (!ExpandEnvironmentStrings(lfn, buf, PATH_MAX))
- return NULL;
- fn = buf;
+ if (!ExpandEnvironmentStrings(global, buf, PATH_MAX))
+ /* FIXME: Needs to set errno! */
+ return NULL;
+ global = buf;
#endif
- f = fopen(fn, mode);
- if (f != NULL) {
- if (result)
- *result = pa_xstrdup(fn);
- pa_xfree(lfn);
- return f;
- }
+ if ((f = fopen(global, "r"))) {
- if (errno != ENOENT)
- pa_log_warn("Failed to open configuration file '%s': %s", lfn, pa_cstrerror(errno));
+ if (result)
+ *result = pa_xstrdup(global);
- pa_xfree(lfn);
+ return f;
}
}
- if (!global) {
- if (result)
- *result = NULL;
- errno = ENOENT;
+ 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))
+ /* FIXME: Needs to set errno! */
+ 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);
+ else
+ return NULL;
+
+#ifdef OS_IS_WIN32
+ if (!ExpandEnvironmentStrings(lfn, buf, PATH_MAX)) {
+ /* FIXME: Needs to set errno! */
+ pa_xfree(lfn);
+ return NULL;
+ }
+ fn = buf;
+#endif
+
+ 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;
+ if (!ExpandEnvironmentStrings(global, buf, PATH_MAX))
+ /* FIXME: Needs to set errno! */
+ return NULL;
+ global = buf;
#endif
- if (result)
- *result = pa_xstrdup(global);
+ if (access(global, R_OK) == 0)
+ return pa_xstrdup(global);
+ }
+
+ errno = ENOENT;
- return fopen(global, mode);
+ return NULL;
}
/* Format the specified data as a hexademical string */
@@ -1214,6 +1730,7 @@ static int hexc(char c) {
if (c >= 'a' && c <= 'f')
return c - 'a' + 10;
+ errno = EINVAL;
return -1;
}
@@ -1246,7 +1763,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);
@@ -1258,7 +1775,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);
@@ -1270,45 +1787,75 @@ 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;
+}
-#ifdef OS_IS_WIN32
- {
- char buf[l];
- strcpy(buf, s);
- ExpandEnvironmentStrings(buf, s, l);
- }
-#endif
+/* 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 prependmid, pa_bool_t rt) {
+ char *rtp;
- return s;
+ if (pa_is_path_absolute(fn))
+ return pa_xstrdup(fn);
+
+ rtp = rt ? pa_get_runtime_dir() : pa_get_state_dir();
+
+ if (!rtp)
+ return NULL;
+
+ if (fn) {
+ char *r;
+
+ if (prependmid) {
+ char *mid;
+
+ if (!(mid = pa_machine_id())) {
+ pa_xfree(rtp);
+ return NULL;
+ }
+
+ r = pa_sprintf_malloc("%s" PA_PATH_SEP "%s:%s", rtp, mid, fn);
+ pa_xfree(mid);
+ } else
+ 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, FALSE, TRUE);
+}
+
+char *pa_state_path(const char *fn, pa_bool_t appendmid) {
+ return get_path(fn, appendmid, FALSE);
}
/* Convert the string s to a signed integer in *ret_i */
@@ -1322,11 +1869,16 @@ int pa_atoi(const char *s, int32_t *ret_i) {
errno = 0;
l = strtol(s, &x, 0);
- if (!x || *x || errno != 0)
+ if (!x || *x || errno) {
+ if (!errno)
+ errno = EINVAL;
return -1;
+ }
- if ((int32_t) l != l)
+ if ((int32_t) l != l) {
+ errno = ERANGE;
return -1;
+ }
*ret_i = (int32_t) l;
@@ -1344,11 +1896,16 @@ int pa_atou(const char *s, uint32_t *ret_u) {
errno = 0;
l = strtoul(s, &x, 0);
- if (!x || *x || errno != 0)
+ if (!x || *x || errno) {
+ if (!errno)
+ errno = EINVAL;
return -1;
+ }
- if ((uint32_t) l != l)
+ if ((uint32_t) l != l) {
+ errno = ERANGE;
return -1;
+ }
*ret_u = (uint32_t) l;
@@ -1363,13 +1920,12 @@ 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;
- int r = 0;
+ double f;
pa_assert(s);
- pa_assert(ret_f);
+ pa_assert(ret_d);
/* This should be locale independent */
@@ -1384,29 +1940,28 @@ 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;
+ if (!x || *x || errno) {
+ if (!errno)
+ errno = EINVAL;
+ return -1;
+ }
- return r;
+ *ret_d = f;
+
+ return 0;
}
/* Same as snprintf, but guarantees NUL-termination on every platform */
-int pa_snprintf(char *str, size_t size, const char *format, ...) {
- int ret;
+size_t pa_snprintf(char *str, size_t size, const char *format, ...) {
+ size_t ret;
va_list ap;
pa_assert(str);
@@ -1414,12 +1969,31 @@ 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 */
+size_t 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)
+ return strlen(str);
+
+ if ((size_t) ret > size-1)
+ return size-1;
+
+ return (size_t) ret;
}
/* Truncate the specified string, but guarantee that the string
@@ -1443,7 +2017,7 @@ char *pa_getcwd(void) {
size_t l = 128;
for (;;) {
- char *p = pa_xnew(char, l);
+ char *p = pa_xmalloc(l);
if (getcwd(p, l))
return p;
@@ -1455,23 +2029,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;
@@ -1485,7 +2042,7 @@ void *pa_will_need(const void *p, size_t l) {
pa_assert(l > 0);
a = PA_PAGE_ALIGN_PTR(p);
- size = (const uint8_t*) p + l - (const uint8_t*) a;
+ size = (size_t) ((const uint8_t*) p + l - (const uint8_t*) a);
#ifdef HAVE_POSIX_MADVISE
if ((r = posix_madvise((void*) a, size, POSIX_MADV_WILLNEED)) == 0) {
@@ -1506,10 +2063,11 @@ void *pa_will_need(const void *p, size_t l) {
if (rlim.rlim_cur < PA_PAGE_SIZE) {
pa_log_debug("posix_madvise() failed (or doesn't exist), resource limits don't allow mlock(), can't page in data: %s", pa_cstrerror(r));
+ errno = EPERM;
return (void*) p;
}
- bs = PA_PAGE_ALIGN(rlim.rlim_cur);
+ bs = PA_PAGE_ALIGN((size_t) rlim.rlim_cur);
#else
bs = PA_PAGE_SIZE*4;
#endif
@@ -1561,7 +2119,7 @@ char *pa_readlink(const char *p) {
char *c;
ssize_t n;
- c = pa_xnew(char, l);
+ c = pa_xmalloc(l);
if ((n = readlink(p, c, l-1)) < 0) {
pa_xfree(c);
@@ -1577,3 +2135,355 @@ char *pa_readlink(const char *p) {
l *= 2;
}
}
+
+int pa_close_all(int except_fd, ...) {
+ va_list ap;
+ unsigned n = 0, i;
+ int r, *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 = 3; fd < (int) rl.rlim_max; fd++) {
+ int i;
+ pa_bool_t found;
+
+ 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 && errno != EBADF)
+ return -1;
+ }
+
+ return 0;
+}
+
+int pa_unblock_sigs(int except, ...) {
+ va_list ap;
+ unsigned n = 0, i;
+ int r, *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;
+ unsigned n = 0, i;
+ int *p, r;
+
+ 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)
+ sig = p[i++];
+ }
+ 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);
+}
+
+char *pa_machine_id(void) {
+ FILE *f;
+ size_t l;
+
+ /* The returned value is supposed be some kind of ascii identifier
+ * that is unique and stable across reboots. */
+
+ /* First we try the D-Bus UUID, which is the best option we have,
+ * since it fits perfectly our needs and is not as volatile as the
+ * hostname which might be set from dhcp. */
+
+ if ((f = fopen(PA_MACHINE_ID, "r"))) {
+ char ln[34] = "", *r;
+
+ r = fgets(ln, sizeof(ln)-1, f);
+ fclose(f);
+
+ pa_strip_nl(ln);
+
+ if (ln[0])
+ return pa_xstrdup(ln);
+ }
+
+ /* The we fall back to the host name. It supposed to be somewhat
+ * unique, at least in a network, but may change. */
+
+ l = 100;
+ for (;;) {
+ char *c;
+
+ c = pa_xmalloc(l);
+
+ if (!pa_get_host_name(c, l)) {
+
+ if (errno != EINVAL && errno != ENAMETOOLONG)
+ break;
+
+ } else if (strlen(c) < l-1) {
+
+ if (*c == 0) {
+ pa_xfree(c);
+ break;
+ }
+
+ return c;
+ }
+
+ /* Hmm, the hostname is as long the space we offered the
+ * function, we cannot know if it fully fit in, so let's play
+ * safe and retry. */
+
+ pa_xfree(c);
+ l *= 2;
+ }
+
+ /* If no hostname was set we use the POSIX hostid. It's usually
+ * the IPv4 address. Mit not be that stable. */
+ return pa_sprintf_malloc("%08lx", (unsigned long) gethostid);
+}
+
+char *pa_uname_string(void) {
+ struct utsname u;
+
+ pa_assert_se(uname(&u) == 0);
+
+ return pa_sprintf_malloc("%s %s %s %s", u.sysname, u.machine, u.release, u.version);
+}
+
+#ifdef HAVE_VALGRIND_MEMCHECK_H
+pa_bool_t pa_in_valgrind(void) {
+ static int b = 0;
+
+ /* To make heisenbugs a bit simpler to find we check for $VALGRIND
+ * here instead of really checking whether we run in valgrind or
+ * not. */
+
+ if (b < 1)
+ b = getenv("VALGRIND") ? 2 : 1;
+
+ return b > 1;
+}
+#endif
diff --git a/src/pulsecore/core-util.h b/src/pulsecore/core-util.h
index fd5e80c..fd6ee89 100644
--- a/src/pulsecore/core-util.h
+++ b/src/pulsecore/core-util.h
@@ -1,8 +1,6 @@
#ifndef foocoreutilhfoo
#define foocoreutilhfoo
-/* $Id: core-util.h 2014 2007-11-01 02:58:26Z lennart $ */
-
/***
This file is part of PulseAudio.
@@ -30,11 +28,31 @@
#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>
+#ifndef PACKAGE
+#error "Please include config.h before including this file!"
+#endif
+
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);
@@ -61,12 +79,23 @@ 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);
@@ -84,26 +113,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;
-char *pa_runtime_path(const char *fn, char *s, size_t l);
+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_get_runtime_dir(void);
+char *pa_get_state_dir(void);
+char *pa_runtime_path(const char *fn);
+char *pa_state_path(const char *fn, pa_bool_t prepend_machine_id);
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, ...);
+size_t pa_snprintf(char *str, size_t size, const char *format, ...);
+size_t 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,22 +146,64 @@ static inline int pa_is_power_of_two(unsigned n) {
return !(n & (n - 1));
}
+static inline unsigned pa_ulog2(unsigned n) {
+
+ if (n <= 1)
+ return 0;
+
+#if __GNUC__ >= 4 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
+ return 8U * (unsigned) sizeof(unsigned) - (unsigned) __builtin_clz(n) - 1;
+#else
+{
+ unsigned r = 0;
+
+ for (;;) {
+ n = n >> 1;
+
+ if (!n)
+ return r;
+
+ r++;
+ }
+}
+#endif
+}
+
static inline unsigned pa_make_power_of_two(unsigned n) {
- unsigned j = n;
if (pa_is_power_of_two(n))
return n;
- while (j) {
- j = j >> 1;
- n = n | j;
- }
-
- return n + 1;
+ return 1U << (pa_ulog2(n) + 1);
}
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)))
+
+char *pa_machine_id(void);
+char *pa_uname_string(void);
+
+
+#ifdef HAVE_VALGRIND_MEMCHECK_H
+pa_bool_t pa_in_valgrind(void);
+#else
+static inline pa_bool_t pa_in_valgrind(void) {
+ return FALSE;
+}
+#endif
+
#endif
diff --git a/src/pulsecore/core.c b/src/pulsecore/core.c
index 84131de..5761bbc 100644
--- a/src/pulsecore/core.c
+++ b/src/pulsecore/core.c
@@ -1,5 +1,3 @@
-/* $Id: core.c 2067 2007-11-21 01:30:40Z lennart $ */
-
/***
This file is part of PulseAudio.
@@ -41,7 +39,7 @@
#include <pulsecore/core-scache.h>
#include <pulsecore/autoload.h>
#include <pulsecore/core-subscribe.h>
-#include <pulsecore/props.h>
+#include <pulsecore/shared.h>
#include <pulsecore/random.h>
#include <pulsecore/log.h>
#include <pulsecore/macro.h>
@@ -58,7 +56,7 @@ static int core_process_msg(pa_msgobject *o, int code, void *userdata, int64_t o
switch (code) {
case PA_CORE_MESSAGE_UNLOAD_MODULE:
- pa_module_unload(c, userdata);
+ pa_module_unload(c, userdata, TRUE);
return 0;
default:
@@ -68,7 +66,7 @@ static int core_process_msg(pa_msgobject *o, int code, void *userdata, int64_t o
static void core_free(pa_object *o);
-pa_core* pa_core_new(pa_mainloop_api *m, int shared) {
+pa_core* pa_core_new(pa_mainloop_api *m, pa_bool_t shared, size_t shm_size) {
pa_core* c;
pa_mempool *pool;
int j;
@@ -76,14 +74,14 @@ pa_core* pa_core_new(pa_mainloop_api *m, int shared) {
pa_assert(m);
if (shared) {
- if (!(pool = pa_mempool_new(shared))) {
+ if (!(pool = pa_mempool_new(shared, shm_size))) {
pa_log_warn("failed to allocate shared memory pool. Falling back to a normal memory pool.");
- shared = 0;
+ shared = FALSE;
}
}
if (!shared) {
- if (!(pool = pa_mempool_new(shared))) {
+ if (!(pool = pa_mempool_new(shared, shm_size))) {
pa_log("pa_mempool_new() failed.");
return NULL;
}
@@ -125,8 +123,9 @@ 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;
+ c->exit_event = NULL;
c->exit_idle_time = -1;
c->module_idle_time = 20;
@@ -134,16 +133,17 @@ pa_core* pa_core_new(pa_mainloop_api *m, int shared) {
c->resample_method = PA_RESAMPLER_SPEEX_FLOAT_BASE + 3;
- c->is_system_instance = FALSE;
c->disallow_module_loading = FALSE;
+ c->disallow_exit = FALSE;
c->realtime_scheduling = FALSE;
c->realtime_priority = 5;
c->disable_remixing = FALSE;
+ c->disable_lfe_remixing = FALSE;
for (j = 0; j < PA_CORE_HOOK_MAX; j++)
pa_hook_init(&c->hooks[j], c);
- pa_property_init(c);
+ pa_shared_init(c);
pa_random(&c->cookie, sizeof(c->cookie));
@@ -151,6 +151,8 @@ pa_core* pa_core_new(pa_mainloop_api *m, int shared) {
pa_check_signal_is_blocked(SIGPIPE);
#endif
+ pa_core_check_idle(c);
+
return c;
}
@@ -182,39 +184,56 @@ static void core_free(pa_object *o) {
pa_autoload_free(c);
pa_subscription_free_all(c);
- if (c->quit_event)
- c->mainloop->time_free(c->quit_event);
+ if (c->exit_event)
+ c->mainloop->time_free(c->exit_event);
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);
+ pa_shared_cleanup(c);
for (j = 0; j < PA_CORE_HOOK_MAX; j++)
- pa_hook_free(&c->hooks[j]);
+ pa_hook_done(&c->hooks[j]);
pa_xfree(c);
}
-static void quit_callback(pa_mainloop_api*m, pa_time_event *e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) {
+static void exit_callback(pa_mainloop_api*m, pa_time_event *e, const struct timeval *tv, void *userdata) {
pa_core *c = userdata;
- pa_assert(c->quit_event == e);
+ pa_assert(c->exit_event == e);
- m->quit(m, 0);
+ pa_log_info("We are idle, quitting...");
+ pa_core_exit(c, TRUE, 0);
}
-void pa_core_check_quit(pa_core *c) {
+void pa_core_check_idle(pa_core *c) {
pa_assert(c);
- if (!c->quit_event && c->exit_idle_time >= 0 && pa_idxset_size(c->clients) == 0) {
+ if (!c->exit_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;
+
+ c->exit_event = c->mainloop->time_new(c->mainloop, &tv, exit_callback, c);
+
+ } else if (c->exit_event && pa_idxset_size(c->clients) > 0) {
+ c->mainloop->time_free(c->exit_event);
+ c->exit_event = NULL;
}
}
+
+int pa_core_exit(pa_core *c, pa_bool_t force, int retval) {
+ pa_assert(c);
+
+ if (c->disallow_exit && !force)
+ return -1;
+
+ c->mainloop->quit(c->mainloop, retval);
+ return 0;
+}
diff --git a/src/pulsecore/core.h b/src/pulsecore/core.h
index d5bc10b..3955908 100644
--- a/src/pulsecore/core.h
+++ b/src/pulsecore/core.h
@@ -1,8 +1,6 @@
#ifndef foocorehfoo
#define foocorehfoo
-/* $Id: core.h 2067 2007-11-21 01:30:40Z lennart $ */
-
/***
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,16 +42,20 @@ 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,
@@ -60,8 +63,8 @@ typedef enum pa_core_hook {
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,
@@ -69,8 +72,8 @@ typedef enum pa_core_hook {
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;
@@ -91,7 +94,7 @@ struct pa_core {
pa_idxset *clients, *sinks, *sources, *sink_inputs, *source_outputs, *modules, *scache, *autoload_idxset;
/* Some hashmaps for all sorts of entities */
- pa_hashmap *namereg, *autoload_hashmap, *properties;
+ pa_hashmap *namereg, *autoload_hashmap, *shared;
/* The name of the default sink/source */
char *default_source_name, *default_sink_name;
@@ -108,19 +111,23 @@ 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;
- pa_time_event *quit_event;
+ pa_time_event *exit_event;
pa_time_event *scache_auto_unload_event;
- pa_bool_t disallow_module_loading, running_as_daemon;
+ pa_bool_t disallow_module_loading:1;
+ pa_bool_t disallow_exit:1;
+ pa_bool_t running_as_daemon:1;
+ pa_bool_t realtime_scheduling:1;
+ pa_bool_t disable_remixing:1;
+ pa_bool_t disable_lfe_remixing:1;
+
pa_resample_method_t resample_method;
- pa_bool_t is_system_instance;
- pa_bool_t realtime_scheduling;
int realtime_priority;
- pa_bool_t disable_remixing;
/* hooks */
pa_hook hooks[PA_CORE_HOOK_MAX];
@@ -134,9 +141,11 @@ enum {
PA_CORE_MESSAGE_MAX
};
-pa_core* pa_core_new(pa_mainloop_api *m, int shared);
+pa_core* pa_core_new(pa_mainloop_api *m, pa_bool_t shared, size_t shm_size);
/* Check whether noone is connected to this core */
-void pa_core_check_quit(pa_core *c);
+void pa_core_check_idle(pa_core *c);
+
+int pa_core_exit(pa_core *c, pa_bool_t force, int retval);
#endif
diff --git a/src/pulsecore/creds.h b/src/pulsecore/creds.h
index 34f3684..c15c469 100644
--- a/src/pulsecore/creds.h
+++ b/src/pulsecore/creds.h
@@ -1,8 +1,6 @@
#ifndef foocredshfoo
#define foocredshfoo
-/* $Id: creds.h 1971 2007-10-28 19:13:50Z lennart $ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/dllmain.c b/src/pulsecore/dllmain.c
index 5e80cb7..269de60 100644
--- a/src/pulsecore/dllmain.c
+++ b/src/pulsecore/dllmain.c
@@ -1,5 +1,3 @@
-/* $Id: dllmain.c 1426 2007-02-13 15:35:19Z ossman $ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/dynarray.c b/src/pulsecore/dynarray.c
index c5d2ef8..69d835a 100644
--- a/src/pulsecore/dynarray.c
+++ b/src/pulsecore/dynarray.c
@@ -1,9 +1,7 @@
-/* $Id: dynarray.c 1971 2007-10-28 19:13:50Z lennart $ */
-
/***
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
@@ -33,8 +31,8 @@
#include "dynarray.h"
-/* If the array becomes to small, increase its size by 100 entries */
-#define INCREASE_BY 100
+/* If the array becomes to small, increase its size by 25 entries */
+#define INCREASE_BY 25
struct pa_dynarray {
void **data;
@@ -43,21 +41,23 @@ struct pa_dynarray {
pa_dynarray* pa_dynarray_new(void) {
pa_dynarray *a;
+
a = pa_xnew(pa_dynarray, 1);
a->data = NULL;
a->n_entries = 0;
a->n_allocated = 0;
+
return a;
}
-void pa_dynarray_free(pa_dynarray* a, void (*func)(void *p, void *userdata), void *userdata) {
+void pa_dynarray_free(pa_dynarray* a, pa_free2_cb_t free_func, void *userdata) {
unsigned i;
pa_assert(a);
- if (func)
+ if (free_func)
for (i = 0; i < a->n_entries; i++)
if (a->data[i])
- func(a->data[i], userdata);
+ free_func(a->data[i], userdata);
pa_xfree(a->data);
pa_xfree(a);
@@ -91,6 +91,7 @@ unsigned pa_dynarray_append(pa_dynarray*a, void *p) {
i = a->n_entries;
pa_dynarray_put(a, i, p);
+
return i;
}
diff --git a/src/pulsecore/dynarray.h b/src/pulsecore/dynarray.h
index d726a7c..9a8e64e 100644
--- a/src/pulsecore/dynarray.h
+++ b/src/pulsecore/dynarray.h
@@ -1,12 +1,10 @@
-#ifndef foodynarrayhfoo
-#define foodynarrayhfoo
-
-/* $Id: dynarray.h 1426 2007-02-13 15:35:19Z ossman $ */
+#ifndef foopulsecoredynarrayhfoo
+#define foopulsecoredynarrayhfoo
/***
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
@@ -24,6 +22,8 @@
USA.
***/
+#include <pulsecore/idxset.h>
+
typedef struct pa_dynarray pa_dynarray;
/* Implementation of a simple dynamically sized array. The array
@@ -34,7 +34,7 @@ pa_dynarray* pa_dynarray_new(void);
/* Free the array calling the specified function for every entry in
* the array. The function may be NULL. */
-void pa_dynarray_free(pa_dynarray* a, void (*func)(void *p, void *userdata), void *userdata);
+void pa_dynarray_free(pa_dynarray* a, pa_free2_cb_t free_func, void *userdata);
/* Store p at position i in the array */
void pa_dynarray_put(pa_dynarray*a, unsigned i, void *p);
diff --git a/src/pulsecore/endianmacros.h b/src/pulsecore/endianmacros.h
index da2d1d5..1b94de1 100644
--- a/src/pulsecore/endianmacros.h
+++ b/src/pulsecore/endianmacros.h
@@ -1,8 +1,6 @@
#ifndef fooendianmacroshfoo
#define fooendianmacroshfoo
-/* $Id: endianmacros.h 2143 2008-03-27 21:24:03Z lennart $ */
-
/***
This file is part of PulseAudio.
@@ -48,9 +46,14 @@
#endif
static inline float PA_FLOAT32_SWAP(float x) {
- uint32_t i = *(uint32_t*) &x;
- i = PA_UINT32_SWAP(i);
- return *(float*) &i;
+ union {
+ float f;
+ uint32_t u;
+ } t;
+
+ t.f = x;
+ t.u = PA_UINT32_SWAP(t.u);
+ return t.f;
}
#define PA_MAYBE_INT16_SWAP(c,x) ((c) ? PA_INT32_SWAP(x) : x)
diff --git a/src/pulsecore/envelope.c b/src/pulsecore/envelope.c
new file mode 100644
index 0000000..7f2252e
--- /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) ((double) _y1 + (double) (x3 - x1) * (double) (y2 - _y1) / (double) (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 + ((float) x3 - (float) x1) * (y2 - _y1) / ((float) x2 - (float) 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]) /
+ ((float) e->points[v].x[e->points[v].n_current+1] - (float) 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] + (float) (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) {
+ int32_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) {
+ 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 k = st_ulaw2linear16(*t);
+ *t = (uint8_t) st_14linear2ulaw((int16_t) (((factor * k) / 0x10000) >> 2));
+ }
+ }
+
+ break;
+ }
+
+ case PA_SAMPLE_ALAW: {
+ uint8_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 k = st_alaw2linear16(*t);
+ *t = (uint8_t) st_13linear2alaw((int16_t) (((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 = (int16_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 = (int16_t) ((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 0000000..5296415
--- /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 0bfe88d..79322ae 100644
--- a/src/pulsecore/esound.h
+++ b/src/pulsecore/esound.h
@@ -1,8 +1,6 @@
#ifndef fooesoundhfoo
#define fooesoundhfoo
-/* $Id: esound.h 1971 2007-10-28 19:13:50Z lennart $ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/fdsem.c b/src/pulsecore/fdsem.c
index 59eec18..380f34f 100644
--- a/src/pulsecore/fdsem.c
+++ b/src/pulsecore/fdsem.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -43,56 +41,30 @@
#include <pulsecore/pipe.h>
#endif
-#ifdef __linux__
-
-#if !defined(__NR_eventfd) && defined(__i386__)
-#define __NR_eventfd 323
-#endif
-
-#if !defined(__NR_eventfd) && defined(__x86_64__)
-#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
-
-#ifdef SYS_eventfd
-#define HAVE_EVENTFD
-
-static inline long eventfd(unsigned count) {
- return syscall(SYS_eventfd, count);
-}
-
-#endif
+#ifdef HAVE_SYS_EVENTFD_H
+#include <sys/eventfd.h>
#endif
#include "fdsem.h"
struct pa_fdsem {
int fds[2];
-#ifdef HAVE_EVENTFD
+#ifdef HAVE_SYS_EVENTFD_H
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) {
+#ifdef HAVE_SYS_EVENTFD_H
+ if ((f->efd = eventfd(0, 0)) >= 0) {
pa_make_fd_cloexec(f->efd);
f->fds[0] = f->fds[1] = -1;
-
} else
#endif
{
@@ -105,9 +77,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_SYS_EVENTFD_H
+ 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_SYS_EVENTFD_H
+
+ f = pa_xnew(pa_fdsem, 1);
+
+ if ((f->efd = eventfd(0, 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;
}
@@ -115,7 +135,7 @@ pa_fdsem *pa_fdsem_new(void) {
void pa_fdsem_free(pa_fdsem *f) {
pa_assert(f);
-#ifdef HAVE_EVENTFD
+#ifdef HAVE_SYS_EVENTFD_H
if (f->efd >= 0)
pa_close(f->efd);
#endif
@@ -128,13 +148,13 @@ 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 {
char x[10];
-#ifdef HAVE_EVENTFD
+#ifdef HAVE_SYS_EVENTFD_H
if (f->efd >= 0) {
uint64_t u;
@@ -151,23 +171,23 @@ static void flush(pa_fdsem *f) {
continue;
}
- } while (pa_atomic_sub(&f->in_pipe, r) > r);
+ } while (pa_atomic_sub(&f->data->in_pipe, (int) r) > (int) 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 (;;) {
-#ifdef HAVE_EVENTFD
+#ifdef HAVE_SYS_EVENTFD_H
if (f->efd >= 0) {
uint64_t u = 1;
@@ -194,16 +214,16 @@ 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;
-#ifdef HAVE_EVENTFD
+#ifdef HAVE_SYS_EVENTFD_H
if (f->efd >= 0) {
uint64_t u;
@@ -221,10 +241,10 @@ void pa_fdsem_wait(pa_fdsem *f) {
continue;
}
- pa_atomic_sub(&f->in_pipe, r);
+ pa_atomic_sub(&f->data->in_pipe, (int) 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) {
@@ -232,7 +252,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;
@@ -241,7 +261,7 @@ int pa_fdsem_try(pa_fdsem *f) {
int pa_fdsem_get(pa_fdsem *f) {
pa_assert(f);
-#ifdef HAVE_EVENTFD
+#ifdef HAVE_SYS_EVENTFD_H
if (f->efd >= 0)
return f->efd;
#endif
@@ -254,13 +274,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;
@@ -269,11 +289,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 f38ef20..48a77c4 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/resample2.c b/src/pulsecore/ffmpeg/resample2.c
index da1443d..ed59448 100644
--- a/src/pulsecore/ffmpeg/resample2.c
+++ b/src/pulsecore/ffmpeg/resample2.c
@@ -208,7 +208,7 @@ void av_resample_close(AVResampleContext *c){
/**
* Compensates samplerate/timestamp drift. The compensation is done by changing
- * the resampler parameters, so no audible clicks or similar distortions ocur
+ * the resampler parameters, so no audible clicks or similar distortions occur
* @param compensation_distance distance in output samples over which the compensation should be performed
* @param sample_delta number of output samples which should be output less
*
@@ -231,7 +231,7 @@ void av_resample_compensate(AVResampleContext *c, int sample_delta, int compensa
* @param src_size the number of unconsumed samples available
* @param dst_size the amount of space in samples available in dst
* @param update_ctx if this is 0 then the context wont be modified, that way several channels can be resampled with the same context
- * @return the number of samples written in dst or -1 if an error occured
+ * @return the number of samples written in dst or -1 if an error occurred
*/
int av_resample(AVResampleContext *c, short *dst, short *src, int *consumed, int src_size, int dst_size, int update_ctx){
int dst_index, i;
diff --git a/src/pulsecore/flist.c b/src/pulsecore/flist.c
index c9271cd..6fb944f 100644
--- a/src/pulsecore/flist.c
+++ b/src/pulsecore/flist.c
@@ -1,9 +1,7 @@
-/* $Id: flist.c 1971 2007-10-28 19:13:50Z lennart $ */
-
/***
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
@@ -40,14 +38,8 @@
* from the flist although it isn't empty, and fail to push into the
* flist, although it isn't full.
*
- * We keep a fixed size array of entries, each item is either marked
- * UNUSED, USED or BUSY and contains a user data pointer. When pushing
- * into the queue we look for an UNUSED cell and mark it BUSY with a
- * CAS operation. If successful we use it and mark it USED, otherwise
- * we go on and look for the next UNUSED cell. The algorithm for
- * popping an item from the queue is practically inverse: look for a
- * USED cell and and mark it BUSY with a CAS operation, after reading
- * from it mark it UNUSED again.
+ * We keep a fixed size array of entries, each item is an atomic
+ * pointer.
*
* To accelerate finding of used/unused cells we maintain a read and a
* write index which is used like a ring buffer. After each push we
@@ -74,7 +66,7 @@
* Please note that this algorithm is home grown.*/
#define FLIST_SIZE 128
-#define N_EXTRA_SCAN 2
+#define N_EXTRA_SCAN 3
/* For debugging purposes we can define _Y to put and extra thread
* yield between each operation. */
@@ -85,17 +77,6 @@
#define _Y do { } while(0)
#endif
-enum {
- STATE_UNUSED,
- STATE_USED,
- STATE_BUSY
-};
-
-struct cell {
- pa_atomic_t state;
- void *data;
-};
-
struct pa_flist {
unsigned size;
pa_atomic_t length;
@@ -103,7 +84,7 @@ struct pa_flist {
pa_atomic_t write_idx;
};
-#define PA_FLIST_CELLS(x) ((struct cell*) ((uint8_t*) (x) + PA_ALIGN(sizeof(struct pa_flist))))
+#define PA_FLIST_CELLS(x) ((pa_atomic_ptr_t*) ((uint8_t*) (x) + PA_ALIGN(sizeof(struct pa_flist))))
pa_flist *pa_flist_new(unsigned size) {
pa_flist *l;
@@ -113,7 +94,7 @@ pa_flist *pa_flist_new(unsigned size) {
pa_assert(pa_is_power_of_two(size));
- l = pa_xmalloc0(PA_ALIGN(sizeof(pa_flist)) + (sizeof(struct cell) * size));
+ l = pa_xmalloc0(PA_ALIGN(sizeof(pa_flist)) + (sizeof(pa_atomic_ptr_t) * size));
l->size = size;
@@ -124,28 +105,24 @@ pa_flist *pa_flist_new(unsigned size) {
return l;
}
-static int reduce(pa_flist *l, int value) {
- return value & (unsigned) (l->size - 1);
+static unsigned reduce(pa_flist *l, unsigned value) {
+ return value & (l->size - 1);
}
void pa_flist_free(pa_flist *l, pa_free_cb_t free_cb) {
pa_assert(l);
if (free_cb) {
- struct cell *cells;
- int len, idx;
+ pa_atomic_ptr_t*cells;
+ unsigned idx;
cells = PA_FLIST_CELLS(l);
- idx = reduce(l, pa_atomic_load(&l->read_idx));
- len = pa_atomic_load(&l->length);
-
- for (; len > 0; len--) {
-
- if (pa_atomic_load(&cells[idx].state) == STATE_USED)
- free_cb(cells[idx].data);
+ for (idx = 0; idx < l->size; idx ++) {
+ void *p;
- idx = reduce(l, idx + 1);
+ if ((p = pa_atomic_ptr_load(&cells[idx])))
+ free_cb(p);
}
}
@@ -153,30 +130,31 @@ void pa_flist_free(pa_flist *l, pa_free_cb_t free_cb) {
}
int pa_flist_push(pa_flist*l, void *p) {
- int idx, len, n;
- struct cell *cells;
+ unsigned idx, n, len;
+ pa_atomic_ptr_t*cells;
pa_assert(l);
pa_assert(p);
cells = PA_FLIST_CELLS(l);
- n = len = (int) l->size - pa_atomic_load(&l->length) + N_EXTRA_SCAN;
+ n = len = l->size + N_EXTRA_SCAN - (unsigned) pa_atomic_load(&l->length);
+
_Y;
- idx = reduce(l, pa_atomic_load(&l->write_idx));
+ idx = reduce(l, (unsigned) pa_atomic_load(&l->write_idx));
for (; n > 0 ; n--) {
+
_Y;
- if (pa_atomic_cmpxchg(&cells[idx].state, STATE_UNUSED, STATE_BUSY)) {
+ if (pa_atomic_ptr_cmpxchg(&cells[idx], NULL, p)) {
+
_Y;
pa_atomic_inc(&l->write_idx);
- _Y;
- cells[idx].data = p;
- _Y;
- pa_atomic_store(&cells[idx].state, STATE_USED);
+
_Y;
pa_atomic_inc(&l->length);
+
return 0;
}
@@ -193,31 +171,36 @@ int pa_flist_push(pa_flist*l, void *p) {
}
void* pa_flist_pop(pa_flist*l) {
- int idx, len, n;
- struct cell *cells;
+ unsigned idx, len, n;
+ pa_atomic_ptr_t *cells;
pa_assert(l);
cells = PA_FLIST_CELLS(l);
- n = len = pa_atomic_load(&l->length) + N_EXTRA_SCAN;
+ n = len = (unsigned) pa_atomic_load(&l->length) + N_EXTRA_SCAN;
+
_Y;
- idx = reduce(l, pa_atomic_load(&l->read_idx));
+ idx = reduce(l, (unsigned) pa_atomic_load(&l->read_idx));
for (; n > 0 ; n--) {
+ void *p;
+
_Y;
+ p = pa_atomic_ptr_load(&cells[idx]);
+
+ if (p) {
- if (pa_atomic_cmpxchg(&cells[idx].state, STATE_USED, STATE_BUSY)) {
- void *p;
- _Y;
- pa_atomic_inc(&l->read_idx);
- _Y;
- p = cells[idx].data;
_Y;
- pa_atomic_store(&cells[idx].state, STATE_UNUSED);
+ if (!pa_atomic_ptr_cmpxchg(&cells[idx], p, NULL))
+ continue;
+
_Y;
+ pa_atomic_inc(&l->read_idx);
+ _Y;
pa_atomic_dec(&l->length);
+
return p;
}
diff --git a/src/pulsecore/flist.h b/src/pulsecore/flist.h
index 547c736..512dd35 100644
--- a/src/pulsecore/flist.h
+++ b/src/pulsecore/flist.h
@@ -1,12 +1,10 @@
#ifndef foopulseflisthfoo
#define foopulseflisthfoo
-/* $Id: flist.h 1971 2007-10-28 19:13:50Z lennart $ */
-
/***
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
@@ -25,9 +23,10 @@
***/
#include <pulse/def.h>
+#include <pulse/gccmacro.h>
#include <pulsecore/once.h>
-#include <pulsecore/gccmacro.h>
+#include <pulsecore/core-util.h>
/* A multiple-reader multipler-write lock-free free list implementation */
@@ -58,6 +57,8 @@ void* pa_flist_pop(pa_flist*l);
} \
static void name##_flist_destructor(void) PA_GCC_DESTRUCTOR; \
static void name##_flist_destructor(void) { \
+ if (!pa_in_valgrind()) \
+ return; \
if (name##_flist.flist) \
pa_flist_free(name##_flist.flist, (free_cb)); \
} \
diff --git a/src/pulsecore/gccmacro.h b/src/pulsecore/gccmacro.h
deleted file mode 100644
index 02a4a24..0000000
--- a/src/pulsecore/gccmacro.h
+++ /dev/null
@@ -1,90 +0,0 @@
-#ifndef foopulsegccmacrohfoo
-#define foopulsegccmacrohfoo
-
-/* $Id: gccmacro.h 2150 2008-03-27 23:18:27Z lennart $ */
-
-/***
- 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 __GNUC__
-#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 __GNUC__
-#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
-
-#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
-
-#endif
diff --git a/src/pulsecore/hashmap.c b/src/pulsecore/hashmap.c
index 8dc42ae..3c6f41e 100644
--- a/src/pulsecore/hashmap.c
+++ b/src/pulsecore/hashmap.c
@@ -1,9 +1,7 @@
-/* $Id: hashmap.c 2192 2008-03-30 00:38:47Z lennart $ */
-
/***
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
@@ -29,7 +27,6 @@
#include <string.h>
#include <pulse/xmalloc.h>
-
#include <pulsecore/idxset.h>
#include <pulsecore/log.h>
#include <pulsecore/flist.h>
@@ -37,37 +34,39 @@
#include "hashmap.h"
-#define BUCKETS 127
+#define NBUCKETS 127
struct hashmap_entry {
- struct hashmap_entry *next, *previous, *bucket_next, *bucket_previous;
- unsigned hash;
const void *key;
void *value;
+
+ struct hashmap_entry *bucket_next, *bucket_previous;
+ struct hashmap_entry *iterate_next, *iterate_previous;
};
struct pa_hashmap {
- unsigned size;
- struct hashmap_entry **data;
- struct hashmap_entry *first_entry;
-
- unsigned n_entries;
pa_hash_func_t hash_func;
pa_compare_func_t compare_func;
+
+ struct hashmap_entry *iterate_list_head, *iterate_list_tail;
+ unsigned n_entries;
};
+#define BY_HASH(h) ((struct hashmap_entry**) ((uint8_t*) (h) + PA_ALIGN(sizeof(pa_hashmap))))
+
PA_STATIC_FLIST_DECLARE(entries, 0, pa_xfree);
pa_hashmap *pa_hashmap_new(pa_hash_func_t hash_func, pa_compare_func_t compare_func) {
pa_hashmap *h;
- h = pa_xnew(pa_hashmap, 1);
- h->data = pa_xnew0(struct hashmap_entry*, h->size = BUCKETS);
- h->first_entry = NULL;
- h->n_entries = 0;
+ h = pa_xmalloc0(PA_ALIGN(sizeof(pa_hashmap)) + NBUCKETS*sizeof(struct hashmap_entry*));
+
h->hash_func = hash_func ? hash_func : pa_idxset_trivial_hash_func;
h->compare_func = compare_func ? compare_func : pa_idxset_trivial_compare_func;
+ h->n_entries = 0;
+ h->iterate_list_head = h->iterate_list_tail = NULL;
+
return h;
}
@@ -75,47 +74,56 @@ static void remove_entry(pa_hashmap *h, struct hashmap_entry *e) {
pa_assert(h);
pa_assert(e);
- if (e->next)
- e->next->previous = e->previous;
- if (e->previous)
- e->previous->next = e->next;
+ /* Remove from iteration list */
+ if (e->iterate_next)
+ e->iterate_next->iterate_previous = e->iterate_previous;
else
- h->first_entry = e->next;
+ h->iterate_list_tail = e->iterate_previous;
+ if (e->iterate_previous)
+ e->iterate_previous->iterate_next = e->iterate_next;
+ else
+ h->iterate_list_head = e->iterate_next;
+
+ /* Remove from hash table bucket list */
if (e->bucket_next)
e->bucket_next->bucket_previous = e->bucket_previous;
+
if (e->bucket_previous)
e->bucket_previous->bucket_next = e->bucket_next;
else {
- pa_assert(e->hash < h->size);
- h->data[e->hash] = e->bucket_next;
+ unsigned hash = h->hash_func(e->key) % NBUCKETS;
+ BY_HASH(h)[hash] = e->bucket_next;
}
if (pa_flist_push(PA_STATIC_FLIST_GET(entries), e) < 0)
pa_xfree(e);
+ pa_assert(h->n_entries >= 1);
h->n_entries--;
}
-void pa_hashmap_free(pa_hashmap*h, void (*free_func)(void *p, void *userdata), void *userdata) {
+void pa_hashmap_free(pa_hashmap*h, pa_free2_cb_t free_cb, void *userdata) {
pa_assert(h);
- while (h->first_entry) {
- if (free_func)
- free_func(h->first_entry->value, userdata);
- remove_entry(h, h->first_entry);
+ while (h->iterate_list_head) {
+ void *data;
+ data = h->iterate_list_head->value;
+ remove_entry(h, h->iterate_list_head);
+
+ if (free_cb)
+ free_cb(data, userdata);
}
- pa_xfree(h->data);
pa_xfree(h);
}
-static struct hashmap_entry *get(pa_hashmap *h, unsigned hash, const void *key) {
+static struct hashmap_entry *hash_scan(pa_hashmap *h, unsigned hash, const void *key) {
struct hashmap_entry *e;
pa_assert(h);
- pa_assert(hash < h->size);
+ pa_assert(hash < NBUCKETS);
- for (e = h->data[hash]; e; e = e->bucket_next)
+ for (e = BY_HASH(h)[hash]; e; e = e->bucket_next)
if (h->compare_func(e->key, key) == 0)
return e;
@@ -125,33 +133,42 @@ static struct hashmap_entry *get(pa_hashmap *h, unsigned hash, const void *key)
int pa_hashmap_put(pa_hashmap *h, const void *key, void *value) {
struct hashmap_entry *e;
unsigned hash;
+
pa_assert(h);
- hash = h->hash_func(key) % h->size;
+ hash = h->hash_func(key) % NBUCKETS;
- if ((e = get(h, hash, key)))
+ if ((e = hash_scan(h, hash, key)))
return -1;
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;
- e->previous = NULL;
- e->next = h->first_entry;
- if (h->first_entry)
- h->first_entry->previous = e;
- h->first_entry = e;
-
+ /* Insert into hash table */
+ e->bucket_next = BY_HASH(h)[hash];
e->bucket_previous = NULL;
- e->bucket_next = h->data[hash];
- if (h->data[hash])
- h->data[hash]->bucket_previous = e;
- h->data[hash] = e;
+ if (BY_HASH(h)[hash])
+ BY_HASH(h)[hash]->bucket_previous = e;
+ BY_HASH(h)[hash] = e;
+
+ /* Insert into iteration list */
+ e->iterate_previous = h->iterate_list_tail;
+ e->iterate_next = NULL;
+ if (h->iterate_list_tail) {
+ pa_assert(h->iterate_list_head);
+ h->iterate_list_tail->iterate_next = e;
+ } else {
+ pa_assert(!h->iterate_list_head);
+ h->iterate_list_head = e;
+ }
+ h->iterate_list_tail = e;
+
+ h->n_entries++;
+ pa_assert(h->n_entries >= 1);
- h->n_entries ++;
return 0;
}
@@ -161,9 +178,9 @@ void* pa_hashmap_get(pa_hashmap *h, const void *key) {
pa_assert(h);
- hash = h->hash_func(key) % h->size;
+ hash = h->hash_func(key) % NBUCKETS;
- if (!(e = get(h, hash, key)))
+ if (!(e = hash_scan(h, hash, key)))
return NULL;
return e->value;
@@ -176,39 +193,57 @@ void* pa_hashmap_remove(pa_hashmap *h, const void *key) {
pa_assert(h);
- hash = h->hash_func(key) % h->size;
+ hash = h->hash_func(key) % NBUCKETS;
- if (!(e = get(h, hash, key)))
+ if (!(e = hash_scan(h, hash, key)))
return NULL;
data = e->value;
remove_entry(h, e);
- return data;
-}
-unsigned pa_hashmap_size(pa_hashmap *h) {
- return h->n_entries;
+ return data;
}
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->iterate_list_head)
+ goto at_end;
+
+ e = *state ? *state : h->iterate_list_head;
+
+ if (e->iterate_next)
+ *state = e->iterate_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_first(pa_hashmap *h) {
+ pa_assert(h);
+
+ if (!h->iterate_list_head)
+ return NULL;
+
+ return h->iterate_list_head->value;
}
void* pa_hashmap_steal_first(pa_hashmap *h) {
@@ -216,19 +251,23 @@ void* pa_hashmap_steal_first(pa_hashmap *h) {
pa_assert(h);
- if (!h->first_entry)
+ if (!h->iterate_list_head)
return NULL;
- data = h->first_entry->value;
- remove_entry(h, h->first_entry);
+ data = h->iterate_list_head->value;
+ remove_entry(h, h->iterate_list_head);
+
return data;
}
-void *pa_hashmap_get_first(pa_hashmap *h) {
+unsigned pa_hashmap_size(pa_hashmap *h) {
pa_assert(h);
- if (!h->first_entry)
- return NULL;
+ return h->n_entries;
+}
+
+pa_bool_t pa_hashmap_isempty(pa_hashmap *h) {
+ pa_assert(h);
- return h->first_entry->value;
+ return h->n_entries == 0;
}
diff --git a/src/pulsecore/hashmap.h b/src/pulsecore/hashmap.h
index f010e20..70d78b7 100644
--- a/src/pulsecore/hashmap.h
+++ b/src/pulsecore/hashmap.h
@@ -1,12 +1,10 @@
-#ifndef foohashmaphfoo
-#define foohashmaphfoo
-
-/* $Id: hashmap.h 1971 2007-10-28 19:13:50Z lennart $ */
+#ifndef foopulsecorehashmaphfoo
+#define foopulsecorehashmaphfoo
/***
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
@@ -32,32 +30,39 @@
typedef struct pa_hashmap pa_hashmap;
-typedef void (*pa_free2_cb_t)(void *p, void *userdata);
-
/* Create a new hashmap. Use the specified functions for hashing and comparing objects in the map */
pa_hashmap *pa_hashmap_new(pa_hash_func_t hash_func, pa_compare_func_t compare_func);
/* Free the hash table. Calls the specified function for every value in the table. The function may be NULL */
void pa_hashmap_free(pa_hashmap*, pa_free2_cb_t free_cb, void *userdata);
-/* Returns non-zero when the entry already exists */
+/* Add an entry to the hashmap. Returns non-zero when the entry already exists */
int pa_hashmap_put(pa_hashmap *h, const void *key, void *value);
+
+/* Return an entry from the hashmap */
void* pa_hashmap_get(pa_hashmap *h, const void *key);
/* Returns the data of the entry while removing */
void* pa_hashmap_remove(pa_hashmap *h, const void *key);
+/* Return the current number of entries of the hashmap */
unsigned pa_hashmap_size(pa_hashmap *h);
+/* Return TRUE if the hashmap is empty */
+pa_bool_t pa_hashmap_isempty(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);
+/* Remove the oldest entry in the hashmap and return it */
void *pa_hashmap_steal_first(pa_hashmap *h);
-void *pa_hashmap_get_first(pa_hashmap *h);
+/* Return the oldest entry in the hashmap */
+void* pa_hashmap_first(pa_hashmap *h);
#endif
diff --git a/src/pulsecore/hook-list.c b/src/pulsecore/hook-list.c
index b8ddca5..3969403 100644
--- a/src/pulsecore/hook-list.c
+++ b/src/pulsecore/hook-list.c
@@ -1,5 +1,3 @@
-/* $Id: hook-list.c 1971 2007-10-28 19:13:50Z lennart $ */
-
/***
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,17 +39,14 @@ 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);
}
-void pa_hook_free(pa_hook *hook) {
+void pa_hook_done(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 2cc5c95..de947ad 100644
--- a/src/pulsecore/hook-list.h
+++ b/src/pulsecore/hook-list.h
@@ -1,8 +1,6 @@
#ifndef foohooklistfoo
#define foohooklistfoo
-/* $Id: hook-list.h 1426 2007-02-13 15:35:19Z ossman $ */
-
/***
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,16 +58,15 @@ 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;
};
void pa_hook_init(pa_hook *hook, void *data);
-void pa_hook_free(pa_hook *hook);
+void pa_hook_done(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 5fde082..24a28db 100644
--- a/src/pulsecore/idxset.c
+++ b/src/pulsecore/idxset.c
@@ -1,9 +1,7 @@
-/* $Id: idxset.c 1971 2007-10-28 19:13:50Z lennart $ */
-
/***
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
@@ -31,29 +29,36 @@
#include <string.h>
#include <pulse/xmalloc.h>
-#include <pulsecore/macro.h>
+#include <pulsecore/log.h>
#include <pulsecore/flist.h>
+#include <pulsecore/macro.h>
#include "idxset.h"
+#define NBUCKETS 127
+
struct idxset_entry {
+ uint32_t idx;
void *data;
- uint32_t index;
- unsigned hash_value;
- struct idxset_entry *hash_prev, *hash_next;
- struct idxset_entry* iterate_prev, *iterate_next;
+ struct idxset_entry *data_next, *data_previous;
+ struct idxset_entry *index_next, *index_previous;
+ struct idxset_entry *iterate_next, *iterate_previous;
};
struct pa_idxset {
pa_hash_func_t hash_func;
pa_compare_func_t compare_func;
- unsigned hash_table_size, n_entries;
- struct idxset_entry **hash_table, **array, *iterate_list_head, *iterate_list_tail;
- uint32_t index, start_index, array_size;
+ uint32_t current_index;
+
+ struct idxset_entry *iterate_list_head, *iterate_list_tail;
+ unsigned n_entries;
};
+#define BY_DATA(i) ((struct idxset_entry**) ((uint8_t*) (i) + PA_ALIGN(sizeof(pa_idxset))))
+#define BY_INDEX(i) (BY_DATA(i) + NBUCKETS)
+
PA_STATIC_FLIST_DECLARE(entries, 0, pa_xfree);
unsigned pa_idxset_string_hash_func(const void *p) {
@@ -61,7 +66,7 @@ unsigned pa_idxset_string_hash_func(const void *p) {
const char *c;
for (c = p; *c; c++)
- hash = 31 * hash + *c;
+ hash = 31 * hash + (unsigned) *c;
return hash;
}
@@ -75,137 +80,146 @@ unsigned pa_idxset_trivial_hash_func(const void *p) {
}
int pa_idxset_trivial_compare_func(const void *a, const void *b) {
- return a != b;
+ return a < b ? -1 : (a > b ? 1 : 0);
}
pa_idxset* pa_idxset_new(pa_hash_func_t hash_func, pa_compare_func_t compare_func) {
pa_idxset *s;
- s = pa_xnew(pa_idxset, 1);
+ s = pa_xmalloc0(PA_ALIGN(sizeof(pa_idxset)) + NBUCKETS*2*sizeof(struct idxset_entry*));
+
s->hash_func = hash_func ? hash_func : pa_idxset_trivial_hash_func;
s->compare_func = compare_func ? compare_func : pa_idxset_trivial_compare_func;
- s->hash_table_size = 127;
- s->hash_table = pa_xnew0(struct idxset_entry*, s->hash_table_size);
- s->array = NULL;
- s->array_size = 0;
- s->index = 0;
- s->start_index = 0;
- s->n_entries = 0;
+ s->current_index = 0;
+ s->n_entries = 0;
s->iterate_list_head = s->iterate_list_tail = NULL;
return s;
}
-void pa_idxset_free(pa_idxset *s, void (*free_func) (void *p, void *userdata), void *userdata) {
+static void remove_entry(pa_idxset *s, struct idxset_entry *e) {
pa_assert(s);
+ pa_assert(e);
- while (s->iterate_list_head) {
- struct idxset_entry *e = s->iterate_list_head;
- s->iterate_list_head = s->iterate_list_head->iterate_next;
+ /* Remove from iteration linked list */
+ if (e->iterate_next)
+ e->iterate_next->iterate_previous = e->iterate_previous;
+ else
+ s->iterate_list_tail = e->iterate_previous;
- if (free_func)
- free_func(e->data, userdata);
+ if (e->iterate_previous)
+ e->iterate_previous->iterate_next = e->iterate_next;
+ else
+ s->iterate_list_head = e->iterate_next;
- if (pa_flist_push(PA_STATIC_FLIST_GET(entries), e) < 0)
- pa_xfree(e);
+ /* Remove from data hash table */
+ if (e->data_next)
+ e->data_next->data_previous = e->data_previous;
+
+ if (e->data_previous)
+ e->data_previous->data_next = e->data_next;
+ else {
+ unsigned hash = s->hash_func(e->data) % NBUCKETS;
+ BY_DATA(s)[hash] = e->data_next;
}
- pa_xfree(s->hash_table);
- pa_xfree(s->array);
- pa_xfree(s);
-}
+ /* Remove from index hash table */
+ if (e->index_next)
+ e->index_next->index_previous = e->index_previous;
-static struct idxset_entry* hash_scan(pa_idxset *s, struct idxset_entry* e, const void *p) {
- pa_assert(p);
+ if (e->index_previous)
+ e->index_previous->index_next = e->index_next;
+ else
+ BY_INDEX(s)[e->idx % NBUCKETS] = e->index_next;
- pa_assert(s->compare_func);
- for (; e; e = e->hash_next)
- if (s->compare_func(e->data, p) == 0)
- return e;
+ if (pa_flist_push(PA_STATIC_FLIST_GET(entries), e) < 0)
+ pa_xfree(e);
- return NULL;
+ pa_assert(s->n_entries >= 1);
+ s->n_entries--;
}
-static void extend_array(pa_idxset *s, uint32_t idx) {
- uint32_t i, j, l;
- struct idxset_entry** n;
-
+void pa_idxset_free(pa_idxset *s, pa_free2_cb_t free_cb, void *userdata) {
pa_assert(s);
- pa_assert(idx >= s->start_index);
- if (idx < s->start_index + s->array_size)
- return;
+ while (s->iterate_list_head) {
+ void *data = s->iterate_list_head->data;
- for (i = 0; i < s->array_size; i++)
- if (s->array[i])
- break;
+ remove_entry(s, s->iterate_list_head);
- l = idx - s->start_index - i + 100;
- n = pa_xnew0(struct idxset_entry*, l);
+ if (free_cb)
+ free_cb(data, userdata);
+ }
- for (j = 0; j < s->array_size-i; j++)
- n[j] = s->array[i+j];
+ pa_xfree(s);
+}
- pa_xfree(s->array);
+static struct idxset_entry* data_scan(pa_idxset *s, unsigned hash, const void *p) {
+ struct idxset_entry *e;
+ pa_assert(s);
+ pa_assert(hash < NBUCKETS);
+ pa_assert(p);
- s->array = n;
- s->array_size = l;
- s->start_index += i;
+ for (e = BY_DATA(s)[hash]; e; e = e->data_next)
+ if (s->compare_func(e->data, p) == 0)
+ return e;
+
+ return NULL;
}
-static struct idxset_entry** array_index(pa_idxset*s, uint32_t idx) {
+static struct idxset_entry* index_scan(pa_idxset *s, unsigned hash, uint32_t idx) {
+ struct idxset_entry *e;
pa_assert(s);
+ pa_assert(hash < NBUCKETS);
- if (idx >= s->start_index + s->array_size)
- return NULL;
-
- if (idx < s->start_index)
- return NULL;
+ for (e = BY_INDEX(s)[hash]; e; e = e->index_next)
+ if (e->idx == idx)
+ return e;
- return s->array + idx - s->start_index;
+ return NULL;
}
int pa_idxset_put(pa_idxset*s, void *p, uint32_t *idx) {
- unsigned h;
- struct idxset_entry *e, **a;
+ unsigned hash;
+ struct idxset_entry *e;
pa_assert(s);
- pa_assert(p);
- pa_assert(s->hash_func);
- h = s->hash_func(p) % s->hash_table_size;
+ hash = s->hash_func(p) % NBUCKETS;
- pa_assert(s->hash_table);
- if ((e = hash_scan(s, s->hash_table[h], p))) {
+ if ((e = data_scan(s, hash, p))) {
if (idx)
- *idx = e->index;
+ *idx = e->idx;
return -1;
}
if (!(e = pa_flist_pop(PA_STATIC_FLIST_GET(entries))))
e = pa_xnew(struct idxset_entry, 1);
+
e->data = p;
- e->index = s->index++;
- e->hash_value = h;
-
- /* Insert into hash table */
- e->hash_next = s->hash_table[h];
- e->hash_prev = NULL;
- if (s->hash_table[h])
- s->hash_table[h]->hash_prev = e;
- s->hash_table[h] = e;
-
- /* Insert into array */
- extend_array(s, e->index);
- a = array_index(s, e->index);
- pa_assert(a && !*a);
- *a = e;
-
- /* Insert into linked list */
+ e->idx = s->current_index++;
+
+ /* Insert into data hash table */
+ e->data_next = BY_DATA(s)[hash];
+ e->data_previous = NULL;
+ if (BY_DATA(s)[hash])
+ BY_DATA(s)[hash]->data_previous = e;
+ BY_DATA(s)[hash] = e;
+
+ hash = e->idx % NBUCKETS;
+
+ /* Insert into index hash table */
+ e->index_next = BY_INDEX(s)[hash];
+ e->index_previous = NULL;
+ if (BY_INDEX(s)[hash])
+ BY_INDEX(s)[hash]->index_previous = e;
+ BY_INDEX(s)[hash] = e;
+
+ /* Insert into iteration list */
+ e->iterate_previous = s->iterate_list_tail;
e->iterate_next = NULL;
- e->iterate_prev = s->iterate_list_tail;
if (s->iterate_list_tail) {
pa_assert(s->iterate_list_head);
s->iterate_list_tail->iterate_next = e;
@@ -219,117 +233,76 @@ int pa_idxset_put(pa_idxset*s, void *p, uint32_t *idx) {
pa_assert(s->n_entries >= 1);
if (idx)
- *idx = e->index;
+ *idx = e->idx;
return 0;
}
void* pa_idxset_get_by_index(pa_idxset*s, uint32_t idx) {
- struct idxset_entry **a;
+ unsigned hash;
+ struct idxset_entry *e;
+
pa_assert(s);
- if (!(a = array_index(s, idx)))
- return NULL;
+ hash = idx % NBUCKETS;
- if (!*a)
+ if (!(e = index_scan(s, hash, idx)))
return NULL;
- return (*a)->data;
+ return e->data;
}
void* pa_idxset_get_by_data(pa_idxset*s, const void *p, uint32_t *idx) {
- unsigned h;
+ unsigned hash;
struct idxset_entry *e;
pa_assert(s);
- pa_assert(p);
- pa_assert(s->hash_func);
- h = s->hash_func(p) % s->hash_table_size;
+ hash = s->hash_func(p) % NBUCKETS;
- pa_assert(s->hash_table);
- if (!(e = hash_scan(s, s->hash_table[h], p)))
+ if (!(e = data_scan(s, hash, p)))
return NULL;
if (idx)
- *idx = e->index;
+ *idx = e->idx;
return e->data;
}
-static void remove_entry(pa_idxset *s, struct idxset_entry *e) {
- struct idxset_entry **a;
-
- pa_assert(s);
- pa_assert(e);
-
- /* Remove from array */
- a = array_index(s, e->index);
- pa_assert(a && *a && *a == e);
- *a = NULL;
-
- /* Remove from linked list */
- if (e->iterate_next)
- e->iterate_next->iterate_prev = e->iterate_prev;
- else
- s->iterate_list_tail = e->iterate_prev;
-
- if (e->iterate_prev)
- e->iterate_prev->iterate_next = e->iterate_next;
- else
- s->iterate_list_head = e->iterate_next;
-
- /* Remove from hash table */
- if (e->hash_next)
- e->hash_next->hash_prev = e->hash_prev;
-
- if (e->hash_prev)
- e->hash_prev->hash_next = e->hash_next;
- else
- s->hash_table[e->hash_value] = e->hash_next;
-
- if (pa_flist_push(PA_STATIC_FLIST_GET(entries), e) < 0)
- pa_xfree(e);
-
- pa_assert(s->n_entries >= 1);
- s->n_entries--;
-}
-
void* pa_idxset_remove_by_index(pa_idxset*s, uint32_t idx) {
- struct idxset_entry **a;
+ struct idxset_entry *e;
+ unsigned hash;
void *data;
pa_assert(s);
- if (!(a = array_index(s, idx)))
- return NULL;
+ hash = idx % NBUCKETS;
- if (!*a)
+ if (!(e = index_scan(s, hash, idx)))
return NULL;
- data = (*a)->data;
- remove_entry(s, *a);
+ data = e->data;
+ remove_entry(s, e);
return data;
}
void* pa_idxset_remove_by_data(pa_idxset*s, const void *data, uint32_t *idx) {
struct idxset_entry *e;
- unsigned h;
+ unsigned hash;
void *r;
pa_assert(s);
- pa_assert(s->hash_func);
- h = s->hash_func(data) % s->hash_table_size;
+ hash = s->hash_func(data) % NBUCKETS;
- pa_assert(s->hash_table);
- if (!(e = hash_scan(s, s->hash_table[h], data)))
+ if (!(e = data_scan(s, hash, data)))
return NULL;
r = e->data;
+
if (idx)
- *idx = e->index;
+ *idx = e->idx;
remove_entry(s, e);
@@ -337,76 +310,112 @@ 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;
+ unsigned hash;
+ struct idxset_entry *e;
pa_assert(s);
pa_assert(idx);
- if ((a = array_index(s, *idx)) && *a)
- e = (*a)->iterate_next;
+ hash = *idx % NBUCKETS;
- if (!e)
+ e = index_scan(s, hash, *idx);
+
+ if (e && e->iterate_next)
+ e = e->iterate_next;
+ else
e = s->iterate_list_head;
if (!e)
return NULL;
- *idx = e->index;
+ *idx = e->idx;
return e->data;
}
-void* pa_idxset_first(pa_idxset *s, uint32_t *idx) {
+void *pa_idxset_iterate(pa_idxset *s, void **state, uint32_t *idx) {
+ struct idxset_entry *e;
+
pa_assert(s);
+ pa_assert(state);
- if (!s->iterate_list_head)
- return NULL;
+ if (*state == (void*) -1)
+ goto at_end;
+
+ if ((!*state && !s->iterate_list_head))
+ goto at_end;
+
+ e = *state ? *state : s->iterate_list_head;
+
+ if (e->iterate_next)
+ *state = e->iterate_next;
+ else
+ *state = (void*) -1;
if (idx)
- *idx = s->iterate_list_head->index;
- return s->iterate_list_head->data;
+ *idx = e->idx;
+
+ return e->data;
+
+at_end:
+ *state = (void *) -1;
+
+ if (idx)
+ *idx = PA_IDXSET_INVALID;
+
+ return NULL;
}
-void *pa_idxset_next(pa_idxset *s, uint32_t *idx) {
- struct idxset_entry **a, *e = NULL;
+void* pa_idxset_steal_first(pa_idxset *s, uint32_t *idx) {
+ void *data;
pa_assert(s);
- pa_assert(idx);
- if ((a = array_index(s, *idx)) && *a)
- e = (*a)->iterate_next;
+ if (!s->iterate_list_head)
+ return NULL;
- if (e) {
- *idx = e->index;
- return e->data;
- } else {
- *idx = PA_IDXSET_INVALID;
+ data = s->iterate_list_head->data;
+
+ if (idx)
+ *idx = s->iterate_list_head->idx;
+
+ remove_entry(s, s->iterate_list_head);
+
+ return data;
+}
+
+void* pa_idxset_first(pa_idxset *s, uint32_t *idx) {
+ pa_assert(s);
+
+ if (!s->iterate_list_head)
return NULL;
- }
+
+ if (idx)
+ *idx = s->iterate_list_head->idx;
+
+ return s->iterate_list_head->data;
}
-int pa_idxset_foreach(pa_idxset*s, int (*func)(void *p, uint32_t idx, int *del, void*userdata), void *userdata) {
+void *pa_idxset_next(pa_idxset *s, uint32_t *idx) {
struct idxset_entry *e;
+ unsigned hash;
pa_assert(s);
- pa_assert(func);
-
- e = s->iterate_list_head;
- while (e) {
- int del = 0, r;
- struct idxset_entry *n = e->iterate_next;
-
- r = func(e->data, e->index, &del, userdata);
+ pa_assert(idx);
- if (del)
- remove_entry(s, e);
+ hash = *idx % NBUCKETS;
- if (r < 0)
- return r;
+ if (!(e = index_scan(s, hash, *idx)))
+ return NULL;
- e = n;
+ if (!e->iterate_next) {
+ *idx = PA_IDXSET_INVALID;
+ return NULL;
}
- return 0;
+ e = e->iterate_next;
+
+ *idx = e->idx;
+ return e->data;
}
unsigned pa_idxset_size(pa_idxset*s) {
@@ -415,9 +424,8 @@ unsigned pa_idxset_size(pa_idxset*s) {
return s->n_entries;
}
-int pa_idxset_isempty(pa_idxset *s) {
+pa_bool_t 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 b152206..7531ea3 100644
--- a/src/pulsecore/idxset.h
+++ b/src/pulsecore/idxset.h
@@ -1,12 +1,10 @@
-#ifndef fooidxsethfoo
-#define fooidxsethfoo
-
-/* $Id: idxset.h 1971 2007-10-28 19:13:50Z lennart $ */
+#ifndef foopulsecoreidxsethfoo
+#define foopulsecoreidxsethfoo
/***
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
@@ -27,13 +25,19 @@
#include <inttypes.h>
+#include <pulsecore/macro.h>
+
/* A combination of a set and a dynamic array. Entries are indexable
- * both through a numeric automatically generated index and the entry's
- * data pointer. As usual, memory management is the user's job. */
+ * both through an automatically generated numeric index and the
+ * entry's data pointer. As usual, memory management is the user's
+ * job. */
/* A special index value denoting the invalid index. */
#define PA_IDXSET_INVALID ((uint32_t) -1)
+/* Similar to pa_free_cb_t, but takes a userdata argument */
+typedef void (*pa_free2_cb_t)(void *p, void *userdata);
+
/* Generic implementations for hash and comparison functions. Just
* compares the pointer or calculates the hash value directly from the
* pointer value. */
@@ -53,7 +57,7 @@ typedef struct pa_idxset pa_idxset;
pa_idxset* pa_idxset_new(pa_hash_func_t hash_func, pa_compare_func_t compare_func);
/* Free the idxset. When the idxset is not empty the specified function is called for every entry contained */
-void pa_idxset_free(pa_idxset *s, void (*free_func) (void *p, void *userdata), void *userdata);
+void pa_idxset_free(pa_idxset *s, pa_free2_cb_t free_cb, void *userdata);
/* Store a new item in the idxset. The index of the item is returned in *idx */
int pa_idxset_put(pa_idxset*s, void *p, uint32_t *idx);
@@ -78,6 +82,12 @@ void* pa_idxset_remove_by_data(pa_idxset*s, const void *p, uint32_t *idx);
returned before the an entry is returned the second time.*/
void* pa_idxset_rrobin(pa_idxset *s, uint32_t *idx);
+/* Iterate through the idxset. At first iteration state should be NULL */
+void *pa_idxset_iterate(pa_idxset *s, void **state, uint32_t *idx);
+
+/* Return the oldest entry in the idxset and remove it. If idx is not NULL fill in its index in *idx */
+void* pa_idxset_steal_first(pa_idxset *s, uint32_t *idx);
+
/* Return the oldest entry in the idxset. Fill in its index in *idx. */
void* pa_idxset_first(pa_idxset *s, uint32_t *idx);
@@ -87,14 +97,10 @@ void* pa_idxset_first(pa_idxset *s, uint32_t *idx);
* iterate through the set.*/
void *pa_idxset_next(pa_idxset *s, uint32_t *idx);
-/* Call a function for every item in the set. If the callback function
- returns -1, the loop is terminated. If *del is set to non-zero that
- specific item is removed. It is not safe to call any other
- functions on the idxset while pa_idxset_foreach is executed. */
-int pa_idxset_foreach(pa_idxset*s, int (*func)(void *p, uint32_t idx, int *del, void*userdata), void *userdata);
-
+/* Return the current number of entries in the idxset */
unsigned pa_idxset_size(pa_idxset*s);
-int pa_idxset_isempty(pa_idxset *s);
+/* Return TRUE of the idxset is empty */
+pa_bool_t pa_idxset_isempty(pa_idxset *s);
#endif
diff --git a/src/pulsecore/inet_ntop.c b/src/pulsecore/inet_ntop.c
index c8621f8..8755123 100644
--- a/src/pulsecore/inet_ntop.c
+++ b/src/pulsecore/inet_ntop.c
@@ -1,5 +1,3 @@
-/* $Id: inet_ntop.c 1986 2007-10-29 20:32:53Z lennart $ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/inet_pton.c b/src/pulsecore/inet_pton.c
index 739cf4c..d191e55 100644
--- a/src/pulsecore/inet_pton.c
+++ b/src/pulsecore/inet_pton.c
@@ -1,5 +1,3 @@
-/* $Id: inet_pton.c 1986 2007-10-29 20:32:53Z lennart $ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/iochannel.c b/src/pulsecore/iochannel.c
index b4cff26..b40c981 100644
--- a/src/pulsecore/iochannel.c
+++ b/src/pulsecore/iochannel.c
@@ -1,5 +1,3 @@
-/* $Id: iochannel.c 2022 2007-11-04 16:51:26Z lennart $ */
-
/***
This file is part of PulseAudio.
@@ -424,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 36ec8db..9050df9 100644
--- a/src/pulsecore/iochannel.h
+++ b/src/pulsecore/iochannel.h
@@ -1,8 +1,6 @@
#ifndef fooiochannelhfoo
#define fooiochannelhfoo
-/* $Id: iochannel.h 1971 2007-10-28 19:13:50Z lennart $ */
-
/***
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 21fd8fb..88174c0 100644
--- a/src/pulsecore/ioline.c
+++ b/src/pulsecore/ioline.c
@@ -1,5 +1,3 @@
-/* $Id: ioline.c 1971 2007-10-28 19:13:50Z lennart $ */
-
/***
This file is part of PulseAudio.
@@ -49,7 +47,6 @@ struct pa_ioline {
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, (unsigned) 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, (unsigned) n);
if (l->rbuf_valid_length)
memcpy(new, l->rbuf+l->rbuf_index, l->rbuf_valid_length);
pa_xfree(l->rbuf);
@@ -299,19 +302,23 @@ 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;
}
- l->rbuf_valid_length += r;
+ l->rbuf_valid_length += (size_t) r;
/* Look if a line has been terminated in the newly read data */
- scan_for_lines(l, l->rbuf_valid_length - r);
+ scan_for_lines(l, l->rbuf_valid_length - (size_t) r);
}
return 0;
@@ -328,16 +335,19 @@ 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;
}
- l->wbuf_index += r;
- l->wbuf_valid_length -= r;
+ l->wbuf_index += (size_t) r;
+ l->wbuf_valid_length -= (size_t) r;
/* A shortcut for the next time */
if (l->wbuf_valid_length == 0)
@@ -363,7 +373,7 @@ 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);
}
@@ -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 304549e..b9a3d9f 100644
--- a/src/pulsecore/ioline.h
+++ b/src/pulsecore/ioline.h
@@ -1,8 +1,6 @@
#ifndef fooiolinehfoo
#define fooiolinehfoo
-/* $Id: ioline.h 1426 2007-02-13 15:35:19Z ossman $ */
-
/***
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 329b94e..6d5080f 100644
--- a/src/pulsecore/ipacl.c
+++ b/src/pulsecore/ipacl.c
@@ -1,5 +1,3 @@
-/* $Id: ipacl.c 1971 2007-10-28 19:13:50Z lennart $ */
-
/***
This file is part of PulseAudio.
@@ -124,7 +122,7 @@ pa_ip_acl* pa_ip_acl_new(const char *s) {
if (e.bits < 128) {
int t = 0, i;
- for (i = 0, bits = e.bits; i < 16; i++) {
+ for (i = 0, bits = (uint32_t) e.bits; i < 16; i++) {
if (bits >= 8)
bits -= 8;
diff --git a/src/pulsecore/ipacl.h b/src/pulsecore/ipacl.h
index 030cc3c..7b7ffa6 100644
--- a/src/pulsecore/ipacl.h
+++ b/src/pulsecore/ipacl.h
@@ -1,8 +1,6 @@
#ifndef fooparseaddrhfoo
#define fooparseaddrhfoo
-/* $Id: ipacl.h 1426 2007-02-13 15:35:19Z ossman $ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/llist.h b/src/pulsecore/llist.h
index ac3c27c..46b54eb 100644
--- a/src/pulsecore/llist.h
+++ b/src/pulsecore/llist.h
@@ -1,8 +1,6 @@
#ifndef foollistfoo
#define foollistfoo
-/* $Id: llist.h 1971 2007-10-28 19:13:50Z lennart $ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/lock-autospawn.c b/src/pulsecore/lock-autospawn.c
new file mode 100644
index 0000000..d36b669
--- /dev/null
+++ b/src/pulsecore/lock-autospawn.c
@@ -0,0 +1,330 @@
+/***
+ 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 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/poll.h>
+#include <signal.h>
+#include <pthread.h>
+
+#include <pulse/i18n.h>
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/mutex.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/core-util.h>
+
+#include "lock-autospawn.h"
+
+/* So, why do we have this complex code here with threads and pipes
+ * and stuff? For two reasons: POSIX file locks are per-process, not
+ * per-file descriptor. That means that two contexts within the same
+ * process that try to create the autospawn lock might end up assuming
+ * they both managed to lock the file. And then, POSIX locking
+ * operations are synchronous. If two contexts run from the same event
+ * loop it must be made sure that they do not block each other, but
+ * that the locking operation can happen asynchronously. */
+
+#define AUTOSPAWN_LOCK "autospawn.lock"
+
+static pa_mutex *mutex;
+
+static unsigned n_ref = 0;
+static int lock_fd = -1;
+static pa_mutex *lock_fd_mutex = NULL;
+static pa_bool_t taken = FALSE;
+static pa_thread *thread;
+static int pipe_fd[2] = { -1, -1 };
+
+static void destroy_mutex(void) PA_GCC_DESTRUCTOR;
+
+static int ref(void) {
+
+ if (n_ref > 0) {
+
+ pa_assert(pipe_fd[0] >= 0);
+ pa_assert(pipe_fd[1] >= 0);
+
+ n_ref++;
+
+ return 0;
+ }
+
+ pa_assert(lock_fd < 0);
+ pa_assert(!lock_fd_mutex);
+ pa_assert(!taken);
+ pa_assert(!thread);
+ pa_assert(pipe_fd[0] < 0);
+ pa_assert(pipe_fd[1] < 0);
+
+ if (pipe(pipe_fd) < 0)
+ return -1;
+
+ lock_fd_mutex = pa_mutex_new(FALSE, FALSE);
+
+ pa_make_fd_cloexec(pipe_fd[0]);
+ pa_make_fd_cloexec(pipe_fd[1]);
+
+ pa_make_fd_nonblock(pipe_fd[1]);
+ pa_make_fd_nonblock(pipe_fd[0]);
+
+ n_ref = 1;
+ return 0;
+}
+
+static void unref(pa_bool_t after_fork) {
+
+ pa_assert(n_ref > 0);
+ pa_assert(pipe_fd[0] >= 0);
+ pa_assert(pipe_fd[1] >= 0);
+ pa_assert(lock_fd_mutex);
+
+ n_ref--;
+
+ if (n_ref > 0)
+ return;
+
+ pa_assert(!taken);
+
+ if (thread) {
+ pa_thread_free(thread);
+ thread = NULL;
+ }
+
+ pa_mutex_lock(lock_fd_mutex);
+ if (lock_fd >= 0) {
+
+ if (after_fork)
+ pa_close(lock_fd);
+ else {
+ char *lf;
+
+ if (!(lf = pa_runtime_path(AUTOSPAWN_LOCK)))
+ pa_log_warn(_("Cannot access autospawn lock."));
+
+ pa_unlock_lockfile(lf, lock_fd);
+ pa_xfree(lf);
+
+ lock_fd = -1;
+ }
+ }
+ pa_mutex_unlock(lock_fd_mutex);
+
+ pa_mutex_free(lock_fd_mutex);
+ lock_fd_mutex = NULL;
+
+ pa_close(pipe_fd[0]);
+ pa_close(pipe_fd[1]);
+ pipe_fd[0] = pipe_fd[1] = -1;
+}
+
+static void ping(void) {
+ ssize_t s;
+
+ pa_assert(pipe_fd[1] >= 0);
+
+ for (;;) {
+ char x = 'x';
+
+ if ((s = write(pipe_fd[1], &x, 1)) == 1)
+ break;
+
+ pa_assert(s < 0);
+
+ if (errno == EAGAIN)
+ break;
+
+ pa_assert(errno == EINTR);
+ }
+}
+
+static void wait_for_ping(void) {
+ ssize_t s;
+ char x;
+ struct pollfd pfd;
+ int k;
+
+ pa_assert(pipe_fd[0] >= 0);
+
+ memset(&pfd, 0, sizeof(pfd));
+ pfd.fd = pipe_fd[0];
+ pfd.events = POLLIN;
+
+ if ((k = poll(&pfd, 1, -1)) != 1) {
+ pa_assert(k < 0);
+ pa_assert(errno == EINTR);
+ } else if ((s = read(pipe_fd[0], &x, 1)) != 1) {
+ pa_assert(s < 0);
+ pa_assert(errno == EAGAIN);
+ }
+}
+
+static void empty_pipe(void) {
+ char x[16];
+ ssize_t s;
+
+ pa_assert(pipe_fd[0] >= 0);
+
+ if ((s = read(pipe_fd[0], &x, sizeof(x))) < 1) {
+ pa_assert(s < 0);
+ pa_assert(errno == EAGAIN);
+ }
+}
+
+static void thread_func(void *u) {
+ int fd;
+ char *lf;
+ sigset_t fullset;
+
+ /* No signals in this thread please */
+ sigfillset(&fullset);
+ pthread_sigmask(SIG_BLOCK, &fullset, NULL);
+
+ if (!(lf = pa_runtime_path(AUTOSPAWN_LOCK))) {
+ pa_log_warn(_("Cannot access autospawn lock."));
+ goto finish;
+ }
+
+ if ((fd = pa_lock_lockfile(lf)) < 0)
+ goto finish;
+
+ pa_mutex_lock(lock_fd_mutex);
+ pa_assert(lock_fd < 0);
+ lock_fd = fd;
+ pa_mutex_unlock(lock_fd_mutex);
+
+finish:
+ pa_xfree(lf);
+
+ ping();
+}
+
+static int start_thread(void) {
+
+ if (!thread)
+ if (!(thread = pa_thread_new(thread_func, NULL)))
+ return -1;
+
+ return 0;
+}
+
+static void create_mutex(void) {
+ PA_ONCE_BEGIN {
+ mutex = pa_mutex_new(FALSE, FALSE);
+ } PA_ONCE_END;
+}
+
+static void destroy_mutex(void) {
+
+ if (mutex)
+ pa_mutex_free(mutex);
+}
+
+
+int pa_autospawn_lock_init(void) {
+ int ret = -1;
+
+ create_mutex();
+ pa_mutex_lock(mutex);
+
+ if (ref() < 0)
+ ret = -1;
+ else
+ ret = pipe_fd[0];
+
+ pa_mutex_unlock(mutex);
+
+ return ret;
+}
+
+int pa_autospawn_lock_acquire(pa_bool_t block) {
+ int ret = -1;
+
+ create_mutex();
+ pa_mutex_lock(mutex);
+ pa_assert(n_ref >= 1);
+
+ pa_mutex_lock(lock_fd_mutex);
+
+ for (;;) {
+
+ empty_pipe();
+
+ if (lock_fd >= 0 && !taken) {
+ taken = TRUE;
+ ret = 1;
+ break;
+ }
+
+ if (lock_fd < 0)
+ if (start_thread() < 0)
+ break;
+
+ if (!block) {
+ ret = 0;
+ break;
+ }
+
+ pa_mutex_unlock(lock_fd_mutex);
+ pa_mutex_unlock(mutex);
+
+ wait_for_ping();
+
+ pa_mutex_lock(mutex);
+ pa_mutex_lock(lock_fd_mutex);
+ }
+
+ pa_mutex_unlock(lock_fd_mutex);
+
+ pa_mutex_unlock(mutex);
+
+ return ret;
+}
+
+void pa_autospawn_lock_release(void) {
+
+ create_mutex();
+ pa_mutex_lock(mutex);
+ pa_assert(n_ref >= 1);
+
+ pa_assert(taken);
+ taken = FALSE;
+
+ ping();
+
+ pa_mutex_unlock(mutex);
+}
+
+void pa_autospawn_lock_done(pa_bool_t after_fork) {
+
+ create_mutex();
+ pa_mutex_lock(mutex);
+ pa_assert(n_ref >= 1);
+
+ unref(after_fork);
+
+ pa_mutex_unlock(mutex);
+}
diff --git a/src/pulsecore/lock-autospawn.h b/src/pulsecore/lock-autospawn.h
new file mode 100644
index 0000000..c04c4bd
--- /dev/null
+++ b/src/pulsecore/lock-autospawn.h
@@ -0,0 +1,32 @@
+#ifndef foopulselockautospawnhfoo
+#define foopulselockautospawnhfoo
+
+/***
+ 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.
+***/
+
+#include <pulsecore/macro.h>
+
+int pa_autospawn_lock_init(void);
+int pa_autospawn_lock_acquire(pa_bool_t block);
+void pa_autospawn_lock_release(void);
+void pa_autospawn_lock_done(pa_bool_t after_fork);
+
+#endif
diff --git a/src/pulsecore/log.c b/src/pulsecore/log.c
index 46c486c..b1de696 100644
--- a/src/pulsecore/log.c
+++ b/src/pulsecore/log.c
@@ -1,5 +1,3 @@
-/* $Id: log.c 1971 2007-10-28 19:13:50Z lennart $ */
-
/***
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>
@@ -38,19 +37,23 @@
#include <pulse/utf8.h>
#include <pulse/xmalloc.h>
#include <pulse/util.h>
+#include <pulse/timeval.h>
#include <pulsecore/macro.h>
#include <pulsecore/core-util.h>
+#include <pulsecore/rtclock.h>
+#include <pulsecore/once.h>
#include "log.h"
#define ENV_LOGLEVEL "PULSE_LOG"
#define ENV_LOGMETA "PULSE_LOG_META"
+#define ENV_LOGTIME "PULSE_LOG_TIME"
static char *log_ident = NULL, *log_ident_local = NULL;
static pa_log_target_t log_target = PA_LOG_STDERR;
-static void (*user_log_func)(pa_log_level_t l, const char *s) = NULL;
-static pa_log_level_t maximal_level = PA_LOG_NOTICE;
+static pa_log_func_t user_log_func = NULL;
+static pa_log_level_t maximal_level = PA_LOG_ERROR;
#ifdef HAVE_SYSLOG_H
static const int level_to_syslog[] = {
@@ -82,6 +85,9 @@ 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) {
+ if (!pa_in_valgrind())
+ return;
+
pa_xfree(log_ident);
pa_xfree(log_ident_local);
}
@@ -92,7 +98,7 @@ void pa_log_set_maximal_level(pa_log_level_t l) {
maximal_level = l;
}
-void pa_log_set_target(pa_log_target_t t, void (*func)(pa_log_level_t l, const char*s)) {
+void pa_log_set_target(pa_log_target_t t, pa_log_func_t func) {
pa_assert(t == PA_LOG_USER || !func);
log_target = t;
@@ -108,7 +114,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], timestamp[32];
pa_assert(level < PA_LOG_LEVEL_MAX);
pa_assert(format);
@@ -116,17 +127,46 @@ 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 (getenv(ENV_LOGTIME)) {
+ static pa_usec_t start, last;
+ pa_usec_t u, a, r;
+
+ u = pa_rtclock_usec();
+
+ PA_ONCE_BEGIN {
+ start = u;
+ last = u;
+ } PA_ONCE_END;
+
+ r = u - last;
+ a = u - start;
+
+ /* This is not thread safe, but this is a debugging tool only
+ * anyway. */
+ last = u;
+
+ pa_snprintf(timestamp, sizeof(timestamp), "(%4llu.%03llu|%4llu.%03llu) ",
+ (unsigned long long) (a / PA_USEC_PER_SEC),
+ (unsigned long long) (((a / PA_USEC_PER_MSEC)) % 1000),
+ (unsigned long long) (r / PA_USEC_PER_SEC),
+ (unsigned long long) (((r / PA_USEC_PER_MSEC)) % 1000));
+
+ } else
+ timestamp[0] = 0;
if (!pa_utf8_valid(text))
pa_log_level(level, __FILE__": invalid UTF-8 string following below:");
@@ -158,11 +198,13 @@ 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);
+ fprintf(stderr, "%s%c: %s%s%s%s\n", timestamp, level_to_char[level], location, prefix, t, suffix);
else {
- fprintf(stderr, "%c: %s%s%s%s\n", level_to_char[level], location, prefix, local_t, suffix);
+ fprintf(stderr, "%s%c: %s%s%s%s\n", timestamp, level_to_char[level], location, prefix, local_t, suffix);
pa_xfree(local_t);
}
@@ -177,9 +219,9 @@ void pa_log_levelv_meta(
local_t = pa_utf8_to_locale(t);
if (!local_t)
- syslog(level_to_syslog[level], "%s%s", location, t);
+ syslog(level_to_syslog[level], "%s%s%s", timestamp, location, t);
else {
- syslog(level_to_syslog[level], "%s%s", location, local_t);
+ syslog(level_to_syslog[level], "%s%s%s", timestamp, location, local_t);
pa_xfree(local_t);
}
@@ -189,11 +231,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%s", timestamp, location, t);
user_log_func(level, x);
- pa_xfree(x);
break;
}
@@ -204,8 +245,7 @@ void pa_log_levelv_meta(
}
}
- pa_xfree(text);
- pa_xfree(location);
+ errno = saved_errno;
}
void pa_log_level_meta(
diff --git a/src/pulsecore/log.h b/src/pulsecore/log.h
index c8ef5d0..633227f 100644
--- a/src/pulsecore/log.h
+++ b/src/pulsecore/log.h
@@ -1,8 +1,6 @@
#ifndef foologhfoo
#define foologhfoo
-/* $Id: log.h 1426 2007-02-13 15:35:19Z ossman $ */
-
/***
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 */
@@ -51,8 +49,10 @@ typedef enum pa_log_level {
/* Set an identification for the current daemon. Used when logging to syslog. */
void pa_log_set_ident(const char *p);
+typedef void (*pa_log_func_t)(pa_log_level_t t, const char*s);
+
/* Set another log target. If t is PA_LOG_USER you may specify a function that is called every log string */
-void pa_log_set_target(pa_log_target_t t, void (*func)(pa_log_level_t t, const char*s));
+void pa_log_set_target(pa_log_target_t t, pa_log_func_t func);
/* Minimal log level */
void pa_log_set_maximal_level(pa_log_level_t l);
diff --git a/src/pulsecore/ltdl-helper.c b/src/pulsecore/ltdl-helper.c
index c994622..0d4c22f 100644
--- a/src/pulsecore/ltdl-helper.c
+++ b/src/pulsecore/ltdl-helper.c
@@ -1,5 +1,3 @@
-/* $Id: ltdl-helper.c 1971 2007-10-28 19:13:50Z lennart $ */
-
/***
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 4280ead..ea73de5 100644
--- a/src/pulsecore/ltdl-helper.h
+++ b/src/pulsecore/ltdl-helper.h
@@ -1,8 +1,6 @@
#ifndef foopulsecoreltdlhelperhfoo
#define foopulsecoreltdlhelperhfoo
-/* $Id: ltdl-helper.h 1971 2007-10-28 19:13:50Z lennart $ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/macro.h b/src/pulsecore/macro.h
index b4ca0c7..39e9b58 100644
--- a/src/pulsecore/macro.h
+++ b/src/pulsecore/macro.h
@@ -1,8 +1,6 @@
#ifndef foopulsemacrohfoo
#define foopulsemacrohfoo
-/* $Id: macro.h 2194 2008-03-30 01:42:34Z lennart $ */
-
/***
This file is part of PulseAudio.
@@ -33,12 +31,22 @@
#include <stdlib.h>
#include <pulsecore/log.h>
-#include <pulsecore/gccmacro.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)
@@ -67,19 +75,53 @@ 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
-#ifndef CLAMP
-#define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x)))
+#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
+#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.*/
@@ -166,8 +208,17 @@ typedef int pa_bool_t;
#define PA_PATH_SEP_CHAR '/'
#endif
-static inline const char *pa_strnull(const char *x) {
- return x ? x : "(null)";
-}
+#if defined(__GNUC__) && defined(__ELF__)
+
+#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 5500f29..a03d5ae 100644
--- a/src/pulsecore/mcalign.c
+++ b/src/pulsecore/mcalign.c
@@ -1,5 +1,3 @@
-/* $Id: mcalign.c 1971 2007-10-28 19:13:50Z lennart $ */
-
/***
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 ad997b2..e82eb00 100644
--- a/src/pulsecore/mcalign.h
+++ b/src/pulsecore/mcalign.h
@@ -1,8 +1,6 @@
#ifndef foomcalignhfoo
#define foomcalignhfoo
-/* $Id: mcalign.h 1426 2007-02-13 15:35:19Z ossman $ */
-
/***
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 c4d33a8..d9e1bf1 100644
--- a/src/pulsecore/memblock.c
+++ b/src/pulsecore/memblock.c
@@ -1,5 +1,3 @@
-/* $Id: memblock.c 1971 2007-10-28 19:13:50Z lennart $ */
-
/***
This file is part of PulseAudio.
@@ -33,6 +31,10 @@
#include <signal.h>
#include <errno.h>
+#ifdef HAVE_VALGRIND_MEMCHECK_H
+#include <valgrind/memcheck.h>
+#endif
+
#include <pulse/xmalloc.h>
#include <pulse/def.h>
@@ -46,8 +48,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 +65,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 +133,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;
@@ -159,14 +162,14 @@ static void stat_add(pa_memblock*b) {
pa_assert(b->pool);
pa_atomic_inc(&b->pool->stat.n_allocated);
- pa_atomic_add(&b->pool->stat.allocated_size, b->length);
+ pa_atomic_add(&b->pool->stat.allocated_size, (int) b->length);
pa_atomic_inc(&b->pool->stat.n_accumulated);
- pa_atomic_add(&b->pool->stat.accumulated_size, b->length);
+ pa_atomic_add(&b->pool->stat.accumulated_size, (int) b->length);
if (b->type == PA_MEMBLOCK_IMPORTED) {
pa_atomic_inc(&b->pool->stat.n_imported);
- pa_atomic_add(&b->pool->stat.imported_size, b->length);
+ pa_atomic_add(&b->pool->stat.imported_size, (int) b->length);
}
pa_atomic_inc(&b->pool->stat.n_allocated_by_type[b->type]);
@@ -182,14 +185,14 @@ static void stat_remove(pa_memblock *b) {
pa_assert(pa_atomic_load(&b->pool->stat.allocated_size) >= (int) b->length);
pa_atomic_dec(&b->pool->stat.n_allocated);
- pa_atomic_sub(&b->pool->stat.allocated_size, b->length);
+ pa_atomic_sub(&b->pool->stat.allocated_size, (int) b->length);
if (b->type == PA_MEMBLOCK_IMPORTED) {
pa_assert(pa_atomic_load(&b->pool->stat.n_imported) > 0);
pa_assert(pa_atomic_load(&b->pool->stat.imported_size) >= (int) b->length);
pa_atomic_dec(&b->pool->stat.n_imported);
- pa_atomic_sub(&b->pool->stat.imported_size, b->length);
+ pa_atomic_sub(&b->pool->stat.imported_size, (int) b->length);
}
pa_atomic_dec(&b->pool->stat.n_allocated_by_type[b->type]);
@@ -202,7 +205,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 +218,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);
@@ -249,23 +252,27 @@ static struct mempool_slot* mempool_allocate_slot(pa_mempool *p) {
if ((unsigned) (idx = pa_atomic_inc(&p->n_init)) >= p->n_blocks)
pa_atomic_dec(&p->n_init);
else
- slot = (struct mempool_slot*) ((uint8_t*) p->memory.ptr + (p->block_size * idx));
+ slot = (struct mempool_slot*) ((uint8_t*) p->memory.ptr + (p->block_size * (size_t) idx));
if (!slot) {
- pa_log_debug("Pool full");
+ pa_log_info("Pool full");
pa_atomic_inc(&p->stat.n_pool_full);
return NULL;
}
}
+/* #ifdef HAVE_VALGRIND_MEMCHECK_H */
+/* if (PA_UNLIKELY(pa_in_valgrind())) { */
+/* VALGRIND_MALLOCLIKE_BLOCK(slot, p->block_size, 0, 0); */
+/* } */
+/* #endif */
+
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 */
@@ -275,7 +282,7 @@ static unsigned mempool_slot_idx(pa_mempool *p, void *ptr) {
pa_assert((uint8_t*) ptr >= (uint8_t*) p->memory.ptr);
pa_assert((uint8_t*) ptr < (uint8_t*) p->memory.ptr + p->memory.size);
- return ((uint8_t*) ptr - (uint8_t*) p->memory.ptr) / p->block_size;
+ return (unsigned) ((size_t) ((uint8_t*) ptr - (uint8_t*) p->memory.ptr) / p->block_size);
}
/* No lock necessary */
@@ -294,7 +301,7 @@ 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. */
@@ -302,7 +309,7 @@ pa_memblock *pa_memblock_new_pool(pa_mempool *p, size_t length) {
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,7 +318,7 @@ 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;
@@ -323,14 +330,14 @@ pa_memblock *pa_memblock_new_pool(pa_mempool *p, size_t length) {
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 +347,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 +361,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 +372,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 +387,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 +400,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 +408,27 @@ int pa_memblock_is_read_only(pa_memblock *b) {
}
/* No lock necessary */
-int pa_memblock_ref_is_one(pa_memblock *b) {
- int r;
+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);
- r = PA_REFCNT_VALUE(b);
- pa_assert(r > 0);
+ b->is_silence = v;
+}
+
+/* No lock necessary */
+pa_bool_t pa_memblock_ref_is_one(pa_memblock *b) {
+ int r;
+ pa_assert(b);
+
+ pa_assert_se((r = PA_REFCNT_VALUE(b)) > 0);
return r == 1;
}
@@ -506,13 +529,19 @@ static void memblock_free(pa_memblock *b) {
case PA_MEMBLOCK_POOL_EXTERNAL:
case PA_MEMBLOCK_POOL: {
struct mempool_slot *slot;
- int call_free;
+ pa_bool_t call_free;
slot = mempool_slot_by_ptr(b->pool, pa_atomic_ptr_load(&b->data));
pa_assert(slot);
call_free = b->type == PA_MEMBLOCK_POOL_EXTERNAL;
+/* #ifdef HAVE_VALGRIND_MEMCHECK_H */
+/* if (PA_UNLIKELY(pa_in_valgrind())) { */
+/* VALGRIND_FREELIKE_BLOCK(slot, b->pool->block_size); */
+/* } */
+/* #endif */
+
/* The free list dimensions should easily allow all slots
* to fit in, hence try harder if pushing this slot into
* the free list fails */
@@ -567,7 +596,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 +608,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 +619,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]);
@@ -634,7 +663,7 @@ static void memblock_replace_import(pa_memblock *b) {
pa_assert(pa_atomic_load(&b->pool->stat.n_imported) > 0);
pa_assert(pa_atomic_load(&b->pool->stat.imported_size) >= (int) b->length);
pa_atomic_dec(&b->pool->stat.n_imported);
- pa_atomic_sub(&b->pool->stat.imported_size, b->length);
+ pa_atomic_sub(&b->pool->stat.imported_size, (int) b->length);
seg = b->per_type.imported.segment;
pa_assert(seg);
@@ -655,8 +684,9 @@ static void memblock_replace_import(pa_memblock *b) {
pa_mutex_unlock(seg->import->mutex);
}
-pa_mempool* pa_mempool_new(int shared) {
+pa_mempool* pa_mempool_new(pa_bool_t shared, size_t size) {
pa_mempool *p;
+ char t1[64], t2[64];
p = pa_xnew(pa_mempool, 1);
@@ -667,22 +697,33 @@ pa_mempool* pa_mempool_new(int shared) {
if (p->block_size < PA_PAGE_SIZE)
p->block_size = PA_PAGE_SIZE;
- p->n_blocks = PA_MEMPOOL_SLOTS_MAX;
+ if (size <= 0)
+ p->n_blocks = PA_MEMPOOL_SLOTS_MAX;
+ else {
+ p->n_blocks = (unsigned) (size / p->block_size);
- pa_assert(p->block_size > PA_ALIGN(sizeof(struct mempool_slot)));
+ if (p->n_blocks < 2)
+ p->n_blocks = 2;
+ }
if (pa_shm_create_rw(&p->memory, p->n_blocks * p->block_size, shared, 0700) < 0) {
pa_xfree(p);
return NULL;
}
+ pa_log_debug("Using %s memory pool with %u slots of size %s each, total size is %s",
+ p->memory.shared ? "shared" : "private",
+ p->n_blocks,
+ pa_bytes_snprint(t1, sizeof(t1), (unsigned) p->block_size),
+ pa_bytes_snprint(t2, sizeof(t2), (unsigned) (p->n_blocks * p->block_size)));
+
memset(&p->stat, 0, sizeof(p->stat));
pa_atomic_store(&p->n_init, 0);
PA_LLIST_HEAD_INIT(pa_memimport, p->imports);
PA_LLIST_HEAD_INIT(pa_memexport, p->exports);
- p->free_slots = pa_flist_new(p->n_blocks*2);
+ p->free_slots = pa_flist_new(p->n_blocks);
return p;
}
@@ -726,7 +767,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 */
@@ -736,16 +777,14 @@ void pa_mempool_vacuum(pa_mempool *p) {
pa_assert(p);
- list = pa_flist_new(p->n_blocks*2);
+ list = pa_flist_new(p->n_blocks);
while ((slot = pa_flist_pop(p->free_slots)))
while (pa_flist_push(list, slot) < 0)
;
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, (size_t) ((uint8_t*) slot - (uint8_t*) p->memory.ptr), p->block_size);
while (pa_flist_push(p->free_slots, slot))
;
@@ -767,7 +806,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;
@@ -836,7 +875,7 @@ void pa_memimport_free(pa_memimport *i) {
pa_mutex_lock(i->mutex);
- while ((b = pa_hashmap_get_first(i->blocks)))
+ while ((b = pa_hashmap_first(i->blocks)))
memblock_replace_import(b);
pa_assert(pa_hashmap_size(i->segments) == 0);
@@ -886,7 +925,8 @@ pa_memblock* pa_memimport_get(pa_memimport *i, uint32_t block_id, uint32_t shm_i
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);
@@ -957,7 +997,7 @@ void pa_memexport_free(pa_memexport *e) {
pa_mutex_lock(e->mutex);
while (e->used_slots)
- pa_memexport_process_release(e, e->used_slots - e->slots);
+ pa_memexport_process_release(e, (uint32_t) (e->used_slots - e->slots));
pa_mutex_unlock(e->mutex);
pa_mutex_lock(e->pool->mutex);
@@ -996,7 +1036,7 @@ int pa_memexport_process_release(pa_memexport *e, uint32_t id) {
pa_assert(pa_atomic_load(&e->pool->stat.exported_size) >= (int) b->length);
pa_atomic_dec(&e->pool->stat.n_exported);
- pa_atomic_sub(&e->pool->stat.exported_size, b->length);
+ pa_atomic_sub(&e->pool->stat.exported_size, (int) b->length);
pa_memblock_unref(b);
@@ -1024,7 +1064,7 @@ static void memexport_revoke_blocks(pa_memexport *e, pa_memimport *i) {
slot->block->per_type.imported.segment->import != i)
continue;
- idx = slot - e->slots;
+ idx = (uint32_t) (slot - e->slots);
e->revoke_cb(e, idx, e->userdata);
pa_memexport_process_release(e, idx);
}
@@ -1085,7 +1125,7 @@ int pa_memexport_put(pa_memexport *e, pa_memblock *b, uint32_t *block_id, uint32
PA_LLIST_PREPEND(struct memexport_slot, e->used_slots, slot);
slot->block = b;
- *block_id = slot - e->slots;
+ *block_id = (uint32_t) (slot - e->slots);
pa_mutex_unlock(e->mutex);
/* pa_log("Got block id %u", *block_id); */
@@ -1105,13 +1145,13 @@ int pa_memexport_put(pa_memexport *e, pa_memblock *b, uint32_t *block_id, uint32
pa_assert((uint8_t*) data + b->length <= (uint8_t*) memory->ptr + memory->size);
*shm_id = memory->id;
- *offset = (uint8_t*) data - (uint8_t*) memory->ptr;
+ *offset = (size_t) ((uint8_t*) data - (uint8_t*) memory->ptr);
*size = b->length;
pa_memblock_release(b);
pa_atomic_inc(&e->pool->stat.n_exported);
- pa_atomic_add(&e->pool->stat.exported_size, b->length);
+ pa_atomic_add(&e->pool->stat.exported_size, (int) b->length);
return 0;
}
diff --git a/src/pulsecore/memblock.h b/src/pulsecore/memblock.h
index 0955d0b..b1eab2a 100644
--- a/src/pulsecore/memblock.h
+++ b/src/pulsecore/memblock.h
@@ -1,8 +1,6 @@
#ifndef foopulsememblockhfoo
#define foopulsememblockhfoo
-/* $Id: memblock.h 1971 2007-10-28 19:13:50Z lennart $ */
-
/***
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, size_t size);
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 e719151..265da37 100644
--- a/src/pulsecore/memblockq.c
+++ b/src/pulsecore/memblockq.c
@@ -1,5 +1,3 @@
-/* $Id: memblockq.c 2063 2007-11-21 01:19:28Z lennart $ */
-
/***
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,7 +66,8 @@ 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;
@@ -75,27 +75,35 @@ pa_memblockq* pa_memblockq_new(
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);
+ 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);
- bq->missing = bq->requested = bq->maxlength = bq->tlength = bq->prebuf = bq->minreq = 0;
+ bq->missing = 0;
+ bq->requested = bq->maxlength = bq->tlength = bq->prebuf = bq->minreq = bq->maxrewind = 0;
bq->in_prebuf = TRUE;
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 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);
- 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);
+ if (silence) {
+ bq->silence = *silence;
+ pa_memblock_ref(bq->silence.memblock);
+ } else
+ pa_memchunk_reset(&bq->silence);
- bq->silence = silence ? pa_memblock_ref(silence) : NULL;
- bq->mcalign = NULL;
+ bq->mcalign = pa_mcalign_new(bq->base);
return bq;
}
@@ -103,10 +111,10 @@ pa_memblockq* pa_memblockq_new(
void pa_memblockq_free(pa_memblockq* bq) {
pa_assert(bq);
- pa_memblockq_flush(bq);
+ pa_memblockq_silence(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);
@@ -114,6 +122,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);
@@ -122,13 +186,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);
@@ -138,25 +212,35 @@ 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 - (int64_t) 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;
pa_assert(bq);
if (bq->read_index > bq->write_index) {
- size_t d = bq->read_index - bq->write_index;
+ int64_t d = bq->read_index - bq->write_index;
- if (l > d)
- l -= d;
+ if ((int64_t) l > d)
+ l -= (size_t) d;
else
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 + l - bq->read_index > bq->maxlength)
+ if (bq->write_index + (int64_t) l > end)
+ if (bq->write_index + (int64_t) l - bq->read_index > (int64_t) bq->maxlength)
return FALSE;
return TRUE;
@@ -182,28 +266,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)
@@ -213,7 +295,7 @@ int pa_memblockq_push(pa_memblockq* bq, const pa_memchunk *uchunk) {
/* This entry isn't touched at all, let's skip it */
q = q->prev;
} else if (bq->write_index <= q->index &&
- bq->write_index + chunk.length >= q->index + q->chunk.length) {
+ bq->write_index + (int64_t) chunk.length >= q->index + (int64_t) q->chunk.length) {
/* This entry is fully replaced by the new entry, so let's drop it */
@@ -225,7 +307,7 @@ int pa_memblockq_push(pa_memblockq* bq, const pa_memchunk *uchunk) {
/* The write index points into this memblock, so let's
* truncate or split it */
- if (bq->write_index + chunk.length < q->index + q->chunk.length) {
+ if (bq->write_index + (int64_t) chunk.length < q->index + (int64_t) q->chunk.length) {
/* We need to save the end of this memchunk */
struct list_item *p;
@@ -239,11 +321,11 @@ int pa_memblockq_push(pa_memblockq* bq, const pa_memchunk *uchunk) {
pa_memblock_ref(p->chunk.memblock);
/* Calculate offset */
- d = bq->write_index + chunk.length - q->index;
+ d = (size_t) (bq->write_index + (int64_t) chunk.length - q->index);
pa_assert(d > 0);
/* Drop it from the new entry */
- p->index = q->index + d;
+ p->index = q->index + (int64_t) d;
p->chunk.length -= d;
/* Add it to the list */
@@ -258,7 +340,7 @@ int pa_memblockq_push(pa_memblockq* bq, const pa_memchunk *uchunk) {
}
/* Truncate the chunk */
- if (!(q->chunk.length = bq->write_index - q->index)) {
+ if (!(q->chunk.length = (size_t) (bq->write_index - q->index))) {
struct list_item *p;
p = q;
q = q->prev;
@@ -276,8 +358,8 @@ int pa_memblockq_push(pa_memblockq* bq, const pa_memchunk *uchunk) {
/* The job overwrites the current entry at the end, so let's drop the beginning of this entry */
- d = bq->write_index + chunk.length - q->index;
- q->index += d;
+ d = (size_t) (bq->write_index + (int64_t) chunk.length - q->index);
+ q->index += (int64_t) d;
q->chunk.index += d;
q->chunk.length -= d;
@@ -292,11 +374,11 @@ int pa_memblockq_push(pa_memblockq* bq, const pa_memchunk *uchunk) {
/* Try to merge memory blocks */
if (q->chunk.memblock == chunk.memblock &&
- q->chunk.index + (int64_t)q->chunk.length == chunk.index &&
- bq->write_index == q->index + (int64_t)q->chunk.length) {
+ q->chunk.index + q->chunk.length == chunk.index &&
+ bq->write_index == q->index + (int64_t) q->chunk.length) {
q->chunk.length += chunk.length;
- bq->write_index += chunk.length;
+ bq->write_index += (int64_t) chunk.length;
goto finish;
}
} else
@@ -308,7 +390,7 @@ int pa_memblockq_push(pa_memblockq* bq, const pa_memchunk *uchunk) {
n->chunk = chunk;
pa_memblock_ref(n->chunk.memblock);
n->index = bq->write_index;
- bq->write_index += n->chunk.length;
+ bq->write_index += (int64_t) n->chunk.length;
n->next = q ? q->next : bq->blocks;
n->prev = q;
@@ -329,11 +411,11 @@ finish:
delta = bq->write_index - old;
- if (delta >= bq->requested) {
- delta -= bq->requested;
+ if (delta >= (int64_t) bq->requested) {
+ delta -= (int64_t) bq->requested;
bq->requested = 0;
} else {
- bq->requested -= delta;
+ bq->requested -= (size_t) delta;
delta = 0;
}
@@ -342,7 +424,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) {
@@ -364,34 +455,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 = (size_t) (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;
@@ -403,11 +502,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 += (size_t) d;
+ chunk->length -= (size_t) d;
+
return 0;
}
@@ -421,68 +523,61 @@ 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);
+ fix_current_read(bq);
- d = (size_t) (bq->blocks->index - bq->read_index);
+ if (bq->current_read) {
+ int64_t p, d;
- if (d >= length) {
- /* The first block is too far in the future */
+ /* We go through this piece by piece to make sure we don't
+ * drop more than allowed by prebuf */
- bq->read_index += length;
- break;
- } else {
+ p = bq->current_read->index + (int64_t) bq->current_read->chunk.length;
+ pa_assert(p >= bq->read_index);
+ d = p - bq->read_index;
- length -= d;
- bq->read_index += d;
- }
+ if (d > (int64_t) length)
+ d = (int64_t) length;
- pa_assert(bq->blocks->index == bq->read_index);
-
- if (bq->blocks->chunk.length <= length) {
- /* We need to drop the full block */
-
- length -= bq->blocks->chunk.length;
- bq->read_index += bq->blocks->chunk.length;
-
- drop_block(bq, bq->blocks);
- } else {
- /* Only the start of this block needs to be dropped */
-
- bq->blocks->chunk.index += length;
- bq->blocks->chunk.length -= length;
- bq->blocks->index += length;
- bq->read_index += length;
- break;
- }
+ bq->read_index += d;
+ length -= (size_t) d;
} else {
/* The list is empty, there's nothing we could drop */
- bq->read_index += length;
+ bq->read_index += (int64_t) length;
break;
}
}
+ 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 -= (int64_t) length;
+ bq->missing -= (int64_t) 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) {
@@ -506,12 +601,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);
@@ -535,27 +624,26 @@ 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) {
- delta -= bq->requested;
+ if (delta >= (int64_t) bq->requested) {
+ delta -= (int64_t) bq->requested;
bq->requested = 0;
} else if (delta >= 0) {
- bq->requested -= delta;
+ bq->requested -= (size_t) delta;
delta = 0;
}
bq->missing -= delta;
}
-void pa_memblockq_flush(pa_memblockq *bq) {
+void pa_memblockq_flush_write(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;
@@ -564,30 +652,53 @@ void pa_memblockq_flush(pa_memblockq *bq) {
delta = bq->write_index - old;
- if (delta > bq->requested) {
- delta -= bq->requested;
+ if (delta >= (int64_t) bq->requested) {
+ delta -= (int64_t) bq->requested;
bq->requested = 0;
} else if (delta >= 0) {
- bq->requested -= delta;
+ bq->requested -= (size_t) delta;
delta = 0;
}
bq->missing -= delta;
}
+void pa_memblockq_flush_read(pa_memblockq *bq) {
+ int64_t old, delta;
+ pa_assert(bq);
+
+ pa_memblockq_silence(bq);
+
+ old = bq->read_index;
+ bq->read_index = bq->write_index;
+
+ pa_memblockq_prebuf_force(bq);
+
+ delta = bq->read_index - old;
+ bq->missing += delta;
+}
+
size_t pa_memblockq_get_tlength(pa_memblockq *bq) {
pa_assert(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;
}
@@ -600,9 +711,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;
@@ -613,23 +721,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);
@@ -639,7 +739,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;
}
@@ -691,18 +791,20 @@ void pa_memblockq_set_tlength(pa_memblockq *bq, size_t tlength) {
size_t old_tlength;
pa_assert(bq);
- old_tlength = bq->tlength;
-
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->minreq > bq->tlength - bq->prebuf)
- pa_memblockq_set_minreq(bq, bq->tlength - bq->prebuf);
+ 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;
}
@@ -710,20 +812,22 @@ void pa_memblockq_set_tlength(pa_memblockq *bq, size_t tlength) {
void pa_memblockq_set_prebuf(pa_memblockq *bq, size_t prebuf) {
pa_assert(bq);
- bq->prebuf = (prebuf == (size_t) -1) ? bq->tlength/2 : prebuf;
- bq->prebuf = ((bq->prebuf+bq->base-1)/bq->base)*bq->base;
+ 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->maxlength)
- bq->prebuf = bq->maxlength;
+ 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->tlength - bq->prebuf)
- pa_memblockq_set_minreq(bq, bq->tlength - bq->prebuf);
+ if (bq->minreq > bq->prebuf)
+ pa_memblockq_set_minreq(bq, bq->prebuf);
}
void pa_memblockq_set_minreq(pa_memblockq *bq, size_t minreq) {
@@ -731,9 +835,99 @@ void pa_memblockq_set_minreq(pa_memblockq *bq, size_t minreq) {
bq->minreq = (minreq/bq->base)*bq->base;
- if (bq->minreq > bq->tlength - bq->prebuf)
- bq->minreq = bq->tlength - bq->prebuf;
+ 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, (int64_t) 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 a251c7c..31f908d 100644
--- a/src/pulsecore/memblockq.h
+++ b/src/pulsecore/memblockq.h
@@ -1,8 +1,6 @@
#ifndef foomemblockqhfoo
#define foomemblockqhfoo
-/* $Id: memblockq.h 2063 2007-11-21 01:19:28Z lennart $ */
-
/***
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);
@@ -83,6 +84,9 @@ int pa_memblockq_push(pa_memblockq* bq, const pa_memchunk *chunk);
* you know what you do. */
int pa_memblockq_push_align(pa_memblockq* bq, const pa_memchunk *chunk);
+/* Manipulate the write pointer */
+void pa_memblockq_seek(pa_memblockq *bq, int64_t offset, pa_seek_mode_t seek);
+
/* Return a copy of the next memory chunk in the queue. It is not
* removed from the queue. There are two reasons this function might
* fail: 1. prebuffering is active, 2. queue is empty and no silence
@@ -94,8 +98,11 @@ int pa_memblockq_peek(pa_memblockq* bq, pa_memchunk *chunk);
/* Drop the specified bytes from the queue. */
void pa_memblockq_drop(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);
+
/* 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,28 +114,14 @@ 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);
-/* Returns the minimal request value */
-size_t pa_memblockq_get_minreq(pa_memblockq *bq);
-
-/* Manipulate the write pointer */
-void pa_memblockq_seek(pa_memblockq *bq, int64_t offset, pa_seek_mode_t seek);
+/* Directly moves the data from the source memblockq into bq */
+int pa_memblockq_splice(pa_memblockq *bq, pa_memblockq *source);
/* Set the queue to silence, set write index to read index */
-void pa_memblockq_flush(pa_memblockq *bq);
-
-/* Get Target length */
-size_t pa_memblockq_get_tlength(pa_memblockq *bq);
-
-/* Return the current read index */
-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);
+void pa_memblockq_flush_write(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);
+/* Set the queue to silence, set write read index to write index*/
+void pa_memblockq_flush_read(pa_memblockq *bq);
/* Ignore prebuf for now */
void pa_memblockq_prebuf_disable(pa_memblockq *bq);
@@ -139,13 +132,48 @@ void pa_memblockq_prebuf_force(pa_memblockq *bq);
/* Return the maximum length of the queue in bytes */
size_t pa_memblockq_get_maxlength(pa_memblockq *bq);
+/* Get Target length */
+size_t pa_memblockq_get_tlength(pa_memblockq *bq);
+
/* Return the prebuffer length in bytes */
size_t pa_memblockq_get_prebuf(pa_memblockq *bq);
-/* Change metrics. */
-void pa_memblockq_set_maxlength(pa_memblockq *memblockq, size_t maxlength);
-void pa_memblockq_set_tlength(pa_memblockq *memblockq, size_t tlength);
-void pa_memblockq_set_prebuf(pa_memblockq *memblockq, size_t prebuf);
+/* Returns the minimal request value */
+size_t pa_memblockq_get_minreq(pa_memblockq *bq);
+
+/* Return the base unit in bytes */
+size_t pa_memblockq_get_base(pa_memblockq *bq);
+
+/* Return the current read index */
+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);
+
+/* 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 maxrewind); /* 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);
+
+/* Drop everything in the queue, but don't modify the indexes */
+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);
+
+/* Return how many items are currently stored in the queue */
+unsigned pa_memblockq_get_nblocks(pa_memblockq *bq);
+
#endif
diff --git a/src/pulsecore/memchunk.c b/src/pulsecore/memchunk.c
index e870416..0bbf859 100644
--- a/src/pulsecore/memchunk.c
+++ b/src/pulsecore/memchunk.c
@@ -1,5 +1,3 @@
-/* $Id: memchunk.c 1971 2007-10-28 19:13:50Z lennart $ */
-
/***
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 20a8432..9458f4f 100644
--- a/src/pulsecore/memchunk.h
+++ b/src/pulsecore/memchunk.h
@@ -1,8 +1,6 @@
#ifndef foomemchunkhfoo
#define foomemchunkhfoo
-/* $Id: memchunk.h 1971 2007-10-28 19:13:50Z lennart $ */
-
/***
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 4c19a18..9e60125 100644
--- a/src/pulsecore/modargs.c
+++ b/src/pulsecore/modargs.c
@@ -1,5 +1,3 @@
-/* $Id: modargs.c 2050 2007-11-13 17:37:44Z lennart $ */
-
/***
This file is part of PulseAudio.
@@ -53,6 +51,12 @@ static int add_key_value(pa_hashmap *map, char *key, char *value, const char* co
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++)
@@ -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;
@@ -169,7 +183,7 @@ fail:
return NULL;
}
-static void free_func(void *p, PA_GCC_UNUSED void*userdata) {
+static void free_func(void *p, void*userdata) {
struct entry *e = p;
pa_assert(e);
@@ -308,8 +322,7 @@ int pa_modargs_get_sample_spec_and_channel_map(pa_modargs *ma, pa_sample_spec *r
if (pa_modargs_get_sample_spec(ma, &ss) < 0)
return -1;
- if (!pa_channel_map_init_auto(&map, ss.channels, def))
- map.channels = 0;
+ pa_channel_map_init_extend(&map, ss.channels, def);
if (pa_modargs_get_channel_map(ma, NULL, &map) < 0)
return -1;
diff --git a/src/pulsecore/modargs.h b/src/pulsecore/modargs.h
index 462f721..23766cf 100644
--- a/src/pulsecore/modargs.h
+++ b/src/pulsecore/modargs.h
@@ -1,8 +1,6 @@
#ifndef foomodargshfoo
#define foomodargshfoo
-/* $Id: modargs.h 2050 2007-11-13 17:37:44Z lennart $ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/modinfo.c b/src/pulsecore/modinfo.c
index e606936..ac4ca88 100644
--- a/src/pulsecore/modinfo.c
+++ b/src/pulsecore/modinfo.c
@@ -1,5 +1,3 @@
-/* $Id: modinfo.c 2043 2007-11-09 18:25:40Z lennart $ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/modinfo.h b/src/pulsecore/modinfo.h
index ea8a520..605637c 100644
--- a/src/pulsecore/modinfo.h
+++ b/src/pulsecore/modinfo.h
@@ -1,8 +1,6 @@
#ifndef foomodinfohfoo
#define foomodinfohfoo
-/* $Id: modinfo.h 2043 2007-11-09 18:25:40Z lennart $ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/module.c b/src/pulsecore/module.c
index a293bc8..9b17cb9 100644
--- a/src/pulsecore/module.c
+++ b/src/pulsecore/module.c
@@ -1,5 +1,3 @@
-/* $Id: module.c 2049 2007-11-13 17:35:48Z lennart $ */
-
/***
This file is part of PulseAudio.
@@ -50,7 +48,7 @@
#define UNLOAD_POLL_TIME 2
-static void timeout_callback(pa_mainloop_api *m, pa_time_event*e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) {
+static void timeout_callback(pa_mainloop_api *m, pa_time_event*e, const struct timeval *tv, void *userdata) {
pa_core *c = PA_CORE(userdata);
struct timeval ntv;
@@ -61,7 +59,7 @@ static void timeout_callback(pa_mainloop_api *m, pa_time_event*e, PA_GCC_UNUSED
pa_module_unload_unused(c);
pa_gettimeofday(&ntv);
- pa_timeval_add(&ntv, UNLOAD_POLL_TIME*1000000);
+ pa_timeval_add(&ntv, UNLOAD_POLL_TIME*PA_USEC_PER_SEC);
m->time_restart(e, &ntv);
}
@@ -78,6 +76,7 @@ pa_module* pa_module_load(pa_core *c, const char *name, const char *argument) {
m = pa_xnew(pa_module, 1);
m->name = pa_xstrdup(name);
m->argument = pa_xstrdup(argument);
+ m->load_once = FALSE;
if (!(m->dl = lt_dlopenext(name))) {
pa_log("Failed to open module \"%s\": %s", name, lt_dlerror());
@@ -86,7 +85,9 @@ pa_module* pa_module_load(pa_core *c, const char *name, const char *argument) {
if ((load_once = (pa_bool_t (*)(void)) pa_load_sym(m->dl, name, PA_SYMBOL_LOAD_ONCE))) {
- if (load_once() && c->modules) {
+ m->load_once = load_once();
+
+ if (m->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 */
@@ -109,8 +110,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 : "");
@@ -120,10 +121,10 @@ 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);
+ pa_timeval_add(&ntv, UNLOAD_POLL_TIME*PA_USEC_PER_SEC);
c->module_auto_unload_event = c->mainloop->time_new(c->mainloop, &ntv, timeout_callback, c);
}
@@ -155,9 +156,6 @@ static void pa_module_free(pa_module *m) {
pa_assert(m);
pa_assert(m->core);
- if (m->core->disallow_module_loading)
- return;
-
pa_log_info("Unloading \"%s\" (index: #%u).", m->name, m->index);
if (m->done)
@@ -174,47 +172,45 @@ static void pa_module_free(pa_module *m) {
pa_xfree(m);
}
-void pa_module_unload(pa_core *c, pa_module *m) {
+void pa_module_unload(pa_core *c, pa_module *m, pa_bool_t force) {
pa_assert(c);
pa_assert(m);
- pa_assert(c->modules);
+ if (m->core->disallow_module_loading && !force)
+ return;
+
if (!(m = pa_idxset_remove_by_data(c->modules, m, NULL)))
return;
pa_module_free(m);
}
-void pa_module_unload_by_index(pa_core *c, uint32_t idx) {
+void pa_module_unload_by_index(pa_core *c, uint32_t idx, pa_bool_t force) {
pa_module *m;
pa_assert(c);
pa_assert(idx != PA_IDXSET_INVALID);
- if (!(m = pa_idxset_remove_by_index(c->modules, idx)))
+ if (c->disallow_module_loading && !force)
return;
- pa_module_free(m);
-}
+ if (!(m = pa_idxset_remove_by_index(c->modules, idx)))
+ return;
-static void free_callback(void *p, PA_GCC_UNUSED void *userdata) {
- pa_module *m = p;
- pa_assert(m);
pa_module_free(m);
}
void pa_module_unload_all(pa_core *c) {
- pa_module *m;
-
pa_assert(c);
- if (!c->modules)
- return;
+ if (c->modules) {
+ pa_module *m;
- while ((m = pa_idxset_first(c->modules, NULL)))
- pa_module_unload(c, m);
+ while ((m = pa_idxset_steal_first(c->modules, NULL)))
+ pa_module_free(m);
- pa_idxset_free(c->modules, free_callback, NULL);
- c->modules = NULL;
+ pa_idxset_free(c->modules, NULL, NULL);
+ c->modules = NULL;
+ }
if (c->module_auto_unload_event) {
c->mainloop->time_free(c->module_auto_unload_event);
@@ -227,61 +223,56 @@ 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);
-
- if (m->n_used == 0 && m->auto_unload && m->last_used_time+m->core->module_idle_time <= *now) {
- pa_module_free(m);
- *del = 1;
- }
-
- return 0;
-}
-
void pa_module_unload_unused(pa_core *c) {
+ void *state = NULL;
time_t now;
+ pa_module *m;
+
pa_assert(c);
if (!c->modules)
return;
time(&now);
- pa_idxset_foreach(c->modules, unused_callback, &now);
-}
-static int unload_callback(void *p, PA_GCC_UNUSED uint32_t idx, int *del, PA_GCC_UNUSED void *userdata) {
- pa_module *m = p;
- pa_assert(m);
+ while ((m = pa_idxset_iterate(c->modules, &state, NULL))) {
- if (m->unload_requested) {
- pa_module_free(m);
- *del = 1;
- }
+ if (m->n_used > 0)
+ continue;
- return 0;
+ if (!m->auto_unload)
+ continue;
+
+ if (m->last_used_time + m->core->module_idle_time > now)
+ continue;
+
+ pa_module_unload(c, m, FALSE);
+ }
}
static void defer_cb(pa_mainloop_api*api, pa_defer_event *e, void *userdata) {
- pa_core *core = PA_CORE(userdata);
+ void *state = NULL;
+ pa_core *c = PA_CORE(userdata);
+ pa_module *m;
- pa_core_assert_ref(core);
+ pa_core_assert_ref(c);
api->defer_enable(e, 0);
- if (!core->modules)
+ if (!c->modules)
return;
- pa_idxset_foreach(core->modules, unload_callback, NULL);
+ while ((m = pa_idxset_iterate(c->modules, &state, NULL)))
+ if (m->unload_requested)
+ pa_module_unload(c, m, TRUE);
}
-void pa_module_unload_request(pa_module *m) {
+void pa_module_unload_request(pa_module *m, pa_bool_t force) {
pa_assert(m);
- m->unload_requested = 1;
+ if (m->core->disallow_module_loading && !force)
+ return;
+
+ 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 4e08ab1..365ab67 100644
--- a/src/pulsecore/module.h
+++ b/src/pulsecore/module.h
@@ -1,8 +1,6 @@
#ifndef foomodulehfoo
#define foomodulehfoo
-/* $Id: module.h 2043 2007-11-09 18:25:40Z lennart $ */
-
/***
This file is part of PulseAudio.
@@ -45,20 +43,22 @@ struct pa_module {
void *userdata;
int n_used;
- int auto_unload;
- time_t last_used_time;
- int unload_requested;
+ pa_bool_t auto_unload:1;
+ pa_bool_t load_once:1;
+ pa_bool_t unload_requested:1;
+
+ time_t last_used_time;
};
pa_module* pa_module_load(pa_core *c, const char *name, const char*argument);
-void pa_module_unload(pa_core *c, pa_module *m);
-void pa_module_unload_by_index(pa_core *c, uint32_t idx);
+void pa_module_unload(pa_core *c, pa_module *m, pa_bool_t force);
+void pa_module_unload_by_index(pa_core *c, uint32_t idx, pa_bool_t force);
void pa_module_unload_all(pa_core *c);
void pa_module_unload_unused(pa_core *c);
-void pa_module_unload_request(pa_module *m);
+void pa_module_unload_request(pa_module *m, pa_bool_t force);
void pa_module_set_used(pa_module*m, int used);
diff --git a/src/pulsecore/msgobject.c b/src/pulsecore/msgobject.c
index d00e8d0..81417ea 100644
--- a/src/pulsecore/msgobject.c
+++ b/src/pulsecore/msgobject.c
@@ -1,5 +1,3 @@
-/* $Id: msgobject.c 1971 2007-10-28 19:13:50Z lennart $ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/msgobject.h b/src/pulsecore/msgobject.h
index 7e386c9..1a43fa3 100644
--- a/src/pulsecore/msgobject.h
+++ b/src/pulsecore/msgobject.h
@@ -1,8 +1,6 @@
#ifndef foopulsemsgobjecthfoo
#define foopulsemsgobjecthfoo
-/* $Id: msgobject.h 1971 2007-10-28 19:13:50Z lennart $ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/mutex-posix.c b/src/pulsecore/mutex-posix.c
index 45eec65..35465b7 100644
--- a/src/pulsecore/mutex-posix.c
+++ b/src/pulsecore/mutex-posix.c
@@ -1,5 +1,3 @@
-/* $Id: mutex-posix.c 1971 2007-10-28 19:13:50Z lennart $ */
-
/***
This file is part of PulseAudio.
@@ -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 f75767c..5e884e7 100644
--- a/src/pulsecore/mutex-win32.c
+++ b/src/pulsecore/mutex-win32.c
@@ -1,5 +1,3 @@
-/* $Id: mutex-win32.c 1971 2007-10-28 19:13:50Z lennart $ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/mutex.h b/src/pulsecore/mutex.h
index 9d6f3b1..36e1d63 100644
--- a/src/pulsecore/mutex.h
+++ b/src/pulsecore/mutex.h
@@ -1,8 +1,6 @@
#ifndef foopulsemutexhfoo
#define foopulsemutexhfoo
-/* $Id: mutex.h 1971 2007-10-28 19:13:50Z lennart $ */
-
/***
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 0c11621..ecd8def 100644
--- a/src/pulsecore/namereg.c
+++ b/src/pulsecore/namereg.c
@@ -1,5 +1,3 @@
-/* $Id: namereg.c 1981 2007-10-29 20:01:49Z lennart $ */
-
/***
This file is part of PulseAudio.
@@ -47,12 +45,13 @@ struct namereg_entry {
void *data;
};
-static int is_valid_char(char c) {
+static pa_bool_t is_valid_char(char c) {
return
(c >= 'a' && c <= 'z') ||
(c >= 'A' && c <= 'Z') ||
(c >= '0' && c <= '9') ||
c == '.' ||
+ c == '-' ||
c == '_';
}
@@ -72,17 +71,17 @@ pa_bool_t pa_namereg_is_valid_name(const char *name) {
return TRUE;
}
-static char* cleanup_name(const char *name) {
+char* pa_namereg_make_valid_name(const char *name) {
const char *a;
char *b, *n;
if (*name == 0)
return NULL;
- n = pa_xnew(char, strlen(name)+1);
+ n = pa_xmalloc(strlen(name)+1);
for (a = name, b = n; *a && (a-name < PA_NAME_MAX); a++, b++)
- *b = is_valid_char(*a) ? *a : '_';
+ *b = (char) (is_valid_char(*a) ? *a : '_');
*b = 0;
@@ -99,7 +98,7 @@ void pa_namereg_free(pa_core *c) {
pa_hashmap_free(c->namereg, NULL, NULL);
}
-const char *pa_namereg_register(pa_core *c, const char *name, pa_namereg_type_t type, void *data, int fail) {
+const char *pa_namereg_register(pa_core *c, const char *name, pa_namereg_type_t type, void *data, pa_bool_t fail) {
struct namereg_entry *e;
char *n = NULL;
@@ -111,12 +110,12 @@ 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) &&
- !pa_namereg_is_valid_name(name) ) {
+ !pa_namereg_is_valid_name(name)) {
if (fail)
return NULL;
- if (!(name = n = cleanup_name(name)))
+ if (!(name = n = pa_namereg_make_valid_name(name)))
return NULL;
}
@@ -138,7 +137,7 @@ const char *pa_namereg_register(pa_core *c, const char *name, pa_namereg_type_t
return NULL;
}
- k = pa_xnew(char, l+4);
+ k = pa_xmalloc(l+4);
for (i = 2; i <= 99; i++) {
pa_snprintf(k, l+4, "%s.%u", name, i);
@@ -179,7 +178,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);
diff --git a/src/pulsecore/namereg.h b/src/pulsecore/namereg.h
index d12d8c0..f458100 100644
--- a/src/pulsecore/namereg.h
+++ b/src/pulsecore/namereg.h
@@ -1,8 +1,6 @@
#ifndef foonamereghfoo
#define foonamereghfoo
-/* $Id: namereg.h 1981 2007-10-29 20:01:49Z lennart $ */
-
/***
This file is part of PulseAudio.
@@ -37,14 +35,15 @@ typedef enum pa_namereg_type {
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);
+const char *pa_namereg_register(pa_core *c, const char *name, pa_namereg_type_t type, void *data, pa_bool_t 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);
+char* pa_namereg_make_valid_name(const char *name);
#endif
diff --git a/src/pulsecore/native-common.h b/src/pulsecore/native-common.h
index b4b57d4..8138b7a 100644
--- a/src/pulsecore/native-common.h
+++ b/src/pulsecore/native-common.h
@@ -1,8 +1,6 @@
#ifndef foonativecommonhfoo
#define foonativecommonhfoo
-/* $Id: native-common.h 2064 2007-11-21 01:20:16Z lennart $ */
-
/***
This file is part of PulseAudio.
@@ -126,7 +124,7 @@ enum {
PA_COMMAND_SUSPEND_SINK,
PA_COMMAND_SUSPEND_SOURCE,
- /* Supported since protocol v13 (0.9.8) */
+ /* Supported since protocol v12 (0.9.8) */
PA_COMMAND_SET_PLAYBACK_STREAM_BUFFER_ATTR,
PA_COMMAND_SET_RECORD_STREAM_BUFFER_ATTR,
@@ -139,6 +137,20 @@ enum {
PA_COMMAND_PLAYBACK_STREAM_MOVED,
PA_COMMAND_RECORD_STREAM_MOVED,
+ /* Supported since protocol v13 (0.9.11) */
+ 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,
+
+ /* Supported since protocol v14 (0.9.12) */
+ PA_COMMAND_EXTENSION,
+
PA_COMMAND_MAX
};
@@ -152,7 +164,6 @@ enum {
#define PA_NATIVE_DEFAULT_UNIX_SOCKET "native"
-
PA_C_DECL_END
#endif
diff --git a/src/pulsecore/object.c b/src/pulsecore/object.c
index dc9e343..9a2f28f 100644
--- a/src/pulsecore/object.c
+++ b/src/pulsecore/object.c
@@ -1,5 +1,3 @@
-/* $Id: object.c 1971 2007-10-28 19:13:50Z lennart $ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/object.h b/src/pulsecore/object.h
index 7d3f9b0..2ee4fc3 100644
--- a/src/pulsecore/object.h
+++ b/src/pulsecore/object.h
@@ -1,8 +1,6 @@
#ifndef foopulseobjecthfoo
#define foopulseobjecthfoo
-/* $Id: object.h 1971 2007-10-28 19:13:50Z lennart $ */
-
/***
This file is part of PulseAudio.
@@ -44,7 +42,7 @@ struct pa_object {
pa_object *pa_object_new_internal(size_t size, const char *type_name, int (*check_type)(const char *type_name));
#define pa_object_new(type) ((type*) pa_object_new_internal(sizeof(type), #type, type##_check_type)
-#define pa_object_free ((void (*) (pa_object* o)) pa_xfree)
+#define pa_object_free ((void (*) (pa_object* _obj)) pa_xfree)
int pa_object_check_type(const char *type);
diff --git a/src/pulsecore/once.c b/src/pulsecore/once.c
index 0cb1f17..989741d 100644
--- a/src/pulsecore/once.c
+++ b/src/pulsecore/once.c
@@ -1,5 +1,3 @@
-/* $Id: once.c 1971 2007-10-28 19:13:50Z lennart $ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/once.h b/src/pulsecore/once.h
index 5f8f50f..576d40f 100644
--- a/src/pulsecore/once.h
+++ b/src/pulsecore/once.h
@@ -1,8 +1,6 @@
#ifndef foopulseoncehfoo
#define foopulseoncehfoo
-/* $Id: once.h 1971 2007-10-28 19:13:50Z lennart $ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/packet.c b/src/pulsecore/packet.c
index 75da59f..cee468b 100644
--- a/src/pulsecore/packet.c
+++ b/src/pulsecore/packet.c
@@ -1,5 +1,3 @@
-/* $Id: packet.c 1971 2007-10-28 19:13:50Z lennart $ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/packet.h b/src/pulsecore/packet.h
index ab8ab59..5989b1f 100644
--- a/src/pulsecore/packet.h
+++ b/src/pulsecore/packet.h
@@ -1,8 +1,6 @@
#ifndef foopackethfoo
#define foopackethfoo
-/* $Id: packet.h 1971 2007-10-28 19:13:50Z lennart $ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/parseaddr.c b/src/pulsecore/parseaddr.c
index 205efbc..c5cd7fe 100644
--- a/src/pulsecore/parseaddr.c
+++ b/src/pulsecore/parseaddr.c
@@ -1,5 +1,3 @@
-/* $Id: parseaddr.c 1998 2007-10-30 14:05:18Z lennart $ */
-
/***
This file is part of PulseAudio.
@@ -53,20 +51,29 @@ static char *parse_host(const char *s, uint16_t *ret_port) {
if (!(e = strchr(s+1, ']')))
return NULL;
- if (e[1] == ':')
- *ret_port = atoi(e+2);
- else if (e[1] != 0)
+ if (e[1] == ':') {
+ uint32_t p;
+
+ if (pa_atou(e+2, &p) < 0)
+ return NULL;
+
+ *ret_port = (uint16_t) p;
+ } else if (e[1] != 0)
return NULL;
- return pa_xstrndup(s+1, e-s-1);
+ return pa_xstrndup(s+1, (size_t) (e-s-1));
} else {
char *e;
+ uint32_t p;
if (!(e = strrchr(s, ':')))
return pa_xstrdup(s);
- *ret_port = atoi(e+1);
- return pa_xstrndup(s, e-s);
+ if (pa_atou(e+1, &p) < 0)
+ return NULL;
+
+ *ret_port = (uint16_t) p;
+ return pa_xstrndup(s, (size_t) (e-s));
}
}
diff --git a/src/pulsecore/parseaddr.h b/src/pulsecore/parseaddr.h
index f03dbad..5fbcb9a 100644
--- a/src/pulsecore/parseaddr.h
+++ b/src/pulsecore/parseaddr.h
@@ -1,8 +1,6 @@
#ifndef fooparseaddrhfoo
#define fooparseaddrhfoo
-/* $Id: parseaddr.h 1426 2007-02-13 15:35:19Z ossman $ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/pdispatch.c b/src/pulsecore/pdispatch.c
index 1ab944c..00df0f7 100644
--- a/src/pulsecore/pdispatch.c
+++ b/src/pulsecore/pdispatch.c
@@ -1,5 +1,3 @@
-/* $Id: pdispatch.c 2057 2007-11-14 16:10:36Z lennart $ */
-
/***
This file is part of PulseAudio.
@@ -257,7 +255,7 @@ finish:
return ret;
}
-static void timeout_callback(pa_mainloop_api*m, pa_time_event*e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) {
+static void timeout_callback(pa_mainloop_api*m, pa_time_event*e, const struct timeval *tv, void *userdata) {
struct reply_info*r = userdata;
pa_assert(r);
diff --git a/src/pulsecore/pdispatch.h b/src/pulsecore/pdispatch.h
index c946255..5c31d80 100644
--- a/src/pulsecore/pdispatch.h
+++ b/src/pulsecore/pdispatch.h
@@ -1,8 +1,6 @@
#ifndef foopdispatchhfoo
#define foopdispatchhfoo
-/* $Id: pdispatch.h 1426 2007-02-13 15:35:19Z ossman $ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/pid.c b/src/pulsecore/pid.c
index 4d2c48e..ce8ef19 100644
--- a/src/pulsecore/pid.c
+++ b/src/pulsecore/pid.c
@@ -1,5 +1,3 @@
-/* $Id: pid.c 2067 2007-11-21 01:30:40Z lennart $ */
-
/***
This file is part of PulseAudio.
@@ -75,6 +73,7 @@ static pid_t read_pid(const char *fn, int fd) {
if (pa_atou(t, &pid) < 0) {
pa_log_warn("Failed to parse PID file '%s'", fn);
+ errno = EINVAL;
return (pid_t) -1;
}
@@ -112,7 +111,7 @@ static int open_pid_file(const char *fn, int mode) {
goto fail;
}
- /* Does the file still exist in the file system? When ye, w're done, otherwise restart */
+ /* Does the file still exist in the file system? When yes, we're done, otherwise restart */
if (st.st_nlink >= 1)
break;
@@ -133,27 +132,78 @@ static int open_pid_file(const char *fn, int mode) {
fail:
if (fd >= 0) {
+ int saved_errno = errno;
pa_lock_fd(fd, 0);
pa_close(fd);
+ errno = saved_errno;
}
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))) {
+ int saved_errno = feof(f) ? EINVAL : errno;
+ pa_log_info("Failed to read from %s: %s", bn, feof(f) ? "EOF" : pa_cstrerror(errno));
+ fclose(f);
+
+ errno = saved_errno;
+ 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;
+ }
+#else
+
+ return 1;
+#endif
+
+}
+
/* 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;
@@ -161,21 +211,31 @@ 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.");
}
/* Overwrite the current PID file */
- if (lseek(fd, 0, SEEK_SET) == (off_t) -1 || ftruncate(fd, 0) < 0) {
+ if (lseek(fd, (off_t) 0, SEEK_SET) == (off_t) -1 || ftruncate(fd, (off_t) 0) < 0) {
pa_log("Failed to truncate PID file '%s': %s", fn, pa_cstrerror(errno));
goto fail;
}
@@ -200,17 +260,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));
@@ -225,14 +288,14 @@ int pa_pid_file_remove(void) {
goto fail;
}
- if (ftruncate(fd, 0) < 0) {
+ if (ftruncate(fd, (off_t) 0) < 0) {
pa_log_warn("Failed to truncate PID file '%s': %s", fn, pa_cstrerror(errno));
goto fail;
}
#ifdef OS_IS_WIN32
pa_lock_fd(fd, 0);
- close(fd);
+ pa_close(fd);
fd = -1;
#endif
@@ -254,6 +317,8 @@ fail:
}
}
+ pa_xfree(fn);
+
return ret;
}
@@ -261,8 +326,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, const char *binary_name) {
- return pa_pid_file_kill(0, pid, binary_name);
+int pa_pid_file_check_running(pid_t *pid, const char *procname) {
+ return pa_pid_file_kill(0, pid, procname);
}
#ifndef OS_IS_WIN32
@@ -270,55 +335,61 @@ int pa_pid_file_check_running(pid_t *pid, const char *binary_name) {
/* 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, const char *binary_name) {
+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) {
+
+ if (errno == ENOENT)
+ errno = ESRCH;
- if ((fd = open_pid_file(fn, O_RDONLY)) < 0)
goto fail;
+ }
if ((*pid = read_pid(fn, fd)) == (pid_t) -1)
goto fail;
-#ifdef __linux__
- if (binary_name) {
- pa_snprintf(fn, sizeof(fn), "/proc/%lu/exe", (unsigned long) pid);
-
- if ((e = pa_readlink(fn))) {
- char *f = pa_path_get_filename(e);
- if (strcmp(f, binary_name)
-#if defined(__OPTIMIZE__)
- /* libtool likes to rename our binary names ... */
- && !(pa_startswith(f, "lt-") && strcmp(f+3, binary_name) == 0)
-#endif
- )
- goto fail;
+ if (procname) {
+ int ours;
+
+ if ((ours = proc_name_ours(*pid, procname)) < 0)
+ goto fail;
+
+ if (!ours) {
+ errno = ESRCH;
+ goto fail;
}
}
-#endif
ret = kill(*pid, sig);
fail:
if (fd >= 0) {
+ int saved_errno = errno;
pa_lock_fd(fd, 0);
pa_close(fd);
+ errno = saved_errno;
}
#ifdef __linux__
pa_xfree(e);
#endif
+ pa_xfree(fn);
+
return ret;
}
diff --git a/src/pulsecore/pid.h b/src/pulsecore/pid.h
index 6762c11..3c8a9de 100644
--- a/src/pulsecore/pid.h
+++ b/src/pulsecore/pid.h
@@ -1,8 +1,6 @@
#ifndef foopidhfoo
#define foopidhfoo
-/* $Id: pid.h 2067 2007-11-21 01:30:40Z lennart $ */
-
/***
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, const char *binary_name);
-int pa_pid_file_kill(int sig, pid_t *pid, const char *binary_name);
+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 e3ae67f..93d78a2 100644
--- a/src/pulsecore/pipe.c
+++ b/src/pulsecore/pipe.c
@@ -1,5 +1,3 @@
-/* $Id: pipe.c 1971 2007-10-28 19:13:50Z lennart $ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/pipe.h b/src/pulsecore/pipe.h
index 8fdf27d..9a7e62c 100644
--- a/src/pulsecore/pipe.h
+++ b/src/pulsecore/pipe.h
@@ -1,8 +1,6 @@
#ifndef foopipehfoo
#define foopipehfoo
-/* $Id: pipe.h 1426 2007-02-13 15:35:19Z ossman $ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/play-memblockq.c b/src/pulsecore/play-memblockq.c
index 1c0b427..86edfe9 100644
--- a/src/pulsecore/play-memblockq.c
+++ b/src/pulsecore/play-memblockq.c
@@ -1,9 +1,7 @@
-/* $Id: play-memblockq.c 1971 2007-10-28 19:13:50Z lennart $ */
-
/***
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,7 +58,6 @@ 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;
@@ -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);
@@ -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(MEMBLOCKQ_STREAM(i->userdata));
+ memblockq_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) {
memblockq_stream *u;
- pa_assert(i);
+ pa_sink_input_assert_ref(i);
+ u = MEMBLOCKQ_STREAM(i->userdata);
+ memblockq_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);
+}
+
+static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) {
+ memblockq_stream *u;
+
+ pa_sink_input_assert_ref(i);
pa_assert(chunk);
u = MEMBLOCKQ_STREAM(i->userdata);
memblockq_stream_assert_ref(u);
@@ -109,36 +124,57 @@ 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);
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,41 +185,36 @@ 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. */
@@ -202,11 +233,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 +246,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;
@@ -232,5 +268,10 @@ void pa_memblockq_sink_input_set_queue(pa_sink_input *i, pa_memblockq *q) {
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 43a4bfb..1a42867 100644
--- a/src/pulsecore/play-memblockq.h
+++ b/src/pulsecore/play-memblockq.h
@@ -1,8 +1,6 @@
#ifndef fooplaymemblockqhfoo
#define fooplaymemblockqhfoo
-/* $Id: play-memblockq.h 1971 2007-10-28 19:13:50Z lennart $ */
-
/***
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 7ada60e..0dd4825 100644
--- a/src/pulsecore/play-memchunk.c
+++ b/src/pulsecore/play-memchunk.c
@@ -1,9 +1,7 @@
-/* $Id: play-memchunk.c 1971 2007-10-28 19:13:50Z lennart $ */
-
/***
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);
-
- if (!(u->sink_input = pa_sink_input_new(sink->core, &data, 0)))
- goto fail;
+ 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);
- 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);
-
- /* The reference to u is dangling here, because we want to keep
- * this stream around until it is fully played. */
+ if ((r = pa_play_memblockq(sink, ss, map, q, volume, p, sink_input_index)) < 0) {
+ pa_memblockq_free(q);
+ return r;
+ }
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 01df35b..c312ae8 100644
--- a/src/pulsecore/play-memchunk.h
+++ b/src/pulsecore/play-memchunk.h
@@ -1,8 +1,6 @@
#ifndef fooplaychunkhfoo
#define fooplaychunkhfoo
-/* $Id: play-memchunk.h 1426 2007-02-13 15:35:19Z ossman $ */
-
/***
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 625b642..88ac21e 100644
--- a/src/pulsecore/poll.c
+++ b/src/pulsecore/poll.c
@@ -1,5 +1,3 @@
-/* $Id: poll.c 1971 2007-10-28 19:13:50Z lennart $ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/poll.h b/src/pulsecore/poll.h
index e44cf06..86c37a0 100644
--- a/src/pulsecore/poll.h
+++ b/src/pulsecore/poll.h
@@ -1,5 +1,3 @@
-/* $Id: poll.h 1426 2007-02-13 15:35:19Z ossman $ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/prioq.c b/src/pulsecore/prioq.c
new file mode 100644
index 0000000..693dc51
--- /dev/null
+++ b/src/pulsecore/prioq.c
@@ -0,0 +1,256 @@
+/***
+ 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 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/flist.h>
+
+#include "prioq.h"
+
+struct pa_prioq_item {
+ void *value;
+ unsigned idx;
+};
+
+struct pa_prioq {
+ pa_prioq_item **items;
+ unsigned n_items;
+ unsigned n_allocated;
+ pa_compare_func_t compare_func;
+};
+
+PA_STATIC_FLIST_DECLARE(items, 0, pa_xfree);
+
+pa_prioq *pa_prioq_new(pa_compare_func_t compare_func) {
+
+ pa_prioq *q;
+
+ q = pa_xnew(pa_prioq, 1);
+ q->compare_func = compare_func;
+ q->n_items = 0;
+ q->n_allocated = 64;
+ q->items = pa_xnew(pa_prioq_item*, q->n_allocated);
+
+ return q;
+}
+
+void pa_prioq_free(pa_prioq *q, pa_free2_cb_t free_cb, void *userdata) {
+ pa_prioq_item **i, **e;
+
+ pa_assert(q);
+
+ for (i = q->items, e = q->items + q->n_items; i < e; i++) {
+
+ if (!*i)
+ continue;
+
+ if (free_cb)
+ free_cb((*i)->value, userdata);
+
+ pa_xfree(*i);
+ }
+
+ pa_xfree(q->items);
+ pa_xfree(q);
+}
+
+static void shuffle_up(pa_prioq *q, pa_prioq_item *i) {
+ unsigned j;
+
+ pa_assert(q);
+ pa_assert(i);
+
+ j = i->idx;
+
+ while (j > 0) {
+ unsigned k;
+
+ k = (j-1)/2;
+
+ if (q->compare_func(q->items[k]->value, i->value) < 0)
+ break;
+
+ q->items[k]->idx = j;
+ q->items[j] = q->items[k];
+
+ j = k;
+ }
+
+ i->idx = j;
+ q->items[j] = i;
+
+}
+
+pa_prioq_item* pa_prioq_put(pa_prioq *q, void *p) {
+ pa_prioq_item *i;
+
+ pa_assert(q);
+
+ if (q->n_items >= q->n_allocated) {
+ q->n_allocated = PA_MAX(q->n_items+1, q->n_allocated)*2;
+ q->items = pa_xrealloc(q->items, sizeof(pa_prioq_item*) * q->n_allocated);
+ }
+
+ if (!(i = pa_flist_pop(PA_STATIC_FLIST_GET(items))))
+ i = pa_xnew(pa_prioq_item, 1);
+
+ i->value = p;
+ i->idx = q->n_items++;
+
+ shuffle_up(q, i);
+
+ return i;
+}
+
+void* pa_prioq_peek(pa_prioq *q) {
+ pa_assert(q);
+
+ if (q->n_items <= 0)
+ return NULL;
+
+ return q->items[0]->value;
+}
+
+void* pa_prioq_pop(pa_prioq *q){
+ pa_assert(q);
+
+ if (q->n_items <= 0)
+ return NULL;
+
+ return pa_prioq_remove(q, q->items[0]);
+}
+
+static void swap(pa_prioq *q, unsigned j, unsigned k) {
+ pa_prioq_item *t;
+
+ pa_assert(q);
+ pa_assert(j < q->n_items);
+ pa_assert(k < q->n_items);
+
+ pa_assert(q->items[j]->idx == j);
+ pa_assert(q->items[k]->idx == k);
+
+ t = q->items[j];
+
+ q->items[j]->idx = k;
+ q->items[j] = q->items[k];
+
+ q->items[k]->idx = j;
+ q->items[k] = t;
+}
+
+static void shuffle_down(pa_prioq *q, unsigned idx) {
+
+ pa_assert(q);
+ pa_assert(idx < q->n_items);
+
+ for (;;) {
+ unsigned j, k, s;
+
+ k = (idx+1)*2; /* right child */
+ j = k-1; /* left child */
+
+ if (j >= q->n_items)
+ break;
+
+ if (q->compare_func(q->items[j]->value, q->items[idx]->value) < 0)
+
+ /* So our left child is smaller than we are, let's
+ * remember this fact */
+ s = j;
+ else
+ s = idx;
+
+ if (k < q->n_items &&
+ q->compare_func(q->items[k]->value, q->items[s]->value) < 0)
+
+ /* So our right child is smaller than we are, let's
+ * remember this fact */
+ s = k;
+
+ /* s now points to the smallest of the three items */
+
+ if (s == idx)
+ /* No swap necessary, we're done */
+ break;
+
+ swap(q, idx, s);
+ idx = s;
+ }
+}
+
+void* pa_prioq_remove(pa_prioq *q, pa_prioq_item *i) {
+ void *p;
+
+ pa_assert(q);
+ pa_assert(i);
+ pa_assert(q->n_items >= 1);
+
+ p = i->value;
+
+ if (q->n_items-1 == i->idx) {
+ /* We are the last entry, so let's just remove us and good */
+ q->n_items--;
+
+ } else {
+
+ /* We are not the last entry, we need to replace ourselves
+ * with the last node and reshuffle */
+
+ q->items[i->idx] = q->items[q->n_items-1];
+ q->items[i->idx]->idx = i->idx;
+ q->n_items--;
+
+ shuffle_down(q, i->idx);
+ }
+
+ if (pa_flist_push(PA_STATIC_FLIST_GET(items), i) < 0)
+ pa_xfree(i);
+
+ return p;
+}
+
+unsigned pa_prioq_size(pa_prioq *q) {
+ pa_assert(q);
+
+ return q->n_items;
+}
+
+pa_bool_t pa_prioq_isempty(pa_prioq *q) {
+ pa_assert(q);
+
+ return q->n_items == 0;
+}
+
+void pa_prioq_reshuffle(pa_prioq *q, pa_prioq_item *i) {
+ pa_assert(q);
+ pa_assert(i);
+
+ /* This will move the entry down as far as necessary */
+ shuffle_down(q, i->idx);
+
+ /* And this will move the entry up as far as necessary */
+ shuffle_up(q, i);
+}
diff --git a/src/pulsecore/prioq.h b/src/pulsecore/prioq.h
new file mode 100644
index 0000000..fd3550b
--- /dev/null
+++ b/src/pulsecore/prioq.h
@@ -0,0 +1,64 @@
+#ifndef foopulsecoreprioqhfoo
+#define foopulsecoreprioqhfoo
+
+/***
+ 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.
+***/
+
+#include <inttypes.h>
+
+#include <pulsecore/macro.h>
+#include <pulsecore/idxset.h>
+
+/* A heap-based priority queue. Removal and insertion is O(log
+ * n). Removal can happen a the top or at any position referenced by a
+ * pa_prioq_item. */
+
+typedef struct pa_prioq pa_prioq;
+typedef struct pa_prioq_item pa_prioq_item;
+
+/* Instantiate a new prioq with the specified comparison functions */
+pa_prioq* pa_prioq_new(pa_compare_func_t compare_func);
+
+/* Free the prioq. When the prioq is not empty the specified function is called for every entry contained */
+void pa_prioq_free(pa_prioq *q, pa_free2_cb_t free_cb, void *userdata);
+
+/* Store a new item in the prioq. */
+pa_prioq_item* pa_prioq_put(pa_prioq *q, void* data);
+
+/* Get the item on the top of the queue, but don't remove it from the queue*/
+void* pa_prioq_peek(pa_prioq*q);
+
+/* Get the item on the top of the queue, and remove it from thq queue */
+void* pa_prioq_pop(pa_prioq*q);
+
+/* Remove an arbitrary from theq prioq, returning it's data */
+void* pa_prioq_remove(pa_prioq*q, pa_prioq_item *i);
+
+/* The priority of an item was modified. Adjustthe queue to that */
+void pa_prioq_reshuffle(pa_prioq *q, pa_prioq_item *i);
+
+/* Return the current number of items in the prioq */
+unsigned pa_prioq_size(pa_prioq*s);
+
+/* Return TRUE of the prioq is empty */
+pa_bool_t pa_prioq_isempty(pa_prioq *s);
+
+#endif
diff --git a/src/pulsecore/proplist-util.c b/src/pulsecore/proplist-util.c
new file mode 100644
index 0000000..4d505f5
--- /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;
+#if !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/proplist-util.h b/src/pulsecore/proplist-util.h
new file mode 100644
index 0000000..c6bdc10
--- /dev/null
+++ b/src/pulsecore/proplist-util.h
@@ -0,0 +1,29 @@
+#ifndef fooproplistutilutilhfoo
+#define fooproplistutilutilhfoo
+
+/***
+ 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.
+***/
+
+#include <pulse/proplist.h>
+
+void pa_init_proplist(pa_proplist *p);
+
+#endif
diff --git a/src/pulsecore/protocol-cli.c b/src/pulsecore/protocol-cli.c
index f284822..9247bb4 100644
--- a/src/pulsecore/protocol-cli.c
+++ b/src/pulsecore/protocol-cli.c
@@ -1,5 +1,3 @@
-/* $Id: protocol-cli.c 1971 2007-10-28 19:13:50Z lennart $ */
-
/***
This file is part of PulseAudio.
@@ -32,34 +30,41 @@
#include <pulsecore/cli.h>
#include <pulsecore/log.h>
#include <pulsecore/macro.h>
+#include <pulsecore/shared.h>
#include "protocol-cli.h"
/* Don't allow more than this many concurrent connections */
#define MAX_CONNECTIONS 25
-struct pa_protocol_cli {
- pa_module *module;
+struct pa_cli_protocol {
+ PA_REFCNT_DECLARE;
+
pa_core *core;
- pa_socket_server*server;
pa_idxset *connections;
};
-static void cli_eof_cb(pa_cli*c, void*userdata) {
- pa_protocol_cli *p = userdata;
+static void cli_unlink(pa_cli_protocol *p, pa_cli *c) {
pa_assert(p);
+ pa_assert(c);
pa_idxset_remove_by_data(p->connections, c, NULL);
pa_cli_free(c);
}
-static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata) {
- pa_protocol_cli *p = userdata;
+static void cli_eof_cb(pa_cli*c, void*userdata) {
+ pa_cli_protocol *p = userdata;
+ pa_assert(p);
+
+ cli_unlink(p, c);
+}
+
+void pa_cli_protocol_connect(pa_cli_protocol *p, pa_iochannel *io, pa_module *m) {
pa_cli *c;
- pa_assert(s);
- pa_assert(io);
pa_assert(p);
+ pa_assert(io);
+ pa_assert(m);
if (pa_idxset_size(p->connections)+1 > MAX_CONNECTIONS) {
pa_log("Warning! Too many connections (%u), dropping incoming connection.", MAX_CONNECTIONS);
@@ -67,39 +72,71 @@ static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata)
return;
}
- c = pa_cli_new(p->core, io, p->module);
+ c = pa_cli_new(p->core, io, m);
pa_cli_set_eof_callback(c, cli_eof_cb, p);
pa_idxset_put(p->connections, c, NULL);
}
-pa_protocol_cli* pa_protocol_cli_new(pa_core *core, pa_socket_server *server, pa_module *m, PA_GCC_UNUSED pa_modargs *ma) {
- pa_protocol_cli* p;
+void pa_cli_protocol_disconnect(pa_cli_protocol *p, pa_module *m) {
+ pa_cli *c;
+ void *state = NULL;
+
+ pa_assert(p);
+ pa_assert(m);
+
+ while ((c = pa_idxset_iterate(p->connections, &state, NULL)))
+ if (pa_cli_get_module(c) == m)
+ cli_unlink(p, c);
+}
+
+static pa_cli_protocol* cli_protocol_new(pa_core *c) {
+ pa_cli_protocol *p;
- pa_core_assert_ref(core);
- pa_assert(server);
+ pa_assert(c);
- p = pa_xnew(pa_protocol_cli, 1);
- p->module = m;
- p->core = core;
- p->server = server;
+ p = pa_xnew(pa_cli_protocol, 1);
+ PA_REFCNT_INIT(p);
+ p->core = c;
p->connections = pa_idxset_new(NULL, NULL);
- pa_socket_server_set_callback(p->server, on_connection, p);
+ pa_assert_se(pa_shared_set(c, "cli-protocol", p) >= 0);
return p;
}
-static void free_connection(void *p, PA_GCC_UNUSED void *userdata) {
+pa_cli_protocol* pa_cli_protocol_get(pa_core *c) {
+ pa_cli_protocol *p;
+
+ if ((p = pa_shared_get(c, "cli-protocol")))
+ return pa_cli_protocol_ref(p);
+
+ return cli_protocol_new(c);
+}
+
+pa_cli_protocol* pa_cli_protocol_ref(pa_cli_protocol *p) {
pa_assert(p);
+ pa_assert(PA_REFCNT_VALUE(p) >= 1);
+
+ PA_REFCNT_INC(p);
- pa_cli_free(p);
+ return p;
}
-void pa_protocol_cli_free(pa_protocol_cli *p) {
+void pa_cli_protocol_unref(pa_cli_protocol *p) {
+ pa_cli *c;
pa_assert(p);
+ pa_assert(PA_REFCNT_VALUE(p) >= 1);
+
+ if (PA_REFCNT_DEC(p) > 0)
+ return;
+
+ while ((c = pa_idxset_first(p->connections, NULL)))
+ cli_unlink(p, c);
+
+ pa_idxset_free(p->connections, NULL, NULL);
+
+ pa_assert_se(pa_shared_remove(p->core, "cli-protocol") >= 0);
- pa_idxset_free(p->connections, free_connection, NULL);
- pa_socket_server_unref(p->server);
pa_xfree(p);
}
diff --git a/src/pulsecore/protocol-cli.h b/src/pulsecore/protocol-cli.h
index b900377..9e26dcd 100644
--- a/src/pulsecore/protocol-cli.h
+++ b/src/pulsecore/protocol-cli.h
@@ -1,8 +1,6 @@
#ifndef fooprotocolclihfoo
#define fooprotocolclihfoo
-/* $Id: protocol-cli.h 1426 2007-02-13 15:35:19Z ossman $ */
-
/***
This file is part of PulseAudio.
@@ -29,9 +27,12 @@
#include <pulsecore/module.h>
#include <pulsecore/modargs.h>
-typedef struct pa_protocol_cli pa_protocol_cli;
+typedef struct pa_cli_protocol pa_cli_protocol;
-pa_protocol_cli* pa_protocol_cli_new(pa_core *core, pa_socket_server *server, pa_module *m, pa_modargs *ma);
-void pa_protocol_cli_free(pa_protocol_cli *n);
+pa_cli_protocol* pa_cli_protocol_get(pa_core *core);
+pa_cli_protocol* pa_cli_protocol_ref(pa_cli_protocol *p);
+void pa_cli_protocol_unref(pa_cli_protocol *p);
+void pa_cli_protocol_connect(pa_cli_protocol *p, pa_iochannel *io, pa_module *m);
+void pa_cli_protocol_disconnect(pa_cli_protocol *o, pa_module *m);
#endif
diff --git a/src/pulsecore/protocol-esound.c b/src/pulsecore/protocol-esound.c
index 784d122..460119a 100644
--- a/src/pulsecore/protocol-esound.c
+++ b/src/pulsecore/protocol-esound.c
@@ -1,5 +1,3 @@
-/* $Id: protocol-esound.c 2176 2008-03-27 23:40:40Z lennart $ */
-
/***
This file is part of PulseAudio.
@@ -54,6 +52,7 @@
#include <pulsecore/ipacl.h>
#include <pulsecore/macro.h>
#include <pulsecore/thread-mq.h>
+#include <pulsecore/shared.h>
#include "endianmacros.h"
@@ -70,10 +69,12 @@
#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 (2048000)
+#define DEFAULT_SINK_LATENCY (150*PA_USEC_PER_MSEC)
+#define DEFAULT_SOURCE_LATENCY (150*PA_USEC_PER_MSEC)
+
#define SCACHE_PREFIX "esound."
/* This is heavily based on esound's code */
@@ -83,7 +84,8 @@ typedef struct connection {
uint32_t index;
pa_bool_t dead;
- pa_protocol_esound *protocol;
+ pa_esound_protocol *protocol;
+ pa_esound_options *options;
pa_iochannel *io;
pa_client *client;
pa_bool_t authorized, swap_byte_order;
@@ -102,8 +104,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 {
@@ -119,17 +122,12 @@ PA_DECLARE_CLASS(connection);
#define CONNECTION(o) (connection_cast(o))
static PA_DEFINE_CHECK_TYPE(connection, pa_msgobject);
-struct pa_protocol_esound {
- pa_module *module;
+struct pa_esound_protocol {
+ PA_REFCNT_DECLARE;
+
pa_core *core;
- int public;
- pa_socket_server *server;
pa_idxset *connections;
-
- char *sink_name, *source_name;
unsigned n_player;
- uint8_t esd_key[ESD_KEY_LEN];
- pa_ip_acl *auth_ip_acl;
};
enum {
@@ -149,8 +147,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);
@@ -185,7 +184,7 @@ static struct proto_handler proto_map[ESD_PROTO_MAX] = {
{ sizeof(int), esd_proto_sample_free_or_play, "sample play" }, /* 8 */
{ sizeof(int), NULL, "sample loop" },
{ sizeof(int), NULL, "sample stop" },
- { -1, NULL, "TODO: sample kill" },
+ { (size_t) -1, NULL, "TODO: sample kill" },
{ ESD_KEY_LEN + sizeof(int), esd_proto_standby_or_resume, "standby" }, /* NOOP! */
{ ESD_KEY_LEN + sizeof(int), esd_proto_standby_or_resume, "resume" }, /* NOOP! */ /* 13 */
@@ -195,8 +194,8 @@ static struct proto_handler proto_map[ESD_PROTO_MAX] = {
{ sizeof(int), esd_proto_server_info, "server info" },
{ sizeof(int), esd_proto_all_info, "all info" },
- { -1, NULL, "TODO: subscribe" },
- { -1, NULL, "TODO: unsubscribe" },
+ { (size_t) -1, NULL, "TODO: subscribe" },
+ { (size_t) -1, NULL, "TODO: unsubscribe" },
{ 3 * sizeof(int), esd_proto_stream_pan, "stream pan"},
{ 3 * sizeof(int), NULL, "sample pan" },
@@ -211,6 +210,11 @@ static void connection_unlink(connection *c) {
if (!c->protocol)
return;
+ if (c->options) {
+ pa_esound_options_unref(c->options);
+ c->options = NULL;
+ }
+
if (c->sink_input) {
pa_sink_input_unlink(c->sink_input);
pa_sink_input_unref(c->sink_input);
@@ -305,7 +309,7 @@ static void connection_write(connection *c, const void *data, size_t length) {
static void format_esd2native(int format, pa_bool_t swap_bytes, pa_sample_spec *ss) {
pa_assert(ss);
- ss->channels = ((format & ESD_MASK_CHAN) == ESD_STEREO) ? 2 : 1;
+ ss->channels = (uint8_t) (((format & ESD_MASK_CHAN) == ESD_STEREO) ? 2 : 1);
if ((format & ESD_MASK_BITS) == ESD_BITS16)
ss->format = swap_bytes ? PA_SAMPLE_S16RE : PA_SAMPLE_S16NE;
else
@@ -330,7 +334,7 @@ static int format_native2esd(pa_sample_spec *ss) {
/*** esound commands ***/
-static int esd_proto_connect(connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) {
+static int esd_proto_connect(connection *c, esd_proto_t request, const void *data, size_t length) {
uint32_t ekey;
int ok;
@@ -338,17 +342,22 @@ static int esd_proto_connect(connection *c, PA_GCC_UNUSED esd_proto_t request, c
pa_assert(data);
pa_assert(length == (ESD_KEY_LEN + sizeof(uint32_t)));
+ if (!c->authorized && c->options->auth_cookie) {
+ const uint8_t*key;
+
+ if ((key = pa_auth_cookie_read(c->options->auth_cookie, ESD_KEY_LEN)))
+ if (memcmp(data, key, ESD_KEY_LEN) == 0)
+ c->authorized = TRUE;
+ }
+
if (!c->authorized) {
- if (memcmp(data, c->protocol->esd_key, ESD_KEY_LEN) != 0) {
- pa_log("kicked client with invalid authorization key.");
- return -1;
- }
+ pa_log("Kicked client with invalid authorization key.");
+ return -1;
+ }
- c->authorized = TRUE;
- if (c->auth_timeout_event) {
- c->protocol->core->mainloop->time_free(c->auth_timeout_event);
- c->auth_timeout_event = NULL;
- }
+ if (c->auth_timeout_event) {
+ c->protocol->core->mainloop->time_free(c->auth_timeout_event);
+ c->auth_timeout_event = NULL;
}
data = (const char*)data + ESD_KEY_LEN;
@@ -368,7 +377,7 @@ static int esd_proto_connect(connection *c, PA_GCC_UNUSED esd_proto_t request, c
return 0;
}
-static int esd_proto_stream_play(connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) {
+static int esd_proto_stream_play(connection *c, esd_proto_t request, const void *data, size_t length) {
char name[ESD_NAME_MAX], *utf8_name;
int32_t format, rate;
pa_sample_spec ss;
@@ -388,18 +397,17 @@ static int esd_proto_stream_play(connection *c, PA_GCC_UNUSED esd_proto_t reques
rate = PA_MAYBE_INT32_SWAP(c->swap_byte_order, rate);
data = (const char*) data + sizeof(int32_t);
- ss.rate = rate;
+ ss.rate = (uint32_t) rate;
format_esd2native(format, c->swap_byte_order, &ss);
CHECK_VALIDITY(pa_sample_spec_valid(&ss), "Invalid sample specification");
- if (c->protocol->sink_name) {
- sink = pa_namereg_get(c->protocol->core, c->protocol->sink_name, PA_NAMEREG_SINK, 1);
- CHECK_VALIDITY(sink, "No such sink: %s", c->protocol->sink_name);
+ if (c->options->default_sink) {
+ sink = pa_namereg_get(c->protocol->core, c->options->default_sink, PA_NAMEREG_SINK, 1);
+ CHECK_VALIDITY(sink, "No such sink: %s", c->options->default_sink);
}
- 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,39 +418,44 @@ 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.module = c->options->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);
+ l = (size_t) ((double) 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++;
- pa_atomic_store(&c->playback.missing, pa_memblockq_missing(c->input_memblockq));
+ pa_atomic_store(&c->playback.missing, (int) pa_memblockq_missing(c->input_memblockq));
pa_sink_input_put(c->sink_input);
@@ -469,7 +482,7 @@ static int esd_proto_stream_record(connection *c, esd_proto_t request, const voi
rate = PA_MAYBE_INT32_SWAP(c->swap_byte_order, rate);
data = (const char*) data + sizeof(int32_t);
- ss.rate = rate;
+ ss.rate = (uint32_t) rate;
format_esd2native(format, c->swap_byte_order, &ss);
CHECK_VALIDITY(pa_sample_spec_valid(&ss), "Invalid sample specification.");
@@ -477,7 +490,7 @@ static int esd_proto_stream_record(connection *c, esd_proto_t request, const voi
if (request == ESD_PROTO_STREAM_MON) {
pa_sink* sink;
- if (!(sink = pa_namereg_get(c->protocol->core, c->protocol->sink_name, PA_NAMEREG_SINK, 1))) {
+ if (!(sink = pa_namereg_get(c->protocol->core, c->options->default_sink, PA_NAMEREG_SINK, 1))) {
pa_log("no such sink.");
return -1;
}
@@ -489,16 +502,15 @@ static int esd_proto_stream_record(connection *c, esd_proto_t request, const voi
} else {
pa_assert(request == ESD_PROTO_STREAM_REC);
- if (c->protocol->source_name) {
- if (!(source = pa_namereg_get(c->protocol->core, c->protocol->source_name, PA_NAMEREG_SOURCE, 1))) {
+ if (c->options->default_source) {
+ if (!(source = pa_namereg_get(c->protocol->core, c->options->default_source, PA_NAMEREG_SOURCE, 1))) {
pa_log("no such source.");
return -1;
}
}
}
- 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 +521,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.module = c->options->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++;
@@ -544,7 +561,7 @@ static int esd_proto_stream_record(connection *c, esd_proto_t request, const voi
return 0;
}
-static int esd_proto_get_latency(connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) {
+static int esd_proto_get_latency(connection *c, esd_proto_t request, const void *data, size_t length) {
pa_sink *sink;
int32_t latency;
@@ -552,10 +569,10 @@ static int esd_proto_get_latency(connection *c, PA_GCC_UNUSED esd_proto_t reques
pa_assert(!data);
pa_assert(length == 0);
- if (!(sink = pa_namereg_get(c->protocol->core, c->protocol->sink_name, PA_NAMEREG_SINK, 1)))
+ if (!(sink = pa_namereg_get(c->protocol->core, c->options->default_sink, PA_NAMEREG_SINK, 1)))
latency = 0;
else {
- double usec = pa_sink_get_latency(sink);
+ double usec = (double) pa_sink_get_latency(sink);
latency = (int) ((usec*44100)/1000000);
}
@@ -564,7 +581,7 @@ static int esd_proto_get_latency(connection *c, PA_GCC_UNUSED esd_proto_t reques
return 0;
}
-static int esd_proto_server_info(connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) {
+static int esd_proto_server_info(connection *c, esd_proto_t request, const void *data, size_t length) {
int32_t rate = 44100, format = ESD_STEREO|ESD_BITS16;
int32_t response;
pa_sink *sink;
@@ -573,8 +590,8 @@ static int esd_proto_server_info(connection *c, PA_GCC_UNUSED esd_proto_t reques
pa_assert(data);
pa_assert(length == sizeof(int32_t));
- if ((sink = pa_namereg_get(c->protocol->core, c->protocol->sink_name, PA_NAMEREG_SINK, 1))) {
- rate = sink->sample_spec.rate;
+ if ((sink = pa_namereg_get(c->protocol->core, c->options->default_sink, PA_NAMEREG_SINK, 1))) {
+ rate = (int32_t) sink->sample_spec.rate;
format = format_native2esd(&sink->sample_spec);
}
@@ -624,9 +641,9 @@ static int esd_proto_all_info(connection *c, esd_proto_t request, const void *da
if (conn->sink_input) {
pa_cvolume volume = *pa_sink_input_get_volume(conn->sink_input);
- rate = conn->sink_input->sample_spec.rate;
- lvolume = (volume.values[0]*ESD_VOLUME_BASE)/PA_VOLUME_NORM;
- rvolume = (volume.values[1]*ESD_VOLUME_BASE)/PA_VOLUME_NORM;
+ rate = (int32_t) conn->sink_input->sample_spec.rate;
+ lvolume = (int32_t) ((volume.values[0]*ESD_VOLUME_BASE)/PA_VOLUME_NORM);
+ rvolume = (int32_t) ((volume.values[1]*ESD_VOLUME_BASE)/PA_VOLUME_NORM);
format = format_native2esd(&conn->sink_input->sample_spec);
}
@@ -638,8 +655,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 */
@@ -689,15 +706,15 @@ static int esd_proto_all_info(connection *c, esd_proto_t request, const void *da
connection_write(c, name, ESD_NAME_MAX);
/* rate */
- rate = PA_MAYBE_UINT32_SWAP(c->swap_byte_order, ce->sample_spec.rate);
+ rate = PA_MAYBE_INT32_SWAP(c->swap_byte_order, (int32_t) ce->sample_spec.rate);
connection_write(c, &rate, sizeof(int32_t));
/* left */
- lvolume = PA_MAYBE_UINT32_SWAP(c->swap_byte_order, (ce->volume.values[0]*ESD_VOLUME_BASE)/PA_VOLUME_NORM);
+ lvolume = PA_MAYBE_INT32_SWAP(c->swap_byte_order, (int32_t) ((ce->volume.values[0]*ESD_VOLUME_BASE)/PA_VOLUME_NORM));
connection_write(c, &lvolume, sizeof(int32_t));
/*right*/
- rvolume = PA_MAYBE_UINT32_SWAP(c->swap_byte_order, (ce->volume.values[0]*ESD_VOLUME_BASE)/PA_VOLUME_NORM);
+ rvolume = PA_MAYBE_INT32_SWAP(c->swap_byte_order, (int32_t) ((ce->volume.values[0]*ESD_VOLUME_BASE)/PA_VOLUME_NORM));
connection_write(c, &rvolume, sizeof(int32_t));
/*format*/
@@ -719,7 +736,7 @@ static int esd_proto_all_info(connection *c, esd_proto_t request, const void *da
return 0;
}
-static int esd_proto_stream_pan(connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) {
+static int esd_proto_stream_pan(connection *c, esd_proto_t request, const void *data, size_t length) {
int32_t ok;
uint32_t idx, lvolume, rvolume;
connection *conn;
@@ -755,7 +772,7 @@ static int esd_proto_stream_pan(connection *c, PA_GCC_UNUSED esd_proto_t request
return 0;
}
-static int esd_proto_sample_cache(connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) {
+static int esd_proto_sample_cache(connection *c, esd_proto_t request, const void *data, size_t length) {
pa_sample_spec ss;
int32_t format, rate, sc_length;
uint32_t idx;
@@ -773,7 +790,7 @@ static int esd_proto_sample_cache(connection *c, PA_GCC_UNUSED esd_proto_t reque
rate = PA_MAYBE_INT32_SWAP(c->swap_byte_order, rate);
data = (const char*)data + sizeof(int32_t);
- ss.rate = rate;
+ ss.rate = (uint32_t) rate;
format_esd2native(format, c->swap_byte_order, &ss);
CHECK_VALIDITY(pa_sample_spec_valid(&ss), "Invalid sample specification.");
@@ -785,22 +802,21 @@ 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.");
pa_assert(!c->scache.memchunk.memblock);
- c->scache.memchunk.memblock = pa_memblock_new(c->protocol->core->mempool, sc_length);
+ c->scache.memchunk.memblock = pa_memblock_new(c->protocol->core->mempool, (size_t) sc_length);
c->scache.memchunk.index = 0;
- c->scache.memchunk.length = sc_length;
+ c->scache.memchunk.length = (size_t) sc_length;
c->scache.sample_spec = ss;
pa_assert(!c->scache.name);
c->scache.name = pa_xstrdup(name);
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));
@@ -808,7 +824,7 @@ static int esd_proto_sample_cache(connection *c, PA_GCC_UNUSED esd_proto_t reque
return 0;
}
-static int esd_proto_sample_get_id(connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) {
+static int esd_proto_sample_get_id(connection *c, esd_proto_t request, const void *data, size_t length) {
int32_t ok;
uint32_t idx;
char name[ESD_NAME_MAX+sizeof(SCACHE_PREFIX)-1];
@@ -818,14 +834,13 @@ 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.");
ok = -1;
if ((idx = pa_scache_get_id_by_name(c->protocol->core, name)) != PA_IDXSET_INVALID)
- ok = idx + 1;
+ ok = (int32_t) idx + 1;
connection_write(c, &ok, sizeof(int32_t));
@@ -850,14 +865,14 @@ static int esd_proto_sample_free_or_play(connection *c, esd_proto_t request, con
if (request == ESD_PROTO_SAMPLE_PLAY) {
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)
- ok = idx + 1;
+ if ((sink = pa_namereg_get(c->protocol->core, c->options->default_sink, PA_NAMEREG_SINK, 1)))
+ if (pa_scache_play_item(c->protocol->core, name, sink, PA_VOLUME_NORM, c->client->proplist, NULL) >= 0)
+ ok = (int32_t) idx + 1;
} else {
pa_assert(request == ESD_PROTO_SAMPLE_FREE);
if (pa_scache_remove_item(c->protocol->core, name) >= 0)
- ok = idx + 1;
+ ok = (int32_t) idx + 1;
}
}
@@ -866,7 +881,7 @@ static int esd_proto_sample_free_or_play(connection *c, esd_proto_t request, con
return 0;
}
-static int esd_proto_standby_or_resume(connection *c, PA_GCC_UNUSED esd_proto_t request, PA_GCC_UNUSED const void *data, PA_GCC_UNUSED size_t length) {
+static int esd_proto_standby_or_resume(connection *c, esd_proto_t request, const void *data, size_t length) {
int32_t ok;
connection_assert_ref(c);
@@ -904,7 +919,9 @@ static int do_read(connection *c) {
return -1;
}
- if ((c->read_data_length+= r) >= sizeof(c->request)) {
+ c->read_data_length += (size_t) r;
+
+ if (c->read_data_length >= sizeof(c->request)) {
struct proto_handler *handler;
c->request = PA_MAYBE_INT32_SWAP(c->swap_byte_order, c->request);
@@ -955,7 +972,8 @@ static int do_read(connection *c) {
return -1;
}
- if ((c->read_data_length += r) >= handler->data_length) {
+ c->read_data_length += (size_t) r;
+ if (c->read_data_length >= handler->data_length) {
size_t l = c->read_data_length;
pa_assert(handler->proc);
@@ -985,14 +1003,14 @@ static int do_read(connection *c) {
return -1;
}
- c->scache.memchunk.index += r;
+ c->scache.memchunk.index += (size_t) r;
pa_assert(c->scache.memchunk.index <= c->scache.memchunk.length);
if (c->scache.memchunk.index == c->scache.memchunk.length) {
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,29 +1030,35 @@ static int do_read(connection *c) {
ssize_t r;
size_t l;
void *p;
+ size_t space;
pa_assert(c->input_memblockq);
/* pa_log("STREAMING_DATA"); */
- if (!(l = pa_atomic_load(&c->playback.missing)))
+ if (!(l = (size_t) pa_atomic_load(&c->playback.missing)))
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, 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);
@@ -1050,12 +1074,12 @@ static int do_read(connection *c) {
chunk.memblock = c->playback.current_memblock;
chunk.index = c->playback.memblock_index;
- chunk.length = r;
+ chunk.length = (size_t) r;
- c->playback.memblock_index += r;
+ c->playback.memblock_index += (size_t) r;
pa_asyncmsgq_post(c->sink_input->sink->asyncmsgq, PA_MSGOBJECT(c->sink_input), SINK_INPUT_MESSAGE_POST_DATA, NULL, 0, &chunk, NULL);
- pa_atomic_sub(&c->playback.missing, r);
+ pa_atomic_sub(&c->playback.missing, (int) r);
}
return 0;
@@ -1079,7 +1103,8 @@ static int do_write(connection *c) {
return -1;
}
- if ((c->write_data_index +=r) >= c->write_data_length)
+ c->write_data_index += (size_t) r;
+ if (c->write_data_index >= c->write_data_length)
c->write_data_length = c->write_data_index = 0;
} else if (c->state == ESD_STREAMING_DATA && c->source_output) {
@@ -1108,7 +1133,7 @@ static int do_write(connection *c) {
return -1;
}
- pa_memblockq_drop(c->output_memblockq, r);
+ pa_memblockq_drop(c->output_memblockq, (size_t) r);
}
return 0;
@@ -1122,12 +1147,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 +1236,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 +1265,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;
- return r;
+ 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, (int) 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) {
- 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 +1337,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,14 +1354,14 @@ 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);
return pa_bytes_to_usec(pa_memblockq_get_length(c->output_memblockq), &c->source_output->sample_spec);
}
-/*** socket server callback ***/
+/*** entry points ***/
static void auth_timeout(pa_mainloop_api*m, pa_time_event *e, const struct timeval *tv, void *userdata) {
connection *c = CONNECTION(userdata);
@@ -1324,14 +1375,13 @@ static void auth_timeout(pa_mainloop_api*m, pa_time_event *e, const struct timev
connection_unlink(c);
}
-static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata) {
+void pa_esound_protocol_connect(pa_esound_protocol *p, pa_iochannel *io, pa_esound_options *o) {
connection *c;
- pa_protocol_esound *p = userdata;
char cname[256], pname[128];
- pa_assert(s);
- pa_assert(io);
pa_assert(p);
+ pa_assert(io);
+ pa_assert(o);
if (pa_idxset_size(p->connections)+1 > MAX_CONNECTIONS) {
pa_log("Warning! Too many connections (%u), dropping incoming connection.", MAX_CONNECTIONS);
@@ -1349,11 +1399,13 @@ 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 = o->module;
c->client->kill = client_kill_cb;
c->client->userdata = c;
- c->authorized = !!p->public;
+ c->options = pa_esound_options_ref(o);
+ c->authorized = FALSE;
c->swap_byte_order = FALSE;
c->dead = FALSE;
@@ -1374,16 +1426,23 @@ 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;
- if (!c->authorized && p->auth_ip_acl && pa_ip_acl_check(p->auth_ip_acl, pa_iochannel_get_recv_fd(io)) > 0) {
+ if (o->auth_anonymous) {
+ pa_log_info("Client authenticated anonymously.");
+ c->authorized = TRUE;
+ }
+
+ if (!c->authorized &&
+ o->auth_ip_acl &&
+ pa_ip_acl_check(o->auth_ip_acl, pa_iochannel_get_recv_fd(io)) > 0) {
+
pa_log_info("Client authenticated by IP ACL.");
c->authorized = TRUE;
}
@@ -1402,70 +1461,163 @@ static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata)
pa_idxset_put(p->connections, c, &c->index);
}
-/*** entry points ***/
-
-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;
- pa_bool_t public = FALSE;
- const char *acl;
+void pa_esound_protocol_disconnect(pa_esound_protocol *p, pa_module *m) {
+ connection *c;
+ void *state = NULL;
- pa_assert(core);
- pa_assert(server);
+ pa_assert(p);
pa_assert(m);
- pa_assert(ma);
- if (pa_modargs_get_value_boolean(ma, "auth-anonymous", &public) < 0) {
- pa_log("auth-anonymous= expects a boolean argument.");
- goto fail;
- }
+ while ((c = pa_idxset_iterate(p->connections, &state, NULL)))
+ if (c->options->module == m)
+ connection_unlink(c);
+}
- p = pa_xnew(pa_protocol_esound, 1);
+static pa_esound_protocol* esound_protocol_new(pa_core *c) {
+ pa_esound_protocol *p;
- if (pa_authkey_load_auto(pa_modargs_get_value(ma, "cookie", DEFAULT_COOKIE_FILE), p->esd_key, sizeof(p->esd_key)) < 0)
- goto fail;
-
- if ((acl = pa_modargs_get_value(ma, "auth-ip-acl", NULL))) {
-
- if (!(p->auth_ip_acl = pa_ip_acl_new(acl))) {
- pa_log("Failed to parse IP ACL '%s'", acl);
- goto fail;
- }
- } else
- p->auth_ip_acl = NULL;
+ pa_assert(c);
- p->core = core;
- p->module = m;
- p->public = public;
- p->server = server;
- pa_socket_server_set_callback(p->server, on_connection, p);
+ p = pa_xnew(pa_esound_protocol, 1);
+ PA_REFCNT_INIT(p);
+ p->core = c;
p->connections = pa_idxset_new(NULL, NULL);
-
- p->sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL));
- p->source_name = pa_xstrdup(pa_modargs_get_value(ma, "source", NULL));
p->n_player = 0;
+ pa_assert_se(pa_shared_set(c, "esound-protocol", p) >= 0);
+
return p;
+}
-fail:
- pa_xfree(p);
- return NULL;
+pa_esound_protocol* pa_esound_protocol_get(pa_core *c) {
+ pa_esound_protocol *p;
+
+ if ((p = pa_shared_get(c, "esound-protocol")))
+ return pa_esound_protocol_ref(p);
+
+ return esound_protocol_new(c);
}
-void pa_protocol_esound_free(pa_protocol_esound *p) {
+pa_esound_protocol* pa_esound_protocol_ref(pa_esound_protocol *p) {
+ pa_assert(p);
+ pa_assert(PA_REFCNT_VALUE(p) >= 1);
+
+ PA_REFCNT_INC(p);
+
+ return p;
+}
+
+void pa_esound_protocol_unref(pa_esound_protocol *p) {
connection *c;
pa_assert(p);
+ pa_assert(PA_REFCNT_VALUE(p) >= 1);
+
+ if (PA_REFCNT_DEC(p) > 0)
+ return;
while ((c = pa_idxset_first(p->connections, NULL)))
connection_unlink(c);
+
pa_idxset_free(p->connections, NULL, NULL);
- pa_socket_server_unref(p->server);
+ pa_assert_se(pa_shared_remove(p->core, "esound-protocol") >= 0);
- if (p->auth_ip_acl)
- pa_ip_acl_free(p->auth_ip_acl);
+ pa_xfree(p);
+}
- pa_xfree(p->sink_name);
- pa_xfree(p->source_name);
+pa_esound_options* pa_esound_options_new(void) {
+ pa_esound_options *o;
- pa_xfree(p);
+ o = pa_xnew0(pa_esound_options, 1);
+ PA_REFCNT_INIT(o);
+
+ return o;
+}
+
+pa_esound_options* pa_esound_options_ref(pa_esound_options *o) {
+ pa_assert(o);
+ pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+ PA_REFCNT_INC(o);
+
+ return o;
+}
+
+void pa_esound_options_unref(pa_esound_options *o) {
+ pa_assert(o);
+ pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+ if (PA_REFCNT_DEC(o) > 0)
+ return;
+
+ if (o->auth_ip_acl)
+ pa_ip_acl_free(o->auth_ip_acl);
+
+ if (o->auth_cookie)
+ pa_auth_cookie_unref(o->auth_cookie);
+
+ pa_xfree(o->default_sink);
+ pa_xfree(o->default_source);
+
+ pa_xfree(o);
+}
+
+int pa_esound_options_parse(pa_esound_options *o, pa_core *c, pa_modargs *ma) {
+ pa_bool_t enabled;
+ const char *acl;
+
+ pa_assert(o);
+ pa_assert(PA_REFCNT_VALUE(o) >= 1);
+ pa_assert(ma);
+
+ if (pa_modargs_get_value_boolean(ma, "auth-anonymous", &o->auth_anonymous) < 0) {
+ pa_log("auth-anonymous= expects a boolean argument.");
+ return -1;
+ }
+
+ if ((acl = pa_modargs_get_value(ma, "auth-ip-acl", NULL))) {
+ pa_ip_acl *ipa;
+
+ if (!(ipa = pa_ip_acl_new(acl))) {
+ pa_log("Failed to parse IP ACL '%s'", acl);
+ return -1;
+ }
+
+ if (o->auth_ip_acl)
+ pa_ip_acl_free(o->auth_ip_acl);
+
+ o->auth_ip_acl = ipa;
+ }
+
+ enabled = TRUE;
+ if (pa_modargs_get_value_boolean(ma, "auth-cookie-enabled", &enabled) < 0) {
+ pa_log("auth-cookie-enabled= expects a boolean argument.");
+ return -1;
+ }
+
+ if (o->auth_cookie)
+ pa_auth_cookie_unref(o->auth_cookie);
+
+ if (enabled) {
+ const char *cn;
+
+ /* The new name for this is 'auth-cookie', for compat reasons
+ * we check the old name too */
+ if (!(cn = pa_modargs_get_value(ma, "auth-cookie", NULL)))
+ if (!(cn = pa_modargs_get_value(ma, "cookie", NULL)))
+ cn = DEFAULT_COOKIE_FILE;
+
+ if (!(o->auth_cookie = pa_auth_cookie_get(c, cn, ESD_KEY_LEN)))
+ return -1;
+
+ } else
+ o->auth_cookie = NULL;
+
+ pa_xfree(o->default_sink);
+ o->default_sink = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL));
+
+ pa_xfree(o->default_source);
+ o->default_source = pa_xstrdup(pa_modargs_get_value(ma, "source", NULL));
+
+ return 0;
}
diff --git a/src/pulsecore/protocol-esound.h b/src/pulsecore/protocol-esound.h
index 7f04edd..232df66 100644
--- a/src/pulsecore/protocol-esound.h
+++ b/src/pulsecore/protocol-esound.h
@@ -1,8 +1,6 @@
#ifndef fooprotocolesoundhfoo
#define fooprotocolesoundhfoo
-/* $Id: protocol-esound.h 1426 2007-02-13 15:35:19Z ossman $ */
-
/***
This file is part of PulseAudio.
@@ -26,13 +24,35 @@
***/
#include <pulsecore/core.h>
-#include <pulsecore/socket-server.h>
+#include <pulsecore/ipacl.h>
+#include <pulsecore/auth-cookie.h>
+#include <pulsecore/iochannel.h>
#include <pulsecore/module.h>
#include <pulsecore/modargs.h>
-typedef struct pa_protocol_esound pa_protocol_esound;
+typedef struct pa_esound_protocol pa_esound_protocol;
+
+typedef struct pa_esound_options {
+ PA_REFCNT_DECLARE;
+
+ pa_module *module;
+
+ pa_bool_t auth_anonymous;
+ pa_ip_acl *auth_ip_acl;
+ pa_auth_cookie *auth_cookie;
+
+ char *default_sink, *default_source;
+} pa_esound_options;
+
+pa_esound_protocol* pa_esound_protocol_get(pa_core*core);
+pa_esound_protocol* pa_esound_protocol_ref(pa_esound_protocol *p);
+void pa_esound_protocol_unref(pa_esound_protocol *p);
+void pa_esound_protocol_connect(pa_esound_protocol *p, pa_iochannel *io, pa_esound_options *o);
+void pa_esound_protocol_disconnect(pa_esound_protocol *p, pa_module *m);
-pa_protocol_esound* pa_protocol_esound_new(pa_core*core, pa_socket_server *server, pa_module *m, pa_modargs *ma);
-void pa_protocol_esound_free(pa_protocol_esound *p);
+pa_esound_options* pa_esound_options_new(void);
+pa_esound_options* pa_esound_options_ref(pa_esound_options *o);
+void pa_esound_options_unref(pa_esound_options *o);
+int pa_esound_options_parse(pa_esound_options *o, pa_core *c, pa_modargs *ma);
#endif
diff --git a/src/pulsecore/protocol-http.c b/src/pulsecore/protocol-http.c
index 8ae318e..c89d48b 100644
--- a/src/pulsecore/protocol-http.c
+++ b/src/pulsecore/protocol-http.c
@@ -1,5 +1,3 @@
-/* $Id: protocol-http.c 1971 2007-10-28 19:13:50Z lennart $ */
-
/***
This file is part of PulseAudio.
@@ -37,6 +35,7 @@
#include <pulsecore/log.h>
#include <pulsecore/namereg.h>
#include <pulsecore/cli-text.h>
+#include <pulsecore/shared.h>
#include "protocol-http.h"
@@ -50,16 +49,21 @@
#define URL_STATUS "/status"
struct connection {
- pa_protocol_http *protocol;
+ pa_http_protocol *protocol;
pa_ioline *line;
- enum { REQUEST_LINE, MIME_HEADER, DATA } state;
+ enum {
+ REQUEST_LINE,
+ MIME_HEADER,
+ DATA
+ } state;
char *url;
+ pa_module *module;
};
-struct pa_protocol_http {
- pa_module *module;
+struct pa_http_protocol {
+ PA_REFCNT_DECLARE;
+
pa_core *core;
- pa_socket_server*server;
pa_idxset *connections;
};
@@ -103,14 +107,13 @@ static void http_message(struct connection *c, int code, const char *msg, const
}
-static void connection_free(struct connection *c, int del) {
+static void connection_unlink(struct connection *c) {
pa_assert(c);
if (c->url)
pa_xfree(c->url);
- if (del)
- pa_idxset_remove_by_data(c->protocol->connections, c, NULL);
+ pa_idxset_remove_by_data(c->protocol->connections, c, NULL);
pa_ioline_unref(c->line);
pa_xfree(c);
@@ -123,7 +126,7 @@ static void line_callback(pa_ioline *line, const char *s, void *userdata) {
if (!s) {
/* EOF */
- connection_free(c, 1);
+ connection_unlink(c);
return;
}
@@ -168,7 +171,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));
@@ -222,16 +225,15 @@ fail:
internal_server_error(c);
}
-static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata) {
- pa_protocol_http *p = userdata;
+void pa_http_protocol_connect(pa_http_protocol *p, pa_iochannel *io, pa_module *m) {
struct connection *c;
- pa_assert(s);
- pa_assert(io);
pa_assert(p);
+ pa_assert(io);
+ pa_assert(m);
if (pa_idxset_size(p->connections)+1 > MAX_CONNECTIONS) {
- pa_log_warn("Warning! Too many connections (%u), dropping incoming connection.", MAX_CONNECTIONS);
+ pa_log("Warning! Too many connections (%u), dropping incoming connection.", MAX_CONNECTIONS);
pa_iochannel_free(io);
return;
}
@@ -241,37 +243,73 @@ static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata)
c->line = pa_ioline_new(io);
c->state = REQUEST_LINE;
c->url = NULL;
+ c->module = m;
pa_ioline_set_callback(c->line, line_callback, c);
+
pa_idxset_put(p->connections, c, NULL);
}
-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;
+void pa_http_protocol_disconnect(pa_http_protocol *p, pa_module *m) {
+ struct connection *c;
+ void *state = NULL;
+
+ pa_assert(p);
+ pa_assert(m);
- pa_core_assert_ref(core);
- pa_assert(server);
+ while ((c = pa_idxset_iterate(p->connections, &state, NULL)))
+ if (c->module == m)
+ connection_unlink(c);
+}
+
+static pa_http_protocol* http_protocol_new(pa_core *c) {
+ pa_http_protocol *p;
+
+ pa_assert(c);
- p = pa_xnew(pa_protocol_http, 1);
- p->module = m;
- p->core = core;
- p->server = server;
+ p = pa_xnew(pa_http_protocol, 1);
+ PA_REFCNT_INIT(p);
+ p->core = c;
p->connections = pa_idxset_new(NULL, NULL);
- pa_socket_server_set_callback(p->server, on_connection, p);
+ pa_assert_se(pa_shared_set(c, "http-protocol", p) >= 0);
return p;
}
-static void free_connection(void *p, PA_GCC_UNUSED void *userdata) {
+pa_http_protocol* pa_http_protocol_get(pa_core *c) {
+ pa_http_protocol *p;
+
+ if ((p = pa_shared_get(c, "http-protocol")))
+ return pa_http_protocol_ref(p);
+
+ return http_protocol_new(c);
+}
+
+pa_http_protocol* pa_http_protocol_ref(pa_http_protocol *p) {
pa_assert(p);
- connection_free(p, 0);
+ pa_assert(PA_REFCNT_VALUE(p) >= 1);
+
+ PA_REFCNT_INC(p);
+
+ return p;
}
-void pa_protocol_http_free(pa_protocol_http *p) {
+void pa_http_protocol_unref(pa_http_protocol *p) {
+ struct connection *c;
+
pa_assert(p);
+ pa_assert(PA_REFCNT_VALUE(p) >= 1);
+
+ if (PA_REFCNT_DEC(p) > 0)
+ return;
+
+ while ((c = pa_idxset_first(p->connections, NULL)))
+ connection_unlink(c);
+
+ pa_idxset_free(p->connections, NULL, NULL);
+
+ pa_assert_se(pa_shared_remove(p->core, "http-protocol") >= 0);
- pa_idxset_free(p->connections, free_connection, NULL);
- pa_socket_server_unref(p->server);
pa_xfree(p);
}
diff --git a/src/pulsecore/protocol-http.h b/src/pulsecore/protocol-http.h
index 6ed7b65..7e8f976 100644
--- a/src/pulsecore/protocol-http.h
+++ b/src/pulsecore/protocol-http.h
@@ -1,8 +1,6 @@
#ifndef fooprotocolhttphfoo
#define fooprotocolhttphfoo
-/* $Id: protocol-http.h 1426 2007-02-13 15:35:19Z ossman $ */
-
/***
This file is part of PulseAudio.
@@ -25,13 +23,17 @@
***/
#include <pulsecore/core.h>
-#include <pulsecore/socket-server.h>
#include <pulsecore/module.h>
#include <pulsecore/modargs.h>
+#include <pulsecore/iochannel.h>
+
-typedef struct pa_protocol_http pa_protocol_http;
+typedef struct pa_http_protocol pa_http_protocol;
-pa_protocol_http* pa_protocol_http_new(pa_core *core, pa_socket_server *server, pa_module *m, pa_modargs *ma);
-void pa_protocol_http_free(pa_protocol_http *n);
+pa_http_protocol* pa_http_protocol_get(pa_core *core);
+pa_http_protocol* pa_http_protocol_ref(pa_http_protocol *p);
+void pa_http_protocol_unref(pa_http_protocol *p);
+void pa_http_protocol_connect(pa_http_protocol *p, pa_iochannel *io, pa_module *m);
+void pa_http_protocol_disconnect(pa_http_protocol *p, pa_module *m);
#endif
diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c
index 0a0b911..778aab5 100644
--- a/src/pulsecore/protocol-native.c
+++ b/src/pulsecore/protocol-native.c
@@ -1,5 +1,3 @@
-/* $Id: protocol-native.c 2188 2008-03-29 00:31:10Z lennart $ */
-
/***
This file is part of PulseAudio.
@@ -52,9 +50,8 @@
#include <pulsecore/core-subscribe.h>
#include <pulsecore/log.h>
#include <pulsecore/autoload.h>
-#include <pulsecore/authkey-prop.h>
#include <pulsecore/strlist.h>
-#include <pulsecore/props.h>
+#include <pulsecore/shared.h>
#include <pulsecore/sample-util.h>
#include <pulsecore/llist.h>
#include <pulsecore/creds.h>
@@ -71,50 +68,66 @@
#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;
+struct pa_native_protocol;
typedef struct record_stream {
pa_msgobject parent;
- connection *connection;
+ pa_native_connection *connection;
uint32_t index;
pa_source_output *source_output;
pa_memblockq *memblockq;
size_t fragment_size;
+ pa_usec_t source_latency;
} record_stream;
+PA_DECLARE_CLASS(record_stream);
+#define RECORD_STREAM(o) (record_stream_cast(o))
+static PA_DEFINE_CHECK_TYPE(record_stream, pa_msgobject);
+
typedef struct output_stream {
pa_msgobject parent;
} output_stream;
+PA_DECLARE_CLASS(output_stream);
+#define OUTPUT_STREAM(o) (output_stream_cast(o))
+static PA_DEFINE_CHECK_TYPE(output_stream, pa_msgobject);
+
typedef struct playback_stream {
output_stream parent;
- connection *connection;
+ pa_native_connection *connection;
uint32_t index;
pa_sink_input *sink_input;
pa_memblockq *memblockq;
- int drain_request;
+ pa_bool_t is_underrun:1;
+ pa_bool_t drain_request:1;
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;
+PA_DECLARE_CLASS(playback_stream);
+#define PLAYBACK_STREAM(o) (playback_stream_cast(o))
+static PA_DEFINE_CHECK_TYPE(playback_stream, output_stream);
+
typedef struct upload_stream {
output_stream parent;
- connection *connection;
+ pa_native_connection *connection;
uint32_t index;
pa_memchunk memchunk;
@@ -122,14 +135,20 @@ 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;
+PA_DECLARE_CLASS(upload_stream);
+#define UPLOAD_STREAM(o) (upload_stream_cast(o))
+static PA_DEFINE_CHECK_TYPE(upload_stream, output_stream);
- int authorized;
+struct pa_native_connection {
+ pa_msgobject parent;
+ pa_native_protocol *protocol;
+ pa_native_options *options;
+ pa_bool_t authorized:1;
+ pa_bool_t is_local:1;
uint32_t version;
- pa_protocol_native *protocol;
pa_client *client;
pa_pstream *pstream;
pa_pdispatch *pdispatch;
@@ -139,38 +158,20 @@ struct connection {
pa_time_event *auth_timeout_event;
};
-PA_DECLARE_CLASS(record_stream);
-#define RECORD_STREAM(o) (record_stream_cast(o))
-static PA_DEFINE_CHECK_TYPE(record_stream, pa_msgobject);
+PA_DECLARE_CLASS(pa_native_connection);
+#define PA_NATIVE_CONNECTION(o) (pa_native_connection_cast(o))
+static PA_DEFINE_CHECK_TYPE(pa_native_connection, pa_msgobject);
-PA_DECLARE_CLASS(output_stream);
-#define OUTPUT_STREAM(o) (output_stream_cast(o))
-static PA_DEFINE_CHECK_TYPE(output_stream, pa_msgobject);
+struct pa_native_protocol {
+ PA_REFCNT_DECLARE;
-PA_DECLARE_CLASS(playback_stream);
-#define PLAYBACK_STREAM(o) (playback_stream_cast(o))
-static PA_DEFINE_CHECK_TYPE(playback_stream, output_stream);
-
-PA_DECLARE_CLASS(upload_stream);
-#define UPLOAD_STREAM(o) (upload_stream_cast(o))
-static PA_DEFINE_CHECK_TYPE(upload_stream, output_stream);
-
-PA_DECLARE_CLASS(connection);
-#define CONNECTION(o) (connection_cast(o))
-static PA_DEFINE_CHECK_TYPE(connection, pa_msgobject);
-
-struct pa_protocol_native {
- pa_module *module;
pa_core *core;
- int public;
- pa_socket_server *server;
pa_idxset *connections;
- uint8_t auth_cookie[PA_NATIVE_COOKIE_LENGTH];
- int auth_cookie_in_property;
-#ifdef HAVE_CREDS
- char *auth_group;
-#endif
- pa_ip_acl *auth_ip_acl;
+
+ pa_strlist *servers;
+ pa_hook hooks[PA_NATIVE_HOOK_MAX];
+
+ pa_hashmap *extensions;
};
enum {
@@ -187,7 +188,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,14 +201,16 @@ 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 sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes);
-static void send_memblock(connection *c);
-static void request_bytes(struct playback_stream*s);
+static void native_connection_send_memblock(pa_native_connection *c);
+static void playback_stream_request_bytes(struct playback_stream*s);
static void source_output_kill_cb(pa_source_output *o);
static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk);
@@ -254,6 +258,9 @@ static void command_move_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag
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 void command_extension(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,
@@ -335,7 +342,17 @@ static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {
[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_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,
+
+ [PA_COMMAND_EXTENSION] = command_extension
};
/* structure management */
@@ -359,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);
@@ -366,10 +386,12 @@ static void upload_stream_free(pa_object *o) {
}
static upload_stream* upload_stream_new(
- connection *c,
+ pa_native_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;
@@ -377,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;
@@ -386,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);
@@ -436,7 +461,7 @@ static int record_stream_process_msg(pa_msgobject *o, int code, void*userdata, i
}
if (!pa_pstream_is_pending(s->connection->pstream))
- send_memblock(s->connection);
+ native_connection_send_memblock(s->connection);
break;
}
@@ -444,15 +469,128 @@ static int record_stream_process_msg(pa_msgobject *o, int code, void*userdata, i
return 0;
}
+static void fix_record_buffer_attr_pre(
+ record_stream *s,
+ pa_bool_t adjust_latency,
+ pa_bool_t early_requests,
+ uint32_t *maxlength,
+ uint32_t *fragsize) {
+
+ size_t frame_size;
+ pa_usec_t orig_fragsize_usec, fragsize_usec, source_usec;
+
+ pa_assert(s);
+ pa_assert(maxlength);
+ pa_assert(fragsize);
+
+ frame_size = pa_frame_size(&s->source_output->sample_spec);
+
+ if (*maxlength == (uint32_t) -1 || *maxlength > MAX_MEMBLOCKQ_LENGTH)
+ *maxlength = MAX_MEMBLOCKQ_LENGTH;
+ if (*maxlength <= 0)
+ *maxlength = (uint32_t) frame_size;
+
+ if (*fragsize == (uint32_t) -1)
+ *fragsize = (uint32_t) pa_usec_to_bytes(DEFAULT_FRAGSIZE_MSEC*PA_USEC_PER_MSEC, &s->source_output->sample_spec);
+ if (*fragsize <= 0)
+ *fragsize = (uint32_t) frame_size;
+
+ orig_fragsize_usec = fragsize_usec = pa_bytes_to_usec(*fragsize, &s->source_output->sample_spec);
+
+ if (early_requests) {
+
+ /* In early request mode we need to emulate the classic
+ * fragment-based playback model. We do this setting the source
+ * latency to the fragment size. */
+
+ source_usec = fragsize_usec;
+
+ } else if (adjust_latency) {
+
+ /* 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. */
+
+ source_usec = fragsize_usec/2;
+
+ } else {
+
+ /* Ok, the user didn't ask us to adjust the latency, hence we
+ * don't */
+
+ source_usec = 0;
+ }
+
+ if (source_usec > 0)
+ s->source_latency = pa_source_output_set_requested_latency(s->source_output, source_usec);
+ else
+ s->source_latency = 0;
+
+ if (early_requests) {
+
+ /* Ok, we didn't necessarily get what we were asking for, so
+ * let's tell the user */
+
+ fragsize_usec = s->source_latency;
+
+ } else if (adjust_latency) {
+
+ /* Now subtract what we actually got */
+
+ if (fragsize_usec >= s->source_latency*2)
+ fragsize_usec -= s->source_latency;
+ else
+ fragsize_usec = s->source_latency;
+ }
+
+ if (pa_usec_to_bytes(orig_fragsize_usec, &s->source_output->sample_spec) !=
+ pa_usec_to_bytes(fragsize_usec, &s->source_output->sample_spec))
+
+ *fragsize = (uint32_t) pa_usec_to_bytes(fragsize_usec, &s->source_output->sample_spec);
+
+ if (*fragsize <= 0)
+ *fragsize = (uint32_t) frame_size;
+}
+
+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 = (uint32_t) 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 = (uint32_t) s->fragment_size;
+}
+
static record_stream* record_stream_new(
- connection *c,
+ pa_native_connection *c,
pa_source *source,
pa_sample_spec *ss,
pa_channel_map *map,
- const char *name,
+ pa_bool_t peak_detect,
uint32_t *maxlength,
- uint32_t fragment_size,
- pa_source_output_flags_t flags) {
+ uint32_t *fragsize,
+ pa_source_output_flags_t flags,
+ pa_proplist *p,
+ pa_bool_t adjust_latency,
+ pa_sink_input *direct_on_input,
+ pa_bool_t early_requests) {
record_stream *s;
pa_source_output *source_output;
@@ -461,20 +599,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);
- data.module = c->protocol->module;
+
+ 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->options->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;
- if (!(source_output = pa_source_output_new(c->protocol->core, &data, flags)))
+ source_output = pa_source_output_new(c->protocol->core, &data, flags);
+
+ pa_source_output_new_data_done(&data);
+
+ if (!source_output)
return NULL;
s = pa_msgobject_new(record_stream);
@@ -482,6 +628,7 @@ 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;
@@ -489,33 +636,45 @@ static record_stream* record_stream_new(
s->source_output->suspend = source_output_suspend_cb;
s->source_output->userdata = s;
+ fix_record_buffer_attr_pre(s, adjust_latency, early_requests, maxlength, fragsize);
+
s->memblockq = pa_memblockq_new(
0,
*maxlength,
0,
- base = pa_frame_size(&s->source_output->sample_spec),
+ base = pa_frame_size(&source_output->sample_spec),
1,
0,
+ 0,
NULL);
- *maxlength = pa_memblockq_get_maxlength(s->memblockq);
-
- s->fragment_size = (fragment_size/base)*base;
- if (s->fragment_size <= 0)
- s->fragment_size = base;
-
- if (s->fragment_size > *maxlength)
- s->fragment_size = *maxlength;
+ 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;
}
+static void record_stream_send_killed(record_stream *r) {
+ pa_tagstruct *t;
+ record_stream_assert_ref(r);
+
+ t = pa_tagstruct_new(NULL, 0);
+ pa_tagstruct_putu32(t, PA_COMMAND_RECORD_STREAM_KILLED);
+ pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
+ pa_tagstruct_putu32(t, r->index);
+ pa_pstream_send_tagstruct(r->connection->pstream, t);
+}
+
static void playback_stream_unlink(playback_stream *s) {
pa_assert(s);
@@ -559,21 +718,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)
- break;
-
- l += k;
-
- if (l < s->minreq)
+ if ((l = (uint32_t) pa_atomic_load(&s->missing)) <= 0)
break;
- if (pa_atomic_sub(&s->missing, k) <= k)
+ if (pa_atomic_cmpxchg(&s->missing, (int) l, 0))
break;
}
- if (l < s->minreq)
+ if (l <= 0)
break;
t = pa_tagstruct_new(NULL, 0);
@@ -583,13 +735,15 @@ 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;
}
case PLAYBACK_STREAM_MESSAGE_UNDERFLOW: {
pa_tagstruct *t;
+/* pa_log("signalling underflow"); */
+
/* Report that we're empty */
t = pa_tagstruct_new(NULL, 0);
pa_tagstruct_putu32(t, PA_COMMAND_UNDERFLOW);
@@ -611,41 +765,222 @@ 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,
+ pa_bool_t early_requests,
+ uint32_t *maxlength,
+ uint32_t *tlength,
+ uint32_t* prebuf,
+ uint32_t* minreq) {
+
+ size_t frame_size;
+ pa_usec_t orig_tlength_usec, tlength_usec, orig_minreq_usec, minreq_usec, sink_usec;
+
+ pa_assert(s);
+ pa_assert(maxlength);
+ pa_assert(tlength);
+ pa_assert(prebuf);
+ pa_assert(minreq);
+
+ frame_size = pa_frame_size(&s->sink_input->sample_spec);
+
+ if (*maxlength == (uint32_t) -1 || *maxlength > MAX_MEMBLOCKQ_LENGTH)
+ *maxlength = MAX_MEMBLOCKQ_LENGTH;
+ if (*maxlength <= 0)
+ *maxlength = (uint32_t) frame_size;
+
+ if (*tlength == (uint32_t) -1)
+ *tlength = (uint32_t) pa_usec_to_bytes_round_up(DEFAULT_TLENGTH_MSEC*PA_USEC_PER_MSEC, &s->sink_input->sample_spec);
+ if (*tlength <= 0)
+ *tlength = (uint32_t) frame_size;
+
+ if (*minreq == (uint32_t) -1)
+ *minreq = (uint32_t) pa_usec_to_bytes_round_up(DEFAULT_PROCESS_MSEC*PA_USEC_PER_MSEC, &s->sink_input->sample_spec);
+ if (*minreq <= 0)
+ *minreq = (uint32_t) frame_size;
+
+ if (*tlength < *minreq+frame_size)
+ *tlength = *minreq+(uint32_t) frame_size;
+
+ orig_tlength_usec = tlength_usec = pa_bytes_to_usec(*tlength, &s->sink_input->sample_spec);
+ orig_minreq_usec = minreq_usec = pa_bytes_to_usec(*minreq, &s->sink_input->sample_spec);
+
+ pa_log_info("Requested tlength=%0.2f ms, minreq=%0.2f ms",
+ (double) tlength_usec / PA_USEC_PER_MSEC,
+ (double) minreq_usec / PA_USEC_PER_MSEC);
+
+ if (early_requests) {
+
+ /* In early request mode we need to emulate the classic
+ * fragment-based playback model. We do this setting the sink
+ * latency to the fragment size. */
+
+ sink_usec = minreq_usec;
+
+ pa_log_debug("Early requests mode enabled, configuring sink latency to minreq.");
+
+ } else if (adjust_latency) {
+
+ /* 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;
+
+ pa_log_debug("Adjust latency mode enabled, configuring sink latency to half of overall latency.");
+
+ } 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;
+
+ pa_log_debug("Traditional mode enabled, modifying sink usec only for compat with minreq.");
+ }
+
+ s->sink_latency = pa_sink_input_set_requested_latency(s->sink_input, sink_usec);
+
+ if (early_requests) {
+
+ /* Ok, we didn't necessarily get what we were asking for, so
+ * let's tell the user */
+
+ minreq_usec = s->sink_latency;
+
+ } else 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;
+ }
+
+ /* FIXME: This is actually larger than necessary, since not all of
+ * the sink latency is actually rewritable. */
+ if (tlength_usec < s->sink_latency + 2*minreq_usec)
+ tlength_usec = s->sink_latency + 2*minreq_usec;
+
+ if (pa_usec_to_bytes_round_up(orig_tlength_usec, &s->sink_input->sample_spec) !=
+ pa_usec_to_bytes_round_up(tlength_usec, &s->sink_input->sample_spec))
+ *tlength = (uint32_t) pa_usec_to_bytes_round_up(tlength_usec, &s->sink_input->sample_spec);
+
+ if (pa_usec_to_bytes(orig_minreq_usec, &s->sink_input->sample_spec) !=
+ pa_usec_to_bytes(minreq_usec, &s->sink_input->sample_spec))
+ *minreq = (uint32_t) pa_usec_to_bytes(minreq_usec, &s->sink_input->sample_spec);
+
+ if (*minreq <= 0) {
+ *minreq = (uint32_t) frame_size;
+ *tlength += (uint32_t) frame_size*2;
+ }
+
+ if (*tlength <= *minreq)
+ *tlength = *minreq*2 + (uint32_t) frame_size;
+
+ if (*prebuf == (uint32_t) -1 || *prebuf > *tlength)
+ *prebuf = *tlength;
+}
+
+static void fix_playback_buffer_attr_post(
+ playback_stream *s,
+ uint32_t *maxlength,
+ uint32_t *tlength,
+ uint32_t* prebuf,
+ uint32_t* minreq) {
+
+ pa_assert(s);
+ pa_assert(maxlength);
+ pa_assert(tlength);
+ pa_assert(prebuf);
+ pa_assert(minreq);
+
+ *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_native_connection *c,
pa_sink *sink,
pa_sample_spec *ss,
pa_channel_map *map,
- const char *name,
uint32_t *maxlength,
uint32_t *tlength,
uint32_t *prebuf,
uint32_t *minreq,
pa_cvolume *volume,
+ pa_bool_t muted,
uint32_t syncid,
uint32_t *missing,
- pa_sink_input_flags_t flags) {
+ pa_sink_input_flags_t flags,
+ pa_proplist *p,
+ pa_bool_t adjust_latency,
+ pa_bool_t early_requests) {
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(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)) {
@@ -667,17 +1002,25 @@ 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->options->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;
+ if (volume)
+ pa_sink_input_new_data_set_volume(&data, volume);
+ 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, flags)))
+ 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);
@@ -686,11 +1029,15 @@ static playback_stream* playback_stream_new(
s->connection = c;
s->syncid = syncid;
s->sink_input = sink_input;
- s->underrun = 1;
+ s->is_underrun = TRUE;
+ s->drain_request = FALSE;
+ pa_atomic_store(&s->missing, 0);
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->update_max_request = sink_input_update_max_request_cb;
s->sink_input->kill = sink_input_kill_cb;
s->sink_input->moved = sink_input_moved_cb;
s->sink_input->suspend = sink_input_suspend_cb;
@@ -698,42 +1045,74 @@ static playback_stream* playback_stream_new(
start_index = ssync ? pa_memblockq_get_read_index(ssync->memblockq) : 0;
- silence = pa_silence_memblock_new(c->protocol->core->mempool, &s->sink_input->sample_spec, 0);
+ fix_playback_buffer_attr_pre(s, adjust_latency, early_requests, maxlength, tlength, prebuf, minreq);
+ pa_sink_input_get_silence(sink_input, &silence);
s->memblockq = pa_memblockq_new(
start_index,
*maxlength,
*tlength,
- pa_frame_size(&s->sink_input->sample_spec),
+ 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);
*ss = s->sink_input->sample_spec;
*map = s->sink_input->channel_map;
- s->minreq = pa_memblockq_get_minreq(s->memblockq);
- pa_atomic_store(&s->missing, 0);
- s->drain_request = 0;
-
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;
}
-static int connection_process_msg(pa_msgobject *o, int code, void*userdata, int64_t offset, pa_memchunk *chunk) {
- connection *c = CONNECTION(o);
- connection_assert_ref(c);
+/* Called from thread context */
+static void playback_stream_request_bytes(playback_stream *s) {
+ size_t m, previous_missing;
+
+ playback_stream_assert_ref(s);
+
+ m = pa_memblockq_pop_missing(s->memblockq);
+
+ if (m <= 0)
+ return;
+
+/* pa_log("request_bytes(%lu)", (unsigned long) m); */
+
+ previous_missing = (size_t) pa_atomic_add(&s->missing, (int) m);
+
+ 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 playback_stream_send_killed(playback_stream *p) {
+ pa_tagstruct *t;
+ playback_stream_assert_ref(p);
+
+ t = pa_tagstruct_new(NULL, 0);
+ pa_tagstruct_putu32(t, PA_COMMAND_PLAYBACK_STREAM_KILLED);
+ pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
+ pa_tagstruct_putu32(t, p->index);
+ pa_pstream_send_tagstruct(p->connection->pstream, t);
+}
+
+static int native_connection_process_msg(pa_msgobject *o, int code, void*userdata, int64_t offset, pa_memchunk *chunk) {
+ pa_native_connection *c = PA_NATIVE_CONNECTION(o);
+ pa_native_connection_assert_ref(c);
if (!c->protocol)
return -1;
@@ -752,7 +1131,7 @@ static int connection_process_msg(pa_msgobject *o, int code, void*userdata, int6
return 0;
}
-static void connection_unlink(connection *c) {
+static void native_connection_unlink(pa_native_connection *c) {
record_stream *r;
output_stream *o;
@@ -761,6 +1140,11 @@ static void connection_unlink(connection *c) {
if (!c->protocol)
return;
+ pa_hook_fire(&c->protocol->hooks[PA_NATIVE_HOOK_CONNECTION_UNLINK], c);
+
+ if (c->options)
+ pa_native_options_unref(c->options);
+
while ((r = pa_idxset_first(c->record_streams, NULL)))
record_stream_unlink(r);
@@ -783,15 +1167,15 @@ static void connection_unlink(connection *c) {
pa_assert_se(pa_idxset_remove_by_data(c->protocol->connections, c, NULL) == c);
c->protocol = NULL;
- connection_unref(c);
+ pa_native_connection_unref(c);
}
-static void connection_free(pa_object *o) {
- connection *c = CONNECTION(o);
+static void native_connection_free(pa_object *o) {
+ pa_native_connection *c = PA_NATIVE_CONNECTION(o);
pa_assert(c);
- connection_unlink(c);
+ native_connection_unlink(c);
pa_idxset_free(c->record_streams, NULL, NULL);
pa_idxset_free(c->output_streams, NULL, NULL);
@@ -803,27 +1187,7 @@ static void connection_free(pa_object *o) {
pa_xfree(c);
}
-/* Called from thread context */
-static void request_bytes(playback_stream *s) {
- size_t m, previous_missing;
-
- playback_stream_assert_ref(s);
-
- m = pa_memblockq_pop_missing(s->memblockq);
-
- if (m <= 0)
- return;
-
-/* pa_log("request_bytes(%u)", 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());
- 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) {
+static void native_connection_send_memblock(pa_native_connection *c) {
uint32_t start;
record_stream *r;
@@ -855,29 +1219,44 @@ static void send_memblock(connection *c) {
}
}
-static void send_playback_stream_killed(playback_stream *p) {
- pa_tagstruct *t;
- playback_stream_assert_ref(p);
+/*** sink input callbacks ***/
- t = pa_tagstruct_new(NULL, 0);
- pa_tagstruct_putu32(t, PA_COMMAND_PLAYBACK_STREAM_KILLED);
- pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
- pa_tagstruct_putu32(t, p->index);
- pa_pstream_send_tagstruct(p->connection->pstream, t);
-}
+static void handle_seek(playback_stream *s, int64_t indexw) {
+ playback_stream_assert_ref(s);
-static void send_record_stream_killed(record_stream *r) {
- pa_tagstruct *t;
- record_stream_assert_ref(r);
+/* pa_log("handle_seek: %llu -- %i", (unsigned long long) s->sink_input->thread_info.underrun_for, pa_memblockq_is_readable(s->memblockq)); */
- t = pa_tagstruct_new(NULL, 0);
- pa_tagstruct_putu32(t, PA_COMMAND_RECORD_STREAM_KILLED);
- pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
- pa_tagstruct_putu32(t, r->index);
- pa_pstream_send_tagstruct(r->connection->pstream, t);
-}
+ if (s->sink_input->thread_info.underrun_for > 0) {
-/*** sink input callbacks ***/
+/* 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,
+ (size_t) (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, (size_t) (indexr - indexw), TRUE, FALSE);
+ }
+ }
+
+ playback_stream_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) {
@@ -890,60 +1269,57 @@ 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);
+ pa_memblockq_seek(s->memblockq, (int64_t) chunk->length, PA_SEEK_RELATIVE);
}
- request_bytes(s);
+ handle_seek(s, windex);
- s->underrun = 0;
- return 0;
- }
-
- case SINK_INPUT_MESSAGE_DRAIN: {
-
- 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);
switch (code) {
case SINK_INPUT_MESSAGE_FLUSH:
- func = pa_memblockq_flush;
+ func = pa_memblockq_flush_write;
break;
case SINK_INPUT_MESSAGE_PREBUF_FORCE:
func = pa_memblockq_prebuf_force;
break;
+ case SINK_INPUT_MESSAGE_DRAIN:
case SINK_INPUT_MESSAGE_TRIGGER:
func = pa_memblockq_prebuf_disable;
break;
@@ -952,23 +1328,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;
@@ -978,14 +1363,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;
@@ -1002,7 +1394,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);
@@ -1010,42 +1402,76 @@ 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;
+/* pa_log("%s, pop(): %lu", pa_proplist_gets(i->proplist, PA_PROP_MEDIA_NAME), (unsigned long) pa_memblockq_get_length(s->memblockq)); */
+
+ if (pa_memblockq_is_readable(s->memblockq))
+ s->is_underrun = FALSE;
+ else {
+/* pa_log("%s, UNDERRUN: %lu", pa_proplist_gets(i->proplist, PA_PROP_MEDIA_NAME), (unsigned long) pa_memblockq_get_length(s->memblockq)); */
+
+ if (s->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 (!s->is_underrun)
+ pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_UNDERFLOW, NULL, 0, NULL, NULL);
+
+ s->is_underrun = TRUE;
+
+ playback_stream_request_bytes(s);
}
- if (pa_memblockq_peek(s->memblockq, chunk) < 0) {
-/* pa_log("peek: failure"); */
+ /* This call will not fail with prebuf=0, hence we check for
+ underrun explicitly above */
+ if (pa_memblockq_peek(s->memblockq, chunk) < 0)
return -1;
- }
-/* pa_log("peek: %u", 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);
- request_bytes(s);
+ pa_memblockq_drop(s->memblockq, chunk->length);
+ playback_stream_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);
+}
+
+static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) {
+ playback_stream *s;
- request_bytes(s);
+ pa_sink_input_assert_ref(i);
+ s = PLAYBACK_STREAM(i->userdata);
+ playback_stream_assert_ref(s);
-/* pa_log("after_drop: %u %u", pa_memblockq_get_length(s->memblockq), pa_memblockq_is_readable(s->memblockq)); */
+ pa_memblockq_set_maxrewind(s->memblockq, nbytes);
+}
+
+static void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes) {
+ playback_stream *s;
+ size_t tlength;
+
+ pa_sink_input_assert_ref(i);
+ s = PLAYBACK_STREAM(i->userdata);
+ playback_stream_assert_ref(s);
+
+ tlength = nbytes+2*pa_memblockq_get_minreq(s->memblockq);
+
+ if (pa_memblockq_get_tlength(s->memblockq) < tlength)
+ pa_memblockq_set_tlength(s->memblockq, tlength);
}
/* Called from main context */
@@ -1056,7 +1482,7 @@ static void sink_input_kill_cb(pa_sink_input *i) {
s = PLAYBACK_STREAM(i->userdata);
playback_stream_assert_ref(s);
- send_playback_stream_killed(s);
+ playback_stream_send_killed(s);
playback_stream_unlink(s);
}
@@ -1084,11 +1510,24 @@ static void sink_input_suspend_cb(pa_sink_input *i, pa_bool_t suspend) {
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, FALSE, &maxlength, &tlength, &prebuf, &minreq);
+ pa_memblockq_set_maxlength(s->memblockq, maxlength);
+ pa_memblockq_set_tlength(s->memblockq, tlength);
+ pa_memblockq_set_prebuf(s->memblockq, prebuf);
+ pa_memblockq_set_minreq(s->memblockq, minreq);
+ fix_playback_buffer_attr_post(s, &maxlength, &tlength, &prebuf, &minreq);
+
if (s->connection->version < 12)
return;
@@ -1099,6 +1538,15 @@ static void sink_input_moved_cb(pa_sink_input *i) {
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);
}
@@ -1123,7 +1571,7 @@ static void source_output_kill_cb(pa_source_output *o) {
s = RECORD_STREAM(o->userdata);
record_stream_assert_ref(s);
- send_record_stream_killed(s);
+ record_stream_send_killed(s);
record_stream_unlink(s);
}
@@ -1163,11 +1611,19 @@ static void source_output_suspend_cb(pa_source_output *o, pa_bool_t suspend) {
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, FALSE, &maxlength, &fragsize);
+ pa_memblockq_set_maxlength(s->memblockq, maxlength);
+ fix_record_buffer_attr_post(s, &maxlength, &fragsize);
+
if (s->connection->version < 12)
return;
@@ -1178,14 +1634,21 @@ static void source_output_moved_cb(pa_source_output *o) {
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) {
+static void protocol_error(pa_native_connection *c) {
pa_log("protocol error, kicking client");
- connection_unlink(c);
+ native_connection_unlink(c);
}
#define CHECK_VALIDITY(pstream, expression, tag, error) do { \
@@ -1204,42 +1667,70 @@ static pa_tagstruct *reply_new(uint32_t tag) {
return reply;
}
-static void command_create_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);
+static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
playback_stream *s;
uint32_t maxlength, tlength, prebuf, minreq, sink_index, syncid, missing;
- 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;
- int no_remap = 0, no_remix = 0, fix_format = 0, fix_rate = 0, fix_channels = 0, no_move = 0, variable_rate = 0;
+ 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,
+ early_requests = FALSE;
+
pa_sink_input_flags_t flags = 0;
+ pa_proplist *p;
+ pa_bool_t volume_set = TRUE;
- connection_assert_ref(c);
+ pa_native_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 || !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, !sink_name || pa_namereg_is_valid_name(sink_name), tag, PA_ERR_INVALID);
+ CHECK_VALIDITY(c->pstream, sink_index == PA_INVALID_INDEX || !sink_name, tag, PA_ERR_INVALID);
+ CHECK_VALIDITY(c->pstream, !sink_name || sink_index == PA_INVALID_INDEX, tag, PA_ERR_INVALID);
+ CHECK_VALIDITY(c->pstream, pa_channel_map_valid(&map), tag, PA_ERR_INVALID);
+ 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);
+
+ 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 */
@@ -1250,32 +1741,55 @@ static void command_create_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GC
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 (c->version >= 14) {
+
+ if (pa_tagstruct_get_boolean(t, &volume_set) < 0 ||
+ pa_tagstruct_get_boolean(t, &early_requests) < 0) {
+ protocol_error(c);
+ pa_proplist_free(p);
return;
}
}
if (!pa_tagstruct_eof(t)) {
protocol_error(c);
+ pa_proplist_free(p);
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, 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, tag, PA_ERR_INVALID);
- CHECK_VALIDITY(c->pstream, maxlength <= MAX_MEMBLOCKQ_LENGTH, tag, PA_ERR_INVALID);
-
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;
+ }
}
flags =
@@ -1288,7 +1802,9 @@ static void command_create_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GC
(no_move ? PA_SINK_INPUT_DONT_MOVE : 0) |
(variable_rate ? PA_SINK_INPUT_VARIABLE_RATE : 0);
- s = playback_stream_new(c, sink, &ss, &map, name, &maxlength, &tlength, &prebuf, &minreq, &volume, syncid, &missing, flags);
+ s = playback_stream_new(c, sink, &ss, &map, &maxlength, &tlength, &prebuf, &minreq, volume_set ? &volume : NULL, muted, syncid, &missing, flags, p, adjust_latency, early_requests);
+ pa_proplist_free(p);
+
CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_INVALID);
reply = reply_new(tag);
@@ -1322,14 +1838,17 @@ static void command_create_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GC
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);
}
-static void command_delete_stream(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
- connection *c = CONNECTION(userdata);
+static void command_delete_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
uint32_t channel;
- connection_assert_ref(c);
+ pa_native_connection_assert_ref(c);
pa_assert(t);
if (pa_tagstruct_getu32(t, &channel) < 0 ||
@@ -1383,24 +1902,37 @@ static void command_delete_stream(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t comma
pa_pstream_send_simple_ack(c->pstream, tag);
}
-static void command_create_record_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);
+static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
record_stream *s;
uint32_t maxlength, fragment_size;
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;
- int no_remap = 0, no_remix = 0, fix_format = 0, fix_rate = 0, fix_channels = 0, no_move = 0, variable_rate = 0;
+ 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,
+ early_requests = 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_native_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 ||
@@ -1412,6 +1944,19 @@ static void command_create_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_
return;
}
+ CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+ CHECK_VALIDITY(c->pstream, !source_name || pa_namereg_is_valid_name(source_name), tag, PA_ERR_INVALID);
+ CHECK_VALIDITY(c->pstream, source_index == PA_INVALID_INDEX || !source_name, tag, PA_ERR_INVALID);
+ CHECK_VALIDITY(c->pstream, !source_name || source_index == PA_INVALID_INDEX, tag, PA_ERR_INVALID);
+ CHECK_VALIDITY(c->pstream, pa_sample_spec_valid(&ss), tag, PA_ERR_INVALID);
+ CHECK_VALIDITY(c->pstream, pa_channel_map_valid(&map), tag, PA_ERR_INVALID);
+ CHECK_VALIDITY(c->pstream, map.channels == ss.channels, 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 */
@@ -1422,16 +1967,66 @@ static void command_create_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_
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 (c->version >= 14) {
+
+ if (pa_tagstruct_get_boolean(t, &early_requests) < 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) {
+
+ 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) {
+
+ 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;
+ }
+ }
+
+ 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) |
@@ -1442,24 +2037,9 @@ static void command_create_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_
(no_move ? PA_SOURCE_OUTPUT_DONT_MOVE : 0) |
(variable_rate ? PA_SOURCE_OUTPUT_VARIABLE_RATE : 0);
- 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 > 0, tag, PA_ERR_INVALID);
- CHECK_VALIDITY(c->pstream, maxlength <= MAX_MEMBLOCKQ_LENGTH, tag, PA_ERR_INVALID);
-
- 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);
- } 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);
- }
+ s = record_stream_new(c, source, &ss, &map, peak_detect, &maxlength, &fragment_size, flags, p, adjust_latency, direct_on_input, early_requests);
+ pa_proplist_free(p);
- s = record_stream_new(c, source, &ss, &map, name, &maxlength, fragment_size, flags);
CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_INVALID);
reply = reply_new(tag);
@@ -1471,7 +2051,7 @@ 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) {
@@ -1488,13 +2068,17 @@ static void command_create_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_
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);
}
-static void command_exit(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
- connection *c = CONNECTION(userdata);
+static void command_exit(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
+ int ret;
- connection_assert_ref(c);
+ pa_native_connection_assert_ref(c);
pa_assert(t);
if (!pa_tagstruct_eof(t)) {
@@ -1503,17 +2087,19 @@ static void command_exit(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t
}
CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+ ret = pa_core_exit(c->protocol->core, FALSE, 0);
+ CHECK_VALIDITY(c->pstream, ret >= 0, tag, PA_ERR_ACCESS);
- c->protocol->core->mainloop->quit(c->protocol->core->mainloop, 0);
pa_pstream_send_simple_ack(c->pstream, tag); /* nonsense */
}
-static void command_auth(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
- connection *c = CONNECTION(userdata);
+static void command_auth(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
const void*cookie;
pa_tagstruct *reply;
+ pa_bool_t shm_on_remote = FALSE, do_shm;
- connection_assert_ref(c);
+ pa_native_connection_assert_ref(c);
pa_assert(t);
if (pa_tagstruct_getu32(t, &c->version) < 0 ||
@@ -1529,50 +2115,58 @@ 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 pa_native_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;
- else if (c->protocol->auth_group) {
+ success = TRUE;
+ else if (c->options->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);
+ if ((gid = pa_get_gid_of_group(c->options->auth_group)) == (gid_t) -1)
+ pa_log_warn("Failed to get GID of group '%s'", c->options->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.");
+ if ((r = pa_uid_in_group(creds->uid, c->options->auth_group)) < 0)
+ 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;
+ if (!success && c->options->auth_cookie) {
+ const uint8_t *ac;
+
+ if ((ac = pa_auth_cookie_read(c->options->auth_cookie, PA_NATIVE_COOKIE_LENGTH)))
+ if (memcmp(ac, cookie, PA_NATIVE_COOKIE_LENGTH) == 0)
+ success = TRUE;
+ }
if (!success) {
pa_log_warn("Denied access to client with invalid authorization data.");
@@ -1580,15 +2174,41 @@ 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;
+
+#ifdef HAVE_CREDS
+ 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;
+ }
+#endif
+
+ 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
{
@@ -1606,31 +2226,52 @@ static void command_auth(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t
#endif
}
-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;
+static void command_set_client_name(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
+ const char *name = NULL;
+ pa_proplist *p;
+ pa_tagstruct *reply;
- connection_assert_ref(c);
+ pa_native_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) {
- connection *c = CONNECTION(userdata);
+static void command_lookup(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
const char *name;
uint32_t idx = PA_IDXSET_INVALID;
- connection_assert_ref(c);
+ pa_native_connection_assert_ref(c);
pa_assert(t);
if (pa_tagstruct_gets(t, &name) < 0 ||
@@ -1640,7 +2281,7 @@ static void command_lookup(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uin
}
CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
- CHECK_VALIDITY(c->pstream, name && *name && pa_utf8_valid(name), tag, PA_ERR_INVALID);
+ CHECK_VALIDITY(c->pstream, name && pa_namereg_is_valid_name(name), tag, PA_ERR_INVALID);
if (command == PA_COMMAND_LOOKUP_SINK) {
pa_sink *sink;
@@ -1663,12 +2304,12 @@ static void command_lookup(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uin
}
}
-static void command_drain_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);
+static void command_drain_playback_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
uint32_t idx;
playback_stream *s;
- connection_assert_ref(c);
+ pa_native_connection_assert_ref(c);
pa_assert(t);
if (pa_tagstruct_getu32(t, &idx) < 0 ||
@@ -1685,12 +2326,12 @@ static void command_drain_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC
pa_asyncmsgq_post(s->sink_input->sink->asyncmsgq, PA_MSGOBJECT(s->sink_input), SINK_INPUT_MESSAGE_DRAIN, PA_UINT_TO_PTR(tag), 0, NULL, NULL);
}
-static void command_stat(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
- connection *c = CONNECTION(userdata);
+static void command_stat(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
pa_tagstruct *reply;
const pa_mempool_stat *stat;
- connection_assert_ref(c);
+ pa_native_connection_assert_ref(c);
pa_assert(t);
if (!pa_tagstruct_eof(t)) {
@@ -1707,19 +2348,19 @@ static void command_stat(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t
pa_tagstruct_putu32(reply, (uint32_t) pa_atomic_load(&stat->allocated_size));
pa_tagstruct_putu32(reply, (uint32_t) pa_atomic_load(&stat->n_accumulated));
pa_tagstruct_putu32(reply, (uint32_t) pa_atomic_load(&stat->accumulated_size));
- pa_tagstruct_putu32(reply, pa_scache_total_size(c->protocol->core));
+ pa_tagstruct_putu32(reply, (uint32_t) pa_scache_total_size(c->protocol->core));
pa_pstream_send_tagstruct(c->pstream, reply);
}
-static void command_get_playback_latency(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
- connection *c = CONNECTION(userdata);
+static void command_get_playback_latency(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
pa_tagstruct *reply;
playback_stream *s;
struct timeval tv, now;
uint32_t idx;
pa_usec_t latency;
- connection_assert_ref(c);
+ pa_native_connection_assert_ref(c);
pa_assert(t);
if (pa_tagstruct_getu32(t, &idx) < 0 ||
@@ -1738,27 +2379,33 @@ 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);
}
-static void command_get_record_latency(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
- connection *c = CONNECTION(userdata);
+static void command_get_record_latency(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
pa_tagstruct *reply;
record_stream *s;
struct timeval tv, now;
uint32_t idx;
- connection_assert_ref(c);
+ pa_native_connection_assert_ref(c);
pa_assert(t);
if (pa_tagstruct_getu32(t, &idx) < 0 ||
@@ -1775,7 +2422,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));
@@ -1783,23 +2430,23 @@ static void command_get_record_latency(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UN
pa_pstream_send_tagstruct(c->pstream, reply);
}
-static void command_create_upload_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);
+static void command_create_upload_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_native_connection *c = PA_NATIVE_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_native_connection_assert_ref(c);
pa_assert(t);
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;
}
@@ -1810,9 +2457,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);
- CHECK_VALIDITY(c->pstream, name && *name && pa_utf8_valid(name), tag, PA_ERR_INVALID);
- s = upload_stream_new(c, &ss, &map, name, length);
+ 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 && pa_namereg_is_valid_name(name), tag, PA_ERR_INVALID);
+
+ 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);
@@ -1821,13 +2485,13 @@ static void command_create_upload_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_
pa_pstream_send_tagstruct(c->pstream, reply);
}
-static void command_finish_upload_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);
+static void command_finish_upload_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
uint32_t channel;
upload_stream *s;
uint32_t idx;
- connection_assert_ref(c);
+ pa_native_connection_assert_ref(c);
pa_assert(t);
if (pa_tagstruct_getu32(t, &channel) < 0 ||
@@ -1842,7 +2506,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);
@@ -1850,28 +2514,33 @@ static void command_finish_upload_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_
upload_stream_unlink(s);
}
-static void command_play_sample(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
- connection *c = CONNECTION(userdata);
+static void command_play_sample(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
uint32_t sink_index;
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_native_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);
+ CHECK_VALIDITY(c->pstream, !sink_name || pa_namereg_is_valid_name(sink_name), tag, PA_ERR_INVALID);
+ CHECK_VALIDITY(c->pstream, sink_index == PA_INVALID_INDEX || !sink_name, tag, PA_ERR_INVALID);
+ CHECK_VALIDITY(c->pstream, !sink_name || sink_index == PA_INVALID_INDEX, tag, PA_ERR_INVALID);
+ CHECK_VALIDITY(c->pstream, name && pa_namereg_is_valid_name(name), tag, PA_ERR_INVALID);
if (sink_index != PA_INVALID_INDEX)
sink = pa_idxset_get_by_index(c->protocol->core->sinks, sink_index);
@@ -1880,19 +2549,38 @@ 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;
+ }
+
+ pa_proplist_update(p, PA_UPDATE_MERGE, c->client->proplist);
+
+ 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) {
- connection *c = CONNECTION(userdata);
+static void command_remove_sample(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
const char *name;
- connection_assert_ref(c);
+ pa_native_connection_assert_ref(c);
pa_assert(t);
if (pa_tagstruct_gets(t, &name) < 0 ||
@@ -1902,7 +2590,7 @@ static void command_remove_sample(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED
}
CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
- CHECK_VALIDITY(c->pstream, name && *name && pa_utf8_valid(name), tag, PA_ERR_INVALID);
+ CHECK_VALIDITY(c->pstream, name && pa_namereg_is_valid_name(name), tag, PA_ERR_INVALID);
if (pa_scache_remove_item(c->protocol->core, name) < 0) {
pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY);
@@ -1912,7 +2600,7 @@ static void command_remove_sample(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED
pa_pstream_send_simple_ack(c->pstream, tag);
}
-static void fixup_sample_spec(connection *c, pa_sample_spec *fixed, const pa_sample_spec *original) {
+static void fixup_sample_spec(pa_native_connection *c, pa_sample_spec *fixed, const pa_sample_spec *original) {
pa_assert(c);
pa_assert(fixed);
pa_assert(original);
@@ -1930,7 +2618,7 @@ static void fixup_sample_spec(connection *c, pa_sample_spec *fixed, const pa_sam
}
}
-static void sink_fill_tagstruct(connection *c, pa_tagstruct *t, pa_sink *sink) {
+static void sink_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_sink *sink) {
pa_sample_spec fixed_ss;
pa_assert(t);
@@ -1942,21 +2630,26 @@ static void sink_fill_tagstruct(connection *c, pa_tagstruct *t, pa_sink *sink) {
t,
PA_TAG_U32, sink->index,
PA_TAG_STRING, sink->name,
- PA_TAG_STRING, sink->description,
+ 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),
- PA_TAG_BOOLEAN, pa_sink_get_mute(sink),
+ PA_TAG_CVOLUME, pa_sink_get_volume(sink, FALSE),
+ PA_TAG_BOOLEAN, pa_sink_get_mute(sink, FALSE),
PA_TAG_U32, sink->monitor_source ? sink->monitor_source->index : PA_INVALID_INDEX,
PA_TAG_STRING, sink->monitor_source ? sink->monitor_source->name : NULL,
PA_TAG_USEC, pa_sink_get_latency(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(connection *c, pa_tagstruct *t, pa_source *source) {
+static void source_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_source *source) {
pa_sample_spec fixed_ss;
pa_assert(t);
@@ -1968,28 +2661,38 @@ static void source_fill_tagstruct(connection *c, pa_tagstruct *t, pa_source *sou
t,
PA_TAG_U32, source->index,
PA_TAG_STRING, source->name,
- PA_TAG_STRING, source->description,
+ 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),
- PA_TAG_BOOLEAN, pa_source_get_mute(source),
+ PA_TAG_CVOLUME, pa_source_get_volume(source, FALSE),
+ PA_TAG_BOOLEAN, pa_source_get_mute(source, FALSE),
PA_TAG_U32, source->monitor_of ? source->monitor_of->index : PA_INVALID_INDEX,
PA_TAG_STRING, source->monitor_of ? source->monitor_of->name : NULL,
PA_TAG_USEC, pa_source_get_latency(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(pa_native_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) {
@@ -1999,12 +2702,13 @@ static void module_fill_tagstruct(pa_tagstruct *t, pa_module *module) {
pa_tagstruct_putu32(t, module->index);
pa_tagstruct_puts(t, module->name);
pa_tagstruct_puts(t, module->argument);
- pa_tagstruct_putu32(t, module->n_used);
+ pa_tagstruct_putu32(t, (uint32_t) module->n_used);
pa_tagstruct_put_boolean(t, module->auto_unload);
}
-static void sink_input_fill_tagstruct(connection *c, pa_tagstruct *t, pa_sink_input *s) {
+static void sink_input_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_sink_input *s) {
pa_sample_spec fixed_ss;
+ pa_usec_t sink_latency;
pa_assert(t);
pa_sink_input_assert_ref(s);
@@ -2012,23 +2716,26 @@ static void sink_input_fill_tagstruct(connection *c, pa_tagstruct *t, pa_sink_in
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, &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(connection *c, pa_tagstruct *t, pa_source_output *s) {
+static void source_output_fill_tagstruct(pa_native_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);
@@ -2036,39 +2743,48 @@ static void source_output_fill_tagstruct(connection *c, pa_tagstruct *t, pa_sour
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, &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(connection *c, pa_tagstruct *t, pa_scache_entry *e) {
+static void scache_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_scache_entry *e) {
pa_sample_spec fixed_ss;
pa_assert(t);
pa_assert(e);
- fixup_sample_spec(c, &fixed_ss, &e->sample_spec);
+ 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_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_putu32(t, (uint32_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) {
- connection *c = CONNECTION(userdata);
+static void command_get_info(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
uint32_t idx;
pa_sink *sink = NULL;
pa_source *source = NULL;
@@ -2077,10 +2793,10 @@ static void command_get_info(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, u
pa_sink_input *si = NULL;
pa_source_output *so = NULL;
pa_scache_entry *sce = NULL;
- const char *name;
+ const char *name = NULL;
pa_tagstruct *reply;
- connection_assert_ref(c);
+ pa_native_connection_assert_ref(c);
pa_assert(t);
if (pa_tagstruct_getu32(t, &idx) < 0 ||
@@ -2095,7 +2811,10 @@ static void command_get_info(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, u
}
CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
- CHECK_VALIDITY(c->pstream, idx != PA_INVALID_INDEX || !name || (*name && pa_utf8_valid(name)), tag, PA_ERR_INVALID);
+ CHECK_VALIDITY(c->pstream, !name || pa_namereg_is_valid_name(name), tag, PA_ERR_INVALID);
+ CHECK_VALIDITY(c->pstream, idx != PA_INVALID_INDEX || name, tag, PA_ERR_INVALID);
+ CHECK_VALIDITY(c->pstream, idx == PA_INVALID_INDEX || !name, tag, PA_ERR_INVALID);
+ CHECK_VALIDITY(c->pstream, !name || idx == PA_INVALID_INDEX, tag, PA_ERR_INVALID);
if (command == PA_COMMAND_GET_SINK_INFO) {
if (idx != PA_INVALID_INDEX)
@@ -2134,7 +2853,7 @@ static void command_get_info(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, u
else if (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)
@@ -2146,14 +2865,14 @@ static void command_get_info(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, u
pa_pstream_send_tagstruct(c->pstream, reply);
}
-static void command_get_info_list(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
- connection *c = CONNECTION(userdata);
+static void command_get_info_list(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
pa_idxset *i;
uint32_t idx;
void *p;
pa_tagstruct *reply;
- connection_assert_ref(c);
+ pa_native_connection_assert_ref(c);
pa_assert(t);
if (!pa_tagstruct_eof(t)) {
@@ -2189,7 +2908,7 @@ static void command_get_info_list(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t comma
else if (command == PA_COMMAND_GET_SOURCE_INFO_LIST)
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)
@@ -2206,14 +2925,14 @@ static void command_get_info_list(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t comma
pa_pstream_send_tagstruct(c->pstream, reply);
}
-static void command_get_server_info(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
- connection *c = CONNECTION(userdata);
+static void command_get_server_info(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
pa_tagstruct *reply;
char txt[256];
const char *n;
pa_sample_spec fixed_ss;
- connection_assert_ref(c);
+ pa_native_connection_assert_ref(c);
pa_assert(t);
if (!pa_tagstruct_eof(t)) {
@@ -2227,7 +2946,7 @@ 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_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);
@@ -2244,9 +2963,9 @@ static void command_get_server_info(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSE
static void subscription_cb(pa_core *core, pa_subscription_event_type_t e, uint32_t idx, void *userdata) {
pa_tagstruct *t;
- connection *c = CONNECTION(userdata);
+ pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
- connection_assert_ref(c);
+ pa_native_connection_assert_ref(c);
t = pa_tagstruct_new(NULL, 0);
pa_tagstruct_putu32(t, PA_COMMAND_SUBSCRIBE_EVENT);
@@ -2256,11 +2975,11 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t e, uint3
pa_pstream_send_tagstruct(c->pstream, t);
}
-static void command_subscribe(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
- connection *c = CONNECTION(userdata);
+static void command_subscribe(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
pa_subscription_mask_t m;
- connection_assert_ref(c);
+ pa_native_connection_assert_ref(c);
pa_assert(t);
if (pa_tagstruct_getu32(t, &m) < 0 ||
@@ -2285,13 +3004,13 @@ static void command_subscribe(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint
}
static void command_set_volume(
- PA_GCC_UNUSED pa_pdispatch *pd,
+ pa_pdispatch *pd,
uint32_t command,
uint32_t tag,
pa_tagstruct *t,
void *userdata) {
- connection *c = CONNECTION(userdata);
+ pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
uint32_t idx;
pa_cvolume volume;
pa_sink *sink = NULL;
@@ -2299,7 +3018,7 @@ static void command_set_volume(
pa_sink_input *si = NULL;
const char *name = NULL;
- connection_assert_ref(c);
+ pa_native_connection_assert_ref(c);
pa_assert(t);
if (pa_tagstruct_getu32(t, &idx) < 0 ||
@@ -2312,7 +3031,10 @@ static void command_set_volume(
}
CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
- CHECK_VALIDITY(c->pstream, idx != PA_INVALID_INDEX || !name || (*name && pa_utf8_valid(name)), tag, PA_ERR_INVALID);
+ CHECK_VALIDITY(c->pstream, !name || pa_namereg_is_valid_name(name), tag, PA_ERR_INVALID);
+ CHECK_VALIDITY(c->pstream, idx != PA_INVALID_INDEX || name, tag, PA_ERR_INVALID);
+ CHECK_VALIDITY(c->pstream, idx == PA_INVALID_INDEX || !name, tag, PA_ERR_INVALID);
+ CHECK_VALIDITY(c->pstream, !name || idx == PA_INVALID_INDEX, tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, pa_cvolume_valid(&volume), tag, PA_ERR_INVALID);
switch (command) {
@@ -2352,21 +3074,21 @@ static void command_set_volume(
}
static void command_set_mute(
- PA_GCC_UNUSED pa_pdispatch *pd,
+ pa_pdispatch *pd,
uint32_t command,
uint32_t tag,
pa_tagstruct *t,
void *userdata) {
- connection *c = CONNECTION(userdata);
+ pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
uint32_t idx;
- int mute;
+ pa_bool_t mute;
pa_sink *sink = NULL;
pa_source *source = NULL;
pa_sink_input *si = NULL;
const char *name = NULL;
- connection_assert_ref(c);
+ pa_native_connection_assert_ref(c);
pa_assert(t);
if (pa_tagstruct_getu32(t, &idx) < 0 ||
@@ -2379,7 +3101,10 @@ static void command_set_mute(
}
CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
- CHECK_VALIDITY(c->pstream, idx != PA_INVALID_INDEX || !name || (*name && pa_utf8_valid(name)), tag, PA_ERR_INVALID);
+ CHECK_VALIDITY(c->pstream, !name || pa_namereg_is_valid_name(name), tag, PA_ERR_INVALID);
+ CHECK_VALIDITY(c->pstream, idx != PA_INVALID_INDEX || name, tag, PA_ERR_INVALID);
+ CHECK_VALIDITY(c->pstream, idx == PA_INVALID_INDEX || !name, tag, PA_ERR_INVALID);
+ CHECK_VALIDITY(c->pstream, !name || idx == PA_INVALID_INDEX, tag, PA_ERR_INVALID);
switch (command) {
@@ -2420,13 +3145,13 @@ static void command_set_mute(
pa_pstream_send_simple_ack(c->pstream, tag);
}
-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);
+static void command_cork_playback_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
uint32_t idx;
- int b;
+ pa_bool_t b;
playback_stream *s;
- connection_assert_ref(c);
+ pa_native_connection_assert_ref(c);
pa_assert(t);
if (pa_tagstruct_getu32(t, &idx) < 0 ||
@@ -2443,15 +3168,19 @@ static void command_cork_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_
CHECK_VALIDITY(c->pstream, playback_stream_isinstance(s), tag, PA_ERR_NOENTITY);
pa_sink_input_cork(s->sink_input, b);
+
+ if (b)
+ s->is_underrun = TRUE;
+
pa_pstream_send_simple_ack(c->pstream, tag);
}
-static void command_trigger_or_flush_or_prebuf_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);
+static void command_trigger_or_flush_or_prebuf_playback_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
uint32_t idx;
playback_stream *s;
- connection_assert_ref(c);
+ pa_native_connection_assert_ref(c);
pa_assert(t);
if (pa_tagstruct_getu32(t, &idx) < 0 ||
@@ -2486,13 +3215,13 @@ static void command_trigger_or_flush_or_prebuf_playback_stream(PA_GCC_UNUSED pa_
pa_pstream_send_simple_ack(c->pstream, tag);
}
-static void command_cork_record_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);
+static void command_cork_record_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
uint32_t idx;
record_stream *s;
- int b;
+ pa_bool_t b;
- connection_assert_ref(c);
+ pa_native_connection_assert_ref(c);
pa_assert(t);
if (pa_tagstruct_getu32(t, &idx) < 0 ||
@@ -2511,12 +3240,12 @@ static void command_cork_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UN
pa_pstream_send_simple_ack(c->pstream, tag);
}
-static void command_flush_record_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);
+static void command_flush_record_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
uint32_t idx;
record_stream *s;
- connection_assert_ref(c);
+ pa_native_connection_assert_ref(c);
pa_assert(t);
if (pa_tagstruct_getu32(t, &idx) < 0 ||
@@ -2529,17 +3258,17 @@ static void command_flush_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_U
s = pa_idxset_get_by_index(c->record_streams, idx);
CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
- pa_memblockq_flush(s->memblockq);
+ pa_memblockq_flush_read(s->memblockq);
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);
+ pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
uint32_t idx;
uint32_t maxlength, tlength, prebuf, minreq, fragsize;
pa_tagstruct *reply;
- connection_assert_ref(c);
+ pa_native_connection_assert_ref(c);
pa_assert(t);
if (pa_tagstruct_getu32(t, &idx) < 0) {
@@ -2551,6 +3280,7 @@ static void command_set_stream_buffer_attr(pa_pdispatch *pd, uint32_t command, u
if (command == PA_COMMAND_SET_PLAYBACK_STREAM_BUFFER_ATTR) {
playback_stream *s;
+ pa_bool_t adjust_latency = FALSE, early_requests = FALSE;
s = pa_idxset_get_by_index(c->output_streams, idx);
CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
@@ -2563,28 +3293,32 @@ static void command_set_stream_buffer_attr(pa_pdispatch *pd, uint32_t command, u
PA_TAG_U32, &prebuf,
PA_TAG_U32, &minreq,
PA_TAG_INVALID) < 0 ||
+ (c->version >= 13 && pa_tagstruct_get_boolean(t, &adjust_latency) < 0) ||
+ (c->version >= 14 && pa_tagstruct_get_boolean(t, &early_requests) < 0) ||
!pa_tagstruct_eof(t)) {
protocol_error(c);
return;
}
- CHECK_VALIDITY(c->pstream, maxlength > 0, tag, PA_ERR_INVALID);
- CHECK_VALIDITY(c->pstream, maxlength <= MAX_MEMBLOCKQ_LENGTH, tag, PA_ERR_INVALID);
-
+ fix_playback_buffer_attr_pre(s, adjust_latency, early_requests, &maxlength, &tlength, &prebuf, &minreq);
pa_memblockq_set_maxlength(s->memblockq, maxlength);
pa_memblockq_set_tlength(s->memblockq, tlength);
pa_memblockq_set_prebuf(s->memblockq, prebuf);
pa_memblockq_set_minreq(s->memblockq, minreq);
+ fix_playback_buffer_attr_post(s, &maxlength, &tlength, &prebuf, &minreq);
reply = reply_new(tag);
- pa_tagstruct_putu32(reply, (uint32_t) pa_memblockq_get_maxlength(s->memblockq));
- pa_tagstruct_putu32(reply, (uint32_t) pa_memblockq_get_tlength(s->memblockq));
- pa_tagstruct_putu32(reply, (uint32_t) pa_memblockq_get_prebuf(s->memblockq));
- pa_tagstruct_putu32(reply, (uint32_t) pa_memblockq_get_minreq(s->memblockq));
+ 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;
- size_t base;
+ pa_bool_t adjust_latency = FALSE, early_requests = FALSE;
pa_assert(command == PA_COMMAND_SET_RECORD_STREAM_BUFFER_ATTR);
s = pa_idxset_get_by_index(c->record_streams, idx);
@@ -2595,38 +3329,34 @@ static void command_set_stream_buffer_attr(pa_pdispatch *pd, uint32_t command, u
PA_TAG_U32, &maxlength,
PA_TAG_U32, &fragsize,
PA_TAG_INVALID) < 0 ||
+ (c->version >= 13 && pa_tagstruct_get_boolean(t, &adjust_latency) < 0) ||
+ (c->version >= 14 && pa_tagstruct_get_boolean(t, &early_requests) < 0) ||
!pa_tagstruct_eof(t)) {
protocol_error(c);
return;
}
- CHECK_VALIDITY(c->pstream, maxlength > 0, tag, PA_ERR_INVALID);
- CHECK_VALIDITY(c->pstream, maxlength <= MAX_MEMBLOCKQ_LENGTH, tag, PA_ERR_INVALID);
-
+ fix_record_buffer_attr_pre(s, adjust_latency, early_requests, &maxlength, &fragsize);
pa_memblockq_set_maxlength(s->memblockq, maxlength);
-
- 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 > pa_memblockq_get_maxlength(s->memblockq))
- s->fragment_size = pa_memblockq_get_maxlength(s->memblockq);
+ fix_record_buffer_attr_post(s, &maxlength, &fragsize);
reply = reply_new(tag);
- pa_tagstruct_putu32(reply, (uint32_t) pa_memblockq_get_maxlength(s->memblockq));
- pa_tagstruct_putu32(reply, s->fragment_size);
+ 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);
+ pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
uint32_t idx;
uint32_t rate;
- connection_assert_ref(c);
+ pa_native_connection_assert_ref(c);
pa_assert(t);
if (pa_tagstruct_getu32(t, &idx) < 0 ||
@@ -2661,11 +3391,173 @@ static void command_update_stream_sample_rate(pa_pdispatch *pd, uint32_t command
pa_pstream_send_simple_ack(c->pstream, tag);
}
-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);
+static void command_update_proplist(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
+ uint32_t idx;
+ uint32_t mode;
+ pa_proplist *p;
+
+ pa_native_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) {
+ pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
+ uint32_t idx;
+ unsigned changed = 0;
+ pa_proplist *p;
+ pa_strlist *l = NULL;
+
+ pa_native_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 += (unsigned) (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_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
const char *s;
- connection_assert_ref(c);
+ pa_native_connection_assert_ref(c);
pa_assert(t);
if (pa_tagstruct_gets(t, &s) < 0 ||
@@ -2675,18 +3567,18 @@ static void command_set_default_sink_or_source(PA_GCC_UNUSED pa_pdispatch *pd, u
}
CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
- CHECK_VALIDITY(c->pstream, !s || (*s && pa_utf8_valid(s)), tag, PA_ERR_INVALID);
+ CHECK_VALIDITY(c->pstream, !s || pa_namereg_is_valid_name(s), tag, PA_ERR_INVALID);
pa_namereg_set_default(c->protocol->core, s, command == PA_COMMAND_SET_DEFAULT_SOURCE ? PA_NAMEREG_SOURCE : PA_NAMEREG_SINK);
pa_pstream_send_simple_ack(c->pstream, tag);
}
-static void command_set_stream_name(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
- connection *c = CONNECTION(userdata);
+static void command_set_stream_name(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
uint32_t idx;
const char *name;
- connection_assert_ref(c);
+ pa_native_connection_assert_ref(c);
pa_assert(t);
if (pa_tagstruct_getu32(t, &idx) < 0 ||
@@ -2721,11 +3613,11 @@ static void command_set_stream_name(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t com
pa_pstream_send_simple_ack(c->pstream, tag);
}
-static void command_kill(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
- connection *c = CONNECTION(userdata);
+static void command_kill(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
uint32_t idx;
- connection_assert_ref(c);
+ pa_native_connection_assert_ref(c);
pa_assert(t);
if (pa_tagstruct_getu32(t, &idx) < 0 ||
@@ -2742,7 +3634,7 @@ static void command_kill(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uint3
client = pa_idxset_get_by_index(c->protocol->core->clients, idx);
CHECK_VALIDITY(c->pstream, client, tag, PA_ERR_NOENTITY);
- connection_ref(c);
+ pa_native_connection_ref(c);
pa_client_kill(client);
} else if (command == PA_COMMAND_KILL_SINK_INPUT) {
@@ -2751,7 +3643,7 @@ static void command_kill(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uint3
s = pa_idxset_get_by_index(c->protocol->core->sink_inputs, idx);
CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
- connection_ref(c);
+ pa_native_connection_ref(c);
pa_sink_input_kill(s);
} else {
pa_source_output *s;
@@ -2761,21 +3653,21 @@ static void command_kill(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uint3
s = pa_idxset_get_by_index(c->protocol->core->source_outputs, idx);
CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
- connection_ref(c);
+ pa_native_connection_ref(c);
pa_source_output_kill(s);
}
pa_pstream_send_simple_ack(c->pstream, tag);
- connection_unref(c);
+ pa_native_connection_unref(c);
}
-static void command_load_module(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
- connection *c = CONNECTION(userdata);
+static void command_load_module(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
pa_module *m;
const char *name, *argument;
pa_tagstruct *reply;
- connection_assert_ref(c);
+ pa_native_connection_assert_ref(c);
pa_assert(t);
if (pa_tagstruct_gets(t, &name) < 0 ||
@@ -2799,12 +3691,12 @@ static void command_load_module(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED ui
pa_pstream_send_tagstruct(c->pstream, reply);
}
-static void command_unload_module(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
- connection *c = CONNECTION(userdata);
+static void command_unload_module(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
uint32_t idx;
pa_module *m;
- connection_assert_ref(c);
+ pa_native_connection_assert_ref(c);
pa_assert(t);
if (pa_tagstruct_getu32(t, &idx) < 0 ||
@@ -2817,18 +3709,18 @@ static void command_unload_module(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED
m = pa_idxset_get_by_index(c->protocol->core->modules, idx);
CHECK_VALIDITY(c->pstream, m, tag, PA_ERR_NOENTITY);
- pa_module_unload_request(m);
+ pa_module_unload_request(m, FALSE);
pa_pstream_send_simple_ack(c->pstream, tag);
}
-static void command_add_autoload(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
- connection *c = CONNECTION(userdata);
+static void command_add_autoload(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
const char *name, *module, *argument;
uint32_t type;
uint32_t idx;
pa_tagstruct *reply;
- connection_assert_ref(c);
+ pa_native_connection_assert_ref(c);
pa_assert(t);
if (pa_tagstruct_gets(t, &name) < 0 ||
@@ -2856,13 +3748,13 @@ static void command_add_autoload(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED u
pa_pstream_send_tagstruct(c->pstream, reply);
}
-static void command_remove_autoload(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
- connection *c = CONNECTION(userdata);
+static void command_remove_autoload(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
const char *name = NULL;
uint32_t type, idx = PA_IDXSET_INVALID;
int r;
- connection_assert_ref(c);
+ pa_native_connection_assert_ref(c);
pa_assert(t);
if ((pa_tagstruct_getu32(t, &idx) < 0 &&
@@ -2892,19 +3784,19 @@ static void autoload_fill_tagstruct(pa_tagstruct *t, const pa_autoload_entry *e)
pa_tagstruct_putu32(t, e->index);
pa_tagstruct_puts(t, e->name);
- pa_tagstruct_putu32(t, e->type == PA_NAMEREG_SINK ? 0 : 1);
+ pa_tagstruct_putu32(t, e->type == PA_NAMEREG_SINK ? 0U : 1U);
pa_tagstruct_puts(t, e->module);
pa_tagstruct_puts(t, e->argument);
}
-static void command_get_autoload_info(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
- connection *c = CONNECTION(userdata);
+static void command_get_autoload_info(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
const pa_autoload_entry *a = NULL;
uint32_t type, idx;
const char *name;
pa_tagstruct *reply;
- connection_assert_ref(c);
+ pa_native_connection_assert_ref(c);
pa_assert(t);
if ((pa_tagstruct_getu32(t, &idx) < 0 &&
@@ -2931,11 +3823,11 @@ static void command_get_autoload_info(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNU
pa_pstream_send_tagstruct(c->pstream, reply);
}
-static void command_get_autoload_info_list(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
- connection *c = CONNECTION(userdata);
+static void command_get_autoload_info_list(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
pa_tagstruct *reply;
- connection_assert_ref(c);
+ pa_native_connection_assert_ref(c);
pa_assert(t);
if (!pa_tagstruct_eof(t)) {
@@ -2959,16 +3851,16 @@ static void command_get_autoload_info_list(PA_GCC_UNUSED pa_pdispatch *pd, PA_GC
}
static void command_move_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
- connection *c = CONNECTION(userdata);
+ pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
uint32_t idx = PA_INVALID_INDEX, idx_device = PA_INVALID_INDEX;
- const char *name = NULL;
+ const char *name_device = NULL;
- connection_assert_ref(c);
+ pa_native_connection_assert_ref(c);
pa_assert(t);
if (pa_tagstruct_getu32(t, &idx) < 0 ||
pa_tagstruct_getu32(t, &idx_device) < 0 ||
- pa_tagstruct_gets(t, &name) < 0 ||
+ pa_tagstruct_gets(t, &name_device) < 0 ||
!pa_tagstruct_eof(t)) {
protocol_error(c);
return;
@@ -2976,7 +3868,11 @@ static void command_move_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag
CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
CHECK_VALIDITY(c->pstream, idx != PA_INVALID_INDEX, tag, PA_ERR_INVALID);
- CHECK_VALIDITY(c->pstream, idx_device != PA_INVALID_INDEX || !name || (*name && pa_utf8_valid(name)), tag, PA_ERR_INVALID);
+
+ CHECK_VALIDITY(c->pstream, !name_device || pa_namereg_is_valid_name(name_device), tag, PA_ERR_INVALID);
+ CHECK_VALIDITY(c->pstream, idx_device != PA_INVALID_INDEX || name_device, tag, PA_ERR_INVALID);
+ CHECK_VALIDITY(c->pstream, idx_device == PA_INVALID_INDEX || !name_device, tag, PA_ERR_INVALID);
+ CHECK_VALIDITY(c->pstream, !name_device || idx_device == PA_INVALID_INDEX, tag, PA_ERR_INVALID);
if (command == PA_COMMAND_MOVE_SINK_INPUT) {
pa_sink_input *si = NULL;
@@ -2987,11 +3883,11 @@ static void command_move_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag
if (idx_device != PA_INVALID_INDEX)
sink = pa_idxset_get_by_index(c->protocol->core->sinks, idx_device);
else
- sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK, 1);
+ sink = pa_namereg_get(c->protocol->core, name_device, PA_NAMEREG_SINK, 1);
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;
}
@@ -3006,7 +3902,7 @@ static void command_move_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag
if (idx_device != PA_INVALID_INDEX)
source = pa_idxset_get_by_index(c->protocol->core->sources, idx_device);
else
- source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE, 1);
+ source = pa_namereg_get(c->protocol->core, name_device, PA_NAMEREG_SOURCE, 1);
CHECK_VALIDITY(c->pstream, so && source, tag, PA_ERR_NOENTITY);
@@ -3020,12 +3916,12 @@ static void command_move_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag
}
static void command_suspend(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
- connection *c = CONNECTION(userdata);
+ pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
uint32_t idx = PA_INVALID_INDEX;
const char *name = NULL;
- int b;
+ pa_bool_t b;
- connection_assert_ref(c);
+ pa_native_connection_assert_ref(c);
pa_assert(t);
if (pa_tagstruct_getu32(t, &idx) < 0 ||
@@ -3037,7 +3933,10 @@ static void command_suspend(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa
}
CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
- CHECK_VALIDITY(c->pstream, idx != PA_INVALID_INDEX || !name || !*name || pa_utf8_valid(name), tag, PA_ERR_INVALID);
+ CHECK_VALIDITY(c->pstream, !name || pa_namereg_is_valid_name(name), tag, PA_ERR_INVALID);
+ CHECK_VALIDITY(c->pstream, idx != PA_INVALID_INDEX || name, tag, PA_ERR_INVALID);
+ CHECK_VALIDITY(c->pstream, idx == PA_INVALID_INDEX || !name, tag, PA_ERR_INVALID);
+ CHECK_VALIDITY(c->pstream, !name || idx == PA_INVALID_INDEX, tag, PA_ERR_INVALID);
if (command == PA_COMMAND_SUSPEND_SINK) {
@@ -3093,28 +3992,69 @@ static void command_suspend(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa
pa_pstream_send_simple_ack(c->pstream, tag);
}
+static void command_extension(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
+ uint32_t idx = PA_INVALID_INDEX;
+ const char *name = NULL;
+ pa_module *m;
+ pa_native_protocol_ext_cb_t cb;
+
+ pa_native_connection_assert_ref(c);
+ pa_assert(t);
+
+ if (pa_tagstruct_getu32(t, &idx) < 0 ||
+ pa_tagstruct_gets(t, &name) < 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, idx != PA_INVALID_INDEX || name, tag, PA_ERR_INVALID);
+ CHECK_VALIDITY(c->pstream, idx == PA_INVALID_INDEX || !name, tag, PA_ERR_INVALID);
+ CHECK_VALIDITY(c->pstream, !name || idx == PA_INVALID_INDEX, tag, PA_ERR_INVALID);
+
+ if (idx != PA_INVALID_INDEX)
+ m = pa_idxset_get_by_index(c->protocol->core->modules, idx);
+ else {
+ for (m = pa_idxset_first(c->protocol->core->modules, &idx); m; m = pa_idxset_next(c->protocol->core->modules, &idx))
+ if (strcmp(name, m->name) == 0)
+ break;
+ }
+
+ CHECK_VALIDITY(c->pstream, m, tag, PA_ERR_NOEXTENSION);
+ CHECK_VALIDITY(c->pstream, m->load_once || idx != PA_INVALID_INDEX, tag, PA_ERR_INVALID);
+
+ cb = (pa_native_protocol_ext_cb_t) pa_hashmap_get(c->protocol->extensions, m);
+ CHECK_VALIDITY(c->pstream, m, tag, PA_ERR_NOEXTENSION);
+
+ if (cb(c->protocol, m, c, tag, t) < 0)
+ protocol_error(c);
+}
+
+
/*** pstream callbacks ***/
static void pstream_packet_callback(pa_pstream *p, pa_packet *packet, const pa_creds *creds, void *userdata) {
- connection *c = CONNECTION(userdata);
+ pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
pa_assert(p);
pa_assert(packet);
- connection_assert_ref(c);
+ pa_native_connection_assert_ref(c);
if (pa_pdispatch_run(c->pdispatch, packet, creds, c) < 0) {
pa_log("invalid packet.");
- connection_unlink(c);
+ native_connection_unlink(c);
}
}
static void pstream_memblock_callback(pa_pstream *p, uint32_t channel, int64_t offset, pa_seek_mode_t seek, const pa_memchunk *chunk, void *userdata) {
- connection *c = CONNECTION(userdata);
+ pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
output_stream *stream;
pa_assert(p);
pa_assert(chunk);
- connection_assert_ref(c);
+ pa_native_connection_assert_ref(c);
if (!(stream = OUTPUT_STREAM(pa_idxset_get_by_index(c->output_streams, channel)))) {
pa_log("client sent block for invalid stream.");
@@ -3122,6 +4062,8 @@ static void pstream_memblock_callback(pa_pstream *p, uint32_t channel, int64_t o
return;
}
+/* pa_log("got %lu bytes", (unsigned long) chunk->length); */
+
if (playback_stream_isinstance(stream)) {
playback_stream *ps = PLAYBACK_STREAM(stream);
@@ -3170,22 +4112,22 @@ static void pstream_memblock_callback(pa_pstream *p, uint32_t channel, int64_t o
}
static void pstream_die_callback(pa_pstream *p, void *userdata) {
- connection *c = CONNECTION(userdata);
+ pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
pa_assert(p);
- connection_assert_ref(c);
+ pa_native_connection_assert_ref(c);
- connection_unlink(c);
- pa_log_info("connection died.");
+ native_connection_unlink(c);
+ pa_log_info("Connection died.");
}
static void pstream_drain_callback(pa_pstream *p, void *userdata) {
- connection *c = CONNECTION(userdata);
+ pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
pa_assert(p);
- connection_assert_ref(c);
+ pa_native_connection_assert_ref(c);
- send_memblock(c);
+ native_connection_send_memblock(c);
}
static void pstream_revoke_callback(pa_pstream *p, uint32_t block_id, void *userdata) {
@@ -3211,31 +4153,33 @@ static void pstream_release_callback(pa_pstream *p, uint32_t block_id, void *use
static void client_kill_cb(pa_client *c) {
pa_assert(c);
- connection_unlink(CONNECTION(c->userdata));
+ native_connection_unlink(PA_NATIVE_CONNECTION(c->userdata));
+ pa_log_info("Connection killed.");
}
-/*** socket server callbacks ***/
+/*** module entry points ***/
static void auth_timeout(pa_mainloop_api*m, pa_time_event *e, const struct timeval *tv, void *userdata) {
- connection *c = CONNECTION(userdata);
+ pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
pa_assert(m);
pa_assert(tv);
- connection_assert_ref(c);
+ pa_native_connection_assert_ref(c);
pa_assert(c->auth_timeout_event == e);
- if (!c->authorized)
- connection_unlink(c);
+ if (!c->authorized) {
+ native_connection_unlink(c);
+ pa_log_info("Connection terminated due to authentication timeout.");
+ }
}
-static void on_connection(PA_GCC_UNUSED pa_socket_server*s, pa_iochannel *io, void *userdata) {
- pa_protocol_native *p = userdata;
- connection *c;
+void pa_native_protocol_connect(pa_native_protocol *p, pa_iochannel *io, pa_native_options *o) {
+ pa_native_connection *c;
char cname[256], pname[128];
- pa_assert(s);
- pa_assert(io);
pa_assert(p);
+ pa_assert(io);
+ pa_assert(o);
if (pa_idxset_size(p->connections)+1 > MAX_CONNECTIONS) {
pa_log_warn("Warning! Too many connections (%u), dropping incoming connection.", MAX_CONNECTIONS);
@@ -3243,15 +4187,24 @@ static void on_connection(PA_GCC_UNUSED pa_socket_server*s, pa_iochannel *io, vo
return;
}
- c = pa_msgobject_new(connection);
- c->parent.parent.free = connection_free;
- c->parent.process_msg = connection_process_msg;
+ c = pa_msgobject_new(pa_native_connection);
+ c->parent.parent.free = native_connection_free;
+ c->parent.process_msg = native_connection_process_msg;
+ c->protocol = p;
+ c->options = pa_native_options_ref(o);
+ c->authorized = FALSE;
- c->authorized = !!p->public;
+ if (o->auth_anonymous) {
+ pa_log_info("Client authenticated anonymously.");
+ c->authorized = TRUE;
+ }
+
+ if (!c->authorized &&
+ o->auth_ip_acl &&
+ pa_ip_acl_check(o->auth_ip_acl, pa_iochannel_get_recv_fd(io)) > 0) {
- 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) {
@@ -3262,17 +4215,18 @@ 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 = o->module;
c->pstream = pa_pstream_new(p->core->mainloop, io, p->core->mempool);
-
pa_pstream_set_recieve_packet_callback(c->pstream, pstream_packet_callback, c);
pa_pstream_set_recieve_memblock_callback(c->pstream, pstream_memblock_callback, c);
pa_pstream_set_die_callback(c->pstream, pstream_die_callback, c);
@@ -3293,167 +4247,250 @@ 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
+
+ pa_hook_fire(&p->hooks[PA_NATIVE_HOOK_CONNECTION_PUT], c);
}
-/*** module entry points ***/
+void pa_native_protocol_disconnect(pa_native_protocol *p, pa_module *m) {
+ pa_native_connection *c;
+ void *state = NULL;
-static int load_key(pa_protocol_native*p, const char*fn) {
pa_assert(p);
+ pa_assert(m);
- p->auth_cookie_in_property = 0;
+ while ((c = pa_idxset_iterate(p->connections, &state, NULL)))
+ if (c->options->module == m)
+ native_connection_unlink(c);
+}
- 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;
- return 0;
- }
+static pa_native_protocol* native_protocol_new(pa_core *c) {
+ pa_native_protocol *p;
+ pa_native_hook_t h;
- if (!fn)
- fn = PA_NATIVE_COOKIE_FILE;
+ pa_assert(c);
- if (pa_authkey_load_auto(fn, p->auth_cookie, sizeof(p->auth_cookie)) < 0)
- return -1;
+ p = pa_xnew(pa_native_protocol, 1);
+ PA_REFCNT_INIT(p);
+ p->core = c;
+ p->connections = pa_idxset_new(NULL, NULL);
- pa_log_info("loading cookie from disk.");
+ p->servers = NULL;
- 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->extensions = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
- return 0;
+ for (h = 0; h < PA_NATIVE_HOOK_MAX; h++)
+ pa_hook_init(&p->hooks[h], p);
+
+ pa_assert_se(pa_shared_set(c, "native-protocol", p) >= 0);
+
+ return p;
}
-static pa_protocol_native* protocol_new_internal(pa_core *c, pa_module *m, pa_modargs *ma) {
- pa_protocol_native *p;
- pa_bool_t public = FALSE;
- const char *acl;
+pa_native_protocol* pa_native_protocol_get(pa_core *c) {
+ pa_native_protocol *p;
- pa_assert(c);
- pa_assert(ma);
+ if ((p = pa_shared_get(c, "native-protocol")))
+ return pa_native_protocol_ref(p);
- if (pa_modargs_get_value_boolean(ma, "auth-anonymous", &public) < 0) {
- pa_log("auth-anonymous= expects a boolean argument.");
- return NULL;
- }
+ return native_protocol_new(c);
+}
- p = pa_xnew(pa_protocol_native, 1);
- p->core = c;
- p->module = m;
- p->public = public;
- p->server = NULL;
- p->auth_ip_acl = NULL;
+pa_native_protocol* pa_native_protocol_ref(pa_native_protocol *p) {
+ pa_assert(p);
+ pa_assert(PA_REFCNT_VALUE(p) >= 1);
-#ifdef HAVE_CREDS
- {
- pa_bool_t a = 1;
- 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;
+ PA_REFCNT_INC(p);
- if (p->auth_group)
- pa_log_info("Allowing access to group '%s'.", p->auth_group);
- }
-#endif
+ return p;
+}
+void pa_native_protocol_unref(pa_native_protocol *p) {
+ pa_native_connection *c;
+ pa_native_hook_t h;
- if ((acl = pa_modargs_get_value(ma, "auth-ip-acl", NULL))) {
+ pa_assert(p);
+ pa_assert(PA_REFCNT_VALUE(p) >= 1);
- if (!(p->auth_ip_acl = pa_ip_acl_new(acl))) {
- pa_log("Failed to parse IP ACL '%s'", acl);
- goto fail;
- }
- }
+ if (PA_REFCNT_DEC(p) > 0)
+ return;
- if (load_key(p, pa_modargs_get_value(ma, "cookie", NULL)) < 0)
- goto fail;
+ while ((c = pa_idxset_first(p->connections, NULL)))
+ native_connection_unlink(c);
- p->connections = pa_idxset_new(NULL, NULL);
+ pa_idxset_free(p->connections, NULL, NULL);
- return p;
+ pa_strlist_free(p->servers);
+
+ for (h = 0; h < PA_NATIVE_HOOK_MAX; h++)
+ pa_hook_done(&p->hooks[h]);
+
+ pa_hashmap_free(p->extensions, NULL, NULL);
+
+ pa_assert_se(pa_shared_remove(p->core, "native-protocol") >= 0);
-fail:
-#ifdef HAVE_CREDS
- pa_xfree(p->auth_group);
-#endif
- if (p->auth_ip_acl)
- pa_ip_acl_free(p->auth_ip_acl);
pa_xfree(p);
- return NULL;
}
-pa_protocol_native* pa_protocol_native_new(pa_core *core, pa_socket_server *server, pa_module *m, pa_modargs *ma) {
- char t[256];
- pa_protocol_native *p;
+void pa_native_protocol_add_server_string(pa_native_protocol *p, const char *name) {
+ pa_assert(p);
+ pa_assert(PA_REFCNT_VALUE(p) >= 1);
+ pa_assert(name);
- if (!(p = protocol_new_internal(core, m, ma)))
- return NULL;
+ p->servers = pa_strlist_prepend(p->servers, name);
- p->server = server;
- pa_socket_server_set_callback(p->server, on_connection, p);
+ pa_hook_fire(&p->hooks[PA_NATIVE_HOOK_SERVERS_CHANGED], p->servers);
+}
- if (pa_socket_server_get_address(p->server, t, sizeof(t))) {
- pa_strlist *l;
- l = pa_property_get(core, PA_NATIVE_SERVER_PROPERTY_NAME);
- l = pa_strlist_prepend(l, t);
- pa_property_replace(core, PA_NATIVE_SERVER_PROPERTY_NAME, l);
- }
+void pa_native_protocol_remove_server_string(pa_native_protocol *p, const char *name) {
+ pa_assert(p);
+ pa_assert(PA_REFCNT_VALUE(p) >= 1);
+ pa_assert(name);
- return p;
+ p->servers = pa_strlist_remove(p->servers, name);
+
+ pa_hook_fire(&p->hooks[PA_NATIVE_HOOK_SERVERS_CHANGED], p->servers);
}
-void pa_protocol_native_free(pa_protocol_native *p) {
- connection *c;
+pa_hook *pa_native_protocol_hooks(pa_native_protocol *p) {
pa_assert(p);
+ pa_assert(PA_REFCNT_VALUE(p) >= 1);
- while ((c = pa_idxset_first(p->connections, NULL)))
- connection_unlink(c);
- pa_idxset_free(p->connections, NULL, NULL);
+ return p->hooks;
+}
- if (p->server) {
- char t[256];
+pa_strlist *pa_native_protocol_servers(pa_native_protocol *p) {
+ pa_assert(p);
+ pa_assert(PA_REFCNT_VALUE(p) >= 1);
- if (pa_socket_server_get_address(p->server, t, sizeof(t))) {
- pa_strlist *l;
- l = pa_property_get(p->core, PA_NATIVE_SERVER_PROPERTY_NAME);
- l = pa_strlist_remove(l, t);
+ return p->servers;
+}
- if (l)
- pa_property_replace(p->core, PA_NATIVE_SERVER_PROPERTY_NAME, l);
- else
- pa_property_remove(p->core, PA_NATIVE_SERVER_PROPERTY_NAME);
- }
+int pa_native_protocol_install_ext(pa_native_protocol *p, pa_module *m, pa_native_protocol_ext_cb_t cb) {
+ pa_assert(p);
+ pa_assert(PA_REFCNT_VALUE(p) >= 1);
+ pa_assert(m);
+ pa_assert(cb);
+ pa_assert(!pa_hashmap_get(p->extensions, m));
+
+ pa_assert_se(pa_hashmap_put(p->extensions, m, (void*) cb) == 0);
+ return 0;
+}
+
+void pa_native_protocol_remove_ext(pa_native_protocol *p, pa_module *m) {
+ pa_assert(p);
+ pa_assert(PA_REFCNT_VALUE(p) >= 1);
+ pa_assert(m);
- pa_socket_server_unref(p->server);
+ pa_assert_se(pa_hashmap_remove(p->extensions, m));
+}
+
+pa_native_options* pa_native_options_new(void) {
+ pa_native_options *o;
+
+ o = pa_xnew0(pa_native_options, 1);
+ PA_REFCNT_INIT(o);
+
+ return o;
+}
+
+pa_native_options* pa_native_options_ref(pa_native_options *o) {
+ pa_assert(o);
+ pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+ PA_REFCNT_INC(o);
+
+ return o;
+}
+
+void pa_native_options_unref(pa_native_options *o) {
+ pa_assert(o);
+ pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+ if (PA_REFCNT_DEC(o) > 0)
+ return;
+
+ pa_xfree(o->auth_group);
+
+ if (o->auth_ip_acl)
+ pa_ip_acl_free(o->auth_ip_acl);
+
+ if (o->auth_cookie)
+ pa_auth_cookie_unref(o->auth_cookie);
+
+ pa_xfree(o);
+}
+
+int pa_native_options_parse(pa_native_options *o, pa_core *c, pa_modargs *ma) {
+ pa_bool_t enabled;
+ const char *acl;
+
+ pa_assert(o);
+ pa_assert(PA_REFCNT_VALUE(o) >= 1);
+ pa_assert(ma);
+
+ if (pa_modargs_get_value_boolean(ma, "auth-anonymous", &o->auth_anonymous) < 0) {
+ pa_log("auth-anonymous= expects a boolean argument.");
+ return -1;
}
- if (p->auth_cookie_in_property)
- pa_authkey_prop_unref(p->core, PA_NATIVE_COOKIE_PROPERTY_NAME);
+ enabled = TRUE;
+ if (pa_modargs_get_value_boolean(ma, "auth-group-enabled", &enabled) < 0) {
+ pa_log("auth-group-enabled= expects a boolean argument.");
+ return -1;
+ }
- if (p->auth_ip_acl)
- pa_ip_acl_free(p->auth_ip_acl);
+ pa_xfree(o->auth_group);
+ o->auth_group = enabled ? pa_xstrdup(pa_modargs_get_value(ma, "auth-group", pa_in_system_mode() ? PA_ACCESS_GROUP : NULL)) : NULL;
-#ifdef HAVE_CREDS
- pa_xfree(p->auth_group);
+#ifndef HAVE_CREDS
+ if (o->auth_group)
+ pa_log_warn("Authentication group configured, but not available on local system. Ignoring.");
#endif
- pa_xfree(p);
-}
-pa_protocol_native* pa_protocol_native_new_iochannel(
- pa_core*core,
- pa_iochannel *io,
- pa_module *m,
- pa_modargs *ma) {
+ if ((acl = pa_modargs_get_value(ma, "auth-ip-acl", NULL))) {
+ pa_ip_acl *ipa;
- pa_protocol_native *p;
+ if (!(ipa = pa_ip_acl_new(acl))) {
+ pa_log("Failed to parse IP ACL '%s'", acl);
+ return -1;
+ }
- if (!(p = protocol_new_internal(core, m, ma)))
- return NULL;
+ if (o->auth_ip_acl)
+ pa_ip_acl_free(o->auth_ip_acl);
- on_connection(NULL, io, p);
+ o->auth_ip_acl = ipa;
+ }
- return p;
+ enabled = TRUE;
+ if (pa_modargs_get_value_boolean(ma, "auth-cookie-enabled", &enabled) < 0) {
+ pa_log("auth-cookie-enabled= expects a boolean argument.");
+ return -1;
+ }
+
+ if (o->auth_cookie)
+ pa_auth_cookie_unref(o->auth_cookie);
+
+ if (enabled) {
+ const char *cn;
+
+ /* The new name for this is 'auth-cookie', for compat reasons
+ * we check the old name too */
+ if (!(cn = pa_modargs_get_value(ma, "auth-cookie", NULL)))
+ if (!(cn = pa_modargs_get_value(ma, "cookie", NULL)))
+ cn = PA_NATIVE_COOKIE_FILE;
+
+ if (!(o->auth_cookie = pa_auth_cookie_get(c, cn, PA_NATIVE_COOKIE_LENGTH)))
+ return -1;
+
+ } else
+ o->auth_cookie = NULL;
+
+ return 0;
+}
+
+pa_pstream* pa_native_connection_get_pstream(pa_native_connection *c) {
+ pa_native_connection_assert_ref(c);
+
+ return c->pstream;
}
diff --git a/src/pulsecore/protocol-native.h b/src/pulsecore/protocol-native.h
index 6675343..06731c0 100644
--- a/src/pulsecore/protocol-native.h
+++ b/src/pulsecore/protocol-native.h
@@ -1,8 +1,6 @@
#ifndef fooprotocolnativehfoo
#define fooprotocolnativehfoo
-/* $Id: protocol-native.h 1426 2007-02-13 15:35:19Z ossman $ */
-
/***
This file is part of PulseAudio.
@@ -26,15 +24,66 @@
***/
#include <pulsecore/core.h>
-#include <pulsecore/socket-server.h>
+#include <pulsecore/ipacl.h>
+#include <pulsecore/auth-cookie.h>
+#include <pulsecore/iochannel.h>
#include <pulsecore/module.h>
#include <pulsecore/modargs.h>
+#include <pulsecore/strlist.h>
+#include <pulsecore/hook-list.h>
+#include <pulsecore/pstream.h>
+#include <pulsecore/tagstruct.h>
+
+typedef struct pa_native_protocol pa_native_protocol;
+
+typedef struct pa_native_connection pa_native_connection;
+
+typedef struct pa_native_options {
+ PA_REFCNT_DECLARE;
+
+ pa_module *module;
+
+ pa_bool_t auth_anonymous;
+ char *auth_group;
+ pa_ip_acl *auth_ip_acl;
+ pa_auth_cookie *auth_cookie;
+
+} pa_native_options;
+
+typedef enum pa_native_hook {
+ PA_NATIVE_HOOK_SERVERS_CHANGED,
+ PA_NATIVE_HOOK_CONNECTION_PUT,
+ PA_NATIVE_HOOK_CONNECTION_UNLINK,
+ PA_NATIVE_HOOK_MAX
+} pa_native_hook_t;
+
+pa_native_protocol* pa_native_protocol_get(pa_core *core);
+pa_native_protocol* pa_native_protocol_ref(pa_native_protocol *p);
+void pa_native_protocol_unref(pa_native_protocol *p);
+void pa_native_protocol_connect(pa_native_protocol *p, pa_iochannel *io, pa_native_options *a);
+void pa_native_protocol_disconnect(pa_native_protocol *p, pa_module *m);
+
+pa_hook *pa_native_protocol_hooks(pa_native_protocol *p);
+
+void pa_native_protocol_add_server_string(pa_native_protocol *p, const char *name);
+void pa_native_protocol_remove_server_string(pa_native_protocol *p, const char *name);
+pa_strlist *pa_native_protocol_servers(pa_native_protocol *p);
+
+typedef int (*pa_native_protocol_ext_cb_t)(
+ pa_native_protocol *p,
+ pa_module *m,
+ pa_native_connection *c,
+ uint32_t tag,
+ pa_tagstruct *t);
-typedef struct pa_protocol_native pa_protocol_native;
+int pa_native_protocol_install_ext(pa_native_protocol *p, pa_module *m, pa_native_protocol_ext_cb_t cb);
+void pa_native_protocol_remove_ext(pa_native_protocol *p, pa_module *m);
-pa_protocol_native* pa_protocol_native_new(pa_core*core, pa_socket_server *server, pa_module *m, pa_modargs *ma);
-void pa_protocol_native_free(pa_protocol_native *n);
+pa_pstream* pa_native_connection_get_pstream(pa_native_connection *c);
-pa_protocol_native* pa_protocol_native_new_iochannel(pa_core*core, pa_iochannel *io, pa_module *m, pa_modargs *ma);
+pa_native_options* pa_native_options_new(void);
+pa_native_options* pa_native_options_ref(pa_native_options *o);
+void pa_native_options_unref(pa_native_options *o);
+int pa_native_options_parse(pa_native_options *o, pa_core *c, pa_modargs *ma);
#endif
diff --git a/src/pulsecore/protocol-simple.c b/src/pulsecore/protocol-simple.c
index 47454fd..743bf2e 100644
--- a/src/pulsecore/protocol-simple.c
+++ b/src/pulsecore/protocol-simple.c
@@ -1,5 +1,3 @@
-/* $Id: protocol-simple.c 2050 2007-11-13 17:37:44Z lennart $ */
-
/***
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,8 @@
#include <pulsecore/core-error.h>
#include <pulsecore/atomic.h>
#include <pulsecore/thread-mq.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/shared.h>
#include "protocol-simple.h"
@@ -50,19 +51,21 @@
typedef struct connection {
pa_msgobject parent;
- pa_protocol_simple *protocol;
+ pa_simple_protocol *protocol;
+ pa_simple_options *options;
pa_iochannel *io;
pa_sink_input *sink_input;
pa_source_output *source_output;
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;
@@ -70,20 +73,11 @@ PA_DECLARE_CLASS(connection);
#define CONNECTION(o) (connection_cast(o))
static PA_DEFINE_CHECK_TYPE(connection, pa_msgobject);
-struct pa_protocol_simple {
- pa_module *module;
+struct pa_simple_protocol {
+ PA_REFCNT_DECLARE;
+
pa_core *core;
- pa_socket_server*server;
pa_idxset *connections;
-
- enum {
- RECORD = 1,
- PLAYBACK = 2,
- DUPLEX = 3
- } mode;
-
- pa_sample_spec sample_spec;
- char *source_name, *sink_name;
};
enum {
@@ -97,11 +91,11 @@ enum {
CONNECTION_MESSAGE_UNLINK_CONNECTION /* Please drop a aconnection now */
};
-
#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);
@@ -109,6 +103,11 @@ static void connection_unlink(connection *c) {
if (!c->protocol)
return;
+ if (c->options) {
+ pa_simple_options_unref(c->options);
+ c->options = NULL;
+ }
+
if (c->sink_input) {
pa_sink_input_unlink(c->sink_input);
pa_sink_input_unref(c->sink_input);
@@ -140,8 +139,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 +155,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)
+ if (!c->sink_input || (l = (size_t) 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, (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);
@@ -194,12 +197,12 @@ static int do_read(connection *c) {
chunk.memblock = c->playback.current_memblock;
chunk.index = c->playback.memblock_index;
- chunk.length = r;
+ chunk.length = (size_t) r;
- c->playback.memblock_index += r;
+ c->playback.memblock_index += (size_t) r;
pa_asyncmsgq_post(c->sink_input->sink->asyncmsgq, PA_MSGOBJECT(c->sink_input), SINK_INPUT_MESSAGE_POST_DATA, NULL, 0, &chunk, NULL);
- pa_atomic_sub(&c->playback.missing, r);
+ pa_atomic_sub(&c->playback.missing, (int) r);
return 0;
}
@@ -237,7 +240,7 @@ static int do_write(connection *c) {
return -1;
}
- pa_memblockq_drop(c->output_memblockq, r);
+ pa_memblockq_drop(c->output_memblockq, (size_t) r);
return 0;
}
@@ -248,16 +251,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 +269,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 +321,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 +350,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) {
+
+ c->playback.underrun = TRUE;
-/* pa_log("peeked %u %i", r >= 0 ? chunk->length: 0, r); */
+ 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;
- if (c->dead && r < 0)
- pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(c), CONNECTION_MESSAGE_UNLINK_CONNECTION, NULL, 0, NULL, NULL);
+ chunk->length = PA_MIN(length, chunk->length);
- return r;
+ 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, (int) 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 +423,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 +442,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);
@@ -446,14 +474,13 @@ static void io_callback(pa_iochannel*io, void *userdata) {
/*** socket_server callbacks ***/
-static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata) {
- pa_protocol_simple *p = userdata;
+void pa_simple_protocol_connect(pa_simple_protocol *p, pa_iochannel *io, pa_simple_options *o) {
connection *c = NULL;
- char cname[256];
+ char cname[256], pname[128];
- pa_assert(s);
- pa_assert(io);
pa_assert(p);
+ pa_assert(io);
+ pa_assert(o);
if (pa_idxset_size(p->connections)+1 > MAX_CONNECTIONS) {
pa_log("Warning! Too many connections (%u), dropping incoming connection.", MAX_CONNECTIONS);
@@ -465,73 +492,101 @@ static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata)
c->parent.parent.free = connection_free;
c->parent.process_msg = connection_process_msg;
c->io = io;
+ pa_iochannel_set_callback(c->io, io_callback, c);
+
c->sink_input = NULL;
c->source_output = NULL;
c->input_memblockq = c->output_memblockq = NULL;
c->protocol = p;
+ c->options = pa_simple_options_ref(o);
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 = o->module;
c->client->kill = client_kill_cb;
c->client->userdata = c;
- if (p->mode & PLAYBACK) {
+ if (o->playback) {
pa_sink_input_new_data data;
size_t l;
+ pa_sink *sink;
+
+ if (!(sink = pa_namereg_get(c->protocol->core, o->default_sink, 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.module = o->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, &o->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;
- l = (size_t) (pa_bytes_per_second(&p->sample_spec)*PLAYBACK_BUFFER_SECONDS);
+ pa_sink_input_set_requested_latency(c->sink_input, DEFAULT_SINK_LATENCY);
+
+ l = (size_t) ((double) pa_bytes_per_second(&o->sample_spec)*PLAYBACK_BUFFER_SECONDS);
c->input_memblockq = pa_memblockq_new(
0,
l,
- 0,
- pa_frame_size(&p->sample_spec),
+ l,
+ pa_frame_size(&o->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));
+ pa_atomic_store(&c->playback.missing, (int) pa_memblockq_missing(c->input_memblockq));
pa_sink_input_put(c->sink_input);
}
- if (p->mode & RECORD) {
+ if (o->record) {
pa_source_output_new_data data;
size_t l;
+ pa_source *source;
+
+ if (!(source = pa_namereg_get(c->protocol->core, o->default_source, 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.module = o->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, &o->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,21 +595,23 @@ 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;
- l = (size_t) (pa_bytes_per_second(&p->sample_spec)*RECORD_BUFFER_SECONDS);
+ pa_source_output_set_requested_latency(c->source_output, DEFAULT_SOURCE_LATENCY);
+
+ l = (size_t) (pa_bytes_per_second(&o->sample_spec)*RECORD_BUFFER_SECONDS);
c->output_memblockq = pa_memblockq_new(
0,
l,
0,
- pa_frame_size(&p->sample_spec),
+ pa_frame_size(&o->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);
}
- pa_iochannel_set_callback(c->io, io_callback, c);
pa_idxset_put(p->connections, c, NULL);
return;
@@ -564,73 +621,140 @@ fail:
connection_unlink(c);
}
-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;
- pa_bool_t enable;
+void pa_simple_protocol_disconnect(pa_simple_protocol *p, pa_module *m) {
+ connection *c;
+ void *state = NULL;
- pa_assert(core);
- pa_assert(server);
- pa_assert(ma);
+ pa_assert(p);
+ pa_assert(m);
+
+ while ((c = pa_idxset_iterate(p->connections, &state, NULL)))
+ if (c->options->module == m)
+ connection_unlink(c);
+}
+
+static pa_simple_protocol* simple_protocol_new(pa_core *c) {
+ pa_simple_protocol *p;
+
+ pa_assert(c);
- p = pa_xnew0(pa_protocol_simple, 1);
- p->module = m;
- p->core = core;
- p->server = server;
+ p = pa_xnew(pa_simple_protocol, 1);
+ PA_REFCNT_INIT(p);
+ p->core = c;
p->connections = pa_idxset_new(NULL, NULL);
- p->sample_spec = core->default_sample_spec;
- if (pa_modargs_get_sample_spec(ma, &p->sample_spec) < 0) {
- pa_log("Failed to parse sample type specification.");
- goto fail;
- }
+ pa_assert_se(pa_shared_set(c, "simple-protocol", p) >= 0);
- p->source_name = pa_xstrdup(pa_modargs_get_value(ma, "source", NULL));
- p->sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL));
+ return p;
+}
- 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;
+pa_simple_protocol* pa_simple_protocol_get(pa_core *c) {
+ pa_simple_protocol *p;
- enable = 1;
- if (pa_modargs_get_value_boolean(ma, "playback", &enable) < 0) {
- pa_log("playback= expects a numeric argument.");
- goto fail;
- }
- p->mode |= enable ? PLAYBACK : 0;
+ if ((p = pa_shared_get(c, "simple-protocol")))
+ return pa_simple_protocol_ref(p);
- if ((p->mode & (RECORD|PLAYBACK)) == 0) {
- pa_log("neither playback nor recording enabled for protocol.");
- goto fail;
- }
+ return simple_protocol_new(c);
+}
+
+pa_simple_protocol* pa_simple_protocol_ref(pa_simple_protocol *p) {
+ pa_assert(p);
+ pa_assert(PA_REFCNT_VALUE(p) >= 1);
- pa_socket_server_set_callback(p->server, on_connection, p);
+ PA_REFCNT_INC(p);
return p;
+}
-fail:
- if (p)
- pa_protocol_simple_free(p);
+void pa_simple_protocol_unref(pa_simple_protocol *p) {
+ connection *c;
+ pa_assert(p);
+ pa_assert(PA_REFCNT_VALUE(p) >= 1);
+
+ if (PA_REFCNT_DEC(p) > 0)
+ return;
- return NULL;
+ while ((c = pa_idxset_first(p->connections, NULL)))
+ connection_unlink(c);
+
+ pa_idxset_free(p->connections, NULL, NULL);
+
+ pa_assert_se(pa_shared_remove(p->core, "simple-protocol") >= 0);
+
+ pa_xfree(p);
}
+pa_simple_options* pa_simple_options_new(void) {
+ pa_simple_options *o;
-void pa_protocol_simple_free(pa_protocol_simple *p) {
- connection *c;
- pa_assert(p);
+ o = pa_xnew0(pa_simple_options, 1);
+ PA_REFCNT_INIT(o);
- if (p->connections) {
- while((c = pa_idxset_first(p->connections, NULL)))
- connection_unlink(c);
+ o->record = FALSE;
+ o->playback = TRUE;
- pa_idxset_free(p->connections, NULL, NULL);
+ return o;
+}
+
+pa_simple_options* pa_simple_options_ref(pa_simple_options *o) {
+ pa_assert(o);
+ pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+ PA_REFCNT_INC(o);
+
+ return o;
+}
+
+void pa_simple_options_unref(pa_simple_options *o) {
+ pa_assert(o);
+ pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+ if (PA_REFCNT_DEC(o) > 0)
+ return;
+
+ pa_xfree(o->default_sink);
+ pa_xfree(o->default_source);
+
+ pa_xfree(o);
+}
+
+int pa_simple_options_parse(pa_simple_options *o, pa_core *c, pa_modargs *ma) {
+ pa_bool_t enabled;
+
+ pa_assert(o);
+ pa_assert(PA_REFCNT_VALUE(o) >= 1);
+ pa_assert(ma);
+
+ o->sample_spec = c->default_sample_spec;
+ if (pa_modargs_get_sample_spec_and_channel_map(ma, &o->sample_spec, &o->channel_map, PA_CHANNEL_MAP_DEFAULT) < 0) {
+ pa_log("Failed to parse sample type specification.");
+ return -1;
}
- if (p->server)
- pa_socket_server_unref(p->server);
+ pa_xfree(o->default_source);
+ o->default_source = pa_xstrdup(pa_modargs_get_value(ma, "source", NULL));
- pa_xfree(p);
+ pa_xfree(o->default_sink);
+ o->default_sink = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL));
+
+ enabled = o->record;
+ if (pa_modargs_get_value_boolean(ma, "record", &enabled) < 0) {
+ pa_log("record= expects a boolean argument.");
+ return -1;
+ }
+ o->record = enabled;
+
+ enabled = o->playback;
+ if (pa_modargs_get_value_boolean(ma, "playback", &enabled) < 0) {
+ pa_log("playback= expects a boolean argument.");
+ return -1;
+ }
+ o->playback = enabled;
+
+ if (!o->playback && !o->record) {
+ pa_log("neither playback nor recording enabled for protocol.");
+ return -1;
+ }
+
+ return 0;
}
diff --git a/src/pulsecore/protocol-simple.h b/src/pulsecore/protocol-simple.h
index 9d589e6..c10eabe 100644
--- a/src/pulsecore/protocol-simple.h
+++ b/src/pulsecore/protocol-simple.h
@@ -1,8 +1,6 @@
#ifndef fooprotocolsimplehfoo
#define fooprotocolsimplehfoo
-/* $Id: protocol-simple.h 1426 2007-02-13 15:35:19Z ossman $ */
-
/***
This file is part of PulseAudio.
@@ -29,9 +27,31 @@
#include <pulsecore/core.h>
#include <pulsecore/modargs.h>
-typedef struct pa_protocol_simple pa_protocol_simple;
+typedef struct pa_simple_protocol pa_simple_protocol;
+
+typedef struct pa_simple_options {
+ PA_REFCNT_DECLARE;
+
+ pa_module *module;
+
+ char *default_sink, *default_source;
+
+ pa_sample_spec sample_spec;
+ pa_channel_map channel_map;
+
+ pa_bool_t record:1;
+ pa_bool_t playback:1;
+} pa_simple_options;
+
+pa_simple_protocol* pa_simple_protocol_get(pa_core*core);
+pa_simple_protocol* pa_simple_protocol_ref(pa_simple_protocol *p);
+void pa_simple_protocol_unref(pa_simple_protocol *p);
+void pa_simple_protocol_connect(pa_simple_protocol *p, pa_iochannel *io, pa_simple_options *o);
+void pa_simple_protocol_disconnect(pa_simple_protocol *p, pa_module *m);
-pa_protocol_simple* pa_protocol_simple_new(pa_core *core, pa_socket_server *server, pa_module *m, pa_modargs *ma);
-void pa_protocol_simple_free(pa_protocol_simple *n);
+pa_simple_options* pa_simple_options_new(void);
+pa_simple_options* pa_simple_options_ref(pa_simple_options *o);
+void pa_simple_options_unref(pa_simple_options *o);
+int pa_simple_options_parse(pa_simple_options *o, pa_core *c, pa_modargs *ma);
#endif
diff --git a/src/pulsecore/pstream-util.c b/src/pulsecore/pstream-util.c
index 3796406..f84f486 100644
--- a/src/pulsecore/pstream-util.c
+++ b/src/pulsecore/pstream-util.c
@@ -1,5 +1,3 @@
-/* $Id: pstream-util.c 1971 2007-10-28 19:13:50Z lennart $ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/pstream-util.h b/src/pulsecore/pstream-util.h
index 29e9112..ae0d79c 100644
--- a/src/pulsecore/pstream-util.h
+++ b/src/pulsecore/pstream-util.h
@@ -1,8 +1,6 @@
#ifndef foopstreamutilhfoo
#define foopstreamutilhfoo
-/* $Id: pstream-util.h 1426 2007-02-13 15:35:19Z ossman $ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/pstream.c b/src/pulsecore/pstream.c
index c35ade1..7ff8edc 100644
--- a/src/pulsecore/pstream.c
+++ b/src/pulsecore/pstream.c
@@ -1,5 +1,3 @@
-/* $Id: pstream.c 1971 2007-10-28 19:13:50Z lennart $ */
-
/***
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,23 +267,23 @@ 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;
}
-static void item_free(void *item, PA_GCC_UNUSED void *q) {
+static void item_free(void *item, void *q) {
struct item_info *i = item;
pa_assert(i);
@@ -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);
@@ -490,7 +488,7 @@ static void prepare_next_write_item(pa_pstream *p) {
pa_assert(p->write.current->packet);
p->write.data = p->write.current->packet->data;
- p->write.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH] = htonl(p->write.current->packet->length);
+ p->write.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH] = htonl((uint32_t) p->write.current->packet->length);
} else if (p->write.current->type == PA_PSTREAM_ITEM_SHMRELEASE) {
@@ -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);
@@ -513,7 +511,7 @@ static void prepare_next_write_item(pa_pstream *p) {
p->write.descriptor[PA_PSTREAM_DESCRIPTOR_OFFSET_HI] = htonl((uint32_t) (((uint64_t) p->write.current->offset) >> 32));
p->write.descriptor[PA_PSTREAM_DESCRIPTOR_OFFSET_LO] = htonl((uint32_t) ((uint64_t) p->write.current->offset));
- flags = p->write.current->seek_mode & PA_FLAG_SEEKMASK;
+ flags = (uint32_t) (p->write.current->seek_mode & PA_FLAG_SEEKMASK);
if (p->use_shm) {
uint32_t block_id, shm_id;
@@ -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);
@@ -544,7 +542,7 @@ static void prepare_next_write_item(pa_pstream *p) {
}
if (send_payload) {
- p->write.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH] = htonl(p->write.current->chunk.length);
+ p->write.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH] = htonl((uint32_t) p->write.current->chunk.length);
p->write.memchunk = p->write.current->chunk;
pa_memblock_ref(p->write.memchunk.memblock);
p->write.data = NULL;
@@ -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
@@ -609,7 +607,7 @@ static int do_write(pa_pstream *p) {
if (release_memblock)
pa_memblock_release(release_memblock);
- p->write.index += r;
+ p->write.index += (size_t) r;
if (p->write.index >= PA_PSTREAM_DESCRIPTOR_SIZE + ntohl(p->write.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH])) {
pa_assert(p->write.current);
@@ -677,7 +675,7 @@ static int do_read(pa_pstream *p) {
if (release_memblock)
pa_memblock_release(release_memblock);
- p->read.index += r;
+ p->read.index += (size_t) r;
if (p->read.index == PA_PSTREAM_DESCRIPTOR_SIZE) {
uint32_t flags, length, channel;
@@ -771,7 +769,7 @@ static int do_read(pa_pstream *p) {
if (p->read.memblock && p->recieve_memblock_callback) {
/* Is this memblock data? Than pass it to the user */
- l = (p->read.index - r) < PA_PSTREAM_DESCRIPTOR_SIZE ? p->read.index - PA_PSTREAM_DESCRIPTOR_SIZE : (size_t) r;
+ l = (p->read.index - (size_t) r) < PA_PSTREAM_DESCRIPTOR_SIZE ? (size_t) (p->read.index - PA_PSTREAM_DESCRIPTOR_SIZE) : (size_t) r;
if (l > 0) {
pa_memchunk chunk;
@@ -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,16 +933,16 @@ 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);
+ b = p->write.current || !pa_queue_isempty(p->send_queue);
return b;
}
@@ -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 f505f60..a528b25 100644
--- a/src/pulsecore/pstream.h
+++ b/src/pulsecore/pstream.h
@@ -1,8 +1,6 @@
#ifndef foopstreamhfoo
#define foopstreamhfoo
-/* $Id: pstream.h 1971 2007-10-28 19:13:50Z lennart $ */
-
/***
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 cbab6ff..2c73a3d 100644
--- a/src/pulsecore/queue.c
+++ b/src/pulsecore/queue.c
@@ -1,9 +1,7 @@
-/* $Id: queue.c 1971 2007-10-28 19:13:50Z lennart $ */
-
/***
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
@@ -54,13 +52,13 @@ pa_queue* pa_queue_new(void) {
return q;
}
-void pa_queue_free(pa_queue* q, void (*destroy)(void *p, void *userdata), void *userdata) {
+void pa_queue_free(pa_queue* q, pa_free2_cb_t free_func, void *userdata) {
void *data;
pa_assert(q);
while ((data = pa_queue_pop(q)))
- if (destroy)
- destroy(data, userdata);
+ if (free_func)
+ free_func(data, userdata);
pa_assert(!q->front);
pa_assert(!q->back);
@@ -96,6 +94,7 @@ void pa_queue_push(pa_queue *q, void *p) {
void* pa_queue_pop(pa_queue *q) {
void *p;
struct queue_entry *e;
+
pa_assert(q);
if (!(e = q->front))
@@ -118,7 +117,7 @@ void* pa_queue_pop(pa_queue *q) {
return p;
}
-int pa_queue_is_empty(pa_queue *q) {
+int pa_queue_isempty(pa_queue *q) {
pa_assert(q);
return q->length == 0;
diff --git a/src/pulsecore/queue.h b/src/pulsecore/queue.h
index 72c53c1..f3cec9b 100644
--- a/src/pulsecore/queue.h
+++ b/src/pulsecore/queue.h
@@ -1,12 +1,10 @@
-#ifndef fooqueuehfoo
-#define fooqueuehfoo
-
-/* $Id: queue.h 1426 2007-02-13 15:35:19Z ossman $ */
+#ifndef foopulsecorequeuehfoo
+#define foopulsecorequeuehfoo
/***
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
@@ -24,6 +22,8 @@
USA.
***/
+#include <pulsecore/idxset.h>
+
typedef struct pa_queue pa_queue;
/* A simple implementation of the abstract data type queue. Stores
@@ -31,12 +31,13 @@ typedef struct pa_queue pa_queue;
pa_queue* pa_queue_new(void);
-/* Free the queue and run the specified callback function for every remaining entry. The callback function may be NULL. */
-void pa_queue_free(pa_queue* q, void (*destroy)(void *p, void *userdata), void *userdata);
+/* Free the queue and run the specified callback function for every
+ * remaining entry. The callback function may be NULL. */
+void pa_queue_free(pa_queue* q, pa_free2_cb_t free_func, void *userdata);
void pa_queue_push(pa_queue *q, void *p);
void* pa_queue_pop(pa_queue *q);
-int pa_queue_is_empty(pa_queue *q);
+int pa_queue_isempty(pa_queue *q);
#endif
diff --git a/src/pulsecore/random.c b/src/pulsecore/random.c
index c72876e..518c281 100644
--- a/src/pulsecore/random.c
+++ b/src/pulsecore/random.c
@@ -1,5 +1,3 @@
-/* $Id: random.c 1971 2007-10-28 19:13:50Z lennart $ */
-
/***
This file is part of PulseAudio.
@@ -39,7 +37,7 @@
#include "random.h"
-static int has_whined = 0;
+static pa_bool_t has_whined = TRUE;
static const char * const devices[] = { "/dev/urandom", "/dev/random", NULL };
@@ -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;
@@ -75,6 +77,8 @@ static int random_proper(void *ret_data, size_t length) {
if (ret == 0)
break;
+
+ device++;
}
return ret;
@@ -85,9 +89,11 @@ void pa_random_seed(void) {
unsigned int seed;
if (random_proper(&seed, sizeof(unsigned int)) < 0) {
- if (!has_whined)
+
+ if (!has_whined) {
pa_log_warn("Failed to get proper entropy. Falling back to seeding with current time.");
- has_whined = 1;
+ has_whined = TRUE;
+ }
seed = (unsigned int) time(NULL);
}
@@ -105,9 +111,10 @@ void pa_random(void *ret_data, size_t length) {
if (random_proper(ret_data, length) >= 0)
return;
- if (!has_whined)
+ if (!has_whined) {
pa_log_warn("Failed to get proper entropy. Falling back to unsecure pseudo RNG.");
- has_whined = 1;
+ has_whined = TRUE;
+ }
for (p = ret_data, l = length; l > 0; p++, l--)
*p = (uint8_t) rand();
diff --git a/src/pulsecore/random.h b/src/pulsecore/random.h
index d64a45e..36d7f9d 100644
--- a/src/pulsecore/random.h
+++ b/src/pulsecore/random.h
@@ -1,8 +1,6 @@
#ifndef foorandomhfoo
#define foorandomhfoo
-/* $Id: random.h 1426 2007-02-13 15:35:19Z ossman $ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/refcnt.h b/src/pulsecore/refcnt.h
index c0d66c2..291f450 100644
--- a/src/pulsecore/refcnt.h
+++ b/src/pulsecore/refcnt.h
@@ -1,8 +1,6 @@
#ifndef foopulserefcnthfoo
#define foopulserefcnthfoo
-/* $Id: refcnt.h 1971 2007-10-28 19:13:50Z lennart $ */
-
/***
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 49f7d9a..b2d512c 100644
--- a/src/pulsecore/resampler.c
+++ b/src/pulsecore/resampler.c
@@ -1,5 +1,3 @@
-/* $Id: resampler.c 2159 2008-03-27 23:29:32Z lennart $ */
-
/***
This file is part of PulseAudio.
@@ -31,6 +29,8 @@
#include <samplerate.h>
#endif
+#include <speex/speex_resampler.h>
+
#include <liboil/liboilfuncs.h>
#include <liboil/liboil.h>
@@ -40,14 +40,12 @@
#include <pulsecore/macro.h>
#include <pulsecore/strbuf.h>
-#include "speexwrap.h"
-
#include "ffmpeg/avcodec.h"
#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 method;
@@ -72,12 +70,22 @@ struct pa_resampler {
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;
@@ -98,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
@@ -143,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) {
@@ -208,6 +218,7 @@ pa_resampler* pa_resampler_new(
r->impl_free = NULL;
r->impl_update_rates = NULL;
r->impl_resample = NULL;
+ r->impl_reset = NULL;
/* Fill sample specs */
r->i_ss = *a;
@@ -240,9 +251,9 @@ pa_resampler* pa_resampler_new(
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 (method == PA_RESAMPLER_TRIVIAL || 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_S32NE || a->format == PA_SAMPLE_S32RE ||
a->format == PA_SAMPLE_FLOAT32NE || a->format == PA_SAMPLE_FLOAT32RE ||
@@ -345,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;
@@ -356,22 +373,24 @@ 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 = (uint8_t) (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) {
@@ -411,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) {
@@ -622,7 +642,7 @@ static void calc_map_table(pa_resampler *r) {
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;
+ r->map_table[oc][ic] = 1.0f / (float) n;
ic_connected[ic] = TRUE;
}
@@ -643,7 +663,7 @@ static void calc_map_table(pa_resampler *r) {
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;
+ r->map_table[oc][ic] = 1.0f / (float) n;
ic_connected[ic] = TRUE;
}
@@ -664,7 +684,7 @@ static void calc_map_table(pa_resampler *r) {
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;
+ r->map_table[oc][ic] = 1.0f / (float) n;
ic_connected[ic] = TRUE;
}
} else {
@@ -681,7 +701,7 @@ static void calc_map_table(pa_resampler *r) {
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;
+ r->map_table[oc][ic] = 1.0f / (float) n;
ic_connected[ic] = TRUE;
}
@@ -696,7 +716,11 @@ static void calc_map_table(pa_resampler *r) {
* channels for LFE. */
for (ic = 0; ic < r->i_ss.channels; ic++) {
- r->map_table[oc][ic] = 1.0 / r->i_ss.channels;
+
+ if (!(r->flags & PA_RESAMPLER_NO_LFE))
+ r->map_table[oc][ic] = 1.0f / (float) r->i_ss.channels;
+ else
+ r->map_table[oc][ic] = 0;
/* Please note that a channel connected to LFE
* doesn't really count as connected. */
@@ -743,12 +767,12 @@ static void calc_map_table(pa_resampler *r) {
for (ic = 0; ic < r->i_ss.channels; ic++) {
if (ic_connected[ic]) {
- r->map_table[oc][ic] *= .9;
+ r->map_table[oc][ic] *= .9f;
continue;
}
if (on_left(r->i_cm.map[ic]))
- r->map_table[oc][ic] = .1 / ic_unconnected_left;
+ r->map_table[oc][ic] = .1f / (float) ic_unconnected_left;
}
}
}
@@ -768,12 +792,12 @@ static void calc_map_table(pa_resampler *r) {
for (ic = 0; ic < r->i_ss.channels; ic++) {
if (ic_connected[ic]) {
- r->map_table[oc][ic] *= .9;
+ r->map_table[oc][ic] *= .9f;
continue;
}
if (on_right(r->i_cm.map[ic]))
- r->map_table[oc][ic] = .1 / ic_unconnected_right;
+ r->map_table[oc][ic] = .1f / (float) ic_unconnected_right;
}
}
}
@@ -794,12 +818,12 @@ static void calc_map_table(pa_resampler *r) {
for (ic = 0; ic < r->i_ss.channels; ic++) {
if (ic_connected[ic]) {
- r->map_table[oc][ic] *= .9;
+ r->map_table[oc][ic] *= .9f;
continue;
}
if (on_center(r->i_cm.map[ic])) {
- r->map_table[oc][ic] = .1 / ic_unconnected_center;
+ r->map_table[oc][ic] = .1f / (float) ic_unconnected_center;
mixed_in = TRUE;
}
}
@@ -820,18 +844,18 @@ static void calc_map_table(pa_resampler *r) {
for (ic = 0; ic < r->i_ss.channels; ic++) {
if (ic_connected[ic]) {
- r->map_table[oc][ic] *= .75;
+ r->map_table[oc][ic] *= .75f;
continue;
}
if (on_center(r->i_cm.map[ic]))
- r->map_table[oc][ic] = .375 / ic_unconnected_center;
+ r->map_table[oc][ic] = .375f / (float) ic_unconnected_center;
}
}
}
}
- if (ic_unconnected_lfe > 0) {
+ if (ic_unconnected_lfe > 0 && !(r->flags & PA_RESAMPLER_NO_LFE)) {
/* OK, so there is an unconnected LFE channel. Let's mix
* it into all channels, with factor 0.375 */
@@ -842,7 +866,7 @@ static void calc_map_table(pa_resampler *r) {
continue;
for (oc = 0; oc < r->o_ss.channels; oc++)
- r->map_table[oc][ic] = 0.375 / ic_unconnected_lfe;
+ r->map_table[oc][ic] = 0.375f / (float) ic_unconnected_lfe;
}
}
}
@@ -885,7 +909,7 @@ static pa_memchunk* convert_to_work_format(pa_resampler *r, pa_memchunk *input)
if (!r->to_work_format_func || !input->length)
return input;
- n_samples = (input->length / r->i_fz) * r->i_ss.channels;
+ n_samples = (unsigned) ((input->length / r->i_fz) * r->i_ss.channels);
r->buf1.index = 0;
r->buf1.length = r->w_sz * n_samples;
@@ -954,7 +978,7 @@ static pa_memchunk *remap_channels(pa_resampler *r, pa_memchunk *input) {
if (!r->map_required || !input->length)
return input;
- in_n_samples = input->length / r->w_sz;
+ in_n_samples = (unsigned) (input->length / r->w_sz);
n_frames = in_n_samples / r->i_ss.channels;
out_n_samples = n_frames * r->o_ss.channels;
@@ -974,8 +998,8 @@ static pa_memchunk *remap_channels(pa_resampler *r, pa_memchunk *input) {
memset(dst, 0, r->buf2.length);
- o_skip = r->w_sz * r->o_ss.channels;
- i_skip = r->w_sz * r->i_ss.channels;
+ o_skip = (int) (r->w_sz * r->o_ss.channels);
+ i_skip = (int) (r->w_sz * r->i_ss.channels);
switch (r->work_format) {
case PA_SAMPLE_FLOAT32NE:
@@ -993,7 +1017,7 @@ static pa_memchunk *remap_channels(pa_resampler *r, pa_memchunk *input) {
(float*) dst + oc, o_skip,
(float*) dst + oc, o_skip,
(float*) src + ic, i_skip,
- n_frames,
+ (int) n_frames,
&one, &r->map_table[oc][ic]);
}
}
@@ -1017,7 +1041,7 @@ static pa_memchunk *remap_channels(pa_resampler *r, pa_memchunk *input) {
(int16_t*) dst + oc, o_skip,
(int16_t*) dst + oc, o_skip,
(int16_t*) src + ic, i_skip,
- n_frames,
+ (int) n_frames,
&one, &one);
} else
@@ -1026,8 +1050,8 @@ static pa_memchunk *remap_channels(pa_resampler *r, pa_memchunk *input) {
(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]);
+ (int) n_frames,
+ 1.0f, r->map_table[oc][ic]);
}
}
@@ -1057,10 +1081,10 @@ static pa_memchunk *resample(pa_resampler *r, pa_memchunk *input) {
if (!r->impl_resample || !input->length)
return input;
- in_n_samples = input->length / r->w_sz;
- in_n_frames = in_n_samples / r->o_ss.channels;
+ in_n_samples = (unsigned) (input->length / r->w_sz);
+ in_n_frames = (unsigned) (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;
@@ -1092,8 +1116,8 @@ static pa_memchunk *convert_from_work_format(pa_resampler *r, pa_memchunk *input
if (!r->from_work_format_func || !input->length)
return input;
- n_samples = input->length / r->w_sz;
- n_frames = n_samples / r->o_ss.channels;
+ n_samples = (unsigned) (input->length / r->w_sz);
+ n_frames = n_samples / r->o_ss.channels;
r->buf4.index = 0;
r->buf4.length = r->o_fz * n_frames;
@@ -1158,10 +1182,10 @@ static void libsamplerate_resample(pa_resampler *r, const pa_memchunk *input, un
memset(&data, 0, sizeof(data));
data.data_in = (float*) ((uint8_t*) pa_memblock_acquire(input->memblock) + input->index);
- data.input_frames = in_n_frames;
+ data.input_frames = (long int) in_n_frames;
data.data_out = (float*) ((uint8_t*) pa_memblock_acquire(output->memblock) + output->index);
- data.output_frames = *out_n_frames;
+ data.output_frames = (long int) *out_n_frames;
data.src_ratio = (double) r->o_ss.rate / r->i_ss.rate;
data.end_of_input = 0;
@@ -1172,7 +1196,7 @@ static void libsamplerate_resample(pa_resampler *r, const pa_memchunk *input, un
pa_memblock_release(input->memblock);
pa_memblock_release(output->memblock);
- *out_n_frames = data.output_frames_gen;
+ *out_n_frames = (unsigned) data.output_frames_gen;
}
static void libsamplerate_update_rates(pa_resampler *r) {
@@ -1181,6 +1205,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);
@@ -1199,6 +1229,7 @@ static int libsamplerate_init(pa_resampler *r) {
r->impl_free = libsamplerate_free;
r->impl_update_rates = libsamplerate_update_rates;
r->impl_resample = libsamplerate_resample;
+ r->impl_reset = libsamplerate_reset;
return 0;
}
@@ -1218,7 +1249,7 @@ static void speex_resample_float(pa_resampler *r, const pa_memchunk *input, unsi
in = (float*) ((uint8_t*) pa_memblock_acquire(input->memblock) + input->index);
out = (float*) ((uint8_t*) pa_memblock_acquire(output->memblock) + output->index);
- pa_assert_se(paspfl_resampler_process_interleaved_float(r->speex.state, in, &inf, out, &outf) == 0);
+ pa_assert_se(speex_resampler_process_interleaved_float(r->speex.state, in, &inf, out, &outf) == 0);
pa_memblock_release(input->memblock);
pa_memblock_release(output->memblock);
@@ -1239,7 +1270,7 @@ static void speex_resample_int(pa_resampler *r, const pa_memchunk *input, unsign
in = (int16_t*) ((uint8_t*) pa_memblock_acquire(input->memblock) + input->index);
out = (int16_t*) ((uint8_t*) pa_memblock_acquire(output->memblock) + output->index);
- pa_assert_se(paspfx_resampler_process_interleaved_int(r->speex.state, in, &inf, out, &outf) == 0);
+ pa_assert_se(speex_resampler_process_interleaved_int(r->speex.state, in, &inf, out, &outf) == 0);
pa_memblock_release(input->memblock);
pa_memblock_release(output->memblock);
@@ -1251,12 +1282,13 @@ 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->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->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);
- }
+ pa_assert_se(speex_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);
+
+ pa_assert_se(speex_resampler_reset_mem(r->speex.state) == 0);
}
static void speex_free(pa_resampler *r) {
@@ -1265,12 +1297,7 @@ static void speex_free(pa_resampler *r) {
if (!r->speex.state)
return;
- 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->method >= PA_RESAMPLER_SPEEX_FLOAT_BASE && r->method <= PA_RESAMPLER_SPEEX_FLOAT_MAX);
- paspfl_resampler_destroy(r->speex.state);
- }
+ speex_resampler_destroy(r->speex.state);
}
static int speex_init(pa_resampler *r) {
@@ -1280,28 +1307,25 @@ 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->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);
-
- if (!(r->speex.state = paspfx_resampler_init(r->o_ss.channels, r->i_ss.rate, r->o_ss.rate, q, &err)))
- return -1;
+ q = r->method - PA_RESAMPLER_SPEEX_FIXED_BASE;
r->impl_resample = speex_resample_int;
+
} else {
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);
-
- if (!(r->speex.state = paspfl_resampler_init(r->o_ss.channels, r->i_ss.rate, r->o_ss.rate, q, &err)))
- return -1;
+ q = r->method - PA_RESAMPLER_SPEEX_FLOAT_BASE;
r->impl_resample = speex_resample_float;
}
+ pa_log_info("Choosing speex quality setting %i.", q);
+
+ if (!(r->speex.state = speex_resampler_init(r->o_ss.channels, r->i_ss.rate, r->o_ss.rate, q, &err)))
+ return -1;
+
return 0;
}
@@ -1334,7 +1358,7 @@ static void trivial_resample(pa_resampler *r, const pa_memchunk *input, unsigned
pa_assert(o_index * fz < pa_memblock_get_length(output->memblock));
oil_memcpy((uint8_t*) dst + fz * o_index,
- (uint8_t*) src + fz * j, fz);
+ (uint8_t*) src + fz * j, (int) fz);
}
pa_memblock_release(input->memblock);
@@ -1353,7 +1377,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;
@@ -1366,8 +1390,126 @@ 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);
+
+ if (j > r->peaks.i_counter)
+ j -= r->peaks.i_counter;
+ else
+ j = 0;
+
+ 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 * start);
+ int16_t *d = (int16_t*) ((uint8_t*) dst + fz * o_index);
+
+ for (i = start; i <= j && i < in_n_frames; i++)
+
+ for (c = 0; c < r->o_ss.channels; c++, s++) {
+ int16_t n;
+
+ n = (int16_t) (*s < 0 ? -*s : *s);
+
+ if (PA_UNLIKELY(n > r->peaks.max_i[c]))
+ r->peaks.max_i[c] = n;
+ }
+
+ if (i >= in_n_frames)
+ break;
+
+ 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 * start);
+ float *d = (float*) ((uint8_t*) dst + fz * o_index);
+
+ pa_assert(r->work_format == PA_SAMPLE_FLOAT32NE);
+
+ for (i = start; i <= j && i < in_n_frames; 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;
+ }
+
+ if (i >= in_n_frames)
+ break;
+
+ for (c = 0; c < r->o_ss.channels; c++, d++) {
+ *d = r->peaks.max_f[c];
+ r->peaks.max_f[c] = 0;
+ }
+ }
+
+ start = j;
+ }
+
+ 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;
}
@@ -1394,7 +1536,7 @@ static void ffmpeg_resample(pa_resampler *r, const pa_memchunk *input, unsigned
p = pa_memblock_acquire(b);
/* Copy the remaining data into it */
- l = r->ffmpeg.buf[c].length;
+ l = (unsigned) r->ffmpeg.buf[c].length;
if (r->ffmpeg.buf[c].memblock) {
t = (int16_t*) ((uint8_t*) pa_memblock_acquire(r->ffmpeg.buf[c].memblock) + r->ffmpeg.buf[c].index);
memcpy(p, t, l);
@@ -1414,18 +1556,18 @@ static void ffmpeg_resample(pa_resampler *r, const pa_memchunk *input, unsigned
pa_memblock_release(input->memblock);
/* Calculate the resulting number of frames */
- in = in_n_frames + l / sizeof(int16_t);
+ in = (unsigned) in_n_frames + l / (unsigned) sizeof(int16_t);
/* Allocate buffer for the result */
w = pa_memblock_new(r->mempool, *out_n_frames * sizeof(int16_t));
q = pa_memblock_acquire(w);
/* Now, resample */
- used_frames = av_resample(r->ffmpeg.state,
- q, p,
- &consumed_frames,
- in, *out_n_frames,
- c >= (unsigned) r->o_ss.channels-1);
+ used_frames = (unsigned) av_resample(r->ffmpeg.state,
+ q, p,
+ &consumed_frames,
+ (int) in, (int) *out_n_frames,
+ c >= (unsigned) (r->o_ss.channels-1));
pa_memblock_release(b);
@@ -1433,8 +1575,8 @@ static void ffmpeg_resample(pa_resampler *r, const pa_memchunk *input, unsigned
pa_assert(consumed_frames <= (int) in);
if (consumed_frames < (int) in) {
r->ffmpeg.buf[c].memblock = b;
- r->ffmpeg.buf[c].index = consumed_frames * sizeof(int16_t);
- r->ffmpeg.buf[c].length = (in - consumed_frames) * sizeof(int16_t);
+ r->ffmpeg.buf[c].index = (size_t) consumed_frames * sizeof(int16_t);
+ r->ffmpeg.buf[c].length = (size_t) (in - (unsigned) consumed_frames) * sizeof(int16_t);
} else
pa_memblock_unref(b);
@@ -1476,7 +1618,7 @@ static int ffmpeg_init(pa_resampler *r) {
* internally only uses these hardcoded values, so let's use them
* here for now as well until ffmpeg makes this configurable. */
- if (!(r->ffmpeg.state = av_resample_init(r->o_ss.rate, r->i_ss.rate, 16, 10, 0, 0.8)))
+ if (!(r->ffmpeg.state = av_resample_init((int) r->o_ss.rate, (int) r->i_ss.rate, 16, 10, 0, 0.8)))
return -1;
r->impl_free = ffmpeg_free;
@@ -1495,9 +1637,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 5d4373c..87110cc 100644
--- a/src/pulsecore/resampler.h
+++ b/src/pulsecore/resampler.h
@@ -1,8 +1,6 @@
#ifndef fooresamplerhfoo
#define fooresamplerhfoo
-/* $Id: resampler.h 2044 2007-11-11 02:30:59Z lennart $ */
-
/***
This file is part of PulseAudio.
@@ -46,13 +44,15 @@ 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_RESAMPLER_VARIABLE_RATE = 0x0001U,
+ PA_RESAMPLER_NO_REMAP = 0x0002U, /* implies NO_REMIX */
+ PA_RESAMPLER_NO_REMIX = 0x0004U,
+ PA_RESAMPLER_NO_LFE = 0x0008U
} pa_resample_flags_t;
pa_resampler* pa_resampler_new(
@@ -69,6 +69,9 @@ 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);
@@ -81,6 +84,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);
@@ -93,4 +99,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 07d776e..f33de83 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 f0360af..aa2cdac 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.
@@ -25,6 +23,7 @@
***/
#include <pulsecore/macro.h>
+#include <pulse/sample.h>
struct timeval;
@@ -40,4 +39,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 8300826..543262b 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;
@@ -368,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));
}
@@ -391,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) ? (int) ((timeout.tv_sec*1000) + (timeout.tv_usec / 1000)) : -1);
if (r < 0) {
if (errno == EAGAIN || errno == EINTR)
@@ -409,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) {
@@ -481,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__
@@ -510,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);
@@ -535,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;
@@ -546,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;
@@ -683,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;
@@ -725,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;
@@ -735,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 02f5c7c..08776ef 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.
@@ -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 bfc49c8..4cd6aa8 100644
--- a/src/pulsecore/rtsig.c
+++ b/src/pulsecore/rtsig.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -42,7 +40,7 @@ static void _free_rtsig(void *p) {
pa_rtsig_put(PA_PTR_TO_INT(p));
}
-PA_STATIC_FLIST_DECLARE(rtsig_flist, pa_make_power_of_two(SIGRTMAX-SIGRTMIN+1), NULL);
+PA_STATIC_FLIST_DECLARE(rtsig_flist, pa_make_power_of_two((unsigned) (SIGRTMAX-SIGRTMIN+1)), NULL);
PA_STATIC_TLS_DECLARE(rtsig_tls, _free_rtsig);
static pa_atomic_t rtsig_current = PA_ATOMIC_INIT(-1);
diff --git a/src/pulsecore/rtsig.h b/src/pulsecore/rtsig.h
index 7830d27..e414122 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 a94da30..7b9ac7b 100644
--- a/src/pulsecore/sample-util.c
+++ b/src/pulsecore/sample-util.c
@@ -1,5 +1,3 @@
-/* $Id: sample-util.c 2041 2007-11-09 17:11:45Z lennart $ */
-
/***
This file is part of PulseAudio.
@@ -33,6 +31,8 @@
#include <liboil/liboilfuncs.h>
#include <liboil/liboil.h>
+#include <pulse/timeval.h>
+
#include <pulsecore/log.h>
#include <pulsecore/macro.h>
#include <pulsecore/g711.h>
@@ -42,29 +42,6 @@
#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;
@@ -74,10 +51,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);
@@ -87,53 +65,37 @@ 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_S32LE:
case PA_SAMPLE_S32BE:
- case PA_SAMPLE_FLOAT32:
- case PA_SAMPLE_FLOAT32RE:
- c = 0;
- break;
+ 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();
}
-
- memset(p, c, length);
}
-static void calc_linear_integer_stream_volumes(pa_mix_info streams[], unsigned nstreams, const pa_sample_spec *spec) {
- unsigned k;
-
- pa_assert(streams);
+void* pa_silence_memory(void *p, size_t length, const pa_sample_spec *spec) {
+ pa_assert(p);
+ pa_assert(length > 0);
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);
- }
- }
+ memset(p, silence_byte(spec->format), length);
+ return p;
}
static void calc_linear_integer_volume(int32_t linear[], const pa_cvolume *volume) {
@@ -143,33 +105,55 @@ static void calc_linear_integer_volume(int32_t linear[], const pa_cvolume *volum
pa_assert(volume);
for (channel = 0; channel < volume->channels; channel++)
- linear[channel] = (int32_t) (pa_sw_volume_to_linear(volume->values[channel]) * 0x10000);
+ linear[channel] = (int32_t) lrint(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;
+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] = (float) pa_sw_volume_to_linear(volume->values[channel]);
+}
+
+static void calc_linear_integer_stream_volumes(pa_mix_info streams[], unsigned nstreams, const pa_cvolume *volume, const pa_sample_spec *spec) {
+ unsigned k, channel;
+ float linear[PA_CHANNELS_MAX];
pa_assert(streams);
pa_assert(spec);
+ pa_assert(volume);
+
+ calc_linear_float_volume(linear, volume);
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]);
+ m->linear[channel].i = (int32_t) lrint(pa_sw_volume_to_linear(m->volume.values[channel]) * linear[channel] * 0x10000);
}
}
}
-static void calc_linear_float_volume(float linear[], const pa_cvolume *volume) {
- unsigned channel;
+static void calc_linear_float_stream_volumes(pa_mix_info streams[], unsigned nstreams, const pa_cvolume *volume, const pa_sample_spec *spec) {
+ unsigned k, channel;
+ float linear[PA_CHANNELS_MAX];
- pa_assert(linear);
+ pa_assert(streams);
+ pa_assert(spec);
pa_assert(volume);
- for (channel = 0; channel < volume->channels; channel++)
- linear[channel] = pa_sw_volume_to_linear(volume->values[channel]);
+ calc_linear_float_volume(linear, volume);
+
+ for (k = 0; k < nstreams; k++) {
+
+ for (channel = 0; channel < spec->channels; channel++) {
+ pa_mix_info *m = streams + k;
+ m->linear[channel].f = (float) (pa_sw_volume_to_linear(m->volume.values[channel]) * linear[channel]);
+ }
+ }
}
size_t pa_mix(
@@ -183,7 +167,8 @@ size_t pa_mix(
pa_cvolume full_volume;
unsigned k;
- size_t d = 0;
+ unsigned z;
+ void *end;
pa_assert(streams);
pa_assert(data);
@@ -193,45 +178,46 @@ size_t pa_mix(
if (!volume)
volume = pa_cvolume_reset(&full_volume, spec->channels);
+ if (mute || pa_cvolume_is_muted(volume) || nstreams <= 0) {
+ pa_silence_memory(data, length, spec);
+ return length;
+ }
+
for (k = 0; k < nstreams; k++)
streams[k].ptr = (uint8_t*) pa_memblock_acquire(streams[k].chunk.memblock) + streams[k].chunk.index;
+ for (z = 0; z < nstreams; z++)
+ if (length > streams[z].chunk.length)
+ length = streams[z].chunk.length;
+
+ end = (uint8_t*) data + length;
+
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);
+ calc_linear_integer_stream_volumes(streams, nstreams, volume, spec);
- for (d = 0;; d += sizeof(int16_t)) {
+ while (data < end) {
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 = *((int16_t*) m->ptr);
- v = (v * cv) / 0x10000;
- }
+ if (PA_UNLIKELY(cv <= 0))
+ continue;
+ v = *((int16_t*) m->ptr);
+ v = (v * cv) / 0x10000;
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);
@@ -245,38 +231,28 @@ 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);
+ calc_linear_integer_stream_volumes(streams, nstreams, volume, spec);
- for (d = 0;; d += sizeof(int16_t)) {
+ while (data < end) {
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 = PA_INT16_SWAP(*((int16_t*) m->ptr));
- v = (v * cv) / 0x10000;
- }
+ if (PA_UNLIKELY(cv <= 0))
+ continue;
+ v = PA_INT16_SWAP(*((int16_t*) m->ptr));
+ v = (v * cv) / 0x10000;
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) = PA_INT16_SWAP((int16_t) sum);
data = (uint8_t*) data + sizeof(int16_t);
@@ -290,39 +266,29 @@ size_t pa_mix(
case PA_SAMPLE_S32NE:{
unsigned channel = 0;
- int32_t linear[PA_CHANNELS_MAX];
- calc_linear_integer_stream_volumes(streams, nstreams, spec);
- calc_linear_integer_volume(linear, volume);
+ calc_linear_integer_stream_volumes(streams, nstreams, volume, spec);
- for (d = 0;; d += sizeof(int32_t)) {
+ while (data < end) {
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;
+ int64_t v;
- 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 (PA_UNLIKELY(cv <= 0))
+ continue;
+ v = *((int32_t*) m->ptr);
+ v = (v * cv) / 0x10000;
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);
@@ -336,39 +302,29 @@ size_t pa_mix(
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);
+ calc_linear_integer_stream_volumes(streams, nstreams, volume, spec);
- for (d = 0;; d += sizeof(int32_t)) {
+ while (data < end) {
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;
+ int64_t v;
- 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;
- }
+ if (PA_UNLIKELY(cv <= 0))
+ continue;
+ v = PA_INT32_SWAP(*((int32_t*) m->ptr));
+ v = (v * cv) / 0x10000;
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) = PA_INT32_SWAP((int32_t) sum);
data = (uint8_t*) data + sizeof(int32_t);
@@ -382,37 +338,27 @@ 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);
+ calc_linear_integer_stream_volumes(streams, nstreams, volume, spec);
- for (d = 0;; d ++) {
+ while (data < end) {
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) *((uint8_t*) m->ptr) - 0x80;
- v = (v * cv) / 0x10000;
- }
+ if (PA_UNLIKELY(cv <= 0))
+ continue;
+ v = (int32_t) *((uint8_t*) m->ptr) - 0x80;
+ v = (v * cv) / 0x10000;
sum += v;
+
m->ptr = (uint8_t*) m->ptr + 1;
}
- sum = (sum * linear[channel]) / 0x10000;
sum = PA_CLAMP_UNLIKELY(sum, -0x80, 0x7F);
*((uint8_t*) data) = (uint8_t) (sum + 0x80);
@@ -427,39 +373,29 @@ size_t pa_mix(
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);
+ calc_linear_integer_stream_volumes(streams, nstreams, volume, spec);
- for (d = 0;; d ++) {
+ while (data < end) {
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 (PA_UNLIKELY(cv <= 0))
+ continue;
+ v = (int32_t) st_ulaw2linear16(*((uint8_t*) m->ptr));
+ v = (v * cv) / 0x10000;
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);
+ *((uint8_t*) data) = (uint8_t) st_14linear2ulaw((int16_t) sum >> 2);
data = (uint8_t*) data + 1;
@@ -472,39 +408,29 @@ size_t pa_mix(
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);
+ calc_linear_integer_stream_volumes(streams, nstreams, volume, spec);
- for (d = 0;; d ++) {
+ while (data < end) {
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_alaw2linear16(*((uint8_t*) m->ptr));
- v = (v * cv) / 0x10000;
- }
+ if (PA_UNLIKELY(cv <= 0))
+ continue;
+ v = (int32_t) st_alaw2linear16(*((uint8_t*) m->ptr));
+ v = (v * cv) / 0x10000;
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_13linear2alaw(sum >> 3);
+ *((uint8_t*) data) = (uint8_t) st_13linear2alaw((int16_t) sum >> 3);
data = (uint8_t*) data + 1;
@@ -517,37 +443,27 @@ 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);
+ calc_linear_float_stream_volumes(streams, nstreams, volume, spec);
- for (d = 0;; d += sizeof(float)) {
+ while (data < end) {
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 {
- v = *((float*) m->ptr);
- v *= cv;
- }
+ if (PA_UNLIKELY(cv <= 0))
+ continue;
+ v = *((float*) m->ptr);
+ v *= cv;
sum += v;
+
m->ptr = (uint8_t*) m->ptr + sizeof(float);
}
- sum *= linear[channel];
*((float*) data) = sum;
data = (uint8_t*) data + sizeof(float);
@@ -563,38 +479,27 @@ size_t pa_mix(
unsigned channel = 0;
float linear[PA_CHANNELS_MAX];
- calc_linear_float_stream_volumes(streams, nstreams, spec);
- calc_linear_float_volume(linear, volume);
+ calc_linear_float_stream_volumes(streams, nstreams, volume, spec);
- for (d = 0;; d += sizeof(float)) {
+ while (data < end) {
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 (PA_UNLIKELY(cv <= 0))
+ continue;
+ v = PA_FLOAT32_SWAP(*(float*) m->ptr);
+ v *= cv;
sum += v;
+
m->ptr = (uint8_t*) m->ptr + sizeof(float);
}
- sum *= linear[channel];
- *((uint32_t*) data) = PA_UINT32_SWAP(*(uint32_t*) &sum);
+ *((float*) data) = PA_FLOAT32_SWAP(sum);
data = (uint8_t*) data + sizeof(float);
@@ -606,16 +511,14 @@ size_t pa_mix(
}
default:
- pa_log_error("ERROR: Unable to mix audio data of format %s.", pa_sample_format_to_string(spec->format));
+ pa_log_error("Unable to mix audio data of format %s.", pa_sample_format_to_string(spec->format));
pa_assert_not_reached();
}
-finish:
-
for (k = 0; k < nstreams; k++)
pa_memblock_release(streams[k].chunk.memblock);
- return d;
+ return length;
}
@@ -631,6 +534,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;
@@ -644,14 +550,15 @@ void pa_volume_memchunk(
switch (spec->format) {
case PA_SAMPLE_S16NE: {
- int16_t *d;
- size_t n;
+ int16_t *d, *e;
unsigned channel;
int32_t linear[PA_CHANNELS_MAX];
calc_linear_integer_volume(linear, volume);
- for (channel = 0, d = ptr, n = c->length/sizeof(int16_t); n > 0; d++, n--) {
+ e = (int16_t*) ptr + c->length/sizeof(int16_t);
+
+ for (channel = 0, d = ptr; d < e; d++) {
int32_t t;
t = (int32_t)(*d);
@@ -666,17 +573,18 @@ void pa_volume_memchunk(
}
case PA_SAMPLE_S16RE: {
- int16_t *d;
- size_t n;
+ int16_t *d, *e;
unsigned channel;
int32_t linear[PA_CHANNELS_MAX];
calc_linear_integer_volume(linear, volume);
- for (channel = 0, d = ptr, n = c->length/sizeof(int16_t); n > 0; d++, n--) {
+ e = (int16_t*) ptr + c->length/sizeof(int16_t);
+
+ for (channel = 0, d = ptr; d < e; d++) {
int32_t t;
- t = (int32_t)(PA_INT16_SWAP(*d));
+ t = (int32_t) PA_INT16_SWAP(*d);
t = (t * linear[channel]) / 0x10000;
t = PA_CLAMP_UNLIKELY(t, -0x8000, 0x7FFF);
*d = PA_INT16_SWAP((int16_t) t);
@@ -689,14 +597,15 @@ void pa_volume_memchunk(
}
case PA_SAMPLE_S32NE: {
- int32_t *d;
- size_t n;
+ int32_t *d, *e;
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--) {
+ e = (int32_t*) ptr + c->length/sizeof(int32_t);
+
+ for (channel = 0, d = ptr; d < e; d++) {
int64_t t;
t = (int64_t)(*d);
@@ -711,17 +620,18 @@ void pa_volume_memchunk(
}
case PA_SAMPLE_S32RE: {
- int32_t *d;
- size_t n;
+ int32_t *d, *e;
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--) {
+ e = (int32_t*) ptr + c->length/sizeof(int32_t);
+
+ for (channel = 0, d = ptr; d < e; d++) {
int64_t t;
- t = (int64_t)(PA_INT32_SWAP(*d));
+ t = (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);
@@ -734,14 +644,15 @@ void pa_volume_memchunk(
}
case PA_SAMPLE_U8: {
- uint8_t *d;
- size_t n;
+ uint8_t *d, *e;
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--) {
+ e = (uint8_t*) ptr + c->length;
+
+ for (channel = 0, d = ptr; d < e; d++) {
int32_t t;
t = (int32_t) *d - 0x80;
@@ -756,20 +667,21 @@ void pa_volume_memchunk(
}
case PA_SAMPLE_ULAW: {
- uint8_t *d;
- size_t n;
+ uint8_t *d, *e;
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--) {
+ e = (uint8_t*) ptr + c->length;
+
+ for (channel = 0, d = ptr; d < e; d++) {
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);
+ *d = (uint8_t) st_14linear2ulaw((int16_t) t >> 2);
if (PA_UNLIKELY(++channel >= spec->channels))
channel = 0;
@@ -778,20 +690,21 @@ void pa_volume_memchunk(
}
case PA_SAMPLE_ALAW: {
- uint8_t *d;
- size_t n;
+ uint8_t *d, *e;
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--) {
+ e = (uint8_t*) ptr + c->length;
+
+ for (channel = 0, d = ptr; d < e; d++) {
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);
+ *d = (uint8_t) st_13linear2alaw((int16_t) t >> 3);
if (PA_UNLIKELY(++channel >= spec->channels))
channel = 0;
@@ -806,8 +719,8 @@ void pa_volume_memchunk(
unsigned channel;
d = ptr;
- skip = spec->channels * sizeof(float);
- n = c->length/sizeof(float)/spec->channels;
+ skip = (int) (spec->channels * sizeof(float));
+ n = (unsigned) (c->length/sizeof(float)/spec->channels);
for (channel = 0; channel < spec->channels; channel ++) {
float v, *t;
@@ -817,28 +730,26 @@ void pa_volume_memchunk(
v = (float) pa_sw_volume_to_linear(volume->values[channel]);
t = d + channel;
- oil_scalarmult_f32(t, skip, t, skip, &v, n);
+ oil_scalarmult_f32(t, skip, t, skip, &v, (int) n);
}
break;
}
case PA_SAMPLE_FLOAT32RE: {
- uint32_t *d;
- size_t n;
+ float *d, *e;
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--) {
+ e = (float*) ptr + c->length/sizeof(float);
+
+ for (channel = 0, d = ptr; d < e; d++) {
float t;
- uint32_t z;
- z = PA_UINT32_SWAP(*d);
- t = *(float*) &z;
+ t = PA_FLOAT32_SWAP(*d);
t *= linear[channel];
- z = *(uint32_t*) &t;
- *d = PA_UINT32_SWAP(z);
+ *d = PA_FLOAT32_SWAP(t);
if (PA_UNLIKELY(++channel >= spec->channels))
channel = 0;
@@ -897,7 +808,7 @@ void pa_interleave(const void *src[], unsigned channels, void *dst, size_t ss, u
d = (uint8_t*) dst + c * ss;
for (j = 0; j < n; j ++) {
- oil_memcpy(d, s, ss);
+ oil_memcpy(d, s, (int) ss);
s = (uint8_t*) s + ss;
d = (uint8_t*) d + fs;
}
@@ -925,9 +836,154 @@ void pa_deinterleave(const void *src, void *dst[], unsigned channels, size_t ss,
d = dst[c];
for (j = 0; j < n; j ++) {
- oil_memcpy(d, s, ss);
+ oil_memcpy(d, s, (int) ss);
s = (uint8_t*) s + fs;
d = (uint8_t*) d + 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, (int) dstr, s, (int) sstr, (int) 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.0f, 1.0f);
+ *d = PA_FLOAT32_SWAP(f);
+
+ s = (const float*) ((const uint8_t*) s + sstr);
+ d = (float*) ((uint8_t*) d + dstr);
+ }
+ }
+}
+
+/* Similar to pa_bytes_to_usec() but rounds up, not down */
+
+pa_usec_t pa_bytes_to_usec_round_up(uint64_t length, const pa_sample_spec *spec) {
+ size_t fs;
+ pa_usec_t usec;
+
+ pa_assert(spec);
+
+ fs = pa_frame_size(spec);
+ length = (length + fs - 1) / fs;
+
+ usec = (pa_usec_t) length * PA_USEC_PER_SEC;
+
+ return (usec + spec->rate - 1) / spec->rate;
+}
+
+/* Similar to pa_usec_to_bytes() but rounds up, not down */
+
+size_t pa_usec_to_bytes_round_up(pa_usec_t t, const pa_sample_spec *spec) {
+ uint64_t u;
+ pa_assert(spec);
+
+ u = (uint64_t) t * (uint64_t) spec->rate;
+
+ u = (u + PA_USEC_PER_SEC - 1) / PA_USEC_PER_SEC;
+
+ u *= pa_frame_size(spec);
+
+ return (size_t) u;
+}
diff --git a/src/pulsecore/sample-util.h b/src/pulsecore/sample-util.h
index 56fc5e1..06ecb72 100644
--- a/src/pulsecore/sample-util.h
+++ b/src/pulsecore/sample-util.h
@@ -1,8 +1,6 @@
#ifndef foosampleutilhfoo
#define foosampleutilhfoo
-/* $Id: sample-util.h 2041 2007-11-09 17:11:45Z lennart $ */
-
/***
This file is part of PulseAudio.
@@ -30,10 +28,18 @@
#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;
@@ -70,4 +76,9 @@ 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);
+
+pa_usec_t pa_bytes_to_usec_round_up(uint64_t length, const pa_sample_spec *spec);
+size_t pa_usec_to_bytes_round_up(pa_usec_t t, const pa_sample_spec *spec);
+
#endif
diff --git a/src/pulsecore/sconv-s16be.c b/src/pulsecore/sconv-s16be.c
index 02785cb..e7e1449 100644
--- a/src/pulsecore/sconv-s16be.c
+++ b/src/pulsecore/sconv-s16be.c
@@ -1,5 +1,3 @@
-/* $Id: sconv-s16be.c 2037 2007-11-09 02:45:07Z lennart $ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/sconv-s16be.h b/src/pulsecore/sconv-s16be.h
index ffc4eb7..6058081 100644
--- a/src/pulsecore/sconv-s16be.h
+++ b/src/pulsecore/sconv-s16be.h
@@ -1,8 +1,6 @@
#ifndef foosconv_s16befoo
#define foosconv_s16befoo
-/* $Id: sconv-s16be.h 2037 2007-11-09 02:45:07Z lennart $ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/sconv-s16le.c b/src/pulsecore/sconv-s16le.c
index 895acc4..159c465 100644
--- a/src/pulsecore/sconv-s16le.c
+++ b/src/pulsecore/sconv-s16le.c
@@ -1,5 +1,3 @@
-/* $Id: sconv-s16le.c 2037 2007-11-09 02:45:07Z lennart $ */
-
/***
This file is part of PulseAudio.
@@ -72,13 +70,13 @@ void pa_sconv_s16le_to_float32ne(unsigned n, const int16_t *a, float *b) {
for (; n > 0; n--) {
int16_t s = *(a++);
- *(b++) = ((float) INT16_FROM(s))/0x7FFF;
+ *(b++) = ((float) INT16_FROM(s))/(float) 0x7FFF;
}
#else
{
static const double add = 0, factor = 1.0/0x7FFF;
- oil_scaleconv_f32_s16(b, a, n, &add, &factor);
+ oil_scaleconv_f32_s16(b, a, (int) n, &add, &factor);
}
#endif
}
@@ -97,7 +95,7 @@ void pa_sconv_s32le_to_float32ne(unsigned n, const int32_t *a, float *b) {
#else
{
static const double add = 0, factor = 1.0/0x7FFFFFFF;
- oil_scaleconv_f32_s32(b, a, n, &add, &factor);
+ oil_scaleconv_f32_s32(b, a, (int) n, &add, &factor);
}
#endif
}
@@ -112,15 +110,15 @@ void pa_sconv_s16le_from_float32ne(unsigned n, const float *a, int16_t *b) {
int16_t s;
float v = *(a++);
- v = PA_CLAMP_UNLIKELY(v, -1, 1);
- s = (int16_t) (v * 0x7FFF);
+ v = PA_CLAMP_UNLIKELY(v, -1.0f, 1.f);
+ s = (int16_t) lrintf(v * 0x7FFF);
*(b++) = INT16_TO(s);
}
#else
{
static const double add = 0, factor = 0x7FFF;
- oil_scaleconv_s16_f32(b, a, n, &add, &factor);
+ oil_scaleconv_s16_f32(b, a, (int) n, &add, &factor);
}
#endif
}
@@ -135,15 +133,15 @@ void pa_sconv_s32le_from_float32ne(unsigned n, const float *a, int32_t *b) {
int32_t s;
float v = *(a++);
- v = PA_CLAMP_UNLIKELY(v, -1, 1);
- s = (int32_t) ((double) v * (double) 0x7FFFFFFF);
+ v = PA_CLAMP_UNLIKELY(v, -1.0f, 1.0f);
+ s = (int32_t) lrint((double) v * (double) 0x7FFFFFFF);
*(b++) = INT32_TO(s);
}
#else
{
static const double add = 0, factor = 0x7FFFFFFF;
- oil_scaleconv_s32_f32(b, a, n, &add, &factor);
+ oil_scaleconv_s32_f32(b, a, (int) n, &add, &factor);
}
#endif
}
@@ -155,8 +153,7 @@ void pa_sconv_s16le_to_float32re(unsigned n, const int16_t *a, float *b) {
for (; n > 0; n--) {
int16_t s = *(a++);
float k = ((float) INT16_FROM(s))/0x7FFF;
- uint32_t *j = (uint32_t*) &k;
- *j = PA_UINT32_SWAP(*j);
+ k = PA_FLOAT32_SWAP(k);
*(b++) = k;
}
}
@@ -168,8 +165,7 @@ void pa_sconv_s32le_to_float32re(unsigned n, const int32_t *a, float *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);
+ k = PA_FLOAT32_SWAP(k);
*(b++) = k;
}
}
@@ -181,10 +177,9 @@ void pa_sconv_s16le_from_float32re(unsigned n, const float *a, int16_t *b) {
for (; n > 0; n--) {
int16_t s;
float v = *(a++);
- uint32_t *j = (uint32_t*) &v;
- *j = PA_UINT32_SWAP(*j);
- v = PA_CLAMP_UNLIKELY(v, -1, 1);
- s = (int16_t) (v * 0x7FFF);
+ v = PA_FLOAT32_SWAP(v);
+ v = PA_CLAMP_UNLIKELY(v, -1.0f, 1.0f);
+ s = (int16_t) lrintf(v * 0x7FFF);
*(b++) = INT16_TO(s);
}
}
@@ -196,10 +191,9 @@ void pa_sconv_s32le_from_float32re(unsigned n, const float *a, int32_t *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);
+ v = PA_FLOAT32_SWAP(v);
+ v = PA_CLAMP_UNLIKELY(v, -1.0f, 1.0f);
+ s = (int32_t) lrint((double) v * 0x7FFFFFFF);
*(b++) = INT32_TO(s);
}
}
@@ -221,7 +215,7 @@ void pa_sconv_s32le_to_s16re(unsigned n, const int32_t*a, int16_t *b) {
for (; n > 0; n--) {
int16_t s = (int16_t) (INT32_FROM(*a) >> 16);
- *b = PA_UINT32_SWAP(s);
+ *b = PA_INT16_SWAP(s);
a++;
b++;
}
@@ -243,7 +237,7 @@ void pa_sconv_s32le_from_s16re(unsigned n, const int16_t *a, int32_t *b) {
pa_assert(b);
for (; n > 0; n--) {
- int32_t s = ((int32_t) PA_UINT16_SWAP(*a)) << 16;
+ int32_t s = ((int32_t) PA_INT16_SWAP(*a)) << 16;
*b = INT32_TO(s);
a++;
b++;
diff --git a/src/pulsecore/sconv-s16le.h b/src/pulsecore/sconv-s16le.h
index aac74c4..8d4c87c 100644
--- a/src/pulsecore/sconv-s16le.h
+++ b/src/pulsecore/sconv-s16le.h
@@ -1,8 +1,6 @@
#ifndef foosconv_s16lefoo
#define foosconv_s16lefoo
-/* $Id: sconv-s16le.h 2037 2007-11-09 02:45:07Z lennart $ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/sconv.c b/src/pulsecore/sconv.c
index ae114f6..6c4d420 100644
--- a/src/pulsecore/sconv.c
+++ b/src/pulsecore/sconv.c
@@ -1,5 +1,3 @@
-/* $Id: sconv.c 2040 2007-11-09 14:20:12Z lennart $ */
-
/***
This file is part of PulseAudio.
@@ -48,7 +46,7 @@ static void u8_to_float32ne(unsigned n, const uint8_t *a, float *b) {
pa_assert(a);
pa_assert(b);
- oil_scaleconv_f32_u8(b, a, n, &add, &factor);
+ oil_scaleconv_f32_u8(b, a, (int) n, &add, &factor);
}
static void u8_from_float32ne(unsigned n, const float *a, uint8_t *b) {
@@ -57,7 +55,7 @@ static void u8_from_float32ne(unsigned n, const float *a, uint8_t *b) {
pa_assert(a);
pa_assert(b);
- oil_scaleconv_u8_f32(b, a, n, &add, &factor);
+ oil_scaleconv_u8_f32(b, a, (int) n, &add, &factor);
}
static void u8_to_s16ne(unsigned n, const uint8_t *a, int16_t *b) {
@@ -66,9 +64,9 @@ static void u8_to_s16ne(unsigned n, const uint8_t *a, int16_t *b) {
pa_assert(a);
pa_assert(b);
- oil_conv_s16_u8(b, 2, a, 1, n);
- oil_scalaradd_s16(b, 2, b, 2, &add, n);
- oil_scalarmult_s16(b, 2, b, 2, &factor, n);
+ oil_conv_s16_u8(b, 2, a, 1, (int) n);
+ oil_scalaradd_s16(b, 2, b, 2, &add, (int) n);
+ oil_scalarmult_s16(b, 2, b, 2, &factor, (int) n);
}
static void u8_from_s16ne(unsigned n, const int16_t *a, uint8_t *b) {
@@ -86,7 +84,7 @@ static void float32ne_to_float32ne(unsigned n, const float *a, float *b) {
pa_assert(a);
pa_assert(b);
- oil_memcpy(b, a, sizeof(float) * n);
+ oil_memcpy(b, a, (int) (sizeof(float) * n));
}
static void float32re_to_float32ne(unsigned n, const float *a, float *b) {
@@ -103,7 +101,7 @@ static void s16ne_to_s16ne(unsigned n, const int16_t *a, int16_t *b) {
pa_assert(a);
pa_assert(b);
- oil_memcpy(b, a, sizeof(int16_t) * n);
+ oil_memcpy(b, a, (int) (sizeof(int16_t) * n));
}
static void s16re_to_s16ne(unsigned n, const int16_t *a, int16_t *b) {
@@ -111,7 +109,7 @@ static void s16re_to_s16ne(unsigned n, const int16_t *a, int16_t *b) {
pa_assert(b);
for (; n > 0; n--, a++, b++)
- *b = PA_UINT16_SWAP(*a);
+ *b = PA_INT16_SWAP(*a);
}
/* ulaw */
@@ -130,9 +128,9 @@ static void ulaw_from_float32ne(unsigned n, const float *a, uint8_t *b) {
for (; n > 0; n--) {
float v = *(a++);
- v = PA_CLAMP_UNLIKELY(v, -1, 1);
+ v = PA_CLAMP_UNLIKELY(v, -1.0f, 1.0f);
v *= 0x1FFF;
- *(b++) = st_14linear2ulaw((int16_t) v);
+ *(b++) = st_14linear2ulaw((int16_t) lrintf(v));
}
}
@@ -168,9 +166,9 @@ static void alaw_from_float32ne(unsigned n, const float *a, uint8_t *b) {
for (; n > 0; n--, a++, b++) {
float v = *a;
- v = PA_CLAMP_UNLIKELY(v, -1, 1);
+ v = PA_CLAMP_UNLIKELY(v, -1.0f, 1.0f);
v *= 0xFFF;
- *b = st_13linear2alaw((int16_t) v);
+ *b = st_13linear2alaw((int16_t) lrintf(v));
}
}
@@ -179,7 +177,7 @@ static void alaw_to_s16ne(unsigned n, const int8_t *a, int16_t *b) {
pa_assert(b);
for (; n > 0; n--, a++, b++)
- *b = st_alaw2linear16(*a);
+ *b = st_alaw2linear16((uint8_t) *a);
}
static void alaw_from_s16ne(unsigned n, const int16_t *a, uint8_t *b) {
diff --git a/src/pulsecore/sconv.h b/src/pulsecore/sconv.h
index f2bdbba..5971036 100644
--- a/src/pulsecore/sconv.h
+++ b/src/pulsecore/sconv.h
@@ -1,8 +1,6 @@
#ifndef foosconvhfoo
#define foosconvhfoo
-/* $Id: sconv.h 1971 2007-10-28 19:13:50Z lennart $ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/semaphore-posix.c b/src/pulsecore/semaphore-posix.c
index 527bcbd..7c9f859 100644
--- a/src/pulsecore/semaphore-posix.c
+++ b/src/pulsecore/semaphore-posix.c
@@ -1,5 +1,3 @@
-/* $Id: semaphore-posix.c 1971 2007-10-28 19:13:50Z lennart $ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/semaphore-win32.c b/src/pulsecore/semaphore-win32.c
index f657634..41e0b8d 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 d1c9968..850ae81 100644
--- a/src/pulsecore/semaphore.h
+++ b/src/pulsecore/semaphore.h
@@ -1,8 +1,6 @@
#ifndef foopulsesemaphorehfoo
#define foopulsesemaphorehfoo
-/* $Id: semaphore.h 1971 2007-10-28 19:13:50Z lennart $ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/props.c b/src/pulsecore/shared.c
index cb7835b..77d919d 100644
--- a/src/pulsecore/props.c
+++ b/src/pulsecore/shared.c
@@ -1,5 +1,3 @@
-/* $Id: props.c 1971 2007-10-28 19:13:50Z lennart $ */
-
/***
This file is part of PulseAudio.
@@ -29,112 +27,112 @@
#include <pulsecore/log.h>
#include <pulsecore/macro.h>
-#include "props.h"
+#include "shared.h"
-typedef struct pa_property {
- char *name; /* Points to memory allocated by the property subsystem */
+typedef struct pa_shared {
+ char *name; /* Points to memory allocated by the shared property system */
void *data; /* Points to memory maintained by the caller */
-} pa_property;
+} pa_shared;
-/* Allocate a new property object */
-static pa_property* property_new(const char *name, void *data) {
- pa_property* p;
+/* Allocate a new shared property object */
+static pa_shared* shared_new(const char *name, void *data) {
+ pa_shared* p;
pa_assert(name);
pa_assert(data);
- p = pa_xnew(pa_property, 1);
+ p = pa_xnew(pa_shared, 1);
p->name = pa_xstrdup(name);
p->data = data;
return p;
}
-/* Free a property object */
-static void property_free(pa_property *p) {
+/* Free a shared property object */
+static void shared_free(pa_shared *p) {
pa_assert(p);
pa_xfree(p->name);
pa_xfree(p);
}
-void* pa_property_get(pa_core *c, const char *name) {
- pa_property *p;
+void* pa_shared_get(pa_core *c, const char *name) {
+ pa_shared *p;
pa_assert(c);
pa_assert(name);
- pa_assert(c->properties);
+ pa_assert(c->shared);
- if (!(p = pa_hashmap_get(c->properties, name)))
+ if (!(p = pa_hashmap_get(c->shared, name)))
return NULL;
return p->data;
}
-int pa_property_set(pa_core *c, const char *name, void *data) {
- pa_property *p;
+int pa_shared_set(pa_core *c, const char *name, void *data) {
+ pa_shared *p;
pa_assert(c);
pa_assert(name);
pa_assert(data);
- pa_assert(c->properties);
+ pa_assert(c->shared);
- if (pa_hashmap_get(c->properties, name))
+ if (pa_hashmap_get(c->shared, name))
return -1;
- p = property_new(name, data);
- pa_hashmap_put(c->properties, p->name, p);
+ p = shared_new(name, data);
+ pa_hashmap_put(c->shared, p->name, p);
return 0;
}
-int pa_property_remove(pa_core *c, const char *name) {
- pa_property *p;
+int pa_shared_remove(pa_core *c, const char *name) {
+ pa_shared *p;
pa_assert(c);
pa_assert(name);
- pa_assert(c->properties);
+ pa_assert(c->shared);
- if (!(p = pa_hashmap_remove(c->properties, name)))
+ if (!(p = pa_hashmap_remove(c->shared, name)))
return -1;
- property_free(p);
+ shared_free(p);
return 0;
}
-void pa_property_init(pa_core *c) {
+void pa_shared_init(pa_core *c) {
pa_assert(c);
- c->properties = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+ c->shared = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
}
-void pa_property_cleanup(pa_core *c) {
+void pa_shared_cleanup(pa_core *c) {
pa_assert(c);
- if (!c->properties)
+ if (!c->shared)
return;
- pa_assert(!pa_hashmap_size(c->properties));
+ pa_assert(pa_hashmap_isempty(c->shared));
- pa_hashmap_free(c->properties, NULL, NULL);
- c->properties = NULL;
+ pa_hashmap_free(c->shared, NULL, NULL);
+ c->shared = NULL;
}
-void pa_property_dump(pa_core *c, pa_strbuf *s) {
+void pa_shared_dump(pa_core *c, pa_strbuf *s) {
void *state = NULL;
- pa_property *p;
+ pa_shared *p;
pa_assert(c);
pa_assert(s);
- while ((p = pa_hashmap_iterate(c->properties, &state, NULL)))
+ while ((p = pa_hashmap_iterate(c->shared, &state, NULL)))
pa_strbuf_printf(s, "[%s] -> [%p]\n", p->name, p->data);
}
-int pa_property_replace(pa_core *c, const char *name, void *data) {
+int pa_shared_replace(pa_core *c, const char *name, void *data) {
pa_assert(c);
pa_assert(name);
- pa_property_remove(c, name);
- return pa_property_set(c, name, data);
+ pa_shared_remove(c, name);
+ return pa_shared_set(c, name, data);
}
diff --git a/src/pulsecore/props.h b/src/pulsecore/shared.h
index 332d095..dd3f94e 100644
--- a/src/pulsecore/props.h
+++ b/src/pulsecore/shared.h
@@ -1,7 +1,5 @@
-#ifndef foopropshfoo
-#define foopropshfoo
-
-/* $Id: props.h 1426 2007-02-13 15:35:19Z ossman $ */
+#ifndef foosharedshfoo
+#define foosharedshfoo
/***
This file is part of PulseAudio.
@@ -27,34 +25,37 @@
#include <pulsecore/core.h>
#include <pulsecore/strbuf.h>
-/* The property subsystem is to be used to share data between
+/* The shared property subsystem is to be used to share data between
* modules. Consider them to be kind of "global" variables for a
* core. Why not use the hashmap functions directly? The hashmap
* functions copy neither the key nor value, while this property
* system copies the key. Users of this system have to think about
* reference counting themselves. */
-/* Return a pointer to the value of the specified property. */
-void* pa_property_get(pa_core *c, const char *name);
+/* Note: please don't confuse this with the proplist framework in
+ * pulse/proplist.[ch]! */
+
+/* Return a pointer to the value of the specified shared property. */
+void* pa_shared_get(pa_core *c, const char *name);
-/* Set the property 'name' to 'data'. This function fails in case a
- * property by this name already exists. The property data is not
- * copied or reference counted. This is the caller's job. */
-int pa_property_set(pa_core *c, const char *name, void *data);
+/* Set the shared property 'name' to 'data'. This function fails in
+ * case a property by this name already exists. The property data is
+ * not copied or reference counted. This is the caller's job. */
+int pa_shared_set(pa_core *c, const char *name, void *data);
-/* Remove the specified property. Return non-zero on failure */
-int pa_property_remove(pa_core *c, const char *name);
+/* Remove the specified shared property. Return non-zero on failure */
+int pa_shared_remove(pa_core *c, const char *name);
-/* A combination of pa_property_remove() and pa_property_set() */
-int pa_property_replace(pa_core *c, const char *name, void *data);
+/* A combination of pa_shared_remove() and pa_shared_set() */
+int pa_shared_replace(pa_core *c, const char *name, void *data);
-/* Free all memory used by the property system */
-void pa_property_cleanup(pa_core *c);
+/* Free all memory used by the shared property system */
+void pa_shared_cleanup(pa_core *c);
-/* Initialize the properties subsystem */
-void pa_property_init(pa_core *c);
+/* Initialize the shared property system */
+void pa_shared_init(pa_core *c);
-/* Dump the current set of properties */
-void pa_property_dump(pa_core *c, pa_strbuf *s);
+/* Dump the current set of shared properties */
+void pa_shared_dump(pa_core *c, pa_strbuf *s);
#endif
diff --git a/src/pulsecore/shm.c b/src/pulsecore/shm.c
index bcc2a5e..b299757 100644
--- a/src/pulsecore/shm.c
+++ b/src/pulsecore/shm.c
@@ -1,5 +1,3 @@
-/* $Id: shm.c 2164 2008-03-27 23:32:57Z lennart $ */
-
/***
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,14 +68,15 @@
#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) {
@@ -84,13 +84,13 @@ static char *segment_name(char *fn, size_t l, unsigned 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
@@ -105,7 +105,7 @@ int pa_shm_create_rw(pa_shm *m, size_t size, int shared, mode_t mode) {
m->size = size;
#ifdef MAP_ANONYMOUS
- if ((m->ptr = mmap(NULL, m->size, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0)) == MAP_FAILED) {
+ if ((m->ptr = mmap(NULL, m->size, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, (off_t) 0)) == MAP_FAILED) {
pa_log("mmap() failed: %s", pa_cstrerror(errno));
goto fail;
}
@@ -122,7 +122,7 @@ 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
@@ -138,12 +138,12 @@ int pa_shm_create_rw(pa_shm *m, size_t size, int shared, mode_t mode) {
m->size = size + PA_ALIGN(sizeof(struct shm_marker));
- if (ftruncate(fd, m->size) < 0) {
+ if (ftruncate(fd, (off_t) m->size) < 0) {
pa_log("ftruncate() failed: %s", pa_cstrerror(errno));
goto fail;
}
- if ((m->ptr = mmap(NULL, m->size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) {
+ if ((m->ptr = mmap(NULL, m->size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, (off_t) 0)) == MAP_FAILED) {
pa_log("mmap() failed: %s", pa_cstrerror(errno));
goto fail;
}
@@ -155,7 +155,7 @@ int pa_shm_create_rw(pa_shm *m, size_t size, int shared, mode_t mode) {
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
@@ -235,7 +235,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);
+ o = (size_t) ((uint8_t*) ptr - (uint8_t*) PA_PAGE_ALIGN_PTR(ptr));
if (o > 0) {
ps = PA_PAGE_SIZE;
@@ -282,14 +282,16 @@ 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;
}
- m->size = st.st_size;
+ m->size = (size_t) st.st_size;
- if ((m->ptr = mmap(NULL, m->size, PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) {
+ if ((m->ptr = mmap(NULL, m->size, PROT_READ, MAP_SHARED, fd, (off_t) 0)) == MAP_FAILED) {
pa_log("mmap() failed: %s", pa_cstrerror(errno));
goto fail;
}
@@ -371,7 +373,7 @@ int pa_shm_cleanup(void) {
/* 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));
}
diff --git a/src/pulsecore/shm.h b/src/pulsecore/shm.h
index 89ccfa2..c2adbd0 100644
--- a/src/pulsecore/shm.h
+++ b/src/pulsecore/shm.h
@@ -1,8 +1,6 @@
#ifndef foopulseshmhfoo
#define foopulseshmhfoo
-/* $Id: shm.h 1971 2007-10-28 19:13:50Z lennart $ */
-
/***
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/sink-input.c b/src/pulsecore/sink-input.c
index fa0f208..4f70347 100644
--- a/src/pulsecore/sink-input.c
+++ b/src/pulsecore/sink-input.c
@@ -1,5 +1,3 @@
-/* $Id: sink-input.c 2159 2008-03-27 23:29:32Z lennart $ */
-
/***
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,
@@ -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);
@@ -132,6 +156,9 @@ pa_sink_input* pa_sink_input_new(
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 (flags & PA_SINK_INPUT_FIX_FORMAT)
data->sample_spec.format = data->sink->sample_spec.format;
@@ -150,9 +177,6 @@ pa_sink_input* pa_sink_input_new(
if (data->volume.channels != data->sample_spec.channels)
pa_cvolume_set(&data->volume, data->sample_spec.channels, pa_cvolume_avg(&data->volume));
- if (!data->muted_is_set)
- data->muted = 0;
-
if (data->resample_method == PA_RESAMPLER_INVALID)
data->resample_method = core->resample_method;
@@ -177,7 +201,8 @@ pa_sink_input* pa_sink_input_new(
data->resample_method,
((flags & PA_SINK_INPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0) |
((flags & PA_SINK_INPUT_NO_REMAP) ? PA_RESAMPLER_NO_REMAP : 0) |
- (core->disable_remixing || (flags & PA_SINK_INPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0)))) {
+ (core->disable_remixing || (flags & PA_SINK_INPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0) |
+ (core->disable_lfe_remixing ? PA_RESAMPLER_NO_LFE : 0)))) {
pa_log_warn("Unsupported resampling operation.");
return NULL;
}
@@ -192,7 +217,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;
@@ -215,33 +240,41 @@ 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->moved = 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 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_channel_map_snprint(cm, sizeof(cm), &i->channel_map));
@@ -251,6 +284,7 @@ pa_sink_input* pa_sink_input_new(
return i;
}
+/* Called from main context */
static void update_n_corked(pa_sink_input *i, pa_sink_input_state_t state) {
pa_assert(i);
@@ -262,6 +296,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);
@@ -272,8 +307,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;
@@ -287,14 +321,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
@@ -302,7 +345,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);
@@ -318,21 +361,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;
- i->moved = 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);
@@ -343,329 +385,431 @@ 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);
- return r;
+ if (sink_latency)
+ *sink_latency = r[1];
+
+ 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);
- 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);
- if ((ret = i->peek(i, l, &tchunk)) < 0)
- goto finish;
+ pa_memblockq_seek(i->thread_info.render_memblockq, (int64_t) 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;
+ }
+
+ 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);
- /* It might be necessary to adjust the volume here */
- if (do_volume_adj_here && !volume_is_norm) {
- pa_memchunk_make_writable(&tchunk, 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);
- 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);
+/* 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);
+ }
+ }
+
+ pa_memblock_unref(wchunk.memblock);
+
+ 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);
-
- 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 */
+ /* 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;
- }
+ pa_memblockq_drop(i->thread_info.render_memblockq, nbytes);
+}
- 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;
- }
- }
+/* 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_bool_t called = FALSE;
+ 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));
- if (length <= 0)
- return;
+/* 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 (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_write(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;
- length -= l;
+ /* Transform into local domain */
+ if (i->thread_info.resampler)
+ max_rewrite = pa_resampler_request(i->thread_info.resampler, max_rewrite);
+
+ /* 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);
+ called = TRUE;
+
+ /* 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) {
+ if (!called)
+ if (i->process_rewind)
+ i->process_rewind(i, 0);
- 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. */
+ i->thread_info.rewrite_nbytes = 0;
+ i->thread_info.rewrite_flush = FALSE;
+}
- while (length > 0) {
- pa_memchunk chunk;
- pa_cvolume volume;
+/* 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));
- if (pa_sink_input_peek(i, length, &chunk, &volume) >= 0) {
- size_t l;
+ pa_memblockq_set_maxrewind(i->thread_info.render_memblockq, nbytes);
- pa_memblock_unref(chunk.memblock);
+ if (i->update_max_rewind)
+ i->update_max_rewind(i, i->thread_info.resampler ? pa_resampler_request(i->thread_info.resampler, nbytes) : nbytes);
+}
- l = chunk.length;
- if (l > length)
- l = length;
+/* 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));
- pa_sink_input_drop(i, l);
- length -= l;
+ if (i->update_max_request)
+ i->update_max_request(i, i->thread_info.resampler ? pa_resampler_request(i->thread_info.resampler, nbytes) : nbytes);
+}
- } else {
- size_t l;
+/* Called from thread context */
+static pa_usec_t fixup_latency(pa_sink *s, pa_usec_t usec) {
+ pa_sink_assert_ref(s);
- l = pa_resampler_request(i->thread_info.resampler, length);
+ if (usec == (pa_usec_t) -1)
+ return usec;
- /* Hmmm, peeking failed, so let's at least drop
- * the right amount of data */
+ if (s->thread_info.max_latency > 0 && usec > s->thread_info.max_latency)
+ usec = s->thread_info.max_latency;
- if (l > 0)
- if (i->drop)
- i->drop(i, l);
+ if (s->thread_info.min_latency > 0 && usec < s->thread_info.min_latency)
+ usec = s->thread_info.min_latency;
- break;
- }
- }
+ return usec;
+}
- } else {
+/* 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);
- /* We have no resampler, hence let's just drop the data */
+ usec = fixup_latency(i->sink, usec);
+ i->thread_info.requested_sink_latency = usec;
+ pa_sink_invalidate_requested_latency(i->sink);
- if (i->drop)
- i->drop(i, length);
- }
- }
+ return usec;
+}
+
+/* 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 */
+
+ i->thread_info.requested_sink_latency = usec;
+
+ return usec;
}
+/* Called from main context */
+pa_usec_t pa_sink_input_get_requested_latency(pa_sink_input *i) {
+ pa_usec_t usec = 0;
+
+ pa_sink_input_assert_ref(i);
+
+ if (PA_SINK_INPUT_IS_LINKED(i->state))
+ 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;
@@ -676,23 +820,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)
@@ -706,39 +853,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;
@@ -759,6 +913,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))
@@ -790,71 +951,7 @@ int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, int immediately) {
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);
@@ -865,42 +962,31 @@ 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);
@@ -914,30 +1000,72 @@ 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_bool_t corking, uncorking;
+ 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);
+
+ corking = state == PA_SINK_INPUT_CORKED && i->thread_info.state == PA_SINK_INPUT_RUNNING;
+ uncorking = i->thread_info.state == PA_SINK_INPUT_CORKED && state == PA_SINK_INPUT_RUNNING;
+
+ if (i->state_change)
+ i->state_change(i, state);
+
+ i->thread_info.state = state;
+
+ if (corking) {
+
+ pa_log_debug("Requesting rewind due to corking");
+
+ /* 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 (uncorking) {
+
+ i->thread_info.underrun_for = (uint64_t) -1;
+ i->thread_info.playing_for = 0;
+
+ pa_log_debug("Requesting rewind due to uncorking");
+
+ /* 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);
+ }
+}
+
+/* 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;
}
@@ -952,26 +1080,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;
}
}
@@ -979,6 +1109,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);
@@ -987,3 +1118,93 @@ 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);
+
+/* pa_log_debug("request rewrite %lu", (unsigned long) nbytes); */
+
+ /* 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);
+ else
+ /* This call will make sure process_rewind() is called later */
+ pa_sink_request_rewind(i->sink, 0);
+}
+
+/* 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 3cbb28c..7663f22 100644
--- a/src/pulsecore/sink-input.h
+++ b/src/pulsecore/sink-input.h
@@ -1,8 +1,6 @@
#ifndef foopulsesinkinputhfoo
#define foopulsesinkinputhfoo
-/* $Id: sink-input.h 2067 2007-11-21 01:30:40Z lennart $ */
-
/***
This file is part of PulseAudio.
@@ -46,7 +44,7 @@ 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;
}
@@ -73,12 +71,19 @@ struct pa_sink_input {
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;
@@ -87,17 +92,38 @@ struct pa_sink_input {
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
@@ -120,15 +146,17 @@ struct pa_sink_input {
/* 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;
@@ -138,19 +166,24 @@ struct pa_sink_input {
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;
@@ -165,41 +198,46 @@ 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;
pa_sink *sink;
+ pa_resample_method_t resample_method;
+
+ pa_sink_input *sync_base;
+
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_bool_t muted:1;
- pa_resample_method_t resample_method;
-
- pa_sink_input *sync_base;
+ pa_bool_t sample_spec_is_set:1;
+ pa_bool_t channel_map_is_set:1;
+ pa_bool_t volume_is_set:1;
+ pa_bool_t muted_is_set:1;
} pa_sink_input_new_data;
-typedef struct pa_sink_input_move_hook_data {
- pa_sink_input *sink_input;
- pa_sink *destination;
-} pa_sink_input_move_hook_data;
-
pa_sink_input_new_data* pa_sink_input_new_data_init(pa_sink_input_new_data *data);
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_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 */
@@ -213,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 732ba76..e04fc08 100644
--- a/src/pulsecore/sink.c
+++ b/src/pulsecore/sink.c
@@ -1,5 +1,3 @@
-/* $Id: sink.c 2159 2008-03-27 23:29:32Z lennart $ */
-
/***
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)
- pa_return_null_if_fail((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,80 +178,114 @@ 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.rewind_requested = FALSE;
+ 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_sink_state_t original_state;
pa_assert(s);
if (s->state == state)
return 0;
+ original_state = s->state;
+
suspend_change =
- (s->state == PA_SINK_SUSPENDED && PA_SINK_OPENED(state)) ||
- (PA_SINK_OPENED(s->state) && state == PA_SINK_SUSPENDED);
+ (original_state == PA_SINK_SUSPENDED && PA_SINK_IS_OPENED(state)) ||
+ (PA_SINK_IS_OPENED(original_state) && state == PA_SINK_SUSPENDED);
if (s->set_state)
if ((ret = s->set_state(s, state)) < 0)
- return -1;
+ return ret;
- if (pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_STATE, PA_UINT_TO_PTR(state), 0, NULL) < 0)
- return -1;
+ if (s->asyncmsgq)
+ if ((ret = pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_STATE, PA_UINT_TO_PTR(state), 0, NULL)) < 0) {
+
+ if (s->set_state)
+ s->set_state(s, original_state);
+
+ return ret;
+ }
s->state = state;
@@ -186,21 +306,34 @@ static int sink_set_state(pa_sink *s, pa_sink_state_t state) {
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;
@@ -215,7 +348,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);
@@ -235,12 +368,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);
@@ -251,6 +379,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;
@@ -258,7 +387,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);
@@ -275,18 +404,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;
@@ -294,18 +426,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;
@@ -313,9 +446,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);
@@ -323,17 +457,35 @@ 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);
+ s->thread_info.rewind_nbytes = 0;
+ s->thread_info.rewind_requested = FALSE;
+
+ if (nbytes > 0)
+ 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 (nbytes > 0)
+ 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);
@@ -341,9 +493,17 @@ 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);
pa_assert(info->chunk.memblock);
@@ -354,27 +514,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 ++) {
@@ -389,14 +554,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;
}
@@ -413,20 +611,27 @@ 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);
+ pa_assert(!s->thread_info.rewind_requested);
+ pa_assert(s->thread_info.rewind_nbytes == 0);
+
if (length <= 0)
length = pa_frame_align(MIX_BUFFER_LENGTH, &s->sample_spec);
@@ -436,24 +641,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;
@@ -478,27 +674,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);
@@ -506,34 +705,49 @@ 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;
+ pa_assert(!s->thread_info.rewind_requested);
+ pa_assert(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;
+ pa_memchunk vchunk;
- ptr = pa_memblock_acquire(target->memblock);
- src = pa_memblock_acquire(info[0].chunk.memblock);
+ vchunk = info[0].chunk;
+ pa_memblock_ref(vchunk.memblock);
- memcpy((uint8_t*) ptr + target->index,
- (uint8_t*) src + info[0].chunk.index,
- target->length);
+ if (vchunk.length > length)
+ vchunk.length = length;
- pa_memblock_release(target->memblock);
- pa_memblock_release(info[0].chunk.memblock);
-
- 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 {
@@ -542,8 +756,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);
@@ -552,20 +765,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);
@@ -573,6 +784,9 @@ void pa_sink_render_into_full(pa_sink *s, pa_memchunk *target) {
pa_sink_ref(s);
+ pa_assert(!s->thread_info.rewind_requested);
+ pa_assert(s->thread_info.rewind_nbytes == 0);
+
l = target->length;
d = 0;
while (l > 0) {
@@ -589,13 +803,17 @@ 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);
+ pa_assert(!s->thread_info.rewind_requested);
+ pa_assert(s->thread_info.rewind_nbytes == 0);
+
/*** This needs optimization ***/
result->index = 0;
@@ -605,62 +823,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));
+ pa_assert(PA_SINK_IS_LINKED(s->state));
- if (!PA_SINK_OPENED(s->state))
- return 0;
-
- 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);
@@ -670,37 +855,50 @@ 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_sink_set_soft_volume(s, volume);
if (changed)
pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
}
-const pa_cvolume *pa_sink_get_volume(pa_sink *s) {
- struct pa_cvolume old_volume;
+/* Called from main thread */
+void pa_sink_set_soft_volume(pa_sink *s, const pa_cvolume *volume) {
+ pa_sink_assert_ref(s);
+ pa_assert(volume);
+ if (PA_SINK_IS_LINKED(s->state))
+ pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_VOLUME, volume, 0, NULL);
+ else
+ s->thread_info.soft_volume = *volume;
+}
+
+/* Called from main thread */
+const pa_cvolume *pa_sink_get_volume(pa_sink *s, pa_bool_t force_refresh) {
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 || force_refresh) {
+ 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;
@@ -715,71 +913,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);
}
-pa_bool_t pa_sink_get_mute(pa_sink *s) {
- pa_bool_t old_muted;
+/* Called from main thread */
+pa_bool_t pa_sink_get_mute(pa_sink *s, pa_bool_t force_refresh) {
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 || force_refresh) {
+ 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);
@@ -792,31 +985,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
@@ -841,9 +1039,21 @@ 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);
+
+ /* The requested latency of the sink input needs to be
+ * fixed up and then configured on the sink */
+
+ if (i->thread_info.requested_sink_latency != (pa_usec_t) -1)
+ pa_sink_input_set_requested_latency_within_thread(i, i->thread_info.requested_sink_latency);
+
+ pa_sink_input_update_max_rewind(i, s->thread_info.max_rewind);
+ pa_sink_input_update_max_request(i, s->thread_info.max_request);
+
+ /* 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;
}
@@ -853,11 +1063,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;
@@ -865,8 +1077,8 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse
* we can safely access data outside of thread_info even
* though it is mutable */
- pa_assert(!i->thread_info.sync_prev);
- pa_assert(!i->thread_info.sync_next);
+ pa_assert(!i->sync_prev);
+ pa_assert(!i->sync_next);
if (i->thread_info.sync_prev) {
i->thread_info.sync_prev->thread_info.sync_next = i->thread_info.sync_prev->sync_next;
@@ -881,82 +1093,95 @@ 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, (size_t) -1);
+
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 (info->sink_input->detach)
- info->sink_input->detach(info->sink_input);
+ if (i->thread_info.state != PA_SINK_INPUT_CORKED) {
+ pa_usec_t usec = 0;
+ size_t sink_nbytes, total_nbytes;
- pa_assert(info->sink_input->thread_info.attached);
- info->sink_input->thread_info.attached = FALSE;
+ /* 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;
- if (info->ghost_sink_input) {
- pa_assert(info->buffer_bytes > 0);
- pa_assert(info->buffer);
+ sink_nbytes = pa_usec_to_bytes(usec, &s->sample_spec);
+ total_nbytes = sink_nbytes + pa_memblockq_get_length(i->thread_info.render_memblockq);
- volume_is_norm = pa_cvolume_is_norm(&info->sink_input->thread_info.volume);
+ 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);
+ }
+ }
- pa_log_debug("Buffering %lu bytes ...", (unsigned long) info->buffer_bytes);
+ if (i->detach)
+ i->detach(i);
- while (info->buffer_bytes > 0) {
- pa_memchunk memchunk;
- pa_cvolume volume;
- size_t n;
+ pa_assert(i->thread_info.attached);
+ i->thread_info.attached = FALSE;
- if (pa_sink_input_peek(info->sink_input, info->buffer_bytes, &memchunk, &volume) < 0)
- break;
+ /* 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);
- n = memchunk.length > info->buffer_bytes ? info->buffer_bytes : memchunk.length;
- pa_sink_input_drop(info->sink_input, n);
- memchunk.length = n;
+ pa_sink_invalidate_requested_latency(s);
- if (!volume_is_norm) {
- pa_memchunk_make_writable(&memchunk, 0);
- pa_volume_memchunk(&memchunk, &s->sample_spec, &volume);
- }
+ pa_log_debug("Requesting rewind due to started move");
+ pa_sink_request_rewind(s, (size_t) -1);
- if (pa_memblockq_push(info->buffer, &memchunk) < 0) {
- pa_memblock_unref(memchunk.memblock);
- break;
- }
+ return 0;
+ }
- pa_memblock_unref(memchunk.memblock);
- info->buffer_bytes -= n;
- }
+ case PA_SINK_MESSAGE_FINISH_MOVE: {
+ pa_sink_input *i = PA_SINK_INPUT(userdata);
- /* 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);
+ /* 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);
- pa_memblockq_sink_input_set_queue(info->ghost_sink_input, info->buffer);
+ pa_hashmap_put(s->thread_info.inputs, PA_UINT32_TO_PTR(i->index), pa_sink_input_ref(i));
- pa_log_debug("Buffered %lu bytes ...", (unsigned long) pa_memblockq_get_length(info->buffer));
- }
+ pa_assert(!i->thread_info.attached);
+ i->thread_info.attached = TRUE;
- /* 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);
+ if (i->attach)
+ i->attach(i);
+
+ if (i->thread_info.requested_sink_latency != (pa_usec_t) -1)
+ pa_sink_input_set_requested_latency_within_thread(i, i->thread_info.requested_sink_latency);
+
+ pa_sink_input_update_max_rewind(i, s->thread_info.max_rewind);
+ pa_sink_input_update_max_request(i, s->thread_info.max_request);
+
+ if (i->thread_info.state != PA_SINK_INPUT_CORKED) {
+ pa_usec_t usec = 0;
+ size_t 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;
- /* .. 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;
@@ -964,10 +1189,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, (size_t) -1);
return 0;
case PA_SINK_MESSAGE_SET_MUTE:
s->thread_info.soft_muted = PA_PTR_TO_UINT(userdata);
+
+ pa_sink_request_rewind(s, (size_t) -1);
return 0;
case PA_SINK_MESSAGE_GET_VOLUME:
@@ -978,9 +1207,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);
@@ -988,17 +1214,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:
@@ -1008,6 +1270,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;
@@ -1021,26 +1284,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)
@@ -1050,12 +1316,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)
@@ -1064,3 +1331,232 @@ 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 == (size_t) -1)
+ nbytes = s->thread_info.max_rewind;
+
+ nbytes = PA_MIN(nbytes, s->thread_info.max_rewind);
+
+ if (s->thread_info.rewind_requested &&
+ nbytes <= s->thread_info.rewind_nbytes)
+ return;
+
+ s->thread_info.rewind_nbytes = nbytes;
+ s->thread_info.rewind_requested = TRUE;
+
+ 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) {
+ 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)) {
+ pa_sink_input *i;
+
+ 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 31eb8cd..672bdd3 100644
--- a/src/pulsecore/sink.h
+++ b/src/pulsecore/sink.h
@@ -1,8 +1,6 @@
#ifndef foopulsesinkhfoo
#define foopulsesinkhfoo
-/* $Id: sink.h 1971 2007-10-28 19:13:50Z lennart $ */
-
/***
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,76 @@ 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_bool_t rewind_requested;
+
+ 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,52 +165,85 @@ 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_proplist *proplist;
+
+ const char *driver;
+ pa_module *module;
+
+ pa_sample_spec sample_spec;
+ pa_channel_map channel_map;
+ pa_cvolume volume;
+ pa_bool_t muted :1;
+
+ pa_bool_t sample_spec_is_set:1;
+ pa_bool_t channel_map_is_set:1;
+ pa_bool_t volume_is_set:1;
+ pa_bool_t muted_is_set:1;
+
+ pa_bool_t namereg_fail:1;
+} 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_soft_volume(pa_sink *s, const pa_cvolume *volume);
+const pa_cvolume *pa_sink_get_volume(pa_sink *sink, pa_bool_t force_refresh);
void pa_sink_set_mute(pa_sink *sink, pa_bool_t mute);
-pa_bool_t pa_sink_get_mute(pa_sink *sink);
+pa_bool_t pa_sink_get_mute(pa_sink *sink, pa_bool_t force_refres);
unsigned pa_sink_linked_by(pa_sink *s); /* Number of connected streams */
unsigned pa_sink_used_by(pa_sink *s); /* Number of connected streams which are not corked */
@@ -178,11 +256,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 7e4b9e5..7e5b186 100644
--- a/src/pulsecore/sioman.c
+++ b/src/pulsecore/sioman.c
@@ -1,5 +1,3 @@
-/* $Id: sioman.c 1971 2007-10-28 19:13:50Z lennart $ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/sioman.h b/src/pulsecore/sioman.h
index 8a30dce..d0cacc9 100644
--- a/src/pulsecore/sioman.h
+++ b/src/pulsecore/sioman.h
@@ -1,8 +1,6 @@
#ifndef foosiomanhfoo
#define foosiomanhfoo
-/* $Id: sioman.h 1426 2007-02-13 15:35:19Z ossman $ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/socket-client.c b/src/pulsecore/socket-client.c
index 579d286..6739eff 100644
--- a/src/pulsecore/socket-client.c
+++ b/src/pulsecore/socket-client.c
@@ -1,5 +1,3 @@
-/* $Id: socket-client.c 1971 2007-10-28 19:13:50Z lennart $ */
-
/***
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);
@@ -188,7 +187,7 @@ static void connect_fixed_cb(pa_mainloop_api *m, pa_defer_event *e, void *userda
do_call(c);
}
-static void connect_io_cb(pa_mainloop_api*m, pa_io_event *e, int fd, PA_GCC_UNUSED pa_io_event_flags_t f, void *userdata) {
+static void connect_io_cb(pa_mainloop_api*m, pa_io_event *e, int fd, pa_io_event_flags_t f, 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,12 +277,13 @@ 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
pa_make_socket_low_delay(c->fd);
- if (do_connect(c, sa, salen) < 0)
+ if (do_connect(c, sa, (socklen_t) salen) < 0)
return -1;
return 0;
@@ -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);
@@ -386,7 +370,7 @@ pa_socket_client* pa_socket_client_new_ipv6(pa_mainloop_api *m, uint8_t address[
#ifdef HAVE_LIBASYNCNS
-static void asyncns_cb(pa_mainloop_api*m, pa_io_event *e, int fd, PA_GCC_UNUSED pa_io_event_flags_t f, void *userdata) {
+static void asyncns_cb(pa_mainloop_api*m, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata) {
pa_socket_client *c = userdata;
struct addrinfo *res = NULL;
int ret;
@@ -453,7 +437,7 @@ static void start_timeout(pa_socket_client *c) {
pa_assert(!c->timeout_event);
pa_gettimeofday(&tv);
- pa_timeval_add(&tv, CONNECT_TIMEOUT * 1000000);
+ pa_timeval_add(&tv, CONNECT_TIMEOUT * PA_USEC_PER_SEC);
c->timeout_event = c->mainloop->time_new(c->mainloop, &tv, timeout_cb, c);
}
@@ -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 4befad0..9ceeadd 100644
--- a/src/pulsecore/socket-client.h
+++ b/src/pulsecore/socket-client.h
@@ -1,8 +1,6 @@
#ifndef foosocketclienthfoo
#define foosocketclienthfoo
-/* $Id: socket-client.h 1426 2007-02-13 15:35:19Z ossman $ */
-
/***
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 1b9dd64..a600e0a 100644
--- a/src/pulsecore/socket-server.c
+++ b/src/pulsecore/socket-server.c
@@ -1,5 +1,3 @@
-/* $Id: socket-server.c 1971 2007-10-28 19:13:50Z lennart $ */
-
/***
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,7 +89,7 @@ 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;
@@ -195,13 +193,13 @@ 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);
- if (bind(fd, (struct sockaddr*) &sa, SUN_LEN(&sa)) < 0) {
+ if (bind(fd, (struct sockaddr*) &sa, (socklen_t) SUN_LEN(&sa)) < 0) {
pa_log("bind(): %s", pa_cstrerror(errno));
goto fail;
}
@@ -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 eb40d53..1edfb43 100644
--- a/src/pulsecore/socket-server.h
+++ b/src/pulsecore/socket-server.h
@@ -1,8 +1,6 @@
#ifndef foosocketserverhfoo
#define foosocketserverhfoo
-/* $Id: socket-server.h 1426 2007-02-13 15:35:19Z ossman $ */
-
/***
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 67d7d8b..f721f69 100644
--- a/src/pulsecore/socket-util.c
+++ b/src/pulsecore/socket-util.c
@@ -1,5 +1,3 @@
-/* $Id: socket-util.c 1971 2007-10-28 19:13:50Z lennart $ */
-
/***
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;
@@ -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 411a927..7a40285 100644
--- a/src/pulsecore/socket-util.h
+++ b/src/pulsecore/socket-util.h
@@ -1,8 +1,6 @@
#ifndef foosocketutilhfoo
#define foosocketutilhfoo
-/* $Id: socket-util.h 1971 2007-10-28 19:13:50Z lennart $ */
-
/***
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 bd511c7..c30c16e 100644
--- a/src/pulsecore/sound-file-stream.c
+++ b/src/pulsecore/sound-file-stream.c
@@ -1,9 +1,7 @@
-/* $Id: sound-file-stream.c 1971 2007-10-28 19:13:50Z lennart $ */
-
/***
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,6 +67,7 @@ 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);
@@ -69,7 +75,6 @@ static void file_stream_unlink(file_stream *u) {
return;
pa_sink_input_unlink(u->sink_input);
-
pa_sink_input_unref(u->sink_input);
u->sink_input = NULL;
@@ -77,14 +82,13 @@ static void file_stream_unlink(file_stream *u) {
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,6 +96,7 @@ 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);
@@ -105,117 +110,122 @@ 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);
+ u = FILE_STREAM(i->userdata);
+ file_stream_assert_ref(u);
- file_stream_unlink(FILE_STREAM(i->userdata));
+ 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_assert(chunk);
+ pa_sink_input_assert_ref(i);
u = FILE_STREAM(i->userdata);
file_stream_assert_ref(u);
- if (!u->sndfile)
- return -1;
-
- for (;;) {
+ /* 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);
+}
- if (!u->memchunk.memblock) {
+/* Called from IO thread context */
+static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) {
+ file_stream *u;
- u->memchunk.memblock = pa_memblock_new(i->sink->core->mempool, length);
- u->memchunk.index = 0;
+ pa_sink_input_assert_ref(i);
+ pa_assert(chunk);
+ u = FILE_STREAM(i->userdata);
+ file_stream_assert_ref(u);
- if (u->readf_function) {
- sf_count_t n;
- void *p;
- size_t fs = pa_frame_size(&i->sample_spec);
+ if (!u->memblockq)
+ return -1;
- p = pa_memblock_acquire(u->memchunk.memblock);
- n = u->readf_function(u->sndfile, p, length/fs);
- pa_memblock_release(u->memchunk.memblock);
+ for (;;) {
+ 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 (n <= 0)
- n = 0;
+ if (!u->sndfile)
+ break;
- u->memchunk.length = n * fs;
- } else {
- sf_count_t n;
- void *p;
+ tchunk.memblock = pa_memblock_new(i->sink->core->mempool, length);
+ tchunk.index = 0;
- p = pa_memblock_acquire(u->memchunk.memblock);
- n = sf_read_raw(u->sndfile, p, length);
- pa_memblock_release(u->memchunk.memblock);
+ p = pa_memblock_acquire(tchunk.memblock);
- if (n <= 0)
- n = 0;
+ if (u->readf_function) {
+ fs = pa_frame_size(&i->sample_spec);
+ n = u->readf_function(u->sndfile, p, (sf_count_t) (length/fs));
+ } else {
+ fs = 1;
+ n = sf_read_raw(u->sndfile, p, (sf_count_t) length);
+ }
- u->memchunk.length = n;
- }
+ pa_memblock_release(tchunk.memblock);
- if (u->memchunk.length <= 0) {
+ if (n <= 0) {
+ pa_memblock_unref(tchunk.memblock);
- pa_memblock_unref(u->memchunk.memblock);
- pa_memchunk_reset(&u->memchunk);
+ sf_close(u->sndfile);
+ u->sndfile = NULL;
+ break;
+ }
- pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(u), FILE_STREAM_MESSAGE_UNLINK, NULL, 0, NULL, NULL);
+ tchunk.length = (size_t) n * fs;
- sf_close(u->sndfile);
- u->sndfile = NULL;
+ pa_memblockq_push(u->memblockq, &tchunk);
+ pa_memblock_unref(tchunk.memblock);
+ }
- return -1;
- }
- }
+ if (pa_sink_input_safe_to_remove(i)) {
+ pa_memblockq_free(u->memblockq);
+ u->memblockq = NULL;
- pa_assert(u->memchunk.memblock);
- pa_assert(u->memchunk.length > 0);
+ pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(u), FILE_STREAM_MESSAGE_UNLINK, NULL, 0, NULL, NULL);
+ }
- if (u->drop < u->memchunk.length) {
- u->memchunk.index += u->drop;
- u->memchunk.length -= u->drop;
- u->drop = 0;
- break;
- }
+ return -1;
+ }
- u->drop -= u->memchunk.length;
- pa_memblock_unref(u->memchunk.memblock);
- pa_memchunk_reset(&u->memchunk);
- }
+static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
+ file_stream *u;
- *chunk = u->memchunk;
- pa_memblock_ref(chunk->memblock);
+ pa_sink_input_assert_ref(i);
+ u = FILE_STREAM(i->userdata);
+ file_stream_assert_ref(u);
- pa_assert(chunk->length > 0);
- pa_assert(u->drop <= 0);
+ if (!u->memblockq)
+ return;
- return 0;
+ 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;
- }
-
- length -= u->memchunk.length;
- pa_memblock_unref(u->memchunk.memblock);
- pa_memchunk_reset(&u->memchunk);
- }
+ if (!u->memblockq)
+ return;
- u->drop += length;
+ pa_memblockq_set_maxrewind(u->memblockq, nbytes);
}
int pa_play_file(
@@ -237,10 +247,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));
@@ -301,8 +310,8 @@ int pa_play_file(
break;
}
- ss.rate = sfinfo.samplerate;
- ss.channels = sfinfo.channels;
+ ss.rate = (uint32_t) sfinfo.samplerate;
+ ss.channels = (uint8_t) sfinfo.channels;
if (!pa_sample_spec_valid(&ss)) {
pa_log("Unsupported sample format in file %s", fname);
@@ -312,18 +321,26 @@ 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);
- if (!(u->sink_input = pa_sink_input_new(sink->core, &data, 0)))
+ u->sink_input = pa_sink_input_new(sink->core, &data, 0);
+ pa_sink_input_new_data_done(&data);
+
+ 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
diff --git a/src/pulsecore/sound-file-stream.h b/src/pulsecore/sound-file-stream.h
index 928056a..4cc6914 100644
--- a/src/pulsecore/sound-file-stream.h
+++ b/src/pulsecore/sound-file-stream.h
@@ -1,8 +1,6 @@
#ifndef foosoundfilestreamhfoo
#define foosoundfilestreamhfoo
-/* $Id: sound-file-stream.h 1426 2007-02-13 15:35:19Z ossman $ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/sound-file.c b/src/pulsecore/sound-file.c
index 24f0ca1..380cef1 100644
--- a/src/pulsecore/sound-file.c
+++ b/src/pulsecore/sound-file.c
@@ -1,5 +1,3 @@
-/* $Id: sound-file.c 2159 2008-03-27 23:29:32Z lennart $ */
-
/***
This file is part of PulseAudio.
@@ -91,7 +89,7 @@ int pa_sound_file_load(
case SF_FORMAT_PCM_U8:
case SF_FORMAT_PCM_S8:
ss->format = PA_SAMPLE_S16NE;
- readf_function = (sf_count_t (*)(SNDFILE *sndfile, void *ptr, sf_count_t frames)) sf_readf_short;
+ readf_function = (sf_count_t (*)(SNDFILE *sndfile, void *_ptr, sf_count_t frames)) sf_readf_short;
break;
case SF_FORMAT_ULAW:
@@ -106,12 +104,12 @@ int pa_sound_file_load(
case SF_FORMAT_DOUBLE:
default:
ss->format = PA_SAMPLE_FLOAT32NE;
- readf_function = (sf_count_t (*)(SNDFILE *sndfile, void *ptr, sf_count_t frames)) sf_readf_float;
+ readf_function = (sf_count_t (*)(SNDFILE *sndfile, void *_ptr, sf_count_t frames)) sf_readf_float;
break;
}
- ss->rate = sfinfo.samplerate;
- ss->channels = sfinfo.channels;
+ ss->rate = (uint32_t) sfinfo.samplerate;
+ ss->channels = (uint8_t) sfinfo.channels;
if (!pa_sample_spec_valid(ss)) {
pa_log("Unsupported sample format in file %s", fname);
@@ -119,12 +117,9 @@ int pa_sound_file_load(
}
if (map)
- if (!pa_channel_map_init_auto(map, ss->channels, PA_CHANNEL_MAP_DEFAULT)) {
- pa_log("Unsupported channel map in file %s", fname);
- goto finish;
- }
+ 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) {
+ if ((l = pa_frame_size(ss) * (size_t) sfinfo.frames) > PA_SCACHE_ENTRY_SIZE_MAX) {
pa_log("File too large");
goto finish;
}
@@ -136,7 +131,7 @@ int pa_sound_file_load(
ptr = pa_memblock_acquire(chunk->memblock);
if ((readf_function && readf_function(sf, ptr, sfinfo.frames) != sfinfo.frames) ||
- (!readf_function && sf_read_raw(sf, ptr, l) != (sf_count_t) l)) {
+ (!readf_function && sf_read_raw(sf, ptr, (sf_count_t) l) != (sf_count_t) l)) {
pa_log("Premature file end");
goto finish;
}
@@ -194,15 +189,15 @@ int pa_sound_file_too_big_to_cache(const char *fname) {
break;
}
- ss.rate = sfinfo.samplerate;
- ss.channels = sfinfo.channels;
+ ss.rate = (uint32_t) sfinfo.samplerate;
+ ss.channels = (uint8_t) sfinfo.channels;
if (!pa_sample_spec_valid(&ss)) {
pa_log("Unsupported sample format in file %s", fname);
return -1;
}
- if ((pa_frame_size(&ss) * sfinfo.frames) > PA_SCACHE_ENTRY_SIZE_MAX) {
+ if ((pa_frame_size(&ss) * (size_t) 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 9f7a8cf..e4d703d 100644
--- a/src/pulsecore/sound-file.h
+++ b/src/pulsecore/sound-file.h
@@ -1,8 +1,6 @@
#ifndef soundfilehfoo
#define soundfilehfoo
-/* $Id: sound-file.h 1426 2007-02-13 15:35:19Z ossman $ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c
index 5892d06..d76f6e4 100644
--- a/src/pulsecore/source-output.c
+++ b/src/pulsecore/source-output.c
@@ -1,5 +1,3 @@
-/* $Id: source-output.c 2159 2008-03-27 23:29:32Z lennart $ */
-
/***
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,
@@ -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;
@@ -141,7 +171,8 @@ pa_source_output* pa_source_output_new(
data->resample_method,
((flags & PA_SOURCE_OUTPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0) |
((flags & PA_SOURCE_OUTPUT_NO_REMAP) ? PA_RESAMPLER_NO_REMAP : 0) |
- (core->disable_remixing || (flags & PA_SOURCE_OUTPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0)))) {
+ (core->disable_remixing || (flags & PA_SOURCE_OUTPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0) |
+ (core->disable_lfe_remixing ? PA_RESAMPLER_NO_LFE : 0)))) {
pa_log_warn("Unsupported resampling operation.");
return NULL;
}
@@ -156,7 +187,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;
@@ -166,26 +197,37 @@ 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->moved = 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);
+ 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_channel_map_snprint(cm, sizeof(cm), &o->channel_map));
@@ -195,22 +237,28 @@ pa_source_output* pa_source_output_new(
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)
@@ -219,6 +267,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);
@@ -228,29 +277,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;
- o->moved = 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);
@@ -261,106 +306,249 @@ 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, (int64_t) 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 source 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 source output is not realized yet, we have to touch
+ * the thread info data directly */
+
+ o->thread_info.requested_source_latency = usec;
+
+ return usec;
+}
+
+/* Called from main context */
+pa_usec_t pa_source_output_get_requested_latency(pa_source_output *o) {
+ pa_usec_t usec = 0;
+
+ pa_source_output_assert_ref(o);
+
+ if (PA_SOURCE_OUTPUT_IS_LINKED(o->state))
+ pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_GET_REQUESTED_LATENCY, &usec, 0, NULL) == 0);
+ else
+ /* If this source output is not realized yet, we have to touch
+ * the thread info data directly */
+ usec = o->thread_info.requested_source_latency;
+
+ return usec;
}
+/* 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)
@@ -374,37 +562,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;
@@ -415,6 +611,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;
@@ -444,14 +643,15 @@ int pa_source_output_move_to(pa_source_output *o, pa_source *dest) {
pa_log_warn("Unsupported resampling operation.");
return -1;
}
- }
+ } else
+ new_resampler = NULL;
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);
@@ -467,13 +667,25 @@ 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);
@@ -487,26 +699,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_SOURCE_OUTPUT_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 52de1c6..a7aac81 100644
--- a/src/pulsecore/source-output.h
+++ b/src/pulsecore/source-output.h
@@ -1,8 +1,6 @@
#ifndef foopulsesourceoutputhfoo
#define foopulsesourceoutputhfoo
-/* $Id: source-output.h 2067 2007-11-21 01:30:40Z lennart $ */
-
/***
This file is part of PulseAudio.
@@ -42,7 +40,7 @@ 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;
}
@@ -62,21 +60,45 @@ 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 */
@@ -87,24 +109,26 @@ struct pa_source_output {
void (*detach) (pa_source_output *o); /* may be NULL */
/* If non-NULL called whenever the the source this output is attached
- * to changes. Called from main context */
- void (*moved) (pa_source_output *o); /* may be NULL */
-
- /* If non-NULL called whenever the the source this output is attached
* to suspends or resumes. Called from main context */
void (*suspend) (pa_source_output *o, pa_bool_t b); /* may be NULL */
+ /* If non-NULL called whenever the the source this output is attached
+ * to changes. Called from main context */
+ void (*moved) (pa_source_output *o); /* may be NULL */
+
/* 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;
@@ -114,6 +138,15 @@ struct pa_source_output {
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;
@@ -126,34 +159,40 @@ 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;
pa_source *source;
+ pa_resample_method_t resample_method;
+
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_resample_method_t resample_method;
+ pa_bool_t sample_spec_is_set:1;
+ pa_bool_t channel_map_is_set:1;
} 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_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;
-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);
-
/* To be called by the implementing module only */
pa_source_output* pa_source_output_new(
@@ -166,16 +205,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);
@@ -183,9 +224,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 c691eea..edbbf01 100644
--- a/src/pulsecore/source.c
+++ b/src/pulsecore/source.c
@@ -1,5 +1,3 @@
-/* $Id: source.c 2159 2008-03-27 23:29:32Z lennart $ */
-
/***
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);
- pa_return_null_if_fail(pa_sample_spec_valid(spec));
+ s = pa_msgobject_new(pa_source);
- if (!map)
- pa_return_null_if_fail(map = pa_channel_map_init_auto(&tmap, spec->channels, PA_CHANNEL_MAP_DEFAULT));
+ if (!(name = pa_namereg_register(core, data->name, PA_NAMEREG_SOURCE, 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(pa_utf8_valid(name) && *name);
+ pa_source_new_data_set_name(data, name);
- s = pa_msgobject_new(pa_source);
+ if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SOURCE_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));
- 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,66 +169,86 @@ 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_source_state_t original_state;
pa_assert(s);
if (s->state == state)
return 0;
+ original_state = s->state;
+
suspend_change =
- (s->state == PA_SOURCE_SUSPENDED && PA_SOURCE_OPENED(state)) ||
- (PA_SOURCE_OPENED(s->state) && state == PA_SOURCE_SUSPENDED);
+ (original_state == PA_SOURCE_SUSPENDED && PA_SOURCE_IS_OPENED(state)) ||
+ (PA_SOURCE_IS_OPENED(original_state) && state == PA_SOURCE_SUSPENDED);
if (s->set_state)
if ((ret = s->set_state(s, state)) < 0)
- return -1;
+ return ret;
+
+ if (s->asyncmsgq)
+ if ((ret = pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_STATE, PA_UINT_TO_PTR(state), 0, NULL)) < 0) {
- if (pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_STATE, PA_UINT_TO_PTR(state), 0, NULL) < 0)
- return -1;
+ if (s->set_state)
+ s->set_state(s, original_state);
+
+ return ret;
+ }
s->state = state;
@@ -154,7 +260,7 @@ static int source_set_state(pa_source *s, pa_source_state_t state) {
for (o = PA_SOURCE_OUTPUT(pa_idxset_first(s->outputs, &idx)); o; o = PA_SOURCE_OUTPUT(pa_idxset_next(s->outputs, &idx)))
if (o->suspend)
- o->suspend(o, state == PA_SINK_SUSPENDED);
+ o->suspend(o, state == PA_SOURCE_SUSPENDED);
}
if (state != PA_SOURCE_UNLINKED) /* if we enter UNLINKED state pa_source_unlink() will fire the apropriate events */
@@ -163,19 +269,32 @@ static int source_set_state(pa_source *s, pa_source_state_t state) {
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);
+ pa_assert(s->state == PA_SOURCE_INIT);
+
+ /* The following fields must be initialized properly when calling _put() */
pa_assert(s->asyncmsgq);
+ pa_assert(s->rtpoll);
+ pa_assert(!s->thread_info.min_latency || !s->thread_info.max_latency ||
+ 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;
@@ -185,7 +304,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);
@@ -205,12 +324,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);
@@ -218,6 +332,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);
@@ -225,7 +340,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);
@@ -237,15 +352,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;
@@ -253,9 +389,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);
@@ -263,19 +400,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_log_debug("Processing rewind...");
- pa_asyncmsgq_post(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_PING, NULL, 0, NULL, NULL);
+ 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)
@@ -292,40 +442,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);
@@ -335,37 +520,50 @@ 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_source_set_soft_volume(s, volume);
if (changed)
pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
}
-const pa_cvolume *pa_source_get_volume(pa_source *s) {
- pa_cvolume old_volume;
+/* Called from main thread */
+void pa_source_set_soft_volume(pa_source *s, const pa_cvolume *volume) {
+ pa_source_assert_ref(s);
+ pa_assert(volume);
+ if (PA_SOURCE_IS_LINKED(s->state))
+ pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_VOLUME, volume, 0, NULL);
+ else
+ s->thread_info.soft_volume = *volume;
+}
+
+/* Called from main thread */
+const pa_cvolume *pa_source_get_volume(pa_source *s, pa_bool_t force_refresh) {
pa_source_assert_ref(s);
- pa_assert(PA_SOURCE_LINKED(s->state));
+ pa_assert(PA_SOURCE_IS_LINKED(s->state));
- old_volume = s->volume;
+ if (s->refresh_volume || force_refresh) {
+ 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;
@@ -380,81 +578,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);
}
-pa_bool_t pa_source_get_mute(pa_source *s) {
- pa_bool_t old_muted;
+/* Called from main thread */
+pa_bool_t pa_source_get_mute(pa_source *s, pa_bool_t force_refresh) {
pa_source_assert_ref(s);
- pa_assert(PA_SOURCE_LINKED(s->state));
+ pa_assert(PA_SOURCE_IS_LINKED(s->state));
- old_muted = s->muted;
+ if (s->refresh_muted || force_refresh) {
+ 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);
@@ -462,37 +645,65 @@ 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);
+
+ if (o->thread_info.requested_source_latency != (pa_usec_t) -1)
+ pa_source_output_set_requested_latency_within_thread(o, o->thread_info.requested_source_latency);
+
+ 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;
}
@@ -512,28 +723,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:
;
}
@@ -541,6 +789,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;
@@ -554,41 +803,207 @@ 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)))
+ if (o->update_source_requested_latency)
+ 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_SOURCE_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 7310a65..cae7869 100644
--- a/src/pulsecore/source.h
+++ b/src/pulsecore/source.h
@@ -1,8 +1,6 @@
#ifndef foopulsesourcehfoo
#define foopulsesourcehfoo
-/* $Id: source.h 1971 2007-10-28 19:13:50Z lennart $ */
-
/***
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,48 +156,81 @@ 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_proplist *proplist;
+
+ const char *driver;
+ pa_module *module;
+
+ pa_sample_spec sample_spec;
+ pa_channel_map channel_map;
+ pa_cvolume volume;
+ pa_bool_t muted:1;
+
+ pa_bool_t volume_is_set:1;
+ pa_bool_t muted_is_set:1;
+ pa_bool_t sample_spec_is_set:1;
+ pa_bool_t channel_map_is_set:1;
+
+ pa_bool_t namereg_fail:1;
+} 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_soft_volume(pa_source *s, const pa_cvolume *volume);
+const pa_cvolume *pa_source_get_volume(pa_source *source, pa_bool_t force_refresh);
void pa_source_set_mute(pa_source *source, pa_bool_t mute);
-pa_bool_t pa_source_get_mute(pa_source *source);
+pa_bool_t pa_source_get_mute(pa_source *source, pa_bool_t force_refresh);
unsigned pa_source_linked_by(pa_source *s); /* Number of connected streams */
unsigned pa_source_used_by(pa_source *s); /* Number of connected streams that are not corked */
@@ -169,11 +238,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
deleted file mode 100644
index 9987c8f..0000000
--- a/src/pulsecore/speex/arch.h
+++ /dev/null
@@ -1,241 +0,0 @@
-/* Copyright (C) 2003 Jean-Marc Valin */
-/**
- @file arch.h
- @brief Various architecture definitions Speex
-*/
-/*
- 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
- A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
- CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-*/
-
-#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
-
-#define ABS(x) ((x) < 0 ? (-(x)) : (x)) /**< Absolute integer value. */
-#define ABS16(x) ((x) < 0 ? (-(x)) : (x)) /**< Absolute 16-bit value. */
-#define MIN16(a,b) ((a) < (b) ? (a) : (b)) /**< Maximum 16-bit value. */
-#define MAX16(a,b) ((a) > (b) ? (a) : (b)) /**< Maximum 16-bit value. */
-#define ABS32(x) ((x) < 0 ? (-(x)) : (x)) /**< Absolute 32-bit value. */
-#define MIN32(a,b) ((a) < (b) ? (a) : (b)) /**< Maximum 32-bit value. */
-#define MAX32(a,b) ((a) > (b) ? (a) : (b)) /**< Maximum 32-bit value. */
-
-#ifdef FIXED_POINT
-
-typedef spx_int16_t spx_word16_t;
-typedef spx_int32_t spx_word32_t;
-typedef spx_word32_t spx_mem_t;
-typedef spx_word16_t spx_coef_t;
-typedef spx_word16_t spx_lsp_t;
-typedef spx_word32_t spx_sig_t;
-
-#define Q15ONE 32767
-
-#define LPC_SCALING 8192
-#define SIG_SCALING 16384
-#define LSP_SCALING 8192.
-#define GAMMA_SCALING 32768.
-#define GAIN_SCALING 64
-#define GAIN_SCALING_1 0.015625
-
-#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)
-#define VERY_LARGE16 ((spx_word16_t)32767)
-#define Q15_ONE ((spx_word16_t)32767)
-
-
-#ifdef FIXED_DEBUG
-#include "fixed_debug.h"
-#else
-
-#include "fixed_generic.h"
-
-#ifdef ARM5E_ASM
-#include "fixed_arm5e.h"
-#elif defined (ARM4_ASM)
-#include "fixed_arm4.h"
-#elif defined (ARM5E_ASM)
-#include "fixed_arm5e.h"
-#elif defined (BFIN_ASM)
-#include "fixed_bfin.h"
-#endif
-
-#endif
-
-
-#else
-
-typedef float spx_mem_t;
-typedef float spx_coef_t;
-typedef float spx_lsp_t;
-typedef float spx_sig_t;
-typedef float spx_word16_t;
-typedef float spx_word32_t;
-
-#define Q15ONE 1.0f
-#define LPC_SCALING 1.f
-#define SIG_SCALING 1.f
-#define LSP_SCALING 1.f
-#define GAMMA_SCALING 1.f
-#define GAIN_SCALING 1.f
-#define GAIN_SCALING_1 1.f
-
-
-#define VERY_SMALL 1e-15f
-#define VERY_LARGE32 1e15f
-#define VERY_LARGE16 1e15f
-#define Q15_ONE ((spx_word16_t)1.f)
-
-#define QCONST16(x,bits) (x)
-#define QCONST32(x,bits) (x)
-
-#define NEG16(x) (-(x))
-#define NEG32(x) (-(x))
-#define EXTRACT16(x) (x)
-#define EXTEND32(x) (x)
-#define SHR16(a,shift) (a)
-#define SHL16(a,shift) (a)
-#define SHR32(a,shift) (a)
-#define SHL32(a,shift) (a)
-#define PSHR16(a,shift) (a)
-#define PSHR32(a,shift) (a)
-#define VSHR32(a,shift) (a)
-#define SATURATE16(x,a) (x)
-#define SATURATE32(x,a) (x)
-
-#define PSHR(a,shift) (a)
-#define SHR(a,shift) (a)
-#define SHL(a,shift) (a)
-#define SATURATE(x,a) (x)
-
-#define ADD16(a,b) ((a)+(b))
-#define SUB16(a,b) ((a)-(b))
-#define ADD32(a,b) ((a)+(b))
-#define SUB32(a,b) ((a)-(b))
-#define MULT16_16_16(a,b) ((a)*(b))
-#define MULT16_16(a,b) ((spx_word32_t)(a)*(spx_word32_t)(b))
-#define MAC16_16(c,a,b) ((c)+(spx_word32_t)(a)*(spx_word32_t)(b))
-
-#define MULT16_32_Q11(a,b) ((a)*(b))
-#define MULT16_32_Q13(a,b) ((a)*(b))
-#define MULT16_32_Q14(a,b) ((a)*(b))
-#define MULT16_32_Q15(a,b) ((a)*(b))
-#define MULT16_32_P15(a,b) ((a)*(b))
-
-#define MAC16_32_Q11(c,a,b) ((c)+(a)*(b))
-#define MAC16_32_Q15(c,a,b) ((c)+(a)*(b))
-
-#define MAC16_16_Q11(c,a,b) ((c)+(a)*(b))
-#define MAC16_16_Q13(c,a,b) ((c)+(a)*(b))
-#define MAC16_16_P13(c,a,b) ((c)+(a)*(b))
-#define MULT16_16_Q11_32(a,b) ((a)*(b))
-#define MULT16_16_Q13(a,b) ((a)*(b))
-#define MULT16_16_Q14(a,b) ((a)*(b))
-#define MULT16_16_Q15(a,b) ((a)*(b))
-#define MULT16_16_P15(a,b) ((a)*(b))
-#define MULT16_16_P13(a,b) ((a)*(b))
-#define MULT16_16_P14(a,b) ((a)*(b))
-
-#define DIV32_16(a,b) (((spx_word32_t)(a))/(spx_word16_t)(b))
-#define PDIV32_16(a,b) (((spx_word32_t)(a))/(spx_word16_t)(b))
-#define DIV32(a,b) (((spx_word32_t)(a))/(spx_word32_t)(b))
-#define PDIV32(a,b) (((spx_word32_t)(a))/(spx_word32_t)(b))
-
-
-#endif
-
-
-#if defined (CONFIG_TI_C54X) || defined (CONFIG_TI_C55X)
-
-/* 2 on TI C5x DSP */
-#define BYTES_PER_CHAR 2
-#define BITS_PER_CHAR 16
-#define LOG2_BITS_PER_CHAR 4
-
-#else
-
-#define BYTES_PER_CHAR 1
-#define BITS_PER_CHAR 8
-#define LOG2_BITS_PER_CHAR 3
-
-#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
deleted file mode 100644
index 547e22c..0000000
--- a/src/pulsecore/speex/fixed_generic.h
+++ /dev/null
@@ -1,106 +0,0 @@
-/* Copyright (C) 2003 Jean-Marc Valin */
-/**
- @file fixed_generic.h
- @brief Generic fixed-point operations
-*/
-/*
- 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
- A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
- CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-*/
-
-#ifndef FIXED_GENERIC_H
-#define FIXED_GENERIC_H
-
-#define QCONST16(x,bits) ((spx_word16_t)(.5+(x)*(((spx_word32_t)1)<<(bits))))
-#define QCONST32(x,bits) ((spx_word32_t)(.5+(x)*(((spx_word32_t)1)<<(bits))))
-
-#define NEG16(x) (-(x))
-#define NEG32(x) (-(x))
-#define EXTRACT16(x) ((spx_word16_t)(x))
-#define EXTEND32(x) ((spx_word32_t)(x))
-#define SHR16(a,shift) ((a) >> (shift))
-#define SHL16(a,shift) ((a) << (shift))
-#define SHR32(a,shift) ((a) >> (shift))
-#define SHL32(a,shift) ((a) << (shift))
-#define PSHR16(a,shift) (SHR16((a)+((1<<((shift))>>1)),shift))
-#define PSHR32(a,shift) (SHR32((a)+((1<<((shift))>>1)),shift))
-#define VSHR32(a, shift) (((shift)>0) ? SHR32(a, shift) : SHL32(a, -(shift)))
-#define SATURATE16(x,a) (((x)>(a) ? (a) : (x)<-(a) ? -(a) : (x)))
-#define SATURATE32(x,a) (((x)>(a) ? (a) : (x)<-(a) ? -(a) : (x)))
-
-#define SHR(a,shift) ((a) >> (shift))
-#define SHL(a,shift) ((spx_word32_t)(a) << (shift))
-#define PSHR(a,shift) (SHR((a)+((1<<((shift))>>1)),shift))
-#define SATURATE(x,a) (((x)>(a) ? (a) : (x)<-(a) ? -(a) : (x)))
-
-
-#define ADD16(a,b) ((spx_word16_t)((spx_word16_t)(a)+(spx_word16_t)(b)))
-#define SUB16(a,b) ((spx_word16_t)(a)-(spx_word16_t)(b))
-#define ADD32(a,b) ((spx_word32_t)(a)+(spx_word32_t)(b))
-#define SUB32(a,b) ((spx_word32_t)(a)-(spx_word32_t)(b))
-
-
-/* result fits in 16 bits */
-#define MULT16_16_16(a,b) ((((spx_word16_t)(a))*((spx_word16_t)(b))))
-
-/* (spx_word32_t)(spx_word16_t) gives TI compiler a hint that it's 16x16->32 multiply */
-#define MULT16_16(a,b) (((spx_word32_t)(spx_word16_t)(a))*((spx_word32_t)(spx_word16_t)(b)))
-
-#define MAC16_16(c,a,b) (ADD32((c),MULT16_16((a),(b))))
-#define MULT16_32_Q12(a,b) ADD32(MULT16_16((a),SHR((b),12)), SHR(MULT16_16((a),((b)&0x00000fff)),12))
-#define MULT16_32_Q13(a,b) ADD32(MULT16_16((a),SHR((b),13)), SHR(MULT16_16((a),((b)&0x00001fff)),13))
-#define MULT16_32_Q14(a,b) ADD32(MULT16_16((a),SHR((b),14)), SHR(MULT16_16((a),((b)&0x00003fff)),14))
-
-#define MULT16_32_Q11(a,b) ADD32(MULT16_16((a),SHR((b),11)), SHR(MULT16_16((a),((b)&0x000007ff)),11))
-#define MAC16_32_Q11(c,a,b) ADD32(c,ADD32(MULT16_16((a),SHR((b),11)), SHR(MULT16_16((a),((b)&0x000007ff)),11)))
-
-#define MULT16_32_P15(a,b) ADD32(MULT16_16((a),SHR((b),15)), PSHR(MULT16_16((a),((b)&0x00007fff)),15))
-#define MULT16_32_Q15(a,b) ADD32(MULT16_16((a),SHR((b),15)), SHR(MULT16_16((a),((b)&0x00007fff)),15))
-#define MAC16_32_Q15(c,a,b) ADD32(c,ADD32(MULT16_16((a),SHR((b),15)), SHR(MULT16_16((a),((b)&0x00007fff)),15)))
-
-
-#define MAC16_16_Q11(c,a,b) (ADD32((c),SHR(MULT16_16((a),(b)),11)))
-#define MAC16_16_Q13(c,a,b) (ADD32((c),SHR(MULT16_16((a),(b)),13)))
-#define MAC16_16_P13(c,a,b) (ADD32((c),SHR(ADD32(4096,MULT16_16((a),(b))),13)))
-
-#define MULT16_16_Q11_32(a,b) (SHR(MULT16_16((a),(b)),11))
-#define MULT16_16_Q13(a,b) (SHR(MULT16_16((a),(b)),13))
-#define MULT16_16_Q14(a,b) (SHR(MULT16_16((a),(b)),14))
-#define MULT16_16_Q15(a,b) (SHR(MULT16_16((a),(b)),15))
-
-#define MULT16_16_P13(a,b) (SHR(ADD32(4096,MULT16_16((a),(b))),13))
-#define MULT16_16_P14(a,b) (SHR(ADD32(8192,MULT16_16((a),(b))),14))
-#define MULT16_16_P15(a,b) (SHR(ADD32(16384,MULT16_16((a),(b))),15))
-
-#define MUL_16_32_R15(a,bh,bl) ADD32(MULT16_16((a),(bh)), SHR(MULT16_16((a),(bl)),15))
-
-#define DIV32_16(a,b) ((spx_word16_t)(((spx_word32_t)(a))/((spx_word16_t)(b))))
-#define PDIV32_16(a,b) ((spx_word16_t)(((spx_word32_t)(a)+((spx_word16_t)(b)>>1))/((spx_word16_t)(b))))
-#define DIV32(a,b) (((spx_word32_t)(a))/((spx_word32_t)(b)))
-#define PDIV32(a,b) (((spx_word32_t)(a)+((spx_word16_t)(b)>>1))/((spx_word32_t)(b)))
-
-#endif
diff --git a/src/pulsecore/speex/resample.c b/src/pulsecore/speex/resample.c
deleted file mode 100644
index 1e59200..0000000
--- a/src/pulsecore/speex/resample.c
+++ /dev/null
@@ -1,1121 +0,0 @@
-/* Copyright (C) 2007 Jean-Marc Valin
-
- File: resample.c
- Arbitrary resampling code
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions are
- met:
-
- 1. Redistributions of source code must retain the above copyright notice,
- this list of conditions and the following disclaimer.
-
- 2. 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.
-
- 3. The name of the author may not be used to endorse or promote products
- derived from this software without specific prior written permission.
-
- THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
- IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
- INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
- ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- POSSIBILITY OF SUCH DAMAGE.
-*/
-
-/*
- The design goals of this code are:
- - Very fast algorithm
- - SIMD-friendly algorithm
- - Low memory requirement
- - Good *perceptual* quality (and not best SNR)
-
- 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
-#include "config.h"
-#endif
-
-#ifdef OUTSIDE_SPEEX
-#include <stdlib.h>
-static void *speex_alloc (int size) {return calloc(size,1);}
-static void *speex_realloc (void *ptr, int size) {return realloc(ptr, size);}
-static void speex_free (void *ptr) {free(ptr);}
-#include "speex_resampler.h"
-#include "arch.h"
-#else /* OUTSIDE_SPEEX */
-
-#include "speex/speex_resampler.h"
-#include "arch.h"
-#include "os_support.h"
-#endif /* OUTSIDE_SPEEX */
-
-#include <math.h>
-
-#ifndef M_PI
-#define M_PI 3.14159263
-#endif
-
-#ifdef FIXED_POINT
-#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))))
-#endif
-
-/*#define float double*/
-#define FILTER_SIZE 64
-#define OVERSAMPLE 8
-
-#define IMAX(a,b) ((a) > (b) ? (a) : (b))
-#define IMIN(a,b) ((a) < (b) ? (a) : (b))
-
-#ifndef NULL
-#define NULL 0
-#endif
-
-typedef int (*resampler_basic_func)(SpeexResamplerState *, spx_uint32_t , const spx_word16_t *, spx_uint32_t *, spx_word16_t *, spx_uint32_t *);
-
-struct SpeexResamplerState_ {
- spx_uint32_t in_rate;
- 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;
- spx_uint32_t mem_alloc_size;
- int int_advance;
- int frac_advance;
- float cutoff;
- 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;
-} ;
-
-static double kaiser12_table[68] = {
- 0.99859849, 1.00000000, 0.99859849, 0.99440475, 0.98745105, 0.97779076,
- 0.96549770, 0.95066529, 0.93340547, 0.91384741, 0.89213598, 0.86843014,
- 0.84290116, 0.81573067, 0.78710866, 0.75723148, 0.72629970, 0.69451601,
- 0.66208321, 0.62920216, 0.59606986, 0.56287762, 0.52980938, 0.49704014,
- 0.46473455, 0.43304576, 0.40211431, 0.37206735, 0.34301800, 0.31506490,
- 0.28829195, 0.26276832, 0.23854851, 0.21567274, 0.19416736, 0.17404546,
- 0.15530766, 0.13794294, 0.12192957, 0.10723616, 0.09382272, 0.08164178,
- 0.07063950, 0.06075685, 0.05193064, 0.04409466, 0.03718069, 0.03111947,
- 0.02584161, 0.02127838, 0.01736250, 0.01402878, 0.01121463, 0.00886058,
- 0.00691064, 0.00531256, 0.00401805, 0.00298291, 0.00216702, 0.00153438,
- 0.00105297, 0.00069463, 0.00043489, 0.00025272, 0.00013031, 0.0000527734,
- 0.00001000, 0.00000000};
-/*
-static double kaiser12_table[36] = {
- 0.99440475, 1.00000000, 0.99440475, 0.97779076, 0.95066529, 0.91384741,
- 0.86843014, 0.81573067, 0.75723148, 0.69451601, 0.62920216, 0.56287762,
- 0.49704014, 0.43304576, 0.37206735, 0.31506490, 0.26276832, 0.21567274,
- 0.17404546, 0.13794294, 0.10723616, 0.08164178, 0.06075685, 0.04409466,
- 0.03111947, 0.02127838, 0.01402878, 0.00886058, 0.00531256, 0.00298291,
- 0.00153438, 0.00069463, 0.00025272, 0.0000527734, 0.00000500, 0.00000000};
-*/
-static double kaiser10_table[36] = {
- 0.99537781, 1.00000000, 0.99537781, 0.98162644, 0.95908712, 0.92831446,
- 0.89005583, 0.84522401, 0.79486424, 0.74011713, 0.68217934, 0.62226347,
- 0.56155915, 0.50119680, 0.44221549, 0.38553619, 0.33194107, 0.28205962,
- 0.23636152, 0.19515633, 0.15859932, 0.12670280, 0.09935205, 0.07632451,
- 0.05731132, 0.04193980, 0.02979584, 0.02044510, 0.01345224, 0.00839739,
- 0.00488951, 0.00257636, 0.00115101, 0.00035515, 0.00000000, 0.00000000};
-
-static double kaiser8_table[36] = {
- 0.99635258, 1.00000000, 0.99635258, 0.98548012, 0.96759014, 0.94302200,
- 0.91223751, 0.87580811, 0.83439927, 0.78875245, 0.73966538, 0.68797126,
- 0.63451750, 0.58014482, 0.52566725, 0.47185369, 0.41941150, 0.36897272,
- 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,
- 0.71712752, 0.67172623, 0.62508937, 0.57774224, 0.53019925, 0.48295561,
- 0.43647969, 0.39120616, 0.34752997, 0.30580127, 0.26632152, 0.22934058,
- 0.19505503, 0.16360756, 0.13508755, 0.10953262, 0.08693120, 0.06722600,
- 0.05031820, 0.03607231, 0.02432151, 0.01487334, 0.00752000, 0.00000000};
-
-struct FuncDef {
- double *table;
- int oversample;
-};
-
-static struct FuncDef _KAISER12 = {kaiser12_table, 64};
-#define KAISER12 (&_KAISER12)
-/*static struct FuncDef _KAISER12 = {kaiser12_table, 32};
-#define KAISER12 (&_KAISER12)*/
-static struct FuncDef _KAISER10 = {kaiser10_table, 32};
-#define KAISER10 (&_KAISER10)
-static struct FuncDef _KAISER8 = {kaiser8_table, 32};
-#define KAISER8 (&_KAISER8)
-static struct FuncDef _KAISER6 = {kaiser6_table, 32};
-#define KAISER6 (&_KAISER6)
-
-struct QualityMapping {
- int base_length;
- int oversample;
- float downsample_bandwidth;
- float upsample_bandwidth;
- struct FuncDef *window_func;
-};
-
-
-/* This table maps conversion quality to internal parameters. There are two
- 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)
- 2) Any aliasing that occurs very close to the Nyquist rate will be masked
- by the sinusoids/noise just below the Nyquist rate (guaranteed only for
- up-sampling).
-*/
-static const struct QualityMapping quality_map[11] = {
- { 8, 4, 0.830f, 0.860f, KAISER6 }, /* Q0 */
- { 16, 4, 0.850f, 0.880f, KAISER6 }, /* Q1 */
- { 32, 4, 0.882f, 0.910f, KAISER6 }, /* Q2 */ /* 82.3% cutoff ( ~60 dB stop) 6 */
- { 48, 8, 0.895f, 0.917f, KAISER8 }, /* Q3 */ /* 84.9% cutoff ( ~80 dB stop) 8 */
- { 64, 8, 0.921f, 0.940f, KAISER8 }, /* Q4 */ /* 88.7% cutoff ( ~80 dB stop) 8 */
- { 80, 16, 0.922f, 0.940f, KAISER10}, /* Q5 */ /* 89.1% cutoff (~100 dB stop) 10 */
- { 96, 16, 0.940f, 0.945f, KAISER10}, /* Q6 */ /* 91.5% cutoff (~100 dB stop) 10 */
- {128, 16, 0.950f, 0.950f, KAISER10}, /* Q7 */ /* 93.1% cutoff (~100 dB stop) 10 */
- {160, 16, 0.960f, 0.960f, KAISER10}, /* Q8 */ /* 94.5% cutoff (~100 dB stop) 10 */
- {192, 32, 0.968f, 0.968f, KAISER12}, /* Q9 */ /* 95.5% cutoff (~100 dB stop) 10 */
- {256, 32, 0.975f, 0.975f, KAISER12}, /* Q10 */ /* 96.6% cutoff (~100 dB stop) 10 */
-};
-/*8,24,40,56,80,104,128,160,200,256,320*/
-static double compute_func(float x, struct FuncDef *func)
-{
- float y, frac;
- double interp[4];
- int ind;
- y = x*func->oversample;
- ind = (int)floor(y);
- frac = (y-ind);
- /* CSE with handle the repeated powers */
- interp[3] = -0.1666666667*frac + 0.1666666667*(frac*frac*frac);
- interp[2] = frac + 0.5*(frac*frac) - 0.5*(frac*frac*frac);
- /*interp[2] = 1.f - 0.5f*frac - frac*frac + 0.5f*frac*frac*frac;*/
- 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];
-}
-
-#if 0
-#include <stdio.h>
-int main(int argc, char **argv)
-{
- int i;
- for (i=0;i<256;i++)
- {
- printf ("%f\n", compute_func(i/256., KAISER12));
- }
- return 0;
-}
-#endif
-
-#ifdef FIXED_POINT
-/* The slow way of computing a sinc for the table. Should improve that some day */
-static spx_word16_t sinc(float cutoff, float x, int N, struct FuncDef *window_func)
-{
- /*fprintf (stderr, "%f ", x);*/
- float xx = x * cutoff;
- if (fabs(x)<1e-6f)
- return WORD2INT(32768.*cutoff);
- else if (fabs(x) > .5f*N)
- return 0;
- /*FIXME: Can it really be any slower than this? */
- return WORD2INT(32768.*cutoff*sin(M_PI*xx)/(M_PI*xx) * compute_func(fabs(2.*x/N), window_func));
-}
-#else
-/* The slow way of computing a sinc for the table. Should improve that some day */
-static spx_word16_t sinc(float cutoff, float x, int N, struct FuncDef *window_func)
-{
- /*fprintf (stderr, "%f ", x);*/
- float xx = x * cutoff;
- if (fabs(x)<1e-6)
- return cutoff;
- else if (fabs(x) > .5*N)
- return 0;
- /*FIXME: Can it really be any slower than this? */
- return cutoff*sin(M_PI*xx)/(M_PI*xx) * compute_func(fabs(2.*x/N), window_func);
-}
-#endif
-
-#ifdef FIXED_POINT
-static void cubic_coef(spx_word16_t x, spx_word16_t interp[4])
-{
- /* Compute interpolation coefficients. I'm not sure whether this corresponds to cubic interpolation
- but I know it's MMSE-optimal on a sinc */
- spx_word16_t x2, x3;
- x2 = MULT16_16_P15(x, x);
- x3 = MULT16_16_P15(x, x2);
- interp[0] = PSHR32(MULT16_16(QCONST16(-0.16667f, 15),x) + MULT16_16(QCONST16(0.16667f, 15),x3),15);
- interp[1] = EXTRACT16(EXTEND32(x) + SHR32(SUB32(EXTEND32(x2),EXTEND32(x3)),1));
- interp[3] = PSHR32(MULT16_16(QCONST16(-0.33333f, 15),x) + MULT16_16(QCONST16(.5f,15),x2) - MULT16_16(QCONST16(0.16667f, 15),x3),15);
- /* Just to make sure we don't have rounding problems */
- interp[2] = Q15_ONE-interp[0]-interp[1]-interp[3];
- if (interp[2]<32767)
- interp[2]+=1;
-}
-#else
-static void cubic_coef(spx_word16_t frac, spx_word16_t interp[4])
-{
- /* Compute interpolation coefficients. I'm not sure whether this corresponds to cubic interpolation
- but I know it's MMSE-optimal on a sinc */
- interp[0] = -0.16667f*frac + 0.16667f*frac*frac*frac;
- interp[1] = frac + 0.5f*frac*frac - 0.5f*frac*frac*frac;
- /*interp[2] = 1.f - 0.5f*frac - frac*frac + 0.5f*frac*frac*frac;*/
- interp[3] = -0.33333f*frac + 0.5f*frac*frac - 0.16667f*frac*frac*frac;
- /* Just to make sure we don't have rounding problems */
- interp[2] = 1.-interp[0]-interp[1]-interp[3];
-}
-#endif
-
-static int resampler_basic_direct_single(SpeexResamplerState *st, spx_uint32_t channel_index, const spx_word16_t *in, spx_uint32_t *in_len, spx_word16_t *out, spx_uint32_t *out_len)
-{
- int N = st->filt_len;
- int out_sample = 0;
- spx_word16_t *mem;
- int last_sample = st->last_sample[channel_index];
- spx_uint32_t samp_frac_num = st->samp_frac_num[channel_index];
- mem = st->mem + channel_index * st->mem_alloc_size;
- while (!(last_sample >= (spx_int32_t)*in_len || out_sample >= (spx_int32_t)*out_len))
- {
- 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 */
- for (j=0;last_sample-N+1+j < 0;j++)
- {
- 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++)
- {
- 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++;
- last_sample += st->int_advance;
- samp_frac_num += st->frac_advance;
- if (samp_frac_num >= st->den_rate)
- {
- samp_frac_num -= st->den_rate;
- last_sample++;
- }
- }
- st->last_sample[channel_index] = last_sample;
- st->samp_frac_num[channel_index] = samp_frac_num;
- return out_sample;
-}
-
-#ifdef FIXED_POINT
-#else
-/* This is the same as the previous function, except with a double-precision accumulator */
-static int resampler_basic_direct_double(SpeexResamplerState *st, spx_uint32_t channel_index, const spx_word16_t *in, spx_uint32_t *in_len, spx_word16_t *out, spx_uint32_t *out_len)
-{
- int N = st->filt_len;
- int out_sample = 0;
- spx_word16_t *mem;
- int last_sample = st->last_sample[channel_index];
- spx_uint32_t samp_frac_num = st->samp_frac_num[channel_index];
- mem = st->mem + channel_index * st->mem_alloc_size;
- while (!(last_sample >= (spx_int32_t)*in_len || out_sample >= (spx_int32_t)*out_len))
- {
- 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 */
- for (j=0;last_sample-N+1+j < 0;j++)
- {
- 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++)
- {
- 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++;
- last_sample += st->int_advance;
- samp_frac_num += st->frac_advance;
- if (samp_frac_num >= st->den_rate)
- {
- samp_frac_num -= st->den_rate;
- last_sample++;
- }
- }
- st->last_sample[channel_index] = last_sample;
- st->samp_frac_num[channel_index] = samp_frac_num;
- return out_sample;
-}
-#endif
-
-static int resampler_basic_interpolate_single(SpeexResamplerState *st, spx_uint32_t channel_index, const spx_word16_t *in, spx_uint32_t *in_len, spx_word16_t *out, spx_uint32_t *out_len)
-{
- int N = st->filt_len;
- int out_sample = 0;
- spx_word16_t *mem;
- int last_sample = st->last_sample[channel_index];
- spx_uint32_t samp_frac_num = st->samp_frac_num[channel_index];
- mem = st->mem + channel_index * st->mem_alloc_size;
- while (!(last_sample >= (spx_int32_t)*in_len || out_sample >= (spx_int32_t)*out_len))
- {
- 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];
- const spx_word16_t *ptr;
- int offset;
- spx_word16_t frac;
- offset = samp_frac_num*st->oversample/st->den_rate;
-#ifdef FIXED_POINT
- frac = PDIV32(SHL32((samp_frac_num*st->oversample) % st->den_rate,15),st->den_rate);
-#else
- 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
- have only two accumulators */
- for (j=0;last_sample-N+1+j < 0;j++)
- {
- spx_word16_t curr_mem = mem[last_sample+j];
- accum[0] += MULT16_16(curr_mem,st->sinc_table[4+(j+1)*st->oversample-offset-2]);
- accum[1] += MULT16_16(curr_mem,st->sinc_table[4+(j+1)*st->oversample-offset-1]);
- accum[2] += MULT16_16(curr_mem,st->sinc_table[4+(j+1)*st->oversample-offset]);
- accum[3] += MULT16_16(curr_mem,st->sinc_table[4+(j+1)*st->oversample-offset+1]);
- }
- ptr = in+st->in_stride*(last_sample-N+1+j);
- /* Do the new part */
- for (;j<N;j++)
- {
- spx_word16_t curr_in = *ptr;
- ptr += st->in_stride;
- accum[0] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset-2]);
- accum[1] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset-1]);
- accum[2] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset]);
- accum[3] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset+1]);
- }
- 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++;
- last_sample += st->int_advance;
- samp_frac_num += st->frac_advance;
- if (samp_frac_num >= st->den_rate)
- {
- samp_frac_num -= st->den_rate;
- last_sample++;
- }
- }
- st->last_sample[channel_index] = last_sample;
- st->samp_frac_num[channel_index] = samp_frac_num;
- return out_sample;
-}
-
-#ifdef FIXED_POINT
-#else
-/* This is the same as the previous function, except with a double-precision accumulator */
-static int resampler_basic_interpolate_double(SpeexResamplerState *st, spx_uint32_t channel_index, const spx_word16_t *in, spx_uint32_t *in_len, spx_word16_t *out, spx_uint32_t *out_len)
-{
- int N = st->filt_len;
- int out_sample = 0;
- spx_word16_t *mem;
- int last_sample = st->last_sample[channel_index];
- spx_uint32_t samp_frac_num = st->samp_frac_num[channel_index];
- mem = st->mem + channel_index * st->mem_alloc_size;
- while (!(last_sample >= (spx_int32_t)*in_len || out_sample >= (spx_int32_t)*out_len))
- {
- 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];
- const spx_word16_t *ptr;
- float alpha = ((float)samp_frac_num)/st->den_rate;
- 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
- have only two accumulators */
- for (j=0;last_sample-N+1+j < 0;j++)
- {
- double curr_mem = mem[last_sample+j];
- accum[0] += MULT16_16(curr_mem,st->sinc_table[4+(j+1)*st->oversample-offset-2]);
- accum[1] += MULT16_16(curr_mem,st->sinc_table[4+(j+1)*st->oversample-offset-1]);
- accum[2] += MULT16_16(curr_mem,st->sinc_table[4+(j+1)*st->oversample-offset]);
- accum[3] += MULT16_16(curr_mem,st->sinc_table[4+(j+1)*st->oversample-offset+1]);
- }
- ptr = in+st->in_stride*(last_sample-N+1+j);
- /* Do the new part */
- for (;j<N;j++)
- {
- double curr_in = *ptr;
- ptr += st->in_stride;
- accum[0] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset-2]);
- accum[1] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset-1]);
- accum[2] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset]);
- accum[3] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset+1]);
- }
- 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++;
- last_sample += st->int_advance;
- samp_frac_num += st->frac_advance;
- if (samp_frac_num >= st->den_rate)
- {
- samp_frac_num -= st->den_rate;
- last_sample++;
- }
- }
- st->last_sample[channel_index] = last_sample;
- st->samp_frac_num[channel_index] = samp_frac_num;
- return out_sample;
-}
-#endif
-
-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 */
- st->cutoff = quality_map[st->quality].downsample_bandwidth * st->den_rate / st->num_rate;
- /* FIXME: divide the numerator and denominator by a certain amount if they're too large */
- st->filt_len = st->filt_len*st->num_rate / st->den_rate;
- /* Round down to make sure we have a multiple of 4 */
- st->filt_len &= (~0x3);
- if (2*st->den_rate < st->num_rate)
- st->oversample >>= 1;
- if (4*st->den_rate < st->num_rate)
- st->oversample >>= 1;
- if (8*st->den_rate < st->num_rate)
- st->oversample >>= 1;
- if (16*st->den_rate < st->num_rate)
- st->oversample >>= 1;
- if (st->oversample < 1)
- st->oversample = 1;
- } else {
- /* up-sampling */
- st->cutoff = quality_map[st->quality].upsample_bandwidth;
- }
-
- /* Choose the resampling type that requires the least amount of memory */
- if (st->den_rate <= st->oversample)
- {
- spx_uint32_t i;
- if (!st->sinc_table)
- st->sinc_table = (spx_word16_t *)speex_alloc(st->filt_len*st->den_rate*sizeof(spx_word16_t));
- else if (st->sinc_table_length < st->filt_len*st->den_rate)
- {
- st->sinc_table = (spx_word16_t *)speex_realloc(st->sinc_table,st->filt_len*st->den_rate*sizeof(spx_word16_t));
- st->sinc_table_length = st->filt_len*st->den_rate;
- }
- for (i=0;i<st->den_rate;i++)
- {
- spx_int32_t j;
- for (j=0;j<st->filt_len;j++)
- {
- st->sinc_table[i*st->filt_len+j] = sinc(st->cutoff,((j-(spx_int32_t)st->filt_len/2+1)-((float)i)/st->den_rate), st->filt_len, quality_map[st->quality].window_func);
- }
- }
-#ifdef FIXED_POINT
- st->resampler_ptr = resampler_basic_direct_single;
-#else
- if (st->quality>8)
- st->resampler_ptr = resampler_basic_direct_double;
- else
- st->resampler_ptr = resampler_basic_direct_single;
-#endif
- /*fprintf (stderr, "resampler uses direct sinc table and normalised cutoff %f\n", cutoff);*/
- } else {
- spx_int32_t i;
- if (!st->sinc_table)
- st->sinc_table = (spx_word16_t *)speex_alloc((st->filt_len*st->oversample+8)*sizeof(spx_word16_t));
- else if (st->sinc_table_length < st->filt_len*st->oversample+8)
- {
- st->sinc_table = (spx_word16_t *)speex_realloc(st->sinc_table,(st->filt_len*st->oversample+8)*sizeof(spx_word16_t));
- st->sinc_table_length = st->filt_len*st->oversample+8;
- }
- for (i=-4;i<(spx_int32_t)(st->oversample*st->filt_len+4);i++)
- st->sinc_table[i+4] = sinc(st->cutoff,(i/(float)st->oversample - st->filt_len/2), st->filt_len, quality_map[st->quality].window_func);
-#ifdef FIXED_POINT
- st->resampler_ptr = resampler_basic_interpolate_single;
-#else
- if (st->quality>8)
- st->resampler_ptr = resampler_basic_interpolate_double;
- else
- st->resampler_ptr = resampler_basic_interpolate_single;
-#endif
- /*fprintf (stderr, "resampler uses interpolated sinc table and normalised cutoff %f\n", cutoff);*/
- }
- 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. */
- if (!st->mem)
- {
- spx_uint32_t i;
- st->mem = (spx_word16_t*)speex_alloc(st->nb_channels*(st->filt_len-1) * sizeof(spx_word16_t));
- for (i=0;i<st->nb_channels*(st->filt_len-1);i++)
- st->mem[i] = 0;
- st->mem_alloc_size = st->filt_len-1;
- /*speex_warning("init filter");*/
- } else if (!st->started)
- {
- spx_uint32_t i;
- st->mem = (spx_word16_t*)speex_realloc(st->mem, st->nb_channels*(st->filt_len-1) * sizeof(spx_word16_t));
- for (i=0;i<st->nb_channels*(st->filt_len-1);i++)
- st->mem[i] = 0;
- st->mem_alloc_size = st->filt_len-1;
- /*speex_warning("reinit filter");*/
- } else if (st->filt_len > old_length)
- {
- spx_int32_t i;
- /* Increase the filter length */
- /*speex_warning("increase filter size");*/
- int old_alloc_size = st->mem_alloc_size;
- if (st->filt_len-1 > st->mem_alloc_size)
- {
- st->mem = (spx_word16_t*)speex_realloc(st->mem, st->nb_channels*(st->filt_len-1) * sizeof(spx_word16_t));
- st->mem_alloc_size = st->filt_len-1;
- }
- for (i=st->nb_channels-1;i>=0;i--)
- {
- spx_int32_t j;
- spx_uint32_t olen = old_length;
- /*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--)
- st->mem[i*st->mem_alloc_size+j+st->magic_samples[i]] = st->mem[i*old_alloc_size+j];
- for (j=0;j<st->magic_samples[i];j++)
- st->mem[i*st->mem_alloc_size+j] = 0;
- st->magic_samples[i] = 0;
- }
- if (st->filt_len > olen)
- {
- /* If the new filter length is still bigger than the "augmented" length */
- /* Copy data going backward */
- for (j=0;j<olen-1;j++)
- st->mem[i*st->mem_alloc_size+(st->filt_len-2-j)] = st->mem[i*st->mem_alloc_size+(olen-2-j)];
- /* Then put zeros for lack of anything better */
- for (;j<st->filt_len-1;j++)
- st->mem[i*st->mem_alloc_size+(st->filt_len-2-j)] = 0;
- /* Adjust last_sample */
- st->last_sample[i] += (st->filt_len - olen)/2;
- } else {
- /* Put back some of the magic! */
- st->magic_samples[i] = (olen - st->filt_len)/2;
- for (j=0;j<st->filt_len-1+st->magic_samples[i];j++)
- st->mem[i*st->mem_alloc_size+j] = st->mem[i*st->mem_alloc_size+j+st->magic_samples[i]];
- }
- }
- } else if (st->filt_len < old_length)
- {
- spx_uint32_t i;
- /* Reduce filter length, this a bit tricky. We need to store some of the memory as "magic"
- samples so they can be used directly as input the next time(s) */
- for (i=0;i<st->nb_channels;i++)
- {
- spx_uint32_t j;
- spx_uint32_t old_magic = st->magic_samples[i];
- st->magic_samples[i] = (old_length - st->filt_len)/2;
- /* We must copy some of the memory that's no longer used */
- /* Copy data going backward */
- for (j=0;j<st->filt_len-1+st->magic_samples[i]+old_magic;j++)
- st->mem[i*st->mem_alloc_size+j] = st->mem[i*st->mem_alloc_size+j+st->magic_samples[i]];
- st->magic_samples[i] += old_magic;
- }
- }
-
-}
-
-SpeexResamplerState *speex_resampler_init(spx_uint32_t nb_channels, spx_uint32_t in_rate, spx_uint32_t out_rate, int quality, int *err)
-{
- return speex_resampler_init_frac(nb_channels, in_rate, out_rate, in_rate, out_rate, quality, err);
-}
-
-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)
-{
- spx_uint32_t i;
- SpeexResamplerState *st;
- if (quality > 10 || quality < 0)
- {
- if (err)
- *err = RESAMPLER_ERR_INVALID_ARG;
- return NULL;
- }
- st = (SpeexResamplerState *)speex_alloc(sizeof(SpeexResamplerState));
- st->initialised = 0;
- st->started = 0;
- st->in_rate = 0;
- st->out_rate = 0;
- st->num_rate = 0;
- st->den_rate = 0;
- st->quality = -1;
- st->sinc_table_length = 0;
- st->mem_alloc_size = 0;
- 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));
- st->samp_frac_num = (spx_uint32_t*)speex_alloc(nb_channels*sizeof(int));
- for (i=0;i<nb_channels;i++)
- {
- st->last_sample[i] = 0;
- st->magic_samples[i] = 0;
- st->samp_frac_num[i] = 0;
- }
-
- 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;
-
- return st;
-}
-
-void speex_resampler_destroy(SpeexResamplerState *st)
-{
- speex_free(st->mem);
- speex_free(st->sinc_table);
- speex_free(st->last_sample);
- speex_free(st->magic_samples);
- speex_free(st->samp_frac_num);
- speex_free(st);
-}
-
-
-
-static int speex_resampler_process_native(SpeexResamplerState *st, spx_uint32_t channel_index, const spx_word16_t *in, spx_uint32_t *in_len, spx_word16_t *out, spx_uint32_t *out_len)
-{
- int j=0;
- int N = st->filt_len;
- int out_sample = 0;
- spx_word16_t *mem;
- 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;
- /* magic_samples needs to be set to zero to avoid infinite recursion */
- tmp_magic = st->magic_samples[channel_index];
- st->magic_samples[channel_index] = 0;
- st->in_stride = 1;
- speex_resampler_process_native(st, channel_index, mem+N-1, &tmp_in_len, out, &tmp_out_len);
- st->in_stride = istride_save;
- /*speex_warning_int("extra samples:", tmp_out_len);*/
- /* If we couldn't process all "magic" input samples, save the rest for next time */
- if (tmp_in_len < tmp_magic)
- {
- spx_uint32_t i;
- st->magic_samples[channel_index] = tmp_magic-tmp_in_len;
- for (i=0;i<st->magic_samples[channel_index];i++)
- mem[N-1+i]=mem[N-1+i+tmp_in_len];
- }
- 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;
-}
-
-#define FIXED_STACK_ALLOC 1024
-
-#ifdef FIXED_POINT
-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)
-{
- spx_uint32_t i;
- int istride_save, ostride_save;
-#ifdef VAR_ARRAYS
- spx_word16_t x[*in_len];
- spx_word16_t y[*out_len];
- /*VARDECL(spx_word16_t *x);
- VARDECL(spx_word16_t *y);
- ALLOC(x, *in_len, spx_word16_t);
- ALLOC(y, *out_len, spx_word16_t);*/
- istride_save = st->in_stride;
- ostride_save = st->out_stride;
- for (i=0;i<*in_len;i++)
- x[i] = WORD2INT(in[i*st->in_stride]);
- st->in_stride = st->out_stride = 1;
- speex_resampler_process_native(st, channel_index, x, in_len, y, out_len);
- st->in_stride = istride_save;
- st->out_stride = ostride_save;
- for (i=0;i<*out_len;i++)
- out[i*st->out_stride] = y[i];
-#else
- spx_word16_t x[FIXED_STACK_ALLOC];
- spx_word16_t y[FIXED_STACK_ALLOC];
- spx_uint32_t ilen=*in_len, olen=*out_len;
- istride_save = st->in_stride;
- ostride_save = st->out_stride;
- while (ilen && olen)
- {
- spx_uint32_t ichunk, ochunk;
- ichunk = ilen;
- ochunk = olen;
- if (ichunk>FIXED_STACK_ALLOC)
- ichunk=FIXED_STACK_ALLOC;
- if (ochunk>FIXED_STACK_ALLOC)
- ochunk=FIXED_STACK_ALLOC;
- for (i=0;i<ichunk;i++)
- x[i] = WORD2INT(in[i*st->in_stride]);
- st->in_stride = st->out_stride = 1;
- speex_resampler_process_native(st, channel_index, x, &ichunk, y, &ochunk);
- st->in_stride = istride_save;
- st->out_stride = ostride_save;
- for (i=0;i<ochunk;i++)
- out[i*st->out_stride] = y[i];
- out += ochunk;
- in += ichunk;
- ilen -= ichunk;
- olen -= ochunk;
- }
- *in_len -= ilen;
- *out_len -= olen;
-#endif
- return RESAMPLER_ERR_SUCCESS;
-}
-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)
-{
- return speex_resampler_process_native(st, channel_index, in, in_len, out, out_len);
-}
-#else
-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)
-{
- return speex_resampler_process_native(st, channel_index, in, in_len, out, out_len);
-}
-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)
-{
- spx_uint32_t i;
- int istride_save, ostride_save;
-#ifdef VAR_ARRAYS
- spx_word16_t x[*in_len];
- spx_word16_t y[*out_len];
- /*VARDECL(spx_word16_t *x);
- VARDECL(spx_word16_t *y);
- ALLOC(x, *in_len, spx_word16_t);
- ALLOC(y, *out_len, spx_word16_t);*/
- istride_save = st->in_stride;
- ostride_save = st->out_stride;
- for (i=0;i<*in_len;i++)
- x[i] = in[i*st->in_stride];
- st->in_stride = st->out_stride = 1;
- speex_resampler_process_native(st, channel_index, x, in_len, y, out_len);
- st->in_stride = istride_save;
- st->out_stride = ostride_save;
- for (i=0;i<*out_len;i++)
- out[i*st->out_stride] = WORD2INT(y[i]);
-#else
- spx_word16_t x[FIXED_STACK_ALLOC];
- spx_word16_t y[FIXED_STACK_ALLOC];
- spx_uint32_t ilen=*in_len, olen=*out_len;
- istride_save = st->in_stride;
- ostride_save = st->out_stride;
- while (ilen && olen)
- {
- spx_uint32_t ichunk, ochunk;
- ichunk = ilen;
- ochunk = olen;
- if (ichunk>FIXED_STACK_ALLOC)
- ichunk=FIXED_STACK_ALLOC;
- if (ochunk>FIXED_STACK_ALLOC)
- ochunk=FIXED_STACK_ALLOC;
- for (i=0;i<ichunk;i++)
- x[i] = in[i*st->in_stride];
- st->in_stride = st->out_stride = 1;
- speex_resampler_process_native(st, channel_index, x, &ichunk, y, &ochunk);
- st->in_stride = istride_save;
- st->out_stride = ostride_save;
- for (i=0;i<ochunk;i++)
- out[i*st->out_stride] = WORD2INT(y[i]);
- out += ochunk;
- in += ichunk;
- ilen -= ichunk;
- olen -= ochunk;
- }
- *in_len -= ilen;
- *out_len -= olen;
-#endif
- return RESAMPLER_ERR_SUCCESS;
-}
-#endif
-
-int speex_resampler_process_interleaved_float(SpeexResamplerState *st, const float *in, spx_uint32_t *in_len, float *out, spx_uint32_t *out_len)
-{
- spx_uint32_t i;
- int istride_save, ostride_save;
- spx_uint32_t bak_len = *out_len;
- istride_save = st->in_stride;
- ostride_save = st->out_stride;
- st->in_stride = st->out_stride = st->nb_channels;
- for (i=0;i<st->nb_channels;i++)
- {
- *out_len = bak_len;
- speex_resampler_process_float(st, i, in+i, in_len, out+i, out_len);
- }
- st->in_stride = istride_save;
- st->out_stride = ostride_save;
- 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;
- int istride_save, ostride_save;
- spx_uint32_t bak_len = *out_len;
- istride_save = st->in_stride;
- ostride_save = st->out_stride;
- st->in_stride = st->out_stride = st->nb_channels;
- for (i=0;i<st->nb_channels;i++)
- {
- *out_len = bak_len;
- speex_resampler_process_int(st, i, in+i, in_len, out+i, out_len);
- }
- st->in_stride = istride_save;
- st->out_stride = ostride_save;
- return RESAMPLER_ERR_SUCCESS;
-}
-
-int speex_resampler_set_rate(SpeexResamplerState *st, spx_uint32_t in_rate, spx_uint32_t out_rate)
-{
- return speex_resampler_set_rate_frac(st, in_rate, out_rate, in_rate, out_rate);
-}
-
-void speex_resampler_get_rate(SpeexResamplerState *st, spx_uint32_t *in_rate, spx_uint32_t *out_rate)
-{
- *in_rate = st->in_rate;
- *out_rate = st->out_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)
-{
- spx_uint32_t fact;
- spx_uint32_t old_den;
- 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;
- st->num_rate = ratio_num;
- st->den_rate = ratio_den;
- /* FIXME: This is terribly inefficient, but who cares (at least for now)? */
- for (fact=2;fact<=IMIN(st->num_rate, st->den_rate);fact++)
- {
- while ((st->num_rate % fact == 0) && (st->den_rate % fact == 0))
- {
- st->num_rate /= fact;
- st->den_rate /= fact;
- }
- }
-
- if (old_den > 0)
- {
- for (i=0;i<st->nb_channels;i++)
- {
- st->samp_frac_num[i]=st->samp_frac_num[i]*st->den_rate/old_den;
- /* Safety net */
- if (st->samp_frac_num[i] >= st->den_rate)
- st->samp_frac_num[i] = st->den_rate-1;
- }
- }
-
- if (st->initialised)
- update_filter(st);
- return RESAMPLER_ERR_SUCCESS;
-}
-
-void speex_resampler_get_ratio(SpeexResamplerState *st, spx_uint32_t *ratio_num, spx_uint32_t *ratio_den)
-{
- *ratio_num = st->num_rate;
- *ratio_den = st->den_rate;
-}
-
-int speex_resampler_set_quality(SpeexResamplerState *st, int quality)
-{
- if (quality > 10 || quality < 0)
- return RESAMPLER_ERR_INVALID_ARG;
- if (st->quality == quality)
- return RESAMPLER_ERR_SUCCESS;
- st->quality = quality;
- if (st->initialised)
- update_filter(st);
- return RESAMPLER_ERR_SUCCESS;
-}
-
-void speex_resampler_get_quality(SpeexResamplerState *st, int *quality)
-{
- *quality = st->quality;
-}
-
-void speex_resampler_set_input_stride(SpeexResamplerState *st, spx_uint32_t stride)
-{
- st->in_stride = stride;
-}
-
-void speex_resampler_get_input_stride(SpeexResamplerState *st, spx_uint32_t *stride)
-{
- *stride = st->in_stride;
-}
-
-void speex_resampler_set_output_stride(SpeexResamplerState *st, spx_uint32_t stride)
-{
- st->out_stride = stride;
-}
-
-void speex_resampler_get_output_stride(SpeexResamplerState *st, spx_uint32_t *stride)
-{
- *stride = st->out_stride;
-}
-
-int speex_resampler_skip_zeros(SpeexResamplerState *st)
-{
- spx_uint32_t i;
- for (i=0;i<st->nb_channels;i++)
- st->last_sample[i] = st->filt_len/2;
- return RESAMPLER_ERR_SUCCESS;
-}
-
-int speex_resampler_reset_mem(SpeexResamplerState *st)
-{
- spx_uint32_t i;
- for (i=0;i<st->nb_channels*(st->filt_len-1);i++)
- st->mem[i] = 0;
- return RESAMPLER_ERR_SUCCESS;
-}
-
-const char *speex_resampler_strerror(int err)
-{
- switch (err)
- {
- case RESAMPLER_ERR_SUCCESS:
- return "Success.";
- case RESAMPLER_ERR_ALLOC_FAILED:
- return "Memory allocation failed.";
- case RESAMPLER_ERR_BAD_STATE:
- return "Bad resampler state.";
- case RESAMPLER_ERR_INVALID_ARG:
- return "Invalid argument.";
- case RESAMPLER_ERR_PTR_OVERLAP:
- return "Input and output buffers overlap.";
- default:
- return "Unknown error. Bad error code or strange version mismatch.";
- }
-}
diff --git a/src/pulsecore/speex/speex_resampler.h b/src/pulsecore/speex/speex_resampler.h
deleted file mode 100644
index 8629eeb..0000000
--- a/src/pulsecore/speex/speex_resampler.h
+++ /dev/null
@@ -1,328 +0,0 @@
-/* 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
- - Good *perceptual* quality (and not best SNR)
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions are
- met:
-
- 1. Redistributions of source code must retain the above copyright notice,
- this list of conditions and the following disclaimer.
-
- 2. 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.
-
- 3. The name of the author may not be used to endorse or promote products
- derived from this software without specific prior written permission.
-
- THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
- IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
- INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
- ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- POSSIBILITY OF SUCH DAMAGE.
-*/
-
-
-#ifndef SPEEX_RESAMPLER_H
-#define SPEEX_RESAMPLER_H
-
-#ifdef OUTSIDE_SPEEX
-
-/********* WARNING: MENTAL SANITY ENDS HERE *************/
-
-/* 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 */
-#ifndef RANDOM_PREFIX
-#error "Please define RANDOM_PREFIX (above) to something specific to your project to prevent symbol name clashes"
-#endif
-
-#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)
-#define speex_resampler_process_float CAT_PREFIX(RANDOM_PREFIX,_resampler_process_float)
-#define speex_resampler_process_int CAT_PREFIX(RANDOM_PREFIX,_resampler_process_int)
-#define speex_resampler_process_interleaved_float CAT_PREFIX(RANDOM_PREFIX,_resampler_process_interleaved_float)
-#define speex_resampler_process_interleaved_int CAT_PREFIX(RANDOM_PREFIX,_resampler_process_interleaved_int)
-#define speex_resampler_set_rate CAT_PREFIX(RANDOM_PREFIX,_resampler_set_rate)
-#define speex_resampler_get_rate CAT_PREFIX(RANDOM_PREFIX,_resampler_get_rate)
-#define speex_resampler_set_rate_frac CAT_PREFIX(RANDOM_PREFIX,_resampler_set_rate_frac)
-#define speex_resampler_get_ratio CAT_PREFIX(RANDOM_PREFIX,_resampler_get_ratio)
-#define speex_resampler_set_quality CAT_PREFIX(RANDOM_PREFIX,_resampler_set_quality)
-#define speex_resampler_get_quality CAT_PREFIX(RANDOM_PREFIX,_resampler_get_quality)
-#define speex_resampler_set_input_stride CAT_PREFIX(RANDOM_PREFIX,_resampler_set_input_stride)
-#define speex_resampler_get_input_stride CAT_PREFIX(RANDOM_PREFIX,_resampler_get_input_stride)
-#define speex_resampler_set_output_stride CAT_PREFIX(RANDOM_PREFIX,_resampler_set_output_stride)
-#define speex_resampler_get_output_stride CAT_PREFIX(RANDOM_PREFIX,_resampler_get_output_stride)
-#define speex_resampler_skip_zeros CAT_PREFIX(RANDOM_PREFIX,_resampler_skip_zeros)
-#define speex_resampler_reset_mem CAT_PREFIX(RANDOM_PREFIX,_resampler_reset_mem)
-#define speex_resampler_strerror CAT_PREFIX(RANDOM_PREFIX,_resampler_strerror)
-
-#define spx_int16_t short
-#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"
-
-#endif /* OUTSIDE_SPEEX */
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#define SPEEX_RESAMPLER_QUALITY_MAX 10
-#define SPEEX_RESAMPLER_QUALITY_MIN 0
-#define SPEEX_RESAMPLER_QUALITY_DEFAULT 4
-#define SPEEX_RESAMPLER_QUALITY_VOIP 3
-#define SPEEX_RESAMPLER_QUALITY_DESKTOP 5
-
-enum {
- RESAMPLER_ERR_SUCCESS = 0,
- RESAMPLER_ERR_ALLOC_FAILED = 1,
- RESAMPLER_ERR_BAD_STATE = 2,
- RESAMPLER_ERR_INVALID_ARG = 3,
- RESAMPLER_ERR_PTR_OVERLAP = 4,
-
- RESAMPLER_ERR_MAX_ERROR
-};
-
-struct SpeexResamplerState_;
-typedef struct SpeexResamplerState_ SpeexResamplerState;
-
-/** Create a new resampler with integer input and output rates.
- * @param nb_channels Number of channels to be processed
- * @param in_rate Input sampling rate (integer number of Hz).
- * @param out_rate Output sampling rate (integer number of Hz).
- * @param quality Resampling quality between 0 and 10, where 0 has poor quality
- * and 10 has very high quality.
- * @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,
- 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
- * denominator being 32-bit integers.
- * @param nb_channels Number of channels to be processed
- * @param ratio_num Numerator of the sampling rate ratio
- * @param ratio_den Denominator of the sampling rate ratio
- * @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).
- * @param quality Resampling quality between 0 and 10, where 0 has poor quality
- * and 10 has very high quality.
- * @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,
- int quality,
- int *err);
-
-/** Destroy a resampler state.
- * @param st Resampler state
- */
-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
- * base (0 otherwise)
- * @param in Input buffer
- * @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,
- 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
- * base (0 otherwise)
- * @param in Input buffer
- * @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_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.
- * @param st Resampler state
- * @param in Input buffer
- * @param in_len Number of input samples in the input buffer. Returns the number
- * of samples processed. This is all per-channel.
- * @param out Output buffer
- * @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,
- spx_uint32_t *out_len);
-
-/** Resample an interleaved int array. The input and output buffers must *not* overlap.
- * @param st Resampler state
- * @param in Input buffer
- * @param in_len Number of input samples in the input buffer. Returns the number
- * of samples processed. This is all per-channel.
- * @param out Output buffer
- * @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,
- spx_uint32_t *out_len);
-
-/** Set (change) the input/output sampling rates (integer value).
- * @param st Resampler state
- * @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,
- spx_uint32_t out_rate);
-
-/** Get the current input/output sampling rates (integer value).
- * @param st Resampler state
- * @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,
- spx_uint32_t *out_rate);
-
-/** 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
- * @param ratio_den Denominator of the sampling rate ratio
- * @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,
- spx_uint32_t out_rate);
-
-/** Get the current resampling ratio. This will be reduced to the least
- * common denominator.
- * @param st Resampler state
- * @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,
- 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
- * quality and 10 has very high quality.
- */
-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
- * quality and 10 has very high quality.
- */
-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,
- 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,
- 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,
- 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,
- 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
- * 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
- * is the same for the first frame).
- * @param st Resampler state
- */
-int speex_resampler_skip_zeros(SpeexResamplerState *st);
-
-/** Reset a resampler so a new (unrelated) stream can be processed.
- * @param st Resampler state
- */
-int speex_resampler_reset_mem(SpeexResamplerState *st);
-
-/** Returns the English meaning for an error code
- * @param err Error code
- * @return English string
- */
-const char *speex_resampler_strerror(int err);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/src/pulsecore/speexwrap.h b/src/pulsecore/speexwrap.h
deleted file mode 100644
index 8deca5c..0000000
--- a/src/pulsecore/speexwrap.h
+++ /dev/null
@@ -1,48 +0,0 @@
-#ifndef foopulsespeexwraphfoo
-#define foopulsespeexwraphfoo
-
-/* $Id: speexwrap.h 1971 2007-10-28 19:13:50Z lennart $ */
-
-/***
- This file is part of PulseAudio.
-
- Copyright 2004-2006 Lennart Poettering
- Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
-
- PulseAudio is free software; you can redistribute it and/or modify
- it under the terms of the GNU Lesser General Public License as published
- by the Free Software Foundation; either version 2 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.
-***/
-
-/* We define a minimal version of speex_resampler.h however define one
- * version for fixed and another one for float. Yes, somewhat ugly */
-
-#define spx_int16_t short
-#define spx_int32_t int
-#define spx_uint16_t unsigned short
-#define spx_uint32_t unsigned int
-
-typedef struct SpeexResamplerState_ SpeexResamplerState;
-
-SpeexResamplerState *paspfx_resampler_init(spx_uint32_t nb_channels, spx_uint32_t in_rate, spx_uint32_t out_rate, int quality, int *err);
-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);
-
-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);
-
-#endif
diff --git a/src/pulsecore/start-child.c b/src/pulsecore/start-child.c
index e01011d..7774bde 100644
--- a/src/pulsecore/start-child.c
+++ b/src/pulsecore/start-child.c
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/***
This file is part of PulseAudio.
@@ -68,11 +66,6 @@ int pa_start_child_for_read(const char *name, const char *argv1, pid_t *pid) {
return pipe_fds[0];
} else {
-#ifdef __linux__
- DIR* d;
-#endif
- int max_fd, i;
-
/* child */
pa_reset_priority();
@@ -89,48 +82,9 @@ int pa_start_child_for_read(const char *name, const char *argv1, pid_t *pid) {
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
+ pa_close_all(-1);
+ pa_reset_sigs(-1);
+ pa_unblock_sigs(-1);
#ifdef PR_SET_PDEATHSIG
/* On Linux we can use PR_SET_PDEATHSIG to have the helper
@@ -141,16 +95,6 @@ int pa_start_child_for_read(const char *name, const char *argv1, pid_t *pid) {
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);
}
diff --git a/src/pulsecore/start-child.h b/src/pulsecore/start-child.h
index 359b504..0b5ff66 100644
--- a/src/pulsecore/start-child.h
+++ b/src/pulsecore/start-child.h
@@ -1,8 +1,6 @@
#ifndef foopulsestartchildhfoo
#define foopulsestartchildhfoo
-/* $Id$ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/strbuf.c b/src/pulsecore/strbuf.c
index d9a6701..540faef 100644
--- a/src/pulsecore/strbuf.c
+++ b/src/pulsecore/strbuf.c
@@ -1,5 +1,3 @@
-/* $Id: strbuf.c 1971 2007-10-28 19:13:50Z lennart $ */
-
/***
This file is part of PulseAudio.
@@ -79,7 +77,7 @@ char *pa_strbuf_tostring(pa_strbuf *sb) {
pa_assert(sb);
- e = t = pa_xnew(char, sb->length+1);
+ e = t = pa_xmalloc(sb->length+1);
for (c = sb->head; c; c = c->next) {
pa_assert((size_t) (e-t) <= sb->length);
@@ -152,8 +150,8 @@ void pa_strbuf_putsn(pa_strbuf *sb, const char *t, size_t l) {
/* Append a printf() style formatted string to the string buffer. */
/* The following is based on an example from the GNU libc documentation */
-int pa_strbuf_printf(pa_strbuf *sb, const char *format, ...) {
- int size = 100;
+size_t pa_strbuf_printf(pa_strbuf *sb, const char *format, ...) {
+ size_t size = 100;
struct chunk *c = NULL;
pa_assert(sb);
@@ -170,14 +168,14 @@ int pa_strbuf_printf(pa_strbuf *sb, const char *format, ...) {
CHUNK_TO_TEXT(c)[size-1] = 0;
va_end(ap);
- if (r > -1 && r < size) {
- c->length = r;
+ if (r > -1 && (size_t) r < size) {
+ c->length = (size_t) r;
append(sb, c);
- return r;
+ return (size_t) r;
}
if (r > -1) /* glibc 2.1 */
- size = r+1;
+ size = (size_t) r+1;
else /* glibc 2.0 */
size *= 2;
}
diff --git a/src/pulsecore/strbuf.h b/src/pulsecore/strbuf.h
index ca402b7..ac68d7b 100644
--- a/src/pulsecore/strbuf.h
+++ b/src/pulsecore/strbuf.h
@@ -1,8 +1,6 @@
#ifndef foostrbufhfoo
#define foostrbufhfoo
-/* $Id: strbuf.h 1426 2007-02-13 15:35:19Z ossman $ */
-
/***
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;
@@ -33,7 +31,7 @@ void pa_strbuf_free(pa_strbuf *sb);
char *pa_strbuf_tostring(pa_strbuf *sb);
char *pa_strbuf_tostring_free(pa_strbuf *sb);
-int pa_strbuf_printf(pa_strbuf *sb, const char *format, ...) PA_GCC_PRINTF_ATTR(2,3);
+size_t pa_strbuf_printf(pa_strbuf *sb, const char *format, ...) PA_GCC_PRINTF_ATTR(2,3);
void pa_strbuf_puts(pa_strbuf *sb, const char *t);
void pa_strbuf_putsn(pa_strbuf *sb, const char *t, size_t m);
diff --git a/src/pulsecore/strlist.c b/src/pulsecore/strlist.c
index 6421f76..f587a2f 100644
--- a/src/pulsecore/strlist.c
+++ b/src/pulsecore/strlist.c
@@ -1,5 +1,3 @@
-/* $Id: strlist.c 1984 2007-10-29 20:30:15Z lennart $ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/strlist.h b/src/pulsecore/strlist.h
index 250a46c..1cb7537 100644
--- a/src/pulsecore/strlist.h
+++ b/src/pulsecore/strlist.h
@@ -1,8 +1,6 @@
#ifndef foostrlisthfoo
#define foostrlisthfoo
-/* $Id: strlist.h 1984 2007-10-29 20:30:15Z lennart $ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/tagstruct.c b/src/pulsecore/tagstruct.c
index 4fa3924..62a3014 100644
--- a/src/pulsecore/tagstruct.c
+++ b/src/pulsecore/tagstruct.c
@@ -1,5 +1,3 @@
-/* $Id: tagstruct.c 1971 2007-10-28 19:13:50Z lennart $ */
-
/***
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) {
@@ -154,18 +154,18 @@ void pa_tagstruct_put_arbitrary(pa_tagstruct *t, const void *p, size_t length) {
extend(t, 5+length);
t->data[t->length] = PA_TAG_ARBITRARY;
- tmp = htonl(length);
+ tmp = htonl((uint32_t) length);
memcpy(t->data+t->length+1, &tmp, 4);
if (length)
memcpy(t->data+t->length+5, p, 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);
- t->data[t->length] = b ? PA_TAG_BOOLEAN_TRUE : PA_TAG_BOOLEAN_FALSE;
+ t->data[t->length] = (uint8_t) (b ? PA_TAG_BOOLEAN_TRUE : PA_TAG_BOOLEAN_FALSE);
t->length += 1;
}
@@ -175,9 +175,9 @@ void pa_tagstruct_put_timeval(pa_tagstruct*t, const struct timeval *tv) {
extend(t, 9);
t->data[t->length] = PA_TAG_TIMEVAL;
- tmp = htonl(tv->tv_sec);
+ tmp = htonl((uint32_t) tv->tv_sec);
memcpy(t->data+t->length+1, &tmp, 4);
- tmp = htonl(tv->tv_usec);
+ tmp = htonl((uint32_t) tv->tv_usec);
memcpy(t->data+t->length+5, &tmp, 4);
t->length += 9;
}
@@ -228,7 +228,7 @@ void pa_tagstruct_put_channel_map(pa_tagstruct *t, const pa_channel_map *map) {
unsigned i;
pa_assert(t);
- extend(t, 2 + map->channels);
+ extend(t, 2 + (size_t) map->channels);
t->data[t->length++] = PA_TAG_CHANNEL_MAP;
t->data[t->length++] = map->channels;
@@ -254,6 +254,32 @@ 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;
@@ -379,7 +405,7 @@ const uint8_t* pa_tagstruct_data(pa_tagstruct*t, size_t *l) {
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);
@@ -387,9 +413,9 @@ int pa_tagstruct_get_boolean(pa_tagstruct*t, int *b) {
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;
@@ -409,9 +435,9 @@ int pa_tagstruct_get_timeval(pa_tagstruct*t, struct timeval *tv) {
return -1;
memcpy(&tv->tv_sec, t->data+t->rindex+1, 4);
- tv->tv_sec = ntohl(tv->tv_sec);
+ tv->tv_sec = (time_t) ntohl((uint32_t) tv->tv_sec);
memcpy(&tv->tv_usec, t->data+t->rindex+5, 4);
- tv->tv_usec = ntohl(tv->tv_usec);
+ tv->tv_usec = (suseconds_t) ntohl((uint32_t) tv->tv_usec);
t->rindex += 9;
return 0;
}
@@ -497,7 +523,7 @@ int pa_tagstruct_get_channel_map(pa_tagstruct *t, pa_channel_map *map) {
for (i = 0; i < map->channels; i ++)
map->map[i] = (int8_t) t->data[t->rindex + 2 + i];
- t->rindex += 2 + map->channels;
+ t->rindex += 2 + (size_t) map->channels;
return 0;
}
@@ -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 7739a4c..e7d0705 100644
--- a/src/pulsecore/tagstruct.h
+++ b/src/pulsecore/tagstruct.h
@@ -1,8 +1,6 @@
#ifndef footagstructhfoo
#define footagstructhfoo
-/* $Id: tagstruct.h 1426 2007-02-13 15:35:19Z ossman $ */
-
/***
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 9b87942..34f92a7 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,14 +66,24 @@ 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);
@@ -83,15 +91,22 @@ void pa_thread_mq_init(pa_thread_mq *q, pa_mainloop_api *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);
diff --git a/src/pulsecore/thread-mq.h b/src/pulsecore/thread-mq.h
index 13b6e01..3b5e0e7 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 78cfd30..ade398f 100644
--- a/src/pulsecore/thread-posix.c
+++ b/src/pulsecore/thread-posix.c
@@ -1,5 +1,3 @@
-/* $Id: thread-posix.c 1971 2007-10-28 19:13:50Z lennart $ */
-
/***
This file is part of PulseAudio.
@@ -43,6 +41,7 @@ struct pa_thread {
pa_thread_func_t thread_func;
void *userdata;
pa_atomic_t running;
+ pa_bool_t joined;
};
struct pa_tls {
@@ -84,6 +83,7 @@ pa_thread* pa_thread_new(pa_thread_func_t thread_func, void *userdata) {
t = pa_xnew(pa_thread, 1);
t->thread_func = thread_func;
t->userdata = userdata;
+ t->joined = FALSE;
pa_atomic_store(&t->running, 0);
if (pthread_create(&t->id, NULL, internal_thread_func, t) < 0) {
@@ -117,7 +117,12 @@ void pa_thread_free(pa_thread *t) {
int pa_thread_join(pa_thread *t) {
pa_assert(t);
+ pa_assert(t->thread_func);
+ if (t->joined)
+ return -1;
+
+ t->joined = TRUE;
return pthread_join(t->id, NULL);
}
@@ -134,6 +139,7 @@ pa_thread* pa_thread_self(void) {
t->id = pthread_self();
t->thread_func = NULL;
t->userdata = NULL;
+ t->joined = TRUE;
pa_atomic_store(&t->running, 2);
PA_STATIC_TLS_SET(current_thread, t);
@@ -194,4 +200,3 @@ void *pa_tls_set(pa_tls *t, void *userdata) {
pa_assert_se(pthread_setspecific(t->key, userdata) == 0);
return r;
}
-
diff --git a/src/pulsecore/thread-win32.c b/src/pulsecore/thread-win32.c
index 0163be1..c40d334 100644
--- a/src/pulsecore/thread-win32.c
+++ b/src/pulsecore/thread-win32.c
@@ -1,5 +1,3 @@
-/* $Id: thread-win32.c 1971 2007-10-28 19:13:50Z lennart $ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/thread.h b/src/pulsecore/thread.h
index 060d8ae..eabe9ba 100644
--- a/src/pulsecore/thread.h
+++ b/src/pulsecore/thread.h
@@ -1,8 +1,6 @@
#ifndef foopulsethreadhfoo
#define foopulsethreadhfoo
-/* $Id: thread.h 1971 2007-10-28 19:13:50Z lennart $ */
-
/***
This file is part of PulseAudio.
@@ -27,6 +25,7 @@
#include <pulse/def.h>
#include <pulsecore/once.h>
+#include <pulsecore/core-util.h>
#ifndef PACKAGE
#error "Please include config.h before including this file!"
@@ -71,6 +70,8 @@ void *pa_tls_set(pa_tls *t, void *userdata);
static void name##_tls_destructor(void) PA_GCC_DESTRUCTOR; \
static void name##_tls_destructor(void) { \
static void (*_free_cb)(void*) = free_cb; \
+ if (!pa_in_valgrind()) \
+ return; \
if (!name##_tls.tls) \
return; \
if (_free_cb) { \
@@ -88,7 +89,7 @@ void *pa_tls_set(pa_tls *t, void *userdata);
} \
struct __stupid_useless_struct_to_allow_trailing_semicolon
-#ifdef HAVE_TLS_BUILTIN
+#ifdef SUPPORT_TLS___THREAD
/* An optimized version of the above that requires no dynamic
* allocation if the compiler supports __thread */
#define PA_STATIC_TLS_DECLARE_NO_FREE(name) \
diff --git a/src/pulsecore/time-smoother.c b/src/pulsecore/time-smoother.c
index 1ef4bb1..6a2ffaa 100644
--- a/src/pulsecore/time-smoother.c
+++ b/src/pulsecore/time-smoother.c
@@ -1,5 +1,3 @@
-/* $Id: time-smoother.c 1977 2007-10-29 16:38:57Z lennart $ */
-
/***
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,25 +201,22 @@ 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;
for (j = s->n_history; j > 0; j--) {
- ax += s->history_x[i];
- ay += s->history_y[i];
+ ax += (int64_t) s->history_x[i];
+ ay += (int64_t) 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;
+ r = (double) k / (double) t;
+
+ 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;
- return s->monotonic && r < 0 ? 0 : r;
+ 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)/ (double) kx - dp - (double) (2*de))) / (double) kx;
+ s->a = (dp/(double) kx - 2*s->b - de/(double) kx) / (double) (3*kx);
+
+ s->abc_valid = TRUE;
}
static void estimate(pa_smoother *s, pa_usec_t x, pa_usec_t *y, double *deriv) {
@@ -230,7 +284,7 @@ static void estimate(pa_smoother *s, pa_usec_t x, pa_usec_t *y, double *deriv) {
/* The requested point is right of the point where we wanted
* to be on track again, thus just linearly estimate */
- t = (int64_t) s->py + (int64_t) (s->dp * (x - s->px));
+ t = (int64_t) s->py + (int64_t) llrint(s->dp * (double) (x - s->px));
if (t < 0)
t = 0;
@@ -241,60 +295,34 @@ static void estimate(pa_smoother *s, pa_usec_t x, pa_usec_t *y, double *deriv) {
*deriv = s->dp;
} else {
+ double tx, ty;
- 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 */
+ /* Ok, we're not yet on track, thus let's interpolate, and
+ * make sure that the first derivative is smooth */
- ex = s->ex; ey = s->ey;
- px = s->px; py = s->py;
- de = s->de; dp = s->dp;
+ calc_abc(s);
- 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;
- }
+ tx = (double) x;
/* Move to origin */
- x -= s->ex;
+ tx -= (double) s->ex;
/* Horner scheme */
- *y = (pa_usec_t) ((double) x * (s->c + (double) x * (s->b + (double) x * s->a)));
+ ty = (tx * (s->c + tx * (s->b + tx * s->a)));
/* Move back from origin */
- *y += s->ey;
+ ty += (double) s->ey;
+
+ *y = ty >= 0 ? (pa_usec_t) lrint(ty) : 0;
/* Horner scheme */
if (deriv)
- *deriv = s->c + ((double) x * (s->b*2 + (double) x * s->a*3));
+ *deriv = s->c + (tx * (s->b*2 + tx * s->a*3));
}
/* 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,23 +331,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;
- pa_assert(x >= s->time_offset);
- x -= s->time_offset;
+ x = PA_LIKELY(x >= s->time_offset) ? x - s->time_offset : 0;
- pa_assert(x >= s->ex);
+ is_new = x >= s->ex;
- /* 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;
+ 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);
@@ -328,28 +359,44 @@ 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 + (pa_usec_t) lrint(s->dp * (double) 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;
- pa_assert(x >= s->time_offset);
- x -= s->time_offset;
+ x = PA_LIKELY(x >= s->time_offset) ? x - s->time_offset : 0;
- pa_assert(x >= s->ex);
+ if (s->monotonic)
+ if (x <= s->last_x)
+ x = s->last_x;
estimate(s, x, &y, NULL);
+
+ if (s->monotonic) {
+
+ /* Make sure the querier doesn't jump forth and back. */
+ 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;
}
@@ -357,6 +404,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) {
@@ -365,6 +414,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;
}
@@ -375,8 +426,42 @@ void pa_smoother_resume(pa_smoother *s, pa_usec_t x) {
if (!s->paused)
return;
- pa_assert(x >= s->pause_time);
+ if (x < s->pause_time)
+ 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) lrint((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 ec6a018..2051e64 100644
--- a/src/pulsecore/time-smoother.h
+++ b/src/pulsecore/time-smoother.h
@@ -1,8 +1,6 @@
#ifndef foopulsetimesmootherhfoo
#define foopulsetimesmootherhfoo
-/* $Id: time-smoother.h 1971 2007-10-28 19:13:50Z lennart $ */
-
/***
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 bea0fdc..07a9f3a 100644
--- a/src/pulsecore/tokenizer.c
+++ b/src/pulsecore/tokenizer.c
@@ -1,5 +1,3 @@
-/* $Id: tokenizer.c 1971 2007-10-28 19:13:50Z lennart $ */
-
/***
This file is part of PulseAudio.
@@ -29,14 +27,14 @@
#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"
-static void token_free(void *p, PA_GCC_UNUSED void *userdata) {
+static void token_free(void *p, void *userdata) {
pa_xfree(p);
}
diff --git a/src/pulsecore/tokenizer.h b/src/pulsecore/tokenizer.h
index f1d3a95..d51cd73 100644
--- a/src/pulsecore/tokenizer.h
+++ b/src/pulsecore/tokenizer.h
@@ -1,8 +1,6 @@
#ifndef footokenizerhfoo
#define footokenizerhfoo
-/* $Id: tokenizer.h 1426 2007-02-13 15:35:19Z ossman $ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/x11prop.c b/src/pulsecore/x11prop.c
index 31343af..7f91ba3 100644
--- a/src/pulsecore/x11prop.c
+++ b/src/pulsecore/x11prop.c
@@ -1,5 +1,3 @@
-/* $Id: x11prop.c 1971 2007-10-28 19:13:50Z lennart $ */
-
/***
This file is part of PulseAudio.
@@ -34,7 +32,7 @@
void pa_x11_set_prop(Display *d, const char *name, const char *data) {
Atom a = XInternAtom(d, name, False);
- XChangeProperty(d, RootWindow(d, 0), a, XA_STRING, 8, PropModeReplace, (const unsigned char*) data, strlen(data)+1);
+ XChangeProperty(d, RootWindow(d, 0), a, XA_STRING, 8, PropModeReplace, (const unsigned char*) data, (int) (strlen(data)+1));
}
void pa_x11_del_prop(Display *d, const char *name) {
@@ -51,7 +49,7 @@ char* pa_x11_get_prop(Display *d, const char *name, char *p, size_t l) {
char *ret = NULL;
Atom a = XInternAtom(d, name, False);
- if (XGetWindowProperty(d, RootWindow(d, 0), a, 0, (l+2)/4, False, XA_STRING, &actual_type, &actual_format, &nitems, &nbytes_after, &prop) != Success)
+ if (XGetWindowProperty(d, RootWindow(d, 0), a, 0, (long) ((l+2)/4), False, XA_STRING, &actual_type, &actual_format, &nitems, &nbytes_after, &prop) != Success)
goto finish;
if (actual_type != XA_STRING)
diff --git a/src/pulsecore/x11prop.h b/src/pulsecore/x11prop.h
index c965345..c5998d3 100644
--- a/src/pulsecore/x11prop.h
+++ b/src/pulsecore/x11prop.h
@@ -1,8 +1,6 @@
#ifndef foox11prophfoo
#define foox11prophfoo
-/* $Id: x11prop.h 1426 2007-02-13 15:35:19Z ossman $ */
-
/***
This file is part of PulseAudio.
diff --git a/src/pulsecore/x11wrap.c b/src/pulsecore/x11wrap.c
index 79b8aa7..332ebb2 100644
--- a/src/pulsecore/x11wrap.c
+++ b/src/pulsecore/x11wrap.c
@@ -1,5 +1,3 @@
-/* $Id: x11wrap.c 1971 2007-10-28 19:13:50Z lennart $ */
-
/***
This file is part of PulseAudio.
@@ -31,7 +29,7 @@
#include <pulsecore/llist.h>
#include <pulsecore/log.h>
-#include <pulsecore/props.h>
+#include <pulsecore/shared.h>
#include <pulsecore/core-util.h>
#include <pulsecore/macro.h>
@@ -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,21 +71,27 @@ 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 */
-static void display_io_event(pa_mainloop_api *m, pa_io_event *e, int fd, PA_GCC_UNUSED pa_io_event_flags_t f, void *userdata) {
+static void display_io_event(pa_mainloop_api *m, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata) {
pa_x11_wrapper *w = userdata;
pa_assert(m);
@@ -113,7 +118,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) {
+static void internal_io_event(pa_mainloop_api *m, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata) {
pa_x11_wrapper *w = userdata;
pa_assert(m);
@@ -187,7 +192,7 @@ static pa_x11_wrapper* x11_wrapper_new(pa_core *c, const char *name, const char
XAddConnectionWatch(d, x11_watch, (XPointer) w);
- pa_assert_se(pa_property_set(c, w->property_name, w) >= 0);
+ pa_assert_se(pa_shared_set(c, w->property_name, w) >= 0);
return w;
}
@@ -195,7 +200,7 @@ static pa_x11_wrapper* x11_wrapper_new(pa_core *c, const char *name, const char
static void x11_wrapper_free(pa_x11_wrapper*w) {
pa_assert(w);
- pa_assert_se(pa_property_remove(w->core, w->property_name) >= 0);
+ pa_assert_se(pa_shared_remove(w->core, w->property_name) >= 0);
pa_assert(!w->clients);
@@ -218,8 +223,9 @@ pa_x11_wrapper* pa_x11_wrapper_get(pa_core *c, const char *name) {
pa_core_assert_ref(c);
- pa_snprintf(t, sizeof(t), "x11-wrapper%s%s", name ? "-" : "", name ? name : "");
- if ((w = pa_property_get(c, t)))
+ pa_snprintf(t, sizeof(t), "x11-wrapper%s%s", name ? "@" : "", name ? name : "");
+
+ if ((w = pa_shared_get(c, t)))
return pa_x11_wrapper_ref(w);
return x11_wrapper_new(c, name, t);
@@ -237,8 +243,10 @@ void pa_x11_wrapper_unref(pa_x11_wrapper* w) {
pa_assert(w);
pa_assert(PA_REFCNT_VALUE(w) >= 1);
- if (PA_REFCNT_DEC(w) <= 0)
- x11_wrapper_free(w);
+ if (PA_REFCNT_DEC(w) > 0)
+ return;
+
+ x11_wrapper_free(w);
}
Display *pa_x11_wrapper_get_display(pa_x11_wrapper *w) {
@@ -251,7 +259,24 @@ 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);
@@ -259,7 +284,8 @@ pa_x11_client* pa_x11_client_new(pa_x11_wrapper *w, int (*cb)(pa_x11_wrapper *w,
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);
diff --git a/src/pulsecore/x11wrap.h b/src/pulsecore/x11wrap.h
index 89dacef..badc3a1 100644
--- a/src/pulsecore/x11wrap.h
+++ b/src/pulsecore/x11wrap.h
@@ -1,8 +1,6 @@
#ifndef foox11wraphfoo
#define foox11wraphfoo
-/* $Id: x11wrap.h 1426 2007-02-13 15:35:19Z ossman $ */
-
/***
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);