merge 'lennart' branch back into trunk.

git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@1971 fefdeb5f-60dc-0310-8127-8f9354f1896f
This commit is contained in:
Lennart Poettering 2007-10-28 19:13:50 +00:00
parent 6687dd0131
commit a67c21f093
294 changed files with 79057 additions and 11614 deletions

View file

@ -33,6 +33,7 @@
#include <pulse/xmalloc.h>
#include <pulsecore/log.h>
#include <pulsecore/macro.h>
#include "alsa-util.h"
@ -42,7 +43,6 @@ struct pa_alsa_fdlist {
/* This is a temporary buffer used to avoid lots of mallocs */
struct pollfd *work_fds;
snd_pcm_t *pcm;
snd_mixer_t *mixer;
pa_mainloop_api *m;
@ -56,11 +56,16 @@ struct pa_alsa_fdlist {
};
static void io_cb(pa_mainloop_api*a, pa_io_event* e, PA_GCC_UNUSED int fd, pa_io_event_flags_t events, void *userdata) {
struct pa_alsa_fdlist *fdl = (struct pa_alsa_fdlist*)userdata;
struct pa_alsa_fdlist *fdl = userdata;
int err, i;
unsigned short revents;
assert(a && fdl && (fdl->pcm || fdl->mixer) && fdl->fds && fdl->work_fds);
pa_assert(a);
pa_assert(fdl);
pa_assert(fdl->mixer);
pa_assert(fdl->fds);
pa_assert(fdl->work_fds);
if (fdl->polled)
return;
@ -69,7 +74,7 @@ static void io_cb(pa_mainloop_api*a, pa_io_event* e, PA_GCC_UNUSED int fd, pa_io
memcpy(fdl->work_fds, fdl->fds, sizeof(struct pollfd) * fdl->num_fds);
for (i = 0;i < fdl->num_fds;i++) {
for (i = 0;i < fdl->num_fds; i++) {
if (e == fdl->ios[i]) {
if (events & PA_IO_EVENT_INPUT)
fdl->work_fds[i].revents |= POLLIN;
@ -83,63 +88,46 @@ static void io_cb(pa_mainloop_api*a, pa_io_event* e, PA_GCC_UNUSED int fd, pa_io
}
}
assert(i != fdl->num_fds);
pa_assert(i != fdl->num_fds);
if (fdl->pcm)
err = snd_pcm_poll_descriptors_revents(fdl->pcm, fdl->work_fds, fdl->num_fds, &revents);
else
err = snd_mixer_poll_descriptors_revents(fdl->mixer, fdl->work_fds, fdl->num_fds, &revents);
if (err < 0) {
pa_log_error("Unable to get poll revent: %s",
snd_strerror(err));
if ((err = snd_mixer_poll_descriptors_revents(fdl->mixer, fdl->work_fds, fdl->num_fds, &revents)) < 0) {
pa_log_error("Unable to get poll revent: %s", snd_strerror(err));
return;
}
a->defer_enable(fdl->defer, 1);
if (revents) {
if (fdl->pcm)
fdl->cb(fdl->userdata);
else
snd_mixer_handle_events(fdl->mixer);
}
if (revents)
snd_mixer_handle_events(fdl->mixer);
}
static void defer_cb(pa_mainloop_api*a, PA_GCC_UNUSED pa_defer_event* e, void *userdata) {
struct pa_alsa_fdlist *fdl = (struct pa_alsa_fdlist*)userdata;
struct pa_alsa_fdlist *fdl = userdata;
int num_fds, i, err;
struct pollfd *temp;
assert(a && fdl && (fdl->pcm || fdl->mixer));
pa_assert(a);
pa_assert(fdl);
pa_assert(fdl->mixer);
a->defer_enable(fdl->defer, 0);
if (fdl->pcm)
num_fds = snd_pcm_poll_descriptors_count(fdl->pcm);
else
num_fds = snd_mixer_poll_descriptors_count(fdl->mixer);
assert(num_fds > 0);
num_fds = snd_mixer_poll_descriptors_count(fdl->mixer);
pa_assert(num_fds > 0);
if (num_fds != fdl->num_fds) {
if (fdl->fds)
pa_xfree(fdl->fds);
if (fdl->work_fds)
pa_xfree(fdl->work_fds);
fdl->fds = pa_xmalloc0(sizeof(struct pollfd) * num_fds);
fdl->work_fds = pa_xmalloc(sizeof(struct pollfd) * num_fds);
fdl->fds = pa_xnew0(struct pollfd, num_fds);
fdl->work_fds = pa_xnew(struct pollfd, num_fds);
}
memset(fdl->work_fds, 0, sizeof(struct pollfd) * num_fds);
if (fdl->pcm)
err = snd_pcm_poll_descriptors(fdl->pcm, fdl->work_fds, num_fds);
else
err = snd_mixer_poll_descriptors(fdl->mixer, fdl->work_fds, num_fds);
if (err < 0) {
pa_log_error("Unable to get poll descriptors: %s",
snd_strerror(err));
if ((err = snd_mixer_poll_descriptors(fdl->mixer, fdl->work_fds, num_fds)) < 0) {
pa_log_error("Unable to get poll descriptors: %s", snd_strerror(err));
return;
}
@ -149,18 +137,18 @@ static void defer_cb(pa_mainloop_api*a, PA_GCC_UNUSED pa_defer_event* e, void *u
return;
if (fdl->ios) {
for (i = 0;i < fdl->num_fds;i++)
for (i = 0; i < fdl->num_fds; i++)
a->io_free(fdl->ios[i]);
if (num_fds != fdl->num_fds) {
pa_xfree(fdl->ios);
fdl->ios = pa_xmalloc(sizeof(pa_io_event*) * num_fds);
assert(fdl->ios);
fdl->ios = NULL;
}
} else {
fdl->ios = pa_xmalloc(sizeof(pa_io_event*) * num_fds);
assert(fdl->ios);
}
if (!fdl->ios)
fdl->ios = pa_xnew(pa_io_event*, num_fds);
/* Swap pointers */
temp = fdl->work_fds;
fdl->work_fds = fdl->fds;
@ -168,47 +156,41 @@ static void defer_cb(pa_mainloop_api*a, PA_GCC_UNUSED pa_defer_event* e, void *u
fdl->num_fds = num_fds;
for (i = 0;i < num_fds;i++) {
for (i = 0;i < num_fds;i++)
fdl->ios[i] = a->io_new(a, fdl->fds[i].fd,
((fdl->fds[i].events & POLLIN) ? PA_IO_EVENT_INPUT : 0) |
((fdl->fds[i].events & POLLOUT) ? PA_IO_EVENT_OUTPUT : 0),
io_cb, fdl);
assert(fdl->ios[i]);
}
}
struct pa_alsa_fdlist *pa_alsa_fdlist_new(void) {
struct pa_alsa_fdlist *fdl;
fdl = pa_xmalloc(sizeof(struct pa_alsa_fdlist));
fdl = pa_xnew0(struct pa_alsa_fdlist, 1);
fdl->num_fds = 0;
fdl->fds = NULL;
fdl->work_fds = NULL;
fdl->pcm = NULL;
fdl->mixer = NULL;
fdl->m = NULL;
fdl->defer = NULL;
fdl->ios = NULL;
fdl->polled = 0;
return fdl;
}
void pa_alsa_fdlist_free(struct pa_alsa_fdlist *fdl) {
assert(fdl);
pa_assert(fdl);
if (fdl->defer) {
assert(fdl->m);
pa_assert(fdl->m);
fdl->m->defer_free(fdl->defer);
}
if (fdl->ios) {
int i;
assert(fdl->m);
pa_assert(fdl->m);
for (i = 0;i < fdl->num_fds;i++)
fdl->m->io_free(fdl->ios[i]);
pa_xfree(fdl->ios);
@ -222,29 +204,15 @@ void pa_alsa_fdlist_free(struct pa_alsa_fdlist *fdl) {
pa_xfree(fdl);
}
int pa_alsa_fdlist_init_pcm(struct pa_alsa_fdlist *fdl, snd_pcm_t *pcm_handle, pa_mainloop_api* m, void (*cb)(void *userdata), void *userdata) {
assert(fdl && pcm_handle && m && !fdl->m && cb);
fdl->pcm = pcm_handle;
fdl->m = m;
fdl->defer = m->defer_new(m, defer_cb, fdl);
assert(fdl->defer);
fdl->cb = cb;
fdl->userdata = userdata;
return 0;
}
int pa_alsa_fdlist_init_mixer(struct pa_alsa_fdlist *fdl, snd_mixer_t *mixer_handle, pa_mainloop_api* m) {
assert(fdl && mixer_handle && m && !fdl->m);
int pa_alsa_fdlist_set_mixer(struct pa_alsa_fdlist *fdl, snd_mixer_t *mixer_handle, pa_mainloop_api* m) {
pa_assert(fdl);
pa_assert(mixer_handle);
pa_assert(m);
pa_assert(!fdl->m);
fdl->mixer = mixer_handle;
fdl->m = m;
fdl->defer = m->defer_new(m, defer_cb, fdl);
assert(fdl->defer);
return 0;
}
@ -274,8 +242,8 @@ static int set_format(snd_pcm_t *pcm_handle, snd_pcm_hw_params_t *hwparams, pa_s
int i, ret;
assert(pcm_handle);
assert(f);
pa_assert(pcm_handle);
pa_assert(f);
if ((ret = snd_pcm_hw_params_set_format(pcm_handle, hwparams, format_trans[*f])) >= 0)
return ret;
@ -308,7 +276,7 @@ try_auto:
/* Set the hardware parameters of the given ALSA device. Returns the
* selected fragment settings in *period and *period_size */
int pa_alsa_set_hw_params(snd_pcm_t *pcm_handle, pa_sample_spec *ss, uint32_t *periods, snd_pcm_uframes_t *period_size) {
int pa_alsa_set_hw_params(snd_pcm_t *pcm_handle, pa_sample_spec *ss, uint32_t *periods, snd_pcm_uframes_t *period_size, int *use_mmap) {
int ret = -1;
snd_pcm_uframes_t buffer_size;
unsigned int r = ss->rate;
@ -316,17 +284,32 @@ int pa_alsa_set_hw_params(snd_pcm_t *pcm_handle, pa_sample_spec *ss, uint32_t *p
pa_sample_format_t f = ss->format;
snd_pcm_hw_params_t *hwparams;
assert(pcm_handle);
assert(ss);
assert(periods);
assert(period_size);
pa_assert(pcm_handle);
pa_assert(ss);
pa_assert(periods);
pa_assert(period_size);
snd_pcm_hw_params_alloca(&hwparams);
buffer_size = *periods * *period_size;
if ((ret = snd_pcm_hw_params_malloc(&hwparams)) < 0 ||
(ret = snd_pcm_hw_params_any(pcm_handle, hwparams)) < 0 ||
(ret = snd_pcm_hw_params_set_rate_resample(pcm_handle, hwparams, 0)) < 0 ||
(ret = snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
if ((ret = snd_pcm_hw_params_any(pcm_handle, hwparams)) < 0 ||
(ret = snd_pcm_hw_params_set_rate_resample(pcm_handle, hwparams, 0)) < 0)
goto finish;
if (use_mmap && *use_mmap) {
if ((ret = snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_MMAP_INTERLEAVED)) < 0) {
/* mmap() didn't work, fall back to interleaved */
if ((ret = snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
goto finish;
if (use_mmap)
*use_mmap = 0;
}
} else if ((ret = snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
goto finish;
if ((ret = set_format(pcm_handle, hwparams, &f)) < 0)
@ -346,7 +329,7 @@ int pa_alsa_set_hw_params(snd_pcm_t *pcm_handle, pa_sample_spec *ss, uint32_t *p
goto finish;
if (ss->rate != r) {
pa_log_warn("device doesn't support %u Hz, changed to %u Hz.", ss->rate, r);
pa_log_warn("Device %s doesn't support %u Hz, changed to %u Hz.", snd_pcm_name(pcm_handle), ss->rate, r);
/* If the sample rate deviates too much, we need to resample */
if (r < ss->rate*.95 || r > ss->rate*1.05)
@ -354,12 +337,12 @@ int pa_alsa_set_hw_params(snd_pcm_t *pcm_handle, pa_sample_spec *ss, uint32_t *p
}
if (ss->channels != c) {
pa_log_warn("device doesn't support %u channels, changed to %u.", ss->channels, c);
pa_log_warn("Device %s doesn't support %u channels, changed to %u.", snd_pcm_name(pcm_handle), ss->channels, c);
ss->channels = c;
}
if (ss->format != f) {
pa_log_warn("device doesn't support sample format %s, changed to %s.", pa_sample_format_to_string(ss->format), pa_sample_format_to_string(f));
pa_log_warn("Device %s doesn't support sample format %s, changed to %s.", snd_pcm_name(pcm_handle), pa_sample_format_to_string(ss->format), pa_sample_format_to_string(f));
ss->format = f;
}
@ -370,24 +353,54 @@ int pa_alsa_set_hw_params(snd_pcm_t *pcm_handle, pa_sample_spec *ss, uint32_t *p
(ret = snd_pcm_hw_params_get_period_size(hwparams, period_size, NULL)) < 0)
goto finish;
assert(buffer_size > 0);
assert(*period_size > 0);
pa_assert(buffer_size > 0);
pa_assert(*period_size > 0);
*periods = buffer_size / *period_size;
assert(*periods > 0);
pa_assert(*periods > 0);
ret = 0;
finish:
if (hwparams)
snd_pcm_hw_params_free(hwparams);
return ret;
}
int pa_alsa_set_sw_params(snd_pcm_t *pcm) {
snd_pcm_sw_params_t *swparams;
int err;
pa_assert(pcm);
snd_pcm_sw_params_alloca(&swparams);
if ((err = snd_pcm_sw_params_current(pcm, swparams) < 0)) {
pa_log_warn("Unable to determine current swparams: %s\n", snd_strerror(err));
return err;
}
if ((err = snd_pcm_sw_params_set_stop_threshold(pcm, swparams, (snd_pcm_uframes_t) -1)) < 0) {
pa_log_warn("Unable to set stop threshold: %s\n", snd_strerror(err));
return err;
}
if ((err = snd_pcm_sw_params_set_start_threshold(pcm, swparams, (snd_pcm_uframes_t) -1)) < 0) {
pa_log_warn("Unable to set start threshold: %s\n", snd_strerror(err));
return err;
}
if ((err = snd_pcm_sw_params(pcm, swparams)) < 0) {
pa_log_warn("Unable to set sw params: %s\n", snd_strerror(err));
return err;
}
return 0;
}
int pa_alsa_prepare_mixer(snd_mixer_t *mixer, const char *dev) {
int err;
assert(mixer && dev);
pa_assert(mixer);
pa_assert(dev);
if ((err = snd_mixer_attach(mixer, dev)) < 0) {
pa_log_warn("Unable to attach to mixer %s: %s", dev, snd_strerror(err));
@ -410,10 +423,11 @@ int pa_alsa_prepare_mixer(snd_mixer_t *mixer, const char *dev) {
snd_mixer_elem_t *pa_alsa_find_elem(snd_mixer_t *mixer, const char *name, const char *fallback) {
snd_mixer_elem_t *elem;
snd_mixer_selem_id_t *sid = NULL;
snd_mixer_selem_id_alloca(&sid);
assert(mixer);
assert(name);
pa_assert(mixer);
pa_assert(name);
snd_mixer_selem_id_set_name(sid, name);

View file

@ -32,15 +32,14 @@
#include <pulse/channelmap.h>
struct pa_alsa_fdlist;
typedef struct pa_alsa_fdlist pa_alsa_fdlist;
struct pa_alsa_fdlist *pa_alsa_fdlist_new(void);
void pa_alsa_fdlist_free(struct pa_alsa_fdlist *fdl);
int pa_alsa_fdlist_set_mixer(struct pa_alsa_fdlist *fdl, snd_mixer_t *mixer_handle, pa_mainloop_api* m);
int pa_alsa_fdlist_init_pcm(struct pa_alsa_fdlist *fdl, snd_pcm_t *pcm_handle, pa_mainloop_api* m, void (*cb)(void *userdata), void *userdata);
int pa_alsa_fdlist_init_mixer(struct pa_alsa_fdlist *fdl, snd_mixer_t *mixer_handle, pa_mainloop_api* m);
int pa_alsa_set_hw_params(snd_pcm_t *pcm_handle, pa_sample_spec *ss, uint32_t *periods, snd_pcm_uframes_t *period_size);
int pa_alsa_set_hw_params(snd_pcm_t *pcm_handle, pa_sample_spec *ss, uint32_t *periods, snd_pcm_uframes_t *period_size, int *use_mmap);
int pa_alsa_set_sw_params(snd_pcm_t *pcm);
int pa_alsa_prepare_mixer(snd_mixer_t *mixer, const char *dev);
snd_mixer_elem_t *pa_alsa_find_elem(snd_mixer_t *mixer, const char *name, const char *fallback);

View file

@ -26,25 +26,25 @@
#include <config.h>
#endif
#include <assert.h>
#include <pulsecore/log.h>
#include <pulsecore/props.h>
#include <pulse/xmalloc.h>
#include <pulse/timeval.h>
#include <pulsecore/log.h>
#include <pulsecore/props.h>
#include "dbus-util.h"
struct pa_dbus_connection {
int refcount;
PA_REFCNT_DECLARE;
pa_core *core;
DBusConnection *connection;
const char *property_name;
pa_defer_event* dispatch_event;
};
static void dispatch_cb(pa_mainloop_api *ea, pa_defer_event *ev, void *userdata)
{
DBusConnection *conn = (DBusConnection *) userdata;
static void dispatch_cb(pa_mainloop_api *ea, pa_defer_event *ev, void *userdata) {
DBusConnection *conn = userdata;
if (dbus_connection_dispatch(conn) == DBUS_DISPATCH_COMPLETE) {
/* no more data to process, disable the deferred */
ea->defer_enable(ev, 0);
@ -52,14 +52,17 @@ static void dispatch_cb(pa_mainloop_api *ea, pa_defer_event *ev, void *userdata)
}
/* DBusDispatchStatusFunction callback for the pa mainloop */
static void dispatch_status(DBusConnection *conn, DBusDispatchStatus status,
void *userdata)
{
pa_dbus_connection *c = (pa_dbus_connection*) userdata;
static void dispatch_status(DBusConnection *conn, DBusDispatchStatus status, void *userdata) {
pa_dbus_connection *c = userdata;
pa_assert(c);
switch(status) {
case DBUS_DISPATCH_COMPLETE:
c->core->mainloop->defer_enable(c->dispatch_event, 0);
break;
case DBUS_DISPATCH_DATA_REMAINS:
case DBUS_DISPATCH_NEED_MEMORY:
default:
@ -68,11 +71,13 @@ static void dispatch_status(DBusConnection *conn, DBusDispatchStatus status,
}
}
static pa_io_event_flags_t
get_watch_flags(DBusWatch *watch)
{
unsigned int flags = dbus_watch_get_flags(watch);
pa_io_event_flags_t events = PA_IO_EVENT_HANGUP | PA_IO_EVENT_ERROR;
static pa_io_event_flags_t get_watch_flags(DBusWatch *watch) {
unsigned int flags;
pa_io_event_flags_t events = 0;
pa_assert(watch);
flags = dbus_watch_get_flags(watch);
/* no watch flags for disabled watches */
if (!dbus_watch_get_enabled(watch))
@ -83,21 +88,22 @@ get_watch_flags(DBusWatch *watch)
if (flags & DBUS_WATCH_WRITABLE)
events |= PA_IO_EVENT_OUTPUT;
return events;
return events | PA_IO_EVENT_HANGUP | PA_IO_EVENT_ERROR;
}
/* pa_io_event_cb_t IO event handler */
static void handle_io_event(PA_GCC_UNUSED pa_mainloop_api *ea, pa_io_event *e,
int fd, pa_io_event_flags_t events, void *userdata)
{
static void handle_io_event(PA_GCC_UNUSED pa_mainloop_api *ea, pa_io_event *e, int fd, pa_io_event_flags_t events, void *userdata) {
unsigned int flags = 0;
DBusWatch *watch = (DBusWatch*) userdata;
DBusWatch *watch = userdata;
assert(fd == dbus_watch_get_fd(watch));
#if HAVE_DBUS_WATCH_GET_UNIX_FD
pa_assert(fd == dbus_watch_get_unix_fd(watch));
#else
pa_assert(fd == dbus_watch_get_fd(watch));
#endif
if (!dbus_watch_get_enabled(watch)) {
pa_log_warn("Asked to handle disabled watch: %p %i",
(void *) watch, fd);
pa_log_warn("Asked to handle disabled watch: %p %i", (void*) watch, fd);
return;
}
@ -114,10 +120,8 @@ static void handle_io_event(PA_GCC_UNUSED pa_mainloop_api *ea, pa_io_event *e,
}
/* pa_time_event_cb_t timer event handler */
static void handle_time_event(pa_mainloop_api *ea, pa_time_event* e,
const struct timeval *tv, void *userdata)
{
DBusTimeout *timeout = (DBusTimeout*) userdata;
static void handle_time_event(pa_mainloop_api *ea, pa_time_event* e, const struct timeval *tv, void *userdata) {
DBusTimeout *timeout = userdata;
if (dbus_timeout_get_enabled(timeout)) {
struct timeval next = *tv;
@ -130,103 +134,154 @@ static void handle_time_event(pa_mainloop_api *ea, pa_time_event* e,
}
/* DBusAddWatchFunction callback for pa mainloop */
static dbus_bool_t add_watch(DBusWatch *watch, void *data)
{
static dbus_bool_t add_watch(DBusWatch *watch, void *data) {
pa_core *c = PA_CORE(data);
pa_io_event *ev;
pa_core *c = (pa_core*) data;
ev = c->mainloop->io_new(c->mainloop, dbus_watch_get_fd(watch),
get_watch_flags(watch),
handle_io_event, (void*) watch);
if (NULL == ev)
return FALSE;
pa_assert(watch);
pa_assert(c);
/* dbus_watch_set_data(watch, (void*) ev, c->mainloop->io_free); */
dbus_watch_set_data(watch, (void*) ev, NULL);
ev = c->mainloop->io_new(
c->mainloop,
#if HAVE_DBUS_WATCH_GET_UNIX_FD
dbus_watch_get_unix_fd(watch),
#else
dbus_watch_get_fd(watch),
#endif
get_watch_flags(watch), handle_io_event, watch);
dbus_watch_set_data(watch, ev, NULL);
return TRUE;
}
/* DBusRemoveWatchFunction callback for pa mainloop */
static void remove_watch(DBusWatch *watch, void *data)
{
pa_core *c = (pa_core*) data;
pa_io_event *ev = (pa_io_event*) dbus_watch_get_data(watch);
static void remove_watch(DBusWatch *watch, void *data) {
pa_core *c = PA_CORE(data);
pa_io_event *ev;
/* free the event */
if (NULL != ev)
pa_assert(watch);
pa_assert(c);
if ((ev = dbus_watch_get_data(watch)))
c->mainloop->io_free(ev);
}
/* DBusWatchToggledFunction callback for pa mainloop */
static void toggle_watch(DBusWatch *watch, void *data)
{
pa_core *c = (pa_core*) data;
pa_io_event *ev = (pa_io_event*) dbus_watch_get_data(watch);
static void toggle_watch(DBusWatch *watch, void *data) {
pa_core *c = PA_CORE(data);
pa_io_event *ev;
pa_assert(watch);
pa_core_assert_ref(c);
pa_assert_se(ev = dbus_watch_get_data(watch));
/* get_watch_flags() checks if the watch is enabled */
c->mainloop->io_enable(ev, get_watch_flags(watch));
}
/* DBusAddTimeoutFunction callback for pa mainloop */
static dbus_bool_t add_timeout(DBusTimeout *timeout, void *data)
{
struct timeval tv;
static dbus_bool_t add_timeout(DBusTimeout *timeout, void *data) {
pa_core *c = PA_CORE(data);
pa_time_event *ev;
pa_core *c = (pa_core*) data;
struct timeval tv;
pa_assert(timeout);
pa_assert(c);
if (!dbus_timeout_get_enabled(timeout))
return FALSE;
if (!pa_gettimeofday(&tv))
return -1;
pa_gettimeofday(&tv);
pa_timeval_add(&tv, dbus_timeout_get_interval(timeout) * 1000);
ev = c->mainloop->time_new(c->mainloop, &tv, handle_time_event,
(void*) timeout);
if (NULL == ev)
return FALSE;
ev = c->mainloop->time_new(c->mainloop, &tv, handle_time_event, timeout);
/* dbus_timeout_set_data(timeout, (void*) ev, c->mainloop->time_free); */
dbus_timeout_set_data(timeout, (void*) ev, NULL);
dbus_timeout_set_data(timeout, ev, NULL);
return TRUE;
}
/* DBusRemoveTimeoutFunction callback for pa mainloop */
static void remove_timeout(DBusTimeout *timeout, void *data)
{
pa_core *c = (pa_core*) data;
pa_time_event *ev = (pa_time_event*) dbus_timeout_get_data(timeout);
static void remove_timeout(DBusTimeout *timeout, void *data) {
pa_core *c = PA_CORE(data);
pa_time_event *ev;
/* free the event */
if (NULL != ev)
pa_assert(timeout);
pa_assert(c);
if ((ev = dbus_timeout_get_data(timeout)))
c->mainloop->time_free(ev);
}
/* DBusTimeoutToggledFunction callback for pa mainloop */
static void toggle_timeout(DBusTimeout *timeout, void *data)
{
struct timeval tv;
pa_core *c = (pa_core*) data;
pa_time_event *ev = (pa_time_event*) dbus_timeout_get_data(timeout);
static void toggle_timeout(DBusTimeout *timeout, void *data) {
pa_core *c = PA_CORE(data);
pa_time_event *ev;
pa_assert(timeout);
pa_assert(c);
pa_assert_se(ev = dbus_timeout_get_data(timeout));
if (dbus_timeout_get_enabled(timeout)) {
struct timeval tv;
pa_gettimeofday(&tv);
pa_timeval_add(&tv, dbus_timeout_get_interval(timeout) * 1000);
c->mainloop->time_restart(ev, &tv);
} else {
/* disable the timeout */
} else
c->mainloop->time_restart(ev, NULL);
}
}
static void
pa_dbus_connection_free(pa_dbus_connection *c)
{
assert(c);
assert(!dbus_connection_get_is_connected(c->connection));
static void wakeup_main(void *userdata) {
pa_dbus_connection *c = userdata;
pa_assert(c);
/* this will wakeup the mainloop and dispatch events, although
* it may not be the cleanest way of accomplishing it */
c->core->mainloop->defer_enable(c->dispatch_event, 1);
}
static pa_dbus_connection* pa_dbus_connection_new(pa_core* c, DBusConnection *conn, const char* name) {
pa_dbus_connection *pconn;
pconn = pa_xnew(pa_dbus_connection, 1);
PA_REFCNT_INIT(pconn);
pconn->core = c;
pconn->property_name = name;
pconn->connection = conn;
pconn->dispatch_event = c->mainloop->defer_new(c->mainloop, dispatch_cb, conn);
pa_property_set(c, name, pconn);
return pconn;
}
DBusConnection* pa_dbus_connection_get(pa_dbus_connection *c){
pa_assert(c);
pa_assert(PA_REFCNT_VALUE(c) > 0);
pa_assert(c->connection);
return c->connection;
}
void pa_dbus_connection_unref(pa_dbus_connection *c) {
pa_assert(c);
pa_assert(PA_REFCNT_VALUE(c) > 0);
if (PA_REFCNT_DEC(c) > 0)
return;
if (dbus_connection_get_is_connected(c->connection)) {
dbus_connection_close(c->connection);
/* must process remaining messages, bit of a kludge to handle
* both unload and shutdown */
while (dbus_connection_read_write_dispatch(c->connection, -1));
}
/* already disconnected, just free */
pa_property_remove(c->core, c->property_name);
@ -235,113 +290,39 @@ pa_dbus_connection_free(pa_dbus_connection *c)
pa_xfree(c);
}
static void
wakeup_main(void *userdata)
{
pa_dbus_connection *c = (pa_dbus_connection*) userdata;
/* this will wakeup the mainloop and dispatch events, although
* it may not be the cleanest way of accomplishing it */
c->core->mainloop->defer_enable(c->dispatch_event, 1);
}
pa_dbus_connection* pa_dbus_connection_ref(pa_dbus_connection *c) {
pa_assert(c);
pa_assert(PA_REFCNT_VALUE(c) > 0);
static pa_dbus_connection* pa_dbus_connection_new(pa_core* c, DBusConnection *conn, const char* name)
{
pa_dbus_connection *pconn = pa_xnew(pa_dbus_connection, 1);
pconn->refcount = 1;
pconn->core = c;
pconn->property_name = name;
pconn->connection = conn;
pconn->dispatch_event = c->mainloop->defer_new(c->mainloop, dispatch_cb,
(void*) conn);
pa_property_set(c, name, pconn);
return pconn;
}
DBusConnection* pa_dbus_connection_get(pa_dbus_connection *c)
{
assert(c && c->connection);
return c->connection;
}
void pa_dbus_connection_unref(pa_dbus_connection *c)
{
assert(c);
/* non-zero refcount, still outstanding refs */
if (--(c->refcount))
return;
/* refcount is zero */
if (dbus_connection_get_is_connected(c->connection)) {
/* disconnect as we have no more internal references */
dbus_connection_close(c->connection);
/* must process remaining messages, bit of a kludge to
* handle both unload and shutdown */
while(dbus_connection_read_write_dispatch(c->connection, -1));
}
pa_dbus_connection_free(c);
}
pa_dbus_connection* pa_dbus_connection_ref(pa_dbus_connection *c)
{
assert(c);
++(c->refcount);
PA_REFCNT_INC(c);
return c;
}
pa_dbus_connection* pa_dbus_bus_get(pa_core *c, DBusBusType type,
DBusError *error)
{
const char* name;
pa_dbus_connection* pa_dbus_bus_get(pa_core *c, DBusBusType type, DBusError *error) {
static const char *const prop_name[] = {
[DBUS_BUS_SESSION] = "dbus-connection-session",
[DBUS_BUS_SYSTEM] = "dbus-connection-system",
[DBUS_BUS_STARTER] = "dbus-connection-starter"
};
DBusConnection *conn;
pa_dbus_connection *pconn;
switch (type) {
case DBUS_BUS_SYSTEM:
name = "dbus-connection-system";
break;
case DBUS_BUS_SESSION:
name = "dbus-connection-session";
break;
case DBUS_BUS_STARTER:
name = "dbus-connection-starter";
break;
default:
assert(0); /* never reached */
break;
}
pa_assert(type == DBUS_BUS_SYSTEM || type == DBUS_BUS_SESSION || type == DBUS_BUS_STARTER);
if ((pconn = pa_property_get(c, name)))
if ((pconn = pa_property_get(c, prop_name[type])))
return pa_dbus_connection_ref(pconn);
/* else */
conn = dbus_bus_get_private(type, error);
if (conn == NULL || dbus_error_is_set(error)) {
if (!(conn = dbus_bus_get_private(type, error)))
return NULL;
}
pconn = pa_dbus_connection_new(c, conn, name);
pconn = pa_dbus_connection_new(c, conn, prop_name[type]);
/* don't exit on disconnect */
dbus_connection_set_exit_on_disconnect(conn, FALSE);
/* set up the DBUS call backs */
dbus_connection_set_dispatch_status_function(conn, dispatch_status,
(void*) pconn, NULL);
dbus_connection_set_watch_functions(conn,
add_watch,
remove_watch,
toggle_watch,
(void*) c, NULL);
dbus_connection_set_timeout_functions(conn,
add_timeout,
remove_timeout,
toggle_timeout,
(void*) c, NULL);
dbus_connection_set_dispatch_status_function(conn, dispatch_status, pconn, NULL);
dbus_connection_set_watch_functions(conn, add_watch, remove_watch, toggle_watch, c, NULL);
dbus_connection_set_timeout_functions(conn, add_timeout, remove_timeout, toggle_timeout, c, NULL);
dbus_connection_set_wakeup_main_function(conn, wakeup_main, pconn, NULL);
return pconn;

View file

@ -32,6 +32,8 @@
#include <gconf/gconf-client.h>
#include <glib.h>
#include <pulsecore/core-util.h>
#define PA_GCONF_ROOT "/system/pulseaudio"
#define PA_GCONF_PATH_MODULES PA_GCONF_ROOT"/modules"
@ -40,13 +42,13 @@ static void handle_module(GConfClient *client, const char *name) {
gboolean enabled, locked;
int i;
snprintf(p, sizeof(p), PA_GCONF_PATH_MODULES"/%s/locked", name);
pa_snprintf(p, sizeof(p), PA_GCONF_PATH_MODULES"/%s/locked", name);
locked = gconf_client_get_bool(client, p, FALSE);
if (locked)
return;
snprintf(p, sizeof(p), PA_GCONF_PATH_MODULES"/%s/enabled", name);
pa_snprintf(p, sizeof(p), PA_GCONF_PATH_MODULES"/%s/enabled", name);
enabled = gconf_client_get_bool(client, p, FALSE);
printf("%c%s%c", enabled ? '+' : '-', name, 0);
@ -56,11 +58,11 @@ static void handle_module(GConfClient *client, const char *name) {
for (i = 0; i < 10; i++) {
gchar *n, *a;
snprintf(p, sizeof(p), PA_GCONF_PATH_MODULES"/%s/name%i", name, i);
pa_snprintf(p, sizeof(p), PA_GCONF_PATH_MODULES"/%s/name%i", name, i);
if (!(n = gconf_client_get_string(client, p, NULL)) || !*n)
break;
snprintf(p, sizeof(p), PA_GCONF_PATH_MODULES"/%s/args%i", name, i);
pa_snprintf(p, sizeof(p), PA_GCONF_PATH_MODULES"/%s/args%i", name, i);
a = gconf_client_get_string(client, p, NULL);
printf("%s%c%s%c", n, 0, a ? a : "", 0);

View file

@ -25,7 +25,6 @@
#include <config.h>
#endif
#include <assert.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
@ -34,6 +33,7 @@
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <dirent.h>
#ifdef HAVE_SYS_PRCTL_H
#include <sys/prctl.h>
@ -95,7 +95,7 @@ struct userdata {
static int fill_buf(struct userdata *u) {
ssize_t r;
assert(u);
pa_assert(u);
if (u->buf_fill >= BUF_MAX) {
pa_log("read buffer overflow");
@ -111,21 +111,21 @@ static int fill_buf(struct userdata *u) {
static int read_byte(struct userdata *u) {
int ret;
assert(u);
pa_assert(u);
if (u->buf_fill < 1)
if (fill_buf(u) < 0)
return -1;
ret = u->buf[0];
assert(u->buf_fill > 0);
pa_assert(u->buf_fill > 0);
u->buf_fill--;
memmove(u->buf, u->buf+1, u->buf_fill);
return ret;
}
static char *read_string(struct userdata *u) {
assert(u);
pa_assert(u);
for (;;) {
char *e;
@ -143,9 +143,9 @@ static char *read_string(struct userdata *u) {
}
static void unload_one_module(struct userdata *u, struct module_info*m, unsigned i) {
assert(u);
assert(m);
assert(i < m->n_items);
pa_assert(u);
pa_assert(m);
pa_assert(i < m->n_items);
if (m->items[i].index == PA_INVALID_INDEX)
return;
@ -161,8 +161,8 @@ static void unload_one_module(struct userdata *u, struct module_info*m, unsigned
static void unload_all_modules(struct userdata *u, struct module_info*m) {
unsigned i;
assert(u);
assert(m);
pa_assert(u);
pa_assert(m);
for (i = 0; i < m->n_items; i++)
unload_one_module(u, m, i);
@ -180,10 +180,10 @@ static void load_module(
pa_module *mod;
assert(u);
assert(m);
assert(name);
assert(args);
pa_assert(u);
pa_assert(m);
pa_assert(name);
pa_assert(args);
if (!is_new) {
if (m->items[i].index != PA_INVALID_INDEX &&
@ -212,8 +212,8 @@ static void module_info_free(void *p, void *userdata) {
struct module_info *m = p;
struct userdata *u = userdata;
assert(m);
assert(u);
pa_assert(m);
pa_assert(u);
unload_all_modules(u, m);
pa_xfree(m->name);
@ -356,8 +356,10 @@ static int start_client(const char *n, pid_t *pid) {
return pipe_fds[0];
} else {
#ifdef __linux__
DIR* d;
#endif
int max_fd, i;
/* child */
close(pipe_fds[0]);
@ -372,18 +374,48 @@ static int start_client(const char *n, pid_t *pid) {
close(2);
open("/dev/null", O_WRONLY);
max_fd = 1024;
#ifdef __linux__
if ((d = opendir("/proc/self/fd/"))) {
struct dirent *de;
while ((de = readdir(d))) {
char *e = NULL;
int fd;
if (de->d_name[0] == '.')
continue;
errno = 0;
fd = strtol(de->d_name, &e, 10);
pa_assert(errno == 0 && e && *e == 0);
if (fd >= 3 && dirfd(d) != fd)
close(fd);
}
closedir(d);
} else {
#ifdef HAVE_SYS_RESOURCE_H
{
struct rlimit r;
if (getrlimit(RLIMIT_NOFILE, &r) == 0)
max_fd = r.rlim_max;
}
#endif
for (i = 3; i < max_fd; i++)
close(i);
max_fd = 1024;
#ifdef HAVE_SYS_RESOURCE_H
{
struct rlimit r;
if (getrlimit(RLIMIT_NOFILE, &r) == 0)
max_fd = r.rlim_max;
}
#endif
for (i = 3; i < max_fd; i++)
close(i);
#
#ifdef __linux__
}
#endif
#ifdef PR_SET_PDEATHSIG
/* On Linux we can use PR_SET_PDEATHSIG to have the helper
@ -413,12 +445,12 @@ fail:
return -1;
}
int pa__init(pa_core *c, pa_module*m) {
int pa__init(pa_module*m) {
struct userdata *u;
int r;
u = pa_xnew(struct userdata, 1);
u->core = c;
u->core = m->core;
u->module = m;
m->userdata = u;
u->module_infos = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
@ -431,8 +463,8 @@ int pa__init(pa_core *c, pa_module*m) {
if ((u->fd = start_client(PA_GCONF_HELPER, &u->pid)) < 0)
goto fail;
u->io_event = c->mainloop->io_new(
c->mainloop,
u->io_event = m->core->mainloop->io_new(
m->core->mainloop,
u->fd,
PA_IO_EVENT_INPUT,
io_event_cb,
@ -449,21 +481,20 @@ int pa__init(pa_core *c, pa_module*m) {
return 0;
fail:
pa__done(c, m);
pa__done(m);
return -1;
}
void pa__done(pa_core *c, pa_module*m) {
void pa__done(pa_module*m) {
struct userdata *u;
assert(c);
assert(m);
pa_assert(m);
if (!(u = m->userdata))
return;
if (u->io_event)
c->mainloop->io_free(u->io_event);
m->core->mainloop->io_free(u->io_event);
if (u->fd >= 0)
close(u->fd);

603
src/modules/ladspa.h Normal file
View file

@ -0,0 +1,603 @@
/* ladspa.h
Linux Audio Developer's Simple Plugin API Version 1.1[LGPL].
Copyright (C) 2000-2002 Richard W.E. Furse, Paul Barton-Davis,
Stefan Westerfeld.
This library 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.
This library 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 this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA. */
#ifndef LADSPA_INCLUDED
#define LADSPA_INCLUDED
#define LADSPA_VERSION "1.1"
#define LADSPA_VERSION_MAJOR 1
#define LADSPA_VERSION_MINOR 1
#ifdef __cplusplus
extern "C" {
#endif
/*****************************************************************************/
/* Overview:
There is a large number of synthesis packages in use or development
on the Linux platform at this time. This API (`The Linux Audio
Developer's Simple Plugin API') attempts to give programmers the
ability to write simple `plugin' audio processors in C/C++ and link
them dynamically (`plug') into a range of these packages (`hosts').
It should be possible for any host and any plugin to communicate
completely through this interface.
This API is deliberately short and simple. To achieve compatibility
with a range of promising Linux sound synthesis packages it
attempts to find the `greatest common divisor' in their logical
behaviour. Having said this, certain limiting decisions are
implicit, notably the use of a fixed type (LADSPA_Data) for all
data transfer and absence of a parameterised `initialisation'
phase. See below for the LADSPA_Data typedef.
Plugins are expected to distinguish between control and audio
data. Plugins have `ports' that are inputs or outputs for audio or
control data and each plugin is `run' for a `block' corresponding
to a short time interval measured in samples. Audio data is
communicated using arrays of LADSPA_Data, allowing a block of audio
to be processed by the plugin in a single pass. Control data is
communicated using single LADSPA_Data values. Control data has a
single value at the start of a call to the `run()' or `run_adding()'
function, and may be considered to remain this value for its
duration. The plugin may assume that all its input and output ports
have been connected to the relevant data location (see the
`connect_port()' function below) before it is asked to run.
Plugins will reside in shared object files suitable for dynamic
linking by dlopen() and family. The file will provide a number of
`plugin types' that can be used to instantiate actual plugins
(sometimes known as `plugin instances') that can be connected
together to perform tasks.
This API contains very limited error-handling. */
/*****************************************************************************/
/* Fundamental data type passed in and out of plugin. This data type
is used to communicate audio samples and control values. It is
assumed that the plugin will work sensibly given any numeric input
value although it may have a preferred range (see hints below).
For audio it is generally assumed that 1.0f is the `0dB' reference
amplitude and is a `normal' signal level. */
typedef float LADSPA_Data;
/*****************************************************************************/
/* Special Plugin Properties:
Optional features of the plugin type are encapsulated in the
LADSPA_Properties type. This is assembled by ORing individual
properties together. */
typedef int LADSPA_Properties;
/* Property LADSPA_PROPERTY_REALTIME indicates that the plugin has a
real-time dependency (e.g. listens to a MIDI device) and so its
output must not be cached or subject to significant latency. */
#define LADSPA_PROPERTY_REALTIME 0x1
/* Property LADSPA_PROPERTY_INPLACE_BROKEN indicates that the plugin
may cease to work correctly if the host elects to use the same data
location for both input and output (see connect_port()). This
should be avoided as enabling this flag makes it impossible for
hosts to use the plugin to process audio `in-place.' */
#define LADSPA_PROPERTY_INPLACE_BROKEN 0x2
/* Property LADSPA_PROPERTY_HARD_RT_CAPABLE indicates that the plugin
is capable of running not only in a conventional host but also in a
`hard real-time' environment. To qualify for this the plugin must
satisfy all of the following:
(1) The plugin must not use malloc(), free() or other heap memory
management within its run() or run_adding() functions. All new
memory used in run() must be managed via the stack. These
restrictions only apply to the run() function.
(2) The plugin will not attempt to make use of any library
functions with the exceptions of functions in the ANSI standard C
and C maths libraries, which the host is expected to provide.
(3) The plugin will not access files, devices, pipes, sockets, IPC
or any other mechanism that might result in process or thread
blocking.
(4) The plugin will take an amount of time to execute a run() or
run_adding() call approximately of form (A+B*SampleCount) where A
and B depend on the machine and host in use. This amount of time
may not depend on input signals or plugin state. The host is left
the responsibility to perform timings to estimate upper bounds for
A and B. */
#define LADSPA_PROPERTY_HARD_RT_CAPABLE 0x4
#define LADSPA_IS_REALTIME(x) ((x) & LADSPA_PROPERTY_REALTIME)
#define LADSPA_IS_INPLACE_BROKEN(x) ((x) & LADSPA_PROPERTY_INPLACE_BROKEN)
#define LADSPA_IS_HARD_RT_CAPABLE(x) ((x) & LADSPA_PROPERTY_HARD_RT_CAPABLE)
/*****************************************************************************/
/* Plugin Ports:
Plugins have `ports' that are inputs or outputs for audio or
data. Ports can communicate arrays of LADSPA_Data (for audio
inputs/outputs) or single LADSPA_Data values (for control
input/outputs). This information is encapsulated in the
LADSPA_PortDescriptor type which is assembled by ORing individual
properties together.
Note that a port must be an input or an output port but not both
and that a port must be a control or audio port but not both. */
typedef int LADSPA_PortDescriptor;
/* Property LADSPA_PORT_INPUT indicates that the port is an input. */
#define LADSPA_PORT_INPUT 0x1
/* Property LADSPA_PORT_OUTPUT indicates that the port is an output. */
#define LADSPA_PORT_OUTPUT 0x2
/* Property LADSPA_PORT_CONTROL indicates that the port is a control
port. */
#define LADSPA_PORT_CONTROL 0x4
/* Property LADSPA_PORT_AUDIO indicates that the port is a audio
port. */
#define LADSPA_PORT_AUDIO 0x8
#define LADSPA_IS_PORT_INPUT(x) ((x) & LADSPA_PORT_INPUT)
#define LADSPA_IS_PORT_OUTPUT(x) ((x) & LADSPA_PORT_OUTPUT)
#define LADSPA_IS_PORT_CONTROL(x) ((x) & LADSPA_PORT_CONTROL)
#define LADSPA_IS_PORT_AUDIO(x) ((x) & LADSPA_PORT_AUDIO)
/*****************************************************************************/
/* Plugin Port Range Hints:
The host may wish to provide a representation of data entering or
leaving a plugin (e.g. to generate a GUI automatically). To make
this more meaningful, the plugin should provide `hints' to the host
describing the usual values taken by the data.
Note that these are only hints. The host may ignore them and the
plugin must not assume that data supplied to it is meaningful. If
the plugin receives invalid input data it is expected to continue
to run without failure and, where possible, produce a sensible
output (e.g. a high-pass filter given a negative cutoff frequency
might switch to an all-pass mode).
Hints are meaningful for all input and output ports but hints for
input control ports are expected to be particularly useful.
More hint information is encapsulated in the
LADSPA_PortRangeHintDescriptor type which is assembled by ORing
individual hint types together. Hints may require further
LowerBound and UpperBound information.
All the hint information for a particular port is aggregated in the
LADSPA_PortRangeHint structure. */
typedef int LADSPA_PortRangeHintDescriptor;
/* Hint LADSPA_HINT_BOUNDED_BELOW indicates that the LowerBound field
of the LADSPA_PortRangeHint should be considered meaningful. The
value in this field should be considered the (inclusive) lower
bound of the valid range. If LADSPA_HINT_SAMPLE_RATE is also
specified then the value of LowerBound should be multiplied by the
sample rate. */
#define LADSPA_HINT_BOUNDED_BELOW 0x1
/* Hint LADSPA_HINT_BOUNDED_ABOVE indicates that the UpperBound field
of the LADSPA_PortRangeHint should be considered meaningful. The
value in this field should be considered the (inclusive) upper
bound of the valid range. If LADSPA_HINT_SAMPLE_RATE is also
specified then the value of UpperBound should be multiplied by the
sample rate. */
#define LADSPA_HINT_BOUNDED_ABOVE 0x2
/* Hint LADSPA_HINT_TOGGLED indicates that the data item should be
considered a Boolean toggle. Data less than or equal to zero should
be considered `off' or `false,' and data above zero should be
considered `on' or `true.' LADSPA_HINT_TOGGLED may not be used in
conjunction with any other hint except LADSPA_HINT_DEFAULT_0 or
LADSPA_HINT_DEFAULT_1. */
#define LADSPA_HINT_TOGGLED 0x4
/* Hint LADSPA_HINT_SAMPLE_RATE indicates that any bounds specified
should be interpreted as multiples of the sample rate. For
instance, a frequency range from 0Hz to the Nyquist frequency (half
the sample rate) could be requested by this hint in conjunction
with LowerBound = 0 and UpperBound = 0.5. Hosts that support bounds
at all must support this hint to retain meaning. */
#define LADSPA_HINT_SAMPLE_RATE 0x8
/* Hint LADSPA_HINT_LOGARITHMIC indicates that it is likely that the
user will find it more intuitive to view values using a logarithmic
scale. This is particularly useful for frequencies and gains. */
#define LADSPA_HINT_LOGARITHMIC 0x10
/* Hint LADSPA_HINT_INTEGER indicates that a user interface would
probably wish to provide a stepped control taking only integer
values. Any bounds set should be slightly wider than the actual
integer range required to avoid floating point rounding errors. For
instance, the integer set {0,1,2,3} might be described as [-0.1,
3.1]. */
#define LADSPA_HINT_INTEGER 0x20
/* The various LADSPA_HINT_HAS_DEFAULT_* hints indicate a `normal'
value for the port that is sensible as a default. For instance,
this value is suitable for use as an initial value in a user
interface or as a value the host might assign to a control port
when the user has not provided one. Defaults are encoded using a
mask so only one default may be specified for a port. Some of the
hints make use of lower and upper bounds, in which case the
relevant bound or bounds must be available and
LADSPA_HINT_SAMPLE_RATE must be applied as usual. The resulting
default must be rounded if LADSPA_HINT_INTEGER is present. Default
values were introduced in LADSPA v1.1. */
#define LADSPA_HINT_DEFAULT_MASK 0x3C0
/* This default values indicates that no default is provided. */
#define LADSPA_HINT_DEFAULT_NONE 0x0
/* This default hint indicates that the suggested lower bound for the
port should be used. */
#define LADSPA_HINT_DEFAULT_MINIMUM 0x40
/* This default hint indicates that a low value between the suggested
lower and upper bounds should be chosen. For ports with
LADSPA_HINT_LOGARITHMIC, this should be exp(log(lower) * 0.75 +
log(upper) * 0.25). Otherwise, this should be (lower * 0.75 + upper
* 0.25). */
#define LADSPA_HINT_DEFAULT_LOW 0x80
/* This default hint indicates that a middle value between the
suggested lower and upper bounds should be chosen. For ports with
LADSPA_HINT_LOGARITHMIC, this should be exp(log(lower) * 0.5 +
log(upper) * 0.5). Otherwise, this should be (lower * 0.5 + upper *
0.5). */
#define LADSPA_HINT_DEFAULT_MIDDLE 0xC0
/* This default hint indicates that a high value between the suggested
lower and upper bounds should be chosen. For ports with
LADSPA_HINT_LOGARITHMIC, this should be exp(log(lower) * 0.25 +
log(upper) * 0.75). Otherwise, this should be (lower * 0.25 + upper
* 0.75). */
#define LADSPA_HINT_DEFAULT_HIGH 0x100
/* This default hint indicates that the suggested upper bound for the
port should be used. */
#define LADSPA_HINT_DEFAULT_MAXIMUM 0x140
/* This default hint indicates that the number 0 should be used. Note
that this default may be used in conjunction with
LADSPA_HINT_TOGGLED. */
#define LADSPA_HINT_DEFAULT_0 0x200
/* This default hint indicates that the number 1 should be used. Note
that this default may be used in conjunction with
LADSPA_HINT_TOGGLED. */
#define LADSPA_HINT_DEFAULT_1 0x240
/* This default hint indicates that the number 100 should be used. */
#define LADSPA_HINT_DEFAULT_100 0x280
/* This default hint indicates that the Hz frequency of `concert A'
should be used. This will be 440 unless the host uses an unusual
tuning convention, in which case it may be within a few Hz. */
#define LADSPA_HINT_DEFAULT_440 0x2C0
#define LADSPA_IS_HINT_BOUNDED_BELOW(x) ((x) & LADSPA_HINT_BOUNDED_BELOW)
#define LADSPA_IS_HINT_BOUNDED_ABOVE(x) ((x) & LADSPA_HINT_BOUNDED_ABOVE)
#define LADSPA_IS_HINT_TOGGLED(x) ((x) & LADSPA_HINT_TOGGLED)
#define LADSPA_IS_HINT_SAMPLE_RATE(x) ((x) & LADSPA_HINT_SAMPLE_RATE)
#define LADSPA_IS_HINT_LOGARITHMIC(x) ((x) & LADSPA_HINT_LOGARITHMIC)
#define LADSPA_IS_HINT_INTEGER(x) ((x) & LADSPA_HINT_INTEGER)
#define LADSPA_IS_HINT_HAS_DEFAULT(x) ((x) & LADSPA_HINT_DEFAULT_MASK)
#define LADSPA_IS_HINT_DEFAULT_MINIMUM(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \
== LADSPA_HINT_DEFAULT_MINIMUM)
#define LADSPA_IS_HINT_DEFAULT_LOW(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \
== LADSPA_HINT_DEFAULT_LOW)
#define LADSPA_IS_HINT_DEFAULT_MIDDLE(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \
== LADSPA_HINT_DEFAULT_MIDDLE)
#define LADSPA_IS_HINT_DEFAULT_HIGH(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \
== LADSPA_HINT_DEFAULT_HIGH)
#define LADSPA_IS_HINT_DEFAULT_MAXIMUM(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \
== LADSPA_HINT_DEFAULT_MAXIMUM)
#define LADSPA_IS_HINT_DEFAULT_0(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \
== LADSPA_HINT_DEFAULT_0)
#define LADSPA_IS_HINT_DEFAULT_1(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \
== LADSPA_HINT_DEFAULT_1)
#define LADSPA_IS_HINT_DEFAULT_100(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \
== LADSPA_HINT_DEFAULT_100)
#define LADSPA_IS_HINT_DEFAULT_440(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \
== LADSPA_HINT_DEFAULT_440)
typedef struct _LADSPA_PortRangeHint {
/* Hints about the port. */
LADSPA_PortRangeHintDescriptor HintDescriptor;
/* Meaningful when hint LADSPA_HINT_BOUNDED_BELOW is active. When
LADSPA_HINT_SAMPLE_RATE is also active then this value should be
multiplied by the relevant sample rate. */
LADSPA_Data LowerBound;
/* Meaningful when hint LADSPA_HINT_BOUNDED_ABOVE is active. When
LADSPA_HINT_SAMPLE_RATE is also active then this value should be
multiplied by the relevant sample rate. */
LADSPA_Data UpperBound;
} LADSPA_PortRangeHint;
/*****************************************************************************/
/* Plugin Handles:
This plugin handle indicates a particular instance of the plugin
concerned. It is valid to compare this to NULL (0 for C++) but
otherwise the host should not attempt to interpret it. The plugin
may use it to reference internal instance data. */
typedef void * LADSPA_Handle;
/*****************************************************************************/
/* Descriptor for a Type of Plugin:
This structure is used to describe a plugin type. It provides a
number of functions to examine the type, instantiate it, link it to
buffers and workspaces and to run it. */
typedef struct _LADSPA_Descriptor {
/* This numeric identifier indicates the plugin type
uniquely. Plugin programmers may reserve ranges of IDs from a
central body to avoid clashes. Hosts may assume that IDs are
below 0x1000000. */
unsigned long UniqueID;
/* This identifier can be used as a unique, case-sensitive
identifier for the plugin type within the plugin file. Plugin
types should be identified by file and label rather than by index
or plugin name, which may be changed in new plugin
versions. Labels must not contain white-space characters. */
const char * Label;
/* This indicates a number of properties of the plugin. */
LADSPA_Properties Properties;
/* This member points to the null-terminated name of the plugin
(e.g. "Sine Oscillator"). */
const char * Name;
/* This member points to the null-terminated string indicating the
maker of the plugin. This can be an empty string but not NULL. */
const char * Maker;
/* This member points to the null-terminated string indicating any
copyright applying to the plugin. If no Copyright applies the
string "None" should be used. */
const char * Copyright;
/* This indicates the number of ports (input AND output) present on
the plugin. */
unsigned long PortCount;
/* This member indicates an array of port descriptors. Valid indices
vary from 0 to PortCount-1. */
const LADSPA_PortDescriptor * PortDescriptors;
/* This member indicates an array of null-terminated strings
describing ports (e.g. "Frequency (Hz)"). Valid indices vary from
0 to PortCount-1. */
const char * const * PortNames;
/* This member indicates an array of range hints for each port (see
above). Valid indices vary from 0 to PortCount-1. */
const LADSPA_PortRangeHint * PortRangeHints;
/* This may be used by the plugin developer to pass any custom
implementation data into an instantiate call. It must not be used
or interpreted by the host. It is expected that most plugin
writers will not use this facility as LADSPA_Handle should be
used to hold instance data. */
void * ImplementationData;
/* This member is a function pointer that instantiates a plugin. A
handle is returned indicating the new plugin instance. The
instantiation function accepts a sample rate as a parameter. The
plugin descriptor from which this instantiate function was found
must also be passed. This function must return NULL if
instantiation fails.
Note that instance initialisation should generally occur in
activate() rather than here. */
LADSPA_Handle (*instantiate)(const struct _LADSPA_Descriptor * Descriptor,
unsigned long SampleRate);
/* This member is a function pointer that connects a port on an
instantiated plugin to a memory location at which a block of data
for the port will be read/written. The data location is expected
to be an array of LADSPA_Data for audio ports or a single
LADSPA_Data value for control ports. Memory issues will be
managed by the host. The plugin must read/write the data at these
locations every time run() or run_adding() is called and the data
present at the time of this connection call should not be
considered meaningful.
connect_port() may be called more than once for a plugin instance
to allow the host to change the buffers that the plugin is
reading or writing. These calls may be made before or after
activate() or deactivate() calls.
connect_port() must be called at least once for each port before
run() or run_adding() is called. When working with blocks of
LADSPA_Data the plugin should pay careful attention to the block
size passed to the run function as the block allocated may only
just be large enough to contain the block of samples.
Plugin writers should be aware that the host may elect to use the
same buffer for more than one port and even use the same buffer
for both input and output (see LADSPA_PROPERTY_INPLACE_BROKEN).
However, overlapped buffers or use of a single buffer for both
audio and control data may result in unexpected behaviour. */
void (*connect_port)(LADSPA_Handle Instance,
unsigned long Port,
LADSPA_Data * DataLocation);
/* This member is a function pointer that initialises a plugin
instance and activates it for use. This is separated from
instantiate() to aid real-time support and so that hosts can
reinitialise a plugin instance by calling deactivate() and then
activate(). In this case the plugin instance must reset all state
information dependent on the history of the plugin instance
except for any data locations provided by connect_port() and any
gain set by set_run_adding_gain(). If there is nothing for
activate() to do then the plugin writer may provide a NULL rather
than an empty function.
When present, hosts must call this function once before run() (or
run_adding()) is called for the first time. This call should be
made as close to the run() call as possible and indicates to
real-time plugins that they are now live. Plugins should not rely
on a prompt call to run() after activate(). activate() may not be
called again unless deactivate() is called first. Note that
connect_port() may be called before or after a call to
activate(). */
void (*activate)(LADSPA_Handle Instance);
/* This method is a function pointer that runs an instance of a
plugin for a block. Two parameters are required: the first is a
handle to the particular instance to be run and the second
indicates the block size (in samples) for which the plugin
instance may run.
Note that if an activate() function exists then it must be called
before run() or run_adding(). If deactivate() is called for a
plugin instance then the plugin instance may not be reused until
activate() has been called again.
If the plugin has the property LADSPA_PROPERTY_HARD_RT_CAPABLE
then there are various things that the plugin should not do
within the run() or run_adding() functions (see above). */
void (*run)(LADSPA_Handle Instance,
unsigned long SampleCount);
/* This method is a function pointer that runs an instance of a
plugin for a block. This has identical behaviour to run() except
in the way data is output from the plugin. When run() is used,
values are written directly to the memory areas associated with
the output ports. However when run_adding() is called, values
must be added to the values already present in the memory
areas. Furthermore, output values written must be scaled by the
current gain set by set_run_adding_gain() (see below) before
addition.
run_adding() is optional. When it is not provided by a plugin,
this function pointer must be set to NULL. When it is provided,
the function set_run_adding_gain() must be provided also. */
void (*run_adding)(LADSPA_Handle Instance,
unsigned long SampleCount);
/* This method is a function pointer that sets the output gain for
use when run_adding() is called (see above). If this function is
never called the gain is assumed to default to 1. Gain
information should be retained when activate() or deactivate()
are called.
This function should be provided by the plugin if and only if the
run_adding() function is provided. When it is absent this
function pointer must be set to NULL. */
void (*set_run_adding_gain)(LADSPA_Handle Instance,
LADSPA_Data Gain);
/* This is the counterpart to activate() (see above). If there is
nothing for deactivate() to do then the plugin writer may provide
a NULL rather than an empty function.
Hosts must deactivate all activated units after they have been
run() (or run_adding()) for the last time. This call should be
made as close to the last run() call as possible and indicates to
real-time plugins that they are no longer live. Plugins should
not rely on prompt deactivation. Note that connect_port() may be
called before or after a call to deactivate().
Deactivation is not similar to pausing as the plugin instance
will be reinitialised when activate() is called to reuse it. */
void (*deactivate)(LADSPA_Handle Instance);
/* Once an instance of a plugin has been finished with it can be
deleted using the following function. The instance handle passed
ceases to be valid after this call.
If activate() was called for a plugin instance then a
corresponding call to deactivate() must be made before cleanup()
is called. */
void (*cleanup)(LADSPA_Handle Instance);
} LADSPA_Descriptor;
/**********************************************************************/
/* Accessing a Plugin: */
/* The exact mechanism by which plugins are loaded is host-dependent,
however all most hosts will need to know is the name of shared
object file containing the plugin types. To allow multiple hosts to
share plugin types, hosts may wish to check for environment
variable LADSPA_PATH. If present, this should contain a
colon-separated path indicating directories that should be searched
(in order) when loading plugin types.
A plugin programmer must include a function called
"ladspa_descriptor" with the following function prototype within
the shared object file. This function will have C-style linkage (if
you are using C++ this is taken care of by the `extern "C"' clause
at the top of the file).
A host will find the plugin shared object file by one means or
another, find the ladspa_descriptor() function, call it, and
proceed from there.
Plugin types are accessed by index (not ID) using values from 0
upwards. Out of range indexes must result in this function
returning NULL, so the plugin count can be determined by checking
for the least index that results in NULL being returned. */
const LADSPA_Descriptor * ladspa_descriptor(unsigned long Index);
/* Datatype corresponding to the ladspa_descriptor() function. */
typedef const LADSPA_Descriptor *
(*LADSPA_Descriptor_Function)(unsigned long Index);
/**********************************************************************/
#ifdef __cplusplus
}
#endif
#endif /* LADSPA_INCLUDED */
/* EOF */

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -26,7 +26,6 @@
#endif
#include <stdio.h>
#include <assert.h>
#include <unistd.h>
#include <pulsecore/module.h>
@ -35,6 +34,7 @@
#include <pulsecore/sioman.h>
#include <pulsecore/log.h>
#include <pulsecore/modargs.h>
#include <pulsecore/macro.h>
#include "module-cli-symdef.h"
@ -51,8 +51,8 @@ static const char* const valid_modargs[] = {
static void eof_and_unload_cb(pa_cli*c, void *userdata) {
pa_module *m = userdata;
assert(c);
assert(m);
pa_assert(c);
pa_assert(m);
pa_module_unload_request(m);
}
@ -60,21 +60,20 @@ static void eof_and_unload_cb(pa_cli*c, void *userdata) {
static void eof_and_exit_cb(pa_cli*c, void *userdata) {
pa_module *m = userdata;
assert(c);
assert(m);
pa_assert(c);
pa_assert(m);
m->core->mainloop->quit(m->core->mainloop, 0);
}
int pa__init(pa_core *c, pa_module*m) {
int pa__init(pa_module*m) {
pa_iochannel *io;
pa_modargs *ma;
int exit_on_eof = 0;
assert(c);
assert(m);
pa_assert(m);
if (c->running_as_daemon) {
if (m->core->running_as_daemon) {
pa_log_info("Running as daemon, refusing to load this module.");
return 0;
}
@ -94,12 +93,10 @@ int pa__init(pa_core *c, pa_module*m) {
goto fail;
}
io = pa_iochannel_new(c->mainloop, STDIN_FILENO, STDOUT_FILENO);
assert(io);
io = pa_iochannel_new(m->core->mainloop, STDIN_FILENO, STDOUT_FILENO);
pa_iochannel_set_noclose(io, 1);
m->userdata = pa_cli_new(c, io, m);
assert(m->userdata);
m->userdata = pa_cli_new(m->core, io, m);
pa_cli_set_eof_callback(m->userdata, exit_on_eof ? eof_and_exit_cb : eof_and_unload_cb, m);
@ -115,11 +112,10 @@ fail:
return -1;
}
void pa__done(pa_core *c, pa_module*m) {
assert(c);
assert(m);
void pa__done(pa_module*m) {
pa_assert(m);
if (c->running_as_daemon == 0) {
if (m->core->running_as_daemon == 0) {
pa_cli_free(m->userdata);
pa_stdio_release();
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,103 @@
/* $Id$ */
/***
This file is part of PulseAudio.
Copyright 2006 Lennart Poettering
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <pulsecore/core-util.h>
#include <pulsecore/module.h>
#include <pulsecore/log.h>
#include <pulsecore/namereg.h>
#include "module-default-device-restore-symdef.h"
PA_MODULE_AUTHOR("Lennart Poettering")
PA_MODULE_DESCRIPTION("Automatically restore the default sink and source")
PA_MODULE_VERSION(PACKAGE_VERSION)
#define DEFAULT_SINK_FILE "default-sink"
#define DEFAULT_SOURCE_FILE "default-source"
int pa__init(pa_module *m) {
FILE *f;
/* We never overwrite manually configured settings */
if (m->core->default_sink_name)
pa_log_info("Manually configured default sink, not overwriting.");
else if ((f = pa_open_config_file(NULL, DEFAULT_SINK_FILE, NULL, NULL, "r"))) {
char ln[256] = "";
fgets(ln, sizeof(ln)-1, f);
pa_strip_nl(ln);
fclose(f);
if (!ln[0])
pa_log_debug("No previous default sink setting, ignoring.");
else if (pa_namereg_get(m->core, ln, PA_NAMEREG_SINK, 1)) {
pa_namereg_set_default(m->core, ln, PA_NAMEREG_SINK);
pa_log_debug("Restored default sink '%s'.", ln);
} else
pa_log_info("Saved default sink '%s' not existant, not restoring default sink setting.", ln);
}
if (m->core->default_source_name)
pa_log_info("Manually configured default source, not overwriting.");
else if ((f = pa_open_config_file(NULL, DEFAULT_SOURCE_FILE, NULL, NULL, "r"))) {
char ln[256] = "";
fgets(ln, sizeof(ln)-1, f);
pa_strip_nl(ln);
fclose(f);
if (!ln[0])
pa_log_debug("No previous default source setting, ignoring.");
else if (pa_namereg_get(m->core, ln, PA_NAMEREG_SOURCE, 1)) {
pa_namereg_set_default(m->core, ln, PA_NAMEREG_SOURCE);
pa_log_debug("Restored default source '%s'.", ln);
} else
pa_log_info("Saved default source '%s' not existant, not restoring default source setting.", ln);
}
return 0;
}
void pa__done(pa_module*m) {
FILE *f;
if ((f = pa_open_config_file(NULL, DEFAULT_SINK_FILE, NULL, NULL, "w"))) {
const char *n = pa_namereg_get_default_sink_name(m->core);
fprintf(f, "%s\n", n ? n : "");
fclose(f);
}
if ((f = pa_open_config_file(NULL, DEFAULT_SOURCE_FILE, NULL, NULL, "w"))) {
const char *n = pa_namereg_get_default_source_name(m->core);
fprintf(f, "%s\n", n ? n : "");
fclose(f);
}
}

View file

@ -18,8 +18,8 @@ gen_symbol(pa__get_description)
gen_symbol(pa__get_usage)
gen_symbol(pa__get_version)
int pa__init(struct pa_core *c, struct pa_module*m);
void pa__done(struct pa_core *c, struct pa_module*m);
int pa__init(pa_module*m);
void pa__done(pa_module*m);
const char* pa__get_author(void);
const char* pa__get_description(void);

View file

@ -28,7 +28,6 @@
#endif
#include <stdio.h>
#include <assert.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
@ -44,6 +43,7 @@
#include <pulsecore/modargs.h>
#include <pulsecore/log.h>
#include <pulsecore/core-util.h>
#include <pulsecore/macro.h>
#include "module-detect-symdef.h"
@ -52,6 +52,11 @@ PA_MODULE_DESCRIPTION("Detect available audio hardware and load matching drivers
PA_MODULE_VERSION(PACKAGE_VERSION)
PA_MODULE_USAGE("just-one=<boolean>")
static const char* const valid_modargs[] = {
"just-one",
NULL
};
#ifdef HAVE_ALSA
static int detect_alsa(pa_core *c, int just_one) {
@ -96,7 +101,7 @@ static int detect_alsa(pa_core *c, int just_one) {
if (subdevice != 0)
continue;
snprintf(args, sizeof(args), "device=hw:%u", device);
pa_snprintf(args, sizeof(args), "device=hw:%u", device);
if (!pa_module_load(c, is_sink ? "module-alsa-sink" : "module-alsa-source", args))
continue;
@ -139,7 +144,7 @@ static int detect_oss(pa_core *c, int just_one) {
line[strcspn(line, "\r\n")] = 0;
if (!b) {
b = strcmp(line, "Audio devices:") == 0 || strcmp(line, "Installed devices:") == 0;
b = strcmp(line, "Audio devices:") == 0 || strcmp(line, "Installed devices:") == 0;
continue;
}
@ -148,20 +153,20 @@ static int detect_oss(pa_core *c, int just_one) {
if (sscanf(line, "%u: ", &device) == 1) {
if (device == 0)
snprintf(args, sizeof(args), "device=/dev/dsp");
pa_snprintf(args, sizeof(args), "device=/dev/dsp");
else
snprintf(args, sizeof(args), "device=/dev/dsp%u", device);
pa_snprintf(args, sizeof(args), "device=/dev/dsp%u", device);
if (!pa_module_load(c, "module-oss", args))
continue;
} else if (sscanf(line, "pcm%u: ", &device) == 1) {
} else if (sscanf(line, "pcm%u: ", &device) == 1) {
/* FreeBSD support, the devices are named /dev/dsp0.0, dsp0.1 and so on */
snprintf(args, sizeof(args), "device=/dev/dsp%u.0", device);
pa_snprintf(args, sizeof(args), "device=/dev/dsp%u.0", device);
if (!pa_module_load(c, "module-oss", args))
continue;
}
}
n++;
@ -193,7 +198,7 @@ static int detect_solaris(pa_core *c, int just_one) {
if (!S_ISCHR(s.st_mode))
return 0;
snprintf(args, sizeof(args), "device=%s", dev);
pa_snprintf(args, sizeof(args), "device=%s", dev);
if (!pa_module_load(c, "module-solaris", args))
return 0;
@ -215,17 +220,11 @@ static int detect_waveout(pa_core *c, int just_one) {
}
#endif
int pa__init(pa_core *c, pa_module*m) {
int pa__init(pa_module*m) {
int just_one = 0, n = 0;
pa_modargs *ma;
static const char* const valid_modargs[] = {
"just-one",
NULL
};
assert(c);
assert(m);
pa_assert(m);
if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
pa_log("Failed to parse module arguments");
@ -238,16 +237,16 @@ int pa__init(pa_core *c, pa_module*m) {
}
#if HAVE_ALSA
if ((n = detect_alsa(c, just_one)) <= 0)
if ((n = detect_alsa(m->core, just_one)) <= 0)
#endif
#if HAVE_OSS
if ((n = detect_oss(c, just_one)) <= 0)
if ((n = detect_oss(m->core, just_one)) <= 0)
#endif
#if HAVE_SOLARIS
if ((n = detect_solaris(c, just_one)) <= 0)
if ((n = detect_solaris(m->core, just_one)) <= 0)
#endif
#if OS_IS_WIN32
if ((n = detect_waveout(c, just_one)) <= 0)
if ((n = detect_waveout(m->core, just_one)) <= 0)
#endif
{
pa_log_warn("failed to detect any sound hardware.");
@ -269,9 +268,3 @@ fail:
return -1;
}
void pa__done(PA_GCC_UNUSED pa_core *c, PA_GCC_UNUSED pa_module*m) {
/* NOP */
}

View file

@ -26,7 +26,6 @@
#endif
#include <unistd.h>
#include <assert.h>
#include <string.h>
#include <errno.h>
@ -48,23 +47,25 @@ static const char* const valid_modargs[] = {
NULL,
};
int pa__init(pa_core *c, pa_module*m) {
int pa__init(pa_module*m) {
pa_modargs *ma = NULL;
int ret = -1, fd = -1;
char x = 1;
assert(c && m);
pa_assert(m);
if (!(ma = pa_modargs_new(m->argument, valid_modargs)) ||
pa_modargs_get_value_s32(ma, "fd", &fd) < 0 ||
fd < 0) {
pa_log("Failed to parse module arguments");
goto finish;
}
if (pa_loop_write(fd, &x, sizeof(x), NULL) != sizeof(x))
pa_log("WARNING: write(%u, 1, 1) failed: %s", fd, pa_cstrerror(errno));
pa_log_warn("write(%u, 1, 1) failed: %s", fd, pa_cstrerror(errno));
close(fd);
pa_assert_se(pa_close(fd) == 0);
pa_module_unload_request(m);
@ -76,9 +77,3 @@ finish:
return ret;
}
void pa__done(pa_core *c, pa_module*m) {
assert(c && m);
}

View file

@ -25,7 +25,6 @@
#endif
#include <unistd.h>
#include <assert.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
@ -48,11 +47,12 @@ static const char* const valid_modargs[] = {
NULL,
};
int pa__init(pa_core *c, pa_module*m) {
int pa__init(pa_module*m) {
pa_modargs *ma = NULL;
int ret = -1;
uint32_t pid = 0;
assert(c && m);
pa_assert(m);
if (!(ma = pa_modargs_new(m->argument, valid_modargs)) ||
pa_modargs_get_value_u32(ma, "pid", &pid) < 0 ||
@ -62,7 +62,7 @@ int pa__init(pa_core *c, pa_module*m) {
}
if (kill(pid, SIGUSR1) < 0)
pa_log("WARNING: kill(%u) failed: %s", pid, pa_cstrerror(errno));
pa_log_warn("kill(%u) failed: %s", pid, pa_cstrerror(errno));
pa_module_unload_request(m);
@ -74,9 +74,3 @@ finish:
return ret;
}
void pa__done(pa_core *c, pa_module*m) {
assert(c && m);
}

View file

@ -28,14 +28,23 @@
#include <stdlib.h>
#include <sys/stat.h>
#include <stdio.h>
#include <assert.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <limits.h>
#include <poll.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <sys/ioctl.h>
#ifdef HAVE_LINUX_SOCKIOS_H
#include <linux/sockios.h>
#endif
#include <pulse/xmalloc.h>
#include <pulse/timeval.h>
#include <pulsecore/core-error.h>
#include <pulsecore/iochannel.h>
@ -47,27 +56,37 @@
#include <pulsecore/socket-client.h>
#include <pulsecore/esound.h>
#include <pulsecore/authkey.h>
#include <pulsecore/thread-mq.h>
#include <pulsecore/thread.h>
#include <pulsecore/time-smoother.h>
#include <pulsecore/rtclock.h>
#include <pulsecore/socket-util.h>
#include "module-esound-sink-symdef.h"
PA_MODULE_AUTHOR("Lennart Poettering")
PA_MODULE_DESCRIPTION("ESOUND Sink")
PA_MODULE_VERSION(PACKAGE_VERSION)
PA_MODULE_USAGE("sink_name=<name for the sink> server=<address> cookie=<filename> format=<sample format> channels=<number of channels> rate=<sample rate>")
PA_MODULE_USAGE(
"sink_name=<name for the sink> "
"server=<address> cookie=<filename> "
"format=<sample format> "
"channels=<number of channels> "
"rate=<sample rate>")
#define DEFAULT_SINK_NAME "esound_output"
#define DEFAULT_SINK_NAME "esound_out"
struct userdata {
pa_core *core;
pa_module *module;
pa_sink *sink;
pa_iochannel *io;
pa_socket_client *client;
pa_defer_event *defer_event;
pa_thread_mq thread_mq;
pa_rtpoll *rtpoll;
pa_rtpoll_item *rtpoll_item;
pa_thread *thread;
pa_memchunk memchunk;
pa_module *module;
void *write_data;
size_t write_length, write_index;
@ -75,12 +94,28 @@ struct userdata {
void *read_data;
size_t read_length, read_index;
enum { STATE_AUTH, STATE_LATENCY, STATE_RUNNING, STATE_DEAD } state;
enum {
STATE_AUTH,
STATE_LATENCY,
STATE_PREPARE,
STATE_RUNNING,
STATE_DEAD
} state;
pa_usec_t latency;
esd_format_t format;
int32_t rate;
pa_smoother *smoother;
int fd;
int64_t offset;
pa_iochannel *io;
pa_socket_client *client;
size_t block_size;
};
static const char* const valid_modargs[] = {
@ -93,42 +128,211 @@ static const char* const valid_modargs[] = {
NULL
};
static void cancel(struct userdata *u) {
assert(u);
enum {
SINK_MESSAGE_PASS_SOCKET = PA_SINK_MESSAGE_MAX
};
u->state = STATE_DEAD;
static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
struct userdata *u = PA_SINK(o)->userdata;
if (u->io) {
pa_iochannel_free(u->io);
u->io = NULL;
switch (code) {
case PA_SINK_MESSAGE_SET_STATE:
switch ((pa_sink_state_t) PA_PTR_TO_UINT(data)) {
case PA_SINK_SUSPENDED:
pa_assert(PA_SINK_OPENED(u->sink->thread_info.state));
pa_smoother_pause(u->smoother, pa_rtclock_usec());
break;
case PA_SINK_IDLE:
case PA_SINK_RUNNING:
if (u->sink->thread_info.state == PA_SINK_SUSPENDED)
pa_smoother_resume(u->smoother, pa_rtclock_usec());
break;
case PA_SINK_UNLINKED:
case PA_SINK_INIT:
;
}
break;
case PA_SINK_MESSAGE_GET_LATENCY: {
pa_usec_t w, r;
r = pa_smoother_get(u->smoother, pa_rtclock_usec());
w = pa_bytes_to_usec(u->offset + u->memchunk.length, &u->sink->sample_spec);
*((pa_usec_t*) data) = w > r ? w - r : 0;
break;
}
case SINK_MESSAGE_PASS_SOCKET: {
struct pollfd *pollfd;
pa_assert(!u->rtpoll_item);
u->rtpoll_item = pa_rtpoll_item_new(u->rtpoll, PA_RTPOLL_NEVER, 1);
pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
pollfd->fd = u->fd;
pollfd->events = pollfd->revents = 0;
return 0;
}
}
if (u->defer_event) {
u->core->mainloop->defer_free(u->defer_event);
u->defer_event = NULL;
return pa_sink_process_msg(o, code, data, offset, chunk);
}
static void thread_func(void *userdata) {
struct userdata *u = userdata;
int write_type = 0;
pa_assert(u);
pa_log_debug("Thread starting up");
pa_thread_mq_install(&u->thread_mq);
pa_rtpoll_install(u->rtpoll);
pa_smoother_set_time_offset(u->smoother, pa_rtclock_usec());
for (;;) {
int ret;
if (u->rtpoll_item) {
struct pollfd *pollfd;
pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
/* Render some data and write it to the fifo */
if (PA_SINK_OPENED(u->sink->thread_info.state) && pollfd->revents) {
pa_usec_t usec;
int64_t n;
for (;;) {
ssize_t l;
void *p;
if (u->memchunk.length <= 0)
pa_sink_render(u->sink, u->block_size, &u->memchunk);
pa_assert(u->memchunk.length > 0);
p = pa_memblock_acquire(u->memchunk.memblock);
l = pa_write(u->fd, (uint8_t*) p + u->memchunk.index, u->memchunk.length, &write_type);
pa_memblock_release(u->memchunk.memblock);
pa_assert(l != 0);
if (l < 0) {
if (errno == EINTR)
continue;
else if (errno == EAGAIN) {
/* OK, we filled all socket buffers up
* now. */
goto filled_up;
} else {
pa_log("Failed to write data to FIFO: %s", pa_cstrerror(errno));
goto fail;
}
} else {
u->offset += l;
u->memchunk.index += l;
u->memchunk.length -= l;
if (u->memchunk.length <= 0) {
pa_memblock_unref(u->memchunk.memblock);
pa_memchunk_reset(&u->memchunk);
}
pollfd->revents = 0;
if (u->memchunk.length > 0)
/* OK, we wrote less that we asked for,
* hence we can assume that the socket
* buffers are full now */
goto filled_up;
}
}
filled_up:
/* At this spot we know that the socket buffers are
* fully filled up. This is the best time to estimate
* the playback position of the server */
n = u->offset;
#ifdef SIOCOUTQ
{
int l;
if (ioctl(u->fd, SIOCOUTQ, &l) >= 0 && l > 0)
n -= l;
}
#endif
usec = pa_bytes_to_usec(n, &u->sink->sample_spec);
if (usec > u->latency)
usec -= u->latency;
else
usec = 0;
pa_smoother_put(u->smoother, pa_rtclock_usec(), usec);
}
/* Hmm, nothing to do. Let's sleep */
pollfd->events = PA_SINK_OPENED(u->sink->thread_info.state) ? POLLOUT : 0;
}
if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0)
goto fail;
if (ret == 0)
goto finish;
if (u->rtpoll_item) {
struct pollfd* pollfd;
pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
if (pollfd->revents & ~POLLOUT) {
pa_log("FIFO shutdown.");
goto fail;
}
}
}
if (u->sink) {
pa_sink_disconnect(u->sink);
pa_sink_unref(u->sink);
u->sink = NULL;
}
fail:
/* If this was no regular exit from the loop we have to continue
* processing messages until we received PA_MESSAGE_SHUTDOWN */
pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);
if (u->module) {
pa_module_unload_request(u->module);
u->module = NULL;
}
finish:
pa_log_debug("Thread shutting down");
}
static int do_write(struct userdata *u) {
ssize_t r;
assert(u);
pa_assert(u);
if (!pa_iochannel_is_writable(u->io))
return 0;
if (u->write_data) {
assert(u->write_index < u->write_length);
pa_assert(u->write_index < u->write_length);
if ((r = pa_iochannel_write(u->io, (uint8_t*) u->write_data + u->write_index, u->write_length - u->write_index)) <= 0) {
pa_log("write() failed: %s", pa_cstrerror(errno));
@ -136,45 +340,44 @@ static int do_write(struct userdata *u) {
}
u->write_index += r;
assert(u->write_index <= u->write_length);
pa_assert(u->write_index <= u->write_length);
if (u->write_index == u->write_length) {
free(u->write_data);
pa_xfree(u->write_data);
u->write_data = NULL;
u->write_index = u->write_length = 0;
}
} else if (u->state == STATE_RUNNING) {
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;
if (!u->write_data && u->state == STATE_PREPARE) {
/* OK, we're done with sending all control data we need to, so
* let's hand the socket over to the IO thread now */
assert(u->memchunk.memblock && u->memchunk.length);
pa_assert(u->fd < 0);
u->fd = pa_iochannel_get_send_fd(u->io);
if ((r = pa_iochannel_write(u->io, (uint8_t*) u->memchunk.memblock->data + u->memchunk.index, u->memchunk.length)) < 0) {
pa_log("write() failed: %s", pa_cstrerror(errno));
return -1;
}
pa_iochannel_set_noclose(u->io, TRUE);
pa_iochannel_free(u->io);
u->io = NULL;
u->memchunk.index += r;
u->memchunk.length -= r;
pa_make_tcp_socket_low_delay(u->fd);
if (u->memchunk.length <= 0) {
pa_memblock_unref(u->memchunk.memblock);
u->memchunk.memblock = NULL;
}
pa_log_info("Connection authenticated, handing fd to IO thread...");
pa_asyncmsgq_post(u->thread_mq.inq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_PASS_SOCKET, NULL, 0, NULL, NULL);
u->state = STATE_RUNNING;
}
return 0;
}
static int handle_response(struct userdata *u) {
assert(u);
pa_assert(u);
switch (u->state) {
case STATE_AUTH:
assert(u->read_length == sizeof(int32_t));
pa_assert(u->read_length == sizeof(int32_t));
/* Process auth data */
if (!*(int32_t*) u->read_data) {
@ -183,14 +386,14 @@ static int handle_response(struct userdata *u) {
}
/* Request latency data */
assert(!u->write_data);
pa_assert(!u->write_data);
*(int32_t*) (u->write_data = pa_xmalloc(u->write_length = sizeof(int32_t))) = ESD_PROTO_LATENCY;
u->write_index = 0;
u->state = STATE_LATENCY;
/* Space for next response */
assert(u->read_length >= sizeof(int32_t));
pa_assert(u->read_length >= sizeof(int32_t));
u->read_index = 0;
u->read_length = sizeof(int32_t);
@ -198,17 +401,17 @@ static int handle_response(struct userdata *u) {
case STATE_LATENCY: {
int32_t *p;
assert(u->read_length == sizeof(int32_t));
pa_assert(u->read_length == sizeof(int32_t));
/* Process latency info */
u->latency = (pa_usec_t) ((double) (*(int32_t*) u->read_data) * 1000000 / 44100);
if (u->latency > 10000000) {
pa_log("WARNING! Invalid latency information received from server");
pa_log_warn("Invalid latency information received from server");
u->latency = 0;
}
/* Create stream */
assert(!u->write_data);
pa_assert(!u->write_data);
p = u->write_data = pa_xmalloc0(u->write_length = sizeof(int32_t)*3+ESD_NAME_MAX);
*(p++) = ESD_PROTO_STREAM_PLAY;
*(p++) = u->format;
@ -216,7 +419,7 @@ static int handle_response(struct userdata *u) {
pa_strlcpy((char*) p, "PulseAudio Tunnel", ESD_NAME_MAX);
u->write_index = 0;
u->state = STATE_RUNNING;
u->state = STATE_PREPARE;
/* Don't read any further */
pa_xfree(u->read_data);
@ -227,14 +430,14 @@ static int handle_response(struct userdata *u) {
}
default:
abort();
pa_assert_not_reached();
}
return 0;
}
static int do_read(struct userdata *u) {
assert(u);
pa_assert(u);
if (!pa_iochannel_is_readable(u->io))
return 0;
@ -245,16 +448,15 @@ static int do_read(struct userdata *u) {
if (!u->read_data)
return 0;
assert(u->read_index < u->read_length);
pa_assert(u->read_index < u->read_length);
if ((r = pa_iochannel_read(u->io, (uint8_t*) u->read_data + u->read_index, u->read_length - u->read_index)) <= 0) {
pa_log("read() failed: %s", r < 0 ? pa_cstrerror(errno) : "EOF");
cancel(u);
return -1;
}
u->read_index += r;
assert(u->read_index <= u->read_length);
pa_assert(u->read_index <= u->read_length);
if (u->read_index == u->read_length)
return handle_response(u);
@ -263,42 +465,19 @@ static int do_read(struct userdata *u) {
return 0;
}
static void do_work(struct userdata *u) {
assert(u);
u->core->mainloop->defer_enable(u->defer_event, 0);
if (do_read(u) < 0 || do_write(u) < 0)
cancel(u);
}
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->latency +
(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) {
struct userdata *u = userdata;
assert(u);
do_work(u);
}
static void io_callback(PA_GCC_UNUSED pa_iochannel *io, void*userdata) {
struct userdata *u = userdata;
assert(u);
do_work(u);
pa_assert(u);
if (do_read(u) < 0 || do_write(u) < 0) {
if (u->io) {
pa_iochannel_free(u->io);
u->io = NULL;
}
pa_module_unload_request(u->module);
}
}
static void on_connection(PA_GCC_UNUSED pa_socket_client *c, pa_iochannel*io, void *userdata) {
@ -308,30 +487,34 @@ static void on_connection(PA_GCC_UNUSED pa_socket_client *c, pa_iochannel*io, vo
u->client = NULL;
if (!io) {
pa_log("connection failed: %s", pa_cstrerror(errno));
cancel(u);
pa_log("Connection failed: %s", pa_cstrerror(errno));
pa_module_unload_request(u->module);
return;
}
pa_assert(!u->io);
u->io = io;
pa_iochannel_set_callback(u->io, io_callback, u);
pa_log_info("Connection established, authenticating ...");
}
int pa__init(pa_core *c, pa_module*m) {
int pa__init(pa_module*m) {
struct userdata *u = NULL;
const char *p;
pa_sample_spec ss;
pa_modargs *ma = NULL;
char *t;
const char *espeaker;
assert(c && m);
pa_assert(m);
if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
pa_log("failed to parse module arguments");
goto fail;
}
ss = c->default_sample_spec;
ss = m->core->default_sample_spec;
if (pa_modargs_get_sample_spec(ma, &ss) < 0) {
pa_log("invalid sample format specification");
goto fail;
@ -343,37 +526,62 @@ int pa__init(pa_core *c, pa_module*m) {
goto fail;
}
u = pa_xmalloc0(sizeof(struct userdata));
u->core = c;
u = pa_xnew0(struct userdata, 1);
u->core = m->core;
u->module = m;
m->userdata = u;
u->fd = -1;
u->smoother = pa_smoother_new(PA_USEC_PER_SEC, PA_USEC_PER_SEC*2, TRUE);
pa_memchunk_reset(&u->memchunk);
u->offset = 0;
pa_thread_mq_init(&u->thread_mq, m->core->mainloop);
u->rtpoll = pa_rtpoll_new();
pa_rtpoll_item_new_asyncmsgq(u->rtpoll, PA_RTPOLL_EARLY, u->thread_mq.inq);
u->rtpoll_item = NULL;
u->format =
(ss.format == PA_SAMPLE_U8 ? ESD_BITS8 : ESD_BITS16) |
(ss.channels == 2 ? ESD_STEREO : ESD_MONO);
u->rate = ss.rate;
u->sink = NULL;
u->client = NULL;
u->io = NULL;
u->block_size = pa_usec_to_bytes(PA_USEC_PER_SEC/20, &ss);
u->read_data = u->write_data = NULL;
u->read_index = u->write_index = u->read_length = u->write_length = 0;
u->state = STATE_AUTH;
u->latency = 0;
if (!(u->sink = pa_sink_new(c, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss, NULL))) {
if (!(u->sink = pa_sink_new(m->core, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss, NULL))) {
pa_log("failed to create sink.");
goto fail;
}
if (!(u->client = pa_socket_client_new_string(u->core->mainloop, p = pa_modargs_get_value(ma, "server", ESD_UNIX_SOCKET_NAME), ESD_DEFAULT_PORT))) {
pa_log("failed to connect to server.");
u->sink->parent.process_msg = sink_process_msg;
u->sink->userdata = u;
u->sink->flags = PA_SINK_LATENCY;
pa_sink_set_module(u->sink, m);
pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
pa_sink_set_rtpoll(u->sink, u->rtpoll);
if (!(espeaker = getenv("ESPEAKER")))
espeaker = ESD_UNIX_SOCKET_NAME;
if (!(u->client = pa_socket_client_new_string(u->core->mainloop, p = pa_modargs_get_value(ma, "server", espeaker), ESD_DEFAULT_PORT))) {
pa_log("Failed to connect to server.");
goto fail;
}
pa_sink_set_description(u->sink, t = pa_sprintf_malloc("Esound sink '%s'", p));
pa_xfree(t);
pa_socket_client_set_callback(u->client, on_connection, u);
/* Prepare the initial request */
u->write_data = pa_xmalloc(u->write_length = ESD_KEY_LEN + sizeof(int32_t));
if (pa_authkey_load_auto(pa_modargs_get_value(ma, "cookie", ".esd_auth"), u->write_data, ESD_KEY_LEN) < 0) {
pa_log("failed to load cookie");
pa_log("Failed to load cookie");
goto fail;
}
*(int32_t*) ((uint8_t*) u->write_data + ESD_KEY_LEN) = ESD_ENDIAN_KEY;
@ -381,19 +589,12 @@ int pa__init(pa_core *c, pa_module*m) {
/* Reserve space for the response */
u->read_data = pa_xmalloc(u->read_length = sizeof(int32_t));
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("Esound sink '%s'", p));
pa_xfree(t);
u->memchunk.memblock = NULL;
u->memchunk.length = 0;
u->defer_event = c->mainloop->defer_new(c->mainloop, defer_callback, u);
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_sink_put(u->sink);
pa_modargs_free(ma);
@ -403,20 +604,39 @@ fail:
if (ma)
pa_modargs_free(ma);
pa__done(c, m);
pa__done(m);
return -1;
}
void pa__done(pa_core *c, pa_module*m) {
void pa__done(pa_module*m) {
struct userdata *u;
assert(c && m);
pa_assert(m);
if (!(u = m->userdata))
return;
u->module = NULL;
cancel(u);
if (u->sink)
pa_sink_unlink(u->sink);
if (u->thread) {
pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL);
pa_thread_free(u->thread);
}
pa_thread_mq_done(&u->thread_mq);
if (u->sink)
pa_sink_unref(u->sink);
if (u->io)
pa_iochannel_free(u->io);
if (u->rtpoll_item)
pa_rtpoll_item_free(u->rtpoll_item);
if (u->rtpoll)
pa_rtpoll_free(u->rtpoll);
if (u->memchunk.memblock)
pa_memblock_unref(u->memchunk.memblock);
@ -427,8 +647,11 @@ void pa__done(pa_core *c, pa_module*m) {
pa_xfree(u->read_data);
pa_xfree(u->write_data);
if (u->smoother)
pa_smoother_free(u->smoother);
if (u->fd >= 0)
pa_close(u->fd);
pa_xfree(u);
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -28,31 +28,34 @@
#include <stdlib.h>
#include <sys/stat.h>
#include <stdio.h>
#include <assert.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <limits.h>
#include <pthread.h>
#include <jack/jack.h>
#include <pulse/xmalloc.h>
#include <pulsecore/core-error.h>
#include <pulsecore/iochannel.h>
#include <pulsecore/source.h>
#include <pulsecore/module.h>
#include <pulsecore/core-util.h>
#include <pulsecore/modargs.h>
#include <pulsecore/log.h>
#include <pulse/mainloop-api.h>
#include <pulsecore/thread.h>
#include <pulsecore/thread-mq.h>
#include <pulsecore/rtpoll.h>
#include <pulsecore/sample-util.h>
#include "module-jack-source-symdef.h"
/* See module-jack-sink for a few comments how this module basically
* works */
PA_MODULE_AUTHOR("Lennart Poettering")
PA_MODULE_DESCRIPTION("Jack Source")
PA_MODULE_DESCRIPTION("JACK Source")
PA_MODULE_VERSION(PACKAGE_VERSION)
PA_MODULE_USAGE(
"source_name=<name of source> "
@ -67,7 +70,6 @@ PA_MODULE_USAGE(
struct userdata {
pa_core *core;
pa_module *module;
pa_source *source;
unsigned channels;
@ -75,19 +77,15 @@ struct userdata {
jack_port_t* port[PA_CHANNELS_MAX];
jack_client_t *client;
pthread_mutex_t mutex;
pthread_cond_t cond;
pa_thread_mq thread_mq;
pa_asyncmsgq *jack_msgq;
pa_rtpoll *rtpoll;
pa_rtpoll_item *rtpoll_item;
void * buffer[PA_CHANNELS_MAX];
jack_nframes_t frames_posted;
int quit_requested;
pa_thread *thread;
int pipe_fds[2];
int pipe_fd_type;
pa_io_event *io_event;
jack_nframes_t frames_in_buffer;
jack_nframes_t timestamp;
jack_nframes_t saved_frame_time;
pa_bool_t saved_frame_time_valid;
};
static const char* const valid_modargs[] = {
@ -100,141 +98,150 @@ static const char* const valid_modargs[] = {
NULL
};
static void stop_source(struct userdata *u) {
assert (u);
enum {
SOURCE_MESSAGE_POST = PA_SOURCE_MESSAGE_MAX,
SOURCE_MESSAGE_ON_SHUTDOWN
};
jack_client_close(u->client);
u->client = NULL;
u->core->mainloop->io_free(u->io_event);
u->io_event = NULL;
pa_source_disconnect(u->source);
pa_source_unref(u->source);
u->source = NULL;
pa_module_unload_request(u->module);
}
static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
struct userdata *u = PA_SOURCE(o)->userdata;
static void io_event_cb(pa_mainloop_api *m, pa_io_event *e, int fd, pa_io_event_flags_t flags, void *userdata) {
struct userdata *u = userdata;
char x;
switch (code) {
assert(m);
assert(flags == PA_IO_EVENT_INPUT);
assert(u);
assert(u->pipe_fds[0] == fd);
case SOURCE_MESSAGE_POST:
pa_read(fd, &x, 1, &u->pipe_fd_type);
/* Handle the new block from the JACK thread */
pa_assert(chunk);
pa_assert(chunk->length > 0);
if (u->quit_requested) {
stop_source(u);
u->quit_requested = 0;
return;
}
if (u->source->thread_info.state == PA_SOURCE_RUNNING)
pa_source_post(u->source, chunk);
pthread_mutex_lock(&u->mutex);
u->saved_frame_time = offset;
u->saved_frame_time_valid = TRUE;
if (u->frames_posted > 0) {
unsigned fs;
jack_nframes_t frame_idx;
pa_memchunk chunk;
return 0;
fs = pa_frame_size(&u->source->sample_spec);
case SOURCE_MESSAGE_ON_SHUTDOWN:
pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
return 0;
chunk.memblock = pa_memblock_new(u->core->mempool, chunk.length = u->frames_posted * fs);
chunk.index = 0;
case PA_SOURCE_MESSAGE_GET_LATENCY: {
jack_nframes_t l, ft, d;
size_t n;
for (frame_idx = 0; frame_idx < u->frames_posted; frame_idx ++) {
unsigned c;
/* This is the "worst-case" latency */
l = jack_port_get_total_latency(u->client, u->port[0]);
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;
if (u->saved_frame_time_valid) {
/* Adjust the worst case latency by the time that
* passed since we last handed data to JACK */
*d = *s;
ft = jack_frame_time(u->client);
d = ft > u->saved_frame_time ? ft - u->saved_frame_time : 0;
l += d;
}
/* Convert it to usec */
n = l * pa_frame_size(&u->source->sample_spec);
*((pa_usec_t*) data) = pa_bytes_to_usec(n, &u->source->sample_spec);
return 0;
}
pa_source_post(u->source, &chunk);
pa_memblock_unref(chunk.memblock);
u->frames_posted = 0;
pthread_cond_signal(&u->cond);
}
pthread_mutex_unlock(&u->mutex);
}
static void request_post(struct userdata *u) {
char c = 'x';
assert(u);
assert(u->pipe_fds[1] >= 0);
pa_write(u->pipe_fds[1], &c, 1, &u->pipe_fd_type);
}
static void jack_shutdown(void *arg) {
struct userdata *u = arg;
assert(u);
u->quit_requested = 1;
request_post(u);
return pa_source_process_msg(o, code, data, offset, chunk);
}
static int jack_process(jack_nframes_t nframes, void *arg) {
unsigned c;
struct userdata *u = arg;
assert(u);
const void *buffer[PA_CHANNELS_MAX];
void *p;
jack_nframes_t frame_time;
pa_memchunk chunk;
if (jack_transport_query(u->client, NULL) == JackTransportRolling) {
unsigned c;
pa_assert(u);
pthread_mutex_lock(&u->mutex);
for (c = 0; c < u->channels; c++)
pa_assert(buffer[c] = jack_port_get_buffer(u->port[c], nframes));
u->frames_posted = nframes;
/* We interleave the data and pass it on to the other RT thread */
for (c = 0; c < u->channels; c++) {
u->buffer[c] = jack_port_get_buffer(u->port[c], nframes);
assert(u->buffer[c]);
}
pa_memchunk_reset(&chunk);
chunk.length = nframes * pa_frame_size(&u->source->sample_spec);
chunk.memblock = pa_memblock_new(u->core->mempool, chunk.length);
p = pa_memblock_acquire(chunk.memblock);
pa_interleave(buffer, u->channels, p, sizeof(float), nframes);
pa_memblock_release(chunk.memblock);
request_post(u);
frame_time = jack_frame_time(u->client);
pthread_cond_wait(&u->cond, &u->mutex);
pa_asyncmsgq_post(u->jack_msgq, PA_MSGOBJECT(u->source), SOURCE_MESSAGE_POST, NULL, frame_time, &chunk, NULL);
u->frames_in_buffer = nframes;
u->timestamp = jack_get_current_transport_frame(u->client);
pthread_mutex_unlock(&u->mutex);
}
pa_memblock_unref(chunk.memblock);
return 0;
}
static pa_usec_t source_get_latency_cb(pa_source *s) {
struct userdata *u;
jack_nframes_t n, l, d;
static void thread_func(void *userdata) {
struct userdata *u = userdata;
assert(s);
u = s->userdata;
pa_assert(u);
if (jack_transport_query(u->client, NULL) != JackTransportRolling)
return 0;
pa_log_debug("Thread starting up");
n = jack_get_current_transport_frame(u->client);
if (u->core->high_priority)
pa_make_realtime();
if (n < u->timestamp)
return 0;
pa_thread_mq_install(&u->thread_mq);
pa_rtpoll_install(u->rtpoll);
d = n - u->timestamp;
l = jack_port_get_total_latency(u->client, u->port[0]);
for (;;) {
int ret;
return pa_bytes_to_usec((l + d) * pa_frame_size(&s->sample_spec), &s->sample_spec);
if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0)
goto fail;
if (ret == 0)
goto finish;
}
fail:
/* If this was no regular exit from the loop we have to continue
* processing messages until we received PA_MESSAGE_SHUTDOWN */
pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);
finish:
pa_log_debug("Thread shutting down");
}
static void jack_error_func(const char*t) {
pa_log_warn("JACK error >%s<", t);
char *s;
s = pa_xstrndup(t, strcspn(t, "\n\r"));
pa_log_warn("JACK error >%s<", s);
pa_xfree(s);
}
int pa__init(pa_core *c, pa_module*m) {
static void jack_init(void *arg) {
struct userdata *u = arg;
pa_log_info("JACK thread starting up.");
if (u->core->high_priority)
pa_make_realtime();
}
static void jack_shutdown(void* arg) {
struct userdata *u = arg;
pa_log_info("JACK thread shutting down..");
pa_asyncmsgq_post(u->jack_msgq, PA_MSGOBJECT(u->source), SOURCE_MESSAGE_ON_SHUTDOWN, NULL, 0, NULL, NULL);
}
int pa__init(pa_module*m) {
struct userdata *u = NULL;
pa_sample_spec ss;
pa_channel_map map;
@ -247,40 +254,35 @@ int pa__init(pa_core *c, pa_module*m) {
const char **ports = NULL, **p;
char *t;
assert(c);
assert(m);
pa_assert(m);
jack_set_error_function(jack_error_func);
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;
}
if (pa_modargs_get_value_boolean(ma, "connect", &do_connect) < 0) {
pa_log("failed to parse connect= argument.");
pa_log("Failed to parse connect= argument.");
goto fail;
}
server_name = pa_modargs_get_value(ma, "server_name", NULL);
client_name = pa_modargs_get_value(ma, "client_name", "PulseAudio");
client_name = pa_modargs_get_value(ma, "client_name", "PulseAudio JACK Source");
u = pa_xnew0(struct userdata, 1);
m->userdata = u;
u->core = c;
u->core = m->core;
u->module = m;
u->pipe_fds[0] = u->pipe_fds[1] = -1;
u->pipe_fd_type = 0;
m->userdata = u;
u->saved_frame_time_valid = FALSE;
pthread_mutex_init(&u->mutex, NULL);
pthread_cond_init(&u->cond, NULL);
pa_thread_mq_init(&u->thread_mq, m->core->mainloop);
u->rtpoll = pa_rtpoll_new();
pa_rtpoll_item_new_asyncmsgq(u->rtpoll, PA_RTPOLL_EARLY, u->thread_mq.inq);
if (pipe(u->pipe_fds) < 0) {
pa_log("pipe() failed: %s", pa_cstrerror(errno));
goto fail;
}
pa_make_nonblock_fd(u->pipe_fds[1]);
u->jack_msgq = pa_asyncmsgq_new(0);
u->rtpoll_item = pa_rtpoll_item_new_asyncmsgq(u->rtpoll, PA_RTPOLL_EARLY-1, u->jack_msgq);
if (!(u->client = jack_client_open(client_name, server_name ? JackServerName : JackNullOption, &status, server_name))) {
pa_log("jack_client_open() failed.");
@ -294,7 +296,7 @@ int pa__init(pa_core *c, pa_module*m) {
channels++;
if (!channels)
channels = c->default_sample_spec.channels;
channels = m->core->default_sample_spec.channels;
if (pa_modargs_get_value_u32(ma, "channels", &channels) < 0 || channels <= 0 || channels >= PA_CHANNELS_MAX) {
pa_log("failed to parse channels= argument.");
@ -302,7 +304,7 @@ int pa__init(pa_core *c, pa_module*m) {
}
pa_channel_map_init_auto(&map, channels, PA_CHANNEL_MAP_ALSA);
if (pa_modargs_get_channel_map(ma, &map) < 0 || map.channels != channels) {
if (pa_modargs_get_channel_map(ma, NULL, &map) < 0 || map.channels != channels) {
pa_log("failed to parse channel_map= argument.");
goto fail;
}
@ -313,7 +315,7 @@ int pa__init(pa_core *c, pa_module*m) {
ss.rate = jack_get_sample_rate(u->client);
ss.format = PA_SAMPLE_FLOAT32NE;
assert(pa_sample_spec_valid(&ss));
pa_assert(pa_sample_spec_valid(&ss));
for (i = 0; i < ss.channels; i++) {
if (!(u->port[i] = jack_port_register(u->client, pa_channel_position_to_string(map.map[i]), JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput|JackPortIsTerminal, 0))) {
@ -322,19 +324,29 @@ int pa__init(pa_core *c, pa_module*m) {
}
}
if (!(u->source = pa_source_new(c, __FILE__, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME), 0, &ss, &map))) {
if (!(u->source = pa_source_new(m->core, __FILE__, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME), 0, &ss, &map))) {
pa_log("failed to create source.");
goto fail;
}
u->source->parent.process_msg = source_process_msg;
u->source->userdata = u;
pa_source_set_owner(u->source, m);
u->source->flags = PA_SOURCE_LATENCY;
pa_source_set_module(u->source, m);
pa_source_set_asyncmsgq(u->source, u->thread_mq.inq);
pa_source_set_rtpoll(u->source, u->rtpoll);
pa_source_set_description(u->source, t = pa_sprintf_malloc("Jack source (%s)", jack_get_client_name(u->client)));
pa_xfree(t);
u->source->get_latency = source_get_latency_cb;
jack_set_process_callback(u->client, jack_process, u);
jack_on_shutdown(u->client, jack_shutdown, u);
jack_set_thread_init_callback(u->client, jack_init, u);
if (!(u->thread = pa_thread_new(thread_func, u))) {
pa_log("Failed to create thread.");
goto fail;
}
if (jack_activate(u->client)) {
pa_log("jack_activate() failed");
@ -359,7 +371,7 @@ int pa__init(pa_core *c, pa_module*m) {
}
u->io_event = c->mainloop->io_new(c->mainloop, u->pipe_fds[0], PA_IO_EVENT_INPUT, io_event_cb, u);
pa_source_put(u->source);
free(ports);
pa_modargs_free(ma);
@ -372,14 +384,14 @@ fail:
free(ports);
pa__done(c, m);
pa__done(m);
return -1;
}
void pa__done(pa_core *c, pa_module*m) {
void pa__done(pa_module*m) {
struct userdata *u;
assert(c && m);
pa_assert(m);
if (!(u = m->userdata))
return;
@ -387,20 +399,27 @@ void pa__done(pa_core *c, pa_module*m) {
if (u->client)
jack_client_close(u->client);
if (u->io_event)
c->mainloop->io_free(u->io_event);
if (u->source)
pa_source_unlink(u->source);
if (u->source) {
pa_source_disconnect(u->source);
pa_source_unref(u->source);
if (u->thread) {
pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL);
pa_thread_free(u->thread);
}
if (u->pipe_fds[0] >= 0)
close(u->pipe_fds[0]);
if (u->pipe_fds[1] >= 0)
close(u->pipe_fds[1]);
pa_thread_mq_done(&u->thread_mq);
if (u->source)
pa_source_unref(u->source);
if (u->rtpoll_item)
pa_rtpoll_item_free(u->rtpoll_item);
if (u->jack_msgq)
pa_asyncmsgq_unref(u->jack_msgq);
if (u->rtpoll)
pa_rtpoll_free(u->rtpoll);
pthread_mutex_destroy(&u->mutex);
pthread_cond_destroy(&u->cond);
pa_xfree(u);
}

View file

@ -0,0 +1,673 @@
/* $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.
***/
/* TODO: Some plugins cause latency, and some even report it by using a control
out port. We don't currently use the latency information. */
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <pulse/xmalloc.h>
#include <pulsecore/core-error.h>
#include <pulsecore/namereg.h>
#include <pulsecore/sink.h>
#include <pulsecore/module.h>
#include <pulsecore/core-util.h>
#include <pulsecore/modargs.h>
#include <pulsecore/log.h>
#include <pulsecore/thread.h>
#include <pulsecore/thread-mq.h>
#include <pulsecore/rtpoll.h>
#include <pulsecore/sample-util.h>
#include "module-ladspa-sink-symdef.h"
#include "ladspa.h"
PA_MODULE_AUTHOR("Lennart Poettering")
PA_MODULE_DESCRIPTION("Virtual LADSPA sink")
PA_MODULE_VERSION(PACKAGE_VERSION)
PA_MODULE_USAGE(
"sink_name=<name for the sink> "
"master=<name of sink to remap> "
"format=<sample format> "
"channels=<number of channels> "
"rate=<sample rate> "
"channel_map=<channel map> "
"plugin=<ladspa plugin name> "
"label=<ladspa plugin label> "
"control=<comma seperated list of input control values>")
struct userdata {
pa_core *core;
pa_module *module;
pa_sink *sink, *master;
pa_sink_input *sink_input;
const LADSPA_Descriptor *descriptor;
unsigned channels;
LADSPA_Handle handle[PA_CHANNELS_MAX];
LADSPA_Data *input, *output;
size_t block_size;
unsigned long input_port, output_port;
LADSPA_Data *control;
/* This is a dummy buffer. Every port must be connected, but we don't care
about control out ports. We connect them all to this single buffer. */
LADSPA_Data control_out;
pa_memchunk memchunk;
};
static const char* const valid_modargs[] = {
"sink_name",
"master",
"format",
"channels",
"rate",
"channel_map",
"plugin",
"label",
"control",
NULL
};
/* Called from I/O thread context */
static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
struct userdata *u = PA_SINK(o)->userdata;
switch (code) {
case PA_SINK_MESSAGE_GET_LATENCY: {
pa_usec_t usec = 0;
if (PA_MSGOBJECT(u->master)->process_msg(PA_MSGOBJECT(u->master), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0)
usec = 0;
*((pa_usec_t*) data) = usec + pa_bytes_to_usec(u->memchunk.length, &u->sink->sample_spec);
return 0;
}
}
return pa_sink_process_msg(o, code, data, offset, chunk);
}
/* Called from main context */
static int sink_set_state(pa_sink *s, pa_sink_state_t state) {
struct userdata *u;
pa_sink_assert_ref(s);
pa_assert_se(u = s->userdata);
if (PA_SINK_LINKED(state) && u->sink_input && PA_SINK_INPUT_LINKED(pa_sink_input_get_state(u->sink_input)))
pa_sink_input_cork(u->sink_input, state == PA_SINK_SUSPENDED);
return 0;
}
/* Called from I/O thread context */
static int sink_input_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
struct userdata *u = PA_SINK_INPUT(o)->userdata;
switch (code) {
case PA_SINK_INPUT_MESSAGE_GET_LATENCY:
*((pa_usec_t*) data) = pa_bytes_to_usec(u->memchunk.length, &u->sink_input->sample_spec);
/* Fall through, the default handler will add in the extra
* latency added by the resampler */
break;
}
return pa_sink_input_process_msg(o, code, data, offset, chunk);
}
/* Called from I/O thread context */
static int sink_input_peek_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) {
struct userdata *u;
pa_sink_input_assert_ref(i);
pa_assert_se(u = i->userdata);
if (!u->memchunk.memblock) {
pa_memchunk tchunk;
float *src, *dst;
size_t fs;
unsigned n, c;
pa_sink_render(u->sink, length, &tchunk);
fs = pa_frame_size(&i->sample_spec);
n = tchunk.length / fs;
pa_assert(n > 0);
u->memchunk.memblock = pa_memblock_new(i->sink->core->mempool, tchunk.length);
u->memchunk.index = 0;
u->memchunk.length = tchunk.length;
src = (float*) ((uint8_t*) pa_memblock_acquire(tchunk.memblock) + tchunk.index);
dst = (float*) pa_memblock_acquire(u->memchunk.memblock);
for (c = 0; c < u->channels; c++) {
unsigned j;
float *p, *q;
p = src + c;
q = u->input;
for (j = 0; j < n; j++, p += u->channels, q++)
*q = CLAMP(*p, -1.0, 1.0);
u->descriptor->run(u->handle[c], n);
q = u->output;
p = dst + c;
for (j = 0; j < n; j++, q++, p += u->channels)
*p = CLAMP(*q, -1.0, 1.0);
}
pa_memblock_release(tchunk.memblock);
pa_memblock_release(u->memchunk.memblock);
pa_memblock_unref(tchunk.memblock);
}
pa_assert(u->memchunk.length > 0);
pa_assert(u->memchunk.memblock);
*chunk = u->memchunk;
pa_memblock_ref(chunk->memblock);
return 0;
}
/* Called from I/O thread context */
static void sink_input_drop_cb(pa_sink_input *i, size_t length) {
struct userdata *u;
pa_sink_input_assert_ref(i);
pa_assert_se(u = i->userdata);
pa_assert(length > 0);
if (u->memchunk.memblock) {
if (length < u->memchunk.length) {
u->memchunk.index += length;
u->memchunk.length -= length;
return;
}
pa_memblock_unref(u->memchunk.memblock);
length -= u->memchunk.length;
pa_memchunk_reset(&u->memchunk);
}
if (length > 0)
pa_sink_skip(u->sink, length);
}
/* Called from I/O thread context */
static void sink_input_detach_cb(pa_sink_input *i) {
struct userdata *u;
pa_sink_input_assert_ref(i);
pa_assert_se(u = i->userdata);
pa_sink_detach_within_thread(u->sink);
}
/* Called from I/O thread context */
static void sink_input_attach_cb(pa_sink_input *i) {
struct userdata *u;
pa_sink_input_assert_ref(i);
pa_assert_se(u = i->userdata);
pa_sink_set_asyncmsgq(u->sink, i->sink->asyncmsgq);
pa_sink_set_rtpoll(u->sink, i->sink->rtpoll);
pa_sink_attach_within_thread(u->sink);
}
/* Called from main context */
static void sink_input_kill_cb(pa_sink_input *i) {
struct userdata *u;
pa_sink_input_assert_ref(i);
pa_assert_se(u = i->userdata);
pa_sink_input_unlink(u->sink_input);
pa_sink_input_unref(u->sink_input);
u->sink_input = NULL;
pa_sink_unlink(u->sink);
pa_sink_unref(u->sink);
u->sink = NULL;
pa_module_unload_request(u->module);
}
int pa__init(pa_module*m) {
struct userdata *u;
pa_sample_spec ss;
pa_channel_map map;
pa_modargs *ma;
char *t;
pa_sink *master;
pa_sink_input_new_data data;
const char *plugin, *label;
LADSPA_Descriptor_Function descriptor_func;
const char *e, *cdata;
const LADSPA_Descriptor *d;
unsigned long input_port, output_port, p, j, n_control;
unsigned c;
pa_bool_t *use_default = NULL;
char *default_sink_name = NULL;
pa_assert(m);
pa_assert(sizeof(LADSPA_Data) == sizeof(float));
if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
pa_log("Failed to parse module arguments.");
goto fail;
}
if (!(master = pa_namereg_get(m->core, pa_modargs_get_value(ma, "master", NULL), PA_NAMEREG_SINK, 1))) {
pa_log("Master sink not found");
goto fail;
}
ss = master->sample_spec;
ss.format = PA_SAMPLE_FLOAT32;
map = master->channel_map;
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");
goto fail;
}
if (!(plugin = pa_modargs_get_value(ma, "plugin", NULL))) {
pa_log("Missing LADSPA plugin name");
goto fail;
}
if (!(label = pa_modargs_get_value(ma, "label", NULL))) {
pa_log("Missing LADSPA plugin label");
goto fail;
}
cdata = pa_modargs_get_value(ma, "control", NULL);
u = pa_xnew0(struct userdata, 1);
u->core = m->core;
u->module = m;
m->userdata = u;
u->master = master;
pa_memchunk_reset(&u->memchunk);
if (!(e = getenv("LADSPA_PATH")))
e = LADSPA_PATH;
/* FIXME: This is not exactly thread safe */
t = pa_xstrdup(lt_dlgetsearchpath());
lt_dlsetsearchpath(e);
m->dl = lt_dlopenext(plugin);
lt_dlsetsearchpath(t);
pa_xfree(t);
if (!m->dl) {
pa_log("Failed to load LADSPA plugin: %s", lt_dlerror());
goto fail;
}
if (!(descriptor_func = (LADSPA_Descriptor_Function) lt_dlsym(m->dl, "ladspa_descriptor"))) {
pa_log("LADSPA module lacks ladspa_descriptor() symbol.");
goto fail;
}
for (j = 0;; j++) {
if (!(d = descriptor_func(j))) {
pa_log("Failed to find plugin label '%s' in plugin '%s'.", plugin, label);
goto fail;
}
if (strcmp(d->Label, label) == 0)
break;
}
u->descriptor = d;
pa_log_debug("Module: %s", plugin);
pa_log_debug("Label: %s", d->Label);
pa_log_debug("Unique ID: %lu", d->UniqueID);
pa_log_debug("Name: %s", d->Name);
pa_log_debug("Maker: %s", d->Maker);
pa_log_debug("Copyright: %s", d->Copyright);
input_port = output_port = (unsigned long) -1;
n_control = 0;
for (p = 0; p < d->PortCount; p++) {
if (LADSPA_IS_PORT_INPUT(d->PortDescriptors[p]) && LADSPA_IS_PORT_AUDIO(d->PortDescriptors[p])) {
if (strcmp(d->PortNames[p], "Input") == 0) {
pa_assert(input_port == (unsigned long) -1);
input_port = p;
} else {
pa_log("Found audio input port on plugin we cannot handle: %s", d->PortNames[p]);
goto fail;
}
} else if (LADSPA_IS_PORT_OUTPUT(d->PortDescriptors[p]) && LADSPA_IS_PORT_AUDIO(d->PortDescriptors[p])) {
if (strcmp(d->PortNames[p], "Output") == 0) {
pa_assert(output_port == (unsigned long) -1);
output_port = p;
} else {
pa_log("Found audio output port on plugin we cannot handle: %s", d->PortNames[p]);
goto fail;
}
} else if (LADSPA_IS_PORT_INPUT(d->PortDescriptors[p]) && LADSPA_IS_PORT_CONTROL(d->PortDescriptors[p]))
n_control++;
else {
pa_assert(LADSPA_IS_PORT_OUTPUT(d->PortDescriptors[p]) && LADSPA_IS_PORT_CONTROL(d->PortDescriptors[p]));
pa_log_info("Ignored port \"%s\", because we ignore all control out ports.", d->PortNames[p]);
}
}
if ((input_port == (unsigned long) -1) || (output_port == (unsigned long) -1)) {
pa_log("Failed to identify input and output ports. "
"Right now this module can only deal with plugins which provide an 'Input' and an 'Output' audio port. "
"Patches welcome!");
goto fail;
}
u->block_size = pa_frame_align(pa_mempool_block_size_max(m->core->mempool), &ss);
u->input = (LADSPA_Data*) pa_xnew(uint8_t, u->block_size);
if (LADSPA_IS_INPLACE_BROKEN(d->Properties))
u->output = (LADSPA_Data*) pa_xnew(uint8_t, u->block_size);
else
u->output = u->input;
u->channels = ss.channels;
for (c = 0; c < ss.channels; c++) {
if (!(u->handle[c] = d->instantiate(d, ss.rate))) {
pa_log("Failed to instantiate plugin %s with label %s for channel %i", plugin, d->Label, c);
goto fail;
}
d->connect_port(u->handle[c], input_port, u->input);
d->connect_port(u->handle[c], output_port, u->output);
}
if (!cdata && n_control > 0) {
pa_log("This plugin requires specification of %lu control parameters.", n_control);
goto fail;
}
if (n_control > 0) {
const char *state = NULL;
char *k;
unsigned long h;
u->control = pa_xnew(LADSPA_Data, n_control);
use_default = pa_xnew(pa_bool_t, n_control);
p = 0;
while ((k = pa_split(cdata, ",", &state))) {
float f;
if (*k == 0) {
use_default[p++] = TRUE;
pa_xfree(k);
continue;
}
if (pa_atof(k, &f) < 0) {
pa_log("Failed to parse control value '%s'", k);
pa_xfree(k);
goto fail;
}
pa_xfree(k);
if (p >= n_control) {
pa_log("Too many control values passed, %lu expected.", n_control);
goto fail;
}
use_default[p] = FALSE;
u->control[p++] = f;
}
if (p < n_control) {
pa_log("Not enough control values passed, %lu expected, %lu passed.", n_control, p);
goto fail;
}
h = 0;
for (p = 0; p < d->PortCount; p++) {
LADSPA_PortRangeHintDescriptor hint = d->PortRangeHints[p].HintDescriptor;
if (!LADSPA_IS_PORT_CONTROL(d->PortDescriptors[p]))
continue;
if (LADSPA_IS_PORT_OUTPUT(d->PortDescriptors[p])) {
for (c = 0; c < ss.channels; c++)
d->connect_port(u->handle[c], p, &u->control_out);
continue;
}
pa_assert(h < n_control);
if (use_default[h]) {
LADSPA_Data lower, upper;
if (!LADSPA_IS_HINT_HAS_DEFAULT(hint)) {
pa_log("Control port value left empty but plugin defines no default.");
goto fail;
}
lower = d->PortRangeHints[p].LowerBound;
upper = d->PortRangeHints[p].UpperBound;
if (LADSPA_IS_HINT_SAMPLE_RATE(hint)) {
lower *= ss.rate;
upper *= ss.rate;
}
switch (hint & LADSPA_HINT_DEFAULT_MASK) {
case LADSPA_HINT_DEFAULT_MINIMUM:
u->control[h] = lower;
break;
case LADSPA_HINT_DEFAULT_MAXIMUM:
u->control[h] = upper;
break;
case LADSPA_HINT_DEFAULT_LOW:
if (LADSPA_IS_HINT_LOGARITHMIC(hint))
u->control[h] = exp(log(lower) * 0.75 + log(upper) * 0.25);
else
u->control[h] = lower * 0.75 + upper * 0.25;
break;
case LADSPA_HINT_DEFAULT_MIDDLE:
if (LADSPA_IS_HINT_LOGARITHMIC(hint))
u->control[h] = exp(log(lower) * 0.5 + log(upper) * 0.5);
else
u->control[h] = lower * 0.5 + upper * 0.5;
break;
case LADSPA_HINT_DEFAULT_HIGH:
if (LADSPA_IS_HINT_LOGARITHMIC(hint))
u->control[h] = exp(log(lower) * 0.25 + log(upper) * 0.75);
else
u->control[h] = lower * 0.25 + upper * 0.75;
break;
case LADSPA_HINT_DEFAULT_0:
u->control[h] = 0;
break;
case LADSPA_HINT_DEFAULT_1:
u->control[h] = 1;
break;
case LADSPA_HINT_DEFAULT_100:
u->control[h] = 100;
break;
case LADSPA_HINT_DEFAULT_440:
u->control[h] = 440;
break;
default:
pa_assert_not_reached();
}
}
if (LADSPA_IS_HINT_INTEGER(hint))
u->control[h] = roundf(u->control[h]);
pa_log_debug("Binding %f to port %s", u->control[h], d->PortNames[p]);
for (c = 0; c < ss.channels; c++)
d->connect_port(u->handle[c], p, &u->control[h]);
h++;
}
pa_assert(h == n_control);
}
if (d->activate)
for (c = 0; c < u->channels; c++)
d->activate(u->handle[c]);
default_sink_name = pa_sprintf_malloc("%s.ladspa", master->name);
/* Create sink */
if (!(u->sink = pa_sink_new(m->core, __FILE__, pa_modargs_get_value(ma, "sink_name", default_sink_name), 0, &ss, &map))) {
pa_log("Failed to create sink.");
goto fail;
}
u->sink->parent.process_msg = sink_process_msg;
u->sink->set_state = sink_set_state;
u->sink->userdata = u;
u->sink->flags = PA_SINK_LATENCY;
pa_sink_set_module(u->sink, m);
pa_sink_set_description(u->sink, t = pa_sprintf_malloc("LADSPA plugin '%s' on '%s'", label, master->description));
pa_xfree(t);
pa_sink_set_asyncmsgq(u->sink, master->asyncmsgq);
pa_sink_set_rtpoll(u->sink, master->rtpoll);
/* Create sink input */
pa_sink_input_new_data_init(&data);
data.sink = u->master;
data.driver = __FILE__;
data.name = "LADSPA Stream";
pa_sink_input_new_data_set_sample_spec(&data, &ss);
pa_sink_input_new_data_set_channel_map(&data, &map);
data.module = m;
if (!(u->sink_input = pa_sink_input_new(m->core, &data, PA_SINK_INPUT_DONT_MOVE)))
goto fail;
u->sink_input->parent.process_msg = sink_input_process_msg;
u->sink_input->peek = sink_input_peek_cb;
u->sink_input->drop = sink_input_drop_cb;
u->sink_input->kill = sink_input_kill_cb;
u->sink_input->attach = sink_input_attach_cb;
u->sink_input->detach = sink_input_detach_cb;
u->sink_input->userdata = u;
pa_sink_put(u->sink);
pa_sink_input_put(u->sink_input);
pa_modargs_free(ma);
pa_xfree(use_default);
pa_xfree(default_sink_name);
return 0;
fail:
if (ma)
pa_modargs_free(ma);
pa_xfree(use_default);
pa_xfree(default_sink_name);
pa__done(m);
return -1;
}
void pa__done(pa_module*m) {
struct userdata *u;
unsigned c;
pa_assert(m);
if (!(u = m->userdata))
return;
if (u->sink_input) {
pa_sink_input_unlink(u->sink_input);
pa_sink_input_unref(u->sink_input);
}
if (u->sink) {
pa_sink_unlink(u->sink);
pa_sink_unref(u->sink);
}
if (u->memchunk.memblock)
pa_memblock_unref(u->memchunk.memblock);
for (c = 0; c < u->channels; c++)
if (u->handle[c]) {
if (u->descriptor->deactivate)
u->descriptor->deactivate(u->handle[c]);
u->descriptor->cleanup(u->handle[c]);
}
if (u->output != u->input)
pa_xfree(u->output);
pa_xfree(u->input);
pa_xfree(u->control);
pa_xfree(u);
}

View file

@ -26,12 +26,12 @@
#endif
#include <stdio.h>
#include <assert.h>
#include <unistd.h>
#include <string.h>
#include <lirc/lirc_client.h>
#include <stdlib.h>
#include <lirc/lirc_client.h>
#include <pulse/xmalloc.h>
#include <pulsecore/module.h>
@ -39,6 +39,7 @@
#include <pulsecore/namereg.h>
#include <pulsecore/sink.h>
#include <pulsecore/modargs.h>
#include <pulsecore/macro.h>
#include "module-lirc-symdef.h"
@ -68,11 +69,12 @@ static int lirc_in_use = 0;
static void io_callback(pa_mainloop_api *io, PA_GCC_UNUSED pa_io_event *e, PA_GCC_UNUSED int fd, pa_io_event_flags_t events, void*userdata) {
struct userdata *u = userdata;
char *name = NULL, *code = NULL;
assert(io);
assert(u);
pa_assert(io);
pa_assert(u);
if (events & (PA_IO_EVENT_HANGUP|PA_IO_EVENT_ERROR)) {
pa_log("lost connection to LIRC daemon.");
pa_log("Lost connection to LIRC daemon.");
goto fail;
}
@ -86,7 +88,7 @@ static void io_callback(pa_mainloop_api *io, PA_GCC_UNUSED pa_io_event *e, PA_GC
c = pa_xstrdup(code);
c[strcspn(c, "\n\r")] = 0;
pa_log_debug("raw IR code '%s'", c);
pa_log_debug("Raw IR code '%s'", c);
pa_xfree(c);
while (lirc_code2char(u->config, code, &name) == 0 && name) {
@ -99,7 +101,7 @@ static void io_callback(pa_mainloop_api *io, PA_GCC_UNUSED pa_io_event *e, PA_GC
MUTE_TOGGLE
} volchange = INVALID;
pa_log_info("translated IR code '%s'", name);
pa_log_info("Translated IR code '%s'", name);
if (strcasecmp(name, "volume-up") == 0)
volchange = UP;
@ -113,15 +115,15 @@ static void io_callback(pa_mainloop_api *io, PA_GCC_UNUSED pa_io_event *e, PA_GC
volchange = RESET;
if (volchange == INVALID)
pa_log_warn("recieved unknown IR code '%s'", name);
pa_log_warn("Recieved unknown IR code '%s'", name);
else {
pa_sink *s;
if (!(s = pa_namereg_get(u->module->core, u->sink_name, PA_NAMEREG_SINK, 1)))
pa_log("failed to get sink '%s'", u->sink_name);
pa_log("Failed to get sink '%s'", u->sink_name);
else {
int i;
pa_cvolume cv = *pa_sink_get_volume(s, PA_MIXER_HARDWARE);
pa_cvolume cv = *pa_sink_get_volume(s);
#define DELTA (PA_VOLUME_NORM/20)
@ -134,7 +136,7 @@ static void io_callback(pa_mainloop_api *io, PA_GCC_UNUSED pa_io_event *e, PA_GC
cv.values[i] = PA_VOLUME_NORM;
}
pa_sink_set_volume(s, PA_MIXER_HARDWARE, &cv);
pa_sink_set_volume(s, &cv);
break;
case DOWN:
@ -145,20 +147,20 @@ static void io_callback(pa_mainloop_api *io, PA_GCC_UNUSED pa_io_event *e, PA_GC
cv.values[i] = PA_VOLUME_MUTED;
}
pa_sink_set_volume(s, PA_MIXER_HARDWARE, &cv);
pa_sink_set_volume(s, &cv);
break;
case MUTE:
pa_sink_set_mute(s, PA_MIXER_HARDWARE, 0);
pa_sink_set_mute(s, 0);
break;
case RESET:
pa_sink_set_mute(s, PA_MIXER_HARDWARE, 1);
pa_sink_set_mute(s, 1);
break;
case MUTE_TOGGLE:
pa_sink_set_mute(s, PA_MIXER_HARDWARE, !pa_sink_get_mute(s, PA_MIXER_HARDWARE));
pa_sink_set_mute(s, !pa_sink_get_mute(s));
break;
case INVALID:
@ -179,13 +181,14 @@ fail:
pa_module_unload_request(u->module);
free(code);
pa_xfree(code);
}
int pa__init(pa_core *c, pa_module*m) {
int pa__init(pa_module*m) {
pa_modargs *ma = NULL;
struct userdata *u;
assert(c && m);
pa_assert(m);
if (lirc_in_use) {
pa_log("module-lirc may no be loaded twice.");
@ -197,7 +200,7 @@ int pa__init(pa_core *c, pa_module*m) {
goto fail;
}
m->userdata = u = pa_xmalloc(sizeof(struct userdata));
m->userdata = u = pa_xnew(struct userdata, 1);
u->module = m;
u->io = NULL;
u->config = NULL;
@ -215,7 +218,7 @@ int pa__init(pa_core *c, pa_module*m) {
goto fail;
}
u->io = c->mainloop->io_new(c->mainloop, u->lirc_fd, PA_IO_EVENT_INPUT|PA_IO_EVENT_HANGUP, io_callback, u);
u->io = m->core->mainloop->io_new(m->core->mainloop, u->lirc_fd, PA_IO_EVENT_INPUT|PA_IO_EVENT_HANGUP, io_callback, u);
lirc_in_use = 1;
@ -228,14 +231,13 @@ fail:
if (ma)
pa_modargs_free(ma);
pa__done(c, m);
pa__done(m);
return -1;
}
void pa__done(pa_core *c, pa_module*m) {
void pa__done(pa_module*m) {
struct userdata *u;
assert(c);
assert(m);
pa_assert(m);
if (!(u = m->userdata))
return;

View file

@ -26,7 +26,6 @@
#endif
#include <unistd.h>
#include <assert.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
@ -80,6 +79,8 @@ static int load_rules(struct userdata *u, const char *filename) {
struct rule *end = NULL;
char *fn = NULL;
pa_assert(u);
f = filename ?
fopen(fn = pa_xstrdup(filename), "r") :
pa_open_config_file(DEFAULT_MATCH_TABLE_FILE, DEFAULT_MATCH_TABLE_FILE_USER, NULL, &fn, "r");
@ -132,7 +133,7 @@ static int load_rules(struct userdata *u, const char *filename) {
goto finish;
}
rule = pa_xmalloc(sizeof(struct rule));
rule = pa_xnew(struct rule, 1);
rule->regex = regex;
rule->volume = volume;
rule->next = NULL;
@ -164,7 +165,9 @@ static void callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, v
struct userdata *u = userdata;
pa_sink_input *si;
struct rule *r;
assert(c && u);
pa_assert(c);
pa_assert(u);
if (t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW))
return;
@ -179,23 +182,24 @@ static void callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, v
if (!regexec(&r->regex, si->name, 0, NULL, 0)) {
pa_cvolume cv;
pa_log_debug("changing volume of sink input '%s' to 0x%03x", si->name, r->volume);
pa_cvolume_set(&cv, r->volume, si->sample_spec.channels);
pa_cvolume_set(&cv, si->sample_spec.channels, r->volume);
pa_sink_input_set_volume(si, &cv);
}
}
}
int pa__init(pa_core *c, pa_module*m) {
int pa__init(pa_module*m) {
pa_modargs *ma = NULL;
struct userdata *u;
assert(c && m);
pa_assert(m);
if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
pa_log("Failed to parse module arguments");
goto fail;
}
u = pa_xmalloc(sizeof(struct userdata));
u = pa_xnew(struct userdata, 1);
u->rules = NULL;
u->subscription = NULL;
m->userdata = u;
@ -203,23 +207,24 @@ int pa__init(pa_core *c, pa_module*m) {
if (load_rules(u, pa_modargs_get_value(ma, "table", NULL)) < 0)
goto fail;
u->subscription = pa_subscription_new(c, PA_SUBSCRIPTION_MASK_SINK_INPUT, callback, u);
u->subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_SINK_INPUT, callback, u);
pa_modargs_free(ma);
return 0;
fail:
pa__done(c, m);
pa__done(m);
if (ma)
pa_modargs_free(ma);
return -1;
}
void pa__done(pa_core *c, pa_module*m) {
void pa__done(pa_module*m) {
struct userdata* u;
struct rule *r, *n;
assert(c && m);
pa_assert(m);
if (!(u = m->userdata))
return;

View file

@ -26,7 +26,6 @@
#endif
#include <stdio.h>
#include <assert.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
@ -80,11 +79,12 @@ struct userdata {
static void io_callback(pa_mainloop_api *io, PA_GCC_UNUSED pa_io_event *e, PA_GCC_UNUSED int fd, pa_io_event_flags_t events, void*userdata) {
struct userdata *u = userdata;
assert(io);
assert(u);
pa_assert(io);
pa_assert(u);
if (events & (PA_IO_EVENT_HANGUP|PA_IO_EVENT_ERROR)) {
pa_log("lost connection to evdev device.");
pa_log("Lost connection to evdev device.");
goto fail;
}
@ -92,14 +92,14 @@ static void io_callback(pa_mainloop_api *io, PA_GCC_UNUSED pa_io_event *e, PA_GC
struct input_event ev;
if (pa_loop_read(u->fd, &ev, sizeof(ev), &u->fd_type) <= 0) {
pa_log("failed to read from event device: %s", pa_cstrerror(errno));
pa_log("Failed to read from event device: %s", pa_cstrerror(errno));
goto fail;
}
if (ev.type == EV_KEY && (ev.value == 1 || ev.value == 2)) {
enum { INVALID, UP, DOWN, MUTE_TOGGLE } volchange = INVALID;
pa_log_debug("key code=%u, value=%u", ev.code, ev.value);
pa_log_debug("Key code=%u, value=%u", ev.code, ev.value);
switch (ev.code) {
case KEY_VOLUMEDOWN: volchange = DOWN; break;
@ -111,10 +111,10 @@ static void io_callback(pa_mainloop_api *io, PA_GCC_UNUSED pa_io_event *e, PA_GC
pa_sink *s;
if (!(s = pa_namereg_get(u->module->core, u->sink_name, PA_NAMEREG_SINK, 1)))
pa_log("failed to get sink '%s'", u->sink_name);
pa_log("Failed to get sink '%s'", u->sink_name);
else {
int i;
pa_cvolume cv = *pa_sink_get_volume(s, PA_MIXER_HARDWARE);
pa_cvolume cv = *pa_sink_get_volume(s);
#define DELTA (PA_VOLUME_NORM/20)
@ -127,7 +127,7 @@ static void io_callback(pa_mainloop_api *io, PA_GCC_UNUSED pa_io_event *e, PA_GC
cv.values[i] = PA_VOLUME_NORM;
}
pa_sink_set_volume(s, PA_MIXER_HARDWARE, &cv);
pa_sink_set_volume(s, &cv);
break;
case DOWN:
@ -138,12 +138,12 @@ static void io_callback(pa_mainloop_api *io, PA_GCC_UNUSED pa_io_event *e, PA_GC
cv.values[i] = PA_VOLUME_MUTED;
}
pa_sink_set_volume(s, PA_MIXER_HARDWARE, &cv);
pa_sink_set_volume(s, &cv);
break;
case MUTE_TOGGLE:
pa_sink_set_mute(s, PA_MIXER_HARDWARE, !pa_sink_get_mute(s, PA_MIXER_HARDWARE));
pa_sink_set_mute(s, !pa_sink_get_mute(s));
break;
case INVALID:
@ -165,21 +165,23 @@ fail:
#define test_bit(bit, array) (array[bit/8] & (1<<(bit%8)))
int pa__init(pa_core *c, pa_module*m) {
int pa__init(pa_module*m) {
pa_modargs *ma = NULL;
struct userdata *u;
int version;
struct _input_id input_id;
char name[256];
uint8_t evtype_bitmask[EV_MAX/8 + 1];
assert(c && m);
pa_assert(m);
if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
pa_log("Failed to parse module arguments");
goto fail;
}
m->userdata = u = pa_xmalloc(sizeof(struct userdata));
m->userdata = u = pa_xnew(struct userdata,1);
u->module = m;
u->io = NULL;
u->sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL));
@ -221,11 +223,11 @@ int pa__init(pa_core *c, pa_module*m) {
}
if (!test_bit(EV_KEY, evtype_bitmask)) {
pa_log("device has no keys.");
pa_log("Device has no keys.");
goto fail;
}
u->io = c->mainloop->io_new(c->mainloop, u->fd, PA_IO_EVENT_INPUT|PA_IO_EVENT_HANGUP, io_callback, u);
u->io = m->core->mainloop->io_new(m->core->mainloop, u->fd, PA_IO_EVENT_INPUT|PA_IO_EVENT_HANGUP, io_callback, u);
pa_modargs_free(ma);
@ -236,14 +238,14 @@ fail:
if (ma)
pa_modargs_free(ma);
pa__done(c, m);
pa__done(m);
return -1;
}
void pa__done(pa_core *c, pa_module*m) {
void pa__done(pa_module*m) {
struct userdata *u;
assert(c);
assert(m);
pa_assert(m);
if (!(u = m->userdata))
return;
@ -252,7 +254,7 @@ void pa__done(pa_core *c, pa_module*m) {
m->core->mainloop->io_free(u->io);
if (u->fd >= 0)
close(u->fd);
pa_assert_se(pa_close(u->fd) == 0);
pa_xfree(u->sink_name);
pa_xfree(u);

View file

@ -26,10 +26,10 @@
#endif
#include <stdio.h>
#include <assert.h>
#include <unistd.h>
#include <pulsecore/module.h>
#include <pulsecore/macro.h>
#include <pulsecore/iochannel.h>
#include <pulsecore/modargs.h>
#include <pulsecore/protocol-native.h>
@ -48,25 +48,26 @@ static const char* const valid_modargs[] = {
NULL,
};
int pa__init(pa_core *c, pa_module*m) {
int pa__init(pa_module*m) {
pa_iochannel *io;
pa_modargs *ma;
int fd, r = -1;
assert(c && m);
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 finish;
}
if (pa_modargs_get_value_s32(ma, "fd", &fd) < 0) {
pa_log("invalid file descriptor.");
pa_log("Invalid file descriptor.");
goto finish;
}
io = pa_iochannel_new(c->mainloop, fd, fd);
io = pa_iochannel_new(m->core->mainloop, fd, fd);
if (!(m->userdata = pa_protocol_native_new_iochannel(c, io, m, ma))) {
if (!(m->userdata = pa_protocol_native_new_iochannel(m->core, io, m, ma))) {
pa_iochannel_free(io);
goto finish;
}
@ -80,8 +81,8 @@ finish:
return r;
}
void pa__done(pa_core *c, pa_module*m) {
assert(c && m);
void pa__done(pa_module*m) {
pa_assert(m);
pa_protocol_native_free(m->userdata);
}

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,12 +37,17 @@
#include <pulse/timeval.h>
#include <pulse/xmalloc.h>
#include <pulsecore/iochannel.h>
#include <pulsecore/macro.h>
#include <pulsecore/sink.h>
#include <pulsecore/module.h>
#include <pulsecore/core-util.h>
#include <pulsecore/core-error.h>
#include <pulsecore/modargs.h>
#include <pulsecore/log.h>
#include <pulsecore/thread.h>
#include <pulsecore/thread-mq.h>
#include <pulsecore/rtpoll.h>
#include <pulsecore/rtclock.h>
#include "module-null-sink-symdef.h"
@ -64,11 +68,14 @@ struct userdata {
pa_core *core;
pa_module *module;
pa_sink *sink;
pa_time_event *time_event;
pa_thread *thread;
pa_thread_mq thread_mq;
pa_rtpoll *rtpoll;
size_t block_size;
uint64_t n_bytes;
struct timeval start_time;
struct timeval timestamp;
};
static const char* const valid_modargs[] = {
@ -81,78 +88,132 @@ 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 int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
struct userdata *u = PA_SINK(o)->userdata;
switch (code) {
case PA_SINK_MESSAGE_SET_STATE:
if (PA_PTR_TO_UINT(data) == PA_SINK_RUNNING)
pa_rtclock_get(&u->timestamp);
break;
case PA_SINK_MESSAGE_GET_LATENCY: {
struct timeval now;
pa_rtclock_get(&now);
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;
}
}
return pa_sink_process_msg(o, code, data, offset, chunk);
}
static void thread_func(void *userdata) {
struct userdata *u = userdata;
pa_memchunk chunk;
struct timeval ntv = *tv;
size_t l;
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);
pa_thread_mq_install(&u->thread_mq);
pa_rtpoll_install(u->rtpoll);
u->n_bytes += l;
pa_rtclock_get(&u->timestamp);
for (;;) {
int ret;
/* Render some data and drop it immediately */
if (u->sink->thread_info.state == PA_SINK_RUNNING) {
struct timeval now;
pa_rtclock_get(&now);
if (pa_timeval_cmp(&u->timestamp, &now) <= 0) {
pa_sink_skip(u->sink, u->block_size);
pa_timeval_add(&u->timestamp, pa_bytes_to_usec(u->block_size, &u->sink->sample_spec));
}
pa_rtpoll_set_timer_absolute(u->rtpoll, &u->timestamp);
} else
pa_rtpoll_set_timer_disabled(u->rtpoll);
/* Hmm, nothing to do. Let's sleep */
if ((ret = pa_rtpoll_run(u->rtpoll, 1)) < 0)
goto fail;
if (ret == 0)
goto finish;
}
fail:
/* If this was no regular exit from the loop we have to continue
* processing messages until we received PA_MESSAGE_SHUTDOWN */
pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);
finish:
pa_log_debug("Thread shutting down");
}
static pa_usec_t get_latency(pa_sink *s) {
struct userdata *u = s->userdata;
pa_usec_t a, b;
struct timeval now;
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;
}
int pa__init(pa_core *c, pa_module*m) {
int pa__init(pa_module*m) {
struct userdata *u = NULL;
pa_sample_spec ss;
pa_channel_map map;
pa_modargs *ma = NULL;
assert(c);
assert(m);
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;
ss = m->core->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;
}
u = pa_xnew0(struct userdata, 1);
u->core = c;
u->core = m->core;
u->module = m;
m->userdata = u;
pa_thread_mq_init(&u->thread_mq, m->core->mainloop);
u->rtpoll = pa_rtpoll_new();
pa_rtpoll_item_new_asyncmsgq(u->rtpoll, PA_RTPOLL_EARLY, u->thread_mq.inq);
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.");
if (!(u->sink = pa_sink_new(m->core, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss, &map))) {
pa_log("Failed to create sink.");
goto fail;
}
u->sink->get_latency = get_latency;
u->sink->parent.process_msg = sink_process_msg;
u->sink->userdata = u;
pa_sink_set_owner(u->sink, m);
u->sink->flags = PA_SINK_LATENCY;
pa_sink_set_module(u->sink, m);
pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
pa_sink_set_rtpoll(u->sink, u->rtpoll);
pa_sink_set_description(u->sink, pa_modargs_get_value(ma, "description", "NULL sink"));
u->n_bytes = 0;
pa_gettimeofday(&u->start_time);
u->block_size = pa_bytes_per_second(&ss) / 20; /* 50 ms */
if (u->block_size <= 0)
u->block_size = pa_frame_size(&ss);
u->time_event = c->mainloop->time_new(c->mainloop, &u->start_time, time_callback, u);
if (!(u->thread = pa_thread_new(thread_func, u))) {
pa_log("Failed to create thread.");
goto fail;
}
u->block_size = pa_bytes_per_second(&ss) / 10;
pa_sink_put(u->sink);
pa_modargs_free(ma);
@ -162,22 +223,34 @@ fail:
if (ma)
pa_modargs_free(ma);
pa__done(c, m);
pa__done(m);
return -1;
}
void pa__done(pa_core *c, pa_module*m) {
void pa__done(pa_module*m) {
struct userdata *u;
assert(c && m);
pa_assert(m);
if (!(u = m->userdata))
return;
pa_sink_disconnect(u->sink);
pa_sink_unref(u->sink);
if (u->sink)
pa_sink_unlink(u->sink);
u->core->mainloop->time_free(u->time_event);
if (u->thread) {
pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL);
pa_thread_free(u->thread);
}
pa_thread_mq_done(&u->thread_mq);
if (u->sink)
pa_sink_unref(u->sink);
if (u->rtpoll)
pa_rtpoll_free(u->rtpoll);
pa_xfree(u);
}

View file

@ -1,637 +0,0 @@
/* $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 <sys/soundcard.h>
#include <sys/ioctl.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <stdio.h>
#include <assert.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <limits.h>
#ifdef HAVE_SYS_MMAN_H
#include <sys/mman.h>
#endif
#include <pulse/xmalloc.h>
#include <pulse/util.h>
#include <pulsecore/core-error.h>
#include <pulsecore/iochannel.h>
#include <pulsecore/sink.h>
#include <pulsecore/source.h>
#include <pulsecore/module.h>
#include <pulsecore/sample-util.h>
#include <pulsecore/core-util.h>
#include <pulsecore/modargs.h>
#include <pulsecore/log.h>
#include "oss-util.h"
#include "module-oss-mmap-symdef.h"
PA_MODULE_AUTHOR("Lennart Poettering")
PA_MODULE_DESCRIPTION("OSS Sink/Source (mmap)")
PA_MODULE_VERSION(PACKAGE_VERSION)
PA_MODULE_USAGE(
"sink_name=<name for the sink> "
"source_name=<name for the source> "
"device=<OSS device> "
"record=<enable source?> "
"playback=<enable sink?> "
"format=<sample format> "
"channels=<number of channels> "
"rate=<sample rate> "
"fragments=<number of fragments> "
"fragment_size=<fragment size> "
"channel_map=<channel map>")
struct userdata {
pa_sink *sink;
pa_source *source;
pa_core *core;
pa_sample_spec sample_spec;
size_t in_fragment_size, out_fragment_size;
unsigned in_fragments, out_fragments;
unsigned out_blocks_saved, in_blocks_saved;
int fd;
void *in_mmap, *out_mmap;
size_t in_mmap_length, out_mmap_length;
pa_io_event *io_event;
pa_memblock **in_memblocks, **out_memblocks;
unsigned out_current, in_current;
pa_module *module;
};
static const char* const valid_modargs[] = {
"sink_name",
"source_name",
"device",
"record",
"playback",
"fragments",
"fragment_size",
"format",
"rate",
"channels",
"channel_map",
NULL
};
#define DEFAULT_DEVICE "/dev/dsp"
#define DEFAULT_NFRAGS 12
#define DEFAULT_FRAGSIZE 1024
static void update_usage(struct userdata *u) {
pa_module_set_used(u->module,
(u->sink ? pa_sink_used_by(u->sink) : 0) +
(u->source ? pa_source_used_by(u->source) : 0));
}
static void clear_up(struct userdata *u) {
assert(u);
if (u->sink) {
pa_sink_disconnect(u->sink);
pa_sink_unref(u->sink);
u->sink = NULL;
}
if (u->source) {
pa_source_disconnect(u->source);
pa_source_unref(u->source);
u->source = NULL;
}
if (u->in_mmap && u->in_mmap != MAP_FAILED) {
munmap(u->in_mmap, u->in_mmap_length);
u->in_mmap = NULL;
}
if (u->out_mmap && u->out_mmap != MAP_FAILED) {
munmap(u->out_mmap, u->out_mmap_length);
u->out_mmap = NULL;
}
if (u->io_event) {
u->core->mainloop->io_free(u->io_event);
u->io_event = NULL;
}
if (u->fd >= 0) {
close(u->fd);
u->fd = -1;
}
}
static void out_fill_memblocks(struct userdata *u, unsigned n) {
assert(u && u->out_memblocks);
while (n > 0) {
pa_memchunk chunk;
if (u->out_memblocks[u->out_current])
pa_memblock_unref_fixed(u->out_memblocks[u->out_current]);
chunk.memblock = u->out_memblocks[u->out_current] =
pa_memblock_new_fixed(
u->core->mempool,
(uint8_t*) u->out_mmap+u->out_fragment_size*u->out_current,
u->out_fragment_size,
1);
assert(chunk.memblock);
chunk.length = chunk.memblock->length;
chunk.index = 0;
pa_sink_render_into_full(u->sink, &chunk);
u->out_current++;
while (u->out_current >= u->out_fragments)
u->out_current -= u->out_fragments;
n--;
}
}
static void do_write(struct userdata *u) {
struct count_info info;
assert(u && u->sink);
update_usage(u);
if (ioctl(u->fd, SNDCTL_DSP_GETOPTR, &info) < 0) {
pa_log("SNDCTL_DSP_GETOPTR: %s", pa_cstrerror(errno));
clear_up(u);
pa_module_unload_request(u->module);
return;
}
info.blocks += u->out_blocks_saved;
u->out_blocks_saved = 0;
if (!info.blocks)
return;
out_fill_memblocks(u, info.blocks);
}
static void in_post_memblocks(struct userdata *u, unsigned n) {
assert(u && u->in_memblocks);
while (n > 0) {
pa_memchunk chunk;
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.index = 0;
pa_source_post(u->source, &chunk);
}
u->in_current++;
while (u->in_current >= u->in_fragments)
u->in_current -= u->in_fragments;
n--;
}
}
static void in_clear_memblocks(struct userdata*u, unsigned n) {
unsigned i = u->in_current;
assert(u && u->in_memblocks);
if (n > u->in_fragments)
n = u->in_fragments;
while (n > 0) {
if (u->in_memblocks[i]) {
pa_memblock_unref_fixed(u->in_memblocks[i]);
u->in_memblocks[i] = NULL;
}
i++;
while (i >= u->in_fragments)
i -= u->in_fragments;
n--;
}
}
static void do_read(struct userdata *u) {
struct count_info info;
assert(u && u->source);
update_usage(u);
if (ioctl(u->fd, SNDCTL_DSP_GETIPTR, &info) < 0) {
pa_log("SNDCTL_DSP_GETIPTR: %s", pa_cstrerror(errno));
clear_up(u);
pa_module_unload_request(u->module);
return;
}
info.blocks += u->in_blocks_saved;
u->in_blocks_saved = 0;
if (!info.blocks)
return;
in_post_memblocks(u, info.blocks);
in_clear_memblocks(u, u->in_fragments/2);
}
static void io_callback(pa_mainloop_api *m, pa_io_event *e, PA_GCC_UNUSED int fd, pa_io_event_flags_t f, void *userdata) {
struct userdata *u = userdata;
assert (u && u->core->mainloop == m && u->io_event == e);
if (f & PA_IO_EVENT_ERROR) {
clear_up(u);
pa_module_unload_request(u->module);
return;
}
if (f & PA_IO_EVENT_INPUT)
do_read(u);
if (f & PA_IO_EVENT_OUTPUT)
do_write(u);
}
static pa_usec_t sink_get_latency_cb(pa_sink *s) {
struct userdata *u = s->userdata;
struct count_info info;
size_t bpos, n, total;
assert(s && u);
if (ioctl(u->fd, SNDCTL_DSP_GETOPTR, &info) < 0) {
pa_log("SNDCTL_DSP_GETOPTR: %s", pa_cstrerror(errno));
return 0;
}
u->out_blocks_saved += info.blocks;
total = u->out_fragments * u->out_fragment_size;
bpos = ((u->out_current + u->out_blocks_saved) * u->out_fragment_size) % total;
if (bpos <= (size_t) info.ptr)
n = total - (info.ptr - bpos);
else
n = bpos - info.ptr;
/* pa_log("n = %u, bpos = %u, ptr = %u, total=%u, fragsize = %u, n_frags = %u\n", n, bpos, (unsigned) info.ptr, total, u->out_fragment_size, u->out_fragments); */
return pa_bytes_to_usec(n, &s->sample_spec);
}
static pa_usec_t source_get_latency_cb(pa_source *s) {
struct userdata *u = s->userdata;
struct count_info info;
size_t bpos, n, total;
assert(s && u);
if (ioctl(u->fd, SNDCTL_DSP_GETIPTR, &info) < 0) {
pa_log("SNDCTL_DSP_GETIPTR: %s", pa_cstrerror(errno));
return 0;
}
u->in_blocks_saved += info.blocks;
total = u->in_fragments * u->in_fragment_size;
bpos = ((u->in_current + u->in_blocks_saved) * u->in_fragment_size) % total;
if (bpos <= (size_t) info.ptr)
n = info.ptr - bpos;
else
n = (u->in_fragments * u->in_fragment_size) - bpos + info.ptr;
/* pa_log("n = %u, bpos = %u, ptr = %u, total=%u, fragsize = %u, n_frags = %u\n", n, bpos, (unsigned) info.ptr, total, u->in_fragment_size, u->in_fragments); */
return pa_bytes_to_usec(n, &s->sample_spec);
}
static int sink_get_hw_volume(pa_sink *s) {
struct userdata *u = s->userdata;
if (pa_oss_get_pcm_volume(u->fd, &s->sample_spec, &s->hw_volume) < 0) {
pa_log_info("device doesn't support reading mixer settings: %s", pa_cstrerror(errno));
s->get_hw_volume = NULL;
return -1;
}
return 0;
}
static int sink_set_hw_volume(pa_sink *s) {
struct userdata *u = s->userdata;
if (pa_oss_set_pcm_volume(u->fd, &s->sample_spec, &s->hw_volume) < 0) {
pa_log_info("device doesn't support writing mixer settings: %s", pa_cstrerror(errno));
s->set_hw_volume = NULL;
return -1;
}
return 0;
}
static int source_get_hw_volume(pa_source *s) {
struct userdata *u = s->userdata;
if (pa_oss_get_input_volume(u->fd, &s->sample_spec, &s->hw_volume) < 0) {
pa_log_info("device doesn't support reading mixer settings: %s", pa_cstrerror(errno));
s->get_hw_volume = NULL;
return -1;
}
return 0;
}
static int source_set_hw_volume(pa_source *s) {
struct userdata *u = s->userdata;
if (pa_oss_set_input_volume(u->fd, &s->sample_spec, &s->hw_volume) < 0) {
pa_log_info("device doesn't support writing mixer settings: %s", pa_cstrerror(errno));
s->set_hw_volume = NULL;
return -1;
}
return 0;
}
int pa__init(pa_core *c, pa_module*m) {
struct audio_buf_info info;
struct userdata *u = NULL;
const char *p;
int nfrags, frag_size;
int mode, caps;
int enable_bits = 0, zero = 0;
int playback = 1, record = 1;
pa_modargs *ma = NULL;
char hwdesc[64], *t;
pa_channel_map map;
const char *name;
char *name_buf = NULL;
int namereg_fail;
assert(c);
assert(m);
m->userdata = u = pa_xnew0(struct userdata, 1);
u->module = m;
u->fd = -1;
u->core = c;
if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
pa_log("failed to parse module arguments.");
goto fail;
}
if (pa_modargs_get_value_boolean(ma, "record", &record) < 0 || pa_modargs_get_value_boolean(ma, "playback", &playback) < 0) {
pa_log("record= and playback= expect numeric arguments.");
goto fail;
}
if (!playback && !record) {
pa_log("neither playback nor record enabled for device.");
goto fail;
}
mode = (playback&&record) ? O_RDWR : (playback ? O_WRONLY : (record ? O_RDONLY : 0));
nfrags = DEFAULT_NFRAGS;
frag_size = DEFAULT_FRAGSIZE;
if (pa_modargs_get_value_s32(ma, "fragments", &nfrags) < 0 || pa_modargs_get_value_s32(ma, "fragment_size", &frag_size) < 0) {
pa_log("failed to parse fragments arguments");
goto fail;
}
u->sample_spec = c->default_sample_spec;
if (pa_modargs_get_sample_spec_and_channel_map(ma, &u->sample_spec, &map, PA_CHANNEL_MAP_OSS) < 0) {
pa_log("failed to parse sample specification or channel map");
goto fail;
}
if ((u->fd = pa_oss_open(p = pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), &mode, &caps)) < 0)
goto fail;
if (!(caps & DSP_CAP_MMAP) || !(caps & DSP_CAP_TRIGGER)) {
pa_log("OSS device not mmap capable.");
goto fail;
}
pa_log_info("device opened in %s mode.", mode == O_WRONLY ? "O_WRONLY" : (mode == O_RDONLY ? "O_RDONLY" : "O_RDWR"));
if (pa_oss_get_hw_description(p, hwdesc, sizeof(hwdesc)) >= 0)
pa_log_info("hardware name is '%s'.", hwdesc);
else
hwdesc[0] = 0;
if (nfrags >= 2 && frag_size >= 1)
if (pa_oss_set_fragments(u->fd, nfrags, frag_size) < 0)
goto fail;
if (pa_oss_auto_format(u->fd, &u->sample_spec) < 0)
goto fail;
if (mode != O_WRONLY) {
if (ioctl(u->fd, SNDCTL_DSP_GETISPACE, &info) < 0) {
pa_log("SNDCTL_DSP_GETISPACE: %s", pa_cstrerror(errno));
goto fail;
}
pa_log_info("input -- %u fragments of size %u.", info.fragstotal, info.fragsize);
u->in_mmap_length = (u->in_fragment_size = info.fragsize) * (u->in_fragments = info.fragstotal);
if ((u->in_mmap = mmap(NULL, u->in_mmap_length, PROT_READ, MAP_SHARED, u->fd, 0)) == MAP_FAILED) {
if (mode == O_RDWR) {
pa_log("mmap failed for input. Changing to O_WRONLY mode.");
mode = O_WRONLY;
} else {
pa_log("mmap(): %s", pa_cstrerror(errno));
goto fail;
}
} else {
if ((name = pa_modargs_get_value(ma, "source_name", NULL)))
namereg_fail = 1;
else {
name = name_buf = pa_sprintf_malloc("oss_input.%s", pa_path_get_filename(p));
namereg_fail = 0;
}
if (!(u->source = pa_source_new(c, __FILE__, name, namereg_fail, &u->sample_spec, &map)))
goto fail;
u->source->userdata = u;
u->source->get_latency = source_get_latency_cb;
u->source->get_hw_volume = source_get_hw_volume;
u->source->set_hw_volume = source_set_hw_volume;
pa_source_set_owner(u->source, m);
pa_source_set_description(u->source, t = pa_sprintf_malloc("OSS PCM/mmap() on %s%s%s%s",
p,
hwdesc[0] ? " (" : "",
hwdesc[0] ? hwdesc : "",
hwdesc[0] ? ")" : ""));
pa_xfree(t);
u->source->is_hardware = 1;
u->in_memblocks = pa_xnew0(pa_memblock*, u->in_fragments);
enable_bits |= PCM_ENABLE_INPUT;
}
}
pa_xfree(name_buf);
name_buf = NULL;
if (mode != O_RDONLY) {
if (ioctl(u->fd, SNDCTL_DSP_GETOSPACE, &info) < 0) {
pa_log("SNDCTL_DSP_GETOSPACE: %s", pa_cstrerror(errno));
goto fail;
}
pa_log_info("output -- %u fragments of size %u.", info.fragstotal, info.fragsize);
u->out_mmap_length = (u->out_fragment_size = info.fragsize) * (u->out_fragments = info.fragstotal);
if ((u->out_mmap = mmap(NULL, u->out_mmap_length, PROT_WRITE, MAP_SHARED, u->fd, 0)) == MAP_FAILED) {
if (mode == O_RDWR) {
pa_log("mmap filed for output. Changing to O_RDONLY mode.");
mode = O_RDONLY;
} else {
pa_log("mmap(): %s", pa_cstrerror(errno));
goto fail;
}
} else {
pa_silence_memory(u->out_mmap, u->out_mmap_length, &u->sample_spec);
if ((name = pa_modargs_get_value(ma, "sink_name", NULL)))
namereg_fail = 1;
else {
name = name_buf = pa_sprintf_malloc("oss_output.%s", pa_path_get_filename(p));
namereg_fail = 0;
}
if (!(u->sink = pa_sink_new(c, __FILE__, name, namereg_fail, &u->sample_spec, &map)))
goto fail;
u->sink->get_latency = sink_get_latency_cb;
u->sink->get_hw_volume = sink_get_hw_volume;
u->sink->set_hw_volume = sink_set_hw_volume;
u->sink->userdata = u;
pa_sink_set_owner(u->sink, m);
pa_sink_set_description(u->sink, t = pa_sprintf_malloc("OSS PCM/mmap() on %s%s%s%s",
p,
hwdesc[0] ? " (" : "",
hwdesc[0] ? hwdesc : "",
hwdesc[0] ? ")" : ""));
pa_xfree(t);
u->sink->is_hardware = 1;
u->out_memblocks = pa_xmalloc0(sizeof(struct memblock *)*u->out_fragments);
enable_bits |= PCM_ENABLE_OUTPUT;
}
}
pa_xfree(name_buf);
name_buf = NULL;
zero = 0;
if (ioctl(u->fd, SNDCTL_DSP_SETTRIGGER, &zero) < 0) {
pa_log("SNDCTL_DSP_SETTRIGGER: %s", pa_cstrerror(errno));
goto fail;
}
if (ioctl(u->fd, SNDCTL_DSP_SETTRIGGER, &enable_bits) < 0) {
pa_log("SNDCTL_DSP_SETTRIGGER: %s", pa_cstrerror(errno));
goto fail;
}
assert(u->source || u->sink);
u->io_event = c->mainloop->io_new(c->mainloop, u->fd, (u->source ? PA_IO_EVENT_INPUT : 0) | (u->sink ? PA_IO_EVENT_OUTPUT : 0), io_callback, u);
assert(u->io_event);
pa_modargs_free(ma);
/* Read mixer settings */
if (u->source)
source_get_hw_volume(u->source);
if (u->sink)
sink_get_hw_volume(u->sink);
return 0;
fail:
pa__done(c, m);
if (ma)
pa_modargs_free(ma);
pa_xfree(name_buf);
return -1;
}
void pa__done(pa_core *c, pa_module*m) {
struct userdata *u;
assert(c);
assert(m);
if (!(u = m->userdata))
return;
clear_up(u);
if (u->out_memblocks) {
unsigned i;
for (i = 0; i < u->out_fragments; i++)
if (u->out_memblocks[i])
pa_memblock_unref_fixed(u->out_memblocks[i]);
pa_xfree(u->out_memblocks);
}
if (u->in_memblocks) {
unsigned i;
for (i = 0; i < u->in_fragments; i++)
if (u->in_memblocks[i])
pa_memblock_unref_fixed(u->in_memblocks[i]);
pa_xfree(u->in_memblocks);
}
pa_xfree(u);
}

File diff suppressed because it is too large Load diff

View file

@ -28,22 +28,25 @@
#include <stdlib.h>
#include <sys/stat.h>
#include <stdio.h>
#include <assert.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <limits.h>
#include <sys/ioctl.h>
#include <poll.h>
#include <pulse/xmalloc.h>
#include <pulsecore/core-error.h>
#include <pulsecore/iochannel.h>
#include <pulsecore/sink.h>
#include <pulsecore/module.h>
#include <pulsecore/core-util.h>
#include <pulsecore/modargs.h>
#include <pulsecore/log.h>
#include <pulsecore/thread.h>
#include <pulsecore/thread-mq.h>
#include <pulsecore/rtpoll.h>
#include "module-pipe-sink-symdef.h"
@ -58,20 +61,24 @@ 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;
pa_module *module;
pa_sink *sink;
pa_thread *thread;
pa_thread_mq thread_mq;
pa_rtpoll *rtpoll;
char *filename;
pa_sink *sink;
pa_iochannel *io;
pa_defer_event *defer_event;
int fd;
pa_memchunk memchunk;
pa_module *module;
pa_rtpoll_item *rtpoll_item;
};
static const char* const valid_modargs[] = {
@ -84,133 +91,191 @@ static const char* const valid_modargs[] = {
NULL
};
static void do_write(struct userdata *u) {
ssize_t r;
assert(u);
static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
struct userdata *u = PA_SINK(o)->userdata;
u->core->mainloop->defer_enable(u->defer_event, 0);
switch (code) {
if (!pa_iochannel_is_writable(u->io))
return;
case PA_SINK_MESSAGE_GET_LATENCY: {
size_t n = 0;
int l;
pa_module_set_used(u->module, pa_sink_used_by(u->sink));
#ifdef TIOCINQ
if (ioctl(u->fd, TIOCINQ, &l) >= 0 && l > 0)
n = (size_t) l;
#endif
if (!u->memchunk.length)
if (pa_sink_render(u->sink, PIPE_BUF, &u->memchunk) < 0)
return;
n += u->memchunk.length;
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;
*((pa_usec_t*) data) = pa_bytes_to_usec(n, &u->sink->sample_spec);
break;
}
}
u->memchunk.index += r;
u->memchunk.length -= r;
return pa_sink_process_msg(o, code, data, offset, chunk);
}
if (u->memchunk.length <= 0) {
pa_memblock_unref(u->memchunk.memblock);
u->memchunk.memblock = NULL;
static void thread_func(void *userdata) {
struct userdata *u = userdata;
int write_type = 0;
pa_assert(u);
pa_log_debug("Thread starting up");
pa_thread_mq_install(&u->thread_mq);
pa_rtpoll_install(u->rtpoll);
for (;;) {
struct pollfd *pollfd;
int ret;
pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
/* Render some data and write it to the fifo */
if (u->sink->thread_info.state == PA_SINK_RUNNING && pollfd->revents) {
ssize_t l;
void *p;
if (u->memchunk.length <= 0)
pa_sink_render(u->sink, PIPE_BUF, &u->memchunk);
pa_assert(u->memchunk.length > 0);
p = pa_memblock_acquire(u->memchunk.memblock);
l = pa_write(u->fd, (uint8_t*) p + u->memchunk.index, u->memchunk.length, &write_type);
pa_memblock_release(u->memchunk.memblock);
pa_assert(l != 0);
if (l < 0) {
if (errno == EINTR)
continue;
else if (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);
pa_memchunk_reset(&u->memchunk);
}
pollfd->revents = 0;
}
}
/* Hmm, nothing to do. Let's sleep */
pollfd->events = u->sink->thread_info.state == PA_SINK_RUNNING ? POLLOUT : 0;
if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0)
goto fail;
if (ret == 0)
goto finish;
pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
if (pollfd->revents & ~POLLOUT) {
pa_log("FIFO shutdown.");
goto fail;
}
}
fail:
/* If this was no regular exit from the loop we have to continue
* processing messages until we received PA_MESSAGE_SHUTDOWN */
pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);
finish:
pa_log_debug("Thread shutting down");
}
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) {
struct userdata *u = userdata;
assert(u);
do_write(u);
}
static void io_callback(PA_GCC_UNUSED pa_iochannel *io, void*userdata) {
struct userdata *u = userdata;
assert(u);
do_write(u);
}
int pa__init(pa_core *c, pa_module*m) {
struct userdata *u = NULL;
int pa__init(pa_module*m) {
struct userdata *u;
struct stat st;
const char *p;
int fd = -1;
pa_sample_spec ss;
pa_channel_map map;
pa_modargs *ma = NULL;
pa_modargs *ma;
char *t;
struct pollfd *pollfd;
assert(c && m);
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;
ss = m->core->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 or channel map");
goto fail;
}
mkfifo(p = pa_modargs_get_value(ma, "file", DEFAULT_FIFO_NAME), 0777);
u = pa_xnew0(struct userdata, 1);
u->core = m->core;
u->module = m;
m->userdata = u;
pa_memchunk_reset(&u->memchunk);
pa_thread_mq_init(&u->thread_mq, m->core->mainloop);
u->rtpoll = pa_rtpoll_new();
pa_rtpoll_item_new_asyncmsgq(u->rtpoll, PA_RTPOLL_EARLY, u->thread_mq.inq);
if ((fd = open(p, O_RDWR)) < 0) {
pa_log("open('%s'): %s", p, pa_cstrerror(errno));
u->filename = pa_xstrdup(pa_modargs_get_value(ma, "file", DEFAULT_FILE_NAME));
mkfifo(u->filename, 0666);
if ((u->fd = open(u->filename, O_RDWR|O_NOCTTY)) < 0) {
pa_log("open('%s'): %s", u->filename, pa_cstrerror(errno));
goto fail;
}
pa_fd_set_cloexec(fd, 1);
pa_make_fd_cloexec(u->fd);
pa_make_fd_nonblock(u->fd);
if (fstat(fd, &st) < 0) {
pa_log("fstat('%s'): %s", p, pa_cstrerror(errno));
if (fstat(u->fd, &st) < 0) {
pa_log("fstat('%s'): %s", u->filename, pa_cstrerror(errno));
goto fail;
}
if (!S_ISFIFO(st.st_mode)) {
pa_log("'%s' is not a FIFO.", p);
pa_log("'%s' is not a FIFO.", u->filename);
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.");
if (!(u->sink = pa_sink_new(m->core, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss, &map))) {
pa_log("Failed to create sink.");
goto fail;
}
u->sink->notify = notify_cb;
u->sink->get_latency = get_latency_cb;
u->sink->parent.process_msg = sink_process_msg;
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));
u->sink->flags = PA_SINK_LATENCY;
pa_sink_set_module(u->sink, m);
pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
pa_sink_set_rtpoll(u->sink, u->rtpoll);
pa_sink_set_description(u->sink, t = pa_sprintf_malloc("Unix FIFO sink '%s'", u->filename));
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->rtpoll_item = pa_rtpoll_item_new(u->rtpoll, PA_RTPOLL_NEVER, 1);
pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
pollfd->fd = u->fd;
pollfd->events = pollfd->revents = 0;
u->memchunk.memblock = NULL;
u->memchunk.length = 0;
if (!(u->thread = pa_thread_new(thread_func, u))) {
pa_log("Failed to create thread.");
goto fail;
}
u->defer_event = c->mainloop->defer_new(c->mainloop, defer_callback, u);
assert(u->defer_event);
c->mainloop->defer_enable(u->defer_event, 0);
pa_sink_put(u->sink);
pa_modargs_free(ma);
@ -220,32 +285,48 @@ fail:
if (ma)
pa_modargs_free(ma);
if (fd >= 0)
close(fd);
pa__done(c, m);
pa__done(m);
return -1;
}
void pa__done(pa_core *c, pa_module*m) {
void pa__done(pa_module*m) {
struct userdata *u;
assert(c && m);
pa_assert(m);
if (!(u = m->userdata))
return;
if (u->sink)
pa_sink_unlink(u->sink);
if (u->thread) {
pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL);
pa_thread_free(u->thread);
}
pa_thread_mq_done(&u->thread_mq);
if (u->sink)
pa_sink_unref(u->sink);
if (u->memchunk.memblock)
pa_memblock_unref(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);
if (u->rtpoll_item)
pa_rtpoll_item_free(u->rtpoll_item);
assert(u->filename);
unlink(u->filename);
pa_xfree(u->filename);
if (u->rtpoll)
pa_rtpoll_free(u->rtpoll);
if (u->filename) {
unlink(u->filename);
pa_xfree(u->filename);
}
if (u->fd >= 0)
pa_assert_se(pa_close(u->fd) == 0);
pa_xfree(u);
}

View file

@ -28,22 +28,24 @@
#include <stdlib.h>
#include <sys/stat.h>
#include <stdio.h>
#include <assert.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <limits.h>
#include <sys/poll.h>
#include <pulse/xmalloc.h>
#include <pulsecore/core-error.h>
#include <pulsecore/iochannel.h>
#include <pulsecore/source.h>
#include <pulsecore/module.h>
#include <pulsecore/core-util.h>
#include <pulsecore/modargs.h>
#include <pulsecore/log.h>
#include <pulsecore/thread.h>
#include <pulsecore/thread-mq.h>
#include <pulsecore/rtpoll.h>
#include "module-pipe-source-symdef.h"
@ -58,18 +60,24 @@ PA_MODULE_USAGE(
"rate=<sample rate> "
"channel_map=<channel map>")
#define DEFAULT_FIFO_NAME "/tmp/music.input"
#define DEFAULT_FILE_NAME "/tmp/music.input"
#define DEFAULT_SOURCE_NAME "fifo_input"
struct userdata {
pa_core *core;
pa_module *module;
pa_source *source;
pa_thread *thread;
pa_thread_mq thread_mq;
pa_rtpoll *rtpoll;
char *filename;
int fd;
pa_source *source;
pa_iochannel *io;
pa_module *module;
pa_memchunk chunk;
pa_memchunk memchunk;
pa_rtpoll_item *rtpoll_item;
};
static const char* const valid_modargs[] = {
@ -82,109 +90,168 @@ static const char* const valid_modargs[] = {
NULL
};
static void do_read(struct userdata *u) {
ssize_t r;
pa_memchunk chunk;
assert(u);
if (!pa_iochannel_is_readable(u->io))
return;
pa_module_set_used(u->module, pa_idxset_size(u->source->outputs));
if (!u->chunk.memblock) {
u->chunk.memblock = pa_memblock_new(u->core->mempool, PIPE_BUF);
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) {
pa_log("read(): %s", pa_cstrerror(errno));
return;
}
u->chunk.length = r;
pa_source_post(u->source, &u->chunk);
u->chunk.index += r;
if (u->chunk.index >= u->chunk.memblock->length) {
u->chunk.index = u->chunk.length = 0;
pa_memblock_unref(u->chunk.memblock);
u->chunk.memblock = NULL;
}
}
static void io_callback(PA_GCC_UNUSED pa_iochannel *io, void*userdata) {
static void thread_func(void *userdata) {
struct userdata *u = userdata;
assert(u);
do_read(u);
int read_type = 0;
pa_assert(u);
pa_log_debug("Thread starting up");
pa_thread_mq_install(&u->thread_mq);
pa_rtpoll_install(u->rtpoll);
for (;;) {
int ret;
struct pollfd *pollfd;
pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
/* Try to read some data and pass it on to the source driver */
if (u->source->thread_info.state == PA_SOURCE_RUNNING && pollfd->revents) {
ssize_t l;
void *p;
if (!u->memchunk.memblock) {
u->memchunk.memblock = pa_memblock_new(u->core->mempool, PIPE_BUF);
u->memchunk.index = u->memchunk.length = 0;
}
pa_assert(pa_memblock_get_length(u->memchunk.memblock) > u->memchunk.index);
p = pa_memblock_acquire(u->memchunk.memblock);
l = pa_read(u->fd, (uint8_t*) p + u->memchunk.index, pa_memblock_get_length(u->memchunk.memblock) - u->memchunk.index, &read_type);
pa_memblock_release(u->memchunk.memblock);
pa_assert(l != 0); /* EOF cannot happen, since we opened the fifo for both reading and writing */
if (l < 0) {
if (errno == EINTR)
continue;
else if (errno != EAGAIN) {
pa_log("Faile to read data from FIFO: %s", pa_cstrerror(errno));
goto fail;
}
} else {
u->memchunk.length = l;
pa_source_post(u->source, &u->memchunk);
u->memchunk.index += l;
if (u->memchunk.index >= pa_memblock_get_length(u->memchunk.memblock)) {
pa_memblock_unref(u->memchunk.memblock);
pa_memchunk_reset(&u->memchunk);
}
pollfd->revents = 0;
}
}
/* Hmm, nothing to do. Let's sleep */
pollfd->events = u->source->thread_info.state == PA_SOURCE_RUNNING ? POLLIN : 0;
if ((ret = pa_rtpoll_run(u->rtpoll, 1)) < 0)
goto fail;
if (ret == 0)
goto finish;
pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
if (pollfd->revents & ~POLLIN) {
pa_log("FIFO shutdown.");
goto fail;
}
}
fail:
/* If this was no regular exit from the loop we have to continue
* processing messages until we received PA_MESSAGE_SHUTDOWN */
pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);
finish:
pa_log_debug("Thread shutting down");
}
int pa__init(pa_core *c, pa_module*m) {
struct userdata *u = NULL;
int pa__init(pa_module*m) {
struct userdata *u;
struct stat st;
const char *p;
int fd = -1;
pa_sample_spec ss;
pa_channel_map map;
pa_modargs *ma = NULL;
pa_modargs *ma;
char *t;
struct pollfd *pollfd;
assert(c && m);
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;
ss = m->core->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");
goto fail;
}
mkfifo(p = pa_modargs_get_value(ma, "file", DEFAULT_FIFO_NAME), 0777);
u = pa_xnew0(struct userdata, 1);
u->core = m->core;
u->module = m;
m->userdata = u;
pa_memchunk_reset(&u->memchunk);
pa_thread_mq_init(&u->thread_mq, m->core->mainloop);
u->rtpoll = pa_rtpoll_new();
pa_rtpoll_item_new_asyncmsgq(u->rtpoll, PA_RTPOLL_EARLY, u->thread_mq.inq);
if ((fd = open(p, O_RDWR)) < 0) {
pa_log("open('%s'): %s", p, pa_cstrerror(errno));
u->filename = pa_xstrdup(pa_modargs_get_value(ma, "file", DEFAULT_FILE_NAME));
mkfifo(u->filename, 0666);
if ((u->fd = open(u->filename, O_RDWR|O_NOCTTY)) < 0) {
pa_log("open('%s'): %s", u->filename, pa_cstrerror(errno));
goto fail;
}
pa_fd_set_cloexec(fd, 1);
pa_make_fd_cloexec(u->fd);
pa_make_fd_nonblock(u->fd);
if (fstat(fd, &st) < 0) {
pa_log("fstat('%s'): %s", p, pa_cstrerror(errno));
if (fstat(u->fd, &st) < 0) {
pa_log("fstat('%s'): %s",u->filename, pa_cstrerror(errno));
goto fail;
}
if (!S_ISFIFO(st.st_mode)) {
pa_log("'%s' is not a FIFO.", p);
pa_log("'%s' is not a FIFO.", u->filename);
goto fail;
}
u = pa_xmalloc0(sizeof(struct userdata));
u->filename = pa_xstrdup(p);
u->core = c;
if (!(u->source = pa_source_new(c, __FILE__, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME), 0, &ss, &map))) {
pa_log("failed to create source.");
if (!(u->source = pa_source_new(m->core, __FILE__, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME), 0, &ss, &map))) {
pa_log("Failed to create source.");
goto fail;
}
u->source->userdata = u;
pa_source_set_owner(u->source, m);
pa_source_set_description(u->source, t = pa_sprintf_malloc("Unix FIFO source '%s'", p));
u->source->flags = 0;
pa_source_set_module(u->source, m);
pa_source_set_asyncmsgq(u->source, u->thread_mq.inq);
pa_source_set_rtpoll(u->source, u->rtpoll);
pa_source_set_description(u->source, t = pa_sprintf_malloc("Unix FIFO source '%s'", u->filename));
pa_xfree(t);
u->io = pa_iochannel_new(c->mainloop, fd, -1);
assert(u->io);
pa_iochannel_set_callback(u->io, io_callback, u);
u->rtpoll_item = pa_rtpoll_item_new(u->rtpoll, PA_RTPOLL_NEVER, 1);
pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
pollfd->fd = u->fd;
pollfd->events = pollfd->revents = 0;
u->chunk.memblock = NULL;
u->chunk.index = u->chunk.length = 0;
if (!(u->thread = pa_thread_new(thread_func, u))) {
pa_log("Failed to create thread.");
goto fail;
}
u->module = m;
m->userdata = u;
pa_source_put(u->source);
pa_modargs_free(ma);
@ -194,31 +261,48 @@ fail:
if (ma)
pa_modargs_free(ma);
if (fd >= 0)
close(fd);
pa__done(c, m);
pa__done(m);
return -1;
}
void pa__done(pa_core *c, pa_module*m) {
void pa__done(pa_module*m) {
struct userdata *u;
assert(c && m);
pa_assert(m);
if (!(u = m->userdata))
return;
if (u->chunk.memblock)
pa_memblock_unref(u->chunk.memblock);
if (u->source)
pa_source_unlink(u->source);
pa_source_disconnect(u->source);
pa_source_unref(u->source);
pa_iochannel_free(u->io);
if (u->thread) {
pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL);
pa_thread_free(u->thread);
}
assert(u->filename);
unlink(u->filename);
pa_xfree(u->filename);
pa_thread_mq_done(&u->thread_mq);
if (u->source)
pa_source_unref(u->source);
if (u->memchunk.memblock)
pa_memblock_unref(u->memchunk.memblock);
if (u->rtpoll_item)
pa_rtpoll_item_free(u->rtpoll_item);
if (u->rtpoll)
pa_rtpoll_free(u->rtpoll);
if (u->filename) {
unlink(u->filename);
pa_xfree(u->filename);
}
if (u->fd >= 0)
pa_assert_se(pa_close(u->fd) == 0);
pa_xfree(u);
}

View file

@ -29,7 +29,6 @@
#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <assert.h>
#include <unistd.h>
#include <limits.h>
@ -43,10 +42,9 @@
#include <netinet/in.h>
#endif
#include "../pulsecore/winsock.h"
#include <pulse/xmalloc.h>
#include <pulsecore/winsock.h>
#include <pulsecore/core-error.h>
#include <pulsecore/module.h>
#include <pulsecore/socket-server.h>
@ -154,7 +152,6 @@
#define protocol_free pa_protocol_esound_free
#define TCPWRAP_SERVICE "esound"
#define IPV4_PORT ESD_DEFAULT_PORT
#define UNIX_SOCKET ESD_UNIX_SOCKET_NAME
#define MODULE_ARGUMENTS_COMMON "sink", "source", "auth-anonymous", "cookie",
#ifdef USE_TCP_SOCKETS
#include "module-esound-protocol-tcp-symdef.h"
@ -205,10 +202,9 @@ struct userdata {
#endif
};
int pa__init(pa_core *c, pa_module*m) {
int pa__init(pa_module*m) {
pa_modargs *ma = NULL;
int ret = -1;
struct userdata *u = NULL;
#if defined(USE_TCP_SOCKETS)
@ -219,9 +215,13 @@ int pa__init(pa_core *c, pa_module*m) {
pa_socket_server *s;
int r;
char tmp[PATH_MAX];
#if defined(USE_PROTOCOL_ESOUND)
char tmp2[PATH_MAX];
#endif
#endif
assert(c && m);
pa_assert(m);
if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
pa_log("Failed to parse module arguments");
@ -239,22 +239,22 @@ int pa__init(pa_core *c, pa_module*m) {
listen_on = pa_modargs_get_value(ma, "listen", NULL);
if (listen_on) {
s_ipv6 = pa_socket_server_new_ipv6_string(c->mainloop, listen_on, port, TCPWRAP_SERVICE);
s_ipv4 = pa_socket_server_new_ipv4_string(c->mainloop, listen_on, port, TCPWRAP_SERVICE);
s_ipv6 = pa_socket_server_new_ipv6_string(m->core->mainloop, listen_on, port, TCPWRAP_SERVICE);
s_ipv4 = pa_socket_server_new_ipv4_string(m->core->mainloop, listen_on, port, TCPWRAP_SERVICE);
} else {
s_ipv6 = pa_socket_server_new_ipv6_any(c->mainloop, port, TCPWRAP_SERVICE);
s_ipv4 = pa_socket_server_new_ipv4_any(c->mainloop, port, TCPWRAP_SERVICE);
s_ipv6 = pa_socket_server_new_ipv6_any(m->core->mainloop, port, TCPWRAP_SERVICE);
s_ipv4 = pa_socket_server_new_ipv4_any(m->core->mainloop, port, TCPWRAP_SERVICE);
}
if (!s_ipv4 && !s_ipv6)
goto fail;
if (s_ipv4)
if (!(u->protocol_ipv4 = protocol_new(c, s_ipv4, m, ma)))
if (!(u->protocol_ipv4 = protocol_new(m->core, s_ipv4, m, ma)))
pa_socket_server_unref(s_ipv4);
if (s_ipv6)
if (!(u->protocol_ipv6 = protocol_new(c, s_ipv6, m, ma)))
if (!(u->protocol_ipv6 = protocol_new(m->core, s_ipv6, m, ma)))
pa_socket_server_unref(s_ipv6);
if (!u->protocol_ipv4 && !u->protocol_ipv6)
@ -262,18 +262,23 @@ int pa__init(pa_core *c, pa_module*m) {
#else
pa_runtime_path(pa_modargs_get_value(ma, "socket", UNIX_SOCKET), tmp, sizeof(tmp));
u->socket_path = pa_xstrdup(tmp);
#if defined(USE_PROTOCOL_ESOUND)
snprintf(tmp2, sizeof(tmp2), "/tmp/.esd-%lu/socket", (unsigned long) getuid());
pa_runtime_path(pa_modargs_get_value(ma, "socket", tmp2), tmp, sizeof(tmp));
u->socket_path = pa_xstrdup(tmp);
/* This socket doesn't reside in our own runtime dir but in
* /tmp/.esd/, hence we have to create the dir first */
if (pa_make_secure_parent_dir(u->socket_path, c->is_system_instance ? 0755 : 0700, (uid_t)-1, (gid_t)-1) < 0) {
if (pa_make_secure_parent_dir(u->socket_path, m->core->is_system_instance ? 0755 : 0700, (uid_t)-1, (gid_t)-1) < 0) {
pa_log("Failed to create socket directory '%s': %s\n", u->socket_path, pa_cstrerror(errno));
goto fail;
}
#else
pa_runtime_path(pa_modargs_get_value(ma, "socket", UNIX_SOCKET), tmp, sizeof(tmp));
u->socket_path = pa_xstrdup(tmp);
#endif
if ((r = pa_unix_socket_remove_stale(tmp)) < 0) {
@ -284,10 +289,10 @@ int pa__init(pa_core *c, pa_module*m) {
if (r)
pa_log("Removed stale UNIX socket '%s'.", tmp);
if (!(s = pa_socket_server_new_unix(c->mainloop, tmp)))
if (!(s = pa_socket_server_new_unix(m->core->mainloop, tmp)))
goto fail;
if (!(u->protocol_unix = protocol_new(c, s, m, ma)))
if (!(u->protocol_unix = protocol_new(m->core, s, m, ma)))
goto fail;
#endif
@ -333,11 +338,10 @@ fail:
goto finish;
}
void pa__done(pa_core *c, pa_module*m) {
void pa__done(pa_module*m) {
struct userdata *u;
assert(c);
assert(m);
pa_assert(m);
u = m->userdata;
@ -358,7 +362,6 @@ void pa__done(pa_core *c, pa_module*m) {
}
#endif
pa_xfree(u->socket_path);
#endif

View file

@ -0,0 +1,334 @@
/* $Id$ */
/***
This file is part of PulseAudio.
Copyright 2004-2006 Lennart Poettering
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <pulse/xmalloc.h>
#include <pulsecore/core-error.h>
#include <pulsecore/namereg.h>
#include <pulsecore/sink.h>
#include <pulsecore/module.h>
#include <pulsecore/core-util.h>
#include <pulsecore/modargs.h>
#include <pulsecore/log.h>
#include <pulsecore/thread.h>
#include <pulsecore/thread-mq.h>
#include <pulsecore/rtpoll.h>
#include "module-remap-sink-symdef.h"
PA_MODULE_AUTHOR("Lennart Poettering")
PA_MODULE_DESCRIPTION("Virtual channel remapping sink")
PA_MODULE_VERSION(PACKAGE_VERSION)
PA_MODULE_USAGE(
"sink_name=<name for the sink> "
"master=<name of sink to remap> "
"master_channel_map=<channel map> "
"format=<sample format> "
"channels=<number of channels> "
"rate=<sample rate> "
"channel_map=<channel map>")
struct userdata {
pa_core *core;
pa_module *module;
pa_sink *sink, *master;
pa_sink_input *sink_input;
pa_memchunk memchunk;
};
static const char* const valid_modargs[] = {
"sink_name",
"master",
"master_channel_map",
"rate",
"format",
"channels",
"channel_map",
NULL
};
/* Called from I/O thread context */
static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
struct userdata *u = PA_SINK(o)->userdata;
switch (code) {
case PA_SINK_MESSAGE_GET_LATENCY: {
pa_usec_t usec = 0;
if (PA_MSGOBJECT(u->master)->process_msg(PA_MSGOBJECT(u->master), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0)
usec = 0;
*((pa_usec_t*) data) = usec + pa_bytes_to_usec(u->memchunk.length, &u->sink->sample_spec);
return 0;
}
}
return pa_sink_process_msg(o, code, data, offset, chunk);
}
/* Called from main context */
static int sink_set_state(pa_sink *s, pa_sink_state_t state) {
struct userdata *u;
pa_sink_assert_ref(s);
pa_assert_se(u = s->userdata);
if (PA_SINK_LINKED(state) && u->sink_input && PA_SINK_INPUT_LINKED(pa_sink_input_get_state(u->sink_input)))
pa_sink_input_cork(u->sink_input, state == PA_SINK_SUSPENDED);
return 0;
}
/* Called from I/O thread context */
static int sink_input_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
struct userdata *u = PA_SINK_INPUT(o)->userdata;
switch (code) {
case PA_SINK_INPUT_MESSAGE_GET_LATENCY:
*((pa_usec_t*) data) = pa_bytes_to_usec(u->memchunk.length, &u->sink_input->sample_spec);
/* Fall through, the default handler will add in the extra
* latency added by the resampler */
break;
}
return pa_sink_input_process_msg(o, code, data, offset, chunk);
}
/* Called from I/O thread context */
static int sink_input_peek_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) {
struct userdata *u;
pa_sink_input_assert_ref(i);
pa_assert_se(u = i->userdata);
if (!u->memchunk.memblock)
pa_sink_render(u->sink, length, &u->memchunk);
pa_assert(u->memchunk.memblock);
*chunk = u->memchunk;
pa_memblock_ref(chunk->memblock);
return 0;
}
/* Called from I/O thread context */
static void sink_input_drop_cb(pa_sink_input *i, size_t length) {
struct userdata *u;
pa_sink_input_assert_ref(i);
pa_assert_se(u = i->userdata);
pa_assert(length > 0);
if (u->memchunk.memblock) {
if (length < u->memchunk.length) {
u->memchunk.index += length;
u->memchunk.length -= length;
return;
}
pa_memblock_unref(u->memchunk.memblock);
length -= u->memchunk.length;
pa_memchunk_reset(&u->memchunk);
}
if (length > 0)
pa_sink_skip(u->sink, length);
}
/* Called from I/O thread context */
static void sink_input_detach_cb(pa_sink_input *i) {
struct userdata *u;
pa_sink_input_assert_ref(i);
pa_assert_se(u = i->userdata);
pa_sink_detach_within_thread(u->sink);
}
/* Called from I/O thread context */
static void sink_input_attach_cb(pa_sink_input *i) {
struct userdata *u;
pa_sink_input_assert_ref(i);
pa_assert_se(u = i->userdata);
pa_sink_set_asyncmsgq(u->sink, i->sink->asyncmsgq);
pa_sink_set_rtpoll(u->sink, i->sink->rtpoll);
pa_sink_attach_within_thread(u->sink);
}
/* Called from main context */
static void sink_input_kill_cb(pa_sink_input *i) {
struct userdata *u;
pa_sink_input_assert_ref(i);
pa_assert_se(u = i->userdata);
pa_sink_input_unlink(u->sink_input);
pa_sink_input_unref(u->sink_input);
u->sink_input = NULL;
pa_sink_unlink(u->sink);
pa_sink_unref(u->sink);
u->sink = NULL;
pa_module_unload_request(u->module);
}
int pa__init(pa_module*m) {
struct userdata *u;
pa_sample_spec ss;
pa_channel_map sink_map, stream_map;
pa_modargs *ma;
char *t;
pa_sink *master;
pa_sink_input_new_data data;
char *default_sink_name = NULL;
pa_assert(m);
if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
pa_log("Failed to parse module arguments.");
goto fail;
}
if (!(master = pa_namereg_get(m->core, pa_modargs_get_value(ma, "master", NULL), PA_NAMEREG_SINK, 1))) {
pa_log("Master sink not found");
goto fail;
}
ss = master->sample_spec;
sink_map = master->channel_map;
if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &sink_map, PA_CHANNEL_MAP_DEFAULT) < 0) {
pa_log("Invalid sample format specification or channel map");
goto fail;
}
stream_map = sink_map;
if (pa_modargs_get_channel_map(ma, "master_channel_map", &stream_map) < 0) {
pa_log("Invalid master hannel map");
goto fail;
}
if (stream_map.channels != ss.channels) {
pa_log("Number of channels doesn't match");
goto fail;
}
u = pa_xnew0(struct userdata, 1);
u->core = m->core;
u->module = m;
m->userdata = u;
u->master = master;
pa_memchunk_reset(&u->memchunk);
default_sink_name = pa_sprintf_malloc("%s.remapped", master->name);
/* Create sink */
if (!(u->sink = pa_sink_new(m->core, __FILE__, pa_modargs_get_value(ma, "sink_name", default_sink_name), 0, &ss, &sink_map))) {
pa_log("Failed to create sink.");
goto fail;
}
u->sink->parent.process_msg = sink_process_msg;
u->sink->set_state = sink_set_state;
u->sink->userdata = u;
u->sink->flags = PA_SINK_LATENCY;
pa_sink_set_module(u->sink, m);
pa_sink_set_description(u->sink, t = pa_sprintf_malloc("Remapped %s", master->description));
pa_xfree(t);
pa_sink_set_asyncmsgq(u->sink, master->asyncmsgq);
pa_sink_set_rtpoll(u->sink, master->rtpoll);
/* Create sink input */
pa_sink_input_new_data_init(&data);
data.sink = u->master;
data.driver = __FILE__;
data.name = "Remapped Stream";
pa_sink_input_new_data_set_sample_spec(&data, &ss);
pa_sink_input_new_data_set_channel_map(&data, &stream_map);
data.module = m;
if (!(u->sink_input = pa_sink_input_new(m->core, &data, PA_SINK_INPUT_DONT_MOVE)))
goto fail;
u->sink_input->parent.process_msg = sink_input_process_msg;
u->sink_input->peek = sink_input_peek_cb;
u->sink_input->drop = sink_input_drop_cb;
u->sink_input->kill = sink_input_kill_cb;
u->sink_input->attach = sink_input_attach_cb;
u->sink_input->detach = sink_input_detach_cb;
u->sink_input->userdata = u;
pa_sink_put(u->sink);
pa_sink_input_put(u->sink_input);
pa_modargs_free(ma);
pa_xfree(default_sink_name);
return 0;
fail:
if (ma)
pa_modargs_free(ma);
pa__done(m);
pa_xfree(default_sink_name);
return -1;
}
void pa__done(pa_module*m) {
struct userdata *u;
pa_assert(m);
if (!(u = m->userdata))
return;
if (u->sink_input) {
pa_sink_input_unlink(u->sink_input);
pa_sink_input_unref(u->sink_input);
}
if (u->sink) {
pa_sink_unlink(u->sink);
pa_sink_unref(u->sink);
}
if (u->memchunk.memblock)
pa_memblock_unref(u->memchunk.memblock);
pa_xfree(u);
}

View file

@ -52,20 +52,26 @@ static pa_hook_result_t sink_hook_callback(pa_core *c, pa_sink *sink, void* user
pa_sink_input *i;
pa_sink *target;
assert(c);
assert(sink);
pa_assert(c);
pa_assert(sink);
if (!pa_idxset_size(sink->inputs)) {
pa_log_debug("No sink inputs to move away.");
return PA_HOOK_OK;
}
if (!(target = pa_namereg_get(c, NULL, PA_NAMEREG_SINK, 0))) {
pa_log_info("No evacuation sink found.");
return PA_HOOK_OK;
}
if (!(target = pa_namereg_get(c, NULL, PA_NAMEREG_SINK, 0)) || target == sink) {
uint32_t idx;
assert(target != sink);
for (target = pa_idxset_first(c->sinks, &idx); target; target = pa_idxset_next(c->sinks, &idx))
if (target != sink)
break;
if (!target) {
pa_log_info("No evacuation sink found.");
return PA_HOOK_OK;
}
}
while ((i = pa_idxset_first(sink->inputs, NULL))) {
if (pa_sink_input_move_to(i, target, 1) < 0) {
@ -84,20 +90,28 @@ static pa_hook_result_t source_hook_callback(pa_core *c, pa_source *source, void
pa_source_output *o;
pa_source *target;
assert(c);
assert(source);
pa_assert(c);
pa_assert(source);
if (!pa_idxset_size(source->outputs)) {
pa_log_debug("No source outputs to move away.");
return PA_HOOK_OK;
}
if (!(target = pa_namereg_get(c, NULL, PA_NAMEREG_SOURCE, 0))) {
pa_log_info("No evacuation source found.");
return PA_HOOK_OK;
if (!(target = pa_namereg_get(c, NULL, PA_NAMEREG_SOURCE, 0)) || target == source) {
uint32_t idx;
for (target = pa_idxset_first(c->sources, &idx); target; target = pa_idxset_next(c->sources, &idx))
if (target != source && !target->monitor_of == !source->monitor_of)
break;
if (!target) {
pa_log_info("No evacuation source found.");
return PA_HOOK_OK;
}
}
assert(target != source);
pa_assert(target != source);
while ((o = pa_idxset_first(source->outputs, NULL))) {
if (pa_source_output_move_to(o, target) < 0) {
@ -112,12 +126,11 @@ static pa_hook_result_t source_hook_callback(pa_core *c, pa_source *source, void
return PA_HOOK_OK;
}
int pa__init(pa_core *c, pa_module*m) {
int pa__init(pa_module*m) {
pa_modargs *ma = NULL;
struct userdata *u;
assert(c);
assert(m);
pa_assert(m);
if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
pa_log("Failed to parse module arguments");
@ -125,18 +138,17 @@ int pa__init(pa_core *c, pa_module*m) {
}
m->userdata = u = pa_xnew(struct userdata, 1);
u->sink_slot = pa_hook_connect(&c->hook_sink_disconnect, (pa_hook_cb_t) sink_hook_callback, NULL);
u->source_slot = pa_hook_connect(&c->hook_source_disconnect, (pa_hook_cb_t) source_hook_callback, NULL);
u->sink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_UNLINK], (pa_hook_cb_t) sink_hook_callback, NULL);
u->source_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK], (pa_hook_cb_t) source_hook_callback, NULL);
pa_modargs_free(ma);
return 0;
}
void pa__done(pa_core *c, pa_module*m) {
void pa__done(pa_module*m) {
struct userdata *u;
assert(c);
assert(m);
pa_assert(m);
if (!m->userdata)
return;

View file

@ -26,7 +26,6 @@
#endif
#include <stdio.h>
#include <assert.h>
#include <math.h>
#include <pulse/xmalloc.h>
@ -36,6 +35,7 @@
#include <pulsecore/modargs.h>
#include <pulsecore/namereg.h>
#include <pulsecore/log.h>
#include <pulsecore/core-util.h>
#include "module-sine-symdef.h"
@ -58,36 +58,46 @@ static const char* const valid_modargs[] = {
NULL,
};
static int sink_input_peek(pa_sink_input *i, pa_memchunk *chunk) {
static int sink_input_peek_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) {
struct userdata *u;
assert(i && chunk && i->userdata);
pa_assert(i);
u = i->userdata;
pa_assert(u);
pa_assert(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;
}
static void sink_input_drop(pa_sink_input *i, const pa_memchunk *chunk, size_t length) {
static void sink_input_drop_cb(pa_sink_input *i, size_t length) {
struct userdata *u;
assert(i && chunk && length && i->userdata);
u = i->userdata;
size_t l;
assert(chunk->memblock == u->memblock && length <= u->memblock->length-u->peek_index);
pa_assert(i);
u = i->userdata;
pa_assert(u);
pa_assert(length > 0);
u->peek_index += length;
if (u->peek_index >= u->memblock->length)
u->peek_index = 0;
l = pa_memblock_get_length(u->memblock);
while (u->peek_index >= l)
u->peek_index -= l;
}
static void sink_input_kill(pa_sink_input *i) {
static void sink_input_kill_cb(pa_sink_input *i) {
struct userdata *u;
assert(i && i->userdata);
u = i->userdata;
pa_sink_input_disconnect(u->sink_input);
pa_assert(i);
u = i->userdata;
pa_assert(u);
pa_sink_input_unlink(u->sink_input);
pa_sink_input_unref(u->sink_input);
u->sink_input = NULL;
@ -103,14 +113,14 @@ static void calc_sine(float *f, size_t l, float freq) {
f[i] = (float) sin((double) i/l*M_PI*2*freq)/2;
}
int pa__init(pa_core *c, pa_module*m) {
int pa__init(pa_module*m) {
pa_modargs *ma = NULL;
struct userdata *u;
pa_sink *sink;
const char *sink_name;
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))) {
@ -118,15 +128,14 @@ int pa__init(pa_core *c, pa_module*m) {
goto fail;
}
m->userdata = u = pa_xmalloc(sizeof(struct userdata));
u->core = c;
m->userdata = u = pa_xnew0(struct userdata, 1);
u->core = m->core;
u->module = m;
u->sink_input = NULL;
u->memblock = NULL;
u->peek_index = 0;
sink_name = pa_modargs_get_value(ma, "sink", NULL);
if (!(sink = pa_namereg_get(c, sink_name, PA_NAMEREG_SINK, 1))) {
if (!(sink = pa_namereg_get(m->core, pa_modargs_get_value(ma, "sink", NULL), PA_NAMEREG_SINK, 1))) {
pa_log("No such sink.");
goto fail;
}
@ -141,10 +150,12 @@ int pa__init(pa_core *c, pa_module*m) {
goto fail;
}
u->memblock = pa_memblock_new(c->mempool, pa_bytes_per_second(&ss));
calc_sine(u->memblock->data, u->memblock->length, frequency);
u->memblock = pa_memblock_new(m->core->mempool, pa_bytes_per_second(&ss));
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);
pa_snprintf(t, sizeof(t), "Sine Generator at %u Hz", frequency);
pa_sink_input_new_data_init(&data);
data.sink = sink;
@ -153,15 +164,15 @@ int pa__init(pa_core *c, pa_module*m) {
pa_sink_input_new_data_set_sample_spec(&data, &ss);
data.module = m;
if (!(u->sink_input = pa_sink_input_new(c, &data, 0)))
if (!(u->sink_input = pa_sink_input_new(m->core, &data, 0)))
goto fail;
u->sink_input->peek = sink_input_peek;
u->sink_input->drop = sink_input_drop;
u->sink_input->kill = sink_input_kill;
u->sink_input->peek = sink_input_peek_cb;
u->sink_input->drop = sink_input_drop_cb;
u->sink_input->kill = sink_input_kill_cb;
u->sink_input->userdata = u;
u->peek_index = 0;
pa_sink_input_put(u->sink_input);
pa_modargs_free(ma);
return 0;
@ -170,24 +181,26 @@ fail:
if (ma)
pa_modargs_free(ma);
pa__done(c, m);
pa__done(m);
return -1;
}
void pa__done(pa_core *c, pa_module*m) {
struct userdata *u = m->userdata;
assert(c && m);
void pa__done(pa_module*m) {
struct userdata *u;
if (!u)
pa_assert(m);
if (!(u = m->userdata))
return;
if (u->sink_input) {
pa_sink_input_disconnect(u->sink_input);
pa_sink_input_unlink(u->sink_input);
pa_sink_input_unref(u->sink_input);
}
if (u->memblock)
pa_memblock_unref(u->memblock);
pa_xfree(u);
}

View file

@ -4,7 +4,7 @@
This file is part of PulseAudio.
Copyright 2006 Lennart Poettering
Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
Copyright 2006-2007 Pierre Ossman <ossman@cendio.se> for Cendio AB
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
@ -57,6 +57,9 @@
#include <pulsecore/modargs.h>
#include <pulsecore/log.h>
#include <pulsecore/core-error.h>
#include <pulsecore/thread-mq.h>
#include <pulsecore/rtpoll.h>
#include <pulsecore/thread.h>
#include "module-solaris-symdef.h"
@ -75,12 +78,14 @@ PA_MODULE_USAGE(
"channel_map=<channel map>")
struct userdata {
pa_core *core;
pa_sink *sink;
pa_source *source;
pa_iochannel *io;
pa_core *core;
pa_time_event *timer;
pa_usec_t poll_timeout;
pa_thread *thread;
pa_thread_mq thread_mq;
pa_rtpoll *rtpoll;
pa_signal_event *sig;
pa_memchunk memchunk;
@ -90,9 +95,9 @@ struct userdata {
uint32_t frame_size;
uint32_t buffer_size;
unsigned int written_bytes, read_bytes;
int sink_underflow;
int fd;
pa_rtpoll_item *rtpoll_item;
pa_module *module;
};
@ -114,309 +119,357 @@ static const char* const valid_modargs[] = {
#define DEFAULT_SOURCE_NAME "solaris_input"
#define DEFAULT_DEVICE "/dev/audio"
#define CHUNK_SIZE 2048
static void update_usage(struct userdata *u) {
pa_module_set_used(u->module,
(u->sink ? pa_sink_used_by(u->sink) : 0) +
(u->source ? pa_source_used_by(u->source) : 0));
}
static void do_write(struct userdata *u) {
static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
struct userdata *u = PA_SINK(o)->userdata;
int err;
audio_info_t info;
int err;
size_t len;
ssize_t r;
assert(u);
switch (code) {
case PA_SINK_MESSAGE_GET_LATENCY: {
pa_usec_t r = 0;
/* We cannot check pa_iochannel_is_writable() because of our buffer hack */
if (!u->sink)
return;
if (u->fd >= 0) {
update_usage(u);
err = ioctl(u->fd, AUDIO_GETINFO, &info);
pa_assert(err >= 0);
err = ioctl(u->fd, AUDIO_GETINFO, &info);
assert(err >= 0);
r += pa_bytes_to_usec(u->written_bytes, &PA_SINK(o)->sample_spec);
r -= pa_bytes_to_usec(info.play.samples * u->frame_size, &PA_SINK(o)->sample_spec);
/*
* Since we cannot modify the size of the output buffer we fake it
* by not filling it more than u->buffer_size.
*/
len = u->buffer_size;
len -= u->written_bytes - (info.play.samples * u->frame_size);
/* The sample counter can sometimes go backwards :( */
if (len > u->buffer_size)
len = 0;
if (!u->sink_underflow && (len == u->buffer_size))
pa_log_debug("Solaris buffer underflow!");
len -= len % u->frame_size;
if (len == 0)
return;
if (!u->memchunk.length) {
if (pa_sink_render(u->sink, len, &u->memchunk) < 0) {
u->sink_underflow = 1;
return;
if (u->memchunk.memblock)
r += pa_bytes_to_usec(u->memchunk.length, &PA_SINK(o)->sample_spec);
}
*((pa_usec_t*) data) = r;
return 0;
}
u->sink_underflow = 0;
case PA_SINK_MESSAGE_SET_VOLUME:
if (u->fd >= 0) {
AUDIO_INITINFO(&info);
assert(u->memchunk.memblock);
assert(u->memchunk.memblock->data);
assert(u->memchunk.length);
info.play.gain = pa_cvolume_avg((pa_cvolume*)data) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;
assert(info.play.gain <= AUDIO_MAX_GAIN);
if (u->memchunk.length < len) {
len = u->memchunk.length;
len -= len % u->frame_size;
assert(len);
if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0) {
if (errno == EINVAL)
pa_log("AUDIO_SETINFO: Unsupported volume.");
else
pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
} else {
return 0;
}
}
break;
case PA_SINK_MESSAGE_GET_VOLUME:
if (u->fd >= 0) {
err = ioctl(u->fd, AUDIO_GETINFO, &info);
assert(err >= 0);
pa_cvolume_set((pa_cvolume*) data, ((pa_cvolume*) data)->channels,
info.play.gain * PA_VOLUME_NORM / AUDIO_MAX_GAIN);
return 0;
}
break;
case PA_SINK_MESSAGE_SET_MUTE:
if (u->fd >= 0) {
AUDIO_INITINFO(&info);
info.output_muted = !!PA_PTR_TO_UINT(data);
if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0)
pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
else
return 0;
}
break;
case PA_SINK_MESSAGE_GET_MUTE:
if (u->fd >= 0) {
err = ioctl(u->fd, AUDIO_GETINFO, &info);
pa_assert(err >= 0);
*(int*)data = !!info.output_muted;
return 0;
}
break;
}
if ((r = pa_iochannel_write(u->io,
(uint8_t*) u->memchunk.memblock->data + u->memchunk.index, len)) < 0) {
pa_log("write() failed: %s", pa_cstrerror(errno));
return;
}
assert(r % u->frame_size == 0);
u->memchunk.index += r;
u->memchunk.length -= r;
if (u->memchunk.length <= 0) {
pa_memblock_unref(u->memchunk.memblock);
u->memchunk.memblock = NULL;
}
u->written_bytes += r;
return pa_sink_process_msg(o, code, data, offset, chunk);
}
static void do_read(struct userdata *u) {
pa_memchunk memchunk;
static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
struct userdata *u = PA_SOURCE(o)->userdata;
int err;
size_t l;
ssize_t r;
assert(u);
audio_info_t info;
if (!u->source || !pa_iochannel_is_readable(u->io))
return;
switch (code) {
case PA_SOURCE_MESSAGE_GET_LATENCY: {
pa_usec_t r = 0;
update_usage(u);
if (u->fd) {
err = ioctl(u->fd, AUDIO_GETINFO, &info);
pa_assert(err >= 0);
err = ioctl(u->fd, I_NREAD, &l);
assert(err >= 0);
r += pa_bytes_to_usec(info.record.samples * u->frame_size, &PA_SOURCE(o)->sample_spec);
r -= pa_bytes_to_usec(u->read_bytes, &PA_SOURCE(o)->sample_spec);
}
/* This is to make sure it fits in the memory pool. Also, a page
should be the most efficient transfer size. */
if (l > u->page_size)
l = u->page_size;
*((pa_usec_t*) data) = r;
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) {
pa_memblock_unref(memchunk.memblock);
if (errno != EAGAIN)
pa_log("read() failed: %s", pa_cstrerror(errno));
return;
return 0;
}
case PA_SOURCE_MESSAGE_SET_VOLUME:
if (u->fd >= 0) {
AUDIO_INITINFO(&info);
info.record.gain = pa_cvolume_avg((pa_cvolume*) data) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;
assert(info.record.gain <= AUDIO_MAX_GAIN);
if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0) {
if (errno == EINVAL)
pa_log("AUDIO_SETINFO: Unsupported volume.");
else
pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
} else {
return 0;
}
}
break;
case PA_SOURCE_MESSAGE_GET_VOLUME:
if (u->fd >= 0) {
err = ioctl(u->fd, AUDIO_GETINFO, &info);
pa_assert(err >= 0);
pa_cvolume_set((pa_cvolume*) data, ((pa_cvolume*) data)->channels,
info.record.gain * PA_VOLUME_NORM / AUDIO_MAX_GAIN);
return 0;
}
break;
}
assert(r <= (ssize_t) memchunk.memblock->length);
memchunk.length = memchunk.memblock->length = r;
memchunk.index = 0;
pa_source_post(u->source, &memchunk);
pa_memblock_unref(memchunk.memblock);
u->read_bytes += r;
return pa_source_process_msg(o, code, data, offset, chunk);
}
static void io_callback(pa_iochannel *io, void*userdata) {
struct userdata *u = userdata;
assert(u);
do_write(u);
do_read(u);
static void clear_underflow(struct userdata *u)
{
audio_info_t info;
AUDIO_INITINFO(&info);
info.play.error = 0;
if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0)
pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
}
static void timer_cb(pa_mainloop_api*a, pa_time_event *e, const struct timeval *tv, void *userdata) {
static void clear_overflow(struct userdata *u)
{
audio_info_t info;
AUDIO_INITINFO(&info);
info.record.error = 0;
if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0)
pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
}
static void thread_func(void *userdata) {
struct userdata *u = userdata;
struct timeval ntv;
unsigned short revents = 0;
int ret;
assert(u);
pa_assert(u);
do_write(u);
pa_log_debug("Thread starting up");
pa_gettimeofday(&ntv);
pa_timeval_add(&ntv, u->poll_timeout);
if (u->core->high_priority)
pa_make_realtime();
a->time_restart(e, &ntv);
pa_thread_mq_install(&u->thread_mq);
pa_rtpoll_install(u->rtpoll);
for (;;) {
/* Render some data and write it to the dsp */
if (u->sink && PA_SINK_OPENED(u->sink->thread_info.state)) {
audio_info_t info;
int err;
size_t len;
err = ioctl(u->fd, AUDIO_GETINFO, &info);
pa_assert(err >= 0);
/*
* Since we cannot modify the size of the output buffer we fake it
* by not filling it more than u->buffer_size.
*/
len = u->buffer_size;
len -= u->written_bytes - (info.play.samples * u->frame_size);
/* The sample counter can sometimes go backwards :( */
if (len > u->buffer_size)
len = 0;
if (info.play.error) {
pa_log_debug("Solaris buffer underflow!");
clear_underflow(u);
}
len -= len % u->frame_size;
while (len) {
void *p;
ssize_t r;
if (!u->memchunk.length)
pa_sink_render(u->sink, len, &u->memchunk);
pa_assert(u->memchunk.length);
p = pa_memblock_acquire(u->memchunk.memblock);
r = pa_write(u->fd, (uint8_t*) p + u->memchunk.index, u->memchunk.length, NULL);
pa_memblock_release(u->memchunk.memblock);
if (r < 0) {
if (errno == EINTR)
continue;
else if (errno != EAGAIN) {
pa_log("Failed to read data from DSP: %s", pa_cstrerror(errno));
goto fail;
}
} else {
pa_assert(r % u->frame_size == 0);
u->memchunk.index += r;
u->memchunk.length -= r;
if (u->memchunk.length <= 0) {
pa_memblock_unref(u->memchunk.memblock);
pa_memchunk_reset(&u->memchunk);
}
len -= r;
u->written_bytes += r;
}
}
}
/* Try to read some data and pass it on to the source driver */
if (u->source && PA_SOURCE_OPENED(u->source->thread_info.state) && ((revents & POLLIN))) {
pa_memchunk memchunk;
int err;
size_t l;
void *p;
ssize_t r;
audio_info_t info;
err = ioctl(u->fd, AUDIO_GETINFO, &info);
pa_assert(err >= 0);
if (info.record.error) {
pa_log_debug("Solaris buffer overflow!");
clear_overflow(u);
}
err = ioctl(u->fd, I_NREAD, &l);
pa_assert(err >= 0);
if (l > 0) {
/* This is to make sure it fits in the memory pool. Also, a page
should be the most efficient transfer size. */
if (l > u->page_size)
l = u->page_size;
memchunk.memblock = pa_memblock_new(u->core->mempool, l);
pa_assert(memchunk.memblock);
p = pa_memblock_acquire(memchunk.memblock);
r = pa_read(u->fd, p, l, NULL);
pa_memblock_release(memchunk.memblock);
if (r < 0) {
pa_memblock_unref(memchunk.memblock);
if (errno != EAGAIN) {
pa_log("Failed to read data from DSP: %s", pa_cstrerror(errno));
goto fail;
}
} else {
memchunk.index = 0;
memchunk.length = r;
pa_source_post(u->source, &memchunk);
pa_memblock_unref(memchunk.memblock);
u->read_bytes += r;
revents &= ~POLLIN;
}
}
}
if (u->fd >= 0) {
struct pollfd *pollfd;
pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
pollfd->events =
((u->source && PA_SOURCE_OPENED(u->source->thread_info.state)) ? POLLIN : 0);
}
/* Hmm, nothing to do. Let's sleep */
if ((ret = pa_rtpoll_run(u->rtpoll, 1)) < 0)
goto fail;
if (ret == 0)
goto finish;
if (u->fd >= 0) {
struct pollfd *pollfd;
pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
if (pollfd->revents & ~(POLLOUT|POLLIN)) {
pa_log("DSP shutdown.");
goto fail;
}
revents = pollfd->revents;
} else
revents = 0;
}
fail:
/* We have to continue processing messages until we receive the
* SHUTDOWN message */
pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);
finish:
pa_log_debug("Thread shutting down");
}
static void sig_callback(pa_mainloop_api *api, pa_signal_event*e, int sig, void *userdata) {
struct userdata *u = userdata;
pa_cvolume old_vol;
assert(u);
if (u->sink) {
assert(u->sink->get_hw_volume);
memcpy(&old_vol, &u->sink->hw_volume, sizeof(pa_cvolume));
if (u->sink->get_hw_volume(u->sink) < 0)
return;
if (memcmp(&old_vol, &u->sink->hw_volume, sizeof(pa_cvolume)) != 0) {
pa_subscription_post(u->sink->core,
PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE,
u->sink->index);
}
pa_sink_get_volume(u->sink);
pa_sink_get_mute(u->sink);
}
if (u->source) {
assert(u->source->get_hw_volume);
memcpy(&old_vol, &u->source->hw_volume, sizeof(pa_cvolume));
if (u->source->get_hw_volume(u->source) < 0)
return;
if (memcmp(&old_vol, &u->source->hw_volume, sizeof(pa_cvolume)) != 0) {
pa_subscription_post(u->source->core,
PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE,
u->source->index);
}
}
}
static pa_usec_t sink_get_latency_cb(pa_sink *s) {
pa_usec_t r = 0;
audio_info_t info;
int err;
struct userdata *u = s->userdata;
assert(s && u && u->sink);
err = ioctl(u->fd, AUDIO_GETINFO, &info);
assert(err >= 0);
r += pa_bytes_to_usec(u->written_bytes, &s->sample_spec);
r -= pa_bytes_to_usec(info.play.samples * u->frame_size, &s->sample_spec);
if (u->memchunk.memblock)
r += pa_bytes_to_usec(u->memchunk.length, &s->sample_spec);
return r;
}
static pa_usec_t source_get_latency_cb(pa_source *s) {
pa_usec_t r = 0;
struct userdata *u = s->userdata;
audio_info_t info;
int err;
assert(s && u && u->source);
err = ioctl(u->fd, AUDIO_GETINFO, &info);
assert(err >= 0);
r += pa_bytes_to_usec(info.record.samples * u->frame_size, &s->sample_spec);
r -= pa_bytes_to_usec(u->read_bytes, &s->sample_spec);
return r;
}
static int sink_get_hw_volume_cb(pa_sink *s) {
struct userdata *u = s->userdata;
audio_info_t info;
int err;
err = ioctl(u->fd, AUDIO_GETINFO, &info);
assert(err >= 0);
pa_cvolume_set(&s->hw_volume, s->hw_volume.channels,
info.play.gain * PA_VOLUME_NORM / AUDIO_MAX_GAIN);
return 0;
}
static int sink_set_hw_volume_cb(pa_sink *s) {
struct userdata *u = s->userdata;
audio_info_t info;
AUDIO_INITINFO(&info);
info.play.gain = pa_cvolume_avg(&s->hw_volume) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;
assert(info.play.gain <= AUDIO_MAX_GAIN);
if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0) {
if (errno == EINVAL)
pa_log("AUDIO_SETINFO: Unsupported volume.");
else
pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
return -1;
}
return 0;
}
static int sink_get_hw_mute_cb(pa_sink *s) {
struct userdata *u = s->userdata;
audio_info_t info;
int err;
err = ioctl(u->fd, AUDIO_GETINFO, &info);
assert(err >= 0);
s->hw_muted = !!info.output_muted;
return 0;
}
static int sink_set_hw_mute_cb(pa_sink *s) {
struct userdata *u = s->userdata;
audio_info_t info;
AUDIO_INITINFO(&info);
info.output_muted = !!s->hw_muted;
if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0) {
pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
return -1;
}
return 0;
}
static int source_get_hw_volume_cb(pa_source *s) {
struct userdata *u = s->userdata;
audio_info_t info;
int err;
err = ioctl(u->fd, AUDIO_GETINFO, &info);
assert(err >= 0);
pa_cvolume_set(&s->hw_volume, s->hw_volume.channels,
info.record.gain * PA_VOLUME_NORM / AUDIO_MAX_GAIN);
return 0;
}
static int source_set_hw_volume_cb(pa_source *s) {
struct userdata *u = s->userdata;
audio_info_t info;
AUDIO_INITINFO(&info);
info.record.gain = pa_cvolume_avg(&s->hw_volume) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;
assert(info.record.gain <= AUDIO_MAX_GAIN);
if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0) {
if (errno == EINVAL)
pa_log("AUDIO_SETINFO: Unsupported volume.");
else
pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
return -1;
}
return 0;
if (u->source)
pa_source_get_volume(u->source);
}
static int pa_solaris_auto_format(int fd, int mode, pa_sample_spec *ss) {
@ -490,6 +543,7 @@ static int pa_solaris_set_buffer(int fd, int buffer_size) {
AUDIO_INITINFO(&info);
info.play.buffer_size = buffer_size;
info.record.buffer_size = buffer_size;
if (ioctl(fd, AUDIO_SETINFO, &info) < 0) {
@ -503,7 +557,7 @@ static int pa_solaris_set_buffer(int fd, int buffer_size) {
return 0;
}
int pa__init(pa_core *c, pa_module*m) {
int pa__init(pa_module *m) {
struct userdata *u = NULL;
const char *p;
int fd = -1;
@ -513,9 +567,10 @@ int pa__init(pa_core *c, pa_module*m) {
pa_sample_spec ss;
pa_channel_map map;
pa_modargs *ma = NULL;
struct timeval tv;
char *t;
assert(c && m);
struct pollfd *pollfd;
pa_assert(m);
if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
pa_log("failed to parse module arguments.");
@ -540,7 +595,7 @@ int pa__init(pa_core *c, pa_module*m) {
goto fail;
}
ss = c->default_sample_spec;
ss = m->core->default_sample_spec;
if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0) {
pa_log("failed to parse sample specification");
goto fail;
@ -554,55 +609,18 @@ int pa__init(pa_core *c, pa_module*m) {
if (pa_solaris_auto_format(fd, mode, &ss) < 0)
goto fail;
if ((mode != O_WRONLY) && (buffer_size >= 1))
if (pa_solaris_set_buffer(fd, buffer_size) < 0)
goto fail;
if (pa_solaris_set_buffer(fd, buffer_size) < 0)
goto fail;
u = pa_xmalloc(sizeof(struct userdata));
u->core = c;
u->core = m->core;
if (mode != O_WRONLY) {
u->source = pa_source_new(c, __FILE__, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME), 0, &ss, &map);
assert(u->source);
u->source->userdata = u;
u->source->get_latency = source_get_latency_cb;
u->source->get_hw_volume = source_get_hw_volume_cb;
u->source->set_hw_volume = source_set_hw_volume_cb;
pa_source_set_owner(u->source, m);
pa_source_set_description(u->source, t = pa_sprintf_malloc("Solaris PCM on '%s'", p));
pa_xfree(t);
u->source->is_hardware = 1;
} else
u->source = NULL;
if (mode != O_RDONLY) {
u->sink = pa_sink_new(c, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss, &map);
assert(u->sink);
u->sink->get_latency = sink_get_latency_cb;
u->sink->get_hw_volume = sink_get_hw_volume_cb;
u->sink->set_hw_volume = sink_set_hw_volume_cb;
u->sink->get_hw_mute = sink_get_hw_mute_cb;
u->sink->set_hw_mute = sink_set_hw_mute_cb;
u->sink->userdata = u;
pa_sink_set_owner(u->sink, m);
pa_sink_set_description(u->sink, t = pa_sprintf_malloc("Solaris PCM on '%s'", p));
pa_xfree(t);
u->sink->is_hardware = 1;
} else
u->sink = NULL;
assert(u->source || u->sink);
u->io = pa_iochannel_new(c->mainloop, u->source ? fd : -1, u->sink ? fd : 0);
assert(u->io);
pa_iochannel_set_callback(u->io, io_callback, u);
u->fd = fd;
u->memchunk.memblock = NULL;
u->memchunk.length = 0;
pa_memchunk_reset(&u->memchunk);
/* We use this to get a reasonable chunk size */
u->page_size = sysconf(_SC_PAGESIZE);
u->page_size = PA_PAGE_SIZE;
u->frame_size = pa_frame_size(&ss);
u->buffer_size = buffer_size;
@ -610,37 +628,91 @@ int pa__init(pa_core *c, pa_module*m) {
u->written_bytes = 0;
u->read_bytes = 0;
u->sink_underflow = 1;
u->module = m;
m->userdata = u;
u->poll_timeout = pa_bytes_to_usec(u->buffer_size / 10, &ss);
pa_thread_mq_init(&u->thread_mq, m->core->mainloop);
pa_gettimeofday(&tv);
pa_timeval_add(&tv, u->poll_timeout);
u->rtpoll = pa_rtpoll_new();
pa_rtpoll_item_new_asyncmsgq(u->rtpoll, PA_RTPOLL_EARLY, u->thread_mq.inq);
u->timer = c->mainloop->time_new(c->mainloop, &tv, timer_cb, u);
assert(u->timer);
pa_rtpoll_set_timer_periodic(u->rtpoll, pa_bytes_to_usec(u->buffer_size / 10, &ss));
u->rtpoll_item = pa_rtpoll_item_new(u->rtpoll, PA_RTPOLL_NEVER, 1);
pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
pollfd->fd = fd;
pollfd->events = 0;
pollfd->revents = 0;
if (mode != O_WRONLY) {
u->source = pa_source_new(m->core, __FILE__, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME), 0, &ss, &map);
pa_assert(u->source);
u->source->userdata = u;
u->source->parent.process_msg = source_process_msg;
pa_source_set_module(u->source, m);
pa_source_set_description(u->source, t = pa_sprintf_malloc("Solaris PCM on '%s'", p));
pa_xfree(t);
pa_source_set_asyncmsgq(u->source, u->thread_mq.inq);
pa_source_set_rtpoll(u->source, u->rtpoll);
u->source->flags = PA_SOURCE_HARDWARE|PA_SOURCE_LATENCY|PA_SOURCE_HW_VOLUME_CTRL;
u->source->refresh_volume = 1;
} else
u->source = NULL;
if (mode != O_RDONLY) {
u->sink = pa_sink_new(m->core, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss, &map);
pa_assert(u->sink);
u->sink->userdata = u;
u->sink->parent.process_msg = sink_process_msg;
pa_sink_set_module(u->sink, m);
pa_sink_set_description(u->sink, t = pa_sprintf_malloc("Solaris PCM on '%s'", p));
pa_xfree(t);
pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
pa_sink_set_rtpoll(u->sink, u->rtpoll);
u->sink->flags = PA_SINK_HARDWARE|PA_SINK_LATENCY|PA_SINK_HW_VOLUME_CTRL;
u->sink->refresh_volume = 1;
u->sink->refresh_mute = 1;
} else
u->sink = NULL;
pa_assert(u->source || u->sink);
u->sig = pa_signal_new(SIGPOLL, sig_callback, u);
assert(u->sig);
pa_assert(u->sig);
ioctl(u->fd, I_SETSIG, S_MSG);
pa_modargs_free(ma);
if (!(u->thread = pa_thread_new(thread_func, u))) {
pa_log("Failed to create thread.");
goto fail;
}
/* Read mixer settings */
if (u->source)
source_get_hw_volume_cb(u->source);
pa_asyncmsgq_send(u->thread_mq.inq, PA_MSGOBJECT(u->source), PA_SOURCE_MESSAGE_GET_VOLUME, &u->source->volume, 0, NULL);
if (u->sink) {
sink_get_hw_volume_cb(u->sink);
sink_get_hw_mute_cb(u->sink);
pa_asyncmsgq_send(u->thread_mq.inq, PA_MSGOBJECT(u->sink), PA_SINK_MESSAGE_GET_VOLUME, &u->sink->volume, 0, NULL);
pa_asyncmsgq_send(u->thread_mq.inq, PA_MSGOBJECT(u->sink), PA_SINK_MESSAGE_GET_MUTE, &u->sink->muted, 0, NULL);
}
if (u->sink)
pa_sink_put(u->sink);
if (u->source)
pa_source_put(u->source);
pa_modargs_free(ma);
return 0;
fail:
if (fd >= 0)
if (u)
pa__done(m);
else if (fd >= 0)
close(fd);
if (ma)
@ -649,31 +721,47 @@ fail:
return -1;
}
void pa__done(pa_core *c, pa_module*m) {
void pa__done(pa_module *m) {
struct userdata *u;
assert(c && m);
pa_assert(m);
if (!(u = m->userdata))
return;
if (u->timer)
c->mainloop->time_free(u->timer);
ioctl(u->fd, I_SETSIG, 0);
pa_signal_free(u->sig);
if (u->memchunk.memblock)
if (u->sink)
pa_sink_unlink(u->sink);
if (u->source)
pa_source_unlink(u->source);
if (u->thread) {
pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL);
pa_thread_free(u->thread);
}
pa_thread_mq_done(&u->thread_mq);
if (u->sink)
pa_sink_unref(u->sink);
if (u->source)
pa_source_unref(u->source);
if (u->memchunk.memblock)
pa_memblock_unref(u->memchunk.memblock);
if (u->sink) {
pa_sink_disconnect(u->sink);
pa_sink_unref(u->sink);
}
if (u->rtpoll_item)
pa_rtpoll_item_free(u->rtpoll_item);
if (u->source) {
pa_source_disconnect(u->source);
pa_source_unref(u->source);
}
if (u->rtpoll)
pa_rtpoll_free(u->rtpoll);
if (u->fd >= 0)
close(u->fd);
pa_iochannel_free(u->io);
pa_xfree(u);
}

View file

@ -0,0 +1,473 @@
/* $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 <pulse/xmalloc.h>
#include <pulse/timeval.h>
#include <pulsecore/core.h>
#include <pulsecore/sink-input.h>
#include <pulsecore/source-output.h>
#include <pulsecore/modargs.h>
#include <pulsecore/log.h>
#include <pulsecore/namereg.h>
#include "module-suspend-on-idle-symdef.h"
PA_MODULE_AUTHOR("Lennart Poettering")
PA_MODULE_DESCRIPTION("When a sink/source is idle for too long, suspend it")
PA_MODULE_VERSION(PACKAGE_VERSION)
static const char* const valid_modargs[] = {
"timeout",
NULL,
};
struct userdata {
pa_core *core;
pa_usec_t timeout;
pa_hashmap *device_infos;
pa_hook_slot
*sink_new_slot,
*source_new_slot,
*sink_unlink_slot,
*source_unlink_slot,
*sink_state_changed_slot,
*source_state_changed_slot;
pa_hook_slot
*sink_input_new_slot,
*source_output_new_slot,
*sink_input_unlink_slot,
*source_output_unlink_slot,
*sink_input_move_slot,
*source_output_move_slot,
*sink_input_move_post_slot,
*source_output_move_post_slot,
*sink_input_state_changed_slot,
*source_output_state_changed_slot;
};
struct device_info {
struct userdata *userdata;
pa_sink *sink;
pa_source *source;
struct timeval last_use;
pa_time_event *time_event;
};
static void timeout_cb(pa_mainloop_api*a, pa_time_event* e, const struct timeval *tv, void *userdata) {
struct device_info *d = userdata;
pa_assert(d);
d->userdata->core->mainloop->time_restart(d->time_event, NULL);
if (d->sink && pa_sink_used_by(d->sink) <= 0 && pa_sink_get_state(d->sink) != PA_SINK_SUSPENDED) {
pa_log_info("Sink %s idle for too long, suspending ...", d->sink->name);
pa_sink_suspend(d->sink, TRUE);
}
if (d->source && pa_source_used_by(d->source) <= 0 && pa_source_get_state(d->source) != PA_SOURCE_SUSPENDED) {
pa_log_info("Source %s idle for too long, suspending ...", d->source->name);
pa_source_suspend(d->source, TRUE);
}
}
static void restart(struct device_info *d) {
struct timeval tv;
pa_assert(d);
pa_gettimeofday(&tv);
d->last_use = tv;
pa_timeval_add(&tv, d->userdata->timeout*1000000);
d->userdata->core->mainloop->time_restart(d->time_event, &tv);
if (d->sink)
pa_log_debug("Sink %s becomes idle.", d->sink->name);
if (d->source)
pa_log_debug("Source %s becomes idle.", d->source->name);
}
static void resume(struct device_info *d) {
pa_assert(d);
d->userdata->core->mainloop->time_restart(d->time_event, NULL);
if (d->sink) {
pa_sink_suspend(d->sink, FALSE);
pa_log_debug("Sink %s becomes busy.", d->sink->name);
}
if (d->source) {
pa_source_suspend(d->source, FALSE);
pa_log_debug("Source %s becomes busy.", d->source->name);
}
}
static pa_hook_result_t sink_input_new_hook_cb(pa_core *c, pa_sink_input *s, struct userdata *u) {
struct device_info *d;
pa_assert(c);
pa_sink_input_assert_ref(s);
pa_assert(u);
if ((d = pa_hashmap_get(u->device_infos, s->sink)))
resume(d);
return PA_HOOK_OK;
}
static pa_hook_result_t source_output_new_hook_cb(pa_core *c, pa_source_output *s, struct userdata *u) {
struct device_info *d;
pa_assert(c);
pa_source_output_assert_ref(s);
pa_assert(u);
if ((d = pa_hashmap_get(u->device_infos, s->source)))
resume(d);
return PA_HOOK_OK;
}
static pa_hook_result_t sink_input_unlink_hook_cb(pa_core *c, pa_sink_input *s, struct userdata *u) {
pa_assert(c);
pa_sink_input_assert_ref(s);
pa_assert(u);
if (pa_sink_used_by(s->sink) <= 0) {
struct device_info *d;
if ((d = pa_hashmap_get(u->device_infos, s->sink)))
restart(d);
}
return PA_HOOK_OK;
}
static pa_hook_result_t source_output_unlink_hook_cb(pa_core *c, pa_source_output *s, struct userdata *u) {
pa_assert(c);
pa_source_output_assert_ref(s);
pa_assert(u);
if (pa_source_used_by(s->source) <= 0) {
struct device_info *d;
if ((d = pa_hashmap_get(u->device_infos, s->source)))
restart(d);
}
return PA_HOOK_OK;
}
static pa_hook_result_t sink_input_move_hook_cb(pa_core *c, pa_sink_input *s, struct userdata *u) {
pa_assert(c);
pa_sink_input_assert_ref(s);
pa_assert(u);
if (pa_sink_used_by(s->sink) <= 1) {
struct device_info *d;
if ((d = pa_hashmap_get(u->device_infos, s->sink)))
restart(d);
}
return PA_HOOK_OK;
}
static pa_hook_result_t sink_input_move_post_hook_cb(pa_core *c, pa_sink_input *s, struct userdata *u) {
struct device_info *d;
pa_assert(c);
pa_sink_input_assert_ref(s);
pa_assert(u);
if ((d = pa_hashmap_get(u->device_infos, s->sink)))
resume(d);
return PA_HOOK_OK;
}
static pa_hook_result_t source_output_move_hook_cb(pa_core *c, pa_source_output *s, struct userdata *u) {
pa_assert(c);
pa_source_output_assert_ref(s);
pa_assert(u);
if (pa_source_used_by(s->source) <= 1) {
struct device_info *d;
if ((d = pa_hashmap_get(u->device_infos, s->source)))
restart(d);
}
return PA_HOOK_OK;
}
static pa_hook_result_t source_output_move_post_hook_cb(pa_core *c, pa_source_output *s, struct userdata *u) {
struct device_info *d;
pa_assert(c);
pa_source_output_assert_ref(s);
pa_assert(u);
if ((d = pa_hashmap_get(u->device_infos, s->source)))
resume(d);
return PA_HOOK_OK;
}
static pa_hook_result_t sink_input_state_changed_hook_cb(pa_core *c, pa_sink_input *s, struct userdata *u) {
struct device_info *d;
pa_sink_input_state_t state;
pa_assert(c);
pa_sink_input_assert_ref(s);
pa_assert(u);
state = pa_sink_input_get_state(s);
if (state == PA_SINK_INPUT_RUNNING || state == PA_SINK_INPUT_DRAINED)
if ((d = pa_hashmap_get(u->device_infos, s->sink)))
resume(d);
return PA_HOOK_OK;
}
static pa_hook_result_t source_output_state_changed_hook_cb(pa_core *c, pa_source_output *s, struct userdata *u) {
struct device_info *d;
pa_source_output_state_t state;
pa_assert(c);
pa_source_output_assert_ref(s);
pa_assert(u);
state = pa_source_output_get_state(s);
if (state == PA_SOURCE_OUTPUT_RUNNING)
if ((d = pa_hashmap_get(u->device_infos, s->source)))
resume(d);
return PA_HOOK_OK;
}
static pa_hook_result_t device_new_hook_cb(pa_core *c, pa_object *o, struct userdata *u) {
struct device_info *d;
pa_source *source;
pa_sink *sink;
pa_assert(c);
pa_object_assert_ref(o);
pa_assert(u);
source = pa_source_isinstance(o) ? PA_SOURCE(o) : NULL;
sink = pa_sink_isinstance(o) ? PA_SINK(o) : NULL;
pa_assert(source || sink);
d = pa_xnew(struct device_info, 1);
d->userdata = u;
d->source = source ? pa_source_ref(source) : NULL;
d->sink = sink ? pa_sink_ref(sink) : NULL;
d->time_event = c->mainloop->time_new(c->mainloop, NULL, timeout_cb, d);
pa_hashmap_put(u->device_infos, o, d);
if ((d->sink && pa_sink_used_by(d->sink) <= 0) ||
(d->source && pa_source_used_by(d->source) <= 0))
restart(d);
return PA_HOOK_OK;
}
static void device_info_free(struct device_info *d) {
pa_assert(d);
if (d->source)
pa_source_unref(d->source);
if (d->sink)
pa_sink_unref(d->sink);
d->userdata->core->mainloop->time_free(d->time_event);
pa_xfree(d);
}
static pa_hook_result_t device_unlink_hook_cb(pa_core *c, pa_object *o, struct userdata *u) {
struct device_info *d;
pa_assert(c);
pa_object_assert_ref(o);
pa_assert(u);
if ((d = pa_hashmap_remove(u->device_infos, o)))
device_info_free(d);
return PA_HOOK_OK;
}
static pa_hook_result_t device_state_changed_hook_cb(pa_core *c, pa_object *o, struct userdata *u) {
struct device_info *d;
pa_assert(c);
pa_object_assert_ref(o);
pa_assert(u);
if (!(d = pa_hashmap_get(u->device_infos, o)))
return PA_HOOK_OK;
if (pa_sink_isinstance(o)) {
pa_sink *s = PA_SINK(o);
pa_sink_state_t state = pa_sink_get_state(s);
if (pa_sink_used_by(s) <= 0) {
if (PA_SINK_OPENED(state))
restart(d);
}
} else if (pa_source_isinstance(o)) {
pa_source *s = PA_SOURCE(o);
pa_source_state_t state = pa_source_get_state(s);
if (pa_source_used_by(s) <= 0) {
if (PA_SOURCE_OPENED(state))
restart(d);
}
}
return PA_HOOK_OK;
}
int pa__init(pa_module*m) {
pa_modargs *ma = NULL;
struct userdata *u;
uint32_t timeout = 1;
uint32_t idx;
pa_sink *sink;
pa_source *source;
pa_assert(m);
if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
pa_log("Failed to parse module arguments.");
goto fail;
}
if (pa_modargs_get_value_u32(ma, "timeout", &timeout) < 0) {
pa_log("Failed to parse timeout value.");
goto fail;
}
m->userdata = u = pa_xnew(struct userdata, 1);
u->core = m->core;
u->timeout = timeout;
u->device_infos = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
for (sink = pa_idxset_first(m->core->sinks, &idx); sink; sink = pa_idxset_next(m->core->sinks, &idx))
device_new_hook_cb(m->core, PA_OBJECT(sink), u);
for (source = pa_idxset_first(m->core->sources, &idx); source; source = pa_idxset_next(m->core->sources, &idx))
device_new_hook_cb(m->core, PA_OBJECT(source), u);
u->sink_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_NEW_POST], (pa_hook_cb_t) device_new_hook_cb, u);
u->source_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_NEW_POST], (pa_hook_cb_t) device_new_hook_cb, u);
u->sink_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_UNLINK_POST], (pa_hook_cb_t) device_unlink_hook_cb, u);
u->source_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK_POST], (pa_hook_cb_t) device_unlink_hook_cb, u);
u->sink_state_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_STATE_CHANGED], (pa_hook_cb_t) device_state_changed_hook_cb, u);
u->source_state_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_STATE_CHANGED], (pa_hook_cb_t) device_state_changed_hook_cb, u);
u->sink_input_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_PUT], (pa_hook_cb_t) sink_input_new_hook_cb, u);
u->source_output_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PUT], (pa_hook_cb_t) source_output_new_hook_cb, u);
u->sink_input_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_UNLINK_POST], (pa_hook_cb_t) sink_input_unlink_hook_cb, u);
u->source_output_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK_POST], (pa_hook_cb_t) source_output_unlink_hook_cb, u);
u->sink_input_move_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE], (pa_hook_cb_t) sink_input_move_hook_cb, u);
u->source_output_move_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE], (pa_hook_cb_t) source_output_move_hook_cb, u);
u->sink_input_move_post_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_POST], (pa_hook_cb_t) sink_input_move_post_hook_cb, u);
u->source_output_move_post_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_POST], (pa_hook_cb_t) source_output_move_post_hook_cb, u);
u->sink_input_state_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED], (pa_hook_cb_t) sink_input_state_changed_hook_cb, u);
u->source_output_state_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_STATE_CHANGED], (pa_hook_cb_t) source_output_state_changed_hook_cb, u);
pa_modargs_free(ma);
return 0;
fail:
if (ma)
pa_modargs_free(ma);
return -1;
}
void pa__done(pa_module*m) {
struct userdata *u;
struct device_info *d;
pa_assert(m);
if (!m->userdata)
return;
u = m->userdata;
if (u->sink_new_slot)
pa_hook_slot_free(u->sink_new_slot);
if (u->sink_unlink_slot)
pa_hook_slot_free(u->sink_unlink_slot);
if (u->sink_state_changed_slot)
pa_hook_slot_free(u->sink_state_changed_slot);
if (u->source_new_slot)
pa_hook_slot_free(u->source_new_slot);
if (u->source_unlink_slot)
pa_hook_slot_free(u->source_unlink_slot);
if (u->source_state_changed_slot)
pa_hook_slot_free(u->source_state_changed_slot);
if (u->sink_input_new_slot)
pa_hook_slot_free(u->sink_input_new_slot);
if (u->sink_input_unlink_slot)
pa_hook_slot_free(u->sink_input_unlink_slot);
if (u->sink_input_move_slot)
pa_hook_slot_free(u->sink_input_move_slot);
if (u->sink_input_move_post_slot)
pa_hook_slot_free(u->sink_input_move_post_slot);
if (u->sink_input_state_changed_slot)
pa_hook_slot_free(u->sink_input_state_changed_slot);
if (u->source_output_new_slot)
pa_hook_slot_free(u->source_output_new_slot);
if (u->source_output_unlink_slot)
pa_hook_slot_free(u->source_output_unlink_slot);
if (u->source_output_move_slot)
pa_hook_slot_free(u->source_output_move_slot);
if (u->source_output_move_post_slot)
pa_hook_slot_free(u->source_output_move_post_slot);
if (u->source_output_state_changed_slot)
pa_hook_slot_free(u->source_output_state_changed_slot);
while ((d = pa_hashmap_steal_first(u->device_infos)))
device_info_free(d);
pa_hashmap_free(u->device_infos, NULL, NULL);
pa_xfree(u);
}

View file

@ -596,12 +596,12 @@ static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t
}
#ifdef TUNNEL_SINK
snprintf(name, sizeof(name), "Tunnel from host %s, user %s, sink %s",
pa_snprintf(name, sizeof(name), "Tunnel from host %s, user %s, sink %s",
pa_get_host_name(hn, sizeof(hn)),
pa_get_user_name(un, sizeof(un)),
u->sink->name);
#else
snprintf(name, sizeof(name), "Tunnel from host %s, user %s, source %s",
pa_snprintf(name, sizeof(name), "Tunnel from host %s, user %s, source %s",
pa_get_host_name(hn, sizeof(hn)),
pa_get_user_name(un, sizeof(un)),
u->source->name);

View file

@ -26,7 +26,6 @@
#endif
#include <unistd.h>
#include <assert.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
@ -35,6 +34,7 @@
#include <ctype.h>
#include <pulse/xmalloc.h>
#include <pulse/volume.h>
#include <pulsecore/core-error.h>
#include <pulsecore/module.h>
@ -44,9 +44,7 @@
#include <pulsecore/core-subscribe.h>
#include <pulsecore/sink-input.h>
#include <pulsecore/source-output.h>
#include <pulsecore/core-util.h>
#include <pulsecore/namereg.h>
#include <pulse/volume.h>
#include "module-volume-restore-symdef.h"
@ -85,8 +83,8 @@ static pa_cvolume* parse_volume(const char *s, pa_cvolume *v) {
long k;
unsigned i;
assert(s);
assert(v);
pa_assert(s);
pa_assert(v);
if (!isdigit(*s))
return NULL;
@ -170,7 +168,7 @@ static int load_rules(struct userdata *u) {
continue;
}
assert(ln == buf_source);
pa_assert(ln == buf_source);
if (buf_volume[0]) {
if (!parse_volume(buf_volume, &v)) {
@ -297,8 +295,8 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
struct rule *r;
char *name;
assert(c);
assert(u);
pa_assert(c);
pa_assert(u);
if (t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW) &&
t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE) &&
@ -313,7 +311,7 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
if (!si->client || !(name = client_name(si->client)))
return;
} else {
assert((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT);
pa_assert((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT);
if (!(so = pa_idxset_get_by_index(c->source_outputs, idx)))
return;
@ -341,7 +339,7 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
u->modified = 1;
}
} else {
assert(so);
pa_assert(so);
if (!r->source || strcmp(so->source->name, r->source) != 0) {
pa_log_info("Saving source for <%s>", r->name);
@ -363,7 +361,7 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
r->sink = pa_xstrdup(si->sink->name);
r->source = NULL;
} else {
assert(so);
pa_assert(so);
r->volume_is_set = 0;
r->sink = NULL;
r->source = pa_xstrdup(so->source->name);
@ -378,7 +376,7 @@ static pa_hook_result_t sink_input_hook_callback(pa_core *c, pa_sink_input_new_d
struct rule *r;
char *name;
assert(data);
pa_assert(data);
if (!data->client || !(name = client_name(data->client)))
return PA_HOOK_OK;
@ -396,6 +394,8 @@ static pa_hook_result_t sink_input_hook_callback(pa_core *c, pa_sink_input_new_d
}
}
pa_xfree(name);
return PA_HOOK_OK;
}
@ -403,7 +403,7 @@ static pa_hook_result_t source_output_hook_callback(pa_core *c, pa_source_output
struct rule *r;
char *name;
assert(data);
pa_assert(data);
if (!data->client || !(name = client_name(data->client)))
return PA_HOOK_OK;
@ -418,12 +418,11 @@ static pa_hook_result_t source_output_hook_callback(pa_core *c, pa_source_output
return PA_HOOK_OK;
}
int pa__init(pa_core *c, pa_module*m) {
int pa__init(pa_module*m) {
pa_modargs *ma = NULL;
struct userdata *u;
assert(c);
assert(m);
pa_assert(m);
if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
pa_log("Failed to parse module arguments");
@ -442,16 +441,15 @@ int pa__init(pa_core *c, pa_module*m) {
if (load_rules(u) < 0)
goto fail;
u->subscription = pa_subscription_new(c, PA_SUBSCRIPTION_MASK_SINK_INPUT|PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT, subscribe_callback, u);
u->sink_input_hook_slot = pa_hook_connect(&c->hook_sink_input_new, (pa_hook_cb_t) sink_input_hook_callback, u);
u->source_output_hook_slot = pa_hook_connect(&c->hook_source_output_new, (pa_hook_cb_t) source_output_hook_callback, u);
u->subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_SINK_INPUT|PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT, subscribe_callback, u);
u->sink_input_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_NEW], (pa_hook_cb_t) sink_input_hook_callback, u);
u->source_output_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_NEW], (pa_hook_cb_t) source_output_hook_callback, u);
pa_modargs_free(ma);
return 0;
fail:
pa__done(c, m);
pa__done(m);
if (ma)
pa_modargs_free(ma);
@ -460,7 +458,7 @@ fail:
static void free_func(void *p, void *userdata) {
struct rule *r = p;
assert(r);
pa_assert(r);
pa_xfree(r->name);
pa_xfree(r->sink);
@ -468,11 +466,10 @@ static void free_func(void *p, void *userdata) {
pa_xfree(r);
}
void pa__done(pa_core *c, pa_module*m) {
void pa__done(pa_module*m) {
struct userdata* u;
assert(c);
assert(m);
pa_assert(m);
if (!(u = m->userdata))
return;

View file

@ -26,7 +26,6 @@
#endif
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
@ -67,30 +66,21 @@ static const char* const valid_modargs[] = {
NULL
};
static int ring_bell(struct userdata *u, int percent) {
pa_sink *s;
assert(u);
if (!(s = pa_namereg_get(u->core, u->sink_name, PA_NAMEREG_SINK, 1))) {
pa_log("Invalid sink: %s", u->sink_name);
return -1;
}
pa_scache_play_item(u->core, u->scache_item, s, (percent*PA_VOLUME_NORM)/100);
return 0;
}
static int x11_event_callback(pa_x11_wrapper *w, XEvent *e, void *userdata) {
XkbBellNotifyEvent *bne;
struct userdata *u = userdata;
assert(w && e && u && u->x11_wrapper == w);
pa_assert(w);
pa_assert(e);
pa_assert(u);
pa_assert(u->x11_wrapper == w);
if (((XkbEvent*) e)->any.xkb_type != XkbBellNotify)
return 0;
bne = (XkbBellNotifyEvent*) e;
if (ring_bell(u, bne->percent) < 0) {
if (pa_scache_play_item_by_name(u->core, u->scache_item, u->sink_name, (bne->percent*PA_VOLUME_NORM)/100, 1) < 0) {
pa_log_info("Ringing bell failed, reverting to X11 device bell.");
XkbForceDeviceBell(pa_x11_wrapper_get_display(w), bne->device, bne->bell_class, bne->bell_id, bne->percent);
}
@ -98,25 +88,27 @@ static int x11_event_callback(pa_x11_wrapper *w, XEvent *e, void *userdata) {
return 1;
}
int pa__init(pa_core *c, pa_module*m) {
int pa__init(pa_module*m) {
struct userdata *u = NULL;
pa_modargs *ma = NULL;
int major, minor;
unsigned int auto_ctrls, auto_values;
assert(c && m);
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;
}
m->userdata = u = pa_xmalloc(sizeof(struct userdata));
u->core = c;
m->userdata = u = pa_xnew(struct userdata, 1);
u->core = m->core;
u->scache_item = pa_xstrdup(pa_modargs_get_value(ma, "sample", "x11-bell"));
u->sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL));
u->x11_client = NULL;
if (!(u->x11_wrapper = pa_x11_wrapper_get(c, pa_modargs_get_value(ma, "display", NULL))))
if (!(u->x11_wrapper = pa_x11_wrapper_get(m->core, pa_modargs_get_value(ma, "display", NULL))))
goto fail;
major = XkbMajorVersion;
@ -130,7 +122,6 @@ int pa__init(pa_core *c, pa_module*m) {
major = XkbMajorVersion;
minor = XkbMinorVersion;
if (!XkbQueryExtension(pa_x11_wrapper_get_display(u->x11_wrapper), NULL, &u->xkb_event_base, NULL, &major, &minor)) {
pa_log("XkbQueryExtension() failed");
goto fail;
@ -150,14 +141,21 @@ int pa__init(pa_core *c, pa_module*m) {
fail:
if (ma)
pa_modargs_free(ma);
if (m->userdata)
pa__done(c, m);
pa__done(m);
return -1;
}
void pa__done(pa_core *c, pa_module*m) {
struct userdata *u = m->userdata;
assert(c && m && u);
void pa__done(pa_module*m) {
struct userdata *u;
pa_assert(m);
if (!m->userdata)
return;
u = m->userdata;
pa_xfree(u->scache_item);
pa_xfree(u->sink_name);

View file

@ -26,7 +26,6 @@
#endif
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
@ -76,7 +75,7 @@ struct userdata {
};
static int load_key(struct userdata *u, const char*fn) {
assert(u);
pa_assert(u);
u->auth_cookie_in_property = 0;
@ -93,7 +92,7 @@ static int load_key(struct userdata *u, const char*fn) {
if (pa_authkey_load_auto(fn, u->auth_cookie, sizeof(u->auth_cookie)) < 0)
return -1;
pa_log_debug("loading cookie from disk.");
pa_log_debug("Loading cookie from disk.");
if (pa_authkey_prop_put(u->core, PA_NATIVE_COOKIE_PROPERTY_NAME, u->auth_cookie, sizeof(u->auth_cookie)) >= 0)
u->auth_cookie_in_property = 1;
@ -101,7 +100,7 @@ static int load_key(struct userdata *u, const char*fn) {
return 0;
}
int pa__init(pa_core *c, pa_module*m) {
int pa__init(pa_module*m) {
struct userdata *u;
pa_modargs *ma = NULL;
char hn[256], un[128];
@ -110,23 +109,25 @@ int pa__init(pa_core *c, pa_module*m) {
char *s;
pa_strlist *l;
pa_assert(m);
if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
pa_log("failed to parse module arguments");
goto fail;
}
m->userdata = u = pa_xmalloc(sizeof(struct userdata));
u->core = c;
u->core = m->core;
u->id = NULL;
u->auth_cookie_in_property = 0;
if (load_key(u, pa_modargs_get_value(ma, "cookie", NULL)) < 0)
goto fail;
if (!(u->x11_wrapper = pa_x11_wrapper_get(c, pa_modargs_get_value(ma, "display", NULL))))
if (!(u->x11_wrapper = pa_x11_wrapper_get(m->core, pa_modargs_get_value(ma, "display", NULL))))
goto fail;
if (!(l = pa_property_get(c, PA_NATIVE_SERVER_PROPERTY_NAME)))
if (!(l = pa_property_get(m->core, PA_NATIVE_SERVER_PROPERTY_NAME)))
goto fail;
s = pa_strlist_tostring(l);
@ -154,13 +155,14 @@ fail:
if (ma)
pa_modargs_free(ma);
pa__done(c, m);
pa__done(m);
return -1;
}
void pa__done(pa_core *c, pa_module*m) {
void pa__done(pa_module*m) {
struct userdata*u;
assert(c && m);
pa_assert(m);
if (!(u = m->userdata))
return;
@ -185,7 +187,7 @@ void pa__done(pa_core *c, pa_module*m) {
pa_x11_wrapper_unref(u->x11_wrapper);
if (u->auth_cookie_in_property)
pa_authkey_prop_unref(c, PA_NATIVE_COOKIE_PROPERTY_NAME);
pa_authkey_prop_unref(m->core, PA_NATIVE_COOKIE_PROPERTY_NAME);
pa_xfree(u->id);
pa_xfree(u);

View file

@ -0,0 +1,195 @@
/* $Id$ */
/***
This file is part of PulseAudio.
Copyright 2004-2006 Lennart Poettering
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <X11/Xlib.h>
#include <X11/SM/SMlib.h>
#include <pulse/xmalloc.h>
#include <pulse/util.h>
#include <pulsecore/iochannel.h>
#include <pulsecore/sink.h>
#include <pulsecore/core-scache.h>
#include <pulsecore/modargs.h>
#include <pulsecore/namereg.h>
#include <pulsecore/log.h>
#include <pulsecore/core-util.h>
#include "module-x11-xsmp-symdef.h"
PA_MODULE_AUTHOR("Lennart Poettering")
PA_MODULE_DESCRIPTION("X11 session management")
PA_MODULE_VERSION(PACKAGE_VERSION)
static int ice_in_use = 0;
static const char* const valid_modargs[] = {
NULL
};
static void die_cb(SmcConn connection, SmPointer client_data){
pa_core *c = PA_CORE(client_data);
pa_log_debug("Got die message from XSM. Exiting...");
pa_core_assert_ref(c);
c->mainloop->quit(c->mainloop, 0);
}
static void save_complete_cb(SmcConn connection, SmPointer client_data) {
}
static void shutdown_cancelled_cb(SmcConn connection, SmPointer client_data) {
SmcSaveYourselfDone(connection, True);
}
static void save_yourself_cb(SmcConn connection, SmPointer client_data, int save_type, Bool _shutdown, int interact_style, Bool fast) {
SmcSaveYourselfDone(connection, True);
}
static void ice_io_cb(pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_event_flags_t flags, void *userdata) {
IceConn connection = userdata;
if (IceProcessMessages(connection, NULL, NULL) == IceProcessMessagesIOError) {
IceSetShutdownNegotiation(connection, False);
IceCloseConnection(connection);
}
}
static void new_ice_connection(IceConn connection, IcePointer client_data, Bool opening, IcePointer *watch_data) {
pa_core *c = client_data;
pa_assert(c);
if (opening)
*watch_data = c->mainloop->io_new(c->mainloop, IceConnectionNumber(connection), PA_IO_EVENT_INPUT, ice_io_cb, connection);
else
c->mainloop->io_free(*watch_data);
}
int pa__init(pa_module*m) {
pa_modargs *ma = NULL;
char t[256], *vendor, *client_id;
SmcCallbacks callbacks;
SmProp prop_program, prop_user;
SmProp *prop_list[2];
SmPropValue val_program, val_user;
SmcConn connection;
pa_assert(m);
if (ice_in_use) {
pa_log("module-x11-xsmp may no be loaded twice.");
return -1;
}
IceAddConnectionWatch(new_ice_connection, m->core);
ice_in_use = 1;
if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
pa_log("Failed to parse module arguments");
goto fail;
}
if (!getenv("SESSION_MANAGER")) {
pa_log("X11 session manager not running.");
goto fail;
}
memset(&callbacks, 0, sizeof(callbacks));
callbacks.die.callback = die_cb;
callbacks.die.client_data = m->core;
callbacks.save_yourself.callback = save_yourself_cb;
callbacks.save_yourself.client_data = m->core;
callbacks.save_complete.callback = save_complete_cb;
callbacks.save_complete.client_data = m->core;
callbacks.shutdown_cancelled.callback = shutdown_cancelled_cb;
callbacks.shutdown_cancelled.client_data = m->core;
if (!(m->userdata = connection = SmcOpenConnection(
NULL, m->core,
SmProtoMajor, SmProtoMinor,
SmcSaveYourselfProcMask | SmcDieProcMask | SmcSaveCompleteProcMask | SmcShutdownCancelledProcMask,
&callbacks, NULL, &client_id,
sizeof(t), t))) {
pa_log("Failed to open connection to session manager: %s", t);
goto fail;
}
prop_program.name = (char*) SmProgram;
prop_program.type = (char*) SmARRAY8;
val_program.value = (char*) PACKAGE_NAME;
val_program.length = strlen(val_program.value);
prop_program.num_vals = 1;
prop_program.vals = &val_program;
prop_list[0] = &prop_program;
prop_user.name = (char*) SmUserID;
prop_user.type = (char*) SmARRAY8;
pa_get_user_name(t, sizeof(t));
val_user.value = t;
val_user.length = strlen(val_user.value);
prop_user.num_vals = 1;
prop_user.vals = &val_user;
prop_list[1] = &prop_user;
SmcSetProperties(connection, PA_ELEMENTSOF(prop_list), prop_list);
pa_log_info("Connected to session manager '%s' as '%s'.", vendor = SmcVendor(connection), client_id);
free(vendor);
free(client_id);
pa_modargs_free(ma);
return 0;
fail:
if (ma)
pa_modargs_free(ma);
pa__done(m);
return -1;
}
void pa__done(pa_module*m) {
pa_assert(m);
if (m->userdata)
SmcCloseConnection(m->userdata, 0, NULL);
if (ice_in_use) {
IceRemoveConnectionWatch(new_ice_connection, m->core);
ice_in_use = 0;
}
}

View file

@ -26,7 +26,6 @@
#endif
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
@ -35,11 +34,11 @@
#include <avahi-client/publish.h>
#include <avahi-common/alternative.h>
#include <avahi-common/error.h>
#include <avahi-common/domain.h>
#include <pulse/xmalloc.h>
#include <pulse/util.h>
#include <pulsecore/autoload.h>
#include <pulsecore/sink.h>
#include <pulsecore/source.h>
#include <pulsecore/native-common.h>
@ -71,56 +70,52 @@ struct service {
struct userdata *userdata;
AvahiEntryGroup *entry_group;
char *service_name;
char *name;
enum { UNPUBLISHED, PUBLISHED_REAL, PUBLISHED_AUTOLOAD } published ;
struct {
int valid;
pa_namereg_type_t type;
uint32_t index;
} loaded;
struct {
int valid;
pa_namereg_type_t type;
uint32_t index;
} autoload;
pa_object *device;
};
struct userdata {
pa_core *core;
AvahiPoll *avahi_poll;
AvahiClient *client;
pa_hashmap *services;
pa_dynarray *sink_dynarray, *source_dynarray, *autoload_dynarray;
pa_subscription *subscription;
char *service_name;
AvahiEntryGroup *main_entry_group;
uint16_t port;
pa_hook_slot *sink_new_slot, *source_new_slot, *sink_unlink_slot, *source_unlink_slot, *sink_changed_slot, *source_changed_slot;
};
static void get_service_data(struct userdata *u, struct service *s, pa_sample_spec *ret_ss, char **ret_description) {
assert(u && s && s->loaded.valid && ret_ss && ret_description);
static void get_service_data(struct service *s, pa_sample_spec *ret_ss, pa_channel_map *ret_map, const char **ret_name, const char **ret_description) {
pa_assert(s);
pa_assert(ret_ss);
pa_assert(ret_description);
if (pa_sink_isinstance(s->device)) {
pa_sink *sink = PA_SINK(s->device);
if (s->loaded.type == PA_NAMEREG_SINK) {
pa_sink *sink = pa_idxset_get_by_index(u->core->sinks, s->loaded.index);
assert(sink);
*ret_ss = sink->sample_spec;
*ret_map = sink->channel_map;
*ret_name = sink->name;
*ret_description = sink->description;
} else if (s->loaded.type == PA_NAMEREG_SOURCE) {
pa_source *source = pa_idxset_get_by_index(u->core->sources, s->loaded.index);
assert(source);
} else if (pa_source_isinstance(s->device)) {
pa_source *source = PA_SOURCE(s->device);
*ret_ss = source->sample_spec;
*ret_map = source->channel_map;
*ret_name = source->name;
*ret_description = source->description;
} else
assert(0);
pa_assert_not_reached();
}
static AvahiStringList* txt_record_server_data(pa_core *c, AvahiStringList *l) {
char s[128];
assert(c);
pa_assert(c);
l = avahi_string_list_add_pair(l, "server-version", PACKAGE_NAME" "PACKAGE_VERSION);
l = avahi_string_list_add_pair(l, "user-name", pa_get_user_name(s, sizeof(s)));
@ -130,325 +125,217 @@ static AvahiStringList* txt_record_server_data(pa_core *c, AvahiStringList *l) {
return l;
}
static int publish_service(struct userdata *u, struct service *s);
static int publish_service(struct service *s);
static void service_entry_group_callback(AvahiEntryGroup *g, AvahiEntryGroupState state, void *userdata) {
struct service *s = userdata;
if (state == AVAHI_ENTRY_GROUP_COLLISION) {
char *t;
pa_assert(s);
t = avahi_alternative_service_name(s->service_name);
pa_xfree(s->service_name);
s->service_name = t;
switch (state) {
publish_service(s->userdata, s);
case AVAHI_ENTRY_GROUP_ESTABLISHED:
pa_log_info("Successfully established service %s.", s->service_name);
break;
case AVAHI_ENTRY_GROUP_COLLISION: {
char *t;
t = avahi_alternative_service_name(s->service_name);
pa_log_info("Name collision, renaming %s to %s.", s->service_name, t);
pa_xfree(s->service_name);
s->service_name = t;
publish_service(s);
break;
}
case AVAHI_ENTRY_GROUP_FAILURE: {
pa_log("Failed to register service: %s", avahi_strerror(avahi_client_errno(avahi_entry_group_get_client(g))));
avahi_entry_group_free(g);
s->entry_group = NULL;
break;
}
case AVAHI_ENTRY_GROUP_UNCOMMITED:
case AVAHI_ENTRY_GROUP_REGISTERING:
;
}
}
static int publish_service(struct userdata *u, struct service *s) {
static void service_free(struct service *s);
static int publish_service(struct service *s) {
int r = -1;
AvahiStringList *txt = NULL;
const char *description = NULL, *name = NULL;
pa_sample_spec ss;
pa_channel_map map;
char cm[PA_CHANNEL_MAP_SNPRINT_MAX];
assert(u);
assert(s);
pa_assert(s);
if (!u->client || avahi_client_get_state(u->client) != AVAHI_CLIENT_S_RUNNING)
if (!s->userdata->client || avahi_client_get_state(s->userdata->client) != AVAHI_CLIENT_S_RUNNING)
return 0;
if ((s->published == PUBLISHED_REAL && s->loaded.valid) ||
(s->published == PUBLISHED_AUTOLOAD && s->autoload.valid && !s->loaded.valid))
return 0;
if (s->published != UNPUBLISHED) {
if (!s->entry_group) {
if (!(s->entry_group = avahi_entry_group_new(s->userdata->client, service_entry_group_callback, s))) {
pa_log("avahi_entry_group_new(): %s", avahi_strerror(avahi_client_errno(s->userdata->client)));
goto finish;
}
} else
avahi_entry_group_reset(s->entry_group);
s->published = UNPUBLISHED;
txt = txt_record_server_data(s->userdata->core, txt);
get_service_data(s, &ss, &map, &name, &description);
txt = avahi_string_list_add_pair(txt, "device", name);
txt = avahi_string_list_add_printf(txt, "rate=%u", ss.rate);
txt = avahi_string_list_add_printf(txt, "channels=%u", ss.channels);
txt = avahi_string_list_add_pair(txt, "format", pa_sample_format_to_string(ss.format));
txt = avahi_string_list_add_pair(txt, "channel_map", pa_channel_map_snprint(cm, sizeof(cm), &map));
if (avahi_entry_group_add_service_strlst(
s->entry_group,
AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
0,
s->service_name,
pa_sink_isinstance(s->device) ? SERVICE_TYPE_SINK : SERVICE_TYPE_SOURCE,
NULL,
NULL,
s->userdata->port,
txt) < 0) {
pa_log("avahi_entry_group_add_service_strlst(): %s", avahi_strerror(avahi_client_errno(s->userdata->client)));
goto finish;
}
if (s->loaded.valid || s->autoload.valid) {
pa_namereg_type_t type;
if (!s->entry_group) {
if (!(s->entry_group = avahi_entry_group_new(u->client, service_entry_group_callback, s))) {
pa_log("avahi_entry_group_new(): %s", avahi_strerror(avahi_client_errno(u->client)));
goto finish;
}
}
txt = avahi_string_list_add_pair(txt, "device", s->name);
txt = txt_record_server_data(u->core, txt);
if (s->loaded.valid) {
char *description;
pa_sample_spec ss;
get_service_data(u, s, &ss, &description);
txt = avahi_string_list_add_printf(txt, "rate=%u", ss.rate);
txt = avahi_string_list_add_printf(txt, "channels=%u", ss.channels);
txt = avahi_string_list_add_pair(txt, "format", pa_sample_format_to_string(ss.format));
if (description)
txt = avahi_string_list_add_pair(txt, "description", description);
type = s->loaded.type;
} else if (s->autoload.valid)
type = s->autoload.type;
if (avahi_entry_group_add_service_strlst(
s->entry_group,
AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
0,
s->service_name,
type == PA_NAMEREG_SINK ? SERVICE_TYPE_SINK : SERVICE_TYPE_SOURCE,
NULL,
NULL,
u->port,
txt) < 0) {
pa_log("avahi_entry_group_add_service_strlst(): %s", avahi_strerror(avahi_client_errno(u->client)));
goto finish;
}
if (avahi_entry_group_commit(s->entry_group) < 0) {
pa_log("avahi_entry_group_commit(): %s", avahi_strerror(avahi_client_errno(u->client)));
goto finish;
}
if (s->loaded.valid)
s->published = PUBLISHED_REAL;
else if (s->autoload.valid)
s->published = PUBLISHED_AUTOLOAD;
if (avahi_entry_group_commit(s->entry_group) < 0) {
pa_log("avahi_entry_group_commit(): %s", avahi_strerror(avahi_client_errno(s->userdata->client)));
goto finish;
}
r = 0;
pa_log_debug("Successfully created entry group for %s.", s->service_name);
finish:
if (s->published == UNPUBLISHED) {
/* Remove this service */
/* Remove this service */
if (r < 0)
service_free(s);
if (s->entry_group)
avahi_entry_group_free(s->entry_group);
pa_hashmap_remove(u->services, s->name);
pa_xfree(s->name);
pa_xfree(s->service_name);
pa_xfree(s);
}
if (txt)
avahi_string_list_free(txt);
avahi_string_list_free(txt);
return r;
}
static struct service *get_service(struct userdata *u, const char *name, const char *description) {
static struct service *get_service(struct userdata *u, pa_object *device) {
struct service *s;
char hn[64];
char hn[64], un[64];
const char *n;
if ((s = pa_hashmap_get(u->services, name)))
pa_assert(u);
pa_object_assert_ref(device);
if ((s = pa_hashmap_get(u->services, device)))
return s;
s = pa_xnew(struct service, 1);
s->userdata = u;
s->entry_group = NULL;
s->published = UNPUBLISHED;
s->name = pa_xstrdup(name);
s->loaded.valid = s->autoload.valid = 0;
s->service_name = pa_sprintf_malloc("%s on %s", description ? description : s->name, pa_get_host_name(hn, sizeof(hn)));
s->device = device;
pa_hashmap_put(u->services, s->name, s);
if (pa_sink_isinstance(device)) {
if (!(n = PA_SINK(device)->description))
n = PA_SINK(device)->name;
} else {
if (!(n = PA_SOURCE(device)->description))
n = PA_SOURCE(device)->name;
}
s->service_name = pa_truncate_utf8(pa_sprintf_malloc("%s@%s: %s",
pa_get_user_name(un, sizeof(un)),
pa_get_host_name(hn, sizeof(hn)),
n),
AVAHI_LABEL_MAX-1);
pa_hashmap_put(u->services, s->device, s);
return s;
}
static int publish_sink(struct userdata *u, pa_sink *s) {
struct service *svc;
int ret;
assert(u && s);
static void service_free(struct service *s) {
pa_assert(s);
svc = get_service(u, s->name, s->description);
if (svc->loaded.valid)
return publish_service(u, svc);
pa_hashmap_remove(s->userdata->services, s->device);
svc->loaded.valid = 1;
svc->loaded.type = PA_NAMEREG_SINK;
svc->loaded.index = s->index;
if ((ret = publish_service(u, svc)) < 0)
return ret;
pa_dynarray_put(u->sink_dynarray, s->index, svc);
return ret;
}
static int publish_source(struct userdata *u, pa_source *s) {
struct service *svc;
int ret;
assert(u && s);
svc = get_service(u, s->name, s->description);
if (svc->loaded.valid)
return publish_service(u, svc);
svc->loaded.valid = 1;
svc->loaded.type = PA_NAMEREG_SOURCE;
svc->loaded.index = s->index;
pa_dynarray_put(u->source_dynarray, s->index, svc);
if ((ret = publish_service(u, svc)) < 0)
return ret;
pa_dynarray_put(u->sink_dynarray, s->index, svc);
return ret;
}
static int publish_autoload(struct userdata *u, pa_autoload_entry *s) {
struct service *svc;
int ret;
assert(u && s);
svc = get_service(u, s->name, NULL);
if (svc->autoload.valid)
return publish_service(u, svc);
svc->autoload.valid = 1;
svc->autoload.type = s->type;
svc->autoload.index = s->index;
if ((ret = publish_service(u, svc)) < 0)
return ret;
pa_dynarray_put(u->autoload_dynarray, s->index, svc);
return ret;
}
static int remove_sink(struct userdata *u, uint32_t idx) {
struct service *svc;
assert(u && idx != PA_INVALID_INDEX);
if (!(svc = pa_dynarray_get(u->sink_dynarray, idx)))
return 0;
if (!svc->loaded.valid || svc->loaded.type != PA_NAMEREG_SINK)
return 0;
svc->loaded.valid = 0;
pa_dynarray_put(u->sink_dynarray, idx, NULL);
return publish_service(u, svc);
}
static int remove_source(struct userdata *u, uint32_t idx) {
struct service *svc;
assert(u && idx != PA_INVALID_INDEX);
if (!(svc = pa_dynarray_get(u->source_dynarray, idx)))
return 0;
if (!svc->loaded.valid || svc->loaded.type != PA_NAMEREG_SOURCE)
return 0;
svc->loaded.valid = 0;
pa_dynarray_put(u->source_dynarray, idx, NULL);
return publish_service(u, svc);
}
static int remove_autoload(struct userdata *u, uint32_t idx) {
struct service *svc;
assert(u && idx != PA_INVALID_INDEX);
if (!(svc = pa_dynarray_get(u->autoload_dynarray, idx)))
return 0;
if (!svc->autoload.valid)
return 0;
svc->autoload.valid = 0;
pa_dynarray_put(u->autoload_dynarray, idx, NULL);
return publish_service(u, svc);
}
static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
struct userdata *u = userdata;
assert(u && c);
switch (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK)
case PA_SUBSCRIPTION_EVENT_SINK: {
if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) {
pa_sink *sink;
if ((sink = pa_idxset_get_by_index(c->sinks, idx))) {
if (publish_sink(u, sink) < 0)
goto fail;
}
} else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
if (remove_sink(u, idx) < 0)
goto fail;
}
break;
case PA_SUBSCRIPTION_EVENT_SOURCE:
if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) {
pa_source *source;
if ((source = pa_idxset_get_by_index(c->sources, idx))) {
if (publish_source(u, source) < 0)
goto fail;
}
} else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
if (remove_source(u, idx) < 0)
goto fail;
}
break;
case PA_SUBSCRIPTION_EVENT_AUTOLOAD:
if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) {
pa_autoload_entry *autoload;
if ((autoload = pa_idxset_get_by_index(c->autoload_idxset, idx))) {
if (publish_autoload(u, autoload) < 0)
goto fail;
}
} else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
if (remove_autoload(u, idx) < 0)
goto fail;
}
break;
if (s->entry_group) {
pa_log_debug("Removing entry group for %s.", s->service_name);
avahi_entry_group_free(s->entry_group);
}
return;
pa_xfree(s->service_name);
pa_xfree(s);
}
fail:
if (u->subscription) {
pa_subscription_free(u->subscription);
u->subscription = NULL;
}
static pa_hook_result_t device_new_or_changed_cb(pa_core *c, pa_object *o, struct userdata *u) {
pa_assert(c);
pa_object_assert_ref(o);
publish_service(get_service(u, o));
return PA_HOOK_OK;
}
static pa_hook_result_t device_unlink_cb(pa_core *c, pa_object *o, struct userdata *u) {
struct service *s;
pa_assert(c);
pa_object_assert_ref(o);
if ((s = pa_hashmap_get(u->services, o)))
service_free(s);
return PA_HOOK_OK;
}
static int publish_main_service(struct userdata *u);
static void main_entry_group_callback(AvahiEntryGroup *g, AvahiEntryGroupState state, void *userdata) {
struct userdata *u = userdata;
assert(u);
pa_assert(u);
if (state == AVAHI_ENTRY_GROUP_COLLISION) {
char *t;
switch (state) {
t = avahi_alternative_service_name(u->service_name);
pa_xfree(u->service_name);
u->service_name = t;
case AVAHI_ENTRY_GROUP_ESTABLISHED:
pa_log_info("Successfully established main service.");
break;
publish_main_service(u);
case AVAHI_ENTRY_GROUP_COLLISION: {
char *t;
t = avahi_alternative_service_name(u->service_name);
pa_log_info("Name collision: renaming main service %s to %s.", u->service_name, t);
pa_xfree(u->service_name);
u->service_name = t;
publish_main_service(u);
break;
}
case AVAHI_ENTRY_GROUP_FAILURE: {
pa_log("Failed to register main service: %s", avahi_strerror(avahi_client_errno(avahi_entry_group_get_client(g))));
avahi_entry_group_free(g);
u->main_entry_group = NULL;
break;
}
case AVAHI_ENTRY_GROUP_UNCOMMITED:
case AVAHI_ENTRY_GROUP_REGISTERING:
break;
}
}
@ -456,6 +343,8 @@ static int publish_main_service(struct userdata *u) {
AvahiStringList *txt = NULL;
int r = -1;
pa_assert(u);
if (!u->main_entry_group) {
if (!(u->main_entry_group = avahi_entry_group_new(u->client, main_entry_group_callback, u))) {
pa_log("avahi_entry_group_new() failed: %s", avahi_strerror(avahi_client_errno(u->client)));
@ -464,7 +353,7 @@ static int publish_main_service(struct userdata *u) {
} else
avahi_entry_group_reset(u->main_entry_group);
txt = txt_record_server_data(u->core, NULL);
txt = txt_record_server_data(u->core, txt);
if (avahi_entry_group_add_service_strlst(
u->main_entry_group,
@ -497,26 +386,18 @@ fail:
static int publish_all_services(struct userdata *u) {
pa_sink *sink;
pa_source *source;
pa_autoload_entry *autoload;
int r = -1;
uint32_t idx;
assert(u);
pa_assert(u);
pa_log_debug("Publishing services in Zeroconf");
for (sink = pa_idxset_first(u->core->sinks, &idx); sink; sink = pa_idxset_next(u->core->sinks, &idx))
if (publish_sink(u, sink) < 0)
goto fail;
for (sink = PA_SINK(pa_idxset_first(u->core->sinks, &idx)); sink; sink = PA_SINK(pa_idxset_next(u->core->sinks, &idx)))
publish_service(get_service(u, PA_OBJECT(sink)));
for (source = pa_idxset_first(u->core->sources, &idx); source; source = pa_idxset_next(u->core->sources, &idx))
if (publish_source(u, source) < 0)
goto fail;
if (u->core->autoload_idxset)
for (autoload = pa_idxset_first(u->core->autoload_idxset, &idx); autoload; autoload = pa_idxset_next(u->core->autoload_idxset, &idx))
if (publish_autoload(u, autoload) < 0)
goto fail;
for (source = PA_SOURCE(pa_idxset_first(u->core->sources, &idx)); source; source = PA_SOURCE(pa_idxset_next(u->core->sources, &idx)))
publish_service(get_service(u, PA_OBJECT(source)));
if (publish_main_service(u) < 0)
goto fail;
@ -527,38 +408,44 @@ fail:
return r;
}
static void unpublish_all_services(struct userdata *u, int rem) {
static void unpublish_all_services(struct userdata *u, pa_bool_t rem) {
void *state = NULL;
struct service *s;
assert(u);
pa_assert(u);
pa_log_debug("Unpublishing services in Zeroconf");
while ((s = pa_hashmap_iterate(u->services, &state, NULL))) {
if (s->entry_group) {
if (rem) {
pa_log_debug("Removing entry group for %s.", s->service_name);
avahi_entry_group_free(s->entry_group);
s->entry_group = NULL;
} else
} else {
avahi_entry_group_reset(s->entry_group);
pa_log_debug("Resetting entry group for %s.", s->service_name);
}
}
s->published = UNPUBLISHED;
}
if (u->main_entry_group) {
if (rem) {
pa_log_debug("Removing main entry group.");
avahi_entry_group_free(u->main_entry_group);
u->main_entry_group = NULL;
} else
} else {
avahi_entry_group_reset(u->main_entry_group);
pa_log_debug("Resetting main entry group.");
}
}
}
static void client_callback(AvahiClient *c, AvahiClientState state, void *userdata) {
struct userdata *u = userdata;
assert(c);
pa_assert(c);
pa_assert(u);
u->client = c;
@ -568,13 +455,17 @@ static void client_callback(AvahiClient *c, AvahiClientState state, void *userda
break;
case AVAHI_CLIENT_S_COLLISION:
unpublish_all_services(u, 0);
pa_log_debug("Host name collision");
unpublish_all_services(u, FALSE);
break;
case AVAHI_CLIENT_FAILURE:
if (avahi_client_errno(c) == AVAHI_ERR_DISCONNECTED) {
int error;
unpublish_all_services(u, 1);
pa_log_debug("Avahi daemon disconnected.");
unpublish_all_services(u, TRUE);
avahi_client_free(u->client);
if (!(u->client = avahi_client_new(u->avahi_poll, AVAHI_CLIENT_NO_FAIL, client_callback, u, &error)))
@ -587,11 +478,12 @@ static void client_callback(AvahiClient *c, AvahiClientState state, void *userda
}
}
int pa__init(pa_core *c, pa_module*m) {
int pa__init(pa_module*m) {
struct userdata *u;
uint32_t port = PA_NATIVE_DEFAULT_PORT;
pa_modargs *ma = NULL;
char hn[256];
char hn[256], un[256];
int error;
if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
@ -599,30 +491,29 @@ int pa__init(pa_core *c, pa_module*m) {
goto fail;
}
if (pa_modargs_get_value_u32(ma, "port", &port) < 0 || port == 0 || port >= 0xFFFF) {
if (pa_modargs_get_value_u32(ma, "port", &port) < 0 || port <= 0 || port > 0xFFFF) {
pa_log("invalid port specified.");
goto fail;
}
m->userdata = u = pa_xnew(struct userdata, 1);
u->core = c;
u->core = m->core;
u->port = (uint16_t) port;
u->avahi_poll = pa_avahi_poll_new(c->mainloop);
u->avahi_poll = pa_avahi_poll_new(m->core->mainloop);
u->services = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
u->sink_dynarray = pa_dynarray_new();
u->source_dynarray = pa_dynarray_new();
u->autoload_dynarray = pa_dynarray_new();
u->services = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
u->subscription = pa_subscription_new(c,
PA_SUBSCRIPTION_MASK_SINK|
PA_SUBSCRIPTION_MASK_SOURCE|
PA_SUBSCRIPTION_MASK_AUTOLOAD, subscribe_callback, u);
u->sink_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_NEW_POST], (pa_hook_cb_t) device_new_or_changed_cb, u);
u->sink_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_DESCRIPTION_CHANGED], (pa_hook_cb_t) device_new_or_changed_cb, u);
u->sink_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_UNLINK], (pa_hook_cb_t) device_unlink_cb, u);
u->source_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_NEW_POST], (pa_hook_cb_t) device_new_or_changed_cb, u);
u->source_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_DESCRIPTION_CHANGED], (pa_hook_cb_t) device_new_or_changed_cb, u);
u->source_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK], (pa_hook_cb_t) device_unlink_cb, u);
u->main_entry_group = NULL;
u->service_name = pa_xstrdup(pa_get_host_name(hn, sizeof(hn)));
u->service_name = pa_truncate_utf8(pa_sprintf_malloc("%s@%s", pa_get_user_name(un, sizeof(un)), pa_get_host_name(hn, sizeof(hn))), AVAHI_LABEL_MAX);
if (!(u->client = avahi_client_new(u->avahi_poll, AVAHI_CLIENT_NO_FAIL, client_callback, u, &error))) {
pa_log("pa_avahi_client_new() failed: %s", avahi_strerror(error));
@ -634,7 +525,7 @@ int pa__init(pa_core *c, pa_module*m) {
return 0;
fail:
pa__done(c, m);
pa__done(m);
if (ma)
pa_modargs_free(ma);
@ -642,41 +533,34 @@ fail:
return -1;
}
static void service_free(void *p, void *userdata) {
struct service *s = p;
struct userdata *u = userdata;
assert(s);
assert(u);
if (s->entry_group)
avahi_entry_group_free(s->entry_group);
pa_xfree(s->service_name);
pa_xfree(s->name);
pa_xfree(s);
}
void pa__done(pa_core *c, pa_module*m) {
void pa__done(pa_module*m) {
struct userdata*u;
assert(c && m);
pa_assert(m);
if (!(u = m->userdata))
return;
if (u->services)
pa_hashmap_free(u->services, service_free, u);
if (u->services) {
struct service *s;
if (u->subscription)
pa_subscription_free(u->subscription);
while ((s = pa_hashmap_get_first(u->services)))
service_free(s);
if (u->sink_dynarray)
pa_dynarray_free(u->sink_dynarray, NULL, NULL);
if (u->source_dynarray)
pa_dynarray_free(u->source_dynarray, NULL, NULL);
if (u->autoload_dynarray)
pa_dynarray_free(u->autoload_dynarray, NULL, NULL);
pa_hashmap_free(u->services, NULL, NULL);
}
if (u->sink_new_slot)
pa_hook_slot_free(u->sink_new_slot);
if (u->source_new_slot)
pa_hook_slot_free(u->source_new_slot);
if (u->sink_changed_slot)
pa_hook_slot_free(u->sink_changed_slot);
if (u->source_changed_slot)
pa_hook_slot_free(u->source_changed_slot);
if (u->sink_unlink_slot)
pa_hook_slot_free(u->sink_unlink_slot);
if (u->source_unlink_slot)
pa_hook_slot_free(u->source_unlink_slot);
if (u->main_entry_group)
avahi_entry_group_free(u->main_entry_group);
@ -690,4 +574,3 @@ void pa__done(pa_core *c, pa_module*m) {
pa_xfree(u->service_name);
pa_xfree(u);
}

View file

@ -26,7 +26,6 @@
#include <config.h>
#endif
#include <assert.h>
#include <sys/soundcard.h>
#include <sys/ioctl.h>
#include <stdio.h>
@ -37,9 +36,11 @@
#include <sys/stat.h>
#include <fcntl.h>
#include <pulse/xmalloc.h>
#include <pulsecore/core-error.h>
#include <pulsecore/core-util.h>
#include <pulsecore/log.h>
#include <pulsecore/macro.h>
#include "oss-util.h"
@ -47,46 +48,43 @@ int pa_oss_open(const char *device, int *mode, int* pcaps) {
int fd = -1;
int caps;
assert(device && mode && (*mode == O_RDWR || *mode == O_RDONLY || *mode == O_WRONLY));
pa_assert(device);
pa_assert(mode);
pa_assert(*mode == O_RDWR || *mode == O_RDONLY || *mode == O_WRONLY);
if(!pcaps)
pcaps = &caps;
if (*mode == O_RDWR) {
if ((fd = open(device, O_RDWR|O_NDELAY)) >= 0) {
int dcaps, *tcaps;
if ((fd = open(device, O_RDWR|O_NDELAY|O_NOCTTY)) >= 0) {
ioctl(fd, SNDCTL_DSP_SETDUPLEX, 0);
tcaps = pcaps ? pcaps : &dcaps;
if (ioctl(fd, SNDCTL_DSP_GETCAPS, tcaps) < 0) {
if (ioctl(fd, SNDCTL_DSP_GETCAPS, pcaps) < 0) {
pa_log("SNDCTL_DSP_GETCAPS: %s", pa_cstrerror(errno));
goto fail;
}
if (*tcaps & DSP_CAP_DUPLEX)
if (*pcaps & DSP_CAP_DUPLEX)
goto success;
pa_log_warn("'%s' doesn't support full duplex", device);
close(fd);
pa_close(fd);
}
if ((fd = open(device, (*mode = O_WRONLY)|O_NDELAY)) < 0) {
if ((fd = open(device, (*mode = O_RDONLY)|O_NDELAY)) < 0) {
if ((fd = open(device, (*mode = O_WRONLY)|O_NDELAY|O_NOCTTY)) < 0) {
if ((fd = open(device, (*mode = O_RDONLY)|O_NDELAY|O_NOCTTY)) < 0) {
pa_log("open('%s'): %s", device, pa_cstrerror(errno));
goto fail;
}
}
} else {
if ((fd = open(device, *mode|O_NDELAY)) < 0) {
if ((fd = open(device, *mode|O_NDELAY|O_NOCTTY)) < 0) {
pa_log("open('%s'): %s", device, pa_cstrerror(errno));
goto fail;
}
}
success:
*pcaps = 0;
if (ioctl(fd, SNDCTL_DSP_GETCAPS, pcaps) < 0) {
@ -94,12 +92,14 @@ success:
goto fail;
}
success:
pa_log_debug("capabilities:%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
*pcaps & DSP_CAP_BATCH ? " BATCH" : "",
#ifdef DSP_CAP_BIND
*pcaps & DSP_CAP_BIND ? " BIND" : "",
#else
"",
"",
#endif
*pcaps & DSP_CAP_COPROC ? " COPROC" : "",
*pcaps & DSP_CAP_DUPLEX ? " DUPLEX" : "",
@ -122,7 +122,7 @@ success:
#ifdef DSP_CAP_MULTI
*pcaps & DSP_CAP_MULTI ? " MULTI" : "",
#else
"",
"",
#endif
#ifdef DSP_CAP_OUTPUT
*pcaps & DSP_CAP_OUTPUT ? " OUTPUT" : "",
@ -142,13 +142,13 @@ success:
#endif
*pcaps & DSP_CAP_TRIGGER ? " TRIGGER" : "");
pa_fd_set_cloexec(fd, 1);
pa_make_fd_cloexec(fd);
return fd;
fail:
if (fd >= 0)
close(fd);
pa_close(fd);
return -1;
}
@ -166,7 +166,8 @@ int pa_oss_auto_format(int fd, pa_sample_spec *ss) {
[PA_SAMPLE_FLOAT32BE] = AFMT_QUERY, /* not supported */
};
assert(fd >= 0 && ss);
pa_assert(fd >= 0);
pa_assert(ss);
orig_format = ss->format;
@ -199,7 +200,7 @@ int pa_oss_auto_format(int fd, pa_sample_spec *ss) {
pa_log("SNDCTL_DSP_CHANNELS: %s", pa_cstrerror(errno));
return -1;
}
assert(channels > 0);
pa_assert(channels > 0);
if (ss->channels != channels) {
pa_log_warn("device doesn't support %i channels, using %i channels.", ss->channels, channels);
@ -211,7 +212,7 @@ int pa_oss_auto_format(int fd, pa_sample_spec *ss) {
pa_log("SNDCTL_DSP_SPEED: %s", pa_cstrerror(errno));
return -1;
}
assert(speed > 0);
pa_assert(speed > 0);
if (ss->rate != (unsigned) speed) {
pa_log_warn("device doesn't support %i Hz, changed to %i Hz.", ss->rate, speed);
@ -248,27 +249,29 @@ int pa_oss_set_fragments(int fd, int nfrags, int frag_size) {
return 0;
}
static int pa_oss_get_volume(int fd, int mixer, const pa_sample_spec *ss, pa_cvolume *volume) {
int pa_oss_get_volume(int fd, int mixer, const pa_sample_spec *ss, pa_cvolume *volume) {
char cv[PA_CVOLUME_SNPRINT_MAX];
unsigned vol;
assert(fd >= 0);
assert(ss);
assert(volume);
pa_assert(fd >= 0);
pa_assert(ss);
pa_assert(volume);
if (ioctl(fd, mixer, &vol) < 0)
return -1;
pa_cvolume_reset(volume, ss->channels);
volume->values[0] = ((vol & 0xFF) * PA_VOLUME_NORM) / 100;
if ((volume->channels = ss->channels) >= 2)
if (volume->channels >= 2)
volume->values[1] = (((vol >> 8) & 0xFF) * PA_VOLUME_NORM) / 100;
pa_log_debug("Read mixer settings: %s", pa_cvolume_snprint(cv, sizeof(cv), volume));
return 0;
}
static int pa_oss_set_volume(int fd, int mixer, const pa_sample_spec *ss, const pa_cvolume *volume) {
int pa_oss_set_volume(int fd, long mixer, const pa_sample_spec *ss, const pa_cvolume *volume) {
char cv[PA_CVOLUME_SNPRINT_MAX];
unsigned vol;
pa_volume_t l, r;
@ -289,40 +292,38 @@ static int pa_oss_set_volume(int fd, int mixer, const pa_sample_spec *ss, const
return 0;
}
int pa_oss_get_pcm_volume(int fd, const pa_sample_spec *ss, pa_cvolume *volume) {
return pa_oss_get_volume(fd, SOUND_MIXER_READ_PCM, ss, volume);
}
static int get_device_number(const char *dev) {
char buf[PATH_MAX];
const char *p, *e;
int pa_oss_set_pcm_volume(int fd, const pa_sample_spec *ss, const pa_cvolume *volume) {
return pa_oss_set_volume(fd, SOUND_MIXER_WRITE_PCM, ss, volume);
}
if (readlink(dev, buf, sizeof(buf)) < 0) {
if (errno != EINVAL && errno != ENOLINK)
return -1;
int pa_oss_get_input_volume(int fd, const pa_sample_spec *ss, pa_cvolume *volume) {
return pa_oss_get_volume(fd, SOUND_MIXER_READ_IGAIN, ss, volume);
}
p = dev;
} else
p = buf;
int pa_oss_set_input_volume(int fd, const pa_sample_spec *ss, const pa_cvolume *volume) {
return pa_oss_set_volume(fd, SOUND_MIXER_WRITE_IGAIN, ss, volume);
if ((e = strrchr(p, '/')))
p = e+1;
if (p == 0)
return 0;
p = strchr(p, 0) -1;
if (*p >= '0' && *p <= '9')
return *p - '0';
return -1;
}
int pa_oss_get_hw_description(const char *dev, char *name, size_t l) {
FILE *f;
const char *e = NULL;
int n, r = -1;
int b = 0;
if (strncmp(dev, "/dev/dsp", 8) == 0)
e = dev+8;
else if (strncmp(dev, "/dev/adsp", 9) == 0)
e = dev+9;
else
return -1;
if (*e == 0)
n = 0;
else if (*e >= '0' && *e <= '9' && *(e+1) == 0)
n = *e - '0';
else
if ((n = get_device_number(dev)) < 0)
return -1;
if (!(f = fopen("/dev/sndstat", "r")) &&
@ -357,7 +358,7 @@ int pa_oss_get_hw_description(const char *dev, char *name, size_t l) {
if (device == n) {
char *k = strchr(line, ':');
assert(k);
pa_assert(k);
k++;
k += strspn(k, " ");
@ -373,3 +374,34 @@ int pa_oss_get_hw_description(const char *dev, char *name, size_t l) {
fclose(f);
return r;
}
static int open_mixer(const char *mixer) {
int fd;
if ((fd = open(mixer, O_RDWR|O_NDELAY|O_NOCTTY)) >= 0)
return fd;
return -1;
}
int pa_oss_open_mixer_for_device(const char *device) {
int n;
char *fn;
int fd;
if ((n = get_device_number(device)) < 0)
return -1;
if (n == 0)
if ((fd = open_mixer("/dev/mixer")) >= 0)
return fd;
fn = pa_sprintf_malloc("/dev/mixer%i", n);
fd = open_mixer(fn);
pa_xfree(fn);
if (fd < 0)
pa_log_warn("Failed to open mixer '%s': %s", device, pa_cstrerror(errno));
return fd;
}

View file

@ -33,12 +33,11 @@ int pa_oss_auto_format(int fd, pa_sample_spec *ss);
int pa_oss_set_fragments(int fd, int frags, int frag_size);
int pa_oss_get_pcm_volume(int fd, const pa_sample_spec *ss, pa_cvolume *volume);
int pa_oss_set_pcm_volume(int fd, const pa_sample_spec *ss, const pa_cvolume *volume);
int pa_oss_get_input_volume(int fd, const pa_sample_spec *ss, pa_cvolume *volume);
int pa_oss_set_input_volume(int fd, const pa_sample_spec *ss, const pa_cvolume *volume);
int pa_oss_set_volume(int fd, long mixer, const pa_sample_spec *ss, const pa_cvolume *volume);
int pa_oss_get_volume(int fd, int mixer, const pa_sample_spec *ss, pa_cvolume *volume);
int pa_oss_get_hw_description(const char *dev, char *name, size_t l);
int pa_oss_open_mixer_for_device(const char *device);
#endif

View file

@ -24,7 +24,6 @@
#include <config.h>
#endif
#include <assert.h>
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
@ -32,6 +31,7 @@
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <poll.h>
#include <pulse/timeval.h>
#include <pulse/xmalloc.h>
@ -47,6 +47,10 @@
#include <pulsecore/modargs.h>
#include <pulsecore/namereg.h>
#include <pulsecore/sample-util.h>
#include <pulsecore/macro.h>
#include <pulsecore/atomic.h>
#include <pulsecore/rtclock.h>
#include <pulsecore/atomic.h>
#include "module-rtp-recv-symdef.h"
@ -66,7 +70,7 @@ PA_MODULE_USAGE(
#define DEFAULT_SAP_ADDRESS "224.0.0.56"
#define MEMBLOCKQ_MAXLENGTH (1024*170)
#define MAX_SESSIONS 16
#define DEATH_TIMEOUT 20000000
#define DEATH_TIMEOUT 20
static const char* const valid_modargs[] = {
"sink",
@ -76,102 +80,126 @@ static const char* const valid_modargs[] = {
struct session {
struct userdata *userdata;
PA_LLIST_FIELDS(struct session);
pa_sink_input *sink_input;
pa_memblockq *memblockq;
pa_time_event *death_event;
int first_packet;
pa_bool_t first_packet;
uint32_t ssrc;
uint32_t offset;
struct pa_sdp_info sdp_info;
pa_rtp_context rtp_context;
pa_io_event* rtp_event;
pa_rtpoll_item *rtpoll_item;
pa_atomic_t timestamp;
};
struct userdata {
pa_module *module;
pa_core *core;
pa_sap_context sap_context;
pa_io_event* sap_event;
pa_hashmap *by_origin;
pa_time_event *check_death_event;
char *sink_name;
PA_LLIST_HEAD(struct session, sessions);
pa_hashmap *by_origin;
int n_sessions;
};
static void session_free(struct session *s, int from_hash);
static void session_free(struct session *s);
static int sink_input_peek(pa_sink_input *i, pa_memchunk *chunk) {
/* Called from I/O thread context */
static int sink_input_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
struct session *s = PA_SINK_INPUT(o)->userdata;
switch (code) {
case PA_SINK_INPUT_MESSAGE_GET_LATENCY:
*((pa_usec_t*) data) = pa_bytes_to_usec(pa_memblockq_get_length(s->memblockq), &s->sink_input->sample_spec);
/* Fall through, the default handler will add in the extra
* latency added by the resampler */
break;
}
return pa_sink_input_process_msg(o, code, data, offset, chunk);
}
/* Called from I/O thread context */
static int sink_input_peek(pa_sink_input *i, size_t length, pa_memchunk *chunk) {
struct session *s;
assert(i);
s = i->userdata;
pa_sink_input_assert_ref(i);
pa_assert_se(s = i->userdata);
return pa_memblockq_peek(s->memblockq, chunk);
}
static void sink_input_drop(pa_sink_input *i, const pa_memchunk *chunk, size_t length) {
/* Called from I/O thread context */
static void sink_input_drop(pa_sink_input *i, size_t length) {
struct session *s;
assert(i);
s = i->userdata;
pa_sink_input_assert_ref(i);
pa_assert_se(s = i->userdata);
pa_memblockq_drop(s->memblockq, chunk, length);
pa_memblockq_drop(s->memblockq, length);
}
/* Called from main context */
static void sink_input_kill(pa_sink_input* i) {
struct session *s;
assert(i);
s = i->userdata;
pa_sink_input_assert_ref(i);
pa_assert_se(s = i->userdata);
session_free(s, 1);
session_free(s);
}
static pa_usec_t sink_input_get_latency(pa_sink_input *i) {
struct session *s;
assert(i);
s = i->userdata;
return pa_bytes_to_usec(pa_memblockq_get_length(s->memblockq), &i->sample_spec);
}
static void rtp_event_cb(pa_mainloop_api *m, pa_io_event *e, int fd, pa_io_event_flags_t flags, void *userdata) {
struct session *s = userdata;
/* Called from I/O thread context */
static int rtpoll_work_cb(pa_rtpoll_item *i) {
pa_memchunk chunk;
int64_t k, j, delta;
struct timeval tv;
struct timeval now;
struct session *s;
struct pollfd *p;
assert(m);
assert(e);
assert(s);
assert(fd == s->rtp_context.fd);
assert(flags == PA_IO_EVENT_INPUT);
pa_assert_se(s = pa_rtpoll_item_get_userdata(i));
if (pa_rtp_recv(&s->rtp_context, &chunk, s->userdata->core->mempool) < 0)
return;
p = pa_rtpoll_item_get_pollfd(i, NULL);
if (p->revents & (POLLERR|POLLNVAL|POLLHUP|POLLOUT)) {
pa_log("poll() signalled bad revents.");
return -1;
}
if ((p->revents & POLLIN) == 0)
return 0;
p->revents = 0;
if (pa_rtp_recv(&s->rtp_context, &chunk, s->userdata->module->core->mempool) < 0)
return 0;
if (s->sdp_info.payload != s->rtp_context.payload) {
pa_memblock_unref(chunk.memblock);
return;
return 0;
}
if (!s->first_packet) {
s->first_packet = 1;
s->first_packet = TRUE;
s->ssrc = s->rtp_context.ssrc;
s->offset = s->rtp_context.timestamp;
if (s->ssrc == s->userdata->core->cookie)
pa_log_warn("WARNING! Detected RTP packet loop!");
if (s->ssrc == s->userdata->module->core->cookie)
pa_log_warn("Detected RTP packet loop!");
} else {
if (s->ssrc != s->rtp_context.ssrc) {
pa_memblock_unref(chunk.memblock);
return;
return 0;
}
}
@ -197,26 +225,49 @@ static void rtp_event_cb(pa_mainloop_api *m, pa_io_event *e, int fd, pa_io_event
pa_memblock_unref(chunk.memblock);
/* Reset death timer */
pa_gettimeofday(&tv);
pa_timeval_add(&tv, DEATH_TIMEOUT);
m->time_restart(s->death_event, &tv);
pa_rtclock_get(&now);
pa_atomic_store(&s->timestamp, now.tv_sec);
return 1;
}
static void death_event_cb(pa_mainloop_api *m, pa_time_event *t, const struct timeval *tv, void *userdata) {
struct session *s = userdata;
/* Called from I/O thread context */
static void sink_input_attach(pa_sink_input *i) {
struct session *s;
struct pollfd *p;
assert(m);
assert(t);
assert(tv);
assert(s);
pa_sink_input_assert_ref(i);
pa_assert_se(s = i->userdata);
session_free(s, 1);
pa_assert(!s->rtpoll_item);
s->rtpoll_item = pa_rtpoll_item_new(i->sink->rtpoll, PA_RTPOLL_LATE, 1);
p = pa_rtpoll_item_get_pollfd(s->rtpoll_item, NULL);
p->fd = s->rtp_context.fd;
p->events = POLLIN;
p->revents = 0;
pa_rtpoll_item_set_work_callback(s->rtpoll_item, rtpoll_work_cb);
pa_rtpoll_item_set_userdata(s->rtpoll_item, s);
}
/* Called from I/O thread context */
static void sink_input_detach(pa_sink_input *i) {
struct session *s;
pa_sink_input_assert_ref(i);
pa_assert_se(s = i->userdata);
pa_assert(s->rtpoll_item);
pa_rtpoll_item_free(s->rtpoll_item);
s->rtpoll_item = NULL;
}
static int mcast_socket(const struct sockaddr* sa, socklen_t salen) {
int af, fd = -1, r, one;
pa_assert(sa);
pa_assert(salen > 0);
af = sa->sa_family;
if ((fd = socket(af, SOCK_DGRAM, 0)) < 0) {
pa_log("Failed to create socket: %s", pa_cstrerror(errno));
@ -262,27 +313,34 @@ fail:
static struct session *session_new(struct userdata *u, const pa_sdp_info *sdp_info) {
struct session *s = NULL;
struct timeval tv;
char *c;
pa_sink *sink;
int fd = -1;
pa_memblock *silence;
pa_sink_input_new_data data;
struct timeval now;
pa_assert(u);
pa_assert(sdp_info);
if (u->n_sessions >= MAX_SESSIONS) {
pa_log("session limit reached.");
pa_log("Session limit reached.");
goto fail;
}
if (!(sink = pa_namereg_get(u->core, u->sink_name, PA_NAMEREG_SINK, 1))) {
pa_log("sink does not exist.");
if (!(sink = pa_namereg_get(u->module->core, u->sink_name, PA_NAMEREG_SINK, 1))) {
pa_log("Sink does not exist.");
goto fail;
}
s = pa_xnew0(struct session, 1);
s->userdata = u;
s->first_packet = 0;
s->first_packet = FALSE;
s->sdp_info = *sdp_info;
s->rtpoll_item = NULL;
pa_rtclock_get(&now);
pa_atomic_store(&s->timestamp, now.tv_sec);
if ((fd = mcast_socket((const struct sockaddr*) &sdp_info->sa, sdp_info->salen)) < 0)
goto fail;
@ -299,25 +357,27 @@ static struct session *session_new(struct userdata *u, const pa_sdp_info *sdp_in
data.module = u->module;
pa_sink_input_new_data_set_sample_spec(&data, &sdp_info->sample_spec);
s->sink_input = pa_sink_input_new(u->core, &data, 0);
s->sink_input = pa_sink_input_new(u->module->core, &data, 0);
pa_xfree(c);
if (!s->sink_input) {
pa_log("failed to create sink input.");
pa_log("Failed to create sink input.");
goto fail;
}
s->sink_input->userdata = s;
s->sink_input->parent.process_msg = sink_input_process_msg;
s->sink_input->peek = sink_input_peek;
s->sink_input->drop = sink_input_drop;
s->sink_input->kill = sink_input_kill;
s->sink_input->get_latency = sink_input_get_latency;
s->sink_input->attach = sink_input_attach;
s->sink_input->detach = sink_input_detach;
silence = pa_silence_memblock_new(s->userdata->core->mempool,
&s->sink_input->sample_spec,
(pa_bytes_per_second(&s->sink_input->sample_spec)/128/pa_frame_size(&s->sink_input->sample_spec))*
pa_frame_size(&s->sink_input->sample_spec));
silence = pa_silence_memblock_new(
s->userdata->module->core->mempool,
&s->sink_input->sample_spec,
pa_frame_align(pa_bytes_per_second(&s->sink_input->sample_spec)/128, &s->sink_input->sample_spec));
s->memblockq = pa_memblockq_new(
0,
@ -330,54 +390,44 @@ static struct session *session_new(struct userdata *u, const pa_sdp_info *sdp_in
pa_memblock_unref(silence);
s->rtp_event = u->core->mainloop->io_new(u->core->mainloop, fd, PA_IO_EVENT_INPUT, rtp_event_cb, s);
pa_gettimeofday(&tv);
pa_timeval_add(&tv, DEATH_TIMEOUT);
s->death_event = u->core->mainloop->time_new(u->core->mainloop, &tv, death_event_cb, s);
pa_hashmap_put(s->userdata->by_origin, s->sdp_info.origin, s);
pa_rtp_context_init_recv(&s->rtp_context, fd, pa_frame_size(&s->sdp_info.sample_spec));
pa_log_info("Found new session '%s'", s->sdp_info.session_name);
pa_hashmap_put(s->userdata->by_origin, s->sdp_info.origin, s);
u->n_sessions++;
PA_LLIST_PREPEND(struct session, s->userdata->sessions, s);
pa_sink_input_put(s->sink_input);
pa_log_info("New session '%s'", s->sdp_info.session_name);
return s;
fail:
if (s) {
if (fd >= 0)
close(fd);
pa_xfree(s);
pa_xfree(s);
}
if (fd >= 0)
pa_close(fd);
return NULL;
}
static void session_free(struct session *s, int from_hash) {
assert(s);
static void session_free(struct session *s) {
pa_assert(s);
pa_log_info("Freeing session '%s'", s->sdp_info.session_name);
s->userdata->core->mainloop->time_free(s->death_event);
s->userdata->core->mainloop->io_free(s->rtp_event);
if (from_hash)
pa_hashmap_remove(s->userdata->by_origin, s->sdp_info.origin);
pa_sink_input_disconnect(s->sink_input);
pa_sink_input_unlink(s->sink_input);
pa_sink_input_unref(s->sink_input);
PA_LLIST_REMOVE(struct session, s->userdata->sessions, s);
pa_assert(s->userdata->n_sessions >= 1);
s->userdata->n_sessions--;
pa_hashmap_remove(s->userdata->by_origin, s->sdp_info.origin);
pa_memblockq_free(s->memblockq);
pa_sdp_info_destroy(&s->sdp_info);
pa_rtp_context_destroy(&s->rtp_context);
assert(s->userdata->n_sessions >= 1);
s->userdata->n_sessions--;
pa_xfree(s);
}
@ -387,11 +437,11 @@ static void sap_event_cb(pa_mainloop_api *m, pa_io_event *e, int fd, pa_io_event
pa_sdp_info info;
struct session *s;
assert(m);
assert(e);
assert(u);
assert(fd == u->sap_context.fd);
assert(flags == PA_IO_EVENT_INPUT);
pa_assert(m);
pa_assert(e);
pa_assert(u);
pa_assert(fd == u->sap_context.fd);
pa_assert(flags == PA_IO_EVENT_INPUT);
if (pa_sap_recv(&u->sap_context, &goodbye) < 0)
return;
@ -402,7 +452,7 @@ static void sap_event_cb(pa_mainloop_api *m, pa_io_event *e, int fd, pa_io_event
if (goodbye) {
if ((s = pa_hashmap_get(u->by_origin, info.origin)))
session_free(s, 1);
session_free(s);
pa_sdp_info_destroy(&info);
} else {
@ -412,18 +462,47 @@ static void sap_event_cb(pa_mainloop_api *m, pa_io_event *e, int fd, pa_io_event
pa_sdp_info_destroy(&info);
} else {
struct timeval tv;
pa_gettimeofday(&tv);
pa_timeval_add(&tv, DEATH_TIMEOUT);
m->time_restart(s->death_event, &tv);
struct timeval now;
pa_rtclock_get(&now);
pa_atomic_store(&s->timestamp, now.tv_sec);
pa_sdp_info_destroy(&info);
}
}
}
int pa__init(pa_core *c, pa_module*m) {
static void check_death_event_cb(pa_mainloop_api *m, pa_time_event *t, const struct timeval *ptv, void *userdata) {
struct session *s, *n;
struct userdata *u = userdata;
struct timeval now;
struct timeval tv;
pa_assert(m);
pa_assert(t);
pa_assert(ptv);
pa_assert(u);
pa_rtclock_get(&now);
pa_log_debug("Checking for dead streams ...");
for (s = u->sessions; s; s = n) {
int k;
n = s->next;
k = pa_atomic_load(&s->timestamp);
if (k + DEATH_TIMEOUT < now.tv_sec)
session_free(s);
}
/* Restart timer */
pa_gettimeofday(&tv);
pa_timeval_add(&tv, DEATH_TIMEOUT*PA_USEC_PER_SEC);
m->time_restart(t, &tv);
}
int pa__init(pa_module*m) {
struct userdata *u;
pa_modargs *ma = NULL;
struct sockaddr_in sa4;
@ -432,9 +511,9 @@ int pa__init(pa_core *c, pa_module*m) {
socklen_t salen;
const char *sap_address;
int fd = -1;
struct timeval tv;
assert(c);
assert(m);
pa_assert(m);
if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
pa_log("failed to parse module arguments");
@ -454,7 +533,7 @@ int pa__init(pa_core *c, pa_module*m) {
sa = (struct sockaddr*) &sa4;
salen = sizeof(sa4);
} else {
pa_log("invalid SAP address '%s'", sap_address);
pa_log("Invalid SAP address '%s'", sap_address);
goto fail;
}
@ -464,15 +543,18 @@ int pa__init(pa_core *c, pa_module*m) {
u = pa_xnew(struct userdata, 1);
m->userdata = u;
u->module = m;
u->core = c;
u->sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL));
u->sap_event = m->core->mainloop->io_new(m->core->mainloop, fd, PA_IO_EVENT_INPUT, sap_event_cb, u);
pa_sap_context_init_recv(&u->sap_context, fd);
PA_LLIST_HEAD_INIT(struct session, u->sessions);
u->n_sessions = 0;
u->sap_event = c->mainloop->io_new(c->mainloop, fd, PA_IO_EVENT_INPUT, sap_event_cb, u);
u->by_origin = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
pa_sap_context_init_recv(&u->sap_context, fd);
pa_gettimeofday(&tv);
pa_timeval_add(&tv, DEATH_TIMEOUT * PA_USEC_PER_SEC);
u->check_death_event = m->core->mainloop->time_new(m->core->mainloop, &tv, check_death_event_cb, u);
pa_modargs_free(ma);
@ -483,27 +565,34 @@ fail:
pa_modargs_free(ma);
if (fd >= 0)
close(fd);
pa_close(fd);
return -1;
}
static void free_func(void *p, PA_GCC_UNUSED void *userdata) {
session_free(p, 0);
}
void pa__done(pa_core *c, pa_module*m) {
void pa__done(pa_module*m) {
struct userdata *u;
assert(c);
assert(m);
struct session *s;
pa_assert(m);
if (!(u = m->userdata))
return;
c->mainloop->io_free(u->sap_event);
if (u->sap_event)
m->core->mainloop->io_free(u->sap_event);
if (u->check_death_event)
m->core->mainloop->time_free(u->check_death_event);
pa_sap_context_destroy(&u->sap_context);
pa_hashmap_free(u->by_origin, free_func, NULL);
if (u->by_origin) {
while ((s = pa_hashmap_get_first(u->by_origin)))
session_free(s);
pa_hashmap_free(u->by_origin, NULL, NULL);
}
pa_xfree(u->sink_name);
pa_xfree(u);

View file

@ -25,7 +25,6 @@
#include <config.h>
#endif
#include <assert.h>
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
@ -48,6 +47,9 @@
#include <pulsecore/core-util.h>
#include <pulsecore/modargs.h>
#include <pulsecore/namereg.h>
#include <pulsecore/sample-util.h>
#include <pulsecore/macro.h>
#include <pulsecore/socket-util.h>
#include "module-rtp-send-symdef.h"
@ -74,7 +76,7 @@ PA_MODULE_USAGE(
#define DEFAULT_DESTINATION "224.0.0.56"
#define MEMBLOCKQ_MAXLENGTH (1024*170)
#define DEFAULT_MTU 1280
#define SAP_INTERVAL 5000000
#define SAP_INTERVAL 5
static const char* const valid_modargs[] = {
"source",
@ -90,7 +92,6 @@ static const char* const valid_modargs[] = {
struct userdata {
pa_module *module;
pa_core *core;
pa_source_output *source_output;
pa_memblockq *memblockq;
@ -102,56 +103,67 @@ struct userdata {
pa_time_event *sap_event;
};
/* Called from I/O thread context */
static int source_output_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
struct userdata *u;
pa_assert_se(u = PA_SOURCE_OUTPUT(o)->userdata);
switch (code) {
case PA_SOURCE_OUTPUT_MESSAGE_GET_LATENCY:
*((pa_usec_t*) data) = pa_bytes_to_usec(pa_memblockq_get_length(u->memblockq), &u->source_output->sample_spec);
/* Fall through, the default handler will add in the extra
* latency added by the resampler */
break;
}
return pa_source_output_process_msg(o, code, data, offset, chunk);
}
/* Called from I/O thread context */
static void source_output_push(pa_source_output *o, const pa_memchunk *chunk) {
struct userdata *u;
assert(o);
u = o->userdata;
pa_source_output_assert_ref(o);
pa_assert_se(u = o->userdata);
if (pa_memblockq_push(u->memblockq, chunk) < 0) {
pa_log("Failed to push chunk into memblockq.");
pa_log_warn("Failed to push chunk into memblockq.");
return;
}
pa_rtp_send(&u->rtp_context, u->mtu, u->memblockq);
}
/* Called from main context */
static void source_output_kill(pa_source_output* o) {
struct userdata *u;
assert(o);
u = o->userdata;
pa_source_output_assert_ref(o);
pa_assert_se(u = o->userdata);
pa_module_unload_request(u->module);
pa_source_output_disconnect(u->source_output);
pa_source_output_unlink(u->source_output);
pa_source_output_unref(u->source_output);
u->source_output = NULL;
}
static pa_usec_t source_output_get_latency (pa_source_output *o) {
struct userdata *u;
assert(o);
u = o->userdata;
return pa_bytes_to_usec(pa_memblockq_get_length(u->memblockq), &o->sample_spec);
}
static void sap_event_cb(pa_mainloop_api *m, pa_time_event *t, const struct timeval *tv, void *userdata) {
struct userdata *u = userdata;
struct timeval next;
assert(m);
assert(t);
assert(tv);
assert(u);
pa_assert(m);
pa_assert(t);
pa_assert(tv);
pa_assert(u);
pa_sap_send(&u->sap_context, 0);
pa_gettimeofday(&next);
pa_timeval_add(&next, SAP_INTERVAL);
pa_timeval_add(&next, SAP_INTERVAL * PA_USEC_PER_SEC);
m->time_restart(t, &next);
}
int pa__init(pa_core *c, pa_module*m) {
int pa__init(pa_module*m) {
struct userdata *u;
pa_modargs *ma = NULL;
const char *dest;
@ -173,21 +185,20 @@ int pa__init(pa_core *c, pa_module*m) {
int loop = 0;
pa_source_output_new_data data;
assert(c);
assert(m);
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;
}
if (!(s = pa_namereg_get(m->core, pa_modargs_get_value(ma, "source", NULL), PA_NAMEREG_SOURCE, 1))) {
pa_log("source does not exist.");
pa_log("Source does not exist.");
goto fail;
}
if (pa_modargs_get_value_boolean(ma, "loop", &loop) < 0) {
pa_log("failed to parse \"loop\" parameter.");
pa_log("Failed to parse \"loop\" parameter.");
goto fail;
}
@ -195,12 +206,12 @@ int pa__init(pa_core *c, pa_module*m) {
pa_rtp_sample_spec_fixup(&ss);
cm = s->channel_map;
if (pa_modargs_get_sample_spec(ma, &ss) < 0) {
pa_log("failed to parse sample specification");
pa_log("Failed to parse sample specification");
goto fail;
}
if (!pa_rtp_sample_spec_valid(&ss)) {
pa_log("specified sample type not compatible with RTP");
pa_log("Specified sample type not compatible with RTP");
goto fail;
}
@ -209,10 +220,10 @@ int pa__init(pa_core *c, pa_module*m) {
payload = pa_rtp_payload_from_sample_spec(&ss);
mtu = (DEFAULT_MTU/pa_frame_size(&ss))*pa_frame_size(&ss);
mtu = pa_frame_align(DEFAULT_MTU, &ss);
if (pa_modargs_get_value_u32(ma, "mtu", &mtu) < 0 || mtu < 1 || mtu % pa_frame_size(&ss) != 0) {
pa_log("invalid mtu.");
pa_log("Invalid MTU.");
goto fail;
}
@ -223,7 +234,7 @@ int pa__init(pa_core *c, pa_module*m) {
}
if (port & 1)
pa_log_warn("WARNING: port number not even as suggested in RFC3550!");
pa_log_warn("Port number not even as suggested in RFC3550!");
dest = pa_modargs_get_value(ma, "destination", DEFAULT_DESTINATION);
@ -238,7 +249,7 @@ int pa__init(pa_core *c, pa_module*m) {
sap_sa4 = sa4;
sap_sa4.sin_port = htons(SAP_PORT);
} else {
pa_log("invalid destination '%s'", dest);
pa_log("Invalid destination '%s'", dest);
goto fail;
}
@ -268,6 +279,12 @@ int pa__init(pa_core *c, pa_module*m) {
goto fail;
}
/* If the socket queue is full, let's drop packets */
pa_make_fd_nonblock(fd);
pa_make_udp_socket_low_delay(fd);
pa_make_fd_cloexec(fd);
pa_make_fd_cloexec(sap_fd);
pa_source_output_new_data_init(&data);
data.name = "RTP Monitor Stream";
data.driver = __FILE__;
@ -276,21 +293,20 @@ int pa__init(pa_core *c, pa_module*m) {
pa_source_output_new_data_set_sample_spec(&data, &ss);
pa_source_output_new_data_set_channel_map(&data, &cm);
if (!(o = pa_source_output_new(c, &data, 0))) {
if (!(o = pa_source_output_new(m->core, &data, 0))) {
pa_log("failed to create source output.");
goto fail;
}
o->parent.process_msg = source_output_process_msg;
o->push = source_output_push;
o->kill = source_output_kill;
o->get_latency = source_output_get_latency;
u = pa_xnew(struct userdata, 1);
m->userdata = u;
o->userdata = u;
u->module = m;
u->core = c;
u->source_output = o;
u->memblockq = pa_memblockq_new(
@ -305,8 +321,7 @@ int pa__init(pa_core *c, pa_module*m) {
u->mtu = mtu;
k = sizeof(sa_dst);
r = getsockname(fd, (struct sockaddr*) &sa_dst, &k);
assert(r >= 0);
pa_assert_se((r = getsockname(fd, (struct sockaddr*) &sa_dst, &k)) >= 0);
n = pa_sprintf_malloc("PulseAudio RTP Stream on %s", pa_get_fqdn(hn, sizeof(hn)));
@ -317,17 +332,19 @@ int pa__init(pa_core *c, pa_module*m) {
pa_xfree(n);
pa_rtp_context_init_send(&u->rtp_context, fd, c->cookie, payload, pa_frame_size(&ss));
pa_rtp_context_init_send(&u->rtp_context, fd, m->core->cookie, payload, pa_frame_size(&ss));
pa_sap_context_init_send(&u->sap_context, sap_fd, p);
pa_log_info("RTP stream initialized with mtu %u on %s:%u, SSRC=0x%08x, payload=%u, initial sequence #%u", mtu, dest, port, u->rtp_context.ssrc, payload, u->rtp_context.sequence);
pa_log_info("SDP-Data:\n%s\n"__FILE__": EOF", p);
pa_log_info("SDP-Data:\n%s\nEOF", p);
pa_sap_send(&u->sap_context, 0);
pa_gettimeofday(&tv);
pa_timeval_add(&tv, SAP_INTERVAL);
u->sap_event = c->mainloop->time_new(c->mainloop, &tv, sap_event_cb, u);
pa_timeval_add(&tv, SAP_INTERVAL * PA_USEC_PER_SEC);
u->sap_event = m->core->mainloop->time_new(m->core->mainloop, &tv, sap_event_cb, u);
pa_source_output_put(u->source_output);
pa_modargs_free(ma);
@ -338,31 +355,31 @@ fail:
pa_modargs_free(ma);
if (fd >= 0)
close(fd);
pa_close(fd);
if (sap_fd >= 0)
close(sap_fd);
pa_close(sap_fd);
if (o) {
pa_source_output_disconnect(o);
pa_source_output_unlink(o);
pa_source_output_unref(o);
}
return -1;
}
void pa__done(pa_core *c, pa_module*m) {
void pa__done(pa_module*m) {
struct userdata *u;
assert(c);
assert(m);
pa_assert(m);
if (!(u = m->userdata))
return;
c->mainloop->time_free(u->sap_event);
if (u->sap_event)
m->core->mainloop->time_free(u->sap_event);
if (u->source_output) {
pa_source_output_disconnect(u->source_output);
pa_source_output_unlink(u->source_output);
pa_source_output_unref(u->source_output);
}
@ -371,7 +388,8 @@ void pa__done(pa_core *c, pa_module*m) {
pa_sap_send(&u->sap_context, 1);
pa_sap_context_destroy(&u->sap_context);
pa_memblockq_free(u->memblockq);
if (u->memblockq)
pa_memblockq_free(u->memblockq);
pa_xfree(u);
}

View file

@ -25,7 +25,6 @@
#include <config.h>
#endif
#include <assert.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
@ -40,12 +39,14 @@
#include <pulsecore/core-error.h>
#include <pulsecore/log.h>
#include <pulsecore/macro.h>
#include <pulsecore/core-util.h>
#include "rtp.h"
pa_rtp_context* pa_rtp_context_init_send(pa_rtp_context *c, int fd, uint32_t ssrc, uint8_t payload, size_t frame_size) {
assert(c);
assert(fd >= 0);
pa_assert(c);
pa_assert(fd >= 0);
c->fd = fd;
c->sequence = (uint16_t) (rand()*rand());
@ -63,11 +64,11 @@ int pa_rtp_send(pa_rtp_context *c, size_t size, pa_memblockq *q) {
struct iovec iov[MAX_IOVECS];
pa_memblock* mb[MAX_IOVECS];
int iov_idx = 1;
size_t n = 0, skip = 0;
size_t n = 0;
assert(c);
assert(size > 0);
assert(q);
pa_assert(c);
pa_assert(size > 0);
pa_assert(q);
if (pa_memblockq_get_length(q) < size)
return 0;
@ -76,24 +77,26 @@ int pa_rtp_send(pa_rtp_context *c, size_t size, pa_memblockq *q) {
int r;
pa_memchunk chunk;
pa_memchunk_reset(&chunk);
if ((r = pa_memblockq_peek(q, &chunk)) >= 0) {
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_len = k;
mb[iov_idx] = chunk.memblock;
iov_idx ++;
pa_assert(chunk.memblock);
n += k;
}
iov[iov_idx].iov_base = ((uint8_t*) pa_memblock_acquire(chunk.memblock) + chunk.index);
iov[iov_idx].iov_len = k;
mb[iov_idx] = chunk.memblock;
iov_idx ++;
skip += k;
pa_memblockq_drop(q, &chunk, k);
n += k;
pa_memblockq_drop(q, k);
}
if (r < 0 || !chunk.memblock || n >= size || iov_idx >= MAX_IOVECS) {
pa_assert(n % c->frame_size == 0);
if (r < 0 || n >= size || iov_idx >= MAX_IOVECS) {
uint32_t header[3];
struct msghdr m;
int k, i;
@ -116,17 +119,19 @@ 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
k = 0;
c->timestamp += skip/c->frame_size;
c->timestamp += n/c->frame_size;
if (k < 0) {
if (errno != EAGAIN) /* If the queue is full, just ignore it */
if (errno != EAGAIN && errno != EINTR) /* If the queue is full, just ignore it */
pa_log("sendmsg() failed: %s", pa_cstrerror(errno));
return -1;
}
@ -135,7 +140,6 @@ int pa_rtp_send(pa_rtp_context *c, size_t size, pa_memblockq *q) {
break;
n = 0;
skip = 0;
iov_idx = 1;
}
}
@ -144,7 +148,7 @@ int pa_rtp_send(pa_rtp_context *c, size_t size, pa_memblockq *q) {
}
pa_rtp_context* pa_rtp_context_init_recv(pa_rtp_context *c, int fd, size_t frame_size) {
assert(c);
pa_assert(c);
c->fd = fd;
c->frame_size = frame_size;
@ -159,13 +163,13 @@ int pa_rtp_recv(pa_rtp_context *c, pa_memchunk *chunk, pa_mempool *pool) {
int cc;
ssize_t r;
assert(c);
assert(chunk);
pa_assert(c);
pa_assert(chunk);
chunk->memblock = NULL;
pa_memchunk_reset(chunk);
if (ioctl(c->fd, FIONREAD, &size) < 0) {
pa_log("FIONREAD failed: %s", pa_cstrerror(errno));
pa_log_warn("FIONREAD failed: %s", pa_cstrerror(errno));
goto fail;
}
@ -174,7 +178,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;
@ -185,36 +189,41 @@ int pa_rtp_recv(pa_rtp_context *c, pa_memchunk *chunk, pa_mempool *pool) {
m.msg_controllen = 0;
m.msg_flags = 0;
if ((r = recvmsg(c->fd, &m, 0)) != size) {
pa_log("recvmsg() failed: %s", r < 0 ? pa_cstrerror(errno) : "size mismatch");
r = recvmsg(c->fd, &m, 0);
pa_memblock_release(chunk->memblock);
if (r != size) {
if (r < 0 && errno != EAGAIN && errno != EINTR)
pa_log_warn("recvmsg() failed: %s", r < 0 ? pa_cstrerror(errno) : "size mismatch");
goto fail;
}
if (size < 12) {
pa_log("RTP packet too short.");
pa_log_warn("RTP packet too short.");
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);
c->ssrc = ntohl(c->ssrc);
if ((header >> 30) != 2) {
pa_log("Unsupported RTP version.");
pa_log_warn("Unsupported RTP version.");
goto fail;
}
if ((header >> 29) & 1) {
pa_log("RTP padding not supported.");
pa_log_warn("RTP padding not supported.");
goto fail;
}
if ((header >> 28) & 1) {
pa_log("RTP header extensions not supported.");
pa_log_warn("RTP header extensions not supported.");
goto fail;
}
@ -223,7 +232,7 @@ int pa_rtp_recv(pa_rtp_context *c, pa_memchunk *chunk, pa_mempool *pool) {
c->sequence = header & 0xFFFF;
if (12 + cc*4 > size) {
pa_log("RTP packet too short. (CSRC)");
pa_log_warn("RTP packet too short. (CSRC)");
goto fail;
}
@ -231,7 +240,7 @@ int pa_rtp_recv(pa_rtp_context *c, pa_memchunk *chunk, pa_mempool *pool) {
chunk->length = size - chunk->index;
if (chunk->length % c->frame_size != 0) {
pa_log("Vad RTP packet size.");
pa_log_warn("Bad RTP packet size.");
goto fail;
}
@ -245,7 +254,7 @@ fail:
}
uint8_t pa_rtp_payload_from_sample_spec(const pa_sample_spec *ss) {
assert(ss);
pa_assert(ss);
if (ss->format == PA_SAMPLE_ULAW && ss->rate == 8000 && ss->channels == 1)
return 0;
@ -260,7 +269,7 @@ uint8_t pa_rtp_payload_from_sample_spec(const pa_sample_spec *ss) {
}
pa_sample_spec *pa_rtp_sample_spec_from_payload(uint8_t payload, pa_sample_spec *ss) {
assert(ss);
pa_assert(ss);
switch (payload) {
case 0:
@ -295,17 +304,17 @@ pa_sample_spec *pa_rtp_sample_spec_from_payload(uint8_t payload, pa_sample_spec
}
pa_sample_spec *pa_rtp_sample_spec_fixup(pa_sample_spec * ss) {
assert(ss);
pa_assert(ss);
if (!pa_rtp_sample_spec_valid(ss))
ss->format = PA_SAMPLE_S16BE;
assert(pa_rtp_sample_spec_valid(ss));
pa_assert(pa_rtp_sample_spec_valid(ss));
return ss;
}
int pa_rtp_sample_spec_valid(const pa_sample_spec *ss) {
assert(ss);
pa_assert(ss);
if (!pa_sample_spec_valid(ss))
return 0;
@ -318,9 +327,9 @@ int pa_rtp_sample_spec_valid(const pa_sample_spec *ss) {
}
void pa_rtp_context_destroy(pa_rtp_context *c) {
assert(c);
pa_assert(c);
close(c->fd);
pa_close(c->fd);
}
const char* pa_rtp_format_to_string(pa_sample_format_t f) {
@ -339,7 +348,7 @@ const char* pa_rtp_format_to_string(pa_sample_format_t f) {
}
pa_sample_format_t pa_rtp_string_to_format(const char *s) {
assert(s);
pa_assert(s);
if (!(strcmp(s, "L16")))
return PA_SAMPLE_S16BE;

View file

@ -25,7 +25,6 @@
#include <config.h>
#endif
#include <assert.h>
#include <time.h>
#include <stdlib.h>
#include <sys/types.h>
@ -46,6 +45,7 @@
#include <pulsecore/core-error.h>
#include <pulsecore/core-util.h>
#include <pulsecore/log.h>
#include <pulsecore/macro.h>
#include "sap.h"
#include "sdp.h"
@ -53,9 +53,9 @@
#define MIME_TYPE "application/sdp"
pa_sap_context* pa_sap_context_init_send(pa_sap_context *c, int fd, char *sdp_data) {
assert(c);
assert(fd >= 0);
assert(sdp_data);
pa_assert(c);
pa_assert(fd >= 0);
pa_assert(sdp_data);
c->fd = fd;
c->sdp_data = sdp_data;
@ -65,9 +65,9 @@ pa_sap_context* pa_sap_context_init_send(pa_sap_context *c, int fd, char *sdp_da
}
void pa_sap_context_destroy(pa_sap_context *c) {
assert(c);
pa_assert(c);
close(c->fd);
pa_close(c->fd);
pa_xfree(c->sdp_data);
}
@ -85,7 +85,7 @@ int pa_sap_send(pa_sap_context *c, int goodbye) {
return -1;
}
assert(sa->sa_family == AF_INET || sa->sa_family == AF_INET6);
pa_assert(sa->sa_family == AF_INET || sa->sa_family == AF_INET6);
header = htonl(((uint32_t) 1 << 29) |
(sa->sa_family == AF_INET6 ? (uint32_t) 1 << 28 : 0) |
@ -113,14 +113,14 @@ int pa_sap_send(pa_sap_context *c, int goodbye) {
m.msg_flags = 0;
if ((k = sendmsg(c->fd, &m, MSG_DONTWAIT)) < 0)
pa_log("sendmsg() failed: %s\n", pa_cstrerror(errno));
pa_log_warn("sendmsg() failed: %s\n", pa_cstrerror(errno));
return k;
}
pa_sap_context* pa_sap_context_init_recv(pa_sap_context *c, int fd) {
assert(c);
assert(fd >= 0);
pa_assert(c);
pa_assert(fd >= 0);
c->fd = fd;
c->sdp_data = NULL;
@ -136,11 +136,11 @@ int pa_sap_recv(pa_sap_context *c, int *goodbye) {
int six, ac;
ssize_t r;
assert(c);
assert(goodbye);
pa_assert(c);
pa_assert(goodbye);
if (ioctl(c->fd, FIONREAD, &size) < 0) {
pa_log("FIONREAD failed: %s", pa_cstrerror(errno));
pa_log_warn("FIONREAD failed: %s", pa_cstrerror(errno));
goto fail;
}
@ -159,12 +159,12 @@ int pa_sap_recv(pa_sap_context *c, int *goodbye) {
m.msg_flags = 0;
if ((r = recvmsg(c->fd, &m, 0)) != size) {
pa_log("recvmsg() failed: %s", r < 0 ? pa_cstrerror(errno) : "size mismatch");
pa_log_warn("recvmsg() failed: %s", r < 0 ? pa_cstrerror(errno) : "size mismatch");
goto fail;
}
if (size < 4) {
pa_log("SAP packet too short.");
pa_log_warn("SAP packet too short.");
goto fail;
}
@ -172,17 +172,17 @@ int pa_sap_recv(pa_sap_context *c, int *goodbye) {
header = ntohl(header);
if (header >> 29 != 1) {
pa_log("Unsupported SAP version.");
pa_log_warn("Unsupported SAP version.");
goto fail;
}
if ((header >> 25) & 1) {
pa_log("Encrypted SAP not supported.");
pa_log_warn("Encrypted SAP not supported.");
goto fail;
}
if ((header >> 24) & 1) {
pa_log("Compressed SAP not supported.");
pa_log_warn("Compressed SAP not supported.");
goto fail;
}
@ -191,7 +191,7 @@ int pa_sap_recv(pa_sap_context *c, int *goodbye) {
k = 4 + (six ? 16 : 4) + ac*4;
if (size < k) {
pa_log("SAP packet too short (AD).");
pa_log_warn("SAP packet too short (AD).");
goto fail;
}
@ -202,7 +202,7 @@ int pa_sap_recv(pa_sap_context *c, int *goodbye) {
e += sizeof(MIME_TYPE);
size -= sizeof(MIME_TYPE);
} else if ((unsigned) size < sizeof(PA_SDP_HEADER)-1 || strncmp(e, PA_SDP_HEADER, sizeof(PA_SDP_HEADER)-1)) {
pa_log("Invalid SDP header.");
pa_log_warn("Invalid SDP header.");
goto fail;
}

View file

@ -25,7 +25,6 @@
#include <config.h>
#endif
#include <assert.h>
#include <time.h>
#include <stdlib.h>
#include <sys/types.h>
@ -35,36 +34,33 @@
#include <string.h>
#include <pulse/xmalloc.h>
#include <pulse/util.h>
#include <pulsecore/core-util.h>
#include <pulsecore/log.h>
#include <pulsecore/macro.h>
#include "sdp.h"
#include "rtp.h"
char *pa_sdp_build(int af, const void *src, const void *dst, const char *name, uint16_t port, uint8_t payload, const pa_sample_spec *ss) {
uint32_t ntp;
char buf_src[64], buf_dst[64];
char buf_src[64], buf_dst[64], un[64];
const char *u, *f, *a;
assert(src);
assert(dst);
assert(af == AF_INET || af == AF_INET6);
pa_assert(src);
pa_assert(dst);
pa_assert(af == AF_INET || af == AF_INET6);
f = pa_rtp_format_to_string(ss->format);
assert(f);
pa_assert_se(f = pa_rtp_format_to_string(ss->format));
if (!(u = getenv("USER")))
if (!(u = getenv("USERNAME")))
u = "-";
if (!(u = pa_get_user_name(un, sizeof(un))))
u = "-";
ntp = time(NULL) + 2208988800U;
a = inet_ntop(af, src, buf_src, sizeof(buf_src));
assert(a);
a = inet_ntop(af, dst, buf_dst, sizeof(buf_dst));
assert(a);
pa_assert_se(a = inet_ntop(af, src, buf_src, sizeof(buf_src)));
pa_assert_se(a = inet_ntop(af, dst, buf_dst, sizeof(buf_dst)));
return pa_sprintf_malloc(
PA_SDP_HEADER
@ -86,8 +82,8 @@ char *pa_sdp_build(int af, const void *src, const void *dst, const char *name, u
static pa_sample_spec *parse_sdp_sample_spec(pa_sample_spec *ss, char *c) {
unsigned rate, channels;
assert(ss);
assert(c);
pa_assert(ss);
pa_assert(c);
if (pa_startswith(c, "L16/")) {
ss->format = PA_SAMPLE_S16BE;
@ -123,8 +119,8 @@ pa_sdp_info *pa_sdp_parse(const char *t, pa_sdp_info *i, int is_goodbye) {
uint16_t port = 0;
int ss_valid = 0;
assert(t);
assert(i);
pa_assert(t);
pa_assert(i);
i->origin = i->session_name = NULL;
i->salen = 0;
@ -258,7 +254,7 @@ fail:
}
void pa_sdp_info_destroy(pa_sdp_info *i) {
assert(i);
pa_assert(i);
pa_xfree(i->origin);
pa_xfree(i->session_name);