merge glitch-free branch back into trunk

git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@2445 fefdeb5f-60dc-0310-8127-8f9354f1896f
This commit is contained in:
Lennart Poettering 2008-05-15 23:34:41 +00:00
parent 91f092eadc
commit 045c1d602d
189 changed files with 12559 additions and 4959 deletions

View file

@ -78,3 +78,49 @@ New opcodes for notifications:
PA_COMMAND_CAPTURE_STREAM_SUSPENDED
PA_COMMAND_PLAYBACK_STREAM_MOVED
PA_COMMAND_CAPTURE_STREAM_MOVED
### v13, implemented by >= 0.9.11
New fields for PA_COMMAND_CREATE_PLAYBACK_STREAM, PA_COMMAND_CREATE_RECORD_STREAM request at the end:
peak_detect (bool)
Replace field "name" for PA_COMMAND_CREATE_PLAYBACK_STREAM, PA_COMMAND_CREATE_RECORD_STREAM at the end:
proplist
Replace field "name" for PA_COMMAND_SET_CLIENT_NAME request at the end:
proplist
On response of PA_COMMAND_SET_CLIENT_NAME:
client_index
New proplist field for sink, source, sink input, source output introspection opcodes and at the end:
proplist
New opcodes for proplist modifications
PA_COMMAND_UPDATE_RECORD_STREAM_PROPLIST
PA_COMMAND_UPDATE_PLAYBACK_STREAM_PROPLIST
PA_COMMAND_UPDATE_CLIENT_PROPLIST
PA_COMMAND_REMOVE_RECORD_STREAM_PROPLIST
PA_COMMAND_REMOVE_PLAYBACK_STREAM_PROPLIST
PA_COMMAND_REMOVE_CLIENT_PROPLIST
New field for PA_COMMAND_PLAY_SAMPLE:
proplist
New field for PA_COMMAND_PLAY_SAMPLE response:
idx
New field for PA_COMMAND_CREATE_PLAYBACK_STREAM at the end:
start_muted
Buffer attributes for PA_COMMAND_CREATE_PLAYBACK_STREAM and
PA_COMMAND_CREATE_RECORD_STREAM may now be 0 for default values.

View file

@ -26,7 +26,7 @@ AC_PREREQ(2.60)
m4_define(PA_MAJOR, [0])
m4_define(PA_MINOR, [9])
m4_define(PA_MICRO, [10])
m4_define(PA_MICRO, [11])
AC_INIT([pulseaudio], PA_MAJOR.PA_MINOR.PA_MICRO,[mzchyfrnhqvb (at) 0pointer (dot) net])
AC_CONFIG_SRCDIR([src/daemon/main.c])
@ -37,7 +37,7 @@ AC_SUBST(PA_MAJORMINOR, "PA_MAJOR.PA_MINOR")
AC_SUBST(PACKAGE_URL, [http://pulseaudio.org/])
AC_SUBST(PA_API_VERSION, 11)
AC_SUBST(PA_PROTOCOL_VERSION, 12)
AC_SUBST(PA_PROTOCOL_VERSION, 13)
# The stable ABI for client applications, for the version info x:y:z
# always will hold y=z
@ -339,6 +339,7 @@ AC_CHECK_TYPES(ssize_t, , [AC_DEFINE([ssize_t], [signed long],
AC_TYPE_OFF_T
AC_TYPE_SIGNAL
AC_TYPE_UID_T
AC_CHECK_DECLS(environ)
AC_CHECK_DEFINE([SIGXCPU], [signal.h], [
HAVE_SIGXCPU=1
@ -376,6 +377,9 @@ AC_SEARCH_LIBS([connect], [socket])
# build, disabling its ability to make dlls.
AC_CHECK_FUNCS([getopt_long], [], [AC_CHECK_LIB([iberty], [getopt_long])])
AC_CHECK_LIB(gdbm, gdbm_open)
AC_CHECK_HEADERS(gdbm.h)
#### Check for functions ####
# ISO
@ -591,7 +595,7 @@ AC_ARG_ENABLE([alsa],
[alsa=auto])
if test "x${alsa}" != xno ; then
PKG_CHECK_MODULES(ASOUNDLIB, [ alsa >= 1.0.0 ],
PKG_CHECK_MODULES(ASOUNDLIB, [ alsa >= 1.0.16 ],
[
HAVE_ALSA=1
AC_DEFINE([HAVE_ALSA], 1, [Have ALSA?])

View file

@ -417,7 +417,7 @@ WARN_LOGFILE =
# directories like "/usr/src/myproject". Separate the files or directories
# with spaces.
INPUT = ../src/pulse/context.h ../src/pulse/stream.h ../src/pulse/pulseaudio.h ../src/pulse/sample.h ../src/pulse/def.h ../src/pulse/subscribe.h ../src/pulse/introspect.h ../src/pulse/scache.h ../src/pulse/mainloop-api.h ../src/pulse/glib-mainloop.h ../src/pulse/mainloop.h ../src/pulse/mainloop-signal.h ../src/pulse/error.h ../src/pulse/operation.h ../src/pulse/simple.h ../src/pulse/version.h ../src/pulse/volume.h ../src/pulse/channelmap.h ../src/pulse/thread-mainloop.h ../src/pulse/xmalloc.h ../src/pulse/utf8.h ../src/pulse/util.h ../src/pulse/timeval.h ../src/pulse/browser.h
INPUT = ../src/pulse/context.h ../src/pulse/stream.h ../src/pulse/pulseaudio.h ../src/pulse/sample.h ../src/pulse/def.h ../src/pulse/subscribe.h ../src/pulse/introspect.h ../src/pulse/scache.h ../src/pulse/mainloop-api.h ../src/pulse/glib-mainloop.h ../src/pulse/mainloop.h ../src/pulse/mainloop-signal.h ../src/pulse/error.h ../src/pulse/operation.h ../src/pulse/simple.h ../src/pulse/version.h ../src/pulse/volume.h ../src/pulse/channelmap.h ../src/pulse/thread-mainloop.h ../src/pulse/xmalloc.h ../src/pulse/utf8.h ../src/pulse/util.h ../src/pulse/timeval.h ../src/pulse/proplist.h ../src/pulse/gccmacro.h
# If the value of the INPUT tag contains directories, you can use the
# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp

View file

@ -131,8 +131,7 @@ pulseaudio_SOURCES = \
daemon/daemon-conf.c daemon/daemon-conf.h \
daemon/dumpmodules.c daemon/dumpmodules.h \
daemon/ltdl-bind-now.c daemon/ltdl-bind-now.h \
daemon/main.c \
pulsecore/gccmacro.h
daemon/main.c
pulseaudio_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS) $(LIBSAMPLERATE_CFLAGS) $(LIBSNDFILE_CFLAGS) $(CAP_CFLAGS) $(LIBOIL_CFLAGS) $(DBUS_CFLAGS)
pulseaudio_CPPFLAGS = $(AM_CPPFLAGS)
@ -226,7 +225,7 @@ pabrowse_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
# Test programs #
###################################
check_PROGRAMS = \
noinst_PROGRAMS = \
mainloop-test \
mcalign-test \
pacat-simple \
@ -255,16 +254,18 @@ check_PROGRAMS = \
mix-test \
remix-test \
envelope-test \
proplist-test
proplist-test \
rtstutter \
stripnul
if HAVE_SIGXCPU
check_PROGRAMS += \
noinst_PROGRAMS += \
cpulimit-test \
cpulimit-test2
endif
if HAVE_GLIB20
check_PROGRAMS += \
noinst_PROGRAMS += \
mainloop-test-glib
endif
@ -426,10 +427,20 @@ envelope_test_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS)
envelope_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBOIL_LIBS)
proplist_test_SOURCES = tests/proplist-test.c
proplist_test_LDADD = $(AM_LDADD) libpulse.la
proplist_test_LDADD = $(AM_LDADD) libpulsecore.la
proplist_test_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS)
proplist_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBOIL_LIBS)
rtstutter_SOURCES = tests/rtstutter.c
rtstutter_LDADD = $(AM_LDADD) libpulsecore.la
rtstutter_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS)
rtstutter_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBOIL_LIBS)
stripnul_SOURCES = tests/stripnul.c
stripnul_LDADD = $(AM_LDADD) libpulsecore.la
stripnul_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS)
stripnul_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBOIL_LIBS)
###################################
# Client library #
###################################
@ -458,7 +469,8 @@ pulseinclude_HEADERS = \
pulse/version.h \
pulse/volume.h \
pulse/xmalloc.h \
pulse/proplist.h
pulse/proplist.h \
pulse/gccmacro.h
if HAVE_AVAHI
pulseinclude_HEADERS += \
@ -517,7 +529,6 @@ libpulse_la_SOURCES += \
pulsecore/conf-parser.c pulsecore/conf-parser.h \
pulsecore/core-util.c pulsecore/core-util.h \
pulsecore/dynarray.c pulsecore/dynarray.h \
pulsecore/gccmacro.h \
pulsecore/hashmap.c pulsecore/hashmap.h \
pulsecore/idxset.c pulsecore/idxset.h \
pulsecore/inet_ntop.c pulsecore/inet_ntop.h \
@ -550,6 +561,8 @@ libpulse_la_SOURCES += \
pulsecore/object.c pulsecore/object.h \
pulsecore/msgobject.c pulsecore/msgobject.h \
pulsecore/once.c pulsecore/once.h \
pulsecore/rtclock.c pulsecore/rtclock.h \
pulsecore/time-smoother.c pulsecore/time-smoother.h \
$(PA_THREAD_OBJS)
if OS_IS_WIN32
@ -649,7 +662,6 @@ noinst_HEADERS = \
pulsecore/cli-text.h \
pulsecore/client.h \
pulsecore/core.h \
pulsecore/core-def.h \
pulsecore/core-scache.h \
pulsecore/core-subscribe.h \
pulsecore/conf-parser.h \
@ -708,7 +720,8 @@ libpulsecore_la_SOURCES = \
pulse/utf8.c pulse/utf8.h \
pulse/util.c pulse/util.h \
pulse/volume.c pulse/volume.h \
pulse/xmalloc.c pulse/xmalloc.h
pulse/xmalloc.c pulse/xmalloc.h \
pulse/proplist.c pulse/proplist.h
# Pure core stuff (some are shared in libpulse though).
libpulsecore_la_SOURCES += \
@ -998,6 +1011,7 @@ modlibexec_LTLIBRARIES += \
module-null-sink.la \
module-detect.la \
module-volume-restore.la \
module-device-restore.la \
module-default-device-restore.la \
module-rescue-streams.la \
module-suspend-on-idle.la \
@ -1168,6 +1182,7 @@ SYMDEF_FILES = \
modules/module-jack-sink-symdef.h \
modules/module-jack-source-symdef.h \
modules/module-volume-restore-symdef.h \
modules/module-device-restore-symdef.h \
modules/module-default-device-restore-symdef.h \
modules/module-rescue-streams-symdef.h \
modules/module-suspend-on-idle-symdef.h \
@ -1296,7 +1311,7 @@ module_remap_sink_la_LDFLAGS = -module -avoid-version
module_remap_sink_la_LIBADD = $(AM_LIBADD) libpulsecore.la
module_ladspa_sink_la_SOURCES = modules/module-ladspa-sink.c modules/ladspa.h
module_ladspa_sink_la_CFLAGS = -DLADSPA_PATH=\"$(libdir)/ladspa:/usr/local/lib/ladspa:/usr/lib/ladspa\" $(AM_CFLAGS)
module_ladspa_sink_la_CFLAGS = -DLADSPA_PATH=\"$(libdir)/ladspa:/usr/local/lib/ladspa:/usr/lib/ladspa:/usr/local/lib64/ladspa:/usr/lib64/ladspa\" $(AM_CFLAGS)
module_ladspa_sink_la_LDFLAGS = -module -avoid-version
module_ladspa_sink_la_LIBADD = $(AM_LIBADD) $(LIBLTDL) libpulsecore.la
@ -1408,6 +1423,12 @@ module_volume_restore_la_LDFLAGS = -module -avoid-version
module_volume_restore_la_LIBADD = $(AM_LIBADD) libpulsecore.la
module_volume_restore_la_CFLAGS = $(AM_CFLAGS)
# Device volume restore module
module_device_restore_la_SOURCES = modules/module-device-restore.c
module_device_restore_la_LDFLAGS = -module -avoid-version
module_device_restore_la_LIBADD = $(AM_LIBADD) libpulsecore.la -lgdbm
module_device_restore_la_CFLAGS = $(AM_CFLAGS)
# Default sink/source restore module
module_default_device_restore_la_SOURCES = modules/module-default-device-restore.c
module_default_device_restore_la_LDFLAGS = -module -avoid-version

View file

@ -85,31 +85,21 @@ void pa_drop_root(void) {
#if defined(HAVE_SYS_CAPABILITY_H) && defined(HAVE_SYS_PRCTL_H)
/* Limit permitted capabilities set to CAPSYS_NICE */
int pa_limit_caps(void) {
int r = -1;
void pa_limit_caps(void) {
cap_t caps;
cap_value_t nice_cap = CAP_SYS_NICE;
pa_assert_se(caps = cap_init());
pa_assert_se(cap_clear(caps) == 0);
pa_assert_se(cap_set_flag(caps, CAP_EFFECTIVE, 1, &nice_cap, CAP_SET) == 0);
pa_assert_se(cap_set_flag(caps, CAP_PERMITTED, 1, &nice_cap, CAP_SET) == 0);
pa_assert_se(cap_set_proc(caps) == 0);
cap_clear(caps);
cap_set_flag(caps, CAP_EFFECTIVE, 1, &nice_cap, CAP_SET);
cap_set_flag(caps, CAP_PERMITTED, 1, &nice_cap, CAP_SET);
if (cap_set_proc(caps) < 0)
goto fail;
if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) < 0)
goto fail;
pa_assert_se(prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) == 0);
pa_log_info("Dropped capabilities successfully.");
r = 1;
fail:
cap_free(caps);
return r;
pa_assert_se(cap_free(caps) == 0);
}
/* Drop all capabilities, effectively becoming a normal user */
@ -119,9 +109,9 @@ void pa_drop_caps(void) {
pa_assert_se(prctl(PR_SET_KEEPCAPS, 0, 0, 0, 0) == 0);
pa_assert_se(caps = cap_init());
cap_clear(caps);
pa_assert_se(cap_clear(caps) == 0);
pa_assert_se(cap_set_proc(caps) == 0);
cap_free(caps);
pa_assert_se(cap_free(caps) == 0);
}
#else

View file

@ -26,6 +26,6 @@
void pa_drop_root(void);
void pa_drop_caps(void);
int pa_limit_caps(void);
void pa_limit_caps(void);
#endif

View file

@ -293,8 +293,7 @@ int pa_cmdline_parse(pa_daemon_conf *conf, int argc, char *const argv [], int *d
break;
case 'n':
pa_xfree(conf->default_script_file);
conf->default_script_file = NULL;
conf->load_default_script_file = FALSE;
break;
case ARG_LOG_TARGET:

View file

@ -82,7 +82,7 @@ static pa_io_event *io_event = NULL;
static struct sigaction sigaction_prev;
/* Nonzero after pa_cpu_limit_init() */
static int installed = 0;
static pa_bool_t installed = FALSE;
/* The current state of operation */
static enum {
@ -210,7 +210,7 @@ int pa_cpu_limit_init(pa_mainloop_api *m) {
return -1;
}
installed = 1;
installed = TRUE;
reset_cpu_time(CPUTIME_INTERVAL_SOFT);
@ -231,7 +231,7 @@ void pa_cpu_limit_done(void) {
if (installed) {
pa_assert_se(sigaction(SIGXCPU, &sigaction_prev, NULL) >= 0);
installed = 0;
installed = FALSE;
}
}

View file

@ -33,6 +33,7 @@
#include <sched.h>
#include <pulse/xmalloc.h>
#include <pulse/timeval.h>
#include <pulsecore/core-error.h>
#include <pulsecore/core-util.h>
@ -45,6 +46,8 @@
#define DEFAULT_SCRIPT_FILE PA_DEFAULT_CONFIG_DIR PA_PATH_SEP "default.pa"
#define DEFAULT_SCRIPT_FILE_USER PA_PATH_SEP "default.pa"
#define DEFAULT_SYSTEM_SCRIPT_FILE PA_DEFAULT_CONFIG_DIR PA_PATH_SEP "system.pa"
#define DEFAULT_CONFIG_FILE PA_DEFAULT_CONFIG_DIR PA_PATH_SEP "daemon.conf"
#define DEFAULT_CONFIG_FILE_USER PA_PATH_SEP "daemon.conf"
@ -67,6 +70,7 @@ static const pa_daemon_conf default_conf = {
.auto_log_target = 1,
.script_commands = NULL,
.dl_search_path = NULL,
.load_default_script_file = TRUE,
.default_script_file = NULL,
.log_target = PA_LOG_SYSLOG,
.log_level = PA_LOG_NOTICE,
@ -81,34 +85,43 @@ static const pa_daemon_conf default_conf = {
.default_fragment_size_msec = 25,
.default_sample_spec = { .format = PA_SAMPLE_S16NE, .rate = 44100, .channels = 2 }
#ifdef HAVE_SYS_RESOURCE_H
, .rlimit_as = { .value = 0, .is_set = FALSE },
.rlimit_core = { .value = 0, .is_set = FALSE },
,.rlimit_fsize = { .value = 0, .is_set = FALSE },
.rlimit_data = { .value = 0, .is_set = FALSE },
.rlimit_fsize = { .value = 0, .is_set = FALSE },
.rlimit_nofile = { .value = 256, .is_set = TRUE },
.rlimit_stack = { .value = 0, .is_set = FALSE }
.rlimit_stack = { .value = 0, .is_set = FALSE },
.rlimit_core = { .value = 0, .is_set = FALSE },
.rlimit_rss = { .value = 0, .is_set = FALSE }
#ifdef RLIMIT_NPROC
, .rlimit_nproc = { .value = 0, .is_set = FALSE }
,.rlimit_nproc = { .value = 0, .is_set = FALSE }
#endif
,.rlimit_nofile = { .value = 256, .is_set = TRUE }
#ifdef RLIMIT_MEMLOCK
, .rlimit_memlock = { .value = 0, .is_set = FALSE }
,.rlimit_memlock = { .value = 0, .is_set = FALSE }
#endif
,.rlimit_as = { .value = 0, .is_set = FALSE }
#ifdef RLIMIT_LOCKS
,.rlimit_locks = { .value = 0, .is_set = FALSE }
#endif
#ifdef RLIMIT_SIGPENDING
,.rlimit_sigpending = { .value = 0, .is_set = FALSE }
#endif
#ifdef RLIMIT_MSGQUEUE
,.rlimit_msgqueue = { .value = 0, .is_set = FALSE }
#endif
#ifdef RLIMIT_NICE
, .rlimit_nice = { .value = 31, .is_set = TRUE } /* nice level of -11 */
,.rlimit_nice = { .value = 31, .is_set = TRUE } /* nice level of -11 */
#endif
#ifdef RLIMIT_RTPRIO
, .rlimit_rtprio = { .value = 9, .is_set = TRUE } /* One below JACK's default for the server */
,.rlimit_rtprio = { .value = 9, .is_set = TRUE } /* One below JACK's default for the server */
#endif
#ifdef RLIMIT_RTTIME
,.rlimit_rttime = { .value = PA_USEC_PER_SEC, .is_set = TRUE }
#endif
#endif
};
pa_daemon_conf* pa_daemon_conf_new(void) {
FILE *f;
pa_daemon_conf *c = pa_xnewdup(pa_daemon_conf, &default_conf, 1);
if ((f = pa_open_config_file(DEFAULT_SCRIPT_FILE, DEFAULT_SCRIPT_FILE_USER, ENV_SCRIPT_FILE, &c->default_script_file, "r")))
fclose(f);
c->dl_search_path = pa_xstrdup(PA_DLSEARCHPATH);
return c;
}
@ -412,25 +425,39 @@ int pa_daemon_conf_load(pa_daemon_conf *c, const char *filename) {
{ "default-fragment-size-msec", parse_fragment_size_msec, NULL },
{ "nice-level", parse_nice_level, NULL },
{ "disable-remixing", pa_config_parse_bool, NULL },
{ "load-default-script-file", pa_config_parse_bool, NULL },
#ifdef HAVE_SYS_RESOURCE_H
{ "rlimit-as", parse_rlimit, NULL },
{ "rlimit-core", parse_rlimit, NULL },
{ "rlimit-data", parse_rlimit, NULL },
{ "rlimit-fsize", parse_rlimit, NULL },
{ "rlimit-nofile", parse_rlimit, NULL },
{ "rlimit-data", parse_rlimit, NULL },
{ "rlimit-stack", parse_rlimit, NULL },
{ "rlimit-core", parse_rlimit, NULL },
{ "rlimit-rss", parse_rlimit, NULL },
{ "rlimit-nofile", parse_rlimit, NULL },
{ "rlimit-as", parse_rlimit, NULL },
#ifdef RLIMIT_NPROC
{ "rlimit-nproc", parse_rlimit, NULL },
#endif
#ifdef RLIMIT_MEMLOCK
{ "rlimit-memlock", parse_rlimit, NULL },
#endif
#ifdef RLIMIT_LOCKS
{ "rlimit-locks", parse_rlimit, NULL },
#endif
#ifdef RLIMIT_SIGPENDING
{ "rlimit-sigpending", parse_rlimit, NULL },
#endif
#ifdef RLIMIT_MSGQUEUE
{ "rlimit-msgqueue", parse_rlimit, NULL },
#endif
#ifdef RLIMIT_NICE
{ "rlimit-nice", parse_rlimit, NULL },
#endif
#ifdef RLIMIT_RTPRIO
{ "rlimit-rtprio", parse_rlimit, NULL },
#endif
#ifdef RLIMIT_RTTIME
{ "rlimit-rttime", parse_rlimit, NULL },
#endif
#endif
{ NULL, NULL, NULL },
};
@ -461,33 +488,66 @@ int pa_daemon_conf_load(pa_daemon_conf *c, const char *filename) {
table[23].data = c;
table[24].data = c;
table[25].data = &c->disable_remixing;
table[26].data = &c->load_default_script_file;
#ifdef HAVE_SYS_RESOURCE_H
table[26].data = &c->rlimit_as;
table[27].data = &c->rlimit_core;
table[27].data = &c->rlimit_fsize;
table[28].data = &c->rlimit_data;
table[29].data = &c->rlimit_fsize;
table[30].data = &c->rlimit_nofile;
table[31].data = &c->rlimit_stack;
table[29].data = &c->rlimit_stack;
table[30].data = &c->rlimit_as;
table[31].data = &c->rlimit_core;
table[32].data = &c->rlimit_nofile;
table[33].data = &c->rlimit_as;
#ifdef RLIMIT_NPROC
table[32].data = &c->rlimit_nproc;
table[34].data = &c->rlimit_nproc;
#endif
#ifdef RLIMIT_MEMLOCK
#ifndef RLIMIT_NPROC
#error "Houston, we have a numbering problem!"
#endif
table[33].data = &c->rlimit_memlock;
table[35].data = &c->rlimit_memlock;
#endif
#ifdef RLIMIT_NICE
#ifdef RLIMIT_LOCKS
#ifndef RLIMIT_MEMLOCK
#error "Houston, we have a numbering problem!"
#endif
table[34].data = &c->rlimit_nice;
table[36].data = &c->rlimit_locks;
#endif
#ifdef RLIMIT_SIGPENDING
#ifndef RLIMIT_LOCKS
#error "Houston, we have a numbering problem!"
#endif
table[37].data = &c->rlimit_sigpending;
#endif
#ifdef RLIMIT_MSGQUEUE
#ifndef RLIMIT_SIGPENDING
#error "Houston, we have a numbering problem!"
#endif
table[38].data = &c->rlimit_msgqueue;
#endif
#ifdef RLIMIT_NICE
#ifndef RLIMIT_MSGQUEUE
#error "Houston, we have a numbering problem!"
#endif
table[39].data = &c->rlimit_nice;
#endif
#ifdef RLIMIT_RTPRIO
#ifndef RLIMIT_NICE
#error "Houston, we have a numbering problem!"
#endif
table[35].data = &c->rlimit_rtprio;
table[40].data = &c->rlimit_rtprio;
#endif
#ifdef RLIMIT_RTTIME
#ifndef RLIMIT_RTTIME
#error "Houston, we have a numbering problem!"
#endif
table[41].data = &c->rlimit_rttime;
#endif
#endif
@ -496,10 +556,10 @@ int pa_daemon_conf_load(pa_daemon_conf *c, const char *filename) {
f = filename ?
fopen(c->config_file = pa_xstrdup(filename), "r") :
pa_open_config_file(DEFAULT_CONFIG_FILE, DEFAULT_CONFIG_FILE_USER, ENV_CONFIG_FILE, &c->config_file, "r");
pa_open_config_file(DEFAULT_CONFIG_FILE, DEFAULT_CONFIG_FILE_USER, ENV_CONFIG_FILE, &c->config_file);
if (!f && errno != ENOENT) {
pa_log_warn("Failed to open configuration file '%s': %s", c->config_file, pa_cstrerror(errno));
pa_log_warn("Failed to open configuration file: %s", pa_cstrerror(errno));
goto finish;
}
@ -514,6 +574,7 @@ finish:
int pa_daemon_conf_env(pa_daemon_conf *c) {
char *e;
pa_assert(c);
if ((e = getenv(ENV_DL_SEARCH_PATH))) {
pa_xfree(c->dl_search_path);
@ -527,6 +588,35 @@ int pa_daemon_conf_env(pa_daemon_conf *c) {
return 0;
}
const char *pa_daemon_conf_get_default_script_file(pa_daemon_conf *c) {
pa_assert(c);
if (!c->default_script_file) {
if (c->system_instance)
c->default_script_file = pa_find_config_file(DEFAULT_SYSTEM_SCRIPT_FILE, NULL, ENV_SCRIPT_FILE);
else
c->default_script_file = pa_find_config_file(DEFAULT_SCRIPT_FILE, DEFAULT_SCRIPT_FILE_USER, ENV_SCRIPT_FILE);
}
return c->default_script_file;
}
FILE *pa_daemon_conf_open_default_script_file(pa_daemon_conf *c) {
FILE *f;
pa_assert(c);
if (!c->default_script_file) {
if (c->system_instance)
f = pa_open_config_file(DEFAULT_SYSTEM_SCRIPT_FILE, NULL, ENV_SCRIPT_FILE, &c->default_script_file);
else
f = pa_open_config_file(DEFAULT_SCRIPT_FILE, DEFAULT_SCRIPT_FILE_USER, ENV_SCRIPT_FILE, &c->default_script_file);
} else
f = fopen(c->default_script_file, "r");
return f;
}
static const char* const log_level_to_string[] = {
[PA_LOG_DEBUG] = "debug",
[PA_LOG_INFO] = "info",
@ -561,8 +651,9 @@ char *pa_daemon_conf_dump(pa_daemon_conf *c) {
pa_strbuf_printf(s, "exit-idle-time = %i\n", c->exit_idle_time);
pa_strbuf_printf(s, "module-idle-time = %i\n", c->module_idle_time);
pa_strbuf_printf(s, "scache-idle-time = %i\n", c->scache_idle_time);
pa_strbuf_printf(s, "dl-search-path = %s\n", c->dl_search_path ? c->dl_search_path : "");
pa_strbuf_printf(s, "default-script-file = %s\n", c->default_script_file);
pa_strbuf_printf(s, "dl-search-path = %s\n", pa_strempty(c->dl_search_path));
pa_strbuf_printf(s, "default-script-file = %s\n", pa_strempty(pa_daemon_conf_get_default_script_file(c)));
pa_strbuf_printf(s, "load-default-script-file = %s\n", pa_yes_no(c->load_default_script_file));
pa_strbuf_printf(s, "log-target = %s\n", c->auto_log_target ? "auto" : (c->log_target == PA_LOG_SYSLOG ? "syslog" : "stderr"));
pa_strbuf_printf(s, "log-level = %s\n", log_level_to_string[c->log_level]);
pa_strbuf_printf(s, "resample-method = %s\n", pa_resample_method_to_string(c->resample_method));
@ -573,23 +664,36 @@ char *pa_daemon_conf_dump(pa_daemon_conf *c) {
pa_strbuf_printf(s, "default-fragments = %u\n", c->default_n_fragments);
pa_strbuf_printf(s, "default-fragment-size-msec = %u\n", c->default_fragment_size_msec);
#ifdef HAVE_SYS_RESOURCE_H
pa_strbuf_printf(s, "rlimit-as = %li\n", c->rlimit_as.is_set ? (long int) c->rlimit_as.value : -1);
pa_strbuf_printf(s, "rlimit-core = %li\n", c->rlimit_core.is_set ? (long int) c->rlimit_core.value : -1);
pa_strbuf_printf(s, "rlimit-data = %li\n", c->rlimit_data.is_set ? (long int) c->rlimit_data.value : -1);
pa_strbuf_printf(s, "rlimit-fsize = %li\n", c->rlimit_fsize.is_set ? (long int) c->rlimit_fsize.value : -1);
pa_strbuf_printf(s, "rlimit-nofile = %li\n", c->rlimit_nofile.is_set ? (long int) c->rlimit_nofile.value : -1);
pa_strbuf_printf(s, "rlimit-data = %li\n", c->rlimit_data.is_set ? (long int) c->rlimit_data.value : -1);
pa_strbuf_printf(s, "rlimit-stack = %li\n", c->rlimit_stack.is_set ? (long int) c->rlimit_stack.value : -1);
pa_strbuf_printf(s, "rlimit-core = %li\n", c->rlimit_core.is_set ? (long int) c->rlimit_core.value : -1);
pa_strbuf_printf(s, "rlimit-as = %li\n", c->rlimit_as.is_set ? (long int) c->rlimit_as.value : -1);
pa_strbuf_printf(s, "rlimit-rss = %li\n", c->rlimit_rss.is_set ? (long int) c->rlimit_rss.value : -1);
#ifdef RLIMIT_NPROC
pa_strbuf_printf(s, "rlimit-nproc = %li\n", c->rlimit_nproc.is_set ? (long int) c->rlimit_nproc.value : -1);
#endif
pa_strbuf_printf(s, "rlimit-nofile = %li\n", c->rlimit_nofile.is_set ? (long int) c->rlimit_nofile.value : -1);
#ifdef RLIMIT_MEMLOCK
pa_strbuf_printf(s, "rlimit-memlock = %li\n", c->rlimit_memlock.is_set ? (long int) c->rlimit_memlock.value : -1);
#endif
#ifdef RLIMIT_LOCKS
pa_strbuf_printf(s, "rlimit-locks = %li\n", c->rlimit_locks.is_set ? (long int) c->rlimit_locks.value : -1);
#endif
#ifdef RLIMIT_SIGPENDING
pa_strbuf_printf(s, "rlimit-sigpending = %li\n", c->rlimit_sigpending.is_set ? (long int) c->rlimit_sigpending.value : -1);
#endif
#ifdef RLIMIT_MSGQUEUE
pa_strbuf_printf(s, "rlimit-msgqueue = %li\n", c->rlimit_msgqueue.is_set ? (long int) c->rlimit_msgqueue.value : -1);
#endif
#ifdef RLIMIT_NICE
pa_strbuf_printf(s, "rlimit-nice = %li\n", c->rlimit_memlock.is_set ? (long int) c->rlimit_nice.value : -1);
pa_strbuf_printf(s, "rlimit-nice = %li\n", c->rlimit_nice.is_set ? (long int) c->rlimit_nice.value : -1);
#endif
#ifdef RLIMIT_RTPRIO
pa_strbuf_printf(s, "rlimit-rtprio = %li\n", c->rlimit_memlock.is_set ? (long int) c->rlimit_rtprio.value : -1);
pa_strbuf_printf(s, "rlimit-rtprio = %li\n", c->rlimit_rtprio.is_set ? (long int) c->rlimit_rtprio.value : -1);
#endif
#ifdef RLIMIT_RTTIME
pa_strbuf_printf(s, "rlimit-rttime = %li\n", c->rlimit_rttime.is_set ? (long int) c->rlimit_rttime.value : -1);
#endif
#endif

View file

@ -27,6 +27,7 @@
#include <pulsecore/log.h>
#include <pulsecore/macro.h>
#include <pulsecore/core-util.h>
#include <pulse/sample.h>
#ifdef HAVE_SYS_RESOURCE_H
@ -65,7 +66,8 @@ typedef struct pa_daemon_conf {
system_instance,
no_cpu_limit,
disable_shm,
disable_remixing;
disable_remixing,
load_default_script_file;
int exit_idle_time,
module_idle_time,
scache_idle_time,
@ -79,19 +81,31 @@ typedef struct pa_daemon_conf {
char *config_file;
#ifdef HAVE_SYS_RESOURCE_H
pa_rlimit rlimit_as, rlimit_core, rlimit_data, rlimit_fsize, rlimit_nofile, rlimit_stack;
pa_rlimit rlimit_fsize, rlimit_data, rlimit_stack, rlimit_core, rlimit_rss, rlimit_nofile, rlimit_as;
#ifdef RLIMIT_NPROC
pa_rlimit rlimit_nproc;
#endif
#ifdef RLIMIT_MEMLOCK
pa_rlimit rlimit_memlock;
#endif
#ifdef RLIMIT_LOCKS
pa_rlimit rlimit_locks;
#endif
#ifdef RLIMIT_SIGPENDING
pa_rlimit rlimit_sigpending;
#endif
#ifdef RLIMIT_MSGQUEUE
pa_rlimit rlimit_msgqueue;
#endif
#ifdef RLIMIT_NICE
pa_rlimit rlimit_nice;
#endif
#ifdef RLIMIT_RTPRIO
pa_rlimit rlimit_rtprio;
#endif
#ifdef RLIMIT_RTTIME
pa_rlimit rlimit_rttime;
#endif
#endif
unsigned default_n_fragments, default_fragment_size_msec;
@ -121,4 +135,7 @@ int pa_daemon_conf_set_log_target(pa_daemon_conf *c, const char *string);
int pa_daemon_conf_set_log_level(pa_daemon_conf *c, const char *string);
int pa_daemon_conf_set_resample_method(pa_daemon_conf *c, const char *string);
const char *pa_daemon_conf_get_default_script_file(pa_daemon_conf *c);
FILE *pa_daemon_conf_open_default_script_file(pa_daemon_conf *c);
#endif

View file

@ -40,6 +40,7 @@
; dl-search-path = (depends on architecture)
; load-defaul-script-file = yes
; default-script-file = @PA_DEFAULT_CONFIG_FILE@
; log-target = auto
@ -50,16 +51,21 @@
; no-cpu-limit = no
; rlimit-as = -1
; rlimit-core = -1
; rlimit-data = -1
; rlimit-fsize = -1
; rlimit-nofile = 256
; rlimit-data = -1
; rlimit-stack = -1
; rlimit-core = -1
; rlimit-as = -1
; rlimit-rss = -1
; rlimit-nproc = -1
; rlimit-nofile = 256
; rlimit-memlock = -1
; rlimit-locks = -1
; rlimit-sigpending = -1
; rlimit-msgqueue = -1
; rlimit-nice = 31
; rlimit-rtprio = 9
; rlimit-rtttime = 1000000
; default-sample-format = s16le
; default-sample-rate = 44100

View file

@ -115,7 +115,7 @@ static void message_cb(pa_mainloop_api*a, pa_time_event*e, PA_GCC_UNUSED const s
MSG msg;
struct timeval tvnext;
while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
if (msg.message == WM_QUIT)
raise(SIGTERM);
else {
@ -164,8 +164,6 @@ static void signal_callback(pa_mainloop_api*m, PA_GCC_UNUSED pa_signal_event *e,
}
}
#define set_env(key, value) putenv(pa_sprintf_malloc("%s=%s", (key), (value)))
#if defined(HAVE_PWD_H) && defined(HAVE_GRP_H)
static int change_user(void) {
@ -241,14 +239,14 @@ static int change_user(void) {
return -1;
}
set_env("USER", PA_SYSTEM_USER);
set_env("USERNAME", PA_SYSTEM_USER);
set_env("LOGNAME", PA_SYSTEM_USER);
set_env("HOME", PA_SYSTEM_RUNTIME_PATH);
pa_set_env("USER", PA_SYSTEM_USER);
pa_set_env("USERNAME", PA_SYSTEM_USER);
pa_set_env("LOGNAME", PA_SYSTEM_USER);
pa_set_env("HOME", PA_SYSTEM_RUNTIME_PATH);
/* Relevant for pa_runtime_path() */
set_env("PULSE_RUNTIME_PATH", PA_SYSTEM_RUNTIME_PATH);
set_env("PULSE_CONFIG_PATH", PA_SYSTEM_RUNTIME_PATH);
pa_set_env("PULSE_RUNTIME_PATH", PA_SYSTEM_RUNTIME_PATH);
pa_set_env("PULSE_CONFIG_PATH", PA_SYSTEM_RUNTIME_PATH);
pa_log_info("Successfully dropped root privileges.");
@ -264,23 +262,6 @@ static int change_user(void) {
#endif /* HAVE_PWD_H && HAVE_GRP_H */
static int create_runtime_dir(void) {
char fn[PATH_MAX];
pa_runtime_path(NULL, fn, sizeof(fn));
/* This function is called only when the daemon is started in
* per-user mode. We create the runtime directory somewhere in
* /tmp/ with the current UID/GID */
if (pa_make_secure_dir(fn, 0700, (uid_t)-1, (gid_t)-1) < 0) {
pa_log("Failed to create '%s': %s", fn, pa_cstrerror(errno));
return -1;
}
return 0;
}
#ifdef HAVE_SYS_RESOURCE_H
static int set_one_rlimit(const pa_rlimit *r, int resource, const char *name) {
@ -293,7 +274,7 @@ static int set_one_rlimit(const pa_rlimit *r, int resource, const char *name) {
rl.rlim_cur = rl.rlim_max = r->value;
if (setrlimit(resource, &rl) < 0) {
pa_log_warn("setrlimit(%s, (%u, %u)) failed: %s", name, (unsigned) r->value, (unsigned) r->value, pa_cstrerror(errno));
pa_log_info("setrlimit(%s, (%u, %u)) failed: %s", name, (unsigned) r->value, (unsigned) r->value, pa_cstrerror(errno));
return -1;
}
@ -301,17 +282,27 @@ static int set_one_rlimit(const pa_rlimit *r, int resource, const char *name) {
}
static void set_all_rlimits(const pa_daemon_conf *conf) {
set_one_rlimit(&conf->rlimit_as, RLIMIT_AS, "RLIMIT_AS");
set_one_rlimit(&conf->rlimit_core, RLIMIT_CORE, "RLIMIT_CORE");
set_one_rlimit(&conf->rlimit_data, RLIMIT_DATA, "RLIMIT_DATA");
set_one_rlimit(&conf->rlimit_fsize, RLIMIT_FSIZE, "RLIMIT_FSIZE");
set_one_rlimit(&conf->rlimit_nofile, RLIMIT_NOFILE, "RLIMIT_NOFILE");
set_one_rlimit(&conf->rlimit_data, RLIMIT_DATA, "RLIMIT_DATA");
set_one_rlimit(&conf->rlimit_stack, RLIMIT_STACK, "RLIMIT_STACK");
set_one_rlimit(&conf->rlimit_core, RLIMIT_CORE, "RLIMIT_CORE");
set_one_rlimit(&conf->rlimit_rss, RLIMIT_RSS, "RLIMIT_RSS");
#ifdef RLIMIT_NPROC
set_one_rlimit(&conf->rlimit_nproc, RLIMIT_NPROC, "RLIMIT_NPROC");
#endif
set_one_rlimit(&conf->rlimit_nofile, RLIMIT_NOFILE, "RLIMIT_NOFILE");
#ifdef RLIMIT_MEMLOCK
set_one_rlimit(&conf->rlimit_memlock, RLIMIT_MEMLOCK, "RLIMIT_MEMLOCK");
#endif
set_one_rlimit(&conf->rlimit_as, RLIMIT_AS, "RLIMIT_AS");
#ifdef RLIMIT_LOCKS
set_one_rlimit(&conf->rlimit_locks, RLIMIT_LOCKS, "RLIMIT_LOCKS");
#endif
#ifdef RLIMIT_SIGPENDING
set_one_rlimit(&conf->rlimit_sigpending, RLIMIT_SIGPENDING, "RLIMIT_SIGPENDING");
#endif
#ifdef RLIMIT_MSGQUEUE
set_one_rlimit(&conf->rlimit_msgqueue, RLIMIT_MSGQUEUE, "RLIMIT_MSGQUEUE");
#endif
#ifdef RLIMIT_NICE
set_one_rlimit(&conf->rlimit_nice, RLIMIT_NICE, "RLIMIT_NICE");
@ -319,6 +310,9 @@ static void set_all_rlimits(const pa_daemon_conf *conf) {
#ifdef RLIMIT_RTPRIO
set_one_rlimit(&conf->rlimit_rtprio, RLIMIT_RTPRIO, "RLIMIT_RTPRIO");
#endif
#ifdef RLIMIT_RTTIME
set_one_rlimit(&conf->rlimit_rttime, RLIMIT_RTTIME, "RLIMIT_RTTIME");
#endif
}
#endif
@ -329,18 +323,19 @@ int main(int argc, char *argv[]) {
pa_mainloop *mainloop = NULL;
char *s;
int r = 0, retval = 1, d = 0;
int daemon_pipe[2] = { -1, -1 };
pa_bool_t suid_root, real_root;
int valid_pid_file = 0;
pa_bool_t valid_pid_file = FALSE;
gid_t gid = (gid_t) -1;
pa_bool_t allow_realtime, allow_high_priority;
pa_bool_t ltdl_init = FALSE;
#ifdef OS_IS_WIN32
pa_time_event *timer;
struct timeval tv;
int passed_fd = -1;
const char *e;
#ifdef HAVE_FORK
int daemon_pipe[2] = { -1, -1 };
#endif
#ifdef OS_IS_WIN32
pa_time_event *win32_timer;
struct timeval win32_tv;
#endif
#if defined(__linux__) && defined(__OPTIMIZE__)
/*
@ -355,7 +350,7 @@ int main(int argc, char *argv[]) {
/* We have to execute ourselves, because the libc caches the
* value of $LD_BIND_NOW on initialization. */
putenv(pa_xstrdup("LD_BIND_NOW=1"));
pa_set_env("LD_BIND_NOW", "1");
pa_assert_se(rp = pa_readlink("/proc/self/exe"));
pa_assert_se(execv(rp, argv) == 0);
}
@ -385,6 +380,18 @@ int main(int argc, char *argv[]) {
* is just too risky tun let PA run as root all the time. */
}
if ((e = getenv("PULSE_PASSED_FD"))) {
passed_fd = atoi(e);
if (passed_fd <= 2)
passed_fd = -1;
}
pa_close_all(passed_fd, -1);
pa_reset_sigs(-1);
pa_unblock_sigs(-1);
/* At this point, we are a normal user, possibly with CAP_NICE if
* we were started SUID. If we are started as normal root, than we
* still are normal root. */
@ -410,67 +417,66 @@ int main(int argc, char *argv[]) {
pa_log_set_target(conf->auto_log_target ? PA_LOG_STDERR : conf->log_target, NULL);
if (suid_root) {
pa_bool_t allow_realtime, allow_high_priority;
/* Ok, we're suid root, so let's better not enable high prio
* or RT by default */
allow_high_priority = allow_realtime = FALSE;
if (conf->high_priority || conf->realtime_scheduling)
if (pa_own_uid_in_group(PA_REALTIME_GROUP, &gid) > 0) {
pa_log_info("We're in the group '"PA_REALTIME_GROUP"', allowing real-time and high-priority scheduling.");
allow_realtime = conf->realtime_scheduling;
allow_high_priority = conf->high_priority;
}
#ifdef HAVE_POLKIT
if (conf->high_priority) {
if (conf->high_priority && !allow_high_priority) {
if (pa_polkit_check("org.pulseaudio.acquire-high-priority") > 0) {
pa_log_info("PolicyKit grants us acquire-high-priority privilige.");
pa_log_info("PolicyKit grants us acquire-high-priority privilege.");
allow_high_priority = TRUE;
} else
pa_log_info("PolicyKit refuses acquire-high-priority privilige.");
pa_log_info("PolicyKit refuses acquire-high-priority privilege.");
}
if (conf->realtime_scheduling) {
if (conf->realtime_scheduling && !allow_realtime) {
if (pa_polkit_check("org.pulseaudio.acquire-real-time") > 0) {
pa_log_info("PolicyKit grants us acquire-real-time privilige.");
pa_log_info("PolicyKit grants us acquire-real-time privilege.");
allow_realtime = TRUE;
} else
pa_log_info("PolicyKit refuses acquire-real-time privilige.");
pa_log_info("PolicyKit refuses acquire-real-time privilege.");
}
#endif
if ((conf->high_priority || conf->realtime_scheduling) && pa_own_uid_in_group(PA_REALTIME_GROUP, &gid) > 0) {
pa_log_info("We're in the group '"PA_REALTIME_GROUP"', allowing real-time and high-priority scheduling.");
allow_realtime = conf->realtime_scheduling;
allow_high_priority = conf->high_priority;
}
if (!allow_high_priority && !allow_realtime) {
/* OK, there's no further need to keep CAP_NICE. Hence
* let's give it up early */
pa_drop_caps();
pa_drop_root();
suid_root = real_root = FALSE;
suid_root = FALSE;
if (conf->high_priority || conf->realtime_scheduling)
pa_log_notice("Called SUID root and real-time/high-priority scheduling was requested in the configuration. However, we lack the necessary priviliges:\n"
"We are not in group '"PA_REALTIME_GROUP"' and PolicyKit refuse to grant us priviliges. Dropping SUID again.\n"
"For enabling real-time scheduling please acquire the appropriate PolicyKit priviliges, or become a member of '"PA_REALTIME_GROUP"', or increase the RLIMIT_NICE/RLIMIT_RTPRIO resource limits for this user.");
}
} else {
/* OK, we're a normal user, so let's allow the user evrything
* he asks for, it's now the kernel's job to enforce limits,
* not ours anymore */
allow_high_priority = allow_realtime = TRUE;
}
if (conf->high_priority && !allow_high_priority) {
pa_log_info("High-priority scheduling enabled in configuration but now allowed by policy. Disabling forcibly.");
conf->high_priority = FALSE;
}
#ifdef HAVE_SYS_RESOURCE_H
/* Reset resource limits. If we are run as root (for system mode)
* this might end up increasing the limits, which is intended
* behaviour. For all other cases, i.e. started as normal user, or
* SUID root at this point we should have no CAP_SYS_RESOURCE and
* increasing the limits thus should fail. Which is, too, intended
* behaviour */
if (conf->realtime_scheduling && !allow_realtime) {
pa_log_info("Real-time scheduling enabled in configuration but now allowed by policy. Disabling forcibly.");
conf->realtime_scheduling = FALSE;
}
set_all_rlimits(conf);
#endif
if (conf->high_priority && !pa_can_high_priority())
pa_log_warn("High-priority scheduling enabled in configuration but not allowed by policy.");
if (conf->high_priority && conf->cmd == PA_CMD_DAEMON)
pa_raise_priority(conf->nice_level);
@ -482,28 +488,38 @@ int main(int argc, char *argv[]) {
#ifdef RLIMIT_RTPRIO
if (!drop) {
struct rlimit rl;
/* At this point we still have CAP_NICE if we were loaded
* SUID root. If possible let's acquire RLIMIT_RTPRIO
* instead and give CAP_NICE up. */
const pa_rlimit rl = { 9, TRUE };
if (getrlimit(RLIMIT_RTPRIO, &rl) >= 0) {
if (set_one_rlimit(&rl, RLIMIT_RTPRIO, "RLIMIT_RTPRIO") >= 0) {
pa_log_info("Successfully increased RLIMIT_RTPRIO, giving up CAP_NICE.");
drop = TRUE;
} else
pa_log_warn("RLIMIT_RTPRIO failed: %s", pa_cstrerror(errno));
if (rl.rlim_cur >= 9)
drop = TRUE;
else {
rl.rlim_max = rl.rlim_cur = 9;
if (setrlimit(RLIMIT_RTPRIO, &rl) >= 0) {
pa_log_info("Successfully increased RLIMIT_RTPRIO");
drop = TRUE;
} else
pa_log_warn("RLIMIT_RTPRIO failed: %s", pa_cstrerror(errno));
}
}
}
#endif
if (drop) {
pa_log_info("Giving up CAP_NICE");
pa_drop_caps();
pa_drop_root();
suid_root = real_root = FALSE;
suid_root = FALSE;
}
}
if (conf->realtime_scheduling && !pa_can_realtime())
pa_log_warn("Real-time scheduling enabled in configuration but not allowed by policy.");
LTDL_SET_PRELOADED_SYMBOLS();
pa_ltdl_init();
ltdl_init = TRUE;
@ -605,7 +621,7 @@ int main(int argc, char *argv[]) {
#ifdef HAVE_FORK
if (pipe(daemon_pipe) < 0) {
pa_log("Failed to create pipe.");
pa_log("pipe failed: %s", pa_cstrerror(errno));
goto finish;
}
@ -615,20 +631,24 @@ int main(int argc, char *argv[]) {
}
if (child != 0) {
ssize_t n;
/* Father */
pa_assert_se(pa_close(daemon_pipe[1]) == 0);
daemon_pipe[1] = -1;
if (pa_loop_read(daemon_pipe[0], &retval, sizeof(retval), NULL) != sizeof(retval)) {
pa_log("read() failed: %s", pa_cstrerror(errno));
if ((n = pa_loop_read(daemon_pipe[0], &retval, sizeof(retval), NULL)) != sizeof(retval)) {
if (n < 0)
pa_log("read() failed: %s", pa_cstrerror(errno));
retval = 1;
}
if (retval)
pa_log("daemon startup failed.");
pa_log("Daemon startup failed.");
else
pa_log_info("daemon startup successful.");
pa_log_info("Daemon startup successful.");
goto finish;
}
@ -652,9 +672,9 @@ int main(int argc, char *argv[]) {
pa_close(1);
pa_close(2);
open("/dev/null", O_RDONLY);
open("/dev/null", O_WRONLY);
open("/dev/null", O_WRONLY);
pa_assert_se(open("/dev/null", O_RDONLY) == 0);
pa_assert_se(open("/dev/null", O_WRONLY) == 1);
pa_assert_se(open("/dev/null", O_WRONLY) == 2);
#else
FreeConsole();
#endif
@ -677,39 +697,32 @@ int main(int argc, char *argv[]) {
#endif
}
pa_set_env("PULSE_INTERNAL", "1");
pa_assert_se(chdir("/") == 0);
umask(0022);
if (conf->system_instance) {
if (conf->system_instance)
if (change_user() < 0)
goto finish;
} else if (create_runtime_dir() < 0)
goto finish;
pa_log_info("This is PulseAudio " PACKAGE_VERSION);
pa_log_info("Page size is %lu bytes", (unsigned long) PA_PAGE_SIZE);
pa_log_info("Using runtime directory %s.", s = pa_get_runtime_dir());
pa_xfree(s);
if (conf->use_pid_file) {
if (pa_pid_file_create() < 0) {
pa_log("pa_pid_file_create() failed.");
#ifdef HAVE_FORK
if (conf->daemonize)
pa_loop_write(daemon_pipe[1], &retval, sizeof(retval), NULL);
#endif
goto finish;
}
valid_pid_file = 1;
valid_pid_file = TRUE;
}
#ifdef HAVE_SYS_RESOURCE_H
set_all_rlimits(conf);
#endif
#ifdef SIGPIPE
signal(SIGPIPE, SIG_IGN);
#endif
pa_log_info("This is PulseAudio " PACKAGE_VERSION);
pa_log_info("Page size is %lu bytes", (unsigned long) PA_PAGE_SIZE);
if (pa_rtclock_hrtimer())
pa_log_info("Fresh high-resolution timers available! Bon appetit!");
else
@ -738,11 +751,11 @@ int main(int argc, char *argv[]) {
c->realtime_priority = conf->realtime_priority;
c->realtime_scheduling = !!conf->realtime_scheduling;
c->disable_remixing = !!conf->disable_remixing;
c->running_as_daemon = !!conf->daemonize;
pa_assert_se(pa_signal_init(pa_mainloop_get_api(mainloop)) == 0);
pa_signal_new(SIGINT, signal_callback, c);
pa_signal_new(SIGTERM, signal_callback, c);
#ifdef SIGUSR1
pa_signal_new(SIGUSR1, signal_callback, c);
#endif
@ -754,23 +767,27 @@ int main(int argc, char *argv[]) {
#endif
#ifdef OS_IS_WIN32
pa_assert_se(timer = pa_mainloop_get_api(mainloop)->time_new(pa_mainloop_get_api(mainloop), pa_gettimeofday(&tv), message_cb, NULL));
win32_timer = pa_mainloop_get_api(mainloop)->time_new(pa_mainloop_get_api(mainloop), pa_gettimeofday(&win32_tv), message_cb, NULL);
#endif
if (conf->daemonize)
c->running_as_daemon = TRUE;
oil_init();
if (!conf->no_cpu_limit)
pa_assert_se(pa_cpu_limit_init(pa_mainloop_get_api(mainloop)) == 0);
buf = pa_strbuf_new();
if (conf->default_script_file)
r = pa_cli_command_execute_file(c, conf->default_script_file, buf, &conf->fail);
if (conf->load_default_script_file) {
FILE *f;
if ((f = pa_daemon_conf_open_default_script_file(conf))) {
r = pa_cli_command_execute_file_stream(c, f, buf, &conf->fail);
fclose(f);
}
}
if (r >= 0)
r = pa_cli_command_execute(c, conf->script_commands, buf, &conf->fail);
pa_log_error("%s", s = pa_strbuf_tostring_free(buf));
pa_xfree(s);
@ -780,53 +797,55 @@ int main(int argc, char *argv[]) {
if (r < 0 && conf->fail) {
pa_log("Failed to initialize daemon.");
#ifdef HAVE_FORK
if (conf->daemonize)
pa_loop_write(daemon_pipe[1], &retval, sizeof(retval), NULL);
#endif
} else if (!c->modules || pa_idxset_size(c->modules) == 0) {
pa_log("daemon startup without any loaded modules, refusing to work.");
#ifdef HAVE_FORK
if (conf->daemonize)
pa_loop_write(daemon_pipe[1], &retval, sizeof(retval), NULL);
#endif
} else {
retval = 0;
if (c->default_sink_name &&
pa_namereg_get(c, c->default_sink_name, PA_NAMEREG_SINK, 1) == NULL) {
pa_log_error("%s : Default sink name (%s) does not exist in name register.", __FILE__, c->default_sink_name);
retval = !!conf->fail;
}
#ifdef HAVE_FORK
if (conf->daemonize)
pa_loop_write(daemon_pipe[1], &retval, sizeof(retval), NULL);
#endif
if (!retval) {
pa_log_info("Daemon startup complete.");
if (pa_mainloop_run(mainloop, &retval) < 0)
retval = 1;
pa_log_info("Daemon shutdown initiated.");
}
goto finish;
}
#ifdef OS_IS_WIN32
pa_mainloop_get_api(mainloop)->time_free(timer);
if (!c->modules || pa_idxset_size(c->modules) == 0) {
pa_log("Daemon startup without any loaded modules, refusing to work.");
goto finish;
}
if (c->default_sink_name && !pa_namereg_get(c, c->default_sink_name, PA_NAMEREG_SINK, TRUE) && conf->fail) {
pa_log_error("Default sink name (%s) does not exist in name register.", c->default_sink_name);
goto finish;
}
#ifdef HAVE_FORK
if (conf->daemonize) {
int ok = 0;
pa_loop_write(daemon_pipe[1], &ok, sizeof(ok), NULL);
}
#endif
pa_core_unref(c);
pa_log_info("Daemon startup complete.");
retval = 0;
if (pa_mainloop_run(mainloop, &retval) < 0)
goto finish;
pa_log_info("Daemon shutdown initiated.");
finish:
#ifdef OS_IS_WIN32
if (win32_timer)
pa_mainloop_get_api(mainloop)->time_free(win32_timer);
#endif
if (c) {
pa_core_unref(c);
pa_log_info("Daemon terminated.");
}
if (!conf->no_cpu_limit)
pa_cpu_limit_done();
pa_signal_done();
pa_log_info("Daemon terminated.");
finish:
#ifdef HAVE_FORK
pa_close_pipe(daemon_pipe);
#endif
if (mainloop)
pa_mainloop_free(mainloop);
@ -837,8 +856,6 @@ finish:
if (valid_pid_file)
pa_pid_file_remove();
pa_close_pipe(daemon_pipe);
#ifdef OS_IS_WIN32
WSACleanup();
#endif

View file

@ -30,6 +30,7 @@ pa_context_get_autoload_info_by_name;
pa_context_get_autoload_info_list;
pa_context_get_client_info;
pa_context_get_client_info_list;
pa_context_get_index;
pa_context_get_module_info;
pa_context_get_module_info_list;
pa_context_get_protocol_version;
@ -61,7 +62,11 @@ pa_context_move_sink_input_by_name;
pa_context_move_source_output_by_index;
pa_context_move_source_output_by_name;
pa_context_new;
pa_context_new_with_proplist;
pa_context_play_sample;
pa_context_play_sample_with_proplist;
pa_context_proplist_remove;
pa_context_proplist_update;
pa_context_ref;
pa_context_remove_autoload_by_index;
pa_context_remove_autoload_by_name;
@ -128,14 +133,19 @@ pa_operation_unref;
pa_parse_sample_format;
pa_path_get_filename;
pa_proplist_free;
pa_proplist_contains;
pa_proplist_clear;
pa_proplist_copy;
pa_proplist_get;
pa_proplist_gets;
pa_proplist_iterate;
pa_proplist_merge;
pa_proplist_update;
pa_proplist_new;
pa_proplist_put;
pa_proplist_puts;
pa_proplist_remove;
pa_proplist_set;
pa_proplist_sets;
pa_proplist_setf;
pa_proplist_unset;
pa_proplist_unset_many;
pa_proplist_to_string;
pa_sample_format_to_string;
pa_sample_size;
@ -174,10 +184,14 @@ pa_stream_get_sample_spec;
pa_stream_get_state;
pa_stream_get_time;
pa_stream_get_timing_info;
pa_stream_is_corked;
pa_stream_is_suspended;
pa_stream_new;
pa_stream_new_with_proplist;
pa_stream_peek;
pa_stream_prebuf;
pa_stream_proplist_remove;
pa_stream_proplist_update;
pa_stream_readable_size;
pa_stream_ref;
pa_stream_set_buffer_attr;
@ -186,6 +200,7 @@ pa_stream_set_moved_callback;
pa_stream_set_name;
pa_stream_set_overflow_callback;
pa_stream_set_read_callback;
pa_stream_set_started_callback;
pa_stream_set_state_callback;
pa_stream_set_suspended_callback;
pa_stream_set_underflow_callback;
@ -221,6 +236,7 @@ pa_timeval_cmp;
pa_timeval_diff;
pa_timeval_load;
pa_timeval_store;
pa_timeval_sub;
pa_usec_to_bytes;
pa_utf8_filter;
pa_utf8_to_locale;

View file

@ -27,6 +27,7 @@
#endif
#include <sys/types.h>
#include <limits.h>
#include <asoundlib.h>
#include <pulse/sample.h>
@ -35,6 +36,7 @@
#include <pulsecore/log.h>
#include <pulsecore/macro.h>
#include <pulsecore/core-util.h>
#include <pulsecore/atomic.h>
#include "alsa-util.h"
@ -290,16 +292,22 @@ int pa_alsa_set_hw_params(
pa_sample_spec *ss,
uint32_t *periods,
snd_pcm_uframes_t *period_size,
snd_pcm_uframes_t tsched_size,
pa_bool_t *use_mmap,
pa_bool_t *use_tsched,
pa_bool_t require_exact_channel_number) {
int ret = -1;
snd_pcm_uframes_t _period_size = *period_size;
unsigned int _periods = *periods;
snd_pcm_uframes_t buffer_size;
unsigned int r = ss->rate;
unsigned int c = ss->channels;
pa_sample_format_t f = ss->format;
snd_pcm_hw_params_t *hwparams;
pa_bool_t _use_mmap = use_mmap && *use_mmap;
pa_bool_t _use_tsched = use_tsched && *use_tsched;
int dir;
pa_assert(pcm_handle);
pa_assert(ss);
@ -308,8 +316,6 @@ int pa_alsa_set_hw_params(
snd_pcm_hw_params_alloca(&hwparams);
buffer_size = *periods * *period_size;
if ((ret = snd_pcm_hw_params_any(pcm_handle, hwparams)) < 0)
goto finish;
@ -330,12 +336,19 @@ int pa_alsa_set_hw_params(
} else if ((ret = snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
goto finish;
if (!_use_mmap)
_use_tsched = FALSE;
if ((ret = set_format(pcm_handle, hwparams, &f)) < 0)
goto finish;
if ((ret = snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, &r, NULL)) < 0)
goto finish;
/* Adjust the buffer sizes, if we didn't get the rate we were asking for */
_period_size = (snd_pcm_uframes_t) (((uint64_t) _period_size * r) / ss->rate);
tsched_size = (snd_pcm_uframes_t) (((uint64_t) tsched_size * r) / ss->rate);
if (require_exact_channel_number) {
if ((ret = snd_pcm_hw_params_set_channels(pcm_handle, hwparams, c)) < 0)
goto finish;
@ -344,10 +357,32 @@ int pa_alsa_set_hw_params(
goto finish;
}
if ((*period_size > 0 && (ret = snd_pcm_hw_params_set_period_size_near(pcm_handle, hwparams, period_size, NULL)) < 0) ||
(*periods > 0 && (ret = snd_pcm_hw_params_set_buffer_size_near(pcm_handle, hwparams, &buffer_size)) < 0))
if (_use_tsched) {
_period_size = tsched_size;
_periods = 1;
pa_assert_se(snd_pcm_hw_params_get_buffer_size_max(hwparams, &buffer_size) == 0);
pa_log_debug("Maximum hw buffer size is %u ms", (unsigned) buffer_size * 1000 / r);
}
buffer_size = _periods * _period_size;
if ((ret = snd_pcm_hw_params_set_periods_integer(pcm_handle, hwparams)) < 0)
goto finish;
if (_periods > 0) {
dir = 1;
if ((ret = snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, &_periods, &dir)) < 0) {
dir = -1;
if ((ret = snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, &_periods, &dir)) < 0)
goto finish;
}
}
if (_period_size > 0)
if ((ret = snd_pcm_hw_params_set_buffer_size_near(pcm_handle, hwparams, &buffer_size)) < 0)
goto finish;
if ((ret = snd_pcm_hw_params(pcm_handle, hwparams)) < 0)
goto finish;
@ -363,8 +398,8 @@ int pa_alsa_set_hw_params(
if ((ret = snd_pcm_prepare(pcm_handle)) < 0)
goto finish;
if ((ret = snd_pcm_hw_params_get_buffer_size(hwparams, &buffer_size)) < 0 ||
(ret = snd_pcm_hw_params_get_period_size(hwparams, period_size, NULL)) < 0)
if ((ret = snd_pcm_hw_params_get_period_size(hwparams, &_period_size, &dir)) < 0 ||
(ret = snd_pcm_hw_params_get_periods(hwparams, &_periods, &dir)) < 0)
goto finish;
/* If the sample rate deviates too much, we need to resample */
@ -373,14 +408,18 @@ int pa_alsa_set_hw_params(
ss->channels = c;
ss->format = f;
pa_assert(buffer_size > 0);
pa_assert(*period_size > 0);
*periods = buffer_size / *period_size;
pa_assert(*periods > 0);
pa_assert(_periods > 0);
pa_assert(_period_size > 0);
*periods = _periods;
*period_size = _period_size;
if (use_mmap)
*use_mmap = _use_mmap;
if (use_tsched)
*use_tsched = _use_tsched;
ret = 0;
finish:
@ -388,7 +427,7 @@ finish:
return ret;
}
int pa_alsa_set_sw_params(snd_pcm_t *pcm) {
int pa_alsa_set_sw_params(snd_pcm_t *pcm, snd_pcm_uframes_t avail_min) {
snd_pcm_sw_params_t *swparams;
int err;
@ -411,6 +450,11 @@ int pa_alsa_set_sw_params(snd_pcm_t *pcm) {
return err;
}
if ((err = snd_pcm_sw_params_set_avail_min(pcm, swparams, avail_min)) < 0) {
pa_log_error("snd_pcm_sw_params_set_avail_min() failed: %s", 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;
@ -477,7 +521,9 @@ snd_pcm_t *pa_alsa_open_by_device_id(
int mode,
uint32_t *nfrags,
snd_pcm_uframes_t *period_size,
pa_bool_t *use_mmap) {
snd_pcm_uframes_t tsched_size,
pa_bool_t *use_mmap,
pa_bool_t *use_tsched) {
int i;
int direction = 1;
@ -526,7 +572,11 @@ snd_pcm_t *pa_alsa_open_by_device_id(
d = pa_sprintf_malloc("%s:%s", device_table[i].name, dev_id);
pa_log_debug("Trying %s...", d);
if ((err = snd_pcm_open(&pcm_handle, d, mode, SND_PCM_NONBLOCK)) < 0) {
if ((err = snd_pcm_open(&pcm_handle, d, mode,
SND_PCM_NONBLOCK|
SND_PCM_NO_AUTO_RESAMPLE|
SND_PCM_NO_AUTO_CHANNELS|
SND_PCM_NO_AUTO_FORMAT)) < 0) {
pa_log_info("Couldn't open PCM device %s: %s", d, snd_strerror(err));
pa_xfree(d);
continue;
@ -536,7 +586,7 @@ snd_pcm_t *pa_alsa_open_by_device_id(
try_ss.rate = ss->rate;
try_ss.format = ss->format;
if ((err = pa_alsa_set_hw_params(pcm_handle, &try_ss, nfrags, period_size, use_mmap, TRUE)) < 0) {
if ((err = pa_alsa_set_hw_params(pcm_handle, &try_ss, nfrags, period_size, tsched_size, use_mmap, use_tsched, TRUE)) < 0) {
pa_log_info("PCM device %s refused our hw parameters: %s", d, snd_strerror(err));
pa_xfree(d);
snd_pcm_close(pcm_handle);
@ -550,11 +600,11 @@ snd_pcm_t *pa_alsa_open_by_device_id(
return pcm_handle;
}
/* OK, we didn't find any good device, so let's try the raw hw: stuff */
/* OK, we didn't find any good device, so let's try the raw plughw: stuff */
d = pa_sprintf_malloc("hw:%s", dev_id);
d = pa_sprintf_malloc("plughw:%s", dev_id);
pa_log_debug("Trying %s as last resort...", d);
pcm_handle = pa_alsa_open_by_device_string(d, dev, ss, map, mode, nfrags, period_size, use_mmap);
pcm_handle = pa_alsa_open_by_device_string(d, dev, ss, map, mode, nfrags, period_size, tsched_size, use_mmap, use_tsched);
pa_xfree(d);
return pcm_handle;
@ -568,7 +618,9 @@ snd_pcm_t *pa_alsa_open_by_device_string(
int mode,
uint32_t *nfrags,
snd_pcm_uframes_t *period_size,
pa_bool_t *use_mmap) {
snd_pcm_uframes_t tsched_size,
pa_bool_t *use_mmap,
pa_bool_t *use_tsched) {
int err;
char *d;
@ -585,13 +637,16 @@ snd_pcm_t *pa_alsa_open_by_device_string(
for (;;) {
if ((err = snd_pcm_open(&pcm_handle, d, mode, SND_PCM_NONBLOCK)) < 0) {
if ((err = snd_pcm_open(&pcm_handle, d, mode, SND_PCM_NONBLOCK|
SND_PCM_NO_AUTO_RESAMPLE|
SND_PCM_NO_AUTO_CHANNELS|
SND_PCM_NO_AUTO_FORMAT)) < 0) {
pa_log("Error opening PCM device %s: %s", d, snd_strerror(err));
pa_xfree(d);
return NULL;
}
if ((err = pa_alsa_set_hw_params(pcm_handle, ss, nfrags, period_size, use_mmap, FALSE)) < 0) {
if ((err = pa_alsa_set_hw_params(pcm_handle, ss, nfrags, period_size, tsched_size, use_mmap, use_tsched, FALSE)) < 0) {
if (err == -EPERM) {
/* Hmm, some hw is very exotic, so we retry with plug, if without it didn't work */
@ -616,8 +671,24 @@ snd_pcm_t *pa_alsa_open_by_device_string(
*dev = d;
if (ss->channels != map->channels) {
pa_assert_se(pa_channel_map_init_auto(map, ss->channels, PA_CHANNEL_MAP_AUX));
pa_channel_map_init_auto(map, ss->channels, PA_CHANNEL_MAP_ALSA);
if (!pa_channel_map_init_auto(map, ss->channels, PA_CHANNEL_MAP_ALSA)) {
unsigned c;
pa_channel_position_t pos;
pa_log_warn("Device has an unknown channel mapping. This is a limitation of ALSA. Synthesizing channel map.");
for (c = ss->channels; c > 0; c--)
if (pa_channel_map_init_auto(map, c, PA_CHANNEL_MAP_ALSA))
break;
pa_assert(c > 0);
pos = PA_CHANNEL_POSITION_AUX0;
for (; c < map->channels; c ++)
map->map[c] = pos++;
map->channels = ss->channels;
}
}
return pcm_handle;
@ -773,7 +844,7 @@ int pa_alsa_calc_mixer_map(snd_mixer_elem_t *elem, const pa_channel_map *channel
}
if ((is_mono && mono_used) || (!is_mono && alsa_channel_used[id])) {
pa_log_info("Channel map has duplicate channel '%s', failling back to software volume control.", pa_channel_position_to_string(channel_map->map[i]));
pa_log_info("Channel map has duplicate channel '%s', falling back to software volume control.", pa_channel_position_to_string(channel_map->map[i]));
return -1;
}
@ -793,7 +864,275 @@ int pa_alsa_calc_mixer_map(snd_mixer_elem_t *elem, const pa_channel_map *channel
}
}
pa_log_info("All %u channels can be mapped to mixer channels. Using hardware volume control.", channel_map->channels);
pa_log_info("All %u channels can be mapped to mixer channels.", channel_map->channels);
return 0;
}
void pa_alsa_0dB_playback(snd_mixer_elem_t *elem) {
long min, max, v;
pa_assert(elem);
/* Try to enable 0 dB if possible. If ALSA cannot do dB, then use
* raw volume levels and fix them to 75% */
if (snd_mixer_selem_set_playback_dB_all(elem, 0, -1) >= 0)
return;
if (snd_mixer_selem_set_playback_dB_all(elem, 0, 1) >= 0)
return;
if (snd_mixer_selem_get_playback_volume_range(elem, &min, &max) < 0)
return;
v = min + ((max - min) * 3) / 4; /* 75% */
if (v <= min)
v = max;
snd_mixer_selem_set_playback_volume_all(elem, v);
}
void pa_alsa_0dB_capture(snd_mixer_elem_t *elem) {
long min, max, v;
pa_assert(elem);
/* Try to enable 0 dB if possible. If ALSA cannot do dB, then use
* raw volume levels and fix them to 75% */
if (snd_mixer_selem_set_capture_dB_all(elem, 0, -1) >= 0)
return;
if (snd_mixer_selem_set_capture_dB_all(elem, 0, 1) >= 0)
return;
if (snd_mixer_selem_get_capture_volume_range(elem, &min, &max) < 0)
return;
v = min + ((max - min) * 3) / 4; /* 75% */
if (v <= min)
v = max;
snd_mixer_selem_set_capture_volume_all(elem, v);
}
void pa_alsa_dump(snd_pcm_t *pcm) {
int err;
snd_output_t *out;
pa_assert(pcm);
pa_assert_se(snd_output_buffer_open(&out) == 0);
if ((err = snd_pcm_dump(pcm, out)) < 0)
pa_log_debug("snd_pcm_dump(): %s", snd_strerror(err));
else {
char *s = NULL;
snd_output_buffer_string(out, &s);
pa_log_debug("snd_pcm_dump():\n%s", pa_strnull(s));
}
pa_assert_se(snd_output_close(out) == 0);
}
void pa_alsa_dump_status(snd_pcm_t *pcm) {
int err;
snd_output_t *out;
snd_pcm_status_t *status;
pa_assert(pcm);
snd_pcm_status_alloca(&status);
pa_assert_se(snd_output_buffer_open(&out) == 0);
pa_assert_se(snd_pcm_status(pcm, status) == 0);
if ((err = snd_pcm_status_dump(status, out)) < 0)
pa_log_debug("snd_pcm_dump(): %s", snd_strerror(err));
else {
char *s = NULL;
snd_output_buffer_string(out, &s);
pa_log_debug("snd_pcm_dump():\n%s", pa_strnull(s));
}
pa_assert_se(snd_output_close(out) == 0);
}
static void alsa_error_handler(const char *file, int line, const char *function, int err, const char *fmt,...) {
va_list ap;
va_start(ap, fmt);
pa_log_levelv_meta(PA_LOG_WARN, file, line, function, fmt, ap);
va_end(ap);
}
static pa_atomic_t n_error_handler_installed = PA_ATOMIC_INIT(0);
void pa_alsa_redirect_errors_inc(void) {
/* This is not really thread safe, but we do our best */
if (pa_atomic_inc(&n_error_handler_installed) == 0)
snd_lib_error_set_handler(alsa_error_handler);
}
void pa_alsa_redirect_errors_dec(void) {
int r;
pa_assert_se((r = pa_atomic_dec(&n_error_handler_installed)) >= 1);
if (r == 1)
snd_lib_error_set_handler(NULL);
}
void pa_alsa_init_proplist(pa_proplist *p, snd_pcm_info_t *pcm_info) {
static const char * const alsa_class_table[SND_PCM_CLASS_LAST+1] = {
[SND_PCM_CLASS_GENERIC] = "generic",
[SND_PCM_CLASS_MULTI] = "multi",
[SND_PCM_CLASS_MODEM] = "modem",
[SND_PCM_CLASS_DIGITIZER] = "digitizer"
};
static const char * const class_table[SND_PCM_CLASS_LAST+1] = {
[SND_PCM_CLASS_GENERIC] = "sound",
[SND_PCM_CLASS_MULTI] = NULL,
[SND_PCM_CLASS_MODEM] = "modem",
[SND_PCM_CLASS_DIGITIZER] = NULL
};
static const char * const alsa_subclass_table[SND_PCM_SUBCLASS_LAST+1] = {
[SND_PCM_SUBCLASS_GENERIC_MIX] = "generic-mix",
[SND_PCM_SUBCLASS_MULTI_MIX] = "multi-mix"
};
snd_pcm_class_t class;
snd_pcm_subclass_t subclass;
const char *n, *id, *sdn;
char *cn = NULL, *lcn = NULL;
int card;
pa_assert(p);
pa_assert(pcm_info);
pa_proplist_sets(p, PA_PROP_DEVICE_API, "alsa");
class = snd_pcm_info_get_class(pcm_info);
if (class <= SND_PCM_CLASS_LAST) {
if (class_table[class])
pa_proplist_sets(p, PA_PROP_DEVICE_CLASS, class_table[class]);
if (alsa_class_table[class])
pa_proplist_sets(p, "alsa.class", alsa_class_table[class]);
}
subclass = snd_pcm_info_get_subclass(pcm_info);
if (subclass <= SND_PCM_SUBCLASS_LAST)
if (alsa_subclass_table[subclass])
pa_proplist_sets(p, "alsa.subclass", alsa_subclass_table[subclass]);
if ((n = snd_pcm_info_get_name(pcm_info)))
pa_proplist_sets(p, "alsa.name", n);
if ((id = snd_pcm_info_get_id(pcm_info)))
pa_proplist_sets(p, "alsa.id", id);
pa_proplist_setf(p, "alsa.subdevice", "%u", snd_pcm_info_get_subdevice(pcm_info));
if ((sdn = snd_pcm_info_get_subdevice_name(pcm_info)))
pa_proplist_sets(p, "alsa.subdevice_name", sdn);
pa_proplist_setf(p, "alsa.device", "%u", snd_pcm_info_get_device(pcm_info));
if ((card = snd_pcm_info_get_card(pcm_info)) >= 0) {
pa_proplist_setf(p, "alsa.card", "%i", card);
if (snd_card_get_name(card, &cn) >= 0)
pa_proplist_sets(p, "alsa.card_name", cn);
if (snd_card_get_longname(card, &lcn) >= 0)
pa_proplist_sets(p, "alsa.long_card_name", lcn);
}
if (cn && n)
pa_proplist_setf(p, PA_PROP_DEVICE_DESCRIPTION, "%s - %s", cn, n);
else if (cn)
pa_proplist_sets(p, PA_PROP_DEVICE_DESCRIPTION, cn);
else if (n)
pa_proplist_sets(p, PA_PROP_DEVICE_DESCRIPTION, n);
free(lcn);
free(cn);
}
int pa_alsa_recover_from_poll(snd_pcm_t *pcm, int revents) {
snd_pcm_state_t state;
int err;
pa_assert(pcm);
if (revents & POLLERR)
pa_log_warn("Got POLLERR from ALSA");
if (revents & POLLNVAL)
pa_log_warn("Got POLLNVAL from ALSA");
if (revents & POLLHUP)
pa_log_warn("Got POLLHUP from ALSA");
state = snd_pcm_state(pcm);
pa_log_warn("PCM state is %s", snd_pcm_state_name(state));
/* Try to recover from this error */
switch (state) {
case SND_PCM_STATE_XRUN:
if ((err = snd_pcm_recover(pcm, -EPIPE, 1)) != 0) {
pa_log_warn("Could not recover from POLLERR|POLLNVAL|POLLHUP and XRUN: %s", snd_strerror(err));
return -1;
}
break;
case SND_PCM_STATE_SUSPENDED:
if ((err = snd_pcm_recover(pcm, -ESTRPIPE, 1)) != 0) {
pa_log_warn("Could not recover from POLLERR|POLLNVAL|POLLHUP and SUSPENDED: %s", snd_strerror(err));
return -1;
}
break;
default:
snd_pcm_drop(pcm);
if ((err = snd_pcm_prepare(pcm)) < 0) {
pa_log_warn("Could not recover from POLLERR|POLLNVAL|POLLHUP with snd_pcm_prepare(): %s", snd_strerror(err));
return -1;
}
break;
}
return 0;
}
pa_rtpoll_item* pa_alsa_build_pollfd(snd_pcm_t *pcm, pa_rtpoll *rtpoll) {
int n, err;
struct pollfd *pollfd;
pa_rtpoll_item *item;
pa_assert(pcm);
if ((n = snd_pcm_poll_descriptors_count(pcm)) < 0) {
pa_log("snd_pcm_poll_descriptors_count() failed: %s", snd_strerror(n));
return NULL;
}
item = pa_rtpoll_item_new(rtpoll, PA_RTPOLL_NEVER, n);
pollfd = pa_rtpoll_item_get_pollfd(item, NULL);
if ((err = snd_pcm_poll_descriptors(pcm, pollfd, n)) < 0) {
pa_log("snd_pcm_poll_descriptors() failed: %s", snd_strerror(err));
pa_rtpoll_item_free(item);
return NULL;
}
return item;
}

View file

@ -29,8 +29,10 @@
#include <pulse/sample.h>
#include <pulse/mainloop-api.h>
#include <pulse/channelmap.h>
#include <pulse/proplist.h>
#include <pulsecore/rtpoll.h>
typedef struct pa_alsa_fdlist pa_alsa_fdlist;
@ -43,10 +45,12 @@ int pa_alsa_set_hw_params(
pa_sample_spec *ss,
uint32_t *periods,
snd_pcm_uframes_t *period_size,
snd_pcm_uframes_t tsched_size,
pa_bool_t *use_mmap,
pa_bool_t *use_tsched,
pa_bool_t require_exact_channel_number);
int pa_alsa_set_sw_params(snd_pcm_t *pcm);
int pa_alsa_set_sw_params(snd_pcm_t *pcm, snd_pcm_uframes_t avail_min);
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);
@ -59,7 +63,9 @@ snd_pcm_t *pa_alsa_open_by_device_id(
int mode,
uint32_t *nfrags,
snd_pcm_uframes_t *period_size,
pa_bool_t *use_mmap);
snd_pcm_uframes_t tsched_size,
pa_bool_t *use_mmap,
pa_bool_t *use_tsched);
snd_pcm_t *pa_alsa_open_by_device_string(
const char *device,
@ -69,8 +75,25 @@ snd_pcm_t *pa_alsa_open_by_device_string(
int mode,
uint32_t *nfrags,
snd_pcm_uframes_t *period_size,
pa_bool_t *use_mmap);
snd_pcm_uframes_t tsched_size,
pa_bool_t *use_mmap,
pa_bool_t *use_tsched);
int pa_alsa_calc_mixer_map(snd_mixer_elem_t *elem, const pa_channel_map *channel_map, snd_mixer_selem_channel_id_t mixer_map[], pa_bool_t playback);
void pa_alsa_0dB_playback(snd_mixer_elem_t *elem);
void pa_alsa_0dB_capture(snd_mixer_elem_t *elem);
void pa_alsa_dump(snd_pcm_t *pcm);
void pa_alsa_dump_status(snd_pcm_t *pcm);
void pa_alsa_redirect_errors_inc(void);
void pa_alsa_redirect_errors_dec(void);
void pa_alsa_init_proplist(pa_proplist *p, snd_pcm_info_t *pcm_info);
int pa_alsa_recover_from_poll(snd_pcm_t *pcm, int revents);
pa_rtpoll_item* pa_alsa_build_pollfd(snd_pcm_t *pcm, pa_rtpoll *rtpoll);
#endif

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -66,7 +66,7 @@ PA_MODULE_USAGE(
"channel_map=<channel map>");
#define DEFAULT_SINK_NAME "combined"
#define MEMBLOCKQ_MAXLENGTH (1024*170)
#define MEMBLOCKQ_MAXLENGTH (1024*1024*16)
#define DEFAULT_ADJUST_TIME 10
@ -139,7 +139,7 @@ enum {
};
enum {
SINK_INPUT_MESSAGE_POST = PA_SINK_INPUT_MESSAGE_MAX
SINK_INPUT_MESSAGE_POST = PA_SINK_INPUT_MESSAGE_MAX,
};
static void output_free(struct output *o);
@ -162,13 +162,13 @@ static void adjust_rates(struct userdata *u) {
if (!u->master)
return;
if (!PA_SINK_OPENED(pa_sink_get_state(u->sink)))
if (!PA_SINK_IS_OPENED(pa_sink_get_state(u->sink)))
return;
for (o = pa_idxset_first(u->outputs, &idx); o; o = pa_idxset_next(u->outputs, &idx)) {
pa_usec_t sink_latency;
if (!o->sink_input || !PA_SINK_OPENED(pa_sink_get_state(o->sink)))
if (!o->sink_input || !PA_SINK_IS_OPENED(pa_sink_get_state(o->sink)))
continue;
sink_latency = pa_sink_get_latency(o->sink);
@ -194,7 +194,7 @@ static void adjust_rates(struct userdata *u) {
for (o = pa_idxset_first(u->outputs, &idx); o; o = pa_idxset_next(u->outputs, &idx)) {
uint32_t r = base_rate;
if (!o->sink_input || !PA_SINK_OPENED(pa_sink_get_state(o->sink)))
if (!o->sink_input || !PA_SINK_IS_OPENED(pa_sink_get_state(o->sink)))
continue;
if (o->total_latency < target_latency)
@ -203,10 +203,10 @@ static void adjust_rates(struct userdata *u) {
r += (uint32_t) (((((double) o->total_latency - target_latency))/u->adjust_time)*r/PA_USEC_PER_SEC);
if (r < (uint32_t) (base_rate*0.9) || r > (uint32_t) (base_rate*1.1)) {
pa_log_warn("[%s] sample rates too different, not adjusting (%u vs. %u).", o->sink_input->name, base_rate, r);
pa_log_warn("[%s] sample rates too different, not adjusting (%u vs. %u).", pa_proplist_gets(o->sink_input->proplist, PA_PROP_MEDIA_NAME), base_rate, r);
pa_sink_input_set_rate(o->sink_input, base_rate);
} else {
pa_log_info("[%s] new rate is %u Hz; ratio is %0.3f; latency is %0.0f usec.", o->sink_input->name, r, (double) r / base_rate, (float) o->total_latency);
pa_log_info("[%s] new rate is %u Hz; ratio is %0.3f; latency is %0.0f usec.", pa_proplist_gets(o->sink_input->proplist, PA_PROP_MEDIA_NAME), r, (double) r / base_rate, (float) o->total_latency);
pa_sink_input_set_rate(o->sink_input, r);
}
}
@ -250,10 +250,18 @@ static void thread_func(void *userdata) {
if (u->sink->thread_info.state == PA_SINK_RUNNING && !u->thread_info.active_outputs) {
struct timeval now;
/* Just rewind if necessary, since we are in NULL mode, we
* don't have to pass this on */
pa_sink_process_rewind(u->sink, u->sink->thread_info.rewind_nbytes);
u->sink->thread_info.rewind_nbytes = 0;
pa_rtclock_get(&now);
if (!u->thread_info.in_null_mode || pa_timeval_cmp(&u->thread_info.timestamp, &now) <= 0) {
pa_sink_skip(u->sink, u->block_size);
pa_memchunk chunk;
pa_sink_render_full(u->sink, u->block_size, &chunk);
pa_memblock_unref(chunk.memblock);
if (!u->thread_info.in_null_mode)
u->thread_info.timestamp = now;
@ -354,27 +362,20 @@ static void request_memblock(struct output *o, size_t length) {
}
/* Called from I/O thread context */
static int sink_input_peek_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) {
static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) {
struct output *o;
pa_sink_input_assert_ref(i);
pa_assert_se(o = i->userdata);
/* If necessary, get some new data */
request_memblock(o, length);
request_memblock(o, nbytes);
return pa_memblockq_peek(o->memblockq, chunk);
}
if (pa_memblockq_peek(o->memblockq, chunk) < 0)
return -1;
/* Called from I/O thread context */
static void sink_input_drop_cb(pa_sink_input *i, size_t length) {
struct output *o;
pa_sink_input_assert_ref(i);
pa_assert(length > 0);
pa_assert_se(o = i->userdata);
pa_memblockq_drop(o->memblockq, length);
pa_memblockq_drop(o->memblockq, chunk->length);
return 0;
}
/* Called from I/O thread context */
@ -386,7 +387,7 @@ static void sink_input_attach_cb(pa_sink_input *i) {
/* Set up the queue from the sink thread to us */
pa_assert(!o->inq_rtpoll_item);
o->inq_rtpoll_item = pa_rtpoll_item_new_asyncmsgq(
o->inq_rtpoll_item = pa_rtpoll_item_new_asyncmsgq_read(
i->sink->rtpoll,
PA_RTPOLL_LATE, /* This one is not that important, since we check for data in _peek() anyway. */
o->inq);
@ -434,12 +435,13 @@ static int sink_input_process_msg(pa_msgobject *obj, int code, void *data, int64
case SINK_INPUT_MESSAGE_POST:
if (PA_SINK_OPENED(o->sink_input->sink->thread_info.state))
if (PA_SINK_IS_OPENED(o->sink_input->sink->thread_info.state))
pa_memblockq_push_align(o->memblockq, chunk);
else
pa_memblockq_flush(o->memblockq);
break;
}
return pa_sink_input_process_msg(obj, code, data, offset, chunk);
@ -472,7 +474,7 @@ static void enable_output(struct output *o) {
pa_sink_input_put(o->sink_input);
if (o->userdata->sink && PA_SINK_LINKED(pa_sink_get_state(o->userdata->sink)))
if (o->userdata->sink && PA_SINK_IS_LINKED(pa_sink_get_state(o->userdata->sink)))
pa_asyncmsgq_send(o->userdata->sink->asyncmsgq, PA_MSGOBJECT(o->userdata->sink), SINK_MESSAGE_ADD_OUTPUT, o, 0, NULL);
}
}
@ -505,7 +507,7 @@ static void unsuspend(struct userdata *u) {
pa_sink_suspend(o->sink, FALSE);
if (PA_SINK_OPENED(pa_sink_get_state(o->sink)))
if (PA_SINK_IS_OPENED(pa_sink_get_state(o->sink)))
enable_output(o);
}
@ -526,7 +528,7 @@ static int sink_set_state(pa_sink *sink, pa_sink_state_t state) {
switch (state) {
case PA_SINK_SUSPENDED:
pa_assert(PA_SINK_OPENED(pa_sink_get_state(u->sink)));
pa_assert(PA_SINK_IS_OPENED(pa_sink_get_state(u->sink)));
suspend(u);
break;
@ -585,7 +587,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
/* Create pa_asyncmsgq to the sink thread */
op->outq_rtpoll_item = pa_rtpoll_item_new_asyncmsgq(
op->outq_rtpoll_item = pa_rtpoll_item_new_asyncmsgq_read(
u->rtpoll,
PA_RTPOLL_EARLY-1, /* This item is very important */
op->outq);
@ -616,35 +618,35 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
}
/* Called from main context */
static pa_usec_t sink_get_latency_cb(pa_sink *s) {
struct userdata *u;
/* static pa_usec_t sink_get_latency_cb(pa_sink *s) { */
/* struct userdata *u; */
pa_sink_assert_ref(s);
pa_assert_se(u = s->userdata);
/* pa_sink_assert_ref(s); */
/* pa_assert_se(u = s->userdata); */
if (u->master) {
/* If we have a master sink, we just return the latency of it
* and add our own buffering on top */
/* if (u->master) { */
/* /\* If we have a master sink, we just return the latency of it */
/* * and add our own buffering on top *\/ */
if (!u->master->sink_input)
return 0;
/* if (!u->master->sink_input) */
/* return 0; */
return
pa_sink_input_get_latency(u->master->sink_input) +
pa_sink_get_latency(u->master->sink);
/* return */
/* pa_sink_input_get_latency(u->master->sink_input) + */
/* pa_sink_get_latency(u->master->sink); */
} else {
pa_usec_t usec = 0;
/* } else { */
/* pa_usec_t usec = 0; */
/* We have no master, hence let's ask our own thread which
* implements the NULL sink */
/* /\* We have no master, hence let's ask our own thread which */
/* * implements the NULL sink *\/ */
if (pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0)
return 0;
/* if (pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0) */
/* return 0; */
return usec;
}
}
/* return usec; */
/* } */
/* } */
static void update_description(struct userdata *u) {
int first = 1;
@ -665,10 +667,10 @@ static void update_description(struct userdata *u) {
char *e;
if (first) {
e = pa_sprintf_malloc("%s %s", t, o->sink->description);
e = pa_sprintf_malloc("%s %s", t, pa_strnull(pa_proplist_gets(o->sink->proplist, PA_PROP_DEVICE_DESCRIPTION)));
first = 0;
} else
e = pa_sprintf_malloc("%s, %s", t, o->sink->description);
e = pa_sprintf_malloc("%s, %s", t, pa_strnull(pa_proplist_gets(o->sink->proplist, PA_PROP_DEVICE_DESCRIPTION)));
pa_xfree(t);
t = e;
@ -698,7 +700,7 @@ static void pick_master(struct userdata *u, struct output *except) {
if (u->master &&
u->master != except &&
u->master->sink_input &&
PA_SINK_OPENED(pa_sink_get_state(u->master->sink))) {
PA_SINK_IS_OPENED(pa_sink_get_state(u->master->sink))) {
update_master(u, u->master);
return;
}
@ -706,7 +708,7 @@ static void pick_master(struct userdata *u, struct output *except) {
for (o = pa_idxset_first(u->outputs, &idx); o; o = pa_idxset_next(u->outputs, &idx))
if (o != except &&
o->sink_input &&
PA_SINK_OPENED(pa_sink_get_state(o->sink))) {
PA_SINK_IS_OPENED(pa_sink_get_state(o->sink))) {
update_master(u, o);
return;
}
@ -723,12 +725,12 @@ static int output_create_sink_input(struct output *o) {
if (o->sink_input)
return 0;
t = pa_sprintf_malloc("Simultaneous output on %s", o->sink->description);
t = pa_sprintf_malloc("Simultaneous output on %s", pa_strnull(pa_proplist_gets(o->sink->proplist, PA_PROP_DEVICE_DESCRIPTION)));
pa_sink_input_new_data_init(&data);
data.sink = o->sink;
data.driver = __FILE__;
data.name = t;
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_DESCRIPTION, t);
pa_sink_input_new_data_set_sample_spec(&data, &o->userdata->sink->sample_spec);
pa_sink_input_new_data_set_channel_map(&data, &o->userdata->sink->channel_map);
data.module = o->userdata->module;
@ -736,14 +738,15 @@ static int output_create_sink_input(struct output *o) {
o->sink_input = pa_sink_input_new(o->userdata->core, &data, PA_SINK_INPUT_VARIABLE_RATE|PA_SINK_INPUT_DONT_MOVE);
pa_sink_input_new_data_done(&data);
pa_xfree(t);
if (!o->sink_input)
return -1;
o->sink_input->parent.process_msg = sink_input_process_msg;
o->sink_input->peek = sink_input_peek_cb;
o->sink_input->drop = sink_input_drop_cb;
o->sink_input->pop = sink_input_pop_cb;
o->sink_input->attach = sink_input_attach_cb;
o->sink_input->detach = sink_input_detach_cb;
o->sink_input->kill = sink_input_kill_cb;
@ -775,26 +778,27 @@ static struct output *output_new(struct userdata *u, pa_sink *sink) {
pa_frame_size(&u->sink->sample_spec),
1,
0,
0,
NULL);
pa_assert_se(pa_idxset_put(u->outputs, o, NULL) == 0);
if (u->sink && PA_SINK_LINKED(pa_sink_get_state(u->sink)))
if (u->sink && PA_SINK_IS_LINKED(pa_sink_get_state(u->sink)))
pa_asyncmsgq_send(u->sink->asyncmsgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_ADD_OUTPUT, o, 0, NULL);
else {
/* If the sink is not yet started, we need to do the activation ourselves */
PA_LLIST_PREPEND(struct output, u->thread_info.active_outputs, o);
o->outq_rtpoll_item = pa_rtpoll_item_new_asyncmsgq(
o->outq_rtpoll_item = pa_rtpoll_item_new_asyncmsgq_read(
u->rtpoll,
PA_RTPOLL_EARLY-1, /* This item is very important */
o->outq);
}
if (PA_SINK_OPENED(pa_sink_get_state(u->sink)) || pa_sink_get_state(u->sink) == PA_SINK_INIT) {
if (PA_SINK_IS_OPENED(pa_sink_get_state(u->sink)) || pa_sink_get_state(u->sink) == PA_SINK_INIT) {
pa_sink_suspend(sink, FALSE);
if (PA_SINK_OPENED(pa_sink_get_state(sink)))
if (PA_SINK_IS_OPENED(pa_sink_get_state(sink)))
if (output_create_sink_input(o) < 0)
goto fail;
}
@ -897,7 +901,7 @@ static pa_hook_result_t sink_state_changed_hook_cb(pa_core *c, pa_sink *s, struc
state = pa_sink_get_state(s);
if (PA_SINK_OPENED(state) && PA_SINK_OPENED(pa_sink_get_state(u->sink)) && !o->sink_input) {
if (PA_SINK_IS_OPENED(state) && PA_SINK_IS_OPENED(pa_sink_get_state(u->sink)) && !o->sink_input) {
enable_output(o);
pick_master(u, NULL);
}
@ -920,6 +924,7 @@ int pa__init(pa_module*m) {
pa_channel_map map;
struct output *o;
uint32_t idx;
pa_sink_new_data data;
pa_assert(m);
@ -943,8 +948,8 @@ int pa__init(pa_module*m) {
u->master = NULL;
u->time_event = NULL;
u->adjust_time = DEFAULT_ADJUST_TIME;
pa_thread_mq_init(&u->thread_mq, m->core->mainloop);
u->rtpoll = pa_rtpoll_new();
pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);
u->thread = NULL;
u->resample_method = resample_method;
u->outputs = pa_idxset_new(NULL, NULL);
@ -953,7 +958,6 @@ int pa__init(pa_module*m) {
PA_LLIST_HEAD_INIT(struct output, u->thread_info.active_outputs);
pa_atomic_store(&u->thread_info.running, FALSE);
u->thread_info.in_null_mode = FALSE;
pa_rtpoll_item_new_asyncmsgq(u->rtpoll, PA_RTPOLL_EARLY, u->thread_mq.inq);
if (pa_modargs_get_value_u32(ma, "adjust_time", &u->adjust_time) < 0) {
pa_log("Failed to parse adjust_time value");
@ -1003,19 +1007,28 @@ int pa__init(pa_module*m) {
goto fail;
}
if (!(u->sink = pa_sink_new(m->core, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss, &map))) {
pa_sink_new_data_init(&data);
data.name = pa_xstrdup(pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME));
data.namereg_fail = FALSE;
data.driver = __FILE__;
data.module = m;
pa_sink_new_data_set_sample_spec(&data, &ss);
pa_sink_new_data_set_channel_map(&data, &map);
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Simultaneous Output");
u->sink = pa_sink_new(m->core, &data, PA_SINK_LATENCY);
pa_sink_new_data_done(&data);
if (!u->sink) {
pa_log("Failed to create sink");
goto fail;
}
u->sink->parent.process_msg = sink_process_msg;
u->sink->get_latency = sink_get_latency_cb;
/* u->sink->get_latency = sink_get_latency_cb; */
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, "Simultaneous output");
pa_sink_set_rtpoll(u->sink, u->rtpoll);
pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
@ -1075,7 +1088,7 @@ int pa__init(pa_module*m) {
}
}
u->sink_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_NEW_POST], (pa_hook_cb_t) sink_new_hook_cb, u);
u->sink_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PUT], (pa_hook_cb_t) sink_new_hook_cb, u);
}
u->sink_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_UNLINK], (pa_hook_cb_t) sink_unlink_hook_cb, u);

View file

@ -3,7 +3,7 @@
/***
This file is part of PulseAudio.
Copyright 2006 Lennart Poettering
Copyright 2006-2008 Lennart Poettering
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
@ -25,10 +25,16 @@
#include <config.h>
#endif
#include <errno.h>
#include <stdio.h>
#include <pulse/timeval.h>
#include <pulsecore/core-util.h>
#include <pulsecore/module.h>
#include <pulsecore/log.h>
#include <pulsecore/namereg.h>
#include <pulsecore/core-error.h>
#include "module-default-device-restore-symdef.h"
@ -39,15 +45,24 @@ PA_MODULE_LOAD_ONCE(TRUE);
#define DEFAULT_SINK_FILE "default-sink"
#define DEFAULT_SOURCE_FILE "default-source"
#define DEFAULT_SAVE_INTERVAL 5
int pa__init(pa_module *m) {
struct userdata {
pa_core *core;
pa_subscription *subscription;
pa_time_event *time_event;
char *sink_filename, *source_filename;
pa_bool_t modified;
};
static void load(struct userdata *u) {
FILE *f;
/* We never overwrite manually configured settings */
if (m->core->default_sink_name)
if (u->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"))) {
else if ((f = fopen(u->sink_filename, "r"))) {
char ln[256] = "";
fgets(ln, sizeof(ln)-1, f);
@ -55,17 +70,19 @@ int pa__init(pa_module *m) {
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);
pa_log_info("No previous default sink setting, ignoring.");
else if (pa_namereg_get(u->core, ln, PA_NAMEREG_SINK, TRUE)) {
pa_namereg_set_default(u->core, ln, PA_NAMEREG_SINK);
pa_log_info("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)
} else if (errno != ENOENT)
pa_log("Failed to load default sink: %s", pa_cstrerror(errno));
if (u->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"))) {
else if ((f = fopen(u->source_filename, "r"))) {
char ln[256] = "";
fgets(ln, sizeof(ln)-1, f);
@ -73,29 +90,114 @@ int pa__init(pa_module *m) {
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);
pa_log_info("No previous default source setting, ignoring.");
else if (pa_namereg_get(u->core, ln, PA_NAMEREG_SOURCE, TRUE)) {
pa_namereg_set_default(u->core, ln, PA_NAMEREG_SOURCE);
pa_log_info("Restored default source '%s'.", ln);
} else
pa_log_info("Saved default source '%s' not existant, not restoring default source setting.", ln);
} else if (errno != ENOENT)
pa_log("Failed to load default sink: %s", pa_cstrerror(errno));
}
static void save(struct userdata *u) {
FILE *f;
if (!u->modified)
return;
if (u->sink_filename) {
if ((f = fopen(u->sink_filename, "w"))) {
const char *n = pa_namereg_get_default_sink_name(u->core);
fprintf(f, "%s\n", pa_strempty(n));
fclose(f);
} else
pa_log("Failed to save default sink: %s", pa_cstrerror(errno));
}
if (u->source_filename) {
if ((f = fopen(u->source_filename, "w"))) {
const char *n = pa_namereg_get_default_source_name(u->core);
fprintf(f, "%s\n", pa_strempty(n));
fclose(f);
} else
pa_log("Failed to save default source: %s", pa_cstrerror(errno));
}
u->modified = FALSE;
}
static void time_cb(pa_mainloop_api *a, pa_time_event *e, const struct timeval *tv, void *userdata) {
struct userdata *u = userdata;
pa_assert(u);
save(u);
if (u->time_event) {
u->core->mainloop->time_free(u->time_event);
u->time_event = NULL;
}
}
static void subscribe_cb(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
struct userdata *u = userdata;
pa_assert(u);
u->modified = TRUE;
if (!u->time_event) {
struct timeval tv;
pa_gettimeofday(&tv);
pa_timeval_add(&tv, DEFAULT_SAVE_INTERVAL*PA_USEC_PER_SEC);
u->time_event = u->core->mainloop->time_new(u->core->mainloop, &tv, time_cb, u);
}
}
int pa__init(pa_module *m) {
struct userdata *u;
pa_assert(u);
u = pa_xnew0(struct userdata, 1);
u->core = m->core;
if (!(u->sink_filename = pa_runtime_path(DEFAULT_SINK_FILE)))
goto fail;
if (!(u->source_filename = pa_runtime_path(DEFAULT_SOURCE_FILE)))
goto fail;
load(u);
u->subscription = pa_subscription_new(u->core, PA_SUBSCRIPTION_MASK_SERVER, subscribe_cb, u);
return 0;
fail:
pa__done(m);
return -1;
}
void pa__done(pa_module*m) {
FILE *f;
struct userdata *u;
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);
}
pa_assert(m);
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);
}
if (!(u = m->userdata))
return;
save(u);
if (u->subscription)
pa_subscription_free(u->subscription);
if (u->time_event)
m->core->mainloop->time_free(u->time_event);
pa_xfree(u->sink_filename);
pa_xfree(u->source_filename);
pa_xfree(u);
}

View file

@ -0,0 +1,349 @@
/* $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 <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <gdbm.h>
#include <pulse/xmalloc.h>
#include <pulse/volume.h>
#include <pulse/timeval.h>
#include <pulse/util.h>
#include <pulsecore/core-error.h>
#include <pulsecore/module.h>
#include <pulsecore/core-util.h>
#include <pulsecore/modargs.h>
#include <pulsecore/log.h>
#include <pulsecore/core-subscribe.h>
#include <pulsecore/sink-input.h>
#include <pulsecore/source-output.h>
#include <pulsecore/namereg.h>
#include "module-device-restore-symdef.h"
PA_MODULE_AUTHOR("Lennart Poettering");
PA_MODULE_DESCRIPTION("Automatically restore the volume/mute state of devices");
PA_MODULE_VERSION(PACKAGE_VERSION);
PA_MODULE_LOAD_ONCE(TRUE);
#define SAVE_INTERVAL 10
static const char* const valid_modargs[] = {
NULL,
};
struct userdata {
pa_core *core;
pa_subscription *subscription;
pa_hook_slot *sink_fixate_hook_slot, *source_fixate_hook_slot;
pa_time_event *save_time_event;
GDBM_FILE gdbm_file;
};
struct entry {
pa_cvolume volume;
int muted;
};
static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *tv, void *userdata) {
struct userdata *u = userdata;
pa_assert(a);
pa_assert(e);
pa_assert(tv);
pa_assert(u);
pa_assert(e == u->save_time_event);
u->core->mainloop->time_free(u->save_time_event);
u->save_time_event = NULL;
gdbm_sync(u->gdbm_file);
pa_log_info("Synced.");
}
static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
struct userdata *u = userdata;
struct entry entry;
char *name;
datum key, data;
pa_assert(c);
pa_assert(u);
if (t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW) &&
t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE) &&
t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW) &&
t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE))
return;
if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK) {
pa_sink *sink;
if (!(sink = pa_idxset_get_by_index(c->sinks, idx)))
return;
name = pa_sprintf_malloc("sink:%s", sink->name);
entry.volume = *pa_sink_get_volume(sink);
entry.muted = pa_sink_get_mute(sink);
} else {
pa_source *source;
pa_assert((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE);
if (!(source = pa_idxset_get_by_index(c->sources, idx)))
return;
name = pa_sprintf_malloc("source:%s", source->name);
entry.volume = *pa_source_get_volume(source);
entry.muted = pa_source_get_mute(source);
}
key.dptr = name;
key.dsize = strlen(name);
data = gdbm_fetch(u->gdbm_file, key);
if (data.dptr) {
if (data.dsize == sizeof(struct entry)) {
struct entry *old = (struct entry*) data.dptr;
if (pa_cvolume_valid(&old->volume)) {
if (pa_cvolume_equal(&old->volume, &entry.volume) &&
!old->muted == !entry.muted) {
pa_xfree(data.dptr);
pa_xfree(name);
return;
}
} else
pa_log_warn("Invalid volume stored in database for device %s", name);
} else
pa_log_warn("Database contains entry for device %s of wrong size %lu != %lu", name, (unsigned long) data.dsize, (unsigned long) sizeof(struct entry));
pa_xfree(data.dptr);
}
data.dptr = (void*) &entry;
data.dsize = sizeof(entry);
pa_log_info("Storing volume/mute for device %s.", name);
gdbm_store(u->gdbm_file, key, data, GDBM_REPLACE);
if (!u->save_time_event) {
struct timeval tv;
pa_gettimeofday(&tv);
tv.tv_sec += SAVE_INTERVAL;
u->save_time_event = u->core->mainloop->time_new(u->core->mainloop, &tv, save_time_callback, u);
}
pa_xfree(name);
}
static struct entry* read_entry(struct userdata *u, char *name) {
datum key, data;
struct entry *e;
pa_assert(u);
pa_assert(name);
key.dptr = name;
key.dsize = strlen(name);
data = gdbm_fetch(u->gdbm_file, key);
if (!data.dptr)
goto fail;
if (data.dsize != sizeof(struct entry)) {
pa_log_warn("Database contains entry for device %s of wrong size %lu != %lu", name, (unsigned long) data.dsize, (unsigned long) sizeof(struct entry));
goto fail;
}
e = (struct entry*) data.dptr;
if (!(pa_cvolume_valid(&e->volume))) {
pa_log_warn("Invalid volume stored in database for device %s", name);
goto fail;
}
return e;
fail:
pa_xfree(data.dptr);
return NULL;
}
static pa_hook_result_t sink_fixate_hook_callback(pa_core *c, pa_sink_new_data *new_data, struct userdata *u) {
char *name;
struct entry *e;
pa_assert(new_data);
name = pa_sprintf_malloc("sink:%s", new_data->name);
if ((e = read_entry(u, name))) {
if (e->volume.channels == new_data->sample_spec.channels) {
pa_log_info("Restoring volume for sink %s.", new_data->name);
pa_sink_new_data_set_volume(new_data, &e->volume);
}
pa_log_info("Restoring mute state for sink %s.", new_data->name);
pa_sink_new_data_set_muted(new_data, e->muted);
pa_xfree(e);
}
pa_xfree(name);
return PA_HOOK_OK;
}
static pa_hook_result_t source_fixate_hook_callback(pa_core *c, pa_source_new_data *new_data, struct userdata *u) {
char *name;
struct entry *e;
pa_assert(new_data);
name = pa_sprintf_malloc("source:%s", new_data->name);
if ((e = read_entry(u, name))) {
if (e->volume.channels == new_data->sample_spec.channels) {
pa_log_info("Restoring volume for source %s.", new_data->name);
pa_source_new_data_set_volume(new_data, &e->volume);
}
pa_log_info("Restoring mute state for source %s.", new_data->name);
pa_source_new_data_set_muted(new_data, e->muted);
pa_xfree(e);
}
pa_xfree(name);
return PA_HOOK_OK;
}
int pa__init(pa_module*m) {
pa_modargs *ma = NULL;
struct userdata *u;
char *fname, *runtime_dir;
char hn[256];
pa_sink *sink;
pa_source *source;
uint32_t idx;
pa_assert(m);
if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
pa_log("Failed to parse module arguments");
goto fail;
}
u = pa_xnew(struct userdata, 1);
u->core = m->core;
u->save_time_event = NULL;
u->subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_SINK|PA_SUBSCRIPTION_MASK_SOURCE, subscribe_callback, u);
u->sink_fixate_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_FIXATE], (pa_hook_cb_t) sink_fixate_hook_callback, u);
u->source_fixate_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_FIXATE], (pa_hook_cb_t) source_fixate_hook_callback, u);
m->userdata = u;
if (!pa_get_host_name(hn, sizeof(hn)))
goto fail;
if (!(runtime_dir = pa_get_runtime_dir()))
goto fail;
fname = pa_sprintf_malloc("%s/device-volumes.%s.gdbm", runtime_dir, hn);
pa_xfree(runtime_dir);
if (!(u->gdbm_file = gdbm_open(fname, 0, GDBM_WRCREAT, 0600, NULL))) {
pa_log("Failed to open volume database '%s': %s", fname, gdbm_strerror(gdbm_errno));
pa_xfree(fname);
goto fail;
}
pa_log_info("Sucessfully opened database file '%s'.", fname);
pa_xfree(fname);
for (sink = pa_idxset_first(m->core->sinks, &idx); sink; sink = pa_idxset_next(m->core->sinks, &idx))
subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW, sink->index, u);
for (source = pa_idxset_first(m->core->sources, &idx); source; source = pa_idxset_next(m->core->sources, &idx))
subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW, source->index, u);
pa_modargs_free(ma);
return 0;
fail:
pa__done(m);
if (ma)
pa_modargs_free(ma);
return -1;
}
void pa__done(pa_module*m) {
struct userdata* u;
pa_assert(m);
if (!(u = m->userdata))
return;
if (u->subscription)
pa_subscription_free(u->subscription);
if (u->sink_fixate_hook_slot)
pa_hook_slot_free(u->sink_fixate_hook_slot);
if (u->source_fixate_hook_slot)
pa_hook_slot_free(u->source_fixate_hook_slot);
if (u->save_time_event)
u->core->mainloop->time_free(u->save_time_event);
if (u->gdbm_file)
gdbm_close(u->gdbm_file);
pa_xfree(u);
}

View file

@ -143,7 +143,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
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_assert(PA_SINK_IS_OPENED(u->sink->thread_info.state));
pa_smoother_pause(u->smoother, pa_rtclock_usec());
break;
@ -211,7 +211,7 @@ static void thread_func(void *userdata) {
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) {
if (PA_SINK_IS_OPENED(u->sink->thread_info.state) && pollfd->revents) {
pa_usec_t usec;
int64_t n;
@ -294,7 +294,7 @@ static void thread_func(void *userdata) {
}
/* Hmm, nothing to do. Let's sleep */
pollfd->events = PA_SINK_OPENED(u->sink->thread_info.state) ? POLLOUT : 0;
pollfd->events = PA_SINK_IS_OPENED(u->sink->thread_info.state) ? POLLOUT : 0;
}
if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0)
@ -502,12 +502,11 @@ static void on_connection(PA_GCC_UNUSED pa_socket_client *c, pa_iochannel*io, vo
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;
uint32_t key;
pa_sink_new_data data;
pa_assert(m);
@ -533,13 +532,12 @@ int pa__init(pa_module*m) {
u->module = m;
m->userdata = u;
u->fd = -1;
u->smoother = pa_smoother_new(PA_USEC_PER_SEC, PA_USEC_PER_SEC*2, TRUE);
u->smoother = pa_smoother_new(PA_USEC_PER_SEC, PA_USEC_PER_SEC*2, TRUE, 10);
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);
pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);
u->rtpoll_item = NULL;
u->format =
@ -554,30 +552,38 @@ int pa__init(pa_module*m) {
u->state = STATE_AUTH;
u->latency = 0;
if (!(u->sink = pa_sink_new(m->core, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss, NULL))) {
if (!(espeaker = getenv("ESPEAKER")))
espeaker = ESD_UNIX_SOCKET_NAME;
espeaker = pa_modargs_get_value(ma, "server", espeaker);
pa_sink_new_data_init(&data);
data.driver = __FILE__;
data.module = m;
pa_sink_new_data_set_name(&data, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME));
pa_sink_new_data_set_sample_spec(&data, &ss);
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, espeaker);
pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Esound sink '%s'", espeaker);
u->sink = pa_sink_new(m->core, &data, PA_SINK_LATENCY|PA_SINK_NETWORK);
pa_sink_new_data_done(&data);
if (!u->sink) {
pa_log("Failed to create sink.");
goto fail;
}
u->sink->parent.process_msg = sink_process_msg;
u->sink->userdata = u;
u->sink->flags = PA_SINK_LATENCY|PA_SINK_NETWORK;
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))) {
if (!(u->client = pa_socket_client_new_string(u->core->mainloop, 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 */

View file

@ -372,7 +372,7 @@ static int hal_device_add_all(struct userdata *u, const char *capability) {
pa_log_debug("Not loaded device %s", udis[i]);
else {
if (d->sink_name)
pa_scache_play_item_by_name(u->core, "pulse-coldplug", d->sink_name, PA_VOLUME_NORM, 0);
pa_scache_play_item_by_name(u->core, "pulse-coldplug", d->sink_name, FALSE, PA_VOLUME_NORM, NULL, NULL);
count++;
}
}
@ -412,7 +412,7 @@ static void device_added_time_cb(pa_mainloop_api *ea, pa_time_event *ev, const s
pa_log_debug("Not loaded device %s", td->udi);
else {
if (d->sink_name)
pa_scache_play_item_by_name(td->u->core, "pulse-hotplug", d->sink_name, PA_VOLUME_NORM, 0);
pa_scache_play_item_by_name(td->u->core, "pulse-hotplug", d->sink_name, FALSE, PA_VOLUME_NORM, NULL, NULL);
}
}
}
@ -575,7 +575,7 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *message, vo
if (prev_suspended && !suspend) {
/* resume */
if (pa_sink_suspend(sink, 0) >= 0)
pa_scache_play_item_by_name(u->core, "pulse-access", d->sink_name, PA_VOLUME_NORM, 0);
pa_scache_play_item_by_name(u->core, "pulse-access", d->sink_name, FALSE, PA_VOLUME_NORM, NULL, NULL);
else
d->acl_race_fix = 1;
@ -643,7 +643,7 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *message, vo
if (prev_suspended) {
/* resume */
if (pa_sink_suspend(sink, 0) >= 0)
pa_scache_play_item_by_name(u->core, "pulse-access", d->sink_name, PA_VOLUME_NORM, 0);
pa_scache_play_item_by_name(u->core, "pulse-access", d->sink_name, FALSE, PA_VOLUME_NORM, NULL, NULL);
}
}
}

View file

@ -53,7 +53,7 @@
/* General overview:
*
* Because JACK has a very unflexible event loop management, which
* Because JACK has a very unflexible event loop management which
* doesn't allow us to add our own event sources to the event thread
* we cannot use the JACK real-time thread for dispatching our PA
* work. Instead, we run an additional RT thread which does most of
@ -276,7 +276,7 @@ int pa__init(pa_module*m) {
pa_bool_t do_connect = TRUE;
unsigned i;
const char **ports = NULL, **p;
char *t;
pa_sink_new_data data;
pa_assert(m);
@ -300,9 +300,8 @@ int pa__init(pa_module*m) {
u->module = m;
m->userdata = u;
u->saved_frame_time_valid = FALSE;
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);
pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);
/* The queue linking the JACK thread and our RT thread */
u->jack_msgq = pa_asyncmsgq_new(0);
@ -312,7 +311,7 @@ int pa__init(pa_module*m) {
* all other drivers make: supplying the audio device with data is
* the top priority -- and as long as that is possible we don't do
* anything else */
u->rtpoll_item = pa_rtpoll_item_new_asyncmsgq(u->rtpoll, PA_RTPOLL_EARLY-1, u->jack_msgq);
u->rtpoll_item = pa_rtpoll_item_new_asyncmsgq_read(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.");
@ -355,20 +354,31 @@ int pa__init(pa_module*m) {
}
}
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.");
pa_sink_new_data_init(&data);
data.driver = __FILE__;
data.module = m;
pa_sink_new_data_set_name(&data, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME));
pa_sink_new_data_set_sample_spec(&data, &ss);
pa_sink_new_data_set_channel_map(&data, &map);
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_API, "jack");
if (server_name)
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, server_name);
pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Jack sink (%s)", jack_get_client_name(u->client));
pa_proplist_sets(data.proplist, "jack.client_name", jack_get_client_name(u->client));
u->sink = pa_sink_new(m->core, &data, PA_SINK_LATENCY);
pa_sink_new_data_done(&data);
if (!u->sink) {
pa_log("Failed to create sink.");
goto fail;
}
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);
pa_sink_set_description(u->sink, t = pa_sprintf_malloc("Jack sink (%s)", jack_get_client_name(u->client)));
pa_xfree(t);
jack_set_process_callback(u->client, jack_process, u);
jack_on_shutdown(u->client, jack_shutdown, u);

View file

@ -253,7 +253,7 @@ int pa__init(pa_module*m) {
pa_bool_t do_connect = TRUE;
unsigned i;
const char **ports = NULL, **p;
char *t;
pa_source_new_data data;
pa_assert(m);
@ -278,12 +278,11 @@ int pa__init(pa_module*m) {
m->userdata = u;
u->saved_frame_time_valid = FALSE;
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);
pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);
u->jack_msgq = pa_asyncmsgq_new(0);
u->rtpoll_item = pa_rtpoll_item_new_asyncmsgq(u->rtpoll, PA_RTPOLL_EARLY-1, u->jack_msgq);
u->rtpoll_item = pa_rtpoll_item_new_asyncmsgq_read(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.");
@ -326,20 +325,31 @@ int pa__init(pa_module*m) {
}
}
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.");
pa_source_new_data_init(&data);
data.driver = __FILE__;
data.module = m;
pa_source_new_data_set_name(&data, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME));
pa_source_new_data_set_sample_spec(&data, &ss);
pa_source_new_data_set_channel_map(&data, &map);
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_API, "jack");
if (server_name)
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, server_name);
pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Jack source (%s)", jack_get_client_name(u->client));
pa_proplist_sets(data.proplist, "jack.client_name", jack_get_client_name(u->client));
u->source = pa_source_new(m->core, &data, PA_SOURCE_LATENCY);
pa_source_new_data_done(&data);
if (!u->source) {
pa_log("Failed to create source.");
goto fail;
}
u->source->parent.process_msg = source_process_msg;
u->source->userdata = u;
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);
jack_set_process_callback(u->client, jack_process, u);
jack_on_shutdown(u->client, jack_shutdown, u);

View file

@ -3,7 +3,7 @@
/***
This file is part of PulseAudio.
Copyright 2004-2006 Lennart Poettering
Copyright 2004-2008 Lennart Poettering
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
@ -41,6 +41,7 @@
#include <pulsecore/thread-mq.h>
#include <pulsecore/rtpoll.h>
#include <pulsecore/sample-util.h>
#include <pulsecore/ltdl-helper.h>
#include "module-ladspa-sink-symdef.h"
#include "ladspa.h"
@ -60,6 +61,8 @@ PA_MODULE_USAGE(
"label=<ladspa plugin label> "
"control=<comma seperated list of input control values>");
#define MEMBLOCKQ_MAXLENGTH (16*1024*1024)
struct userdata {
pa_core *core;
pa_module *module;
@ -79,7 +82,7 @@ struct userdata {
about control out ports. We connect them all to this single buffer. */
LADSPA_Data control_out;
pa_memchunk memchunk;
pa_memblockq *memblockq;
};
static const char* const valid_modargs[] = {
@ -104,10 +107,14 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
case PA_SINK_MESSAGE_GET_LATENCY: {
pa_usec_t usec = 0;
/* Get the latency of the master sink */
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);
/* Add the latency internal to our sink input on top */
usec += pa_bytes_to_usec(pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq), &u->master->sample_spec);
*((pa_usec_t*) data) = usec;
return 0;
}
}
@ -122,110 +129,143 @@ static int sink_set_state(pa_sink *s, pa_sink_state_t state) {
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)))
if (PA_SINK_IS_LINKED(state) &&
u->sink_input &&
PA_SINK_INPUT_IS_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;
static void sink_request_rewind(pa_sink *s) {
struct userdata *u;
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);
pa_sink_assert_ref(s);
pa_assert_se(u = s->userdata);
/* 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);
/* Just hand this one over to the master sink */
pa_sink_input_request_rewind(u->sink_input, s->thread_info.rewind_nbytes + pa_memblockq_get_length(u->memblockq), TRUE, FALSE);
}
/* Called from I/O thread context */
static int sink_input_peek_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) {
static void sink_update_requested_latency(pa_sink *s) {
struct userdata *u;
pa_sink_assert_ref(s);
pa_assert_se(u = s->userdata);
/* Just hand this one over to the master sink */
pa_sink_input_set_requested_latency_within_thread(
u->sink_input,
pa_sink_get_requested_latency_within_thread(s));
}
/* Called from I/O thread context */
static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) {
struct userdata *u;
float *src, *dst;
size_t fs;
unsigned n, c;
pa_memchunk tchunk;
pa_sink_input_assert_ref(i);
pa_assert(chunk);
pa_assert_se(u = i->userdata);
if (!u->memchunk.memblock) {
pa_memchunk tchunk;
float *src, *dst;
size_t fs;
unsigned n, c;
if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state))
return -1;
pa_sink_render(u->sink, length, &tchunk);
while (pa_memblockq_peek(u->memblockq, &tchunk) < 0) {
pa_memchunk nchunk;
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 = PA_CLAMP_UNLIKELY(*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 = PA_CLAMP_UNLIKELY(*q, -1.0, 1.0);
}
pa_memblock_release(tchunk.memblock);
pa_memblock_release(u->memchunk.memblock);
pa_memblock_unref(tchunk.memblock);
pa_sink_render(u->sink, nbytes, &nchunk);
pa_memblockq_push(u->memblockq, &nchunk);
pa_memblock_unref(nchunk.memblock);
}
pa_assert(u->memchunk.length > 0);
pa_assert(u->memchunk.memblock);
pa_assert(tchunk.length > 0);
*chunk = u->memchunk;
pa_memblock_ref(chunk->memblock);
fs = pa_frame_size(&i->sample_spec);
n = PA_MIN(tchunk.length, u->block_size) / fs;
pa_assert(n > 0);
chunk->index = 0;
chunk->length = n*fs;
chunk->memblock = pa_memblock_new(i->sink->core->mempool, chunk->length);
pa_memblockq_drop(u->memblockq, chunk->length);
src = (float*) ((uint8_t*) pa_memblock_acquire(tchunk.memblock) + tchunk.index);
dst = (float*) pa_memblock_acquire(chunk->memblock);
for (c = 0; c < u->channels; c++) {
pa_sample_clamp(PA_SAMPLE_FLOAT32NE, u->input, sizeof(float), src+c, u->channels*sizeof(float), n);
u->descriptor->run(u->handle[c], n);
pa_sample_clamp(PA_SAMPLE_FLOAT32NE, dst+c, u->channels*sizeof(float), u->output, sizeof(float), n);
}
pa_memblock_release(tchunk.memblock);
pa_memblock_release(chunk->memblock);
pa_memblock_unref(tchunk.memblock);
return 0;
}
/* Called from I/O thread context */
static void sink_input_drop_cb(pa_sink_input *i, size_t length) {
static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
struct userdata *u;
pa_sink_input_assert_ref(i);
pa_assert_se(u = i->userdata);
pa_assert(length > 0);
pa_assert(nbytes > 0);
if (u->memchunk.memblock) {
if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state))
return;
if (length < u->memchunk.length) {
u->memchunk.index += length;
u->memchunk.length -= length;
return;
if (u->sink->thread_info.rewind_nbytes > 0) {
size_t max_rewrite, amount;
max_rewrite = nbytes + pa_memblockq_get_length(u->memblockq);
amount = PA_MIN(u->sink->thread_info.rewind_nbytes, max_rewrite);
u->sink->thread_info.rewind_nbytes = 0;
if (amount > 0) {
unsigned c;
pa_memblockq_seek(u->memblockq, - (int64_t) amount, PA_SEEK_RELATIVE);
pa_sink_process_rewind(u->sink, amount);
pa_log_debug("Resetting plugin");
/* Reset the plugin */
if (u->descriptor->deactivate)
for (c = 0; c < u->channels; c++)
u->descriptor->deactivate(u->handle[c]);
if (u->descriptor->activate)
for (c = 0; c < u->channels; c++)
u->descriptor->activate(u->handle[c]);
}
pa_memblock_unref(u->memchunk.memblock);
length -= u->memchunk.length;
pa_memchunk_reset(&u->memchunk);
}
if (length > 0)
pa_sink_skip(u->sink, length);
pa_memblockq_rewind(u->memblockq, nbytes);
}
/* Called from I/O thread context */
static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) {
struct userdata *u;
pa_sink_input_assert_ref(i);
pa_assert_se(u = i->userdata);
if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state))
return;
pa_memblockq_set_maxrewind(u->memblockq, nbytes);
pa_sink_set_max_rewind(u->sink, nbytes);
}
/* Called from I/O thread context */
@ -235,7 +275,12 @@ static void sink_input_detach_cb(pa_sink_input *i) {
pa_sink_input_assert_ref(i);
pa_assert_se(u = i->userdata);
if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state))
return;
pa_sink_detach_within_thread(u->sink);
pa_sink_set_asyncmsgq(u->sink, NULL);
pa_sink_set_rtpoll(u->sink, NULL);
}
/* Called from I/O thread context */
@ -245,10 +290,15 @@ static void sink_input_attach_cb(pa_sink_input *i) {
pa_sink_input_assert_ref(i);
pa_assert_se(u = i->userdata);
if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state))
return;
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);
u->sink->max_latency = u->master->max_latency;
u->sink->min_latency = u->master->min_latency;
}
/* Called from main context */
@ -258,25 +308,43 @@ static void sink_input_kill_cb(pa_sink_input *i) {
pa_sink_input_assert_ref(i);
pa_assert_se(u = i->userdata);
pa_sink_unlink(u->sink);
pa_sink_input_unlink(u->sink_input);
pa_sink_unref(u->sink);
u->sink = NULL;
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);
}
/* Called from IO thread context */
static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t state) {
struct userdata *u;
pa_sink_input_assert_ref(i);
pa_assert_se(u = i->userdata);
/* If we are added for the first time, ask for a rewinding so that
* we are heard right-away. */
if (PA_SINK_INPUT_IS_LINKED(state) &&
i->thread_info.state == PA_SINK_INPUT_INIT) {
pa_log_debug("Requesting rewind due to state change.");
pa_sink_input_request_rewind(i, 0, FALSE, TRUE);
}
}
int pa__init(pa_module*m) {
struct userdata *u;
pa_sample_spec ss;
pa_channel_map map;
pa_modargs *ma;
char *t;
const char *z;
pa_sink *master;
pa_sink_input_new_data data;
pa_sink_input_new_data sink_input_data;
pa_sink_new_data sink_data;
const char *plugin, *label;
LADSPA_Descriptor_Function descriptor_func;
const char *e, *cdata;
@ -284,7 +352,6 @@ int pa__init(pa_module*m) {
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);
@ -325,7 +392,9 @@ int pa__init(pa_module*m) {
u->module = m;
m->userdata = u;
u->master = master;
pa_memchunk_reset(&u->memchunk);
u->sink = NULL;
u->sink_input = NULL;
u->memblockq = pa_memblockq_new(0, MEMBLOCKQ_MAXLENGTH, 0, pa_frame_size(&ss), 1, 1, 0, NULL);
if (!(e = getenv("LADSPA_PATH")))
e = LADSPA_PATH;
@ -342,7 +411,7 @@ int pa__init(pa_module*m) {
goto fail;
}
if (!(descriptor_func = (LADSPA_Descriptor_Function) lt_dlsym(m->dl, "ladspa_descriptor"))) {
if (!(descriptor_func = (LADSPA_Descriptor_Function) pa_load_sym(m->dl, NULL, "ladspa_descriptor"))) {
pa_log("LADSPA module lacks ladspa_descriptor() symbol.");
goto fail;
}
@ -350,7 +419,7 @@ int pa__init(pa_module*m) {
for (j = 0;; j++) {
if (!(d = descriptor_func(j))) {
pa_log("Failed to find plugin label '%s' in plugin '%s'.", plugin, label);
pa_log("Failed to find plugin label '%s' in plugin '%s'.", label, plugin);
goto fail;
}
@ -582,43 +651,66 @@ int pa__init(pa_module*m) {
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_sink_new_data_init(&sink_data);
sink_data.driver = __FILE__;
sink_data.module = m;
if (!(sink_data.name = pa_xstrdup(pa_modargs_get_value(ma, "sink_name", NULL))))
sink_data.name = pa_sprintf_malloc("%s.ladspa", master->name);
sink_data.namereg_fail = FALSE;
pa_sink_new_data_set_sample_spec(&sink_data, &ss);
pa_sink_new_data_set_channel_map(&sink_data, &map);
z = pa_proplist_gets(master->proplist, PA_PROP_DEVICE_DESCRIPTION);
pa_proplist_setf(sink_data.proplist, PA_PROP_DEVICE_DESCRIPTION, "LADSPA Plugin %s on %s", label, z ? z : master->name);
pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_MASTER_DEVICE, master->name);
pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_CLASS, "filter");
pa_proplist_sets(sink_data.proplist, "device.ladspa.module", plugin);
pa_proplist_sets(sink_data.proplist, "device.ladspa.label", d->Label);
pa_proplist_sets(sink_data.proplist, "device.ladspa.name", d->Name);
pa_proplist_sets(sink_data.proplist, "device.ladspa.maker", d->Maker);
pa_proplist_sets(sink_data.proplist, "device.ladspa.copyright", d->Copyright);
pa_proplist_setf(sink_data.proplist, "device.ladspa.unique_id", "%lu", (unsigned long) d->UniqueID);
u->sink = pa_sink_new(m->core, &sink_data, PA_SINK_LATENCY);
pa_sink_new_data_done(&sink_data);
if (!u->sink) {
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->update_requested_latency = sink_update_requested_latency;
u->sink->request_rewind = sink_request_rewind;
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;
pa_sink_input_new_data_init(&sink_input_data);
sink_input_data.driver = __FILE__;
sink_input_data.module = m;
sink_input_data.sink = u->master;
pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_NAME, "LADSPA Stream");
pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "filter");
pa_sink_input_new_data_set_sample_spec(&sink_input_data, &ss);
pa_sink_input_new_data_set_channel_map(&sink_input_data, &map);
if (!(u->sink_input = pa_sink_input_new(m->core, &data, PA_SINK_INPUT_DONT_MOVE)))
u->sink_input = pa_sink_input_new(m->core, &sink_input_data, PA_SINK_INPUT_DONT_MOVE);
pa_sink_input_new_data_done(&sink_input_data);
if (!u->sink_input)
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->pop = sink_input_pop_cb;
u->sink_input->process_rewind = sink_input_process_rewind_cb;
u->sink_input->update_max_rewind = sink_input_update_max_rewind_cb;
u->sink_input->kill = sink_input_kill_cb;
u->sink_input->attach = sink_input_attach_cb;
u->sink_input->detach = sink_input_detach_cb;
u->sink_input->state_change = sink_input_state_change_cb;
u->sink_input->userdata = u;
pa_sink_put(u->sink);
@ -627,7 +719,6 @@ int pa__init(pa_module*m) {
pa_modargs_free(ma);
pa_xfree(use_default);
pa_xfree(default_sink_name);
return 0;
@ -636,7 +727,6 @@ fail:
pa_modargs_free(ma);
pa_xfree(use_default);
pa_xfree(default_sink_name);
pa__done(m);
@ -652,18 +742,15 @@ void pa__done(pa_module*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);
if (u->sink_input) {
pa_sink_input_unlink(u->sink_input);
pa_sink_input_unref(u->sink_input);
}
for (c = 0; c < u->channels; c++)
if (u->handle[c]) {
@ -675,6 +762,9 @@ void pa__done(pa_module*m) {
if (u->output != u->input)
pa_xfree(u->output);
if (u->memblockq)
pa_memblockq_free(u->memblockq);
pa_xfree(u->input);
pa_xfree(u->control);

View file

@ -82,12 +82,14 @@ static int load_rules(struct userdata *u, const char *filename) {
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");
if (filename)
f = fopen(fn = pa_xstrdup(filename), "r");
else
f = pa_open_config_file(DEFAULT_MATCH_TABLE_FILE, DEFAULT_MATCH_TABLE_FILE_USER, NULL, &fn);
if (!f) {
pa_log("failed to open file '%s': %s", fn, pa_cstrerror(errno));
pa_xfree(fn);
pa_log("Failed to open file config file: %s", pa_cstrerror(errno));
goto finish;
}
@ -166,6 +168,7 @@ 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;
const char *n;
pa_assert(c);
pa_assert(u);
@ -176,13 +179,13 @@ static void callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, v
if (!(si = pa_idxset_get_by_index(c->sink_inputs, idx)))
return;
if (!si->name)
if (!(n = pa_proplist_gets(si->proplist, PA_PROP_MEDIA_NAME)))
return;
for (r = u->rules; r; r = r->next) {
if (!regexec(&r->regex, si->name, 0, NULL, 0)) {
if (!regexec(&r->regex, n, 0, NULL, 0)) {
pa_cvolume cv;
pa_log_debug("changing volume of sink input '%s' to 0x%03x", si->name, r->volume);
pa_log_debug("changing volume of sink input '%s' to 0x%03x", n, r->volume);
pa_cvolume_set(&cv, si->sample_spec.channels, r->volume);
pa_sink_input_set_volume(si, &cv);
}

View file

@ -3,7 +3,7 @@
/***
This file is part of PulseAudio.
Copyright 2004-2006 Lennart Poettering
Copyright 2004-2008 Lennart Poettering
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
@ -64,6 +64,7 @@ PA_MODULE_USAGE(
"description=<description for the sink>");
#define DEFAULT_SINK_NAME "null"
#define MAX_LATENCY_USEC (PA_USEC_PER_SEC * 2)
struct userdata {
pa_core *core;
@ -76,7 +77,8 @@ struct userdata {
size_t block_size;
struct timeval timestamp;
pa_usec_t block_usec;
pa_usec_t timestamp;
};
static const char* const valid_modargs[] = {
@ -96,26 +98,95 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
case PA_SINK_MESSAGE_SET_STATE:
if (PA_PTR_TO_UINT(data) == PA_SINK_RUNNING)
pa_rtclock_get(&u->timestamp);
u->timestamp = pa_rtclock_usec();
break;
case PA_SINK_MESSAGE_GET_LATENCY: {
struct timeval now;
pa_usec_t now;
pa_rtclock_get(&now);
now = pa_rtclock_usec();
*((pa_usec_t*) data) = u->timestamp > now ? u->timestamp - now : 0;
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 0;
}
}
return pa_sink_process_msg(o, code, data, offset, chunk);
}
static void sink_update_requested_latency_cb(pa_sink *s) {
struct userdata *u;
pa_sink_assert_ref(s);
u = s->userdata;
pa_assert(u);
u->block_usec = pa_sink_get_requested_latency_within_thread(s);
}
static void process_rewind(struct userdata *u, pa_usec_t now) {
size_t rewind_nbytes, in_buffer;
pa_usec_t delay;
pa_assert(u);
/* Figure out how much we shall rewind and reset the counter */
rewind_nbytes = u->sink->thread_info.rewind_nbytes;
u->sink->thread_info.rewind_nbytes = 0;
pa_assert(rewind_nbytes > 0);
pa_log_debug("Requested to rewind %lu bytes.", (unsigned long) rewind_nbytes);
if (u->timestamp <= now)
return;
delay = u->timestamp - now;
in_buffer = pa_usec_to_bytes(delay, &u->sink->sample_spec);
if (in_buffer <= 0)
return;
if (rewind_nbytes > in_buffer)
rewind_nbytes = in_buffer;
pa_sink_process_rewind(u->sink, rewind_nbytes);
u->timestamp -= pa_bytes_to_usec(rewind_nbytes, &u->sink->sample_spec);
pa_log_debug("Rewound %lu bytes.", (unsigned long) rewind_nbytes);
}
static void process_render(struct userdata *u, pa_usec_t now) {
size_t nbytes;
size_t ate = 0;
pa_assert(u);
/* This is the configured latency. Sink inputs connected to us
might not have a single frame more than this value queued. Hence:
at maximum read this many bytes from the sink inputs. */
nbytes = pa_usec_to_bytes(u->block_usec, &u->sink->sample_spec);
/* Fill the buffer up the the latency size */
while (u->timestamp < now + u->block_usec) {
pa_memchunk chunk;
pa_sink_render(u->sink, nbytes, &chunk);
pa_memblock_unref(chunk.memblock);
pa_log_debug("Ate %lu bytes.", (unsigned long) chunk.length);
u->timestamp += pa_bytes_to_usec(chunk.length, &u->sink->sample_spec);
ate += chunk.length;
if (ate >= nbytes)
break;
}
pa_log_debug("Ate in sum %lu bytes (of %lu)", (unsigned long) ate, (unsigned long) nbytes);
}
static void thread_func(void *userdata) {
struct userdata *u = userdata;
@ -126,28 +197,29 @@ static void thread_func(void *userdata) {
pa_thread_mq_install(&u->thread_mq);
pa_rtpoll_install(u->rtpoll);
pa_rtclock_get(&u->timestamp);
u->timestamp = pa_rtclock_usec();
for (;;) {
int ret;
/* Render some data and drop it immediately */
if (u->sink->thread_info.state == PA_SINK_RUNNING) {
struct timeval now;
pa_usec_t now;
pa_rtclock_get(&now);
now = pa_rtclock_usec();
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));
}
if (u->sink->thread_info.rewind_nbytes > 0)
process_rewind(u, now);
pa_rtpoll_set_timer_absolute(u->rtpoll, &u->timestamp);
if (u->timestamp <= now)
process_render(u, now);
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)
if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0)
goto fail;
if (ret == 0)
@ -169,6 +241,7 @@ int pa__init(pa_module*m) {
pa_sample_spec ss;
pa_channel_map map;
pa_modargs *ma = NULL;
pa_sink_new_data data;
pa_assert(m);
@ -187,27 +260,35 @@ int pa__init(pa_module*m) {
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);
pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);
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.");
pa_sink_new_data_init(&data);
data.driver = __FILE__;
data.module = m;
pa_sink_new_data_set_name(&data, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME));
pa_sink_new_data_set_sample_spec(&data, &ss);
pa_sink_new_data_set_channel_map(&data, &map);
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_DESCRIPTION, pa_modargs_get_value(ma, "description", "Null Output"));
u->sink = pa_sink_new(m->core, &data, PA_SINK_LATENCY);
pa_sink_new_data_done(&data);
if (!u->sink) {
pa_log("Failed to create sink object.");
goto fail;
}
u->sink->parent.process_msg = sink_process_msg;
u->sink->update_requested_latency = sink_update_requested_latency_cb;
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);
pa_sink_set_description(u->sink, pa_modargs_get_value(ma, "description", "NULL sink"));
u->block_size = pa_bytes_per_second(&ss) / 20; /* 50 ms */
if (u->block_size <= 0)
u->block_size = pa_frame_size(&ss);
u->block_usec = u->sink->max_latency = MAX_LATENCY_USEC;
u->sink->thread_info.max_rewind = pa_usec_to_bytes(u->block_usec, &u->sink->sample_spec);
if (!(u->thread = pa_thread_new(thread_func, u))) {
pa_log("Failed to create thread.");

View file

@ -161,10 +161,10 @@ static void trigger(struct userdata *u, pa_bool_t quick) {
pa_log_debug("trigger");
if (u->source && PA_SOURCE_OPENED(u->source->thread_info.state))
if (u->source && PA_SOURCE_IS_OPENED(u->source->thread_info.state))
enable_bits |= PCM_ENABLE_INPUT;
if (u->sink && PA_SINK_OPENED(u->sink->thread_info.state))
if (u->sink && PA_SINK_IS_OPENED(u->sink->thread_info.state))
enable_bits |= PCM_ENABLE_OUTPUT;
pa_log_debug("trigger: %i", enable_bits);
@ -202,7 +202,7 @@ static void trigger(struct userdata *u, pa_bool_t quick) {
* register the fd as ready.
*/
if (u->source && PA_SOURCE_OPENED(u->source->thread_info.state)) {
if (u->source && PA_SOURCE_IS_OPENED(u->source->thread_info.state)) {
uint8_t *buf = pa_xnew(uint8_t, u->in_fragment_size);
pa_read(u->fd, buf, u->in_fragment_size, NULL);
pa_xfree(buf);
@ -641,7 +641,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
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_assert(PA_SINK_IS_OPENED(u->sink->thread_info.state));
if (!u->source || u->source_suspended) {
if (suspend(u) < 0)
@ -658,7 +658,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
if (u->sink->thread_info.state == PA_SINK_INIT) {
do_trigger = TRUE;
quick = u->source && PA_SOURCE_OPENED(u->source->thread_info.state);
quick = u->source && PA_SOURCE_IS_OPENED(u->source->thread_info.state);
}
if (u->sink->thread_info.state == PA_SINK_SUSPENDED) {
@ -721,7 +721,7 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off
switch ((pa_source_state_t) PA_PTR_TO_UINT(data)) {
case PA_SOURCE_SUSPENDED:
pa_assert(PA_SOURCE_OPENED(u->source->thread_info.state));
pa_assert(PA_SOURCE_IS_OPENED(u->source->thread_info.state));
if (!u->sink || u->sink_suspended) {
if (suspend(u) < 0)
@ -738,7 +738,7 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off
if (u->source->thread_info.state == PA_SOURCE_INIT) {
do_trigger = TRUE;
quick = u->sink && PA_SINK_OPENED(u->sink->thread_info.state);
quick = u->sink && PA_SINK_IS_OPENED(u->sink->thread_info.state);
}
if (u->source->thread_info.state == PA_SOURCE_SUSPENDED) {
@ -877,7 +877,7 @@ static void thread_func(void *userdata) {
/* Render some data and write it to the dsp */
if (u->sink && PA_SINK_OPENED(u->sink->thread_info.state) && ((revents & POLLOUT) || u->use_mmap || u->use_getospace)) {
if (u->sink && PA_SINK_IS_OPENED(u->sink->thread_info.state) && ((revents & POLLOUT) || u->use_mmap || u->use_getospace)) {
if (u->use_mmap) {
@ -985,7 +985,7 @@ static void thread_func(void *userdata) {
/* 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) || u->use_mmap || u->use_getispace)) {
if (u->source && PA_SOURCE_IS_OPENED(u->source->thread_info.state) && ((revents & POLLIN) || u->use_mmap || u->use_getispace)) {
if (u->use_mmap) {
@ -1095,8 +1095,8 @@ static void thread_func(void *userdata) {
pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
pollfd->events =
((u->source && PA_SOURCE_OPENED(u->source->thread_info.state)) ? POLLIN : 0) |
((u->sink && PA_SINK_OPENED(u->sink->thread_info.state)) ? POLLOUT : 0);
((u->source && PA_SOURCE_IS_OPENED(u->source->thread_info.state)) ? POLLIN : 0) |
((u->sink && PA_SINK_IS_OPENED(u->sink->thread_info.state)) ? POLLOUT : 0);
}
/* Hmm, nothing to do. Let's sleep */
@ -1143,9 +1143,11 @@ int pa__init(pa_module*m) {
pa_sample_spec ss;
pa_channel_map map;
pa_modargs *ma = NULL;
char hwdesc[64], *t;
char hwdesc[64];
const char *name;
int namereg_fail;
pa_bool_t namereg_fail;
pa_sink_new_data sink_new_data;
pa_source_new_data source_new_data;
pa_assert(m);
@ -1226,17 +1228,16 @@ int pa__init(pa_module*m) {
m->userdata = u;
u->fd = fd;
u->mixer_fd = -1;
u->use_getospace = u->use_getispace = 1;
u->use_getodelay = 1;
u->use_getospace = u->use_getispace = TRUE;
u->use_getodelay = TRUE;
u->mode = mode;
u->frame_size = pa_frame_size(&ss);
u->device_name = pa_xstrdup(dev);
u->in_nfrags = u->out_nfrags = u->nfrags = nfrags;
u->out_fragment_size = u->in_fragment_size = u->frag_size = frag_size;
u->use_mmap = use_mmap;
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);
pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);
u->rtpoll_item = NULL;
build_pollfd(u);
@ -1244,14 +1245,14 @@ int pa__init(pa_module*m) {
pa_log_info("Input -- %u fragments of size %u.", info.fragstotal, info.fragsize);
u->in_fragment_size = info.fragsize;
u->in_nfrags = info.fragstotal;
u->use_getispace = 1;
u->use_getispace = TRUE;
}
if (ioctl(fd, SNDCTL_DSP_GETOSPACE, &info) >= 0) {
pa_log_info("Output -- %u fragments of size %u.", info.fragstotal, info.fragsize);
u->out_fragment_size = info.fragsize;
u->out_nfrags = info.fragstotal;
u->use_getospace = 1;
u->use_getospace = TRUE;
}
u->in_hwbuf_size = u->in_nfrags * u->in_fragment_size;
@ -1263,21 +1264,37 @@ int pa__init(pa_module*m) {
if (use_mmap) {
if ((u->in_mmap = mmap(NULL, u->in_hwbuf_size, PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) {
pa_log_warn("mmap(PROT_READ) failed, reverting to non-mmap mode: %s", pa_cstrerror(errno));
use_mmap = u->use_mmap = 0;
use_mmap = u->use_mmap = FALSE;
u->in_mmap = NULL;
} else
pa_log_debug("Successfully mmap()ed input buffer.");
}
if ((name = pa_modargs_get_value(ma, "source_name", NULL)))
namereg_fail = 1;
namereg_fail = TRUE;
else {
name = name_buf = pa_sprintf_malloc("oss_input.%s", pa_path_get_filename(dev));
namereg_fail = 0;
namereg_fail = FALSE;
}
u->source = pa_source_new(m->core, __FILE__, name, namereg_fail, &ss, &map);
pa_source_new_data_init(&source_new_data);
source_new_data.driver = __FILE__;
source_new_data.module = m;
pa_source_new_data_set_name(&source_new_data, name);
source_new_data.namereg_fail = namereg_fail;
pa_source_new_data_set_sample_spec(&source_new_data, &ss);
pa_source_new_data_set_channel_map(&source_new_data, &map);
pa_proplist_sets(source_new_data.proplist, PA_PROP_DEVICE_STRING, dev);
pa_proplist_sets(source_new_data.proplist, PA_PROP_DEVICE_API, "oss");
pa_proplist_sets(source_new_data.proplist, PA_PROP_DEVICE_DESCRIPTION, hwdesc[0] ? hwdesc : dev);
pa_proplist_sets(source_new_data.proplist, PA_PROP_DEVICE_ACCESS_MODE, use_mmap ? "mmap" : "serial");
pa_proplist_setf(source_new_data.proplist, PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE, "%lu", (unsigned long) (u->in_hwbuf_size));
pa_proplist_setf(source_new_data.proplist, PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE, "%lu", (unsigned long) (u->in_fragment_size));
u->source = pa_source_new(m->core, &source_new_data, PA_SOURCE_HARDWARE|PA_SOURCE_LATENCY);
pa_source_new_data_done(&source_new_data);
pa_xfree(name_buf);
if (!u->source) {
pa_log("Failed to create source object");
goto fail;
@ -1286,18 +1303,8 @@ int pa__init(pa_module*m) {
u->source->parent.process_msg = source_process_msg;
u->source->userdata = u;
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(
"OSS PCM on %s%s%s%s%s",
dev,
hwdesc[0] ? " (" : "",
hwdesc[0] ? hwdesc : "",
hwdesc[0] ? ")" : "",
use_mmap ? " via DMA" : ""));
pa_xfree(t);
u->source->flags = PA_SOURCE_HARDWARE|PA_SOURCE_LATENCY;
u->source->refresh_volume = TRUE;
if (use_mmap)
@ -1315,7 +1322,7 @@ int pa__init(pa_module*m) {
goto go_on;
} else {
pa_log_warn("mmap(PROT_WRITE) failed, reverting to non-mmap mode: %s", pa_cstrerror(errno));
u->use_mmap = (use_mmap = FALSE);
u->use_mmap = use_mmap = FALSE;
u->out_mmap = NULL;
}
} else {
@ -1325,14 +1332,30 @@ int pa__init(pa_module*m) {
}
if ((name = pa_modargs_get_value(ma, "sink_name", NULL)))
namereg_fail = 1;
namereg_fail = TRUE;
else {
name = name_buf = pa_sprintf_malloc("oss_output.%s", pa_path_get_filename(dev));
namereg_fail = 0;
namereg_fail = FALSE;
}
u->sink = pa_sink_new(m->core, __FILE__, name, namereg_fail, &ss, &map);
pa_sink_new_data_init(&sink_new_data);
sink_new_data.driver = __FILE__;
sink_new_data.module = m;
pa_sink_new_data_set_name(&sink_new_data, name);
sink_new_data.namereg_fail = namereg_fail;
pa_sink_new_data_set_sample_spec(&sink_new_data, &ss);
pa_sink_new_data_set_channel_map(&sink_new_data, &map);
pa_proplist_sets(sink_new_data.proplist, PA_PROP_DEVICE_STRING, dev);
pa_proplist_sets(sink_new_data.proplist, PA_PROP_DEVICE_API, "oss");
pa_proplist_sets(sink_new_data.proplist, PA_PROP_DEVICE_DESCRIPTION, hwdesc[0] ? hwdesc : dev);
pa_proplist_sets(sink_new_data.proplist, PA_PROP_DEVICE_ACCESS_MODE, use_mmap ? "mmap" : "serial");
pa_proplist_setf(sink_new_data.proplist, PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE, "%lu", (unsigned long) (u->out_hwbuf_size));
pa_proplist_setf(sink_new_data.proplist, PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE, "%lu", (unsigned long) (u->out_fragment_size));
u->sink = pa_sink_new(m->core, &sink_new_data, PA_SINK_HARDWARE|PA_SINK_LATENCY);
pa_sink_new_data_done(&sink_new_data);
pa_xfree(name_buf);
if (!u->sink) {
pa_log("Failed to create sink object");
goto fail;
@ -1341,18 +1364,8 @@ int pa__init(pa_module*m) {
u->sink->parent.process_msg = sink_process_msg;
u->sink->userdata = u;
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(
"OSS PCM on %s%s%s%s%s",
dev,
hwdesc[0] ? " (" : "",
hwdesc[0] ? hwdesc : "",
hwdesc[0] ? ")" : "",
use_mmap ? " via DMA" : ""));
pa_xfree(t);
u->sink->flags = PA_SINK_HARDWARE|PA_SINK_LATENCY;
u->sink->refresh_volume = TRUE;
if (use_mmap)
@ -1360,7 +1373,7 @@ int pa__init(pa_module*m) {
}
if ((u->mixer_fd = pa_oss_open_mixer_for_device(u->device_name)) >= 0) {
int do_close = 1;
pa_bool_t do_close = TRUE;
u->mixer_devmask = 0;
if (ioctl(fd, SOUND_MIXER_READ_DEVMASK, &u->mixer_devmask) < 0)
@ -1372,7 +1385,7 @@ int pa__init(pa_module*m) {
u->sink->flags |= PA_SINK_HW_VOLUME_CTRL;
u->sink->get_volume = sink_get_volume;
u->sink->set_volume = sink_set_volume;
do_close = 0;
do_close = FALSE;
}
if (u->source && (u->mixer_devmask & (SOUND_MASK_RECLEV|SOUND_MASK_IGAIN))) {
@ -1380,7 +1393,7 @@ int pa__init(pa_module*m) {
u->source->flags |= PA_SOURCE_HW_VOLUME_CTRL;
u->source->get_volume = source_get_volume;
u->source->set_volume = source_set_volume;
do_close = 0;
do_close = FALSE;
}
}
@ -1402,10 +1415,25 @@ go_on:
}
/* Read mixer settings */
if (u->sink && u->sink->get_volume)
sink_get_volume(u->sink);
if (u->source && u->source->get_volume)
source_get_volume(u->source);
if (u->sink) {
if (sink_new_data.volume_is_set) {
if (u->sink->set_volume)
u->sink->set_volume(u->sink);
} else {
if (u->sink->get_volume)
u->sink->get_volume(u->sink);
}
}
if (u->source) {
if (source_new_data.volume_is_set) {
if (u->source->set_volume)
u->source->set_volume(u->source);
} else {
if (u->source->get_volume)
u->source->get_volume(u->source);
}
}
if (u->sink)
pa_sink_put(u->sink);

View file

@ -62,7 +62,7 @@ PA_MODULE_USAGE(
"rate=<sample rate>"
"channel_map=<channel map>");
#define DEFAULT_FILE_NAME "/tmp/music.output"
#define DEFAULT_FILE_NAME "fifo_output"
#define DEFAULT_SINK_NAME "fifo_output"
struct userdata {
@ -80,6 +80,8 @@ struct userdata {
pa_memchunk memchunk;
pa_rtpoll_item *rtpoll_item;
int write_type;
};
static const char* const valid_modargs[] = {
@ -109,16 +111,64 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
n += u->memchunk.length;
*((pa_usec_t*) data) = pa_bytes_to_usec(n, &u->sink->sample_spec);
break;
return 0;
}
}
return pa_sink_process_msg(o, code, data, offset, chunk);
}
static void process_rewind(struct userdata *u) {
pa_assert(u);
pa_log_debug("Rewind requested but not supported by pipe sink. Ignoring.");
u->sink->thread_info.rewind_nbytes = 0;
}
static int process_render(struct userdata *u) {
pa_assert(u);
if (u->memchunk.length <= 0)
pa_sink_render(u->sink, PIPE_BUF, &u->memchunk);
pa_assert(u->memchunk.length > 0);
for (;;) {
ssize_t l;
void *p;
p = pa_memblock_acquire(u->memchunk.memblock);
l = pa_write(u->fd, (uint8_t*) p + u->memchunk.index, u->memchunk.length, &u->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));
return -1;
}
} 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);
}
}
return 0;
}
}
static void thread_func(void *userdata) {
struct userdata *u = userdata;
int write_type = 0;
pa_assert(u);
@ -134,39 +184,14 @@ static void thread_func(void *userdata) {
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->sink->thread_info.state == PA_SINK_RUNNING) {
if (u->memchunk.length <= 0)
pa_sink_render(u->sink, PIPE_BUF, &u->memchunk);
if (u->sink->thread_info.rewind_nbytes > 0)
process_rewind(u);
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));
if (pollfd->revents) {
if (process_render(u) < 0)
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;
}
@ -205,8 +230,8 @@ int pa__init(pa_module*m) {
pa_sample_spec ss;
pa_channel_map map;
pa_modargs *ma;
char *t;
struct pollfd *pollfd;
pa_sink_new_data data;
pa_assert(m);
@ -226,11 +251,11 @@ int pa__init(pa_module*m) {
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);
pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);
u->write_type = 0;
u->filename = pa_xstrdup(pa_modargs_get_value(ma, "file", DEFAULT_FILE_NAME));
u->filename = pa_runtime_path(pa_modargs_get_value(ma, "file", DEFAULT_FILE_NAME));
mkfifo(u->filename, 0666);
if ((u->fd = open(u->filename, O_RDWR|O_NOCTTY)) < 0) {
@ -251,20 +276,28 @@ int pa__init(pa_module*m) {
goto fail;
}
if (!(u->sink = pa_sink_new(m->core, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss, &map))) {
pa_sink_new_data_init(&data);
data.driver = __FILE__;
data.module = m;
pa_sink_new_data_set_name(&data, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME));
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, u->filename);
pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Unix FIFO sink %s", u->filename);
pa_sink_new_data_set_sample_spec(&data, &ss);
pa_sink_new_data_set_channel_map(&data, &map);
u->sink = pa_sink_new(m->core, &data, PA_SINK_LATENCY);
pa_sink_new_data_done(&data);
if (!u->sink) {
pa_log("Failed to create sink.");
goto fail;
}
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);
pa_sink_set_description(u->sink, t = pa_sprintf_malloc("Unix FIFO sink '%s'", u->filename));
pa_xfree(t);
u->rtpoll_item = pa_rtpoll_item_new(u->rtpoll, PA_RTPOLL_NEVER, 1);
pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);

View file

@ -153,13 +153,14 @@ static void thread_func(void *userdata) {
/* 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)
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 & ~POLLIN) {
pa_log("FIFO shutdown.");
goto fail;
@ -182,8 +183,8 @@ int pa__init(pa_module*m) {
pa_sample_spec ss;
pa_channel_map map;
pa_modargs *ma;
char *t;
struct pollfd *pollfd;
pa_source_new_data data;
pa_assert(m);
@ -203,11 +204,10 @@ int pa__init(pa_module*m) {
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);
pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);
u->filename = pa_xstrdup(pa_modargs_get_value(ma, "file", DEFAULT_FILE_NAME));
u->filename = pa_runtime_path(pa_modargs_get_value(ma, "file", DEFAULT_FILE_NAME));
mkfifo(u->filename, 0666);
if ((u->fd = open(u->filename, O_RDWR|O_NOCTTY)) < 0) {
@ -228,19 +228,27 @@ int pa__init(pa_module*m) {
goto fail;
}
if (!(u->source = pa_source_new(m->core, __FILE__, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME), 0, &ss, &map))) {
pa_source_new_data_init(&data);
data.driver = __FILE__;
data.module = m;
pa_source_new_data_set_name(&data, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME));
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, u->filename);
pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Unix FIFO source %s", u->filename);
pa_source_new_data_set_sample_spec(&data, &ss);
pa_source_new_data_set_channel_map(&data, &map);
u->source = pa_source_new(m->core, &data, 0);
pa_source_new_data_done(&data);
if (!u->source) {
pa_log("Failed to create source.");
goto fail;
}
u->source->userdata = u;
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->rtpoll_item = pa_rtpoll_item_new(u->rtpoll, PA_RTPOLL_NEVER, 1);
pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);

View file

@ -215,15 +215,6 @@ int pa__init(pa_module*m) {
#else
pa_socket_server *s;
int r;
char tmp[PATH_MAX];
#if defined(USE_PROTOCOL_ESOUND)
#if defined(USE_PER_USER_ESOUND_SOCKET)
char esdsocketpath[PATH_MAX];
#else
const char esdsocketpath[] = "/tmp/.esd/socket";
#endif
#endif
#endif
pa_assert(m);
@ -255,27 +246,28 @@ int pa__init(pa_module*m) {
goto fail;
if (s_ipv4)
if (!(u->protocol_ipv4 = protocol_new(m->core, s_ipv4, m, ma)))
pa_socket_server_unref(s_ipv4);
u->protocol_ipv4 = protocol_new(m->core, s_ipv4, m, ma);
if (s_ipv6)
if (!(u->protocol_ipv6 = protocol_new(m->core, s_ipv6, m, ma)))
pa_socket_server_unref(s_ipv6);
u->protocol_ipv6 = protocol_new(m->core, s_ipv6, m, ma);
if (!u->protocol_ipv4 && !u->protocol_ipv6)
goto fail;
if (s_ipv6)
pa_socket_server_unref(s_ipv6);
if (s_ipv6)
pa_socket_server_unref(s_ipv4);
#else
#if defined(USE_PROTOCOL_ESOUND)
#if defined(USE_PER_USER_ESOUND_SOCKET)
snprintf(esdsocketpath, sizeof(esdsocketpath), "/tmp/.esd-%lu/socket", (unsigned long) getuid());
u->socket_path = pa_sprintf_malloc("/tmp/.esd-%lu/socket", (unsigned long) getuid());
#else
u->socket_path = pa_xstrdup("/tmp/.esd/socket");
#endif
pa_runtime_path(pa_modargs_get_value(ma, "socket", esdsocketpath), 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 */
@ -285,24 +277,26 @@ int pa__init(pa_module*m) {
}
#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) {
pa_log("Failed to remove stale UNIX socket '%s': %s", tmp, pa_cstrerror(errno));
if (!(u->socket_path = pa_runtime_path(pa_modargs_get_value(ma, "socket", UNIX_SOCKET)))) {
pa_log("Failed to generate socket path.");
goto fail;
}
#endif
if (r)
pa_log("Removed stale UNIX socket '%s'.", tmp);
if ((r = pa_unix_socket_remove_stale(u->socket_path)) < 0) {
pa_log("Failed to remove stale UNIX socket '%s': %s", u->socket_path, pa_cstrerror(errno));
goto fail;
} else if (r > 0)
pa_log_info("Removed stale UNIX socket '%s'.", u->socket_path);
if (!(s = pa_socket_server_new_unix(m->core->mainloop, tmp)))
if (!(s = pa_socket_server_new_unix(m->core->mainloop, u->socket_path)))
goto fail;
if (!(u->protocol_unix = protocol_new(m->core, s, m, ma)))
goto fail;
pa_socket_server_unref(s);
#endif
m->userdata = u;
@ -325,24 +319,22 @@ fail:
#else
if (u->protocol_unix)
protocol_free(u->protocol_unix);
if (u->socket_path)
pa_xfree(u->socket_path);
pa_xfree(u->socket_path);
#endif
pa_xfree(u);
} else {
#if defined(USE_TCP_SOCKETS)
if (s_ipv4)
pa_socket_server_unref(s_ipv4);
if (s_ipv6)
pa_socket_server_unref(s_ipv6);
#else
if (s)
pa_socket_server_unref(s);
#endif
}
#if defined(USE_TCP_SOCKETS)
if (s_ipv4)
pa_socket_server_unref(s_ipv4);
if (s_ipv6)
pa_socket_server_unref(s_ipv6);
#else
if (s)
pa_socket_server_unref(s);
#endif
goto finish;
}
@ -362,7 +354,7 @@ void pa__done(pa_module*m) {
if (u->protocol_unix)
protocol_free(u->protocol_unix);
#if defined(USE_PROTOCOL_ESOUND)
#if defined(USE_PROTOCOL_ESOUND) && !defined(USE_PER_USER_ESOUND_SOCKET)
if (u->socket_path) {
char *p = pa_parent_dir(u->socket_path);
rmdir(p);

View file

@ -3,7 +3,7 @@
/***
This file is part of PulseAudio.
Copyright 2004-2006 Lennart Poettering
Copyright 2004-2008 Lennart Poettering
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
@ -59,8 +59,6 @@ struct userdata {
pa_sink *sink, *master;
pa_sink_input *sink_input;
pa_memchunk memchunk;
};
static const char* const valid_modargs[] = {
@ -83,10 +81,14 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
case PA_SINK_MESSAGE_GET_LATENCY: {
pa_usec_t usec = 0;
/* Get the latency of the master sink */
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);
/* Add the latency internal to our sink input on top */
usec += pa_bytes_to_usec(pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq), &u->master->sample_spec);
*((pa_usec_t*) data) = usec;
return 0;
}
}
@ -101,67 +103,86 @@ static int sink_set_state(pa_sink *s, pa_sink_state_t state) {
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)))
if (PA_SINK_IS_LINKED(state) &&
u->sink_input &&
PA_SINK_INPUT_IS_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;
static void sink_request_rewind(pa_sink *s) {
struct userdata *u;
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);
pa_sink_assert_ref(s);
pa_assert_se(u = s->userdata);
/* 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);
pa_sink_input_request_rewind(u->sink_input, s->thread_info.rewind_nbytes, TRUE, FALSE);
}
/* Called from I/O thread context */
static int sink_input_peek_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) {
static void sink_update_requested_latency(pa_sink *s) {
struct userdata *u;
pa_sink_assert_ref(s);
pa_assert_se(u = s->userdata);
/* Just hand this one over to the master sink */
pa_sink_input_set_requested_latency_within_thread(
u->sink_input,
pa_sink_get_requested_latency_within_thread(s));
}
/* Called from I/O thread context */
static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) {
struct userdata *u;
pa_sink_input_assert_ref(i);
pa_assert(chunk);
pa_assert_se(u = i->userdata);
if (!u->memchunk.memblock)
pa_sink_render(u->sink, length, &u->memchunk);
if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state))
return -1;
pa_assert(u->memchunk.memblock);
*chunk = u->memchunk;
pa_memblock_ref(chunk->memblock);
pa_sink_render(u->sink, nbytes, chunk);
return 0;
}
/* Called from I/O thread context */
static void sink_input_drop_cb(pa_sink_input *i, size_t length) {
static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
struct userdata *u;
pa_sink_input_assert_ref(i);
pa_assert_se(u = i->userdata);
pa_assert(length > 0);
pa_assert(nbytes > 0);
if (u->memchunk.memblock) {
if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state))
return;
if (length < u->memchunk.length) {
u->memchunk.index += length;
u->memchunk.length -= length;
return;
}
if (u->sink->thread_info.rewind_nbytes > 0) {
size_t amount;
pa_memblock_unref(u->memchunk.memblock);
length -= u->memchunk.length;
pa_memchunk_reset(&u->memchunk);
amount = PA_MIN(u->sink->thread_info.rewind_nbytes, nbytes);
u->sink->thread_info.rewind_nbytes = 0;
if (amount > 0)
pa_sink_process_rewind(u->sink, amount);
}
}
if (length > 0)
pa_sink_skip(u->sink, length);
/* Called from I/O thread context */
static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) {
struct userdata *u;
pa_sink_input_assert_ref(i);
pa_assert_se(u = i->userdata);
if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state))
return;
pa_sink_set_max_rewind(u->sink, nbytes);
}
/* Called from I/O thread context */
@ -171,7 +192,12 @@ static void sink_input_detach_cb(pa_sink_input *i) {
pa_sink_input_assert_ref(i);
pa_assert_se(u = i->userdata);
if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state))
return;
pa_sink_detach_within_thread(u->sink);
pa_sink_set_asyncmsgq(u->sink, NULL);
pa_sink_set_rtpoll(u->sink, NULL);
}
/* Called from I/O thread context */
@ -181,10 +207,15 @@ static void sink_input_attach_cb(pa_sink_input *i) {
pa_sink_input_assert_ref(i);
pa_assert_se(u = i->userdata);
if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state))
return;
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);
u->sink->max_latency = u->master->max_latency;
u->sink->min_latency = u->master->min_latency;
}
/* Called from main context */
@ -194,26 +225,42 @@ static void sink_input_kill_cb(pa_sink_input *i) {
pa_sink_input_assert_ref(i);
pa_assert_se(u = i->userdata);
pa_sink_unlink(u->sink);
pa_sink_input_unlink(u->sink_input);
pa_sink_unref(u->sink);
u->sink = NULL;
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);
}
/* Called from IO thread context */
static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t state) {
struct userdata *u;
pa_sink_input_assert_ref(i);
pa_assert_se(u = i->userdata);
/* If we are added for the first time, ask for a rewinding so that
* we are heard right-away. */
if (PA_SINK_INPUT_IS_LINKED(state) &&
i->thread_info.state == PA_SINK_INPUT_INIT) {
pa_log_debug("Requesting rewind due to state change.");
pa_sink_input_request_rewind(i, 0, FALSE, TRUE);
}
}
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;
const char *k;
pa_sink *master;
pa_sink_input_new_data data;
char *default_sink_name = NULL;
pa_sink_input_new_data sink_input_data;
pa_sink_new_data sink_data;
pa_assert(m);
@ -245,57 +292,76 @@ int pa__init(pa_module*m) {
goto fail;
}
if (pa_channel_map_equal(&stream_map, &master->channel_map))
pa_log_warn("No remapping configured, proceeding nonetheless!");
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);
u->sink = NULL;
u->sink_input = NULL;
/* 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_sink_new_data_init(&sink_data);
sink_data.driver = __FILE__;
sink_data.module = m;
if (!(sink_data.name = pa_xstrdup(pa_modargs_get_value(ma, "sink_name", NULL))))
sink_data.name = pa_sprintf_malloc("%s.remapped", master->name);
pa_sink_new_data_set_sample_spec(&sink_data, &ss);
pa_sink_new_data_set_channel_map(&sink_data, &sink_map);
k = pa_proplist_gets(master->proplist, PA_PROP_DEVICE_DESCRIPTION);
pa_proplist_setf(sink_data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Remapped %s", k ? k : master->name);
pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_MASTER_DEVICE, master->name);
pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_CLASS, "filter");
u->sink = pa_sink_new(m->core, &sink_data, PA_SINK_LATENCY);
pa_sink_new_data_done(&sink_data);
if (!u->sink) {
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->update_requested_latency = sink_update_requested_latency;
u->sink->request_rewind = sink_request_rewind;
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;
pa_sink_input_new_data_init(&sink_input_data);
sink_input_data.driver = __FILE__;
sink_input_data.module = m;
sink_input_data.sink = u->master;
pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_NAME, "Remapped Stream");
pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "filter");
pa_sink_input_new_data_set_sample_spec(&sink_input_data, &ss);
pa_sink_input_new_data_set_channel_map(&sink_input_data, &stream_map);
if (!(u->sink_input = pa_sink_input_new(m->core, &data, PA_SINK_INPUT_DONT_MOVE)))
u->sink_input = pa_sink_input_new(m->core, &sink_input_data, PA_SINK_INPUT_DONT_MOVE);
pa_sink_input_new_data_done(&sink_input_data);
if (!u->sink_input)
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->pop = sink_input_pop_cb;
u->sink_input->process_rewind = sink_input_process_rewind_cb;
u->sink_input->update_max_rewind = sink_input_update_max_rewind_cb;
u->sink_input->kill = sink_input_kill_cb;
u->sink_input->attach = sink_input_attach_cb;
u->sink_input->detach = sink_input_detach_cb;
u->sink_input->state_change = sink_input_state_change_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;
@ -305,8 +371,6 @@ fail:
pa__done(m);
pa_xfree(default_sink_name);
return -1;
}
@ -318,18 +382,15 @@ void pa__done(pa_module*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);
if (u->sink_input) {
pa_sink_input_unlink(u->sink_input);
pa_sink_input_unref(u->sink_input);
}
pa_xfree(u);
}

View file

@ -75,12 +75,12 @@ static pa_hook_result_t sink_hook_callback(pa_core *c, pa_sink *sink, void* user
}
while ((i = pa_idxset_first(sink->inputs, NULL))) {
if (pa_sink_input_move_to(i, target, 1) < 0) {
pa_log_warn("Failed to move sink input %u \"%s\" to %s.", i->index, i->name, target->name);
if (pa_sink_input_move_to(i, target) < 0) {
pa_log_warn("Failed to move sink input %u \"%s\" to %s.", i->index, pa_proplist_gets(i->proplist, PA_PROP_APPLICATION_NAME), target->name);
return PA_HOOK_OK;
}
pa_log_info("Sucessfully moved sink input %u \"%s\" to %s.", i->index, i->name, target->name);
pa_log_info("Sucessfully moved sink input %u \"%s\" to %s.", i->index, pa_proplist_gets(i->proplist, PA_PROP_APPLICATION_NAME), target->name);
}
@ -116,11 +116,11 @@ static pa_hook_result_t source_hook_callback(pa_core *c, pa_source *source, void
while ((o = pa_idxset_first(source->outputs, NULL))) {
if (pa_source_output_move_to(o, target) < 0) {
pa_log_warn("Failed to move source output %u \"%s\" to %s.", o->index, o->name, target->name);
pa_log_warn("Failed to move source output %u \"%s\" to %s.", o->index, pa_proplist_gets(o->proplist, PA_PROP_APPLICATION_NAME), target->name);
return PA_HOOK_OK;
}
pa_log_info("Sucessfully moved source output %u \"%s\" to %s.", o->index, o->name, target->name);
pa_log_info("Sucessfully moved source output %u \"%s\" to %s.", o->index, pa_proplist_gets(o->proplist, PA_PROP_APPLICATION_NAME), target->name);
}

View file

@ -59,44 +59,43 @@ static const char* const valid_modargs[] = {
NULL,
};
static int sink_input_peek_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) {
static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) {
struct userdata *u;
pa_assert(i);
u = i->userdata;
pa_assert(u);
pa_sink_input_assert_ref(i);
pa_assert_se(u = i->userdata);
pa_assert(chunk);
chunk->memblock = pa_memblock_ref(u->memblock);
chunk->index = u->peek_index;
chunk->length = pa_memblock_get_length(u->memblock) - u->peek_index;
chunk->index = u->peek_index;
u->peek_index = 0;
return 0;
}
static void sink_input_drop_cb(pa_sink_input *i, size_t length) {
struct userdata *u;
static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
size_t l;
struct userdata *u;
pa_assert(i);
u = i->userdata;
pa_assert(u);
pa_assert(length > 0);
u->peek_index += length;
pa_sink_input_assert_ref(i);
pa_assert_se(u = i->userdata);
l = pa_memblock_get_length(u->memblock);
nbytes %= l;
while (u->peek_index >= l)
u->peek_index -= l;
if (u->peek_index >= nbytes)
u->peek_index -= nbytes;
else
u->peek_index = l + u->peek_index - nbytes;
}
static void sink_input_kill_cb(pa_sink_input *i) {
struct userdata *u;
pa_assert(i);
u = i->userdata;
pa_assert(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);
@ -105,6 +104,20 @@ static void sink_input_kill_cb(pa_sink_input *i) {
pa_module_unload_request(u->module);
}
/* Called from IO thread context */
static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t state) {
struct userdata *u;
pa_sink_input_assert_ref(i);
pa_assert_se(u = i->userdata);
/* If we are added for the first time, ask for a rewinding so that
* we are heard right-away. */
if (PA_SINK_INPUT_IS_LINKED(state) &&
i->thread_info.state == PA_SINK_INPUT_INIT)
pa_sink_input_request_rewind(i, 0, FALSE, TRUE);
}
static void calc_sine(float *f, size_t l, float freq) {
size_t i;
@ -120,7 +133,6 @@ int pa__init(pa_module*m) {
pa_sink *sink;
pa_sample_spec ss;
uint32_t frequency;
char t[256];
void *p;
pa_sink_input_new_data data;
@ -156,21 +168,25 @@ int pa__init(pa_module*m) {
calc_sine(p, pa_memblock_get_length(u->memblock), frequency);
pa_memblock_release(u->memblock);
pa_snprintf(t, sizeof(t), "Sine Generator at %u Hz", frequency);
pa_sink_input_new_data_init(&data);
data.sink = sink;
data.driver = __FILE__;
data.name = t;
pa_proplist_setf(data.proplist, PA_PROP_MEDIA_NAME, "%u Hz Sine", frequency);
pa_proplist_sets(data.proplist, PA_PROP_MEDIA_ROLE, "abstract");
pa_proplist_setf(data.proplist, "sine.hz", "%u", frequency);
pa_sink_input_new_data_set_sample_spec(&data, &ss);
data.module = m;
if (!(u->sink_input = pa_sink_input_new(m->core, &data, 0)))
u->sink_input = pa_sink_input_new(m->core, &data, 0);
pa_sink_input_new_data_done(&data);
if (!u->sink_input)
goto fail;
u->sink_input->peek = sink_input_peek_cb;
u->sink_input->drop = sink_input_drop_cb;
u->sink_input->pop = sink_input_pop_cb;
u->sink_input->process_rewind = sink_input_process_rewind_cb;
u->sink_input->kill = sink_input_kill_cb;
u->sink_input->state_change = sink_input_state_change_cb;
u->sink_input->userdata = u;
pa_sink_input_put(u->sink_input);

View file

@ -317,7 +317,7 @@ static pa_hook_result_t device_state_changed_hook_cb(pa_core *c, pa_object *o, s
if (pa_sink_used_by(s) <= 0) {
if (PA_SINK_OPENED(state))
if (PA_SINK_IS_OPENED(state))
restart(d);
}
@ -328,7 +328,7 @@ static pa_hook_result_t device_state_changed_hook_cb(pa_core *c, pa_object *o, s
if (pa_source_used_by(s) <= 0) {
if (PA_SOURCE_OPENED(state))
if (PA_SOURCE_IS_OPENED(state))
restart(d);
}
}
@ -367,8 +367,8 @@ int pa__init(pa_module*m) {
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_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PUT], (pa_hook_cb_t) device_new_hook_cb, u);
u->source_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_PUT], (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);

View file

@ -303,7 +303,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
/* First, change the state, because otherwide pa_sink_render() would fail */
if ((r = pa_sink_process_msg(o, code, data, offset, chunk)) >= 0)
if (PA_SINK_OPENED((pa_sink_state_t) PA_PTR_TO_UINT(data)))
if (PA_SINK_IS_OPENED((pa_sink_state_t) PA_PTR_TO_UINT(data)))
send_data(u);
return r;
@ -314,7 +314,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
pa_assert(offset > 0);
u->requested_bytes += (size_t) offset;
if (PA_SINK_OPENED(u->sink->thread_info.state))
if (PA_SINK_IS_OPENED(u->sink->thread_info.state))
send_data(u);
return 0;
@ -343,7 +343,7 @@ static int sink_set_state(pa_sink *s, pa_sink_state_t state) {
switch ((pa_sink_state_t) state) {
case PA_SINK_SUSPENDED:
pa_assert(PA_SINK_OPENED(s->state));
pa_assert(PA_SINK_IS_OPENED(s->state));
stream_cork(u, TRUE);
break;
@ -369,7 +369,7 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off
switch (code) {
case SOURCE_MESSAGE_POST:
if (PA_SOURCE_OPENED(u->source->thread_info.state))
if (PA_SOURCE_IS_OPENED(u->source->thread_info.state))
pa_source_post(u->source, chunk);
return 0;
}
@ -385,7 +385,7 @@ static int source_set_state(pa_source *s, pa_source_state_t state) {
switch ((pa_source_state_t) state) {
case PA_SOURCE_SUSPENDED:
pa_assert(PA_SOURCE_OPENED(s->state));
pa_assert(PA_SOURCE_IS_OPENED(s->state));
stream_cork(u, TRUE);
break;
@ -577,29 +577,29 @@ static void timeout_callback(pa_mainloop_api *m, pa_time_event*e, PA_GCC_UNUSED
}
#ifdef TUNNEL_SINK
static pa_usec_t sink_get_latency(pa_sink *s) {
pa_usec_t t, c;
struct userdata *u = s->userdata;
/* static pa_usec_t sink_get_latency(pa_sink *s) { */
/* pa_usec_t t, c; */
/* struct userdata *u = s->userdata; */
pa_sink_assert_ref(s);
/* pa_sink_assert_ref(s); */
c = pa_bytes_to_usec(u->counter, &s->sample_spec);
t = pa_smoother_get(u->smoother, pa_rtclock_usec());
/* c = pa_bytes_to_usec(u->counter, &s->sample_spec); */
/* t = pa_smoother_get(u->smoother, pa_rtclock_usec()); */
return c > t ? c - t : 0;
}
/* return c > t ? c - t : 0; */
/* } */
#else
static pa_usec_t source_get_latency(pa_source *s) {
pa_usec_t t, c;
struct userdata *u = s->userdata;
/* static pa_usec_t source_get_latency(pa_source *s) { */
/* pa_usec_t t, c; */
/* struct userdata *u = s->userdata; */
pa_source_assert_ref(s);
/* pa_source_assert_ref(s); */
c = pa_bytes_to_usec(u->counter, &s->sample_spec);
t = pa_smoother_get(u->smoother, pa_rtclock_usec());
/* c = pa_bytes_to_usec(u->counter, &s->sample_spec); */
/* t = pa_smoother_get(u->smoother, pa_rtclock_usec()); */
return t > c ? t - c : 0;
}
/* return t > c ? t - c : 0; */
/* } */
#endif
static void update_description(struct userdata *u) {
@ -1066,7 +1066,7 @@ static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t
pa_tagstruct_putu32(reply, PA_INVALID_INDEX);
pa_tagstruct_puts(reply, u->sink_name);
pa_tagstruct_putu32(reply, u->maxlength);
pa_tagstruct_put_boolean(reply, !PA_SINK_OPENED(pa_sink_get_state(u->sink)));
pa_tagstruct_put_boolean(reply, !PA_SINK_IS_OPENED(pa_sink_get_state(u->sink)));
pa_tagstruct_putu32(reply, u->tlength);
pa_tagstruct_putu32(reply, u->prebuf);
pa_tagstruct_putu32(reply, u->minreq);
@ -1082,7 +1082,7 @@ static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t
pa_tagstruct_putu32(reply, PA_INVALID_INDEX);
pa_tagstruct_puts(reply, u->source_name);
pa_tagstruct_putu32(reply, u->maxlength);
pa_tagstruct_put_boolean(reply, !PA_SOURCE_OPENED(pa_source_get_state(u->source)));
pa_tagstruct_put_boolean(reply, !PA_SOURCE_IS_OPENED(pa_source_get_state(u->source)));
pa_tagstruct_putu32(reply, u->fragsize);
#endif
@ -1294,6 +1294,11 @@ int pa__init(pa_module*m) {
pa_sample_spec ss;
pa_channel_map map;
char *t, *dn = NULL;
#ifdef TUNNEL_SINK
pa_sink_new_data data;
#else
pa_source_new_data data;
#endif
pa_assert(m);
@ -1318,15 +1323,14 @@ int pa__init(pa_module*m) {
u->source_name = pa_xstrdup(pa_modargs_get_value(ma, "source", NULL));;
u->source = NULL;
#endif
u->smoother = pa_smoother_new(PA_USEC_PER_SEC, PA_USEC_PER_SEC*2, TRUE);
u->smoother = pa_smoother_new(PA_USEC_PER_SEC, PA_USEC_PER_SEC*2, TRUE, 10);
u->ctag = 1;
u->device_index = u->channel = PA_INVALID_INDEX;
u->auth_cookie_in_property = FALSE;
u->time_event = 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);
pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);
if (load_key(u, pa_modargs_get_value(ma, "cookie", NULL)) < 0)
goto fail;
@ -1354,7 +1358,18 @@ int pa__init(pa_module*m) {
if (!(dn = pa_xstrdup(pa_modargs_get_value(ma, "sink_name", NULL))))
dn = pa_sprintf_malloc("tunnel.%s", u->server_name);
if (!(u->sink = pa_sink_new(m->core, __FILE__, dn, 1, &ss, &map))) {
pa_sink_new_data_init(&data);
data.driver = __FILE__;
data.module = m;
data.namereg_fail = TRUE;
pa_sink_new_data_set_name(&data, dn);
pa_sink_new_data_set_sample_spec(&data, &ss);
pa_sink_new_data_set_channel_map(&data, &map);
u->sink = pa_sink_new(m->core, &data, PA_SINK_NETWORK|PA_SINK_LATENCY|PA_SINK_HW_VOLUME_CTRL);
pa_sink_new_data_done(&data);
if (!u->sink) {
pa_log("Failed to create sink.");
goto fail;
}
@ -1362,14 +1377,12 @@ int pa__init(pa_module*m) {
u->sink->parent.process_msg = sink_process_msg;
u->sink->userdata = u;
u->sink->set_state = sink_set_state;
u->sink->get_latency = sink_get_latency;
/* u->sink->get_latency = sink_get_latency; */
u->sink->get_volume = sink_get_volume;
u->sink->get_mute = sink_get_mute;
u->sink->set_volume = sink_set_volume;
u->sink->set_mute = sink_set_mute;
u->sink->flags = PA_SINK_NETWORK|PA_SINK_LATENCY|PA_SINK_HW_VOLUME_CTRL;
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("%s%s%s", u->sink_name ? u->sink_name : "", u->sink_name ? " on " : "", u->server_name));
@ -1380,7 +1393,18 @@ int pa__init(pa_module*m) {
if (!(dn = pa_xstrdup(pa_modargs_get_value(ma, "source_name", NULL))))
dn = pa_sprintf_malloc("tunnel.%s", u->server_name);
if (!(u->source = pa_source_new(m->core, __FILE__, dn, 1, &ss, &map))) {
pa_source_new_data_init(&data);
data.driver = __FILE__;
data.module = m;
data.namereg_fail = TRUE;
pa_source_new_data_set_name(&data, dn);
pa_source_new_data_set_sample_spec(&data, &ss);
pa_source_new_data_set_channel_map(&data, &map);
u->source = pa_source_new(m->core, &data, PA_SOURCE_NETWORK|PA_SOURCE_LATENCY);
pa_source_new_data_done(&data);
if (!u->source) {
pa_log("Failed to create source.");
goto fail;
}
@ -1388,10 +1412,8 @@ int pa__init(pa_module*m) {
u->source->parent.process_msg = source_process_msg;
u->source->userdata = u;
u->source->set_state = source_set_state;
u->source->get_latency = source_get_latency;
u->source->flags = PA_SOURCE_NETWORK|PA_SOURCE_LATENCY;
/* u->source->get_latency = source_get_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("%s%s%s", u->source_name ? u->source_name : "", u->source_name ? " on " : "", u->server_name));

View file

@ -115,7 +115,7 @@ static pa_cvolume* parse_volume(const char *s, pa_cvolume *v) {
k = strtol(p, &p, 0);
if (k < PA_VOLUME_MUTED)
if (k < (long) PA_VOLUME_MUTED)
return NULL;
v->values[i] = (pa_volume_t) k;
@ -134,16 +134,12 @@ static int load_rules(struct userdata *u) {
char buf_name[256], buf_volume[256], buf_sink[256], buf_source[256];
char *ln = buf_name;
f = u->table_file ?
fopen(u->table_file, "r") :
pa_open_config_file(NULL, DEFAULT_VOLUME_TABLE_FILE, NULL, &u->table_file, "r");
if (!f) {
if (!(f = fopen(u->table_file, "r"))) {
if (errno == ENOENT) {
pa_log_info("starting with empty ruleset.");
pa_log_info("Starting with empty ruleset.");
ret = 0;
} else
pa_log("failed to open file '%s': %s", u->table_file, pa_cstrerror(errno));
pa_log("Failed to open file '%s': %s", u->table_file, pa_cstrerror(errno));
goto finish;
}
@ -236,11 +232,7 @@ static int save_rules(struct userdata *u) {
pa_log_info("Saving rules...");
f = u->table_file ?
fopen(u->table_file, "w") :
pa_open_config_file(NULL, DEFAULT_VOLUME_TABLE_FILE, NULL, &u->table_file, "w");
if (!f) {
if (!(f = fopen(u->table_file, "w"))) {
pa_log("Failed to open file '%s': %s", u->table_file, pa_cstrerror(errno));
goto finish;
}
@ -280,10 +272,10 @@ finish:
static char* client_name(pa_client *c) {
char *t, *e;
if (!c->name || !c->driver)
if (!pa_proplist_gets(c->proplist, PA_PROP_APPLICATION_NAME) || !c->driver)
return NULL;
t = pa_sprintf_malloc("%s$%s", c->driver, c->name);
t = pa_sprintf_malloc("%s$%s", c->driver, pa_proplist_gets(c->proplist, PA_PROP_APPLICATION_NAME));
t[strcspn(t, "\n\r#")] = 0;
if (!*t) {
@ -496,7 +488,7 @@ int pa__init(pa_module*m) {
u = pa_xnew(struct userdata, 1);
u->core = m->core;
u->hashmap = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
u->table_file = pa_xstrdup(pa_modargs_get_value(ma, "table", NULL));
u->table_file = pa_runtime_path(pa_modargs_get_value(ma, "table", DEFAULT_VOLUME_TABLE_FILE));
u->modified = FALSE;
u->subscription = NULL;
u->sink_input_new_hook_slot = u->sink_input_fixate_hook_slot = u->source_output_new_hook_slot = NULL;

View file

@ -81,7 +81,7 @@ static int x11_event_callback(pa_x11_wrapper *w, XEvent *e, void *userdata) {
bne = (XkbBellNotifyEvent*) e;
if (pa_scache_play_item_by_name(u->core, u->scache_item, u->sink_name, (bne->percent*PA_VOLUME_NORM)/100, 1) < 0) {
if (pa_scache_play_item_by_name(u->core, u->scache_item, u->sink_name, TRUE, (bne->percent*PA_VOLUME_NORM)/100, NULL, NULL) < 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);
}

View file

@ -115,7 +115,7 @@ static void get_service_data(struct service *s, pa_sample_spec *ret_ss, pa_chann
*ret_ss = sink->sample_spec;
*ret_map = sink->channel_map;
*ret_name = sink->name;
*ret_description = sink->description;
*ret_description = pa_strnull(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION));
*ret_subtype = sink->flags & PA_SINK_HARDWARE ? SUBTYPE_HARDWARE : SUBTYPE_VIRTUAL;
} else if (pa_source_isinstance(s->device)) {
@ -124,7 +124,7 @@ static void get_service_data(struct service *s, pa_sample_spec *ret_ss, pa_chann
*ret_ss = source->sample_spec;
*ret_map = source->channel_map;
*ret_name = source->name;
*ret_description = source->description;
*ret_description = pa_strnull(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION));
*ret_subtype = source->monitor_of ? SUBTYPE_MONITOR : (source->flags & PA_SOURCE_HARDWARE ? SUBTYPE_HARDWARE : SUBTYPE_VIRTUAL);
} else
@ -304,10 +304,10 @@ static struct service *get_service(struct userdata *u, pa_object *device) {
s->device = device;
if (pa_sink_isinstance(device)) {
if (!(n = PA_SINK(device)->description))
if (!(n = pa_proplist_gets(PA_SINK(device)->proplist, PA_PROP_DEVICE_DESCRIPTION)))
n = PA_SINK(device)->name;
} else {
if (!(n = PA_SOURCE(device)->description))
if (!(n = pa_proplist_gets(PA_SOURCE(device)->proplist, PA_PROP_DEVICE_DESCRIPTION)))
n = PA_SOURCE(device)->name;
}
@ -578,11 +578,11 @@ int pa__init(pa_module*m) {
u->services = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
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_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PUT], (pa_hook_cb_t) device_new_or_changed_cb, u);
u->sink_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PROPLIST_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_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_PUT], (pa_hook_cb_t) device_new_or_changed_cb, u);
u->source_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_PROPLIST_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;

View file

@ -251,7 +251,7 @@ int pa_oss_set_fragments(int fd, int nfrags, int frag_size) {
return 0;
}
int pa_oss_get_volume(int fd, int mixer, const pa_sample_spec *ss, pa_cvolume *volume) {
int pa_oss_get_volume(int fd, unsigned long mixer, const pa_sample_spec *ss, pa_cvolume *volume) {
char cv[PA_CVOLUME_SNPRINT_MAX];
unsigned vol;
@ -273,7 +273,7 @@ int pa_oss_get_volume(int fd, int mixer, const pa_sample_spec *ss, pa_cvolume *v
return 0;
}
int pa_oss_set_volume(int fd, long mixer, const pa_sample_spec *ss, const pa_cvolume *volume) {
int pa_oss_set_volume(int fd, unsigned long mixer, const pa_sample_spec *ss, const pa_cvolume *volume) {
char cv[PA_CVOLUME_SNPRINT_MAX];
unsigned vol;
pa_volume_t l, r;

View file

@ -33,8 +33,8 @@ 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_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_set_volume(int fd, unsigned long mixer, const pa_sample_spec *ss, const pa_cvolume *volume);
int pa_oss_get_volume(int fd, unsigned long mixer, const pa_sample_spec *ss, pa_cvolume *volume);
int pa_oss_get_hw_description(const char *dev, char *name, size_t l);

View file

@ -51,6 +51,7 @@
#include <pulsecore/atomic.h>
#include <pulsecore/rtclock.h>
#include <pulsecore/atomic.h>
#include <pulsecore/time-smoother.h>
#include "module-rtp-recv-symdef.h"
@ -69,9 +70,11 @@ PA_MODULE_USAGE(
#define SAP_PORT 9875
#define DEFAULT_SAP_ADDRESS "224.0.0.56"
#define MEMBLOCKQ_MAXLENGTH (1024*170)
#define MEMBLOCKQ_MAXLENGTH (1024*1024*40)
#define MAX_SESSIONS 16
#define DEATH_TIMEOUT 20
#define RATE_UPDATE_INTERVAL (5*PA_USEC_PER_SEC)
#define LATENCY_USEC (500*PA_USEC_PER_MSEC)
static const char* const valid_modargs[] = {
"sink",
@ -97,6 +100,12 @@ struct session {
pa_rtpoll_item *rtpoll_item;
pa_atomic_t timestamp;
pa_smoother *smoother;
pa_usec_t intended_latency;
pa_usec_t sink_latency;
pa_usec_t last_rate_update;
};
struct userdata {
@ -133,21 +142,37 @@ static int sink_input_process_msg(pa_msgobject *o, int code, void *data, int64_t
}
/* Called from I/O thread context */
static int sink_input_peek(pa_sink_input *i, size_t length, pa_memchunk *chunk) {
static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) {
struct session *s;
pa_sink_input_assert_ref(i);
pa_assert_se(s = i->userdata);
return pa_memblockq_peek(s->memblockq, chunk);
if (pa_memblockq_peek(s->memblockq, chunk) < 0)
return -1;
pa_memblockq_drop(s->memblockq, chunk->length);
return 0;
}
/* Called from I/O thread context */
static void sink_input_drop(pa_sink_input *i, size_t length) {
static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
struct session *s;
pa_sink_input_assert_ref(i);
pa_assert_se(s = i->userdata);
pa_memblockq_drop(s->memblockq, length);
pa_memblockq_rewind(s->memblockq, nbytes);
}
/* Called from thread context */
static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) {
struct session *s;
pa_sink_input_assert_ref(i);
pa_assert_se(s = i->userdata);
pa_memblockq_set_maxrewind(s->memblockq, nbytes);
}
/* Called from main context */
@ -215,20 +240,82 @@ static int rtpoll_work_cb(pa_rtpoll_item *i) {
pa_memblockq_seek(s->memblockq, delta * s->rtp_context.frame_size, PA_SEEK_RELATIVE);
pa_rtclock_get(&now);
pa_smoother_put(s->smoother, pa_timeval_load(&now), pa_bytes_to_usec(pa_memblockq_get_write_index(s->memblockq), &s->sink_input->sample_spec));
if (pa_memblockq_push(s->memblockq, &chunk) < 0) {
/* queue overflow, let's flush it and try again */
pa_memblockq_flush(s->memblockq);
pa_memblockq_push(s->memblockq, &chunk);
pa_log_warn("Queue overrun");
pa_memblockq_seek(s->memblockq, chunk.length, PA_SEEK_RELATIVE);
}
pa_log("blocks in q: %u", pa_memblockq_get_nblocks(s->memblockq));
pa_memblock_unref(chunk.memblock);
/* The next timestamp we expect */
s->offset = s->rtp_context.timestamp + (chunk.length / s->rtp_context.frame_size);
pa_memblock_unref(chunk.memblock);
pa_rtclock_get(&now);
pa_atomic_store(&s->timestamp, now.tv_sec);
if (s->last_rate_update + RATE_UPDATE_INTERVAL < pa_timeval_load(&now)) {
pa_usec_t wi, ri, render_delay, sink_delay = 0, latency, fix;
unsigned fix_samples;
pa_log("Updating sample rate");
wi = pa_smoother_get(s->smoother, pa_timeval_load(&now));
ri = pa_bytes_to_usec(pa_memblockq_get_read_index(s->memblockq), &s->sink_input->sample_spec);
if (PA_MSGOBJECT(s->sink_input->sink)->process_msg(PA_MSGOBJECT(s->sink_input->sink), PA_SINK_MESSAGE_GET_LATENCY, &sink_delay, 0, NULL) < 0)
sink_delay = 0;
render_delay = pa_bytes_to_usec(pa_memblockq_get_length(s->sink_input->thread_info.render_memblockq), &s->sink_input->sink->sample_spec);
if (ri > render_delay+sink_delay)
ri -= render_delay+sink_delay;
else
ri = 0;
if (wi < ri)
latency = 0;
else
latency = wi - ri;
pa_log_debug("Write index deviates by %0.2f ms, expected %0.2f ms", (double) latency/PA_USEC_PER_MSEC, (double) s->intended_latency/PA_USEC_PER_MSEC);
/* Calculate deviation */
if (latency < s->intended_latency)
fix = s->intended_latency - latency;
else
fix = latency - s->intended_latency;
/* How many samples is this per second? */
fix_samples = fix * s->sink_input->thread_info.sample_spec.rate / RATE_UPDATE_INTERVAL;
/* Check if deviation is in bounds */
if (fix_samples > s->sink_input->sample_spec.rate*.20)
pa_log_debug("Hmmm, rate fix is too large (%lu Hz), not applying.", (unsigned long) fix_samples);
/* Fix up rate */
if (latency < s->intended_latency)
s->sink_input->sample_spec.rate -= fix_samples;
else
s->sink_input->sample_spec.rate += fix_samples;
pa_resampler_set_input_rate(s->sink_input->thread_info.resampler, s->sink_input->sample_spec.rate);
pa_log_debug("Updated sampling rate to %lu Hz.", (unsigned long) s->sink_input->sample_spec.rate);
s->last_rate_update = pa_timeval_load(&now);
}
if (pa_memblockq_is_readable(s->memblockq) &&
s->sink_input->thread_info.underrun_for > 0) {
pa_log_debug("Requesting rewind due to end of underrun");
pa_sink_input_request_rewind(s->sink_input, 0, FALSE, TRUE);
}
return 1;
}
@ -314,10 +401,9 @@ fail:
static struct session *session_new(struct userdata *u, const pa_sdp_info *sdp_info) {
struct session *s = NULL;
char *c;
pa_sink *sink;
int fd = -1;
pa_memblock *silence;
pa_memchunk silence;
pa_sink_input_new_data data;
struct timeval now;
@ -329,37 +415,46 @@ static struct session *session_new(struct userdata *u, const pa_sdp_info *sdp_in
goto fail;
}
if (!(sink = pa_namereg_get(u->module->core, u->sink_name, PA_NAMEREG_SINK, 1))) {
if (!(sink = pa_namereg_get(u->module->core, u->sink_name, PA_NAMEREG_SINK, TRUE))) {
pa_log("Sink does not exist.");
goto fail;
}
pa_rtclock_get(&now);
s = pa_xnew0(struct session, 1);
s->userdata = u;
s->first_packet = FALSE;
s->sdp_info = *sdp_info;
s->rtpoll_item = NULL;
pa_rtclock_get(&now);
s->intended_latency = LATENCY_USEC;
s->smoother = pa_smoother_new(PA_USEC_PER_SEC*5, PA_USEC_PER_SEC*2, TRUE, 10);
pa_smoother_set_time_offset(s->smoother, pa_timeval_load(&now));
s->last_rate_update = pa_timeval_load(&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;
c = pa_sprintf_malloc("RTP Stream%s%s%s",
sdp_info->session_name ? " (" : "",
sdp_info->session_name ? sdp_info->session_name : "",
sdp_info->session_name ? ")" : "");
pa_sink_input_new_data_init(&data);
data.sink = sink;
data.driver = __FILE__;
data.name = c;
pa_proplist_sets(data.proplist, PA_PROP_MEDIA_ROLE, "stream");
pa_proplist_setf(data.proplist, PA_PROP_MEDIA_NAME,
"RTP Stream%s%s%s",
sdp_info->session_name ? " (" : "",
sdp_info->session_name ? sdp_info->session_name : "",
sdp_info->session_name ? ")" : "");
if (sdp_info->session_name)
pa_proplist_sets(data.proplist, "rtp.session", sdp_info->session_name);
pa_proplist_sets(data.proplist, "rtp.origin", sdp_info->origin);
pa_proplist_setf(data.proplist, "rtp.payload", "%u", (unsigned) sdp_info->payload);
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->module->core, &data, 0);
pa_xfree(c);
pa_sink_input_new_data_done(&data);
if (!s->sink_input) {
pa_log("Failed to create sink input.");
@ -369,27 +464,31 @@ static struct session *session_new(struct userdata *u, const pa_sdp_info *sdp_in
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->pop = sink_input_pop_cb;
s->sink_input->process_rewind = sink_input_process_rewind_cb;
s->sink_input->update_max_rewind = sink_input_update_max_rewind_cb;
s->sink_input->kill = sink_input_kill;
s->sink_input->attach = sink_input_attach;
s->sink_input->detach = sink_input_detach;
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));
pa_sink_input_get_silence(s->sink_input, &silence);
s->sink_latency = pa_sink_input_set_requested_latency(s->sink_input, s->intended_latency/2);
if (s->intended_latency < s->sink_latency*2)
s->intended_latency = s->sink_latency*2;
s->memblockq = pa_memblockq_new(
0,
MEMBLOCKQ_MAXLENGTH,
MEMBLOCKQ_MAXLENGTH,
pa_frame_size(&s->sink_input->sample_spec),
pa_bytes_per_second(&s->sink_input->sample_spec)/10+1,
pa_usec_to_bytes(s->intended_latency - s->sink_latency, &s->sink_input->sample_spec),
0,
silence);
0,
&silence);
pa_memblock_unref(silence);
pa_memblock_unref(silence.memblock);
pa_rtp_context_init_recv(&s->rtp_context, fd, pa_frame_size(&s->sdp_info.sample_spec));
@ -429,12 +528,14 @@ static void session_free(struct session *s) {
pa_sdp_info_destroy(&s->sdp_info);
pa_rtp_context_destroy(&s->rtp_context);
pa_smoother_free(s->smoother);
pa_xfree(s);
}
static void sap_event_cb(pa_mainloop_api *m, pa_io_event *e, int fd, pa_io_event_flags_t flags, void *userdata) {
struct userdata *u = userdata;
int goodbye;
pa_bool_t goodbye = FALSE;
pa_sdp_info info;
struct session *s;

View file

@ -288,14 +288,20 @@ int pa__init(pa_module*m) {
pa_make_fd_cloexec(sap_fd);
pa_source_output_new_data_init(&data);
data.name = "RTP Monitor Stream";
pa_proplist_sets(data.proplist, PA_PROP_MEDIA_NAME, "RTP Monitor Stream");
pa_proplist_sets(data.proplist, "rtp.destination", dest);
pa_proplist_setf(data.proplist, "rtp.mtu", "%lu", (unsigned long) mtu);
pa_proplist_setf(data.proplist, "rtp.port", "%lu", (unsigned long) port);
data.driver = __FILE__;
data.module = m;
data.source = s;
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(m->core, &data, 0))) {
o = pa_source_output_new(m->core, &data, 0);
pa_source_output_new_data_done(&data);
if (!o) {
pa_log("failed to create source output.");
goto fail;
}
@ -318,6 +324,7 @@ int pa__init(pa_module*m) {
pa_frame_size(&ss),
1,
0,
0,
NULL);
u->mtu = mtu;

View file

@ -55,6 +55,8 @@ pa_rtp_context* pa_rtp_context_init_send(pa_rtp_context *c, int fd, uint32_t ssr
c->payload = payload & 127;
c->frame_size = frame_size;
pa_memchunk_reset(&c->memchunk);
return c;
}
@ -152,6 +154,8 @@ pa_rtp_context* pa_rtp_context_init_recv(pa_rtp_context *c, int fd, size_t frame
c->fd = fd;
c->frame_size = frame_size;
pa_memchunk_reset(&c->memchunk);
return c;
}
@ -173,12 +177,28 @@ int pa_rtp_recv(pa_rtp_context *c, pa_memchunk *chunk, pa_mempool *pool) {
goto fail;
}
if (!size)
if (size <= 0)
return 0;
chunk->memblock = pa_memblock_new(pool, size);
if (c->memchunk.length < (unsigned) size) {
size_t l;
iov.iov_base = pa_memblock_acquire(chunk->memblock);
if (c->memchunk.memblock)
pa_memblock_unref(c->memchunk.memblock);
l = PA_MAX((size_t) size, pa_mempool_block_size_max(pool));
c->memchunk.memblock = pa_memblock_new(pool, l);
c->memchunk.index = 0;
c->memchunk.length = pa_memblock_get_length(c->memchunk.memblock);
}
pa_assert(c->memchunk.length >= (size_t) size);
chunk->memblock = pa_memblock_ref(c->memchunk.memblock);
chunk->index = c->memchunk.index;
iov.iov_base = (uint8_t*) pa_memblock_acquire(chunk->memblock) + chunk->index;
iov.iov_len = size;
m.msg_name = NULL;
@ -236,14 +256,22 @@ int pa_rtp_recv(pa_rtp_context *c, pa_memchunk *chunk, pa_mempool *pool) {
goto fail;
}
chunk->index = 12 + cc*4;
chunk->length = size - chunk->index;
chunk->index += 12 + cc*4;
chunk->length = size - 12 + cc*4;
if (chunk->length % c->frame_size != 0) {
pa_log_warn("Bad RTP packet size.");
goto fail;
}
c->memchunk.index = chunk->index + chunk->length;
c->memchunk.length = pa_memblock_get_length(c->memchunk.memblock) - c->memchunk.index;
if (c->memchunk.length <= 0) {
pa_memblock_unref(c->memchunk.memblock);
pa_memchunk_reset(&c->memchunk);
}
return 0;
fail:
@ -329,7 +357,10 @@ int pa_rtp_sample_spec_valid(const pa_sample_spec *ss) {
void pa_rtp_context_destroy(pa_rtp_context *c) {
pa_assert(c);
pa_close(c->fd);
pa_assert_se(pa_close(c->fd) == 0);
if (c->memchunk.memblock)
pa_memblock_unref(c->memchunk.memblock);
}
const char* pa_rtp_format_to_string(pa_sample_format_t f) {
@ -361,4 +392,3 @@ pa_sample_format_t pa_rtp_string_to_format(const char *s) {
else
return PA_SAMPLE_INVALID;
}

View file

@ -37,6 +37,8 @@ typedef struct pa_rtp_context {
uint32_t ssrc;
uint8_t payload;
size_t frame_size;
pa_memchunk memchunk;
} pa_rtp_context;
pa_rtp_context* pa_rtp_context_init_send(pa_rtp_context *c, int fd, uint32_t ssrc, uint8_t payload, size_t frame_size);

View file

@ -71,7 +71,7 @@ void pa_sap_context_destroy(pa_sap_context *c) {
pa_xfree(c->sdp_data);
}
int pa_sap_send(pa_sap_context *c, int goodbye) {
int pa_sap_send(pa_sap_context *c, pa_bool_t goodbye) {
uint32_t header;
struct sockaddr_storage sa_buf;
struct sockaddr *sa = (struct sockaddr*) &sa_buf;
@ -127,7 +127,7 @@ pa_sap_context* pa_sap_context_init_recv(pa_sap_context *c, int fd) {
return c;
}
int pa_sap_recv(pa_sap_context *c, int *goodbye) {
int pa_sap_recv(pa_sap_context *c, pa_bool_t *goodbye) {
struct msghdr m;
struct iovec iov;
int size, k;

View file

@ -40,9 +40,9 @@ typedef struct pa_sap_context {
pa_sap_context* pa_sap_context_init_send(pa_sap_context *c, int fd, char *sdp_data);
void pa_sap_context_destroy(pa_sap_context *c);
int pa_sap_send(pa_sap_context *c, int goodbye);
int pa_sap_send(pa_sap_context *c, pa_bool_t goodbye);
pa_sap_context* pa_sap_context_init_recv(pa_sap_context *c, int fd);
int pa_sap_recv(pa_sap_context *c, int *goodbye);
int pa_sap_recv(pa_sap_context *c, pa_bool_t *goodbye);
#endif

View file

@ -117,7 +117,7 @@ static pa_sample_spec *parse_sdp_sample_spec(pa_sample_spec *ss, char *c) {
pa_sdp_info *pa_sdp_parse(const char *t, pa_sdp_info *i, int is_goodbye) {
uint16_t port = 0;
int ss_valid = 0;
pa_bool_t ss_valid = FALSE;
pa_assert(t);
pa_assert(i);
@ -202,7 +202,7 @@ pa_sdp_info *pa_sdp_parse(const char *t, pa_sdp_info *i, int is_goodbye) {
i->payload = (uint8_t) _payload;
if (pa_rtp_sample_spec_from_payload(i->payload, &i->sample_spec))
ss_valid = 1;
ss_valid = TRUE;
}
}
} else if (pa_startswith(t, "a=rtpmap:")) {
@ -222,7 +222,7 @@ pa_sdp_info *pa_sdp_parse(const char *t, pa_sdp_info *i, int is_goodbye) {
c[strcspn(c, "\n")] = 0;
if (parse_sdp_sample_spec(&i->sample_spec, c))
ss_valid = 1;
ss_valid = TRUE;
}
}
}

View file

@ -313,10 +313,15 @@ static void client_callback(AvahiClient *s, AvahiClientState state, void *userda
static void browser_free(pa_browser *b);
PA_WARN_REFERENCE(pa_browser_new, "libpulse-browse is being phased out.");
pa_browser *pa_browser_new(pa_mainloop_api *mainloop) {
return pa_browser_new_full(mainloop, PA_BROWSE_FOR_SERVERS|PA_BROWSE_FOR_SINKS|PA_BROWSE_FOR_SOURCES, NULL);
}
PA_WARN_REFERENCE(pa_browser_new_full, "libpulse-browse is being phased out.");
pa_browser *pa_browser_new_full(pa_mainloop_api *mainloop, pa_browse_flags_t flags, const char **error_string) {
pa_browser *b;
int error;
@ -420,6 +425,8 @@ static void browser_free(pa_browser *b) {
pa_xfree(b);
}
PA_WARN_REFERENCE(pa_browser_ref, "libpulse-browse is being phased out.");
pa_browser *pa_browser_ref(pa_browser *b) {
pa_assert(b);
pa_assert(PA_REFCNT_VALUE(b) >= 1);
@ -428,6 +435,8 @@ pa_browser *pa_browser_ref(pa_browser *b) {
return b;
}
PA_WARN_REFERENCE(pa_browser_unref, "libpulse-browse is being phased out.");
void pa_browser_unref(pa_browser *b) {
pa_assert(b);
pa_assert(PA_REFCNT_VALUE(b) >= 1);
@ -436,6 +445,8 @@ void pa_browser_unref(pa_browser *b) {
browser_free(b);
}
PA_WARN_REFERENCE(pa_browser_set_callback, "libpulse-browse is being phased out.");
void pa_browser_set_callback(pa_browser *b, pa_browse_cb_t cb, void *userdata) {
pa_assert(b);
pa_assert(PA_REFCNT_VALUE(b) >= 1);
@ -444,6 +455,8 @@ void pa_browser_set_callback(pa_browser *b, pa_browse_cb_t cb, void *userdata) {
b->userdata = userdata;
}
PA_WARN_REFERENCE(pa_browser_set_error_callback, "libpulse-browse is being phased out.");
void pa_browser_set_error_callback(pa_browser *b, pa_browser_error_cb_t cb, void *userdata) {
pa_assert(b);
pa_assert(PA_REFCNT_VALUE(b) >= 1);

View file

@ -41,22 +41,4 @@
#endif
#ifndef PA_GCC_PURE
#ifdef __GNUCC__
#define PA_GCC_PURE __attribute__ ((pure))
#else
/** This function's return value depends only the arguments list and global state **/
#define PA_GCC_PURE
#endif
#endif
#ifndef PA_GCC_CONST
#ifdef __GNUCC__
#define PA_GCC_CONST __attribute__ ((pure))
#else
/** This function's return value depends only the arguments list (stricter version of PA_GCC_CONST) **/
#define PA_GCC_CONST
#endif
#endif
#endif

View file

@ -27,6 +27,7 @@
#include <pulse/sample.h>
#include <pulse/cdecl.h>
#include <pulse/gccmacro.h>
/** \page channelmap Channel Maps
*
@ -183,7 +184,7 @@ const char* pa_channel_position_to_pretty_string(pa_channel_position_t pos);
/** Make a humand readable string from the specified channel map */
char* pa_channel_map_snprint(char *s, size_t l, const pa_channel_map *map);
/** Parse a channel position list into a channel map structure. \since 0.8.1 */
/** Parse a channel position list into a channel map structure. */
pa_channel_map *pa_channel_map_parse(pa_channel_map *map, const char *s);
/** Compare two channel maps. Return 1 if both match. */

View file

@ -46,7 +46,10 @@ int pa_client_conf_from_x11(pa_client_conf *c, const char *dname) {
pa_assert(c);
if (!dname && (!(dname = getenv("DISPLAY")) || *dname == '\0'))
if (!dname && !(dname = getenv("DISPLAY")))
goto finish;
if (*dname == 0)
goto finish;
if (!(d = XOpenDisplay(dname))) {
@ -80,7 +83,7 @@ int pa_client_conf_from_x11(pa_client_conf *c, const char *dname) {
pa_assert(sizeof(cookie) == sizeof(c->cookie));
memcpy(c->cookie, cookie, sizeof(cookie));
c->cookie_valid = 1;
c->cookie_valid = TRUE;
pa_xfree(c->cookie_file);
c->cookie_file = NULL;

View file

@ -112,13 +112,20 @@ int pa_client_conf_load(pa_client_conf *c, const char *filename) {
table[6].data = &c->cookie_file;
table[7].data = &c->disable_shm;
f = filename ?
fopen((fn = pa_xstrdup(filename)), "r") :
pa_open_config_file(DEFAULT_CLIENT_CONFIG_FILE, DEFAULT_CLIENT_CONFIG_FILE_USER, ENV_CLIENT_CONFIG_FILE, &fn, "r");
if (filename) {
if (!f && errno != EINTR) {
pa_log_warn("Failed to open configuration file '%s': %s", fn, pa_cstrerror(errno));
goto finish;
if (!(f = fopen(filename, "r"))) {
pa_log("Failed to open configuration file '%s': %s", fn, pa_cstrerror(errno));
goto finish;
}
fn = pa_xstrdup(fn);
} else {
if (!(f = pa_open_config_file(DEFAULT_CLIENT_CONFIG_FILE, DEFAULT_CLIENT_CONFIG_FILE_USER, ENV_CLIENT_CONFIG_FILE, &fn)))
if (errno != ENOENT)
goto finish;
}
r = f ? pa_config_parse(fn, f, table, NULL) : 0;
@ -126,7 +133,6 @@ int pa_client_conf_load(pa_client_conf *c, const char *filename) {
if (!r)
r = pa_client_conf_load_cookie(c);
finish:
pa_xfree(fn);

View file

@ -3,7 +3,7 @@
/***
This file is part of PulseAudio.
Copyright 2004-2006 Lennart Poettering
Copyright 2004-2008 Lennart Poettering
Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
PulseAudio is free software; you can redistribute it and/or modify
@ -35,6 +35,7 @@
#include <errno.h>
#include <signal.h>
#include <limits.h>
#include <locale.h>
#ifdef HAVE_SYS_WAIT_H
#include <sys/wait.h>
@ -52,6 +53,8 @@
#include <pulse/version.h>
#include <pulse/xmalloc.h>
#include <pulse/utf8.h>
#include <pulse/util.h>
#include <pulsecore/winsock.h>
#include <pulsecore/core-error.h>
@ -90,6 +93,7 @@ static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {
[PA_COMMAND_RECORD_STREAM_MOVED] = pa_command_stream_moved,
[PA_COMMAND_PLAYBACK_STREAM_SUSPENDED] = pa_command_stream_suspended,
[PA_COMMAND_RECORD_STREAM_SUSPENDED] = pa_command_stream_suspended,
[PA_COMMAND_STARTED] = pa_command_stream_started,
[PA_COMMAND_SUBSCRIBE_EVENT] = pa_command_subscribe_event
};
@ -97,10 +101,12 @@ static void unlock_autospawn_lock_file(pa_context *c) {
pa_assert(c);
if (c->autospawn_lock_fd >= 0) {
char lf[PATH_MAX];
pa_runtime_path(AUTOSPAWN_LOCK, lf, sizeof(lf));
char *lf;
lf = pa_runtime_path(AUTOSPAWN_LOCK);
pa_unlock_lockfile(lf, c->autospawn_lock_fd);
pa_xfree(lf);
c->autospawn_lock_fd = -1;
}
}
@ -108,20 +114,42 @@ static void unlock_autospawn_lock_file(pa_context *c) {
static void context_free(pa_context *c);
pa_context *pa_context_new(pa_mainloop_api *mainloop, const char *name) {
return pa_context_new_with_proplist(mainloop, name, NULL);
}
static void reset_callbacks(pa_context *c) {
pa_assert(c);
c->state_callback = NULL;
c->state_userdata = NULL;
c->subscribe_callback = NULL;
c->subscribe_userdata = NULL;
}
pa_context *pa_context_new_with_proplist(pa_mainloop_api *mainloop, const char *name, pa_proplist *p) {
pa_context *c;
pa_assert(mainloop);
pa_assert(name);
if (!name && !pa_proplist_contains(p, PA_PROP_APPLICATION_NAME))
return NULL;
c = pa_xnew(pa_context, 1);
PA_REFCNT_INIT(c);
c->name = pa_xstrdup(name);
c->proplist = p ? pa_proplist_copy(p) : pa_proplist_new();
if (name)
pa_proplist_sets(c->proplist, PA_PROP_APPLICATION_NAME, name);
c->mainloop = mainloop;
c->client = NULL;
c->pstream = NULL;
c->pdispatch = NULL;
c->playback_streams = pa_dynarray_new();
c->record_streams = pa_dynarray_new();
c->client_index = PA_INVALID_INDEX;
PA_LLIST_HEAD_INIT(pa_stream, c->streams);
PA_LLIST_HEAD_INIT(pa_operation, c->operations);
@ -131,18 +159,14 @@ pa_context *pa_context_new(pa_mainloop_api *mainloop, const char *name) {
c->ctag = 0;
c->csyncid = 0;
c->state_callback = NULL;
c->state_userdata = NULL;
reset_callbacks(c);
c->subscribe_callback = NULL;
c->subscribe_userdata = NULL;
c->is_local = -1;
c->is_local = FALSE;
c->server_list = NULL;
c->server = NULL;
c->autospawn_lock_fd = -1;
memset(&c->spawn_api, 0, sizeof(c->spawn_api));
c->do_autospawn = 0;
c->do_autospawn = FALSE;
#ifndef MSG_NOSIGNAL
#ifdef SIGPIPE
@ -171,26 +195,48 @@ pa_context *pa_context_new(pa_mainloop_api *mainloop, const char *name) {
return c;
}
static void context_free(pa_context *c) {
static void context_unlink(pa_context *c) {
pa_stream *s;
pa_assert(c);
unlock_autospawn_lock_file(c);
s = c->streams ? pa_stream_ref(c->streams) : NULL;
while (s) {
pa_stream *n = s->next ? pa_stream_ref(s->next) : NULL;
pa_stream_set_state(s, c->state == PA_CONTEXT_FAILED ? PA_STREAM_FAILED : PA_STREAM_TERMINATED);
pa_stream_unref(s);
s = n;
}
while (c->operations)
pa_operation_cancel(c->operations);
while (c->streams)
pa_stream_set_state(c->streams, PA_STREAM_TERMINATED);
if (c->client)
pa_socket_client_unref(c->client);
if (c->pdispatch)
if (c->pdispatch) {
pa_pdispatch_unref(c->pdispatch);
c->pdispatch = NULL;
}
if (c->pstream) {
pa_pstream_unlink(c->pstream);
pa_pstream_unref(c->pstream);
c->pstream = NULL;
}
if (c->client) {
pa_socket_client_unref(c->client);
c->client = NULL;
}
reset_callbacks(c);
}
static void context_free(pa_context *c) {
pa_assert(c);
context_unlink(c);
unlock_autospawn_lock_file(c);
if (c->record_streams)
pa_dynarray_free(c->record_streams, NULL, NULL);
if (c->playback_streams)
@ -204,7 +250,9 @@ static void context_free(pa_context *c) {
pa_strlist_free(c->server_list);
pa_xfree(c->name);
if (c->proplist)
pa_proplist_free(c->proplist);
pa_xfree(c->server);
pa_xfree(c);
}
@ -235,46 +283,16 @@ void pa_context_set_state(pa_context *c, pa_context_state_t st) {
pa_context_ref(c);
c->state = st;
if (c->state_callback)
c->state_callback(c, c->state_userdata);
if (st == PA_CONTEXT_FAILED || st == PA_CONTEXT_TERMINATED) {
pa_stream *s;
s = c->streams ? pa_stream_ref(c->streams) : NULL;
while (s) {
pa_stream *n = s->next ? pa_stream_ref(s->next) : NULL;
pa_stream_set_state(s, st == PA_CONTEXT_FAILED ? PA_STREAM_FAILED : PA_STREAM_TERMINATED);
pa_stream_unref(s);
s = n;
}
if (c->pdispatch)
pa_pdispatch_unref(c->pdispatch);
c->pdispatch = NULL;
if (c->pstream) {
pa_pstream_unlink(c->pstream);
pa_pstream_unref(c->pstream);
}
c->pstream = NULL;
if (c->client)
pa_socket_client_unref(c->client);
c->client = NULL;
}
if (st == PA_CONTEXT_FAILED || st == PA_CONTEXT_TERMINATED)
context_unlink(c);
pa_context_unref(c);
}
void pa_context_fail(pa_context *c, int error) {
pa_assert(c);
pa_assert(PA_REFCNT_VALUE(c) >= 1);
pa_context_set_error(c, error);
pa_context_set_state(c, PA_CONTEXT_FAILED);
}
int pa_context_set_error(pa_context *c, int error) {
pa_assert(error >= 0);
pa_assert(error < PA_ERR_MAX);
@ -285,6 +303,14 @@ int pa_context_set_error(pa_context *c, int error) {
return error;
}
void pa_context_fail(pa_context *c, int error) {
pa_assert(c);
pa_assert(PA_REFCNT_VALUE(c) >= 1);
pa_context_set_error(c, error);
pa_context_set_state(c, PA_CONTEXT_FAILED);
}
static void pstream_die_callback(pa_pstream *p, void *userdata) {
pa_context *c = userdata;
@ -341,25 +367,41 @@ static void pstream_memblock_callback(pa_pstream *p, uint32_t channel, int64_t o
pa_context_unref(c);
}
int pa_context_handle_error(pa_context *c, uint32_t command, pa_tagstruct *t) {
int pa_context_handle_error(pa_context *c, uint32_t command, pa_tagstruct *t, pa_bool_t fail) {
uint32_t err;
pa_assert(c);
pa_assert(PA_REFCNT_VALUE(c) >= 1);
if (command == PA_COMMAND_ERROR) {
pa_assert(t);
if (pa_tagstruct_getu32(t, &c->error) < 0) {
if (pa_tagstruct_getu32(t, &err) < 0) {
pa_context_fail(c, PA_ERR_PROTOCOL);
return -1;
}
} else if (command == PA_COMMAND_TIMEOUT)
c->error = PA_ERR_TIMEOUT;
err = PA_ERR_TIMEOUT;
else {
pa_context_fail(c, PA_ERR_PROTOCOL);
return -1;
}
if (err == PA_OK) {
pa_context_fail(c, PA_ERR_PROTOCOL);
return -1;
}
if (err >= PA_ERR_MAX)
err = PA_ERR_UNKNOWN;
if (fail) {
pa_context_fail(c, err);
return -1;
}
pa_context_set_error(c, err);
return 0;
}
@ -373,11 +415,7 @@ static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t
pa_context_ref(c);
if (command != PA_COMMAND_REPLY) {
if (pa_context_handle_error(c, command, t) < 0)
pa_context_fail(c, PA_ERR_PROTOCOL);
pa_context_fail(c, c->error);
pa_context_handle_error(c, command, t, TRUE);
goto finish;
}
@ -400,7 +438,7 @@ static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t
/* Enable shared memory support if possible */
if (c->version >= 10 &&
pa_mempool_is_shared(c->mempool) &&
c->is_local > 0) {
c->is_local) {
/* Only enable SHM if both sides are owned by the same
* user. This is a security measure because otherwise
@ -410,12 +448,18 @@ static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t
const pa_creds *creds;
if ((creds = pa_pdispatch_creds(pd)))
if (getuid() == creds->uid)
pa_pstream_use_shm(c->pstream, 1);
pa_pstream_enable_shm(c->pstream, TRUE);
#endif
}
reply = pa_tagstruct_command(c, PA_COMMAND_SET_CLIENT_NAME, &tag);
pa_tagstruct_puts(reply, c->name);
if (c->version >= 13) {
pa_init_proplist(c->proplist);
pa_tagstruct_put_proplist(reply, c->proplist);
} else
pa_tagstruct_puts(reply, pa_proplist_gets(c->proplist, PA_PROP_APPLICATION_NAME));
pa_pstream_send_tagstruct(c->pstream, reply);
pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, setup_complete_callback, c, NULL);
@ -424,11 +468,19 @@ static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t
}
case PA_CONTEXT_SETTING_NAME :
if ((c->version >= 13 && (pa_tagstruct_getu32(t, &c->client_index) < 0 ||
c->client_index == PA_INVALID_INDEX)) ||
!pa_tagstruct_eof(t)) {
pa_context_fail(c, PA_ERR_PROTOCOL);
goto finish;
}
pa_context_set_state(c, PA_CONTEXT_READY);
break;
default:
pa_assert(0);
pa_assert_not_reached();
}
finish:
@ -455,7 +507,7 @@ static void setup_context(pa_context *c, pa_iochannel *io) {
c->pdispatch = pa_pdispatch_new(c->mainloop, command_table, PA_COMMAND_MAX);
if (!c->conf->cookie_valid)
pa_log_warn("No cookie loaded. Attempting to connect without.");
pa_log_info("No cookie loaded. Attempting to connect without.");
t = pa_tagstruct_command(c, PA_COMMAND_AUTH, &tag);
pa_tagstruct_putu32(t, PA_PROTOCOL_VERSION);
@ -494,10 +546,13 @@ static int context_connect_spawn(pa_context *c) {
int fds[2] = { -1, -1} ;
pa_iochannel *io;
if (getuid() == 0)
return -1;
pa_context_ref(c);
if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) < 0) {
pa_log("socketpair(): %s", pa_cstrerror(errno));
pa_log_error("socketpair(): %s", pa_cstrerror(errno));
pa_context_fail(c, PA_ERR_INTERNAL);
goto fail;
}
@ -511,7 +566,7 @@ static int context_connect_spawn(pa_context *c) {
c->spawn_api.prefork();
if ((pid = fork()) < 0) {
pa_log("fork(): %s", pa_cstrerror(errno));
pa_log_error("fork(): %s", pa_cstrerror(errno));
pa_context_fail(c, PA_ERR_INTERNAL);
if (c->spawn_api.postfork)
@ -526,9 +581,13 @@ static int context_connect_spawn(pa_context *c) {
#define MAX_ARGS 64
const char * argv[MAX_ARGS+1];
int n;
char *f;
/* Not required, since fds[0] has CLOEXEC enabled anyway */
pa_assert_se(pa_close(fds[0]) == 0);
pa_close_all(fds[1], -1);
f = pa_sprintf_malloc("%i", fds[1]);
pa_set_env("PULSE_PASSED_FD", f);
pa_xfree(f);
if (c->spawn_api.atfork)
c->spawn_api.atfork();
@ -561,6 +620,8 @@ static int context_connect_spawn(pa_context *c) {
/* Parent */
pa_assert_se(pa_close(fds[1]) == 0);
r = waitpid(pid, &status, 0);
if (c->spawn_api.postfork)
@ -575,14 +636,12 @@ static int context_connect_spawn(pa_context *c) {
goto fail;
}
pa_assert_se(pa_close(fds[1]) == 0);
c->is_local = TRUE;
c->is_local = 1;
unlock_autospawn_lock_file(c);
io = pa_iochannel_new(c->mainloop, fds[0], fds[0]);
setup_context(c, io);
unlock_autospawn_lock_file(c);
pa_context_unref(c);
@ -634,7 +693,7 @@ static int try_next_connection(pa_context *c) {
if (!(c->client = pa_socket_client_new_string(c->mainloop, u, PA_NATIVE_DEFAULT_PORT)))
continue;
c->is_local = pa_socket_client_is_local(c->client);
c->is_local = !!pa_socket_client_is_local(c->client);
pa_socket_client_set_callback(c->client, on_connection, c);
break;
}
@ -649,6 +708,7 @@ finish:
static void on_connection(pa_socket_client *client, pa_iochannel*io, void *userdata) {
pa_context *c = userdata;
int saved_errno = errno;
pa_assert(client);
pa_assert(c);
@ -661,7 +721,9 @@ static void on_connection(pa_socket_client *client, pa_iochannel*io, void *userd
if (!io) {
/* Try the item in the list */
if (errno == ECONNREFUSED || errno == ETIMEDOUT || errno == EHOSTUNREACH) {
if (saved_errno == ECONNREFUSED ||
saved_errno == ETIMEDOUT ||
saved_errno == EHOSTUNREACH) {
try_next_connection(c);
goto finish;
}
@ -677,6 +739,25 @@ finish:
pa_context_unref(c);
}
static char *get_legacy_runtime_dir(void) {
char *p, u[128];
struct stat st;
if (!pa_get_user_name(u, sizeof(u)))
return NULL;
p = pa_sprintf_malloc("/tmp/pulse-%s", u);
if (stat(p, &st) < 0)
return NULL;
if (st.st_uid != getuid())
return NULL;
return p;
}
int pa_context_connect(
pa_context *c,
const char *server,
@ -705,8 +786,8 @@ int pa_context_connect(
goto finish;
}
} else {
char *d;
char ufn[PATH_MAX];
char *d, *ufn;
static char *legacy_dir;
/* Prepend in reverse order */
@ -726,25 +807,34 @@ int pa_context_connect(
c->server_list = pa_strlist_prepend(c->server_list, "tcp4:localhost");
/* The system wide instance */
c->server_list = pa_strlist_prepend(c->server_list, PA_SYSTEM_RUNTIME_PATH "/" PA_NATIVE_DEFAULT_UNIX_SOCKET);
c->server_list = pa_strlist_prepend(c->server_list, PA_SYSTEM_RUNTIME_PATH PA_PATH_SEP PA_NATIVE_DEFAULT_UNIX_SOCKET);
/* The old per-user instance path. This is supported only to easy upgrades */
if ((legacy_dir = get_legacy_runtime_dir())) {
char *p = pa_sprintf_malloc("%s" PA_PATH_SEP PA_NATIVE_DEFAULT_UNIX_SOCKET, legacy_dir);
c->server_list = pa_strlist_prepend(c->server_list, p);
pa_xfree(p);
pa_xfree(legacy_dir);
}
/* The per-user instance */
c->server_list = pa_strlist_prepend(c->server_list, pa_runtime_path(PA_NATIVE_DEFAULT_UNIX_SOCKET, ufn, sizeof(ufn)));
c->server_list = pa_strlist_prepend(c->server_list, ufn = pa_runtime_path(PA_NATIVE_DEFAULT_UNIX_SOCKET));
pa_xfree(ufn);
/* Wrap the connection attempts in a single transaction for sane autospawn locking */
if (!(flags & PA_CONTEXT_NOAUTOSPAWN) && c->conf->autospawn) {
char lf[PATH_MAX];
char *lf;
pa_runtime_path(AUTOSPAWN_LOCK, lf, sizeof(lf));
pa_make_secure_parent_dir(lf, 0700, (uid_t)-1, (gid_t)-1);
lf = pa_runtime_path(AUTOSPAWN_LOCK);
pa_assert(c->autospawn_lock_fd <= 0);
c->autospawn_lock_fd = pa_lock_lockfile(lf);
pa_xfree(lf);
if (api)
c->spawn_api = *api;
c->do_autospawn = 1;
}
c->do_autospawn = TRUE;
}
}
pa_context_set_state(c, PA_CONTEXT_CONNECTING);
@ -760,7 +850,8 @@ void pa_context_disconnect(pa_context *c) {
pa_assert(c);
pa_assert(PA_REFCNT_VALUE(c) >= 1);
pa_context_set_state(c, PA_CONTEXT_TERMINATED);
if (PA_CONTEXT_IS_GOOD(c->state))
pa_context_set_state(c, PA_CONTEXT_TERMINATED);
}
pa_context_state_t pa_context_get_state(pa_context *c) {
@ -781,6 +872,9 @@ void pa_context_set_state_callback(pa_context *c, pa_context_notify_cb_t cb, voi
pa_assert(c);
pa_assert(PA_REFCNT_VALUE(c) >= 1);
if (c->state == PA_CONTEXT_TERMINATED || c->state == PA_CONTEXT_FAILED)
return;
c->state_callback = cb;
c->state_userdata = userdata;
}
@ -789,11 +883,7 @@ int pa_context_is_pending(pa_context *c) {
pa_assert(c);
pa_assert(PA_REFCNT_VALUE(c) >= 1);
PA_CHECK_VALIDITY(c,
c->state == PA_CONTEXT_CONNECTING ||
c->state == PA_CONTEXT_AUTHORIZING ||
c->state == PA_CONTEXT_SETTING_NAME ||
c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY(c, PA_CONTEXT_IS_GOOD(c->state), PA_ERR_BADSTATE);
return (c->pstream && pa_pstream_is_pending(c->pstream)) ||
(c->pdispatch && pa_pdispatch_is_pending(c->pdispatch)) ||
@ -870,7 +960,7 @@ void pa_context_simple_ack_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_U
goto finish;
if (command != PA_COMMAND_REPLY) {
if (pa_context_handle_error(o->context, command, t) < 0)
if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
goto finish;
success = 0;
@ -889,25 +979,6 @@ finish:
pa_operation_unref(o);
}
pa_operation* pa_context_exit_daemon(pa_context *c, pa_context_success_cb_t cb, void *userdata) {
pa_tagstruct *t;
pa_operation *o;
uint32_t tag;
pa_assert(c);
pa_assert(PA_REFCNT_VALUE(c) >= 1);
PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
t = pa_tagstruct_command(c, PA_COMMAND_EXIT, &tag);
pa_pstream_send_tagstruct(c->pstream, t);
pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
return o;
}
pa_operation* pa_context_send_simple_command(pa_context *c, uint32_t command, pa_pdispatch_cb_t internal_cb, pa_operation_cb_t cb, void *userdata) {
pa_tagstruct *t;
pa_operation *o;
@ -927,6 +998,13 @@ pa_operation* pa_context_send_simple_command(pa_context *c, uint32_t command, pa
return o;
}
pa_operation* pa_context_exit_daemon(pa_context *c, pa_context_success_cb_t cb, void *userdata) {
pa_assert(c);
pa_assert(PA_REFCNT_VALUE(c) >= 1);
return pa_context_send_simple_command(c, PA_COMMAND_EXIT, pa_context_simple_ack_callback, (pa_operation_cb_t) cb, userdata);
}
pa_operation* pa_context_set_default_sink(pa_context *c, const char *name, pa_context_success_cb_t cb, void *userdata) {
pa_tagstruct *t;
pa_operation *o;
@ -938,7 +1016,6 @@ pa_operation* pa_context_set_default_sink(pa_context *c, const char *name, pa_co
PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
t = pa_tagstruct_command(c, PA_COMMAND_SET_DEFAULT_SINK, &tag);
pa_tagstruct_puts(t, name);
pa_pstream_send_tagstruct(c->pstream, t);
@ -958,7 +1035,6 @@ pa_operation* pa_context_set_default_source(pa_context *c, const char *name, pa_
PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
t = pa_tagstruct_command(c, PA_COMMAND_SET_DEFAULT_SOURCE, &tag);
pa_tagstruct_puts(t, name);
pa_pstream_send_tagstruct(c->pstream, t);
@ -971,15 +1047,13 @@ int pa_context_is_local(pa_context *c) {
pa_assert(c);
pa_assert(PA_REFCNT_VALUE(c) >= 1);
PA_CHECK_VALIDITY(c, c->is_local >= 0, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY_RETURN_ANY(c, PA_CONTEXT_IS_GOOD(c->state), PA_ERR_BADSTATE, -1);
return c->is_local;
return !!c->is_local;
}
pa_operation* pa_context_set_name(pa_context *c, const char *name, pa_context_success_cb_t cb, void *userdata) {
pa_tagstruct *t;
pa_operation *o;
uint32_t tag;
pa_assert(c);
pa_assert(PA_REFCNT_VALUE(c) >= 1);
@ -987,12 +1061,22 @@ pa_operation* pa_context_set_name(pa_context *c, const char *name, pa_context_su
PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
if (c->version >= 13) {
pa_proplist *p = pa_proplist_new();
t = pa_tagstruct_command(c, PA_COMMAND_SET_CLIENT_NAME, &tag);
pa_tagstruct_puts(t, name);
pa_pstream_send_tagstruct(c->pstream, t);
pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
pa_proplist_sets(p, PA_PROP_APPLICATION_NAME, name);
o = pa_context_proplist_update(c, PA_UPDATE_REPLACE, p, cb, userdata);
pa_proplist_free(p);
} else {
pa_tagstruct *t;
uint32_t tag;
o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
t = pa_tagstruct_command(c, PA_COMMAND_SET_CLIENT_NAME, &tag);
pa_tagstruct_puts(t, name);
pa_pstream_send_tagstruct(c->pstream, t);
pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
}
return o;
}
@ -1024,6 +1108,8 @@ uint32_t pa_context_get_server_protocol_version(pa_context *c) {
pa_assert(c);
pa_assert(PA_REFCNT_VALUE(c) >= 1);
PA_CHECK_VALIDITY_RETURN_ANY(c, PA_CONTEXT_IS_GOOD(c->state), PA_ERR_BADSTATE, PA_INVALID_INDEX);
return c->version;
}
@ -1039,3 +1125,153 @@ pa_tagstruct *pa_tagstruct_command(pa_context *c, uint32_t command, uint32_t *ta
return t;
}
uint32_t pa_context_get_index(pa_context *c) {
pa_assert(c);
pa_assert(PA_REFCNT_VALUE(c) >= 1);
PA_CHECK_VALIDITY_RETURN_ANY(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE, PA_INVALID_INDEX);
PA_CHECK_VALIDITY_RETURN_ANY(c, c->version >= 13, PA_ERR_NOTSUPPORTED, PA_INVALID_INDEX);
return c->client_index;
}
pa_operation *pa_context_proplist_update(pa_context *c, pa_update_mode_t mode, pa_proplist *p, pa_context_success_cb_t cb, void *userdata) {
pa_operation *o;
pa_tagstruct *t;
uint32_t tag;
pa_assert(c);
pa_assert(PA_REFCNT_VALUE(c) >= 1);
PA_CHECK_VALIDITY_RETURN_NULL(c, mode == PA_UPDATE_SET || mode == PA_UPDATE_MERGE || mode == PA_UPDATE_REPLACE, PA_ERR_INVALID);
PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 13, PA_ERR_NOTSUPPORTED);
o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
t = pa_tagstruct_command(c, PA_COMMAND_UPDATE_CLIENT_PROPLIST, &tag);
pa_tagstruct_putu32(t, (uint32_t) mode);
pa_tagstruct_put_proplist(t, p);
pa_pstream_send_tagstruct(c->pstream, t);
pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
/* Please note that we don't update c->proplist here, because we
* don't export that field */
return o;
}
pa_operation *pa_context_proplist_remove(pa_context *c, const char *const keys[], pa_context_success_cb_t cb, void *userdata) {
pa_operation *o;
pa_tagstruct *t;
uint32_t tag;
const char * const *k;
pa_assert(c);
pa_assert(PA_REFCNT_VALUE(c) >= 1);
PA_CHECK_VALIDITY_RETURN_NULL(c, keys && keys[0], PA_ERR_INVALID);
PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 13, PA_ERR_NOTSUPPORTED);
o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
t = pa_tagstruct_command(c, PA_COMMAND_REMOVE_CLIENT_PROPLIST, &tag);
for (k = keys; *k; k++)
pa_tagstruct_puts(t, *k);
pa_tagstruct_puts(t, NULL);
pa_pstream_send_tagstruct(c->pstream, t);
pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
/* Please note that we don't update c->proplist here, because we
* don't export that field */
return o;
}
void pa_init_proplist(pa_proplist *p) {
int a, b;
#ifndef HAVE_DECL_ENVIRON
extern char **environ;
#endif
char **e;
pa_assert(p);
for (e = environ; *e; e++) {
if (pa_startswith(*e, "PULSE_PROP_")) {
size_t kl = strcspn(*e+11, "=");
char *k;
if ((*e)[11+kl] != '=')
continue;
if (!pa_utf8_valid(*e+11+kl+1))
continue;
k = pa_xstrndup(*e+11, kl);
if (pa_proplist_contains(p, k)) {
pa_xfree(k);
continue;
}
pa_proplist_sets(p, k, *e+11+kl+1);
pa_xfree(k);
}
}
if (!pa_proplist_contains(p, PA_PROP_APPLICATION_PROCESS_ID)) {
char t[32];
pa_snprintf(t, sizeof(t), "%lu", (unsigned long) getpid());
pa_proplist_sets(p, PA_PROP_APPLICATION_PROCESS_ID, t);
}
if (!pa_proplist_contains(p, PA_PROP_APPLICATION_PROCESS_USER)) {
char t[64];
if (pa_get_user_name(t, sizeof(t))) {
char *c = pa_utf8_filter(t);
pa_proplist_sets(p, PA_PROP_APPLICATION_PROCESS_USER, c);
pa_xfree(c);
}
}
if (!pa_proplist_contains(p, PA_PROP_APPLICATION_PROCESS_HOST)) {
char t[64];
if (pa_get_host_name(t, sizeof(t))) {
char *c = pa_utf8_filter(t);
pa_proplist_sets(p, PA_PROP_APPLICATION_PROCESS_HOST, c);
pa_xfree(c);
}
}
a = pa_proplist_contains(p, PA_PROP_APPLICATION_PROCESS_BINARY);
b = pa_proplist_contains(p, PA_PROP_APPLICATION_NAME);
if (!a || !b) {
char t[PATH_MAX];
if (pa_get_binary_name(t, sizeof(t))) {
char *c = pa_utf8_filter(t);
if (!a)
pa_proplist_sets(p, PA_PROP_APPLICATION_PROCESS_BINARY, c);
if (!b)
pa_proplist_sets(p, PA_PROP_APPLICATION_NAME, c);
pa_xfree(c);
}
}
if (!pa_proplist_contains(p, PA_PROP_APPLICATION_LANGUAGE)) {
const char *l;
if ((l = setlocale(LC_MESSAGES, NULL)))
pa_proplist_sets(p, PA_PROP_APPLICATION_LANGUAGE, l);
}
}

View file

@ -30,6 +30,7 @@
#include <pulse/mainloop-api.h>
#include <pulse/cdecl.h>
#include <pulse/operation.h>
#include <pulse/proplist.h>
/** \page async Asynchronous API
*
@ -166,9 +167,15 @@ typedef void (*pa_context_notify_cb_t)(pa_context *c, void *userdata);
typedef void (*pa_context_success_cb_t) (pa_context *c, int success, void *userdata);
/** Instantiate a new connection context with an abstract mainloop API
* and an application name */
* and an application name. It is recommended to use pa_context_new_with_proplist()
* instead and specify some initial properties.*/
pa_context *pa_context_new(pa_mainloop_api *mainloop, const char *name);
/** Instantiate a new connection context with an abstract mainloop API
* and an application name, and specify the the initial client property
* list. \since 0.9.11 */
pa_context *pa_context_new_with_proplist(pa_mainloop_api *mainloop, const char *name, pa_proplist *proplist);
/** Decrease the reference counter of the context by one */
void pa_context_unref(pa_context *c);
@ -207,27 +214,42 @@ pa_operation* pa_context_drain(pa_context *c, pa_context_notify_cb_t cb, void *u
* returning a success notification */
pa_operation* pa_context_exit_daemon(pa_context *c, pa_context_success_cb_t cb, void *userdata);
/** Set the name of the default sink. \since 0.4 */
/** Set the name of the default sink. */
pa_operation* pa_context_set_default_sink(pa_context *c, const char *name, pa_context_success_cb_t cb, void *userdata);
/** Set the name of the default source. \since 0.4 */
/** Set the name of the default source. */
pa_operation* pa_context_set_default_source(pa_context *c, const char *name, pa_context_success_cb_t cb, void *userdata);
/** Returns 1 when the connection is to a local daemon. Returns negative when no connection has been made yet. \since 0.5 */
/** Returns 1 when the connection is to a local daemon. Returns negative when no connection has been made yet. */
int pa_context_is_local(pa_context *c);
/** Set a different application name for context on the server. \since 0.5 */
/** Set a different application name for context on the server. */
pa_operation* pa_context_set_name(pa_context *c, const char *name, pa_context_success_cb_t cb, void *userdata);
/** Return the server name this context is connected to. \since 0.7 */
/** Return the server name this context is connected to. */
const char* pa_context_get_server(pa_context *c);
/** Return the protocol version of the library. \since 0.8 */
/** Return the protocol version of the library. */
uint32_t pa_context_get_protocol_version(pa_context *c);
/** Return the protocol version of the connected server. \since 0.8 */
/** Return the protocol version of the connected server. */
uint32_t pa_context_get_server_protocol_version(pa_context *c);
/* Update the property list of the client, adding new entries. Please
* note that it is highly recommended to set as much properties
* initially via pa_context_new_with_proplist() as possible instead a
* posteriori with this function, since that information may then be
* used to route streams of the client to the right device. \since 0.9.11 */
pa_operation *pa_context_proplist_update(pa_context *c, pa_update_mode_t mode, pa_proplist *p, pa_context_success_cb_t cb, void *userdata);
/* Update the property list of the client, remove entries. \since 0.9.11 */
pa_operation *pa_context_proplist_remove(pa_context *c, const char *const keys[], pa_context_success_cb_t cb, void *userdata);
/** Return the client index this context is
* identified in the server with. This is useful for usage with the
* introspection functions, such as pa_context_get_client_info(). \since 0.9.11 */
uint32_t pa_context_get_index(pa_context *s);
PA_C_DECL_END
#endif

View file

@ -48,6 +48,15 @@ typedef enum pa_context_state {
PA_CONTEXT_TERMINATED /**< The connection was terminated cleanly */
} pa_context_state_t;
/** Return non-zero if the passed state is one of the connected states */
static inline int PA_CONTEXT_IS_GOOD(pa_context_state_t x) {
return
x == PA_CONTEXT_CONNECTING ||
x == PA_CONTEXT_AUTHORIZING ||
x == PA_CONTEXT_SETTING_NAME ||
x == PA_CONTEXT_READY;
}
/** The state of a stream */
typedef enum pa_stream_state {
PA_STREAM_UNCONNECTED, /**< The stream is not yet connected to any sink or source */
@ -57,6 +66,13 @@ typedef enum pa_stream_state {
PA_STREAM_TERMINATED /**< The stream has been terminated cleanly */
} pa_stream_state_t;
/** Return non-zero if the passed state is one of the connected states */
static inline int PA_STREAM_IS_GOOD(pa_stream_state_t x) {
return
x == PA_STREAM_CREATING ||
x == PA_STREAM_READY;
}
/** The state of an operation */
typedef enum pa_operation_state {
PA_OPERATION_RUNNING, /**< The operation is still running */
@ -67,7 +83,7 @@ typedef enum pa_operation_state {
/** An invalid index */
#define PA_INVALID_INDEX ((uint32_t) -1)
/** Some special flags for contexts. \since 0.8 */
/** Some special flags for contexts. */
typedef enum pa_context_flags {
PA_CONTEXT_NOAUTOSPAWN = 1 /**< Disabled autospawning of the PulseAudio daemon if required */
} pa_context_flags_t;
@ -80,7 +96,7 @@ typedef enum pa_stream_direction {
PA_STREAM_UPLOAD /**< Sample upload stream */
} pa_stream_direction_t;
/** Some special flags for stream connections. \since 0.6 */
/** Some special flags for stream connections. */
typedef enum pa_stream_flags {
PA_STREAM_START_CORKED = 1, /**< Create the stream corked, requiring an explicit pa_stream_cork() call to uncork it. */
PA_STREAM_INTERPOLATE_TIMING = 2, /**< Interpolate the latency for
@ -209,15 +225,73 @@ typedef enum pa_stream_flags {
* least PA 0.9.8. It is ignored
* on older servers. \since
* 0.9.8 */
PA_STREAM_PEAK_DETECT = 2048, /**< Find peaks instead of
* resampling. \since 0.9.11 */
PA_STREAM_START_MUTED = 4096, /**< Create in muted state. \since 0.9.11 */
PA_STREAM_ADJUST_LATENCY = 8192, /**< Try to adjust the latency of
* the sink/source based on the
* requested buffer metrics and
* adjust buffer metrics
* accordingly. \since 0.9.11 */
} pa_stream_flags_t;
/** English is an evil language \since 0.9.11 */
#define PA_STREAM_NOT_MONOTONIC PA_STREAM_NOT_MONOTONOUS
/** Playback and record buffer metrics */
typedef struct pa_buffer_attr {
uint32_t maxlength; /**< Maximum length of the buffer */
uint32_t tlength; /**< Playback only: target length of the buffer. The server tries to assure that at least tlength bytes are always available in the buffer */
uint32_t prebuf; /**< Playback only: pre-buffering. The server does not start with playback before at least prebug bytes are available in the buffer */
uint32_t minreq; /**< Playback only: minimum request. The server does not request less than minreq bytes from the client, instead waints until the buffer is free enough to request more bytes at once */
uint32_t fragsize; /**< Recording only: fragment size. The server sends data in blocks of fragsize bytes size. Large values deminish interactivity with other operations on the connection context but decrease control overhead. */
uint32_t maxlength; /**< Maximum length of the
* buffer. Setting this to 0 will
* initialize this to the maximum value
* supported by server, which is
* recommended. */
uint32_t tlength; /**< Playback only: target length of the
* buffer. The server tries to assure
* that at least tlength bytes are always
* available in the buffer. It is
* recommended to set this to 0, which
* will initialize this to a value that
* is deemed sensible by the
* server. However, this value will
* default to something like 2s, i.e. for
* applications that have specific
* latency requirements this value should
* be set to the maximum latency that the
* application can deal with. */
uint32_t prebuf; /**< Playback only: pre-buffering. The
* server does not start with playback
* before at least prebug bytes are
* available in the buffer. It is
* recommended to set this to 0, which
* will initialize this to the same value
* as tlength, whatever that may be. */
uint32_t minreq; /**< Playback only: minimum request. The
* server does not request less than
* minreq bytes from the client, instead
* waits until the buffer is free enough
* to request more bytes at once. It is
* recommended to set this to 0, which
* will initialize this to a value that
* is deemed sensible by the server. */
uint32_t fragsize; /**< Recording only: fragment size. The
* server sends data in blocks of
* fragsize bytes size. Large values
* deminish interactivity with other
* operations on the connection context
* but decrease control overhead. It is
* recommended to set this to 0, which
* will initialize this to a value that
* is deemed sensible by the
* server. However, this value will
* default to something like 2s, i.e. for
* applications that have specific
* latency requirements this value should
* be set to the maximum latency that the
* application can deal with. */
} pa_buffer_attr;
/** Error values as used by pa_context_errno(). Use pa_strerror() to convert these values to human readable strings */
@ -239,9 +313,10 @@ enum {
PA_ERR_MODINITFAILED, /**< Module initialization failed */
PA_ERR_BADSTATE, /**< Bad state */
PA_ERR_NODATA, /**< No data */
PA_ERR_VERSION, /**< Incompatible protocol version \since 0.8 */
PA_ERR_TOOLARGE, /**< Data too large \since 0.8.1 */
PA_ERR_VERSION, /**< Incompatible protocol version */
PA_ERR_TOOLARGE, /**< Data too large */
PA_ERR_NOTSUPPORTED, /**< Operation not supported \since 0.9.5 */
PA_ERR_UNKNOWN, /**< The error code was unknown to the client */
PA_ERR_MAX /**< Not really an error but the first invalid error code */
};
@ -255,9 +330,9 @@ typedef enum pa_subscription_mask {
PA_SUBSCRIPTION_MASK_MODULE = 16, /**< Module events */
PA_SUBSCRIPTION_MASK_CLIENT = 32, /**< Client events */
PA_SUBSCRIPTION_MASK_SAMPLE_CACHE = 64, /**< Sample cache events */
PA_SUBSCRIPTION_MASK_SERVER = 128, /**< Other global server changes. \since 0.4 */
PA_SUBSCRIPTION_MASK_AUTOLOAD = 256, /**< Autoload table events. \since 0.5 */
PA_SUBSCRIPTION_MASK_ALL = 511 /**< Catch all events \since 0.8 */
PA_SUBSCRIPTION_MASK_SERVER = 128, /**< Other global server changes. */
PA_SUBSCRIPTION_MASK_AUTOLOAD = 256, /**< Autoload table events. */
PA_SUBSCRIPTION_MASK_ALL = 511 /**< Catch all events */
} pa_subscription_mask_t;
/** Subscription event types, as used by pa_context_subscribe() */
@ -269,8 +344,8 @@ typedef enum pa_subscription_event_type {
PA_SUBSCRIPTION_EVENT_MODULE = 4, /**< Event type: Module */
PA_SUBSCRIPTION_EVENT_CLIENT = 5, /**< Event type: Client */
PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE = 6, /**< Event type: Sample cache item */
PA_SUBSCRIPTION_EVENT_SERVER = 7, /**< Event type: Global server change, only occuring with PA_SUBSCRIPTION_EVENT_CHANGE. \since 0.4 */
PA_SUBSCRIPTION_EVENT_AUTOLOAD = 8, /**< Event type: Autoload table changes. \since 0.5 */
PA_SUBSCRIPTION_EVENT_SERVER = 7, /**< Event type: Global server change, only occuring with PA_SUBSCRIPTION_EVENT_CHANGE. */
PA_SUBSCRIPTION_EVENT_AUTOLOAD = 8, /**< Event type: Autoload table changes. */
PA_SUBSCRIPTION_EVENT_FACILITY_MASK = 15, /**< A mask to extract the event type from an event value */
PA_SUBSCRIPTION_EVENT_NEW = 0, /**< A new object was created */
@ -297,7 +372,9 @@ typedef enum pa_subscription_event_type {
* source_usec+buffer_usec+transport_usec-sink_usec. (Take care of
* sign issues!) When connected to a monitor source sink_usec contains
* the latency of the owning sink. The two latency estimations
* described here are implemented in pa_stream_get_latency().*/
* described here are implemented in pa_stream_get_latency(). Please
* note that this structure can be extended as part of evolutionary
* API updates at any time in any new release.*/
typedef struct pa_timing_info {
struct timeval timestamp; /**< The time when this timing info structure was current */
int synchronized_clocks; /**< Non-zero if the local and the
@ -306,14 +383,21 @@ typedef struct pa_timing_info {
* detected transport_usec becomes much
* more reliable. However, the code that
* detects synchronized clocks is very
* limited und unreliable itself. \since
* 0.5 */
* limited und unreliable itself. */
pa_usec_t sink_usec; /**< Time in usecs a sample takes to be played on the sink. For playback streams and record streams connected to a monitor source. */
pa_usec_t source_usec; /**< Time in usecs a sample takes from being recorded to being delivered to the application. Only for record streams. \since 0.5*/
pa_usec_t transport_usec; /**< Estimated time in usecs a sample takes to be transferred to/from the daemon. For both playback and record streams. \since 0.5 */
pa_usec_t source_usec; /**< Time in usecs a sample takes from being recorded to being delivered to the application. Only for record streams. */
pa_usec_t transport_usec; /**< Estimated time in usecs a sample takes to be transferred to/from the daemon. For both playback and record streams. */
int playing; /**< Non-zero when the stream is currently playing. Only for playback streams. */
int playing; /**< Non-zero when the stream is
* currently not underrun and data is
* being passed on to the device. Only
* for playback streams. This field does
* not say whether the data is actually
* already being played. To determine
* this check whether since_underrun
* (converted to usec) is larger than
* sink_usec.*/
int write_index_corrupt; /**< Non-zero if write_index is not
* up-to-date because a local write
@ -322,20 +406,19 @@ typedef struct pa_timing_info {
* info was current . Only write
* commands with SEEK_RELATIVE_ON_READ
* and SEEK_RELATIVE_END can corrupt
* write_index. \since 0.8 */
* write_index. */
int64_t write_index; /**< Current write index into the
* playback buffer in bytes. Think twice before
* using this for seeking purposes: it
* might be out of date a the time you
* want to use it. Consider using
* PA_SEEK_RELATIVE instead. \since
* 0.8 */
* PA_SEEK_RELATIVE instead. */
int read_index_corrupt; /**< Non-zero if read_index is not
* up-to-date because a local pause or
* flush request that corrupted it has
* been issued in the time since this
* latency info was current. \since 0.8 */
* latency info was current. */
int64_t read_index; /**< Current read index into the
* playback buffer in bytes. Think twice before
@ -343,7 +426,20 @@ typedef struct pa_timing_info {
* might be out of date a the time you
* want to use it. Consider using
* PA_SEEK_RELATIVE_ON_READ
* instead. \since 0.8 */
* instead. */
pa_usec_t configured_sink_usec; /**< The static configured latency for
* the sink. \since 0.9.11 */
pa_usec_t configured_source_usec; /**< The static configured latency for
* the source. \since 0.9.11 */
int64_t since_underrun; /**< Bytes that were handed to the sink
since the last underrun happened, or
since playback started again after
the last underrun. playing will tell
you which case it is. \since
0.9.11 */
} pa_timing_info;
/** A structure for the spawn api. This may be used to integrate auto
@ -352,7 +448,7 @@ typedef struct pa_timing_info {
* waitpid() is used on the child's PID. The spawn routine will not
* block or ignore SIGCHLD signals, since this cannot be done in a
* thread compatible way. You might have to do this in
* prefork/postfork. \since 0.4 */
* prefork/postfork. */
typedef struct pa_spawn_api {
void (*prefork)(void); /**< Is called just before the fork in the parent process. May be NULL. */
void (*postfork)(void); /**< Is called immediately after the fork in the parent process. May be NULL.*/
@ -365,7 +461,7 @@ typedef struct pa_spawn_api {
* passed to the new process. */
} pa_spawn_api;
/** Seek type for pa_stream_write(). \since 0.8*/
/** Seek type for pa_stream_write(). */
typedef enum pa_seek_mode {
PA_SEEK_RELATIVE = 0, /**< Seek relatively to the write index */
PA_SEEK_ABSOLUTE = 1, /**< Seek relatively to the start of the buffer queue */
@ -373,20 +469,24 @@ typedef enum pa_seek_mode {
PA_SEEK_RELATIVE_END = 3 /**< Seek relatively to the current end of the buffer queue. */
} pa_seek_mode_t;
/** Special sink flags. \since 0.8 */
/** Special sink flags. */
typedef enum pa_sink_flags {
PA_SINK_HW_VOLUME_CTRL = 1, /**< Supports hardware volume control */
PA_SINK_LATENCY = 2, /**< Supports latency querying */
PA_SINK_HARDWARE = 4, /**< Is a hardware sink of some kind, in contrast to "virtual"/software sinks \since 0.9.3 */
PA_SINK_NETWORK = 8 /**< Is a networked sink of some kind. \since 0.9.7 */
PA_SINK_NETWORK = 8, /**< Is a networked sink of some kind. \since 0.9.7 */
PA_SINK_HW_MUTE_CTRL = 16, /**< Supports hardware mute control \since 0.9.11 */
PA_SINK_DECIBEL_VOLUME = 32 /**< Volume can be translated to dB with pa_sw_volume_to_dB() \since 0.9.11 */
} pa_sink_flags_t;
/** Special source flags. \since 0.8 */
/** Special source flags. */
typedef enum pa_source_flags {
PA_SOURCE_HW_VOLUME_CTRL = 1, /**< Supports hardware volume control */
PA_SOURCE_LATENCY = 2, /**< Supports latency querying */
PA_SOURCE_HARDWARE = 4, /**< Is a hardware source of some kind, in contrast to "virtual"/software source \since 0.9.3 */
PA_SOURCE_NETWORK = 8 /**< Is a networked sink of some kind. \since 0.9.7 */
PA_SOURCE_NETWORK = 8, /**< Is a networked sink of some kind. \since 0.9.7 */
PA_SOURCE_HW_MUTE_CTRL = 16, /**< Supports hardware mute control \since 0.9.11 */
PA_SOURCE_DECIBEL_VOLUME = 32 /**< Volume can be translated to dB with pa_sw_volume_to_dB() \since 0.9.11 */
} pa_source_flags_t;
/** A generic free() like callback prototype */

View file

@ -77,13 +77,21 @@
#endif
#endif
#ifndef PA_LIKELY
#ifndef PA_GCC_DEPRECATED
#ifdef __GNUC__
#define PA_LIKELY(x) (__builtin_expect(!!(x),1))
#define PA_UNLIKELY(x) (__builtin_expect((x),0))
#define PA_GCC_DEPRECATED __attribute__ ((deprecated))
#else
#define PA_LIKELY(x) (x)
#define PA_UNLIKELY(x) (x)
/** This function is deprecated **/
#define PA_GCC_DEPRECATED
#endif
#endif
#ifndef PA_GCC_PACKED
#ifdef __GNUCC__
#define PA_GCC_PACKED __attribute__ ((packed))
#else
/** Structure shall be packed in memory **/
#define PA_GCC_PACKED
#endif
#endif

View file

@ -42,6 +42,7 @@
#include <pulsecore/memblockq.h>
#include <pulsecore/hashmap.h>
#include <pulsecore/refcnt.h>
#include <pulsecore/time-smoother.h>
#include "client-conf.h"
@ -50,7 +51,7 @@
struct pa_context {
PA_REFCNT_DECLARE;
char *name;
pa_proplist *proplist;
pa_mainloop_api* mainloop;
pa_socket_client *client;
@ -69,14 +70,13 @@ struct pa_context {
pa_context_notify_cb_t state_callback;
void *state_userdata;
pa_context_subscribe_cb_t subscribe_callback;
void *subscribe_userdata;
pa_mempool *mempool;
int is_local;
int do_autospawn;
pa_bool_t is_local;
pa_bool_t do_autospawn;
int autospawn_lock_fd;
pa_spawn_api spawn_api;
@ -85,38 +85,43 @@ struct pa_context {
char *server;
pa_client_conf *conf;
uint32_t client_index;
};
#define PA_MAX_WRITE_INDEX_CORRECTIONS 10
#define PA_MAX_WRITE_INDEX_CORRECTIONS 32
typedef struct pa_index_correction {
uint32_t tag;
int valid;
int64_t value;
int absolute, corrupt;
pa_bool_t valid:1;
pa_bool_t absolute:1;
pa_bool_t corrupt:1;
} pa_index_correction;
struct pa_stream {
PA_REFCNT_DECLARE;
pa_context *context;
pa_mainloop_api *mainloop;
PA_LLIST_FIELDS(pa_stream);
char *name;
pa_bool_t manual_buffer_attr;
pa_buffer_attr buffer_attr;
pa_sample_spec sample_spec;
pa_channel_map channel_map;
pa_stream_flags_t flags;
uint32_t channel;
uint32_t syncid;
int channel_valid;
uint32_t stream_index;
pa_context *context;
pa_mainloop_api *mainloop;
pa_stream_direction_t direction;
pa_stream_state_t state;
pa_bool_t buffer_attr_not_ready, timing_info_not_ready;
pa_stream_flags_t flags;
pa_sample_spec sample_spec;
pa_channel_map channel_map;
pa_proplist *proplist;
uint32_t channel;
pa_bool_t channel_valid;
uint32_t syncid;
uint32_t stream_index;
uint32_t requested_bytes;
pa_buffer_attr buffer_attr;
uint32_t device_index;
char *device_name;
@ -126,11 +131,11 @@ struct pa_stream {
void *peek_data;
pa_memblockq *record_memblockq;
int corked;
pa_bool_t corked;
/* Store latest latency info */
pa_timing_info timing_info;
int timing_info_valid;
pa_bool_t timing_info_valid;
/* Use to make sure that time advances monotonically */
pa_usec_t previous_time;
@ -145,10 +150,9 @@ struct pa_stream {
/* Latency interpolation stuff */
pa_time_event *auto_timing_update_event;
int auto_timing_update_requested;
pa_bool_t auto_timing_update_requested;
pa_usec_t cached_time;
int cached_time_valid;
pa_smoother *smoother;
/* Callbacks */
pa_stream_notify_cb_t state_callback;
@ -167,6 +171,8 @@ struct pa_stream {
void *moved_userdata;
pa_stream_notify_cb_t suspended_callback;
void *suspended_userdata;
pa_stream_notify_cb_t started_callback;
void *started_userdata;
};
typedef void (*pa_operation_cb_t)(void);
@ -192,7 +198,7 @@ void pa_command_subscribe_event(pa_pdispatch *pd, uint32_t command, uint32_t tag
void pa_command_overflow_or_underflow(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
void pa_command_stream_suspended(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
void pa_command_stream_moved(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
void pa_command_stream_started(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
pa_operation *pa_operation_new(pa_context *c, pa_stream *s, pa_operation_cb_t callback, void *userdata);
void pa_operation_done(pa_operation *o);
@ -204,7 +210,7 @@ void pa_stream_simple_ack_callback(pa_pdispatch *pd, uint32_t command, uint32_t
void pa_context_fail(pa_context *c, int error);
int pa_context_set_error(pa_context *c, int error);
void pa_context_set_state(pa_context *c, pa_context_state_t st);
int pa_context_handle_error(pa_context *c, uint32_t command, pa_tagstruct *t);
int pa_context_handle_error(pa_context *c, uint32_t command, pa_tagstruct *t, pa_bool_t fail);
pa_operation* pa_context_send_simple_command(pa_context *c, uint32_t command, void (*internal_callback)(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata), void (*cb)(void), void *userdata);
void pa_stream_set_state(pa_stream *s, pa_stream_state_t st);
@ -226,5 +232,6 @@ pa_tagstruct *pa_tagstruct_command(pa_context *c, uint32_t command, uint32_t *ta
#define PA_CHECK_VALIDITY_RETURN_NULL(context, expression, error) PA_CHECK_VALIDITY_RETURN_ANY(context, expression, error, NULL)
void pa_init_proplist(pa_proplist *p);
#endif

View file

@ -27,8 +27,8 @@
#endif
#include <pulse/context.h>
#include <pulse/gccmacro.h>
#include <pulsecore/gccmacro.h>
#include <pulsecore/macro.h>
#include <pulsecore/pstream-util.h>
@ -52,7 +52,7 @@ static void context_stat_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNU
goto finish;
if (command != PA_COMMAND_REPLY) {
if (pa_context_handle_error(o->context, command, t) < 0)
if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
goto finish;
p = NULL;
@ -95,7 +95,7 @@ static void context_get_server_info_callback(pa_pdispatch *pd, uint32_t command,
goto finish;
if (command != PA_COMMAND_REPLY) {
if (pa_context_handle_error(o->context, command, t) < 0)
if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
goto finish;
p = NULL;
@ -140,7 +140,7 @@ static void context_get_sink_info_callback(pa_pdispatch *pd, uint32_t command, P
goto finish;
if (command != PA_COMMAND_REPLY) {
if (pa_context_handle_error(o->context, command, t) < 0)
if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
goto finish;
eol = -1;
@ -149,7 +149,10 @@ static void context_get_sink_info_callback(pa_pdispatch *pd, uint32_t command, P
while (!pa_tagstruct_eof(t)) {
pa_sink_info i;
pa_bool_t mute = FALSE;
memset(&i, 0, sizeof(i));
i.proplist = pa_proplist_new();
if (pa_tagstruct_getu32(t, &i.index) < 0 ||
pa_tagstruct_gets(t, &i.name) < 0 ||
@ -158,23 +161,30 @@ static void context_get_sink_info_callback(pa_pdispatch *pd, uint32_t command, P
pa_tagstruct_get_channel_map(t, &i.channel_map) < 0 ||
pa_tagstruct_getu32(t, &i.owner_module) < 0 ||
pa_tagstruct_get_cvolume(t, &i.volume) < 0 ||
pa_tagstruct_get_boolean(t, &i.mute) < 0 ||
pa_tagstruct_get_boolean(t, &mute) < 0 ||
pa_tagstruct_getu32(t, &i.monitor_source) < 0 ||
pa_tagstruct_gets(t, &i.monitor_source_name) < 0 ||
pa_tagstruct_get_usec(t, &i.latency) < 0 ||
pa_tagstruct_gets(t, &i.driver) < 0 ||
pa_tagstruct_getu32(t, &flags) < 0) {
pa_tagstruct_getu32(t, &flags) < 0 ||
(o->context->version >= 13 &&
(pa_tagstruct_get_proplist(t, i.proplist) < 0 ||
pa_tagstruct_get_usec(t, &i.configured_latency) < 0))) {
pa_context_fail(o->context, PA_ERR_PROTOCOL);
pa_proplist_free(i.proplist);
goto finish;
}
i.mute = (int) mute;
i.flags = (pa_sink_flags_t) flags;
if (o->callback) {
pa_sink_info_cb_t cb = (pa_sink_info_cb_t) o->callback;
cb(o->context, &i, 0, o->userdata);
}
pa_proplist_free(i.proplist);
}
}
@ -251,7 +261,7 @@ static void context_get_source_info_callback(pa_pdispatch *pd, uint32_t command,
goto finish;
if (command != PA_COMMAND_REPLY) {
if (pa_context_handle_error(o->context, command, t) < 0)
if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
goto finish;
eol = -1;
@ -260,7 +270,10 @@ static void context_get_source_info_callback(pa_pdispatch *pd, uint32_t command,
while (!pa_tagstruct_eof(t)) {
pa_source_info i;
uint32_t flags;
pa_bool_t mute = FALSE;
memset(&i, 0, sizeof(i));
i.proplist = pa_proplist_new();
if (pa_tagstruct_getu32(t, &i.index) < 0 ||
pa_tagstruct_gets(t, &i.name) < 0 ||
@ -269,23 +282,30 @@ static void context_get_source_info_callback(pa_pdispatch *pd, uint32_t command,
pa_tagstruct_get_channel_map(t, &i.channel_map) < 0 ||
pa_tagstruct_getu32(t, &i.owner_module) < 0 ||
pa_tagstruct_get_cvolume(t, &i.volume) < 0 ||
pa_tagstruct_get_boolean(t, &i.mute) < 0 ||
pa_tagstruct_get_boolean(t, &mute) < 0 ||
pa_tagstruct_getu32(t, &i.monitor_of_sink) < 0 ||
pa_tagstruct_gets(t, &i.monitor_of_sink_name) < 0 ||
pa_tagstruct_get_usec(t, &i.latency) < 0 ||
pa_tagstruct_gets(t, &i.driver) < 0 ||
pa_tagstruct_getu32(t, &flags) < 0) {
pa_tagstruct_getu32(t, &flags) < 0 ||
(o->context->version >= 13 &&
(pa_tagstruct_get_proplist(t, i.proplist) < 0 ||
pa_tagstruct_get_usec(t, &i.configured_latency) < 0))) {
pa_context_fail(o->context, PA_ERR_PROTOCOL);
pa_proplist_free(i.proplist);
goto finish;
}
i.mute = (int) mute;
i.flags = (pa_source_flags_t) flags;
if (o->callback) {
pa_source_info_cb_t cb = (pa_source_info_cb_t) o->callback;
cb(o->context, &i, 0, o->userdata);
}
pa_proplist_free(i.proplist);
}
}
@ -362,7 +382,7 @@ static void context_get_client_info_callback(pa_pdispatch *pd, uint32_t command,
goto finish;
if (command != PA_COMMAND_REPLY) {
if (pa_context_handle_error(o->context, command, t) < 0)
if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
goto finish;
eol = -1;
@ -370,13 +390,18 @@ static void context_get_client_info_callback(pa_pdispatch *pd, uint32_t command,
while (!pa_tagstruct_eof(t)) {
pa_client_info i;
memset(&i, 0, sizeof(i));
i.proplist = pa_proplist_new();
if (pa_tagstruct_getu32(t, &i.index) < 0 ||
pa_tagstruct_gets(t, &i.name) < 0 ||
pa_tagstruct_getu32(t, &i.owner_module) < 0 ||
pa_tagstruct_gets(t, &i.driver) < 0 ) {
pa_tagstruct_gets(t, &i.driver) < 0 ||
(o->context->version >= 13 && pa_tagstruct_get_proplist(t, i.proplist) < 0)) {
pa_context_fail(o->context, PA_ERR_PROTOCOL);
pa_proplist_free(i.proplist);
goto finish;
}
@ -384,6 +409,8 @@ static void context_get_client_info_callback(pa_pdispatch *pd, uint32_t command,
pa_client_info_cb_t cb = (pa_client_info_cb_t) o->callback;
cb(o->context, &i, 0, o->userdata);
}
pa_proplist_free(i.proplist);
}
}
@ -437,7 +464,7 @@ static void context_get_module_info_callback(pa_pdispatch *pd, uint32_t command,
goto finish;
if (command != PA_COMMAND_REPLY) {
if (pa_context_handle_error(o->context, command, t) < 0)
if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
goto finish;
eol = -1;
@ -445,17 +472,20 @@ static void context_get_module_info_callback(pa_pdispatch *pd, uint32_t command,
while (!pa_tagstruct_eof(t)) {
pa_module_info i;
pa_bool_t auto_unload = FALSE;
memset(&i, 0, sizeof(i));
if (pa_tagstruct_getu32(t, &i.index) < 0 ||
pa_tagstruct_gets(t, &i.name) < 0 ||
pa_tagstruct_gets(t, &i.argument) < 0 ||
pa_tagstruct_getu32(t, &i.n_used) < 0 ||
pa_tagstruct_get_boolean(t, &i.auto_unload) < 0) {
pa_tagstruct_get_boolean(t, &auto_unload) < 0) {
pa_context_fail(o->context, PA_ERR_PROTOCOL);
goto finish;
}
i.auto_unload = (int) auto_unload;
if (o->callback) {
pa_module_info_cb_t cb = (pa_module_info_cb_t) o->callback;
cb(o->context, &i, 0, o->userdata);
@ -513,7 +543,7 @@ static void context_get_sink_input_info_callback(pa_pdispatch *pd, uint32_t comm
goto finish;
if (command != PA_COMMAND_REPLY) {
if (pa_context_handle_error(o->context, command, t) < 0)
if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
goto finish;
eol = -1;
@ -521,7 +551,10 @@ static void context_get_sink_input_info_callback(pa_pdispatch *pd, uint32_t comm
while (!pa_tagstruct_eof(t)) {
pa_sink_input_info i;
pa_bool_t mute = FALSE;
memset(&i, 0, sizeof(i));
i.proplist = pa_proplist_new();
if (pa_tagstruct_getu32(t, &i.index) < 0 ||
pa_tagstruct_gets(t, &i.name) < 0 ||
@ -535,16 +568,22 @@ static void context_get_sink_input_info_callback(pa_pdispatch *pd, uint32_t comm
pa_tagstruct_get_usec(t, &i.sink_usec) < 0 ||
pa_tagstruct_gets(t, &i.resample_method) < 0 ||
pa_tagstruct_gets(t, &i.driver) < 0 ||
(o->context->version >= 11 && pa_tagstruct_get_boolean(t, &i.mute) < 0)) {
(o->context->version >= 11 && pa_tagstruct_get_boolean(t, &mute) < 0) ||
(o->context->version >= 13 && pa_tagstruct_get_proplist(t, i.proplist) < 0)) {
pa_context_fail(o->context, PA_ERR_PROTOCOL);
pa_proplist_free(i.proplist);
goto finish;
}
i.mute = (int) mute;
if (o->callback) {
pa_sink_input_info_cb_t cb = (pa_sink_input_info_cb_t) o->callback;
cb(o->context, &i, 0, o->userdata);
}
pa_proplist_free(i.proplist);
}
}
@ -598,7 +637,7 @@ static void context_get_source_output_info_callback(pa_pdispatch *pd, uint32_t c
goto finish;
if (command != PA_COMMAND_REPLY) {
if (pa_context_handle_error(o->context, command, t) < 0)
if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
goto finish;
eol = -1;
@ -608,6 +647,7 @@ static void context_get_source_output_info_callback(pa_pdispatch *pd, uint32_t c
pa_source_output_info i;
memset(&i, 0, sizeof(i));
i.proplist = pa_proplist_new();
if (pa_tagstruct_getu32(t, &i.index) < 0 ||
pa_tagstruct_gets(t, &i.name) < 0 ||
@ -619,9 +659,11 @@ static void context_get_source_output_info_callback(pa_pdispatch *pd, uint32_t c
pa_tagstruct_get_usec(t, &i.buffer_usec) < 0 ||
pa_tagstruct_get_usec(t, &i.source_usec) < 0 ||
pa_tagstruct_gets(t, &i.resample_method) < 0 ||
pa_tagstruct_gets(t, &i.driver) < 0) {
pa_tagstruct_gets(t, &i.driver) < 0 ||
(o->context->version >= 13 && pa_tagstruct_get_proplist(t, i.proplist) < 0)) {
pa_context_fail(o->context, PA_ERR_PROTOCOL);
pa_proplist_free(i.proplist);
goto finish;
}
@ -629,6 +671,8 @@ static void context_get_source_output_info_callback(pa_pdispatch *pd, uint32_t c
pa_source_output_info_cb_t cb = (pa_source_output_info_cb_t) o->callback;
cb(o->context, &i, 0, o->userdata);
}
pa_proplist_free(i.proplist);
}
}
@ -923,7 +967,7 @@ static void context_get_sample_info_callback(pa_pdispatch *pd, uint32_t command,
goto finish;
if (command != PA_COMMAND_REPLY) {
if (pa_context_handle_error(o->context, command, t) < 0)
if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
goto finish;
eol = -1;
@ -931,8 +975,10 @@ static void context_get_sample_info_callback(pa_pdispatch *pd, uint32_t command,
while (!pa_tagstruct_eof(t)) {
pa_sample_info i;
pa_bool_t lazy = FALSE;
memset(&i, 0, sizeof(i));
i.proplist = pa_proplist_new();
if (pa_tagstruct_getu32(t, &i.index) < 0 ||
pa_tagstruct_gets(t, &i.name) < 0 ||
@ -941,17 +987,22 @@ static void context_get_sample_info_callback(pa_pdispatch *pd, uint32_t command,
pa_tagstruct_get_sample_spec(t, &i.sample_spec) < 0 ||
pa_tagstruct_get_channel_map(t, &i.channel_map) < 0 ||
pa_tagstruct_getu32(t, &i.bytes) < 0 ||
pa_tagstruct_get_boolean(t, &i.lazy) < 0 ||
pa_tagstruct_gets(t, &i.filename) < 0) {
pa_tagstruct_get_boolean(t, &lazy) < 0 ||
pa_tagstruct_gets(t, &i.filename) < 0 ||
(o->context->version >= 13 && pa_tagstruct_get_proplist(t, i.proplist) < 0)) {
pa_context_fail(o->context, PA_ERR_PROTOCOL);
goto finish;
}
i.lazy = (int) lazy;
if (o->callback) {
pa_sample_info_cb_t cb = (pa_sample_info_cb_t) o->callback;
cb(o->context, &i, 0, o->userdata);
}
pa_proplist_free(i.proplist);
}
}
@ -1060,7 +1111,7 @@ static void context_index_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UN
goto finish;
if (command != PA_COMMAND_REPLY) {
if (pa_context_handle_error(o->context, command, t) < 0)
if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
goto finish;
idx = PA_INVALID_INDEX;
@ -1121,7 +1172,7 @@ static void context_get_autoload_info_callback(pa_pdispatch *pd, uint32_t comman
goto finish;
if (command != PA_COMMAND_REPLY) {
if (pa_context_handle_error(o->context, command, t) < 0)
if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
goto finish;
eol = -1;
@ -1158,6 +1209,8 @@ finish:
pa_operation_unref(o);
}
PA_WARN_REFERENCE(pa_context_get_autoload_info_by_name, "Autoload will no longer be implemented by future versions of the PulseAudio server.");
pa_operation* pa_context_get_autoload_info_by_name(pa_context *c, const char *name, pa_autoload_type_t type, pa_autoload_info_cb_t cb, void *userdata) {
pa_tagstruct *t;
pa_operation *o;
@ -1182,6 +1235,8 @@ pa_operation* pa_context_get_autoload_info_by_name(pa_context *c, const char *na
return o;
}
PA_WARN_REFERENCE(pa_context_get_autoload_info_by_index, "Autoload will no longer be implemented by future versions of the PulseAudio server.");
pa_operation* pa_context_get_autoload_info_by_index(pa_context *c, uint32_t idx, pa_autoload_info_cb_t cb, void *userdata) {
pa_tagstruct *t;
pa_operation *o;
@ -1204,10 +1259,15 @@ pa_operation* pa_context_get_autoload_info_by_index(pa_context *c, uint32_t idx,
return o;
}
PA_WARN_REFERENCE(pa_context_get_autoload_info_list, "Autoload will no longer be implemented by future versions of the PulseAudio server.");
pa_operation* pa_context_get_autoload_info_list(pa_context *c, pa_autoload_info_cb_t cb, void *userdata) {
return pa_context_send_simple_command(c, PA_COMMAND_GET_AUTOLOAD_INFO_LIST, context_get_autoload_info_callback, (pa_operation_cb_t) cb, userdata);
}
PA_WARN_REFERENCE(pa_context_add_autoload, "Autoload will no longer be implemented by future versions of the PulseAudio server.");
pa_operation* pa_context_add_autoload(pa_context *c, const char *name, pa_autoload_type_t type, const char *module, const char*argument, pa_context_index_cb_t cb, void* userdata) {
pa_operation *o;
pa_tagstruct *t;
@ -1234,6 +1294,8 @@ pa_operation* pa_context_add_autoload(pa_context *c, const char *name, pa_autolo
return o;
}
PA_WARN_REFERENCE(pa_context_remove_autoload_by_name, "Autoload will no longer be implemented by future versions of the PulseAudio server.");
pa_operation* pa_context_remove_autoload_by_name(pa_context *c, const char *name, pa_autoload_type_t type, pa_context_success_cb_t cb, void* userdata) {
pa_operation *o;
pa_tagstruct *t;
@ -1257,6 +1319,8 @@ pa_operation* pa_context_remove_autoload_by_name(pa_context *c, const char *name
return o;
}
PA_WARN_REFERENCE(pa_context_remove_autoload_by_index, "Autoload will no longer be implemented by future versions of the PulseAudio server.");
pa_operation* pa_context_remove_autoload_by_index(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void* userdata) {
pa_operation *o;
pa_tagstruct *t;

View file

@ -30,8 +30,10 @@
#include <pulse/operation.h>
#include <pulse/context.h>
#include <pulse/cdecl.h>
#include <pulse/gccmacro.h>
#include <pulse/channelmap.h>
#include <pulse/volume.h>
#include <pulse/proplist.h>
/** \page introspect Server Query and Control
*
@ -206,21 +208,32 @@
PA_C_DECL_BEGIN
/** Stores information about sinks */
#define PA_PORT_DIGITAL "spdif"
#define PA_PORT_ANALOG_STEREO "analog-stereo"
#define PA_PORT_ANALOG_5_1 "analog-5-1"
#define PA_PORT_ANALOG_4_0 "analog-4-0"
/** @{ \name Sinks */
/** Stores information about sinks. Please note that this structure
* can be extended as part of evolutionary API updates at any time in
* any new release. */
typedef struct pa_sink_info {
const char *name; /**< Name of the sink */
uint32_t index; /**< Index of the sink */
const char *description; /**< Description of this sink */
pa_sample_spec sample_spec; /**< Sample spec of this sink */
pa_channel_map channel_map; /**< Channel map \since 0.8 */
pa_channel_map channel_map; /**< Channel map */
uint32_t owner_module; /**< Index of the owning module of this sink, or PA_INVALID_INDEX */
pa_cvolume volume; /**< Volume of the sink */
int mute; /**< Mute switch of the sink \since 0.8 */
int mute; /**< Mute switch of the sink */
uint32_t monitor_source; /**< Index of the monitor source connected to this sink */
const char *monitor_source_name; /**< The name of the monitor source */
pa_usec_t latency; /**< Length of filled playback buffer of this sink */
const char *driver; /**< Driver name. \since 0.8 */
pa_sink_flags_t flags; /**< Flags \since 0.8 */
pa_usec_t latency; /**< Length of queued audio in the output buffer. */
const char *driver; /**< Driver name. */
pa_sink_flags_t flags; /**< Flags */
pa_proplist *proplist; /**< Property list \since 0.9.11 */
pa_usec_t configured_latency; /**< The latency this device has been configured to. \since 0.9.11 */
} pa_sink_info;
/** Callback prototype for pa_context_get_sink_info_by_name() and friends */
@ -235,21 +248,47 @@ pa_operation* pa_context_get_sink_info_by_index(pa_context *c, uint32_t id, pa_s
/** Get the complete sink list */
pa_operation* pa_context_get_sink_info_list(pa_context *c, pa_sink_info_cb_t cb, void *userdata);
/** Stores information about sources */
/** Set the volume of a sink device specified by its index */
pa_operation* pa_context_set_sink_volume_by_index(pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata);
/** Set the volume of a sink device specified by its name */
pa_operation* pa_context_set_sink_volume_by_name(pa_context *c, const char *name, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata);
/** Set the mute switch of a sink device specified by its index */
pa_operation* pa_context_set_sink_mute_by_index(pa_context *c, uint32_t idx, int mute, pa_context_success_cb_t cb, void *userdata);
/** Set the mute switch of a sink device specified by its name */
pa_operation* pa_context_set_sink_mute_by_name(pa_context *c, const char *name, int mute, pa_context_success_cb_t cb, void *userdata);
/** Suspend/Resume a sink. \since 0.9.7 */
pa_operation* pa_context_suspend_sink_by_name(pa_context *c, char *sink_name, int suspend, pa_context_success_cb_t cb, void* userdata);
/** Suspend/Resume a sink. If idx is PA_INVALID_INDEX all sinks will be suspended. \since 0.9.7 */
pa_operation* pa_context_suspend_sink_by_index(pa_context *c, uint32_t idx, int suspend, pa_context_success_cb_t cb, void* userdata);
/** @} */
/** @{ \name Sources */
/** Stores information about sources. Please note that this structure
* can be extended as part of evolutionary API updates at any time in
* any new release. */
typedef struct pa_source_info {
const char *name ; /**< Name of the source */
const char *name; /**< Name of the source */
uint32_t index; /**< Index of the source */
const char *description; /**< Description of this source */
pa_sample_spec sample_spec; /**< Sample spec of this source */
pa_channel_map channel_map; /**< Channel map \since 0.8 */
pa_channel_map channel_map; /**< Channel map */
uint32_t owner_module; /**< Owning module index, or PA_INVALID_INDEX */
pa_cvolume volume; /**< Volume of the source \since 0.8 */
int mute; /**< Mute switch of the sink \since 0.8 */
pa_cvolume volume; /**< Volume of the source */
int mute; /**< Mute switch of the sink */
uint32_t monitor_of_sink; /**< If this is a monitor source the index of the owning sink, otherwise PA_INVALID_INDEX */
const char *monitor_of_sink_name; /**< Name of the owning sink, or PA_INVALID_INDEX */
pa_usec_t latency; /**< Length of filled record buffer of this source. \since 0.5 */
const char *driver; /**< Driver name \since 0.8 */
pa_source_flags_t flags; /**< Flags \since 0.8 */
pa_usec_t latency; /**< Length of filled record buffer of this source. */
const char *driver; /**< Driver name */
pa_source_flags_t flags; /**< Flags */
pa_proplist *proplist; /**< Property list \since 0.9.11 */
pa_usec_t configured_latency; /**< The latency this device has been configured to. \since 0.9.11 */
} pa_source_info;
/** Callback prototype for pa_context_get_source_info_by_name() and friends */
@ -264,16 +303,34 @@ pa_operation* pa_context_get_source_info_by_index(pa_context *c, uint32_t id, pa
/** Get the complete source list */
pa_operation* pa_context_get_source_info_list(pa_context *c, pa_source_info_cb_t cb, void *userdata);
/** Server information */
/** Set the volume of a source device specified by its index */
pa_operation* pa_context_set_source_volume_by_index(pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata);
/** Set the volume of a source device specified by its name */
pa_operation* pa_context_set_source_volume_by_name(pa_context *c, const char *name, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata);
/** Set the mute switch of a source device specified by its index */
pa_operation* pa_context_set_source_mute_by_index(pa_context *c, uint32_t idx, int mute, pa_context_success_cb_t cb, void *userdata);
/** Set the mute switch of a source device specified by its name */
pa_operation* pa_context_set_source_mute_by_name(pa_context *c, const char *name, int mute, pa_context_success_cb_t cb, void *userdata);
/** @} */
/** @{ \name Server */
/** Server information. Please note that this structure can be
* extended as part of evolutionary API updates at any time in any new
* release. */
typedef struct pa_server_info {
const char *user_name; /**< User name of the daemon process */
const char *host_name; /**< Host name the daemon is running on */
const char *server_version; /**< Version string of the daemon */
const char *server_name; /**< Server package name (usually "pulseaudio") */
pa_sample_spec sample_spec; /**< Default sample specification */
const char *default_sink_name; /**< Name of default sink. \since 0.4 */
const char *default_source_name; /**< Name of default sink. \since 0.4*/
uint32_t cookie; /**< A random cookie for identifying this instance of PulseAudio. \since 0.8 */
const char *default_sink_name; /**< Name of default sink. */
const char *default_source_name; /**< Name of default sink. */
uint32_t cookie; /**< A random cookie for identifying this instance of PulseAudio. */
} pa_server_info;
/** Callback prototype for pa_context_get_server_info() */
@ -282,7 +339,13 @@ typedef void (*pa_server_info_cb_t) (pa_context *c, const pa_server_info*i, void
/** Get some information about the server */
pa_operation* pa_context_get_server_info(pa_context *c, pa_server_info_cb_t cb, void *userdata);
/** Stores information about modules */
/** @} */
/** @{ \name Modules */
/** Stores information about modules. Please note that this structure
* can be extended as part of evolutionary API updates at any time in
* any new release. */
typedef struct pa_module_info {
uint32_t index; /**< Index of the module */
const char*name, /**< Name of the module */
@ -300,12 +363,28 @@ pa_operation* pa_context_get_module_info(pa_context *c, uint32_t idx, pa_module_
/** Get the complete list of currently loaded modules */
pa_operation* pa_context_get_module_info_list(pa_context *c, pa_module_info_cb_t cb, void *userdata);
/** Stores information about clients */
/** Callback prototype for pa_context_load_module() */
typedef void (*pa_context_index_cb_t)(pa_context *c, uint32_t idx, void *userdata);
/** Load a module. */
pa_operation* pa_context_load_module(pa_context *c, const char*name, const char *argument, pa_context_index_cb_t cb, void *userdata);
/** Unload a module. */
pa_operation* pa_context_unload_module(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata);
/** @} */
/** @{ \name Clients */
/** Stores information about clients. Please note that this structure
* can be extended as part of evolutionary API updates at any time in
* any new release. */
typedef struct pa_client_info {
uint32_t index; /**< Index of this client */
const char *name; /**< Name of this client */
uint32_t owner_module; /**< Index of the owning module, or PA_INVALID_INDEX */
const char *driver; /**< Driver name \since 0.8 */
const char *driver; /**< Driver name */
pa_proplist *proplist; /**< Property list \since 0.9.11 */
} pa_client_info;
/** Callback prototype for pa_context_get_client_info() and firends*/
@ -317,7 +396,16 @@ pa_operation* pa_context_get_client_info(pa_context *c, uint32_t idx, pa_client_
/** Get the complete client list */
pa_operation* pa_context_get_client_info_list(pa_context *c, pa_client_info_cb_t cb, void *userdata);
/** Stores information about sink inputs */
/** Kill a client. */
pa_operation* pa_context_kill_client(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata);
/** @} */
/** @{ \name Sink Inputs */
/** Stores information about sink inputs. Please note that this structure
* can be extended as part of evolutionary API updates at any time in
* any new release. */
typedef struct pa_sink_input_info {
uint32_t index; /**< Index of the sink input */
const char *name; /**< Name of the sink input */
@ -329,9 +417,10 @@ typedef struct pa_sink_input_info {
pa_cvolume volume; /**< The volume of this sink input */
pa_usec_t buffer_usec; /**< Latency due to buffering in sink input, see pa_latency_info for details */
pa_usec_t sink_usec; /**< Latency of the sink device, see pa_latency_info for details */
const char *resample_method; /**< Thre resampling method used by this sink input. \since 0.7 */
const char *driver; /**< Driver name \since 0.8 */
const char *resample_method; /**< Thre resampling method used by this sink input. */
const char *driver; /**< Driver name */
int mute; /**< Stream muted \since 0.9.7 */
pa_proplist *proplist; /**< Property list \since 0.9.11 */
} pa_sink_input_info;
/** Callback prototype for pa_context_get_sink_input_info() and firends*/
@ -343,7 +432,28 @@ pa_operation* pa_context_get_sink_input_info(pa_context *c, uint32_t idx, pa_sin
/** Get the complete sink input list */
pa_operation* pa_context_get_sink_input_info_list(pa_context *c, pa_sink_input_info_cb_t cb, void *userdata);
/** Stores information about source outputs */
/** Move the specified sink input to a different sink. \since 0.9.5 */
pa_operation* pa_context_move_sink_input_by_name(pa_context *c, uint32_t idx, char *sink_name, pa_context_success_cb_t cb, void* userdata);
/** Move the specified sink input to a different sink. \since 0.9.5 */
pa_operation* pa_context_move_sink_input_by_index(pa_context *c, uint32_t idx, uint32_t sink_idx, pa_context_success_cb_t cb, void* userdata);
/** Set the volume of a sink input stream */
pa_operation* pa_context_set_sink_input_volume(pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata);
/** Set the mute switch of a sink input stream \since 0.9.7 */
pa_operation* pa_context_set_sink_input_mute(pa_context *c, uint32_t idx, int mute, pa_context_success_cb_t cb, void *userdata);
/** Kill a sink input. */
pa_operation* pa_context_kill_sink_input(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata);
/** @} */
/** @{ \name Source Outputs */
/** Stores information about source outputs. Please note that this structure
* can be extended as part of evolutionary API updates at any time in
* any new release. */
typedef struct pa_source_output_info {
uint32_t index; /**< Index of the sink input */
const char *name; /**< Name of the sink input */
@ -352,10 +462,11 @@ typedef struct pa_source_output_info {
uint32_t source; /**< Index of the connected source */
pa_sample_spec sample_spec; /**< The sample specification of the source output */
pa_channel_map channel_map; /**< Channel map */
pa_usec_t buffer_usec; /**< Latency due to buffering in the source output, see pa_latency_info for details. \since 0.5 */
pa_usec_t source_usec; /**< Latency of the source device, see pa_latency_info for details. \since 0.5 */
const char *resample_method; /**< Thre resampling method used by this source output. \since 0.7 */
const char *driver; /**< Driver name \since 0.8 */
pa_usec_t buffer_usec; /**< Latency due to buffering in the source output, see pa_latency_info for details. */
pa_usec_t source_usec; /**< Latency of the source device, see pa_latency_info for details. */
const char *resample_method; /**< Thre resampling method used by this source output. */
const char *driver; /**< Driver name */
pa_proplist *proplist; /**< Property list \since 0.9.11 */
} pa_source_output_info;
/** Callback prototype for pa_context_get_source_output_info() and firends*/
@ -367,43 +478,34 @@ pa_operation* pa_context_get_source_output_info(pa_context *c, uint32_t idx, pa_
/** Get the complete list of source outputs */
pa_operation* pa_context_get_source_output_info_list(pa_context *c, pa_source_output_info_cb_t cb, void *userdata);
/** Set the volume of a sink device specified by its index */
pa_operation* pa_context_set_sink_volume_by_index(pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata);
/** Move the specified source output to a different source. \since 0.9.5 */
pa_operation* pa_context_move_source_output_by_name(pa_context *c, uint32_t idx, char *source_name, pa_context_success_cb_t cb, void* userdata);
/** Set the volume of a sink device specified by its name */
pa_operation* pa_context_set_sink_volume_by_name(pa_context *c, const char *name, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata);
/** Move the specified source output to a different source. \since 0.9.5 */
pa_operation* pa_context_move_source_output_by_index(pa_context *c, uint32_t idx, uint32_t source_idx, pa_context_success_cb_t cb, void* userdata);
/** Set the mute switch of a sink device specified by its index \since 0.8 */
pa_operation* pa_context_set_sink_mute_by_index(pa_context *c, uint32_t idx, int mute, pa_context_success_cb_t cb, void *userdata);
/** Suspend/Resume a source. \since 0.9.7 */
pa_operation* pa_context_suspend_source_by_name(pa_context *c, char *source_name, int suspend, pa_context_success_cb_t cb, void* userdata);
/** Set the mute switch of a sink device specified by its name \since 0.8 */
pa_operation* pa_context_set_sink_mute_by_name(pa_context *c, const char *name, int mute, pa_context_success_cb_t cb, void *userdata);
/** Suspend/Resume a source. If idx is PA_INVALID_INDEX all sources will be suspended. \since 0.9.7 */
pa_operation* pa_context_suspend_source_by_index(pa_context *c, uint32_t idx, int suspend, pa_context_success_cb_t cb, void* userdata);
/** Set the volume of a sink input stream */
pa_operation* pa_context_set_sink_input_volume(pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata);
/** Kill a source output. */
pa_operation* pa_context_kill_source_output(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata);
/** Set the mute switch of a sink input stream \since 0.9.7 */
pa_operation* pa_context_set_sink_input_mute(pa_context *c, uint32_t idx, int mute, pa_context_success_cb_t cb, void *userdata);
/** @} */
/** Set the volume of a source device specified by its index \since 0.8 */
pa_operation* pa_context_set_source_volume_by_index(pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata);
/** @{ \name Statistics */
/** Set the volume of a source device specified by its name \since 0.8 */
pa_operation* pa_context_set_source_volume_by_name(pa_context *c, const char *name, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata);
/** Set the mute switch of a source device specified by its index \since 0.8 */
pa_operation* pa_context_set_source_mute_by_index(pa_context *c, uint32_t idx, int mute, pa_context_success_cb_t cb, void *userdata);
/** Set the mute switch of a source device specified by its name \since 0.8 */
pa_operation* pa_context_set_source_mute_by_name(pa_context *c, const char *name, int mute, pa_context_success_cb_t cb, void *userdata);
/** Memory block statistics */
/** Memory block statistics. Please note that this structure
* can be extended as part of evolutionary API updates at any time in
* any new release. */
typedef struct pa_stat_info {
uint32_t memblock_total; /**< Currently allocated memory blocks */
uint32_t memblock_total_size; /**< Currentl total size of allocated memory blocks */
uint32_t memblock_allocated; /**< Allocated memory blocks during the whole lifetime of the daemon */
uint32_t memblock_allocated_size; /**< Total size of all memory blocks allocated during the whole lifetime of the daemon */
uint32_t scache_size; /**< Total size of all sample cache entries. \since 0.4 */
uint32_t scache_size; /**< Total size of all sample cache entries. */
} pa_stat_info;
/** Callback prototype for pa_context_stat() */
@ -412,7 +514,13 @@ typedef void (*pa_stat_info_cb_t) (pa_context *c, const pa_stat_info *i, void *u
/** Get daemon memory block statistics */
pa_operation* pa_context_stat(pa_context *c, pa_stat_info_cb_t cb, void *userdata);
/** Stores information about sample cache entries */
/** @} */
/** @{ \name Cached Samples */
/** Stores information about sample cache entries. Please note that this structure
* can be extended as part of evolutionary API updates at any time in
* any new release. */
typedef struct pa_sample_info {
uint32_t index; /**< Index of this entry */
const char *name; /**< Name of this entry */
@ -420,9 +528,10 @@ typedef struct pa_sample_info {
pa_sample_spec sample_spec; /**< Sample specification of the sample */
pa_channel_map channel_map; /**< The channel map */
pa_usec_t duration; /**< Duration of this entry */
uint32_t bytes; /**< Length of this sample in bytes. \since 0.4 */
int lazy; /**< Non-zero when this is a lazy cache entry. \since 0.5 */
const char *filename; /**< In case this is a lazy cache entry, the filename for the sound file to be loaded on demand. \since 0.5 */
uint32_t bytes; /**< Length of this sample in bytes. */
int lazy; /**< Non-zero when this is a lazy cache entry. */
const char *filename; /**< In case this is a lazy cache entry, the filename for the sound file to be loaded on demand. */
pa_proplist *proplist; /**< Property list for this sample. \since 0.9.11 */
} pa_sample_info;
/** Callback prototype for pa_context_get_sample_info_by_name() and firends */
@ -437,31 +546,21 @@ pa_operation* pa_context_get_sample_info_by_index(pa_context *c, uint32_t idx, p
/** Get the complete list of samples stored in the daemon. */
pa_operation* pa_context_get_sample_info_list(pa_context *c, pa_sample_info_cb_t cb, void *userdata);
/** Kill a client. \since 0.5 */
pa_operation* pa_context_kill_client(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata);
/** @} */
/** Kill a sink input. \since 0.5 */
pa_operation* pa_context_kill_sink_input(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata);
/** \cond fulldocs */
/** Kill a source output. \since 0.5 */
pa_operation* pa_context_kill_source_output(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata);
/** @{ \name Autoload Entries */
/** Callback prototype for pa_context_load_module() and pa_context_add_autoload() */
typedef void (*pa_context_index_cb_t)(pa_context *c, uint32_t idx, void *userdata);
/** Load a module. \since 0.5 */
pa_operation* pa_context_load_module(pa_context *c, const char*name, const char *argument, pa_context_index_cb_t cb, void *userdata);
/** Unload a module. \since 0.5 */
pa_operation* pa_context_unload_module(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata);
/** Type of an autoload entry. \since 0.5 */
/** Type of an autoload entry. */
typedef enum pa_autoload_type {
PA_AUTOLOAD_SINK = 0,
PA_AUTOLOAD_SOURCE = 1
} pa_autoload_type_t;
/** Stores information about autoload entries. \since 0.5 */
/** Stores information about autoload entries. Please note that this structure
* can be extended as part of evolutionary API updates at any time in
* any new release. */
typedef struct pa_autoload_info {
uint32_t index; /**< Index of this autoload entry */
const char *name; /**< Name of the sink or source */
@ -473,47 +572,27 @@ typedef struct pa_autoload_info {
/** Callback prototype for pa_context_get_autoload_info_by_name() and firends */
typedef void (*pa_autoload_info_cb_t)(pa_context *c, const pa_autoload_info *i, int eol, void *userdata);
/** Get info about a specific autoload entry. \since 0.6 */
pa_operation* pa_context_get_autoload_info_by_name(pa_context *c, const char *name, pa_autoload_type_t type, pa_autoload_info_cb_t cb, void *userdata);
/** Get info about a specific autoload entry. */
pa_operation* pa_context_get_autoload_info_by_name(pa_context *c, const char *name, pa_autoload_type_t type, pa_autoload_info_cb_t cb, void *userdata) PA_GCC_DEPRECATED;
/** Get info about a specific autoload entry. \since 0.6 */
pa_operation* pa_context_get_autoload_info_by_index(pa_context *c, uint32_t idx, pa_autoload_info_cb_t cb, void *userdata);
/** Get info about a specific autoload entry. */
pa_operation* pa_context_get_autoload_info_by_index(pa_context *c, uint32_t idx, pa_autoload_info_cb_t cb, void *userdata) PA_GCC_DEPRECATED;
/** Get the complete list of autoload entries. \since 0.5 */
pa_operation* pa_context_get_autoload_info_list(pa_context *c, pa_autoload_info_cb_t cb, void *userdata);
/** Get the complete list of autoload entries. */
pa_operation* pa_context_get_autoload_info_list(pa_context *c, pa_autoload_info_cb_t cb, void *userdata) PA_GCC_DEPRECATED;
/** Add a new autoload entry. \since 0.5 */
pa_operation* pa_context_add_autoload(pa_context *c, const char *name, pa_autoload_type_t type, const char *module, const char*argument, pa_context_index_cb_t, void* userdata);
/** Add a new autoload entry. */
pa_operation* pa_context_add_autoload(pa_context *c, const char *name, pa_autoload_type_t type, const char *module, const char*argument, pa_context_index_cb_t, void* userdata) PA_GCC_DEPRECATED;
/** Remove an autoload entry. \since 0.6 */
pa_operation* pa_context_remove_autoload_by_name(pa_context *c, const char *name, pa_autoload_type_t type, pa_context_success_cb_t cb, void* userdata);
/** Remove an autoload entry. */
pa_operation* pa_context_remove_autoload_by_name(pa_context *c, const char *name, pa_autoload_type_t type, pa_context_success_cb_t cb, void* userdata) PA_GCC_DEPRECATED;
/** Remove an autoload entry. \since 0.6 */
pa_operation* pa_context_remove_autoload_by_index(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void* userdata);
/** Remove an autoload entry. */
pa_operation* pa_context_remove_autoload_by_index(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void* userdata) PA_GCC_DEPRECATED;
/** Move the specified sink input to a different sink. \since 0.9.5 */
pa_operation* pa_context_move_sink_input_by_name(pa_context *c, uint32_t idx, char *sink_name, pa_context_success_cb_t cb, void* userdata);
/** @} */
/** Move the specified sink input to a different sink. \since 0.9.5 */
pa_operation* pa_context_move_sink_input_by_index(pa_context *c, uint32_t idx, uint32_t sink_idx, pa_context_success_cb_t cb, void* userdata);
/** Move the specified source output to a different source. \since 0.9.5 */
pa_operation* pa_context_move_source_output_by_name(pa_context *c, uint32_t idx, char *source_name, pa_context_success_cb_t cb, void* userdata);
/** Move the specified source output to a different source. \since 0.9.5 */
pa_operation* pa_context_move_source_output_by_index(pa_context *c, uint32_t idx, uint32_t source_idx, pa_context_success_cb_t cb, void* userdata);
/** Suspend/Resume a sink. \since 0.9.7 */
pa_operation* pa_context_suspend_sink_by_name(pa_context *c, char *sink_name, int suspend, pa_context_success_cb_t cb, void* userdata);
/** Suspend/Resume a sink. If idx is PA_INVALID_INDEX all sinks will be suspended. \since 0.9.7 */
pa_operation* pa_context_suspend_sink_by_index(pa_context *c, uint32_t idx, int suspend, pa_context_success_cb_t cb, void* userdata);
/** Suspend/Resume a source. \since 0.9.7 */
pa_operation* pa_context_suspend_source_by_name(pa_context *c, char *source_name, int suspend, pa_context_success_cb_t cb, void* userdata);
/** Suspend/Resume a source. If idx is PA_INVALID_INDEX all sources will be suspended. \since 0.9.7 */
pa_operation* pa_context_suspend_source_by_index(pa_context *c, uint32_t idx, int suspend, pa_context_success_cb_t cb, void* userdata);
/** \endcond */
PA_C_DECL_END

View file

@ -28,8 +28,8 @@
#include <stdlib.h>
#include <pulse/xmalloc.h>
#include <pulse/gccmacro.h>
#include <pulsecore/gccmacro.h>
#include <pulsecore/macro.h>
#include "mainloop-api.h"
@ -75,4 +75,3 @@ void pa_mainloop_api_once(pa_mainloop_api* m, void (*callback)(pa_mainloop_api *
pa_assert_se(e = m->defer_new(m, once_callback, i));
m->defer_set_destroy(e, free_callback);
}

View file

@ -3,7 +3,7 @@
/***
This file is part of PulseAudio.
Copyright 2004-2006 Lennart Poettering
Copyright 2004-2008 Lennart Poettering
Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
PulseAudio is free software; you can redistribute it and/or modify
@ -39,11 +39,11 @@
#endif
#include <pulse/xmalloc.h>
#include <pulse/gccmacro.h>
#include <pulsecore/core-error.h>
#include <pulsecore/core-util.h>
#include <pulsecore/log.h>
#include <pulsecore/gccmacro.h>
#include <pulsecore/macro.h>
#include "mainloop-signal.h"
@ -55,9 +55,9 @@ struct pa_signal_event {
#else
void (*saved_handler)(int sig);
#endif
void (*callback) (pa_mainloop_api*a, pa_signal_event *e, int sig, void *userdata);
void *userdata;
void (*destroy_callback) (pa_mainloop_api*a, pa_signal_event*e, void *userdata);
pa_signal_cb_t callback;
pa_signal_destroy_cb_t destroy_callback;
pa_signal_event *previous, *next;
};
@ -74,6 +74,7 @@ static void signal_handler(int sig) {
#ifndef HAVE_SIGACTION
signal(sig, signal_handler);
#endif
pa_write(signal_pipe[1], &sig, sizeof(sig), NULL);
errno = saved_errno;
@ -142,23 +143,21 @@ int pa_signal_init(pa_mainloop_api *a) {
}
void pa_signal_done(void) {
pa_assert(api);
pa_assert(signal_pipe[0] >= 0);
pa_assert(signal_pipe[1] >= 0);
pa_assert(io_event);
while (signals)
pa_signal_free(signals);
api->io_free(io_event);
io_event = NULL;
if (io_event) {
pa_assert(api);
api->io_free(io_event);
io_event = NULL;
}
pa_close_pipe(signal_pipe);
api = NULL;
}
pa_signal_event* pa_signal_new(int sig, void (*_callback) (pa_mainloop_api *api, pa_signal_event*e, int sig, void *userdata), void *userdata) {
pa_signal_event* pa_signal_new(int sig, pa_signal_cb_t _callback, void *userdata) {
pa_signal_event *e = NULL;
#ifdef HAVE_SIGACTION
@ -223,7 +222,7 @@ void pa_signal_free(pa_signal_event *e) {
pa_xfree(e);
}
void pa_signal_set_destroy(pa_signal_event *e, void (*_callback) (pa_mainloop_api *api, pa_signal_event*e, void *userdata)) {
void pa_signal_set_destroy(pa_signal_event *e, pa_signal_destroy_cb_t _callback) {
pa_assert(e);
e->destroy_callback = _callback;

View file

@ -6,7 +6,7 @@
/***
This file is part of PulseAudio.
Copyright 2004-2006 Lennart Poettering
Copyright 2004-2008 Lennart Poettering
Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
PulseAudio is free software; you can redistribute it and/or modify
@ -39,23 +39,27 @@ PA_C_DECL_BEGIN
* signals. However, you may hook signal support into an abstract main loop via the routines defined herein.
*/
/** An opaque UNIX signal event source object */
typedef struct pa_signal_event pa_signal_event;
typedef void (*pa_signal_cb_t) (pa_mainloop_api *api, pa_signal_event*e, int sig, void *userdata);
typedef void (*pa_signal_destroy_cb_t) (pa_mainloop_api *api, pa_signal_event*e, void *userdata);
/** Initialize the UNIX signal subsystem and bind it to the specified main loop */
int pa_signal_init(pa_mainloop_api *api);
/** Cleanup the signal subsystem */
void pa_signal_done(void);
/** An opaque UNIX signal event source object */
typedef struct pa_signal_event pa_signal_event;
/** Create a new UNIX signal event source object */
pa_signal_event* pa_signal_new(int sig, void (*callback) (pa_mainloop_api *api, pa_signal_event*e, int sig, void *userdata), void *userdata);
pa_signal_event* pa_signal_new(int sig, pa_signal_cb_t callback, void *userdata);
/** Free a UNIX signal event source object */
void pa_signal_free(pa_signal_event *e);
/** Set a function that is called when the signal event source is destroyed. Use this to free the userdata argument if required */
void pa_signal_set_destroy(pa_signal_event *e, void (*callback) (pa_mainloop_api *api, pa_signal_event*e, void *userdata));
void pa_signal_set_destroy(pa_signal_event *e, pa_signal_destroy_cb_t callback);
PA_C_DECL_END

View file

@ -77,6 +77,23 @@ void pa_operation_unref(pa_operation *o) {
}
}
static void operation_unlink(pa_operation *o) {
pa_assert(o);
if (o->context) {
pa_assert(PA_REFCNT_VALUE(o) >= 2);
PA_LLIST_REMOVE(pa_operation, o->context->operations, o);
pa_operation_unref(o);
o->context = NULL;
}
o->stream = NULL;
o->callback = NULL;
o->userdata = NULL;
}
static void operation_set_state(pa_operation *o, pa_operation_state_t st) {
pa_assert(o);
pa_assert(PA_REFCNT_VALUE(o) >= 1);
@ -88,20 +105,8 @@ static void operation_set_state(pa_operation *o, pa_operation_state_t st) {
o->state = st;
if ((o->state == PA_OPERATION_DONE) || (o->state == PA_OPERATION_CANCELED)) {
if (o->context) {
pa_assert(PA_REFCNT_VALUE(o) >= 2);
PA_LLIST_REMOVE(pa_operation, o->context->operations, o);
pa_operation_unref(o);
}
o->context = NULL;
o->stream = NULL;
o->callback = NULL;
o->userdata = NULL;
}
if ((o->state == PA_OPERATION_DONE) || (o->state == PA_OPERATION_CANCELED))
operation_unlink(o);
pa_operation_unref(o);
}

View file

@ -69,16 +69,14 @@ pa_proplist* pa_proplist_new(void) {
}
void pa_proplist_free(pa_proplist* p) {
struct property *prop;
while ((prop = pa_hashmap_steal_first(MAKE_HASHMAP(p))))
property_free(prop);
pa_assert(p);
pa_proplist_clear(p);
pa_hashmap_free(MAKE_HASHMAP(p), NULL, NULL);
}
/** Will accept only valid UTF-8 */
int pa_proplist_puts(pa_proplist *p, const char *key, const char *value) {
int pa_proplist_sets(pa_proplist *p, const char *key, const char *value) {
struct property *prop;
pa_bool_t add = FALSE;
@ -104,7 +102,29 @@ int pa_proplist_puts(pa_proplist *p, const char *key, const char *value) {
return 0;
}
int pa_proplist_put(pa_proplist *p, const char *key, const void *data, size_t nbytes) {
/** Will accept only valid UTF-8 */
int pa_proplist_setf(pa_proplist *p, const char *key, const char *format, ...) {
va_list ap;
int r;
char *t;
pa_assert(p);
pa_assert(key);
if (!property_name_valid(key) || !pa_utf8_valid(format))
return -1;
va_start(ap, format);
t = pa_vsprintf_malloc(format, ap);
va_end(ap);
r = pa_proplist_sets(p, key, t);
pa_xfree(t);
return r;
}
int pa_proplist_set(pa_proplist *p, const char *key, const void *data, size_t nbytes) {
struct property *prop;
pa_bool_t add = FALSE;
@ -175,18 +195,27 @@ int pa_proplist_get(pa_proplist *p, const char *key, const void **data, size_t *
return 0;
}
void pa_proplist_merge(pa_proplist *p, pa_proplist *other) {
void pa_proplist_update(pa_proplist *p, pa_update_mode_t mode, pa_proplist *other) {
struct property *prop;
void *state = NULL;
pa_assert(p);
pa_assert(mode == PA_UPDATE_SET || mode == PA_UPDATE_MERGE || mode == PA_UPDATE_REPLACE);
pa_assert(other);
while ((prop = pa_hashmap_iterate(MAKE_HASHMAP(other), &state, NULL)))
pa_assert_se(pa_proplist_put(p, prop->key, prop->value, prop->nbytes) == 0);
if (mode == PA_UPDATE_SET)
pa_proplist_clear(p);
while ((prop = pa_hashmap_iterate(MAKE_HASHMAP(other), &state, NULL))) {
if (mode == PA_UPDATE_MERGE && pa_proplist_contains(p, prop->key))
continue;
pa_assert_se(pa_proplist_set(p, prop->key, prop->value, prop->nbytes) == 0);
}
}
int pa_proplist_remove(pa_proplist *p, const char *key) {
int pa_proplist_unset(pa_proplist *p, const char *key) {
struct property *prop;
pa_assert(p);
@ -196,12 +225,30 @@ int pa_proplist_remove(pa_proplist *p, const char *key) {
return -1;
if (!(prop = pa_hashmap_remove(MAKE_HASHMAP(p), key)))
return -1;
return -2;
property_free(prop);
return 0;
}
int pa_proplist_unset_many(pa_proplist *p, const char * const keys[]) {
const char * const * k;
int n = 0;
pa_assert(p);
pa_assert(keys);
for (k = keys; *k; k++)
if (!property_name_valid(*k))
return -1;
for (k = keys; *k; k++)
if (pa_proplist_unset(p, *k) >= 0)
n++;
return n;
}
const char *pa_proplist_iterate(pa_proplist *p, void **state) {
struct property *prop;
@ -255,3 +302,22 @@ int pa_proplist_contains(pa_proplist *p, const char *key) {
return 1;
}
void pa_proplist_clear(pa_proplist *p) {
struct property *prop;
pa_assert(p);
while ((prop = pa_hashmap_steal_first(MAKE_HASHMAP(p))))
property_free(prop);
}
pa_proplist* pa_proplist_copy(pa_proplist *template) {
pa_proplist *p;
pa_assert_se(p = pa_proplist_new());
if (template)
pa_proplist_update(p, PA_UPDATE_REPLACE, template);
return p;
}

View file

@ -24,67 +24,176 @@
USA.
***/
#include <pulsecore/macro.h>
#include <pulse/cdecl.h>
#include <pulse/gccmacro.h>
PA_C_DECL_BEGIN
/* Defined properties:
*
* x11.xid
* x11.display
* x11.x_pointer
* x11.y_pointer
* x11.button
* media.name
* media.title
* media.artist
* media.language
* media.name "Guns'N'Roses: Civil War"
* media.title "Civil War"
* media.artist "Guns'N'Roses"
* media.language "de_DE"
* media.filename
* media.icon
* media.icon_name
* media.role video, music, game, event, phone, production
* application.name
* media.role video, music, game, event, phone, production, filter, abstract, stream
* event.id button-click, session-login
* event.x11.display
* event.x11.xid
* event.x11.x_pointer
* event.x11.y_pointer
* event.x11.button
* application.name "Rhythmbox Media Player"
* application.id "org.gnome.rhythmbox"
* application.version
* application.icon
* application.icon_name
* application.process.id
* application.process.binary
* application.process.user
* application.process.host
* device.string
* device.api oss, alsa, sunaudio
* device.description
* device.bus_path
* device.serial
* device.vendor_product_id
* device.class sound, modem, monitor, filter
* device.form_factor laptop-speakers, external-speakers, telephone, tv-capture, webcam-capture, microphone-capture, headset
* device.connector isa, pci, usb, firewire, bluetooth
* device.access_mode mmap, mmap_rewrite, serial
* device.master_device
* device.buffer_size
*/
#define PA_PROP_X11_XID "x11.xid"
#define PA_PROP_X11_DISPLAY "x11.display"
#define PA_PROP_X11_X_POINTER "x11.x_pointer"
#define PA_PROP_X11_Y_POINTER "x11.y_pointer"
#define PA_PROP_X11_BUTTON "x11.button"
#define PA_PROP_MEDIA_NAME "media.name"
#define PA_PROP_MEDIA_TITLE "media.title"
#define PA_PROP_MEDIA_ARTIST "media.artist"
#define PA_PROP_MEDIA_LANGUAGE "media.language"
#define PA_PROP_MEDIA_FILENAME "media.filename"
#define PA_PROP_MEDIA_ICON "media.icon"
#define PA_PROP_MEDIA_ICON_NAME "media.icon_name"
#define PA_PROP_MEDIA_ROLE "media.role"
#define PA_PROP_APPLICATION_NAME "application.name"
#define PA_PROP_APPLICATION_VERSION "application.version"
#define PA_PROP_APPLICATION_ICON "application.icon"
#define PA_PROP_APPLICATION_ICON_NAME "application.icon_name"
#define PA_PROP_MEDIA_NAME "media.name"
#define PA_PROP_MEDIA_TITLE "media.title"
#define PA_PROP_MEDIA_ARTIST "media.artist"
#define PA_PROP_MEDIA_LANGUAGE "media.language"
#define PA_PROP_MEDIA_FILENAME "media.filename"
#define PA_PROP_MEDIA_ICON "media.icon"
#define PA_PROP_MEDIA_ICON_NAME "media.icon_name"
#define PA_PROP_MEDIA_ROLE "media.role"
#define PA_PROP_EVENT_ID "event.id"
#define PA_PROP_EVENT_X11_DISPLAY "event.x11.display"
#define PA_PROP_EVENT_X11_XID "event.x11.xid"
#define PA_PROP_EVENT_MOUSE_X "event.mouse.x"
#define PA_PROP_EVENT_MOUSE_Y "event.mouse.y"
#define PA_PROP_EVENT_MOUSE_BUTTON "event.mouse.button"
#define PA_PROP_APPLICATION_NAME "application.name"
#define PA_PROP_APPLICATION_ID "application.id"
#define PA_PROP_APPLICATION_VERSION "application.version"
#define PA_PROP_APPLICATION_ICON "application.icon"
#define PA_PROP_APPLICATION_ICON_NAME "application.icon_name"
#define PA_PROP_APPLICATION_LANGUAGE "application.language"
#define PA_PROP_APPLICATION_PROCESS_ID "application.process.id"
#define PA_PROP_APPLICATION_PROCESS_BINARY "application.process.binary"
#define PA_PROP_APPLICATION_PROCESS_USER "application.process.user"
#define PA_PROP_APPLICATION_PROCESS_HOST "application.process.host"
#define PA_PROP_DEVICE_STRING "device.string"
#define PA_PROP_DEVICE_API "device.api"
#define PA_PROP_DEVICE_DESCRIPTION "device.description"
#define PA_PROP_DEVICE_BUS_PATH "device.bus_path"
#define PA_PROP_DEVICE_SERIAL "device.serial"
#define PA_PROP_DEVICE_VENDOR_PRODUCT_ID "device.vendor_product_id"
#define PA_PROP_DEVICE_CLASS "device.class"
#define PA_PROP_DEVICE_FORM_FACTOR "device.form_factor"
#define PA_PROP_DEVICE_CONNECTOR "device.connector"
#define PA_PROP_DEVICE_ACCESS_MODE "device.access_mode"
#define PA_PROP_DEVICE_MASTER_DEVICE "device.master_device"
#define PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE "device.buffering.buffer_size"
#define PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE "device.buffering.fragment_size"
/** A property list object. Basically a dictionary with UTF-8 strings
* as keys and arbitrary data as values. \since 0.9.11 */
typedef struct pa_proplist pa_proplist;
/** Allocate a property list. \since 0.9.11 */
pa_proplist* pa_proplist_new(void);
/** Free the property list. \since 0.9.11 */
void pa_proplist_free(pa_proplist* p);
/** Will accept only valid UTF-8 */
int pa_proplist_puts(pa_proplist *p, const char *key, const char *value);
int pa_proplist_put(pa_proplist *p, const char *key, const void *data, size_t nbytes);
/** Append a new string entry to the property list, possibly
* overwriting an already existing entry with the same key. An
* internal copy of the data passed is made. Will accept only valid
* UTF-8. \since 0.9.11 */
int pa_proplist_sets(pa_proplist *p, const char *key, const char *value);
/* Will return NULL if the data is not valid UTF-8 */
/** Append a new string entry to the property list, possibly
* overwriting an already existing entry with the same key. An
* internal copy of the data passed is made. Will accept only valid
* UTF-8. The data can be passed as printf()-style format string with
* arguments. \since 0.9.11 */
int pa_proplist_setf(pa_proplist *p, const char *key, const char *format, ...) PA_GCC_PRINTF_ATTR(3,4);
/** Append a new arbitrary data entry to the property list, possibly
* overwriting an already existing entry with the same key. An
* internal copy of the data passed is made. \since 0.9.11 */
int pa_proplist_set(pa_proplist *p, const char *key, const void *data, size_t nbytes);
/* Return a string entry for the specified key. Will return NULL if
* the data is not valid UTF-8. Will return a NUL-terminated string in
* an internally allocated buffer. The caller should make a copy of
* the data before accessing the property list again. \since 0.9.11 */
const char *pa_proplist_gets(pa_proplist *p, const char *key);
/** Return the the value for the specified key. Will return a
* NUL-terminated string for string entries. The pointer returned will
* point to an internally allocated buffer. The caller should make a
* copy of the data before the property list is accessed again. \since
* 0.9.11 */
int pa_proplist_get(pa_proplist *p, const char *key, const void **data, size_t *nbytes);
void pa_proplist_merge(pa_proplist *p, pa_proplist *other);
int pa_proplist_remove(pa_proplist *p, const char *key);
/** Update mode enum for pa_proplist_update(). \since 0.9.11 */
typedef enum pa_update_mode {
PA_UPDATE_SET, /*< Replace the entirey property list with the new one. Don't keep any of the old data around */
PA_UPDATE_MERGE, /*< Merge new property list into the existing one, not replacing any old entries if they share a common key with the new property list. */
PA_UPDATE_REPLACE /*< Merge new property list into the existing one, replacing all old entries that share a common key with the new property list. */
} pa_update_mode_t;
/** Merge property list "other" into "p", adhering the merge mode as
* specified in "mode". \since 0.9.11 */
void pa_proplist_update(pa_proplist *p, pa_update_mode_t mode, pa_proplist *other);
/** Removes a single entry from the property list, identified be the
* specified key name. \since 0.9.11 */
int pa_proplist_unset(pa_proplist *p, const char *key);
/** Similar to pa_proplist_remove() but takes an array of keys to
* remove. The array should be terminated by a NULL pointer. Return -1
* on failure, otherwise the number of entries actually removed (which
* might even be 0, if there where no matching entries to
* remove). \since 0.9.11 */
int pa_proplist_unset_many(pa_proplist *p, const char * const keys[]);
/** Iterate through the property list. The user should allocate a
* state variable of type void* and initialize it with NULL. A pointer
* to this variable should then be passed to pa_proplist_iterate()
* which should be called in a loop until it returns NULL which
* signifies EOL. The property list should not be modified during
* iteration through the list. On each invication this function will
* return the key string for the next entry. The keys in the property
* list do not have any particular order. \since 0.9.11 */
const char *pa_proplist_iterate(pa_proplist *p, void **state);
/** Format the property list nicely as a human readable string. Call pa_xfree() on the result. \since
* 0.9.11 */
char *pa_proplist_to_string(pa_proplist *p);
/** Returns 1 if an entry for the specified key is existant in the
* property list. \since 0.9.11 */
int pa_proplist_contains(pa_proplist *p, const char *key);
/** Remove all entries from the property list object. \since 0.9.11 */
void pa_proplist_clear(pa_proplist *p);
/** Allocate a new property list and copy over every single entry from
* the specific list. \since 0.9.11 */
pa_proplist* pa_proplist_copy(pa_proplist *t);
PA_C_DECL_END
#endif

View file

@ -32,6 +32,7 @@
#include <pulsecore/core-util.h>
#include <pulsecore/macro.h>
#include <pulse/timeval.h>
#include "sample.h"
@ -70,13 +71,13 @@ size_t pa_bytes_per_second(const pa_sample_spec *spec) {
pa_usec_t pa_bytes_to_usec(uint64_t length, const pa_sample_spec *spec) {
pa_assert(spec);
return (pa_usec_t) (((double) length/pa_frame_size(spec)*1000000)/spec->rate);
return (((pa_usec_t) (length / pa_frame_size(spec)) * PA_USEC_PER_SEC) / spec->rate);
}
size_t pa_usec_to_bytes(pa_usec_t t, const pa_sample_spec *spec) {
pa_assert(spec);
return (size_t) (((double) t * spec->rate / 1000000))*pa_frame_size(spec);
return (size_t) (((t * spec->rate) / PA_USEC_PER_SEC)) * pa_frame_size(spec);
}
int pa_sample_spec_valid(const pa_sample_spec *spec) {
@ -97,7 +98,10 @@ int pa_sample_spec_equal(const pa_sample_spec*a, const pa_sample_spec*b) {
pa_assert(a);
pa_assert(b);
return (a->format == b->format) && (a->rate == b->rate) && (a->channels == b->channels);
return
(a->format == b->format) &&
(a->rate == b->rate) &&
(a->channels == b->channels);
}
const char *pa_sample_format_to_string(pa_sample_format_t f) {

View file

@ -30,6 +30,7 @@
#include <sys/param.h>
#include <math.h>
#include <pulse/gccmacro.h>
#include <pulse/cdecl.h>
/** \page sample Sample Format Specifications
@ -172,7 +173,7 @@ typedef struct pa_sample_spec {
uint8_t channels; /**< Audio channels. (1 for mono, 2 for stereo, ...) */
} pa_sample_spec;
/** Type for usec specifications (unsigned). May be either 32 or 64 bit, depending on the architecture */
/** Type for usec specifications (unsigned). Always 64 bit. */
typedef uint64_t pa_usec_t;
/** Return the amount of bytes playback of a second of audio with the specified sample type takes */

View file

@ -49,12 +49,22 @@ int pa_stream_connect_upload(pa_stream *s, size_t length) {
pa_stream_ref(s);
s->direction = PA_STREAM_UPLOAD;
s->flags = 0;
t = pa_tagstruct_command(s->context, PA_COMMAND_CREATE_UPLOAD_STREAM, &tag);
pa_tagstruct_puts(t, s->name);
if (s->context->version < 13)
pa_tagstruct_puts(t, pa_proplist_gets(s->proplist, PA_PROP_MEDIA_NAME));
pa_tagstruct_put_sample_spec(t, &s->sample_spec);
pa_tagstruct_put_channel_map(t, &s->channel_map);
pa_tagstruct_putu32(t, length);
if (s->context->version >= 13) {
pa_init_proplist(s->proplist);
pa_tagstruct_put_proplist(t, s->proplist);
}
pa_pstream_send_tagstruct(s->context->pstream, t);
pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_create_stream_callback, s, NULL);
@ -85,6 +95,73 @@ int pa_stream_finish_upload(pa_stream *s) {
return 0;
}
static void play_sample_ack_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
pa_operation *o = userdata;
int success = 1;
uint32_t idx = PA_INVALID_INDEX;
pa_assert(pd);
pa_assert(o);
pa_assert(PA_REFCNT_VALUE(o) >= 1);
if (!o->context)
goto finish;
if (command != PA_COMMAND_REPLY) {
if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
goto finish;
success = 0;
} else if ((o->context->version >= 13 && pa_tagstruct_getu32(t, &idx) < 0) ||
!pa_tagstruct_eof(t)) {
pa_context_fail(o->context, PA_ERR_PROTOCOL);
goto finish;
} else if (o->context->version >= 13 && idx == PA_INVALID_INDEX)
success = 0;
if (o->callback) {
pa_context_success_cb_t cb = (pa_context_success_cb_t) o->callback;
cb(o->context, success, o->userdata);
}
finish:
pa_operation_done(o);
pa_operation_unref(o);
}
static void play_sample_with_proplist_ack_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
pa_operation *o = userdata;
uint32_t idx;
pa_assert(pd);
pa_assert(o);
pa_assert(PA_REFCNT_VALUE(o) >= 1);
if (!o->context)
goto finish;
if (command != PA_COMMAND_REPLY) {
if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
goto finish;
idx = PA_INVALID_INDEX;
} else if (pa_tagstruct_getu32(t, &idx) < 0 ||
!pa_tagstruct_eof(t)) {
pa_context_fail(o->context, PA_ERR_PROTOCOL);
goto finish;
}
if (o->callback) {
pa_context_play_sample_cb_t cb = (pa_context_play_sample_cb_t) o->callback;
cb(o->context, idx, o->userdata);
}
finish:
pa_operation_done(o);
pa_operation_unref(o);
}
pa_operation *pa_context_play_sample(pa_context *c, const char *name, const char *dev, pa_volume_t volume, pa_context_success_cb_t cb, void *userdata) {
pa_operation *o;
pa_tagstruct *t;
@ -107,8 +184,47 @@ pa_operation *pa_context_play_sample(pa_context *c, const char *name, const char
pa_tagstruct_puts(t, dev);
pa_tagstruct_putu32(t, volume);
pa_tagstruct_puts(t, name);
if (c->version >= 13) {
pa_proplist *p = pa_proplist_new();
pa_tagstruct_put_proplist(t, p);
pa_proplist_free(p);
}
pa_pstream_send_tagstruct(c->pstream, t);
pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, play_sample_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
return o;
}
pa_operation *pa_context_play_sample_with_proplist(pa_context *c, const char *name, const char *dev, pa_volume_t volume, pa_proplist *p, pa_context_play_sample_cb_t cb, void *userdata) {
pa_operation *o;
pa_tagstruct *t;
uint32_t tag;
pa_assert(c);
pa_assert(PA_REFCNT_VALUE(c) >= 1);
PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY_RETURN_NULL(c, name && *name, PA_ERR_INVALID);
PA_CHECK_VALIDITY_RETURN_NULL(c, !dev || *dev, PA_ERR_INVALID);
PA_CHECK_VALIDITY_RETURN_NULL(c, p, PA_ERR_INVALID);
PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 13, PA_ERR_NOTSUPPORTED);
o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
if (!dev)
dev = c->conf->default_sink;
t = pa_tagstruct_command(c, PA_COMMAND_PLAY_SAMPLE, &tag);
pa_tagstruct_putu32(t, PA_INVALID_INDEX);
pa_tagstruct_puts(t, dev);
pa_tagstruct_putu32(t, volume);
pa_tagstruct_puts(t, name);
pa_tagstruct_put_proplist(t, p);
pa_pstream_send_tagstruct(c->pstream, t);
pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, play_sample_with_proplist_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
return o;
}
@ -128,9 +244,9 @@ pa_operation* pa_context_remove_sample(pa_context *c, const char *name, pa_conte
t = pa_tagstruct_command(c, PA_COMMAND_REMOVE_SAMPLE, &tag);
pa_tagstruct_puts(t, name);
pa_pstream_send_tagstruct(c->pstream, t);
pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
return o;
}

View file

@ -79,14 +79,25 @@
PA_C_DECL_BEGIN
/** Callback prototype for pa_context_play_sample_with_proplist(). The
* idx value is the index of the sink input object, or
* PA_INVALID_INDEX on failure. \since 0.9.11 */
typedef void (*pa_context_play_sample_cb_t)(pa_context *c, uint32_t idx, void *userdata);
/** Make this stream a sample upload stream */
int pa_stream_connect_upload(pa_stream *s, size_t length);
/** Finish the sample upload, the stream name will become the sample name. You cancel a samp
* le upload by issuing pa_stream_disconnect() */
/** Finish the sample upload, the stream name will become the sample
* name. You cancel a sample upload by issuing
* pa_stream_disconnect() */
int pa_stream_finish_upload(pa_stream *s);
/** Play a sample from the sample cache to the specified device. If the latter is NULL use the default sink. Returns an operation object */
/** Remove a sample from the sample cache. Returns an operation object which may be used to cancel the operation while it is running */
pa_operation* pa_context_remove_sample(pa_context *c, const char *name, pa_context_success_cb_t cb, void *userdata);
/** Play a sample from the sample cache to the specified device. If
* the latter is NULL use the default sink. Returns an operation
* object */
pa_operation* pa_context_play_sample(
pa_context *c /**< Context */,
const char *name /**< Name of the sample to play */,
@ -95,8 +106,18 @@ pa_operation* pa_context_play_sample(
pa_context_success_cb_t cb /**< Call this function after successfully starting playback, or NULL */,
void *userdata /**< Userdata to pass to the callback */);
/** Remove a sample from the sample cache. Returns an operation object which may be used to cancel the operation while it is running */
pa_operation* pa_context_remove_sample(pa_context *c, const char *name, pa_context_success_cb_t, void *userdata);
/** Play a sample from the sample cache to the specified device,
* allowing specification of a property list for the playback
* stream. If the latter is NULL use the default sink. Returns an
* operation object. \since 0.9.11 */
pa_operation* pa_context_play_sample_with_proplist(
pa_context *c /**< Context */,
const char *name /**< Name of the sample to play */,
const char *dev /**< Sink to play this sample on */,
pa_volume_t volume /**< Volume to play this sample with */ ,
pa_proplist *proplist /**< Property list for this sound. The property list of the cached entry will be merged into this property list */,
pa_context_play_sample_cb_t cb /**< Call this function after successfully starting playback, or NULL */,
void *userdata /**< Userdata to pass to the callback */);
PA_C_DECL_END

View file

@ -138,10 +138,10 @@ int pa_simple_drain(pa_simple *s, int *error);
/** Read some data from the server */
int pa_simple_read(pa_simple *s, void*data, size_t bytes, int *error);
/** Return the playback latency. \since 0.5 */
/** Return the playback latency. */
pa_usec_t pa_simple_get_latency(pa_simple *s, int *error);
/** Flush the playback buffer. \since 0.5 */
/** Flush the playback buffer. */
int pa_simple_flush(pa_simple *s, int *error);
PA_C_DECL_END

File diff suppressed because it is too large Load diff

View file

@ -276,13 +276,25 @@ typedef void (*pa_stream_request_cb_t)(pa_stream *p, size_t bytes, void *userdat
/** A generic notification callback */
typedef void (*pa_stream_notify_cb_t)(pa_stream *p, void *userdata);
/** Create a new, unconnected stream with the specified name and sample type */
/** Create a new, unconnected stream with the specified name and
* sample type. It is recommended to use pa_stream_new_with_proplist()
* instead and specify some initial properties. */
pa_stream* pa_stream_new(
pa_context *c /**< The context to create this stream in */,
const char *name /**< A name for this stream */,
const pa_sample_spec *ss /**< The desired sample format */,
const pa_channel_map *map /**< The desired channel map, or NULL for default */);
/** Create a new, unconnected stream with the specified name and
* sample type, and specify the the initial stream property
* list. \since 0.9.11 */
pa_stream* pa_stream_new_with_proplist(
pa_context *c /**< The context to create this stream in */,
const char *name /**< A name for this stream */,
const pa_sample_spec *ss /**< The desired sample format */,
const pa_channel_map *map /**< The desired channel map, or NULL for default */,
pa_proplist *p /**< The initial property list */);
/** Decrease the reference counter by one */
void pa_stream_unref(pa_stream *s);
@ -327,6 +339,10 @@ const char *pa_stream_get_device_name(pa_stream *s);
* server is older than 0.9.8. \since 0.9.8 */
int pa_stream_is_suspended(pa_stream *s);
/** Return 1 if the this stream has been corked. This will return 0 if
* not, and negative on error. \since 0.9.11 */
int pa_stream_is_corked(pa_stream *s);
/** Connect the stream to a sink */
int pa_stream_connect_playback(
pa_stream *s /**< The stream to connect to a sink */,
@ -356,7 +372,7 @@ int pa_stream_disconnect(pa_stream *s);
int pa_stream_write(
pa_stream *p /**< The stream to use */,
const void *data /**< The data to write */,
size_t bytes /**< The length of the data to write in bytes*/,
size_t nbytes /**< The length of the data to write in bytes*/,
pa_free_cb_t free_cb /**< A cleanup routine for the data or NULL to request an internal copy */,
int64_t offset, /**< Offset for seeking, must be 0 for upload streams */
pa_seek_mode_t seek /**< Seek mode, must be PA_SEEK_RELATIVE for upload streams */);
@ -365,20 +381,20 @@ int pa_stream_write(
* data will point to the actual data and length will contain the size
* of the data in bytes (which can be less than a complete framgnet).
* Use pa_stream_drop() to actually remove the data from the
* buffer. If no data is available will return a NULL pointer \since 0.8 */
* buffer. If no data is available will return a NULL pointer */
int pa_stream_peek(
pa_stream *p /**< The stream to use */,
const void **data /**< Pointer to pointer that will point to data */,
size_t *bytes /**< The length of the data read in bytes */);
size_t *nbytes /**< The length of the data read in bytes */);
/** Remove the current fragment on record streams. It is invalid to do this without first
* calling pa_stream_peek(). \since 0.8 */
* calling pa_stream_peek(). */
int pa_stream_drop(pa_stream *p);
/** Return the number of bytes that may be written using pa_stream_write() */
size_t pa_stream_writable_size(pa_stream *p);
/** Return the number of bytes that may be read using pa_stream_read() \since 0.8 */
/** Return the number of bytes that may be read using pa_stream_read()*/
size_t pa_stream_readable_size(pa_stream *p);
/** Drain a playback stream. Use this for notification when the buffer is empty */
@ -398,18 +414,25 @@ void pa_stream_set_state_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *
void pa_stream_set_write_callback(pa_stream *p, pa_stream_request_cb_t cb, void *userdata);
/** Set the callback function that is called when new data is available from the stream.
* Return the number of bytes read. \since 0.8 */
* Return the number of bytes read.*/
void pa_stream_set_read_callback(pa_stream *p, pa_stream_request_cb_t cb, void *userdata);
/** Set the callback function that is called when a buffer overflow happens. (Only for playback streams) \since 0.8 */
/** Set the callback function that is called when a buffer overflow happens. (Only for playback streams) */
void pa_stream_set_overflow_callback(pa_stream *p, pa_stream_notify_cb_t cb, void *userdata);
/** Set the callback function that is called when a buffer underflow happens. (Only for playback streams) \since 0.8 */
/** Set the callback function that is called when a buffer underflow happens. (Only for playback streams) */
void pa_stream_set_underflow_callback(pa_stream *p, pa_stream_notify_cb_t cb, void *userdata);
/** Set the callback function that is called when a the server starts
* playback after an underrun or on initial startup. This only informs
* that audio is flowing again, it is no indication that audio startet
* to reach the speakers already. (Only for playback streams). \since
* 0.9.11 */
void pa_stream_set_started_callback(pa_stream *p, pa_stream_notify_cb_t cb, void *userdata);
/** Set the callback function that is called whenever a latency
* information update happens. Useful on PA_STREAM_AUTO_TIMING_UPDATE
* streams only. (Only for playback streams) \since 0.8.2 */
* streams only. (Only for playback streams) */
void pa_stream_set_latency_update_callback(pa_stream *p, pa_stream_notify_cb_t cb, void *userdata);
/** Set the callback function that is called whenever the stream is
@ -429,24 +452,25 @@ void pa_stream_set_moved_callback(pa_stream *p, pa_stream_notify_cb_t cb, void *
* 0.9.8. \since 0.9.8 */
void pa_stream_set_suspended_callback(pa_stream *p, pa_stream_notify_cb_t cb, void *userdata);
/** Pause (or resume) playback of this stream temporarily. Available on both playback and recording streams. \since 0.3 */
/** Pause (or resume) playback of this stream temporarily. Available on both playback and recording streams. */
pa_operation* pa_stream_cork(pa_stream *s, int b, pa_stream_success_cb_t cb, void *userdata);
/** Flush the playback buffer of this stream. Most of the time you're
* better off using the parameter delta of pa_stream_write() instead of this
* function. Available on both playback and recording streams. \since 0.3 */
* better off using the parameter delta of pa_stream_write() instead
* of this function. Available on both playback and recording
* streams. */
pa_operation* pa_stream_flush(pa_stream *s, pa_stream_success_cb_t cb, void *userdata);
/** Reenable prebuffering as specified in the pa_buffer_attr
* structure. Available for playback streams only. \since 0.6 */
* structure. Available for playback streams only. */
pa_operation* pa_stream_prebuf(pa_stream *s, pa_stream_success_cb_t cb, void *userdata);
/** Request immediate start of playback on this stream. This disables
* prebuffering as specified in the pa_buffer_attr
* structure, temporarily. Available for playback streams only. \since 0.3 */
* prebuffering as specified in the pa_buffer_attr structure,
* temporarily. Available for playback streams only. */
pa_operation* pa_stream_trigger(pa_stream *s, pa_stream_success_cb_t cb, void *userdata);
/** Rename the stream. \since 0.5 */
/** Rename the stream. */
pa_operation* pa_stream_set_name(pa_stream *s, const char *name, pa_stream_success_cb_t cb, void *userdata);
/** Return the current playback/recording time. This is based on the
@ -463,13 +487,13 @@ pa_operation* pa_stream_set_name(pa_stream *s, const char *name, pa_stream_succe
* be disabled by using PA_STREAM_NOT_MONOTONOUS. This may be
* desirable to deal better with bad estimations of transport
* latencies, but may have strange effects if the application is not
* able to deal with time going 'backwards'. \since 0.6 */
* able to deal with time going 'backwards'. */
int pa_stream_get_time(pa_stream *s, pa_usec_t *r_usec);
/** Return the total stream latency. This function is based on
* pa_stream_get_time(). In case the stream is a monitoring stream the
* result can be negative, i.e. the captured samples are not yet
* played. In this case *negative is set to 1. \since 0.6 */
* played. In this case *negative is set to 1. */
int pa_stream_get_latency(pa_stream *s, pa_usec_t *r_usec, int *negative);
/** Return the latest raw timing data structure. The returned pointer
@ -481,13 +505,13 @@ int pa_stream_get_latency(pa_stream *s, pa_usec_t *r_usec, int *negative);
* function will fail with PA_ERR_NODATA. Please note that the
* write_index member field (and only this field) is updated on each
* pa_stream_write() call, not just when a timing update has been
* recieved. \since 0.8 */
* recieved. */
const pa_timing_info* pa_stream_get_timing_info(pa_stream *s);
/** Return a pointer to the stream's sample specification. \since 0.6 */
/** Return a pointer to the stream's sample specification. */
const pa_sample_spec* pa_stream_get_sample_spec(pa_stream *s);
/** Return a pointer to the stream's channel map. \since 0.8 */
/** Return a pointer to the stream's channel map. */
const pa_channel_map* pa_stream_get_channel_map(pa_stream *s);
/** Return the buffer metrics of the stream. Only valid after the
@ -510,6 +534,18 @@ pa_operation *pa_stream_set_buffer_attr(pa_stream *s, const pa_buffer_attr *attr
* is at least PulseAudio 0.9.8. \since 0.9.8 */
pa_operation *pa_stream_update_sample_rate(pa_stream *s, uint32_t rate, pa_stream_success_cb_t cb, void *userdata);
/* Update the property list of the sink input/source output of this
* stream, adding new entries. Please note that it is highly
* recommended to set as much properties initially via
* pa_stream_new_with_proplist() as possible instead a posteriori with
* this function, since that information may then be used to route
* this stream to the right device. \since 0.9.11 */
pa_operation *pa_stream_proplist_update(pa_stream *s, pa_update_mode_t mode, pa_proplist *p, pa_stream_success_cb_t cb, void *userdata);
/* Update the property list of the sink input/source output of this
* stream, remove entries. \since 0.9.11 */
pa_operation *pa_stream_proplist_remove(pa_stream *s, const char *const keys[], pa_stream_success_cb_t cb, void *userdata);
PA_C_DECL_END
#endif

View file

@ -27,7 +27,8 @@
#include <stdio.h>
#include <pulsecore/gccmacro.h>
#include <pulse/gccmacro.h>
#include <pulsecore/macro.h>
#include <pulsecore/pstream-util.h>
@ -87,6 +88,9 @@ void pa_context_set_subscribe_callback(pa_context *c, pa_context_subscribe_cb_t
pa_assert(c);
pa_assert(PA_REFCNT_VALUE(c) >= 1);
if (c->state == PA_CONTEXT_TERMINATED || c->state == PA_CONTEXT_FAILED)
return;
c->subscribe_callback = cb;
c->subscribe_userdata = userdata;
}

View file

@ -148,6 +148,24 @@ struct timeval* pa_timeval_add(struct timeval *tv, pa_usec_t v) {
return tv;
}
struct timeval* pa_timeval_sub(struct timeval *tv, pa_usec_t v) {
unsigned long secs;
pa_assert(tv);
secs = (unsigned long) (v/PA_USEC_PER_SEC);
tv->tv_sec -= secs;
v -= ((pa_usec_t) secs) * PA_USEC_PER_SEC;
if (tv->tv_usec >= (suseconds_t) v)
tv->tv_usec -= (suseconds_t) v;
else {
tv->tv_sec --;
tv->tv_usec = tv->tv_usec + PA_USEC_PER_SEC - v;
}
return tv;
}
struct timeval* pa_timeval_store(struct timeval *tv, pa_usec_t v) {
pa_assert(tv);

View file

@ -26,6 +26,7 @@
***/
#include <pulse/cdecl.h>
#include <pulse/gccmacro.h>
#include <pulse/sample.h>
/** \file
@ -37,6 +38,8 @@ PA_C_DECL_BEGIN
#define PA_USEC_PER_SEC 1000000
#define PA_NSEC_PER_SEC 1000000000
#define PA_USEC_PER_MSEC 1000
#define PA_NSEC_PER_MSEC 1000000
#define PA_NSEC_PER_USEC 1000
struct timeval;
@ -54,7 +57,10 @@ int pa_timeval_cmp(const struct timeval *a, const struct timeval *b) PA_GCC_PURE
pa_usec_t pa_timeval_age(const struct timeval *tv);
/** Add the specified time inmicroseconds to the specified timeval structure */
struct timeval* pa_timeval_add(struct timeval *tv, pa_usec_t v) PA_GCC_PURE;
struct timeval* pa_timeval_add(struct timeval *tv, pa_usec_t v);
/** Subtract the specified time inmicroseconds to the specified timeval structure. \since 0.9.11 */
struct timeval* pa_timeval_sub(struct timeval *tv, pa_usec_t v);
/** Store the specified uec value in the timeval struct. \since 0.9.7 */
struct timeval* pa_timeval_store(struct timeval *tv, pa_usec_t v);

View file

@ -26,6 +26,7 @@
***/
#include <pulse/cdecl.h>
#include <pulse/gccmacro.h>
/** \file
* UTF8 Validation functions

View file

@ -28,6 +28,7 @@
#include <stddef.h>
#include <pulse/cdecl.h>
#include <pulse/gccmacro.h>
/** \file
* Assorted utility functions */

View file

@ -39,7 +39,8 @@ a macro and not a function, so it is impossible to get the pointer of
it. */
#define pa_get_headers_version() ("@PACKAGE_VERSION@")
/** Return the version of the library the current application is linked to. */
/** Return the version of the library the current application is
* linked to. */
const char* pa_get_library_version(void);
/** The current API version. Version 6 relates to Polypaudio
@ -47,8 +48,8 @@ const char* pa_get_library_version(void);
* PA_API_VERSION undefined. */
#define PA_API_VERSION @PA_API_VERSION@
/** The current protocol version. Version 8 relates to Polypaudio 0.8/PulseAudio 0.9.
* \since 0.8 */
/** The current protocol version. Version 8 relates to Polypaudio
* 0.8/PulseAudio 0.9. */
#define PA_PROTOCOL_VERSION @PA_PROTOCOL_VERSION@
PA_C_DECL_END

View file

@ -80,10 +80,10 @@ pa_volume_t pa_sw_volume_multiply(pa_volume_t a, pa_volume_t b) {
return pa_sw_volume_from_linear(pa_sw_volume_to_linear(a)* pa_sw_volume_to_linear(b));
}
#define USER_DECIBEL_RANGE 30
#define USER_DECIBEL_RANGE 60
pa_volume_t pa_sw_volume_from_dB(double dB) {
if (dB <= -USER_DECIBEL_RANGE)
if (isinf(dB) < 0 || dB <= -USER_DECIBEL_RANGE)
return PA_VOLUME_MUTED;
return (pa_volume_t) ((dB/USER_DECIBEL_RANGE+1)*PA_VOLUME_NORM);

View file

@ -26,7 +26,9 @@
***/
#include <inttypes.h>
#include <pulse/cdecl.h>
#include <pulse/gccmacro.h>
#include <pulse/sample.h>
/** \page volume Volume Control
@ -101,10 +103,10 @@ PA_C_DECL_BEGIN
typedef uint32_t pa_volume_t;
/** Normal volume (100%) */
#define PA_VOLUME_NORM (0x10000)
#define PA_VOLUME_NORM ((pa_volume_t) 0x10000)
/** Muted volume (0%) */
#define PA_VOLUME_MUTED (0)
#define PA_VOLUME_MUTED ((pa_volume_t) 0)
/** A structure encapsulating a per-channel volume */
typedef struct pa_cvolume {
@ -149,25 +151,25 @@ int pa_cvolume_channels_equal_to(const pa_cvolume *a, pa_volume_t v) PA_GCC_PURE
pa_volume_t pa_sw_volume_multiply(pa_volume_t a, pa_volume_t b) PA_GCC_CONST;
/** Multiply to per-channel volumes and return the result in *dest. This is only valid for software volumes! */
pa_cvolume *pa_sw_cvolume_multiply(pa_cvolume *dest, const pa_cvolume *a, const pa_cvolume *b) PA_GCC_PURE;
pa_cvolume *pa_sw_cvolume_multiply(pa_cvolume *dest, const pa_cvolume *a, const pa_cvolume *b);
/** Convert a decibel value to a volume. This is only valid for software volumes! \since 0.4 */
/** Convert a decibel value to a volume. This is only valid for software volumes! */
pa_volume_t pa_sw_volume_from_dB(double f) PA_GCC_CONST;
/** Convert a volume to a decibel value. This is only valid for software volumes! \since 0.4 */
/** Convert a volume to a decibel value. This is only valid for software volumes! */
double pa_sw_volume_to_dB(pa_volume_t v) PA_GCC_CONST;
/** Convert a linear factor to a volume. This is only valid for software volumes! \since 0.8 */
/** Convert a linear factor to a volume. This is only valid for software volumes! */
pa_volume_t pa_sw_volume_from_linear(double v) PA_GCC_CONST;
/** Convert a volume to a linear factor. This is only valid for software volumes! \since 0.8 */
/** Convert a volume to a linear factor. This is only valid for software volumes! */
double pa_sw_volume_to_linear(pa_volume_t v) PA_GCC_CONST;
#ifdef INFINITY
#define PA_DECIBEL_MININFTY (-INFINITY)
#define PA_DECIBEL_MININFTY ((double) -INFINITY)
#else
/** This value is used as minus infinity when using pa_volume_{to,from}_dB(). \since 0.4 */
#define PA_DECIBEL_MININFTY (-200)
/** This value is used as minus infinity when using pa_volume_{to,from}_dB(). */
#define PA_DECIBEL_MININFTY ((double) -200)
#endif
PA_C_DECL_END

View file

@ -29,9 +29,10 @@
#include <signal.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <pulse/gccmacro.h>
#include <pulsecore/core-util.h>
#include <pulsecore/gccmacro.h>
#include <pulsecore/macro.h>
#include "xmalloc.h"
@ -123,8 +124,12 @@ char *pa_xstrndup(const char *s, size_t l) {
}
void pa_xfree(void *p) {
int saved_errno;
if (!p)
return;
saved_errno = errno;
free(p);
errno = saved_errno;
}

View file

@ -136,7 +136,7 @@ void pa_asyncmsgq_post(pa_asyncmsgq *a, pa_msgobject *object, int code, const vo
/* This mutex makes the queue multiple-writer safe. This lock is only used on the writing side */
pa_mutex_lock(a->mutex);
pa_assert_se(pa_asyncq_push(a->asyncq, i, 1) == 0);
pa_asyncq_post(a->asyncq, i);
pa_mutex_unlock(a->mutex);
}
@ -163,7 +163,7 @@ int pa_asyncmsgq_send(pa_asyncmsgq *a, pa_msgobject *object, int code, const voi
/* Thus mutex makes the queue multiple-writer safe. This lock is only used on the writing side */
pa_mutex_lock(a->mutex);
pa_assert_se(pa_asyncq_push(a->asyncq, &i, 1) == 0);
pa_assert_se(pa_asyncq_push(a->asyncq, &i, TRUE) == 0);
pa_mutex_unlock(a->mutex);
pa_semaphore_wait(i.semaphore);
@ -174,7 +174,7 @@ int pa_asyncmsgq_send(pa_asyncmsgq *a, pa_msgobject *object, int code, const voi
return i.ret;
}
int pa_asyncmsgq_get(pa_asyncmsgq *a, pa_msgobject **object, int *code, void **userdata, int64_t *offset, pa_memchunk *chunk, int wait) {
int pa_asyncmsgq_get(pa_asyncmsgq *a, pa_msgobject **object, int *code, void **userdata, int64_t *offset, pa_memchunk *chunk, pa_bool_t wait) {
pa_assert(PA_REFCNT_VALUE(a) > 0);
pa_assert(!a->current);
@ -276,22 +276,40 @@ int pa_asyncmsgq_process_one(pa_asyncmsgq *a) {
return 1;
}
int pa_asyncmsgq_get_fd(pa_asyncmsgq *a) {
int pa_asyncmsgq_read_fd(pa_asyncmsgq *a) {
pa_assert(PA_REFCNT_VALUE(a) > 0);
return pa_asyncq_get_fd(a->asyncq);
return pa_asyncq_read_fd(a->asyncq);
}
int pa_asyncmsgq_before_poll(pa_asyncmsgq *a) {
int pa_asyncmsgq_read_before_poll(pa_asyncmsgq *a) {
pa_assert(PA_REFCNT_VALUE(a) > 0);
return pa_asyncq_before_poll(a->asyncq);
return pa_asyncq_read_before_poll(a->asyncq);
}
void pa_asyncmsgq_after_poll(pa_asyncmsgq *a) {
void pa_asyncmsgq_read_after_poll(pa_asyncmsgq *a) {
pa_assert(PA_REFCNT_VALUE(a) > 0);
pa_asyncq_after_poll(a->asyncq);
pa_asyncq_read_after_poll(a->asyncq);
}
int pa_asyncmsgq_write_fd(pa_asyncmsgq *a) {
pa_assert(PA_REFCNT_VALUE(a) > 0);
return pa_asyncq_write_fd(a->asyncq);
}
void pa_asyncmsgq_write_before_poll(pa_asyncmsgq *a) {
pa_assert(PA_REFCNT_VALUE(a) > 0);
pa_asyncq_write_before_poll(a->asyncq);
}
void pa_asyncmsgq_write_after_poll(pa_asyncmsgq *a) {
pa_assert(PA_REFCNT_VALUE(a) > 0);
pa_asyncq_write_after_poll(a->asyncq);
}
int pa_asyncmsgq_dispatch(pa_msgobject *object, int code, void *userdata, int64_t offset, pa_memchunk *memchunk) {

View file

@ -56,20 +56,26 @@ typedef struct pa_asyncmsgq pa_asyncmsgq;
pa_asyncmsgq* pa_asyncmsgq_new(unsigned size);
pa_asyncmsgq* pa_asyncmsgq_ref(pa_asyncmsgq *q);
void pa_asyncmsgq_unref(pa_asyncmsgq* q);
void pa_asyncmsgq_post(pa_asyncmsgq *q, pa_msgobject *object, int code, const void *userdata, int64_t offset, const pa_memchunk *memchunk, pa_free_cb_t userdata_free_cb);
int pa_asyncmsgq_send(pa_asyncmsgq *q, pa_msgobject *object, int code, const void *userdata, int64_t offset, const pa_memchunk *memchunk);
int pa_asyncmsgq_get(pa_asyncmsgq *q, pa_msgobject **object, int *code, void **userdata, int64_t *offset, pa_memchunk *memchunk, int wait);
int pa_asyncmsgq_get(pa_asyncmsgq *q, pa_msgobject **object, int *code, void **userdata, int64_t *offset, pa_memchunk *memchunk, pa_bool_t wait);
int pa_asyncmsgq_dispatch(pa_msgobject *object, int code, void *userdata, int64_t offset, pa_memchunk *memchunk);
void pa_asyncmsgq_done(pa_asyncmsgq *q, int ret);
int pa_asyncmsgq_wait_for(pa_asyncmsgq *a, int code);
int pa_asyncmsgq_process_one(pa_asyncmsgq *a);
/* Just for the reading side */
int pa_asyncmsgq_get_fd(pa_asyncmsgq *q);
int pa_asyncmsgq_before_poll(pa_asyncmsgq *a);
void pa_asyncmsgq_after_poll(pa_asyncmsgq *a);
/* For the reading side */
int pa_asyncmsgq_read_fd(pa_asyncmsgq *q);
int pa_asyncmsgq_read_before_poll(pa_asyncmsgq *a);
void pa_asyncmsgq_read_after_poll(pa_asyncmsgq *a);
/* For the write side */
int pa_asyncmsgq_write_fd(pa_asyncmsgq *q);
void pa_asyncmsgq_write_before_poll(pa_asyncmsgq *a);
void pa_asyncmsgq_write_after_poll(pa_asyncmsgq *a);
#endif

View file

@ -3,7 +3,7 @@
/***
This file is part of PulseAudio.
Copyright 2006 Lennart Poettering
Copyright 2006-2008 Lennart Poettering
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
@ -33,14 +33,16 @@
#include <pulsecore/thread.h>
#include <pulsecore/macro.h>
#include <pulsecore/core-util.h>
#include <pulsecore/llist.h>
#include <pulsecore/flist.h>
#include <pulse/xmalloc.h>
#include "asyncq.h"
#include "fdsem.h"
#define ASYNCQ_SIZE 128
#define ASYNCQ_SIZE 256
/* For debugging purposes we can define _Y to put and extra thread
/* For debugging purposes we can define _Y to put an extra thread
* yield between each operation. */
/* #define PROFILE */
@ -51,18 +53,25 @@
#define _Y do { } while(0)
#endif
struct localq {
void *data;
PA_LLIST_FIELDS(struct localq);
};
struct pa_asyncq {
unsigned size;
unsigned read_idx;
unsigned write_idx;
pa_fdsem *read_fdsem, *write_fdsem;
PA_LLIST_HEAD(struct localq, localq);
struct localq *last_localq;
pa_bool_t waiting_for_post;
};
#define PA_ASYNCQ_CELLS(x) ((pa_atomic_ptr_t*) ((uint8_t*) (x) + PA_ALIGN(sizeof(struct pa_asyncq))))
PA_STATIC_FLIST_DECLARE(localq, 0, pa_xfree);
static int is_power_of_two(unsigned size) {
return !(size & (size - 1));
}
#define PA_ASYNCQ_CELLS(x) ((pa_atomic_ptr_t*) ((uint8_t*) (x) + PA_ALIGN(sizeof(struct pa_asyncq))))
static int reduce(pa_asyncq *l, int value) {
return value & (unsigned) (l->size - 1);
@ -74,12 +83,16 @@ pa_asyncq *pa_asyncq_new(unsigned size) {
if (!size)
size = ASYNCQ_SIZE;
pa_assert(is_power_of_two(size));
pa_assert(pa_is_power_of_two(size));
l = pa_xmalloc0(PA_ALIGN(sizeof(pa_asyncq)) + (sizeof(pa_atomic_ptr_t) * size));
l->size = size;
PA_LLIST_HEAD_INIT(struct localq, l->localq);
l->last_localq = NULL;
l->waiting_for_post = FALSE;
if (!(l->read_fdsem = pa_fdsem_new())) {
pa_xfree(l);
return NULL;
@ -95,6 +108,7 @@ pa_asyncq *pa_asyncq_new(unsigned size) {
}
void pa_asyncq_free(pa_asyncq *l, pa_free_cb_t free_cb) {
struct localq *q;
pa_assert(l);
if (free_cb) {
@ -104,12 +118,22 @@ void pa_asyncq_free(pa_asyncq *l, pa_free_cb_t free_cb) {
free_cb(p);
}
while ((q = l->localq)) {
if (free_cb)
free_cb(q->data);
PA_LLIST_REMOVE(struct localq, l->localq, q);
if (pa_flist_push(PA_STATIC_FLIST_GET(localq), q) < 0)
pa_xfree(q);
}
pa_fdsem_free(l->read_fdsem);
pa_fdsem_free(l->write_fdsem);
pa_xfree(l);
}
int pa_asyncq_push(pa_asyncq*l, void *p, int wait) {
static int push(pa_asyncq*l, void *p, pa_bool_t wait) {
int idx;
pa_atomic_ptr_t *cells;
@ -141,7 +165,63 @@ int pa_asyncq_push(pa_asyncq*l, void *p, int wait) {
return 0;
}
void* pa_asyncq_pop(pa_asyncq*l, int wait) {
static pa_bool_t flush_postq(pa_asyncq *l) {
struct localq *q;
pa_assert(l);
while ((q = l->last_localq)) {
if (push(l, q->data, FALSE) < 0)
return FALSE;
l->last_localq = q->prev;
PA_LLIST_REMOVE(struct localq, l->localq, q);
if (pa_flist_push(PA_STATIC_FLIST_GET(localq), q) < 0)
pa_xfree(q);
}
return TRUE;
}
int pa_asyncq_push(pa_asyncq*l, void *p, pa_bool_t wait) {
pa_assert(l);
if (!flush_postq(l))
return -1;
return push(l, p, wait);
}
void pa_asyncq_post(pa_asyncq*l, void *p) {
struct localq *q;
pa_assert(l);
pa_assert(p);
if (pa_asyncq_push(l, p, FALSE) >= 0)
return;
/* OK, we couldn't push anything in the queue. So let's queue it
* locally and push it later */
pa_log("q overrun, queuing locally");
if (!(q = pa_flist_pop(PA_STATIC_FLIST_GET(localq))))
q = pa_xnew(struct localq, 1);
q->data = p;
PA_LLIST_PREPEND(struct localq, l->localq, q);
if (!l->last_localq)
l->last_localq = q;
return;
}
void* pa_asyncq_pop(pa_asyncq*l, pa_bool_t wait) {
int idx;
void *ret;
pa_atomic_ptr_t *cells;
@ -178,13 +258,13 @@ void* pa_asyncq_pop(pa_asyncq*l, int wait) {
return ret;
}
int pa_asyncq_get_fd(pa_asyncq *q) {
int pa_asyncq_read_fd(pa_asyncq *q) {
pa_assert(q);
return pa_fdsem_get(q->write_fdsem);
}
int pa_asyncq_before_poll(pa_asyncq *l) {
int pa_asyncq_read_before_poll(pa_asyncq *l) {
int idx;
pa_atomic_ptr_t *cells;
@ -206,8 +286,38 @@ int pa_asyncq_before_poll(pa_asyncq *l) {
return 0;
}
void pa_asyncq_after_poll(pa_asyncq *l) {
void pa_asyncq_read_after_poll(pa_asyncq *l) {
pa_assert(l);
pa_fdsem_after_poll(l->write_fdsem);
}
int pa_asyncq_write_fd(pa_asyncq *q) {
pa_assert(q);
return pa_fdsem_get(q->read_fdsem);
}
void pa_asyncq_write_before_poll(pa_asyncq *l) {
pa_assert(l);
for (;;) {
if (flush_postq(l))
break;
if (pa_fdsem_before_poll(l->read_fdsem) >= 0) {
l->waiting_for_post = TRUE;
break;
}
}
}
void pa_asyncq_write_after_poll(pa_asyncq *l) {
pa_assert(l);
if (l->waiting_for_post) {
pa_fdsem_after_poll(l->read_fdsem);
l->waiting_for_post = FALSE;
}
}

View file

@ -26,6 +26,7 @@
#include <sys/types.h>
#include <pulse/def.h>
#include <pulsecore/macro.h>
/* A simple, asynchronous, lock-free (if requested also wait-free)
* queue. Not multiple-reader/multiple-writer safe. If that is
@ -46,11 +47,21 @@ typedef struct pa_asyncq pa_asyncq;
pa_asyncq* pa_asyncq_new(unsigned size);
void pa_asyncq_free(pa_asyncq* q, pa_free_cb_t free_cb);
void* pa_asyncq_pop(pa_asyncq *q, int wait);
int pa_asyncq_push(pa_asyncq *q, void *p, int wait);
void* pa_asyncq_pop(pa_asyncq *q, pa_bool_t wait);
int pa_asyncq_push(pa_asyncq *q, void *p, pa_bool_t wait);
int pa_asyncq_get_fd(pa_asyncq *q);
int pa_asyncq_before_poll(pa_asyncq *a);
void pa_asyncq_after_poll(pa_asyncq *a);
/* Similar to pa_asyncq_push(), but if the queue is full, postpone it
* locally and delay until pa_asyncq_before_poll_post() */
void pa_asyncq_post(pa_asyncq*l, void *p);
/* For the reading side */
int pa_asyncq_read_fd(pa_asyncq *q);
int pa_asyncq_read_before_poll(pa_asyncq *a);
void pa_asyncq_read_after_poll(pa_asyncq *a);
/* For the writing side */
int pa_asyncq_write_fd(pa_asyncq *q);
void pa_asyncq_write_before_poll(pa_asyncq *a);
void pa_asyncq_write_after_poll(pa_asyncq *a);
#endif

View file

@ -90,6 +90,7 @@ static int pa_cli_command_stat(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_b
static int pa_cli_command_info(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
static int pa_cli_command_load(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
static int pa_cli_command_unload(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
static int pa_cli_command_describe(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
static int pa_cli_command_sink_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
static int pa_cli_command_sink_input_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
static int pa_cli_command_source_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
@ -136,6 +137,7 @@ static const struct command commands[] = {
{ "list", pa_cli_command_info, NULL, 1 },
{ "load-module", pa_cli_command_load, "Load a module (args: name, arguments)", 3},
{ "unload-module", pa_cli_command_unload, "Unload a module (args: index)", 2},
{ "describe-module", pa_cli_command_describe, "Describe a module (arg: name)", 2},
{ "set-sink-volume", pa_cli_command_sink_volume, "Set the volume of a sink (args: index|name, volume)", 3},
{ "set-sink-input-volume", pa_cli_command_sink_input_volume, "Set the volume of a sink input (args: index, volume)", 3},
{ "set-source-volume", pa_cli_command_source_volume, "Set the volume of a source (args: index|name, volume)", 3},
@ -155,10 +157,10 @@ static const struct command commands[] = {
{ "load-sample-dir-lazy", pa_cli_command_scache_load_dir, "Lazily load all files in a directory into the sample cache (args: pathname)", 2},
{ "play-file", pa_cli_command_play_file, "Play a sound file (args: filename, sink|index)", 3},
{ "list-autoload", pa_cli_command_autoload_list, "List autoload entries", 1},
{ "add-autoload-sink", pa_cli_command_autoload_add, "Add autoload entry for a sink (args: sink, module name, arguments)", 4},
{ "add-autoload-source", pa_cli_command_autoload_add, "Add autoload entry for a source (args: source, module name, arguments)", 4},
{ "remove-autoload-sink", pa_cli_command_autoload_remove, "Remove autoload entry for a sink (args: name)", 2},
{ "remove-autoload-source", pa_cli_command_autoload_remove, "Remove autoload entry for a source (args: name)", 2},
{ "add-autoload-sink", pa_cli_command_autoload_add, NULL /*"Add autoload entry for a sink (args: sink, module name, arguments)"*/, 4},
{ "add-autoload-source", pa_cli_command_autoload_add, NULL /*"Add autoload entry for a source (args: source, module name, arguments)"*/, 4},
{ "remove-autoload-sink", pa_cli_command_autoload_remove, NULL /*"Remove autoload entry for a sink (args: name)"*/, 2},
{ "remove-autoload-source", pa_cli_command_autoload_remove, NULL /*"Remove autoload entry for a source (args: name)"*/, 2},
{ "dump", pa_cli_command_dump, "Dump daemon configuration", 1},
{ "list-props", pa_cli_command_list_props, NULL, 1},
{ "move-sink-input", pa_cli_command_move_sink_input, "Move sink input to another sink (args: index, sink)", 3},
@ -367,7 +369,7 @@ static int pa_cli_command_info(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_b
pa_cli_command_sink_inputs(c, t, buf, fail);
pa_cli_command_source_outputs(c, t, buf, fail);
pa_cli_command_scache_list(c, t, buf, fail);
pa_cli_command_autoload_list(c, t, buf, fail);
/* pa_cli_command_autoload_list(c, t, buf, fail); */
return 0;
}
@ -419,6 +421,45 @@ static int pa_cli_command_unload(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa
return 0;
}
static int pa_cli_command_describe(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
const char *name;
pa_modinfo *i;
pa_core_assert_ref(c);
pa_assert(t);
pa_assert(buf);
pa_assert(fail);
if (!(name = pa_tokenizer_get(t, 1))) {
pa_strbuf_puts(buf, "You need to specify the module name.\n");
return -1;
}
if ((i = pa_modinfo_get_by_name(name))) {
pa_strbuf_printf(buf, "Name: %s\n", name);
if (!i->description && !i->version && !i->author && !i->usage)
pa_strbuf_printf(buf, "No module information available\n");
else {
if (i->version)
pa_strbuf_printf(buf, "Version: %s\n", i->version);
if (i->description)
pa_strbuf_printf(buf, "Description: %s\n", i->description);
if (i->author)
pa_strbuf_printf(buf, "Author: %s\n", i->author);
if (i->usage)
pa_strbuf_printf(buf, "Usage: %s\n", i->usage);
pa_strbuf_printf(buf, "Load Once: %s\n", pa_yes_no(i->load_once));
}
pa_modinfo_free(i);
} else
pa_strbuf_puts(buf, "Failed to open module.\n");
return 0;
}
static int pa_cli_command_sink_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
const char *n, *v;
pa_sink *sink;
@ -436,7 +477,7 @@ static int pa_cli_command_sink_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *bu
}
if (!(v = pa_tokenizer_get(t, 2))) {
pa_strbuf_puts(buf, "You need to specify a volume >= 0. (0 is muted, 0x100 is normal volume)\n");
pa_strbuf_puts(buf, "You need to specify a volume >= 0. (0 is muted, 0x10000 is normal volume)\n");
return -1;
}
@ -478,7 +519,7 @@ static int pa_cli_command_sink_input_volume(pa_core *c, pa_tokenizer *t, pa_strb
}
if (!(v = pa_tokenizer_get(t, 2))) {
pa_strbuf_puts(buf, "You need to specify a volume >= 0. (0 is muted, 0x100 is normal volume)\n");
pa_strbuf_puts(buf, "You need to specify a volume >= 0. (0 is muted, 0x10000 is normal volume)\n");
return -1;
}
@ -514,7 +555,7 @@ static int pa_cli_command_source_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *
}
if (!(v = pa_tokenizer_get(t, 2))) {
pa_strbuf_puts(buf, "You need to specify a volume >= 0. (0 is muted, 0x100 is normal volume)\n");
pa_strbuf_puts(buf, "You need to specify a volume >= 0. (0 is muted, 0x10000 is normal volume)\n");
return -1;
}
@ -553,7 +594,7 @@ static int pa_cli_command_sink_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf,
return -1;
}
if (pa_atoi(m, &mute) < 0) {
if ((mute = pa_parse_boolean(m)) < 0) {
pa_strbuf_puts(buf, "Failed to parse mute switch.\n");
return -1;
}
@ -587,7 +628,7 @@ static int pa_cli_command_source_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *bu
return -1;
}
if (pa_atoi(m, &mute) < 0) {
if ((mute = pa_parse_boolean(m)) < 0) {
pa_strbuf_puts(buf, "Failed to parse mute switch.\n");
return -1;
}
@ -623,11 +664,11 @@ static int pa_cli_command_sink_input_mute(pa_core *c, pa_tokenizer *t, pa_strbuf
}
if (!(v = pa_tokenizer_get(t, 2))) {
pa_strbuf_puts(buf, "You need to specify a volume >= 0. (0 is muted, 0x100 is normal volume)\n");
pa_strbuf_puts(buf, "You need to specify a mute switch setting (0/1).\n");
return -1;
}
if (pa_atoi(v, &mute) < 0) {
if ((mute = pa_parse_boolean(v)) < 0) {
pa_strbuf_puts(buf, "Failed to parse mute switch.\n");
return -1;
}
@ -780,6 +821,7 @@ static int pa_cli_command_scache_list(pa_core *c, pa_tokenizer *t, pa_strbuf *bu
static int pa_cli_command_scache_play(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
const char *n, *sink_name;
pa_sink *sink;
uint32_t idx;
pa_core_assert_ref(c);
pa_assert(t);
@ -796,11 +838,13 @@ static int pa_cli_command_scache_play(pa_core *c, pa_tokenizer *t, pa_strbuf *bu
return -1;
}
if (pa_scache_play_item(c, n, sink, PA_VOLUME_NORM) < 0) {
if (pa_scache_play_item(c, n, sink, PA_VOLUME_NORM, NULL, &idx) < 0) {
pa_strbuf_puts(buf, "Failed to play sample.\n");
return -1;
}
pa_strbuf_printf(buf, "Playing on sink input #%i\n", idx);
return 0;
}
@ -902,6 +946,8 @@ static int pa_cli_command_autoload_add(pa_core *c, pa_tokenizer *t, pa_strbuf *b
pa_assert(buf);
pa_assert(fail);
pa_log_warn("Autoload will no longer be implemented by future versions of the PulseAudio server.");
if (!(a = pa_tokenizer_get(t, 1)) || !(b = pa_tokenizer_get(t, 2))) {
pa_strbuf_puts(buf, "You need to specify a device name, a filename or a module name and optionally module arguments\n");
return -1;
@ -920,6 +966,8 @@ static int pa_cli_command_autoload_remove(pa_core *c, pa_tokenizer *t, pa_strbuf
pa_assert(buf);
pa_assert(fail);
pa_log_warn("Autoload will no longer be implemented by future versions of the PulseAudio server.");
if (!(name = pa_tokenizer_get(t, 1))) {
pa_strbuf_puts(buf, "You need to specify a device name\n");
return -1;
@ -941,6 +989,8 @@ static int pa_cli_command_autoload_list(pa_core *c, pa_tokenizer *t, pa_strbuf *
pa_assert(buf);
pa_assert(fail);
pa_log_warn("Autoload will no longer be implemented by future versions of the PulseAudio server.");
pa_assert_se(s = pa_autoload_list_to_string(c));
pa_strbuf_puts(buf, s);
pa_xfree(s);
@ -1005,7 +1055,7 @@ static int pa_cli_command_move_sink_input(pa_core *c, pa_tokenizer *t, pa_strbuf
return -1;
}
if (pa_sink_input_move_to(si, sink, 0) < 0) {
if (pa_sink_input_move_to(si, sink) < 0) {
pa_strbuf_puts(buf, "Moved failed.\n");
return -1;
}
@ -1075,7 +1125,7 @@ static int pa_cli_command_suspend_sink(pa_core *c, pa_tokenizer *t, pa_strbuf *b
return -1;
}
if (pa_atoi(m, &suspend) < 0) {
if ((suspend = pa_parse_boolean(m)) < 0) {
pa_strbuf_puts(buf, "Failed to parse suspend switch.\n");
return -1;
}
@ -1109,7 +1159,7 @@ static int pa_cli_command_suspend_source(pa_core *c, pa_tokenizer *t, pa_strbuf
return -1;
}
if (pa_atoi(m, &suspend) < 0) {
if ((suspend = pa_parse_boolean(m)) < 0) {
pa_strbuf_puts(buf, "Failed to parse suspend switch.\n");
return -1;
}
@ -1138,7 +1188,7 @@ static int pa_cli_command_suspend(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, p
return -1;
}
if (pa_atoi(m, &suspend) < 0) {
if ((suspend = pa_parse_boolean(m)) < 0) {
pa_strbuf_puts(buf, "Failed to parse suspend switch.\n");
return -1;
}
@ -1202,7 +1252,8 @@ static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_b
}
pa_strbuf_printf(buf, "set-sink-volume %s 0x%03x\n", sink->name, pa_cvolume_avg(pa_sink_get_volume(sink)));
pa_strbuf_printf(buf, "set-sink-mute %s %d\n", sink->name, pa_sink_get_mute(sink));
pa_strbuf_printf(buf, "set-sink-mute %s %s\n", sink->name, pa_yes_no(pa_sink_get_mute(sink)));
pa_strbuf_printf(buf, "suspend-sink %s %s\n", sink->name, pa_yes_no(pa_sink_get_state(sink) == PA_SINK_SUSPENDED));
}
for (source = pa_idxset_first(c->sources, &idx); source; source = pa_idxset_next(c->sources, &idx)) {
@ -1215,7 +1266,8 @@ static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_b
}
pa_strbuf_printf(buf, "set-source-volume %s 0x%03x\n", source->name, pa_cvolume_avg(pa_source_get_volume(source)));
pa_strbuf_printf(buf, "set-source-mute %s %d\n", source->name, pa_source_get_mute(source));
pa_strbuf_printf(buf, "set-source-mute %s %s\n", source->name, pa_yes_no(pa_source_get_mute(source)));
pa_strbuf_printf(buf, "suspend-source %s %s\n", source->name, pa_yes_no(pa_source_get_state(source) == PA_SOURCE_SUSPENDED));
}
@ -1390,16 +1442,45 @@ int pa_cli_command_execute_line(pa_core *c, const char *s, pa_strbuf *buf, pa_bo
return pa_cli_command_execute_line_stateful(c, s, buf, fail, NULL);
}
int pa_cli_command_execute_file(pa_core *c, const char *fn, pa_strbuf *buf, pa_bool_t *fail) {
int pa_cli_command_execute_file_stream(pa_core *c, FILE *f, pa_strbuf *buf, pa_bool_t *fail) {
char line[1024];
FILE *f = NULL;
int ifstate = IFSTATE_NONE;
int ret = -1;
pa_bool_t _fail = TRUE;
pa_assert(c);
pa_assert(f);
pa_assert(buf);
if (!fail)
fail = &_fail;
while (fgets(line, sizeof(line), f)) {
pa_strip_nl(line);
if (pa_cli_command_execute_line_stateful(c, line, buf, fail, &ifstate) < 0 && *fail)
goto fail;
}
ret = 0;
fail:
return ret;
}
int pa_cli_command_execute_file(pa_core *c, const char *fn, pa_strbuf *buf, pa_bool_t *fail) {
FILE *f = NULL;
int ret = -1;
pa_bool_t _fail = TRUE;
pa_assert(c);
pa_assert(fn);
pa_assert(buf);
if (!fail)
fail = &_fail;
if (!(f = fopen(fn, "r"))) {
pa_strbuf_printf(buf, "open('%s') failed: %s\n", fn, pa_cstrerror(errno));
if (!*fail)
@ -1407,13 +1488,7 @@ int pa_cli_command_execute_file(pa_core *c, const char *fn, pa_strbuf *buf, pa_b
goto fail;
}
while (fgets(line, sizeof(line), f)) {
char *e = line + strcspn(line, linebreak);
*e = 0;
if (pa_cli_command_execute_line_stateful(c, line, buf, fail, &ifstate) < 0 && *fail)
goto fail;
}
ret = pa_cli_command_execute_file_stream(c, f, buf, fail);
ret = 0;
@ -1427,11 +1502,15 @@ fail:
int pa_cli_command_execute(pa_core *c, const char *s, pa_strbuf *buf, pa_bool_t *fail) {
const char *p;
int ifstate = IFSTATE_NONE;
pa_bool_t _fail = TRUE;
pa_assert(c);
pa_assert(s);
pa_assert(buf);
if (!fail)
fail = &_fail;
p = s;
while (*p) {
size_t l = strcspn(p, linebreak);

View file

@ -36,6 +36,9 @@ int pa_cli_command_execute_line(pa_core *c, const char *s, pa_strbuf *buf, pa_bo
/* Execute a whole file of CLI commands */
int pa_cli_command_execute_file(pa_core *c, const char *fn, pa_strbuf *buf, pa_bool_t *fail);
/* Execute a whole file of CLI commands */
int pa_cli_command_execute_file_stream(pa_core *c, FILE *f, pa_strbuf *buf, pa_bool_t *fail);
/* Split the specified string into lines and run pa_cli_command_execute_line() for each. */
int pa_cli_command_execute(pa_core *c, const char *s, pa_strbuf *buf, pa_bool_t *fail);

View file

@ -29,6 +29,7 @@
#include <pulse/volume.h>
#include <pulse/xmalloc.h>
#include <pulse/timeval.h>
#include <pulsecore/module.h>
#include <pulsecore/client.h>
@ -41,6 +42,7 @@
#include <pulsecore/core-scache.h>
#include <pulsecore/autoload.h>
#include <pulsecore/macro.h>
#include <pulsecore/core-util.h>
#include "cli-text.h"
@ -56,12 +58,12 @@ char *pa_module_list_to_string(pa_core *c) {
for (m = pa_idxset_first(c->modules, &idx); m; m = pa_idxset_next(c->modules, &idx)) {
pa_strbuf_printf(s, " index: %u\n"
"\tname: <%s>\n"
"\targument: <%s>\n"
"\tused: %i\n"
"\tauto unload: %s\n",
m->index, m->name, m->argument ? m->argument : "", m->n_used,
m->auto_unload ? "yes" : "no");
"\tname: <%s>\n"
"\targument: <%s>\n"
"\tused: %i\n"
"\tauto unload: %s\n",
m->index, m->name, m->argument ? m->argument : "", m->n_used,
pa_yes_no(m->auto_unload));
}
return pa_strbuf_tostring_free(s);
@ -78,10 +80,20 @@ char *pa_client_list_to_string(pa_core *c) {
pa_strbuf_printf(s, "%u client(s) logged in.\n", pa_idxset_size(c->clients));
for (client = pa_idxset_first(c->clients, &idx); client; client = pa_idxset_next(c->clients, &idx)) {
pa_strbuf_printf(s, " index: %u\n\tname: <%s>\n\tdriver: <%s>\n", client->index, client->name, client->driver);
char *t;
pa_strbuf_printf(
s,
" index: %u\n"
"\tdriver: <%s>\n",
client->index,
client->driver);
if (client->owner)
pa_strbuf_printf(s, "\towner module: <%u>\n", client->owner->index);
if (client->module)
pa_strbuf_printf(s, "\towner module: %u\n", client->module->index);
t = pa_proplist_to_string(client->proplist);
pa_strbuf_printf(s, "\tproperties:\n%s", t);
pa_xfree(t);
}
return pa_strbuf_tostring_free(s);
@ -92,6 +104,7 @@ char *pa_sink_list_to_string(pa_core *c) {
pa_sink *sink;
uint32_t idx = PA_IDXSET_INVALID;
static const char* const state_table[] = {
[PA_SINK_INIT] = "INIT",
[PA_SINK_RUNNING] = "RUNNING",
[PA_SINK_SUSPENDED] = "SUSPENDED",
[PA_SINK_IDLE] = "IDLE",
@ -104,35 +117,39 @@ char *pa_sink_list_to_string(pa_core *c) {
pa_strbuf_printf(s, "%u sink(s) available.\n", pa_idxset_size(c->sinks));
for (sink = pa_idxset_first(c->sinks, &idx); sink; sink = pa_idxset_next(c->sinks, &idx)) {
char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX], *t;
pa_strbuf_printf(
s,
" %c index: %u\n"
"\tname: <%s>\n"
"\tdriver: <%s>\n"
"\tflags: %s%s%s%s\n"
"\tflags: %s%s%s%s%s%s\n"
"\tstate: %s\n"
"\tvolume: <%s>\n"
"\tmute: <%i>\n"
"\tlatency: <%0.0f usec>\n"
"\tmonitor source: <%u>\n"
"\tsample spec: <%s>\n"
"\tchannel map: <%s>\n"
"\tused by: <%u>\n"
"\tlinked by: <%u>\n",
"\tvolume: %s\n"
"\tmuted: %s\n"
"\tcurrent latency: %0.2f ms\n"
"\tconfigured latency: %0.2f ms; range is %0.2f .. %0.2f ms\n"
"\tmonitor source: %u\n"
"\tsample spec: %s\n"
"\tchannel map: %s\n"
"\tused by: %u\n"
"\tlinked by: %u\n",
c->default_sink_name && !strcmp(sink->name, c->default_sink_name) ? '*' : ' ',
sink->index,
sink->name,
sink->driver,
sink->flags & PA_SINK_HW_VOLUME_CTRL ? "HW_VOLUME_CTRL " : "",
sink->flags & PA_SINK_LATENCY ? "LATENCY " : "",
sink->flags & PA_SINK_HARDWARE ? "HARDWARE " : "",
sink->flags & PA_SINK_NETWORK ? "NETWORK " : "",
sink->flags & PA_SINK_HW_MUTE_CTRL ? "HW_MUTE_CTRL " : "",
sink->flags & PA_SINK_HW_VOLUME_CTRL ? "HW_VOLUME_CTRL " : "",
sink->flags & PA_SINK_DECIBEL_VOLUME ? "DECIBEL_VOLUME " : "",
sink->flags & PA_SINK_LATENCY ? "LATENCY " : "",
state_table[pa_sink_get_state(sink)],
pa_cvolume_snprint(cv, sizeof(cv), pa_sink_get_volume(sink)),
!!pa_sink_get_mute(sink),
(double) pa_sink_get_latency(sink),
pa_yes_no(pa_sink_get_mute(sink)),
(double) pa_sink_get_latency(sink) / PA_USEC_PER_MSEC,
(double) pa_sink_get_requested_latency(sink) / PA_USEC_PER_MSEC, (double) sink->min_latency / PA_USEC_PER_MSEC, (double) sink->max_latency / PA_USEC_PER_MSEC,
sink->monitor_source ? sink->monitor_source->index : PA_INVALID_INDEX,
pa_sample_spec_snprint(ss, sizeof(ss), &sink->sample_spec),
pa_channel_map_snprint(cm, sizeof(cm), &sink->channel_map),
@ -140,9 +157,11 @@ char *pa_sink_list_to_string(pa_core *c) {
pa_sink_linked_by(sink));
if (sink->module)
pa_strbuf_printf(s, "\tmodule: <%u>\n", sink->module->index);
if (sink->description)
pa_strbuf_printf(s, "\tdescription: <%s>\n", sink->description);
pa_strbuf_printf(s, "\tmodule: %u\n", sink->module->index);
t = pa_proplist_to_string(sink->proplist);
pa_strbuf_printf(s, "\tproperties:\n%s", t);
pa_xfree(t);
}
return pa_strbuf_tostring_free(s);
@ -153,6 +172,7 @@ char *pa_source_list_to_string(pa_core *c) {
pa_source *source;
uint32_t idx = PA_IDXSET_INVALID;
static const char* const state_table[] = {
[PA_SOURCE_INIT] = "INIT",
[PA_SOURCE_RUNNING] = "RUNNING",
[PA_SOURCE_SUSPENDED] = "SUSPENDED",
[PA_SOURCE_IDLE] = "IDLE",
@ -165,46 +185,51 @@ char *pa_source_list_to_string(pa_core *c) {
pa_strbuf_printf(s, "%u source(s) available.\n", pa_idxset_size(c->sources));
for (source = pa_idxset_first(c->sources, &idx); source; source = pa_idxset_next(c->sources, &idx)) {
char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX];
char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], *t;
pa_strbuf_printf(
s,
" %c index: %u\n"
"\tname: <%s>\n"
"\tdriver: <%s>\n"
"\tflags: %s%s%s%s\n"
"\tflags: %s%s%s%s%s%s\n"
"\tstate: %s\n"
"\tvolume: <%s>\n"
"\tmute: <%u>\n"
"\tlatency: <%0.0f usec>\n"
"\tsample spec: <%s>\n"
"\tchannel map: <%s>\n"
"\tused by: <%u>\n"
"\tlinked by: <%u>\n",
"\tvolume: %s\n"
"\tmuted: %s\n"
"\tcurrent latency: %0.2f ms\n"
"\tconfigured latency: %0.2f ms; range is %0.2f .. %0.2f ms\n"
"\tsample spec: %s\n"
"\tchannel map: %s\n"
"\tused by: %u\n"
"\tlinked by: %u\n",
c->default_source_name && !strcmp(source->name, c->default_source_name) ? '*' : ' ',
source->index,
source->name,
source->driver,
source->flags & PA_SOURCE_HW_VOLUME_CTRL ? "HW_VOLUME_CTRL " : "",
source->flags & PA_SOURCE_LATENCY ? "LATENCY " : "",
source->flags & PA_SOURCE_HARDWARE ? "HARDWARE " : "",
source->flags & PA_SOURCE_NETWORK ? "NETWORK " : "",
source->flags & PA_SOURCE_HW_MUTE_CTRL ? "HW_MUTE_CTRL " : "",
source->flags & PA_SOURCE_HW_VOLUME_CTRL ? "HW_VOLUME_CTRL " : "",
source->flags & PA_SOURCE_DECIBEL_VOLUME ? "DECIBEL_VOLUME " : "",
source->flags & PA_SOURCE_LATENCY ? "LATENCY " : "",
state_table[pa_source_get_state(source)],
pa_cvolume_snprint(cv, sizeof(cv), pa_source_get_volume(source)),
!!pa_source_get_mute(source),
(double) pa_source_get_latency(source),
pa_yes_no(pa_source_get_mute(source)),
(double) pa_source_get_latency(source) / PA_USEC_PER_MSEC,
(double) pa_source_get_requested_latency(source) / PA_USEC_PER_MSEC, (double) source->min_latency / PA_USEC_PER_MSEC, (double) source->max_latency / PA_USEC_PER_MSEC,
pa_sample_spec_snprint(ss, sizeof(ss), &source->sample_spec),
pa_channel_map_snprint(cm, sizeof(cm), &source->channel_map),
pa_source_used_by(source),
pa_source_linked_by(source));
if (source->monitor_of)
pa_strbuf_printf(s, "\tmonitor_of: <%u>\n", source->monitor_of->index);
pa_strbuf_printf(s, "\tmonitor_of: %u\n", source->monitor_of->index);
if (source->module)
pa_strbuf_printf(s, "\tmodule: <%u>\n", source->module->index);
if (source->description)
pa_strbuf_printf(s, "\tdescription: <%s>\n", source->description);
pa_strbuf_printf(s, "\tmodule: %u\n", source->module->index);
t = pa_proplist_to_string(source->proplist);
pa_strbuf_printf(s, "\tproperties:\n%s", t);
pa_xfree(t);
}
return pa_strbuf_tostring_free(s);
@ -216,6 +241,7 @@ char *pa_source_output_list_to_string(pa_core *c) {
pa_source_output *o;
uint32_t idx = PA_IDXSET_INVALID;
static const char* const state_table[] = {
[PA_SOURCE_OUTPUT_INIT] = "INIT",
[PA_SOURCE_OUTPUT_RUNNING] = "RUNNING",
[PA_SOURCE_OUTPUT_CORKED] = "CORKED",
[PA_SOURCE_OUTPUT_UNLINKED] = "UNLINKED"
@ -227,27 +253,33 @@ char *pa_source_output_list_to_string(pa_core *c) {
pa_strbuf_printf(s, "%u source outputs(s) available.\n", pa_idxset_size(c->source_outputs));
for (o = pa_idxset_first(c->source_outputs, &idx); o; o = pa_idxset_next(c->source_outputs, &idx)) {
char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX], *t, clt[28];
pa_usec_t cl;
if ((cl = pa_source_output_get_requested_latency(o)) == (pa_usec_t) -1)
pa_snprintf(clt, sizeof(clt), "n/a");
else
pa_snprintf(clt, sizeof(clt), "%0.2f ms", (double) cl / PA_USEC_PER_MSEC);
pa_assert(o->source);
pa_strbuf_printf(
s,
" index: %u\n"
"\tname: '%s'\n"
"\tdriver: <%s>\n"
"\tflags: %s%s%s%s%s%s%s\n"
"\tflags: %s%s%s%s%s%s%s%s\n"
"\tstate: %s\n"
"\tsource: <%u> '%s'\n"
"\tlatency: <%0.0f usec>\n"
"\tsample spec: <%s>\n"
"\tchannel map: <%s>\n"
"\tsource: %u <%s>\n"
"\tcurrent latency: %0.2f ms\n"
"\trequested latency: %s\n"
"\tsample spec: %s\n"
"\tchannel map: %s\n"
"\tresample method: %s\n",
o->index,
o->name,
o->driver,
o->flags & PA_SOURCE_OUTPUT_VARIABLE_RATE ? "VARIABLE_RATE " : "",
o->flags & PA_SOURCE_OUTPUT_DONT_MOVE ? "DONT_MOVE " : "",
o->flags & PA_SOURCE_OUTPUT_START_CORKED ? "START_CORKED " : "",
o->flags & PA_SOURCE_OUTPUT_NO_REMAP ? "NO_REMAP " : "",
o->flags & PA_SOURCE_OUTPUT_NO_REMIX ? "NO_REMIX " : "",
o->flags & PA_SOURCE_OUTPUT_FIX_FORMAT ? "FIX_FORMAT " : "",
@ -255,14 +287,19 @@ char *pa_source_output_list_to_string(pa_core *c) {
o->flags & PA_SOURCE_OUTPUT_FIX_CHANNELS ? "FIX_CHANNELS " : "",
state_table[pa_source_output_get_state(o)],
o->source->index, o->source->name,
(double) pa_source_output_get_latency(o),
(double) pa_source_output_get_latency(o) / PA_USEC_PER_MSEC,
clt,
pa_sample_spec_snprint(ss, sizeof(ss), &o->sample_spec),
pa_channel_map_snprint(cm, sizeof(cm), &o->channel_map),
pa_resample_method_to_string(pa_source_output_get_resample_method(o)));
if (o->module)
pa_strbuf_printf(s, "\towner module: <%u>\n", o->module->index);
pa_strbuf_printf(s, "\towner module: %u\n", o->module->index);
if (o->client)
pa_strbuf_printf(s, "\tclient: <%u> '%s'\n", o->client->index, o->client->name);
pa_strbuf_printf(s, "\tclient: %u <%s>\n", o->client->index, pa_strnull(pa_proplist_gets(o->client->proplist, PA_PROP_APPLICATION_NAME)));
t = pa_proplist_to_string(o->proplist);
pa_strbuf_printf(s, "\tproperties:\n%s", t);
pa_xfree(t);
}
return pa_strbuf_tostring_free(s);
@ -273,6 +310,7 @@ char *pa_sink_input_list_to_string(pa_core *c) {
pa_sink_input *i;
uint32_t idx = PA_IDXSET_INVALID;
static const char* const state_table[] = {
[PA_SINK_INPUT_INIT] = "INIT",
[PA_SINK_INPUT_RUNNING] = "RUNNING",
[PA_SINK_INPUT_DRAINED] = "DRAINED",
[PA_SINK_INPUT_CORKED] = "CORKED",
@ -285,29 +323,35 @@ char *pa_sink_input_list_to_string(pa_core *c) {
pa_strbuf_printf(s, "%u sink input(s) available.\n", pa_idxset_size(c->sink_inputs));
for (i = pa_idxset_first(c->sink_inputs, &idx); i; i = pa_idxset_next(c->sink_inputs, &idx)) {
char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX], *t, clt[28];
pa_usec_t cl;
if ((cl = pa_sink_input_get_requested_latency(i)) == (pa_usec_t) -1)
pa_snprintf(clt, sizeof(clt), "n/a");
else
pa_snprintf(clt, sizeof(clt), "%0.2f ms", (double) cl / PA_USEC_PER_MSEC);
pa_assert(i->sink);
pa_strbuf_printf(
s,
" index: %u\n"
"\tname: <%s>\n"
"\tdriver: <%s>\n"
"\tflags: %s%s%s%s%s%s%s\n"
"\tflags: %s%s%s%s%s%s%s%s\n"
"\tstate: %s\n"
"\tsink: <%u> '%s'\n"
"\tvolume: <%s>\n"
"\tmute: <%i>\n"
"\tlatency: <%0.0f usec>\n"
"\tsample spec: <%s>\n"
"\tchannel map: <%s>\n"
"\tsink: %u <%s>\n"
"\tvolume: %s\n"
"\tmuted: %s\n"
"\tcurrent latency: %0.2f ms\n"
"\trequested latency: %s\n"
"\tsample spec: %s\n"
"\tchannel map: %s\n"
"\tresample method: %s\n",
i->index,
i->name,
i->driver,
i->flags & PA_SINK_INPUT_VARIABLE_RATE ? "VARIABLE_RATE " : "",
i->flags & PA_SINK_INPUT_DONT_MOVE ? "DONT_MOVE " : "",
i->flags & PA_SINK_INPUT_START_CORKED ? "START_CORKED " : "",
i->flags & PA_SINK_INPUT_NO_REMAP ? "NO_REMAP " : "",
i->flags & PA_SINK_INPUT_NO_REMIX ? "NO_REMIX " : "",
i->flags & PA_SINK_INPUT_FIX_FORMAT ? "FIX_FORMAT " : "",
@ -316,16 +360,21 @@ char *pa_sink_input_list_to_string(pa_core *c) {
state_table[pa_sink_input_get_state(i)],
i->sink->index, i->sink->name,
pa_cvolume_snprint(cv, sizeof(cv), pa_sink_input_get_volume(i)),
!!pa_sink_input_get_mute(i),
(double) pa_sink_input_get_latency(i),
pa_yes_no(pa_sink_input_get_mute(i)),
(double) pa_sink_input_get_latency(i) / PA_USEC_PER_MSEC,
clt,
pa_sample_spec_snprint(ss, sizeof(ss), &i->sample_spec),
pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map),
pa_resample_method_to_string(pa_sink_input_get_resample_method(i)));
if (i->module)
pa_strbuf_printf(s, "\tmodule: <%u>\n", i->module->index);
pa_strbuf_printf(s, "\tmodule: %u\n", i->module->index);
if (i->client)
pa_strbuf_printf(s, "\tclient: <%u> '%s'\n", i->client->index, i->client->name);
pa_strbuf_printf(s, "\tclient: %u <%s>\n", i->client->index, pa_strnull(pa_proplist_gets(i->client->proplist, PA_PROP_APPLICATION_NAME)));
t = pa_proplist_to_string(i->proplist);
pa_strbuf_printf(s, "\tproperties:\n%s", t);
pa_xfree(t);
}
return pa_strbuf_tostring_free(s);
@ -345,7 +394,7 @@ char *pa_scache_list_to_string(pa_core *c) {
for (e = pa_idxset_first(c->scache, &idx); e; e = pa_idxset_next(c->scache, &idx)) {
double l = 0;
char ss[PA_SAMPLE_SPEC_SNPRINT_MAX] = "n/a", cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX] = "n/a";
char ss[PA_SAMPLE_SPEC_SNPRINT_MAX] = "n/a", cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX] = "n/a", *t;
if (e->memchunk.memblock) {
pa_sample_spec_snprint(ss, sizeof(ss), &e->sample_spec);
@ -356,14 +405,14 @@ char *pa_scache_list_to_string(pa_core *c) {
pa_strbuf_printf(
s,
" name: <%s>\n"
"\tindex: <%u>\n"
"\tsample spec: <%s>\n"
"\tchannel map: <%s>\n"
"\tlength: <%lu>\n"
"\tduration: <%0.1fs>\n"
"\tvolume: <%s>\n"
"\tindex: %u\n"
"\tsample spec: %s\n"
"\tchannel map: %s\n"
"\tlength: %lu\n"
"\tduration: %0.1f s\n"
"\tvolume: %s\n"
"\tlazy: %s\n"
"\tfilename: %s\n",
"\tfilename: <%s>\n",
e->name,
e->index,
ss,
@ -371,8 +420,12 @@ char *pa_scache_list_to_string(pa_core *c) {
(long unsigned)(e->memchunk.memblock ? e->memchunk.length : 0),
l,
pa_cvolume_snprint(cv, sizeof(cv), &e->volume),
e->lazy ? "yes" : "no",
pa_yes_no(e->lazy),
e->filename ? e->filename : "n/a");
t = pa_proplist_to_string(e->proplist);
pa_strbuf_printf(s, "\tproperties:\n%s", t);
pa_xfree(t);
}
}
@ -393,7 +446,12 @@ char *pa_autoload_list_to_string(pa_core *c) {
while ((e = pa_hashmap_iterate(c->autoload_hashmap, &state, NULL))) {
pa_strbuf_printf(
s, " name: <%s>\n\ttype: <%s>\n\tindex: <%u>\n\tmodule_name: <%s>\n\targuments: <%s>\n",
s,
" name: <%s>\n"
"\ttype: %s\n"
"\tindex: %u\n"
"\tmodule_name: <%s>\n"
"\targuments: <%s>\n",
e->name,
e->type == PA_NAMEREG_SOURCE ? "source" : "sink",
e->index,

View file

@ -82,7 +82,7 @@ pa_cli* pa_cli_new(pa_core *core, pa_iochannel *io, pa_module *m) {
pa_assert_se(c->client = pa_client_new(core, __FILE__, cname));
c->client->kill = client_kill;
c->client->userdata = c;
c->client->owner = m;
c->client->module = m;
pa_ioline_set_callback(c->line, line_callback, c);
pa_ioline_puts(c->line, "Welcome to PulseAudio! Use \"help\" for usage information.\n"PROMPT);

View file

@ -35,6 +35,7 @@
#include <pulsecore/core-subscribe.h>
#include <pulsecore/log.h>
#include <pulsecore/macro.h>
#include <pulsecore/core-util.h>
#include "client.h"
@ -44,17 +45,19 @@ pa_client *pa_client_new(pa_core *core, const char *driver, const char *name) {
pa_core_assert_ref(core);
c = pa_xnew(pa_client, 1);
c->name = pa_xstrdup(name);
c->driver = pa_xstrdup(driver);
c->owner = NULL;
c->core = core;
c->proplist = pa_proplist_new();
if (name)
pa_proplist_sets(c->proplist, PA_PROP_APPLICATION_NAME, name);
c->driver = pa_xstrdup(driver);
c->module = NULL;
c->kill = NULL;
c->userdata = NULL;
pa_assert_se(pa_idxset_put(core->clients, c, &c->index) >= 0);
pa_log_info("Created %u \"%s\"", c->index, c->name);
pa_log_info("Created %u \"%s\"", c->index, pa_strnull(name));
pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_NEW, c->index);
pa_core_check_quit(core);
@ -70,9 +73,9 @@ void pa_client_free(pa_client *c) {
pa_core_check_quit(c->core);
pa_log_info("Freed %u \"%s\"", c->index, c->name);
pa_log_info("Freed %u \"%s\"", c->index, pa_strnull(pa_proplist_gets(c->proplist, PA_PROP_APPLICATION_NAME)));
pa_subscription_post(c->core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_REMOVE, c->index);
pa_xfree(c->name);
pa_proplist_free(c->proplist);
pa_xfree(c->driver);
pa_xfree(c);
}
@ -91,10 +94,7 @@ void pa_client_kill(pa_client *c) {
void pa_client_set_name(pa_client *c, const char *name) {
pa_assert(c);
pa_log_info("Client %u changed name from \"%s\" to \"%s\"", c->index, c->name, name);
pa_xfree(c->name);
c->name = pa_xstrdup(name);
pa_log_info("Client %u changed name from \"%s\" to \"%s\"", c->index, pa_strnull(pa_proplist_gets(c->proplist, PA_PROP_APPLICATION_NAME)), name);
pa_proplist_sets(c->proplist, PA_PROP_APPLICATION_NAME, name);
pa_subscription_post(c->core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_CHANGE, c->index);
}

View file

@ -28,6 +28,7 @@
typedef struct pa_client pa_client;
#include <pulse/proplist.h>
#include <pulsecore/core.h>
#include <pulsecore/module.h>
@ -37,11 +38,12 @@ typedef struct pa_client pa_client;
struct pa_client {
uint32_t index;
pa_module *owner;
char *name, *driver;
pa_core *core;
pa_proplist *proplist;
pa_module *module;
char *driver;
void (*kill)(pa_client *c);
void *userdata;
};

View file

@ -1,29 +0,0 @@
#ifndef foocoredefhfoo
#define foocoredefhfoo
/* $Id$ */
/***
This file is part of PulseAudio.
Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
/* FIXME: Remove this shit */
#endif

View file

@ -3,7 +3,7 @@
/***
This file is part of PulseAudio.
Copyright 2004-2006 Lennart Poettering
Copyright 2004-2008 Lennart Poettering
Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
PulseAudio is free software; you can redistribute it and/or modify
@ -63,7 +63,7 @@
#include "core-scache.h"
#define UNLOAD_POLL_TIME 2
#define UNLOAD_POLL_TIME 5
static void timeout_callback(pa_mainloop_api *m, pa_time_event*e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) {
pa_core *c = userdata;
@ -89,6 +89,8 @@ static void free_entry(pa_scache_entry *e) {
pa_xfree(e->filename);
if (e->memchunk.memblock)
pa_memblock_unref(e->memchunk.memblock);
if (e->proplist)
pa_proplist_free(e->proplist);
pa_xfree(e);
}
@ -103,6 +105,7 @@ static pa_scache_entry* scache_add_item(pa_core *c, const char *name) {
pa_memblock_unref(e->memchunk.memblock);
pa_xfree(e->filename);
pa_proplist_clear(e->proplist);
pa_assert(e->core == c);
@ -117,11 +120,10 @@ static pa_scache_entry* scache_add_item(pa_core *c, const char *name) {
e->name = pa_xstrdup(name);
e->core = c;
e->proplist = pa_proplist_new();
if (!c->scache) {
if (!c->scache)
c->scache = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
pa_assert(c->scache);
}
pa_idxset_put(c->scache, e, &e->index);
@ -132,17 +134,27 @@ static pa_scache_entry* scache_add_item(pa_core *c, const char *name) {
e->memchunk.memblock = NULL;
e->memchunk.index = e->memchunk.length = 0;
e->filename = NULL;
e->lazy = 0;
e->lazy = FALSE;
e->last_used_time = 0;
memset(&e->sample_spec, 0, sizeof(e->sample_spec));
pa_channel_map_init(&e->channel_map);
pa_cvolume_reset(&e->volume, PA_CHANNELS_MAX);
pa_proplist_sets(e->proplist, PA_PROP_MEDIA_ROLE, "event");
return e;
}
int pa_scache_add_item(pa_core *c, const char *name, const pa_sample_spec *ss, const pa_channel_map *map, const pa_memchunk *chunk, uint32_t *idx) {
int pa_scache_add_item(
pa_core *c,
const char *name,
const pa_sample_spec *ss,
const pa_channel_map *map,
const pa_memchunk *chunk,
pa_proplist *p,
uint32_t *idx) {
pa_scache_entry *e;
char st[PA_SAMPLE_SPEC_SNPRINT_MAX];
pa_channel_map tmap;
@ -178,6 +190,9 @@ int pa_scache_add_item(pa_core *c, const char *name, const pa_sample_spec *ss, c
pa_memblock_ref(e->memchunk.memblock);
}
if (p)
pa_proplist_update(e->proplist, PA_UPDATE_REPLACE, p);
if (idx)
*idx = e->index;
@ -193,6 +208,7 @@ int pa_scache_add_file(pa_core *c, const char *name, const char *filename, uint3
pa_channel_map map;
pa_memchunk chunk;
int r;
pa_proplist *p;
#ifdef OS_IS_WIN32
char buf[MAX_PATH];
@ -208,8 +224,11 @@ int pa_scache_add_file(pa_core *c, const char *name, const char *filename, uint3
if (pa_sound_file_load(c->mempool, filename, &ss, &map, &chunk) < 0)
return -1;
r = pa_scache_add_item(c, name, &ss, &map, &chunk, idx);
p = pa_proplist_new();
pa_proplist_sets(p, PA_PROP_MEDIA_FILENAME, filename);
r = pa_scache_add_item(c, name, &ss, &map, &chunk, p, idx);
pa_memblock_unref(chunk.memblock);
pa_proplist_free(p);
return r;
}
@ -231,9 +250,11 @@ int pa_scache_add_file_lazy(pa_core *c, const char *name, const char *filename,
if (!(e = scache_add_item(c, name)))
return -1;
e->lazy = 1;
e->lazy = TRUE;
e->filename = pa_xstrdup(filename);
pa_proplist_sets(e->proplist, PA_PROP_MEDIA_FILENAME, filename);
if (!c->scache_auto_unload_event) {
struct timeval ntv;
pa_gettimeofday(&ntv);
@ -285,10 +306,10 @@ void pa_scache_free(pa_core *c) {
c->mainloop->time_free(c->scache_auto_unload_event);
}
int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, pa_volume_t volume) {
int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, pa_volume_t volume, pa_proplist *p, uint32_t *sink_input_idx) {
pa_scache_entry *e;
char *t;
pa_cvolume r;
pa_proplist *merged;
pa_assert(c);
pa_assert(name);
@ -312,17 +333,24 @@ int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, pa_volume_t
pa_log_debug("Playing sample \"%s\" on \"%s\"", name, sink->name);
t = pa_sprintf_malloc("sample:%s", name);
pa_cvolume_set(&r, e->volume.channels, volume);
pa_sw_cvolume_multiply(&r, &r, &e->volume);
if (pa_play_memchunk(sink, t, &e->sample_spec, &e->channel_map, &e->memchunk, &r) < 0) {
pa_xfree(t);
merged = pa_proplist_new();
pa_proplist_setf(merged, PA_PROP_MEDIA_NAME, "Sample %s", name);
pa_proplist_update(merged, PA_UPDATE_REPLACE, e->proplist);
if (p)
pa_proplist_update(merged, PA_UPDATE_REPLACE, p);
if (pa_play_memchunk(sink, &e->sample_spec, &e->channel_map, &e->memchunk, &r, merged, sink_input_idx) < 0) {
pa_proplist_free(merged);
return -1;
}
pa_xfree(t);
pa_proplist_free(merged);
if (e->lazy)
time(&e->last_used_time);
@ -330,7 +358,7 @@ int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, pa_volume_t
return 0;
}
int pa_scache_play_item_by_name(pa_core *c, const char *name, const char*sink_name, pa_volume_t volume, int autoload) {
int pa_scache_play_item_by_name(pa_core *c, const char *name, const char*sink_name, pa_bool_t autoload, pa_volume_t volume, pa_proplist *p, uint32_t *sink_input_idx) {
pa_sink *sink;
pa_assert(c);
@ -339,10 +367,10 @@ int pa_scache_play_item_by_name(pa_core *c, const char *name, const char*sink_na
if (!(sink = pa_namereg_get(c, sink_name, PA_NAMEREG_SINK, autoload)))
return -1;
return pa_scache_play_item(c, name, sink, volume);
return pa_scache_play_item(c, name, sink, volume, p, sink_input_idx);
}
const char * pa_scache_get_name_by_id(pa_core *c, uint32_t id) {
const char *pa_scache_get_name_by_id(pa_core *c, uint32_t id) {
pa_scache_entry *e;
pa_assert(c);
@ -366,9 +394,10 @@ uint32_t pa_scache_get_id_by_name(pa_core *c, const char *name) {
return e->index;
}
uint32_t pa_scache_total_size(pa_core *c) {
size_t pa_scache_total_size(pa_core *c) {
pa_scache_entry *e;
uint32_t idx, sum = 0;
uint32_t idx;
size_t sum = 0;
pa_assert(c);
@ -403,8 +432,7 @@ void pa_scache_unload_unused(pa_core *c) {
continue;
pa_memblock_unref(e->memchunk.memblock);
e->memchunk.memblock = NULL;
e->memchunk.index = e->memchunk.length = 0;
pa_memchunk_reset(&e->memchunk);
pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE|PA_SUBSCRIPTION_EVENT_CHANGE, e->index);
}
@ -467,8 +495,9 @@ int pa_scache_add_directory_lazy(pa_core *c, const char *pathname) {
pa_snprintf(p, sizeof(p), "%s/%s", pathname, e->d_name);
add_file(c, p);
}
closedir(dir);
}
closedir(dir);
return 0;
}

View file

@ -29,11 +29,12 @@
#include <pulsecore/memchunk.h>
#include <pulsecore/sink.h>
#define PA_SCACHE_ENTRY_SIZE_MAX (1024*1024*2)
#define PA_SCACHE_ENTRY_SIZE_MAX (1024*1024*16)
typedef struct pa_scache_entry {
pa_core *core;
uint32_t index;
pa_core *core;
char *name;
pa_cvolume volume;
@ -43,25 +44,27 @@ typedef struct pa_scache_entry {
char *filename;
int lazy;
pa_bool_t lazy;
time_t last_used_time;
pa_proplist *proplist;
} pa_scache_entry;
int pa_scache_add_item(pa_core *c, const char *name, const pa_sample_spec *ss, const pa_channel_map *map, const pa_memchunk *chunk, uint32_t *idx);
int pa_scache_add_item(pa_core *c, const char *name, const pa_sample_spec *ss, const pa_channel_map *map, const pa_memchunk *chunk, pa_proplist *p, uint32_t *idx);
int pa_scache_add_file(pa_core *c, const char *name, const char *filename, uint32_t *idx);
int pa_scache_add_file_lazy(pa_core *c, const char *name, const char *filename, uint32_t *idx);
int pa_scache_add_directory_lazy(pa_core *c, const char *pathname);
int pa_scache_remove_item(pa_core *c, const char *name);
int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, pa_volume_t volume);
int pa_scache_play_item_by_name(pa_core *c, const char *name, const char*sink_name, pa_volume_t volume, int autoload);
int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, pa_volume_t volume, pa_proplist *p, uint32_t *sink_input_idx);
int pa_scache_play_item_by_name(pa_core *c, const char *name, const char*sink_name, pa_bool_t autoload, pa_volume_t volume, pa_proplist *p, uint32_t *sink_input_idx);
void pa_scache_free(pa_core *c);
const char *pa_scache_get_name_by_id(pa_core *c, uint32_t id);
uint32_t pa_scache_get_id_by_name(pa_core *c, const char *name);
uint32_t pa_scache_total_size(pa_core *c);
size_t pa_scache_total_size(pa_core *c);
void pa_scache_unload_unused(pa_core *c);

View file

@ -41,6 +41,7 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <dirent.h>
#ifdef HAVE_STRTOF_L
#include <locale.h>
@ -103,12 +104,6 @@
#define MSG_NOSIGNAL 0
#endif
#ifndef OS_IS_WIN32
#define PA_USER_RUNTIME_PATH_PREFIX "/tmp/pulse-"
#else
#define PA_USER_RUNTIME_PATH_PREFIX "%TEMP%\\pulse-"
#endif
#ifdef OS_IS_WIN32
#define PULSE_ROOTENV "PULSE_ROOT"
@ -221,7 +216,7 @@ int pa_make_secure_dir(const char* dir, mode_t m, uid_t uid, gid_t gid) {
goto fail;
}
#else
pa_log_warn("secure directory creation not supported on Win32.");
pa_log_warn("Secure directory creation not supported on Win32.");
#endif
return 0;
@ -557,6 +552,82 @@ int pa_make_realtime(int rtprio) {
#endif
}
/* This is merely used for giving the user a hint. This is not correct
* for anything security related */
pa_bool_t pa_can_realtime(void) {
if (geteuid() == 0)
return TRUE;
#if defined(HAVE_SYS_RESOURCE_H) && defined(RLIMIT_RTPRIO)
{
struct rlimit rl;
if (getrlimit(RLIMIT_RTPRIO, &rl) >= 0)
if (rl.rlim_cur > 0 || rl.rlim_cur == RLIM_INFINITY)
return TRUE;
}
#endif
#if defined(HAVE_SYS_CAPABILITY_H) && defined(CAP_SYS_NICE)
{
cap_t cap;
if ((cap = cap_get_proc())) {
cap_flag_value_t flag = CAP_CLEAR;
if (cap_get_flag(cap, CAP_SYS_NICE, CAP_EFFECTIVE, &flag) >= 0)
if (flag == CAP_SET) {
cap_free(cap);
return TRUE;
}
cap_free(cap);
}
}
#endif
return FALSE;
}
/* This is merely used for giving the user a hint. This is not correct
* for anything security related */
pa_bool_t pa_can_high_priority(void) {
if (geteuid() == 0)
return TRUE;
#if defined(HAVE_SYS_RESOURCE_H) && defined(RLIMIT_RTPRIO)
{
struct rlimit rl;
if (getrlimit(RLIMIT_NICE, &rl) >= 0)
if (rl.rlim_cur >= 21 || rl.rlim_cur == RLIM_INFINITY)
return TRUE;
}
#endif
#if defined(HAVE_SYS_CAPABILITY_H) && defined(CAP_SYS_NICE)
{
cap_t cap;
if ((cap = cap_get_proc())) {
cap_flag_value_t flag = CAP_CLEAR;
if (cap_get_flag(cap, CAP_SYS_NICE, CAP_EFFECTIVE, &flag) >= 0)
if (flag == CAP_SET) {
cap_free(cap);
return TRUE;
}
cap_free(cap);
}
}
#endif
return FALSE;
}
/* Raise the priority of the current process as much as possible that
* is <= the specified nice level..*/
int pa_raise_priority(int nice_level) {
@ -612,6 +683,7 @@ void pa_reset_priority(void) {
/* Try to parse a boolean string value.*/
int pa_parse_boolean(const char *v) {
pa_assert(v);
if (!strcmp(v, "1") || v[0] == 'y' || v[0] == 'Y' || v[0] == 't' || v[0] == 'T' || !strcasecmp(v, "on"))
return 1;
@ -1093,16 +1165,126 @@ int pa_unlock_lockfile(const char *fn, int fd) {
return r;
}
char *pa_get_runtime_dir(void) {
const char *e;
char *d;
if ((e = getenv("PULSE_RUNTIME_PATH")))
d = pa_xstrdup(e);
else {
char h[PATH_MAX];
if (!pa_get_home_dir(h, sizeof(h))) {
pa_log_error("Failed to get home directory.");
return NULL;
}
d = pa_sprintf_malloc("%s" PA_PATH_SEP ".pulse", h);
}
if (pa_make_secure_dir(d, 0700, (pid_t) -1, (pid_t) -1) < 0) {
pa_log_error("Failed to create secure directory: %s", pa_cstrerror(errno));
return NULL;
}
return d;
}
/* Try to open a configuration file. If "env" is specified, open the
* value of the specified environment variable. Otherwise look for a
* file "local" in the home directory or a file "global" in global
* file system. If "result" is non-NULL, a pointer to a newly
* allocated buffer containing the used configuration file is
* stored there.*/
FILE *pa_open_config_file(const char *global, const char *local, const char *env, char **result, const char *mode) {
FILE *pa_open_config_file(const char *global, const char *local, const char *env, char **result) {
const char *fn;
char h[PATH_MAX];
#ifdef OS_IS_WIN32
char buf[PATH_MAX];
if (!getenv(PULSE_ROOTENV))
pa_set_root(NULL);
#endif
if (env && (fn = getenv(env))) {
FILE *f;
#ifdef OS_IS_WIN32
if (!ExpandEnvironmentStrings(fn, buf, PATH_MAX))
return NULL;
fn = buf;
#endif
if ((f = fopen(fn, "r"))) {
if (result)
*result = pa_xstrdup(fn);
return f;
}
pa_log_warn("Failed to open configuration file '%s': %s", fn, pa_cstrerror(errno));
return NULL;
}
if (local) {
const char *e;
char *lfn;
char h[PATH_MAX];
FILE *f;
if ((e = getenv("PULSE_CONFIG_PATH")))
fn = lfn = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", e, local);
else if (pa_get_home_dir(h, sizeof(h)))
fn = lfn = pa_sprintf_malloc("%s" PA_PATH_SEP ".pulse" PA_PATH_SEP "%s", h, local);
#ifdef OS_IS_WIN32
if (!ExpandEnvironmentStrings(lfn, buf, PATH_MAX)) {
pa_xfree(lfn);
return NULL;
}
fn = buf;
#endif
if ((f = fopen(fn, "r"))) {
if (result)
*result = pa_xstrdup(fn);
pa_xfree(lfn);
return f;
}
if (errno != ENOENT) {
pa_log_warn("Failed to open configuration file '%s': %s", fn, pa_cstrerror(errno));
pa_xfree(lfn);
return NULL;
}
pa_xfree(lfn);
}
if (global) {
FILE *f;
#ifdef OS_IS_WIN32
if (!ExpandEnvironmentStrings(global, buf, PATH_MAX))
return NULL;
global = buf;
#endif
if ((f = fopen(global, "r"))) {
if (result)
*result = pa_xstrdup(global);
return f;
}
} else
errno = ENOENT;
return NULL;
}
char *pa_find_config_file(const char *global, const char *local, const char *env) {
const char *fn;
#ifdef OS_IS_WIN32
char buf[PATH_MAX];
@ -1117,69 +1299,59 @@ FILE *pa_open_config_file(const char *global, const char *local, const char *env
fn = buf;
#endif
if (result)
*result = pa_xstrdup(fn);
if (access(fn, R_OK) == 0)
return pa_xstrdup(fn);
return fopen(fn, mode);
pa_log_warn("Failed to access configuration file '%s': %s", fn, pa_cstrerror(errno));
return NULL;
}
if (local) {
const char *e;
char *lfn = NULL;
char *lfn;
char h[PATH_MAX];
if ((e = getenv("PULSE_CONFIG_PATH")))
fn = lfn = pa_sprintf_malloc("%s/%s", e, local);
else if (pa_get_home_dir(h, sizeof(h))) {
char *d;
d = pa_sprintf_malloc("%s/.pulse", h);
mkdir(d, 0755);
pa_xfree(d);
fn = lfn = pa_sprintf_malloc("%s/.pulse/%s", h, local);
}
if (lfn) {
FILE *f;
fn = lfn = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", e, local);
else if (pa_get_home_dir(h, sizeof(h)))
fn = lfn = pa_sprintf_malloc("%s" PA_PATH_SEP ".pulse" PA_PATH_SEP "%s", h, local);
#ifdef OS_IS_WIN32
if (!ExpandEnvironmentStrings(lfn, buf, PATH_MAX))
return NULL;
fn = buf;
#endif
f = fopen(fn, mode);
if (f != NULL) {
if (result)
*result = pa_xstrdup(fn);
pa_xfree(lfn);
return f;
}
if (errno != ENOENT)
pa_log_warn("Failed to open configuration file '%s': %s", lfn, pa_cstrerror(errno));
if (!ExpandEnvironmentStrings(lfn, buf, PATH_MAX)) {
pa_xfree(lfn);
return NULL;
}
}
if (!global) {
if (result)
*result = NULL;
errno = ENOENT;
return NULL;
}
#ifdef OS_IS_WIN32
if (!ExpandEnvironmentStrings(global, buf, PATH_MAX))
return NULL;
global = buf;
fn = buf;
#endif
if (result)
*result = pa_xstrdup(global);
if (access(fn, R_OK) == 0) {
char *r = pa_xstrdup(fn);
pa_xfree(lfn);
return r;
}
return fopen(global, mode);
if (errno != ENOENT) {
pa_log_warn("Failed to access configuration file '%s': %s", fn, pa_cstrerror(errno));
pa_xfree(lfn);
return NULL;
}
pa_xfree(lfn);
}
if (global) {
#ifdef OS_IS_WIN32
if (!ExpandEnvironmentStrings(global, buf, PATH_MAX))
return NULL;
global = buf;
#endif
if (access(fn, R_OK) == 0)
return pa_xstrdup(global);
} else
errno = ENOENT;
return NULL;
}
/* Format the specified data as a hexademical string */
@ -1270,45 +1442,51 @@ int pa_endswith(const char *s, const char *sfx) {
return l1 >= l2 && strcmp(s+l1-l2, sfx) == 0;
}
/* if fn is null return the PulseAudio run time path in s (/tmp/pulse)
* if fn is non-null and starts with / return fn in s
* otherwise append fn to the run time path and return it in s */
char *pa_runtime_path(const char *fn, char *s, size_t l) {
const char *e;
pa_bool_t pa_is_path_absolute(const char *fn) {
pa_assert(fn);
#ifndef OS_IS_WIN32
if (fn && *fn == '/')
return *fn == '/';
#else
if (fn && strlen(fn) >= 3 && isalpha(fn[0]) && fn[1] == ':' && fn[2] == '\\')
return strlen(fn) >= 3 && isalpha(fn[0]) && fn[1] == ':' && fn[2] == '\\';
#endif
return pa_strlcpy(s, fn, l);
}
if ((e = getenv("PULSE_RUNTIME_PATH"))) {
char *pa_make_path_absolute(const char *p) {
char *r;
char *cwd;
if (fn)
pa_snprintf(s, l, "%s%c%s", e, PA_PATH_SEP_CHAR, fn);
else
pa_snprintf(s, l, "%s", e);
pa_assert(p);
} else {
char u[256];
if (pa_is_path_absolute(p))
return pa_xstrdup(p);
if (fn)
pa_snprintf(s, l, "%s%s%c%s", PA_USER_RUNTIME_PATH_PREFIX, pa_get_user_name(u, sizeof(u)), PA_PATH_SEP_CHAR, fn);
else
pa_snprintf(s, l, "%s%s", PA_USER_RUNTIME_PATH_PREFIX, pa_get_user_name(u, sizeof(u)));
}
if (!(cwd = pa_getcwd()))
return pa_xstrdup(p);
r = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", cwd, p);
pa_xfree(cwd);
return r;
}
#ifdef OS_IS_WIN32
{
char buf[l];
strcpy(buf, s);
ExpandEnvironmentStrings(buf, s, l);
}
#endif
/* if fn is null return the PulseAudio run time path in s (~/.pulse)
* if fn is non-null and starts with / return fn
* otherwise append fn to the run time path and return it */
char *pa_runtime_path(const char *fn) {
char *rtp;
return s;
if (pa_is_path_absolute(fn))
return pa_xstrdup(fn);
rtp = pa_get_runtime_dir();
if (fn) {
char *r;
r = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", rtp, fn);
pa_xfree(rtp);
return r;
} else
return rtp;
}
/* Convert the string s to a signed integer in *ret_i */
@ -1414,12 +1592,28 @@ int pa_snprintf(char *str, size_t size, const char *format, ...) {
pa_assert(format);
va_start(ap, format);
ret = vsnprintf(str, size, format, ap);
ret = pa_vsnprintf(str, size, format, ap);
va_end(ap);
return ret;
}
/* Same as vsnprintf, but guarantees NUL-termination on every platform */
int pa_vsnprintf(char *str, size_t size, const char *format, va_list ap) {
int ret;
pa_assert(str);
pa_assert(size > 0);
pa_assert(format);
ret = vsnprintf(str, size, format, ap);
str[size-1] = 0;
return ret;
if (ret < 0)
ret = strlen(str);
return PA_MIN((int) size-1, ret);
}
/* Truncate the specified string, but guarantee that the string
@ -1455,23 +1649,6 @@ char *pa_getcwd(void) {
}
}
char *pa_make_path_absolute(const char *p) {
char *r;
char *cwd;
pa_assert(p);
if (p[0] == '/')
return pa_xstrdup(p);
if (!(cwd = pa_getcwd()))
return pa_xstrdup(p);
r = pa_sprintf_malloc("%s/%s", cwd, p);
pa_xfree(cwd);
return r;
}
void *pa_will_need(const void *p, size_t l) {
#ifdef RLIMIT_MEMLOCK
struct rlimit rlim;
@ -1577,3 +1754,249 @@ char *pa_readlink(const char *p) {
l *= 2;
}
}
int pa_close_all(int except_fd, ...) {
va_list ap;
int n = 0, i, r;
int *p;
va_start(ap, except_fd);
if (except_fd >= 0)
for (n = 1; va_arg(ap, int) >= 0; n++)
;
va_end(ap);
p = pa_xnew(int, n+1);
va_start(ap, except_fd);
i = 0;
if (except_fd >= 0) {
p[i++] = except_fd;
while ((p[i++] = va_arg(ap, int)) >= 0)
;
}
p[i] = -1;
va_end(ap);
r = pa_close_allv(p);
free(p);
return r;
}
int pa_close_allv(const int except_fds[]) {
struct rlimit rl;
int fd;
int saved_errno;
#ifdef __linux__
DIR *d;
if ((d = opendir("/proc/self/fd"))) {
struct dirent *de;
while ((de = readdir(d))) {
long l;
char *e = NULL;
int i;
if (de->d_name[0] == '.')
continue;
errno = 0;
l = strtol(de->d_name, &e, 10);
if (errno != 0 || !e || *e) {
closedir(d);
errno = EINVAL;
return -1;
}
fd = (int) l;
if ((long) fd != l) {
closedir(d);
errno = EINVAL;
return -1;
}
if (fd <= 3)
continue;
if (fd == dirfd(d))
continue;
for (i = 0; except_fds[i] >= 0; i++)
if (except_fds[i] == fd)
continue;
if (close(fd) < 0) {
saved_errno = errno;
closedir(d);
errno = saved_errno;
return -1;
}
}
closedir(d);
return 0;
}
#endif
if (getrlimit(RLIMIT_NOFILE, &rl) < 0)
return -1;
for (fd = 0; fd < (int) rl.rlim_max; fd++) {
int i;
if (fd <= 3)
continue;
for (i = 0; except_fds[i] >= 0; i++)
if (except_fds[i] == fd)
continue;
if (close(fd) < 0 && errno != EBADF)
return -1;
}
return 0;
}
int pa_unblock_sigs(int except, ...) {
va_list ap;
int n = 0, i, r;
int *p;
va_start(ap, except);
if (except >= 1)
for (n = 1; va_arg(ap, int) >= 0; n++)
;
va_end(ap);
p = pa_xnew(int, n+1);
va_start(ap, except);
i = 0;
if (except >= 1) {
p[i++] = except;
while ((p[i++] = va_arg(ap, int)) >= 0)
;
}
p[i] = -1;
va_end(ap);
r = pa_unblock_sigsv(p);
pa_xfree(p);
return r;
}
int pa_unblock_sigsv(const int except[]) {
int i;
sigset_t ss;
if (sigemptyset(&ss) < 0)
return -1;
for (i = 0; except[i] > 0; i++)
if (sigaddset(&ss, except[i]) < 0)
return -1;
return sigprocmask(SIG_SETMASK, &ss, NULL);
}
int pa_reset_sigs(int except, ...) {
va_list ap;
int n = 0, i, r;
int *p;
va_start(ap, except);
if (except >= 1)
for (n = 1; va_arg(ap, int) >= 0; n++)
;
va_end(ap);
p = pa_xnew(int, n+1);
va_start(ap, except);
i = 0;
if (except >= 1) {
p[i++] = except;
while ((p[i++] = va_arg(ap, int)) >= 0)
;
}
p[i] = -1;
va_end(ap);
r = pa_reset_sigsv(p);
pa_xfree(p);
return r;
}
int pa_reset_sigsv(const int except[]) {
int sig;
for (sig = 1; sig < _NSIG; sig++) {
int reset = 1;
switch (sig) {
case SIGKILL:
case SIGSTOP:
reset = 0;
break;
default: {
int i;
for (i = 0; except[i] > 0; i++) {
if (sig == except[i]) {
reset = 0;
break;
}
}
}
}
if (reset) {
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_handler = SIG_DFL;
/* On Linux the first two RT signals are reserved by
* glibc, and sigaction() will return EINVAL for them. */
if ((sigaction(sig, &sa, NULL) < 0))
if (errno != EINVAL)
return -1;
}
}
return 0;
}
void pa_set_env(const char *key, const char *value) {
pa_assert(key);
pa_assert(value);
putenv(pa_sprintf_malloc("%s=%s", key, value));
}

View file

@ -30,11 +30,27 @@
#include <stdarg.h>
#include <stdio.h>
#include <pulsecore/gccmacro.h>
#ifdef HAVE_SYS_RESOURCE_H
#include <sys/resource.h>
#endif
#include <pulse/gccmacro.h>
#include <pulsecore/macro.h>
struct timeval;
/* These resource limits are pretty new on Linux, let's define them
* here manually, in case the kernel is newer than the glibc */
#if !defined(RLIMIT_NICE) && defined(__linux__)
#define RLIMIT_NICE 13
#endif
#if !defined(RLIMIT_RTPRIO) && defined(__linux__)
#define RLIMIT_RTPRIO 14
#endif
#if !defined(RLIMIT_RTTIME) && defined(__linux__)
#define RLIMIT_RTTIME 15
#endif
void pa_make_fd_nonblock(int fd);
void pa_make_fd_cloexec(int fd);
@ -61,12 +77,23 @@ int pa_make_realtime(int rtprio);
int pa_raise_priority(int nice_level);
void pa_reset_priority(void);
pa_bool_t pa_can_realtime(void);
pa_bool_t pa_can_high_priority(void);
int pa_parse_boolean(const char *s) PA_GCC_PURE;
static inline const char *pa_yes_no(pa_bool_t b) {
return b ? "yes" : "no";
}
static inline const char *pa_strnull(const char *x) {
return x ? x : "(null)";
}
static inline const char *pa_strempty(const char *x) {
return x ? x : "";
}
char *pa_split(const char *c, const char*delimiters, const char **state);
char *pa_split_spaces(const char *c, const char **state);
@ -84,26 +111,30 @@ int pa_lock_fd(int fd, int b);
int pa_lock_lockfile(const char *fn);
int pa_unlock_lockfile(const char *fn, int fd);
FILE *pa_open_config_file(const char *global, const char *local, const char *env, char **result, const char *mode);
char *pa_hexstr(const uint8_t* d, size_t dlength, char *s, size_t slength);
size_t pa_parsehex(const char *p, uint8_t *d, size_t dlength);
int pa_startswith(const char *s, const char *pfx) PA_GCC_PURE;
int pa_endswith(const char *s, const char *sfx) PA_GCC_PURE;
char *pa_runtime_path(const char *fn, char *s, size_t l);
FILE *pa_open_config_file(const char *global, const char *local, const char *env, char **result);
char* pa_find_config_file(const char *global, const char *local, const char *env);
char *pa_get_runtime_dir(void);
char *pa_runtime_path(const char *fn);
int pa_atoi(const char *s, int32_t *ret_i);
int pa_atou(const char *s, uint32_t *ret_u);
int pa_atof(const char *s, float *ret_f);
int pa_snprintf(char *str, size_t size, const char *format, ...);
int pa_vsnprintf(char *str, size_t size, const char *format, va_list ap);
char *pa_truncate_utf8(char *c, size_t l);
char *pa_getcwd(void);
char *pa_make_path_absolute(const char *p);
pa_bool_t pa_is_path_absolute(const char *p);
void *pa_will_need(const void *p, size_t l);
@ -125,8 +156,28 @@ static inline unsigned pa_make_power_of_two(unsigned n) {
return n + 1;
}
static inline unsigned pa_ulog2(unsigned n) {
unsigned r = 0;
while (n) {
r++;
n = n >> 1;
}
return r;
}
void pa_close_pipe(int fds[2]);
char *pa_readlink(const char *p);
int pa_close_all(int except_fd, ...);
int pa_close_allv(const int except_fds[]);
int pa_unblock_sigs(int except, ...);
int pa_unblock_sigsv(const int except[]);
int pa_reset_sigs(int except, ...);
int pa_reset_sigsv(const int except[]);
void pa_set_env(const char *key, const char *value);
#endif

View file

@ -125,6 +125,7 @@ pa_core* pa_core_new(pa_mainloop_api *m, int shared) {
c->subscription_event_last = NULL;
c->mempool = pool;
pa_silence_cache_init(&c->silence_cache);
c->quit_event = NULL;
@ -188,6 +189,7 @@ static void core_free(pa_object *o) {
pa_xfree(c->default_source_name);
pa_xfree(c->default_sink_name);
pa_silence_cache_done(&c->silence_cache);
pa_mempool_free(c->mempool);
pa_property_cleanup(c);

View file

@ -35,6 +35,7 @@
#include <pulsecore/llist.h>
#include <pulsecore/hook-list.h>
#include <pulsecore/asyncmsgq.h>
#include <pulsecore/sample-util.h>
typedef struct pa_core pa_core;
@ -43,16 +44,20 @@ typedef struct pa_core pa_core;
#include <pulsecore/msgobject.h>
typedef enum pa_core_hook {
PA_CORE_HOOK_SINK_NEW_POST,
PA_CORE_HOOK_SINK_NEW,
PA_CORE_HOOK_SINK_FIXATE,
PA_CORE_HOOK_SINK_PUT,
PA_CORE_HOOK_SINK_UNLINK,
PA_CORE_HOOK_SINK_UNLINK_POST,
PA_CORE_HOOK_SINK_STATE_CHANGED,
PA_CORE_HOOK_SINK_DESCRIPTION_CHANGED,
PA_CORE_HOOK_SOURCE_NEW_POST,
PA_CORE_HOOK_SINK_PROPLIST_CHANGED,
PA_CORE_HOOK_SOURCE_NEW,
PA_CORE_HOOK_SOURCE_FIXATE,
PA_CORE_HOOK_SOURCE_PUT,
PA_CORE_HOOK_SOURCE_UNLINK,
PA_CORE_HOOK_SOURCE_UNLINK_POST,
PA_CORE_HOOK_SOURCE_STATE_CHANGED,
PA_CORE_HOOK_SOURCE_DESCRIPTION_CHANGED,
PA_CORE_HOOK_SOURCE_PROPLIST_CHANGED,
PA_CORE_HOOK_SINK_INPUT_NEW,
PA_CORE_HOOK_SINK_INPUT_FIXATE,
PA_CORE_HOOK_SINK_INPUT_PUT,
@ -60,8 +65,8 @@ typedef enum pa_core_hook {
PA_CORE_HOOK_SINK_INPUT_UNLINK_POST,
PA_CORE_HOOK_SINK_INPUT_MOVE,
PA_CORE_HOOK_SINK_INPUT_MOVE_POST,
PA_CORE_HOOK_SINK_INPUT_NAME_CHANGED,
PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED,
PA_CORE_HOOK_SINK_INPUT_PROPLIST_CHANGED,
PA_CORE_HOOK_SOURCE_OUTPUT_NEW,
PA_CORE_HOOK_SOURCE_OUTPUT_FIXATE,
PA_CORE_HOOK_SOURCE_OUTPUT_PUT,
@ -69,8 +74,8 @@ typedef enum pa_core_hook {
PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK_POST,
PA_CORE_HOOK_SOURCE_OUTPUT_MOVE,
PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_POST,
PA_CORE_HOOK_SOURCE_OUTPUT_NAME_CHANGED,
PA_CORE_HOOK_SOURCE_OUTPUT_STATE_CHANGED,
PA_CORE_HOOK_SOURCE_OUTPUT_PROPLIST_CHANGED,
PA_CORE_HOOK_MAX
} pa_core_hook_t;
@ -108,6 +113,7 @@ struct pa_core {
pa_subscription_event *subscription_event_last;
pa_mempool *mempool;
pa_silence_cache silence_cache;
int exit_idle_time, module_idle_time, scache_idle_time;

View file

@ -381,7 +381,7 @@ static void envelope_merge(pa_envelope *e, int v) {
break;
if (e->points[v].n_points >= e->points[v].n_allocated) {
e->points[v].n_allocated = MAX(e->points[v].n_points*2, PA_ENVELOPE_POINTS_MAX);
e->points[v].n_allocated = PA_MAX(e->points[v].n_points*2, PA_ENVELOPE_POINTS_MAX);
e->points[v].x = pa_xrealloc(e->points[v].x, sizeof(size_t) * e->points[v].n_allocated);
e->points[v].y.i = pa_xrealloc(e->points[v].y.i, sizeof(int32_t) * e->points[v].n_allocated);

Some files were not shown because too many files have changed in this diff Show more