summaryrefslogtreecommitdiffstats
path: root/src/pulsecore
diff options
context:
space:
mode:
Diffstat (limited to 'src/pulsecore')
-rw-r--r--src/pulsecore/anotify.c145
-rw-r--r--src/pulsecore/asyncmsgq.c303
-rw-r--r--src/pulsecore/asyncmsgq.h75
-rw-r--r--src/pulsecore/asyncq.c213
-rw-r--r--src/pulsecore/asyncq.h56
-rw-r--r--src/pulsecore/atomic.h193
-rw-r--r--src/pulsecore/authkey-prop.c49
-rw-r--r--src/pulsecore/authkey.c80
-rw-r--r--src/pulsecore/autoload.c43
-rw-r--r--src/pulsecore/avahi-wrap.c52
-rw-r--r--src/pulsecore/cli-command.c497
-rw-r--r--src/pulsecore/cli-text.c122
-rw-r--r--src/pulsecore/cli.c30
-rw-r--r--src/pulsecore/client.c25
-rw-r--r--src/pulsecore/conf-parser.c35
-rw-r--r--src/pulsecore/core-def.h5
-rw-r--r--src/pulsecore/core-error.c178
-rw-r--r--src/pulsecore/core-scache.c94
-rw-r--r--src/pulsecore/core-scache.h1
-rw-r--r--src/pulsecore/core-subscribe.c40
-rw-r--r--src/pulsecore/core-util.c639
-rw-r--r--src/pulsecore/core-util.h44
-rw-r--r--src/pulsecore/core.c73
-rw-r--r--src/pulsecore/core.h55
-rw-r--r--src/pulsecore/creds.h4
-rw-r--r--src/pulsecore/dynarray.c20
-rw-r--r--src/pulsecore/endianmacros.h78
-rw-r--r--src/pulsecore/esound.h2
-rw-r--r--src/pulsecore/fdsem.c276
-rw-r--r--src/pulsecore/fdsem.h49
-rw-r--r--src/pulsecore/ffmpeg/Makefile13
-rw-r--r--src/pulsecore/ffmpeg/avcodec.h82
-rw-r--r--src/pulsecore/ffmpeg/dsputil.h1
-rw-r--r--src/pulsecore/ffmpeg/resample2.c324
-rw-r--r--src/pulsecore/flist.c62
-rw-r--r--src/pulsecore/flist.h27
-rw-r--r--src/pulsecore/g711.c392
-rw-r--r--src/pulsecore/gccmacro.h25
-rw-r--r--src/pulsecore/hashmap.c39
-rw-r--r--src/pulsecore/hashmap.h4
-rw-r--r--src/pulsecore/hook-list.c26
-rw-r--r--src/pulsecore/idxset.c130
-rw-r--r--src/pulsecore/idxset.h5
-rw-r--r--src/pulsecore/inet_ntop.c4
-rw-r--r--src/pulsecore/iochannel.c168
-rw-r--r--src/pulsecore/iochannel.h18
-rw-r--r--src/pulsecore/ioline.c95
-rw-r--r--src/pulsecore/ipacl.c24
-rw-r--r--src/pulsecore/llist.h123
-rw-r--r--src/pulsecore/log.c29
-rw-r--r--src/pulsecore/ltdl-helper.c64
-rw-r--r--src/pulsecore/ltdl-helper.h34
-rw-r--r--src/pulsecore/macro.h149
-rw-r--r--src/pulsecore/mcalign.c49
-rw-r--r--src/pulsecore/memblock.c685
-rw-r--r--src/pulsecore/memblock.h65
-rw-r--r--src/pulsecore/memblockq.c331
-rw-r--r--src/pulsecore/memblockq.h22
-rw-r--r--src/pulsecore/memchunk.c52
-rw-r--r--src/pulsecore/memchunk.h11
-rw-r--r--src/pulsecore/modargs.c50
-rw-r--r--src/pulsecore/modargs.h4
-rw-r--r--src/pulsecore/modinfo.c31
-rw-r--r--src/pulsecore/modinfo.h2
-rw-r--r--src/pulsecore/module.c122
-rw-r--r--src/pulsecore/module.h10
-rw-r--r--src/pulsecore/msgobject.c49
-rw-r--r--src/pulsecore/msgobject.h54
-rw-r--r--src/pulsecore/mutex-posix.c70
-rw-r--r--src/pulsecore/mutex-win32.c2
-rw-r--r--src/pulsecore/mutex.h10
-rw-r--r--src/pulsecore/namereg.c35
-rw-r--r--src/pulsecore/native-common.h5
-rw-r--r--src/pulsecore/object.c72
-rw-r--r--src/pulsecore/object.h106
-rw-r--r--src/pulsecore/once-posix.c71
-rw-r--r--src/pulsecore/once-win32.c69
-rw-r--r--src/pulsecore/once.c96
-rw-r--r--src/pulsecore/once.h48
-rw-r--r--src/pulsecore/packet.c28
-rw-r--r--src/pulsecore/packet.h4
-rw-r--r--src/pulsecore/parseaddr.c14
-rw-r--r--src/pulsecore/pdispatch.c73
-rw-r--r--src/pulsecore/pid.c74
-rw-r--r--src/pulsecore/pipe.c16
-rw-r--r--src/pulsecore/play-memblockq.c194
-rw-r--r--src/pulsecore/play-memblockq.h10
-rw-r--r--src/pulsecore/play-memchunk.c160
-rw-r--r--src/pulsecore/poll.c2
-rw-r--r--src/pulsecore/props.c43
-rw-r--r--src/pulsecore/protocol-cli.c22
-rw-r--r--src/pulsecore/protocol-esound.c640
-rw-r--r--src/pulsecore/protocol-http.c39
-rw-r--r--src/pulsecore/protocol-native.c1651
-rw-r--r--src/pulsecore/protocol-simple.c368
-rw-r--r--src/pulsecore/pstream-util.c24
-rw-r--r--src/pulsecore/pstream.c367
-rw-r--r--src/pulsecore/pstream.h8
-rw-r--r--src/pulsecore/queue.c52
-rw-r--r--src/pulsecore/random.c21
-rw-r--r--src/pulsecore/refcnt.h10
-rw-r--r--src/pulsecore/resampler.c1099
-rw-r--r--src/pulsecore/resampler.h31
-rw-r--r--src/pulsecore/rtclock.c98
-rw-r--r--src/pulsecore/rtclock.h43
-rw-r--r--src/pulsecore/rtpoll.c751
-rw-r--r--src/pulsecore/rtpoll.h116
-rw-r--r--src/pulsecore/rtsig.c133
-rw-r--r--src/pulsecore/rtsig.h41
-rw-r--r--src/pulsecore/sample-util.c288
-rw-r--r--src/pulsecore/sample-util.h10
-rw-r--r--src/pulsecore/sconv-s16be.c7
-rw-r--r--src/pulsecore/sconv-s16be.h15
-rw-r--r--src/pulsecore/sconv-s16le.c65
-rw-r--r--src/pulsecore/sconv-s16le.h15
-rw-r--r--src/pulsecore/sconv.c273
-rw-r--r--src/pulsecore/sconv.h10
-rw-r--r--src/pulsecore/semaphore-posix.c69
-rw-r--r--src/pulsecore/semaphore-win32.c65
-rw-r--r--src/pulsecore/semaphore.h (renamed from src/pulsecore/anotify.h)19
-rw-r--r--src/pulsecore/shm.c247
-rw-r--r--src/pulsecore/shm.h2
-rw-r--r--src/pulsecore/sink-input.c832
-rw-r--r--src/pulsecore/sink-input.h163
-rw-r--r--src/pulsecore/sink.c984
-rw-r--r--src/pulsecore/sink.h161
-rw-r--r--src/pulsecore/sioman.c14
-rw-r--r--src/pulsecore/socket-client.c180
-rw-r--r--src/pulsecore/socket-server.c121
-rw-r--r--src/pulsecore/socket-util.c118
-rw-r--r--src/pulsecore/socket-util.h5
-rw-r--r--src/pulsecore/sound-file-stream.c249
-rw-r--r--src/pulsecore/sound-file.c71
-rw-r--r--src/pulsecore/source-output.c376
-rw-r--r--src/pulsecore/source-output.h97
-rw-r--r--src/pulsecore/source.c560
-rw-r--r--src/pulsecore/source.h140
-rw-r--r--src/pulsecore/speex/Makefile13
-rw-r--r--src/pulsecore/speex/arch.h197
-rw-r--r--src/pulsecore/speex/fixed_generic.h106
-rw-r--r--src/pulsecore/speex/resample.c1114
-rw-r--r--src/pulsecore/speex/speex_resampler.h328
-rw-r--r--src/pulsecore/speexwrap.h48
-rw-r--r--src/pulsecore/strbuf.c49
-rw-r--r--src/pulsecore/strlist.c39
-rw-r--r--src/pulsecore/tagstruct.c125
-rw-r--r--src/pulsecore/thread-mq.c112
-rw-r--r--src/pulsecore/thread-mq.h49
-rw-r--r--src/pulsecore/thread-posix.c59
-rw-r--r--src/pulsecore/thread-win32.c19
-rw-r--r--src/pulsecore/thread.h61
-rw-r--r--src/pulsecore/time-smoother.c378
-rw-r--r--src/pulsecore/time-smoother.h43
-rw-r--r--src/pulsecore/tokenizer.c34
-rw-r--r--src/pulsecore/winsock.h2
-rw-r--r--src/pulsecore/x11prop.c1
-rw-r--r--src/pulsecore/x11wrap.c92
157 files changed, 16134 insertions, 5458 deletions
diff --git a/src/pulsecore/anotify.c b/src/pulsecore/anotify.c
deleted file mode 100644
index 25c5fe7d..00000000
--- a/src/pulsecore/anotify.c
+++ /dev/null
@@ -1,145 +0,0 @@
-/* $Id$ */
-
-/***
- This file is part of PulseAudio.
-
- Copyright 2006 Lennart Poettering
-
- PulseAudio is free software; you can redistribute it and/or modify
- it under the terms of the GNU Lesser General Public License as
- published by the Free Software Foundation; either version 2.1 of the
- License, or (at your option) any later version.
-
- PulseAudio is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with PulseAudio; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
- USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <assert.h>
-#include <unistd.h>
-#include <fcntl.h>
-
-#include <pulse/xmalloc.h>
-
-#include "anotify.h"
-
-#define EVENTS_MAX 16
-
-struct pa_anotify {
- pa_mainloop_api *api;
- pa_anotify_cb_t callback;
- void *userdata;
- int fds[2];
- pa_io_event *io_event;
- pa_defer_event *defer_event;
-
- uint8_t queued_events[EVENTS_MAX];
- unsigned n_queued_events, queue_index;
-};
-
-static void dispatch_event(pa_anotify *a) {
- assert(a);
- assert(a->queue_index < a->n_queued_events);
-
- a->callback(a->queued_events[a->queue_index++], a->userdata);
-
- if (a->queue_index >= a->n_queued_events) {
- a->n_queued_events = 0;
- a->queue_index = 0;
-
- a->api->io_enable(a->io_event, PA_IO_EVENT_INPUT);
- a->api->defer_enable(a->defer_event, 0);
- } else {
- a->api->io_enable(a->io_event, 0);
- a->api->defer_enable(a->defer_event, 1);
- }
-}
-
-static void io_callback(
- pa_mainloop_api *api,
- pa_io_event *e,
- int fd,
- pa_io_event_flags_t events,
- void *userdata) {
-
- pa_anotify *a = userdata;
- ssize_t r;
-
- assert(a);
- assert(events == PA_IO_EVENT_INPUT);
- assert(a->n_queued_events == 0);
-
- r = read(fd, a->queued_events, sizeof(a->queued_events));
- assert(r > 0);
-
- a->n_queued_events = (unsigned) r;
- a->queue_index = 0;
-
- /* Only dispatch a single event */
- dispatch_event(a);
-}
-
-static void defer_callback(pa_mainloop_api *api, pa_defer_event *e, void *userdata) {
- pa_anotify *a = userdata;
- assert(a);
-
- dispatch_event(a);
-}
-
-pa_anotify *pa_anotify_new(pa_mainloop_api*api, pa_anotify_cb_t cb, void *userdata) {
- pa_anotify *a;
-
- assert(api);
- assert(cb);
-
- a = pa_xnew(pa_anotify, 1);
-
- if (pipe(a->fds) < 0) {
- pa_xfree(a);
- return NULL;
- }
-
- a->api = api;
- a->callback = cb;
- a->userdata = userdata;
-
- a->io_event = api->io_new(api, a->fds[0], PA_IO_EVENT_INPUT, io_callback, a);
- a->defer_event = api->defer_new(api, defer_callback, a);
- a->api->defer_enable(a->defer_event, 0);
-
- a->n_queued_events = 0;
-
- return a;
-}
-
-void pa_anotify_free(pa_anotify *a) {
- assert(a);
-
- a->api->io_free(a->io_event);
- a->api->defer_free(a->defer_event);
-
- if (a->fds[0] >= 0)
- close(a->fds[0]);
- if (a->fds[1] >= 0)
- close(a->fds[1]);
-
- pa_xfree(a);
-}
-
-int pa_anotify_signal(pa_anotify *a, uint8_t event) {
- ssize_t r;
- assert(a);
-
- r = write(a->fds[1], &event, 1);
- return r != 1 ? -1 : 0;
-}
diff --git a/src/pulsecore/asyncmsgq.c b/src/pulsecore/asyncmsgq.c
new file mode 100644
index 00000000..96b43a71
--- /dev/null
+++ b/src/pulsecore/asyncmsgq.c
@@ -0,0 +1,303 @@
+/* $Id$ */
+
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2006 Lennart Poettering
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <unistd.h>
+#include <errno.h>
+
+#include <pulsecore/atomic.h>
+#include <pulsecore/log.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/semaphore.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/flist.h>
+#include <pulse/xmalloc.h>
+
+#include "asyncmsgq.h"
+
+PA_STATIC_FLIST_DECLARE(asyncmsgq, 0, pa_xfree);
+PA_STATIC_FLIST_DECLARE(semaphores, 0, (void(*)(void*)) pa_semaphore_free);
+
+struct asyncmsgq_item {
+ int code;
+ pa_msgobject *object;
+ void *userdata;
+ pa_free_cb_t free_cb;
+ int64_t offset;
+ pa_memchunk memchunk;
+ pa_semaphore *semaphore;
+ int ret;
+};
+
+struct pa_asyncmsgq {
+ PA_REFCNT_DECLARE;
+ pa_asyncq *asyncq;
+ pa_mutex *mutex; /* only for the writer side */
+
+ struct asyncmsgq_item *current;
+};
+
+pa_asyncmsgq *pa_asyncmsgq_new(unsigned size) {
+ pa_asyncmsgq *a;
+
+ a = pa_xnew(pa_asyncmsgq, 1);
+
+ PA_REFCNT_INIT(a);
+ pa_assert_se(a->asyncq = pa_asyncq_new(size));
+ pa_assert_se(a->mutex = pa_mutex_new(FALSE, TRUE));
+ a->current = NULL;
+
+ return a;
+}
+
+static void asyncmsgq_free(pa_asyncmsgq *a) {
+ struct asyncmsgq_item *i;
+ pa_assert(a);
+
+ while ((i = pa_asyncq_pop(a->asyncq, 0))) {
+
+ pa_assert(!i->semaphore);
+
+ if (i->object)
+ pa_msgobject_unref(i->object);
+
+ if (i->memchunk.memblock)
+ pa_memblock_unref(i->memchunk.memblock);
+
+ if (i->free_cb)
+ i->free_cb(i->userdata);
+
+ if (pa_flist_push(PA_STATIC_FLIST_GET(asyncmsgq), i) < 0)
+ pa_xfree(i);
+ }
+
+ pa_asyncq_free(a->asyncq, NULL);
+ pa_mutex_free(a->mutex);
+ pa_xfree(a);
+}
+
+pa_asyncmsgq* pa_asyncmsgq_ref(pa_asyncmsgq *q) {
+ pa_assert(PA_REFCNT_VALUE(q) > 0);
+
+ PA_REFCNT_INC(q);
+ return q;
+}
+
+void pa_asyncmsgq_unref(pa_asyncmsgq* q) {
+ pa_assert(PA_REFCNT_VALUE(q) > 0);
+
+ if (PA_REFCNT_DEC(q) <= 0)
+ asyncmsgq_free(q);
+}
+
+void pa_asyncmsgq_post(pa_asyncmsgq *a, pa_msgobject *object, int code, const void *userdata, int64_t offset, const pa_memchunk *chunk, pa_free_cb_t free_cb) {
+ struct asyncmsgq_item *i;
+ pa_assert(PA_REFCNT_VALUE(a) > 0);
+
+ if (!(i = pa_flist_pop(PA_STATIC_FLIST_GET(asyncmsgq))))
+ i = pa_xnew(struct asyncmsgq_item, 1);
+
+ i->code = code;
+ i->object = object ? pa_msgobject_ref(object) : NULL;
+ i->userdata = (void*) userdata;
+ i->free_cb = free_cb;
+ i->offset = offset;
+ if (chunk) {
+ pa_assert(chunk->memblock);
+ i->memchunk = *chunk;
+ pa_memblock_ref(i->memchunk.memblock);
+ } else
+ pa_memchunk_reset(&i->memchunk);
+ i->semaphore = NULL;
+
+ /* 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_mutex_unlock(a->mutex);
+}
+
+int pa_asyncmsgq_send(pa_asyncmsgq *a, pa_msgobject *object, int code, const void *userdata, int64_t offset, const pa_memchunk *chunk) {
+ struct asyncmsgq_item i;
+ pa_assert(PA_REFCNT_VALUE(a) > 0);
+
+ i.code = code;
+ i.object = object;
+ i.userdata = (void*) userdata;
+ i.free_cb = NULL;
+ i.ret = -1;
+ i.offset = offset;
+ if (chunk) {
+ pa_assert(chunk->memblock);
+ i.memchunk = *chunk;
+ } else
+ pa_memchunk_reset(&i.memchunk);
+
+ if (!(i.semaphore = pa_flist_pop(PA_STATIC_FLIST_GET(semaphores))))
+ i.semaphore = pa_semaphore_new(0);
+
+ pa_assert_se(i.semaphore);
+
+ /* 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_mutex_unlock(a->mutex);
+
+ pa_semaphore_wait(i.semaphore);
+
+ if (pa_flist_push(PA_STATIC_FLIST_GET(semaphores), i.semaphore) < 0)
+ pa_semaphore_free(i.semaphore);
+
+ 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) {
+ pa_assert(PA_REFCNT_VALUE(a) > 0);
+ pa_assert(!a->current);
+
+ if (!(a->current = pa_asyncq_pop(a->asyncq, wait))) {
+/* pa_log("failure"); */
+ return -1;
+ }
+
+/* pa_log("success"); */
+
+ if (code)
+ *code = a->current->code;
+ if (userdata)
+ *userdata = a->current->userdata;
+ if (offset)
+ *offset = a->current->offset;
+ if (object) {
+ if ((*object = a->current->object))
+ pa_msgobject_assert_ref(*object);
+ }
+ if (chunk)
+ *chunk = a->current->memchunk;
+
+/* pa_log_debug("Get q=%p object=%p (%s) code=%i data=%p chunk.length=%lu", (void*) a, (void*) a->current->object, a->current->object ? a->current->object->parent.type_name : NULL, a->current->code, (void*) a->current->userdata, (unsigned long) a->current->memchunk.length); */
+
+ return 0;
+}
+
+void pa_asyncmsgq_done(pa_asyncmsgq *a, int ret) {
+ pa_assert(PA_REFCNT_VALUE(a) > 0);
+ pa_assert(a);
+ pa_assert(a->current);
+
+ if (a->current->semaphore) {
+ a->current->ret = ret;
+ pa_semaphore_post(a->current->semaphore);
+ } else {
+
+ if (a->current->free_cb)
+ a->current->free_cb(a->current->userdata);
+
+ if (a->current->object)
+ pa_msgobject_unref(a->current->object);
+
+ if (a->current->memchunk.memblock)
+ pa_memblock_unref(a->current->memchunk.memblock);
+
+ if (pa_flist_push(PA_STATIC_FLIST_GET(asyncmsgq), a->current) < 0)
+ pa_xfree(a->current);
+ }
+
+ a->current = NULL;
+}
+
+int pa_asyncmsgq_wait_for(pa_asyncmsgq *a, int code) {
+ int c;
+ pa_assert(PA_REFCNT_VALUE(a) > 0);
+
+ pa_asyncmsgq_ref(a);
+
+ do {
+ pa_msgobject *o;
+ void *data;
+ int64_t offset;
+ pa_memchunk chunk;
+ int ret;
+
+ if (pa_asyncmsgq_get(a, &o, &c, &data, &offset, &chunk, 1) < 0)
+ return -1;
+
+ ret = pa_asyncmsgq_dispatch(o, c, data, offset, &chunk);
+ pa_asyncmsgq_done(a, ret);
+
+ } while (c != code);
+
+ pa_asyncmsgq_unref(a);
+
+ return 0;
+}
+
+int pa_asyncmsgq_process_one(pa_asyncmsgq *a) {
+ pa_msgobject *object;
+ int code;
+ void *data;
+ pa_memchunk chunk;
+ int64_t offset;
+ int ret;
+
+ pa_assert(PA_REFCNT_VALUE(a) > 0);
+
+ if (pa_asyncmsgq_get(a, &object, &code, &data, &offset, &chunk, 0) < 0)
+ return 0;
+
+ pa_asyncmsgq_ref(a);
+ ret = pa_asyncmsgq_dispatch(object, code, data, offset, &chunk);
+ pa_asyncmsgq_done(a, ret);
+ pa_asyncmsgq_unref(a);
+
+ return 1;
+}
+
+int pa_asyncmsgq_get_fd(pa_asyncmsgq *a) {
+ pa_assert(PA_REFCNT_VALUE(a) > 0);
+
+ return pa_asyncq_get_fd(a->asyncq);
+}
+
+int pa_asyncmsgq_before_poll(pa_asyncmsgq *a) {
+ pa_assert(PA_REFCNT_VALUE(a) > 0);
+
+ return pa_asyncq_before_poll(a->asyncq);
+}
+
+void pa_asyncmsgq_after_poll(pa_asyncmsgq *a) {
+ pa_assert(PA_REFCNT_VALUE(a) > 0);
+
+ pa_asyncq_after_poll(a->asyncq);
+}
+
+int pa_asyncmsgq_dispatch(pa_msgobject *object, int code, void *userdata, int64_t offset, pa_memchunk *memchunk) {
+
+ if (object)
+ return object->process_msg(object, code, userdata, offset, memchunk);
+
+ return 0;
+}
diff --git a/src/pulsecore/asyncmsgq.h b/src/pulsecore/asyncmsgq.h
new file mode 100644
index 00000000..5d3867ba
--- /dev/null
+++ b/src/pulsecore/asyncmsgq.h
@@ -0,0 +1,75 @@
+#ifndef foopulseasyncmsgqhfoo
+#define foopulseasyncmsgqhfoo
+
+/* $Id$ */
+
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2004-2006 Lennart Poettering
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <sys/types.h>
+
+#include <pulsecore/asyncq.h>
+#include <pulsecore/memchunk.h>
+#include <pulsecore/msgobject.h>
+
+/* A simple asynchronous message queue, based on pa_asyncq. In
+ * contrast to pa_asyncq this one is multiple-writer safe, though
+ * still not multiple-reader safe. This queue is intended to be used
+ * for controlling real-time threads from normal-priority
+ * threads. Multiple-writer-safety is accomplished by using a mutex on
+ * the writer side. This queue is thus not useful for communication
+ * between several real-time threads.
+ *
+ * The queue takes messages consisting of:
+ * "Object" for which this messages is intended (may be NULL)
+ * A numeric message code
+ * Arbitrary userdata pointer (may be NULL)
+ * A memchunk (may be NULL)
+ *
+ * There are two functions for submitting messages: _post and
+ * _send. The former just enqueues the message asynchronously, the
+ * latter waits for completion, synchronously. */
+
+enum {
+ PA_MESSAGE_SHUTDOWN = -1/* A generic message to inform the handler of this queue to quit */
+};
+
+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_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);
+
+#endif
diff --git a/src/pulsecore/asyncq.c b/src/pulsecore/asyncq.c
new file mode 100644
index 00000000..75b15c0e
--- /dev/null
+++ b/src/pulsecore/asyncq.c
@@ -0,0 +1,213 @@
+/* $Id$ */
+
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2006 Lennart Poettering
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <unistd.h>
+#include <errno.h>
+
+#include <pulsecore/atomic.h>
+#include <pulsecore/log.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/core-util.h>
+#include <pulse/xmalloc.h>
+
+#include "asyncq.h"
+#include "fdsem.h"
+
+#define ASYNCQ_SIZE 128
+
+/* For debugging purposes we can define _Y to put and extra thread
+ * yield between each operation. */
+
+/* #define PROFILE */
+
+#ifdef PROFILE
+#define _Y pa_thread_yield()
+#else
+#define _Y do { } while(0)
+#endif
+
+struct pa_asyncq {
+ unsigned size;
+ unsigned read_idx;
+ unsigned write_idx;
+ pa_fdsem *read_fdsem, *write_fdsem;
+};
+
+#define PA_ASYNCQ_CELLS(x) ((pa_atomic_ptr_t*) ((uint8_t*) (x) + PA_ALIGN(sizeof(struct pa_asyncq))))
+
+static int is_power_of_two(unsigned size) {
+ return !(size & (size - 1));
+}
+
+static int reduce(pa_asyncq *l, int value) {
+ return value & (unsigned) (l->size - 1);
+}
+
+pa_asyncq *pa_asyncq_new(unsigned size) {
+ pa_asyncq *l;
+
+ if (!size)
+ size = ASYNCQ_SIZE;
+
+ pa_assert(is_power_of_two(size));
+
+ l = pa_xmalloc0(PA_ALIGN(sizeof(pa_asyncq)) + (sizeof(pa_atomic_ptr_t) * size));
+
+ l->size = size;
+
+ if (!(l->read_fdsem = pa_fdsem_new())) {
+ pa_xfree(l);
+ return NULL;
+ }
+
+ if (!(l->write_fdsem = pa_fdsem_new())) {
+ pa_fdsem_free(l->read_fdsem);
+ pa_xfree(l);
+ return NULL;
+ }
+
+ return l;
+}
+
+void pa_asyncq_free(pa_asyncq *l, pa_free_cb_t free_cb) {
+ pa_assert(l);
+
+ if (free_cb) {
+ void *p;
+
+ while ((p = pa_asyncq_pop(l, 0)))
+ free_cb(p);
+ }
+
+ 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;
+ pa_atomic_ptr_t *cells;
+
+ pa_assert(l);
+ pa_assert(p);
+
+ cells = PA_ASYNCQ_CELLS(l);
+
+ _Y;
+ idx = reduce(l, l->write_idx);
+
+ if (!pa_atomic_ptr_cmpxchg(&cells[idx], NULL, p)) {
+
+ if (!wait)
+ return -1;
+
+/* pa_log("sleeping on push"); */
+
+ do {
+ pa_fdsem_wait(l->read_fdsem);
+ } while (!pa_atomic_ptr_cmpxchg(&cells[idx], NULL, p));
+ }
+
+ _Y;
+ l->write_idx++;
+
+ pa_fdsem_post(l->write_fdsem);
+
+ return 0;
+}
+
+void* pa_asyncq_pop(pa_asyncq*l, int wait) {
+ int idx;
+ void *ret;
+ pa_atomic_ptr_t *cells;
+
+ pa_assert(l);
+
+ cells = PA_ASYNCQ_CELLS(l);
+
+ _Y;
+ idx = reduce(l, l->read_idx);
+
+ if (!(ret = pa_atomic_ptr_load(&cells[idx]))) {
+
+ if (!wait)
+ return NULL;
+
+/* pa_log("sleeping on pop"); */
+
+ do {
+ pa_fdsem_wait(l->write_fdsem);
+ } while (!(ret = pa_atomic_ptr_load(&cells[idx])));
+ }
+
+ pa_assert(ret);
+
+ /* Guaranteed to succeed if we only have a single reader */
+ pa_assert_se(pa_atomic_ptr_cmpxchg(&cells[idx], ret, NULL));
+
+ _Y;
+ l->read_idx++;
+
+ pa_fdsem_post(l->read_fdsem);
+
+ return ret;
+}
+
+int pa_asyncq_get_fd(pa_asyncq *q) {
+ pa_assert(q);
+
+ return pa_fdsem_get(q->write_fdsem);
+}
+
+int pa_asyncq_before_poll(pa_asyncq *l) {
+ int idx;
+ pa_atomic_ptr_t *cells;
+
+ pa_assert(l);
+
+ cells = PA_ASYNCQ_CELLS(l);
+
+ _Y;
+ idx = reduce(l, l->read_idx);
+
+ for (;;) {
+ if (pa_atomic_ptr_load(&cells[idx]))
+ return -1;
+
+ if (pa_fdsem_before_poll(l->write_fdsem) >= 0)
+ return 0;
+ }
+
+ return 0;
+}
+
+void pa_asyncq_after_poll(pa_asyncq *l) {
+ pa_assert(l);
+
+ pa_fdsem_after_poll(l->write_fdsem);
+}
diff --git a/src/pulsecore/asyncq.h b/src/pulsecore/asyncq.h
new file mode 100644
index 00000000..53d45866
--- /dev/null
+++ b/src/pulsecore/asyncq.h
@@ -0,0 +1,56 @@
+#ifndef foopulseasyncqhfoo
+#define foopulseasyncqhfoo
+
+/* $Id$ */
+
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2004-2006 Lennart Poettering
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <sys/types.h>
+#include <pulse/def.h>
+
+/* A simple, asynchronous, lock-free (if requested also wait-free)
+ * queue. Not multiple-reader/multiple-writer safe. If that is
+ * required both sides can be protected by a mutex each. --- Which is
+ * not a bad thing in most cases, since this queue is intended for
+ * communication between a normal thread and a single real-time
+ * thread. Only the real-time side needs to be lock-free/wait-free.
+ *
+ * If the queue is full and another entry shall be pushed, or when the
+ * queue is empty and another entry shall be popped and the "wait"
+ * argument is non-zero, the queue will block on a UNIX FIFO object --
+ * that will probably require locking on the kernel side -- which
+ * however is probably not problematic, because we do it only on
+ * starvation or overload in which case we have to block anyway. */
+
+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);
+
+int pa_asyncq_get_fd(pa_asyncq *q);
+int pa_asyncq_before_poll(pa_asyncq *a);
+void pa_asyncq_after_poll(pa_asyncq *a);
+
+#endif
diff --git a/src/pulsecore/atomic.h b/src/pulsecore/atomic.h
index 013e8c20..c2c99888 100644
--- a/src/pulsecore/atomic.h
+++ b/src/pulsecore/atomic.h
@@ -24,48 +24,201 @@
USA.
***/
-#include <atomic_ops.h>
-
-/* atomic_ops guarantees us that sizeof(AO_t) == sizeof(void*).
+/*
+ * atomic_ops guarantees us that sizeof(AO_t) == sizeof(void*). It is
+ * not guaranteed however, that sizeof(AO_t) == sizeof(size_t).
+ * however very likely.
+ *
+ * For now we do only full memory barriers. Eventually we might want
+ * to support more elaborate memory barriers, in which case we will add
+ * suffixes to the function names.
*
- * It is not guaranteed however, that sizeof(AO_t) == sizeof(size_t).
- * however very likely. */
+ * On gcc >= 4.1 we use the builtin atomic functions. otherwise we use
+ * libatomic_ops
+ */
-typedef struct pa_atomic_int {
- volatile AO_t value;
-} pa_atomic_int_t;
+#ifndef PACKAGE
+#error "Please include config.h before including this file!"
+#endif
+
+#ifdef HAVE_ATOMIC_BUILTINS
+
+/* __sync based implementation */
+
+typedef struct pa_atomic {
+ volatile int value;
+} pa_atomic_t;
#define PA_ATOMIC_INIT(v) { .value = (v) }
-/* For now we do only full memory barriers. Eventually we might want
- * to support more elaborate memory barriers, in which case we will add
- * suffixes to the function names */
+static inline int pa_atomic_load(const pa_atomic_t *a) {
+ __sync_synchronize();
+ return a->value;
+}
+
+static inline void pa_atomic_store(pa_atomic_t *a, int i) {
+ a->value = i;
+ __sync_synchronize();
+}
+
+/* Returns the previously set value */
+static inline int pa_atomic_add(pa_atomic_t *a, int i) {
+ return __sync_fetch_and_add(&a->value, i);
+}
+
+/* Returns the previously set value */
+static inline int pa_atomic_sub(pa_atomic_t *a, int i) {
+ return __sync_fetch_and_sub(&a->value, i);
+}
+
+/* Returns the previously set value */
+static inline int pa_atomic_inc(pa_atomic_t *a) {
+ return pa_atomic_add(a, 1);
+}
+
+/* Returns the previously set value */
+static inline int pa_atomic_dec(pa_atomic_t *a) {
+ return pa_atomic_sub(a, 1);
+}
+
+/* Returns non-zero when the operation was successful. */
+static inline int pa_atomic_cmpxchg(pa_atomic_t *a, int old_i, int new_i) {
+ return __sync_bool_compare_and_swap(&a->value, old_i, new_i);
+}
+
+typedef struct pa_atomic_ptr {
+ volatile unsigned long value;
+} pa_atomic_ptr_t;
+
+#define PA_ATOMIC_PTR_INIT(v) { .value = (long) (v) }
+
+static inline void* pa_atomic_ptr_load(const pa_atomic_ptr_t *a) {
+ __sync_synchronize();
+ return (void*) a->value;
+}
+
+static inline void pa_atomic_ptr_store(pa_atomic_ptr_t *a, void *p) {
+ a->value = (unsigned long) p;
+ __sync_synchronize();
+}
+
+static inline int 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."
+
+/* Addapted from glibc */
+
+typedef struct pa_atomic {
+ volatile int value;
+} pa_atomic_t;
+
+#define PA_ATOMIC_INIT(v) { .value = (v) }
+
+static inline int pa_atomic_load(const pa_atomic_t *a) {
+ return a->value;
+}
+
+static inline void pa_atomic_store(pa_atomic_t *a, int i) {
+ a->value = i;
+}
+
+static inline int pa_atomic_add(pa_atomic_t *a, int i) {
+ int result;
+
+ __asm __volatile ("lock; xaddl %0, %1"
+ : "=r" (result), "=m" (a->value)
+ : "0" (i), "m" (a->value));
+
+ return result;
+}
+
+static inline int pa_atomic_sub(pa_atomic_t *a, int i) {
+ return pa_atomic_add(a, -i);
+}
+
+static inline int pa_atomic_inc(pa_atomic_t *a) {
+ return pa_atomic_add(a, 1);
+}
-static inline int pa_atomic_load(const pa_atomic_int_t *a) {
+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) {
+ 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;
+}
+
+typedef struct pa_atomic_ptr {
+ volatile unsigned long value;
+} pa_atomic_ptr_t;
+
+#define PA_ATOMIC_PTR_INIT(v) { .value = (long) (v) }
+
+static inline void* pa_atomic_ptr_load(const pa_atomic_ptr_t *a) {
+ return (void*) a->value;
+}
+
+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) {
+ void *result;
+
+ __asm__ __volatile__ ("lock; cmpxchgq %q2, %1"
+ : "=a" (result), "=m" (a->value)
+ : "r" (new_p), "m" (a->value), "0" (old_p));
+
+ return result;
+}
+
+#else
+
+/* libatomic_ops based implementation */
+
+#include <atomic_ops.h>
+
+typedef struct pa_atomic {
+ volatile AO_t value;
+} pa_atomic_t;
+
+#define PA_ATOMIC_INIT(v) { .value = (v) }
+
+static inline int pa_atomic_load(const pa_atomic_t *a) {
return (int) AO_load_full((AO_t*) &a->value);
}
-static inline void pa_atomic_store(pa_atomic_int_t *a, int i) {
+static inline void pa_atomic_store(pa_atomic_t *a, int i) {
AO_store_full(&a->value, (AO_t) i);
}
-static inline int pa_atomic_add(pa_atomic_int_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);
}
-static inline int pa_atomic_sub(pa_atomic_int_t *a, int i) {
+static inline int pa_atomic_sub(pa_atomic_t *a, int i) {
return AO_fetch_and_add_full(&a->value, (AO_t) -i);
}
-static inline int pa_atomic_inc(pa_atomic_int_t *a) {
+static inline int pa_atomic_inc(pa_atomic_t *a) {
return AO_fetch_and_add1_full(&a->value);
}
-static inline int pa_atomic_dec(pa_atomic_int_t *a) {
+static inline int pa_atomic_dec(pa_atomic_t *a) {
return AO_fetch_and_sub1_full(&a->value);
}
-static inline int pa_atomic_cmpxchg(pa_atomic_int_t *a, int old_i, int new_i) {
+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);
}
@@ -73,6 +226,8 @@ typedef struct pa_atomic_ptr {
volatile AO_t value;
} pa_atomic_ptr_t;
+#define PA_ATOMIC_PTR_INIT(v) { .value = (AO_t) (v) }
+
static inline void* pa_atomic_ptr_load(const pa_atomic_ptr_t *a) {
return (void*) AO_load_full((AO_t*) &a->value);
}
@@ -86,3 +241,5 @@ static inline int pa_atomic_ptr_cmpxchg(pa_atomic_ptr_t *a, void *old_p, void* n
}
#endif
+
+#endif
diff --git a/src/pulsecore/authkey-prop.c b/src/pulsecore/authkey-prop.c
index 3b8304b2..54154500 100644
--- a/src/pulsecore/authkey-prop.c
+++ b/src/pulsecore/authkey-prop.c
@@ -21,44 +21,56 @@
USA.
***/
-#include <assert.h>
+#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 {
- int ref;
+ 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;
- assert(c && name && data && len > 0);
+
+ pa_assert(c);
+ pa_assert(name);
+ pa_assert(data);
+ pa_assert(len > 0);
if (!(a = pa_property_get(c, name)))
return -1;
- assert(a->length == len);
- memcpy(data, a+1, len);
+ pa_assert(a->length == len);
+ memcpy(data, (uint8_t*) a + PA_ALIGN(sizeof(struct authkey_data)), len);
+
return 0;
}
int pa_authkey_prop_put(pa_core *c, const char *name, const void *data, size_t len) {
struct authkey_data *a;
- assert(c && name);
+
+ pa_assert(c);
+ pa_assert(name);
if (pa_property_get(c, name))
return -1;
- a = pa_xmalloc(sizeof(struct authkey_data) + len);
- a->ref = 1;
+ a = pa_xmalloc(PA_ALIGN(sizeof(struct authkey_data)) + len);
+ PA_REFCNT_INIT(a);
a->length = len;
- memcpy(a+1, data, len);
+ memcpy((uint8_t*) a + PA_ALIGN(sizeof(struct authkey_data)), data, len);
pa_property_set(c, name, a);
@@ -67,22 +79,27 @@ int pa_authkey_prop_put(pa_core *c, const char *name, const void *data, size_t l
void pa_authkey_prop_ref(pa_core *c, const char *name) {
struct authkey_data *a;
- assert(c && name);
- a = pa_property_get(c, name);
- assert(a && a->ref >= 1);
+ pa_assert(c);
+ pa_assert(name);
- a->ref++;
+ 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;
- assert(c && name);
+
+ pa_assert(c);
+ pa_assert(name);
a = pa_property_get(c, name);
- assert(a && a->ref >= 1);
+ pa_assert(a);
+ pa_assert(PA_REFCNT_VALUE(a) >= 1);
- if (!(--a->ref)) {
+ if (PA_REFCNT_DEC(a) <= 0) {
pa_property_remove(c, name);
pa_xfree(a);
}
diff --git a/src/pulsecore/authkey.c b/src/pulsecore/authkey.c
index a6150d0e..80bc8576 100644
--- a/src/pulsecore/authkey.c
+++ b/src/pulsecore/authkey.c
@@ -26,7 +26,6 @@
#include <config.h>
#endif
-#include <assert.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
@@ -43,21 +42,25 @@
#include <pulsecore/core-util.h>
#include <pulsecore/log.h>
#include <pulsecore/random.h>
+#include <pulsecore/macro.h>
#include "authkey.h"
/* Generate a new authorization key, store it in file fd and return it in *data */
static int generate(int fd, void *ret_data, size_t length) {
ssize_t r;
- assert(fd >= 0 && ret_data && length);
+
+ pa_assert(fd >= 0);
+ pa_assert(ret_data);
+ pa_assert(length > 0);
pa_random(ret_data, length);
lseek(fd, 0, SEEK_SET);
- ftruncate(fd, 0);
+ (void) ftruncate(fd, 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));
+ pa_log("Failed to write cookie file: %s", pa_cstrerror(errno));
return -1;
}
@@ -68,6 +71,10 @@ static int generate(int fd, void *ret_data, size_t length) {
#define O_BINARY 0
#endif
+#ifndef O_NOCTTY
+#define O_NOCTTY 0
+#endif
+
/* Load an euthorization cookie from file fn and store it in data. If
* the cookie file doesn't exist, create it */
static int load(const char *fn, void *data, size_t length) {
@@ -75,11 +82,15 @@ static int load(const char *fn, void *data, size_t length) {
int writable = 1;
int unlock = 0, ret = -1;
ssize_t r;
- assert(fn && data && length);
- if ((fd = open(fn, O_RDWR|O_CREAT|O_BINARY, S_IRUSR|S_IWUSR)) < 0) {
- if (errno != EACCES || (fd = open(fn, O_RDONLY|O_BINARY)) < 0) {
- pa_log("failed to open cookie file '%s': %s", fn, pa_cstrerror(errno));
+ pa_assert(fn);
+ pa_assert(data);
+ pa_assert(length > 0);
+
+ if ((fd = open(fn, O_RDWR|O_CREAT|O_BINARY|O_NOCTTY, S_IRUSR|S_IWUSR)) < 0) {
+
+ if (errno != EACCES || (fd = open(fn, O_RDONLY|O_BINARY|O_NOCTTY)) < 0) {
+ pa_log("Failed to open cookie file '%s': %s", fn, pa_cstrerror(errno));
goto finish;
} else
writable = 0;
@@ -88,15 +99,15 @@ static int load(const char *fn, void *data, size_t length) {
unlock = pa_lock_fd(fd, 1) >= 0;
if ((r = pa_loop_read(fd, data, length, NULL)) < 0) {
- pa_log("failed to read cookie file '%s': %s", fn, pa_cstrerror(errno));
+ pa_log("Failed to read cookie file '%s': %s", fn, pa_cstrerror(errno));
goto finish;
}
if ((size_t) r != length) {
- pa_log_debug("got %d bytes from cookie file '%s', expected %d", (int)r, fn, (int)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("Unable to write cookie to read only file");
goto finish;
}
@@ -113,7 +124,10 @@ finish:
if (unlock)
pa_lock_fd(fd, 0);
- close(fd);
+ if (pa_close(fd) < 0) {
+ pa_log_warn("Failed to close cookie file: %s", pa_cstrerror(errno));
+ ret = -1;
+ }
}
return ret;
@@ -123,13 +137,12 @@ finish:
int pa_authkey_load(const char *path, void *data, size_t length) {
int ret;
- assert(path && data && length);
+ pa_assert(path);
+ pa_assert(data);
+ pa_assert(length > 0);
- ret = load(path, data, length);
-
- if (ret < 0)
- pa_log("Failed to load authorization key '%s': %s", path,
- (ret == -1) ? pa_cstrerror(errno) : "file corrupt");
+ if ((ret = load(path, data, length)) < 0)
+ pa_log("Failed to load authorization key '%s': %s", path, (ret < 0) ? pa_cstrerror(errno) : "File corrupt");
return ret;
}
@@ -137,7 +150,10 @@ int pa_authkey_load(const char *path, void *data, size_t length) {
/* If the specified file path starts with / return it, otherwise
* return path prepended with home directory */
static const char *normalize_path(const char *fn, char *s, size_t l) {
- assert(fn && s && l > 0);
+
+ pa_assert(fn);
+ pa_assert(s);
+ pa_assert(l > 0);
#ifndef OS_IS_WIN32
if (fn[0] != '/') {
@@ -145,13 +161,14 @@ static const char *normalize_path(const char *fn, char *s, size_t l) {
if (strlen(fn) < 3 || !isalpha(fn[0]) || fn[1] != ':' || fn[2] != '\\') {
#endif
char homedir[PATH_MAX];
+
if (!pa_get_home_dir(homedir, sizeof(homedir)))
return NULL;
#ifndef OS_IS_WIN32
- snprintf(s, l, "%s/%s", homedir, fn);
+ pa_snprintf(s, l, "%s/%s", homedir, fn);
#else
- snprintf(s, l, "%s\\%s", homedir, fn);
+ pa_snprintf(s, l, "%s\\%s", homedir, fn);
#endif
return s;
}
@@ -164,7 +181,10 @@ static const char *normalize_path(const char *fn, char *s, size_t l) {
int pa_authkey_load_auto(const char *fn, void *data, size_t length) {
char path[PATH_MAX];
const char *p;
- assert(fn && data && length);
+
+ pa_assert(fn);
+ pa_assert(data);
+ pa_assert(length > 0);
if (!(p = normalize_path(fn, path, sizeof(path))))
return -2;
@@ -179,20 +199,23 @@ int pa_authkey_save(const char *fn, const void *data, size_t length) {
ssize_t r;
char path[PATH_MAX];
const char *p;
- assert(fn && data && length);
+
+ pa_assert(fn);
+ pa_assert(data);
+ pa_assert(length > 0);
if (!(p = normalize_path(fn, path, sizeof(path))))
return -2;
- if ((fd = open(p, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR)) < 0) {
- pa_log("failed to open cookie file '%s': %s", fn, pa_cstrerror(errno));
+ 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));
goto finish;
}
unlock = pa_lock_fd(fd, 1) >= 0;
if ((r = pa_loop_write(fd, data, length, NULL)) < 0 || (size_t) r != length) {
- pa_log("failed to read cookie file '%s': %s", fn, pa_cstrerror(errno));
+ pa_log("Failed to read cookie file '%s': %s", fn, pa_cstrerror(errno));
goto finish;
}
@@ -205,7 +228,10 @@ finish:
if (unlock)
pa_lock_fd(fd, 0);
- close(fd);
+ if (pa_close(fd) < 0) {
+ pa_log_warn("Failed to close cookie file: %s", pa_cstrerror(errno));
+ ret = -1;
+ }
}
return ret;
diff --git a/src/pulsecore/autoload.c b/src/pulsecore/autoload.c
index 6f888526..a1d3e02d 100644
--- a/src/pulsecore/autoload.c
+++ b/src/pulsecore/autoload.c
@@ -26,7 +26,6 @@
#include <config.h>
#endif
-#include <assert.h>
#include <stdlib.h>
#include <string.h>
@@ -36,13 +35,14 @@
#include <pulsecore/memchunk.h>
#include <pulsecore/sound-file.h>
#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
#include <pulsecore/core-scache.h>
#include <pulsecore/core-subscribe.h>
#include "autoload.h"
static void entry_free(pa_autoload_entry *e) {
- assert(e);
+ pa_assert(e);
pa_subscription_post(e->core, PA_SUBSCRIPTION_EVENT_AUTOLOAD|PA_SUBSCRIPTION_EVENT_REMOVE, PA_INVALID_INDEX);
pa_xfree(e->name);
pa_xfree(e->module);
@@ -51,7 +51,8 @@ static void entry_free(pa_autoload_entry *e) {
}
static void entry_remove_and_free(pa_autoload_entry *e) {
- assert(e && e->core);
+ pa_assert(e);
+ pa_assert(e->core);
pa_idxset_remove_by_data(e->core->autoload_idxset, e, NULL);
pa_hashmap_remove(e->core->autoload_hashmap, e->name);
@@ -60,12 +61,14 @@ static void entry_remove_and_free(pa_autoload_entry *e) {
static pa_autoload_entry* entry_new(pa_core *c, const char *name) {
pa_autoload_entry *e = NULL;
- assert(c && name);
+
+ pa_core_assert_ref(c);
+ pa_assert(name);
if (c->autoload_hashmap && (e = pa_hashmap_get(c->autoload_hashmap, name)))
return NULL;
- e = pa_xmalloc(sizeof(pa_autoload_entry));
+ e = pa_xnew(pa_autoload_entry, 1);
e->core = c;
e->name = pa_xstrdup(name);
e->module = e->argument = NULL;
@@ -73,7 +76,7 @@ static pa_autoload_entry* entry_new(pa_core *c, const char *name) {
if (!c->autoload_hashmap)
c->autoload_hashmap = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
- assert(c->autoload_hashmap);
+ pa_assert(c->autoload_hashmap);
pa_hashmap_put(c->autoload_hashmap, e->name, e);
@@ -88,7 +91,11 @@ static pa_autoload_entry* entry_new(pa_core *c, const char *name) {
int pa_autoload_add(pa_core *c, const char*name, pa_namereg_type_t type, const char*module, const char *argument, uint32_t *idx) {
pa_autoload_entry *e = NULL;
- assert(c && name && module && (type == PA_NAMEREG_SINK || type == PA_NAMEREG_SOURCE));
+
+ pa_assert(c);
+ pa_assert(name);
+ pa_assert(module);
+ pa_assert(type == PA_NAMEREG_SINK || type == PA_NAMEREG_SOURCE);
if (!(e = entry_new(c, name)))
return -1;
@@ -105,7 +112,10 @@ int pa_autoload_add(pa_core *c, const char*name, pa_namereg_type_t type, const c
int pa_autoload_remove_by_name(pa_core *c, const char*name, pa_namereg_type_t type) {
pa_autoload_entry *e;
- assert(c && name && (type == PA_NAMEREG_SINK || type == PA_NAMEREG_SOURCE));
+
+ pa_assert(c);
+ pa_assert(name);
+ pa_assert(type == PA_NAMEREG_SINK || type == PA_NAMEREG_SOURCE);
if (!c->autoload_hashmap || !(e = pa_hashmap_get(c->autoload_hashmap, name)) || e->type != type)
return -1;
@@ -116,7 +126,9 @@ int pa_autoload_remove_by_name(pa_core *c, const char*name, pa_namereg_type_t ty
int pa_autoload_remove_by_index(pa_core *c, uint32_t idx) {
pa_autoload_entry *e;
- assert(c && idx != PA_IDXSET_INVALID);
+
+ pa_assert(c);
+ pa_assert(idx != PA_IDXSET_INVALID);
if (!c->autoload_idxset || !(e = pa_idxset_get_by_index(c->autoload_idxset, idx)))
return -1;
@@ -128,7 +140,9 @@ int pa_autoload_remove_by_index(pa_core *c, uint32_t idx) {
void pa_autoload_request(pa_core *c, const char *name, pa_namereg_type_t type) {
pa_autoload_entry *e;
pa_module *m;
- assert(c && name);
+
+ pa_assert(c);
+ pa_assert(name);
if (!c->autoload_hashmap || !(e = pa_hashmap_get(c->autoload_hashmap, name)) || (e->type != type))
return;
@@ -153,6 +167,7 @@ static void free_func(void *p, PA_GCC_UNUSED void *userdata) {
}
void pa_autoload_free(pa_core *c) {
+
if (c->autoload_hashmap) {
pa_hashmap_free(c->autoload_hashmap, free_func, NULL);
c->autoload_hashmap = NULL;
@@ -166,7 +181,9 @@ void pa_autoload_free(pa_core *c) {
const pa_autoload_entry* pa_autoload_get_by_name(pa_core *c, const char*name, pa_namereg_type_t type) {
pa_autoload_entry *e;
- assert(c && name);
+
+ pa_core_assert_ref(c);
+ pa_assert(name);
if (!c->autoload_hashmap || !(e = pa_hashmap_get(c->autoload_hashmap, name)) || e->type != type)
return NULL;
@@ -176,7 +193,9 @@ const pa_autoload_entry* pa_autoload_get_by_name(pa_core *c, const char*name, pa
const pa_autoload_entry* pa_autoload_get_by_index(pa_core *c, uint32_t idx) {
pa_autoload_entry *e;
- assert(c && idx != PA_IDXSET_INVALID);
+
+ pa_core_assert_ref(c);
+ pa_assert(idx != PA_IDXSET_INVALID);
if (!c->autoload_idxset || !(e = pa_idxset_get_by_index(c->autoload_idxset, idx)))
return NULL;
diff --git a/src/pulsecore/avahi-wrap.c b/src/pulsecore/avahi-wrap.c
index 855ed567..fae54810 100644
--- a/src/pulsecore/avahi-wrap.c
+++ b/src/pulsecore/avahi-wrap.c
@@ -21,11 +21,14 @@
USA.
***/
-#include <assert.h>
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
#include <pulse/xmalloc.h>
#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
#include "avahi-wrap.h"
@@ -61,9 +64,9 @@ static pa_io_event_flags_t translate_io_flags(AvahiWatchEvent e) {
static void watch_callback(pa_mainloop_api*a, pa_io_event* e, int fd, pa_io_event_flags_t events, void *userdata) {
AvahiWatch *w = userdata;
- assert(a);
- assert(e);
- assert(w);
+ pa_assert(a);
+ pa_assert(e);
+ pa_assert(w);
w->current_event = translate_io_flags_back(events);
w->callback(w, fd, w->current_event, w->userdata);
@@ -74,12 +77,10 @@ static AvahiWatch* watch_new(const AvahiPoll *api, int fd, AvahiWatchEvent event
pa_avahi_poll *p;
AvahiWatch *w;
- assert(api);
- assert(fd >= 0);
- assert(callback);
-
- p = api->userdata;
- assert(p);
+ pa_assert(api);
+ pa_assert(fd >= 0);
+ pa_assert(callback);
+ pa_assert_se(p = api->userdata);
w = pa_xnew(AvahiWatch, 1);
w->avahi_poll = p;
@@ -92,19 +93,19 @@ static AvahiWatch* watch_new(const AvahiPoll *api, int fd, AvahiWatchEvent event
}
static void watch_update(AvahiWatch *w, AvahiWatchEvent event) {
- assert(w);
+ pa_assert(w);
w->avahi_poll->mainloop->io_enable(w->io_event, translate_io_flags(event));
}
static AvahiWatchEvent watch_get_events(AvahiWatch *w) {
- assert(w);
+ pa_assert(w);
return w->current_event;
}
static void watch_free(AvahiWatch *w) {
- assert(w);
+ pa_assert(w);
w->avahi_poll->mainloop->io_free(w->io_event);
pa_xfree(w);
@@ -120,9 +121,9 @@ struct AvahiTimeout {
static void timeout_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *tv, void *userdata) {
AvahiTimeout *t = userdata;
- assert(a);
- assert(e);
- assert(t);
+ pa_assert(a);
+ pa_assert(e);
+ pa_assert(t);
t->callback(t, t->userdata);
}
@@ -131,11 +132,9 @@ static AvahiTimeout* timeout_new(const AvahiPoll *api, const struct timeval *tv,
pa_avahi_poll *p;
AvahiTimeout *t;
- assert(api);
- assert(callback);
-
- p = api->userdata;
- assert(p);
+ pa_assert(api);
+ pa_assert(callback);
+ pa_assert_se(p = api->userdata);
t = pa_xnew(AvahiTimeout, 1);
t->avahi_poll = p;
@@ -148,7 +147,7 @@ static AvahiTimeout* timeout_new(const AvahiPoll *api, const struct timeval *tv,
}
static void timeout_update(AvahiTimeout *t, const struct timeval *tv) {
- assert(t);
+ pa_assert(t);
if (t->time_event && tv)
t->avahi_poll->mainloop->time_restart(t->time_event, tv);
@@ -161,7 +160,7 @@ static void timeout_update(AvahiTimeout *t, const struct timeval *tv) {
}
static void timeout_free(AvahiTimeout *t) {
- assert(t);
+ pa_assert(t);
if (t->time_event)
t->avahi_poll->mainloop->time_free(t->time_event);
@@ -171,7 +170,7 @@ static void timeout_free(AvahiTimeout *t) {
AvahiPoll* pa_avahi_poll_new(pa_mainloop_api *m) {
pa_avahi_poll *p;
- assert(m);
+ pa_assert(m);
p = pa_xnew(pa_avahi_poll, 1);
@@ -190,9 +189,8 @@ AvahiPoll* pa_avahi_poll_new(pa_mainloop_api *m) {
void pa_avahi_poll_free(AvahiPoll *api) {
pa_avahi_poll *p;
- assert(api);
- p = api->userdata;
- assert(p);
+ pa_assert(api);
+ pa_assert_se(p = api->userdata);
pa_xfree(p);
}
diff --git a/src/pulsecore/cli-command.c b/src/pulsecore/cli-command.c
index 6989069d..539fd34d 100644
--- a/src/pulsecore/cli-command.c
+++ b/src/pulsecore/cli-command.c
@@ -28,7 +28,6 @@
#include <stdio.h>
#include <string.h>
-#include <assert.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
@@ -95,6 +94,7 @@ static int pa_cli_command_sink_input_volume(pa_core *c, pa_tokenizer *t, pa_strb
static int pa_cli_command_source_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
static int pa_cli_command_sink_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
static int pa_cli_command_source_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
+static int pa_cli_command_sink_input_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
static int pa_cli_command_sink_default(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
static int pa_cli_command_source_default(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
static int pa_cli_command_kill_client(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
@@ -114,6 +114,9 @@ static int pa_cli_command_list_props(pa_core *c, pa_tokenizer *t, pa_strbuf *buf
static int pa_cli_command_move_sink_input(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
static int pa_cli_command_move_source_output(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
static int pa_cli_command_vacuum(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
+static int pa_cli_command_suspend_sink(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
+static int pa_cli_command_suspend_source(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
+static int pa_cli_command_suspend(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
/* A method table for all available commands */
@@ -130,13 +133,14 @@ static const struct command commands[] = {
{ "info", pa_cli_command_info, "Show comprehensive status", 1 },
{ "ls", pa_cli_command_info, NULL, 1 },
{ "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},
- { "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|name, volume)", 3},
+ { "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},
+ { "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},
- { "set-sink-mute", pa_cli_command_sink_mute, "Set the mute switch of a sink (args: index|name, mute)", 3},
- { "set-source-mute", pa_cli_command_source_mute, "Set the mute switch of a source (args: index|name, mute)", 3},
+ { "set-sink-mute", pa_cli_command_sink_mute, "Set the mute switch of a sink (args: index|name, bool)", 3},
+ { "set-sink-input-mute", pa_cli_command_sink_input_mute, "Set the mute switch of a sink input (args: index, bool)", 3},
+ { "set-source-mute", pa_cli_command_source_mute, "Set the mute switch of a source (args: index|name, bool)", 3},
{ "set-default-sink", pa_cli_command_sink_default, "Set the default sink (args: index|name)", 2},
{ "set-default-source", pa_cli_command_source_default, "Set the default source (args: index|name)", 2},
{ "kill-client", pa_cli_command_kill_client, "Kill a client (args: index)", 2},
@@ -159,6 +163,9 @@ static const struct command commands[] = {
{ "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},
+ { "suspend-sink", pa_cli_command_suspend_sink, "Suspend sink (args: index|name, bool)", 3},
+ { "suspend-source", pa_cli_command_suspend_source, "Suspend source (args: index|name, bool)", 3},
+ { "suspend", pa_cli_command_suspend, "Suspend all sinks and all sources (args: bool)", 2},
{ NULL, NULL, NULL, 0 }
};
@@ -174,15 +181,23 @@ static uint32_t parse_index(const char *n) {
return idx;
}
-static int pa_cli_command_exit(pa_core *c, pa_tokenizer *t, PA_GCC_UNUSED pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
- assert(c && c->mainloop && t);
+static int pa_cli_command_exit(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
+
c->mainloop->quit(c->mainloop, 0);
return 0;
}
-static int pa_cli_command_help(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
+static int pa_cli_command_help(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
const struct command*command;
- assert(c && t && buf);
+
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
pa_strbuf_puts(buf, "Available commands:\n");
@@ -192,67 +207,91 @@ static int pa_cli_command_help(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_G
return 0;
}
-static int pa_cli_command_modules(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
+static int pa_cli_command_modules(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
char *s;
- assert(c && t);
- s = pa_module_list_to_string(c);
- assert(s);
+
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
+
+ pa_assert_se(s = pa_module_list_to_string(c));
pa_strbuf_puts(buf, s);
pa_xfree(s);
return 0;
}
-static int pa_cli_command_clients(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
+static int pa_cli_command_clients(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
char *s;
- assert(c && t);
- s = pa_client_list_to_string(c);
- assert(s);
+
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
+
+ pa_assert_se(s = pa_client_list_to_string(c));
pa_strbuf_puts(buf, s);
pa_xfree(s);
return 0;
}
-static int pa_cli_command_sinks(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
+static int pa_cli_command_sinks(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
char *s;
- assert(c && t);
- s = pa_sink_list_to_string(c);
- assert(s);
+
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
+
+ pa_assert_se(s = pa_sink_list_to_string(c));
pa_strbuf_puts(buf, s);
pa_xfree(s);
return 0;
}
-static int pa_cli_command_sources(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
+static int pa_cli_command_sources(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
char *s;
- assert(c && t);
- s = pa_source_list_to_string(c);
- assert(s);
+
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
+
+ pa_assert_se(s = pa_source_list_to_string(c));
pa_strbuf_puts(buf, s);
pa_xfree(s);
return 0;
}
-static int pa_cli_command_sink_inputs(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
+static int pa_cli_command_sink_inputs(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
char *s;
- assert(c && t);
- s = pa_sink_input_list_to_string(c);
- assert(s);
+
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
+
+ pa_assert_se(s = pa_sink_input_list_to_string(c));
pa_strbuf_puts(buf, s);
pa_xfree(s);
return 0;
}
-static int pa_cli_command_source_outputs(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
+static int pa_cli_command_source_outputs(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
char *s;
- assert(c && t);
- s = pa_source_output_list_to_string(c);
- assert(s);
+
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
+
+ pa_assert_se(s = pa_source_output_list_to_string(c));
pa_strbuf_puts(buf, s);
pa_xfree(s);
return 0;
}
-static int pa_cli_command_stat(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
+static int pa_cli_command_stat(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
char s[256];
const pa_mempool_stat *stat;
unsigned k;
@@ -267,8 +306,10 @@ static int pa_cli_command_stat(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_G
[PA_MEMBLOCK_IMPORTED] = "IMPORTED",
};
- assert(c);
- assert(t);
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
stat = pa_mempool_get_stat(c->mempool);
@@ -312,7 +353,11 @@ static int pa_cli_command_stat(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_G
}
static int pa_cli_command_info(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
- assert(c && t);
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
+
pa_cli_command_stat(c, t, buf, fail);
pa_cli_command_modules(c, t, buf, fail);
pa_cli_command_sinks(c, t, buf, fail);
@@ -325,10 +370,14 @@ static int pa_cli_command_info(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int
return 0;
}
-static int pa_cli_command_load(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
+static int pa_cli_command_load(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
pa_module *m;
const char *name;
- assert(c && t);
+
+ 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 and optionally arguments.\n");
@@ -343,12 +392,16 @@ static int pa_cli_command_load(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_G
return 0;
}
-static int pa_cli_command_unload(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
+static int pa_cli_command_unload(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
pa_module *m;
uint32_t idx;
const char *i;
char *e;
- assert(c && t);
+
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
if (!(i = pa_tokenizer_get(t, 1))) {
pa_strbuf_puts(buf, "You need to specify the module index.\n");
@@ -365,12 +418,17 @@ static int pa_cli_command_unload(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA
return 0;
}
-static int pa_cli_command_sink_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
+static int pa_cli_command_sink_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
const char *n, *v;
pa_sink *sink;
uint32_t volume;
pa_cvolume cvolume;
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
+
if (!(n = pa_tokenizer_get(t, 1))) {
pa_strbuf_puts(buf, "You need to specify a sink either by its name or its index.\n");
return -1;
@@ -392,17 +450,22 @@ static int pa_cli_command_sink_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *bu
}
pa_cvolume_set(&cvolume, sink->sample_spec.channels, volume);
- pa_sink_set_volume(sink, PA_MIXER_HARDWARE, &cvolume);
+ pa_sink_set_volume(sink, &cvolume);
return 0;
}
-static int pa_cli_command_sink_input_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
+static int pa_cli_command_sink_input_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
const char *n, *v;
pa_sink_input *si;
pa_volume_t volume;
pa_cvolume cvolume;
uint32_t idx;
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
+
if (!(n = pa_tokenizer_get(t, 1))) {
pa_strbuf_puts(buf, "You need to specify a sink input by its index.\n");
return -1;
@@ -433,12 +496,17 @@ static int pa_cli_command_sink_input_volume(pa_core *c, pa_tokenizer *t, pa_strb
return 0;
}
-static int pa_cli_command_source_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
+static int pa_cli_command_source_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
const char *n, *v;
pa_source *source;
uint32_t volume;
pa_cvolume cvolume;
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
+
if (!(n = pa_tokenizer_get(t, 1))) {
pa_strbuf_puts(buf, "You need to specify a source either by its name or its index.\n");
return -1;
@@ -460,15 +528,20 @@ static int pa_cli_command_source_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *
}
pa_cvolume_set(&cvolume, source->sample_spec.channels, volume);
- pa_source_set_volume(source, PA_MIXER_HARDWARE, &cvolume);
+ pa_source_set_volume(source, &cvolume);
return 0;
}
-static int pa_cli_command_sink_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
+static int pa_cli_command_sink_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
const char *n, *m;
pa_sink *sink;
int mute;
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
+
if (!(n = pa_tokenizer_get(t, 1))) {
pa_strbuf_puts(buf, "You need to specify a sink either by its name or its index.\n");
return -1;
@@ -489,15 +562,20 @@ static int pa_cli_command_sink_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf,
return -1;
}
- pa_sink_set_mute(sink, PA_MIXER_HARDWARE, mute);
+ pa_sink_set_mute(sink, mute);
return 0;
}
-static int pa_cli_command_source_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
+static int pa_cli_command_source_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
const char *n, *m;
pa_source *source;
int mute;
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
+
if (!(n = pa_tokenizer_get(t, 1))) {
pa_strbuf_puts(buf, "You need to specify a source either by its name or its index.\n");
return -1;
@@ -518,13 +596,57 @@ static int pa_cli_command_source_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *bu
return -1;
}
- pa_source_set_mute(source, PA_MIXER_HARDWARE, mute);
+ pa_source_set_mute(source, mute);
return 0;
}
-static int pa_cli_command_sink_default(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
+static int pa_cli_command_sink_input_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
+ const char *n, *v;
+ pa_sink_input *si;
+ uint32_t idx;
+ int mute;
+
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
+
+ if (!(n = pa_tokenizer_get(t, 1))) {
+ pa_strbuf_puts(buf, "You need to specify a sink input by its index.\n");
+ return -1;
+ }
+
+ if ((idx = parse_index(n)) == PA_IDXSET_INVALID) {
+ pa_strbuf_puts(buf, "Failed to parse index.\n");
+ return -1;
+ }
+
+ 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");
+ return -1;
+ }
+
+ if (pa_atoi(v, &mute) < 0) {
+ pa_strbuf_puts(buf, "Failed to parse mute switch.\n");
+ return -1;
+ }
+
+ if (!(si = pa_idxset_get_by_index(c->sink_inputs, (uint32_t) idx))) {
+ pa_strbuf_puts(buf, "No sink input found with this index.\n");
+ return -1;
+ }
+
+ pa_sink_input_set_mute(si, mute);
+ return 0;
+}
+
+static int pa_cli_command_sink_default(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
const char *n;
- assert(c && t);
+
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
if (!(n = pa_tokenizer_get(t, 1))) {
pa_strbuf_puts(buf, "You need to specify a sink either by its name or its index.\n");
@@ -535,9 +657,13 @@ static int pa_cli_command_sink_default(pa_core *c, pa_tokenizer *t, pa_strbuf *b
return 0;
}
-static int pa_cli_command_source_default(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
+static int pa_cli_command_source_default(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
const char *n;
- assert(c && t);
+
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
if (!(n = pa_tokenizer_get(t, 1))) {
pa_strbuf_puts(buf, "You need to specify a source either by its name or its index.\n");
@@ -548,11 +674,15 @@ static int pa_cli_command_source_default(pa_core *c, pa_tokenizer *t, pa_strbuf
return 0;
}
-static int pa_cli_command_kill_client(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
+static int pa_cli_command_kill_client(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
const char *n;
pa_client *client;
uint32_t idx;
- assert(c && t);
+
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
if (!(n = pa_tokenizer_get(t, 1))) {
pa_strbuf_puts(buf, "You need to specify a client by its index.\n");
@@ -573,11 +703,15 @@ static int pa_cli_command_kill_client(pa_core *c, pa_tokenizer *t, pa_strbuf *bu
return 0;
}
-static int pa_cli_command_kill_sink_input(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
+static int pa_cli_command_kill_sink_input(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
const char *n;
pa_sink_input *sink_input;
uint32_t idx;
- assert(c && t);
+
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
if (!(n = pa_tokenizer_get(t, 1))) {
pa_strbuf_puts(buf, "You need to specify a sink input by its index.\n");
@@ -598,11 +732,15 @@ static int pa_cli_command_kill_sink_input(pa_core *c, pa_tokenizer *t, pa_strbuf
return 0;
}
-static int pa_cli_command_kill_source_output(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
+static int pa_cli_command_kill_source_output(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
const char *n;
pa_source_output *source_output;
uint32_t idx;
- assert(c && t);
+
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
if (!(n = pa_tokenizer_get(t, 1))) {
pa_strbuf_puts(buf, "You need to specify a source output by its index.\n");
@@ -623,20 +761,29 @@ static int pa_cli_command_kill_source_output(pa_core *c, pa_tokenizer *t, pa_str
return 0;
}
-static int pa_cli_command_scache_list(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
+static int pa_cli_command_scache_list(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
char *s;
- assert(c && t);
- s = pa_scache_list_to_string(c);
- assert(s);
+
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
+
+ pa_assert_se(s = pa_scache_list_to_string(c));
pa_strbuf_puts(buf, s);
pa_xfree(s);
+
return 0;
}
static int pa_cli_command_scache_play(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
const char *n, *sink_name;
pa_sink *sink;
- assert(c && t && buf && fail);
+
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
if (!(n = pa_tokenizer_get(t, 1)) || !(sink_name = pa_tokenizer_get(t, 2))) {
pa_strbuf_puts(buf, "You need to specify a sample name and a sink name.\n");
@@ -658,7 +805,11 @@ static int pa_cli_command_scache_play(pa_core *c, pa_tokenizer *t, pa_strbuf *bu
static int pa_cli_command_scache_remove(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
const char *n;
- assert(c && t && buf && fail);
+
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
if (!(n = pa_tokenizer_get(t, 1))) {
pa_strbuf_puts(buf, "You need to specify a sample name.\n");
@@ -676,7 +827,11 @@ static int pa_cli_command_scache_remove(pa_core *c, pa_tokenizer *t, pa_strbuf *
static int pa_cli_command_scache_load(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
const char *fname, *n;
int r;
- assert(c && t && buf && fail);
+
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
if (!(fname = pa_tokenizer_get(t, 2)) || !(n = pa_tokenizer_get(t, 1))) {
pa_strbuf_puts(buf, "You need to specify a file name and a sample name.\n");
@@ -696,7 +851,11 @@ static int pa_cli_command_scache_load(pa_core *c, pa_tokenizer *t, pa_strbuf *bu
static int pa_cli_command_scache_load_dir(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
const char *pname;
- assert(c && t && buf && fail);
+
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
if (!(pname = pa_tokenizer_get(t, 1))) {
pa_strbuf_puts(buf, "You need to specify a path name.\n");
@@ -714,7 +873,11 @@ static int pa_cli_command_scache_load_dir(pa_core *c, pa_tokenizer *t, pa_strbuf
static int pa_cli_command_play_file(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
const char *fname, *sink_name;
pa_sink *sink;
- assert(c && t && buf && fail);
+
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
if (!(fname = pa_tokenizer_get(t, 1)) || !(sink_name = pa_tokenizer_get(t, 2))) {
pa_strbuf_puts(buf, "You need to specify a file name and a sink name.\n");
@@ -732,7 +895,11 @@ static int pa_cli_command_play_file(pa_core *c, pa_tokenizer *t, pa_strbuf *buf,
static int pa_cli_command_autoload_add(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
const char *a, *b;
- assert(c && t && buf && fail);
+
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
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");
@@ -746,7 +913,11 @@ static int pa_cli_command_autoload_add(pa_core *c, pa_tokenizer *t, pa_strbuf *b
static int pa_cli_command_autoload_remove(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
const char *name;
- assert(c && t && buf && fail);
+
+ 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 a device name\n");
@@ -761,25 +932,36 @@ static int pa_cli_command_autoload_remove(pa_core *c, pa_tokenizer *t, pa_strbuf
return 0;
}
-static int pa_cli_command_autoload_list(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
+static int pa_cli_command_autoload_list(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
char *s;
- assert(c && t);
- s = pa_autoload_list_to_string(c);
- assert(s);
+
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
+
+ pa_assert_se(s = pa_autoload_list_to_string(c));
pa_strbuf_puts(buf, s);
pa_xfree(s);
+
return 0;
}
-static int pa_cli_command_list_props(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
- assert(c && t);
+static int pa_cli_command_list_props(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
+
pa_property_dump(c, buf);
return 0;
}
static int pa_cli_command_vacuum(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
- assert(c);
- assert(t);
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
pa_mempool_vacuum(c->mempool);
@@ -792,6 +974,11 @@ static int pa_cli_command_move_sink_input(pa_core *c, pa_tokenizer *t, pa_strbuf
pa_sink *sink;
uint32_t idx;
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
+
if (!(n = pa_tokenizer_get(t, 1))) {
pa_strbuf_puts(buf, "You need to specify a sink input by its index.\n");
return -1;
@@ -830,6 +1017,11 @@ static int pa_cli_command_move_source_output(pa_core *c, pa_tokenizer *t, pa_str
pa_source *source;
uint32_t idx;
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
+
if (!(n = pa_tokenizer_get(t, 1))) {
pa_strbuf_puts(buf, "You need to specify a source output by its index.\n");
return -1;
@@ -862,7 +1054,105 @@ static int pa_cli_command_move_source_output(pa_core *c, pa_tokenizer *t, pa_str
return 0;
}
-static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
+static int pa_cli_command_suspend_sink(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
+ const char *n, *m;
+ pa_sink *sink;
+ int suspend;
+
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
+
+ if (!(n = pa_tokenizer_get(t, 1))) {
+ pa_strbuf_puts(buf, "You need to specify a sink either by its name or its index.\n");
+ return -1;
+ }
+
+ if (!(m = pa_tokenizer_get(t, 2))) {
+ pa_strbuf_puts(buf, "You need to specify a suspend switch setting (0/1).\n");
+ return -1;
+ }
+
+ if (pa_atoi(m, &suspend) < 0) {
+ pa_strbuf_puts(buf, "Failed to parse suspend switch.\n");
+ return -1;
+ }
+
+ if (!(sink = pa_namereg_get(c, n, PA_NAMEREG_SINK, 1))) {
+ pa_strbuf_puts(buf, "No sink found by this name or index.\n");
+ return -1;
+ }
+
+ pa_sink_suspend(sink, suspend);
+ return 0;
+}
+
+static int pa_cli_command_suspend_source(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
+ const char *n, *m;
+ pa_source *source;
+ int suspend;
+
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
+
+ if (!(n = pa_tokenizer_get(t, 1))) {
+ pa_strbuf_puts(buf, "You need to specify a source either by its name or its index.\n");
+ return -1;
+ }
+
+ if (!(m = pa_tokenizer_get(t, 2))) {
+ pa_strbuf_puts(buf, "You need to specify a suspend switch setting (0/1).\n");
+ return -1;
+ }
+
+ if (pa_atoi(m, &suspend) < 0) {
+ pa_strbuf_puts(buf, "Failed to parse suspend switch.\n");
+ return -1;
+ }
+
+ if (!(source = pa_namereg_get(c, n, PA_NAMEREG_SOURCE, 1))) {
+ pa_strbuf_puts(buf, "No source found by this name or index.\n");
+ return -1;
+ }
+
+ pa_source_suspend(source, suspend);
+ return 0;
+}
+
+static int pa_cli_command_suspend(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
+ const char *m;
+ int suspend;
+ int ret;
+
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
+
+ if (!(m = pa_tokenizer_get(t, 1))) {
+ pa_strbuf_puts(buf, "You need to specify a suspend switch setting (0/1).\n");
+ return -1;
+ }
+
+ if (pa_atoi(m, &suspend) < 0) {
+ pa_strbuf_puts(buf, "Failed to parse suspend switch.\n");
+ return -1;
+ }
+
+ ret = - (pa_sink_suspend_all(c, suspend) < 0);
+ if (pa_source_suspend_all(c, suspend) < 0)
+ ret = -1;
+
+ if (ret < 0)
+ pa_strbuf_puts(buf, "Failed to resume/suspend all sinks/sources.\n");
+
+ return 0;
+}
+
+static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
pa_module *m;
pa_sink *sink;
pa_source *source;
@@ -874,7 +1164,10 @@ static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_G
void *i;
pa_autoload_entry *a;
- assert(c && t);
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
time(&now);
@@ -884,7 +1177,6 @@ static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_G
pa_strbuf_printf(buf, "### Configuration dump generated at %s\n", ctime(&now));
#endif
-
for (m = pa_idxset_first(c->modules, &idx); m; m = pa_idxset_next(c->modules, &idx)) {
if (m->auto_unload)
continue;
@@ -900,7 +1192,7 @@ static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_G
nl = 0;
for (sink = pa_idxset_first(c->sinks, &idx); sink; sink = pa_idxset_next(c->sinks, &idx)) {
- if (sink->owner && sink->owner->auto_unload)
+ if (sink->module && sink->module->auto_unload)
continue;
if (!nl) {
@@ -908,12 +1200,12 @@ static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_G
nl = 1;
}
- pa_strbuf_printf(buf, "set-sink-volume %s 0x%03x\n", sink->name, pa_cvolume_avg(pa_sink_get_volume(sink, PA_MIXER_HARDWARE)));
- pa_strbuf_printf(buf, "set-sink-mute %s %d\n", sink->name, pa_sink_get_mute(sink, PA_MIXER_HARDWARE));
+ 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));
}
for (source = pa_idxset_first(c->sources, &idx); source; source = pa_idxset_next(c->sources, &idx)) {
- if (source->owner && source->owner->auto_unload)
+ if (source->module && source->module->auto_unload)
continue;
if (!nl) {
@@ -921,8 +1213,8 @@ static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_G
nl = 1;
}
- pa_strbuf_printf(buf, "set-source-volume %s 0x%03x\n", source->name, pa_cvolume_avg(pa_source_get_volume(source, PA_MIXER_HARDWARE)));
- pa_strbuf_printf(buf, "set-source-mute %s %d\n", source->name, pa_source_get_mute(source, PA_MIXER_HARDWARE));
+ 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));
}
@@ -972,6 +1264,10 @@ static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_G
int pa_cli_command_execute_line_stateful(pa_core *c, const char *s, pa_strbuf *buf, int *fail, int *ifstate) {
const char *cs;
+ pa_assert(c);
+ pa_assert(s);
+ pa_assert(buf);
+
cs = s+strspn(s, whitespace);
if (*cs == '#' || !*cs)
@@ -1006,9 +1302,9 @@ int pa_cli_command_execute_line_stateful(pa_core *c, const char *s, pa_strbuf *b
if (l == sizeof(META_INCLUDE)-1 && !strncmp(cs, META_INCLUDE, l)) {
const char *filename = cs+l+strspn(cs+l, whitespace);
-
if (pa_cli_command_execute_file(c, filename, buf, fail) < 0)
- if (*fail) return -1;
+ if (*fail)
+ return -1;
} else if (l == sizeof(META_IFEXISTS)-1 && !strncmp(cs, META_IFEXISTS, l)) {
if (!ifstate) {
pa_strbuf_printf(buf, "Meta command %s is not valid in this context\n", cs);
@@ -1020,6 +1316,7 @@ int pa_cli_command_execute_line_stateful(pa_core *c, const char *s, pa_strbuf *b
const char *filename = cs+l+strspn(cs+l, whitespace);
*ifstate = access(filename, F_OK) == 0 ? IFSTATE_TRUE : IFSTATE_FALSE;
+ pa_log_debug("Checking for existance of '%s': %s", filename, *ifstate == IFSTATE_TRUE ? "success" : "failure");
}
} else {
pa_strbuf_printf(buf, "Invalid meta command: %s\n", cs);
@@ -1034,14 +1331,13 @@ int pa_cli_command_execute_line_stateful(pa_core *c, const char *s, pa_strbuf *b
if (ifstate && *ifstate == IFSTATE_FALSE)
return 0;
-
l = strcspn(cs, whitespace);
for (command = commands; command->name; command++)
if (strlen(command->name) == l && !strncmp(cs, command->name, l)) {
int ret;
pa_tokenizer *t = pa_tokenizer_new(cs, command->args);
- assert(t);
+ pa_assert(t);
ret = command->proc(c, t, buf, fail);
pa_tokenizer_free(t);
unknown = 0;
@@ -1072,9 +1368,9 @@ int pa_cli_command_execute_file(pa_core *c, const char *fn, pa_strbuf *buf, int
int ifstate = IFSTATE_NONE;
int ret = -1;
- assert(c);
- assert(fn);
- assert(buf);
+ pa_assert(c);
+ pa_assert(fn);
+ pa_assert(buf);
if (!(f = fopen(fn, "r"))) {
pa_strbuf_printf(buf, "open('%s') failed: %s\n", fn, pa_cstrerror(errno));
@@ -1104,9 +1400,9 @@ int pa_cli_command_execute(pa_core *c, const char *s, pa_strbuf *buf, int *fail)
const char *p;
int ifstate = IFSTATE_NONE;
- assert(c);
- assert(s);
- assert(buf);
+ pa_assert(c);
+ pa_assert(s);
+ pa_assert(buf);
p = s;
while (*p) {
@@ -1125,3 +1421,4 @@ int pa_cli_command_execute(pa_core *c, const char *s, pa_strbuf *buf, int *fail)
return 0;
}
+
diff --git a/src/pulsecore/cli-text.c b/src/pulsecore/cli-text.c
index 413f9334..6683e697 100644
--- a/src/pulsecore/cli-text.c
+++ b/src/pulsecore/cli-text.c
@@ -25,7 +25,6 @@
#include <config.h>
#endif
-#include <assert.h>
#include <string.h>
#include <pulse/volume.h>
@@ -41,6 +40,7 @@
#include <pulsecore/sample-util.h>
#include <pulsecore/core-scache.h>
#include <pulsecore/autoload.h>
+#include <pulsecore/macro.h>
#include "cli-text.h"
@@ -48,10 +48,9 @@ char *pa_module_list_to_string(pa_core *c) {
pa_strbuf *s;
pa_module *m;
uint32_t idx = PA_IDXSET_INVALID;
- assert(c);
+ pa_assert(c);
s = pa_strbuf_new();
- assert(s);
pa_strbuf_printf(s, "%u module(s) loaded.\n", pa_idxset_size(c->modules));
@@ -72,10 +71,9 @@ char *pa_client_list_to_string(pa_core *c) {
pa_strbuf *s;
pa_client *client;
uint32_t idx = PA_IDXSET_INVALID;
- assert(c);
+ pa_assert(c);
s = pa_strbuf_new();
- assert(s);
pa_strbuf_printf(s, "%u client(s) logged in.\n", pa_idxset_size(c->clients));
@@ -93,10 +91,15 @@ char *pa_sink_list_to_string(pa_core *c) {
pa_strbuf *s;
pa_sink *sink;
uint32_t idx = PA_IDXSET_INVALID;
- assert(c);
+ static const char* const state_table[] = {
+ [PA_SINK_RUNNING] = "RUNNING",
+ [PA_SINK_SUSPENDED] = "SUSPENDED",
+ [PA_SINK_IDLE] = "IDLE",
+ [PA_SINK_UNLINKED] = "UNLINKED"
+ };
+ pa_assert(c);
s = pa_strbuf_new();
- assert(s);
pa_strbuf_printf(s, "%u sink(s) available.\n", pa_idxset_size(c->sinks));
@@ -108,22 +111,35 @@ char *pa_sink_list_to_string(pa_core *c) {
" %c index: %u\n"
"\tname: <%s>\n"
"\tdriver: <%s>\n"
+ "\tflags: %s%s%s\n"
+ "\tstate: %s\n"
"\tvolume: <%s>\n"
+ "\tmute: <%i>\n"
"\tlatency: <%0.0f usec>\n"
- "\tmonitor_source: <%u>\n"
+ "\tmonitor source: <%u>\n"
"\tsample spec: <%s>\n"
- "\tchannel map: <%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->index,
+ sink->name,
sink->driver,
- pa_cvolume_snprint(cv, sizeof(cv), pa_sink_get_volume(sink, PA_MIXER_HARDWARE)),
+ sink->flags & PA_SINK_HW_VOLUME_CTRL ? "HW_VOLUME_CTRL " : "",
+ sink->flags & PA_SINK_LATENCY ? "LATENCY " : "",
+ sink->flags & PA_SINK_HARDWARE ? "HARDWARE " : "",
+ state_table[pa_sink_get_state(sink)],
+ pa_cvolume_snprint(cv, sizeof(cv), pa_sink_get_volume(sink)),
+ !!pa_sink_get_mute(sink),
(double) pa_sink_get_latency(sink),
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));
+ pa_channel_map_snprint(cm, sizeof(cm), &sink->channel_map),
+ pa_sink_used_by(sink),
+ pa_sink_linked_by(sink));
- if (sink->owner)
- pa_strbuf_printf(s, "\towner module: <%u>\n", sink->owner->index);
+ 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);
}
@@ -135,15 +151,20 @@ char *pa_source_list_to_string(pa_core *c) {
pa_strbuf *s;
pa_source *source;
uint32_t idx = PA_IDXSET_INVALID;
- assert(c);
+ static const char* const state_table[] = {
+ [PA_SOURCE_RUNNING] = "RUNNING",
+ [PA_SOURCE_SUSPENDED] = "SUSPENDED",
+ [PA_SOURCE_IDLE] = "IDLE",
+ [PA_SOURCE_UNLINKED] = "UNLINKED"
+ };
+ pa_assert(c);
s = pa_strbuf_new();
- assert(s);
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];
+ char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX];
pa_strbuf_printf(
@@ -151,21 +172,35 @@ char *pa_source_list_to_string(pa_core *c) {
" %c index: %u\n"
"\tname: <%s>\n"
"\tdriver: <%s>\n"
+ "\tflags: %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",
+ "\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 " : "",
+ 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_sample_spec_snprint(ss, sizeof(ss), &source->sample_spec),
- pa_channel_map_snprint(cm, sizeof(cm), &source->channel_map));
+ 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);
- if (source->owner)
- pa_strbuf_printf(s, "\towner module: <%u>\n", source->owner->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);
}
@@ -179,37 +214,41 @@ 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[] = {
- "RUNNING",
- "CORKED",
- "DISCONNECTED"
+ [PA_SOURCE_OUTPUT_RUNNING] = "RUNNING",
+ [PA_SOURCE_OUTPUT_CORKED] = "CORKED",
+ [PA_SOURCE_OUTPUT_UNLINKED] = "UNLINKED"
};
- assert(c);
+ pa_assert(c);
s = pa_strbuf_new();
- assert(s);
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];
- assert(o->source);
+ pa_assert(o->source);
pa_strbuf_printf(
s,
" index: %u\n"
"\tname: '%s'\n"
"\tdriver: <%s>\n"
+ "\tflags: %s%s\n"
"\tstate: %s\n"
"\tsource: <%u> '%s'\n"
+ "\tlatency: <%0.0f usec>\n"
"\tsample spec: <%s>\n"
"\tchannel map: <%s>\n"
"\tresample method: %s\n",
o->index,
o->name,
o->driver,
- state_table[o->state],
+ o->flags & PA_SOURCE_OUTPUT_VARIABLE_RATE ? "VARIABLE_RATE " : "",
+ o->flags & PA_SOURCE_OUTPUT_DONT_MOVE ? "DONT_MOVE " : "",
+ state_table[pa_source_output_get_state(o)],
o->source->index, o->source->name,
+ (double) pa_source_output_get_latency(o),
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)));
@@ -227,30 +266,32 @@ 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[] = {
- "RUNNING",
- "CORKED",
- "DISCONNECTED"
+ [PA_SINK_INPUT_RUNNING] = "RUNNING",
+ [PA_SINK_INPUT_DRAINED] = "DRAINED",
+ [PA_SINK_INPUT_CORKED] = "CORKED",
+ [PA_SINK_INPUT_UNLINKED] = "UNLINKED"
};
- assert(c);
+ pa_assert(c);
s = pa_strbuf_new();
- assert(s);
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];
- assert(i->sink);
+ pa_assert(i->sink);
pa_strbuf_printf(
s,
" index: %u\n"
"\tname: <%s>\n"
"\tdriver: <%s>\n"
+ "\tflags: %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"
@@ -258,16 +299,19 @@ char *pa_sink_input_list_to_string(pa_core *c) {
i->index,
i->name,
i->driver,
- state_table[i->state],
+ i->flags & PA_SINK_INPUT_VARIABLE_RATE ? "VARIABLE_RATE " : "",
+ i->flags & PA_SINK_INPUT_DONT_MOVE ? "DONT_MOVE " : "",
+ 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_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, "\towner module: <%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);
}
@@ -277,10 +321,9 @@ char *pa_sink_input_list_to_string(pa_core *c) {
char *pa_scache_list_to_string(pa_core *c) {
pa_strbuf *s;
- assert(c);
+ pa_assert(c);
s = pa_strbuf_new();
- assert(s);
pa_strbuf_printf(s, "%u cache entries available.\n", c->scache ? pa_idxset_size(c->scache) : 0);
@@ -326,10 +369,9 @@ char *pa_scache_list_to_string(pa_core *c) {
char *pa_autoload_list_to_string(pa_core *c) {
pa_strbuf *s;
- assert(c);
+ pa_assert(c);
s = pa_strbuf_new();
- assert(s);
pa_strbuf_printf(s, "%u autoload entries available.\n", c->autoload_hashmap ? pa_hashmap_size(c->autoload_hashmap) : 0);
diff --git a/src/pulsecore/cli.c b/src/pulsecore/cli.c
index ee05d7f9..3f3c9cde 100644
--- a/src/pulsecore/cli.c
+++ b/src/pulsecore/cli.c
@@ -27,7 +27,6 @@
#include <stdio.h>
#include <string.h>
-#include <assert.h>
#include <stdlib.h>
#include <pulse/xmalloc.h>
@@ -45,6 +44,7 @@
#include <pulsecore/cli-text.h>
#include <pulsecore/cli-command.h>
#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
#include "cli.h"
@@ -68,19 +68,17 @@ static void client_kill(pa_client *c);
pa_cli* pa_cli_new(pa_core *core, pa_iochannel *io, pa_module *m) {
char cname[256];
pa_cli *c;
- assert(io);
+ pa_assert(io);
- c = pa_xmalloc(sizeof(pa_cli));
+ c = pa_xnew(pa_cli, 1);
c->core = core;
- c->line = pa_ioline_new(io);
- assert(c->line);
+ pa_assert_se(c->line = pa_ioline_new(io));
c->userdata = NULL;
c->eof_callback = NULL;
pa_iochannel_socket_peer_to_string(io, cname, sizeof(cname));
- c->client = pa_client_new(core, __FILE__, cname);
- assert(c->client);
+ pa_assert_se(c->client = pa_client_new(core, __FILE__, cname));
c->client->kill = client_kill;
c->client->userdata = c;
c->client->owner = m;
@@ -94,7 +92,8 @@ pa_cli* pa_cli_new(pa_core *core, pa_iochannel *io, pa_module *m) {
}
void pa_cli_free(pa_cli *c) {
- assert(c);
+ pa_assert(c);
+
pa_ioline_close(c->line);
pa_ioline_unref(c->line);
pa_client_free(c->client);
@@ -103,8 +102,9 @@ void pa_cli_free(pa_cli *c) {
static void client_kill(pa_client *client) {
pa_cli *c;
- assert(client && client->userdata);
- c = client->userdata;
+
+ pa_assert(client);
+ pa_assert_se(c = client->userdata);
pa_log_debug("CLI client killed.");
if (c->defer_kill)
@@ -119,7 +119,9 @@ static void line_callback(pa_ioline *line, const char *s, void *userdata) {
pa_strbuf *buf;
pa_cli *c = userdata;
char *p;
- assert(line && c);
+
+ pa_assert(line);
+ pa_assert(c);
if (!s) {
pa_log_debug("CLI got EOF from user.");
@@ -129,8 +131,7 @@ static void line_callback(pa_ioline *line, const char *s, void *userdata) {
return;
}
- buf = pa_strbuf_new();
- assert(buf);
+ pa_assert_se(buf = pa_strbuf_new());
c->defer_kill++;
pa_cli_command_execute_line(c->core, s, buf, &c->fail);
c->defer_kill--;
@@ -145,7 +146,8 @@ static void line_callback(pa_ioline *line, const char *s, void *userdata) {
}
void pa_cli_set_eof_callback(pa_cli *c, void (*cb)(pa_cli*c, void *userdata), void *userdata) {
- assert(c);
+ pa_assert(c);
+
c->eof_callback = cb;
c->userdata = userdata;
}
diff --git a/src/pulsecore/client.c b/src/pulsecore/client.c
index 0d792bb4..319b8387 100644
--- a/src/pulsecore/client.c
+++ b/src/pulsecore/client.c
@@ -27,7 +27,6 @@
#endif
#include <stdio.h>
-#include <assert.h>
#include <stdlib.h>
#include <string.h>
@@ -35,15 +34,16 @@
#include <pulsecore/core-subscribe.h>
#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
#include "client.h"
pa_client *pa_client_new(pa_core *core, const char *driver, const char *name) {
pa_client *c;
- int r;
- assert(core);
- c = pa_xmalloc(sizeof(pa_client));
+ pa_core_assert_ref(core);
+
+ c = pa_xnew(pa_client, 1);
c->name = pa_xstrdup(name);
c->driver = pa_xstrdup(driver);
c->owner = NULL;
@@ -52,10 +52,9 @@ pa_client *pa_client_new(pa_core *core, const char *driver, const char *name) {
c->kill = NULL;
c->userdata = NULL;
- r = pa_idxset_put(core->clients, c, &c->index);
- assert(c->index != PA_IDXSET_INVALID && r >= 0);
+ 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, c->name);
pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_NEW, c->index);
pa_core_check_quit(core);
@@ -64,13 +63,14 @@ pa_client *pa_client_new(pa_core *core, const char *driver, const char *name) {
}
void pa_client_free(pa_client *c) {
- assert(c && c->core);
+ pa_assert(c);
+ pa_assert(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, c->name);
pa_subscription_post(c->core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_REMOVE, c->index);
pa_xfree(c->name);
pa_xfree(c->driver);
@@ -78,7 +78,8 @@ void pa_client_free(pa_client *c) {
}
void pa_client_kill(pa_client *c) {
- assert(c);
+ pa_assert(c);
+
if (!c->kill) {
pa_log_warn("kill() operation not implemented for client %u", c->index);
return;
@@ -88,9 +89,9 @@ void pa_client_kill(pa_client *c) {
}
void pa_client_set_name(pa_client *c, const char *name) {
- assert(c);
+ pa_assert(c);
- pa_log_info("client %u changed name from \"%s\" to \"%s\"", c->index, c->name, name);
+ 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);
diff --git a/src/pulsecore/conf-parser.c b/src/pulsecore/conf-parser.c
index 12efbd2c..0e0ba95a 100644
--- a/src/pulsecore/conf-parser.c
+++ b/src/pulsecore/conf-parser.c
@@ -25,7 +25,6 @@
#include <config.h>
#endif
-#include <assert.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
@@ -35,6 +34,7 @@
#include <pulsecore/core-error.h>
#include <pulsecore/log.h>
#include <pulsecore/core-util.h>
+#include <pulsecore/macro.h>
#include "conf-parser.h"
@@ -43,7 +43,10 @@
/* Run the user supplied parser for an assignment */
static int next_assignment(const char *filename, unsigned line, const pa_config_item *t, const char *lvalue, const char *rvalue, void *userdata) {
- assert(filename && t && lvalue && rvalue);
+ pa_assert(filename);
+ pa_assert(t);
+ pa_assert(lvalue);
+ pa_assert(rvalue);
for (; t->parse; t++)
if (!strcmp(lvalue, t->lvalue))
@@ -56,7 +59,7 @@ static int next_assignment(const char *filename, unsigned line, const pa_config_
/* Returns non-zero when c is contained in s */
static int in_string(char c, const char *s) {
- assert(s);
+ pa_assert(s);
for (; *s; s++)
if (*s == c)
@@ -107,7 +110,9 @@ int pa_config_parse(const char *filename, FILE *f, const pa_config_item *t, void
int r = -1;
unsigned line = 0;
int do_close = !f;
- assert(filename && t);
+
+ pa_assert(filename);
+ pa_assert(t);
if (!f && !(f = fopen(filename, "r"))) {
if (errno == ENOENT) {
@@ -115,7 +120,7 @@ int pa_config_parse(const char *filename, FILE *f, const pa_config_item *t, void
goto finish;
}
- pa_log_warn("WARNING: failed to open configuration file '%s': %s",
+ pa_log_warn("Failed to open configuration file '%s': %s",
filename, pa_cstrerror(errno));
goto finish;
}
@@ -126,7 +131,7 @@ int pa_config_parse(const char *filename, FILE *f, const pa_config_item *t, void
if (feof(f))
break;
- pa_log_warn("WARNING: failed to read configuration file '%s': %s",
+ pa_log_warn("Failed to read configuration file '%s': %s",
filename, pa_cstrerror(errno));
goto finish;
}
@@ -148,7 +153,11 @@ finish:
int pa_config_parse_int(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, PA_GCC_UNUSED void *userdata) {
int *i = data;
int32_t k;
- assert(filename && lvalue && rvalue && data);
+
+ pa_assert(filename);
+ pa_assert(lvalue);
+ pa_assert(rvalue);
+ pa_assert(data);
if (pa_atoi(rvalue, &k) < 0) {
pa_log("[%s:%u] Failed to parse numeric value: %s", filename, line, rvalue);
@@ -161,7 +170,11 @@ int pa_config_parse_int(const char *filename, unsigned line, const char *lvalue,
int pa_config_parse_bool(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, PA_GCC_UNUSED void *userdata) {
int *b = data, k;
- assert(filename && lvalue && rvalue && data);
+
+ pa_assert(filename);
+ pa_assert(lvalue);
+ pa_assert(rvalue);
+ pa_assert(data);
if ((k = pa_parse_boolean(rvalue)) < 0) {
pa_log("[%s:%u] Failed to parse boolean value: %s", filename, line, rvalue);
@@ -175,7 +188,11 @@ int pa_config_parse_bool(const char *filename, unsigned line, const char *lvalue
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) {
char **s = data;
- assert(filename && lvalue && rvalue && data);
+
+ pa_assert(filename);
+ pa_assert(lvalue);
+ pa_assert(rvalue);
+ pa_assert(data);
pa_xfree(*s);
*s = *rvalue ? pa_xstrdup(rvalue) : NULL;
diff --git a/src/pulsecore/core-def.h b/src/pulsecore/core-def.h
index 10a3be42..4bc05137 100644
--- a/src/pulsecore/core-def.h
+++ b/src/pulsecore/core-def.h
@@ -24,9 +24,6 @@
USA.
***/
-typedef enum pa_mixer {
- PA_MIXER_SOFTWARE,
- PA_MIXER_HARDWARE
-} pa_mixer_t;
+/* FIXME: Remove this shit */
#endif
diff --git a/src/pulsecore/core-error.c b/src/pulsecore/core-error.c
index 044bea12..8a61e726 100644
--- a/src/pulsecore/core-error.c
+++ b/src/pulsecore/core-error.c
@@ -31,178 +31,50 @@
#include <stdlib.h>
#include <string.h>
-#ifdef HAVE_PTHREAD
-#include <pthread.h>
-#endif
-
-#ifdef HAVE_WINDOWS_H
-#include <windows.h>
-#endif
-
#include <pulse/utf8.h>
#include <pulse/xmalloc.h>
#include <pulsecore/core-util.h>
#include <pulsecore/native-common.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/log.h>
#include "core-error.h"
-#ifdef HAVE_PTHREAD
-
-static pthread_once_t cstrerror_once = PTHREAD_ONCE_INIT;
-static pthread_key_t tlsstr_key;
-
-static void inittls(void) {
- int ret;
-
- ret = pthread_key_create(&tlsstr_key, pa_xfree);
- if (ret) {
- fprintf(stderr, __FILE__ ": CRITICAL: Unable to allocate TLS key (%d)\n", errno);
- exit(-1);
- }
-}
-
-#elif HAVE_WINDOWS_H
-
-static DWORD tlsstr_key = TLS_OUT_OF_INDEXES;
-static DWORD monitor_key = TLS_OUT_OF_INDEXES;
-
-static void inittls(void) {
- HANDLE mutex;
- char name[64];
-
- sprintf(name, "pulse%d", (int)GetCurrentProcessId());
-
- mutex = CreateMutex(NULL, FALSE, name);
- if (!mutex) {
- fprintf(stderr, __FILE__ ": CRITICAL: Unable to create named mutex (%d)\n", (int)GetLastError());
- exit(-1);
- }
-
- WaitForSingleObject(mutex, INFINITE);
-
- if (tlsstr_key == TLS_OUT_OF_INDEXES) {
- tlsstr_key = TlsAlloc();
- monitor_key = TlsAlloc();
- if ((tlsstr_key == TLS_OUT_OF_INDEXES) || (monitor_key == TLS_OUT_OF_INDEXES)) {
- fprintf(stderr, __FILE__ ": CRITICAL: Unable to allocate TLS key (%d)\n", (int)GetLastError());
- exit(-1);
- }
- }
-
- ReleaseMutex(mutex);
-
- CloseHandle(mutex);
-}
-
-/*
- * This is incredibly brain dead, but this is necessary when dealing with
- * the hell that is Win32.
- */
-struct monitor_data {
- HANDLE thread;
- void *data;
-};
-
-static DWORD WINAPI monitor_thread(LPVOID param) {
- struct monitor_data *data;
-
- data = (struct monitor_data*)param;
- assert(data);
-
- WaitForSingleObject(data->thread, INFINITE);
-
- CloseHandle(data->thread);
- pa_xfree(data->data);
- pa_xfree(data);
-
- return 0;
-}
-
-static void start_monitor(void) {
- HANDLE thread;
- struct monitor_data *data;
-
- data = pa_xnew(struct monitor_data, 1);
- assert(data);
-
- DuplicateHandle(GetCurrentProcess(), GetCurrentThread(),
- GetCurrentProcess(), &data->thread, 0, FALSE, DUPLICATE_SAME_ACCESS);
-
- thread = CreateThread(NULL, 0, monitor_thread, data, 0, NULL);
- assert(thread);
-
- TlsSetValue(monitor_key, data);
-
- CloseHandle(thread);
-}
-
-#else
-
-/* Unsafe, but we have no choice */
-static char *tlsstr;
-
-#endif
+PA_STATIC_TLS_DECLARE(cstrerror, pa_xfree);
const char* pa_cstrerror(int errnum) {
- const char *origbuf;
-
-#ifdef HAVE_STRERROR_R
+ const char *original = NULL;
+ char *translated, *t;
char errbuf[128];
-#endif
-#ifdef HAVE_PTHREAD
- char *tlsstr;
+ if ((t = PA_STATIC_TLS_GET(cstrerror)))
+ pa_xfree(t);
- pthread_once(&cstrerror_once, inittls);
-
- tlsstr = pthread_getspecific(tlsstr_key);
-#elif defined(HAVE_WINDOWS_H)
- char *tlsstr;
- struct monitor_data *data;
-
- inittls();
-
- tlsstr = TlsGetValue(tlsstr_key);
- if (!tlsstr)
- start_monitor();
- data = TlsGetValue(monitor_key);
-#endif
-
- if (tlsstr)
- pa_xfree(tlsstr);
-
-#ifdef HAVE_STRERROR_R
-
-#ifdef __GLIBC__
- origbuf = strerror_r(errnum, errbuf, sizeof(errbuf));
- if (origbuf == NULL)
- origbuf = "";
-#else
+#if defined(HAVE_STRERROR_R) && defined(__GLIBC__)
+ original = strerror_r(errnum, errbuf, sizeof(errbuf));
+#elif defined(HAVE_STRERROR_R)
if (strerror_r(errnum, errbuf, sizeof(errbuf)) == 0) {
- origbuf = errbuf;
- errbuf[sizeof(errbuf) - 1] = '\0';
- } else
- origbuf = "";
-#endif
-
+ errbuf[sizeof(errbuf) - 1] = 0;
+ original = errbuf;
+ }
#else
/* This might not be thread safe, but we hope for the best */
- origbuf = strerror(errnum);
+ original = strerror(errnum);
#endif
- tlsstr = pa_locale_to_utf8(origbuf);
- if (!tlsstr) {
- fprintf(stderr, "Unable to convert, filtering\n");
- tlsstr = pa_utf8_filter(origbuf);
+ if (!original) {
+ pa_snprintf(errbuf, sizeof(errbuf), "Unknown error %i", errnum);
+ original = errbuf;
}
-#ifdef HAVE_PTHREAD
- pthread_setspecific(tlsstr_key, tlsstr);
-#elif defined(HAVE_WINDOWS_H)
- TlsSetValue(tlsstr_key, tlsstr);
- data->data = tlsstr;
-#endif
+ if (!(translated = pa_locale_to_utf8(original))) {
+ pa_log_warn("Unable to convert error string to locale, filtering.");
+ translated = pa_utf8_filter(original);
+ }
+
+ PA_STATIC_TLS_SET(cstrerror, translated);
- return tlsstr;
+ return translated;
}
diff --git a/src/pulsecore/core-scache.c b/src/pulsecore/core-scache.c
index cb272784..732d90dd 100644
--- a/src/pulsecore/core-scache.c
+++ b/src/pulsecore/core-scache.c
@@ -26,7 +26,6 @@
#include <config.h>
#endif
-#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
@@ -60,6 +59,7 @@
#include <pulsecore/core-util.h>
#include <pulsecore/log.h>
#include <pulsecore/core-error.h>
+#include <pulsecore/macro.h>
#include "core-scache.h"
@@ -68,7 +68,10 @@
static void timeout_callback(pa_mainloop_api *m, pa_time_event*e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) {
pa_core *c = userdata;
struct timeval ntv;
- assert(c && c->mainloop == m && c->scache_auto_unload_event == e);
+
+ pa_assert(c);
+ pa_assert(c->mainloop == m);
+ pa_assert(c->scache_auto_unload_event == e);
pa_scache_unload_unused(c);
@@ -78,7 +81,8 @@ static void timeout_callback(pa_mainloop_api *m, pa_time_event*e, PA_GCC_UNUSED
}
static void free_entry(pa_scache_entry *e) {
- assert(e);
+ pa_assert(e);
+
pa_namereg_unregister(e->core, e->name);
pa_subscription_post(e->core, PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE|PA_SUBSCRIPTION_EVENT_REMOVE, e->index);
pa_xfree(e->name);
@@ -90,7 +94,9 @@ static void free_entry(pa_scache_entry *e) {
static pa_scache_entry* scache_add_item(pa_core *c, const char *name) {
pa_scache_entry *e;
- assert(c && name);
+
+ pa_assert(c);
+ pa_assert(name);
if ((e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE, 0))) {
if (e->memchunk.memblock)
@@ -98,11 +104,11 @@ static pa_scache_entry* scache_add_item(pa_core *c, const char *name) {
pa_xfree(e->filename);
- assert(e->core == c);
+ pa_assert(e->core == c);
pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE|PA_SUBSCRIPTION_EVENT_CHANGE, e->index);
} else {
- e = pa_xmalloc(sizeof(pa_scache_entry));
+ e = pa_xnew(pa_scache_entry, 1);
if (!pa_namereg_register(c, name, PA_NAMEREG_SAMPLE, e, 1)) {
pa_xfree(e);
@@ -114,7 +120,7 @@ static pa_scache_entry* scache_add_item(pa_core *c, const char *name) {
if (!c->scache) {
c->scache = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
- assert(c->scache);
+ pa_assert(c->scache);
}
pa_idxset_put(c->scache, e, &e->index);
@@ -139,7 +145,9 @@ static pa_scache_entry* scache_add_item(pa_core *c, const char *name) {
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) {
pa_scache_entry *e;
char st[PA_SAMPLE_SPEC_SNPRINT_MAX];
- assert(c && name);
+
+ pa_assert(c);
+ pa_assert(name);
if (chunk && chunk->length > PA_SCACHE_ENTRY_SIZE_MAX)
return -1;
@@ -164,9 +172,9 @@ int pa_scache_add_item(pa_core *c, const char *name, const pa_sample_spec *ss, c
if (idx)
*idx = e->index;
- pa_log_debug("created sample \"%s\" (#%d), %d bytes with sample spec %s",
- name, e->index, e->memchunk.length,
- pa_sample_spec_snprint(st, sizeof(st), &e->sample_spec));
+ pa_log_debug("Created sample \"%s\" (#%d), %lu bytes with sample spec %s",
+ name, e->index, (unsigned long) e->memchunk.length,
+ pa_sample_spec_snprint(st, sizeof(st), &e->sample_spec));
return 0;
}
@@ -184,6 +192,10 @@ int pa_scache_add_file(pa_core *c, const char *name, const char *filename, uint3
filename = buf;
#endif
+ pa_assert(c);
+ pa_assert(name);
+ pa_assert(filename);
+
if (pa_sound_file_load(c->mempool, filename, &ss, &map, &chunk) < 0)
return -1;
@@ -203,7 +215,9 @@ int pa_scache_add_file_lazy(pa_core *c, const char *name, const char *filename,
filename = buf;
#endif
- assert(c && name);
+ pa_assert(c);
+ pa_assert(name);
+ pa_assert(filename);
if (!(e = scache_add_item(c, name)))
return -1;
@@ -226,15 +240,17 @@ int pa_scache_add_file_lazy(pa_core *c, const char *name, const char *filename,
int pa_scache_remove_item(pa_core *c, const char *name) {
pa_scache_entry *e;
- assert(c && name);
+
+ pa_assert(c);
+ pa_assert(name);
if (!(e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE, 0)))
return -1;
if (pa_idxset_remove_by_data(c->scache, e, NULL) != e)
- assert(0);
+ pa_assert(0);
- pa_log_debug("removed sample \"%s\"", name);
+ pa_log_debug("Removed sample \"%s\"", name);
free_entry(e);
@@ -243,12 +259,13 @@ int pa_scache_remove_item(pa_core *c, const char *name) {
static void free_cb(void *p, PA_GCC_UNUSED void *userdata) {
pa_scache_entry *e = p;
- assert(e);
+ pa_assert(e);
+
free_entry(e);
}
void pa_scache_free(pa_core *c) {
- assert(c);
+ pa_assert(c);
if (c->scache) {
pa_idxset_free(c->scache, free_cb, NULL);
@@ -264,9 +281,9 @@ int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, pa_volume_t
char *t;
pa_cvolume r;
- assert(c);
- assert(name);
- assert(sink);
+ pa_assert(c);
+ pa_assert(name);
+ pa_assert(sink);
if (!(e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE, 1)))
return -1;
@@ -284,7 +301,7 @@ int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, pa_volume_t
if (!e->memchunk.memblock)
return -1;
- pa_log_debug("playing sample \"%s\" on \"%s\"", name, sink->name);
+ pa_log_debug("Playing sample \"%s\" on \"%s\"", name, sink->name);
t = pa_sprintf_malloc("sample:%s", name);
@@ -304,9 +321,23 @@ 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) {
+ pa_sink *sink;
+
+ pa_assert(c);
+ pa_assert(name);
+
+ if (!(sink = pa_namereg_get(c, sink_name, PA_NAMEREG_SINK, autoload)))
+ return -1;
+
+ return pa_scache_play_item(c, name, sink, volume);
+}
+
const char * pa_scache_get_name_by_id(pa_core *c, uint32_t id) {
pa_scache_entry *e;
- assert(c && id != PA_IDXSET_INVALID);
+
+ pa_assert(c);
+ pa_assert(id != PA_IDXSET_INVALID);
if (!c->scache || !(e = pa_idxset_get_by_index(c->scache, id)))
return NULL;
@@ -316,7 +347,9 @@ const char * pa_scache_get_name_by_id(pa_core *c, uint32_t id) {
uint32_t pa_scache_get_id_by_name(pa_core *c, const char *name) {
pa_scache_entry *e;
- assert(c && name);
+
+ pa_assert(c);
+ pa_assert(name);
if (!(e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE, 0)))
return PA_IDXSET_INVALID;
@@ -327,7 +360,8 @@ uint32_t pa_scache_get_id_by_name(pa_core *c, const char *name) {
uint32_t pa_scache_total_size(pa_core *c) {
pa_scache_entry *e;
uint32_t idx, sum = 0;
- assert(c);
+
+ pa_assert(c);
if (!c->scache || !pa_idxset_size(c->scache))
return 0;
@@ -343,7 +377,8 @@ void pa_scache_unload_unused(pa_core *c) {
pa_scache_entry *e;
time_t now;
uint32_t idx;
- assert(c);
+
+ pa_assert(c);
if (!c->scache || !pa_idxset_size(c->scache))
return;
@@ -370,6 +405,9 @@ static void add_file(pa_core *c, const char *pathname) {
struct stat st;
const char *e;
+ pa_core_assert_ref(c);
+ pa_assert(pathname);
+
e = pa_path_get_filename(pathname);
if (stat(pathname, &st) < 0) {
@@ -385,7 +423,9 @@ static void add_file(pa_core *c, const char *pathname) {
int pa_scache_add_directory_lazy(pa_core *c, const char *pathname) {
DIR *dir;
- assert(c && pathname);
+
+ pa_core_assert_ref(c);
+ pa_assert(pathname);
/* First try to open this as directory */
if (!(dir = opendir(pathname))) {
@@ -415,7 +455,7 @@ int pa_scache_add_directory_lazy(pa_core *c, const char *pathname) {
if (e->d_name[0] == '.')
continue;
- snprintf(p, sizeof(p), "%s/%s", pathname, e->d_name);
+ pa_snprintf(p, sizeof(p), "%s/%s", pathname, e->d_name);
add_file(c, p);
}
}
diff --git a/src/pulsecore/core-scache.h b/src/pulsecore/core-scache.h
index bbf13f15..ab7ec0ef 100644
--- a/src/pulsecore/core-scache.h
+++ b/src/pulsecore/core-scache.h
@@ -55,6 +55,7 @@ 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);
void pa_scache_free(pa_core *c);
const char *pa_scache_get_name_by_id(pa_core *c, uint32_t id);
diff --git a/src/pulsecore/core-subscribe.c b/src/pulsecore/core-subscribe.c
index 6608d57a..06c5a4ad 100644
--- a/src/pulsecore/core-subscribe.c
+++ b/src/pulsecore/core-subscribe.c
@@ -26,12 +26,12 @@
#endif
#include <stdio.h>
-#include <assert.h>
#include <pulse/xmalloc.h>
#include <pulsecore/queue.h>
#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
#include "core-subscribe.h"
@@ -68,9 +68,9 @@ static void sched_event(pa_core *c);
pa_subscription* pa_subscription_new(pa_core *c, pa_subscription_mask_t m, pa_subscription_cb_t callback, void *userdata) {
pa_subscription *s;
- assert(c);
- assert(m);
- assert(callback);
+ pa_assert(c);
+ pa_assert(m);
+ pa_assert(callback);
s = pa_xnew(pa_subscription, 1);
s->core = c;
@@ -85,24 +85,24 @@ pa_subscription* pa_subscription_new(pa_core *c, pa_subscription_mask_t m, pa_su
/* Free a subscription object, effectively marking it for deletion */
void pa_subscription_free(pa_subscription*s) {
- assert(s);
- assert(!s->dead);
+ pa_assert(s);
+ pa_assert(!s->dead);
s->dead = 1;
sched_event(s->core);
}
static void free_subscription(pa_subscription *s) {
- assert(s);
- assert(s->core);
+ pa_assert(s);
+ pa_assert(s->core);
PA_LLIST_REMOVE(pa_subscription, s->core->subscriptions, s);
pa_xfree(s);
}
static void free_event(pa_subscription_event *s) {
- assert(s);
- assert(s->core);
+ pa_assert(s);
+ pa_assert(s->core);
if (!s->next)
s->core->subscription_event_last = s->prev;
@@ -113,7 +113,7 @@ static void free_event(pa_subscription_event *s) {
/* Free all subscription objects */
void pa_subscription_free_all(pa_core *c) {
- assert(c);
+ pa_assert(c);
while (c->subscriptions)
free_subscription(c->subscriptions);
@@ -160,9 +160,9 @@ static void defer_cb(pa_mainloop_api *m, pa_defer_event *de, void *userdata) {
pa_core *c = userdata;
pa_subscription *s;
- assert(c->mainloop == m);
- assert(c);
- assert(c->subscription_defer_event == de);
+ pa_assert(c->mainloop == m);
+ pa_assert(c);
+ pa_assert(c->subscription_defer_event == de);
c->mainloop->defer_enable(c->subscription_defer_event, 0);
@@ -196,20 +196,20 @@ static void defer_cb(pa_mainloop_api *m, pa_defer_event *de, void *userdata) {
/* Schedule an mainloop event so that a pending subscription event is dispatched */
static void sched_event(pa_core *c) {
- assert(c);
+ pa_assert(c);
if (!c->subscription_defer_event) {
c->subscription_defer_event = c->mainloop->defer_new(c->mainloop, defer_cb, c);
- assert(c->subscription_defer_event);
+ pa_assert(c->subscription_defer_event);
}
c->mainloop->defer_enable(c->subscription_defer_event, 1);
}
/* Append a new subscription event to the subscription event queue and schedule a main loop event */
-void pa_subscription_post(pa_core *c, pa_subscription_event_type_t t, uint32_t index) {
+void pa_subscription_post(pa_core *c, pa_subscription_event_type_t t, uint32_t idx) {
pa_subscription_event *e;
- assert(c);
+ pa_assert(c);
/* No need for queuing subscriptions of noone is listening */
if (!c->subscriptions)
@@ -227,7 +227,7 @@ void pa_subscription_post(pa_core *c, pa_subscription_event_type_t t, uint32_t i
continue;
/* not the same object */
- if (i->index != index)
+ if (i->index != idx)
continue;
if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
@@ -253,7 +253,7 @@ void pa_subscription_post(pa_core *c, pa_subscription_event_type_t t, uint32_t i
e = pa_xnew(pa_subscription_event, 1);
e->core = c;
e->type = t;
- e->index = index;
+ e->index = idx;
PA_LLIST_INSERT_AFTER(pa_subscription_event, c->subscription_event_queue, c->subscription_event_last, e);
c->subscription_event_last = e;
diff --git a/src/pulsecore/core-util.c b/src/pulsecore/core-util.c
index e5766b2f..a644b664 100644
--- a/src/pulsecore/core-util.c
+++ b/src/pulsecore/core-util.c
@@ -31,7 +31,6 @@
#include <stdlib.h>
#include <signal.h>
#include <errno.h>
-#include <assert.h>
#include <string.h>
#include <stdio.h>
#include <fcntl.h>
@@ -43,6 +42,10 @@
#include <sys/stat.h>
#include <sys/time.h>
+#ifdef HAVE_STRTOF_L
+#include <locale.h>
+#endif
+
#ifdef HAVE_SCHED_H
#include <sched.h>
#endif
@@ -55,6 +58,10 @@
#include <sys/capability.h>
#endif
+#ifdef HAVE_SYS_MMAN_H
+#include <sys/mman.h>
+#endif
+
#ifdef HAVE_PTHREAD
#include <pthread.h>
#endif
@@ -75,14 +82,19 @@
#include <grp.h>
#endif
+#ifdef HAVE_LIBSAMPLERATE
#include <samplerate.h>
+#endif
#include <pulse/xmalloc.h>
#include <pulse/util.h>
+#include <pulse/utf8.h>
#include <pulsecore/core-error.h>
#include <pulsecore/winsock.h>
#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/thread.h>
#include "core-util.h"
@@ -93,10 +105,8 @@
#ifndef OS_IS_WIN32
#define PA_USER_RUNTIME_PATH_PREFIX "/tmp/pulse-"
-#define PATH_SEP '/'
#else
#define PA_USER_RUNTIME_PATH_PREFIX "%TEMP%\\pulse-"
-#define PATH_SEP '\\'
#endif
#ifdef OS_IS_WIN32
@@ -111,7 +121,7 @@ int pa_set_root(HANDLE handle) {
if (!GetModuleFileName(handle, library_path + sizeof(PULSE_ROOTENV), MAX_PATH))
return 0;
- sep = strrchr(library_path, '\\');
+ sep = strrchr(library_path, PA_PATH_SEP_CHAR);
if (sep)
*sep = '\0';
@@ -124,23 +134,42 @@ int pa_set_root(HANDLE handle) {
#endif
/** Make a file descriptor nonblock. Doesn't do any error checking */
-void pa_make_nonblock_fd(int fd) {
+void pa_make_fd_nonblock(int fd) {
+
#ifdef O_NONBLOCK
int v;
- assert(fd >= 0);
+ pa_assert(fd >= 0);
+
+ pa_assert_se((v = fcntl(fd, F_GETFL)) >= 0);
+
+ if (!(v & O_NONBLOCK))
+ pa_assert_se(fcntl(fd, F_SETFL, v|O_NONBLOCK) >= 0);
- if ((v = fcntl(fd, F_GETFL)) >= 0)
- if (!(v & O_NONBLOCK))
- fcntl(fd, F_SETFL, v|O_NONBLOCK);
#elif defined(OS_IS_WIN32)
u_long arg = 1;
if (ioctlsocket(fd, FIONBIO, &arg) < 0) {
- if (WSAGetLastError() == WSAENOTSOCK)
- pa_log_warn("WARNING: Only sockets can be made non-blocking!");
+ pa_assert_se(WSAGetLastError() == WSAENOTSOCK);
+ pa_log_warn("Only sockets can be made non-blocking!");
}
#else
- pa_log_warn("WARNING: Non-blocking I/O not supported.!");
+ pa_log_warn("Non-blocking I/O not supported.!");
#endif
+
+}
+
+/* Set the FD_CLOEXEC flag for a fd */
+void pa_make_fd_cloexec(int fd) {
+
+#ifdef FD_CLOEXEC
+ int v;
+ pa_assert(fd >= 0);
+
+ pa_assert_se((v = fcntl(fd, F_GETFD, 0)) >= 0);
+
+ if (!(v & FD_CLOEXEC))
+ pa_assert_se(fcntl(fd, F_SETFD, v|FD_CLOEXEC) >= 0);
+#endif
+
}
/** Creates a directory securely */
@@ -148,7 +177,7 @@ int pa_make_secure_dir(const char* dir, mode_t m, uid_t uid, gid_t gid) {
struct stat st;
int r;
- assert(dir);
+ pa_assert(dir);
#ifdef OS_IS_WIN32
r = mkdir(dir);
@@ -169,7 +198,7 @@ int pa_make_secure_dir(const char* dir, mode_t m, uid_t uid, gid_t gid) {
uid = getuid();
if (gid == (gid_t)-1)
gid = getgid();
- chown(dir, uid, gid);
+ (void) chown(dir, uid, gid);
#endif
#ifdef HAVE_CHMOD
@@ -295,9 +324,9 @@ ssize_t pa_loop_read(int fd, void*data, size_t size, int *type) {
ssize_t ret = 0;
int _type;
- assert(fd >= 0);
- assert(data);
- assert(size);
+ pa_assert(fd >= 0);
+ pa_assert(data);
+ pa_assert(size);
if (!type) {
_type = 0;
@@ -326,9 +355,9 @@ ssize_t pa_loop_write(int fd, const void*data, size_t size, int *type) {
ssize_t ret = 0;
int _type;
- assert(fd >= 0);
- assert(data);
- assert(size);
+ pa_assert(fd >= 0);
+ pa_assert(data);
+ pa_assert(size);
if (!type) {
_type = 0;
@@ -354,13 +383,12 @@ ssize_t pa_loop_write(int fd, const void*data, size_t size, int *type) {
/** Platform independent read function. Necessary since not all
* systems treat all file descriptors equal. */
-int pa_close(int fd)
-{
+int pa_close(int fd) {
+
#ifdef OS_IS_WIN32
int ret;
- ret = closesocket(fd);
- if (ret == 0)
+ if ((ret = closesocket(fd)) == 0)
return 0;
if (WSAGetLastError() != WSAENOTSOCK) {
@@ -407,9 +435,9 @@ void pa_check_signal_is_blocked(int sig) {
if (sa.sa_handler != SIG_DFL)
return;
- pa_log("WARNING: %s is not trapped. This might cause malfunction!", pa_strsignal(sig));
+ pa_log_warn("%s is not trapped. This might cause malfunction!", pa_sig2str(sig));
#else /* HAVE_SIGACTION */
- pa_log("WARNING: %s might not be trapped. This might cause malfunction!", pa_strsignal(sig));
+ pa_log_warn("%s might not be trapped. This might cause malfunction!", pa_sig2str(sig));
#endif
}
@@ -419,7 +447,7 @@ char *pa_sprintf_malloc(const char *format, ...) {
int size = 100;
char *c = NULL;
- assert(format);
+ pa_assert(format);
for(;;) {
int r;
@@ -431,6 +459,8 @@ char *pa_sprintf_malloc(const char *format, ...) {
r = vsnprintf(c, size, format, ap);
va_end(ap);
+ c[size-1] = 0;
+
if (r > -1 && r < size)
return c;
@@ -447,19 +477,20 @@ char *pa_vsprintf_malloc(const char *format, va_list ap) {
int size = 100;
char *c = NULL;
- assert(format);
+ pa_assert(format);
for(;;) {
int r;
va_list aq;
- va_copy(aq, ap);
-
c = pa_xrealloc(c, size);
- r = vsnprintf(c, size, format, aq);
+ va_copy(aq, ap);
+ r = vsnprintf(c, size, format, aq);
va_end(aq);
+ c[size-1] = 0;
+
if (r > -1 && r < size)
return c;
@@ -472,36 +503,47 @@ char *pa_vsprintf_malloc(const char *format, va_list ap) {
/* Similar to OpenBSD's strlcpy() function */
char *pa_strlcpy(char *b, const char *s, size_t l) {
- assert(b && s && l > 0);
+ pa_assert(b);
+ pa_assert(s);
+ pa_assert(l > 0);
strncpy(b, s, l);
b[l-1] = 0;
return b;
}
-#define NICE_LEVEL (-15)
+/* Make the current thread a realtime thread*/
+void pa_make_realtime(void) {
-/* Raise the priority of the current process as much as possible and
-sensible: set the nice level to -15 and enable realtime scheduling if
-supported.*/
-void pa_raise_priority(void) {
-#if defined(HAVE_SYS_CAPABILITY_H)
- cap_t caps;
-
- /* Temporarily acquire CAP_SYS_NICE in the effective set */
- if ((caps = cap_get_proc())) {
- cap_t caps_new;
- cap_value_t nice_cap = CAP_SYS_NICE;
-
- if ((caps_new = cap_dup(caps))) {
- cap_set_flag(caps_new, CAP_EFFECTIVE, 1, &nice_cap, CAP_SET);
- cap_set_flag(caps_new, CAP_PERMITTED, 1, &nice_cap, CAP_SET);
- cap_set_proc(caps_new);
- cap_free(caps_new);
- }
+#ifdef _POSIX_PRIORITY_SCHEDULING
+ struct sched_param sp;
+ int r, policy;
+
+ memset(&sp, 0, sizeof(sp));
+ policy = 0;
+
+ if ((r = pthread_getschedparam(pthread_self(), &policy, &sp)) != 0) {
+ pa_log("pthread_getschedgetparam(): %s", pa_cstrerror(r));
+ return;
+ }
+
+ sp.sched_priority = 1;
+ if ((r = pthread_setschedparam(pthread_self(), SCHED_FIFO, &sp)) != 0) {
+ pa_log_warn("pthread_setschedparam(): %s", pa_cstrerror(r));
+ return;
}
+
+ pa_log_info("Successfully enabled SCHED_FIFO scheduling for thread.");
#endif
+}
+
+#define NICE_LEVEL (-11)
+
+/* Raise the priority of the current process as much as possible and
+sensible: set the nice level to -15.*/
+void pa_raise_priority(void) {
+
#ifdef HAVE_SYS_RESOURCE_H
if (setpriority(PRIO_PROCESS, 0, NICE_LEVEL) < 0)
pa_log_warn("setpriority(): %s", pa_cstrerror(errno));
@@ -509,86 +551,26 @@ void pa_raise_priority(void) {
pa_log_info("Successfully gained nice level %i.", NICE_LEVEL);
#endif
-#ifdef _POSIX_PRIORITY_SCHEDULING
- {
- struct sched_param sp;
-
- if (sched_getparam(0, &sp) < 0) {
- pa_log("sched_getparam(): %s", pa_cstrerror(errno));
- goto fail;
- }
-
- sp.sched_priority = 1;
- if (sched_setscheduler(0, SCHED_FIFO, &sp) < 0) {
- pa_log_warn("sched_setscheduler(): %s", pa_cstrerror(errno));
- goto fail;
- }
-
- pa_log_info("Successfully enabled SCHED_FIFO scheduling.");
- }
-#endif
-
#ifdef OS_IS_WIN32
if (!SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS))
pa_log_warn("SetPriorityClass() failed: 0x%08X", GetLastError());
else
pa_log_info("Successfully gained high priority class.");
#endif
-
-fail:
-
-#if defined(HAVE_SYS_CAPABILITY_H)
- if (caps) {
- /* Restore original caps */
- cap_set_proc(caps);
- cap_free(caps);
- }
-#endif
-
- ; /* We put this here to get the code to compile when
- * HAVE_SYS_CAPABILITY_H is not defined. Don't remove unless you
- * know what you do */
}
-/* Reset the priority to normal, inverting the changes made by pa_raise_priority() */
+/* Reset the priority to normal, inverting the changes made by
+ * pa_raise_priority() */
void pa_reset_priority(void) {
#ifdef OS_IS_WIN32
SetPriorityClass(GetCurrentProcess(), NORMAL_PRIORITY_CLASS);
#endif
-#ifdef _POSIX_PRIORITY_SCHEDULING
- {
- struct sched_param sp;
- sched_getparam(0, &sp);
- sp.sched_priority = 0;
- sched_setscheduler(0, SCHED_OTHER, &sp);
- }
-#endif
-
#ifdef HAVE_SYS_RESOURCE_H
setpriority(PRIO_PROCESS, 0, 0);
#endif
}
-/* Set the FD_CLOEXEC flag for a fd */
-int pa_fd_set_cloexec(int fd, int b) {
-
-#ifdef FD_CLOEXEC
- int v;
- assert(fd >= 0);
-
- if ((v = fcntl(fd, F_GETFD, 0)) < 0)
- return -1;
-
- v = (v & ~FD_CLOEXEC) | (b ? FD_CLOEXEC : 0);
-
- if (fcntl(fd, F_SETFD, v) < 0)
- return -1;
-#endif
-
- return 0;
-}
-
/* Try to parse a boolean string value.*/
int pa_parse_boolean(const char *v) {
@@ -639,31 +621,134 @@ char *pa_split_spaces(const char *c, const char **state) {
return pa_xstrndup(current, l);
}
-/* Return the name of an UNIX signal. Similar to GNU's strsignal() */
-const char *pa_strsignal(int sig) {
+PA_STATIC_TLS_DECLARE(signame, pa_xfree);
+
+/* Return the name of an UNIX signal. Similar to Solaris sig2str() */
+const char *pa_sig2str(int sig) {
+ char *t;
+
+ if (sig <= 0)
+ goto fail;
+
+#ifdef NSIG
+ if (sig >= NSIG)
+ goto fail;
+#endif
+
+#ifdef HAVE_SIG2STR
+ {
+ char buf[SIG2STR_MAX];
+
+ if (sig2str(sig, buf) == 0) {
+ pa_xfree(PA_STATIC_TLS_GET(signame));
+ t = pa_sprintf_malloc("SIG%s", buf);
+ PA_STATIC_TLS_SET(signame, t);
+ return t;
+ }
+ }
+#else
+
switch(sig) {
- case SIGINT: return "SIGINT";
- case SIGTERM: return "SIGTERM";
+#ifdef SIGHUP
+ case SIGHUP: return "SIGHUP";
+#endif
+ case SIGINT: return "SIGINT";
+#ifdef SIGQUIT
+ case SIGQUIT: return "SIGQUIT";
+#endif
+ case SIGILL: return "SIGULL";
+#ifdef SIGTRAP
+ case SIGTRAP: return "SIGTRAP";
+#endif
+ case SIGABRT: return "SIGABRT";
+#ifdef SIGBUS
+ case SIGBUS: return "SIGBUS";
+#endif
+ case SIGFPE: return "SIGFPE";
+#ifdef SIGKILL
+ case SIGKILL: return "SIGKILL";
+#endif
#ifdef SIGUSR1
- case SIGUSR1: return "SIGUSR1";
+ case SIGUSR1: return "SIGUSR1";
#endif
+ case SIGSEGV: return "SIGSEGV";
#ifdef SIGUSR2
- case SIGUSR2: return "SIGUSR2";
-#endif
-#ifdef SIGXCPU
- case SIGXCPU: return "SIGXCPU";
+ case SIGUSR2: return "SIGUSR2";
#endif
#ifdef SIGPIPE
- case SIGPIPE: return "SIGPIPE";
+ case SIGPIPE: return "SIGPIPE";
+#endif
+#ifdef SIGALRM
+ case SIGALRM: return "SIGALRM";
+#endif
+ case SIGTERM: return "SIGTERM";
+#ifdef SIGSTKFLT
+ case SIGSTKFLT: return "SIGSTKFLT";
#endif
#ifdef SIGCHLD
- case SIGCHLD: return "SIGCHLD";
+ case SIGCHLD: return "SIGCHLD";
#endif
-#ifdef SIGHUP
- case SIGHUP: return "SIGHUP";
+#ifdef SIGCONT
+ case SIGCONT: return "SIGCONT";
+#endif
+#ifdef SIGSTOP
+ case SIGSTOP: return "SIGSTOP";
+#endif
+#ifdef SIGTSTP
+ case SIGTSTP: return "SIGTSTP";
+#endif
+#ifdef SIGTTIN
+ case SIGTTIN: return "SIGTTIN";
+#endif
+#ifdef SIGTTOU
+ case SIGTTOU: return "SIGTTOU";
+#endif
+#ifdef SIGURG
+ case SIGURG: return "SIGURG";
+#endif
+#ifdef SIGXCPU
+ case SIGXCPU: return "SIGXCPU";
+#endif
+#ifdef SIGXFSZ
+ case SIGXFSZ: return "SIGXFSZ";
#endif
- default: return "UNKNOWN SIGNAL";
+#ifdef SIGVTALRM
+ case SIGVTALRM: return "SIGVTALRM";
+#endif
+#ifdef SIGPROF
+ case SIGPROF: return "SIGPROF";
+#endif
+#ifdef SIGWINCH
+ case SIGWINCH: return "SIGWINCH";
+#endif
+#ifdef SIGIO
+ case SIGIO: return "SIGIO";
+#endif
+#ifdef SIGPWR
+ case SIGPWR: return "SIGPWR";
+#endif
+#ifdef SIGSYS
+ case SIGSYS: return "SIGSYS";
+#endif
+ }
+
+#ifdef SIGRTMIN
+ if (sig >= SIGRTMIN && sig <= SIGRTMAX) {
+ pa_xfree(PA_STATIC_TLS_GET(signame));
+ t = pa_sprintf_malloc("SIGRTMIN+%i", sig - SIGRTMIN);
+ PA_STATIC_TLS_SET(signame, t);
+ return t;
}
+#endif
+
+#endif
+
+fail:
+
+ pa_xfree(PA_STATIC_TLS_GET(signame));
+ t = pa_sprintf_malloc("SIG%i", sig);
+ PA_STATIC_TLS_SET(signame, t);
+ return t;
}
#ifdef HAVE_GRP_H
@@ -715,7 +800,7 @@ int pa_own_uid_in_group(const char *name, gid_t *gid) {
int n = sysconf(_SC_NGROUPS_MAX);
int r = -1, i;
- assert(n > 0);
+ pa_assert(n > 0);
gids = pa_xmalloc(sizeof(GETGROUPS_T)*n);
@@ -861,8 +946,7 @@ int pa_lock_fd(int fd, int b) {
return 0;
}
- pa_log("%slock: %s", !b? "un" : "",
- pa_cstrerror(errno));
+ pa_log("%slock: %s", !b? "un" : "", pa_cstrerror(errno));
#endif
#ifdef OS_IS_WIN32
@@ -881,7 +965,7 @@ int pa_lock_fd(int fd, int b) {
/* Remove trailing newlines from a string */
char* pa_strip_nl(char *s) {
- assert(s);
+ pa_assert(s);
s[strcspn(s, "\r\n")] = 0;
return s;
@@ -890,38 +974,46 @@ char* pa_strip_nl(char *s) {
/* Create a temporary lock file and lock it. */
int pa_lock_lockfile(const char *fn) {
int fd = -1;
- assert(fn);
+ pa_assert(fn);
for (;;) {
struct stat st;
- if ((fd = open(fn, O_CREAT|O_RDWR, S_IRUSR|S_IWUSR)) < 0) {
- pa_log("failed to create lock file '%s': %s", fn,
- pa_cstrerror(errno));
+ if ((fd = open(fn, O_CREAT|O_RDWR
+#ifdef O_NOCTTY
+ |O_NOCTTY
+#endif
+#ifdef O_NOFOLLOW
+ |O_NOFOLLOW
+#endif
+ , S_IRUSR|S_IWUSR)) < 0) {
+ pa_log_warn("Failed to create lock file '%s': %s", fn, pa_cstrerror(errno));
goto fail;
}
if (pa_lock_fd(fd, 1) < 0) {
- pa_log("failed to lock file '%s'.", fn);
+ pa_log_warn("Failed to lock file '%s'.", fn);
goto fail;
}
if (fstat(fd, &st) < 0) {
- pa_log("failed to fstat() file '%s'.", fn);
+ pa_log_warn("Failed to fstat() file '%s': %s", fn, pa_cstrerror(errno));
goto fail;
}
- /* Check wheter the file has been removed meanwhile. When yes, restart this loop, otherwise, we're done */
+ /* Check wheter the file has been removed meanwhile. When yes,
+ * restart this loop, otherwise, we're done */
if (st.st_nlink >= 1)
break;
if (pa_lock_fd(fd, 0) < 0) {
- pa_log("failed to unlock file '%s'.", fn);
+ pa_log_warn("Failed to unlock file '%s'.", fn);
goto fail;
}
- if (close(fd) < 0) {
- pa_log("failed to close file '%s'.", fn);
+ if (pa_close(fd) < 0) {
+ pa_log_warn("Failed to close file '%s': %s", fn, pa_cstrerror(errno));
+ fd = -1;
goto fail;
}
@@ -933,7 +1025,7 @@ int pa_lock_lockfile(const char *fn) {
fail:
if (fd >= 0)
- close(fd);
+ pa_close(fd);
return -1;
}
@@ -941,22 +1033,21 @@ fail:
/* Unlock a temporary lcok file */
int pa_unlock_lockfile(const char *fn, int fd) {
int r = 0;
- assert(fn && fd >= 0);
+ pa_assert(fn);
+ pa_assert(fd >= 0);
if (unlink(fn) < 0) {
- pa_log_warn("WARNING: unable to remove lock file '%s': %s",
- fn, pa_cstrerror(errno));
+ pa_log_warn("Unable to remove lock file '%s': %s", fn, pa_cstrerror(errno));
r = -1;
}
if (pa_lock_fd(fd, 0) < 0) {
- pa_log_warn("WARNING: failed to unlock file '%s'.", fn);
+ pa_log_warn("Failed to unlock file '%s'.", fn);
r = -1;
}
- if (close(fd) < 0) {
- pa_log_warn("WARNING: failed to close lock file '%s': %s",
- fn, pa_cstrerror(errno));
+ if (pa_close(fd) < 0) {
+ pa_log_warn("Failed to close '%s': %s", fn, pa_cstrerror(errno));
r = -1;
}
@@ -1019,10 +1110,8 @@ FILE *pa_open_config_file(const char *global, const char *local, const char *env
return f;
}
- if (errno != ENOENT) {
- pa_log_warn("WARNING: failed to open configuration file '%s': %s",
- lfn, pa_cstrerror(errno));
- }
+ if (errno != ENOENT)
+ pa_log_warn("Failed to open configuration file '%s': %s", lfn, pa_cstrerror(errno));
pa_xfree(lfn);
}
@@ -1051,7 +1140,10 @@ FILE *pa_open_config_file(const char *global, const char *local, const char *env
char *pa_hexstr(const uint8_t* d, size_t dlength, char *s, size_t slength) {
size_t i = 0, j = 0;
const char hex[] = "0123456789abcdef";
- assert(d && s && slength > 0);
+
+ pa_assert(d);
+ pa_assert(s);
+ pa_assert(slength > 0);
while (i < dlength && j+3 <= slength) {
s[j++] = hex[*d >> 4];
@@ -1082,7 +1174,9 @@ static int hexc(char c) {
/* Parse a hexadecimal string as created by pa_hexstr() to a BLOB */
size_t pa_parsehex(const char *p, uint8_t *d, size_t dlength) {
size_t j = 0;
- assert(p && d);
+
+ pa_assert(p);
+ pa_assert(d);
while (j < dlength && *p) {
int b;
@@ -1109,8 +1203,8 @@ size_t pa_parsehex(const char *p, uint8_t *d, size_t dlength) {
int pa_startswith(const char *s, const char *pfx) {
size_t l;
- assert(s);
- assert(pfx);
+ pa_assert(s);
+ pa_assert(pfx);
l = strlen(pfx);
@@ -1121,8 +1215,8 @@ int pa_startswith(const char *s, const char *pfx) {
int pa_endswith(const char *s, const char *sfx) {
size_t l1, l2;
- assert(s);
- assert(sfx);
+ pa_assert(s);
+ pa_assert(sfx);
l1 = strlen(s);
l2 = strlen(sfx);
@@ -1146,17 +1240,17 @@ char *pa_runtime_path(const char *fn, char *s, size_t l) {
if ((e = getenv("PULSE_RUNTIME_PATH"))) {
if (fn)
- snprintf(s, l, "%s%c%s", e, PATH_SEP, fn);
+ pa_snprintf(s, l, "%s%c%s", e, PA_PATH_SEP_CHAR, fn);
else
- snprintf(s, l, "%s", e);
+ pa_snprintf(s, l, "%s", e);
} else {
char u[256];
if (fn)
- snprintf(s, l, "%s%s%c%s", PA_USER_RUNTIME_PATH_PREFIX, pa_get_user_name(u, sizeof(u)), PATH_SEP, 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
- snprintf(s, l, "%s%s", PA_USER_RUNTIME_PATH_PREFIX, pa_get_user_name(u, sizeof(u)));
+ pa_snprintf(s, l, "%s%s", PA_USER_RUNTIME_PATH_PREFIX, pa_get_user_name(u, sizeof(u)));
}
@@ -1175,11 +1269,17 @@ char *pa_runtime_path(const char *fn, char *s, size_t l) {
int pa_atoi(const char *s, int32_t *ret_i) {
char *x = NULL;
long l;
- assert(s && ret_i);
+ pa_assert(s);
+ pa_assert(ret_i);
+
+ errno = 0;
l = strtol(s, &x, 0);
- if (!x || *x)
+ if (!x || *x || errno != 0)
+ return -1;
+
+ if ((int32_t) l != l)
return -1;
*ret_i = (int32_t) l;
@@ -1191,14 +1291,219 @@ int pa_atoi(const char *s, int32_t *ret_i) {
int pa_atou(const char *s, uint32_t *ret_u) {
char *x = NULL;
unsigned long l;
- assert(s && ret_u);
+ pa_assert(s);
+ pa_assert(ret_u);
+
+ errno = 0;
l = strtoul(s, &x, 0);
- if (!x || *x)
+ if (!x || *x || errno != 0)
+ return -1;
+
+ if ((uint32_t) l != l)
return -1;
*ret_u = (uint32_t) l;
return 0;
}
+
+#ifdef HAVE_STRTOF_L
+static locale_t c_locale = NULL;
+
+static void c_locale_destroy(void) {
+ freelocale(c_locale);
+}
+#endif
+
+int pa_atof(const char *s, float *ret_f) {
+ char *x = NULL;
+ float f;
+ int r = 0;
+
+ pa_assert(s);
+ pa_assert(ret_f);
+
+ /* This should be locale independent */
+
+#ifdef HAVE_STRTOF_L
+
+ PA_ONCE_BEGIN {
+
+ if ((c_locale = newlocale(LC_ALL_MASK, "C", NULL)))
+ atexit(c_locale_destroy);
+
+ } PA_ONCE_END;
+
+ if (c_locale) {
+ errno = 0;
+ f = strtof_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;
+
+ return r;
+}
+
+/* Same as snprintf, but guarantees NUL-termination on every platform */
+int pa_snprintf(char *str, size_t size, const char *format, ...) {
+ int ret;
+ va_list ap;
+
+ pa_assert(str);
+ pa_assert(size > 0);
+ pa_assert(format);
+
+ va_start(ap, format);
+ ret = vsnprintf(str, size, format, ap);
+ va_end(ap);
+
+ str[size-1] = 0;
+
+ return ret;
+}
+
+/* Truncate the specified string, but guarantee that the string
+ * returned still validates as UTF8 */
+char *pa_truncate_utf8(char *c, size_t l) {
+ pa_assert(c);
+ pa_assert(pa_utf8_valid(c));
+
+ if (strlen(c) <= l)
+ return c;
+
+ c[l] = 0;
+
+ while (l > 0 && !pa_utf8_valid(c))
+ c[--l] = 0;
+
+ return c;
+}
+
+char *pa_getcwd(void) {
+ size_t l = 128;
+
+ for (;;) {
+ char *p = pa_xnew(char, l);
+ if (getcwd(p, l))
+ return p;
+
+ if (errno != ERANGE)
+ return NULL;
+
+ pa_xfree(p);
+ l *= 2;
+ }
+}
+
+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;
+#endif
+ const void *a;
+ size_t size;
+ int r;
+ size_t bs;
+
+ pa_assert(p);
+ pa_assert(l > 0);
+
+ a = PA_PAGE_ALIGN_PTR(p);
+ size = (const uint8_t*) p + l - (const uint8_t*) a;
+
+#ifdef HAVE_POSIX_MADVISE
+ if ((r = posix_madvise((void*) a, size, POSIX_MADV_WILLNEED)) == 0) {
+ pa_log_debug("posix_madvise() worked fine!");
+ return (void*) p;
+ }
+#endif
+
+ /* Most likely the memory was not mmap()ed from a file and thus
+ * madvise() didn't work, so let's misuse mlock() do page this
+ * stuff back into RAM. Yeah, let's fuck with the MM! It's so
+ * inviting, the man page of mlock() tells us: "All pages that
+ * contain a part of the specified address range are guaranteed to
+ * be resident in RAM when the call returns successfully." */
+
+#ifdef RLIMIT_MEMLOCK
+ pa_assert_se(getrlimit(RLIMIT_MEMLOCK, &rlim) == 0);
+
+ 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));
+ return (void*) p;
+ }
+
+ bs = PA_PAGE_ALIGN(rlim.rlim_cur);
+#else
+ bs = PA_PAGE_SIZE*4;
+#endif
+
+ pa_log_debug("posix_madvise() failed (or doesn't exist), trying mlock(): %s", pa_cstrerror(r));
+
+#ifdef HAVE_MLOCK
+ while (size > 0 && bs > 0) {
+
+ if (bs > size)
+ bs = size;
+
+ if (mlock(a, bs) < 0) {
+ bs = PA_PAGE_ALIGN(bs / 2);
+ continue;
+ }
+
+ pa_assert_se(munlock(a, bs) == 0);
+
+ a = (const uint8_t*) a + bs;
+ size -= bs;
+ }
+#endif
+
+ if (bs <= 0)
+ pa_log_debug("mlock() failed too (or doesn't exist), giving up: %s", pa_cstrerror(errno));
+ else
+ pa_log_debug("mlock() worked fine!");
+
+ return (void*) p;
+}
+
+void pa_close_pipe(int fds[2]) {
+ pa_assert(fds);
+
+ if (fds[0] >= 0)
+ pa_assert_se(pa_close(fds[0]) == 0);
+
+ if (fds[1] >= 0)
+ pa_assert_se(pa_close(fds[1]) == 0);
+
+ fds[0] = fds[1] = -1;
+}
diff --git a/src/pulsecore/core-util.h b/src/pulsecore/core-util.h
index 1d921e03..0fe865ec 100644
--- a/src/pulsecore/core-util.h
+++ b/src/pulsecore/core-util.h
@@ -34,7 +34,8 @@
struct timeval;
-void pa_make_nonblock_fd(int fd);
+void pa_make_fd_nonblock(int fd);
+void pa_make_fd_cloexec(int fd);
int pa_make_secure_dir(const char* dir, mode_t m, uid_t uid, gid_t gid);
int pa_make_secure_parent_dir(const char *fn, mode_t, uid_t uid, gid_t gid);
@@ -55,19 +56,18 @@ char *pa_strlcpy(char *b, const char *s, size_t l);
char *pa_parent_dir(const char *fn);
+void pa_make_realtime(void);
void pa_raise_priority(void);
void pa_reset_priority(void);
-int pa_fd_set_cloexec(int fd, int b);
-
-int pa_parse_boolean(const char *s);
+int pa_parse_boolean(const char *s) PA_GCC_PURE;
char *pa_split(const char *c, const char*delimiters, const char **state);
char *pa_split_spaces(const char *c, const char **state);
char *pa_strip_nl(char *s);
-const char *pa_strsignal(int sig);
+const char *pa_sig2str(int sig) PA_GCC_PURE;
int pa_own_uid_in_group(const char *name, gid_t *gid);
int pa_uid_in_group(uid_t uid, const char *name);
@@ -84,12 +84,42 @@ FILE *pa_open_config_file(const char *global, const char *local, const char *env
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);
-int pa_endswith(const char *s, const char *sfx);
+int pa_startswith(const char *s, const char *pfx) PA_GCC_PURE;
+int pa_endswith(const char *s, const char *sfx) PA_GCC_PURE;
char *pa_runtime_path(const char *fn, char *s, size_t l);
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_snprintf(char *str, size_t size, const char *format, ...);
+
+char *pa_truncate_utf8(char *c, size_t l);
+
+char *pa_getcwd(void);
+char *pa_make_path_absolute(const char *p);
+
+void *pa_will_need(const void *p, size_t l);
+
+static inline int pa_is_power_of_two(unsigned n) {
+ return !(n & (n - 1));
+}
+
+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;
+}
+
+void pa_close_pipe(int fds[2]);
#endif
diff --git a/src/pulsecore/core.c b/src/pulsecore/core.c
index 31b6c188..e9008833 100644
--- a/src/pulsecore/core.c
+++ b/src/pulsecore/core.c
@@ -27,7 +27,6 @@
#endif
#include <stdlib.h>
-#include <assert.h>
#include <stdio.h>
#include <signal.h>
@@ -45,12 +44,36 @@
#include <pulsecore/props.h>
#include <pulsecore/random.h>
#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
#include "core.h"
+static PA_DEFINE_CHECK_TYPE(pa_core, pa_msgobject);
+
+static int core_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk) {
+ pa_core *c = PA_CORE(o);
+
+ pa_core_assert_ref(c);
+
+ switch (code) {
+
+ case PA_CORE_MESSAGE_UNLOAD_MODULE:
+ pa_module_unload(c, userdata);
+ return 0;
+
+ default:
+ return -1;
+ }
+}
+
+static void core_free(pa_object *o);
+
pa_core* pa_core_new(pa_mainloop_api *m, int shared) {
pa_core* c;
pa_mempool *pool;
+ int j;
+
+ pa_assert(m);
if (shared) {
if (!(pool = pa_mempool_new(shared))) {
@@ -66,7 +89,9 @@ pa_core* pa_core_new(pa_mainloop_api *m, int shared) {
}
}
- c = pa_xnew(pa_core, 1);
+ c = pa_msgobject_new(pa_core);
+ c->parent.parent.free = core_free;
+ c->parent.process_msg = core_process_msg;
c->mainloop = m;
c->clients = pa_idxset_new(NULL, NULL);
@@ -87,6 +112,8 @@ pa_core* pa_core_new(pa_mainloop_api *m, int shared) {
c->default_sample_spec.format = PA_SAMPLE_S16NE;
c->default_sample_spec.rate = 44100;
c->default_sample_spec.channels = 2;
+ c->default_n_fragments = 4;
+ c->default_fragment_size_msec = 25;
c->module_auto_unload_event = NULL;
c->module_defer_unload_event = NULL;
@@ -99,22 +126,21 @@ pa_core* pa_core_new(pa_mainloop_api *m, int shared) {
c->mempool = pool;
- c->disallow_module_loading = 0;
-
c->quit_event = NULL;
c->exit_idle_time = -1;
c->module_idle_time = 20;
c->scache_idle_time = 20;
- c->resample_method = PA_RESAMPLER_SRC_SINC_FASTEST;
+ c->resample_method = PA_RESAMPLER_SPEEX_FLOAT_BASE;
c->is_system_instance = 0;
+ c->disallow_module_loading = 0;
+ c->high_priority = 0;
+
- pa_hook_init(&c->hook_sink_input_new, c);
- pa_hook_init(&c->hook_sink_disconnect, c);
- pa_hook_init(&c->hook_source_output_new, c);
- pa_hook_init(&c->hook_source_disconnect, c);
+ for (j = 0; j < PA_CORE_HOOK_MAX; j++)
+ pa_hook_init(&c->hooks[j], c);
pa_property_init(c);
@@ -123,28 +149,31 @@ pa_core* pa_core_new(pa_mainloop_api *m, int shared) {
#ifdef SIGPIPE
pa_check_signal_is_blocked(SIGPIPE);
#endif
+
return c;
}
-void pa_core_free(pa_core *c) {
- assert(c);
+static void core_free(pa_object *o) {
+ pa_core *c = PA_CORE(o);
+ int j;
+ pa_assert(c);
pa_module_unload_all(c);
- assert(!c->modules);
+ pa_assert(!c->modules);
- assert(pa_idxset_isempty(c->clients));
+ pa_assert(pa_idxset_isempty(c->clients));
pa_idxset_free(c->clients, NULL, NULL);
- assert(pa_idxset_isempty(c->sinks));
+ pa_assert(pa_idxset_isempty(c->sinks));
pa_idxset_free(c->sinks, NULL, NULL);
- assert(pa_idxset_isempty(c->sources));
+ pa_assert(pa_idxset_isempty(c->sources));
pa_idxset_free(c->sources, NULL, NULL);
- assert(pa_idxset_isempty(c->source_outputs));
+ pa_assert(pa_idxset_isempty(c->source_outputs));
pa_idxset_free(c->source_outputs, NULL, NULL);
- assert(pa_idxset_isempty(c->sink_inputs));
+ pa_assert(pa_idxset_isempty(c->sink_inputs));
pa_idxset_free(c->sink_inputs, NULL, NULL);
pa_scache_free(c);
@@ -162,23 +191,21 @@ void pa_core_free(pa_core *c) {
pa_property_cleanup(c);
- pa_hook_free(&c->hook_sink_input_new);
- pa_hook_free(&c->hook_sink_disconnect);
- pa_hook_free(&c->hook_source_output_new);
- pa_hook_free(&c->hook_source_disconnect);
+ for (j = 0; j < PA_CORE_HOOK_MAX; j++)
+ pa_hook_free(&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) {
pa_core *c = userdata;
- assert(c->quit_event = e);
+ pa_assert(c->quit_event == e);
m->quit(m, 0);
}
void pa_core_check_quit(pa_core *c) {
- assert(c);
+ pa_assert(c);
if (!c->quit_event && c->exit_idle_time >= 0 && pa_idxset_size(c->clients) == 0) {
struct timeval tv;
diff --git a/src/pulsecore/core.h b/src/pulsecore/core.h
index 51a18b62..dfa80f8d 100644
--- a/src/pulsecore/core.h
+++ b/src/pulsecore/core.h
@@ -34,17 +34,51 @@
#include <pulsecore/queue.h>
#include <pulsecore/llist.h>
#include <pulsecore/hook-list.h>
+#include <pulsecore/asyncmsgq.h>
typedef struct pa_core pa_core;
#include <pulsecore/core-subscribe.h>
#include <pulsecore/sink-input.h>
+#include <pulsecore/msgobject.h>
+
+typedef enum pa_core_hook {
+ PA_CORE_HOOK_SINK_NEW_POST,
+ 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_SOURCE_UNLINK,
+ PA_CORE_HOOK_SOURCE_UNLINK_POST,
+ PA_CORE_HOOK_SOURCE_STATE_CHANGED,
+ PA_CORE_HOOK_SOURCE_DESCRIPTION_CHANGED,
+ PA_CORE_HOOK_SINK_INPUT_NEW,
+ PA_CORE_HOOK_SINK_INPUT_PUT,
+ PA_CORE_HOOK_SINK_INPUT_UNLINK,
+ PA_CORE_HOOK_SINK_INPUT_UNLINK_POST,
+ PA_CORE_HOOK_SINK_INPUT_MOVE,
+ PA_CORE_HOOK_SINK_INPUT_MOVE_POST,
+ PA_CORE_HOOK_SINK_INPUT_NAME_CHANGED,
+ PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED,
+ PA_CORE_HOOK_SOURCE_OUTPUT_NEW,
+ PA_CORE_HOOK_SOURCE_OUTPUT_PUT,
+ PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK,
+ PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK_POST,
+ PA_CORE_HOOK_SOURCE_OUTPUT_MOVE,
+ PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_POST,
+ PA_CORE_HOOK_SOURCE_OUTPUT_NAME_CHANGED,
+ PA_CORE_HOOK_SOURCE_OUTPUT_STATE_CHANGED,
+ PA_CORE_HOOK_MAX
+} pa_core_hook_t;
/* The core structure of PulseAudio. Every PulseAudio daemon contains
* exactly one of these. It is used for storing kind of global
* variables for the daemon. */
struct pa_core {
+ pa_msgobject parent;
+
/* A random value which may be used to identify this instance of
* PulseAudio. Not cryptographically secure in any way. */
uint32_t cookie;
@@ -61,6 +95,8 @@ struct pa_core {
char *default_source_name, *default_sink_name;
pa_sample_spec default_sample_spec;
+ unsigned default_n_fragments, default_fragment_size_msec;
+
pa_time_event *module_auto_unload_event;
pa_defer_event *module_defer_unload_event;
@@ -71,27 +107,30 @@ struct pa_core {
pa_mempool *mempool;
- int disallow_module_loading, running_as_daemon;
int exit_idle_time, module_idle_time, scache_idle_time;
pa_time_event *quit_event;
pa_time_event *scache_auto_unload_event;
+ int disallow_module_loading, running_as_daemon;
pa_resample_method_t resample_method;
-
int is_system_instance;
+ int high_priority;
/* hooks */
- pa_hook
- hook_sink_input_new,
- hook_sink_disconnect,
- hook_source_output_new,
- hook_source_disconnect;
+ pa_hook hooks[PA_CORE_HOOK_MAX];
+};
+
+PA_DECLARE_CLASS(pa_core);
+#define PA_CORE(o) pa_core_cast(o)
+
+enum {
+ PA_CORE_MESSAGE_UNLOAD_MODULE,
+ PA_CORE_MESSAGE_MAX
};
pa_core* pa_core_new(pa_mainloop_api *m, int shared);
-void pa_core_free(pa_core*c);
/* Check whether noone is connected to this core */
void pa_core_check_quit(pa_core *c);
diff --git a/src/pulsecore/creds.h b/src/pulsecore/creds.h
index e0a025bd..51dfc33d 100644
--- a/src/pulsecore/creds.h
+++ b/src/pulsecore/creds.h
@@ -26,7 +26,9 @@
#include <sys/types.h>
-/* config.h must be included before this file */
+#ifndef PACKAGE
+#error "Please include config.h before including this file!"
+#endif
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
diff --git a/src/pulsecore/dynarray.c b/src/pulsecore/dynarray.c
index 944e3570..8bdb46fa 100644
--- a/src/pulsecore/dynarray.c
+++ b/src/pulsecore/dynarray.c
@@ -26,10 +26,10 @@
#endif
#include <string.h>
-#include <assert.h>
#include <stdlib.h>
#include <pulse/xmalloc.h>
+#include <pulsecore/macro.h>
#include "dynarray.h"
@@ -52,7 +52,7 @@ pa_dynarray* pa_dynarray_new(void) {
void pa_dynarray_free(pa_dynarray* a, void (*func)(void *p, void *userdata), void *userdata) {
unsigned i;
- assert(a);
+ pa_assert(a);
if (func)
for (i = 0; i < a->n_entries; i++)
@@ -64,7 +64,7 @@ void pa_dynarray_free(pa_dynarray* a, void (*func)(void *p, void *userdata), voi
}
void pa_dynarray_put(pa_dynarray*a, unsigned i, void *p) {
- assert(a);
+ pa_assert(a);
if (i >= a->n_allocated) {
unsigned n;
@@ -85,21 +85,27 @@ void pa_dynarray_put(pa_dynarray*a, unsigned i, void *p) {
}
unsigned pa_dynarray_append(pa_dynarray*a, void *p) {
- unsigned i = a->n_entries;
+ unsigned i;
+
+ pa_assert(a);
+
+ i = a->n_entries;
pa_dynarray_put(a, i, p);
return i;
}
void *pa_dynarray_get(pa_dynarray*a, unsigned i) {
- assert(a);
+ pa_assert(a);
+
if (i >= a->n_entries)
return NULL;
- assert(a->data);
+ pa_assert(a->data);
return a->data[i];
}
unsigned pa_dynarray_size(pa_dynarray*a) {
- assert(a);
+ pa_assert(a);
+
return a->n_entries;
}
diff --git a/src/pulsecore/endianmacros.h b/src/pulsecore/endianmacros.h
index c0c3a6d8..8f7cfade 100644
--- a/src/pulsecore/endianmacros.h
+++ b/src/pulsecore/endianmacros.h
@@ -27,54 +27,68 @@
#include <inttypes.h>
-#ifdef HAVE_CONFIG_H
-#include <config.h>
+#ifndef PACKAGE
+#error "Please include config.h before including this file!"
#endif
-#define INT16_SWAP(x) ( (int16_t) ( ((uint16_t) x >> 8) | ((uint16_t) x << 8) ) )
-#define UINT16_SWAP(x) ( (uint16_t) ( ((uint16_t) x >> 8) | ((uint16_t) x << 8) ) )
-#define INT32_SWAP(x) ( (int32_t) ( ((uint32_t) x >> 24) | ((uint32_t) x << 24) | (((uint32_t) x & 0xFF00) << 8) | ((((uint32_t) x) >> 8) & 0xFF00) ) )
-#define UINT32_SWAP(x) ( (uint32_t) ( ((uint32_t) x >> 24) | ((uint32_t) x << 24) | (((uint32_t) x & 0xFF00) << 8) | ((((uint32_t) x) >> 8) & 0xFF00) ) )
+#ifdef HAVE_BYTESWAP_H
+#include <byteswap.h>
+#endif
+
+#ifdef HAVE_BYTESWAP_H
+#define PA_INT16_SWAP(x) ((int16_t) bswap_16((uint16_t) x))
+#define PA_UINT16_SWAP(x) ((uint16_t) bswap_16((uint16_t) x))
+#define PA_INT32_SWAP(x) ((int32_t) bswap_32((uint32_t) x))
+#define PA_UINT32_SWAP(x) ((uint32_t) bswap_32((uint32_t) x))
+#else
+#define PA_INT16_SWAP(x) ( (int16_t) ( ((uint16_t) x >> 8) | ((uint16_t) x << 8) ) )
+#define PA_UINT16_SWAP(x) ( (uint16_t) ( ((uint16_t) x >> 8) | ((uint16_t) x << 8) ) )
+#define PA_INT32_SWAP(x) ( (int32_t) ( ((uint32_t) x >> 24) | ((uint32_t) x << 24) | (((uint32_t) x & 0xFF00) << 8) | ((((uint32_t) x) >> 8) & 0xFF00) ) )
+#define PA_UINT32_SWAP(x) ( (uint32_t) ( ((uint32_t) x >> 24) | ((uint32_t) x << 24) | (((uint32_t) x & 0xFF00) << 8) | ((((uint32_t) x) >> 8) & 0xFF00) ) )
+#endif
+
+#define PA_MAYBE_INT16_SWAP(c,x) ((c) ? PA_INT32_SWAP(x) : x)
+#define PA_MAYBE_UINT16_SWAP(c,x) ((c) ? PA_UINT32_SWAP(x) : x)
-#define MAYBE_INT32_SWAP(c,x) ((c) ? INT32_SWAP(x) : x)
-#define MAYBE_UINT32_SWAP(c,x) ((c) ? UINT32_SWAP(x) : x)
+#define PA_MAYBE_INT32_SWAP(c,x) ((c) ? PA_INT32_SWAP(x) : x)
+#define PA_MAYBE_UINT32_SWAP(c,x) ((c) ? PA_UINT32_SWAP(x) : x)
#ifdef WORDS_BIGENDIAN
- #define INT16_FROM_LE(x) INT16_SWAP(x)
- #define INT16_FROM_BE(x) ((int16_t)(x))
+ #define PA_INT16_FROM_LE(x) PA_INT16_SWAP(x)
+ #define PA_INT16_FROM_BE(x) ((int16_t)(x))
- #define INT16_TO_LE(x) INT16_SWAP(x)
- #define INT16_TO_BE(x) ((int16_t)(x))
+ #define PA_INT16_TO_LE(x) PA_INT16_SWAP(x)
+ #define PA_INT16_TO_BE(x) ((int16_t)(x))
- #define UINT16_FROM_LE(x) UINT16_SWAP(x)
- #define UINT16_FROM_BE(x) ((uint16_t)(x))
+ #define PA_UINT16_FROM_LE(x) PA_UINT16_SWAP(x)
+ #define PA_UINT16_FROM_BE(x) ((uint16_t)(x))
- #define INT32_FROM_LE(x) INT32_SWAP(x)
- #define INT32_FROM_BE(x) ((int32_t)(x))
+ #define PA_INT32_FROM_LE(x) PA_INT32_SWAP(x)
+ #define PA_INT32_FROM_BE(x) ((int32_t)(x))
- #define UINT32_FROM_LE(x) UINT32_SWAP(x)
- #define UINT32_FROM_BE(x) ((uint32_t)(x))
+ #define PA_UINT32_FROM_LE(x) PA_UINT32_SWAP(x)
+ #define PA_UINT32_FROM_BE(x) ((uint32_t)(x))
- #define UINT32_TO_LE(x) UINT32_SWAP(x)
- #define UINT32_TO_BE(x) ((uint32_t)(x))
+ #define PA_UINT32_TO_LE(x) PA_UINT32_SWAP(x)
+ #define PA_UINT32_TO_BE(x) ((uint32_t)(x))
#else
- #define INT16_FROM_LE(x) ((int16_t)(x))
- #define INT16_FROM_BE(x) INT16_SWAP(x)
+ #define PA_INT16_FROM_LE(x) ((int16_t)(x))
+ #define PA_INT16_FROM_BE(x) PA_INT16_SWAP(x)
- #define INT16_TO_LE(x) ((int16_t)(x))
- #define INT16_TO_BE(x) INT16_SWAP(x)
+ #define PA_INT16_TO_LE(x) ((int16_t)(x))
+ #define PA_INT16_TO_BE(x) PA_INT16_SWAP(x)
- #define UINT16_FROM_LE(x) ((uint16_t)(x))
- #define UINT16_FROM_BE(x) UINT16_SWAP(x)
+ #define PA_UINT16_FROM_LE(x) ((uint16_t)(x))
+ #define PA_UINT16_FROM_BE(x) PA_UINT16_SWAP(x)
- #define INT32_FROM_LE(x) ((int32_t)(x))
- #define INT32_FROM_BE(x) INT32_SWAP(x)
+ #define PA_INT32_FROM_LE(x) ((int32_t)(x))
+ #define PA_INT32_FROM_BE(x) PA_INT32_SWAP(x)
- #define UINT32_FROM_LE(x) ((uint32_t)(x))
- #define UINT32_FROM_BE(x) UINT32_SWAP(x)
+ #define PA_UINT32_FROM_LE(x) ((uint32_t)(x))
+ #define PA_UINT32_FROM_BE(x) PA_UINT32_SWAP(x)
- #define UINT32_TO_LE(x) ((uint32_t)(x))
- #define UINT32_TO_BE(x) UINT32_SWAP(x)
+ #define PA_UINT32_TO_LE(x) ((uint32_t)(x))
+ #define PA_UINT32_TO_BE(x) PA_UINT32_SWAP(x)
#endif
#endif
diff --git a/src/pulsecore/esound.h b/src/pulsecore/esound.h
index 3778a535..ea6a5665 100644
--- a/src/pulsecore/esound.h
+++ b/src/pulsecore/esound.h
@@ -205,7 +205,7 @@ typedef int esd_client_state_t;
/* the endian key is transferred in binary, if it's read into int, */
/* and matches ESD_ENDIAN_KEY (ENDN), then the endianness of the */
/* server and the client match; if it's SWAP_ENDIAN_KEY, swap data */
-#define ESD_SWAP_ENDIAN_KEY (UINT32_SWAP(ESD_ENDIAN_KEY))
+#define ESD_SWAP_ENDIAN_KEY (PA_UINT32_SWAP(ESD_ENDIAN_KEY))
#endif
diff --git a/src/pulsecore/fdsem.c b/src/pulsecore/fdsem.c
new file mode 100644
index 00000000..927bf00c
--- /dev/null
+++ b/src/pulsecore/fdsem.c
@@ -0,0 +1,276 @@
+/* $Id$ */
+
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2006 Lennart Poettering
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_SYS_SYSCALL_H
+#include <sys/syscall.h>
+#endif
+
+#include <unistd.h>
+#include <errno.h>
+
+#include <pulsecore/atomic.h>
+#include <pulsecore/log.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/core-util.h>
+#include <pulse/xmalloc.h>
+
+#ifndef HAVE_PIPE
+#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(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
+#endif
+
+#include "fdsem.h"
+
+struct pa_fdsem {
+ int fds[2];
+#ifdef HAVE_EVENTFD
+ int efd;
+#endif
+ pa_atomic_t waiting;
+ pa_atomic_t signalled;
+ pa_atomic_t in_pipe;
+};
+
+pa_fdsem *pa_fdsem_new(void) {
+ pa_fdsem *f;
+
+ f = pa_xnew(pa_fdsem, 1);
+
+#ifdef HAVE_EVENTFD
+ if ((f->efd = eventfd(0)) >= 0) {
+ pa_make_fd_cloexec(f->efd);
+ f->fds[0] = f->fds[1] = -1;
+
+ } else
+#endif
+ {
+ if (pipe(f->fds) < 0) {
+ pa_xfree(f);
+ return NULL;
+ }
+
+ pa_make_fd_cloexec(f->fds[0]);
+ 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);
+
+ return f;
+}
+
+void pa_fdsem_free(pa_fdsem *f) {
+ pa_assert(f);
+
+#ifdef HAVE_EVENTFD
+ if (f->efd >= 0)
+ pa_close(f->efd);
+#endif
+ pa_close_pipe(f->fds);
+
+ pa_xfree(f);
+}
+
+static void flush(pa_fdsem *f) {
+ ssize_t r;
+ pa_assert(f);
+
+ if (pa_atomic_load(&f->in_pipe) <= 0)
+ return;
+
+ do {
+ char x[10];
+
+#ifdef HAVE_EVENTFD
+ if (f->efd >= 0) {
+ uint64_t u;
+
+ if ((r = read(f->efd, &u, sizeof(u))) != sizeof(u)) {
+ pa_assert(r < 0 && errno == EINTR);
+ continue;
+ }
+ r = (ssize_t) u;
+ } else
+#endif
+
+ if ((r = read(f->fds[0], &x, sizeof(x))) <= 0) {
+ pa_assert(r < 0 && errno == EINTR);
+ continue;
+ }
+
+ } while (pa_atomic_sub(&f->in_pipe, r) > r);
+}
+
+void pa_fdsem_post(pa_fdsem *f) {
+ pa_assert(f);
+
+ if (pa_atomic_cmpxchg(&f->signalled, 0, 1)) {
+
+ if (pa_atomic_load(&f->waiting)) {
+ ssize_t r;
+ char x = 'x';
+
+ pa_atomic_inc(&f->in_pipe);
+
+ for (;;) {
+
+#ifdef HAVE_EVENTFD
+ if (f->efd >= 0) {
+ uint64_t u = 1;
+
+ if ((r = write(f->efd, &u, sizeof(u))) != sizeof(u)) {
+ pa_assert(r < 0 && errno == EINTR);
+ continue;
+ }
+ } else
+#endif
+
+ if ((r = write(f->fds[1], &x, 1)) != 1) {
+ pa_assert(r < 0 && errno == EINTR);
+ continue;
+ }
+
+ break;
+ }
+ }
+ }
+}
+
+void pa_fdsem_wait(pa_fdsem *f) {
+ pa_assert(f);
+
+ flush(f);
+
+ if (pa_atomic_cmpxchg(&f->signalled, 1, 0))
+ return;
+
+ pa_atomic_inc(&f->waiting);
+
+ while (!pa_atomic_cmpxchg(&f->signalled, 1, 0)) {
+ char x[10];
+ ssize_t r;
+
+#ifdef HAVE_EVENTFD
+ if (f->efd >= 0) {
+ uint64_t u;
+
+ if ((r = read(f->efd, &u, sizeof(u))) != sizeof(u)) {
+ pa_assert(r < 0 && errno == EINTR);
+ continue;
+ }
+
+ r = (ssize_t) u;
+ } else
+#endif
+
+ if ((r = read(f->fds[0], &x, sizeof(x))) <= 0) {
+ pa_assert(r < 0 && errno == EINTR);
+ continue;
+ }
+
+ pa_atomic_sub(&f->in_pipe, r);
+ }
+
+ pa_assert_se(pa_atomic_dec(&f->waiting) >= 1);
+}
+
+int pa_fdsem_try(pa_fdsem *f) {
+ pa_assert(f);
+
+ flush(f);
+
+ if (pa_atomic_cmpxchg(&f->signalled, 1, 0))
+ return 1;
+
+ return 0;
+}
+
+int pa_fdsem_get(pa_fdsem *f) {
+ pa_assert(f);
+
+#ifdef HAVE_EVENTFD
+ if (f->efd >= 0)
+ return f->efd;
+#endif
+
+ return f->fds[0];
+}
+
+int pa_fdsem_before_poll(pa_fdsem *f) {
+ pa_assert(f);
+
+ flush(f);
+
+ if (pa_atomic_cmpxchg(&f->signalled, 1, 0))
+ return -1;
+
+ pa_atomic_inc(&f->waiting);
+
+ if (pa_atomic_cmpxchg(&f->signalled, 1, 0)) {
+ pa_assert_se(pa_atomic_dec(&f->waiting) >= 1);
+ return -1;
+ }
+ return 0;
+}
+
+int pa_fdsem_after_poll(pa_fdsem *f) {
+ pa_assert(f);
+
+ pa_assert_se(pa_atomic_dec(&f->waiting) >= 1);
+
+ flush(f);
+
+ if (pa_atomic_cmpxchg(&f->signalled, 1, 0))
+ return 1;
+
+ return 0;
+}
diff --git a/src/pulsecore/fdsem.h b/src/pulsecore/fdsem.h
new file mode 100644
index 00000000..f38ef205
--- /dev/null
+++ b/src/pulsecore/fdsem.h
@@ -0,0 +1,49 @@
+#ifndef foopulsefdsemhfoo
+#define foopulsefdsemhfoo
+
+/* $Id$ */
+
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2004-2006 Lennart Poettering
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <sys/types.h>
+#include <pulse/def.h>
+
+/* A simple, asynchronous semaphore which uses fds for sleeping. In
+ * the best case all functions are lock-free unless sleeping is
+ * required. */
+
+typedef struct pa_fdsem pa_fdsem;
+
+pa_fdsem *pa_fdsem_new(void);
+void pa_fdsem_free(pa_fdsem *f);
+
+void pa_fdsem_post(pa_fdsem *f);
+void pa_fdsem_wait(pa_fdsem *f);
+int pa_fdsem_try(pa_fdsem *f);
+
+int pa_fdsem_get(pa_fdsem *f);
+
+int pa_fdsem_before_poll(pa_fdsem *f);
+int pa_fdsem_after_poll(pa_fdsem *f);
+
+
+#endif
diff --git a/src/pulsecore/ffmpeg/Makefile b/src/pulsecore/ffmpeg/Makefile
new file mode 100644
index 00000000..316beb72
--- /dev/null
+++ b/src/pulsecore/ffmpeg/Makefile
@@ -0,0 +1,13 @@
+# This is a dirty trick just to ease compilation with emacs
+#
+# This file is not intended to be distributed or anything
+#
+# So: don't touch it, even better ignore it!
+
+all:
+ $(MAKE) -C ../..
+
+clean:
+ $(MAKE) -C ../.. clean
+
+.PHONY: all clean
diff --git a/src/pulsecore/ffmpeg/avcodec.h b/src/pulsecore/ffmpeg/avcodec.h
new file mode 100644
index 00000000..696fc986
--- /dev/null
+++ b/src/pulsecore/ffmpeg/avcodec.h
@@ -0,0 +1,82 @@
+/*
+ * copyright (c) 2001 Fabrice Bellard
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg 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.
+ *
+ * FFmpeg 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 FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef AVCODEC_H
+#define AVCODEC_H
+
+/* Just a heavily bastardized version of the original file from
+ * ffmpeg, just enough to get resample2.c to compile without
+ * modification -- Lennart */
+
+#if !defined(PACKAGE) && defined(HAVE_CONFIG_H)
+#include <config.h>
+#endif
+
+#include <sys/types.h>
+#include <inttypes.h>
+#include <math.h>
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#define av_mallocz(l) calloc(1, (l))
+#define av_malloc(l) malloc(l)
+#define av_realloc(p,l) realloc((p),(l))
+#define av_free(p) free(p)
+
+static inline void av_freep(void *k) {
+ void **p = k;
+
+ if (p) {
+ free(*p);
+ *p = NULL;
+ }
+}
+
+static inline int av_clip(int a, int amin, int amax)
+{
+ if (a < amin) return amin;
+ else if (a > amax) return amax;
+ else return a;
+}
+
+#define av_log(a,b,c)
+
+#define FFABS(a) ((a) >= 0 ? (a) : (-(a)))
+#define FFSIGN(a) ((a) > 0 ? 1 : -1)
+
+#define FFMAX(a,b) ((a) > (b) ? (a) : (b))
+#define FFMIN(a,b) ((a) > (b) ? (b) : (a))
+
+struct AVResampleContext;
+struct AVResampleContext *av_resample_init(int out_rate, int in_rate, int filter_length, int log2_phase_count, int linear, double cutoff);
+int av_resample(struct AVResampleContext *c, short *dst, short *src, int *consumed, int src_size, int dst_size, int update_ctx);
+void av_resample_compensate(struct AVResampleContext *c, int sample_delta, int compensation_distance);
+void av_resample_close(struct AVResampleContext *c);
+void av_build_filter(int16_t *filter, double factor, int tap_count, int phase_count, int scale, int type);
+
+/*
+ * crude lrintf for non-C99 systems.
+ */
+#ifndef HAVE_LRINTF
+#define lrintf(x) ((long int)(x))
+#endif
+
+#endif /* AVCODEC_H */
diff --git a/src/pulsecore/ffmpeg/dsputil.h b/src/pulsecore/ffmpeg/dsputil.h
new file mode 100644
index 00000000..8da742d0
--- /dev/null
+++ b/src/pulsecore/ffmpeg/dsputil.h
@@ -0,0 +1 @@
+/* empty file, just here to allow us to compile an unmodified resampler2.c */
diff --git a/src/pulsecore/ffmpeg/resample2.c b/src/pulsecore/ffmpeg/resample2.c
new file mode 100644
index 00000000..da1443d9
--- /dev/null
+++ b/src/pulsecore/ffmpeg/resample2.c
@@ -0,0 +1,324 @@
+/*
+ * audio resampling
+ * Copyright (c) 2004 Michael Niedermayer <michaelni@gmx.at>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg 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.
+ *
+ * FFmpeg 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 FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * @file resample2.c
+ * audio resampling
+ * @author Michael Niedermayer <michaelni@gmx.at>
+ */
+
+#include "avcodec.h"
+#include "dsputil.h"
+
+#ifndef CONFIG_RESAMPLE_HP
+#define FILTER_SHIFT 15
+
+#define FELEM int16_t
+#define FELEM2 int32_t
+#define FELEML int64_t
+#define FELEM_MAX INT16_MAX
+#define FELEM_MIN INT16_MIN
+#define WINDOW_TYPE 9
+#elif !defined(CONFIG_RESAMPLE_AUDIOPHILE_KIDDY_MODE)
+#define FILTER_SHIFT 30
+
+#define FELEM int32_t
+#define FELEM2 int64_t
+#define FELEML int64_t
+#define FELEM_MAX INT32_MAX
+#define FELEM_MIN INT32_MIN
+#define WINDOW_TYPE 12
+#else
+#define FILTER_SHIFT 0
+
+#define FELEM double
+#define FELEM2 double
+#define FELEML double
+#define WINDOW_TYPE 24
+#endif
+
+
+typedef struct AVResampleContext{
+ FELEM *filter_bank;
+ int filter_length;
+ int ideal_dst_incr;
+ int dst_incr;
+ int index;
+ int frac;
+ int src_incr;
+ int compensation_distance;
+ int phase_shift;
+ int phase_mask;
+ int linear;
+}AVResampleContext;
+
+/**
+ * 0th order modified bessel function of the first kind.
+ */
+static double bessel(double x){
+ double v=1;
+ double t=1;
+ int i;
+
+ x= x*x/4;
+ for(i=1; i<50; i++){
+ t *= x/(i*i);
+ v += t;
+ }
+ return v;
+}
+
+/**
+ * builds a polyphase filterbank.
+ * @param factor resampling factor
+ * @param scale wanted sum of coefficients for each filter
+ * @param type 0->cubic, 1->blackman nuttall windowed sinc, 2..16->kaiser windowed sinc beta=2..16
+ */
+void av_build_filter(FELEM *filter, double factor, int tap_count, int phase_count, int scale, int type){
+ int ph, i;
+ double x, y, w, tab[tap_count];
+ const int center= (tap_count-1)/2;
+
+ /* if upsampling, only need to interpolate, no filter */
+ if (factor > 1.0)
+ factor = 1.0;
+
+ for(ph=0;ph<phase_count;ph++) {
+ double norm = 0;
+ for(i=0;i<tap_count;i++) {
+ x = M_PI * ((double)(i - center) - (double)ph / phase_count) * factor;
+ if (x == 0) y = 1.0;
+ else y = sin(x) / x;
+ switch(type){
+ case 0:{
+ const float d= -0.5; //first order derivative = -0.5
+ x = fabs(((double)(i - center) - (double)ph / phase_count) * factor);
+ if(x<1.0) y= 1 - 3*x*x + 2*x*x*x + d*( -x*x + x*x*x);
+ else y= d*(-4 + 8*x - 5*x*x + x*x*x);
+ break;}
+ case 1:
+ w = 2.0*x / (factor*tap_count) + M_PI;
+ y *= 0.3635819 - 0.4891775 * cos(w) + 0.1365995 * cos(2*w) - 0.0106411 * cos(3*w);
+ break;
+ default:
+ w = 2.0*x / (factor*tap_count*M_PI);
+ y *= bessel(type*sqrt(FFMAX(1-w*w, 0)));
+ break;
+ }
+
+ tab[i] = y;
+ norm += y;
+ }
+
+ /* normalize so that an uniform color remains the same */
+ for(i=0;i<tap_count;i++) {
+#ifdef CONFIG_RESAMPLE_AUDIOPHILE_KIDDY_MODE
+ filter[ph * tap_count + i] = tab[i] / norm;
+#else
+ filter[ph * tap_count + i] = av_clip(lrintf(tab[i] * scale / norm), FELEM_MIN, FELEM_MAX);
+#endif
+ }
+ }
+#if 0
+ {
+#define LEN 1024
+ int j,k;
+ double sine[LEN + tap_count];
+ double filtered[LEN];
+ double maxff=-2, minff=2, maxsf=-2, minsf=2;
+ for(i=0; i<LEN; i++){
+ double ss=0, sf=0, ff=0;
+ for(j=0; j<LEN+tap_count; j++)
+ sine[j]= cos(i*j*M_PI/LEN);
+ for(j=0; j<LEN; j++){
+ double sum=0;
+ ph=0;
+ for(k=0; k<tap_count; k++)
+ sum += filter[ph * tap_count + k] * sine[k+j];
+ filtered[j]= sum / (1<<FILTER_SHIFT);
+ ss+= sine[j + center] * sine[j + center];
+ ff+= filtered[j] * filtered[j];
+ sf+= sine[j + center] * filtered[j];
+ }
+ ss= sqrt(2*ss/LEN);
+ ff= sqrt(2*ff/LEN);
+ sf= 2*sf/LEN;
+ maxff= FFMAX(maxff, ff);
+ minff= FFMIN(minff, ff);
+ maxsf= FFMAX(maxsf, sf);
+ minsf= FFMIN(minsf, sf);
+ if(i%11==0){
+ av_log(NULL, AV_LOG_ERROR, "i:%4d ss:%f ff:%13.6e-%13.6e sf:%13.6e-%13.6e\n", i, ss, maxff, minff, maxsf, minsf);
+ minff=minsf= 2;
+ maxff=maxsf= -2;
+ }
+ }
+ }
+#endif
+}
+
+/**
+ * Initializes an audio resampler.
+ * Note, if either rate is not an integer then simply scale both rates up so they are.
+ */
+AVResampleContext *av_resample_init(int out_rate, int in_rate, int filter_size, int phase_shift, int linear, double cutoff){
+ AVResampleContext *c= av_mallocz(sizeof(AVResampleContext));
+ double factor= FFMIN(out_rate * cutoff / in_rate, 1.0);
+ int phase_count= 1<<phase_shift;
+
+ c->phase_shift= phase_shift;
+ c->phase_mask= phase_count-1;
+ c->linear= linear;
+
+ c->filter_length= FFMAX((int)ceil(filter_size/factor), 1);
+ c->filter_bank= av_mallocz(c->filter_length*(phase_count+1)*sizeof(FELEM));
+ av_build_filter(c->filter_bank, factor, c->filter_length, phase_count, 1<<FILTER_SHIFT, WINDOW_TYPE);
+ memcpy(&c->filter_bank[c->filter_length*phase_count+1], c->filter_bank, (c->filter_length-1)*sizeof(FELEM));
+ c->filter_bank[c->filter_length*phase_count]= c->filter_bank[c->filter_length - 1];
+
+ c->src_incr= out_rate;
+ c->ideal_dst_incr= c->dst_incr= in_rate * phase_count;
+ c->index= -phase_count*((c->filter_length-1)/2);
+
+ return c;
+}
+
+void av_resample_close(AVResampleContext *c){
+ av_freep(&c->filter_bank);
+ av_freep(&c);
+}
+
+/**
+ * Compensates samplerate/timestamp drift. The compensation is done by changing
+ * the resampler parameters, so no audible clicks or similar distortions ocur
+ * @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
+ *
+ * example: av_resample_compensate(c, 10, 500)
+ * here instead of 510 samples only 500 samples would be output
+ *
+ * note, due to rounding the actual compensation might be slightly different,
+ * especially if the compensation_distance is large and the in_rate used during init is small
+ */
+void av_resample_compensate(AVResampleContext *c, int sample_delta, int compensation_distance){
+// sample_delta += (c->ideal_dst_incr - c->dst_incr)*(int64_t)c->compensation_distance / c->ideal_dst_incr;
+ c->compensation_distance= compensation_distance;
+ c->dst_incr = c->ideal_dst_incr - c->ideal_dst_incr * (int64_t)sample_delta / compensation_distance;
+}
+
+/**
+ * resamples.
+ * @param src an array of unconsumed samples
+ * @param consumed the number of samples of src which have been consumed are returned here
+ * @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
+ */
+int av_resample(AVResampleContext *c, short *dst, short *src, int *consumed, int src_size, int dst_size, int update_ctx){
+ int dst_index, i;
+ int index= c->index;
+ int frac= c->frac;
+ int dst_incr_frac= c->dst_incr % c->src_incr;
+ int dst_incr= c->dst_incr / c->src_incr;
+ int compensation_distance= c->compensation_distance;
+
+ if(compensation_distance == 0 && c->filter_length == 1 && c->phase_shift==0){
+ int64_t index2= ((int64_t)index)<<32;
+ int64_t incr= (1LL<<32) * c->dst_incr / c->src_incr;
+ dst_size= FFMIN(dst_size, (src_size-1-index) * (int64_t)c->src_incr / c->dst_incr);
+
+ for(dst_index=0; dst_index < dst_size; dst_index++){
+ dst[dst_index] = src[index2>>32];
+ index2 += incr;
+ }
+ frac += dst_index * dst_incr_frac;
+ index += dst_index * dst_incr;
+ index += frac / c->src_incr;
+ frac %= c->src_incr;
+ }else{
+ for(dst_index=0; dst_index < dst_size; dst_index++){
+ FELEM *filter= c->filter_bank + c->filter_length*(index & c->phase_mask);
+ int sample_index= index >> c->phase_shift;
+ FELEM2 val=0;
+
+ if(sample_index < 0){
+ for(i=0; i<c->filter_length; i++)
+ val += src[FFABS(sample_index + i) % src_size] * filter[i];
+ }else if(sample_index + c->filter_length > src_size){
+ break;
+ }else if(c->linear){
+ FELEM2 v2=0;
+ for(i=0; i<c->filter_length; i++){
+ val += src[sample_index + i] * (FELEM2)filter[i];
+ v2 += src[sample_index + i] * (FELEM2)filter[i + c->filter_length];
+ }
+ val+=(v2-val)*(FELEML)frac / c->src_incr;
+ }else{
+ for(i=0; i<c->filter_length; i++){
+ val += src[sample_index + i] * (FELEM2)filter[i];
+ }
+ }
+
+#ifdef CONFIG_RESAMPLE_AUDIOPHILE_KIDDY_MODE
+ dst[dst_index] = av_clip_int16(lrintf(val));
+#else
+ val = (val + (1<<(FILTER_SHIFT-1)))>>FILTER_SHIFT;
+ dst[dst_index] = (unsigned)(val + 32768) > 65535 ? (val>>31) ^ 32767 : val;
+#endif
+
+ frac += dst_incr_frac;
+ index += dst_incr;
+ if(frac >= c->src_incr){
+ frac -= c->src_incr;
+ index++;
+ }
+
+ if(dst_index + 1 == compensation_distance){
+ compensation_distance= 0;
+ dst_incr_frac= c->ideal_dst_incr % c->src_incr;
+ dst_incr= c->ideal_dst_incr / c->src_incr;
+ }
+ }
+ }
+ *consumed= FFMAX(index, 0) >> c->phase_shift;
+ if(index>=0) index &= c->phase_mask;
+
+ if(compensation_distance){
+ compensation_distance -= dst_index;
+ assert(compensation_distance > 0);
+ }
+ if(update_ctx){
+ c->frac= frac;
+ c->index= index;
+ c->dst_incr= dst_incr_frac + c->src_incr*dst_incr;
+ c->compensation_distance= compensation_distance;
+ }
+#if 0
+ if(update_ctx && !c->compensation_distance){
+#undef rand
+ av_resample_compensate(c, rand() % (8000*2) - 8000, 8000*2);
+av_log(NULL, AV_LOG_DEBUG, "%d %d %d\n", c->dst_incr, c->ideal_dst_incr, c->compensation_distance);
+ }
+#endif
+
+ return dst_index;
+}
diff --git a/src/pulsecore/flist.c b/src/pulsecore/flist.c
index 00567ab3..d9740777 100644
--- a/src/pulsecore/flist.c
+++ b/src/pulsecore/flist.c
@@ -25,12 +25,14 @@
#include <config.h>
#endif
-#include <assert.h>
+#include <pulse/xmalloc.h>
#include <pulsecore/atomic.h>
#include <pulsecore/log.h>
#include <pulsecore/thread.h>
-#include <pulse/xmalloc.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/macro.h>
#include "flist.h"
@@ -90,21 +92,18 @@ enum {
};
struct cell {
- pa_atomic_int_t state;
+ pa_atomic_t state;
void *data;
};
struct pa_flist {
- struct cell *cells;
unsigned size;
- pa_atomic_int_t length;
- pa_atomic_int_t read_idx;
- pa_atomic_int_t write_idx;
+ pa_atomic_t length;
+ pa_atomic_t read_idx;
+ pa_atomic_t write_idx;
};
-static int is_power_of_two(unsigned size) {
- return !(size & (size - 1));
-}
+#define PA_FLIST_CELLS(x) ((struct cell*) ((uint8_t*) (x) + PA_ALIGN(sizeof(struct pa_flist))))
pa_flist *pa_flist_new(unsigned size) {
pa_flist *l;
@@ -112,12 +111,11 @@ pa_flist *pa_flist_new(unsigned size) {
if (!size)
size = FLIST_SIZE;
- assert(is_power_of_two(size));
+ pa_assert(pa_is_power_of_two(size));
- l = pa_xnew(pa_flist, 1);
+ l = pa_xmalloc0(PA_ALIGN(sizeof(pa_flist)) + (sizeof(struct cell) * size));
l->size = size;
- l->cells = pa_xnew0(struct cell, size);
pa_atomic_store(&l->read_idx, 0);
pa_atomic_store(&l->write_idx, 0);
@@ -131,32 +129,37 @@ static int reduce(pa_flist *l, int value) {
}
void pa_flist_free(pa_flist *l, pa_free_cb_t free_cb) {
- assert(l);
+ pa_assert(l);
if (free_cb) {
+ struct cell *cells;
int len, 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(&l->cells[idx].state) == STATE_USED)
- free_cb(l->cells[idx].data);
+ if (pa_atomic_load(&cells[idx].state) == STATE_USED)
+ free_cb(cells[idx].data);
idx = reduce(l, idx + 1);
}
}
- pa_xfree(l->cells);
pa_xfree(l);
}
int pa_flist_push(pa_flist*l, void *p) {
int idx, len, n;
+ struct cell *cells;
+
+ pa_assert(l);
+ pa_assert(p);
- assert(l);
- assert(p);
+ cells = PA_FLIST_CELLS(l);
n = len = (int) l->size - pa_atomic_load(&l->length) + N_EXTRA_SCAN;
_Y;
@@ -165,13 +168,13 @@ int pa_flist_push(pa_flist*l, void *p) {
for (; n > 0 ; n--) {
_Y;
- if (pa_atomic_cmpxchg(&l->cells[idx].state, STATE_UNUSED, STATE_BUSY)) {
+ if (pa_atomic_cmpxchg(&cells[idx].state, STATE_UNUSED, STATE_BUSY)) {
_Y;
pa_atomic_inc(&l->write_idx);
_Y;
- l->cells[idx].data = p;
+ cells[idx].data = p;
_Y;
- pa_atomic_store(&l->cells[idx].state, STATE_USED);
+ pa_atomic_store(&cells[idx].state, STATE_USED);
_Y;
pa_atomic_inc(&l->length);
return 0;
@@ -183,7 +186,7 @@ int pa_flist_push(pa_flist*l, void *p) {
#ifdef PROFILE
if (len > N_EXTRA_SCAN)
- pa_log("WARNING: Didn't find free cell after %u iterations.", len);
+ pa_log_warn("Didn't find free cell after %u iterations.", len);
#endif
return -1;
@@ -191,8 +194,11 @@ int pa_flist_push(pa_flist*l, void *p) {
void* pa_flist_pop(pa_flist*l) {
int idx, len, n;
+ struct cell *cells;
+
+ pa_assert(l);
- assert(l);
+ cells = PA_FLIST_CELLS(l);
n = len = pa_atomic_load(&l->length) + N_EXTRA_SCAN;
_Y;
@@ -201,14 +207,14 @@ void* pa_flist_pop(pa_flist*l) {
for (; n > 0 ; n--) {
_Y;
- if (pa_atomic_cmpxchg(&l->cells[idx].state, STATE_USED, STATE_BUSY)) {
+ if (pa_atomic_cmpxchg(&cells[idx].state, STATE_USED, STATE_BUSY)) {
void *p;
_Y;
pa_atomic_inc(&l->read_idx);
_Y;
- p = l->cells[idx].data;
+ p = cells[idx].data;
_Y;
- pa_atomic_store(&l->cells[idx].state, STATE_UNUSED);
+ pa_atomic_store(&cells[idx].state, STATE_UNUSED);
_Y;
pa_atomic_dec(&l->length);
@@ -221,7 +227,7 @@ void* pa_flist_pop(pa_flist*l) {
#ifdef PROFILE
if (len > N_EXTRA_SCAN)
- pa_log("WARNING: Didn't find used cell after %u iterations.", len);
+ pa_log_warn("Didn't find used cell after %u iterations.", len);
#endif
return NULL;
diff --git a/src/pulsecore/flist.h b/src/pulsecore/flist.h
index bf702bf3..daf0fec4 100644
--- a/src/pulsecore/flist.h
+++ b/src/pulsecore/flist.h
@@ -26,6 +26,9 @@
#include <pulse/def.h>
+#include <pulsecore/once.h>
+#include <pulsecore/gccmacro.h>
+
/* A multiple-reader multipler-write lock-free free list implementation */
typedef struct pa_flist pa_flist;
@@ -38,4 +41,28 @@ void pa_flist_free(pa_flist *l, pa_free_cb_t free_cb);
int pa_flist_push(pa_flist*l, void *p);
void* pa_flist_pop(pa_flist*l);
+/* Please not that the destructor stuff is not really necesary, we do
+ * this just to make valgrind output more useful. */
+
+#define PA_STATIC_FLIST_DECLARE(name, size, free_cb) \
+ static struct { \
+ pa_flist *flist; \
+ pa_once once; \
+ } name##_flist = { NULL, PA_ONCE_INIT }; \
+ static void name##_flist_init(void) { \
+ name##_flist.flist = pa_flist_new(size); \
+ } \
+ static inline pa_flist* name##_flist_get(void) { \
+ pa_run_once(&name##_flist.once, name##_flist_init); \
+ return name##_flist.flist; \
+ } \
+ static void name##_flist_destructor(void) PA_GCC_DESTRUCTOR; \
+ static void name##_flist_destructor(void) { \
+ if (name##_flist.flist) \
+ pa_flist_free(name##_flist.flist, (free_cb)); \
+ } \
+ struct __stupid_useless_struct_to_allow_trailing_semicolon
+
+#define PA_STATIC_FLIST_GET(name) (name##_flist_get())
+
#endif
diff --git a/src/pulsecore/g711.c b/src/pulsecore/g711.c
index 8c2bbf00..aa2d703a 100644
--- a/src/pulsecore/g711.c
+++ b/src/pulsecore/g711.c
@@ -43,30 +43,30 @@
#include "g711.h"
-#define SIGN_BIT (0x80) /* Sign bit for a A-law byte. */
-#define QUANT_MASK (0xf) /* Quantization field mask. */
-#define NSEGS (8) /* Number of A-law segments. */
-#define SEG_SHIFT (4) /* Left shift for segment number. */
-#define SEG_MASK (0x70) /* Segment field mask. */
+#define SIGN_BIT (0x80) /* Sign bit for a A-law byte. */
+#define QUANT_MASK (0xf) /* Quantization field mask. */
+#define NSEGS (8) /* Number of A-law segments. */
+#define SEG_SHIFT (4) /* Left shift for segment number. */
+#define SEG_MASK (0x70) /* Segment field mask. */
#if !defined(FAST_ALAW_CONVERSION) || !defined(FAST_ULAW_CONVERSION)
static int16_t seg_aend[8] = {0x1F, 0x3F, 0x7F, 0xFF,
- 0x1FF, 0x3FF, 0x7FF, 0xFFF};
+ 0x1FF, 0x3FF, 0x7FF, 0xFFF};
static int16_t seg_uend[8] = {0x3F, 0x7F, 0xFF, 0x1FF,
- 0x3FF, 0x7FF, 0xFFF, 0x1FFF};
+ 0x3FF, 0x7FF, 0xFFF, 0x1FFF};
static int16_t search(
- int16_t val,
- int16_t *table,
- int size)
+ int16_t val,
+ int16_t *table,
+ int size)
{
- int i;
+ int i;
- for (i = 0; i < size; i++) {
- if (val <= *table++)
- return (i);
- }
- return (size);
+ for (i = 0; i < size; i++) {
+ if (val <= *table++)
+ return (i);
+ }
+ return (size);
}
#endif /* !FAST_*_CONVERSION */
@@ -77,55 +77,55 @@ static int16_t search(
* the data shifted such that it only contains information in the lower
* 13-bits.
*
- * Linear Input Code Compressed Code
- * ------------------------ ---------------
- * 0000000wxyza 000wxyz
- * 0000001wxyza 001wxyz
- * 000001wxyzab 010wxyz
- * 00001wxyzabc 011wxyz
- * 0001wxyzabcd 100wxyz
- * 001wxyzabcde 101wxyz
- * 01wxyzabcdef 110wxyz
- * 1wxyzabcdefg 111wxyz
+ * Linear Input Code Compressed Code
+ * ------------------------ ---------------
+ * 0000000wxyza 000wxyz
+ * 0000001wxyza 001wxyz
+ * 000001wxyzab 010wxyz
+ * 00001wxyzabc 011wxyz
+ * 0001wxyzabcd 100wxyz
+ * 001wxyzabcde 101wxyz
+ * 01wxyzabcdef 110wxyz
+ * 1wxyzabcdefg 111wxyz
*
* For further information see John C. Bellamy's Digital Telephony, 1982,
* John Wiley & Sons, pps 98-111 and 472-476.
*/
unsigned char st_13linear2alaw(
- int16_t pcm_val) /* 2's complement (13-bit range) */
+ int16_t pcm_val) /* 2's complement (13-bit range) */
{
- int16_t mask;
- short seg;
- unsigned char aval;
+ int16_t mask;
+ short seg;
+ unsigned char aval;
- /* Have calling software do it since its already doing a shift
- * from 32-bits down to 16-bits.
- */
- /* pcm_val = pcm_val >> 3; */
+ /* Have calling software do it since its already doing a shift
+ * from 32-bits down to 16-bits.
+ */
+ /* pcm_val = pcm_val >> 3; */
- /* A-law using even bit inversion */
- if (pcm_val >= 0) {
- mask = 0xD5; /* sign (7th) bit = 1 */
- } else {
- mask = 0x55; /* sign bit = 0 */
- pcm_val = -pcm_val - 1;
- }
+ /* A-law using even bit inversion */
+ if (pcm_val >= 0) {
+ mask = 0xD5; /* sign (7th) bit = 1 */
+ } else {
+ mask = 0x55; /* sign bit = 0 */
+ pcm_val = -pcm_val - 1;
+ }
- /* Convert the scaled magnitude to segment number. */
- seg = search(pcm_val, seg_aend, 8);
+ /* Convert the scaled magnitude to segment number. */
+ seg = search(pcm_val, seg_aend, 8);
- /* Combine the sign, segment, and quantization bits. */
+ /* Combine the sign, segment, and quantization bits. */
- if (seg >= 8) /* out of range, return maximum value. */
- return (unsigned char) (0x7F ^ mask);
- else {
- aval = (unsigned char) seg << SEG_SHIFT;
- if (seg < 2)
- aval |= (pcm_val >> 1) & QUANT_MASK;
- else
- aval |= (pcm_val >> seg) & QUANT_MASK;
- return (aval ^ mask);
- }
+ if (seg >= 8) /* out of range, return maximum value. */
+ return (unsigned char) (0x7F ^ mask);
+ else {
+ aval = (unsigned char) seg << SEG_SHIFT;
+ if (seg < 2)
+ aval |= (pcm_val >> 1) & QUANT_MASK;
+ else
+ aval |= (pcm_val >> seg) & QUANT_MASK;
+ return (aval ^ mask);
+ }
}
/*
@@ -133,31 +133,31 @@ unsigned char st_13linear2alaw(
*
*/
int16_t st_alaw2linear16(
- unsigned char a_val)
+ unsigned char a_val)
{
- int16_t t;
- int16_t seg;
+ int16_t t;
+ int16_t seg;
- a_val ^= 0x55;
+ a_val ^= 0x55;
- t = (a_val & QUANT_MASK) << 4;
- seg = ((unsigned)a_val & SEG_MASK) >> SEG_SHIFT;
- switch (seg) {
- case 0:
- t += 8;
- break;
- case 1:
- t += 0x108;
- break;
- default:
- t += 0x108;
- t <<= seg - 1;
- }
- return ((a_val & SIGN_BIT) ? t : -t);
+ t = (a_val & QUANT_MASK) << 4;
+ seg = ((unsigned)a_val & SEG_MASK) >> SEG_SHIFT;
+ switch (seg) {
+ case 0:
+ t += 8;
+ break;
+ case 1:
+ t += 0x108;
+ break;
+ default:
+ t += 0x108;
+ t <<= seg - 1;
+ }
+ return ((a_val & SIGN_BIT) ? t : -t);
}
#endif /* !FAST_ALAW_CONVERSION */
-#define BIAS (0x84) /* Bias for linear code. */
+#define BIAS (0x84) /* Bias for linear code. */
#define CLIP 8159
#ifndef FAST_ULAW_CONVERSION
@@ -171,16 +171,16 @@ int16_t st_alaw2linear16(
* is biased by adding 33 which shifts the encoding range from (0 - 8158) to
* (33 - 8191). The result can be seen in the following encoding table:
*
- * Biased Linear Input Code Compressed Code
- * ------------------------ ---------------
- * 00000001wxyza 000wxyz
- * 0000001wxyzab 001wxyz
- * 000001wxyzabc 010wxyz
- * 00001wxyzabcd 011wxyz
- * 0001wxyzabcde 100wxyz
- * 001wxyzabcdef 101wxyz
- * 01wxyzabcdefg 110wxyz
- * 1wxyzabcdefgh 111wxyz
+ * Biased Linear Input Code Compressed Code
+ * ------------------------ ---------------
+ * 00000001wxyza 000wxyz
+ * 0000001wxyzab 001wxyz
+ * 000001wxyzabc 010wxyz
+ * 00001wxyzabcd 011wxyz
+ * 0001wxyzabcde 100wxyz
+ * 001wxyzabcdef 101wxyz
+ * 01wxyzabcdefg 110wxyz
+ * 1wxyzabcdefgh 111wxyz
*
* Each biased linear code has a leading 1 which identifies the segment
* number. The value of the segment number is equal to 7 minus the number
@@ -194,41 +194,41 @@ int16_t st_alaw2linear16(
* John Wiley & Sons, pps 98-111 and 472-476.
*/
unsigned char st_14linear2ulaw(
- int16_t pcm_val) /* 2's complement (14-bit range) */
+ int16_t pcm_val) /* 2's complement (14-bit range) */
{
- int16_t mask;
- int16_t seg;
- unsigned char uval;
+ int16_t mask;
+ int16_t seg;
+ unsigned char uval;
- /* Have calling software do it since its already doing a shift
- * from 32-bits down to 16-bits.
- */
- /* pcm_val = pcm_val >> 2; */
+ /* Have calling software do it since its already doing a shift
+ * from 32-bits down to 16-bits.
+ */
+ /* pcm_val = pcm_val >> 2; */
- /* u-law inverts all bits */
- /* Get the sign and the magnitude of the value. */
- if (pcm_val < 0) {
- pcm_val = -pcm_val;
- mask = 0x7F;
- } else {
- mask = 0xFF;
- }
- if ( pcm_val > CLIP ) pcm_val = CLIP; /* clip the magnitude */
- pcm_val += (BIAS >> 2);
+ /* u-law inverts all bits */
+ /* Get the sign and the magnitude of the value. */
+ if (pcm_val < 0) {
+ pcm_val = -pcm_val;
+ mask = 0x7F;
+ } else {
+ mask = 0xFF;
+ }
+ if ( pcm_val > CLIP ) pcm_val = CLIP; /* clip the magnitude */
+ pcm_val += (BIAS >> 2);
- /* Convert the scaled magnitude to segment number. */
- seg = search(pcm_val, seg_uend, 8);
+ /* Convert the scaled magnitude to segment number. */
+ seg = search(pcm_val, seg_uend, 8);
- /*
- * Combine the sign, segment, quantization bits;
- * and complement the code word.
- */
- if (seg >= 8) /* out of range, return maximum value. */
- return (unsigned char) (0x7F ^ mask);
- else {
- uval = (unsigned char) (seg << 4) | ((pcm_val >> (seg + 1)) & 0xF);
- return (uval ^ mask);
- }
+ /*
+ * Combine the sign, segment, quantization bits;
+ * and complement the code word.
+ */
+ if (seg >= 8) /* out of range, return maximum value. */
+ return (unsigned char) (0x7F ^ mask);
+ else {
+ uval = (unsigned char) (seg << 4) | ((pcm_val >> (seg + 1)) & 0xF);
+ return (uval ^ mask);
+ }
}
@@ -242,21 +242,21 @@ unsigned char st_14linear2ulaw(
* original code word. This is in keeping with ISDN conventions.
*/
int16_t st_ulaw2linear16(
- unsigned char u_val)
+ unsigned char u_val)
{
- int16_t t;
+ int16_t t;
- /* Complement to obtain normal u-law value. */
- u_val = ~u_val;
+ /* Complement to obtain normal u-law value. */
+ u_val = ~u_val;
- /*
- * Extract and bias the quantization bits. Then
- * shift up by the segment number and subtract out the bias.
- */
- t = ((u_val & QUANT_MASK) << 3) + BIAS;
- t <<= ((unsigned)u_val & SEG_MASK) >> SEG_SHIFT;
+ /*
+ * Extract and bias the quantization bits. Then
+ * shift up by the segment number and subtract out the bias.
+ */
+ t = ((u_val & QUANT_MASK) << 3) + BIAS;
+ t <<= ((unsigned)u_val & SEG_MASK) >> SEG_SHIFT;
- return ((u_val & SIGN_BIT) ? (BIAS - t) : (t - BIAS));
+ return ((u_val & SIGN_BIT) ? (BIAS - t) : (t - BIAS));
}
#endif /* !FAST_ULAW_CONVERSION */
@@ -2413,52 +2413,52 @@ int main()
printf("int16_t _st_alaw2linear16[256] = {\n ");
for (x = 0; x < 256; x++)
{
- printf("%8d,", st_alaw2linear16(x));
- y++;
- if (y == 7)
- {
- y = 0;
- printf("\n ");
- }
+ printf("%8d,", st_alaw2linear16(x));
+ y++;
+ if (y == 7)
+ {
+ y = 0;
+ printf("\n ");
+ }
}
printf("\n};\n\nuint8_t _st_13linear2alaw[0x2000] = {\n ");
y = 0;
for (x = 0; x < 0x2000; x++)
{
- printf(" 0x%02x,", st_13linear2alaw((-0x1000)+x));
- y++;
- if (y == 12)
- {
- y = 0;
- printf("\n ");
- }
+ printf(" 0x%02x,", st_13linear2alaw((-0x1000)+x));
+ y++;
+ if (y == 12)
+ {
+ y = 0;
+ printf("\n ");
+ }
}
printf("\n};\n\nint16_t _st_ulaw2linear16[256] = {\n ");
y = 0;
for (x = 0; x < 256; x++)
{
- printf("%8d,", st_ulaw2linear16(x));
- y++;
- if (y == 7)
- {
- y = 0;
- printf("\n ");
- }
+ printf("%8d,", st_ulaw2linear16(x));
+ y++;
+ if (y == 7)
+ {
+ y = 0;
+ printf("\n ");
+ }
}
printf("\n};\n\nuint8_t _st_14linear2ulaw[0x4000] = {\n ");
y = 0;
for (x = 0; x < 0x4000; x++)
{
- printf(" 0x%02x,", st_14linear2ulaw((-0x2000)+x));
- y++;
- if (y == 12)
- {
- y = 0;
- printf("\n ");
- }
+ printf(" 0x%02x,", st_14linear2ulaw((-0x2000)+x));
+ y++;
+ if (y == 12)
+ {
+ y = 0;
+ printf("\n ");
+ }
}
printf("\n};\n");
@@ -2468,64 +2468,64 @@ int main()
/* The following is not used by SoX but kept for reference */
#if 0
/* copy from CCITT G.711 specifications */
-unsigned char _u2a[128] = { /* u- to A-law conversions */
- 1, 1, 2, 2, 3, 3, 4, 4,
- 5, 5, 6, 6, 7, 7, 8, 8,
- 9, 10, 11, 12, 13, 14, 15, 16,
- 17, 18, 19, 20, 21, 22, 23, 24,
- 25, 27, 29, 31, 33, 34, 35, 36,
- 37, 38, 39, 40, 41, 42, 43, 44,
- 46, 48, 49, 50, 51, 52, 53, 54,
- 55, 56, 57, 58, 59, 60, 61, 62,
- 64, 65, 66, 67, 68, 69, 70, 71,
- 72, 73, 74, 75, 76, 77, 78, 79,
+unsigned char _u2a[128] = { /* u- to A-law conversions */
+ 1, 1, 2, 2, 3, 3, 4, 4,
+ 5, 5, 6, 6, 7, 7, 8, 8,
+ 9, 10, 11, 12, 13, 14, 15, 16,
+ 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, 27, 29, 31, 33, 34, 35, 36,
+ 37, 38, 39, 40, 41, 42, 43, 44,
+ 46, 48, 49, 50, 51, 52, 53, 54,
+ 55, 56, 57, 58, 59, 60, 61, 62,
+ 64, 65, 66, 67, 68, 69, 70, 71,
+ 72, 73, 74, 75, 76, 77, 78, 79,
/* corrected:
- 81, 82, 83, 84, 85, 86, 87, 88,
+ 81, 82, 83, 84, 85, 86, 87, 88,
should be: */
- 80, 82, 83, 84, 85, 86, 87, 88,
- 89, 90, 91, 92, 93, 94, 95, 96,
- 97, 98, 99, 100, 101, 102, 103, 104,
- 105, 106, 107, 108, 109, 110, 111, 112,
- 113, 114, 115, 116, 117, 118, 119, 120,
- 121, 122, 123, 124, 125, 126, 127, 128};
+ 80, 82, 83, 84, 85, 86, 87, 88,
+ 89, 90, 91, 92, 93, 94, 95, 96,
+ 97, 98, 99, 100, 101, 102, 103, 104,
+ 105, 106, 107, 108, 109, 110, 111, 112,
+ 113, 114, 115, 116, 117, 118, 119, 120,
+ 121, 122, 123, 124, 125, 126, 127, 128};
-unsigned char _a2u[128] = { /* A- to u-law conversions */
- 1, 3, 5, 7, 9, 11, 13, 15,
- 16, 17, 18, 19, 20, 21, 22, 23,
- 24, 25, 26, 27, 28, 29, 30, 31,
- 32, 32, 33, 33, 34, 34, 35, 35,
- 36, 37, 38, 39, 40, 41, 42, 43,
- 44, 45, 46, 47, 48, 48, 49, 49,
- 50, 51, 52, 53, 54, 55, 56, 57,
- 58, 59, 60, 61, 62, 63, 64, 64,
- 65, 66, 67, 68, 69, 70, 71, 72,
+unsigned char _a2u[128] = { /* A- to u-law conversions */
+ 1, 3, 5, 7, 9, 11, 13, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23,
+ 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 32, 33, 33, 34, 34, 35, 35,
+ 36, 37, 38, 39, 40, 41, 42, 43,
+ 44, 45, 46, 47, 48, 48, 49, 49,
+ 50, 51, 52, 53, 54, 55, 56, 57,
+ 58, 59, 60, 61, 62, 63, 64, 64,
+ 65, 66, 67, 68, 69, 70, 71, 72,
/* corrected:
- 73, 74, 75, 76, 77, 78, 79, 79,
+ 73, 74, 75, 76, 77, 78, 79, 79,
should be: */
- 73, 74, 75, 76, 77, 78, 79, 80,
+ 73, 74, 75, 76, 77, 78, 79, 80,
- 80, 81, 82, 83, 84, 85, 86, 87,
- 88, 89, 90, 91, 92, 93, 94, 95,
- 96, 97, 98, 99, 100, 101, 102, 103,
- 104, 105, 106, 107, 108, 109, 110, 111,
- 112, 113, 114, 115, 116, 117, 118, 119,
- 120, 121, 122, 123, 124, 125, 126, 127};
+ 80, 81, 82, 83, 84, 85, 86, 87,
+ 88, 89, 90, 91, 92, 93, 94, 95,
+ 96, 97, 98, 99, 100, 101, 102, 103,
+ 104, 105, 106, 107, 108, 109, 110, 111,
+ 112, 113, 114, 115, 116, 117, 118, 119,
+ 120, 121, 122, 123, 124, 125, 126, 127};
/* A-law to u-law conversion */
unsigned char st_alaw2ulaw(
- unsigned char aval)
+ unsigned char aval)
{
- aval &= 0xff;
- return (unsigned char) ((aval & 0x80) ? (0xFF ^ _a2u[aval ^ 0xD5]) :
- (0x7F ^ _a2u[aval ^ 0x55]));
+ aval &= 0xff;
+ return (unsigned char) ((aval & 0x80) ? (0xFF ^ _a2u[aval ^ 0xD5]) :
+ (0x7F ^ _a2u[aval ^ 0x55]));
}
/* u-law to A-law conversion */
unsigned char st_ulaw2alaw(
- unsigned char uval)
+ unsigned char uval)
{
- uval &= 0xff;
- return (unsigned char) ((uval & 0x80) ? (0xD5 ^ (_u2a[0xFF ^ uval] - 1)) :
- (unsigned char) (0x55 ^ (_u2a[0x7F ^ uval] - 1)));
+ uval &= 0xff;
+ return (unsigned char) ((uval & 0x80) ? (0xD5 ^ (_u2a[0xFF ^ uval] - 1)) :
+ (unsigned char) (0x55 ^ (_u2a[0x7F ^ uval] - 1)));
}
#endif
diff --git a/src/pulsecore/gccmacro.h b/src/pulsecore/gccmacro.h
index 57d28006..e9f0d093 100644
--- a/src/pulsecore/gccmacro.h
+++ b/src/pulsecore/gccmacro.h
@@ -52,4 +52,29 @@
#define PA_GCC_UNUSED
#endif
+#ifdef __GNUC__
+#define PA_GCC_DESTRUCTOR __attribute__ ((destructor))
+#else
+/** Call this function when process terminates */
+#define PA_GCC_DESTRUCTOR
+#endif
+
+#ifndef PA_GCC_PURE
+#ifdef __GNUCC__
+#define PA_GCC_PURE __attribute__ ((pure))
+#else
+/** This function's return value depends only the arguments list and global state **/
+#define PA_GCC_PURE
+#endif
+#endif
+
+#ifndef PA_GCC_CONST
+#ifdef __GNUCC__
+#define PA_GCC_CONST __attribute__ ((const))
+#else
+/** This function's return value depends only the arguments list (stricter version of PA_GCC_PURE) **/
+#define PA_GCC_CONST
+#endif
+#endif
+
#endif
diff --git a/src/pulsecore/hashmap.c b/src/pulsecore/hashmap.c
index 818e12bf..f5589664 100644
--- a/src/pulsecore/hashmap.c
+++ b/src/pulsecore/hashmap.c
@@ -26,13 +26,14 @@
#endif
#include <stdlib.h>
-#include <assert.h>
#include <string.h>
#include <pulse/xmalloc.h>
#include <pulsecore/idxset.h>
#include <pulsecore/log.h>
+#include <pulsecore/flist.h>
+#include <pulsecore/macro.h>
#include "hashmap.h"
@@ -55,6 +56,8 @@ struct pa_hashmap {
pa_compare_func_t compare_func;
};
+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;
@@ -69,8 +72,8 @@ pa_hashmap *pa_hashmap_new(pa_hash_func_t hash_func, pa_compare_func_t compare_f
}
static void remove(pa_hashmap *h, struct hashmap_entry *e) {
- assert(h);
- assert(e);
+ pa_assert(h);
+ pa_assert(e);
if (e->next)
e->next->previous = e->previous;
@@ -84,16 +87,18 @@ static void remove(pa_hashmap *h, struct hashmap_entry *e) {
if (e->bucket_previous)
e->bucket_previous->bucket_next = e->bucket_next;
else {
- assert(e->hash < h->size);
+ pa_assert(e->hash < h->size);
h->data[e->hash] = e->bucket_next;
}
- pa_xfree(e);
+ if (pa_flist_push(PA_STATIC_FLIST_GET(entries), e) < 0)
+ pa_xfree(e);
+
h->n_entries--;
}
void pa_hashmap_free(pa_hashmap*h, void (*free_func)(void *p, void *userdata), void *userdata) {
- assert(h);
+ pa_assert(h);
while (h->first_entry) {
if (free_func)
@@ -107,8 +112,8 @@ void pa_hashmap_free(pa_hashmap*h, void (*free_func)(void *p, void *userdata), v
static struct hashmap_entry *get(pa_hashmap *h, unsigned hash, const void *key) {
struct hashmap_entry *e;
- assert(h);
- assert(hash < h->size);
+ pa_assert(h);
+ pa_assert(hash < h->size);
for (e = h->data[hash]; e; e = e->bucket_next)
if (h->compare_func(e->key, key) == 0)
@@ -120,14 +125,16 @@ 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;
- assert(h);
+ pa_assert(h);
hash = h->hash_func(key) % h->size;
if ((e = get(h, hash, key)))
return -1;
- e = pa_xnew(struct hashmap_entry, 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;
@@ -152,7 +159,7 @@ void* pa_hashmap_get(pa_hashmap *h, const void *key) {
unsigned hash;
struct hashmap_entry *e;
- assert(h);
+ pa_assert(h);
hash = h->hash_func(key) % h->size;
@@ -167,7 +174,7 @@ void* pa_hashmap_remove(pa_hashmap *h, const void *key) {
unsigned hash;
void *data;
- assert(h);
+ pa_assert(h);
hash = h->hash_func(key) % h->size;
@@ -184,8 +191,8 @@ unsigned pa_hashmap_size(pa_hashmap *h) {
}
void *pa_hashmap_iterate(pa_hashmap *h, void **state, const void **key) {
- assert(h);
- assert(state);
+ pa_assert(h);
+ pa_assert(state);
if (!*state)
*state = h->first_entry;
@@ -207,7 +214,7 @@ void *pa_hashmap_iterate(pa_hashmap *h, void **state, const void **key) {
void* pa_hashmap_steal_first(pa_hashmap *h) {
void *data;
- assert(h);
+ pa_assert(h);
if (!h->first_entry)
return NULL;
@@ -218,7 +225,7 @@ void* pa_hashmap_steal_first(pa_hashmap *h) {
}
void *pa_hashmap_get_first(pa_hashmap *h) {
- assert(h);
+ pa_assert(h);
if (!h->first_entry)
return NULL;
diff --git a/src/pulsecore/hashmap.h b/src/pulsecore/hashmap.h
index 3ca2a479..98df4502 100644
--- a/src/pulsecore/hashmap.h
+++ b/src/pulsecore/hashmap.h
@@ -32,11 +32,13 @@
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*, void (*free_func)(void *p, void *userdata), void *userdata);
+void pa_hashmap_free(pa_hashmap*, pa_free2_cb_t free_cb, void *userdata);
/* Returns non-zero when the entry already exists */
int pa_hashmap_put(pa_hashmap *h, const void *key, void *value);
diff --git a/src/pulsecore/hook-list.c b/src/pulsecore/hook-list.c
index 4f884187..3a6874c4 100644
--- a/src/pulsecore/hook-list.c
+++ b/src/pulsecore/hook-list.c
@@ -21,10 +21,16 @@
USA.
***/
-#include <pulsecore/hook-list.h>
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulsecore/macro.h>
+
+#include "hook-list.h"
void pa_hook_init(pa_hook *hook, void *data) {
- assert(hook);
+ pa_assert(hook);
PA_LLIST_HEAD_INIT(pa_hook_slot, hook->slots);
hook->last = NULL;
@@ -33,8 +39,8 @@ void pa_hook_init(pa_hook *hook, void *data) {
}
static void slot_free(pa_hook *hook, pa_hook_slot *slot) {
- assert(hook);
- assert(slot);
+ pa_assert(hook);
+ pa_assert(slot);
if (hook->last == slot)
hook->last = slot->prev;
@@ -45,8 +51,8 @@ static void slot_free(pa_hook *hook, pa_hook_slot *slot) {
}
void pa_hook_free(pa_hook *hook) {
- assert(hook);
- assert(!hook->firing);
+ pa_assert(hook);
+ pa_assert(!hook->firing);
while (hook->slots)
slot_free(hook, hook->slots);
@@ -57,7 +63,7 @@ void pa_hook_free(pa_hook *hook) {
pa_hook_slot* pa_hook_connect(pa_hook *hook, pa_hook_cb_t cb, void *data) {
pa_hook_slot *slot;
- assert(cb);
+ pa_assert(cb);
slot = pa_xnew(pa_hook_slot, 1);
slot->hook = hook;
@@ -72,8 +78,8 @@ pa_hook_slot* pa_hook_connect(pa_hook *hook, pa_hook_cb_t cb, void *data) {
}
void pa_hook_slot_free(pa_hook_slot *slot) {
- assert(slot);
- assert(!slot->dead);
+ pa_assert(slot);
+ pa_assert(!slot->dead);
if (slot->hook->firing > 0) {
slot->dead = 1;
@@ -86,7 +92,7 @@ pa_hook_result_t pa_hook_fire(pa_hook *hook, void *data) {
pa_hook_slot *slot, *next;
pa_hook_result_t result = PA_HOOK_OK;
- assert(hook);
+ pa_assert(hook);
hook->firing ++;
diff --git a/src/pulsecore/idxset.c b/src/pulsecore/idxset.c
index 70ef7ba7..8a88471f 100644
--- a/src/pulsecore/idxset.c
+++ b/src/pulsecore/idxset.c
@@ -27,32 +27,35 @@
#endif
#include <stdio.h>
-#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <pulse/xmalloc.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/flist.h>
#include "idxset.h"
-typedef struct idxset_entry {
+struct idxset_entry {
void *data;
uint32_t index;
unsigned hash_value;
struct idxset_entry *hash_prev, *hash_next;
struct idxset_entry* iterate_prev, *iterate_next;
-} idxset_entry;
+};
struct pa_idxset {
pa_hash_func_t hash_func;
pa_compare_func_t compare_func;
unsigned hash_table_size, n_entries;
- idxset_entry **hash_table, **array, *iterate_list_head, *iterate_list_tail;
+ struct idxset_entry **hash_table, **array, *iterate_list_head, *iterate_list_tail;
uint32_t index, start_index, array_size;
};
+PA_STATIC_FLIST_DECLARE(entries, 0, pa_xfree);
+
unsigned pa_idxset_string_hash_func(const void *p) {
unsigned hash = 0;
const char *c;
@@ -82,7 +85,7 @@ pa_idxset* pa_idxset_new(pa_hash_func_t hash_func, pa_compare_func_t compare_fun
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(idxset_entry*, s->hash_table_size);
+ s->hash_table = pa_xnew0(struct idxset_entry*, s->hash_table_size);
s->array = NULL;
s->array_size = 0;
s->index = 0;
@@ -95,15 +98,17 @@ pa_idxset* pa_idxset_new(pa_hash_func_t hash_func, pa_compare_func_t compare_fun
}
void pa_idxset_free(pa_idxset *s, void (*free_func) (void *p, void *userdata), void *userdata) {
- assert(s);
+ pa_assert(s);
while (s->iterate_list_head) {
- idxset_entry *e = s->iterate_list_head;
+ struct idxset_entry *e = s->iterate_list_head;
s->iterate_list_head = s->iterate_list_head->iterate_next;
if (free_func)
free_func(e->data, userdata);
- pa_xfree(e);
+
+ if (pa_flist_push(PA_STATIC_FLIST_GET(entries), e) < 0)
+ pa_xfree(e);
}
pa_xfree(s->hash_table);
@@ -111,10 +116,10 @@ void pa_idxset_free(pa_idxset *s, void (*free_func) (void *p, void *userdata), v
pa_xfree(s);
}
-static idxset_entry* hash_scan(pa_idxset *s, idxset_entry* e, const void *p) {
- assert(p);
+static struct idxset_entry* hash_scan(pa_idxset *s, struct idxset_entry* e, const void *p) {
+ pa_assert(p);
- assert(s->compare_func);
+ pa_assert(s->compare_func);
for (; e; e = e->hash_next)
if (s->compare_func(e->data, p) == 0)
return e;
@@ -124,8 +129,10 @@ static idxset_entry* hash_scan(pa_idxset *s, idxset_entry* e, const void *p) {
static void extend_array(pa_idxset *s, uint32_t idx) {
uint32_t i, j, l;
- idxset_entry** n;
- assert(idx >= s->start_index);
+ struct idxset_entry** n;
+
+ pa_assert(s);
+ pa_assert(idx >= s->start_index);
if (idx < s->start_index + s->array_size)
return;
@@ -135,7 +142,7 @@ static void extend_array(pa_idxset *s, uint32_t idx) {
break;
l = idx - s->start_index - i + 100;
- n = pa_xnew0(idxset_entry*, l);
+ n = pa_xnew0(struct idxset_entry*, l);
for (j = 0; j < s->array_size-i; j++)
n[j] = s->array[i+j];
@@ -147,7 +154,9 @@ static void extend_array(pa_idxset *s, uint32_t idx) {
s->start_index += i;
}
-static idxset_entry** array_index(pa_idxset*s, uint32_t idx) {
+static struct idxset_entry** array_index(pa_idxset*s, uint32_t idx) {
+ pa_assert(s);
+
if (idx >= s->start_index + s->array_size)
return NULL;
@@ -159,15 +168,15 @@ static idxset_entry** array_index(pa_idxset*s, uint32_t idx) {
int pa_idxset_put(pa_idxset*s, void *p, uint32_t *idx) {
unsigned h;
- idxset_entry *e, **a;
+ struct idxset_entry *e, **a;
- assert(s);
- assert(p);
+ pa_assert(s);
+ pa_assert(p);
- assert(s->hash_func);
+ pa_assert(s->hash_func);
h = s->hash_func(p) % s->hash_table_size;
- assert(s->hash_table);
+ pa_assert(s->hash_table);
if ((e = hash_scan(s, s->hash_table[h], p))) {
if (idx)
*idx = e->index;
@@ -175,7 +184,8 @@ int pa_idxset_put(pa_idxset*s, void *p, uint32_t *idx) {
return -1;
}
- e = pa_xmalloc(sizeof(idxset_entry));
+ 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;
@@ -190,23 +200,23 @@ int pa_idxset_put(pa_idxset*s, void *p, uint32_t *idx) {
/* Insert into array */
extend_array(s, e->index);
a = array_index(s, e->index);
- assert(a && !*a);
+ pa_assert(a && !*a);
*a = e;
/* Insert into linked list */
e->iterate_next = NULL;
e->iterate_prev = s->iterate_list_tail;
if (s->iterate_list_tail) {
- assert(s->iterate_list_head);
+ pa_assert(s->iterate_list_head);
s->iterate_list_tail->iterate_next = e;
} else {
- assert(!s->iterate_list_head);
+ pa_assert(!s->iterate_list_head);
s->iterate_list_head = e;
}
s->iterate_list_tail = e;
s->n_entries++;
- assert(s->n_entries >= 1);
+ pa_assert(s->n_entries >= 1);
if (idx)
*idx = e->index;
@@ -215,8 +225,8 @@ int pa_idxset_put(pa_idxset*s, void *p, uint32_t *idx) {
}
void* pa_idxset_get_by_index(pa_idxset*s, uint32_t idx) {
- idxset_entry **a;
- assert(s);
+ struct idxset_entry **a;
+ pa_assert(s);
if (!(a = array_index(s, idx)))
return NULL;
@@ -229,13 +239,15 @@ void* pa_idxset_get_by_index(pa_idxset*s, uint32_t idx) {
void* pa_idxset_get_by_data(pa_idxset*s, const void *p, uint32_t *idx) {
unsigned h;
- idxset_entry *e;
- assert(s && p);
+ struct idxset_entry *e;
- assert(s->hash_func);
+ pa_assert(s);
+ pa_assert(p);
+
+ pa_assert(s->hash_func);
h = s->hash_func(p) % s->hash_table_size;
- assert(s->hash_table);
+ pa_assert(s->hash_table);
if (!(e = hash_scan(s, s->hash_table[h], p)))
return NULL;
@@ -245,13 +257,15 @@ void* pa_idxset_get_by_data(pa_idxset*s, const void *p, uint32_t *idx) {
return e->data;
}
-static void remove_entry(pa_idxset *s, idxset_entry *e) {
- idxset_entry **a;
- assert(s && e);
+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);
- assert(a && *a && *a == e);
+ pa_assert(a && *a && *a == e);
*a = NULL;
/* Remove from linked list */
@@ -274,17 +288,18 @@ static void remove_entry(pa_idxset *s, idxset_entry *e) {
else
s->hash_table[e->hash_value] = e->hash_next;
- pa_xfree(e);
+ if (pa_flist_push(PA_STATIC_FLIST_GET(entries), e) < 0)
+ pa_xfree(e);
- assert(s->n_entries >= 1);
+ pa_assert(s->n_entries >= 1);
s->n_entries--;
}
void* pa_idxset_remove_by_index(pa_idxset*s, uint32_t idx) {
- idxset_entry **a;
+ struct idxset_entry **a;
void *data;
- assert(s);
+ pa_assert(s);
if (!(a = array_index(s, idx)))
return NULL;
@@ -299,14 +314,16 @@ void* pa_idxset_remove_by_index(pa_idxset*s, uint32_t idx) {
}
void* pa_idxset_remove_by_data(pa_idxset*s, const void *data, uint32_t *idx) {
- idxset_entry *e;
+ struct idxset_entry *e;
unsigned h;
void *r;
- assert(s->hash_func);
+ pa_assert(s);
+
+ pa_assert(s->hash_func);
h = s->hash_func(data) % s->hash_table_size;
- assert(s->hash_table);
+ pa_assert(s->hash_table);
if (!(e = hash_scan(s, s->hash_table[h], data)))
return NULL;
@@ -320,8 +337,10 @@ 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) {
- idxset_entry **a, *e = NULL;
- assert(s && idx);
+ struct idxset_entry **a, *e = NULL;
+
+ pa_assert(s);
+ pa_assert(idx);
if ((a = array_index(s, *idx)) && *a)
e = (*a)->iterate_next;
@@ -337,7 +356,7 @@ void* pa_idxset_rrobin(pa_idxset *s, uint32_t *idx) {
}
void* pa_idxset_first(pa_idxset *s, uint32_t *idx) {
- assert(s);
+ pa_assert(s);
if (!s->iterate_list_head)
return NULL;
@@ -348,9 +367,10 @@ void* pa_idxset_first(pa_idxset *s, uint32_t *idx) {
}
void *pa_idxset_next(pa_idxset *s, uint32_t *idx) {
- idxset_entry **a, *e = NULL;
- assert(s);
- assert(idx);
+ struct idxset_entry **a, *e = NULL;
+
+ pa_assert(s);
+ pa_assert(idx);
if ((a = array_index(s, *idx)) && *a)
e = (*a)->iterate_next;
@@ -365,13 +385,15 @@ void *pa_idxset_next(pa_idxset *s, uint32_t *idx) {
}
int pa_idxset_foreach(pa_idxset*s, int (*func)(void *p, uint32_t idx, int *del, void*userdata), void *userdata) {
- idxset_entry *e;
- assert(s && func);
+ struct idxset_entry *e;
+
+ pa_assert(s);
+ pa_assert(func);
e = s->iterate_list_head;
while (e) {
int del = 0, r;
- idxset_entry *n = e->iterate_next;
+ struct idxset_entry *n = e->iterate_next;
r = func(e->data, e->index, &del, userdata);
@@ -388,12 +410,14 @@ int pa_idxset_foreach(pa_idxset*s, int (*func)(void *p, uint32_t idx, int *del,
}
unsigned pa_idxset_size(pa_idxset*s) {
- assert(s);
+ pa_assert(s);
+
return s->n_entries;
}
int pa_idxset_isempty(pa_idxset *s) {
- assert(s);
+ pa_assert(s);
+
return s->n_entries == 0;
}
diff --git a/src/pulsecore/idxset.h b/src/pulsecore/idxset.h
index 17a70f4f..5b55cec2 100644
--- a/src/pulsecore/idxset.h
+++ b/src/pulsecore/idxset.h
@@ -44,11 +44,6 @@ int pa_idxset_trivial_compare_func(const void *a, const void *b);
unsigned pa_idxset_string_hash_func(const void *p);
int pa_idxset_string_compare_func(const void *a, const void *b);
-#define PA_PTR_TO_UINT(p) ((unsigned int) (unsigned long) (p))
-#define PA_UINT_TO_PTR(u) ((void*) (unsigned long) (u))
-#define PA_PTR_TO_UINT32(p) ((uint32_t) PA_PTR_TO_UINT(p))
-#define PA_UINT32_TO_PTR(u) PA_UINT_TO_PTR(u)
-
typedef unsigned (*pa_hash_func_t)(const void *p);
typedef int (*pa_compare_func_t)(const void *a, const void *b);
diff --git a/src/pulsecore/inet_ntop.c b/src/pulsecore/inet_ntop.c
index 302369f7..041bc09b 100644
--- a/src/pulsecore/inet_ntop.c
+++ b/src/pulsecore/inet_ntop.c
@@ -47,7 +47,7 @@ const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt) {
switch (af) {
case AF_INET:
- snprintf(dst, cnt, "%d.%d.%d.%d",
+ pa_snprintf(dst, cnt, "%d.%d.%d.%d",
#ifdef WORDS_BIGENDIAN
(int)(in->s_addr >> 24) & 0xff,
(int)(in->s_addr >> 16) & 0xff,
@@ -61,7 +61,7 @@ const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt) {
#endif
break;
case AF_INET6:
- snprintf(dst, cnt, "%x:%x:%x:%x:%x:%x:%x:%x",
+ pa_snprintf(dst, cnt, "%x:%x:%x:%x:%x:%x:%x:%x",
in6->s6_addr[ 0] << 8 | in6->s6_addr[ 1],
in6->s6_addr[ 2] << 8 | in6->s6_addr[ 3],
in6->s6_addr[ 4] << 8 | in6->s6_addr[ 5],
diff --git a/src/pulsecore/iochannel.c b/src/pulsecore/iochannel.c
index 6f58ae75..01f17ab3 100644
--- a/src/pulsecore/iochannel.c
+++ b/src/pulsecore/iochannel.c
@@ -27,7 +27,6 @@
#endif
#include <stdlib.h>
-#include <assert.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
@@ -47,6 +46,7 @@
#include <pulsecore/core-util.h>
#include <pulsecore/socket-util.h>
#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
#include "iochannel.h"
@@ -58,21 +58,21 @@ struct pa_iochannel {
pa_iochannel_cb_t callback;
void*userdata;
- int readable;
- int writable;
- int hungup;
+ pa_bool_t readable;
+ pa_bool_t writable;
+ pa_bool_t hungup;
- int no_close;
+ pa_bool_t no_close;
pa_io_event* input_event, *output_event;
};
static void enable_mainloop_sources(pa_iochannel *io) {
- assert(io);
+ pa_assert(io);
if (io->input_event == io->output_event && io->input_event) {
pa_io_event_flags_t f = PA_IO_EVENT_NULL;
- assert(io->input_event);
+ pa_assert(io->input_event);
if (!io->readable)
f |= PA_IO_EVENT_INPUT;
@@ -90,28 +90,28 @@ static void enable_mainloop_sources(pa_iochannel *io) {
static void callback(pa_mainloop_api* m, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata) {
pa_iochannel *io = userdata;
- int changed = 0;
+ pa_bool_t changed = FALSE;
- assert(m);
- assert(e);
- assert(fd >= 0);
- assert(userdata);
+ pa_assert(m);
+ pa_assert(e);
+ pa_assert(fd >= 0);
+ pa_assert(userdata);
if ((f & (PA_IO_EVENT_HANGUP|PA_IO_EVENT_ERROR)) && !io->hungup) {
- io->hungup = 1;
- changed = 1;
+ io->hungup = TRUE;
+ changed = TRUE;
}
if ((f & PA_IO_EVENT_INPUT) && !io->readable) {
- io->readable = 1;
- changed = 1;
- assert(e == io->input_event);
+ io->readable = TRUE;
+ changed = TRUE;
+ pa_assert(e == io->input_event);
}
if ((f & PA_IO_EVENT_OUTPUT) && !io->writable) {
- io->writable = 1;
- changed = 1;
- assert(e == io->output_event);
+ io->writable = TRUE;
+ changed = TRUE;
+ pa_assert(e == io->output_event);
}
if (changed) {
@@ -125,8 +125,8 @@ static void callback(pa_mainloop_api* m, pa_io_event *e, int fd, pa_io_event_fla
pa_iochannel* pa_iochannel_new(pa_mainloop_api*m, int ifd, int ofd) {
pa_iochannel *io;
- assert(m);
- assert(ifd >= 0 || ofd >= 0);
+ pa_assert(m);
+ pa_assert(ifd >= 0 || ofd >= 0);
io = pa_xnew(pa_iochannel, 1);
io->ifd = ifd;
@@ -136,26 +136,26 @@ pa_iochannel* pa_iochannel_new(pa_mainloop_api*m, int ifd, int ofd) {
io->userdata = NULL;
io->callback = NULL;
- io->readable = 0;
- io->writable = 0;
- io->hungup = 0;
- io->no_close = 0;
+ io->readable = FALSE;
+ io->writable = FALSE;
+ io->hungup = FALSE;
+ io->no_close = FALSE;
io->input_event = io->output_event = NULL;
if (ifd == ofd) {
- assert(ifd >= 0);
- pa_make_nonblock_fd(io->ifd);
+ pa_assert(ifd >= 0);
+ pa_make_fd_nonblock(io->ifd);
io->input_event = io->output_event = m->io_new(m, ifd, PA_IO_EVENT_INPUT|PA_IO_EVENT_OUTPUT, callback, io);
} else {
if (ifd >= 0) {
- pa_make_nonblock_fd(io->ifd);
+ pa_make_fd_nonblock(io->ifd);
io->input_event = m->io_new(m, ifd, PA_IO_EVENT_INPUT, callback, io);
}
if (ofd >= 0) {
- pa_make_nonblock_fd(io->ofd);
+ pa_make_fd_nonblock(io->ofd);
io->output_event = m->io_new(m, ofd, PA_IO_EVENT_OUTPUT, callback, io);
}
}
@@ -164,7 +164,7 @@ pa_iochannel* pa_iochannel_new(pa_mainloop_api*m, int ifd, int ofd) {
}
void pa_iochannel_free(pa_iochannel*io) {
- assert(io);
+ pa_assert(io);
if (io->input_event)
io->mainloop->io_free(io->input_event);
@@ -182,20 +182,20 @@ void pa_iochannel_free(pa_iochannel*io) {
pa_xfree(io);
}
-int pa_iochannel_is_readable(pa_iochannel*io) {
- assert(io);
+pa_bool_t pa_iochannel_is_readable(pa_iochannel*io) {
+ pa_assert(io);
return io->readable || io->hungup;
}
-int pa_iochannel_is_writable(pa_iochannel*io) {
- assert(io);
+pa_bool_t pa_iochannel_is_writable(pa_iochannel*io) {
+ pa_assert(io);
return io->writable && !io->hungup;
}
-int pa_iochannel_is_hungup(pa_iochannel*io) {
- assert(io);
+pa_bool_t pa_iochannel_is_hungup(pa_iochannel*io) {
+ pa_assert(io);
return io->hungup;
}
@@ -203,14 +203,13 @@ int pa_iochannel_is_hungup(pa_iochannel*io) {
ssize_t pa_iochannel_write(pa_iochannel*io, const void*data, size_t l) {
ssize_t r;
- assert(io);
- assert(data);
- assert(l);
- assert(io->ofd >= 0);
+ pa_assert(io);
+ pa_assert(data);
+ pa_assert(l);
+ pa_assert(io->ofd >= 0);
- r = pa_write(io->ofd, data, l, &io->ofd_type);
- if (r >= 0) {
- io->writable = 0;
+ if ((r = pa_write(io->ofd, data, l, &io->ofd_type)) >= 0) {
+ io->writable = FALSE;
enable_mainloop_sources(io);
}
@@ -220,13 +219,12 @@ ssize_t pa_iochannel_write(pa_iochannel*io, const void*data, size_t l) {
ssize_t pa_iochannel_read(pa_iochannel*io, void*data, size_t l) {
ssize_t r;
- assert(io);
- assert(data);
- assert(io->ifd >= 0);
+ pa_assert(io);
+ pa_assert(data);
+ pa_assert(io->ifd >= 0);
- r = pa_read(io->ifd, data, l, &io->ifd_type);
- if (r >= 0) {
- io->readable = 0;
+ if ((r = pa_read(io->ifd, data, l, &io->ifd_type)) >= 0) {
+ io->readable = FALSE;
enable_mainloop_sources(io);
}
@@ -235,13 +233,13 @@ ssize_t pa_iochannel_read(pa_iochannel*io, void*data, size_t l) {
#ifdef HAVE_CREDS
-int pa_iochannel_creds_supported(pa_iochannel *io) {
+pa_bool_t pa_iochannel_creds_supported(pa_iochannel *io) {
struct sockaddr_un sa;
socklen_t l;
- assert(io);
- assert(io->ifd >= 0);
- assert(io->ofd == io->ifd);
+ pa_assert(io);
+ pa_assert(io->ifd >= 0);
+ pa_assert(io->ofd == io->ifd);
l = sizeof(sa);
@@ -254,8 +252,8 @@ int pa_iochannel_creds_supported(pa_iochannel *io) {
int pa_iochannel_creds_enable(pa_iochannel *io) {
int t = 1;
- assert(io);
- assert(io->ifd >= 0);
+ pa_assert(io);
+ pa_assert(io->ifd >= 0);
if (setsockopt(io->ifd, SOL_SOCKET, SO_PASSCRED, &t, sizeof(t)) < 0) {
pa_log_error("setsockopt(SOL_SOCKET, SO_PASSCRED): %s", pa_cstrerror(errno));
@@ -273,10 +271,10 @@ ssize_t pa_iochannel_write_with_creds(pa_iochannel*io, const void*data, size_t l
struct ucred *u;
struct cmsghdr *cmsg;
- assert(io);
- assert(data);
- assert(l);
- assert(io->ofd >= 0);
+ pa_assert(io);
+ pa_assert(data);
+ pa_assert(l);
+ pa_assert(io->ofd >= 0);
memset(&iov, 0, sizeof(iov));
iov.iov_base = (void*) data;
@@ -309,25 +307,25 @@ ssize_t pa_iochannel_write_with_creds(pa_iochannel*io, const void*data, size_t l
mh.msg_flags = 0;
if ((r = sendmsg(io->ofd, &mh, MSG_NOSIGNAL)) >= 0) {
- io->writable = 0;
+ io->writable = FALSE;
enable_mainloop_sources(io);
}
return r;
}
-ssize_t pa_iochannel_read_with_creds(pa_iochannel*io, void*data, size_t l, pa_creds *creds, int *creds_valid) {
+ssize_t pa_iochannel_read_with_creds(pa_iochannel*io, void*data, size_t l, pa_creds *creds, pa_bool_t *creds_valid) {
ssize_t r;
struct msghdr mh;
struct iovec iov;
uint8_t cmsg_data[CMSG_SPACE(sizeof(struct ucred))];
- assert(io);
- assert(data);
- assert(l);
- assert(io->ifd >= 0);
- assert(creds);
- assert(creds_valid);
+ pa_assert(io);
+ pa_assert(data);
+ pa_assert(l);
+ pa_assert(io->ifd >= 0);
+ pa_assert(creds);
+ pa_assert(creds_valid);
memset(&iov, 0, sizeof(iov));
iov.iov_base = data;
@@ -353,17 +351,17 @@ ssize_t pa_iochannel_read_with_creds(pa_iochannel*io, void*data, size_t l, pa_cr
if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_CREDENTIALS) {
struct ucred u;
- assert(cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred)));
+ pa_assert(cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred)));
memcpy(&u, CMSG_DATA(cmsg), sizeof(struct ucred));
creds->gid = u.gid;
creds->uid = u.uid;
- *creds_valid = 1;
+ *creds_valid = TRUE;
break;
}
}
- io->readable = 0;
+ io->readable = FALSE;
enable_mainloop_sources(io);
}
@@ -373,46 +371,52 @@ ssize_t pa_iochannel_read_with_creds(pa_iochannel*io, void*data, size_t l, pa_cr
#endif /* HAVE_CREDS */
void pa_iochannel_set_callback(pa_iochannel*io, pa_iochannel_cb_t _callback, void *userdata) {
- assert(io);
+ pa_assert(io);
io->callback = _callback;
io->userdata = userdata;
}
-void pa_iochannel_set_noclose(pa_iochannel*io, int b) {
- assert(io);
+void pa_iochannel_set_noclose(pa_iochannel*io, pa_bool_t b) {
+ pa_assert(io);
- io->no_close = b;
+ io->no_close = !!b;
}
void pa_iochannel_socket_peer_to_string(pa_iochannel*io, char*s, size_t l) {
- assert(io);
- assert(s);
- assert(l);
+ pa_assert(io);
+ pa_assert(s);
+ pa_assert(l);
pa_socket_peer_to_string(io->ifd, s, l);
}
int pa_iochannel_socket_set_rcvbuf(pa_iochannel *io, size_t l) {
- assert(io);
+ pa_assert(io);
return pa_socket_set_rcvbuf(io->ifd, l);
}
int pa_iochannel_socket_set_sndbuf(pa_iochannel *io, size_t l) {
- assert(io);
+ pa_assert(io);
return pa_socket_set_sndbuf(io->ofd, l);
}
pa_mainloop_api* pa_iochannel_get_mainloop_api(pa_iochannel *io) {
- assert(io);
+ pa_assert(io);
return io->mainloop;
}
int pa_iochannel_get_recv_fd(pa_iochannel *io) {
- assert(io);
+ pa_assert(io);
return io->ifd;
}
+
+int pa_iochannel_get_send_fd(pa_iochannel *io) {
+ pa_assert(io);
+
+ return io->ofd;
+}
diff --git a/src/pulsecore/iochannel.h b/src/pulsecore/iochannel.h
index c22fefd3..c9794d99 100644
--- a/src/pulsecore/iochannel.h
+++ b/src/pulsecore/iochannel.h
@@ -25,10 +25,15 @@
USA.
***/
+#ifndef PACKAGE
+#error "Please include config.h before including this file!"
+#endif
+
#include <sys/types.h>
#include <pulse/mainloop-api.h>
#include <pulsecore/creds.h>
+#include <pulsecore/macro.h>
/* A wrapper around UNIX file descriptors for attaching them to the a
main event loop. Everytime new data may be read or be written to
@@ -54,20 +59,20 @@ ssize_t pa_iochannel_write(pa_iochannel*io, const void*data, size_t l);
ssize_t pa_iochannel_read(pa_iochannel*io, void*data, size_t l);
#ifdef HAVE_CREDS
-int pa_iochannel_creds_supported(pa_iochannel *io);
+pa_bool_t pa_iochannel_creds_supported(pa_iochannel *io);
int pa_iochannel_creds_enable(pa_iochannel *io);
ssize_t pa_iochannel_write_with_creds(pa_iochannel*io, const void*data, size_t l, const pa_creds *ucred);
-ssize_t pa_iochannel_read_with_creds(pa_iochannel*io, void*data, size_t l, pa_creds *ucred, int *creds_valid);
+ssize_t pa_iochannel_read_with_creds(pa_iochannel*io, void*data, size_t l, pa_creds *ucred, pa_bool_t *creds_valid);
#endif
-int pa_iochannel_is_readable(pa_iochannel*io);
-int pa_iochannel_is_writable(pa_iochannel*io);
-int pa_iochannel_is_hungup(pa_iochannel*io);
+pa_bool_t pa_iochannel_is_readable(pa_iochannel*io);
+pa_bool_t pa_iochannel_is_writable(pa_iochannel*io);
+pa_bool_t pa_iochannel_is_hungup(pa_iochannel*io);
/* Don't close the file descirptors when the io channel is freed. By
* default the file descriptors are closed. */
-void pa_iochannel_set_noclose(pa_iochannel*io, int b);
+void pa_iochannel_set_noclose(pa_iochannel*io, pa_bool_t b);
/* Set the callback function that is called whenever data becomes available for read or write */
typedef void (*pa_iochannel_cb_t)(pa_iochannel*io, void *userdata);
@@ -83,5 +88,6 @@ int pa_iochannel_socket_set_sndbuf(pa_iochannel*io, size_t l);
pa_mainloop_api* pa_iochannel_get_mainloop_api(pa_iochannel *io);
int pa_iochannel_get_recv_fd(pa_iochannel *io);
+int pa_iochannel_get_send_fd(pa_iochannel *io);
#endif
diff --git a/src/pulsecore/ioline.c b/src/pulsecore/ioline.c
index 07b60bee..5fd2189b 100644
--- a/src/pulsecore/ioline.c
+++ b/src/pulsecore/ioline.c
@@ -27,14 +27,16 @@
#include <errno.h>
#include <stdio.h>
-#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <pulse/xmalloc.h>
+#include <pulsecore/winsock.h>
#include <pulsecore/core-error.h>
#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/refcnt.h>
#include "ioline.h"
@@ -42,10 +44,11 @@
#define READ_SIZE (1024)
struct pa_ioline {
+ PA_REFCNT_DECLARE;
+
pa_iochannel *io;
pa_defer_event *defer_event;
pa_mainloop_api *mainloop;
- int ref;
int dead;
char *wbuf;
@@ -65,9 +68,10 @@ static void defer_callback(pa_mainloop_api*m, pa_defer_event*e, void *userdata);
pa_ioline* pa_ioline_new(pa_iochannel *io) {
pa_ioline *l;
- assert(io);
+ pa_assert(io);
l = pa_xnew(pa_ioline, 1);
+ PA_REFCNT_INIT(l);
l->io = io;
l->dead = 0;
@@ -79,7 +83,6 @@ pa_ioline* pa_ioline_new(pa_iochannel *io) {
l->callback = NULL;
l->userdata = NULL;
- l->ref = 1;
l->mainloop = pa_iochannel_get_mainloop_api(io);
@@ -94,7 +97,7 @@ pa_ioline* pa_ioline_new(pa_iochannel *io) {
}
static void ioline_free(pa_ioline *l) {
- assert(l);
+ pa_assert(l);
if (l->io)
pa_iochannel_free(l->io);
@@ -108,24 +111,24 @@ static void ioline_free(pa_ioline *l) {
}
void pa_ioline_unref(pa_ioline *l) {
- assert(l);
- assert(l->ref >= 1);
+ pa_assert(l);
+ pa_assert(PA_REFCNT_VALUE(l) >= 1);
- if ((--l->ref) <= 0)
+ if (PA_REFCNT_DEC(l) <= 0)
ioline_free(l);
}
pa_ioline* pa_ioline_ref(pa_ioline *l) {
- assert(l);
- assert(l->ref >= 1);
+ pa_assert(l);
+ pa_assert(PA_REFCNT_VALUE(l) >= 1);
- l->ref++;
+ PA_REFCNT_INC(l);
return l;
}
void pa_ioline_close(pa_ioline *l) {
- assert(l);
- assert(l->ref >= 1);
+ pa_assert(l);
+ pa_assert(PA_REFCNT_VALUE(l) >= 1);
l->dead = 1;
@@ -146,9 +149,9 @@ void pa_ioline_close(pa_ioline *l) {
void pa_ioline_puts(pa_ioline *l, const char *c) {
size_t len;
- assert(l);
- assert(l->ref >= 1);
- assert(c);
+ pa_assert(l);
+ pa_assert(PA_REFCNT_VALUE(l) >= 1);
+ pa_assert(c);
if (l->dead)
return;
@@ -158,7 +161,7 @@ void pa_ioline_puts(pa_ioline *l, const char *c) {
len = BUFFER_LIMIT - l->wbuf_valid_length;
if (len) {
- assert(l->wbuf_length >= l->wbuf_valid_length);
+ pa_assert(l->wbuf_length >= l->wbuf_valid_length);
/* In case the allocated buffer is too small, enlarge it. */
if (l->wbuf_valid_length + len > l->wbuf_length) {
@@ -178,7 +181,7 @@ void pa_ioline_puts(pa_ioline *l, const char *c) {
l->wbuf_index = 0;
}
- assert(l->wbuf_index + l->wbuf_valid_length + len <= l->wbuf_length);
+ pa_assert(l->wbuf_index + l->wbuf_valid_length + len <= l->wbuf_length);
/* Append the new string */
memcpy(l->wbuf + l->wbuf_index + l->wbuf_valid_length, c, len);
@@ -189,17 +192,17 @@ 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) {
- assert(l);
- assert(l->ref >= 1);
+ pa_assert(l);
+ pa_assert(PA_REFCNT_VALUE(l) >= 1);
l->callback = callback;
l->userdata = userdata;
}
static void failure(pa_ioline *l, int process_leftover) {
- assert(l);
- assert(l->ref >= 1);
- assert(!l->dead);
+ pa_assert(l);
+ pa_assert(PA_REFCNT_VALUE(l) >= 1);
+ pa_assert(!l->dead);
if (process_leftover && l->rbuf_valid_length > 0) {
/* Pass the last missing bit to the client */
@@ -220,7 +223,9 @@ static void failure(pa_ioline *l, int process_leftover) {
}
static void scan_for_lines(pa_ioline *l, size_t skip) {
- assert(l && l->ref >= 1 && skip < l->rbuf_valid_length);
+ pa_assert(l);
+ pa_assert(PA_REFCNT_VALUE(l) >= 1);
+ pa_assert(skip < l->rbuf_valid_length);
while (!l->dead && l->rbuf_valid_length > skip) {
char *e, *p;
@@ -255,7 +260,8 @@ static void scan_for_lines(pa_ioline *l, size_t skip) {
static int do_write(pa_ioline *l);
static int do_read(pa_ioline *l) {
- assert(l && l->ref >= 1);
+ pa_assert(l);
+ pa_assert(PA_REFCNT_VALUE(l) >= 1);
while (!l->dead && pa_iochannel_is_readable(l->io)) {
ssize_t r;
@@ -289,11 +295,11 @@ static int do_read(pa_ioline *l) {
len = l->rbuf_length - l->rbuf_index - l->rbuf_valid_length;
- assert(len >= READ_SIZE);
+ pa_assert(len >= READ_SIZE);
/* Read some data */
if ((r = pa_iochannel_read(l->io, l->rbuf+l->rbuf_index+l->rbuf_valid_length, len)) <= 0) {
- if (r < 0) {
+ if (r < 0 && errno != ECONNRESET) {
pa_log("read(): %s", pa_cstrerror(errno));
failure(l, 0);
} else
@@ -314,13 +320,19 @@ static int do_read(pa_ioline *l) {
/* Try to flush the buffer */
static int do_write(pa_ioline *l) {
ssize_t r;
- assert(l && l->ref >= 1);
+
+ pa_assert(l);
+ pa_assert(PA_REFCNT_VALUE(l) >= 1);
while (!l->dead && pa_iochannel_is_writable(l->io) && l->wbuf_valid_length) {
- if ((r = pa_iochannel_write(l->io, l->wbuf+l->wbuf_index, l->wbuf_valid_length)) < 0) {
- pa_log("write(): %s", r < 0 ? pa_cstrerror(errno) : "EOF");
+ if ((r = pa_iochannel_write(l->io, l->wbuf+l->wbuf_index, l->wbuf_valid_length)) <= 0) {
+
+ if (r < 0 && errno != EPIPE)
+ pa_log("write(): %s", pa_cstrerror(errno));
+
failure(l, 0);
+
return -1;
}
@@ -337,8 +349,8 @@ static int do_write(pa_ioline *l) {
/* Try to flush read/write data */
static void do_work(pa_ioline *l) {
- assert(l);
- assert(l->ref >= 1);
+ pa_assert(l);
+ pa_assert(PA_REFCNT_VALUE(l) >= 1);
pa_ioline_ref(l);
@@ -358,21 +370,28 @@ static void do_work(pa_ioline *l) {
static void io_callback(pa_iochannel*io, void *userdata) {
pa_ioline *l = userdata;
- assert(io && l && l->ref >= 1);
+
+ pa_assert(io);
+ pa_assert(l);
+ pa_assert(PA_REFCNT_VALUE(l) >= 1);
do_work(l);
}
static void defer_callback(pa_mainloop_api*m, pa_defer_event*e, void *userdata) {
pa_ioline *l = userdata;
- assert(l && l->ref >= 1 && l->mainloop == m && l->defer_event == e);
+
+ pa_assert(l);
+ pa_assert(PA_REFCNT_VALUE(l) >= 1);
+ pa_assert(l->mainloop == m);
+ pa_assert(l->defer_event == e);
do_work(l);
}
void pa_ioline_defer_close(pa_ioline *l) {
- assert(l);
- assert(l->ref >= 1);
+ pa_assert(l);
+ pa_assert(PA_REFCNT_VALUE(l) >= 1);
l->defer_close = 1;
@@ -384,8 +403,8 @@ void pa_ioline_printf(pa_ioline *l, const char *format, ...) {
char *t;
va_list ap;
- assert(l);
- assert(l->ref >= 1);
+ pa_assert(l);
+ pa_assert(PA_REFCNT_VALUE(l) >= 1);
va_start(ap, format);
t = pa_vsprintf_malloc(format, ap);
diff --git a/src/pulsecore/ipacl.c b/src/pulsecore/ipacl.c
index a240d2a0..9b22e8f5 100644
--- a/src/pulsecore/ipacl.c
+++ b/src/pulsecore/ipacl.c
@@ -46,13 +46,13 @@
#include <arpa/inet.h>
#endif
-#include "winsock.h"
-
#include <pulse/xmalloc.h>
#include <pulsecore/core-util.h>
#include <pulsecore/llist.h>
#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/winsock.h>
#ifndef HAVE_INET_PTON
#include "inet_pton.h"
@@ -77,7 +77,7 @@ pa_ip_acl* pa_ip_acl_new(const char *s) {
char *a;
pa_ip_acl *acl;
- assert(s);
+ pa_assert(s);
acl = pa_xnew(pa_ip_acl, 1);
PA_LLIST_HEAD_INIT(struct acl_entry, acl->entries);
@@ -91,7 +91,7 @@ pa_ip_acl* pa_ip_acl_new(const char *s) {
*slash = 0;
slash++;
if (pa_atou(slash, &bits) < 0) {
- pa_log("failed to parse number of bits: %s", slash);
+ pa_log_warn("Failed to parse number of bits: %s", slash);
goto fail;
}
} else
@@ -102,21 +102,21 @@ pa_ip_acl* pa_ip_acl_new(const char *s) {
e.bits = bits == (uint32_t) -1 ? 32 : (int) bits;
if (e.bits > 32) {
- pa_log("number of bits out of range: %i", e.bits);
+ pa_log_warn("Number of bits out of range: %i", e.bits);
goto fail;
}
e.family = AF_INET;
if (e.bits < 32 && (uint32_t) (ntohl(e.address_ipv4.s_addr) << e.bits) != 0)
- pa_log_warn("WARNING: Host part of ACL entry '%s/%u' is not zero!", a, e.bits);
+ pa_log_warn("Host part of ACL entry '%s/%u' is not zero!", a, e.bits);
} else if (inet_pton(AF_INET6, a, &e.address_ipv6) > 0) {
e.bits = bits == (uint32_t) -1 ? 128 : (int) bits;
if (e.bits > 128) {
- pa_log("number of bits out of range: %i", e.bits);
+ pa_log_warn("Number of bits out of range: %i", e.bits);
goto fail;
}
e.family = AF_INET6;
@@ -138,11 +138,11 @@ pa_ip_acl* pa_ip_acl_new(const char *s) {
}
if (t)
- pa_log_warn("WARNING: Host part of ACL entry '%s/%u' is not zero!", a, e.bits);
+ pa_log_warn("Host part of ACL entry '%s/%u' is not zero!", a, e.bits);
}
} else {
- pa_log("failed to parse address: %s", a);
+ pa_log_warn("Failed to parse address: %s", a);
goto fail;
}
@@ -162,7 +162,7 @@ fail:
}
void pa_ip_acl_free(pa_ip_acl *acl) {
- assert(acl);
+ pa_assert(acl);
while (acl->entries) {
struct acl_entry *e = acl->entries;
@@ -178,8 +178,8 @@ int pa_ip_acl_check(pa_ip_acl *acl, int fd) {
struct acl_entry *e;
socklen_t salen;
- assert(acl);
- assert(fd >= 0);
+ pa_assert(acl);
+ pa_assert(fd >= 0);
salen = sizeof(sa);
if (getpeername(fd, (struct sockaddr*) &sa, &salen) < 0)
diff --git a/src/pulsecore/llist.h b/src/pulsecore/llist.h
index 8fc8e22b..e62f15b4 100644
--- a/src/pulsecore/llist.h
+++ b/src/pulsecore/llist.h
@@ -24,77 +24,86 @@
USA.
***/
-#include <assert.h>
+#include <pulsecore/macro.h>
/* Some macros for maintaining doubly linked lists */
/* The head of the linked list. Use this in the structure that shall
* contain the head of the linked list */
-#define PA_LLIST_HEAD(t,name) t *name
+#define PA_LLIST_HEAD(t,name) \
+ t *name
/* The pointers in the linked list's items. Use this in the item structure */
-#define PA_LLIST_FIELDS(t) t *next, *prev
+#define PA_LLIST_FIELDS(t) \
+ t *next, *prev
/* Initialize the list's head */
-#define PA_LLIST_HEAD_INIT(t,item) do { (item) = (t*) NULL; } while(0)
+#define PA_LLIST_HEAD_INIT(t,item) \
+ do { \
+ (item) = (t*) NULL; } \
+ while(0)
/* Initialize a list item */
-#define PA_LLIST_INIT(t,item) do { \
- t *_item = (item); \
- assert(_item); \
- _item->prev = _item->next = NULL; \
- } while(0)
+#define PA_LLIST_INIT(t,item) \
+ do { \
+ t *_item = (item); \
+ pa_assert(_item); \
+ _item->prev = _item->next = NULL; \
+ } while(0)
/* Prepend an item to the list */
-#define PA_LLIST_PREPEND(t,head,item) do { \
- t **_head = &(head), *_item = (item); \
- assert(_item); \
- if ((_item->next = *_head)) \
- _item->next->prev = _item; \
- _item->prev = NULL; \
- *_head = _item; \
- } while (0)
+#define PA_LLIST_PREPEND(t,head,item) \
+ do { \
+ t **_head = &(head), *_item = (item); \
+ pa_assert(_item); \
+ if ((_item->next = *_head)) \
+ _item->next->prev = _item; \
+ _item->prev = NULL; \
+ *_head = _item; \
+ } while (0)
/* Remove an item from the list */
-#define PA_LLIST_REMOVE(t,head,item) do { \
- t **_head = &(head), *_item = (item); \
- assert(_item); \
- if (_item->next) \
- _item->next->prev = _item->prev; \
- if (_item->prev) \
- _item->prev->next = _item->next; \
- else {\
- assert(*_head == _item); \
- *_head = _item->next; \
- } \
- _item->next = _item->prev = NULL; \
- } while(0)
-
-#define PA_LLIST_FIND_HEAD(t,item,head) \
-do { \
- t **_head = (head), *_item = (item); \
- *_head = _item; \
- assert(_head); \
- while ((*_head)->prev) \
- *_head = (*_head)->prev; \
-} while (0)
-
-#define PA_LLIST_INSERT_AFTER(t,head,a,b) \
-do { \
- t **_head = &(head), *_a = (a), *_b = (b); \
- assert(_b); \
- if (!_a) { \
- if ((_b->next = *_head)) \
- _b->next->prev = _b; \
- _b->prev = NULL; \
- *_head = _b; \
- } else { \
- if ((_b->next = _a->next)) \
- _b->next->prev = _b; \
- _b->prev = _a; \
- _a->next = _b; \
- } \
-} while (0)
-
+#define PA_LLIST_REMOVE(t,head,item) \
+ do { \
+ t **_head = &(head), *_item = (item); \
+ pa_assert(_item); \
+ if (_item->next) \
+ _item->next->prev = _item->prev; \
+ if (_item->prev) \
+ _item->prev->next = _item->next; \
+ else { \
+ pa_assert(*_head == _item); \
+ *_head = _item->next; \
+ } \
+ _item->next = _item->prev = NULL; \
+ } while(0)
+
+/* Find the head of the list */
+#define PA_LLIST_FIND_HEAD(t,item,head) \
+ do { \
+ t **_head = (head), *_item = (item); \
+ *_head = _item; \
+ pa_assert(_head); \
+ while ((*_head)->prev) \
+ *_head = (*_head)->prev; \
+ } while (0)
+
+/* Insert an item after another one (a = where, b = what) */
+#define PA_LLIST_INSERT_AFTER(t,head,a,b) \
+ do { \
+ t **_head = &(head), *_a = (a), *_b = (b); \
+ pa_assert(_b); \
+ if (!_a) { \
+ if ((_b->next = *_head)) \
+ _b->next->prev = _b; \
+ _b->prev = NULL; \
+ *_head = _b; \
+ } else { \
+ if ((_b->next = _a->next)) \
+ _b->next->prev = _b; \
+ _b->prev = _a; \
+ _a->next = _b; \
+ } \
+ } while (0)
#endif
diff --git a/src/pulsecore/log.c b/src/pulsecore/log.c
index 0033adb9..c824e84d 100644
--- a/src/pulsecore/log.c
+++ b/src/pulsecore/log.c
@@ -26,7 +26,6 @@
#include <config.h>
#endif
-#include <assert.h>
#include <stdarg.h>
#include <stdio.h>
#include <unistd.h>
@@ -40,6 +39,7 @@
#include <pulse/xmalloc.h>
#include <pulse/util.h>
+#include <pulsecore/macro.h>
#include <pulsecore/core-util.h>
#include "log.h"
@@ -71,24 +71,30 @@ static const char level_to_char[] = {
};
void pa_log_set_ident(const char *p) {
- if (log_ident)
- pa_xfree(log_ident);
- if (log_ident_local)
- pa_xfree(log_ident_local);
+ pa_xfree(log_ident);
+ pa_xfree(log_ident_local);
log_ident = pa_xstrdup(p);
- log_ident_local = pa_utf8_to_locale(log_ident);
- if (!log_ident_local)
+ if (!(log_ident_local = pa_utf8_to_locale(log_ident)))
log_ident_local = pa_xstrdup(log_ident);
}
+/* To make valgrind shut up. */
+static void ident_destructor(void) PA_GCC_DESTRUCTOR;
+static void ident_destructor(void) {
+ pa_xfree(log_ident);
+ pa_xfree(log_ident_local);
+}
+
void pa_log_set_maximal_level(pa_log_level_t l) {
- assert(l < PA_LOG_LEVEL_MAX);
+ pa_assert(l < PA_LOG_LEVEL_MAX);
+
maximal_level = l;
}
void pa_log_set_target(pa_log_target_t t, void (*func)(pa_log_level_t l, const char*s)) {
- assert(t == PA_LOG_USER || !func);
+ pa_assert(t == PA_LOG_USER || !func);
+
log_target = t;
user_log_func = func;
}
@@ -104,8 +110,8 @@ void pa_log_levelv_meta(
const char *e;
char *text, *t, *n, *location;
- assert(level < PA_LOG_LEVEL_MAX);
- assert(format);
+ pa_assert(level < PA_LOG_LEVEL_MAX);
+ pa_assert(format);
if ((e = getenv(ENV_LOGLEVEL)))
maximal_level = atoi(e);
@@ -221,6 +227,7 @@ void pa_log_levelv(pa_log_level_t level, const char *format, va_list ap) {
void pa_log_level(pa_log_level_t level, const char *format, ...) {
va_list ap;
+
va_start(ap, format);
pa_log_levelv_meta(level, NULL, 0, NULL, format, ap);
va_end(ap);
diff --git a/src/pulsecore/ltdl-helper.c b/src/pulsecore/ltdl-helper.c
new file mode 100644
index 00000000..711396d8
--- /dev/null
+++ b/src/pulsecore/ltdl-helper.c
@@ -0,0 +1,64 @@
+/* $Id$ */
+
+/***
+ 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.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <ctype.h>
+
+#include <pulse/xmalloc.h>
+#include <pulse/util.h>
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/macro.h>
+
+#include "ltdl-helper.h"
+
+pa_void_func_t pa_load_sym(lt_dlhandle handle, const char *module, const char *symbol) {
+ char *sn, *c;
+ 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))))
+ return f;
+
+ /* As the .la files might have been cleansed from the system, we should
+ * try with the ltdl prefix as well. */
+
+ sn = pa_sprintf_malloc("%s_LTX_%s", module, symbol);
+
+ for (c = sn; *c; c++)
+ if (!isalnum(*c))
+ *c = '_';
+
+ f = (pa_void_func_t) (long) lt_dlsym(handle, sn);
+ pa_xfree(sn);
+
+ return f;
+}
diff --git a/src/pulsecore/ltdl-helper.h b/src/pulsecore/ltdl-helper.h
new file mode 100644
index 00000000..5c7388a1
--- /dev/null
+++ b/src/pulsecore/ltdl-helper.h
@@ -0,0 +1,34 @@
+#ifndef foopulsecoreltdlhelperhfoo
+#define foopulsecoreltdlhelperhfoo
+
+/* $Id$ */
+
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2004-2006 Lennart Poettering
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <ltdl.h>
+
+typedef void (*pa_void_func_t)(void);
+
+pa_void_func_t pa_load_sym(lt_dlhandle handle, const char*module, const char *symbol);
+
+#endif
+
diff --git a/src/pulsecore/macro.h b/src/pulsecore/macro.h
new file mode 100644
index 00000000..c6bba437
--- /dev/null
+++ b/src/pulsecore/macro.h
@@ -0,0 +1,149 @@
+#ifndef foopulsemacrohfoo
+#define foopulsemacrohfoo
+
+/* $Id$ */
+
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2004-2006 Lennart Poettering
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <assert.h>
+#include <limits.h>
+#include <unistd.h>
+
+#include <pulsecore/log.h>
+
+#ifndef PACKAGE
+#error "Please include config.h before including this file!"
+#endif
+
+#if defined(PAGE_SIZE)
+#define PA_PAGE_SIZE ((size_t) PAGE_SIZE)
+#elif defined(PAGESIZE)
+#define PA_PAGE_SIZE ((size_t) PAGESIZE)
+#elif defined(HAVE_SYSCONF)
+#define PA_PAGE_SIZE ((size_t) (sysconf(_SC_PAGE_SIZE)))
+#else
+/* Let's hope it's like x86. */
+#define PA_PAGE_SIZE ((size_t) 4096)
+#endif
+
+static inline size_t pa_align(size_t l) {
+ return (((l + sizeof(void*) - 1) / sizeof(void*)) * sizeof(void*));
+}
+#define PA_ALIGN(x) (pa_align(x))
+
+static inline void* pa_page_align_ptr(const void *p) {
+ return (void*) (((size_t) p) & ~(PA_PAGE_SIZE-1));
+}
+#define PA_PAGE_ALIGN_PTR(x) (pa_page_align_ptr(x))
+
+static inline size_t pa_page_align(size_t l) {
+ return l & ~(PA_PAGE_SIZE-1);
+}
+#define PA_PAGE_ALIGN(x) (pa_page_align(x))
+
+#define PA_ELEMENTSOF(x) (sizeof(x)/sizeof((x)[0]))
+
+#ifndef MAX
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#endif
+
+#ifndef MIN
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#endif
+
+#ifndef CLAMP
+#define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x)))
+#endif
+
+/* This type is not intended to be used in exported APIs! Use classic "int" there! */
+#ifdef HAVE_STD_BOOL
+typedef _Bool pa_bool_t;
+#else
+typedef int pa_bool_t;
+#endif
+
+#ifndef FALSE
+#define FALSE ((pa_bool_t) 0)
+#endif
+
+#ifndef TRUE
+#define TRUE (!FALSE)
+#endif
+
+#ifdef __GNUC__
+#define PA_PRETTY_FUNCTION __PRETTY_FUNCTION__
+#else
+#define PA_PRETTY_FUNCTION ""
+#endif
+
+#define pa_return_if_fail(expr) \
+ do { \
+ if (!(expr)) { \
+ pa_log_debug("%s: Assertion <%s> failed.\n", PA_PRETTY_FUNCTION, #expr ); \
+ return; \
+ } \
+ } while(0)
+
+#define pa_return_val_if_fail(expr, val) \
+ do { \
+ if (!(expr)) { \
+ pa_log_debug("%s: Assertion <%s> failed.\n", PA_PRETTY_FUNCTION, #expr ); \
+ return (val); \
+ } \
+ } while(0)
+
+#define pa_return_null_if_fail(expr) pa_return_val_if_fail(expr, NULL)
+
+#define pa_assert assert
+
+#define pa_assert_not_reached() pa_assert(!"Should not be reached.")
+
+/* An assert which guarantees side effects of x */
+#ifdef NDEBUG
+#define pa_assert_se(x) x
+#else
+#define pa_assert_se(x) pa_assert(x)
+#endif
+
+#define PA_PTR_TO_UINT(p) ((unsigned int) (unsigned long) (p))
+#define PA_UINT_TO_PTR(u) ((void*) (unsigned long) (u))
+
+#define PA_PTR_TO_UINT32(p) ((uint32_t) PA_PTR_TO_UINT(p))
+#define PA_UINT32_TO_PTR(u) PA_UINT_TO_PTR((uint32_t) u)
+
+#define PA_PTR_TO_INT(p) ((int) PA_PTR_TO_UINT(p))
+#define PA_INT_TO_PTR(u) PA_UINT_TO_PTR((int) u)
+
+#define PA_PTR_TO_INT32(p) ((int32_t) PA_PTR_TO_UINT(p))
+#define PA_INT32_TO_PTR(u) PA_UINT_TO_PTR((int32_t) u)
+
+#ifdef OS_IS_WIN32
+#define PA_PATH_SEP "\\"
+#define PA_PATH_SEP_CHAR '\\'
+#else
+#define PA_PATH_SEP "/"
+#define PA_PATH_SEP_CHAR '/'
+#endif
+
+#endif
diff --git a/src/pulsecore/mcalign.c b/src/pulsecore/mcalign.c
index dd1d71f3..8ca7c962 100644
--- a/src/pulsecore/mcalign.c
+++ b/src/pulsecore/mcalign.c
@@ -27,10 +27,10 @@
#include <stdio.h>
#include <stdlib.h>
-#include <assert.h>
#include <string.h>
#include <pulse/xmalloc.h>
+#include <pulsecore/macro.h>
#include "mcalign.h"
@@ -41,7 +41,7 @@ struct pa_mcalign {
pa_mcalign *pa_mcalign_new(size_t base) {
pa_mcalign *m;
- assert(base);
+ pa_assert(base);
m = pa_xnew(pa_mcalign, 1);
@@ -53,7 +53,7 @@ pa_mcalign *pa_mcalign_new(size_t base) {
}
void pa_mcalign_free(pa_mcalign *m) {
- assert(m);
+ pa_assert(m);
if (m->leftover.memblock)
pa_memblock_unref(m->leftover.memblock);
@@ -65,13 +65,13 @@ void pa_mcalign_free(pa_mcalign *m) {
}
void pa_mcalign_push(pa_mcalign *m, const pa_memchunk *c) {
- assert(m);
- assert(c);
+ pa_assert(m);
+ pa_assert(c);
- assert(c->memblock);
- assert(c->length > 0);
+ pa_assert(c->memblock);
+ pa_assert(c->length > 0);
- assert(!m->current.memblock);
+ pa_assert(!m->current.memblock);
/* Append to the leftover memory block */
if (m->leftover.memblock) {
@@ -91,9 +91,10 @@ void pa_mcalign_push(pa_mcalign *m, const pa_memchunk *c) {
} else {
size_t l;
+ void *lo_data, *m_data;
/* We have to copy */
- assert(m->leftover.length < m->base);
+ pa_assert(m->leftover.length < m->base);
l = m->base - m->leftover.length;
if (l > c->length)
@@ -102,10 +103,15 @@ void pa_mcalign_push(pa_mcalign *m, const pa_memchunk *c) {
/* Can we use the current block? */
pa_memchunk_make_writable(&m->leftover, m->base);
- memcpy((uint8_t*) m->leftover.memblock->data + m->leftover.index + m->leftover.length, (uint8_t*) c->memblock->data + c->index, l);
+ lo_data = pa_memblock_acquire(m->leftover.memblock);
+ m_data = pa_memblock_acquire(c->memblock);
+ memcpy((uint8_t*) lo_data + m->leftover.index + m->leftover.length, (uint8_t*) m_data + c->index, l);
+ pa_memblock_release(m->leftover.memblock);
+ pa_memblock_release(c->memblock);
m->leftover.length += l;
- assert(m->leftover.length <= m->base && m->leftover.length <= m->leftover.memblock->length);
+ pa_assert(m->leftover.length <= m->base);
+ pa_assert(m->leftover.length <= pa_memblock_get_length(m->leftover.memblock));
if (c->length > l) {
/* Save the remainder of the memory block */
@@ -128,12 +134,13 @@ void pa_mcalign_push(pa_mcalign *m, const pa_memchunk *c) {
}
int pa_mcalign_pop(pa_mcalign *m, pa_memchunk *c) {
- assert(m);
- assert(c);
+ pa_assert(m);
+ pa_assert(c);
/* First test if there's a leftover memory block available */
if (m->leftover.memblock) {
- assert(m->leftover.length > 0 && m->leftover.length <= m->base);
+ pa_assert(m->leftover.length > 0);
+ pa_assert(m->leftover.length <= m->base);
/* The leftover memory block is not yet complete */
if (m->leftover.length < m->base)
@@ -155,13 +162,13 @@ int pa_mcalign_pop(pa_mcalign *m, pa_memchunk *c) {
/* Now let's see if there is other data available */
if (m->current.memblock) {
size_t l;
- assert(m->current.length >= m->base);
+ pa_assert(m->current.length >= m->base);
/* The length of the returned memory block */
l = m->current.length;
l /= m->base;
l *= m->base;
- assert(l > 0);
+ pa_assert(l > 0);
/* Prepare the returned block */
*c = m->current;
@@ -169,7 +176,7 @@ int pa_mcalign_pop(pa_mcalign *m, pa_memchunk *c) {
c->length = l;
/* Drop that from the current memory block */
- assert(l <= m->current.length);
+ pa_assert(l <= m->current.length);
m->current.index += l;
m->current.length -= l;
@@ -178,7 +185,7 @@ int pa_mcalign_pop(pa_mcalign *m, pa_memchunk *c) {
pa_memblock_unref(m->current.memblock);
else {
/* Move the raimainder to leftover */
- assert(m->current.length < m->base && !m->leftover.memblock);
+ pa_assert(m->current.length < m->base && !m->leftover.memblock);
m->leftover = m->current;
}
@@ -194,10 +201,10 @@ int pa_mcalign_pop(pa_mcalign *m, pa_memchunk *c) {
}
size_t pa_mcalign_csize(pa_mcalign *m, size_t l) {
- assert(m);
- assert(l > 0);
+ pa_assert(m);
+ pa_assert(l > 0);
- assert(!m->current.memblock);
+ pa_assert(!m->current.memblock);
if (m->leftover.memblock)
l += m->leftover.length;
diff --git a/src/pulsecore/memblock.c b/src/pulsecore/memblock.c
index 6f09a906..99b5a13f 100644
--- a/src/pulsecore/memblock.c
+++ b/src/pulsecore/memblock.c
@@ -14,7 +14,7 @@
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.
+ 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
@@ -28,15 +28,21 @@
#include <stdio.h>
#include <stdlib.h>
-#include <assert.h>
#include <string.h>
#include <unistd.h>
+#include <signal.h>
+#include <errno.h>
#include <pulse/xmalloc.h>
+#include <pulse/def.h>
#include <pulsecore/shm.h>
#include <pulsecore/log.h>
#include <pulsecore/hashmap.h>
+#include <pulsecore/semaphore.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/flist.h>
+#include <pulsecore/core-util.h>
#include "memblock.h"
@@ -48,6 +54,32 @@
#define PA_MEMIMPORT_SLOTS_MAX 128
#define PA_MEMIMPORT_SEGMENTS_MAX 16
+struct pa_memblock {
+ PA_REFCNT_DECLARE; /* the reference counter */
+ pa_mempool *pool;
+
+ pa_memblock_type_t type;
+ int read_only; /* boolean */
+
+ pa_atomic_ptr_t data;
+ size_t length;
+
+ pa_atomic_t n_acquired;
+ pa_atomic_t please_signal;
+
+ union {
+ struct {
+ /* If type == PA_MEMBLOCK_USER this points to a function for freeing this memory block */
+ pa_free_cb_t free_cb;
+ } user;
+
+ struct {
+ uint32_t id;
+ pa_memimport_segment *segment;
+ } imported;
+ } per_type;
+};
+
struct pa_memimport_segment {
pa_memimport *import;
pa_shm memory;
@@ -55,6 +87,8 @@ struct pa_memimport_segment {
};
struct pa_memimport {
+ pa_mutex *mutex;
+
pa_mempool *pool;
pa_hashmap *segments;
pa_hashmap *blocks;
@@ -73,9 +107,11 @@ struct memexport_slot {
};
struct pa_memexport {
+ pa_mutex *mutex;
pa_mempool *pool;
struct memexport_slot slots[PA_MEMEXPORT_SLOTS_MAX];
+
PA_LLIST_HEAD(struct memexport_slot, free_slots);
PA_LLIST_HEAD(struct memexport_slot, used_slots);
unsigned n_init;
@@ -95,24 +131,32 @@ struct mempool_slot {
};
struct pa_mempool {
+ pa_semaphore *semaphore;
+ pa_mutex *mutex;
+
pa_shm memory;
size_t block_size;
- unsigned n_blocks, n_init;
+ unsigned n_blocks;
+
+ pa_atomic_t n_init;
PA_LLIST_HEAD(pa_memimport, imports);
PA_LLIST_HEAD(pa_memexport, exports);
/* A list of free slots that may be reused */
- PA_LLIST_HEAD(struct mempool_slot, free_slots);
+ pa_flist *free_slots;
pa_mempool_stat stat;
};
static void segment_detach(pa_memimport_segment *seg);
+PA_STATIC_FLIST_DECLARE(unused_memblocks, 0, pa_xfree);
+
+/* No lock necessary */
static void stat_add(pa_memblock*b) {
- assert(b);
- assert(b->pool);
+ pa_assert(b);
+ pa_assert(b->pool);
pa_atomic_inc(&b->pool->stat.n_allocated);
pa_atomic_add(&b->pool->stat.allocated_size, b->length);
@@ -129,19 +173,20 @@ static void stat_add(pa_memblock*b) {
pa_atomic_inc(&b->pool->stat.n_accumulated_by_type[b->type]);
}
+/* No lock necessary */
static void stat_remove(pa_memblock *b) {
- assert(b);
- assert(b->pool);
+ pa_assert(b);
+ pa_assert(b->pool);
- assert(pa_atomic_load(&b->pool->stat.n_allocated) > 0);
- assert(pa_atomic_load(&b->pool->stat.allocated_size) >= (int) b->length);
+ pa_assert(pa_atomic_load(&b->pool->stat.n_allocated) > 0);
+ 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);
if (b->type == PA_MEMBLOCK_IMPORTED) {
- assert(pa_atomic_load(&b->pool->stat.n_imported) > 0);
- assert(pa_atomic_load(&b->pool->stat.imported_size) >= (int) b->length);
+ 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);
@@ -152,11 +197,12 @@ static void stat_remove(pa_memblock *b) {
static pa_memblock *memblock_new_appended(pa_mempool *p, size_t length);
+/* No lock necessary */
pa_memblock *pa_memblock_new(pa_mempool *p, size_t length) {
pa_memblock *b;
- assert(p);
- assert(length > 0);
+ pa_assert(p);
+ pa_assert(length > 0);
if (!(b = pa_memblock_new_pool(p, length)))
b = memblock_new_appended(p, length);
@@ -164,56 +210,75 @@ pa_memblock *pa_memblock_new(pa_mempool *p, size_t length) {
return b;
}
+/* No lock necessary */
static pa_memblock *memblock_new_appended(pa_mempool *p, size_t length) {
pa_memblock *b;
- assert(p);
- assert(length > 0);
+ pa_assert(p);
+ pa_assert(length > 0);
- b = pa_xmalloc(sizeof(pa_memblock) + 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));
+
+ 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;
- PA_REFCNT_INIT(b);
+ pa_atomic_ptr_store(&b->data, (uint8_t*) b + PA_ALIGN(sizeof(pa_memblock)));
b->length = length;
- b->data = (uint8_t*) b + sizeof(pa_memblock);
- b->pool = p;
+ pa_atomic_store(&b->n_acquired, 0);
+ pa_atomic_store(&b->please_signal, 0);
stat_add(b);
return b;
}
+/* No lock necessary */
static struct mempool_slot* mempool_allocate_slot(pa_mempool *p) {
struct mempool_slot *slot;
- assert(p);
+ pa_assert(p);
- if (p->free_slots) {
- slot = p->free_slots;
- PA_LLIST_REMOVE(struct mempool_slot, p->free_slots, slot);
- } else if (p->n_init < p->n_blocks)
- slot = (struct mempool_slot*) ((uint8_t*) p->memory.ptr + (p->block_size * p->n_init++));
- else {
- pa_log_debug("Pool full");
- pa_atomic_inc(&p->stat.n_pool_full);
- return NULL;
+ if (!(slot = pa_flist_pop(p->free_slots))) {
+ int idx;
+
+ /* The free list was empty, we have to allocate a new entry */
+
+ 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));
+
+ if (!slot) {
+ pa_log_debug("Pool full");
+ pa_atomic_inc(&p->stat.n_pool_full);
+ return NULL;
+ }
}
return slot;
}
+/* No lock necessary */
static void* mempool_slot_data(struct mempool_slot *slot) {
- assert(slot);
+ pa_assert(slot);
- return (uint8_t*) slot + sizeof(struct mempool_slot);
+ return (uint8_t*) slot + PA_ALIGN(sizeof(struct mempool_slot));
}
+/* No lock necessary */
static unsigned mempool_slot_idx(pa_mempool *p, void *ptr) {
- assert(p);
- assert((uint8_t*) ptr >= (uint8_t*) p->memory.ptr);
- assert((uint8_t*) ptr < (uint8_t*) p->memory.ptr + p->memory.size);
+ pa_assert(p);
+
+ 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;
}
+/* No lock necessary */
static struct mempool_slot* mempool_slot_by_ptr(pa_mempool *p, void *ptr) {
unsigned idx;
@@ -223,189 +288,321 @@ static struct mempool_slot* mempool_slot_by_ptr(pa_mempool *p, void *ptr) {
return (struct mempool_slot*) ((uint8_t*) p->memory.ptr + (idx * p->block_size));
}
+/* No lock necessary */
pa_memblock *pa_memblock_new_pool(pa_mempool *p, size_t length) {
pa_memblock *b = NULL;
struct mempool_slot *slot;
- assert(p);
- assert(length > 0);
+ pa_assert(p);
+ pa_assert(length > 0);
+
+ /* If -1 is passed as length we choose the size for the caller: we
+ * take the largest size that fits in one of our slots. */
- if (p->block_size - sizeof(struct mempool_slot) >= sizeof(pa_memblock) + 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 (!(slot = mempool_allocate_slot(p)))
return NULL;
b = mempool_slot_data(slot);
b->type = PA_MEMBLOCK_POOL;
- b->data = (uint8_t*) b + sizeof(pa_memblock);
+ pa_atomic_ptr_store(&b->data, (uint8_t*) b + PA_ALIGN(sizeof(pa_memblock)));
- } else if (p->block_size - sizeof(struct mempool_slot) >= length) {
+ } else if (p->block_size - PA_ALIGN(sizeof(struct mempool_slot)) >= length) {
if (!(slot = mempool_allocate_slot(p)))
return NULL;
- b = pa_xnew(pa_memblock, 1);
+ if (!(b = pa_flist_pop(PA_STATIC_FLIST_GET(unused_memblocks))))
+ b = pa_xnew(pa_memblock, 1);
+
b->type = PA_MEMBLOCK_POOL_EXTERNAL;
- b->data = mempool_slot_data(slot);
+ pa_atomic_ptr_store(&b->data, mempool_slot_data(slot));
+
} else {
- pa_log_debug("Memory block too large for pool: %u > %u", length, p->block_size - sizeof(struct mempool_slot));
+ 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_atomic_inc(&p->stat.n_too_large_for_pool);
return NULL;
}
- b->length = length;
- b->read_only = 0;
PA_REFCNT_INIT(b);
b->pool = p;
+ b->read_only = 0;
+ b->length = length;
+ pa_atomic_store(&b->n_acquired, 0);
+ pa_atomic_store(&b->please_signal, 0);
stat_add(b);
return b;
}
+/* No lock necessary */
pa_memblock *pa_memblock_new_fixed(pa_mempool *p, void *d, size_t length, int read_only) {
pa_memblock *b;
- assert(p);
- assert(d);
- assert(length > 0);
+ pa_assert(p);
+ pa_assert(d);
+ pa_assert(length != (size_t) -1);
+ pa_assert(length > 0);
- b = pa_xnew(pa_memblock, 1);
+ if (!(b = pa_flist_pop(PA_STATIC_FLIST_GET(unused_memblocks))))
+ b = pa_xnew(pa_memblock, 1);
+ PA_REFCNT_INIT(b);
+ b->pool = p;
b->type = PA_MEMBLOCK_FIXED;
b->read_only = read_only;
- PA_REFCNT_INIT(b);
+ pa_atomic_ptr_store(&b->data, d);
b->length = length;
- b->data = d;
- b->pool = p;
+ pa_atomic_store(&b->n_acquired, 0);
+ pa_atomic_store(&b->please_signal, 0);
stat_add(b);
return b;
}
+/* 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 *b;
- assert(p);
- assert(d);
- assert(length > 0);
- assert(free_cb);
+ pa_assert(p);
+ pa_assert(d);
+ pa_assert(length > 0);
+ pa_assert(length != (size_t) -1);
+ pa_assert(free_cb);
- b = pa_xnew(pa_memblock, 1);
+ if (!(b = pa_flist_pop(PA_STATIC_FLIST_GET(unused_memblocks))))
+ b = pa_xnew(pa_memblock, 1);
+ PA_REFCNT_INIT(b);
+ b->pool = p;
b->type = PA_MEMBLOCK_USER;
b->read_only = read_only;
- PA_REFCNT_INIT(b);
+ pa_atomic_ptr_store(&b->data, d);
b->length = length;
- b->data = d;
+ pa_atomic_store(&b->n_acquired, 0);
+ pa_atomic_store(&b->please_signal, 0);
+
b->per_type.user.free_cb = free_cb;
- b->pool = p;
stat_add(b);
return b;
}
+/* No lock necessary */
+int pa_memblock_is_read_only(pa_memblock *b) {
+ pa_assert(b);
+ pa_assert(PA_REFCNT_VALUE(b) > 0);
+
+ return b->read_only && PA_REFCNT_VALUE(b) == 1;
+}
+
+/* No lock necessary */
+int pa_memblock_ref_is_one(pa_memblock *b) {
+ int r;
+
+ pa_assert(b);
+
+ r = PA_REFCNT_VALUE(b);
+ pa_assert(r > 0);
+
+ return r == 1;
+}
+
+/* No lock necessary */
+void* pa_memblock_acquire(pa_memblock *b) {
+ pa_assert(b);
+ pa_assert(PA_REFCNT_VALUE(b) > 0);
+
+ pa_atomic_inc(&b->n_acquired);
+
+ return pa_atomic_ptr_load(&b->data);
+}
+
+/* No lock necessary, in corner cases locks by its own */
+void pa_memblock_release(pa_memblock *b) {
+ int r;
+ pa_assert(b);
+ pa_assert(PA_REFCNT_VALUE(b) > 0);
+
+ r = pa_atomic_dec(&b->n_acquired);
+ pa_assert(r >= 1);
+
+ /* Signal a waiting thread that this memblock is no longer used */
+ if (r == 1 && pa_atomic_load(&b->please_signal))
+ pa_semaphore_post(b->pool->semaphore);
+}
+
+size_t pa_memblock_get_length(pa_memblock *b) {
+ pa_assert(b);
+ pa_assert(PA_REFCNT_VALUE(b) > 0);
+
+ return b->length;
+}
+
+pa_mempool* pa_memblock_get_pool(pa_memblock *b) {
+ pa_assert(b);
+ pa_assert(PA_REFCNT_VALUE(b) > 0);
+
+ return b->pool;
+}
+
+/* No lock necessary */
pa_memblock* pa_memblock_ref(pa_memblock*b) {
- assert(b);
- assert(PA_REFCNT_VALUE(b) > 0);
+ pa_assert(b);
+ pa_assert(PA_REFCNT_VALUE(b) > 0);
PA_REFCNT_INC(b);
return b;
}
-void pa_memblock_unref(pa_memblock*b) {
- assert(b);
- assert(PA_REFCNT_VALUE(b) > 0);
+static void memblock_free(pa_memblock *b) {
+ pa_assert(b);
- if (PA_REFCNT_DEC(b) > 0)
- return;
+ pa_assert(pa_atomic_load(&b->n_acquired) == 0);
stat_remove(b);
switch (b->type) {
case PA_MEMBLOCK_USER :
- assert(b->per_type.user.free_cb);
- b->per_type.user.free_cb(b->data);
+ pa_assert(b->per_type.user.free_cb);
+ b->per_type.user.free_cb(pa_atomic_ptr_load(&b->data));
/* Fall through */
case PA_MEMBLOCK_FIXED:
case PA_MEMBLOCK_APPENDED :
- pa_xfree(b);
+ if (pa_flist_push(PA_STATIC_FLIST_GET(unused_memblocks), b) < 0)
+ pa_xfree(b);
+
break;
case PA_MEMBLOCK_IMPORTED : {
pa_memimport_segment *segment;
+ pa_memimport *import;
- segment = b->per_type.imported.segment;
- assert(segment);
- assert(segment->import);
+ /* FIXME! This should be implemented lock-free */
- pa_hashmap_remove(segment->import->blocks, PA_UINT32_TO_PTR(b->per_type.imported.id));
- segment->import->release_cb(segment->import, b->per_type.imported.id, segment->import->userdata);
+ segment = b->per_type.imported.segment;
+ pa_assert(segment);
+ import = segment->import;
+ pa_assert(import);
+ pa_mutex_lock(import->mutex);
+ pa_hashmap_remove(import->blocks, PA_UINT32_TO_PTR(b->per_type.imported.id));
if (-- segment->n_blocks <= 0)
segment_detach(segment);
- pa_xfree(b);
+ pa_mutex_unlock(import->mutex);
+
+ import->release_cb(import, b->per_type.imported.id, import->userdata);
+
+ if (pa_flist_push(PA_STATIC_FLIST_GET(unused_memblocks), b) < 0)
+ pa_xfree(b);
break;
}
case PA_MEMBLOCK_POOL_EXTERNAL:
case PA_MEMBLOCK_POOL: {
struct mempool_slot *slot;
+ int call_free;
- slot = mempool_slot_by_ptr(b->pool, b->data);
- assert(slot);
+ slot = mempool_slot_by_ptr(b->pool, pa_atomic_ptr_load(&b->data));
+ pa_assert(slot);
- PA_LLIST_PREPEND(struct mempool_slot, b->pool->free_slots, slot);
+ call_free = b->type == PA_MEMBLOCK_POOL_EXTERNAL;
- if (b->type == PA_MEMBLOCK_POOL_EXTERNAL)
- pa_xfree(b);
+ /* The free list dimensions should easily allow all slots
+ * to fit in, hence try harder if pushing this slot into
+ * the free list fails */
+ while (pa_flist_push(b->pool->free_slots, slot) < 0)
+ ;
+
+ if (call_free)
+ if (pa_flist_push(PA_STATIC_FLIST_GET(unused_memblocks), b) < 0)
+ pa_xfree(b);
break;
}
case PA_MEMBLOCK_TYPE_MAX:
default:
- abort();
+ pa_assert_not_reached();
}
}
+/* No lock necessary */
+void pa_memblock_unref(pa_memblock*b) {
+ pa_assert(b);
+ pa_assert(PA_REFCNT_VALUE(b) > 0);
+
+ if (PA_REFCNT_DEC(b) > 0)
+ return;
+
+ memblock_free(b);
+}
+
+/* Self locked */
+static void memblock_wait(pa_memblock *b) {
+ pa_assert(b);
+
+ if (pa_atomic_load(&b->n_acquired) > 0) {
+ /* We need to wait until all threads gave up access to the
+ * memory block before we can go on. Unfortunately this means
+ * that we have to lock and wait here. Sniff! */
+
+ pa_atomic_inc(&b->please_signal);
+
+ while (pa_atomic_load(&b->n_acquired) > 0)
+ pa_semaphore_wait(b->pool->semaphore);
+
+ pa_atomic_dec(&b->please_signal);
+ }
+}
+
+/* No lock necessary. This function is not multiple caller safe! */
static void memblock_make_local(pa_memblock *b) {
- assert(b);
+ pa_assert(b);
pa_atomic_dec(&b->pool->stat.n_allocated_by_type[b->type]);
- if (b->length <= b->pool->block_size - sizeof(struct mempool_slot)) {
+ if (b->length <= b->pool->block_size - PA_ALIGN(sizeof(struct mempool_slot))) {
struct mempool_slot *slot;
if ((slot = mempool_allocate_slot(b->pool))) {
void *new_data;
/* We can move it into a local pool, perfect! */
+ new_data = mempool_slot_data(slot);
+ memcpy(new_data, pa_atomic_ptr_load(&b->data), b->length);
+ pa_atomic_ptr_store(&b->data, new_data);
+
b->type = PA_MEMBLOCK_POOL_EXTERNAL;
b->read_only = 0;
- new_data = mempool_slot_data(slot);
- memcpy(new_data, b->data, b->length);
- b->data = new_data;
goto finish;
}
}
/* Humm, not enough space in the pool, so lets allocate the memory with malloc() */
- b->type = PA_MEMBLOCK_USER;
b->per_type.user.free_cb = pa_xfree;
+ 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->data = pa_xmemdup(b->data, b->length);
finish:
pa_atomic_inc(&b->pool->stat.n_allocated_by_type[b->type]);
pa_atomic_inc(&b->pool->stat.n_accumulated_by_type[b->type]);
+ memblock_wait(b);
}
+/* No lock necessary. This function is not multiple caller safe*/
void pa_memblock_unref_fixed(pa_memblock *b) {
- assert(b);
- assert(PA_REFCNT_VALUE(b) > 0);
- assert(b->type == PA_MEMBLOCK_FIXED);
+ pa_assert(b);
+ pa_assert(PA_REFCNT_VALUE(b) > 0);
+ pa_assert(b->type == PA_MEMBLOCK_FIXED);
if (PA_REFCNT_VALUE(b) > 1)
memblock_make_local(b);
@@ -413,20 +610,37 @@ void pa_memblock_unref_fixed(pa_memblock *b) {
pa_memblock_unref(b);
}
+/* No lock necessary. */
+pa_memblock *pa_memblock_will_need(pa_memblock *b) {
+ void *p;
+
+ pa_assert(b);
+ pa_assert(PA_REFCNT_VALUE(b) > 0);
+
+ p = pa_memblock_acquire(b);
+ pa_will_need(p, b->length);
+ pa_memblock_release(b);
+
+ return b;
+}
+
+/* Self-locked. This function is not multiple-caller safe */
static void memblock_replace_import(pa_memblock *b) {
pa_memimport_segment *seg;
- assert(b);
- assert(b->type == PA_MEMBLOCK_IMPORTED);
+ pa_assert(b);
+ pa_assert(b->type == PA_MEMBLOCK_IMPORTED);
- assert(pa_atomic_load(&b->pool->stat.n_imported) > 0);
- assert(pa_atomic_load(&b->pool->stat.imported_size) >= (int) b->length);
+ 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);
seg = b->per_type.imported.segment;
- assert(seg);
- assert(seg->import);
+ pa_assert(seg);
+ pa_assert(seg->import);
+
+ pa_mutex_lock(seg->import->mutex);
pa_hashmap_remove(
seg->import->blocks,
@@ -434,51 +648,49 @@ static void memblock_replace_import(pa_memblock *b) {
memblock_make_local(b);
- if (-- seg->n_blocks <= 0)
+ if (-- seg->n_blocks <= 0) {
+ pa_mutex_unlock(seg->import->mutex);
segment_detach(seg);
+ } else
+ pa_mutex_unlock(seg->import->mutex);
}
pa_mempool* pa_mempool_new(int shared) {
- size_t ps;
pa_mempool *p;
p = pa_xnew(pa_mempool, 1);
-#ifdef HAVE_SYSCONF
- ps = (size_t) sysconf(_SC_PAGESIZE);
-#elif defined(PAGE_SIZE)
- ps = (size_t) PAGE_SIZE;
-#else
- ps = 4096; /* Let's hope it's like x86. */
-#endif
-
- p->block_size = (PA_MEMPOOL_SLOT_SIZE/ps)*ps;
+ p->mutex = pa_mutex_new(TRUE, TRUE);
+ p->semaphore = pa_semaphore_new(0);
- if (p->block_size < ps)
- p->block_size = ps;
+ p->block_size = PA_PAGE_ALIGN(PA_MEMPOOL_SLOT_SIZE);
+ if (p->block_size < PA_PAGE_SIZE)
+ p->block_size = PA_PAGE_SIZE;
p->n_blocks = PA_MEMPOOL_SLOTS_MAX;
- assert(p->block_size > sizeof(struct mempool_slot));
+ pa_assert(p->block_size > PA_ALIGN(sizeof(struct mempool_slot)));
if (pa_shm_create_rw(&p->memory, p->n_blocks * p->block_size, shared, 0700) < 0) {
pa_xfree(p);
return NULL;
}
- p->n_init = 0;
+ 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);
- PA_LLIST_HEAD_INIT(struct mempool_slot, p->free_slots);
- memset(&p->stat, 0, sizeof(p->stat));
+ p->free_slots = pa_flist_new(p->n_blocks*2);
return p;
}
void pa_mempool_free(pa_mempool *p) {
- assert(p);
+ pa_assert(p);
+
+ pa_mutex_lock(p->mutex);
while (p->imports)
pa_memimport_free(p->imports);
@@ -486,30 +698,65 @@ void pa_mempool_free(pa_mempool *p) {
while (p->exports)
pa_memexport_free(p->exports);
- if (pa_atomic_load(&p->stat.n_allocated) > 0)
- pa_log_warn("WARNING! Memory pool destroyed but not all memory blocks freed!");
+ pa_mutex_unlock(p->mutex);
+
+ pa_flist_free(p->free_slots, NULL);
+
+ if (pa_atomic_load(&p->stat.n_allocated) > 0) {
+/* raise(SIGTRAP); */
+ pa_log_warn("Memory pool destroyed but not all memory blocks freed! %u remain.", pa_atomic_load(&p->stat.n_allocated));
+ }
pa_shm_free(&p->memory);
+
+ pa_mutex_free(p->mutex);
+ pa_semaphore_free(p->semaphore);
+
pa_xfree(p);
}
+/* No lock necessary */
const pa_mempool_stat* pa_mempool_get_stat(pa_mempool *p) {
- assert(p);
+ pa_assert(p);
return &p->stat;
}
+/* No lock necessary */
+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));
+}
+
+/* No lock necessary */
void pa_mempool_vacuum(pa_mempool *p) {
struct mempool_slot *slot;
+ pa_flist *list;
+
+ pa_assert(p);
+
+ list = pa_flist_new(p->n_blocks*2);
- assert(p);
+ while ((slot = pa_flist_pop(p->free_slots)))
+ while (pa_flist_push(list, slot) < 0)
+ ;
- for (slot = p->free_slots; slot; slot = slot->next)
- pa_shm_punch(&p->memory, (uint8_t*) slot + sizeof(struct mempool_slot) - (uint8_t*) p->memory.ptr, p->block_size - sizeof(struct mempool_slot));
+ 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)));
+
+ while (pa_flist_push(p->free_slots, slot))
+ ;
+ }
+
+ pa_flist_free(list, NULL);
}
+/* No lock necessary */
int pa_mempool_get_shm_id(pa_mempool *p, uint32_t *id) {
- assert(p);
+ pa_assert(p);
if (!p->memory.shared)
return -1;
@@ -519,8 +766,9 @@ int pa_mempool_get_shm_id(pa_mempool *p, uint32_t *id) {
return 0;
}
+/* No lock necessary */
int pa_mempool_is_shared(pa_mempool *p) {
- assert(p);
+ pa_assert(p);
return !!p->memory.shared;
}
@@ -529,22 +777,27 @@ int pa_mempool_is_shared(pa_mempool *p) {
pa_memimport* pa_memimport_new(pa_mempool *p, pa_memimport_release_cb_t cb, void *userdata) {
pa_memimport *i;
- assert(p);
- assert(cb);
+ pa_assert(p);
+ pa_assert(cb);
i = pa_xnew(pa_memimport, 1);
+ i->mutex = pa_mutex_new(TRUE, TRUE);
i->pool = p;
i->segments = pa_hashmap_new(NULL, NULL);
i->blocks = pa_hashmap_new(NULL, NULL);
i->release_cb = cb;
i->userdata = userdata;
+ pa_mutex_lock(p->mutex);
PA_LLIST_PREPEND(pa_memimport, p->imports, i);
+ pa_mutex_unlock(p->mutex);
+
return i;
}
static void memexport_revoke_blocks(pa_memexport *e, pa_memimport *i);
+/* Should be called locked */
static pa_memimport_segment* segment_attach(pa_memimport *i, uint32_t shm_id) {
pa_memimport_segment* seg;
@@ -565,59 +818,79 @@ static pa_memimport_segment* segment_attach(pa_memimport *i, uint32_t shm_id) {
return seg;
}
+/* Should be called locked */
static void segment_detach(pa_memimport_segment *seg) {
- assert(seg);
+ pa_assert(seg);
pa_hashmap_remove(seg->import->segments, PA_UINT32_TO_PTR(seg->memory.id));
pa_shm_free(&seg->memory);
pa_xfree(seg);
}
+/* Self-locked. Not multiple-caller safe */
void pa_memimport_free(pa_memimport *i) {
pa_memexport *e;
pa_memblock *b;
- assert(i);
+ pa_assert(i);
+
+ pa_mutex_lock(i->mutex);
+
+ while ((b = pa_hashmap_get_first(i->blocks)))
+ memblock_replace_import(b);
+
+ pa_assert(pa_hashmap_size(i->segments) == 0);
+
+ pa_mutex_unlock(i->mutex);
+
+ pa_mutex_lock(i->pool->mutex);
/* If we've exported this block further we need to revoke that export */
for (e = i->pool->exports; e; e = e->next)
memexport_revoke_blocks(e, i);
- while ((b = pa_hashmap_get_first(i->blocks)))
- memblock_replace_import(b);
+ PA_LLIST_REMOVE(pa_memimport, i->pool->imports, i);
- assert(pa_hashmap_size(i->segments) == 0);
+ pa_mutex_unlock(i->pool->mutex);
pa_hashmap_free(i->blocks, NULL, NULL);
pa_hashmap_free(i->segments, NULL, NULL);
- PA_LLIST_REMOVE(pa_memimport, i->pool->imports, i);
+ pa_mutex_free(i->mutex);
+
pa_xfree(i);
}
+/* Self-locked */
pa_memblock* pa_memimport_get(pa_memimport *i, uint32_t block_id, uint32_t shm_id, size_t offset, size_t size) {
- pa_memblock *b;
+ pa_memblock *b = NULL;
pa_memimport_segment *seg;
- assert(i);
+ pa_assert(i);
+
+ pa_mutex_lock(i->mutex);
if (pa_hashmap_size(i->blocks) >= PA_MEMIMPORT_SLOTS_MAX)
- return NULL;
+ goto finish;
if (!(seg = pa_hashmap_get(i->segments, PA_UINT32_TO_PTR(shm_id))))
if (!(seg = segment_attach(i, shm_id)))
- return NULL;
+ goto finish;
if (offset+size > seg->memory.size)
- return NULL;
+ goto finish;
- b = pa_xnew(pa_memblock, 1);
+ if (!(b = pa_flist_pop(PA_STATIC_FLIST_GET(unused_memblocks))))
+ b = pa_xnew(pa_memblock, 1);
+
+ PA_REFCNT_INIT(b);
+ b->pool = i->pool;
b->type = PA_MEMBLOCK_IMPORTED;
b->read_only = 1;
- PA_REFCNT_INIT(b);
+ pa_atomic_ptr_store(&b->data, (uint8_t*) seg->memory.ptr + offset);
b->length = size;
- b->data = (uint8_t*) seg->memory.ptr + offset;
- b->pool = i->pool;
+ pa_atomic_store(&b->n_acquired, 0);
+ pa_atomic_store(&b->please_signal, 0);
b->per_type.imported.id = block_id;
b->per_type.imported.segment = seg;
@@ -625,6 +898,10 @@ pa_memblock* pa_memimport_get(pa_memimport *i, uint32_t block_id, uint32_t shm_i
seg->n_blocks++;
+finish:
+ pa_mutex_unlock(i->mutex);
+
+ if (b)
stat_add(b);
return b;
@@ -632,26 +909,36 @@ pa_memblock* pa_memimport_get(pa_memimport *i, uint32_t block_id, uint32_t shm_i
int pa_memimport_process_revoke(pa_memimport *i, uint32_t id) {
pa_memblock *b;
- assert(i);
+ int ret = 0;
+ pa_assert(i);
- if (!(b = pa_hashmap_get(i->blocks, PA_UINT32_TO_PTR(id))))
- return -1;
+ pa_mutex_lock(i->mutex);
+
+ if (!(b = pa_hashmap_get(i->blocks, PA_UINT32_TO_PTR(id)))) {
+ ret = -1;
+ goto finish;
+ }
memblock_replace_import(b);
- return 0;
+
+finish:
+ pa_mutex_unlock(i->mutex);
+
+ return ret;
}
/* For sending blocks to other nodes */
pa_memexport* pa_memexport_new(pa_mempool *p, pa_memexport_revoke_cb_t cb, void *userdata) {
pa_memexport *e;
- assert(p);
- assert(cb);
+ pa_assert(p);
+ pa_assert(cb);
if (!p->memory.shared)
return NULL;
e = pa_xnew(pa_memexport, 1);
+ e->mutex = pa_mutex_new(TRUE, TRUE);
e->pool = p;
PA_LLIST_HEAD_INIT(struct memexport_slot, e->free_slots);
PA_LLIST_HEAD_INIT(struct memexport_slot, e->used_slots);
@@ -659,50 +946,75 @@ pa_memexport* pa_memexport_new(pa_mempool *p, pa_memexport_revoke_cb_t cb, void
e->revoke_cb = cb;
e->userdata = userdata;
+ pa_mutex_lock(p->mutex);
PA_LLIST_PREPEND(pa_memexport, p->exports, e);
+ pa_mutex_unlock(p->mutex);
return e;
}
void pa_memexport_free(pa_memexport *e) {
- assert(e);
+ pa_assert(e);
+ pa_mutex_lock(e->mutex);
while (e->used_slots)
pa_memexport_process_release(e, e->used_slots - e->slots);
+ pa_mutex_unlock(e->mutex);
+ pa_mutex_lock(e->pool->mutex);
PA_LLIST_REMOVE(pa_memexport, e->pool->exports, e);
+ pa_mutex_unlock(e->pool->mutex);
+
+ pa_mutex_free(e->mutex);
pa_xfree(e);
}
+/* Self-locked */
int pa_memexport_process_release(pa_memexport *e, uint32_t id) {
- assert(e);
+ pa_memblock *b;
+
+ pa_assert(e);
+
+ pa_mutex_lock(e->mutex);
if (id >= e->n_init)
- return -1;
+ goto fail;
if (!e->slots[id].block)
- return -1;
+ goto fail;
+
+ b = e->slots[id].block;
+ e->slots[id].block = NULL;
+
+ PA_LLIST_REMOVE(struct memexport_slot, e->used_slots, &e->slots[id]);
+ PA_LLIST_PREPEND(struct memexport_slot, e->free_slots, &e->slots[id]);
+
+ pa_mutex_unlock(e->mutex);
/* pa_log("Processing release for %u", id); */
- assert(pa_atomic_load(&e->pool->stat.n_exported) > 0);
- assert(pa_atomic_load(&e->pool->stat.exported_size) >= (int) e->slots[id].block->length);
+ pa_assert(pa_atomic_load(&e->pool->stat.n_exported) > 0);
+ 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, e->slots[id].block->length);
+ pa_atomic_sub(&e->pool->stat.exported_size, b->length);
- pa_memblock_unref(e->slots[id].block);
- e->slots[id].block = NULL;
-
- PA_LLIST_REMOVE(struct memexport_slot, e->used_slots, &e->slots[id]);
- PA_LLIST_PREPEND(struct memexport_slot, e->free_slots, &e->slots[id]);
+ pa_memblock_unref(b);
return 0;
+
+fail:
+ pa_mutex_unlock(e->mutex);
+
+ return -1;
}
+/* Self-locked */
static void memexport_revoke_blocks(pa_memexport *e, pa_memimport *i) {
struct memexport_slot *slot, *next;
- assert(e);
- assert(i);
+ pa_assert(e);
+ pa_assert(i);
+
+ pa_mutex_lock(e->mutex);
for (slot = e->used_slots; slot; slot = next) {
uint32_t idx;
@@ -716,49 +1028,57 @@ static void memexport_revoke_blocks(pa_memexport *e, pa_memimport *i) {
e->revoke_cb(e, idx, e->userdata);
pa_memexport_process_release(e, idx);
}
+
+ pa_mutex_unlock(e->mutex);
}
+/* No lock necessary */
static pa_memblock *memblock_shared_copy(pa_mempool *p, pa_memblock *b) {
pa_memblock *n;
- assert(p);
- assert(b);
+ pa_assert(p);
+ pa_assert(b);
if (b->type == PA_MEMBLOCK_IMPORTED ||
b->type == PA_MEMBLOCK_POOL ||
b->type == PA_MEMBLOCK_POOL_EXTERNAL) {
- assert(b->pool == p);
+ pa_assert(b->pool == p);
return pa_memblock_ref(b);
}
if (!(n = pa_memblock_new_pool(p, b->length)))
return NULL;
- memcpy(n->data, b->data, b->length);
+ memcpy(pa_atomic_ptr_load(&n->data), pa_atomic_ptr_load(&b->data), b->length);
return n;
}
+/* Self-locked */
int pa_memexport_put(pa_memexport *e, pa_memblock *b, uint32_t *block_id, uint32_t *shm_id, size_t *offset, size_t * size) {
pa_shm *memory;
struct memexport_slot *slot;
+ void *data;
- assert(e);
- assert(b);
- assert(block_id);
- assert(shm_id);
- assert(offset);
- assert(size);
- assert(b->pool == e->pool);
+ pa_assert(e);
+ pa_assert(b);
+ pa_assert(block_id);
+ pa_assert(shm_id);
+ pa_assert(offset);
+ pa_assert(size);
+ pa_assert(b->pool == e->pool);
if (!(b = memblock_shared_copy(e->pool, b)))
return -1;
+ pa_mutex_lock(e->mutex);
+
if (e->free_slots) {
slot = e->free_slots;
PA_LLIST_REMOVE(struct memexport_slot, e->free_slots, slot);
- } else if (e->n_init < PA_MEMEXPORT_SLOTS_MAX) {
+ } else if (e->n_init < PA_MEMEXPORT_SLOTS_MAX)
slot = &e->slots[e->n_init++];
- } else {
+ else {
+ pa_mutex_unlock(e->mutex);
pa_memblock_unref(b);
return -1;
}
@@ -767,24 +1087,29 @@ int pa_memexport_put(pa_memexport *e, pa_memblock *b, uint32_t *block_id, uint32
slot->block = b;
*block_id = slot - e->slots;
+ pa_mutex_unlock(e->mutex);
/* pa_log("Got block id %u", *block_id); */
+ data = pa_memblock_acquire(b);
+
if (b->type == PA_MEMBLOCK_IMPORTED) {
- assert(b->per_type.imported.segment);
+ pa_assert(b->per_type.imported.segment);
memory = &b->per_type.imported.segment->memory;
} else {
- assert(b->type == PA_MEMBLOCK_POOL || b->type == PA_MEMBLOCK_POOL_EXTERNAL);
- assert(b->pool);
+ pa_assert(b->type == PA_MEMBLOCK_POOL || b->type == PA_MEMBLOCK_POOL_EXTERNAL);
+ pa_assert(b->pool);
memory = &b->pool->memory;
}
- assert(b->data >= memory->ptr);
- assert((uint8_t*) b->data + b->length <= (uint8_t*) memory->ptr + memory->size);
+ pa_assert(data >= memory->ptr);
+ pa_assert((uint8_t*) data + b->length <= (uint8_t*) memory->ptr + memory->size);
*shm_id = memory->id;
- *offset = (uint8_t*) b->data - (uint8_t*) memory->ptr;
+ *offset = (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);
diff --git a/src/pulsecore/memblock.h b/src/pulsecore/memblock.h
index fe4773d4..c704014a 100644
--- a/src/pulsecore/memblock.h
+++ b/src/pulsecore/memblock.h
@@ -28,6 +28,7 @@
#include <sys/types.h>
#include <inttypes.h>
+#include <pulse/def.h>
#include <pulsecore/llist.h>
#include <pulsecore/refcnt.h>
#include <pulsecore/atomic.h>
@@ -58,45 +59,25 @@ typedef struct pa_memexport pa_memexport;
typedef void (*pa_memimport_release_cb_t)(pa_memimport *i, uint32_t block_id, void *userdata);
typedef void (*pa_memexport_revoke_cb_t)(pa_memexport *e, uint32_t block_id, void *userdata);
-struct pa_memblock {
- pa_memblock_type_t type;
- int read_only; /* boolean */
- PA_REFCNT_DECLARE; /* the reference counter */
- size_t length;
- void *data;
- pa_mempool *pool;
-
- union {
- struct {
- void (*free_cb)(void *p); /* If type == PA_MEMBLOCK_USER this points to a function for freeing this memory block */
- } user;
-
- struct {
- uint32_t id;
- pa_memimport_segment *segment;
- } imported;
- } per_type;
-};
-
/* Please note that updates to this structure are not locked,
* i.e. n_allocated might be updated at a point in time where
* n_accumulated is not yet. Take these values with a grain of salt,
- * threy are here for purely statistical reasons.*/
+ * they are here for purely statistical reasons.*/
struct pa_mempool_stat {
- pa_atomic_int_t n_allocated;
- pa_atomic_int_t n_accumulated;
- pa_atomic_int_t n_imported;
- pa_atomic_int_t n_exported;
- pa_atomic_int_t allocated_size;
- pa_atomic_int_t accumulated_size;
- pa_atomic_int_t imported_size;
- pa_atomic_int_t exported_size;
-
- pa_atomic_int_t n_too_large_for_pool;
- pa_atomic_int_t n_pool_full;
-
- pa_atomic_int_t n_allocated_by_type[PA_MEMBLOCK_TYPE_MAX];
- pa_atomic_int_t n_accumulated_by_type[PA_MEMBLOCK_TYPE_MAX];
+ pa_atomic_t n_allocated;
+ pa_atomic_t n_accumulated;
+ pa_atomic_t n_imported;
+ pa_atomic_t n_exported;
+ pa_atomic_t allocated_size;
+ pa_atomic_t accumulated_size;
+ pa_atomic_t imported_size;
+ pa_atomic_t exported_size;
+
+ pa_atomic_t n_too_large_for_pool;
+ pa_atomic_t n_pool_full;
+
+ pa_atomic_t n_allocated_by_type[PA_MEMBLOCK_TYPE_MAX];
+ pa_atomic_t n_accumulated_by_type[PA_MEMBLOCK_TYPE_MAX];
};
/* Allocate a new memory block of type PA_MEMBLOCK_MEMPOOL or PA_MEMBLOCK_APPENDED, depending on the size */
@@ -120,9 +101,20 @@ pa_memblock* pa_memblock_ref(pa_memblock*b);
/* This special unref function has to be called by the owner of the
memory of a static memory block when he wants to release all
references to the memory. This causes the memory to be copied and
-converted into a PA_MEMBLOCK_DYNAMIC type memory block */
+converted into a pool or malloc'ed memory block. Please note that this
+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);
+void* pa_memblock_acquire(pa_memblock *b);
+void pa_memblock_release(pa_memblock *b);
+size_t pa_memblock_get_length(pa_memblock *b);
+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);
void pa_mempool_free(pa_mempool *p);
@@ -130,6 +122,7 @@ 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);
+size_t pa_mempool_block_size_max(pa_mempool *p);
/* For recieving blocks from other nodes */
pa_memimport* pa_memimport_new(pa_mempool *p, pa_memimport_release_cb_t cb, void *userdata);
diff --git a/src/pulsecore/memblockq.c b/src/pulsecore/memblockq.c
index e31fb6df..a46155a9 100644
--- a/src/pulsecore/memblockq.c
+++ b/src/pulsecore/memblockq.c
@@ -28,7 +28,6 @@
#include <sys/time.h>
#include <time.h>
#include <stdio.h>
-#include <assert.h>
#include <stdlib.h>
#include <string.h>
@@ -36,23 +35,29 @@
#include <pulsecore/log.h>
#include <pulsecore/mcalign.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/flist.h>
#include "memblockq.h"
-struct memblock_list {
- struct memblock_list *next, *prev;
+struct list_item {
+ struct list_item *next, *prev;
int64_t index;
pa_memchunk chunk;
};
+PA_STATIC_FLIST_DECLARE(list_items, 0, pa_xfree);
+
struct pa_memblockq {
- struct memblock_list *blocks, *blocks_tail;
+ struct list_item *blocks, *blocks_tail;
unsigned n_blocks;
size_t maxlength, tlength, base, prebuf, minreq;
int64_t read_index, write_index;
- enum { PREBUF, RUNNING } state;
+ pa_bool_t in_prebuf;
pa_memblock *silence;
pa_mcalign *mcalign;
+ int64_t missing;
+ size_t requested;
};
pa_memblockq* pa_memblockq_new(
@@ -66,8 +71,8 @@ pa_memblockq* pa_memblockq_new(
pa_memblockq* bq;
- assert(base > 0);
- assert(maxlength >= base);
+ pa_assert(base > 0);
+ pa_assert(maxlength >= base);
bq = pa_xnew(pa_memblockq, 1);
bq->blocks = bq->blocks_tail = NULL;
@@ -77,13 +82,13 @@ pa_memblockq* pa_memblockq_new(
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);
+ (unsigned long) maxlength, (unsigned long) tlength, (unsigned long) base, (unsigned long) prebuf, (unsigned long) minreq);
bq->maxlength = ((maxlength+base-1)/base)*base;
- assert(bq->maxlength >= base);
+ pa_assert(bq->maxlength >= base);
bq->tlength = ((tlength+base-1)/base)*base;
- if (!bq->tlength || bq->tlength >= bq->maxlength)
+ if (bq->tlength <= 0 || bq->tlength > bq->maxlength)
bq->tlength = bq->maxlength;
bq->prebuf = (prebuf == (size_t) -1) ? bq->tlength/2 : prebuf;
@@ -102,15 +107,18 @@ pa_memblockq* pa_memblockq_new(
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);
- bq->state = bq->prebuf ? PREBUF : RUNNING;
+ bq->in_prebuf = bq->prebuf > 0;
bq->silence = silence ? pa_memblock_ref(silence) : NULL;
bq->mcalign = NULL;
+ bq->missing = bq->tlength;
+ bq->requested = 0;
+
return bq;
}
void pa_memblockq_free(pa_memblockq* bq) {
- assert(bq);
+ pa_assert(bq);
pa_memblockq_flush(bq);
@@ -123,11 +131,11 @@ void pa_memblockq_free(pa_memblockq* bq) {
pa_xfree(bq);
}
-static void drop_block(pa_memblockq *bq, struct memblock_list *q) {
- assert(bq);
- assert(q);
+static void drop_block(pa_memblockq *bq, struct list_item *q) {
+ pa_assert(bq);
+ pa_assert(q);
- assert(bq->n_blocks >= 1);
+ pa_assert(bq->n_blocks >= 1);
if (q->prev)
q->prev->next = q->next;
@@ -140,15 +148,17 @@ static void drop_block(pa_memblockq *bq, struct memblock_list *q) {
bq->blocks_tail = q->prev;
pa_memblock_unref(q->chunk.memblock);
- pa_xfree(q);
+
+ if (pa_flist_push(PA_STATIC_FLIST_GET(list_items), q) < 0)
+ pa_xfree(q);
bq->n_blocks--;
}
-static int can_push(pa_memblockq *bq, size_t l) {
+static pa_bool_t can_push(pa_memblockq *bq, size_t l) {
int64_t end;
- assert(bq);
+ pa_assert(bq);
if (bq->read_index > bq->write_index) {
size_t d = bq->read_index - bq->write_index;
@@ -156,7 +166,7 @@ static int can_push(pa_memblockq *bq, size_t l) {
if (l > d)
l -= d;
else
- return 1;
+ return TRUE;
}
end = bq->blocks_tail ? bq->blocks_tail->index + bq->blocks_tail->chunk.length : 0;
@@ -164,21 +174,21 @@ static int can_push(pa_memblockq *bq, size_t l) {
/* 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)
- return 0;
+ return FALSE;
- return 1;
+ return TRUE;
}
int pa_memblockq_push(pa_memblockq* bq, const pa_memchunk *uchunk) {
-
- struct memblock_list *q, *n;
+ struct list_item *q, *n;
pa_memchunk chunk;
+ int64_t old, delta;
- assert(bq);
- assert(uchunk);
- assert(uchunk->memblock);
- assert(uchunk->length > 0);
- assert(uchunk->index + uchunk->length <= uchunk->memblock->length);
+ pa_assert(bq);
+ pa_assert(uchunk);
+ pa_assert(uchunk->memblock);
+ pa_assert(uchunk->length > 0);
+ pa_assert(uchunk->index + uchunk->length <= pa_memblock_get_length(uchunk->memblock));
if (uchunk->length % bq->base)
return -1;
@@ -186,6 +196,7 @@ int pa_memblockq_push(pa_memblockq* bq, const pa_memchunk *uchunk) {
if (!can_push(bq, uchunk->length))
return -1;
+ old = bq->write_index;
chunk = *uchunk;
if (bq->read_index > bq->write_index) {
@@ -198,11 +209,11 @@ int pa_memblockq_push(pa_memblockq* bq, const pa_memchunk *uchunk) {
if (chunk.length > d) {
chunk.index += d;
chunk.length -= d;
- bq->write_index = bq->read_index;
+ bq->write_index += d;
} else {
/* We drop the incoming data completely */
bq->write_index += chunk.length;
- return 0;
+ goto finish;
}
}
@@ -212,10 +223,10 @@ int pa_memblockq_push(pa_memblockq* bq, const pa_memchunk *uchunk) {
q = bq->blocks_tail;
while (q) {
- if (bq->write_index >= q->index + (int64_t)q->chunk.length)
+ if (bq->write_index >= q->index + (int64_t) q->chunk.length)
/* We found the entry where we need to place the new entry immediately after */
break;
- else if (bq->write_index + (int64_t)chunk.length <= q->index) {
+ else if (bq->write_index + (int64_t) chunk.length <= q->index) {
/* This entry isn't touched at all, let's skip it */
q = q->prev;
} else if (bq->write_index <= q->index &&
@@ -223,7 +234,7 @@ int pa_memblockq_push(pa_memblockq* bq, const pa_memchunk *uchunk) {
/* This entry is fully replaced by the new entry, so let's drop it */
- struct memblock_list *p;
+ struct list_item *p;
p = q;
q = q->prev;
drop_block(bq, p);
@@ -234,17 +245,19 @@ int pa_memblockq_push(pa_memblockq* bq, const pa_memchunk *uchunk) {
if (bq->write_index + chunk.length < q->index + q->chunk.length) {
/* We need to save the end of this memchunk */
- struct memblock_list *p;
+ struct list_item *p;
size_t d;
/* Create a new list entry for the end of thie memchunk */
- p = pa_xnew(struct memblock_list, 1);
+ if (!(p = pa_flist_pop(PA_STATIC_FLIST_GET(list_items))))
+ p = pa_xnew(struct list_item, 1);
+
p->chunk = q->chunk;
pa_memblock_ref(p->chunk.memblock);
/* Calculate offset */
d = bq->write_index + chunk.length - q->index;
- assert(d > 0);
+ pa_assert(d > 0);
/* Drop it from the new entry */
p->index = q->index + d;
@@ -263,7 +276,7 @@ int pa_memblockq_push(pa_memblockq* bq, const pa_memchunk *uchunk) {
/* Truncate the chunk */
if (!(q->chunk.length = bq->write_index - q->index)) {
- struct memblock_list *p;
+ struct list_item *p;
p = q;
q = q->prev;
drop_block(bq, p);
@@ -274,7 +287,7 @@ int pa_memblockq_push(pa_memblockq* bq, const pa_memchunk *uchunk) {
} else {
size_t d;
- assert(bq->write_index + (int64_t)chunk.length > q->index &&
+ pa_assert(bq->write_index + (int64_t)chunk.length > q->index &&
bq->write_index + (int64_t)chunk.length < q->index + (int64_t)q->chunk.length &&
bq->write_index < q->index);
@@ -287,12 +300,11 @@ int pa_memblockq_push(pa_memblockq* bq, const pa_memchunk *uchunk) {
q = q->prev;
}
-
}
if (q) {
- assert(bq->write_index >= q->index + (int64_t)q->chunk.length);
- assert(!q->next || (bq->write_index + (int64_t)chunk.length <= q->next->index));
+ pa_assert(bq->write_index >= q->index + (int64_t)q->chunk.length);
+ pa_assert(!q->next || (bq->write_index + (int64_t)chunk.length <= q->next->index));
/* Try to merge memory blocks */
@@ -302,13 +314,14 @@ int pa_memblockq_push(pa_memblockq* bq, const pa_memchunk *uchunk) {
q->chunk.length += chunk.length;
bq->write_index += chunk.length;
- return 0;
+ goto finish;
}
} else
- assert(!bq->blocks || (bq->write_index + (int64_t)chunk.length <= bq->blocks->index));
+ pa_assert(!bq->blocks || (bq->write_index + (int64_t)chunk.length <= bq->blocks->index));
+ if (!(n = pa_flist_pop(PA_STATIC_FLIST_GET(list_items))))
+ n = pa_xnew(struct list_item, 1);
- n = pa_xnew(struct memblock_list, 1);
n->chunk = chunk;
pa_memblock_ref(n->chunk.memblock);
n->index = bq->write_index;
@@ -328,27 +341,52 @@ int pa_memblockq_push(pa_memblockq* bq, const pa_memchunk *uchunk) {
bq->blocks = n;
bq->n_blocks++;
+
+finish:
+
+ delta = bq->write_index - old;
+
+ if (delta >= bq->requested) {
+ delta -= bq->requested;
+ bq->requested = 0;
+ } else {
+ bq->requested -= delta;
+ delta = 0;
+ }
+
+ bq->missing -= delta;
+
return 0;
}
-int pa_memblockq_peek(pa_memblockq* bq, pa_memchunk *chunk) {
- assert(bq);
- assert(chunk);
+static pa_bool_t memblockq_check_prebuf(pa_memblockq *bq) {
+ pa_assert(bq);
- if (bq->state == PREBUF) {
+ if (bq->in_prebuf) {
- /* We need to pre-buffer */
if (pa_memblockq_get_length(bq) < bq->prebuf)
- return -1;
+ return TRUE;
- bq->state = RUNNING;
+ bq->in_prebuf = FALSE;
+ return FALSE;
+ } else {
- } else if (bq->prebuf > 0 && bq->read_index >= bq->write_index) {
+ if (bq->prebuf > 0 && bq->read_index >= bq->write_index) {
+ bq->in_prebuf = TRUE;
+ return TRUE;
+ }
- /* Buffer underflow protection */
- bq->state = PREBUF;
- return -1;
+ return FALSE;
}
+}
+
+int pa_memblockq_peek(pa_memblockq* bq, pa_memchunk *chunk) {
+ pa_assert(bq);
+ pa_assert(chunk);
+
+ /* We need to pre-buffer */
+ if (memblockq_check_prebuf(bq))
+ return -1;
/* Do we need to spit out silence? */
if (!bq->blocks || bq->blocks->index > bq->read_index) {
@@ -362,8 +400,8 @@ int pa_memblockq_peek(pa_memblockq* bq, pa_memchunk *chunk) {
if (bq->silence) {
chunk->memblock = pa_memblock_ref(bq->silence);
- if (!length || length > chunk->memblock->length)
- length = chunk->memblock->length;
+ if (!length || length > pa_memblock_get_length(chunk->memblock))
+ length = pa_memblock_get_length(chunk->memblock);
chunk->length = length;
} else {
@@ -382,7 +420,7 @@ int pa_memblockq_peek(pa_memblockq* bq, pa_memchunk *chunk) {
}
/* Ok, let's pass real data to the caller */
- assert(bq->blocks->index == bq->read_index);
+ pa_assert(bq->blocks->index == bq->read_index);
*chunk = bq->blocks->chunk;
pa_memblock_ref(chunk->memblock);
@@ -390,48 +428,23 @@ int pa_memblockq_peek(pa_memblockq* bq, pa_memchunk *chunk) {
return 0;
}
-void pa_memblockq_drop(pa_memblockq *bq, const pa_memchunk *chunk, size_t length) {
- assert(bq);
- assert(length % bq->base == 0);
-
- assert(!chunk || length <= chunk->length);
+void pa_memblockq_drop(pa_memblockq *bq, size_t length) {
+ int64_t old, delta;
+ pa_assert(bq);
+ pa_assert(length % bq->base == 0);
- if (chunk) {
-
- if (bq->blocks && bq->blocks->index == bq->read_index) {
- /* The first item in queue is valid */
-
- /* Does the chunk match with what the user supplied us? */
- if (memcmp(chunk, &bq->blocks->chunk, sizeof(pa_memchunk)) != 0)
- return;
-
- } else {
- size_t l;
-
- /* The first item in the queue is not yet relevant */
-
- assert(!bq->blocks || bq->blocks->index > bq->read_index);
- l = bq->blocks ? bq->blocks->index - bq->read_index : 0;
-
- if (bq->silence) {
-
- if (!l || l > bq->silence->length)
- l = bq->silence->length;
-
- }
-
- /* Do the entries still match? */
- if (chunk->index != 0 || chunk->length != l || chunk->memblock != bq->silence)
- return;
- }
- }
+ old = bq->read_index;
while (length > 0) {
+ /* Do not drop any data when we are in prebuffering mode */
+ if (memblockq_check_prebuf(bq))
+ break;
+
if (bq->blocks) {
size_t d;
- assert(bq->blocks->index >= bq->read_index);
+ pa_assert(bq->blocks->index >= bq->read_index);
d = (size_t) (bq->blocks->index - bq->read_index);
@@ -446,7 +459,7 @@ void pa_memblockq_drop(pa_memblockq *bq, const pa_memchunk *chunk, size_t length
bq->read_index += d;
}
- assert(bq->blocks->index == bq->read_index);
+ pa_assert(bq->blocks->index == bq->read_index);
if (bq->blocks->chunk.length <= length) {
/* We need to drop the full block */
@@ -472,35 +485,25 @@ void pa_memblockq_drop(pa_memblockq *bq, const pa_memchunk *chunk, size_t length
break;
}
}
+
+ delta = bq->read_index - old;
+ bq->missing += delta;
}
int pa_memblockq_is_readable(pa_memblockq *bq) {
- assert(bq);
-
- if (bq->prebuf > 0) {
- size_t l = pa_memblockq_get_length(bq);
-
- if (bq->state == PREBUF && l < bq->prebuf)
- return 0;
-
- if (l <= 0)
- return 0;
- }
-
- return 1;
-}
+ pa_assert(bq);
-int pa_memblockq_is_writable(pa_memblockq *bq, size_t length) {
- assert(bq);
+ if (memblockq_check_prebuf(bq))
+ return 0;
- if (length % bq->base)
+ if (pa_memblockq_get_length(bq) <= 0)
return 0;
- return pa_memblockq_get_length(bq) + length <= bq->tlength;
+ return 1;
}
size_t pa_memblockq_get_length(pa_memblockq *bq) {
- assert(bq);
+ pa_assert(bq);
if (bq->write_index <= bq->read_index)
return 0;
@@ -510,76 +513,106 @@ size_t pa_memblockq_get_length(pa_memblockq *bq) {
size_t pa_memblockq_missing(pa_memblockq *bq) {
size_t l;
- assert(bq);
+ pa_assert(bq);
if ((l = pa_memblockq_get_length(bq)) >= bq->tlength)
return 0;
l = bq->tlength - l;
- return (l >= bq->minreq) ? l : 0;
+
+ return l >= bq->minreq ? l : 0;
}
size_t pa_memblockq_get_minreq(pa_memblockq *bq) {
- assert(bq);
+ pa_assert(bq);
return bq->minreq;
}
void pa_memblockq_seek(pa_memblockq *bq, int64_t offset, pa_seek_mode_t seek) {
- assert(bq);
+ int64_t old, delta;
+ pa_assert(bq);
+
+ old = bq->write_index;
switch (seek) {
case PA_SEEK_RELATIVE:
bq->write_index += offset;
- return;
+ break;
case PA_SEEK_ABSOLUTE:
bq->write_index = offset;
- return;
+ break;
case PA_SEEK_RELATIVE_ON_READ:
bq->write_index = bq->read_index + offset;
- return;
+ break;
case PA_SEEK_RELATIVE_END:
- bq->write_index = (bq->blocks_tail ? bq->blocks_tail->index + (int64_t)bq->blocks_tail->chunk.length : bq->read_index) + offset;
- return;
+ bq->write_index = (bq->blocks_tail ? bq->blocks_tail->index + (int64_t) bq->blocks_tail->chunk.length : bq->read_index) + offset;
+ break;
+ default:
+ pa_assert_not_reached();
}
- assert(0);
+ delta = bq->write_index - old;
+
+ if (delta >= bq->requested) {
+ delta -= bq->requested;
+ bq->requested = 0;
+ } else if (delta >= 0) {
+ bq->requested -= delta;
+ delta = 0;
+ }
+
+ bq->missing -= delta;
}
void pa_memblockq_flush(pa_memblockq *bq) {
- assert(bq);
+ int64_t old, delta;
+ pa_assert(bq);
while (bq->blocks)
drop_block(bq, bq->blocks);
- assert(bq->n_blocks == 0);
+ pa_assert(bq->n_blocks == 0);
+ old = bq->write_index;
bq->write_index = bq->read_index;
pa_memblockq_prebuf_force(bq);
+
+ delta = bq->write_index - old;
+
+ if (delta > bq->requested) {
+ delta -= bq->requested;
+ bq->requested = 0;
+ } else if (delta >= 0) {
+ bq->requested -= delta;
+ delta = 0;
+ }
+
+ bq->missing -= delta;
}
size_t pa_memblockq_get_tlength(pa_memblockq *bq) {
- assert(bq);
+ pa_assert(bq);
return bq->tlength;
}
int64_t pa_memblockq_get_read_index(pa_memblockq *bq) {
- assert(bq);
+ pa_assert(bq);
return bq->read_index;
}
int64_t pa_memblockq_get_write_index(pa_memblockq *bq) {
- assert(bq);
+ pa_assert(bq);
return bq->write_index;
}
int pa_memblockq_push_align(pa_memblockq* bq, const pa_memchunk *chunk) {
pa_memchunk rchunk;
- assert(bq);
- assert(chunk && bq->base);
+ pa_assert(bq);
+ pa_assert(chunk);
if (bq->base == 1)
return pa_memblockq_push(bq, chunk);
@@ -606,36 +639,52 @@ int pa_memblockq_push_align(pa_memblockq* bq, const pa_memchunk *chunk) {
void pa_memblockq_shorten(pa_memblockq *bq, size_t length) {
size_t l;
- assert(bq);
+ pa_assert(bq);
l = pa_memblockq_get_length(bq);
if (l > length)
- pa_memblockq_drop(bq, NULL, l - length);
+ pa_memblockq_drop(bq, l - length);
}
void pa_memblockq_prebuf_disable(pa_memblockq *bq) {
- assert(bq);
+ pa_assert(bq);
- if (bq->state == PREBUF)
- bq->state = RUNNING;
+ bq->in_prebuf = FALSE;
}
void pa_memblockq_prebuf_force(pa_memblockq *bq) {
- assert(bq);
+ pa_assert(bq);
- if (bq->state == RUNNING && bq->prebuf > 0)
- bq->state = PREBUF;
+ if (!bq->in_prebuf && bq->prebuf > 0)
+ bq->in_prebuf = TRUE;
}
size_t pa_memblockq_get_maxlength(pa_memblockq *bq) {
- assert(bq);
+ pa_assert(bq);
return bq->maxlength;
}
size_t pa_memblockq_get_prebuf(pa_memblockq *bq) {
- assert(bq);
+ pa_assert(bq);
return bq->prebuf;
}
+
+size_t pa_memblockq_pop_missing(pa_memblockq *bq) {
+ size_t l;
+
+ pa_assert(bq);
+
+/* pa_log("pop: %lli", bq->missing); */
+
+ if (bq->missing <= 0)
+ return 0;
+
+ l = (size_t) bq->missing;
+ bq->missing = 0;
+ bq->requested += l;
+
+ return l;
+}
diff --git a/src/pulsecore/memblockq.h b/src/pulsecore/memblockq.h
index 437c5a41..8c3e70fc 100644
--- a/src/pulsecore/memblockq.h
+++ b/src/pulsecore/memblockq.h
@@ -62,7 +62,7 @@ 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 whzen reading unitialized data
+ - silence: return this memblock when reading unitialized data
*/
pa_memblockq* pa_memblockq_new(
int64_t idx,
@@ -83,26 +83,30 @@ 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);
-/* Return a copy of the next memory chunk in the queue. It is not removed from the queue */
+/* 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
+ * memblock was passed at initialization. If the queue is not empty,
+ * but we're currently at a hole in the queue and no silence memblock
+ * was passed we return the length of the hole in chunk->length. */
int pa_memblockq_peek(pa_memblockq* bq, pa_memchunk *chunk);
-/* Drop the specified bytes from the queue, but only if the first
- * chunk in the queue matches the one passed here. If NULL is passed,
- * this check isn't done. */
-void pa_memblockq_drop(pa_memblockq *bq, const pa_memchunk *chunk, size_t length);
+/* Drop the specified bytes from the queue. */
+void pa_memblockq_drop(pa_memblockq *bq, size_t length);
/* Test if the pa_memblockq is currently readable, that is, more data than base */
int pa_memblockq_is_readable(pa_memblockq *bq);
-/* Test if the pa_memblockq is currently writable for the specified amount of bytes */
-int pa_memblockq_is_writable(pa_memblockq *bq, size_t length);
-
/* Return the length of the queue in bytes */
size_t pa_memblockq_get_length(pa_memblockq *bq);
/* Return how many bytes are missing in queue to the specified fill amount */
size_t pa_memblockq_missing(pa_memblockq *bq);
+/* Return the number of bytes that are missing since the last call to
+ * 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);
diff --git a/src/pulsecore/memchunk.c b/src/pulsecore/memchunk.c
index 7111e1ec..4e73b636 100644
--- a/src/pulsecore/memchunk.c
+++ b/src/pulsecore/memchunk.c
@@ -27,40 +27,66 @@
#include <stdio.h>
#include <stdlib.h>
-#include <assert.h>
#include <string.h>
+#include <errno.h>
#include <pulse/xmalloc.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/core-util.h>
#include "memchunk.h"
-void pa_memchunk_make_writable(pa_memchunk *c, size_t min) {
+pa_memchunk* pa_memchunk_make_writable(pa_memchunk *c, size_t min) {
pa_memblock *n;
size_t l;
+ void *tdata, *sdata;
- assert(c);
- assert(c->memblock);
- assert(PA_REFCNT_VALUE(c->memblock) > 0);
+ pa_assert(c);
+ pa_assert(c->memblock);
- if (PA_REFCNT_VALUE(c->memblock) == 1 &&
- !c->memblock->read_only &&
- c->memblock->length >= c->index+min)
- return;
+ if (pa_memblock_ref_is_one(c->memblock) &&
+ !pa_memblock_is_read_only(c->memblock) &&
+ pa_memblock_get_length(c->memblock) >= c->index+min)
+ return c;
l = c->length;
if (l < min)
l = min;
- n = pa_memblock_new(c->memblock->pool, l);
- memcpy(n->data, (uint8_t*) c->memblock->data + c->index, c->length);
+ n = pa_memblock_new(pa_memblock_get_pool(c->memblock), l);
+ tdata = pa_memblock_acquire(n);
+ sdata = pa_memblock_acquire(c->memblock);
+ memcpy(tdata, (uint8_t*) sdata + c->index, c->length);
+ pa_memblock_release(n);
+ pa_memblock_release(c->memblock);
pa_memblock_unref(c->memblock);
c->memblock = n;
c->index = 0;
+
+ return c;
}
-void pa_memchunk_reset(pa_memchunk *c) {
- assert(c);
+pa_memchunk* pa_memchunk_reset(pa_memchunk *c) {
+ pa_assert(c);
c->memblock = NULL;
c->length = c->index = 0;
+
+ return c;
+}
+
+pa_memchunk *pa_memchunk_will_need(const pa_memchunk *c) {
+ void *p;
+
+ pa_assert(c);
+ pa_assert(c->memblock);
+
+ /* A version of pa_memblock_will_need() that works on memchunks
+ * instead of memblocks */
+
+ p = (uint8_t*) pa_memblock_acquire(c->memblock) + c->index;
+ pa_will_need(p, c->length);
+ pa_memblock_release(c->memblock);
+
+ return (pa_memchunk*) c;
}
diff --git a/src/pulsecore/memchunk.h b/src/pulsecore/memchunk.h
index 0b982b6d..e6105ace 100644
--- a/src/pulsecore/memchunk.h
+++ b/src/pulsecore/memchunk.h
@@ -37,11 +37,16 @@ typedef struct pa_memchunk {
/* Make a memchunk writable, i.e. make sure that the caller may have
* exclusive access to the memblock and it is not read_only. If needed
- * the memblock in the structure is replaced by a copy. */
-void pa_memchunk_make_writable(pa_memchunk *c, size_t min);
+ * the memblock in the structure is replaced by a copy. If min is not
+ * 0 it is made sure that the returned memblock is at least of the
+ * specified size, i.e. is enlarged if necessary. */
+pa_memchunk* pa_memchunk_make_writable(pa_memchunk *c, size_t min);
/* Invalidate a memchunk. This does not free the cotaining memblock,
* but sets all members to zero. */
-void pa_memchunk_reset(pa_memchunk *c);
+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);
#endif
diff --git a/src/pulsecore/modargs.c b/src/pulsecore/modargs.c
index 3733f655..1311af3c 100644
--- a/src/pulsecore/modargs.c
+++ b/src/pulsecore/modargs.c
@@ -26,7 +26,6 @@
#endif
#include <ctype.h>
-#include <assert.h>
#include <stdlib.h>
#include <string.h>
@@ -39,6 +38,7 @@
#include <pulsecore/sink.h>
#include <pulsecore/source.h>
#include <pulsecore/core-util.h>
+#include <pulsecore/macro.h>
#include "modargs.h"
@@ -48,7 +48,10 @@ struct entry {
static int add_key_value(pa_hashmap *map, char *key, char *value, const char* const valid_keys[]) {
struct entry *e;
- assert(map && key && value);
+
+ pa_assert(map);
+ pa_assert(key);
+ pa_assert(value);
if (valid_keys) {
const char*const* v;
@@ -63,10 +66,11 @@ static int add_key_value(pa_hashmap *map, char *key, char *value, const char* co
}
}
- e = pa_xmalloc(sizeof(struct entry));
+ e = pa_xnew(struct entry, 1);
e->key = key;
e->value = value;
pa_hashmap_put(map, key, e);
+
return 0;
}
@@ -74,7 +78,6 @@ pa_modargs *pa_modargs_new(const char *args, const char* const* valid_keys) {
pa_hashmap *map = NULL;
map = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
- assert(map);
if (args) {
enum { WHITESPACE, KEY, VALUE_START, VALUE_SIMPLE, VALUE_DOUBLE_QUOTES, VALUE_TICKS } state;
@@ -166,10 +169,10 @@ fail:
return NULL;
}
-
static void free_func(void *p, PA_GCC_UNUSED void*userdata) {
struct entry *e = p;
- assert(e);
+ pa_assert(e);
+
pa_xfree(e->key);
pa_xfree(e->value);
pa_xfree(e);
@@ -192,7 +195,10 @@ const char *pa_modargs_get_value(pa_modargs *ma, const char *key, const char *de
int pa_modargs_get_value_u32(pa_modargs *ma, const char *key, uint32_t *value) {
const char *v;
- assert(ma && key && value);
+
+ pa_assert(ma);
+ pa_assert(key);
+ pa_assert(value);
if (!(v = pa_modargs_get_value(ma, key, NULL)))
return 0;
@@ -205,7 +211,10 @@ int pa_modargs_get_value_u32(pa_modargs *ma, const char *key, uint32_t *value) {
int pa_modargs_get_value_s32(pa_modargs *ma, const char *key, int32_t *value) {
const char *v;
- assert(ma && key && value);
+
+ pa_assert(ma);
+ pa_assert(key);
+ pa_assert(value);
if (!(v = pa_modargs_get_value(ma, key, NULL)))
return 0;
@@ -219,7 +228,10 @@ int pa_modargs_get_value_s32(pa_modargs *ma, const char *key, int32_t *value) {
int pa_modargs_get_value_boolean(pa_modargs *ma, const char *key, int *value) {
const char *v;
int r;
- assert(ma && key && value);
+
+ pa_assert(ma);
+ pa_assert(key);
+ pa_assert(value);
if (!(v = pa_modargs_get_value(ma, key, NULL)))
return 0;
@@ -238,9 +250,9 @@ int pa_modargs_get_sample_spec(pa_modargs *ma, pa_sample_spec *rss) {
const char *format;
uint32_t channels;
pa_sample_spec ss;
- assert(ma && rss);
-/* DEBUG_TRAP;*/
+ pa_assert(ma);
+ pa_assert(rss);
ss = *rss;
if ((pa_modargs_get_value_u32(ma, "rate", &ss.rate)) < 0)
@@ -263,16 +275,16 @@ int pa_modargs_get_sample_spec(pa_modargs *ma, pa_sample_spec *rss) {
return 0;
}
-int pa_modargs_get_channel_map(pa_modargs *ma, pa_channel_map *rmap) {
+int pa_modargs_get_channel_map(pa_modargs *ma, const char *name, pa_channel_map *rmap) {
pa_channel_map map;
const char *cm;
- assert(ma);
- assert(rmap);
+ pa_assert(ma);
+ pa_assert(rmap);
map = *rmap;
- if ((cm = pa_modargs_get_value(ma, "channel_map", NULL)))
+ if ((cm = pa_modargs_get_value(ma, name ? name : "channel_map", NULL)))
if (!pa_channel_map_parse(&map, cm))
return -1;
@@ -287,9 +299,9 @@ int pa_modargs_get_sample_spec_and_channel_map(pa_modargs *ma, pa_sample_spec *r
pa_sample_spec ss;
pa_channel_map map;
- assert(ma);
- assert(rss);
- assert(rmap);
+ pa_assert(ma);
+ pa_assert(rss);
+ pa_assert(rmap);
ss = *rss;
@@ -299,7 +311,7 @@ int pa_modargs_get_sample_spec_and_channel_map(pa_modargs *ma, pa_sample_spec *r
if (!pa_channel_map_init_auto(&map, ss.channels, def))
map.channels = 0;
- if (pa_modargs_get_channel_map(ma, &map) < 0)
+ if (pa_modargs_get_channel_map(ma, NULL, &map) < 0)
return -1;
if (map.channels != ss.channels)
diff --git a/src/pulsecore/modargs.h b/src/pulsecore/modargs.h
index 77262e1e..aa175885 100644
--- a/src/pulsecore/modargs.h
+++ b/src/pulsecore/modargs.h
@@ -49,8 +49,8 @@ int pa_modargs_get_value_boolean(pa_modargs *ma, const char *key, int *value);
/* Return sample spec data from the three arguments "rate", "format" and "channels" */
int pa_modargs_get_sample_spec(pa_modargs *ma, pa_sample_spec *ss);
-/* Return channel map data from the argument "channel_map" */
-int pa_modargs_get_channel_map(pa_modargs *ma, pa_channel_map *map);
+/* Return channel map data from the argument "channel_map" if name is NULL, otherwise read from the specified argument */
+int pa_modargs_get_channel_map(pa_modargs *ma, const char *name, pa_channel_map *map);
/* Combination of pa_modargs_get_sample_spec() and
pa_modargs_get_channel_map(). Not always suitable, since this routine
diff --git a/src/pulsecore/modinfo.c b/src/pulsecore/modinfo.c
index 58394e59..da2df653 100644
--- a/src/pulsecore/modinfo.c
+++ b/src/pulsecore/modinfo.c
@@ -26,12 +26,13 @@
#endif
#include <ltdl.h>
-#include <assert.h>
#include <pulse/xmalloc.h>
#include <pulsecore/core-util.h>
#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/ltdl-helper.h>
#include "modinfo.h"
@@ -40,30 +41,24 @@
#define PA_SYMBOL_USAGE "pa__get_usage"
#define PA_SYMBOL_VERSION "pa__get_version"
-/* lt_dlsym() violates ISO C, so confide the breakage into this function to
- * avoid warnings. */
-typedef void (*fnptr)(void);
-static inline fnptr lt_dlsym_fn(lt_dlhandle handle, const char *symbol) {
- return (fnptr) (long) lt_dlsym(handle, symbol);
-}
-
-pa_modinfo *pa_modinfo_get_by_handle(lt_dlhandle dl) {
+pa_modinfo *pa_modinfo_get_by_handle(lt_dlhandle dl, const char *module_name) {
pa_modinfo *i;
const char* (*func)(void);
- assert(dl);
+
+ pa_assert(dl);
i = pa_xnew0(pa_modinfo, 1);
- if ((func = (const char* (*)(void)) lt_dlsym_fn(dl, PA_SYMBOL_AUTHOR)))
+ if ((func = (const char* (*)(void)) pa_load_sym(dl, module_name, PA_SYMBOL_AUTHOR)))
i->author = pa_xstrdup(func());
- if ((func = (const char* (*)(void)) lt_dlsym_fn(dl, PA_SYMBOL_DESCRIPTION)))
+ if ((func = (const char* (*)(void)) pa_load_sym(dl, module_name, PA_SYMBOL_DESCRIPTION)))
i->description = pa_xstrdup(func());
- if ((func = (const char* (*)(void)) lt_dlsym_fn(dl, PA_SYMBOL_USAGE)))
+ if ((func = (const char* (*)(void)) pa_load_sym(dl, module_name, PA_SYMBOL_USAGE)))
i->usage = pa_xstrdup(func());
- if ((func = (const char* (*)(void)) lt_dlsym_fn(dl, PA_SYMBOL_VERSION)))
+ if ((func = (const char* (*)(void)) pa_load_sym(dl, module_name, PA_SYMBOL_VERSION)))
i->version = pa_xstrdup(func());
return i;
@@ -72,21 +67,23 @@ pa_modinfo *pa_modinfo_get_by_handle(lt_dlhandle dl) {
pa_modinfo *pa_modinfo_get_by_name(const char *name) {
lt_dlhandle dl;
pa_modinfo *i;
- assert(name);
+
+ pa_assert(name);
if (!(dl = lt_dlopenext(name))) {
pa_log("Failed to open module \"%s\": %s", name, lt_dlerror());
return NULL;
}
- i = pa_modinfo_get_by_handle(dl);
+ i = pa_modinfo_get_by_handle(dl, name);
lt_dlclose(dl);
return i;
}
void pa_modinfo_free(pa_modinfo *i) {
- assert(i);
+ pa_assert(i);
+
pa_xfree(i->author);
pa_xfree(i->description);
pa_xfree(i->usage);
diff --git a/src/pulsecore/modinfo.h b/src/pulsecore/modinfo.h
index 3ee33ede..02e536c6 100644
--- a/src/pulsecore/modinfo.h
+++ b/src/pulsecore/modinfo.h
@@ -34,7 +34,7 @@ typedef struct pa_modinfo {
} pa_modinfo;
/* Read meta data from an libtool handle */
-pa_modinfo *pa_modinfo_get_by_handle(lt_dlhandle dl);
+pa_modinfo *pa_modinfo_get_by_handle(lt_dlhandle dl, const char *module_name);
/* Read meta data from a module file */
pa_modinfo *pa_modinfo_get_by_name(const char *name);
diff --git a/src/pulsecore/module.c b/src/pulsecore/module.c
index 09b15b8b..dce91a71 100644
--- a/src/pulsecore/module.c
+++ b/src/pulsecore/module.c
@@ -29,7 +29,6 @@
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
-#include <assert.h>
#include <string.h>
#include <errno.h>
#include <ctype.h>
@@ -40,6 +39,8 @@
#include <pulsecore/core-subscribe.h>
#include <pulsecore/log.h>
#include <pulsecore/core-util.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/ltdl-helper.h>
#include "module.h"
@@ -48,69 +49,31 @@
#define UNLOAD_POLL_TIME 2
-/* lt_dlsym() violates ISO C, so confide the breakage into this function to
- * avoid warnings. */
-typedef void (*fnptr)(void);
-static inline fnptr lt_dlsym_fn(lt_dlhandle handle, const char *symbol) {
- return (fnptr) (long) lt_dlsym(handle, symbol);
-}
-
static void timeout_callback(pa_mainloop_api *m, pa_time_event*e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) {
- pa_core *c = userdata;
+ pa_core *c = PA_CORE(userdata);
struct timeval ntv;
- assert(c && c->mainloop == m && c->module_auto_unload_event == e);
+
+ pa_core_assert_ref(c);
+ pa_assert(c->mainloop == m);
+ pa_assert(c->module_auto_unload_event == e);
pa_module_unload_unused(c);
pa_gettimeofday(&ntv);
- ntv.tv_sec += UNLOAD_POLL_TIME;
+ pa_timeval_add(&ntv, UNLOAD_POLL_TIME*1000000);
m->time_restart(e, &ntv);
}
-static inline fnptr load_sym(lt_dlhandle handle, const char *module, const char *symbol) {
- char *buffer, *ch;
- size_t buflen;
- fnptr res;
-
- res = lt_dlsym_fn(handle, symbol);
- if (res)
- return res;
-
- /* As the .la files might have been cleansed from the system, we should
- * try with the ltdl prefix as well. */
-
- buflen = strlen(symbol) + strlen(module) + strlen("_LTX_") + 1;
- buffer = pa_xmalloc(buflen);
- assert(buffer);
-
- strcpy(buffer, module);
-
- for (ch = buffer;*ch != '\0';ch++) {
- if (!isalnum(*ch))
- *ch = '_';
- }
-
- strcat(buffer, "_LTX_");
- strcat(buffer, symbol);
-
- res = lt_dlsym_fn(handle, buffer);
-
- pa_xfree(buffer);
-
- return res;
-}
-
pa_module* pa_module_load(pa_core *c, const char *name, const char *argument) {
pa_module *m = NULL;
- int r;
- assert(c && name);
+ pa_assert(c);
+ pa_assert(name);
if (c->disallow_module_loading)
goto fail;
- m = pa_xmalloc(sizeof(pa_module));
-
+ m = pa_xnew(pa_module, 1);
m->name = pa_xstrdup(name);
m->argument = pa_xstrdup(argument);
@@ -119,24 +82,19 @@ pa_module* pa_module_load(pa_core *c, const char *name, const char *argument) {
goto fail;
}
- if (!(m->init = (int (*)(pa_core *_c, pa_module*_m)) load_sym(m->dl, name, PA_SYMBOL_INIT))) {
+ if (!(m->init = (int (*)(pa_module*_m)) pa_load_sym(m->dl, name, PA_SYMBOL_INIT))) {
pa_log("Failed to load module \"%s\": symbol \""PA_SYMBOL_INIT"\" not found.", name);
goto fail;
}
- if (!(m->done = (void (*)(pa_core *_c, pa_module*_m)) load_sym(m->dl, name, PA_SYMBOL_DONE))) {
- pa_log("Failed to load module \"%s\": symbol \""PA_SYMBOL_DONE"\" not found.", name);
- goto fail;
- }
-
+ m->done = (void (*)(pa_module*_m)) pa_load_sym(m->dl, name, PA_SYMBOL_DONE);
m->userdata = NULL;
m->core = c;
m->n_used = -1;
m->auto_unload = 0;
m->unload_requested = 0;
- assert(m->init);
- if (m->init(c, m) < 0) {
+ if (m->init(m) < 0) {
pa_log_error("Failed to load module \"%s\" (argument: \"%s\"): initialization failed.", name, argument ? argument : "");
goto fail;
}
@@ -147,14 +105,12 @@ pa_module* pa_module_load(pa_core *c, const char *name, const char *argument) {
if (!c->module_auto_unload_event) {
struct timeval ntv;
pa_gettimeofday(&ntv);
- ntv.tv_sec += UNLOAD_POLL_TIME;
+ pa_timeval_add(&ntv, UNLOAD_POLL_TIME*1000000);
c->module_auto_unload_event = c->mainloop->time_new(c->mainloop, &ntv, timeout_callback, c);
}
- assert(c->module_auto_unload_event);
- assert(c->modules);
- r = pa_idxset_put(c->modules, m, &m->index);
- assert(r >= 0 && m->index != PA_IDXSET_INVALID);
+ pa_assert_se(pa_idxset_put(c->modules, m, &m->index) >= 0);
+ pa_assert(m->index != PA_IDXSET_INVALID);
pa_log_info("Loaded \"%s\" (index: #%u; argument: \"%s\").", m->name, m->index, m->argument ? m->argument : "");
@@ -178,14 +134,16 @@ fail:
}
static void pa_module_free(pa_module *m) {
- assert(m && m->done && m->core);
+ 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);
- m->done(m->core, m);
+ if (m->done)
+ m->done(m);
lt_dlclose(m->dl);
@@ -199,9 +157,10 @@ static void pa_module_free(pa_module *m) {
}
void pa_module_unload(pa_core *c, pa_module *m) {
- assert(c && m);
+ pa_assert(c);
+ pa_assert(m);
- assert(c->modules);
+ pa_assert(c->modules);
if (!(m = pa_idxset_remove_by_data(c->modules, m, NULL)))
return;
@@ -210,9 +169,9 @@ void pa_module_unload(pa_core *c, pa_module *m) {
void pa_module_unload_by_index(pa_core *c, uint32_t idx) {
pa_module *m;
- assert(c && idx != PA_IDXSET_INVALID);
+ pa_assert(c);
+ pa_assert(idx != PA_IDXSET_INVALID);
- assert(c->modules);
if (!(m = pa_idxset_remove_by_index(c->modules, idx)))
return;
@@ -221,13 +180,14 @@ void pa_module_unload_by_index(pa_core *c, uint32_t idx) {
static void free_callback(void *p, PA_GCC_UNUSED void *userdata) {
pa_module *m = p;
- assert(m);
+ pa_assert(m);
pa_module_free(m);
}
void pa_module_unload_all(pa_core *c) {
pa_module *m;
- assert(c);
+
+ pa_assert(c);
if (!c->modules)
return;
@@ -252,7 +212,10 @@ 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;
- assert(p && del && now);
+
+ 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);
@@ -264,7 +227,7 @@ static int unused_callback(void *p, PA_GCC_UNUSED uint32_t idx, int *del, void *
void pa_module_unload_unused(pa_core *c) {
time_t now;
- assert(c);
+ pa_assert(c);
if (!c->modules)
return;
@@ -275,7 +238,7 @@ void pa_module_unload_unused(pa_core *c) {
static int unload_callback(void *p, PA_GCC_UNUSED uint32_t idx, int *del, PA_GCC_UNUSED void *userdata) {
pa_module *m = p;
- assert(m);
+ pa_assert(m);
if (m->unload_requested) {
pa_module_free(m);
@@ -286,18 +249,19 @@ static int unload_callback(void *p, PA_GCC_UNUSED uint32_t idx, int *del, PA_GCC
}
static void defer_cb(pa_mainloop_api*api, pa_defer_event *e, void *userdata) {
- pa_core *core = userdata;
+ pa_core *core = PA_CORE(userdata);
+
+ pa_core_assert_ref(core);
api->defer_enable(e, 0);
if (!core->modules)
return;
pa_idxset_foreach(core->modules, unload_callback, NULL);
-
}
void pa_module_unload_request(pa_module *m) {
- assert(m);
+ pa_assert(m);
m->unload_requested = 1;
@@ -308,19 +272,19 @@ void pa_module_unload_request(pa_module *m) {
}
void pa_module_set_used(pa_module*m, int used) {
- assert(m);
+ pa_assert(m);
if (m->n_used != used)
pa_subscription_post(m->core, PA_SUBSCRIPTION_EVENT_MODULE|PA_SUBSCRIPTION_EVENT_CHANGE, m->index);
- if (m->n_used != used && used == 0)
+ if (used == 0 && m->n_used > 0)
time(&m->last_used_time);
m->n_used = used;
}
pa_modinfo *pa_module_get_info(pa_module *m) {
- assert(m);
+ pa_assert(m);
- return pa_modinfo_get_by_handle(m->dl);
+ return pa_modinfo_get_by_handle(m->dl, m->name);
}
diff --git a/src/pulsecore/module.h b/src/pulsecore/module.h
index 750dfaa8..7a93a071 100644
--- a/src/pulsecore/module.h
+++ b/src/pulsecore/module.h
@@ -39,8 +39,8 @@ struct pa_module {
lt_dlhandle dl;
- int (*init)(pa_core *c, pa_module*m);
- void (*done)(pa_core *c, pa_module*m);
+ int (*init)(pa_module*m);
+ void (*done)(pa_module*m);
void *userdata;
@@ -62,9 +62,9 @@ void pa_module_unload_request(pa_module *m);
void pa_module_set_used(pa_module*m, int used);
-#define PA_MODULE_AUTHOR(s) const char * pa__get_author(void) { return s; }
-#define PA_MODULE_DESCRIPTION(s) const char * pa__get_description(void) { return s; }
-#define PA_MODULE_USAGE(s) const char * pa__get_usage(void) { return s; }
+#define PA_MODULE_AUTHOR(s) const char *pa__get_author(void) { return s; }
+#define PA_MODULE_DESCRIPTION(s) const char *pa__get_description(void) { return s; }
+#define PA_MODULE_USAGE(s) const char *pa__get_usage(void) { return s; }
#define PA_MODULE_VERSION(s) const char * pa__get_version(void) { return s; }
pa_modinfo *pa_module_get_info(pa_module *m);
diff --git a/src/pulsecore/msgobject.c b/src/pulsecore/msgobject.c
new file mode 100644
index 00000000..f54e69f2
--- /dev/null
+++ b/src/pulsecore/msgobject.c
@@ -0,0 +1,49 @@
+/* $Id$ */
+
+/***
+ 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.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "msgobject.h"
+
+PA_DEFINE_CHECK_TYPE(pa_msgobject, pa_object);
+
+pa_msgobject *pa_msgobject_new_internal(size_t size, const char *type_name, int (*check_type)(const char *type_name)) {
+ pa_msgobject *o;
+
+ pa_assert(size > sizeof(pa_msgobject));
+ pa_assert(type_name);
+
+ if (!check_type)
+ check_type = pa_msgobject_check_type;
+
+ pa_assert(check_type(type_name));
+ pa_assert(check_type("pa_object"));
+ pa_assert(check_type("pa_msgobject"));
+
+ o = PA_MSGOBJECT(pa_object_new_internal(size, type_name, check_type));
+ o->process_msg = NULL;
+ return o;
+}
diff --git a/src/pulsecore/msgobject.h b/src/pulsecore/msgobject.h
new file mode 100644
index 00000000..8221cc33
--- /dev/null
+++ b/src/pulsecore/msgobject.h
@@ -0,0 +1,54 @@
+#ifndef foopulsemsgobjecthfoo
+#define foopulsemsgobjecthfoo
+
+/* $Id$ */
+
+/***
+ 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.
+***/
+
+#include <sys/types.h>
+
+#include <pulse/xmalloc.h>
+#include <pulsecore/refcnt.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/object.h>
+#include <pulsecore/memchunk.h>
+
+typedef struct pa_msgobject pa_msgobject;
+
+struct pa_msgobject {
+ pa_object parent;
+ int (*process_msg)(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk);
+};
+
+pa_msgobject *pa_msgobject_new_internal(size_t size, const char *type_name, int (*check_type)(const char *type_name));
+
+int pa_msgobject_check_type(const char *type);
+
+#define pa_msgobject_new(type) ((type*) pa_msgobject_new_internal(sizeof(type), #type, type##_check_type))
+#define pa_msgobject_free ((void (*) (pa_msgobject* o)) pa_object_free)
+
+#define PA_MSGOBJECT(o) pa_msgobject_cast(o)
+
+PA_DECLARE_CLASS(pa_msgobject);
+
+#endif
diff --git a/src/pulsecore/mutex-posix.c b/src/pulsecore/mutex-posix.c
index 52e731b3..1b13ede1 100644
--- a/src/pulsecore/mutex-posix.c
+++ b/src/pulsecore/mutex-posix.c
@@ -25,20 +25,16 @@
#include <config.h>
#endif
-#include <assert.h>
#include <pthread.h>
-
-#include <atomic_ops.h>
+#include <errno.h>
#include <pulse/xmalloc.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/log.h>
+#include <pulsecore/core-error.h>
#include "mutex.h"
-#define ASSERT_SUCCESS(x) do { \
- int _r = (x); \
- assert(_r == 0); \
-} while(0)
-
struct pa_mutex {
pthread_mutex_t mutex;
};
@@ -47,68 +43,88 @@ struct pa_cond {
pthread_cond_t cond;
};
-pa_mutex* pa_mutex_new(int recursive) {
+pa_mutex* pa_mutex_new(pa_bool_t recursive, pa_bool_t inherit_priority) {
pa_mutex *m;
pthread_mutexattr_t attr;
+ int r;
- pthread_mutexattr_init(&attr);
+ pa_assert_se(pthread_mutexattr_init(&attr) == 0);
if (recursive)
- ASSERT_SUCCESS(pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE));
+ pa_assert_se(pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE) == 0);
+
+#ifdef HAVE_PTHREAD_PRIO_INHERIT
+ if (inherit_priority)
+ pa_assert_se(pthread_mutexattr_setprotocol(&attr, PTHREAD_PRIO_INHERIT) == 0);
+#endif
m = pa_xnew(pa_mutex, 1);
- ASSERT_SUCCESS(pthread_mutex_init(&m->mutex, &attr));
+#ifndef HAVE_PTHREAD_PRIO_INHERIT
+ pa_assert_se(pthread_mutex_init(&m->mutex, &attr) == 0);
+
+#else
+ if ((r = pthread_mutex_init(&m->mutex, &attr))) {
+
+ /* If this failed, then this was probably due to non-available
+ * priority inheritance. In which case we fall back to normal
+ * mutexes. */
+ pa_assert(r == ENOTSUP && inherit_priority);
+
+ pa_assert_se(pthread_mutexattr_setprotocol(&attr, PTHREAD_PRIO_NONE) == 0);
+ pa_assert_se(pthread_mutex_init(&m->mutex, &attr) == 0);
+ }
+#endif
+
return m;
}
void pa_mutex_free(pa_mutex *m) {
- assert(m);
+ pa_assert(m);
- ASSERT_SUCCESS(pthread_mutex_destroy(&m->mutex));
+ pa_assert_se(pthread_mutex_destroy(&m->mutex) == 0);
pa_xfree(m);
}
void pa_mutex_lock(pa_mutex *m) {
- assert(m);
+ pa_assert(m);
- ASSERT_SUCCESS(pthread_mutex_lock(&m->mutex));
+ pa_assert_se(pthread_mutex_lock(&m->mutex) == 0);
}
void pa_mutex_unlock(pa_mutex *m) {
- assert(m);
+ pa_assert(m);
- ASSERT_SUCCESS(pthread_mutex_unlock(&m->mutex));
+ pa_assert_se(pthread_mutex_unlock(&m->mutex) == 0);
}
pa_cond *pa_cond_new(void) {
pa_cond *c;
c = pa_xnew(pa_cond, 1);
-
- ASSERT_SUCCESS(pthread_cond_init(&c->cond, NULL));
+ pa_assert_se(pthread_cond_init(&c->cond, NULL) == 0);
return c;
}
void pa_cond_free(pa_cond *c) {
- assert(c);
+ pa_assert(c);
- ASSERT_SUCCESS(pthread_cond_destroy(&c->cond));
+ pa_assert_se(pthread_cond_destroy(&c->cond) == 0);
pa_xfree(c);
}
void pa_cond_signal(pa_cond *c, int broadcast) {
- assert(c);
+ pa_assert(c);
if (broadcast)
- ASSERT_SUCCESS(pthread_cond_broadcast(&c->cond));
+ pa_assert_se(pthread_cond_broadcast(&c->cond) == 0);
else
- ASSERT_SUCCESS(pthread_cond_signal(&c->cond));
+ pa_assert_se(pthread_cond_signal(&c->cond) == 0);
}
int pa_cond_wait(pa_cond *c, pa_mutex *m) {
- assert(c);
- assert(m);
+ pa_assert(c);
+ pa_assert(m);
return pthread_cond_wait(&c->cond, &m->mutex);
}
diff --git a/src/pulsecore/mutex-win32.c b/src/pulsecore/mutex-win32.c
index 1f16e24c..77d63d15 100644
--- a/src/pulsecore/mutex-win32.c
+++ b/src/pulsecore/mutex-win32.c
@@ -40,7 +40,7 @@ struct pa_cond {
pa_hashmap *wait_events;
};
-pa_mutex* pa_mutex_new(int recursive) {
+pa_mutex* pa_mutex_new(pa_bool_t recursive, pa_bool_t inherit_priority) {
pa_mutex *m;
m = pa_xnew(pa_mutex, 1);
diff --git a/src/pulsecore/mutex.h b/src/pulsecore/mutex.h
index b2e34c07..72e88781 100644
--- a/src/pulsecore/mutex.h
+++ b/src/pulsecore/mutex.h
@@ -24,9 +24,17 @@
USA.
***/
+#include <pulsecore/macro.h>
+
typedef struct pa_mutex pa_mutex;
-pa_mutex* pa_mutex_new(int recursive);
+/* Please think twice before enabling priority inheritance. This is no
+ * magic wand! Use it only when the potentially priorized threads are
+ * good candidates for it. Don't use this blindly! Also, note that
+ * only very few operating systems actually implement this, hence this
+ * is merely a hint. */
+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);
void pa_mutex_unlock(pa_mutex *m);
diff --git a/src/pulsecore/namereg.c b/src/pulsecore/namereg.c
index 7f66af05..fe2be467 100644
--- a/src/pulsecore/namereg.c
+++ b/src/pulsecore/namereg.c
@@ -27,7 +27,6 @@
#include <stdlib.h>
#include <string.h>
-#include <assert.h>
#include <string.h>
#include <stdio.h>
@@ -38,6 +37,7 @@
#include <pulsecore/sink.h>
#include <pulsecore/core-subscribe.h>
#include <pulsecore/core-util.h>
+#include <pulsecore/macro.h>
#include "namereg.h"
@@ -90,23 +90,22 @@ static char* cleanup_name(const char *name) {
}
void pa_namereg_free(pa_core *c) {
- assert(c);
+ pa_assert(c);
if (!c->namereg)
return;
- assert(pa_hashmap_size(c->namereg) == 0);
+ pa_assert(pa_hashmap_size(c->namereg) == 0);
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) {
struct namereg_entry *e;
char *n = NULL;
- int r;
- assert(c);
- assert(name);
- assert(data);
+ pa_assert(c);
+ pa_assert(name);
+ pa_assert(data);
if (!*name)
return NULL;
@@ -142,7 +141,7 @@ const char *pa_namereg_register(pa_core *c, const char *name, pa_namereg_type_t
k = pa_xnew(char, l+4);
for (i = 2; i <= 99; i++) {
- snprintf(k, l+4, "%s.%u", name, i);
+ pa_snprintf(k, l+4, "%s.%u", name, i);
if (!(e = pa_hashmap_get(c->namereg, k)))
break;
@@ -163,8 +162,7 @@ const char *pa_namereg_register(pa_core *c, const char *name, pa_namereg_type_t
e->name = n ? n : pa_xstrdup(name);
e->data = data;
- r = pa_hashmap_put(c->namereg, e->name, e);
- assert (r >= 0);
+ pa_assert_se(pa_hashmap_put(c->namereg, e->name, e) >= 0);
return e->name;
}
@@ -172,11 +170,10 @@ const char *pa_namereg_register(pa_core *c, const char *name, pa_namereg_type_t
void pa_namereg_unregister(pa_core *c, const char *name) {
struct namereg_entry *e;
- assert(c);
- assert(name);
+ pa_assert(c);
+ pa_assert(name);
- e = pa_hashmap_remove(c->namereg, name);
- assert(e);
+ pa_assert_se(e = pa_hashmap_remove(c->namereg, name));
pa_xfree(e->name);
pa_xfree(e);
@@ -185,7 +182,7 @@ 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) {
struct namereg_entry *e;
uint32_t idx;
- assert(c);
+ pa_assert(c);
if (!name) {
@@ -245,8 +242,8 @@ void* pa_namereg_get(pa_core *c, const char *name, pa_namereg_type_t type, int a
int pa_namereg_set_default(pa_core*c, const char *name, pa_namereg_type_t type) {
char **s;
- assert(c);
- assert(type == PA_NAMEREG_SINK || type == PA_NAMEREG_SOURCE);
+ pa_assert(c);
+ pa_assert(type == PA_NAMEREG_SINK || type == PA_NAMEREG_SOURCE);
s = type == PA_NAMEREG_SINK ? &c->default_sink_name : &c->default_source_name;
@@ -269,7 +266,7 @@ 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) {
pa_sink *s;
- assert(c);
+ pa_assert(c);
if (c->default_sink_name)
return c->default_sink_name;
@@ -284,7 +281,7 @@ const char *pa_namereg_get_default_source_name(pa_core *c) {
pa_source *s;
uint32_t idx;
- assert(c);
+ pa_assert(c);
if (c->default_source_name)
return c->default_source_name;
diff --git a/src/pulsecore/native-common.h b/src/pulsecore/native-common.h
index f7a7da1d..9defc4a5 100644
--- a/src/pulsecore/native-common.h
+++ b/src/pulsecore/native-common.h
@@ -115,6 +115,11 @@ enum {
PA_COMMAND_MOVE_SINK_INPUT,
PA_COMMAND_MOVE_SOURCE_OUTPUT,
+ PA_COMMAND_SET_SINK_INPUT_MUTE,
+
+ PA_COMMAND_SUSPEND_SINK,
+ PA_COMMAND_SUSPEND_SOURCE,
+
PA_COMMAND_MAX
};
diff --git a/src/pulsecore/object.c b/src/pulsecore/object.c
new file mode 100644
index 00000000..6c36242b
--- /dev/null
+++ b/src/pulsecore/object.c
@@ -0,0 +1,72 @@
+/* $Id$ */
+
+/***
+ 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.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "object.h"
+
+pa_object *pa_object_new_internal(size_t size, const char *type_name, int (*check_type)(const char *type_name)) {
+ pa_object *o;
+
+ pa_assert(size > sizeof(pa_object));
+ pa_assert(type_name);
+
+ if (!check_type)
+ check_type = pa_object_check_type;
+
+ pa_assert(check_type(type_name));
+ pa_assert(check_type("pa_object"));
+
+ o = pa_xmalloc(size);
+ PA_REFCNT_INIT(o);
+ o->type_name = type_name;
+ o->free = pa_object_free;
+ o->check_type = check_type;
+
+ return o;
+}
+
+pa_object *pa_object_ref(pa_object *o) {
+ pa_object_assert_ref(o);
+
+ PA_REFCNT_INC(o);
+ return o;
+}
+
+void pa_object_unref(pa_object *o) {
+ pa_object_assert_ref(o);
+
+ if (PA_REFCNT_DEC(o) <= 0) {
+ pa_assert(o->free);
+ o->free(o);
+ }
+}
+
+int pa_object_check_type(const char *type_name) {
+ pa_assert(type_name);
+
+ return strcmp(type_name, "pa_object") == 0;
+}
diff --git a/src/pulsecore/object.h b/src/pulsecore/object.h
new file mode 100644
index 00000000..562fd113
--- /dev/null
+++ b/src/pulsecore/object.h
@@ -0,0 +1,106 @@
+#ifndef foopulseobjecthfoo
+#define foopulseobjecthfoo
+
+/* $Id$ */
+
+/***
+ 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.
+***/
+
+#include <string.h>
+#include <sys/types.h>
+
+#include <pulse/xmalloc.h>
+#include <pulsecore/refcnt.h>
+#include <pulsecore/macro.h>
+
+typedef struct pa_object pa_object;
+
+struct pa_object {
+ PA_REFCNT_DECLARE;
+ const char *type_name;
+ void (*free)(pa_object *o);
+ int (*check_type)(const char *type_name);
+};
+
+pa_object *pa_object_new_internal(size_t size, const char *type_name, int (*check_type)(const char *type_name));
+#define pa_object_new(type) ((type*) pa_object_new_internal(sizeof(type), #type, type##_check_type)
+
+#define pa_object_free ((void (*) (pa_object* o)) pa_xfree)
+
+int pa_object_check_type(const char *type);
+
+static inline int pa_object_isinstance(void *o) {
+ pa_object *obj = (pa_object*) o;
+ return obj ? obj->check_type("pa_object") : 0;
+}
+
+pa_object *pa_object_ref(pa_object *o);
+void pa_object_unref(pa_object *o);
+
+static inline int pa_object_refcnt(pa_object *o) {
+ return o ? PA_REFCNT_VALUE(o) : 0;
+}
+
+static inline pa_object* pa_object_cast(void *o) {
+ pa_object *obj = (pa_object*) o;
+ pa_assert(!obj || obj->check_type("pa_object"));
+ return obj;
+}
+
+#define pa_object_assert_ref(o) pa_assert(pa_object_refcnt(o) > 0)
+
+#define PA_OBJECT(o) pa_object_cast(o)
+
+#define PA_DECLARE_CLASS(c) \
+ static inline int c##_isinstance(void *o) { \
+ pa_object *obj = (pa_object*) o; \
+ return obj ? obj->check_type(#c) : 1; \
+ } \
+ static inline c* c##_cast(void *o) { \
+ pa_assert(c##_isinstance(o)); \
+ return (c*) o; \
+ } \
+ static inline c* c##_ref(c *o) { \
+ return (c*) pa_object_ref(PA_OBJECT(o)); \
+ } \
+ static inline void c##_unref(c* o) { \
+ pa_object_unref(PA_OBJECT(o)); \
+ } \
+ static inline int c##_refcnt(c* o) { \
+ return pa_object_refcnt(PA_OBJECT(o)); \
+ } \
+ static inline void c##_assert_ref(c *o) { \
+ pa_object_assert_ref(PA_OBJECT(o)); \
+ } \
+ struct __stupid_useless_struct_to_allow_trailing_semicolon
+
+#define PA_DEFINE_CHECK_TYPE(c, parent) \
+ int c##_check_type(const char *type) { \
+ pa_assert(type); \
+ if (strcmp(type, #c) == 0) \
+ return 1; \
+ return parent##_check_type(type); \
+ } \
+ struct __stupid_useless_struct_to_allow_trailing_semicolon
+
+
+#endif
diff --git a/src/pulsecore/once-posix.c b/src/pulsecore/once-posix.c
deleted file mode 100644
index 4af7b36e..00000000
--- a/src/pulsecore/once-posix.c
+++ /dev/null
@@ -1,71 +0,0 @@
-/* $Id$ */
-
-/***
- This file is part of PulseAudio.
-
- Copyright 2006 Lennart Poettering
-
- PulseAudio is free software; you can redistribute it and/or modify
- it under the terms of the GNU Lesser General Public License as published
- by the Free Software Foundation; either version 2 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 <pthread.h>
-#include <assert.h>
-
-#include <pulsecore/mutex.h>
-
-#include "once.h"
-
-#define ASSERT_SUCCESS(x) do { \
- int _r = (x); \
- assert(_r == 0); \
-} while(0)
-
-static pa_mutex *global_mutex;
-static pthread_once_t global_mutex_once = PTHREAD_ONCE_INIT;
-
-static void global_mutex_once_func(void) {
- global_mutex = pa_mutex_new(0);
-}
-
-void pa_once(pa_once_t *control, pa_once_func_t func) {
- assert(control);
- assert(func);
-
- /* Create the global mutex */
- ASSERT_SUCCESS(pthread_once(&global_mutex_once, global_mutex_once_func));
-
- /* Create the local mutex */
- pa_mutex_lock(global_mutex);
- if (!control->mutex)
- control->mutex = pa_mutex_new(1);
- pa_mutex_unlock(global_mutex);
-
- /* Execute function */
- pa_mutex_lock(control->mutex);
- if (!control->once_value) {
- control->once_value = 1;
- func();
- }
- pa_mutex_unlock(control->mutex);
-
- /* Caveat: We have to make sure that the once func has completed
- * before returning, even if the once func is not actually
- * executed by us. Hence the awkward locking. */
-}
diff --git a/src/pulsecore/once-win32.c b/src/pulsecore/once-win32.c
deleted file mode 100644
index b30097c8..00000000
--- a/src/pulsecore/once-win32.c
+++ /dev/null
@@ -1,69 +0,0 @@
-/* $Id$ */
-
-/***
- This file is part of PulseAudio.
-
- 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.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <assert.h>
-#include <stdio.h>
-
-#include <windows.h>
-
-#include <pulsecore/mutex.h>
-
-#include "once.h"
-
-void pa_once(pa_once_t *control, pa_once_func_t func) {
- HANDLE mutex;
- char name[64];
-
- assert(control);
- assert(func);
-
- /* Create the global mutex */
- sprintf(name, "pulse%d", (int)GetCurrentProcessId());
-
- mutex = CreateMutex(NULL, FALSE, name);
- assert(mutex);
-
- /* Create the local mutex */
- WaitForSingleObject(mutex, INFINITE);
- if (!control->mutex)
- control->mutex = pa_mutex_new(1);
- ReleaseMutex(mutex);
-
- CloseHandle(mutex);
-
- /* Execute function */
- pa_mutex_lock(control->mutex);
- if (!control->once_value) {
- control->once_value = 1;
- func();
- }
- pa_mutex_unlock(control->mutex);
-
- /* Caveat: We have to make sure that the once func has completed
- * before returning, even if the once func is not actually
- * executed by us. Hence the awkward locking. */
-}
diff --git a/src/pulsecore/once.c b/src/pulsecore/once.c
new file mode 100644
index 00000000..a358cf65
--- /dev/null
+++ b/src/pulsecore/once.c
@@ -0,0 +1,96 @@
+/* $Id$ */
+
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2006 Lennart Poettering
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulsecore/macro.h>
+#include <pulsecore/mutex.h>
+
+#include "once.h"
+
+int pa_once_begin(pa_once *control) {
+ pa_mutex *m;
+
+ pa_assert(control);
+
+ if (pa_atomic_load(&control->done))
+ return 0;
+
+ pa_atomic_inc(&control->ref);
+
+ /* Caveat: We have to make sure that the once func has completed
+ * before returning, even if the once func is not actually
+ * executed by us. Hence the awkward locking. */
+
+ for (;;) {
+
+ if ((m = pa_atomic_ptr_load(&control->mutex))) {
+
+ /* The mutex is stored in locked state, hence let's just
+ * wait until it is unlocked */
+ pa_mutex_lock(m);
+
+ pa_once_end(control);
+ return 0;
+ }
+
+ pa_assert_se(m = pa_mutex_new(FALSE, FALSE));
+ pa_mutex_lock(m);
+
+ if (pa_atomic_ptr_cmpxchg(&control->mutex, NULL, m))
+ return 1;
+
+ pa_mutex_unlock(m);
+ pa_mutex_free(m);
+ }
+}
+
+void pa_once_end(pa_once *control) {
+ pa_mutex *m;
+
+ pa_assert(control);
+
+ pa_atomic_store(&control->done, 1);
+
+ pa_assert_se(m = pa_atomic_ptr_load(&control->mutex));
+ pa_mutex_unlock(m);
+
+ if (pa_atomic_dec(&control->ref) <= 1) {
+ pa_assert_se(pa_atomic_ptr_cmpxchg(&control->mutex, m, NULL));
+ pa_mutex_free(m);
+ }
+}
+
+/* Not reentrant -- how could it be? */
+void pa_run_once(pa_once *control, pa_once_func_t func) {
+ pa_assert(control);
+ pa_assert(func);
+
+ if (pa_once_begin(control)) {
+ func();
+ pa_once_end(control);
+ }
+}
+
diff --git a/src/pulsecore/once.h b/src/pulsecore/once.h
index c20fc0b4..c9fe6d0a 100644
--- a/src/pulsecore/once.h
+++ b/src/pulsecore/once.h
@@ -25,16 +25,52 @@
***/
#include <pulsecore/mutex.h>
+#include <pulsecore/atomic.h>
typedef struct pa_once {
- unsigned int once_value;
- pa_mutex *mutex;
-} pa_once_t;
+ pa_atomic_ptr_t mutex;
+ pa_atomic_t ref, done;
+} pa_once;
-#define PA_ONCE_INIT { .once_value = 0, .mutex = NULL }
+#define PA_ONCE_INIT \
+ { \
+ .mutex = PA_ATOMIC_PTR_INIT(NULL), \
+ .ref = PA_ATOMIC_INIT(0), \
+ .done = PA_ATOMIC_INIT(0) \
+ }
-typedef void (*pa_once_func_t) (void);
+/* Not to be called directly, use the macros defined below instead */
+int pa_once_begin(pa_once *o);
+void pa_once_end(pa_once *o);
+
+#define PA_ONCE_BEGIN \
+ do { \
+ static pa_once _once = PA_ONCE_INIT; \
+ if (pa_once_begin(&_once)) {{
+
+#define PA_ONCE_END \
+ } \
+ pa_once_end(&_once); \
+ } \
+ } while(0)
+
+/*
+
+ Usage of these macros is like this:
+
+ void foo() {
-void pa_once(pa_once_t *o, pa_once_func_t f);
+ PA_ONCE_BEGIN {
+
+ ... stuff to be called just once ...
+
+ } PA_ONCE_END;
+ }
+
+*/
+
+/* Same API but calls a function */
+typedef void (*pa_once_func_t) (void);
+void pa_run_once(pa_once *o, pa_once_func_t f);
#endif
diff --git a/src/pulsecore/packet.c b/src/pulsecore/packet.c
index ce57cb3e..2706efea 100644
--- a/src/pulsecore/packet.c
+++ b/src/pulsecore/packet.c
@@ -25,22 +25,22 @@
#include <config.h>
#endif
-#include <assert.h>
#include <stdlib.h>
#include <pulse/xmalloc.h>
+#include <pulsecore/macro.h>
#include "packet.h"
pa_packet* pa_packet_new(size_t length) {
pa_packet *p;
- assert(length);
+ pa_assert(length > 0);
- p = pa_xmalloc(sizeof(pa_packet)+length);
- p->ref = 1;
+ p = pa_xmalloc(PA_ALIGN(sizeof(pa_packet)) + length);
+ PA_REFCNT_INIT(p);
p->length = length;
- p->data = (uint8_t*) (p+1);
+ p->data = (uint8_t*) p + PA_ALIGN(sizeof(pa_packet));
p->type = PA_PACKET_APPENDED;
return p;
@@ -49,11 +49,11 @@ pa_packet* pa_packet_new(size_t length) {
pa_packet* pa_packet_new_dynamic(void* data, size_t length) {
pa_packet *p;
- assert(data);
- assert(length);
+ pa_assert(data);
+ pa_assert(length > 0);
p = pa_xnew(pa_packet, 1);
- p->ref = 1;
+ PA_REFCNT_INIT(p);
p->length = length;
p->data = data;
p->type = PA_PACKET_DYNAMIC;
@@ -62,18 +62,18 @@ pa_packet* pa_packet_new_dynamic(void* data, size_t length) {
}
pa_packet* pa_packet_ref(pa_packet *p) {
- assert(p);
- assert(p->ref >= 1);
+ pa_assert(p);
+ pa_assert(PA_REFCNT_VALUE(p) >= 1);
- p->ref++;
+ PA_REFCNT_INC(p);
return p;
}
void pa_packet_unref(pa_packet *p) {
- assert(p);
- assert(p->ref >= 1);
+ pa_assert(p);
+ pa_assert(PA_REFCNT_VALUE(p) >= 1);
- if (--p->ref == 0) {
+ if (PA_REFCNT_DEC(p) <= 0) {
if (p->type == PA_PACKET_DYNAMIC)
pa_xfree(p->data);
pa_xfree(p);
diff --git a/src/pulsecore/packet.h b/src/pulsecore/packet.h
index 842582c8..bcac4a7f 100644
--- a/src/pulsecore/packet.h
+++ b/src/pulsecore/packet.h
@@ -27,9 +27,11 @@
#include <sys/types.h>
#include <inttypes.h>
+#include <pulsecore/refcnt.h>
+
typedef struct pa_packet {
+ PA_REFCNT_DECLARE;
enum { PA_PACKET_APPENDED, PA_PACKET_DYNAMIC } type;
- unsigned ref;
size_t length;
uint8_t *data;
} pa_packet;
diff --git a/src/pulsecore/parseaddr.c b/src/pulsecore/parseaddr.c
index a49a09ed..65ba64c1 100644
--- a/src/pulsecore/parseaddr.c
+++ b/src/pulsecore/parseaddr.c
@@ -26,13 +26,13 @@
#endif
#include <string.h>
-#include <assert.h>
#include <stdlib.h>
#include <pulse/xmalloc.h>
-
#include <pulse/util.h>
+
#include <pulsecore/core-util.h>
+#include <pulsecore/macro.h>
#include "parseaddr.h"
@@ -45,7 +45,9 @@
* Return a newly allocated string of the hostname and fill in *ret_port if specified */
static char *parse_host(const char *s, uint16_t *ret_port) {
- assert(s && ret_port);
+ pa_assert(s);
+ pa_assert(ret_port);
+
if (*s == '[') {
char *e;
if (!(e = strchr(s+1, ']')))
@@ -70,7 +72,10 @@ static char *parse_host(const char *s, uint16_t *ret_port) {
int pa_parse_address(const char *name, pa_parsed_address *ret_p) {
const char *p;
- assert(name && ret_p);
+
+ pa_assert(name);
+ pa_assert(ret_p);
+
memset(ret_p, 0, sizeof(pa_parsed_address));
ret_p->type = PA_PARSED_ADDRESS_TCP_AUTO;
@@ -112,6 +117,5 @@ int pa_parse_address(const char *name, pa_parsed_address *ret_p) {
if (!(ret_p->path_or_host = parse_host(p, &ret_p->port)))
return -1;
-
return 0;
}
diff --git a/src/pulsecore/pdispatch.c b/src/pulsecore/pdispatch.c
index 10238acb..2c95d740 100644
--- a/src/pulsecore/pdispatch.c
+++ b/src/pulsecore/pdispatch.c
@@ -28,7 +28,6 @@
#include <stdio.h>
#include <stdlib.h>
-#include <assert.h>
#include <pulse/timeval.h>
#include <pulse/xmalloc.h>
@@ -37,6 +36,8 @@
#include <pulsecore/llist.h>
#include <pulsecore/log.h>
#include <pulsecore/core-util.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/refcnt.h>
#include "pdispatch.h"
@@ -108,7 +109,7 @@ struct reply_info {
};
struct pa_pdispatch {
- int ref;
+ PA_REFCNT_DECLARE;
pa_mainloop_api *mainloop;
const pa_pdispatch_cb_t *callback_table;
unsigned n_commands;
@@ -119,7 +120,9 @@ struct pa_pdispatch {
};
static void reply_info_free(struct reply_info *r) {
- assert(r && r->pdispatch && r->pdispatch->mainloop);
+ pa_assert(r);
+ pa_assert(r->pdispatch);
+ pa_assert(r->pdispatch->mainloop);
if (r->time_event)
r->pdispatch->mainloop->time_free(r->time_event);
@@ -131,12 +134,12 @@ static void reply_info_free(struct reply_info *r) {
pa_pdispatch* pa_pdispatch_new(pa_mainloop_api *mainloop, const pa_pdispatch_cb_t*table, unsigned entries) {
pa_pdispatch *pd;
- assert(mainloop);
+ pa_assert(mainloop);
- assert((entries && table) || (!entries && !table));
+ pa_assert((entries && table) || (!entries && !table));
- pd = pa_xmalloc(sizeof(pa_pdispatch));
- pd->ref = 1;
+ pd = pa_xnew(pa_pdispatch, 1);
+ PA_REFCNT_INIT(pd);
pd->mainloop = mainloop;
pd->callback_table = table;
pd->n_commands = entries;
@@ -149,7 +152,7 @@ pa_pdispatch* pa_pdispatch_new(pa_mainloop_api *mainloop, const pa_pdispatch_cb_
}
static void pdispatch_free(pa_pdispatch *pd) {
- assert(pd);
+ pa_assert(pd);
while (pd->replies) {
if (pd->replies->free_cb)
@@ -165,7 +168,7 @@ static void run_action(pa_pdispatch *pd, struct reply_info *r, uint32_t command,
pa_pdispatch_cb_t callback;
void *userdata;
uint32_t tag;
- assert(r);
+ pa_assert(r);
pa_pdispatch_ref(pd);
@@ -187,7 +190,12 @@ int pa_pdispatch_run(pa_pdispatch *pd, pa_packet*packet, const pa_creds *creds,
uint32_t tag, command;
pa_tagstruct *ts = NULL;
int ret = -1;
- assert(pd && packet && packet->data);
+
+ pa_assert(pd);
+ pa_assert(PA_REFCNT_VALUE(pd) >= 1);
+ pa_assert(packet);
+ pa_assert(PA_REFCNT_VALUE(packet) >= 1);
+ pa_assert(packet->data);
pa_pdispatch_ref(pd);
@@ -195,7 +203,6 @@ int pa_pdispatch_run(pa_pdispatch *pd, pa_packet*packet, const pa_creds *creds,
goto finish;
ts = pa_tagstruct_new(packet->data, packet->length);
- assert(ts);
if (pa_tagstruct_getu32(ts, &command) < 0 ||
pa_tagstruct_getu32(ts, &tag) < 0)
@@ -206,7 +213,7 @@ int pa_pdispatch_run(pa_pdispatch *pd, pa_packet*packet, const pa_creds *creds,
char t[256];
char const *p;
if (!(p = command_names[command]))
- snprintf((char*) (p = t), sizeof(t), "%u", command);
+ pa_snprintf((char*) (p = t), sizeof(t), "%u", command);
pa_log("Recieved opcode <%s>", p);
}
@@ -248,7 +255,12 @@ finish:
static void timeout_callback(pa_mainloop_api*m, pa_time_event*e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) {
struct reply_info*r = userdata;
- assert(r && r->time_event == e && r->pdispatch && r->pdispatch->mainloop == m && r->callback);
+
+ pa_assert(r);
+ pa_assert(r->time_event == e);
+ pa_assert(r->pdispatch);
+ pa_assert(r->pdispatch->mainloop == m);
+ pa_assert(r->callback);
run_action(r->pdispatch, r, PA_COMMAND_TIMEOUT, NULL);
}
@@ -256,7 +268,10 @@ static void timeout_callback(pa_mainloop_api*m, pa_time_event*e, PA_GCC_UNUSED c
void pa_pdispatch_register_reply(pa_pdispatch *pd, uint32_t tag, int timeout, pa_pdispatch_cb_t cb, void *userdata, pa_free_cb_t free_cb) {
struct reply_info *r;
struct timeval tv;
- assert(pd && pd->ref >= 1 && cb);
+
+ pa_assert(pd);
+ pa_assert(PA_REFCNT_VALUE(pd) >= 1);
+ pa_assert(cb);
r = pa_xnew(struct reply_info, 1);
r->pdispatch = pd;
@@ -268,21 +283,22 @@ void pa_pdispatch_register_reply(pa_pdispatch *pd, uint32_t tag, int timeout, pa
pa_gettimeofday(&tv);
tv.tv_sec += timeout;
- r->time_event = pd->mainloop->time_new(pd->mainloop, &tv, timeout_callback, r);
- assert(r->time_event);
+ pa_assert_se(r->time_event = pd->mainloop->time_new(pd->mainloop, &tv, timeout_callback, r));
PA_LLIST_PREPEND(struct reply_info, pd->replies, r);
}
int pa_pdispatch_is_pending(pa_pdispatch *pd) {
- assert(pd);
+ pa_assert(pd);
+ pa_assert(PA_REFCNT_VALUE(pd) >= 1);
return !!pd->replies;
}
void pa_pdispatch_set_drain_callback(pa_pdispatch *pd, void (*cb)(pa_pdispatch *pd, void *userdata), void *userdata) {
- assert(pd);
- assert(!cb || pa_pdispatch_is_pending(pd));
+ pa_assert(pd);
+ pa_assert(PA_REFCNT_VALUE(pd) >= 1);
+ pa_assert(!cb || pa_pdispatch_is_pending(pd));
pd->drain_callback = cb;
pd->drain_userdata = userdata;
@@ -290,7 +306,9 @@ void pa_pdispatch_set_drain_callback(pa_pdispatch *pd, void (*cb)(pa_pdispatch *
void pa_pdispatch_unregister_reply(pa_pdispatch *pd, void *userdata) {
struct reply_info *r, *n;
- assert(pd);
+
+ pa_assert(pd);
+ pa_assert(PA_REFCNT_VALUE(pd) >= 1);
for (r = pd->replies; r; r = n) {
n = r->next;
@@ -301,21 +319,24 @@ void pa_pdispatch_unregister_reply(pa_pdispatch *pd, void *userdata) {
}
void pa_pdispatch_unref(pa_pdispatch *pd) {
- assert(pd && pd->ref >= 1);
+ pa_assert(pd);
+ pa_assert(PA_REFCNT_VALUE(pd) >= 1);
- if (!(--(pd->ref)))
+ if (PA_REFCNT_DEC(pd) <= 0)
pdispatch_free(pd);
}
pa_pdispatch* pa_pdispatch_ref(pa_pdispatch *pd) {
- assert(pd && pd->ref >= 1);
- pd->ref++;
+ pa_assert(pd);
+ pa_assert(PA_REFCNT_VALUE(pd) >= 1);
+
+ PA_REFCNT_INC(pd);
return pd;
}
const pa_creds * pa_pdispatch_creds(pa_pdispatch *pd) {
- assert(pd);
- assert(pd->ref >= 1);
+ pa_assert(pd);
+ pa_assert(PA_REFCNT_VALUE(pd) >= 1);
return pd->creds;
}
diff --git a/src/pulsecore/pid.c b/src/pulsecore/pid.c
index 5e670e17..55ff2088 100644
--- a/src/pulsecore/pid.c
+++ b/src/pulsecore/pid.c
@@ -33,7 +33,6 @@
#include <sys/stat.h>
#include <string.h>
#include <stdio.h>
-#include <assert.h>
#include <stdlib.h>
#include <limits.h>
#include <signal.h>
@@ -47,6 +46,7 @@
#include <pulsecore/core-error.h>
#include <pulsecore/core-util.h>
#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
#include "pid.h"
@@ -57,11 +57,11 @@ static pid_t read_pid(const char *fn, int fd) {
char t[20], *e;
uint32_t pid;
- assert(fn && fd >= 0);
+ pa_assert(fn);
+ pa_assert(fd >= 0);
if ((r = pa_loop_read(fd, t, sizeof(t)-1, NULL)) < 0) {
- pa_log_warn("WARNING: failed to read PID file '%s': %s",
- fn, pa_cstrerror(errno));
+ pa_log_warn("Failed to read PID file '%s': %s", fn, pa_cstrerror(errno));
return (pid_t) -1;
}
@@ -73,7 +73,7 @@ static pid_t read_pid(const char *fn, int fd) {
*e = 0;
if (pa_atou(t, &pid) < 0) {
- pa_log("WARNING: failed to parse PID file '%s'", fn);
+ pa_log_warn("Failed to parse PID file '%s'", fn);
return (pid_t) -1;
}
@@ -83,13 +83,22 @@ static pid_t read_pid(const char *fn, int fd) {
static int open_pid_file(const char *fn, int mode) {
int fd = -1;
+ pa_assert(fn);
+
for (;;) {
struct stat st;
- if ((fd = open(fn, mode, S_IRUSR|S_IWUSR)) < 0) {
+ if ((fd = open(fn, mode
+#ifdef O_NOCTTY
+ |O_NOCTTY
+#endif
+#ifdef O_NOFOLLOW
+ |O_NOFOLLOW
+#endif
+ , S_IRUSR|S_IWUSR
+ )) < 0) {
if (mode != O_RDONLY || errno != ENOENT)
- pa_log_warn("WARNING: failed to open PID file '%s': %s",
- fn, pa_cstrerror(errno));
+ pa_log_warn("Failed to open PID file '%s': %s", fn, pa_cstrerror(errno));
goto fail;
}
@@ -98,8 +107,7 @@ static int open_pid_file(const char *fn, int mode) {
goto fail;
if (fstat(fd, &st) < 0) {
- pa_log_warn("WARNING: failed to fstat() PID file '%s': %s",
- fn, pa_cstrerror(errno));
+ pa_log_warn("Failed to fstat() PID file '%s': %s", fn, pa_cstrerror(errno));
goto fail;
}
@@ -110,9 +118,9 @@ static int open_pid_file(const char *fn, int mode) {
if (pa_lock_fd(fd, 0) < 0)
goto fail;
- if (close(fd) < 0) {
- pa_log_warn("WARNING: failed to close file '%s': %s",
- fn, pa_cstrerror(errno));
+ if (pa_close(fd) < 0) {
+ pa_log_warn("Failed to close file '%s': %s", fn, pa_cstrerror(errno));
+ fd = -1;
goto fail;
}
@@ -125,7 +133,7 @@ fail:
if (fd >= 0) {
pa_lock_fd(fd, 0);
- close(fd);
+ pa_close(fd);
}
return -1;
@@ -150,7 +158,7 @@ int pa_pid_file_create(void) {
goto fail;
if ((pid = read_pid(fn, fd)) == (pid_t) -1)
- pa_log("corrupt PID file, overwriting.");
+ pa_log_warn("Corrupt PID file, overwriting.");
else if (pid > 0) {
#ifdef OS_IS_WIN32
if ((process = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid)) != NULL) {
@@ -158,25 +166,24 @@ int pa_pid_file_create(void) {
#else
if (kill(pid, 0) >= 0 || errno != ESRCH) {
#endif
- pa_log("daemon already running.");
+ pa_log("Daemon already running.");
goto fail;
}
- pa_log("stale PID file, overwriting.");
+ 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) {
- pa_log("failed to truncate PID file '%s': %s",
- fn, pa_cstrerror(errno));
+ pa_log("Failed to truncate PID file '%s': %s", fn, pa_cstrerror(errno));
goto fail;
}
- snprintf(t, sizeof(t), "%lu\n", (unsigned long) getpid());
+ pa_snprintf(t, sizeof(t), "%lu\n", (unsigned long) getpid());
l = strlen(t);
if (pa_loop_write(fd, t, l, NULL) != (ssize_t) l) {
- pa_log("failed to write PID file.");
+ pa_log("Failed to write PID file.");
goto fail;
}
@@ -185,7 +192,11 @@ int pa_pid_file_create(void) {
fail:
if (fd >= 0) {
pa_lock_fd(fd, 0);
- close(fd);
+
+ if (pa_close(fd) < 0) {
+ pa_log("Failed to close PID file '%s': %s", fn, pa_cstrerror(errno));
+ ret = -1;
+ }
}
return ret;
@@ -201,8 +212,7 @@ int pa_pid_file_remove(void) {
pa_runtime_path("pid", fn, sizeof(fn));
if ((fd = open_pid_file(fn, O_RDWR)) < 0) {
- pa_log_warn("WARNING: failed to open PID file '%s': %s",
- fn, pa_cstrerror(errno));
+ pa_log_warn("Failed to open PID file '%s': %s", fn, pa_cstrerror(errno));
goto fail;
}
@@ -210,13 +220,12 @@ int pa_pid_file_remove(void) {
goto fail;
if (pid != getpid()) {
- pa_log("WARNING: PID file '%s' not mine!", fn);
+ pa_log("PID file '%s' not mine!", fn);
goto fail;
}
if (ftruncate(fd, 0) < 0) {
- pa_log_warn("WARNING: failed to truncate PID file '%s': %s",
- fn, pa_cstrerror(errno));
+ pa_log_warn("Failed to truncate PID file '%s': %s", fn, pa_cstrerror(errno));
goto fail;
}
@@ -227,8 +236,7 @@ int pa_pid_file_remove(void) {
#endif
if (unlink(fn) < 0) {
- pa_log_warn("WARNING: failed to remove PID file '%s': %s",
- fn, pa_cstrerror(errno));
+ pa_log_warn("Failed to remove PID file '%s': %s", fn, pa_cstrerror(errno));
goto fail;
}
@@ -238,7 +246,11 @@ fail:
if (fd >= 0) {
pa_lock_fd(fd, 0);
- close(fd);
+
+ if (pa_close(fd) < 0) {
+ pa_log_warn("Failed to close PID file '%s': %s", fn, pa_cstrerror(errno));
+ ret = -1;
+ }
}
return ret;
@@ -280,7 +292,7 @@ fail:
if (fd >= 0) {
pa_lock_fd(fd, 0);
- close(fd);
+ pa_close(fd);
}
return ret;
diff --git a/src/pulsecore/pipe.c b/src/pulsecore/pipe.c
index 7f6bb2e9..e614c9c6 100644
--- a/src/pulsecore/pipe.c
+++ b/src/pulsecore/pipe.c
@@ -149,14 +149,14 @@ int pipe(int filedes[2]) {
return 0;
error:
- if (listener >= 0)
- pa_close(listener);
- if (filedes[0] >= 0)
- pa_close(filedes[0]);
- if (filedes[1] >= 0)
- pa_close(filedes[0]);
-
- return -1;
+ if (listener >= 0)
+ pa_close(listener);
+ if (filedes[0] >= 0)
+ pa_close(filedes[0]);
+ if (filedes[1] >= 0)
+ pa_close(filedes[0]);
+
+ return -1;
}
#endif /* HAVE_PIPE */
diff --git a/src/pulsecore/play-memblockq.c b/src/pulsecore/play-memblockq.c
index 76edd27a..5d3c2d39 100644
--- a/src/pulsecore/play-memblockq.c
+++ b/src/pulsecore/play-memblockq.c
@@ -26,7 +26,6 @@
#endif
#include <stdlib.h>
-#include <assert.h>
#include <stdio.h>
#include <string.h>
@@ -34,53 +33,106 @@
#include <pulsecore/sink-input.h>
#include <pulsecore/gccmacro.h>
+#include <pulsecore/thread-mq.h>
#include "play-memblockq.h"
-static void sink_input_kill(pa_sink_input *i) {
- pa_memblockq *q;
- assert(i);
- assert(i->userdata);
+typedef struct memblockq_stream {
+ pa_msgobject parent;
+ pa_core *core;
+ pa_sink_input *sink_input;
+ pa_memblockq *memblockq;
+} memblockq_stream;
- q = i->userdata;
+enum {
+ MEMBLOCKQ_STREAM_MESSAGE_UNLINK,
+};
- pa_sink_input_disconnect(i);
- pa_sink_input_unref(i);
+PA_DECLARE_CLASS(memblockq_stream);
+#define MEMBLOCKQ_STREAM(o) (memblockq_stream_cast(o))
+static PA_DEFINE_CHECK_TYPE(memblockq_stream, pa_msgobject);
+
+static void memblockq_stream_unlink(memblockq_stream *u) {
+ pa_assert(u);
+
+ if (!u->sink_input)
+ return;
+
+ pa_sink_input_unlink(u->sink_input);
- pa_memblockq_free(q);
+ pa_sink_input_unref(u->sink_input);
+ u->sink_input = NULL;
+
+ memblockq_stream_unref(u);
}
-static int sink_input_peek(pa_sink_input *i, pa_memchunk *chunk) {
- pa_memblockq *q;
- assert(i);
- assert(chunk);
- assert(i->userdata);
+static void memblockq_stream_free(pa_object *o) {
+ memblockq_stream *u = MEMBLOCKQ_STREAM(o);
+ pa_assert(u);
+
+ memblockq_stream_unlink(u);
- q = i->userdata;
+ if (u->memblockq)
+ pa_memblockq_free(u->memblockq);
- return pa_memblockq_peek(q, chunk);
+ pa_xfree(u);
}
-static void si_kill(PA_GCC_UNUSED pa_mainloop_api *m, void *i) {
- sink_input_kill(i);
+static int memblockq_stream_process_msg(pa_msgobject *o, int code, void*userdata, int64_t offset, pa_memchunk *chunk) {
+ memblockq_stream *u = MEMBLOCKQ_STREAM(o);
+ memblockq_stream_assert_ref(u);
+
+ switch (code) {
+ case MEMBLOCKQ_STREAM_MESSAGE_UNLINK:
+ memblockq_stream_unlink(u);
+ break;
+ }
+
+ return 0;
+}
+
+static void sink_input_kill_cb(pa_sink_input *i) {
+ pa_sink_input_assert_ref(i);
+
+ memblockq_stream_unlink(MEMBLOCKQ_STREAM(i->userdata));
}
-static void sink_input_drop(pa_sink_input *i, const pa_memchunk*chunk, size_t length) {
- pa_memblockq *q;
+static int sink_input_peek_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) {
+ memblockq_stream *u;
- assert(i);
- assert(length > 0);
- assert( i->userdata);
+ pa_assert(i);
+ pa_assert(chunk);
+ u = MEMBLOCKQ_STREAM(i->userdata);
+ memblockq_stream_assert_ref(u);
- q = i->userdata;
+ if (!u->memblockq)
+ return -1;
- pa_memblockq_drop(q, chunk, length);
+ 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);
+ return -1;
+ }
- if (pa_memblockq_get_length(q) <= 0)
- pa_mainloop_api_once(i->sink->core->mainloop, si_kill, i);
+ return 0;
}
-int pa_play_memblockq(
+static void sink_input_drop_cb(pa_sink_input *i, size_t length) {
+ memblockq_stream *u;
+
+ pa_assert(i);
+ pa_assert(length > 0);
+ u = MEMBLOCKQ_STREAM(i->userdata);
+ memblockq_stream_assert_ref(u);
+
+ if (!u->memblockq)
+ return;
+
+ pa_memblockq_drop(u->memblockq, length);
+}
+
+pa_sink_input* pa_memblockq_sink_input_new(
pa_sink *sink,
const char *name,
const pa_sample_spec *ss,
@@ -88,41 +140,97 @@ int pa_play_memblockq(
pa_memblockq *q,
pa_cvolume *volume) {
- pa_sink_input *si;
+ memblockq_stream *u = NULL;
pa_sink_input_new_data data;
- assert(sink);
- assert(ss);
- assert(q);
+ pa_assert(sink);
+ pa_assert(ss);
- if (pa_memblockq_get_length(q) <= 0) {
+ /* 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 0;
+ return NULL;
}
if (volume && pa_cvolume_is_muted(volume)) {
pa_memblockq_free(q);
- return 0;
+ 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;
+
pa_sink_input_new_data_init(&data);
data.sink = sink;
data.name = name;
data.driver = __FILE__;
- pa_sink_input_new_data_set_channel_map(&data, map);
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 (!(si = pa_sink_input_new(sink->core, &data, 0)))
- return -1;
+ if (!(u->sink_input = pa_sink_input_new(sink->core, &data, 0)))
+ goto fail;
+
+ u->sink_input->peek = sink_input_peek_cb;
+ u->sink_input->drop = sink_input_drop_cb;
+ u->sink_input->kill = sink_input_kill_cb;
+ u->sink_input->userdata = u;
+
+ if (q)
+ pa_memblockq_prebuf_disable(q);
- si->peek = sink_input_peek;
- si->drop = sink_input_drop;
- si->kill = sink_input_kill;
+ /* The reference to u is dangling here, because we want
+ * to keep this stream around until it is fully played. */
- si->userdata = q;
+ /* This sink input is not "put" yet, i.e. pa_sink_input_put() has
+ * not been called! */
- pa_sink_notify(si->sink);
+ return pa_sink_input_ref(u->sink_input);
+
+fail:
+ if (u)
+ memblockq_stream_unref(u);
+
+ return NULL;
+}
+
+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_sink_input *i;
+
+ pa_assert(sink);
+ pa_assert(ss);
+ pa_assert(q);
+
+ if (!(i = pa_memblockq_sink_input_new(sink, name, ss, map, q, volume)))
+ return -1;
+
+ pa_sink_input_put(i);
+ pa_sink_input_unref(i);
return 0;
}
+
+void pa_memblockq_sink_input_set_queue(pa_sink_input *i, pa_memblockq *q) {
+ memblockq_stream *u;
+
+ pa_sink_input_assert_ref(i);
+ u = MEMBLOCKQ_STREAM(i->userdata);
+ memblockq_stream_assert_ref(u);
+
+ if (u->memblockq)
+ pa_memblockq_free(u->memblockq);
+ u->memblockq = q;
+}
diff --git a/src/pulsecore/play-memblockq.h b/src/pulsecore/play-memblockq.h
index 8248e859..d8790316 100644
--- a/src/pulsecore/play-memblockq.h
+++ b/src/pulsecore/play-memblockq.h
@@ -27,6 +27,16 @@
#include <pulsecore/sink.h>
#include <pulsecore/memblockq.h>
+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);
+
+void pa_memblockq_sink_input_set_queue(pa_sink_input *i, pa_memblockq *q);
+
int pa_play_memblockq(
pa_sink *sink,
const char *name,
diff --git a/src/pulsecore/play-memchunk.c b/src/pulsecore/play-memchunk.c
index 9132e294..6aaec567 100644
--- a/src/pulsecore/play-memchunk.c
+++ b/src/pulsecore/play-memchunk.c
@@ -26,7 +26,6 @@
#endif
#include <stdlib.h>
-#include <assert.h>
#include <stdio.h>
#include <string.h>
@@ -34,53 +33,108 @@
#include <pulsecore/sink-input.h>
#include <pulsecore/gccmacro.h>
+#include <pulsecore/thread-mq.h>
#include "play-memchunk.h"
-static void sink_input_kill(pa_sink_input *i) {
- pa_memchunk *c;
- assert(i && i->userdata);
- c = i->userdata;
+typedef struct memchunk_stream {
+ pa_msgobject parent;
+ pa_core *core;
+ pa_sink_input *sink_input;
+ pa_memchunk memchunk;
+} memchunk_stream;
- pa_sink_input_disconnect(i);
- pa_sink_input_unref(i);
+enum {
+ MEMCHUNK_STREAM_MESSAGE_UNLINK,
+};
- pa_memblock_unref(c->memblock);
- pa_xfree(c);
+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 int sink_input_peek(pa_sink_input *i, pa_memchunk *chunk) {
- pa_memchunk *c;
- assert(i && chunk && i->userdata);
- c = i->userdata;
+static void memchunk_stream_free(pa_object *o) {
+ memchunk_stream *u = MEMCHUNK_STREAM(o);
+ pa_assert(u);
- if (c->length <= 0)
- return -1;
+ 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);
- assert(c->memblock && c->memblock->length);
- *chunk = *c;
- pa_memblock_ref(c->memblock);
+ switch (code) {
+ case MEMCHUNK_STREAM_MESSAGE_UNLINK:
+ memchunk_stream_unlink(u);
+ break;
+ }
return 0;
}
-static void si_kill(PA_GCC_UNUSED pa_mainloop_api *m, void *i) {
- sink_input_kill(i);
+static void sink_input_kill_cb(pa_sink_input *i) {
+ pa_sink_input_assert_ref(i);
+
+ memchunk_stream_unlink(MEMCHUNK_STREAM(i->userdata));
}
-static void sink_input_drop(pa_sink_input *i, const pa_memchunk*chunk, size_t length) {
- pa_memchunk *c;
- assert(i && length && i->userdata);
- c = 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);
- assert(!memcmp(chunk, c, sizeof(chunk)));
- assert(length <= c->length);
+ 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;
+ }
- c->length -= length;
- c->index += length;
+ pa_assert(u->memchunk.memblock);
+ *chunk = u->memchunk;
+ pa_memblock_ref(chunk->memblock);
- if (c->length <= 0)
- pa_mainloop_api_once(i->sink->core->mainloop, si_kill, i);
+ 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(
@@ -91,38 +145,52 @@ int pa_play_memchunk(
const pa_memchunk *chunk,
pa_cvolume *volume) {
- pa_sink_input *si;
- pa_memchunk *nchunk;
+ memchunk_stream *u = NULL;
pa_sink_input_new_data data;
- assert(sink);
- assert(ss);
- assert(chunk);
+ 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.name = name;
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 (!(si = pa_sink_input_new(sink->core, &data, 0)))
- return -1;
-
- si->peek = sink_input_peek;
- si->drop = sink_input_drop;
- si->kill = sink_input_kill;
+ if (!(u->sink_input = pa_sink_input_new(sink->core, &data, 0)))
+ goto fail;
- si->userdata = nchunk = pa_xnew(pa_memchunk, 1);
- *nchunk = *chunk;
+ 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_memblock_ref(chunk->memblock);
+ pa_sink_input_put(u->sink_input);
- pa_sink_notify(si->sink);
+ /* The reference to u is dangling here, because we want to keep
+ * this stream around until it is fully played. */
return 0;
+
+fail:
+ if (u)
+ memchunk_stream_unref(u);
+
+ return -1;
}
+
diff --git a/src/pulsecore/poll.c b/src/pulsecore/poll.c
index 2f8eae89..288f7dfb 100644
--- a/src/pulsecore/poll.c
+++ b/src/pulsecore/poll.c
@@ -45,7 +45,7 @@
#include "winsock.h"
-#ifndef HAVE_SYS_POLL_H
+#ifndef HAVE_POLL_H
#include <pulsecore/core-util.h>
diff --git a/src/pulsecore/props.c b/src/pulsecore/props.c
index 4a39f0fb..cbf748df 100644
--- a/src/pulsecore/props.c
+++ b/src/pulsecore/props.c
@@ -21,11 +21,13 @@
USA.
***/
-#include <assert.h>
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
#include <pulse/xmalloc.h>
-
#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
#include "props.h"
@@ -37,9 +39,11 @@ typedef struct pa_property {
/* Allocate a new property object */
static pa_property* property_new(const char *name, void *data) {
pa_property* p;
- assert(name && data);
- p = pa_xmalloc(sizeof(pa_property));
+ pa_assert(name);
+ pa_assert(data);
+
+ p = pa_xnew(pa_property, 1);
p->name = pa_xstrdup(name);
p->data = data;
@@ -48,7 +52,7 @@ static pa_property* property_new(const char *name, void *data) {
/* Free a property object */
static void property_free(pa_property *p) {
- assert(p);
+ pa_assert(p);
pa_xfree(p->name);
pa_xfree(p);
@@ -56,7 +60,10 @@ static void property_free(pa_property *p) {
void* pa_property_get(pa_core *c, const char *name) {
pa_property *p;
- assert(c && name && c->properties);
+
+ pa_assert(c);
+ pa_assert(name);
+ pa_assert(c->properties);
if (!(p = pa_hashmap_get(c->properties, name)))
return NULL;
@@ -66,7 +73,11 @@ void* pa_property_get(pa_core *c, const char *name) {
int pa_property_set(pa_core *c, const char *name, void *data) {
pa_property *p;
- assert(c && name && data && c->properties);
+
+ pa_assert(c);
+ pa_assert(name);
+ pa_assert(data);
+ pa_assert(c->properties);
if (pa_hashmap_get(c->properties, name))
return -1;
@@ -78,7 +89,10 @@ int pa_property_set(pa_core *c, const char *name, void *data) {
int pa_property_remove(pa_core *c, const char *name) {
pa_property *p;
- assert(c && name && c->properties);
+
+ pa_assert(c);
+ pa_assert(name);
+ pa_assert(c->properties);
if (!(p = pa_hashmap_remove(c->properties, name)))
return -1;
@@ -88,18 +102,18 @@ int pa_property_remove(pa_core *c, const char *name) {
}
void pa_property_init(pa_core *c) {
- assert(c);
+ pa_assert(c);
c->properties = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
}
void pa_property_cleanup(pa_core *c) {
- assert(c);
+ pa_assert(c);
if (!c->properties)
return;
- assert(!pa_hashmap_size(c->properties));
+ pa_assert(!pa_hashmap_size(c->properties));
pa_hashmap_free(c->properties, NULL, NULL);
c->properties = NULL;
@@ -109,14 +123,17 @@ void pa_property_cleanup(pa_core *c) {
void pa_property_dump(pa_core *c, pa_strbuf *s) {
void *state = NULL;
pa_property *p;
- assert(c && s);
+
+ pa_assert(c);
+ pa_assert(s);
while ((p = pa_hashmap_iterate(c->properties, &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) {
- assert(c && name);
+ pa_assert(c);
+ pa_assert(name);
pa_property_remove(c, name);
return pa_property_set(c, name, data);
diff --git a/src/pulsecore/protocol-cli.c b/src/pulsecore/protocol-cli.c
index 1d543ae5..ceb6ae4d 100644
--- a/src/pulsecore/protocol-cli.c
+++ b/src/pulsecore/protocol-cli.c
@@ -25,13 +25,13 @@
#include <config.h>
#endif
-#include <assert.h>
#include <stdlib.h>
#include <pulse/xmalloc.h>
#include <pulsecore/cli.h>
#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
#include "protocol-cli.h"
@@ -47,7 +47,8 @@ struct pa_protocol_cli {
static void cli_eof_cb(pa_cli*c, void*userdata) {
pa_protocol_cli *p = userdata;
- assert(p);
+ pa_assert(p);
+
pa_idxset_remove_by_data(p->connections, c, NULL);
pa_cli_free(c);
}
@@ -55,7 +56,10 @@ static void cli_eof_cb(pa_cli*c, void*userdata) {
static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata) {
pa_protocol_cli *p = userdata;
pa_cli *c;
- assert(s && io && p);
+
+ pa_assert(s);
+ pa_assert(io);
+ pa_assert(p);
if (pa_idxset_size(p->connections)+1 > MAX_CONNECTIONS) {
pa_log("Warning! Too many connections (%u), dropping incoming connection.", MAX_CONNECTIONS);
@@ -64,7 +68,6 @@ static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata)
}
c = pa_cli_new(p->core, io, p->module);
- assert(c);
pa_cli_set_eof_callback(c, cli_eof_cb, p);
pa_idxset_put(p->connections, c, NULL);
@@ -72,9 +75,11 @@ static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata)
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;
- assert(core && server);
- p = pa_xmalloc(sizeof(pa_protocol_cli));
+ pa_core_assert_ref(core);
+ pa_assert(server);
+
+ p = pa_xnew(pa_protocol_cli, 1);
p->module = m;
p->core = core;
p->server = server;
@@ -86,12 +91,13 @@ pa_protocol_cli* pa_protocol_cli_new(pa_core *core, pa_socket_server *server, pa
}
static void free_connection(void *p, PA_GCC_UNUSED void *userdata) {
- assert(p);
+ pa_assert(p);
+
pa_cli_free(p);
}
void pa_protocol_cli_free(pa_protocol_cli *p) {
- assert(p);
+ pa_assert(p);
pa_idxset_free(p->connections, free_connection, NULL);
pa_socket_server_unref(p->server);
diff --git a/src/pulsecore/protocol-esound.c b/src/pulsecore/protocol-esound.c
index 49a78d41..76ba9dd0 100644
--- a/src/pulsecore/protocol-esound.c
+++ b/src/pulsecore/protocol-esound.c
@@ -29,7 +29,6 @@
#include <errno.h>
#include <string.h>
#include <stdio.h>
-#include <assert.h>
#include <stdlib.h>
#include <limits.h>
@@ -53,6 +52,8 @@
#include <pulsecore/core-util.h>
#include <pulsecore/core-error.h>
#include <pulsecore/ipacl.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/thread-mq.h>
#include "endianmacros.h"
@@ -77,13 +78,15 @@
/* This is heavily based on esound's code */
-struct connection {
+typedef struct connection {
+ pa_msgobject parent;
+
uint32_t index;
- int dead;
+ pa_bool_t dead;
pa_protocol_esound *protocol;
pa_iochannel *io;
pa_client *client;
- int authorized, swap_byte_order;
+ pa_bool_t authorized, swap_byte_order;
void *write_data;
size_t write_data_alloc, write_data_index, write_data_length;
void *read_data;
@@ -100,6 +103,7 @@ struct connection {
struct {
pa_memblock *current_memblock;
size_t memblock_index, fragment_size;
+ pa_atomic_t missing;
} playback;
struct {
@@ -109,46 +113,62 @@ struct connection {
} scache;
pa_time_event *auth_timeout_event;
-};
+} connection;
+
+PA_DECLARE_CLASS(connection);
+#define CONNECTION(o) (connection_cast(o))
+static PA_DEFINE_CHECK_TYPE(connection, pa_msgobject);
struct pa_protocol_esound {
- int public;
pa_module *module;
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 {
+ SINK_INPUT_MESSAGE_POST_DATA = PA_SINK_INPUT_MESSAGE_MAX, /* data from main loop to sink input */
+ SINK_INPUT_MESSAGE_DISABLE_PREBUF
+};
+
+enum {
+ CONNECTION_MESSAGE_REQUEST_DATA,
+ CONNECTION_MESSAGE_POST_DATA,
+ CONNECTION_MESSAGE_UNLINK_CONNECTION
+};
+
typedef struct proto_handler {
size_t data_length;
- int (*proc)(struct connection *c, esd_proto_t request, const void *data, size_t length);
+ int (*proc)(connection *c, esd_proto_t request, const void *data, size_t length);
const char *description;
} esd_proto_handler_info_t;
-static void sink_input_drop_cb(pa_sink_input *i, const pa_memchunk *chunk, size_t length);
-static int sink_input_peek_cb(pa_sink_input *i, pa_memchunk *chunk);
+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 void sink_input_kill_cb(pa_sink_input *i);
-static pa_usec_t sink_input_get_latency_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);
static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk);
static void source_output_kill_cb(pa_source_output *o);
-static int esd_proto_connect(struct connection *c, esd_proto_t request, const void *data, size_t length);
-static int esd_proto_stream_play(struct connection *c, esd_proto_t request, const void *data, size_t length);
-static int esd_proto_stream_record(struct connection *c, esd_proto_t request, const void *data, size_t length);
-static int esd_proto_get_latency(struct connection *c, esd_proto_t request, const void *data, size_t length);
-static int esd_proto_server_info(struct connection *c, esd_proto_t request, const void *data, size_t length);
-static int esd_proto_all_info(struct connection *c, esd_proto_t request, const void *data, size_t length);
-static int esd_proto_stream_pan(struct connection *c, esd_proto_t request, const void *data, size_t length);
-static int esd_proto_sample_cache(struct connection *c, esd_proto_t request, const void *data, size_t length);
-static int esd_proto_sample_free_or_play(struct connection *c, esd_proto_t request, const void *data, size_t length);
-static int esd_proto_sample_get_id(struct connection *c, esd_proto_t request, const void *data, size_t length);
-static int esd_proto_standby_or_resume(struct connection *c, 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);
+static int esd_proto_stream_play(connection *c, esd_proto_t request, const void *data, size_t length);
+static int esd_proto_stream_record(connection *c, 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);
+static int esd_proto_server_info(connection *c, esd_proto_t request, const void *data, size_t length);
+static int esd_proto_all_info(connection *c, 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);
+static int esd_proto_sample_cache(connection *c, esd_proto_t request, const void *data, size_t length);
+static int esd_proto_sample_free_or_play(connection *c, 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);
+static int esd_proto_standby_or_resume(connection *c, esd_proto_t request, const void *data, size_t length);
/* the big map of protocol handler info */
static struct proto_handler proto_map[ESD_PROTO_MAX] = {
@@ -185,25 +205,56 @@ static struct proto_handler proto_map[ESD_PROTO_MAX] = {
{ 0, esd_proto_get_latency, "get latency" }
};
-static void connection_free(struct connection *c) {
- assert(c);
- pa_idxset_remove_by_data(c->protocol->connections, c, NULL);
+static void connection_unlink(connection *c) {
+ pa_assert(c);
- if (c->state == ESD_STREAMING_DATA)
- c->protocol->n_player--;
-
- pa_client_free(c->client);
+ if (!c->protocol)
+ return;
if (c->sink_input) {
- pa_sink_input_disconnect(c->sink_input);
+ pa_sink_input_unlink(c->sink_input);
pa_sink_input_unref(c->sink_input);
+ c->sink_input = NULL;
}
if (c->source_output) {
- pa_source_output_disconnect(c->source_output);
+ pa_source_output_unlink(c->source_output);
pa_source_output_unref(c->source_output);
+ c->source_output = NULL;
}
+ if (c->client) {
+ pa_client_free(c->client);
+ c->client = NULL;
+ }
+
+ if (c->state == ESD_STREAMING_DATA)
+ c->protocol->n_player--;
+
+ if (c->io) {
+ pa_iochannel_free(c->io);
+ c->io = NULL;
+ }
+
+ if (c->defer_event) {
+ c->protocol->core->mainloop->defer_free(c->defer_event);
+ c->defer_event = NULL;
+ }
+
+ if (c->auth_timeout_event) {
+ c->protocol->core->mainloop->time_free(c->auth_timeout_event);
+ c->auth_timeout_event = NULL;
+ }
+
+ pa_assert_se(pa_idxset_remove_by_data(c->protocol->connections, c, NULL) == c);
+ c->protocol = NULL;
+ connection_unref(c);
+}
+
+static void connection_free(pa_object *obj) {
+ connection *c = CONNECTION(obj);
+ pa_assert(c);
+
if (c->input_memblockq)
pa_memblockq_free(c->input_memblockq);
if (c->output_memblockq)
@@ -215,54 +266,44 @@ static void connection_free(struct connection *c) {
pa_xfree(c->read_data);
pa_xfree(c->write_data);
- if (c->io)
- pa_iochannel_free(c->io);
-
- if (c->defer_event)
- c->protocol->core->mainloop->defer_free(c->defer_event);
-
if (c->scache.memchunk.memblock)
pa_memblock_unref(c->scache.memchunk.memblock);
pa_xfree(c->scache.name);
- if (c->auth_timeout_event)
- c->protocol->core->mainloop->time_free(c->auth_timeout_event);
-
pa_xfree(c->original_name);
pa_xfree(c);
}
-static void connection_write_prepare(struct connection *c, size_t length) {
+static void connection_write_prepare(connection *c, size_t length) {
size_t t;
- assert(c);
+ pa_assert(c);
t = c->write_data_length+length;
if (c->write_data_alloc < t)
c->write_data = pa_xrealloc(c->write_data, c->write_data_alloc = t);
- assert(c->write_data);
+ pa_assert(c->write_data);
}
-static void connection_write(struct connection *c, const void *data, size_t length) {
+static void connection_write(connection *c, const void *data, size_t length) {
size_t i;
- assert(c);
+ pa_assert(c);
- assert(c->protocol && c->protocol->core && c->protocol->core->mainloop && c->protocol->core->mainloop->defer_enable);
c->protocol->core->mainloop->defer_enable(c->defer_event, 1);
connection_write_prepare(c, length);
- assert(c->write_data);
+ pa_assert(c->write_data);
i = c->write_data_length;
c->write_data_length += length;
- memcpy((char*)c->write_data + i, data, length);
+ memcpy((uint8_t*) c->write_data + i, data, length);
}
-static void format_esd2native(int format, int swap_bytes, pa_sample_spec *ss) {
- assert(ss);
+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;
if ((format & ESD_MASK_BITS) == ESD_BITS16)
@@ -289,11 +330,13 @@ static int format_native2esd(pa_sample_spec *ss) {
/*** esound commands ***/
-static int esd_proto_connect(struct connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) {
+static int esd_proto_connect(connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) {
uint32_t ekey;
int ok;
- assert(length == (ESD_KEY_LEN + sizeof(uint32_t)));
+ connection_assert_ref(c);
+ pa_assert(data);
+ pa_assert(length == (ESD_KEY_LEN + sizeof(uint32_t)));
if (!c->authorized) {
if (memcmp(data, c->protocol->esd_key, ESD_KEY_LEN) != 0) {
@@ -301,7 +344,7 @@ static int esd_proto_connect(struct connection *c, PA_GCC_UNUSED esd_proto_t req
return -1;
}
- 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;
@@ -312,11 +355,11 @@ static int esd_proto_connect(struct connection *c, PA_GCC_UNUSED esd_proto_t req
memcpy(&ekey, data, sizeof(uint32_t));
if (ekey == ESD_ENDIAN_KEY)
- c->swap_byte_order = 0;
+ c->swap_byte_order = FALSE;
else if (ekey == ESD_SWAP_ENDIAN_KEY)
- c->swap_byte_order = 1;
+ c->swap_byte_order = TRUE;
else {
- pa_log("client sent invalid endian key");
+ pa_log_warn("Client sent invalid endian key");
return -1;
}
@@ -325,7 +368,7 @@ static int esd_proto_connect(struct connection *c, PA_GCC_UNUSED esd_proto_t req
return 0;
}
-static int esd_proto_stream_play(struct connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) {
+static int esd_proto_stream_play(connection *c, PA_GCC_UNUSED 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;
@@ -333,15 +376,17 @@ static int esd_proto_stream_play(struct connection *c, PA_GCC_UNUSED esd_proto_t
pa_sink *sink = NULL;
pa_sink_input_new_data sdata;
- assert(c && length == (sizeof(int32_t)*2+ESD_NAME_MAX));
+ connection_assert_ref(c);
+ pa_assert(data);
+ pa_assert(length == (sizeof(int32_t)*2+ESD_NAME_MAX));
memcpy(&format, data, sizeof(int32_t));
- format = MAYBE_INT32_SWAP(c->swap_byte_order, format);
- data = (const char*)data + sizeof(int32_t);
+ format = PA_MAYBE_INT32_SWAP(c->swap_byte_order, format);
+ data = (const char*) data + sizeof(int32_t);
memcpy(&rate, data, sizeof(int32_t));
- rate = MAYBE_INT32_SWAP(c->swap_byte_order, rate);
- data = (const char*)data + sizeof(int32_t);
+ rate = PA_MAYBE_INT32_SWAP(c->swap_byte_order, rate);
+ data = (const char*) data + sizeof(int32_t);
ss.rate = rate;
format_esd2native(format, c->swap_byte_order, &ss);
@@ -362,7 +407,7 @@ static int esd_proto_stream_play(struct connection *c, PA_GCC_UNUSED esd_proto_t
c->original_name = pa_xstrdup(name);
- assert(!c->sink_input && !c->input_memblockq);
+ pa_assert(!c->sink_input && !c->input_memblockq);
pa_sink_input_new_data_init(&sdata);
sdata.sink = sink;
@@ -385,22 +430,26 @@ static int esd_proto_stream_play(struct connection *c, PA_GCC_UNUSED esd_proto_t
l/PLAYBACK_BUFFER_FRAGMENTS,
NULL);
pa_iochannel_socket_set_rcvbuf(c->io, l/PLAYBACK_BUFFER_FRAGMENTS*2);
- c->playback.fragment_size = l/10;
+ c->playback.fragment_size = l/PLAYBACK_BUFFER_FRAGMENTS;
+ 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->kill = sink_input_kill_cb;
- c->sink_input->get_latency = sink_input_get_latency_cb;
c->sink_input->userdata = c;
c->state = ESD_STREAMING_DATA;
c->protocol->n_player++;
+ pa_atomic_store(&c->playback.missing, pa_memblockq_missing(c->input_memblockq));
+
+ pa_sink_input_put(c->sink_input);
+
return 0;
}
-static int esd_proto_stream_record(struct connection *c, esd_proto_t request, const void *data, size_t length) {
+static int esd_proto_stream_record(connection *c, esd_proto_t request, const void *data, size_t length) {
char name[ESD_NAME_MAX], *utf8_name;
int32_t format, rate;
pa_source *source = NULL;
@@ -408,15 +457,17 @@ static int esd_proto_stream_record(struct connection *c, esd_proto_t request, co
size_t l;
pa_source_output_new_data sdata;
- assert(c && length == (sizeof(int32_t)*2+ESD_NAME_MAX));
+ connection_assert_ref(c);
+ pa_assert(data);
+ pa_assert(length == (sizeof(int32_t)*2+ESD_NAME_MAX));
memcpy(&format, data, sizeof(int32_t));
- format = MAYBE_INT32_SWAP(c->swap_byte_order, format);
- data = (const char*)data + sizeof(int32_t);
+ format = PA_MAYBE_INT32_SWAP(c->swap_byte_order, format);
+ data = (const char*) data + sizeof(int32_t);
memcpy(&rate, data, sizeof(int32_t));
- rate = MAYBE_INT32_SWAP(c->swap_byte_order, rate);
- data = (const char*)data + sizeof(int32_t);
+ rate = PA_MAYBE_INT32_SWAP(c->swap_byte_order, rate);
+ data = (const char*) data + sizeof(int32_t);
ss.rate = rate;
format_esd2native(format, c->swap_byte_order, &ss);
@@ -436,7 +487,7 @@ static int esd_proto_stream_record(struct connection *c, esd_proto_t request, co
return -1;
}
} else {
- assert(request == ESD_PROTO_STREAM_REC);
+ 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))) {
@@ -455,7 +506,7 @@ static int esd_proto_stream_record(struct connection *c, esd_proto_t request, co
c->original_name = pa_xstrdup(name);
- assert(!c->output_memblockq && !c->source_output);
+ pa_assert(!c->output_memblockq && !c->source_output);
pa_source_output_new_data_init(&sdata);
sdata.source = source;
@@ -488,14 +539,18 @@ static int esd_proto_stream_record(struct connection *c, esd_proto_t request, co
c->protocol->n_player++;
+ pa_source_output_put(c->source_output);
+
return 0;
}
-static int esd_proto_get_latency(struct connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) {
+static int esd_proto_get_latency(connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) {
pa_sink *sink;
int32_t latency;
- assert(c && !data && length == 0);
+ connection_ref(c);
+ pa_assert(!data);
+ pa_assert(length == 0);
if (!(sink = pa_namereg_get(c->protocol->core, c->protocol->sink_name, PA_NAMEREG_SINK, 1)))
latency = 0;
@@ -504,17 +559,19 @@ static int esd_proto_get_latency(struct connection *c, PA_GCC_UNUSED esd_proto_t
latency = (int) ((usec*44100)/1000000);
}
- latency = MAYBE_INT32_SWAP(c->swap_byte_order, latency);
+ latency = PA_MAYBE_INT32_SWAP(c->swap_byte_order, latency);
connection_write(c, &latency, sizeof(int32_t));
return 0;
}
-static int esd_proto_server_info(struct connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) {
+static int esd_proto_server_info(connection *c, PA_GCC_UNUSED 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;
- assert(c && data && length == sizeof(int32_t));
+ connection_ref(c);
+ 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;
@@ -525,22 +582,24 @@ static int esd_proto_server_info(struct connection *c, PA_GCC_UNUSED esd_proto_t
response = 0;
connection_write(c, &response, sizeof(int32_t));
- rate = MAYBE_INT32_SWAP(c->swap_byte_order, rate);
+ rate = PA_MAYBE_INT32_SWAP(c->swap_byte_order, rate);
connection_write(c, &rate, sizeof(int32_t));
- format = MAYBE_INT32_SWAP(c->swap_byte_order, format);
+ format = PA_MAYBE_INT32_SWAP(c->swap_byte_order, format);
connection_write(c, &format, sizeof(int32_t));
return 0;
}
-static int esd_proto_all_info(struct connection *c, esd_proto_t request, const void *data, size_t length) {
+static int esd_proto_all_info(connection *c, esd_proto_t request, const void *data, size_t length) {
size_t t, k, s;
- struct connection *conn;
+ connection *conn;
uint32_t idx = PA_IDXSET_INVALID;
unsigned nsamples;
char terminator[sizeof(int32_t)*6+ESD_NAME_MAX];
- assert(c && data && length == sizeof(int32_t));
+ connection_ref(c);
+ pa_assert(data);
+ pa_assert(length == sizeof(int32_t));
if (esd_proto_server_info(c, request, data, length) < 0)
return -1;
@@ -561,7 +620,7 @@ static int esd_proto_all_info(struct connection *c, esd_proto_t request, const v
if (conn->state != ESD_STREAMING_DATA)
continue;
- assert(t >= k*2+s);
+ pa_assert(t >= k*2+s);
if (conn->sink_input) {
pa_cvolume volume = *pa_sink_input_get_volume(conn->sink_input);
@@ -572,7 +631,7 @@ static int esd_proto_all_info(struct connection *c, esd_proto_t request, const v
}
/* id */
- id = MAYBE_INT32_SWAP(c->swap_byte_order, (int32_t) (conn->index+1));
+ id = PA_MAYBE_INT32_SWAP(c->swap_byte_order, (int32_t) (conn->index+1));
connection_write(c, &id, sizeof(int32_t));
/* name */
@@ -584,25 +643,25 @@ static int esd_proto_all_info(struct connection *c, esd_proto_t request, const v
connection_write(c, name, ESD_NAME_MAX);
/* rate */
- rate = MAYBE_INT32_SWAP(c->swap_byte_order, rate);
+ rate = PA_MAYBE_INT32_SWAP(c->swap_byte_order, rate);
connection_write(c, &rate, sizeof(int32_t));
/* left */
- lvolume = MAYBE_INT32_SWAP(c->swap_byte_order, lvolume);
+ lvolume = PA_MAYBE_INT32_SWAP(c->swap_byte_order, lvolume);
connection_write(c, &lvolume, sizeof(int32_t));
/*right*/
- rvolume = MAYBE_INT32_SWAP(c->swap_byte_order, rvolume);
+ rvolume = PA_MAYBE_INT32_SWAP(c->swap_byte_order, rvolume);
connection_write(c, &rvolume, sizeof(int32_t));
/*format*/
- format = MAYBE_INT32_SWAP(c->swap_byte_order, format);
+ format = PA_MAYBE_INT32_SWAP(c->swap_byte_order, format);
connection_write(c, &format, sizeof(int32_t));
t -= k;
}
- assert(t == s*(nsamples+1)+k);
+ pa_assert(t == s*(nsamples+1)+k);
t -= k;
connection_write(c, terminator, k);
@@ -615,10 +674,10 @@ static int esd_proto_all_info(struct connection *c, esd_proto_t request, const v
int32_t id, rate, lvolume, rvolume, format, len;
char name[ESD_NAME_MAX];
- assert(t >= s*2);
+ pa_assert(t >= s*2);
/* id */
- id = MAYBE_INT32_SWAP(c->swap_byte_order, (int) (ce->index+1));
+ id = PA_MAYBE_INT32_SWAP(c->swap_byte_order, (int) (ce->index+1));
connection_write(c, &id, sizeof(int32_t));
/* name */
@@ -626,57 +685,59 @@ static int esd_proto_all_info(struct connection *c, esd_proto_t request, const v
if (strncmp(ce->name, SCACHE_PREFIX, sizeof(SCACHE_PREFIX)-1) == 0)
strncpy(name, ce->name+sizeof(SCACHE_PREFIX)-1, ESD_NAME_MAX);
else
- snprintf(name, ESD_NAME_MAX, "native.%s", ce->name);
+ pa_snprintf(name, ESD_NAME_MAX, "native.%s", ce->name);
connection_write(c, name, ESD_NAME_MAX);
/* rate */
- rate = MAYBE_UINT32_SWAP(c->swap_byte_order, ce->sample_spec.rate);
+ rate = PA_MAYBE_UINT32_SWAP(c->swap_byte_order, ce->sample_spec.rate);
connection_write(c, &rate, sizeof(int32_t));
/* left */
- lvolume = MAYBE_UINT32_SWAP(c->swap_byte_order, (ce->volume.values[0]*ESD_VOLUME_BASE)/PA_VOLUME_NORM);
+ lvolume = PA_MAYBE_UINT32_SWAP(c->swap_byte_order, (ce->volume.values[0]*ESD_VOLUME_BASE)/PA_VOLUME_NORM);
connection_write(c, &lvolume, sizeof(int32_t));
/*right*/
- rvolume = MAYBE_UINT32_SWAP(c->swap_byte_order, (ce->volume.values[0]*ESD_VOLUME_BASE)/PA_VOLUME_NORM);
+ rvolume = PA_MAYBE_UINT32_SWAP(c->swap_byte_order, (ce->volume.values[0]*ESD_VOLUME_BASE)/PA_VOLUME_NORM);
connection_write(c, &rvolume, sizeof(int32_t));
/*format*/
- format = MAYBE_INT32_SWAP(c->swap_byte_order, format_native2esd(&ce->sample_spec));
+ format = PA_MAYBE_INT32_SWAP(c->swap_byte_order, format_native2esd(&ce->sample_spec));
connection_write(c, &format, sizeof(int32_t));
/*length*/
- len = MAYBE_INT32_SWAP(c->swap_byte_order, (int) ce->memchunk.length);
+ len = PA_MAYBE_INT32_SWAP(c->swap_byte_order, (int) ce->memchunk.length);
connection_write(c, &len, sizeof(int32_t));
t -= s;
}
}
- assert(t == s);
+ pa_assert(t == s);
connection_write(c, terminator, s);
return 0;
}
-static int esd_proto_stream_pan(struct connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) {
+static int esd_proto_stream_pan(connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) {
int32_t ok;
uint32_t idx, lvolume, rvolume;
- struct connection *conn;
+ connection *conn;
- assert(c && data && length == sizeof(int32_t)*3);
+ connection_assert_ref(c);
+ pa_assert(data);
+ pa_assert(length == sizeof(int32_t)*3);
memcpy(&idx, data, sizeof(uint32_t));
- idx = MAYBE_UINT32_SWAP(c->swap_byte_order, idx) - 1;
+ idx = PA_MAYBE_UINT32_SWAP(c->swap_byte_order, idx) - 1;
data = (const char*)data + sizeof(uint32_t);
memcpy(&lvolume, data, sizeof(uint32_t));
- lvolume = MAYBE_UINT32_SWAP(c->swap_byte_order, lvolume);
+ lvolume = PA_MAYBE_UINT32_SWAP(c->swap_byte_order, lvolume);
data = (const char*)data + sizeof(uint32_t);
memcpy(&rvolume, data, sizeof(uint32_t));
- rvolume = MAYBE_UINT32_SWAP(c->swap_byte_order, rvolume);
+ rvolume = PA_MAYBE_UINT32_SWAP(c->swap_byte_order, rvolume);
data = (const char*)data + sizeof(uint32_t);
if ((conn = pa_idxset_get_by_index(c->protocol->connections, idx)) && conn->sink_input) {
@@ -694,20 +755,22 @@ static int esd_proto_stream_pan(struct connection *c, PA_GCC_UNUSED esd_proto_t
return 0;
}
-static int esd_proto_sample_cache(struct connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) {
+static int esd_proto_sample_cache(connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) {
pa_sample_spec ss;
int32_t format, rate, sc_length;
uint32_t idx;
char name[ESD_NAME_MAX+sizeof(SCACHE_PREFIX)-1];
- assert(c && data && length == (ESD_NAME_MAX+3*sizeof(int32_t)));
+ connection_assert_ref(c);
+ pa_assert(data);
+ pa_assert(length == (ESD_NAME_MAX+3*sizeof(int32_t)));
memcpy(&format, data, sizeof(int32_t));
- format = MAYBE_INT32_SWAP(c->swap_byte_order, format);
+ format = PA_MAYBE_INT32_SWAP(c->swap_byte_order, format);
data = (const char*)data + sizeof(int32_t);
memcpy(&rate, data, sizeof(int32_t));
- rate = MAYBE_INT32_SWAP(c->swap_byte_order, rate);
+ rate = PA_MAYBE_INT32_SWAP(c->swap_byte_order, rate);
data = (const char*)data + sizeof(int32_t);
ss.rate = rate;
@@ -716,7 +779,7 @@ static int esd_proto_sample_cache(struct connection *c, PA_GCC_UNUSED esd_proto_
CHECK_VALIDITY(pa_sample_spec_valid(&ss), "Invalid sample specification.");
memcpy(&sc_length, data, sizeof(int32_t));
- sc_length = MAYBE_INT32_SWAP(c->swap_byte_order, sc_length);
+ sc_length = PA_MAYBE_INT32_SWAP(c->swap_byte_order, sc_length);
data = (const char*)data + sizeof(int32_t);
CHECK_VALIDITY(sc_length <= MAX_CACHE_SAMPLE_SIZE, "Sample too large (%d bytes).", (int)sc_length);
@@ -727,12 +790,12 @@ static int esd_proto_sample_cache(struct connection *c, PA_GCC_UNUSED esd_proto_
CHECK_VALIDITY(pa_utf8_valid(name), "Invalid UTF8 in sample name.");
- assert(!c->scache.memchunk.memblock);
+ pa_assert(!c->scache.memchunk.memblock);
c->scache.memchunk.memblock = pa_memblock_new(c->protocol->core->mempool, sc_length);
c->scache.memchunk.index = 0;
c->scache.memchunk.length = sc_length;
c->scache.sample_spec = ss;
- assert(!c->scache.name);
+ pa_assert(!c->scache.name);
c->scache.name = pa_xstrdup(name);
c->state = ESD_CACHING_SAMPLE;
@@ -745,12 +808,14 @@ static int esd_proto_sample_cache(struct connection *c, PA_GCC_UNUSED esd_proto_
return 0;
}
-static int esd_proto_sample_get_id(struct connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) {
+static int esd_proto_sample_get_id(connection *c, PA_GCC_UNUSED 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];
- assert(c && data && length == ESD_NAME_MAX);
+ connection_assert_ref(c);
+ pa_assert(data);
+ pa_assert(length == ESD_NAME_MAX);
strcpy(name, SCACHE_PREFIX);
strncpy(name+sizeof(SCACHE_PREFIX)-1, data, ESD_NAME_MAX);
@@ -767,15 +832,17 @@ static int esd_proto_sample_get_id(struct connection *c, PA_GCC_UNUSED esd_proto
return 0;
}
-static int esd_proto_sample_free_or_play(struct connection *c, esd_proto_t request, const void *data, size_t length) {
+static int esd_proto_sample_free_or_play(connection *c, esd_proto_t request, const void *data, size_t length) {
int32_t ok;
const char *name;
uint32_t idx;
- assert(c && data && length == sizeof(int32_t));
+ connection_assert_ref(c);
+ pa_assert(data);
+ pa_assert(length == sizeof(int32_t));
memcpy(&idx, data, sizeof(uint32_t));
- idx = MAYBE_UINT32_SWAP(c->swap_byte_order, idx) - 1;
+ idx = PA_MAYBE_UINT32_SWAP(c->swap_byte_order, idx) - 1;
ok = 0;
@@ -787,7 +854,7 @@ static int esd_proto_sample_free_or_play(struct connection *c, esd_proto_t reque
if (pa_scache_play_item(c->protocol->core, name, sink, PA_VOLUME_NORM) >= 0)
ok = idx + 1;
} else {
- assert(request == ESD_PROTO_SAMPLE_FREE);
+ pa_assert(request == ESD_PROTO_SAMPLE_FREE);
if (pa_scache_remove_item(c->protocol->core, name) >= 0)
ok = idx + 1;
@@ -799,9 +866,11 @@ static int esd_proto_sample_free_or_play(struct connection *c, esd_proto_t reque
return 0;
}
-static int esd_proto_standby_or_resume(struct 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, PA_GCC_UNUSED esd_proto_t request, PA_GCC_UNUSED const void *data, PA_GCC_UNUSED size_t length) {
int32_t ok;
+ connection_assert_ref(c);
+
connection_write_prepare(c, sizeof(int32_t) * 2);
ok = 1;
@@ -814,20 +883,21 @@ static int esd_proto_standby_or_resume(struct connection *c, PA_GCC_UNUSED esd_p
/*** client callbacks ***/
static void client_kill_cb(pa_client *c) {
- assert(c && c->userdata);
- connection_free(c->userdata);
+ pa_assert(c);
+
+ connection_unlink(CONNECTION(c->userdata));
}
/*** pa_iochannel callbacks ***/
-static int do_read(struct connection *c) {
- assert(c && c->io);
+static int do_read(connection *c) {
+ connection_assert_ref(c);
-/* pa_log("READ"); */
+/* pa_log("READ"); */
if (c->state == ESD_NEXT_REQUEST) {
ssize_t r;
- assert(c->read_data_length < sizeof(c->request));
+ pa_assert(c->read_data_length < sizeof(c->request));
if ((r = pa_iochannel_read(c->io, ((uint8_t*) &c->request) + c->read_data_length, sizeof(c->request) - c->read_data_length)) <= 0) {
pa_log_debug("read(): %s", r < 0 ? pa_cstrerror(errno) : "EOF");
@@ -837,7 +907,7 @@ static int do_read(struct connection *c) {
if ((c->read_data_length+= r) >= sizeof(c->request)) {
struct proto_handler *handler;
- c->request = MAYBE_INT32_SWAP(c->swap_byte_order, c->request);
+ c->request = PA_MAYBE_INT32_SWAP(c->swap_byte_order, c->request);
if (c->request < ESD_PROTO_CONNECT || c->request > ESD_PROTO_MAX) {
pa_log("recieved invalid request.");
@@ -862,7 +932,7 @@ static int do_read(struct connection *c) {
} else {
if (c->read_data_alloc < handler->data_length)
c->read_data = pa_xrealloc(c->read_data, c->read_data_alloc = handler->data_length);
- assert(c->read_data);
+ pa_assert(c->read_data);
c->state = ESD_NEEDS_REQDATA;
c->read_data_length = 0;
@@ -873,18 +943,21 @@ static int do_read(struct connection *c) {
ssize_t r;
struct proto_handler *handler = proto_map+c->request;
- assert(handler->proc);
+ pa_assert(handler->proc);
- assert(c->read_data && c->read_data_length < handler->data_length);
+ pa_assert(c->read_data && c->read_data_length < handler->data_length);
if ((r = pa_iochannel_read(c->io, (uint8_t*) c->read_data + c->read_data_length, handler->data_length - c->read_data_length)) <= 0) {
+ if (r < 0 && (errno == EINTR || errno == EAGAIN))
+ return 0;
+
pa_log_debug("read(): %s", r < 0 ? pa_cstrerror(errno) : "EOF");
return -1;
}
if ((c->read_data_length += r) >= handler->data_length) {
size_t l = c->read_data_length;
- assert(handler->proc);
+ pa_assert(handler->proc);
c->state = ESD_NEXT_REQUEST;
c->read_data_length = 0;
@@ -894,16 +967,26 @@ static int do_read(struct connection *c) {
}
} else if (c->state == ESD_CACHING_SAMPLE) {
ssize_t r;
+ void *p;
- assert(c->scache.memchunk.memblock && c->scache.name && c->scache.memchunk.index < c->scache.memchunk.length);
+ pa_assert(c->scache.memchunk.memblock);
+ pa_assert(c->scache.name);
+ pa_assert(c->scache.memchunk.index < c->scache.memchunk.length);
+
+ p = pa_memblock_acquire(c->scache.memchunk.memblock);
+ r = pa_iochannel_read(c->io, (uint8_t*) p+c->scache.memchunk.index, c->scache.memchunk.length-c->scache.memchunk.index);
+ pa_memblock_release(c->scache.memchunk.memblock);
+
+ if (r <= 0) {
+ if (r < 0 && (errno == EINTR || errno == EAGAIN))
+ return 0;
- if ((r = pa_iochannel_read(c->io, (uint8_t*) c->scache.memchunk.memblock->data+c->scache.memchunk.index, c->scache.memchunk.length-c->scache.memchunk.index)) <= 0) {
pa_log_debug("read(): %s", r < 0 ? pa_cstrerror(errno) : "EOF");
return -1;
}
c->scache.memchunk.index += r;
- assert(c->scache.memchunk.index <= c->scache.memchunk.length);
+ pa_assert(c->scache.memchunk.index <= c->scache.memchunk.length);
if (c->scache.memchunk.index == c->scache.memchunk.length) {
uint32_t idx;
@@ -928,31 +1011,39 @@ static int do_read(struct connection *c) {
pa_memchunk chunk;
ssize_t r;
size_t l;
+ void *p;
- assert(c->input_memblockq);
+ pa_assert(c->input_memblockq);
/* pa_log("STREAMING_DATA"); */
- if (!(l = pa_memblockq_missing(c->input_memblockq)))
+ if (!(l = pa_atomic_load(&c->playback.missing)))
return 0;
if (l > c->playback.fragment_size)
l = c->playback.fragment_size;
if (c->playback.current_memblock)
- if (c->playback.current_memblock->length - c->playback.memblock_index < l) {
+ if (pa_memblock_get_length(c->playback.current_memblock) - c->playback.memblock_index < l) {
pa_memblock_unref(c->playback.current_memblock);
c->playback.current_memblock = NULL;
c->playback.memblock_index = 0;
}
if (!c->playback.current_memblock) {
- c->playback.current_memblock = pa_memblock_new(c->protocol->core->mempool, c->playback.fragment_size*2);
- assert(c->playback.current_memblock && c->playback.current_memblock->length >= l);
+ pa_assert_se(c->playback.current_memblock = pa_memblock_new(c->protocol->core->mempool, c->playback.fragment_size*2));
c->playback.memblock_index = 0;
}
- if ((r = pa_iochannel_read(c->io, (uint8_t*) c->playback.current_memblock->data+c->playback.memblock_index, l)) <= 0) {
+ 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);
+
+ if (r <= 0) {
+
+ if (r < 0 && (errno == EINTR || errno == EAGAIN))
+ return 0;
+
pa_log_debug("read(): %s", r < 0 ? pa_cstrerror(errno) : "EOF");
return -1;
}
@@ -960,29 +1051,30 @@ static int do_read(struct connection *c) {
chunk.memblock = c->playback.current_memblock;
chunk.index = c->playback.memblock_index;
chunk.length = r;
- assert(chunk.memblock);
c->playback.memblock_index += r;
- assert(c->input_memblockq);
- pa_memblockq_push_align(c->input_memblockq, &chunk);
- assert(c->sink_input);
- pa_sink_notify(c->sink_input->sink);
+ 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);
}
return 0;
}
-static int do_write(struct connection *c) {
- assert(c && c->io);
+static int do_write(connection *c) {
+ connection_assert_ref(c);
/* pa_log("WRITE"); */
if (c->write_data_length) {
ssize_t r;
- assert(c->write_data_index < c->write_data_length);
+ pa_assert(c->write_data_index < c->write_data_length);
if ((r = pa_iochannel_write(c->io, (uint8_t*) c->write_data+c->write_data_index, c->write_data_length-c->write_data_index)) < 0) {
+
+ if (r < 0 && (errno == EINTR || errno == EAGAIN))
+ return 0;
+
pa_log("write(): %s", pa_cstrerror(errno));
return -1;
}
@@ -993,32 +1085,38 @@ static int do_write(struct connection *c) {
} else if (c->state == ESD_STREAMING_DATA && c->source_output) {
pa_memchunk chunk;
ssize_t r;
+ void *p;
- assert(c->output_memblockq);
if (pa_memblockq_peek(c->output_memblockq, &chunk) < 0)
return 0;
- assert(chunk.memblock && chunk.length);
+ pa_assert(chunk.memblock);
+ pa_assert(chunk.length);
+
+ p = pa_memblock_acquire(chunk.memblock);
+ r = pa_iochannel_write(c->io, (uint8_t*) p+chunk.index, chunk.length);
+ pa_memblock_release(chunk.memblock);
+
+ pa_memblock_unref(chunk.memblock);
+
+ if (r < 0) {
+
+ if (r < 0 && (errno == EINTR || errno == EAGAIN))
+ return 0;
- if ((r = pa_iochannel_write(c->io, (uint8_t*) chunk.memblock->data+chunk.index, chunk.length)) < 0) {
- pa_memblock_unref(chunk.memblock);
pa_log("write(): %s", pa_cstrerror(errno));
return -1;
}
- pa_memblockq_drop(c->output_memblockq, &chunk, r);
- pa_memblock_unref(chunk.memblock);
-
- pa_source_notify(c->source_output->source);
+ pa_memblockq_drop(c->output_memblockq, r);
}
return 0;
}
-static void do_work(struct connection *c) {
- assert(c);
+static void do_work(connection *c) {
+ connection_assert_ref(c);
- assert(c->protocol && c->protocol->core && c->protocol->core->mainloop && c->protocol->core->mainloop->defer_enable);
c->protocol->core->mainloop->defer_enable(c->defer_event, 0);
if (c->dead)
@@ -1044,122 +1142,196 @@ static void do_work(struct connection *c) {
fail:
if (c->state == ESD_STREAMING_DATA && c->sink_input) {
- c->dead = 1;
+ c->dead = TRUE;
pa_iochannel_free(c->io);
c->io = NULL;
- pa_memblockq_prebuf_disable(c->input_memblockq);
- pa_sink_notify(c->sink_input->sink);
+ pa_asyncmsgq_post(c->sink_input->sink->asyncmsgq, PA_MSGOBJECT(c->sink_input), SINK_INPUT_MESSAGE_DISABLE_PREBUF, NULL, 0, NULL, NULL);
} else
- connection_free(c);
+ connection_unlink(c);
}
static void io_callback(pa_iochannel*io, void *userdata) {
- struct connection *c = userdata;
- assert(io && c && c->io == io);
+ connection *c = CONNECTION(userdata);
+
+ connection_assert_ref(c);
+ pa_assert(io);
do_work(c);
}
-/*** defer callback ***/
-
static void defer_callback(pa_mainloop_api*a, pa_defer_event *e, void *userdata) {
- struct connection *c = userdata;
- assert(a && c && c->defer_event == e);
+ connection *c = CONNECTION(userdata);
-/* pa_log("DEFER"); */
+ connection_assert_ref(c);
+ pa_assert(e);
do_work(c);
}
-/*** sink_input callbacks ***/
+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);
-static int sink_input_peek_cb(pa_sink_input *i, pa_memchunk *chunk) {
- struct connection*c;
- assert(i && i->userdata && chunk);
- c = i->userdata;
+ switch (code) {
+ case CONNECTION_MESSAGE_REQUEST_DATA:
+ do_work(c);
+ break;
- if (pa_memblockq_peek(c->input_memblockq, chunk) < 0) {
+ case CONNECTION_MESSAGE_POST_DATA:
+/* pa_log("got data %u", chunk->length); */
+ pa_memblockq_push_align(c->output_memblockq, chunk);
+ do_work(c);
+ break;
- if (c->dead)
- connection_free(c);
-
- return -1;
+ case CONNECTION_MESSAGE_UNLINK_CONNECTION:
+ connection_unlink(c);
+ break;
}
return 0;
}
-static void sink_input_drop_cb(pa_sink_input *i, const pa_memchunk *chunk, size_t length) {
- struct connection*c = i->userdata;
- assert(i && c && length);
+/*** sink_input callbacks ***/
+
+/* Called from thread context */
+static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk) {
+ pa_sink_input *i = PA_SINK_INPUT(o);
+ connection*c;
+
+ pa_sink_input_assert_ref(i);
+ c = CONNECTION(i->userdata);
+ connection_assert_ref(c);
+
+ switch (code) {
+
+ case SINK_INPUT_MESSAGE_POST_DATA: {
+ pa_assert(chunk);
+
+ /* New data from the main loop */
+ pa_memblockq_push_align(c->input_memblockq, chunk);
+
+/* pa_log("got data, %u", pa_memblockq_get_length(c->input_memblockq)); */
+
+ return 0;
+ }
-/* pa_log("DROP"); */
+ case SINK_INPUT_MESSAGE_DISABLE_PREBUF: {
+ pa_memblockq_prebuf_disable(c->input_memblockq);
+ return 0;
+ }
- pa_memblockq_drop(c->input_memblockq, chunk, length);
+ case PA_SINK_INPUT_MESSAGE_GET_LATENCY: {
+ pa_usec_t *r = userdata;
- /* do something */
- assert(c->protocol && c->protocol->core && c->protocol->core->mainloop && c->protocol->core->mainloop->defer_enable);
+ *r = pa_bytes_to_usec(pa_memblockq_get_length(c->input_memblockq), &c->sink_input->sample_spec);
- if (!c->dead)
- c->protocol->core->mainloop->defer_enable(c->defer_event, 1);
+ /* Fall through, the default handler will add in the extra
+ * latency added by the resampler */
+ }
-/* assert(pa_memblockq_get_length(c->input_memblockq) > 2048); */
+ default:
+ return pa_sink_input_process_msg(o, code, userdata, offset, chunk);
+ }
}
-static void sink_input_kill_cb(pa_sink_input *i) {
- assert(i && i->userdata);
- connection_free((struct connection *) i->userdata);
+/* Called from thread context */
+static int sink_input_peek_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) {
+ connection*c;
+ int r;
+
+ pa_assert(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);
+
+ return r;
+}
+
+/* Called from thread context */
+static void sink_input_drop_cb(pa_sink_input *i, size_t length) {
+ connection*c;
+ size_t old, new;
+
+ pa_assert(i);
+ c = CONNECTION(i->userdata);
+ connection_assert_ref(c);
+ pa_assert(length);
+
+ /* pa_log("DROP"); */
+
+ old = pa_memblockq_missing(c->input_memblockq);
+ pa_memblockq_drop(c->input_memblockq, length);
+ new = pa_memblockq_missing(c->input_memblockq);
+
+ 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);
+ }
}
-static pa_usec_t sink_input_get_latency_cb(pa_sink_input *i) {
- struct connection*c = i->userdata;
- assert(i && c);
- return pa_bytes_to_usec(pa_memblockq_get_length(c->input_memblockq), &c->sink_input->sample_spec);
+static void sink_input_kill_cb(pa_sink_input *i) {
+ pa_sink_input_assert_ref(i);
+
+ connection_unlink(CONNECTION(i->userdata));
}
/*** source_output callbacks ***/
+/* Called from thread context */
static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk) {
- struct connection *c = o->userdata;
- assert(o && c && chunk);
-
- pa_memblockq_push(c->output_memblockq, chunk);
+ connection *c;
- /* do something */
- assert(c->protocol && c->protocol->core && c->protocol->core->mainloop && c->protocol->core->mainloop->defer_enable);
+ pa_assert(o);
+ c = CONNECTION(o->userdata);
+ pa_assert(c);
+ pa_assert(chunk);
- if (!c->dead)
- c->protocol->core->mainloop->defer_enable(c->defer_event, 1);
+ pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(c), CONNECTION_MESSAGE_POST_DATA, NULL, 0, chunk, NULL);
}
static void source_output_kill_cb(pa_source_output *o) {
- assert(o && o->userdata);
- connection_free((struct connection *) o->userdata);
+ pa_source_output_assert_ref(o);
+
+ connection_unlink(CONNECTION(o->userdata));
}
static pa_usec_t source_output_get_latency_cb(pa_source_output *o) {
- struct connection*c = o->userdata;
- assert(o && c);
+ connection*c;
+
+ pa_assert(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 ***/
static void auth_timeout(pa_mainloop_api*m, pa_time_event *e, const struct timeval *tv, void *userdata) {
- struct connection *c = userdata;
- assert(m && tv && c && c->auth_timeout_event == e);
+ connection *c = CONNECTION(userdata);
+
+ pa_assert(m);
+ pa_assert(tv);
+ connection_assert_ref(c);
+ pa_assert(c->auth_timeout_event == e);
if (!c->authorized)
- connection_free(c);
+ connection_unlink(c);
}
static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata) {
- struct connection *c;
+ connection *c;
pa_protocol_esound *p = userdata;
char cname[256], pname[128];
- assert(s && io && p);
+
+ pa_assert(s);
+ pa_assert(io);
+ pa_assert(p);
if (pa_idxset_size(p->connections)+1 > MAX_CONNECTIONS) {
pa_log("Warning! Too many connections (%u), dropping incoming connection.", MAX_CONNECTIONS);
@@ -1167,23 +1339,23 @@ static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata)
return;
}
- c = pa_xnew(struct connection, 1);
+ c = pa_msgobject_new(connection);
+ c->parent.parent.free = connection_free;
+ c->parent.process_msg = connection_process_msg;
c->protocol = p;
c->io = io;
pa_iochannel_set_callback(c->io, io_callback, c);
pa_iochannel_socket_peer_to_string(io, pname, sizeof(pname));
- snprintf(cname, sizeof(cname), "EsounD client (%s)", pname);
- assert(p->core);
+ pa_snprintf(cname, sizeof(cname), "EsounD client (%s)", pname);
c->client = pa_client_new(p->core, __FILE__, cname);
- assert(c->client);
c->client->owner = p->module;
c->client->kill = client_kill_cb;
c->client->userdata = c;
c->authorized = !!p->public;
- c->swap_byte_order = 0;
- c->dead = 0;
+ c->swap_byte_order = FALSE;
+ c->dead = FALSE;
c->read_data_length = 0;
c->read_data = pa_xmalloc(c->read_data_alloc = proto_map[ESD_PROTO_CONNECT].data_length);
@@ -1203,6 +1375,7 @@ 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;
+ pa_atomic_store(&c->playback.missing, 0);
c->scache.memchunk.length = c->scache.memchunk.index = 0;
c->scache.memchunk.memblock = NULL;
@@ -1212,7 +1385,7 @@ static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata)
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) {
@@ -1224,7 +1397,6 @@ static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata)
c->auth_timeout_event = NULL;
c->defer_event = p->core->mainloop->defer_new(p->core->mainloop, defer_callback, c);
- assert(c->defer_event);
p->core->mainloop->defer_enable(c->defer_event, 0);
pa_idxset_put(p->connections, c, &c->index);
@@ -1233,22 +1405,22 @@ static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata)
/*** 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;
+ pa_protocol_esound *p = NULL;
int public = 0;
const char *acl;
- assert(core);
- assert(server);
- assert(m);
- assert(ma);
-
- p = pa_xnew(pa_protocol_esound, 1);
+ pa_assert(core);
+ pa_assert(server);
+ 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;
}
+ p = pa_xnew(pa_protocol_esound, 1);
+
if (pa_authkey_load_auto(pa_modargs_get_value(ma, "cookie", DEFAULT_COOKIE_FILE), p->esd_key, sizeof(p->esd_key)) < 0)
goto fail;
@@ -1261,13 +1433,12 @@ pa_protocol_esound* pa_protocol_esound_new(pa_core*core, pa_socket_server *serve
} else
p->auth_ip_acl = NULL;
+ p->core = core;
p->module = m;
p->public = public;
p->server = server;
pa_socket_server_set_callback(p->server, on_connection, p);
- p->core = core;
p->connections = pa_idxset_new(NULL, NULL);
- assert(p->connections);
p->sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL));
p->source_name = pa_xstrdup(pa_modargs_get_value(ma, "source", NULL));
@@ -1281,17 +1452,20 @@ fail:
}
void pa_protocol_esound_free(pa_protocol_esound *p) {
- struct connection *c;
- assert(p);
+ connection *c;
+ pa_assert(p);
while ((c = pa_idxset_first(p->connections, NULL)))
- connection_free(c);
-
+ connection_unlink(c);
pa_idxset_free(p->connections, NULL, NULL);
+
pa_socket_server_unref(p->server);
if (p->auth_ip_acl)
pa_ip_acl_free(p->auth_ip_acl);
+ pa_xfree(p->sink_name);
+ pa_xfree(p->source_name);
+
pa_xfree(p);
}
diff --git a/src/pulsecore/protocol-http.c b/src/pulsecore/protocol-http.c
index 3541721a..d91ae142 100644
--- a/src/pulsecore/protocol-http.c
+++ b/src/pulsecore/protocol-http.c
@@ -25,7 +25,6 @@
#include <config.h>
#endif
-#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
@@ -34,6 +33,7 @@
#include <pulse/xmalloc.h>
#include <pulsecore/ioline.h>
+#include <pulsecore/macro.h>
#include <pulsecore/log.h>
#include <pulsecore/namereg.h>
#include <pulsecore/cli-text.h>
@@ -65,11 +65,12 @@ struct pa_protocol_http {
static void http_response(struct connection *c, int code, const char *msg, const char *mime) {
char s[256];
- assert(c);
- assert(msg);
- assert(mime);
- snprintf(s, sizeof(s),
+ pa_assert(c);
+ pa_assert(msg);
+ pa_assert(mime);
+
+ pa_snprintf(s, sizeof(s),
"HTTP/1.0 %i %s\n"
"Connection: close\n"
"Content-Type: %s\n"
@@ -83,14 +84,14 @@ static void http_response(struct connection *c, int code, const char *msg, const
static void http_message(struct connection *c, int code, const char *msg, const char *text) {
char s[256];
- assert(c);
+ pa_assert(c);
http_response(c, code, msg, "text/html");
if (!text)
text = msg;
- snprintf(s, sizeof(s),
+ pa_snprintf(s, sizeof(s),
"<?xml version=\"1.0\"?>\n"
"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n"
"<html xmlns=\"http://www.w3.org/1999/xhtml\"><head><title>%s</title></head>\n"
@@ -103,21 +104,22 @@ static void http_message(struct connection *c, int code, const char *msg, const
static void connection_free(struct connection *c, int del) {
- assert(c);
+ pa_assert(c);
if (c->url)
pa_xfree(c->url);
if (del)
pa_idxset_remove_by_data(c->protocol->connections, c, NULL);
+
pa_ioline_unref(c->line);
pa_xfree(c);
}
static void line_callback(pa_ioline *line, const char *s, void *userdata) {
struct connection *c = userdata;
- assert(line);
- assert(c);
+ pa_assert(line);
+ pa_assert(c);
if (!s) {
/* EOF */
@@ -223,7 +225,10 @@ fail:
static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata) {
pa_protocol_http *p = userdata;
struct connection *c;
- assert(s && io && p);
+
+ pa_assert(s);
+ pa_assert(io);
+ pa_assert(p);
if (pa_idxset_size(p->connections)+1 > MAX_CONNECTIONS) {
pa_log_warn("Warning! Too many connections (%u), dropping incoming connection.", MAX_CONNECTIONS);
@@ -231,7 +236,7 @@ static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata)
return;
}
- c = pa_xmalloc(sizeof(struct connection));
+ c = pa_xnew(struct connection, 1);
c->protocol = p;
c->line = pa_ioline_new(io);
c->state = REQUEST_LINE;
@@ -243,9 +248,11 @@ static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata)
pa_protocol_http* pa_protocol_http_new(pa_core *core, pa_socket_server *server, pa_module *m, PA_GCC_UNUSED pa_modargs *ma) {
pa_protocol_http* p;
- assert(core && server);
- p = pa_xmalloc(sizeof(pa_protocol_http));
+ pa_core_assert_ref(core);
+ pa_assert(server);
+
+ p = pa_xnew(pa_protocol_http, 1);
p->module = m;
p->core = core;
p->server = server;
@@ -257,12 +264,12 @@ pa_protocol_http* pa_protocol_http_new(pa_core *core, pa_socket_server *server,
}
static void free_connection(void *p, PA_GCC_UNUSED void *userdata) {
- assert(p);
+ pa_assert(p);
connection_free(p, 0);
}
void pa_protocol_http_free(pa_protocol_http *p) {
- assert(p);
+ pa_assert(p);
pa_idxset_free(p->connections, free_connection, NULL);
pa_socket_server_unref(p->server);
diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c
index dd41b3d5..9ae0f083 100644
--- a/src/pulsecore/protocol-native.c
+++ b/src/pulsecore/protocol-native.c
@@ -28,7 +28,6 @@
#include <string.h>
#include <stdio.h>
-#include <assert.h>
#include <stdlib.h>
#include <unistd.h>
@@ -61,6 +60,7 @@
#include <pulsecore/creds.h>
#include <pulsecore/core-util.h>
#include <pulsecore/ipacl.h>
+#include <pulsecore/thread-mq.h>
#include "protocol-native.h"
@@ -72,54 +72,61 @@
#define MAX_MEMBLOCKQ_LENGTH (4*1024*1024) /* 4MB */
-struct connection;
+typedef struct connection connection;
struct pa_protocol_native;
-struct record_stream {
- struct connection *connection;
+typedef struct record_stream {
+ pa_msgobject parent;
+
+ connection *connection;
uint32_t index;
+
pa_source_output *source_output;
pa_memblockq *memblockq;
size_t fragment_size;
-};
+} record_stream;
+
+typedef struct output_stream {
+ pa_msgobject parent;
+} output_stream;
-struct playback_stream {
- int type;
- struct connection *connection;
+typedef struct playback_stream {
+ output_stream parent;
+
+ connection *connection;
uint32_t index;
+
pa_sink_input *sink_input;
pa_memblockq *memblockq;
- size_t requested_bytes;
int drain_request;
uint32_t drain_tag;
uint32_t syncid;
int underrun;
- /* Sync group members */
- PA_LLIST_FIELDS(struct playback_stream);
-};
+ pa_atomic_t missing;
+ size_t minreq;
+
+ /* Only updated after SINK_INPUT_MESSAGE_UPDATE_LATENCY */
+ int64_t read_index, write_index;
+ size_t resampled_chunk_length;
+} playback_stream;
-struct upload_stream {
- int type;
- struct connection *connection;
+typedef struct upload_stream {
+ output_stream parent;
+
+ connection *connection;
uint32_t index;
+
pa_memchunk memchunk;
size_t length;
char *name;
pa_sample_spec sample_spec;
pa_channel_map channel_map;
-};
-
-struct output_stream {
- int type;
-};
-
-enum {
- UPLOAD_STREAM,
- PLAYBACK_STREAM
-};
+} upload_stream;
struct connection {
+ pa_msgobject parent;
+
int authorized;
uint32_t version;
pa_protocol_native *protocol;
@@ -132,10 +139,30 @@ 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(output_stream);
+#define OUTPUT_STREAM(o) (output_stream_cast(o))
+static PA_DEFINE_CHECK_TYPE(output_stream, pa_msgobject);
+
+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;
- int public;
pa_core *core;
+ int public;
pa_socket_server *server;
pa_idxset *connections;
uint8_t auth_cookie[PA_NATIVE_COOKIE_LENGTH];
@@ -146,17 +173,45 @@ struct pa_protocol_native {
pa_ip_acl *auth_ip_acl;
};
-static int sink_input_peek_cb(pa_sink_input *i, pa_memchunk *chunk);
-static void sink_input_drop_cb(pa_sink_input *i, const pa_memchunk *chunk, size_t length);
+enum {
+ SINK_INPUT_MESSAGE_POST_DATA = PA_SINK_INPUT_MESSAGE_MAX, /* data from main loop to sink input */
+ SINK_INPUT_MESSAGE_DRAIN, /* disabled prebuf, get playback started. */
+ SINK_INPUT_MESSAGE_FLUSH,
+ SINK_INPUT_MESSAGE_TRIGGER,
+ SINK_INPUT_MESSAGE_SEEK,
+ SINK_INPUT_MESSAGE_PREBUF_FORCE,
+ SINK_INPUT_MESSAGE_UPDATE_LATENCY
+};
+
+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
+};
+
+enum {
+ RECORD_STREAM_MESSAGE_POST_DATA /* data from source output to main loop */
+};
+
+enum {
+ CONNECTION_MESSAGE_RELEASE,
+ 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 void sink_input_kill_cb(pa_sink_input *i);
-static pa_usec_t sink_input_get_latency_cb(pa_sink_input *i);
+static void send_memblock(connection *c);
static void request_bytes(struct playback_stream*s);
static void source_output_kill_cb(pa_source_output *o);
static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk);
static pa_usec_t source_output_get_latency_cb(pa_source_output *o);
+static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk);
+
static void command_exit(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
static void command_drain_playback_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
@@ -179,8 +234,7 @@ static void command_subscribe(pa_pdispatch *pd, uint32_t command, uint32_t tag,
static void command_set_volume(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
static void command_set_mute(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
static void command_cork_playback_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
-static void command_flush_playback_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
-static void command_trigger_or_prebuf_playback_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *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);
static void command_set_default_sink_or_source(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
static void command_set_stream_name(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
static void command_kill(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
@@ -193,6 +247,7 @@ static void command_get_autoload_info_list(pa_pdispatch *pd, uint32_t command, u
static void command_cork_record_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
static void command_flush_record_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
static void command_move_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_suspend(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {
[PA_COMMAND_ERROR] = NULL,
@@ -239,12 +294,16 @@ static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {
[PA_COMMAND_SET_SOURCE_VOLUME] = command_set_volume,
[PA_COMMAND_SET_SINK_MUTE] = command_set_mute,
+ [PA_COMMAND_SET_SINK_INPUT_MUTE] = command_set_mute,
[PA_COMMAND_SET_SOURCE_MUTE] = command_set_mute,
+ [PA_COMMAND_SUSPEND_SINK] = command_suspend,
+ [PA_COMMAND_SUSPEND_SOURCE] = command_suspend,
+
[PA_COMMAND_CORK_PLAYBACK_STREAM] = command_cork_playback_stream,
- [PA_COMMAND_FLUSH_PLAYBACK_STREAM] = command_flush_playback_stream,
- [PA_COMMAND_TRIGGER_PLAYBACK_STREAM] = command_trigger_or_prebuf_playback_stream,
- [PA_COMMAND_PREBUF_PLAYBACK_STREAM] = command_trigger_or_prebuf_playback_stream,
+ [PA_COMMAND_FLUSH_PLAYBACK_STREAM] = command_trigger_or_flush_or_prebuf_playback_stream,
+ [PA_COMMAND_TRIGGER_PLAYBACK_STREAM] = command_trigger_or_flush_or_prebuf_playback_stream,
+ [PA_COMMAND_PREBUF_PLAYBACK_STREAM] = command_trigger_or_flush_or_prebuf_playback_stream,
[PA_COMMAND_CORK_RECORD_STREAM] = command_cork_record_stream,
[PA_COMMAND_FLUSH_RECORD_STREAM] = command_flush_record_stream,
@@ -269,74 +328,146 @@ static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {
/* structure management */
-static struct upload_stream* upload_stream_new(
- struct connection *c,
- const pa_sample_spec *ss,
- const pa_channel_map *map,
- const char *name, size_t length) {
+static void upload_stream_unlink(upload_stream *s) {
+ pa_assert(s);
+
+ if (!s->connection)
+ return;
+
+ pa_assert_se(pa_idxset_remove_by_data(s->connection->output_streams, s, NULL) == s);
+ s->connection = NULL;
+ upload_stream_unref(s);
+}
+
+static void upload_stream_free(pa_object *o) {
+ upload_stream *s = UPLOAD_STREAM(o);
+ pa_assert(s);
+
+ upload_stream_unlink(s);
+
+ pa_xfree(s->name);
- struct upload_stream *s;
- assert(c && ss && name && length);
+ if (s->memchunk.memblock)
+ pa_memblock_unref(s->memchunk.memblock);
- s = pa_xnew(struct upload_stream, 1);
- s->type = UPLOAD_STREAM;
+ pa_xfree(s);
+}
+
+static upload_stream* upload_stream_new(
+ connection *c,
+ const pa_sample_spec *ss,
+ const pa_channel_map *map,
+ const char *name, size_t length) {
+
+ upload_stream *s;
+
+ pa_assert(c);
+ pa_assert(ss);
+ pa_assert(name);
+ pa_assert(length > 0);
+
+ s = pa_msgobject_new(upload_stream);
+ s->parent.parent.parent.free = upload_stream_free;
s->connection = c;
s->sample_spec = *ss;
s->channel_map = *map;
s->name = pa_xstrdup(name);
-
- s->memchunk.memblock = NULL;
- s->memchunk.index = 0;
- s->memchunk.length = 0;
-
+ pa_memchunk_reset(&s->memchunk);
s->length = length;
pa_idxset_put(c->output_streams, s, &s->index);
+
return s;
}
-static void upload_stream_free(struct upload_stream *o) {
- assert(o && o->connection);
+static void record_stream_unlink(record_stream *s) {
+ pa_assert(s);
+
+ if (!s->connection)
+ return;
+
+ if (s->source_output) {
+ pa_source_output_unlink(s->source_output);
+ pa_source_output_unref(s->source_output);
+ s->source_output = NULL;
+ }
+
+ pa_assert_se(pa_idxset_remove_by_data(s->connection->record_streams, s, NULL) == s);
+ s->connection = NULL;
+ record_stream_unref(s);
+}
+
+static void record_stream_free(pa_object *o) {
+ record_stream *s = RECORD_STREAM(o);
+ pa_assert(s);
+
+ record_stream_unlink(s);
+
+ pa_memblockq_free(s->memblockq);
+ pa_xfree(s);
+}
+
+static int record_stream_process_msg(pa_msgobject *o, int code, void*userdata, int64_t offset, pa_memchunk *chunk) {
+ record_stream *s = RECORD_STREAM(o);
+ record_stream_assert_ref(s);
- pa_idxset_remove_by_data(o->connection->output_streams, o, NULL);
+ if (!s->connection)
+ return -1;
- pa_xfree(o->name);
+ switch (code) {
- if (o->memchunk.memblock)
- pa_memblock_unref(o->memchunk.memblock);
+ case RECORD_STREAM_MESSAGE_POST_DATA:
- pa_xfree(o);
+ if (pa_memblockq_push_align(s->memblockq, chunk) < 0) {
+/* pa_log_warn("Failed to push data into output queue."); */
+ return -1;
+ }
+
+ if (!pa_pstream_is_pending(s->connection->pstream))
+ send_memblock(s->connection);
+
+ break;
+ }
+
+ return 0;
}
-static struct record_stream* record_stream_new(
- struct connection *c,
- pa_source *source,
- const pa_sample_spec *ss,
- const pa_channel_map *map,
- const char *name,
- size_t maxlength,
- size_t fragment_size) {
+static record_stream* record_stream_new(
+ connection *c,
+ pa_source *source,
+ const pa_sample_spec *ss,
+ const pa_channel_map *map,
+ const char *name,
+ uint32_t *maxlength,
+ uint32_t fragment_size,
+ int corked) {
- struct record_stream *s;
+ record_stream *s;
pa_source_output *source_output;
size_t base;
pa_source_output_new_data data;
- assert(c && ss && name && maxlength);
+ pa_assert(c);
+ pa_assert(ss);
+ pa_assert(name);
+ pa_assert(maxlength);
+ pa_assert(*maxlength > 0);
pa_source_output_new_data_init(&data);
+ data.module = c->protocol->module;
+ data.client = c->client;
data.source = source;
data.driver = __FILE__;
data.name = name;
pa_source_output_new_data_set_sample_spec(&data, ss);
pa_source_output_new_data_set_channel_map(&data, map);
- data.module = c->protocol->module;
- data.client = c->client;
- if (!(source_output = pa_source_output_new(c->protocol->core, &data, 0)))
+ if (!(source_output = pa_source_output_new(c->protocol->core, &data, corked ? PA_SOURCE_OUTPUT_START_CORKED : 0)))
return NULL;
- s = pa_xnew(struct record_stream, 1);
+ s = pa_msgobject_new(record_stream);
+ s->parent.parent.free = record_stream_free;
+ s->parent.process_msg = record_stream_process_msg;
s->connection = c;
s->source_output = source_output;
s->source_output->push = source_output_push_cb;
@@ -346,58 +477,159 @@ static struct record_stream* record_stream_new(
s->memblockq = pa_memblockq_new(
0,
- maxlength,
+ *maxlength,
0,
base = pa_frame_size(ss),
1,
0,
NULL);
- assert(s->memblockq);
s->fragment_size = (fragment_size/base)*base;
- if (!s->fragment_size)
+ if (s->fragment_size <= 0)
s->fragment_size = base;
+ *maxlength = pa_memblockq_get_maxlength(s->memblockq);
pa_idxset_put(c->record_streams, s, &s->index);
+
+ pa_source_output_put(s->source_output);
return s;
}
-static void record_stream_free(struct record_stream* r) {
- assert(r && r->connection);
+static void playback_stream_unlink(playback_stream *s) {
+ pa_assert(s);
+
+ if (!s->connection)
+ return;
+
+ if (s->sink_input) {
+ pa_sink_input_unlink(s->sink_input);
+ pa_sink_input_unref(s->sink_input);
+ s->sink_input = NULL;
+ }
+
+ if (s->drain_request)
+ pa_pstream_send_error(s->connection->pstream, s->drain_tag, PA_ERR_NOENTITY);
+
+ pa_assert_se(pa_idxset_remove_by_data(s->connection->output_streams, s, NULL) == s);
+ s->connection = NULL;
+ playback_stream_unref(s);
+}
+
+static void playback_stream_free(pa_object* o) {
+ playback_stream *s = PLAYBACK_STREAM(o);
+ pa_assert(s);
+
+ playback_stream_unlink(s);
+
+ pa_memblockq_free(s->memblockq);
+ pa_xfree(s);
+}
+
+static int playback_stream_process_msg(pa_msgobject *o, int code, void*userdata, int64_t offset, pa_memchunk *chunk) {
+ playback_stream *s = PLAYBACK_STREAM(o);
+ playback_stream_assert_ref(s);
+
+ if (!s->connection)
+ return -1;
+
+ switch (code) {
+ case PLAYBACK_STREAM_MESSAGE_REQUEST_DATA: {
+ pa_tagstruct *t;
+ uint32_t l = 0;
+
+ for (;;) {
+ int32_t k;
+
+ if ((k = pa_atomic_load(&s->missing)) <= 0)
+ break;
+
+ l += k;
+
+ if (l < s->minreq)
+ break;
+
+ if (pa_atomic_sub(&s->missing, k) <= k)
+ break;
+ }
+
+ if (l < s->minreq)
+ break;
+
+ t = pa_tagstruct_new(NULL, 0);
+ pa_tagstruct_putu32(t, PA_COMMAND_REQUEST);
+ pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
+ pa_tagstruct_putu32(t, s->index);
+ pa_tagstruct_putu32(t, l);
+ pa_pstream_send_tagstruct(s->connection->pstream, t);
+
+/* pa_log("Requesting %u bytes", l); */
+ break;
+ }
+
+ case PLAYBACK_STREAM_MESSAGE_UNDERFLOW: {
+ pa_tagstruct *t;
+
+ /* Report that we're empty */
+ t = pa_tagstruct_new(NULL, 0);
+ pa_tagstruct_putu32(t, PA_COMMAND_UNDERFLOW);
+ 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_OVERFLOW: {
+ pa_tagstruct *t;
+
+ /* Notify the user we're overflowed*/
+ t = pa_tagstruct_new(NULL, 0);
+ pa_tagstruct_putu32(t, PA_COMMAND_OVERFLOW);
+ pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
+ pa_tagstruct_putu32(t, s->index);
+ pa_pstream_send_tagstruct(s->connection->pstream, t);
+ break;
+ }
- pa_idxset_remove_by_data(r->connection->record_streams, r, NULL);
- pa_source_output_disconnect(r->source_output);
- pa_source_output_unref(r->source_output);
- pa_memblockq_free(r->memblockq);
- pa_xfree(r);
+ case PLAYBACK_STREAM_MESSAGE_DRAIN_ACK:
+ pa_pstream_send_simple_ack(s->connection->pstream, PA_PTR_TO_UINT(userdata));
+ break;
+
+ }
+
+ return 0;
}
-static struct playback_stream* playback_stream_new(
- struct connection *c,
+static playback_stream* playback_stream_new(
+ connection *c,
pa_sink *sink,
const pa_sample_spec *ss,
const pa_channel_map *map,
const char *name,
- size_t maxlength,
- size_t tlength,
- size_t prebuf,
- size_t minreq,
+ uint32_t *maxlength,
+ uint32_t *tlength,
+ uint32_t *prebuf,
+ uint32_t *minreq,
pa_cvolume *volume,
- uint32_t syncid) {
+ uint32_t syncid,
+ int corked,
+ uint32_t *missing) {
- struct playback_stream *s, *ssync;
+ playback_stream *s, *ssync;
pa_sink_input *sink_input;
pa_memblock *silence;
uint32_t idx;
int64_t start_index;
pa_sink_input_new_data data;
- assert(c && ss && name && maxlength);
+ pa_assert(c);
+ pa_assert(ss);
+ pa_assert(name);
+ pa_assert(maxlength);
/* Find syncid group */
for (ssync = pa_idxset_first(c->output_streams, &idx); ssync; ssync = pa_idxset_next(c->output_streams, &idx)) {
- if (ssync->type != PLAYBACK_STREAM)
+ if (!playback_stream_isinstance(ssync))
continue;
if (ssync->syncid == syncid)
@@ -405,8 +637,13 @@ static struct playback_stream* playback_stream_new(
}
/* Synced streams must connect to the same sink */
- if (ssync)
- sink = ssync->sink_input->sink;
+ if (ssync) {
+
+ if (!sink)
+ sink = ssync->sink_input->sink;
+ else if (sink != ssync->sink_input->sink)
+ return NULL;
+ }
pa_sink_input_new_data_init(&data);
data.sink = sink;
@@ -417,146 +654,158 @@ static struct playback_stream* playback_stream_new(
pa_sink_input_new_data_set_volume(&data, volume);
data.module = c->protocol->module;
data.client = c->client;
+ data.sync_base = ssync ? ssync->sink_input : NULL;
- if (!(sink_input = pa_sink_input_new(c->protocol->core, &data, 0)))
+ if (!(sink_input = pa_sink_input_new(c->protocol->core, &data, corked ? PA_SINK_INPUT_START_CORKED : 0)))
return NULL;
- s = pa_xnew(struct playback_stream, 1);
- s->type = PLAYBACK_STREAM;
+ s = pa_msgobject_new(playback_stream);
+ s->parent.parent.parent.free = playback_stream_free;
+ s->parent.parent.process_msg = playback_stream_process_msg;
s->connection = c;
s->syncid = syncid;
s->sink_input = sink_input;
s->underrun = 1;
+ s->sink_input->parent.process_msg = sink_input_process_msg;
s->sink_input->peek = sink_input_peek_cb;
s->sink_input->drop = sink_input_drop_cb;
s->sink_input->kill = sink_input_kill_cb;
- s->sink_input->get_latency = sink_input_get_latency_cb;
s->sink_input->userdata = s;
- if (ssync) {
- /* Sync id found, now find head of list */
- PA_LLIST_FIND_HEAD(struct playback_stream, ssync, &ssync);
-
- /* Prepend ourselves */
- PA_LLIST_PREPEND(struct playback_stream, ssync, s);
-
- /* Set our start index to the current read index of the other grozp member(s) */
- assert(ssync->next);
- start_index = pa_memblockq_get_read_index(ssync->next->memblockq);
- } else {
- /* This ia a new sync group */
- PA_LLIST_INIT(struct playback_stream, s);
- start_index = 0;
- }
+ start_index = ssync ? pa_memblockq_get_read_index(ssync->memblockq) : 0;
silence = pa_silence_memblock_new(c->protocol->core->mempool, ss, 0);
s->memblockq = pa_memblockq_new(
start_index,
- maxlength,
- tlength,
+ *maxlength,
+ *tlength,
pa_frame_size(ss),
- prebuf,
- minreq,
+ *prebuf,
+ *minreq,
silence);
pa_memblock_unref(silence);
- s->requested_bytes = 0;
+ *maxlength = (uint32_t) pa_memblockq_get_maxlength(s->memblockq);
+ *tlength = (uint32_t) pa_memblockq_get_tlength(s->memblockq);
+ *prebuf = (uint32_t) pa_memblockq_get_prebuf(s->memblockq);
+ *minreq = (uint32_t) pa_memblockq_get_minreq(s->memblockq);
+ *missing = (uint32_t) pa_memblockq_pop_missing(s->memblockq);
+
+ s->minreq = pa_memblockq_get_minreq(s->memblockq);
+ 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);
+
return s;
}
-static void playback_stream_free(struct playback_stream* p) {
- struct playback_stream *head;
- assert(p && p->connection);
+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);
- if (p->drain_request)
- pa_pstream_send_error(p->connection->pstream, p->drain_tag, PA_ERR_NOENTITY);
+ if (!c->protocol)
+ return -1;
- PA_LLIST_FIND_HEAD(struct playback_stream, p, &head);
- PA_LLIST_REMOVE(struct playback_stream, head, p);
+ switch (code) {
- pa_idxset_remove_by_data(p->connection->output_streams, p, NULL);
- pa_sink_input_disconnect(p->sink_input);
- pa_sink_input_unref(p->sink_input);
- pa_memblockq_free(p->memblockq);
- pa_xfree(p);
+ case CONNECTION_MESSAGE_REVOKE:
+ pa_pstream_send_revoke(c->pstream, PA_PTR_TO_UINT(userdata));
+ break;
+
+ case CONNECTION_MESSAGE_RELEASE:
+ pa_pstream_send_release(c->pstream, PA_PTR_TO_UINT(userdata));
+ break;
+ }
+
+ return 0;
}
-static void connection_free(struct connection *c) {
- struct record_stream *r;
- struct output_stream *o;
- assert(c && c->protocol);
+static void connection_unlink(connection *c) {
+ record_stream *r;
+ output_stream *o;
+
+ pa_assert(c);
+
+ if (!c->protocol)
+ return;
- pa_idxset_remove_by_data(c->protocol->connections, c, NULL);
while ((r = pa_idxset_first(c->record_streams, NULL)))
- record_stream_free(r);
- pa_idxset_free(c->record_streams, NULL, NULL);
+ record_stream_unlink(r);
while ((o = pa_idxset_first(c->output_streams, NULL)))
- if (o->type == PLAYBACK_STREAM)
- playback_stream_free((struct playback_stream*) o);
+ if (playback_stream_isinstance(o))
+ playback_stream_unlink(PLAYBACK_STREAM(o));
else
- upload_stream_free((struct upload_stream*) o);
- pa_idxset_free(c->output_streams, NULL, NULL);
-
- pa_pdispatch_unref(c->pdispatch);
- pa_pstream_close(c->pstream);
- pa_pstream_unref(c->pstream);
- pa_client_free(c->client);
+ upload_stream_unlink(UPLOAD_STREAM(o));
if (c->subscription)
pa_subscription_free(c->subscription);
- if (c->auth_timeout_event)
+ if (c->pstream)
+ pa_pstream_unlink(c->pstream);
+
+ if (c->auth_timeout_event) {
c->protocol->core->mainloop->time_free(c->auth_timeout_event);
+ c->auth_timeout_event = NULL;
+ }
- pa_xfree(c);
+ pa_assert_se(pa_idxset_remove_by_data(c->protocol->connections, c, NULL) == c);
+ c->protocol = NULL;
+ connection_unref(c);
}
-static void request_bytes(struct playback_stream *s) {
- pa_tagstruct *t;
- size_t l;
- assert(s);
+static void connection_free(pa_object *o) {
+ connection *c = CONNECTION(o);
- if (!(l = pa_memblockq_missing(s->memblockq)))
- return;
+ pa_assert(c);
- if (l <= s->requested_bytes)
- return;
+ connection_unlink(c);
- l -= s->requested_bytes;
+ pa_idxset_free(c->record_streams, NULL, NULL);
+ pa_idxset_free(c->output_streams, NULL, NULL);
- if (l < pa_memblockq_get_minreq(s->memblockq))
- return;
+ pa_pdispatch_unref(c->pdispatch);
+ pa_pstream_unref(c->pstream);
+ pa_client_free(c->client);
- s->requested_bytes += l;
+ pa_xfree(c);
+}
- t = pa_tagstruct_new(NULL, 0);
- assert(t);
- pa_tagstruct_putu32(t, PA_COMMAND_REQUEST);
- pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
- pa_tagstruct_putu32(t, s->index);
- pa_tagstruct_putu32(t, l);
- pa_pstream_send_tagstruct(s->connection->pstream, t);
+/* Called from thread context */
+static void request_bytes(playback_stream *s) {
+ size_t m, previous_missing;
-/* pa_log("Requesting %u bytes", l); */
+ 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(struct connection *c) {
+static void send_memblock(connection *c) {
uint32_t start;
- struct record_stream *r;
+ record_stream *r;
start = PA_IDXSET_INVALID;
for (;;) {
pa_memchunk chunk;
- if (!(r = pa_idxset_rrobin(c->record_streams, &c->rrobin_index)))
+ if (!(r = RECORD_STREAM(pa_idxset_rrobin(c->record_streams, &c->rrobin_index))))
return;
if (start == PA_IDXSET_INVALID)
@@ -571,7 +820,8 @@ static void send_memblock(struct connection *c) {
schunk.length = r->fragment_size;
pa_pstream_send_memblock(c->pstream, r->index, 0, PA_SEEK_RELATIVE, &schunk);
- pa_memblockq_drop(r->memblockq, &chunk, schunk.length);
+
+ pa_memblockq_drop(r->memblockq, schunk.length);
pa_memblock_unref(schunk.memblock);
return;
@@ -579,9 +829,9 @@ static void send_memblock(struct connection *c) {
}
}
-static void send_playback_stream_killed(struct playback_stream *p) {
+static void send_playback_stream_killed(playback_stream *p) {
pa_tagstruct *t;
- assert(p);
+ playback_stream_assert_ref(p);
t = pa_tagstruct_new(NULL, 0);
pa_tagstruct_putu32(t, PA_COMMAND_PLAYBACK_STREAM_KILLED);
@@ -590,9 +840,9 @@ static void send_playback_stream_killed(struct playback_stream *p) {
pa_pstream_send_tagstruct(p->connection->pstream, t);
}
-static void send_record_stream_killed(struct record_stream *r) {
+static void send_record_stream_killed(record_stream *r) {
pa_tagstruct *t;
- assert(r);
+ record_stream_assert_ref(r);
t = pa_tagstruct_new(NULL, 0);
pa_tagstruct_putu32(t, PA_COMMAND_RECORD_STREAM_KILLED);
@@ -601,96 +851,219 @@ static void send_record_stream_killed(struct record_stream *r) {
pa_pstream_send_tagstruct(r->connection->pstream, t);
}
-/*** sinkinput callbacks ***/
+/*** sink input callbacks ***/
-static int sink_input_peek_cb(pa_sink_input *i, pa_memchunk *chunk) {
- struct playback_stream *s;
- assert(i && i->userdata && chunk);
- s = i->userdata;
+/* Called from thread context */
+static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk) {
+ pa_sink_input *i = PA_SINK_INPUT(o);
+ playback_stream *s;
- if (pa_memblockq_get_length(s->memblockq) <= 0 && !s->underrun) {
- pa_tagstruct *t;
+ pa_sink_input_assert_ref(i);
+ s = PLAYBACK_STREAM(i->userdata);
+ playback_stream_assert_ref(s);
+
+ switch (code) {
+
+ case SINK_INPUT_MESSAGE_SEEK:
+ pa_memblockq_seek(s->memblockq, offset, PA_PTR_TO_UINT(userdata));
+ request_bytes(s);
+ return 0;
+
+ case SINK_INPUT_MESSAGE_POST_DATA: {
+ pa_assert(chunk);
+
+/* pa_log("sink input post: %u", chunk->length); */
+
+ 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);
+ }
- /* Report that we're empty */
+ request_bytes(s);
- t = pa_tagstruct_new(NULL, 0);
- pa_tagstruct_putu32(t, PA_COMMAND_UNDERFLOW);
- pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
- pa_tagstruct_putu32(t, s->index);
- pa_pstream_send_tagstruct(s->connection->pstream, t);
+ 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);
+
+ return 0;
+ }
+
+ case SINK_INPUT_MESSAGE_FLUSH:
+ case SINK_INPUT_MESSAGE_PREBUF_FORCE:
+ case SINK_INPUT_MESSAGE_TRIGGER: {
+
+ pa_sink_input *isync;
+ void (*func)(pa_memblockq *bq);
+
+ switch (code) {
+ case SINK_INPUT_MESSAGE_FLUSH:
+ func = pa_memblockq_flush;
+ break;
+
+ case SINK_INPUT_MESSAGE_PREBUF_FORCE:
+ func = pa_memblockq_prebuf_force;
+ break;
+
+ case SINK_INPUT_MESSAGE_TRIGGER:
+ func = pa_memblockq_prebuf_disable;
+ break;
+
+ default:
+ pa_assert_not_reached();
+ }
+
+ func(s->memblockq);
+ s->underrun = 0;
+ request_bytes(s);
+
+ /* 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);
+ func(ssync->memblockq);
+ ssync->underrun = 0;
+ request_bytes(ssync);
+ }
+
+ for (isync = i->sync_next; isync; isync = isync->sync_next) {
+ playback_stream *ssync = PLAYBACK_STREAM(isync->userdata);
+ func(ssync->memblockq);
+ ssync->underrun = 0;
+ request_bytes(ssync);
+ }
+
+ return 0;
+ }
+
+ case SINK_INPUT_MESSAGE_UPDATE_LATENCY:
+
+ 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;
+ return 0;
+
+ case PA_SINK_INPUT_MESSAGE_SET_STATE:
+
+ pa_memblockq_prebuf_force(s->memblockq);
+ request_bytes(s);
+ break;
+
+ case PA_SINK_INPUT_MESSAGE_GET_LATENCY: {
+ pa_usec_t *r = userdata;
+
+ *r = pa_bytes_to_usec(pa_memblockq_get_length(s->memblockq), &i->sample_spec);
+
+ /* Fall through, the default handler will add in the extra
+ * latency added by the resampler */
+ break;
+ }
+ }
+
+ return pa_sink_input_process_msg(o, code, userdata, offset, chunk);
+}
+
+/* Called from thread context */
+static int sink_input_peek_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) {
+ playback_stream *s;
+
+ pa_sink_input_assert_ref(i);
+ s = PLAYBACK_STREAM(i->userdata);
+ playback_stream_assert_ref(s);
+ pa_assert(chunk);
+
+ if (pa_memblockq_get_length(s->memblockq) <= 0 && !s->underrun) {
+ pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_UNDERFLOW, NULL, 0, NULL, NULL);
s->underrun = 1;
}
if (pa_memblockq_peek(s->memblockq, chunk) < 0) {
-/* pa_log("peek: failure"); */
+/* pa_log("peek: failure"); */
return -1;
}
-/* pa_log("peek: %u", chunk->length); */
+/* pa_log("peek: %u", chunk->length); */
+
+ request_bytes(s);
return 0;
}
-static void sink_input_drop_cb(pa_sink_input *i, const pa_memchunk *chunk, size_t length) {
- struct playback_stream *s;
- assert(i && i->userdata && length);
- s = i->userdata;
+/* Called from thread context */
+static void sink_input_drop_cb(pa_sink_input *i, size_t length) {
+ playback_stream *s;
- pa_memblockq_drop(s->memblockq, chunk, length);
+ pa_sink_input_assert_ref(i);
+ s = PLAYBACK_STREAM(i->userdata);
+ playback_stream_assert_ref(s);
+ pa_assert(length > 0);
- request_bytes(s);
+ pa_memblockq_drop(s->memblockq, length);
if (s->drain_request && !pa_memblockq_is_readable(s->memblockq)) {
- pa_pstream_send_simple_ack(s->connection->pstream, s->drain_tag);
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_log("after_drop: %u %u", pa_memblockq_get_length(s->memblockq), pa_memblockq_is_readable(s->memblockq)); */
-}
+ request_bytes(s);
-static void sink_input_kill_cb(pa_sink_input *i) {
- assert(i && i->userdata);
- send_playback_stream_killed((struct playback_stream *) i->userdata);
- playback_stream_free((struct playback_stream *) i->userdata);
+/* pa_log("after_drop: %u %u", pa_memblockq_get_length(s->memblockq), pa_memblockq_is_readable(s->memblockq)); */
}
-static pa_usec_t sink_input_get_latency_cb(pa_sink_input *i) {
- struct playback_stream *s;
- assert(i && i->userdata);
- s = i->userdata;
+static void sink_input_kill_cb(pa_sink_input *i) {
+ playback_stream *s;
- /*pa_log("get_latency: %u", pa_memblockq_get_length(s->memblockq));*/
+ pa_sink_input_assert_ref(i);
+ s = PLAYBACK_STREAM(i->userdata);
+ playback_stream_assert_ref(s);
- return pa_bytes_to_usec(pa_memblockq_get_length(s->memblockq), &s->sink_input->sample_spec);
+ send_playback_stream_killed(s);
+ playback_stream_unlink(s);
}
/*** source_output callbacks ***/
+/* Called from thread context */
static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk) {
- struct record_stream *s;
- assert(o && o->userdata && chunk);
- s = o->userdata;
+ record_stream *s;
- if (pa_memblockq_push_align(s->memblockq, chunk) < 0) {
- pa_log_warn("Failed to push data into output queue.");
- return;
- }
+ pa_source_output_assert_ref(o);
+ s = RECORD_STREAM(o->userdata);
+ record_stream_assert_ref(s);
+ pa_assert(chunk);
- if (!pa_pstream_is_pending(s->connection->pstream))
- send_memblock(s->connection);
+ pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), RECORD_STREAM_MESSAGE_POST_DATA, NULL, 0, chunk, NULL);
}
static void source_output_kill_cb(pa_source_output *o) {
- assert(o && o->userdata);
- send_record_stream_killed((struct record_stream *) o->userdata);
- record_stream_free((struct record_stream *) o->userdata);
+ record_stream *s;
+
+ pa_source_output_assert_ref(o);
+ s = RECORD_STREAM(o->userdata);
+ record_stream_assert_ref(s);
+
+ send_record_stream_killed(s);
+ record_stream_unlink(s);
}
static pa_usec_t source_output_get_latency_cb(pa_source_output *o) {
- struct record_stream *s;
- assert(o && o->userdata);
- s = o->userdata;
+ record_stream *s;
+
+ pa_source_output_assert_ref(o);
+ s = RECORD_STREAM(o->userdata);
+ record_stream_assert_ref(s);
/*pa_log("get_latency: %u", pa_memblockq_get_length(s->memblockq));*/
@@ -699,9 +1072,9 @@ static pa_usec_t source_output_get_latency_cb(pa_source_output *o) {
/*** pdispatch callbacks ***/
-static void protocol_error(struct connection *c) {
+static void protocol_error(connection *c) {
pa_log("protocol error, kicking client");
- connection_free(c);
+ connection_unlink(c);
}
#define CHECK_VALIDITY(pstream, expression, tag, error) do { \
@@ -721,9 +1094,9 @@ static pa_tagstruct *reply_new(uint32_t tag) {
}
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) {
- struct connection *c = userdata;
- struct playback_stream *s;
- uint32_t maxlength, tlength, prebuf, minreq, sink_index, syncid;
+ connection *c = CONNECTION(userdata);
+ playback_stream *s;
+ uint32_t maxlength, tlength, prebuf, minreq, sink_index, syncid, missing;
const char *name, *sink_name;
pa_sample_spec ss;
pa_channel_map map;
@@ -732,7 +1105,8 @@ static void command_create_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GC
pa_cvolume volume;
int corked;
- assert(c && t && c->protocol && c->protocol->core);
+ connection_assert_ref(c);
+ pa_assert(t);
if (pa_tagstruct_get(
t,
@@ -773,34 +1147,35 @@ static void command_create_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GC
CHECK_VALIDITY(c->pstream, sink, tag, PA_ERR_NOENTITY);
}
- s = playback_stream_new(c, sink, &ss, &map, name, maxlength, tlength, prebuf, minreq, &volume, syncid);
+ s = playback_stream_new(c, sink, &ss, &map, name, &maxlength, &tlength, &prebuf, &minreq, &volume, syncid, corked, &missing);
CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_INVALID);
- pa_sink_input_cork(s->sink_input, corked);
-
reply = reply_new(tag);
pa_tagstruct_putu32(reply, s->index);
- assert(s->sink_input);
+ pa_assert(s->sink_input);
pa_tagstruct_putu32(reply, s->sink_input->index);
- pa_tagstruct_putu32(reply, s->requested_bytes = pa_memblockq_missing(s->memblockq));
+ pa_tagstruct_putu32(reply, missing);
+
+/* pa_log("initial request is %u", missing); */
if (c->version >= 9) {
/* Since 0.9 we support sending the buffer metrics back to the client */
- 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, (uint32_t) maxlength);
+ pa_tagstruct_putu32(reply, (uint32_t) tlength);
+ pa_tagstruct_putu32(reply, (uint32_t) prebuf);
+ pa_tagstruct_putu32(reply, (uint32_t) minreq);
}
pa_pstream_send_tagstruct(c->pstream, reply);
- request_bytes(s);
}
static void command_delete_stream(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
- struct connection *c = userdata;
+ connection *c = CONNECTION(userdata);
uint32_t channel;
- assert(c && t);
+
+ connection_assert_ref(c);
+ pa_assert(t);
if (pa_tagstruct_getu32(t, &channel) < 0 ||
!pa_tagstruct_eof(t)) {
@@ -810,39 +1185,52 @@ static void command_delete_stream(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t comma
CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
- if (command == PA_COMMAND_DELETE_PLAYBACK_STREAM) {
- struct playback_stream *s;
- if (!(s = pa_idxset_get_by_index(c->output_streams, channel)) || (s->type != PLAYBACK_STREAM)) {
- pa_pstream_send_error(c->pstream, tag, PA_ERR_EXIST);
- return;
+ switch (command) {
+
+ case PA_COMMAND_DELETE_PLAYBACK_STREAM: {
+ playback_stream *s;
+ if (!(s = pa_idxset_get_by_index(c->output_streams, channel)) || !playback_stream_isinstance(s)) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERR_EXIST);
+ return;
+ }
+
+ playback_stream_unlink(s);
+ break;
}
- playback_stream_free(s);
- } else if (command == PA_COMMAND_DELETE_RECORD_STREAM) {
- struct record_stream *s;
- if (!(s = pa_idxset_get_by_index(c->record_streams, channel))) {
- pa_pstream_send_error(c->pstream, tag, PA_ERR_EXIST);
- return;
+ case PA_COMMAND_DELETE_RECORD_STREAM: {
+ record_stream *s;
+ if (!(s = pa_idxset_get_by_index(c->record_streams, channel))) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERR_EXIST);
+ return;
+ }
+
+ record_stream_unlink(s);
+ break;
}
- record_stream_free(s);
- } else {
- struct upload_stream *s;
- assert(command == PA_COMMAND_DELETE_UPLOAD_STREAM);
- if (!(s = pa_idxset_get_by_index(c->output_streams, channel)) || (s->type != UPLOAD_STREAM)) {
- pa_pstream_send_error(c->pstream, tag, PA_ERR_EXIST);
- return;
+ case PA_COMMAND_DELETE_UPLOAD_STREAM: {
+ upload_stream *s;
+
+ if (!(s = pa_idxset_get_by_index(c->output_streams, channel)) || !upload_stream_isinstance(s)) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERR_EXIST);
+ return;
+ }
+
+ upload_stream_unlink(s);
+ break;
}
- upload_stream_free(s);
+ default:
+ pa_assert_not_reached();
}
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) {
- struct connection *c = userdata;
- struct record_stream *s;
+ connection *c = CONNECTION(userdata);
+ record_stream *s;
uint32_t maxlength, fragment_size;
uint32_t source_index;
const char *name, *source_name;
@@ -851,7 +1239,9 @@ static void command_create_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_
pa_tagstruct *reply;
pa_source *source = NULL;
int corked;
- assert(c && t && c->protocol && c->protocol->core);
+
+ connection_assert_ref(c);
+ pa_assert(t);
if (pa_tagstruct_gets(t, &name) < 0 ||
pa_tagstruct_get_sample_spec(t, &ss) < 0 ||
@@ -882,20 +1272,18 @@ static void command_create_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_
CHECK_VALIDITY(c->pstream, source, tag, PA_ERR_NOENTITY);
}
- s = record_stream_new(c, source, &ss, &map, name, maxlength, fragment_size);
+ s = record_stream_new(c, source, &ss, &map, name, &maxlength, fragment_size, corked);
CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_INVALID);
- pa_source_output_cork(s->source_output, corked);
-
reply = reply_new(tag);
pa_tagstruct_putu32(reply, s->index);
- assert(s->source_output);
+ pa_assert(s->source_output);
pa_tagstruct_putu32(reply, s->source_output->index);
if (c->version >= 9) {
/* Since 0.9 we support sending the buffer metrics back to the client */
- pa_tagstruct_putu32(reply, (uint32_t) pa_memblockq_get_maxlength(s->memblockq));
+ pa_tagstruct_putu32(reply, (uint32_t) maxlength);
pa_tagstruct_putu32(reply, (uint32_t) s->fragment_size);
}
@@ -903,8 +1291,10 @@ static void command_create_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_
}
static void command_exit(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
- struct connection *c = userdata;
- assert(c && t);
+ connection *c = CONNECTION(userdata);
+
+ connection_assert_ref(c);
+ pa_assert(t);
if (!pa_tagstruct_eof(t)) {
protocol_error(c);
@@ -913,16 +1303,17 @@ 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);
- assert(c->protocol && c->protocol->core && c->protocol->core->mainloop);
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) {
- struct connection *c = userdata;
+ connection *c = CONNECTION(userdata);
const void*cookie;
pa_tagstruct *reply;
- assert(c && t);
+
+ connection_assert_ref(c);
+ pa_assert(t);
if (pa_tagstruct_getu32(t, &c->version) < 0 ||
pa_tagstruct_get_arbitrary(t, &cookie, PA_NATIVE_COOKIE_LENGTH) < 0 ||
@@ -1015,9 +1406,11 @@ static void command_auth(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t
}
static void command_set_client_name(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
- struct connection *c = userdata;
+ connection *c = CONNECTION(userdata);
const char *name;
- assert(c && t);
+
+ connection_assert_ref(c);
+ pa_assert(t);
if (pa_tagstruct_gets(t, &name) < 0 ||
!pa_tagstruct_eof(t)) {
@@ -1032,10 +1425,12 @@ static void command_set_client_name(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSE
}
static void command_lookup(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
- struct connection *c = userdata;
+ connection *c = CONNECTION(userdata);
const char *name;
uint32_t idx = PA_IDXSET_INVALID;
- assert(c && t);
+
+ connection_assert_ref(c);
+ pa_assert(t);
if (pa_tagstruct_gets(t, &name) < 0 ||
!pa_tagstruct_eof(t)) {
@@ -1052,7 +1447,7 @@ static void command_lookup(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uin
idx = sink->index;
} else {
pa_source *source;
- assert(command == PA_COMMAND_LOOKUP_SOURCE);
+ pa_assert(command == PA_COMMAND_LOOKUP_SOURCE);
if ((source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE, 1)))
idx = source->index;
}
@@ -1068,10 +1463,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) {
- struct connection *c = userdata;
+ connection *c = CONNECTION(userdata);
uint32_t idx;
- struct playback_stream *s;
- assert(c && t);
+ playback_stream *s;
+
+ connection_assert_ref(c);
+ pa_assert(t);
if (pa_tagstruct_getu32(t, &idx) < 0 ||
!pa_tagstruct_eof(t)) {
@@ -1082,29 +1479,18 @@ static void command_drain_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC
CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
s = pa_idxset_get_by_index(c->output_streams, idx);
CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
- CHECK_VALIDITY(c->pstream, s->type == PLAYBACK_STREAM, tag, PA_ERR_NOENTITY);
+ CHECK_VALIDITY(c->pstream, playback_stream_isinstance(s), tag, PA_ERR_NOENTITY);
- s->drain_request = 0;
-
- pa_memblockq_prebuf_disable(s->memblockq);
-
- if (!pa_memblockq_is_readable(s->memblockq)) {
-/* pa_log("immediate drain: %u", pa_memblockq_get_length(s->memblockq)); */
- pa_pstream_send_simple_ack(c->pstream, tag);
- } else {
-/* pa_log("slow drain triggered"); */
- s->drain_request = 1;
- s->drain_tag = tag;
-
- pa_sink_notify(s->sink_input->sink);
- }
+ 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) {
- struct connection *c = userdata;
+ connection *c = CONNECTION(userdata);
pa_tagstruct *reply;
const pa_mempool_stat *stat;
- assert(c && t);
+
+ connection_assert_ref(c);
+ pa_assert(t);
if (!pa_tagstruct_eof(t)) {
protocol_error(c);
@@ -1125,13 +1511,15 @@ static void command_stat(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t
}
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) {
- struct connection *c = userdata;
+ connection *c = CONNECTION(userdata);
pa_tagstruct *reply;
- struct playback_stream *s;
+ playback_stream *s;
struct timeval tv, now;
uint32_t idx;
pa_usec_t latency;
- assert(c && t);
+
+ connection_assert_ref(c);
+ pa_assert(t);
if (pa_tagstruct_getu32(t, &idx) < 0 ||
pa_tagstruct_get_timeval(t, &tv) < 0 ||
@@ -1143,31 +1531,34 @@ static void command_get_playback_latency(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_
CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
s = pa_idxset_get_by_index(c->output_streams, idx);
CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
- CHECK_VALIDITY(c->pstream, s->type == PLAYBACK_STREAM, tag, PA_ERR_NOENTITY);
+ CHECK_VALIDITY(c->pstream, playback_stream_isinstance(s), tag, PA_ERR_NOENTITY);
+ CHECK_VALIDITY(c->pstream, pa_asyncmsgq_send(s->sink_input->sink->asyncmsgq, PA_MSGOBJECT(s->sink_input), SINK_INPUT_MESSAGE_UPDATE_LATENCY, s, 0, NULL) == 0, tag, PA_ERR_NOENTITY)
reply = reply_new(tag);
latency = pa_sink_get_latency(s->sink_input->sink);
- if (s->sink_input->resampled_chunk.memblock)
- latency += pa_bytes_to_usec(s->sink_input->resampled_chunk.length, &s->sink_input->sample_spec);
+ latency += pa_bytes_to_usec(s->resampled_chunk_length, &s->sink_input->sample_spec);
+
pa_tagstruct_put_usec(reply, latency);
pa_tagstruct_put_usec(reply, 0);
- pa_tagstruct_put_boolean(reply, s->sink_input->state == PA_SINK_INPUT_RUNNING);
+ pa_tagstruct_put_boolean(reply, pa_sink_input_get_state(s->sink_input) == PA_SINK_INPUT_RUNNING);
pa_tagstruct_put_timeval(reply, &tv);
pa_tagstruct_put_timeval(reply, pa_gettimeofday(&now));
- pa_tagstruct_puts64(reply, pa_memblockq_get_write_index(s->memblockq));
- pa_tagstruct_puts64(reply, pa_memblockq_get_read_index(s->memblockq));
+ pa_tagstruct_puts64(reply, s->write_index);
+ pa_tagstruct_puts64(reply, s->read_index);
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) {
- struct connection *c = userdata;
+ connection *c = CONNECTION(userdata);
pa_tagstruct *reply;
- struct record_stream *s;
+ record_stream *s;
struct timeval tv, now;
uint32_t idx;
- assert(c && t);
+
+ connection_assert_ref(c);
+ pa_assert(t);
if (pa_tagstruct_getu32(t, &idx) < 0 ||
pa_tagstruct_get_timeval(t, &tv) < 0 ||
@@ -1192,14 +1583,16 @@ static void command_get_record_latency(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UN
}
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) {
- struct connection *c = userdata;
- struct upload_stream *s;
+ connection *c = CONNECTION(userdata);
+ upload_stream *s;
uint32_t length;
const char *name;
pa_sample_spec ss;
pa_channel_map map;
pa_tagstruct *reply;
- assert(c && t && c->protocol && c->protocol->core);
+
+ connection_assert_ref(c);
+ pa_assert(t);
if (pa_tagstruct_gets(t, &name) < 0 ||
pa_tagstruct_get_sample_spec(t, &ss) < 0 ||
@@ -1228,11 +1621,13 @@ static void command_create_upload_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_
}
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) {
- struct connection *c = userdata;
+ connection *c = CONNECTION(userdata);
uint32_t channel;
- struct upload_stream *s;
+ upload_stream *s;
uint32_t idx;
- assert(c && t);
+
+ connection_assert_ref(c);
+ pa_assert(t);
if (pa_tagstruct_getu32(t, &channel) < 0 ||
!pa_tagstruct_eof(t)) {
@@ -1244,23 +1639,25 @@ static void command_finish_upload_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_
s = pa_idxset_get_by_index(c->output_streams, channel);
CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
- CHECK_VALIDITY(c->pstream, s->type == UPLOAD_STREAM, 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)
pa_pstream_send_error(c->pstream, tag, PA_ERR_INTERNAL);
else
pa_pstream_send_simple_ack(c->pstream, tag);
- upload_stream_free(s);
+ 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) {
- struct connection *c = userdata;
+ connection *c = CONNECTION(userdata);
uint32_t sink_index;
pa_volume_t volume;
pa_sink *sink;
const char *name, *sink_name;
- assert(c && t);
+
+ connection_assert_ref(c);
+ pa_assert(t);
if (pa_tagstruct_getu32(t, &sink_index) < 0 ||
pa_tagstruct_gets(t, &sink_name) < 0 ||
@@ -1291,9 +1688,11 @@ static void command_play_sample(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED ui
}
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) {
- struct connection *c = userdata;
+ connection *c = CONNECTION(userdata);
const char *name;
- assert(c && t);
+
+ connection_assert_ref(c);
+ pa_assert(t);
if (pa_tagstruct_gets(t, &name) < 0 ||
!pa_tagstruct_eof(t)) {
@@ -1313,7 +1712,9 @@ static void command_remove_sample(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED
}
static void sink_fill_tagstruct(pa_tagstruct *t, pa_sink *sink) {
- assert(t && sink);
+ pa_assert(t);
+ pa_sink_assert_ref(sink);
+
pa_tagstruct_put(
t,
PA_TAG_U32, sink->index,
@@ -1321,22 +1722,21 @@ static void sink_fill_tagstruct(pa_tagstruct *t, pa_sink *sink) {
PA_TAG_STRING, sink->description,
PA_TAG_SAMPLE_SPEC, &sink->sample_spec,
PA_TAG_CHANNEL_MAP, &sink->channel_map,
- PA_TAG_U32, sink->owner ? sink->owner->index : PA_INVALID_INDEX,
- PA_TAG_CVOLUME, pa_sink_get_volume(sink, PA_MIXER_HARDWARE),
- PA_TAG_BOOLEAN, pa_sink_get_mute(sink, PA_MIXER_HARDWARE),
+ 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_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->get_hw_volume ? PA_SINK_HW_VOLUME_CTRL : 0) |
- (sink->get_latency ? PA_SINK_LATENCY : 0) |
- (sink->is_hardware ? PA_SINK_HARDWARE : 0),
+ PA_TAG_U32, sink->flags,
PA_TAG_INVALID);
}
static void source_fill_tagstruct(pa_tagstruct *t, pa_source *source) {
- assert(t && source);
+ pa_assert(t);
+ pa_source_assert_ref(source);
+
pa_tagstruct_put(
t,
PA_TAG_U32, source->index,
@@ -1344,22 +1744,21 @@ static void source_fill_tagstruct(pa_tagstruct *t, pa_source *source) {
PA_TAG_STRING, source->description,
PA_TAG_SAMPLE_SPEC, &source->sample_spec,
PA_TAG_CHANNEL_MAP, &source->channel_map,
- PA_TAG_U32, source->owner ? source->owner->index : PA_INVALID_INDEX,
- PA_TAG_CVOLUME, pa_source_get_volume(source, PA_MIXER_HARDWARE),
- PA_TAG_BOOLEAN, pa_source_get_mute(source, PA_MIXER_HARDWARE),
+ 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_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->get_hw_volume ? PA_SOURCE_HW_VOLUME_CTRL : 0) |
- (source->get_latency ? PA_SOURCE_LATENCY : 0) |
- (source->is_hardware ? PA_SOURCE_HARDWARE : 0),
+ PA_TAG_U32, source->flags,
PA_TAG_INVALID);
}
static void client_fill_tagstruct(pa_tagstruct *t, pa_client *client) {
- assert(t && 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);
@@ -1367,7 +1766,9 @@ static void client_fill_tagstruct(pa_tagstruct *t, pa_client *client) {
}
static void module_fill_tagstruct(pa_tagstruct *t, pa_module *module) {
- assert(t && module);
+ pa_assert(t);
+ pa_assert(module);
+
pa_tagstruct_putu32(t, module->index);
pa_tagstruct_puts(t, module->name);
pa_tagstruct_puts(t, module->argument);
@@ -1375,8 +1776,10 @@ static void module_fill_tagstruct(pa_tagstruct *t, pa_module *module) {
pa_tagstruct_put_boolean(t, module->auto_unload);
}
-static void sink_input_fill_tagstruct(pa_tagstruct *t, pa_sink_input *s) {
- assert(t && s);
+static void sink_input_fill_tagstruct(connection *c, pa_tagstruct *t, pa_sink_input *s) {
+ pa_assert(t);
+ pa_sink_input_assert_ref(s);
+
pa_tagstruct_putu32(t, s->index);
pa_tagstruct_puts(t, s->name);
pa_tagstruct_putu32(t, s->module ? s->module->index : PA_INVALID_INDEX);
@@ -1389,10 +1792,14 @@ static void sink_input_fill_tagstruct(pa_tagstruct *t, pa_sink_input *s) {
pa_tagstruct_put_usec(t, pa_sink_get_latency(s->sink));
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));
}
static void source_output_fill_tagstruct(pa_tagstruct *t, pa_source_output *s) {
- assert(t && s);
+ pa_assert(t);
+ pa_source_output_assert_ref(s);
+
pa_tagstruct_putu32(t, s->index);
pa_tagstruct_puts(t, s->name);
pa_tagstruct_putu32(t, s->module ? s->module->index : PA_INVALID_INDEX);
@@ -1407,7 +1814,9 @@ static void source_output_fill_tagstruct(pa_tagstruct *t, pa_source_output *s) {
}
static void scache_fill_tagstruct(pa_tagstruct *t, pa_scache_entry *e) {
- assert(t && e);
+ pa_assert(t);
+ pa_assert(e);
+
pa_tagstruct_putu32(t, e->index);
pa_tagstruct_puts(t, e->name);
pa_tagstruct_put_cvolume(t, &e->volume);
@@ -1420,7 +1829,7 @@ static void scache_fill_tagstruct(pa_tagstruct *t, pa_scache_entry *e) {
}
static void command_get_info(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
- struct connection *c = userdata;
+ connection *c = CONNECTION(userdata);
uint32_t idx;
pa_sink *sink = NULL;
pa_source *source = NULL;
@@ -1431,7 +1840,9 @@ static void command_get_info(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, u
pa_scache_entry *sce = NULL;
const char *name;
pa_tagstruct *reply;
- assert(c && t);
+
+ connection_assert_ref(c);
+ pa_assert(t);
if (pa_tagstruct_getu32(t, &idx) < 0 ||
(command != PA_COMMAND_GET_CLIENT_INFO &&
@@ -1466,7 +1877,7 @@ static void command_get_info(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, u
else if (command == PA_COMMAND_GET_SOURCE_OUTPUT_INFO)
so = pa_idxset_get_by_index(c->protocol->core->source_outputs, idx);
else {
- assert(command == PA_COMMAND_GET_SAMPLE_INFO);
+ pa_assert(command == PA_COMMAND_GET_SAMPLE_INFO);
if (idx != PA_INVALID_INDEX)
sce = pa_idxset_get_by_index(c->protocol->core->scache, idx);
else
@@ -1488,7 +1899,7 @@ static void command_get_info(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, u
else if (module)
module_fill_tagstruct(reply, module);
else if (si)
- sink_input_fill_tagstruct(reply, si);
+ sink_input_fill_tagstruct(c, reply, si);
else if (so)
source_output_fill_tagstruct(reply, so);
else
@@ -1497,12 +1908,14 @@ static void command_get_info(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, u
}
static void command_get_info_list(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
- struct connection *c = userdata;
+ connection *c = CONNECTION(userdata);
pa_idxset *i;
uint32_t idx;
void *p;
pa_tagstruct *reply;
- assert(c && t);
+
+ connection_assert_ref(c);
+ pa_assert(t);
if (!pa_tagstruct_eof(t)) {
protocol_error(c);
@@ -1526,7 +1939,7 @@ static void command_get_info_list(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t comma
else if (command == PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST)
i = c->protocol->core->source_outputs;
else {
- assert(command == PA_COMMAND_GET_SAMPLE_INFO_LIST);
+ pa_assert(command == PA_COMMAND_GET_SAMPLE_INFO_LIST);
i = c->protocol->core->scache;
}
@@ -1541,11 +1954,11 @@ static void command_get_info_list(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t comma
else if (command == PA_COMMAND_GET_MODULE_INFO_LIST)
module_fill_tagstruct(reply, p);
else if (command == PA_COMMAND_GET_SINK_INPUT_INFO_LIST)
- sink_input_fill_tagstruct(reply, p);
+ sink_input_fill_tagstruct(c, reply, p);
else if (command == PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST)
source_output_fill_tagstruct(reply, p);
else {
- assert(command == PA_COMMAND_GET_SAMPLE_INFO_LIST);
+ pa_assert(command == PA_COMMAND_GET_SAMPLE_INFO_LIST);
scache_fill_tagstruct(reply, p);
}
}
@@ -1555,11 +1968,13 @@ static void command_get_info_list(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t comma
}
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) {
- struct connection *c = userdata;
+ connection *c = CONNECTION(userdata);
pa_tagstruct *reply;
char txt[256];
const char *n;
- assert(c && t);
+
+ connection_assert_ref(c);
+ pa_assert(t);
if (!pa_tagstruct_eof(t)) {
protocol_error(c);
@@ -1587,8 +2002,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;
- struct connection *c = userdata;
- assert(c && core);
+ connection *c = CONNECTION(userdata);
+
+ connection_assert_ref(c);
t = pa_tagstruct_new(NULL, 0);
pa_tagstruct_putu32(t, PA_COMMAND_SUBSCRIBE_EVENT);
@@ -1599,9 +2015,11 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t e, uint3
}
static void command_subscribe(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
- struct connection *c = userdata;
+ connection *c = CONNECTION(userdata);
pa_subscription_mask_t m;
- assert(c && t);
+
+ connection_assert_ref(c);
+ pa_assert(t);
if (pa_tagstruct_getu32(t, &m) < 0 ||
!pa_tagstruct_eof(t)) {
@@ -1617,7 +2035,7 @@ static void command_subscribe(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint
if (m != 0) {
c->subscription = pa_subscription_new(c->protocol->core, m, subscription_cb, c);
- assert(c->subscription);
+ pa_assert(c->subscription);
} else
c->subscription = NULL;
@@ -1631,14 +2049,16 @@ static void command_set_volume(
pa_tagstruct *t,
void *userdata) {
- struct connection *c = userdata;
+ connection *c = CONNECTION(userdata);
uint32_t idx;
pa_cvolume volume;
pa_sink *sink = NULL;
pa_source *source = NULL;
pa_sink_input *si = NULL;
const char *name = NULL;
- assert(c && t);
+
+ connection_assert_ref(c);
+ pa_assert(t);
if (pa_tagstruct_getu32(t, &idx) < 0 ||
(command == PA_COMMAND_SET_SINK_VOLUME && pa_tagstruct_gets(t, &name) < 0) ||
@@ -1653,27 +2073,36 @@ static void command_set_volume(
CHECK_VALIDITY(c->pstream, idx != PA_INVALID_INDEX || !name || (*name && pa_utf8_valid(name)), tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, pa_cvolume_valid(&volume), tag, PA_ERR_INVALID);
- if (command == PA_COMMAND_SET_SINK_VOLUME) {
- if (idx != PA_INVALID_INDEX)
- sink = pa_idxset_get_by_index(c->protocol->core->sinks, idx);
- else
- sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK, 1);
- } else if (command == PA_COMMAND_SET_SOURCE_VOLUME) {
- if (idx != (uint32_t) -1)
- source = pa_idxset_get_by_index(c->protocol->core->sources, idx);
- else
- source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE, 1);
- } else {
- assert(command == PA_COMMAND_SET_SINK_INPUT_VOLUME);
- si = pa_idxset_get_by_index(c->protocol->core->sink_inputs, idx);
+ switch (command) {
+
+ case PA_COMMAND_SET_SINK_VOLUME:
+ if (idx != PA_INVALID_INDEX)
+ sink = pa_idxset_get_by_index(c->protocol->core->sinks, idx);
+ else
+ sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK, 1);
+ break;
+
+ case PA_COMMAND_SET_SOURCE_VOLUME:
+ if (idx != PA_INVALID_INDEX)
+ source = pa_idxset_get_by_index(c->protocol->core->sources, idx);
+ else
+ source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE, 1);
+ break;
+
+ case PA_COMMAND_SET_SINK_INPUT_VOLUME:
+ si = pa_idxset_get_by_index(c->protocol->core->sink_inputs, idx);
+ break;
+
+ default:
+ pa_assert_not_reached();
}
CHECK_VALIDITY(c->pstream, si || sink || source, tag, PA_ERR_NOENTITY);
if (sink)
- pa_sink_set_volume(sink, PA_MIXER_HARDWARE, &volume);
+ pa_sink_set_volume(sink, &volume);
else if (source)
- pa_source_set_volume(source, PA_MIXER_HARDWARE, &volume);
+ pa_source_set_volume(source, &volume);
else if (si)
pa_sink_input_set_volume(si, &volume);
@@ -1687,16 +2116,20 @@ static void command_set_mute(
pa_tagstruct *t,
void *userdata) {
- struct connection *c = userdata;
+ connection *c = CONNECTION(userdata);
uint32_t idx;
int mute;
pa_sink *sink = NULL;
pa_source *source = NULL;
+ pa_sink_input *si = NULL;
const char *name = NULL;
- assert(c && t);
+
+ connection_assert_ref(c);
+ pa_assert(t);
if (pa_tagstruct_getu32(t, &idx) < 0 ||
- pa_tagstruct_gets(t, &name) < 0 ||
+ (command == PA_COMMAND_SET_SINK_MUTE && pa_tagstruct_gets(t, &name) < 0) ||
+ (command == PA_COMMAND_SET_SOURCE_MUTE && pa_tagstruct_gets(t, &name) < 0) ||
pa_tagstruct_get_boolean(t, &mute) ||
!pa_tagstruct_eof(t)) {
protocol_error(c);
@@ -1706,35 +2139,53 @@ 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);
- if (command == PA_COMMAND_SET_SINK_MUTE) {
- if (idx != PA_INVALID_INDEX)
- sink = pa_idxset_get_by_index(c->protocol->core->sinks, idx);
- else
- sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK, 1);
- } else {
- assert(command == PA_COMMAND_SET_SOURCE_MUTE);
- if (idx != (uint32_t) -1)
- source = pa_idxset_get_by_index(c->protocol->core->sources, idx);
- else
- source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE, 1);
+ switch (command) {
+
+ case PA_COMMAND_SET_SINK_MUTE:
+
+ if (idx != PA_INVALID_INDEX)
+ sink = pa_idxset_get_by_index(c->protocol->core->sinks, idx);
+ else
+ sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK, 1);
+
+ break;
+
+ case PA_COMMAND_SET_SOURCE_MUTE:
+ if (idx != PA_INVALID_INDEX)
+ source = pa_idxset_get_by_index(c->protocol->core->sources, idx);
+ else
+ source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE, 1);
+
+ break;
+
+ case PA_COMMAND_SET_SINK_INPUT_MUTE:
+ si = pa_idxset_get_by_index(c->protocol->core->sink_inputs, idx);
+ break;
+
+ default:
+ pa_assert_not_reached();
}
- CHECK_VALIDITY(c->pstream, sink || source, tag, PA_ERR_NOENTITY);
+ CHECK_VALIDITY(c->pstream, si || sink || source, tag, PA_ERR_NOENTITY);
if (sink)
- pa_sink_set_mute(sink, PA_MIXER_HARDWARE, mute);
+ pa_sink_set_mute(sink, mute);
else if (source)
- pa_source_set_mute(source, PA_MIXER_HARDWARE, mute);
+ pa_source_set_mute(source, mute);
+ else if (si)
+ pa_sink_input_set_mute(si, 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) {
- struct connection *c = userdata;
+ connection *c = CONNECTION(userdata);
uint32_t idx;
int b;
- struct playback_stream *s, *ssync;
- assert(c && t);
+ playback_stream *s;
+
+ connection_assert_ref(c);
+ pa_assert(t);
if (pa_tagstruct_getu32(t, &idx) < 0 ||
pa_tagstruct_get_boolean(t, &b) < 0 ||
@@ -1747,73 +2198,19 @@ static void command_cork_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_
CHECK_VALIDITY(c->pstream, idx != PA_INVALID_INDEX, tag, PA_ERR_INVALID);
s = pa_idxset_get_by_index(c->output_streams, idx);
CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
- CHECK_VALIDITY(c->pstream, s->type == PLAYBACK_STREAM, tag, PA_ERR_NOENTITY);
+ CHECK_VALIDITY(c->pstream, playback_stream_isinstance(s), tag, PA_ERR_NOENTITY);
pa_sink_input_cork(s->sink_input, b);
- pa_memblockq_prebuf_force(s->memblockq);
-
- /* Do the same for all other members in the sync group */
- for (ssync = s->prev; ssync; ssync = ssync->prev) {
- pa_sink_input_cork(ssync->sink_input, b);
- pa_memblockq_prebuf_force(ssync->memblockq);
- }
-
- for (ssync = s->next; ssync; ssync = ssync->next) {
- pa_sink_input_cork(ssync->sink_input, b);
- pa_memblockq_prebuf_force(ssync->memblockq);
- }
-
pa_pstream_send_simple_ack(c->pstream, tag);
}
-static void command_flush_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
- struct connection *c = userdata;
+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);
uint32_t idx;
- struct playback_stream *s, *ssync;
- assert(c && t);
-
- if (pa_tagstruct_getu32(t, &idx) < 0 ||
- !pa_tagstruct_eof(t)) {
- protocol_error(c);
- return;
- }
-
- CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
- CHECK_VALIDITY(c->pstream, idx != PA_INVALID_INDEX, tag, PA_ERR_INVALID);
- s = pa_idxset_get_by_index(c->output_streams, idx);
- CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
- CHECK_VALIDITY(c->pstream, s->type == PLAYBACK_STREAM, tag, PA_ERR_NOENTITY);
-
- pa_memblockq_flush(s->memblockq);
- s->underrun = 0;
+ playback_stream *s;
- /* Do the same for all other members in the sync group */
- for (ssync = s->prev; ssync; ssync = ssync->prev) {
- pa_memblockq_flush(ssync->memblockq);
- ssync->underrun = 0;
- }
-
- for (ssync = s->next; ssync; ssync = ssync->next) {
- pa_memblockq_flush(ssync->memblockq);
- ssync->underrun = 0;
- }
-
- pa_pstream_send_simple_ack(c->pstream, tag);
- pa_sink_notify(s->sink_input->sink);
- request_bytes(s);
-
- for (ssync = s->prev; ssync; ssync = ssync->prev)
- request_bytes(ssync);
-
- for (ssync = s->next; ssync; ssync = ssync->next)
- request_bytes(ssync);
-}
-
-static void command_trigger_or_prebuf_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
- struct connection *c = userdata;
- uint32_t idx;
- struct playback_stream *s;
- assert(c && t);
+ connection_assert_ref(c);
+ pa_assert(t);
if (pa_tagstruct_getu32(t, &idx) < 0 ||
!pa_tagstruct_eof(t)) {
@@ -1825,32 +2222,36 @@ static void command_trigger_or_prebuf_playback_stream(PA_GCC_UNUSED pa_pdispatch
CHECK_VALIDITY(c->pstream, idx != PA_INVALID_INDEX, tag, PA_ERR_INVALID);
s = pa_idxset_get_by_index(c->output_streams, idx);
CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
- CHECK_VALIDITY(c->pstream, s->type == PLAYBACK_STREAM, tag, PA_ERR_NOENTITY);
+ CHECK_VALIDITY(c->pstream, playback_stream_isinstance(s), tag, PA_ERR_NOENTITY);
switch (command) {
+ case PA_COMMAND_FLUSH_PLAYBACK_STREAM:
+ pa_asyncmsgq_send(s->sink_input->sink->asyncmsgq, PA_MSGOBJECT(s->sink_input), SINK_INPUT_MESSAGE_FLUSH, NULL, 0, NULL);
+ break;
+
case PA_COMMAND_PREBUF_PLAYBACK_STREAM:
- pa_memblockq_prebuf_force(s->memblockq);
+ pa_asyncmsgq_send(s->sink_input->sink->asyncmsgq, PA_MSGOBJECT(s->sink_input), SINK_INPUT_MESSAGE_PREBUF_FORCE, NULL, 0, NULL);
break;
case PA_COMMAND_TRIGGER_PLAYBACK_STREAM:
- pa_memblockq_prebuf_disable(s->memblockq);
+ pa_asyncmsgq_send(s->sink_input->sink->asyncmsgq, PA_MSGOBJECT(s->sink_input), SINK_INPUT_MESSAGE_TRIGGER, NULL, 0, NULL);
break;
default:
- abort();
+ pa_assert_not_reached();
}
- pa_sink_notify(s->sink_input->sink);
pa_pstream_send_simple_ack(c->pstream, tag);
- request_bytes(s);
}
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) {
- struct connection *c = userdata;
+ connection *c = CONNECTION(userdata);
uint32_t idx;
- struct record_stream *s;
+ record_stream *s;
int b;
- assert(c && t);
+
+ connection_assert_ref(c);
+ pa_assert(t);
if (pa_tagstruct_getu32(t, &idx) < 0 ||
pa_tagstruct_get_boolean(t, &b) < 0 ||
@@ -1869,10 +2270,12 @@ static void command_cork_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UN
}
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) {
- struct connection *c = userdata;
+ connection *c = CONNECTION(userdata);
uint32_t idx;
- struct record_stream *s;
- assert(c && t);
+ record_stream *s;
+
+ connection_assert_ref(c);
+ pa_assert(t);
if (pa_tagstruct_getu32(t, &idx) < 0 ||
!pa_tagstruct_eof(t)) {
@@ -1889,9 +2292,11 @@ static void command_flush_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_U
}
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) {
- struct connection *c = userdata;
+ connection *c = CONNECTION(userdata);
const char *s;
- assert(c && t);
+
+ connection_assert_ref(c);
+ pa_assert(t);
if (pa_tagstruct_gets(t, &s) < 0 ||
!pa_tagstruct_eof(t)) {
@@ -1907,10 +2312,12 @@ static void command_set_default_sink_or_source(PA_GCC_UNUSED pa_pdispatch *pd, u
}
static void command_set_stream_name(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
- struct connection *c = userdata;
+ connection *c = CONNECTION(userdata);
uint32_t idx;
const char *name;
- assert(c && t);
+
+ connection_assert_ref(c);
+ pa_assert(t);
if (pa_tagstruct_getu32(t, &idx) < 0 ||
pa_tagstruct_gets(t, &name) < 0 ||
@@ -1923,16 +2330,16 @@ static void command_set_stream_name(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t com
CHECK_VALIDITY(c->pstream, name && pa_utf8_valid(name), tag, PA_ERR_INVALID);
if (command == PA_COMMAND_SET_PLAYBACK_STREAM_NAME) {
- struct playback_stream *s;
+ 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, s->type == PLAYBACK_STREAM, tag, PA_ERR_NOENTITY);
+ CHECK_VALIDITY(c->pstream, playback_stream_isinstance(s), tag, PA_ERR_NOENTITY);
pa_sink_input_set_name(s->sink_input, name);
} else {
- struct record_stream *s;
+ record_stream *s;
s = pa_idxset_get_by_index(c->record_streams, idx);
CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
@@ -1944,9 +2351,11 @@ static void command_set_stream_name(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t com
}
static void command_kill(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
- struct connection *c = userdata;
+ connection *c = CONNECTION(userdata);
uint32_t idx;
- assert(c && t);
+
+ connection_assert_ref(c);
+ pa_assert(t);
if (pa_tagstruct_getu32(t, &idx) < 0 ||
!pa_tagstruct_eof(t)) {
@@ -1961,6 +2370,8 @@ 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_client_kill(client);
} else if (command == PA_COMMAND_KILL_SINK_INPUT) {
@@ -1969,27 +2380,32 @@ 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_sink_input_kill(s);
} else {
pa_source_output *s;
- assert(command == PA_COMMAND_KILL_SOURCE_OUTPUT);
+ pa_assert(command == PA_COMMAND_KILL_SOURCE_OUTPUT);
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_source_output_kill(s);
}
pa_pstream_send_simple_ack(c->pstream, tag);
+ 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) {
- struct connection *c = userdata;
+ connection *c = CONNECTION(userdata);
pa_module *m;
const char *name, *argument;
pa_tagstruct *reply;
- assert(c && t);
+
+ connection_assert_ref(c);
+ pa_assert(t);
if (pa_tagstruct_gets(t, &name) < 0 ||
pa_tagstruct_gets(t, &argument) < 0 ||
@@ -2013,10 +2429,12 @@ static void command_load_module(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED ui
}
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) {
- struct connection *c = userdata;
+ connection *c = CONNECTION(userdata);
uint32_t idx;
pa_module *m;
- assert(c && t);
+
+ connection_assert_ref(c);
+ pa_assert(t);
if (pa_tagstruct_getu32(t, &idx) < 0 ||
!pa_tagstruct_eof(t)) {
@@ -2033,12 +2451,14 @@ static void command_unload_module(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED
}
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) {
- struct connection *c = userdata;
+ connection *c = CONNECTION(userdata);
const char *name, *module, *argument;
uint32_t type;
uint32_t idx;
pa_tagstruct *reply;
- assert(c && t);
+
+ connection_assert_ref(c);
+ pa_assert(t);
if (pa_tagstruct_gets(t, &name) < 0 ||
pa_tagstruct_getu32(t, &type) < 0 ||
@@ -2066,11 +2486,13 @@ static void command_add_autoload(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED u
}
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) {
- struct connection *c = userdata;
+ connection *c = CONNECTION(userdata);
const char *name = NULL;
uint32_t type, idx = PA_IDXSET_INVALID;
int r;
- assert(c && t);
+
+ connection_assert_ref(c);
+ pa_assert(t);
if ((pa_tagstruct_getu32(t, &idx) < 0 &&
(pa_tagstruct_gets(t, &name) < 0 ||
@@ -2095,7 +2517,7 @@ static void command_remove_autoload(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSE
}
static void autoload_fill_tagstruct(pa_tagstruct *t, const pa_autoload_entry *e) {
- assert(t && e);
+ pa_assert(t && e);
pa_tagstruct_putu32(t, e->index);
pa_tagstruct_puts(t, e->name);
@@ -2105,12 +2527,14 @@ static void autoload_fill_tagstruct(pa_tagstruct *t, const pa_autoload_entry *e)
}
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) {
- struct connection *c = userdata;
+ connection *c = CONNECTION(userdata);
const pa_autoload_entry *a = NULL;
uint32_t type, idx;
const char *name;
pa_tagstruct *reply;
- assert(c && t);
+
+ connection_assert_ref(c);
+ pa_assert(t);
if ((pa_tagstruct_getu32(t, &idx) < 0 &&
(pa_tagstruct_gets(t, &name) < 0 ||
@@ -2137,9 +2561,11 @@ static void command_get_autoload_info(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNU
}
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) {
- struct connection *c = userdata;
+ connection *c = CONNECTION(userdata);
pa_tagstruct *reply;
- assert(c && t);
+
+ connection_assert_ref(c);
+ pa_assert(t);
if (!pa_tagstruct_eof(t)) {
protocol_error(c);
@@ -2162,12 +2588,12 @@ 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) {
- struct connection *c = userdata;
+ connection *c = CONNECTION(userdata);
uint32_t idx = PA_INVALID_INDEX, idx_device = PA_INVALID_INDEX;
const char *name = NULL;
- assert(c);
- assert(t);
+ connection_assert_ref(c);
+ pa_assert(t);
if (pa_tagstruct_getu32(t, &idx) < 0 ||
pa_tagstruct_getu32(t, &idx_device) < 0 ||
@@ -2202,6 +2628,8 @@ static void command_move_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag
pa_source_output *so = NULL;
pa_source *source;
+ pa_assert(command == PA_COMMAND_MOVE_SOURCE_OUTPUT);
+
so = pa_idxset_get_by_index(c->protocol->core->source_outputs, idx);
if (idx_device != PA_INVALID_INDEX)
@@ -2218,67 +2646,122 @@ static void command_move_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag
}
pa_pstream_send_simple_ack(c->pstream, tag);
+}
+
+static void command_suspend(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ connection *c = CONNECTION(userdata);
+ uint32_t idx = PA_INVALID_INDEX;
+ const char *name = NULL;
+ int b;
+
+ connection_assert_ref(c);
+ pa_assert(t);
+
+ if (pa_tagstruct_getu32(t, &idx) < 0 ||
+ pa_tagstruct_gets(t, &name) < 0 ||
+ pa_tagstruct_get_boolean(t, &b) < 0 ||
+ !pa_tagstruct_eof(t)) {
+ protocol_error(c);
+ return;
+ }
+
+ 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);
+ if (command == PA_COMMAND_SUSPEND_SINK) {
+
+ if (idx == PA_INVALID_INDEX && name && !*name) {
+
+ if (pa_sink_suspend_all(c->protocol->core, b) < 0) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID);
+ return;
+ }
+ } else {
+ pa_sink *sink = NULL;
+
+ if (idx != PA_INVALID_INDEX)
+ sink = pa_idxset_get_by_index(c->protocol->core->sinks, idx);
+ else
+ sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK, 1);
+
+ CHECK_VALIDITY(c->pstream, sink, tag, PA_ERR_NOENTITY);
+
+ if (pa_sink_suspend(sink, b) < 0) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID);
+ return;
+ }
+ }
+ } else {
+
+ pa_assert(command == PA_COMMAND_SUSPEND_SOURCE);
+
+ if (idx == PA_INVALID_INDEX && name && !*name) {
+
+ if (pa_source_suspend_all(c->protocol->core, b) < 0) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID);
+ return;
+ }
+
+ } else {
+ pa_source *source;
+
+ if (idx != PA_INVALID_INDEX)
+ source = pa_idxset_get_by_index(c->protocol->core->sources, idx);
+ else
+ source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE, 1);
+
+ CHECK_VALIDITY(c->pstream, source, tag, PA_ERR_NOENTITY);
+
+ if (pa_source_suspend(source, b) < 0) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID);
+ return;
+ }
+ }
+ }
+
+ pa_pstream_send_simple_ack(c->pstream, tag);
}
/*** pstream callbacks ***/
static void pstream_packet_callback(pa_pstream *p, pa_packet *packet, const pa_creds *creds, void *userdata) {
- struct connection *c = userdata;
- assert(p && packet && packet->data && c);
+ connection *c = CONNECTION(userdata);
+
+ pa_assert(p);
+ pa_assert(packet);
+ connection_assert_ref(c);
if (pa_pdispatch_run(c->pdispatch, packet, creds, c) < 0) {
pa_log("invalid packet.");
- connection_free(c);
+ 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) {
- struct connection *c = userdata;
- struct output_stream *stream;
- assert(p && chunk && userdata);
+ connection *c = CONNECTION(userdata);
+ output_stream *stream;
+
+ pa_assert(p);
+ pa_assert(chunk);
+ connection_assert_ref(c);
- if (!(stream = pa_idxset_get_by_index(c->output_streams, channel))) {
+ if (!(stream = OUTPUT_STREAM(pa_idxset_get_by_index(c->output_streams, channel)))) {
pa_log("client sent block for invalid stream.");
/* Ignoring */
return;
}
- if (stream->type == PLAYBACK_STREAM) {
- struct playback_stream *ps = (struct playback_stream*) stream;
- if (chunk->length >= ps->requested_bytes)
- ps->requested_bytes = 0;
- else
- ps->requested_bytes -= chunk->length;
-
- pa_memblockq_seek(ps->memblockq, offset, seek);
-
- if (pa_memblockq_push_align(ps->memblockq, chunk) < 0) {
- pa_tagstruct *t;
-
- pa_log_warn("failed to push data into queue");
-
- /* Pushing this block into the queue failed, so we simulate
- * it by skipping ahead */
-
- pa_memblockq_seek(ps->memblockq, chunk->length, PA_SEEK_RELATIVE);
+ if (playback_stream_isinstance(stream)) {
+ playback_stream *ps = PLAYBACK_STREAM(stream);
- /* Notify the user */
- t = pa_tagstruct_new(NULL, 0);
- pa_tagstruct_putu32(t, PA_COMMAND_OVERFLOW);
- pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
- pa_tagstruct_putu32(t, ps->index);
- pa_pstream_send_tagstruct(p, t);
- }
+ if (seek != PA_SEEK_RELATIVE || offset != 0)
+ pa_asyncmsgq_post(ps->sink_input->sink->asyncmsgq, PA_MSGOBJECT(ps->sink_input), SINK_INPUT_MESSAGE_SEEK, PA_UINT_TO_PTR(seek), offset, NULL, NULL);
- ps->underrun = 0;
-
- pa_sink_notify(ps->sink_input->sink);
+ pa_asyncmsgq_post(ps->sink_input->sink->asyncmsgq, PA_MSGOBJECT(ps->sink_input), SINK_INPUT_MESSAGE_POST_DATA, NULL, 0, chunk, NULL);
} else {
- struct upload_stream *u = (struct upload_stream*) stream;
+ upload_stream *u = UPLOAD_STREAM(stream);
size_t l;
- assert(u->type == UPLOAD_STREAM);
if (!u->memchunk.memblock) {
if (u->length == chunk->length) {
@@ -2291,15 +2774,24 @@ static void pstream_memblock_callback(pa_pstream *p, uint32_t channel, int64_t o
}
}
- assert(u->memchunk.memblock);
+ pa_assert(u->memchunk.memblock);
l = u->length;
if (l > chunk->length)
l = chunk->length;
+
if (l > 0) {
- memcpy((uint8_t*) u->memchunk.memblock->data + u->memchunk.index + u->memchunk.length,
- (uint8_t*) chunk->memblock->data+chunk->index, l);
+ void *src, *dst;
+ dst = pa_memblock_acquire(u->memchunk.memblock);
+ src = pa_memblock_acquire(chunk->memblock);
+
+ memcpy((uint8_t*) dst + u->memchunk.index + u->memchunk.length,
+ (uint8_t*) src+chunk->index, l);
+
+ pa_memblock_release(u->memchunk.memblock);
+ pa_memblock_release(chunk->memblock);
+
u->memchunk.length += l;
u->length -= l;
}
@@ -2307,43 +2799,72 @@ static void pstream_memblock_callback(pa_pstream *p, uint32_t channel, int64_t o
}
static void pstream_die_callback(pa_pstream *p, void *userdata) {
- struct connection *c = userdata;
- assert(p && c);
- connection_free(c);
+ connection *c = CONNECTION(userdata);
-/* pa_log("connection died.");*/
-}
+ pa_assert(p);
+ connection_assert_ref(c);
+ connection_unlink(c);
+ pa_log_info("connection died.");
+}
static void pstream_drain_callback(pa_pstream *p, void *userdata) {
- struct connection *c = userdata;
- assert(p && c);
+ connection *c = CONNECTION(userdata);
+
+ pa_assert(p);
+ connection_assert_ref(c);
send_memblock(c);
}
+static void pstream_revoke_callback(pa_pstream *p, uint32_t block_id, void *userdata) {
+ pa_thread_mq *q;
+
+ if (!(q = pa_thread_mq_get()))
+ pa_pstream_send_revoke(p, block_id);
+ else
+ pa_asyncmsgq_post(q->outq, PA_MSGOBJECT(userdata), CONNECTION_MESSAGE_REVOKE, PA_UINT_TO_PTR(block_id), 0, NULL, NULL);
+}
+
+static void pstream_release_callback(pa_pstream *p, uint32_t block_id, void *userdata) {
+ pa_thread_mq *q;
+
+ if (!(q = pa_thread_mq_get()))
+ pa_pstream_send_release(p, block_id);
+ else
+ pa_asyncmsgq_post(q->outq, PA_MSGOBJECT(userdata), CONNECTION_MESSAGE_RELEASE, PA_UINT_TO_PTR(block_id), 0, NULL, NULL);
+}
+
/*** client callbacks ***/
static void client_kill_cb(pa_client *c) {
- assert(c && c->userdata);
- connection_free(c->userdata);
+ pa_assert(c);
+
+ connection_unlink(CONNECTION(c->userdata));
}
/*** socket server callbacks ***/
static void auth_timeout(pa_mainloop_api*m, pa_time_event *e, const struct timeval *tv, void *userdata) {
- struct connection *c = userdata;
- assert(m && tv && c && c->auth_timeout_event == e);
+ connection *c = CONNECTION(userdata);
+
+ pa_assert(m);
+ pa_assert(tv);
+ connection_assert_ref(c);
+ pa_assert(c->auth_timeout_event == e);
if (!c->authorized)
- connection_free(c);
+ connection_unlink(c);
}
static void on_connection(PA_GCC_UNUSED pa_socket_server*s, pa_iochannel *io, void *userdata) {
pa_protocol_native *p = userdata;
- struct connection *c;
+ connection *c;
char cname[256], pname[128];
- assert(io && p);
+
+ pa_assert(s);
+ pa_assert(io);
+ pa_assert(p);
if (pa_idxset_size(p->connections)+1 > MAX_CONNECTIONS) {
pa_log_warn("Warning! Too many connections (%u), dropping incoming connection.", MAX_CONNECTIONS);
@@ -2351,7 +2872,9 @@ static void on_connection(PA_GCC_UNUSED pa_socket_server*s, pa_iochannel *io, vo
return;
}
- c = pa_xmalloc(sizeof(struct connection));
+ c = pa_msgobject_new(connection);
+ c->parent.parent.free = connection_free;
+ c->parent.process_msg = connection_process_msg;
c->authorized = !!p->public;
@@ -2371,35 +2894,31 @@ static void on_connection(PA_GCC_UNUSED pa_socket_server*s, pa_iochannel *io, vo
c->version = 8;
c->protocol = p;
pa_iochannel_socket_peer_to_string(io, pname, sizeof(pname));
- snprintf(cname, sizeof(cname), "Native client (%s)", pname);
- assert(p->core);
+ pa_snprintf(cname, sizeof(cname), "Native client (%s)", pname);
c->client = pa_client_new(p->core, __FILE__, cname);
- assert(c->client);
c->client->kill = client_kill_cb;
c->client->userdata = c;
c->client->owner = p->module;
c->pstream = pa_pstream_new(p->core->mainloop, io, p->core->mempool);
- assert(c->pstream);
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);
pa_pstream_set_drain_callback(c->pstream, pstream_drain_callback, c);
+ pa_pstream_set_revoke_callback(c->pstream, pstream_revoke_callback, c);
+ pa_pstream_set_release_callback(c->pstream, pstream_release_callback, c);
c->pdispatch = pa_pdispatch_new(p->core->mainloop, command_table, PA_COMMAND_MAX);
- assert(c->pdispatch);
c->record_streams = pa_idxset_new(NULL, NULL);
c->output_streams = pa_idxset_new(NULL, NULL);
- assert(c->record_streams && c->output_streams);
c->rrobin_index = PA_IDXSET_INVALID;
c->subscription = NULL;
pa_idxset_put(p->connections, c, NULL);
-
#ifdef HAVE_CREDS
if (pa_iochannel_creds_supported(io))
pa_iochannel_creds_enable(io);
@@ -2410,7 +2929,7 @@ static void on_connection(PA_GCC_UNUSED pa_socket_server*s, pa_iochannel *io, vo
/*** module entry points ***/
static int load_key(pa_protocol_native*p, const char*fn) {
- assert(p);
+ pa_assert(p);
p->auth_cookie_in_property = 0;
@@ -2440,8 +2959,8 @@ static pa_protocol_native* protocol_new_internal(pa_core *c, pa_module *m, pa_mo
int public = 0;
const char *acl;
- assert(c);
- assert(ma);
+ pa_assert(c);
+ pa_assert(ma);
if (pa_modargs_get_value_boolean(ma, "auth-anonymous", &public) < 0) {
pa_log("auth-anonymous= expects a boolean argument.");
@@ -2482,7 +3001,6 @@ static pa_protocol_native* protocol_new_internal(pa_core *c, pa_module *m, pa_mo
goto fail;
p->connections = pa_idxset_new(NULL, NULL);
- assert(p->connections);
return p;
@@ -2517,11 +3035,11 @@ pa_protocol_native* pa_protocol_native_new(pa_core *core, pa_socket_server *serv
}
void pa_protocol_native_free(pa_protocol_native *p) {
- struct connection *c;
- assert(p);
+ connection *c;
+ pa_assert(p);
while ((c = pa_idxset_first(p->connections, NULL)))
- connection_free(c);
+ connection_unlink(c);
pa_idxset_free(p->connections, NULL, NULL);
if (p->server) {
@@ -2553,7 +3071,12 @@ void pa_protocol_native_free(pa_protocol_native *p) {
pa_xfree(p);
}
-pa_protocol_native* pa_protocol_native_new_iochannel(pa_core*core, pa_iochannel *io, pa_module *m, pa_modargs *ma) {
+pa_protocol_native* pa_protocol_native_new_iochannel(
+ pa_core*core,
+ pa_iochannel *io,
+ pa_module *m,
+ pa_modargs *ma) {
+
pa_protocol_native *p;
if (!(p = protocol_new_internal(core, m, ma)))
diff --git a/src/pulsecore/protocol-simple.c b/src/pulsecore/protocol-simple.c
index 31ad6ddd..64e2a81c 100644
--- a/src/pulsecore/protocol-simple.c
+++ b/src/pulsecore/protocol-simple.c
@@ -25,7 +25,6 @@
#include <config.h>
#endif
-#include <assert.h>
#include <stdlib.h>
#include <limits.h>
#include <stdio.h>
@@ -41,101 +40,154 @@
#include <pulsecore/namereg.h>
#include <pulsecore/log.h>
#include <pulsecore/core-error.h>
+#include <pulsecore/atomic.h>
+#include <pulsecore/thread-mq.h>
#include "protocol-simple.h"
/* Don't allow more than this many concurrent connections */
#define MAX_CONNECTIONS 10
-struct connection {
+typedef struct connection {
+ pa_msgobject parent;
pa_protocol_simple *protocol;
pa_iochannel *io;
pa_sink_input *sink_input;
pa_source_output *source_output;
pa_client *client;
pa_memblockq *input_memblockq, *output_memblockq;
- pa_defer_event *defer_event;
int dead;
struct {
pa_memblock *current_memblock;
size_t memblock_index, fragment_size;
+ pa_atomic_t missing;
} playback;
-};
+} connection;
+
+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;
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 {
+ SINK_INPUT_MESSAGE_POST_DATA = PA_SINK_INPUT_MESSAGE_MAX, /* data from main loop to sink input */
+ SINK_INPUT_MESSAGE_DISABLE_PREBUF /* disabled prebuf, get playback started. */
+};
+
+enum {
+ CONNECTION_MESSAGE_REQUEST_DATA, /* data requested from sink input from the main loop */
+ CONNECTION_MESSAGE_POST_DATA, /* data from source output to main loop */
+ 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)
-static void connection_free(struct connection *c) {
- assert(c);
+static void connection_unlink(connection *c) {
+ pa_assert(c);
- pa_idxset_remove_by_data(c->protocol->connections, c, NULL);
+ if (!c->protocol)
+ return;
- if (c->playback.current_memblock)
- pa_memblock_unref(c->playback.current_memblock);
if (c->sink_input) {
- pa_sink_input_disconnect(c->sink_input);
+ pa_sink_input_unlink(c->sink_input);
pa_sink_input_unref(c->sink_input);
+ c->sink_input = NULL;
}
+
if (c->source_output) {
- pa_source_output_disconnect(c->source_output);
+ pa_source_output_unlink(c->source_output);
pa_source_output_unref(c->source_output);
+ c->source_output = NULL;
}
- if (c->client)
+
+ if (c->client) {
pa_client_free(c->client);
- if (c->io)
+ c->client = NULL;
+ }
+
+ if (c->io) {
pa_iochannel_free(c->io);
+ c->io = NULL;
+ }
+
+ pa_assert_se(pa_idxset_remove_by_data(c->protocol->connections, c, NULL) == c);
+ c->protocol = NULL;
+ connection_unref(c);
+}
+
+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);
+
if (c->input_memblockq)
pa_memblockq_free(c->input_memblockq);
if (c->output_memblockq)
pa_memblockq_free(c->output_memblockq);
- if (c->defer_event)
- c->protocol->core->mainloop->defer_free(c->defer_event);
+
pa_xfree(c);
}
-static int do_read(struct connection *c) {
+static int do_read(connection *c) {
pa_memchunk chunk;
ssize_t r;
size_t l;
+ void *p;
+
+ connection_assert_ref(c);
- if (!c->sink_input || !(l = pa_memblockq_missing(c->input_memblockq)))
+ if (!c->sink_input || (l = pa_atomic_load(&c->playback.missing)) <= 0)
return 0;
if (l > c->playback.fragment_size)
l = c->playback.fragment_size;
if (c->playback.current_memblock)
- if (c->playback.current_memblock->length - c->playback.memblock_index < l) {
+ if (pa_memblock_get_length(c->playback.current_memblock) - c->playback.memblock_index < l) {
pa_memblock_unref(c->playback.current_memblock);
c->playback.current_memblock = NULL;
c->playback.memblock_index = 0;
}
if (!c->playback.current_memblock) {
- c->playback.current_memblock = pa_memblock_new(c->protocol->core->mempool, c->playback.fragment_size*2);
- assert(c->playback.current_memblock && c->playback.current_memblock->length >= l);
+ pa_assert_se(c->playback.current_memblock = pa_memblock_new(c->protocol->core->mempool, l));
c->playback.memblock_index = 0;
}
- if ((r = pa_iochannel_read(c->io, (uint8_t*) c->playback.current_memblock->data+c->playback.memblock_index, l)) <= 0) {
+ 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);
+
+ if (r <= 0) {
+
+ if (r < 0 && (errno == EINTR || errno == EAGAIN))
+ return 0;
+
pa_log_debug("read(): %s", r == 0 ? "EOF" : pa_cstrerror(errno));
return -1;
}
@@ -143,50 +195,55 @@ static int do_read(struct connection *c) {
chunk.memblock = c->playback.current_memblock;
chunk.index = c->playback.memblock_index;
chunk.length = r;
- assert(chunk.memblock);
c->playback.memblock_index += r;
- assert(c->input_memblockq);
- pa_memblockq_push_align(c->input_memblockq, &chunk);
- assert(c->sink_input);
- pa_sink_notify(c->sink_input->sink);
+ 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);
return 0;
}
-static int do_write(struct connection *c) {
+static int do_write(connection *c) {
pa_memchunk chunk;
ssize_t r;
+ void *p;
+
+ connection_assert_ref(c);
if (!c->source_output)
return 0;
- assert(c->output_memblockq);
- if (pa_memblockq_peek(c->output_memblockq, &chunk) < 0)
+ if (pa_memblockq_peek(c->output_memblockq, &chunk) < 0) {
+/* pa_log("peek failed"); */
return 0;
+ }
+
+ pa_assert(chunk.memblock);
+ pa_assert(chunk.length);
- assert(chunk.memblock && chunk.length);
+ p = pa_memblock_acquire(chunk.memblock);
+ r = pa_iochannel_write(c->io, (uint8_t*) p+chunk.index, chunk.length);
+ pa_memblock_release(chunk.memblock);
+
+ pa_memblock_unref(chunk.memblock);
+
+ if (r < 0) {
+
+ if (errno == EINTR || errno == EAGAIN)
+ return 0;
- if ((r = pa_iochannel_write(c->io, (uint8_t*) chunk.memblock->data+chunk.index, chunk.length)) < 0) {
- pa_memblock_unref(chunk.memblock);
pa_log("write(): %s", pa_cstrerror(errno));
return -1;
}
- pa_memblockq_drop(c->output_memblockq, &chunk, r);
- pa_memblock_unref(chunk.memblock);
-
- pa_source_notify(c->source_output->source);
+ pa_memblockq_drop(c->output_memblockq, r);
return 0;
}
-static void do_work(struct connection *c) {
- assert(c);
-
- assert(c->protocol && c->protocol->core && c->protocol->core->mainloop && c->protocol->core->mainloop->defer_enable);
- c->protocol->core->mainloop->defer_enable(c->defer_event, 0);
+static void do_work(connection *c) {
+ connection_assert_ref(c);
if (c->dead)
return;
@@ -207,103 +264,182 @@ static void do_work(struct connection *c) {
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;
pa_iochannel_free(c->io);
c->io = NULL;
- pa_memblockq_prebuf_disable(c->input_memblockq);
- pa_sink_notify(c->sink_input->sink);
+ pa_asyncmsgq_post(c->sink_input->sink->asyncmsgq, PA_MSGOBJECT(c->sink_input), SINK_INPUT_MESSAGE_DISABLE_PREBUF, NULL, 0, NULL, NULL);
} else
- connection_free(c);
+ connection_unlink(c);
}
-/*** sink_input callbacks ***/
+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);
-static int sink_input_peek_cb(pa_sink_input *i, pa_memchunk *chunk) {
- struct connection*c;
- assert(i && i->userdata && chunk);
- c = i->userdata;
+ switch (code) {
+ case CONNECTION_MESSAGE_REQUEST_DATA:
+ do_work(c);
+ break;
- if (pa_memblockq_peek(c->input_memblockq, chunk) < 0) {
+ case CONNECTION_MESSAGE_POST_DATA:
+/* pa_log("got data %u", chunk->length); */
+ pa_memblockq_push_align(c->output_memblockq, chunk);
+ do_work(c);
+ break;
- if (c->dead)
- connection_free(c);
-
- return -1;
+ case CONNECTION_MESSAGE_UNLINK_CONNECTION:
+ connection_unlink(c);
+ break;
}
return 0;
}
-static void sink_input_drop_cb(pa_sink_input *i, const pa_memchunk *chunk, size_t length) {
- struct connection*c = i->userdata;
- assert(i && c && length);
+/*** sink_input callbacks ***/
+
+/* Called from thread context */
+static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk) {
+ pa_sink_input *i = PA_SINK_INPUT(o);
+ connection*c;
+
+ pa_sink_input_assert_ref(i);
+ c = CONNECTION(i->userdata);
+ connection_assert_ref(c);
+
+ switch (code) {
+
+ case SINK_INPUT_MESSAGE_POST_DATA: {
+ pa_assert(chunk);
+
+ /* New data from the main loop */
+ pa_memblockq_push_align(c->input_memblockq, chunk);
+
+/* pa_log("got data, %u", pa_memblockq_get_length(c->input_memblockq)); */
+
+ return 0;
+ }
+
+ 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;
- pa_memblockq_drop(c->input_memblockq, chunk, length);
+ *r = pa_bytes_to_usec(pa_memblockq_get_length(c->input_memblockq), &c->sink_input->sample_spec);
- /* do something */
- assert(c->protocol && c->protocol->core && c->protocol->core->mainloop && c->protocol->core->mainloop->defer_enable);
- c->protocol->core->mainloop->defer_enable(c->defer_event, 1);
+ /* Fall through, the default handler will add in the extra
+ * latency added by the resampler */
+ }
+
+ default:
+ return pa_sink_input_process_msg(o, code, userdata, offset, chunk);
+ }
}
-static void sink_input_kill_cb(pa_sink_input *i) {
- assert(i && i->userdata);
- connection_free((struct connection *) i->userdata);
+/* Called from thread context */
+static int sink_input_peek_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) {
+ connection *c;
+ int r;
+
+ pa_assert(i);
+ c = CONNECTION(i->userdata);
+ connection_assert_ref(c);
+ pa_assert(chunk);
+
+ r = pa_memblockq_peek(c->input_memblockq, chunk);
+
+/* pa_log("peeked %u %i", r >= 0 ? chunk->length: 0, r); */
+
+ if (c->dead && r < 0)
+ pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(c), CONNECTION_MESSAGE_UNLINK_CONNECTION, NULL, 0, NULL, NULL);
+
+ return r;
}
+/* Called from thread context */
+static void sink_input_drop_cb(pa_sink_input *i, size_t length) {
+ connection *c;
+ size_t old, new;
+
+ pa_assert(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);
-static pa_usec_t sink_input_get_latency_cb(pa_sink_input *i) {
- struct connection*c = i->userdata;
- assert(i && c);
- return pa_bytes_to_usec(pa_memblockq_get_length(c->input_memblockq), &c->sink_input->sample_spec);
+ 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 main context */
+static void sink_input_kill_cb(pa_sink_input *i) {
+ pa_sink_input_assert_ref(i);
+
+ connection_unlink(CONNECTION(i->userdata));
}
/*** source_output callbacks ***/
+/* Called from thread context */
static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk) {
- struct connection *c = o->userdata;
- assert(o && c && chunk);
+ connection *c;
- pa_memblockq_push(c->output_memblockq, chunk);
+ pa_assert(o);
+ c = CONNECTION(o->userdata);
+ pa_assert(c);
+ pa_assert(chunk);
- /* do something */
- assert(c->protocol && c->protocol->core && c->protocol->core->mainloop && c->protocol->core->mainloop->defer_enable);
- c->protocol->core->mainloop->defer_enable(c->defer_event, 1);
+ pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(c), CONNECTION_MESSAGE_POST_DATA, NULL, 0, chunk, NULL);
}
+/* Called from main context */
static void source_output_kill_cb(pa_source_output *o) {
- assert(o && o->userdata);
- connection_free((struct connection *) o->userdata);
+ pa_source_output_assert_ref(o);
+
+ connection_unlink(CONNECTION(o->userdata));
}
+/* Called from main context */
static pa_usec_t source_output_get_latency_cb(pa_source_output *o) {
- struct connection*c = o->userdata;
- assert(o && c);
+ connection*c;
+
+ pa_assert(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);
}
/*** client callbacks ***/
-static void client_kill_cb(pa_client *c) {
- assert(c && c->userdata);
- connection_free((struct connection *) c->userdata);
+static void client_kill_cb(pa_client *client) {
+ connection*c;
+
+ pa_assert(client);
+ c = CONNECTION(client->userdata);
+ pa_assert(c);
+
+ connection_unlink(c);
}
/*** pa_iochannel callbacks ***/
static void io_callback(pa_iochannel*io, void *userdata) {
- struct connection *c = userdata;
- assert(io && c && c->io == io);
-
- do_work(c);
-}
-
-/*** fixed callback ***/
+ connection *c = CONNECTION(userdata);
-static void defer_callback(pa_mainloop_api*a, pa_defer_event *e, void *userdata) {
- struct connection *c = userdata;
- assert(a && c && c->defer_event == e);
+ connection_assert_ref(c);
+ pa_assert(io);
do_work(c);
}
@@ -312,9 +448,12 @@ static void defer_callback(pa_mainloop_api*a, pa_defer_event *e, void *userdata)
static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata) {
pa_protocol_simple *p = userdata;
- struct connection *c = NULL;
+ connection *c = NULL;
char cname[256];
- assert(s && io && p);
+
+ pa_assert(s);
+ pa_assert(io);
+ pa_assert(p);
if (pa_idxset_size(p->connections)+1 > MAX_CONNECTIONS) {
pa_log("Warning! Too many connections (%u), dropping incoming connection.", MAX_CONNECTIONS);
@@ -322,21 +461,22 @@ static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata)
return;
}
- c = pa_xmalloc(sizeof(struct connection));
+ c = pa_msgobject_new(connection);
+ c->parent.parent.free = connection_free;
+ c->parent.process_msg = connection_process_msg;
c->io = io;
c->sink_input = NULL;
c->source_output = NULL;
- c->defer_event = NULL;
c->input_memblockq = c->output_memblockq = NULL;
c->protocol = p;
c->playback.current_memblock = NULL;
c->playback.memblock_index = 0;
c->playback.fragment_size = 0;
c->dead = 0;
+ pa_atomic_store(&c->playback.missing, 0);
pa_iochannel_socket_peer_to_string(io, cname, sizeof(cname));
- c->client = pa_client_new(p->core, __FILE__, cname);
- assert(c->client);
+ pa_assert_se(c->client = pa_client_new(p->core, __FILE__, cname));
c->client->owner = p->module;
c->client->kill = client_kill_cb;
c->client->userdata = c;
@@ -357,10 +497,10 @@ static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata)
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->kill = sink_input_kill_cb;
- c->sink_input->get_latency = sink_input_get_latency_cb;
c->sink_input->userdata = c;
l = (size_t) (pa_bytes_per_second(&p->sample_spec)*PLAYBACK_BUFFER_SECONDS);
@@ -372,11 +512,12 @@ static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata)
(size_t) -1,
l/PLAYBACK_BUFFER_FRAGMENTS,
NULL);
- assert(c->input_memblockq);
pa_iochannel_socket_set_rcvbuf(io, l/PLAYBACK_BUFFER_FRAGMENTS*5);
- c->playback.fragment_size = l/10;
+ c->playback.fragment_size = l/PLAYBACK_BUFFER_FRAGMENTS;
+
+ pa_atomic_store(&c->playback.missing, pa_memblockq_missing(c->input_memblockq));
- pa_sink_notify(c->sink_input->sink);
+ pa_sink_input_put(c->sink_input);
}
if (p->mode & RECORD) {
@@ -409,29 +550,29 @@ static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata)
0,
NULL);
pa_iochannel_socket_set_sndbuf(io, l/RECORD_BUFFER_FRAGMENTS*2);
- pa_source_notify(c->source_output->source);
+
+ pa_source_output_put(c->source_output);
}
pa_iochannel_set_callback(c->io, io_callback, c);
pa_idxset_put(p->connections, c, NULL);
- c->defer_event = p->core->mainloop->defer_new(p->core->mainloop, defer_callback, c);
- assert(c->defer_event);
- p->core->mainloop->defer_enable(c->defer_event, 0);
-
return;
fail:
if (c)
- connection_free(c);
+ 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;
int enable;
- assert(core && server && ma);
- p = pa_xmalloc0(sizeof(pa_protocol_simple));
+ pa_assert(core);
+ pa_assert(server);
+ pa_assert(ma);
+
+ p = pa_xnew0(pa_protocol_simple, 1);
p->module = m;
p->core = core;
p->server = server;
@@ -472,23 +613,24 @@ pa_protocol_simple* pa_protocol_simple_new(pa_core *core, pa_socket_server *serv
fail:
if (p)
pa_protocol_simple_free(p);
+
return NULL;
}
void pa_protocol_simple_free(pa_protocol_simple *p) {
- struct connection *c;
- assert(p);
+ connection *c;
+ pa_assert(p);
if (p->connections) {
while((c = pa_idxset_first(p->connections, NULL)))
- connection_free(c);
+ connection_unlink(c);
pa_idxset_free(p->connections, NULL, NULL);
}
if (p->server)
pa_socket_server_unref(p->server);
+
pa_xfree(p);
}
-
diff --git a/src/pulsecore/pstream-util.c b/src/pulsecore/pstream-util.c
index fae1e49b..a6932158 100644
--- a/src/pulsecore/pstream-util.c
+++ b/src/pulsecore/pstream-util.c
@@ -25,9 +25,8 @@
#include <config.h>
#endif
-#include <assert.h>
-
#include <pulsecore/native-common.h>
+#include <pulsecore/macro.h>
#include "pstream-util.h"
@@ -35,20 +34,20 @@ void pa_pstream_send_tagstruct_with_creds(pa_pstream *p, pa_tagstruct *t, const
size_t length;
uint8_t *data;
pa_packet *packet;
- assert(p);
- assert(t);
- data = pa_tagstruct_free_data(t, &length);
- assert(data && length);
- packet = pa_packet_new_dynamic(data, length);
- assert(packet);
+ pa_assert(p);
+ pa_assert(t);
+
+ pa_assert_se(data = pa_tagstruct_free_data(t, &length));
+ pa_assert_se(packet = pa_packet_new_dynamic(data, length));
pa_pstream_send_packet(p, packet, creds);
pa_packet_unref(packet);
}
void pa_pstream_send_error(pa_pstream *p, uint32_t tag, uint32_t error) {
- pa_tagstruct *t = pa_tagstruct_new(NULL, 0);
- assert(t);
+ pa_tagstruct *t;
+
+ pa_assert_se(t = pa_tagstruct_new(NULL, 0));
pa_tagstruct_putu32(t, PA_COMMAND_ERROR);
pa_tagstruct_putu32(t, tag);
pa_tagstruct_putu32(t, error);
@@ -56,8 +55,9 @@ void pa_pstream_send_error(pa_pstream *p, uint32_t tag, uint32_t error) {
}
void pa_pstream_send_simple_ack(pa_pstream *p, uint32_t tag) {
- pa_tagstruct *t = pa_tagstruct_new(NULL, 0);
- assert(t);
+ pa_tagstruct *t;
+
+ pa_assert_se(t = pa_tagstruct_new(NULL, 0));
pa_tagstruct_putu32(t, PA_COMMAND_REPLY);
pa_tagstruct_putu32(t, tag);
pa_pstream_send_tagstruct(p, t);
diff --git a/src/pulsecore/pstream.c b/src/pulsecore/pstream.c
index fdb1a66a..9d32a363 100644
--- a/src/pulsecore/pstream.c
+++ b/src/pulsecore/pstream.c
@@ -28,7 +28,6 @@
#include <stdio.h>
#include <stdlib.h>
-#include <assert.h>
#include <unistd.h>
#ifdef HAVE_SYS_SOCKET_H
@@ -41,16 +40,17 @@
#include <netinet/in.h>
#endif
-#include "winsock.h"
#include <pulse/xmalloc.h>
+#include <pulsecore/winsock.h>
#include <pulsecore/queue.h>
#include <pulsecore/log.h>
#include <pulsecore/core-scache.h>
#include <pulsecore/creds.h>
-#include <pulsecore/mutex.h>
#include <pulsecore/refcnt.h>
+#include <pulsecore/flist.h>
+#include <pulsecore/macro.h>
#include "pstream.h"
@@ -84,7 +84,8 @@ typedef uint32_t pa_pstream_descriptor[PA_PSTREAM_DESCRIPTOR_MAX];
#define PA_PSTREAM_DESCRIPTOR_SIZE (PA_PSTREAM_DESCRIPTOR_MAX*sizeof(uint32_t))
#define FRAME_SIZE_MAX_ALLOW PA_SCACHE_ENTRY_SIZE_MAX /* allow uploading a single sample in one frame at max */
-#define FRAME_SIZE_MAX_USE (1024*64)
+
+PA_STATIC_FLIST_DECLARE(items, 0, pa_xfree);
struct item_info {
enum {
@@ -94,7 +95,6 @@ struct item_info {
PA_PSTREAM_ITEM_SHMREVOKE
} type;
-
/* packet info */
pa_packet *packet;
#ifdef HAVE_CREDS
@@ -118,8 +118,8 @@ struct pa_pstream {
pa_mainloop_api *mainloop;
pa_defer_event *defer_event;
pa_iochannel *io;
+
pa_queue *send_queue;
- pa_mutex *mutex;
int dead;
@@ -129,6 +129,7 @@ struct pa_pstream {
uint32_t shm_info[PA_PSTREAM_SHM_MAX];
void *data;
size_t index;
+ pa_memchunk memchunk;
} write;
struct {
@@ -156,6 +157,12 @@ struct pa_pstream {
pa_pstream_notify_cb_t die_callback;
void *die_callback_userdata;
+ pa_pstream_block_id_cb_t revoke_callback;
+ void *revoke_callback_userdata;
+
+ pa_pstream_block_id_cb_t release_callback;
+ void *release_callback_userdata;
+
pa_mempool *mempool;
#ifdef HAVE_CREDS
@@ -168,13 +175,11 @@ static int do_write(pa_pstream *p);
static int do_read(pa_pstream *p);
static void do_something(pa_pstream *p) {
- assert(p);
- assert(PA_REFCNT_VALUE(p) > 0);
+ pa_assert(p);
+ pa_assert(PA_REFCNT_VALUE(p) > 0);
pa_pstream_ref(p);
- pa_mutex_lock(p->mutex);
-
p->mainloop->defer_enable(p->defer_event, 0);
if (!p->dead && pa_iochannel_is_readable(p->io)) {
@@ -188,28 +193,24 @@ static void do_something(pa_pstream *p) {
goto fail;
}
- pa_mutex_unlock(p->mutex);
-
pa_pstream_unref(p);
return;
fail:
- p->dead = 1;
-
if (p->die_callback)
p->die_callback(p, p->die_callback_userdata);
- pa_mutex_unlock(p->mutex);
-
+ pa_pstream_unlink(p);
pa_pstream_unref(p);
}
static void io_callback(pa_iochannel*io, void *userdata) {
pa_pstream *p = userdata;
- assert(p);
- assert(p->io == io);
+ pa_assert(p);
+ pa_assert(PA_REFCNT_VALUE(p) > 0);
+ pa_assert(p->io == io);
do_something(p);
}
@@ -217,9 +218,10 @@ static void io_callback(pa_iochannel*io, void *userdata) {
static void defer_callback(pa_mainloop_api *m, pa_defer_event *e, void*userdata) {
pa_pstream *p = userdata;
- assert(p);
- assert(p->defer_event == e);
- assert(p->mainloop == m);
+ pa_assert(p);
+ pa_assert(PA_REFCNT_VALUE(p) > 0);
+ pa_assert(p->defer_event == e);
+ pa_assert(p->mainloop == m);
do_something(p);
}
@@ -229,9 +231,9 @@ static void memimport_release_cb(pa_memimport *i, uint32_t block_id, void *userd
pa_pstream *pa_pstream_new(pa_mainloop_api *m, pa_iochannel *io, pa_mempool *pool) {
pa_pstream *p;
- assert(m);
- assert(io);
- assert(pool);
+ pa_assert(m);
+ pa_assert(io);
+ pa_assert(pool);
p = pa_xnew(pa_pstream, 1);
PA_REFCNT_INIT(p);
@@ -239,17 +241,15 @@ pa_pstream *pa_pstream_new(pa_mainloop_api *m, pa_iochannel *io, pa_mempool *poo
pa_iochannel_set_callback(io, io_callback, p);
p->dead = 0;
- p->mutex = pa_mutex_new(1);
-
p->mainloop = m;
p->defer_event = m->defer_new(m, defer_callback, p);
m->defer_enable(p->defer_event, 0);
p->send_queue = pa_queue_new();
- assert(p->send_queue);
p->write.current = NULL;
p->write.index = 0;
+ pa_memchunk_reset(&p->write.memchunk);
p->read.memblock = NULL;
p->read.packet = NULL;
p->read.index = 0;
@@ -262,6 +262,10 @@ pa_pstream *pa_pstream_new(pa_mainloop_api *m, pa_iochannel *io, pa_mempool *poo
p->drain_callback_userdata = NULL;
p->die_callback = NULL;
p->die_callback_userdata = NULL;
+ p->revoke_callback = NULL;
+ p->revoke_callback_userdata = NULL;
+ p->release_callback = NULL;
+ p->release_callback_userdata = NULL;
p->mempool = pool;
@@ -281,56 +285,57 @@ pa_pstream *pa_pstream_new(pa_mainloop_api *m, pa_iochannel *io, pa_mempool *poo
return p;
}
-static void item_free(void *item, PA_GCC_UNUSED void *p) {
+static void item_free(void *item, PA_GCC_UNUSED void *q) {
struct item_info *i = item;
- assert(i);
+ pa_assert(i);
if (i->type == PA_PSTREAM_ITEM_MEMBLOCK) {
- assert(i->chunk.memblock);
+ pa_assert(i->chunk.memblock);
pa_memblock_unref(i->chunk.memblock);
} else if (i->type == PA_PSTREAM_ITEM_PACKET) {
- assert(i->packet);
+ pa_assert(i->packet);
pa_packet_unref(i->packet);
}
- pa_xfree(i);
+ if (pa_flist_push(PA_STATIC_FLIST_GET(items), i) < 0)
+ pa_xfree(i);
}
static void pstream_free(pa_pstream *p) {
- assert(p);
+ pa_assert(p);
- pa_pstream_close(p);
+ pa_pstream_unlink(p);
pa_queue_free(p->send_queue, item_free, NULL);
if (p->write.current)
item_free(p->write.current, NULL);
+ if (p->write.memchunk.memblock)
+ pa_memblock_unref(p->write.memchunk.memblock);
+
if (p->read.memblock)
pa_memblock_unref(p->read.memblock);
if (p->read.packet)
pa_packet_unref(p->read.packet);
- if (p->mutex)
- pa_mutex_free(p->mutex);
-
pa_xfree(p);
}
void pa_pstream_send_packet(pa_pstream*p, pa_packet *packet, const pa_creds *creds) {
struct item_info *i;
- assert(p);
- assert(PA_REFCNT_VALUE(p) > 0);
- assert(packet);
-
- pa_mutex_lock(p->mutex);
+ pa_assert(p);
+ pa_assert(PA_REFCNT_VALUE(p) > 0);
+ pa_assert(packet);
if (p->dead)
- goto finish;
+ return;
+
+ if (!(i = pa_flist_pop(PA_STATIC_FLIST_GET(items))))
+ i = pa_xnew(struct item_info, 1);
- i = pa_xnew(struct item_info, 1);
i->type = PA_PSTREAM_ITEM_PACKET;
i->packet = pa_packet_ref(packet);
@@ -340,37 +345,36 @@ void pa_pstream_send_packet(pa_pstream*p, pa_packet *packet, const pa_creds *cre
#endif
pa_queue_push(p->send_queue, i);
- p->mainloop->defer_enable(p->defer_event, 1);
-finish:
-
- pa_mutex_unlock(p->mutex);
+ p->mainloop->defer_enable(p->defer_event, 1);
}
void pa_pstream_send_memblock(pa_pstream*p, uint32_t channel, int64_t offset, pa_seek_mode_t seek_mode, const pa_memchunk *chunk) {
size_t length, idx;
+ size_t bsm;
- assert(p);
- assert(PA_REFCNT_VALUE(p) > 0);
- assert(channel != (uint32_t) -1);
- assert(chunk);
-
- pa_mutex_lock(p->mutex);
+ pa_assert(p);
+ pa_assert(PA_REFCNT_VALUE(p) > 0);
+ pa_assert(channel != (uint32_t) -1);
+ pa_assert(chunk);
if (p->dead)
- goto finish;
+ return;
- length = chunk->length;
idx = 0;
+ length = chunk->length;
+
+ bsm = pa_mempool_block_size_max(p->mempool);
while (length > 0) {
struct item_info *i;
size_t n;
- i = pa_xnew(struct item_info, 1);
+ if (!(i = pa_flist_pop(PA_STATIC_FLIST_GET(items))))
+ i = pa_xnew(struct item_info, 1);
i->type = PA_PSTREAM_ITEM_MEMBLOCK;
- n = length < FRAME_SIZE_MAX_USE ? length : FRAME_SIZE_MAX_USE;
+ n = MIN(length, bsm);
i->chunk.index = chunk->index + idx;
i->chunk.length = n;
i->chunk.memblock = pa_memblock_ref(chunk->memblock);
@@ -389,27 +393,20 @@ void pa_pstream_send_memblock(pa_pstream*p, uint32_t channel, int64_t offset, pa
}
p->mainloop->defer_enable(p->defer_event, 1);
-
-finish:
-
- pa_mutex_unlock(p->mutex);
}
-static void memimport_release_cb(pa_memimport *i, uint32_t block_id, void *userdata) {
+void pa_pstream_send_release(pa_pstream *p, uint32_t block_id) {
struct item_info *item;
- pa_pstream *p = userdata;
-
- assert(p);
- assert(PA_REFCNT_VALUE(p) > 0);
-
- pa_mutex_lock(p->mutex);
+ pa_assert(p);
+ pa_assert(PA_REFCNT_VALUE(p) > 0);
if (p->dead)
- goto finish;
+ return;
/* pa_log("Releasing block %u", block_id); */
- item = pa_xnew(struct item_info, 1);
+ if (!(item = pa_flist_pop(PA_STATIC_FLIST_GET(items))))
+ item = pa_xnew(struct item_info, 1);
item->type = PA_PSTREAM_ITEM_SHMRELEASE;
item->block_id = block_id;
#ifdef HAVE_CREDS
@@ -418,27 +415,35 @@ static void memimport_release_cb(pa_memimport *i, uint32_t block_id, void *userd
pa_queue_push(p->send_queue, item);
p->mainloop->defer_enable(p->defer_event, 1);
-
-finish:
-
- pa_mutex_unlock(p->mutex);
}
-static void memexport_revoke_cb(pa_memexport *e, uint32_t block_id, void *userdata) {
- struct item_info *item;
+/* might be called from thread context */
+static void memimport_release_cb(pa_memimport *i, uint32_t block_id, void *userdata) {
pa_pstream *p = userdata;
- assert(p);
- assert(PA_REFCNT_VALUE(p) > 0);
-
- pa_mutex_lock(p->mutex);
+ pa_assert(p);
+ pa_assert(PA_REFCNT_VALUE(p) > 0);
if (p->dead)
- goto finish;
+ return;
+ if (p->release_callback)
+ p->release_callback(p, block_id, p->release_callback_userdata);
+ else
+ pa_pstream_send_release(p, block_id);
+}
+
+void pa_pstream_send_revoke(pa_pstream *p, uint32_t block_id) {
+ struct item_info *item;
+ pa_assert(p);
+ pa_assert(PA_REFCNT_VALUE(p) > 0);
+
+ if (p->dead)
+ return;
/* pa_log("Revoking block %u", block_id); */
- item = pa_xnew(struct item_info, 1);
+ if (!(item = pa_flist_pop(PA_STATIC_FLIST_GET(items))))
+ item = pa_xnew(struct item_info, 1);
item->type = PA_PSTREAM_ITEM_SHMREVOKE;
item->block_id = block_id;
#ifdef HAVE_CREDS
@@ -447,21 +452,33 @@ static void memexport_revoke_cb(pa_memexport *e, uint32_t block_id, void *userda
pa_queue_push(p->send_queue, item);
p->mainloop->defer_enable(p->defer_event, 1);
+}
+
+/* might be called from thread context */
+static void memexport_revoke_cb(pa_memexport *e, uint32_t block_id, void *userdata) {
+ pa_pstream *p = userdata;
-finish:
+ pa_assert(p);
+ pa_assert(PA_REFCNT_VALUE(p) > 0);
- pa_mutex_unlock(p->mutex);
+ if (p->revoke_callback)
+ p->revoke_callback(p, block_id, p->revoke_callback_userdata);
+ else
+ pa_pstream_send_revoke(p, block_id);
}
static void prepare_next_write_item(pa_pstream *p) {
- assert(p);
- assert(PA_REFCNT_VALUE(p) > 0);
+ pa_assert(p);
+ pa_assert(PA_REFCNT_VALUE(p) > 0);
- if (!(p->write.current = pa_queue_pop(p->send_queue)))
+ p->write.current = pa_queue_pop(p->send_queue);
+
+ if (!p->write.current)
return;
p->write.index = 0;
p->write.data = NULL;
+ pa_memchunk_reset(&p->write.memchunk);
p->write.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH] = 0;
p->write.descriptor[PA_PSTREAM_DESCRIPTOR_CHANNEL] = htonl((uint32_t) -1);
@@ -471,7 +488,7 @@ static void prepare_next_write_item(pa_pstream *p) {
if (p->write.current->type == PA_PSTREAM_ITEM_PACKET) {
- assert(p->write.current->packet);
+ 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);
@@ -489,8 +506,8 @@ static void prepare_next_write_item(pa_pstream *p) {
uint32_t flags;
int send_payload = 1;
- assert(p->write.current->type == PA_PSTREAM_ITEM_MEMBLOCK);
- assert(p->write.current->chunk.memblock);
+ pa_assert(p->write.current->type == PA_PSTREAM_ITEM_MEMBLOCK);
+ pa_assert(p->write.current->chunk.memblock);
p->write.descriptor[PA_PSTREAM_DESCRIPTOR_CHANNEL] = htonl(p->write.current->channel);
p->write.descriptor[PA_PSTREAM_DESCRIPTOR_OFFSET_HI] = htonl((uint32_t) (((uint64_t) p->write.current->offset) >> 32));
@@ -502,7 +519,7 @@ static void prepare_next_write_item(pa_pstream *p) {
uint32_t block_id, shm_id;
size_t offset, length;
- assert(p->export);
+ pa_assert(p->export);
if (pa_memexport_put(p->export,
p->write.current->chunk.memblock,
@@ -528,7 +545,9 @@ 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.data = (uint8_t*) p->write.current->chunk.memblock->data + p->write.current->chunk.index;
+ p->write.memchunk = p->write.current->chunk;
+ pa_memblock_ref(p->write.memchunk.memblock);
+ p->write.data = NULL;
}
p->write.descriptor[PA_PSTREAM_DESCRIPTOR_FLAGS] = htonl(flags);
@@ -544,9 +563,10 @@ static int do_write(pa_pstream *p) {
void *d;
size_t l;
ssize_t r;
+ pa_memblock *release_memblock = NULL;
- assert(p);
- assert(PA_REFCNT_VALUE(p) > 0);
+ pa_assert(p);
+ pa_assert(PA_REFCNT_VALUE(p) > 0);
if (!p->write.current)
prepare_next_write_item(p);
@@ -558,72 +578,105 @@ static int do_write(pa_pstream *p) {
d = (uint8_t*) p->write.descriptor + p->write.index;
l = PA_PSTREAM_DESCRIPTOR_SIZE - p->write.index;
} else {
- assert(p->write.data);
+ pa_assert(p->write.data || p->write.memchunk.memblock);
+
+ if (p->write.data)
+ d = p->write.data;
+ else {
+ d = (uint8_t*) pa_memblock_acquire(p->write.memchunk.memblock) + p->write.memchunk.index;
+ release_memblock = p->write.memchunk.memblock;
+ }
- d = (uint8_t*) p->write.data + p->write.index - PA_PSTREAM_DESCRIPTOR_SIZE;
+ d = (uint8_t*) d + p->write.index - PA_PSTREAM_DESCRIPTOR_SIZE;
l = ntohl(p->write.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH]) - (p->write.index - PA_PSTREAM_DESCRIPTOR_SIZE);
}
- assert(l > 0);
+ pa_assert(l > 0);
#ifdef HAVE_CREDS
if (p->send_creds_now) {
if ((r = pa_iochannel_write_with_creds(p->io, d, l, &p->write_creds)) < 0)
- return -1;
+ goto fail;
p->send_creds_now = 0;
} else
#endif
if ((r = pa_iochannel_write(p->io, d, l)) < 0)
- return -1;
+ goto fail;
+
+ if (release_memblock)
+ pa_memblock_release(release_memblock);
p->write.index += r;
if (p->write.index >= PA_PSTREAM_DESCRIPTOR_SIZE + ntohl(p->write.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH])) {
- assert(p->write.current);
- item_free(p->write.current, (void *) 1);
+ pa_assert(p->write.current);
+ item_free(p->write.current, NULL);
p->write.current = NULL;
+ if (p->write.memchunk.memblock)
+ pa_memblock_unref(p->write.memchunk.memblock);
+
+ pa_memchunk_reset(&p->write.memchunk);
+
if (p->drain_callback && !pa_pstream_is_pending(p))
p->drain_callback(p, p->drain_callback_userdata);
}
return 0;
+
+fail:
+
+ if (release_memblock)
+ pa_memblock_release(release_memblock);
+
+ return -1;
}
static int do_read(pa_pstream *p) {
void *d;
size_t l;
ssize_t r;
-
- assert(p);
- assert(PA_REFCNT_VALUE(p) > 0);
+ pa_memblock *release_memblock = NULL;
+ pa_assert(p);
+ pa_assert(PA_REFCNT_VALUE(p) > 0);
if (p->read.index < PA_PSTREAM_DESCRIPTOR_SIZE) {
d = (uint8_t*) p->read.descriptor + p->read.index;
l = PA_PSTREAM_DESCRIPTOR_SIZE - p->read.index;
} else {
- assert(p->read.data);
- d = (uint8_t*) p->read.data + p->read.index - PA_PSTREAM_DESCRIPTOR_SIZE;
+ pa_assert(p->read.data || p->read.memblock);
+
+ if (p->read.data)
+ d = p->read.data;
+ else {
+ d = pa_memblock_acquire(p->read.memblock);
+ release_memblock = p->read.memblock;
+ }
+
+ d = (uint8_t*) d + p->read.index - PA_PSTREAM_DESCRIPTOR_SIZE;
l = ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH]) - (p->read.index - PA_PSTREAM_DESCRIPTOR_SIZE);
}
#ifdef HAVE_CREDS
{
- int b = 0;
+ pa_bool_t b = 0;
if ((r = pa_iochannel_read_with_creds(p->io, d, l, &p->read_creds, &b)) <= 0)
- return -1;
+ goto fail;
p->read_creds_valid = p->read_creds_valid || b;
}
#else
if ((r = pa_iochannel_read(p->io, d, l)) <= 0)
- return -1;
+ goto fail;
#endif
+ if (release_memblock)
+ pa_memblock_release(release_memblock);
+
p->read.index += r;
if (p->read.index == PA_PSTREAM_DESCRIPTOR_SIZE) {
@@ -643,7 +696,7 @@ static int do_read(pa_pstream *p) {
/* pa_log("Got release frame for %u", ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_OFFSET_HI])); */
- assert(p->export);
+ pa_assert(p->export);
pa_memexport_process_release(p->export, ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_OFFSET_HI]));
goto frame_done;
@@ -654,7 +707,7 @@ static int do_read(pa_pstream *p) {
/* pa_log("Got revoke frame for %u", ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_OFFSET_HI])); */
- assert(p->import);
+ pa_assert(p->import);
pa_memimport_process_revoke(p->import, ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_OFFSET_HI]));
goto frame_done;
@@ -667,7 +720,7 @@ static int do_read(pa_pstream *p) {
return -1;
}
- assert(!p->read.packet && !p->read.memblock);
+ pa_assert(!p->read.packet && !p->read.memblock);
channel = ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_CHANNEL]);
@@ -704,7 +757,7 @@ static int do_read(pa_pstream *p) {
/* Frame is a memblock frame */
p->read.memblock = pa_memblock_new(p->mempool, length);
- p->read.data = p->read.memblock->data;
+ p->read.data = NULL;
} else {
pa_log_warn("Recieved memblock frame with invalid flags value.");
@@ -771,9 +824,9 @@ static int do_read(pa_pstream *p) {
} else {
pa_memblock *b;
- assert((ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_FLAGS]) & PA_FLAG_SHMMASK) == PA_FLAG_SHMDATA);
+ pa_assert((ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_FLAGS]) & PA_FLAG_SHMMASK) == PA_FLAG_SHMDATA);
- assert(p->import);
+ pa_assert(p->import);
if (!(b = pa_memimport_get(p->import,
ntohl(p->read.shm_info[PA_PSTREAM_SHM_BLOCKID]),
@@ -791,7 +844,7 @@ static int do_read(pa_pstream *p) {
chunk.memblock = b;
chunk.index = 0;
- chunk.length = b->length;
+ chunk.length = pa_memblock_get_length(b);
offset = (int64_t) (
(((uint64_t) ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_OFFSET_HI])) << 32) |
@@ -819,92 +872,104 @@ frame_done:
p->read.memblock = NULL;
p->read.packet = NULL;
p->read.index = 0;
+ p->read.data = NULL;
#ifdef HAVE_CREDS
p->read_creds_valid = 0;
#endif
return 0;
+
+fail:
+ if (release_memblock)
+ pa_memblock_release(release_memblock);
+
+ return -1;
}
void pa_pstream_set_die_callback(pa_pstream *p, pa_pstream_notify_cb_t cb, void *userdata) {
- assert(p);
- assert(PA_REFCNT_VALUE(p) > 0);
+ pa_assert(p);
+ pa_assert(PA_REFCNT_VALUE(p) > 0);
- pa_mutex_lock(p->mutex);
p->die_callback = cb;
p->die_callback_userdata = userdata;
- pa_mutex_unlock(p->mutex);
}
void pa_pstream_set_drain_callback(pa_pstream *p, pa_pstream_notify_cb_t cb, void *userdata) {
- assert(p);
- assert(PA_REFCNT_VALUE(p) > 0);
+ pa_assert(p);
+ pa_assert(PA_REFCNT_VALUE(p) > 0);
- pa_mutex_lock(p->mutex);
p->drain_callback = cb;
p->drain_callback_userdata = userdata;
- pa_mutex_unlock(p->mutex);
}
void pa_pstream_set_recieve_packet_callback(pa_pstream *p, pa_pstream_packet_cb_t cb, void *userdata) {
- assert(p);
- assert(PA_REFCNT_VALUE(p) > 0);
+ pa_assert(p);
+ pa_assert(PA_REFCNT_VALUE(p) > 0);
- pa_mutex_lock(p->mutex);
p->recieve_packet_callback = cb;
p->recieve_packet_callback_userdata = userdata;
- pa_mutex_unlock(p->mutex);
}
void pa_pstream_set_recieve_memblock_callback(pa_pstream *p, pa_pstream_memblock_cb_t cb, void *userdata) {
- assert(p);
- assert(PA_REFCNT_VALUE(p) > 0);
+ pa_assert(p);
+ pa_assert(PA_REFCNT_VALUE(p) > 0);
- pa_mutex_lock(p->mutex);
p->recieve_memblock_callback = cb;
p->recieve_memblock_callback_userdata = userdata;
- pa_mutex_unlock(p->mutex);
+}
+
+void pa_pstream_set_release_callback(pa_pstream *p, pa_pstream_block_id_cb_t cb, void *userdata) {
+ pa_assert(p);
+ pa_assert(PA_REFCNT_VALUE(p) > 0);
+
+ p->release_callback = cb;
+ p->release_callback_userdata = userdata;
+}
+
+void pa_pstream_set_revoke_callback(pa_pstream *p, pa_pstream_block_id_cb_t cb, void *userdata) {
+ pa_assert(p);
+ pa_assert(PA_REFCNT_VALUE(p) > 0);
+
+ p->release_callback = cb;
+ p->release_callback_userdata = userdata;
}
int pa_pstream_is_pending(pa_pstream *p) {
int b;
- assert(p);
- assert(PA_REFCNT_VALUE(p) > 0);
-
- pa_mutex_lock(p->mutex);
+ pa_assert(p);
+ pa_assert(PA_REFCNT_VALUE(p) > 0);
if (p->dead)
b = 0;
else
b = p->write.current || !pa_queue_is_empty(p->send_queue);
- pa_mutex_unlock(p->mutex);
-
return b;
}
void pa_pstream_unref(pa_pstream*p) {
- assert(p);
- assert(PA_REFCNT_VALUE(p) > 0);
+ pa_assert(p);
+ pa_assert(PA_REFCNT_VALUE(p) > 0);
if (PA_REFCNT_DEC(p) <= 0)
pstream_free(p);
}
pa_pstream* pa_pstream_ref(pa_pstream*p) {
- assert(p);
- assert(PA_REFCNT_VALUE(p) > 0);
+ pa_assert(p);
+ pa_assert(PA_REFCNT_VALUE(p) > 0);
PA_REFCNT_INC(p);
return p;
}
-void pa_pstream_close(pa_pstream *p) {
- assert(p);
+void pa_pstream_unlink(pa_pstream *p) {
+ pa_assert(p);
- pa_mutex_lock(p->mutex);
+ if (p->dead)
+ return;
p->dead = 1;
@@ -932,15 +997,11 @@ void pa_pstream_close(pa_pstream *p) {
p->drain_callback = NULL;
p->recieve_packet_callback = NULL;
p->recieve_memblock_callback = NULL;
-
- pa_mutex_unlock(p->mutex);
}
void pa_pstream_use_shm(pa_pstream *p, int enable) {
- assert(p);
- assert(PA_REFCNT_VALUE(p) > 0);
-
- pa_mutex_lock(p->mutex);
+ pa_assert(p);
+ pa_assert(PA_REFCNT_VALUE(p) > 0);
p->use_shm = enable;
@@ -956,6 +1017,4 @@ void pa_pstream_use_shm(pa_pstream *p, int enable) {
p->export = NULL;
}
}
-
- pa_mutex_unlock(p->mutex);
}
diff --git a/src/pulsecore/pstream.h b/src/pulsecore/pstream.h
index 5900ecea..72babea9 100644
--- a/src/pulsecore/pstream.h
+++ b/src/pulsecore/pstream.h
@@ -41,6 +41,7 @@ typedef struct pa_pstream pa_pstream;
typedef void (*pa_pstream_packet_cb_t)(pa_pstream *p, pa_packet *packet, const pa_creds *creds, void *userdata);
typedef void (*pa_pstream_memblock_cb_t)(pa_pstream *p, uint32_t channel, int64_t offset, pa_seek_mode_t seek, const pa_memchunk *chunk, void *userdata);
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);
@@ -48,17 +49,20 @@ pa_pstream* pa_pstream_ref(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);
+void pa_pstream_send_release(pa_pstream *p, uint32_t block_id);
+void pa_pstream_send_revoke(pa_pstream *p, uint32_t block_id);
void pa_pstream_set_recieve_packet_callback(pa_pstream *p, pa_pstream_packet_cb_t cb, void *userdata);
void pa_pstream_set_recieve_memblock_callback(pa_pstream *p, pa_pstream_memblock_cb_t cb, void *userdata);
void pa_pstream_set_drain_callback(pa_pstream *p, pa_pstream_notify_cb_t cb, void *userdata);
-
void pa_pstream_set_die_callback(pa_pstream *p, pa_pstream_notify_cb_t cb, void *userdata);
+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);
void pa_pstream_use_shm(pa_pstream *p, int enable);
-void pa_pstream_close(pa_pstream *p);
+void pa_pstream_unlink(pa_pstream *p);
#endif
diff --git a/src/pulsecore/queue.c b/src/pulsecore/queue.c
index 1dd0f606..9b6a37f0 100644
--- a/src/pulsecore/queue.c
+++ b/src/pulsecore/queue.c
@@ -25,13 +25,16 @@
#include <config.h>
#endif
-#include <assert.h>
#include <stdlib.h>
#include <pulse/xmalloc.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/flist.h>
#include "queue.h"
+PA_STATIC_FLIST_DECLARE(entries, 0, pa_xfree);
+
struct queue_entry {
struct queue_entry *next;
void *data;
@@ -44,25 +47,24 @@ struct pa_queue {
pa_queue* pa_queue_new(void) {
pa_queue *q = pa_xnew(pa_queue, 1);
+
q->front = q->back = NULL;
q->length = 0;
+
return q;
}
void pa_queue_free(pa_queue* q, void (*destroy)(void *p, void *userdata), void *userdata) {
- struct queue_entry *e;
- assert(q);
-
- e = q->front;
- while (e) {
- struct queue_entry *n = e->next;
+ void *data;
+ pa_assert(q);
+ while ((data = pa_queue_pop(q)))
if (destroy)
- destroy(e->data, userdata);
+ destroy(data, userdata);
- pa_xfree(e);
- e = n;
- }
+ pa_assert(!q->front);
+ pa_assert(!q->back);
+ pa_assert(q->length == 0);
pa_xfree(q);
}
@@ -70,14 +72,20 @@ void pa_queue_free(pa_queue* q, void (*destroy)(void *p, void *userdata), void *
void pa_queue_push(pa_queue *q, void *p) {
struct queue_entry *e;
- e = pa_xnew(struct queue_entry, 1);
+ pa_assert(q);
+ pa_assert(p);
+
+ if (!(e = pa_flist_pop(PA_STATIC_FLIST_GET(entries))))
+ e = pa_xnew(struct queue_entry, 1);
+
e->data = p;
e->next = NULL;
- if (q->back)
+ if (q->back) {
+ pa_assert(q->front);
q->back->next = e;
- else {
- assert(!q->front);
+ } else {
+ pa_assert(!q->front);
q->front = e;
}
@@ -88,17 +96,22 @@ void pa_queue_push(pa_queue *q, void *p) {
void* pa_queue_pop(pa_queue *q) {
void *p;
struct queue_entry *e;
- assert(q);
+ pa_assert(q);
if (!(e = q->front))
return NULL;
q->front = e->next;
- if (q->back == e)
+
+ if (q->back == e) {
+ pa_assert(!e->next);
q->back = NULL;
+ }
p = e->data;
- pa_xfree(e);
+
+ if (pa_flist_push(PA_STATIC_FLIST_GET(entries), e) < 0)
+ pa_xfree(e);
q->length--;
@@ -106,6 +119,7 @@ void* pa_queue_pop(pa_queue *q) {
}
int pa_queue_is_empty(pa_queue *q) {
- assert(q);
+ pa_assert(q);
+
return q->length == 0;
}
diff --git a/src/pulsecore/random.c b/src/pulsecore/random.c
index 3f591917..87afebfa 100644
--- a/src/pulsecore/random.c
+++ b/src/pulsecore/random.c
@@ -31,21 +31,22 @@
#include <errno.h>
#include <string.h>
#include <stdlib.h>
-#include <assert.h>
#include <time.h>
#include <pulsecore/core-util.h>
#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
#include "random.h"
static int has_whined = 0;
-static const char *devices[] = { "/dev/urandom", "/dev/random", NULL };
+static const char * const devices[] = { "/dev/urandom", "/dev/random", NULL };
static int random_proper(void *ret_data, size_t length) {
#ifdef OS_IS_WIN32
- assert(ret_data && length);
+ pa_assert(ret_data);
+ pa_assert(length > 0);
return -1;
@@ -53,9 +54,10 @@ static int random_proper(void *ret_data, size_t length) {
int fd, ret = -1;
ssize_t r = 0;
- const char **device;
+ const char *const * device;
- assert(ret_data && length);
+ pa_assert(ret_data);
+ pa_assert(length > 0);
device = devices;
@@ -67,7 +69,7 @@ static int random_proper(void *ret_data, size_t length) {
if ((r = pa_loop_read(fd, ret_data, length, NULL)) < 0 || (size_t) r != length)
ret = -1;
- close(fd);
+ pa_close(fd);
} else
ret = -1;
@@ -84,7 +86,7 @@ void pa_random_seed(void) {
if (random_proper(&seed, sizeof(unsigned int)) < 0) {
if (!has_whined)
- pa_log_warn("failed to get proper entropy. Falling back to seeding with current time.");
+ pa_log_warn("Failed to get proper entropy. Falling back to seeding with current time.");
has_whined = 1;
seed = (unsigned int) time(NULL);
@@ -97,13 +99,14 @@ void pa_random(void *ret_data, size_t length) {
uint8_t *p;
size_t l;
- assert(ret_data && length);
+ pa_assert(ret_data);
+ pa_assert(length > 0);
if (random_proper(ret_data, length) >= 0)
return;
if (!has_whined)
- pa_log_warn("failed to get proper entropy. Falling back to unsecure pseudo RNG.");
+ pa_log_warn("Failed to get proper entropy. Falling back to unsecure pseudo RNG.");
has_whined = 1;
for (p = ret_data, l = length; l > 0; p++, l--)
diff --git a/src/pulsecore/refcnt.h b/src/pulsecore/refcnt.h
index 43433ff8..64271ab2 100644
--- a/src/pulsecore/refcnt.h
+++ b/src/pulsecore/refcnt.h
@@ -27,18 +27,18 @@
#include <pulsecore/atomic.h>
#define PA_REFCNT_DECLARE \
- pa_atomic_int_t _ref
+ pa_atomic_t _ref
#define PA_REFCNT_INIT(p) \
- pa_atomic_store(&p->_ref, 1)
+ pa_atomic_store(&(p)->_ref, 1)
#define PA_REFCNT_INC(p) \
- pa_atomic_inc(&p->_ref)
+ pa_atomic_inc(&(p)->_ref)
#define PA_REFCNT_DEC(p) \
- (pa_atomic_dec(&p->_ref)-1)
+ (pa_atomic_dec(&(p)->_ref)-1)
#define PA_REFCNT_VALUE(p) \
- pa_atomic_load(&p->_ref)
+ pa_atomic_load(&(p)->_ref)
#endif
diff --git a/src/pulsecore/resampler.c b/src/pulsecore/resampler.c
index 3827ff94..5bbc6bf4 100644
--- a/src/pulsecore/resampler.c
+++ b/src/pulsecore/resampler.c
@@ -25,53 +25,133 @@
#include <config.h>
#endif
-#include <assert.h>
#include <string.h>
+#if HAVE_LIBSAMPLERATE
#include <samplerate.h>
+#endif
+
#include <liboil/liboilfuncs.h>
#include <liboil/liboil.h>
#include <pulse/xmalloc.h>
-
#include <pulsecore/sconv.h>
#include <pulsecore/log.h>
+#include <pulsecore/macro.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
+
struct pa_resampler {
pa_resample_method_t resample_method;
pa_sample_spec i_ss, o_ss;
pa_channel_map i_cm, o_cm;
- size_t i_fz, o_fz;
+ size_t i_fz, o_fz, w_sz;
pa_mempool *mempool;
- void (*impl_free)(pa_resampler *r);
- void (*impl_update_input_rate)(pa_resampler *r, uint32_t rate);
- void (*impl_run)(pa_resampler *r, const pa_memchunk *in, pa_memchunk *out);
- void *impl_data;
-};
-
-struct impl_libsamplerate {
- pa_memblock *buf1_block, *buf2_block, *buf3_block, *buf4_block;
- float* buf1, *buf2, *buf3, *buf4;
+ pa_memchunk buf1, buf2, buf3, buf4;
unsigned buf1_samples, buf2_samples, buf3_samples, buf4_samples;
- pa_convert_to_float32ne_func_t to_float32ne_func;
- pa_convert_from_float32ne_func_t from_float32ne_func;
- SRC_STATE *src_state;
+ pa_sample_format_t work_format;
+
+ pa_convert_func_t to_work_format_func;
+ pa_convert_func_t from_work_format_func;
int map_table[PA_CHANNELS_MAX][PA_CHANNELS_MAX];
int map_required;
-};
-struct impl_trivial {
- unsigned o_counter;
- unsigned i_counter;
+ 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);
+
+ struct { /* data specific to the trivial resampler */
+ unsigned o_counter;
+ unsigned i_counter;
+ } trivial;
+
+#ifdef HAVE_LIBSAMPLERATE
+ struct { /* data specific to libsamplerate */
+ SRC_STATE *state;
+ } src;
+#endif
+
+ struct { /* data specific to speex */
+ SpeexResamplerState* state;
+ } speex;
+
+ struct { /* data specific to ffmpeg */
+ struct AVResampleContext *state;
+ pa_memchunk buf[PA_CHANNELS_MAX];
+ } ffmpeg;
};
-static int libsamplerate_init(pa_resampler*r);
+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);
+#ifdef HAVE_LIBSAMPLERATE
+static int libsamplerate_init(pa_resampler*r);
+#endif
+
+static void calc_map_table(pa_resampler *r);
+
+static int (* const init_table[])(pa_resampler*r) = {
+#ifdef HAVE_LIBSAMPLERATE
+ [PA_RESAMPLER_SRC_SINC_BEST_QUALITY] = libsamplerate_init,
+ [PA_RESAMPLER_SRC_SINC_MEDIUM_QUALITY] = libsamplerate_init,
+ [PA_RESAMPLER_SRC_SINC_FASTEST] = libsamplerate_init,
+ [PA_RESAMPLER_SRC_ZERO_ORDER_HOLD] = libsamplerate_init,
+ [PA_RESAMPLER_SRC_LINEAR] = libsamplerate_init,
+#else
+ [PA_RESAMPLER_SRC_SINC_BEST_QUALITY] = NULL,
+ [PA_RESAMPLER_SRC_SINC_MEDIUM_QUALITY] = NULL,
+ [PA_RESAMPLER_SRC_SINC_FASTEST] = NULL,
+ [PA_RESAMPLER_SRC_ZERO_ORDER_HOLD] = NULL,
+ [PA_RESAMPLER_SRC_LINEAR] = NULL,
+#endif
+ [PA_RESAMPLER_TRIVIAL] = trivial_init,
+ [PA_RESAMPLER_SPEEX_FLOAT_BASE+0] = speex_init,
+ [PA_RESAMPLER_SPEEX_FLOAT_BASE+1] = speex_init,
+ [PA_RESAMPLER_SPEEX_FLOAT_BASE+2] = speex_init,
+ [PA_RESAMPLER_SPEEX_FLOAT_BASE+3] = speex_init,
+ [PA_RESAMPLER_SPEEX_FLOAT_BASE+4] = speex_init,
+ [PA_RESAMPLER_SPEEX_FLOAT_BASE+5] = speex_init,
+ [PA_RESAMPLER_SPEEX_FLOAT_BASE+6] = speex_init,
+ [PA_RESAMPLER_SPEEX_FLOAT_BASE+7] = speex_init,
+ [PA_RESAMPLER_SPEEX_FLOAT_BASE+8] = speex_init,
+ [PA_RESAMPLER_SPEEX_FLOAT_BASE+9] = speex_init,
+ [PA_RESAMPLER_SPEEX_FLOAT_BASE+10] = speex_init,
+ [PA_RESAMPLER_SPEEX_FIXED_BASE+0] = speex_init,
+ [PA_RESAMPLER_SPEEX_FIXED_BASE+1] = speex_init,
+ [PA_RESAMPLER_SPEEX_FIXED_BASE+2] = speex_init,
+ [PA_RESAMPLER_SPEEX_FIXED_BASE+3] = speex_init,
+ [PA_RESAMPLER_SPEEX_FIXED_BASE+4] = speex_init,
+ [PA_RESAMPLER_SPEEX_FIXED_BASE+5] = speex_init,
+ [PA_RESAMPLER_SPEEX_FIXED_BASE+6] = speex_init,
+ [PA_RESAMPLER_SPEEX_FIXED_BASE+7] = speex_init,
+ [PA_RESAMPLER_SPEEX_FIXED_BASE+8] = speex_init,
+ [PA_RESAMPLER_SPEEX_FIXED_BASE+9] = speex_init,
+ [PA_RESAMPLER_SPEEX_FIXED_BASE+10] = speex_init,
+ [PA_RESAMPLER_FFMPEG] = ffmpeg_init,
+ [PA_RESAMPLER_AUTO] = NULL,
+ [PA_RESAMPLER_COPY] = copy_init
+};
+
+static inline size_t sample_size(pa_sample_format_t f) {
+ pa_sample_spec ss = {
+ .format = f,
+ .rate = 0,
+ .channels = 1
+ };
+
+ return pa_sample_size(&ss);
+}
pa_resampler* pa_resampler_new(
pa_mempool *pool,
@@ -79,25 +159,51 @@ pa_resampler* pa_resampler_new(
const pa_channel_map *am,
const pa_sample_spec *b,
const pa_channel_map *bm,
- pa_resample_method_t resample_method) {
+ pa_resample_method_t resample_method,
+ int variable_rate) {
pa_resampler *r = NULL;
- assert(pool);
- assert(a);
- assert(b);
- assert(pa_sample_spec_valid(a));
- assert(pa_sample_spec_valid(b));
- assert(resample_method != PA_RESAMPLER_INVALID);
+ pa_assert(pool);
+ pa_assert(a);
+ pa_assert(b);
+ pa_assert(pa_sample_spec_valid(a));
+ pa_assert(pa_sample_spec_valid(b));
+ pa_assert(resample_method >= 0);
+ pa_assert(resample_method < PA_RESAMPLER_MAX);
+
+ /* Fix method */
+
+ if (!variable_rate && a->rate == b->rate) {
+ pa_log_info("Forcing resampler 'copy', because of fixed, identical sample rates.");
+ resample_method = PA_RESAMPLER_COPY;
+ }
+
+ if (!pa_resample_method_supported(resample_method)) {
+ pa_log_warn("Support for resampler '%s' not compiled in, reverting to 'auto'.", pa_resample_method_to_string(resample_method));
+ resample_method = PA_RESAMPLER_AUTO;
+ }
+
+ if (resample_method == PA_RESAMPLER_FFMPEG && variable_rate) {
+ pa_log_info("Resampler 'ffmpeg' cannot do variable rate, reverting to resampler 'auto'.");
+ resample_method = PA_RESAMPLER_AUTO;
+ }
+
+ if (resample_method == PA_RESAMPLER_COPY && (variable_rate || a->rate != b->rate)) {
+ pa_log_info("Resampler 'copy' cannot change sampling rate, reverting to resampler 'auto'.");
+ resample_method = PA_RESAMPLER_AUTO;
+ }
+
+ if (resample_method == PA_RESAMPLER_AUTO)
+ resample_method = PA_RESAMPLER_SPEEX_FLOAT_BASE + 0;
r = pa_xnew(pa_resampler, 1);
- r->impl_data = NULL;
r->mempool = pool;
r->resample_method = resample_method;
r->impl_free = NULL;
- r->impl_update_input_rate = NULL;
- r->impl_run = NULL;
+ r->impl_update_rates = NULL;
+ r->impl_resample = NULL;
/* Fill sample specs */
r->i_ss = *a;
@@ -116,25 +222,66 @@ pa_resampler* pa_resampler_new(
r->i_fz = pa_frame_size(a);
r->o_fz = pa_frame_size(b);
- /* Choose implementation */
- if (a->channels != b->channels ||
- a->format != b->format ||
- !pa_channel_map_equal(&r->i_cm, &r->o_cm) ||
- resample_method != PA_RESAMPLER_TRIVIAL) {
+ pa_memchunk_reset(&r->buf1);
+ pa_memchunk_reset(&r->buf2);
+ pa_memchunk_reset(&r->buf3);
+ pa_memchunk_reset(&r->buf4);
+
+ r->buf1_samples = r->buf2_samples = r->buf3_samples = r->buf4_samples = 0;
+
+ calc_map_table(r);
+
+ pa_log_info("Using resampler '%s'", pa_resample_method_to_string(resample_method));
+
+ if ((resample_method >= PA_RESAMPLER_SPEEX_FIXED_BASE && resample_method <= PA_RESAMPLER_SPEEX_FIXED_MAX) ||
+ (resample_method == PA_RESAMPLER_FFMPEG))
+ r->work_format = PA_SAMPLE_S16NE;
+ else if (resample_method == PA_RESAMPLER_TRIVIAL || resample_method == PA_RESAMPLER_COPY) {
- /* Use the libsamplerate based resampler for the complicated cases */
- if (resample_method == PA_RESAMPLER_TRIVIAL)
- r->resample_method = PA_RESAMPLER_SRC_ZERO_ORDER_HOLD;
+ if (r->map_required || a->format != b->format) {
- if (libsamplerate_init(r) < 0)
+ if (a->format == PA_SAMPLE_FLOAT32NE || a->format == PA_SAMPLE_FLOAT32RE ||
+ b->format == PA_SAMPLE_FLOAT32NE || b->format == PA_SAMPLE_FLOAT32RE)
+ r->work_format = PA_SAMPLE_FLOAT32NE;
+ else
+ r->work_format = PA_SAMPLE_S16NE;
+
+ } else
+ r->work_format = a->format;
+
+ } else
+ r->work_format = PA_SAMPLE_FLOAT32NE;
+
+ pa_log_info("Using %s as working format.", pa_sample_format_to_string(r->work_format));
+
+ r->w_sz = sample_size(r->work_format);
+
+ if (r->i_ss.format == r->work_format)
+ r->to_work_format_func = NULL;
+ else if (r->work_format == PA_SAMPLE_FLOAT32NE) {
+ if (!(r->to_work_format_func = pa_get_convert_to_float32ne_function(r->i_ss.format)))
+ goto fail;
+ } else {
+ pa_assert(r->work_format == PA_SAMPLE_S16NE);
+ if (!(r->to_work_format_func = pa_get_convert_to_s16ne_function(r->i_ss.format)))
goto fail;
+ }
+ if (r->o_ss.format == r->work_format)
+ r->from_work_format_func = NULL;
+ else if (r->work_format == PA_SAMPLE_FLOAT32NE) {
+ if (!(r->from_work_format_func = pa_get_convert_from_float32ne_function(r->o_ss.format)))
+ goto fail;
} else {
- /* Use our own simple non-fp resampler for the trivial cases and when the user selects it */
- if (trivial_init(r) < 0)
+ pa_assert(r->work_format == PA_SAMPLE_S16NE);
+ if (!(r->from_work_format_func = pa_get_convert_from_s16ne_function(r->o_ss.format)))
goto fail;
}
+ /* initialize implementation */
+ if (init_table[resample_method](r) < 0)
+ goto fail;
+
return r;
fail:
@@ -145,41 +292,85 @@ fail:
}
void pa_resampler_free(pa_resampler *r) {
- assert(r);
+ pa_assert(r);
if (r->impl_free)
r->impl_free(r);
+ if (r->buf1.memblock)
+ pa_memblock_unref(r->buf1.memblock);
+ if (r->buf2.memblock)
+ pa_memblock_unref(r->buf2.memblock);
+ if (r->buf3.memblock)
+ pa_memblock_unref(r->buf3.memblock);
+ if (r->buf4.memblock)
+ pa_memblock_unref(r->buf4.memblock);
+
pa_xfree(r);
}
void pa_resampler_set_input_rate(pa_resampler *r, uint32_t rate) {
- assert(r);
- assert(rate > 0);
+ pa_assert(r);
+ pa_assert(rate > 0);
if (r->i_ss.rate == rate)
return;
r->i_ss.rate = rate;
- if (r->impl_update_input_rate)
- r->impl_update_input_rate(r, rate);
+ r->impl_update_rates(r);
}
-void pa_resampler_run(pa_resampler *r, const pa_memchunk *in, pa_memchunk *out) {
- assert(r && in && out && r->impl_run);
+void pa_resampler_set_output_rate(pa_resampler *r, uint32_t rate) {
+ pa_assert(r);
+ pa_assert(rate > 0);
- r->impl_run(r, in, out);
+ if (r->o_ss.rate == rate)
+ return;
+
+ r->o_ss.rate = rate;
+
+ r->impl_update_rates(r);
}
size_t pa_resampler_request(pa_resampler *r, size_t out_length) {
- assert(r);
+ pa_assert(r);
return (((out_length / r->o_fz)*r->i_ss.rate)/r->o_ss.rate) * r->i_fz;
}
+size_t pa_resampler_max_block_size(pa_resampler *r) {
+ size_t block_size_max;
+ pa_sample_spec ss;
+ size_t fs;
+
+ pa_assert(r);
+
+ block_size_max = pa_mempool_block_size_max(r->mempool);
+
+ /* 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;
+
+ /* 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;
+
+ if (r->o_ss.rate > ss.rate)
+ 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;
+}
+
pa_resample_method_t pa_resampler_get_method(pa_resampler *r) {
- assert(r);
+ pa_assert(r);
+
return r->resample_method;
}
@@ -189,7 +380,32 @@ static const char * const resample_methods[] = {
"src-sinc-fastest",
"src-zero-order-hold",
"src-linear",
- "trivial"
+ "trivial",
+ "speex-float-0",
+ "speex-float-1",
+ "speex-float-2",
+ "speex-float-3",
+ "speex-float-4",
+ "speex-float-5",
+ "speex-float-6",
+ "speex-float-7",
+ "speex-float-8",
+ "speex-float-9",
+ "speex-float-10",
+ "speex-fixed-0",
+ "speex-fixed-1",
+ "speex-fixed-2",
+ "speex-fixed-3",
+ "speex-fixed-4",
+ "speex-fixed-5",
+ "speex-fixed-6",
+ "speex-fixed-7",
+ "speex-fixed-8",
+ "speex-fixed-9",
+ "speex-fixed-10",
+ "ffmpeg",
+ "auto",
+ "copy"
};
const char *pa_resample_method_to_string(pa_resample_method_t m) {
@@ -200,52 +416,43 @@ const char *pa_resample_method_to_string(pa_resample_method_t m) {
return resample_methods[m];
}
-pa_resample_method_t pa_parse_resample_method(const char *string) {
- pa_resample_method_t m;
+int pa_resample_method_supported(pa_resample_method_t m) {
- assert(string);
+ if (m < 0 || m >= PA_RESAMPLER_MAX)
+ return 0;
- for (m = 0; m < PA_RESAMPLER_MAX; m++)
- if (!strcmp(string, resample_methods[m]))
- return m;
+#ifndef HAVE_LIBSAMPLERATE
+ if (m <= PA_RESAMPLER_SRC_LINEAR)
+ return 0;
+#endif
- return PA_RESAMPLER_INVALID;
+ return 1;
}
+pa_resample_method_t pa_parse_resample_method(const char *string) {
+ pa_resample_method_t m;
-/*** libsamplerate based implementation ***/
-
-static void libsamplerate_free(pa_resampler *r) {
- struct impl_libsamplerate *u;
+ pa_assert(string);
- assert(r);
- assert(r->impl_data);
+ for (m = 0; m < PA_RESAMPLER_MAX; m++)
+ if (!strcmp(string, resample_methods[m]))
+ return m;
- u = r->impl_data;
+ if (!strcmp(string, "speex-fixed"))
+ return PA_RESAMPLER_SPEEX_FIXED_BASE + 0;
- if (u->src_state)
- src_delete(u->src_state);
+ if (!strcmp(string, "speex-float"))
+ return PA_RESAMPLER_SPEEX_FLOAT_BASE + 0;
- if (u->buf1_block)
- pa_memblock_unref(u->buf1_block);
- if (u->buf2_block)
- pa_memblock_unref(u->buf2_block);
- if (u->buf3_block)
- pa_memblock_unref(u->buf3_block);
- if (u->buf4_block)
- pa_memblock_unref(u->buf4_block);
- pa_xfree(u);
+ return PA_RESAMPLER_INVALID;
}
static void calc_map_table(pa_resampler *r) {
- struct impl_libsamplerate *u;
unsigned oc;
- assert(r);
- assert(r->impl_data);
- u = r->impl_data;
+ pa_assert(r);
- if (!(u->map_required = (!pa_channel_map_equal(&r->i_cm, &r->o_cm) || r->i_ss.channels != r->o_ss.channels)))
+ if (!(r->map_required = (r->i_ss.channels != r->o_ss.channels || !pa_channel_map_equal(&r->i_cm, &r->o_cm))))
return;
for (oc = 0; oc < r->o_ss.channels; oc++) {
@@ -263,392 +470,590 @@ static void calc_map_table(pa_resampler *r) {
(a == PA_CHANNEL_POSITION_LEFT && b == PA_CHANNEL_POSITION_MONO) ||
(a == PA_CHANNEL_POSITION_RIGHT && b == PA_CHANNEL_POSITION_MONO))
- u->map_table[oc][i++] = ic;
+ r->map_table[oc][i++] = ic;
}
/* Add an end marker */
if (i < PA_CHANNELS_MAX)
- u->map_table[oc][i] = -1;
+ r->map_table[oc][i] = -1;
}
}
-static float * convert_to_float(pa_resampler *r, void *input, unsigned n_frames) {
- struct impl_libsamplerate *u;
+static pa_memchunk* convert_to_work_format(pa_resampler *r, pa_memchunk *input) {
unsigned n_samples;
+ void *src, *dst;
- assert(r);
- assert(input);
- assert(r->impl_data);
- u = r->impl_data;
+ pa_assert(r);
+ pa_assert(input);
+ pa_assert(input->memblock);
- /* Convert the incoming sample into floats and place them in buf1 */
+ /* Convert the incoming sample into the work sample format and place them in buf1 */
- if (!u->to_float32ne_func)
+ if (!r->to_work_format_func || !input->length)
return input;
- n_samples = n_frames * r->i_ss.channels;
+ n_samples = (input->length / r->i_fz) * r->i_ss.channels;
- if (u->buf1_samples < n_samples) {
- if (u->buf1_block)
- pa_memblock_unref(u->buf1_block);
+ r->buf1.index = 0;
+ r->buf1.length = r->w_sz * n_samples;
- u->buf1_samples = n_samples;
- u->buf1_block = pa_memblock_new(r->mempool, sizeof(float) * n_samples);
- u->buf1 = u->buf1_block->data;
+ if (!r->buf1.memblock || r->buf1_samples < n_samples) {
+ if (r->buf1.memblock)
+ pa_memblock_unref(r->buf1.memblock);
+
+ r->buf1_samples = n_samples;
+ r->buf1.memblock = pa_memblock_new(r->mempool, r->buf1.length);
}
- u->to_float32ne_func(n_samples, input, u->buf1);
+ src = (uint8_t*) pa_memblock_acquire(input->memblock) + input->index;
+ dst = (uint8_t*) pa_memblock_acquire(r->buf1.memblock);
+
+ r->to_work_format_func(n_samples, src, dst);
+
+ pa_memblock_release(input->memblock);
+ pa_memblock_release(r->buf1.memblock);
- return u->buf1;
+ return &r->buf1;
}
-static float *remap_channels(pa_resampler *r, float *input, unsigned n_frames) {
- struct impl_libsamplerate *u;
- unsigned n_samples;
+static pa_memchunk *remap_channels(pa_resampler *r, pa_memchunk *input) {
+ unsigned in_n_samples, out_n_samples, n_frames;
int i_skip, o_skip;
unsigned oc;
+ void *src, *dst;
- assert(r);
- assert(input);
- assert(r->impl_data);
- u = r->impl_data;
+ pa_assert(r);
+ pa_assert(input);
+ pa_assert(input->memblock);
/* Remap channels and place the result int buf2 */
- if (!u->map_required)
+ if (!r->map_required || !input->length)
return input;
- n_samples = n_frames * r->o_ss.channels;
+ in_n_samples = input->length / r->w_sz;
+ n_frames = in_n_samples / r->i_ss.channels;
+ out_n_samples = n_frames * r->o_ss.channels;
+
+ r->buf2.index = 0;
+ r->buf2.length = r->w_sz * out_n_samples;
- if (u->buf2_samples < n_samples) {
- if (u->buf2_block)
- pa_memblock_unref(u->buf2_block);
+ if (!r->buf2.memblock || r->buf2_samples < out_n_samples) {
+ if (r->buf2.memblock)
+ pa_memblock_unref(r->buf2.memblock);
- u->buf2_samples = n_samples;
- u->buf2_block = pa_memblock_new(r->mempool, sizeof(float) * n_samples);
- u->buf2 = u->buf2_block->data;
+ r->buf2_samples = out_n_samples;
+ r->buf2.memblock = pa_memblock_new(r->mempool, r->buf2.length);
}
- memset(u->buf2, 0, n_samples * sizeof(float));
+ src = ((uint8_t*) pa_memblock_acquire(input->memblock) + input->index);
+ dst = pa_memblock_acquire(r->buf2.memblock);
- o_skip = sizeof(float) * r->o_ss.channels;
- i_skip = sizeof(float) * r->i_ss.channels;
+ memset(dst, 0, r->buf2.length);
- for (oc = 0; oc < r->o_ss.channels; oc++) {
- unsigned i;
- static const float one = 1.0;
-
- for (i = 0; i < PA_CHANNELS_MAX && u->map_table[oc][i] >= 0; i++)
- oil_vectoradd_f32(
- u->buf2 + oc, o_skip,
- u->buf2 + oc, o_skip,
- input + u->map_table[oc][i], i_skip,
- n_frames,
- &one, &one);
+ o_skip = r->w_sz * r->o_ss.channels;
+ i_skip = r->w_sz * r->i_ss.channels;
+
+ switch (r->work_format) {
+ case PA_SAMPLE_FLOAT32NE:
+
+ for (oc = 0; oc < r->o_ss.channels; oc++) {
+ unsigned i;
+ static const float one = 1.0;
+
+ for (i = 0; i < PA_CHANNELS_MAX && r->map_table[oc][i] >= 0; i++)
+ oil_vectoradd_f32(
+ (float*) dst + oc, o_skip,
+ (float*) dst + oc, o_skip,
+ (float*) src + r->map_table[oc][i], i_skip,
+ n_frames,
+ &one, &one);
+ }
+
+ break;
+
+ case PA_SAMPLE_S16NE:
+
+ for (oc = 0; oc < r->o_ss.channels; oc++) {
+ unsigned i;
+ static const int16_t one = 1;
+
+ for (i = 0; i < PA_CHANNELS_MAX && r->map_table[oc][i] >= 0; i++)
+ oil_vectoradd_s16(
+ (int16_t*) dst + oc, o_skip,
+ (int16_t*) dst + oc, o_skip,
+ (int16_t*) src + r->map_table[oc][i], i_skip,
+ n_frames,
+ &one, &one);
+ }
+
+ break;
+
+ default:
+ pa_assert_not_reached();
}
- return u->buf2;
+ pa_memblock_release(input->memblock);
+ pa_memblock_release(r->buf2.memblock);
+
+ r->buf2.length = out_n_samples * r->w_sz;
+
+ return &r->buf2;
}
-static float *resample(pa_resampler *r, float *input, unsigned *n_frames) {
- struct impl_libsamplerate *u;
- SRC_DATA data;
+static pa_memchunk *resample(pa_resampler *r, pa_memchunk *input) {
+ unsigned in_n_frames, in_n_samples;
unsigned out_n_frames, out_n_samples;
- int ret;
- assert(r);
- assert(input);
- assert(n_frames);
- assert(r->impl_data);
- u = r->impl_data;
+ pa_assert(r);
+ pa_assert(input);
/* Resample the data and place the result in buf3 */
- if (!u->src_state)
+ if (!r->impl_resample || !input->length)
return input;
- out_n_frames = (*n_frames*r->o_ss.rate/r->i_ss.rate)+1024;
+ in_n_samples = input->length / r->w_sz;
+ in_n_frames = in_n_samples / r->o_ss.channels;
+
+ out_n_frames = ((in_n_frames*r->o_ss.rate)/r->i_ss.rate)+EXTRA_SAMPLES;
out_n_samples = out_n_frames * r->o_ss.channels;
- if (u->buf3_samples < out_n_samples) {
- if (u->buf3_block)
- pa_memblock_unref(u->buf3_block);
+ r->buf3.index = 0;
+ r->buf3.length = r->w_sz * out_n_samples;
+
+ if (!r->buf3.memblock || r->buf3_samples < out_n_samples) {
+ if (r->buf3.memblock)
+ pa_memblock_unref(r->buf3.memblock);
- u->buf3_samples = out_n_samples;
- u->buf3_block = pa_memblock_new(r->mempool, sizeof(float) * out_n_samples);
- u->buf3 = u->buf3_block->data;
+ r->buf3_samples = out_n_samples;
+ r->buf3.memblock = pa_memblock_new(r->mempool, r->buf3.length);
}
- data.data_in = input;
- data.input_frames = *n_frames;
+ r->impl_resample(r, input, in_n_frames, &r->buf3, &out_n_frames);
+ r->buf3.length = out_n_frames * r->w_sz * r->o_ss.channels;
- data.data_out = u->buf3;
- data.output_frames = out_n_frames;
+ return &r->buf3;
+}
- data.src_ratio = (double) r->o_ss.rate / r->i_ss.rate;
- data.end_of_input = 0;
+static pa_memchunk *convert_from_work_format(pa_resampler *r, pa_memchunk *input) {
+ unsigned n_samples, n_frames;
+ void *src, *dst;
+
+ pa_assert(r);
+ pa_assert(input);
+
+ /* Convert the data into the correct sample type and place the result in buf4 */
+
+ 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;
+
+ r->buf4.index = 0;
+ r->buf4.length = r->o_fz * n_frames;
+
+ if (!r->buf4.memblock || r->buf4_samples < n_samples) {
+ if (r->buf4.memblock)
+ pa_memblock_unref(r->buf4.memblock);
+
+ r->buf4_samples = n_samples;
+ r->buf4.memblock = pa_memblock_new(r->mempool, r->buf4.length);
+ }
- ret = src_process(u->src_state, &data);
- assert(ret == 0);
- assert((unsigned) data.input_frames_used == *n_frames);
+ src = (uint8_t*) pa_memblock_acquire(input->memblock) + input->index;
+ dst = pa_memblock_acquire(r->buf4.memblock);
+ r->from_work_format_func(n_samples, src, dst);
+ pa_memblock_release(input->memblock);
+ pa_memblock_release(r->buf4.memblock);
- *n_frames = data.output_frames_gen;
+ r->buf4.length = r->o_fz * n_frames;
- return u->buf3;
+ return &r->buf4;
}
-static void *convert_from_float(pa_resampler *r, float *input, unsigned n_frames) {
- struct impl_libsamplerate *u;
- unsigned n_samples;
+void pa_resampler_run(pa_resampler *r, const pa_memchunk *in, pa_memchunk *out) {
+ pa_memchunk *buf;
+
+ pa_assert(r);
+ pa_assert(in);
+ pa_assert(out);
+ pa_assert(in->length);
+ pa_assert(in->memblock);
+ pa_assert(in->length % r->i_fz == 0);
+
+ buf = (pa_memchunk*) in;
+ buf = convert_to_work_format(r, buf);
+ buf = remap_channels(r, buf);
+ buf = resample(r, buf);
+
+ if (buf->length) {
+ buf = convert_from_work_format(r, buf);
+ *out = *buf;
+
+ if (buf == in)
+ pa_memblock_ref(buf->memblock);
+ else
+ pa_memchunk_reset(buf);
+ } else
+ pa_memchunk_reset(out);
+}
- assert(r);
- assert(input);
- assert(r->impl_data);
- u = r->impl_data;
+/*** libsamplerate based implementation ***/
- /* Convert the data into the correct sample type and place the result in buf4 */
+#ifdef HAVE_LIBSAMPLERATE
+static void libsamplerate_resample(pa_resampler *r, const pa_memchunk *input, unsigned in_n_frames, pa_memchunk *output, unsigned *out_n_frames) {
+ SRC_DATA data;
- if (!u->from_float32ne_func)
- return input;
+ pa_assert(r);
+ pa_assert(input);
+ pa_assert(output);
+ pa_assert(out_n_frames);
- n_samples = n_frames * r->o_ss.channels;
+ memset(&data, 0, sizeof(data));
- if (u->buf4_samples < n_samples) {
- if (u->buf4_block)
- pa_memblock_unref(u->buf4_block);
+ data.data_in = (float*) ((uint8_t*) pa_memblock_acquire(input->memblock) + input->index);
+ data.input_frames = in_n_frames;
- u->buf4_samples = n_samples;
- u->buf4_block = pa_memblock_new(r->mempool, sizeof(float) * n_samples);
- u->buf4 = u->buf4_block->data;
- }
+ data.data_out = (float*) ((uint8_t*) pa_memblock_acquire(output->memblock) + output->index);
+ data.output_frames = *out_n_frames;
- u->from_float32ne_func(n_samples, input, u->buf4);
-
- return u->buf4;
-}
-
-static void libsamplerate_run(pa_resampler *r, const pa_memchunk *in, pa_memchunk *out) {
- struct impl_libsamplerate *u;
- float *buf;
- void *input, *output;
- unsigned n_frames;
-
- assert(r);
- assert(in);
- assert(out);
- assert(in->length);
- assert(in->memblock);
- assert(in->length % r->i_fz == 0);
- assert(r->impl_data);
-
- u = r->impl_data;
-
- input = ((uint8_t*) in->memblock->data + in->index);
- n_frames = in->length / r->i_fz;
- assert(n_frames > 0);
-
- buf = convert_to_float(r, input, n_frames);
- buf = remap_channels(r, buf, n_frames);
- buf = resample(r, buf, &n_frames);
-
- if (n_frames) {
- output = convert_from_float(r, buf, n_frames);
-
- if (output == input) {
- /* Mm, no adjustment has been necessary, so let's return the original block */
- out->memblock = pa_memblock_ref(in->memblock);
- out->index = in->index;
- out->length = in->length;
- } else {
- out->length = n_frames * r->o_fz;
- out->index = 0;
- out->memblock = NULL;
-
- if (output == u->buf1) {
- u->buf1 = NULL;
- u->buf1_samples = 0;
- out->memblock = u->buf1_block;
- u->buf1_block = NULL;
- } else if (output == u->buf2) {
- u->buf2 = NULL;
- u->buf2_samples = 0;
- out->memblock = u->buf2_block;
- u->buf2_block = NULL;
- } else if (output == u->buf3) {
- u->buf3 = NULL;
- u->buf3_samples = 0;
- out->memblock = u->buf3_block;
- u->buf3_block = NULL;
- } else if (output == u->buf4) {
- u->buf4 = NULL;
- u->buf4_samples = 0;
- out->memblock = u->buf4_block;
- u->buf4_block = NULL;
- }
+ data.src_ratio = (double) r->o_ss.rate / r->i_ss.rate;
+ data.end_of_input = 0;
- assert(out->memblock);
- }
- } else {
- out->memblock = NULL;
- out->index = out->length = 0;
- }
+ pa_assert_se(src_process(r->src.state, &data) == 0);
+ pa_assert((unsigned) data.input_frames_used == in_n_frames);
+
+ pa_memblock_release(input->memblock);
+ pa_memblock_release(output->memblock);
+
+ *out_n_frames = data.output_frames_gen;
}
-static void libsamplerate_update_input_rate(pa_resampler *r, uint32_t rate) {
- struct impl_libsamplerate *u;
+static void libsamplerate_update_rates(pa_resampler *r) {
+ pa_assert(r);
- assert(r);
- assert(rate > 0);
- assert(r->impl_data);
- u = r->impl_data;
+ pa_assert_se(src_set_ratio(r->src.state, (double) r->o_ss.rate / r->i_ss.rate) == 0);
+}
- if (!u->src_state) {
- int err;
- u->src_state = src_new(r->resample_method, r->o_ss.channels, &err);
- assert(u->src_state);
- } else {
- int ret = src_set_ratio(u->src_state, (double) r->o_ss.rate / rate);
- assert(ret == 0);
- }
+static void libsamplerate_free(pa_resampler *r) {
+ pa_assert(r);
+
+ if (r->src.state)
+ src_delete(r->src.state);
}
static int libsamplerate_init(pa_resampler *r) {
- struct impl_libsamplerate *u = NULL;
int err;
- r->impl_data = u = pa_xnew(struct impl_libsamplerate, 1);
+ pa_assert(r);
- u->buf1 = u->buf2 = u->buf3 = u->buf4 = NULL;
- u->buf1_block = u->buf2_block = u->buf3_block = u->buf4_block = NULL;
- u->buf1_samples = u->buf2_samples = u->buf3_samples = u->buf4_samples = 0;
+ if (!(r->src.state = src_new(r->resample_method, r->o_ss.channels, &err)))
+ return -1;
- if (r->i_ss.format == PA_SAMPLE_FLOAT32NE)
- u->to_float32ne_func = NULL;
- else if (!(u->to_float32ne_func = pa_get_convert_to_float32ne_function(r->i_ss.format)))
- goto fail;
+ r->impl_free = libsamplerate_free;
+ r->impl_update_rates = libsamplerate_update_rates;
+ r->impl_resample = libsamplerate_resample;
- if (r->o_ss.format == PA_SAMPLE_FLOAT32NE)
- u->from_float32ne_func = NULL;
- else if (!(u->from_float32ne_func = pa_get_convert_from_float32ne_function(r->o_ss.format)))
- goto fail;
+ return 0;
+}
+#endif
- if (r->o_ss.rate == r->i_ss.rate)
- u->src_state = NULL;
- else if (!(u->src_state = src_new(r->resample_method, r->o_ss.channels, &err)))
- goto fail;
+/*** speex based implementation ***/
- r->impl_free = libsamplerate_free;
- r->impl_update_input_rate = libsamplerate_update_input_rate;
- r->impl_run = libsamplerate_run;
+static void speex_resample_float(pa_resampler *r, const pa_memchunk *input, unsigned in_n_frames, pa_memchunk *output, unsigned *out_n_frames) {
+ float *in, *out;
+ uint32_t inf = in_n_frames, outf = *out_n_frames;
- calc_map_table(r);
+ pa_assert(r);
+ pa_assert(input);
+ pa_assert(output);
+ pa_assert(out_n_frames);
- return 0;
+ in = (float*) ((uint8_t*) pa_memblock_acquire(input->memblock) + input->index);
+ out = (float*) ((uint8_t*) pa_memblock_acquire(output->memblock) + output->index);
-fail:
- pa_xfree(u);
- return -1;
+ pa_assert_se(paspfl_resampler_process_interleaved_float(r->speex.state, in, &inf, out, &outf) == 0);
+
+ pa_memblock_release(input->memblock);
+ pa_memblock_release(output->memblock);
+
+ pa_assert(inf == in_n_frames);
+ *out_n_frames = outf;
}
-/* Trivial implementation */
+static void speex_resample_int(pa_resampler *r, const pa_memchunk *input, unsigned in_n_frames, pa_memchunk *output, unsigned *out_n_frames) {
+ int16_t *in, *out;
+ uint32_t inf = in_n_frames, outf = *out_n_frames;
-static void trivial_run(pa_resampler *r, const pa_memchunk *in, pa_memchunk *out) {
- size_t fz;
- unsigned n_frames;
- struct impl_trivial *u;
+ pa_assert(r);
+ pa_assert(input);
+ pa_assert(output);
+ pa_assert(out_n_frames);
+
+ 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_memblock_release(input->memblock);
+ pa_memblock_release(output->memblock);
- assert(r);
- assert(in);
- assert(out);
- assert(r->impl_data);
+ pa_assert(inf == in_n_frames);
+ *out_n_frames = outf;
+}
- u = r->impl_data;
+static void speex_update_rates(pa_resampler *r) {
+ pa_assert(r);
- fz = r->i_fz;
- assert(fz == r->o_fz);
+ if (r->resample_method >= PA_RESAMPLER_SPEEX_FIXED_BASE && r->resample_method <= PA_RESAMPLER_SPEEX_FIXED_MAX)
+ pa_assert_se(paspfx_resampler_set_rate(r->speex.state, r->i_ss.rate, r->o_ss.rate) == 0);
+ else {
+ pa_assert(r->resample_method >= PA_RESAMPLER_SPEEX_FLOAT_BASE && r->resample_method <= PA_RESAMPLER_SPEEX_FLOAT_MAX);
+ pa_assert_se(paspfl_resampler_set_rate(r->speex.state, r->i_ss.rate, r->o_ss.rate) == 0);
+ }
+}
- n_frames = in->length/fz;
+static void speex_free(pa_resampler *r) {
+ pa_assert(r);
- if (r->i_ss.rate == r->o_ss.rate) {
+ if (!r->speex.state)
+ return;
+
+ if (r->resample_method >= PA_RESAMPLER_SPEEX_FIXED_BASE && r->resample_method <= PA_RESAMPLER_SPEEX_FIXED_MAX)
+ paspfx_resampler_destroy(r->speex.state);
+ else {
+ pa_assert(r->resample_method >= PA_RESAMPLER_SPEEX_FLOAT_BASE && r->resample_method <= PA_RESAMPLER_SPEEX_FLOAT_MAX);
+ paspfl_resampler_destroy(r->speex.state);
+ }
+}
- /* In case there's no diefference in sample types, do nothing */
- *out = *in;
- pa_memblock_ref(out->memblock);
+static int speex_init(pa_resampler *r) {
+ int q, err;
- u->o_counter += n_frames;
+ pa_assert(r);
+
+ r->impl_free = speex_free;
+ r->impl_update_rates = speex_update_rates;
+
+ if (r->resample_method >= PA_RESAMPLER_SPEEX_FIXED_BASE && r->resample_method <= PA_RESAMPLER_SPEEX_FIXED_MAX) {
+ q = r->resample_method - PA_RESAMPLER_SPEEX_FIXED_BASE;
+
+ 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;
+
+ r->impl_resample = speex_resample_int;
} else {
- /* Do real resampling */
- size_t l;
- unsigned o_index;
+ pa_assert(r->resample_method >= PA_RESAMPLER_SPEEX_FLOAT_BASE && r->resample_method <= PA_RESAMPLER_SPEEX_FLOAT_MAX);
+ q = r->resample_method - PA_RESAMPLER_SPEEX_FLOAT_BASE;
+
+ pa_log_info("Choosing speex quality setting %i.", q);
- /* The length of the new memory block rounded up */
- l = ((((n_frames+1) * r->o_ss.rate) / r->i_ss.rate) + 1) * fz;
+ if (!(r->speex.state = paspfl_resampler_init(r->o_ss.channels, r->i_ss.rate, r->o_ss.rate, q, &err)))
+ return -1;
- out->index = 0;
- out->memblock = pa_memblock_new(r->mempool, l);
+ r->impl_resample = speex_resample_float;
+ }
- for (o_index = 0;; o_index++, u->o_counter++) {
- unsigned j;
+ return 0;
+}
- j = (u->o_counter * r->i_ss.rate / r->o_ss.rate);
- j = j > u->i_counter ? j - u->i_counter : 0;
+/* Trivial implementation */
- if (j >= n_frames)
- break;
+static void trivial_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;
- assert(o_index*fz < out->memblock->length);
+ pa_assert(r);
+ pa_assert(input);
+ pa_assert(output);
+ pa_assert(out_n_frames);
- memcpy((uint8_t*) out->memblock->data + fz*o_index,
- (uint8_t*) in->memblock->data + in->index + fz*j, fz);
+ 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->trivial.o_counter++) {
+ unsigned j;
- out->length = o_index*fz;
+ j = ((r->trivial.o_counter * r->i_ss.rate) / r->o_ss.rate);
+ j = j > r->trivial.i_counter ? j - r->trivial.i_counter : 0;
+
+ if (j >= in_n_frames)
+ break;
+
+ 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);
}
- u->i_counter += n_frames;
+ pa_memblock_release(input->memblock);
+ pa_memblock_release(output->memblock);
+
+ *out_n_frames = o_index;
+
+ r->trivial.i_counter += in_n_frames;
/* Normalize counters */
- while (u->i_counter >= r->i_ss.rate) {
- u->i_counter -= r->i_ss.rate;
- assert(u->o_counter >= r->o_ss.rate);
- u->o_counter -= r->o_ss.rate;
+ while (r->trivial.i_counter >= r->i_ss.rate) {
+ pa_assert(r->trivial.o_counter >= r->o_ss.rate);
+
+ r->trivial.i_counter -= r->i_ss.rate;
+ r->trivial.o_counter -= r->o_ss.rate;
}
}
-static void trivial_free(pa_resampler *r) {
- assert(r);
+static void trivial_update_rates(pa_resampler *r) {
+ pa_assert(r);
- pa_xfree(r->impl_data);
+ r->trivial.i_counter = 0;
+ r->trivial.o_counter = 0;
}
-static void trivial_update_input_rate(pa_resampler *r, uint32_t rate) {
- struct impl_trivial *u;
+static int trivial_init(pa_resampler*r) {
+ pa_assert(r);
+
+ r->trivial.o_counter = r->trivial.i_counter = 0;
- assert(r);
- assert(rate > 0);
- assert(r->impl_data);
+ r->impl_resample = trivial_resample;
+ r->impl_update_rates = trivial_update_rates;
+ r->impl_free = NULL;
- u = r->impl_data;
- u->i_counter = 0;
- u->o_counter = 0;
+ return 0;
}
-static int trivial_init(pa_resampler*r) {
- struct impl_trivial *u;
+/*** ffmpeg based implementation ***/
+
+static void ffmpeg_resample(pa_resampler *r, const pa_memchunk *input, unsigned in_n_frames, pa_memchunk *output, unsigned *out_n_frames) {
+ unsigned used_frames = 0, c;
+
+ pa_assert(r);
+ pa_assert(input);
+ pa_assert(output);
+ pa_assert(out_n_frames);
+
+ for (c = 0; c < r->o_ss.channels; c++) {
+ unsigned u;
+ pa_memblock *b, *w;
+ int16_t *p, *t, *k, *q, *s;
+ int consumed_frames;
+ unsigned in, l;
+
+ /* Allocate a new block */
+ b = pa_memblock_new(r->mempool, r->ffmpeg.buf[c].length + in_n_frames * sizeof(int16_t));
+ p = pa_memblock_acquire(b);
+
+ /* Copy the remaining data into it */
+ l = 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);
+ pa_memblock_release(r->ffmpeg.buf[c].memblock);
+ pa_memblock_unref(r->ffmpeg.buf[c].memblock);
+ pa_memchunk_reset(&r->ffmpeg.buf[c]);
+ }
+
+ /* Now append the new data, splitting up channels */
+ t = ((int16_t*) ((uint8_t*) pa_memblock_acquire(input->memblock) + input->index)) + c;
+ k = (int16_t*) ((uint8_t*) p + l);
+ for (u = 0; u < in_n_frames; u++) {
+ *k = *t;
+ t += r->o_ss.channels;
+ k ++;
+ }
+ pa_memblock_release(input->memblock);
+
+ /* Calculate the resulting number of frames */
+ in = in_n_frames + l / 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);
+
+ pa_memblock_release(b);
+
+ /* Now store the remaining samples away */
+ 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);
+ } else
+ pa_memblock_unref(b);
+
+ /* And place the results in the output buffer */
+ s = (short*) ((uint8_t*) pa_memblock_acquire(output->memblock) + output->index) + c;
+ for (u = 0; u < used_frames; u++) {
+ *s = *q;
+ q++;
+ s += r->o_ss.channels;
+ }
+ pa_memblock_release(output->memblock);
+ pa_memblock_release(w);
+ pa_memblock_unref(w);
+ }
+
+ *out_n_frames = used_frames;
+}
+
+static void ffmpeg_free(pa_resampler *r) {
+ unsigned c;
- assert(r);
- assert(r->i_ss.format == r->o_ss.format);
- assert(r->i_ss.channels == r->o_ss.channels);
+ pa_assert(r);
- r->impl_data = u = pa_xnew(struct impl_trivial, 1);
- u->o_counter = u->i_counter = 0;
+ if (r->ffmpeg.state)
+ av_resample_close(r->ffmpeg.state);
- r->impl_run = trivial_run;
- r->impl_free = trivial_free;
- r->impl_update_input_rate = trivial_update_input_rate;
+ for (c = 0; c < PA_ELEMENTSOF(r->ffmpeg.buf); c++)
+ if (r->ffmpeg.buf[c].memblock)
+ pa_memblock_unref(r->ffmpeg.buf[c].memblock);
+}
+
+static int ffmpeg_init(pa_resampler *r) {
+ unsigned c;
+
+ pa_assert(r);
+
+ /* We could probably implement different quality levels by
+ * adjusting the filter parameters here. However, ffmpeg
+ * 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)))
+ return -1;
+
+ r->impl_free = ffmpeg_free;
+ r->impl_resample = ffmpeg_resample;
+
+ for (c = 0; c < PA_ELEMENTSOF(r->ffmpeg.buf); c++)
+ pa_memchunk_reset(&r->ffmpeg.buf[c]);
return 0;
}
+/*** copy (noop) implementation ***/
+static int copy_init(pa_resampler *r) {
+ pa_assert(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 c283593d..23e1acb7 100644
--- a/src/pulsecore/resampler.h
+++ b/src/pulsecore/resampler.h
@@ -24,8 +24,6 @@
USA.
***/
-#include <samplerate.h>
-
#include <pulse/sample.h>
#include <pulse/channelmap.h>
#include <pulsecore/memblock.h>
@@ -35,12 +33,19 @@ typedef struct pa_resampler pa_resampler;
typedef enum pa_resample_method {
PA_RESAMPLER_INVALID = -1,
- PA_RESAMPLER_SRC_SINC_BEST_QUALITY = SRC_SINC_BEST_QUALITY,
- PA_RESAMPLER_SRC_SINC_MEDIUM_QUALITY = SRC_SINC_MEDIUM_QUALITY,
- PA_RESAMPLER_SRC_SINC_FASTEST = SRC_SINC_FASTEST,
- PA_RESAMPLER_SRC_ZERO_ORDER_HOLD = SRC_ZERO_ORDER_HOLD,
- PA_RESAMPLER_SRC_LINEAR = SRC_LINEAR,
+ PA_RESAMPLER_SRC_SINC_BEST_QUALITY = 0, /* = SRC_SINC_BEST_QUALITY */
+ PA_RESAMPLER_SRC_SINC_MEDIUM_QUALITY = 1, /* = SRC_SINC_MEDIUM_QUALITY */
+ PA_RESAMPLER_SRC_SINC_FASTEST = 2, /* = SRC_SINC_FASTEST */
+ PA_RESAMPLER_SRC_ZERO_ORDER_HOLD = 3, /* = SRC_ZERO_ORDER_HOLD */
+ PA_RESAMPLER_SRC_LINEAR = 4, /* = SRC_LINEAR */
PA_RESAMPLER_TRIVIAL,
+ PA_RESAMPLER_SPEEX_FLOAT_BASE,
+ PA_RESAMPLER_SPEEX_FLOAT_MAX = PA_RESAMPLER_SPEEX_FLOAT_BASE + 10,
+ PA_RESAMPLER_SPEEX_FIXED_BASE,
+ PA_RESAMPLER_SPEEX_FIXED_MAX = PA_RESAMPLER_SPEEX_FIXED_BASE + 10,
+ PA_RESAMPLER_FFMPEG,
+ PA_RESAMPLER_AUTO, /* automatic select based on sample format */
+ PA_RESAMPLER_COPY,
PA_RESAMPLER_MAX
} pa_resample_method_t;
@@ -50,19 +55,26 @@ pa_resampler* pa_resampler_new(
const pa_channel_map *am,
const pa_sample_spec *b,
const pa_channel_map *bm,
- pa_resample_method_t resample_method);
+ pa_resample_method_t resample_method,
+ int variable_rate);
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);
+/* 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);
+
/* Pass the specified memory chunk to the resampler and return the newly resampled data */
void pa_resampler_run(pa_resampler *r, const pa_memchunk *in, pa_memchunk *out);
/* Change the input rate of the resampler object */
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);
+
/* Return the resampling method of the resampler object */
pa_resample_method_t pa_resampler_get_method(pa_resampler *r);
@@ -72,4 +84,7 @@ pa_resample_method_t pa_parse_resample_method(const char *string);
/* return a human readable string for the specified resampling method. Inverse of pa_parse_resample_method() */
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
new file mode 100644
index 00000000..07d776e4
--- /dev/null
+++ b/src/pulsecore/rtclock.c
@@ -0,0 +1,98 @@
+/* $Id$ */
+
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2004-2006 Lennart Poettering
+ Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stddef.h>
+#include <time.h>
+#include <sys/time.h>
+
+#include <pulse/timeval.h>
+#include <pulsecore/macro.h>
+
+#include "rtclock.h"
+
+pa_usec_t pa_rtclock_age(const struct timeval *tv) {
+ struct timeval now;
+ pa_assert(tv);
+
+ return pa_timeval_diff(pa_rtclock_get(&now), tv);
+}
+
+struct timeval *pa_rtclock_get(struct timeval *tv) {
+#ifdef HAVE_CLOCK_GETTIME
+ struct timespec ts;
+
+#ifdef CLOCK_MONOTONIC
+ /* No locking or atomic ops for no_monotonic here */
+ static pa_bool_t no_monotonic = FALSE;
+
+ if (!no_monotonic)
+ if (clock_gettime(CLOCK_MONOTONIC, &ts) < 0)
+ no_monotonic = TRUE;
+
+ if (no_monotonic)
+#endif
+ pa_assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0);
+
+ pa_assert(tv);
+
+ tv->tv_sec = ts.tv_sec;
+ tv->tv_usec = ts.tv_nsec / 1000;
+
+ return tv;
+
+#else /* HAVE_CLOCK_GETTIME */
+
+ return pa_gettimeofday(tv);
+
+#endif
+}
+
+pa_bool_t pa_rtclock_hrtimer(void) {
+#ifdef HAVE_CLOCK_GETTIME
+ struct timespec ts;
+
+#ifdef CLOCK_MONOTONIC
+ if (clock_getres(CLOCK_MONOTONIC, &ts) >= 0)
+ return ts.tv_sec == 0 && ts.tv_nsec <= PA_HRTIMER_THRESHOLD_USEC*1000;
+#endif
+
+ pa_assert_se(clock_getres(CLOCK_REALTIME, &ts) == 0);
+ return ts.tv_sec == 0 && ts.tv_nsec <= PA_HRTIMER_THRESHOLD_USEC*1000;
+
+#else /* HAVE_CLOCK_GETTIME */
+
+ return FALSE;
+
+#endif
+}
+
+pa_usec_t pa_rtclock_usec(void) {
+ struct timeval tv;
+
+ return pa_timeval_load(pa_rtclock_get(&tv));
+}
diff --git a/src/pulsecore/rtclock.h b/src/pulsecore/rtclock.h
new file mode 100644
index 00000000..f0360af3
--- /dev/null
+++ b/src/pulsecore/rtclock.h
@@ -0,0 +1,43 @@
+#ifndef foopulsertclockhfoo
+#define foopulsertclockhfoo
+
+/* $Id$ */
+
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2004-2006 Lennart Poettering
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.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>
+
+struct timeval;
+
+/* Something like pulse/timeval.h but based on CLOCK_MONOTONIC */
+
+struct timeval *pa_rtclock_get(struct timeval *ts);
+
+pa_usec_t pa_rtclock_usec(void);
+
+pa_usec_t pa_rtclock_age(const struct timeval *tv);
+pa_bool_t pa_rtclock_hrtimer(void);
+
+/* timer with a resolution better than this are considered high-resolution */
+#define PA_HRTIMER_THRESHOLD_USEC 10
+
+#endif
diff --git a/src/pulsecore/rtpoll.c b/src/pulsecore/rtpoll.c
new file mode 100644
index 00000000..354c4c0e
--- /dev/null
+++ b/src/pulsecore/rtpoll.c
@@ -0,0 +1,751 @@
+/* $Id$ */
+
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2004-2006 Lennart Poettering
+ Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <signal.h>
+#include <string.h>
+#include <errno.h>
+
+#ifdef __linux__
+#include <sys/utsname.h>
+#endif
+
+#ifdef HAVE_POLL_H
+#include <poll.h>
+#else
+#include <pulsecore/poll.h>
+#endif
+
+#include <pulse/xmalloc.h>
+#include <pulse/timeval.h>
+
+#include <pulsecore/core-error.h>
+#include <pulsecore/rtclock.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/llist.h>
+#include <pulsecore/rtsig.h>
+#include <pulsecore/flist.h>
+#include <pulsecore/core-util.h>
+
+#include <pulsecore/winsock.h>
+
+#include "rtpoll.h"
+
+struct pa_rtpoll {
+ struct pollfd *pollfd, *pollfd2;
+ unsigned n_pollfd_alloc, n_pollfd_used;
+
+ 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;
+
+#ifdef HAVE_PPOLL
+ int rtsig;
+ sigset_t sigset_unblocked;
+ timer_t timer;
+#ifdef __linux__
+ pa_bool_t dont_use_ppoll;
+#endif
+#endif
+
+ PA_LLIST_HEAD(pa_rtpoll_item, items);
+};
+
+struct pa_rtpoll_item {
+ pa_rtpoll *rtpoll;
+ pa_bool_t dead;
+
+ pa_rtpoll_priority_t priority;
+
+ struct pollfd *pollfd;
+ unsigned n_pollfd;
+
+ int (*work_cb)(pa_rtpoll_item *i);
+ int (*before_cb)(pa_rtpoll_item *i);
+ void (*after_cb)(pa_rtpoll_item *i);
+ void *userdata;
+
+ PA_LLIST_FIELDS(pa_rtpoll_item);
+};
+
+PA_STATIC_FLIST_DECLARE(items, 0, pa_xfree);
+
+static void signal_handler_noop(int s) { }
+
+pa_rtpoll *pa_rtpoll_new(void) {
+ pa_rtpoll *p;
+
+ p = pa_xnew(pa_rtpoll, 1);
+
+#ifdef HAVE_PPOLL
+
+#ifdef __linux__
+ /* ppoll is broken on Linux < 2.6.16 */
+ p->dont_use_ppoll = FALSE;
+
+ {
+ struct utsname u;
+ unsigned major, minor, micro;
+
+ pa_assert_se(uname(&u) == 0);
+
+ if (sscanf(u.release, "%u.%u.%u", &major, &minor, &micro) != 3 ||
+ (major < 2) ||
+ (major == 2 && minor < 6) ||
+ (major == 2 && minor == 6 && micro < 16))
+
+ p->dont_use_ppoll = TRUE;
+ }
+
+#endif
+
+ p->rtsig = -1;
+ sigemptyset(&p->sigset_unblocked);
+ p->timer = (timer_t) -1;
+
+#endif
+
+ p->n_pollfd_alloc = 32;
+ p->pollfd = pa_xnew(struct pollfd, p->n_pollfd_alloc);
+ p->pollfd2 = pa_xnew(struct pollfd, p->n_pollfd_alloc);
+ p->n_pollfd_used = 0;
+
+ p->period = 0;
+ memset(&p->next_elapse, 0, sizeof(p->next_elapse));
+ p->timer_enabled = FALSE;
+
+ p->running = FALSE;
+ p->installed = FALSE;
+ p->scan_for_dead = FALSE;
+ p->rebuild_needed = FALSE;
+ p->quit = FALSE;
+
+ PA_LLIST_HEAD_INIT(pa_rtpoll_item, p->items);
+
+ return p;
+}
+
+void pa_rtpoll_install(pa_rtpoll *p) {
+ pa_assert(p);
+ pa_assert(!p->installed);
+
+ p->installed = 1;
+
+#ifdef HAVE_PPOLL
+ if (p->dont_use_ppoll)
+ return;
+
+ if ((p->rtsig = pa_rtsig_get_for_thread()) < 0) {
+ pa_log_warn("Failed to reserve POSIX realtime signal.");
+ return;
+ }
+
+ pa_log_debug("Acquired POSIX realtime signal %s", pa_sig2str(p->rtsig));
+
+ {
+ sigset_t ss;
+ struct sigaction sa;
+
+ pa_assert_se(sigemptyset(&ss) == 0);
+ pa_assert_se(sigaddset(&ss, p->rtsig) == 0);
+ pa_assert_se(pthread_sigmask(SIG_BLOCK, &ss, &p->sigset_unblocked) == 0);
+ pa_assert_se(sigdelset(&p->sigset_unblocked, p->rtsig) == 0);
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = signal_handler_noop;
+ pa_assert_se(sigemptyset(&sa.sa_mask) == 0);
+
+ pa_assert_se(sigaction(p->rtsig, &sa, NULL) == 0);
+
+ /* We never reset the signal handler. Why should we? */
+ }
+
+#endif
+}
+
+static void rtpoll_rebuild(pa_rtpoll *p) {
+
+ struct pollfd *e, *t;
+ pa_rtpoll_item *i;
+ int ra = 0;
+
+ pa_assert(p);
+
+ p->rebuild_needed = FALSE;
+
+ if (p->n_pollfd_used > p->n_pollfd_alloc) {
+ /* Hmm, we have to allocate some more space */
+ p->n_pollfd_alloc = p->n_pollfd_used * 2;
+ p->pollfd2 = pa_xrealloc(p->pollfd2, p->n_pollfd_alloc * sizeof(struct pollfd));
+ ra = 1;
+ }
+
+ e = p->pollfd2;
+
+ for (i = p->items; i; i = i->next) {
+
+ if (i->n_pollfd > 0) {
+ size_t l = i->n_pollfd * sizeof(struct pollfd);
+
+ if (i->pollfd)
+ memcpy(e, i->pollfd, l);
+ else
+ memset(e, 0, l);
+
+ i->pollfd = e;
+ } else
+ i->pollfd = NULL;
+
+ e += i->n_pollfd;
+ }
+
+ pa_assert((unsigned) (e - p->pollfd2) == p->n_pollfd_used);
+ t = p->pollfd;
+ p->pollfd = p->pollfd2;
+ p->pollfd2 = t;
+
+ if (ra)
+ p->pollfd2 = pa_xrealloc(p->pollfd2, p->n_pollfd_alloc * sizeof(struct pollfd));
+
+}
+
+static void rtpoll_item_destroy(pa_rtpoll_item *i) {
+ pa_rtpoll *p;
+
+ pa_assert(i);
+
+ p = i->rtpoll;
+
+ PA_LLIST_REMOVE(pa_rtpoll_item, p->items, i);
+
+ p->n_pollfd_used -= i->n_pollfd;
+
+ if (pa_flist_push(PA_STATIC_FLIST_GET(items), i) < 0)
+ pa_xfree(i);
+
+ p->rebuild_needed = TRUE;
+}
+
+void pa_rtpoll_free(pa_rtpoll *p) {
+ pa_assert(p);
+
+ while (p->items)
+ rtpoll_item_destroy(p->items);
+
+ pa_xfree(p->pollfd);
+ pa_xfree(p->pollfd2);
+
+#ifdef HAVE_PPOLL
+ if (p->timer != (timer_t) -1)
+ timer_delete(p->timer);
+#endif
+
+ pa_xfree(p);
+}
+
+static void reset_revents(pa_rtpoll_item *i) {
+ struct pollfd *f;
+ unsigned n;
+
+ pa_assert(i);
+
+ if (!(f = pa_rtpoll_item_get_pollfd(i, &n)))
+ return;
+
+ for (; n > 0; n--)
+ f[n-1].revents = 0;
+}
+
+static void reset_all_revents(pa_rtpoll *p) {
+ pa_rtpoll_item *i;
+
+ pa_assert(p);
+
+ for (i = p->items; i; i = i->next) {
+
+ if (i->dead)
+ continue;
+
+ reset_revents(i);
+ }
+}
+
+int pa_rtpoll_run(pa_rtpoll *p, pa_bool_t wait) {
+ pa_rtpoll_item *i;
+ int r = 0;
+ struct timeval timeout;
+
+ pa_assert(p);
+ pa_assert(!p->running);
+ pa_assert(p->installed);
+
+ p->running = TRUE;
+
+ /* First, let's do some work */
+ for (i = p->items; i && i->priority < PA_RTPOLL_NEVER; i = i->next) {
+ int k;
+
+ if (i->dead)
+ continue;
+
+ if (!i->work_cb)
+ continue;
+
+ if (p->quit)
+ goto finish;
+
+ if ((k = i->work_cb(i)) != 0) {
+ if (k < 0)
+ r = k;
+
+ goto finish;
+ }
+ }
+
+ /* Now let's prepare for entering the sleep */
+ for (i = p->items; i && i->priority < PA_RTPOLL_NEVER; i = i->next) {
+ int k = 0;
+
+ if (i->dead)
+ continue;
+
+ if (!i->before_cb)
+ continue;
+
+ if (p->quit || (k = i->before_cb(i)) != 0) {
+
+ /* Hmm, this one doesn't let us enter the poll, so rewind everything */
+
+ for (i = i->prev; i; i = i->prev) {
+
+ if (i->dead)
+ continue;
+
+ if (!i->after_cb)
+ continue;
+
+ i->after_cb(i);
+ }
+
+ if (k < 0)
+ r = k;
+
+ goto finish;
+ }
+ }
+
+ if (p->rebuild_needed)
+ rtpoll_rebuild(p);
+
+ /* Calculate timeout */
+ if (!wait || p->quit) {
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 0;
+ } else if (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));
+ }
+
+ /* OK, now let's sleep */
+#ifdef HAVE_PPOLL
+
+#ifdef __linux__
+ if (!p->dont_use_ppoll)
+#endif
+ {
+ 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);
+ }
+#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);
+
+ if (r < 0) {
+ if (errno == EAGAIN || errno == EINTR)
+ r = 0;
+ else
+ pa_log_error("poll(): %s", pa_cstrerror(errno));
+
+ 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) {
+
+ if (i->dead)
+ continue;
+
+ if (!i->after_cb)
+ continue;
+
+ i->after_cb(i);
+ }
+
+finish:
+
+ p->running = FALSE;
+
+ if (p->scan_for_dead) {
+ pa_rtpoll_item *n;
+
+ p->scan_for_dead = FALSE;
+
+ for (i = p->items; i; i = n) {
+ n = i->next;
+
+ if (i->dead)
+ rtpoll_item_destroy(i);
+ }
+ }
+
+ return r < 0 ? r : !p->quit;
+}
+
+static void update_timer(pa_rtpoll *p) {
+ pa_assert(p);
+
+#ifdef HAVE_PPOLL
+
+#ifdef __linux__
+ if (!p->dont_use_ppoll) {
+#endif
+
+ if (p->timer == (timer_t) -1) {
+ struct sigevent se;
+
+ memset(&se, 0, sizeof(se));
+ se.sigev_notify = SIGEV_SIGNAL;
+ se.sigev_signo = p->rtsig;
+
+ if (timer_create(CLOCK_MONOTONIC, &se, &p->timer) < 0)
+ if (timer_create(CLOCK_REALTIME, &se, &p->timer) < 0) {
+ pa_log_warn("Failed to allocate POSIX timer: %s", pa_cstrerror(errno));
+ p->timer = (timer_t) -1;
+ }
+ }
+
+ if (p->timer != (timer_t) -1) {
+ struct itimerspec its;
+ memset(&its, 0, sizeof(its));
+
+ if (p->timer_enabled) {
+ 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)
+ 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);
+ }
+
+#ifdef __linux__
+ }
+#endif
+
+#endif
+}
+
+void pa_rtpoll_set_timer_absolute(pa_rtpoll *p, const struct timeval *ts) {
+ 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);
+ p->timer_enabled = TRUE;
+
+ update_timer(p);
+}
+
+void pa_rtpoll_set_timer_relative(pa_rtpoll *p, pa_usec_t usec) {
+ pa_assert(p);
+
+ p->period = 0;
+ pa_rtclock_get(&p->next_elapse);
+ pa_timeval_add(&p->next_elapse, usec);
+ p->timer_enabled = TRUE;
+
+ update_timer(p);
+}
+
+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;
+
+ update_timer(p);
+}
+
+pa_rtpoll_item *pa_rtpoll_item_new(pa_rtpoll *p, pa_rtpoll_priority_t prio, unsigned n_fds) {
+ pa_rtpoll_item *i, *j, *l = NULL;
+
+ pa_assert(p);
+
+ if (!(i = pa_flist_pop(PA_STATIC_FLIST_GET(items))))
+ i = pa_xnew(pa_rtpoll_item, 1);
+
+ i->rtpoll = p;
+ i->dead = FALSE;
+ i->n_pollfd = n_fds;
+ i->pollfd = NULL;
+ i->priority = prio;
+
+ i->userdata = NULL;
+ i->before_cb = NULL;
+ i->after_cb = NULL;
+ i->work_cb = NULL;
+
+ for (j = p->items; j; j = j->next) {
+ if (prio <= j->priority)
+ break;
+
+ l = j;
+ }
+
+ PA_LLIST_INSERT_AFTER(pa_rtpoll_item, p->items, j ? j->prev : l, i);
+
+ if (n_fds > 0) {
+ p->rebuild_needed = 1;
+ p->n_pollfd_used += n_fds;
+ }
+
+ return i;
+}
+
+void pa_rtpoll_item_free(pa_rtpoll_item *i) {
+ pa_assert(i);
+
+ if (i->rtpoll->running) {
+ i->dead = TRUE;
+ i->rtpoll->scan_for_dead = TRUE;
+ return;
+ }
+
+ rtpoll_item_destroy(i);
+}
+
+struct pollfd *pa_rtpoll_item_get_pollfd(pa_rtpoll_item *i, unsigned *n_fds) {
+ pa_assert(i);
+
+ if (i->n_pollfd > 0)
+ if (i->rtpoll->rebuild_needed)
+ rtpoll_rebuild(i->rtpoll);
+
+ if (n_fds)
+ *n_fds = i->n_pollfd;
+
+ return i->pollfd;
+}
+
+void pa_rtpoll_item_set_before_callback(pa_rtpoll_item *i, int (*before_cb)(pa_rtpoll_item *i)) {
+ pa_assert(i);
+ pa_assert(i->priority < PA_RTPOLL_NEVER);
+
+ i->before_cb = before_cb;
+}
+
+void pa_rtpoll_item_set_after_callback(pa_rtpoll_item *i, void (*after_cb)(pa_rtpoll_item *i)) {
+ pa_assert(i);
+ pa_assert(i->priority < PA_RTPOLL_NEVER);
+
+ i->after_cb = after_cb;
+}
+
+void pa_rtpoll_item_set_work_callback(pa_rtpoll_item *i, int (*work_cb)(pa_rtpoll_item *i)) {
+ pa_assert(i);
+ pa_assert(i->priority < PA_RTPOLL_NEVER);
+
+ i->work_cb = work_cb;
+}
+
+void pa_rtpoll_item_set_userdata(pa_rtpoll_item *i, void *userdata) {
+ pa_assert(i);
+
+ i->userdata = userdata;
+}
+
+void* pa_rtpoll_item_get_userdata(pa_rtpoll_item *i) {
+ pa_assert(i);
+
+ return i->userdata;
+}
+
+static int fdsem_before(pa_rtpoll_item *i) {
+
+ if (pa_fdsem_before_poll(i->userdata) < 0)
+ return 1; /* 1 means immediate restart of the loop */
+
+ return 0;
+}
+
+static void fdsem_after(pa_rtpoll_item *i) {
+ pa_assert(i);
+
+ pa_assert((i->pollfd[0].revents & ~POLLIN) == 0);
+ pa_fdsem_after_poll(i->userdata);
+}
+
+pa_rtpoll_item *pa_rtpoll_item_new_fdsem(pa_rtpoll *p, pa_rtpoll_priority_t prio, pa_fdsem *f) {
+ pa_rtpoll_item *i;
+ struct pollfd *pollfd;
+
+ pa_assert(p);
+ pa_assert(f);
+
+ i = pa_rtpoll_item_new(p, prio, 1);
+
+ pollfd = pa_rtpoll_item_get_pollfd(i, NULL);
+
+ pollfd->fd = pa_fdsem_get(f);
+ pollfd->events = POLLIN;
+
+ i->before_cb = fdsem_before;
+ i->after_cb = fdsem_after;
+ i->userdata = f;
+
+ return i;
+}
+
+static int asyncmsgq_before(pa_rtpoll_item *i) {
+ pa_assert(i);
+
+ if (pa_asyncmsgq_before_poll(i->userdata) < 0)
+ return 1; /* 1 means immediate restart of the loop */
+
+ return 0;
+}
+
+static void asyncmsgq_after(pa_rtpoll_item *i) {
+ pa_assert(i);
+
+ pa_assert((i->pollfd[0].revents & ~POLLIN) == 0);
+ pa_asyncmsgq_after_poll(i->userdata);
+}
+
+static int asyncmsgq_work(pa_rtpoll_item *i) {
+ pa_msgobject *object;
+ int code;
+ void *data;
+ pa_memchunk chunk;
+ int64_t offset;
+
+ pa_assert(i);
+
+ if (pa_asyncmsgq_get(i->userdata, &object, &code, &data, &offset, &chunk, 0) == 0) {
+ int ret;
+
+ if (!object && code == PA_MESSAGE_SHUTDOWN) {
+ pa_asyncmsgq_done(i->userdata, 0);
+ pa_rtpoll_quit(i->rtpoll);
+ return 1;
+ }
+
+ ret = pa_asyncmsgq_dispatch(object, code, data, offset, &chunk);
+ pa_asyncmsgq_done(i->userdata, ret);
+ return 1;
+ }
+
+ return 0;
+}
+
+pa_rtpoll_item *pa_rtpoll_item_new_asyncmsgq(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_get_fd(q);
+ pollfd->events = POLLIN;
+
+ i->before_cb = asyncmsgq_before;
+ i->after_cb = asyncmsgq_after;
+ i->work_cb = asyncmsgq_work;
+ i->userdata = q;
+
+ return i;
+}
+
+void pa_rtpoll_quit(pa_rtpoll *p) {
+ pa_assert(p);
+
+ p->quit = TRUE;
+}
diff --git a/src/pulsecore/rtpoll.h b/src/pulsecore/rtpoll.h
new file mode 100644
index 00000000..02f5c7c2
--- /dev/null
+++ b/src/pulsecore/rtpoll.h
@@ -0,0 +1,116 @@
+#ifndef foopulsertpollhfoo
+#define foopulsertpollhfoo
+
+/* $Id$ */
+
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2004-2006 Lennart Poettering
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <sys/types.h>
+#include <limits.h>
+
+#include <pulse/sample.h>
+#include <pulsecore/asyncmsgq.h>
+#include <pulsecore/fdsem.h>
+#include <pulsecore/macro.h>
+
+/* An implementation of a "real-time" poll loop. Basically, this is
+ * yet another wrapper around poll(). However it has certain
+ * advantages over pa_mainloop and suchlike:
+ *
+ * 1) It uses timer_create() and POSIX real time signals to guarantee
+ * optimal high-resolution timing. Starting with Linux 2.6.21 hrtimers
+ * are available, and since right now only nanosleep() and the POSIX
+ * clock and timer interfaces have been ported to hrtimers (and not
+ * ppoll/pselect!) we have to combine ppoll() with timer_create(). The
+ * fact that POSIX timers and POSIX rt signals are used internally is
+ * completely hidden.
+ *
+ * 2) It allows raw access to the pollfd data to users
+ *
+ * 3) It allows arbitrary functions to be run before entering the
+ * actual poll() and after it.
+ *
+ * Only a single interval timer is supported..*/
+
+typedef struct pa_rtpoll pa_rtpoll;
+typedef struct pa_rtpoll_item pa_rtpoll_item;
+
+typedef enum pa_rtpoll_priority {
+ PA_RTPOLL_EARLY = -100, /* For veeery important stuff, like handling control messages */
+ PA_RTPOLL_NORMAL = 0, /* For normal stuff */
+ PA_RTPOLL_LATE = +100, /* For housekeeping */
+ PA_RTPOLL_NEVER = INT_MAX, /* For stuff that doesn't register any callbacks, but only fds to listen on */
+} pa_rtpoll_priority_t;
+
+pa_rtpoll *pa_rtpoll_new(void);
+void pa_rtpoll_free(pa_rtpoll *p);
+
+/* Install the rtpoll in the current thread */
+void pa_rtpoll_install(pa_rtpoll *p);
+
+/* Sleep on the rtpoll until the time event, or any of the fd events
+ * is triggered. If "wait" is 0 we don't sleep but only update the
+ * struct pollfd. Returns negative on error, positive if the loop
+ * should continue to run, 0 when the loop should be terminated
+ * 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_relative(pa_rtpoll *p, pa_usec_t usec);
+void pa_rtpoll_set_timer_disabled(pa_rtpoll *p);
+
+/* A new fd wakeup item for pa_rtpoll */
+pa_rtpoll_item *pa_rtpoll_item_new(pa_rtpoll *p, pa_rtpoll_priority_t prio, unsigned n_fds);
+void pa_rtpoll_item_free(pa_rtpoll_item *i);
+
+/* Please note that this pointer might change on every call and when
+ * pa_rtpoll_run() is called. Hence: call this immediately before
+ * using the pointer and don't save the result anywhere */
+struct pollfd *pa_rtpoll_item_get_pollfd(pa_rtpoll_item *i, unsigned *n_fds);
+
+/* Set the callback that shall be called when there's time to do some work: If the
+ * callback returns a value > 0, the poll is skipped and the next
+ * iteraton of the loop will start immediately. */
+void pa_rtpoll_item_set_work_callback(pa_rtpoll_item *i, int (*work_cb)(pa_rtpoll_item *i));
+
+/* Set the callback that shall be called immediately before entering
+ * the sleeping poll: If the callback returns a value > 0, the poll is
+ * skipped and the next iteraton of the loop will start
+ * immediately.. */
+void pa_rtpoll_item_set_before_callback(pa_rtpoll_item *i, int (*before_cb)(pa_rtpoll_item *i));
+
+/* Set the callback that shall be called immediately after having
+ * entered the sleeping poll */
+void pa_rtpoll_item_set_after_callback(pa_rtpoll_item *i, void (*after_cb)(pa_rtpoll_item *i));
+
+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);
+
+/* Requests the loop to exit. Will cause the next iteration of
+ * pa_rtpoll_run() to return 0 */
+void pa_rtpoll_quit(pa_rtpoll *p);
+
+#endif
diff --git a/src/pulsecore/rtsig.c b/src/pulsecore/rtsig.c
new file mode 100644
index 00000000..bfc49c88
--- /dev/null
+++ b/src/pulsecore/rtsig.c
@@ -0,0 +1,133 @@
+/* $Id$ */
+
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2004-2006 Lennart Poettering
+ Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <signal.h>
+
+#include <pulsecore/macro.h>
+#include <pulsecore/flist.h>
+#include <pulsecore/once.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/core-util.h>
+
+#include "rtsig.h"
+
+#ifdef SIGRTMIN
+
+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_TLS_DECLARE(rtsig_tls, _free_rtsig);
+
+static pa_atomic_t rtsig_current = PA_ATOMIC_INIT(-1);
+
+static int rtsig_start = -1, rtsig_end = -1;
+
+int pa_rtsig_get(void) {
+ void *p;
+ int sig;
+
+ if ((p = pa_flist_pop(PA_STATIC_FLIST_GET(rtsig_flist))))
+ return PA_PTR_TO_INT(p);
+
+ sig = pa_atomic_dec(&rtsig_current);
+
+ pa_assert(sig <= SIGRTMAX);
+ pa_assert(sig <= rtsig_end);
+
+ if (sig < rtsig_start) {
+ pa_atomic_inc(&rtsig_current);
+ return -1;
+ }
+
+ return sig;
+}
+
+int pa_rtsig_get_for_thread(void) {
+ int sig;
+ void *p;
+
+ if ((p = PA_STATIC_TLS_GET(rtsig_tls)))
+ return PA_PTR_TO_INT(p);
+
+ if ((sig = pa_rtsig_get()) < 0)
+ return -1;
+
+ PA_STATIC_TLS_SET(rtsig_tls, PA_INT_TO_PTR(sig));
+ return sig;
+}
+
+void pa_rtsig_put(int sig) {
+ pa_assert(sig >= rtsig_start);
+ pa_assert(sig <= rtsig_end);
+
+ pa_assert_se(pa_flist_push(PA_STATIC_FLIST_GET(rtsig_flist), PA_INT_TO_PTR(sig)) >= 0);
+}
+
+void pa_rtsig_configure(int start, int end) {
+ int s;
+ sigset_t ss;
+
+ pa_assert(pa_atomic_load(&rtsig_current) == -1);
+
+ pa_assert(SIGRTMIN <= start);
+ pa_assert(start <= end);
+ pa_assert(end <= SIGRTMAX);
+
+ rtsig_start = start;
+ rtsig_end = end;
+
+ sigemptyset(&ss);
+
+ for (s = rtsig_start; s <= rtsig_end; s++)
+ pa_assert_se(sigaddset(&ss, s) == 0);
+
+ pa_assert(pthread_sigmask(SIG_BLOCK, &ss, NULL) == 0);
+
+ /* We allocate starting from the end */
+ pa_atomic_store(&rtsig_current, rtsig_end);
+}
+
+#else /* SIGRTMIN */
+
+int pa_rtsig_get(void) {
+ return -1;
+}
+
+int pa_rtsig_get_for_thread(void) {
+ return -1;
+}
+
+void pa_rtsig_put(int sig) {
+}
+
+void pa_rtsig_configure(int start, int end) {
+}
+
+#endif /* SIGRTMIN */
diff --git a/src/pulsecore/rtsig.h b/src/pulsecore/rtsig.h
new file mode 100644
index 00000000..7830d272
--- /dev/null
+++ b/src/pulsecore/rtsig.h
@@ -0,0 +1,41 @@
+#ifndef foopulsertsighfoo
+#define foopulsertsighfoo
+
+/* $Id$ */
+
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2004-2006 Lennart Poettering
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.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.
+***/
+
+/* Return the next unused POSIX Realtime signals */
+int pa_rtsig_get(void);
+
+/* If not called before in the current thread, return the next unused
+ * rtsig, and install it in a TLS region and give it up automatically
+ * when the thread shuts down */
+int pa_rtsig_get_for_thread(void);
+
+/* Give an rtsig back. */
+void pa_rtsig_put(int sig);
+
+/* Block all RT signals */
+void pa_rtsig_configure(int start, int end);
+
+#endif
diff --git a/src/pulsecore/sample-util.c b/src/pulsecore/sample-util.c
index a9971408..21771302 100644
--- a/src/pulsecore/sample-util.c
+++ b/src/pulsecore/sample-util.c
@@ -28,53 +28,71 @@
#include <stdio.h>
#include <string.h>
-#include <assert.h>
#include <stdlib.h>
#include <liboil/liboilfuncs.h>
+#include <liboil/liboil.h>
#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
#include "sample-util.h"
#include "endianmacros.h"
-#define PA_SILENCE_MAX (1024*1024*1)
+#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;
- assert(pool);
- assert(spec);
+ pa_assert(pool);
+ pa_assert(spec);
- if (length == 0)
+ 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 = ((PA_SILENCE_MAX+fs-1) / fs) * fs;
+
+ length = (length+fs-1)/fs;
if (length <= 0)
- length = fs;
+ 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) {
- assert(b && b->data && spec);
- pa_silence_memory(b->data, b->length, spec);
+ void *data;
+
+ pa_assert(b);
+ pa_assert(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) {
- assert(c && c->memblock && c->memblock->data && spec && c->length);
+ void *data;
- pa_silence_memory((uint8_t*) c->memblock->data+c->index, c->length, spec);
+ pa_assert(c);
+ pa_assert(c->memblock);
+ pa_assert(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;
- assert(p && length && spec);
+ pa_assert(p);
+ pa_assert(length > 0);
+ pa_assert(spec);
switch (spec->format) {
case PA_SAMPLE_U8:
@@ -87,37 +105,51 @@ void pa_silence_memory(void *p, size_t length, const pa_sample_spec *spec) {
c = 0;
break;
case PA_SAMPLE_ALAW:
+ c = 0xd5;
+ break;
case PA_SAMPLE_ULAW:
- c = 80;
+ c = 0xff;
break;
default:
- assert(0);
+ pa_assert_not_reached();
}
memset(p, c, length);
}
size_t pa_mix(
- const pa_mix_info streams[],
- unsigned nstreams,
- void *data,
- size_t length,
- const pa_sample_spec *spec,
- const pa_cvolume *volume,
- int mute) {
+ pa_mix_info streams[],
+ unsigned nstreams,
+ void *data,
+ size_t length,
+ const pa_sample_spec *spec,
+ const pa_cvolume *volume,
+ int mute) {
+
+ pa_cvolume full_volume;
+ size_t d = 0;
+ unsigned k;
+
+ pa_assert(streams);
+ pa_assert(data);
+ pa_assert(length);
+ pa_assert(spec);
- assert(streams && data && length && spec);
+ if (!volume)
+ volume = pa_cvolume_reset(&full_volume, spec->channels);
+
+ for (k = 0; k < nstreams; k++)
+ streams[k].internal = pa_memblock_acquire(streams[k].chunk.memblock);
switch (spec->format) {
case PA_SAMPLE_S16NE:{
- size_t d;
unsigned channel = 0;
for (d = 0;; d += sizeof(int16_t)) {
int32_t sum = 0;
if (d >= length)
- return d;
+ goto finish;
if (!mute && volume->values[channel] != PA_VOLUME_MUTED) {
unsigned i;
@@ -127,12 +159,12 @@ size_t pa_mix(
pa_volume_t cvolume = streams[i].volume.values[channel];
if (d >= streams[i].chunk.length)
- return d;
+ goto finish;
if (cvolume == PA_VOLUME_MUTED)
v = 0;
else {
- v = *((int16_t*) ((uint8_t*) streams[i].chunk.memblock->data + streams[i].chunk.index + d));
+ v = *((int16_t*) ((uint8_t*) streams[i].internal + streams[i].chunk.index + d));
if (cvolume != PA_VOLUME_NORM)
v = (int32_t) (v * pa_sw_volume_to_linear(cvolume));
@@ -144,28 +176,27 @@ size_t pa_mix(
if (volume->values[channel] != PA_VOLUME_NORM)
sum = (int32_t) (sum * pa_sw_volume_to_linear(volume->values[channel]));
- if (sum < -0x8000) sum = -0x8000;
- if (sum > 0x7FFF) sum = 0x7FFF;
-
+ sum = CLAMP(sum, -0x8000, 0x7FFF);
}
- *((int16_t*) data) = sum;
+ *((int16_t*) data) = (int16_t) sum;
data = (uint8_t*) data + sizeof(int16_t);
if (++channel >= spec->channels)
channel = 0;
}
+
+ break;
}
case PA_SAMPLE_S16RE:{
- size_t d;
unsigned channel = 0;
for (d = 0;; d += sizeof(int16_t)) {
int32_t sum = 0;
if (d >= length)
- return d;
+ goto finish;
if (!mute && volume->values[channel] != PA_VOLUME_MUTED) {
unsigned i;
@@ -175,12 +206,12 @@ size_t pa_mix(
pa_volume_t cvolume = streams[i].volume.values[channel];
if (d >= streams[i].chunk.length)
- return d;
+ goto finish;
if (cvolume == PA_VOLUME_MUTED)
v = 0;
else {
- v = INT16_SWAP(*((int16_t*) ((uint8_t*) streams[i].chunk.memblock->data + streams[i].chunk.index + d)));
+ v = PA_INT16_SWAP(*((int16_t*) ((uint8_t*) streams[i].internal + streams[i].chunk.index + d)));
if (cvolume != PA_VOLUME_NORM)
v = (int32_t) (v * pa_sw_volume_to_linear(cvolume));
@@ -192,28 +223,27 @@ size_t pa_mix(
if (volume->values[channel] != PA_VOLUME_NORM)
sum = (int32_t) (sum * pa_sw_volume_to_linear(volume->values[channel]));
- if (sum < -0x8000) sum = -0x8000;
- if (sum > 0x7FFF) sum = 0x7FFF;
-
+ sum = CLAMP(sum, -0x8000, 0x7FFF);
}
- *((int16_t*) data) = INT16_SWAP(sum);
+ *((int16_t*) data) = PA_INT16_SWAP((int16_t) sum);
data = (uint8_t*) data + sizeof(int16_t);
if (++channel >= spec->channels)
channel = 0;
}
+
+ break;
}
case PA_SAMPLE_U8: {
- size_t d;
unsigned channel = 0;
for (d = 0;; d ++) {
int32_t sum = 0;
if (d >= length)
- return d;
+ goto finish;
if (!mute && volume->values[channel] != PA_VOLUME_MUTED) {
unsigned i;
@@ -223,12 +253,12 @@ size_t pa_mix(
pa_volume_t cvolume = streams[i].volume.values[channel];
if (d >= streams[i].chunk.length)
- return d;
+ goto finish;
if (cvolume == PA_VOLUME_MUTED)
v = 0;
else {
- v = (int32_t) *((uint8_t*) streams[i].chunk.memblock->data + streams[i].chunk.index + d) - 0x80;
+ v = (int32_t) *((uint8_t*) streams[i].internal + streams[i].chunk.index + d) - 0x80;
if (cvolume != PA_VOLUME_NORM)
v = (int32_t) (v * pa_sw_volume_to_linear(cvolume));
@@ -240,9 +270,7 @@ size_t pa_mix(
if (volume->values[channel] != PA_VOLUME_NORM)
sum = (int32_t) (sum * pa_sw_volume_to_linear(volume->values[channel]));
- if (sum < -0x80) sum = -0x80;
- if (sum > 0x7F) sum = 0x7F;
-
+ sum = CLAMP(sum, -0x80, 0x7F);
}
*((uint8_t*) data) = (uint8_t) (sum + 0x80);
@@ -251,17 +279,18 @@ size_t pa_mix(
if (++channel >= spec->channels)
channel = 0;
}
+
+ break;
}
case PA_SAMPLE_FLOAT32NE: {
- size_t d;
unsigned channel = 0;
for (d = 0;; d += sizeof(float)) {
float sum = 0;
if (d >= length)
- return d;
+ goto finish;
if (!mute && volume->values[channel] != PA_VOLUME_MUTED) {
unsigned i;
@@ -271,12 +300,12 @@ size_t pa_mix(
pa_volume_t cvolume = streams[i].volume.values[channel];
if (d >= streams[i].chunk.length)
- return d;
+ goto finish;
if (cvolume == PA_VOLUME_MUTED)
v = 0;
else {
- v = *((float*) ((uint8_t*) streams[i].chunk.memblock->data + streams[i].chunk.index + d));
+ v = *((float*) ((uint8_t*) streams[i].internal + streams[i].chunk.index + d));
if (cvolume != PA_VOLUME_NORM)
v *= pa_sw_volume_to_linear(cvolume);
@@ -295,18 +324,35 @@ size_t pa_mix(
if (++channel >= spec->channels)
channel = 0;
}
+
+ break;
}
default:
pa_log_error("ERROR: Unable to mix audio data of format %s.", pa_sample_format_to_string(spec->format));
abort();
}
+
+finish:
+
+ for (k = 0; k < nstreams; k++)
+ pa_memblock_release(streams[k].chunk.memblock);
+
+ return d;
}
-void pa_volume_memchunk(pa_memchunk*c, const pa_sample_spec *spec, const pa_cvolume *volume) {
- assert(c && spec && (c->length % pa_frame_size(spec) == 0));
- assert(volume);
+void pa_volume_memchunk(
+ pa_memchunk*c,
+ const pa_sample_spec *spec,
+ const pa_cvolume *volume) {
+
+ void *ptr;
+
+ pa_assert(c);
+ pa_assert(spec);
+ pa_assert(c->length % pa_frame_size(spec) == 0);
+ pa_assert(volume);
if (pa_cvolume_channels_equal_to(volume, PA_VOLUME_NORM))
return;
@@ -316,24 +362,25 @@ void pa_volume_memchunk(pa_memchunk*c, const pa_sample_spec *spec, const pa_cvol
return;
}
+ ptr = pa_memblock_acquire(c->memblock);
+
switch (spec->format) {
+
case PA_SAMPLE_S16NE: {
int16_t *d;
size_t n;
unsigned channel;
- double linear[PA_CHANNELS_MAX];
+ int32_t linear[PA_CHANNELS_MAX];
for (channel = 0; channel < spec->channels; channel++)
- linear[channel] = pa_sw_volume_to_linear(volume->values[channel]);
-
- for (channel = 0, d = (int16_t*) ((uint8_t*) c->memblock->data+c->index), n = c->length/sizeof(int16_t); n > 0; d++, n--) {
- int32_t t = (int32_t)(*d);
+ linear[channel] = (int32_t) (pa_sw_volume_to_linear(volume->values[channel]) * 0x10000);
- t = (int32_t) (t * linear[channel]);
-
- if (t < -0x8000) t = -0x8000;
- if (t > 0x7FFF) t = 0x7FFF;
+ for (channel = 0, d = (int16_t*) ((uint8_t*) ptr + c->index), n = c->length/sizeof(int16_t); n > 0; d++, n--) {
+ int32_t t;
+ t = (int32_t)(*d);
+ t = (t * linear[channel]) / 0x10000;
+ t = CLAMP(t, -0x8000, 0x7FFF);
*d = (int16_t) t;
if (++channel >= spec->channels)
@@ -346,20 +393,18 @@ void pa_volume_memchunk(pa_memchunk*c, const pa_sample_spec *spec, const pa_cvol
int16_t *d;
size_t n;
unsigned channel;
- double linear[PA_CHANNELS_MAX];
+ int32_t linear[PA_CHANNELS_MAX];
for (channel = 0; channel < spec->channels; channel++)
- linear[channel] = pa_sw_volume_to_linear(volume->values[channel]);
-
- for (channel = 0, d = (int16_t*) ((uint8_t*) c->memblock->data+c->index), n = c->length/sizeof(int16_t); n > 0; d++, n--) {
- int32_t t = (int32_t)(INT16_SWAP(*d));
+ linear[channel] = (int32_t) (pa_sw_volume_to_linear(volume->values[channel]) * 0x10000);
- t = (int32_t) (t * linear[channel]);
+ for (channel = 0, d = (int16_t*) ((uint8_t*) ptr + c->index), n = c->length/sizeof(int16_t); n > 0; d++, n--) {
+ int32_t t;
- if (t < -0x8000) t = -0x8000;
- if (t > 0x7FFF) t = 0x7FFF;
-
- *d = INT16_SWAP((int16_t) t);
+ t = (int32_t)(PA_INT16_SWAP(*d));
+ t = (t * linear[channel]) / 0x10000;
+ t = CLAMP(t, -0x8000, 0x7FFF);
+ *d = PA_INT16_SWAP((int16_t) t);
if (++channel >= spec->channels)
channel = 0;
@@ -371,16 +416,18 @@ void pa_volume_memchunk(pa_memchunk*c, const pa_sample_spec *spec, const pa_cvol
case PA_SAMPLE_U8: {
uint8_t *d;
size_t n;
- unsigned channel = 0;
-
- for (d = (uint8_t*) c->memblock->data + c->index, n = c->length; n > 0; d++, n--) {
- int32_t t = (int32_t) *d - 0x80;
+ unsigned channel;
+ int32_t linear[PA_CHANNELS_MAX];
- t = (int32_t) (t * pa_sw_volume_to_linear(volume->values[channel]));
+ for (channel = 0; channel < spec->channels; channel++)
+ linear[channel] = (int32_t) (pa_sw_volume_to_linear(volume->values[channel]) * 0x10000);
- if (t < -0x80) t = -0x80;
- if (t > 0x7F) t = 0x7F;
+ for (channel = 0, d = (uint8_t*) ptr + c->index, n = c->length; n > 0; d++, n--) {
+ int32_t t;
+ t = (int32_t) *d - 0x80;
+ t = (t * linear[channel]) / 0x10000;
+ t = CLAMP(t, -0x80, 0x7F);
*d = (uint8_t) (t + 0x80);
if (++channel >= spec->channels)
@@ -395,7 +442,7 @@ void pa_volume_memchunk(pa_memchunk*c, const pa_sample_spec *spec, const pa_cvol
unsigned n;
unsigned channel;
- d = (float*) ((uint8_t*) c->memblock->data + c->index);
+ d = (float*) ((uint8_t*) ptr + c->index);
skip = spec->channels * sizeof(float);
n = c->length/sizeof(float)/spec->channels;
@@ -406,7 +453,6 @@ void pa_volume_memchunk(pa_memchunk*c, const pa_sample_spec *spec, const pa_cvol
continue;
v = (float) pa_sw_volume_to_linear(volume->values[channel]);
-
t = d + channel;
oil_scalarmult_f32(t, skip, t, skip, &v, n);
}
@@ -414,9 +460,85 @@ void pa_volume_memchunk(pa_memchunk*c, const pa_sample_spec *spec, const pa_cvol
}
default:
- pa_log_error("ERROR: Unable to change volume of format %s.",
- pa_sample_format_to_string(spec->format));
- abort();
+ pa_log_warn(" Unable to change volume of format %s.", pa_sample_format_to_string(spec->format));
+ /* If we cannot change the volume, we just don't do it */
}
+
+ pa_memblock_release(c->memblock);
+}
+
+size_t pa_frame_align(size_t l, const pa_sample_spec *ss) {
+ size_t fs;
+
+ pa_assert(ss);
+
+ fs = pa_frame_size(ss);
+
+ return (l/fs) * fs;
}
+int pa_frame_aligned(size_t l, const pa_sample_spec *ss) {
+ size_t fs;
+
+ pa_assert(ss);
+
+ fs = pa_frame_size(ss);
+
+ return l % fs == 0;
+}
+
+void pa_interleave(const void *src[], unsigned channels, void *dst, size_t ss, unsigned n) {
+ unsigned c;
+ size_t fs;
+
+ pa_assert(src);
+ pa_assert(channels > 0);
+ pa_assert(dst);
+ pa_assert(ss > 0);
+ pa_assert(n > 0);
+
+ fs = ss * channels;
+
+ for (c = 0; c < channels; c++) {
+ unsigned j;
+ void *d;
+ const void *s;
+
+ s = src[c];
+ d = (uint8_t*) dst + c * ss;
+
+ for (j = 0; j < n; j ++) {
+ oil_memcpy(d, s, ss);
+ s = (uint8_t*) s + ss;
+ d = (uint8_t*) d + fs;
+ }
+ }
+}
+
+void pa_deinterleave(const void *src, void *dst[], unsigned channels, size_t ss, unsigned n) {
+ size_t fs;
+ unsigned c;
+
+ pa_assert(src);
+ pa_assert(dst);
+ pa_assert(channels > 0);
+ pa_assert(ss > 0);
+ pa_assert(n > 0);
+
+ fs = ss * channels;
+
+ for (c = 0; c < channels; c++) {
+ unsigned j;
+ const void *s;
+ void *d;
+
+ s = (uint8_t*) src + c * ss;
+ d = dst[c];
+
+ for (j = 0; j < n; j ++) {
+ oil_memcpy(d, s, ss);
+ s = (uint8_t*) s + fs;
+ d = (uint8_t*) d + ss;
+ }
+ }
+}
diff --git a/src/pulsecore/sample-util.h b/src/pulsecore/sample-util.h
index 3ff065ab..0a39d5ca 100644
--- a/src/pulsecore/sample-util.h
+++ b/src/pulsecore/sample-util.h
@@ -39,10 +39,11 @@ typedef struct pa_mix_info {
pa_memchunk chunk;
pa_cvolume volume;
void *userdata;
+ void *internal; /* Used internally by pa_mix(), should not be initialised when calling pa_mix() */
} pa_mix_info;
size_t pa_mix(
- const pa_mix_info channels[],
+ pa_mix_info channels[],
unsigned nchannels,
void *data,
size_t length,
@@ -55,4 +56,11 @@ void pa_volume_memchunk(
const pa_sample_spec *spec,
const pa_cvolume *volume);
+size_t pa_frame_align(size_t l, const pa_sample_spec *ss) PA_GCC_PURE;
+
+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);
+
#endif
diff --git a/src/pulsecore/sconv-s16be.c b/src/pulsecore/sconv-s16be.c
index c530e79b..f74d0282 100644
--- a/src/pulsecore/sconv-s16be.c
+++ b/src/pulsecore/sconv-s16be.c
@@ -27,12 +27,15 @@
#include "endianmacros.h"
-#define INT16_FROM INT16_FROM_BE
-#define INT16_TO INT16_TO_BE
+#define INT16_FROM PA_INT16_FROM_BE
+#define INT16_TO PA_INT16_TO_BE
#define pa_sconv_s16le_to_float32ne pa_sconv_s16be_to_float32ne
#define pa_sconv_s16le_from_float32ne pa_sconv_s16be_from_float32ne
+#define pa_sconv_s16le_to_float32re pa_sconv_s16be_to_float32re
+#define pa_sconv_s16le_from_float32re pa_sconv_s16be_from_float32re
+
#ifdef WORDS_BIGENDIAN
#define SWAP_WORDS 0
#else
diff --git a/src/pulsecore/sconv-s16be.h b/src/pulsecore/sconv-s16be.h
index 6b736f69..ad034489 100644
--- a/src/pulsecore/sconv-s16be.h
+++ b/src/pulsecore/sconv-s16be.h
@@ -24,7 +24,18 @@
USA.
***/
-void pa_sconv_s16be_to_float32ne(unsigned n, const void *a, float *b);
-void pa_sconv_s16be_from_float32ne(unsigned n, const float *a, void *b);
+#include <inttypes.h>
+
+void pa_sconv_s16be_to_float32ne(unsigned n, const int16_t *a, float *b);
+void pa_sconv_s16be_from_float32ne(unsigned n, const float *a, int16_t *b);
+void pa_sconv_s16be_to_float32re(unsigned n, const int16_t *a, float *b);
+void pa_sconv_s16be_from_float32re(unsigned n, const float *a, int16_t *b);
+
+#ifdef WORDS_BIGENDIAN
+#define pa_sconv_float32be_to_s16ne pa_sconv_s16be_from_float32ne
+#define pa_sconv_float32be_from_s16ne pa_sconv_s16be_to_float32ne
+#define pa_sconv_float32le_to_s16ne pa_sconv_s16be_from_float32re
+#define pa_sconv_float32le_from_s16ne pa_sconv_s16be_to_float32re
+#endif
#endif
diff --git a/src/pulsecore/sconv-s16le.c b/src/pulsecore/sconv-s16le.c
index 5f45ef66..6925052c 100644
--- a/src/pulsecore/sconv-s16le.c
+++ b/src/pulsecore/sconv-s16le.c
@@ -25,12 +25,12 @@
#include <config.h>
#endif
-#include <assert.h>
#include <inttypes.h>
#include <liboil/liboilfuncs.h>
#include <pulsecore/sconv.h>
+#include <pulsecore/macro.h>
#include <pulsecore/log.h>
#include "endianmacros.h"
@@ -38,11 +38,11 @@
#include "sconv-s16le.h"
#ifndef INT16_FROM
-#define INT16_FROM INT16_FROM_LE
+#define INT16_FROM PA_INT16_FROM_LE
#endif
#ifndef INT16_TO
-#define INT16_TO INT16_TO_LE
+#define INT16_TO PA_INT16_TO_LE
#endif
#ifndef SWAP_WORDS
@@ -53,32 +53,28 @@
#endif
#endif
-void pa_sconv_s16le_to_float32ne(unsigned n, const void *a, float *b) {
- const int16_t *ca = a;
-
- assert(a);
- assert(b);
+void pa_sconv_s16le_to_float32ne(unsigned n, const int16_t *a, float *b) {
+ pa_assert(a);
+ pa_assert(b);
#if SWAP_WORDS == 1
for (; n > 0; n--) {
- int16_t s = *(ca++);
+ int16_t s = *(a++);
*(b++) = ((float) INT16_FROM(s))/0x7FFF;
}
#else
{
static const double add = 0, factor = 1.0/0x7FFF;
- oil_scaleconv_f32_s16(b, ca, n, &add, &factor);
+ oil_scaleconv_f32_s16(b, a, n, &add, &factor);
}
#endif
}
-void pa_sconv_s16le_from_float32ne(unsigned n, const float *a, void *b) {
- int16_t *cb = b;
-
- assert(a);
- assert(b);
+void pa_sconv_s16le_from_float32ne(unsigned n, const float *a, int16_t *b) {
+ pa_assert(a);
+ pa_assert(b);
#if SWAP_WORDS == 1
@@ -86,20 +82,43 @@ void pa_sconv_s16le_from_float32ne(unsigned n, const float *a, void *b) {
int16_t s;
float v = *(a++);
- if (v > 1)
- v = 1;
-
- if (v < -1)
- v = -1;
-
+ v = CLAMP(v, -1, 1);
s = (int16_t) (v * 0x7FFF);
- *(cb++) = INT16_TO(s);
+ *(b++) = INT16_TO(s);
}
#else
{
static const double add = 0, factor = 0x7FFF;
- oil_scaleconv_s16_f32(cb, a, n, &add, &factor);
+ oil_scaleconv_s16_f32(b, a, n, &add, &factor);
}
#endif
}
+
+void pa_sconv_s16le_to_float32re(unsigned n, const int16_t *a, float *b) {
+ pa_assert(a);
+ pa_assert(b);
+
+ 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);
+ *(b++) = k;
+ }
+}
+
+void pa_sconv_s16le_from_float32re(unsigned n, const float *a, int16_t *b) {
+ pa_assert(a);
+ pa_assert(b);
+
+ for (; n > 0; n--) {
+ int16_t s;
+ float v = *(a++);
+ uint32_t *j = (uint32_t*) &v;
+ *j = PA_UINT32_SWAP(*j);
+ v = CLAMP(v, -1, 1);
+ s = (int16_t) (v * 0x7FFF);
+ *(b++) = INT16_TO(s);
+ }
+}
diff --git a/src/pulsecore/sconv-s16le.h b/src/pulsecore/sconv-s16le.h
index c4e4911a..4203315a 100644
--- a/src/pulsecore/sconv-s16le.h
+++ b/src/pulsecore/sconv-s16le.h
@@ -24,7 +24,18 @@
USA.
***/
-void pa_sconv_s16le_to_float32ne(unsigned n, const void *a, float *b);
-void pa_sconv_s16le_from_float32ne(unsigned n, const float *a, void *b);
+#include <inttypes.h>
+
+void pa_sconv_s16le_to_float32ne(unsigned n, const int16_t *a, float *b);
+void pa_sconv_s16le_from_float32ne(unsigned n, const float *a, int16_t *b);
+void pa_sconv_s16le_to_float32re(unsigned n, const int16_t *a, float *b);
+void pa_sconv_s16le_from_float32re(unsigned n, const float *a, int16_t *b);
+
+#ifndef WORDS_BIGENDIAN
+#define pa_sconv_float32be_to_s16ne pa_sconv_s16le_from_float32re
+#define pa_sconv_float32be_from_s16ne pa_sconv_s16le_to_float32re
+#define pa_sconv_float32le_to_s16ne pa_sconv_s16le_from_float32ne
+#define pa_sconv_float32le_from_s16ne pa_sconv_s16le_to_float32ne
+#endif
#endif
diff --git a/src/pulsecore/sconv.c b/src/pulsecore/sconv.c
index d15cec84..7f5da63f 100644
--- a/src/pulsecore/sconv.c
+++ b/src/pulsecore/sconv.c
@@ -28,12 +28,12 @@
#include <stdio.h>
#include <stdlib.h>
-#include <assert.h>
#include <liboil/liboilfuncs.h>
#include <liboil/liboil.h>
#include <pulsecore/g711.h>
+#include <pulsecore/macro.h>
#include "endianmacros.h"
#include "sconv-s16le.h"
@@ -41,152 +41,223 @@
#include "sconv.h"
-static void u8_to_float32ne(unsigned n, const void *a, float *b) {
- const uint8_t *ca = a;
- static const double add = -128.0/127.0, factor = 1.0/127.0;
+/* u8 */
+static void u8_to_float32ne(unsigned n, const uint8_t *a, float *b) {
+ static const double add = -1, factor = 1.0/128.0;
- assert(a);
- assert(b);
+ pa_assert(a);
+ pa_assert(b);
- oil_scaleconv_f32_u8(b, ca, n, &add, &factor);
+ oil_scaleconv_f32_u8(b, a, n, &add, &factor);
}
-static void u8_from_float32ne(unsigned n, const float *a, void *b) {
- uint8_t *cb = b;
- static const double add = 128.0, factor = 127.0;
+static void u8_from_float32ne(unsigned n, const float *a, uint8_t *b) {
+ static const double add = 128, factor = 127.0;
- assert(a);
- assert(b);
+ pa_assert(a);
+ pa_assert(b);
- oil_scaleconv_u8_f32(cb, a, n, &add, &factor);
+ oil_scaleconv_u8_f32(b, a, n, &add, &factor);
}
-static void float32ne_to_float32ne(unsigned n, const void *a, float *b) {
- assert(a);
- assert(b);
+static void u8_to_s16ne(unsigned n, const uint8_t *a, int16_t *b) {
+ static const int16_t add = -0x80, factor = 0x100;
- oil_memcpy(b, a, sizeof(float) * n);
+ 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);
+}
+
+static void u8_from_s16ne(unsigned n, const int16_t *a, uint8_t *b) {
+
+ pa_assert(a);
+ pa_assert(b);
+
+ for (; n > 0; n--, a++, b++)
+ *b = (uint8_t) (*a / 0x100 + 0x80);
}
-static void float32ne_from_float32ne(unsigned n, const float *a, void *b) {
- assert(a);
- assert(b);
+/* float32 */
+
+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);
}
-static void float32re_to_float32ne(unsigned n, const void *a, float *b) {
- assert(a);
- assert(b);
+static void float32re_to_float32ne(unsigned n, const float *a, float *b) {
+ pa_assert(a);
+ pa_assert(b);
+
+ for (; n > 0; n--, a++, b++)
+ *((uint32_t *) b) = PA_UINT32_SWAP(*((uint32_t *) a));
+}
+
+/* s16 */
- while (n-- > 0)
- ((uint32_t *)b)[n] = UINT32_SWAP (((uint32_t *)a)[n]);
+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);
}
-static void float32re_from_float32ne(unsigned n, const float *a, void *b) {
- assert(a);
- assert(b);
+static void s16re_to_s16ne(unsigned n, const int16_t *a, int16_t *b) {
+ pa_assert(a);
+ pa_assert(b);
- while (n-- > 0)
- ((uint32_t *)b)[n] = UINT32_SWAP (((uint32_t *)a)[n]);
+ for (; n > 0; n--, a++, b++)
+ *b = PA_UINT16_SWAP(*a);
}
-static void ulaw_to_float32ne(unsigned n, const void *a, float *b) {
- const uint8_t *ca = a;
+/* ulaw */
- assert(a);
- assert(b);
+static void ulaw_to_float32ne(unsigned n, const uint8_t *a, float *b) {
+ pa_assert(a);
+ pa_assert(b);
for (; n > 0; n--)
- *(b++) = st_ulaw2linear16(*(ca++)) * 1.0F / 0x7FFF;
+ *(b++) = (float) st_ulaw2linear16(*(a++)) / 0x8000;
}
-static void ulaw_from_float32ne(unsigned n, const float *a, void *b) {
- uint8_t *cb = b;
-
- assert(a);
- assert(b);
+static void ulaw_from_float32ne(unsigned n, const float *a, uint8_t *b) {
+ pa_assert(a);
+ pa_assert(b);
for (; n > 0; n--) {
float v = *(a++);
+ v = CLAMP(v, -1, 1);
+ v *= 0x1FFF;
+ *(b++) = st_14linear2ulaw((int16_t) v);
+ }
+}
+
+static void ulaw_to_s16ne(unsigned n, const uint8_t *a, int16_t *b) {
+ pa_assert(a);
+ pa_assert(b);
+
+ for (; n > 0; n--, a++, b++)
+ *b = st_ulaw2linear16(*a);
+}
+
+static void ulaw_from_s16ne(unsigned n, const int16_t *a, uint8_t *b) {
+ pa_assert(a);
+ pa_assert(b);
+
+ for (; n > 0; n--, a++, b++)
+ *b = st_14linear2ulaw(*a >> 2);
+}
- if (v > 1)
- v = 1;
+/* alaw */
- if (v < -1)
- v = -1;
+static void alaw_to_float32ne(unsigned n, const uint8_t *a, float *b) {
+ pa_assert(a);
+ pa_assert(b);
- *(cb++) = st_14linear2ulaw((int16_t) (v * 0x1FFF));
+ for (; n > 0; n--, a++, b++)
+ *b = (float) st_alaw2linear16(*a) / 0x8000;
+}
+
+static void alaw_from_float32ne(unsigned n, const float *a, uint8_t *b) {
+ pa_assert(a);
+ pa_assert(b);
+
+ for (; n > 0; n--, a++, b++) {
+ float v = *a;
+ v = CLAMP(v, -1, 1);
+ v *= 0xFFF;
+ *b = st_13linear2alaw((int16_t) v);
}
}
-static void alaw_to_float32ne(unsigned n, const void *a, float *b) {
- const uint8_t *ca = a;
+static void alaw_to_s16ne(unsigned n, const int8_t *a, int16_t *b) {
+ pa_assert(a);
+ pa_assert(b);
- assert(a);
- assert(b);
+ for (; n > 0; n--, a++, b++)
+ *b = st_alaw2linear16(*a);
+}
- for (; n > 0; n--)
- *(b++) = st_alaw2linear16(*(ca++)) * 1.0F / 0x7FFF;
+static void alaw_from_s16ne(unsigned n, const int16_t *a, uint8_t *b) {
+ pa_assert(a);
+ pa_assert(b);
+
+ for (; n > 0; n--, a++, b++)
+ *b = st_13linear2alaw(*a >> 3);
}
-static void alaw_from_float32ne(unsigned n, const float *a, void *b) {
- uint8_t *cb = b;
+pa_convert_func_t pa_get_convert_to_float32ne_function(pa_sample_format_t f) {
- assert(a);
- assert(b);
+ static const pa_convert_func_t table[] = {
+ [PA_SAMPLE_U8] = (pa_convert_func_t) u8_to_float32ne,
+ [PA_SAMPLE_ALAW] = (pa_convert_func_t) alaw_to_float32ne,
+ [PA_SAMPLE_ULAW] = (pa_convert_func_t) ulaw_to_float32ne,
+ [PA_SAMPLE_S16LE] = (pa_convert_func_t) pa_sconv_s16le_to_float32ne,
+ [PA_SAMPLE_S16BE] = (pa_convert_func_t) pa_sconv_s16be_to_float32ne,
+ [PA_SAMPLE_FLOAT32NE] = (pa_convert_func_t) float32ne_to_float32ne,
+ [PA_SAMPLE_FLOAT32RE] = (pa_convert_func_t) float32re_to_float32ne,
+ };
- for (; n > 0; n--) {
- float v = *(a++);
+ pa_assert(f >= 0);
+ pa_assert(f < PA_SAMPLE_MAX);
- if (v > 1)
- v = 1;
+ return table[f];
+}
- if (v < -1)
- v = -1;
+pa_convert_func_t pa_get_convert_from_float32ne_function(pa_sample_format_t f) {
- *(cb++) = st_13linear2alaw((int16_t) (v * 0xFFF));
- }
+ static const pa_convert_func_t table[] = {
+ [PA_SAMPLE_U8] = (pa_convert_func_t) u8_from_float32ne,
+ [PA_SAMPLE_S16LE] = (pa_convert_func_t) pa_sconv_s16le_from_float32ne,
+ [PA_SAMPLE_S16BE] = (pa_convert_func_t) pa_sconv_s16be_from_float32ne,
+ [PA_SAMPLE_FLOAT32NE] = (pa_convert_func_t) float32ne_to_float32ne,
+ [PA_SAMPLE_FLOAT32RE] = (pa_convert_func_t) float32re_to_float32ne,
+ [PA_SAMPLE_ALAW] = (pa_convert_func_t) alaw_from_float32ne,
+ [PA_SAMPLE_ULAW] = (pa_convert_func_t) ulaw_from_float32ne
+ };
+
+ pa_assert(f >= 0);
+ pa_assert(f < PA_SAMPLE_MAX);
+
+ return table[f];
}
-pa_convert_to_float32ne_func_t pa_get_convert_to_float32ne_function(pa_sample_format_t f) {
- switch(f) {
- case PA_SAMPLE_U8:
- return u8_to_float32ne;
- case PA_SAMPLE_S16LE:
- return pa_sconv_s16le_to_float32ne;
- case PA_SAMPLE_S16BE:
- return pa_sconv_s16be_to_float32ne;
- case PA_SAMPLE_FLOAT32NE:
- return float32ne_to_float32ne;
- case PA_SAMPLE_FLOAT32RE:
- return float32re_to_float32ne;
- case PA_SAMPLE_ALAW:
- return alaw_to_float32ne;
- case PA_SAMPLE_ULAW:
- return ulaw_to_float32ne;
- default:
- return NULL;
- }
+pa_convert_func_t pa_get_convert_to_s16ne_function(pa_sample_format_t f) {
+
+ static const pa_convert_func_t table[] = {
+ [PA_SAMPLE_U8] = (pa_convert_func_t) u8_to_s16ne,
+ [PA_SAMPLE_S16NE] = (pa_convert_func_t) s16ne_to_s16ne,
+ [PA_SAMPLE_S16RE] = (pa_convert_func_t) s16re_to_s16ne,
+ [PA_SAMPLE_FLOAT32BE] = (pa_convert_func_t) pa_sconv_float32be_to_s16ne,
+ [PA_SAMPLE_FLOAT32LE] = (pa_convert_func_t) pa_sconv_float32le_to_s16ne,
+ [PA_SAMPLE_ALAW] = (pa_convert_func_t) alaw_to_s16ne,
+ [PA_SAMPLE_ULAW] = (pa_convert_func_t) ulaw_to_s16ne
+ };
+
+ pa_assert(f >= 0);
+ pa_assert(f < PA_SAMPLE_MAX);
+
+ return table[f];
}
-pa_convert_from_float32ne_func_t pa_get_convert_from_float32ne_function(pa_sample_format_t f) {
- switch(f) {
- case PA_SAMPLE_U8:
- return u8_from_float32ne;
- case PA_SAMPLE_S16LE:
- return pa_sconv_s16le_from_float32ne;
- case PA_SAMPLE_S16BE:
- return pa_sconv_s16be_from_float32ne;
- case PA_SAMPLE_FLOAT32NE:
- return float32ne_from_float32ne;
- case PA_SAMPLE_FLOAT32RE:
- return float32re_from_float32ne;
- case PA_SAMPLE_ALAW:
- return alaw_from_float32ne;
- case PA_SAMPLE_ULAW:
- return ulaw_from_float32ne;
- default:
- return NULL;
- }
+pa_convert_func_t pa_get_convert_from_s16ne_function(pa_sample_format_t f) {
+
+ static const pa_convert_func_t table[] = {
+ [PA_SAMPLE_U8] = (pa_convert_func_t) u8_from_s16ne,
+ [PA_SAMPLE_S16NE] = (pa_convert_func_t) s16ne_to_s16ne,
+ [PA_SAMPLE_S16RE] = (pa_convert_func_t) s16re_to_s16ne,
+ [PA_SAMPLE_FLOAT32BE] = (pa_convert_func_t) pa_sconv_float32be_from_s16ne,
+ [PA_SAMPLE_FLOAT32LE] = (pa_convert_func_t) pa_sconv_float32le_from_s16ne,
+ [PA_SAMPLE_ALAW] = (pa_convert_func_t) alaw_from_s16ne,
+ [PA_SAMPLE_ULAW] = (pa_convert_func_t) ulaw_from_s16ne,
+ };
+
+ pa_assert(f >= 0);
+ pa_assert(f < PA_SAMPLE_MAX);
+
+ return table[f];
}
diff --git a/src/pulsecore/sconv.h b/src/pulsecore/sconv.h
index 1e97aad9..901f50a3 100644
--- a/src/pulsecore/sconv.h
+++ b/src/pulsecore/sconv.h
@@ -27,10 +27,12 @@
#include <pulse/sample.h>
-typedef void (*pa_convert_to_float32ne_func_t)(unsigned n, const void *a, float *b);
-typedef void (*pa_convert_from_float32ne_func_t)(unsigned n, const float *a, void *b);
+typedef void (*pa_convert_func_t)(unsigned n, const void *a, void *b);
-pa_convert_to_float32ne_func_t pa_get_convert_to_float32ne_function(pa_sample_format_t f);
-pa_convert_from_float32ne_func_t pa_get_convert_from_float32ne_function(pa_sample_format_t f);
+pa_convert_func_t pa_get_convert_to_float32ne_function(pa_sample_format_t f) PA_GCC_PURE;
+pa_convert_func_t pa_get_convert_from_float32ne_function(pa_sample_format_t f) PA_GCC_PURE;
+
+pa_convert_func_t pa_get_convert_to_s16ne_function(pa_sample_format_t f) PA_GCC_PURE;
+pa_convert_func_t pa_get_convert_from_s16ne_function(pa_sample_format_t f) PA_GCC_PURE;
#endif
diff --git a/src/pulsecore/semaphore-posix.c b/src/pulsecore/semaphore-posix.c
new file mode 100644
index 00000000..750c2afc
--- /dev/null
+++ b/src/pulsecore/semaphore-posix.c
@@ -0,0 +1,69 @@
+/* $Id$ */
+
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2006 Lennart Poettering
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2 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 <errno.h>
+#include <pthread.h>
+#include <semaphore.h>
+
+#include <pulse/xmalloc.h>
+#include <pulsecore/macro.h>
+
+#include "semaphore.h"
+
+struct pa_semaphore {
+ sem_t sem;
+};
+
+pa_semaphore* pa_semaphore_new(unsigned value) {
+ pa_semaphore *s;
+
+ s = pa_xnew(pa_semaphore, 1);
+ pa_assert_se(sem_init(&s->sem, 0, value) == 0);
+ return s;
+}
+
+void pa_semaphore_free(pa_semaphore *s) {
+ pa_assert(s);
+ pa_assert_se(sem_destroy(&s->sem) == 0);
+ pa_xfree(s);
+}
+
+void pa_semaphore_post(pa_semaphore *s) {
+ pa_assert(s);
+ pa_assert_se(sem_post(&s->sem) == 0);
+}
+
+void pa_semaphore_wait(pa_semaphore *s) {
+ int ret;
+ pa_assert(s);
+
+ do {
+ ret = sem_wait(&s->sem);
+ } while (ret < 0 && errno == EINTR);
+
+ pa_assert(ret == 0);
+}
diff --git a/src/pulsecore/semaphore-win32.c b/src/pulsecore/semaphore-win32.c
new file mode 100644
index 00000000..f6576348
--- /dev/null
+++ b/src/pulsecore/semaphore-win32.c
@@ -0,0 +1,65 @@
+/* $Id: mutex-win32.c 1426 2007-02-13 15:35:19Z ossman $ */
+
+/***
+ This file is part of PulseAudio.
+
+ 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.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <windows.h>
+
+#include <pulse/xmalloc.h>
+#include <pulsecore/macro.h>
+
+#include "semaphore.h"
+
+struct pa_semaphore
+{
+ HANDLE sema;
+};
+
+pa_semaphore* pa_semaphore_new(unsigned value) {
+ pa_semaphore *s;
+
+ s = pa_xnew(pa_semaphore, 1);
+
+ s->sema = CreateSemaphore(NULL, value, 32767, NULL);
+ pa_assert(s->sema != NULL);
+
+ return s;
+}
+
+void pa_semaphore_free(pa_semaphore *s) {
+ pa_assert(s);
+ CloseHandle(s->sema);
+ pa_xfree(s);
+}
+
+void pa_semaphore_post(pa_semaphore *s) {
+ pa_assert(s);
+ ReleaseSemaphore(s->sema, 1, NULL);
+}
+
+void pa_semaphore_wait(pa_semaphore *s) {
+ pa_assert(s);
+ WaitForSingleObject(s->sema, INFINITE);
+}
diff --git a/src/pulsecore/anotify.h b/src/pulsecore/semaphore.h
index b3f75b7e..c394e0f2 100644
--- a/src/pulsecore/anotify.h
+++ b/src/pulsecore/semaphore.h
@@ -1,5 +1,5 @@
-#ifndef foopulseanotifyhfoo
-#define foopulseanotifyhfoo
+#ifndef foopulsesemaphorehfoo
+#define foopulsesemaphorehfoo
/* $Id$ */
@@ -24,17 +24,12 @@
USA.
***/
-/* Asynchronous thread-safe notification of mainloops */
+typedef struct pa_semaphore pa_semaphore;
+pa_semaphore* pa_semaphore_new(unsigned value);
+void pa_semaphore_free(pa_semaphore *m);
-#include <inttypes.h>
-#include <pulse/mainloop-api.h>
-
-typedef struct pa_anotify pa_anotify;
-typedef void (*pa_anotify_cb_t)(uint8_t event, void *userdata);
-
-pa_anotify *pa_anotify_new(pa_mainloop_api*api, pa_anotify_cb_t cb, void *userdata);
-void pa_anotify_free(pa_anotify *a);
-int pa_anotify_signal(pa_anotify *a, uint8_t event);
+void pa_semaphore_post(pa_semaphore *m);
+void pa_semaphore_wait(pa_semaphore *m);
#endif
diff --git a/src/pulsecore/shm.c b/src/pulsecore/shm.c
index 444d4010..6882e7f8 100644
--- a/src/pulsecore/shm.c
+++ b/src/pulsecore/shm.c
@@ -1,32 +1,31 @@
/* $Id$ */
/***
- This file is part of PulseAudio.
+ This file is part of PulseAudio.
- Copyright 2006 Lennart Poettering
- Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+ Copyright 2006 Lennart Poettering
+ Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
- PulseAudio is free software; you can redistribute it and/or modify
- it under the terms of the GNU Lesser General Public License as
- published by the Free Software Foundation; either version 2.1 of the
- License, or (at your option) any later version.
+ PulseAudio is 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.
+ 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.
+ 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 <assert.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
@@ -34,15 +33,22 @@
#include <errno.h>
#include <string.h>
#include <sys/stat.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <signal.h>
#ifdef HAVE_SYS_MMAN_H
#include <sys/mman.h>
#endif
+#include <pulse/xmalloc.h>
+
#include <pulsecore/core-error.h>
#include <pulsecore/log.h>
#include <pulsecore/random.h>
-#include <pulse/xmalloc.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/atomic.h>
#include "shm.h"
@@ -50,10 +56,31 @@
#define MADV_REMOVE 9
#endif
-#define MAX_SHM_SIZE (1024*1024*20)
+#define MAX_SHM_SIZE (PA_ALIGN(1024*1024*20))
+
+#ifdef __linux__
+/* On Linux we know that the shared memory blocks are files in
+ * /dev/shm. We can use that information to list all blocks and
+ * cleanup unused ones */
+#define SHM_PATH "/dev/shm/"
+#else
+#undef SHM_PATH
+#endif
+
+#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 {
+ pa_atomic_t marker; /* 0xbeefcafe */
+ pa_atomic_t pid;
+ void *_reserverd1;
+ void *_reserverd2;
+ void *_reserverd3;
+ void *_reserverd4;
+};
static char *segment_name(char *fn, size_t l, unsigned id) {
- snprintf(fn, l, "/pulse-shm-%u", id);
+ pa_snprintf(fn, l, "/pulse-shm-%u", id);
return fn;
}
@@ -61,10 +88,17 @@ int pa_shm_create_rw(pa_shm *m, size_t size, int shared, mode_t mode) {
char fn[32];
int fd = -1;
- assert(m);
- assert(size > 0);
- assert(size < MAX_SHM_SIZE);
- assert(mode >= 0600);
+ pa_assert(m);
+ pa_assert(size > 0);
+ pa_assert(size < MAX_SHM_SIZE);
+ pa_assert(mode >= 0600);
+
+ /* Each time we create a new SHM area, let's first drop all stale
+ * ones */
+ pa_shm_cleanup();
+
+ /* Round up to make it aligned */
+ size = PA_ALIGN(size);
if (!shared) {
m->id = 0;
@@ -79,7 +113,7 @@ int pa_shm_create_rw(pa_shm *m, size_t size, int shared, mode_t mode) {
{
int r;
- if ((r = posix_memalign(&m->ptr, sysconf(_SC_PAGESIZE), size)) < 0) {
+ if ((r = posix_memalign(&m->ptr, PA_PAGE_SIZE, size)) < 0) {
pa_log("posix_memalign() failed: %s", pa_cstrerror(r));
goto fail;
}
@@ -92,6 +126,8 @@ int pa_shm_create_rw(pa_shm *m, size_t size, int shared, mode_t mode) {
} else {
#ifdef HAVE_SHM_OPEN
+ struct shm_marker *marker;
+
pa_random(&m->id, sizeof(m->id));
segment_name(fn, sizeof(fn), m->id);
@@ -100,7 +136,9 @@ int pa_shm_create_rw(pa_shm *m, size_t size, int shared, mode_t mode) {
goto fail;
}
- if (ftruncate(fd, m->size = size) < 0) {
+ m->size = size + PA_ALIGN(sizeof(struct shm_marker));
+
+ if (ftruncate(fd, m->size) < 0) {
pa_log("ftruncate() failed: %s", pa_cstrerror(errno));
goto fail;
}
@@ -110,10 +148,16 @@ int pa_shm_create_rw(pa_shm *m, size_t size, int shared, mode_t mode) {
goto fail;
}
- close(fd);
+ /* We store our PID at the end of the shm block, so that we
+ * can check for dead shm segments later */
+ marker = (struct shm_marker*) ((uint8_t*) m->ptr + m->size - PA_ALIGN(sizeof(struct shm_marker)));
+ pa_atomic_store(&marker->pid, (int) getpid());
+ pa_atomic_store(&marker->marker, SHM_MARKER);
+
+ pa_assert_se(close(fd) == 0);
m->do_unlink = 1;
#else
- return -1;
+ return -1;
#endif
}
@@ -126,7 +170,7 @@ fail:
#ifdef HAVE_SHM_OPEN
if (fd >= 0) {
shm_unlink(fn);
- close(fd);
+ pa_close(fd);
}
#endif
@@ -134,76 +178,70 @@ fail:
}
void pa_shm_free(pa_shm *m) {
- assert(m);
- assert(m->ptr);
- assert(m->size > 0);
+ pa_assert(m);
+ pa_assert(m->ptr);
+ pa_assert(m->size > 0);
#ifdef MAP_FAILED
- assert(m->ptr != MAP_FAILED);
+ pa_assert(m->ptr != MAP_FAILED);
#endif
- if (!m->shared) {
+ if (!m->shared) {
#ifdef MAP_ANONYMOUS
- if (munmap(m->ptr, m->size) < 0)
- pa_log("munmap() failed: %s", pa_cstrerror(errno));
+ if (munmap(m->ptr, m->size) < 0)
+ pa_log("munmap() failed: %s", pa_cstrerror(errno));
#elif defined(HAVE_POSIX_MEMALIGN)
free(m->ptr);
#else
pa_xfree(m->ptr);
#endif
- } else {
+ } else {
#ifdef HAVE_SHM_OPEN
- if (munmap(m->ptr, m->size) < 0)
- pa_log("munmap() failed: %s", pa_cstrerror(errno));
+ if (munmap(m->ptr, m->size) < 0)
+ pa_log("munmap() failed: %s", pa_cstrerror(errno));
- if (m->do_unlink) {
- char fn[32];
+ if (m->do_unlink) {
+ char fn[32];
- segment_name(fn, sizeof(fn), m->id);
+ segment_name(fn, sizeof(fn), m->id);
- if (shm_unlink(fn) < 0)
- pa_log(" shm_unlink(%s) failed: %s", fn, pa_cstrerror(errno));
- }
+ if (shm_unlink(fn) < 0)
+ pa_log(" shm_unlink(%s) failed: %s", fn, pa_cstrerror(errno));
+ }
#else
- /* We shouldn't be here without shm support */
- assert(0);
+ /* We shouldn't be here without shm support */
+ pa_assert_not_reached();
#endif
- }
+ }
memset(m, 0, sizeof(*m));
}
void pa_shm_punch(pa_shm *m, size_t offset, size_t size) {
void *ptr;
+ size_t o, ps;
- assert(m);
- assert(m->ptr);
- assert(m->size > 0);
- assert(offset+size <= m->size);
+ pa_assert(m);
+ pa_assert(m->ptr);
+ pa_assert(m->size > 0);
+ pa_assert(offset+size <= m->size);
#ifdef MAP_FAILED
- assert(m->ptr != MAP_FAILED);
+ pa_assert(m->ptr != MAP_FAILED);
#endif
/* You're welcome to implement this as NOOP on systems that don't
* support it */
+ /* Align this to multiples of the page size */
ptr = (uint8_t*) m->ptr + offset;
-
-#ifdef __linux__
-{
- /* On Linux ptr must be page aligned */
- long psz = sysconf(_SC_PAGESIZE);
- unsigned o;
-
- o = ((unsigned long) ptr) - ((((unsigned long) ptr)/psz) * psz);
+ o = (uint8_t*) ptr - (uint8_t*) PA_PAGE_ALIGN_PTR(ptr);
if (o > 0) {
- ptr = (uint8_t*) ptr + (psz - o);
- size -= psz - o;
+ ps = PA_PAGE_SIZE;
+ ptr = (uint8_t*) ptr + (ps - o);
+ size -= ps - o;
}
-}
-#endif
#ifdef MADV_REMOVE
if (madvise(ptr, size, MADV_REMOVE) >= 0)
@@ -216,7 +254,9 @@ void pa_shm_punch(pa_shm *m, size_t offset, size_t size) {
#endif
#ifdef MADV_DONTNEED
- madvise(ptr, size, MADV_DONTNEED);
+ pa_assert_se(madvise(ptr, size, MADV_DONTNEED) == 0);
+#elif defined(POSIX_MADV_DONTNEED)
+ pa_assert_se(posix_madvise(ptr, size, POSIX_MADV_DONTNEED) == 0);
#endif
}
@@ -227,12 +267,13 @@ int pa_shm_attach_ro(pa_shm *m, unsigned id) {
int fd = -1;
struct stat st;
- assert(m);
+ pa_assert(m);
segment_name(fn, sizeof(fn), m->id = id);
if ((fd = shm_open(fn, O_RDONLY, 0)) < 0) {
- pa_log("shm_open() failed: %s", pa_cstrerror(errno));
+ if (errno != EACCES)
+ pa_log("shm_open() failed: %s", pa_cstrerror(errno));
goto fail;
}
@@ -241,7 +282,7 @@ int pa_shm_attach_ro(pa_shm *m, unsigned id) {
goto fail;
}
- if (st.st_size <= 0 || st.st_size > MAX_SHM_SIZE) {
+ 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) {
pa_log("Invalid shared memory segment size");
goto fail;
}
@@ -256,13 +297,13 @@ int pa_shm_attach_ro(pa_shm *m, unsigned id) {
m->do_unlink = 0;
m->shared = 1;
- close(fd);
+ pa_assert_se(pa_close(fd) == 0);
return 0;
fail:
if (fd >= 0)
- close(fd);
+ pa_close(fd);
return -1;
}
@@ -270,7 +311,71 @@ fail:
#else /* HAVE_SHM_OPEN */
int pa_shm_attach_ro(pa_shm *m, unsigned id) {
- return -1;
+ return -1;
}
#endif /* HAVE_SHM_OPEN */
+
+int pa_shm_cleanup(void) {
+
+#ifdef SHM_PATH
+ DIR *d;
+ struct dirent *de;
+
+ if (!(d = opendir(SHM_PATH))) {
+ pa_log_warn("Failed to read "SHM_PATH": %s", pa_cstrerror(errno));
+ return -1;
+ }
+
+ while ((de = readdir(d))) {
+ pa_shm seg;
+ unsigned id;
+ pid_t pid;
+ char fn[128];
+ struct shm_marker *m;
+
+ if (strncmp(de->d_name, "pulse-shm-", 10))
+ continue;
+
+ if (pa_atou(de->d_name + 10, &id) < 0)
+ continue;
+
+ if (pa_shm_attach_ro(&seg, id) < 0)
+ continue;
+
+ if (seg.size < PA_ALIGN(sizeof(struct shm_marker))) {
+ pa_shm_free(&seg);
+ continue;
+ }
+
+ m = (struct shm_marker*) ((uint8_t*) seg.ptr + seg.size - PA_ALIGN(sizeof(struct shm_marker)));
+
+ if (pa_atomic_load(&m->marker) != SHM_MARKER) {
+ pa_shm_free(&seg);
+ continue;
+ }
+
+ if (!(pid = (pid_t) pa_atomic_load(&m->pid))) {
+ pa_shm_free(&seg);
+ continue;
+ }
+
+ if (kill(pid, 0) == 0 || errno != ESRCH) {
+ pa_shm_free(&seg);
+ continue;
+ }
+
+ pa_shm_free(&seg);
+
+ /* Ok, the owner of this shms segment is dead, so, let's remove the segment */
+ segment_name(fn, sizeof(fn), id);
+
+ if (shm_unlink(fn) < 0 && errno != EACCES)
+ pa_log_warn("Failed to remove SHM segment %s: %s\n", fn, pa_cstrerror(errno));
+ }
+
+ closedir(d);
+#endif
+
+ return 0;
+}
diff --git a/src/pulsecore/shm.h b/src/pulsecore/shm.h
index e695a2a1..270591de 100644
--- a/src/pulsecore/shm.h
+++ b/src/pulsecore/shm.h
@@ -41,4 +41,6 @@ void pa_shm_punch(pa_shm *m, size_t offset, size_t size);
void pa_shm_free(pa_shm *m);
+int pa_shm_cleanup(void);
+
#endif
diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c
index 3ddd7435..6f654b61 100644
--- a/src/pulsecore/sink-input.c
+++ b/src/pulsecore/sink-input.c
@@ -27,7 +27,6 @@
#endif
#include <stdio.h>
-#include <assert.h>
#include <stdlib.h>
#include <string.h>
@@ -42,45 +41,51 @@
#include "sink-input.h"
-#define CONVERT_BUFFER_LENGTH 4096
-#define MOVE_BUFFER_LENGTH (1024*1024)
-#define SILENCE_BUFFER_LENGTH (64*1024)
+#define CONVERT_BUFFER_LENGTH (PA_PAGE_SIZE)
+#define SILENCE_BUFFER_LENGTH (PA_PAGE_SIZE*12)
+#define MOVE_BUFFER_LENGTH (PA_PAGE_SIZE*256)
-#define CHECK_VALIDITY_RETURN_NULL(condition) \
-do {\
-if (!(condition)) \
- return NULL; \
-} while (0)
+static PA_DEFINE_CHECK_TYPE(pa_sink_input, pa_msgobject);
+
+static void sink_input_free(pa_object *o);
pa_sink_input_new_data* pa_sink_input_new_data_init(pa_sink_input_new_data *data) {
- assert(data);
+ pa_assert(data);
memset(data, 0, sizeof(*data));
data->resample_method = PA_RESAMPLER_INVALID;
+
return data;
}
void pa_sink_input_new_data_set_channel_map(pa_sink_input_new_data *data, const pa_channel_map *map) {
- assert(data);
+ pa_assert(data);
if ((data->channel_map_is_set = !!map))
data->channel_map = *map;
}
void pa_sink_input_new_data_set_volume(pa_sink_input_new_data *data, const pa_cvolume *volume) {
- assert(data);
+ pa_assert(data);
if ((data->volume_is_set = !!volume))
data->volume = *volume;
}
void pa_sink_input_new_data_set_sample_spec(pa_sink_input_new_data *data, const pa_sample_spec *spec) {
- assert(data);
+ pa_assert(data);
if ((data->sample_spec_is_set = !!spec))
data->sample_spec = *spec;
}
+void pa_sink_input_new_data_set_muted(pa_sink_input_new_data *data, pa_bool_t mute) {
+ pa_assert(data);
+
+ data->muted_is_set = TRUE;
+ data->muted = !!mute;
+}
+
pa_sink_input* pa_sink_input_new(
pa_core *core,
pa_sink_input_new_data *data,
@@ -88,46 +93,52 @@ pa_sink_input* pa_sink_input_new(
pa_sink_input *i;
pa_resampler *resampler = NULL;
- int r;
char st[PA_SAMPLE_SPEC_SNPRINT_MAX];
- assert(core);
- assert(data);
+ pa_assert(core);
+ pa_assert(data);
- if (!(flags & PA_SINK_INPUT_NO_HOOKS))
- if (pa_hook_fire(&core->hook_sink_input_new, data) < 0)
- return NULL;
+ if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SINK_INPUT_NEW], data) < 0)
+ return NULL;
- CHECK_VALIDITY_RETURN_NULL(!data->driver || pa_utf8_valid(data->driver));
- CHECK_VALIDITY_RETURN_NULL(!data->name || pa_utf8_valid(data->name));
+ 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);
- CHECK_VALIDITY_RETURN_NULL(data->sink);
- CHECK_VALIDITY_RETURN_NULL(data->sink->state == PA_SINK_RUNNING);
+ pa_return_null_if_fail(data->sink);
+ pa_return_null_if_fail(pa_sink_get_state(data->sink) != PA_SINK_UNLINKED);
+ pa_return_null_if_fail(!data->sync_base || (data->sync_base->sink == data->sink && pa_sink_input_get_state(data->sync_base) == PA_SINK_INPUT_CORKED));
if (!data->sample_spec_is_set)
data->sample_spec = data->sink->sample_spec;
- CHECK_VALIDITY_RETURN_NULL(pa_sample_spec_valid(&data->sample_spec));
+ pa_return_null_if_fail(pa_sample_spec_valid(&data->sample_spec));
- if (!data->channel_map_is_set)
- pa_channel_map_init_auto(&data->channel_map, data->sample_spec.channels, PA_CHANNEL_MAP_DEFAULT);
+ if (!data->channel_map_is_set) {
+ if (data->sink->channel_map.channels == data->sample_spec.channels)
+ data->channel_map = data->sink->channel_map;
+ else
+ pa_channel_map_init_auto(&data->channel_map, data->sample_spec.channels, PA_CHANNEL_MAP_DEFAULT);
+ }
- CHECK_VALIDITY_RETURN_NULL(pa_channel_map_valid(&data->channel_map));
- CHECK_VALIDITY_RETURN_NULL(data->channel_map.channels == data->sample_spec.channels);
+ 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);
- CHECK_VALIDITY_RETURN_NULL(pa_cvolume_valid(&data->volume));
- CHECK_VALIDITY_RETURN_NULL(data->volume.channels == 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 = 0;
if (data->resample_method == PA_RESAMPLER_INVALID)
data->resample_method = core->resample_method;
- CHECK_VALIDITY_RETURN_NULL(data->resample_method < PA_RESAMPLER_MAX);
+ pa_return_null_if_fail(data->resample_method < PA_RESAMPLER_MAX);
if (pa_idxset_size(data->sink->inputs) >= PA_MAX_INPUTS_PER_SINK) {
pa_log_warn("Failed to create sink input: too many inputs per sink.");
@@ -136,20 +147,27 @@ pa_sink_input* pa_sink_input_new(
if ((flags & PA_SINK_INPUT_VARIABLE_RATE) ||
!pa_sample_spec_equal(&data->sample_spec, &data->sink->sample_spec) ||
- !pa_channel_map_equal(&data->channel_map, &data->sink->channel_map))
+ !pa_channel_map_equal(&data->channel_map, &data->sink->channel_map)) {
if (!(resampler = pa_resampler_new(
core->mempool,
&data->sample_spec, &data->channel_map,
&data->sink->sample_spec, &data->sink->channel_map,
- data->resample_method))) {
+ data->resample_method,
+ !!(flags & PA_SINK_INPUT_VARIABLE_RATE)))) {
pa_log_warn("Unsupported resampling operation.");
return NULL;
}
- i = pa_xnew(pa_sink_input, 1);
- i->ref = 1;
- i->state = PA_SINK_INPUT_DRAINED;
+ data->resample_method = pa_resampler_get_method(resampler);
+ }
+
+ i = pa_msgobject_new(pa_sink_input);
+ i->parent.parent.free = sink_input_free;
+ i->parent.process_msg = pa_sink_input_process_msg;
+
+ i->core = core;
+ i->state = PA_SINK_INPUT_INIT;
i->flags = flags;
i->name = pa_xstrdup(data->name);
i->driver = pa_xstrdup(data->driver);
@@ -157,105 +175,203 @@ pa_sink_input* pa_sink_input_new(
i->sink = data->sink;
i->client = data->client;
+ i->resample_method = data->resample_method;
i->sample_spec = data->sample_spec;
i->channel_map = data->channel_map;
+
i->volume = data->volume;
+ i->muted = data->muted;
+
+ if (data->sync_base) {
+ i->sync_next = data->sync_base->sync_next;
+ i->sync_prev = data->sync_base;
+
+ if (data->sync_base->sync_next)
+ data->sync_base->sync_next->sync_prev = i;
+ data->sync_base->sync_next = i;
+ } else
+ i->sync_next = i->sync_prev = NULL;
i->peek = NULL;
i->drop = NULL;
i->kill = NULL;
i->get_latency = NULL;
- i->underrun = NULL;
+ i->attach = NULL;
+ i->detach = NULL;
+ i->suspend = NULL;
i->userdata = NULL;
- i->move_silence = 0;
-
- pa_memchunk_reset(&i->resampled_chunk);
- i->resampler = resampler;
- i->resample_method = data->resample_method;
- i->silence_memblock = NULL;
-
- r = pa_idxset_put(core->sink_inputs, i, &i->index);
- assert(r == 0);
- r = pa_idxset_put(i->sink->inputs, i, NULL);
- assert(r == 0);
-
- pa_log_info("created %u \"%s\" on %s with sample spec %s",
+ i->thread_info.state = i->state;
+ 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;
+
+ 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",
i->index,
i->name,
i->sink->name,
pa_sample_spec_snprint(st, sizeof(st), &i->sample_spec));
- pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW, i->index);
-
- /* We do not call pa_sink_notify() here, because the virtual
- * functions have not yet been initialized */
+ /* Don't forget to call pa_sink_input_put! */
return i;
}
-void pa_sink_input_disconnect(pa_sink_input *i) {
- assert(i);
- assert(i->state != PA_SINK_INPUT_DISCONNECTED);
- assert(i->sink);
- assert(i->sink->core);
+static void update_n_corked(pa_sink_input *i, pa_sink_input_state_t state) {
+ pa_assert(i);
+
+ if (i->state == PA_SINK_INPUT_CORKED && state != PA_SINK_INPUT_CORKED)
+ pa_assert_se(i->sink->n_corked -- >= 1);
+ else if (i->state != PA_SINK_INPUT_CORKED && state == PA_SINK_INPUT_CORKED)
+ i->sink->n_corked++;
+
+ pa_sink_update_status(i->sink);
+}
+
+static int sink_input_set_state(pa_sink_input *i, pa_sink_input_state_t state) {
+ pa_sink_input *ssync;
+ pa_assert(i);
+
+ if (state == PA_SINK_INPUT_DRAINED)
+ state = PA_SINK_INPUT_RUNNING;
+
+ 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;
+
+ update_n_corked(i, state);
+ i->state = state;
+
+ for (ssync = i->sync_prev; ssync; ssync = ssync->sync_prev) {
+ update_n_corked(ssync, state);
+ ssync->state = state;
+ }
+ for (ssync = i->sync_next; ssync; ssync = ssync->sync_next) {
+ update_n_corked(ssync, state);
+ ssync->state = state;
+ }
+
+ if (state != PA_SINK_INPUT_UNLINKED)
+ pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED], i);
+
+ return 0;
+}
+
+void pa_sink_input_unlink(pa_sink_input *i) {
+ pa_bool_t linked;
+ pa_assert(i);
+
+ /* See pa_sink_unlink() for a couple of comments how this function
+ * works */
+
+ pa_sink_input_ref(i);
+
+ linked = PA_SINK_INPUT_LINKED(i->state);
+
+ if (linked)
+ pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_UNLINK], i);
+
+ if (i->sync_prev)
+ i->sync_prev->sync_next = i->sync_next;
+ if (i->sync_next)
+ i->sync_next->sync_prev = i->sync_prev;
+
+ i->sync_prev = i->sync_next = NULL;
pa_idxset_remove_by_data(i->sink->core->sink_inputs, i, NULL);
- pa_idxset_remove_by_data(i->sink->inputs, i, NULL);
+ if (pa_idxset_remove_by_data(i->sink->inputs, i, NULL))
+ pa_sink_input_unref(i);
- pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_REMOVE, i->index);
- i->sink = NULL;
+ 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;
i->peek = NULL;
i->drop = NULL;
i->kill = NULL;
i->get_latency = NULL;
- i->underrun = NULL;
+ i->attach = NULL;
+ i->detach = NULL;
+ i->suspend = NULL;
+
+ if (linked) {
+ pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_REMOVE, i->index);
+ pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_UNLINK_POST], i);
+ }
- i->state = PA_SINK_INPUT_DISCONNECTED;
+ i->sink = NULL;
+ pa_sink_input_unref(i);
}
-static void sink_input_free(pa_sink_input* i) {
- assert(i);
+static void sink_input_free(pa_object *o) {
+ pa_sink_input* i = PA_SINK_INPUT(o);
- if (i->state != PA_SINK_INPUT_DISCONNECTED)
- pa_sink_input_disconnect(i);
+ pa_assert(i);
+ pa_assert(pa_sink_input_refcnt(i) == 0);
- pa_log_info("freed %u \"%s\"", i->index, i->name);
+ if (PA_SINK_INPUT_LINKED(i->state))
+ pa_sink_input_unlink(i);
- if (i->resampled_chunk.memblock)
- pa_memblock_unref(i->resampled_chunk.memblock);
+ pa_log_info("Freeing output %u \"%s\"", i->index, i->name);
- if (i->resampler)
- pa_resampler_free(i->resampler);
+ pa_assert(!i->thread_info.attached);
- if (i->silence_memblock)
- pa_memblock_unref(i->silence_memblock);
+ if (i->thread_info.resampled_chunk.memblock)
+ pa_memblock_unref(i->thread_info.resampled_chunk.memblock);
+
+ 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);
pa_xfree(i->name);
pa_xfree(i->driver);
pa_xfree(i);
}
-void pa_sink_input_unref(pa_sink_input *i) {
- assert(i);
- assert(i->ref >= 1);
+void pa_sink_input_put(pa_sink_input *i) {
+ pa_sink_input_assert_ref(i);
- if (!(--i->ref))
- sink_input_free(i);
-}
+ pa_assert(i->state == PA_SINK_INPUT_INIT);
+ pa_assert(i->peek);
+ pa_assert(i->drop);
-pa_sink_input* pa_sink_input_ref(pa_sink_input *i) {
- assert(i);
- assert(i->ref >= 1);
+ i->thread_info.state = i->state = i->flags & PA_SINK_INPUT_START_CORKED ? PA_SINK_INPUT_CORKED : PA_SINK_INPUT_RUNNING;
+ i->thread_info.volume = i->volume;
+ i->thread_info.muted = i->muted;
- i->ref++;
- return i;
+ if (i->state == PA_SINK_INPUT_CORKED)
+ i->sink->n_corked++;
+
+ pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_ADD_INPUT, i, 0, NULL);
+ pa_sink_update_status(i->sink);
+
+ 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. */
}
void pa_sink_input_kill(pa_sink_input*i) {
- assert(i);
- assert(i->ref >= 1);
+ pa_sink_input_assert_ref(i);
+ pa_assert(PA_SINK_INPUT_LINKED(i->state));
if (i->kill)
i->kill(i);
@@ -264,108 +380,127 @@ void pa_sink_input_kill(pa_sink_input*i) {
pa_usec_t pa_sink_input_get_latency(pa_sink_input *i) {
pa_usec_t r = 0;
- assert(i);
- assert(i->ref >= 1);
+ pa_sink_input_assert_ref(i);
+ pa_assert(PA_SINK_INPUT_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;
if (i->get_latency)
r += i->get_latency(i);
- if (i->resampled_chunk.memblock)
- r += pa_bytes_to_usec(i->resampled_chunk.length, &i->sink->sample_spec);
-
- if (i->move_silence)
- r += pa_bytes_to_usec(i->move_silence, &i->sink->sample_spec);
-
return r;
}
-int pa_sink_input_peek(pa_sink_input *i, pa_memchunk *chunk, pa_cvolume *volume) {
+/* 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;
- assert(i);
- assert(i->ref >= 1);
- assert(chunk);
- assert(volume);
-
- pa_sink_input_ref(i);
+ 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(chunk);
+ pa_assert(volume);
- if (!i->peek || !i->drop || i->state == PA_SINK_INPUT_CORKED)
+ if (!i->peek || !i->drop || i->thread_info.state == PA_SINK_INPUT_CORKED)
goto finish;
- assert(i->state == PA_SINK_INPUT_RUNNING || i->state == PA_SINK_INPUT_DRAINED);
+ pa_assert(i->thread_info.state == PA_SINK_INPUT_RUNNING || i->thread_info.state == PA_SINK_INPUT_DRAINED);
- if (i->move_silence > 0) {
+ /* 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->silence_memblock)
- i->silence_memblock = pa_silence_memblock_new(i->sink->core->mempool, &i->sink->sample_spec, SILENCE_BUFFER_LENGTH);
+ 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->silence_memblock);
+ chunk->memblock = pa_memblock_ref(i->thread_info.silence_memblock);
chunk->index = 0;
- chunk->length = i->move_silence < chunk->memblock->length ? i->move_silence : chunk->memblock->length;
+ 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 (!i->resampler) {
- do_volume_adj_here = 0;
- ret = i->peek(i, chunk);
+ if (!i->thread_info.resampler) {
+ do_volume_adj_here = 0; /* FIXME??? */
+ ret = i->peek(i, length, chunk);
goto finish;
}
do_volume_adj_here = !pa_channel_map_equal(&i->channel_map, &i->sink->channel_map);
- volume_is_norm = pa_cvolume_is_norm(&i->volume);
+ volume_is_norm = pa_cvolume_is_norm(&i->thread_info.volume) && !i->thread_info.muted;
- while (!i->resampled_chunk.memblock) {
+ while (!i->thread_info.resampled_chunk.memblock) {
pa_memchunk tchunk;
- size_t l;
+ size_t l, rmbs;
- if ((ret = i->peek(i, &tchunk)) < 0)
- goto finish;
+ l = pa_resampler_request(i->thread_info.resampler, length);
- assert(tchunk.length);
+ if (l <= 0)
+ l = pa_frame_align(CONVERT_BUFFER_LENGTH, &i->sample_spec);
- l = pa_resampler_request(i->resampler, CONVERT_BUFFER_LENGTH);
+ rmbs = pa_resampler_max_block_size(i->thread_info.resampler);
+ if (l > rmbs)
+ l = rmbs;
- if (l > tchunk.length)
- l = tchunk.length;
+ if ((ret = i->peek(i, l, &tchunk)) < 0)
+ goto finish;
+
+ pa_assert(tchunk.length > 0);
+
+ if (tchunk.length > l)
+ tchunk.length = l;
- i->drop(i, &tchunk, l);
- tchunk.length = l;
+ 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(&tchunk, 0);
- pa_volume_memchunk(&tchunk, &i->sample_spec, &i->volume);
+
+ 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_resampler_run(i->resampler, &tchunk, &i->resampled_chunk);
+ pa_resampler_run(i->thread_info.resampler, &tchunk, &i->thread_info.resampled_chunk);
pa_memblock_unref(tchunk.memblock);
}
- assert(i->resampled_chunk.memblock);
- assert(i->resampled_chunk.length);
+ pa_assert(i->thread_info.resampled_chunk.memblock);
+ pa_assert(i->thread_info.resampled_chunk.length > 0);
- *chunk = i->resampled_chunk;
- pa_memblock_ref(i->resampled_chunk.memblock);
+ *chunk = i->thread_info.resampled_chunk;
+ pa_memblock_ref(i->thread_info.resampled_chunk.memblock);
ret = 0;
finish:
- if (ret < 0 && i->state == PA_SINK_INPUT_RUNNING && i->underrun)
- i->underrun(i);
-
if (ret >= 0)
- i->state = PA_SINK_INPUT_RUNNING;
- else if (ret < 0 && i->state == PA_SINK_INPUT_RUNNING)
- i->state = PA_SINK_INPUT_DRAINED;
+ 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
@@ -374,120 +509,179 @@ finish:
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
+ else if (i->thread_info.muted)
/* We've both the same channel map, so let's have the sink do the adjustment for us*/
- *volume = i->volume;
+ pa_cvolume_mute(volume, i->sink->sample_spec.channels);
+ else
+ *volume = i->thread_info.volume;
}
- pa_sink_input_unref(i);
-
return ret;
}
-void pa_sink_input_drop(pa_sink_input *i, const pa_memchunk *chunk, size_t length) {
- assert(i);
- assert(i->ref >= 1);
- assert(length > 0);
+/* Called from thread context */
+void pa_sink_input_drop(pa_sink_input *i, size_t length) {
+ 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->move_silence > 0) {
+ if (!i->peek || !i->drop || i->thread_info.state == PA_SINK_INPUT_CORKED)
+ return;
- if (chunk) {
+ if (i->thread_info.move_silence > 0) {
- if (chunk->memblock != i->silence_memblock ||
- chunk->index != 0 ||
- (chunk->memblock && (chunk->length != (i->silence_memblock->length < i->move_silence ? i->silence_memblock->length : i->move_silence))))
- return;
+ if (i->thread_info.move_silence >= length) {
+ i->thread_info.move_silence -= length;
+ length = 0;
+ } else {
+ length -= i->thread_info.move_silence;
+ i->thread_info.move_silence = 0;
+ }
+ if (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;
+ }
}
- assert(i->move_silence >= length);
+ if (length <= 0)
+ return;
+ }
+
+ if (i->thread_info.resampled_chunk.memblock) {
+ size_t l = length;
+
+ if (l > i->thread_info.resampled_chunk.length)
+ l = i->thread_info.resampled_chunk.length;
- i->move_silence -= length;
+ i->thread_info.resampled_chunk.index += l;
+ i->thread_info.resampled_chunk.length -= l;
- if (i->move_silence <= 0) {
- assert(i->silence_memblock);
- pa_memblock_unref(i->silence_memblock);
- i->silence_memblock = NULL;
+ 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);
}
- return;
+ length -= l;
}
- if (!i->resampler) {
- if (i->drop)
- i->drop(i, chunk, length);
- return;
- }
+ if (length > 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. */
+
+ while (length > 0) {
+ pa_memchunk chunk;
+ pa_cvolume volume;
+
+ if (pa_sink_input_peek(i, length, &chunk, &volume) >= 0) {
+ size_t l;
+
+ pa_memblock_unref(chunk.memblock);
+
+ l = chunk.length;
+ if (l > length)
+ l = length;
+
+ pa_sink_input_drop(i, l);
+ length -= l;
- assert(i->resampled_chunk.memblock);
- assert(i->resampled_chunk.length >= length);
+ } else {
+ size_t l;
- i->resampled_chunk.index += length;
- i->resampled_chunk.length -= length;
+ l = pa_resampler_request(i->thread_info.resampler, length);
+
+ /* Hmmm, peeking failed, so let's at least drop
+ * the right amount of data */
+
+ if (l > 0)
+ if (i->drop)
+ i->drop(i, l);
+
+ break;
+ }
+ }
- if (i->resampled_chunk.length <= 0) {
- pa_memblock_unref(i->resampled_chunk.memblock);
- i->resampled_chunk.memblock = NULL;
- i->resampled_chunk.index = i->resampled_chunk.length = 0;
+ } else {
+
+ /* We have no resampler, hence let's just drop the data */
+
+ if (i->drop)
+ i->drop(i, length);
+ }
}
}
void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume) {
- assert(i);
- assert(i->ref >= 1);
- assert(i->sink);
- assert(i->sink->core);
+ pa_sink_input_assert_ref(i);
+ pa_assert(PA_SINK_INPUT_LINKED(i->state));
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_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
}
-const pa_cvolume * pa_sink_input_get_volume(pa_sink_input *i) {
- assert(i);
- assert(i->ref >= 1);
+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));
return &i->volume;
}
-void pa_sink_input_cork(pa_sink_input *i, int b) {
- int n;
+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));
- assert(i);
- assert(i->ref >= 1);
+ if (!i->muted == !mute)
+ return;
- assert(i->state != PA_SINK_INPUT_DISCONNECTED);
+ i->muted = mute;
- n = i->state == PA_SINK_INPUT_CORKED && !b;
+ pa_asyncmsgq_post(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_MUTE, PA_UINT_TO_PTR(mute), 0, NULL, NULL);
+ 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) {
+ pa_sink_input_assert_ref(i);
+ pa_assert(PA_SINK_INPUT_LINKED(i->state));
+
+ return !!i->muted;
+}
- if (b)
- i->state = PA_SINK_INPUT_CORKED;
- else if (i->state == PA_SINK_INPUT_CORKED)
- i->state = PA_SINK_INPUT_DRAINED;
+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));
- if (n)
- pa_sink_notify(i->sink);
+ sink_input_set_state(i, b ? PA_SINK_INPUT_CORKED : PA_SINK_INPUT_RUNNING);
}
-void pa_sink_input_set_rate(pa_sink_input *i, uint32_t rate) {
- assert(i);
- assert(i->resampler);
- assert(i->ref >= 1);
+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_return_val_if_fail(i->thread_info.resampler, -1);
if (i->sample_spec.rate == rate)
- return;
+ return 0;
i->sample_spec.rate = rate;
- pa_resampler_set_input_rate(i->resampler, rate);
+
+ pa_asyncmsgq_post(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_RATE, PA_UINT_TO_PTR(rate), 0, NULL, NULL);
pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
+ return 0;
}
void pa_sink_input_set_name(pa_sink_input *i, const char *name) {
- assert(i);
- assert(i->ref >= 1);
+ pa_sink_input_assert_ref(i);
if (!i->name && !name)
return;
@@ -498,47 +692,56 @@ void pa_sink_input_set_name(pa_sink_input *i, const char *name) {
pa_xfree(i->name);
i->name = pa_xstrdup(name);
- pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
+ if (PA_SINK_INPUT_LINKED(i->state)) {
+ pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_NAME_CHANGED], i);
+ pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
+ }
}
pa_resample_method_t pa_sink_input_get_resample_method(pa_sink_input *i) {
- assert(i);
- assert(i->ref >= 1);
+ pa_sink_input_assert_ref(i);
- if (!i->resampler)
- return i->resample_method;
-
- return pa_resampler_get_method(i->resampler);
+ return i->resample_method;
}
int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, int immediately) {
- pa_resampler *new_resampler = NULL;
- pa_memblockq *buffer = NULL;
+ pa_resampler *new_resampler;
pa_sink *origin;
+ pa_usec_t silence_usec = 0;
+ pa_sink_input_move_info info;
- assert(i);
- assert(dest);
+ pa_sink_input_assert_ref(i);
+ pa_assert(PA_SINK_INPUT_LINKED(i->state));
+ pa_sink_assert_ref(dest);
origin = i->sink;
if (dest == origin)
return 0;
+ if (i->flags & PA_SINK_INPUT_DONT_MOVE)
+ return -1;
+
+ if (i->sync_next || i->sync_prev) {
+ pa_log_warn("Moving synchronised streams not supported.");
+ return -1;
+ }
+
if (pa_idxset_size(dest->inputs) >= PA_MAX_INPUTS_PER_SINK) {
pa_log_warn("Failed to move sink input: too many inputs per sink.");
return -1;
}
- if (i->resampler &&
+ 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))
/* Try to reuse the old resampler if possible */
- new_resampler = i->resampler;
+ new_resampler = i->thread_info.resampler;
else if ((i->flags & PA_SINK_INPUT_VARIABLE_RATE) ||
- !pa_sample_spec_equal(&i->sample_spec, &dest->sample_spec) ||
- !pa_channel_map_equal(&i->channel_map, &dest->channel_map)) {
+ !pa_sample_spec_equal(&i->sample_spec, &dest->sample_spec) ||
+ !pa_channel_map_equal(&i->channel_map, &dest->channel_map)) {
/* Okey, we need a new resampler for the new sink */
@@ -546,20 +749,26 @@ int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, int immediately) {
dest->core->mempool,
&i->sample_spec, &i->channel_map,
&dest->sample_spec, &dest->channel_map,
- i->resample_method))) {
+ i->resample_method,
+ !!(i->flags & PA_SINK_INPUT_VARIABLE_RATE)))) {
pa_log_warn("Unsupported resampling operation.");
return -1;
}
- }
+ } else
+ new_resampler = NULL;
+
+ pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE], i);
+
+ memset(&info, 0, sizeof(info));
+ info.sink_input = i;
if (!immediately) {
pa_usec_t old_latency, new_latency;
- pa_usec_t silence_usec = 0;
-
- buffer = pa_memblockq_new(0, MOVE_BUFFER_LENGTH, 0, pa_frame_size(&origin->sample_spec), 0, 0, NULL);
/* Let's do a little bit of Voodoo for compensating latency
- * differences */
+ * 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);
@@ -576,8 +785,6 @@ int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, int immediately) {
silence_usec = old_latency - new_latency;
} else {
- size_t l;
- int volume_is_norm;
/* The latency of new sink is larger than the latency of
* the old sink. Therefore we have to precompute a little
@@ -585,87 +792,164 @@ int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, int immediately) {
* sink, until we can play the first sample on the new
* sink.*/
- l = pa_usec_to_bytes(new_latency - old_latency, &origin->sample_spec);
+ info.buffer_bytes = pa_usec_to_bytes(new_latency - old_latency, &origin->sample_spec);
+ }
- volume_is_norm = pa_cvolume_is_norm(&i->volume);
+ /* Okey, let's move it */
- while (l > 0) {
- pa_memchunk chunk;
- pa_cvolume volume;
- size_t n;
+ if (info.buffer_bytes > 0) {
- if (pa_sink_input_peek(i, &chunk, &volume) < 0)
- break;
+ info.ghost_sink_input = pa_memblockq_sink_input_new(
+ origin,
+ "Ghost Stream",
+ &origin->sample_spec,
+ &origin->channel_map,
+ NULL,
+ NULL);
- n = chunk.length > l ? l : chunk.length;
- pa_sink_input_drop(i, &chunk, n);
- chunk.length = n;
+ 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;
- if (!volume_is_norm) {
- pa_memchunk_make_writable(&chunk, 0);
- pa_volume_memchunk(&chunk, &origin->sample_spec, &volume);
- }
-
- if (pa_memblockq_push(buffer, &chunk) < 0) {
- pa_memblock_unref(chunk.memblock);
- break;
- }
-
- pa_memblock_unref(chunk.memblock);
- l -= n;
- }
+ info.buffer = pa_memblockq_new(0, MOVE_BUFFER_LENGTH, 0, pa_frame_size(&origin->sample_spec), 0, 0, NULL);
}
+ }
- if (i->resampled_chunk.memblock) {
-
- /* There is still some data left in the already resampled
- * memory block. Hence, let's output it on the old sink
- * and sleep so long on the new sink */
+ pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_REMOVE_INPUT_AND_BUFFER, &info, 0, NULL);
- pa_memblockq_push(buffer, &i->resampled_chunk);
- silence_usec += pa_bytes_to_usec(i->resampled_chunk.length, &origin->sample_spec);
- }
+ if (info.ghost_sink_input) {
+ /* Basically, do what pa_sink_input_put() does ...*/
- /* Calculate the new sleeping time */
- i->move_silence = pa_usec_to_bytes(
- pa_bytes_to_usec(i->move_silence, &i->sample_spec) +
- silence_usec,
- &i->sample_spec);
+ 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);
}
- /* Okey, let's move it */
pa_idxset_remove_by_data(origin->inputs, i, NULL);
pa_idxset_put(dest->inputs, i, NULL);
i->sink = dest;
+ if (pa_sink_input_get_state(i) == PA_SINK_INPUT_CORKED) {
+ pa_assert_se(origin->n_corked-- >= 1);
+ dest->n_corked++;
+ }
+
/* Replace resampler */
- if (new_resampler != i->resampler) {
- if (i->resampler)
- pa_resampler_free(i->resampler);
- i->resampler = new_resampler;
+ 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->silence_memblock) {
- pa_memblock_unref(i->silence_memblock);
- i->silence_memblock = NULL;
+ 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->resampled_chunk.memblock) {
- pa_memblock_unref(i->resampled_chunk.memblock);
- i->resampled_chunk.memblock = NULL;
- i->resampled_chunk.index = i->resampled_chunk.length = 0;
+ 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);
}
+ /* 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_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_POST], i);
+
+ pa_log_debug("Successfully moved sink input %i from %s to %s.", i->index, origin->name, dest->name);
+
/* Notify everyone */
pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
- pa_sink_notify(i->sink);
-
- /* Ok, no let's feed the precomputed buffer to the old sink */
- if (buffer)
- pa_play_memblockq(origin, "Ghost Stream", &origin->sample_spec, &origin->channel_map, buffer, NULL);
return 0;
}
+
+/* Called from thread context */
+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);
+ return 0;
+
+ case PA_SINK_INPUT_MESSAGE_SET_MUTE:
+ i->thread_info.muted = PA_PTR_TO_UINT(userdata);
+ return 0;
+
+ case PA_SINK_INPUT_MESSAGE_GET_LATENCY: {
+ pa_usec_t *r = userdata;
+
+ if (i->thread_info.resampled_chunk.memblock)
+ *r += pa_bytes_to_usec(i->thread_info.resampled_chunk.length, &i->sink->sample_spec);
+
+ if (i->thread_info.move_silence)
+ *r += pa_bytes_to_usec(i->thread_info.move_silence, &i->sink->sample_spec);
+
+ return 0;
+ }
+
+ case PA_SINK_INPUT_MESSAGE_SET_RATE:
+
+ i->thread_info.sample_spec.rate = PA_PTR_TO_UINT(userdata);
+ pa_resampler_set_input_rate(i->thread_info.resampler, PA_PTR_TO_UINT(userdata));
+
+ return 0;
+
+ 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);
+
+ i->thread_info.state = 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) {
+ 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;
+ }
+ }
+
+ return -1;
+}
+
+pa_sink_input_state_t pa_sink_input_get_state(pa_sink_input *i) {
+ pa_sink_input_assert_ref(i);
+
+ if (i->state == PA_SINK_INPUT_RUNNING || i->state == PA_SINK_INPUT_DRAINED)
+ return pa_atomic_load(&i->thread_info.drained) ? PA_SINK_INPUT_DRAINED : PA_SINK_INPUT_RUNNING;
+
+ return i->state;
+}
diff --git a/src/pulsecore/sink-input.h b/src/pulsecore/sink-input.h
index 51d9ec78..3f8e2039 100644
--- a/src/pulsecore/sink-input.h
+++ b/src/pulsecore/sink-input.h
@@ -1,5 +1,5 @@
-#ifndef foosinkinputhfoo
-#define foosinkinputhfoo
+#ifndef foopulsesinkinputhfoo
+#define foopulsesinkinputhfoo
/* $Id$ */
@@ -39,20 +39,32 @@ typedef struct pa_sink_input pa_sink_input;
#include <pulsecore/core.h>
typedef enum pa_sink_input_state {
- PA_SINK_INPUT_RUNNING, /*< The stream is alive and kicking */
+ PA_SINK_INPUT_INIT, /*< The stream is not active yet, because pa_sink_put() has not been called yet */
PA_SINK_INPUT_DRAINED, /*< The stream stopped playing because there was no data to play */
+ PA_SINK_INPUT_RUNNING, /*< The stream is alive and kicking */
PA_SINK_INPUT_CORKED, /*< The stream was corked on user request */
- PA_SINK_INPUT_DISCONNECTED /*< The stream is dead */
+ 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) {
+ return x == PA_SINK_INPUT_DRAINED || x == PA_SINK_INPUT_RUNNING || x == PA_SINK_INPUT_CORKED;
+}
+
typedef enum pa_sink_input_flags {
PA_SINK_INPUT_VARIABLE_RATE = 1,
- PA_SINK_INPUT_NO_HOOKS = 2
+ PA_SINK_INPUT_DONT_MOVE = 2,
+ PA_SINK_INPUT_START_CORKED = 4
} pa_sink_input_flags_t;
struct pa_sink_input {
- int ref;
+ pa_msgobject parent;
+
uint32_t index;
+ pa_core *core;
+
+ /* Please note that this state should only be read with
+ * pa_sink_input_get_state(). That function will transparently
+ * merge the thread_info.drained value in. */
pa_sink_input_state_t state;
pa_sink_input_flags_t flags;
@@ -64,27 +76,87 @@ struct pa_sink_input {
pa_sample_spec sample_spec;
pa_channel_map channel_map;
- pa_cvolume volume;
- /* 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_sink_input *sync_prev, *sync_next;
- int (*peek) (pa_sink_input *i, pa_memchunk *chunk);
- void (*drop) (pa_sink_input *i, const pa_memchunk *chunk, size_t length);
+ 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);
+
+ /* 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);
+
+ /* If non-NULL this function is called when the input is first
+ * connected to a sink or when the rtpoll/asyncmsgq fields
+ * change. You usually don't need to implement this function
+ * unless you rewrite a sink that is piggy-backed onto
+ * another. Called from IO thread context */
+ void (*attach) (pa_sink_input *i); /* may be NULL */
+
+ /* If non-NULL this function is called when the output is
+ * disconnected from its sink. Called from IO thread context */
+ void (*detach) (pa_sink_input *i); /* may be NULL */
+
+ /* If non-NULL called whenever the the sink this input is attached
+ * to suspends or resumes. Called from main context */
+ void (*suspend) (pa_sink_input *i, int b); /* may be NULL */
+
+ /* Supposed to unlink and destroy this stream. Called from main
+ * context. */
void (*kill) (pa_sink_input *i); /* may 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. */
pa_usec_t (*get_latency) (pa_sink_input *i); /* may be NULL */
- void (*underrun) (pa_sink_input *i); /* may be NULL */
- void *userdata;
+ pa_resample_method_t resample_method;
- pa_memchunk resampled_chunk;
- pa_resampler *resampler; /* may be NULL */
+ struct {
+ pa_sink_input_state_t state;
+ pa_atomic_t drained;
- pa_resample_method_t resample_method;
+ pa_bool_t attached; /* True only between ->attach() and ->detach() calls */
+
+ pa_sample_spec sample_spec;
+
+ pa_memchunk resampled_chunk;
+ pa_resampler *resampler; /* may be NULL */
+
+ /* Some silence to play before the actual data. This is used to
+ * compensate for latency differences when moving a sink input
+ * "hot" between sinks. */
+ size_t move_silence;
+ pa_memblock *silence_memblock; /* may be NULL */
+
+ pa_sink_input *sync_prev, *sync_next;
+
+ pa_cvolume volume;
+ pa_bool_t muted;
+ } thread_info;
+
+ void *userdata;
+};
+
+PA_DECLARE_CLASS(pa_sink_input);
+#define PA_SINK_INPUT(o) pa_sink_input_cast(o)
- pa_memblock *silence_memblock; /* may be NULL */
+enum {
+ PA_SINK_INPUT_MESSAGE_SET_VOLUME,
+ PA_SINK_INPUT_MESSAGE_SET_MUTE,
+ PA_SINK_INPUT_MESSAGE_GET_LATENCY,
+ PA_SINK_INPUT_MESSAGE_SET_RATE,
+ PA_SINK_INPUT_MESSAGE_SET_STATE,
+ PA_SINK_INPUT_MESSAGE_MAX
};
typedef struct pa_sink_input_new_data {
@@ -95,50 +167,71 @@ typedef struct pa_sink_input_new_data {
pa_sink *sink;
pa_sample_spec sample_spec;
- int sample_spec_is_set;
+ pa_bool_t sample_spec_is_set;
pa_channel_map channel_map;
- int channel_map_is_set;
+ pa_bool_t channel_map_is_set;
+
pa_cvolume volume;
- int volume_is_set;
+ pa_bool_t volume_is_set;
+ pa_bool_t muted;
+ pa_bool_t muted_is_set;
pa_resample_method_t resample_method;
+
+ pa_sink_input *sync_base;
} pa_sink_input_new_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);
+
+/* To be called by the implementing module only */
pa_sink_input* pa_sink_input_new(
pa_core *core,
pa_sink_input_new_data *data,
pa_sink_input_flags_t flags);
-void pa_sink_input_unref(pa_sink_input* i);
-pa_sink_input* pa_sink_input_ref(pa_sink_input* i);
+void pa_sink_input_put(pa_sink_input *i);
+void pa_sink_input_unlink(pa_sink_input* i);
-/* To be called by the implementing module only */
-void pa_sink_input_disconnect(pa_sink_input* i);
+void pa_sink_input_set_name(pa_sink_input *i, const char *name);
-/* External code may request disconnection with this funcion */
+/* Callable by everyone */
+
+/* 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);
-int pa_sink_input_peek(pa_sink_input *i, pa_memchunk *chunk, pa_cvolume *volume);
-void pa_sink_input_drop(pa_sink_input *i, const pa_memchunk *chunk, size_t length);
-
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);
+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, int b);
+void pa_sink_input_cork(pa_sink_input *i, pa_bool_t b);
-void pa_sink_input_set_rate(pa_sink_input *i, uint32_t rate);
-
-void pa_sink_input_set_name(pa_sink_input *i, const char *name);
+int pa_sink_input_set_rate(pa_sink_input *i, uint32_t rate);
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);
+pa_sink_input_state_t pa_sink_input_get_state(pa_sink_input *i);
+
+/* To be used exclusively by the sink driver 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);
+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;
+
#endif
diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
index 9588c2c3..dccb34cc 100644
--- a/src/pulsecore/sink.c
+++ b/src/pulsecore/sink.c
@@ -27,7 +27,6 @@
#endif
#include <stdlib.h>
-#include <assert.h>
#include <string.h>
#include <stdio.h>
@@ -41,16 +40,18 @@
#include <pulsecore/sample-util.h>
#include <pulsecore/core-subscribe.h>
#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/play-memblockq.h>
#include "sink.h"
#define MAX_MIX_CHANNELS 32
+#define MIX_BUFFER_LENGTH (PA_PAGE_SIZE)
+#define SILENCE_BUFFER_LENGTH (PA_PAGE_SIZE*12)
-#define CHECK_VALIDITY_RETURN_NULL(condition) \
-do {\
-if (!(condition)) \
- return NULL; \
-} while (0)
+static PA_DEFINE_CHECK_TYPE(pa_sink, pa_msgobject);
+
+static void sink_free(pa_object *s);
pa_sink* pa_sink_new(
pa_core *core,
@@ -63,68 +64,71 @@ pa_sink* pa_sink_new(
pa_sink *s;
char *n = NULL;
char st[256];
- int r;
pa_channel_map tmap;
- assert(core);
- assert(name);
- assert(spec);
+ pa_assert(core);
+ pa_assert(name);
+ pa_assert(spec);
- CHECK_VALIDITY_RETURN_NULL(pa_sample_spec_valid(spec));
+ pa_return_null_if_fail(pa_sample_spec_valid(spec));
if (!map)
map = pa_channel_map_init_auto(&tmap, spec->channels, PA_CHANNEL_MAP_DEFAULT);
- CHECK_VALIDITY_RETURN_NULL(map && pa_channel_map_valid(map));
- CHECK_VALIDITY_RETURN_NULL(map->channels == spec->channels);
- CHECK_VALIDITY_RETURN_NULL(!driver || pa_utf8_valid(driver));
- CHECK_VALIDITY_RETURN_NULL(pa_utf8_valid(name) && *name);
+ pa_return_null_if_fail(map && pa_channel_map_valid(map));
+ pa_return_null_if_fail(map->channels == spec->channels);
+ pa_return_null_if_fail(!driver || pa_utf8_valid(driver));
+ pa_return_null_if_fail(name && pa_utf8_valid(name) && *name);
- s = pa_xnew(pa_sink, 1);
+ s = pa_msgobject_new(pa_sink);
if (!(name = pa_namereg_register(core, name, PA_NAMEREG_SINK, s, fail))) {
pa_xfree(s);
return NULL;
}
- s->ref = 1;
+ s->parent.parent.free = sink_free;
+ s->parent.process_msg = pa_sink_process_msg;
+
s->core = core;
- s->state = PA_SINK_RUNNING;
+ s->state = PA_SINK_INIT;
+ s->flags = 0;
s->name = pa_xstrdup(name);
s->description = NULL;
s->driver = pa_xstrdup(driver);
- s->owner = NULL;
+ s->module = NULL;
s->sample_spec = *spec;
s->channel_map = *map;
s->inputs = pa_idxset_new(NULL, NULL);
+ s->n_corked = 0;
- pa_cvolume_reset(&s->sw_volume, spec->channels);
- pa_cvolume_reset(&s->hw_volume, spec->channels);
- s->sw_muted = 0;
- s->hw_muted = 0;
-
- s->is_hardware = 0;
+ pa_cvolume_reset(&s->volume, spec->channels);
+ s->muted = FALSE;
+ s->refresh_volume = s->refresh_mute = FALSE;
s->get_latency = NULL;
- s->notify = NULL;
- s->set_hw_volume = NULL;
- s->get_hw_volume = NULL;
- s->set_hw_mute = NULL;
- s->get_hw_mute = NULL;
+ s->set_volume = NULL;
+ s->get_volume = NULL;
+ s->set_mute = NULL;
+ s->get_mute = NULL;
+ s->set_state = NULL;
s->userdata = NULL;
- r = pa_idxset_put(core->sinks, s, &s->index);
- assert(s->index != PA_IDXSET_INVALID && r >= 0);
+ s->asyncmsgq = NULL;
+ s->rtpoll = NULL;
+ s->silence = NULL;
+
+ pa_assert_se(pa_idxset_put(core->sinks, s, &s->index) >= 0);
pa_sample_spec_snprint(st, sizeof(st), spec);
- pa_log_info("created %u \"%s\" with sample spec \"%s\"", s->index, s->name, st);
+ pa_log_info("Created sink %u \"%s\" with sample spec \"%s\"", s->index, s->name, st);
n = pa_sprintf_malloc("%s.monitor", name);
if (!(s->monitor_source = pa_source_new(core, driver, n, 0, spec, map)))
- pa_log_warn("failed to create monitor source.");
+ pa_log_warn("Failed to create monitor source.");
else {
char *d;
s->monitor_source->monitor_of = s;
@@ -135,51 +139,124 @@ pa_sink* pa_sink_new(
pa_xfree(n);
- pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_SINK | PA_SUBSCRIPTION_EVENT_NEW, s->index);
+ 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;
return s;
}
-void pa_sink_disconnect(pa_sink* s) {
+static int sink_set_state(pa_sink *s, pa_sink_state_t state) {
+ int ret;
+
+ pa_assert(s);
+
+ if (s->state == state)
+ return 0;
+
+ if ((s->state == PA_SINK_SUSPENDED && PA_SINK_OPENED(state)) ||
+ (PA_SINK_OPENED(s->state) && state == PA_SINK_SUSPENDED)) {
+ pa_sink_input *i;
+ uint32_t idx;
+
+ /* We're suspending or resuming, tell everyone about it */
+
+ for (i = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); i; i = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx)))
+ if (i->suspend)
+ i->suspend(i, state == PA_SINK_SUSPENDED);
+ }
+
+ if (s->set_state)
+ if ((ret = s->set_state(s, state)) < 0)
+ return -1;
+
+ if (pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_STATE, PA_UINT_TO_PTR(state), 0, NULL) < 0)
+ return -1;
+
+ s->state = state;
+
+ if (state != PA_SINK_UNLINKED) /* if we enter UNLINKED state pa_sink_unlink() will fire the apropriate events */
+ pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_STATE_CHANGED], s);
+ return 0;
+}
+
+void pa_sink_put(pa_sink* s) {
+ pa_sink_assert_ref(s);
+
+ pa_assert(s->state == PA_SINK_INIT);
+ pa_assert(s->asyncmsgq);
+ pa_assert(s->rtpoll);
+
+ 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);
+}
+
+void pa_sink_unlink(pa_sink* s) {
+ pa_bool_t linked;
pa_sink_input *i, *j = NULL;
- assert(s);
- assert(s->state == PA_SINK_RUNNING);
+ pa_assert(s);
+
+ /* Please note that pa_sink_unlink() does more than simply
+ * reversing pa_sink_put(). It also undoes the registrations
+ * already done in pa_sink_new()! */
- s->state = PA_SINK_DISCONNECTED;
- pa_namereg_unregister(s->core, s->name);
+ /* All operations here shall be idempotent, i.e. pa_sink_unlink()
+ * may be called multiple times on the same sink without bad
+ * effects. */
- pa_hook_fire(&s->core->hook_sink_disconnect, s);
+ linked = PA_SINK_LINKED(s->state);
+
+ if (linked)
+ pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_UNLINK], s);
+
+ if (s->state != PA_SINK_UNLINKED)
+ pa_namereg_unregister(s->core, s->name);
+ pa_idxset_remove_by_data(s->core->sinks, s, NULL);
while ((i = pa_idxset_first(s->inputs, NULL))) {
- assert(i != j);
+ pa_assert(i != j);
pa_sink_input_kill(i);
j = i;
}
- if (s->monitor_source)
- pa_source_disconnect(s->monitor_source);
-
- pa_idxset_remove_by_data(s->core->sinks, s, NULL);
+ if (linked)
+ sink_set_state(s, PA_SINK_UNLINKED);
+ else
+ s->state = PA_SINK_UNLINKED;
s->get_latency = NULL;
- s->notify = NULL;
- s->get_hw_volume = NULL;
- s->set_hw_volume = NULL;
- s->set_hw_mute = NULL;
- s->get_hw_mute = NULL;
+ s->get_volume = NULL;
+ s->set_volume = NULL;
+ s->set_mute = NULL;
+ s->get_mute = NULL;
+ s->set_state = NULL;
- pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK | PA_SUBSCRIPTION_EVENT_REMOVE, s->index);
+ if (s->monitor_source)
+ pa_source_unlink(s->monitor_source);
+
+ if (linked) {
+ pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK | PA_SUBSCRIPTION_EVENT_REMOVE, s->index);
+ pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_UNLINK_POST], s);
+ }
}
-static void sink_free(pa_sink *s) {
- assert(s);
- assert(!s->ref);
+static void sink_free(pa_object *o) {
+ pa_sink *s = PA_SINK(o);
+ pa_sink_input *i;
+
+ pa_assert(s);
+ pa_assert(pa_sink_refcnt(s) == 0);
- if (s->state != PA_SINK_DISCONNECTED)
- pa_sink_disconnect(s);
+ if (PA_SINK_LINKED(s->state))
+ pa_sink_unlink(s);
- pa_log_info("freed %u \"%s\"", s->index, s->name);
+ pa_log_info("Freeing sink %u \"%s\"", s->index, s->name);
if (s->monitor_source) {
pa_source_unref(s->monitor_source);
@@ -188,108 +265,192 @@ static void sink_free(pa_sink *s) {
pa_idxset_free(s->inputs, NULL, NULL);
+ while ((i = pa_hashmap_steal_first(s->thread_info.inputs)))
+ pa_sink_input_unref(i);
+
+ pa_hashmap_free(s->thread_info.inputs, NULL, NULL);
+
+ if (s->silence)
+ pa_memblock_unref(s->silence);
+
pa_xfree(s->name);
pa_xfree(s->description);
pa_xfree(s->driver);
pa_xfree(s);
}
-void pa_sink_unref(pa_sink*s) {
- assert(s);
- assert(s->ref >= 1);
+void pa_sink_set_asyncmsgq(pa_sink *s, pa_asyncmsgq *q) {
+ pa_sink_assert_ref(s);
+ pa_assert(q);
+
+ s->asyncmsgq = q;
- if (!(--s->ref))
- sink_free(s);
+ if (s->monitor_source)
+ pa_source_set_asyncmsgq(s->monitor_source, q);
}
-pa_sink* pa_sink_ref(pa_sink *s) {
- assert(s);
- assert(s->ref >= 1);
+void pa_sink_set_rtpoll(pa_sink *s, pa_rtpoll *p) {
+ pa_sink_assert_ref(s);
+ pa_assert(p);
- s->ref++;
- return s;
+ s->rtpoll = p;
+ if (s->monitor_source)
+ pa_source_set_rtpoll(s->monitor_source, p);
+}
+
+int pa_sink_update_status(pa_sink*s) {
+ pa_sink_assert_ref(s);
+ pa_assert(PA_SINK_LINKED(s->state));
+
+ if (s->state == PA_SINK_SUSPENDED)
+ return 0;
+
+ return sink_set_state(s, pa_sink_used_by(s) ? PA_SINK_RUNNING : PA_SINK_IDLE);
+}
+
+int pa_sink_suspend(pa_sink *s, pa_bool_t suspend) {
+ pa_sink_assert_ref(s);
+ pa_assert(PA_SINK_LINKED(s->state));
+
+ if (suspend)
+ return sink_set_state(s, PA_SINK_SUSPENDED);
+ else
+ return sink_set_state(s, pa_sink_used_by(s) ? PA_SINK_RUNNING : PA_SINK_IDLE);
}
-void pa_sink_notify(pa_sink*s) {
- assert(s);
- assert(s->ref >= 1);
+void pa_sink_ping(pa_sink *s) {
+ pa_sink_assert_ref(s);
+ pa_assert(PA_SINK_LINKED(s->state));
- if (s->notify)
- s->notify(s);
+ pa_asyncmsgq_post(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_PING, NULL, 0, NULL, NULL);
}
-static unsigned fill_mix_info(pa_sink *s, pa_mix_info *info, unsigned maxinfo) {
- uint32_t idx = PA_IDXSET_INVALID;
+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;
- assert(s);
- assert(s->ref >= 1);
- assert(info);
+ pa_sink_assert_ref(s);
+ pa_assert(info);
- for (i = pa_idxset_first(s->inputs, &idx); maxinfo > 0 && i; i = pa_idxset_next(s->inputs, &idx)) {
- /* Increase ref counter, to make sure that this input doesn't
- * vanish while we still need it */
- pa_sink_input_ref(i);
+ 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, &info->chunk, &info->volume) < 0) {
- pa_sink_input_unref(i);
+ if (pa_sink_input_peek(i, length, &info->chunk, &info->volume) < 0)
continue;
- }
- info->userdata = i;
+ info->userdata = pa_sink_input_ref(i);
- assert(info->chunk.memblock);
- assert(info->chunk.memblock->data);
- assert(info->chunk.length);
+ pa_assert(info->chunk.memblock);
+ pa_assert(info->chunk.length > 0);
info++;
- maxinfo--;
n++;
+ maxinfo--;
}
return n;
}
-static void inputs_drop(pa_sink *s, pa_mix_info *info, unsigned maxinfo, size_t length) {
- assert(s);
- assert(s->ref >= 1);
- assert(info);
+static void inputs_drop(pa_sink *s, pa_mix_info *info, unsigned n, size_t length) {
+ pa_sink_input *i;
+ void *state = NULL;
+ unsigned p = 0;
+ unsigned n_unreffed = 0;
+
+ pa_sink_assert_ref(s);
+
+ /* 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_sink_input_assert_ref(i);
- for (; maxinfo > 0; maxinfo--, info++) {
- pa_sink_input *i = info->userdata;
+ m = NULL;
- assert(i);
- assert(info->chunk.memblock);
+ /* Let's try to find the matching entry info the pa_mix_info array */
+ for (j = 0; j < n; j ++) {
+
+ if (info[p].userdata == i) {
+ m = info + p;
+ break;
+ }
+
+ p++;
+ if (p >= n)
+ p = 0;
+ }
/* Drop read data */
- pa_sink_input_drop(i, &info->chunk, length);
- pa_memblock_unref(info->chunk.memblock);
+ pa_sink_input_drop(i, length);
- /* Decrease ref counter */
- pa_sink_input_unref(i);
- info->userdata = NULL;
+ 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);
+
+ n_unreffed += 1;
+ }
+ }
+
+ /* Now drop references to entries that are included in the
+ * pa_mix_info array but don't exist anymore */
+
+ if (n_unreffed < n) {
+ for (; n > 0; info++, n--) {
+ if (info->userdata)
+ pa_sink_input_unref(info->userdata);
+ if (info->chunk.memblock)
+ pa_memblock_unref(info->chunk.memblock);
+ }
}
}
-int pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) {
+void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) {
pa_mix_info info[MAX_MIX_CHANNELS];
unsigned n;
- int r = -1;
+ size_t block_size_max;
- assert(s);
- assert(s->ref >= 1);
- assert(length);
- assert(result);
+ pa_sink_assert_ref(s);
+ pa_assert(PA_SINK_OPENED(s->thread_info.state));
+ pa_assert(pa_frame_aligned(length, &s->sample_spec));
+ pa_assert(result);
pa_sink_ref(s);
- n = fill_mix_info(s, info, MAX_MIX_CHANNELS);
+ if (length <= 0)
+ length = pa_frame_align(MIX_BUFFER_LENGTH, &s->sample_spec);
+
+ 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 (length > SILENCE_BUFFER_LENGTH)
+ length = pa_frame_align(SILENCE_BUFFER_LENGTH, &s->sample_spec);
- if (n <= 0)
- goto finish;
+ pa_assert(length > 0);
- if (n == 1) {
+ 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->memblock = pa_memblock_ref(s->silence);
+ result->length = length;
+ result->index = 0;
+
+ } else if (n == 1) {
pa_cvolume volume;
*result = info[0].chunk;
@@ -298,105 +459,112 @@ int pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) {
if (result->length > length)
result->length = length;
- pa_sw_cvolume_multiply(&volume, &s->sw_volume, &info[0].volume);
+ pa_sw_cvolume_multiply(&volume, &s->thread_info.soft_volume, &info[0].volume);
- if (s->sw_muted || !pa_cvolume_is_norm(&volume)) {
+ if (s->thread_info.soft_muted || !pa_cvolume_is_norm(&volume)) {
pa_memchunk_make_writable(result, 0);
- if (s->sw_muted)
+ if (s->thread_info.soft_muted || pa_cvolume_is_muted(&volume))
pa_silence_memchunk(result, &s->sample_spec);
else
pa_volume_memchunk(result, &s->sample_spec, &volume);
}
} else {
+ void *ptr;
result->memblock = pa_memblock_new(s->core->mempool, length);
- assert(result->memblock);
-/* pa_log("mixing %i", n); */
+ 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);
+ pa_memblock_release(result->memblock);
- result->length = pa_mix(info, n, result->memblock->data, length,
- &s->sample_spec, &s->sw_volume, s->sw_muted);
result->index = 0;
}
- inputs_drop(s, info, n, result->length);
+ if (s->thread_info.state == PA_SINK_RUNNING)
+ inputs_drop(s, info, n, result->length);
- if (s->monitor_source)
+ if (s->monitor_source && PA_SOURCE_OPENED(pa_source_get_state(s->monitor_source)))
pa_source_post(s->monitor_source, result);
- r = 0;
-
-finish:
pa_sink_unref(s);
-
- return r;
}
-int pa_sink_render_into(pa_sink*s, pa_memchunk *target) {
+void pa_sink_render_into(pa_sink*s, pa_memchunk *target) {
pa_mix_info info[MAX_MIX_CHANNELS];
unsigned n;
- int r = -1;
- assert(s);
- assert(s->ref >= 1);
- assert(target);
- assert(target->memblock);
- assert(target->length);
- assert(target->memblock->data);
+ pa_sink_assert_ref(s);
+ pa_assert(PA_SINK_OPENED(s->thread_info.state));
+ pa_assert(target);
+ pa_assert(target->memblock);
+ pa_assert(target->length > 0);
+ pa_assert(pa_frame_aligned(target->length, &s->sample_spec));
pa_sink_ref(s);
- n = fill_mix_info(s, info, MAX_MIX_CHANNELS);
-
- if (n <= 0)
- goto finish;
-
- if (n == 1) {
- pa_cvolume volume;
+ n = s->thread_info.state == PA_SINK_RUNNING ? fill_mix_info(s, target->length, info, MAX_MIX_CHANNELS) : 0;
+ if (n == 0) {
+ pa_silence_memchunk(target, &s->sample_spec);
+ } else if (n == 1) {
if (target->length > info[0].chunk.length)
target->length = info[0].chunk.length;
- memcpy((uint8_t*) target->memblock->data + target->index,
- (uint8_t*) info[0].chunk.memblock->data + info[0].chunk.index,
- target->length);
+ if (s->thread_info.soft_muted)
+ pa_silence_memchunk(target, &s->sample_spec);
+ else {
+ void *src, *ptr;
+ pa_cvolume volume;
- pa_sw_cvolume_multiply(&volume, &s->sw_volume, &info[0].volume);
+ ptr = pa_memblock_acquire(target->memblock);
+ src = pa_memblock_acquire(info[0].chunk.memblock);
+
+ memcpy((uint8_t*) ptr + target->index,
+ (uint8_t*) src + info[0].chunk.index,
+ target->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_volume_memchunk(target, &s->sample_spec, &volume);
+ }
+
+ } else {
+ void *ptr;
+
+ ptr = pa_memblock_acquire(target->memblock);
- if (s->sw_muted)
- pa_silence_memchunk(target, &s->sample_spec);
- else if (!pa_cvolume_is_norm(&volume))
- pa_volume_memchunk(target, &s->sample_spec, &volume);
- } else
target->length = pa_mix(info, n,
- (uint8_t*) target->memblock->data + target->index,
+ (uint8_t*) ptr + target->index,
target->length,
&s->sample_spec,
- &s->sw_volume,
- s->sw_muted);
+ &s->thread_info.soft_volume,
+ s->thread_info.soft_muted);
- inputs_drop(s, info, n, target->length);
+ pa_memblock_release(target->memblock);
+ }
- if (s->monitor_source)
- pa_source_post(s->monitor_source, target);
+ if (s->thread_info.state == PA_SINK_RUNNING)
+ inputs_drop(s, info, n, target->length);
- r = 0;
+ if (s->monitor_source && PA_SOURCE_OPENED(pa_source_get_state(s->monitor_source)))
+ pa_source_post(s->monitor_source, target);
-finish:
pa_sink_unref(s);
-
- return r;
}
void pa_sink_render_into_full(pa_sink *s, pa_memchunk *target) {
pa_memchunk chunk;
size_t l, d;
- assert(s);
- assert(s->ref >= 1);
- assert(target);
- assert(target->memblock);
- assert(target->length);
- assert(target->memblock->data);
+ pa_sink_assert_ref(s);
+ pa_assert(PA_SINK_OPENED(s->thread_info.state));
+ pa_assert(target);
+ pa_assert(target->memblock);
+ pa_assert(target->length > 0);
+ pa_assert(pa_frame_aligned(target->length, &s->sample_spec));
pa_sink_ref(s);
@@ -407,140 +575,177 @@ void pa_sink_render_into_full(pa_sink *s, pa_memchunk *target) {
chunk.index += d;
chunk.length -= d;
- if (pa_sink_render_into(s, &chunk) < 0)
- break;
+ pa_sink_render_into(s, &chunk);
d += chunk.length;
l -= chunk.length;
}
- if (l > 0) {
- chunk = *target;
- chunk.index += d;
- chunk.length -= d;
- pa_silence_memchunk(&chunk, &s->sample_spec);
- }
-
pa_sink_unref(s);
}
void pa_sink_render_full(pa_sink *s, size_t length, pa_memchunk *result) {
- assert(s);
- assert(s->ref >= 1);
- assert(length);
- assert(result);
+ 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));
+ pa_assert(result);
/*** This needs optimization ***/
- result->memblock = pa_memblock_new(s->core->mempool, result->length = length);
result->index = 0;
+ result->length = length;
+ result->memblock = pa_memblock_new(s->core->mempool, length);
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);
+ }
+ }
+}
+
pa_usec_t pa_sink_get_latency(pa_sink *s) {
- assert(s);
- assert(s->ref >= 1);
+ pa_usec_t usec = 0;
- if (!s->get_latency)
+ pa_sink_assert_ref(s);
+ pa_assert(PA_SINK_LINKED(s->state));
+
+ if (!PA_SINK_OPENED(s->state))
+ return 0;
+
+ if (s->get_latency)
+ return s->get_latency(s);
+
+ if (pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0)
return 0;
- return s->get_latency(s);
+ return usec;
}
-void pa_sink_set_owner(pa_sink *s, pa_module *m) {
- assert(s);
- assert(s->ref >= 1);
+void pa_sink_set_volume(pa_sink *s, const pa_cvolume *volume) {
+ int changed;
- if (s->owner == m)
- return;
+ pa_sink_assert_ref(s);
+ pa_assert(PA_SINK_LINKED(s->state));
+ pa_assert(volume);
- s->owner = m;
+ changed = !pa_cvolume_equal(volume, &s->volume);
+ s->volume = *volume;
- if (s->monitor_source)
- pa_source_set_owner(s->monitor_source, m);
+ if (s->set_volume && s->set_volume(s) < 0)
+ s->set_volume = NULL;
- pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+ 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);
+
+ if (changed)
+ pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
}
-void pa_sink_set_volume(pa_sink *s, pa_mixer_t m, const pa_cvolume *volume) {
- pa_cvolume *v;
+const pa_cvolume *pa_sink_get_volume(pa_sink *s) {
+ struct pa_cvolume old_volume;
- assert(s);
- assert(s->ref >= 1);
- assert(volume);
+ pa_sink_assert_ref(s);
+ pa_assert(PA_SINK_LINKED(s->state));
- if (m == PA_MIXER_HARDWARE && s->set_hw_volume)
- v = &s->hw_volume;
- else
- v = &s->sw_volume;
+ old_volume = s->volume;
- if (pa_cvolume_equal(v, volume))
- return;
+ if (s->get_volume && s->get_volume(s) < 0)
+ s->get_volume = NULL;
- *v = *volume;
+ 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 (v == &s->hw_volume)
- if (s->set_hw_volume(s) < 0)
- s->sw_volume = *volume;
+ if (!pa_cvolume_equal(&old_volume, &s->volume))
+ pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
- pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+ return &s->volume;
}
-const pa_cvolume *pa_sink_get_volume(pa_sink *s, pa_mixer_t m) {
- assert(s);
- assert(s->ref >= 1);
+void pa_sink_set_mute(pa_sink *s, pa_bool_t mute) {
+ int changed;
+
+ pa_sink_assert_ref(s);
+ pa_assert(PA_SINK_LINKED(s->state));
- if (m == PA_MIXER_HARDWARE && s->set_hw_volume) {
+ changed = s->muted != mute;
+ s->muted = mute;
- if (s->get_hw_volume)
- s->get_hw_volume(s);
+ if (s->set_mute && s->set_mute(s) < 0)
+ s->set_mute = NULL;
- return &s->hw_volume;
- } else
- return &s->sw_volume;
+ if (!s->set_mute)
+ pa_asyncmsgq_post(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_MUTE, PA_UINT_TO_PTR(mute), 0, NULL, NULL);
+
+ if (changed)
+ pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
}
-void pa_sink_set_mute(pa_sink *s, pa_mixer_t m, int mute) {
- int *t;
+pa_bool_t pa_sink_get_mute(pa_sink *s) {
+ pa_bool_t old_muted;
- assert(s);
- assert(s->ref >= 1);
+ pa_sink_assert_ref(s);
+ pa_assert(PA_SINK_LINKED(s->state));
- if (m == PA_MIXER_HARDWARE && s->set_hw_mute)
- t = &s->hw_muted;
- else
- t = &s->sw_muted;
+ old_muted = s->muted;
- if (!!*t == !!mute)
- return;
+ if (s->get_mute && s->get_mute(s) < 0)
+ s->get_mute = NULL;
- *t = !!mute;
+ 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 (t == &s->hw_muted)
- if (s->set_hw_mute(s) < 0)
- s->sw_muted = !!mute;
+ if (old_muted != s->muted)
+ pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
- pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+ return s->muted;
}
-int pa_sink_get_mute(pa_sink *s, pa_mixer_t m) {
- assert(s);
- assert(s->ref >= 1);
+void pa_sink_set_module(pa_sink *s, pa_module *m) {
+ pa_sink_assert_ref(s);
- if (m == PA_MIXER_HARDWARE && s->set_hw_mute) {
+ if (s->module == m)
+ return;
+
+ s->module = m;
- if (s->get_hw_mute)
- s->get_hw_mute(s);
+ if (s->monitor_source)
+ pa_source_set_module(s->monitor_source, m);
- return s->hw_muted;
- } else
- return s->sw_muted;
+ pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
}
void pa_sink_set_description(pa_sink *s, const char *description) {
- assert(s);
- assert(s->ref >= 1);
+ pa_sink_assert_ref(s);
if (!description && !s->description)
return;
@@ -559,19 +764,298 @@ void pa_sink_set_description(pa_sink *s, const char *description) {
pa_xfree(n);
}
- pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+ if (PA_SINK_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);
+ }
}
-unsigned pa_sink_used_by(pa_sink *s) {
+unsigned pa_sink_linked_by(pa_sink *s) {
unsigned ret;
- assert(s);
- assert(s->ref >= 1);
+ pa_sink_assert_ref(s);
+ pa_assert(PA_SINK_LINKED(s->state));
ret = pa_idxset_size(s->inputs);
+ /* We add in the number of streams connected to us here. Please
+ * not the asymmmetry to pa_sink_used_by()! */
+
if (s->monitor_source)
- ret += pa_source_used_by(s->monitor_source);
+ ret += pa_source_linked_by(s->monitor_source);
return ret;
}
+
+unsigned pa_sink_used_by(pa_sink *s) {
+ unsigned ret;
+
+ pa_sink_assert_ref(s);
+ pa_assert(PA_SINK_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;
+}
+
+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);
+ 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
+ * safely access data outside of thread_info even though
+ * it is mutable */
+
+ if ((i->thread_info.sync_prev = i->sync_prev)) {
+ pa_assert(i->sink == i->thread_info.sync_prev->sink);
+ pa_assert(i->sync_prev->sync_next == i);
+ i->thread_info.sync_prev->thread_info.sync_next = i;
+ }
+
+ if ((i->thread_info.sync_next = i->sync_next)) {
+ pa_assert(i->sink == i->thread_info.sync_next->sink);
+ pa_assert(i->sync_next->sync_prev == i);
+ i->thread_info.sync_next->thread_info.sync_prev = i;
+ }
+
+ pa_assert(!i->thread_info.attached);
+ i->thread_info.attached = TRUE;
+
+ 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. */
+
+ return 0;
+ }
+
+ case PA_SINK_MESSAGE_REMOVE_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_REMOVE_INPUT_AND_BUFFER, too. */
+
+ if (i->detach)
+ i->detach(i);
+
+ pa_assert(i->thread_info.attached);
+ i->thread_info.attached = FALSE;
+
+ /* Since the caller sleeps in pa_sink_input_unlink(),
+ * 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);
+
+ if (i->thread_info.sync_prev) {
+ i->thread_info.sync_prev->thread_info.sync_next = i->thread_info.sync_prev->sync_next;
+ i->thread_info.sync_prev = NULL;
+ }
+
+ if (i->thread_info.sync_next) {
+ i->thread_info.sync_next->thread_info.sync_prev = i->thread_info.sync_next->sync_prev;
+ i->thread_info.sync_next = NULL;
+ }
+
+ if (pa_hashmap_remove(s->thread_info.inputs, PA_UINT32_TO_PTR(i->index)))
+ pa_sink_input_unref(i);
+
+ return 0;
+ }
+
+ case PA_SINK_MESSAGE_REMOVE_INPUT_AND_BUFFER: {
+ pa_sink_input_move_info *info = userdata;
+ int volume_is_norm;
+
+ /* 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);
+
+ if (info->sink_input->detach)
+ info->sink_input->detach(info->sink_input);
+
+ pa_assert(info->sink_input->thread_info.attached);
+ info->sink_input->thread_info.attached = FALSE;
+
+ if (info->ghost_sink_input) {
+ pa_assert(info->buffer_bytes > 0);
+ pa_assert(info->buffer);
+
+ volume_is_norm = pa_cvolume_is_norm(&info->sink_input->thread_info.volume);
+
+ pa_log_debug("Buffering %lu bytes ...", (unsigned long) info->buffer_bytes);
+
+ while (info->buffer_bytes > 0) {
+ pa_memchunk memchunk;
+ pa_cvolume volume;
+ size_t n;
+
+ if (pa_sink_input_peek(info->sink_input, info->buffer_bytes, &memchunk, &volume) < 0)
+ break;
+
+ n = memchunk.length > info->buffer_bytes ? info->buffer_bytes : memchunk.length;
+ pa_sink_input_drop(info->sink_input, n);
+ memchunk.length = n;
+
+ if (!volume_is_norm) {
+ pa_memchunk_make_writable(&memchunk, 0);
+ pa_volume_memchunk(&memchunk, &s->sample_spec, &volume);
+ }
+
+ if (pa_memblockq_push(info->buffer, &memchunk) < 0) {
+ pa_memblock_unref(memchunk.memblock);
+ break;
+ }
+
+ pa_memblock_unref(memchunk.memblock);
+ info->buffer_bytes -= n;
+ }
+
+ /* Add the remaining already resampled chunk to the buffer */
+ if (info->sink_input->thread_info.resampled_chunk.memblock)
+ pa_memblockq_push(info->buffer, &info->sink_input->thread_info.resampled_chunk);
+
+ pa_memblockq_sink_input_set_queue(info->ghost_sink_input, info->buffer);
+
+ pa_log_debug("Buffered %lu bytes ...", (unsigned long) pa_memblockq_get_length(info->buffer));
+ }
+
+ /* 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);
+
+ /* .. 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;
+
+ pa_assert(!info->ghost_sink_input->thread_info.attached);
+ info->ghost_sink_input->thread_info.attached = TRUE;
+
+ if (info->ghost_sink_input->attach)
+ info->ghost_sink_input->attach(info->ghost_sink_input);
+ }
+
+ return 0;
+ }
+
+ case PA_SINK_MESSAGE_SET_VOLUME:
+ s->thread_info.soft_volume = *((pa_cvolume*) userdata);
+ return 0;
+
+ case PA_SINK_MESSAGE_SET_MUTE:
+ s->thread_info.soft_muted = PA_PTR_TO_UINT(userdata);
+ return 0;
+
+ case PA_SINK_MESSAGE_GET_VOLUME:
+ *((pa_cvolume*) userdata) = s->thread_info.soft_volume;
+ return 0;
+
+ case PA_SINK_MESSAGE_GET_MUTE:
+ *((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);
+ return 0;
+
+ case PA_SINK_MESSAGE_DETACH:
+
+ /* We're detaching all our input streams so that the
+ * asyncmsgq and rtpoll fields can be changed without
+ * problems */
+ pa_sink_detach_within_thread(s);
+ break;
+
+ case PA_SINK_MESSAGE_ATTACH:
+
+ /* Reattach all streams */
+ pa_sink_attach_within_thread(s);
+ break;
+
+ case PA_SINK_MESSAGE_GET_LATENCY:
+ case PA_SINK_MESSAGE_MAX:
+ ;
+ }
+
+ return -1;
+}
+
+int pa_sink_suspend_all(pa_core *c, pa_bool_t suspend) {
+ pa_sink *sink;
+ uint32_t idx;
+ int ret = 0;
+
+ pa_core_assert_ref(c);
+
+ for (sink = PA_SINK(pa_idxset_first(c->sinks, &idx)); sink; sink = PA_SINK(pa_idxset_next(c->sinks, &idx)))
+ ret -= pa_sink_suspend(sink, suspend) < 0;
+
+ return ret;
+}
+
+void pa_sink_detach(pa_sink *s) {
+ pa_sink_assert_ref(s);
+ pa_assert(PA_SINK_LINKED(s->state));
+
+ pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_DETACH, NULL, 0, NULL);
+}
+
+void pa_sink_attach(pa_sink *s) {
+ pa_sink_assert_ref(s);
+ pa_assert(PA_SINK_LINKED(s->state));
+
+ pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_ATTACH, NULL, 0, NULL);
+}
+
+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));
+
+ while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
+ if (i->detach)
+ i->detach(i);
+
+ if (s->monitor_source)
+ pa_source_detach_within_thread(s->monitor_source);
+}
+
+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));
+
+ while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
+ if (i->attach)
+ i->attach(i);
+
+ if (s->monitor_source)
+ pa_source_attach_within_thread(s->monitor_source);
+}
diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h
index ef73f67d..e9969309 100644
--- a/src/pulsecore/sink.h
+++ b/src/pulsecore/sink.h
@@ -1,5 +1,5 @@
-#ifndef foosinkhfoo
-#define foosinkhfoo
+#ifndef foopulsesinkhfoo
+#define foopulsesinkhfoo
/* $Id$ */
@@ -25,87 +25,164 @@
USA.
***/
-#include <inttypes.h>
-
typedef struct pa_sink pa_sink;
+#include <inttypes.h>
+
#include <pulse/sample.h>
#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>
#include <pulsecore/module.h>
+#include <pulsecore/refcnt.h>
+#include <pulsecore/msgobject.h>
+#include <pulsecore/rtpoll.h>
#define PA_MAX_INPUTS_PER_SINK 32
typedef enum pa_sink_state {
+ PA_SINK_INIT,
PA_SINK_RUNNING,
- PA_SINK_DISCONNECTED
+ PA_SINK_SUSPENDED,
+ PA_SINK_IDLE,
+ PA_SINK_UNLINKED
} pa_sink_state_t;
+static inline pa_bool_t PA_SINK_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) {
+ return x == PA_SINK_RUNNING || x == PA_SINK_IDLE || x == PA_SINK_SUSPENDED;
+}
+
struct pa_sink {
- int ref;
+ pa_msgobject parent;
+
uint32_t index;
pa_core *core;
pa_sink_state_t state;
+ pa_sink_flags_t flags;
char *name;
char *description, *driver; /* may be NULL */
- int is_hardware;
- pa_module *owner; /* may be NULL */
+ pa_module *module; /* may be NULL */
pa_sample_spec sample_spec;
pa_channel_map channel_map;
pa_idxset *inputs;
- pa_source *monitor_source; /* may be NULL */
-
- pa_cvolume hw_volume, sw_volume;
- int hw_muted, sw_muted;
-
- void (*notify)(pa_sink*sink); /* may be NULL */
- pa_usec_t (*get_latency)(pa_sink *s); /* dito */
- int (*set_hw_volume)(pa_sink *s); /* dito */
- int (*get_hw_volume)(pa_sink *s); /* dito */
- int (*set_hw_mute)(pa_sink *s); /* dito */
- int (*get_hw_mute)(pa_sink *s); /* dito */
+ unsigned n_corked;
+ pa_source *monitor_source;
+
+ 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_asyncmsgq *asyncmsgq;
+ pa_rtpoll *rtpoll;
+
+ /* 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_memblock *silence;
void *userdata;
};
+PA_DECLARE_CLASS(pa_sink);
+#define PA_SINK(s) (pa_sink_cast(s))
+
+typedef enum pa_sink_message {
+ PA_SINK_MESSAGE_ADD_INPUT,
+ PA_SINK_MESSAGE_REMOVE_INPUT,
+ PA_SINK_MESSAGE_GET_VOLUME,
+ PA_SINK_MESSAGE_SET_VOLUME,
+ PA_SINK_MESSAGE_GET_MUTE,
+ PA_SINK_MESSAGE_SET_MUTE,
+ PA_SINK_MESSAGE_GET_LATENCY,
+ PA_SINK_MESSAGE_SET_STATE,
+ PA_SINK_MESSAGE_PING,
+ PA_SINK_MESSAGE_REMOVE_INPUT_AND_BUFFER,
+ PA_SINK_MESSAGE_ATTACH,
+ PA_SINK_MESSAGE_DETACH,
+ PA_SINK_MESSAGE_MAX
+} pa_sink_message_t;
+
+/* 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);
-
-void pa_sink_disconnect(pa_sink* s);
-void pa_sink_unref(pa_sink*s);
-pa_sink* pa_sink_ref(pa_sink *s);
-
-int pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result);
-void pa_sink_render_full(pa_sink *s, size_t length, pa_memchunk *result);
-int pa_sink_render_into(pa_sink*s, pa_memchunk *target);
-void pa_sink_render_into_full(pa_sink *s, pa_memchunk *target);
+ pa_core *core,
+ const char *driver,
+ const char *name,
+ int namereg_fail,
+ const pa_sample_spec *spec,
+ const pa_channel_map *map);
+
+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_detach(pa_sink *s);
+void pa_sink_attach(pa_sink *s);
+
+/* May be called by everyone, from main context */
pa_usec_t pa_sink_get_latency(pa_sink *s);
-void pa_sink_notify(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);
-void pa_sink_set_owner(pa_sink *sink, pa_module *m);
+/* 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, pa_mixer_t m, const pa_cvolume *volume);
-const pa_cvolume *pa_sink_get_volume(pa_sink *sink, pa_mixer_t m);
-void pa_sink_set_mute(pa_sink *sink, pa_mixer_t m, int mute);
-int pa_sink_get_mute(pa_sink *sink, pa_mixer_t m);
+void pa_sink_set_volume(pa_sink *sink, const pa_cvolume *volume);
+const pa_cvolume *pa_sink_get_volume(pa_sink *sink);
+void pa_sink_set_mute(pa_sink *sink, pa_bool_t mute);
+pa_bool_t pa_sink_get_mute(pa_sink *sink);
-void pa_sink_set_description(pa_sink *s, const char *description);
+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 */
+#define pa_sink_get_state(s) ((s)->state)
+
+/* To be called exclusively by the sink driver, from IO context */
+
+void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result);
+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);
+
+int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk);
-unsigned pa_sink_used_by(pa_sink *s);
+void pa_sink_attach_within_thread(pa_sink *s);
+void pa_sink_detach_within_thread(pa_sink *s);
#endif
diff --git a/src/pulsecore/sioman.c b/src/pulsecore/sioman.c
index d3d7538e..8d4c6fa7 100644
--- a/src/pulsecore/sioman.c
+++ b/src/pulsecore/sioman.c
@@ -25,21 +25,17 @@
#include <config.h>
#endif
-#include <assert.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/atomic.h>
#include "sioman.h"
-static int stdio_inuse = 0;
+static pa_atomic_t stdio_inuse = PA_ATOMIC_INIT(0);
int pa_stdio_acquire(void) {
- if (stdio_inuse)
- return -1;
-
- stdio_inuse = 1;
- return 0;
+ return pa_atomic_cmpxchg(&stdio_inuse, 0, 1) ? 0 : -1;
}
void pa_stdio_release(void) {
- assert(stdio_inuse);
- stdio_inuse = 0;
+ pa_assert_se(pa_atomic_cmpxchg(&stdio_inuse, 1, 0));
}
diff --git a/src/pulsecore/socket-client.c b/src/pulsecore/socket-client.c
index b99c8025..5b5bc5ca 100644
--- a/src/pulsecore/socket-client.c
+++ b/src/pulsecore/socket-client.c
@@ -1,25 +1,25 @@
/* $Id$ */
/***
- This file is part of PulseAudio.
+ This file is part of PulseAudio.
- Copyright 2004-2006 Lennart Poettering
- Copyright 2006-2007 Pierre Ossman <ossman@cendio.se> for Cendio AB
+ Copyright 2004-2006 Lennart Poettering
+ Copyright 2006-2007 Pierre Ossman <ossman@cendio.se> for Cendio AB
- PulseAudio is free software; you can redistribute it and/or modify
- it under the terms of the GNU Lesser General Public License as
- published by the Free Software Foundation; either version 2.1 of the
- License, or (at your option) any later version.
+ PulseAudio is 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.
+ 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.
+ 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
@@ -32,7 +32,6 @@
#include <stdio.h>
#include <errno.h>
#include <string.h>
-#include <assert.h>
#include <stdlib.h>
#ifdef HAVE_SYS_SOCKET_H
@@ -55,23 +54,24 @@
#include <asyncns.h>
#endif
-#include "winsock.h"
-
#include <pulse/timeval.h>
#include <pulse/xmalloc.h>
+#include <pulsecore/winsock.h>
#include <pulsecore/core-error.h>
#include <pulsecore/socket-util.h>
#include <pulsecore/core-util.h>
#include <pulsecore/log.h>
#include <pulsecore/parseaddr.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/refcnt.h>
#include "socket-client.h"
#define CONNECT_TIMEOUT 5
struct pa_socket_client {
- int ref;
+ PA_REFCNT_DECLARE;
pa_mainloop_api *mainloop;
int fd;
pa_io_event *io_event;
@@ -89,10 +89,10 @@ struct pa_socket_client {
static pa_socket_client*pa_socket_client_new(pa_mainloop_api *m) {
pa_socket_client *c;
- assert(m);
+ pa_assert(m);
- c = pa_xmalloc(sizeof(pa_socket_client));
- c->ref = 1;
+ c = pa_xnew(pa_socket_client, 1);
+ PA_REFCNT_INIT(c);
c->mainloop = m;
c->fd = -1;
c->io_event = NULL;
@@ -112,7 +112,7 @@ static pa_socket_client*pa_socket_client_new(pa_mainloop_api *m) {
}
static void free_events(pa_socket_client *c) {
- assert(c);
+ pa_assert(c);
if (c->io_event) {
c->mainloop->io_free(c->io_event);
@@ -134,7 +134,10 @@ static void do_call(pa_socket_client *c) {
pa_iochannel *io = NULL;
int error;
socklen_t lerror;
- assert(c && c->callback);
+
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+ pa_assert(c->callback);
pa_socket_client_ref(c);
@@ -153,13 +156,13 @@ static void do_call(pa_socket_client *c) {
}
if (error != 0) {
- pa_log_debug("connect(): %s", pa_cstrerror(errno));
+ pa_log_debug("connect(): %s", pa_cstrerror(error));
errno = error;
goto finish;
}
io = pa_iochannel_new(c->mainloop, c->fd, c->fd);
- assert(io);
+ pa_assert(io);
finish:
if (!io && c->fd >= 0)
@@ -168,7 +171,7 @@ finish:
free_events(c);
- assert(c->callback);
+ pa_assert(c->callback);
c->callback(c, io, c->userdata);
pa_socket_client_unref(c);
@@ -176,21 +179,36 @@ finish:
static void connect_fixed_cb(pa_mainloop_api *m, pa_defer_event *e, void *userdata) {
pa_socket_client *c = userdata;
- assert(m && c && c->defer_event == e);
+
+ pa_assert(m);
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+ pa_assert(c->defer_event == e);
+
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) {
pa_socket_client *c = userdata;
- assert(m && c && c->io_event == e && fd >= 0);
+
+ pa_assert(m);
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+ pa_assert(c->io_event == e);
+ pa_assert(fd >= 0);
+
do_call(c);
}
static int do_connect(pa_socket_client *c, const struct sockaddr *sa, socklen_t len) {
int r;
- assert(c && sa && len);
- pa_make_nonblock_fd(c->fd);
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+ pa_assert(sa);
+ pa_assert(len > 0);
+
+ pa_make_fd_nonblock(c->fd);
if ((r = connect(c->fd, sa, len)) < 0) {
#ifdef OS_IS_WIN32
@@ -203,19 +221,18 @@ static int do_connect(pa_socket_client *c, const struct sockaddr *sa, socklen_t
return -1;
}
- c->io_event = c->mainloop->io_new(c->mainloop, c->fd, PA_IO_EVENT_OUTPUT, connect_io_cb, c);
- assert(c->io_event);
- } else {
- c->defer_event = c->mainloop->defer_new(c->mainloop, connect_fixed_cb, c);
- assert(c->defer_event);
- }
+ 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));
return 0;
}
pa_socket_client* pa_socket_client_new_ipv4(pa_mainloop_api *m, uint32_t address, uint16_t port) {
struct sockaddr_in sa;
- assert(m && port > 0);
+
+ pa_assert(m);
+ pa_assert(port > 0);
memset(&sa, 0, sizeof(sa));
sa.sin_family = AF_INET;
@@ -229,7 +246,9 @@ pa_socket_client* pa_socket_client_new_ipv4(pa_mainloop_api *m, uint32_t address
pa_socket_client* pa_socket_client_new_unix(pa_mainloop_api *m, const char *filename) {
struct sockaddr_un sa;
- assert(m && filename);
+
+ pa_assert(m);
+ pa_assert(filename);
memset(&sa, 0, sizeof(sa));
sa.sun_family = AF_UNIX;
@@ -248,9 +267,9 @@ pa_socket_client* pa_socket_client_new_unix(pa_mainloop_api *m, const char *file
#endif /* HAVE_SYS_UN_H */
static int sockaddr_prepare(pa_socket_client *c, const struct sockaddr *sa, size_t salen) {
- assert(c);
- assert(sa);
- assert(salen);
+ pa_assert(c);
+ pa_assert(sa);
+ pa_assert(salen);
switch (sa->sa_family) {
case AF_UNIX:
@@ -274,11 +293,11 @@ static int sockaddr_prepare(pa_socket_client *c, const struct sockaddr *sa, size
return -1;
}
- pa_fd_set_cloexec(c->fd, 1);
+ pa_make_fd_cloexec(c->fd);
if (sa->sa_family == AF_INET || sa->sa_family == AF_INET6)
- pa_socket_tcp_low_delay(c->fd);
+ pa_make_tcp_socket_low_delay(c->fd);
else
- pa_socket_low_delay(c->fd);
+ pa_make_socket_low_delay(c->fd);
if (do_connect(c, sa, salen) < 0)
return -1;
@@ -288,9 +307,12 @@ static int sockaddr_prepare(pa_socket_client *c, const struct sockaddr *sa, size
pa_socket_client* pa_socket_client_new_sockaddr(pa_mainloop_api *m, const struct sockaddr *sa, size_t salen) {
pa_socket_client *c;
- assert(m && sa);
- c = pa_socket_client_new(m);
- assert(c);
+
+ pa_assert(m);
+ pa_assert(sa);
+ pa_assert(salen > 0);
+
+ pa_assert_se(c = pa_socket_client_new(m));
if (sockaddr_prepare(c, sa, salen) < 0)
goto fail;
@@ -300,12 +322,11 @@ pa_socket_client* pa_socket_client_new_sockaddr(pa_mainloop_api *m, const struct
fail:
pa_socket_client_unref(c);
return NULL;
-
}
static void socket_client_free(pa_socket_client *c) {
- assert(c && c->mainloop);
-
+ pa_assert(c);
+ pa_assert(c->mainloop);
free_events(c);
@@ -325,20 +346,25 @@ static void socket_client_free(pa_socket_client *c) {
}
void pa_socket_client_unref(pa_socket_client *c) {
- assert(c && c->ref >= 1);
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
- if (!(--(c->ref)))
+ if (PA_REFCNT_DEC(c) <= 0)
socket_client_free(c);
}
pa_socket_client* pa_socket_client_ref(pa_socket_client *c) {
- assert(c && c->ref >= 1);
- c->ref++;
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+ PA_REFCNT_INC(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) {
- assert(c);
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
c->callback = on_connection;
c->userdata = userdata;
}
@@ -346,6 +372,10 @@ void pa_socket_client_set_callback(pa_socket_client *c, void (*on_connection)(pa
pa_socket_client* pa_socket_client_new_ipv6(pa_mainloop_api *m, uint8_t address[16], uint16_t port) {
struct sockaddr_in6 sa;
+ pa_assert(m);
+ pa_assert(address);
+ pa_assert(port > 0);
+
memset(&sa, 0, sizeof(sa));
sa.sin6_family = AF_INET6;
sa.sin6_port = htons(port);
@@ -360,7 +390,12 @@ static void asyncns_cb(pa_mainloop_api*m, pa_io_event *e, int fd, PA_GCC_UNUSED
pa_socket_client *c = userdata;
struct addrinfo *res = NULL;
int ret;
- assert(m && c && c->asyncns_io_event == e && fd >= 0);
+
+ pa_assert(m);
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+ pa_assert(c->asyncns_io_event == e);
+ pa_assert(fd >= 0);
if (asyncns_wait(c->asyncns, 0) < 0)
goto fail;
@@ -397,10 +432,11 @@ fail:
static void timeout_cb(pa_mainloop_api *m, pa_time_event *e, const struct timeval *tv, void *userdata) {
pa_socket_client *c = userdata;
- assert(m);
- assert(e);
- assert(tv);
- assert(c);
+
+ pa_assert(m);
+ pa_assert(e);
+ pa_assert(tv);
+ pa_assert(c);
if (c->fd >= 0) {
pa_close(c->fd);
@@ -413,8 +449,8 @@ static void timeout_cb(pa_mainloop_api *m, pa_time_event *e, const struct timeva
static void start_timeout(pa_socket_client *c) {
struct timeval tv;
- assert(c);
- assert(!c->timeout_event);
+ pa_assert(c);
+ pa_assert(!c->timeout_event);
pa_gettimeofday(&tv);
pa_timeval_add(&tv, CONNECT_TIMEOUT * 1000000);
@@ -424,7 +460,9 @@ static void start_timeout(pa_socket_client *c) {
pa_socket_client* pa_socket_client_new_string(pa_mainloop_api *m, const char*name, uint16_t default_port) {
pa_socket_client *c = NULL;
pa_parsed_address a;
- assert(m && name);
+
+ pa_assert(m);
+ pa_assert(name);
if (pa_parse_address(name, &a) < 0)
return NULL;
@@ -435,7 +473,7 @@ pa_socket_client* pa_socket_client_new_string(pa_mainloop_api *m, const char*nam
switch (a.type) {
case PA_PARSED_ADDRESS_UNIX:
if ((c = pa_socket_client_new_unix(m, a.path_or_host)))
- start_timeout(c);
+ start_timeout(c);
break;
case PA_PARSED_ADDRESS_TCP4: /* Fallthrough */
@@ -445,7 +483,7 @@ pa_socket_client* pa_socket_client_new_string(pa_mainloop_api *m, const char*nam
struct addrinfo hints;
char port[12];
- snprintf(port, sizeof(port), "%u", (unsigned) a.port);
+ pa_snprintf(port, sizeof(port), "%u", (unsigned) a.port);
memset(&hints, 0, sizeof(hints));
hints.ai_family = a.type == PA_PARSED_ADDRESS_TCP4 ? PF_INET : (a.type == PA_PARSED_ADDRESS_TCP6 ? PF_INET6 : PF_UNSPEC);
@@ -462,7 +500,7 @@ pa_socket_client* pa_socket_client_new_string(pa_mainloop_api *m, const char*nam
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);
- assert(c->asyncns_query);
+ pa_assert(c->asyncns_query);
start_timeout(c);
}
#else /* HAVE_LIBASYNCNS */
@@ -479,7 +517,7 @@ pa_socket_client* pa_socket_client_new_string(pa_mainloop_api *m, const char*nam
if (res->ai_addr) {
if ((c = pa_socket_client_new_sockaddr(m, res->ai_addr, res->ai_addrlen)))
start_timeout(c);
- }
+ }
freeaddrinfo(res);
#else /* HAVE_GETADDRINFO */
@@ -507,7 +545,7 @@ pa_socket_client* pa_socket_client_new_string(pa_mainloop_api *m, const char*nam
s.sin_port = htons(a.port);
if ((c = pa_socket_client_new_sockaddr(m, (struct sockaddr*)&s, sizeof(s))))
- start_timeout(c);
+ start_timeout(c);
#endif /* HAVE_GETADDRINFO */
}
#endif /* HAVE_LIBASYNCNS */
@@ -524,6 +562,8 @@ finish:
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) {
- assert(c);
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
return c->local;
}
diff --git a/src/pulsecore/socket-server.c b/src/pulsecore/socket-server.c
index b5a6dc31..162a1aac 100644
--- a/src/pulsecore/socket-server.c
+++ b/src/pulsecore/socket-server.c
@@ -27,7 +27,6 @@
#endif
#include <stdlib.h>
-#include <assert.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
@@ -72,12 +71,14 @@
#include <pulsecore/socket-util.h>
#include <pulsecore/core-util.h>
#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
#include <pulsecore/core-error.h>
+#include <pulsecore/refcnt.h>
#include "socket-server.h"
struct pa_socket_server {
- int ref;
+ PA_REFCNT_DECLARE;
int fd;
char *filename;
char *tcpwrap_service;
@@ -94,7 +95,14 @@ static void callback(pa_mainloop_api *mainloop, pa_io_event *e, int fd, PA_GCC_U
pa_socket_server *s = userdata;
pa_iochannel *io;
int nfd;
- assert(s && s->mainloop == mainloop && s->io_event == e && e && fd >= 0 && fd == s->fd);
+
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
+ pa_assert(s->mainloop == mainloop);
+ pa_assert(s->io_event == e);
+ pa_assert(e);
+ pa_assert(fd >= 0);
+ pa_assert(fd == s->fd);
pa_socket_server_ref(s);
@@ -103,7 +111,7 @@ static void callback(pa_mainloop_api *mainloop, pa_io_event *e, int fd, PA_GCC_U
goto finish;
}
- pa_fd_set_cloexec(nfd, 1);
+ pa_make_fd_cloexec(nfd);
if (!s->on_connection) {
pa_close(nfd);
@@ -129,12 +137,11 @@ static void callback(pa_mainloop_api *mainloop, pa_io_event *e, int fd, PA_GCC_U
/* There should be a check for socket type here */
if (s->type == SOCKET_SERVER_IPV4)
- pa_socket_tcp_low_delay(fd);
+ pa_make_tcp_socket_low_delay(fd);
else
- pa_socket_low_delay(fd);
+ pa_make_socket_low_delay(fd);
- io = pa_iochannel_new(s->mainloop, nfd, nfd);
- assert(io);
+ pa_assert_se(io = pa_iochannel_new(s->mainloop, nfd, nfd));
s->on_connection(s, io, s->userdata);
finish:
@@ -143,10 +150,12 @@ finish:
pa_socket_server* pa_socket_server_new(pa_mainloop_api *m, int fd) {
pa_socket_server *s;
- assert(m && fd >= 0);
- s = pa_xmalloc(sizeof(pa_socket_server));
- s->ref = 1;
+ pa_assert(m);
+ pa_assert(fd >= 0);
+
+ s = pa_xnew(pa_socket_server, 1);
+ PA_REFCNT_INIT(s);
s->fd = fd;
s->filename = NULL;
s->on_connection = NULL;
@@ -154,8 +163,7 @@ pa_socket_server* pa_socket_server_new(pa_mainloop_api *m, int fd) {
s->tcpwrap_service = NULL;
s->mainloop = m;
- s->io_event = m->io_new(m, fd, PA_IO_EVENT_INPUT, callback, s);
- assert(s->io_event);
+ pa_assert_se(s->io_event = m->io_new(m, fd, PA_IO_EVENT_INPUT, callback, s));
s->type = SOCKET_SERVER_GENERIC;
@@ -163,8 +171,10 @@ pa_socket_server* pa_socket_server_new(pa_mainloop_api *m, int fd) {
}
pa_socket_server* pa_socket_server_ref(pa_socket_server *s) {
- assert(s && s->ref >= 1);
- s->ref++;
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+ PA_REFCNT_INC(s);
return s;
}
@@ -175,20 +185,21 @@ pa_socket_server* pa_socket_server_new_unix(pa_mainloop_api *m, const char *file
struct sockaddr_un sa;
pa_socket_server *s;
- assert(m && filename);
+ pa_assert(m);
+ pa_assert(filename);
if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) {
pa_log("socket(): %s", pa_cstrerror(errno));
goto fail;
}
- pa_fd_set_cloexec(fd, 1);
+ pa_make_fd_cloexec(fd);
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_socket_low_delay(fd);
+ pa_make_socket_low_delay(fd);
if (bind(fd, (struct sockaddr*) &sa, SUN_LEN(&sa)) < 0) {
pa_log("bind(): %s", pa_cstrerror(errno));
@@ -206,8 +217,7 @@ pa_socket_server* pa_socket_server_new_unix(pa_mainloop_api *m, const char *file
goto fail;
}
- s = pa_socket_server_new(m, fd);
- assert(s);
+ pa_assert_se(s = pa_socket_server_new(m, fd));
s->filename = pa_xstrdup(filename);
s->type = SOCKET_SERVER_UNIX;
@@ -235,21 +245,22 @@ pa_socket_server* pa_socket_server_new_ipv4(pa_mainloop_api *m, uint32_t address
struct sockaddr_in sa;
int on = 1;
- assert(m && port);
+ pa_assert(m);
+ pa_assert(port);
if ((fd = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
pa_log("socket(PF_INET): %s", pa_cstrerror(errno));
goto fail;
}
- pa_fd_set_cloexec(fd, 1);
+ pa_make_fd_cloexec(fd);
#ifdef SO_REUSEADDR
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
pa_log("setsockopt(): %s", pa_cstrerror(errno));
#endif
- pa_socket_tcp_low_delay(fd);
+ pa_make_tcp_socket_low_delay(fd);
memset(&sa, 0, sizeof(sa));
sa.sin_family = AF_INET;
@@ -286,14 +297,15 @@ pa_socket_server* pa_socket_server_new_ipv6(pa_mainloop_api *m, const uint8_t ad
struct sockaddr_in6 sa;
int on = 1;
- assert(m && port);
+ pa_assert(m);
+ pa_assert(port > 0);
if ((fd = socket(PF_INET6, SOCK_STREAM, 0)) < 0) {
pa_log("socket(PF_INET6): %s", pa_cstrerror(errno));
goto fail;
}
- pa_fd_set_cloexec(fd, 1);
+ pa_make_fd_cloexec(fd);
#ifdef IPV6_V6ONLY
if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) < 0)
@@ -305,7 +317,7 @@ pa_socket_server* pa_socket_server_new_ipv6(pa_mainloop_api *m, const uint8_t ad
pa_log("setsockopt(SOL_SOCKET, SO_REUSEADDR, 1): %s", pa_cstrerror(errno));
#endif
- pa_socket_tcp_low_delay(fd);
+ pa_make_tcp_socket_low_delay(fd);
memset(&sa, 0, sizeof(sa));
sa.sin6_family = AF_INET6;
@@ -337,29 +349,29 @@ fail:
}
pa_socket_server* pa_socket_server_new_ipv4_loopback(pa_mainloop_api *m, uint16_t port, const char *tcpwrap_service) {
- assert(m);
- assert(port > 0);
+ pa_assert(m);
+ pa_assert(port > 0);
return pa_socket_server_new_ipv4(m, INADDR_LOOPBACK, port, tcpwrap_service);
}
pa_socket_server* pa_socket_server_new_ipv6_loopback(pa_mainloop_api *m, uint16_t port, const char *tcpwrap_service) {
- assert(m);
- assert(port > 0);
+ pa_assert(m);
+ pa_assert(port > 0);
return pa_socket_server_new_ipv6(m, in6addr_loopback.s6_addr, port, tcpwrap_service);
}
pa_socket_server* pa_socket_server_new_ipv4_any(pa_mainloop_api *m, uint16_t port, const char *tcpwrap_service) {
- assert(m);
- assert(port > 0);
+ pa_assert(m);
+ pa_assert(port > 0);
return pa_socket_server_new_ipv4(m, INADDR_ANY, port, tcpwrap_service);
}
pa_socket_server* pa_socket_server_new_ipv6_any(pa_mainloop_api *m, uint16_t port, const char *tcpwrap_service) {
- assert(m);
- assert(port > 0);
+ pa_assert(m);
+ pa_assert(port > 0);
return pa_socket_server_new_ipv6(m, in6addr_any.s6_addr, port, tcpwrap_service);
}
@@ -367,9 +379,9 @@ pa_socket_server* pa_socket_server_new_ipv6_any(pa_mainloop_api *m, uint16_t por
pa_socket_server* pa_socket_server_new_ipv4_string(pa_mainloop_api *m, const char *name, uint16_t port, const char *tcpwrap_service) {
struct in_addr ipv4;
- assert(m);
- assert(name);
- assert(port > 0);
+ pa_assert(m);
+ pa_assert(name);
+ pa_assert(port > 0);
if (inet_pton(AF_INET, name, &ipv4) > 0)
return pa_socket_server_new_ipv4(m, ntohl(ipv4.s_addr), port, tcpwrap_service);
@@ -380,9 +392,9 @@ pa_socket_server* pa_socket_server_new_ipv4_string(pa_mainloop_api *m, const cha
pa_socket_server* pa_socket_server_new_ipv6_string(pa_mainloop_api *m, const char *name, uint16_t port, const char *tcpwrap_service) {
struct in6_addr ipv6;
- assert(m);
- assert(name);
- assert(port > 0);
+ pa_assert(m);
+ pa_assert(name);
+ pa_assert(port > 0);
if (inet_pton(AF_INET6, name, &ipv6) > 0)
return pa_socket_server_new_ipv6(m, ipv6.s6_addr, port, tcpwrap_service);
@@ -391,7 +403,7 @@ pa_socket_server* pa_socket_server_new_ipv6_string(pa_mainloop_api *m, const cha
}
static void socket_server_free(pa_socket_server*s) {
- assert(s);
+ pa_assert(s);
if (s->filename) {
unlink(s->filename);
@@ -407,21 +419,26 @@ static void socket_server_free(pa_socket_server*s) {
}
void pa_socket_server_unref(pa_socket_server *s) {
- assert(s && s->ref >= 1);
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
- if (!(--(s->ref)))
+ if (PA_REFCNT_DEC(s) <= 0)
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) {
- assert(s && s->ref >= 1);
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
s->on_connection = on_connection;
s->userdata = userdata;
}
char *pa_socket_server_get_address(pa_socket_server *s, char *c, size_t l) {
- assert(s && c && l > 0);
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
+ pa_assert(c);
+ pa_assert(l > 0);
switch (s->type) {
case SOCKET_SERVER_IPV6: {
@@ -438,14 +455,14 @@ char *pa_socket_server_get_address(pa_socket_server *s, char *c, size_t l) {
if (!pa_get_fqdn(fqdn, sizeof(fqdn)))
return NULL;
- snprintf(c, l, "tcp6:%s:%u", fqdn, (unsigned) ntohs(sa.sin6_port));
+ pa_snprintf(c, l, "tcp6:%s:%u", fqdn, (unsigned) ntohs(sa.sin6_port));
} else if (memcmp(&in6addr_loopback, &sa.sin6_addr, sizeof(in6addr_loopback)) == 0) {
char hn[256];
if (!pa_get_host_name(hn, sizeof(hn)))
return NULL;
- snprintf(c, l, "{%s}tcp6:localhost:%u", hn, (unsigned) ntohs(sa.sin6_port));
+ pa_snprintf(c, l, "{%s}tcp6:localhost:%u", hn, (unsigned) ntohs(sa.sin6_port));
} else {
char ip[INET6_ADDRSTRLEN];
@@ -454,7 +471,7 @@ char *pa_socket_server_get_address(pa_socket_server *s, char *c, size_t l) {
return NULL;
}
- snprintf(c, l, "tcp6:[%s]:%u", ip, (unsigned) ntohs(sa.sin6_port));
+ pa_snprintf(c, l, "tcp6:[%s]:%u", ip, (unsigned) ntohs(sa.sin6_port));
}
return c;
@@ -474,13 +491,13 @@ char *pa_socket_server_get_address(pa_socket_server *s, char *c, size_t l) {
if (!pa_get_fqdn(fqdn, sizeof(fqdn)))
return NULL;
- snprintf(c, l, "tcp:%s:%u", fqdn, (unsigned) ntohs(sa.sin_port));
+ pa_snprintf(c, l, "tcp:%s:%u", fqdn, (unsigned) ntohs(sa.sin_port));
} else if (sa.sin_addr.s_addr == INADDR_LOOPBACK) {
char hn[256];
if (!pa_get_host_name(hn, sizeof(hn)))
return NULL;
- snprintf(c, l, "{%s}tcp:localhost:%u", hn, (unsigned) ntohs(sa.sin_port));
+ pa_snprintf(c, l, "{%s}tcp:localhost:%u", hn, (unsigned) ntohs(sa.sin_port));
} else {
char ip[INET_ADDRSTRLEN];
@@ -489,7 +506,7 @@ char *pa_socket_server_get_address(pa_socket_server *s, char *c, size_t l) {
return NULL;
}
- snprintf(c, l, "tcp:[%s]:%u", ip, (unsigned) ntohs(sa.sin_port));
+ pa_snprintf(c, l, "tcp:[%s]:%u", ip, (unsigned) ntohs(sa.sin_port));
}
@@ -505,7 +522,7 @@ char *pa_socket_server_get_address(pa_socket_server *s, char *c, size_t l) {
if (!pa_get_host_name(hn, sizeof(hn)))
return NULL;
- snprintf(c, l, "{%s}unix:%s", hn, s->filename);
+ pa_snprintf(c, l, "{%s}unix:%s", hn, s->filename);
return c;
}
diff --git a/src/pulsecore/socket-util.c b/src/pulsecore/socket-util.c
index 673058e2..456accb8 100644
--- a/src/pulsecore/socket-util.c
+++ b/src/pulsecore/socket-util.c
@@ -31,7 +31,6 @@
#include <stdlib.h>
#include <signal.h>
#include <errno.h>
-#include <assert.h>
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
@@ -75,19 +74,19 @@
#include <pulsecore/core-error.h>
#include <pulsecore/core-util.h>
#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
#include "socket-util.h"
void pa_socket_peer_to_string(int fd, char *c, size_t l) {
struct stat st;
- assert(c && l && fd >= 0);
+ pa_assert(fd >= 0);
+ pa_assert(c);
+ pa_assert(l > 0);
#ifndef OS_IS_WIN32
- if (fstat(fd, &st) < 0) {
- snprintf(c, l, "Invalid client fd");
- return;
- }
+ pa_assert_se(fstat(fd, &st) == 0);
#endif
#ifndef OS_IS_WIN32
@@ -108,12 +107,12 @@ void pa_socket_peer_to_string(int fd, char *c, size_t l) {
if (sa.sa.sa_family == AF_INET) {
uint32_t ip = ntohl(sa.in.sin_addr.s_addr);
- snprintf(c, l, "TCP/IP client from %i.%i.%i.%i:%u",
- ip >> 24,
- (ip >> 16) & 0xFF,
- (ip >> 8) & 0xFF,
- ip & 0xFF,
- ntohs(sa.in.sin_port));
+ pa_snprintf(c, l, "TCP/IP client from %i.%i.%i.%i:%u",
+ ip >> 24,
+ (ip >> 16) & 0xFF,
+ (ip >> 8) & 0xFF,
+ ip & 0xFF,
+ ntohs(sa.in.sin_port));
return;
} else if (sa.sa.sa_family == AF_INET6) {
char buf[INET6_ADDRSTRLEN];
@@ -121,94 +120,107 @@ void pa_socket_peer_to_string(int fd, char *c, size_t l) {
res = inet_ntop(AF_INET6, &sa.in6.sin6_addr, buf, sizeof(buf));
if (res) {
- snprintf(c, l, "TCP/IP client from [%s]:%u", buf, ntohs(sa.in6.sin6_port));
+ pa_snprintf(c, l, "TCP/IP client from [%s]:%u", buf, ntohs(sa.in6.sin6_port));
return;
}
#ifdef HAVE_SYS_UN_H
} else if (sa.sa.sa_family == AF_UNIX) {
- snprintf(c, l, "UNIX socket client");
+ pa_snprintf(c, l, "UNIX socket client");
return;
#endif
}
}
#ifndef OS_IS_WIN32
- snprintf(c, l, "Unknown network client");
+ pa_snprintf(c, l, "Unknown network client");
return;
} else if (S_ISCHR(st.st_mode) && (fd == 0 || fd == 1)) {
- snprintf(c, l, "STDIN/STDOUT client");
+ pa_snprintf(c, l, "STDIN/STDOUT client");
return;
}
#endif /* OS_IS_WIN32 */
- snprintf(c, l, "Unknown client");
+ pa_snprintf(c, l, "Unknown client");
}
-int pa_socket_low_delay(int fd) {
+void pa_make_socket_low_delay(int fd) {
+
#ifdef SO_PRIORITY
int priority;
- assert(fd >= 0);
+ pa_assert(fd >= 0);
- priority = 7;
+ priority = 6;
if (setsockopt(fd, SOL_SOCKET, SO_PRIORITY, (void*)&priority, sizeof(priority)) < 0)
- return -1;
+ pa_log_warn("SO_PRIORITY failed: %s", pa_cstrerror(errno));
#endif
-
- return 0;
}
-int pa_socket_tcp_low_delay(int fd) {
- int ret, tos, on;
+void pa_make_tcp_socket_low_delay(int fd) {
+ pa_assert(fd >= 0);
- assert(fd >= 0);
-
- ret = pa_socket_low_delay(fd);
-
- on = 1;
- tos = 0;
+ pa_make_socket_low_delay(fd);
#if defined(SOL_TCP) || defined(IPPROTO_TCP)
+ {
+ int on = 1;
#if defined(SOL_TCP)
- if (setsockopt(fd, SOL_TCP, TCP_NODELAY, (void*)&on, sizeof(on)) < 0)
+ if (setsockopt(fd, SOL_TCP, TCP_NODELAY, (void*)&on, sizeof(on)) < 0)
#else
- if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (void*)&on, sizeof(on)) < 0)
+ if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (void*)&on, sizeof(on)) < 0)
#endif
- ret = -1;
+ pa_log_warn("TCP_NODELAY failed: %s", pa_cstrerror(errno));
+ }
#endif
-#if defined(IPTOS_LOWDELAY) && defined(IP_TOS) && (defined(SOL_IP) || \
- defined(IPPROTO_IP))
- tos = IPTOS_LOWDELAY;
+#if defined(IPTOS_LOWDELAY) && defined(IP_TOS) && (defined(SOL_IP) || defined(IPPROTO_IP))
+ {
+ int tos = IPTOS_LOWDELAY;
#ifdef SOL_IP
- if (setsockopt(fd, SOL_IP, IP_TOS, (void*)&tos, sizeof(tos)) < 0)
+ if (setsockopt(fd, SOL_IP, IP_TOS, (void*)&tos, sizeof(tos)) < 0)
#else
- if (setsockopt(fd, IPPROTO_IP, IP_TOS, (void*)&tos, sizeof(tos)) < 0)
+ if (setsockopt(fd, IPPROTO_IP, IP_TOS, (void*)&tos, sizeof(tos)) < 0)
#endif
- ret = -1;
+ pa_log_warn("IP_TOS failed: %s", pa_cstrerror(errno));
+ }
#endif
+}
- return ret;
+void pa_make_udp_socket_low_delay(int fd) {
+ pa_assert(fd >= 0);
+ pa_make_socket_low_delay(fd);
+
+#if defined(IPTOS_LOWDELAY) && defined(IP_TOS) && (defined(SOL_IP) || defined(IPPROTO_IP))
+ {
+ int tos = IPTOS_LOWDELAY;
+#ifdef SOL_IP
+ if (setsockopt(fd, SOL_IP, IP_TOS, (void*)&tos, sizeof(tos)) < 0)
+#else
+ if (setsockopt(fd, IPPROTO_IP, IP_TOS, (void*)&tos, sizeof(tos)) < 0)
+#endif
+ pa_log_warn("IP_TOS failed: %s", pa_cstrerror(errno));
+ }
+#endif
}
int pa_socket_set_rcvbuf(int fd, size_t l) {
- assert(fd >= 0);
+ pa_assert(fd >= 0);
-/* if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (void*)&l, sizeof(l)) < 0) { */
-/* pa_log("SO_RCVBUF: %s", strerror(errno)); */
-/* return -1; */
-/* } */
+ if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (void*)&l, sizeof(l)) < 0) {
+ pa_log_warn("SO_RCVBUF: %s", pa_cstrerror(errno));
+ return -1;
+ }
return 0;
}
int pa_socket_set_sndbuf(int fd, size_t l) {
- assert(fd >= 0);
+ pa_assert(fd >= 0);
-/* if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (void*)&l, sizeof(l)) < 0) { */
-/* pa_log("SO_SNDBUF: %s", strerror(errno)); */
-/* return -1; */
-/* } */
+ if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (void*)&l, sizeof(l)) < 0) {
+ pa_log("SO_SNDBUF: %s", pa_cstrerror(errno));
+ return -1;
+ }
return 0;
}
@@ -219,6 +231,8 @@ int pa_unix_socket_is_stale(const char *fn) {
struct sockaddr_un sa;
int fd = -1, ret = -1;
+ pa_assert(fn);
+
if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) {
pa_log("socket(): %s", pa_cstrerror(errno));
goto finish;
@@ -244,6 +258,8 @@ finish:
int pa_unix_socket_remove_stale(const char *fn) {
int r;
+ pa_assert(fn);
+
if ((r = pa_unix_socket_is_stale(fn)) < 0)
return errno != ENOENT ? -1 : 0;
diff --git a/src/pulsecore/socket-util.h b/src/pulsecore/socket-util.h
index 616c40ac..a0344c68 100644
--- a/src/pulsecore/socket-util.h
+++ b/src/pulsecore/socket-util.h
@@ -29,8 +29,9 @@
void pa_socket_peer_to_string(int fd, char *c, size_t l);
-int pa_socket_low_delay(int fd);
-int pa_socket_tcp_low_delay(int fd);
+void pa_make_socket_low_delay(int fd);
+void pa_make_tcp_socket_low_delay(int fd);
+void pa_make_udp_socket_low_delay(int fd);
int pa_socket_set_sndbuf(int fd, size_t l);
int pa_socket_set_rcvbuf(int fd, size_t l);
diff --git a/src/pulsecore/sound-file-stream.c b/src/pulsecore/sound-file-stream.c
index 7a43c743..bb1f3e9a 100644
--- a/src/pulsecore/sound-file-stream.c
+++ b/src/pulsecore/sound-file-stream.c
@@ -26,100 +26,196 @@
#endif
#include <stdlib.h>
-#include <assert.h>
#include <stdio.h>
#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
#include <sndfile.h>
#include <pulse/xmalloc.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 "sound-file-stream.h"
-#define BUF_SIZE (1024*10)
-
-struct userdata {
+typedef struct file_stream {
+ pa_msgobject parent;
+ pa_core *core;
SNDFILE *sndfile;
pa_sink_input *sink_input;
pa_memchunk memchunk;
sf_count_t (*readf_function)(SNDFILE *sndfile, void *ptr, sf_count_t frames);
+ size_t drop;
+} file_stream;
+
+enum {
+ FILE_STREAM_MESSAGE_UNLINK
};
-static void free_userdata(struct userdata *u) {
- assert(u);
- if (u->sink_input) {
- pa_sink_input_disconnect(u->sink_input);
- pa_sink_input_unref(u->sink_input);
- }
+PA_DECLARE_CLASS(file_stream);
+#define FILE_STREAM(o) (file_stream_cast(o))
+static PA_DEFINE_CHECK_TYPE(file_stream, pa_msgobject);
+
+static void file_stream_unlink(file_stream *u) {
+ pa_assert(u);
+
+ if (!u->sink_input)
+ return;
+
+ pa_sink_input_unlink(u->sink_input);
+
+ pa_sink_input_unref(u->sink_input);
+ u->sink_input = NULL;
+
+ /* Make sure we don't decrease the ref count twice. */
+ file_stream_unref(u);
+}
+
+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->sndfile)
sf_close(u->sndfile);
pa_xfree(u);
}
-static void sink_input_kill(pa_sink_input *i) {
- assert(i && i->userdata);
- free_userdata(i->userdata);
+static int file_stream_process_msg(pa_msgobject *o, int code, void*userdata, int64_t offset, pa_memchunk *chunk) {
+ file_stream *u = FILE_STREAM(o);
+ file_stream_assert_ref(u);
+
+ switch (code) {
+ case FILE_STREAM_MESSAGE_UNLINK:
+ file_stream_unlink(u);
+ break;
+ }
+
+ return 0;
+}
+
+static void sink_input_kill_cb(pa_sink_input *i) {
+ pa_sink_input_assert_ref(i);
+
+ file_stream_unlink(FILE_STREAM(i->userdata));
}
-static int sink_input_peek(pa_sink_input *i, pa_memchunk *chunk) {
- struct userdata *u;
- assert(i && chunk && i->userdata);
- u = i->userdata;
+static int sink_input_peek_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) {
+ file_stream *u;
+
+ pa_assert(i);
+ pa_assert(chunk);
+ u = FILE_STREAM(i->userdata);
+ file_stream_assert_ref(u);
+
+ if (!u->sndfile)
+ return -1;
+
+ for (;;) {
+
+ if (!u->memchunk.memblock) {
+
+ u->memchunk.memblock = pa_memblock_new(i->sink->core->mempool, length);
+ u->memchunk.index = 0;
+
+ if (u->readf_function) {
+ sf_count_t n;
+ void *p;
+ size_t fs = pa_frame_size(&i->sample_spec);
+
+ p = pa_memblock_acquire(u->memchunk.memblock);
+ n = u->readf_function(u->sndfile, p, length/fs);
+ pa_memblock_release(u->memchunk.memblock);
- if (!u->memchunk.memblock) {
- uint32_t fs = pa_frame_size(&i->sample_spec);
- sf_count_t n;
+ if (n <= 0)
+ n = 0;
- u->memchunk.memblock = pa_memblock_new(i->sink->core->mempool, BUF_SIZE);
- u->memchunk.index = 0;
+ u->memchunk.length = n * fs;
+ } else {
+ sf_count_t n;
+ void *p;
- if (u->readf_function) {
- if ((n = u->readf_function(u->sndfile, u->memchunk.memblock->data, BUF_SIZE/fs)) <= 0)
- n = 0;
+ p = pa_memblock_acquire(u->memchunk.memblock);
+ n = sf_read_raw(u->sndfile, p, length);
+ pa_memblock_release(u->memchunk.memblock);
- u->memchunk.length = n * fs;
- } else {
- if ((n = sf_read_raw(u->sndfile, u->memchunk.memblock->data, BUF_SIZE)) <= 0)
- n = 0;
+ if (n <= 0)
+ n = 0;
- u->memchunk.length = n;
+ u->memchunk.length = n;
+ }
+
+ if (u->memchunk.length <= 0) {
+
+ pa_memblock_unref(u->memchunk.memblock);
+ pa_memchunk_reset(&u->memchunk);
+
+ pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(u), FILE_STREAM_MESSAGE_UNLINK, NULL, 0, NULL, NULL);
+
+ sf_close(u->sndfile);
+ u->sndfile = NULL;
+
+ return -1;
+ }
}
- if (!u->memchunk.length) {
- free_userdata(u);
- return -1;
+ pa_assert(u->memchunk.memblock);
+ pa_assert(u->memchunk.length > 0);
+
+ if (u->drop < u->memchunk.length) {
+ u->memchunk.index += u->drop;
+ u->memchunk.length -= u->drop;
+ u->drop = 0;
+ break;
}
+
+ u->drop -= u->memchunk.length;
+ pa_memblock_unref(u->memchunk.memblock);
+ pa_memchunk_reset(&u->memchunk);
}
*chunk = u->memchunk;
pa_memblock_ref(chunk->memblock);
- assert(chunk->length);
+
+ pa_assert(chunk->length > 0);
+ pa_assert(u->drop <= 0);
+
return 0;
}
-static void sink_input_drop(pa_sink_input *i, const pa_memchunk*chunk, size_t length) {
- struct userdata *u;
- assert(i && chunk && length && i->userdata);
- u = i->userdata;
+static void sink_input_drop_cb(pa_sink_input *i, size_t length) {
+ file_stream *u;
- assert(!memcmp(chunk, &u->memchunk, sizeof(chunk)));
- assert(length <= u->memchunk.length);
+ pa_assert(i);
+ pa_assert(length > 0);
+ u = FILE_STREAM(i->userdata);
+ file_stream_assert_ref(u);
- u->memchunk.index += length;
- u->memchunk.length -= length;
+ if (u->memchunk.memblock) {
- if (u->memchunk.length <= 0) {
+ if (length < u->memchunk.length) {
+ u->memchunk.index += length;
+ u->memchunk.length -= length;
+ return;
+ }
+
+ length -= u->memchunk.length;
pa_memblock_unref(u->memchunk.memblock);
- u->memchunk.memblock = NULL;
- u->memchunk.index = u->memchunk.length = 0;
+ pa_memchunk_reset(&u->memchunk);
}
+
+ u->drop += length;
}
int pa_play_file(
@@ -127,28 +223,60 @@ int pa_play_file(
const char *fname,
const pa_cvolume *volume) {
- struct userdata *u = NULL;
+ file_stream *u = NULL;
SF_INFO sfinfo;
pa_sample_spec ss;
pa_sink_input_new_data data;
+ int fd;
- assert(sink);
- assert(fname);
+ pa_assert(sink);
+ pa_assert(fname);
- u = pa_xnew(struct userdata, 1);
+ u = pa_msgobject_new(file_stream);
+ u->parent.parent.free = file_stream_free;
+ u->parent.process_msg = file_stream_process_msg;
+ u->core = sink->core;
u->sink_input = NULL;
- u->memchunk.memblock = NULL;
- u->memchunk.index = u->memchunk.length = 0;
+ pa_memchunk_reset(&u->memchunk);
u->sndfile = NULL;
+ u->readf_function = NULL;
+ u->drop = 0;
memset(&sfinfo, 0, sizeof(sfinfo));
- if (!(u->sndfile = sf_open(fname, SFM_READ, &sfinfo))) {
- pa_log("Failed to open file %s", fname);
+ if ((fd = open(fname, O_RDONLY
+#ifdef O_NOCTTY
+ |O_NOCTTY
+#endif
+ )) < 0) {
+ pa_log("Failed to open file %s: %s", fname, pa_cstrerror(errno));
goto fail;
}
- u->readf_function = NULL;
+ /* FIXME: For now we just use posix_fadvise to avoid page faults
+ * when accessing the file data. Eventually we should move the
+ * file reader into the main event loop and pass the data over the
+ * asyncmsgq. */
+
+#ifdef HAVE_POSIX_FADVISE
+ if (posix_fadvise(fd, 0, 0, POSIX_FADV_SEQUENTIAL) < 0) {
+ pa_log_warn("POSIX_FADV_SEQUENTIAL failed: %s", pa_cstrerror(errno));
+ goto fail;
+ } else
+ pa_log_debug("POSIX_FADV_SEQUENTIAL succeeded.");
+
+ if (posix_fadvise(fd, 0, 0, POSIX_FADV_WILLNEED) < 0) {
+ pa_log_warn("POSIX_FADV_WILLNEED failed: %s", pa_cstrerror(errno));
+ goto fail;
+ } else
+ pa_log_debug("POSIX_FADV_WILLNEED succeeded.");
+#endif
+
+ if (!(u->sndfile = sf_open_fd(fd, SFM_READ, &sfinfo, 1))) {
+ pa_log("Failed to open file %s", fname);
+ pa_close(fd);
+ goto fail;
+ }
switch (sfinfo.format & 0xFF) {
case SF_FORMAT_PCM_16:
@@ -191,18 +319,21 @@ int pa_play_file(
if (!(u->sink_input = pa_sink_input_new(sink->core, &data, 0)))
goto fail;
- u->sink_input->peek = sink_input_peek;
- u->sink_input->drop = sink_input_drop;
- u->sink_input->kill = sink_input_kill;
+ 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_notify(u->sink_input->sink);
+ pa_sink_input_put(u->sink_input);
+
+ /* The reference to u is dangling here, because we want to keep
+ * this stream around until it is fully played. */
return 0;
fail:
if (u)
- free_userdata(u);
+ file_stream_unref(u);
return -1;
}
diff --git a/src/pulsecore/sound-file.c b/src/pulsecore/sound-file.c
index 69b543ab..7e88734c 100644
--- a/src/pulsecore/sound-file.c
+++ b/src/pulsecore/sound-file.c
@@ -26,31 +26,63 @@
#endif
#include <string.h>
-#include <assert.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
#include <sndfile.h>
#include <pulse/sample.h>
#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/core-error.h>
+#include <pulsecore/core-util.h>
#include "sound-file.h"
#include "core-scache.h"
-int pa_sound_file_load(pa_mempool *pool, const char *fname, pa_sample_spec *ss, pa_channel_map *map, pa_memchunk *chunk) {
- SNDFILE*sf = NULL;
+int pa_sound_file_load(
+ pa_mempool *pool,
+ const char *fname,
+ pa_sample_spec *ss,
+ pa_channel_map *map,
+ pa_memchunk *chunk) {
+
+ SNDFILE *sf = NULL;
SF_INFO sfinfo;
int ret = -1;
size_t l;
sf_count_t (*readf_function)(SNDFILE *sndfile, void *ptr, sf_count_t frames) = NULL;
- assert(fname && ss && chunk);
+ void *ptr = NULL;
+ int fd;
- chunk->memblock = NULL;
- chunk->index = chunk->length = 0;
+ pa_assert(fname);
+ pa_assert(ss);
+ pa_assert(chunk);
+ pa_memchunk_reset(chunk);
memset(&sfinfo, 0, sizeof(sfinfo));
- if (!(sf = sf_open(fname, SFM_READ, &sfinfo))) {
+ if ((fd = open(fname, O_RDONLY
+#ifdef O_NOCTTY
+ |O_NOCTTY
+#endif
+ )) < 0) {
+ pa_log("Failed to open file %s: %s", fname, pa_cstrerror(errno));
+ goto finish;
+ }
+
+#ifdef HAVE_POSIX_FADVISE
+ if (posix_fadvise(fd, 0, 0, POSIX_FADV_SEQUENTIAL) < 0) {
+ pa_log_warn("POSIX_FADV_SEQUENTIAL failed: %s", pa_cstrerror(errno));
+ goto finish;
+ } else
+ pa_log_debug("POSIX_FADV_SEQUENTIAL succeeded.");
+#endif
+
+ if (!(sf = sf_open_fd(fd, SFM_READ, &sfinfo, 1))) {
pa_log("Failed to open file %s", fname);
+ pa_close(fd);
goto finish;
}
@@ -89,18 +121,19 @@ int pa_sound_file_load(pa_mempool *pool, const char *fname, pa_sample_spec *ss,
if (map)
pa_channel_map_init_auto(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) * sfinfo.frames) > PA_SCACHE_ENTRY_SIZE_MAX) {
pa_log("File too large");
goto finish;
}
chunk->memblock = pa_memblock_new(pool, l);
- assert(chunk->memblock);
chunk->index = 0;
chunk->length = l;
- if ((readf_function && readf_function(sf, chunk->memblock->data, sfinfo.frames) != sfinfo.frames) ||
- (!readf_function && sf_read_raw(sf, chunk->memblock->data, l) != l)) {
+ 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)) {
pa_log("Premature file end");
goto finish;
}
@@ -112,21 +145,26 @@ finish:
if (sf)
sf_close(sf);
+ if (ptr)
+ pa_memblock_release(chunk->memblock);
+
if (ret != 0 && chunk->memblock)
pa_memblock_unref(chunk->memblock);
return ret;
-
}
int pa_sound_file_too_big_to_cache(const char *fname) {
+
SNDFILE*sf = NULL;
SF_INFO sfinfo;
pa_sample_spec ss;
+ pa_assert(fname);
+
if (!(sf = sf_open(fname, SFM_READ, &sfinfo))) {
pa_log("Failed to open file %s", fname);
- return 0;
+ return -1;
}
sf_close(sf);
@@ -156,8 +194,13 @@ int pa_sound_file_too_big_to_cache(const char *fname) {
ss.rate = sfinfo.samplerate;
ss.channels = 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) {
- pa_log("File too large %s", fname);
+ pa_log("File too large: %s", fname);
return 1;
}
diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c
index c7a9858c..2a902dc2 100644
--- a/src/pulsecore/source-output.c
+++ b/src/pulsecore/source-output.c
@@ -26,7 +26,6 @@
#endif
#include <stdio.h>
-#include <assert.h>
#include <stdlib.h>
#include <string.h>
@@ -39,14 +38,12 @@
#include "source-output.h"
-#define CHECK_VALIDITY_RETURN_NULL(condition) \
-do {\
-if (!(condition)) \
- return NULL; \
-} while (0)
+static PA_DEFINE_CHECK_TYPE(pa_source_output, pa_msgobject);
+
+static void source_output_free(pa_object* mo);
pa_source_output_new_data* pa_source_output_new_data_init(pa_source_output_new_data *data) {
- assert(data);
+ pa_assert(data);
memset(data, 0, sizeof(*data));
data->resample_method = PA_RESAMPLER_INVALID;
@@ -54,14 +51,14 @@ pa_source_output_new_data* pa_source_output_new_data_init(pa_source_output_new_d
}
void pa_source_output_new_data_set_channel_map(pa_source_output_new_data *data, const pa_channel_map *map) {
- assert(data);
+ pa_assert(data);
if ((data->channel_map_is_set = !!map))
data->channel_map = *map;
}
void pa_source_output_new_data_set_sample_spec(pa_source_output_new_data *data, const pa_sample_spec *spec) {
- assert(data);
+ pa_assert(data);
if ((data->sample_spec_is_set = !!spec))
data->sample_spec = *spec;
@@ -74,288 +71,415 @@ pa_source_output* pa_source_output_new(
pa_source_output *o;
pa_resampler *resampler = NULL;
- int r;
char st[PA_SAMPLE_SPEC_SNPRINT_MAX];
- assert(core);
- assert(data);
+ pa_assert(core);
+ pa_assert(data);
- if (!(flags & PA_SOURCE_OUTPUT_NO_HOOKS))
- if (pa_hook_fire(&core->hook_source_output_new, data) < 0)
- return NULL;
+ if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_NEW], data) < 0)
+ return NULL;
- CHECK_VALIDITY_RETURN_NULL(!data->driver || pa_utf8_valid(data->driver));
- CHECK_VALIDITY_RETURN_NULL(!data->name || pa_utf8_valid(data->name));
+ 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);
- CHECK_VALIDITY_RETURN_NULL(data->source);
- CHECK_VALIDITY_RETURN_NULL(data->source->state == PA_SOURCE_RUNNING);
+ pa_return_null_if_fail(data->source);
+ pa_return_null_if_fail(pa_source_get_state(data->source) != PA_SOURCE_UNLINKED);
if (!data->sample_spec_is_set)
data->sample_spec = data->source->sample_spec;
- CHECK_VALIDITY_RETURN_NULL(pa_sample_spec_valid(&data->sample_spec));
+ pa_return_null_if_fail(pa_sample_spec_valid(&data->sample_spec));
- if (!data->channel_map_is_set)
- pa_channel_map_init_auto(&data->channel_map, data->sample_spec.channels, PA_CHANNEL_MAP_DEFAULT);
+ if (!data->channel_map_is_set) {
+ if (data->source->channel_map.channels == data->sample_spec.channels)
+ data->channel_map = data->source->channel_map;
+ else
+ pa_channel_map_init_auto(&data->channel_map, data->sample_spec.channels, PA_CHANNEL_MAP_DEFAULT);
+ }
- CHECK_VALIDITY_RETURN_NULL(pa_channel_map_valid(&data->channel_map));
- CHECK_VALIDITY_RETURN_NULL(data->channel_map.channels == data->sample_spec.channels);
+ 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->resample_method == PA_RESAMPLER_INVALID)
data->resample_method = core->resample_method;
- CHECK_VALIDITY_RETURN_NULL(data->resample_method < PA_RESAMPLER_MAX);
+ pa_return_null_if_fail(data->resample_method < PA_RESAMPLER_MAX);
if (pa_idxset_size(data->source->outputs) >= PA_MAX_OUTPUTS_PER_SOURCE) {
pa_log("Failed to create source output: too many outputs per source.");
return NULL;
}
- if (!pa_sample_spec_equal(&data->sample_spec, &data->source->sample_spec) ||
- !pa_channel_map_equal(&data->channel_map, &data->source->channel_map))
+ if ((flags & PA_SOURCE_OUTPUT_VARIABLE_RATE) ||
+ !pa_sample_spec_equal(&data->sample_spec, &data->source->sample_spec) ||
+ !pa_channel_map_equal(&data->channel_map, &data->source->channel_map)) {
+
if (!(resampler = pa_resampler_new(
core->mempool,
&data->source->sample_spec, &data->source->channel_map,
&data->sample_spec, &data->channel_map,
- data->resample_method))) {
+ data->resample_method,
+ !!(flags & PA_SOURCE_OUTPUT_VARIABLE_RATE)))) {
pa_log_warn("Unsupported resampling operation.");
return NULL;
}
- o = pa_xnew(pa_source_output, 1);
- o->ref = 1;
- o->state = PA_SOURCE_OUTPUT_RUNNING;
+ data->resample_method = pa_resampler_get_method(resampler);
+ }
+
+ o = pa_msgobject_new(pa_source_output);
+ o->parent.parent.free = source_output_free;
+ o->parent.process_msg = pa_source_output_process_msg;
+
+ o->core = core;
+ o->state = PA_SOURCE_OUTPUT_INIT;
+ o->flags = flags;
o->name = pa_xstrdup(data->name);
o->driver = pa_xstrdup(data->driver);
o->module = data->module;
o->source = data->source;
o->client = data->client;
+ o->resample_method = data->resample_method;
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->userdata = NULL;
- o->resampler = resampler;
- o->resample_method = data->resample_method;
+ o->thread_info.state = o->state;
+ o->thread_info.attached = FALSE;
+ o->thread_info.sample_spec = o->sample_spec;
+ o->thread_info.resampler = resampler;
- r = pa_idxset_put(core->source_outputs, o, &o->index);
- assert(r == 0);
- r = pa_idxset_put(o->source->outputs, o, NULL);
- assert(r == 0);
+ pa_assert_se(pa_idxset_put(core->source_outputs, o, &o->index) == 0);
+ pa_assert_se(pa_idxset_put(o->source->outputs, pa_source_output_ref(o), NULL) == 0);
- pa_log_info("created %u \"%s\" on %s with sample spec %s",
+ pa_log_info("Created output %u \"%s\" on %s with sample spec %s",
o->index,
o->name,
o->source->name,
pa_sample_spec_snprint(st, sizeof(st), &o->sample_spec));
- pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_NEW, o->index);
-
- /* We do not call pa_source_notify() here, because the virtual
- * functions have not yet been initialized */
+ /* Don't forget to call pa_source_output_put! */
return o;
}
-void pa_source_output_disconnect(pa_source_output*o) {
- assert(o);
- assert(o->state != PA_SOURCE_OUTPUT_DISCONNECTED);
- assert(o->source);
- assert(o->source->core);
+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;
+
+ 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);
+
+ o->state = state;
+
+ if (state != PA_SOURCE_OUTPUT_UNLINKED)
+ pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_STATE_CHANGED], o);
+
+ return 0;
+}
+
+void pa_source_output_unlink(pa_source_output*o) {
+ pa_bool_t linked;
+ pa_assert(o);
+
+ /* See pa_sink_unlink() for a couple of comments how this function
+ * works */
+
+ pa_source_output_ref(o);
+
+ linked = PA_SOURCE_OUTPUT_LINKED(o->state);
+
+ if (linked)
+ pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK], o);
pa_idxset_remove_by_data(o->source->core->source_outputs, o, NULL);
- pa_idxset_remove_by_data(o->source->outputs, o, NULL);
+ if (pa_idxset_remove_by_data(o->source->outputs, o, NULL))
+ pa_source_output_unref(o);
- pa_subscription_post(o->source->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_REMOVE, o->index);
- o->source = NULL;
+ 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;
o->push = NULL;
o->kill = NULL;
o->get_latency = NULL;
+ o->attach = NULL;
+ o->detach = NULL;
+ o->suspend = NULL;
+
+ if (linked) {
+ pa_subscription_post(o->source->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_REMOVE, o->index);
+ pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK_POST], o);
+ }
- o->state = PA_SOURCE_OUTPUT_DISCONNECTED;
+ o->source = NULL;
+ pa_source_output_unref(o);
}
-static void source_output_free(pa_source_output* o) {
- assert(o);
+static void source_output_free(pa_object* mo) {
+ pa_source_output *o = PA_SOURCE_OUTPUT(mo);
+
+ pa_assert(pa_source_output_refcnt(o) == 0);
+
+ if (PA_SOURCE_OUTPUT_LINKED(o->state))
+ pa_source_output_unlink(o);
- if (o->state != PA_SOURCE_OUTPUT_DISCONNECTED)
- pa_source_output_disconnect(o);
+ pa_log_info("Freeing output %u \"%s\"", o->index, o->name);
- pa_log_info("freed %u \"%s\"", o->index, o->name);
+ pa_assert(!o->thread_info.attached);
- if (o->resampler)
- pa_resampler_free(o->resampler);
+ if (o->thread_info.resampler)
+ pa_resampler_free(o->thread_info.resampler);
pa_xfree(o->name);
pa_xfree(o->driver);
pa_xfree(o);
}
-void pa_source_output_unref(pa_source_output* o) {
- assert(o);
- assert(o->ref >= 1);
+void pa_source_output_put(pa_source_output *o) {
+ pa_source_output_assert_ref(o);
- if (!(--o->ref))
- source_output_free(o);
-}
+ pa_assert(o->state == PA_SOURCE_OUTPUT_INIT);
+ pa_assert(o->push);
-pa_source_output* pa_source_output_ref(pa_source_output *o) {
- assert(o);
- assert(o->ref >= 1);
+ o->thread_info.state = o->state = o->flags & PA_SOURCE_OUTPUT_START_CORKED ? PA_SOURCE_OUTPUT_CORKED : PA_SOURCE_OUTPUT_RUNNING;
- o->ref++;
- return o;
+ if (o->state == PA_SOURCE_OUTPUT_CORKED)
+ o->source->n_corked++;
+
+ 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_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);
}
void pa_source_output_kill(pa_source_output*o) {
- assert(o);
- assert(o->ref >= 1);
+ pa_source_output_assert_ref(o);
+ pa_assert(PA_SOURCE_OUTPUT_LINKED(o->state));
if (o->kill)
o->kill(o);
}
+pa_usec_t pa_source_output_get_latency(pa_source_output *o) {
+ pa_usec_t r = 0;
+
+ pa_source_output_assert_ref(o);
+ pa_assert(PA_SOURCE_OUTPUT_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;
+
+ if (o->get_latency)
+ r += o->get_latency(o);
+
+ return r;
+}
+
+/* Called from thread context */
void pa_source_output_push(pa_source_output *o, const pa_memchunk *chunk) {
pa_memchunk rchunk;
- assert(o);
- assert(chunk);
- assert(chunk->length);
- assert(o->push);
+ pa_source_output_assert_ref(o);
+ pa_assert(PA_SOURCE_OUTPUT_LINKED(o->thread_info.state));
+ pa_assert(chunk);
+ pa_assert(chunk->length);
- if (o->state == PA_SOURCE_OUTPUT_CORKED)
+ if (!o->push || o->state == PA_SOURCE_OUTPUT_CORKED)
return;
- if (!o->resampler) {
+ pa_assert(o->state == PA_SOURCE_OUTPUT_RUNNING);
+
+ if (!o->thread_info.resampler) {
o->push(o, chunk);
return;
}
- pa_resampler_run(o->resampler, chunk, &rchunk);
+ pa_resampler_run(o->thread_info.resampler, chunk, &rchunk);
if (!rchunk.length)
return;
- assert(rchunk.memblock);
+ pa_assert(rchunk.memblock);
o->push(o, &rchunk);
pa_memblock_unref(rchunk.memblock);
}
-void pa_source_output_set_name(pa_source_output *o, const char *name) {
- assert(o);
- assert(o->ref >= 1);
-
- if (!o->name && !name)
- return;
+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));
- if (o->name && name && !strcmp(o->name, name))
- return;
+ source_output_set_state(o, b ? PA_SOURCE_OUTPUT_CORKED : PA_SOURCE_OUTPUT_RUNNING);
+}
- pa_xfree(o->name);
- o->name = pa_xstrdup(name);
+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_return_val_if_fail(o->thread_info.resampler, -1);
- pa_subscription_post(o->source->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
-}
+ if (o->sample_spec.rate == rate)
+ return 0;
-pa_usec_t pa_source_output_get_latency(pa_source_output *o) {
- assert(o);
- assert(o->ref >= 1);
+ o->sample_spec.rate = rate;
- if (o->get_latency)
- return o->get_latency(o);
+ pa_asyncmsgq_post(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_SET_RATE, PA_UINT_TO_PTR(rate), 0, NULL, NULL);
+ pa_subscription_post(o->source->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
return 0;
}
-void pa_source_output_cork(pa_source_output *o, int b) {
- int n;
-
- assert(o);
- assert(o->ref >= 1);
+void pa_source_output_set_name(pa_source_output *o, const char *name) {
+ pa_source_output_assert_ref(o);
- if (o->state == PA_SOURCE_OUTPUT_DISCONNECTED)
+ if (!o->name && !name)
return;
- n = o->state == PA_SOURCE_OUTPUT_CORKED && !b;
+ if (o->name && name && !strcmp(o->name, name))
+ return;
- o->state = b ? PA_SOURCE_OUTPUT_CORKED : PA_SOURCE_OUTPUT_RUNNING;
+ pa_xfree(o->name);
+ o->name = pa_xstrdup(name);
- if (n)
- pa_source_notify(o->source);
+ if (PA_SOURCE_OUTPUT_LINKED(o->state)) {
+ pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_NAME_CHANGED], o);
+ pa_subscription_post(o->source->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
+ }
}
pa_resample_method_t pa_source_output_get_resample_method(pa_source_output *o) {
- assert(o);
- assert(o->ref >= 1);
+ pa_source_output_assert_ref(o);
- if (!o->resampler)
- return o->resample_method;
-
- return pa_resampler_get_method(o->resampler);
+ return o->resample_method;
}
int pa_source_output_move_to(pa_source_output *o, pa_source *dest) {
pa_source *origin;
pa_resampler *new_resampler = NULL;
- assert(o);
- assert(o->ref >= 1);
- assert(dest);
+ pa_source_output_assert_ref(o);
+ pa_assert(PA_SOURCE_OUTPUT_LINKED(o->state));
+ pa_source_assert_ref(dest);
origin = o->source;
if (dest == origin)
return 0;
+ if (o->flags & PA_SOURCE_OUTPUT_DONT_MOVE)
+ 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;
}
- if (o->resampler &&
+ if (o->thread_info.resampler &&
pa_sample_spec_equal(&origin->sample_spec, &dest->sample_spec) &&
pa_channel_map_equal(&origin->channel_map, &dest->channel_map))
/* Try to reuse the old resampler if possible */
- new_resampler = o->resampler;
+ new_resampler = o->thread_info.resampler;
- else if (!pa_sample_spec_equal(&o->sample_spec, &dest->sample_spec) ||
- !pa_channel_map_equal(&o->channel_map, &dest->channel_map)) {
+ else if ((o->flags & PA_SOURCE_OUTPUT_VARIABLE_RATE) ||
+ !pa_sample_spec_equal(&o->sample_spec, &dest->sample_spec) ||
+ !pa_channel_map_equal(&o->channel_map, &dest->channel_map)) {
- /* Okey, we need a new resampler for the new sink */
+ /* Okey, we need a new resampler for the new source */
if (!(new_resampler = pa_resampler_new(
dest->core->mempool,
&dest->sample_spec, &dest->channel_map,
&o->sample_spec, &o->channel_map,
- o->resample_method))) {
+ o->resample_method,
+ !!(o->flags & PA_SOURCE_OUTPUT_VARIABLE_RATE)))) {
pa_log_warn("Unsupported resampling operation.");
return -1;
}
}
+ pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE], o);
+
/* Okey, let's move it */
+ pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_REMOVE_OUTPUT, o, 0, NULL);
+
pa_idxset_remove_by_data(origin->outputs, o, NULL);
pa_idxset_put(dest->outputs, o, NULL);
o->source = dest;
+ if (pa_source_output_get_state(o) == PA_SOURCE_OUTPUT_CORKED) {
+ pa_assert_se(origin->n_corked-- >= 1);
+ dest->n_corked++;
+ }
+
/* Replace resampler */
- if (new_resampler != o->resampler) {
- if (o->resampler)
- pa_resampler_free(o->resampler);
- o->resampler = new_resampler;
+ if (new_resampler != o->thread_info.resampler) {
+ 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_source_update_status(origin);
+ pa_source_update_status(dest);
+
+ pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_POST], o);
+
+ pa_log_debug("Successfully moved source output %i from %s to %s.", o->index, origin->name, dest->name);
+
/* Notify everyone */
pa_subscription_post(o->source->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
- pa_source_notify(o->source);
return 0;
}
+
+/* Called from thread context */
+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: {
+
+ 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: {
+ o->thread_info.state = PA_PTR_TO_UINT(userdata);
+
+ return 0;
+ }
+ }
+
+ return -1;
+}
diff --git a/src/pulsecore/source-output.h b/src/pulsecore/source-output.h
index 3da6caac..e38a1e5a 100644
--- a/src/pulsecore/source-output.h
+++ b/src/pulsecore/source-output.h
@@ -1,5 +1,5 @@
-#ifndef foosourceoutputhfoo
-#define foosourceoutputhfoo
+#ifndef foopulsesourceoutputhfoo
+#define foopulsesourceoutputhfoo
/* $Id$ */
@@ -35,40 +35,91 @@ typedef struct pa_source_output pa_source_output;
#include <pulsecore/module.h>
#include <pulsecore/client.h>
-typedef enum {
+typedef enum pa_source_output_state {
+ PA_SOURCE_OUTPUT_INIT,
PA_SOURCE_OUTPUT_RUNNING,
PA_SOURCE_OUTPUT_CORKED,
- PA_SOURCE_OUTPUT_DISCONNECTED
+ PA_SOURCE_OUTPUT_UNLINKED
} pa_source_output_state_t;
+static inline pa_bool_t PA_SOURCE_OUTPUT_LINKED(pa_source_output_state_t x) {
+ return x == PA_SOURCE_OUTPUT_RUNNING || x == PA_SOURCE_OUTPUT_CORKED;
+}
+
typedef enum pa_source_output_flags {
- PA_SOURCE_OUTPUT_NO_HOOKS = 1
+ PA_SOURCE_OUTPUT_VARIABLE_RATE = 1,
+ PA_SOURCE_OUTPUT_DONT_MOVE = 2,
+ PA_SOURCE_OUTPUT_START_CORKED = 4
} pa_source_output_flags_t;
struct pa_source_output {
- int ref;
+ pa_msgobject parent;
+
uint32_t index;
+ pa_core *core;
pa_source_output_state_t state;
+ pa_source_output_flags_t flags;
char *name, *driver; /* may be NULL */
pa_module *module; /* may be NULL */
+ pa_client *client; /* may be NULL */
pa_source *source;
- pa_client *client; /* may be NULL */
pa_sample_spec sample_spec;
pa_channel_map channel_map;
+ /* Pushes a new memchunk into the output. Called from IO thread
+ * context. */
void (*push)(pa_source_output *o, const pa_memchunk *chunk);
+
+ /* If non-NULL this function is called when the output is first
+ * connected to a source. Called from IO thread context */
+ void (*attach) (pa_source_output *o); /* may be NULL */
+
+ /* If non-NULL this function is called when the output is
+ * disconnected from its source. Called from IO thread context */
+ void (*detach) (pa_source_output *o); /* may be NULL */
+
+ /* If non-NULL called whenever the the source this output is attached
+ * to suspends or resumes. Called from main context */
+ void (*suspend) (pa_source_output *o, int b); /* may be NULL */
+
+ /* Supposed to unlink and destroy this stream. Called from main
+ * context. */
void (*kill)(pa_source_output* o); /* may 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. */
pa_usec_t (*get_latency) (pa_source_output *o); /* may be NULL */
- pa_resampler* resampler; /* may be NULL */
pa_resample_method_t resample_method;
+ struct {
+ pa_source_output_state_t state;
+
+ pa_bool_t attached; /* True only between ->attach() and ->detach() calls */
+
+ pa_sample_spec sample_spec;
+
+ pa_resampler* resampler; /* may be NULL */
+ } thread_info;
+
void *userdata;
};
+PA_DECLARE_CLASS(pa_source_output);
+#define PA_SOURCE_OUTPUT(o) pa_source_output_cast(o)
+
+enum {
+ PA_SOURCE_OUTPUT_MESSAGE_GET_LATENCY,
+ PA_SOURCE_OUTPUT_MESSAGE_SET_RATE,
+ PA_SOURCE_OUTPUT_MESSAGE_SET_STATE,
+ PA_SOURCE_OUTPUT_MESSAGE_MAX
+};
+
typedef struct pa_source_output_new_data {
const char *name, *driver;
pa_module *module;
@@ -77,9 +128,9 @@ typedef struct pa_source_output_new_data {
pa_source *source;
pa_sample_spec sample_spec;
- int sample_spec_is_set;
+ pa_bool_t sample_spec_is_set;
pa_channel_map channel_map;
- int channel_map_is_set;
+ pa_bool_t channel_map_is_set;
pa_resample_method_t resample_method;
} pa_source_output_new_data;
@@ -89,30 +140,38 @@ void pa_source_output_new_data_set_sample_spec(pa_source_output_new_data *data,
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(
pa_core *core,
pa_source_output_new_data *data,
pa_source_output_flags_t flags);
-void pa_source_output_unref(pa_source_output* o);
-pa_source_output* pa_source_output_ref(pa_source_output *o);
+void pa_source_output_put(pa_source_output *o);
+void pa_source_output_unlink(pa_source_output*o);
-/* To be called by the implementing module only */
-void pa_source_output_disconnect(pa_source_output*o);
+void pa_source_output_set_name(pa_source_output *i, const char *name);
+
+/* Callable by everyone */
/* External code may request disconnection with this funcion */
void pa_source_output_kill(pa_source_output*o);
-void pa_source_output_push(pa_source_output *o, const pa_memchunk *chunk);
-
-void pa_source_output_set_name(pa_source_output *i, const char *name);
-
pa_usec_t pa_source_output_get_latency(pa_source_output *i);
-void pa_source_output_cork(pa_source_output *i, int b);
+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_resample_method_t pa_source_output_get_resample_method(pa_source_output *o);
int pa_source_output_move_to(pa_source_output *o, pa_source *dest);
+#define pa_source_output_get_state(o) ((o)->state)
+
+/* To be used exclusively by the source driver thread */
+
+void pa_source_output_push(pa_source_output *o, const pa_memchunk *chunk);
+int pa_source_output_process_msg(pa_msgobject *mo, int code, void *userdata, int64_t offset, pa_memchunk *chunk);
+
#endif
diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c
index 9bb2d342..9a6902ae 100644
--- a/src/pulsecore/source.c
+++ b/src/pulsecore/source.c
@@ -27,7 +27,6 @@
#endif
#include <stdio.h>
-#include <assert.h>
#include <stdlib.h>
#include <string.h>
@@ -42,11 +41,9 @@
#include "source.h"
-#define CHECK_VALIDITY_RETURN_NULL(condition) \
-do {\
-if (!(condition)) \
- return NULL; \
-} while (0)
+static PA_DEFINE_CHECK_TYPE(pa_source, pa_msgobject);
+
+static void source_free(pa_object *o);
pa_source* pa_source_new(
pa_core *core,
@@ -58,274 +55,359 @@ pa_source* pa_source_new(
pa_source *s;
char st[256];
- int r;
pa_channel_map tmap;
- assert(core);
- assert(name);
- assert(spec);
+ pa_assert(core);
+ pa_assert(name);
+ pa_assert(spec);
- CHECK_VALIDITY_RETURN_NULL(pa_sample_spec_valid(spec));
+ pa_return_null_if_fail(pa_sample_spec_valid(spec));
if (!map)
map = pa_channel_map_init_auto(&tmap, spec->channels, PA_CHANNEL_MAP_DEFAULT);
- CHECK_VALIDITY_RETURN_NULL(map && pa_channel_map_valid(map));
- CHECK_VALIDITY_RETURN_NULL(map->channels == spec->channels);
- CHECK_VALIDITY_RETURN_NULL(!driver || pa_utf8_valid(driver));
- CHECK_VALIDITY_RETURN_NULL(pa_utf8_valid(name) && *name);
+ pa_return_null_if_fail(map && pa_channel_map_valid(map));
+ pa_return_null_if_fail(map->channels == spec->channels);
+ pa_return_null_if_fail(!driver || pa_utf8_valid(driver));
+ pa_return_null_if_fail(pa_utf8_valid(name) && *name);
- s = pa_xnew(pa_source, 1);
+ s = pa_msgobject_new(pa_source);
if (!(name = pa_namereg_register(core, name, PA_NAMEREG_SOURCE, s, fail))) {
pa_xfree(s);
return NULL;
}
- s->ref = 1;
+ s->parent.parent.free = source_free;
+ s->parent.process_msg = pa_source_process_msg;
+
s->core = core;
- s->state = PA_SOURCE_RUNNING;
+ s->state = PA_SOURCE_INIT;
+ s->flags = 0;
s->name = pa_xstrdup(name);
s->description = NULL;
s->driver = pa_xstrdup(driver);
- s->owner = NULL;
+ s->module = NULL;
s->sample_spec = *spec;
s->channel_map = *map;
s->outputs = pa_idxset_new(NULL, NULL);
+ s->n_corked = 0;
s->monitor_of = NULL;
- pa_cvolume_reset(&s->sw_volume, spec->channels);
- pa_cvolume_reset(&s->hw_volume, spec->channels);
- s->sw_muted = 0;
- s->hw_muted = 0;
-
- s->is_hardware = 0;
+ pa_cvolume_reset(&s->volume, spec->channels);
+ s->muted = FALSE;
+ s->refresh_volume = s->refresh_muted = FALSE;
s->get_latency = NULL;
- s->notify = NULL;
- s->set_hw_volume = NULL;
- s->get_hw_volume = NULL;
- s->set_hw_mute = NULL;
- s->get_hw_mute = NULL;
+ s->set_volume = NULL;
+ s->get_volume = NULL;
+ s->set_mute = NULL;
+ s->get_mute = NULL;
+ s->set_state = NULL;
s->userdata = NULL;
- r = pa_idxset_put(core->sources, s, &s->index);
- assert(s->index != PA_IDXSET_INVALID && r >= 0);
+ 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 %u \"%s\" with sample spec \"%s\"", s->index, s->name, st);
+ pa_log_info("Created source %u \"%s\" with sample spec \"%s\"", s->index, s->name, st);
- pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_SOURCE | PA_SUBSCRIPTION_EVENT_NEW, s->index);
+ 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;
+ s->thread_info.state = s->state;
return s;
}
-void pa_source_disconnect(pa_source *s) {
+static int source_set_state(pa_source *s, pa_source_state_t state) {
+ int ret;
+
+ pa_assert(s);
+
+ if (s->state == state)
+ return 0;
+
+ if ((s->state == PA_SOURCE_SUSPENDED && PA_SOURCE_OPENED(state)) ||
+ (PA_SOURCE_OPENED(s->state) && state == PA_SOURCE_SUSPENDED)) {
+ pa_source_output *o;
+ uint32_t idx;
+
+ /* We're suspending or resuming, tell everyone about it */
+
+ for (o = PA_SOURCE_OUTPUT(pa_idxset_first(s->outputs, &idx)); o; o = PA_SOURCE_OUTPUT(pa_idxset_next(s->outputs, &idx)))
+ if (o->suspend)
+ o->suspend(o, state == PA_SINK_SUSPENDED);
+ }
+
+ if (s->set_state)
+ if ((ret = s->set_state(s, state)) < 0)
+ return -1;
+
+ if (pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_STATE, PA_UINT_TO_PTR(state), 0, NULL) < 0)
+ return -1;
+
+ s->state = state;
+
+ if (state != PA_SOURCE_UNLINKED) /* if we enter UNLINKED state pa_source_unlink() will fire the apropriate events */
+ pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_STATE_CHANGED], s);
+ return 0;
+}
+
+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->asyncmsgq);
+
+ 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);
+}
+
+void pa_source_unlink(pa_source *s) {
+ pa_bool_t linked;
pa_source_output *o, *j = NULL;
- assert(s);
- assert(s->state == PA_SOURCE_RUNNING);
+ pa_assert(s);
+
+ /* See pa_sink_unlink() for a couple of comments how this function
+ * works. */
- s->state = PA_SOURCE_DISCONNECTED;
- pa_namereg_unregister(s->core, s->name);
+ linked = PA_SOURCE_LINKED(s->state);
- pa_hook_fire(&s->core->hook_source_disconnect, s);
+ if (linked)
+ pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK], s);
+
+ if (s->state != PA_SOURCE_UNLINKED)
+ pa_namereg_unregister(s->core, s->name);
+ pa_idxset_remove_by_data(s->core->sources, s, NULL);
while ((o = pa_idxset_first(s->outputs, NULL))) {
- assert(o != j);
+ pa_assert(o != j);
pa_source_output_kill(o);
j = o;
}
- pa_idxset_remove_by_data(s->core->sources, s, NULL);
+ if (linked)
+ source_set_state(s, PA_SOURCE_UNLINKED);
+ else
+ s->state = PA_SOURCE_UNLINKED;
s->get_latency = NULL;
- s->notify = NULL;
- s->get_hw_volume = NULL;
- s->set_hw_volume = NULL;
- s->set_hw_mute = NULL;
- s->get_hw_mute = NULL;
-
- pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE | PA_SUBSCRIPTION_EVENT_REMOVE, s->index);
+ s->get_volume = NULL;
+ s->set_volume = NULL;
+ s->set_mute = NULL;
+ s->get_mute = NULL;
+ s->set_state = NULL;
+
+ if (linked) {
+ pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE | PA_SUBSCRIPTION_EVENT_REMOVE, s->index);
+ pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK_POST], s);
+ }
}
-static void source_free(pa_source *s) {
- assert(s);
- assert(!s->ref);
+static void source_free(pa_object *o) {
+ pa_source_output *so;
+ pa_source *s = PA_SOURCE(o);
- if (s->state != PA_SOURCE_DISCONNECTED)
- pa_source_disconnect(s);
+ pa_assert(s);
+ pa_assert(pa_source_refcnt(s) == 0);
- pa_log_info("freed %u \"%s\"", s->index, s->name);
+ if (PA_SOURCE_LINKED(s->state))
+ pa_source_unlink(s);
+
+ pa_log_info("Freeing source %u \"%s\"", s->index, s->name);
pa_idxset_free(s->outputs, NULL, NULL);
+ while ((so = pa_hashmap_steal_first(s->thread_info.outputs)))
+ pa_source_output_unref(so);
+
+ pa_hashmap_free(s->thread_info.outputs, NULL, NULL);
+
pa_xfree(s->name);
pa_xfree(s->description);
pa_xfree(s->driver);
pa_xfree(s);
}
-void pa_source_unref(pa_source *s) {
- assert(s);
- assert(s->ref >= 1);
-
- if (!(--s->ref))
- source_free(s);
-}
+int pa_source_update_status(pa_source*s) {
+ pa_source_assert_ref(s);
+ pa_assert(PA_SOURCE_LINKED(s->state));
-pa_source* pa_source_ref(pa_source *s) {
- assert(s);
- assert(s->ref >= 1);
+ if (s->state == PA_SOURCE_SUSPENDED)
+ return 0;
- s->ref++;
- return s;
+ return source_set_state(s, pa_source_used_by(s) ? PA_SOURCE_RUNNING : PA_SOURCE_IDLE);
}
-void pa_source_notify(pa_source*s) {
- assert(s);
- assert(s->ref >= 1);
+int pa_source_suspend(pa_source *s, pa_bool_t suspend) {
+ pa_source_assert_ref(s);
+ pa_assert(PA_SOURCE_LINKED(s->state));
- if (s->notify)
- s->notify(s);
+ if (suspend)
+ return source_set_state(s, PA_SOURCE_SUSPENDED);
+ else
+ return source_set_state(s, pa_source_used_by(s) ? PA_SOURCE_RUNNING : PA_SOURCE_IDLE);
}
-static int do_post(void *p, PA_GCC_UNUSED uint32_t idx, PA_GCC_UNUSED int *del, void*userdata) {
- pa_source_output *o = p;
- const pa_memchunk *chunk = userdata;
+void pa_source_ping(pa_source *s) {
+ pa_source_assert_ref(s);
+ pa_assert(PA_SOURCE_LINKED(s->state));
- assert(o);
- assert(chunk);
-
- pa_source_output_push(o, chunk);
- return 0;
+ pa_asyncmsgq_post(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_PING, NULL, 0, NULL, NULL);
}
void pa_source_post(pa_source*s, const pa_memchunk *chunk) {
- assert(s);
- assert(s->ref >= 1);
- assert(chunk);
+ pa_source_output *o;
+ void *state = NULL;
- pa_source_ref(s);
+ pa_source_assert_ref(s);
+ pa_assert(PA_SOURCE_OPENED(s->thread_info.state));
+ pa_assert(chunk);
- if (s->sw_muted || !pa_cvolume_is_norm(&s->sw_volume)) {
+ 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->sw_muted)
+
+ 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->sw_volume);
- pa_idxset_foreach(s->outputs, do_post, &vchunk);
+ 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);
+
pa_memblock_unref(vchunk.memblock);
- } else
- pa_idxset_foreach(s->outputs, do_post, (void*) chunk);
+ } else {
- pa_source_unref(s);
+ while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL)))
+ pa_source_output_push(o, chunk);
+ }
}
-void pa_source_set_owner(pa_source *s, pa_module *m) {
- assert(s);
- assert(s->ref >= 1);
+pa_usec_t pa_source_get_latency(pa_source *s) {
+ pa_usec_t usec;
- if (m == s->owner)
- return;
+ pa_source_assert_ref(s);
+ pa_assert(PA_SOURCE_LINKED(s->state));
- s->owner = m;
- pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
-}
+ if (!PA_SOURCE_OPENED(s->state))
+ return 0;
-pa_usec_t pa_source_get_latency(pa_source *s) {
- assert(s);
- assert(s->ref >= 1);
+ if (s->get_latency)
+ return s->get_latency(s);
- if (!s->get_latency)
+ if (pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0)
return 0;
- return s->get_latency(s);
+ return usec;
}
-void pa_source_set_volume(pa_source *s, pa_mixer_t m, const pa_cvolume *volume) {
- pa_cvolume *v;
+void pa_source_set_volume(pa_source *s, const pa_cvolume *volume) {
+ int changed;
- assert(s);
- assert(s->ref >= 1);
- assert(volume);
+ pa_source_assert_ref(s);
+ pa_assert(PA_SOURCE_LINKED(s->state));
+ pa_assert(volume);
- if (m == PA_MIXER_HARDWARE && s->set_hw_volume)
- v = &s->hw_volume;
- else
- v = &s->sw_volume;
+ changed = !pa_cvolume_equal(volume, &s->volume);
+ s->volume = *volume;
- if (pa_cvolume_equal(v, volume))
- return;
+ if (s->set_volume && s->set_volume(s) < 0)
+ s->set_volume = NULL;
- *v = *volume;
+ 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);
- if (v == &s->hw_volume)
- if (s->set_hw_volume(s) < 0)
- s->sw_volume = *volume;
+ if (changed)
+ pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+}
- 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;
+
+ pa_source_assert_ref(s);
+ pa_assert(PA_SOURCE_LINKED(s->state));
+
+ old_volume = s->volume;
+
+ 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 (!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;
}
-const pa_cvolume *pa_source_get_volume(pa_source *s, pa_mixer_t m) {
- assert(s);
- assert(s->ref >= 1);
+void pa_source_set_mute(pa_source *s, pa_bool_t mute) {
+ int changed;
+
+ pa_source_assert_ref(s);
+ pa_assert(PA_SOURCE_LINKED(s->state));
+
+ changed = s->muted != mute;
+ s->muted = mute;
- if (m == PA_MIXER_HARDWARE && s->set_hw_volume) {
+ if (s->set_mute && s->set_mute(s) < 0)
+ s->set_mute = NULL;
- if (s->get_hw_volume)
- s->get_hw_volume(s);
+ if (!s->set_mute)
+ pa_asyncmsgq_post(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_MUTE, PA_UINT_TO_PTR(mute), 0, NULL, NULL);
- return &s->hw_volume;
- } else
- return &s->sw_volume;
+ if (changed)
+ pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
}
-void pa_source_set_mute(pa_source *s, pa_mixer_t m, int mute) {
- int *t;
+pa_bool_t pa_source_get_mute(pa_source *s) {
+ pa_bool_t old_muted;
- assert(s);
- assert(s->ref >= 1);
+ pa_source_assert_ref(s);
+ pa_assert(PA_SOURCE_LINKED(s->state));
- if (m == PA_MIXER_HARDWARE && s->set_hw_mute)
- t = &s->hw_muted;
- else
- t = &s->sw_muted;
+ old_muted = s->muted;
- if (!!*t == !!mute)
- return;
+ if (s->get_mute && s->get_mute(s) < 0)
+ s->get_mute = NULL;
- *t = !!mute;
+ 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 (t == &s->hw_muted)
- if (s->set_hw_mute(s) < 0)
- s->sw_muted = !!mute;
+ if (old_muted != s->muted)
+ pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
- pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+ return s->muted;
}
-int pa_source_get_mute(pa_source *s, pa_mixer_t m) {
- assert(s);
- assert(s->ref >= 1);
+void pa_source_set_module(pa_source *s, pa_module *m) {
+ pa_source_assert_ref(s);
- if (m == PA_MIXER_HARDWARE && s->set_hw_mute) {
+ if (m == s->module)
+ return;
- if (s->get_hw_mute)
- s->get_hw_mute(s);
+ s->module = m;
- return s->hw_muted;
- } else
- return s->sw_muted;
+ pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
}
void pa_source_set_description(pa_source *s, const char *description) {
- assert(s);
- assert(s->ref >= 1);
+ pa_source_assert_ref(s);
if (!description && !s->description)
return;
@@ -336,12 +418,172 @@ void pa_source_set_description(pa_source *s, const char *description) {
pa_xfree(s->description);
s->description = pa_xstrdup(description);
- pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+ if (PA_SOURCE_LINKED(s->state)) {
+ pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_DESCRIPTION_CHANGED], s);
+ pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+ }
}
-unsigned pa_source_used_by(pa_source *s) {
- assert(s);
- assert(s->ref >= 1);
+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;
+}
+
+unsigned pa_source_linked_by(pa_source *s) {
+ pa_source_assert_ref(s);
+ pa_assert(PA_SOURCE_LINKED(s->state));
return pa_idxset_size(s->outputs);
}
+
+unsigned pa_source_used_by(pa_source *s) {
+ unsigned ret;
+
+ pa_source_assert_ref(s);
+ pa_assert(PA_SOURCE_LINKED(s->state));
+
+ ret = pa_idxset_size(s->outputs);
+ pa_assert(ret >= s->n_corked);
+
+ return ret - s->n_corked;
+}
+
+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));
+
+ pa_assert(!o->thread_info.attached);
+ o->thread_info.attached = TRUE;
+
+ if (o->attach)
+ o->attach(o);
+
+ return 0;
+ }
+
+ case PA_SOURCE_MESSAGE_REMOVE_OUTPUT: {
+ pa_source_output *o = PA_SOURCE_OUTPUT(userdata);
+
+ if (o->detach)
+ o->detach(o);
+
+ pa_assert(o->thread_info.attached);
+ o->thread_info.attached = FALSE;
+
+ if (pa_hashmap_remove(s->thread_info.outputs, PA_UINT32_TO_PTR(o->index)))
+ pa_source_output_unref(o);
+
+ return 0;
+ }
+
+ case PA_SOURCE_MESSAGE_SET_VOLUME:
+ s->thread_info.soft_volume = *((pa_cvolume*) userdata);
+ return 0;
+
+ case PA_SOURCE_MESSAGE_SET_MUTE:
+ s->thread_info.soft_muted = PA_PTR_TO_UINT(userdata);
+ return 0;
+
+ case PA_SOURCE_MESSAGE_GET_VOLUME:
+ *((pa_cvolume*) userdata) = s->thread_info.soft_volume;
+ return 0;
+
+ case PA_SOURCE_MESSAGE_GET_MUTE:
+ *((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 */
+ pa_source_detach_within_thread(s);
+ break;
+
+ case PA_SOURCE_MESSAGE_ATTACH:
+
+ /* Reattach all streams */
+ pa_source_attach_within_thread(s);
+ break;
+
+ case PA_SOURCE_MESSAGE_GET_LATENCY:
+ case PA_SOURCE_MESSAGE_MAX:
+ ;
+ }
+
+ return -1;
+}
+
+int pa_source_suspend_all(pa_core *c, pa_bool_t suspend) {
+ uint32_t idx;
+ pa_source *source;
+ int ret = 0;
+
+ pa_core_assert_ref(c);
+
+ for (source = PA_SOURCE(pa_idxset_first(c->sources, &idx)); source; source = PA_SOURCE(pa_idxset_next(c->sources, &idx)))
+ ret -= pa_source_suspend(source, suspend) < 0;
+
+ return ret;
+}
+
+void pa_source_detach(pa_source *s) {
+ pa_source_assert_ref(s);
+ pa_assert(PA_SOURCE_LINKED(s->state));
+
+ pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_DETACH, NULL, 0, NULL);
+}
+
+void pa_source_attach(pa_source *s) {
+ pa_source_assert_ref(s);
+ pa_assert(PA_SOURCE_LINKED(s->state));
+
+ pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_ATTACH, NULL, 0, NULL);
+}
+
+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));
+
+ while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL)))
+ if (o->detach)
+ o->detach(o);
+}
+
+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));
+
+ while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL)))
+ if (o->attach)
+ o->attach(o);
+
+}
diff --git a/src/pulsecore/source.h b/src/pulsecore/source.h
index 5a28cf4b..bd0a9122 100644
--- a/src/pulsecore/source.h
+++ b/src/pulsecore/source.h
@@ -1,5 +1,5 @@
-#ifndef foosourcehfoo
-#define foosourcehfoo
+#ifndef foopulsesourcehfoo
+#define foopulsesourcehfoo
/* $Id$ */
@@ -32,6 +32,7 @@ typedef struct pa_source pa_source;
#include <pulse/sample.h>
#include <pulse/channelmap.h>
#include <pulse/volume.h>
+
#include <pulsecore/core-def.h>
#include <pulsecore/core.h>
#include <pulsecore/idxset.h>
@@ -39,73 +40,140 @@ typedef struct pa_source pa_source;
#include <pulsecore/memchunk.h>
#include <pulsecore/sink.h>
#include <pulsecore/module.h>
+#include <pulsecore/asyncmsgq.h>
+#include <pulsecore/msgobject.h>
+#include <pulsecore/rtpoll.h>
-#define PA_MAX_OUTPUTS_PER_SOURCE 16
+#define PA_MAX_OUTPUTS_PER_SOURCE 32
typedef enum pa_source_state {
+ PA_SOURCE_INIT,
PA_SOURCE_RUNNING,
- PA_SOURCE_DISCONNECTED
+ PA_SOURCE_SUSPENDED,
+ PA_SOURCE_IDLE,
+ PA_SOURCE_UNLINKED
} pa_source_state_t;
+static inline pa_bool_t PA_SOURCE_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) {
+ return x == PA_SOURCE_RUNNING || x == PA_SOURCE_IDLE || x == PA_SOURCE_SUSPENDED;
+}
+
struct pa_source {
- int ref;
+ pa_msgobject parent;
+
uint32_t index;
pa_core *core;
pa_source_state_t state;
+ pa_source_flags_t flags;
char *name;
char *description, *driver; /* may be NULL */
- pa_module *owner; /* may be NULL */
+ pa_module *module; /* may be NULL */
pa_sample_spec sample_spec;
pa_channel_map channel_map;
pa_idxset *outputs;
+ unsigned n_corked;
pa_sink *monitor_of; /* may be NULL */
- pa_cvolume hw_volume, sw_volume;
- int hw_muted, sw_muted;
-
- int is_hardware;
+ pa_cvolume volume;
+ pa_bool_t muted;
+ pa_bool_t refresh_volume;
+ pa_bool_t refresh_muted;
- void (*notify)(pa_source*source); /* may be NULL */
+ int (*set_state)(pa_source*source, pa_source_state_t state); /* may be NULL */
+ int (*set_volume)(pa_source *s); /* dito */
+ int (*get_volume)(pa_source *s); /* dito */
+ int (*set_mute)(pa_source *s); /* dito */
+ int (*get_mute)(pa_source *s); /* dito */
pa_usec_t (*get_latency)(pa_source *s); /* dito */
- int (*set_hw_volume)(pa_source *s); /* dito */
- int (*get_hw_volume)(pa_source *s); /* dito */
- int (*set_hw_mute)(pa_source *s); /* dito */
- int (*get_hw_mute)(pa_source *s); /* dito */
+
+ pa_asyncmsgq *asyncmsgq;
+ pa_rtpoll *rtpoll;
+
+ /* Contains copies of the above data so that the real-time worker
+ * thread can work without access locking */
+ struct {
+ pa_source_state_t state;
+ pa_hashmap *outputs;
+ pa_cvolume soft_volume;
+ pa_bool_t soft_muted;
+ } thread_info;
void *userdata;
};
+PA_DECLARE_CLASS(pa_source);
+#define PA_SOURCE(s) pa_source_cast(s)
+
+typedef enum pa_source_message {
+ PA_SOURCE_MESSAGE_ADD_OUTPUT,
+ PA_SOURCE_MESSAGE_REMOVE_OUTPUT,
+ PA_SOURCE_MESSAGE_GET_VOLUME,
+ PA_SOURCE_MESSAGE_SET_VOLUME,
+ PA_SOURCE_MESSAGE_GET_MUTE,
+ PA_SOURCE_MESSAGE_SET_MUTE,
+ PA_SOURCE_MESSAGE_GET_LATENCY,
+ PA_SOURCE_MESSAGE_SET_STATE,
+ PA_SOURCE_MESSAGE_PING,
+ PA_SOURCE_MESSAGE_ATTACH,
+ PA_SOURCE_MESSAGE_DETACH,
+ PA_SOURCE_MESSAGE_MAX
+} pa_source_message_t;
+
+/* 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);
-
-void pa_source_disconnect(pa_source *s);
-void pa_source_unref(pa_source *s);
-pa_source* pa_source_ref(pa_source *c);
-
-/* Pass a new memory block to all output streams */
-void pa_source_post(pa_source*s, const pa_memchunk *b);
+ pa_core *core,
+ const char *driver,
+ const char *name,
+ int namereg_fail,
+ const pa_sample_spec *spec,
+ const pa_channel_map *map);
+
+void pa_source_put(pa_source *s);
+void pa_source_unlink(pa_source *s);
-void pa_source_notify(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_detach(pa_source *s);
+void pa_source_attach(pa_source *s);
-void pa_source_set_owner(pa_source *s, pa_module *m);
+/* May be called by everyone, from main context */
pa_usec_t pa_source_get_latency(pa_source *s);
-void pa_source_set_volume(pa_source *source, pa_mixer_t m, const pa_cvolume *volume);
-const pa_cvolume *pa_source_get_volume(pa_source *source, pa_mixer_t m);
-void pa_source_set_mute(pa_source *source, pa_mixer_t m, int mute);
-int pa_source_get_mute(pa_source *source, pa_mixer_t m);
+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_set_description(pa_source *s, const char *description);
+void pa_source_ping(pa_source *s);
+
+void pa_source_set_volume(pa_source *source, const pa_cvolume *volume);
+const pa_cvolume *pa_source_get_volume(pa_source *source);
+void pa_source_set_mute(pa_source *source, pa_bool_t mute);
+pa_bool_t pa_source_get_mute(pa_source *source);
+
+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 */
+#define pa_source_get_state(s) ((pa_source_state_t) (s)->state)
+
+/* To be called exclusively by the source driver, from IO context */
+
+void pa_source_post(pa_source*s, const pa_memchunk *b);
+
+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);
-unsigned pa_source_used_by(pa_source *s);
#endif
diff --git a/src/pulsecore/speex/Makefile b/src/pulsecore/speex/Makefile
new file mode 100644
index 00000000..316beb72
--- /dev/null
+++ b/src/pulsecore/speex/Makefile
@@ -0,0 +1,13 @@
+# This is a dirty trick just to ease compilation with emacs
+#
+# This file is not intended to be distributed or anything
+#
+# So: don't touch it, even better ignore it!
+
+all:
+ $(MAKE) -C ../..
+
+clean:
+ $(MAKE) -C ../.. clean
+
+.PHONY: all clean
diff --git a/src/pulsecore/speex/arch.h b/src/pulsecore/speex/arch.h
new file mode 100644
index 00000000..4be693c3
--- /dev/null
+++ b/src/pulsecore/speex/arch.h
@@ -0,0 +1,197 @@
+/* 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 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 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 LPC_SHIFT 0
+#define LSP_SHIFT 0
+#define SIG_SHIFT 0
+
+#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
+
+#endif
diff --git a/src/pulsecore/speex/fixed_generic.h b/src/pulsecore/speex/fixed_generic.h
new file mode 100644
index 00000000..547e22c7
--- /dev/null
+++ b/src/pulsecore/speex/fixed_generic.h
@@ -0,0 +1,106 @@
+/* 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
new file mode 100644
index 00000000..bf1f88b4
--- /dev/null
+++ b/src/pulsecore/speex/resample.c
@@ -0,0 +1,1114 @@
+/* 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)
+
+ The code is working, but it's in a very early stage, so it may have
+ artifacts, noise or subliminal messages from satan. Also, the API
+ isn't stable and I can actually promise that I *will* change the API
+ some time in the future.
+
+TODO list:
+ - Variable calculation resolution depending on quality setting
+ - Single vs double in float mode
+ - 16-bit vs 32-bit (sinc only) in fixed-point mode
+ - Make sure the filter update works even when changing params
+ after only a few samples procesed
+*/
+
+#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 "misc.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
new file mode 100644
index 00000000..8629eeb3
--- /dev/null
+++ b/src/pulsecore/speex/speex_resampler.h
@@ -0,0 +1,328 @@
+/* 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
new file mode 100644
index 00000000..c0d5c0c0
--- /dev/null
+++ b/src/pulsecore/speexwrap.h
@@ -0,0 +1,48 @@
+#ifndef foopulsespeexwraphfoo
+#define foopulsespeexwraphfoo
+
+/* $Id$ */
+
+/***
+ 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/strbuf.c b/src/pulsecore/strbuf.c
index a3ddc114..7c576c67 100644
--- a/src/pulsecore/strbuf.c
+++ b/src/pulsecore/strbuf.c
@@ -27,12 +27,12 @@
#include <sys/types.h>
#include <stdlib.h>
-#include <assert.h>
#include <string.h>
#include <stdarg.h>
#include <stdio.h>
#include <pulse/xmalloc.h>
+#include <pulsecore/macro.h>
#include "strbuf.h"
@@ -42,7 +42,7 @@ struct chunk {
size_t length;
};
-#define CHUNK_TO_TEXT(c) ((char*) (c) + sizeof(struct chunk))
+#define CHUNK_TO_TEXT(c) ((char*) (c) + PA_ALIGN(sizeof(struct chunk)))
struct pa_strbuf {
size_t length;
@@ -50,14 +50,18 @@ struct pa_strbuf {
};
pa_strbuf *pa_strbuf_new(void) {
- pa_strbuf *sb = pa_xmalloc(sizeof(pa_strbuf));
+ pa_strbuf *sb;
+
+ sb = pa_xnew(pa_strbuf, 1);
sb->length = 0;
sb->head = sb->tail = NULL;
+
return sb;
}
void pa_strbuf_free(pa_strbuf *sb) {
- assert(sb);
+ pa_assert(sb);
+
while (sb->head) {
struct chunk *c = sb->head;
sb->head = sb->head->next;
@@ -72,12 +76,13 @@ void pa_strbuf_free(pa_strbuf *sb) {
char *pa_strbuf_tostring(pa_strbuf *sb) {
char *t, *e;
struct chunk *c;
- assert(sb);
- e = t = pa_xmalloc(sb->length+1);
+ pa_assert(sb);
+
+ e = t = pa_xnew(char, sb->length+1);
for (c = sb->head; c; c = c->next) {
- assert((size_t) (e-t) <= sb->length);
+ pa_assert((size_t) (e-t) <= sb->length);
memcpy(e, CHUNK_TO_TEXT(c), c->length);
e += c->length;
}
@@ -85,7 +90,7 @@ char *pa_strbuf_tostring(pa_strbuf *sb) {
/* Trailing NUL */
*e = 0;
- assert(e == t+sb->length);
+ pa_assert(e == t+sb->length);
return t;
}
@@ -93,27 +98,33 @@ char *pa_strbuf_tostring(pa_strbuf *sb) {
/* Combination of pa_strbuf_free() and pa_strbuf_tostring() */
char *pa_strbuf_tostring_free(pa_strbuf *sb) {
char *t;
- assert(sb);
+
+ pa_assert(sb);
t = pa_strbuf_tostring(sb);
pa_strbuf_free(sb);
+
return t;
}
/* Append a string to the string buffer */
void pa_strbuf_puts(pa_strbuf *sb, const char *t) {
- assert(sb && t);
+
+ pa_assert(sb);
+ pa_assert(t);
+
pa_strbuf_putsn(sb, t, strlen(t));
}
/* Append a new chunk to the linked list */
static void append(pa_strbuf *sb, struct chunk *c) {
- assert(sb && c);
+ pa_assert(sb);
+ pa_assert(c);
if (sb->tail) {
- assert(sb->head);
+ pa_assert(sb->head);
sb->tail->next = c;
} else {
- assert(!sb->head);
+ pa_assert(!sb->head);
sb->head = c;
}
@@ -125,12 +136,14 @@ static void append(pa_strbuf *sb, struct chunk *c) {
/* Append up to l bytes of a string to the string buffer */
void pa_strbuf_putsn(pa_strbuf *sb, const char *t, size_t l) {
struct chunk *c;
- assert(sb && t);
+
+ pa_assert(sb);
+ pa_assert(t);
if (!l)
return;
- c = pa_xmalloc(sizeof(struct chunk)+l);
+ c = pa_xmalloc(PA_ALIGN(sizeof(struct chunk)) + l);
c->length = l;
memcpy(CHUNK_TO_TEXT(c), t, l);
@@ -143,16 +156,18 @@ int pa_strbuf_printf(pa_strbuf *sb, const char *format, ...) {
int size = 100;
struct chunk *c = NULL;
- assert(sb);
+ pa_assert(sb);
+ pa_assert(format);
for(;;) {
va_list ap;
int r;
- c = pa_xrealloc(c, sizeof(struct chunk)+size);
+ c = pa_xrealloc(c, PA_ALIGN(sizeof(struct chunk)) + size);
va_start(ap, format);
r = vsnprintf(CHUNK_TO_TEXT(c), size, format, ap);
+ CHUNK_TO_TEXT(c)[size-1] = 0;
va_end(ap);
if (r > -1 && r < size) {
diff --git a/src/pulsecore/strlist.c b/src/pulsecore/strlist.c
index 955b78e4..792af0ff 100644
--- a/src/pulsecore/strlist.c
+++ b/src/pulsecore/strlist.c
@@ -26,26 +26,31 @@
#endif
#include <string.h>
-#include <assert.h>
#include <pulse/xmalloc.h>
#include <pulsecore/strbuf.h>
+#include <pulsecore/macro.h>
#include <pulsecore/core-util.h>
#include "strlist.h"
struct pa_strlist {
pa_strlist *next;
- char *str;
};
+#define ITEM_TO_TEXT(c) ((char*) (c) + PA_ALIGN(sizeof(pa_strlist)))
+
pa_strlist* pa_strlist_prepend(pa_strlist *l, const char *s) {
pa_strlist *n;
- assert(s);
- n = pa_xmalloc(sizeof(pa_strlist));
- n->str = pa_xstrdup(s);
+ size_t size;
+
+ pa_assert(s);
+ size = strlen(s);
+ n = pa_xmalloc(PA_ALIGN(sizeof(pa_strlist)) + size + 1);
+ memcpy(ITEM_TO_TEXT(n), s, size + 1);
n->next = l;
+
return n;
}
@@ -58,7 +63,7 @@ char *pa_strlist_tostring(pa_strlist *l) {
if (!first)
pa_strbuf_puts(b, " ");
first = 0;
- pa_strbuf_puts(b, l->str);
+ pa_strbuf_puts(b, ITEM_TO_TEXT(l));
}
return pa_strbuf_tostring_free(b);
@@ -66,19 +71,20 @@ char *pa_strlist_tostring(pa_strlist *l) {
pa_strlist* pa_strlist_remove(pa_strlist *l, const char *s) {
pa_strlist *ret = l, *prev = NULL;
- assert(l && s);
+
+ pa_assert(l);
+ pa_assert(s);
while (l) {
- if (!strcmp(l->str, s)) {
+ if (!strcmp(ITEM_TO_TEXT(l), s)) {
pa_strlist *n = l->next;
if (!prev) {
- assert(ret == l);
+ pa_assert(ret == l);
ret = n;
} else
prev->next = n;
- pa_xfree(l->str);
pa_xfree(l);
l = n;
@@ -96,22 +102,21 @@ void pa_strlist_free(pa_strlist *l) {
while (l) {
pa_strlist *c = l;
l = l->next;
-
- pa_xfree(c->str);
pa_xfree(c);
}
}
pa_strlist* pa_strlist_pop(pa_strlist *l, char **s) {
pa_strlist *r;
- assert(s);
+
+ pa_assert(s);
if (!l) {
*s = NULL;
return NULL;
}
- *s = l->str;
+ *s = pa_xstrdup(ITEM_TO_TEXT(l));
r = l->next;
pa_xfree(l);
return r;
@@ -124,10 +129,12 @@ pa_strlist* pa_strlist_parse(const char *s) {
while ((r = pa_split_spaces(s, &state))) {
pa_strlist *n;
+ size_t size = strlen(r);
- n = pa_xmalloc(sizeof(pa_strlist));
- n->str = r;
+ n = pa_xmalloc(PA_ALIGN(sizeof(pa_strlist)) + size + 1);
n->next = NULL;
+ memcpy(ITEM_TO_TEXT(n), r, size+1);
+ pa_xfree(r);
if (p)
p->next = n;
diff --git a/src/pulsecore/tagstruct.c b/src/pulsecore/tagstruct.c
index ac7ae1ab..556fe806 100644
--- a/src/pulsecore/tagstruct.c
+++ b/src/pulsecore/tagstruct.c
@@ -29,19 +29,18 @@
#include <string.h>
#include <unistd.h>
#include <sys/time.h>
-#include <assert.h>
#include <stdarg.h>
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
-#include "winsock.h"
-
#include <pulse/xmalloc.h>
-#include "tagstruct.h"
+#include <pulsecore/winsock.h>
+#include <pulsecore/macro.h>
+#include "tagstruct.h"
struct pa_tagstruct {
uint8_t *data;
@@ -54,18 +53,20 @@ struct pa_tagstruct {
pa_tagstruct *pa_tagstruct_new(const uint8_t* data, size_t length) {
pa_tagstruct*t;
- assert(!data || (data && length));
+ pa_assert(!data || (data && length));
- t = pa_xmalloc(sizeof(pa_tagstruct));
+ t = pa_xnew(pa_tagstruct, 1);
t->data = (uint8_t*) data;
t->allocated = t->length = data ? length : 0;
t->rindex = 0;
t->dynamic = !data;
+
return t;
}
void pa_tagstruct_free(pa_tagstruct*t) {
- assert(t);
+ pa_assert(t);
+
if (t->dynamic)
pa_xfree(t->data);
pa_xfree(t);
@@ -73,7 +74,11 @@ void pa_tagstruct_free(pa_tagstruct*t) {
uint8_t* pa_tagstruct_free_data(pa_tagstruct*t, size_t *l) {
uint8_t *p;
- assert(t && t->dynamic && l);
+
+ pa_assert(t);
+ pa_assert(t->dynamic);
+ pa_assert(l);
+
p = t->data;
*l = t->length;
pa_xfree(t);
@@ -81,8 +86,8 @@ uint8_t* pa_tagstruct_free_data(pa_tagstruct*t, size_t *l) {
}
static void extend(pa_tagstruct*t, size_t l) {
- assert(t);
- assert(t->dynamic);
+ pa_assert(t);
+ pa_assert(t->dynamic);
if (t->length+l <= t->allocated)
return;
@@ -92,7 +97,8 @@ static void extend(pa_tagstruct*t, size_t l) {
void pa_tagstruct_puts(pa_tagstruct*t, const char *s) {
size_t l;
- assert(t);
+ pa_assert(t);
+
if (s) {
l = strlen(s)+2;
extend(t, l);
@@ -107,7 +113,8 @@ void pa_tagstruct_puts(pa_tagstruct*t, const char *s) {
}
void pa_tagstruct_putu32(pa_tagstruct*t, uint32_t i) {
- assert(t);
+ pa_assert(t);
+
extend(t, 5);
t->data[t->length] = PA_TAG_U32;
i = htonl(i);
@@ -116,7 +123,8 @@ void pa_tagstruct_putu32(pa_tagstruct*t, uint32_t i) {
}
void pa_tagstruct_putu8(pa_tagstruct*t, uint8_t c) {
- assert(t);
+ pa_assert(t);
+
extend(t, 2);
t->data[t->length] = PA_TAG_U8;
*(t->data+t->length+1) = c;
@@ -125,7 +133,10 @@ void pa_tagstruct_putu8(pa_tagstruct*t, uint8_t c) {
void pa_tagstruct_put_sample_spec(pa_tagstruct *t, const pa_sample_spec *ss) {
uint32_t rate;
- assert(t && ss);
+
+ pa_assert(t);
+ pa_assert(ss);
+
extend(t, 7);
t->data[t->length] = PA_TAG_SAMPLE_SPEC;
t->data[t->length+1] = (uint8_t) ss->format;
@@ -137,7 +148,9 @@ void pa_tagstruct_put_sample_spec(pa_tagstruct *t, const pa_sample_spec *ss) {
void pa_tagstruct_put_arbitrary(pa_tagstruct *t, const void *p, size_t length) {
uint32_t tmp;
- assert(t && p);
+
+ pa_assert(t);
+ pa_assert(p);
extend(t, 5+length);
t->data[t->length] = PA_TAG_ARBITRARY;
@@ -149,7 +162,8 @@ void pa_tagstruct_put_arbitrary(pa_tagstruct *t, const void *p, size_t length) {
}
void pa_tagstruct_put_boolean(pa_tagstruct*t, int b) {
- assert(t);
+ pa_assert(t);
+
extend(t, 1);
t->data[t->length] = b ? PA_TAG_BOOLEAN_TRUE : PA_TAG_BOOLEAN_FALSE;
t->length += 1;
@@ -157,7 +171,8 @@ void pa_tagstruct_put_boolean(pa_tagstruct*t, int b) {
void pa_tagstruct_put_timeval(pa_tagstruct*t, const struct timeval *tv) {
uint32_t tmp;
- assert(t);
+ pa_assert(t);
+
extend(t, 9);
t->data[t->length] = PA_TAG_TIMEVAL;
tmp = htonl(tv->tv_sec);
@@ -169,7 +184,9 @@ void pa_tagstruct_put_timeval(pa_tagstruct*t, const struct timeval *tv) {
void pa_tagstruct_put_usec(pa_tagstruct*t, pa_usec_t u) {
uint32_t tmp;
- assert(t);
+
+ pa_assert(t);
+
extend(t, 9);
t->data[t->length] = PA_TAG_USEC;
tmp = htonl((uint32_t) (u >> 32));
@@ -181,7 +198,9 @@ void pa_tagstruct_put_usec(pa_tagstruct*t, pa_usec_t u) {
void pa_tagstruct_putu64(pa_tagstruct*t, uint64_t u) {
uint32_t tmp;
- assert(t);
+
+ pa_assert(t);
+
extend(t, 9);
t->data[t->length] = PA_TAG_U64;
tmp = htonl((uint32_t) (u >> 32));
@@ -193,7 +212,9 @@ void pa_tagstruct_putu64(pa_tagstruct*t, uint64_t u) {
void pa_tagstruct_puts64(pa_tagstruct*t, int64_t u) {
uint32_t tmp;
- assert(t);
+
+ pa_assert(t);
+
extend(t, 9);
t->data[t->length] = PA_TAG_S64;
tmp = htonl((uint32_t) ((uint64_t) u >> 32));
@@ -206,7 +227,7 @@ void pa_tagstruct_puts64(pa_tagstruct*t, int64_t u) {
void pa_tagstruct_put_channel_map(pa_tagstruct *t, const pa_channel_map *map) {
unsigned i;
- assert(t);
+ pa_assert(t);
extend(t, 2 + map->channels);
t->data[t->length++] = PA_TAG_CHANNEL_MAP;
@@ -220,7 +241,7 @@ void pa_tagstruct_put_cvolume(pa_tagstruct *t, const pa_cvolume *cvolume) {
unsigned i;
pa_volume_t vol;
- assert(t);
+ pa_assert(t);
extend(t, 2 + cvolume->channels * sizeof(pa_volume_t));
t->data[t->length++] = PA_TAG_CVOLUME;
@@ -237,7 +258,9 @@ int pa_tagstruct_gets(pa_tagstruct*t, const char **s) {
int error = 0;
size_t n;
char *c;
- assert(t && s);
+
+ pa_assert(t);
+ pa_assert(s);
if (t->rindex+1 > t->length)
return -1;
@@ -271,7 +294,8 @@ int pa_tagstruct_gets(pa_tagstruct*t, const char **s) {
}
int pa_tagstruct_getu32(pa_tagstruct*t, uint32_t *i) {
- assert(t && i);
+ pa_assert(t);
+ pa_assert(i);
if (t->rindex+5 > t->length)
return -1;
@@ -286,7 +310,8 @@ int pa_tagstruct_getu32(pa_tagstruct*t, uint32_t *i) {
}
int pa_tagstruct_getu8(pa_tagstruct*t, uint8_t *c) {
- assert(t && c);
+ pa_assert(t);
+ pa_assert(c);
if (t->rindex+2 > t->length)
return -1;
@@ -300,7 +325,8 @@ int pa_tagstruct_getu8(pa_tagstruct*t, uint8_t *c) {
}
int pa_tagstruct_get_sample_spec(pa_tagstruct *t, pa_sample_spec *ss) {
- assert(t && ss);
+ pa_assert(t);
+ pa_assert(ss);
if (t->rindex+7 > t->length)
return -1;
@@ -319,7 +345,9 @@ int pa_tagstruct_get_sample_spec(pa_tagstruct *t, pa_sample_spec *ss) {
int pa_tagstruct_get_arbitrary(pa_tagstruct *t, const void **p, size_t length) {
uint32_t len;
- assert(t && p);
+
+ pa_assert(t);
+ pa_assert(p);
if (t->rindex+5+length > t->length)
return -1;
@@ -337,18 +365,23 @@ int pa_tagstruct_get_arbitrary(pa_tagstruct *t, const void **p, size_t length) {
}
int pa_tagstruct_eof(pa_tagstruct*t) {
- assert(t);
+ pa_assert(t);
+
return t->rindex >= t->length;
}
const uint8_t* pa_tagstruct_data(pa_tagstruct*t, size_t *l) {
- assert(t && t->dynamic && l);
+ pa_assert(t);
+ pa_assert(t->dynamic);
+ pa_assert(l);
+
*l = t->length;
return t->data;
}
int pa_tagstruct_get_boolean(pa_tagstruct*t, int *b) {
- assert(t && b);
+ pa_assert(t);
+ pa_assert(b);
if (t->rindex+1 > t->length)
return -1;
@@ -366,6 +399,9 @@ int pa_tagstruct_get_boolean(pa_tagstruct*t, int *b) {
int pa_tagstruct_get_timeval(pa_tagstruct*t, struct timeval *tv) {
+ pa_assert(t);
+ pa_assert(tv);
+
if (t->rindex+9 > t->length)
return -1;
@@ -382,7 +418,9 @@ int pa_tagstruct_get_timeval(pa_tagstruct*t, struct timeval *tv) {
int pa_tagstruct_get_usec(pa_tagstruct*t, pa_usec_t *u) {
uint32_t tmp;
- assert(t && u);
+
+ pa_assert(t);
+ pa_assert(u);
if (t->rindex+9 > t->length)
return -1;
@@ -400,7 +438,9 @@ int pa_tagstruct_get_usec(pa_tagstruct*t, pa_usec_t *u) {
int pa_tagstruct_getu64(pa_tagstruct*t, uint64_t *u) {
uint32_t tmp;
- assert(t && u);
+
+ pa_assert(t);
+ pa_assert(u);
if (t->rindex+9 > t->length)
return -1;
@@ -418,7 +458,9 @@ int pa_tagstruct_getu64(pa_tagstruct*t, uint64_t *u) {
int pa_tagstruct_gets64(pa_tagstruct*t, int64_t *u) {
uint32_t tmp;
- assert(t && u);
+
+ pa_assert(t);
+ pa_assert(u);
if (t->rindex+9 > t->length)
return -1;
@@ -437,8 +479,8 @@ int pa_tagstruct_gets64(pa_tagstruct*t, int64_t *u) {
int pa_tagstruct_get_channel_map(pa_tagstruct *t, pa_channel_map *map) {
unsigned i;
- assert(t);
- assert(map);
+ pa_assert(t);
+ pa_assert(map);
if (t->rindex+2 > t->length)
return -1;
@@ -463,8 +505,8 @@ int pa_tagstruct_get_cvolume(pa_tagstruct *t, pa_cvolume *cvolume) {
unsigned i;
pa_volume_t vol;
- assert(t);
- assert(cvolume);
+ pa_assert(t);
+ pa_assert(cvolume);
if (t->rindex+2 > t->length)
return -1;
@@ -489,7 +531,7 @@ int pa_tagstruct_get_cvolume(pa_tagstruct *t, pa_cvolume *cvolume) {
void pa_tagstruct_put(pa_tagstruct *t, ...) {
va_list va;
- assert(t);
+ pa_assert(t);
va_start(va, t);
@@ -550,7 +592,7 @@ void pa_tagstruct_put(pa_tagstruct *t, ...) {
break;
default:
- abort();
+ pa_assert_not_reached();
}
}
@@ -561,7 +603,7 @@ int pa_tagstruct_get(pa_tagstruct *t, ...) {
va_list va;
int ret = 0;
- assert(t);
+ pa_assert(t);
va_start(va, t);
while (ret == 0) {
@@ -620,9 +662,8 @@ int pa_tagstruct_get(pa_tagstruct *t, ...) {
ret = pa_tagstruct_get_cvolume(t, va_arg(va, pa_cvolume *));
break;
-
default:
- abort();
+ pa_assert_not_reached();
}
}
diff --git a/src/pulsecore/thread-mq.c b/src/pulsecore/thread-mq.c
new file mode 100644
index 00000000..9b879425
--- /dev/null
+++ b/src/pulsecore/thread-mq.c
@@ -0,0 +1,112 @@
+/* $Id$ */
+
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2006 Lennart Poettering
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <unistd.h>
+#include <errno.h>
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/atomic.h>
+#include <pulsecore/once.h>
+#include <pulsecore/log.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/semaphore.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/flist.h>
+
+#include "thread-mq.h"
+
+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) {
+ pa_thread_mq *q = userdata;
+ pa_asyncmsgq *aq;
+
+ pa_assert(pa_asyncmsgq_get_fd(q->outq) == fd);
+ pa_assert(events == PA_IO_EVENT_INPUT);
+
+ pa_asyncmsgq_ref(aq = q->outq);
+ pa_asyncmsgq_after_poll(aq);
+
+ for (;;) {
+ pa_msgobject *object;
+ int code;
+ void *data;
+ int64_t offset;
+ pa_memchunk chunk;
+
+ /* Check whether there is a message for us to process */
+ while (pa_asyncmsgq_get(aq, &object, &code, &data, &offset, &chunk, 0) == 0) {
+ int ret;
+
+ ret = pa_asyncmsgq_dispatch(object, code, data, offset, &chunk);
+ pa_asyncmsgq_done(aq, ret);
+ }
+
+ if (pa_asyncmsgq_before_poll(aq) == 0)
+ break;
+ }
+
+ pa_asyncmsgq_unref(aq);
+}
+
+void pa_thread_mq_init(pa_thread_mq *q, pa_mainloop_api *mainloop) {
+ pa_assert(q);
+ pa_assert(mainloop);
+
+ q->mainloop = mainloop;
+ pa_assert_se(q->inq = pa_asyncmsgq_new(0));
+ pa_assert_se(q->outq = pa_asyncmsgq_new(0));
+
+ pa_assert_se(pa_asyncmsgq_before_poll(q->outq) == 0);
+ pa_assert_se(q->io_event = mainloop->io_new(mainloop, pa_asyncmsgq_get_fd(q->outq), PA_IO_EVENT_INPUT, asyncmsgq_cb, q));
+}
+
+void pa_thread_mq_done(pa_thread_mq *q) {
+ pa_assert(q);
+
+ q->mainloop->io_free(q->io_event);
+ q->io_event = NULL;
+
+ pa_asyncmsgq_unref(q->inq);
+ pa_asyncmsgq_unref(q->outq);
+ q->inq = q->outq = NULL;
+
+ q->mainloop = NULL;
+}
+
+void pa_thread_mq_install(pa_thread_mq *q) {
+ pa_assert(q);
+
+ pa_assert(!(PA_STATIC_TLS_GET(thread_mq)));
+ PA_STATIC_TLS_SET(thread_mq, q);
+}
+
+pa_thread_mq *pa_thread_mq_get(void) {
+ return PA_STATIC_TLS_GET(thread_mq);
+}
diff --git a/src/pulsecore/thread-mq.h b/src/pulsecore/thread-mq.h
new file mode 100644
index 00000000..13b6e01f
--- /dev/null
+++ b/src/pulsecore/thread-mq.h
@@ -0,0 +1,49 @@
+#ifndef foopulsethreadmqhfoo
+#define foopulsethreadmqhfoo
+
+/* $Id$ */
+
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2004-2006 Lennart Poettering
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.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/mainloop-api.h>
+#include <pulsecore/asyncmsgq.h>
+
+/* Two way communication between a thread and a mainloop. Before the
+ * thread is started a pa_pthread_mq should be initialized and than
+ * attached to the thread using pa_thread_mq_install(). */
+
+typedef struct pa_thread_mq {
+ pa_mainloop_api *mainloop;
+ pa_asyncmsgq *inq, *outq;
+ pa_io_event *io_event;
+} pa_thread_mq;
+
+void pa_thread_mq_init(pa_thread_mq *q, pa_mainloop_api *mainloop);
+void pa_thread_mq_done(pa_thread_mq *q);
+
+/* Install the specified pa_thread_mq object for the current thread */
+void pa_thread_mq_install(pa_thread_mq *q);
+
+/* Return the pa_thread_mq object that is set for the current thread */
+pa_thread_mq *pa_thread_mq_get(void);
+
+#endif
diff --git a/src/pulsecore/thread-posix.c b/src/pulsecore/thread-posix.c
index 4271fa42..7f43f43e 100644
--- a/src/pulsecore/thread-posix.c
+++ b/src/pulsecore/thread-posix.c
@@ -26,7 +26,6 @@
#include <config.h>
#endif
-#include <assert.h>
#include <pthread.h>
#include <sched.h>
#include <errno.h>
@@ -35,56 +34,44 @@
#include <pulsecore/mutex.h>
#include <pulsecore/once.h>
#include <pulsecore/atomic.h>
+#include <pulsecore/macro.h>
#include "thread.h"
-#define ASSERT_SUCCESS(x) do { \
- int _r = (x); \
- assert(_r == 0); \
-} while(0)
-
struct pa_thread {
pthread_t id;
pa_thread_func_t thread_func;
void *userdata;
- pa_atomic_int_t running;
+ pa_atomic_t running;
};
struct pa_tls {
pthread_key_t key;
};
-static pa_tls *thread_tls;
-static pa_once_t thread_tls_once = PA_ONCE_INIT;
-
-static void tls_free_cb(void *p) {
+static void thread_free_cb(void *p) {
pa_thread *t = p;
- assert(t);
+ pa_assert(t);
if (!t->thread_func)
/* This is a foreign thread, we need to free the struct */
pa_xfree(t);
}
-static void thread_tls_once_func(void) {
- thread_tls = pa_tls_new(tls_free_cb);
- assert(thread_tls);
-}
+PA_STATIC_TLS_DECLARE(current_thread, thread_free_cb);
static void* internal_thread_func(void *userdata) {
pa_thread *t = userdata;
- assert(t);
+ pa_assert(t);
t->id = pthread_self();
- pa_once(&thread_tls_once, thread_tls_once_func);
-
- pa_tls_set(thread_tls, t);
+ PA_STATIC_TLS_SET(current_thread, t);
pa_atomic_inc(&t->running);
t->thread_func(t->userdata);
- pa_atomic_add(&t->running, -2);
+ pa_atomic_sub(&t->running, 2);
return NULL;
}
@@ -92,7 +79,7 @@ static void* internal_thread_func(void *userdata) {
pa_thread* pa_thread_new(pa_thread_func_t thread_func, void *userdata) {
pa_thread *t;
- assert(thread_func);
+ pa_assert(thread_func);
t = pa_xnew(pa_thread, 1);
t->thread_func = thread_func;
@@ -110,26 +97,26 @@ pa_thread* pa_thread_new(pa_thread_func_t thread_func, void *userdata) {
}
int pa_thread_is_running(pa_thread *t) {
- assert(t);
+ pa_assert(t);
/* Unfortunately there is no way to tell whether a "foreign"
* thread is still running. See
* http://udrepper.livejournal.com/16844.html for more
* information */
- assert(t->thread_func);
+ pa_assert(t->thread_func);
return pa_atomic_load(&t->running) > 0;
}
void pa_thread_free(pa_thread *t) {
- assert(t);
+ pa_assert(t);
pa_thread_join(t);
pa_xfree(t);
}
int pa_thread_join(pa_thread *t) {
- assert(t);
+ pa_assert(t);
return pthread_join(t->id, NULL);
}
@@ -137,9 +124,7 @@ int pa_thread_join(pa_thread *t) {
pa_thread* pa_thread_self(void) {
pa_thread *t;
- pa_once(&thread_tls_once, thread_tls_once_func);
-
- if ((t = pa_tls_get(thread_tls)))
+ if ((t = PA_STATIC_TLS_GET(current_thread)))
return t;
/* This is a foreign thread, let's create a pthread structure to
@@ -151,19 +136,19 @@ pa_thread* pa_thread_self(void) {
t->userdata = NULL;
pa_atomic_store(&t->running, 2);
- pa_tls_set(thread_tls, t);
+ PA_STATIC_TLS_SET(current_thread, t);
return t;
}
void* pa_thread_get_data(pa_thread *t) {
- assert(t);
+ pa_assert(t);
return t->userdata;
}
void pa_thread_set_data(pa_thread *t, void *userdata) {
- assert(t);
+ pa_assert(t);
t->userdata = userdata;
}
@@ -172,7 +157,7 @@ void pa_thread_yield(void) {
#ifdef HAVE_PTHREAD_YIELD
pthread_yield();
#else
- ASSERT_SUCCESS(sched_yield());
+ pa_assert_se(sched_yield() == 0);
#endif
}
@@ -190,14 +175,14 @@ pa_tls* pa_tls_new(pa_free_cb_t free_cb) {
}
void pa_tls_free(pa_tls *t) {
- assert(t);
+ pa_assert(t);
- ASSERT_SUCCESS(pthread_key_delete(t->key));
+ pa_assert_se(pthread_key_delete(t->key) == 0);
pa_xfree(t);
}
void *pa_tls_get(pa_tls *t) {
- assert(t);
+ pa_assert(t);
return pthread_getspecific(t->key);
}
@@ -206,7 +191,7 @@ void *pa_tls_set(pa_tls *t, void *userdata) {
void *r;
r = pthread_getspecific(t->key);
- ASSERT_SUCCESS(pthread_setspecific(t->key, 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 46d273b4..cad1420a 100644
--- a/src/pulsecore/thread-win32.c
+++ b/src/pulsecore/thread-win32.c
@@ -53,9 +53,8 @@ struct pa_tls_monitor {
};
static pa_tls *thread_tls;
-static pa_once_t thread_tls_once = PA_ONCE_INIT;
+static pa_once thread_tls_once = PA_ONCE_INIT;
static pa_tls *monitor_tls;
-static pa_once_t monitor_tls_once = PA_ONCE_INIT;
static void thread_tls_once_func(void) {
thread_tls = pa_tls_new(NULL);
@@ -66,7 +65,7 @@ static DWORD WINAPI internal_thread_func(LPVOID param) {
pa_thread *t = param;
assert(t);
- pa_once(&thread_tls_once, thread_tls_once_func);
+ pa_run_once(&thread_tls_once, thread_tls_once_func);
pa_tls_set(thread_tls, t);
t->thread_func(t->userdata);
@@ -122,7 +121,7 @@ int pa_thread_join(pa_thread *t) {
}
pa_thread* pa_thread_self(void) {
- pa_once(&thread_tls_once, thread_tls_once_func);
+ pa_run_once(&thread_tls_once, thread_tls_once_func);
return pa_tls_get(thread_tls);
}
@@ -130,12 +129,6 @@ void pa_thread_yield(void) {
Sleep(0);
}
-static void monitor_tls_once_func(void) {
- monitor_tls = pa_tls_new(NULL);
- assert(monitor_tls);
- pa_tls_set(monitor_tls, NULL);
-}
-
static DWORD WINAPI monitor_thread_func(LPVOID param) {
struct pa_tls_monitor *m = param;
assert(m);
@@ -191,7 +184,11 @@ void *pa_tls_set(pa_tls *t, void *userdata) {
if (t->free_func) {
struct pa_tls_monitor *m;
- pa_once(&monitor_tls_once, monitor_tls_once_func);
+ PA_ONCE_BEGIN {
+ monitor_tls = pa_tls_new(NULL);
+ assert(monitor_tls);
+ pa_tls_set(monitor_tls, NULL);
+ } PA_ONCE_END;
m = pa_tls_get(monitor_tls);
if (!m) {
diff --git a/src/pulsecore/thread.h b/src/pulsecore/thread.h
index ca1fe4da..54ef320e 100644
--- a/src/pulsecore/thread.h
+++ b/src/pulsecore/thread.h
@@ -26,6 +26,11 @@
***/
#include <pulse/def.h>
+#include <pulsecore/once.h>
+
+#ifndef PACKAGE
+#error "Please include config.h before including this file!"
+#endif
typedef struct pa_thread pa_thread;
@@ -48,4 +53,60 @@ void pa_tls_free(pa_tls *t);
void * pa_tls_get(pa_tls *t);
void *pa_tls_set(pa_tls *t, void *userdata);
+#define PA_STATIC_TLS_DECLARE(name, free_cb) \
+ static struct { \
+ pa_once once; \
+ pa_tls *tls; \
+ } name##_tls = { \
+ .once = PA_ONCE_INIT, \
+ .tls = NULL \
+ }; \
+ static void name##_tls_init(void) { \
+ name##_tls.tls = pa_tls_new(free_cb); \
+ } \
+ static inline pa_tls* name##_tls_obj(void) { \
+ pa_run_once(&name##_tls.once, name##_tls_init); \
+ return name##_tls.tls; \
+ } \
+ static void name##_tls_destructor(void) PA_GCC_DESTRUCTOR; \
+ static void name##_tls_destructor(void) { \
+ static void (*_free_cb)(void*) = free_cb; \
+ if (!name##_tls.tls) \
+ return; \
+ if (_free_cb) { \
+ void *p; \
+ if ((p = pa_tls_get(name##_tls.tls))) \
+ _free_cb(p); \
+ } \
+ pa_tls_free(name##_tls.tls); \
+ } \
+ static inline void* name##_tls_get(void) { \
+ return pa_tls_get(name##_tls_obj()); \
+ } \
+ static inline void* name##_tls_set(void *p) { \
+ return pa_tls_set(name##_tls_obj(), p); \
+ } \
+ struct __stupid_useless_struct_to_allow_trailing_semicolon
+
+#ifdef HAVE_TLS_BUILTIN
+/* An optimized version of the above that requires no dynamic
+ * allocation if the compiler supports __thread */
+#define PA_STATIC_TLS_DECLARE_NO_FREE(name) \
+ static __thread void *name##_tls = NULL; \
+ static inline void* name##_tls_get(void) { \
+ return name##_tls; \
+ } \
+ static inline void* name##_tls_set(void *p) { \
+ void *r = name##_tls; \
+ name##_tls = p; \
+ return r; \
+ } \
+ struct __stupid_useless_struct_to_allow_trailing_semicolon
+#else
+#define PA_STATIC_TLS_DECLARE_NO_FREE(name) PA_STATIC_TLS_DECLARE(name, NULL)
+#endif
+
+#define PA_STATIC_TLS_GET(name) (name##_tls_get())
+#define PA_STATIC_TLS_SET(name, p) (name##_tls_set(p))
+
#endif
diff --git a/src/pulsecore/time-smoother.c b/src/pulsecore/time-smoother.c
new file mode 100644
index 00000000..6bda3df0
--- /dev/null
+++ b/src/pulsecore/time-smoother.c
@@ -0,0 +1,378 @@
+/* $Id$ */
+
+/***
+ 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/macro.h>
+
+#include "time-smoother.h"
+
+#define HISTORY_MAX 50
+
+/*
+ * Implementation of a time smoothing algorithm to synchronize remote
+ * clocks to a local one. Evens out noise, adjusts to clock skew and
+ * allows cheap estimations of the remote time while clock updates may
+ * be seldom and recieved in non-equidistant intervals.
+ *
+ * Basically, we estimate the gradient of received clock samples in a
+ * certain history window (of size 'history_time') with linear
+ * regression. With that info we estimate the remote time in
+ * 'adjust_time' ahead and smoothen our current estimation function
+ * towards that point with a 3rd order polynomial interpolation with
+ * fitting derivatives. (more or less a b-spline)
+ *
+ * The larger 'history_time' is chosen the better we will surpress
+ * noise -- but we'll adjust to clock skew slower..
+ *
+ * The larger 'adjust_time' is chosen the smoother our estimation
+ * function will be -- but we'll adjust to clock skew slower, too.
+ *
+ * If 'monotonic' is TRUE the resulting estimation function is
+ * guaranteed to be monotonic.
+ */
+
+struct pa_smoother {
+ pa_usec_t adjust_time, history_time;
+ pa_bool_t monotonic;
+
+ pa_usec_t time_offset;
+
+ pa_usec_t px, py; /* Point p, where we want to reach stability */
+ double dp; /* Gradient we want at point p */
+
+ pa_usec_t ex, ey; /* Point e, which we estimated before and need to smooth to */
+ double de; /* Gradient we estimated for point e */
+
+ /* 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;
+
+ /* 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_usec_t pause_time;
+};
+
+pa_smoother* pa_smoother_new(pa_usec_t adjust_time, pa_usec_t history_time, pa_bool_t monotonic) {
+ pa_smoother *s;
+
+ pa_assert(adjust_time > 0);
+ pa_assert(history_time > 0);
+
+ s = pa_xnew(pa_smoother, 1);
+ s->adjust_time = adjust_time;
+ s->history_time = history_time;
+ s->time_offset = 0;
+ s->monotonic = monotonic;
+
+ s->px = s->py = 0;
+ s->dp = 1;
+
+ s->ex = s->ey = 0;
+ s->de = 1;
+
+ s->history_idx = 0;
+ s->n_history = 0;
+
+ s->last_y = 0;
+
+ s->abc_valid = FALSE;
+
+ s->paused = FALSE;
+
+ return s;
+}
+
+void pa_smoother_free(pa_smoother* s) {
+ pa_assert(s);
+
+ pa_xfree(s);
+}
+
+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 */
+
+ for (j = s->n_history; j > 2; j--) {
+
+ 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;
+
+ s->n_history --;
+ }
+}
+
+static void add_to_history(pa_smoother *s, pa_usec_t x, pa_usec_t y) {
+ unsigned j;
+ pa_assert(s);
+
+ drop_old(s, x);
+
+ /* Calculate position for new entry */
+ j = s->history_idx + s->n_history;
+ while (j >= HISTORY_MAX)
+ j -= HISTORY_MAX;
+
+ /* Fill in entry */
+ s->history_x[j] = x;
+ s->history_y[j] = y;
+
+ /* Adjust counter */
+ s->n_history ++;
+
+ /* And make sure we don't store more entries than fit in */
+ if (s->n_history >= HISTORY_MAX) {
+ s->history_idx += s->n_history - HISTORY_MAX;
+ s->n_history = HISTORY_MAX;
+ }
+}
+
+static double avg_gradient(pa_smoother *s, pa_usec_t x) {
+ unsigned i, j, c = 0;
+ int64_t ax = 0, ay = 0, k, t;
+ double r;
+
+ drop_old(s, x);
+
+ /* 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];
+ c++;
+
+ i++;
+ while (i >= HISTORY_MAX)
+ i -= HISTORY_MAX;
+ }
+
+ /* Too few measurements, assume gradient of 1 */
+ if (c < 2)
+ return 1;
+
+ ax /= c;
+ ay /= c;
+
+ /* Now, do linear regression */
+ k = t = 0;
+
+ i = s->history_idx;
+ for (j = s->n_history; j > 0; j--) {
+ int64_t dx, dy;
+
+ dx = (int64_t) s->history_x[i] - ax;
+ dy = (int64_t) s->history_y[i] - ay;
+
+ k += dx*dy;
+ t += dx*dx;
+
+ i++;
+ while (i >= HISTORY_MAX)
+ i -= HISTORY_MAX;
+ }
+
+ r = (double) k / t;
+
+ return s->monotonic && r < 0 ? 0 : r;
+}
+
+static void estimate(pa_smoother *s, pa_usec_t x, pa_usec_t *y, double *deriv) {
+ pa_assert(s);
+ pa_assert(y);
+
+ if (x >= s->px) {
+ int64_t t;
+
+ /* 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));
+
+ if (t < 0)
+ t = 0;
+
+ *y = (pa_usec_t) t;
+
+ if (deriv)
+ *deriv = s->dp;
+
+ } else {
+
+ if (!s->abc_valid) {
+ pa_usec_t ex, ey, px, py;
+ int64_t kx, ky;
+ double de, dp;
+
+ /* Ok, we're not yet on track, thus let's interpolate, and
+ * make sure that the first derivative is smooth */
+
+ /* We have two points: (ex|ey) and (px|py) with two gradients
+ * at these points de and dp. We do a polynomial interpolation
+ * of degree 3 with these 6 values */
+
+ ex = s->ex; ey = s->ey;
+ px = s->px; py = s->py;
+ de = s->de; dp = s->dp;
+
+ 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;
+ }
+
+ /* Move to origin */
+ x -= s->ex;
+
+ /* Horner scheme */
+ *y = (pa_usec_t) ((double) x * (s->c + (double) x * (s->b + (double) x * s->a)));
+
+ /* Move back from origin */
+ *y += s->ey;
+
+ /* Horner scheme */
+ if (deriv)
+ *deriv = s->c + ((double) x * (s->b*2 + (double) x * 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;
+ }
+}
+
+void pa_smoother_put(pa_smoother *s, pa_usec_t x, pa_usec_t y) {
+ pa_usec_t ney;
+ double nde;
+
+ pa_assert(s);
+ pa_assert(x >= s->time_offset);
+
+ /* Fix up x value */
+ if (s->paused)
+ x = s->pause_time;
+ else
+ x -= s->time_offset;
+
+ pa_assert(x >= s->ex);
+
+ /* 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;
+
+ /* Then, we add the new measurement to our history */
+ add_to_history(s, x, y);
+
+ /* And determine the average gradient of the history */
+ 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->abc_valid = FALSE;
+}
+
+pa_usec_t pa_smoother_get(pa_smoother *s, pa_usec_t x) {
+ pa_usec_t y;
+
+ pa_assert(s);
+ pa_assert(x >= s->time_offset);
+
+ /* Fix up x value */
+ if (s->paused)
+ x = s->pause_time;
+ else
+ x -= s->time_offset;
+
+ pa_assert(x >= s->ex);
+
+ estimate(s, x, &y, NULL);
+ return y;
+}
+
+void pa_smoother_set_time_offset(pa_smoother *s, pa_usec_t offset) {
+ pa_assert(s);
+
+ s->time_offset = offset;
+}
+
+void pa_smoother_pause(pa_smoother *s, pa_usec_t x) {
+ pa_assert(s);
+
+ if (s->paused)
+ return;
+
+ s->paused = TRUE;
+ s->pause_time = x;
+}
+
+void pa_smoother_resume(pa_smoother *s, pa_usec_t x) {
+ pa_assert(s);
+
+ if (!s->paused)
+ return;
+
+ s->paused = FALSE;
+ s->time_offset += x - s->pause_time;
+}
diff --git a/src/pulsecore/time-smoother.h b/src/pulsecore/time-smoother.h
new file mode 100644
index 00000000..8b8512e2
--- /dev/null
+++ b/src/pulsecore/time-smoother.h
@@ -0,0 +1,43 @@
+#ifndef foopulsetimesmootherhfoo
+#define foopulsetimesmootherhfoo
+
+/* $Id$ */
+
+/***
+ 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 <pulse/sample.h>
+
+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);
+void pa_smoother_free(pa_smoother* s);
+
+void pa_smoother_put(pa_smoother *s, pa_usec_t x, pa_usec_t y);
+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);
+
+void pa_smoother_pause(pa_smoother *s, pa_usec_t x);
+void pa_smoother_resume(pa_smoother *s, pa_usec_t x);
+
+#endif
diff --git a/src/pulsecore/tokenizer.c b/src/pulsecore/tokenizer.c
index 117c7f88..f79c19c5 100644
--- a/src/pulsecore/tokenizer.c
+++ b/src/pulsecore/tokenizer.c
@@ -26,20 +26,16 @@
#endif
#include <string.h>
-#include <assert.h>
#include <stdlib.h>
#include <pulse/xmalloc.h>
#include <pulsecore/dynarray.h>
#include <pulsecore/gccmacro.h>
+#include <pulsecore/macro.h>
#include "tokenizer.h"
-struct pa_tokenizer {
- pa_dynarray *dynarray;
-};
-
static void token_free(void *p, PA_GCC_UNUSED void *userdata) {
pa_xfree(p);
}
@@ -48,7 +44,9 @@ static void parse(pa_dynarray*a, const char *s, unsigned args) {
int infty = 0;
const char delimiter[] = " \t\n\r";
const char *p;
- assert(a && s);
+
+ pa_assert(a);
+ pa_assert(s);
if (args == 0)
infty = 1;
@@ -70,23 +68,23 @@ static void parse(pa_dynarray*a, const char *s, unsigned args) {
}
pa_tokenizer* pa_tokenizer_new(const char *s, unsigned args) {
- pa_tokenizer *t;
-
- t = pa_xmalloc(sizeof(pa_tokenizer));
- t->dynarray = pa_dynarray_new();
- assert(t->dynarray);
+ pa_dynarray *a;
- parse(t->dynarray, s, args);
- return t;
+ a = pa_dynarray_new();
+ parse(a, s, args);
+ return (pa_tokenizer*) a;
}
void pa_tokenizer_free(pa_tokenizer *t) {
- assert(t);
- pa_dynarray_free(t->dynarray, token_free, NULL);
- pa_xfree(t);
+ pa_dynarray *a = (pa_dynarray*) t;
+
+ pa_assert(a);
+ pa_dynarray_free(a, token_free, NULL);
}
const char *pa_tokenizer_get(pa_tokenizer *t, unsigned i) {
- assert(t);
- return pa_dynarray_get(t->dynarray, i);
+ pa_dynarray *a = (pa_dynarray*) t;
+
+ pa_assert(a);
+ return pa_dynarray_get(a, i);
}
diff --git a/src/pulsecore/winsock.h b/src/pulsecore/winsock.h
index ae868b38..0352bf4d 100644
--- a/src/pulsecore/winsock.h
+++ b/src/pulsecore/winsock.h
@@ -15,6 +15,8 @@
#define EHOSTUNREACH WSAEHOSTUNREACH
#define EWOULDBLOCK WSAEWOULDBLOCK
+typedef long suseconds_t;
+
#endif
#ifdef HAVE_WS2TCPIP_H
diff --git a/src/pulsecore/x11prop.c b/src/pulsecore/x11prop.c
index 5b85ea42..a740e39b 100644
--- a/src/pulsecore/x11prop.c
+++ b/src/pulsecore/x11prop.c
@@ -32,7 +32,6 @@
#include "x11prop.h"
-
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);
diff --git a/src/pulsecore/x11wrap.c b/src/pulsecore/x11wrap.c
index 6a6a2692..800a9458 100644
--- a/src/pulsecore/x11wrap.c
+++ b/src/pulsecore/x11wrap.c
@@ -21,7 +21,10 @@
USA.
***/
-#include <assert.h>
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
#include <stdio.h>
#include <pulse/xmalloc.h>
@@ -29,6 +32,8 @@
#include <pulsecore/llist.h>
#include <pulsecore/log.h>
#include <pulsecore/props.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/macro.h>
#include "x11wrap.h"
@@ -42,8 +47,8 @@ struct pa_x11_internal {
};
struct pa_x11_wrapper {
+ PA_REFCNT_DECLARE;
pa_core *core;
- int ref;
char *property_name;
Display *display;
@@ -64,7 +69,8 @@ struct pa_x11_client {
/* Dispatch all pending X11 events */
static void work(pa_x11_wrapper *w) {
- assert(w && w->ref >= 1);
+ pa_assert(w);
+ pa_assert(PA_REFCNT_VALUE(w) >= 1);
while (XPending(w->display)) {
pa_x11_client *c;
@@ -72,7 +78,7 @@ static void work(pa_x11_wrapper *w) {
XNextEvent(w->display, &e);
for (c = w->clients; c; c = c->next) {
- assert(c->callback);
+ pa_assert(c->callback);
if (c->callback(w, &e, c->userdata) != 0)
break;
}
@@ -82,14 +88,24 @@ static void work(pa_x11_wrapper *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) {
pa_x11_wrapper *w = userdata;
- assert(m && e && fd >= 0 && w && w->ref >= 1);
+
+ pa_assert(m);
+ pa_assert(e);
+ pa_assert(fd >= 0);
+ pa_assert(w);
+ pa_assert(PA_REFCNT_VALUE(w) >= 1);
+
work(w);
}
/* Deferred notification event. Called once each main loop iteration */
static void defer_event(pa_mainloop_api *m, pa_defer_event *e, void *userdata) {
pa_x11_wrapper *w = userdata;
- assert(m && e && w && w->ref >= 1);
+
+ pa_assert(m);
+ pa_assert(e);
+ pa_assert(w);
+ pa_assert(PA_REFCNT_VALUE(w) >= 1);
m->defer_enable(e, 0);
@@ -99,7 +115,12 @@ static void defer_event(pa_mainloop_api *m, pa_defer_event *e, void *userdata) {
/* IO notification event for X11 internal connections */
static void internal_io_event(pa_mainloop_api *m, pa_io_event *e, int fd, PA_GCC_UNUSED pa_io_event_flags_t f, void *userdata) {
pa_x11_wrapper *w = userdata;
- assert(m && e && fd >= 0 && w && w->ref >= 1);
+
+ pa_assert(m);
+ pa_assert(e);
+ pa_assert(fd >= 0);
+ pa_assert(w);
+ pa_assert(PA_REFCNT_VALUE(w) >= 1);
XProcessInternalConnection(w->display, fd);
@@ -109,10 +130,9 @@ static void internal_io_event(pa_mainloop_api *m, pa_io_event *e, int fd, PA_GCC
/* Add a new IO source for the specified X11 internal connection */
static pa_x11_internal* x11_internal_add(pa_x11_wrapper *w, int fd) {
pa_x11_internal *i;
- assert(fd >= 0);
+ pa_assert(fd >= 0);
- i = pa_xmalloc(sizeof(pa_x11_internal));
- assert(i);
+ i = pa_xnew(pa_x11_internal, 1);
i->wrapper = w;
i->io_event = w->core->mainloop->io_new(w->core->mainloop, fd, PA_IO_EVENT_INPUT, internal_io_event, w);
i->fd = fd;
@@ -123,7 +143,7 @@ static pa_x11_internal* x11_internal_add(pa_x11_wrapper *w, int fd) {
/* Remove an IO source for an X11 internal connection */
static void x11_internal_remove(pa_x11_wrapper *w, pa_x11_internal *i) {
- assert(i);
+ pa_assert(i);
PA_LLIST_REMOVE(pa_x11_internal, w->internals, i);
w->core->mainloop->io_free(i->io_event);
@@ -133,7 +153,10 @@ static void x11_internal_remove(pa_x11_wrapper *w, pa_x11_internal *i) {
/* Implementation of XConnectionWatchProc */
static void x11_watch(Display *display, XPointer userdata, int fd, Bool opening, XPointer *watch_data) {
pa_x11_wrapper *w = (pa_x11_wrapper*) userdata;
- assert(display && w && fd >= 0);
+
+ pa_assert(display);
+ pa_assert(w);
+ pa_assert(fd >= 0);
if (opening)
*watch_data = (XPointer) x11_internal_add(w, fd);
@@ -144,16 +167,15 @@ static void x11_watch(Display *display, XPointer userdata, int fd, Bool opening,
static pa_x11_wrapper* x11_wrapper_new(pa_core *c, const char *name, const char *t) {
pa_x11_wrapper*w;
Display *d;
- int r;
if (!(d = XOpenDisplay(name))) {
pa_log("XOpenDisplay() failed");
return NULL;
}
- w = pa_xmalloc(sizeof(pa_x11_wrapper));
+ w = pa_xnew(pa_x11_wrapper, 1);
+ PA_REFCNT_INIT(w);
w->core = c;
- w->ref = 1;
w->property_name = pa_xstrdup(t);
w->display = d;
@@ -165,20 +187,17 @@ static pa_x11_wrapper* x11_wrapper_new(pa_core *c, const char *name, const char
XAddConnectionWatch(d, x11_watch, (XPointer) w);
- r = pa_property_set(c, w->property_name, w);
- assert(r >= 0);
+ pa_assert_se(pa_property_set(c, w->property_name, w) >= 0);
return w;
}
static void x11_wrapper_free(pa_x11_wrapper*w) {
- int r;
- assert(w);
+ pa_assert(w);
- r = pa_property_remove(w->core, w->property_name);
- assert(r >= 0);
+ pa_assert_se(pa_property_remove(w->core, w->property_name) >= 0);
- assert(!w->clients);
+ pa_assert(!w->clients);
XRemoveConnectionWatch(w->display, x11_watch, (XPointer) w);
XCloseDisplay(w->display);
@@ -196,9 +215,10 @@ static void x11_wrapper_free(pa_x11_wrapper*w) {
pa_x11_wrapper* pa_x11_wrapper_get(pa_core *c, const char *name) {
char t[256];
pa_x11_wrapper *w;
- assert(c);
- snprintf(t, sizeof(t), "x11-wrapper%s%s", name ? "-" : "", name ? 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)))
return pa_x11_wrapper_ref(w);
@@ -206,20 +226,24 @@ pa_x11_wrapper* pa_x11_wrapper_get(pa_core *c, const char *name) {
}
pa_x11_wrapper* pa_x11_wrapper_ref(pa_x11_wrapper *w) {
- assert(w && w->ref >= 1);
- w->ref++;
+ pa_assert(w);
+ pa_assert(PA_REFCNT_VALUE(w) >= 1);
+
+ PA_REFCNT_INC(w);
return w;
}
void pa_x11_wrapper_unref(pa_x11_wrapper* w) {
- assert(w && w->ref >= 1);
+ pa_assert(w);
+ pa_assert(PA_REFCNT_VALUE(w) >= 1);
- if (!(--w->ref))
+ if (PA_REFCNT_DEC(w) <= 0)
x11_wrapper_free(w);
}
Display *pa_x11_wrapper_get_display(pa_x11_wrapper *w) {
- assert(w && w->ref >= 1);
+ pa_assert(w);
+ pa_assert(PA_REFCNT_VALUE(w) >= 1);
/* Somebody is using us, schedule a output buffer flush */
w->core->mainloop->defer_enable(w->defer_event, 1);
@@ -229,9 +253,11 @@ Display *pa_x11_wrapper_get_display(pa_x11_wrapper *w) {
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 *c;
- assert(w && w->ref >= 1);
- c = pa_xmalloc(sizeof(pa_x11_client));
+ pa_assert(w);
+ pa_assert(PA_REFCNT_VALUE(w) >= 1);
+
+ c = pa_xnew(pa_x11_client, 1);
c->wrapper = w;
c->callback = cb;
c->userdata = userdata;
@@ -242,7 +268,9 @@ pa_x11_client* pa_x11_client_new(pa_x11_wrapper *w, int (*cb)(pa_x11_wrapper *w,
}
void pa_x11_client_free(pa_x11_client *c) {
- assert(c && c->wrapper && c->wrapper->ref >= 1);
+ pa_assert(c);
+ pa_assert(c->wrapper);
+ pa_assert(PA_REFCNT_VALUE(c->wrapper) >= 1);
PA_LLIST_REMOVE(pa_x11_client, c->wrapper->clients, c);
pa_xfree(c);