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:
Lennart Poettering 2007-06-11 12:08:37 +00:00
parent 6aeec56708
commit 00da37f2c4
72 changed files with 4389 additions and 1767 deletions

View file

@ -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 += \

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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);

View file

@ -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);
}

View file

@ -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);

View file

@ -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);

View file

@ -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);
}

View file

@ -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;

View file

@ -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);

View file

@ -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;
}

View file

@ -116,6 +116,7 @@ struct pa_stream {
uint32_t requested_bytes;
pa_memchunk peek_memchunk;
void *peek_data;
pa_memblockq *record_memblockq;
int corked;

View file

@ -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;

View file

@ -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
View 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
View 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
View 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
View 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

View file

@ -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);
}

View file

@ -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));
}

View file

@ -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);
}

View file

@ -24,9 +24,6 @@
USA.
***/
typedef enum pa_mixer {
PA_MIXER_SOFTWARE,
PA_MIXER_HARDWARE
} pa_mixer_t;
/* FIXME: Remove this shit */
#endif

View file

@ -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);

View file

@ -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);

View file

@ -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);

View file

@ -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

View file

@ -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);

View file

@ -32,6 +32,7 @@
#include <string.h>
#include <pulse/xmalloc.h>
#include <pulsecore/macro.h>
#include "idxset.h"

View file

@ -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
View 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

View file

@ -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 */

View file

@ -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;
}

View file

@ -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);

View file

@ -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);
}

View file

@ -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
View 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
View 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

View file

@ -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
View 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
View 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

View file

@ -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

View file

@ -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);

View file

@ -122,7 +122,5 @@ int pa_play_memblockq(
si->userdata = q;
pa_sink_notify(si->sink);
return 0;
}

View file

@ -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;
}

View file

@ -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);

View file

@ -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;
}

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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

View file

@ -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;
}

View file

@ -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);
}

View file

@ -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,

View 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
View 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

View file

@ -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;
}

View file

@ -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

View file

@ -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;
}
}

View file

@ -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

View file

@ -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);

View file

@ -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);

View file

@ -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;
}

View file

@ -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

View file

@ -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;
}
}

View file

@ -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

View file

@ -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
View 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
View 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;
}

View file

@ -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);

View file

@ -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);

View file

@ -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);