mirror of
https://gitlab.freedesktop.org/pulseaudio/pulseaudio.git
synced 2025-11-06 13:29:56 -05:00
Merge HUGE set of changes temporarily into a branch, to allow me to move them from one machine to another (lock-free and stuff)
git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/lennart@1469 fefdeb5f-60dc-0310-8127-8f9354f1896f
This commit is contained in:
parent
6aeec56708
commit
00da37f2c4
72 changed files with 4389 additions and 1767 deletions
118
src/Makefile.am
118
src/Makefile.am
|
|
@ -78,13 +78,15 @@ if OS_IS_WIN32
|
|||
PA_THREAD_OBJS = \
|
||||
pulsecore/once-win32.c pulsecore/once.h \
|
||||
pulsecore/mutex-win32.c pulsecore/mutex.h \
|
||||
pulsecore/thread-win32.c pulsecore/thread.h
|
||||
pulsecore/thread-win32.c pulsecore/thread.h \
|
||||
pulsecore/semaphore-win32.c pulsecore/semaphore.h
|
||||
else
|
||||
PA_THREAD_OBJS = \
|
||||
pulsecore/atomic.h \
|
||||
pulsecore/once-posix.c pulsecore/once.h \
|
||||
pulsecore/mutex-posix.c pulsecore/mutex.h \
|
||||
pulsecore/thread-posix.c pulsecore/thread.h
|
||||
pulsecore/thread-posix.c pulsecore/thread.h \
|
||||
pulsecore/semaphore-posix.c pulsecore/semaphore.h
|
||||
endif
|
||||
|
||||
###################################
|
||||
|
|
@ -219,7 +221,9 @@ noinst_PROGRAMS = \
|
|||
hook-list-test \
|
||||
memblock-test \
|
||||
thread-test \
|
||||
flist-test
|
||||
flist-test \
|
||||
asyncq-test \
|
||||
asyncmsgq-test
|
||||
|
||||
if HAVE_SIGXCPU
|
||||
noinst_PROGRAMS += \
|
||||
|
|
@ -274,13 +278,21 @@ thread_test_CFLAGS = $(AM_CFLAGS)
|
|||
thread_test_LDADD = $(AM_LDADD) libpulsecore.la
|
||||
thread_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
|
||||
|
||||
flist_test_SOURCES = tests/flist-test.c \
|
||||
pulsecore/atomic.h \
|
||||
pulsecore/flist.c pulsecore/flist.h
|
||||
flist_test_SOURCES = tests/flist-test.c
|
||||
flist_test_CFLAGS = $(AM_CFLAGS)
|
||||
flist_test_LDADD = $(AM_LDADD) libpulsecore.la
|
||||
flist_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
|
||||
|
||||
asyncq_test_SOURCES = tests/asyncq-test.c pulsecore/thread-posix.c pulsecore/thread.h pulsecore/asyncq.c pulsecore/asyncq.h pulsecore/core-util.c pulsecore/core-util.h pulse/xmalloc.c pulse/xmalloc.h pulsecore/log.h pulsecore/log.c pulsecore/core-error.h pulsecore/core-error.c pulsecore/once-posix.c pulsecore/once.h pulsecore/mutex-posix.c pulsecore/mutex.h pulse/utf8.c pulse/utf8.h pulse/util.h pulse/util.c
|
||||
asyncq_test_CFLAGS = $(AM_CFLAGS)
|
||||
asyncq_test_LDADD = $(AM_LDADD) #libpulsecore.la
|
||||
asyncq_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
|
||||
|
||||
asyncmsgq_test_SOURCES = tests/asyncmsgq-test.c pulsecore/thread-posix.c pulsecore/thread.h pulsecore/asyncq.c pulsecore/asyncq.h pulsecore/asyncmsgq.c pulsecore/asyncmsgq.h pulsecore/core-util.c pulsecore/core-util.h pulse/xmalloc.c pulse/xmalloc.h pulsecore/log.h pulsecore/log.c pulsecore/core-error.h pulsecore/core-error.c pulsecore/once-posix.c pulsecore/once.h pulsecore/mutex-posix.c pulsecore/mutex.h pulse/utf8.c pulse/utf8.h pulse/util.h pulse/util.c pulsecore/semaphore.h pulsecore/semaphore-posix.c pulsecore/flist.h pulsecore/flist.c
|
||||
asyncmsgq_test_CFLAGS = $(AM_CFLAGS)
|
||||
asyncmsgq_test_LDADD = $(AM_LDADD) #libpulsecore.la
|
||||
asyncmsgq_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
|
||||
|
||||
mcalign_test_SOURCES = tests/mcalign-test.c
|
||||
mcalign_test_CFLAGS = $(AM_CFLAGS)
|
||||
mcalign_test_LDADD = $(AM_LDADD) $(WINSOCK_LIBS) libpulsecore.la
|
||||
|
|
@ -455,6 +467,9 @@ libpulse_la_SOURCES += \
|
|||
pulsecore/core-error.c pulsecore/core-error.h \
|
||||
pulsecore/winsock.h pulsecore/creds.h \
|
||||
pulsecore/shm.c pulsecore/shm.h \
|
||||
pulsecore/flist.c pulsecore/flist.h \
|
||||
pulsecore/object.c pulsecore/object.h \
|
||||
pulsecore/msgobject.c pulsecore/msgobject.h \
|
||||
$(PA_THREAD_OBJS)
|
||||
|
||||
if OS_IS_WIN32
|
||||
|
|
@ -567,6 +582,7 @@ pulsecoreinclude_HEADERS = \
|
|||
pulsecore/refcnt.h \
|
||||
pulsecore/mutex.h \
|
||||
pulsecore/thread.h \
|
||||
pulsecore/semaphore.h \
|
||||
pulsecore/once.h
|
||||
|
||||
lib_LTLIBRARIES += libpulsecore.la
|
||||
|
|
@ -636,6 +652,8 @@ libpulsecore_la_SOURCES += \
|
|||
pulsecore/core-error.c pulsecore/core-error.h \
|
||||
pulsecore/hook-list.c pulsecore/hook-list.h \
|
||||
pulsecore/shm.c pulsecore/shm.h \
|
||||
pulsecore/flist.c pulsecore/flist.h \
|
||||
pulsecore/anotify.c pulsecore/anotify.h \
|
||||
$(PA_THREAD_OBJS)
|
||||
|
||||
if OS_IS_WIN32
|
||||
|
|
@ -851,34 +869,34 @@ modlibexec_LTLIBRARIES += \
|
|||
module-cli.la \
|
||||
module-cli-protocol-tcp.la \
|
||||
module-simple-protocol-tcp.la \
|
||||
module-esound-protocol-tcp.la \
|
||||
module-native-protocol-tcp.la \
|
||||
module-native-protocol-fd.la \
|
||||
module-sine.la \
|
||||
module-combine.la \
|
||||
module-tunnel-sink.la \
|
||||
module-tunnel-source.la \
|
||||
module-null-sink.la \
|
||||
module-esound-sink.la \
|
||||
module-http-protocol-tcp.la \
|
||||
module-detect.la \
|
||||
module-volume-restore.la \
|
||||
module-rescue-streams.la
|
||||
module-null-sink.la
|
||||
# module-esound-protocol-tcp.la \
|
||||
# module-native-protocol-tcp.la \
|
||||
# module-native-protocol-fd.la \
|
||||
# module-sine.la \
|
||||
# module-combine.la \
|
||||
# module-tunnel-sink.la \
|
||||
# module-tunnel-source.la \
|
||||
# module-esound-sink.la \
|
||||
# module-http-protocol-tcp.la \
|
||||
# module-detect.la \
|
||||
# module-volume-restore.la \
|
||||
# module-rescue-streams.la
|
||||
|
||||
# See comment at librtp.la above
|
||||
if !OS_IS_WIN32
|
||||
modlibexec_LTLIBRARIES += \
|
||||
module-rtp-send.la \
|
||||
module-rtp-recv.la
|
||||
endif
|
||||
#if !OS_IS_WIN32
|
||||
#modlibexec_LTLIBRARIES += \
|
||||
# module-rtp-send.la \
|
||||
# module-rtp-recv.la
|
||||
#endif
|
||||
|
||||
if HAVE_AF_UNIX
|
||||
modlibexec_LTLIBRARIES += \
|
||||
module-cli-protocol-unix.la \
|
||||
module-simple-protocol-unix.la \
|
||||
module-esound-protocol-unix.la \
|
||||
module-native-protocol-unix.la \
|
||||
module-http-protocol-unix.la
|
||||
module-simple-protocol-unix.la
|
||||
# module-esound-protocol-unix.la \
|
||||
# module-native-protocol-unix.la \
|
||||
# module-http-protocol-unix.la
|
||||
endif
|
||||
|
||||
if HAVE_MKFIFO
|
||||
|
|
@ -887,11 +905,11 @@ modlibexec_LTLIBRARIES += \
|
|||
module-pipe-source.la
|
||||
endif
|
||||
|
||||
if !OS_IS_WIN32
|
||||
modlibexec_LTLIBRARIES += \
|
||||
module-esound-compat-spawnfd.la \
|
||||
module-esound-compat-spawnpid.la
|
||||
endif
|
||||
#if !OS_IS_WIN32
|
||||
#modlibexec_LTLIBRARIES += \
|
||||
# module-esound-compat-spawnfd.la \
|
||||
# module-esound-compat-spawnpid.la
|
||||
#endif
|
||||
|
||||
if HAVE_REGEX
|
||||
modlibexec_LTLIBRARIES += \
|
||||
|
|
@ -904,19 +922,19 @@ modlibexec_LTLIBRARIES += \
|
|||
module-x11-publish.la
|
||||
endif
|
||||
|
||||
if HAVE_OSS
|
||||
modlibexec_LTLIBRARIES += \
|
||||
liboss-util.la \
|
||||
module-oss.la \
|
||||
module-oss-mmap.la
|
||||
endif
|
||||
#if HAVE_OSS
|
||||
#modlibexec_LTLIBRARIES += \
|
||||
# liboss-util.la \
|
||||
# module-oss.la \
|
||||
# module-oss-mmap.la
|
||||
#endif
|
||||
|
||||
if HAVE_ALSA
|
||||
modlibexec_LTLIBRARIES += \
|
||||
libalsa-util.la \
|
||||
module-alsa-sink.la \
|
||||
module-alsa-source.la
|
||||
endif
|
||||
#if HAVE_ALSA
|
||||
#modlibexec_LTLIBRARIES += \
|
||||
# libalsa-util.la \
|
||||
# module-alsa-sink.la \
|
||||
# module-alsa-source.la
|
||||
#endif
|
||||
|
||||
if HAVE_SOLARIS
|
||||
modlibexec_LTLIBRARIES += \
|
||||
|
|
@ -938,11 +956,11 @@ modlibexec_LTLIBRARIES += \
|
|||
module-mmkbd-evdev.la
|
||||
endif
|
||||
|
||||
if HAVE_JACK
|
||||
modlibexec_LTLIBRARIES += \
|
||||
module-jack-sink.la \
|
||||
module-jack-source.la
|
||||
endif
|
||||
#if HAVE_JACK
|
||||
#modlibexec_LTLIBRARIES += \
|
||||
# module-jack-sink.la \
|
||||
# module-jack-source.la
|
||||
#endif
|
||||
|
||||
if HAVE_GCONF
|
||||
modlibexec_LTLIBRARIES += \
|
||||
|
|
|
|||
|
|
@ -174,6 +174,7 @@ static void do_write(struct userdata *u) {
|
|||
update_usage(u);
|
||||
|
||||
for (;;) {
|
||||
void *p;
|
||||
pa_memchunk *memchunk = NULL;
|
||||
snd_pcm_sframes_t frames;
|
||||
|
||||
|
|
@ -185,14 +186,15 @@ static void do_write(struct userdata *u) {
|
|||
else
|
||||
memchunk = &u->memchunk;
|
||||
}
|
||||
|
||||
assert(memchunk->memblock);
|
||||
assert(memchunk->memblock->data);
|
||||
assert(memchunk->length);
|
||||
assert(memchunk->memblock->length);
|
||||
assert((memchunk->length % u->frame_size) == 0);
|
||||
|
||||
if ((frames = snd_pcm_writei(u->pcm_handle, (uint8_t*) memchunk->memblock->data + memchunk->index, memchunk->length / u->frame_size)) < 0) {
|
||||
p = pa_memblock_acquire(memchunk->memblock);
|
||||
|
||||
if ((frames = snd_pcm_writei(u->pcm_handle, (uint8_t*) p + memchunk->index, memchunk->length / u->frame_size)) < 0) {
|
||||
pa_memblock_release(memchunk->memblock);
|
||||
|
||||
if (frames == -EAGAIN)
|
||||
return;
|
||||
|
||||
|
|
@ -217,6 +219,8 @@ static void do_write(struct userdata *u) {
|
|||
return;
|
||||
}
|
||||
|
||||
pa_memblock_release(memchunk->memblock);
|
||||
|
||||
if (memchunk == &u->memchunk) {
|
||||
size_t l = frames * u->frame_size;
|
||||
memchunk->index += l;
|
||||
|
|
|
|||
|
|
@ -180,6 +180,7 @@ static void do_read(struct userdata *u) {
|
|||
pa_memchunk post_memchunk;
|
||||
snd_pcm_sframes_t frames;
|
||||
size_t l;
|
||||
void *p;
|
||||
|
||||
if (!u->memchunk.memblock) {
|
||||
u->memchunk.memblock = pa_memblock_new(u->source->core->mempool, u->memchunk.length = u->fragment_size);
|
||||
|
|
@ -188,11 +189,13 @@ static void do_read(struct userdata *u) {
|
|||
|
||||
assert(u->memchunk.memblock);
|
||||
assert(u->memchunk.length);
|
||||
assert(u->memchunk.memblock->data);
|
||||
assert(u->memchunk.memblock->length);
|
||||
assert(u->memchunk.length % u->frame_size == 0);
|
||||
|
||||
if ((frames = snd_pcm_readi(u->pcm_handle, (uint8_t*) u->memchunk.memblock->data + u->memchunk.index, u->memchunk.length / u->frame_size)) < 0) {
|
||||
p = pa_memblock_acquire(u->memchunk.memblock);
|
||||
|
||||
if ((frames = snd_pcm_readi(u->pcm_handle, (uint8_t*) p + u->memchunk.index, u->memchunk.length / u->frame_size)) < 0) {
|
||||
pa_memblock_release(u->memchunk.memblock);
|
||||
|
||||
if (frames == -EAGAIN)
|
||||
return;
|
||||
|
||||
|
|
@ -216,6 +219,7 @@ static void do_read(struct userdata *u) {
|
|||
pa_module_unload_request(u->module);
|
||||
return;
|
||||
}
|
||||
pa_memblock_release(u->memchunk.memblock);
|
||||
|
||||
l = frames * u->frame_size;
|
||||
|
||||
|
|
|
|||
|
|
@ -144,18 +144,25 @@ static int do_write(struct userdata *u) {
|
|||
u->write_index = u->write_length = 0;
|
||||
}
|
||||
} else if (u->state == STATE_RUNNING) {
|
||||
void *p;
|
||||
|
||||
pa_module_set_used(u->module, pa_sink_used_by(u->sink));
|
||||
|
||||
if (!u->memchunk.length)
|
||||
if (pa_sink_render(u->sink, 8192, &u->memchunk) < 0)
|
||||
return 0;
|
||||
|
||||
assert(u->memchunk.memblock && u->memchunk.length);
|
||||
assert(u->memchunk.memblock);
|
||||
assert(u->memchunk.length);
|
||||
|
||||
if ((r = pa_iochannel_write(u->io, (uint8_t*) u->memchunk.memblock->data + u->memchunk.index, u->memchunk.length)) < 0) {
|
||||
p = pa_memblock_acquire(u->memchunk.memblock);
|
||||
|
||||
if ((r = pa_iochannel_write(u->io, (uint8_t*) p + u->memchunk.index, u->memchunk.length)) < 0) {
|
||||
pa_memblock_release(u->memchunk.memblock);
|
||||
pa_log("write() failed: %s", pa_cstrerror(errno));
|
||||
return -1;
|
||||
}
|
||||
pa_memblock_release(u->memchunk.memblock);
|
||||
|
||||
u->memchunk.index += r;
|
||||
u->memchunk.length -= r;
|
||||
|
|
|
|||
|
|
@ -137,22 +137,25 @@ static void io_event_cb(pa_mainloop_api *m, pa_io_event *e, int fd, pa_io_event_
|
|||
unsigned fs;
|
||||
jack_nframes_t frame_idx;
|
||||
pa_memchunk chunk;
|
||||
void *p;
|
||||
|
||||
fs = pa_frame_size(&u->sink->sample_spec);
|
||||
|
||||
pa_sink_render_full(u->sink, u->frames_requested * fs, &chunk);
|
||||
p = pa_memblock_acquire(chunk.memblock);
|
||||
|
||||
for (frame_idx = 0; frame_idx < u->frames_requested; frame_idx ++) {
|
||||
unsigned c;
|
||||
|
||||
for (c = 0; c < u->channels; c++) {
|
||||
float *s = ((float*) ((uint8_t*) chunk.memblock->data + chunk.index)) + (frame_idx * u->channels) + c;
|
||||
float *s = ((float*) ((uint8_t*) p + chunk.index)) + (frame_idx * u->channels) + c;
|
||||
float *d = ((float*) u->buffer[c]) + frame_idx;
|
||||
|
||||
*d = *s;
|
||||
}
|
||||
}
|
||||
|
||||
pa_memblock_release(chunk.memblock);
|
||||
pa_memblock_unref(chunk.memblock);
|
||||
|
||||
u->frames_requested = 0;
|
||||
|
|
|
|||
|
|
@ -136,23 +136,28 @@ static void io_event_cb(pa_mainloop_api *m, pa_io_event *e, int fd, pa_io_event_
|
|||
unsigned fs;
|
||||
jack_nframes_t frame_idx;
|
||||
pa_memchunk chunk;
|
||||
void *p;
|
||||
|
||||
fs = pa_frame_size(&u->source->sample_spec);
|
||||
|
||||
chunk.memblock = pa_memblock_new(u->core->mempool, chunk.length = u->frames_posted * fs);
|
||||
chunk.index = 0;
|
||||
|
||||
p = pa_memblock_acquire(chunk.memblock);
|
||||
|
||||
for (frame_idx = 0; frame_idx < u->frames_posted; frame_idx ++) {
|
||||
unsigned c;
|
||||
|
||||
for (c = 0; c < u->channels; c++) {
|
||||
float *s = ((float*) u->buffer[c]) + frame_idx;
|
||||
float *d = ((float*) ((uint8_t*) chunk.memblock->data + chunk.index)) + (frame_idx * u->channels) + c;
|
||||
float *d = ((float*) ((uint8_t*) p + chunk.index)) + (frame_idx * u->channels) + c;
|
||||
|
||||
*d = *s;
|
||||
}
|
||||
}
|
||||
|
||||
pa_memblock_release(chunk.memblock);
|
||||
|
||||
pa_source_post(u->source, &chunk);
|
||||
pa_memblock_unref(chunk.memblock);
|
||||
|
||||
|
|
|
|||
|
|
@ -28,7 +28,6 @@
|
|||
#include <stdlib.h>
|
||||
#include <sys/stat.h>
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
|
|
@ -38,6 +37,7 @@
|
|||
#include <pulse/timeval.h>
|
||||
#include <pulse/xmalloc.h>
|
||||
|
||||
#include <pulsecore/macro.h>
|
||||
#include <pulsecore/iochannel.h>
|
||||
#include <pulsecore/sink.h>
|
||||
#include <pulsecore/module.h>
|
||||
|
|
@ -64,11 +64,9 @@ struct userdata {
|
|||
pa_core *core;
|
||||
pa_module *module;
|
||||
pa_sink *sink;
|
||||
pa_time_event *time_event;
|
||||
pa_thread *thread;
|
||||
size_t block_size;
|
||||
|
||||
uint64_t n_bytes;
|
||||
struct timeval start_time;
|
||||
struct timeval timestamp;
|
||||
};
|
||||
|
||||
static const char* const valid_modargs[] = {
|
||||
|
|
@ -81,35 +79,131 @@ static const char* const valid_modargs[] = {
|
|||
NULL
|
||||
};
|
||||
|
||||
static void time_callback(pa_mainloop_api *m, pa_time_event*e, const struct timeval *tv, void *userdata) {
|
||||
static void thread_func(void *userdata) {
|
||||
struct userdata *u = userdata;
|
||||
pa_memchunk chunk;
|
||||
struct timeval ntv = *tv;
|
||||
size_t l;
|
||||
int quit = 0;
|
||||
struct pollfd pollfd;
|
||||
int running = 1;
|
||||
|
||||
assert(u);
|
||||
pa_assert(u);
|
||||
|
||||
if (pa_sink_render(u->sink, u->block_size, &chunk) >= 0) {
|
||||
l = chunk.length;
|
||||
pa_memblock_unref(chunk.memblock);
|
||||
} else
|
||||
l = u->block_size;
|
||||
pa_log_debug("Thread starting up");
|
||||
|
||||
pa_timeval_add(&ntv, pa_bytes_to_usec(l, &u->sink->sample_spec));
|
||||
m->time_restart(e, &ntv);
|
||||
memset(&pollfd, 0, sizeof(pollfd));
|
||||
pollfd.fd = pa_asyncmsgq_get_fd(u->sink->asyncmsgq, PA_ASYNCQ_POP);
|
||||
pollfd.events = POLLIN;
|
||||
|
||||
u->n_bytes += l;
|
||||
}
|
||||
pa_gettimeofday(u->timestamp);
|
||||
|
||||
for (;;) {
|
||||
int code;
|
||||
void *data, *object;
|
||||
int r, timeout;
|
||||
struct timeval now;
|
||||
|
||||
static pa_usec_t get_latency(pa_sink *s) {
|
||||
struct userdata *u = s->userdata;
|
||||
pa_usec_t a, b;
|
||||
struct timeval now;
|
||||
/* Check whether there is a message for us to process */
|
||||
if (pa_asyncmsgq_get(u->sink->asyncmsgq, &object, &code, &data) == 0) {
|
||||
|
||||
a = pa_timeval_diff(pa_gettimeofday(&now), &u->start_time);
|
||||
b = pa_bytes_to_usec(u->n_bytes, &s->sample_spec);
|
||||
|
||||
return b > a ? b - a : 0;
|
||||
/* Now process these messages our own way */
|
||||
if (!object) {
|
||||
|
||||
switch (code) {
|
||||
case PA_MESSAGE_SHUTDOWN:
|
||||
goto finish;
|
||||
|
||||
default:
|
||||
pa_sink_process_msg(u->sink->asyncmsgq, object, code, data);
|
||||
|
||||
}
|
||||
|
||||
} else if (object == u->sink) {
|
||||
|
||||
switch (code) {
|
||||
case PA_SINK_MESSAGE_STOP:
|
||||
pa_assert(running);
|
||||
running = 0;
|
||||
break;
|
||||
|
||||
case PA_SINK_MESSAGE_START:
|
||||
pa_assert(!running);
|
||||
running = 1;
|
||||
|
||||
pa_gettimeofday(u->timestamp);
|
||||
break;
|
||||
|
||||
case PA_SINK_MESSAGE_GET_LATENCY:
|
||||
|
||||
if (pa_timeval_cmp(&u->timestamp, &now) > 0)
|
||||
*((pa_usec_t*) data) = 0;
|
||||
else
|
||||
*((pa_usec_t*) data) = pa_timeval_diff(&u->timestamp, &now);
|
||||
break;
|
||||
|
||||
/* ... */
|
||||
|
||||
default:
|
||||
pa_sink_process_msg(u->sink->asyncmsgq, object, code, data);
|
||||
}
|
||||
}
|
||||
|
||||
pa_asyncmsgq_done(u->sink->asyncmsgq);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Render some data and drop it immediately */
|
||||
|
||||
if (running) {
|
||||
pa_gettimeofday(&now);
|
||||
|
||||
if (pa_timeval_cmp(u->timestamp, &now) <= 0) {
|
||||
pa_memchunk chunk;
|
||||
size_t l;
|
||||
|
||||
if (pa_sink_render(u->sink, u->block_size, &chunk) >= 0) {
|
||||
l = chunk.length;
|
||||
pa_memblock_unref(chunk.memblock);
|
||||
} else
|
||||
l = u->block_size;
|
||||
|
||||
pa_timeval_add(&u->timestamp, pa_bytes_to_usec(l, &u->sink->sample_spec));
|
||||
continue;
|
||||
}
|
||||
|
||||
timeout = pa_timeval_diff(&u->timestamp, &now)/1000;
|
||||
|
||||
if (timeout < 1)
|
||||
timeout = 1;
|
||||
} else
|
||||
timeout = -1;
|
||||
|
||||
/* Hmm, nothing to do. Let's sleep */
|
||||
|
||||
if (pa_asyncmsgq_before_poll(u->sink->asyncmsgq) < 0)
|
||||
continue;
|
||||
|
||||
r = poll(&pollfd, 1, timeout);
|
||||
pa_asyncmsgq_after_poll(u->sink->asyncmsgq);
|
||||
|
||||
if (r < 0) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
|
||||
pa_log("poll() failed: %s", pa_cstrerror(errno));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
pa_assert(r == 0 || pollfd.revents == POLLIN);
|
||||
}
|
||||
|
||||
fail:
|
||||
/* We have to continue processing messages until we receive the
|
||||
* SHUTDOWN message */
|
||||
pa_asyncmsgq_post(u->core->asyncmsgq, u->core, PA_CORE_MESSAGE_UNLOAD_MODULE, pa_module_ref(u->module), NULL, pa_module_unref);
|
||||
pa_asyncmsgq_wait_for(PA_MESSAGE_SHUTDOWN);
|
||||
|
||||
finish:
|
||||
pa_log_debug("Thread shutting down");
|
||||
}
|
||||
|
||||
int pa__init(pa_core *c, pa_module*m) {
|
||||
|
|
@ -118,17 +212,17 @@ int pa__init(pa_core *c, pa_module*m) {
|
|||
pa_channel_map map;
|
||||
pa_modargs *ma = NULL;
|
||||
|
||||
assert(c);
|
||||
assert(m);
|
||||
pa_assert(c);
|
||||
pa_assert(m);
|
||||
|
||||
if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
|
||||
pa_log("failed to parse module arguments.");
|
||||
pa_log("Failed to parse module arguments.");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ss = c->default_sample_spec;
|
||||
if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0) {
|
||||
pa_log("invalid sample format specification or channel map.");
|
||||
pa_log("Invalid sample format specification or channel map");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
|
@ -138,22 +232,24 @@ int pa__init(pa_core *c, pa_module*m) {
|
|||
m->userdata = u;
|
||||
|
||||
if (!(u->sink = pa_sink_new(c, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss, &map))) {
|
||||
pa_log("failed to create sink.");
|
||||
pa_log("Failed to create sink.");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
u->sink->get_latency = get_latency;
|
||||
u->sink->userdata = u;
|
||||
pa_sink_set_owner(u->sink, m);
|
||||
pa_sink_set_description(u->sink, pa_modargs_get_value(ma, "description", "NULL sink"));
|
||||
|
||||
u->n_bytes = 0;
|
||||
pa_gettimeofday(&u->start_time);
|
||||
|
||||
u->time_event = c->mainloop->time_new(c->mainloop, &u->start_time, time_callback, u);
|
||||
|
||||
u->block_size = pa_bytes_per_second(&ss) / 10;
|
||||
u->block_size = pa_bytes_per_second(&ss) / 20; /* 50 ms */
|
||||
|
||||
if (u->block_size <= 0)
|
||||
u->block_size = pa_frame_size(&ss);
|
||||
|
||||
if (!(u->thread = pa_thread_new(thread_func, u))) {
|
||||
pa_log("Failed to create thread.");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
pa_modargs_free(ma);
|
||||
|
||||
return 0;
|
||||
|
|
@ -169,15 +265,21 @@ fail:
|
|||
|
||||
void pa__done(pa_core *c, pa_module*m) {
|
||||
struct userdata *u;
|
||||
assert(c && m);
|
||||
|
||||
pa_assert(c);
|
||||
pa_assert(m);
|
||||
|
||||
if (!(u = m->userdata))
|
||||
return;
|
||||
|
||||
pa_sink_disconnect(u->sink);
|
||||
pa_sink_unref(u->sink);
|
||||
|
||||
u->core->mainloop->time_free(u->time_event);
|
||||
if (u->thread) {
|
||||
pa_asyncmsgq_send(u->sink->asyncmsgq, PA_SINK_MESSAGE_SHUTDOWN, NULL);
|
||||
pa_thread_free(u->thread);
|
||||
}
|
||||
|
||||
pa_sink_unref(u->sink);
|
||||
|
||||
pa_xfree(u);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -173,7 +173,7 @@ static void out_fill_memblocks(struct userdata *u, unsigned n) {
|
|||
u->out_fragment_size,
|
||||
1);
|
||||
assert(chunk.memblock);
|
||||
chunk.length = chunk.memblock->length;
|
||||
chunk.length = pa_memblock_get_length(chunk.memblock);
|
||||
chunk.index = 0;
|
||||
|
||||
pa_sink_render_into_full(u->sink, &chunk);
|
||||
|
|
@ -217,7 +217,7 @@ static void in_post_memblocks(struct userdata *u, unsigned n) {
|
|||
|
||||
if (!u->in_memblocks[u->in_current]) {
|
||||
chunk.memblock = u->in_memblocks[u->in_current] = pa_memblock_new_fixed(u->core->mempool, (uint8_t*) u->in_mmap+u->in_fragment_size*u->in_current, u->in_fragment_size, 1);
|
||||
chunk.length = chunk.memblock->length;
|
||||
chunk.length = pa_memblock_get_length(chunk.memblock);
|
||||
chunk.index = 0;
|
||||
|
||||
pa_source_post(u->source, &chunk);
|
||||
|
|
|
|||
|
|
@ -158,6 +158,7 @@ static void do_write(struct userdata *u) {
|
|||
}
|
||||
|
||||
do {
|
||||
void *p;
|
||||
memchunk = &u->memchunk;
|
||||
|
||||
if (!memchunk->length)
|
||||
|
|
@ -165,10 +166,11 @@ static void do_write(struct userdata *u) {
|
|||
memchunk = &u->silence;
|
||||
|
||||
assert(memchunk->memblock);
|
||||
assert(memchunk->memblock->data);
|
||||
assert(memchunk->length);
|
||||
|
||||
if ((r = pa_iochannel_write(u->io, (uint8_t*) memchunk->memblock->data + memchunk->index, memchunk->length)) < 0) {
|
||||
p = pa_memblock_acquire(memchunk->memblock);
|
||||
if ((r = pa_iochannel_write(u->io, (uint8_t*) p + memchunk->index, memchunk->length)) < 0) {
|
||||
pa_memblock_release(memchunk->memblock);
|
||||
|
||||
if (errno != EAGAIN) {
|
||||
pa_log("write() failed: %s", pa_cstrerror(errno));
|
||||
|
|
@ -180,6 +182,8 @@ static void do_write(struct userdata *u) {
|
|||
break;
|
||||
}
|
||||
|
||||
pa_memblock_release(memchunk->memblock);
|
||||
|
||||
if (memchunk == &u->silence)
|
||||
assert(r % u->sample_size == 0);
|
||||
else {
|
||||
|
|
@ -224,9 +228,13 @@ static void do_read(struct userdata *u) {
|
|||
}
|
||||
|
||||
do {
|
||||
void *p;
|
||||
memchunk.memblock = pa_memblock_new(u->core->mempool, l);
|
||||
assert(memchunk.memblock);
|
||||
if ((r = pa_iochannel_read(u->io, memchunk.memblock->data, memchunk.memblock->length)) < 0) {
|
||||
|
||||
p = pa_memblock_acquire(memchunk.memblock);
|
||||
|
||||
if ((r = pa_iochannel_read(u->io, p, pa_memblock_get_length(memchunk.memblock))) < 0) {
|
||||
pa_memblock_release(memchunk.memblock);
|
||||
pa_memblock_unref(memchunk.memblock);
|
||||
|
||||
if (errno != EAGAIN) {
|
||||
|
|
@ -239,8 +247,10 @@ static void do_read(struct userdata *u) {
|
|||
break;
|
||||
}
|
||||
|
||||
assert(r <= (ssize_t) memchunk.memblock->length);
|
||||
memchunk.length = memchunk.memblock->length = r;
|
||||
pa_memblock_release(memchunk.memblock);
|
||||
|
||||
assert(r <= (ssize_t) pa_memblock_get_length(memchunk.memblock));
|
||||
memchunk.length = r;
|
||||
memchunk.index = 0;
|
||||
|
||||
pa_source_post(u->source, &memchunk);
|
||||
|
|
|
|||
|
|
@ -58,20 +58,16 @@ PA_MODULE_USAGE(
|
|||
"rate=<sample rate>"
|
||||
"channel_map=<channel map>")
|
||||
|
||||
#define DEFAULT_FIFO_NAME "/tmp/music.output"
|
||||
#define DEFAULT_FILE_NAME "/tmp/music.output"
|
||||
#define DEFAULT_SINK_NAME "fifo_output"
|
||||
|
||||
struct userdata {
|
||||
pa_core *core;
|
||||
|
||||
char *filename;
|
||||
|
||||
pa_sink *sink;
|
||||
pa_iochannel *io;
|
||||
pa_defer_event *defer_event;
|
||||
|
||||
pa_memchunk memchunk;
|
||||
pa_module *module;
|
||||
pa_sink *sink;
|
||||
char *filename;
|
||||
int fd;
|
||||
pa_thread *thread;
|
||||
};
|
||||
|
||||
static const char* const valid_modargs[] = {
|
||||
|
|
@ -84,97 +80,203 @@ static const char* const valid_modargs[] = {
|
|||
NULL
|
||||
};
|
||||
|
||||
static void do_write(struct userdata *u) {
|
||||
ssize_t r;
|
||||
assert(u);
|
||||
enum {
|
||||
POLLFD_ASYNCQ,
|
||||
POLLFD_FIFO,
|
||||
POLLFD_MAX,
|
||||
};
|
||||
|
||||
u->core->mainloop->defer_enable(u->defer_event, 0);
|
||||
|
||||
if (!pa_iochannel_is_writable(u->io))
|
||||
return;
|
||||
|
||||
pa_module_set_used(u->module, pa_sink_used_by(u->sink));
|
||||
|
||||
if (!u->memchunk.length)
|
||||
if (pa_sink_render(u->sink, PIPE_BUF, &u->memchunk) < 0)
|
||||
return;
|
||||
|
||||
assert(u->memchunk.memblock && u->memchunk.length);
|
||||
|
||||
if ((r = pa_iochannel_write(u->io, (uint8_t*) u->memchunk.memblock->data + u->memchunk.index, u->memchunk.length)) < 0) {
|
||||
pa_log("write(): %s", pa_cstrerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
u->memchunk.index += r;
|
||||
u->memchunk.length -= r;
|
||||
|
||||
if (u->memchunk.length <= 0) {
|
||||
pa_memblock_unref(u->memchunk.memblock);
|
||||
u->memchunk.memblock = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void notify_cb(pa_sink*s) {
|
||||
struct userdata *u = s->userdata;
|
||||
assert(s && u);
|
||||
|
||||
if (pa_iochannel_is_writable(u->io))
|
||||
u->core->mainloop->defer_enable(u->defer_event, 1);
|
||||
}
|
||||
|
||||
static pa_usec_t get_latency_cb(pa_sink *s) {
|
||||
struct userdata *u = s->userdata;
|
||||
assert(s && u);
|
||||
|
||||
return u->memchunk.memblock ? pa_bytes_to_usec(u->memchunk.length, &s->sample_spec) : 0;
|
||||
}
|
||||
|
||||
static void defer_callback(PA_GCC_UNUSED pa_mainloop_api *m, PA_GCC_UNUSED pa_defer_event*e, void *userdata) {
|
||||
static void thread_func(void *userdata) {
|
||||
struct userdata *u = userdata;
|
||||
assert(u);
|
||||
do_write(u);
|
||||
}
|
||||
int quit = 0;
|
||||
struct pollfd pollfd[POLLFD_MAX];
|
||||
int running = 1, underrun = 0;
|
||||
pa_memchunk memchunk;
|
||||
|
||||
static void io_callback(PA_GCC_UNUSED pa_iochannel *io, void*userdata) {
|
||||
struct userdata *u = userdata;
|
||||
assert(u);
|
||||
do_write(u);
|
||||
pa_assert(u);
|
||||
|
||||
pa_log_debug("Thread starting up");
|
||||
|
||||
memset(&pollfd, 0, sizeof(pollfd));
|
||||
pollfd[POLLFD_ASYNCQ].fd = pa_asyncmsgq_get_fd(u->sink->asyncmsgq, PA_ASYNCQ_POP);
|
||||
pollfd[POLLFD_ASYNCQ].events = POLLIN;
|
||||
|
||||
pollfd[POLLFD_FIFO].fd = u->fd;
|
||||
|
||||
memset(&memchunk, 0, sizeof(memchunk));
|
||||
|
||||
for (;;) {
|
||||
int code;
|
||||
void *object, *data;
|
||||
int r;
|
||||
struct timeval now;
|
||||
|
||||
/* Check whether there is a message for us to process */
|
||||
if (pa_asyncmsgq_get(u->sink->asyncmsgq, &object, &code, &data) == 0) {
|
||||
|
||||
|
||||
/* Now process these messages our own way */
|
||||
if (!object) {
|
||||
switch (code) {
|
||||
case PA_SINK_MESSAGE_SHUTDOWN:
|
||||
goto finish;
|
||||
|
||||
default:
|
||||
pa_sink_process_msg(u->sink->asyncmsgq, object, code, data);
|
||||
}
|
||||
|
||||
} else if (object == u->sink) {
|
||||
|
||||
case PA_SINK_MESSAGE_STOP:
|
||||
pa_assert(running);
|
||||
running = 0;
|
||||
break;
|
||||
|
||||
case PA_SINK_MESSAGE_START:
|
||||
pa_assert(!running);
|
||||
running = 1;
|
||||
break;
|
||||
|
||||
case PA_SINK_MESSAGE_GET_LATENCY: {
|
||||
size_t n = 0;
|
||||
int l;
|
||||
|
||||
if (ioctl(u->fd, TIOCINQ, &l) >= 0 && l > 0)
|
||||
n = (size_t) l;
|
||||
|
||||
n += memchunk.length;
|
||||
|
||||
*((pa_usec_t*) data) pa_bytes_to_usec(n, &u->sink->sample_spec);
|
||||
break;
|
||||
}
|
||||
|
||||
/* ... */
|
||||
|
||||
default:
|
||||
pa_sink_process_msg(u->sink->asyncmsgq, object, code, data);
|
||||
}
|
||||
|
||||
pa_asyncmsgq_done(u->sink->asyncmsgq);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Render some data and write it to the fifo */
|
||||
|
||||
if (running && (pollfd[POLLFD_FIFO].revents || underrun)) {
|
||||
|
||||
if (chunk.length <= 0)
|
||||
pa_sink_render(u->fd, PIPE_BUF, &chunk);
|
||||
|
||||
underrun = chunk.length <= 0;
|
||||
|
||||
if (!underrun) {
|
||||
ssize_t l;
|
||||
|
||||
p = pa_memblock_acquire(u->memchunk.memblock);
|
||||
l = pa_write(u->fd, (uint8_t*) p + u->memchunk.index, u->memchunk.length);
|
||||
pa_memblock_release(p);
|
||||
|
||||
if (l < 0) {
|
||||
|
||||
if (errno != EINTR && errno != EAGAIN) {
|
||||
pa_log("Failed to write data to FIFO: %s", pa_cstrerror(errno));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
u->memchunk.index += l;
|
||||
u->memchunk.length -= l;
|
||||
|
||||
if (u->memchunk.length <= 0) {
|
||||
pa_memblock_unref(u->memchunk.memblock);
|
||||
u->memchunk.memblock = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
pollfd[POLLFD_FIFO].revents = 0;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
pollfd[POLLFD_FIFO].events = running && !underrun ? POLLOUT : 0;
|
||||
|
||||
/* Hmm, nothing to do. Let's sleep */
|
||||
|
||||
if (pa_asyncmsgq_before_poll(u->sink->asyncmsgq) < 0)
|
||||
continue;
|
||||
|
||||
r = poll(&pollfd, 1, 0);
|
||||
pa_asyncmsgq_after_poll(u->sink->asyncmsgq);
|
||||
|
||||
if (r < 0) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
|
||||
pa_log("poll() failed: %s", pa_cstrerror(errno));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (pollfd[POLLFD_FIFO].revents & ~POLLIN) {
|
||||
pa_log("FIFO shutdown.");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
pa_assert(pollfd[POLLFD_ASYNCQ].revents & ~POLLIN == 0);
|
||||
}
|
||||
|
||||
fail:
|
||||
/* We have to continue processing messages until we receive the
|
||||
* SHUTDOWN message */
|
||||
pa_asyncmsgq_post(u->core->asyncmsgq, u->core, PA_CORE_MESSAGE_UNLOAD_MODULE, pa_module_ref(u->module), pa_module_unref);
|
||||
pa_asyncmsgq_wait_for(PA_SINK_MESSAGE_SHUTDOWN);
|
||||
|
||||
finish:
|
||||
pa_log_debug("Thread shutting down");
|
||||
}
|
||||
|
||||
int pa__init(pa_core *c, pa_module*m) {
|
||||
struct userdata *u = NULL;
|
||||
struct stat st;
|
||||
const char *p;
|
||||
int fd = -1;
|
||||
pa_sample_spec ss;
|
||||
pa_channel_map map;
|
||||
pa_modargs *ma = NULL;
|
||||
char *t;
|
||||
|
||||
assert(c && m);
|
||||
pa_assert(c);
|
||||
pa_assert(m);
|
||||
|
||||
if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
|
||||
pa_log("failed to parse module arguments");
|
||||
pa_log("Failed to parse module arguments.");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ss = c->default_sample_spec;
|
||||
if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0) {
|
||||
pa_log("invalid sample format specification");
|
||||
pa_log("Invalid sample format specification");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
mkfifo(p = pa_modargs_get_value(ma, "file", DEFAULT_FIFO_NAME), 0777);
|
||||
|
||||
if ((fd = open(p, O_RDWR)) < 0) {
|
||||
u = pa_xnew0(struct userdata, 1);
|
||||
u->core = c;
|
||||
u->module = m;
|
||||
u->filename = pa_xstrdup(pa_modargs_get_value(ma, "file", DEFAULT_FIFO_NAME));
|
||||
u->fd = fd;
|
||||
u->memchunk.memblock = NULL;
|
||||
u->memchunk.length = 0;
|
||||
m->userdata = u;
|
||||
|
||||
mkfifo(u->filename, 0666);
|
||||
|
||||
if ((u->fd = open(u->filename, O_RDWR)) < 0) {
|
||||
pa_log("open('%s'): %s", p, pa_cstrerror(errno));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
pa_fd_set_cloexec(fd, 1);
|
||||
pa_fd_set_cloexec(u->fd, 1);
|
||||
pa_make_nonblock_fd(u->fd);
|
||||
|
||||
if (fstat(fd, &st) < 0) {
|
||||
if (fstat(u->fd, &st) < 0) {
|
||||
pa_log("fstat('%s'): %s", p, pa_cstrerror(errno));
|
||||
goto fail;
|
||||
}
|
||||
|
|
@ -184,34 +286,21 @@ int pa__init(pa_core *c, pa_module*m) {
|
|||
goto fail;
|
||||
}
|
||||
|
||||
u = pa_xmalloc0(sizeof(struct userdata));
|
||||
u->filename = pa_xstrdup(p);
|
||||
u->core = c;
|
||||
u->module = m;
|
||||
m->userdata = u;
|
||||
|
||||
if (!(u->sink = pa_sink_new(c, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss, &map))) {
|
||||
pa_log("failed to create sink.");
|
||||
pa_log("Failed to create sink.");
|
||||
goto fail;
|
||||
}
|
||||
u->sink->notify = notify_cb;
|
||||
u->sink->get_latency = get_latency_cb;
|
||||
|
||||
u->sink->userdata = u;
|
||||
pa_sink_set_owner(u->sink, m);
|
||||
pa_sink_set_description(u->sink, t = pa_sprintf_malloc("Unix FIFO sink '%s'", p));
|
||||
pa_xfree(t);
|
||||
|
||||
u->io = pa_iochannel_new(c->mainloop, -1, fd);
|
||||
assert(u->io);
|
||||
pa_iochannel_set_callback(u->io, io_callback, u);
|
||||
|
||||
u->memchunk.memblock = NULL;
|
||||
u->memchunk.length = 0;
|
||||
|
||||
u->defer_event = c->mainloop->defer_new(c->mainloop, defer_callback, u);
|
||||
assert(u->defer_event);
|
||||
c->mainloop->defer_enable(u->defer_event, 0);
|
||||
|
||||
if (!(u->thread = pa_thread_new(thread_func, u))) {
|
||||
pa_log("Failed to create thread.");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
pa_modargs_free(ma);
|
||||
|
||||
return 0;
|
||||
|
|
@ -220,9 +309,6 @@ fail:
|
|||
if (ma)
|
||||
pa_modargs_free(ma);
|
||||
|
||||
if (fd >= 0)
|
||||
close(fd);
|
||||
|
||||
pa__done(c, m);
|
||||
|
||||
return -1;
|
||||
|
|
@ -230,22 +316,31 @@ fail:
|
|||
|
||||
void pa__done(pa_core *c, pa_module*m) {
|
||||
struct userdata *u;
|
||||
assert(c && m);
|
||||
pa_assert(c);
|
||||
pa_assert(m);
|
||||
|
||||
if (!(u = m->userdata))
|
||||
return;
|
||||
|
||||
if (u->memchunk.memblock)
|
||||
pa_memblock_unref(u->memchunk.memblock);
|
||||
|
||||
pa_sink_disconnect(u->sink);
|
||||
pa_sink_unref(u->sink);
|
||||
pa_iochannel_free(u->io);
|
||||
u->core->mainloop->defer_free(u->defer_event);
|
||||
|
||||
assert(u->filename);
|
||||
unlink(u->filename);
|
||||
pa_xfree(u->filename);
|
||||
if (u->thread) {
|
||||
pa_asyncmsgq_send(u->sink->asyncmsgq, PA_SINK_MESSAGE_SHUTDOWN, NULL);
|
||||
pa_thread_free(u->thread);
|
||||
}
|
||||
|
||||
pa_sink_unref(u->sink);
|
||||
|
||||
if (u->memchunk.memblock)
|
||||
pa_memblock_unref(u->memchunk.memblock);
|
||||
|
||||
if (u->filename) {
|
||||
unlink(u->filename);
|
||||
pa_xfree(u->filename);
|
||||
}
|
||||
|
||||
if (u->fd >= 0)
|
||||
close(u->fd);
|
||||
|
||||
pa_xfree(u);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -84,7 +84,9 @@ static const char* const valid_modargs[] = {
|
|||
|
||||
static void do_read(struct userdata *u) {
|
||||
ssize_t r;
|
||||
void *p;
|
||||
pa_memchunk chunk;
|
||||
|
||||
assert(u);
|
||||
|
||||
if (!pa_iochannel_is_readable(u->io))
|
||||
|
|
@ -97,17 +99,22 @@ static void do_read(struct userdata *u) {
|
|||
u->chunk.index = chunk.length = 0;
|
||||
}
|
||||
|
||||
assert(u->chunk.memblock && u->chunk.memblock->length > u->chunk.index);
|
||||
if ((r = pa_iochannel_read(u->io, (uint8_t*) u->chunk.memblock->data + u->chunk.index, u->chunk.memblock->length - u->chunk.index)) <= 0) {
|
||||
assert(u->chunk.memblock);
|
||||
assert(pa_memblock_get_length(u->chunk.memblock) > u->chunk.index);
|
||||
|
||||
p = pa_memblock_acquire(u->chunk.memblock);
|
||||
if ((r = pa_iochannel_read(u->io, (uint8_t*) p + u->chunk.index, pa_memblock_get_length(u->chunk.memblock) - u->chunk.index)) <= 0) {
|
||||
pa_memblock_release(u->chunk.memblock);
|
||||
pa_log("read(): %s", pa_cstrerror(errno));
|
||||
return;
|
||||
}
|
||||
pa_memblock_release(u->chunk.memblock);
|
||||
|
||||
u->chunk.length = r;
|
||||
pa_source_post(u->source, &u->chunk);
|
||||
u->chunk.index += r;
|
||||
|
||||
if (u->chunk.index >= u->chunk.memblock->length) {
|
||||
if (u->chunk.index >= pa_memblock_get_length(u->chunk.memblock)) {
|
||||
u->chunk.index = u->chunk.length = 0;
|
||||
pa_memblock_unref(u->chunk.memblock);
|
||||
u->chunk.memblock = NULL;
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ static int sink_input_peek(pa_sink_input *i, pa_memchunk *chunk) {
|
|||
|
||||
chunk->memblock = pa_memblock_ref(u->memblock);
|
||||
chunk->index = u->peek_index;
|
||||
chunk->length = u->memblock->length - u->peek_index;
|
||||
chunk->length = pa_memblock_get_length(u->memblock) - u->peek_index;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -74,11 +74,12 @@ static void sink_input_drop(pa_sink_input *i, const pa_memchunk *chunk, size_t l
|
|||
assert(i && chunk && length && i->userdata);
|
||||
u = i->userdata;
|
||||
|
||||
assert(chunk->memblock == u->memblock && length <= u->memblock->length-u->peek_index);
|
||||
assert(chunk->memblock == u->memblock);
|
||||
assert(length <= pa_memblock_get_length(u->memblock)-u->peek_index);
|
||||
|
||||
u->peek_index += length;
|
||||
|
||||
if (u->peek_index >= u->memblock->length)
|
||||
if (u->peek_index >= pa_memblock_get_length(u->memblock))
|
||||
u->peek_index = 0;
|
||||
}
|
||||
|
||||
|
|
@ -111,6 +112,7 @@ int pa__init(pa_core *c, pa_module*m) {
|
|||
pa_sample_spec ss;
|
||||
uint32_t frequency;
|
||||
char t[256];
|
||||
void *p;
|
||||
pa_sink_input_new_data data;
|
||||
|
||||
if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
|
||||
|
|
@ -142,7 +144,9 @@ int pa__init(pa_core *c, pa_module*m) {
|
|||
}
|
||||
|
||||
u->memblock = pa_memblock_new(c->mempool, pa_bytes_per_second(&ss));
|
||||
calc_sine(u->memblock->data, u->memblock->length, frequency);
|
||||
p = pa_memblock_acquire(u->memblock);
|
||||
calc_sine(p, pa_memblock_get_length(u->memblock), frequency);
|
||||
pa_memblock_release(u->memblock);
|
||||
|
||||
snprintf(t, sizeof(t), "Sine Generator at %u Hz", frequency);
|
||||
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ int pa_rtp_send(pa_rtp_context *c, size_t size, pa_memblockq *q) {
|
|||
size_t k = n + chunk.length > size ? size - n : chunk.length;
|
||||
|
||||
if (chunk.memblock) {
|
||||
iov[iov_idx].iov_base = (void*)((uint8_t*) chunk.memblock->data + chunk.index);
|
||||
iov[iov_idx].iov_base = (void*)((uint8_t*) pa_memblock_acquire(chunk.memblock) + chunk.index);
|
||||
iov[iov_idx].iov_len = k;
|
||||
mb[iov_idx] = chunk.memblock;
|
||||
iov_idx ++;
|
||||
|
|
@ -116,8 +116,10 @@ int pa_rtp_send(pa_rtp_context *c, size_t size, pa_memblockq *q) {
|
|||
|
||||
k = sendmsg(c->fd, &m, MSG_DONTWAIT);
|
||||
|
||||
for (i = 1; i < iov_idx; i++)
|
||||
for (i = 1; i < iov_idx; i++) {
|
||||
pa_memblock_release(mb[i]);
|
||||
pa_memblock_unref(mb[i]);
|
||||
}
|
||||
|
||||
c->sequence++;
|
||||
} else
|
||||
|
|
@ -174,7 +176,7 @@ int pa_rtp_recv(pa_rtp_context *c, pa_memchunk *chunk, pa_mempool *pool) {
|
|||
|
||||
chunk->memblock = pa_memblock_new(pool, size);
|
||||
|
||||
iov.iov_base = chunk->memblock->data;
|
||||
iov.iov_base = pa_memblock_acquire(chunk->memblock);
|
||||
iov.iov_len = size;
|
||||
|
||||
m.msg_name = NULL;
|
||||
|
|
@ -195,9 +197,9 @@ int pa_rtp_recv(pa_rtp_context *c, pa_memchunk *chunk, pa_mempool *pool) {
|
|||
goto fail;
|
||||
}
|
||||
|
||||
memcpy(&header, chunk->memblock->data, sizeof(uint32_t));
|
||||
memcpy(&c->timestamp, (uint8_t*) chunk->memblock->data + 4, sizeof(uint32_t));
|
||||
memcpy(&c->ssrc, (uint8_t*) chunk->memblock->data + 8, sizeof(uint32_t));
|
||||
memcpy(&header, iov.iov_base, sizeof(uint32_t));
|
||||
memcpy(&c->timestamp, (uint8_t*) iov.iov_base + 4, sizeof(uint32_t));
|
||||
memcpy(&c->ssrc, (uint8_t*) iov.iov_base + 8, sizeof(uint32_t));
|
||||
|
||||
header = ntohl(header);
|
||||
c->timestamp = ntohl(c->timestamp);
|
||||
|
|
@ -238,8 +240,10 @@ int pa_rtp_recv(pa_rtp_context *c, pa_memchunk *chunk, pa_mempool *pool) {
|
|||
return 0;
|
||||
|
||||
fail:
|
||||
if (chunk->memblock)
|
||||
if (chunk->memblock) {
|
||||
pa_memblock_release(chunk->memblock);
|
||||
pa_memblock_unref(chunk->memblock);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -116,6 +116,7 @@ struct pa_stream {
|
|||
uint32_t requested_bytes;
|
||||
|
||||
pa_memchunk peek_memchunk;
|
||||
void *peek_data;
|
||||
pa_memblockq *record_memblockq;
|
||||
|
||||
int corked;
|
||||
|
|
|
|||
|
|
@ -91,6 +91,7 @@ pa_stream *pa_stream_new(pa_context *c, const char *name, const pa_sample_spec *
|
|||
s->peek_memchunk.index = 0;
|
||||
s->peek_memchunk.length = 0;
|
||||
s->peek_memchunk.memblock = NULL;
|
||||
s->peek_data = NULL;
|
||||
|
||||
s->record_memblockq = NULL;
|
||||
|
||||
|
|
@ -125,8 +126,11 @@ static void stream_free(pa_stream *s) {
|
|||
s->mainloop->time_free(s->auto_timing_update_event);
|
||||
}
|
||||
|
||||
if (s->peek_memchunk.memblock)
|
||||
if (s->peek_memchunk.memblock) {
|
||||
if (s->peek_data)
|
||||
pa_memblock_release(s->peek_memchunk.memblock);
|
||||
pa_memblock_unref(s->peek_memchunk.memblock);
|
||||
}
|
||||
|
||||
if (s->record_memblockq)
|
||||
pa_memblockq_free(s->record_memblockq);
|
||||
|
|
@ -608,8 +612,11 @@ int pa_stream_write(
|
|||
if (free_cb)
|
||||
chunk.memblock = pa_memblock_new_user(s->context->mempool, (void*) data, length, free_cb, 1);
|
||||
else {
|
||||
void *tdata;
|
||||
chunk.memblock = pa_memblock_new(s->context->mempool, length);
|
||||
memcpy(chunk.memblock->data, data, length);
|
||||
tdata = pa_memblock_acquire(chunk.memblock);
|
||||
memcpy(tdata, data, length);
|
||||
pa_memblock_release(chunk.memblock);
|
||||
}
|
||||
|
||||
chunk.index = 0;
|
||||
|
|
@ -675,9 +682,12 @@ int pa_stream_peek(pa_stream *s, const void **data, size_t *length) {
|
|||
*length = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
s->peek_data = pa_memblock_acquire(s->peek_memchunk.memblock);
|
||||
}
|
||||
|
||||
*data = (const char*) s->peek_memchunk.memblock->data + s->peek_memchunk.index;
|
||||
assert(s->peek_data);
|
||||
*data = (uint8_t*) s->peek_data + s->peek_memchunk.index;
|
||||
*length = s->peek_memchunk.length;
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -696,6 +706,8 @@ int pa_stream_drop(pa_stream *s) {
|
|||
if (s->timing_info_valid && !s->timing_info.read_index_corrupt)
|
||||
s->timing_info.read_index += s->peek_memchunk.length;
|
||||
|
||||
assert(s->peek_data);
|
||||
pa_memblock_release(s->peek_memchunk.memblock);
|
||||
pa_memblock_unref(s->peek_memchunk.memblock);
|
||||
s->peek_memchunk.length = 0;
|
||||
s->peek_memchunk.index = 0;
|
||||
|
|
|
|||
|
|
@ -75,6 +75,15 @@ static inline void* pa_xnew0_internal(unsigned n, size_t k) {
|
|||
/** Same as pa_xnew() but set the memory to zero */
|
||||
#define pa_xnew0(type, n) ((type*) pa_xnew0_internal((n), sizeof(type)))
|
||||
|
||||
/** Internal helper for pa_xnew0() */
|
||||
static inline void* pa_xnewdup_internal(const void *p, unsigned n, size_t k) {
|
||||
assert(n < INT_MAX/k);
|
||||
return pa_xmemdup(p, n*k);
|
||||
}
|
||||
|
||||
/** Same as pa_xnew() but set the memory to zero */
|
||||
#define pa_xnewdup(type, p, n) ((type*) pa_xnewdup_internal((p), (n), sizeof(type)))
|
||||
|
||||
PA_C_DECL_END
|
||||
|
||||
#endif
|
||||
|
|
|
|||
235
src/pulsecore/asyncmsgq.c
Normal file
235
src/pulsecore/asyncmsgq.c
Normal file
|
|
@ -0,0 +1,235 @@
|
|||
/* $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);
|
||||
|
||||
struct asyncmsgq_item {
|
||||
int code;
|
||||
pa_msgobject *object;
|
||||
void *userdata;
|
||||
pa_free_cb_t free_cb;
|
||||
pa_memchunk memchunk;
|
||||
pa_semaphore *semaphore;
|
||||
};
|
||||
|
||||
struct pa_asyncmsgq {
|
||||
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_assert_se(a->asyncq = pa_asyncq_new(size));
|
||||
pa_assert_se(a->mutex = pa_mutex_new(0));
|
||||
a->current = NULL;
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
void pa_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->object);
|
||||
|
||||
if (i->userdata_free_cb)
|
||||
i->userdata_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);
|
||||
}
|
||||
|
||||
void pa_asyncmsgq_post(pa_asyncmsgq *a, pa_msgobject *object, int code, const void *userdata, const pa_memchunk *chunk, pa_free_cb_t free_cb) {
|
||||
struct asyncmsgq_item *i;
|
||||
pa_assert(a);
|
||||
|
||||
if (!(i = pa_flist_pop(PA_STATIC_FLIST_GET(asyncmsgq))))
|
||||
i = pa_xnew(struct asyncmsgq_item, 1);
|
||||
|
||||
i->code = code;
|
||||
i->object = pa_msgobject_ref(object);
|
||||
i->userdata = (void*) userdata;
|
||||
i->free_cb = free_cb;
|
||||
if (chunk) {
|
||||
pa_assert(chunk->memblock);
|
||||
i->memchunk = *chunk;
|
||||
pa_memblock_ref(i->memchunk.memblock);
|
||||
} else
|
||||
pa_memchunk_reset(&i->memchunk);
|
||||
i->semaphore = NULL;
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
||||
int pa_asyncmsgq_send(pa_asyncmsgq *a, pa_msgobject *object, int code, const void *userdata, const pa_memchunk *chunk) {
|
||||
struct asyncmsgq_item i;
|
||||
pa_assert(a);
|
||||
|
||||
i.code = code;
|
||||
i.object = object;
|
||||
i.userdata = (void*) userdata;
|
||||
i.free_cb = NULL;
|
||||
i.ret = -1;
|
||||
if (chunk) {
|
||||
pa_assert(chunk->memblock);
|
||||
i->memchunk = *chunk;
|
||||
} else
|
||||
pa_memchunk_reset(&i->memchunk);
|
||||
pa_assert_se(i.semaphore = pa_semaphore_new(0));
|
||||
|
||||
/* 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);
|
||||
pa_semaphore_free(i.semaphore);
|
||||
|
||||
return i.ret;
|
||||
}
|
||||
|
||||
int pa_asyncmsgq_get(pa_asyncmsgq *a, pa_msgobject **object, int *code, void **userdata, pa_memchunk *chunk, int wait) {
|
||||
pa_assert(a);
|
||||
pa_assert(code);
|
||||
pa_assert(!a->current);
|
||||
|
||||
if (!(a->current = pa_asyncq_pop(a->asyncq, wait)))
|
||||
return -1;
|
||||
|
||||
*code = a->current->code;
|
||||
if (userdata)
|
||||
*userdata = a->current->userdata;
|
||||
if (object)
|
||||
*object = a->current->object;
|
||||
if (chunk)
|
||||
*chunk = a->chunk;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void pa_asyncmsgq_done(pa_asyncmsgq *a, int ret) {
|
||||
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(a);
|
||||
|
||||
do {
|
||||
|
||||
if (pa_asyncmsgq_get(a, NULL, &c, NULL, 1) < 0)
|
||||
return -1;
|
||||
|
||||
pa_asyncmsgq_done(a);
|
||||
|
||||
} while (c != code);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pa_asyncmsgq_get_fd(pa_asyncmsgq *a) {
|
||||
pa_assert(a);
|
||||
|
||||
return pa_asyncq_get_fd(a->asyncq);
|
||||
}
|
||||
|
||||
int pa_asyncmsgq_before_poll(pa_asyncmsgq *a) {
|
||||
pa_assert(a);
|
||||
|
||||
return pa_asyncq_before_poll(a->asyncq);
|
||||
}
|
||||
|
||||
void pa_asyncmsgq_after_poll(pa_asyncmsgq *a) {
|
||||
pa_assert(a);
|
||||
|
||||
pa_asyncq_after_poll(a->asyncq);
|
||||
}
|
||||
|
||||
int pa_asyncmsgq_dispatch(pa_msgobject *object, int code, void *userdata, pa_memchunk *memchunk) {
|
||||
pa_assert(q);
|
||||
|
||||
if (object)
|
||||
return object->msg_process(object, code, userdata, memchunk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
73
src/pulsecore/asyncmsgq.h
Normal file
73
src/pulsecore/asyncmsgq.h
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
#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 fromer just enqueues the message asynchronously, the
|
||||
* latter waits for completion, synchronously. */
|
||||
|
||||
enum {
|
||||
PA_MESSAGE_SHUTDOWN /* A generic message to inform the handler of this queue to quit */
|
||||
};
|
||||
|
||||
typedef struct pa_asyncmsgq pa_asyncmsgq;
|
||||
|
||||
pa_asyncmsgq* pa_asyncmsgq_new(size_t size);
|
||||
void pa_asyncmsgq_free(pa_asyncmsgq* q);
|
||||
|
||||
void pa_asyncmsgq_post(pa_asyncmsgq *q, pa_msgobject *object, int code, const void *userdata, 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, const pa_memchunk *memchunk);
|
||||
|
||||
int pa_asyncmsgq_get(pa_asyncmsgq *q, pa_msgobject **object, int *code, void **userdata, pa_memchunk *memchunk, int wait);
|
||||
int pa_asyncmsgq_dispatch(pa_msgobject *object, int code, void *userdata, pa_memchunk *memchunk);
|
||||
void pa_asyncmsgq_done(pa_asyncmsgq *q, int ret);
|
||||
int pa_asyncmsgq_wait_for(pa_asyncmsgq *a, int code);
|
||||
|
||||
/* 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
|
||||
271
src/pulsecore/asyncq.c
Normal file
271
src/pulsecore/asyncq.c
Normal file
|
|
@ -0,0 +1,271 @@
|
|||
/* $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"
|
||||
|
||||
#define ASYNCQ_SIZE 128
|
||||
|
||||
/* For debugging purposes we can define _Y to put and extra thread
|
||||
* yield between each operation. */
|
||||
|
||||
#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_atomic_int_t read_waiting;
|
||||
pa_atomic_int_t write_waiting;
|
||||
int read_fds[2], write_fds[2];
|
||||
};
|
||||
|
||||
#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;
|
||||
pa_atomic_store(&l->read_waiting, 0);
|
||||
pa_atomic_store(&l->write_waiting, 0);
|
||||
|
||||
if (pipe(l->read_fds) < 0) {
|
||||
pa_xfree(l);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (pipe(l->write_fds) < 0) {
|
||||
pa_close(l->read_fds[0]);
|
||||
pa_close(l->read_fds[1]);
|
||||
pa_xfree(l);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pa_make_nonblock_fd(l->read_fds[1]);
|
||||
pa_make_nonblock_fd(l->write_fds[1]);
|
||||
|
||||
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_close(l->read_fds[0]);
|
||||
pa_close(l->read_fds[1]);
|
||||
pa_close(l->write_fds[0]);
|
||||
pa_close(l->write_fds[1]);
|
||||
|
||||
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)) {
|
||||
|
||||
/* First try failed. Let's wait for changes. */
|
||||
|
||||
if (!wait)
|
||||
return -1;
|
||||
|
||||
_Y;
|
||||
|
||||
pa_atomic_inc(&l->write_waiting);
|
||||
|
||||
for (;;) {
|
||||
char x[20];
|
||||
|
||||
_Y;
|
||||
|
||||
if (pa_atomic_ptr_cmpxchg(&cells[idx], NULL, p))
|
||||
break;
|
||||
|
||||
_Y;
|
||||
|
||||
if (read(l->write_fds[0], x, sizeof(x)) < 0 && errno != EINTR) {
|
||||
pa_atomic_dec(&l->write_waiting);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
_Y;
|
||||
|
||||
pa_atomic_dec(&l->write_waiting);
|
||||
}
|
||||
|
||||
_Y;
|
||||
l->write_idx++;
|
||||
|
||||
if (pa_atomic_load(&l->read_waiting)) {
|
||||
char x = 'x';
|
||||
_Y;
|
||||
write(l->read_fds[1], &x, sizeof(x));
|
||||
}
|
||||
|
||||
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]))) {
|
||||
|
||||
/* First try failed. Let's wait for changes. */
|
||||
|
||||
if (!wait)
|
||||
return NULL;
|
||||
|
||||
_Y;
|
||||
|
||||
pa_atomic_inc(&l->read_waiting);
|
||||
|
||||
for (;;) {
|
||||
char x[20];
|
||||
|
||||
_Y;
|
||||
|
||||
if ((ret = pa_atomic_ptr_load(&cells[idx])))
|
||||
break;
|
||||
|
||||
_Y;
|
||||
|
||||
if (read(l->read_fds[0], x, sizeof(x)) < 0 && errno != EINTR) {
|
||||
pa_atomic_dec(&l->read_waiting);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
_Y;
|
||||
|
||||
pa_atomic_dec(&l->read_waiting);
|
||||
}
|
||||
|
||||
/* Guaranteed if we only have a single reader */
|
||||
pa_assert_se(pa_atomic_ptr_cmpxchg(&cells[idx], ret, NULL));
|
||||
|
||||
_Y;
|
||||
l->read_idx++;
|
||||
|
||||
if (pa_atomic_load(&l->write_waiting)) {
|
||||
char x = 'x';
|
||||
_Y;
|
||||
write(l->write_fds[1], &x, sizeof(x));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int pa_asyncq_get_fd(pa_asyncq *q) {
|
||||
pa_assert(q);
|
||||
|
||||
return q->read_fds[0];
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
if (pa_atomic_ptr_load(&cells[idx]))
|
||||
return -1;
|
||||
|
||||
pa_atomic_inc(&l->read_waiting);
|
||||
|
||||
if (pa_atomic_ptr_load(&cells[idx])) {
|
||||
pa_atomic_dec(&l->read_waiting);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pa_asyncq_after_poll(pa_asyncq *l) {
|
||||
pa_assert(l);
|
||||
|
||||
pa_assert(pa_atomic_load(&l->read_waiting) > 0);
|
||||
|
||||
pa_atomic_dec(&l->read_waiting);
|
||||
}
|
||||
56
src/pulsecore/asyncq.h
Normal file
56
src/pulsecore/asyncq.h
Normal file
|
|
@ -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(size_t 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);
|
||||
int pa_asyncq_after_poll(pa_asyncq *a);
|
||||
|
||||
#endif
|
||||
|
|
@ -31,9 +31,9 @@
|
|||
* It is not guaranteed however, that sizeof(AO_t) == sizeof(size_t).
|
||||
* however very likely. */
|
||||
|
||||
typedef struct pa_atomic_int {
|
||||
typedef struct pa_atomic {
|
||||
volatile AO_t value;
|
||||
} pa_atomic_int_t;
|
||||
} pa_atomic_t;
|
||||
|
||||
#define PA_ATOMIC_INIT(v) { .value = (v) }
|
||||
|
||||
|
|
@ -41,31 +41,31 @@ typedef struct pa_atomic_int {
|
|||
* 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_int_t *a) {
|
||||
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 +73,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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -95,6 +95,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);
|
||||
|
|
@ -130,12 +131,13 @@ 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},
|
||||
{ "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},
|
||||
{ "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-sink-input-mute", pa_cli_command_sink_input_mute, "Set the mute switch of a sink input (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-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},
|
||||
|
|
@ -392,7 +394,7 @@ static int pa_cli_command_sink_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *bu
|
|||
}
|
||||
|
||||
pa_cvolume_set(&cvolume, sink->sample_spec.channels, volume);
|
||||
pa_sink_set_volume(sink, PA_MIXER_HARDWARE, &cvolume);
|
||||
pa_sink_set_volume(sink, &cvolume);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -460,7 +462,7 @@ static int pa_cli_command_source_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *
|
|||
}
|
||||
|
||||
pa_cvolume_set(&cvolume, source->sample_spec.channels, volume);
|
||||
pa_source_set_volume(source, PA_MIXER_HARDWARE, &cvolume);
|
||||
pa_source_set_volume(source, &cvolume);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -489,7 +491,7 @@ 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;
|
||||
}
|
||||
|
||||
|
|
@ -518,7 +520,42 @@ 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_input_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
|
||||
const char *n, *v;
|
||||
pa_sink_input *si;
|
||||
uint32_t idx;
|
||||
int mute;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
@ -900,7 +937,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 +945,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 +958,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));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -93,6 +93,12 @@ char *pa_sink_list_to_string(pa_core *c) {
|
|||
pa_strbuf *s;
|
||||
pa_sink *sink;
|
||||
uint32_t idx = PA_IDXSET_INVALID;
|
||||
static const char* const state_table[] = {
|
||||
[PA_SINK_RUNNING] = "RUNNING",
|
||||
[PA_SINK_SUSPENDED] = "SUSPENDED",
|
||||
[PA_SINK_IDLE] = "IDLE",
|
||||
[PA_SINK_DISCONNECTED] = "DISCONNECTED"
|
||||
};
|
||||
assert(c);
|
||||
|
||||
s = pa_strbuf_new();
|
||||
|
|
@ -108,22 +114,29 @@ char *pa_sink_list_to_string(pa_core *c) {
|
|||
" %c index: %u\n"
|
||||
"\tname: <%s>\n"
|
||||
"\tdriver: <%s>\n"
|
||||
"\tis_hardware: <%i>\n"
|
||||
"\tstate: %s\n"
|
||||
"\tvolume: <%s>\n"
|
||||
"\tmute: <%i>\n"
|
||||
"\tlatency: <%0.0f usec>\n"
|
||||
"\tmonitor_source: <%u>\n"
|
||||
"\tsample spec: <%s>\n"
|
||||
"\tchannel map: <%s>\n",
|
||||
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->is_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));
|
||||
|
||||
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,6 +148,12 @@ char *pa_source_list_to_string(pa_core *c) {
|
|||
pa_strbuf *s;
|
||||
pa_source *source;
|
||||
uint32_t idx = PA_IDXSET_INVALID;
|
||||
static const char* const state_table[] = {
|
||||
[PA_SOURCE_RUNNING] = "RUNNING",
|
||||
[PA_SOURCE_SUSPENDED] = "SUSPENDED",
|
||||
[PA_SOURCE_IDLE] = "IDLE",
|
||||
[PA_SOURCE_DISCONNECTED] = "DISCONNECTED"
|
||||
};
|
||||
assert(c);
|
||||
|
||||
s = pa_strbuf_new();
|
||||
|
|
@ -143,7 +162,7 @@ char *pa_source_list_to_string(pa_core *c) {
|
|||
pa_strbuf_printf(s, "%u source(s) available.\n", pa_idxset_size(c->sources));
|
||||
|
||||
for (source = pa_idxset_first(c->sources, &idx); source; source = pa_idxset_next(c->sources, &idx)) {
|
||||
char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
|
||||
char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX];
|
||||
|
||||
|
||||
pa_strbuf_printf(
|
||||
|
|
@ -151,6 +170,10 @@ char *pa_source_list_to_string(pa_core *c) {
|
|||
" %c index: %u\n"
|
||||
"\tname: <%s>\n"
|
||||
"\tdriver: <%s>\n"
|
||||
"\tis_hardware: <%i>\n"
|
||||
"\tstate: %s\n"
|
||||
"\tvolume: <%s>\n"
|
||||
"\tmute: <%u>\n"
|
||||
"\tlatency: <%0.0f usec>\n"
|
||||
"\tsample spec: <%s>\n"
|
||||
"\tchannel map: <%s>\n",
|
||||
|
|
@ -158,14 +181,18 @@ char *pa_source_list_to_string(pa_core *c) {
|
|||
source->index,
|
||||
source->name,
|
||||
source->driver,
|
||||
!!source->is_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));
|
||||
|
||||
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,9 +206,9 @@ 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_DISCONNECTED] = "DISCONNECTED"
|
||||
};
|
||||
assert(c);
|
||||
|
||||
|
|
@ -202,14 +229,16 @@ char *pa_source_output_list_to_string(pa_core *c) {
|
|||
"\tdriver: <%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],
|
||||
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,9 +256,10 @@ 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_DISCONNECTED] = "DISCONNECTED"
|
||||
};
|
||||
|
||||
assert(c);
|
||||
|
|
@ -251,6 +281,7 @@ char *pa_sink_input_list_to_string(pa_core *c) {
|
|||
"\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 +289,17 @@ char *pa_sink_input_list_to_string(pa_core *c) {
|
|||
i->index,
|
||||
i->name,
|
||||
i->driver,
|
||||
state_table[i->state],
|
||||
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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,9 +24,6 @@
|
|||
USA.
|
||||
***/
|
||||
|
||||
typedef enum pa_mixer {
|
||||
PA_MIXER_SOFTWARE,
|
||||
PA_MIXER_HARDWARE
|
||||
} pa_mixer_t;
|
||||
/* FIXME: Remove this shit */
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -45,13 +45,59 @@
|
|||
#include <pulsecore/props.h>
|
||||
#include <pulsecore/random.h>
|
||||
#include <pulsecore/log.h>
|
||||
#include <pulsecore/macro.h>
|
||||
|
||||
#include "core.h"
|
||||
|
||||
static int core_process_msg(pa_msgobject *o, int code, void *userdata, 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 asyncmsgq_cb(pa_mainloop_api*api, pa_io_event* e, int fd, pa_io_event_flags_t events, void *userdata) {
|
||||
pa_core *c = userdata;
|
||||
|
||||
pa_assert(pa_asyncmsgq_get_fd(c->asyncmsgq) == fd);
|
||||
pa_assert(events == PA_IO_EVENT_INPUT);
|
||||
|
||||
pa_asyncmsgq_after_poll(c->asyncmsgq);
|
||||
|
||||
for (;;) {
|
||||
pa_msgobject *object;
|
||||
int code;
|
||||
void *data;
|
||||
pa_memchunk chunk;
|
||||
|
||||
/* Check whether there is a message for us to process */
|
||||
while (pa_asyncmsgq_get(c->asyncmsgq, &object, &code, &data, &chunk, 0) == 0) {
|
||||
pa_asyncmsgq_dispatch(object, code, data, &chunk);
|
||||
pa_asyncmsgq_done(c->asyncmsgq, 0);
|
||||
}
|
||||
|
||||
if (pa_asyncmsgq_before_poll(c->asyncmsgq) == 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void core_free(pa_object *o);
|
||||
|
||||
pa_core* pa_core_new(pa_mainloop_api *m, int shared) {
|
||||
pa_core* c;
|
||||
pa_mempool *pool;
|
||||
|
||||
pa_assert(m);
|
||||
|
||||
if (shared) {
|
||||
if (!(pool = pa_mempool_new(shared))) {
|
||||
pa_log_warn("failed to allocate shared memory pool. Falling back to a normal memory pool.");
|
||||
|
|
@ -66,7 +112,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);
|
||||
|
|
@ -123,11 +171,17 @@ pa_core* pa_core_new(pa_mainloop_api *m, int shared) {
|
|||
#ifdef SIGPIPE
|
||||
pa_check_signal_is_blocked(SIGPIPE);
|
||||
#endif
|
||||
|
||||
pa_assert_se(c->asyncmsgq = pa_asyncmsgq_new(0));
|
||||
pa_assert_se(pa_asyncmsgq_before_poll(c->asyncmsgq) == 0);
|
||||
pa_assert_se(c->asyncmsgq_event = c->mainloop->io_new(c->mainloop, pa_asyncmsgq_get_fd(c->asyncmsgq), PA_IO_EVENT_INPUT, asyncmsgq_cb, c));
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
void pa_core_free(pa_core *c) {
|
||||
assert(c);
|
||||
static void core_free(pa_object *o) {
|
||||
pa_core *c = PA_CORE(o);
|
||||
pa_core_assert_ref(c);
|
||||
|
||||
pa_module_unload_all(c);
|
||||
assert(!c->modules);
|
||||
|
|
@ -162,6 +216,10 @@ void pa_core_free(pa_core *c) {
|
|||
|
||||
pa_property_cleanup(c);
|
||||
|
||||
c->mainloop->io_free(c->asyncmsgq_event);
|
||||
pa_asyncmsgq_after_poll(c->asyncmsgq);
|
||||
pa_asyncmsgq_free(c->asyncmsgq);
|
||||
|
||||
pa_hook_free(&c->hook_sink_input_new);
|
||||
pa_hook_free(&c->hook_sink_disconnect);
|
||||
pa_hook_free(&c->hook_source_output_new);
|
||||
|
|
|
|||
|
|
@ -34,17 +34,21 @@
|
|||
#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>
|
||||
|
||||
/* 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;
|
||||
|
|
@ -88,10 +92,20 @@ struct pa_core {
|
|||
hook_sink_disconnect,
|
||||
hook_source_output_new,
|
||||
hook_source_disconnect;
|
||||
|
||||
pa_asyncmsgq *asyncmsgq;
|
||||
pa_io_event *asyncmsgq_event;
|
||||
};
|
||||
|
||||
PA_DECLARE_CLASS(pa_core);
|
||||
#define PA_CORE(o) ((pa_core*) 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);
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@
|
|||
#include <pulsecore/atomic.h>
|
||||
#include <pulsecore/log.h>
|
||||
#include <pulsecore/thread.h>
|
||||
#include <pulsecore/macro.h>
|
||||
#include <pulse/xmalloc.h>
|
||||
|
||||
#include "flist.h"
|
||||
|
|
@ -90,18 +91,19 @@ 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;
|
||||
};
|
||||
|
||||
#define PA_FLIST_CELLS(x) ((struct cell*) ((uint8_t*) (x) + PA_ALIGN(sizeof(struct pa_flist))))
|
||||
|
||||
static int is_power_of_two(unsigned size) {
|
||||
return !(size & (size - 1));
|
||||
}
|
||||
|
|
@ -114,10 +116,9 @@ pa_flist *pa_flist_new(unsigned size) {
|
|||
|
||||
assert(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);
|
||||
|
|
@ -134,30 +135,35 @@ void pa_flist_free(pa_flist *l, pa_free_cb_t free_cb) {
|
|||
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;
|
||||
|
||||
assert(l);
|
||||
assert(p);
|
||||
|
||||
cells = PA_FLIST_CELLS(l);
|
||||
|
||||
n = len = (int) l->size - pa_atomic_load(&l->length) + N_EXTRA_SCAN;
|
||||
_Y;
|
||||
idx = reduce(l, pa_atomic_load(&l->write_idx));
|
||||
|
|
@ -165,13 +171,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;
|
||||
|
|
@ -191,9 +197,12 @@ int pa_flist_push(pa_flist*l, void *p) {
|
|||
|
||||
void* pa_flist_pop(pa_flist*l) {
|
||||
int idx, len, n;
|
||||
struct cell *cells;
|
||||
|
||||
assert(l);
|
||||
|
||||
cells = PA_FLIST_CELLS(l);
|
||||
|
||||
n = len = pa_atomic_load(&l->length) + N_EXTRA_SCAN;
|
||||
_Y;
|
||||
idx = reduce(l, pa_atomic_load(&l->read_idx));
|
||||
|
|
@ -201,14 +210,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);
|
||||
|
|
|
|||
|
|
@ -26,6 +26,8 @@
|
|||
|
||||
#include <pulse/def.h>
|
||||
|
||||
#include <pulsecore/once.h>
|
||||
|
||||
/* A multiple-reader multipler-write lock-free free list implementation */
|
||||
|
||||
typedef struct pa_flist pa_flist;
|
||||
|
|
@ -38,4 +40,22 @@ 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);
|
||||
|
||||
#define PA_STATIC_FLIST_DECLARE(name, size) \
|
||||
struct { \
|
||||
pa_flist *flist; \
|
||||
pa_once_t once; \
|
||||
} name##_static_flist = { NULL, PA_ONCE_INIT }; \
|
||||
\
|
||||
static void name##_init(void) { \
|
||||
name##_static_flist.flist = pa_flist_new(size); \
|
||||
} \
|
||||
\
|
||||
static inline pa_flist* name##_get(void) { \
|
||||
pa_once(&name##_static_flist.once, name##_init); \
|
||||
return name##_static_flist.flist; \
|
||||
} \
|
||||
struct __stupid_useless_struct_to_allow_trailing_semicolon
|
||||
|
||||
#define PA_STATIC_FLIST_GET(name) (name##_get())
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@
|
|||
#include <string.h>
|
||||
|
||||
#include <pulse/xmalloc.h>
|
||||
#include <pulsecore/macro.h>
|
||||
|
||||
#include "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);
|
||||
|
||||
|
|
|
|||
80
src/pulsecore/macro.h
Normal file
80
src/pulsecore/macro.h
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
#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 <pulsecore/log.h>
|
||||
|
||||
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))
|
||||
|
||||
#define PA_ELEMENTSOF(x) (sizeof(x)/sizeof((x)[0]))
|
||||
|
||||
#define SA_MAX(a, b) ((a) > (b) ? (a) : (b))
|
||||
#define SA_MIN(a, b) ((a) < (b) ? (a) : (b))
|
||||
|
||||
#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 */
|
||||
#define pa_assert_se(x) do { \
|
||||
int _r = !!(x); \
|
||||
pa_assert(_r); \
|
||||
} while(0)
|
||||
|
||||
#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)
|
||||
|
||||
#endif
|
||||
|
|
@ -91,6 +91,7 @@ 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);
|
||||
|
|
@ -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);
|
||||
assert(m->leftover.length <= m->base);
|
||||
assert(m->leftover.length <= pa_memblock_get_length(m->leftover.memblock));
|
||||
|
||||
if (c->length > l) {
|
||||
/* Save the remainder of the memory block */
|
||||
|
|
|
|||
|
|
@ -33,10 +33,14 @@
|
|||
#include <unistd.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 "memblock.h"
|
||||
|
||||
|
|
@ -48,6 +52,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 +85,8 @@ struct pa_memimport_segment {
|
|||
};
|
||||
|
||||
struct pa_memimport {
|
||||
pa_mutex *mutex;
|
||||
|
||||
pa_mempool *pool;
|
||||
pa_hashmap *segments;
|
||||
pa_hashmap *blocks;
|
||||
|
|
@ -73,9 +105,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,21 +129,27 @@ 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);
|
||||
|
||||
/* No lock necessary */
|
||||
static void stat_add(pa_memblock*b) {
|
||||
assert(b);
|
||||
assert(b->pool);
|
||||
|
|
@ -129,6 +169,7 @@ 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);
|
||||
|
|
@ -152,6 +193,7 @@ 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;
|
||||
|
||||
|
|
@ -164,56 +206,70 @@ 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);
|
||||
|
||||
b = pa_xmalloc(sizeof(pa_memblock) + length);
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
return (uint8_t*) slot + 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);
|
||||
|
||||
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,6 +279,7 @@ 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;
|
||||
|
|
@ -237,7 +294,7 @@ pa_memblock *pa_memblock_new_pool(pa_mempool *p, size_t length) {
|
|||
|
||||
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 + sizeof(pa_memblock));
|
||||
|
||||
} else if (p->block_size - sizeof(struct mempool_slot) >= length) {
|
||||
|
||||
|
|
@ -246,22 +303,26 @@ pa_memblock *pa_memblock_new_pool(pa_mempool *p, size_t length) {
|
|||
|
||||
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_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;
|
||||
|
||||
|
|
@ -270,17 +331,20 @@ pa_memblock *pa_memblock_new_fixed(pa_mempool *p, void *d, size_t length, int re
|
|||
assert(length > 0);
|
||||
|
||||
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;
|
||||
|
||||
|
|
@ -290,18 +354,68 @@ pa_memblock *pa_memblock_new_user(pa_mempool *p, void *d, size_t length, void (*
|
|||
assert(free_cb);
|
||||
|
||||
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) {
|
||||
assert(b);
|
||||
assert(PA_REFCNT_VALUE(b) > 0);
|
||||
|
||||
return b->read_only && PA_REFCNT_VALUE(b) == 1;
|
||||
}
|
||||
|
||||
/* No lock necessary */
|
||||
void* pa_memblock_acquire(pa_memblock *b) {
|
||||
assert(b);
|
||||
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;
|
||||
assert(b);
|
||||
assert(PA_REFCNT_VALUE(b) > 0);
|
||||
|
||||
r = pa_atomic_dec(&b->n_acquired);
|
||||
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) {
|
||||
assert(b);
|
||||
assert(PA_REFCNT_VALUE(b) > 0);
|
||||
|
||||
return b->length;
|
||||
}
|
||||
|
||||
pa_mempool* pa_memblock_get_pool(pa_memblock *b) {
|
||||
assert(b);
|
||||
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);
|
||||
|
|
@ -310,19 +424,17 @@ pa_memblock* pa_memblock_ref(pa_memblock*b) {
|
|||
return b;
|
||||
}
|
||||
|
||||
void pa_memblock_unref(pa_memblock*b) {
|
||||
static void memblock_free(pa_memblock *b) {
|
||||
assert(b);
|
||||
assert(PA_REFCNT_VALUE(b) > 0);
|
||||
|
||||
if (PA_REFCNT_DEC(b) > 0)
|
||||
return;
|
||||
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);
|
||||
b->per_type.user.free_cb(pa_atomic_ptr_load(&b->data));
|
||||
|
||||
/* Fall through */
|
||||
|
||||
|
|
@ -333,17 +445,24 @@ void pa_memblock_unref(pa_memblock*b) {
|
|||
|
||||
case PA_MEMBLOCK_IMPORTED : {
|
||||
pa_memimport_segment *segment;
|
||||
pa_memimport *import;
|
||||
|
||||
/* FIXME! This should be implemented lock-free */
|
||||
|
||||
segment = b->per_type.imported.segment;
|
||||
assert(segment);
|
||||
assert(segment->import);
|
||||
|
||||
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);
|
||||
import = segment->import;
|
||||
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_mutex_unlock(import->mutex);
|
||||
|
||||
import->release_cb(import, b->per_type.imported.id, import->userdata);
|
||||
|
||||
pa_xfree(b);
|
||||
break;
|
||||
}
|
||||
|
|
@ -351,13 +470,20 @@ void pa_memblock_unref(pa_memblock*b) {
|
|||
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);
|
||||
slot = mempool_slot_by_ptr(b->pool, pa_atomic_ptr_load(&b->data));
|
||||
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)
|
||||
/* 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)
|
||||
pa_xfree(b);
|
||||
|
||||
break;
|
||||
|
|
@ -369,6 +495,36 @@ void pa_memblock_unref(pa_memblock*b) {
|
|||
}
|
||||
}
|
||||
|
||||
/* No lock necessary */
|
||||
void pa_memblock_unref(pa_memblock*b) {
|
||||
assert(b);
|
||||
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) {
|
||||
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);
|
||||
|
||||
|
|
@ -381,38 +537,43 @@ static void memblock_make_local(pa_memblock *b) {
|
|||
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);
|
||||
|
||||
if (PA_REFCNT_VALUE(b) > 1)
|
||||
if (PA_REFCNT_DEC(b) > 0)
|
||||
memblock_make_local(b);
|
||||
|
||||
pa_memblock_unref(b);
|
||||
else
|
||||
memblock_free(b);
|
||||
}
|
||||
|
||||
/* Self-locked. This function is not multiple-caller safe */
|
||||
static void memblock_replace_import(pa_memblock *b) {
|
||||
pa_memimport_segment *seg;
|
||||
|
||||
|
|
@ -428,6 +589,8 @@ static void memblock_replace_import(pa_memblock *b) {
|
|||
assert(seg);
|
||||
assert(seg->import);
|
||||
|
||||
pa_mutex_lock(seg->import->mutex);
|
||||
|
||||
pa_hashmap_remove(
|
||||
seg->import->blocks,
|
||||
PA_UINT32_TO_PTR(b->per_type.imported.id));
|
||||
|
|
@ -436,6 +599,8 @@ static void memblock_replace_import(pa_memblock *b) {
|
|||
|
||||
if (-- seg->n_blocks <= 0)
|
||||
segment_detach(seg);
|
||||
|
||||
pa_mutex_unlock(seg->import->mutex);
|
||||
}
|
||||
|
||||
pa_mempool* pa_mempool_new(int shared) {
|
||||
|
|
@ -444,12 +609,15 @@ pa_mempool* pa_mempool_new(int shared) {
|
|||
|
||||
p = pa_xnew(pa_mempool, 1);
|
||||
|
||||
p->mutex = pa_mutex_new(1);
|
||||
p->semaphore = pa_semaphore_new(0);
|
||||
|
||||
#ifdef HAVE_SYSCONF
|
||||
ps = (size_t) sysconf(_SC_PAGESIZE);
|
||||
#elif defined(PAGE_SIZE)
|
||||
ps = (size_t) PAGE_SIZE;
|
||||
ps = (size_t) PAGE_SIZE;
|
||||
#else
|
||||
ps = 4096; /* Let's hope it's like x86. */
|
||||
ps = 4096; /* Let's hope it's like x86. */
|
||||
#endif
|
||||
|
||||
p->block_size = (PA_MEMPOOL_SLOT_SIZE/ps)*ps;
|
||||
|
|
@ -466,13 +634,13 @@ pa_mempool* pa_mempool_new(int shared) {
|
|||
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;
|
||||
}
|
||||
|
|
@ -480,34 +648,61 @@ pa_mempool* pa_mempool_new(int shared) {
|
|||
void pa_mempool_free(pa_mempool *p) {
|
||||
assert(p);
|
||||
|
||||
pa_mutex_lock(p->mutex);
|
||||
|
||||
while (p->imports)
|
||||
pa_memimport_free(p->imports);
|
||||
|
||||
while (p->exports)
|
||||
pa_memexport_free(p->exports);
|
||||
|
||||
pa_mutex_unlock(p->mutex);
|
||||
|
||||
if (pa_atomic_load(&p->stat.n_allocated) > 0)
|
||||
pa_log_warn("WARNING! Memory pool destroyed but not all memory blocks freed!");
|
||||
|
||||
pa_flist_free(p->free_slots, NULL);
|
||||
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);
|
||||
|
||||
return &p->stat;
|
||||
}
|
||||
|
||||
/* No lock necessary */
|
||||
void pa_mempool_vacuum(pa_mempool *p) {
|
||||
struct mempool_slot *slot;
|
||||
pa_flist *list;
|
||||
|
||||
assert(p);
|
||||
|
||||
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));
|
||||
list = pa_flist_new(p->n_blocks*2);
|
||||
|
||||
while ((slot = pa_flist_pop(p->free_slots)))
|
||||
while (pa_flist_push(list, slot) < 0)
|
||||
;
|
||||
|
||||
while ((slot = pa_flist_pop(list))) {
|
||||
pa_shm_punch(&p->memory,
|
||||
(uint8_t*) slot - (uint8_t*) p->memory.ptr + sizeof(struct mempool_slot),
|
||||
p->block_size - 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);
|
||||
|
||||
|
|
@ -519,6 +714,7 @@ 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);
|
||||
|
||||
|
|
@ -533,18 +729,23 @@ pa_memimport* pa_memimport_new(pa_mempool *p, pa_memimport_release_cb_t cb, void
|
|||
assert(cb);
|
||||
|
||||
i = pa_xnew(pa_memimport, 1);
|
||||
i->mutex = pa_mutex_new(0);
|
||||
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,6 +766,7 @@ 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);
|
||||
|
||||
|
|
@ -573,51 +775,68 @@ static void segment_detach(pa_memimport_segment *seg) {
|
|||
pa_xfree(seg);
|
||||
}
|
||||
|
||||
/* Self-locked. Not multiple-caller safe */
|
||||
void pa_memimport_free(pa_memimport *i) {
|
||||
pa_memexport *e;
|
||||
pa_memblock *b;
|
||||
|
||||
assert(i);
|
||||
|
||||
/* 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);
|
||||
pa_mutex_lock(i->mutex);
|
||||
|
||||
while ((b = pa_hashmap_get_first(i->blocks)))
|
||||
memblock_replace_import(b);
|
||||
|
||||
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);
|
||||
|
||||
PA_LLIST_REMOVE(pa_memimport, i->pool->imports, i);
|
||||
|
||||
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_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);
|
||||
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 +844,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;
|
||||
|
|
@ -634,10 +857,15 @@ int pa_memimport_process_revoke(pa_memimport *i, uint32_t id) {
|
|||
pa_memblock *b;
|
||||
assert(i);
|
||||
|
||||
pa_mutex_lock(i->mutex);
|
||||
|
||||
if (!(b = pa_hashmap_get(i->blocks, PA_UINT32_TO_PTR(id))))
|
||||
return -1;
|
||||
|
||||
memblock_replace_import(b);
|
||||
|
||||
pa_mutex_unlock(i->mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -652,6 +880,7 @@ pa_memexport* pa_memexport_new(pa_mempool *p, pa_memexport_revoke_cb_t cb, void
|
|||
return NULL;
|
||||
|
||||
e = pa_xnew(pa_memexport, 1);
|
||||
e->mutex = pa_mutex_new(1);
|
||||
e->pool = p;
|
||||
PA_LLIST_HEAD_INIT(struct memexport_slot, e->free_slots);
|
||||
PA_LLIST_HEAD_INIT(struct memexport_slot, e->used_slots);
|
||||
|
|
@ -659,51 +888,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_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_xfree(e);
|
||||
}
|
||||
|
||||
/* Self-locked */
|
||||
int pa_memexport_process_release(pa_memexport *e, uint32_t id) {
|
||||
pa_memblock *b;
|
||||
|
||||
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;
|
||||
|
||||
/* 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_atomic_dec(&e->pool->stat.n_exported);
|
||||
pa_atomic_sub(&e->pool->stat.exported_size, e->slots[id].block->length);
|
||||
|
||||
pa_memblock_unref(e->slots[id].block);
|
||||
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) b->length);
|
||||
|
||||
pa_atomic_dec(&e->pool->stat.n_exported);
|
||||
pa_atomic_sub(&e->pool->stat.exported_size, b->length);
|
||||
|
||||
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_mutex_lock(e->mutex);
|
||||
|
||||
for (slot = e->used_slots; slot; slot = next) {
|
||||
uint32_t idx;
|
||||
next = slot->next;
|
||||
|
|
@ -716,8 +969,11 @@ 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;
|
||||
|
||||
|
|
@ -734,13 +990,16 @@ static pa_memblock *memblock_shared_copy(pa_mempool *p, pa_memblock *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;
|
||||
size_t length;
|
||||
|
||||
assert(e);
|
||||
assert(b);
|
||||
|
|
@ -753,12 +1012,15 @@ int pa_memexport_put(pa_memexport *e, pa_memblock *b, uint32_t *block_id, uint32
|
|||
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,8 +1029,11 @@ 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);
|
||||
memory = &b->per_type.imported.segment->memory;
|
||||
|
|
@ -778,15 +1043,17 @@ int pa_memexport_put(pa_memexport *e, pa_memblock *b, uint32_t *block_id, uint32
|
|||
memory = &b->pool->memory;
|
||||
}
|
||||
|
||||
assert(b->data >= memory->ptr);
|
||||
assert((uint8_t*) b->data + b->length <= (uint8_t*) memory->ptr + memory->size);
|
||||
assert(data >= memory->ptr);
|
||||
assert((uint8_t*) data + length <= (uint8_t*) memory->ptr + memory->size);
|
||||
|
||||
*shm_id = memory->id;
|
||||
*offset = (uint8_t*) b->data - (uint8_t*) memory->ptr;
|
||||
*size = b->length;
|
||||
*offset = (uint8_t*) data - (uint8_t*) memory->ptr;
|
||||
*size = length;
|
||||
|
||||
pa_memblock_release(b);
|
||||
|
||||
pa_atomic_inc(&e->pool->stat.n_exported);
|
||||
pa_atomic_add(&e->pool->stat.exported_size, b->length);
|
||||
pa_atomic_add(&e->pool->stat.exported_size, length);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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_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_int_t n_too_large_for_pool;
|
||||
pa_atomic_int_t n_pool_full;
|
||||
pa_atomic_t n_too_large_for_pool;
|
||||
pa_atomic_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_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,17 @@ 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);
|
||||
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);
|
||||
|
||||
/* The memory block manager */
|
||||
pa_mempool* pa_mempool_new(int shared);
|
||||
void pa_mempool_free(pa_mempool *p);
|
||||
|
|
|
|||
|
|
@ -178,7 +178,7 @@ int pa_memblockq_push(pa_memblockq* bq, const pa_memchunk *uchunk) {
|
|||
assert(uchunk);
|
||||
assert(uchunk->memblock);
|
||||
assert(uchunk->length > 0);
|
||||
assert(uchunk->index + uchunk->length <= uchunk->memblock->length);
|
||||
assert(uchunk->index + uchunk->length <= pa_memblock_get_length(uchunk->memblock));
|
||||
|
||||
if (uchunk->length % bq->base)
|
||||
return -1;
|
||||
|
|
@ -362,8 +362,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 {
|
||||
|
|
@ -415,8 +415,8 @@ void pa_memblockq_drop(pa_memblockq *bq, const pa_memchunk *chunk, size_t length
|
|||
|
||||
if (bq->silence) {
|
||||
|
||||
if (!l || l > bq->silence->length)
|
||||
l = bq->silence->length;
|
||||
if (!l || l > pa_memblock_get_length(bq->silence))
|
||||
l = pa_memblock_get_length(bq->silence);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -37,22 +37,25 @@
|
|||
void 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);
|
||||
|
||||
if (PA_REFCNT_VALUE(c->memblock) == 1 &&
|
||||
!c->memblock->read_only &&
|
||||
c->memblock->length >= c->index+min)
|
||||
if (pa_memblock_is_read_only(c->memblock) &&
|
||||
pa_memblock_get_length(c->memblock) >= c->index+min)
|
||||
return;
|
||||
|
||||
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;
|
||||
|
|
|
|||
40
src/pulsecore/msgobject.c
Normal file
40
src/pulsecore/msgobject.c
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
/* $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_msgobject *pa_msgobject_new_internal(size_t size, const char *type_name) {
|
||||
pa_msgobject *o;
|
||||
|
||||
pa_assert(size > sizeof(pa_msgobject));
|
||||
pa_assert(type_name);
|
||||
|
||||
o = PA_MSGOBJECT(pa_object_new_internal(size, type_name));
|
||||
o->process_msg = NULL;
|
||||
return o;
|
||||
}
|
||||
52
src/pulsecore/msgobject.h
Normal file
52
src/pulsecore/msgobject.h
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
#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, pa_memchunk *chunk);
|
||||
};
|
||||
|
||||
pa_msgobject *pa_msgobject_new_internal(size_t size, const char *type_name);
|
||||
|
||||
#define pa_msgobject_new(type) ((type*) pa_msgobject_new_internal(sizeof(type), #type))
|
||||
#define pa_msgobject_free ((void (*) (pa_msgobject* o)) pa_object_free)
|
||||
|
||||
#define PA_MSGOBJECT(o) ((pa_msgobject*) (o))
|
||||
|
||||
PA_DECLARE_CLASS(pa_msgobject);
|
||||
|
||||
#endif
|
||||
|
|
@ -28,8 +28,6 @@
|
|||
#include <assert.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include <atomic_ops.h>
|
||||
|
||||
#include <pulse/xmalloc.h>
|
||||
|
||||
#include "mutex.h"
|
||||
|
|
|
|||
61
src/pulsecore/object.c
Normal file
61
src/pulsecore/object.c
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
/* $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) {
|
||||
pa_object *o;
|
||||
|
||||
pa_assert(size > sizeof(pa_object));
|
||||
pa_assert(type_name);
|
||||
|
||||
o = pa_xmalloc(size);
|
||||
PA_REFCNT_INIT(o);
|
||||
o->type_name = type_name;
|
||||
o->free = pa_object_free;
|
||||
|
||||
return o;
|
||||
}
|
||||
|
||||
pa_object *pa_object_ref(pa_object *o) {
|
||||
pa_assert(o);
|
||||
pa_assert(PA_REFCNT_VALUE(o) >= 1);
|
||||
|
||||
PA_REFCNT_INC(o);
|
||||
return o;
|
||||
}
|
||||
|
||||
void pa_object_unref(pa_object *o) {
|
||||
pa_assert(o);
|
||||
pa_assert(PA_REFCNT_VALUE(o) >= 1);
|
||||
|
||||
if (PA_REFCNT_DEC(o) <= 0) {
|
||||
pa_assert(o->free);
|
||||
o->free(o);
|
||||
}
|
||||
}
|
||||
72
src/pulsecore/object.h
Normal file
72
src/pulsecore/object.h
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
#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 <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);
|
||||
};
|
||||
|
||||
pa_object *pa_object_new_internal(size_t size, const char *type_name);
|
||||
#define pa_object_new(type) ((type*) pa_object_new_internal(sizeof(type), #type))
|
||||
|
||||
#define pa_object_free ((void (*) (pa_object* o)) pa_xfree)
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
#define pa_object_assert_ref(o) pa_assert(pa_object_refcnt(o))
|
||||
|
||||
#define PA_OBJECT(o) ((pa_object*) (o))
|
||||
|
||||
#define PA_DECLARE_CLASS(c) \
|
||||
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
|
||||
|
||||
#endif
|
||||
|
|
@ -26,44 +26,56 @@
|
|||
#endif
|
||||
|
||||
#include <pthread.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <pulsecore/macro.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);
|
||||
}
|
||||
|
||||
/* Not reentrant -- how could it be? */
|
||||
void pa_once(pa_once_t *control, pa_once_func_t func) {
|
||||
assert(control);
|
||||
assert(func);
|
||||
pa_mutex *m;
|
||||
|
||||
pa_assert(control);
|
||||
pa_assert(func);
|
||||
|
||||
/* Create the global mutex */
|
||||
ASSERT_SUCCESS(pthread_once(&global_mutex_once, global_mutex_once_func));
|
||||
if (pa_atomic_load(&control->done))
|
||||
return;
|
||||
|
||||
pa_atomic_inc(&control->ref);
|
||||
|
||||
for (;;) {
|
||||
|
||||
if ((m = pa_atomic_ptr_load(&control->mutex))) {
|
||||
|
||||
/* Create the local mutex */
|
||||
pa_mutex_lock(global_mutex);
|
||||
if (!control->mutex)
|
||||
control->mutex = pa_mutex_new(1);
|
||||
pa_mutex_unlock(global_mutex);
|
||||
/* The mutex is stored in locked state, hence let's just
|
||||
* wait until it is unlocked */
|
||||
pa_mutex_lock(m);
|
||||
pa_mutex_unlock(m);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Execute function */
|
||||
pa_mutex_lock(control->mutex);
|
||||
if (!control->once_value) {
|
||||
control->once_value = 1;
|
||||
func();
|
||||
pa_assert_se(m = pa_mutex_new(0));
|
||||
pa_mutex_lock(m);
|
||||
|
||||
if (pa_atomic_ptr_cmpxchg(&control->mutex, NULL, m)) {
|
||||
func();
|
||||
pa_atomic_store(&control->done, 1);
|
||||
pa_mutex_unlock(m);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
pa_mutex_unlock(m);
|
||||
pa_mutex_free(m);
|
||||
}
|
||||
|
||||
pa_assert(pa_atomic_load(&control->done));
|
||||
|
||||
if (pa_atomic_dec(&control->ref) <= 1) {
|
||||
pa_assert(pa_atomic_ptr_cmpxchg(&control->mutex, m, NULL));
|
||||
pa_mutex_free(m);
|
||||
}
|
||||
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
|
||||
|
|
|
|||
|
|
@ -25,13 +25,19 @@
|
|||
***/
|
||||
|
||||
#include <pulsecore/mutex.h>
|
||||
#include <pulsecore/atomic.h>
|
||||
|
||||
typedef struct pa_once {
|
||||
unsigned int once_value;
|
||||
pa_mutex *mutex;
|
||||
pa_atomic_ptr_t mutex;
|
||||
pa_atomic_t ref, done;
|
||||
} pa_once_t;
|
||||
|
||||
#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);
|
||||
|
||||
|
|
|
|||
|
|
@ -122,7 +122,5 @@ int pa_play_memblockq(
|
|||
|
||||
si->userdata = q;
|
||||
|
||||
pa_sink_notify(si->sink);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ static int sink_input_peek(pa_sink_input *i, pa_memchunk *chunk) {
|
|||
if (c->length <= 0)
|
||||
return -1;
|
||||
|
||||
assert(c->memblock && c->memblock->length);
|
||||
assert(c->memblock);
|
||||
*chunk = *c;
|
||||
pa_memblock_ref(c->memblock);
|
||||
|
||||
|
|
@ -122,7 +122,5 @@ int pa_play_memchunk(
|
|||
|
||||
pa_memblock_ref(chunk->memblock);
|
||||
|
||||
pa_sink_notify(si->sink);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -894,14 +894,22 @@ 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);
|
||||
assert(c->scache.memchunk.memblock);
|
||||
assert(c->scache.name);
|
||||
assert(c->scache.memchunk.index < c->scache.memchunk.length);
|
||||
|
||||
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) {
|
||||
p = pa_memblock_acquire(c->scache.memchunk.memblock);
|
||||
|
||||
if ((r = pa_iochannel_read(c->io, (uint8_t*) p+c->scache.memchunk.index, c->scache.memchunk.length-c->scache.memchunk.index)) <= 0) {
|
||||
pa_memblock_release(c->scache.memchunk.memblock);
|
||||
pa_log_debug("read(): %s", r < 0 ? pa_cstrerror(errno) : "EOF");
|
||||
return -1;
|
||||
}
|
||||
|
||||
pa_memblock_release(c->scache.memchunk.memblock);
|
||||
|
||||
c->scache.memchunk.index += r;
|
||||
assert(c->scache.memchunk.index <= c->scache.memchunk.length);
|
||||
|
||||
|
|
@ -928,6 +936,7 @@ static int do_read(struct connection *c) {
|
|||
pa_memchunk chunk;
|
||||
ssize_t r;
|
||||
size_t l;
|
||||
void *p;
|
||||
|
||||
assert(c->input_memblockq);
|
||||
|
||||
|
|
@ -940,7 +949,7 @@ static int do_read(struct connection *c) {
|
|||
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;
|
||||
|
|
@ -948,14 +957,19 @@ static int do_read(struct connection *c) {
|
|||
|
||||
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);
|
||||
assert(c->playback.current_memblock);
|
||||
assert(pa_memblock_get_length(c->playback.current_memblock) >= 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);
|
||||
|
||||
if ((r = pa_iochannel_read(c->io, (uint8_t*) p+c->playback.memblock_index, l)) <= 0) {
|
||||
pa_memblock_release(c->playback.current_memblock);
|
||||
pa_log_debug("read(): %s", r < 0 ? pa_cstrerror(errno) : "EOF");
|
||||
return -1;
|
||||
}
|
||||
pa_memblock_release(c->playback.current_memblock);
|
||||
|
||||
chunk.memblock = c->playback.current_memblock;
|
||||
chunk.index = c->playback.memblock_index;
|
||||
|
|
@ -993,19 +1007,26 @@ 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);
|
||||
assert(chunk.memblock);
|
||||
assert(chunk.length);
|
||||
|
||||
if ((r = pa_iochannel_write(c->io, (uint8_t*) chunk.memblock->data+chunk.index, chunk.length)) < 0) {
|
||||
p = pa_memblock_acquire(chunk.memblock);
|
||||
|
||||
if ((r = pa_iochannel_write(c->io, (uint8_t*) p+chunk.index, chunk.length)) < 0) {
|
||||
pa_memblock_release(chunk.memblock);
|
||||
pa_memblock_unref(chunk.memblock);
|
||||
pa_log("write(): %s", pa_cstrerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
pa_memblock_release(chunk.memblock);
|
||||
|
||||
pa_memblockq_drop(c->output_memblockq, &chunk, r);
|
||||
pa_memblock_unref(chunk.memblock);
|
||||
|
||||
|
|
|
|||
|
|
@ -2278,6 +2278,7 @@ static void pstream_memblock_callback(pa_pstream *p, uint32_t channel, int64_t o
|
|||
} else {
|
||||
struct upload_stream *u = (struct upload_stream*) stream;
|
||||
size_t l;
|
||||
|
||||
assert(u->type == UPLOAD_STREAM);
|
||||
|
||||
if (!u->memchunk.memblock) {
|
||||
|
|
@ -2297,9 +2298,18 @@ static void pstream_memblock_callback(pa_pstream *p, uint32_t channel, int64_t o
|
|||
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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,6 @@
|
|||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
|
|
@ -54,13 +53,13 @@ struct connection {
|
|||
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_int missing;
|
||||
} playback;
|
||||
};
|
||||
|
||||
|
|
@ -69,35 +68,52 @@ struct pa_protocol_simple {
|
|||
pa_core *core;
|
||||
pa_socket_server*server;
|
||||
pa_idxset *connections;
|
||||
|
||||
pa_asyncmsgq *asyncmsgq;
|
||||
|
||||
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 */
|
||||
};
|
||||
|
||||
enum {
|
||||
MESSAGE_REQUEST_DATA, /* data from source output to main loop */
|
||||
MESSAGE_POST_DATA /* data from source output to main loop */
|
||||
};
|
||||
|
||||
|
||||
#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);
|
||||
pa_assert(c);
|
||||
|
||||
pa_idxset_remove_by_data(c->protocol->connections, c, NULL);
|
||||
|
||||
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_unref(c->sink_input);
|
||||
}
|
||||
|
||||
if (c->source_output) {
|
||||
pa_source_output_disconnect(c->source_output);
|
||||
pa_source_output_unref(c->source_output);
|
||||
}
|
||||
|
||||
if (c->playback.current_memblock)
|
||||
pa_memblock_unref(c->playback.current_memblock);
|
||||
|
||||
if (c->client)
|
||||
pa_client_free(c->client);
|
||||
if (c->io)
|
||||
|
|
@ -106,8 +122,7 @@ static void connection_free(struct connection *c) {
|
|||
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);
|
||||
}
|
||||
|
||||
|
|
@ -115,27 +130,37 @@ static int do_read(struct connection *c) {
|
|||
pa_memchunk chunk;
|
||||
ssize_t r;
|
||||
size_t l;
|
||||
void *p;
|
||||
|
||||
if (!c->sink_input || !(l = pa_memblockq_missing(c->input_memblockq)))
|
||||
pa_assert(c);
|
||||
|
||||
if (!c->sink_input || !(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, 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 (errno == EINTR || errno == EAGAIN)
|
||||
return 0;
|
||||
|
||||
pa_log_debug("read(): %s", r == 0 ? "EOF" : pa_cstrerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
|
@ -143,14 +168,10 @@ 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->protocol->asyncmsgq, c, MESSAGE_POST_DATA, NULL, &chunk, NULL, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -158,35 +179,41 @@ static int do_read(struct connection *c) {
|
|||
static int do_write(struct connection *c) {
|
||||
pa_memchunk chunk;
|
||||
ssize_t r;
|
||||
void *p;
|
||||
|
||||
p_assert(c);
|
||||
|
||||
if (!c->source_output)
|
||||
return 0;
|
||||
|
||||
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 (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);
|
||||
|
||||
|
||||
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);
|
||||
pa_assert(c);
|
||||
|
||||
if (c->dead)
|
||||
return;
|
||||
|
|
@ -207,103 +234,148 @@ 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);
|
||||
} else
|
||||
connection_free(c);
|
||||
}
|
||||
|
||||
/*** sink_input callbacks ***/
|
||||
|
||||
/* Called from thread context */
|
||||
static int sink_input_process_msg(pa_sink_input *i, int code, void *userdata, const pa_memchunk *chunk) {
|
||||
struct connection*c;
|
||||
|
||||
pa_assert(i);
|
||||
c = i->userdata;
|
||||
pa_assert(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);
|
||||
return 0;
|
||||
}
|
||||
|
||||
case PA_SINK_INPUT_MESSAGE_GET_LATENCY: {
|
||||
pa_usec_t *r = userdata;
|
||||
|
||||
*r = pa_bytes_to_usec(pa_memblockq_get_length(c->input_memblockq), &c->sink_input->sample_spec);
|
||||
|
||||
/* Fall through, the default handler will add in the extra
|
||||
* latency added by the resampler */
|
||||
}
|
||||
|
||||
default:
|
||||
return pa_sink_input_process_msg(i, code, userdata);
|
||||
}
|
||||
}
|
||||
|
||||
/* Called from thread context */
|
||||
static int sink_input_peek_cb(pa_sink_input *i, pa_memchunk *chunk) {
|
||||
struct connection*c;
|
||||
assert(i && i->userdata && chunk);
|
||||
|
||||
pa_assert(i);
|
||||
c = i->userdata;
|
||||
pa_assert(c);
|
||||
pa_assert(chunk);
|
||||
|
||||
if (pa_memblockq_peek(c->input_memblockq, chunk) < 0) {
|
||||
r = pa_memblockq_peek(c->input_memblockq, chunk);
|
||||
|
||||
if (c->dead)
|
||||
connection_free(c);
|
||||
if (c->dead && r < 0)
|
||||
connection_free(c);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Called from thread context */
|
||||
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);
|
||||
size_t old, new;
|
||||
|
||||
pa_assert(i);
|
||||
pa_assert(c);
|
||||
pa_assert(length);
|
||||
|
||||
old = pa_memblockq_missing(c->input_memblockq);
|
||||
pa_memblockq_drop(c->input_memblockq, chunk, length);
|
||||
new = pa_memblockq_missing(c->input_memblockq);
|
||||
|
||||
/* 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_atomic_store(&c->playback.missing, &new);
|
||||
|
||||
if (new > old)
|
||||
pa_asyncmsgq_post(c->protocol->asyncmsgq, c, MESSAGE_REQUEST_DATA, NULL, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
/* Called from main context */
|
||||
static void sink_input_kill_cb(pa_sink_input *i) {
|
||||
assert(i && i->userdata);
|
||||
pa_assert(i);
|
||||
pa_assert(i->userdata);
|
||||
|
||||
connection_free((struct connection *) i->userdata);
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/*** source_output callbacks ***/
|
||||
|
||||
static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk) {
|
||||
struct connection *c = o->userdata;
|
||||
assert(o && c && chunk);
|
||||
struct connection *c;
|
||||
|
||||
pa_assert(o);
|
||||
c = o->userdata;
|
||||
pa_assert(c);
|
||||
pa_assert(chunk);
|
||||
|
||||
pa_memblockq_push(c->output_memblockq, 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(c->protocol->asyncmsgq, c, MESSAGE_REQUEST_DATA, NULL, chunk, NULL, NULL);
|
||||
}
|
||||
|
||||
static void source_output_kill_cb(pa_source_output *o) {
|
||||
assert(o && o->userdata);
|
||||
connection_free((struct connection *) o->userdata);
|
||||
struct connection*c;
|
||||
|
||||
pa_assert(o);
|
||||
c = o->userdata;
|
||||
pa_assert(c);
|
||||
|
||||
connection_free(c);
|
||||
}
|
||||
|
||||
static pa_usec_t source_output_get_latency_cb(pa_source_output *o) {
|
||||
struct connection*c = o->userdata;
|
||||
assert(o && c);
|
||||
struct connection*c;
|
||||
|
||||
pa_assert(o);
|
||||
c = 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) {
|
||||
struct connection*c;
|
||||
|
||||
pa_assert(client);
|
||||
c = client->userdata;
|
||||
pa_assert(c);
|
||||
|
||||
connection_free(client);
|
||||
}
|
||||
|
||||
/*** 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 ***/
|
||||
|
||||
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);
|
||||
pa_assert(io);
|
||||
pa_assert(c);
|
||||
|
||||
do_work(c);
|
||||
}
|
||||
|
|
@ -314,7 +386,10 @@ static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata)
|
|||
pa_protocol_simple *p = userdata;
|
||||
struct 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,25 +397,25 @@ 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->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;
|
||||
|
||||
|
||||
if (p->mode & PLAYBACK) {
|
||||
pa_sink_input_new_data data;
|
||||
size_t l;
|
||||
|
|
@ -372,11 +447,13 @@ 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_assert(c->input_memblockq);
|
||||
pa_iochannel_socket_set_rcvbuf(io, l/PLAYBACK_BUFFER_FRAGMENTS*5);
|
||||
c->playback.fragment_size = l/10;
|
||||
|
||||
pa_sink_notify(c->sink_input->sink);
|
||||
pa_atomic_store(&c->playback.missing, pa_memblockq_missing(c->input_memblockq));
|
||||
|
||||
pa_sink_input_put(c->sink_input);
|
||||
}
|
||||
|
||||
if (p->mode & RECORD) {
|
||||
|
|
@ -409,16 +486,14 @@ 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:
|
||||
|
|
@ -426,16 +501,60 @@ fail:
|
|||
connection_free(c);
|
||||
}
|
||||
|
||||
static void asyncmsgq_cb(pa_mainloop_api*api, pa_io_event* e, int fd, pa_io_event_flags_t events, void *userdata) {
|
||||
pa_protocol_simple *p = userdata;
|
||||
int do_some_work = 0;
|
||||
|
||||
pa_assert(pa_asyncmsgq_get_fd(p->asyncmsgq) == fd);
|
||||
pa_assert(events == PA_IO_EVENT_INPUT);
|
||||
|
||||
pa_asyncmsgq_after_poll(p->asyncmsgq);
|
||||
|
||||
for (;;) {
|
||||
int code;
|
||||
void *object, *data;
|
||||
|
||||
/* Check whether there is a message for us to process */
|
||||
while (pa_asyncmsgq_get(p->asyncmsgq, &object, &code, &data) == 0) {
|
||||
|
||||
connection *c = object;
|
||||
|
||||
pa_assert(c);
|
||||
|
||||
switch (code) {
|
||||
|
||||
case MESSAGE_REQUEST_DATA:
|
||||
do_work(c);
|
||||
break;
|
||||
|
||||
case MESSAGE_POST_DATA:
|
||||
pa_memblockq_push(c->output_memblockq, chunk);
|
||||
do_work(c);
|
||||
break;
|
||||
}
|
||||
|
||||
pa_asyncmsgq_done(p->asyncmsgq);
|
||||
}
|
||||
|
||||
if (pa_asyncmsgq_before_poll(p->asyncmsgq) == 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
pa_assert(core);
|
||||
pa_assert(server);
|
||||
pa_assert(ma);
|
||||
|
||||
p = pa_xmalloc0(sizeof(pa_protocol_simple));
|
||||
p = pa_xnew0(pa_protocol_simple, 1);
|
||||
p->module = m;
|
||||
p->core = core;
|
||||
p->server = server;
|
||||
p->connections = pa_idxset_new(NULL, NULL);
|
||||
pa_assert_se(p->asyncmsgq = pa_asyncmsgq_new(0));
|
||||
|
||||
p->sample_spec = core->default_sample_spec;
|
||||
if (pa_modargs_get_sample_spec(ma, &p->sample_spec) < 0) {
|
||||
|
|
@ -467,18 +586,22 @@ pa_protocol_simple* pa_protocol_simple_new(pa_core *core, pa_socket_server *serv
|
|||
|
||||
pa_socket_server_set_callback(p->server, on_connection, p);
|
||||
|
||||
pa_assert_se(pa_asyncmsgq_before_poll(p->asyncmsgq) == 0);
|
||||
pa_assert_se(p->asyncmsgq_event = core->mainloop->io_event_new(core->mainloop, pa_asyncmsgq_get_fd(p->asyncmsgq), PA_IO_EVENT_INPUT, p));
|
||||
|
||||
return p;
|
||||
|
||||
fail:
|
||||
if (p)
|
||||
pa_protocol_simple_free(p);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
void pa_protocol_simple_free(pa_protocol_simple *p) {
|
||||
struct connection *c;
|
||||
assert(p);
|
||||
pa_assert(p);
|
||||
|
||||
if (p->connections) {
|
||||
while((c = pa_idxset_first(p->connections, NULL)))
|
||||
|
|
@ -489,6 +612,13 @@ void pa_protocol_simple_free(pa_protocol_simple *p) {
|
|||
|
||||
if (p->server)
|
||||
pa_socket_server_unref(p->server);
|
||||
|
||||
if (p->asyncmsgq) {
|
||||
c->mainloop->io_event_free(c->asyncmsgq_event);
|
||||
pa_asyncmsgq_after_poll(c->asyncmsgq);
|
||||
pa_asyncmsgq_free(p->asyncmsgq);
|
||||
}
|
||||
|
||||
pa_xfree(p);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -49,7 +49,6 @@
|
|||
#include <pulsecore/log.h>
|
||||
#include <pulsecore/core-scache.h>
|
||||
#include <pulsecore/creds.h>
|
||||
#include <pulsecore/mutex.h>
|
||||
#include <pulsecore/refcnt.h>
|
||||
|
||||
#include "pstream.h"
|
||||
|
|
@ -118,8 +117,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 +128,7 @@ struct pa_pstream {
|
|||
uint32_t shm_info[PA_PSTREAM_SHM_MAX];
|
||||
void *data;
|
||||
size_t index;
|
||||
pa_memchunk memchunk;
|
||||
} write;
|
||||
|
||||
struct {
|
||||
|
|
@ -173,8 +173,6 @@ static void do_something(pa_pstream *p) {
|
|||
|
||||
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,8 +186,6 @@ static void do_something(pa_pstream *p) {
|
|||
goto fail;
|
||||
}
|
||||
|
||||
pa_mutex_unlock(p->mutex);
|
||||
|
||||
pa_pstream_unref(p);
|
||||
return;
|
||||
|
||||
|
|
@ -200,8 +196,6 @@ fail:
|
|||
if (p->die_callback)
|
||||
p->die_callback(p, p->die_callback_userdata);
|
||||
|
||||
pa_mutex_unlock(p->mutex);
|
||||
|
||||
pa_pstream_unref(p);
|
||||
}
|
||||
|
||||
|
|
@ -214,16 +208,6 @@ static void io_callback(pa_iochannel*io, void *userdata) {
|
|||
do_something(p);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
do_something(p);
|
||||
}
|
||||
|
||||
static void memimport_release_cb(pa_memimport *i, uint32_t block_id, void *userdata);
|
||||
|
||||
pa_pstream *pa_pstream_new(pa_mainloop_api *m, pa_iochannel *io, pa_mempool *pool) {
|
||||
|
|
@ -239,17 +223,14 @@ 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;
|
||||
|
|
@ -312,8 +293,8 @@ static void pstream_free(pa_pstream *p) {
|
|||
if (p->read.packet)
|
||||
pa_packet_unref(p->read.packet);
|
||||
|
||||
if (p->mutex)
|
||||
pa_mutex_free(p->mutex);
|
||||
if (p->write.memchunk.memblock)
|
||||
pa_memblock_unref(p->write.memchunk.memblock);
|
||||
|
||||
pa_xfree(p);
|
||||
}
|
||||
|
|
@ -325,10 +306,8 @@ void pa_pstream_send_packet(pa_pstream*p, pa_packet *packet, const pa_creds *cre
|
|||
assert(PA_REFCNT_VALUE(p) > 0);
|
||||
assert(packet);
|
||||
|
||||
pa_mutex_lock(p->mutex);
|
||||
|
||||
if (p->dead)
|
||||
goto finish;
|
||||
return;
|
||||
|
||||
i = pa_xnew(struct item_info, 1);
|
||||
i->type = PA_PSTREAM_ITEM_PACKET;
|
||||
|
|
@ -340,11 +319,6 @@ 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);
|
||||
}
|
||||
|
||||
void pa_pstream_send_memblock(pa_pstream*p, uint32_t channel, int64_t offset, pa_seek_mode_t seek_mode, const pa_memchunk *chunk) {
|
||||
|
|
@ -355,12 +329,9 @@ void pa_pstream_send_memblock(pa_pstream*p, uint32_t channel, int64_t offset, pa
|
|||
assert(channel != (uint32_t) -1);
|
||||
assert(chunk);
|
||||
|
||||
pa_mutex_lock(p->mutex);
|
||||
|
||||
if (p->dead)
|
||||
goto finish;
|
||||
return;
|
||||
|
||||
length = chunk->length;
|
||||
idx = 0;
|
||||
|
||||
while (length > 0) {
|
||||
|
|
@ -389,10 +360,6 @@ 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) {
|
||||
|
|
@ -402,10 +369,8 @@ static void memimport_release_cb(pa_memimport *i, uint32_t block_id, void *userd
|
|||
assert(p);
|
||||
assert(PA_REFCNT_VALUE(p) > 0);
|
||||
|
||||
pa_mutex_lock(p->mutex);
|
||||
|
||||
if (p->dead)
|
||||
goto finish;
|
||||
return;
|
||||
|
||||
/* pa_log("Releasing block %u", block_id); */
|
||||
|
||||
|
|
@ -417,11 +382,6 @@ static void memimport_release_cb(pa_memimport *i, uint32_t block_id, void *userd
|
|||
#endif
|
||||
|
||||
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) {
|
||||
|
|
@ -431,11 +391,8 @@ static void memexport_revoke_cb(pa_memexport *e, uint32_t block_id, void *userda
|
|||
assert(p);
|
||||
assert(PA_REFCNT_VALUE(p) > 0);
|
||||
|
||||
pa_mutex_lock(p->mutex);
|
||||
|
||||
if (p->dead)
|
||||
goto finish;
|
||||
|
||||
return;
|
||||
/* pa_log("Revoking block %u", block_id); */
|
||||
|
||||
item = pa_xnew(struct item_info, 1);
|
||||
|
|
@ -446,22 +403,20 @@ static void memexport_revoke_cb(pa_memexport *e, uint32_t block_id, void *userda
|
|||
#endif
|
||||
|
||||
pa_queue_push(p->send_queue, item);
|
||||
p->mainloop->defer_enable(p->defer_event, 1);
|
||||
|
||||
finish:
|
||||
|
||||
pa_mutex_unlock(p->mutex);
|
||||
}
|
||||
|
||||
static void prepare_next_write_item(pa_pstream *p) {
|
||||
assert(p);
|
||||
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);
|
||||
|
|
@ -528,7 +483,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,6 +501,7 @@ 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);
|
||||
|
|
@ -558,9 +516,16 @@ 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);
|
||||
assert(p->write.data || p->write.memchunk.memblock);
|
||||
|
||||
d = (uint8_t*) p->write.data + p->write.index - PA_PSTREAM_DESCRIPTOR_SIZE;
|
||||
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*) d + p->write.index - PA_PSTREAM_DESCRIPTOR_SIZE;
|
||||
l = ntohl(p->write.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH]) - (p->write.index - PA_PSTREAM_DESCRIPTOR_SIZE);
|
||||
}
|
||||
|
||||
|
|
@ -570,14 +535,17 @@ static int do_write(pa_pstream *p) {
|
|||
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;
|
||||
|
||||
|
|
@ -591,13 +559,20 @@ static int do_write(pa_pstream *p) {
|
|||
}
|
||||
|
||||
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;
|
||||
|
||||
pa_memblock *release_memblock = NULL;
|
||||
assert(p);
|
||||
assert(PA_REFCNT_VALUE(p) > 0);
|
||||
|
||||
|
|
@ -605,8 +580,16 @@ static int do_read(pa_pstream *p) {
|
|||
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;
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
@ -615,15 +598,18 @@ static int do_read(pa_pstream *p) {
|
|||
int 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) {
|
||||
|
|
@ -704,7 +690,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.");
|
||||
|
|
@ -791,7 +777,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,52 +805,51 @@ 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_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_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_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_mutex_lock(p->mutex);
|
||||
p->recieve_memblock_callback = cb;
|
||||
p->recieve_memblock_callback_userdata = userdata;
|
||||
pa_mutex_unlock(p->mutex);
|
||||
}
|
||||
|
||||
int pa_pstream_is_pending(pa_pstream *p) {
|
||||
|
|
@ -873,15 +858,11 @@ int pa_pstream_is_pending(pa_pstream *p) {
|
|||
assert(p);
|
||||
assert(PA_REFCNT_VALUE(p) > 0);
|
||||
|
||||
pa_mutex_lock(p->mutex);
|
||||
|
||||
if (p->dead)
|
||||
b = 0;
|
||||
else
|
||||
b = p->write.current || !pa_queue_is_empty(p->send_queue);
|
||||
|
||||
pa_mutex_unlock(p->mutex);
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
|
|
@ -904,8 +885,6 @@ pa_pstream* pa_pstream_ref(pa_pstream*p) {
|
|||
void pa_pstream_close(pa_pstream *p) {
|
||||
assert(p);
|
||||
|
||||
pa_mutex_lock(p->mutex);
|
||||
|
||||
p->dead = 1;
|
||||
|
||||
if (p->import) {
|
||||
|
|
@ -923,25 +902,16 @@ void pa_pstream_close(pa_pstream *p) {
|
|||
p->io = NULL;
|
||||
}
|
||||
|
||||
if (p->defer_event) {
|
||||
p->mainloop->defer_free(p->defer_event);
|
||||
p->defer_event = NULL;
|
||||
}
|
||||
|
||||
p->die_callback = NULL;
|
||||
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);
|
||||
|
||||
p->use_shm = enable;
|
||||
|
||||
if (enable) {
|
||||
|
|
@ -956,6 +926,4 @@ void pa_pstream_use_shm(pa_pstream *p, int enable) {
|
|||
p->export = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
pa_mutex_unlock(p->mutex);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -53,8 +53,7 @@ struct pa_resampler {
|
|||
};
|
||||
|
||||
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;
|
||||
|
|
@ -226,14 +225,14 @@ static void libsamplerate_free(pa_resampler *r) {
|
|||
if (u->src_state)
|
||||
src_delete(u->src_state);
|
||||
|
||||
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);
|
||||
if (u->buf1.memblock)
|
||||
pa_memblock_unref(u->buf1.memblock);
|
||||
if (u->buf2.memblock)
|
||||
pa_memblock_unref(u->buf2.memblock);
|
||||
if (u->buf3.memblock)
|
||||
pa_memblock_unref(u->buf3.memblock);
|
||||
if (u->buf4.memblock)
|
||||
pa_memblock_unref(u->buf4.memblock);
|
||||
pa_xfree(u);
|
||||
}
|
||||
|
||||
|
|
@ -272,64 +271,80 @@ static void calc_map_table(pa_resampler *r) {
|
|||
}
|
||||
}
|
||||
|
||||
static float * convert_to_float(pa_resampler *r, void *input, unsigned n_frames) {
|
||||
static pa_memchunk* convert_to_float(pa_resampler *r, pa_memchunk *input) {
|
||||
struct impl_libsamplerate *u;
|
||||
unsigned n_samples;
|
||||
void *src, *dst;
|
||||
|
||||
assert(r);
|
||||
assert(input);
|
||||
assert(input->memblock);
|
||||
|
||||
assert(r->impl_data);
|
||||
u = r->impl_data;
|
||||
|
||||
/* Convert the incoming sample into floats and place them in buf1 */
|
||||
|
||||
if (!u->to_float32ne_func)
|
||||
if (!u->to_float32ne_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);
|
||||
if (!u->buf1.memblock || u->buf1_samples < n_samples) {
|
||||
if (u->buf1.memblock)
|
||||
pa_memblock_unref(u->buf1.memblock);
|
||||
|
||||
u->buf1_samples = n_samples;
|
||||
u->buf1_block = pa_memblock_new(r->mempool, sizeof(float) * n_samples);
|
||||
u->buf1 = u->buf1_block->data;
|
||||
u->buf1.memblock = pa_memblock_new(r->mempool, u->buf1.length = sizeof(float) * n_samples);
|
||||
u->buf1.index = 0;
|
||||
}
|
||||
|
||||
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(u->buf1.memblock);
|
||||
u->to_float32ne_func(n_samples, src, dst);
|
||||
pa_memblock_release(input->memblock);
|
||||
pa_memblock_release(u->buf1.memblock);
|
||||
|
||||
return u->buf1;
|
||||
u->buf1.length = sizeof(float) * n_samples;
|
||||
|
||||
return &u->buf1;
|
||||
}
|
||||
|
||||
static float *remap_channels(pa_resampler *r, float *input, unsigned n_frames) {
|
||||
static pa_memchunk *remap_channels(pa_resampler *r, pa_memchunk *input) {
|
||||
struct impl_libsamplerate *u;
|
||||
unsigned n_samples;
|
||||
unsigned n_samples, n_frames;
|
||||
int i_skip, o_skip;
|
||||
unsigned oc;
|
||||
float *src, *dst;
|
||||
|
||||
assert(r);
|
||||
assert(input);
|
||||
assert(input->memblock);
|
||||
|
||||
assert(r->impl_data);
|
||||
u = r->impl_data;
|
||||
|
||||
/* Remap channels and place the result int buf2 */
|
||||
|
||||
if (!u->map_required)
|
||||
if (!u->map_required || !input->length)
|
||||
return input;
|
||||
|
||||
n_samples = n_frames * r->o_ss.channels;
|
||||
n_samples = input->length / sizeof(float);
|
||||
n_frames = n_samples / r->o_ss.channels;
|
||||
|
||||
if (u->buf2_samples < n_samples) {
|
||||
if (u->buf2_block)
|
||||
pa_memblock_unref(u->buf2_block);
|
||||
if (!u->buf2.memblock || u->buf2_samples < n_samples) {
|
||||
if (u->buf2.memblock)
|
||||
pa_memblock_unref(u->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;
|
||||
u->buf2.memblock = pa_memblock_new(r->mempool, u->buf2.length = sizeof(float) * n_samples);
|
||||
u->buf2.index = 0;
|
||||
}
|
||||
|
||||
memset(u->buf2, 0, n_samples * sizeof(float));
|
||||
src = (float*) ((uint8_t*) pa_memblock_acquire(input->memblock) + input->index);
|
||||
dst = (float*) pa_memblock_acquire(u->buf2.memblock);
|
||||
|
||||
memset(dst, 0, n_samples * sizeof(float));
|
||||
|
||||
o_skip = sizeof(float) * r->o_ss.channels;
|
||||
i_skip = sizeof(float) * r->i_ss.channels;
|
||||
|
|
@ -340,49 +355,57 @@ static float *remap_channels(pa_resampler *r, float *input, unsigned n_frames) {
|
|||
|
||||
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,
|
||||
dst + oc, o_skip,
|
||||
dst + oc, o_skip,
|
||||
src + u->map_table[oc][i], i_skip,
|
||||
n_frames,
|
||||
&one, &one);
|
||||
}
|
||||
|
||||
return u->buf2;
|
||||
pa_memblock_release(input->memblock);
|
||||
pa_memblock_release(u->buf2.memblock);
|
||||
|
||||
u->buf2.length = n_frames * sizeof(float) * r->o_ss.channels;
|
||||
|
||||
return &u->buf2;
|
||||
}
|
||||
|
||||
static float *resample(pa_resampler *r, float *input, unsigned *n_frames) {
|
||||
static pa_memchunk *resample(pa_resampler *r, pa_memchunk *input) {
|
||||
struct impl_libsamplerate *u;
|
||||
SRC_DATA data;
|
||||
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;
|
||||
|
||||
/* Resample the data and place the result in buf3 */
|
||||
|
||||
if (!u->src_state)
|
||||
if (!u->src_state || !input->length)
|
||||
return input;
|
||||
|
||||
out_n_frames = (*n_frames*r->o_ss.rate/r->i_ss.rate)+1024;
|
||||
in_n_samples = input->length / sizeof(float);
|
||||
in_n_frames = in_n_samples * r->o_ss.channels;
|
||||
|
||||
out_n_frames = (in_n_frames*r->o_ss.rate/r->i_ss.rate)+1024;
|
||||
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);
|
||||
if (!u->buf3.memblock || u->buf3_samples < out_n_samples) {
|
||||
if (u->buf3.memblock)
|
||||
pa_memblock_unref(u->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;
|
||||
u->buf3.memblock = pa_memblock_new(r->mempool, u->buf3.length = sizeof(float) * out_n_samples);
|
||||
u->buf3.index = 0;
|
||||
}
|
||||
|
||||
data.data_in = input;
|
||||
data.input_frames = *n_frames;
|
||||
data.data_in = (float*) ((uint8_t*) pa_memblock_acquire(input->memblock) + input->index);
|
||||
data.input_frames = in_n_frames;
|
||||
|
||||
data.data_out = u->buf3;
|
||||
data.data_out = (float*) pa_memblock_acquire(u->buf3.memblock);
|
||||
data.output_frames = out_n_frames;
|
||||
|
||||
data.src_ratio = (double) r->o_ss.rate / r->i_ss.rate;
|
||||
|
|
@ -390,16 +413,20 @@ static float *resample(pa_resampler *r, float *input, unsigned *n_frames) {
|
|||
|
||||
ret = src_process(u->src_state, &data);
|
||||
assert(ret == 0);
|
||||
assert((unsigned) data.input_frames_used == *n_frames);
|
||||
assert((unsigned) data.input_frames_used == in_n_frames);
|
||||
|
||||
*n_frames = data.output_frames_gen;
|
||||
pa_memblock_release(input->memblock);
|
||||
pa_memblock_release(u->buf3.memblock);
|
||||
|
||||
return u->buf3;
|
||||
u->buf3.length = data.output_frames_gen * sizeof(float) * r->o_ss.channels;
|
||||
|
||||
return &u->buf3;
|
||||
}
|
||||
|
||||
static void *convert_from_float(pa_resampler *r, float *input, unsigned n_frames) {
|
||||
static pa_memchunk *convert_from_float(pa_resampler *r, pa_memchunk *input) {
|
||||
struct impl_libsamplerate *u;
|
||||
unsigned n_samples;
|
||||
unsigned n_samples, n_frames;
|
||||
void *src, *dst;
|
||||
|
||||
assert(r);
|
||||
assert(input);
|
||||
|
|
@ -408,30 +435,35 @@ static void *convert_from_float(pa_resampler *r, float *input, unsigned n_frames
|
|||
|
||||
/* Convert the data into the correct sample type and place the result in buf4 */
|
||||
|
||||
if (!u->from_float32ne_func)
|
||||
if (!u->from_float32ne_func || !input->length)
|
||||
return input;
|
||||
|
||||
n_frames = input->length / sizeof(float) / r->o_ss.channels;
|
||||
n_samples = n_frames * r->o_ss.channels;
|
||||
|
||||
if (u->buf4_samples < n_samples) {
|
||||
if (u->buf4_block)
|
||||
pa_memblock_unref(u->buf4_block);
|
||||
if (u->buf4.memblock)
|
||||
pa_memblock_unref(u->buf4.memblock);
|
||||
|
||||
u->buf4_samples = n_samples;
|
||||
u->buf4_block = pa_memblock_new(r->mempool, sizeof(float) * n_samples);
|
||||
u->buf4 = u->buf4_block->data;
|
||||
u->buf4.memblock = pa_memblock_new(r->mempool, u->buf4.length = r->o_fz * n_frames);
|
||||
u->buf4.index = 0;
|
||||
}
|
||||
|
||||
u->from_float32ne_func(n_samples, input, u->buf4);
|
||||
src = (uint8_t*) pa_memblock_acquire(input->memblock) + input->length;
|
||||
dst = pa_memblock_acquire(u->buf4.memblock);
|
||||
u->from_float32ne_func(n_samples, src, dst);
|
||||
pa_memblock_release(input->memblock);
|
||||
pa_memblock_release(u->buf4.memblock);
|
||||
|
||||
return u->buf4;
|
||||
u->buf4.length = r->o_fz * n_frames;
|
||||
|
||||
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;
|
||||
pa_memchunk *buf;
|
||||
|
||||
assert(r);
|
||||
assert(in);
|
||||
|
|
@ -443,55 +475,23 @@ static void libsamplerate_run(pa_resampler *r, const pa_memchunk *in, pa_memchun
|
|||
|
||||
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, (pa_memchunk*) in);
|
||||
buf = remap_channels(r, buf);
|
||||
buf = resample(r, buf);
|
||||
|
||||
buf = convert_to_float(r, input, n_frames);
|
||||
buf = remap_channels(r, buf, n_frames);
|
||||
buf = resample(r, buf, &n_frames);
|
||||
if (buf->length) {
|
||||
buf = convert_from_float(r, buf);
|
||||
*out = *buf;
|
||||
|
||||
if (n_frames) {
|
||||
output = convert_from_float(r, buf, n_frames);
|
||||
if (buf == in)
|
||||
pa_memblock_ref(buf->memblock);
|
||||
else
|
||||
pa_memchunk_reset(buf);
|
||||
} else
|
||||
pa_memchunk_reset(out);
|
||||
|
||||
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;
|
||||
pa_memblock_release(in->memblock);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
assert(out->memblock);
|
||||
}
|
||||
} else {
|
||||
out->memblock = NULL;
|
||||
out->index = out->length = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void libsamplerate_update_input_rate(pa_resampler *r, uint32_t rate) {
|
||||
|
|
@ -518,8 +518,10 @@ static int libsamplerate_init(pa_resampler *r) {
|
|||
|
||||
r->impl_data = u = pa_xnew(struct impl_libsamplerate, 1);
|
||||
|
||||
u->buf1 = u->buf2 = u->buf3 = u->buf4 = NULL;
|
||||
u->buf1_block = u->buf2_block = u->buf3_block = u->buf4_block = NULL;
|
||||
pa_memchunk_reset(&u->buf1);
|
||||
pa_memchunk_reset(&u->buf2);
|
||||
pa_memchunk_reset(&u->buf3);
|
||||
pa_memchunk_reset(&u->buf4);
|
||||
u->buf1_samples = u->buf2_samples = u->buf3_samples = u->buf4_samples = 0;
|
||||
|
||||
if (r->i_ss.format == PA_SAMPLE_FLOAT32NE)
|
||||
|
|
@ -580,6 +582,7 @@ static void trivial_run(pa_resampler *r, const pa_memchunk *in, pa_memchunk *out
|
|||
/* Do real resampling */
|
||||
size_t l;
|
||||
unsigned o_index;
|
||||
void *src, *dst;
|
||||
|
||||
/* The length of the new memory block rounded up */
|
||||
l = ((((n_frames+1) * r->o_ss.rate) / r->i_ss.rate) + 1) * fz;
|
||||
|
|
@ -587,6 +590,9 @@ static void trivial_run(pa_resampler *r, const pa_memchunk *in, pa_memchunk *out
|
|||
out->index = 0;
|
||||
out->memblock = pa_memblock_new(r->mempool, l);
|
||||
|
||||
src = (uint8_t*) pa_memblock_acquire(in->memblock) + in->index;
|
||||
dst = pa_memblock_acquire(out->memblock);
|
||||
|
||||
for (o_index = 0;; o_index++, u->o_counter++) {
|
||||
unsigned j;
|
||||
|
||||
|
|
@ -596,13 +602,16 @@ static void trivial_run(pa_resampler *r, const pa_memchunk *in, pa_memchunk *out
|
|||
if (j >= n_frames)
|
||||
break;
|
||||
|
||||
assert(o_index*fz < out->memblock->length);
|
||||
assert(o_index*fz < pa_memblock_get_length(out->memblock));
|
||||
|
||||
memcpy((uint8_t*) out->memblock->data + fz*o_index,
|
||||
(uint8_t*) in->memblock->data + in->index + fz*j, fz);
|
||||
memcpy((uint8_t*) dst + fz*o_index,
|
||||
(uint8_t*) src + fz*j, fz);
|
||||
|
||||
}
|
||||
|
||||
pa_memblock_release(in->memblock);
|
||||
pa_memblock_release(out->memblock);
|
||||
|
||||
out->length = o_index*fz;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -61,15 +61,27 @@ pa_memblock *pa_silence_memblock_new(pa_mempool *pool, const pa_sample_spec *spe
|
|||
}
|
||||
|
||||
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;
|
||||
|
||||
assert(b);
|
||||
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);
|
||||
assert(c);
|
||||
assert(c->memblock);
|
||||
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) {
|
||||
|
|
@ -98,26 +110,38 @@ void pa_silence_memory(void *p, size_t length, const pa_sample_spec *spec) {
|
|||
}
|
||||
|
||||
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) {
|
||||
|
||||
assert(streams && data && length && spec);
|
||||
pa_cvolume full_volume;
|
||||
size_t d = 0;
|
||||
unsigned k;
|
||||
|
||||
assert(streams);
|
||||
assert(data);
|
||||
assert(length);
|
||||
assert(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 +151,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));
|
||||
|
|
@ -155,17 +179,18 @@ size_t pa_mix(
|
|||
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 +200,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 = 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));
|
||||
|
|
@ -203,17 +228,18 @@ size_t pa_mix(
|
|||
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 +249,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));
|
||||
|
|
@ -251,17 +277,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 +298,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,17 +322,34 @@ 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));
|
||||
void pa_volume_memchunk(
|
||||
pa_memchunk*c,
|
||||
const pa_sample_spec *spec,
|
||||
const pa_cvolume *volume) {
|
||||
|
||||
void *ptr;
|
||||
|
||||
assert(c);
|
||||
assert(spec);
|
||||
assert(c->length % pa_frame_size(spec) == 0);
|
||||
assert(volume);
|
||||
|
||||
if (pa_cvolume_channels_equal_to(volume, PA_VOLUME_NORM))
|
||||
|
|
@ -316,6 +360,8 @@ 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;
|
||||
|
|
@ -326,7 +372,7 @@ void pa_volume_memchunk(pa_memchunk*c, const pa_sample_spec *spec, const pa_cvol
|
|||
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--) {
|
||||
for (channel = 0, d = (int16_t*) ((uint8_t*) ptr + c->index), n = c->length/sizeof(int16_t); n > 0; d++, n--) {
|
||||
int32_t t = (int32_t)(*d);
|
||||
|
||||
t = (int32_t) (t * linear[channel]);
|
||||
|
|
@ -351,7 +397,7 @@ void pa_volume_memchunk(pa_memchunk*c, const pa_sample_spec *spec, const pa_cvol
|
|||
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--) {
|
||||
for (channel = 0, d = (int16_t*) ((uint8_t*) ptr + c->index), n = c->length/sizeof(int16_t); n > 0; d++, n--) {
|
||||
int32_t t = (int32_t)(INT16_SWAP(*d));
|
||||
|
||||
t = (int32_t) (t * linear[channel]);
|
||||
|
|
@ -373,7 +419,7 @@ void pa_volume_memchunk(pa_memchunk*c, const pa_sample_spec *spec, const pa_cvol
|
|||
size_t n;
|
||||
unsigned channel = 0;
|
||||
|
||||
for (d = (uint8_t*) c->memblock->data + c->index, n = c->length; n > 0; d++, n--) {
|
||||
for (d = (uint8_t*) ptr + c->index, n = c->length; n > 0; d++, n--) {
|
||||
int32_t t = (int32_t) *d - 0x80;
|
||||
|
||||
t = (int32_t) (t * pa_sw_volume_to_linear(volume->values[channel]));
|
||||
|
|
@ -395,7 +441,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;
|
||||
|
||||
|
|
@ -418,5 +464,7 @@ void pa_volume_memchunk(pa_memchunk*c, const pa_sample_spec *spec, const pa_cvol
|
|||
pa_sample_format_to_string(spec->format));
|
||||
abort();
|
||||
}
|
||||
|
||||
pa_memblock_release(c->memblock);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
69
src/pulsecore/semaphore-posix.c
Normal file
69
src/pulsecore/semaphore-posix.c
Normal file
|
|
@ -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);
|
||||
}
|
||||
35
src/pulsecore/semaphore.h
Normal file
35
src/pulsecore/semaphore.h
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
#ifndef foopulsesemaphorehfoo
|
||||
#define foopulsesemaphorehfoo
|
||||
|
||||
/* $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.
|
||||
***/
|
||||
|
||||
typedef struct pa_semaphore pa_semaphore;
|
||||
|
||||
pa_semaphore* pa_semaphore_new(unsigned value);
|
||||
void pa_semaphore_free(pa_semaphore *m);
|
||||
|
||||
void pa_semaphore_post(pa_semaphore *m);
|
||||
void pa_semaphore_wait(pa_semaphore *m);
|
||||
|
||||
#endif
|
||||
|
|
@ -27,7 +27,6 @@
|
|||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
|
|
@ -46,41 +45,45 @@
|
|||
#define MOVE_BUFFER_LENGTH (1024*1024)
|
||||
#define SILENCE_BUFFER_LENGTH (64*1024)
|
||||
|
||||
#define CHECK_VALIDITY_RETURN_NULL(condition) \
|
||||
do {\
|
||||
if (!(condition)) \
|
||||
return NULL; \
|
||||
} while (0)
|
||||
static void sink_input_free(pa_msgobject *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, int mute) {
|
||||
pa_assert(data);
|
||||
|
||||
data->muted_is_set = 1;
|
||||
data->muted = !!mute;
|
||||
}
|
||||
|
||||
pa_sink_input* pa_sink_input_new(
|
||||
pa_core *core,
|
||||
pa_sink_input_new_data *data,
|
||||
|
|
@ -88,46 +91,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;
|
||||
|
||||
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_DISCONNECTED);
|
||||
|
||||
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,7 +145,7 @@ 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,
|
||||
|
|
@ -147,20 +156,31 @@ pa_sink_input* pa_sink_input_new(
|
|||
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;
|
||||
pa_atomic_load(&i->state, PA_SINK_INPUT_DRAINED);
|
||||
i->flags = flags;
|
||||
i->name = pa_xstrdup(data->name);
|
||||
i->driver = pa_xstrdup(data->driver);
|
||||
i->module = data->module;
|
||||
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->volume = data->volume;
|
||||
i->muted = data->muted;
|
||||
|
||||
i->process_msg = NULL;
|
||||
i->peek = NULL;
|
||||
i->drop = NULL;
|
||||
i->kill = NULL;
|
||||
|
|
@ -168,94 +188,87 @@ pa_sink_input* pa_sink_input_new(
|
|||
i->underrun = NULL;
|
||||
i->userdata = NULL;
|
||||
|
||||
i->move_silence = 0;
|
||||
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.soft_volume = i->volume;
|
||||
i->thread_info.soft_muted = i->muted;
|
||||
|
||||
pa_memchunk_reset(&i->resampled_chunk);
|
||||
i->resampler = resampler;
|
||||
i->resample_method = data->resample_method;
|
||||
i->silence_memblock = NULL;
|
||||
pa_assert_se(pa_idxset_put(core->sink_inputs, i, &i->index) == 0);
|
||||
pa_assert_se(pa_idxset_put(i->sink->inputs, i, NULL) == 0);
|
||||
|
||||
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",
|
||||
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);
|
||||
pa_assert(i);
|
||||
pa_return_if_fail(pa_sink_input_get_state(i) != PA_SINK_INPUT_DISCONNECTED);
|
||||
|
||||
pa_asyncmsgq_send(i->sink->asyncmsgq, i->sink, PA_SINK_MESSAGE_REMOVE_INPUT, i, NULL);
|
||||
|
||||
pa_idxset_remove_by_data(i->sink->core->sink_inputs, i, NULL);
|
||||
pa_idxset_remove_by_data(i->sink->inputs, i, NULL);
|
||||
|
||||
pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_REMOVE, i->index);
|
||||
i->sink = NULL;
|
||||
|
||||
i->process_msg = NULL;
|
||||
i->peek = NULL;
|
||||
i->drop = NULL;
|
||||
i->kill = NULL;
|
||||
i->get_latency = NULL;
|
||||
i->underrun = NULL;
|
||||
|
||||
i->state = PA_SINK_INPUT_DISCONNECTED;
|
||||
pa_atomic_load(&i->state, PA_SINK_INPUT_DISCONNECTED);
|
||||
}
|
||||
|
||||
static void sink_input_free(pa_sink_input* i) {
|
||||
assert(i);
|
||||
static void sink_input_free(pa_msgobject *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_sink_input_disconnect(i);
|
||||
|
||||
pa_log_info("freed %u \"%s\"", i->index, i->name);
|
||||
pa_log_info("Freeing output %u \"%s\"", i->index, i->name);
|
||||
|
||||
if (i->resampled_chunk.memblock)
|
||||
pa_memblock_unref(i->resampled_chunk.memblock);
|
||||
|
||||
if (i->resampler)
|
||||
pa_resampler_free(i->resampler);
|
||||
if (i->thread_info.resampler)
|
||||
pa_resampler_free(i->thread_info.resampler);
|
||||
|
||||
if (i->silence_memblock)
|
||||
pa_memblock_unref(i->silence_memblock);
|
||||
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);
|
||||
}
|
||||
i->thread_info.volume = i->volume;
|
||||
i->thread_info.muted = i->muted;
|
||||
|
||||
pa_sink_input* pa_sink_input_ref(pa_sink_input *i) {
|
||||
assert(i);
|
||||
assert(i->ref >= 1);
|
||||
pa_asyncmsgq_post(i->sink->asyncmsgq, i->sink, PA_SINK_MESSAGE_ADD_INPUT, i, NULL, pa_sink_unref, pa_sink_input_unref);
|
||||
pa_sink_update_status(i->sink);
|
||||
|
||||
i->ref++;
|
||||
return i;
|
||||
pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW, i->index);
|
||||
}
|
||||
|
||||
void pa_sink_input_kill(pa_sink_input*i) {
|
||||
assert(i);
|
||||
assert(i->ref >= 1);
|
||||
pa_sink_input_assert_ref(i);
|
||||
|
||||
if (i->kill)
|
||||
i->kill(i);
|
||||
|
|
@ -264,18 +277,14 @@ void pa_sink_input_kill(pa_sink_input*i) {
|
|||
pa_usec_t pa_sink_input_get_latency(pa_sink_input *i) {
|
||||
pa_usec_t r = 0;
|
||||
|
||||
assert(i);
|
||||
assert(i->ref >= 1);
|
||||
pa_sink_input_assert_ref(i);
|
||||
|
||||
if (pa_asyncmsgq_send(i->sink->asyncmsgq, i->sink, PA_SINK_INPUT_MESSAGE_GET_LATENCY, &r, 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;
|
||||
}
|
||||
|
||||
|
|
@ -283,35 +292,40 @@ int pa_sink_input_peek(pa_sink_input *i, pa_memchunk *chunk, pa_cvolume *volume)
|
|||
int ret = -1;
|
||||
int do_volume_adj_here;
|
||||
int volume_is_norm;
|
||||
pa_sink_input_state_t state;
|
||||
|
||||
pa_sink_input_assert_ref(i);
|
||||
pa_assert(chunk);
|
||||
pa_assert(volume);
|
||||
|
||||
assert(i);
|
||||
assert(i->ref >= 1);
|
||||
assert(chunk);
|
||||
assert(volume);
|
||||
state = pa_sink_input_get_state(i);
|
||||
|
||||
pa_sink_input_ref(i);
|
||||
if (state == PA_SINK_INPUT_DISCONNECTED)
|
||||
return -1;
|
||||
|
||||
if (!i->peek || !i->drop || i->state == PA_SINK_INPUT_CORKED)
|
||||
if (!i->peek || !i->drop || state == PA_SINK_INPUT_CORKED)
|
||||
goto finish;
|
||||
|
||||
assert(i->state == PA_SINK_INPUT_RUNNING || i->state == PA_SINK_INPUT_DRAINED);
|
||||
pa_assert(state == PA_SINK_INPUT_RUNNING || state == PA_SINK_INPUT_DRAINED);
|
||||
|
||||
if (i->move_silence > 0) {
|
||||
/* 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 */
|
||||
/* /\* 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, SILENCE_BUFFER_LENGTH); */
|
||||
|
||||
chunk->memblock = pa_memblock_ref(i->silence_memblock);
|
||||
chunk->index = 0;
|
||||
chunk->length = i->move_silence < chunk->memblock->length ? i->move_silence : chunk->memblock->length;
|
||||
/* chunk->memblock = pa_memblock_ref(i->thread_info.silence_memblock); */
|
||||
/* chunk->index = 0; */
|
||||
/* l = pa_memblock_get_length(chunk->memblock); */
|
||||
/* chunk->length = i->move_silence < l ? i->move_silence : l; */
|
||||
|
||||
ret = 0;
|
||||
do_volume_adj_here = 1;
|
||||
goto finish;
|
||||
}
|
||||
/* ret = 0; */
|
||||
/* do_volume_adj_here = 1; */
|
||||
/* goto finish; */
|
||||
/* } */
|
||||
|
||||
if (!i->resampler) {
|
||||
do_volume_adj_here = 0;
|
||||
|
|
@ -320,16 +334,16 @@ int pa_sink_input_peek(pa_sink_input *i, pa_memchunk *chunk, pa_cvolume *volume)
|
|||
}
|
||||
|
||||
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.soft_volume) && !i->thread_info.soft_muted;
|
||||
|
||||
while (!i->resampled_chunk.memblock) {
|
||||
while (!i->thread_info.resampled_chunk.memblock) {
|
||||
pa_memchunk tchunk;
|
||||
size_t l;
|
||||
|
||||
if ((ret = i->peek(i, &tchunk)) < 0)
|
||||
goto finish;
|
||||
|
||||
assert(tchunk.length);
|
||||
pa_assert(tchunk.length);
|
||||
|
||||
l = pa_resampler_request(i->resampler, CONVERT_BUFFER_LENGTH);
|
||||
|
||||
|
|
@ -342,30 +356,30 @@ int pa_sink_input_peek(pa_sink_input *i, pa_memchunk *chunk, pa_cvolume *volume)
|
|||
/* 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);
|
||||
pa_volume_memchunk(&tchunk, &i->sample_spec, &i->thread_info.soft_volume);
|
||||
}
|
||||
|
||||
pa_resampler_run(i->resampler, &tchunk, &i->resampled_chunk);
|
||||
pa_resampler_run(i->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);
|
||||
|
||||
*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)
|
||||
if (ret < 0 && state == PA_SINK_INPUT_RUNNING && i->underrun)
|
||||
i->underrun(i);
|
||||
|
||||
if (ret >= 0)
|
||||
i->state = PA_SINK_INPUT_RUNNING;
|
||||
pa_atomic_cmpxchg(&i->state, state, PA_SINK_INPUT_RUNNING);
|
||||
else if (ret < 0 && i->state == PA_SINK_INPUT_RUNNING)
|
||||
i->state = PA_SINK_INPUT_DRAINED;
|
||||
pa_atomic_cmpxchg(&i->state, state, PA_SINK_INPUT_DRAINED);
|
||||
|
||||
if (ret >= 0) {
|
||||
/* Let's see if we had to apply the volume adjustment
|
||||
|
|
@ -376,42 +390,42 @@ finish:
|
|||
pa_cvolume_reset(volume, i->sink->sample_spec.channels);
|
||||
else
|
||||
/* We've both the same channel map, so let's have the sink do the adjustment for us*/
|
||||
*volume = i->volume;
|
||||
*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);
|
||||
pa_sink_input_assert_ref(i);
|
||||
pa_assert(length > 0);
|
||||
|
||||
if (i->move_silence > 0) {
|
||||
/* if (i->move_silence > 0) { */
|
||||
|
||||
if (chunk) {
|
||||
/* if (chunk) { */
|
||||
/* size_t l; */
|
||||
|
||||
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;
|
||||
/* l = pa_memblock_get_length(i->silence_memblock); */
|
||||
|
||||
}
|
||||
/* if (chunk->memblock != i->silence_memblock || */
|
||||
/* chunk->index != 0 || */
|
||||
/* (chunk->memblock && (chunk->length != (l < i->move_silence ? l : i->move_silence)))) */
|
||||
/* return; */
|
||||
|
||||
assert(i->move_silence >= length);
|
||||
/* } */
|
||||
|
||||
i->move_silence -= length;
|
||||
/* pa_assert(i->move_silence >= length); */
|
||||
|
||||
if (i->move_silence <= 0) {
|
||||
assert(i->silence_memblock);
|
||||
pa_memblock_unref(i->silence_memblock);
|
||||
i->silence_memblock = NULL;
|
||||
}
|
||||
/* i->move_silence -= length; */
|
||||
|
||||
return;
|
||||
}
|
||||
/* if (i->move_silence <= 0) { */
|
||||
/* pa_assert(i->silence_memblock); */
|
||||
/* pa_memblock_unref(i->silence_memblock); */
|
||||
/* i->silence_memblock = NULL; */
|
||||
/* } */
|
||||
|
||||
/* return; */
|
||||
/* } */
|
||||
|
||||
if (!i->resampler) {
|
||||
if (i->drop)
|
||||
|
|
@ -419,75 +433,88 @@ void pa_sink_input_drop(pa_sink_input *i, const pa_memchunk *chunk, size_t lengt
|
|||
return;
|
||||
}
|
||||
|
||||
assert(i->resampled_chunk.memblock);
|
||||
assert(i->resampled_chunk.length >= length);
|
||||
pa_assert(i->thread_info.resampled_chunk.memblock);
|
||||
pa_assert(i->thread_info.resampled_chunk.length >= length);
|
||||
|
||||
i->resampled_chunk.index += length;
|
||||
i->resampled_chunk.length -= length;
|
||||
i->thread_info.resampled_chunk.index += length;
|
||||
i->thread_info.resampled_chunk.length -= length;
|
||||
|
||||
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;
|
||||
if (i->thread_info.resampled_chunk.length <= 0) {
|
||||
pa_memblock_unref(i->thread_info.resampled_chunk.memblock);
|
||||
i->thread_info.resampled_chunk.memblock = NULL;
|
||||
i->thread_info.resampled_chunk.index = i->thread_info.resampled_chunk.length = 0;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
if (pa_cvolume_equal(&i->volume, volume))
|
||||
return;
|
||||
|
||||
i->volume = *volume;
|
||||
|
||||
pa_asyncmsgq_post(s->asyncmsgq, pa_sink_input_ref(i), PA_SINK_INPUT_MESSAGE_SET_VOLUME, pa_xnewdup(struct pa_cvolume, volume, 1), pa_sink_input_unref, 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);
|
||||
|
||||
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, int mute) {
|
||||
pa_assert(i);
|
||||
pa_sink_input_assert_ref(i);
|
||||
|
||||
assert(i);
|
||||
assert(i->ref >= 1);
|
||||
|
||||
assert(i->state != PA_SINK_INPUT_DISCONNECTED);
|
||||
|
||||
n = i->state == PA_SINK_INPUT_CORKED && !b;
|
||||
|
||||
if (b)
|
||||
i->state = PA_SINK_INPUT_CORKED;
|
||||
else if (i->state == PA_SINK_INPUT_CORKED)
|
||||
i->state = PA_SINK_INPUT_DRAINED;
|
||||
|
||||
if (n)
|
||||
pa_sink_notify(i->sink);
|
||||
}
|
||||
|
||||
void pa_sink_input_set_rate(pa_sink_input *i, uint32_t rate) {
|
||||
assert(i);
|
||||
assert(i->resampler);
|
||||
assert(i->ref >= 1);
|
||||
|
||||
if (i->sample_spec.rate == rate)
|
||||
if (!i->muted == !mute)
|
||||
return;
|
||||
|
||||
i->sample_spec.rate = rate;
|
||||
pa_resampler_set_input_rate(i->resampler, rate);
|
||||
i->muted = mute;
|
||||
|
||||
pa_asyncmsgq_post(s->asyncmsgq, pa_sink_input_ref(i), PA_SINK_INPUT_MESSAGE_SET_MUTE, PA_UINT_TO_PTR(mute), pa_sink_input_unref, 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);
|
||||
|
||||
return !!i->mute;
|
||||
}
|
||||
|
||||
void pa_sink_input_cork(pa_sink_input *i, int b) {
|
||||
int n;
|
||||
pa_sink_input_state_t state;
|
||||
|
||||
pa_sink_input_assert_ref(i);
|
||||
|
||||
state = pa_sink_input_get_state(i);
|
||||
pa_assert(state != PA_SINK_INPUT_DISCONNECTED);
|
||||
|
||||
if (b && state != PA_SINK_INPUT_CORKED)
|
||||
pa_atomic_store(i->state, PA_SINK_INPUT_CORKED);
|
||||
else if (!b && state == PA_SINK_INPUT_CORKED)
|
||||
pa_atomic_cmpxchg(i->state, state, PA_SINK_INPUT_DRAINED);
|
||||
}
|
||||
|
||||
int pa_sink_input_set_rate(pa_sink_input *i, uint32_t rate) {
|
||||
pa_sink_input_assert_ref(i);
|
||||
pa_return_val_if_fail(u->thread_info.resampler, -1);
|
||||
|
||||
if (i->sample_spec.rate == rate)
|
||||
return 0;
|
||||
|
||||
i->sample_spec.rate = rate;
|
||||
|
||||
pa_asyncmsgq_post(s->asyncmsgq, pa_sink_input_ref(i), PA_SINK_INPUT_MESSAGE_SET_RATE, PA_UINT_TO_PTR(rate), NULL, pa_sink_input_unref, 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;
|
||||
|
|
@ -502,13 +529,9 @@ void pa_sink_input_set_name(pa_sink_input *i, const char *name) {
|
|||
}
|
||||
|
||||
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) {
|
||||
|
|
@ -516,156 +539,196 @@ int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, int immediately) {
|
|||
pa_memblockq *buffer = NULL;
|
||||
pa_sink *origin;
|
||||
|
||||
assert(i);
|
||||
assert(dest);
|
||||
pa_sink_input_assert_ref(i);
|
||||
pa_sink_assert_ref(dest);
|
||||
|
||||
origin = i->sink;
|
||||
return -1;
|
||||
|
||||
/* origin = i->sink; */
|
||||
|
||||
if (dest == origin)
|
||||
return 0;
|
||||
/* if (dest == origin) */
|
||||
/* return 0; */
|
||||
|
||||
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 (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 &&
|
||||
pa_sample_spec_equal(&origin->sample_spec, &dest->sample_spec) &&
|
||||
pa_channel_map_equal(&origin->channel_map, &dest->channel_map))
|
||||
/* if (i->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;
|
||||
/* /\* Try to reuse the old resampler if possible *\/ */
|
||||
/* new_resampler = i->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)) {
|
||||
/* 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)) { */
|
||||
|
||||
/* Okey, we need a new resampler for the new sink */
|
||||
/* /\* Okey, we need a new resampler for the new sink *\/ */
|
||||
|
||||
if (!(new_resampler = pa_resampler_new(
|
||||
dest->core->mempool,
|
||||
&i->sample_spec, &i->channel_map,
|
||||
&dest->sample_spec, &dest->channel_map,
|
||||
i->resample_method))) {
|
||||
pa_log_warn("Unsupported resampling operation.");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
/* if (!(new_resampler = pa_resampler_new( */
|
||||
/* dest->core->mempool, */
|
||||
/* &i->sample_spec, &i->channel_map, */
|
||||
/* &dest->sample_spec, &dest->channel_map, */
|
||||
/* i->resample_method))) { */
|
||||
/* pa_log_warn("Unsupported resampling operation."); */
|
||||
/* return -1; */
|
||||
/* } */
|
||||
/* } */
|
||||
|
||||
if (!immediately) {
|
||||
pa_usec_t old_latency, new_latency;
|
||||
pa_usec_t silence_usec = 0;
|
||||
/* 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);
|
||||
/* 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 */
|
||||
/* /\* Let's do a little bit of Voodoo for compensating latency */
|
||||
/* * differences *\/ */
|
||||
|
||||
old_latency = pa_sink_get_latency(origin);
|
||||
new_latency = pa_sink_get_latency(dest);
|
||||
/* old_latency = pa_sink_get_latency(origin); */
|
||||
/* new_latency = pa_sink_get_latency(dest); */
|
||||
|
||||
/* The already resampled data should go to the old sink */
|
||||
/* /\* The already resampled data should go to the old sink *\/ */
|
||||
|
||||
if (old_latency >= new_latency) {
|
||||
/* if (old_latency >= new_latency) { */
|
||||
|
||||
/* The latency of the old sink is larger than the latency
|
||||
* of the new sink. Therefore to compensate for the
|
||||
* difference we to play silence on the new one for a
|
||||
* while */
|
||||
/* /\* The latency of the old sink is larger than the latency */
|
||||
/* * of the new sink. Therefore to compensate for the */
|
||||
/* * difference we to play silence on the new one for a */
|
||||
/* * while *\/ */
|
||||
|
||||
silence_usec = old_latency - new_latency;
|
||||
/* silence_usec = old_latency - new_latency; */
|
||||
|
||||
} else {
|
||||
size_t l;
|
||||
int volume_is_norm;
|
||||
/* } 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
|
||||
* and make sure that this is still played on the old
|
||||
* sink, until we can play the first sample on the new
|
||||
* sink.*/
|
||||
/* /\* The latency of new sink is larger than the latency of */
|
||||
/* * the old sink. Therefore we have to precompute a little */
|
||||
/* * and make sure that this is still played on the old */
|
||||
/* * sink, until we can play the first sample on the new */
|
||||
/* * sink.*\/ */
|
||||
|
||||
l = pa_usec_to_bytes(new_latency - old_latency, &origin->sample_spec);
|
||||
/* l = pa_usec_to_bytes(new_latency - old_latency, &origin->sample_spec); */
|
||||
|
||||
volume_is_norm = pa_cvolume_is_norm(&i->volume);
|
||||
/* volume_is_norm = pa_cvolume_is_norm(&i->volume); */
|
||||
|
||||
while (l > 0) {
|
||||
pa_memchunk chunk;
|
||||
pa_cvolume volume;
|
||||
size_t n;
|
||||
/* while (l > 0) { */
|
||||
/* pa_memchunk chunk; */
|
||||
/* pa_cvolume volume; */
|
||||
/* size_t n; */
|
||||
|
||||
if (pa_sink_input_peek(i, &chunk, &volume) < 0)
|
||||
break;
|
||||
/* if (pa_sink_input_peek(i, &chunk, &volume) < 0) */
|
||||
/* break; */
|
||||
|
||||
n = chunk.length > l ? l : chunk.length;
|
||||
pa_sink_input_drop(i, &chunk, n);
|
||||
chunk.length = n;
|
||||
/* n = chunk.length > l ? l : chunk.length; */
|
||||
/* pa_sink_input_drop(i, &chunk, n); */
|
||||
/* chunk.length = n; */
|
||||
|
||||
if (!volume_is_norm) {
|
||||
pa_memchunk_make_writable(&chunk, 0);
|
||||
pa_volume_memchunk(&chunk, &origin->sample_spec, &volume);
|
||||
}
|
||||
/* 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;
|
||||
}
|
||||
/* if (pa_memblockq_push(buffer, &chunk) < 0) { */
|
||||
/* pa_memblock_unref(chunk.memblock); */
|
||||
/* break; */
|
||||
/* } */
|
||||
|
||||
pa_memblock_unref(chunk.memblock);
|
||||
l -= n;
|
||||
}
|
||||
}
|
||||
/* pa_memblock_unref(chunk.memblock); */
|
||||
/* l -= n; */
|
||||
/* } */
|
||||
/* } */
|
||||
|
||||
if (i->resampled_chunk.memblock) {
|
||||
/* 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 */
|
||||
/* /\* 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_memblockq_push(buffer, &i->resampled_chunk);
|
||||
silence_usec += pa_bytes_to_usec(i->resampled_chunk.length, &origin->sample_spec);
|
||||
}
|
||||
/* pa_memblockq_push(buffer, &i->resampled_chunk); */
|
||||
/* silence_usec += pa_bytes_to_usec(i->resampled_chunk.length, &origin->sample_spec); */
|
||||
/* } */
|
||||
|
||||
/* 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);
|
||||
}
|
||||
/* /\* 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); */
|
||||
/* } */
|
||||
|
||||
/* Okey, let's move it */
|
||||
pa_idxset_remove_by_data(origin->inputs, i, NULL);
|
||||
pa_idxset_put(dest->inputs, i, NULL);
|
||||
i->sink = dest;
|
||||
/* /\* Okey, let's move it *\/ */
|
||||
/* pa_idxset_remove_by_data(origin->inputs, i, NULL); */
|
||||
/* pa_idxset_put(dest->inputs, i, NULL); */
|
||||
/* i->sink = dest; */
|
||||
|
||||
/* Replace resampler */
|
||||
if (new_resampler != i->resampler) {
|
||||
if (i->resampler)
|
||||
pa_resampler_free(i->resampler);
|
||||
i->resampler = new_resampler;
|
||||
/* /\* Replace resampler *\/ */
|
||||
/* if (new_resampler != i->resampler) { */
|
||||
/* if (i->resampler) */
|
||||
/* pa_resampler_free(i->resampler); */
|
||||
/* i->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 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; */
|
||||
/* } */
|
||||
/* } */
|
||||
|
||||
/* 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;
|
||||
}
|
||||
/* /\* 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; */
|
||||
/* } */
|
||||
|
||||
/* Notify everyone */
|
||||
pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
|
||||
pa_sink_notify(i->sink);
|
||||
/* /\* 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);
|
||||
/* /\* Ok, now 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;
|
||||
/* return 0; */
|
||||
}
|
||||
|
||||
int pa_sink_input_process_msg(pa_msgobject *o, int code, void *userdata, pa_memchunk *chunk) {
|
||||
pa_sink_input *i = PA_SINK_INPUT(o);
|
||||
|
||||
pa_sink_input_assert_ref(i);
|
||||
|
||||
switch (code) {
|
||||
case PA_SINK_INPUT_MESSAGE_SET_VOLUME:
|
||||
s->thread_info.soft_volume = *((pa_cvolume*) userdata);
|
||||
return 0;
|
||||
|
||||
case PA_SINK_INPUT_MESSAGE_SET_MUTE:
|
||||
s->thread_info.soft_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->resampled_chunk.length, &i->sink->sample_spec);
|
||||
|
||||
/* if (i->move_silence) */
|
||||
/* r += pa_bytes_to_usec(i->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->resampler, PA_PTR_TO_UINT(userdata));
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
#ifndef foosinkinputhfoo
|
||||
#define foosinkinputhfoo
|
||||
#ifndef foopulsesinkinputhfoo
|
||||
#define foopulsesinkinputhfoo
|
||||
|
||||
/* $Id$ */
|
||||
|
||||
|
|
@ -51,9 +51,11 @@ typedef enum pa_sink_input_flags {
|
|||
} pa_sink_input_flags_t;
|
||||
|
||||
struct pa_sink_input {
|
||||
int ref;
|
||||
pa_msgobject parent;
|
||||
|
||||
uint32_t index;
|
||||
pa_sink_input_state_t state;
|
||||
pa_core *core;
|
||||
pa_atomic_t state;
|
||||
pa_sink_input_flags_t flags;
|
||||
|
||||
char *name, *driver; /* may be NULL */
|
||||
|
|
@ -64,27 +66,47 @@ struct pa_sink_input {
|
|||
|
||||
pa_sample_spec sample_spec;
|
||||
pa_channel_map channel_map;
|
||||
|
||||
pa_cvolume volume;
|
||||
int muted;
|
||||
|
||||
/* 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;
|
||||
|
||||
int (*process_msg)(pa_sink_input *i, int code, void *userdata);
|
||||
int (*peek) (pa_sink_input *i, pa_memchunk *chunk);
|
||||
void (*drop) (pa_sink_input *i, const pa_memchunk *chunk, size_t length);
|
||||
void (*kill) (pa_sink_input *i); /* may be NULL */
|
||||
pa_usec_t (*get_latency) (pa_sink_input *i); /* may be NULL */
|
||||
void (*underrun) (pa_sink_input *i); /* may be NULL */
|
||||
|
||||
void *userdata;
|
||||
|
||||
pa_memchunk resampled_chunk;
|
||||
pa_resampler *resampler; /* may be NULL */
|
||||
|
||||
pa_resample_method_t resample_method;
|
||||
|
||||
pa_memblock *silence_memblock; /* may be NULL */
|
||||
struct {
|
||||
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_cvolume volume;
|
||||
int muted;
|
||||
} thread_info;
|
||||
|
||||
void *userdata;
|
||||
};
|
||||
|
||||
PA_DECLARE_CLASS(pa_sink_input);
|
||||
#define PA_SINK_INPUT(o) ((pa_sink_input*) (o))
|
||||
|
||||
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_MAX
|
||||
};
|
||||
|
||||
typedef struct pa_sink_input_new_data {
|
||||
|
|
@ -100,6 +122,8 @@ typedef struct pa_sink_input_new_data {
|
|||
int channel_map_is_set;
|
||||
pa_cvolume volume;
|
||||
int volume_is_set;
|
||||
int muted;
|
||||
int muted_is_set;
|
||||
|
||||
pa_resample_method_t resample_method;
|
||||
} pa_sink_input_new_data;
|
||||
|
|
@ -108,37 +132,46 @@ 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, int 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);
|
||||
|
||||
/* To be called by the implementing module only */
|
||||
void pa_sink_input_put(pa_sink_input *i);
|
||||
void pa_sink_input_disconnect(pa_sink_input* i);
|
||||
|
||||
/* External code may request disconnection with this funcion */
|
||||
void pa_sink_input_set_name(pa_sink_input *i, const char *name);
|
||||
|
||||
/* 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, int 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_set_rate(pa_sink_input *i, uint32_t rate);
|
||||
|
||||
void pa_sink_input_set_name(pa_sink_input *i, const char *name);
|
||||
|
||||
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);
|
||||
|
||||
#define pa_sink_input_get_state(i) ((pa_sink_input_state_t) pa_atomic_load(&i->state))
|
||||
|
||||
/* To be used exclusively by the sink driver thread */
|
||||
|
||||
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);
|
||||
int pa_sink_input_process_msg(pa_msgobject *o, int code, void *userdata, pa_memchunk *chunk);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -41,16 +41,13 @@
|
|||
#include <pulsecore/sample-util.h>
|
||||
#include <pulsecore/core-subscribe.h>
|
||||
#include <pulsecore/log.h>
|
||||
#include <pulsecore/macro.h>
|
||||
|
||||
#include "sink.h"
|
||||
|
||||
#define MAX_MIX_CHANNELS 32
|
||||
|
||||
#define CHECK_VALIDITY_RETURN_NULL(condition) \
|
||||
do {\
|
||||
if (!(condition)) \
|
||||
return NULL; \
|
||||
} while (0)
|
||||
static void sink_free(pa_object *s);
|
||||
|
||||
pa_sink* pa_sink_new(
|
||||
pa_core *core,
|
||||
|
|
@ -66,60 +63,64 @@ pa_sink* pa_sink_new(
|
|||
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;
|
||||
pa_atomic_store(&s->state, PA_SINK_IDLE);
|
||||
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);
|
||||
|
||||
pa_cvolume_reset(&s->sw_volume, spec->channels);
|
||||
pa_cvolume_reset(&s->hw_volume, spec->channels);
|
||||
s->sw_muted = 0;
|
||||
s->hw_muted = 0;
|
||||
pa_cvolume_reset(&s->volume, spec->channels);
|
||||
s->muted = 0;
|
||||
s->refresh_volume = s->refresh_mute = 0;
|
||||
|
||||
s->is_hardware = 0;
|
||||
|
||||
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->start = NULL;
|
||||
s->stop = NULL;
|
||||
s->userdata = NULL;
|
||||
|
||||
pa_assert_se(s->asyncmsgq = pa_asyncmsgq_new(0));
|
||||
|
||||
r = pa_idxset_put(core->sinks, s, &s->index);
|
||||
assert(s->index != PA_IDXSET_INVALID && r >= 0);
|
||||
pa_assert(s->index != PA_IDXSET_INVALID && r >= 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);
|
||||
|
||||
|
|
@ -135,24 +136,64 @@ pa_sink* pa_sink_new(
|
|||
|
||||
pa_xfree(n);
|
||||
|
||||
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;
|
||||
|
||||
pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_SINK | PA_SUBSCRIPTION_EVENT_NEW, s->index);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
static void sink_start(pa_sink *s) {
|
||||
pa_sink_state_t state;
|
||||
pa_assert(s);
|
||||
|
||||
state = pa_sink_get_state(s);
|
||||
pa_return_if_fail(state == PA_SINK_IDLE || state == PA_SINK_SUSPENDED);
|
||||
|
||||
pa_atomic_store(&s->state, PA_SINK_RUNNING);
|
||||
|
||||
if (s->start)
|
||||
s->start(s);
|
||||
else
|
||||
pa_asyncmsgq_post(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_START, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
static void sink_stop(pa_sink *s) {
|
||||
pa_sink_state_t state;
|
||||
int stop;
|
||||
|
||||
pa_assert(s);
|
||||
state = pa_sink_get_state(s);
|
||||
pa_return_if_fail(state == PA_SINK_RUNNING || state == PA_SINK_SUSPENDED);
|
||||
|
||||
stop = state == PA_SINK_RUNNING;
|
||||
pa_atomic_store(&s->state, PA_SINK_IDLE);
|
||||
|
||||
if (stop) {
|
||||
if (s->stop)
|
||||
s->stop(s);
|
||||
else
|
||||
pa_asyncmsgq_post(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_STOP, NULL, NULL, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void pa_sink_disconnect(pa_sink* s) {
|
||||
pa_sink_input *i, *j = NULL;
|
||||
|
||||
assert(s);
|
||||
assert(s->state == PA_SINK_RUNNING);
|
||||
pa_assert(s);
|
||||
pa_return_if_fail(pa_sink_get_state(s) != PA_SINK_DISCONNECTED);
|
||||
|
||||
s->state = PA_SINK_DISCONNECTED;
|
||||
sink_stop(s);
|
||||
|
||||
pa_atomic_store(&s->state, PA_SINK_DISCONNECTED);
|
||||
pa_namereg_unregister(s->core, s->name);
|
||||
|
||||
pa_hook_fire(&s->core->hook_sink_disconnect, s);
|
||||
|
||||
while ((i = pa_idxset_first(s->inputs, NULL))) {
|
||||
assert(i != j);
|
||||
pa_assert(i != j);
|
||||
pa_sink_input_kill(i);
|
||||
j = i;
|
||||
}
|
||||
|
|
@ -163,23 +204,25 @@ void pa_sink_disconnect(pa_sink* s) {
|
|||
pa_idxset_remove_by_data(s->core->sinks, s, NULL);
|
||||
|
||||
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->start = NULL;
|
||||
s->stop = NULL;
|
||||
|
||||
pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK | PA_SUBSCRIPTION_EVENT_REMOVE, s->index);
|
||||
}
|
||||
|
||||
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_assert(s);
|
||||
pa_assert(pa_sink_refcnt(s) == 0);
|
||||
|
||||
if (s->state != PA_SINK_DISCONNECTED)
|
||||
pa_sink_disconnect(s);
|
||||
pa_sink_disconnect(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);
|
||||
|
|
@ -187,47 +230,66 @@ static void sink_free(pa_sink *s) {
|
|||
}
|
||||
|
||||
pa_idxset_free(s->inputs, NULL, NULL);
|
||||
|
||||
pa_hashmap_free(s->thread_info.inputs, (pa_free2_cb_t) pa_sink_input_unref, NULL);
|
||||
|
||||
pa_asyncmsgq_free(s->asyncmsgq);
|
||||
|
||||
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_update_status(pa_sink*s) {
|
||||
pa_sink_assert_ref(s);
|
||||
|
||||
if (!(--s->ref))
|
||||
sink_free(s);
|
||||
if (pa_sink_get_state(s) == PA_SINK_SUSPENDED)
|
||||
return;
|
||||
|
||||
if (pa_sink_used_by(s) > 0)
|
||||
sink_start(s);
|
||||
else
|
||||
sink_stop(s);
|
||||
}
|
||||
|
||||
pa_sink* pa_sink_ref(pa_sink *s) {
|
||||
assert(s);
|
||||
assert(s->ref >= 1);
|
||||
void pa_sink_suspend(pa_sink *s, int suspend) {
|
||||
pa_sink_state_t state;
|
||||
|
||||
s->ref++;
|
||||
return s;
|
||||
}
|
||||
pa_sink_assert_ref(s);
|
||||
|
||||
void pa_sink_notify(pa_sink*s) {
|
||||
assert(s);
|
||||
assert(s->ref >= 1);
|
||||
state = pa_sink_get_state(s);
|
||||
pa_return_if_fail(suspend && (state == PA_SINK_RUNNING || state == PA_SINK_IDLE));
|
||||
pa_return_if_fail(!suspend && (state == PA_SINK_SUSPENDED));
|
||||
|
||||
if (s->notify)
|
||||
s->notify(s);
|
||||
|
||||
if (suspend) {
|
||||
pa_atomic_store(&s->state, PA_SINK_SUSPENDED);
|
||||
|
||||
if (s->stop)
|
||||
s->stop(s);
|
||||
else
|
||||
pa_asyncmsgq_post(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_STOP, NULL, NULL, NULL);
|
||||
|
||||
} else {
|
||||
pa_atomic_store(&s->state, PA_SINK_RUNNING);
|
||||
|
||||
if (s->start)
|
||||
s->start(s);
|
||||
else
|
||||
pa_asyncmsgq_post(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_START, NULL, NULL, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned fill_mix_info(pa_sink *s, pa_mix_info *info, unsigned maxinfo) {
|
||||
uint32_t idx = PA_IDXSET_INVALID;
|
||||
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)) {
|
||||
while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL))) {
|
||||
/* Increase ref counter, to make sure that this input doesn't
|
||||
* vanish while we still need it */
|
||||
pa_sink_input_ref(i);
|
||||
|
|
@ -239,9 +301,8 @@ static unsigned fill_mix_info(pa_sink *s, pa_mix_info *info, unsigned maxinfo) {
|
|||
|
||||
info->userdata = i;
|
||||
|
||||
assert(info->chunk.memblock);
|
||||
assert(info->chunk.memblock->data);
|
||||
assert(info->chunk.length);
|
||||
pa_assert(info->chunk.memblock);
|
||||
pa_assert(info->chunk.length);
|
||||
|
||||
info++;
|
||||
maxinfo--;
|
||||
|
|
@ -252,15 +313,14 @@ static unsigned fill_mix_info(pa_sink *s, pa_mix_info *info, unsigned maxinfo) {
|
|||
}
|
||||
|
||||
static void inputs_drop(pa_sink *s, pa_mix_info *info, unsigned maxinfo, size_t length) {
|
||||
assert(s);
|
||||
assert(s->ref >= 1);
|
||||
assert(info);
|
||||
pa_sink_assert_ref(s);
|
||||
pa_assert(info);
|
||||
|
||||
for (; maxinfo > 0; maxinfo--, info++) {
|
||||
pa_sink_input *i = info->userdata;
|
||||
|
||||
assert(i);
|
||||
assert(info->chunk.memblock);
|
||||
pa_assert(i);
|
||||
pa_assert(info->chunk.memblock);
|
||||
|
||||
/* Drop read data */
|
||||
pa_sink_input_drop(i, &info->chunk, length);
|
||||
|
|
@ -277,10 +337,9 @@ int pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) {
|
|||
unsigned n;
|
||||
int r = -1;
|
||||
|
||||
assert(s);
|
||||
assert(s->ref >= 1);
|
||||
assert(length);
|
||||
assert(result);
|
||||
pa_sink_assert_ref(s);
|
||||
pa_assert(length);
|
||||
pa_assert(result);
|
||||
|
||||
pa_sink_ref(s);
|
||||
|
||||
|
|
@ -298,23 +357,23 @@ 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;
|
||||
}
|
||||
|
||||
|
|
@ -336,12 +395,10 @@ int pa_sink_render_into(pa_sink*s, pa_memchunk *target) {
|
|||
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(target);
|
||||
pa_assert(target->memblock);
|
||||
pa_assert(target->length);
|
||||
|
||||
pa_sink_ref(s);
|
||||
|
||||
|
|
@ -350,30 +407,48 @@ int pa_sink_render_into(pa_sink*s, pa_memchunk *target) {
|
|||
if (n <= 0)
|
||||
goto finish;
|
||||
|
||||
if (n == 1) {
|
||||
pa_cvolume volume;
|
||||
|
||||
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);
|
||||
|
||||
pa_sw_cvolume_multiply(&volume, &s->sw_volume, &info[0].volume);
|
||||
|
||||
if (s->sw_muted)
|
||||
if (s->thread_info.soft_muted)
|
||||
pa_silence_memchunk(target, &s->sample_spec);
|
||||
else if (!pa_cvolume_is_norm(&volume))
|
||||
pa_volume_memchunk(target, &s->sample_spec, &volume);
|
||||
} else
|
||||
else {
|
||||
void *src, *ptr;
|
||||
pa_cvolume 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);
|
||||
|
||||
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);
|
||||
|
||||
pa_memblock_release(target->memblock);
|
||||
}
|
||||
|
||||
inputs_drop(s, info, n, target->length);
|
||||
|
||||
if (s->monitor_source)
|
||||
|
|
@ -391,12 +466,10 @@ 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(target);
|
||||
pa_assert(target->memblock);
|
||||
pa_assert(target->length);
|
||||
|
||||
pa_sink_ref(s);
|
||||
|
||||
|
|
@ -425,10 +498,9 @@ void pa_sink_render_into_full(pa_sink *s, pa_memchunk *target) {
|
|||
}
|
||||
|
||||
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(length);
|
||||
pa_assert(result);
|
||||
|
||||
/*** This needs optimization ***/
|
||||
|
||||
|
|
@ -439,108 +511,109 @@ void pa_sink_render_full(pa_sink *s, size_t length, pa_memchunk *result) {
|
|||
}
|
||||
|
||||
pa_usec_t pa_sink_get_latency(pa_sink *s) {
|
||||
assert(s);
|
||||
assert(s->ref >= 1);
|
||||
pa_usec_t usec = 0;
|
||||
|
||||
pa_sink_assert_ref(s);
|
||||
|
||||
if (!s->get_latency)
|
||||
if (s->get_latency)
|
||||
return s->get_latency(s);
|
||||
|
||||
if (pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_LATENCY, &usec, 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)
|
||||
pa_sink_assert_ref(s);
|
||||
pa_assert(volume);
|
||||
|
||||
changed = !pa_cvolume_equal(volume, &s->volume);
|
||||
s->volume = *volume;
|
||||
|
||||
if (s->set_volume && s->set_volume(s) < 0)
|
||||
s->set_volume = NULL;
|
||||
|
||||
if (!s->set_volume)
|
||||
pa_asyncmsgq_post(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_VOLUME, pa_xnewdup(struct pa_cvolume, volume, 1), NULL, pa_xfree);
|
||||
|
||||
if (changed)
|
||||
pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
|
||||
}
|
||||
|
||||
const pa_cvolume *pa_sink_get_volume(pa_sink *s) {
|
||||
struct pa_cvolume old_volume;
|
||||
|
||||
pa_sink_assert_ref(s);
|
||||
|
||||
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_SINK_MESSAGE_GET_VOLUME, &s->volume, NULL);
|
||||
|
||||
if (!pa_cvolume_equal(&old_volume, &s->volume))
|
||||
pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
|
||||
|
||||
return &s->volume;
|
||||
}
|
||||
|
||||
void pa_sink_set_mute(pa_sink *s, int mute) {
|
||||
int changed;
|
||||
|
||||
pa_sink_assert_ref(s);
|
||||
|
||||
changed = s->muted != mute;
|
||||
|
||||
if (s->set_mute && s->set_mute(s) < 0)
|
||||
s->set_mute = NULL;
|
||||
|
||||
if (!s->set_mute)
|
||||
pa_asyncmsgq_post(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_MUTE, PA_UINT_TO_PTR(mute), NULL, NULL);
|
||||
|
||||
if (changed)
|
||||
pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
|
||||
}
|
||||
|
||||
int pa_sink_get_mute(pa_sink *s) {
|
||||
int old_muted;
|
||||
|
||||
pa_sink_assert_ref(s);
|
||||
|
||||
old_muted = s->muted;
|
||||
|
||||
if (s->get_mute && s->get_mute(s) < 0)
|
||||
s->get_mute = NULL;
|
||||
|
||||
if (!s->get_mute && s->refresh_mute)
|
||||
pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_MUTE, &s->muted, NULL);
|
||||
|
||||
if (old_muted != s->muted)
|
||||
pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
|
||||
|
||||
return s->muted;
|
||||
}
|
||||
|
||||
void pa_sink_set_module(pa_sink *s, pa_module *m) {
|
||||
pa_sink_assert_ref(s);
|
||||
|
||||
if (s->module == m)
|
||||
return;
|
||||
|
||||
s->owner = m;
|
||||
s->module = m;
|
||||
|
||||
if (s->monitor_source)
|
||||
pa_source_set_owner(s->monitor_source, m);
|
||||
pa_source_set_module(s->monitor_source, m);
|
||||
|
||||
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;
|
||||
|
||||
assert(s);
|
||||
assert(s->ref >= 1);
|
||||
assert(volume);
|
||||
|
||||
if (m == PA_MIXER_HARDWARE && s->set_hw_volume)
|
||||
v = &s->hw_volume;
|
||||
else
|
||||
v = &s->sw_volume;
|
||||
|
||||
if (pa_cvolume_equal(v, volume))
|
||||
return;
|
||||
|
||||
*v = *volume;
|
||||
|
||||
if (v == &s->hw_volume)
|
||||
if (s->set_hw_volume(s) < 0)
|
||||
s->sw_volume = *volume;
|
||||
|
||||
pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
|
||||
}
|
||||
|
||||
const pa_cvolume *pa_sink_get_volume(pa_sink *s, pa_mixer_t m) {
|
||||
assert(s);
|
||||
assert(s->ref >= 1);
|
||||
|
||||
if (m == PA_MIXER_HARDWARE && s->set_hw_volume) {
|
||||
|
||||
if (s->get_hw_volume)
|
||||
s->get_hw_volume(s);
|
||||
|
||||
return &s->hw_volume;
|
||||
} else
|
||||
return &s->sw_volume;
|
||||
}
|
||||
|
||||
void pa_sink_set_mute(pa_sink *s, pa_mixer_t m, int mute) {
|
||||
int *t;
|
||||
|
||||
assert(s);
|
||||
assert(s->ref >= 1);
|
||||
|
||||
if (m == PA_MIXER_HARDWARE && s->set_hw_mute)
|
||||
t = &s->hw_muted;
|
||||
else
|
||||
t = &s->sw_muted;
|
||||
|
||||
if (!!*t == !!mute)
|
||||
return;
|
||||
|
||||
*t = !!mute;
|
||||
|
||||
if (t == &s->hw_muted)
|
||||
if (s->set_hw_mute(s) < 0)
|
||||
s->sw_muted = !!mute;
|
||||
|
||||
pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
|
||||
}
|
||||
|
||||
int pa_sink_get_mute(pa_sink *s, pa_mixer_t m) {
|
||||
assert(s);
|
||||
assert(s->ref >= 1);
|
||||
|
||||
if (m == PA_MIXER_HARDWARE && s->set_hw_mute) {
|
||||
|
||||
if (s->get_hw_mute)
|
||||
s->get_hw_mute(s);
|
||||
|
||||
return s->hw_muted;
|
||||
} else
|
||||
return s->sw_muted;
|
||||
}
|
||||
|
||||
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;
|
||||
|
|
@ -565,8 +638,7 @@ void pa_sink_set_description(pa_sink *s, const char *description) {
|
|||
unsigned pa_sink_used_by(pa_sink *s) {
|
||||
unsigned ret;
|
||||
|
||||
assert(s);
|
||||
assert(s->ref >= 1);
|
||||
pa_sink_assert_ref(s);
|
||||
|
||||
ret = pa_idxset_size(s->inputs);
|
||||
|
||||
|
|
@ -575,3 +647,41 @@ unsigned pa_sink_used_by(pa_sink *s) {
|
|||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, pa_memchunk *chunk) {
|
||||
pa_sink *s = PA_SINK(o);
|
||||
pa_sink_assert_ref(s);
|
||||
|
||||
switch (code) {
|
||||
case PA_SINK_MESSAGE_ADD_INPUT: {
|
||||
pa_sink_input *i = userdata;
|
||||
pa_hashmap_put(s->thread_info.inputs, PA_UINT32_TO_PTR(i->index), pa_sink_input_ref(i));
|
||||
return 0;
|
||||
}
|
||||
|
||||
case PA_SINK_MESSAGE_REMOVE_INPUT: {
|
||||
pa_sink_input *i = userdata;
|
||||
pa_hashmap_remove(s->thread_info.inputs, PA_UINT32_TO_PTR(i->index));
|
||||
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:
|
||||
*((int*) userdata) = s->thread_info.soft_muted;
|
||||
return 0;
|
||||
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
#ifndef foosinkhfoo
|
||||
#define foosinkhfoo
|
||||
#ifndef foopulsesinkhfoo
|
||||
#define foopulsesinkhfoo
|
||||
|
||||
/* $Id$ */
|
||||
|
||||
|
|
@ -25,37 +25,43 @@
|
|||
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>
|
||||
|
||||
#define PA_MAX_INPUTS_PER_SINK 32
|
||||
|
||||
typedef enum pa_sink_state {
|
||||
PA_SINK_RUNNING,
|
||||
PA_SINK_SUSPENDED,
|
||||
PA_SINK_IDLE,
|
||||
PA_SINK_DISCONNECTED
|
||||
} pa_sink_state_t;
|
||||
|
||||
struct pa_sink {
|
||||
int ref;
|
||||
pa_msgobject parent;
|
||||
|
||||
uint32_t index;
|
||||
pa_core *core;
|
||||
pa_sink_state_t state;
|
||||
pa_atomic_t state;
|
||||
|
||||
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;
|
||||
|
|
@ -63,49 +69,85 @@ struct pa_sink {
|
|||
pa_idxset *inputs;
|
||||
pa_source *monitor_source; /* may be NULL */
|
||||
|
||||
pa_cvolume hw_volume, sw_volume;
|
||||
int hw_muted, sw_muted;
|
||||
pa_cvolume volume;
|
||||
int muted;
|
||||
int refresh_volume;
|
||||
int refresh_mute;
|
||||
|
||||
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 */
|
||||
int (*start)(pa_sink *s);
|
||||
int (*stop)(pa_sink *s);
|
||||
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;
|
||||
|
||||
/* Contains copies of the above data so that the real-time worker
|
||||
* thread can work without access locking */
|
||||
struct {
|
||||
pa_hashmap *inputs;
|
||||
pa_cvolume soft_volume;
|
||||
int soft_muted;
|
||||
} thread_info;
|
||||
|
||||
void *userdata;
|
||||
};
|
||||
|
||||
PA_DECLARE_CLASS(pa_sink);
|
||||
#define PA_SINK(s) ((pa_sink*) (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_START,
|
||||
PA_SINK_MESSAGE_STOP,
|
||||
PA_SINK_MESSAGE_MAX
|
||||
} pa_sink_message_t;
|
||||
|
||||
/* To be used exclusively by the sink driver */
|
||||
|
||||
pa_sink* pa_sink_new(
|
||||
pa_core *core,
|
||||
const char *driver,
|
||||
const char *name,
|
||||
int namereg_fail,
|
||||
const pa_sample_spec *spec,
|
||||
const pa_channel_map *map);
|
||||
pa_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);
|
||||
|
||||
void pa_sink_set_module(pa_sink *sink, pa_module *m);
|
||||
void pa_sink_set_description(pa_sink *s, const char *description);
|
||||
|
||||
/* Usable by everyone */
|
||||
|
||||
pa_usec_t pa_sink_get_latency(pa_sink *s);
|
||||
|
||||
void pa_sink_update_status(pa_sink*s);
|
||||
void pa_sink_suspend(pa_sink *s, int suspend);
|
||||
|
||||
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, int mute);
|
||||
int pa_sink_get_mute(pa_sink *sink);
|
||||
|
||||
unsigned pa_sink_used_by(pa_sink *s);
|
||||
#define pa_sink_get_state(s) ((pa_sink_state_t) pa_atomic_load(&(s)->state))
|
||||
|
||||
/* To be used exclusively by the sink driver thread */
|
||||
|
||||
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_usec_t pa_sink_get_latency(pa_sink *s);
|
||||
|
||||
void pa_sink_notify(pa_sink*s);
|
||||
|
||||
void pa_sink_set_owner(pa_sink *sink, pa_module *m);
|
||||
|
||||
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_description(pa_sink *s, const char *description);
|
||||
|
||||
unsigned pa_sink_used_by(pa_sink *s);
|
||||
int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, pa_memchunk *chunk);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -76,21 +76,25 @@ static int sink_input_peek(pa_sink_input *i, pa_memchunk *chunk) {
|
|||
if (!u->memchunk.memblock) {
|
||||
uint32_t fs = pa_frame_size(&i->sample_spec);
|
||||
sf_count_t n;
|
||||
void *p;
|
||||
|
||||
u->memchunk.memblock = pa_memblock_new(i->sink->core->mempool, BUF_SIZE);
|
||||
u->memchunk.index = 0;
|
||||
|
||||
p = pa_memblock_acquire(u->memchunk.memblock);
|
||||
|
||||
if (u->readf_function) {
|
||||
if ((n = u->readf_function(u->sndfile, u->memchunk.memblock->data, BUF_SIZE/fs)) <= 0)
|
||||
if ((n = u->readf_function(u->sndfile, p, BUF_SIZE/fs)) <= 0)
|
||||
n = 0;
|
||||
|
||||
u->memchunk.length = n * fs;
|
||||
} else {
|
||||
if ((n = sf_read_raw(u->sndfile, u->memchunk.memblock->data, BUF_SIZE)) <= 0)
|
||||
if ((n = sf_read_raw(u->sndfile, p, BUF_SIZE)) <= 0)
|
||||
n = 0;
|
||||
|
||||
u->memchunk.length = n;
|
||||
}
|
||||
pa_memblock_release(u->memchunk.memblock);
|
||||
|
||||
if (!u->memchunk.length) {
|
||||
free_userdata(u);
|
||||
|
|
|
|||
|
|
@ -42,7 +42,11 @@ int pa_sound_file_load(pa_mempool *pool, const char *fname, pa_sample_spec *ss,
|
|||
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;
|
||||
|
||||
assert(fname);
|
||||
assert(ss);
|
||||
assert(chunk);
|
||||
|
||||
chunk->memblock = NULL;
|
||||
chunk->index = chunk->length = 0;
|
||||
|
|
@ -99,8 +103,10 @@ int pa_sound_file_load(pa_mempool *pool, const char *fname, pa_sample_spec *ss,
|
|||
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) != l)) {
|
||||
pa_log("Premature file end");
|
||||
goto finish;
|
||||
}
|
||||
|
|
@ -112,6 +118,9 @@ finish:
|
|||
if (sf)
|
||||
sf_close(sf);
|
||||
|
||||
if (ptr)
|
||||
pa_memblock_release(chunk->memblock);
|
||||
|
||||
if (ret != 0 && chunk->memblock)
|
||||
pa_memblock_unref(chunk->memblock);
|
||||
|
||||
|
|
|
|||
|
|
@ -26,7 +26,6 @@
|
|||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
|
|
@ -39,14 +38,8 @@
|
|||
|
||||
#include "source-output.h"
|
||||
|
||||
#define CHECK_VALIDITY_RETURN_NULL(condition) \
|
||||
do {\
|
||||
if (!(condition)) \
|
||||
return NULL; \
|
||||
} while (0)
|
||||
|
||||
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 +47,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,48 +67,53 @@ 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;
|
||||
|
||||
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_DISCONNECTED);
|
||||
|
||||
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,
|
||||
|
|
@ -125,115 +123,133 @@ pa_source_output* pa_source_output_new(
|
|||
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_source_output_new(pa_source_output);
|
||||
|
||||
o->parent.parent.free = source_output_free;
|
||||
o->parent.process_msg = pa_source_output_process_msg;
|
||||
|
||||
o->core = core;
|
||||
pa_atomic_load(&o->state, PA_SOURCE_OUTPUT_RUNNING);
|
||||
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->process_msg = NULL;
|
||||
o->push = NULL;
|
||||
o->kill = NULL;
|
||||
o->get_latency = NULL;
|
||||
o->userdata = NULL;
|
||||
|
||||
o->resampler = resampler;
|
||||
o->resample_method = data->resample_method;
|
||||
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, 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);
|
||||
pa_assert(o);
|
||||
pa_return_if_fail(pa_source_output_get_state(i) != PA_SOURCE_OUTPUT_DISCONNECTED);
|
||||
pa_assert(o->source);
|
||||
pa_assert(o->source->core);
|
||||
|
||||
pa_asyncmsgq_send(i->sink->asyncmsgq, i->sink, PA_SOURCE_MESSAGE_REMOVE_OUTPUT, o, NULL);
|
||||
|
||||
pa_idxset_remove_by_data(o->source->core->source_outputs, o, NULL);
|
||||
pa_idxset_remove_by_data(o->source->outputs, o, NULL);
|
||||
|
||||
pa_subscription_post(o->source->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_REMOVE, o->index);
|
||||
o->source = NULL;
|
||||
|
||||
o->process_msg = NULL;
|
||||
o->push = NULL;
|
||||
o->kill = NULL;
|
||||
o->get_latency = NULL;
|
||||
|
||||
o->state = PA_SOURCE_OUTPUT_DISCONNECTED;
|
||||
pa_atomic_load(&i->state, PA_SOURCE_OUTPUT_DISCONNECTED);
|
||||
}
|
||||
|
||||
static void source_output_free(pa_source_output* o) {
|
||||
assert(o);
|
||||
static void source_output_free(pa_msgobject* mo) {
|
||||
pa_source_output *o = PA_SOURCE_OUTPUT(mo);
|
||||
|
||||
pa_assert(pa_source_output_refcnt(o) == 0);
|
||||
|
||||
if (o->state != PA_SOURCE_OUTPUT_DISCONNECTED)
|
||||
pa_source_output_disconnect(o);
|
||||
pa_source_output_disconnect(o);
|
||||
|
||||
pa_log_info("freed %u \"%s\"", o->index, o->name);
|
||||
pa_log_info("Freeing output %u \"%s\"", o->index, o->name);
|
||||
|
||||
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);
|
||||
|
||||
pa_asyncmsgq_post(o->source->asyncmsgq, o->source, PA_SOURCE_MESSAGE_ADD_OUTPUT, o, NULL, pa_source_unref, pa_source_output_unref);
|
||||
pa_source_update_status(o->source);
|
||||
|
||||
if (!(--o->ref))
|
||||
source_output_free(o);
|
||||
}
|
||||
|
||||
pa_source_output* pa_source_output_ref(pa_source_output *o) {
|
||||
assert(o);
|
||||
assert(o->ref >= 1);
|
||||
|
||||
o->ref++;
|
||||
return o;
|
||||
pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_NEW, o->index);
|
||||
}
|
||||
|
||||
void pa_source_output_kill(pa_source_output*o) {
|
||||
assert(o);
|
||||
assert(o->ref >= 1);
|
||||
pa_source_output_assert_ref(o);
|
||||
|
||||
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);
|
||||
|
||||
if (pa_asyncmsgq_send(o->source->asyncmsgq, i->source, PA_SOURCE_OUTPUT_MESSAGE_GET_LATENCY, &r, NULL) < 0)
|
||||
r = 0;
|
||||
|
||||
if (o->get_latency)
|
||||
r += o->get_latency(o);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
void pa_source_output_push(pa_source_output *o, const pa_memchunk *chunk) {
|
||||
pa_memchunk rchunk;
|
||||
pa_source_output_state_t state;
|
||||
|
||||
assert(o);
|
||||
assert(chunk);
|
||||
assert(chunk->length);
|
||||
assert(o->push);
|
||||
pa_source_output_assert_ref(o);
|
||||
pa_assert(chunk);
|
||||
pa_assert(chunk->length);
|
||||
|
||||
if (o->state == PA_SOURCE_OUTPUT_CORKED)
|
||||
state = pa_source_output_get_state(o);
|
||||
|
||||
if (!o->push || state == PA_SOURCE_OUTPUT_DISCONNECTED || state == PA_SOURCE_OUTPUT_CORKED)
|
||||
return;
|
||||
|
||||
pa_assert(state = PA_SOURCE_OUTPUT_RUNNING);
|
||||
|
||||
if (!o->resampler) {
|
||||
o->push(o, chunk);
|
||||
return;
|
||||
|
|
@ -243,14 +259,43 @@ void pa_source_output_push(pa_source_output *o, const pa_memchunk *chunk) {
|
|||
if (!rchunk.length)
|
||||
return;
|
||||
|
||||
assert(rchunk.memblock);
|
||||
pa_assert(rchunk.memblock);
|
||||
o->push(o, &rchunk);
|
||||
pa_memblock_unref(rchunk.memblock);
|
||||
}
|
||||
|
||||
void pa_source_output_cork(pa_source_output *o, int b) {
|
||||
int n;
|
||||
pa_source_output_state_t state;
|
||||
|
||||
pa_source_output_assert_ref(o);
|
||||
|
||||
state = pa_source_output_get_state(o);
|
||||
pa_assert(state != PA_SOURCE_OUTPUT_DISCONNECTED);
|
||||
|
||||
if (b && state != PA_SOURCE_OUTPUT_CORKED)
|
||||
pa_atomic_store(o->state, PA_SOURCE_OUTPUT_CORKED);
|
||||
else if (!b && state == PA_SOURCE_OUTPUT_CORKED)
|
||||
pa_atomic_cmpxchg(o->state, state, PA_SOURCE_OUTPUT_RUNNING);
|
||||
}
|
||||
|
||||
int pa_source_output_set_rate(pa_source_output *o, uint32_t rate) {
|
||||
pa_source_output_assert_ref(o);
|
||||
pa_return_val_if_fail(o->thread_info.resampler, -1);
|
||||
|
||||
if (i->sample_spec.rate == rate)
|
||||
return 0;
|
||||
|
||||
i->sample_spec.rate = rate;
|
||||
|
||||
pa_asyncmsgq_post(s->asyncmsgq, pa_source_output_ref(i), PA_SOURCE_OUTPUT_MESSAGE_SET_RATE, PA_UINT_TO_PTR(rate), NULL, pa_source_output_unref, NULL);
|
||||
|
||||
pa_subscription_post(o->source->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT!|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void pa_source_output_set_name(pa_source_output *o, const char *name) {
|
||||
assert(o);
|
||||
assert(o->ref >= 1);
|
||||
pa_source_output_assert_ref(o);
|
||||
|
||||
if (!o->name && !name)
|
||||
return;
|
||||
|
|
@ -264,98 +309,87 @@ void pa_source_output_set_name(pa_source_output *o, const char *name) {
|
|||
pa_subscription_post(o->source->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
|
||||
}
|
||||
|
||||
pa_usec_t pa_source_output_get_latency(pa_source_output *o) {
|
||||
assert(o);
|
||||
assert(o->ref >= 1);
|
||||
|
||||
if (o->get_latency)
|
||||
return o->get_latency(o);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void pa_source_output_cork(pa_source_output *o, int b) {
|
||||
int n;
|
||||
|
||||
assert(o);
|
||||
assert(o->ref >= 1);
|
||||
|
||||
if (o->state == PA_SOURCE_OUTPUT_DISCONNECTED)
|
||||
return;
|
||||
|
||||
n = o->state == PA_SOURCE_OUTPUT_CORKED && !b;
|
||||
|
||||
o->state = b ? PA_SOURCE_OUTPUT_CORKED : PA_SOURCE_OUTPUT_RUNNING;
|
||||
|
||||
if (n)
|
||||
pa_source_notify(o->source);
|
||||
}
|
||||
|
||||
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_source_assert_ref(dest);
|
||||
|
||||
origin = o->source;
|
||||
return -1;
|
||||
|
||||
/* origin = o->source; */
|
||||
|
||||
if (dest == origin)
|
||||
return 0;
|
||||
/* if (dest == origin) */
|
||||
/* return 0; */
|
||||
|
||||
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 (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 &&
|
||||
pa_sample_spec_equal(&origin->sample_spec, &dest->sample_spec) &&
|
||||
pa_channel_map_equal(&origin->channel_map, &dest->channel_map))
|
||||
/* if (o->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;
|
||||
/* /\* Try to reuse the old resampler if possible *\/ */
|
||||
/* new_resampler = o->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 (!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 sink *\/ */
|
||||
|
||||
if (!(new_resampler = pa_resampler_new(
|
||||
dest->core->mempool,
|
||||
&dest->sample_spec, &dest->channel_map,
|
||||
&o->sample_spec, &o->channel_map,
|
||||
o->resample_method))) {
|
||||
pa_log_warn("Unsupported resampling operation.");
|
||||
return -1;
|
||||
/* if (!(new_resampler = pa_resampler_new( */
|
||||
/* dest->core->mempool, */
|
||||
/* &dest->sample_spec, &dest->channel_map, */
|
||||
/* &o->sample_spec, &o->channel_map, */
|
||||
/* o->resample_method))) { */
|
||||
/* pa_log_warn("Unsupported resampling operation."); */
|
||||
/* return -1; */
|
||||
/* } */
|
||||
/* } */
|
||||
|
||||
/* /\* Okey, let's move it *\/ */
|
||||
/* pa_idxset_remove_by_data(origin->outputs, o, NULL); */
|
||||
/* pa_idxset_put(dest->outputs, o, NULL); */
|
||||
/* o->source = dest; */
|
||||
|
||||
/* /\* Replace resampler *\/ */
|
||||
/* if (new_resampler != o->resampler) { */
|
||||
/* if (o->resampler) */
|
||||
/* pa_resampler_free(o->resampler); */
|
||||
/* o->resampler = new_resampler; */
|
||||
/* } */
|
||||
|
||||
/* /\* 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; */
|
||||
}
|
||||
|
||||
int pa_source_output_process_msg(pa_msgobject *mo, int code, void *userdata, pa_memchunk* chunk) {
|
||||
pa_source_output *o = PA_SOURCE_OUTPUT(o);
|
||||
|
||||
pa_source_output_assert_ref(i);
|
||||
|
||||
switch (code) {
|
||||
|
||||
case PA_SOURCE_OUTPUT_MESSAGE_SET_RATE: {
|
||||
|
||||
i->thread_info.sample_spec.rate = PA_PTR_TO_UINT(userdata);
|
||||
pa_resampler_set_output_rate(i->resampler, PA_PTR_TO_UINT(userdata));
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Okey, let's move it */
|
||||
pa_idxset_remove_by_data(origin->outputs, o, NULL);
|
||||
pa_idxset_put(dest->outputs, o, NULL);
|
||||
o->source = dest;
|
||||
|
||||
/* Replace resampler */
|
||||
if (new_resampler != o->resampler) {
|
||||
if (o->resampler)
|
||||
pa_resampler_free(o->resampler);
|
||||
o->resampler = new_resampler;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
return -1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
#ifndef foosourceoutputhfoo
|
||||
#define foosourceoutputhfoo
|
||||
#ifndef foopulsesourceoutputhfoo
|
||||
#define foopulsesourceoutputhfoo
|
||||
|
||||
/* $Id$ */
|
||||
|
||||
|
|
@ -35,40 +35,59 @@ 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_RUNNING,
|
||||
PA_SOURCE_OUTPUT_CORKED,
|
||||
PA_SOURCE_OUTPUT_DISCONNECTED
|
||||
} pa_source_output_state_t;
|
||||
|
||||
typedef enum pa_source_output_flags {
|
||||
PA_SOURCE_OUTPUT_NO_HOOKS = 1
|
||||
PA_SOURCE_OUTPUT_NO_HOOKS = 1,
|
||||
PA_SOURCE_OUTPUT_VARIABLE_RATE = 2
|
||||
} pa_source_output_flags_t;
|
||||
|
||||
struct pa_source_output {
|
||||
int ref;
|
||||
pa_msgobject parent;
|
||||
|
||||
uint32_t index;
|
||||
pa_source_output_state_t state;
|
||||
pa_core *core;
|
||||
pa_atomic_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;
|
||||
|
||||
int (*process_msg)(pa_sink_input *i, int code, void *userdata);
|
||||
void (*push)(pa_source_output *o, const pa_memchunk *chunk);
|
||||
void (*kill)(pa_source_output* o); /* may be NULL */
|
||||
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_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*) (o))
|
||||
|
||||
enum {
|
||||
PA_SOURCE_OUTPUT_MESSAGE_GET_LATENCY,
|
||||
PA_SOURCE_OUTPUT_MESSAGE_SET_RATE,
|
||||
PA_SOURCE_OUTPUT_MESSAGE_MAX
|
||||
};
|
||||
|
||||
typedef struct pa_source_output_new_data {
|
||||
const char *name, *driver;
|
||||
pa_module *module;
|
||||
|
|
@ -89,30 +108,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);
|
||||
|
||||
/* To be called by the implementing module only */
|
||||
void pa_source_output_put(pa_source_output *o);
|
||||
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_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) ((pa_source_output_state_t) pa_atomic_load(&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, pa_memchunk *chunk);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -42,12 +42,6 @@
|
|||
|
||||
#include "source.h"
|
||||
|
||||
#define CHECK_VALIDITY_RETURN_NULL(condition) \
|
||||
do {\
|
||||
if (!(condition)) \
|
||||
return NULL; \
|
||||
} while (0)
|
||||
|
||||
pa_source* pa_source_new(
|
||||
pa_core *core,
|
||||
const char *driver,
|
||||
|
|
@ -65,30 +59,32 @@ pa_source* pa_source_new(
|
|||
assert(name);
|
||||
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;
|
||||
pa_atomic_store(&s->state, PA_SOURCE_IDLE);
|
||||
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;
|
||||
|
|
@ -96,45 +92,87 @@ pa_source* pa_source_new(
|
|||
s->outputs = pa_idxset_new(NULL, NULL);
|
||||
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;
|
||||
pa_cvolume_reset(&s->volume, spec->channels);
|
||||
s->muted = 0;
|
||||
s->refresh_volume = s->refresh_mute = 0;
|
||||
|
||||
s->is_hardware = 0;
|
||||
|
||||
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->start = NULL;
|
||||
s->stop = NULL;
|
||||
s->userdata = NULL;
|
||||
|
||||
pa_assert_se(s->asyncmsgq = pa_asyncmsgq_new(0));
|
||||
|
||||
r = pa_idxset_put(core->sources, s, &s->index);
|
||||
assert(s->index != PA_IDXSET_INVALID && r >= 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);
|
||||
|
||||
s->thread_info.outputs = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
|
||||
s->thread_info.soft_volume = s->volume;
|
||||
s->thread_info.soft_muted = s->muted;
|
||||
|
||||
pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_SOURCE | PA_SUBSCRIPTION_EVENT_NEW, s->index);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
static void source_start(pa_source *s) {
|
||||
pa_source_state_t state;
|
||||
pa_assert(s);
|
||||
|
||||
state = pa_source_get_state(s);
|
||||
pa_return_if_fail(state == PA_SOURCE_IDLE || state == PA_SOURCE_SUSPENDED);
|
||||
|
||||
pa_atomic_store(&s->state, PA_SOURCE_RUNNING);
|
||||
|
||||
if (s->start)
|
||||
s->start(s);
|
||||
else
|
||||
pa_asyncmsgq_post(s->asyncmsgq, s, PA_SOURCE_MESSAGE_START, NULL, NULL, pa_source_unref, NULL);
|
||||
}
|
||||
|
||||
static void source_stop(pa_source *s) {
|
||||
pa_source_state_t state;
|
||||
int stop;
|
||||
|
||||
pa_assert(s);
|
||||
state = pa_source_get_state(s);
|
||||
pa_return_if_fail(state == PA_SOURCE_RUNNING || state == PA_SOURCE_SUSPENDED);
|
||||
|
||||
stop = state == PA_SOURCE_RUNNING;
|
||||
pa_atomic_store(&s->state, PA_SOURCE_IDLE);
|
||||
|
||||
if (stop) {
|
||||
if (s->stop)
|
||||
s->stop(s);
|
||||
else
|
||||
pa_asyncmsgq_post(s->asyncmsgq, s, PA_SOURCE_MESSAGE_STOP, NULL, NULL, pa_source_unref, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void pa_source_disconnect(pa_source *s) {
|
||||
pa_source_output *o, *j = NULL;
|
||||
|
||||
assert(s);
|
||||
assert(s->state == PA_SOURCE_RUNNING);
|
||||
pa_assert(s);
|
||||
pa_return_if_fail(pa_sink_get_state(s) != PA_SINK_DISCONNECT);
|
||||
|
||||
s->state = PA_SOURCE_DISCONNECTED;
|
||||
source_stop(s);
|
||||
|
||||
pa_atomic_store(&s->state, PA_SOURCE_DISCONNECTED);
|
||||
pa_namereg_unregister(s->core, s->name);
|
||||
|
||||
pa_hook_fire(&s->core->hook_source_disconnect, s);
|
||||
|
||||
while ((o = pa_idxset_first(s->outputs, NULL))) {
|
||||
assert(o != j);
|
||||
pa_assert(o != j);
|
||||
pa_source_output_kill(o);
|
||||
j = o;
|
||||
}
|
||||
|
|
@ -142,190 +180,206 @@ void pa_source_disconnect(pa_source *s) {
|
|||
pa_idxset_remove_by_data(s->core->sources, s, NULL);
|
||||
|
||||
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->start = NULL;
|
||||
s->stop = NULL;
|
||||
|
||||
pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE | PA_SUBSCRIPTION_EVENT_REMOVE, s->index);
|
||||
}
|
||||
|
||||
static void source_free(pa_source *s) {
|
||||
assert(s);
|
||||
assert(!s->ref);
|
||||
static void source_free(pa_msgobject *o) {
|
||||
pa_source *s = PA_SOURCE(o);
|
||||
|
||||
pa_assert(s);
|
||||
pa_assert(pa_source_refcnt(s) == 0);
|
||||
|
||||
if (s->state != PA_SOURCE_DISCONNECTED)
|
||||
pa_source_disconnect(s);
|
||||
pa_source_disconnect(s);
|
||||
|
||||
pa_log_info("freed %u \"%s\"", s->index, s->name);
|
||||
pa_log_info("Freeing source %u \"%s\"", s->index, s->name);
|
||||
|
||||
pa_idxset_free(s->outputs, NULL, NULL);
|
||||
pa_hashmap_free(s->thread_info.outputs, pa_sink_output_unref, NULL);
|
||||
|
||||
pa_asyncmsgq_free(s->asyncmsgq);
|
||||
|
||||
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);
|
||||
void pa_source_update_status(pa_source*s) {
|
||||
pa_source_assert_ref(s);
|
||||
|
||||
if (!(--s->ref))
|
||||
source_free(s);
|
||||
if (pa_source_get_state(s) == PA_SOURCE_STATE_SUSPENDED)
|
||||
return;
|
||||
|
||||
if (pa_source_used_by(s) > 0)
|
||||
source_start(s);
|
||||
else
|
||||
source_stop(s);
|
||||
}
|
||||
|
||||
pa_source* pa_source_ref(pa_source *s) {
|
||||
assert(s);
|
||||
assert(s->ref >= 1);
|
||||
void pa_source_suspend(pa_source *s, int suspend) {
|
||||
pa_source_state_t state;
|
||||
|
||||
s->ref++;
|
||||
return s;
|
||||
}
|
||||
pa_source_assert_ref(s);
|
||||
|
||||
void pa_source_notify(pa_source*s) {
|
||||
assert(s);
|
||||
assert(s->ref >= 1);
|
||||
state = pa_source_get_state(s);
|
||||
pa_return_if_fail(suspend && (s->state == PA_SOURCE_RUNNING || s->state == PA_SOURCE_IDLE));
|
||||
pa_return_if_fail(!suspend && (s->state == PA_SOURCE_SUSPENDED));
|
||||
|
||||
if (s->notify)
|
||||
s->notify(s);
|
||||
}
|
||||
|
||||
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;
|
||||
if (suspend) {
|
||||
pa_atomic_store(&s->state, PA_SOURCE_SUSPENDED);
|
||||
|
||||
assert(o);
|
||||
assert(chunk);
|
||||
if (s->stop)
|
||||
s->stop(s);
|
||||
else
|
||||
pa_asyncmsgq_post(s->asyncmsgq, s, PA_SOURCE_MESSAGE_STOP, NULL, NULL, pa_source_unref, NULL);
|
||||
|
||||
} else {
|
||||
pa_atomic_store(&s->state, PA_SOURCE_RUNNING);
|
||||
|
||||
pa_source_output_push(o, chunk);
|
||||
return 0;
|
||||
if (s->start)
|
||||
s->start(s);
|
||||
else
|
||||
pa_asyncmsgq_post(s->asyncmsgq, s, PA_SOURCE_MESSAGE_START, NULL, NULL, pa_source_unref, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void pa_source_post(pa_source*s, const pa_memchunk *chunk) {
|
||||
assert(s);
|
||||
assert(s->ref >= 1);
|
||||
assert(chunk);
|
||||
|
||||
pa_source_ref(s);
|
||||
pa_source_output *o;
|
||||
void *state = NULL;
|
||||
|
||||
pa_source_assert_ref(s);
|
||||
pa_assert(chunk);
|
||||
|
||||
if (s->sw_muted || !pa_cvolume_is_norm(&s->sw_volume)) {
|
||||
pa_memchunk vchunk = *chunk;
|
||||
|
||||
pa_memblock_ref(vchunk.memblock);
|
||||
pa_memchunk_make_writable(&vchunk, 0);
|
||||
if (s->sw_muted)
|
||||
|
||||
if (s->thread_info.muted || pa_cvolume_is_muted(s->thread_info.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.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 {
|
||||
|
||||
while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL)))
|
||||
pa_source_output_push(o, chunk);
|
||||
|
||||
pa_source_unref(s);
|
||||
}
|
||||
|
||||
void pa_source_set_owner(pa_source *s, pa_module *m) {
|
||||
assert(s);
|
||||
assert(s->ref >= 1);
|
||||
|
||||
if (m == s->owner)
|
||||
return;
|
||||
|
||||
s->owner = m;
|
||||
pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
|
||||
}
|
||||
}
|
||||
|
||||
pa_usec_t pa_source_get_latency(pa_source *s) {
|
||||
assert(s);
|
||||
assert(s->ref >= 1);
|
||||
pa_usec_t usec;
|
||||
|
||||
if (!s->get_latency)
|
||||
pa_source_assert_ref(s);
|
||||
|
||||
if (s->get_latency)
|
||||
return s->get_latency(s);
|
||||
|
||||
if (pa_asyncmsgq_send(s->asyncmsgq, s, PA_SOURCE_MESSAGE_GET_LATENCY, &usec, 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) {
|
||||
void pa_source_set_volume(pa_source *s, const pa_cvolume *volume) {
|
||||
pa_cvolume *v;
|
||||
|
||||
assert(s);
|
||||
assert(s->ref >= 1);
|
||||
assert(volume);
|
||||
pa_source_assert_ref(s);
|
||||
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 (s->set_volume && s->set_volume(s) < 0)
|
||||
s->set_volume = NULL;
|
||||
|
||||
if (pa_cvolume_equal(v, volume))
|
||||
return;
|
||||
if (!s->set_volume)
|
||||
pa_asyncmsgq_post(s->asyncmsgq, pa_source_ref(s), PA_SOURCE_MESSAGE_SET_VOLUME, pa_xnewdup(struct pa_cvolume, volume, 1), pa_source_unref, pa_xfree);
|
||||
|
||||
*v = *volume;
|
||||
|
||||
if (v == &s->hw_volume)
|
||||
if (s->set_hw_volume(s) < 0)
|
||||
s->sw_volume = *volume;
|
||||
|
||||
pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
|
||||
if (changed)
|
||||
pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
|
||||
}
|
||||
|
||||
const pa_cvolume *pa_source_get_volume(pa_source *s, pa_mixer_t m) {
|
||||
assert(s);
|
||||
assert(s->ref >= 1);
|
||||
const pa_cvolume *pa_source_get_volume(pa_source *s) {
|
||||
pa_source_assert_ref(s);
|
||||
|
||||
if (m == PA_MIXER_HARDWARE && s->set_hw_volume) {
|
||||
old_volume = s->volume;
|
||||
|
||||
if (s->get_volume && s->get_volume(s) < 0)
|
||||
s->get_volume = NULL;
|
||||
|
||||
if (s->get_hw_volume)
|
||||
s->get_hw_volume(s);
|
||||
if (!s->get_volume && s->refresh_volume)
|
||||
pa_asyncmsgq_send(s->asyncmsgq, s, PA_SOURCE_MESSAGE_GET_VOLUME, &s->volume);
|
||||
|
||||
return &s->hw_volume;
|
||||
} else
|
||||
return &s->sw_volume;
|
||||
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;
|
||||
}
|
||||
|
||||
void pa_source_set_mute(pa_source *s, pa_mixer_t m, int mute) {
|
||||
int *t;
|
||||
int changed;
|
||||
|
||||
pa_source_assert_ref(s);
|
||||
|
||||
assert(s);
|
||||
assert(s->ref >= 1);
|
||||
changed = s->muted != mute;
|
||||
|
||||
if (m == PA_MIXER_HARDWARE && s->set_hw_mute)
|
||||
t = &s->hw_muted;
|
||||
else
|
||||
t = &s->sw_muted;
|
||||
if (s->set_mute && s->set_mute(s) < 0)
|
||||
s->set_mute = NULL;
|
||||
|
||||
if (!!*t == !!mute)
|
||||
return;
|
||||
if (!s->set_mute)
|
||||
pa_asyncmsgq_post(s->asyncmsgq, pa_source_ref(s), PA_SOURCE_MESSAGE_SET_MUTE, PA_UINT_TO_PTR(mute), pa_source_unref, NULL);
|
||||
|
||||
*t = !!mute;
|
||||
|
||||
if (t == &s->hw_muted)
|
||||
if (s->set_hw_mute(s) < 0)
|
||||
s->sw_muted = !!mute;
|
||||
|
||||
pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
|
||||
if (changed)
|
||||
pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
|
||||
}
|
||||
|
||||
int pa_source_get_mute(pa_source *s, pa_mixer_t m) {
|
||||
assert(s);
|
||||
assert(s->ref >= 1);
|
||||
int old_muted;
|
||||
|
||||
pa_source_assert_ref(s);
|
||||
|
||||
if (m == PA_MIXER_HARDWARE && s->set_hw_mute) {
|
||||
old_muted = s->muted;
|
||||
|
||||
if (s->get_mute && s->get_mute(s) < 0)
|
||||
s->get_mute = NULL;
|
||||
|
||||
if (s->get_hw_mute)
|
||||
s->get_hw_mute(s);
|
||||
if (!s->get_mute && s->refresh_mute)
|
||||
pa_asyncmsgq_send(s->asyncmsgq, s, PA_SOURCE_MESSAGE_GET_MUTE, &s->muted);
|
||||
|
||||
return s->hw_muted;
|
||||
} else
|
||||
return s->sw_muted;
|
||||
if (old_muted != s->muted)
|
||||
pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
|
||||
|
||||
return s->muted;
|
||||
}
|
||||
|
||||
void pa_source_set_module(pa_source *s, pa_module *m) {
|
||||
pa_source_assert_ref(s);
|
||||
|
||||
if (m == s->module)
|
||||
return;
|
||||
|
||||
s->module = m;
|
||||
|
||||
pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
|
||||
}
|
||||
|
||||
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;
|
||||
|
|
@ -340,8 +394,45 @@ void pa_source_set_description(pa_source *s, const char *description) {
|
|||
}
|
||||
|
||||
unsigned pa_source_used_by(pa_source *s) {
|
||||
assert(s);
|
||||
assert(s->ref >= 1);
|
||||
pa_source_assert_ref(s);
|
||||
|
||||
return pa_idxset_size(s->outputs);
|
||||
}
|
||||
|
||||
int pa_source_process_msg(pa_msgobject *o, void *object, int code, pa_memchunk *chunk, void *userdata) {
|
||||
pa_source *s = PA_SOURCE(o);
|
||||
pa_source_assert_ref(s);
|
||||
|
||||
switch (code) {
|
||||
case PA_SOURCE_MESSAGE_ADD_OUTPUT: {
|
||||
pa_source_output *i = userdata;
|
||||
pa_hashmap_put(s->thread_info.outputs, PA_UINT32_TO_PTR(i->index), pa_source_output_ref(i));
|
||||
return 0;
|
||||
}
|
||||
|
||||
case PA_SOURCE_MESSAGE_REMOVE_INPUT: {
|
||||
pa_source_input *i = userdata;
|
||||
pa_hashmap_remove(s->thread_info.outputs, PA_UINT32_TO_PTR(i->index), pa_source_output_ref(i));
|
||||
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:
|
||||
*((int*) userdata) = s->thread_info.soft_muted;
|
||||
return 0;
|
||||
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,24 +40,30 @@ 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>
|
||||
|
||||
#define PA_MAX_OUTPUTS_PER_SOURCE 16
|
||||
#define PA_MAX_OUTPUTS_PER_SOURCE 32
|
||||
|
||||
typedef enum pa_source_state {
|
||||
PA_SOURCE_RUNNING,
|
||||
PA_SOURCE_SUSPENDED,
|
||||
PA_SOURCE_IDLE,
|
||||
PA_SOURCE_DISCONNECTED
|
||||
} pa_source_state_t;
|
||||
|
||||
struct pa_source {
|
||||
int ref;
|
||||
pa_msgobject parent;
|
||||
|
||||
uint32_t index;
|
||||
pa_core *core;
|
||||
pa_source_state_t state;
|
||||
pa_atomic_t state;
|
||||
|
||||
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;
|
||||
|
|
@ -64,48 +71,79 @@ struct pa_source {
|
|||
pa_idxset *outputs;
|
||||
pa_sink *monitor_of; /* may be NULL */
|
||||
|
||||
pa_cvolume hw_volume, sw_volume;
|
||||
int hw_muted, sw_muted;
|
||||
pa_cvolume volume;
|
||||
int muted;
|
||||
int refresh_volume;
|
||||
int referesh_mute;
|
||||
|
||||
int is_hardware;
|
||||
|
||||
void (*notify)(pa_source*source); /* may be NULL */
|
||||
void (*start)(pa_source*source); /* may be NULL */
|
||||
void (*stop)(pa_source*source); /* 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;
|
||||
|
||||
struct {
|
||||
pa_hashmap *outputs;
|
||||
pa_cvolume soft_volume;
|
||||
int soft_muted;
|
||||
} thread_info;
|
||||
|
||||
void *userdata;
|
||||
};
|
||||
|
||||
PA_DECLARE_CLASS(pa_source);
|
||||
#define PA_SOURCE(s) ((pa_source*) (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_START,
|
||||
PA_SOURCE_MESSAGE_STOP,
|
||||
PA_SOURCE_MESSAGE_MAX
|
||||
} pa_source_message_t;
|
||||
|
||||
/* To be used exclusively by the source driver */
|
||||
|
||||
pa_source* pa_source_new(
|
||||
pa_core *core,
|
||||
const char *driver,
|
||||
const char *name,
|
||||
int namereg_fail,
|
||||
const pa_sample_spec *spec,
|
||||
const pa_channel_map *map);
|
||||
pa_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);
|
||||
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_notify(pa_source *s);
|
||||
|
||||
void pa_source_set_owner(pa_source *s, pa_module *m);
|
||||
/* Callable by everyone */
|
||||
|
||||
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);
|
||||
void pa_source_update_status(pa_source*s);
|
||||
void pa_source_suspend(pa_source *s);
|
||||
|
||||
void pa_source_set_description(pa_source *s, const char *description);
|
||||
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, int mute);
|
||||
int pa_source_get_mute(pa_source *source);
|
||||
|
||||
unsigned pa_source_used_by(pa_source *s);
|
||||
#define pa_source_get_state(s) ((pa_source_state_t) pa_atomic_load(&(s)->state))
|
||||
|
||||
/* To be used exclusively by the source driver thread */
|
||||
|
||||
void pa_source_post(pa_source*s, const pa_memchunk *b);
|
||||
void pa_source_process_msg(pa_msgobject *o, int code, void *userdata, pa_memchunk *chunk);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -26,7 +26,6 @@
|
|||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <assert.h>
|
||||
#include <pthread.h>
|
||||
#include <sched.h>
|
||||
#include <errno.h>
|
||||
|
|
@ -35,56 +34,50 @@
|
|||
#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 pthread_key_t thread_key;
|
||||
static pthread_once_t thread_once = PTHREAD_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);
|
||||
static void thread_once_func(void) {
|
||||
pa_assert_se(pthread_key_create(&thread_key, thread_free_cb) == 0);
|
||||
}
|
||||
|
||||
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);
|
||||
pthread_once(&thread_once, thread_once_func);
|
||||
pthread_setspecific(thread_key, 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 +85,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 +103,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 +130,9 @@ 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);
|
||||
pthread_once(&thread_once, thread_once_func);
|
||||
|
||||
if ((t = pa_tls_get(thread_tls)))
|
||||
if ((t = pthread_getspecific(thread_key)))
|
||||
return t;
|
||||
|
||||
/* This is a foreign thread, let's create a pthread structure to
|
||||
|
|
@ -151,19 +144,19 @@ pa_thread* pa_thread_self(void) {
|
|||
t->userdata = NULL;
|
||||
pa_atomic_store(&t->running, 2);
|
||||
|
||||
pa_tls_set(thread_tls, t);
|
||||
pthread_setspecific(thread_key, 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 +165,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 +183,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 +199,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;
|
||||
}
|
||||
|
||||
|
|
|
|||
110
src/tests/asyncmsgq-test.c
Normal file
110
src/tests/asyncmsgq-test.c
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
/* $Id$ */
|
||||
|
||||
/***
|
||||
This file is part of PulseAudio.
|
||||
|
||||
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 <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <pulse/util.h>
|
||||
#include <pulse/xmalloc.h>
|
||||
#include <pulsecore/asyncmsgq.h>
|
||||
#include <pulsecore/thread.h>
|
||||
#include <pulsecore/log.h>
|
||||
#include <pulsecore/core-util.h>
|
||||
#include <pulsecore/macro.h>
|
||||
|
||||
enum {
|
||||
OPERATION_A,
|
||||
OPERATION_B,
|
||||
OPERATION_C,
|
||||
QUIT
|
||||
};
|
||||
|
||||
static void the_thread(void *_q) {
|
||||
pa_asyncmsgq *q = _q;
|
||||
int quit = 0;
|
||||
|
||||
do {
|
||||
int code = 0;
|
||||
|
||||
pa_assert_se(pa_asyncmsgq_get(q, NULL, &code, NULL, 1) == 0);
|
||||
|
||||
switch (code) {
|
||||
|
||||
case OPERATION_A:
|
||||
printf("Operation A\n");
|
||||
break;
|
||||
|
||||
case OPERATION_B:
|
||||
printf("Operation B\n");
|
||||
break;
|
||||
|
||||
case OPERATION_C:
|
||||
printf("Operation C\n");
|
||||
break;
|
||||
|
||||
case QUIT:
|
||||
printf("quit\n");
|
||||
quit = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
pa_asyncmsgq_done(q);
|
||||
|
||||
} while (!quit);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
pa_asyncmsgq *q;
|
||||
pa_thread *t;
|
||||
|
||||
pa_assert_se(q = pa_asyncmsgq_new(0));
|
||||
|
||||
pa_assert_se(t = pa_thread_new(the_thread, q));
|
||||
|
||||
printf("Operation A post\n");
|
||||
pa_asyncmsgq_post(q, NULL, OPERATION_A, NULL, NULL, NULL);
|
||||
|
||||
pa_thread_yield();
|
||||
|
||||
printf("Operation B post\n");
|
||||
pa_asyncmsgq_post(q, NULL, OPERATION_B, NULL, NULL, NULL);
|
||||
|
||||
pa_thread_yield();
|
||||
|
||||
printf("Operation C send\n");
|
||||
pa_asyncmsgq_send(q, NULL, OPERATION_C, NULL);
|
||||
|
||||
pa_thread_yield();
|
||||
|
||||
printf("Quit post\n");
|
||||
pa_asyncmsgq_post(q, NULL, QUIT, NULL, NULL, NULL);
|
||||
|
||||
pa_thread_free(t);
|
||||
|
||||
pa_asyncmsgq_free(q);
|
||||
|
||||
return 0;
|
||||
}
|
||||
87
src/tests/asyncq-test.c
Normal file
87
src/tests/asyncq-test.c
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
/* $Id$ */
|
||||
|
||||
/***
|
||||
This file is part of PulseAudio.
|
||||
|
||||
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 <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <pulse/util.h>
|
||||
#include <pulse/xmalloc.h>
|
||||
#include <pulsecore/asyncq.h>
|
||||
#include <pulsecore/thread.h>
|
||||
#include <pulsecore/log.h>
|
||||
#include <pulsecore/core-util.h>
|
||||
#include <pulsecore/macro.h>
|
||||
|
||||
static void producer(void *_q) {
|
||||
pa_asyncq *q = _q;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 1000; i++) {
|
||||
pa_asyncq_push(q, (void*) (i+1), 1);
|
||||
printf("pushed %i\n", i);
|
||||
}
|
||||
|
||||
pa_asyncq_push(q, (void*) -1, 1);
|
||||
printf("pushed end\n");
|
||||
}
|
||||
|
||||
static void consumer(void *_q) {
|
||||
pa_asyncq *q = _q;
|
||||
void *p;
|
||||
int i;
|
||||
|
||||
sleep(1);
|
||||
|
||||
for (i = 0;; i++) {
|
||||
p = pa_asyncq_pop(q, 1);
|
||||
|
||||
if (p == (void*) -1)
|
||||
break;
|
||||
|
||||
pa_assert(p == (void *) (i+1));
|
||||
|
||||
printf("popped %i\n", i);
|
||||
}
|
||||
|
||||
printf("popped end\n");
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
pa_asyncq *q;
|
||||
pa_thread *t1, *t2;
|
||||
|
||||
pa_assert_se(q = pa_asyncq_new(0));
|
||||
|
||||
pa_assert_se(t1 = pa_thread_new(producer, q));
|
||||
pa_assert_se(t2 = pa_thread_new(consumer, q));
|
||||
|
||||
pa_thread_free(t1);
|
||||
pa_thread_free(t2);
|
||||
|
||||
pa_asyncq_free(q, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -59,24 +59,29 @@ int main(PA_GCC_UNUSED int argc, PA_GCC_UNUSED char *argv[]) {
|
|||
c.index = c.length = 0;
|
||||
}
|
||||
|
||||
assert(c.index < c.memblock->length);
|
||||
assert(c.index < pa_memblock_get_length(c.memblock));
|
||||
|
||||
l = c.memblock->length - c.index;
|
||||
l = pa_memblock_get_length(c.memblock) - c.index;
|
||||
|
||||
l = l <= 1 ? l : rand() % (l-1) +1 ;
|
||||
|
||||
if ((r = read(STDIN_FILENO, (uint8_t*) c.memblock->data + c.index, l)) <= 0) {
|
||||
p = pa_memblock_acquire(c.memblock);
|
||||
|
||||
if ((r = read(STDIN_FILENO, (uint8_t*) p + c.index, l)) <= 0) {
|
||||
pa_memblock_release(c.memblock);
|
||||
fprintf(stderr, "read() failed: %s\n", r < 0 ? strerror(errno) : "EOF");
|
||||
break;
|
||||
}
|
||||
|
||||
pa_memblock_release(c.memblock);
|
||||
|
||||
c.length = r;
|
||||
pa_mcalign_push(a, &c);
|
||||
fprintf(stderr, "Read %ld bytes\n", (long)r);
|
||||
|
||||
c.index += r;
|
||||
|
||||
if (c.index >= c.memblock->length) {
|
||||
if (c.index >= pa_memblock_get_length(c.memblock)) {
|
||||
pa_memblock_unref(c.memblock);
|
||||
pa_memchunk_reset(&c);
|
||||
}
|
||||
|
|
@ -87,7 +92,9 @@ int main(PA_GCC_UNUSED int argc, PA_GCC_UNUSED char *argv[]) {
|
|||
if (pa_mcalign_pop(a, &t) < 0)
|
||||
break;
|
||||
|
||||
pa_loop_write(STDOUT_FILENO, (uint8_t*) t.memblock->data + t.index, t.length, NULL);
|
||||
p = pa_memblock_acquire(t.memblock);
|
||||
pa_loop_write(STDOUT_FILENO, (uint8_t*) p + t.index, t.length, NULL);
|
||||
pa_memblock_release(t.memblock);
|
||||
fprintf(stderr, "Wrote %lu bytes.\n", (unsigned long) t.length);
|
||||
|
||||
pa_memblock_unref(t.memblock);
|
||||
|
|
|
|||
|
|
@ -76,6 +76,7 @@ int main(int argc, char *argv[]) {
|
|||
pa_memblock* blocks[5];
|
||||
uint32_t id, shm_id;
|
||||
size_t offset, size;
|
||||
char *x;
|
||||
|
||||
const char txt[] = "This is a test!";
|
||||
|
||||
|
|
@ -90,10 +91,17 @@ int main(int argc, char *argv[]) {
|
|||
assert(pool_a && pool_b && pool_c);
|
||||
|
||||
blocks[0] = pa_memblock_new_fixed(pool_a, (void*) txt, sizeof(txt), 1);
|
||||
|
||||
blocks[1] = pa_memblock_new(pool_a, sizeof(txt));
|
||||
snprintf(blocks[1]->data, blocks[1]->length, "%s", txt);
|
||||
x = pa_memblock_acquire(blocks[1]);
|
||||
snprintf(x, pa_memblock_get_length(blocks[1]), "%s", txt);
|
||||
pa_memblock_release(blocks[1]);
|
||||
|
||||
blocks[2] = pa_memblock_new_pool(pool_a, sizeof(txt));
|
||||
snprintf(blocks[2]->data, blocks[2]->length, "%s", txt);
|
||||
x = pa_memblock_acquire(blocks[2]);
|
||||
snprintf(x, pa_memblock_get_length(blocks[2]), "%s", txt);
|
||||
pa_memblock_release(blocks[1]);
|
||||
|
||||
blocks[3] = pa_memblock_new_malloced(pool_a, pa_xstrdup(txt), sizeof(txt));
|
||||
blocks[4] = NULL;
|
||||
|
||||
|
|
@ -130,14 +138,18 @@ int main(int argc, char *argv[]) {
|
|||
|
||||
mb_c = pa_memimport_get(import_c, id, shm_id, offset, size);
|
||||
assert(mb_c);
|
||||
printf("1 data=%s\n", (char*) mb_c->data);
|
||||
x = pa_memblock_acquire(mb_c);
|
||||
printf("1 data=%s\n", x);
|
||||
pa_memblock_release(mb_c);
|
||||
|
||||
print_stats(pool_a, "A");
|
||||
print_stats(pool_b, "B");
|
||||
print_stats(pool_c, "C");
|
||||
|
||||
pa_memexport_free(export_b);
|
||||
printf("2 data=%s\n", (char*) mb_c->data);
|
||||
x = pa_memblock_acquire(mb_c);
|
||||
printf("2 data=%s\n", x);
|
||||
pa_memblock_release(mb_c);
|
||||
pa_memblock_unref(mb_c);
|
||||
|
||||
pa_memimport_free(import_b);
|
||||
|
|
|
|||
|
|
@ -131,8 +131,10 @@ int main(int argc, char *argv[]) {
|
|||
if (pa_memblockq_peek(bq, &out) < 0)
|
||||
break;
|
||||
|
||||
for (e = (char*) out.memblock->data + out.index, n = 0; n < out.length; n++)
|
||||
p = pa_memblock_acquire(out.memblock);
|
||||
for (e = (char*) p + out.index, n = 0; n < out.length; n++)
|
||||
printf("%c", *e);
|
||||
pa_memblock_release(out.memblock);
|
||||
|
||||
pa_memblock_unref(out.memblock);
|
||||
pa_memblockq_drop(bq, &out, out.length);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue