diff options
Diffstat (limited to 'src')
372 files changed, 3948 insertions, 1970 deletions
diff --git a/src/.gitignore b/src/.gitignore new file mode 100644 index 00000000..f3ed2e2e --- /dev/null +++ b/src/.gitignore @@ -0,0 +1,56 @@ +*.lo +*.o +*.la +.deps +.libs +Makefile +Makefile.in +asyncmsgq-test +asyncq-test +bt-proximity-helper +channelmap-test +client.conf +close-test +cpulimit-test +cpulimit-test2 +daemon.conf +default.pa +envelope-test +esdcompat +flist-test +gconf-helper +get-binary-name-test +hook-list-test +interpol-test +ipacl-test +mainloop-test +mainloop-test-glib +mcalign-test +memblock-test +memblockq-test +mix-test +pabrowse +pacat +pacat-simple +pacmd +pactl +paplay +parec-simple +pasuspender +pax11publish +proplist-test +pulseaudio +queue-test +remix-test +resampler-test +rtpoll-test +rtstutter +sig2str-test +smoother-test +stripnul +strlist-test +sync-playback +thread-mainloop-test +thread-test +utf8-test +voltest diff --git a/src/Makefile.am b/src/Makefile.am index 799e7b26..9cce6ed4 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,5 +1,3 @@ -# $Id$ -# # This file is part of PulseAudio. # # Copyright 2004-2006 Lennart Poettering @@ -55,6 +53,8 @@ AM_CFLAGS += -DPA_DLSEARCHPATH=\"$(modlibexecdir)\" AM_CFLAGS += -DPA_DEFAULT_CONFIG_DIR=\"$(PA_DEFAULT_CONFIG_DIR)\" AM_CFLAGS += -DPA_BINARY=\"$(PA_BINARY)\" AM_CFLAGS += -DPA_SYSTEM_RUNTIME_PATH=\"$(PA_SYSTEM_RUNTIME_PATH)\" +AM_CFLAGS += -DPA_SYSTEM_CONFIG_PATH=\"$(PA_SYSTEM_CONFIG_PATH)\" +AM_CFLAGS += -DPA_SYSTEM_STATE_PATH=\"$(PA_SYSTEM_STATE_PATH)\" AM_CFLAGS += -DAO_REQUIRE_CAS # This cool debug trap works on i386/gcc only @@ -231,6 +231,7 @@ noinst_PROGRAMS = \ pacat-simple \ parec-simple \ strlist-test \ + close-test \ voltest \ memblockq-test \ sync-playback \ @@ -356,6 +357,11 @@ strlist_test_CFLAGS = $(AM_CFLAGS) strlist_test_LDADD = $(AM_LDADD) $(WINSOCK_LIBS) libpulsecore.la libstrlist.la strlist_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) +close_test_SOURCES = tests/close-test.c +close_test_CFLAGS = $(AM_CFLAGS) +close_test_LDADD = $(AM_LDADD) $(WINSOCK_LIBS) libpulsecore.la libstrlist.la +close_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) + voltest_SOURCES = tests/voltest.c voltest_CFLAGS = $(AM_CFLAGS) voltest_LDADD = $(AM_LDADD) libpulse.la @@ -563,6 +569,7 @@ libpulse_la_SOURCES += \ pulsecore/once.c pulsecore/once.h \ pulsecore/rtclock.c pulsecore/rtclock.h \ pulsecore/time-smoother.c pulsecore/time-smoother.h \ + pulsecore/proplist-util.c pulsecore/proplist-util.h \ $(PA_THREAD_OBJS) if OS_IS_WIN32 @@ -630,7 +637,7 @@ bin_SCRIPTS += utils/padsp endif -libpulsedsp_la_SOURCES = utils/padsp.c +libpulsedsp_la_SOURCES = utils/padsp.c pulsecore/core-util.c pulsecore/core-util.h pulsecore/core-error.c pulsecore/core-error.h pulsecore/log.c pulsecore/log.h pulsecore/once.c pulsecore/once.h $(PA_THREAD_OBJS) libpulsedsp_la_CFLAGS = $(AM_CFLAGS) libpulsedsp_la_LIBADD = $(AM_LIBADD) libpulse.la libpulsedsp_la_LDFLAGS = -avoid-version @@ -790,6 +797,7 @@ libpulsecore_la_SOURCES += \ pulsecore/time-smoother.c pulsecore/time-smoother.h \ pulsecore/start-child.c pulsecore/start-child.h \ pulsecore/envelope.c pulsecore/envelope.h \ + pulsecore/proplist-util.c pulsecore/proplist-util.h \ $(PA_THREAD_OBJS) if OS_IS_WIN32 @@ -916,7 +924,7 @@ libpstream_la_LIBADD = $(AM_LIBADD) libpulsecore.la libpacket.la libiochannel.la libpstream_util_la_SOURCES = pulsecore/pstream-util.c pulsecore/pstream-util.h libpstream_util_la_LDFLAGS = -avoid-version -libpstream_util_la_LIBADD = $(AM_LIBADD) libpacket.la libpstream.la libtagstruct.la +libpstream_util_la_LIBADD = $(AM_LIBADD) libpacket.la libpstream.la libtagstruct.la libpulsecore.la libpdispatch_la_SOURCES = pulsecore/pdispatch.c pulsecore/pdispatch.h libpdispatch_la_LDFLAGS = -avoid-version @@ -1013,6 +1021,7 @@ modlibexec_LTLIBRARIES += \ module-volume-restore.la \ module-device-restore.la \ module-default-device-restore.la \ + module-always-sink.la \ module-rescue-streams.la \ module-suspend-on-idle.la \ module-http-protocol-tcp.la \ @@ -1025,7 +1034,9 @@ modlibexec_LTLIBRARIES += \ module-ladspa-sink.la \ module-esound-sink.la \ module-tunnel-sink.la \ - module-tunnel-source.la + module-tunnel-source.la \ + module-position-event-sounds.la + # See comment at librtp.la above if !OS_IS_WIN32 @@ -1128,6 +1139,12 @@ modlibexec_LTLIBRARIES += \ module-hal-detect.la endif +if HAVE_DBUS +modlibexec_LTLIBRARIES += \ + libdbus-util.la \ + module-console-kit.la +endif + if HAVE_BLUEZ modlibexec_LTLIBRARIES += \ module-bt-proximity.la @@ -1184,11 +1201,14 @@ SYMDEF_FILES = \ modules/module-volume-restore-symdef.h \ modules/module-device-restore-symdef.h \ modules/module-default-device-restore-symdef.h \ + modules/module-always-sink-symdef.h \ modules/module-rescue-streams-symdef.h \ modules/module-suspend-on-idle-symdef.h \ modules/module-hal-detect-symdef.h \ modules/module-bt-proximity-symdef.h \ - modules/gconf/module-gconf-symdef.h + modules/gconf/module-gconf-symdef.h \ + modules/module-position-event-sounds-symdef.h \ + modules/module-console-kit-symdef.h EXTRA_DIST += $(SYMDEF_FILES) BUILT_SOURCES += $(SYMDEF_FILES) @@ -1343,7 +1363,7 @@ module_x11_publish_la_LIBADD = $(AM_LIBADD) $(X_PRE_LIBS) -lX11 $(X_LIBS) $(X_EX module_x11_xsmp_la_SOURCES = modules/module-x11-xsmp.c module_x11_xsmp_la_CFLAGS = $(AM_CFLAGS) $(X_CFLAGS) module_x11_xsmp_la_LDFLAGS = -module -avoid-version -module_x11_xsmp_la_LIBADD = $(AM_LIBADD) $(X_PRE_LIBS) -lX11 $(X_LIBS) $(X_EXTRA_LIBS) libpulsecore.la +module_x11_xsmp_la_LIBADD = $(AM_LIBADD) $(X_PRE_LIBS) -lX11 $(X_LIBS) $(X_EXTRA_LIBS) libx11wrap.la libpulsecore.la # OSS @@ -1423,6 +1443,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) +# Position event sounds in space +module_position_event_sounds_la_SOURCES = modules/module-position-event-sounds.c +module_position_event_sounds_la_LDFLAGS = -module -avoid-version +module_position_event_sounds_la_LIBADD = $(AM_LIBADD) libpulsecore.la +module_position_event_sounds_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 @@ -1435,6 +1461,12 @@ module_default_device_restore_la_LDFLAGS = -module -avoid-version module_default_device_restore_la_LIBADD = $(AM_LIBADD) libpulsecore.la module_default_device_restore_la_CFLAGS = $(AM_CFLAGS) +# Always Sink module +module_always_sink_la_SOURCES = modules/module-always-sink.c +module_always_sink_la_LDFLAGS = -module -avoid-version +module_always_sink_la_LIBADD = $(AM_LIBADD) libpulsecore.la +module_always_sink_la_CFLAGS = $(AM_CFLAGS) + # Rescue streams module module_rescue_streams_la_SOURCES = modules/module-rescue-streams.c module_rescue_streams_la_LDFLAGS = -module -avoid-version @@ -1470,7 +1502,7 @@ module_jack_source_la_LDFLAGS = -module -avoid-version module_jack_source_la_LIBADD = $(AM_LIBADD) libpulsecore.la $(JACK_LIBS) module_jack_source_la_CFLAGS = $(AM_CFLAGS) $(JACK_CFLAGS) -# HAL +# HAL/D-Bus libdbus_util_la_SOURCES = modules/dbus-util.c modules/dbus-util.h libdbus_util_la_LDFLAGS = -avoid-version libdbus_util_la_LIBADD = $(AM_LIBADD) $(DBUS_LIBS) libpulsecore.la @@ -1481,6 +1513,11 @@ module_hal_detect_la_LDFLAGS = -module -avoid-version module_hal_detect_la_LIBADD = $(AM_LIBADD) $(HAL_LIBS) libpulsecore.la libdbus-util.la module_hal_detect_la_CFLAGS = $(AM_CFLAGS) $(HAL_CFLAGS) +module_console_kit_la_SOURCES = modules/module-console-kit.c +module_console_kit_la_LDFLAGS = -module -avoid-version +module_console_kit_la_LIBADD = $(AM_LIBADD) $(HAL_LIBS) libpulsecore.la libdbus-util.la +module_console_kit_la_CFLAGS = $(AM_CFLAGS) $(HAL_CFLAGS) + # GConf support module_gconf_la_SOURCES = modules/gconf/module-gconf.c module_gconf_la_LDFLAGS = -module -avoid-version diff --git a/src/daemon/caps.c b/src/daemon/caps.c index e936d6bb..ae07119c 100644 --- a/src/daemon/caps.c +++ b/src/daemon/caps.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -93,13 +91,18 @@ void pa_limit_caps(void) { 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); - - pa_assert_se(prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) == 0); - pa_log_info("Dropped capabilities successfully."); + if (cap_set_proc(caps) < 0) + /* Hmm, so we couldn't limit our caps, which probably means we + * hadn't any in the first place, so let's just make sure of + * that */ + pa_drop_caps(); + else + pa_log_info("Limited capabilities successfully to CAP_SYS_NICE."); pa_assert_se(cap_free(caps) == 0); + + pa_assert_se(prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) == 0); } /* Drop all capabilities, effectively becoming a normal user */ @@ -112,17 +115,33 @@ void pa_drop_caps(void) { pa_assert_se(cap_clear(caps) == 0); pa_assert_se(cap_set_proc(caps) == 0); pa_assert_se(cap_free(caps) == 0); + + pa_assert_se(!pa_have_caps()); +} + +pa_bool_t pa_have_caps(void) { + cap_t caps; + cap_flag_value_t flag = CAP_CLEAR; + + pa_assert_se(caps = cap_get_proc()); + pa_assert_se(cap_get_flag(caps, CAP_SYS_NICE, CAP_EFFECTIVE, &flag) >= 0); + pa_assert_se(cap_free(caps) == 0); + + return flag == CAP_SET; } #else /* NOOPs in case capabilities are not available. */ -int pa_limit_caps(void) { - return 0; +void pa_limit_caps(void) { } void pa_drop_caps(void) { pa_drop_root(); } +pa_bool_t pa_have_caps(void) { + return FALSE; +} + #endif diff --git a/src/daemon/caps.h b/src/daemon/caps.h index 5b21f12e..176aa90e 100644 --- a/src/daemon/caps.h +++ b/src/daemon/caps.h @@ -1,8 +1,6 @@ #ifndef foocapshfoo #define foocapshfoo -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -24,8 +22,11 @@ USA. ***/ +#include <pulsecore/macro.h> + void pa_drop_root(void); void pa_drop_caps(void); void pa_limit_caps(void); +pa_bool_t pa_have_caps(void); #endif diff --git a/src/daemon/cmdline.c b/src/daemon/cmdline.c index 97c75f37..4b2466ce 100644 --- a/src/daemon/cmdline.c +++ b/src/daemon/cmdline.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -66,7 +64,8 @@ enum { ARG_DISABLE_SHM, ARG_DUMP_RESAMPLE_METHODS, ARG_SYSTEM, - ARG_CLEANUP_SHM + ARG_CLEANUP_SHM, + ARG_START }; /* Tabel for getopt_long() */ @@ -91,6 +90,7 @@ static const struct option long_options[] = { {"dl-search-path", 1, 0, ARG_DL_SEARCH_PATH}, {"resample-method", 1, 0, ARG_RESAMPLE_METHOD}, {"kill", 0, 0, ARG_KILL}, + {"start", 0, 0, ARG_START}, {"use-pid-file", 2, 0, ARG_USE_PID_FILE}, {"check", 0, 0, ARG_CHECK}, {"system", 2, 0, ARG_SYSTEM}, @@ -119,6 +119,7 @@ void pa_cmdline_help(const char *argv0) { " --dump-modules Dump list of available modules\n" " --dump-resample-methods Dump available resample methods\n" " --cleanup-shm Cleanup stale shared memory segments\n" + " --start Start the daemon if it is not running\n" " -k --kill Kill a running daemon\n" " --check Check for a running daemon\n\n" @@ -207,6 +208,11 @@ int pa_cmdline_parse(pa_daemon_conf *conf, int argc, char *const argv [], int *d conf->cmd = PA_CMD_KILL; break; + case ARG_START: + conf->cmd = PA_CMD_START; + conf->daemonize = TRUE; + break; + case ARG_CHECK: conf->cmd = PA_CMD_CHECK; break; diff --git a/src/daemon/cmdline.h b/src/daemon/cmdline.h index 18418894..fd72a6d3 100644 --- a/src/daemon/cmdline.h +++ b/src/daemon/cmdline.h @@ -1,8 +1,6 @@ #ifndef foocmdlinehfoo #define foocmdlinehfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/daemon/cpulimit.c b/src/daemon/cpulimit.c index 579b91e3..42a71f7e 100644 --- a/src/daemon/cpulimit.c +++ b/src/daemon/cpulimit.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/daemon/cpulimit.h b/src/daemon/cpulimit.h index 271109b4..cb9a123d 100644 --- a/src/daemon/cpulimit.h +++ b/src/daemon/cpulimit.h @@ -1,8 +1,6 @@ #ifndef foocpulimithfoo #define foocpulimithfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/daemon/daemon-conf.c b/src/daemon/daemon-conf.c index f9ad7ec0..9ac40901 100644 --- a/src/daemon/daemon-conf.c +++ b/src/daemon/daemon-conf.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -295,7 +293,7 @@ static int parse_sample_rate(const char *filename, unsigned line, const char *lv pa_assert(rvalue); pa_assert(data); - if (pa_atoi(rvalue, &r) < 0 || r > PA_RATE_MAX || r <= 0) { + if (pa_atoi(rvalue, &r) < 0 || r > (int32_t) PA_RATE_MAX || r <= 0) { pa_log("[%s:%u] Invalid sample rate '%s'.", filename, line, rvalue); return -1; } @@ -313,7 +311,7 @@ static int parse_sample_channels(const char *filename, unsigned line, const char pa_assert(rvalue); pa_assert(data); - if (pa_atoi(rvalue, &n) < 0 || n > PA_CHANNELS_MAX || n <= 0) { + if (pa_atoi(rvalue, &n) < 0 || n > (int32_t) PA_CHANNELS_MAX || n <= 0) { pa_log("[%s:%u] Invalid sample channels '%s'.", filename, line, rvalue); return -1; } diff --git a/src/daemon/daemon-conf.h b/src/daemon/daemon-conf.h index 03a75661..be2fe1ab 100644 --- a/src/daemon/daemon-conf.h +++ b/src/daemon/daemon-conf.h @@ -1,8 +1,6 @@ #ifndef foodaemonconfhfoo #define foodaemonconfhfoo -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -37,6 +35,7 @@ /* The actual command to execute */ typedef enum pa_daemon_conf_cmd { PA_CMD_DAEMON, /* the default */ + PA_CMD_START, PA_CMD_HELP, PA_CMD_VERSION, PA_CMD_DUMP_CONF, diff --git a/src/daemon/daemon.conf.in b/src/daemon/daemon.conf.in index fd35c0f6..dfabcfb2 100644 --- a/src/daemon/daemon.conf.in +++ b/src/daemon/daemon.conf.in @@ -1,5 +1,3 @@ -# $Id$ -# # This file is part of PulseAudio. # # PulseAudio is free software; you can redistribute it and/or modify diff --git a/src/daemon/default.pa.in b/src/daemon/default.pa.in index 064a6cc9..aad9f5d6 100755 --- a/src/daemon/default.pa.in +++ b/src/daemon/default.pa.in @@ -74,16 +74,26 @@ load-module module-default-device-restore ### connected to dies, similar for sources load-module module-rescue-streams +### Make sure we always have a sink around, even if it is a null sink. +load-module module-always-sink + ### Automatically suspend sinks/sources that become idle for too long load-module module-suspend-on-idle ### Load X11 bell module -#load-module module-x11-bell sample=x11-bell +#load-module module-x11-bell sample=bell-windowing-system ### Register ourselves in the X11 session manager # Deactivated by default, to avoid deadlock when PA is started as esd from gnome-session # Instead we load this via /etc/xdg/autostart/ and "pactl load-module" now -# load-module module-x11-xsmp +#load-module module-x11-xsmp + +### If autoexit on idle is enabled we want to make sure we only quit +### when no local session needs us anymore. +load-module module-console-kit + +### Enable positioned event sounds +load-module module-position-event-sounds ### Load additional modules from GConf settings. This can be configured with the paprefs tool. ### Please keep in mind that the modules configured by paprefs might conflict with manually diff --git a/src/daemon/dumpmodules.c b/src/daemon/dumpmodules.c index 68236c70..cd6866aa 100644 --- a/src/daemon/dumpmodules.c +++ b/src/daemon/dumpmodules.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/daemon/dumpmodules.h b/src/daemon/dumpmodules.h index ab2ddb64..c49a5eda 100644 --- a/src/daemon/dumpmodules.h +++ b/src/daemon/dumpmodules.h @@ -1,8 +1,6 @@ #ifndef foodumpmoduleshfoo #define foodumpmoduleshfoo -/* $Id*/ - /*** This file is part of PulseAudio. diff --git a/src/daemon/esdcompat.in b/src/daemon/esdcompat.in index 942389d2..66501803 100755 --- a/src/daemon/esdcompat.in +++ b/src/daemon/esdcompat.in @@ -1,7 +1,5 @@ #!/bin/sh -# $Id$ -# # This file is part of PulseAudio. # # PulseAudio is free software; you can redistribute it and/or modify diff --git a/src/daemon/ltdl-bind-now.c b/src/daemon/ltdl-bind-now.c index 6915fe0c..b1770674 100644 --- a/src/daemon/ltdl-bind-now.c +++ b/src/daemon/ltdl-bind-now.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/daemon/ltdl-bind-now.h b/src/daemon/ltdl-bind-now.h index e19c7bc1..f95d13b4 100644 --- a/src/daemon/ltdl-bind-now.h +++ b/src/daemon/ltdl-bind-now.h @@ -1,8 +1,6 @@ #ifndef foopulsecoreltdlbindnowhfoo #define foopulsecoreltdlbindnowhfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/daemon/main.c b/src/daemon/main.c index 789d104b..14594416 100644 --- a/src/daemon/main.c +++ b/src/daemon/main.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -96,6 +94,8 @@ #include "ltdl-bind-now.h" #include "polkit.h" +#define AUTOSPAWN_LOCK "autospawn.lock" + #ifdef HAVE_LIBWRAP /* Only one instance of these variables */ int allow_severity = LOG_INFO; @@ -202,6 +202,13 @@ static int change_user(void) { return -1; } + if (pa_make_secure_dir(PA_SYSTEM_STATE_PATH, 0700, pw->pw_uid, gr->gr_gid) < 0) { + pa_log("Failed to create '%s': %s", PA_SYSTEM_STATE_PATH, pa_cstrerror(errno)); + return -1; + } + + /* We don't create the config dir here, because we don't need to write to it */ + if (initgroups(PA_SYSTEM_USER, gr->gr_gid) != 0) { pa_log("Failed to change group list: %s", pa_cstrerror(errno)); return -1; @@ -246,7 +253,8 @@ static int change_user(void) { /* Relevant for pa_runtime_path() */ pa_set_env("PULSE_RUNTIME_PATH", PA_SYSTEM_RUNTIME_PATH); - pa_set_env("PULSE_CONFIG_PATH", PA_SYSTEM_RUNTIME_PATH); + pa_set_env("PULSE_CONFIG_PATH", PA_SYSTEM_CONFIG_PATH); + pa_set_env("PULSE_STATE_PATH", PA_SYSTEM_STATE_PATH); pa_log_info("Successfully dropped root privileges."); @@ -336,6 +344,8 @@ int main(int argc, char *argv[]) { pa_time_event *win32_timer; struct timeval win32_tv; #endif + char *lf = NULL; + int autospawn_lock_fd = -1; #if defined(__linux__) && defined(__OPTIMIZE__) /* @@ -364,7 +374,7 @@ int main(int argc, char *argv[]) { suid_root = FALSE; #endif - if (suid_root) { + if (!real_root) { /* Drop all capabilities except CAP_SYS_NICE */ pa_limit_caps(); @@ -416,20 +426,26 @@ int main(int argc, char *argv[]) { pa_log_set_maximal_level(conf->log_level); 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; + pa_log_debug("Started as real root: %s, suid root: %s", pa_yes_no(real_root), pa_yes_no(suid_root)); + + if (!real_root && pa_have_caps()) { + pa_bool_t allow_high_priority = FALSE, allow_realtime = FALSE; - /* Ok, we're suid root, so let's better not enable high prio - * or RT by default */ + /* Let's better not enable high prio or RT by default */ - allow_high_priority = allow_realtime = FALSE; + if (conf->high_priority && !allow_high_priority) { + if (pa_own_uid_in_group(PA_REALTIME_GROUP, &gid) > 0) { + pa_log_info("We're in the group '"PA_REALTIME_GROUP"', allowing high-priority scheduling."); + allow_high_priority = TRUE; + } + } - if (conf->high_priority || conf->realtime_scheduling) + if (conf->realtime_scheduling && !allow_realtime) { 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; + pa_log_info("We're in the group '"PA_REALTIME_GROUP"', allowing real-time scheduling."); + allow_realtime = TRUE; } + } #ifdef HAVE_POLKIT if (conf->high_priority && !allow_high_priority) { @@ -455,7 +471,6 @@ int main(int argc, char *argv[]) { * let's give it up early */ pa_drop_caps(); - 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" @@ -478,13 +493,13 @@ int main(int argc, char *argv[]) { 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) + if (conf->high_priority && (conf->cmd == PA_CMD_DAEMON || conf->cmd == PA_CMD_START)) pa_raise_priority(conf->nice_level); - if (suid_root) { + if (pa_have_caps()) { pa_bool_t drop; - drop = conf->cmd != PA_CMD_DAEMON || !conf->realtime_scheduling; + drop = (conf->cmd != PA_CMD_DAEMON && conf->cmd != PA_CMD_START) || !conf->realtime_scheduling; #ifdef RLIMIT_RTPRIO if (!drop) { @@ -520,6 +535,8 @@ int main(int argc, char *argv[]) { if (conf->realtime_scheduling && !pa_can_realtime()) pa_log_warn("Real-time scheduling enabled in configuration but not allowed by policy."); + pa_log_debug("Can realtime: %s, can high-priority: %s", pa_yes_no(pa_can_realtime()), pa_yes_no(pa_can_high_priority())); + LTDL_SET_PRELOADED_SYMBOLS(); pa_ltdl_init(); ltdl_init = TRUE; @@ -600,7 +617,7 @@ int main(int argc, char *argv[]) { goto finish; default: - pa_assert(conf->cmd == PA_CMD_DAEMON); + pa_assert(conf->cmd == PA_CMD_DAEMON || conf->cmd == PA_CMD_START); } if (real_root && !conf->system_instance) @@ -610,6 +627,15 @@ int main(int argc, char *argv[]) { goto finish; } + if (conf->cmd == PA_CMD_START) { + /* If we shall start PA only when it is not running yet, we + * first take the autospawn lock to make things + * synchronous. */ + + lf = pa_runtime_path(AUTOSPAWN_LOCK); + autospawn_lock_fd = pa_lock_lockfile(lf); + } + if (conf->daemonize) { pid_t child; int tty_fd; @@ -653,6 +679,14 @@ int main(int argc, char *argv[]) { goto finish; } + if (autospawn_lock_fd >= 0) { + /* The lock file is unlocked from the parent, so we need + * to close it in the child */ + + pa_close(autospawn_lock_fd); + autospawn_lock_fd = -1; + } + pa_assert_se(pa_close(daemon_pipe[0]) == 0); daemon_pipe[0] = -1; #endif @@ -705,13 +739,35 @@ int main(int argc, char *argv[]) { if (change_user() < 0) goto finish; + pa_set_env("PULSE_SYSTEM", conf->system_instance ? "1" : "0"); + 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()); + if (!(s = pa_get_runtime_dir())) + goto finish; + pa_log_info("Using runtime directory %s.", s); pa_xfree(s); + if (!(s = pa_get_state_dir())) + pa_log_info("Using state directory %s.", s); + pa_xfree(s); + + pa_log_info("Running in system mode: %s", pa_yes_no(pa_in_system_mode())); if (conf->use_pid_file) { - if (pa_pid_file_create() < 0) { + int z; + + if ((z = pa_pid_file_create("pulseaudio")) != 0) { + + if (conf->cmd == PA_CMD_START && z > 0) { + /* If we are already running and with are run in + * --start mode, then let's return this as success. */ + + pa_log_info("z=%i rock!", z); + + retval = 0; + goto finish; + } + pa_log("pa_pid_file_create() failed."); goto finish; } @@ -740,7 +796,6 @@ int main(int argc, char *argv[]) { goto finish; } - c->is_system_instance = !!conf->system_instance; c->default_sample_spec = conf->default_sample_spec; c->default_n_fragments = conf->default_n_fragments; c->default_fragment_size_msec = conf->default_fragment_size_msec; @@ -810,11 +865,12 @@ int main(int argc, char *argv[]) { goto finish; } - #ifdef HAVE_FORK - if (conf->daemonize) { + if (daemon_pipe[1] >= 0) { int ok = 0; pa_loop_write(daemon_pipe[1], &ok, sizeof(ok), NULL); + pa_close(daemon_pipe[1]); + daemon_pipe[1] = -1; } #endif @@ -828,6 +884,12 @@ int main(int argc, char *argv[]) { finish: + if (autospawn_lock_fd >= 0) + pa_unlock_lockfile(lf, autospawn_lock_fd); + + if (lf) + pa_xfree(lf); + #ifdef OS_IS_WIN32 if (win32_timer) pa_mainloop_get_api(mainloop)->time_free(win32_timer); @@ -844,6 +906,9 @@ finish: pa_signal_done(); #ifdef HAVE_FORK + if (daemon_pipe[1] >= 0) + pa_loop_write(daemon_pipe[1], &retval, sizeof(retval), NULL); + pa_close_pipe(daemon_pipe); #endif diff --git a/src/daemon/org.pulseaudio.policy b/src/daemon/org.pulseaudio.policy index 507a2cb1..6cdeec68 100644 --- a/src/daemon/org.pulseaudio.policy +++ b/src/daemon/org.pulseaudio.policy @@ -3,8 +3,6 @@ "-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN" "http://www.freedesktop.org/standards/PolicyKit/1.0/policyconfig.dtd"> -<!-- $Id$ --> - <!-- This file is part of PulseAudio. diff --git a/src/daemon/polkit.c b/src/daemon/polkit.c index ce7c83e0..256e3199 100644 --- a/src/daemon/polkit.c +++ b/src/daemon/polkit.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/daemon/polkit.h b/src/daemon/polkit.h index cbcf6a6a..0d65ec52 100644 --- a/src/daemon/polkit.h +++ b/src/daemon/polkit.h @@ -1,8 +1,6 @@ #ifndef foopolkithfoo #define foopolkithfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/depmod.py b/src/depmod.py index a20bc7c0..6cb3cb21 100755 --- a/src/depmod.py +++ b/src/depmod.py @@ -1,6 +1,5 @@ #!/usr/bin/python -# $Id$ -# + # This file is part of PulseAudio. # # PulseAudio is free software; you can redistribute it and/or modify diff --git a/src/map-file b/src/map-file index d9189743..8d1c582e 100644 --- a/src/map-file +++ b/src/map-file @@ -12,6 +12,7 @@ pa_bytes_to_usec; pa_channel_map_equal; pa_channel_map_init; pa_channel_map_init_auto; +pa_channel_map_init_extend; pa_channel_map_init_mono; pa_channel_map_init_stereo; pa_channel_map_parse; @@ -180,6 +181,7 @@ pa_stream_get_device_index; pa_stream_get_device_name; pa_stream_get_index; pa_stream_get_latency; +pa_stream_get_monitor_stream; pa_stream_get_sample_spec; pa_stream_get_state; pa_stream_get_time; @@ -197,6 +199,7 @@ pa_stream_ref; pa_stream_set_buffer_attr; pa_stream_set_latency_update_callback; pa_stream_set_moved_callback; +pa_stream_set_monitor_stream; pa_stream_set_name; pa_stream_set_overflow_callback; pa_stream_set_read_callback; diff --git a/src/modules/.gitignore b/src/modules/.gitignore new file mode 100644 index 00000000..2d2d942d --- /dev/null +++ b/src/modules/.gitignore @@ -0,0 +1 @@ +module-*-symdef.h diff --git a/src/modules/alsa-util.c b/src/modules/alsa-util.c index d212abc2..5d52cbc9 100644 --- a/src/modules/alsa-util.c +++ b/src/modules/alsa-util.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -670,26 +668,8 @@ snd_pcm_t *pa_alsa_open_by_device_string( *dev = d; - if (ss->channels != map->channels) { - 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; - } - } + if (ss->channels != map->channels) + pa_channel_map_init_extend(map, ss->channels, PA_CHANNEL_MAP_ALSA); return pcm_handle; } diff --git a/src/modules/alsa-util.h b/src/modules/alsa-util.h index 442c2645..4de8bcd2 100644 --- a/src/modules/alsa-util.h +++ b/src/modules/alsa-util.h @@ -1,8 +1,6 @@ #ifndef fooalsautilhfoo #define fooalsautilhfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/modules/bt-proximity-helper.c b/src/modules/bt-proximity-helper.c index 5f042c37..3767f01c 100644 --- a/src/modules/bt-proximity-helper.c +++ b/src/modules/bt-proximity-helper.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /* * Small SUID helper that allows us to ping a BT device. Borrows * heavily from bluez-utils' l2ping, which is licensed as GPL2+ diff --git a/src/modules/dbus-util.c b/src/modules/dbus-util.c index fc1e91ea..905be13f 100644 --- a/src/modules/dbus-util.c +++ b/src/modules/dbus-util.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/modules/dbus-util.h b/src/modules/dbus-util.h index 8dca54fe..2b24ac63 100644 --- a/src/modules/dbus-util.h +++ b/src/modules/dbus-util.h @@ -1,8 +1,6 @@ #ifndef foodbusutilhfoo #define foodbusutilhfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/modules/gconf/gconf-helper.c b/src/modules/gconf/gconf-helper.c index abd13287..f5016faf 100644 --- a/src/modules/gconf/gconf-helper.c +++ b/src/modules/gconf/gconf-helper.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/modules/gconf/module-gconf.c b/src/modules/gconf/module-gconf.c index 836157d0..a2a43278 100644 --- a/src/modules/gconf/module-gconf.c +++ b/src/modules/gconf/module-gconf.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/modules/module-alsa-sink.c b/src/modules/module-alsa-sink.c index 95a72fdc..6765775a 100644 --- a/src/modules/module-alsa-sink.c +++ b/src/modules/module-alsa-sink.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -277,7 +275,7 @@ static int mmap_write(struct userdata *u, pa_usec_t *sleep_usec) { * need to guarantee that clients only have to keep around * a single hw buffer length. */ - if (pa_bytes_to_usec(left_to_play, &u->sink->sample_spec) > max_sleep_usec/2) + if (pa_bytes_to_usec(left_to_play, &u->sink->sample_spec) > process_usec+max_sleep_usec/2) break; if (PA_UNLIKELY(n <= u->hwbuf_unused_frames)) @@ -389,7 +387,7 @@ static int unix_write(struct userdata *u, pa_usec_t *sleep_usec) { * need to guarantee that clients only have to keep around * a single hw buffer length. */ - if (pa_bytes_to_usec(left_to_play, &u->sink->sample_spec) > max_sleep_usec/2) + if (pa_bytes_to_usec(left_to_play, &u->sink->sample_spec) > process_usec+max_sleep_usec/2) break; if (PA_UNLIKELY(n <= u->hwbuf_unused_frames)) @@ -602,6 +600,8 @@ static int update_sw_params(struct userdata *u) { return err; } + pa_sink_set_max_request(u->sink, u->hwbuf_size - u->hwbuf_unused_frames * u->frame_size); + return 0; } @@ -1318,9 +1318,11 @@ int pa__init(pa_module*m) { fix_tsched_watermark(u); u->sink->thread_info.max_rewind = use_tsched ? u->hwbuf_size : 0; - u->sink->max_latency = pa_bytes_to_usec(u->hwbuf_size, &ss); - if (!use_tsched) - u->sink->min_latency = u->sink->max_latency; + u->sink->thread_info.max_request = u->hwbuf_size; + + pa_sink_set_latency_range(u->sink, + !use_tsched ? pa_bytes_to_usec(u->hwbuf_size, &ss) : (pa_usec_t) -1, + pa_bytes_to_usec(u->hwbuf_size, &ss)); pa_log_info("Using %u fragments of size %lu bytes, buffer time is %0.2fms", nfrags, (long unsigned) u->fragment_size, diff --git a/src/modules/module-alsa-source.c b/src/modules/module-alsa-source.c index e3090109..1cc467d9 100644 --- a/src/modules/module-alsa-source.c +++ b/src/modules/module-alsa-source.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -262,7 +260,7 @@ static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec) { left_to_record = check_left_to_record(u, n); if (u->use_tsched) - if (pa_bytes_to_usec(left_to_record, &u->source->sample_spec) > max_sleep_usec/2) + if (pa_bytes_to_usec(left_to_record, &u->source->sample_spec) > process_usec+max_sleep_usec/2) break; if (PA_UNLIKELY(n <= 0)) @@ -359,7 +357,7 @@ static int unix_read(struct userdata *u, pa_usec_t *sleep_usec) { left_to_record = check_left_to_record(u, n); if (u->use_tsched) - if (pa_bytes_to_usec(left_to_record, &u->source->sample_spec) > max_sleep_usec/2) + if (pa_bytes_to_usec(left_to_record, &u->source->sample_spec) > process_usec+max_sleep_usec/2) break; if (PA_UNLIKELY(n <= 0)) @@ -1152,9 +1150,9 @@ int pa__init(pa_module*m) { if (use_tsched) fix_tsched_watermark(u); - u->source->max_latency = pa_bytes_to_usec(u->hwbuf_size, &ss); - if (!use_tsched) - u->source->min_latency = u->source->max_latency; + pa_source_set_latency_range(u->source, + !use_tsched ? pa_bytes_to_usec(u->hwbuf_size, &ss) : (pa_usec_t) -1, + pa_bytes_to_usec(u->hwbuf_size, &ss)); pa_log_info("Using %u fragments of size %lu bytes, buffer time is %0.2fms", nfrags, (long unsigned) u->fragment_size, diff --git a/src/modules/module-always-sink.c b/src/modules/module-always-sink.c new file mode 100644 index 00000000..8b67a36d --- /dev/null +++ b/src/modules/module-always-sink.c @@ -0,0 +1,178 @@ +/*** + This file is part of PulseAudio. + + Copyright 2008 Colin Guthrie + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <pulse/xmalloc.h> + +#include <pulsecore/core.h> +#include <pulsecore/sink-input.h> +#include <pulsecore/modargs.h> +#include <pulsecore/log.h> +#include <pulsecore/namereg.h> +#include <pulsecore/core-util.h> + +#include "module-always-sink-symdef.h" + +PA_MODULE_AUTHOR("Colin Guthrie"); +PA_MODULE_DESCRIPTION("Always keeps at least one sink loaded even if it's a null one"); +PA_MODULE_VERSION(PACKAGE_VERSION); +PA_MODULE_LOAD_ONCE(TRUE); +PA_MODULE_USAGE( + "sink_name=<name of sink>"); + +#define DEFAULT_SINK_NAME "auto_null" + +static const char* const valid_modargs[] = { + "sink_name", + NULL, +}; + +struct userdata { + pa_hook_slot *put_slot, *unlink_slot; + pa_module* null_module; + pa_bool_t ignore; + char *sink_name; +}; + +static void load_null_sink_if_needed(pa_core *c, pa_sink *sink, struct userdata* u) { + pa_sink *target; + uint32_t idx; + char *t; + + pa_assert(c); + pa_assert(u); + pa_assert(!u->null_module); + + /* Loop through all sinks and check to see if we have *any* + * sinks. Ignore the sink passed in (if it's not null) */ + for (target = pa_idxset_first(c->sinks, &idx); target; target = pa_idxset_next(c->sinks, &idx)) + if (!sink || target != sink) + break; + + if (target) + return; + + pa_log_debug("Autoloading null-sink as no other sinks detected."); + + u->ignore = TRUE; + + t = pa_sprintf_malloc("sink_name=%s", u->sink_name); + u->null_module = pa_module_load(c, "module-null-sink", t); + pa_xfree(t); + + u->ignore = FALSE; + + if (!u->null_module) + pa_log_warn("Unable to load module-null-sink"); +} + +static pa_hook_result_t put_hook_callback(pa_core *c, pa_sink *sink, void* userdata) { + struct userdata *u = userdata; + + pa_assert(c); + pa_assert(sink); + pa_assert(u); + + /* This is us detecting ourselves on load... just ignore this. */ + if (u->ignore) + return PA_HOOK_OK; + + /* Auto-loaded null-sink not active, so ignoring newly detected sink. */ + if (!u->null_module) + return PA_HOOK_OK; + + /* This is us detecting ourselves on load in a different way... just ignore this too. */ + if (sink->module == u->null_module) + return PA_HOOK_OK; + + pa_log_info("A new sink has been discovered. Unloading null-sink."); + + pa_module_unload_request(u->null_module); + u->null_module = NULL; + + return PA_HOOK_OK; +} + +static pa_hook_result_t unlink_hook_callback(pa_core *c, pa_sink *sink, void* userdata) { + struct userdata *u = userdata; + + pa_assert(c); + pa_assert(sink); + pa_assert(u); + + /* First check to see if it's our own null-sink that's been removed... */ + if (u->null_module && sink->module == u->null_module) { + pa_log_debug("Autoloaded null-sink removed"); + u->null_module = NULL; + return PA_HOOK_OK; + } + + load_null_sink_if_needed(c, sink, u); + + return PA_HOOK_OK; +} + +int pa__init(pa_module*m) { + pa_modargs *ma = NULL; + struct userdata *u; + + pa_assert(m); + + if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { + pa_log("Failed to parse module arguments"); + return -1; + } + + m->userdata = u = pa_xnew(struct userdata, 1); + u->sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME)); + u->put_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PUT], PA_HOOK_LATE, (pa_hook_cb_t) put_hook_callback, u); + u->unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_UNLINK], PA_HOOK_EARLY, (pa_hook_cb_t) unlink_hook_callback, u); + u->null_module = NULL; + u->ignore = FALSE; + + pa_modargs_free(ma); + + load_null_sink_if_needed(m->core, NULL, u); + + return 0; +} + +void pa__done(pa_module*m) { + struct userdata *u; + + pa_assert(m); + + if (!(u = m->userdata)) + return; + + if (u->put_slot) + pa_hook_slot_free(u->put_slot); + if (u->unlink_slot) + pa_hook_slot_free(u->unlink_slot); + if (u->null_module) + pa_module_unload_request(u->null_module); + + pa_xfree(u->sink_name); + pa_xfree(u); +} diff --git a/src/modules/module-bt-proximity.c b/src/modules/module-bt-proximity.c index 62d530d4..77b95868 100644 --- a/src/modules/module-bt-proximity.c +++ b/src/modules/module-bt-proximity.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/modules/module-cli.c b/src/modules/module-cli.c index ab311a82..df7783fa 100644 --- a/src/modules/module-cli.c +++ b/src/modules/module-cli.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/modules/module-combine.c b/src/modules/module-combine.c index fc8be18d..cef7a99a 100644 --- a/src/modules/module-combine.c +++ b/src/modules/module-combine.c @@ -1,9 +1,7 @@ -/* $Id$ */ - /*** 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 @@ -47,6 +45,7 @@ #include <pulsecore/rtpoll.h> #include <pulsecore/rtclock.h> #include <pulsecore/core-error.h> +#include <pulsecore/time-smoother.h> #include "module-combine-symdef.h" @@ -56,7 +55,6 @@ PA_MODULE_VERSION(PACKAGE_VERSION); PA_MODULE_LOAD_ONCE(FALSE); PA_MODULE_USAGE( "sink_name=<name for the sink> " - "master=<master sink> " "slaves=<slave sinks> " "adjust_time=<seconds> " "resample_method=<method> " @@ -66,13 +64,15 @@ PA_MODULE_USAGE( "channel_map=<channel map>"); #define DEFAULT_SINK_NAME "combined" + #define MEMBLOCKQ_MAXLENGTH (1024*1024*16) #define DEFAULT_ADJUST_TIME 10 +#define REQUEST_LATENCY_USEC (PA_USEC_PER_MSEC * 200) + static const char* const valid_modargs[] = { "sink_name", - "master", "slaves", "adjust_time", "resample_method", @@ -91,12 +91,15 @@ struct output { pa_asyncmsgq *inq, /* Message queue from the sink thread to this sink input */ *outq; /* Message queue from this sink input to the sink thread */ - pa_rtpoll_item *inq_rtpoll_item, *outq_rtpoll_item; + pa_rtpoll_item *inq_rtpoll_item_read, *inq_rtpoll_item_write; + pa_rtpoll_item *outq_rtpoll_item_read, *outq_rtpoll_item_write; pa_memblockq *memblockq; pa_usec_t total_latency; + pa_atomic_t max_request; + PA_LLIST_FIELDS(struct output); }; @@ -113,29 +116,33 @@ struct userdata { uint32_t adjust_time; pa_bool_t automatic; - size_t block_size; - pa_hook_slot *sink_new_slot, *sink_unlink_slot, *sink_state_changed_slot; + pa_hook_slot *sink_put_slot, *sink_unlink_slot, *sink_state_changed_slot; pa_resample_method_t resample_method; struct timeval adjust_timestamp; - struct output *master; + pa_usec_t block_usec; + pa_idxset* outputs; /* managed in main context */ struct { PA_LLIST_HEAD(struct output, active_outputs); /* managed in IO thread context */ pa_atomic_t running; /* we cache that value here, so that every thread can query it cheaply */ - struct timeval timestamp; + pa_usec_t timestamp; pa_bool_t in_null_mode; + pa_smoother *smoother; + uint64_t counter; } thread_info; }; enum { SINK_MESSAGE_ADD_OUTPUT = PA_SINK_MESSAGE_MAX, SINK_MESSAGE_REMOVE_OUTPUT, - SINK_MESSAGE_NEED + SINK_MESSAGE_NEED, + SINK_MESSAGE_UPDATE_LATENCY, + SINK_MESSAGE_UPDATE_MAX_REQUEST }; enum { @@ -144,14 +151,13 @@ enum { static void output_free(struct output *o); static int output_create_sink_input(struct output *o); -static void update_master(struct userdata *u, struct output *o); -static void pick_master(struct userdata *u, struct output *except); static void adjust_rates(struct userdata *u) { struct output *o; - pa_usec_t max_sink_latency = 0, min_total_latency = (pa_usec_t) -1, target_latency; + pa_usec_t max_sink_latency = 0, min_total_latency = (pa_usec_t) -1, target_latency, avg_total_latency = 0; uint32_t base_rate; uint32_t idx; + unsigned n = 0; pa_assert(u); pa_sink_assert_ref(u->sink); @@ -159,9 +165,6 @@ static void adjust_rates(struct userdata *u) { if (pa_idxset_size(u->outputs) <= 0) return; - if (!u->master) - return; - if (!PA_SINK_IS_OPENED(pa_sink_get_state(u->sink))) return; @@ -171,23 +174,28 @@ static void adjust_rates(struct userdata *u) { if (!o->sink_input || !PA_SINK_IS_OPENED(pa_sink_get_state(o->sink))) continue; - sink_latency = pa_sink_get_latency(o->sink); - o->total_latency = sink_latency + pa_sink_input_get_latency(o->sink_input); + o->total_latency = pa_sink_input_get_latency(o->sink_input, &sink_latency); + o->total_latency += sink_latency; if (sink_latency > max_sink_latency) max_sink_latency = sink_latency; if (min_total_latency == (pa_usec_t) -1 || o->total_latency < min_total_latency) min_total_latency = o->total_latency; + + avg_total_latency += o->total_latency; + n++; } if (min_total_latency == (pa_usec_t) -1) return; + avg_total_latency /= n; + target_latency = max_sink_latency > min_total_latency ? max_sink_latency : min_total_latency; - pa_log_info("[%s] target latency is %0.0f usec.", u->sink->name, (float) target_latency); - pa_log_info("[%s] master %s latency %0.0f usec.", u->sink->name, u->master->sink->name, (float) u->master->total_latency); + pa_log_info("[%s] avg total latency is %0.2f msec.", u->sink->name, (double) avg_total_latency / PA_USEC_PER_MSEC); + pa_log_info("[%s] target latency is %0.2f msec.", u->sink->name, (double) target_latency / PA_USEC_PER_MSEC); base_rate = u->sink->sample_spec.rate; @@ -210,6 +218,8 @@ static void adjust_rates(struct userdata *u) { pa_sink_input_set_rate(o->sink_input, r); } } + + pa_asyncmsgq_send(u->sink->asyncmsgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_UPDATE_LATENCY, NULL, (int64_t) avg_total_latency, NULL); } static void time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *tv, void *userdata) { @@ -227,6 +237,36 @@ static void time_callback(pa_mainloop_api*a, pa_time_event* e, const struct time u->sink->core->mainloop->time_restart(e, &n); } +static void process_render_null(struct userdata *u, pa_usec_t now) { + size_t ate = 0; + pa_assert(u); + + if (u->thread_info.in_null_mode) + u->thread_info.timestamp = now; + + while (u->thread_info.timestamp < now + u->block_usec) { + pa_memchunk chunk; + + pa_sink_render(u->sink, u->sink->thread_info.max_request, &chunk); + pa_memblock_unref(chunk.memblock); + + u->thread_info.counter += chunk.length; + +/* pa_log_debug("Ate %lu bytes.", (unsigned long) chunk.length); */ + u->thread_info.timestamp += pa_bytes_to_usec(chunk.length, &u->sink->sample_spec); + + ate += chunk.length; + + if (ate >= u->sink->thread_info.max_request) + break; + } + +/* pa_log_debug("Ate in sum %lu bytes (of %lu)", (unsigned long) ate, (unsigned long) nbytes); */ + + pa_smoother_put(u->thread_info.smoother, now, + pa_bytes_to_usec(u->thread_info.counter, &u->sink->sample_spec) - (u->thread_info.timestamp - now)); +} + static void thread_func(void *userdata) { struct userdata *u = userdata; @@ -240,38 +280,23 @@ static void thread_func(void *userdata) { pa_thread_mq_install(&u->thread_mq); pa_rtpoll_install(u->rtpoll); - pa_rtclock_get(&u->thread_info.timestamp); + u->thread_info.timestamp = pa_rtclock_usec(); u->thread_info.in_null_mode = FALSE; for (;;) { int ret; /* If no outputs are connected, render some data and drop it immediately. */ - 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_memchunk chunk; + if (PA_SINK_IS_OPENED(u->sink->thread_info.state) && !u->thread_info.active_outputs) { + pa_usec_t now; - pa_sink_render_full(u->sink, u->block_size, &chunk); - pa_memblock_unref(chunk.memblock); + now = pa_rtclock_usec(); - if (!u->thread_info.in_null_mode) - u->thread_info.timestamp = now; + if (!u->thread_info.in_null_mode || u->thread_info.timestamp <= now) + process_render_null(u, now); - pa_timeval_add(&u->thread_info.timestamp, pa_bytes_to_usec(u->block_size, &u->sink->sample_spec)); - } - - pa_rtpoll_set_timer_absolute(u->rtpoll, &u->thread_info.timestamp); + pa_rtpoll_set_timer_absolute(u->rtpoll, u->thread_info.timestamp); u->thread_info.in_null_mode = TRUE; - } else { pa_rtpoll_set_timer_disabled(u->rtpoll); u->thread_info.in_null_mode = FALSE; @@ -303,7 +328,7 @@ static void render_memblock(struct userdata *u, struct output *o, size_t length) pa_assert(o); /* We are run by the sink thread, on behalf of an output (o). The - * other output is waiting for us, hence it is safe to access its + * output is waiting for us, hence it is safe to access its * mainblockq and asyncmsgq directly. */ /* If we are not running, we cannot produce any data */ @@ -323,6 +348,8 @@ static void render_memblock(struct userdata *u, struct output *o, size_t length) /* Render data! */ pa_sink_render(u->sink, length, &chunk); + u->thread_info.counter += chunk.length; + /* OK, let's send this data to the other threads */ for (j = u->thread_info.active_outputs; j; j = j->next) @@ -379,6 +406,42 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk } /* Called from I/O thread context */ +static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) { + struct output *o; + + pa_sink_input_assert_ref(i); + pa_assert(nbytes > 0); + pa_assert_se(o = i->userdata); + + pa_memblockq_rewind(o->memblockq, nbytes); +} + +/* Called from I/O thread context */ +static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) { + struct output *o; + + pa_sink_input_assert_ref(i); + pa_assert_se(o = i->userdata); + + pa_memblockq_set_maxrewind(o->memblockq, nbytes); +} + +/* Called from I/O thread context */ +static void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes) { + struct output *o; + + pa_sink_input_assert_ref(i); + pa_assert_se(o = i->userdata); + + if (pa_atomic_load(&o->max_request) == (int) nbytes) + return; + + pa_atomic_store(&o->max_request, (int) nbytes); + + pa_asyncmsgq_post(o->outq, PA_MSGOBJECT(o->userdata->sink), SINK_MESSAGE_UPDATE_MAX_REQUEST, NULL, 0, NULL, NULL); +} + +/* Called from I/O thread context */ static void sink_input_attach_cb(pa_sink_input *i) { struct output *o; @@ -386,11 +449,17 @@ static void sink_input_attach_cb(pa_sink_input *i) { pa_assert_se(o = i->userdata); /* 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_read( + pa_assert(!o->inq_rtpoll_item_read && !o->outq_rtpoll_item_write); + + o->inq_rtpoll_item_read = 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); + + o->outq_rtpoll_item_write = pa_rtpoll_item_new_asyncmsgq_write( + i->sink->rtpoll, + PA_RTPOLL_EARLY, + o->outq); } /* Called from I/O thread context */ @@ -401,9 +470,13 @@ static void sink_input_detach_cb(pa_sink_input *i) { pa_assert_se(o = i->userdata); /* Shut down the queue from the sink thread to us */ - pa_assert(o->inq_rtpoll_item); - pa_rtpoll_item_free(o->inq_rtpoll_item); - o->inq_rtpoll_item = NULL; + pa_assert(o->inq_rtpoll_item_read && o->outq_rtpoll_item_write); + + pa_rtpoll_item_free(o->inq_rtpoll_item_read); + o->inq_rtpoll_item_read = NULL; + + pa_rtpoll_item_free(o->outq_rtpoll_item_write); + o->outq_rtpoll_item_write = NULL; } /* Called from main context */ @@ -417,6 +490,20 @@ static void sink_input_kill_cb(pa_sink_input *i) { output_free(o); } +/* 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); +} + /* Called from thread context */ static int sink_input_process_msg(pa_msgobject *obj, int code, void *data, int64_t offset, pa_memchunk *chunk) { struct output *o = PA_SINK_INPUT(obj)->userdata; @@ -440,8 +527,7 @@ static int sink_input_process_msg(pa_msgobject *obj, int code, void *data, int64 else pa_memblockq_flush(o->memblockq); - break; - + return 0; } return pa_sink_input_process_msg(obj, code, data, offset, chunk); @@ -454,11 +540,10 @@ static void disable_output(struct output *o) { if (!o->sink_input) return; - pa_asyncmsgq_send(o->userdata->sink->asyncmsgq, PA_MSGOBJECT(o->userdata->sink), SINK_MESSAGE_REMOVE_OUTPUT, o, 0, NULL); pa_sink_input_unlink(o->sink_input); + pa_asyncmsgq_send(o->userdata->sink->asyncmsgq, PA_MSGOBJECT(o->userdata->sink), SINK_MESSAGE_REMOVE_OUTPUT, o, 0, NULL); pa_sink_input_unref(o->sink_input); o->sink_input = NULL; - } /* Called from main context */ @@ -490,8 +575,6 @@ static void suspend(struct userdata *u) { for (o = pa_idxset_first(u->outputs, &idx); o; o = pa_idxset_next(u->outputs, &idx)) disable_output(o); - pick_master(u, NULL); - pa_log_info("Device suspended..."); } @@ -511,8 +594,6 @@ static void unsuspend(struct userdata *u) { enable_output(o); } - pick_master(u, NULL); - pa_log_info("Resumed successfully..."); } @@ -549,7 +630,25 @@ static int sink_set_state(pa_sink *sink, pa_sink_state_t state) { return 0; } -/* Called from thread context of the master */ +/* Called from IO context */ +static void update_max_request(struct userdata *u) { + size_t max_request = 0; + struct output *o; + + for (o = u->thread_info.active_outputs; o; o = o->next) { + size_t mr = (size_t) pa_atomic_load(&o->max_request); + + if (mr > max_request) + max_request = mr; + } + + if (max_request <= 0) + max_request = pa_usec_to_bytes(u->block_usec, &u->sink->sample_spec); + + pa_sink_set_max_request(u->sink, max_request); +} + +/* Called from thread context of the io thread */ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) { struct userdata *u = PA_SINK(o)->userdata; @@ -557,41 +656,47 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse case PA_SINK_MESSAGE_SET_STATE: pa_atomic_store(&u->thread_info.running, PA_PTR_TO_UINT(data) == PA_SINK_RUNNING); - break; - case PA_SINK_MESSAGE_GET_LATENCY: + if (PA_PTR_TO_UINT(data) == PA_SINK_SUSPENDED) + pa_smoother_pause(u->thread_info.smoother, pa_rtclock_usec()); + else + pa_smoother_resume(u->thread_info.smoother, pa_rtclock_usec()); - /* This code will only be called when running in NULL - * mode, i.e. when no output is attached. See - * sink_get_latency_cb() below */ + break; - if (u->thread_info.in_null_mode) { - struct timeval now; + case PA_SINK_MESSAGE_GET_LATENCY: { + pa_usec_t x, y, c, *delay = data; - if (pa_timeval_cmp(&u->thread_info.timestamp, pa_rtclock_get(&now)) > 0) { - *((pa_usec_t*) data) = pa_timeval_diff(&u->thread_info.timestamp, &now); - break; - } - } + x = pa_rtclock_usec(); + y = pa_smoother_get(u->thread_info.smoother, x); - *((pa_usec_t*) data) = 0; + c = pa_bytes_to_usec(u->thread_info.counter, &u->sink->sample_spec); - break; + if (y < c) + *delay = c - y; + else + *delay = 0; + + return 0; + } case SINK_MESSAGE_ADD_OUTPUT: { struct output *op = data; PA_LLIST_PREPEND(struct output, u->thread_info.active_outputs, op); - pa_assert(!op->outq_rtpoll_item); - - /* Create pa_asyncmsgq to the sink thread */ + pa_assert(!op->outq_rtpoll_item_read && !op->inq_rtpoll_item_write); - op->outq_rtpoll_item = pa_rtpoll_item_new_asyncmsgq_read( + op->outq_rtpoll_item_read = pa_rtpoll_item_new_asyncmsgq_read( u->rtpoll, PA_RTPOLL_EARLY-1, /* This item is very important */ op->outq); + op->inq_rtpoll_item_write = pa_rtpoll_item_new_asyncmsgq_write( + u->rtpoll, + PA_RTPOLL_EARLY, + op->inq); + update_max_request(u); return 0; } @@ -600,56 +705,48 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse PA_LLIST_REMOVE(struct output, u->thread_info.active_outputs, op); - /* Remove the q that leads from this output to the sink thread */ + pa_assert(op->outq_rtpoll_item_read && op->inq_rtpoll_item_write); + + pa_rtpoll_item_free(op->outq_rtpoll_item_read); + op->outq_rtpoll_item_read = NULL; - pa_assert(op->outq_rtpoll_item); - pa_rtpoll_item_free(op->outq_rtpoll_item); - op->outq_rtpoll_item = NULL; + pa_rtpoll_item_free(op->inq_rtpoll_item_write); + op->inq_rtpoll_item_write = NULL; + update_max_request(u); return 0; } case SINK_MESSAGE_NEED: - render_memblock(u, data, (size_t) offset); + render_memblock(u, (struct output*) data, (size_t) offset); return 0; - } - return pa_sink_process_msg(o, code, data, offset, chunk); -} - -/* Called from main context */ -/* static pa_usec_t sink_get_latency_cb(pa_sink *s) { */ -/* struct userdata *u; */ + case SINK_MESSAGE_UPDATE_LATENCY: { + pa_usec_t x, y, latency = (pa_usec_t) offset; -/* pa_sink_assert_ref(s); */ -/* pa_assert_se(u = s->userdata); */ + x = pa_rtclock_usec(); + y = pa_bytes_to_usec(u->thread_info.counter, &u->sink->sample_spec); -/* 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; */ - -/* return */ -/* pa_sink_input_get_latency(u->master->sink_input) + */ -/* pa_sink_get_latency(u->master->sink); */ + if (y > latency) + y -= latency; + else + y = 0; -/* } else { */ -/* pa_usec_t usec = 0; */ + pa_smoother_put(u->thread_info.smoother, x, y); + return 0; + } -/* /\* We have no master, hence let's ask our own thread which */ -/* * implements the NULL sink *\/ */ + case SINK_MESSAGE_UPDATE_MAX_REQUEST: -/* if (pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0) */ -/* return 0; */ + update_max_request(u); + break; + } -/* return usec; */ -/* } */ -/* } */ + return pa_sink_process_msg(o, code, data, offset, chunk); +} static void update_description(struct userdata *u) { - int first = 1; + pa_bool_t first = TRUE; char *t; struct output *o; uint32_t idx; @@ -668,7 +765,7 @@ static void update_description(struct userdata *u) { if (first) { e = pa_sprintf_malloc("%s %s", t, pa_strnull(pa_proplist_gets(o->sink->proplist, PA_PROP_DEVICE_DESCRIPTION))); - first = 0; + first = FALSE; } else e = pa_sprintf_malloc("%s, %s", t, pa_strnull(pa_proplist_gets(o->sink->proplist, PA_PROP_DEVICE_DESCRIPTION))); @@ -680,57 +777,19 @@ static void update_description(struct userdata *u) { pa_xfree(t); } -static void update_master(struct userdata *u, struct output *o) { - pa_assert(u); - - if (u->master == o) - return; - - if ((u->master = o)) - pa_log_info("Master sink is now '%s'", o->sink_input->sink->name); - else - pa_log_info("No master selected, lacking suitable outputs."); -} - -static void pick_master(struct userdata *u, struct output *except) { - struct output *o; - uint32_t idx; - pa_assert(u); - - if (u->master && - u->master != except && - u->master->sink_input && - PA_SINK_IS_OPENED(pa_sink_get_state(u->master->sink))) { - update_master(u, u->master); - return; - } - - for (o = pa_idxset_first(u->outputs, &idx); o; o = pa_idxset_next(u->outputs, &idx)) - if (o != except && - o->sink_input && - PA_SINK_IS_OPENED(pa_sink_get_state(o->sink))) { - update_master(u, o); - return; - } - - update_master(u, NULL); -} - static int output_create_sink_input(struct output *o) { pa_sink_input_new_data data; - char *t; pa_assert(o); if (o->sink_input) return 0; - 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__; - pa_proplist_sets(data.proplist, PA_PROP_DEVICE_DESCRIPTION, t); + pa_proplist_setf(data.proplist, PA_PROP_MEDIA_NAME, "Simultaneous output on %s", pa_strnull(pa_proplist_gets(o->sink->proplist, PA_PROP_DEVICE_DESCRIPTION))); + pa_proplist_sets(data.proplist, PA_PROP_MEDIA_ROLE, "filter"); 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; @@ -740,24 +799,28 @@ static int output_create_sink_input(struct output *o) { 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->pop = sink_input_pop_cb; + o->sink_input->process_rewind = sink_input_process_rewind_cb; + o->sink_input->state_change = sink_input_state_change_cb; + o->sink_input->update_max_rewind = sink_input_update_max_rewind_cb; + o->sink_input->update_max_request = sink_input_update_max_request_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; o->sink_input->userdata = o; + pa_sink_input_set_requested_latency(o->sink_input, REQUEST_LATENCY_USEC); return 0; } static struct output *output_new(struct userdata *u, pa_sink *sink) { struct output *o; + pa_sink_state_t state; pa_assert(u); pa_assert(sink); @@ -767,8 +830,8 @@ static struct output *output_new(struct userdata *u, pa_sink *sink) { o->userdata = u; o->inq = pa_asyncmsgq_new(0); o->outq = pa_asyncmsgq_new(0); - o->inq_rtpoll_item = NULL; - o->outq_rtpoll_item = NULL; + o->inq_rtpoll_item_write = o->inq_rtpoll_item_read = NULL; + o->outq_rtpoll_item_write = o->outq_rtpoll_item_read = NULL; o->sink = sink; o->sink_input = NULL; o->memblockq = pa_memblockq_new( @@ -780,22 +843,30 @@ static struct output *output_new(struct userdata *u, pa_sink *sink) { 0, 0, NULL); + pa_atomic_store(&o->max_request, 0); + PA_LLIST_INIT(struct output, o); pa_assert_se(pa_idxset_put(u->outputs, o, NULL) == 0); - if (u->sink && PA_SINK_IS_LINKED(pa_sink_get_state(u->sink))) + state = pa_sink_get_state(u->sink); + + if (state != PA_SINK_INIT) 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_read( + o->outq_rtpoll_item_read = pa_rtpoll_item_new_asyncmsgq_read( u->rtpoll, PA_RTPOLL_EARLY-1, /* This item is very important */ o->outq); + o->inq_rtpoll_item_write = pa_rtpoll_item_new_asyncmsgq_write( + u->rtpoll, + PA_RTPOLL_EARLY, + o->inq); } - if (PA_SINK_IS_OPENED(pa_sink_get_state(u->sink)) || pa_sink_get_state(u->sink) == PA_SINK_INIT) { + if (PA_SINK_IS_OPENED(state) || state == PA_SINK_INIT) { pa_sink_suspend(sink, FALSE); if (PA_SINK_IS_OPENED(pa_sink_get_state(sink))) @@ -803,7 +874,6 @@ static struct output *output_new(struct userdata *u, pa_sink *sink) { goto fail; } - update_description(u); return o; @@ -833,7 +903,25 @@ fail: return NULL; } -static pa_hook_result_t sink_new_hook_cb(pa_core *c, pa_sink *s, struct userdata* u) { +static pa_bool_t is_suitable_sink(struct userdata *u, pa_sink *s) { + const char *t; + + pa_sink_assert_ref(s); + + if (!(s->flags & PA_SINK_HARDWARE)) + return FALSE; + + if (s == u->sink) + return FALSE; + + if ((t = pa_proplist_gets(s->proplist, PA_PROP_DEVICE_CLASS))) + if (strcmp(t, "sound")) + return FALSE; + + return TRUE; +} + +static pa_hook_result_t sink_put_hook_cb(pa_core *c, pa_sink *s, struct userdata* u) { struct output *o; pa_core_assert_ref(c); @@ -841,7 +929,7 @@ static pa_hook_result_t sink_new_hook_cb(pa_core *c, pa_sink *s, struct userdata pa_assert(u); pa_assert(u->automatic); - if (!(s->flags & PA_SINK_HARDWARE) || s == u->sink) + if (!is_suitable_sink(u, s)) return PA_HOOK_OK; pa_log_info("Configuring new sink: %s", s->name); @@ -854,27 +942,34 @@ static pa_hook_result_t sink_new_hook_cb(pa_core *c, pa_sink *s, struct userdata if (o->sink_input) pa_sink_input_put(o->sink_input); - pick_master(u, NULL); - return PA_HOOK_OK; } -static pa_hook_result_t sink_unlink_hook_cb(pa_core *c, pa_sink *s, struct userdata* u) { +static struct output* find_output(struct userdata *u, pa_sink *s) { struct output *o; uint32_t idx; - pa_assert(c); - pa_sink_assert_ref(s); pa_assert(u); + pa_assert(s); - if (s == u->sink) - return PA_HOOK_OK; + if (u->sink == s) + return NULL; for (o = pa_idxset_first(u->outputs, &idx); o; o = pa_idxset_next(u->outputs, &idx)) if (o->sink == s) - break; + return o; - if (!o) + return NULL; +} + +static pa_hook_result_t sink_unlink_hook_cb(pa_core *c, pa_sink *s, struct userdata* u) { + struct output *o; + + pa_assert(c); + pa_sink_assert_ref(s); + pa_assert(u); + + if (!(o = find_output(u, s))) return PA_HOOK_OK; pa_log_info("Unconfiguring sink: %s", s->name); @@ -886,30 +981,18 @@ static pa_hook_result_t sink_unlink_hook_cb(pa_core *c, pa_sink *s, struct userd static pa_hook_result_t sink_state_changed_hook_cb(pa_core *c, pa_sink *s, struct userdata* u) { struct output *o; - uint32_t idx; pa_sink_state_t state; - if (s == u->sink) - return PA_HOOK_OK; - - for (o = pa_idxset_first(u->outputs, &idx); o; o = pa_idxset_next(u->outputs, &idx)) - if (o->sink == s) - break; - - if (!o) + if (!(o = find_output(u, s))) return PA_HOOK_OK; state = pa_sink_get_state(s); - if (PA_SINK_IS_OPENED(state) && PA_SINK_IS_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); - } - if (state == PA_SINK_SUSPENDED && o->sink_input) { + if (state == PA_SINK_SUSPENDED && o->sink_input) disable_output(o); - pick_master(u, o); - } return PA_HOOK_OK; } @@ -917,8 +1000,7 @@ static pa_hook_result_t sink_state_changed_hook_cb(pa_core *c, pa_sink *s, struc int pa__init(pa_module*m) { struct userdata *u; pa_modargs *ma = NULL; - const char *master_name, *slaves, *rm; - pa_sink *master_sink = NULL; + const char *slaves, *rm; int resample_method = PA_RESAMPLER_TRIVIAL; pa_sample_spec ss; pa_channel_map map; @@ -940,12 +1022,10 @@ int pa__init(pa_module*m) { } } - u = pa_xnew(struct userdata, 1); + m->userdata = u = pa_xnew(struct userdata, 1); u->core = m->core; u->module = m; - m->userdata = u; u->sink = NULL; - u->master = NULL; u->time_event = NULL; u->adjust_time = DEFAULT_ADJUST_TIME; u->rtpoll = pa_rtpoll_new(); @@ -954,67 +1034,39 @@ int pa__init(pa_module*m) { u->resample_method = resample_method; u->outputs = pa_idxset_new(NULL, NULL); memset(&u->adjust_timestamp, 0, sizeof(u->adjust_timestamp)); - u->sink_new_slot = u->sink_unlink_slot = u->sink_state_changed_slot = NULL; + u->sink_put_slot = u->sink_unlink_slot = u->sink_state_changed_slot = NULL; 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; + u->thread_info.counter = 0; + u->thread_info.smoother = pa_smoother_new(PA_USEC_PER_SEC, PA_USEC_PER_SEC*2, TRUE, 10); if (pa_modargs_get_value_u32(ma, "adjust_time", &u->adjust_time) < 0) { pa_log("Failed to parse adjust_time value"); goto fail; } - master_name = pa_modargs_get_value(ma, "master", NULL); slaves = pa_modargs_get_value(ma, "slaves", NULL); - if (!master_name != !slaves) { - pa_log("No master or slave sinks specified"); - goto fail; - } + u->automatic = !slaves; + ss = m->core->default_sample_spec; - if (master_name) { - if (!(master_sink = pa_namereg_get(m->core, master_name, PA_NAMEREG_SINK, 1))) { - pa_log("Invalid master sink '%s'", master_name); - goto fail; - } - - ss = master_sink->sample_spec; - u->automatic = FALSE; - } else { - master_sink = NULL; - ss = m->core->default_sample_spec; - u->automatic = TRUE; - } - - if ((pa_modargs_get_sample_spec(ma, &ss) < 0)) { + if ((pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0)) { pa_log("Invalid sample specification."); goto fail; } - if (master_sink && ss.channels == master_sink->sample_spec.channels) - map = master_sink->channel_map; - else { - 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_DEFAULT); - } - - if ((pa_modargs_get_channel_map(ma, NULL, &map) < 0)) { - pa_log("Invalid channel map."); - goto fail; - } - - if (ss.channels != map.channels) { - pa_log("Channel map and sample specification don't match."); - goto fail; - } - 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_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, "Simultaneous Output"); + pa_proplist_sets(data.proplist, PA_PROP_DEVICE_CLASS, "filter"); + + if (slaves) + pa_proplist_sets(data.proplist, "combine.slaves", slaves); u->sink = pa_sink_new(m->core, &data, PA_SINK_LATENCY); pa_sink_new_data_done(&data); @@ -1025,34 +1077,30 @@ int pa__init(pa_module*m) { } u->sink->parent.process_msg = sink_process_msg; -/* u->sink->get_latency = sink_get_latency_cb; */ u->sink->set_state = sink_set_state; u->sink->userdata = u; pa_sink_set_rtpoll(u->sink, u->rtpoll); pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq); - u->block_size = pa_bytes_per_second(&ss) / 20; /* 50 ms */ - if (u->block_size <= 0) - u->block_size = pa_frame_size(&ss); + pa_sink_set_latency_range(u->sink, REQUEST_LATENCY_USEC, REQUEST_LATENCY_USEC); + u->block_usec = u->sink->thread_info.max_latency; + + u->sink->thread_info.max_request = + pa_usec_to_bytes(u->block_usec, &u->sink->sample_spec); if (!u->automatic) { const char*split_state; char *n = NULL; pa_assert(slaves); - /* The master and slaves have been specified manually */ - - if (!(u->master = output_new(u, master_sink))) { - pa_log("Failed to create master sink input on sink '%s'.", master_sink->name); - goto fail; - } + /* The slaves have been specified manually */ split_state = NULL; while ((n = pa_split(slaves, ",", &split_state))) { pa_sink *slave_sink; - if (!(slave_sink = pa_namereg_get(m->core, n, PA_NAMEREG_SINK, 1)) || slave_sink == u->sink) { + if (!(slave_sink = pa_namereg_get(m->core, n, PA_NAMEREG_SINK, TRUE)) || slave_sink == u->sink) { pa_log("Invalid slave sink '%s'", n); pa_xfree(n); goto fail; @@ -1069,17 +1117,16 @@ int pa__init(pa_module*m) { if (pa_idxset_size(u->outputs) <= 1) pa_log_warn("No slave sinks specified."); - u->sink_new_slot = NULL; + u->sink_put_slot = NULL; } else { pa_sink *s; - /* We're in automatic mode, we elect one hw sink to the master - * and attach all other hw sinks as slaves to it */ + /* We're in automatic mode, we add every sink that matches our needs */ for (s = pa_idxset_first(m->core->sinks, &idx); s; s = pa_idxset_next(m->core->sinks, &idx)) { - if (!(s->flags & PA_SINK_HARDWARE) || s == u->sink) + if (!is_suitable_sink(u, s)) continue; if (!output_new(u, s)) { @@ -1088,13 +1135,11 @@ int pa__init(pa_module*m) { } } - 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_put_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PUT], PA_HOOK_LATE, (pa_hook_cb_t) sink_put_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); - u->sink_state_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_STATE_CHANGED], (pa_hook_cb_t) sink_state_changed_hook_cb, u); - - pick_master(u, NULL); + u->sink_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_UNLINK], PA_HOOK_EARLY, (pa_hook_cb_t) sink_unlink_hook_cb, u); + u->sink_state_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_STATE_CHANGED], PA_HOOK_NORMAL, (pa_hook_cb_t) sink_state_changed_hook_cb, u); if (!(u->thread = pa_thread_new(thread_func, u))) { pa_log("Failed to create thread."); @@ -1132,19 +1177,21 @@ fail: static void output_free(struct output *o) { pa_assert(o); - pick_master(o->userdata, o); - disable_output(o); pa_assert_se(pa_idxset_remove_by_data(o->userdata->outputs, o, NULL)); update_description(o->userdata); - if (o->inq_rtpoll_item) - pa_rtpoll_item_free(o->inq_rtpoll_item); + if (o->inq_rtpoll_item_read) + pa_rtpoll_item_free(o->inq_rtpoll_item_read); + if (o->inq_rtpoll_item_write) + pa_rtpoll_item_free(o->inq_rtpoll_item_write); - if (o->outq_rtpoll_item) - pa_rtpoll_item_free(o->outq_rtpoll_item); + if (o->outq_rtpoll_item_read) + pa_rtpoll_item_free(o->outq_rtpoll_item_read); + if (o->outq_rtpoll_item_write) + pa_rtpoll_item_free(o->outq_rtpoll_item_write); if (o->inq) pa_asyncmsgq_unref(o->inq); @@ -1167,8 +1214,8 @@ void pa__done(pa_module*m) { if (!(u = m->userdata)) return; - if (u->sink_new_slot) - pa_hook_slot_free(u->sink_new_slot); + if (u->sink_put_slot) + pa_hook_slot_free(u->sink_put_slot); if (u->sink_unlink_slot) pa_hook_slot_free(u->sink_unlink_slot); @@ -1202,5 +1249,8 @@ void pa__done(pa_module*m) { if (u->time_event) u->core->mainloop->time_free(u->time_event); + if (u->thread_info.smoother) + pa_smoother_free(u->thread_info.smoother); + pa_xfree(u); } diff --git a/src/modules/module-console-kit.c b/src/modules/module-console-kit.c new file mode 100644 index 00000000..3adee99e --- /dev/null +++ b/src/modules/module-console-kit.c @@ -0,0 +1,334 @@ +/*** + This file is part of PulseAudio. + + Copyright 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 + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include <pulse/xmalloc.h> +#include <pulse/timeval.h> + +#include <pulsecore/core-error.h> +#include <pulsecore/module.h> +#include <pulsecore/log.h> +#include <pulsecore/hashmap.h> +#include <pulsecore/idxset.h> +#include <pulsecore/core-util.h> +#include <pulsecore/namereg.h> +#include <pulsecore/core-scache.h> +#include <pulsecore/modargs.h> + +#include "dbus-util.h" +#include "module-console-kit-symdef.h" + +PA_MODULE_AUTHOR("Lennart Poettering"); +PA_MODULE_DESCRIPTION("Create a client for each ConsoleKit session of this user"); +PA_MODULE_VERSION(PACKAGE_VERSION); +PA_MODULE_LOAD_ONCE(TRUE); + +static const char* const valid_modargs[] = { + NULL +}; + +struct session { + char *id; + pa_client *client; +}; + +struct userdata { + pa_core *core; + pa_dbus_connection *connection; + pa_hashmap *sessions; +}; + +static void add_session(struct userdata *u, const char *id) { + DBusError error; + DBusMessage *m = NULL, *reply = NULL; + int32_t uid; + struct session *session; + char *t; + + dbus_error_init (&error); + + if (pa_hashmap_get(u->sessions, id)) { + pa_log_warn("Duplicate session %s, ignoring.", id); + return; + } + + if (!(m = dbus_message_new_method_call("org.freedesktop.ConsoleKit", id, "org.freedesktop.ConsoleKit.Session", "GetUnixUser"))) { + pa_log("Failed to allocate GetUnixUser() method call."); + goto fail; + } + + if (!(reply = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(u->connection), m, -1, &error))) { + pa_log("GetUnixUser() call failed: %s: %s", error.name, error.message); + goto fail; + } + + /* FIXME: Why is this in int32? and not an uint32? */ + if (!dbus_message_get_args(reply, &error, DBUS_TYPE_INT32, &uid, DBUS_TYPE_INVALID)) { + pa_log("Failed to parse GetUnixUser() result: %s: %s", error.name, error.message); + goto fail; + } + + /* We only care about our own sessions */ + if ((uid_t) uid != getuid()) + goto fail; + + session = pa_xnew(struct session, 1); + session->id = pa_xstrdup(id); + + t = pa_sprintf_malloc("ConsoleKit Session %s", id); + session->client = pa_client_new(u->core, __FILE__, t); + pa_xfree(t); + + pa_proplist_sets(session->client->proplist, "console-kit.session", id); + + pa_hashmap_put(u->sessions, session->id, session); + + pa_log_debug("Added new session %s", id); + +fail: + + if (m) + dbus_message_unref(m); + + if (reply) + dbus_message_unref(reply); + + dbus_error_free(&error); +} + +static void free_session(struct session *session) { + pa_assert(session); + + pa_log_debug("Removing session %s", session->id); + + pa_client_free(session->client); + pa_xfree(session->id); + pa_xfree(session); +} + +static void remove_session(struct userdata *u, const char *id) { + struct session *session; + + if (!(session = pa_hashmap_remove(u->sessions, id))) + return; + + free_session(session); +} + +static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *message, void *userdata) { + struct userdata *u = userdata; + DBusError error; + const char *path; + + pa_assert(bus); + pa_assert(message); + pa_assert(u); + + dbus_error_init(&error); + + pa_log_debug("dbus: interface=%s, path=%s, member=%s\n", + dbus_message_get_interface(message), + dbus_message_get_path(message), + dbus_message_get_member(message)); + + if (dbus_message_is_signal(message, "org.freedesktop.ConsoleKit.Seat", "SessionAdded")) { + + if (!dbus_message_get_args(message, &error, DBUS_TYPE_STRING, &path, DBUS_TYPE_INVALID) || dbus_error_is_set(&error)) { + pa_log_error("Failed to parse SessionAdded message: %s: %s", error.name, error.message); + goto finish; + } + + add_session(u, path); + + } else if (dbus_message_is_signal(message, "org.freedesktop.ConsoleKit.Seat", "SessionRemoved")) { + + if (!dbus_message_get_args(message, &error, DBUS_TYPE_STRING, &path, DBUS_TYPE_INVALID) || dbus_error_is_set(&error)) { + pa_log_error("Failed to parse SessionRemoved message: %s: %s", error.name, error.message); + goto finish; + } + + remove_session(u, path); + } + +finish: + dbus_error_free(&error); + + return DBUS_HANDLER_RESULT_HANDLED; +} + +static int get_session_list(struct userdata *u) { + DBusError error; + DBusMessage *m = NULL, *reply = NULL; + uint32_t uid; + DBusMessageIter iter, sub; + int ret = -1; + + pa_assert(u); + + dbus_error_init(&error); + + if (!(m = dbus_message_new_method_call("org.freedesktop.ConsoleKit", "/org/freedesktop/ConsoleKit/Manager", "org.freedesktop.ConsoleKit.Manager", "GetSessionsForUnixUser"))) { + pa_log("Failed to allocate GetSessionsForUnixUser() method call."); + goto fail; + } + + uid = (uint32_t) getuid(); + if (!(dbus_message_append_args(m, DBUS_TYPE_UINT32, &uid, DBUS_TYPE_INVALID))) { + pa_log("Failed to append arguments to GetSessionsForUnixUser() method call."); + goto fail; + } + + if (!(reply = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(u->connection), m, -1, &error))) { + pa_log("GetSessionsForUnixUser() call failed: %s: %s", error.name, error.message); + goto fail; + } + + dbus_message_iter_init(reply, &iter); + + if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY || + dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_OBJECT_PATH) { + pa_log("Failed to parse GetSessionsForUnixUser() result."); + goto fail; + } + + dbus_message_iter_recurse(&iter, &sub); + + for (;;) { + int at; + const char *id; + + if ((at = dbus_message_iter_get_arg_type(&sub)) == DBUS_TYPE_INVALID) + break; + + assert(at == DBUS_TYPE_OBJECT_PATH); + dbus_message_iter_get_basic(&sub, &id); + + add_session(u, id); + + dbus_message_iter_next(&sub); + } + + ret = 0; + +fail: + + if (m) + dbus_message_unref(m); + + if (reply) + dbus_message_unref(reply); + + dbus_error_free(&error); + + return ret; +} + +int pa__init(pa_module*m) { + DBusError error; + pa_dbus_connection *connection; + struct userdata *u = NULL; + pa_modargs *ma; + + pa_assert(m); + + dbus_error_init(&error); + + if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { + pa_log("Failed to parse module arguments"); + goto fail; + } + + if (!(connection = pa_dbus_bus_get(m->core, DBUS_BUS_SYSTEM, &error)) || dbus_error_is_set(&error)) { + + if (connection) + pa_dbus_connection_unref(connection); + + pa_log_error("Unable to contact D-Bus system bus: %s: %s", error.name, error.message); + goto fail; + } + + m->userdata = u = pa_xnew(struct userdata, 1); + u->core = m->core; + u->connection = connection; + u->sessions = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); + + if (!dbus_connection_add_filter(pa_dbus_connection_get(connection), filter_cb, u, NULL)) { + pa_log_error("Failed to add filter function"); + goto fail; + } + + dbus_bus_add_match(pa_dbus_connection_get(connection), "type='signal',sender='org.freedesktop.ConsoleKit', interface='org.freedesktop.ConsoleKit.Seat'", &error); + if (dbus_error_is_set(&error)) { + pa_log_error("Unable to subscribe to ConsoleKit signals: %s: %s", error.name, error.message); + goto fail; + } + + if (get_session_list(u) < 0) + goto fail; + + pa_modargs_free(ma); + + return 0; + +fail: + if (ma) + pa_modargs_free(ma); + + dbus_error_free(&error); + pa__done(m); + + return -1; +} + + +void pa__done(pa_module *m) { + struct userdata *u; + struct session *session; + + pa_assert(m); + + if (!(u = m->userdata)) + return; + + if (u->sessions) { + while ((session = pa_hashmap_steal_first(u->sessions))) + free_session(session); + + pa_hashmap_free(u->sessions, NULL, NULL); + } + + if (u->connection) + pa_dbus_connection_unref(u->connection); + + pa_xfree(u); +} diff --git a/src/modules/module-default-device-restore.c b/src/modules/module-default-device-restore.c index a7fc3a3f..2168ac71 100644 --- a/src/modules/module-default-device-restore.c +++ b/src/modules/module-default-device-restore.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -158,15 +156,15 @@ static void subscribe_cb(pa_core *c, pa_subscription_event_type_t t, uint32_t id int pa__init(pa_module *m) { struct userdata *u; - pa_assert(u); + pa_assert(m); u = pa_xnew0(struct userdata, 1); u->core = m->core; - if (!(u->sink_filename = pa_runtime_path(DEFAULT_SINK_FILE))) + if (!(u->sink_filename = pa_state_path(DEFAULT_SINK_FILE))) goto fail; - if (!(u->source_filename = pa_runtime_path(DEFAULT_SOURCE_FILE))) + if (!(u->source_filename = pa_state_path(DEFAULT_SOURCE_FILE))) goto fail; load(u); diff --git a/src/modules/module-defs.h.m4 b/src/modules/module-defs.h.m4 index a49e8329..64ce1928 100644 --- a/src/modules/module-defs.h.m4 +++ b/src/modules/module-defs.h.m4 @@ -1,4 +1,3 @@ -dnl $Id$ changecom(`/*', `*/')dnl define(`module_name', patsubst(patsubst(patsubst(fname, `-symdef.h$'), `^.*/'), `[^0-9a-zA-Z]', `_'))dnl define(`c_symbol', patsubst(module_name, `[^0-9a-zA-Z]', `_'))dnl diff --git a/src/modules/module-detect.c b/src/modules/module-detect.c index ee650dfd..13bcfcd1 100644 --- a/src/modules/module-detect.c +++ b/src/modules/module-detect.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/modules/module-device-restore.c b/src/modules/module-device-restore.c index 0a41b84a..b7a1e1b5 100644 --- a/src/modules/module-device-restore.c +++ b/src/modules/module-device-restore.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -263,7 +261,7 @@ static pa_hook_result_t source_fixate_hook_callback(pa_core *c, pa_source_new_da int pa__init(pa_module*m) { pa_modargs *ma = NULL; struct userdata *u; - char *fname, *runtime_dir; + char *fname, *fn; char hn[256]; pa_sink *sink; pa_source *source; @@ -282,19 +280,20 @@ int pa__init(pa_module*m) { 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); + u->sink_fixate_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_FIXATE], PA_HOOK_EARLY, (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_EARLY, (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; + fn = pa_sprintf_malloc("device-volumes.%s.gdbm", hn); + fname = pa_state_path(fn); + pa_xfree(fn); - fname = pa_sprintf_malloc("%s/device-volumes.%s.gdbm", runtime_dir, hn); - pa_xfree(runtime_dir); + if (!fname) + goto fail; 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)); diff --git a/src/modules/module-esound-compat-spawnfd.c b/src/modules/module-esound-compat-spawnfd.c index 8321192b..8eb4985b 100644 --- a/src/modules/module-esound-compat-spawnfd.c +++ b/src/modules/module-esound-compat-spawnfd.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/modules/module-esound-sink.c b/src/modules/module-esound-sink.c index 87b87c3d..e189febd 100644 --- a/src/modules/module-esound-sink.c +++ b/src/modules/module-esound-sink.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/modules/module-hal-detect.c b/src/modules/module-hal-detect.c index 44b31a59..19430a3d 100644 --- a/src/modules/module-hal-detect.c +++ b/src/modules/module-hal-detect.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/modules/module-jack-sink.c b/src/modules/module-jack-sink.c index 1ef5d235..c4d47f8e 100644 --- a/src/modules/module-jack-sink.c +++ b/src/modules/module-jack-sink.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -332,8 +330,7 @@ int pa__init(pa_module*m) { goto fail; } - pa_assert_se(pa_channel_map_init_auto(&map, channels, PA_CHANNEL_MAP_AUX)); - pa_channel_map_init_auto(&map, channels, PA_CHANNEL_MAP_ALSA); + pa_channel_map_init_extend(&map, channels, PA_CHANNEL_MAP_ALSA); if (pa_modargs_get_channel_map(ma, NULL, &map) < 0 || map.channels != channels) { pa_log("Failed to parse channel_map= argument."); goto fail; diff --git a/src/modules/module-jack-source.c b/src/modules/module-jack-source.c index fa2ec5eb..03f9d15c 100644 --- a/src/modules/module-jack-source.c +++ b/src/modules/module-jack-source.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -303,8 +301,7 @@ int pa__init(pa_module*m) { goto fail; } - pa_assert_se(pa_channel_map_init_auto(&map, channels, PA_CHANNEL_MAP_AUX)); - pa_channel_map_init_auto(&map, channels, PA_CHANNEL_MAP_ALSA); + pa_channel_map_init_extend(&map, channels, PA_CHANNEL_MAP_ALSA); if (pa_modargs_get_channel_map(ma, NULL, &map) < 0 || map.channels != channels) { pa_log("failed to parse channel_map= argument."); goto fail; diff --git a/src/modules/module-ladspa-sink.c b/src/modules/module-ladspa-sink.c index 245efcb0..3e0babfa 100644 --- a/src/modules/module-ladspa-sink.c +++ b/src/modules/module-ladspa-sink.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -185,6 +183,7 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk pa_memblock_unref(nchunk.memblock); } + tchunk.length = PA_MIN(nbytes, tchunk.length); pa_assert(tchunk.length > 0); fs = pa_frame_size(&i->sample_spec); @@ -261,7 +260,7 @@ static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) { pa_sink_input_assert_ref(i); pa_assert_se(u = i->userdata); - if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state)) + if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state)) return; pa_memblockq_set_maxrewind(u->memblockq, nbytes); @@ -269,13 +268,39 @@ static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) { } /* Called from I/O thread context */ +static void sink_input_update_max_request_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_LINKED(u->sink->thread_info.state)) + return; + + pa_sink_set_max_request(u->sink, nbytes); +} + +/* Called from I/O thread context */ +static void sink_input_update_sink_latency_range_cb(pa_sink_input *i) { + struct userdata *u; + + pa_sink_input_assert_ref(i); + pa_assert_se(u = i->userdata); + + if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state)) + return; + + pa_sink_update_latency_range(u->sink, i->sink->thread_info.min_latency, i->sink->thread_info.max_latency); +} + +/* Called from I/O thread context */ static void sink_input_detach_cb(pa_sink_input *i) { struct userdata *u; pa_sink_input_assert_ref(i); pa_assert_se(u = i->userdata); - if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state)) + if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state)) return; pa_sink_detach_within_thread(u->sink); @@ -290,15 +315,14 @@ 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)) + if (!u->sink || !PA_SINK_IS_LINKED(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; + pa_sink_update_latency_range(u->sink, u->master->thread_info.min_latency, u->master->thread_info.max_latency); } /* Called from main context */ @@ -511,7 +535,7 @@ int pa__init(pa_module*m) { p = 0; while ((k = pa_split(cdata, ",", &state)) && p < n_control) { - float f; + double f; if (*k == 0) { use_default[p++] = TRUE; @@ -519,7 +543,7 @@ int pa__init(pa_module*m) { continue; } - if (pa_atof(k, &f) < 0) { + if (pa_atod(k, &f) < 0) { pa_log("Failed to parse control value '%s'", k); pa_xfree(k); goto fail; @@ -707,6 +731,8 @@ int pa__init(pa_module*m) { 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->update_max_request = sink_input_update_max_request_cb; + u->sink_input->update_sink_latency_range = sink_input_update_sink_latency_range_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; diff --git a/src/modules/module-lirc.c b/src/modules/module-lirc.c index 24542172..0570a6a1 100644 --- a/src/modules/module-lirc.c +++ b/src/modules/module-lirc.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/modules/module-match.c b/src/modules/module-match.c index d0265455..769a6b59 100644 --- a/src/modules/module-match.c +++ b/src/modules/module-match.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/modules/module-mmkbd-evdev.c b/src/modules/module-mmkbd-evdev.c index 03c0e973..4388e49c 100644 --- a/src/modules/module-mmkbd-evdev.c +++ b/src/modules/module-mmkbd-evdev.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/modules/module-native-protocol-fd.c b/src/modules/module-native-protocol-fd.c index 53f41896..1a6f5368 100644 --- a/src/modules/module-native-protocol-fd.c +++ b/src/modules/module-native-protocol-fd.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/modules/module-null-sink.c b/src/modules/module-null-sink.c index aff244fa..604ab158 100644 --- a/src/modules/module-null-sink.c +++ b/src/modules/module-null-sink.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -59,8 +57,8 @@ PA_MODULE_USAGE( "format=<sample format> " "channels=<number of channels> " "rate=<sample rate> " - "sink_name=<name of sink>" - "channel_map=<channel map>" + "sink_name=<name of sink> " + "channel_map=<channel map> " "description=<description for the sink>"); #define DEFAULT_SINK_NAME "null" @@ -157,34 +155,32 @@ static void process_rewind(struct userdata *u, pa_usec_t now) { } 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); + might not have a single frame more than the maxrequest value + queed. Hence: at maximum read this many bytes from the sink + inputs. */ /* 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_sink_render(u->sink, u->sink->thread_info.max_request, &chunk); pa_memblock_unref(chunk.memblock); - pa_log_debug("Ate %lu bytes.", (unsigned long) chunk.length); +/* 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) + if (ate >= u->sink->thread_info.max_request) break; } - pa_log_debug("Ate in sum %lu bytes (of %lu)", (unsigned long) ate, (unsigned long) nbytes); +/* pa_log_debug("Ate in sum %lu bytes (of %lu)", (unsigned long) ate, (unsigned long) nbytes); */ } static void thread_func(void *userdata) { @@ -203,7 +199,7 @@ static void thread_func(void *userdata) { int ret; /* Render some data and drop it immediately */ - if (u->sink->thread_info.state == PA_SINK_RUNNING) { + if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) { pa_usec_t now; now = pa_rtclock_usec(); @@ -256,10 +252,9 @@ int pa__init(pa_module*m) { goto fail; } - u = pa_xnew0(struct userdata, 1); + m->userdata = u = pa_xnew0(struct userdata, 1); u->core = m->core; u->module = m; - m->userdata = u; u->rtpoll = pa_rtpoll_new(); pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll); @@ -270,6 +265,7 @@ int pa__init(pa_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, pa_modargs_get_value(ma, "description", "Null Output")); + pa_proplist_sets(data.proplist, PA_PROP_DEVICE_CLASS, "abstract"); u->sink = pa_sink_new(m->core, &data, PA_SINK_LATENCY); pa_sink_new_data_done(&data); @@ -286,9 +282,12 @@ int pa__init(pa_module*m) { pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq); pa_sink_set_rtpoll(u->sink, u->rtpoll); - u->block_usec = u->sink->max_latency = MAX_LATENCY_USEC; + pa_sink_set_latency_range(u->sink, (pa_usec_t) -1, MAX_LATENCY_USEC); + u->block_usec = u->sink->thread_info.max_latency; - u->sink->thread_info.max_rewind = pa_usec_to_bytes(u->block_usec, &u->sink->sample_spec); + u->sink->thread_info.max_rewind = + u->sink->thread_info.max_request = + 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."); diff --git a/src/modules/module-oss.c b/src/modules/module-oss.c index cf7584db..76b13ecc 100644 --- a/src/modules/module-oss.c +++ b/src/modules/module-oss.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -510,6 +508,9 @@ static int suspend(struct userdata *u) { return 0; } +static int sink_get_volume(pa_sink *s); +static int source_get_volume(pa_source *s); + static int unsuspend(struct userdata *u) { int m; pa_sample_spec ss, *ss_original; @@ -600,9 +601,9 @@ static int unsuspend(struct userdata *u) { build_pollfd(u); if (u->sink) - pa_sink_get_volume(u->sink); + sink_get_volume(u->sink); if (u->source) - pa_source_get_volume(u->source); + source_get_volume(u->source); pa_log_info("Resumed successfully..."); @@ -1368,6 +1369,8 @@ int pa__init(pa_module*m) { pa_sink_set_rtpoll(u->sink, u->rtpoll); u->sink->refresh_volume = TRUE; + u->sink->thread_info.max_request = u->out_hwbuf_size; + if (use_mmap) u->out_mmap_memblocks = pa_xnew0(pa_memblock*, u->out_nfrags); } diff --git a/src/modules/module-pipe-sink.c b/src/modules/module-pipe-sink.c index cc648928..cd25b890 100644 --- a/src/modules/module-pipe-sink.c +++ b/src/modules/module-pipe-sink.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/modules/module-pipe-source.c b/src/modules/module-pipe-source.c index 83eb4f8a..b0de34ca 100644 --- a/src/modules/module-pipe-source.c +++ b/src/modules/module-pipe-source.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/modules/module-position-event-sounds.c b/src/modules/module-position-event-sounds.c new file mode 100644 index 00000000..90e693a3 --- /dev/null +++ b/src/modules/module-position-event-sounds.c @@ -0,0 +1,165 @@ +/*** + 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 <pulse/xmalloc.h> +#include <pulse/volume.h> +#include <pulse/channelmap.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/sink-input.h> + +#include "module-position-event-sounds-symdef.h" + +PA_MODULE_AUTHOR("Lennart Poettering"); +PA_MODULE_DESCRIPTION("Position event sounds between L and R depending on the position on screen of the widget triggering them."); +PA_MODULE_VERSION(PACKAGE_VERSION); +PA_MODULE_LOAD_ONCE(TRUE); + +static const char* const valid_modargs[] = { + NULL +}; + +struct userdata { + pa_core *core; + pa_hook_slot *sink_input_fixate_hook_slot; +}; + +static pa_bool_t is_left(pa_channel_position_t p) { + return + p == PA_CHANNEL_POSITION_FRONT_LEFT || + p == PA_CHANNEL_POSITION_REAR_LEFT || + p == PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER || + p == PA_CHANNEL_POSITION_SIDE_LEFT || + p == PA_CHANNEL_POSITION_TOP_FRONT_LEFT || + p == PA_CHANNEL_POSITION_TOP_REAR_LEFT; +} + +static pa_bool_t is_right(pa_channel_position_t p) { + return + p == PA_CHANNEL_POSITION_FRONT_RIGHT || + p == PA_CHANNEL_POSITION_REAR_RIGHT|| + p == PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER || + p == PA_CHANNEL_POSITION_SIDE_RIGHT || + p == PA_CHANNEL_POSITION_TOP_FRONT_RIGHT || + p == PA_CHANNEL_POSITION_TOP_REAR_RIGHT; +} + +static pa_hook_result_t sink_input_fixate_hook_callback(pa_core *core, pa_sink_input_new_data *data, struct userdata *u) { + const char *hpos; + double f; + unsigned c; + char t[PA_CVOLUME_SNPRINT_MAX]; + + pa_assert(data); + + if (!(hpos = pa_proplist_gets(data->proplist, PA_PROP_EVENT_MOUSE_HPOS))) + return PA_HOOK_OK; + + if (pa_atod(hpos, &f) < 0) { + pa_log_warn("Failed to parse "PA_PROP_EVENT_MOUSE_HPOS" property '%s'.", hpos); + return PA_HOOK_OK; + } + + if (f < 0.0 || f > 1.0) { + pa_log_warn("Property "PA_PROP_EVENT_MOUSE_HPOS" out of range %0.2f", f); + return PA_HOOK_OK; + } + + pa_log_debug("Positioning event sound '%s' at %0.2f.", pa_strnull(pa_proplist_gets(data->proplist, PA_PROP_EVENT_ID)), f); + + if (!data->volume_is_set) { + pa_cvolume_reset(&data->volume, data->sample_spec.channels); + data->volume_is_set = TRUE; + } + + for (c = 0; c < data->sample_spec.channels; c++) { + + if (is_left(data->channel_map.map[c])) + data->volume.values[c] = + pa_sw_volume_multiply(data->volume.values[c], (pa_volume_t) (PA_VOLUME_NORM * (1.0 - f))); + + if (is_right(data->channel_map.map[c])) + data->volume.values[c] = + pa_sw_volume_multiply(data->volume.values[c], (pa_volume_t) (PA_VOLUME_NORM * f)); + } + + pa_log_debug("Final volume %s.", pa_cvolume_snprint(t, sizeof(t), &data->volume)); + + return PA_HOOK_OK; +} + +int pa__init(pa_module*m) { + pa_modargs *ma = NULL; + struct userdata *u; + + pa_assert(m); + + if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { + pa_log("Failed to parse module arguments"); + goto fail; + } + + m->userdata = u = pa_xnew(struct userdata, 1); + u->core = m->core; + u->sink_input_fixate_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_FIXATE], PA_HOOK_EARLY, (pa_hook_cb_t) sink_input_fixate_hook_callback, 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->sink_input_fixate_hook_slot) + pa_hook_slot_free(u->sink_input_fixate_hook_slot); + + pa_xfree(u); +} diff --git a/src/modules/module-protocol-stub.c b/src/modules/module-protocol-stub.c index 8bcc19b1..0c9529c3 100644 --- a/src/modules/module-protocol-stub.c +++ b/src/modules/module-protocol-stub.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -271,7 +269,7 @@ int pa__init(pa_module*m) { /* This socket doesn't reside in our own runtime dir but in * /tmp/.esd/, hence we have to create the dir first */ - if (pa_make_secure_parent_dir(u->socket_path, m->core->is_system_instance ? 0755 : 0700, (uid_t)-1, (gid_t)-1) < 0) { + if (pa_make_secure_parent_dir(u->socket_path, pa_in_system_mode() ? 0755 : 0700, (uid_t)-1, (gid_t)-1) < 0) { pa_log("Failed to create socket directory '%s': %s\n", u->socket_path, pa_cstrerror(errno)); goto fail; } diff --git a/src/modules/module-remap-sink.c b/src/modules/module-remap-sink.c index 0b9825e1..c87b1ece 100644 --- a/src/modules/module-remap-sink.c +++ b/src/modules/module-remap-sink.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -51,7 +49,8 @@ PA_MODULE_USAGE( "format=<sample format> " "channels=<number of channels> " "rate=<sample rate> " - "channel_map=<channel map>"); + "channel_map=<channel map> " + "remix=<remix channels?>"); struct userdata { pa_core *core; @@ -69,6 +68,7 @@ static const char* const valid_modargs[] = { "format", "channels", "channel_map", + "remix", NULL }; @@ -179,20 +179,46 @@ static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) { pa_sink_input_assert_ref(i); pa_assert_se(u = i->userdata); - if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state)) + if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state)) return; pa_sink_set_max_rewind(u->sink, nbytes); } /* Called from I/O thread context */ +static void sink_input_update_max_request_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_LINKED(u->sink->thread_info.state)) + return; + + pa_sink_set_max_request(u->sink, nbytes); +} + +/* Called from I/O thread context */ +static void sink_input_update_sink_latency_range_cb(pa_sink_input *i) { + struct userdata *u; + + pa_sink_input_assert_ref(i); + pa_assert_se(u = i->userdata); + + if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state)) + return; + + pa_sink_update_latency_range(u->sink, i->sink->thread_info.min_latency, i->sink->thread_info.max_latency); +} + +/* Called from I/O thread context */ static void sink_input_detach_cb(pa_sink_input *i) { struct userdata *u; pa_sink_input_assert_ref(i); pa_assert_se(u = i->userdata); - if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state)) + if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state)) return; pa_sink_detach_within_thread(u->sink); @@ -207,15 +233,14 @@ 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)) + if (!u->sink || !PA_SINK_IS_LINKED(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; + pa_sink_update_latency_range(u->sink, u->master->thread_info.min_latency, u->master->thread_info.max_latency); } /* Called from main context */ @@ -261,6 +286,7 @@ int pa__init(pa_module*m) { pa_sink *master; pa_sink_input_new_data sink_input_data; pa_sink_new_data sink_data; + pa_bool_t remix = TRUE; pa_assert(m); @@ -283,7 +309,7 @@ int pa__init(pa_module*m) { stream_map = sink_map; if (pa_modargs_get_channel_map(ma, "master_channel_map", &stream_map) < 0) { - pa_log("Invalid master hannel map"); + pa_log("Invalid master channel map"); goto fail; } @@ -295,6 +321,11 @@ int pa__init(pa_module*m) { if (pa_channel_map_equal(&stream_map, &master->channel_map)) pa_log_warn("No remapping configured, proceeding nonetheless!"); + if (pa_modargs_get_value_boolean(ma, "remix", &remix) < 0) { + pa_log("Invalid boolean remix parameter"); + goto fail; + } + u = pa_xnew0(struct userdata, 1); u->core = m->core; u->module = m; @@ -343,7 +374,7 @@ int pa__init(pa_module*m) { 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); - u->sink_input = pa_sink_input_new(m->core, &sink_input_data, PA_SINK_INPUT_DONT_MOVE); + u->sink_input = pa_sink_input_new(m->core, &sink_input_data, PA_SINK_INPUT_DONT_MOVE | (remix ? 0 : PA_SINK_INPUT_NO_REMIX)); pa_sink_input_new_data_done(&sink_input_data); if (!u->sink_input) @@ -352,9 +383,11 @@ int pa__init(pa_module*m) { 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->update_max_request = sink_input_update_max_request_cb; + u->sink_input->update_sink_latency_range = sink_input_update_sink_latency_range_cb; u->sink_input->attach = sink_input_attach_cb; u->sink_input->detach = sink_input_detach_cb; + u->sink_input->kill = sink_input_kill_cb; u->sink_input->state_change = sink_input_state_change_cb; u->sink_input->userdata = u; diff --git a/src/modules/module-rescue-streams.c b/src/modules/module-rescue-streams.c index 7241a99f..cc6717cb 100644 --- a/src/modules/module-rescue-streams.c +++ b/src/modules/module-rescue-streams.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -139,8 +137,8 @@ int pa__init(pa_module*m) { } m->userdata = u = pa_xnew(struct userdata, 1); - u->sink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_UNLINK], (pa_hook_cb_t) sink_hook_callback, NULL); - u->source_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK], (pa_hook_cb_t) source_hook_callback, NULL); + u->sink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_UNLINK], PA_HOOK_LATE, (pa_hook_cb_t) sink_hook_callback, NULL); + u->source_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK], PA_HOOK_LATE, (pa_hook_cb_t) source_hook_callback, NULL); pa_modargs_free(ma); return 0; diff --git a/src/modules/module-sine.c b/src/modules/module-sine.c index 3d917054..38780f24 100644 --- a/src/modules/module-sine.c +++ b/src/modules/module-sine.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/modules/module-solaris.c b/src/modules/module-solaris.c index 4a5c88e4..6f50543a 100644 --- a/src/modules/module-solaris.c +++ b/src/modules/module-solaris.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/modules/module-suspend-on-idle.c b/src/modules/module-suspend-on-idle.c index a3985974..bc7c023c 100644 --- a/src/modules/module-suspend-on-idle.c +++ b/src/modules/module-suspend-on-idle.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -367,21 +365,21 @@ 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_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); - u->source_state_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_STATE_CHANGED], (pa_hook_cb_t) device_state_changed_hook_cb, u); - - u->sink_input_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_FIXATE], (pa_hook_cb_t) sink_input_fixate_hook_cb, u); - u->source_output_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_FIXATE], (pa_hook_cb_t) source_output_fixate_hook_cb, u); - u->sink_input_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_UNLINK_POST], (pa_hook_cb_t) sink_input_unlink_hook_cb, u); - u->source_output_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK_POST], (pa_hook_cb_t) source_output_unlink_hook_cb, u); - u->sink_input_move_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE], (pa_hook_cb_t) sink_input_move_hook_cb, u); - u->source_output_move_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE], (pa_hook_cb_t) source_output_move_hook_cb, u); - u->sink_input_state_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED], (pa_hook_cb_t) sink_input_state_changed_hook_cb, u); - u->source_output_state_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_STATE_CHANGED], (pa_hook_cb_t) source_output_state_changed_hook_cb, u); + u->sink_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PUT], PA_HOOK_NORMAL, (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_NORMAL, (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_NORMAL, (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_NORMAL, (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_NORMAL, (pa_hook_cb_t) device_state_changed_hook_cb, u); + u->source_state_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_STATE_CHANGED], PA_HOOK_NORMAL, (pa_hook_cb_t) device_state_changed_hook_cb, u); + + u->sink_input_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_FIXATE], PA_HOOK_NORMAL, (pa_hook_cb_t) sink_input_fixate_hook_cb, u); + u->source_output_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_FIXATE], PA_HOOK_NORMAL, (pa_hook_cb_t) source_output_fixate_hook_cb, u); + u->sink_input_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_UNLINK_POST], PA_HOOK_NORMAL, (pa_hook_cb_t) sink_input_unlink_hook_cb, u); + u->source_output_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK_POST], PA_HOOK_NORMAL, (pa_hook_cb_t) source_output_unlink_hook_cb, u); + u->sink_input_move_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE], PA_HOOK_NORMAL, (pa_hook_cb_t) sink_input_move_hook_cb, u); + u->source_output_move_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE], PA_HOOK_NORMAL, (pa_hook_cb_t) source_output_move_hook_cb, u); + u->sink_input_state_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED], PA_HOOK_NORMAL, (pa_hook_cb_t) sink_input_state_changed_hook_cb, u); + u->source_output_state_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_STATE_CHANGED], PA_HOOK_NORMAL, (pa_hook_cb_t) source_output_state_changed_hook_cb, u); pa_modargs_free(ma); return 0; diff --git a/src/modules/module-tunnel.c b/src/modules/module-tunnel.c index 7a87fd8c..86f30817 100644 --- a/src/modules/module-tunnel.c +++ b/src/modules/module-tunnel.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -56,11 +54,16 @@ #include <pulsecore/thread-mq.h> #include <pulsecore/rtclock.h> #include <pulsecore/core-error.h> +#include <pulsecore/proplist-util.h> #ifdef TUNNEL_SINK #include "module-tunnel-sink-symdef.h" +#else +#include "module-tunnel-source-symdef.h" +#endif + +#ifdef TUNNEL_SINK PA_MODULE_DESCRIPTION("Tunnel module for sinks"); -PA_MODULE_LOAD_ONCE(FALSE); PA_MODULE_USAGE( "server=<address> " "sink=<remote sink name> " @@ -71,7 +74,6 @@ PA_MODULE_USAGE( "sink_name=<name for the local sink> " "channel_map=<channel map>"); #else -#include "module-tunnel-source-symdef.h" PA_MODULE_DESCRIPTION("Tunnel module for sources"); PA_MODULE_USAGE( "server=<address> " @@ -86,15 +88,7 @@ PA_MODULE_USAGE( PA_MODULE_AUTHOR("Lennart Poettering"); PA_MODULE_VERSION(PACKAGE_VERSION); - -#define DEFAULT_TLENGTH_MSEC 100 -#define DEFAULT_MINREQ_MSEC 10 -#define DEFAULT_MAXLENGTH_MSEC ((DEFAULT_TLENGTH_MSEC*3)/2) -#define DEFAULT_FRAGSIZE_MSEC 10 - -#define DEFAULT_TIMEOUT 5 - -#define LATENCY_INTERVAL 10 +PA_MODULE_LOAD_ONCE(FALSE); static const char* const valid_modargs[] = { "server", @@ -113,36 +107,58 @@ static const char* const valid_modargs[] = { NULL, }; -enum { - SOURCE_MESSAGE_POST = PA_SOURCE_MESSAGE_MAX -}; +#define DEFAULT_TIMEOUT 5 + +#define LATENCY_INTERVAL 10 + +#define MIN_NETWORK_LATENCY_USEC (8*PA_USEC_PER_MSEC) + +#ifdef TUNNEL_SINK enum { SINK_MESSAGE_REQUEST = PA_SINK_MESSAGE_MAX, + SINK_MESSAGE_REMOTE_SUSPEND, + SINK_MESSAGE_UPDATE_LATENCY, SINK_MESSAGE_POST }; +#define DEFAULT_TLENGTH_MSEC 150 +#define DEFAULT_MINREQ_MSEC 25 + +#else + +enum { + SOURCE_MESSAGE_POST = PA_SOURCE_MESSAGE_MAX, + SOURCE_MESSAGE_REMOTE_SUSPEND, + SOURCE_MESSAGE_UPDATE_LATENCY +}; + +#define DEFAULT_FRAGSIZE_MSEC 25 + +#endif + #ifdef TUNNEL_SINK static void command_request(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); +static void command_started(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); #endif static void command_subscribe_event(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); static void command_stream_killed(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); -static void command_overflow(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); -static void command_underflow(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); -static void command_suspend(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); +static void command_overflow_or_underflow(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); +static void command_suspended(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); static void command_moved(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = { #ifdef TUNNEL_SINK [PA_COMMAND_REQUEST] = command_request, + [PA_COMMAND_STARTED] = command_started, #endif [PA_COMMAND_SUBSCRIBE_EVENT] = command_subscribe_event, - [PA_COMMAND_OVERFLOW] = command_overflow, - [PA_COMMAND_UNDERFLOW] = command_underflow, + [PA_COMMAND_OVERFLOW] = command_overflow_or_underflow, + [PA_COMMAND_UNDERFLOW] = command_overflow_or_underflow, [PA_COMMAND_PLAYBACK_STREAM_KILLED] = command_stream_killed, [PA_COMMAND_RECORD_STREAM_KILLED] = command_stream_killed, - [PA_COMMAND_PLAYBACK_STREAM_SUSPENDED] = command_suspend, - [PA_COMMAND_RECORD_STREAM_SUSPENDED] = command_suspend, + [PA_COMMAND_PLAYBACK_STREAM_SUSPENDED] = command_suspended, + [PA_COMMAND_RECORD_STREAM_SUSPENDED] = command_suspended, [PA_COMMAND_PLAYBACK_STREAM_MOVED] = command_moved, [PA_COMMAND_RECORD_STREAM_MOVED] = command_moved, }; @@ -163,7 +179,7 @@ struct userdata { #ifdef TUNNEL_SINK char *sink_name; pa_sink *sink; - uint32_t requested_bytes; + int32_t requested_bytes; #else char *source_name; pa_source *source; @@ -178,6 +194,14 @@ struct userdata { int64_t counter, counter_delta; + pa_bool_t remote_corked:1; + pa_bool_t remote_suspended:1; + + pa_usec_t transport_usec; + pa_bool_t transport_usec_valid; + + uint32_t ignore_latency_before; + pa_time_event *time_event; pa_bool_t auth_cookie_in_property; @@ -198,7 +222,10 @@ struct userdata { #endif }; -static void command_stream_killed(pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { +static void request_latency(struct userdata *u); + +/* Called from main context */ +static void command_stream_killed(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { struct userdata *u = userdata; pa_assert(pd); @@ -210,7 +237,8 @@ static void command_stream_killed(pa_pdispatch *pd, PA_GCC_UNUSED uint32_t comma pa_module_unload_request(u->module); } -static void command_overflow(pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { +/* Called from main context */ +static void command_overflow_or_underflow(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { struct userdata *u = userdata; pa_assert(pd); @@ -218,21 +246,40 @@ static void command_overflow(pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, P pa_assert(u); pa_assert(u->pdispatch == pd); - pa_log_warn("Server signalled buffer overrun."); + pa_log_info("Server signalled buffer overrun/underrun."); + request_latency(u); } -static void command_underflow(pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { +/* Called from main context */ +static void command_suspended(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { struct userdata *u = userdata; + uint32_t channel; + pa_bool_t suspended; pa_assert(pd); pa_assert(t); pa_assert(u); pa_assert(u->pdispatch == pd); - pa_log_warn("Server signalled buffer underrun."); + if (pa_tagstruct_getu32(t, &channel) < 0 || + pa_tagstruct_get_boolean(t, &suspended) < 0 || + !pa_tagstruct_eof(t)) { + pa_log("Invalid packet"); + pa_module_unload_request(u->module); + return; + } + +#ifdef TUNNEL_SINK + pa_asyncmsgq_send(u->sink->asyncmsgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_REMOTE_SUSPEND, PA_UINT32_TO_PTR(!!suspended), 0, NULL); +#else + pa_asyncmsgq_send(u->source->asyncmsgq, PA_MSGOBJECT(u->source), SOURCE_MESSAGE_REMOTE_SUSPEND, PA_UINT32_TO_PTR(!!suspended), 0, NULL); +#endif + + request_latency(u); } -static void command_suspend(pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { +/* Called from main context */ +static void command_moved(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { struct userdata *u = userdata; pa_assert(pd); @@ -240,28 +287,54 @@ static void command_suspend(pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, PA pa_assert(u); pa_assert(u->pdispatch == pd); - pa_log_debug("Server reports a stream suspension."); + pa_log_debug("Server reports a stream move."); + request_latency(u); } -static void command_moved(pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { - struct userdata *u = userdata; +#ifdef TUNNEL_SINK + +/* Called from main context */ +static void command_started(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { + struct userdata *u = userdata; pa_assert(pd); pa_assert(t); pa_assert(u); pa_assert(u->pdispatch == pd); - pa_log_debug("Server reports a stream move."); + pa_log_debug("Server reports playback started."); + request_latency(u); } -static void stream_cork(struct userdata *u, pa_bool_t cork) { - pa_tagstruct *t; +#endif + +/* Called from IO thread context */ +static void stream_cork_within_thread(struct userdata *u, pa_bool_t cork) { + pa_usec_t x; pa_assert(u); - if (cork) - pa_smoother_pause(u->smoother, pa_rtclock_usec()); + if (u->remote_corked == cork) + return; + + u->remote_corked = cork; + x = pa_rtclock_usec(); + + /* Correct by the time this needs to travel to the other side. + * This is a valid thread-safe access, because the main thread is + * waiting for us */ + if (u->transport_usec_valid) + x += u->transport_usec; + + if (u->remote_suspended || u->remote_corked) + pa_smoother_pause(u->smoother, x); else - pa_smoother_resume(u->smoother, pa_rtclock_usec()); + pa_smoother_resume(u->smoother, x); +} + +/* Called from main context */ +static void stream_cork(struct userdata *u, pa_bool_t cork) { + pa_tagstruct *t; + pa_assert(u); if (!u->pstream) return; @@ -276,19 +349,50 @@ static void stream_cork(struct userdata *u, pa_bool_t cork) { pa_tagstruct_putu32(t, u->channel); pa_tagstruct_put_boolean(t, !!cork); pa_pstream_send_tagstruct(u->pstream, t); + + request_latency(u); +} + +/* Called from IO thread context */ +static void stream_suspend_within_thread(struct userdata *u, pa_bool_t suspend) { + pa_usec_t x; + pa_assert(u); + + if (u->remote_suspended == suspend) + return; + + u->remote_suspended = suspend; + + x = pa_rtclock_usec(); + + /* Correct by the time this needed to travel from the other side. + * This is a valid thread-safe access, because the main thread is + * waiting for us */ + if (u->transport_usec_valid) + x -= u->transport_usec; + + if (u->remote_suspended || u->remote_corked) + pa_smoother_pause(u->smoother, x); + else + pa_smoother_resume(u->smoother, x); } #ifdef TUNNEL_SINK +/* Called from IO thread context */ static void send_data(struct userdata *u) { pa_assert(u); while (u->requested_bytes > 0) { pa_memchunk memchunk; + pa_sink_render(u->sink, u->requested_bytes, &memchunk); pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_POST, NULL, 0, &memchunk, NULL); pa_memblock_unref(memchunk.memblock); + u->requested_bytes -= memchunk.length; + + u->counter += memchunk.length; } } @@ -302,13 +406,27 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse int r; /* 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_IS_OPENED((pa_sink_state_t) PA_PTR_TO_UINT(data))) + if ((r = pa_sink_process_msg(o, code, data, offset, chunk)) >= 0) { + + stream_cork_within_thread(u, u->sink->state == PA_SINK_SUSPENDED); + + if (PA_SINK_IS_OPENED(u->sink->state)) send_data(u); + } return r; } + case PA_SINK_MESSAGE_GET_LATENCY: { + pa_usec_t yl, yr, *usec = data; + + yl = pa_bytes_to_usec(u->counter, &u->sink->sample_spec); + yr = pa_smoother_get(u->smoother, pa_rtclock_usec()); + + *usec = yl > yr ? yl - yr : 0; + return 0; + } + case SINK_MESSAGE_REQUEST: pa_assert(offset > 0); @@ -319,6 +437,28 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse return 0; + + case SINK_MESSAGE_REMOTE_SUSPEND: + + stream_suspend_within_thread(u, !!PA_PTR_TO_UINT(data)); + return 0; + + + case SINK_MESSAGE_UPDATE_LATENCY: { + pa_usec_t y; + + y = pa_bytes_to_usec(u->counter, &u->sink->sample_spec); + + if (y > (pa_usec_t) offset || offset < 0) + y -= offset; + else + y = 0; + + pa_smoother_put(u->smoother, pa_rtclock_usec(), y); + + return 0; + } + case SINK_MESSAGE_POST: /* OK, This might be a bit confusing. This message is @@ -327,14 +467,16 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse * dispatched. Yeah, ugly, but I am a lazy bastard. */ pa_pstream_send_memblock(u->pstream, u->channel, 0, PA_SEEK_RELATIVE, chunk); - u->counter += chunk->length; + u->counter_delta += chunk->length; + return 0; } return pa_sink_process_msg(o, code, data, offset, chunk); } +/* Called from main context */ static int sink_set_state(pa_sink *s, pa_sink_state_t state) { struct userdata *u; pa_sink_assert_ref(s); @@ -363,20 +505,65 @@ static int sink_set_state(pa_sink *s, pa_sink_state_t state) { #else +/* This function is called from IO context -- except when it is not. */ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) { struct userdata *u = PA_SOURCE(o)->userdata; switch (code) { + + case PA_SINK_MESSAGE_SET_STATE: { + int r; + + if ((r = pa_sink_process_msg(o, code, data, offset, chunk)) >= 0) + stream_cork_within_thread(u, u->source->state == PA_SOURCE_SUSPENDED); + + return r; + } + + case PA_SOURCE_MESSAGE_GET_LATENCY: { + pa_usec_t yr, yl, *usec = data; + + yl = pa_bytes_to_usec(u->counter, &PA_SINK(o)->sample_spec); + yr = pa_smoother_get(u->smoother, pa_rtclock_usec()); + + *usec = yr > yl ? yr - yl : 0; + return 0; + } + case SOURCE_MESSAGE_POST: if (PA_SOURCE_IS_OPENED(u->source->thread_info.state)) pa_source_post(u->source, chunk); + + u->counter += chunk->length; + return 0; + + case SOURCE_MESSAGE_REMOTE_SUSPEND: + + stream_suspend_within_thread(u, !!PA_PTR_TO_UINT(data)); + return 0; + + case SOURCE_MESSAGE_UPDATE_LATENCY: { + pa_usec_t y; + + y = pa_bytes_to_usec(u->counter, &u->source->sample_spec); + + if (offset >= 0 || y > (pa_usec_t) -offset) + y += offset; + else + y = 0; + + pa_smoother_put(u->smoother, pa_rtclock_usec(), y); + + return 0; + } } return pa_source_process_msg(o, code, data, offset, chunk); } +/* Called from main context */ static int source_set_state(pa_source *s, pa_source_state_t state) { struct userdata *u; pa_source_assert_ref(s); @@ -436,7 +623,8 @@ finish: } #ifdef TUNNEL_SINK -static void command_request(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { +/* Called from main context */ +static void command_request(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { struct userdata *u = userdata; uint32_t bytes, channel; @@ -457,7 +645,7 @@ static void command_request(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED ui goto fail; } - pa_asyncmsgq_send(u->sink->asyncmsgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_REQUEST, NULL, bytes, NULL); + pa_asyncmsgq_post(u->sink->asyncmsgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_REQUEST, NULL, bytes, NULL, NULL); return; fail: @@ -466,12 +654,15 @@ fail: #endif -static void stream_get_latency_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { +/* Called from main context */ +static void stream_get_latency_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { struct userdata *u = userdata; - pa_usec_t sink_usec, source_usec, transport_usec, host_usec, k; - int playing; + pa_usec_t sink_usec, source_usec, transport_usec; + pa_bool_t playing; int64_t write_index, read_index; struct timeval local, remote, now; + pa_sample_spec *ss; + int64_t delay; pa_assert(pd); pa_assert(u); @@ -480,7 +671,7 @@ static void stream_get_latency_callback(pa_pdispatch *pd, uint32_t command, PA_G if (command == PA_COMMAND_ERROR) pa_log("Failed to get latency."); else - pa_log("Protocol error 1."); + pa_log("Protocol error."); goto fail; } @@ -491,52 +682,90 @@ static void stream_get_latency_callback(pa_pdispatch *pd, uint32_t command, PA_G pa_tagstruct_get_timeval(t, &remote) < 0 || pa_tagstruct_gets64(t, &write_index) < 0 || pa_tagstruct_gets64(t, &read_index) < 0) { - pa_log("Invalid reply. (latency)"); + pa_log("Invalid reply."); goto fail; } +#ifdef TUNNEL_SINK + if (u->version >= 13) { + uint64_t underrun_for = 0, playing_for = 0; + + if (pa_tagstruct_getu64(t, &underrun_for) < 0 || + pa_tagstruct_getu64(t, &playing_for) < 0) { + pa_log("Invalid reply."); + goto fail; + } + } +#endif + + if (!pa_tagstruct_eof(t)) { + pa_log("Invalid reply."); + goto fail; + } + + if (tag < u->ignore_latency_before) { + request_latency(u); + return; + } + pa_gettimeofday(&now); + /* Calculate transport usec */ if (pa_timeval_cmp(&local, &remote) < 0 && pa_timeval_cmp(&remote, &now)) { /* local and remote seem to have synchronized clocks */ #ifdef TUNNEL_SINK - transport_usec = pa_timeval_diff(&remote, &local); + u->transport_usec = pa_timeval_diff(&remote, &local); #else - transport_usec = pa_timeval_diff(&now, &remote); + u->transport_usec = pa_timeval_diff(&now, &remote); #endif } else - transport_usec = pa_timeval_diff(&now, &local)/2; + u->transport_usec = pa_timeval_diff(&now, &local)/2; + u->transport_usec_valid = TRUE; + /* First, take the device's delay */ #ifdef TUNNEL_SINK - host_usec = sink_usec + transport_usec; + delay = (int64_t) sink_usec; + ss = &u->sink->sample_spec; #else - host_usec = source_usec + transport_usec; - if (host_usec > sink_usec) - host_usec -= sink_usec; - else - host_usec = 0; + delay = (int64_t) source_usec; + ss = &u->source->sample_spec; #endif + /* Add the length of our server-side buffer */ + if (write_index >= read_index) + delay += (int64_t) pa_bytes_to_usec(write_index-read_index, ss); + else + delay -= (int64_t) pa_bytes_to_usec(read_index-write_index, ss); + + /* Our measurements are already out of date, hence correct by the * + * transport latency */ #ifdef TUNNEL_SINK - k = pa_bytes_to_usec(u->counter - u->counter_delta, &u->sink->sample_spec); + delay -= (int64_t) transport_usec; +#else + delay += (int64_t) transport_usec; +#endif - if (k > host_usec) - k -= host_usec; - else - k = 0; + /* Now correct by what we have have read/written since we requested the update */ +#ifdef TUNNEL_SINK + delay += (int64_t) pa_bytes_to_usec(u->counter_delta, ss); #else - k = pa_bytes_to_usec(u->counter - u->counter_delta, &u->source->sample_spec); - k += host_usec; + delay -= (int64_t) pa_bytes_to_usec(u->counter_delta, ss); #endif - pa_smoother_put(u->smoother, pa_rtclock_usec(), k); +#ifdef TUNNEL_SINK + pa_asyncmsgq_send(u->sink->asyncmsgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_UPDATE_LATENCY, 0, delay, NULL); +#else + pa_asyncmsgq_send(u->source->asyncmsgq, PA_MSGOBJECT(u->source), SOURCE_MESSAGE_UPDATE_LATENCY, 0, delay, NULL); +#endif return; fail: + pa_module_unload_request(u->module); } +/* Called from main context */ static void request_latency(struct userdata *u) { pa_tagstruct *t; struct timeval now; @@ -558,10 +787,12 @@ static void request_latency(struct userdata *u) { pa_pstream_send_tagstruct(u->pstream, t); pa_pdispatch_register_reply(u->pdispatch, tag, DEFAULT_TIMEOUT, stream_get_latency_callback, u, NULL); + u->ignore_latency_before = tag; u->counter_delta = 0; } -static void timeout_callback(pa_mainloop_api *m, pa_time_event*e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) { +/* Called from main context */ +static void timeout_callback(pa_mainloop_api *m, pa_time_event*e, const struct timeval *tv, void *userdata) { struct userdata *u = userdata; struct timeval ntv; @@ -576,32 +807,7 @@ static void timeout_callback(pa_mainloop_api *m, pa_time_event*e, PA_GCC_UNUSED m->time_restart(e, &ntv); } -#ifdef TUNNEL_SINK -/* 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); */ - -/* 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; */ -/* } */ -#else -/* 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); */ - -/* 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; */ -/* } */ -#endif - +/* Called from main context */ static void update_description(struct userdata *u) { char *d; char un[128], hn[128]; @@ -616,8 +822,14 @@ static void update_description(struct userdata *u) { #ifdef TUNNEL_SINK pa_sink_set_description(u->sink, d); + pa_proplist_sets(u->sink->proplist, "tunnel.remote.user", u->user_name); + pa_proplist_sets(u->sink->proplist, "tunnel.remote.fqdn", u->server_fqdn); + pa_proplist_sets(u->sink->proplist, "tunnel.remote.description", u->device_description); #else pa_source_set_description(u->source, d); + pa_proplist_sets(u->source->proplist, "tunnel.remote.user", u->user_name); + pa_proplist_sets(u->source->proplist, "tunnel.remote.fqdn", u->server_fqdn); + pa_proplist_sets(u->source->proplist, "tunnel.remote.description", u->device_description); #endif pa_xfree(d); @@ -640,7 +852,8 @@ static void update_description(struct userdata *u) { pa_xfree(d); } -static void server_info_cb(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { +/* Called from main context */ +static void server_info_cb(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { struct userdata *u = userdata; pa_sample_spec ss; const char *server_name, *server_version, *user_name, *host_name, *default_sink_name, *default_source_name; @@ -653,7 +866,7 @@ static void server_info_cb(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uin if (command == PA_COMMAND_ERROR) pa_log("Failed to get info."); else - pa_log("Protocol error 6."); + pa_log("Protocol error."); goto fail; } @@ -665,7 +878,13 @@ static void server_info_cb(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uin pa_tagstruct_gets(t, &default_sink_name) < 0 || pa_tagstruct_gets(t, &default_source_name) < 0 || pa_tagstruct_getu32(t, &cookie) < 0) { - pa_log("Invalid reply. (get_server_info)"); + + pa_log("Parse failure"); + goto fail; + } + + if (!pa_tagstruct_eof(t)) { + pa_log("Packet too long"); goto fail; } @@ -685,24 +904,28 @@ fail: #ifdef TUNNEL_SINK -static void sink_info_cb(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { +/* Called from main context */ +static void sink_info_cb(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { struct userdata *u = userdata; uint32_t idx, owner_module, monitor_source, flags; const char *name, *description, *monitor_source_name, *driver; pa_sample_spec ss; pa_channel_map cm; pa_cvolume volume; - int mute; + pa_bool_t mute; pa_usec_t latency; + pa_proplist *pl; pa_assert(pd); pa_assert(u); + pl = pa_proplist_new(); + if (command != PA_COMMAND_REPLY) { if (command == PA_COMMAND_ERROR) pa_log("Failed to get info."); else - pa_log("Protocol error 5."); + pa_log("Protocol error."); goto fail; } @@ -719,10 +942,29 @@ static void sink_info_cb(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint3 pa_tagstruct_get_usec(t, &latency) < 0 || pa_tagstruct_gets(t, &driver) < 0 || pa_tagstruct_getu32(t, &flags) < 0) { - pa_log("Invalid reply. (get_sink_info)"); + + pa_log("Parse failure"); goto fail; } + if (u->version >= 13) { + pa_usec_t configured_latency; + + if (pa_tagstruct_get_proplist(t, pl) < 0 || + pa_tagstruct_get_usec(t, &configured_latency) < 0) { + + pa_log("Parse failure"); + goto fail; + } + } + + if (!pa_tagstruct_eof(t)) { + pa_log("Packet too long"); + goto fail; + } + + pa_proplist_free(pl); + if (!u->sink_name || strcmp(name, u->sink_name)) return; @@ -735,26 +977,31 @@ static void sink_info_cb(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint3 fail: pa_module_unload_request(u->module); + pa_proplist_free(pl); } -static void sink_input_info_cb(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { +/* Called from main context */ +static void sink_input_info_cb(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { struct userdata *u = userdata; uint32_t idx, owner_module, client, sink; pa_usec_t buffer_usec, sink_usec; const char *name, *driver, *resample_method; - int mute; + pa_bool_t mute; pa_sample_spec sample_spec; pa_channel_map channel_map; pa_cvolume volume; + pa_proplist *pl; pa_assert(pd); pa_assert(u); + pl = pa_proplist_new(); + if (command != PA_COMMAND_REPLY) { if (command == PA_COMMAND_ERROR) pa_log("Failed to get info."); else - pa_log("Protocol error 2."); + pa_log("Protocol error."); goto fail; } @@ -769,12 +1016,35 @@ static void sink_input_info_cb(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED pa_tagstruct_get_usec(t, &buffer_usec) < 0 || pa_tagstruct_get_usec(t, &sink_usec) < 0 || pa_tagstruct_gets(t, &resample_method) < 0 || - pa_tagstruct_gets(t, &driver) < 0 || - (u->version >= 11 && pa_tagstruct_get_boolean(t, &mute) < 0)) { - pa_log("Invalid reply. (get_info)"); + pa_tagstruct_gets(t, &driver) < 0) { + + pa_log("Parse failure"); goto fail; } + if (u->version >= 11) { + if (pa_tagstruct_get_boolean(t, &mute) < 0) { + + pa_log("Parse failure"); + goto fail; + } + } + + if (u->version >= 13) { + if (pa_tagstruct_get_proplist(t, pl) < 0) { + + pa_log("Parse failure"); + goto fail; + } + } + + if (!pa_tagstruct_eof(t)) { + pa_log("Packet too long"); + goto fail; + } + + pa_proplist_free(pl); + if (idx != u->device_index) return; @@ -794,28 +1064,33 @@ static void sink_input_info_cb(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED fail: pa_module_unload_request(u->module); + pa_proplist_free(pl); } #else -static void source_info_cb(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { +/* Called from main context */ +static void source_info_cb(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { struct userdata *u = userdata; uint32_t idx, owner_module, monitor_of_sink, flags; const char *name, *description, *monitor_of_sink_name, *driver; pa_sample_spec ss; pa_channel_map cm; pa_cvolume volume; - int mute; - pa_usec_t latency; + pa_bool_t mute; + pa_usec_t latency, configured_latency; + pa_proplist *pl; pa_assert(pd); pa_assert(u); + pl = pa_proplist_new(); + if (command != PA_COMMAND_REPLY) { if (command == PA_COMMAND_ERROR) pa_log("Failed to get info."); else - pa_log("Protocol error 5."); + pa_log("Protocol error."); goto fail; } @@ -832,10 +1107,27 @@ static void source_info_cb(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uin pa_tagstruct_get_usec(t, &latency) < 0 || pa_tagstruct_gets(t, &driver) < 0 || pa_tagstruct_getu32(t, &flags) < 0) { - pa_log("Invalid reply. (get_source_info)"); + + pa_log("Parse failure"); goto fail; } + if (u->version >= 13) { + if (pa_tagstruct_get_proplist(t, pl) < 0 || + pa_tagstruct_get_usec(t, &configured_latency) < 0) { + + pa_log("Parse failure"); + goto fail; + } + } + + if (!pa_tagstruct_eof(t)) { + pa_log("Packet too long"); + goto fail; + } + + pa_proplist_free(pl); + if (!u->source_name || strcmp(name, u->source_name)) return; @@ -848,10 +1140,12 @@ static void source_info_cb(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uin fail: pa_module_unload_request(u->module); + pa_proplist_free(pl); } #endif +/* Called from main context */ static void request_info(struct userdata *u) { pa_tagstruct *t; uint32_t tag; @@ -871,25 +1165,30 @@ static void request_info(struct userdata *u) { pa_pstream_send_tagstruct(u->pstream, t); pa_pdispatch_register_reply(u->pdispatch, tag, DEFAULT_TIMEOUT, sink_input_info_cb, u, NULL); - t = pa_tagstruct_new(NULL, 0); - pa_tagstruct_putu32(t, PA_COMMAND_GET_SINK_INFO); - pa_tagstruct_putu32(t, tag = u->ctag++); - pa_tagstruct_putu32(t, PA_INVALID_INDEX); - pa_tagstruct_puts(t, u->sink_name); - pa_pstream_send_tagstruct(u->pstream, t); - pa_pdispatch_register_reply(u->pdispatch, tag, DEFAULT_TIMEOUT, sink_info_cb, u, NULL); + if (u->sink_name) { + t = pa_tagstruct_new(NULL, 0); + pa_tagstruct_putu32(t, PA_COMMAND_GET_SINK_INFO); + pa_tagstruct_putu32(t, tag = u->ctag++); + pa_tagstruct_putu32(t, PA_INVALID_INDEX); + pa_tagstruct_puts(t, u->sink_name); + pa_pstream_send_tagstruct(u->pstream, t); + pa_pdispatch_register_reply(u->pdispatch, tag, DEFAULT_TIMEOUT, sink_info_cb, u, NULL); + } #else - t = pa_tagstruct_new(NULL, 0); - pa_tagstruct_putu32(t, PA_COMMAND_GET_SOURCE_INFO); - pa_tagstruct_putu32(t, tag = u->ctag++); - pa_tagstruct_putu32(t, PA_INVALID_INDEX); - pa_tagstruct_puts(t, u->source_name); - pa_pstream_send_tagstruct(u->pstream, t); - pa_pdispatch_register_reply(u->pdispatch, tag, DEFAULT_TIMEOUT, source_info_cb, u, NULL); + if (u->source_name) { + t = pa_tagstruct_new(NULL, 0); + pa_tagstruct_putu32(t, PA_COMMAND_GET_SOURCE_INFO); + pa_tagstruct_putu32(t, tag = u->ctag++); + pa_tagstruct_putu32(t, PA_INVALID_INDEX); + pa_tagstruct_puts(t, u->source_name); + pa_pstream_send_tagstruct(u->pstream, t); + pa_pdispatch_register_reply(u->pdispatch, tag, DEFAULT_TIMEOUT, source_info_cb, u, NULL); + } #endif } -static void command_subscribe_event(pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { +/* Called from main context */ +static void command_subscribe_event(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { struct userdata *u = userdata; pa_subscription_event_type_t e; uint32_t idx; @@ -919,6 +1218,7 @@ static void command_subscribe_event(pa_pdispatch *pd, PA_GCC_UNUSED uint32_t com request_info(u); } +/* Called from main context */ static void start_subscribe(struct userdata *u) { pa_tagstruct *t; uint32_t tag; @@ -938,7 +1238,8 @@ static void start_subscribe(struct userdata *u) { pa_pstream_send_tagstruct(u->pstream, t); } -static void create_stream_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { +/* Called from main context */ +static void create_stream_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { struct userdata *u = userdata; struct timeval ntv; #ifdef TUNNEL_SINK @@ -953,7 +1254,7 @@ static void create_stream_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UN if (command == PA_COMMAND_ERROR) pa_log("Failed to create stream."); else - pa_log("Protocol error 3."); + pa_log("Protocol error."); goto fail; } @@ -967,22 +1268,57 @@ static void create_stream_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UN if (u->version >= 9) { #ifdef TUNNEL_SINK - uint32_t maxlength, tlength, prebuf, minreq; + if (pa_tagstruct_getu32(t, &u->maxlength) < 0 || + pa_tagstruct_getu32(t, &u->tlength) < 0 || + pa_tagstruct_getu32(t, &u->prebuf) < 0 || + pa_tagstruct_getu32(t, &u->minreq) < 0) + goto parse_error; +#else + if (pa_tagstruct_getu32(t, &u->maxlength) < 0 || + pa_tagstruct_getu32(t, &u->fragsize) < 0) + goto parse_error; +#endif + } - if (pa_tagstruct_getu32(t, &maxlength) < 0 || - pa_tagstruct_getu32(t, &tlength) < 0 || - pa_tagstruct_getu32(t, &prebuf) < 0 || - pa_tagstruct_getu32(t, &minreq) < 0) + if (u->version >= 12) { + pa_sample_spec ss; + pa_channel_map cm; + uint32_t device_index; + const char *dn; + pa_bool_t suspended; + + if (pa_tagstruct_get_sample_spec(t, &ss) < 0 || + pa_tagstruct_get_channel_map(t, &cm) < 0 || + pa_tagstruct_getu32(t, &device_index) < 0 || + pa_tagstruct_gets(t, &dn) < 0 || + pa_tagstruct_get_boolean(t, &suspended) < 0) goto parse_error; + +#ifdef TUNNEL_SINK + pa_xfree(u->sink_name); + u->sink_name = pa_xstrdup(dn); #else - uint32_t maxlength, fragsize; + pa_xfree(u->source_name); + u->source_name = pa_xstrdup(dn); +#endif + } + + if (u->version >= 13) { + pa_usec_t usec; - if (pa_tagstruct_getu32(t, &maxlength) < 0 || - pa_tagstruct_getu32(t, &fragsize) < 0) + if (pa_tagstruct_get_usec(t, &usec) < 0) goto parse_error; + +#ifdef TUNNEL_SINK + pa_sink_set_latency_range(u->sink, usec + MIN_NETWORK_LATENCY_USEC, 0); +#else + pa_source_set_latency_range(u->source, usec + MIN_NETWORK_LATENCY_USEC, 0); #endif } + if (!pa_tagstruct_eof(t)) + goto parse_error; + start_subscribe(u); request_info(u); @@ -1006,8 +1342,10 @@ parse_error: fail: pa_module_unload_request(u->module); + } +/* Called from main context */ static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { struct userdata *u = userdata; pa_tagstruct *reply; @@ -1021,11 +1359,13 @@ static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t pa_assert(u->pdispatch == pd); if (command != PA_COMMAND_REPLY || - pa_tagstruct_getu32(t, &u->version) < 0) { + pa_tagstruct_getu32(t, &u->version) < 0 || + !pa_tagstruct_eof(t)) { + if (command == PA_COMMAND_ERROR) pa_log("Failed to authenticate"); else - pa_log("Protocol error 4."); + pa_log("Protocol error."); goto fail; } @@ -1036,6 +1376,15 @@ static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t goto fail; } + /* Starting with protocol version 13 the MSB of the version tag + reflects if shm is enabled for this connection or not. We don't + support SHM here at all, so we just ignore this. */ + + if (u->version >= 13) + u->version &= 0x7FFFFFFFU; + + pa_log_debug("Protocol version: remote %u, local %u", u->version, PA_PROTOCOL_VERSION); + #ifdef TUNNEL_SINK pa_snprintf(name, sizeof(name), "%s for %s@%s", u->sink_name, @@ -1051,16 +1400,42 @@ static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t reply = pa_tagstruct_new(NULL, 0); pa_tagstruct_putu32(reply, PA_COMMAND_SET_CLIENT_NAME); pa_tagstruct_putu32(reply, tag = u->ctag++); - pa_tagstruct_puts(reply, "PulseAudio"); + + if (u->version >= 13) { + pa_proplist *pl; + pl = pa_proplist_new(); + pa_init_proplist(pl); + pa_proplist_sets(pl, PA_PROP_APPLICATION_ID, "org.PulseAudio.PulseAudio"); + pa_proplist_sets(pl, PA_PROP_APPLICATION_VERSION, PACKAGE_VERSION); + pa_tagstruct_put_proplist(reply, pl); + pa_proplist_free(pl); + } else + pa_tagstruct_puts(reply, "PulseAudio"); + pa_pstream_send_tagstruct(u->pstream, reply); /* We ignore the server's reply here */ reply = pa_tagstruct_new(NULL, 0); + if (u->version < 13) + /* Only for older PA versions we need to fill in the maxlength */ + u->maxlength = 4*1024*1024; + +#ifdef TUNNEL_SINK + u->tlength = pa_usec_to_bytes(PA_USEC_PER_MSEC * DEFAULT_TLENGTH_MSEC, &u->sink->sample_spec); + u->minreq = pa_usec_to_bytes(PA_USEC_PER_MSEC * DEFAULT_MINREQ_MSEC, &u->sink->sample_spec); + u->prebuf = u->tlength; +#else + u->fragsize = pa_usec_to_bytes(PA_USEC_PER_MSEC * DEFAULT_FRAGSIZE_MSEC, &u->source->sample_spec); +#endif + #ifdef TUNNEL_SINK pa_tagstruct_putu32(reply, PA_COMMAND_CREATE_PLAYBACK_STREAM); pa_tagstruct_putu32(reply, tag = u->ctag++); - pa_tagstruct_puts(reply, name); + + if (u->version < 13) + pa_tagstruct_puts(reply, name); + pa_tagstruct_put_sample_spec(reply, &u->sink->sample_spec); pa_tagstruct_put_channel_map(reply, &u->sink->channel_map); pa_tagstruct_putu32(reply, PA_INVALID_INDEX); @@ -1076,7 +1451,10 @@ static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t #else pa_tagstruct_putu32(reply, PA_COMMAND_CREATE_RECORD_STREAM); pa_tagstruct_putu32(reply, tag = u->ctag++); - pa_tagstruct_puts(reply, name); + + if (u->version < 13) + pa_tagstruct_puts(reply, name); + pa_tagstruct_put_sample_spec(reply, &u->source->sample_spec); pa_tagstruct_put_channel_map(reply, &u->source->channel_map); pa_tagstruct_putu32(reply, PA_INVALID_INDEX); @@ -1086,16 +1464,31 @@ static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t pa_tagstruct_putu32(reply, u->fragsize); #endif - /* New flags added in 0.9.8 */ if (u->version >= 12) { - /* TODO: set these to useful values */ - pa_tagstruct_put_boolean(reply, FALSE); /*no_remap*/ - pa_tagstruct_put_boolean(reply, FALSE); /*no_remix*/ - pa_tagstruct_put_boolean(reply, FALSE); /*fix_format*/ - pa_tagstruct_put_boolean(reply, FALSE); /*fix_rate*/ - pa_tagstruct_put_boolean(reply, FALSE); /*fix_channels*/ - pa_tagstruct_put_boolean(reply, FALSE); /*no_move*/ - pa_tagstruct_put_boolean(reply, FALSE); /*variable_rate*/ + pa_tagstruct_put_boolean(reply, FALSE); /* no_remap */ + pa_tagstruct_put_boolean(reply, FALSE); /* no_remix */ + pa_tagstruct_put_boolean(reply, FALSE); /* fix_format */ + pa_tagstruct_put_boolean(reply, FALSE); /* fix_rate */ + pa_tagstruct_put_boolean(reply, FALSE); /* fix_channels */ + pa_tagstruct_put_boolean(reply, TRUE); /* no_move */ + pa_tagstruct_put_boolean(reply, FALSE); /* variable_rate */ + } + + if (u->version >= 13) { + pa_proplist *pl; + + pa_tagstruct_put_boolean(reply, FALSE); /* start muted/peak detect*/ + pa_tagstruct_put_boolean(reply, TRUE); /* adjust_latency */ + + pl = pa_proplist_new(); + pa_proplist_sets(pl, PA_PROP_MEDIA_NAME, name); + pa_proplist_sets(pl, PA_PROP_MEDIA_ROLE, "abstract"); + pa_tagstruct_put_proplist(reply, pl); + pa_proplist_free(pl); + +#ifndef TUNNEL_SINK + pa_tagstruct_putu32(reply, PA_INVALID_INDEX); /* direct on input */ +#endif } pa_pstream_send_tagstruct(u->pstream, reply); @@ -1109,6 +1502,7 @@ fail: pa_module_unload_request(u->module); } +/* Called from main context */ static void pstream_die_callback(pa_pstream *p, void *userdata) { struct userdata *u = userdata; @@ -1119,6 +1513,7 @@ static void pstream_die_callback(pa_pstream *p, void *userdata) { pa_module_unload_request(u->module); } +/* Called from main context */ static void pstream_packet_callback(pa_pstream *p, pa_packet *packet, const pa_creds *creds, void *userdata) { struct userdata *u = userdata; @@ -1134,6 +1529,7 @@ static void pstream_packet_callback(pa_pstream *p, pa_packet *packet, const pa_c } #ifndef TUNNEL_SINK +/* Called from main context */ static void pstream_memblock_callback(pa_pstream *p, uint32_t channel, int64_t offset, pa_seek_mode_t seek, const pa_memchunk *chunk, void *userdata) { struct userdata *u = userdata; @@ -1149,12 +1545,12 @@ static void pstream_memblock_callback(pa_pstream *p, uint32_t channel, int64_t o pa_asyncmsgq_send(u->source->asyncmsgq, PA_MSGOBJECT(u->source), SOURCE_MESSAGE_POST, PA_UINT_TO_PTR(seek), offset, chunk); - u->counter += chunk->length; u->counter_delta += chunk->length; } #endif +/* Called from main context */ static void on_connection(pa_socket_client *sc, pa_iochannel *io, void *userdata) { struct userdata *u = userdata; pa_tagstruct *t; @@ -1211,10 +1607,7 @@ static void on_connection(pa_socket_client *sc, pa_iochannel *io, void *userdata #ifdef TUNNEL_SINK -static int sink_get_volume(pa_sink *sink) { - return 0; -} - +/* Called from main context */ static int sink_set_volume(pa_sink *sink) { struct userdata *u; pa_tagstruct *t; @@ -1234,10 +1627,7 @@ static int sink_set_volume(pa_sink *sink) { return 0; } -static int sink_get_mute(pa_sink *sink) { - return 0; -} - +/* Called from main context */ static int sink_set_mute(pa_sink *sink) { struct userdata *u; pa_tagstruct *t; @@ -1262,6 +1652,7 @@ static int sink_set_mute(pa_sink *sink) { #endif +/* Called from main context */ static int load_key(struct userdata *u, const char*fn) { pa_assert(u); @@ -1293,7 +1684,7 @@ int pa__init(pa_module*m) { struct userdata *u = NULL; pa_sample_spec ss; pa_channel_map map; - char *t, *dn = NULL; + char *dn = NULL; #ifdef TUNNEL_SINK pa_sink_new_data data; #else @@ -1303,14 +1694,13 @@ int pa__init(pa_module*m) { pa_assert(m); if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { - pa_log("failed to parse module arguments"); + pa_log("Failed to parse module arguments"); goto fail; } - u = pa_xnew0(struct userdata, 1); - m->userdata = u; - u->module = m; + m->userdata = u = pa_xnew0(struct userdata, 1); u->core = m->core; + u->module = m; u->client = NULL; u->pdispatch = NULL; u->pstream = NULL; @@ -1328,6 +1718,11 @@ int pa__init(pa_module*m) { u->device_index = u->channel = PA_INVALID_INDEX; u->auth_cookie_in_property = FALSE; u->time_event = NULL; + u->ignore_latency_before = 0; + u->transport_usec = 0; + u->transport_usec_valid = FALSE; + u->remote_suspended = u->remote_corked = FALSE; + u->counter = u->counter_delta = 0; u->rtpoll = pa_rtpoll_new(); pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll); @@ -1336,18 +1731,18 @@ int pa__init(pa_module*m) { goto fail; if (!(u->server_name = pa_xstrdup(pa_modargs_get_value(ma, "server", NULL)))) { - pa_log("no server specified."); + pa_log("No server specified."); goto fail; } ss = m->core->default_sample_spec; if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0) { - pa_log("invalid sample format specification"); + pa_log("Invalid sample format specification"); goto fail; } if (!(u->client = pa_socket_client_new_string(m->core->mainloop, u->server_name, PA_NATIVE_DEFAULT_PORT))) { - pa_log("failed to connect to server '%s'", u->server_name); + pa_log("Failed to connect to server '%s'", u->server_name); goto fail; } @@ -1365,6 +1760,10 @@ int pa__init(pa_module*m) { 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); + pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "%s%s%s", pa_strempty(u->sink_name), u->sink_name ? " on " : "", u->server_name); + pa_proplist_sets(data.proplist, "tunnel.remote.server", u->server_name); + if (u->sink_name) + pa_proplist_sets(data.proplist, "tunnel.remote.sink", u->sink_name); 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); @@ -1377,16 +1776,15 @@ 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_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->refresh_volume = u->sink->refresh_muted = FALSE; + + pa_sink_set_latency_range(u->sink, MIN_NETWORK_LATENCY_USEC, 0); + 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)); - pa_xfree(t); #else @@ -1400,6 +1798,10 @@ int pa__init(pa_module*m) { 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); + pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "%s%s%s", pa_strempty(u->source_name), u->source_name ? " on " : "", u->server_name); + pa_proplist_sets(data.proplist, "tunnel.remote.server", u->server_name); + if (u->source_name) + pa_proplist_sets(data.proplist, "tunnel.remote.source", u->source_name); u->source = pa_source_new(m->core, &data, PA_SOURCE_NETWORK|PA_SOURCE_LATENCY); pa_source_new_data_done(&data); @@ -1410,30 +1812,26 @@ 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->userdata = u; + + pa_source_set_latency_range(u->source, MIN_NETWORK_LATENCY_USEC, 0); 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)); - pa_xfree(t); #endif pa_xfree(dn); u->time_event = NULL; - u->maxlength = pa_usec_to_bytes(PA_USEC_PER_MSEC * DEFAULT_MAXLENGTH_MSEC, &ss); + u->maxlength = 0; #ifdef TUNNEL_SINK - u->tlength = pa_usec_to_bytes(PA_USEC_PER_MSEC * DEFAULT_TLENGTH_MSEC, &ss); - u->minreq = pa_usec_to_bytes(PA_USEC_PER_MSEC * DEFAULT_MINREQ_MSEC, &ss); - u->prebuf = u->tlength; + u->tlength = u->minreq = u->prebuf = 0; #else - u->fragsize = pa_usec_to_bytes(PA_USEC_PER_MSEC * DEFAULT_FRAGSIZE_MSEC, &ss); + u->fragsize = 0; #endif - u->counter = u->counter_delta = 0; pa_smoother_set_time_offset(u->smoother, pa_rtclock_usec()); if (!(u->thread = pa_thread_new(thread_func, u))) { diff --git a/src/modules/module-volume-restore.c b/src/modules/module-volume-restore.c index 336bcac9..d862c203 100644 --- a/src/modules/module-volume-restore.c +++ b/src/modules/module-volume-restore.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -102,7 +100,7 @@ static pa_cvolume* parse_volume(const char *s, pa_cvolume *v) { return NULL; k = strtol(s, &p, 0); - if (k <= 0 || k > PA_CHANNELS_MAX) + if (k <= 0 || k > (long) PA_CHANNELS_MAX) return NULL; v->channels = (unsigned) k; @@ -488,7 +486,6 @@ 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_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; @@ -496,6 +493,9 @@ int pa__init(pa_module*m) { m->userdata = u; + if (!(u->table_file = pa_state_path(pa_modargs_get_value(ma, "table", DEFAULT_VOLUME_TABLE_FILE)))) + goto fail; + if (pa_modargs_get_value_boolean(ma, "restore_device", &restore_device) < 0 || pa_modargs_get_value_boolean(ma, "restore_volume", &restore_volume) < 0) { pa_log("restore_volume= and restore_device= expect boolean arguments"); @@ -513,12 +513,12 @@ int pa__init(pa_module*m) { u->subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_SINK_INPUT|PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT, subscribe_callback, u); if (restore_device) { - u->sink_input_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_NEW], (pa_hook_cb_t) sink_input_new_hook_callback, u); - u->source_output_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_NEW], (pa_hook_cb_t) source_output_new_hook_callback, u); + u->sink_input_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_NEW], PA_HOOK_EARLY, (pa_hook_cb_t) sink_input_new_hook_callback, u); + u->source_output_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_NEW], PA_HOOK_EARLY, (pa_hook_cb_t) source_output_new_hook_callback, u); } if (restore_volume) - u->sink_input_fixate_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_FIXATE], (pa_hook_cb_t) sink_input_fixate_hook_callback, u); + u->sink_input_fixate_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_FIXATE], PA_HOOK_EARLY, (pa_hook_cb_t) sink_input_fixate_hook_callback, u); pa_modargs_free(ma); return 0; diff --git a/src/modules/module-waveout.c b/src/modules/module-waveout.c index f8bae02f..b452c3bf 100644 --- a/src/modules/module-waveout.c +++ b/src/modules/module-waveout.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/modules/module-x11-bell.c b/src/modules/module-x11-bell.c index 761b82a9..f7be48f7 100644 --- a/src/modules/module-x11-bell.c +++ b/src/modules/module-x11-bell.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -45,14 +43,24 @@ #include "module-x11-bell-symdef.h" PA_MODULE_AUTHOR("Lennart Poettering"); -PA_MODULE_DESCRIPTION("X11 Bell interceptor"); +PA_MODULE_DESCRIPTION("X11 bell interceptor"); PA_MODULE_VERSION(PACKAGE_VERSION); PA_MODULE_LOAD_ONCE(FALSE); PA_MODULE_USAGE("sink=<sink to connect to> sample=<sample name> display=<X11 display>"); +static const char* const valid_modargs[] = { + "sink", + "sample", + "display", + NULL +}; + struct userdata { pa_core *core; + pa_module *module; + int xkb_event_base; + char *sink_name; char *scache_item; @@ -60,14 +68,7 @@ struct userdata { pa_x11_client *x11_client; }; -static const char* const valid_modargs[] = { - "sink", - "sample", - "display", - NULL -}; - -static int x11_event_callback(pa_x11_wrapper *w, XEvent *e, void *userdata) { +static int x11_event_cb(pa_x11_wrapper *w, XEvent *e, void *userdata) { XkbBellNotifyEvent *bne; struct userdata *u = userdata; @@ -89,6 +90,25 @@ static int x11_event_callback(pa_x11_wrapper *w, XEvent *e, void *userdata) { return 1; } +static void x11_kill_cb(pa_x11_wrapper *w, void *userdata) { + struct userdata *u = userdata; + + pa_assert(w); + pa_assert(u); + pa_assert(u->x11_wrapper == w); + + if (u->x11_client) + pa_x11_client_free(u->x11_client); + + if (u->x11_wrapper) + pa_x11_wrapper_unref(u->x11_wrapper); + + u->x11_client = NULL; + u->x11_wrapper = NULL; + + pa_module_unload_request(u->module); +} + int pa__init(pa_module*m) { struct userdata *u = NULL; @@ -105,7 +125,8 @@ int pa__init(pa_module*m) { m->userdata = u = pa_xnew(struct userdata, 1); u->core = m->core; - u->scache_item = pa_xstrdup(pa_modargs_get_value(ma, "sample", "x11-bell")); + u->module = m; + u->scache_item = pa_xstrdup(pa_modargs_get_value(ma, "sample", "bell-window-system")); u->sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL)); u->x11_client = NULL; @@ -133,7 +154,7 @@ int pa__init(pa_module*m) { XkbSetAutoResetControls(pa_x11_wrapper_get_display(u->x11_wrapper), XkbAudibleBellMask, &auto_ctrls, &auto_values); XkbChangeEnabledControls(pa_x11_wrapper_get_display(u->x11_wrapper), XkbUseCoreKbd, XkbAudibleBellMask, 0); - u->x11_client = pa_x11_client_new(u->x11_wrapper, x11_event_callback, u); + u->x11_client = pa_x11_client_new(u->x11_wrapper, x11_event_cb, x11_kill_cb, u); pa_modargs_free(ma); @@ -153,11 +174,9 @@ void pa__done(pa_module*m) { pa_assert(m); - if (!m->userdata) + if (!(u = m->userdata)) return; - u = m->userdata; - pa_xfree(u->scache_item); pa_xfree(u->sink_name); diff --git a/src/modules/module-x11-publish.c b/src/modules/module-x11-publish.c index 429c2a69..705d90f4 100644 --- a/src/modules/module-x11-publish.c +++ b/src/modules/module-x11-publish.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -54,7 +52,7 @@ #include "module-x11-publish-symdef.h" PA_MODULE_AUTHOR("Lennart Poettering"); -PA_MODULE_DESCRIPTION("X11 Credential Publisher"); +PA_MODULE_DESCRIPTION("X11 credential publisher"); PA_MODULE_VERSION(PACKAGE_VERSION); PA_MODULE_LOAD_ONCE(FALSE); PA_MODULE_USAGE("display=<X11 display>"); @@ -69,16 +67,39 @@ static const char* const valid_modargs[] = { struct userdata { pa_core *core; - pa_x11_wrapper *x11_wrapper; + pa_module *module; + char *id; uint8_t auth_cookie[PA_NATIVE_COOKIE_LENGTH]; - int auth_cookie_in_property; + pa_bool_t auth_cookie_in_property; + + pa_x11_wrapper *x11_wrapper; + pa_x11_client *x11_client; }; +static void x11_kill_cb(pa_x11_wrapper *w, void *userdata) { + struct userdata *u = userdata; + + pa_assert(w); + pa_assert(u); + pa_assert(u->x11_wrapper == w); + + if (u->x11_client) + pa_x11_client_free(u->x11_client); + + if (u->x11_wrapper) + pa_x11_wrapper_unref(u->x11_wrapper); + + u->x11_client = NULL; + u->x11_wrapper = NULL; + + pa_module_unload_request(u->module); +} + static int load_key(struct userdata *u, const char*fn) { pa_assert(u); - u->auth_cookie_in_property = 0; + u->auth_cookie_in_property = FALSE; if (!fn && pa_authkey_prop_get(u->core, PA_NATIVE_COOKIE_PROPERTY_NAME, u->auth_cookie, sizeof(u->auth_cookie)) >= 0) { pa_log_debug("using already loaded auth cookie."); @@ -96,7 +117,7 @@ static int load_key(struct userdata *u, const char*fn) { pa_log_debug("Loading cookie from disk."); if (pa_authkey_prop_put(u->core, PA_NATIVE_COOKIE_PROPERTY_NAME, u->auth_cookie, sizeof(u->auth_cookie)) >= 0) - u->auth_cookie_in_property = 1; + u->auth_cookie_in_property = TRUE; return 0; } @@ -117,10 +138,13 @@ int pa__init(pa_module*m) { goto fail; } - m->userdata = u = pa_xmalloc(sizeof(struct userdata)); + m->userdata = u = pa_xnew(struct userdata, 1); u->core = m->core; + u->module = m; u->id = NULL; - u->auth_cookie_in_property = 0; + u->auth_cookie_in_property = FALSE; + u->x11_client = NULL; + u->x11_wrapper = NULL; if (load_key(u, pa_modargs_get_value(ma, "cookie", NULL)) < 0) goto fail; @@ -152,7 +176,10 @@ int pa__init(pa_module*m) { pa_x11_set_prop(pa_x11_wrapper_get_display(u->x11_wrapper), "PULSE_COOKIE", pa_hexstr(u->auth_cookie, sizeof(u->auth_cookie), hx, sizeof(hx))); + u->x11_client = pa_x11_client_new(u->x11_wrapper, NULL, x11_kill_cb, u); + pa_modargs_free(ma); + return 0; fail: @@ -160,6 +187,7 @@ fail: pa_modargs_free(ma); pa__done(m); + return -1; } @@ -171,6 +199,9 @@ void pa__done(pa_module*m) { if (!(u = m->userdata)) return; + if (u->x11_client) + pa_x11_client_free(u->x11_client); + if (u->x11_wrapper) { char t[256]; @@ -185,10 +216,9 @@ void pa__done(pa_module*m) { pa_x11_del_prop(pa_x11_wrapper_get_display(u->x11_wrapper), "PULSE_COOKIE"); XSync(pa_x11_wrapper_get_display(u->x11_wrapper), False); } - } - if (u->x11_wrapper) pa_x11_wrapper_unref(u->x11_wrapper); + } if (u->auth_cookie_in_property) pa_authkey_prop_unref(m->core, PA_NATIVE_COOKIE_PROPERTY_NAME); diff --git a/src/modules/module-x11-xsmp.c b/src/modules/module-x11-xsmp.c index e9efa096..696826d8 100644 --- a/src/modules/module-x11-xsmp.c +++ b/src/modules/module-x11-xsmp.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -42,6 +40,7 @@ #include <pulsecore/namereg.h> #include <pulsecore/log.h> #include <pulsecore/core-util.h> +#include <pulsecore/x11wrap.h> #include "module-x11-xsmp-symdef.h" @@ -49,20 +48,36 @@ PA_MODULE_AUTHOR("Lennart Poettering"); PA_MODULE_DESCRIPTION("X11 session management"); PA_MODULE_VERSION(PACKAGE_VERSION); PA_MODULE_LOAD_ONCE(TRUE); +PA_MODULE_USAGE("session_manager=<session manager string> display=<X11 display>"); -static int ice_in_use = 0; +static pa_bool_t ice_in_use = FALSE; static const char* const valid_modargs[] = { + "session_manager", + "display", NULL }; +struct userdata { + pa_core *core; + pa_module *module; + pa_client *client; + SmcConn connection; + pa_x11_wrapper *x11; +}; + static void die_cb(SmcConn connection, SmPointer client_data){ - pa_core *c = PA_CORE(client_data); + struct userdata *u = client_data; + pa_assert(u); + + pa_log_debug("Got die message from XSMP."); - pa_log_debug("Got die message from XSM. Exiting..."); + pa_x11_wrapper_kill(u->x11); - pa_core_assert_ref(c); - c->mainloop->quit(c->mainloop, 0); + pa_x11_wrapper_unref(u->x11); + u->x11 = NULL; + + pa_module_unload_request(u->module); } static void save_complete_cb(SmcConn connection, SmPointer client_data) { @@ -86,12 +101,15 @@ static void ice_io_cb(pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_event_fla } static void new_ice_connection(IceConn connection, IcePointer client_data, Bool opening, IcePointer *watch_data) { - pa_core *c = client_data; - - pa_assert(c); + struct pa_core *c = client_data; if (opening) - *watch_data = c->mainloop->io_new(c->mainloop, IceConnectionNumber(connection), PA_IO_EVENT_INPUT, ice_io_cb, connection); + *watch_data = c->mainloop->io_new( + c->mainloop, + IceConnectionNumber(connection), + PA_IO_EVENT_INPUT, + ice_io_cb, + connection); else c->mainloop->io_free(*watch_data); } @@ -99,12 +117,13 @@ static void new_ice_connection(IceConn connection, IcePointer client_data, Bool int pa__init(pa_module*m) { pa_modargs *ma = NULL; - char t[256], *vendor, *client_id; + char t[256], *vendor, *client_id, *k; SmcCallbacks callbacks; SmProp prop_program, prop_user; SmProp *prop_list[2]; SmPropValue val_program, val_user; - SmcConn connection; + struct userdata *u; + const char *e; pa_assert(m); @@ -114,21 +133,33 @@ int pa__init(pa_module*m) { } IceAddConnectionWatch(new_ice_connection, m->core); - ice_in_use = 1; + ice_in_use = TRUE; + + m->userdata = u = pa_xnew(struct userdata, 1); + u->core = m->core; + u->module = m; + u->client = NULL; + u->connection = NULL; + u->x11 = NULL; if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { pa_log("Failed to parse module arguments"); goto fail; } - if (!getenv("SESSION_MANAGER")) { + if (!(u->x11 = pa_x11_wrapper_get(m->core, pa_modargs_get_value(ma, "display", NULL)))) + goto fail; + + e = pa_modargs_get_value(ma, "session_manager", NULL); + + if (!e && !getenv("SESSION_MANAGER")) { pa_log("X11 session manager not running."); goto fail; } memset(&callbacks, 0, sizeof(callbacks)); callbacks.die.callback = die_cb; - callbacks.die.client_data = m->core; + callbacks.die.client_data = u; callbacks.save_yourself.callback = save_yourself_cb; callbacks.save_yourself.client_data = m->core; callbacks.save_complete.callback = save_complete_cb; @@ -136,8 +167,8 @@ int pa__init(pa_module*m) { callbacks.shutdown_cancelled.callback = shutdown_cancelled_cb; callbacks.shutdown_cancelled.client_data = m->core; - if (!(m->userdata = connection = SmcOpenConnection( - NULL, m->core, + if (!(u->connection = SmcOpenConnection( + (char*) e, m->core, SmProtoMajor, SmProtoMinor, SmcSaveYourselfProcMask | SmcDieProcMask | SmcSaveCompleteProcMask | SmcShutdownCancelledProcMask, &callbacks, NULL, &client_id, @@ -164,9 +195,16 @@ int pa__init(pa_module*m) { prop_user.vals = &val_user; prop_list[1] = &prop_user; - SmcSetProperties(connection, PA_ELEMENTSOF(prop_list), prop_list); + SmcSetProperties(u->connection, PA_ELEMENTSOF(prop_list), prop_list); + + pa_log_info("Connected to session manager '%s' as '%s'.", vendor = SmcVendor(u->connection), client_id); + k = pa_sprintf_malloc("XSMP Session on %s as %s", vendor, client_id); + u->client = pa_client_new(u->core, __FILE__, k); + pa_xfree(k); + + pa_proplist_sets(u->client->proplist, "xsmp.vendor", vendor); + pa_proplist_sets(u->client->proplist, "xsmp.client.id", client_id); - pa_log_info("Connected to session manager '%s' as '%s'.", vendor = SmcVendor(connection), client_id); free(vendor); free(client_id); @@ -184,13 +222,26 @@ fail: } void pa__done(pa_module*m) { + struct userdata *u; + pa_assert(m); - if (m->userdata) - SmcCloseConnection(m->userdata, 0, NULL); + if ((u = m->userdata)) { + + if (u->connection) + SmcCloseConnection(u->connection, 0, NULL); + + if (u->client) + pa_client_free(u->client); + + if (u->x11) + pa_x11_wrapper_unref(u->x11); + + pa_xfree(u); + } if (ice_in_use) { IceRemoveConnectionWatch(new_ice_connection, m->core); - ice_in_use = 0; + ice_in_use = FALSE; } } diff --git a/src/modules/module-zeroconf-discover.c b/src/modules/module-zeroconf-discover.c index 4e76f448..2fc81370 100644 --- a/src/modules/module-zeroconf-discover.c +++ b/src/modules/module-zeroconf-discover.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -164,8 +162,7 @@ static void resolver_cb( pa_module *m; ss = u->core->default_sample_spec; - pa_assert_se(pa_channel_map_init_auto(&cm, ss.channels, PA_CHANNEL_MAP_AUX)); - pa_channel_map_init_auto(&cm, ss.channels, PA_CHANNEL_MAP_DEFAULT); + pa_channel_map_init_extend(&cm, ss.channels, PA_CHANNEL_MAP_DEFAULT); for (l = txt; l; l = l->next) { char *key, *value; @@ -190,10 +187,8 @@ static void resolver_cb( avahi_free(value); } - if (!channel_map_set && cm.channels != ss.channels) { - pa_assert_se(pa_channel_map_init_auto(&cm, ss.channels, PA_CHANNEL_MAP_AUX)); - pa_channel_map_init_auto(&cm, ss.channels, PA_CHANNEL_MAP_DEFAULT); - } + if (!channel_map_set && cm.channels != ss.channels) + pa_channel_map_init_extend(&cm, ss.channels, PA_CHANNEL_MAP_DEFAULT); if (!pa_sample_spec_valid(&ss)) { pa_log("Service '%s' contains an invalid sample specification.", name); @@ -237,7 +232,7 @@ static void resolver_cb( t, dname, pa_channel_map_snprint(cmt, sizeof(cmt), &cm)); - pa_log_debug("Loading module-tunnel-%s with arguments '%s'", module_name, args); + pa_log_debug("Loading %s with arguments '%s'", module_name, args); if ((m = pa_module_load(u->core, module_name, args))) { tnl->module_index = m->index; diff --git a/src/modules/module-zeroconf-publish.c b/src/modules/module-zeroconf-publish.c index 6ed8e3d9..cb9c1285 100644 --- a/src/modules/module-zeroconf-publish.c +++ b/src/modules/module-zeroconf-publish.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -578,12 +576,12 @@ 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_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_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->sink_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PUT], PA_HOOK_LATE, (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_LATE, (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_LATE, (pa_hook_cb_t) device_unlink_cb, u); + u->source_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_PUT], PA_HOOK_LATE, (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_LATE, (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_LATE, (pa_hook_cb_t) device_unlink_cb, u); u->main_entry_group = NULL; diff --git a/src/modules/oss-util.c b/src/modules/oss-util.c index e29f0eda..2791e165 100644 --- a/src/modules/oss-util.c +++ b/src/modules/oss-util.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/modules/oss-util.h b/src/modules/oss-util.h index 8fea805c..654f7bba 100644 --- a/src/modules/oss-util.h +++ b/src/modules/oss-util.h @@ -1,8 +1,6 @@ #ifndef fooossutilhfoo #define fooossutilhfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/modules/rtp/module-rtp-send.c b/src/modules/rtp/module-rtp-send.c index 3a526c14..d0d06c4d 100644 --- a/src/modules/rtp/module-rtp-send.c +++ b/src/modules/rtp/module-rtp-send.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/modules/rtp/rtp.c b/src/modules/rtp/rtp.c index 5c299844..5a33ebc2 100644 --- a/src/modules/rtp/rtp.c +++ b/src/modules/rtp/rtp.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/modules/rtp/rtp.h b/src/modules/rtp/rtp.h index a366d7a6..a2728f05 100644 --- a/src/modules/rtp/rtp.h +++ b/src/modules/rtp/rtp.h @@ -1,8 +1,6 @@ #ifndef foortphfoo #define foortphfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/modules/rtp/sap.c b/src/modules/rtp/sap.c index 123bc494..5d9b58fa 100644 --- a/src/modules/rtp/sap.c +++ b/src/modules/rtp/sap.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/modules/rtp/sap.h b/src/modules/rtp/sap.h index db096d61..69c757cb 100644 --- a/src/modules/rtp/sap.h +++ b/src/modules/rtp/sap.h @@ -1,8 +1,6 @@ #ifndef foosaphfoo #define foosaphfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/modules/rtp/sdp.c b/src/modules/rtp/sdp.c index 9265a200..cef90433 100644 --- a/src/modules/rtp/sdp.c +++ b/src/modules/rtp/sdp.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/modules/rtp/sdp.h b/src/modules/rtp/sdp.h index 7c91fca6..933a602b 100644 --- a/src/modules/rtp/sdp.h +++ b/src/modules/rtp/sdp.h @@ -1,8 +1,6 @@ #ifndef foosdphfoo #define foosdphfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulse/.gitignore b/src/pulse/.gitignore new file mode 100644 index 00000000..67020331 --- /dev/null +++ b/src/pulse/.gitignore @@ -0,0 +1 @@ +version.h diff --git a/src/pulse/browser.c b/src/pulse/browser.c index 5e4aa87b..1a3f657f 100644 --- a/src/pulse/browser.c +++ b/src/pulse/browser.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulse/browser.h b/src/pulse/browser.h index b039ca33..c4e0a17e 100644 --- a/src/pulse/browser.h +++ b/src/pulse/browser.h @@ -1,8 +1,6 @@ #ifndef foobrowserhfoo #define foobrowserhfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulse/cdecl.h b/src/pulse/cdecl.h index 922ad276..8c5b2d0f 100644 --- a/src/pulse/cdecl.h +++ b/src/pulse/cdecl.h @@ -1,8 +1,6 @@ #ifndef foopulsecdeclhfoo #define foopulsecdeclhfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulse/channelmap.c b/src/pulse/channelmap.c index 2b35ee75..7348b32e 100644 --- a/src/pulse/channelmap.c +++ b/src/pulse/channelmap.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -396,6 +394,34 @@ pa_channel_map* pa_channel_map_init_auto(pa_channel_map *m, unsigned channels, p } } +pa_channel_map* pa_channel_map_init_extend(pa_channel_map *m, unsigned channels, pa_channel_map_def_t def) { + unsigned c; + + pa_assert(m); + pa_assert(channels > 0); + pa_assert(channels <= PA_CHANNELS_MAX); + + pa_channel_map_init(m); + + for (c = channels; c > 0; c--) { + + if (pa_channel_map_init_auto(m, c, def)) { + unsigned i = 0; + + for (; c < channels; c++) { + + m->map[c] = PA_CHANNEL_POSITION_AUX0 + i; + i++; + } + + m->channels = channels; + + return m; + } + } + + return NULL; +} const char* pa_channel_position_to_string(pa_channel_position_t pos) { diff --git a/src/pulse/channelmap.h b/src/pulse/channelmap.h index 00d3eb0d..2551eae9 100644 --- a/src/pulse/channelmap.h +++ b/src/pulse/channelmap.h @@ -1,8 +1,6 @@ #ifndef foochannelmaphfoo #define foochannelmaphfoo -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -168,10 +166,18 @@ pa_channel_map* pa_channel_map_init_mono(pa_channel_map *m); /** Initialize the specified channel map for stereophonic audio and return a pointer to it */ pa_channel_map* pa_channel_map_init_stereo(pa_channel_map *m); -/** Initialize the specified channel map for the specified number - * of channels using default labels and return a pointer to it. */ +/** Initialize the specified channel map for the specified number of + * channels using default labels and return a pointer to it. This call + * will fail (return NULL) if there is no default channel map known for this + * specific number of channels and mapping. */ pa_channel_map* pa_channel_map_init_auto(pa_channel_map *m, unsigned channels, pa_channel_map_def_t def); +/** Similar to pa_channel_map_init_auto() but instead of failing if no + * default mapping is known with the specified parameters it will + * synthesize a mapping based on a known mapping with fewer channels + * and fill up the rest with AUX0...AUX31 channels \since 0.9.11 */ +pa_channel_map* pa_channel_map_init_extend(pa_channel_map *m, unsigned channels, pa_channel_map_def_t def); + /** Return a text label for the specified channel position */ const char* pa_channel_position_to_string(pa_channel_position_t pos) PA_GCC_PURE; diff --git a/src/pulse/client-conf-x11.c b/src/pulse/client-conf-x11.c index 49df4b6c..393a7cd3 100644 --- a/src/pulse/client-conf-x11.c +++ b/src/pulse/client-conf-x11.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulse/client-conf-x11.h b/src/pulse/client-conf-x11.h index 56cd406d..f2f40e03 100644 --- a/src/pulse/client-conf-x11.h +++ b/src/pulse/client-conf-x11.h @@ -1,8 +1,6 @@ #ifndef fooclientconfx11hfoo #define fooclientconfx11hfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulse/client-conf.c b/src/pulse/client-conf.c index 75f44182..2ead871f 100644 --- a/src/pulse/client-conf.c +++ b/src/pulse/client-conf.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -58,7 +56,7 @@ static const pa_client_conf default_conf = { .default_sink = NULL, .default_source = NULL, .default_server = NULL, - .autospawn = FALSE, + .autospawn = TRUE, .disable_shm = FALSE, .cookie_file = NULL, .cookie_valid = FALSE, @@ -178,11 +176,11 @@ int pa_client_conf_env(pa_client_conf *c) { int pa_client_conf_load_cookie(pa_client_conf* c) { pa_assert(c); - c->cookie_valid = FALSE; - if (!c->cookie_file) return -1; + c->cookie_valid = FALSE; + if (pa_authkey_load_auto(c->cookie_file, c->cookie, sizeof(c->cookie)) < 0) return -1; diff --git a/src/pulse/client-conf.h b/src/pulse/client-conf.h index 7cc975e6..699279aa 100644 --- a/src/pulse/client-conf.h +++ b/src/pulse/client-conf.h @@ -1,8 +1,6 @@ #ifndef fooclientconfhfoo #define fooclientconfhfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulse/client.conf.in b/src/pulse/client.conf.in index 2bc8a7c8..749e9688 100644 --- a/src/pulse/client.conf.in +++ b/src/pulse/client.conf.in @@ -1,5 +1,3 @@ -# $Id$ -# # This file is part of PulseAudio. # # PulseAudio is free software; you can redistribute it and/or modify @@ -25,7 +23,7 @@ ; default-source = ; default-server = -; autospawn = no +; autospawn = yes ; daemon-binary = @PA_BINARY@ ; extra-arguments = --log-target=syslog --exit-idle-time=5 diff --git a/src/pulse/context.c b/src/pulse/context.c index f9f021af..f56cb241 100644 --- a/src/pulse/context.c +++ b/src/pulse/context.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -70,6 +68,7 @@ #include <pulsecore/socket-util.h> #include <pulsecore/creds.h> #include <pulsecore/macro.h> +#include <pulsecore/proplist-util.h> #include "internal.h" @@ -103,7 +102,9 @@ static void unlock_autospawn_lock_file(pa_context *c) { if (c->autospawn_lock_fd >= 0) { char *lf; - lf = pa_runtime_path(AUTOSPAWN_LOCK); + if (!(lf = pa_runtime_path(AUTOSPAWN_LOCK))) + pa_log_warn("Cannot unlock autospawn because runtime path is no more."); + pa_unlock_lockfile(lf, c->autospawn_lock_fd); pa_xfree(lf); @@ -167,6 +168,7 @@ pa_context *pa_context_new_with_proplist(pa_mainloop_api *mainloop, const char * c->autospawn_lock_fd = -1; memset(&c->spawn_api, 0, sizeof(c->spawn_api)); c->do_autospawn = FALSE; + c->do_shm = FALSE; #ifndef MSG_NOSIGNAL #ifdef SIGPIPE @@ -175,10 +177,10 @@ pa_context *pa_context_new_with_proplist(pa_mainloop_api *mainloop, const char * #endif c->conf = pa_client_conf_new(); - pa_client_conf_load(c->conf, NULL); #ifdef HAVE_X11 pa_client_conf_from_x11(c->conf, NULL); #endif + pa_client_conf_load(c->conf, NULL); pa_client_conf_env(c->conf); if (!(c->mempool = pa_mempool_new(!c->conf->disable_shm))) { @@ -422,6 +424,7 @@ static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t switch(c->state) { case PA_CONTEXT_AUTHORIZING: { pa_tagstruct *reply; + pa_bool_t shm_on_remote; if (pa_tagstruct_getu32(t, &c->version) < 0 || !pa_tagstruct_eof(t)) { @@ -435,10 +438,22 @@ static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t goto finish; } + /* Starting with protocol version 13 the MSB of the version + tag reflects if shm is available for this connection or + not. */ + if (c->version >= 13) { + shm_on_remote = !!(c->version & 0x80000000U); + c->version &= 0x7FFFFFFFU; + } + + pa_log_debug("Protocol version: remote %u, local %u", c->version, PA_PROTOCOL_VERSION); + /* Enable shared memory support if possible */ - if (c->version >= 10 && - pa_mempool_is_shared(c->mempool) && - c->is_local) { + if (c->do_shm) + if (c->version < 10 || (c->version >= 13 && !shm_on_remote)) + c->do_shm = FALSE; + + if (c->do_shm) { /* Only enable SHM if both sides are owned by the same * user. This is a security measure because otherwise @@ -446,12 +461,14 @@ static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t #ifdef HAVE_CREDS const pa_creds *creds; - if ((creds = pa_pdispatch_creds(pd))) - if (getuid() == creds->uid) - pa_pstream_enable_shm(c->pstream, TRUE); + if (!(creds = pa_pdispatch_creds(pd)) || getuid() != creds->uid) + c->do_shm = FALSE; #endif } + pa_log_debug("Negotiated SHM: %s", pa_yes_no(c->do_shm)); + pa_pstream_enable_shm(c->pstream, c->do_shm); + reply = pa_tagstruct_command(c, PA_COMMAND_SET_CLIENT_NAME, &tag); if (c->version >= 13) { @@ -510,7 +527,16 @@ static void setup_context(pa_context *c, pa_iochannel *io) { 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); + + c->do_shm = + pa_mempool_is_shared(c->mempool) && + c->is_local; + + pa_log_debug("SHM possible: %s", pa_yes_no(c->do_shm)); + + /* Starting with protocol version 13 we use the MSB of the version + * tag for informing the other side if we could do SHM or not */ + pa_tagstruct_putu32(t, PA_PROTOCOL_VERSION | (c->do_shm ? 0x80000000U : 0)); pa_tagstruct_put_arbitrary(t, c->conf->cookie, sizeof(c->conf->cookie)); #ifdef HAVE_CREDS @@ -621,6 +647,7 @@ static int context_connect_spawn(pa_context *c) { /* Parent */ pa_assert_se(pa_close(fds[1]) == 0); + fds[1] = -1; r = waitpid(pid, &status, 0); @@ -749,11 +776,15 @@ static char *get_legacy_runtime_dir(void) { p = pa_sprintf_malloc("/tmp/pulse-%s", u); - if (stat(p, &st) < 0) + if (stat(p, &st) < 0) { + pa_xfree(p); return NULL; + } - if (st.st_uid != getuid()) + if (st.st_uid != getuid()) { + pa_xfree(p); return NULL; + } return p; } @@ -809,7 +840,7 @@ int pa_context_connect( /* The system wide instance */ 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 */ + /* The old per-user instance path. This is supported only to ease 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); @@ -818,14 +849,20 @@ int pa_context_connect( } /* The per-user instance */ - c->server_list = pa_strlist_prepend(c->server_list, ufn = pa_runtime_path(PA_NATIVE_DEFAULT_UNIX_SOCKET)); - pa_xfree(ufn); + if ((ufn = pa_runtime_path(PA_NATIVE_DEFAULT_UNIX_SOCKET))) { + c->server_list = pa_strlist_prepend(c->server_list, ufn); + 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; - lf = pa_runtime_path(AUTOSPAWN_LOCK); + if (!(lf = pa_runtime_path(AUTOSPAWN_LOCK))) { + pa_context_fail(c, PA_ERR_ACCESS); + goto finish; + } + pa_assert(c->autospawn_lock_fd <= 0); c->autospawn_lock_fd = pa_lock_lockfile(lf); pa_xfree(lf); @@ -1193,85 +1230,3 @@ pa_operation *pa_context_proplist_remove(pa_context *c, const char *const keys[] 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); - } -} diff --git a/src/pulse/context.h b/src/pulse/context.h index 143508f4..8dff7642 100644 --- a/src/pulse/context.h +++ b/src/pulse/context.h @@ -1,8 +1,6 @@ #ifndef foocontexthfoo #define foocontexthfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulse/def.h b/src/pulse/def.h index 10722329..a91c1031 100644 --- a/src/pulse/def.h +++ b/src/pulse/def.h @@ -1,8 +1,6 @@ #ifndef foodefhfoo #define foodefhfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulse/error.c b/src/pulse/error.c index 78f0da95..29690c89 100644 --- a/src/pulse/error.c +++ b/src/pulse/error.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulse/error.h b/src/pulse/error.h index 44a2e5ec..9f9e3d33 100644 --- a/src/pulse/error.h +++ b/src/pulse/error.h @@ -1,8 +1,6 @@ #ifndef fooerrorhfoo #define fooerrorhfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulse/gccmacro.h b/src/pulse/gccmacro.h index 032c3bae..e4062033 100644 --- a/src/pulse/gccmacro.h +++ b/src/pulse/gccmacro.h @@ -1,8 +1,6 @@ #ifndef foopulsegccmacrohfoo #define foopulsegccmacrohfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulse/glib-mainloop.c b/src/pulse/glib-mainloop.c index b7a7537a..6ddb0faa 100644 --- a/src/pulse/glib-mainloop.c +++ b/src/pulse/glib-mainloop.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulse/glib-mainloop.h b/src/pulse/glib-mainloop.h index a4e06ea0..60fd61a3 100644 --- a/src/pulse/glib-mainloop.h +++ b/src/pulse/glib-mainloop.h @@ -1,8 +1,6 @@ #ifndef fooglibmainloophfoo #define fooglibmainloophfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulse/internal.h b/src/pulse/internal.h index d346e945..9ed541d1 100644 --- a/src/pulse/internal.h +++ b/src/pulse/internal.h @@ -1,8 +1,6 @@ #ifndef foointernalhfoo #define foointernalhfoo -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -75,8 +73,9 @@ struct pa_context { pa_mempool *mempool; - pa_bool_t is_local; - pa_bool_t do_autospawn; + pa_bool_t is_local:1; + pa_bool_t do_autospawn:1; + pa_bool_t do_shm:1; int autospawn_lock_fd; pa_spawn_api spawn_api; @@ -106,6 +105,8 @@ struct pa_stream { pa_context *context; pa_mainloop_api *mainloop; + uint32_t direct_on_input; + pa_stream_direction_t direction; pa_stream_state_t state; pa_stream_flags_t flags; @@ -115,8 +116,13 @@ struct pa_stream { pa_proplist *proplist; + pa_bool_t channel_valid:1; + pa_bool_t suspended:1; + pa_bool_t corked:1; + pa_bool_t timing_info_valid:1; + pa_bool_t auto_timing_update_requested:1; + uint32_t channel; - pa_bool_t channel_valid; uint32_t syncid; uint32_t stream_index; @@ -125,17 +131,13 @@ struct pa_stream { uint32_t device_index; char *device_name; - pa_bool_t suspended; pa_memchunk peek_memchunk; void *peek_data; pa_memblockq *record_memblockq; - pa_bool_t corked; - /* Store latest latency info */ pa_timing_info timing_info; - pa_bool_t timing_info_valid; /* Use to make sure that time advances monotonically */ pa_usec_t previous_time; @@ -150,7 +152,6 @@ struct pa_stream { /* Latency interpolation stuff */ pa_time_event *auto_timing_update_event; - pa_bool_t auto_timing_update_requested; pa_smoother *smoother; @@ -232,6 +233,4 @@ 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 diff --git a/src/pulse/introspect.c b/src/pulse/introspect.c index 857e82b4..4be2c62a 100644 --- a/src/pulse/introspect.c +++ b/src/pulse/introspect.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -106,7 +104,8 @@ static void context_get_server_info_callback(pa_pdispatch *pd, uint32_t command, pa_tagstruct_get_sample_spec(t, &i.sample_spec) < 0 || pa_tagstruct_gets(t, &i.default_sink_name) < 0 || pa_tagstruct_gets(t, &i.default_source_name) < 0 || - pa_tagstruct_getu32(t, &i.cookie) < 0) { + pa_tagstruct_getu32(t, &i.cookie) < 0 || + !pa_tagstruct_eof(t)) { pa_context_fail(o->context, PA_ERR_PROTOCOL); goto finish; @@ -1342,7 +1341,7 @@ pa_operation* pa_context_remove_autoload_by_index(pa_context *c, uint32_t idx, p return o; } -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) { +pa_operation* pa_context_move_sink_input_by_name(pa_context *c, uint32_t idx, const char *sink_name, pa_context_success_cb_t cb, void* userdata) { pa_operation *o; pa_tagstruct *t; uint32_t tag; @@ -1392,7 +1391,7 @@ pa_operation* pa_context_move_sink_input_by_index(pa_context *c, uint32_t idx, u return o; } -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) { +pa_operation* pa_context_move_source_output_by_name(pa_context *c, uint32_t idx, const char *source_name, pa_context_success_cb_t cb, void* userdata) { pa_operation *o; pa_tagstruct *t; uint32_t tag; @@ -1442,7 +1441,7 @@ pa_operation* pa_context_move_source_output_by_index(pa_context *c, uint32_t idx return o; } -pa_operation* pa_context_suspend_sink_by_name(pa_context *c, char *sink_name, int suspend, pa_context_success_cb_t cb, void* userdata) { +pa_operation* pa_context_suspend_sink_by_name(pa_context *c, const char *sink_name, int suspend, pa_context_success_cb_t cb, void* userdata) { pa_operation *o; pa_tagstruct *t; uint32_t tag; @@ -1489,7 +1488,7 @@ pa_operation* pa_context_suspend_sink_by_index(pa_context *c, uint32_t idx, int return o; } -pa_operation* pa_context_suspend_source_by_name(pa_context *c, char *source_name, int suspend, pa_context_success_cb_t cb, void* userdata) { +pa_operation* pa_context_suspend_source_by_name(pa_context *c, const char *source_name, int suspend, pa_context_success_cb_t cb, void* userdata) { pa_operation *o; pa_tagstruct *t; uint32_t tag; diff --git a/src/pulse/introspect.h b/src/pulse/introspect.h index d185a3a6..c8c13a75 100644 --- a/src/pulse/introspect.h +++ b/src/pulse/introspect.h @@ -1,8 +1,6 @@ #ifndef foointrospecthfoo #define foointrospecthfoo -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -261,7 +259,7 @@ pa_operation* pa_context_set_sink_mute_by_index(pa_context *c, uint32_t idx, int 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); +pa_operation* pa_context_suspend_sink_by_name(pa_context *c, const 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); @@ -433,7 +431,7 @@ pa_operation* pa_context_get_sink_input_info(pa_context *c, uint32_t idx, pa_sin pa_operation* pa_context_get_sink_input_info_list(pa_context *c, pa_sink_input_info_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_name(pa_context *c, uint32_t idx, char *sink_name, pa_context_success_cb_t cb, void* userdata); +pa_operation* pa_context_move_sink_input_by_name(pa_context *c, uint32_t idx, const 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); @@ -479,13 +477,13 @@ pa_operation* pa_context_get_source_output_info(pa_context *c, uint32_t idx, pa_ pa_operation* pa_context_get_source_output_info_list(pa_context *c, pa_source_output_info_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); +pa_operation* pa_context_move_source_output_by_name(pa_context *c, uint32_t idx, const 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 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); +pa_operation* pa_context_suspend_source_by_name(pa_context *c, const 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); diff --git a/src/pulse/mainloop-api.c b/src/pulse/mainloop-api.c index dda51297..90aff164 100644 --- a/src/pulse/mainloop-api.c +++ b/src/pulse/mainloop-api.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulse/mainloop-api.h b/src/pulse/mainloop-api.h index 985806e6..53c7411e 100644 --- a/src/pulse/mainloop-api.h +++ b/src/pulse/mainloop-api.h @@ -1,8 +1,6 @@ #ifndef foomainloopapihfoo #define foomainloopapihfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulse/mainloop-signal.c b/src/pulse/mainloop-signal.c index 91c6bf6d..9161dec4 100644 --- a/src/pulse/mainloop-signal.c +++ b/src/pulse/mainloop-signal.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulse/mainloop-signal.h b/src/pulse/mainloop-signal.h index bdb0f738..a6c16f2f 100644 --- a/src/pulse/mainloop-signal.h +++ b/src/pulse/mainloop-signal.h @@ -1,8 +1,6 @@ #ifndef foomainloopsignalhfoo #define foomainloopsignalhfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulse/mainloop.c b/src/pulse/mainloop.c index ad4e4e97..aaed3caf 100644 --- a/src/pulse/mainloop.c +++ b/src/pulse/mainloop.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulse/mainloop.h b/src/pulse/mainloop.h index db2797fb..907e94a7 100644 --- a/src/pulse/mainloop.h +++ b/src/pulse/mainloop.h @@ -1,8 +1,6 @@ #ifndef foomainloophfoo #define foomainloophfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulse/operation.c b/src/pulse/operation.c index 6b5c142a..13b470a8 100644 --- a/src/pulse/operation.c +++ b/src/pulse/operation.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulse/operation.h b/src/pulse/operation.h index 97d1c6b8..188e2cb9 100644 --- a/src/pulse/operation.h +++ b/src/pulse/operation.h @@ -1,8 +1,6 @@ #ifndef foooperationhfoo #define foooperationhfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulse/proplist.c b/src/pulse/proplist.c index 33bd274f..74aea20a 100644 --- a/src/pulse/proplist.c +++ b/src/pulse/proplist.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulse/proplist.h b/src/pulse/proplist.h index f433ec62..f75cca54 100644 --- a/src/pulse/proplist.h +++ b/src/pulse/proplist.h @@ -1,8 +1,6 @@ #ifndef foopulseproplisthfoo #define foopulseproplisthfoo -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -24,6 +22,8 @@ USA. ***/ +#include <sys/types.h> + #include <pulse/cdecl.h> #include <pulse/gccmacro.h> @@ -40,16 +40,25 @@ PA_C_DECL_BEGIN * media.icon_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 + * event.mouse.x + * event.mouse.y + * event.mouse.hpos + * event.mouse.vpos + * event.mouse.button + * window.name + * window.id + * window.icon + * window.icon_name + * window.x11.display + * window.x11.screen + * window.x11.monitor + * window.x11.xid * application.name "Rhythmbox Media Player" * application.id "org.gnome.rhythmbox" * application.version * application.icon * application.icon_name + * application.language * application.process.id * application.process.binary * application.process.user @@ -60,14 +69,14 @@ PA_C_DECL_BEGIN * device.bus_path * device.serial * device.vendor_product_id - * device.class sound, modem, monitor, filter + * device.class sound, modem, monitor, filter, abstract * 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 + * device.bufferin.buffer_size + * device.bufferin.fragment_size */ - #define PA_PROP_MEDIA_NAME "media.name" #define PA_PROP_MEDIA_TITLE "media.title" #define PA_PROP_MEDIA_ARTIST "media.artist" @@ -77,11 +86,19 @@ PA_C_DECL_BEGIN #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_HPOS "event.mouse.hpos" +#define PA_PROP_EVENT_MOUSE_VPOS "event.mouse.vpos" #define PA_PROP_EVENT_MOUSE_BUTTON "event.mouse.button" +#define PA_PROP_WINDOW_NAME "window.name" +#define PA_PROP_WINDOW_ID "window.id" +#define PA_PROP_WINDOW_ICON "window.icon" +#define PA_PROP_WINDOW_ICON_NAME "window.icon_name" +#define PA_PROP_WINDOW_X11_DISPLAY "window.x11.display" +#define PA_PROP_WINDOW_X11_SCREEN "window.x11.screen" +#define PA_PROP_WINDOW_X11_MONITOR "window.x11.monitor" +#define PA_PROP_WINDOW_X11_XID "window.x11.xid" #define PA_PROP_APPLICATION_NAME "application.name" #define PA_PROP_APPLICATION_ID "application.id" #define PA_PROP_APPLICATION_VERSION "application.version" @@ -174,9 +191,10 @@ int pa_proplist_unset_many(pa_proplist *p, const char * const keys[]); * 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 */ + * iteration through the list -- except for deleting the current + * looked at entry. 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 diff --git a/src/pulse/pulseaudio.h b/src/pulse/pulseaudio.h index 88d1275b..ee8a4bb8 100644 --- a/src/pulse/pulseaudio.h +++ b/src/pulse/pulseaudio.h @@ -1,8 +1,6 @@ #ifndef foopulseaudiohfoo #define foopulseaudiohfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulse/sample.c b/src/pulse/sample.c index 43340f20..4aef5bb0 100644 --- a/src/pulse/sample.c +++ b/src/pulse/sample.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulse/sample.h b/src/pulse/sample.h index dedd72de..1ba3f871 100644 --- a/src/pulse/sample.h +++ b/src/pulse/sample.h @@ -1,8 +1,6 @@ #ifndef foosamplehfoo #define foosamplehfoo -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -115,10 +113,10 @@ PA_C_DECL_BEGIN #endif /** Maximum number of allowed channels */ -#define PA_CHANNELS_MAX 32 +#define PA_CHANNELS_MAX 32U /** Maximum allowed sample rate */ -#define PA_RATE_MAX (48000*4) +#define PA_RATE_MAX (48000U*4U) /** Sample format */ typedef enum pa_sample_format { diff --git a/src/pulse/scache.c b/src/pulse/scache.c index e43a0b9f..5e31e7af 100644 --- a/src/pulse/scache.c +++ b/src/pulse/scache.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -29,8 +27,11 @@ #include <stdio.h> #include <string.h> +#include <pulse/utf8.h> + #include <pulsecore/pstream-util.h> #include <pulsecore/macro.h> +#include <pulsecore/proplist-util.h> #include "internal.h" @@ -39,6 +40,7 @@ int pa_stream_connect_upload(pa_stream *s, size_t length) { pa_tagstruct *t; uint32_t tag; + const char *name; pa_assert(s); pa_assert(PA_REFCNT_VALUE(s) >= 1); @@ -46,6 +48,11 @@ int pa_stream_connect_upload(pa_stream *s, size_t length) { PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_UNCONNECTED, PA_ERR_BADSTATE); PA_CHECK_VALIDITY(s->context, length > 0, PA_ERR_INVALID); + if (!(name = pa_proplist_gets(s->proplist, PA_PROP_EVENT_ID))) + name = pa_proplist_gets(s->proplist, PA_PROP_MEDIA_NAME); + + PA_CHECK_VALIDITY(s->context, name && *name && pa_utf8_valid(name), PA_ERR_INVALID); + pa_stream_ref(s); s->direction = PA_STREAM_UPLOAD; @@ -53,9 +60,7 @@ int pa_stream_connect_upload(pa_stream *s, size_t length) { t = pa_tagstruct_command(s->context, PA_COMMAND_CREATE_UPLOAD_STREAM, &tag); - if (s->context->version < 13) - pa_tagstruct_puts(t, pa_proplist_gets(s->proplist, PA_PROP_MEDIA_NAME)); - + pa_tagstruct_puts(t, name); pa_tagstruct_put_sample_spec(t, &s->sample_spec); pa_tagstruct_put_channel_map(t, &s->channel_map); pa_tagstruct_putu32(t, length); diff --git a/src/pulse/scache.h b/src/pulse/scache.h index 46d86a19..f380b4e8 100644 --- a/src/pulse/scache.h +++ b/src/pulse/scache.h @@ -1,8 +1,6 @@ #ifndef fooscachehfoo #define fooscachehfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulse/simple.c b/src/pulse/simple.c index 1072fb4d..70396835 100644 --- a/src/pulse/simple.c +++ b/src/pulse/simple.c @@ -1,6 +1,4 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulse/simple.h b/src/pulse/simple.h index 7fca6ac3..a1380a0a 100644 --- a/src/pulse/simple.h +++ b/src/pulse/simple.h @@ -1,8 +1,6 @@ #ifndef foosimplehfoo #define foosimplehfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulse/stream.c b/src/pulse/stream.c index 4268fd6f..3bee7a05 100644 --- a/src/pulse/stream.c +++ b/src/pulse/stream.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -107,6 +105,8 @@ pa_stream *pa_stream_new_with_proplist( s->sample_spec = *ss; s->channel_map = *map; + s->direct_on_input = PA_INVALID_INDEX; + s->proplist = p ? pa_proplist_copy(p) : pa_proplist_new(); if (name) pa_proplist_sets(s->proplist, PA_PROP_MEDIA_NAME, name); @@ -475,6 +475,8 @@ void pa_command_stream_suspended(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUS if (s->suspended || s->corked) pa_smoother_pause(s->smoother, x); + else + pa_smoother_resume(s->smoother, x); } request_auto_timing_update(s, TRUE); @@ -625,7 +627,7 @@ static void invalidate_indexes(pa_stream *s, pa_bool_t r, pa_bool_t w) { s->write_index_not_before = s->context->ctag; if (s->timing_info_valid) - s->timing_info.write_index_corrupt = 1; + s->timing_info.write_index_corrupt = TRUE; /* pa_log("write_index invalidated"); */ } @@ -634,7 +636,7 @@ static void invalidate_indexes(pa_stream *s, pa_bool_t r, pa_bool_t w) { s->read_index_not_before = s->context->ctag; if (s->timing_info_valid) - s->timing_info.read_index_corrupt = 1; + s->timing_info.read_index_corrupt = TRUE; /* pa_log("read_index invalidated"); */ } @@ -838,6 +840,7 @@ static int create_stream( pa_assert(direction == PA_STREAM_PLAYBACK || direction == PA_STREAM_RECORD); PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_UNCONNECTED, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY(s->context, s->direct_on_input == PA_INVALID_INDEX || direction == PA_STREAM_RECORD, PA_ERR_BADSTATE); PA_CHECK_VALIDITY(s->context, !(flags & ~(PA_STREAM_START_CORKED| PA_STREAM_INTERPOLATE_TIMING| PA_STREAM_NOT_MONOTONOUS| @@ -860,8 +863,8 @@ static int create_stream( * when they are passed but actually not supported. This makes * client development easier */ - PA_CHECK_VALIDITY(s->context, direction != PA_STREAM_PLAYBACK || !(flags & (PA_STREAM_START_MUTED)), PA_ERR_INVALID); - PA_CHECK_VALIDITY(s->context, direction != PA_STREAM_RECORD || !(flags & (PA_STREAM_PEAK_DETECT)), PA_ERR_INVALID); + PA_CHECK_VALIDITY(s->context, direction == PA_STREAM_PLAYBACK || !(flags & (PA_STREAM_START_MUTED)), PA_ERR_INVALID); + PA_CHECK_VALIDITY(s->context, direction == PA_STREAM_RECORD || !(flags & (PA_STREAM_PEAK_DETECT)), PA_ERR_INVALID); PA_CHECK_VALIDITY(s->context, !volume || volume->channels == s->sample_spec.channels, PA_ERR_INVALID); PA_CHECK_VALIDITY(s->context, !sync_stream || (direction == PA_STREAM_PLAYBACK && sync_stream->direction == PA_STREAM_PLAYBACK), PA_ERR_INVALID); @@ -954,6 +957,9 @@ static int create_stream( PA_TAG_BOOLEAN, flags & PA_STREAM_ADJUST_LATENCY, PA_TAG_PROPLIST, s->proplist, PA_TAG_INVALID); + + if (s->direction == PA_STREAM_RECORD) + pa_tagstruct_putu32(t, s->direct_on_input); } pa_pstream_send_tagstruct(s->context->pstream, t); @@ -2227,3 +2233,26 @@ pa_operation *pa_stream_proplist_remove(pa_stream *s, const char *const keys[], return o; } + +int pa_stream_set_monitor_stream(pa_stream *s, uint32_t sink_input_idx) { + pa_assert(s); + pa_assert(PA_REFCNT_VALUE(s) >= 1); + + PA_CHECK_VALIDITY(s->context, sink_input_idx != PA_INVALID_INDEX, PA_ERR_INVALID); + PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_UNCONNECTED, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY(s->context, s->context->version >= 13, PA_ERR_NOTSUPPORTED); + + s->direct_on_input = sink_input_idx; + + return 0; +} + +uint32_t pa_stream_get_monitor_stream(pa_stream *s) { + pa_assert(s); + pa_assert(PA_REFCNT_VALUE(s) >= 1); + + PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->direct_on_input != PA_INVALID_INDEX, PA_ERR_BADSTATE, PA_INVALID_INDEX); + PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->context->version >= 13, PA_ERR_NOTSUPPORTED, PA_INVALID_INDEX); + + return s->direct_on_input; +} diff --git a/src/pulse/stream.h b/src/pulse/stream.h index ebb45f2b..55f36b7f 100644 --- a/src/pulse/stream.h +++ b/src/pulse/stream.h @@ -1,8 +1,6 @@ #ifndef foostreamhfoo #define foostreamhfoo -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -527,14 +525,14 @@ const pa_buffer_attr* pa_stream_get_buffer_attr(pa_stream *s); * server is at least PulseAudio 0.9.8. \since 0.9.8 */ pa_operation *pa_stream_set_buffer_attr(pa_stream *s, const pa_buffer_attr *attr, pa_stream_success_cb_t cb, void *userdata); -/* Change the stream sampling rate during playback. You need to pass +/** Change the stream sampling rate during playback. You need to pass * PA_STREAM_VARIABLE_RATE in the flags parameter of * pa_stream_connect() if you plan to use this function. Only valid * after the stream has been connected successfully and if the server * 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 +/** 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 @@ -542,10 +540,20 @@ pa_operation *pa_stream_update_sample_rate(pa_stream *s, uint32_t rate, pa_strea * 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 +/** 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); +/** For record streams connected to a monitor source: monitor only a + * very specific sink input of the sink. Thus function needs to be + * called before pa_stream_connect_record() is called. \since + * 0.9.11 */ +int pa_stream_set_monitor_stream(pa_stream *s, uint32_t sink_input_idx); + +/** Return what has been set with pa_stream_set_monitor_stream() + * ebfore. \since 0.9.11 */ +uint32_t pa_stream_get_monitor_stream(pa_stream *s); + PA_C_DECL_END #endif diff --git a/src/pulse/subscribe.c b/src/pulse/subscribe.c index 0c5686b7..d9c06b7e 100644 --- a/src/pulse/subscribe.c +++ b/src/pulse/subscribe.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulse/subscribe.h b/src/pulse/subscribe.h index c37ead57..0e4be8c3 100644 --- a/src/pulse/subscribe.h +++ b/src/pulse/subscribe.h @@ -1,8 +1,6 @@ #ifndef foosubscribehfoo #define foosubscribehfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulse/thread-mainloop.c b/src/pulse/thread-mainloop.c index e8c956bb..6b66696c 100644 --- a/src/pulse/thread-mainloop.c +++ b/src/pulse/thread-mainloop.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulse/thread-mainloop.h b/src/pulse/thread-mainloop.h index ea08f72a..521e29b0 100644 --- a/src/pulse/thread-mainloop.h +++ b/src/pulse/thread-mainloop.h @@ -1,8 +1,6 @@ #ifndef foothreadmainloophfoo #define foothreadmainloophfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulse/timeval.c b/src/pulse/timeval.c index 180e0159..9708a735 100644 --- a/src/pulse/timeval.c +++ b/src/pulse/timeval.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -140,7 +138,7 @@ struct timeval* pa_timeval_add(struct timeval *tv, pa_usec_t v) { tv->tv_usec += (suseconds_t) v; /* Normalize */ - while (tv->tv_usec >= PA_USEC_PER_SEC) { + while ((unsigned) tv->tv_usec >= PA_USEC_PER_SEC) { tv->tv_sec++; tv->tv_usec -= PA_USEC_PER_SEC; } diff --git a/src/pulse/timeval.h b/src/pulse/timeval.h index 09d53974..ee398296 100644 --- a/src/pulse/timeval.h +++ b/src/pulse/timeval.h @@ -1,8 +1,6 @@ #ifndef footimevalhfoo #define footimevalhfoo -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -34,12 +32,12 @@ PA_C_DECL_BEGIN -#define PA_MSEC_PER_SEC 1000 -#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 +#define PA_MSEC_PER_SEC ((pa_usec_t) 1000ULL) +#define PA_USEC_PER_SEC ((pa_usec_t) 1000000ULL) +#define PA_NSEC_PER_SEC ((pa_usec_t) 1000000000ULL) +#define PA_USEC_PER_MSEC ((pa_usec_t) 1000ULL) +#define PA_NSEC_PER_MSEC ((pa_usec_t) 1000000ULL) +#define PA_NSEC_PER_USEC ((pa_usec_t) 1000ULL) struct timeval; diff --git a/src/pulse/utf8.c b/src/pulse/utf8.c index b2f6c3bd..119be542 100644 --- a/src/pulse/utf8.c +++ b/src/pulse/utf8.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulse/utf8.h b/src/pulse/utf8.h index 840c74e0..6c7e7a5b 100644 --- a/src/pulse/utf8.h +++ b/src/pulse/utf8.h @@ -1,8 +1,6 @@ #ifndef fooutf8hfoo #define fooutf8hfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulse/util.c b/src/pulse/util.c index b6f57b96..c0911b51 100644 --- a/src/pulse/util.c +++ b/src/pulse/util.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulse/util.h b/src/pulse/util.h index 666ccce4..cf06d4fd 100644 --- a/src/pulse/util.h +++ b/src/pulse/util.h @@ -1,8 +1,6 @@ #ifndef fooutilhfoo #define fooutilhfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulse/version.h.in b/src/pulse/version.h.in index dc0f8e3b..e6226c44 100644 --- a/src/pulse/version.h.in +++ b/src/pulse/version.h.in @@ -1,8 +1,6 @@ #ifndef fooversionhfoo /*-*-C-*-*/ #define fooversionhfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulse/volume.c b/src/pulse/volume.c index 33ab1c5f..70d6f86a 100644 --- a/src/pulse/volume.c +++ b/src/pulse/volume.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulse/volume.h b/src/pulse/volume.h index e7ceb0d7..3befb1da 100644 --- a/src/pulse/volume.h +++ b/src/pulse/volume.h @@ -1,8 +1,6 @@ #ifndef foovolumehfoo #define foovolumehfoo -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -103,10 +101,10 @@ PA_C_DECL_BEGIN typedef uint32_t pa_volume_t; /** Normal volume (100%) */ -#define PA_VOLUME_NORM ((pa_volume_t) 0x10000) +#define PA_VOLUME_NORM ((pa_volume_t) 0x10000U) /** Muted volume (0%) */ -#define PA_VOLUME_MUTED ((pa_volume_t) 0) +#define PA_VOLUME_MUTED ((pa_volume_t) 0U) /** A structure encapsulating a per-channel volume */ typedef struct pa_cvolume { @@ -169,7 +167,7 @@ double pa_sw_volume_to_linear(pa_volume_t v) PA_GCC_CONST; #define PA_DECIBEL_MININFTY ((double) -INFINITY) #else /** This value is used as minus infinity when using pa_volume_{to,from}_dB(). */ -#define PA_DECIBEL_MININFTY ((double) -200) +#define PA_DECIBEL_MININFTY ((double) -200.0) #endif PA_C_DECL_END diff --git a/src/pulse/xmalloc.c b/src/pulse/xmalloc.c index a64761bf..90237013 100644 --- a/src/pulse/xmalloc.c +++ b/src/pulse/xmalloc.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulse/xmalloc.h b/src/pulse/xmalloc.h index 62a450dc..c453138b 100644 --- a/src/pulse/xmalloc.h +++ b/src/pulse/xmalloc.h @@ -1,8 +1,6 @@ #ifndef foomemoryhfoo #define foomemoryhfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/asyncmsgq.c b/src/pulsecore/asyncmsgq.c index eba1c2cb..5c7af2a8 100644 --- a/src/pulsecore/asyncmsgq.c +++ b/src/pulsecore/asyncmsgq.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/asyncmsgq.h b/src/pulsecore/asyncmsgq.h index 93f1ce86..1f38207a 100644 --- a/src/pulsecore/asyncmsgq.h +++ b/src/pulsecore/asyncmsgq.h @@ -1,8 +1,6 @@ #ifndef foopulseasyncmsgqhfoo #define foopulseasyncmsgqhfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/asyncq.c b/src/pulsecore/asyncq.c index 8e0dfbc3..03e9f0df 100644 --- a/src/pulsecore/asyncq.c +++ b/src/pulsecore/asyncq.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/asyncq.h b/src/pulsecore/asyncq.h index 4cdf8cd0..e6847ab8 100644 --- a/src/pulsecore/asyncq.h +++ b/src/pulsecore/asyncq.h @@ -1,8 +1,6 @@ #ifndef foopulseasyncqhfoo #define foopulseasyncqhfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/atomic.h b/src/pulsecore/atomic.h index ad3dca30..a91c4d56 100644 --- a/src/pulsecore/atomic.h +++ b/src/pulsecore/atomic.h @@ -1,12 +1,11 @@ #ifndef foopulseatomichfoo #define foopulseatomichfoo -/* $Id$ */ - /*** This file is part of PulseAudio. - Copyright 2006 Lennart Poettering + Copyright 2006-2008 Lennart Poettering + Copyright 2008 Nokia Corporation PulseAudio is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as @@ -380,9 +379,9 @@ static inline int pa_atomic_dec(pa_atomic_t *a) { /* Returns non-zero when the operation was successful. */ static inline int pa_atomic_cmpxchg(pa_atomic_t *a, int old_i, int new_i) { - int failed = 1; + pa_bool_t failed; do { - failed = __kernel_cmpxchg(old_i, new_i, &a->value); + failed = !!__kernel_cmpxchg(old_i, new_i, &a->value); } while(failed && a->value == old_i); return !failed; } @@ -404,10 +403,10 @@ static inline void pa_atomic_ptr_store(pa_atomic_ptr_t *a, void *p) { } static inline int pa_atomic_ptr_cmpxchg(pa_atomic_ptr_t *a, void *old_p, void* new_p) { - int failed = 1; + pa_bool_t failed; do { - failed = __kernel_cmpxchg_u((unsigned long) old_p, (unsigned long) new_p, &a->value); - } while(failed && a->value == old_p); + failed = !!__kernel_cmpxchg_u((unsigned long) old_p, (unsigned long) new_p, &a->value); + } while(failed && a->value == (unsigned long) old_p); return !failed; } diff --git a/src/pulsecore/authkey-prop.c b/src/pulsecore/authkey-prop.c index 54154500..a953bf7d 100644 --- a/src/pulsecore/authkey-prop.c +++ b/src/pulsecore/authkey-prop.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/authkey-prop.h b/src/pulsecore/authkey-prop.h index 247202f3..de02d2aa 100644 --- a/src/pulsecore/authkey-prop.h +++ b/src/pulsecore/authkey-prop.h @@ -1,8 +1,6 @@ #ifndef fooauthkeyprophfoo #define fooauthkeyprophfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/authkey.c b/src/pulsecore/authkey.c index 80bc8576..f3f40f80 100644 --- a/src/pulsecore/authkey.c +++ b/src/pulsecore/authkey.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/authkey.h b/src/pulsecore/authkey.h index 18e5157d..8301db1e 100644 --- a/src/pulsecore/authkey.h +++ b/src/pulsecore/authkey.h @@ -1,8 +1,6 @@ #ifndef fooauthkeyhfoo #define fooauthkeyhfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/autoload.c b/src/pulsecore/autoload.c index a1d3e02d..26c294b2 100644 --- a/src/pulsecore/autoload.c +++ b/src/pulsecore/autoload.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/autoload.h b/src/pulsecore/autoload.h index 8a3522a7..3926351f 100644 --- a/src/pulsecore/autoload.h +++ b/src/pulsecore/autoload.h @@ -1,8 +1,6 @@ #ifndef fooautoloadhfoo #define fooautoloadhfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/avahi-wrap.c b/src/pulsecore/avahi-wrap.c index fae54810..d5f40d83 100644 --- a/src/pulsecore/avahi-wrap.c +++ b/src/pulsecore/avahi-wrap.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/avahi-wrap.h b/src/pulsecore/avahi-wrap.h index 1e20ec38..7d8995bb 100644 --- a/src/pulsecore/avahi-wrap.h +++ b/src/pulsecore/avahi-wrap.h @@ -1,8 +1,6 @@ #ifndef fooavahiwrapperhfoo #define fooavahiwrapperhfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/cli-command.c b/src/pulsecore/cli-command.c index 925a2e8c..cbdb602a 100644 --- a/src/pulsecore/cli-command.c +++ b/src/pulsecore/cli-command.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/cli-command.h b/src/pulsecore/cli-command.h index 2a923443..9bf35dc3 100644 --- a/src/pulsecore/cli-command.h +++ b/src/pulsecore/cli-command.h @@ -1,8 +1,6 @@ #ifndef fooclicommandhfoo #define fooclicommandhfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/cli-text.c b/src/pulsecore/cli-text.c index 029a7089..c92fca20 100644 --- a/src/pulsecore/cli-text.c +++ b/src/pulsecore/cli-text.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -118,6 +116,9 @@ char *pa_sink_list_to_string(pa_core *c) { 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], *t; + pa_usec_t min_latency, max_latency; + + pa_sink_get_latency_range(sink, &min_latency, &max_latency); pa_strbuf_printf( s, @@ -130,6 +131,8 @@ char *pa_sink_list_to_string(pa_core *c) { "\tmuted: %s\n" "\tcurrent latency: %0.2f ms\n" "\tconfigured latency: %0.2f ms; range is %0.2f .. %0.2f ms\n" + "\tmax request: %lu KiB\n" + "\tmax rewind: %lu KiB\n" "\tmonitor source: %u\n" "\tsample spec: %s\n" "\tchannel map: %s\n" @@ -149,7 +152,9 @@ char *pa_sink_list_to_string(pa_core *c) { pa_cvolume_snprint(cv, sizeof(cv), pa_sink_get_volume(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, + (double) pa_sink_get_requested_latency(sink) / PA_USEC_PER_MSEC, (double) min_latency / PA_USEC_PER_MSEC, (double) max_latency / PA_USEC_PER_MSEC, + (unsigned long) pa_sink_get_max_request(sink) / 1024, + (unsigned long) pa_sink_get_max_rewind(sink) / 1024, 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), @@ -186,6 +191,9 @@ char *pa_source_list_to_string(pa_core *c) { 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], *t; + pa_usec_t min_latency, max_latency; + + pa_source_get_latency_range(source, &min_latency, &max_latency); pa_strbuf_printf( s, @@ -198,6 +206,7 @@ char *pa_source_list_to_string(pa_core *c) { "\tmuted: %s\n" "\tcurrent latency: %0.2f ms\n" "\tconfigured latency: %0.2f ms; range is %0.2f .. %0.2f ms\n" + "\tmax rewind: %lu KiB\n" "\tsample spec: %s\n" "\tchannel map: %s\n" "\tused by: %u\n" @@ -216,7 +225,8 @@ char *pa_source_list_to_string(pa_core *c) { pa_cvolume_snprint(cv, sizeof(cv), pa_source_get_volume(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, + (double) pa_source_get_requested_latency(source) / PA_USEC_PER_MSEC, (double) min_latency / PA_USEC_PER_MSEC, (double) max_latency / PA_USEC_PER_MSEC, + (unsigned long) pa_source_get_max_rewind(source) / 1024, 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), @@ -287,7 +297,7 @@ 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) / PA_USEC_PER_MSEC, + (double) pa_source_output_get_latency(o, NULL) / 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), @@ -296,6 +306,8 @@ char *pa_source_output_list_to_string(pa_core *c) { 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, pa_strnull(pa_proplist_gets(o->client->proplist, PA_PROP_APPLICATION_NAME))); + if (o->direct_on_input) + pa_strbuf_printf(s, "\tdirect on input: %u\n", o->direct_on_input->index); t = pa_proplist_to_string(o->proplist); pa_strbuf_printf(s, "\tproperties:\n%s", t); @@ -361,7 +373,7 @@ char *pa_sink_input_list_to_string(pa_core *c) { i->sink->index, i->sink->name, pa_cvolume_snprint(cv, sizeof(cv), pa_sink_input_get_volume(i)), pa_yes_no(pa_sink_input_get_mute(i)), - (double) pa_sink_input_get_latency(i) / PA_USEC_PER_MSEC, + (double) pa_sink_input_get_latency(i, NULL) / 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), diff --git a/src/pulsecore/cli-text.h b/src/pulsecore/cli-text.h index 9e5bf081..f4cb97a5 100644 --- a/src/pulsecore/cli-text.h +++ b/src/pulsecore/cli-text.h @@ -1,8 +1,6 @@ #ifndef fooclitexthfoo #define fooclitexthfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/cli.c b/src/pulsecore/cli.c index 47712d30..b3c639f8 100644 --- a/src/pulsecore/cli.c +++ b/src/pulsecore/cli.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/cli.h b/src/pulsecore/cli.h index 2b58d458..6077a8e8 100644 --- a/src/pulsecore/cli.h +++ b/src/pulsecore/cli.h @@ -1,8 +1,6 @@ #ifndef fooclihfoo #define fooclihfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/client.c b/src/pulsecore/client.c index 4eca4e2a..0ffd2330 100644 --- a/src/pulsecore/client.c +++ b/src/pulsecore/client.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -66,18 +64,21 @@ pa_client *pa_client_new(pa_core *core, const char *driver, const char *name) { } void pa_client_free(pa_client *c) { + pa_core *core; + pa_assert(c); pa_assert(c->core); + core = c->core; pa_idxset_remove_by_data(c->core->clients, c, NULL); - pa_core_check_quit(c->core); - pa_log_info("Freed %u \"%s\"", c->index, 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_proplist_free(c->proplist); pa_xfree(c->driver); pa_xfree(c); + + pa_core_check_quit(core); } void pa_client_kill(pa_client *c) { diff --git a/src/pulsecore/client.h b/src/pulsecore/client.h index bff057ed..28d1fe5f 100644 --- a/src/pulsecore/client.h +++ b/src/pulsecore/client.h @@ -1,8 +1,6 @@ #ifndef foopulseclienthfoo #define foopulseclienthfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/conf-parser.c b/src/pulsecore/conf-parser.c index 12ea49c2..4aec45d7 100644 --- a/src/pulsecore/conf-parser.c +++ b/src/pulsecore/conf-parser.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/conf-parser.h b/src/pulsecore/conf-parser.h index b56d979e..7eb1fae2 100644 --- a/src/pulsecore/conf-parser.h +++ b/src/pulsecore/conf-parser.h @@ -1,8 +1,6 @@ #ifndef fooconfparserhfoo #define fooconfparserhfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/core-error.c b/src/pulsecore/core-error.c index 8a61e726..3d6c2c3b 100644 --- a/src/pulsecore/core-error.c +++ b/src/pulsecore/core-error.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/core-error.h b/src/pulsecore/core-error.h index 443c4883..b0c306c7 100644 --- a/src/pulsecore/core-error.h +++ b/src/pulsecore/core-error.h @@ -1,8 +1,6 @@ #ifndef foocoreerrorhfoo #define foocoreerrorhfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/core-scache.c b/src/pulsecore/core-scache.c index e5546007..75fa2ff1 100644 --- a/src/pulsecore/core-scache.c +++ b/src/pulsecore/core-scache.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -63,7 +61,7 @@ #include "core-scache.h" -#define UNLOAD_POLL_TIME 5 +#define UNLOAD_POLL_TIME 60 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; @@ -131,8 +129,7 @@ static pa_scache_entry* scache_add_item(pa_core *c, const char *name) { } e->last_used_time = 0; - e->memchunk.memblock = NULL; - e->memchunk.index = e->memchunk.length = 0; + pa_memchunk_reset(&e->memchunk); e->filename = NULL; e->lazy = FALSE; e->last_used_time = 0; @@ -165,8 +162,7 @@ int pa_scache_add_item( pa_assert(!map || (pa_channel_map_valid(map) && ss && ss->channels == map->channels)); if (ss && !map) - if (!(map = pa_channel_map_init_auto(&tmap, ss->channels, PA_CHANNEL_MAP_DEFAULT))) - return -1; + pa_channel_map_init_extend(&tmap, ss->channels, PA_CHANNEL_MAP_DEFAULT); if (chunk && chunk->length > PA_SCACHE_ENTRY_SIZE_MAX) return -1; @@ -277,8 +273,7 @@ int pa_scache_remove_item(pa_core *c, const char *name) { if (!(e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE, 0))) return -1; - if (pa_idxset_remove_by_data(c->scache, e, NULL) != e) - pa_assert(0); + pa_assert_se(pa_idxset_remove_by_data(c->scache, e, NULL) == e); pa_log_debug("Removed sample \"%s\"", name); diff --git a/src/pulsecore/core-scache.h b/src/pulsecore/core-scache.h index 31f3ff32..80e0fd04 100644 --- a/src/pulsecore/core-scache.h +++ b/src/pulsecore/core-scache.h @@ -1,8 +1,6 @@ #ifndef foocorescachehfoo #define foocorescachehfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/core-subscribe.c b/src/pulsecore/core-subscribe.c index 06c5a4ad..6107002b 100644 --- a/src/pulsecore/core-subscribe.c +++ b/src/pulsecore/core-subscribe.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/core-subscribe.h b/src/pulsecore/core-subscribe.h index 2b6863f9..2f9730d9 100644 --- a/src/pulsecore/core-subscribe.h +++ b/src/pulsecore/core-subscribe.h @@ -1,8 +1,6 @@ #ifndef foocoresubscribehfoo #define foocoresubscribehfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/core-util.c b/src/pulsecore/core-util.c index c8ea4f52..d259fb16 100644 --- a/src/pulsecore/core-util.c +++ b/src/pulsecore/core-util.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -1144,12 +1142,13 @@ fail: /* Unlock a temporary lcok file */ int pa_unlock_lockfile(const char *fn, int fd) { int r = 0; - pa_assert(fn); pa_assert(fd >= 0); - if (unlink(fn) < 0) { - pa_log_warn("Unable to remove lock file '%s': %s", fn, pa_cstrerror(errno)); - r = -1; + if (fn) { + if (unlink(fn) < 0) { + pa_log_warn("Unable to remove lock file '%s': %s", fn, pa_cstrerror(errno)); + r = -1; + } } if (pa_lock_fd(fd, 0) < 0) { @@ -1165,24 +1164,35 @@ int pa_unlock_lockfile(const char *fn, int fd) { return r; } -char *pa_get_runtime_dir(void) { +static char *get_dir(mode_t m, const char *env_name) { const char *e; char *d; - if ((e = getenv("PULSE_RUNTIME_PATH"))) + if ((e = getenv(env_name))) d = pa_xstrdup(e); else { char h[PATH_MAX]; + struct stat st; if (!pa_get_home_dir(h, sizeof(h))) { pa_log_error("Failed to get home directory."); return NULL; } + if (stat(h, &st) < 0) { + pa_log_error("Failed to stat home directory %s: %s", h, pa_cstrerror(errno)); + return NULL; + } + + if (st.st_uid != getuid()) { + pa_log_error("Home directory %s not ours.", d); + 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) { + if (pa_make_secure_dir(d, m, (pid_t) -1, (pid_t) -1) < 0) { pa_log_error("Failed to create secure directory: %s", pa_cstrerror(errno)); return NULL; } @@ -1190,6 +1200,14 @@ char *pa_get_runtime_dir(void) { return d; } +char *pa_get_runtime_dir(void) { + return get_dir(pa_in_system_mode() ? 0755 : 0700, "PULSE_RUNTIME_PATH"); +} + +char *pa_get_state_dir(void) { + return get_dir(0700, "PULSE_STATE_PATH"); +} + /* 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 @@ -1418,7 +1436,7 @@ size_t pa_parsehex(const char *p, uint8_t *d, size_t dlength) { } /* Returns nonzero when *s starts with *pfx */ -int pa_startswith(const char *s, const char *pfx) { +pa_bool_t pa_startswith(const char *s, const char *pfx) { size_t l; pa_assert(s); @@ -1430,7 +1448,7 @@ int pa_startswith(const char *s, const char *pfx) { } /* Returns nonzero when *s ends with *sfx */ -int pa_endswith(const char *s, const char *sfx) { +pa_bool_t pa_endswith(const char *s, const char *sfx) { size_t l1, l2; pa_assert(s); @@ -1472,13 +1490,16 @@ char *pa_make_path_absolute(const char *p) { /* 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) { +static char *get_path(const char *fn, pa_bool_t rt) { char *rtp; if (pa_is_path_absolute(fn)) return pa_xstrdup(fn); - rtp = pa_get_runtime_dir(); + rtp = rt ? pa_get_runtime_dir() : pa_get_state_dir(); + + if (!rtp) + return NULL; if (fn) { char *r; @@ -1489,6 +1510,14 @@ char *pa_runtime_path(const char *fn) { return rtp; } +char *pa_runtime_path(const char *fn) { + return get_path(fn, 1); +} + +char *pa_state_path(const char *fn) { + return get_path(fn, 0); +} + /* Convert the string s to a signed integer in *ret_i */ int pa_atoi(const char *s, int32_t *ret_i) { char *x = NULL; @@ -1541,13 +1570,13 @@ static void c_locale_destroy(void) { } #endif -int pa_atof(const char *s, float *ret_f) { +int pa_atod(const char *s, double *ret_d) { char *x = NULL; - float f; + double f; int r = 0; pa_assert(s); - pa_assert(ret_f); + pa_assert(ret_d); /* This should be locale independent */ @@ -1562,22 +1591,18 @@ int pa_atof(const char *s, float *ret_f) { if (c_locale) { errno = 0; - f = strtof_l(s, &x, c_locale); + f = strtod_l(s, &x, c_locale); } else #endif { errno = 0; -#ifdef HAVE_STRTOF - f = strtof(s, &x); -#else f = strtod(s, &x); -#endif } if (!x || *x || errno != 0) r = -1; else - *ret_f = f; + *ret_d = f; return r; } @@ -1774,10 +1799,11 @@ int pa_close_all(int except_fd, ...) { i = 0; if (except_fd >= 0) { + int fd; p[i++] = except_fd; - while ((p[i++] = va_arg(ap, int)) >= 0) - ; + while ((fd = va_arg(ap, int)) >= 0) + p[i++] = fd; } p[i] = -1; @@ -1803,6 +1829,7 @@ int pa_close_allv(const int except_fds[]) { struct dirent *de; while ((de = readdir(d))) { + pa_bool_t found; long l; char *e = NULL; int i; @@ -1826,17 +1853,23 @@ int pa_close_allv(const int except_fds[]) { return -1; } - if (fd <= 3) + if (fd < 3) continue; if (fd == dirfd(d)) continue; + found = FALSE; for (i = 0; except_fds[i] >= 0; i++) - if (except_fds[i] == fd) - continue; + if (except_fds[i] == fd) { + found = TRUE; + break; + } + + if (found) + continue; - if (close(fd) < 0) { + if (pa_close(fd) < 0) { saved_errno = errno; closedir(d); errno = saved_errno; @@ -1890,10 +1923,11 @@ int pa_unblock_sigs(int except, ...) { i = 0; if (except >= 1) { + int sig; p[i++] = except; - while ((p[i++] = va_arg(ap, int)) >= 0) - ; + while ((sig = va_arg(ap, int)) >= 0) + p[i++] = sig; } p[i] = -1; @@ -1957,12 +1991,12 @@ int pa_reset_sigsv(const int except[]) { int sig; for (sig = 1; sig < _NSIG; sig++) { - int reset = 1; + pa_bool_t reset = TRUE; switch (sig) { case SIGKILL: case SIGSTOP: - reset = 0; + reset = FALSE; break; default: { @@ -1970,7 +2004,7 @@ int pa_reset_sigsv(const int except[]) { for (i = 0; except[i] > 0; i++) { if (sig == except[i]) { - reset = 0; + reset = FALSE; break; } } @@ -2000,3 +2034,12 @@ void pa_set_env(const char *key, const char *value) { putenv(pa_sprintf_malloc("%s=%s", key, value)); } + +pa_bool_t pa_in_system_mode(void) { + const char *e; + + if (!(e = getenv("PULSE_SYSTEM"))) + return FALSE; + + return !!atoi(e); +} diff --git a/src/pulsecore/core-util.h b/src/pulsecore/core-util.h index ec4cdc43..2ed81fc5 100644 --- a/src/pulsecore/core-util.h +++ b/src/pulsecore/core-util.h @@ -1,8 +1,6 @@ #ifndef foocoreutilhfoo #define foocoreutilhfoo -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -114,18 +112,20 @@ int pa_unlock_lockfile(const char *fn, int fd); 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; +pa_bool_t pa_startswith(const char *s, const char *pfx) PA_GCC_PURE; +pa_bool_t pa_endswith(const char *s, const char *sfx) PA_GCC_PURE; 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_get_state_dir(void); char *pa_runtime_path(const char *fn); +char *pa_state_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_atod(const char *s, double *ret_d); 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); @@ -180,4 +180,8 @@ int pa_reset_sigsv(const int except[]); void pa_set_env(const char *key, const char *value); +pa_bool_t pa_in_system_mode(void); + +#define pa_streq(a,b) (!strcmp((a),(b))) + #endif diff --git a/src/pulsecore/core.c b/src/pulsecore/core.c index 3b758a38..b2638b10 100644 --- a/src/pulsecore/core.c +++ b/src/pulsecore/core.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -135,7 +133,6 @@ pa_core* pa_core_new(pa_mainloop_api *m, int shared) { c->resample_method = PA_RESAMPLER_SPEEX_FLOAT_BASE + 3; - c->is_system_instance = FALSE; c->disallow_module_loading = FALSE; c->realtime_scheduling = FALSE; c->realtime_priority = 5; @@ -210,11 +207,16 @@ static void quit_callback(pa_mainloop_api*m, pa_time_event *e, PA_GCC_UNUSED con void pa_core_check_quit(pa_core *c) { pa_assert(c); - if (!c->quit_event && c->exit_idle_time >= 0 && pa_idxset_size(c->clients) == 0) { + if (!c->quit_event && + c->exit_idle_time >= 0 && + pa_idxset_size(c->clients) == 0) { + struct timeval tv; pa_gettimeofday(&tv); tv.tv_sec+= c->exit_idle_time; + c->quit_event = c->mainloop->time_new(c->mainloop, &tv, quit_callback, c); + } else if (c->quit_event && pa_idxset_size(c->clients) > 0) { c->mainloop->time_free(c->quit_event); c->quit_event = NULL; diff --git a/src/pulsecore/core.h b/src/pulsecore/core.h index 50c05b4c..d9ed46f6 100644 --- a/src/pulsecore/core.h +++ b/src/pulsecore/core.h @@ -1,8 +1,6 @@ #ifndef foocorehfoo #define foocorehfoo -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -123,7 +121,6 @@ struct pa_core { pa_bool_t disallow_module_loading, running_as_daemon; pa_resample_method_t resample_method; - pa_bool_t is_system_instance; pa_bool_t realtime_scheduling; int realtime_priority; pa_bool_t disable_remixing; diff --git a/src/pulsecore/creds.h b/src/pulsecore/creds.h index 51dfc33d..c15c469b 100644 --- a/src/pulsecore/creds.h +++ b/src/pulsecore/creds.h @@ -1,8 +1,6 @@ #ifndef foocredshfoo #define foocredshfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/dllmain.c b/src/pulsecore/dllmain.c index 52cbf9e2..269de604 100644 --- a/src/pulsecore/dllmain.c +++ b/src/pulsecore/dllmain.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/dynarray.c b/src/pulsecore/dynarray.c index 8bdb46fa..a1fcd8a3 100644 --- a/src/pulsecore/dynarray.c +++ b/src/pulsecore/dynarray.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/dynarray.h b/src/pulsecore/dynarray.h index 0f222e10..82b42082 100644 --- a/src/pulsecore/dynarray.h +++ b/src/pulsecore/dynarray.h @@ -1,8 +1,6 @@ #ifndef foodynarrayhfoo #define foodynarrayhfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/endianmacros.h b/src/pulsecore/endianmacros.h index 6b80246b..26336918 100644 --- a/src/pulsecore/endianmacros.h +++ b/src/pulsecore/endianmacros.h @@ -1,8 +1,6 @@ #ifndef fooendianmacroshfoo #define fooendianmacroshfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/envelope.c b/src/pulsecore/envelope.c index 2f5da5a0..e2691611 100644 --- a/src/pulsecore/envelope.c +++ b/src/pulsecore/envelope.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/envelope.h b/src/pulsecore/envelope.h index c54c137a..5296415a 100644 --- a/src/pulsecore/envelope.h +++ b/src/pulsecore/envelope.h @@ -1,8 +1,6 @@ #ifndef foopulseenvelopehfoo #define foopulseenvelopehfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/esound.h b/src/pulsecore/esound.h index ea6a5665..79322ae4 100644 --- a/src/pulsecore/esound.h +++ b/src/pulsecore/esound.h @@ -1,8 +1,6 @@ #ifndef fooesoundhfoo #define fooesoundhfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/fdsem.c b/src/pulsecore/fdsem.c index 22d2a850..1531e3db 100644 --- a/src/pulsecore/fdsem.c +++ b/src/pulsecore/fdsem.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/fdsem.h b/src/pulsecore/fdsem.h index f4f7b99a..48a77c49 100644 --- a/src/pulsecore/fdsem.h +++ b/src/pulsecore/fdsem.h @@ -1,8 +1,6 @@ #ifndef foopulsefdsemhfoo #define foopulsefdsemhfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/flist.c b/src/pulsecore/flist.c index d9740777..f166ee33 100644 --- a/src/pulsecore/flist.c +++ b/src/pulsecore/flist.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/flist.h b/src/pulsecore/flist.h index 3d9a89a2..c040667d 100644 --- a/src/pulsecore/flist.h +++ b/src/pulsecore/flist.h @@ -1,8 +1,6 @@ #ifndef foopulseflisthfoo #define foopulseflisthfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/hashmap.c b/src/pulsecore/hashmap.c index c9d5632c..b7f4109b 100644 --- a/src/pulsecore/hashmap.c +++ b/src/pulsecore/hashmap.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -191,24 +189,36 @@ unsigned pa_hashmap_size(pa_hashmap *h) { } void *pa_hashmap_iterate(pa_hashmap *h, void **state, const void **key) { + struct hashmap_entry *e; + pa_assert(h); pa_assert(state); - if (!*state) - *state = h->first_entry; + if (*state == (void*) -1) + goto at_end; + + if ((!*state && !h->first_entry)) + goto at_end; + + e = *state ? *state : h->first_entry; + + if (e->next) + *state = e->next; else - *state = ((struct hashmap_entry*) *state)->next; + *state = (void*) -1; - if (!*state) { - if (key) - *key = NULL; - return NULL; - } + if (key) + *key = e->key; + + return e->value; + +at_end: + *state = (void *) -1; if (key) - *key = ((struct hashmap_entry*) *state)->key; + *key = NULL; - return ((struct hashmap_entry*) *state)->value; + return NULL; } void* pa_hashmap_steal_first(pa_hashmap *h) { diff --git a/src/pulsecore/hashmap.h b/src/pulsecore/hashmap.h index 98df4502..a4505c4c 100644 --- a/src/pulsecore/hashmap.h +++ b/src/pulsecore/hashmap.h @@ -1,8 +1,6 @@ #ifndef foohashmaphfoo #define foohashmaphfoo -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -51,9 +49,10 @@ unsigned pa_hashmap_size(pa_hashmap *h); /* May be used to iterate through the hashmap. Initially the opaque pointer *state has to be set to NULL. The hashmap may not be - modified during iteration. The key of the entry is returned in - *key, if key is non-NULL. After the last entry in the hashmap NULL - is returned. */ + modified during iteration -- except for deleting the current entry + via pa_hashmap_remove(). The key of the entry is returned in *key, + if key is non-NULL. After the last entry in the hashmap NULL is + returned. */ void *pa_hashmap_iterate(pa_hashmap *h, void **state, const void**key); void *pa_hashmap_steal_first(pa_hashmap *h); diff --git a/src/pulsecore/hook-list.c b/src/pulsecore/hook-list.c index 3a6874c4..0aac4759 100644 --- a/src/pulsecore/hook-list.c +++ b/src/pulsecore/hook-list.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -33,8 +31,7 @@ void pa_hook_init(pa_hook *hook, void *data) { pa_assert(hook); PA_LLIST_HEAD_INIT(pa_hook_slot, hook->slots); - hook->last = NULL; - hook->n_dead = hook->firing = 0; + hook->n_dead = hook->n_firing = 0; hook->data = data; } @@ -42,9 +39,6 @@ static void slot_free(pa_hook *hook, pa_hook_slot *slot) { pa_assert(hook); pa_assert(slot); - if (hook->last == slot) - hook->last = slot->prev; - PA_LLIST_REMOVE(pa_hook_slot, hook->slots, slot); pa_xfree(slot); @@ -52,7 +46,7 @@ static void slot_free(pa_hook *hook, pa_hook_slot *slot) { void pa_hook_free(pa_hook *hook) { pa_assert(hook); - pa_assert(!hook->firing); + pa_assert(hook->n_firing == 0); while (hook->slots) slot_free(hook, hook->slots); @@ -60,19 +54,26 @@ void pa_hook_free(pa_hook *hook) { pa_hook_init(hook, NULL); } -pa_hook_slot* pa_hook_connect(pa_hook *hook, pa_hook_cb_t cb, void *data) { - pa_hook_slot *slot; +pa_hook_slot* pa_hook_connect(pa_hook *hook, pa_hook_priority_t prio, pa_hook_cb_t cb, void *data) { + pa_hook_slot *slot, *where, *prev; pa_assert(cb); slot = pa_xnew(pa_hook_slot, 1); slot->hook = hook; - slot->dead = 0; + slot->dead = FALSE; slot->callback = cb; slot->data = data; + slot->priority = prio; - PA_LLIST_INSERT_AFTER(pa_hook_slot, hook->slots, hook->last, slot); - hook->last = slot; + prev = NULL; + for (where = hook->slots; where; where = where->next) { + if (prio < where->priority) + break; + prev = where; + } + + PA_LLIST_INSERT_AFTER(pa_hook_slot, hook->slots, prev, slot); return slot; } @@ -81,8 +82,8 @@ void pa_hook_slot_free(pa_hook_slot *slot) { pa_assert(slot); pa_assert(!slot->dead); - if (slot->hook->firing > 0) { - slot->dead = 1; + if (slot->hook->n_firing > 0) { + slot->dead = TRUE; slot->hook->n_dead++; } else slot_free(slot->hook, slot); @@ -94,7 +95,7 @@ pa_hook_result_t pa_hook_fire(pa_hook *hook, void *data) { pa_assert(hook); - hook->firing ++; + hook->n_firing ++; for (slot = hook->slots; slot; slot = slot->next) { if (slot->dead) @@ -104,7 +105,8 @@ pa_hook_result_t pa_hook_fire(pa_hook *hook, void *data) { break; } - hook->firing --; + hook->n_firing --; + pa_assert(hook->n_firing >= 0); for (slot = hook->slots; hook->n_dead > 0 && slot; slot = next) { next = slot->next; @@ -115,6 +117,7 @@ pa_hook_result_t pa_hook_fire(pa_hook *hook, void *data) { } } + pa_assert(hook->n_dead == 0); + return result; } - diff --git a/src/pulsecore/hook-list.h b/src/pulsecore/hook-list.h index c288980d..cf85aca5 100644 --- a/src/pulsecore/hook-list.h +++ b/src/pulsecore/hook-list.h @@ -1,8 +1,6 @@ #ifndef foohooklistfoo #define foohooklistfoo -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -38,14 +36,21 @@ typedef enum pa_hook_result { PA_HOOK_CANCEL = -1 } pa_hook_result_t; +typedef enum pa_hook_priority { + PA_HOOK_EARLY = -100, + PA_HOOK_NORMAL = 0, + PA_HOOK_LATE = 100 +} pa_hook_priority_t; + typedef pa_hook_result_t (*pa_hook_cb_t)( void *hook_data, void *call_data, void *slot_data); struct pa_hook_slot { - int dead; + pa_bool_t dead; pa_hook *hook; + pa_hook_priority_t priority; pa_hook_cb_t callback; void *data; PA_LLIST_FIELDS(pa_hook_slot); @@ -53,8 +58,7 @@ struct pa_hook_slot { struct pa_hook { PA_LLIST_HEAD(pa_hook_slot, slots); - pa_hook_slot *last; - int firing, n_dead; + int n_firing, n_dead; void *data; }; @@ -62,7 +66,7 @@ struct pa_hook { void pa_hook_init(pa_hook *hook, void *data); void pa_hook_free(pa_hook *hook); -pa_hook_slot* pa_hook_connect(pa_hook *hook, pa_hook_cb_t, void *data); +pa_hook_slot* pa_hook_connect(pa_hook *hook, pa_hook_priority_t prio, pa_hook_cb_t cb, void *data); void pa_hook_slot_free(pa_hook_slot *slot); pa_hook_result_t pa_hook_fire(pa_hook *hook, void *data); diff --git a/src/pulsecore/idxset.c b/src/pulsecore/idxset.c index 8a88471f..7c9520a4 100644 --- a/src/pulsecore/idxset.c +++ b/src/pulsecore/idxset.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/idxset.h b/src/pulsecore/idxset.h index 5b55cec2..0a4e528e 100644 --- a/src/pulsecore/idxset.h +++ b/src/pulsecore/idxset.h @@ -1,8 +1,6 @@ #ifndef fooidxsethfoo #define fooidxsethfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/inet_ntop.c b/src/pulsecore/inet_ntop.c index 4a4f7aac..87551232 100644 --- a/src/pulsecore/inet_ntop.c +++ b/src/pulsecore/inet_ntop.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/inet_pton.c b/src/pulsecore/inet_pton.c index 84d0c0ea..d191e550 100644 --- a/src/pulsecore/inet_pton.c +++ b/src/pulsecore/inet_pton.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/iochannel.c b/src/pulsecore/iochannel.c index 63ab2ad7..b40c9815 100644 --- a/src/pulsecore/iochannel.c +++ b/src/pulsecore/iochannel.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -424,3 +422,16 @@ int pa_iochannel_get_send_fd(pa_iochannel *io) { return io->ofd; } + +pa_bool_t pa_iochannel_socket_is_local(pa_iochannel *io) { + pa_assert(io); + + if (pa_socket_is_local(io->ifd)) + return TRUE; + + if (io->ifd != io->ofd) + if (pa_socket_is_local(io->ofd)) + return TRUE; + + return FALSE; +} diff --git a/src/pulsecore/iochannel.h b/src/pulsecore/iochannel.h index c9794d99..9050df90 100644 --- a/src/pulsecore/iochannel.h +++ b/src/pulsecore/iochannel.h @@ -1,8 +1,6 @@ #ifndef fooiochannelhfoo #define fooiochannelhfoo -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -85,6 +83,8 @@ void pa_iochannel_socket_peer_to_string(pa_iochannel*io, char*s, size_t l); int pa_iochannel_socket_set_rcvbuf(pa_iochannel*io, size_t l); int pa_iochannel_socket_set_sndbuf(pa_iochannel*io, size_t l); +pa_bool_t pa_iochannel_socket_is_local(pa_iochannel *io); + pa_mainloop_api* pa_iochannel_get_mainloop_api(pa_iochannel *io); int pa_iochannel_get_recv_fd(pa_iochannel *io); diff --git a/src/pulsecore/ioline.c b/src/pulsecore/ioline.c index 85bbadc4..90afaafd 100644 --- a/src/pulsecore/ioline.c +++ b/src/pulsecore/ioline.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/ioline.h b/src/pulsecore/ioline.h index f4edc7b4..b9a3d9f4 100644 --- a/src/pulsecore/ioline.h +++ b/src/pulsecore/ioline.h @@ -1,8 +1,6 @@ #ifndef fooiolinehfoo #define fooiolinehfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/ipacl.c b/src/pulsecore/ipacl.c index 9b22e8f5..7b5f865b 100644 --- a/src/pulsecore/ipacl.c +++ b/src/pulsecore/ipacl.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/ipacl.h b/src/pulsecore/ipacl.h index 175f54e0..7b7ffa61 100644 --- a/src/pulsecore/ipacl.h +++ b/src/pulsecore/ipacl.h @@ -1,8 +1,6 @@ #ifndef fooparseaddrhfoo #define fooparseaddrhfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/llist.h b/src/pulsecore/llist.h index e62f15b4..46b54eb3 100644 --- a/src/pulsecore/llist.h +++ b/src/pulsecore/llist.h @@ -1,8 +1,6 @@ #ifndef foollistfoo #define foollistfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/log.c b/src/pulsecore/log.c index b5929ec4..5eda4f65 100644 --- a/src/pulsecore/log.c +++ b/src/pulsecore/log.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/log.h b/src/pulsecore/log.h index 765dd2e5..2047696e 100644 --- a/src/pulsecore/log.h +++ b/src/pulsecore/log.h @@ -1,8 +1,6 @@ #ifndef foologhfoo #define foologhfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/ltdl-helper.c b/src/pulsecore/ltdl-helper.c index b83897a6..0d4c22f8 100644 --- a/src/pulsecore/ltdl-helper.c +++ b/src/pulsecore/ltdl-helper.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/ltdl-helper.h b/src/pulsecore/ltdl-helper.h index 5c7388a1..ea73de54 100644 --- a/src/pulsecore/ltdl-helper.h +++ b/src/pulsecore/ltdl-helper.h @@ -1,8 +1,6 @@ #ifndef foopulsecoreltdlhelperhfoo #define foopulsecoreltdlhelperhfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/macro.h b/src/pulsecore/macro.h index 1d9eafd5..fd33b7bb 100644 --- a/src/pulsecore/macro.h +++ b/src/pulsecore/macro.h @@ -1,8 +1,6 @@ #ifndef foopulsemacrohfoo #define foopulsemacrohfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/mcalign.c b/src/pulsecore/mcalign.c index e12f84f8..a03d5ae7 100644 --- a/src/pulsecore/mcalign.c +++ b/src/pulsecore/mcalign.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/mcalign.h b/src/pulsecore/mcalign.h index 6c8b8d5f..e82eb007 100644 --- a/src/pulsecore/mcalign.h +++ b/src/pulsecore/mcalign.h @@ -1,8 +1,6 @@ #ifndef foomcalignhfoo #define foomcalignhfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/memblock.c b/src/pulsecore/memblock.c index 7005b441..c2ee1360 100644 --- a/src/pulsecore/memblock.c +++ b/src/pulsecore/memblock.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/memblock.h b/src/pulsecore/memblock.h index 8dc3f5a3..efe55b02 100644 --- a/src/pulsecore/memblock.h +++ b/src/pulsecore/memblock.h @@ -1,8 +1,6 @@ #ifndef foopulsememblockhfoo #define foopulsememblockhfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/memblockq.c b/src/pulsecore/memblockq.c index c047e56f..a9f28a07 100644 --- a/src/pulsecore/memblockq.c +++ b/src/pulsecore/memblockq.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -786,6 +784,9 @@ void pa_memblockq_set_tlength(pa_memblockq *bq, size_t tlength) { if (bq->tlength > bq->maxlength) bq->tlength = bq->maxlength; + if (bq->prebuf > bq->tlength) + pa_memblockq_set_prebuf(bq, bq->tlength); + if (bq->minreq > bq->tlength) pa_memblockq_set_minreq(bq, bq->tlength); @@ -803,8 +804,8 @@ void pa_memblockq_set_prebuf(pa_memblockq *bq, size_t prebuf) { if (prebuf > 0 && bq->prebuf < bq->base) bq->prebuf = bq->base; - if (bq->prebuf > bq->maxlength) - bq->prebuf = bq->maxlength; + if (bq->prebuf > bq->tlength) + bq->prebuf = bq->tlength; if (bq->prebuf <= 0 || pa_memblockq_get_length(bq) >= bq->prebuf) bq->in_prebuf = FALSE; @@ -908,3 +909,9 @@ unsigned pa_memblockq_get_nblocks(pa_memblockq *bq) { return bq->n_blocks; } + +size_t pa_memblockq_get_base(pa_memblockq *bq) { + pa_assert(bq); + + return bq->base; +} diff --git a/src/pulsecore/memblockq.h b/src/pulsecore/memblockq.h index 7c38757f..81f7cbb8 100644 --- a/src/pulsecore/memblockq.h +++ b/src/pulsecore/memblockq.h @@ -1,8 +1,6 @@ #ifndef foomemblockqhfoo #define foomemblockqhfoo -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -169,4 +167,6 @@ pa_bool_t pa_memblockq_prebuf_active(pa_memblockq *bq); unsigned pa_memblockq_get_nblocks(pa_memblockq *bq); +size_t pa_memblockq_get_base(pa_memblockq *bq); + #endif diff --git a/src/pulsecore/memchunk.c b/src/pulsecore/memchunk.c index 16a9c140..0bbf8590 100644 --- a/src/pulsecore/memchunk.c +++ b/src/pulsecore/memchunk.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -49,17 +47,20 @@ pa_memchunk* pa_memchunk_make_writable(pa_memchunk *c, size_t min) { pa_memblock_get_length(c->memblock) >= c->index+min) return c; - l = c->length; - if (l < min) - l = min; + l = PA_MAX(c->length, min); n = pa_memblock_new(pa_memblock_get_pool(c->memblock), l); - tdata = pa_memblock_acquire(n); + sdata = pa_memblock_acquire(c->memblock); + tdata = pa_memblock_acquire(n); + memcpy(tdata, (uint8_t*) sdata + c->index, c->length); - pa_memblock_release(n); + pa_memblock_release(c->memblock); + pa_memblock_release(n); + pa_memblock_unref(c->memblock); + c->memblock = n; c->index = 0; @@ -69,8 +70,7 @@ pa_memchunk* pa_memchunk_make_writable(pa_memchunk *c, size_t min) { pa_memchunk* pa_memchunk_reset(pa_memchunk *c) { pa_assert(c); - c->memblock = NULL; - c->length = c->index = 0; + memset(c, 0, sizeof(*c)); return c; } diff --git a/src/pulsecore/memchunk.h b/src/pulsecore/memchunk.h index 46a82406..9458f4ff 100644 --- a/src/pulsecore/memchunk.h +++ b/src/pulsecore/memchunk.h @@ -1,8 +1,6 @@ #ifndef foomemchunkhfoo #define foomemchunkhfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/modargs.c b/src/pulsecore/modargs.c index 0dab254b..5f5902c9 100644 --- a/src/pulsecore/modargs.c +++ b/src/pulsecore/modargs.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -53,6 +51,12 @@ static int add_key_value(pa_hashmap *map, char *key, char *value, const char* co pa_assert(key); pa_assert(value); + if (pa_hashmap_get(map, key)) { + pa_xfree(key); + pa_xfree(value); + return -1; + } + if (valid_keys) { const char*const* v; for (v = valid_keys; *v; v++) @@ -80,7 +84,15 @@ pa_modargs *pa_modargs_new(const char *args, const char* const* valid_keys) { map = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); if (args) { - enum { WHITESPACE, KEY, VALUE_START, VALUE_SIMPLE, VALUE_DOUBLE_QUOTES, VALUE_TICKS } state; + enum { + WHITESPACE, + KEY, + VALUE_START, + VALUE_SIMPLE, + VALUE_DOUBLE_QUOTES, + VALUE_TICKS + } state; + const char *p, *key, *value; size_t key_len = 0, value_len = 0; @@ -100,6 +112,8 @@ pa_modargs *pa_modargs_new(const char *args, const char* const* valid_keys) { case KEY: if (*p == '=') state = VALUE_START; + else if (isspace(*p)) + goto fail; else key_len++; break; diff --git a/src/pulsecore/modargs.h b/src/pulsecore/modargs.h index 504b9cd7..23766cfc 100644 --- a/src/pulsecore/modargs.h +++ b/src/pulsecore/modargs.h @@ -1,8 +1,6 @@ #ifndef foomodargshfoo #define foomodargshfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/modinfo.c b/src/pulsecore/modinfo.c index d1a78fbb..ac4ca88a 100644 --- a/src/pulsecore/modinfo.c +++ b/src/pulsecore/modinfo.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/modinfo.h b/src/pulsecore/modinfo.h index da6d5428..605637c4 100644 --- a/src/pulsecore/modinfo.h +++ b/src/pulsecore/modinfo.h @@ -1,8 +1,6 @@ #ifndef foomodinfohfoo #define foomodinfohfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/module.c b/src/pulsecore/module.c index 8e5bd2d1..f1eeb762 100644 --- a/src/pulsecore/module.c +++ b/src/pulsecore/module.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -120,7 +118,7 @@ pa_module* pa_module_load(pa_core *c, const char *name, const char *argument) { if (!c->modules) c->modules = pa_idxset_new(NULL, NULL); - if (!c->module_auto_unload_event) { + if (m->auto_unload && !c->module_auto_unload_event) { struct timeval ntv; pa_gettimeofday(&ntv); pa_timeval_add(&ntv, UNLOAD_POLL_TIME*1000000); diff --git a/src/pulsecore/module.h b/src/pulsecore/module.h index 68c7238d..ec582f25 100644 --- a/src/pulsecore/module.h +++ b/src/pulsecore/module.h @@ -1,8 +1,6 @@ #ifndef foomodulehfoo #define foomodulehfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/msgobject.c b/src/pulsecore/msgobject.c index f54e69f2..81417ea4 100644 --- a/src/pulsecore/msgobject.c +++ b/src/pulsecore/msgobject.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/msgobject.h b/src/pulsecore/msgobject.h index 8221cc33..1a43fa35 100644 --- a/src/pulsecore/msgobject.h +++ b/src/pulsecore/msgobject.h @@ -1,8 +1,6 @@ #ifndef foopulsemsgobjecthfoo #define foopulsemsgobjecthfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/mutex-posix.c b/src/pulsecore/mutex-posix.c index 6ac98484..35465b7b 100644 --- a/src/pulsecore/mutex-posix.c +++ b/src/pulsecore/mutex-posix.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/mutex-win32.c b/src/pulsecore/mutex-win32.c index 77d63d15..5e884e7f 100644 --- a/src/pulsecore/mutex-win32.c +++ b/src/pulsecore/mutex-win32.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/mutex.h b/src/pulsecore/mutex.h index 9ca8fae5..36e1d635 100644 --- a/src/pulsecore/mutex.h +++ b/src/pulsecore/mutex.h @@ -1,8 +1,6 @@ #ifndef foopulsemutexhfoo #define foopulsemutexhfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/namereg.c b/src/pulsecore/namereg.c index 1b0977d7..cc18adab 100644 --- a/src/pulsecore/namereg.c +++ b/src/pulsecore/namereg.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/namereg.h b/src/pulsecore/namereg.h index 0f5b4d4d..af0153ec 100644 --- a/src/pulsecore/namereg.h +++ b/src/pulsecore/namereg.h @@ -1,8 +1,6 @@ #ifndef foonamereghfoo #define foonamereghfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/native-common.h b/src/pulsecore/native-common.h index 56f9037e..809d6c75 100644 --- a/src/pulsecore/native-common.h +++ b/src/pulsecore/native-common.h @@ -1,8 +1,6 @@ #ifndef foonativecommonhfoo #define foonativecommonhfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/object.c b/src/pulsecore/object.c index 6c36242b..9a2f28f3 100644 --- a/src/pulsecore/object.c +++ b/src/pulsecore/object.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/object.h b/src/pulsecore/object.h index 562fd113..7dcfa2eb 100644 --- a/src/pulsecore/object.h +++ b/src/pulsecore/object.h @@ -1,8 +1,6 @@ #ifndef foopulseobjecthfoo #define foopulseobjecthfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/once.c b/src/pulsecore/once.c index a358cf65..989741dc 100644 --- a/src/pulsecore/once.c +++ b/src/pulsecore/once.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/once.h b/src/pulsecore/once.h index c9fe6d0a..576d40fa 100644 --- a/src/pulsecore/once.h +++ b/src/pulsecore/once.h @@ -1,8 +1,6 @@ #ifndef foopulseoncehfoo #define foopulseoncehfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/packet.c b/src/pulsecore/packet.c index 2706efea..cee468bd 100644 --- a/src/pulsecore/packet.c +++ b/src/pulsecore/packet.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/packet.h b/src/pulsecore/packet.h index bcac4a7f..5989b1fa 100644 --- a/src/pulsecore/packet.h +++ b/src/pulsecore/packet.h @@ -1,8 +1,6 @@ #ifndef foopackethfoo #define foopackethfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/parseaddr.c b/src/pulsecore/parseaddr.c index 149c9e00..f2b6b2cf 100644 --- a/src/pulsecore/parseaddr.c +++ b/src/pulsecore/parseaddr.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/parseaddr.h b/src/pulsecore/parseaddr.h index fd7cad3b..5fbcb9a7 100644 --- a/src/pulsecore/parseaddr.h +++ b/src/pulsecore/parseaddr.h @@ -1,8 +1,6 @@ #ifndef fooparseaddrhfoo #define fooparseaddrhfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/pdispatch.c b/src/pulsecore/pdispatch.c index bdd7cde1..e6a6ae4d 100644 --- a/src/pulsecore/pdispatch.c +++ b/src/pulsecore/pdispatch.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/pdispatch.h b/src/pulsecore/pdispatch.h index de0aa3ec..5c31d80e 100644 --- a/src/pulsecore/pdispatch.h +++ b/src/pulsecore/pdispatch.h @@ -1,8 +1,6 @@ #ifndef foopdispatchhfoo #define foopdispatchhfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/pid.c b/src/pulsecore/pid.c index 2ff132bb..addb17cc 100644 --- a/src/pulsecore/pid.c +++ b/src/pulsecore/pid.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -140,8 +138,51 @@ fail: return -1; } +static int proc_name_ours(pid_t pid, const char *procname) { +#ifdef __linux__ + char bn[PATH_MAX]; + FILE *f; + + pa_snprintf(bn, sizeof(bn), "/proc/%lu/stat", (unsigned long) pid); + + if (!(f = fopen(bn, "r"))) { + pa_log_info("Failed to open %s: %s", bn, pa_cstrerror(errno)); + return -1; + } else { + char *expected; + pa_bool_t good; + char stored[64]; + + if (!(fgets(stored, sizeof(stored), f))) { + pa_log_info("Failed to read from %s: %s", bn, feof(f) ? "EOF" : pa_cstrerror(errno)); + fclose(f); + return -1; + } + + fclose(f); + + expected = pa_sprintf_malloc("%lu (%s)", (unsigned long) pid, procname); + good = pa_startswith(stored, expected); + pa_xfree(expected); + +#if !defined(__OPTIMIZE__) + if (!good) { + /* libtool likes to rename our binary names ... */ + expected = pa_sprintf_malloc("%lu (lt-%s)", (unsigned long) pid, procname); + good = pa_startswith(stored, expected); + pa_xfree(expected); + } +#endif + + return !!good; + } +#endif + + return 1; +} + /* Create a new PID file for the current process. */ -int pa_pid_file_create(void) { +int pa_pid_file_create(const char *procname) { int fd = -1; int ret = -1; char t[20]; @@ -153,7 +194,8 @@ int pa_pid_file_create(void) { HANDLE process; #endif - fn = pa_runtime_path("pid"); + if (!(fn = pa_runtime_path("pid"))) + goto fail; if ((fd = open_pid_file(fn, O_CREAT|O_RDWR)) < 0) goto fail; @@ -161,14 +203,24 @@ int pa_pid_file_create(void) { if ((pid = read_pid(fn, fd)) == (pid_t) -1) pa_log_warn("Corrupt PID file, overwriting."); else if (pid > 0) { + #ifdef OS_IS_WIN32 if ((process = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid)) != NULL) { CloseHandle(process); #else if (kill(pid, 0) >= 0 || errno != ESRCH) { #endif - pa_log("Daemon already running."); - goto fail; + int ours = 1; + + if (procname) + if ((ours = proc_name_ours(pid, procname)) < 0) + goto fail; + + if (ours) { + pa_log("Daemon already running."); + ret = 1; + goto fail; + } } pa_log_warn("Stale PID file, overwriting."); @@ -212,7 +264,8 @@ int pa_pid_file_remove(void) { int ret = -1; pid_t pid; - fn = pa_runtime_path("pid"); + if (!(fn = pa_runtime_path("pid"))) + goto fail; if ((fd = open_pid_file(fn, O_RDWR)) < 0) { pa_log_warn("Failed to open PID file '%s': %s", fn, pa_cstrerror(errno)); @@ -234,7 +287,7 @@ int pa_pid_file_remove(void) { #ifdef OS_IS_WIN32 pa_lock_fd(fd, 0); - close(fd); + pa_close(fd); fd = -1; #endif @@ -265,8 +318,8 @@ fail: * exists and the PID therein too. Returns 0 on succcess, -1 * otherwise. If pid is non-NULL and a running daemon was found, * return its PID therein */ -int pa_pid_file_check_running(pid_t *pid, const char *binary_name) { - return pa_pid_file_kill(0, pid, binary_name); +int pa_pid_file_check_running(pid_t *pid, const char *procname) { + return pa_pid_file_kill(0, pid, procname); } #ifndef OS_IS_WIN32 @@ -274,7 +327,7 @@ int pa_pid_file_check_running(pid_t *pid, const char *binary_name) { /* Kill a current running daemon. Return non-zero on success, -1 * otherwise. If successful *pid contains the PID of the daemon * process. */ -int pa_pid_file_kill(int sig, pid_t *pid, const char *binary_name) { +int pa_pid_file_kill(int sig, pid_t *pid, const char *procname) { int fd = -1; char *fn; int ret = -1; @@ -282,10 +335,12 @@ int pa_pid_file_kill(int sig, pid_t *pid, const char *binary_name) { #ifdef __linux__ char *e = NULL; #endif + if (!pid) pid = &_pid; - fn = pa_runtime_path("pid"); + if (!(fn = pa_runtime_path("pid"))) + goto fail; if ((fd = open_pid_file(fn, O_RDONLY)) < 0) goto fail; @@ -293,22 +348,15 @@ int pa_pid_file_kill(int sig, pid_t *pid, const char *binary_name) { if ((*pid = read_pid(fn, fd)) == (pid_t) -1) goto fail; -#ifdef __linux__ - if (binary_name) { - pa_snprintf(fn, sizeof(fn), "/proc/%lu/exe", (unsigned long) pid); + if (procname) { + int ours; - if ((e = pa_readlink(fn))) { - char *f = pa_path_get_filename(e); - if (strcmp(f, binary_name) -#if !defined(__OPTIMIZE__) - /* libtool likes to rename our binary names ... */ - && !(pa_startswith(f, "lt-") && strcmp(f+3, binary_name) == 0) -#endif - ) - goto fail; - } + if ((ours = proc_name_ours(*pid, procname)) < 0) + goto fail; + + if (!ours) + goto fail; } -#endif ret = kill(*pid, sig); diff --git a/src/pulsecore/pid.h b/src/pulsecore/pid.h index 1d6de7b5..3c8a9de3 100644 --- a/src/pulsecore/pid.h +++ b/src/pulsecore/pid.h @@ -1,8 +1,6 @@ #ifndef foopidhfoo #define foopidhfoo -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -24,9 +22,9 @@ USA. ***/ -int pa_pid_file_create(void); +int pa_pid_file_create(const char *procname); int pa_pid_file_remove(void); -int pa_pid_file_check_running(pid_t *pid, const char *binary_name); -int pa_pid_file_kill(int sig, pid_t *pid, const char *binary_name); +int pa_pid_file_check_running(pid_t *pid, const char *procname); +int pa_pid_file_kill(int sig, pid_t *pid, const char *procname); #endif diff --git a/src/pulsecore/pipe.c b/src/pulsecore/pipe.c index e614c9c6..93d78a22 100644 --- a/src/pulsecore/pipe.c +++ b/src/pulsecore/pipe.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/pipe.h b/src/pulsecore/pipe.h index e013a2e7..9a7e62c8 100644 --- a/src/pulsecore/pipe.h +++ b/src/pulsecore/pipe.h @@ -1,8 +1,6 @@ #ifndef foopipehfoo #define foopipehfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/play-memblockq.c b/src/pulsecore/play-memblockq.c index 2688f923..8b3e79b9 100644 --- a/src/pulsecore/play-memblockq.c +++ b/src/pulsecore/play-memblockq.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -138,6 +136,7 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk return -1; } + chunk->length = PA_MIN(chunk->length, nbytes); pa_memblockq_drop(u->memblockq, chunk->length); return 0; diff --git a/src/pulsecore/play-memblockq.h b/src/pulsecore/play-memblockq.h index 9ecf7700..1a42867b 100644 --- a/src/pulsecore/play-memblockq.h +++ b/src/pulsecore/play-memblockq.h @@ -1,8 +1,6 @@ #ifndef fooplaymemblockqhfoo #define fooplaymemblockqhfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/play-memchunk.c b/src/pulsecore/play-memchunk.c index 67a92138..0dd48251 100644 --- a/src/pulsecore/play-memchunk.c +++ b/src/pulsecore/play-memchunk.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/play-memchunk.h b/src/pulsecore/play-memchunk.h index f7c9d178..c312ae82 100644 --- a/src/pulsecore/play-memchunk.h +++ b/src/pulsecore/play-memchunk.h @@ -1,8 +1,6 @@ #ifndef fooplaychunkhfoo #define fooplaychunkhfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/poll.c b/src/pulsecore/poll.c index 288f7dfb..88ac21e4 100644 --- a/src/pulsecore/poll.c +++ b/src/pulsecore/poll.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/poll.h b/src/pulsecore/poll.h index 6be6069b..86c37a08 100644 --- a/src/pulsecore/poll.h +++ b/src/pulsecore/poll.h @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/proplist-util.c b/src/pulsecore/proplist-util.c new file mode 100644 index 00000000..6005775e --- /dev/null +++ b/src/pulsecore/proplist-util.c @@ -0,0 +1,118 @@ +/*** + This file is part of PulseAudio. + + Copyright 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 by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <string.h> +#include <locale.h> + +#include <pulse/proplist.h> +#include <pulse/utf8.h> +#include <pulse/xmalloc.h> +#include <pulse/util.h> + +#include <pulsecore/core-util.h> + +#include "proplist-util.h" + +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); + } +} diff --git a/src/pulsecore/proplist-util.h b/src/pulsecore/proplist-util.h new file mode 100644 index 00000000..c6bdc103 --- /dev/null +++ b/src/pulsecore/proplist-util.h @@ -0,0 +1,29 @@ +#ifndef fooproplistutilutilhfoo +#define fooproplistutilutilhfoo + +/*** + This file is part of PulseAudio. + + Copyright 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 by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include <pulse/proplist.h> + +void pa_init_proplist(pa_proplist *p); + +#endif diff --git a/src/pulsecore/props.c b/src/pulsecore/props.c index cbf748df..23d432ec 100644 --- a/src/pulsecore/props.c +++ b/src/pulsecore/props.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/props.h b/src/pulsecore/props.h index 880325f6..95db229b 100644 --- a/src/pulsecore/props.h +++ b/src/pulsecore/props.h @@ -1,8 +1,6 @@ #ifndef foopropshfoo #define foopropshfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/protocol-cli.c b/src/pulsecore/protocol-cli.c index 2f797a14..30cb475d 100644 --- a/src/pulsecore/protocol-cli.c +++ b/src/pulsecore/protocol-cli.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/protocol-cli.h b/src/pulsecore/protocol-cli.h index 3870def3..8922ac62 100644 --- a/src/pulsecore/protocol-cli.h +++ b/src/pulsecore/protocol-cli.h @@ -1,8 +1,6 @@ #ifndef fooprotocolclihfoo #define fooprotocolclihfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/protocol-esound.c b/src/pulsecore/protocol-esound.c index 492dc9fa..db1b4305 100644 --- a/src/pulsecore/protocol-esound.c +++ b/src/pulsecore/protocol-esound.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -1042,7 +1040,7 @@ static int do_read(connection *c) { } if (!c->playback.current_memblock) { - pa_assert_se(c->playback.current_memblock = pa_memblock_new(c->protocol->core->mempool, 0)); + pa_assert_se(c->playback.current_memblock = pa_memblock_new(c->protocol->core->mempool, (size_t) -1)); c->playback.memblock_index = 0; space = pa_memblock_get_length(c->playback.current_memblock); @@ -1275,6 +1273,8 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk } else { size_t m; + chunk->length = PA_MIN(length, chunk->length); + c->playback.underrun = FALSE; pa_memblockq_drop(c->input_memblockq, chunk->length); diff --git a/src/pulsecore/protocol-esound.h b/src/pulsecore/protocol-esound.h index 868ef5d2..0c9447d3 100644 --- a/src/pulsecore/protocol-esound.h +++ b/src/pulsecore/protocol-esound.h @@ -1,8 +1,6 @@ #ifndef fooprotocolesoundhfoo #define fooprotocolesoundhfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/protocol-http.c b/src/pulsecore/protocol-http.c index bc2e9af6..03990435 100644 --- a/src/pulsecore/protocol-http.c +++ b/src/pulsecore/protocol-http.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/protocol-http.h b/src/pulsecore/protocol-http.h index cf952476..e3372335 100644 --- a/src/pulsecore/protocol-http.h +++ b/src/pulsecore/protocol-http.h @@ -1,8 +1,6 @@ #ifndef fooprotocolhttphfoo #define fooprotocolhttphfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c index 2adcdfc7..923a5665 100644 --- a/src/pulsecore/protocol-native.c +++ b/src/pulsecore/protocol-native.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -132,7 +130,8 @@ typedef struct upload_stream { struct connection { pa_msgobject parent; - pa_bool_t authorized; + pa_bool_t authorized:1; + pa_bool_t is_local:1; uint32_t version; pa_protocol_native *protocol; pa_client *client; @@ -485,7 +484,7 @@ static void fix_record_buffer_attr_pre(record_stream *s, pa_bool_t adjust_latenc pa_usec_t fragsize_usec; /* So, the user asked us to adjust the latency according to - * the what the source can provide. Half the latency will be + * what the source can provide. Half the latency will be * spent on the hw buffer, half of it in the async buffer * queue we maintain for each client. */ @@ -499,7 +498,8 @@ static void fix_record_buffer_attr_pre(record_stream *s, pa_bool_t adjust_latenc fragsize_usec = s->source_latency; *fragsize = pa_usec_to_bytes(fragsize_usec, &s->source_output->sample_spec); - } + } else + s->source_latency = 0; } static void fix_record_buffer_attr_post(record_stream *s, uint32_t *maxlength, uint32_t *fragsize) { @@ -533,7 +533,8 @@ static record_stream* record_stream_new( uint32_t *fragsize, pa_source_output_flags_t flags, pa_proplist *p, - pa_bool_t adjust_latency) { + pa_bool_t adjust_latency, + pa_sink_input *direct_on_input) { record_stream *s; pa_source_output *source_output; @@ -553,6 +554,7 @@ static record_stream* record_stream_new( data.module = c->protocol->module; data.client = c->client; data.source = source; + data.direct_on_input = direct_on_input; pa_source_output_new_data_set_sample_spec(&data, ss); pa_source_output_new_data_set_channel_map(&data, map); if (peak_detect) @@ -597,6 +599,11 @@ static record_stream* record_stream_new( pa_idxset_put(c->record_streams, s, &s->index); + pa_log_info("Final latency %0.2f ms = %0.2f ms + %0.2f ms", + ((double) pa_bytes_to_usec(s->fragment_size, &source_output->sample_spec) + (double) s->source_latency) / PA_USEC_PER_MSEC, + (double) pa_bytes_to_usec(s->fragment_size, &source_output->sample_spec) / PA_USEC_PER_MSEC, + (double) s->source_latency / PA_USEC_PER_MSEC); + pa_source_output_put(s->source_output); return s; } @@ -803,7 +810,7 @@ static void fix_playback_buffer_attr_pre(playback_stream *s, pa_bool_t adjust_la if (*tlength <= *minreq) *tlength = *minreq*2 + frame_size; - if (*prebuf <= 0) + if (*prebuf <= 0 || *prebuf > *tlength) *prebuf = *tlength; } @@ -1281,7 +1288,7 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk if (pa_memblockq_peek(s->memblockq, chunk) < 0) { -/* pa_log("UNDERRUN: %lu", pa_memblockq_get_length(s->memblockq)); */ +/* pa_log("UNDERRUN: %lu", (unsigned long) pa_memblockq_get_length(s->memblockq)); */ if (s->drain_request && pa_sink_input_safe_to_remove(i)) { s->drain_request = FALSE; @@ -1298,6 +1305,8 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk /* pa_log("NOTUNDERRUN %lu", (unsigned long) chunk->length); */ + chunk->length = PA_MIN(nbytes, chunk->length); + if (i->thread_info.underrun_for > 0) pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_STARTED, NULL, 0, NULL, NULL); @@ -1750,7 +1759,7 @@ static void command_create_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_ record_stream *s; uint32_t maxlength, fragment_size; uint32_t source_index; - const char *name, *source_name; + const char *name = NULL, *source_name; pa_sample_spec ss; pa_channel_map map; pa_tagstruct *reply; @@ -1768,6 +1777,8 @@ static void command_create_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_ peak_detect = FALSE; pa_source_output_flags_t flags = 0; pa_proplist *p; + uint32_t direct_on_input_idx = PA_INVALID_INDEX; + pa_sink_input *direct_on_input = NULL; connection_assert_ref(c); pa_assert(t); @@ -1816,7 +1827,8 @@ static void command_create_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_ if (pa_tagstruct_get_boolean(t, &peak_detect) < 0 || pa_tagstruct_get_boolean(t, &adjust_latency) < 0 || - pa_tagstruct_get_proplist(t, p) < 0) { + pa_tagstruct_get_proplist(t, p) < 0 || + pa_tagstruct_getu32(t, &direct_on_input_idx) < 0) { protocol_error(c); pa_proplist_free(p); return; @@ -1846,6 +1858,15 @@ static void command_create_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_ } } + if (direct_on_input_idx != PA_INVALID_INDEX) { + + if (!(direct_on_input = pa_idxset_get_by_index(c->protocol->core->sink_inputs, direct_on_input_idx))) { + pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY); + pa_proplist_free(p); + return; + } + } + flags = (corked ? PA_SOURCE_OUTPUT_START_CORKED : 0) | (no_remap ? PA_SOURCE_OUTPUT_NO_REMAP : 0) | @@ -1856,7 +1877,7 @@ static void command_create_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_ (no_move ? PA_SOURCE_OUTPUT_DONT_MOVE : 0) | (variable_rate ? PA_SOURCE_OUTPUT_VARIABLE_RATE : 0); - s = record_stream_new(c, source, &ss, &map, peak_detect, &maxlength, &fragment_size, flags, p, adjust_latency); + s = record_stream_new(c, source, &ss, &map, peak_detect, &maxlength, &fragment_size, flags, p, adjust_latency, direct_on_input); pa_proplist_free(p); CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_INVALID); @@ -1914,7 +1935,7 @@ static void command_auth(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t connection *c = CONNECTION(userdata); const void*cookie; pa_tagstruct *reply; - char tmp[16]; + pa_bool_t shm_on_remote, do_shm; connection_assert_ref(c); pa_assert(t); @@ -1932,8 +1953,17 @@ static void command_auth(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t return; } - pa_snprintf(tmp, sizeof(tmp), "%u", c->version); - pa_proplist_sets(c->client->proplist, "native-protocol.version", tmp); + /* Starting with protocol version 13 the MSB of the version tag + reflects if shm is available for this connection or + not. */ + if (c->version >= 13) { + shm_on_remote = !!(c->version & 0x80000000U); + c->version &= 0x7FFFFFFFU; + } + + pa_log_debug("Protocol version: remote %u, local %u", c->version, PA_PROTOCOL_VERSION); + + pa_proplist_setf(c->client->proplist, "native-protocol.version", "%u", c->version); if (!c->authorized) { pa_bool_t success = FALSE; @@ -1964,16 +1994,7 @@ static void command_auth(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t pa_log_info("Got credentials: uid=%lu gid=%lu success=%i", (unsigned long) creds->uid, (unsigned long) creds->gid, - success); - - if (c->version >= 10 && - pa_mempool_is_shared(c->protocol->core->mempool) && - creds->uid == getuid()) { - - pa_pstream_enable_shm(c->pstream, TRUE); - pa_log_info("Enabled SHM for new connection"); - } - + (int) success); } #endif @@ -1993,8 +2014,32 @@ static void command_auth(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t } } + /* Enable shared memory support if possible */ + do_shm = + pa_mempool_is_shared(c->protocol->core->mempool) && + c->is_local; + + pa_log_debug("SHM possible: %s", pa_yes_no(do_shm)); + + if (do_shm) + if (c->version < 10 || (c->version >= 13 && !shm_on_remote)) + do_shm = FALSE; + + if (do_shm) { + /* Only enable SHM if both sides are owned by the same + * user. This is a security measure because otherwise data + * private to the user might leak. */ + + const pa_creds *creds; + if (!(creds = pa_pdispatch_creds(pd)) || getuid() != creds->uid) + do_shm = FALSE; + } + + pa_log_debug("Negotiated SHM: %s", pa_yes_no(do_shm)); + pa_pstream_enable_shm(c->pstream, do_shm); + reply = reply_new(tag); - pa_tagstruct_putu32(reply, PA_PROTOCOL_VERSION); + pa_tagstruct_putu32(reply, PA_PROTOCOL_VERSION | (do_shm ? 0x80000000 : 0)); #ifdef HAVE_CREDS { @@ -2229,7 +2274,7 @@ static void command_create_upload_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_ connection_assert_ref(c); pa_assert(t); - if ((c->version < 13 && pa_tagstruct_gets(t, &name) < 0) || + if (pa_tagstruct_gets(t, &name) < 0 || pa_tagstruct_get_sample_spec(t, &ss) < 0 || pa_tagstruct_get_channel_map(t, &map) < 0 || pa_tagstruct_getu32(t, &length) < 0) { @@ -2244,9 +2289,6 @@ static void command_create_upload_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_ CHECK_VALIDITY(c->pstream, (length % pa_frame_size(&ss)) == 0 && length > 0, tag, PA_ERR_INVALID); CHECK_VALIDITY(c->pstream, length <= PA_SCACHE_ENTRY_SIZE_MAX, tag, PA_ERR_TOOLARGE); - if (c->version < 13) - CHECK_VALIDITY(c->pstream, name && *name && pa_utf8_valid(name), tag, PA_ERR_INVALID); - p = pa_proplist_new(); if (c->version >= 13 && pa_tagstruct_get_proplist(t, p) < 0) { @@ -2257,6 +2299,11 @@ static void command_create_upload_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_ if (c->version < 13) pa_proplist_sets(p, PA_PROP_MEDIA_NAME, name); + else if (!name) + if (!(name = pa_proplist_gets(p, PA_PROP_EVENT_ID))) + name = pa_proplist_gets(p, PA_PROP_MEDIA_NAME); + + CHECK_VALIDITY(c->pstream, name && *name && pa_utf8_valid(name), tag, PA_ERR_INVALID); s = upload_stream_new(c, &ss, &map, name, length, p); pa_proplist_free(p); @@ -2488,6 +2535,7 @@ static void module_fill_tagstruct(pa_tagstruct *t, pa_module *module) { static void sink_input_fill_tagstruct(connection *c, pa_tagstruct *t, pa_sink_input *s) { pa_sample_spec fixed_ss; + pa_usec_t sink_latency; pa_assert(t); pa_sink_input_assert_ref(s); @@ -2502,8 +2550,8 @@ static void sink_input_fill_tagstruct(connection *c, pa_tagstruct *t, pa_sink_in pa_tagstruct_put_sample_spec(t, &fixed_ss); pa_tagstruct_put_channel_map(t, &s->channel_map); pa_tagstruct_put_cvolume(t, &s->volume); - pa_tagstruct_put_usec(t, pa_sink_input_get_latency(s)); - pa_tagstruct_put_usec(t, pa_sink_get_latency(s->sink)); + pa_tagstruct_put_usec(t, pa_sink_input_get_latency(s, &sink_latency)); + pa_tagstruct_put_usec(t, sink_latency); pa_tagstruct_puts(t, pa_resample_method_to_string(pa_sink_input_get_resample_method(s))); pa_tagstruct_puts(t, s->driver); if (c->version >= 11) @@ -2514,6 +2562,7 @@ static void sink_input_fill_tagstruct(connection *c, pa_tagstruct *t, pa_sink_in static void source_output_fill_tagstruct(connection *c, pa_tagstruct *t, pa_source_output *s) { pa_sample_spec fixed_ss; + pa_usec_t source_latency; pa_assert(t); pa_source_output_assert_ref(s); @@ -2527,8 +2576,8 @@ static void source_output_fill_tagstruct(connection *c, pa_tagstruct *t, pa_sour pa_tagstruct_putu32(t, s->source->index); pa_tagstruct_put_sample_spec(t, &fixed_ss); pa_tagstruct_put_channel_map(t, &s->channel_map); - pa_tagstruct_put_usec(t, pa_source_output_get_latency(s)); - pa_tagstruct_put_usec(t, pa_source_get_latency(s->source)); + pa_tagstruct_put_usec(t, pa_source_output_get_latency(s, &source_latency)); + pa_tagstruct_put_usec(t, source_latency); pa_tagstruct_puts(t, pa_resample_method_to_string(pa_source_output_get_resample_method(s))); pa_tagstruct_puts(t, s->driver); @@ -2542,12 +2591,15 @@ static void scache_fill_tagstruct(connection *c, pa_tagstruct *t, pa_scache_entr pa_assert(t); pa_assert(e); - fixup_sample_spec(c, &fixed_ss, &e->sample_spec); + if (e->memchunk.memblock) + fixup_sample_spec(c, &fixed_ss, &e->sample_spec); + else + memset(&fixed_ss, 0, sizeof(fixed_ss)); pa_tagstruct_putu32(t, e->index); pa_tagstruct_puts(t, e->name); pa_tagstruct_put_cvolume(t, &e->volume); - pa_tagstruct_put_usec(t, pa_bytes_to_usec(e->memchunk.length, &e->sample_spec)); + pa_tagstruct_put_usec(t, e->memchunk.memblock ? pa_bytes_to_usec(e->memchunk.length, &e->sample_spec) : 0); pa_tagstruct_put_sample_spec(t, &fixed_ss); pa_tagstruct_put_channel_map(t, &e->channel_map); pa_tagstruct_putu32(t, e->memchunk.length); @@ -3885,7 +3937,6 @@ static void on_connection(PA_GCC_UNUSED pa_socket_server*s, pa_iochannel *io, vo connection *c; char cname[256], pname[128]; - pa_assert(s); pa_assert(io); pa_assert(p); @@ -3914,6 +3965,7 @@ static void on_connection(PA_GCC_UNUSED pa_socket_server*s, pa_iochannel *io, vo } else c->auth_timeout_event = NULL; + c->is_local = pa_iochannel_socket_is_local(io); c->version = 8; c->protocol = p; pa_iochannel_socket_peer_to_string(io, pname, sizeof(pname)); @@ -3946,7 +3998,6 @@ static void on_connection(PA_GCC_UNUSED pa_socket_server*s, pa_iochannel *io, vo #ifdef HAVE_CREDS if (pa_iochannel_creds_supported(io)) pa_iochannel_creds_enable(io); - #endif } @@ -4005,7 +4056,7 @@ static pa_protocol_native* protocol_new_internal(pa_core *c, pa_module *m, pa_mo pa_log("auth-group-enabled= expects a boolean argument."); return NULL; } - p->auth_group = a ? pa_xstrdup(pa_modargs_get_value(ma, "auth-group", c->is_system_instance ? PA_ACCESS_GROUP : NULL)) : NULL; + p->auth_group = a ? pa_xstrdup(pa_modargs_get_value(ma, "auth-group", pa_in_system_mode() ? PA_ACCESS_GROUP : NULL)) : NULL; if (p->auth_group) pa_log_info("Allowing access to group '%s'.", p->auth_group); diff --git a/src/pulsecore/protocol-native.h b/src/pulsecore/protocol-native.h index bf05f937..a52fa8cf 100644 --- a/src/pulsecore/protocol-native.h +++ b/src/pulsecore/protocol-native.h @@ -1,8 +1,6 @@ #ifndef fooprotocolnativehfoo #define fooprotocolnativehfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/protocol-simple.c b/src/pulsecore/protocol-simple.c index cbe48440..020a281d 100644 --- a/src/pulsecore/protocol-simple.c +++ b/src/pulsecore/protocol-simple.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -374,6 +372,8 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk } else { size_t m; + chunk->length = PA_MIN(length, chunk->length); + c->playback.underrun = FALSE; pa_memblockq_drop(c->input_memblockq, chunk->length); diff --git a/src/pulsecore/protocol-simple.h b/src/pulsecore/protocol-simple.h index 3b02c88e..e1b3143b 100644 --- a/src/pulsecore/protocol-simple.h +++ b/src/pulsecore/protocol-simple.h @@ -1,8 +1,6 @@ #ifndef fooprotocolsimplehfoo #define fooprotocolsimplehfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/pstream-util.c b/src/pulsecore/pstream-util.c index a6932158..f84f486a 100644 --- a/src/pulsecore/pstream-util.c +++ b/src/pulsecore/pstream-util.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/pstream-util.h b/src/pulsecore/pstream-util.h index 67759f2a..ae0d79c3 100644 --- a/src/pulsecore/pstream-util.h +++ b/src/pulsecore/pstream-util.h @@ -1,8 +1,6 @@ #ifndef foopstreamutilhfoo #define foopstreamutilhfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/pstream.c b/src/pulsecore/pstream.c index 5004d67f..e26ca473 100644 --- a/src/pulsecore/pstream.c +++ b/src/pulsecore/pstream.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/pstream.h b/src/pulsecore/pstream.h index 00b37b71..a528b25c 100644 --- a/src/pulsecore/pstream.h +++ b/src/pulsecore/pstream.h @@ -1,8 +1,6 @@ #ifndef foopstreamhfoo #define foopstreamhfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/queue.c b/src/pulsecore/queue.c index 9b6a37f0..08fd1426 100644 --- a/src/pulsecore/queue.c +++ b/src/pulsecore/queue.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/queue.h b/src/pulsecore/queue.h index cd767364..b09216cf 100644 --- a/src/pulsecore/queue.h +++ b/src/pulsecore/queue.h @@ -1,8 +1,6 @@ #ifndef fooqueuehfoo #define fooqueuehfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/random.c b/src/pulsecore/random.c index 87afebfa..5deac37b 100644 --- a/src/pulsecore/random.c +++ b/src/pulsecore/random.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -64,7 +62,11 @@ static int random_proper(void *ret_data, size_t length) { while (*device) { ret = 0; - if ((fd = open(*device, O_RDONLY)) >= 0) { + if ((fd = open(*device, O_RDONLY +#ifdef O_NOCTTY + | O_NOCTTY +#endif + )) >= 0) { if ((r = pa_loop_read(fd, ret_data, length, NULL)) < 0 || (size_t) r != length) ret = -1; diff --git a/src/pulsecore/random.h b/src/pulsecore/random.h index 01b7d746..36d7f9df 100644 --- a/src/pulsecore/random.h +++ b/src/pulsecore/random.h @@ -1,8 +1,6 @@ #ifndef foorandomhfoo #define foorandomhfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/refcnt.h b/src/pulsecore/refcnt.h index f0885fb4..291f4504 100644 --- a/src/pulsecore/refcnt.h +++ b/src/pulsecore/refcnt.h @@ -1,8 +1,6 @@ #ifndef foopulserefcnthfoo #define foopulserefcnthfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/resampler.c b/src/pulsecore/resampler.c index d645639c..00dc794c 100644 --- a/src/pulsecore/resampler.c +++ b/src/pulsecore/resampler.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -1457,10 +1455,10 @@ static void peaks_resample(pa_resampler *r, const pa_memchunk *input, unsigned i r->peaks.max_i[c] = n; } - for (c = 0; c < r->o_ss.channels; c++, d++) + for (c = 0; c < r->o_ss.channels; c++, d++) { *d = r->peaks.max_i[c]; - - memset(r->peaks.max_i, 0, sizeof(r->peaks.max_i)); + r->peaks.max_i[c] = 0; + } } else { unsigned i, c; float *s = (float*) ((uint8_t*) src + fz * j); @@ -1476,11 +1474,13 @@ static void peaks_resample(pa_resampler *r, const pa_memchunk *input, unsigned i r->peaks.max_f[c] = n; } - for (c = 0; c < r->o_ss.channels; c++, d++) + for (c = 0; c < r->o_ss.channels; c++, d++) { *d = r->peaks.max_f[c]; - - memset(r->peaks.max_f, 0, sizeof(r->peaks.max_f)); + r->peaks.max_f[c] = 0; + } } + + start = j+1; } pa_memblock_release(input->memblock); diff --git a/src/pulsecore/resampler.h b/src/pulsecore/resampler.h index 8534f5b5..5e302a9b 100644 --- a/src/pulsecore/resampler.h +++ b/src/pulsecore/resampler.h @@ -1,8 +1,6 @@ #ifndef fooresamplerhfoo #define fooresamplerhfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/rtclock.c b/src/pulsecore/rtclock.c index e74e5243..f33de830 100644 --- a/src/pulsecore/rtclock.c +++ b/src/pulsecore/rtclock.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/rtclock.h b/src/pulsecore/rtclock.h index f68ad761..705de48f 100644 --- a/src/pulsecore/rtclock.h +++ b/src/pulsecore/rtclock.h @@ -1,8 +1,6 @@ #ifndef foopulsertclockhfoo #define foopulsertclockhfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/rtpoll.c b/src/pulsecore/rtpoll.c index 64fa42ad..a67a5516 100644 --- a/src/pulsecore/rtpoll.c +++ b/src/pulsecore/rtpoll.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -514,6 +512,9 @@ void pa_rtpoll_set_timer_absolute(pa_rtpoll *p, pa_usec_t usec) { void pa_rtpoll_set_timer_relative(pa_rtpoll *p, pa_usec_t usec) { pa_assert(p); + /* Scheduling a timeout for more than an hour is very very suspicious */ + pa_assert(usec <= PA_USEC_PER_SEC*60ULL*60ULL); + pa_rtclock_get(&p->next_elapse); pa_timeval_add(&p->next_elapse, usec); p->timer_enabled = TRUE; diff --git a/src/pulsecore/rtpoll.h b/src/pulsecore/rtpoll.h index 16dadbc3..08776ef0 100644 --- a/src/pulsecore/rtpoll.h +++ b/src/pulsecore/rtpoll.h @@ -1,8 +1,6 @@ #ifndef foopulsertpollhfoo #define foopulsertpollhfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/rtsig.c b/src/pulsecore/rtsig.c index bfc49c88..4df217c3 100644 --- a/src/pulsecore/rtsig.c +++ b/src/pulsecore/rtsig.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/rtsig.h b/src/pulsecore/rtsig.h index 7830d272..e414122d 100644 --- a/src/pulsecore/rtsig.h +++ b/src/pulsecore/rtsig.h @@ -1,8 +1,6 @@ #ifndef foopulsertsighfoo #define foopulsertsighfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/sample-util.c b/src/pulsecore/sample-util.c index a8028296..b42b79d1 100644 --- a/src/pulsecore/sample-util.c +++ b/src/pulsecore/sample-util.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/sample-util.h b/src/pulsecore/sample-util.h index 59b4c632..cef70750 100644 --- a/src/pulsecore/sample-util.h +++ b/src/pulsecore/sample-util.h @@ -1,8 +1,6 @@ #ifndef foosampleutilhfoo #define foosampleutilhfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/sconv-s16be.c b/src/pulsecore/sconv-s16be.c index 638beb2e..e7e1449a 100644 --- a/src/pulsecore/sconv-s16be.c +++ b/src/pulsecore/sconv-s16be.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/sconv-s16be.h b/src/pulsecore/sconv-s16be.h index 454c9508..60580815 100644 --- a/src/pulsecore/sconv-s16be.h +++ b/src/pulsecore/sconv-s16be.h @@ -1,8 +1,6 @@ #ifndef foosconv_s16befoo #define foosconv_s16befoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/sconv-s16le.c b/src/pulsecore/sconv-s16le.c index 90e9b6d2..41670f27 100644 --- a/src/pulsecore/sconv-s16le.c +++ b/src/pulsecore/sconv-s16le.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/sconv-s16le.h b/src/pulsecore/sconv-s16le.h index 4165f8a2..8d4c87c3 100644 --- a/src/pulsecore/sconv-s16le.h +++ b/src/pulsecore/sconv-s16le.h @@ -1,8 +1,6 @@ #ifndef foosconv_s16lefoo #define foosconv_s16lefoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/sconv.c b/src/pulsecore/sconv.c index ebd74586..581e4681 100644 --- a/src/pulsecore/sconv.c +++ b/src/pulsecore/sconv.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/sconv.h b/src/pulsecore/sconv.h index 901f50a3..59710369 100644 --- a/src/pulsecore/sconv.h +++ b/src/pulsecore/sconv.h @@ -1,8 +1,6 @@ #ifndef foosconvhfoo #define foosconvhfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/semaphore-posix.c b/src/pulsecore/semaphore-posix.c index 750c2afc..7c9f859d 100644 --- a/src/pulsecore/semaphore-posix.c +++ b/src/pulsecore/semaphore-posix.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/semaphore-win32.c b/src/pulsecore/semaphore-win32.c index f6576348..41e0b8d4 100644 --- a/src/pulsecore/semaphore-win32.c +++ b/src/pulsecore/semaphore-win32.c @@ -1,5 +1,3 @@ -/* $Id: mutex-win32.c 1426 2007-02-13 15:35:19Z ossman $ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/semaphore.h b/src/pulsecore/semaphore.h index c394e0f2..850ae817 100644 --- a/src/pulsecore/semaphore.h +++ b/src/pulsecore/semaphore.h @@ -1,8 +1,6 @@ #ifndef foopulsesemaphorehfoo #define foopulsesemaphorehfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/shm.c b/src/pulsecore/shm.c index a6843819..298bf716 100644 --- a/src/pulsecore/shm.c +++ b/src/pulsecore/shm.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/shm.h b/src/pulsecore/shm.h index 60bc355f..c2adbd07 100644 --- a/src/pulsecore/shm.h +++ b/src/pulsecore/shm.h @@ -1,8 +1,6 @@ #ifndef foopulseshmhfoo #define foopulseshmhfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/shmasyncq.c b/src/pulsecore/shmasyncq.c index 7af2985c..eac56adb 100644 --- a/src/pulsecore/shmasyncq.c +++ b/src/pulsecore/shmasyncq.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/shmasyncq.h b/src/pulsecore/shmasyncq.h index ca35ffd2..9845c391 100644 --- a/src/pulsecore/shmasyncq.h +++ b/src/pulsecore/shmasyncq.h @@ -1,8 +1,6 @@ #ifndef foopulseshmasyncqhfoo #define foopulseshmasyncqhfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c index d51ff810..43e7bb21 100644 --- a/src/pulsecore/sink-input.c +++ b/src/pulsecore/sink-input.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -44,7 +42,6 @@ #define MEMBLOCKQ_MAXLENGTH (32*1024*1024) #define CONVERT_BUFFER_LENGTH (PA_PAGE_SIZE) -#define MOVE_BUFFER_LENGTH (PA_PAGE_SIZE*256) static PA_DEFINE_CHECK_TYPE(pa_sink_input, pa_msgobject); @@ -94,12 +91,16 @@ void pa_sink_input_new_data_done(pa_sink_input_new_data *data) { pa_proplist_free(data->proplist); } +/* Called from main context */ static void reset_callbacks(pa_sink_input *i) { pa_assert(i); i->pop = NULL; i->process_rewind = NULL; i->update_max_rewind = NULL; + i->update_max_request = NULL; + i->update_sink_requested_latency = NULL; + i->update_sink_latency_range = NULL; i->attach = NULL; i->detach = NULL; i->suspend = NULL; @@ -109,6 +110,7 @@ static void reset_callbacks(pa_sink_input *i) { i->state_change = NULL; } +/* Called from main context */ pa_sink_input* pa_sink_input_new( pa_core *core, pa_sink_input_new_data *data, @@ -127,7 +129,7 @@ pa_sink_input* pa_sink_input_new( pa_return_null_if_fail(!data->driver || pa_utf8_valid(data->driver)); if (!data->sink) - data->sink = pa_namereg_get(core, NULL, PA_NAMEREG_SINK, 1); + data->sink = pa_namereg_get(core, NULL, PA_NAMEREG_SINK, TRUE); pa_return_null_if_fail(data->sink); pa_return_null_if_fail(pa_sink_get_state(data->sink) != PA_SINK_UNLINKED); @@ -237,6 +239,8 @@ pa_sink_input* pa_sink_input_new( } else i->sync_next = i->sync_prev = NULL; + i->direct_outputs = pa_idxset_new(NULL, NULL); + reset_callbacks(i); i->userdata = NULL; @@ -252,6 +256,7 @@ pa_sink_input* pa_sink_input_new( i->thread_info.rewrite_flush = FALSE; i->thread_info.underrun_for = (uint64_t) -1; i->thread_info.playing_for = 0; + i->thread_info.direct_outputs = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); i->thread_info.render_memblockq = pa_memblockq_new( 0, @@ -278,6 +283,7 @@ pa_sink_input* pa_sink_input_new( return i; } +/* Called from main context */ static void update_n_corked(pa_sink_input *i, pa_sink_input_state_t state) { pa_assert(i); @@ -289,6 +295,7 @@ static void update_n_corked(pa_sink_input *i, pa_sink_input_state_t state) { pa_sink_update_status(i->sink); } +/* Called from main context */ static int sink_input_set_state(pa_sink_input *i, pa_sink_input_state_t state) { pa_sink_input *ssync; pa_assert(i); @@ -299,8 +306,7 @@ static int sink_input_set_state(pa_sink_input *i, pa_sink_input_state_t state) { if (i->state == state) return 0; - if (pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_STATE, PA_UINT_TO_PTR(state), 0, NULL) < 0) - return -1; + pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_STATE, PA_UINT_TO_PTR(state), 0, NULL) == 0); update_n_corked(i, state); i->state = state; @@ -314,14 +320,23 @@ static int sink_input_set_state(pa_sink_input *i, pa_sink_input_state_t state) { ssync->state = state; } - if (state != PA_SINK_INPUT_UNLINKED) + if (state != PA_SINK_INPUT_UNLINKED) { pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED], i); + for (ssync = i->sync_prev; ssync; ssync = ssync->sync_prev) + pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED], ssync); + + for (ssync = i->sync_next; ssync; ssync = ssync->sync_next) + pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED], ssync); + } + return 0; } +/* Called from main context */ void pa_sink_input_unlink(pa_sink_input *i) { pa_bool_t linked; + pa_source_output *o, *p = NULL; pa_assert(i); /* See pa_sink_unlink() for a couple of comments how this function @@ -345,12 +360,18 @@ void pa_sink_input_unlink(pa_sink_input *i) { if (pa_idxset_remove_by_data(i->sink->inputs, i, NULL)) pa_sink_input_unref(i); + while ((o = pa_idxset_first(i->direct_outputs, NULL))) { + pa_assert(o != p); + pa_source_output_kill(o); + p = o; + } + update_n_corked(i, PA_SINK_INPUT_UNLINKED); i->state = PA_SINK_INPUT_UNLINKED; if (linked) if (i->sink->asyncmsgq) - pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_REMOVE_INPUT, i, 0, NULL); + pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_REMOVE_INPUT, i, 0, NULL) == 0); reset_callbacks(i); @@ -363,6 +384,7 @@ void pa_sink_input_unlink(pa_sink_input *i) { pa_sink_input_unref(i); } +/* Called from main context */ static void sink_input_free(pa_object *o) { pa_sink_input* i = PA_SINK_INPUT(o); @@ -385,17 +407,27 @@ static void sink_input_free(pa_object *o) { if (i->proplist) pa_proplist_free(i->proplist); + if (i->direct_outputs) + pa_idxset_free(i->direct_outputs, NULL, NULL); + + if (i->thread_info.direct_outputs) + pa_hashmap_free(i->thread_info.direct_outputs, NULL, NULL); + pa_xfree(i->driver); pa_xfree(i); } +/* Called from main context */ void pa_sink_input_put(pa_sink_input *i) { pa_sink_input_state_t state; pa_sink_input_assert_ref(i); pa_assert(i->state == PA_SINK_INPUT_INIT); + + /* The following fields must be initialized properly */ pa_assert(i->pop); pa_assert(i->process_rewind); + pa_assert(i->kill); i->thread_info.volume = i->volume; i->thread_info.muted = i->muted; @@ -405,33 +437,36 @@ void pa_sink_input_put(pa_sink_input *i) { update_n_corked(i, state); i->state = state; - pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_ADD_INPUT, i, 0, NULL); + pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_ADD_INPUT, i, 0, NULL) == 0); pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW, i->index); pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_PUT], i); } +/* Called from main context */ void pa_sink_input_kill(pa_sink_input*i) { pa_sink_input_assert_ref(i); pa_assert(PA_SINK_INPUT_IS_LINKED(i->state)); - if (i->kill) - i->kill(i); + i->kill(i); } -pa_usec_t pa_sink_input_get_latency(pa_sink_input *i) { - pa_usec_t r = 0; +/* Called from main context */ +pa_usec_t pa_sink_input_get_latency(pa_sink_input *i, pa_usec_t *sink_latency) { + pa_usec_t r[2] = { 0, 0 }; pa_sink_input_assert_ref(i); pa_assert(PA_SINK_INPUT_IS_LINKED(i->state)); - if (pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_GET_LATENCY, &r, 0, NULL) < 0) - r = 0; + pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_GET_LATENCY, r, 0, NULL) == 0); if (i->get_latency) - r += i->get_latency(i); + r[0] += i->get_latency(i); + + if (sink_latency) + *sink_latency = r[1]; - return r; + return r[0]; } /* Called from thread context */ @@ -684,42 +719,54 @@ void pa_sink_input_update_max_rewind(pa_sink_input *i, size_t nbytes /* in the i->update_max_rewind(i, i->thread_info.resampler ? pa_resampler_request(i->thread_info.resampler, nbytes) : nbytes); } +/* Called from thread context */ +void pa_sink_input_update_max_request(pa_sink_input *i, size_t nbytes /* in the sink's sample spec */) { + pa_sink_input_assert_ref(i); + pa_assert(PA_SINK_INPUT_IS_LINKED(i->thread_info.state)); + pa_assert(pa_frame_aligned(nbytes, &i->sink->sample_spec)); + + if (i->update_max_request) + i->update_max_request(i, i->thread_info.resampler ? pa_resampler_request(i->thread_info.resampler, nbytes) : nbytes); +} + +/* Called from thread context */ static pa_usec_t fixup_latency(pa_sink *s, pa_usec_t usec) { pa_sink_assert_ref(s); if (usec == (pa_usec_t) -1) return usec; - if (s->max_latency > 0 && usec > s->max_latency) - usec = s->max_latency; + if (s->thread_info.max_latency > 0 && usec > s->thread_info.max_latency) + usec = s->thread_info.max_latency; - if (s->min_latency > 0 && usec < s->min_latency) - usec = s->min_latency; + if (s->thread_info.min_latency > 0 && usec < s->thread_info.min_latency) + usec = s->thread_info.min_latency; return usec; } +/* Called from thread context */ pa_usec_t pa_sink_input_set_requested_latency_within_thread(pa_sink_input *i, pa_usec_t usec) { pa_sink_input_assert_ref(i); usec = fixup_latency(i->sink, usec); - i->thread_info.requested_sink_latency = usec; pa_sink_invalidate_requested_latency(i->sink); return usec; } +/* Called from main context */ pa_usec_t pa_sink_input_set_requested_latency(pa_sink_input *i, pa_usec_t usec) { pa_sink_input_assert_ref(i); - usec = fixup_latency(i->sink, usec); - if (PA_SINK_INPUT_IS_LINKED(i->state)) - pa_asyncmsgq_post(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_REQUESTED_LATENCY, NULL, (int64_t) usec, NULL, NULL); + pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_REQUESTED_LATENCY, &usec, 0, NULL) == 0); else { /* If this sink input is not realized yet, we have to touch * the thread info data directly */ + + usec = fixup_latency(i->sink, usec); i->thread_info.requested_sink_latency = usec; i->sink->thread_info.requested_latency_valid = FALSE; } @@ -727,13 +774,14 @@ pa_usec_t pa_sink_input_set_requested_latency(pa_sink_input *i, pa_usec_t usec) return usec; } +/* Called from main context */ pa_usec_t pa_sink_input_get_requested_latency(pa_sink_input *i) { pa_usec_t usec = 0; pa_sink_input_assert_ref(i); if (PA_SINK_INPUT_IS_LINKED(i->state)) - pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_GET_REQUESTED_LATENCY, &usec, 0, NULL); + pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_GET_REQUESTED_LATENCY, &usec, 0, NULL) == 0); else /* If this sink input is not realized yet, we have to touch * the thread info data directly */ @@ -742,19 +790,22 @@ pa_usec_t pa_sink_input_get_requested_latency(pa_sink_input *i) { return usec; } +/* Called from main context */ void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume) { pa_sink_input_assert_ref(i); pa_assert(PA_SINK_INPUT_IS_LINKED(i->state)); + pa_assert(volume); if (pa_cvolume_equal(&i->volume, volume)) return; i->volume = *volume; - pa_asyncmsgq_post(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_VOLUME, pa_xnewdup(struct pa_cvolume, volume, 1), 0, NULL, pa_xfree); + pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_VOLUME, &i->volume, 0, NULL) == 0); pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index); } +/* Called from main context */ const pa_cvolume *pa_sink_input_get_volume(pa_sink_input *i) { pa_sink_input_assert_ref(i); pa_assert(PA_SINK_INPUT_IS_LINKED(i->state)); @@ -762,6 +813,7 @@ const pa_cvolume *pa_sink_input_get_volume(pa_sink_input *i) { return &i->volume; } +/* Called from main context */ void pa_sink_input_set_mute(pa_sink_input *i, pa_bool_t mute) { pa_assert(i); pa_sink_input_assert_ref(i); @@ -776,13 +828,15 @@ void pa_sink_input_set_mute(pa_sink_input *i, pa_bool_t mute) { pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index); } -int pa_sink_input_get_mute(pa_sink_input *i) { +/* Called from main context */ +pa_bool_t pa_sink_input_get_mute(pa_sink_input *i) { pa_sink_input_assert_ref(i); pa_assert(PA_SINK_INPUT_IS_LINKED(i->state)); - return !!i->muted; + return i->muted; } +/* Called from main context */ void pa_sink_input_cork(pa_sink_input *i, pa_bool_t b) { pa_sink_input_assert_ref(i); pa_assert(PA_SINK_INPUT_IS_LINKED(i->state)); @@ -790,6 +844,7 @@ void pa_sink_input_cork(pa_sink_input *i, pa_bool_t b) { sink_input_set_state(i, b ? PA_SINK_INPUT_CORKED : PA_SINK_INPUT_RUNNING); } +/* Called from main context */ int pa_sink_input_set_rate(pa_sink_input *i, uint32_t rate) { pa_sink_input_assert_ref(i); pa_assert(PA_SINK_INPUT_IS_LINKED(i->state)); @@ -806,6 +861,7 @@ int pa_sink_input_set_rate(pa_sink_input *i, uint32_t rate) { return 0; } +/* Called from main context */ void pa_sink_input_set_name(pa_sink_input *i, const char *name) { const char *old; pa_sink_input_assert_ref(i); @@ -829,16 +885,19 @@ void pa_sink_input_set_name(pa_sink_input *i, const char *name) { } } +/* Called from main context */ pa_resample_method_t pa_sink_input_get_resample_method(pa_sink_input *i) { pa_sink_input_assert_ref(i); return i->resample_method; } +/* Called from main context */ int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest) { pa_resampler *new_resampler; pa_sink *origin; pa_sink_input_move_hook_data hook_data; + pa_source_output *o, *p = NULL; pa_sink_input_assert_ref(i); pa_assert(PA_SINK_INPUT_IS_LINKED(i->state)); @@ -862,6 +921,13 @@ int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest) { return -1; } + /* Kill directly connected outputs */ + while ((o = pa_idxset_first(i->direct_outputs, NULL))) { + pa_assert(o != p); + pa_source_output_kill(o); + p = o; + } + if (i->thread_info.resampler && pa_sample_spec_equal(&origin->sample_spec, &dest->sample_spec) && pa_channel_map_equal(&origin->channel_map, &dest->channel_map)) @@ -893,7 +959,7 @@ int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest) { hook_data.destination = dest; pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE], &hook_data); - pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_START_MOVE, i, 0, NULL); + pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_START_MOVE, i, 0, NULL) == 0); pa_idxset_remove_by_data(origin->inputs, i, NULL); pa_idxset_put(dest->inputs, i, NULL); @@ -927,7 +993,7 @@ int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest) { pa_sink_update_status(origin); pa_sink_update_status(dest); - pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_FINISH_MOVE, i, 0, NULL); + pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_FINISH_MOVE, i, 0, NULL) == 0); if (i->moved) i->moved(i); @@ -942,6 +1008,7 @@ int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest) { return 0; } +/* Called from IO thread context */ void pa_sink_input_set_state_within_thread(pa_sink_input *i, pa_sink_input_state_t state) { pa_sink_input_assert_ref(i); @@ -971,14 +1038,13 @@ void pa_sink_input_set_state_within_thread(pa_sink_input *i, pa_sink_input_state i->thread_info.state = state; } -/* Called from thread context */ +/* Called from thread context, except when it is not. */ int pa_sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk) { pa_sink_input *i = PA_SINK_INPUT(o); - pa_sink_input_assert_ref(i); - pa_assert(PA_SINK_INPUT_IS_LINKED(i->thread_info.state)); switch (code) { + case PA_SINK_INPUT_MESSAGE_SET_VOLUME: i->thread_info.volume = *((pa_cvolume*) userdata); pa_sink_input_request_rewind(i, 0, TRUE, FALSE); @@ -991,8 +1057,13 @@ int pa_sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t case PA_SINK_INPUT_MESSAGE_GET_LATENCY: { pa_usec_t *r = userdata; + pa_usec_t sink_usec = 0; + + r[0] += pa_bytes_to_usec(pa_memblockq_get_length(i->thread_info.render_memblockq), &i->sink->sample_spec); + + if (i->sink->parent.process_msg(PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_GET_LATENCY, &sink_usec, 0, NULL) >= 0) + r[1] += sink_usec; - *r += pa_bytes_to_usec(pa_memblockq_get_length(i->thread_info.render_memblockq), &i->sink->sample_spec); return 0; } @@ -1017,10 +1088,12 @@ int pa_sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t return 0; } - case PA_SINK_INPUT_MESSAGE_SET_REQUESTED_LATENCY: + case PA_SINK_INPUT_MESSAGE_SET_REQUESTED_LATENCY: { + pa_usec_t *usec = userdata; - pa_sink_input_set_requested_latency_within_thread(i, (pa_usec_t) offset); + *usec = pa_sink_input_set_requested_latency_within_thread(i, *usec); return 0; + } case PA_SINK_INPUT_MESSAGE_GET_REQUESTED_LATENCY: { pa_usec_t *r = userdata; @@ -1033,6 +1106,7 @@ int pa_sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t return -1; } +/* Called from main thread */ pa_sink_input_state_t pa_sink_input_get_state(pa_sink_input *i) { pa_sink_input_assert_ref(i); @@ -1052,6 +1126,7 @@ pa_bool_t pa_sink_input_safe_to_remove(pa_sink_input *i) { return TRUE; } +/* Called from IO context */ void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes /* in our sample spec */, pa_bool_t rewrite, pa_bool_t flush) { size_t lbq; @@ -1111,6 +1186,7 @@ void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes /* in our sam pa_sink_request_rewind(i->sink, nbytes - lbq); } +/* Called from main context */ pa_memchunk* pa_sink_input_get_silence(pa_sink_input *i, pa_memchunk *ret) { pa_sink_input_assert_ref(i); pa_assert(ret); diff --git a/src/pulsecore/sink-input.h b/src/pulsecore/sink-input.h index 5f146122..c07a7404 100644 --- a/src/pulsecore/sink-input.h +++ b/src/pulsecore/sink-input.h @@ -1,8 +1,6 @@ #ifndef foopulsesinkinputhfoo #define foopulsesinkinputhfoo -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -73,13 +71,19 @@ struct pa_sink_input { pa_sink_input_state_t state; pa_sink_input_flags_t flags; - pa_proplist *proplist; char *driver; /* may be NULL */ + pa_proplist *proplist; + pa_module *module; /* may be NULL */ pa_client *client; /* may be NULL */ pa_sink *sink; + /* A sink input may be connected to multiple source outputs + * directly, so that they don't get mixed data of the entire + * source. */ + pa_idxset *direct_outputs; + pa_sample_spec sample_spec; pa_channel_map channel_map; @@ -109,6 +113,18 @@ struct pa_sink_input { * changes. Called from IO context. */ void (*update_max_rewind) (pa_sink_input *i, size_t nbytes); /* may be NULL */ + /* Called whenever the maxiumum request size of the sink + * changes. Called from IO context. */ + void (*update_max_request) (pa_sink_input *i, size_t nbytes); /* may be NULL */ + + /* Called whenever the configured latency of the sink + * changes. Called from IO context. */ + void (*update_sink_requested_latency) (pa_sink_input *i); /* may be NULL */ + + /* Called whenver the latency range of the sink changes. Called + * from IO context. */ + void (*update_sink_latency_range) (pa_sink_input *i); /* may be NULL */ + /* If non-NULL this function is called when the input is first * connected to a sink or when the rtpoll/asyncmsgq fields * change. You usually don't need to implement this function @@ -130,12 +146,12 @@ struct pa_sink_input { /* Supposed to unlink and destroy this stream. Called from main * context. */ - void (*kill) (pa_sink_input *i); /* may be NULL */ + void (*kill) (pa_sink_input *i); /* may NOT be NULL */ /* Return the current latency (i.e. length of bufferd audio) of - this stream. Called from main context. If NULL a - PA_SINK_INPUT_MESSAGE_GET_LATENCY message is sent to the IO thread - instead. */ + this stream. Called from main context. This is added to what the + PA_SINK_INPUT_MESSAGE_GET_LATENCY message sent to the IO thread + returns */ pa_usec_t (*get_latency) (pa_sink_input *i); /* may be NULL */ /* If non_NULL this function is called from thread context if the @@ -166,6 +182,8 @@ struct pa_sink_input { /* The requested latency for the sink */ pa_usec_t requested_sink_latency; + + pa_hashmap *direct_outputs; } thread_info; void *userdata; @@ -253,12 +271,12 @@ int pa_sink_input_set_rate(pa_sink_input *i, uint32_t rate); /* External code may request disconnection with this function */ void pa_sink_input_kill(pa_sink_input*i); -pa_usec_t pa_sink_input_get_latency(pa_sink_input *i); +pa_usec_t pa_sink_input_get_latency(pa_sink_input *i, pa_usec_t *sink_latency); void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume); const pa_cvolume *pa_sink_input_get_volume(pa_sink_input *i); void pa_sink_input_set_mute(pa_sink_input *i, pa_bool_t mute); -int pa_sink_input_get_mute(pa_sink_input *i); +pa_bool_t pa_sink_input_get_mute(pa_sink_input *i); pa_resample_method_t pa_sink_input_get_resample_method(pa_sink_input *i); @@ -274,6 +292,7 @@ int pa_sink_input_peek(pa_sink_input *i, size_t length, pa_memchunk *chunk, pa_c void pa_sink_input_drop(pa_sink_input *i, size_t length); void pa_sink_input_process_rewind(pa_sink_input *i, size_t nbytes /* in the sink's sample spec */); void pa_sink_input_update_max_rewind(pa_sink_input *i, size_t nbytes /* in the sink's sample spec */); +void pa_sink_input_update_max_request(pa_sink_input *i, size_t nbytes /* in the sink's sample spec */); void pa_sink_input_set_state_within_thread(pa_sink_input *i, pa_sink_input_state_t state); diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c index 31c3cfc8..edb023b2 100644 --- a/src/pulsecore/sink.c +++ b/src/pulsecore/sink.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -105,6 +103,7 @@ void pa_sink_new_data_done(pa_sink_new_data *data) { pa_proplist_free(data->proplist); } +/* Called from main context */ static void reset_callbacks(pa_sink *s) { pa_assert(s); @@ -117,6 +116,7 @@ static void reset_callbacks(pa_sink *s) { s->update_requested_latency = NULL; } +/* Called from main context */ pa_sink* pa_sink_new( pa_core *core, pa_sink_new_data *data, @@ -192,7 +192,7 @@ pa_sink* pa_sink_new( s->volume = data->volume; s->muted = data->muted; - s->refresh_volume = s->refresh_mute = FALSE; + s->refresh_volume = s->refresh_muted = FALSE; reset_callbacks(s); s->userdata = NULL; @@ -207,17 +207,17 @@ pa_sink* pa_sink_new( &s->sample_spec, 0); - s->min_latency = DEFAULT_MIN_LATENCY; - s->max_latency = s->min_latency; - s->thread_info.inputs = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); - s->thread_info.soft_volume = s->volume; - s->thread_info.soft_muted = s->muted; + pa_cvolume_reset(&s->thread_info.soft_volume, s->sample_spec.channels); + s->thread_info.soft_muted = FALSE; s->thread_info.state = s->state; s->thread_info.rewind_nbytes = 0; s->thread_info.max_rewind = 0; + s->thread_info.max_request = 0; s->thread_info.requested_latency_valid = FALSE; s->thread_info.requested_latency = 0; + s->thread_info.min_latency = DEFAULT_MIN_LATENCY; + s->thread_info.max_latency = 0; pa_assert_se(pa_idxset_put(core->sinks, s, &s->index) >= 0); @@ -249,11 +249,14 @@ pa_sink* pa_sink_new( } s->monitor_source->monitor_of = s; + + pa_source_set_latency_range(s->monitor_source, s->thread_info.min_latency, s->thread_info.max_latency); pa_source_set_max_rewind(s->monitor_source, s->thread_info.max_rewind); return s; } +/* Called from main context */ static int sink_set_state(pa_sink *s, pa_sink_state_t state) { int ret; pa_bool_t suspend_change; @@ -272,8 +275,7 @@ static int sink_set_state(pa_sink *s, pa_sink_state_t state) { return -1; if (s->asyncmsgq) - if (pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_STATE, PA_UINT_TO_PTR(state), 0, NULL) < 0) - return -1; + pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_STATE, PA_UINT_TO_PTR(state), 0, NULL) == 0); s->state = state; @@ -294,18 +296,25 @@ static int sink_set_state(pa_sink *s, pa_sink_state_t state) { return 0; } +/* Called from main context */ void pa_sink_put(pa_sink* s) { pa_sink_assert_ref(s); pa_assert(s->state == PA_SINK_INIT); + + /* The following fields must be initialized properly when calling _put() */ pa_assert(s->asyncmsgq); pa_assert(s->rtpoll); + pa_assert(!s->thread_info.min_latency || !s->thread_info.max_latency || + s->thread_info.min_latency <= s->thread_info.max_latency); - pa_assert(!s->min_latency || !s->max_latency || s->min_latency <= s->max_latency); - - if (!(s->flags & PA_SINK_HW_VOLUME_CTRL)) + if (!(s->flags & PA_SINK_HW_VOLUME_CTRL)) { s->flags |= PA_SINK_DECIBEL_VOLUME; + s->thread_info.soft_volume = s->volume; + s->thread_info.soft_muted = s->muted; + } + pa_assert_se(sink_set_state(s, PA_SINK_IDLE) == 0); pa_source_put(s->monitor_source); @@ -314,6 +323,7 @@ void pa_sink_put(pa_sink* s) { pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_PUT], s); } +/* Called from main context */ void pa_sink_unlink(pa_sink* s) { pa_bool_t linked; pa_sink_input *i, *j = NULL; @@ -359,6 +369,7 @@ void pa_sink_unlink(pa_sink* s) { } } +/* Called from main context */ static void sink_free(pa_object *o) { pa_sink *s = PA_SINK(o); pa_sink_input *i; @@ -395,6 +406,7 @@ static void sink_free(pa_object *o) { pa_xfree(s); } +/* Called from main context */ void pa_sink_set_asyncmsgq(pa_sink *s, pa_asyncmsgq *q) { pa_sink_assert_ref(s); @@ -404,6 +416,7 @@ void pa_sink_set_asyncmsgq(pa_sink *s, pa_asyncmsgq *q) { pa_source_set_asyncmsgq(s->monitor_source, q); } +/* Called from main context */ void pa_sink_set_rtpoll(pa_sink *s, pa_rtpoll *p) { pa_sink_assert_ref(s); @@ -412,6 +425,7 @@ void pa_sink_set_rtpoll(pa_sink *s, pa_rtpoll *p) { pa_source_set_rtpoll(s->monitor_source, p); } +/* Called from main context */ int pa_sink_update_status(pa_sink*s) { pa_sink_assert_ref(s); pa_assert(PA_SINK_IS_LINKED(s->state)); @@ -422,6 +436,7 @@ int pa_sink_update_status(pa_sink*s) { return sink_set_state(s, pa_sink_used_by(s) ? PA_SINK_RUNNING : PA_SINK_IDLE); } +/* Called from main context */ int pa_sink_suspend(pa_sink *s, pa_bool_t suspend) { pa_sink_assert_ref(s); pa_assert(PA_SINK_IS_LINKED(s->state)); @@ -432,11 +447,12 @@ int pa_sink_suspend(pa_sink *s, pa_bool_t suspend) { return sink_set_state(s, pa_sink_used_by(s) ? PA_SINK_RUNNING : PA_SINK_IDLE); } +/* Called from IO thread context */ void pa_sink_process_rewind(pa_sink *s, size_t nbytes) { pa_sink_input *i; void *state = NULL; pa_sink_assert_ref(s); - pa_assert(PA_SINK_IS_LINKED(s->state)); + pa_assert(PA_SINK_IS_LINKED(s->thread_info.state)); /* Make sure the sink code already reset the counter! */ pa_assert(s->thread_info.rewind_nbytes <= 0); @@ -451,11 +467,11 @@ void pa_sink_process_rewind(pa_sink *s, size_t nbytes) { pa_sink_input_process_rewind(i, nbytes); } - if (s->monitor_source && PA_SOURCE_IS_OPENED(pa_source_get_state(s->monitor_source))) + if (s->monitor_source && PA_SOURCE_IS_OPENED(s->monitor_source->thread_info.state)) pa_source_process_rewind(s->monitor_source, nbytes); - } +/* Called from IO thread context */ static unsigned fill_mix_info(pa_sink *s, size_t *length, pa_mix_info *info, unsigned maxinfo) { pa_sink_input *i; unsigned n = 0; @@ -495,24 +511,26 @@ static unsigned fill_mix_info(pa_sink *s, size_t *length, pa_mix_info *info, uns return n; } -static void inputs_drop(pa_sink *s, pa_mix_info *info, unsigned n, size_t length) { +/* Called from IO thread context */ +static void inputs_drop(pa_sink *s, pa_mix_info *info, unsigned n, pa_memchunk *result) { pa_sink_input *i; void *state = NULL; unsigned p = 0; unsigned n_unreffed = 0; pa_sink_assert_ref(s); + pa_assert(result); + pa_assert(result->memblock); + pa_assert(result->length > 0); /* We optimize for the case where the order of the inputs has not changed */ while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL))) { unsigned j; - pa_mix_info* m; + pa_mix_info* m = NULL; pa_sink_input_assert_ref(i); - m = NULL; - /* Let's try to find the matching entry info the pa_mix_info array */ for (j = 0; j < n; j ++) { @@ -527,14 +545,47 @@ static void inputs_drop(pa_sink *s, pa_mix_info *info, unsigned n, size_t length } /* Drop read data */ - pa_sink_input_drop(i, length); + pa_sink_input_drop(i, result->length); + + if (s->monitor_source && PA_SOURCE_IS_OPENED(pa_source_get_state(s->monitor_source))) { + + if (pa_hashmap_size(i->thread_info.direct_outputs) > 0) { + void *ostate = NULL; + pa_source_output *o; + pa_memchunk c; + + if (m && m->chunk.memblock) { + c = m->chunk; + pa_memblock_ref(c.memblock); + pa_assert(result->length <= c.length); + c.length = result->length; + + pa_memchunk_make_writable(&c, 0); + pa_volume_memchunk(&c, &s->sample_spec, &m->volume); + } else { + c = s->silence; + pa_memblock_ref(c.memblock); + pa_assert(result->length <= c.length); + c.length = result->length; + } + + while ((o = pa_hashmap_iterate(i->thread_info.direct_outputs, &ostate, NULL))) { + pa_source_output_assert_ref(o); + pa_assert(o->direct_on_input == i); + pa_source_post_direct(s->monitor_source, o, &c); + } + + pa_memblock_unref(c.memblock); + } + } if (m) { - pa_sink_input_unref(m->userdata); - m->userdata = NULL; if (m->chunk.memblock) pa_memblock_unref(m->chunk.memblock); - pa_memchunk_reset(&m->chunk); + pa_memchunk_reset(&m->chunk); + + pa_sink_input_unref(m->userdata); + m->userdata = NULL; n_unreffed += 1; } @@ -551,8 +602,12 @@ static void inputs_drop(pa_sink *s, pa_mix_info *info, unsigned n, size_t length pa_memblock_unref(info->chunk.memblock); } } + + if (s->monitor_source && PA_SOURCE_IS_OPENED(pa_source_get_state(s->monitor_source))) + pa_source_post(s->monitor_source, result); } +/* Called from IO thread context */ void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) { pa_mix_info info[MAX_MIX_CHANNELS]; unsigned n; @@ -621,14 +676,12 @@ void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) { } if (s->thread_info.state == PA_SINK_RUNNING) - inputs_drop(s, info, n, result->length); - - if (s->monitor_source && PA_SOURCE_IS_OPENED(pa_source_get_state(s->monitor_source))) - pa_source_post(s->monitor_source, result); + inputs_drop(s, info, n, result); pa_sink_unref(s); } +/* Called from IO thread context */ void pa_sink_render_into(pa_sink*s, pa_memchunk *target) { pa_mix_info info[MAX_MIX_CHANNELS]; unsigned n; @@ -650,6 +703,8 @@ void pa_sink_render_into(pa_sink*s, pa_memchunk *target) { if (length > block_size_max) length = pa_frame_align(block_size_max, &s->sample_spec); + pa_assert(length > 0); + n = s->thread_info.state == PA_SINK_RUNNING ? fill_mix_info(s, &length, info, MAX_MIX_CHANNELS) : 0; if (n == 0) { @@ -673,8 +728,8 @@ void pa_sink_render_into(pa_sink*s, pa_memchunk *target) { vchunk = info[0].chunk; pa_memblock_ref(vchunk.memblock); - if (vchunk.length > target->length) - vchunk.length = target->length; + if (vchunk.length > length) + vchunk.length = length; if (!pa_cvolume_is_norm(&volume)) { pa_memchunk_make_writable(&vchunk, 0); @@ -700,14 +755,12 @@ void pa_sink_render_into(pa_sink*s, pa_memchunk *target) { } if (s->thread_info.state == PA_SINK_RUNNING) - inputs_drop(s, info, n, target->length); - - if (s->monitor_source && PA_SOURCE_IS_OPENED(pa_source_get_state(s->monitor_source))) - pa_source_post(s->monitor_source, target); + inputs_drop(s, info, n, target); pa_sink_unref(s); } +/* Called from IO thread context */ void pa_sink_render_into_full(pa_sink *s, pa_memchunk *target) { pa_memchunk chunk; size_t l, d; @@ -739,6 +792,7 @@ void pa_sink_render_into_full(pa_sink *s, pa_memchunk *target) { pa_sink_unref(s); } +/* Called from IO thread context */ void pa_sink_render_full(pa_sink *s, size_t length, pa_memchunk *result) { pa_sink_assert_ref(s); pa_assert(PA_SINK_IS_OPENED(s->thread_info.state)); @@ -757,6 +811,7 @@ void pa_sink_render_full(pa_sink *s, size_t length, pa_memchunk *result) { pa_sink_render_into_full(s, result); } +/* Called from main thread */ pa_usec_t pa_sink_get_latency(pa_sink *s) { pa_usec_t usec = 0; @@ -768,14 +823,14 @@ pa_usec_t pa_sink_get_latency(pa_sink *s) { if (!PA_SINK_IS_OPENED(s->state)) return 0; - if (pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0) - return 0; + pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) == 0); return usec; } +/* Called from main thread */ void pa_sink_set_volume(pa_sink *s, const pa_cvolume *volume) { - int changed; + pa_bool_t changed; pa_sink_assert_ref(s); pa_assert(PA_SINK_IS_LINKED(s->state)); @@ -788,34 +843,36 @@ void pa_sink_set_volume(pa_sink *s, const pa_cvolume *volume) { s->set_volume = NULL; if (!s->set_volume) - pa_asyncmsgq_post(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_VOLUME, pa_xnewdup(struct pa_cvolume, volume, 1), 0, NULL, pa_xfree); + pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_VOLUME, volume, 0, NULL); if (changed) pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); } +/* Called from main thread */ const pa_cvolume *pa_sink_get_volume(pa_sink *s) { - struct pa_cvolume old_volume; - pa_sink_assert_ref(s); pa_assert(PA_SINK_IS_LINKED(s->state)); - old_volume = s->volume; + if (s->refresh_volume) { + struct pa_cvolume old_volume = s->volume; - if (s->get_volume && s->get_volume(s) < 0) - s->get_volume = NULL; + if (s->get_volume && s->get_volume(s) < 0) + s->get_volume = NULL; - if (!s->get_volume && s->refresh_volume) - pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_VOLUME, &s->volume, 0, NULL); + if (!s->get_volume) + pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_VOLUME, &s->volume, 0, NULL); - if (!pa_cvolume_equal(&old_volume, &s->volume)) - pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); + if (!pa_cvolume_equal(&old_volume, &s->volume)) + pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); + } return &s->volume; } +/* Called from main thread */ void pa_sink_set_mute(pa_sink *s, pa_bool_t mute) { - int changed; + pa_bool_t changed; pa_sink_assert_ref(s); pa_assert(PA_SINK_IS_LINKED(s->state)); @@ -833,26 +890,29 @@ void pa_sink_set_mute(pa_sink *s, pa_bool_t mute) { pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); } +/* Called from main thread */ pa_bool_t pa_sink_get_mute(pa_sink *s) { - pa_bool_t old_muted; pa_sink_assert_ref(s); pa_assert(PA_SINK_IS_LINKED(s->state)); - old_muted = s->muted; + if (s->refresh_muted) { + pa_bool_t old_muted = s->muted; - if (s->get_mute && s->get_mute(s) < 0) - s->get_mute = NULL; + if (s->get_mute && s->get_mute(s) < 0) + s->get_mute = NULL; - if (!s->get_mute && s->refresh_mute) - pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_MUTE, &s->muted, 0, NULL); + if (!s->get_mute) + pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_MUTE, &s->muted, 0, NULL); - if (old_muted != s->muted) - pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); + if (old_muted != s->muted) + pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); + } return s->muted; } +/* Called from main thread */ void pa_sink_set_description(pa_sink *s, const char *description) { const char *old; pa_sink_assert_ref(s); @@ -884,6 +944,7 @@ void pa_sink_set_description(pa_sink *s, const char *description) { } } +/* Called from main thread */ unsigned pa_sink_linked_by(pa_sink *s) { unsigned ret; @@ -901,6 +962,7 @@ unsigned pa_sink_linked_by(pa_sink *s) { return ret; } +/* Called from main thread */ unsigned pa_sink_used_by(pa_sink *s) { unsigned ret; @@ -916,10 +978,10 @@ unsigned pa_sink_used_by(pa_sink *s) { return ret - s->n_corked; } +/* Called from IO thread, except when it is not */ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk) { pa_sink *s = PA_SINK(o); pa_sink_assert_ref(s); - pa_assert(s->thread_info.state != PA_SINK_UNLINKED); switch ((pa_sink_message_t) code) { @@ -957,6 +1019,7 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse pa_sink_input_set_state_within_thread(i, i->state); pa_sink_input_update_max_rewind(i, s->thread_info.max_rewind); + pa_sink_input_update_max_request(i, s->thread_info.max_request); pa_sink_invalidate_requested_latency(s); @@ -975,11 +1038,11 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse * sink input handling a few lines down at * PA_SINK_MESSAGE_PREPAPRE_MOVE, too. */ - pa_sink_input_set_state_within_thread(i, i->state); - if (i->detach) i->detach(i); + pa_sink_input_set_state_within_thread(i, i->state); + pa_assert(i->thread_info.attached); i->thread_info.attached = FALSE; @@ -1072,6 +1135,7 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse i->attach(i); pa_sink_input_update_max_rewind(i, s->thread_info.max_rewind); + pa_sink_input_update_max_request(i, s->thread_info.max_request); pa_sink_input_set_requested_latency_within_thread(i, i->thread_info.requested_sink_latency); @@ -1122,9 +1186,7 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse case PA_SINK_MESSAGE_DETACH: - /* We're detaching all our input streams so that the - * asyncmsgq and rtpoll fields can be changed without - * problems */ + /* Detach all streams */ pa_sink_detach_within_thread(s); return 0; @@ -1138,9 +1200,40 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse pa_usec_t *usec = userdata; *usec = pa_sink_get_requested_latency_within_thread(s); + + if (*usec == (pa_usec_t) -1) + *usec = s->thread_info.max_latency; + + return 0; + } + + case PA_SINK_MESSAGE_SET_LATENCY_RANGE: { + pa_usec_t *r = userdata; + + pa_sink_update_latency_range(s, r[0], r[1]); + + return 0; + } + + case PA_SINK_MESSAGE_GET_LATENCY_RANGE: { + pa_usec_t *r = userdata; + + r[0] = s->thread_info.min_latency; + r[1] = s->thread_info.max_latency; + return 0; } + case PA_SINK_MESSAGE_GET_MAX_REWIND: + + *((size_t*) userdata) = s->thread_info.max_rewind; + return 0; + + case PA_SINK_MESSAGE_GET_MAX_REQUEST: + + *((size_t*) userdata) = s->thread_info.max_request; + return 0; + case PA_SINK_MESSAGE_GET_LATENCY: case PA_SINK_MESSAGE_MAX: ; @@ -1149,6 +1242,7 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse return -1; } +/* Called from main thread */ int pa_sink_suspend_all(pa_core *c, pa_bool_t suspend) { pa_sink *sink; uint32_t idx; @@ -1162,20 +1256,23 @@ int pa_sink_suspend_all(pa_core *c, pa_bool_t suspend) { return ret; } +/* Called from main thread */ void pa_sink_detach(pa_sink *s) { pa_sink_assert_ref(s); pa_assert(PA_SINK_IS_LINKED(s->state)); - pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_DETACH, NULL, 0, NULL); + pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_DETACH, NULL, 0, NULL) == 0); } +/* Called from main thread */ void pa_sink_attach(pa_sink *s) { pa_sink_assert_ref(s); pa_assert(PA_SINK_IS_LINKED(s->state)); - pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_ATTACH, NULL, 0, NULL); + pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_ATTACH, NULL, 0, NULL) == 0); } +/* Called from IO thread */ void pa_sink_detach_within_thread(pa_sink *s) { pa_sink_input *i; void *state = NULL; @@ -1191,6 +1288,7 @@ void pa_sink_detach_within_thread(pa_sink *s) { pa_source_detach_within_thread(s->monitor_source); } +/* Called from IO thread */ void pa_sink_attach_within_thread(pa_sink *s) { pa_sink_input *i; void *state = NULL; @@ -1206,6 +1304,7 @@ void pa_sink_attach_within_thread(pa_sink *s) { pa_source_attach_within_thread(s->monitor_source); } +/* Called from IO thread */ void pa_sink_request_rewind(pa_sink*s, size_t nbytes) { pa_sink_assert_ref(s); pa_assert(PA_SINK_IS_LINKED(s->thread_info.state)); @@ -1224,10 +1323,12 @@ void pa_sink_request_rewind(pa_sink*s, size_t nbytes) { s->request_rewind(s); } +/* Called from IO thread */ pa_usec_t pa_sink_get_requested_latency_within_thread(pa_sink *s) { pa_usec_t result = (pa_usec_t) -1; pa_sink_input *i; void *state = NULL; + pa_usec_t monitor_latency; pa_sink_assert_ref(s); @@ -1240,12 +1341,18 @@ pa_usec_t pa_sink_get_requested_latency_within_thread(pa_sink *s) { (result == (pa_usec_t) -1 || result > i->thread_info.requested_sink_latency)) result = i->thread_info.requested_sink_latency; + monitor_latency = pa_source_get_requested_latency_within_thread(s->monitor_source); + + if (monitor_latency != (pa_usec_t) -1 && + (result == (pa_usec_t) -1 || result > monitor_latency)) + result = monitor_latency; + if (result != (pa_usec_t) -1) { - if (s->max_latency > 0 && result > s->max_latency) - result = s->max_latency; + if (s->thread_info.max_latency > 0 && result > s->thread_info.max_latency) + result = s->thread_info.max_latency; - if (s->min_latency > 0 && result < s->min_latency) - result = s->min_latency; + if (s->thread_info.min_latency > 0 && result < s->thread_info.min_latency) + result = s->thread_info.min_latency; } s->thread_info.requested_latency = result; @@ -1254,6 +1361,7 @@ pa_usec_t pa_sink_get_requested_latency_within_thread(pa_sink *s) { return result; } +/* Called from main thread */ pa_usec_t pa_sink_get_requested_latency(pa_sink *s) { pa_usec_t usec = 0; @@ -1263,15 +1371,11 @@ pa_usec_t pa_sink_get_requested_latency(pa_sink *s) { if (!PA_SINK_IS_OPENED(s->state)) return 0; - if (pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_REQUESTED_LATENCY, &usec, 0, NULL) < 0) - return 0; - - if (usec == (pa_usec_t) -1) - usec = s->max_latency; - + pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_REQUESTED_LATENCY, &usec, 0, NULL) == 0); return usec; } +/* Called from IO thread */ void pa_sink_set_max_rewind(pa_sink *s, size_t max_rewind) { pa_sink_input *i; void *state = NULL; @@ -1283,20 +1387,145 @@ void pa_sink_set_max_rewind(pa_sink *s, size_t max_rewind) { s->thread_info.max_rewind = max_rewind; - while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL))) - pa_sink_input_update_max_rewind(i, s->thread_info.max_rewind); + if (PA_SINK_IS_LINKED(s->thread_info.state)) { + while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL))) + pa_sink_input_update_max_rewind(i, s->thread_info.max_rewind); + } if (s->monitor_source) pa_source_set_max_rewind(s->monitor_source, s->thread_info.max_rewind); } +/* Called from IO thread */ +void pa_sink_set_max_request(pa_sink *s, size_t max_request) { + pa_sink_input *i; + void *state = NULL; + + pa_sink_assert_ref(s); + + if (max_request == s->thread_info.max_request) + return; + + s->thread_info.max_request = max_request; + + if (PA_SINK_IS_LINKED(s->thread_info.state)) { + while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL))) + pa_sink_input_update_max_request(i, s->thread_info.max_request); + } +} + +/* Called from IO thread */ void pa_sink_invalidate_requested_latency(pa_sink *s) { + pa_sink_input *i; + void *state = NULL; pa_sink_assert_ref(s); - pa_assert(PA_SINK_IS_LINKED(s->thread_info.state)); s->thread_info.requested_latency_valid = FALSE; if (s->update_requested_latency) s->update_requested_latency(s); + + while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL))) + if (i->update_sink_requested_latency) + i->update_sink_requested_latency(i); +} + +/* Called from main thread */ +void pa_sink_set_latency_range(pa_sink *s, pa_usec_t min_latency, pa_usec_t max_latency) { + pa_sink_assert_ref(s); + + /* min_latency == 0: no limit + * min_latency == (size_t) -1: default limit + * min_latency anything else: specified limit + * + * Similar for max_latency */ + + if (min_latency == (pa_usec_t) -1) + min_latency = DEFAULT_MIN_LATENCY; + + if (max_latency == (pa_usec_t) -1) + max_latency = min_latency; + + pa_assert(!min_latency || !max_latency || + min_latency <= max_latency); + + if (PA_SINK_IS_LINKED(s->state)) { + pa_usec_t r[2]; + + r[0] = min_latency; + r[1] = max_latency; + + pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_LATENCY_RANGE, r, 0, NULL) == 0); + } else { + s->thread_info.min_latency = min_latency; + s->thread_info.max_latency = max_latency; + + s->monitor_source->thread_info.min_latency = min_latency; + s->monitor_source->thread_info.max_latency = max_latency; + + s->thread_info.requested_latency_valid = s->monitor_source->thread_info.requested_latency_valid = FALSE; + } +} + +/* Called from main thread */ +void pa_sink_get_latency_range(pa_sink *s, pa_usec_t *min_latency, pa_usec_t *max_latency) { + pa_sink_assert_ref(s); + pa_assert(min_latency); + pa_assert(max_latency); + + if (PA_SINK_IS_LINKED(s->state)) { + pa_usec_t r[2] = { 0, 0 }; + + pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_LATENCY_RANGE, r, 0, NULL) == 0); + + *min_latency = r[0]; + *max_latency = r[1]; + } else { + *min_latency = s->thread_info.min_latency; + *max_latency = s->thread_info.max_latency; + } +} + +/* Called from IO thread */ +void pa_sink_update_latency_range(pa_sink *s, pa_usec_t min_latency, pa_usec_t max_latency) { + pa_sink_input *i; + void *state = NULL; + + pa_sink_assert_ref(s); + + s->thread_info.min_latency = min_latency; + s->thread_info.max_latency = max_latency; + + while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL))) + if (i->update_sink_latency_range) + i->update_sink_latency_range(i); + + pa_sink_invalidate_requested_latency(s); + + pa_source_update_latency_range(s->monitor_source, min_latency, max_latency); +} + +size_t pa_sink_get_max_rewind(pa_sink *s) { + size_t r; + pa_sink_assert_ref(s); + + if (!PA_SINK_IS_LINKED(s->state)) + return s->thread_info.max_rewind; + + pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_MAX_REWIND, &r, 0, NULL) == 0); + + return r; +} + +size_t pa_sink_get_max_request(pa_sink *s) { + size_t r; + pa_sink_assert_ref(s); + + if (!PA_SINK_IS_LINKED(s->state)) + return s->thread_info.max_request; + + pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_MAX_REQUEST, &r, 0, NULL) == 0); + + return r; } diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h index f297c8f1..b73944e8 100644 --- a/src/pulsecore/sink.h +++ b/src/pulsecore/sink.h @@ -1,8 +1,6 @@ #ifndef foopulsesinkhfoo #define foopulsesinkhfoo -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -82,17 +80,15 @@ struct pa_sink { pa_cvolume volume; pa_bool_t muted; - pa_bool_t refresh_volume; - pa_bool_t refresh_mute; + + pa_bool_t refresh_volume:1; + pa_bool_t refresh_muted:1; pa_asyncmsgq *asyncmsgq; pa_rtpoll *rtpoll; pa_memchunk silence; - pa_usec_t min_latency; /* we won't go below this latency */ - pa_usec_t max_latency; /* An upper limit for the latencies */ - /* Called when the main loop requests a state change. Called from * main loop context. If returns -1 the state change will be * inhibited */ @@ -100,8 +96,9 @@ struct pa_sink { /* Callled when the volume is queried. Called from main loop * context. If this is NULL a PA_SINK_MESSAGE_GET_VOLUME message - * will be sent to the IO thread instead. */ - int (*get_volume)(pa_sink *s); /* may be null */ + * will be sent to the IO thread instead. If refresh_volume is + * FALSE neither this function is called nor a message is sent. */ + int (*get_volume)(pa_sink *s); /* may be NULL */ /* Called when the volume shall be changed. Called from main loop * context. If this is NULL a PA_SINK_MESSAGE_SET_VOLUME message @@ -110,7 +107,8 @@ struct pa_sink { /* Called when the mute setting is queried. Called from main loop * context. If this is NULL a PA_SINK_MESSAGE_GET_MUTE message - * will be sent to the IO thread instead. */ + * will be sent to the IO thread instead. If refresh_mute is + * FALSE neither this function is called nor a message is sent.*/ int (*get_mute)(pa_sink *s); /* dito */ /* Called when the mute setting shall be changed. Called from main @@ -132,17 +130,24 @@ struct pa_sink { pa_sink_state_t state; pa_hashmap *inputs; pa_cvolume soft_volume; - pa_bool_t soft_muted; + pa_bool_t soft_muted:1; - pa_bool_t requested_latency_valid; + pa_bool_t requested_latency_valid:1; pa_usec_t requested_latency; - /* The number of bytes we need keep around to be able to satisfy - * every DMA buffer rewrite */ + /* The number of bytes streams need to keep around as history to + * be able to satisfy every DMA buffer rewrite */ size_t max_rewind; + /* The number of bytes streams need to keep around to satisfy + * every DMA write request */ + size_t max_request; + /* Maximum of what clients requested to rewind in this cycle */ size_t rewind_nbytes; + + pa_usec_t min_latency; /* we won't go below this latency */ + pa_usec_t max_latency; /* An upper limit for the latencies */ } thread_info; void *userdata; @@ -165,6 +170,10 @@ typedef enum pa_sink_message { PA_SINK_MESSAGE_FINISH_MOVE, PA_SINK_MESSAGE_ATTACH, PA_SINK_MESSAGE_DETACH, + PA_SINK_MESSAGE_SET_LATENCY_RANGE, + PA_SINK_MESSAGE_GET_LATENCY_RANGE, + PA_SINK_MESSAGE_GET_MAX_REWIND, + PA_SINK_MESSAGE_GET_MAX_REQUEST, PA_SINK_MESSAGE_MAX } pa_sink_message_t; @@ -209,6 +218,8 @@ void pa_sink_set_description(pa_sink *s, const char *description); void pa_sink_set_asyncmsgq(pa_sink *s, pa_asyncmsgq *q); void pa_sink_set_rtpoll(pa_sink *s, pa_rtpoll *p); +void pa_sink_set_latency_range(pa_sink *s, pa_usec_t min_latency, pa_usec_t max_latency); + void pa_sink_detach(pa_sink *s); void pa_sink_attach(pa_sink *s); @@ -217,6 +228,10 @@ void pa_sink_attach(pa_sink *s); /* The returned value is supposed to be in the time domain of the sound card! */ pa_usec_t pa_sink_get_latency(pa_sink *s); pa_usec_t pa_sink_get_requested_latency(pa_sink *s); +void pa_sink_get_latency_range(pa_sink *s, pa_usec_t *min_latency, pa_usec_t *max_latency); + +size_t pa_sink_get_max_rewind(pa_sink *s); +size_t pa_sink_get_max_request(pa_sink *s); int pa_sink_update_status(pa_sink*s); int pa_sink_suspend(pa_sink *s, pa_bool_t suspend); @@ -248,6 +263,9 @@ void pa_sink_detach_within_thread(pa_sink *s); pa_usec_t pa_sink_get_requested_latency_within_thread(pa_sink *s); void pa_sink_set_max_rewind(pa_sink *s, size_t max_rewind); +void pa_sink_set_max_request(pa_sink *s, size_t max_request); + +void pa_sink_update_latency_range(pa_sink *s, pa_usec_t min_latency, pa_usec_t max_latency); /* To be called exclusively by sink input drivers, from IO context */ diff --git a/src/pulsecore/sioman.c b/src/pulsecore/sioman.c index 8d4c6fa7..7e5b186c 100644 --- a/src/pulsecore/sioman.c +++ b/src/pulsecore/sioman.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/sioman.h b/src/pulsecore/sioman.h index 49fffb34..d0cacc9b 100644 --- a/src/pulsecore/sioman.h +++ b/src/pulsecore/sioman.h @@ -1,8 +1,6 @@ #ifndef foosiomanhfoo #define foosiomanhfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/socket-client.c b/src/pulsecore/socket-client.c index a99193b7..e69a63da 100644 --- a/src/pulsecore/socket-client.c +++ b/src/pulsecore/socket-client.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -61,6 +59,7 @@ #include <pulsecore/core-error.h> #include <pulsecore/socket-util.h> #include <pulsecore/core-util.h> +#include <pulsecore/socket-util.h> #include <pulsecore/log.h> #include <pulsecore/parseaddr.h> #include <pulsecore/macro.h> @@ -270,22 +269,7 @@ static int sockaddr_prepare(pa_socket_client *c, const struct sockaddr *sa, size pa_assert(sa); pa_assert(salen); - switch (sa->sa_family) { - case AF_UNIX: - c->local = TRUE; - break; - - case AF_INET: - c->local = ((const struct sockaddr_in*) sa)->sin_addr.s_addr == INADDR_LOOPBACK; - break; - - case AF_INET6: - c->local = memcmp(&((const struct sockaddr_in6*) sa)->sin6_addr, &in6addr_loopback, sizeof(struct in6_addr)) == 0; - break; - - default: - c->local = FALSE; - } + c->local = pa_socket_address_is_local(sa); if ((c->fd = socket(sa->sa_family, SOCK_STREAM, 0)) < 0) { pa_log("socket(): %s", pa_cstrerror(errno)); diff --git a/src/pulsecore/socket-client.h b/src/pulsecore/socket-client.h index 41e8c3bd..9ceeaddc 100644 --- a/src/pulsecore/socket-client.h +++ b/src/pulsecore/socket-client.h @@ -1,8 +1,6 @@ #ifndef foosocketclienthfoo #define foosocketclienthfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/socket-server.c b/src/pulsecore/socket-server.c index 162a1aac..9885a02b 100644 --- a/src/pulsecore/socket-server.c +++ b/src/pulsecore/socket-server.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -83,7 +81,7 @@ struct pa_socket_server { char *filename; char *tcpwrap_service; - void (*on_connection)(pa_socket_server*s, pa_iochannel *io, void *userdata); + pa_socket_server_on_connection_cb_t on_connection; void *userdata; pa_io_event *io_event; @@ -91,7 +89,7 @@ struct pa_socket_server { enum { SOCKET_SERVER_GENERIC, SOCKET_SERVER_IPV4, SOCKET_SERVER_UNIX, SOCKET_SERVER_IPV6 } type; }; -static void callback(pa_mainloop_api *mainloop, pa_io_event *e, int fd, PA_GCC_UNUSED pa_io_event_flags_t f, void *userdata) { +static void callback(pa_mainloop_api *mainloop, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata) { pa_socket_server *s = userdata; pa_iochannel *io; int nfd; @@ -195,9 +193,9 @@ pa_socket_server* pa_socket_server_new_unix(pa_mainloop_api *m, const char *file pa_make_fd_cloexec(fd); + memset(&sa, 0, sizeof(sa)); sa.sun_family = AF_UNIX; - strncpy(sa.sun_path, filename, sizeof(sa.sun_path)-1); - sa.sun_path[sizeof(sa.sun_path) - 1] = 0; + pa_strlcpy(sa.sun_path, filename, sizeof(sa.sun_path)); pa_make_socket_low_delay(fd); @@ -295,7 +293,7 @@ pa_socket_server* pa_socket_server_new_ipv6(pa_mainloop_api *m, const uint8_t ad pa_socket_server *ss; int fd = -1; struct sockaddr_in6 sa; - int on = 1; + int on; pa_assert(m); pa_assert(port > 0); @@ -308,11 +306,13 @@ pa_socket_server* pa_socket_server_new_ipv6(pa_mainloop_api *m, const uint8_t ad pa_make_fd_cloexec(fd); #ifdef IPV6_V6ONLY + on = 1; if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) < 0) pa_log("setsockopt(IPPROTO_IPV6, IPV6_V6ONLY): %s", pa_cstrerror(errno)); #endif #ifdef SO_REUSEADDR + on = 1; if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) pa_log("setsockopt(SOL_SOCKET, SO_REUSEADDR, 1): %s", pa_cstrerror(errno)); #endif @@ -426,7 +426,7 @@ void pa_socket_server_unref(pa_socket_server *s) { socket_server_free(s); } -void pa_socket_server_set_callback(pa_socket_server*s, void (*on_connection)(pa_socket_server*s, pa_iochannel *io, void *userdata), void *userdata) { +void pa_socket_server_set_callback(pa_socket_server*s, pa_socket_server_on_connection_cb_t on_connection, void *userdata) { pa_assert(s); pa_assert(PA_REFCNT_VALUE(s) >= 1); @@ -507,7 +507,6 @@ char *pa_socket_server_get_address(pa_socket_server *s, char *c, size_t l) { } pa_snprintf(c, l, "tcp:[%s]:%u", ip, (unsigned) ntohs(sa.sin_port)); - } return c; diff --git a/src/pulsecore/socket-server.h b/src/pulsecore/socket-server.h index 777599e5..1edfb432 100644 --- a/src/pulsecore/socket-server.h +++ b/src/pulsecore/socket-server.h @@ -1,8 +1,6 @@ #ifndef foosocketserverhfoo #define foosocketserverhfoo -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -47,7 +45,9 @@ pa_socket_server* pa_socket_server_new_ipv6_string(pa_mainloop_api *m, const cha void pa_socket_server_unref(pa_socket_server*s); pa_socket_server* pa_socket_server_ref(pa_socket_server *s); -void pa_socket_server_set_callback(pa_socket_server*s, void (*on_connection)(pa_socket_server*s, pa_iochannel *io, void *userdata), void *userdata); +typedef void (*pa_socket_server_on_connection_cb_t)(pa_socket_server*s, pa_iochannel *io, void *userdata); + +void pa_socket_server_set_callback(pa_socket_server*s, pa_socket_server_on_connection_cb_t connection_cb, void *userdata); char *pa_socket_server_get_address(pa_socket_server *s, char *c, size_t l); diff --git a/src/pulsecore/socket-util.c b/src/pulsecore/socket-util.c index 456accb8..f721f699 100644 --- a/src/pulsecore/socket-util.c +++ b/src/pulsecore/socket-util.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -129,8 +127,8 @@ void pa_socket_peer_to_string(int fd, char *c, size_t l) { return; #endif } - } + #ifndef OS_IS_WIN32 pa_snprintf(c, l, "Unknown network client"); return; @@ -284,3 +282,40 @@ int pa_unix_socket_remove_stale(const char *fn) { } #endif /* HAVE_SYS_UN_H */ + + +pa_bool_t pa_socket_address_is_local(const struct sockaddr *sa) { + pa_assert(sa); + + switch (sa->sa_family) { + case AF_UNIX: + return TRUE; + + case AF_INET: + return ((const struct sockaddr_in*) sa)->sin_addr.s_addr == INADDR_LOOPBACK; + + case AF_INET6: + return memcmp(&((const struct sockaddr_in6*) sa)->sin6_addr, &in6addr_loopback, sizeof(struct in6_addr)) == 0; + + default: + return FALSE; + } +} + +pa_bool_t pa_socket_is_local(int fd) { + + union { + struct sockaddr sa; + struct sockaddr_in in; + struct sockaddr_in6 in6; +#ifdef HAVE_SYS_UN_H + struct sockaddr_un un; +#endif + } sa; + socklen_t sa_len = sizeof(sa); + + if (getpeername(fd, &sa.sa, &sa_len) < 0) + return FALSE; + + return pa_socket_address_is_local(&sa.sa); +} diff --git a/src/pulsecore/socket-util.h b/src/pulsecore/socket-util.h index a0344c68..7a40285a 100644 --- a/src/pulsecore/socket-util.h +++ b/src/pulsecore/socket-util.h @@ -1,8 +1,6 @@ #ifndef foosocketutilhfoo #define foosocketutilhfoo -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -26,6 +24,9 @@ ***/ #include <sys/types.h> +#include <sys/socket.h> + +#include <pulsecore/macro.h> void pa_socket_peer_to_string(int fd, char *c, size_t l); @@ -39,4 +40,7 @@ int pa_socket_set_rcvbuf(int fd, size_t l); int pa_unix_socket_is_stale(const char *fn); int pa_unix_socket_remove_stale(const char *fn); +pa_bool_t pa_socket_address_is_local(const struct sockaddr *sa); +pa_bool_t pa_socket_is_local(int fd); + #endif diff --git a/src/pulsecore/sound-file-stream.c b/src/pulsecore/sound-file-stream.c index e209676f..8eedf830 100644 --- a/src/pulsecore/sound-file-stream.c +++ b/src/pulsecore/sound-file-stream.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -35,6 +33,7 @@ #include <sndfile.h> #include <pulse/xmalloc.h> +#include <pulse/util.h> #include <pulsecore/core-error.h> #include <pulsecore/sink-input.h> @@ -149,8 +148,6 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk if (!u->memblockq) return -1; - pa_log_debug("pop: %lu", (unsigned long) length); - for (;;) { pa_memchunk tchunk; size_t fs; @@ -158,6 +155,7 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk sf_count_t n; if (pa_memblockq_peek(u->memblockq, chunk) >= 0) { + chunk->length = PA_MIN(chunk->length, length); pa_memblockq_drop(u->memblockq, chunk->length); return 0; } @@ -194,11 +192,7 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk pa_memblock_unref(tchunk.memblock); } - pa_log_debug("peek fail"); - if (pa_sink_input_safe_to_remove(i)) { - pa_log_debug("completed to play"); - pa_memblockq_free(u->memblockq); u->memblockq = NULL; @@ -216,6 +210,8 @@ static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) { u = FILE_STREAM(i->userdata); file_stream_assert_ref(u); + pa_log("backwards %lu", (unsigned long) nbytes); + if (!u->memblockq) return; @@ -330,7 +326,7 @@ int pa_play_file( data.driver = __FILE__; pa_sink_input_new_data_set_sample_spec(&data, &ss); pa_sink_input_new_data_set_volume(&data, volume); - pa_proplist_sets(data.proplist, PA_PROP_MEDIA_NAME, fname); + pa_proplist_sets(data.proplist, PA_PROP_MEDIA_NAME, pa_path_get_filename(fname)); pa_proplist_sets(data.proplist, PA_PROP_MEDIA_FILENAME, fname); u->sink_input = pa_sink_input_new(sink->core, &data, 0); diff --git a/src/pulsecore/sound-file-stream.h b/src/pulsecore/sound-file-stream.h index 189e242d..4cc69146 100644 --- a/src/pulsecore/sound-file-stream.h +++ b/src/pulsecore/sound-file-stream.h @@ -1,8 +1,6 @@ #ifndef foosoundfilestreamhfoo #define foosoundfilestreamhfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/sound-file.c b/src/pulsecore/sound-file.c index 3e6f683d..3183ede6 100644 --- a/src/pulsecore/sound-file.c +++ b/src/pulsecore/sound-file.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -119,10 +117,7 @@ int pa_sound_file_load( } if (map) - if (!pa_channel_map_init_auto(map, ss->channels, PA_CHANNEL_MAP_DEFAULT)) { - pa_log("Unsupported channel map in file %s", fname); - goto finish; - } + pa_channel_map_init_extend(map, ss->channels, PA_CHANNEL_MAP_DEFAULT); if ((l = pa_frame_size(ss) * sfinfo.frames) > PA_SCACHE_ENTRY_SIZE_MAX) { pa_log("File too large"); diff --git a/src/pulsecore/sound-file.h b/src/pulsecore/sound-file.h index 46763bd8..e4d703d3 100644 --- a/src/pulsecore/sound-file.h +++ b/src/pulsecore/sound-file.h @@ -1,8 +1,6 @@ #ifndef soundfilehfoo #define soundfilehfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c index 5c36937a..3d1abe30 100644 --- a/src/pulsecore/source-output.c +++ b/src/pulsecore/source-output.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -76,12 +74,15 @@ void pa_source_output_new_data_done(pa_source_output_new_data *data) { pa_proplist_free(data->proplist); } +/* Called from main context */ static void reset_callbacks(pa_source_output *o) { pa_assert(o); o->push = NULL; o->process_rewind = NULL; o->update_max_rewind = NULL; + o->update_source_requested_latency = NULL; + o->update_source_latency_range = NULL; o->attach = NULL; o->detach = NULL; o->suspend = NULL; @@ -91,6 +92,7 @@ static void reset_callbacks(pa_source_output *o) { o->state_change = NULL; } +/* Called from main context */ pa_source_output* pa_source_output_new( pa_core *core, pa_source_output_new_data *data, @@ -109,11 +111,13 @@ pa_source_output* pa_source_output_new( pa_return_null_if_fail(!data->driver || pa_utf8_valid(data->driver)); if (!data->source) - data->source = pa_namereg_get(core, NULL, PA_NAMEREG_SOURCE, 1); + data->source = pa_namereg_get(core, NULL, PA_NAMEREG_SOURCE, TRUE); pa_return_null_if_fail(data->source); pa_return_null_if_fail(pa_source_get_state(data->source) != PA_SOURCE_UNLINKED); + pa_return_null_if_fail(!data->direct_on_input || data->direct_on_input->sink == data->source->monitor_of); + if (!data->sample_spec_is_set) data->sample_spec = data->source->sample_spec; @@ -192,6 +196,8 @@ pa_source_output* pa_source_output_new( o->sample_spec = data->sample_spec; o->channel_map = data->channel_map; + o->direct_on_input = data->direct_on_input; + reset_callbacks(o); o->userdata = NULL; @@ -200,6 +206,7 @@ pa_source_output* pa_source_output_new( o->thread_info.sample_spec = o->sample_spec; o->thread_info.resampler = resampler; o->thread_info.requested_source_latency = (pa_usec_t) -1; + o->thread_info.direct_on_input = o->direct_on_input; o->thread_info.delay_memblockq = pa_memblockq_new( 0, @@ -214,6 +221,9 @@ pa_source_output* pa_source_output_new( pa_assert_se(pa_idxset_put(core->source_outputs, o, &o->index) == 0); pa_assert_se(pa_idxset_put(o->source->outputs, pa_source_output_ref(o), NULL) == 0); + if (o->direct_on_input) + pa_assert_se(pa_idxset_put(o->direct_on_input->direct_outputs, o, NULL) == 0); + pa_log_info("Created output %u \"%s\" on %s with sample spec %s and channel map %s", o->index, pa_strnull(pa_proplist_gets(o->proplist, PA_PROP_MEDIA_NAME)), @@ -226,6 +236,7 @@ pa_source_output* pa_source_output_new( return o; } +/* Called from main context */ static void update_n_corked(pa_source_output *o, pa_source_output_state_t state) { pa_assert(o); @@ -237,14 +248,14 @@ static void update_n_corked(pa_source_output *o, pa_source_output_state_t state) pa_source_update_status(o->source); } +/* Called from main context */ static int source_output_set_state(pa_source_output *o, pa_source_output_state_t state) { pa_assert(o); if (o->state == state) return 0; - if (pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_SET_STATE, PA_UINT_TO_PTR(state), 0, NULL) < 0) - return -1; + pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_SET_STATE, PA_UINT_TO_PTR(state), 0, NULL) == 0); update_n_corked(o, state); o->state = state; @@ -255,6 +266,7 @@ static int source_output_set_state(pa_source_output *o, pa_source_output_state_t return 0; } +/* Called from main context */ void pa_source_output_unlink(pa_source_output*o) { pa_bool_t linked; pa_assert(o); @@ -269,6 +281,8 @@ void pa_source_output_unlink(pa_source_output*o) { if (linked) pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK], o); + if (o->direct_on_input) + pa_idxset_remove_by_data(o->direct_on_input->direct_outputs, o, NULL); pa_idxset_remove_by_data(o->source->core->source_outputs, o, NULL); if (pa_idxset_remove_by_data(o->source->outputs, o, NULL)) pa_source_output_unref(o); @@ -278,7 +292,7 @@ void pa_source_output_unlink(pa_source_output*o) { if (linked) if (o->source->asyncmsgq) - pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_REMOVE_OUTPUT, o, 0, NULL); + pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_REMOVE_OUTPUT, o, 0, NULL) == 0); reset_callbacks(o); @@ -291,6 +305,7 @@ void pa_source_output_unlink(pa_source_output*o) { pa_source_output_unref(o); } +/* Called from main context */ static void source_output_free(pa_object* mo) { pa_source_output *o = PA_SOURCE_OUTPUT(mo); @@ -317,45 +332,52 @@ static void source_output_free(pa_object* mo) { pa_xfree(o); } +/* Called from main context */ void pa_source_output_put(pa_source_output *o) { pa_source_output_state_t state; pa_source_output_assert_ref(o); pa_assert(o->state == PA_SOURCE_OUTPUT_INIT); + + /* The following fields must be initialized properly */ pa_assert(o->push); + pa_assert(o->kill); state = o->flags & PA_SOURCE_OUTPUT_START_CORKED ? PA_SOURCE_OUTPUT_CORKED : PA_SOURCE_OUTPUT_RUNNING; update_n_corked(o, state); o->state = state; - pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_ADD_OUTPUT, o, 0, NULL); + pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_ADD_OUTPUT, o, 0, NULL) == 0); pa_subscription_post(o->source->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_NEW, o->index); pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PUT], o); } +/* Called from main context */ void pa_source_output_kill(pa_source_output*o) { pa_source_output_assert_ref(o); pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state)); - if (o->kill) - o->kill(o); + o->kill(o); } -pa_usec_t pa_source_output_get_latency(pa_source_output *o) { - pa_usec_t r = 0; +/* Called from main context */ +pa_usec_t pa_source_output_get_latency(pa_source_output *o, pa_usec_t *source_latency) { + pa_usec_t r[2] = { 0, 0 }; pa_source_output_assert_ref(o); pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state)); - if (pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_GET_LATENCY, &r, 0, NULL) < 0) - r = 0; + pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_GET_LATENCY, r, 0, NULL) == 0); if (o->get_latency) - r += o->get_latency(o); + r[0] += o->get_latency(o); - return r; + if (source_latency) + *source_latency = r[1]; + + return r[0]; } /* Called from thread context */ @@ -368,10 +390,10 @@ void pa_source_output_push(pa_source_output *o, const pa_memchunk *chunk) { pa_assert(chunk); pa_assert(pa_frame_aligned(chunk->length, &o->source->sample_spec)); - if (!o->push || o->state == PA_SOURCE_OUTPUT_CORKED) + if (!o->push || o->thread_info.state == PA_SOURCE_OUTPUT_CORKED) return; - pa_assert(o->state == PA_SOURCE_OUTPUT_RUNNING); + pa_assert(o->thread_info.state == PA_SOURCE_OUTPUT_RUNNING); if (pa_memblockq_push(o->thread_info.delay_memblockq, chunk) < 0) { pa_log_debug("Delay queue overflow!"); @@ -409,7 +431,8 @@ void pa_source_output_push(pa_source_output *o, const pa_memchunk *chunk) { if (rchunk.length > 0) o->push(o, &rchunk); - pa_memblock_unref(rchunk.memblock); + if (rchunk.memblock) + pa_memblock_unref(rchunk.memblock); } pa_memblock_unref(qchunk.memblock); @@ -421,7 +444,7 @@ void pa_source_output_push(pa_source_output *o, const pa_memchunk *chunk) { void pa_source_output_process_rewind(pa_source_output *o, size_t nbytes /* in sink sample spec */) { pa_source_output_assert_ref(o); - pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state)); + pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->thread_info.state)); pa_assert(pa_frame_aligned(nbytes, &o->source->sample_spec)); if (nbytes <= 0) @@ -455,42 +478,44 @@ void pa_source_output_update_max_rewind(pa_source_output *o, size_t nbytes /* i o->update_max_rewind(o, o->thread_info.resampler ? pa_resampler_result(o->thread_info.resampler, nbytes) : nbytes); } +/* Called from thread context */ static pa_usec_t fixup_latency(pa_source *s, pa_usec_t usec) { pa_source_assert_ref(s); if (usec == (pa_usec_t) -1) return usec; - if (s->max_latency > 0 && usec > s->max_latency) - usec = s->max_latency; + if (s->thread_info.max_latency > 0 && usec > s->thread_info.max_latency) + usec = s->thread_info.max_latency; - if (s->min_latency > 0 && usec < s->min_latency) - usec = s->min_latency; + if (s->thread_info.min_latency > 0 && usec < s->thread_info.min_latency) + usec = s->thread_info.min_latency; return usec; } +/* Called from thread context */ pa_usec_t pa_source_output_set_requested_latency_within_thread(pa_source_output *o, pa_usec_t usec) { pa_source_output_assert_ref(o); usec = fixup_latency(o->source, usec); - o->thread_info.requested_source_latency = usec; pa_source_invalidate_requested_latency(o->source); return usec; } +/* Called from main context */ pa_usec_t pa_source_output_set_requested_latency(pa_source_output *o, pa_usec_t usec) { pa_source_output_assert_ref(o); - usec = fixup_latency(o->source, usec); - if (PA_SOURCE_OUTPUT_IS_LINKED(o->state)) - pa_asyncmsgq_post(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_SET_REQUESTED_LATENCY, NULL, (int64_t) usec, NULL, NULL); + pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_SET_REQUESTED_LATENCY, &usec, 0, NULL) == 0); else { /* If this sink input is not realized yet, we have to touch * the thread info data directly */ + + usec = fixup_latency(o->source, usec); o->thread_info.requested_source_latency = usec; o->source->thread_info.requested_latency_valid = FALSE; } @@ -498,13 +523,14 @@ pa_usec_t pa_source_output_set_requested_latency(pa_source_output *o, pa_usec_t return usec; } +/* Called from main context */ pa_usec_t pa_source_output_get_requested_latency(pa_source_output *o) { pa_usec_t usec = 0; pa_source_output_assert_ref(o); if (PA_SOURCE_OUTPUT_IS_LINKED(o->state)) - pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_GET_REQUESTED_LATENCY, &usec, 0, NULL); + pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_GET_REQUESTED_LATENCY, &usec, 0, NULL) == 0); else /* If this sink input is not realized yet, we have to touch * the thread info data directly */ @@ -513,6 +539,7 @@ pa_usec_t pa_source_output_get_requested_latency(pa_source_output *o) { return usec; } +/* Called from main context */ void pa_source_output_cork(pa_source_output *o, pa_bool_t b) { pa_source_output_assert_ref(o); pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state)); @@ -520,6 +547,7 @@ void pa_source_output_cork(pa_source_output *o, pa_bool_t b) { source_output_set_state(o, b ? PA_SOURCE_OUTPUT_CORKED : PA_SOURCE_OUTPUT_RUNNING); } +/* Called from main context */ int pa_source_output_set_rate(pa_source_output *o, uint32_t rate) { pa_source_output_assert_ref(o); pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state)); @@ -536,6 +564,7 @@ int pa_source_output_set_rate(pa_source_output *o, uint32_t rate) { return 0; } +/* Called from main context */ void pa_source_output_set_name(pa_source_output *o, const char *name) { const char *old; pa_source_output_assert_ref(o); @@ -559,12 +588,14 @@ void pa_source_output_set_name(pa_source_output *o, const char *name) { } } +/* Called from main context */ pa_resample_method_t pa_source_output_get_resample_method(pa_source_output *o) { pa_source_output_assert_ref(o); return o->resample_method; } +/* Called from main context */ int pa_source_output_move_to(pa_source_output *o, pa_source *dest) { pa_source *origin; pa_resampler *new_resampler; @@ -582,6 +613,9 @@ int pa_source_output_move_to(pa_source_output *o, pa_source *dest) { if (o->flags & PA_SOURCE_OUTPUT_DONT_MOVE) return -1; + if (o->direct_on_input) + return -1; + if (pa_idxset_size(dest->outputs) >= PA_MAX_OUTPUTS_PER_SOURCE) { pa_log_warn("Failed to move source output: too many outputs per source."); return -1; @@ -619,7 +653,7 @@ int pa_source_output_move_to(pa_source_output *o, pa_source *dest) { pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE], &hook_data); /* Okey, let's move it */ - pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_REMOVE_OUTPUT, o, 0, NULL); + pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_REMOVE_OUTPUT, o, 0, NULL) == 0); pa_idxset_remove_by_data(origin->outputs, o, NULL); pa_idxset_put(dest->outputs, o, NULL); @@ -652,7 +686,7 @@ int pa_source_output_move_to(pa_source_output *o, pa_source *dest) { pa_source_update_status(origin); pa_source_update_status(dest); - pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_ADD_OUTPUT, o, 0, NULL); + pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_ADD_OUTPUT, o, 0, NULL) == 0); if (o->moved) o->moved(o); @@ -667,6 +701,7 @@ int pa_source_output_move_to(pa_source_output *o, pa_source *dest) { return 0; } +/* Called from IO thread context */ void pa_source_output_set_state_within_thread(pa_source_output *o, pa_source_output_state_t state) { pa_source_output_assert_ref(o); @@ -679,19 +714,22 @@ void pa_source_output_set_state_within_thread(pa_source_output *o, pa_source_out o->thread_info.state = state; } -/* Called from thread context */ +/* Called from IO thread context, except when it is not */ int pa_source_output_process_msg(pa_msgobject *mo, int code, void *userdata, int64_t offset, pa_memchunk* chunk) { pa_source_output *o = PA_SOURCE_OUTPUT(mo); - pa_source_output_assert_ref(o); - pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->thread_info.state)); switch (code) { case PA_SOURCE_OUTPUT_MESSAGE_GET_LATENCY: { pa_usec_t *r = userdata; + pa_usec_t source_usec = 0; + + r[0] += pa_bytes_to_usec(pa_memblockq_get_length(o->thread_info.delay_memblockq), &o->source->sample_spec); + + if (o->source->parent.process_msg(PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_GET_LATENCY, &source_usec, 0, NULL) >= 0) + r[1] += source_usec; - *r += pa_bytes_to_usec(pa_memblockq_get_length(o->thread_info.delay_memblockq), &o->source->sample_spec); return 0; } @@ -706,10 +744,13 @@ int pa_source_output_process_msg(pa_msgobject *mo, int code, void *userdata, int pa_source_output_set_state_within_thread(o, PA_PTR_TO_UINT(userdata)); return 0; - case PA_SOURCE_OUTPUT_MESSAGE_SET_REQUESTED_LATENCY: + case PA_SOURCE_OUTPUT_MESSAGE_SET_REQUESTED_LATENCY: { + pa_usec_t *usec = userdata; + + *usec = pa_source_output_set_requested_latency_within_thread(o, *usec); - pa_source_output_set_requested_latency_within_thread(o, (pa_usec_t) offset); return 0; + } case PA_SINK_INPUT_MESSAGE_GET_REQUESTED_LATENCY: { pa_usec_t *r = userdata; diff --git a/src/pulsecore/source-output.h b/src/pulsecore/source-output.h index 2dadb5c4..61825b22 100644 --- a/src/pulsecore/source-output.h +++ b/src/pulsecore/source-output.h @@ -1,8 +1,6 @@ #ifndef foopulsesourceoutputhfoo #define foopulsesourceoutputhfoo -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -66,13 +64,17 @@ struct pa_source_output { pa_source_output_state_t state; pa_source_output_flags_t flags; - pa_proplist *proplist; char *driver; /* may be NULL */ + pa_proplist *proplist; + pa_module *module; /* may be NULL */ pa_client *client; /* may be NULL */ pa_source *source; + /* A source output can monitor just a single input of a sink, in which case we find it here */ + pa_sink_input *direct_on_input; /* may be NULL */ + pa_sample_spec sample_spec; pa_channel_map channel_map; @@ -80,16 +82,24 @@ struct pa_source_output { /* Pushes a new memchunk into the output. Called from IO thread * context. */ - void (*push)(pa_source_output *o, const pa_memchunk *chunk); + void (*push)(pa_source_output *o, const pa_memchunk *chunk); /* may NOT be NULL */ /* Only relevant for monitor sources right now: called when the - * recorded stream is rewound. Called from IO context*/ - void (*process_rewind)(pa_source_output *o, size_t nbytes); + * recorded stream is rewound. Called from IO context */ + void (*process_rewind)(pa_source_output *o, size_t nbytes); /* may be NULL */ /* Called whenever the maximum rewindable size of the source * changes. Called from IO thread context. */ void (*update_max_rewind) (pa_source_output *o, size_t nbytes); /* may be NULL */ + /* Called whenever the configured latency of the source + * changes. Called from IO context. */ + void (*update_source_requested_latency) (pa_source_output *o); /* may be NULL */ + + /* Called whenver the latency range of the source changes. Called + * from IO context. */ + void (*update_source_latency_range) (pa_source_output *o); /* may be NULL */ + /* If non-NULL this function is called when the output is first * connected to a source. Called from IO thread context */ void (*attach) (pa_source_output *o); /* may be NULL */ @@ -108,12 +118,12 @@ struct pa_source_output { /* Supposed to unlink and destroy this stream. Called from main * context. */ - void (*kill)(pa_source_output* o); /* may be NULL */ + void (*kill)(pa_source_output* o); /* may NOT be NULL */ /* Return the current latency (i.e. length of bufferd audio) of - this stream. Called from main context. If NULL a - PA_SOURCE_OUTPUT_MESSAGE_GET_LATENCY message is sent to the IO - thread instead. */ + this stream. Called from main context. This is added to what the + PA_SOURCE_OUTPUT_MESSAGE_GET_LATENCY message sent to the IO thread + returns */ pa_usec_t (*get_latency) (pa_source_output *o); /* may be NULL */ /* If non_NULL this function is called from thread context if the @@ -135,6 +145,8 @@ struct pa_source_output { /* The requested latency for the source */ pa_usec_t requested_source_latency; + + pa_sink_input *direct_on_input; /* may be NULL */ } thread_info; void *userdata; @@ -154,6 +166,7 @@ enum { typedef struct pa_source_output_new_data { pa_proplist *proplist; + pa_sink_input *direct_on_input; const char *driver; pa_module *module; @@ -202,7 +215,7 @@ int pa_source_output_set_rate(pa_source_output *o, uint32_t rate); /* External code may request disconnection with this funcion */ void pa_source_output_kill(pa_source_output*o); -pa_usec_t pa_source_output_get_latency(pa_source_output *i); +pa_usec_t pa_source_output_get_latency(pa_source_output *i, pa_usec_t *source_latency); pa_resample_method_t pa_source_output_get_resample_method(pa_source_output *o); diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c index c767abcf..8256a988 100644 --- a/src/pulsecore/source.c +++ b/src/pulsecore/source.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -99,6 +97,7 @@ void pa_source_new_data_done(pa_source_new_data *data) { pa_proplist_free(data->proplist); } +/* Called from main context */ static void reset_callbacks(pa_source *s) { pa_assert(s); @@ -110,6 +109,7 @@ static void reset_callbacks(pa_source *s) { s->update_requested_latency = NULL; } +/* Called from main context */ pa_source* pa_source_new( pa_core *core, pa_source_new_data *data, @@ -120,6 +120,8 @@ pa_source* pa_source_new( char st[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX]; pa_assert(core); + pa_assert(data); + pa_assert(data->name); s = pa_msgobject_new(pa_source); @@ -128,6 +130,8 @@ pa_source* pa_source_new( return NULL; } + pa_source_new_data_set_name(data, name); + if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SOURCE_NEW], data) < 0) { pa_xfree(s); pa_namereg_unregister(core, name); @@ -195,16 +199,15 @@ pa_source* pa_source_new( &s->sample_spec, 0); - s->min_latency = DEFAULT_MIN_LATENCY; - s->max_latency = s->min_latency; - s->thread_info.outputs = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); - s->thread_info.soft_volume = s->volume; - s->thread_info.soft_muted = s->muted; + pa_cvolume_reset(&s->thread_info.soft_volume, s->sample_spec.channels); + s->thread_info.soft_muted = FALSE; s->thread_info.state = s->state; s->thread_info.max_rewind = 0; s->thread_info.requested_latency_valid = FALSE; s->thread_info.requested_latency = 0; + s->thread_info.min_latency = DEFAULT_MIN_LATENCY; + s->thread_info.max_latency = 0; pa_assert_se(pa_idxset_put(core->sources, s, &s->index) >= 0); @@ -217,6 +220,7 @@ pa_source* pa_source_new( return s; } +/* Called from main context */ static int source_set_state(pa_source *s, pa_source_state_t state) { int ret; pa_bool_t suspend_change; @@ -235,8 +239,7 @@ static int source_set_state(pa_source *s, pa_source_state_t state) { return -1; if (s->asyncmsgq) - if (pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_STATE, PA_UINT_TO_PTR(state), 0, NULL) < 0) - return -1; + pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_STATE, PA_UINT_TO_PTR(state), 0, NULL) == 0); s->state = state; @@ -257,24 +260,32 @@ static int source_set_state(pa_source *s, pa_source_state_t state) { return 0; } +/* Called from main context */ void pa_source_put(pa_source *s) { pa_source_assert_ref(s); pa_assert(s->state == PA_SINK_INIT); + + /* The following fields must be initialized properly when calling _put() */ pa_assert(s->asyncmsgq); pa_assert(s->rtpoll); + pa_assert(!s->thread_info.min_latency || !s->thread_info.max_latency || + s->thread_info.min_latency <= s->thread_info.max_latency); - pa_assert(!s->min_latency || !s->max_latency || s->min_latency <= s->max_latency); - - if (!(s->flags & PA_SOURCE_HW_VOLUME_CTRL)) + if (!(s->flags & PA_SOURCE_HW_VOLUME_CTRL)) { s->flags |= PA_SOURCE_DECIBEL_VOLUME; + s->thread_info.soft_volume = s->volume; + s->thread_info.soft_muted = s->muted; + } + pa_assert_se(source_set_state(s, PA_SOURCE_IDLE) == 0); pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE | PA_SUBSCRIPTION_EVENT_NEW, s->index); pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_PUT], s); } +/* Called from main context */ void pa_source_unlink(pa_source *s) { pa_bool_t linked; pa_source_output *o, *j = NULL; @@ -312,6 +323,7 @@ void pa_source_unlink(pa_source *s) { } } +/* Called from main context */ static void source_free(pa_object *o) { pa_source_output *so; pa_source *s = PA_SOURCE(o); @@ -343,18 +355,21 @@ static void source_free(pa_object *o) { pa_xfree(s); } +/* Called from main context */ void pa_source_set_asyncmsgq(pa_source *s, pa_asyncmsgq *q) { pa_source_assert_ref(s); s->asyncmsgq = q; } +/* Called from main context */ void pa_source_set_rtpoll(pa_source *s, pa_rtpoll *p) { pa_source_assert_ref(s); s->rtpoll = p; } +/* Called from main context */ int pa_source_update_status(pa_source*s) { pa_source_assert_ref(s); pa_assert(PA_SOURCE_IS_LINKED(s->state)); @@ -365,6 +380,7 @@ int pa_source_update_status(pa_source*s) { return source_set_state(s, pa_source_used_by(s) ? PA_SOURCE_RUNNING : PA_SOURCE_IDLE); } +/* Called from main context */ int pa_source_suspend(pa_source *s, pa_bool_t suspend) { pa_source_assert_ref(s); pa_assert(PA_SOURCE_IS_LINKED(s->state)); @@ -375,6 +391,7 @@ int pa_source_suspend(pa_source *s, pa_bool_t suspend) { return source_set_state(s, pa_source_used_by(s) ? PA_SOURCE_RUNNING : PA_SOURCE_IDLE); } +/* Called from IO thread context */ void pa_source_process_rewind(pa_source *s, size_t nbytes) { pa_source_output *o; void *state = NULL; @@ -393,6 +410,7 @@ void pa_source_process_rewind(pa_source *s, size_t nbytes) { } } +/* Called from IO thread context */ void pa_source_post(pa_source*s, const pa_memchunk *chunk) { pa_source_output *o; void *state = NULL; @@ -417,7 +435,9 @@ void pa_source_post(pa_source*s, const pa_memchunk *chunk) { while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL))) { pa_source_output_assert_ref(o); - pa_source_output_push(o, &vchunk); + + if (!o->thread_info.direct_on_input) + pa_source_output_push(o, &vchunk); } pa_memblock_unref(vchunk.memblock); @@ -425,11 +445,43 @@ void pa_source_post(pa_source*s, const pa_memchunk *chunk) { while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL))) { pa_source_output_assert_ref(o); - pa_source_output_push(o, chunk); + + if (!o->thread_info.direct_on_input) + pa_source_output_push(o, chunk); } } } +/* Called from IO thread context */ +void pa_source_post_direct(pa_source*s, pa_source_output *o, const pa_memchunk *chunk) { + pa_source_assert_ref(s); + pa_assert(PA_SOURCE_IS_OPENED(s->thread_info.state)); + pa_source_output_assert_ref(o); + pa_assert(o->thread_info.direct_on_input); + pa_assert(chunk); + + if (s->thread_info.state != PA_SOURCE_RUNNING) + return; + + if (s->thread_info.soft_muted || !pa_cvolume_is_norm(&s->thread_info.soft_volume)) { + pa_memchunk vchunk = *chunk; + + pa_memblock_ref(vchunk.memblock); + pa_memchunk_make_writable(&vchunk, 0); + + if (s->thread_info.soft_muted || pa_cvolume_is_muted(&s->thread_info.soft_volume)) + pa_silence_memchunk(&vchunk, &s->sample_spec); + else + pa_volume_memchunk(&vchunk, &s->sample_spec, &s->thread_info.soft_volume); + + pa_source_output_push(o, &vchunk); + + pa_memblock_unref(vchunk.memblock); + } else + pa_source_output_push(o, chunk); +} + +/* Called from main thread */ pa_usec_t pa_source_get_latency(pa_source *s) { pa_usec_t usec; @@ -439,14 +491,14 @@ pa_usec_t pa_source_get_latency(pa_source *s) { if (!PA_SOURCE_IS_OPENED(s->state)) return 0; - if (pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0) - return 0; + pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_LATENCY, &usec, 0, NULL) == 0); return usec; } +/* Called from main thread */ void pa_source_set_volume(pa_source *s, const pa_cvolume *volume) { - int changed; + pa_bool_t changed; pa_source_assert_ref(s); pa_assert(PA_SOURCE_IS_LINKED(s->state)); @@ -459,34 +511,36 @@ void pa_source_set_volume(pa_source *s, const pa_cvolume *volume) { s->set_volume = NULL; if (!s->set_volume) - pa_asyncmsgq_post(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_VOLUME, pa_xnewdup(struct pa_cvolume, volume, 1), 0, NULL, pa_xfree); + pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_VOLUME, volume, 0, NULL); if (changed) pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); } +/* Called from main thread */ const pa_cvolume *pa_source_get_volume(pa_source *s) { - pa_cvolume old_volume; - pa_source_assert_ref(s); pa_assert(PA_SOURCE_IS_LINKED(s->state)); - old_volume = s->volume; + if (s->refresh_volume) { + pa_cvolume old_volume = s->volume; - if (s->get_volume && s->get_volume(s) < 0) - s->get_volume = NULL; + if (s->get_volume && s->get_volume(s) < 0) + s->get_volume = NULL; - if (!s->get_volume && s->refresh_volume) - pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_VOLUME, &s->volume, 0, NULL); + if (!s->get_volume) + pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_VOLUME, &s->volume, 0, NULL); - if (!pa_cvolume_equal(&old_volume, &s->volume)) - pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); + if (!pa_cvolume_equal(&old_volume, &s->volume)) + pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); + } return &s->volume; } +/* Called from main thread */ void pa_source_set_mute(pa_source *s, pa_bool_t mute) { - int changed; + pa_bool_t changed; pa_source_assert_ref(s); pa_assert(PA_SOURCE_IS_LINKED(s->state)); @@ -504,26 +558,29 @@ void pa_source_set_mute(pa_source *s, pa_bool_t mute) { pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); } +/* Called from main thread */ pa_bool_t pa_source_get_mute(pa_source *s) { - pa_bool_t old_muted; pa_source_assert_ref(s); pa_assert(PA_SOURCE_IS_LINKED(s->state)); - old_muted = s->muted; + if (s->refresh_muted) { + pa_bool_t old_muted = s->muted; - if (s->get_mute && s->get_mute(s) < 0) - s->get_mute = NULL; + if (s->get_mute && s->get_mute(s) < 0) + s->get_mute = NULL; - if (!s->get_mute && s->refresh_muted) - pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_MUTE, &s->muted, 0, NULL); + if (!s->get_mute) + pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_MUTE, &s->muted, 0, NULL); - if (old_muted != s->muted) - pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); + if (old_muted != s->muted) + pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); + } return s->muted; } +/* Called from main thread */ void pa_source_set_description(pa_source *s, const char *description) { const char *old; pa_source_assert_ref(s); @@ -547,6 +604,7 @@ void pa_source_set_description(pa_source *s, const char *description) { } } +/* Called from main thread */ unsigned pa_source_linked_by(pa_source *s) { pa_source_assert_ref(s); pa_assert(PA_SOURCE_IS_LINKED(s->state)); @@ -554,6 +612,7 @@ unsigned pa_source_linked_by(pa_source *s) { return pa_idxset_size(s->outputs); } +/* Called from main thread */ unsigned pa_source_used_by(pa_source *s) { unsigned ret; @@ -566,17 +625,23 @@ unsigned pa_source_used_by(pa_source *s) { return ret - s->n_corked; } +/* Called from IO thread, except when it is not */ int pa_source_process_msg(pa_msgobject *object, int code, void *userdata, int64_t offset, pa_memchunk *chunk) { pa_source *s = PA_SOURCE(object); pa_source_assert_ref(s); - pa_assert(s->thread_info.state != PA_SOURCE_UNLINKED); switch ((pa_source_message_t) code) { + case PA_SOURCE_MESSAGE_ADD_OUTPUT: { pa_source_output *o = PA_SOURCE_OUTPUT(userdata); pa_hashmap_put(s->thread_info.outputs, PA_UINT32_TO_PTR(o->index), pa_source_output_ref(o)); + if (o->direct_on_input) { + o->thread_info.direct_on_input = o->direct_on_input; + pa_hashmap_put(o->thread_info.direct_on_input->thread_info.direct_outputs, PA_UINT32_TO_PTR(o->index), o); + } + pa_assert(!o->thread_info.attached); o->thread_info.attached = TRUE; @@ -606,6 +671,11 @@ int pa_source_process_msg(pa_msgobject *object, int code, void *userdata, int64_ pa_assert(o->thread_info.attached); o->thread_info.attached = FALSE; + if (o->thread_info.direct_on_input) { + pa_hashmap_remove(o->thread_info.direct_on_input->thread_info.direct_outputs, PA_UINT32_TO_PTR(o->index)); + o->thread_info.direct_on_input = NULL; + } + if (pa_hashmap_remove(s->thread_info.outputs, PA_UINT32_TO_PTR(o->index))) pa_source_output_unref(o); @@ -636,9 +706,7 @@ int pa_source_process_msg(pa_msgobject *object, int code, void *userdata, int64_ case PA_SOURCE_MESSAGE_DETACH: - /* We're detaching all our output streams so that the - * asyncmsgq and rtpoll fields can be changed without - * problems */ + /* Detach all streams */ pa_source_detach_within_thread(s); return 0; @@ -652,10 +720,45 @@ int pa_source_process_msg(pa_msgobject *object, int code, void *userdata, int64_ pa_usec_t *usec = userdata; *usec = pa_source_get_requested_latency_within_thread(s); + + if (*usec == (pa_usec_t) -1) + *usec = s->thread_info.max_latency; + + return 0; + } + + case PA_SOURCE_MESSAGE_SET_LATENCY_RANGE: { + pa_usec_t *r = userdata; + + pa_source_update_latency_range(s, r[0], r[1]); + + return 0; + } + + case PA_SOURCE_MESSAGE_GET_LATENCY_RANGE: { + pa_usec_t *r = userdata; + + r[0] = s->thread_info.min_latency; + r[1] = s->thread_info.max_latency; + return 0; } + case PA_SOURCE_MESSAGE_GET_MAX_REWIND: + + *((size_t*) userdata) = s->thread_info.max_rewind; + return 0; + case PA_SOURCE_MESSAGE_GET_LATENCY: + + if (s->monitor_of) { + *((pa_usec_t*) userdata) = 0; + return 0; + } + + /* Implementors need to overwrite this implementation! */ + return -1; + case PA_SOURCE_MESSAGE_MAX: ; } @@ -663,6 +766,7 @@ int pa_source_process_msg(pa_msgobject *object, int code, void *userdata, int64_ return -1; } +/* Called from main thread */ int pa_source_suspend_all(pa_core *c, pa_bool_t suspend) { uint32_t idx; pa_source *source; @@ -676,20 +780,23 @@ int pa_source_suspend_all(pa_core *c, pa_bool_t suspend) { return ret; } +/* Called from main thread */ void pa_source_detach(pa_source *s) { pa_source_assert_ref(s); pa_assert(PA_SOURCE_IS_LINKED(s->state)); - pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_DETACH, NULL, 0, NULL); + pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_DETACH, NULL, 0, NULL) == 0); } +/* Called from main thread */ void pa_source_attach(pa_source *s) { pa_source_assert_ref(s); pa_assert(PA_SOURCE_IS_LINKED(s->state)); - pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_ATTACH, NULL, 0, NULL); + pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_ATTACH, NULL, 0, NULL) == 0); } +/* Called from IO thread */ void pa_source_detach_within_thread(pa_source *s) { pa_source_output *o; void *state = NULL; @@ -702,6 +809,7 @@ void pa_source_detach_within_thread(pa_source *s) { o->detach(o); } +/* Called from IO thread */ void pa_source_attach_within_thread(pa_source *s) { pa_source_output *o; void *state = NULL; @@ -714,6 +822,7 @@ void pa_source_attach_within_thread(pa_source *s) { o->attach(o); } +/* Called from IO thread */ pa_usec_t pa_source_get_requested_latency_within_thread(pa_source *s) { pa_usec_t result = (pa_usec_t) -1; pa_source_output *o; @@ -731,11 +840,11 @@ pa_usec_t pa_source_get_requested_latency_within_thread(pa_source *s) { result = o->thread_info.requested_source_latency; if (result != (pa_usec_t) -1) { - if (s->max_latency > 0 && result > s->max_latency) - result = s->max_latency; + if (s->thread_info.max_latency > 0 && result > s->thread_info.max_latency) + result = s->thread_info.max_latency; - if (s->min_latency > 0 && result < s->min_latency) - result = s->min_latency; + if (s->thread_info.min_latency > 0 && result < s->thread_info.min_latency) + result = s->thread_info.min_latency; } s->thread_info.requested_latency = result; @@ -744,6 +853,7 @@ pa_usec_t pa_source_get_requested_latency_within_thread(pa_source *s) { return result; } +/* Called from main thread */ pa_usec_t pa_source_get_requested_latency(pa_source *s) { pa_usec_t usec; @@ -753,15 +863,12 @@ pa_usec_t pa_source_get_requested_latency(pa_source *s) { if (!PA_SOURCE_IS_OPENED(s->state)) return 0; - if (pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_REQUESTED_LATENCY, &usec, 0, NULL) < 0) - return 0; - - if (usec == (pa_usec_t) -1) - usec = s->max_latency; + pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_REQUESTED_LATENCY, &usec, 0, NULL) == 0); return usec; } +/* Called from IO thread */ void pa_source_set_max_rewind(pa_source *s, size_t max_rewind) { pa_source_output *o; void *state = NULL; @@ -773,17 +880,106 @@ void pa_source_set_max_rewind(pa_source *s, size_t max_rewind) { s->thread_info.max_rewind = max_rewind; - while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL))) - pa_source_output_update_max_rewind(o, s->thread_info.max_rewind); + if (PA_SOURCE_IS_LINKED(s->thread_info.state)) { + while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL))) + pa_source_output_update_max_rewind(o, s->thread_info.max_rewind); + } } void pa_source_invalidate_requested_latency(pa_source *s) { + pa_source_output *o; + void *state = NULL; pa_source_assert_ref(s); - pa_assert(PA_SOURCE_IS_LINKED(s->thread_info.state)); s->thread_info.requested_latency_valid = FALSE; if (s->update_requested_latency) s->update_requested_latency(s); + + while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL))) + o->update_source_requested_latency(o); + + if (s->monitor_of) + pa_sink_invalidate_requested_latency(s->monitor_of); +} + +void pa_source_set_latency_range(pa_source *s, pa_usec_t min_latency, pa_usec_t max_latency) { + pa_source_assert_ref(s); + + /* min_latency == 0: no limit + * min_latency == (size_t) -1: default limit + * min_latency anything else: specified limit + * + * Similar for max_latency */ + + if (min_latency == (pa_usec_t) -1) + min_latency = DEFAULT_MIN_LATENCY; + + if (max_latency == (pa_usec_t) -1) + max_latency = min_latency; + + pa_assert(!min_latency || !max_latency || + min_latency <= max_latency); + + if (PA_SINK_IS_LINKED(s->state)) { + pa_usec_t r[2]; + + r[0] = min_latency; + r[1] = max_latency; + + pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_LATENCY_RANGE, r, 0, NULL) == 0); + } else { + s->thread_info.min_latency = min_latency; + s->thread_info.max_latency = max_latency; + + s->thread_info.requested_latency_valid = FALSE; + } +} + +void pa_source_get_latency_range(pa_source *s, pa_usec_t *min_latency, pa_usec_t *max_latency) { + pa_source_assert_ref(s); + pa_assert(min_latency); + pa_assert(max_latency); + + if (PA_SOURCE_IS_LINKED(s->state)) { + pa_usec_t r[2] = { 0, 0 }; + + pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_LATENCY_RANGE, r, 0, NULL) == 0); + + *min_latency = r[0]; + *max_latency = r[1]; + } else { + *min_latency = s->thread_info.min_latency; + *max_latency = s->thread_info.max_latency; + } +} + +/* Called from IO thread */ +void pa_source_update_latency_range(pa_source *s, pa_usec_t min_latency, pa_usec_t max_latency) { + pa_source_output *o; + void *state = NULL; + + pa_source_assert_ref(s); + + s->thread_info.min_latency = min_latency; + s->thread_info.max_latency = max_latency; + + while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL))) + if (o->update_source_latency_range) + o->update_source_latency_range(o); + + pa_source_invalidate_requested_latency(s); +} + +size_t pa_source_get_max_rewind(pa_source *s) { + size_t r; + pa_source_assert_ref(s); + + if (!PA_SOURCE_IS_LINKED(s->state)) + return s->thread_info.max_rewind; + + pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_MAX_REWIND, &r, 0, NULL) == 0); + + return r; } diff --git a/src/pulsecore/source.h b/src/pulsecore/source.h index f9c9cbf9..f4a17e8d 100644 --- a/src/pulsecore/source.h +++ b/src/pulsecore/source.h @@ -1,8 +1,6 @@ #ifndef foopulsesourcehfoo #define foopulsesourcehfoo -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -42,6 +40,7 @@ typedef struct pa_source pa_source; #include <pulsecore/asyncmsgq.h> #include <pulsecore/msgobject.h> #include <pulsecore/rtpoll.h> +#include <pulsecore/source-output.h> #define PA_MAX_OUTPUTS_PER_SOURCE 32 @@ -84,22 +83,44 @@ struct pa_source { pa_cvolume volume; pa_bool_t muted; - pa_bool_t refresh_volume; - pa_bool_t refresh_muted; + + pa_bool_t refresh_volume:1; + pa_bool_t refresh_muted:1; pa_asyncmsgq *asyncmsgq; pa_rtpoll *rtpoll; pa_memchunk silence; - pa_usec_t min_latency; /* we won't go below this latency setting */ - pa_usec_t max_latency; /* An upper limit for the latencies */ - + /* Called when the main loop requests a state change. Called from + * main loop context. If returns -1 the state change will be + * inhibited */ int (*set_state)(pa_source*source, pa_source_state_t state); /* may be NULL */ - int (*set_volume)(pa_source *s); /* dito */ + + /* Callled when the volume is queried. Called from main loop + * context. If this is NULL a PA_SOURCE_MESSAGE_GET_VOLUME message + * will be sent to the IO thread instead. If refresh_volume is + * FALSE neither this function is called nor a message is sent. */ int (*get_volume)(pa_source *s); /* dito */ - int (*set_mute)(pa_source *s); /* dito */ + + /* Called when the volume shall be changed. Called from main loop + * context. If this is NULL a PA_SOURCE_MESSAGE_SET_VOLUME message + * will be sent to the IO thread instead. */ + int (*set_volume)(pa_source *s); /* dito */ + + /* Called when the mute setting is queried. Called from main loop + * context. If this is NULL a PA_SOURCE_MESSAGE_GET_MUTE message + * will be sent to the IO thread instead. If refresh_mute is + * FALSE neither this function is called nor a message is sent.*/ int (*get_mute)(pa_source *s); /* dito */ + + /* Called when the mute setting shall be changed. Called from main + * loop context. If this is NULL a PA_SOURCE_MESSAGE_SET_MUTE + * message will be sent to the IO thread instead. */ + int (*set_mute)(pa_source *s); /* dito */ + + /* Called when a the requested latency is changed. Called from IO + * thread context. */ void (*update_requested_latency)(pa_source *s); /* dito */ /* Contains copies of the above data so that the real-time worker @@ -108,14 +129,17 @@ struct pa_source { pa_source_state_t state; pa_hashmap *outputs; pa_cvolume soft_volume; - pa_bool_t soft_muted; + pa_bool_t soft_muted:1; - pa_bool_t requested_latency_valid; - size_t requested_latency; + pa_bool_t requested_latency_valid:1; + pa_usec_t requested_latency; /* Then number of bytes this source will be rewound for at - * max */ + * max. (Only used on monitor sources) */ size_t max_rewind; + + pa_usec_t min_latency; /* we won't go below this latency */ + pa_usec_t max_latency; /* An upper limit for the latencies */ } thread_info; void *userdata; @@ -136,6 +160,9 @@ typedef enum pa_source_message { PA_SOURCE_MESSAGE_SET_STATE, PA_SOURCE_MESSAGE_ATTACH, PA_SOURCE_MESSAGE_DETACH, + PA_SOURCE_MESSAGE_SET_LATENCY_RANGE, + PA_SOURCE_MESSAGE_GET_LATENCY_RANGE, + PA_SOURCE_MESSAGE_GET_MAX_REWIND, PA_SOURCE_MESSAGE_MAX } pa_source_message_t; @@ -180,13 +207,19 @@ void pa_source_set_description(pa_source *s, const char *description); void pa_source_set_asyncmsgq(pa_source *s, pa_asyncmsgq *q); void pa_source_set_rtpoll(pa_source *s, pa_rtpoll *p); +void pa_source_set_latency_range(pa_source *s, pa_usec_t min_latency, pa_usec_t max_latency); + void pa_source_detach(pa_source *s); void pa_source_attach(pa_source *s); /* May be called by everyone, from main context */ +/* The returned value is supposed to be in the time domain of the sound card! */ pa_usec_t pa_source_get_latency(pa_source *s); pa_usec_t pa_source_get_requested_latency(pa_source *s); +void pa_source_get_latency_range(pa_source *s, pa_usec_t *min_latency, pa_usec_t *max_latency); + +size_t pa_source_get_max_rewind(pa_source *s); int pa_source_update_status(pa_source*s); int pa_source_suspend(pa_source *s, pa_bool_t suspend); @@ -203,7 +236,8 @@ unsigned pa_source_used_by(pa_source *s); /* Number of connected streams that ar /* To be called exclusively by the source driver, from IO context */ -void pa_source_post(pa_source*s, const pa_memchunk *b); +void pa_source_post(pa_source*s, const pa_memchunk *chunk); +void pa_source_post_direct(pa_source*s, pa_source_output *o, const pa_memchunk *chunk); void pa_source_process_rewind(pa_source *s, size_t nbytes); int pa_source_process_msg(pa_msgobject *o, int code, void *userdata, int64_t, pa_memchunk *chunk); @@ -214,6 +248,7 @@ void pa_source_detach_within_thread(pa_source *s); pa_usec_t pa_source_get_requested_latency_within_thread(pa_source *s); void pa_source_set_max_rewind(pa_source *s, size_t max_rewind); +void pa_source_update_latency_range(pa_source *s, pa_usec_t min_latency, pa_usec_t max_latency); /* To be called exclusively by source output drivers, from IO context */ diff --git a/src/pulsecore/speexwrap.h b/src/pulsecore/speexwrap.h index df73edf0..617e4afb 100644 --- a/src/pulsecore/speexwrap.h +++ b/src/pulsecore/speexwrap.h @@ -1,8 +1,6 @@ #ifndef foopulsespeexwraphfoo #define foopulsespeexwraphfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/start-child.c b/src/pulsecore/start-child.c index e01011d6..1661383d 100644 --- a/src/pulsecore/start-child.c +++ b/src/pulsecore/start-child.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/start-child.h b/src/pulsecore/start-child.h index 359b5044..0b5ff660 100644 --- a/src/pulsecore/start-child.h +++ b/src/pulsecore/start-child.h @@ -1,8 +1,6 @@ #ifndef foopulsestartchildhfoo #define foopulsestartchildhfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/strbuf.c b/src/pulsecore/strbuf.c index 7c576c67..b59b6f49 100644 --- a/src/pulsecore/strbuf.c +++ b/src/pulsecore/strbuf.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/strbuf.h b/src/pulsecore/strbuf.h index d3555a2c..24c876d5 100644 --- a/src/pulsecore/strbuf.h +++ b/src/pulsecore/strbuf.h @@ -1,8 +1,6 @@ #ifndef foostrbufhfoo #define foostrbufhfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/strlist.c b/src/pulsecore/strlist.c index ac83f6b1..f587a2f8 100644 --- a/src/pulsecore/strlist.c +++ b/src/pulsecore/strlist.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/strlist.h b/src/pulsecore/strlist.h index 6e6e2d4a..1cb7537a 100644 --- a/src/pulsecore/strlist.h +++ b/src/pulsecore/strlist.h @@ -1,8 +1,6 @@ #ifndef foostrlisthfoo #define foostrlisthfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/tagstruct.c b/src/pulsecore/tagstruct.c index 7616cd16..b0ed59ef 100644 --- a/src/pulsecore/tagstruct.c +++ b/src/pulsecore/tagstruct.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/tagstruct.h b/src/pulsecore/tagstruct.h index 8699e6c8..e7d07054 100644 --- a/src/pulsecore/tagstruct.h +++ b/src/pulsecore/tagstruct.h @@ -1,8 +1,6 @@ #ifndef footagstructhfoo #define footagstructhfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/thread-mq.c b/src/pulsecore/thread-mq.c index 7e39c577..34f92a7e 100644 --- a/src/pulsecore/thread-mq.c +++ b/src/pulsecore/thread-mq.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/thread-mq.h b/src/pulsecore/thread-mq.h index 0ae49f8c..3b5e0e78 100644 --- a/src/pulsecore/thread-mq.h +++ b/src/pulsecore/thread-mq.h @@ -1,8 +1,6 @@ #ifndef foopulsethreadmqhfoo #define foopulsethreadmqhfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/thread-posix.c b/src/pulsecore/thread-posix.c index 7f43f43e..20ed16d9 100644 --- a/src/pulsecore/thread-posix.c +++ b/src/pulsecore/thread-posix.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/thread-win32.c b/src/pulsecore/thread-win32.c index cad1420a..c40d3342 100644 --- a/src/pulsecore/thread-win32.c +++ b/src/pulsecore/thread-win32.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/thread.h b/src/pulsecore/thread.h index 54ef320e..f3aca13e 100644 --- a/src/pulsecore/thread.h +++ b/src/pulsecore/thread.h @@ -1,8 +1,6 @@ #ifndef foopulsethreadhfoo #define foopulsethreadhfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/time-smoother.c b/src/pulsecore/time-smoother.c index 9b4be29f..fe5a4f18 100644 --- a/src/pulsecore/time-smoother.c +++ b/src/pulsecore/time-smoother.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -451,3 +449,11 @@ pa_usec_t pa_smoother_translate(pa_smoother *s, pa_usec_t x, pa_usec_t y_delay) return (pa_usec_t) ((double) y_delay / nde); } + +void pa_smoother_reset(pa_smoother *s) { + pa_assert(s); + + s->n_history = 0; + s->abc_valid = FALSE; + +} diff --git a/src/pulsecore/time-smoother.h b/src/pulsecore/time-smoother.h index b301b48c..2051e640 100644 --- a/src/pulsecore/time-smoother.h +++ b/src/pulsecore/time-smoother.h @@ -1,8 +1,6 @@ #ifndef foopulsetimesmootherhfoo #define foopulsetimesmootherhfoo -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -46,4 +44,6 @@ void pa_smoother_set_time_offset(pa_smoother *s, pa_usec_t x_offset); void pa_smoother_pause(pa_smoother *s, pa_usec_t x); void pa_smoother_resume(pa_smoother *s, pa_usec_t x); +void pa_smoother_reset(pa_smoother *s); + #endif diff --git a/src/pulsecore/tokenizer.c b/src/pulsecore/tokenizer.c index cf5da648..d1e0836b 100644 --- a/src/pulsecore/tokenizer.c +++ b/src/pulsecore/tokenizer.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/tokenizer.h b/src/pulsecore/tokenizer.h index 68a8db49..d51cd73e 100644 --- a/src/pulsecore/tokenizer.h +++ b/src/pulsecore/tokenizer.h @@ -1,8 +1,6 @@ #ifndef footokenizerhfoo #define footokenizerhfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/x11prop.c b/src/pulsecore/x11prop.c index a740e39b..9e75f63a 100644 --- a/src/pulsecore/x11prop.c +++ b/src/pulsecore/x11prop.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/x11prop.h b/src/pulsecore/x11prop.h index 388c5a34..c5998d3e 100644 --- a/src/pulsecore/x11prop.h +++ b/src/pulsecore/x11prop.h @@ -1,8 +1,6 @@ #ifndef foox11prophfoo #define foox11prophfoo -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/pulsecore/x11wrap.c b/src/pulsecore/x11wrap.c index 800a9458..00b6a157 100644 --- a/src/pulsecore/x11wrap.c +++ b/src/pulsecore/x11wrap.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -63,7 +61,8 @@ struct pa_x11_wrapper { struct pa_x11_client { PA_LLIST_FIELDS(pa_x11_client); pa_x11_wrapper *wrapper; - int (*callback)(pa_x11_wrapper *w, XEvent *e, void *userdata); + pa_x11_event_cb_t event_cb; + pa_x11_kill_cb_t kill_cb; void *userdata; }; @@ -72,17 +71,23 @@ static void work(pa_x11_wrapper *w) { pa_assert(w); pa_assert(PA_REFCNT_VALUE(w) >= 1); + pa_x11_wrapper_ref(w); + while (XPending(w->display)) { - pa_x11_client *c; + pa_x11_client *c, *n; XEvent e; XNextEvent(w->display, &e); - for (c = w->clients; c; c = c->next) { - pa_assert(c->callback); - if (c->callback(w, &e, c->userdata) != 0) - break; + for (c = w->clients; c; c = n) { + n = c->next; + + if (c->event_cb) + if (c->event_cb(w, &e, c->userdata) != 0) + break; } } + + pa_x11_wrapper_unref(w); } /* IO notification event for the X11 display connection */ @@ -251,7 +256,24 @@ Display *pa_x11_wrapper_get_display(pa_x11_wrapper *w) { return w->display; } -pa_x11_client* pa_x11_client_new(pa_x11_wrapper *w, int (*cb)(pa_x11_wrapper *w, XEvent *e, void *userdata), void *userdata) { +void pa_x11_wrapper_kill(pa_x11_wrapper *w) { + pa_x11_client *c, *n; + + pa_assert(w); + + pa_x11_wrapper_ref(w); + + for (c = w->clients; c; c = n) { + n = c->next; + + if (c->kill_cb) + c->kill_cb(w, c->userdata); + } + + pa_x11_wrapper_unref(w); +} + +pa_x11_client* pa_x11_client_new(pa_x11_wrapper *w, pa_x11_event_cb_t event_cb, pa_x11_kill_cb_t kill_cb, void *userdata) { pa_x11_client *c; pa_assert(w); @@ -259,7 +281,8 @@ pa_x11_client* pa_x11_client_new(pa_x11_wrapper *w, int (*cb)(pa_x11_wrapper *w, c = pa_xnew(pa_x11_client, 1); c->wrapper = w; - c->callback = cb; + c->event_cb = event_cb; + c->kill_cb = kill_cb; c->userdata = userdata; PA_LLIST_PREPEND(pa_x11_client, w->clients, c); diff --git a/src/pulsecore/x11wrap.h b/src/pulsecore/x11wrap.h index 9bed2fce..badc3a1f 100644 --- a/src/pulsecore/x11wrap.h +++ b/src/pulsecore/x11wrap.h @@ -1,8 +1,6 @@ #ifndef foox11wraphfoo #define foox11wraphfoo -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -30,6 +28,11 @@ typedef struct pa_x11_wrapper pa_x11_wrapper; +typedef struct pa_x11_client pa_x11_client; + +typedef int (*pa_x11_event_cb_t)(pa_x11_wrapper *w, XEvent *e, void *userdata); +typedef void (*pa_x11_kill_cb_t)(pa_x11_wrapper *w, void *userdata); + /* Return the X11 wrapper for this core. In case no wrapper was existant before, allocate a new one */ pa_x11_wrapper* pa_x11_wrapper_get(pa_core *c, const char *name); @@ -43,10 +46,11 @@ void pa_x11_wrapper_unref(pa_x11_wrapper* w); /* Return the X11 display object for this connection */ Display *pa_x11_wrapper_get_display(pa_x11_wrapper *w); -typedef struct pa_x11_client pa_x11_client; +/* Kill the connection to the X11 display */ +void pa_x11_wrapper_kill(pa_x11_wrapper *w); /* Register an X11 client, that is called for each X11 event */ -pa_x11_client* pa_x11_client_new(pa_x11_wrapper *w, int (*cb)(pa_x11_wrapper *w, XEvent *e, void *userdata), void *userdata); +pa_x11_client* pa_x11_client_new(pa_x11_wrapper *w, pa_x11_event_cb_t event_cb, pa_x11_kill_cb_t kill_cb, void *userdata); /* Free an X11 client object */ void pa_x11_client_free(pa_x11_client *c); diff --git a/src/tests/asyncmsgq-test.c b/src/tests/asyncmsgq-test.c index 380c5e7f..08ad3dd4 100644 --- a/src/tests/asyncmsgq-test.c +++ b/src/tests/asyncmsgq-test.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/tests/asyncq-test.c b/src/tests/asyncq-test.c index 0e10ed89..4e8a1207 100644 --- a/src/tests/asyncq-test.c +++ b/src/tests/asyncq-test.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/tests/channelmap-test.c b/src/tests/channelmap-test.c index d26d2cff..9c234602 100644 --- a/src/tests/channelmap-test.c +++ b/src/tests/channelmap-test.c @@ -1,5 +1,3 @@ -/* $Id$ */ - #include <stdio.h> #include <assert.h> @@ -22,12 +20,15 @@ int main(PA_GCC_UNUSED int argc, PA_GCC_UNUSED char *argv[]) { fprintf(stderr, "map: <%s>\n", pa_channel_map_snprint(cm, sizeof(cm), &map)); + pa_channel_map_init_extend(&map, 14, PA_CHANNEL_MAP_ALSA); + + fprintf(stderr, "map: <%s>\n", pa_channel_map_snprint(cm, sizeof(cm), &map)); + pa_channel_map_parse(&map2, cm); assert(pa_channel_map_equal(&map, &map2)); pa_channel_map_parse(&map2, "left,test"); - return 0; } diff --git a/src/tests/close-test.c b/src/tests/close-test.c new file mode 100644 index 00000000..7a6fec57 --- /dev/null +++ b/src/tests/close-test.c @@ -0,0 +1,20 @@ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <unistd.h> +#include <fcntl.h> + +#include <pulsecore/core-util.h> + +int main(int argc, char *argv[]) { + + open("/dev/null", O_RDONLY); + open("/dev/null", O_RDONLY); + open("/dev/null", O_RDONLY); + open("/dev/null", O_RDONLY); + + pa_close_all(5, -1); + + return 0; +} diff --git a/src/tests/cpulimit-test.c b/src/tests/cpulimit-test.c index 4563c0f6..b7145e8a 100644 --- a/src/tests/cpulimit-test.c +++ b/src/tests/cpulimit-test.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/tests/envelope-test.c b/src/tests/envelope-test.c index 240747d7..9f914553 100644 --- a/src/tests/envelope-test.c +++ b/src/tests/envelope-test.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/tests/flist-test.c b/src/tests/flist-test.c index 7e54454e..b2c648da 100644 --- a/src/tests/flist-test.c +++ b/src/tests/flist-test.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/tests/get-binary-name-test.c b/src/tests/get-binary-name-test.c index 29ebbe22..7c7a8996 100644 --- a/src/tests/get-binary-name-test.c +++ b/src/tests/get-binary-name-test.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/tests/hook-list-test.c b/src/tests/hook-list-test.c index 8628f521..60b965cd 100644 --- a/src/tests/hook-list-test.c +++ b/src/tests/hook-list-test.c @@ -1,5 +1,3 @@ -/* $Id$ */ - #ifdef HAVE_CONFIG_H #include <config.h> #endif @@ -7,12 +5,12 @@ #include <pulsecore/hook-list.h> #include <pulsecore/log.h> -static pa_hook_result_t func1(const char*hook_data, const char*call_data, const char*slot_data) { +static pa_hook_result_t func1(const char *hook_data, const char *call_data, const char *slot_data) { pa_log("(func1) hook=%s call=%s slot=%s", hook_data, call_data, slot_data); return PA_HOOK_OK; } -static pa_hook_result_t func2(const char*hook_data, const char*call_data, const char*slot_data) { +static pa_hook_result_t func2(const char *hook_data, const char *call_data, const char *slot_data) { pa_log("(func2) hook=%s call=%s slot=%s", hook_data, call_data, slot_data); return PA_HOOK_OK; } @@ -23,9 +21,9 @@ int main(int argc, char *argv[]) { pa_hook_init(&hook, (void*) "hook"); - pa_hook_connect(&hook, (pa_hook_cb_t) func1, (void*) "slot1"); - slot = pa_hook_connect(&hook, (pa_hook_cb_t) func2, (void*) "slot2"); - pa_hook_connect(&hook, (pa_hook_cb_t) func1, (void*) "slot3"); + pa_hook_connect(&hook, PA_HOOK_LATE, (pa_hook_cb_t) func1, (void*) "slot1"); + slot = pa_hook_connect(&hook, PA_HOOK_NORMAL, (pa_hook_cb_t) func2, (void*) "slot2"); + pa_hook_connect(&hook, PA_HOOK_NORMAL, (pa_hook_cb_t) func1, (void*) "slot3"); pa_hook_fire(&hook, (void*) "call1"); diff --git a/src/tests/interpol-test.c b/src/tests/interpol-test.c index f894d2f3..9d930774 100644 --- a/src/tests/interpol-test.c +++ b/src/tests/interpol-test.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/tests/ipacl-test.c b/src/tests/ipacl-test.c index d1bcb3e3..bcdd469a 100644 --- a/src/tests/ipacl-test.c +++ b/src/tests/ipacl-test.c @@ -1,5 +1,3 @@ -/* $Id$ */ - #ifdef HAVE_CONFIG_H #include <config.h> #endif diff --git a/src/tests/mainloop-test.c b/src/tests/mainloop-test.c index 79a4aaa0..9fa2e466 100644 --- a/src/tests/mainloop-test.c +++ b/src/tests/mainloop-test.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/tests/mcalign-test.c b/src/tests/mcalign-test.c index 79dd5797..9e358359 100644 --- a/src/tests/mcalign-test.c +++ b/src/tests/mcalign-test.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/tests/memblock-test.c b/src/tests/memblock-test.c index 2b9d3401..6da1b1e9 100644 --- a/src/tests/memblock-test.c +++ b/src/tests/memblock-test.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/tests/memblockq-test.c b/src/tests/memblockq-test.c index 3fa8d79f..7bf992a1 100644 --- a/src/tests/memblockq-test.c +++ b/src/tests/memblockq-test.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/tests/mix-test.c b/src/tests/mix-test.c index d07b1b0c..f3f6f829 100644 --- a/src/tests/mix-test.c +++ b/src/tests/mix-test.c @@ -1,5 +1,3 @@ -/* $Id: resampler-test.c 2037 2007-11-09 02:45:07Z lennart $ */ - /*** This file is part of PulseAudio. diff --git a/src/tests/pacat-simple.c b/src/tests/pacat-simple.c index c2123b74..b26e4b68 100644 --- a/src/tests/pacat-simple.c +++ b/src/tests/pacat-simple.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/tests/parec-simple.c b/src/tests/parec-simple.c index 9c66cc23..6c0d529b 100644 --- a/src/tests/parec-simple.c +++ b/src/tests/parec-simple.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/tests/proplist-test.c b/src/tests/proplist-test.c index 5f7a78f3..20041af6 100644 --- a/src/tests/proplist-test.c +++ b/src/tests/proplist-test.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/tests/queue-test.c b/src/tests/queue-test.c index b357ab10..105f094a 100644 --- a/src/tests/queue-test.c +++ b/src/tests/queue-test.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/tests/remix-test.c b/src/tests/remix-test.c index d2fa6943..4777c150 100644 --- a/src/tests/remix-test.c +++ b/src/tests/remix-test.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/tests/resampler-test.c b/src/tests/resampler-test.c index 820a0c1e..1a20be2c 100644 --- a/src/tests/resampler-test.c +++ b/src/tests/resampler-test.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/tests/rtpoll-test.c b/src/tests/rtpoll-test.c index af942104..953fd61d 100644 --- a/src/tests/rtpoll-test.c +++ b/src/tests/rtpoll-test.c @@ -1,5 +1,3 @@ -/* $Id: thread-test.c 1621 2007-08-10 22:00:22Z lennart $ */ - /*** This file is part of PulseAudio. diff --git a/src/tests/rtstutter.c b/src/tests/rtstutter.c index 39dfc5d3..91e85c36 100644 --- a/src/tests/rtstutter.c +++ b/src/tests/rtstutter.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -75,7 +73,7 @@ static void* work(void *p) { end.tv_sec += nsec / PA_NSEC_PER_SEC; end.tv_nsec += nsec % PA_NSEC_PER_SEC; - while (end.tv_nsec > PA_NSEC_PER_SEC) { + while ((pa_usec_t) end.tv_nsec > PA_NSEC_PER_SEC) { end.tv_sec++; end.tv_nsec -= PA_NSEC_PER_SEC; } diff --git a/src/tests/sig2str-test.c b/src/tests/sig2str-test.c index 52cb9db4..d64a8902 100644 --- a/src/tests/sig2str-test.c +++ b/src/tests/sig2str-test.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/tests/smoother-test.c b/src/tests/smoother-test.c index de037724..b78f3c91 100644 --- a/src/tests/smoother-test.c +++ b/src/tests/smoother-test.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/tests/stripnul.c b/src/tests/stripnul.c index 2f87e877..0ab06776 100644 --- a/src/tests/stripnul.c +++ b/src/tests/stripnul.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/tests/sync-playback.c b/src/tests/sync-playback.c index 63510eb6..7ab3a25c 100644 --- a/src/tests/sync-playback.c +++ b/src/tests/sync-playback.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/tests/thread-mainloop-test.c b/src/tests/thread-mainloop-test.c index ac6d5049..7a62f85a 100644 --- a/src/tests/thread-mainloop-test.c +++ b/src/tests/thread-mainloop-test.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/tests/thread-test.c b/src/tests/thread-test.c index 72dde6cb..f29b5e71 100644 --- a/src/tests/thread-test.c +++ b/src/tests/thread-test.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/tests/utf8-test.c b/src/tests/utf8-test.c index b9594dcc..f1708ad4 100644 --- a/src/tests/utf8-test.c +++ b/src/tests/utf8-test.c @@ -1,5 +1,3 @@ -/* $Id$ */ - #include <stdio.h> #include <assert.h> diff --git a/src/tests/voltest.c b/src/tests/voltest.c index 91752ad9..d2c0ff69 100644 --- a/src/tests/voltest.c +++ b/src/tests/voltest.c @@ -1,5 +1,3 @@ -/* $Id$ */ - #include <stdio.h> #include <pulse/volume.h> diff --git a/src/utils/pabrowse.c b/src/utils/pabrowse.c index d88001ef..f2ed9553 100644 --- a/src/utils/pabrowse.c +++ b/src/utils/pabrowse.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/utils/pacat.c b/src/utils/pacat.c index fc9d56d6..ee784a99 100644 --- a/src/utils/pacat.c +++ b/src/utils/pacat.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/utils/pacmd.c b/src/utils/pacmd.c index dff9af9d..67d95252 100644 --- a/src/utils/pacmd.c +++ b/src/utils/pacmd.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. @@ -65,7 +63,9 @@ int main(PA_GCC_UNUSED int argc, PA_GCC_UNUSED char*argv[]) { memset(&sa, 0, sizeof(sa)); sa.sun_family = AF_UNIX; - cli = pa_runtime_path("cli"); + if (!(cli = pa_runtime_path("cli"))) + goto fail; + pa_strlcpy(sa.sun_path, cli, sizeof(sa.sun_path)); pa_xfree(cli); diff --git a/src/utils/pactl.c b/src/utils/pactl.c index 1f875a88..4cca2f86 100644 --- a/src/utils/pactl.c +++ b/src/utils/pactl.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/utils/padsp b/src/utils/padsp index c70c3af7..4fe175c2 100755 --- a/src/utils/padsp +++ b/src/utils/padsp @@ -1,7 +1,5 @@ #!/bin/sh -# $Id$ -# # This file is part of PulseAudio. # # Copyright 2006 Lennart Poettering diff --git a/src/utils/padsp.c b/src/utils/padsp.c index e43a0de2..d650707e 100644 --- a/src/utils/padsp.c +++ b/src/utils/padsp.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/utils/paplay.c b/src/utils/paplay.c index fddbb18c..1b6228b1 100644 --- a/src/utils/paplay.c +++ b/src/utils/paplay.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/utils/pasuspender.c b/src/utils/pasuspender.c index 05d96a68..5b4885db 100644 --- a/src/utils/pasuspender.c +++ b/src/utils/pasuspender.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. diff --git a/src/utils/pax11publish.c b/src/utils/pax11publish.c index 9a50f8ef..eee7b6a8 100644 --- a/src/utils/pax11publish.c +++ b/src/utils/pax11publish.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. |