summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2008-05-01 19:51:05 +0000
committerLennart Poettering <lennart@poettering.net>2008-05-01 19:51:05 +0000
commit52e3628c3edd98ae3402605e7f44a0fc4545dd0a (patch)
tree843a7bb1a077141baea5ac29e62d163c90b4acc6
parentf94fae3da3e3201afc060d3ae24c96fd9bba56ab (diff)
Yes, yet another evil all-in-one commit of intervowen changes. I suck.
* Drop "state" directory, fold that into "runtime directory" * No longer automatically rewind when a new stream connects * Rework sound file stream, to cause a rewind on initialisation, shorten _pop() code a bit * Fix reference counting of pa_socket_server in the protocol implementations * Rework daemon initialization code to be compatible with non-SUID-root setups where RLIMIT_RTPRIO is non-zero * Print warning if RT/HP is enabled in the config, but due to missing caps, rlimits, policy we cannot enable it. * Fix potential memory leak in pa_open_config_file() * Add pa_find_config_file() which works much like pa_open_config_file() but doesn't actually open the config file in question. Just searches for it. * Add portable pa_is_path_absolute() * Add pa_close_all() and use it on daemon startup to close leaking file descriptors (inspired from what I did for libdaemon) * Add pa_unblock_sigs() and use it on daemon startup to unblock all signals (inspired from libdaemon, too) * Add pa_reset_sigs() and use it on daemon startup to reset all signal handlers (inspired from libdaemon as well) * Implement pa_set_env() * Define RLIMIT_RTTIME and friends if not defined by glibc * Add pa_strempty() * rename state testing macros to include _IS_, to make clearer that they are no states, but testing macros * Implement pa_source_output_set_requested_latency_within_thread() to be able to forward latency info to sources from within the IO thread * Similar for sink inputs * generelize since_underrun counter in sink inputs to "playing_for" and "underrun_for". Use only this for ignore potential rewind requests over underruns * Add new native protocol message PLAYBACK_STREAM_MESSAGE_STARTED for notification about the end of an underrun * Port native protocol to use underrun_for/playing_for which is maintained by the sink input anyway * Pass underrun_for/playing_for in timing info to client * Drop pa_sink_skip() since it breaks underrun detection code * Move PID file and unix sockets to the runtime dir (i.e. ~/.pulse). This fixes a potention DoS attack from other users stealing dirs in /tmp from us so that we cannot take them anymore) * Allow setting of more resource limits from the config file. Set RTTIME by default * Streamline daemon startup code * Rework algorithm to find default configuration files * If run in system mode use "system.pa" instead of "default.pa" as default script file * Change ladspa sink to use pa_clamp_samples() for clamping samples * Teach module-null-sink how to deal with rewinding * Try to support ALSA devices with no implicit channel map. Synthesize one by padding with PA_CHANNEL_POSITION_AUX channels. This is not tested since I lack hardware with these problems. * Make use of time smoother in the client libraries. * Add new pa_stream_is_corked() and pa_stream_set_started_callback() functions to public API * Since our native socket moved, add some code for finding sockets created by old versions of PA. This should ease upgrades git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2329 fefdeb5f-60dc-0310-8127-8f9354f1896f
-rw-r--r--src/daemon/cmdline.c3
-rw-r--r--src/daemon/daemon-conf.c178
-rw-r--r--src/daemon/daemon-conf.h21
-rw-r--r--src/daemon/daemon.conf.in14
-rw-r--r--src/daemon/main.c290
-rw-r--r--src/modules/alsa-util.c20
-rw-r--r--src/modules/module-alsa-sink.c18
-rw-r--r--src/modules/module-alsa-source.c6
-rw-r--r--src/modules/module-combine.c31
-rw-r--r--src/modules/module-default-device-restore.c154
-rw-r--r--src/modules/module-device-restore.c9
-rw-r--r--src/modules/module-esound-sink.c6
-rw-r--r--src/modules/module-ladspa-sink.c53
-rw-r--r--src/modules/module-match.c10
-rw-r--r--src/modules/module-null-sink.c120
-rw-r--r--src/modules/module-oss.c22
-rw-r--r--src/modules/module-protocol-stub.c70
-rw-r--r--src/modules/module-remap-sink.c39
-rw-r--r--src/modules/module-suspend-on-idle.c4
-rw-r--r--src/modules/module-tunnel.c14
-rw-r--r--src/modules/module-volume-restore.c18
-rw-r--r--src/pulse/client-conf.c20
-rw-r--r--src/pulse/context.c274
-rw-r--r--src/pulse/def.h35
-rw-r--r--src/pulse/internal.h47
-rw-r--r--src/pulse/introspect.c22
-rw-r--r--src/pulse/scache.c4
-rw-r--r--src/pulse/stream.c759
-rw-r--r--src/pulse/stream.h15
-rw-r--r--src/pulsecore/core-util.c586
-rw-r--r--src/pulsecore/core-util.h41
-rw-r--r--src/pulsecore/native-common.h3
-rw-r--r--src/pulsecore/pid.c20
-rw-r--r--src/pulsecore/protocol-cli.c2
-rw-r--r--src/pulsecore/protocol-esound.c2
-rw-r--r--src/pulsecore/protocol-http.c2
-rw-r--r--src/pulsecore/protocol-native.c57
-rw-r--r--src/pulsecore/protocol-simple.c2
-rw-r--r--src/pulsecore/sink-input.c170
-rw-r--r--src/pulsecore/sink-input.h18
-rw-r--r--src/pulsecore/sink.c128
-rw-r--r--src/pulsecore/sink.h39
-rw-r--r--src/pulsecore/sound-file-stream.c59
-rw-r--r--src/pulsecore/source-output.c85
-rw-r--r--src/pulsecore/source-output.h14
-rw-r--r--src/pulsecore/source.c54
-rw-r--r--src/pulsecore/source.h5
-rw-r--r--src/utils/pacmd.c7
48 files changed, 2373 insertions, 1197 deletions
diff --git a/src/daemon/cmdline.c b/src/daemon/cmdline.c
index f1e1282c..97c75f37 100644
--- a/src/daemon/cmdline.c
+++ b/src/daemon/cmdline.c
@@ -293,8 +293,7 @@ int pa_cmdline_parse(pa_daemon_conf *conf, int argc, char *const argv [], int *d
break;
case 'n':
- pa_xfree(conf->default_script_file);
- conf->default_script_file = NULL;
+ conf->load_default_script_file = FALSE;
break;
case ARG_LOG_TARGET:
diff --git a/src/daemon/daemon-conf.c b/src/daemon/daemon-conf.c
index c98c0218..f9ad7ec0 100644
--- a/src/daemon/daemon-conf.c
+++ b/src/daemon/daemon-conf.c
@@ -33,6 +33,7 @@
#include <sched.h>
#include <pulse/xmalloc.h>
+#include <pulse/timeval.h>
#include <pulsecore/core-error.h>
#include <pulsecore/core-util.h>
@@ -45,6 +46,8 @@
#define DEFAULT_SCRIPT_FILE PA_DEFAULT_CONFIG_DIR PA_PATH_SEP "default.pa"
#define DEFAULT_SCRIPT_FILE_USER PA_PATH_SEP "default.pa"
+#define DEFAULT_SYSTEM_SCRIPT_FILE PA_DEFAULT_CONFIG_DIR PA_PATH_SEP "system.pa"
+
#define DEFAULT_CONFIG_FILE PA_DEFAULT_CONFIG_DIR PA_PATH_SEP "daemon.conf"
#define DEFAULT_CONFIG_FILE_USER PA_PATH_SEP "daemon.conf"
@@ -67,6 +70,7 @@ static const pa_daemon_conf default_conf = {
.auto_log_target = 1,
.script_commands = NULL,
.dl_search_path = NULL,
+ .load_default_script_file = TRUE,
.default_script_file = NULL,
.log_target = PA_LOG_SYSLOG,
.log_level = PA_LOG_NOTICE,
@@ -81,34 +85,43 @@ static const pa_daemon_conf default_conf = {
.default_fragment_size_msec = 25,
.default_sample_spec = { .format = PA_SAMPLE_S16NE, .rate = 44100, .channels = 2 }
#ifdef HAVE_SYS_RESOURCE_H
- , .rlimit_as = { .value = 0, .is_set = FALSE },
- .rlimit_core = { .value = 0, .is_set = FALSE },
+ ,.rlimit_fsize = { .value = 0, .is_set = FALSE },
.rlimit_data = { .value = 0, .is_set = FALSE },
- .rlimit_fsize = { .value = 0, .is_set = FALSE },
- .rlimit_nofile = { .value = 256, .is_set = TRUE },
- .rlimit_stack = { .value = 0, .is_set = FALSE }
+ .rlimit_stack = { .value = 0, .is_set = FALSE },
+ .rlimit_core = { .value = 0, .is_set = FALSE },
+ .rlimit_rss = { .value = 0, .is_set = FALSE }
#ifdef RLIMIT_NPROC
- , .rlimit_nproc = { .value = 0, .is_set = FALSE }
+ ,.rlimit_nproc = { .value = 0, .is_set = FALSE }
#endif
+ ,.rlimit_nofile = { .value = 256, .is_set = TRUE }
#ifdef RLIMIT_MEMLOCK
- , .rlimit_memlock = { .value = 0, .is_set = FALSE }
+ ,.rlimit_memlock = { .value = 0, .is_set = FALSE }
+#endif
+ ,.rlimit_as = { .value = 0, .is_set = FALSE }
+#ifdef RLIMIT_LOCKS
+ ,.rlimit_locks = { .value = 0, .is_set = FALSE }
+#endif
+#ifdef RLIMIT_SIGPENDING
+ ,.rlimit_sigpending = { .value = 0, .is_set = FALSE }
+#endif
+#ifdef RLIMIT_MSGQUEUE
+ ,.rlimit_msgqueue = { .value = 0, .is_set = FALSE }
#endif
#ifdef RLIMIT_NICE
- , .rlimit_nice = { .value = 31, .is_set = TRUE } /* nice level of -11 */
+ ,.rlimit_nice = { .value = 31, .is_set = TRUE } /* nice level of -11 */
#endif
#ifdef RLIMIT_RTPRIO
- , .rlimit_rtprio = { .value = 9, .is_set = TRUE } /* One below JACK's default for the server */
+ ,.rlimit_rtprio = { .value = 9, .is_set = TRUE } /* One below JACK's default for the server */
+#endif
+#ifdef RLIMIT_RTTIME
+ ,.rlimit_rttime = { .value = PA_USEC_PER_SEC, .is_set = TRUE }
#endif
#endif
};
pa_daemon_conf* pa_daemon_conf_new(void) {
- FILE *f;
pa_daemon_conf *c = pa_xnewdup(pa_daemon_conf, &default_conf, 1);
- if ((f = pa_open_config_file(DEFAULT_SCRIPT_FILE, DEFAULT_SCRIPT_FILE_USER, ENV_SCRIPT_FILE, &c->default_script_file, "r")))
- fclose(f);
-
c->dl_search_path = pa_xstrdup(PA_DLSEARCHPATH);
return c;
}
@@ -412,25 +425,39 @@ int pa_daemon_conf_load(pa_daemon_conf *c, const char *filename) {
{ "default-fragment-size-msec", parse_fragment_size_msec, NULL },
{ "nice-level", parse_nice_level, NULL },
{ "disable-remixing", pa_config_parse_bool, NULL },
+ { "load-default-script-file", pa_config_parse_bool, NULL },
#ifdef HAVE_SYS_RESOURCE_H
- { "rlimit-as", parse_rlimit, NULL },
- { "rlimit-core", parse_rlimit, NULL },
- { "rlimit-data", parse_rlimit, NULL },
{ "rlimit-fsize", parse_rlimit, NULL },
- { "rlimit-nofile", parse_rlimit, NULL },
+ { "rlimit-data", parse_rlimit, NULL },
{ "rlimit-stack", parse_rlimit, NULL },
+ { "rlimit-core", parse_rlimit, NULL },
+ { "rlimit-rss", parse_rlimit, NULL },
+ { "rlimit-nofile", parse_rlimit, NULL },
+ { "rlimit-as", parse_rlimit, NULL },
#ifdef RLIMIT_NPROC
{ "rlimit-nproc", parse_rlimit, NULL },
#endif
#ifdef RLIMIT_MEMLOCK
{ "rlimit-memlock", parse_rlimit, NULL },
#endif
+#ifdef RLIMIT_LOCKS
+ { "rlimit-locks", parse_rlimit, NULL },
+#endif
+#ifdef RLIMIT_SIGPENDING
+ { "rlimit-sigpending", parse_rlimit, NULL },
+#endif
+#ifdef RLIMIT_MSGQUEUE
+ { "rlimit-msgqueue", parse_rlimit, NULL },
+#endif
#ifdef RLIMIT_NICE
{ "rlimit-nice", parse_rlimit, NULL },
#endif
#ifdef RLIMIT_RTPRIO
{ "rlimit-rtprio", parse_rlimit, NULL },
#endif
+#ifdef RLIMIT_RTTIME
+ { "rlimit-rttime", parse_rlimit, NULL },
+#endif
#endif
{ NULL, NULL, NULL },
};
@@ -461,33 +488,66 @@ int pa_daemon_conf_load(pa_daemon_conf *c, const char *filename) {
table[23].data = c;
table[24].data = c;
table[25].data = &c->disable_remixing;
+ table[26].data = &c->load_default_script_file;
#ifdef HAVE_SYS_RESOURCE_H
- table[26].data = &c->rlimit_as;
- table[27].data = &c->rlimit_core;
+ table[27].data = &c->rlimit_fsize;
table[28].data = &c->rlimit_data;
- table[29].data = &c->rlimit_fsize;
- table[30].data = &c->rlimit_nofile;
- table[31].data = &c->rlimit_stack;
+ table[29].data = &c->rlimit_stack;
+ table[30].data = &c->rlimit_as;
+ table[31].data = &c->rlimit_core;
+ table[32].data = &c->rlimit_nofile;
+ table[33].data = &c->rlimit_as;
#ifdef RLIMIT_NPROC
- table[32].data = &c->rlimit_nproc;
+ table[34].data = &c->rlimit_nproc;
#endif
+
#ifdef RLIMIT_MEMLOCK
#ifndef RLIMIT_NPROC
#error "Houston, we have a numbering problem!"
#endif
- table[33].data = &c->rlimit_memlock;
+ table[35].data = &c->rlimit_memlock;
#endif
-#ifdef RLIMIT_NICE
+
+#ifdef RLIMIT_LOCKS
#ifndef RLIMIT_MEMLOCK
#error "Houston, we have a numbering problem!"
#endif
- table[34].data = &c->rlimit_nice;
+ table[36].data = &c->rlimit_locks;
+#endif
+
+#ifdef RLIMIT_SIGPENDING
+#ifndef RLIMIT_LOCKS
+#error "Houston, we have a numbering problem!"
+#endif
+ table[37].data = &c->rlimit_sigpending;
+#endif
+
+#ifdef RLIMIT_MSGQUEUE
+#ifndef RLIMIT_SIGPENDING
+#error "Houston, we have a numbering problem!"
+#endif
+ table[38].data = &c->rlimit_msgqueue;
+#endif
+
+#ifdef RLIMIT_NICE
+#ifndef RLIMIT_MSGQUEUE
+#error "Houston, we have a numbering problem!"
+#endif
+ table[39].data = &c->rlimit_nice;
#endif
+
#ifdef RLIMIT_RTPRIO
#ifndef RLIMIT_NICE
#error "Houston, we have a numbering problem!"
#endif
- table[35].data = &c->rlimit_rtprio;
+ table[40].data = &c->rlimit_rtprio;
+#endif
+
+#ifdef RLIMIT_RTTIME
+#ifndef RLIMIT_RTTIME
+#error "Houston, we have a numbering problem!"
+#endif
+ table[41].data = &c->rlimit_rttime;
#endif
#endif
@@ -496,10 +556,10 @@ int pa_daemon_conf_load(pa_daemon_conf *c, const char *filename) {
f = filename ?
fopen(c->config_file = pa_xstrdup(filename), "r") :
- pa_open_config_file(DEFAULT_CONFIG_FILE, DEFAULT_CONFIG_FILE_USER, ENV_CONFIG_FILE, &c->config_file, "r");
+ pa_open_config_file(DEFAULT_CONFIG_FILE, DEFAULT_CONFIG_FILE_USER, ENV_CONFIG_FILE, &c->config_file);
if (!f && errno != ENOENT) {
- pa_log_warn("Failed to open configuration file '%s': %s", c->config_file, pa_cstrerror(errno));
+ pa_log_warn("Failed to open configuration file: %s", pa_cstrerror(errno));
goto finish;
}
@@ -514,6 +574,7 @@ finish:
int pa_daemon_conf_env(pa_daemon_conf *c) {
char *e;
+ pa_assert(c);
if ((e = getenv(ENV_DL_SEARCH_PATH))) {
pa_xfree(c->dl_search_path);
@@ -527,6 +588,35 @@ int pa_daemon_conf_env(pa_daemon_conf *c) {
return 0;
}
+const char *pa_daemon_conf_get_default_script_file(pa_daemon_conf *c) {
+ pa_assert(c);
+
+ if (!c->default_script_file) {
+ if (c->system_instance)
+ c->default_script_file = pa_find_config_file(DEFAULT_SYSTEM_SCRIPT_FILE, NULL, ENV_SCRIPT_FILE);
+ else
+ c->default_script_file = pa_find_config_file(DEFAULT_SCRIPT_FILE, DEFAULT_SCRIPT_FILE_USER, ENV_SCRIPT_FILE);
+ }
+
+ return c->default_script_file;
+}
+
+FILE *pa_daemon_conf_open_default_script_file(pa_daemon_conf *c) {
+ FILE *f;
+ pa_assert(c);
+
+ if (!c->default_script_file) {
+ if (c->system_instance)
+ f = pa_open_config_file(DEFAULT_SYSTEM_SCRIPT_FILE, NULL, ENV_SCRIPT_FILE, &c->default_script_file);
+ else
+ f = pa_open_config_file(DEFAULT_SCRIPT_FILE, DEFAULT_SCRIPT_FILE_USER, ENV_SCRIPT_FILE, &c->default_script_file);
+ } else
+ f = fopen(c->default_script_file, "r");
+
+ return f;
+}
+
+
static const char* const log_level_to_string[] = {
[PA_LOG_DEBUG] = "debug",
[PA_LOG_INFO] = "info",
@@ -561,8 +651,9 @@ char *pa_daemon_conf_dump(pa_daemon_conf *c) {
pa_strbuf_printf(s, "exit-idle-time = %i\n", c->exit_idle_time);
pa_strbuf_printf(s, "module-idle-time = %i\n", c->module_idle_time);
pa_strbuf_printf(s, "scache-idle-time = %i\n", c->scache_idle_time);
- pa_strbuf_printf(s, "dl-search-path = %s\n", c->dl_search_path ? c->dl_search_path : "");
- pa_strbuf_printf(s, "default-script-file = %s\n", c->default_script_file);
+ pa_strbuf_printf(s, "dl-search-path = %s\n", pa_strempty(c->dl_search_path));
+ pa_strbuf_printf(s, "default-script-file = %s\n", pa_strempty(pa_daemon_conf_get_default_script_file(c)));
+ pa_strbuf_printf(s, "load-default-script-file = %s\n", pa_yes_no(c->load_default_script_file));
pa_strbuf_printf(s, "log-target = %s\n", c->auto_log_target ? "auto" : (c->log_target == PA_LOG_SYSLOG ? "syslog" : "stderr"));
pa_strbuf_printf(s, "log-level = %s\n", log_level_to_string[c->log_level]);
pa_strbuf_printf(s, "resample-method = %s\n", pa_resample_method_to_string(c->resample_method));
@@ -573,23 +664,36 @@ char *pa_daemon_conf_dump(pa_daemon_conf *c) {
pa_strbuf_printf(s, "default-fragments = %u\n", c->default_n_fragments);
pa_strbuf_printf(s, "default-fragment-size-msec = %u\n", c->default_fragment_size_msec);
#ifdef HAVE_SYS_RESOURCE_H
- pa_strbuf_printf(s, "rlimit-as = %li\n", c->rlimit_as.is_set ? (long int) c->rlimit_as.value : -1);
- pa_strbuf_printf(s, "rlimit-core = %li\n", c->rlimit_core.is_set ? (long int) c->rlimit_core.value : -1);
- pa_strbuf_printf(s, "rlimit-data = %li\n", c->rlimit_data.is_set ? (long int) c->rlimit_data.value : -1);
pa_strbuf_printf(s, "rlimit-fsize = %li\n", c->rlimit_fsize.is_set ? (long int) c->rlimit_fsize.value : -1);
- pa_strbuf_printf(s, "rlimit-nofile = %li\n", c->rlimit_nofile.is_set ? (long int) c->rlimit_nofile.value : -1);
+ pa_strbuf_printf(s, "rlimit-data = %li\n", c->rlimit_data.is_set ? (long int) c->rlimit_data.value : -1);
pa_strbuf_printf(s, "rlimit-stack = %li\n", c->rlimit_stack.is_set ? (long int) c->rlimit_stack.value : -1);
+ pa_strbuf_printf(s, "rlimit-core = %li\n", c->rlimit_core.is_set ? (long int) c->rlimit_core.value : -1);
+ pa_strbuf_printf(s, "rlimit-as = %li\n", c->rlimit_as.is_set ? (long int) c->rlimit_as.value : -1);
+ pa_strbuf_printf(s, "rlimit-rss = %li\n", c->rlimit_rss.is_set ? (long int) c->rlimit_rss.value : -1);
#ifdef RLIMIT_NPROC
pa_strbuf_printf(s, "rlimit-nproc = %li\n", c->rlimit_nproc.is_set ? (long int) c->rlimit_nproc.value : -1);
#endif
+ pa_strbuf_printf(s, "rlimit-nofile = %li\n", c->rlimit_nofile.is_set ? (long int) c->rlimit_nofile.value : -1);
#ifdef RLIMIT_MEMLOCK
pa_strbuf_printf(s, "rlimit-memlock = %li\n", c->rlimit_memlock.is_set ? (long int) c->rlimit_memlock.value : -1);
#endif
+#ifdef RLIMIT_LOCKS
+ pa_strbuf_printf(s, "rlimit-locks = %li\n", c->rlimit_locks.is_set ? (long int) c->rlimit_locks.value : -1);
+#endif
+#ifdef RLIMIT_SIGPENDING
+ pa_strbuf_printf(s, "rlimit-sigpending = %li\n", c->rlimit_sigpending.is_set ? (long int) c->rlimit_sigpending.value : -1);
+#endif
+#ifdef RLIMIT_MSGQUEUE
+ pa_strbuf_printf(s, "rlimit-msgqueue = %li\n", c->rlimit_msgqueue.is_set ? (long int) c->rlimit_msgqueue.value : -1);
+#endif
#ifdef RLIMIT_NICE
- pa_strbuf_printf(s, "rlimit-nice = %li\n", c->rlimit_memlock.is_set ? (long int) c->rlimit_nice.value : -1);
+ pa_strbuf_printf(s, "rlimit-nice = %li\n", c->rlimit_nice.is_set ? (long int) c->rlimit_nice.value : -1);
#endif
#ifdef RLIMIT_RTPRIO
- pa_strbuf_printf(s, "rlimit-rtprio = %li\n", c->rlimit_memlock.is_set ? (long int) c->rlimit_rtprio.value : -1);
+ pa_strbuf_printf(s, "rlimit-rtprio = %li\n", c->rlimit_rtprio.is_set ? (long int) c->rlimit_rtprio.value : -1);
+#endif
+#ifdef RLIMIT_RTTIME
+ pa_strbuf_printf(s, "rlimit-rttime = %li\n", c->rlimit_rttime.is_set ? (long int) c->rlimit_rttime.value : -1);
#endif
#endif
diff --git a/src/daemon/daemon-conf.h b/src/daemon/daemon-conf.h
index 3dcafbfe..03a75661 100644
--- a/src/daemon/daemon-conf.h
+++ b/src/daemon/daemon-conf.h
@@ -27,6 +27,7 @@
#include <pulsecore/log.h>
#include <pulsecore/macro.h>
+#include <pulsecore/core-util.h>
#include <pulse/sample.h>
#ifdef HAVE_SYS_RESOURCE_H
@@ -65,7 +66,8 @@ typedef struct pa_daemon_conf {
system_instance,
no_cpu_limit,
disable_shm,
- disable_remixing;
+ disable_remixing,
+ load_default_script_file;
int exit_idle_time,
module_idle_time,
scache_idle_time,
@@ -79,19 +81,31 @@ typedef struct pa_daemon_conf {
char *config_file;
#ifdef HAVE_SYS_RESOURCE_H
- pa_rlimit rlimit_as, rlimit_core, rlimit_data, rlimit_fsize, rlimit_nofile, rlimit_stack;
+ pa_rlimit rlimit_fsize, rlimit_data, rlimit_stack, rlimit_core, rlimit_rss, rlimit_nofile, rlimit_as;
#ifdef RLIMIT_NPROC
pa_rlimit rlimit_nproc;
#endif
#ifdef RLIMIT_MEMLOCK
pa_rlimit rlimit_memlock;
#endif
+#ifdef RLIMIT_LOCKS
+ pa_rlimit rlimit_locks;
+#endif
+#ifdef RLIMIT_SIGPENDING
+ pa_rlimit rlimit_sigpending;
+#endif
+#ifdef RLIMIT_MSGQUEUE
+ pa_rlimit rlimit_msgqueue;
+#endif
#ifdef RLIMIT_NICE
pa_rlimit rlimit_nice;
#endif
#ifdef RLIMIT_RTPRIO
pa_rlimit rlimit_rtprio;
#endif
+#ifdef RLIMIT_RTTIME
+ pa_rlimit rlimit_rttime;
+#endif
#endif
unsigned default_n_fragments, default_fragment_size_msec;
@@ -121,4 +135,7 @@ int pa_daemon_conf_set_log_target(pa_daemon_conf *c, const char *string);
int pa_daemon_conf_set_log_level(pa_daemon_conf *c, const char *string);
int pa_daemon_conf_set_resample_method(pa_daemon_conf *c, const char *string);
+const char *pa_daemon_conf_get_default_script_file(pa_daemon_conf *c);
+FILE *pa_daemon_conf_open_default_script_file(pa_daemon_conf *c);
+
#endif
diff --git a/src/daemon/daemon.conf.in b/src/daemon/daemon.conf.in
index e4cfb82b..fd35c0f6 100644
--- a/src/daemon/daemon.conf.in
+++ b/src/daemon/daemon.conf.in
@@ -40,6 +40,7 @@
; dl-search-path = (depends on architecture)
+; load-defaul-script-file = yes
; default-script-file = @PA_DEFAULT_CONFIG_FILE@
; log-target = auto
@@ -50,16 +51,21 @@
; no-cpu-limit = no
-; rlimit-as = -1
-; rlimit-core = -1
-; rlimit-data = -1
; rlimit-fsize = -1
-; rlimit-nofile = 256
+; rlimit-data = -1
; rlimit-stack = -1
+; rlimit-core = -1
+; rlimit-as = -1
+; rlimit-rss = -1
; rlimit-nproc = -1
+; rlimit-nofile = 256
; rlimit-memlock = -1
+; rlimit-locks = -1
+; rlimit-sigpending = -1
+; rlimit-msgqueue = -1
; rlimit-nice = 31
; rlimit-rtprio = 9
+; rlimit-rtttime = 1000000
; default-sample-format = s16le
; default-sample-rate = 44100
diff --git a/src/daemon/main.c b/src/daemon/main.c
index 6b0c81da..b1ba5a31 100644
--- a/src/daemon/main.c
+++ b/src/daemon/main.c
@@ -115,7 +115,7 @@ static void message_cb(pa_mainloop_api*a, pa_time_event*e, PA_GCC_UNUSED const s
MSG msg;
struct timeval tvnext;
- while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
+ while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
if (msg.message == WM_QUIT)
raise(SIGTERM);
else {
@@ -164,8 +164,6 @@ static void signal_callback(pa_mainloop_api*m, PA_GCC_UNUSED pa_signal_event *e,
}
}
-#define set_env(key, value) putenv(pa_sprintf_malloc("%s=%s", (key), (value)))
-
#if defined(HAVE_PWD_H) && defined(HAVE_GRP_H)
static int change_user(void) {
@@ -241,14 +239,14 @@ static int change_user(void) {
return -1;
}
- set_env("USER", PA_SYSTEM_USER);
- set_env("USERNAME", PA_SYSTEM_USER);
- set_env("LOGNAME", PA_SYSTEM_USER);
- set_env("HOME", PA_SYSTEM_RUNTIME_PATH);
+ pa_set_env("USER", PA_SYSTEM_USER);
+ pa_set_env("USERNAME", PA_SYSTEM_USER);
+ pa_set_env("LOGNAME", PA_SYSTEM_USER);
+ pa_set_env("HOME", PA_SYSTEM_RUNTIME_PATH);
/* Relevant for pa_runtime_path() */
- set_env("PULSE_RUNTIME_PATH", PA_SYSTEM_RUNTIME_PATH);
- set_env("PULSE_CONFIG_PATH", PA_SYSTEM_RUNTIME_PATH);
+ pa_set_env("PULSE_RUNTIME_PATH", PA_SYSTEM_RUNTIME_PATH);
+ pa_set_env("PULSE_CONFIG_PATH", PA_SYSTEM_RUNTIME_PATH);
pa_log_info("Successfully dropped root privileges.");
@@ -264,23 +262,6 @@ static int change_user(void) {
#endif /* HAVE_PWD_H && HAVE_GRP_H */
-static int create_runtime_dir(void) {
- char fn[PATH_MAX];
-
- pa_runtime_path(NULL, fn, sizeof(fn));
-
- /* This function is called only when the daemon is started in
- * per-user mode. We create the runtime directory somewhere in
- * /tmp/ with the current UID/GID */
-
- if (pa_make_secure_dir(fn, 0700, (uid_t)-1, (gid_t)-1) < 0) {
- pa_log("Failed to create '%s': %s", fn, pa_cstrerror(errno));
- return -1;
- }
-
- return 0;
-}
-
#ifdef HAVE_SYS_RESOURCE_H
static int set_one_rlimit(const pa_rlimit *r, int resource, const char *name) {
@@ -293,7 +274,7 @@ static int set_one_rlimit(const pa_rlimit *r, int resource, const char *name) {
rl.rlim_cur = rl.rlim_max = r->value;
if (setrlimit(resource, &rl) < 0) {
- pa_log_warn("setrlimit(%s, (%u, %u)) failed: %s", name, (unsigned) r->value, (unsigned) r->value, pa_cstrerror(errno));
+ pa_log_info("setrlimit(%s, (%u, %u)) failed: %s", name, (unsigned) r->value, (unsigned) r->value, pa_cstrerror(errno));
return -1;
}
@@ -301,24 +282,37 @@ static int set_one_rlimit(const pa_rlimit *r, int resource, const char *name) {
}
static void set_all_rlimits(const pa_daemon_conf *conf) {
- set_one_rlimit(&conf->rlimit_as, RLIMIT_AS, "RLIMIT_AS");
- set_one_rlimit(&conf->rlimit_core, RLIMIT_CORE, "RLIMIT_CORE");
- set_one_rlimit(&conf->rlimit_data, RLIMIT_DATA, "RLIMIT_DATA");
set_one_rlimit(&conf->rlimit_fsize, RLIMIT_FSIZE, "RLIMIT_FSIZE");
- set_one_rlimit(&conf->rlimit_nofile, RLIMIT_NOFILE, "RLIMIT_NOFILE");
+ set_one_rlimit(&conf->rlimit_data, RLIMIT_DATA, "RLIMIT_DATA");
set_one_rlimit(&conf->rlimit_stack, RLIMIT_STACK, "RLIMIT_STACK");
+ set_one_rlimit(&conf->rlimit_core, RLIMIT_CORE, "RLIMIT_CORE");
+ set_one_rlimit(&conf->rlimit_rss, RLIMIT_RSS, "RLIMIT_RSS");
#ifdef RLIMIT_NPROC
set_one_rlimit(&conf->rlimit_nproc, RLIMIT_NPROC, "RLIMIT_NPROC");
#endif
+ set_one_rlimit(&conf->rlimit_nofile, RLIMIT_NOFILE, "RLIMIT_NOFILE");
#ifdef RLIMIT_MEMLOCK
set_one_rlimit(&conf->rlimit_memlock, RLIMIT_MEMLOCK, "RLIMIT_MEMLOCK");
#endif
+ set_one_rlimit(&conf->rlimit_as, RLIMIT_AS, "RLIMIT_AS");
+#ifdef RLIMIT_LOCKS
+ set_one_rlimit(&conf->rlimit_locks, RLIMIT_LOCKS, "RLIMIT_LOCKS");
+#endif
+#ifdef RLIMIT_SIGPENDING
+ set_one_rlimit(&conf->rlimit_sigpending, RLIMIT_SIGPENDING, "RLIMIT_SIGPENDING");
+#endif
+#ifdef RLIMIT_MSGQUEUE
+ set_one_rlimit(&conf->rlimit_msgqueue, RLIMIT_MSGQUEUE, "RLIMIT_MSGQUEUE");
+#endif
#ifdef RLIMIT_NICE
set_one_rlimit(&conf->rlimit_nice, RLIMIT_NICE, "RLIMIT_NICE");
#endif
#ifdef RLIMIT_RTPRIO
set_one_rlimit(&conf->rlimit_rtprio, RLIMIT_RTPRIO, "RLIMIT_RTPRIO");
#endif
+#ifdef RLIMIT_RTTIME
+ set_one_rlimit(&conf->rlimit_rttime, RLIMIT_RTTIME, "RLIMIT_RTTIME");
+#endif
}
#endif
@@ -329,19 +323,20 @@ int main(int argc, char *argv[]) {
pa_mainloop *mainloop = NULL;
char *s;
int r = 0, retval = 1, d = 0;
- int daemon_pipe[2] = { -1, -1 };
pa_bool_t suid_root, real_root;
- int valid_pid_file = 0;
+ pa_bool_t valid_pid_file = FALSE;
gid_t gid = (gid_t) -1;
- pa_bool_t allow_realtime, allow_high_priority;
pa_bool_t ltdl_init = FALSE;
-
+ int passed_fd = -1;
+ const char *e;
+#ifdef HAVE_FORK
+ int daemon_pipe[2] = { -1, -1 };
+#endif
#ifdef OS_IS_WIN32
- pa_time_event *timer;
- struct timeval tv;
+ pa_time_event *win32_timer;
+ struct timeval win32_tv;
#endif
-
#if defined(__linux__) && defined(__OPTIMIZE__)
/*
Disable lazy relocations to make usage of external libraries
@@ -355,7 +350,7 @@ int main(int argc, char *argv[]) {
/* We have to execute ourselves, because the libc caches the
* value of $LD_BIND_NOW on initialization. */
- putenv(pa_xstrdup("LD_BIND_NOW=1"));
+ pa_set_env("LD_BIND_NOW", "1");
pa_assert_se(rp = pa_readlink("/proc/self/exe"));
pa_assert_se(execv(rp, argv) == 0);
}
@@ -385,6 +380,18 @@ int main(int argc, char *argv[]) {
* is just too risky tun let PA run as root all the time. */
}
+ if ((e = getenv("PULSE_PASSED_FD"))) {
+ passed_fd = atoi(e);
+
+ if (passed_fd <= 2)
+ passed_fd = -1;
+ }
+
+ pa_close_all(passed_fd, -1);
+
+ pa_reset_sigs(-1);
+ pa_unblock_sigs(-1);
+
/* At this point, we are a normal user, possibly with CAP_NICE if
* we were started SUID. If we are started as normal root, than we
* still are normal root. */
@@ -410,67 +417,59 @@ int main(int argc, char *argv[]) {
pa_log_set_target(conf->auto_log_target ? PA_LOG_STDERR : conf->log_target, NULL);
if (suid_root) {
+ pa_bool_t allow_realtime, allow_high_priority;
+
/* Ok, we're suid root, so let's better not enable high prio
* or RT by default */
allow_high_priority = allow_realtime = FALSE;
+ if (conf->high_priority || conf->realtime_scheduling)
+ if (pa_own_uid_in_group(PA_REALTIME_GROUP, &gid) > 0) {
+ pa_log_info("We're in the group '"PA_REALTIME_GROUP"', allowing real-time and high-priority scheduling.");
+ allow_realtime = conf->realtime_scheduling;
+ allow_high_priority = conf->high_priority;
+ }
+
#ifdef HAVE_POLKIT
- if (conf->high_priority) {
+ if (conf->high_priority && !allow_high_priority) {
if (pa_polkit_check("org.pulseaudio.acquire-high-priority") > 0) {
- pa_log_info("PolicyKit grants us acquire-high-priority privilige.");
+ pa_log_info("PolicyKit grants us acquire-high-priority privilege.");
allow_high_priority = TRUE;
} else
- pa_log_info("PolicyKit refuses acquire-high-priority privilige.");
+ pa_log_info("PolicyKit refuses acquire-high-priority privilege.");
}
- if (conf->realtime_scheduling) {
+ if (conf->realtime_scheduling && !allow_realtime) {
if (pa_polkit_check("org.pulseaudio.acquire-real-time") > 0) {
- pa_log_info("PolicyKit grants us acquire-real-time privilige.");
+ pa_log_info("PolicyKit grants us acquire-real-time privilege.");
allow_realtime = TRUE;
} else
- pa_log_info("PolicyKit refuses acquire-real-time privilige.");
+ pa_log_info("PolicyKit refuses acquire-real-time privilege.");
}
#endif
- if ((conf->high_priority || conf->realtime_scheduling) && pa_own_uid_in_group(PA_REALTIME_GROUP, &gid) > 0) {
- pa_log_info("We're in the group '"PA_REALTIME_GROUP"', allowing real-time and high-priority scheduling.");
- allow_realtime = conf->realtime_scheduling;
- allow_high_priority = conf->high_priority;
- }
-
if (!allow_high_priority && !allow_realtime) {
/* OK, there's no further need to keep CAP_NICE. Hence
* let's give it up early */
pa_drop_caps();
- pa_drop_root();
- suid_root = real_root = FALSE;
+ suid_root = FALSE;
if (conf->high_priority || conf->realtime_scheduling)
pa_log_notice("Called SUID root and real-time/high-priority scheduling was requested in the configuration. However, we lack the necessary priviliges:\n"
"We are not in group '"PA_REALTIME_GROUP"' and PolicyKit refuse to grant us priviliges. Dropping SUID again.\n"
"For enabling real-time scheduling please acquire the appropriate PolicyKit priviliges, or become a member of '"PA_REALTIME_GROUP"', or increase the RLIMIT_NICE/RLIMIT_RTPRIO resource limits for this user.");
}
-
- } else {
-
- /* OK, we're a normal user, so let's allow the user evrything
- * he asks for, it's now the kernel's job to enforce limits,
- * not ours anymore */
- allow_high_priority = allow_realtime = TRUE;
}
- if (conf->high_priority && !allow_high_priority) {
- pa_log_info("High-priority scheduling enabled in configuration but now allowed by policy. Disabling forcibly.");
- conf->high_priority = FALSE;
- }
+#ifdef HAVE_SYS_RESOURCE_H
+ set_all_rlimits(conf);
+#endif
- if (conf->realtime_scheduling && !allow_realtime) {
- pa_log_info("Real-time scheduling enabled in configuration but now allowed by policy. Disabling forcibly.");
- conf->realtime_scheduling = FALSE;
- }
+ if (conf->high_priority && !pa_can_high_priority())
+ pa_log_warn("High-priority scheduling enabled in configuration but now allowed by policy.");
if (conf->high_priority && conf->cmd == PA_CMD_DAEMON)
pa_raise_priority(conf->nice_level);
@@ -482,28 +481,38 @@ int main(int argc, char *argv[]) {
#ifdef RLIMIT_RTPRIO
if (!drop) {
-
+ struct rlimit rl;
/* At this point we still have CAP_NICE if we were loaded
* SUID root. If possible let's acquire RLIMIT_RTPRIO
* instead and give CAP_NICE up. */
- const pa_rlimit rl = { 9, TRUE };
+ if (getrlimit(RLIMIT_RTPRIO, &rl) >= 0) {
- if (set_one_rlimit(&rl, RLIMIT_RTPRIO, "RLIMIT_RTPRIO") >= 0) {
- pa_log_info("Successfully increased RLIMIT_RTPRIO, giving up CAP_NICE.");
- drop = TRUE;
- } else
- pa_log_warn("RLIMIT_RTPRIO failed: %s", pa_cstrerror(errno));
+ if (rl.rlim_cur >= 9)
+ drop = TRUE;
+ else {
+ rl.rlim_max = rl.rlim_cur = 9;
+
+ if (setrlimit(RLIMIT_RTPRIO, &rl) < 0) {
+ pa_log_info("Successfully increased RLIMIT_RTPRIO");
+ drop = TRUE;
+ } else
+ pa_log_warn("RLIMIT_RTPRIO failed: %s", pa_cstrerror(errno));
+ }
+ }
}
#endif
if (drop) {
+ pa_log_info("Giving up CAP_NICE");
pa_drop_caps();
- pa_drop_root();
- suid_root = real_root = FALSE;
+ suid_root = FALSE;
}
}
+ if (conf->realtime_scheduling && !pa_can_realtime())
+ pa_log_warn("Real-time scheduling enabled in configuration but now allowed by policy.");
+
LTDL_SET_PRELOADED_SYMBOLS();
pa_ltdl_init();
ltdl_init = TRUE;
@@ -605,7 +614,7 @@ int main(int argc, char *argv[]) {
#ifdef HAVE_FORK
if (pipe(daemon_pipe) < 0) {
- pa_log("Failed to create pipe.");
+ pa_log("pipe failed: %s", pa_cstrerror(errno));
goto finish;
}
@@ -615,20 +624,24 @@ int main(int argc, char *argv[]) {
}
if (child != 0) {
+ ssize_t n;
/* Father */
pa_assert_se(pa_close(daemon_pipe[1]) == 0);
daemon_pipe[1] = -1;
- if (pa_loop_read(daemon_pipe[0], &retval, sizeof(retval), NULL) != sizeof(retval)) {
- pa_log("read() failed: %s", pa_cstrerror(errno));
+ if ((n = pa_loop_read(daemon_pipe[0], &retval, sizeof(retval), NULL)) != sizeof(retval)) {
+
+ if (n < 0)
+ pa_log("read() failed: %s", pa_cstrerror(errno));
+
retval = 1;
}
if (retval)
- pa_log("daemon startup failed.");
+ pa_log("Daemon startup failed.");
else
- pa_log_info("daemon startup successful.");
+ pa_log_info("Daemon startup successful.");
goto finish;
}
@@ -652,9 +665,9 @@ int main(int argc, char *argv[]) {
pa_close(1);
pa_close(2);
- open("/dev/null", O_RDONLY);
- open("/dev/null", O_WRONLY);
- open("/dev/null", O_WRONLY);
+ pa_assert_se(open("/dev/null", O_RDONLY) == 0);
+ pa_assert_se(open("/dev/null", O_WRONLY) == 1);
+ pa_assert_se(open("/dev/null", O_WRONLY) == 2);
#else
FreeConsole();
#endif
@@ -677,39 +690,32 @@ int main(int argc, char *argv[]) {
#endif
}
+ pa_set_env("PULSE_INTERNAL", "1");
pa_assert_se(chdir("/") == 0);
umask(0022);
- if (conf->system_instance) {
+ if (conf->system_instance)
if (change_user() < 0)
goto finish;
- } else if (create_runtime_dir() < 0)
- goto finish;
+
+ pa_log_info("This is PulseAudio " PACKAGE_VERSION);
+ pa_log_info("Page size is %lu bytes", (unsigned long) PA_PAGE_SIZE);
+ pa_log_info("Using runtime directory %s.", s = pa_get_runtime_dir());
+ pa_xfree(s);
if (conf->use_pid_file) {
if (pa_pid_file_create() < 0) {
pa_log("pa_pid_file_create() failed.");
-#ifdef HAVE_FORK
- if (conf->daemonize)
- pa_loop_write(daemon_pipe[1], &retval, sizeof(retval), NULL);
-#endif
goto finish;
}
- valid_pid_file = 1;
+ valid_pid_file = TRUE;
}
-#ifdef HAVE_SYS_RESOURCE_H
- set_all_rlimits(conf);
-#endif
-
#ifdef SIGPIPE
signal(SIGPIPE, SIG_IGN);
#endif
- pa_log_info("This is PulseAudio " PACKAGE_VERSION);
- pa_log_info("Page size is %lu bytes", (unsigned long) PA_PAGE_SIZE);
-
if (pa_rtclock_hrtimer())
pa_log_info("Fresh high-resolution timers available! Bon appetit!");
else
@@ -738,11 +744,11 @@ int main(int argc, char *argv[]) {
c->realtime_priority = conf->realtime_priority;
c->realtime_scheduling = !!conf->realtime_scheduling;
c->disable_remixing = !!conf->disable_remixing;
+ c->running_as_daemon = !!conf->daemonize;
pa_assert_se(pa_signal_init(pa_mainloop_get_api(mainloop)) == 0);
pa_signal_new(SIGINT, signal_callback, c);
pa_signal_new(SIGTERM, signal_callback, c);
-
#ifdef SIGUSR1
pa_signal_new(SIGUSR1, signal_callback, c);
#endif
@@ -754,23 +760,27 @@ int main(int argc, char *argv[]) {
#endif
#ifdef OS_IS_WIN32
- pa_assert_se(timer = pa_mainloop_get_api(mainloop)->time_new(pa_mainloop_get_api(mainloop), pa_gettimeofday(&tv), message_cb, NULL));
+ win32_timer = pa_mainloop_get_api(mainloop)->time_new(pa_mainloop_get_api(mainloop), pa_gettimeofday(&win32_tv), message_cb, NULL);
#endif
- if (conf->daemonize)
- c->running_as_daemon = TRUE;
-
oil_init();
if (!conf->no_cpu_limit)
pa_assert_se(pa_cpu_limit_init(pa_mainloop_get_api(mainloop)) == 0);
buf = pa_strbuf_new();
- if (conf->default_script_file)
- r = pa_cli_command_execute_file(c, conf->default_script_file, buf, &conf->fail);
+ if (conf->load_default_script_file) {
+ FILE *f;
+
+ if ((f = pa_daemon_conf_open_default_script_file(conf))) {
+ r = pa_cli_command_execute_file_stream(c, f, buf, &conf->fail);
+ fclose(f);
+ }
+ }
if (r >= 0)
r = pa_cli_command_execute(c, conf->script_commands, buf, &conf->fail);
+
pa_log_error("%s", s = pa_strbuf_tostring_free(buf));
pa_xfree(s);
@@ -780,53 +790,55 @@ int main(int argc, char *argv[]) {
if (r < 0 && conf->fail) {
pa_log("Failed to initialize daemon.");
-#ifdef HAVE_FORK
- if (conf->daemonize)
- pa_loop_write(daemon_pipe[1], &retval, sizeof(retval), NULL);
-#endif
- } else if (!c->modules || pa_idxset_size(c->modules) == 0) {
- pa_log("daemon startup without any loaded modules, refusing to work.");
-#ifdef HAVE_FORK
- if (conf->daemonize)
- pa_loop_write(daemon_pipe[1], &retval, sizeof(retval), NULL);
-#endif
- } else {
+ goto finish;
+ }
- retval = 0;
+ if (!c->modules || pa_idxset_size(c->modules) == 0) {
+ pa_log("Daemon startup without any loaded modules, refusing to work.");
+ goto finish;
+ }
+
+ if (c->default_sink_name && !pa_namereg_get(c, c->default_sink_name, PA_NAMEREG_SINK, TRUE) && conf->fail) {
+ pa_log_error("Default sink name (%s) does not exist in name register.", c->default_sink_name);
+ goto finish;
+ }
- if (c->default_sink_name &&
- pa_namereg_get(c, c->default_sink_name, PA_NAMEREG_SINK, 1) == NULL) {
- pa_log_error("%s : Default sink name (%s) does not exist in name register.", __FILE__, c->default_sink_name);
- retval = !!conf->fail;
- }
#ifdef HAVE_FORK
- if (conf->daemonize)
- pa_loop_write(daemon_pipe[1], &retval, sizeof(retval), NULL);
+ if (conf->daemonize) {
+ int ok = 0;
+ pa_loop_write(daemon_pipe[1], &ok, sizeof(ok), NULL);
+ }
#endif
- if (!retval) {
- pa_log_info("Daemon startup complete.");
- if (pa_mainloop_run(mainloop, &retval) < 0)
- retval = 1;
- pa_log_info("Daemon shutdown initiated.");
- }
- }
+ pa_log_info("Daemon startup complete.");
+
+ retval = 0;
+ if (pa_mainloop_run(mainloop, &retval) < 0)
+ goto finish;
+
+ pa_log_info("Daemon shutdown initiated.");
+
+finish:
#ifdef OS_IS_WIN32
- pa_mainloop_get_api(mainloop)->time_free(timer);
+ if (win32_timer)
+ pa_mainloop_get_api(mainloop)->time_free(win32_timer);
#endif
- pa_core_unref(c);
+ if (c) {
+ pa_core_unref(c);
+ pa_log_info("Daemon terminated.");
+ }
if (!conf->no_cpu_limit)
pa_cpu_limit_done();
pa_signal_done();
- pa_log_info("Daemon terminated.");
-
-finish:
+#ifdef HAVE_FORK
+ pa_close_pipe(daemon_pipe);
+#endif
if (mainloop)
pa_mainloop_free(mainloop);
@@ -837,8 +849,6 @@ finish:
if (valid_pid_file)
pa_pid_file_remove();
- pa_close_pipe(daemon_pipe);
-
#ifdef OS_IS_WIN32
WSACleanup();
#endif
diff --git a/src/modules/alsa-util.c b/src/modules/alsa-util.c
index 0c4c020b..47ed9ace 100644
--- a/src/modules/alsa-util.c
+++ b/src/modules/alsa-util.c
@@ -671,8 +671,24 @@ snd_pcm_t *pa_alsa_open_by_device_string(
*dev = d;
if (ss->channels != map->channels) {
- pa_assert_se(pa_channel_map_init_auto(map, ss->channels, PA_CHANNEL_MAP_AUX));
- pa_channel_map_init_auto(map, ss->channels, PA_CHANNEL_MAP_ALSA);
+ if (!pa_channel_map_init_auto(map, ss->channels, PA_CHANNEL_MAP_ALSA)) {
+ unsigned c;
+ pa_channel_position_t pos;
+
+ pa_log_warn("Device has an unknown channel mapping. This is a limitation of ALSA. Synthesizing channel map.");
+
+ for (c = ss->channels; c > 0; c--)
+ if (pa_channel_map_init_auto(map, c, PA_CHANNEL_MAP_ALSA))
+ break;
+
+ pa_assert(c > 0);
+
+ pos = PA_CHANNEL_POSITION_AUX0;
+ for (; c < map->channels; c ++)
+ map->map[c] = pos++;
+
+ map->channels = ss->channels;
+ }
}
return pcm_handle;
diff --git a/src/modules/module-alsa-sink.c b/src/modules/module-alsa-sink.c
index 5cc27f11..efb0fd8a 100644
--- a/src/modules/module-alsa-sink.c
+++ b/src/modules/module-alsa-sink.c
@@ -665,7 +665,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
switch ((pa_sink_state_t) PA_PTR_TO_UINT(data)) {
case PA_SINK_SUSPENDED:
- pa_assert(PA_SINK_OPENED(u->sink->thread_info.state));
+ pa_assert(PA_SINK_IS_OPENED(u->sink->thread_info.state));
if (suspend(u) < 0)
return -1;
@@ -836,9 +836,20 @@ static int sink_set_mute_cb(pa_sink *s) {
static void sink_update_requested_latency_cb(pa_sink *s) {
struct userdata *u = s->userdata;
+ snd_pcm_sframes_t before;
pa_assert(u);
+ before = u->hwbuf_unused_frames;
update_sw_params(u);
+
+ /* Let's check whether we now use only a smaller part of the
+ buffer then before. If so, we need to make sure that subsequent
+ rewinds are relative to the new maxium fill level and not to the
+ current fill level. Thus, let's do a full rewind once, to clear
+ things up. */
+
+ if (u->hwbuf_unused_frames > before)
+ pa_sink_request_rewind(s, 0);
}
static int process_rewind(struct userdata *u) {
@@ -846,6 +857,7 @@ static int process_rewind(struct userdata *u) {
size_t rewind_nbytes, unused_nbytes, limit_nbytes;
pa_assert(u);
+ /* Figure out how much we shall rewind and reset the counter */
rewind_nbytes = u->sink->thread_info.rewind_nbytes;
u->sink->thread_info.rewind_nbytes = 0;
@@ -917,7 +929,7 @@ static void thread_func(void *userdata) {
/* pa_log_debug("loop"); */
/* Render some data and write it to the dsp */
- if (PA_SINK_OPENED(u->sink->thread_info.state)) {
+ if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) {
int work_done = 0;
if (u->sink->thread_info.rewind_nbytes > 0)
@@ -982,7 +994,7 @@ static void thread_func(void *userdata) {
goto finish;
/* Tell ALSA about this and process its response */
- if (PA_SINK_OPENED(u->sink->thread_info.state)) {
+ if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) {
struct pollfd *pollfd;
unsigned short revents = 0;
int err;
diff --git a/src/modules/module-alsa-source.c b/src/modules/module-alsa-source.c
index 3c6b8dba..9eb6f06b 100644
--- a/src/modules/module-alsa-source.c
+++ b/src/modules/module-alsa-source.c
@@ -621,7 +621,7 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off
switch ((pa_source_state_t) PA_PTR_TO_UINT(data)) {
case PA_SOURCE_SUSPENDED:
- pa_assert(PA_SOURCE_OPENED(u->source->thread_info.state));
+ pa_assert(PA_SOURCE_IS_OPENED(u->source->thread_info.state));
if (suspend(u) < 0)
return -1;
@@ -819,7 +819,7 @@ static void thread_func(void *userdata) {
pa_log_debug("loop");
/* Read some data and pass it to the sources */
- if (PA_SOURCE_OPENED(u->source->thread_info.state)) {
+ if (PA_SOURCE_IS_OPENED(u->source->thread_info.state)) {
int work_done = 0;
if (u->use_mmap)
@@ -867,7 +867,7 @@ static void thread_func(void *userdata) {
goto finish;
/* Tell ALSA about this and process its response */
- if (PA_SOURCE_OPENED(u->source->thread_info.state)) {
+ if (PA_SOURCE_IS_OPENED(u->source->thread_info.state)) {
struct pollfd *pollfd;
unsigned short revents = 0;
int err;
diff --git a/src/modules/module-combine.c b/src/modules/module-combine.c
index e15654fa..2409ef8f 100644
--- a/src/modules/module-combine.c
+++ b/src/modules/module-combine.c
@@ -162,13 +162,13 @@ static void adjust_rates(struct userdata *u) {
if (!u->master)
return;
- if (!PA_SINK_OPENED(pa_sink_get_state(u->sink)))
+ if (!PA_SINK_IS_OPENED(pa_sink_get_state(u->sink)))
return;
for (o = pa_idxset_first(u->outputs, &idx); o; o = pa_idxset_next(u->outputs, &idx)) {
pa_usec_t sink_latency;
- if (!o->sink_input || !PA_SINK_OPENED(pa_sink_get_state(o->sink)))
+ if (!o->sink_input || !PA_SINK_IS_OPENED(pa_sink_get_state(o->sink)))
continue;
sink_latency = pa_sink_get_latency(o->sink);
@@ -194,7 +194,7 @@ static void adjust_rates(struct userdata *u) {
for (o = pa_idxset_first(u->outputs, &idx); o; o = pa_idxset_next(u->outputs, &idx)) {
uint32_t r = base_rate;
- if (!o->sink_input || !PA_SINK_OPENED(pa_sink_get_state(o->sink)))
+ if (!o->sink_input || !PA_SINK_IS_OPENED(pa_sink_get_state(o->sink)))
continue;
if (o->total_latency < target_latency)
@@ -258,7 +258,10 @@ static void thread_func(void *userdata) {
pa_rtclock_get(&now);
if (!u->thread_info.in_null_mode || pa_timeval_cmp(&u->thread_info.timestamp, &now) <= 0) {
- pa_sink_skip(u->sink, u->block_size);
+ pa_memchunk chunk;
+
+ pa_sink_render_full(u->sink, u->block_size, &chunk);
+ pa_memblock_unref(chunk.memblock);
if (!u->thread_info.in_null_mode)
u->thread_info.timestamp = now;
@@ -432,7 +435,7 @@ static int sink_input_process_msg(pa_msgobject *obj, int code, void *data, int64
case SINK_INPUT_MESSAGE_POST:
- if (PA_SINK_OPENED(o->sink_input->sink->thread_info.state))
+ if (PA_SINK_IS_OPENED(o->sink_input->sink->thread_info.state))
pa_memblockq_push_align(o->memblockq, chunk);
else
pa_memblockq_flush(o->memblockq);
@@ -471,7 +474,7 @@ static void enable_output(struct output *o) {
pa_sink_input_put(o->sink_input);
- if (o->userdata->sink && PA_SINK_LINKED(pa_sink_get_state(o->userdata->sink)))
+ if (o->userdata->sink && PA_SINK_IS_LINKED(pa_sink_get_state(o->userdata->sink)))
pa_asyncmsgq_send(o->userdata->sink->asyncmsgq, PA_MSGOBJECT(o->userdata->sink), SINK_MESSAGE_ADD_OUTPUT, o, 0, NULL);
}
}
@@ -504,7 +507,7 @@ static void unsuspend(struct userdata *u) {
pa_sink_suspend(o->sink, FALSE);
- if (PA_SINK_OPENED(pa_sink_get_state(o->sink)))
+ if (PA_SINK_IS_OPENED(pa_sink_get_state(o->sink)))
enable_output(o);
}
@@ -525,7 +528,7 @@ static int sink_set_state(pa_sink *sink, pa_sink_state_t state) {
switch (state) {
case PA_SINK_SUSPENDED:
- pa_assert(PA_SINK_OPENED(pa_sink_get_state(u->sink)));
+ pa_assert(PA_SINK_IS_OPENED(pa_sink_get_state(u->sink)));
suspend(u);
break;
@@ -697,7 +700,7 @@ static void pick_master(struct userdata *u, struct output *except) {
if (u->master &&
u->master != except &&
u->master->sink_input &&
- PA_SINK_OPENED(pa_sink_get_state(u->master->sink))) {
+ PA_SINK_IS_OPENED(pa_sink_get_state(u->master->sink))) {
update_master(u, u->master);
return;
}
@@ -705,7 +708,7 @@ static void pick_master(struct userdata *u, struct output *except) {
for (o = pa_idxset_first(u->outputs, &idx); o; o = pa_idxset_next(u->outputs, &idx))
if (o != except &&
o->sink_input &&
- PA_SINK_OPENED(pa_sink_get_state(o->sink))) {
+ PA_SINK_IS_OPENED(pa_sink_get_state(o->sink))) {
update_master(u, o);
return;
}
@@ -780,7 +783,7 @@ static struct output *output_new(struct userdata *u, pa_sink *sink) {
pa_assert_se(pa_idxset_put(u->outputs, o, NULL) == 0);
- if (u->sink && PA_SINK_LINKED(pa_sink_get_state(u->sink)))
+ if (u->sink && PA_SINK_IS_LINKED(pa_sink_get_state(u->sink)))
pa_asyncmsgq_send(u->sink->asyncmsgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_ADD_OUTPUT, o, 0, NULL);
else {
/* If the sink is not yet started, we need to do the activation ourselves */
@@ -792,10 +795,10 @@ static struct output *output_new(struct userdata *u, pa_sink *sink) {
o->outq);
}
- if (PA_SINK_OPENED(pa_sink_get_state(u->sink)) || pa_sink_get_state(u->sink) == PA_SINK_INIT) {
+ if (PA_SINK_IS_OPENED(pa_sink_get_state(u->sink)) || pa_sink_get_state(u->sink) == PA_SINK_INIT) {
pa_sink_suspend(sink, FALSE);
- if (PA_SINK_OPENED(pa_sink_get_state(sink)))
+ if (PA_SINK_IS_OPENED(pa_sink_get_state(sink)))
if (output_create_sink_input(o) < 0)
goto fail;
}
@@ -898,7 +901,7 @@ static pa_hook_result_t sink_state_changed_hook_cb(pa_core *c, pa_sink *s, struc
state = pa_sink_get_state(s);
- if (PA_SINK_OPENED(state) && PA_SINK_OPENED(pa_sink_get_state(u->sink)) && !o->sink_input) {
+ if (PA_SINK_IS_OPENED(state) && PA_SINK_IS_OPENED(pa_sink_get_state(u->sink)) && !o->sink_input) {
enable_output(o);
pick_master(u, NULL);
}
diff --git a/src/modules/module-default-device-restore.c b/src/modules/module-default-device-restore.c
index b550ae78..a7fc3a3f 100644
--- a/src/modules/module-default-device-restore.c
+++ b/src/modules/module-default-device-restore.c
@@ -3,7 +3,7 @@
/***
This file is part of PulseAudio.
- Copyright 2006 Lennart Poettering
+ Copyright 2006-2008 Lennart Poettering
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
@@ -25,10 +25,16 @@
#include <config.h>
#endif
+#include <errno.h>
+#include <stdio.h>
+
+#include <pulse/timeval.h>
+
#include <pulsecore/core-util.h>
#include <pulsecore/module.h>
#include <pulsecore/log.h>
#include <pulsecore/namereg.h>
+#include <pulsecore/core-error.h>
#include "module-default-device-restore-symdef.h"
@@ -39,15 +45,24 @@ PA_MODULE_LOAD_ONCE(TRUE);
#define DEFAULT_SINK_FILE "default-sink"
#define DEFAULT_SOURCE_FILE "default-source"
+#define DEFAULT_SAVE_INTERVAL 5
-int pa__init(pa_module *m) {
+struct userdata {
+ pa_core *core;
+ pa_subscription *subscription;
+ pa_time_event *time_event;
+ char *sink_filename, *source_filename;
+ pa_bool_t modified;
+};
+
+static void load(struct userdata *u) {
FILE *f;
/* We never overwrite manually configured settings */
- if (m->core->default_sink_name)
+ if (u->core->default_sink_name)
pa_log_info("Manually configured default sink, not overwriting.");
- else if ((f = pa_open_config_file(NULL, DEFAULT_SINK_FILE, NULL, NULL, "r"))) {
+ else if ((f = fopen(u->sink_filename, "r"))) {
char ln[256] = "";
fgets(ln, sizeof(ln)-1, f);
@@ -55,17 +70,19 @@ int pa__init(pa_module *m) {
fclose(f);
if (!ln[0])
- pa_log_debug("No previous default sink setting, ignoring.");
- else if (pa_namereg_get(m->core, ln, PA_NAMEREG_SINK, 1)) {
- pa_namereg_set_default(m->core, ln, PA_NAMEREG_SINK);
- pa_log_debug("Restored default sink '%s'.", ln);
+ pa_log_info("No previous default sink setting, ignoring.");
+ else if (pa_namereg_get(u->core, ln, PA_NAMEREG_SINK, TRUE)) {
+ pa_namereg_set_default(u->core, ln, PA_NAMEREG_SINK);
+ pa_log_info("Restored default sink '%s'.", ln);
} else
pa_log_info("Saved default sink '%s' not existant, not restoring default sink setting.", ln);
- }
- if (m->core->default_source_name)
+ } else if (errno != ENOENT)
+ pa_log("Failed to load default sink: %s", pa_cstrerror(errno));
+
+ if (u->core->default_source_name)
pa_log_info("Manually configured default source, not overwriting.");
- else if ((f = pa_open_config_file(NULL, DEFAULT_SOURCE_FILE, NULL, NULL, "r"))) {
+ else if ((f = fopen(u->source_filename, "r"))) {
char ln[256] = "";
fgets(ln, sizeof(ln)-1, f);
@@ -73,29 +90,114 @@ int pa__init(pa_module *m) {
fclose(f);
if (!ln[0])
- pa_log_debug("No previous default source setting, ignoring.");
- else if (pa_namereg_get(m->core, ln, PA_NAMEREG_SOURCE, 1)) {
- pa_namereg_set_default(m->core, ln, PA_NAMEREG_SOURCE);
- pa_log_debug("Restored default source '%s'.", ln);
+ pa_log_info("No previous default source setting, ignoring.");
+ else if (pa_namereg_get(u->core, ln, PA_NAMEREG_SOURCE, TRUE)) {
+ pa_namereg_set_default(u->core, ln, PA_NAMEREG_SOURCE);
+ pa_log_info("Restored default source '%s'.", ln);
} else
pa_log_info("Saved default source '%s' not existant, not restoring default source setting.", ln);
- }
- return 0;
+ } else if (errno != ENOENT)
+ pa_log("Failed to load default sink: %s", pa_cstrerror(errno));
}
-void pa__done(pa_module*m) {
+static void save(struct userdata *u) {
FILE *f;
- if ((f = pa_open_config_file(NULL, DEFAULT_SINK_FILE, NULL, NULL, "w"))) {
- const char *n = pa_namereg_get_default_sink_name(m->core);
- fprintf(f, "%s\n", n ? n : "");
- fclose(f);
+ if (!u->modified)
+ return;
+
+ if (u->sink_filename) {
+ if ((f = fopen(u->sink_filename, "w"))) {
+ const char *n = pa_namereg_get_default_sink_name(u->core);
+ fprintf(f, "%s\n", pa_strempty(n));
+ fclose(f);
+ } else
+ pa_log("Failed to save default sink: %s", pa_cstrerror(errno));
}
- if ((f = pa_open_config_file(NULL, DEFAULT_SOURCE_FILE, NULL, NULL, "w"))) {
- const char *n = pa_namereg_get_default_source_name(m->core);
- fprintf(f, "%s\n", n ? n : "");
- fclose(f);
+ if (u->source_filename) {
+ if ((f = fopen(u->source_filename, "w"))) {
+ const char *n = pa_namereg_get_default_source_name(u->core);
+ fprintf(f, "%s\n", pa_strempty(n));
+ fclose(f);
+ } else
+ pa_log("Failed to save default source: %s", pa_cstrerror(errno));
+ }
+
+ u->modified = FALSE;
+}
+
+static void time_cb(pa_mainloop_api *a, pa_time_event *e, const struct timeval *tv, void *userdata) {
+ struct userdata *u = userdata;
+
+ pa_assert(u);
+ save(u);
+
+ if (u->time_event) {
+ u->core->mainloop->time_free(u->time_event);
+ u->time_event = NULL;
+ }
+}
+
+static void subscribe_cb(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
+ struct userdata *u = userdata;
+
+ pa_assert(u);
+
+ u->modified = TRUE;
+
+ if (!u->time_event) {
+ struct timeval tv;
+ pa_gettimeofday(&tv);
+ pa_timeval_add(&tv, DEFAULT_SAVE_INTERVAL*PA_USEC_PER_SEC);
+ u->time_event = u->core->mainloop->time_new(u->core->mainloop, &tv, time_cb, u);
}
}
+
+int pa__init(pa_module *m) {
+ struct userdata *u;
+
+ pa_assert(u);
+
+ u = pa_xnew0(struct userdata, 1);
+ u->core = m->core;
+
+ if (!(u->sink_filename = pa_runtime_path(DEFAULT_SINK_FILE)))
+ goto fail;
+
+ if (!(u->source_filename = pa_runtime_path(DEFAULT_SOURCE_FILE)))
+ goto fail;
+
+ load(u);
+
+ u->subscription = pa_subscription_new(u->core, PA_SUBSCRIPTION_MASK_SERVER, subscribe_cb, u);
+
+ return 0;
+
+fail:
+ pa__done(m);
+
+ return -1;
+}
+
+void pa__done(pa_module*m) {
+ struct userdata *u;
+
+ pa_assert(m);
+
+ if (!(u = m->userdata))
+ return;
+
+ save(u);
+
+ if (u->subscription)
+ pa_subscription_free(u->subscription);
+
+ if (u->time_event)
+ m->core->mainloop->time_free(u->time_event);
+
+ pa_xfree(u->sink_filename);
+ pa_xfree(u->source_filename);
+ pa_xfree(u);
+}
diff --git a/src/modules/module-device-restore.c b/src/modules/module-device-restore.c
index 27c69f31..0a41b84a 100644
--- a/src/modules/module-device-restore.c
+++ b/src/modules/module-device-restore.c
@@ -263,7 +263,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, *state_dir;
+ char *fname, *runtime_dir;
char hn[256];
pa_sink *sink;
pa_source *source;
@@ -290,11 +290,11 @@ int pa__init(pa_module*m) {
if (!pa_get_host_name(hn, sizeof(hn)))
goto fail;
- if (!(state_dir = pa_get_state_dir()))
+ if (!(runtime_dir = pa_get_runtime_dir()))
goto fail;
- fname = pa_sprintf_malloc("%s/device-volumes.%s.gdbm", state_dir, hn);
- pa_xfree(state_dir);
+ fname = pa_sprintf_malloc("%s/device-volumes.%s.gdbm", runtime_dir, hn);
+ pa_xfree(runtime_dir);
if (!(u->gdbm_file = gdbm_open(fname, 0, GDBM_WRCREAT, 0600, NULL))) {
pa_log("Failed to open volume database '%s': %s", fname, gdbm_strerror(gdbm_errno));
@@ -316,6 +316,7 @@ int pa__init(pa_module*m) {
fail:
pa__done(m);
+
if (ma)
pa_modargs_free(ma);
diff --git a/src/modules/module-esound-sink.c b/src/modules/module-esound-sink.c
index 9a4ba58e..2206e2bc 100644
--- a/src/modules/module-esound-sink.c
+++ b/src/modules/module-esound-sink.c
@@ -143,7 +143,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
switch ((pa_sink_state_t) PA_PTR_TO_UINT(data)) {
case PA_SINK_SUSPENDED:
- pa_assert(PA_SINK_OPENED(u->sink->thread_info.state));
+ pa_assert(PA_SINK_IS_OPENED(u->sink->thread_info.state));
pa_smoother_pause(u->smoother, pa_rtclock_usec());
break;
@@ -211,7 +211,7 @@ static void thread_func(void *userdata) {
pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
/* Render some data and write it to the fifo */
- if (PA_SINK_OPENED(u->sink->thread_info.state) && pollfd->revents) {
+ if (PA_SINK_IS_OPENED(u->sink->thread_info.state) && pollfd->revents) {
pa_usec_t usec;
int64_t n;
@@ -294,7 +294,7 @@ static void thread_func(void *userdata) {
}
/* Hmm, nothing to do. Let's sleep */
- pollfd->events = PA_SINK_OPENED(u->sink->thread_info.state) ? POLLOUT : 0;
+ pollfd->events = PA_SINK_IS_OPENED(u->sink->thread_info.state) ? POLLOUT : 0;
}
if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0)
diff --git a/src/modules/module-ladspa-sink.c b/src/modules/module-ladspa-sink.c
index aa398a28..6e0faac7 100644
--- a/src/modules/module-ladspa-sink.c
+++ b/src/modules/module-ladspa-sink.c
@@ -102,10 +102,14 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
case PA_SINK_MESSAGE_GET_LATENCY: {
pa_usec_t usec = 0;
+ /* Get the latency of the master sink */
if (PA_MSGOBJECT(u->master)->process_msg(PA_MSGOBJECT(u->master), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0)
usec = 0;
- *((pa_usec_t*) data) = usec /* + pa_bytes_to_usec(u->memchunk.length, &u->sink->sample_spec) */;
+ /* Add the latency internal to our sink input on top */
+ usec += pa_bytes_to_usec(pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq), &u->master->sample_spec);
+
+ *((pa_usec_t*) data) = usec;
return 0;
}
}
@@ -120,7 +124,10 @@ static int sink_set_state(pa_sink *s, pa_sink_state_t state) {
pa_sink_assert_ref(s);
pa_assert_se(u = s->userdata);
- if (PA_SINK_LINKED(state) && u->sink_input && PA_SINK_INPUT_LINKED(pa_sink_input_get_state(u->sink_input)))
+ if (PA_SINK_IS_LINKED(state) &&
+ u->sink_input &&
+ PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(u->sink_input)))
+
pa_sink_input_cork(u->sink_input, state == PA_SINK_SUSPENDED);
return 0;
@@ -134,7 +141,7 @@ static void sink_request_rewind(pa_sink *s) {
pa_assert_se(u = s->userdata);
/* Just hand this one over to the master sink */
- pa_sink_input_request_rewind(u->sink_input, s->thread_info.rewind_nbytes, FALSE);
+ pa_sink_input_request_rewind(u->sink_input, s->thread_info.rewind_nbytes, FALSE, FALSE);
}
/* Called from I/O thread context */
@@ -145,24 +152,9 @@ static void sink_update_requested_latency(pa_sink *s) {
pa_assert_se(u = s->userdata);
/* Just hand this one over to the master sink */
- u->sink_input->thread_info.requested_sink_latency = pa_sink_get_requested_latency_within_thread(s);
- pa_sink_invalidate_requested_latency(u->master);
-}
-
-/* Called from I/O thread context */
-static int sink_input_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
- struct userdata *u = PA_SINK_INPUT(o)->userdata;
-
- switch (code) {
- case PA_SINK_INPUT_MESSAGE_GET_LATENCY:
- *((pa_usec_t*) data) = 0 /*pa_bytes_to_usec(u->memchunk.length, &u->sink_input->sample_spec)*/;
-
- /* Fall through, the default handler will add in the extra
- * latency added by the resampler */
- break;
- }
-
- return pa_sink_input_process_msg(o, code, data, offset, chunk);
+ pa_sink_input_set_requested_latency_within_thread(
+ u->sink_input,
+ pa_sink_get_requested_latency_within_thread(s));
}
/* Called from I/O thread context */
@@ -192,20 +184,9 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk
dst = (float*) pa_memblock_acquire(chunk->memblock);
for (c = 0; c < u->channels; c++) {
- unsigned j;
- float *p, *q;
-
- p = src + c;
- q = u->input;
- for (j = 0; j < n; j++, p += u->channels, q++)
- *q = PA_CLAMP_UNLIKELY(*p, -1.0, 1.0);
-
+ pa_sample_clamp(PA_SAMPLE_FLOAT32NE, u->input, sizeof(float), src+c, u->channels*sizeof(float), n);
u->descriptor->run(u->handle[c], n);
-
- q = u->output;
- p = dst + c;
- for (j = 0; j < n; j++, q++, p += u->channels)
- *p = PA_CLAMP_UNLIKELY(*q, -1.0, 1.0);
+ pa_sample_clamp(PA_SAMPLE_FLOAT32NE, dst+c, u->channels*sizeof(float), u->output, sizeof(float), n);
}
pa_memblock_release(tchunk.memblock);
@@ -245,6 +226,9 @@ static void sink_input_detach_cb(pa_sink_input *i) {
pa_assert_se(u = i->userdata);
pa_sink_detach_within_thread(u->sink);
+
+ pa_sink_set_asyncmsgq(u->sink, NULL);
+ pa_sink_set_rtpoll(u->sink, NULL);
}
/* Called from I/O thread context */
@@ -648,7 +632,6 @@ int pa__init(pa_module*m) {
if (!u->sink_input)
goto fail;
- u->sink_input->parent.process_msg = sink_input_process_msg;
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;
diff --git a/src/modules/module-match.c b/src/modules/module-match.c
index 0411dcdc..d0265455 100644
--- a/src/modules/module-match.c
+++ b/src/modules/module-match.c
@@ -82,12 +82,14 @@ static int load_rules(struct userdata *u, const char *filename) {
pa_assert(u);
- f = filename ?
- fopen(fn = pa_xstrdup(filename), "r") :
- pa_open_config_file(DEFAULT_MATCH_TABLE_FILE, DEFAULT_MATCH_TABLE_FILE_USER, NULL, &fn, "r");
+ if (filename)
+ f = fopen(fn = pa_xstrdup(filename), "r");
+ else
+ f = pa_open_config_file(DEFAULT_MATCH_TABLE_FILE, DEFAULT_MATCH_TABLE_FILE_USER, NULL, &fn);
if (!f) {
- pa_log("failed to open file '%s': %s", fn, pa_cstrerror(errno));
+ pa_xfree(fn);
+ pa_log("Failed to open file config file: %s", pa_cstrerror(errno));
goto finish;
}
diff --git a/src/modules/module-null-sink.c b/src/modules/module-null-sink.c
index 2301f088..606b87d0 100644
--- a/src/modules/module-null-sink.c
+++ b/src/modules/module-null-sink.c
@@ -3,7 +3,7 @@
/***
This file is part of PulseAudio.
- Copyright 2004-2006 Lennart Poettering
+ Copyright 2004-2008 Lennart Poettering
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
@@ -64,6 +64,7 @@ PA_MODULE_USAGE(
"description=<description for the sink>");
#define DEFAULT_SINK_NAME "null"
+#define MAX_LATENCY_USEC (PA_USEC_PER_SEC * 2)
struct userdata {
pa_core *core;
@@ -76,7 +77,8 @@ struct userdata {
size_t block_size;
- struct timeval timestamp;
+ pa_usec_t block_usec;
+ pa_usec_t timestamp;
};
static const char* const valid_modargs[] = {
@@ -96,26 +98,93 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
case PA_SINK_MESSAGE_SET_STATE:
if (PA_PTR_TO_UINT(data) == PA_SINK_RUNNING)
- pa_rtclock_get(&u->timestamp);
+ u->timestamp = pa_rtclock_usec();
break;
case PA_SINK_MESSAGE_GET_LATENCY: {
- struct timeval now;
+ pa_usec_t now;
- pa_rtclock_get(&now);
+ now = pa_rtclock_usec();
+ *((pa_usec_t*) data) = u->timestamp > now ? u->timestamp - now : 0;
- if (pa_timeval_cmp(&u->timestamp, &now) > 0)
- *((pa_usec_t*) data) = 0;
- else
- *((pa_usec_t*) data) = pa_timeval_diff(&u->timestamp, &now);
- break;
+ return 0;
}
}
return pa_sink_process_msg(o, code, data, offset, chunk);
}
+static void sink_update_requested_latency_cb(pa_sink *s) {
+ struct userdata *u;
+
+ pa_sink_assert_ref(s);
+ u = s->userdata;
+ pa_assert(u);
+
+ u->block_usec = pa_sink_get_requested_latency_within_thread(s);
+}
+
+static void process_rewind(struct userdata *u, pa_usec_t now) {
+ size_t rewind_nbytes, in_buffer;
+ pa_usec_t delay;
+
+ pa_assert(u);
+
+ /* Figure out how much we shall rewind and reset the counter */
+ rewind_nbytes = u->sink->thread_info.rewind_nbytes;
+ u->sink->thread_info.rewind_nbytes = 0;
+
+ pa_assert(rewind_nbytes > 0);
+ pa_log_debug("Requested to rewind %lu bytes.", (unsigned long) rewind_nbytes);
+
+ if (u->timestamp <= now)
+ return;
+
+ delay = u->timestamp - now;
+ in_buffer = pa_usec_to_bytes(delay, &u->sink->sample_spec);
+
+ if (in_buffer <= 0)
+ return;
+
+ if (rewind_nbytes > in_buffer)
+ rewind_nbytes = in_buffer;
+
+ pa_sink_process_rewind(u->sink, rewind_nbytes);
+ u->timestamp -= pa_bytes_to_usec(rewind_nbytes, &u->sink->sample_spec);
+
+ pa_log_debug("Rewound %lu bytes.", (unsigned long) rewind_nbytes);
+}
+
+static void process_render(struct userdata *u, pa_usec_t now) {
+ size_t nbytes;
+ size_t ate = 0;
+
+ /* This is the configured latency. Sink inputs connected to us
+ might not have a single frame more than this value queued. Hence:
+ at maximum read this many bytes from the sink inputs. */
+
+ nbytes = pa_usec_to_bytes(u->block_usec, &u->sink->sample_spec);
+
+ /* Fill the buffer up the the latency size */
+ while (u->timestamp < now + u->block_usec) {
+ pa_memchunk chunk;
+
+ pa_sink_render(u->sink, nbytes, &chunk);
+ pa_memblock_unref(chunk.memblock);
+
+ pa_log_debug("Ate %lu bytes.", (unsigned long) chunk.length);
+ u->timestamp += pa_bytes_to_usec(chunk.length, &u->sink->sample_spec);
+
+ ate += chunk.length;
+
+ if (ate >= nbytes)
+ break;
+ }
+
+ pa_log_debug("Ate in sum %lu bytes (of %lu)", (unsigned long) ate, (unsigned long) nbytes);
+}
+
static void thread_func(void *userdata) {
struct userdata *u = userdata;
@@ -126,23 +195,24 @@ static void thread_func(void *userdata) {
pa_thread_mq_install(&u->thread_mq);
pa_rtpoll_install(u->rtpoll);
- pa_rtclock_get(&u->timestamp);
+ u->timestamp = pa_rtclock_usec();
for (;;) {
int ret;
/* Render some data and drop it immediately */
if (u->sink->thread_info.state == PA_SINK_RUNNING) {
- struct timeval now;
+ pa_usec_t now;
- pa_rtclock_get(&now);
+ now = pa_rtclock_usec();
- if (pa_timeval_cmp(&u->timestamp, &now) <= 0) {
- pa_sink_skip(u->sink, u->block_size);
- pa_timeval_add(&u->timestamp, pa_bytes_to_usec(u->block_size, &u->sink->sample_spec));
- }
+ if (u->sink->thread_info.rewind_nbytes > 0)
+ process_rewind(u, now);
- pa_rtpoll_set_timer_absolute(u->rtpoll, &u->timestamp);
+ if (u->timestamp <= now)
+ process_render(u, now);
+
+ pa_rtpoll_set_timer_absolute(u->rtpoll, u->timestamp);
} else
pa_rtpoll_set_timer_disabled(u->rtpoll);
@@ -197,26 +267,26 @@ int pa__init(pa_module*m) {
pa_sink_new_data_set_name(&data, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME));
pa_sink_new_data_set_sample_spec(&data, &ss);
pa_sink_new_data_set_channel_map(&data, &map);
- pa_proplist_sets(data.proplist, PA_PROP_DEVICE_DESCRIPTION, pa_modargs_get_value(ma, "description", "NULL sink"));
+ pa_proplist_sets(data.proplist, PA_PROP_DEVICE_DESCRIPTION, pa_modargs_get_value(ma, "description", "Null Output"));
- u->sink = pa_sink_new(m->core, &data, 0);
+ u->sink = pa_sink_new(m->core, &data, PA_SINK_LATENCY);
pa_sink_new_data_done(&data);
if (!u->sink) {
- pa_log("Failed to create sink.");
+ pa_log("Failed to create sink object.");
goto fail;
}
u->sink->parent.process_msg = sink_process_msg;
+ u->sink->update_requested_latency = sink_update_requested_latency_cb;
u->sink->userdata = u;
- u->sink->flags = PA_SINK_LATENCY;
pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
pa_sink_set_rtpoll(u->sink, u->rtpoll);
- u->block_size = pa_bytes_per_second(&ss) / 20; /* 50 ms */
- if (u->block_size <= 0)
- u->block_size = pa_frame_size(&ss);
+ u->block_usec = u->sink->max_latency = MAX_LATENCY_USEC;
+
+ u->sink->thread_info.max_rewind = pa_usec_to_bytes(u->block_usec, &u->sink->sample_spec);
if (!(u->thread = pa_thread_new(thread_func, u))) {
pa_log("Failed to create thread.");
diff --git a/src/modules/module-oss.c b/src/modules/module-oss.c
index f07d82a0..3dd4508e 100644
--- a/src/modules/module-oss.c
+++ b/src/modules/module-oss.c
@@ -161,10 +161,10 @@ static void trigger(struct userdata *u, pa_bool_t quick) {
pa_log_debug("trigger");
- if (u->source && PA_SOURCE_OPENED(u->source->thread_info.state))
+ if (u->source && PA_SOURCE_IS_OPENED(u->source->thread_info.state))
enable_bits |= PCM_ENABLE_INPUT;
- if (u->sink && PA_SINK_OPENED(u->sink->thread_info.state))
+ if (u->sink && PA_SINK_IS_OPENED(u->sink->thread_info.state))
enable_bits |= PCM_ENABLE_OUTPUT;
pa_log_debug("trigger: %i", enable_bits);
@@ -202,7 +202,7 @@ static void trigger(struct userdata *u, pa_bool_t quick) {
* register the fd as ready.
*/
- if (u->source && PA_SOURCE_OPENED(u->source->thread_info.state)) {
+ if (u->source && PA_SOURCE_IS_OPENED(u->source->thread_info.state)) {
uint8_t *buf = pa_xnew(uint8_t, u->in_fragment_size);
pa_read(u->fd, buf, u->in_fragment_size, NULL);
pa_xfree(buf);
@@ -641,7 +641,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
switch ((pa_sink_state_t) PA_PTR_TO_UINT(data)) {
case PA_SINK_SUSPENDED:
- pa_assert(PA_SINK_OPENED(u->sink->thread_info.state));
+ pa_assert(PA_SINK_IS_OPENED(u->sink->thread_info.state));
if (!u->source || u->source_suspended) {
if (suspend(u) < 0)
@@ -658,7 +658,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
if (u->sink->thread_info.state == PA_SINK_INIT) {
do_trigger = TRUE;
- quick = u->source && PA_SOURCE_OPENED(u->source->thread_info.state);
+ quick = u->source && PA_SOURCE_IS_OPENED(u->source->thread_info.state);
}
if (u->sink->thread_info.state == PA_SINK_SUSPENDED) {
@@ -721,7 +721,7 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off
switch ((pa_source_state_t) PA_PTR_TO_UINT(data)) {
case PA_SOURCE_SUSPENDED:
- pa_assert(PA_SOURCE_OPENED(u->source->thread_info.state));
+ pa_assert(PA_SOURCE_IS_OPENED(u->source->thread_info.state));
if (!u->sink || u->sink_suspended) {
if (suspend(u) < 0)
@@ -738,7 +738,7 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off
if (u->source->thread_info.state == PA_SOURCE_INIT) {
do_trigger = TRUE;
- quick = u->sink && PA_SINK_OPENED(u->sink->thread_info.state);
+ quick = u->sink && PA_SINK_IS_OPENED(u->sink->thread_info.state);
}
if (u->source->thread_info.state == PA_SOURCE_SUSPENDED) {
@@ -877,7 +877,7 @@ static void thread_func(void *userdata) {
/* Render some data and write it to the dsp */
- if (u->sink && PA_SINK_OPENED(u->sink->thread_info.state) && ((revents & POLLOUT) || u->use_mmap || u->use_getospace)) {
+ if (u->sink && PA_SINK_IS_OPENED(u->sink->thread_info.state) && ((revents & POLLOUT) || u->use_mmap || u->use_getospace)) {
if (u->use_mmap) {
@@ -985,7 +985,7 @@ static void thread_func(void *userdata) {
/* Try to read some data and pass it on to the source driver. */
- if (u->source && PA_SOURCE_OPENED(u->source->thread_info.state) && ((revents & POLLIN) || u->use_mmap || u->use_getispace)) {
+ if (u->source && PA_SOURCE_IS_OPENED(u->source->thread_info.state) && ((revents & POLLIN) || u->use_mmap || u->use_getispace)) {
if (u->use_mmap) {
@@ -1095,8 +1095,8 @@ static void thread_func(void *userdata) {
pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
pollfd->events =
- ((u->source && PA_SOURCE_OPENED(u->source->thread_info.state)) ? POLLIN : 0) |
- ((u->sink && PA_SINK_OPENED(u->sink->thread_info.state)) ? POLLOUT : 0);
+ ((u->source && PA_SOURCE_IS_OPENED(u->source->thread_info.state)) ? POLLIN : 0) |
+ ((u->sink && PA_SINK_IS_OPENED(u->sink->thread_info.state)) ? POLLOUT : 0);
}
/* Hmm, nothing to do. Let's sleep */
diff --git a/src/modules/module-protocol-stub.c b/src/modules/module-protocol-stub.c
index 600201b4..8bcc19b1 100644
--- a/src/modules/module-protocol-stub.c
+++ b/src/modules/module-protocol-stub.c
@@ -215,15 +215,6 @@ int pa__init(pa_module*m) {
#else
pa_socket_server *s;
int r;
- char tmp[PATH_MAX];
-
-#if defined(USE_PROTOCOL_ESOUND)
-#if defined(USE_PER_USER_ESOUND_SOCKET)
- char esdsocketpath[PATH_MAX];
-#else
- const char esdsocketpath[] = "/tmp/.esd/socket";
-#endif
-#endif
#endif
pa_assert(m);
@@ -255,27 +246,28 @@ int pa__init(pa_module*m) {
goto fail;
if (s_ipv4)
- if (!(u->protocol_ipv4 = protocol_new(m->core, s_ipv4, m, ma)))
- pa_socket_server_unref(s_ipv4);
-
+ u->protocol_ipv4 = protocol_new(m->core, s_ipv4, m, ma);
if (s_ipv6)
- if (!(u->protocol_ipv6 = protocol_new(m->core, s_ipv6, m, ma)))
- pa_socket_server_unref(s_ipv6);
+ u->protocol_ipv6 = protocol_new(m->core, s_ipv6, m, ma);
if (!u->protocol_ipv4 && !u->protocol_ipv6)
goto fail;
+ if (s_ipv6)
+ pa_socket_server_unref(s_ipv6);
+ if (s_ipv6)
+ pa_socket_server_unref(s_ipv4);
+
#else
#if defined(USE_PROTOCOL_ESOUND)
#if defined(USE_PER_USER_ESOUND_SOCKET)
- snprintf(esdsocketpath, sizeof(esdsocketpath), "/tmp/.esd-%lu/socket", (unsigned long) getuid());
+ u->socket_path = pa_sprintf_malloc("/tmp/.esd-%lu/socket", (unsigned long) getuid());
+#else
+ u->socket_path = pa_xstrdup("/tmp/.esd/socket");
#endif
- pa_runtime_path(pa_modargs_get_value(ma, "socket", esdsocketpath), tmp, sizeof(tmp));
- u->socket_path = pa_xstrdup(tmp);
-
/* This socket doesn't reside in our own runtime dir but in
* /tmp/.esd/, hence we have to create the dir first */
@@ -285,24 +277,26 @@ int pa__init(pa_module*m) {
}
#else
- pa_runtime_path(pa_modargs_get_value(ma, "socket", UNIX_SOCKET), tmp, sizeof(tmp));
- u->socket_path = pa_xstrdup(tmp);
-#endif
-
- if ((r = pa_unix_socket_remove_stale(tmp)) < 0) {
- pa_log("Failed to remove stale UNIX socket '%s': %s", tmp, pa_cstrerror(errno));
+ if (!(u->socket_path = pa_runtime_path(pa_modargs_get_value(ma, "socket", UNIX_SOCKET)))) {
+ pa_log("Failed to generate socket path.");
goto fail;
}
+#endif
- if (r)
- pa_log("Removed stale UNIX socket '%s'.", tmp);
+ if ((r = pa_unix_socket_remove_stale(u->socket_path)) < 0) {
+ pa_log("Failed to remove stale UNIX socket '%s': %s", u->socket_path, pa_cstrerror(errno));
+ goto fail;
+ } else if (r > 0)
+ pa_log_info("Removed stale UNIX socket '%s'.", u->socket_path);
- if (!(s = pa_socket_server_new_unix(m->core->mainloop, tmp)))
+ if (!(s = pa_socket_server_new_unix(m->core->mainloop, u->socket_path)))
goto fail;
if (!(u->protocol_unix = protocol_new(m->core, s, m, ma)))
goto fail;
+ pa_socket_server_unref(s);
+
#endif
m->userdata = u;
@@ -325,23 +319,21 @@ fail:
#else
if (u->protocol_unix)
protocol_free(u->protocol_unix);
-
- if (u->socket_path)
- pa_xfree(u->socket_path);
+ pa_xfree(u->socket_path);
#endif
pa_xfree(u);
- } else {
+ }
+
#if defined(USE_TCP_SOCKETS)
- if (s_ipv4)
- pa_socket_server_unref(s_ipv4);
- if (s_ipv6)
- pa_socket_server_unref(s_ipv6);
+ if (s_ipv4)
+ pa_socket_server_unref(s_ipv4);
+ if (s_ipv6)
+ pa_socket_server_unref(s_ipv6);
#else
- if (s)
- pa_socket_server_unref(s);
+ if (s)
+ pa_socket_server_unref(s);
#endif
- }
goto finish;
}
@@ -362,7 +354,7 @@ void pa__done(pa_module*m) {
if (u->protocol_unix)
protocol_free(u->protocol_unix);
-#if defined(USE_PROTOCOL_ESOUND)
+#if defined(USE_PROTOCOL_ESOUND) && !defined(USE_PER_USER_ESOUND_SOCKET)
if (u->socket_path) {
char *p = pa_parent_dir(u->socket_path);
rmdir(p);
diff --git a/src/modules/module-remap-sink.c b/src/modules/module-remap-sink.c
index 6a16321d..f68b7191 100644
--- a/src/modules/module-remap-sink.c
+++ b/src/modules/module-remap-sink.c
@@ -81,10 +81,14 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
case PA_SINK_MESSAGE_GET_LATENCY: {
pa_usec_t usec = 0;
+ /* Get the latency of the master sink */
if (PA_MSGOBJECT(u->master)->process_msg(PA_MSGOBJECT(u->master), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0)
usec = 0;
- *((pa_usec_t*) data) = usec/* + pa_bytes_to_usec(u->memchunk.length, &u->sink->sample_spec)*/;
+ /* Add the latency internal to our sink input on top */
+ usec += pa_bytes_to_usec(pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq), &u->master->sample_spec);
+
+ *((pa_usec_t*) data) = usec;
return 0;
}
}
@@ -99,7 +103,10 @@ static int sink_set_state(pa_sink *s, pa_sink_state_t state) {
pa_sink_assert_ref(s);
pa_assert_se(u = s->userdata);
- if (PA_SINK_LINKED(state) && u->sink_input && PA_SINK_INPUT_LINKED(pa_sink_input_get_state(u->sink_input)))
+ if (PA_SINK_IS_LINKED(state) &&
+ u->sink_input &&
+ PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(u->sink_input)))
+
pa_sink_input_cork(u->sink_input, state == PA_SINK_SUSPENDED);
return 0;
@@ -112,7 +119,7 @@ static void sink_request_rewind(pa_sink *s) {
pa_sink_assert_ref(s);
pa_assert_se(u = s->userdata);
- pa_sink_input_request_rewind(u->sink_input, s->thread_info.rewind_nbytes, FALSE);
+ pa_sink_input_request_rewind(u->sink_input, s->thread_info.rewind_nbytes, FALSE, FALSE);
}
/* Called from I/O thread context */
@@ -123,24 +130,9 @@ static void sink_update_requested_latency(pa_sink *s) {
pa_assert_se(u = s->userdata);
/* Just hand this one over to the master sink */
- u->sink_input->thread_info.requested_sink_latency = pa_sink_get_requested_latency_within_thread(s);
- pa_sink_invalidate_requested_latency(u->master);
-}
-
-/* Called from I/O thread context */
-static int sink_input_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
- struct userdata *u = PA_SINK_INPUT(o)->userdata;
-
- switch (code) {
- case PA_SINK_INPUT_MESSAGE_GET_LATENCY:
- *((pa_usec_t*) data) = 0; /*pa_bytes_to_usec(u->memchunk.length, &u->sink_input->sample_spec);*/
-
- /* Fall through, the default handler will add in the extra
- * latency added by the resampler */
- break;
- }
-
- return pa_sink_input_process_msg(o, code, data, offset, chunk);
+ pa_sink_input_set_requested_latency_within_thread(
+ u->sink_input,
+ pa_sink_get_requested_latency_within_thread(s));
}
/* Called from I/O thread context */
@@ -152,7 +144,6 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk
pa_assert_se(u = i->userdata);
pa_sink_render(u->sink, nbytes, chunk);
-
return 0;
}
@@ -185,6 +176,9 @@ static void sink_input_detach_cb(pa_sink_input *i) {
pa_assert_se(u = i->userdata);
pa_sink_detach_within_thread(u->sink);
+
+ pa_sink_set_asyncmsgq(u->sink, NULL);
+ pa_sink_set_rtpoll(u->sink, NULL);
}
/* Called from I/O thread context */
@@ -317,7 +311,6 @@ int pa__init(pa_module*m) {
if (!u->sink_input)
goto fail;
- u->sink_input->parent.process_msg = sink_input_process_msg;
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;
diff --git a/src/modules/module-suspend-on-idle.c b/src/modules/module-suspend-on-idle.c
index ef8239d0..a3985974 100644
--- a/src/modules/module-suspend-on-idle.c
+++ b/src/modules/module-suspend-on-idle.c
@@ -317,7 +317,7 @@ static pa_hook_result_t device_state_changed_hook_cb(pa_core *c, pa_object *o, s
if (pa_sink_used_by(s) <= 0) {
- if (PA_SINK_OPENED(state))
+ if (PA_SINK_IS_OPENED(state))
restart(d);
}
@@ -328,7 +328,7 @@ static pa_hook_result_t device_state_changed_hook_cb(pa_core *c, pa_object *o, s
if (pa_source_used_by(s) <= 0) {
- if (PA_SOURCE_OPENED(state))
+ if (PA_SOURCE_IS_OPENED(state))
restart(d);
}
}
diff --git a/src/modules/module-tunnel.c b/src/modules/module-tunnel.c
index 74d5a826..e3ae5e1f 100644
--- a/src/modules/module-tunnel.c
+++ b/src/modules/module-tunnel.c
@@ -303,7 +303,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
/* First, change the state, because otherwide pa_sink_render() would fail */
if ((r = pa_sink_process_msg(o, code, data, offset, chunk)) >= 0)
- if (PA_SINK_OPENED((pa_sink_state_t) PA_PTR_TO_UINT(data)))
+ if (PA_SINK_IS_OPENED((pa_sink_state_t) PA_PTR_TO_UINT(data)))
send_data(u);
return r;
@@ -314,7 +314,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
pa_assert(offset > 0);
u->requested_bytes += (size_t) offset;
- if (PA_SINK_OPENED(u->sink->thread_info.state))
+ if (PA_SINK_IS_OPENED(u->sink->thread_info.state))
send_data(u);
return 0;
@@ -343,7 +343,7 @@ static int sink_set_state(pa_sink *s, pa_sink_state_t state) {
switch ((pa_sink_state_t) state) {
case PA_SINK_SUSPENDED:
- pa_assert(PA_SINK_OPENED(s->state));
+ pa_assert(PA_SINK_IS_OPENED(s->state));
stream_cork(u, TRUE);
break;
@@ -369,7 +369,7 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off
switch (code) {
case SOURCE_MESSAGE_POST:
- if (PA_SOURCE_OPENED(u->source->thread_info.state))
+ if (PA_SOURCE_IS_OPENED(u->source->thread_info.state))
pa_source_post(u->source, chunk);
return 0;
}
@@ -385,7 +385,7 @@ static int source_set_state(pa_source *s, pa_source_state_t state) {
switch ((pa_source_state_t) state) {
case PA_SOURCE_SUSPENDED:
- pa_assert(PA_SOURCE_OPENED(s->state));
+ pa_assert(PA_SOURCE_IS_OPENED(s->state));
stream_cork(u, TRUE);
break;
@@ -1066,7 +1066,7 @@ static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t
pa_tagstruct_putu32(reply, PA_INVALID_INDEX);
pa_tagstruct_puts(reply, u->sink_name);
pa_tagstruct_putu32(reply, u->maxlength);
- pa_tagstruct_put_boolean(reply, !PA_SINK_OPENED(pa_sink_get_state(u->sink)));
+ pa_tagstruct_put_boolean(reply, !PA_SINK_IS_OPENED(pa_sink_get_state(u->sink)));
pa_tagstruct_putu32(reply, u->tlength);
pa_tagstruct_putu32(reply, u->prebuf);
pa_tagstruct_putu32(reply, u->minreq);
@@ -1082,7 +1082,7 @@ static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t
pa_tagstruct_putu32(reply, PA_INVALID_INDEX);
pa_tagstruct_puts(reply, u->source_name);
pa_tagstruct_putu32(reply, u->maxlength);
- pa_tagstruct_put_boolean(reply, !PA_SOURCE_OPENED(pa_source_get_state(u->source)));
+ pa_tagstruct_put_boolean(reply, !PA_SOURCE_IS_OPENED(pa_source_get_state(u->source)));
pa_tagstruct_putu32(reply, u->fragsize);
#endif
diff --git a/src/modules/module-volume-restore.c b/src/modules/module-volume-restore.c
index 0dc8dcfd..336bcac9 100644
--- a/src/modules/module-volume-restore.c
+++ b/src/modules/module-volume-restore.c
@@ -134,16 +134,12 @@ static int load_rules(struct userdata *u) {
char buf_name[256], buf_volume[256], buf_sink[256], buf_source[256];
char *ln = buf_name;
- f = u->table_file ?
- fopen(u->table_file, "r") :
- pa_open_config_file(NULL, DEFAULT_VOLUME_TABLE_FILE, NULL, &u->table_file, "r");
-
- if (!f) {
+ if (!(f = fopen(u->table_file, "r"))) {
if (errno == ENOENT) {
- pa_log_info("starting with empty ruleset.");
+ pa_log_info("Starting with empty ruleset.");
ret = 0;
} else
- pa_log("failed to open file '%s': %s", u->table_file, pa_cstrerror(errno));
+ pa_log("Failed to open file '%s': %s", u->table_file, pa_cstrerror(errno));
goto finish;
}
@@ -236,11 +232,7 @@ static int save_rules(struct userdata *u) {
pa_log_info("Saving rules...");
- f = u->table_file ?
- fopen(u->table_file, "w") :
- pa_open_config_file(NULL, DEFAULT_VOLUME_TABLE_FILE, NULL, &u->table_file, "w");
-
- if (!f) {
+ if (!(f = fopen(u->table_file, "w"))) {
pa_log("Failed to open file '%s': %s", u->table_file, pa_cstrerror(errno));
goto finish;
}
@@ -496,7 +488,7 @@ int pa__init(pa_module*m) {
u = pa_xnew(struct userdata, 1);
u->core = m->core;
u->hashmap = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
- u->table_file = pa_xstrdup(pa_modargs_get_value(ma, "table", NULL));
+ u->table_file = pa_runtime_path(pa_modargs_get_value(ma, "table", DEFAULT_VOLUME_TABLE_FILE));
u->modified = FALSE;
u->subscription = NULL;
u->sink_input_new_hook_slot = u->sink_input_fixate_hook_slot = u->source_output_new_hook_slot = NULL;
diff --git a/src/pulse/client-conf.c b/src/pulse/client-conf.c
index c054f663..75f44182 100644
--- a/src/pulse/client-conf.c
+++ b/src/pulse/client-conf.c
@@ -112,13 +112,20 @@ int pa_client_conf_load(pa_client_conf *c, const char *filename) {
table[6].data = &c->cookie_file;
table[7].data = &c->disable_shm;
- f = filename ?
- fopen((fn = pa_xstrdup(filename)), "r") :
- pa_open_config_file(DEFAULT_CLIENT_CONFIG_FILE, DEFAULT_CLIENT_CONFIG_FILE_USER, ENV_CLIENT_CONFIG_FILE, &fn, "r");
+ if (filename) {
- if (!f && errno != EINTR) {
- pa_log_warn("Failed to open configuration file '%s': %s", fn, pa_cstrerror(errno));
- goto finish;
+ if (!(f = fopen(filename, "r"))) {
+ pa_log("Failed to open configuration file '%s': %s", fn, pa_cstrerror(errno));
+ goto finish;
+ }
+
+ fn = pa_xstrdup(fn);
+
+ } else {
+
+ if (!(f = pa_open_config_file(DEFAULT_CLIENT_CONFIG_FILE, DEFAULT_CLIENT_CONFIG_FILE_USER, ENV_CLIENT_CONFIG_FILE, &fn)))
+ if (errno != ENOENT)
+ goto finish;
}
r = f ? pa_config_parse(fn, f, table, NULL) : 0;
@@ -126,7 +133,6 @@ int pa_client_conf_load(pa_client_conf *c, const char *filename) {
if (!r)
r = pa_client_conf_load_cookie(c);
-
finish:
pa_xfree(fn);
diff --git a/src/pulse/context.c b/src/pulse/context.c
index 7806e88c..f9f021af 100644
--- a/src/pulse/context.c
+++ b/src/pulse/context.c
@@ -3,7 +3,7 @@
/***
This file is part of PulseAudio.
- Copyright 2004-2006 Lennart Poettering
+ Copyright 2004-2008 Lennart Poettering
Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
PulseAudio is free software; you can redistribute it and/or modify
@@ -93,6 +93,7 @@ static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {
[PA_COMMAND_RECORD_STREAM_MOVED] = pa_command_stream_moved,
[PA_COMMAND_PLAYBACK_STREAM_SUSPENDED] = pa_command_stream_suspended,
[PA_COMMAND_RECORD_STREAM_SUSPENDED] = pa_command_stream_suspended,
+ [PA_COMMAND_STARTED] = pa_command_stream_started,
[PA_COMMAND_SUBSCRIBE_EVENT] = pa_command_subscribe_event
};
@@ -100,10 +101,12 @@ static void unlock_autospawn_lock_file(pa_context *c) {
pa_assert(c);
if (c->autospawn_lock_fd >= 0) {
- char lf[PATH_MAX];
- pa_runtime_path(AUTOSPAWN_LOCK, lf, sizeof(lf));
+ char *lf;
+ lf = pa_runtime_path(AUTOSPAWN_LOCK);
pa_unlock_lockfile(lf, c->autospawn_lock_fd);
+ pa_xfree(lf);
+
c->autospawn_lock_fd = -1;
}
}
@@ -114,6 +117,16 @@ pa_context *pa_context_new(pa_mainloop_api *mainloop, const char *name) {
return pa_context_new_with_proplist(mainloop, name, NULL);
}
+static void reset_callbacks(pa_context *c) {
+ pa_assert(c);
+
+ c->state_callback = NULL;
+ c->state_userdata = NULL;
+
+ c->subscribe_callback = NULL;
+ c->subscribe_userdata = NULL;
+}
+
pa_context *pa_context_new_with_proplist(pa_mainloop_api *mainloop, const char *name, pa_proplist *p) {
pa_context *c;
@@ -146,18 +159,14 @@ pa_context *pa_context_new_with_proplist(pa_mainloop_api *mainloop, const char *
c->ctag = 0;
c->csyncid = 0;
- c->state_callback = NULL;
- c->state_userdata = NULL;
-
- c->subscribe_callback = NULL;
- c->subscribe_userdata = NULL;
+ reset_callbacks(c);
- c->is_local = -1;
+ c->is_local = FALSE;
c->server_list = NULL;
c->server = NULL;
c->autospawn_lock_fd = -1;
memset(&c->spawn_api, 0, sizeof(c->spawn_api));
- c->do_autospawn = 0;
+ c->do_autospawn = FALSE;
#ifndef MSG_NOSIGNAL
#ifdef SIGPIPE
@@ -186,26 +195,48 @@ pa_context *pa_context_new_with_proplist(pa_mainloop_api *mainloop, const char *
return c;
}
-static void context_free(pa_context *c) {
+static void context_unlink(pa_context *c) {
+ pa_stream *s;
+
pa_assert(c);
- unlock_autospawn_lock_file(c);
+ s = c->streams ? pa_stream_ref(c->streams) : NULL;
+ while (s) {
+ pa_stream *n = s->next ? pa_stream_ref(s->next) : NULL;
+ pa_stream_set_state(s, c->state == PA_CONTEXT_FAILED ? PA_STREAM_FAILED : PA_STREAM_TERMINATED);
+ pa_stream_unref(s);
+ s = n;
+ }
while (c->operations)
pa_operation_cancel(c->operations);
- while (c->streams)
- pa_stream_set_state(c->streams, PA_STREAM_TERMINATED);
-
- if (c->client)
- pa_socket_client_unref(c->client);
- if (c->pdispatch)
+ if (c->pdispatch) {
pa_pdispatch_unref(c->pdispatch);
+ c->pdispatch = NULL;
+ }
+
if (c->pstream) {
pa_pstream_unlink(c->pstream);
pa_pstream_unref(c->pstream);
+ c->pstream = NULL;
}
+ if (c->client) {
+ pa_socket_client_unref(c->client);
+ c->client = NULL;
+ }
+
+ reset_callbacks(c);
+}
+
+static void context_free(pa_context *c) {
+ pa_assert(c);
+
+ context_unlink(c);
+
+ unlock_autospawn_lock_file(c);
+
if (c->record_streams)
pa_dynarray_free(c->record_streams, NULL, NULL);
if (c->playback_streams)
@@ -252,46 +283,16 @@ void pa_context_set_state(pa_context *c, pa_context_state_t st) {
pa_context_ref(c);
c->state = st;
+
if (c->state_callback)
c->state_callback(c, c->state_userdata);
- if (st == PA_CONTEXT_FAILED || st == PA_CONTEXT_TERMINATED) {
- pa_stream *s;
-
- s = c->streams ? pa_stream_ref(c->streams) : NULL;
- while (s) {
- pa_stream *n = s->next ? pa_stream_ref(s->next) : NULL;
- pa_stream_set_state(s, st == PA_CONTEXT_FAILED ? PA_STREAM_FAILED : PA_STREAM_TERMINATED);
- pa_stream_unref(s);
- s = n;
- }
-
- if (c->pdispatch)
- pa_pdispatch_unref(c->pdispatch);
- c->pdispatch = NULL;
-
- if (c->pstream) {
- pa_pstream_unlink(c->pstream);
- pa_pstream_unref(c->pstream);
- }
- c->pstream = NULL;
-
- if (c->client)
- pa_socket_client_unref(c->client);
- c->client = NULL;
- }
+ if (st == PA_CONTEXT_FAILED || st == PA_CONTEXT_TERMINATED)
+ context_unlink(c);
pa_context_unref(c);
}
-void pa_context_fail(pa_context *c, int error) {
- pa_assert(c);
- pa_assert(PA_REFCNT_VALUE(c) >= 1);
-
- pa_context_set_error(c, error);
- pa_context_set_state(c, PA_CONTEXT_FAILED);
-}
-
int pa_context_set_error(pa_context *c, int error) {
pa_assert(error >= 0);
pa_assert(error < PA_ERR_MAX);
@@ -302,6 +303,14 @@ int pa_context_set_error(pa_context *c, int error) {
return error;
}
+void pa_context_fail(pa_context *c, int error) {
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+ pa_context_set_error(c, error);
+ pa_context_set_state(c, PA_CONTEXT_FAILED);
+}
+
static void pstream_die_callback(pa_pstream *p, void *userdata) {
pa_context *c = userdata;
@@ -358,25 +367,41 @@ static void pstream_memblock_callback(pa_pstream *p, uint32_t channel, int64_t o
pa_context_unref(c);
}
-int pa_context_handle_error(pa_context *c, uint32_t command, pa_tagstruct *t) {
+int pa_context_handle_error(pa_context *c, uint32_t command, pa_tagstruct *t, pa_bool_t fail) {
+ uint32_t err;
pa_assert(c);
pa_assert(PA_REFCNT_VALUE(c) >= 1);
if (command == PA_COMMAND_ERROR) {
pa_assert(t);
- if (pa_tagstruct_getu32(t, &c->error) < 0) {
+ if (pa_tagstruct_getu32(t, &err) < 0) {
pa_context_fail(c, PA_ERR_PROTOCOL);
return -1;
-
}
+
} else if (command == PA_COMMAND_TIMEOUT)
- c->error = PA_ERR_TIMEOUT;
+ err = PA_ERR_TIMEOUT;
else {
pa_context_fail(c, PA_ERR_PROTOCOL);
return -1;
}
+ if (err == PA_OK) {
+ pa_context_fail(c, PA_ERR_PROTOCOL);
+ return -1;
+ }
+
+ if (err >= PA_ERR_MAX)
+ err = PA_ERR_UNKNOWN;
+
+ if (fail) {
+ pa_context_fail(c, err);
+ return -1;
+ }
+
+ pa_context_set_error(c, err);
+
return 0;
}
@@ -390,11 +415,7 @@ static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t
pa_context_ref(c);
if (command != PA_COMMAND_REPLY) {
-
- if (pa_context_handle_error(c, command, t) < 0)
- pa_context_fail(c, PA_ERR_PROTOCOL);
-
- pa_context_fail(c, c->error);
+ pa_context_handle_error(c, command, t, TRUE);
goto finish;
}
@@ -417,7 +438,7 @@ static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t
/* Enable shared memory support if possible */
if (c->version >= 10 &&
pa_mempool_is_shared(c->mempool) &&
- c->is_local > 0) {
+ c->is_local) {
/* Only enable SHM if both sides are owned by the same
* user. This is a security measure because otherwise
@@ -486,7 +507,7 @@ static void setup_context(pa_context *c, pa_iochannel *io) {
c->pdispatch = pa_pdispatch_new(c->mainloop, command_table, PA_COMMAND_MAX);
if (!c->conf->cookie_valid)
- pa_log_warn("No cookie loaded. Attempting to connect without.");
+ pa_log_info("No cookie loaded. Attempting to connect without.");
t = pa_tagstruct_command(c, PA_COMMAND_AUTH, &tag);
pa_tagstruct_putu32(t, PA_PROTOCOL_VERSION);
@@ -525,10 +546,13 @@ static int context_connect_spawn(pa_context *c) {
int fds[2] = { -1, -1} ;
pa_iochannel *io;
+ if (getuid() == 0)
+ return -1;
+
pa_context_ref(c);
if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) < 0) {
- pa_log("socketpair(): %s", pa_cstrerror(errno));
+ pa_log_error("socketpair(): %s", pa_cstrerror(errno));
pa_context_fail(c, PA_ERR_INTERNAL);
goto fail;
}
@@ -542,7 +566,7 @@ static int context_connect_spawn(pa_context *c) {
c->spawn_api.prefork();
if ((pid = fork()) < 0) {
- pa_log("fork(): %s", pa_cstrerror(errno));
+ pa_log_error("fork(): %s", pa_cstrerror(errno));
pa_context_fail(c, PA_ERR_INTERNAL);
if (c->spawn_api.postfork)
@@ -557,9 +581,13 @@ static int context_connect_spawn(pa_context *c) {
#define MAX_ARGS 64
const char * argv[MAX_ARGS+1];
int n;
+ char *f;
+
+ pa_close_all(fds[1], -1);
- /* Not required, since fds[0] has CLOEXEC enabled anyway */
- pa_assert_se(pa_close(fds[0]) == 0);
+ f = pa_sprintf_malloc("%i", fds[1]);
+ pa_set_env("PULSE_PASSED_FD", f);
+ pa_xfree(f);
if (c->spawn_api.atfork)
c->spawn_api.atfork();
@@ -592,6 +620,8 @@ static int context_connect_spawn(pa_context *c) {
/* Parent */
+ pa_assert_se(pa_close(fds[1]) == 0);
+
r = waitpid(pid, &status, 0);
if (c->spawn_api.postfork)
@@ -606,14 +636,12 @@ static int context_connect_spawn(pa_context *c) {
goto fail;
}
- pa_assert_se(pa_close(fds[1]) == 0);
+ c->is_local = TRUE;
- c->is_local = 1;
+ unlock_autospawn_lock_file(c);
io = pa_iochannel_new(c->mainloop, fds[0], fds[0]);
-
setup_context(c, io);
- unlock_autospawn_lock_file(c);
pa_context_unref(c);
@@ -665,7 +693,7 @@ static int try_next_connection(pa_context *c) {
if (!(c->client = pa_socket_client_new_string(c->mainloop, u, PA_NATIVE_DEFAULT_PORT)))
continue;
- c->is_local = pa_socket_client_is_local(c->client);
+ c->is_local = !!pa_socket_client_is_local(c->client);
pa_socket_client_set_callback(c->client, on_connection, c);
break;
}
@@ -680,6 +708,7 @@ finish:
static void on_connection(pa_socket_client *client, pa_iochannel*io, void *userdata) {
pa_context *c = userdata;
+ int saved_errno = errno;
pa_assert(client);
pa_assert(c);
@@ -692,7 +721,9 @@ static void on_connection(pa_socket_client *client, pa_iochannel*io, void *userd
if (!io) {
/* Try the item in the list */
- if (errno == ECONNREFUSED || errno == ETIMEDOUT || errno == EHOSTUNREACH) {
+ if (saved_errno == ECONNREFUSED ||
+ saved_errno == ETIMEDOUT ||
+ saved_errno == EHOSTUNREACH) {
try_next_connection(c);
goto finish;
}
@@ -708,6 +739,25 @@ finish:
pa_context_unref(c);
}
+
+static char *get_legacy_runtime_dir(void) {
+ char *p, u[128];
+ struct stat st;
+
+ if (!pa_get_user_name(u, sizeof(u)))
+ return NULL;
+
+ p = pa_sprintf_malloc("/tmp/pulse-%s", u);
+
+ if (stat(p, &st) < 0)
+ return NULL;
+
+ if (st.st_uid != getuid())
+ return NULL;
+
+ return p;
+}
+
int pa_context_connect(
pa_context *c,
const char *server,
@@ -736,8 +786,8 @@ int pa_context_connect(
goto finish;
}
} else {
- char *d;
- char ufn[PATH_MAX];
+ char *d, *ufn;
+ static char *legacy_dir;
/* Prepend in reverse order */
@@ -757,25 +807,34 @@ int pa_context_connect(
c->server_list = pa_strlist_prepend(c->server_list, "tcp4:localhost");
/* The system wide instance */
- c->server_list = pa_strlist_prepend(c->server_list, PA_SYSTEM_RUNTIME_PATH "/" PA_NATIVE_DEFAULT_UNIX_SOCKET);
+ c->server_list = pa_strlist_prepend(c->server_list, PA_SYSTEM_RUNTIME_PATH PA_PATH_SEP PA_NATIVE_DEFAULT_UNIX_SOCKET);
+
+ /* The old per-user instance path. This is supported only to easy upgrades */
+ if ((legacy_dir = get_legacy_runtime_dir())) {
+ char *p = pa_sprintf_malloc("%s" PA_PATH_SEP PA_NATIVE_DEFAULT_UNIX_SOCKET, legacy_dir);
+ c->server_list = pa_strlist_prepend(c->server_list, p);
+ pa_xfree(p);
+ pa_xfree(legacy_dir);
+ }
/* The per-user instance */
- c->server_list = pa_strlist_prepend(c->server_list, pa_runtime_path(PA_NATIVE_DEFAULT_UNIX_SOCKET, ufn, sizeof(ufn)));
+ c->server_list = pa_strlist_prepend(c->server_list, ufn = pa_runtime_path(PA_NATIVE_DEFAULT_UNIX_SOCKET));
+ pa_xfree(ufn);
/* Wrap the connection attempts in a single transaction for sane autospawn locking */
if (!(flags & PA_CONTEXT_NOAUTOSPAWN) && c->conf->autospawn) {
- char lf[PATH_MAX];
+ char *lf;
- pa_runtime_path(AUTOSPAWN_LOCK, lf, sizeof(lf));
- pa_make_secure_parent_dir(lf, 0700, (uid_t)-1, (gid_t)-1);
+ lf = pa_runtime_path(AUTOSPAWN_LOCK);
pa_assert(c->autospawn_lock_fd <= 0);
c->autospawn_lock_fd = pa_lock_lockfile(lf);
+ pa_xfree(lf);
if (api)
c->spawn_api = *api;
- c->do_autospawn = 1;
- }
+ c->do_autospawn = TRUE;
+ }
}
pa_context_set_state(c, PA_CONTEXT_CONNECTING);
@@ -791,7 +850,8 @@ void pa_context_disconnect(pa_context *c) {
pa_assert(c);
pa_assert(PA_REFCNT_VALUE(c) >= 1);
- pa_context_set_state(c, PA_CONTEXT_TERMINATED);
+ if (PA_CONTEXT_IS_GOOD(c->state))
+ pa_context_set_state(c, PA_CONTEXT_TERMINATED);
}
pa_context_state_t pa_context_get_state(pa_context *c) {
@@ -812,6 +872,9 @@ void pa_context_set_state_callback(pa_context *c, pa_context_notify_cb_t cb, voi
pa_assert(c);
pa_assert(PA_REFCNT_VALUE(c) >= 1);
+ if (c->state == PA_CONTEXT_TERMINATED || c->state == PA_CONTEXT_FAILED)
+ return;
+
c->state_callback = cb;
c->state_userdata = userdata;
}
@@ -820,11 +883,7 @@ int pa_context_is_pending(pa_context *c) {
pa_assert(c);
pa_assert(PA_REFCNT_VALUE(c) >= 1);
- PA_CHECK_VALIDITY(c,
- c->state == PA_CONTEXT_CONNECTING ||
- c->state == PA_CONTEXT_AUTHORIZING ||
- c->state == PA_CONTEXT_SETTING_NAME ||
- c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY(c, PA_CONTEXT_IS_GOOD(c->state), PA_ERR_BADSTATE);
return (c->pstream && pa_pstream_is_pending(c->pstream)) ||
(c->pdispatch && pa_pdispatch_is_pending(c->pdispatch)) ||
@@ -901,7 +960,7 @@ void pa_context_simple_ack_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_U
goto finish;
if (command != PA_COMMAND_REPLY) {
- if (pa_context_handle_error(o->context, command, t) < 0)
+ if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
goto finish;
success = 0;
@@ -920,7 +979,7 @@ finish:
pa_operation_unref(o);
}
-pa_operation* pa_context_exit_daemon(pa_context *c, pa_context_success_cb_t cb, void *userdata) {
+pa_operation* pa_context_send_simple_command(pa_context *c, uint32_t command, pa_pdispatch_cb_t internal_cb, pa_operation_cb_t cb, void *userdata) {
pa_tagstruct *t;
pa_operation *o;
uint32_t tag;
@@ -930,32 +989,20 @@ pa_operation* pa_context_exit_daemon(pa_context *c, pa_context_success_cb_t cb,
PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
- o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+ o = pa_operation_new(c, NULL, cb, userdata);
- t = pa_tagstruct_command(c, PA_COMMAND_EXIT, &tag);
+ t = pa_tagstruct_command(c, command, &tag);
pa_pstream_send_tagstruct(c->pstream, t);
- pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, internal_cb, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
return o;
}
-pa_operation* pa_context_send_simple_command(pa_context *c, uint32_t command, pa_pdispatch_cb_t internal_cb, pa_operation_cb_t cb, void *userdata) {
- pa_tagstruct *t;
- pa_operation *o;
- uint32_t tag;
-
+pa_operation* pa_context_exit_daemon(pa_context *c, pa_context_success_cb_t cb, void *userdata) {
pa_assert(c);
pa_assert(PA_REFCNT_VALUE(c) >= 1);
- PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
-
- o = pa_operation_new(c, NULL, cb, userdata);
-
- t = pa_tagstruct_command(c, command, &tag);
- pa_pstream_send_tagstruct(c->pstream, t);
- pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, internal_cb, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
-
- return o;
+ return pa_context_send_simple_command(c, PA_COMMAND_EXIT, pa_context_simple_ack_callback, (pa_operation_cb_t) cb, userdata);
}
pa_operation* pa_context_set_default_sink(pa_context *c, const char *name, pa_context_success_cb_t cb, void *userdata) {
@@ -969,7 +1016,6 @@ pa_operation* pa_context_set_default_sink(pa_context *c, const char *name, pa_co
PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
-
t = pa_tagstruct_command(c, PA_COMMAND_SET_DEFAULT_SINK, &tag);
pa_tagstruct_puts(t, name);
pa_pstream_send_tagstruct(c->pstream, t);
@@ -989,7 +1035,6 @@ pa_operation* pa_context_set_default_source(pa_context *c, const char *name, pa_
PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
-
t = pa_tagstruct_command(c, PA_COMMAND_SET_DEFAULT_SOURCE, &tag);
pa_tagstruct_puts(t, name);
pa_pstream_send_tagstruct(c->pstream, t);
@@ -1002,15 +1047,13 @@ int pa_context_is_local(pa_context *c) {
pa_assert(c);
pa_assert(PA_REFCNT_VALUE(c) >= 1);
- PA_CHECK_VALIDITY(c, c->is_local >= 0, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY_RETURN_ANY(c, PA_CONTEXT_IS_GOOD(c->state), PA_ERR_BADSTATE, -1);
- return c->is_local;
+ return !!c->is_local;
}
pa_operation* pa_context_set_name(pa_context *c, const char *name, pa_context_success_cb_t cb, void *userdata) {
- pa_tagstruct *t;
pa_operation *o;
- uint32_t tag;
pa_assert(c);
pa_assert(PA_REFCNT_VALUE(c) >= 1);
@@ -1020,11 +1063,14 @@ pa_operation* pa_context_set_name(pa_context *c, const char *name, pa_context_su
if (c->version >= 13) {
pa_proplist *p = pa_proplist_new();
+
pa_proplist_sets(p, PA_PROP_APPLICATION_NAME, name);
o = pa_context_proplist_update(c, PA_UPDATE_REPLACE, p, cb, userdata);
pa_proplist_free(p);
-
} else {
+ pa_tagstruct *t;
+ uint32_t tag;
+
o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
t = pa_tagstruct_command(c, PA_COMMAND_SET_CLIENT_NAME, &tag);
pa_tagstruct_puts(t, name);
@@ -1062,7 +1108,7 @@ uint32_t pa_context_get_server_protocol_version(pa_context *c) {
pa_assert(c);
pa_assert(PA_REFCNT_VALUE(c) >= 1);
- PA_CHECK_VALIDITY_RETURN_ANY(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE, PA_INVALID_INDEX);
+ PA_CHECK_VALIDITY_RETURN_ANY(c, PA_CONTEXT_IS_GOOD(c->state), PA_ERR_BADSTATE, PA_INVALID_INDEX);
return c->version;
}
diff --git a/src/pulse/def.h b/src/pulse/def.h
index 8a83d7a9..1a0b9cb6 100644
--- a/src/pulse/def.h
+++ b/src/pulse/def.h
@@ -48,6 +48,15 @@ typedef enum pa_context_state {
PA_CONTEXT_TERMINATED /**< The connection was terminated cleanly */
} pa_context_state_t;
+/** Return non-zero if the passed state is one of the connected states */
+static inline int PA_CONTEXT_IS_GOOD(pa_context_state_t x) {
+ return
+ x == PA_CONTEXT_CONNECTING ||
+ x == PA_CONTEXT_AUTHORIZING ||
+ x == PA_CONTEXT_SETTING_NAME ||
+ x == PA_CONTEXT_READY;
+}
+
/** The state of a stream */
typedef enum pa_stream_state {
PA_STREAM_UNCONNECTED, /**< The stream is not yet connected to any sink or source */
@@ -57,6 +66,13 @@ typedef enum pa_stream_state {
PA_STREAM_TERMINATED /**< The stream has been terminated cleanly */
} pa_stream_state_t;
+/** Return non-zero if the passed state is one of the connected states */
+static inline int PA_STREAM_IS_GOOD(pa_stream_state_t x) {
+ return
+ x == PA_STREAM_CREATING ||
+ x == PA_STREAM_READY;
+}
+
/** The state of an operation */
typedef enum pa_operation_state {
PA_OPERATION_RUNNING, /**< The operation is still running */
@@ -296,6 +312,7 @@ enum {
PA_ERR_VERSION, /**< Incompatible protocol version */
PA_ERR_TOOLARGE, /**< Data too large */
PA_ERR_NOTSUPPORTED, /**< Operation not supported \since 0.9.5 */
+ PA_ERR_UNKNOWN, /**< The error code was unknown to the client */
PA_ERR_MAX /**< Not really an error but the first invalid error code */
};
@@ -368,7 +385,15 @@ typedef struct pa_timing_info {
pa_usec_t source_usec; /**< Time in usecs a sample takes from being recorded to being delivered to the application. Only for record streams. */
pa_usec_t transport_usec; /**< Estimated time in usecs a sample takes to be transferred to/from the daemon. For both playback and record streams. */
- int playing; /**< Non-zero when the stream is currently playing. Only for playback streams. */
+ int playing; /**< Non-zero when the stream is
+ * currently not underrun and data is
+ * being passed on to the device. Only
+ * for playback streams. This field does
+ * not say whether the data is actually
+ * already being played. To determine
+ * this check whether since_underrun
+ * (converted to usec) is larger than
+ * sink_usec.*/
int write_index_corrupt; /**< Non-zero if write_index is not
* up-to-date because a local write
@@ -403,6 +428,14 @@ typedef struct pa_timing_info {
* the sink. \since 0.9.11 */
pa_usec_t configured_source_usec; /**< The static configured latency for
* the source. \since 0.9.11 */
+
+ int64_t since_underrun; /**< Bytes that were handed to the sink
+ since the last underrun happened, or
+ since playback started again after
+ the last underrun. playing will tell
+ you which case it is. \since
+ 0.9.11 */
+
} pa_timing_info;
/** A structure for the spawn api. This may be used to integrate auto
diff --git a/src/pulse/internal.h b/src/pulse/internal.h
index f15c69c3..d346e945 100644
--- a/src/pulse/internal.h
+++ b/src/pulse/internal.h
@@ -42,6 +42,7 @@
#include <pulsecore/memblockq.h>
#include <pulsecore/hashmap.h>
#include <pulsecore/refcnt.h>
+#include <pulsecore/time-smoother.h>
#include "client-conf.h"
@@ -69,14 +70,13 @@ struct pa_context {
pa_context_notify_cb_t state_callback;
void *state_userdata;
-
pa_context_subscribe_cb_t subscribe_callback;
void *subscribe_userdata;
pa_mempool *mempool;
- int is_local;
- int do_autospawn;
+ pa_bool_t is_local;
+ pa_bool_t do_autospawn;
int autospawn_lock_fd;
pa_spawn_api spawn_api;
@@ -89,35 +89,39 @@ struct pa_context {
uint32_t client_index;
};
-#define PA_MAX_WRITE_INDEX_CORRECTIONS 10
+#define PA_MAX_WRITE_INDEX_CORRECTIONS 32
typedef struct pa_index_correction {
uint32_t tag;
- int valid;
int64_t value;
- int absolute, corrupt;
+ pa_bool_t valid:1;
+ pa_bool_t absolute:1;
+ pa_bool_t corrupt:1;
} pa_index_correction;
struct pa_stream {
PA_REFCNT_DECLARE;
+ PA_LLIST_FIELDS(pa_stream);
+
pa_context *context;
pa_mainloop_api *mainloop;
- PA_LLIST_FIELDS(pa_stream);
- pa_proplist *proplist;
- pa_bool_t manual_buffer_attr;
- pa_buffer_attr buffer_attr;
+ pa_stream_direction_t direction;
+ pa_stream_state_t state;
+ pa_stream_flags_t flags;
+
pa_sample_spec sample_spec;
pa_channel_map channel_map;
- pa_stream_flags_t flags;
+
+ pa_proplist *proplist;
+
uint32_t channel;
+ pa_bool_t channel_valid;
uint32_t syncid;
- int channel_valid;
uint32_t stream_index;
- pa_stream_direction_t direction;
- pa_stream_state_t state;
uint32_t requested_bytes;
+ pa_buffer_attr buffer_attr;
uint32_t device_index;
char *device_name;
@@ -127,11 +131,11 @@ struct pa_stream {
void *peek_data;
pa_memblockq *record_memblockq;
- int corked;
+ pa_bool_t corked;
/* Store latest latency info */
pa_timing_info timing_info;
- int timing_info_valid;
+ pa_bool_t timing_info_valid;
/* Use to make sure that time advances monotonically */
pa_usec_t previous_time;
@@ -146,10 +150,9 @@ struct pa_stream {
/* Latency interpolation stuff */
pa_time_event *auto_timing_update_event;
- int auto_timing_update_requested;
+ pa_bool_t auto_timing_update_requested;
- pa_usec_t cached_time;
- int cached_time_valid;
+ pa_smoother *smoother;
/* Callbacks */
pa_stream_notify_cb_t state_callback;
@@ -168,6 +171,8 @@ struct pa_stream {
void *moved_userdata;
pa_stream_notify_cb_t suspended_callback;
void *suspended_userdata;
+ pa_stream_notify_cb_t started_callback;
+ void *started_userdata;
};
typedef void (*pa_operation_cb_t)(void);
@@ -193,7 +198,7 @@ void pa_command_subscribe_event(pa_pdispatch *pd, uint32_t command, uint32_t tag
void pa_command_overflow_or_underflow(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
void pa_command_stream_suspended(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
void pa_command_stream_moved(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
-
+void pa_command_stream_started(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
pa_operation *pa_operation_new(pa_context *c, pa_stream *s, pa_operation_cb_t callback, void *userdata);
void pa_operation_done(pa_operation *o);
@@ -205,7 +210,7 @@ void pa_stream_simple_ack_callback(pa_pdispatch *pd, uint32_t command, uint32_t
void pa_context_fail(pa_context *c, int error);
int pa_context_set_error(pa_context *c, int error);
void pa_context_set_state(pa_context *c, pa_context_state_t st);
-int pa_context_handle_error(pa_context *c, uint32_t command, pa_tagstruct *t);
+int pa_context_handle_error(pa_context *c, uint32_t command, pa_tagstruct *t, pa_bool_t fail);
pa_operation* pa_context_send_simple_command(pa_context *c, uint32_t command, void (*internal_callback)(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata), void (*cb)(void), void *userdata);
void pa_stream_set_state(pa_stream *s, pa_stream_state_t st);
diff --git a/src/pulse/introspect.c b/src/pulse/introspect.c
index 49f93463..857e82b4 100644
--- a/src/pulse/introspect.c
+++ b/src/pulse/introspect.c
@@ -52,7 +52,7 @@ static void context_stat_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNU
goto finish;
if (command != PA_COMMAND_REPLY) {
- if (pa_context_handle_error(o->context, command, t) < 0)
+ if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
goto finish;
p = NULL;
@@ -95,7 +95,7 @@ static void context_get_server_info_callback(pa_pdispatch *pd, uint32_t command,
goto finish;
if (command != PA_COMMAND_REPLY) {
- if (pa_context_handle_error(o->context, command, t) < 0)
+ if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
goto finish;
p = NULL;
@@ -140,7 +140,7 @@ static void context_get_sink_info_callback(pa_pdispatch *pd, uint32_t command, P
goto finish;
if (command != PA_COMMAND_REPLY) {
- if (pa_context_handle_error(o->context, command, t) < 0)
+ if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
goto finish;
eol = -1;
@@ -261,7 +261,7 @@ static void context_get_source_info_callback(pa_pdispatch *pd, uint32_t command,
goto finish;
if (command != PA_COMMAND_REPLY) {
- if (pa_context_handle_error(o->context, command, t) < 0)
+ if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
goto finish;
eol = -1;
@@ -382,7 +382,7 @@ static void context_get_client_info_callback(pa_pdispatch *pd, uint32_t command,
goto finish;
if (command != PA_COMMAND_REPLY) {
- if (pa_context_handle_error(o->context, command, t) < 0)
+ if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
goto finish;
eol = -1;
@@ -464,7 +464,7 @@ static void context_get_module_info_callback(pa_pdispatch *pd, uint32_t command,
goto finish;
if (command != PA_COMMAND_REPLY) {
- if (pa_context_handle_error(o->context, command, t) < 0)
+ if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
goto finish;
eol = -1;
@@ -543,7 +543,7 @@ static void context_get_sink_input_info_callback(pa_pdispatch *pd, uint32_t comm
goto finish;
if (command != PA_COMMAND_REPLY) {
- if (pa_context_handle_error(o->context, command, t) < 0)
+ if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
goto finish;
eol = -1;
@@ -637,7 +637,7 @@ static void context_get_source_output_info_callback(pa_pdispatch *pd, uint32_t c
goto finish;
if (command != PA_COMMAND_REPLY) {
- if (pa_context_handle_error(o->context, command, t) < 0)
+ if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
goto finish;
eol = -1;
@@ -967,7 +967,7 @@ static void context_get_sample_info_callback(pa_pdispatch *pd, uint32_t command,
goto finish;
if (command != PA_COMMAND_REPLY) {
- if (pa_context_handle_error(o->context, command, t) < 0)
+ if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
goto finish;
eol = -1;
@@ -1111,7 +1111,7 @@ static void context_index_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UN
goto finish;
if (command != PA_COMMAND_REPLY) {
- if (pa_context_handle_error(o->context, command, t) < 0)
+ if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
goto finish;
idx = PA_INVALID_INDEX;
@@ -1172,7 +1172,7 @@ static void context_get_autoload_info_callback(pa_pdispatch *pd, uint32_t comman
goto finish;
if (command != PA_COMMAND_REPLY) {
- if (pa_context_handle_error(o->context, command, t) < 0)
+ if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
goto finish;
eol = -1;
diff --git a/src/pulse/scache.c b/src/pulse/scache.c
index 24f340ea..e43a0b9f 100644
--- a/src/pulse/scache.c
+++ b/src/pulse/scache.c
@@ -108,7 +108,7 @@ static void play_sample_ack_callback(pa_pdispatch *pd, uint32_t command, uint32_
goto finish;
if (command != PA_COMMAND_REPLY) {
- if (pa_context_handle_error(o->context, command, t) < 0)
+ if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
goto finish;
success = 0;
@@ -141,7 +141,7 @@ static void play_sample_with_proplist_ack_callback(pa_pdispatch *pd, uint32_t co
goto finish;
if (command != PA_COMMAND_REPLY) {
- if (pa_context_handle_error(o->context, command, t) < 0)
+ if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
goto finish;
idx = PA_INVALID_INDEX;
diff --git a/src/pulse/stream.c b/src/pulse/stream.c
index ccbabb55..297e9d7f 100644
--- a/src/pulse/stream.c
+++ b/src/pulse/stream.c
@@ -38,16 +38,47 @@
#include <pulsecore/log.h>
#include <pulsecore/hashmap.h>
#include <pulsecore/macro.h>
+#include <pulsecore/rtclock.h>
#include "internal.h"
-#define LATENCY_IPOL_INTERVAL_USEC (100000L)
+#define LATENCY_IPOL_INTERVAL_USEC (500*PA_USEC_PER_MSEC)
+
+#define SMOOTHER_ADJUST_TIME (1000*PA_USEC_PER_MSEC)
+#define SMOOTHER_HISTORY_TIME (5000*PA_USEC_PER_MSEC)
pa_stream *pa_stream_new(pa_context *c, const char *name, const pa_sample_spec *ss, const pa_channel_map *map) {
return pa_stream_new_with_proplist(c, name, ss, map, NULL);
}
-pa_stream *pa_stream_new_with_proplist(pa_context *c, const char *name, const pa_sample_spec *ss, const pa_channel_map *map, pa_proplist *p) {
+static void reset_callbacks(pa_stream *s) {
+ s->read_callback = NULL;
+ s->read_userdata = NULL;
+ s->write_callback = NULL;
+ s->write_userdata = NULL;
+ s->state_callback = NULL;
+ s->state_userdata = NULL;
+ s->overflow_callback = NULL;
+ s->overflow_userdata = NULL;
+ s->underflow_callback = NULL;
+ s->underflow_userdata = NULL;
+ s->latency_update_callback = NULL;
+ s->latency_update_userdata = NULL;
+ s->moved_callback = NULL;
+ s->moved_userdata = NULL;
+ s->suspended_callback = NULL;
+ s->suspended_userdata = NULL;
+ s->started_callback = NULL;
+ s->started_userdata = NULL;
+}
+
+pa_stream *pa_stream_new_with_proplist(
+ pa_context *c,
+ const char *name,
+ const pa_sample_spec *ss,
+ const pa_channel_map *map,
+ pa_proplist *p) {
+
pa_stream *s;
int i;
pa_channel_map tmap;
@@ -58,7 +89,7 @@ pa_stream *pa_stream_new_with_proplist(pa_context *c, const char *name, const pa
PA_CHECK_VALIDITY_RETURN_NULL(c, ss && pa_sample_spec_valid(ss), PA_ERR_INVALID);
PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 12 || (ss->format != PA_SAMPLE_S32LE || ss->format != PA_SAMPLE_S32NE), PA_ERR_NOTSUPPORTED);
PA_CHECK_VALIDITY_RETURN_NULL(c, !map || (pa_channel_map_valid(map) && map->channels == ss->channels), PA_ERR_INVALID);
- PA_CHECK_VALIDITY_RETURN_NULL(c, name || pa_proplist_contains(p, PA_PROP_MEDIA_NAME), PA_ERR_INVALID);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, name || (p && pa_proplist_contains(p, PA_PROP_MEDIA_NAME)), PA_ERR_INVALID);
if (!map)
PA_CHECK_VALIDITY_RETURN_NULL(c, map = pa_channel_map_init_auto(&tmap, ss->channels, PA_CHANNEL_MAP_DEFAULT), PA_ERR_INVALID);
@@ -68,70 +99,53 @@ pa_stream *pa_stream_new_with_proplist(pa_context *c, const char *name, const pa
s->context = c;
s->mainloop = c->mainloop;
- s->read_callback = NULL;
- s->read_userdata = NULL;
- s->write_callback = NULL;
- s->write_userdata = NULL;
- s->state_callback = NULL;
- s->state_userdata = NULL;
- s->overflow_callback = NULL;
- s->overflow_userdata = NULL;
- s->underflow_callback = NULL;
- s->underflow_userdata = NULL;
- s->latency_update_callback = NULL;
- s->latency_update_userdata = NULL;
- s->moved_callback = NULL;
- s->moved_userdata = NULL;
- s->suspended_callback = NULL;
- s->suspended_userdata = NULL;
-
s->direction = PA_STREAM_NODIRECTION;
+ s->state = PA_STREAM_UNCONNECTED;
+ s->flags = 0;
+
s->sample_spec = *ss;
s->channel_map = *map;
- s->flags = 0;
s->proplist = p ? pa_proplist_copy(p) : pa_proplist_new();
-
if (name)
pa_proplist_sets(s->proplist, PA_PROP_MEDIA_NAME, name);
s->channel = 0;
- s->channel_valid = 0;
+ s->channel_valid = FALSE;
s->syncid = c->csyncid++;
s->stream_index = PA_INVALID_INDEX;
- s->requested_bytes = 0;
- s->state = PA_STREAM_UNCONNECTED;
- s->manual_buffer_attr = FALSE;
+ s->requested_bytes = 0;
memset(&s->buffer_attr, 0, sizeof(s->buffer_attr));
s->device_index = PA_INVALID_INDEX;
s->device_name = NULL;
s->suspended = FALSE;
- s->peek_memchunk.index = 0;
- s->peek_memchunk.length = 0;
- s->peek_memchunk.memblock = NULL;
+ pa_memchunk_reset(&s->peek_memchunk);
s->peek_data = NULL;
s->record_memblockq = NULL;
- s->previous_time = 0;
+ s->corked = FALSE;
+
memset(&s->timing_info, 0, sizeof(s->timing_info));
s->timing_info_valid = FALSE;
+
+ s->previous_time = 0;
+
s->read_index_not_before = 0;
s->write_index_not_before = 0;
-
for (i = 0; i < PA_MAX_WRITE_INDEX_CORRECTIONS; i++)
s->write_index_corrections[i].valid = 0;
s->current_write_index_correction = 0;
- s->corked = 0;
+ s->auto_timing_update_event = NULL;
+ s->auto_timing_update_requested = FALSE;
- s->cached_time_valid = 0;
+ reset_callbacks(s);
- s->auto_timing_update_event = NULL;
- s->auto_timing_update_requested = 0;
+ s->smoother = NULL;
/* Refcounting is strictly one-way: from the "bigger" to the "smaller" object. */
PA_LLIST_PREPEND(pa_stream, c->streams, s);
@@ -140,16 +154,51 @@ pa_stream *pa_stream_new_with_proplist(pa_context *c, const char *name, const pa
return s;
}
-static void stream_free(pa_stream *s) {
+static void stream_unlink(pa_stream *s) {
+ pa_operation *o, *n;
pa_assert(s);
- pa_assert(!s->context);
- pa_assert(!s->channel_valid);
+
+ if (!s->context)
+ return;
+
+ /* Detach from context */
+
+ /* Unref all operatio object that point to us */
+ for (o = s->context->operations; o; o = n) {
+ n = o->next;
+
+ if (o->stream == s)
+ pa_operation_cancel(o);
+ }
+
+ /* Drop all outstanding replies for this stream */
+ if (s->context->pdispatch)
+ pa_pdispatch_unregister_reply(s->context->pdispatch, s);
+
+ if (s->channel_valid) {
+ pa_dynarray_put((s->direction == PA_STREAM_PLAYBACK) ? s->context->playback_streams : s->context->record_streams, s->channel, NULL);
+ s->channel = 0;
+ s->channel_valid = FALSE;
+ }
+
+ PA_LLIST_REMOVE(pa_stream, s->context->streams, s);
+ pa_stream_unref(s);
+
+ s->context = NULL;
if (s->auto_timing_update_event) {
pa_assert(s->mainloop);
s->mainloop->time_free(s->auto_timing_update_event);
}
+ reset_callbacks(s);
+}
+
+static void stream_free(pa_stream *s) {
+ pa_assert(s);
+
+ stream_unlink(s);
+
if (s->peek_memchunk.memblock) {
if (s->peek_data)
pa_memblock_release(s->peek_memchunk.memblock);
@@ -162,6 +211,9 @@ static void stream_free(pa_stream *s) {
if (s->proplist)
pa_proplist_free(s->proplist);
+ if (s->smoother)
+ pa_smoother_free(s->smoother);
+
pa_xfree(s->device_name);
pa_xfree(s);
}
@@ -215,46 +267,41 @@ void pa_stream_set_state(pa_stream *s, pa_stream_state_t st) {
pa_stream_ref(s);
s->state = st;
+
if (s->state_callback)
s->state_callback(s, s->state_userdata);
- if ((st == PA_STREAM_FAILED || st == PA_STREAM_TERMINATED) && s->context) {
-
- /* Detach from context */
- pa_operation *o, *n;
+ if ((st == PA_STREAM_FAILED || st == PA_STREAM_TERMINATED))
+ stream_unlink(s);
- /* Unref all operatio object that point to us */
- for (o = s->context->operations; o; o = n) {
- n = o->next;
-
- if (o->stream == s)
- pa_operation_cancel(o);
- }
-
- /* Drop all outstanding replies for this stream */
- if (s->context->pdispatch)
- pa_pdispatch_unregister_reply(s->context->pdispatch, s);
+ pa_stream_unref(s);
+}
- if (s->channel_valid)
- pa_dynarray_put((s->direction == PA_STREAM_PLAYBACK) ? s->context->playback_streams : s->context->record_streams, s->channel, NULL);
+static void request_auto_timing_update(pa_stream *s, pa_bool_t force) {
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
- PA_LLIST_REMOVE(pa_stream, s->context->streams, s);
- pa_stream_unref(s);
+ if (!(s->flags & PA_STREAM_AUTO_TIMING_UPDATE))
+ return;
- s->channel = 0;
- s->channel_valid = 0;
+ if (s->state == PA_STREAM_READY &&
+ (force || !s->auto_timing_update_requested)) {
+ pa_operation *o;
- s->context = NULL;
+/* pa_log("automatically requesting new timing data"); */
- s->read_callback = NULL;
- s->write_callback = NULL;
- s->state_callback = NULL;
- s->overflow_callback = NULL;
- s->underflow_callback = NULL;
- s->latency_update_callback = NULL;
+ if ((o = pa_stream_update_timing_info(s, NULL, NULL))) {
+ pa_operation_unref(o);
+ s->auto_timing_update_requested = TRUE;
+ }
}
- pa_stream_unref(s);
+ if (s->auto_timing_update_event) {
+ struct timeval next;
+ pa_gettimeofday(&next);
+ pa_timeval_add(&next, LATENCY_IPOL_INTERVAL_USEC);
+ s->mainloop->time_restart(s->auto_timing_update_event, &next);
+ }
}
void pa_command_stream_killed(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
@@ -279,6 +326,9 @@ void pa_command_stream_killed(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED
if (!(s = pa_dynarray_get(command == PA_COMMAND_PLAYBACK_STREAM_KILLED ? c->playback_streams : c->record_streams, channel)))
goto finish;
+ if (s->state != PA_STREAM_READY)
+ goto finish;
+
pa_context_set_error(c, PA_ERR_KILLED);
pa_stream_set_state(s, PA_STREAM_FAILED);
@@ -293,6 +343,7 @@ void pa_command_stream_moved(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED u
const char *dn;
pa_bool_t suspended;
uint32_t di;
+ pa_usec_t usec;
pa_assert(pd);
pa_assert(command == PA_COMMAND_PLAYBACK_STREAM_MOVED || command == PA_COMMAND_RECORD_STREAM_MOVED);
@@ -310,12 +361,23 @@ void pa_command_stream_moved(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED u
if (pa_tagstruct_getu32(t, &channel) < 0 ||
pa_tagstruct_getu32(t, &di) < 0 ||
pa_tagstruct_gets(t, &dn) < 0 ||
- pa_tagstruct_get_boolean(t, &suspended) < 0 ||
- !pa_tagstruct_eof(t)) {
+ pa_tagstruct_get_boolean(t, &suspended) < 0) {
pa_context_fail(c, PA_ERR_PROTOCOL);
goto finish;
}
+ if (c->version >= 13) {
+ if (pa_tagstruct_get_usec(t, &usec) < 0) {
+ pa_context_fail(s->context, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+ }
+
+ if (!pa_tagstruct_eof(t)) {
+ pa_context_fail(s->context, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+
if (!dn || di == PA_INVALID_INDEX) {
pa_context_fail(c, PA_ERR_PROTOCOL);
goto finish;
@@ -324,12 +386,24 @@ void pa_command_stream_moved(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED u
if (!(s = pa_dynarray_get(command == PA_COMMAND_PLAYBACK_STREAM_MOVED ? c->playback_streams : c->record_streams, channel)))
goto finish;
+ if (s->state != PA_STREAM_READY)
+ goto finish;
+
+ if (c->version >= 13) {
+ if (s->direction == PA_STREAM_RECORD)
+ s->timing_info.configured_source_usec = usec;
+ else
+ s->timing_info.configured_sink_usec = usec;
+ }
+
pa_xfree(s->device_name);
s->device_name = pa_xstrdup(dn);
s->device_index = di;
s->suspended = suspended;
+ request_auto_timing_update(s, TRUE);
+
if (s->moved_callback)
s->moved_callback(s, s->moved_userdata);
@@ -366,8 +440,23 @@ void pa_command_stream_suspended(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUS
if (!(s = pa_dynarray_get(command == PA_COMMAND_PLAYBACK_STREAM_SUSPENDED ? c->playback_streams : c->record_streams, channel)))
goto finish;
+ if (s->state != PA_STREAM_READY)
+ goto finish;
+
s->suspended = suspended;
+ if (s->smoother) {
+ pa_usec_t x = pa_rtclock_usec();
+
+ if (s->timing_info_valid)
+ x -= s->timing_info.transport_usec;
+
+ if (s->suspended || s->corked)
+ pa_smoother_pause(s->smoother, x);
+ }
+
+ request_auto_timing_update(s, TRUE);
+
if (s->suspended_callback)
s->suspended_callback(s, s->suspended_userdata);
@@ -375,6 +464,45 @@ finish:
pa_context_unref(c);
}
+void pa_command_stream_started(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_context *c = userdata;
+ pa_stream *s;
+ uint32_t channel;
+
+ pa_assert(pd);
+ pa_assert(command == PA_COMMAND_STARTED);
+ pa_assert(t);
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+ pa_context_ref(c);
+
+ if (c->version < 13) {
+ pa_context_fail(c, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+
+ if (pa_tagstruct_getu32(t, &channel) < 0 ||
+ !pa_tagstruct_eof(t)) {
+ pa_context_fail(c, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+
+ if (!(s = pa_dynarray_get(c->playback_streams, channel)))
+ goto finish;
+
+ if (s->state != PA_STREAM_READY)
+ goto finish;
+
+ request_auto_timing_update(s, TRUE);
+
+ if (s->started_callback)
+ s->started_callback(s, s->suspended_userdata);
+
+finish:
+ pa_context_unref(c);
+}
+
void pa_command_request(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
pa_stream *s;
pa_context *c = userdata;
@@ -398,12 +526,13 @@ void pa_command_request(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32
if (!(s = pa_dynarray_get(c->playback_streams, channel)))
goto finish;
- if (s->state == PA_STREAM_READY) {
- s->requested_bytes += bytes;
+ if (s->state != PA_STREAM_READY)
+ goto finish;
- if (s->requested_bytes > 0 && s->write_callback)
- s->write_callback(s, s->requested_bytes, s->write_userdata);
- }
+ s->requested_bytes += bytes;
+
+ if (s->requested_bytes > 0 && s->write_callback)
+ s->write_callback(s, s->requested_bytes, s->write_userdata);
finish:
pa_context_unref(c);
@@ -431,6 +560,21 @@ void pa_command_overflow_or_underflow(pa_pdispatch *pd, uint32_t command, PA_GCC
if (!(s = pa_dynarray_get(c->playback_streams, channel)))
goto finish;
+ if (s->state != PA_STREAM_READY)
+ goto finish;
+
+ if (s->smoother)
+ if (s->direction == PA_STREAM_PLAYBACK && s->buffer_attr.prebuf > 0) {
+ pa_usec_t x = pa_rtclock_usec();
+
+ if (s->timing_info_valid)
+ x -= s->timing_info.transport_usec;
+
+ pa_smoother_pause(s->smoother, x);
+ }
+
+ request_auto_timing_update(s, TRUE);
+
if (s->state == PA_STREAM_READY) {
if (command == PA_COMMAND_OVERFLOW) {
@@ -446,34 +590,7 @@ void pa_command_overflow_or_underflow(pa_pdispatch *pd, uint32_t command, PA_GCC
pa_context_unref(c);
}
-static void request_auto_timing_update(pa_stream *s, int force) {
- pa_assert(s);
- pa_assert(PA_REFCNT_VALUE(s) >= 1);
-
- if (!(s->flags & PA_STREAM_AUTO_TIMING_UPDATE))
- return;
-
- if (s->state == PA_STREAM_READY &&
- (force || !s->auto_timing_update_requested)) {
- pa_operation *o;
-
-/* pa_log("automatically requesting new timing data"); */
-
- if ((o = pa_stream_update_timing_info(s, NULL, NULL))) {
- pa_operation_unref(o);
- s->auto_timing_update_requested = TRUE;
- }
- }
-
- if (s->auto_timing_update_event) {
- struct timeval next;
- pa_gettimeofday(&next);
- pa_timeval_add(&next, LATENCY_IPOL_INTERVAL_USEC);
- s->mainloop->time_restart(s->auto_timing_update_event, &next);
- }
-}
-
-static void invalidate_indexes(pa_stream *s, int r, int w) {
+static void invalidate_indexes(pa_stream *s, pa_bool_t r, pa_bool_t w) {
pa_assert(s);
pa_assert(PA_REFCNT_VALUE(s) >= 1);
@@ -500,11 +617,7 @@ static void invalidate_indexes(pa_stream *s, int r, int w) {
/* pa_log("read_index invalidated"); */
}
- if ((s->direction == PA_STREAM_PLAYBACK && r) ||
- (s->direction == PA_STREAM_RECORD && w))
- s->cached_time_valid = 0;
-
- request_auto_timing_update(s, 1);
+ request_auto_timing_update(s, TRUE);
}
static void auto_timing_update_callback(PA_GCC_UNUSED pa_mainloop_api *m, PA_GCC_UNUSED pa_time_event *e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) {
@@ -513,10 +626,8 @@ static void auto_timing_update_callback(PA_GCC_UNUSED pa_mainloop_api *m, PA_GCC
pa_assert(s);
pa_assert(PA_REFCNT_VALUE(s) >= 1);
-/* pa_log("time event"); */
-
pa_stream_ref(s);
- request_auto_timing_update(s, 0);
+ request_auto_timing_update(s, FALSE);
pa_stream_unref(s);
}
@@ -536,6 +647,8 @@ static void create_stream_complete(pa_stream *s) {
tv.tv_usec += LATENCY_IPOL_INTERVAL_USEC; /* every 100 ms */
pa_assert(!s->auto_timing_update_event);
s->auto_timing_update_event = s->mainloop->time_new(s->mainloop, &tv, &auto_timing_update_callback, s);
+
+ request_auto_timing_update(s, TRUE);
}
}
@@ -577,7 +690,7 @@ void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED
pa_stream_ref(s);
if (command != PA_COMMAND_REPLY) {
- if (pa_context_handle_error(s->context, command, t) < 0)
+ if (pa_context_handle_error(s->context, command, t, FALSE) < 0)
goto finish;
pa_stream_set_state(s, PA_STREAM_FAILED);
@@ -585,7 +698,8 @@ void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED
}
if (pa_tagstruct_getu32(t, &s->channel) < 0 ||
- ((s->direction != PA_STREAM_UPLOAD) && pa_tagstruct_getu32(t, &s->stream_index) < 0) ||
+ s->channel == PA_INVALID_INDEX ||
+ ((s->direction != PA_STREAM_UPLOAD) && (pa_tagstruct_getu32(t, &s->stream_index) < 0 || s->stream_index == PA_INVALID_INDEX)) ||
((s->direction != PA_STREAM_RECORD) && pa_tagstruct_getu32(t, &s->requested_bytes) < 0)) {
pa_context_fail(s->context, PA_ERR_PROTOCOL);
goto finish;
@@ -676,7 +790,7 @@ void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED
NULL);
}
- s->channel_valid = 1;
+ s->channel_valid = TRUE;
pa_dynarray_put((s->direction == PA_STREAM_RECORD) ? s->context->record_streams : s->context->playback_streams, s->channel, s);
create_stream_complete(s);
@@ -737,16 +851,23 @@ static int create_stream(
if (sync_stream)
s->syncid = sync_stream->syncid;
- if (attr) {
+ if (attr)
s->buffer_attr = *attr;
- s->manual_buffer_attr = TRUE;
- } else {
- memset(&s->buffer_attr, 0, sizeof(s->buffer_attr));
- s->manual_buffer_attr = FALSE;
- }
-
automatic_buffer_attr(s, &s->buffer_attr, &s->sample_spec);
+ if (flags & PA_STREAM_INTERPOLATE_TIMING) {
+ pa_usec_t x;
+
+ if (s->smoother)
+ pa_smoother_free(s->smoother);
+
+ s->smoother = pa_smoother_new(SMOOTHER_ADJUST_TIME, SMOOTHER_HISTORY_TIME, !(flags & PA_STREAM_NOT_MONOTONOUS));
+
+ x = pa_rtclock_usec();
+ pa_smoother_set_time_offset(s->smoother, x);
+ pa_smoother_pause(s->smoother, x);
+ }
+
if (!dev)
dev = s->direction == PA_STREAM_PLAYBACK ? s->context->conf->default_sink : s->context->conf->default_source;
@@ -922,31 +1043,31 @@ int pa_stream_write(
if (s->write_index_corrections[s->current_write_index_correction].valid) {
if (seek == PA_SEEK_ABSOLUTE) {
- s->write_index_corrections[s->current_write_index_correction].corrupt = 0;
- s->write_index_corrections[s->current_write_index_correction].absolute = 1;
+ s->write_index_corrections[s->current_write_index_correction].corrupt = FALSE;
+ s->write_index_corrections[s->current_write_index_correction].absolute = TRUE;
s->write_index_corrections[s->current_write_index_correction].value = offset + length;
} else if (seek == PA_SEEK_RELATIVE) {
if (!s->write_index_corrections[s->current_write_index_correction].corrupt)
s->write_index_corrections[s->current_write_index_correction].value += offset + length;
} else
- s->write_index_corrections[s->current_write_index_correction].corrupt = 1;
+ s->write_index_corrections[s->current_write_index_correction].corrupt = TRUE;
}
/* Update the write index in the already available latency data */
if (s->timing_info_valid) {
if (seek == PA_SEEK_ABSOLUTE) {
- s->timing_info.write_index_corrupt = 0;
+ s->timing_info.write_index_corrupt = FALSE;
s->timing_info.write_index = offset + length;
} else if (seek == PA_SEEK_RELATIVE) {
if (!s->timing_info.write_index_corrupt)
s->timing_info.write_index += offset + length;
} else
- s->timing_info.write_index_corrupt = 1;
+ s->timing_info.write_index_corrupt = TRUE;
}
if (!s->timing_info_valid || s->timing_info.write_index_corrupt)
- request_auto_timing_update(s, 1);
+ request_auto_timing_update(s, TRUE);
}
return 0;
@@ -995,9 +1116,7 @@ int pa_stream_drop(pa_stream *s) {
pa_assert(s->peek_data);
pa_memblock_release(s->peek_memchunk.memblock);
pa_memblock_unref(s->peek_memchunk.memblock);
- s->peek_memchunk.length = 0;
- s->peek_memchunk.index = 0;
- s->peek_memchunk.memblock = NULL;
+ pa_memchunk_reset(&s->peek_memchunk);
return 0;
}
@@ -1043,11 +1162,71 @@ pa_operation * pa_stream_drain(pa_stream *s, pa_stream_success_cb_t cb, void *us
return o;
}
+static pa_usec_t calc_time(pa_stream *s, pa_bool_t ignore_transport) {
+ pa_usec_t usec;
+
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
+ pa_assert(s->state == PA_STREAM_READY);
+ pa_assert(s->direction != PA_STREAM_UPLOAD);
+ pa_assert(s->timing_info_valid);
+ pa_assert(s->direction != PA_STREAM_PLAYBACK || !s->timing_info.read_index_corrupt);
+ pa_assert(s->direction != PA_STREAM_RECORD || !s->timing_info.write_index_corrupt);
+
+ if (s->direction == PA_STREAM_PLAYBACK) {
+ /* The last byte that was written into the output device
+ * had this time value associated */
+ usec = pa_bytes_to_usec(s->timing_info.read_index < 0 ? 0 : (uint64_t) s->timing_info.read_index, &s->sample_spec);
+
+ if (!s->corked && !s->suspended) {
+
+ if (!ignore_transport)
+ /* Because the latency info took a little time to come
+ * to us, we assume that the real output time is actually
+ * a little ahead */
+ usec += s->timing_info.transport_usec;
+
+ /* However, the output device usually maintains a buffer
+ too, hence the real sample currently played is a little
+ back */
+ if (s->timing_info.sink_usec >= usec)
+ usec = 0;
+ else
+ usec -= s->timing_info.sink_usec;
+ }
+
+ } else if (s->direction == PA_STREAM_RECORD) {
+ /* The last byte written into the server side queue had
+ * this time value associated */
+ usec = pa_bytes_to_usec(s->timing_info.write_index < 0 ? 0 : (uint64_t) s->timing_info.write_index, &s->sample_spec);
+
+ if (!s->corked && !s->suspended) {
+
+ if (!ignore_transport)
+ /* Add transport latency */
+ usec += s->timing_info.transport_usec;
+
+ /* Add latency of data in device buffer */
+ usec += s->timing_info.source_usec;
+
+ /* If this is a monitor source, we need to correct the
+ * time by the playback device buffer */
+ if (s->timing_info.sink_usec >= usec)
+ usec = 0;
+ else
+ usec -= s->timing_info.sink_usec;
+ }
+ }
+
+ return usec;
+}
+
static void stream_get_timing_info_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
pa_operation *o = userdata;
struct timeval local, remote, now;
pa_timing_info *i;
pa_bool_t playing = FALSE;
+ uint64_t underrun_for = 0, playing_for = 0;
pa_assert(pd);
pa_assert(o);
@@ -1061,29 +1240,46 @@ static void stream_get_timing_info_callback(pa_pdispatch *pd, uint32_t command,
/* pa_log("pre corrupt w:%u r:%u\n", !o->stream->timing_info_valid || i->write_index_corrupt,!o->stream->timing_info_valid || i->read_index_corrupt); */
o->stream->timing_info_valid = FALSE;
- i->write_index_corrupt = 0;
- i->read_index_corrupt = 0;
+ i->write_index_corrupt = FALSE;
+ i->read_index_corrupt = FALSE;
/* pa_log("timing update %u\n", tag); */
if (command != PA_COMMAND_REPLY) {
- if (pa_context_handle_error(o->context, command, t) < 0)
+ if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
goto finish;
- } else if (pa_tagstruct_get_usec(t, &i->sink_usec) < 0 ||
- pa_tagstruct_get_usec(t, &i->source_usec) < 0 ||
- pa_tagstruct_get_boolean(t, &playing) < 0 ||
- pa_tagstruct_get_timeval(t, &local) < 0 ||
- pa_tagstruct_get_timeval(t, &remote) < 0 ||
- pa_tagstruct_gets64(t, &i->write_index) < 0 ||
- pa_tagstruct_gets64(t, &i->read_index) < 0 ||
- !pa_tagstruct_eof(t)) {
- pa_context_fail(o->context, PA_ERR_PROTOCOL);
- goto finish;
-
} else {
- o->stream->timing_info_valid = 1;
+
+ if (pa_tagstruct_get_usec(t, &i->sink_usec) < 0 ||
+ pa_tagstruct_get_usec(t, &i->source_usec) < 0 ||
+ pa_tagstruct_get_boolean(t, &playing) < 0 ||
+ pa_tagstruct_get_timeval(t, &local) < 0 ||
+ pa_tagstruct_get_timeval(t, &remote) < 0 ||
+ pa_tagstruct_gets64(t, &i->write_index) < 0 ||
+ pa_tagstruct_gets64(t, &i->read_index) < 0) {
+
+ pa_context_fail(o->context, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+
+ if (o->context->version >= 13)
+ if (pa_tagstruct_getu64(t, &underrun_for) < 0 ||
+ pa_tagstruct_getu64(t, &playing_for) < 0) {
+
+ pa_context_fail(o->context, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+
+
+ if (!pa_tagstruct_eof(t)) {
+ pa_context_fail(o->context, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+
+ o->stream->timing_info_valid = TRUE;
i->playing = (int) playing;
+ i->since_underrun = playing ? playing_for : underrun_for;
pa_gettimeofday(&now);
@@ -1096,22 +1292,22 @@ static void stream_get_timing_info_callback(pa_pdispatch *pd, uint32_t command,
else
i->transport_usec = pa_timeval_diff(&now, &remote);
- i->synchronized_clocks = 1;
+ i->synchronized_clocks = TRUE;
i->timestamp = remote;
} else {
/* clocks are not synchronized, let's estimate latency then */
i->transport_usec = pa_timeval_diff(&now, &local)/2;
- i->synchronized_clocks = 0;
+ i->synchronized_clocks = FALSE;
i->timestamp = local;
pa_timeval_add(&i->timestamp, i->transport_usec);
}
/* Invalidate read and write indexes if necessary */
if (tag < o->stream->read_index_not_before)
- i->read_index_corrupt = 1;
+ i->read_index_corrupt = TRUE;
if (tag < o->stream->write_index_not_before)
- i->write_index_corrupt = 1;
+ i->write_index_corrupt = TRUE;
if (o->stream->direction == PA_STREAM_PLAYBACK) {
/* Write index correction */
@@ -1137,11 +1333,11 @@ static void stream_get_timing_info_callback(pa_pdispatch *pd, uint32_t command,
if (o->stream->write_index_corrections[j].corrupt) {
/* A corrupting seek was made */
i->write_index = 0;
- i->write_index_corrupt = 1;
+ i->write_index_corrupt = TRUE;
} else if (o->stream->write_index_corrections[j].absolute) {
/* An absolute seek was made */
i->write_index = o->stream->write_index_corrections[j].value;
- i->write_index_corrupt = 0;
+ i->write_index_corrupt = FALSE;
} else if (!i->write_index_corrupt) {
/* A relative seek was made */
i->write_index += o->stream->write_index_corrections[j].value;
@@ -1156,25 +1352,57 @@ static void stream_get_timing_info_callback(pa_pdispatch *pd, uint32_t command,
i->read_index -= pa_memblockq_get_length(o->stream->record_memblockq);
}
- o->stream->cached_time_valid = 0;
- }
-
- o->stream->auto_timing_update_requested = 0;
/* pa_log("post corrupt w:%u r:%u\n", i->write_index_corrupt || !o->stream->timing_info_valid, i->read_index_corrupt || !o->stream->timing_info_valid); */
- /* Clear old correction entries */
- if (o->stream->direction == PA_STREAM_PLAYBACK) {
- int n;
+ /* Clear old correction entries */
+ if (o->stream->direction == PA_STREAM_PLAYBACK) {
+ int n;
+
+ for (n = 0; n < PA_MAX_WRITE_INDEX_CORRECTIONS; n++) {
+ if (!o->stream->write_index_corrections[n].valid)
+ continue;
+
+ if (o->stream->write_index_corrections[n].tag <= tag)
+ o->stream->write_index_corrections[n].valid = FALSE;
+ }
+ }
+
+ /* Update smoother */
+ if (o->stream->smoother) {
+ pa_usec_t u, x;
+
+ u = x = pa_rtclock_usec() - i->transport_usec;
- for (n = 0; n < PA_MAX_WRITE_INDEX_CORRECTIONS; n++) {
- if (!o->stream->write_index_corrections[n].valid)
- continue;
+ if (o->stream->direction == PA_STREAM_PLAYBACK &&
+ o->context->version >= 13) {
+ pa_usec_t su;
- if (o->stream->write_index_corrections[n].tag <= tag)
- o->stream->write_index_corrections[n].valid = 0;
+ /* If we weren't playing then it will take some time
+ * until the audio will actually come out through the
+ * speakers. Since we follow that timing here, we need
+ * to try to fix this up */
+
+ su = pa_bytes_to_usec(i->since_underrun, &o->stream->sample_spec);
+
+ if (su < i->sink_usec)
+ x += i->sink_usec - su;
+ }
+
+ if (!i->playing)
+ pa_smoother_pause(o->stream->smoother, x);
+
+ /* Update the smoother */
+ if ((o->stream->direction == PA_STREAM_PLAYBACK && !i->read_index_corrupt) ||
+ (o->stream->direction == PA_STREAM_RECORD && !i->write_index_corrupt))
+ pa_smoother_put(o->stream->smoother, u, calc_time(o->stream, TRUE));
+
+ if (i->playing)
+ pa_smoother_resume(o->stream->smoother, x);
}
}
+ o->stream->auto_timing_update_requested = FALSE;
+
if (o->stream->latency_update_callback)
o->stream->latency_update_callback(o->stream, o->stream->latency_update_userdata);
@@ -1223,15 +1451,15 @@ pa_operation* pa_stream_update_timing_info(pa_stream *s, pa_stream_success_cb_t
if (s->direction == PA_STREAM_PLAYBACK) {
/* Fill in initial correction data */
- o->stream->current_write_index_correction = cidx;
- o->stream->write_index_corrections[cidx].valid = 1;
- o->stream->write_index_corrections[cidx].tag = tag;
- o->stream->write_index_corrections[cidx].absolute = 0;
- o->stream->write_index_corrections[cidx].value = 0;
- o->stream->write_index_corrections[cidx].corrupt = 0;
- }
-/* pa_log("requesting update %u\n", tag); */
+ s->current_write_index_correction = cidx;
+
+ s->write_index_corrections[cidx].valid = TRUE;
+ s->write_index_corrections[cidx].absolute = FALSE;
+ s->write_index_corrections[cidx].corrupt = FALSE;
+ s->write_index_corrections[cidx].tag = tag;
+ s->write_index_corrections[cidx].value = 0;
+ }
return o;
}
@@ -1246,7 +1474,7 @@ void pa_stream_disconnect_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UN
pa_stream_ref(s);
if (command != PA_COMMAND_REPLY) {
- if (pa_context_handle_error(s->context, command, t) < 0)
+ if (pa_context_handle_error(s->context, command, t, FALSE) < 0)
goto finish;
pa_stream_set_state(s, PA_STREAM_FAILED);
@@ -1291,6 +1519,9 @@ void pa_stream_set_read_callback(pa_stream *s, pa_stream_request_cb_t cb, void *
pa_assert(s);
pa_assert(PA_REFCNT_VALUE(s) >= 1);
+ if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
+ return;
+
s->read_callback = cb;
s->read_userdata = userdata;
}
@@ -1299,6 +1530,9 @@ void pa_stream_set_write_callback(pa_stream *s, pa_stream_request_cb_t cb, void
pa_assert(s);
pa_assert(PA_REFCNT_VALUE(s) >= 1);
+ if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
+ return;
+
s->write_callback = cb;
s->write_userdata = userdata;
}
@@ -1307,6 +1541,9 @@ void pa_stream_set_state_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *
pa_assert(s);
pa_assert(PA_REFCNT_VALUE(s) >= 1);
+ if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
+ return;
+
s->state_callback = cb;
s->state_userdata = userdata;
}
@@ -1315,6 +1552,9 @@ void pa_stream_set_overflow_callback(pa_stream *s, pa_stream_notify_cb_t cb, voi
pa_assert(s);
pa_assert(PA_REFCNT_VALUE(s) >= 1);
+ if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
+ return;
+
s->overflow_callback = cb;
s->overflow_userdata = userdata;
}
@@ -1323,6 +1563,9 @@ void pa_stream_set_underflow_callback(pa_stream *s, pa_stream_notify_cb_t cb, vo
pa_assert(s);
pa_assert(PA_REFCNT_VALUE(s) >= 1);
+ if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
+ return;
+
s->underflow_callback = cb;
s->underflow_userdata = userdata;
}
@@ -1331,6 +1574,9 @@ void pa_stream_set_latency_update_callback(pa_stream *s, pa_stream_notify_cb_t c
pa_assert(s);
pa_assert(PA_REFCNT_VALUE(s) >= 1);
+ if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
+ return;
+
s->latency_update_callback = cb;
s->latency_update_userdata = userdata;
}
@@ -1339,6 +1585,9 @@ void pa_stream_set_moved_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *
pa_assert(s);
pa_assert(PA_REFCNT_VALUE(s) >= 1);
+ if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
+ return;
+
s->moved_callback = cb;
s->moved_userdata = userdata;
}
@@ -1347,10 +1596,24 @@ void pa_stream_set_suspended_callback(pa_stream *s, pa_stream_notify_cb_t cb, vo
pa_assert(s);
pa_assert(PA_REFCNT_VALUE(s) >= 1);
+ if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
+ return;
+
s->suspended_callback = cb;
s->suspended_userdata = userdata;
}
+void pa_stream_set_started_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata) {
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+ if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
+ return;
+
+ s->started_callback = cb;
+ s->started_userdata = userdata;
+}
+
void pa_stream_simple_ack_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
pa_operation *o = userdata;
int success = 1;
@@ -1363,7 +1626,7 @@ void pa_stream_simple_ack_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UN
goto finish;
if (command != PA_COMMAND_REPLY) {
- if (pa_context_handle_error(o->context, command, t) < 0)
+ if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
goto finish;
success = 0;
@@ -1406,8 +1669,18 @@ pa_operation* pa_stream_cork(pa_stream *s, int b, pa_stream_success_cb_t cb, voi
pa_pstream_send_tagstruct(s->context->pstream, t);
pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+ if (s->smoother) {
+ pa_usec_t x = pa_rtclock_usec();
+
+ if (s->timing_info_valid)
+ x += s->timing_info.transport_usec;
+
+ if (s->suspended || s->corked)
+ pa_smoother_pause(s->smoother, x);
+ }
+
if (s->direction == PA_STREAM_PLAYBACK)
- invalidate_indexes(s, 1, 0);
+ invalidate_indexes(s, TRUE, FALSE);
return o;
}
@@ -1438,23 +1711,34 @@ pa_operation* pa_stream_flush(pa_stream *s, pa_stream_success_cb_t cb, void *use
pa_assert(s);
pa_assert(PA_REFCNT_VALUE(s) >= 1);
+ PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
if ((o = stream_send_simple_command(s, s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_FLUSH_PLAYBACK_STREAM : PA_COMMAND_FLUSH_RECORD_STREAM, cb, userdata))) {
if (s->direction == PA_STREAM_PLAYBACK) {
if (s->write_index_corrections[s->current_write_index_correction].valid)
- s->write_index_corrections[s->current_write_index_correction].corrupt = 1;
+ s->write_index_corrections[s->current_write_index_correction].corrupt = TRUE;
if (s->timing_info_valid)
- s->timing_info.write_index_corrupt = 1;
+ s->timing_info.write_index_corrupt = TRUE;
if (s->buffer_attr.prebuf > 0)
- invalidate_indexes(s, 1, 0);
+ invalidate_indexes(s, TRUE, FALSE);
else
- request_auto_timing_update(s, 1);
+ request_auto_timing_update(s, TRUE);
+
+ if (s->smoother && s->buffer_attr.prebuf > 0) {
+ pa_usec_t x = pa_rtclock_usec();
+
+ if (s->timing_info_valid)
+ x += s->timing_info.transport_usec;
+
+ pa_smoother_pause(s->smoother, x);
+ }
+
} else
- invalidate_indexes(s, 0, 1);
+ invalidate_indexes(s, FALSE, TRUE);
}
return o;
@@ -1466,11 +1750,12 @@ pa_operation* pa_stream_prebuf(pa_stream *s, pa_stream_success_cb_t cb, void *us
pa_assert(s);
pa_assert(PA_REFCNT_VALUE(s) >= 1);
+ PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction == PA_STREAM_PLAYBACK, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->buffer_attr.prebuf > 0, PA_ERR_BADSTATE);
if ((o = stream_send_simple_command(s, PA_COMMAND_PREBUF_PLAYBACK_STREAM, cb, userdata)))
- invalidate_indexes(s, 1, 0);
+ invalidate_indexes(s, TRUE, FALSE);
return o;
}
@@ -1481,19 +1766,18 @@ pa_operation* pa_stream_trigger(pa_stream *s, pa_stream_success_cb_t cb, void *u
pa_assert(s);
pa_assert(PA_REFCNT_VALUE(s) >= 1);
+ PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction == PA_STREAM_PLAYBACK, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->buffer_attr.prebuf > 0, PA_ERR_BADSTATE);
if ((o = stream_send_simple_command(s, PA_COMMAND_TRIGGER_PLAYBACK_STREAM, cb, userdata)))
- invalidate_indexes(s, 1, 0);
+ invalidate_indexes(s, TRUE, FALSE);
return o;
}
pa_operation* pa_stream_set_name(pa_stream *s, const char *name, pa_stream_success_cb_t cb, void *userdata) {
pa_operation *o;
- pa_tagstruct *t;
- uint32_t tag;
pa_assert(s);
pa_assert(PA_REFCNT_VALUE(s) >= 1);
@@ -1502,22 +1786,32 @@ pa_operation* pa_stream_set_name(pa_stream *s, const char *name, pa_stream_succe
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
- o = pa_operation_new(s->context, s, (pa_operation_cb_t) cb, userdata);
+ if (s->context->version >= 13) {
+ pa_proplist *p = pa_proplist_new();
- t = pa_tagstruct_command(
- s->context,
- s->direction == PA_STREAM_RECORD ? PA_COMMAND_SET_RECORD_STREAM_NAME : PA_COMMAND_SET_PLAYBACK_STREAM_NAME,
- &tag);
- pa_tagstruct_putu32(t, s->channel);
- pa_tagstruct_puts(t, name);
- pa_pstream_send_tagstruct(s->context->pstream, t);
- pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+ pa_proplist_sets(p, PA_PROP_APPLICATION_NAME, name);
+ o = pa_stream_proplist_update(s, PA_UPDATE_REPLACE, p, cb, userdata);
+ pa_proplist_free(p);
+ } else {
+ pa_tagstruct *t;
+ uint32_t tag;
+
+ o = pa_operation_new(s->context, s, (pa_operation_cb_t) cb, userdata);
+ t = pa_tagstruct_command(
+ s->context,
+ s->direction == PA_STREAM_RECORD ? PA_COMMAND_SET_RECORD_STREAM_NAME : PA_COMMAND_SET_PLAYBACK_STREAM_NAME,
+ &tag);
+ pa_tagstruct_putu32(t, s->channel);
+ pa_tagstruct_puts(t, name);
+ pa_pstream_send_tagstruct(s->context->pstream, t);
+ pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+ }
return o;
}
int pa_stream_get_time(pa_stream *s, pa_usec_t *r_usec) {
- pa_usec_t usec = 0;
+ pa_usec_t usec;
pa_assert(s);
pa_assert(PA_REFCNT_VALUE(s) >= 1);
@@ -1528,65 +1822,10 @@ int pa_stream_get_time(pa_stream *s, pa_usec_t *r_usec) {
PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_PLAYBACK || !s->timing_info.read_index_corrupt, PA_ERR_NODATA);
PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_RECORD || !s->timing_info.write_index_corrupt, PA_ERR_NODATA);
- if (s->cached_time_valid)
- /* We alredy calculated the time value for this timing info, so let's reuse it */
- usec = s->cached_time;
- else {
- if (s->direction == PA_STREAM_PLAYBACK) {
- /* The last byte that was written into the output device
- * had this time value associated */
- usec = pa_bytes_to_usec(s->timing_info.read_index < 0 ? 0 : (uint64_t) s->timing_info.read_index, &s->sample_spec);
-
- if (!s->corked) {
- /* Because the latency info took a little time to come
- * to us, we assume that the real output time is actually
- * a little ahead */
- usec += s->timing_info.transport_usec;
-
- /* However, the output device usually maintains a buffer
- too, hence the real sample currently played is a little
- back */
- if (s->timing_info.sink_usec >= usec)
- usec = 0;
- else
- usec -= s->timing_info.sink_usec;
- }
-
- } else if (s->direction == PA_STREAM_RECORD) {
- /* The last byte written into the server side queue had
- * this time value associated */
- usec = pa_bytes_to_usec(s->timing_info.write_index < 0 ? 0 : (uint64_t) s->timing_info.write_index, &s->sample_spec);
-
- if (!s->corked) {
- /* Add transport latency */
- usec += s->timing_info.transport_usec;
-
- /* Add latency of data in device buffer */
- usec += s->timing_info.source_usec;
-
- /* If this is a monitor source, we need to correct the
- * time by the playback device buffer */
- if (s->timing_info.sink_usec >= usec)
- usec = 0;
- else
- usec -= s->timing_info.sink_usec;
- }
- }
-
- s->cached_time = usec;
- s->cached_time_valid = 1;
- }
-
- /* Interpolate if requested */
- if (s->flags & PA_STREAM_INTERPOLATE_TIMING) {
-
- /* We just add the time that passed since the latency info was
- * current */
- if (!s->corked && s->timing_info.playing) {
- struct timeval now;
- usec += pa_timeval_diff(pa_gettimeofday(&now), &s->timing_info.timestamp);
- }
- }
+ if (s->smoother)
+ usec = pa_smoother_get(s->smoother, pa_rtclock_usec());
+ else
+ usec = calc_time(s, FALSE);
/* Make sure the time runs monotonically */
if (!(s->flags & PA_STREAM_NOT_MONOTONOUS)) {
@@ -1687,7 +1926,7 @@ const pa_buffer_attr* pa_stream_get_buffer_attr(pa_stream *s) {
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
- PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->context->version >= 9, PA_ERR_NODATA);
+ PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->context->version >= 9, PA_ERR_NOTSUPPORTED);
return &s->buffer_attr;
}
@@ -1704,7 +1943,7 @@ static void stream_set_buffer_attr_callback(pa_pdispatch *pd, uint32_t command,
goto finish;
if (command != PA_COMMAND_REPLY) {
- if (pa_context_handle_error(o->context, command, t) < 0)
+ if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
goto finish;
success = 0;
@@ -1730,8 +1969,6 @@ static void stream_set_buffer_attr_callback(pa_pdispatch *pd, uint32_t command,
pa_context_fail(o->context, PA_ERR_PROTOCOL);
goto finish;
}
-
- o->stream->manual_buffer_attr = TRUE;
}
if (o->callback) {
@@ -1822,6 +2059,16 @@ int pa_stream_is_suspended(pa_stream *s) {
return s->suspended;
}
+int pa_stream_is_corked(pa_stream *s) {
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+ PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
+
+ return s->corked;
+}
+
static void stream_update_sample_rate_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
pa_operation *o = userdata;
int success = 1;
@@ -1834,7 +2081,7 @@ static void stream_update_sample_rate_callback(pa_pdispatch *pd, uint32_t comman
goto finish;
if (command != PA_COMMAND_REPLY) {
- if (pa_context_handle_error(o->context, command, t) < 0)
+ if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
goto finish;
success = 0;
diff --git a/src/pulse/stream.h b/src/pulse/stream.h
index 69943a70..ebb45f2b 100644
--- a/src/pulse/stream.h
+++ b/src/pulse/stream.h
@@ -339,6 +339,10 @@ const char *pa_stream_get_device_name(pa_stream *s);
* server is older than 0.9.8. \since 0.9.8 */
int pa_stream_is_suspended(pa_stream *s);
+/** Return 1 if the this stream has been corked. This will return 0 if
+ * not, and negative on error. \since 0.9.11 */
+int pa_stream_is_corked(pa_stream *s);
+
/** Connect the stream to a sink */
int pa_stream_connect_playback(
pa_stream *s /**< The stream to connect to a sink */,
@@ -368,7 +372,7 @@ int pa_stream_disconnect(pa_stream *s);
int pa_stream_write(
pa_stream *p /**< The stream to use */,
const void *data /**< The data to write */,
- size_t bytes /**< The length of the data to write in bytes*/,
+ size_t nbytes /**< The length of the data to write in bytes*/,
pa_free_cb_t free_cb /**< A cleanup routine for the data or NULL to request an internal copy */,
int64_t offset, /**< Offset for seeking, must be 0 for upload streams */
pa_seek_mode_t seek /**< Seek mode, must be PA_SEEK_RELATIVE for upload streams */);
@@ -381,7 +385,7 @@ int pa_stream_write(
int pa_stream_peek(
pa_stream *p /**< The stream to use */,
const void **data /**< Pointer to pointer that will point to data */,
- size_t *bytes /**< The length of the data read in bytes */);
+ size_t *nbytes /**< The length of the data read in bytes */);
/** Remove the current fragment on record streams. It is invalid to do this without first
* calling pa_stream_peek(). */
@@ -419,6 +423,13 @@ void pa_stream_set_overflow_callback(pa_stream *p, pa_stream_notify_cb_t cb, voi
/** Set the callback function that is called when a buffer underflow happens. (Only for playback streams) */
void pa_stream_set_underflow_callback(pa_stream *p, pa_stream_notify_cb_t cb, void *userdata);
+/** Set the callback function that is called when a the server starts
+ * playback after an underrun or on initial startup. This only informs
+ * that audio is flowing again, it is no indication that audio startet
+ * to reach the speakers already. (Only for playback streams). \since
+ * 0.9.11 */
+void pa_stream_set_started_callback(pa_stream *p, pa_stream_notify_cb_t cb, void *userdata);
+
/** Set the callback function that is called whenever a latency
* information update happens. Useful on PA_STREAM_AUTO_TIMING_UPDATE
* streams only. (Only for playback streams) */
diff --git a/src/pulsecore/core-util.c b/src/pulsecore/core-util.c
index 28885b2c..df110966 100644
--- a/src/pulsecore/core-util.c
+++ b/src/pulsecore/core-util.c
@@ -41,6 +41,7 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
+#include <dirent.h>
#ifdef HAVE_STRTOF_L
#include <locale.h>
@@ -103,12 +104,6 @@
#define MSG_NOSIGNAL 0
#endif
-#ifndef OS_IS_WIN32
-#define PA_USER_RUNTIME_PATH_PREFIX "/tmp/pulse-"
-#else
-#define PA_USER_RUNTIME_PATH_PREFIX "%TEMP%\\pulse-"
-#endif
-
#ifdef OS_IS_WIN32
#define PULSE_ROOTENV "PULSE_ROOT"
@@ -221,7 +216,7 @@ int pa_make_secure_dir(const char* dir, mode_t m, uid_t uid, gid_t gid) {
goto fail;
}
#else
- pa_log_warn("secure directory creation not supported on Win32.");
+ pa_log_warn("Secure directory creation not supported on Win32.");
#endif
return 0;
@@ -557,6 +552,82 @@ int pa_make_realtime(int rtprio) {
#endif
}
+/* This is merely used for giving the user a hint. This is not correct
+ * for anything security related */
+pa_bool_t pa_can_realtime(void) {
+
+ if (geteuid() == 0)
+ return TRUE;
+
+#if defined(HAVE_SYS_RESOURCE_H) && defined(RLIMIT_RTPRIO)
+ {
+ struct rlimit rl;
+
+ if (getrlimit(RLIMIT_RTPRIO, &rl) >= 0)
+ if (rl.rlim_cur > 0 || rl.rlim_cur == RLIM_INFINITY)
+ return TRUE;
+ }
+#endif
+
+#if defined(HAVE_SYS_CAPABILITY_H) && defined(CAP_SYS_NICE)
+ {
+ cap_t cap;
+
+ if ((cap = cap_get_proc())) {
+ cap_flag_value_t flag = CAP_CLEAR;
+
+ if (cap_get_flag(cap, CAP_SYS_NICE, CAP_EFFECTIVE, &flag) >= 0)
+ if (flag == CAP_SET) {
+ cap_free(cap);
+ return TRUE;
+ }
+
+ cap_free(cap);
+ }
+ }
+#endif
+
+ return FALSE;
+}
+
+/* This is merely used for giving the user a hint. This is not correct
+ * for anything security related */
+pa_bool_t pa_can_high_priority(void) {
+
+ if (geteuid() == 0)
+ return TRUE;
+
+#if defined(HAVE_SYS_RESOURCE_H) && defined(RLIMIT_RTPRIO)
+ {
+ struct rlimit rl;
+
+ if (getrlimit(RLIMIT_NICE, &rl) >= 0)
+ if (rl.rlim_cur >= 21 || rl.rlim_cur == RLIM_INFINITY)
+ return TRUE;
+ }
+#endif
+
+#if defined(HAVE_SYS_CAPABILITY_H) && defined(CAP_SYS_NICE)
+ {
+ cap_t cap;
+
+ if ((cap = cap_get_proc())) {
+ cap_flag_value_t flag = CAP_CLEAR;
+
+ if (cap_get_flag(cap, CAP_SYS_NICE, CAP_EFFECTIVE, &flag) >= 0)
+ if (flag == CAP_SET) {
+ cap_free(cap);
+ return TRUE;
+ }
+
+ cap_free(cap);
+ }
+ }
+#endif
+
+ return FALSE;
+}
+
/* Raise the priority of the current process as much as possible that
* is <= the specified nice level..*/
int pa_raise_priority(int nice_level) {
@@ -612,6 +683,7 @@ void pa_reset_priority(void) {
/* Try to parse a boolean string value.*/
int pa_parse_boolean(const char *v) {
+ pa_assert(v);
if (!strcmp(v, "1") || v[0] == 'y' || v[0] == 'Y' || v[0] == 't' || v[0] == 'T' || !strcasecmp(v, "on"))
return 1;
@@ -1093,11 +1165,11 @@ int pa_unlock_lockfile(const char *fn, int fd) {
return r;
}
-char *pa_get_state_dir(void) {
+char *pa_get_runtime_dir(void) {
const char *e;
char *d;
- if ((e = getenv("PULSE_STATE_PATH")))
+ if ((e = getenv("PULSE_RUNTIME_PATH")))
d = pa_xstrdup(e);
else {
char h[PATH_MAX];
@@ -1107,19 +1179,15 @@ char *pa_get_state_dir(void) {
return NULL;
}
- d = pa_sprintf_malloc("%s/.pulse", h);
+ d = pa_sprintf_malloc("%s" PA_PATH_SEP ".pulse", h);
}
- mkdir(d, 0755);
-
- if (access(d, W_OK) == 0)
- return d;
-
- pa_log_error("Failed to set up state directory %s", d);
-
- pa_xfree(d);
+ if (pa_make_secure_dir(d, 0700, (pid_t) -1, (pid_t) -1) < 0) {
+ pa_log_error("Failed to create secure directory: %s", pa_cstrerror(errno));
+ return NULL;
+ }
- return NULL;
+ return d;
}
/* Try to open a configuration file. If "env" is specified, open the
@@ -1128,10 +1196,8 @@ char *pa_get_state_dir(void) {
* file system. If "result" is non-NULL, a pointer to a newly
* allocated buffer containing the used configuration file is
* stored there.*/
-FILE *pa_open_config_file(const char *global, const char *local, const char *env, char **result, const char *mode) {
+FILE *pa_open_config_file(const char *global, const char *local, const char *env, char **result) {
const char *fn;
- char h[PATH_MAX];
-
#ifdef OS_IS_WIN32
char buf[PATH_MAX];
@@ -1140,75 +1206,152 @@ FILE *pa_open_config_file(const char *global, const char *local, const char *env
#endif
if (env && (fn = getenv(env))) {
+ FILE *f;
+
#ifdef OS_IS_WIN32
if (!ExpandEnvironmentStrings(fn, buf, PATH_MAX))
return NULL;
fn = buf;
#endif
- if (result)
- *result = pa_xstrdup(fn);
+ if ((f = fopen(fn, "r"))) {
+ if (result)
+ *result = pa_xstrdup(fn);
+
+ return f;
+ }
- return fopen(fn, mode);
+ pa_log_warn("Failed to open configuration file '%s': %s", fn, pa_cstrerror(errno));
+ return NULL;
}
if (local) {
const char *e;
- char *lfn = NULL;
+ char *lfn;
+ char h[PATH_MAX];
+ FILE *f;
if ((e = getenv("PULSE_CONFIG_PATH")))
- fn = lfn = pa_sprintf_malloc("%s/%s", e, local);
- else if (pa_get_home_dir(h, sizeof(h))) {
- char *d;
+ fn = lfn = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", e, local);
+ else if (pa_get_home_dir(h, sizeof(h)))
+ fn = lfn = pa_sprintf_malloc("%s" PA_PATH_SEP ".pulse" PA_PATH_SEP "%s", h, local);
- d = pa_sprintf_malloc("%s/.pulse", h);
- mkdir(d, 0755);
- pa_xfree(d);
+#ifdef OS_IS_WIN32
+ if (!ExpandEnvironmentStrings(lfn, buf, PATH_MAX)) {
+ pa_xfree(lfn);
+ return NULL;
+ }
+ fn = buf;
+#endif
+
+ if ((f = fopen(fn, "r"))) {
+ if (result)
+ *result = pa_xstrdup(fn);
+
+ pa_xfree(lfn);
+ return f;
+ }
- fn = lfn = pa_sprintf_malloc("%s/.pulse/%s", h, local);
+ if (errno != ENOENT) {
+ pa_log_warn("Failed to open configuration file '%s': %s", fn, pa_cstrerror(errno));
+ pa_xfree(lfn);
+ return NULL;
}
- if (lfn) {
- FILE *f;
+ pa_xfree(lfn);
+ }
+
+ if (global) {
+ FILE *f;
#ifdef OS_IS_WIN32
- if (!ExpandEnvironmentStrings(lfn, buf, PATH_MAX))
- return NULL;
- fn = buf;
+ if (!ExpandEnvironmentStrings(global, buf, PATH_MAX))
+ return NULL;
+ global = buf;
#endif
- f = fopen(fn, mode);
- if (f != NULL) {
- if (result)
- *result = pa_xstrdup(fn);
- pa_xfree(lfn);
- return f;
- }
+ if ((f = fopen(global, "r"))) {
- if (errno != ENOENT)
- pa_log_warn("Failed to open configuration file '%s': %s", lfn, pa_cstrerror(errno));
+ if (result)
+ *result = pa_xstrdup(global);
- pa_xfree(lfn);
+ return f;
}
- }
-
- if (!global) {
- if (result)
- *result = NULL;
+ } else
errno = ENOENT;
+
+ return NULL;
+}
+
+char *pa_find_config_file(const char *global, const char *local, const char *env) {
+ const char *fn;
+#ifdef OS_IS_WIN32
+ char buf[PATH_MAX];
+
+ if (!getenv(PULSE_ROOTENV))
+ pa_set_root(NULL);
+#endif
+
+ if (env && (fn = getenv(env))) {
+#ifdef OS_IS_WIN32
+ if (!ExpandEnvironmentStrings(fn, buf, PATH_MAX))
+ return NULL;
+ fn = buf;
+#endif
+
+ if (access(fn, R_OK) == 0)
+ return pa_xstrdup(fn);
+
+ pa_log_warn("Failed to access configuration file '%s': %s", fn, pa_cstrerror(errno));
return NULL;
}
+ if (local) {
+ const char *e;
+ char *lfn;
+ char h[PATH_MAX];
+
+ if ((e = getenv("PULSE_CONFIG_PATH")))
+ fn = lfn = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", e, local);
+ else if (pa_get_home_dir(h, sizeof(h)))
+ fn = lfn = pa_sprintf_malloc("%s" PA_PATH_SEP ".pulse" PA_PATH_SEP "%s", h, local);
+
#ifdef OS_IS_WIN32
- if (!ExpandEnvironmentStrings(global, buf, PATH_MAX))
- return NULL;
- global = buf;
+ if (!ExpandEnvironmentStrings(lfn, buf, PATH_MAX)) {
+ pa_xfree(lfn);
+ return NULL;
+ }
+ fn = buf;
#endif
- if (result)
- *result = pa_xstrdup(global);
+ if (access(fn, R_OK) == 0) {
+ char *r = pa_xstrdup(fn);
+ pa_xfree(lfn);
+ return r;
+ }
+
+ if (errno != ENOENT) {
+ pa_log_warn("Failed to access configuration file '%s': %s", fn, pa_cstrerror(errno));
+ pa_xfree(lfn);
+ return NULL;
+ }
+
+ pa_xfree(lfn);
+ }
+
+ if (global) {
+#ifdef OS_IS_WIN32
+ if (!ExpandEnvironmentStrings(global, buf, PATH_MAX))
+ return NULL;
+ global = buf;
+#endif
+
+ if (access(fn, R_OK) == 0)
+ return pa_xstrdup(global);
+ } else
+ errno = ENOENT;
- return fopen(global, mode);
+ return NULL;
}
/* Format the specified data as a hexademical string */
@@ -1299,45 +1442,51 @@ int pa_endswith(const char *s, const char *sfx) {
return l1 >= l2 && strcmp(s+l1-l2, sfx) == 0;
}
-/* if fn is null return the PulseAudio run time path in s (/tmp/pulse)
- * if fn is non-null and starts with / return fn in s
- * otherwise append fn to the run time path and return it in s */
-char *pa_runtime_path(const char *fn, char *s, size_t l) {
- const char *e;
+pa_bool_t pa_is_path_absolute(const char *fn) {
+ pa_assert(fn);
#ifndef OS_IS_WIN32
- if (fn && *fn == '/')
+ return *fn == '/';
#else
- if (fn && strlen(fn) >= 3 && isalpha(fn[0]) && fn[1] == ':' && fn[2] == '\\')
+ return strlen(fn) >= 3 && isalpha(fn[0]) && fn[1] == ':' && fn[2] == '\\';
#endif
- return pa_strlcpy(s, fn, l);
+}
- if ((e = getenv("PULSE_RUNTIME_PATH"))) {
+char *pa_make_path_absolute(const char *p) {
+ char *r;
+ char *cwd;
- if (fn)
- pa_snprintf(s, l, "%s%c%s", e, PA_PATH_SEP_CHAR, fn);
- else
- pa_snprintf(s, l, "%s", e);
+ pa_assert(p);
- } else {
- char u[256];
+ if (pa_is_path_absolute(p))
+ return pa_xstrdup(p);
- if (fn)
- pa_snprintf(s, l, "%s%s%c%s", PA_USER_RUNTIME_PATH_PREFIX, pa_get_user_name(u, sizeof(u)), PA_PATH_SEP_CHAR, fn);
- else
- pa_snprintf(s, l, "%s%s", PA_USER_RUNTIME_PATH_PREFIX, pa_get_user_name(u, sizeof(u)));
- }
+ if (!(cwd = pa_getcwd()))
+ return pa_xstrdup(p);
+ r = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", cwd, p);
+ pa_xfree(cwd);
+ return r;
+}
-#ifdef OS_IS_WIN32
- {
- char buf[l];
- strcpy(buf, s);
- ExpandEnvironmentStrings(buf, s, l);
- }
-#endif
+/* if fn is null return the PulseAudio run time path in s (~/.pulse)
+ * if fn is non-null and starts with / return fn
+ * otherwise append fn to the run time path and return it */
+char *pa_runtime_path(const char *fn) {
+ char *rtp;
- return s;
+ if (pa_is_path_absolute(fn))
+ return pa_xstrdup(fn);
+
+ rtp = pa_get_runtime_dir();
+
+ if (fn) {
+ char *r;
+ r = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", rtp, fn);
+ pa_xfree(rtp);
+ return r;
+ } else
+ return rtp;
}
/* Convert the string s to a signed integer in *ret_i */
@@ -1484,23 +1633,6 @@ char *pa_getcwd(void) {
}
}
-char *pa_make_path_absolute(const char *p) {
- char *r;
- char *cwd;
-
- pa_assert(p);
-
- if (p[0] == '/')
- return pa_xstrdup(p);
-
- if (!(cwd = pa_getcwd()))
- return pa_xstrdup(p);
-
- r = pa_sprintf_malloc("%s/%s", cwd, p);
- pa_xfree(cwd);
- return r;
-}
-
void *pa_will_need(const void *p, size_t l) {
#ifdef RLIMIT_MEMLOCK
struct rlimit rlim;
@@ -1606,3 +1738,249 @@ char *pa_readlink(const char *p) {
l *= 2;
}
}
+
+int pa_close_all(int except_fd, ...) {
+ va_list ap;
+ int n = 0, i, r;
+ int *p;
+
+ va_start(ap, except_fd);
+
+ if (except_fd >= 0)
+ for (n = 1; va_arg(ap, int) >= 0; n++)
+ ;
+
+ va_end(ap);
+
+ p = pa_xnew(int, n+1);
+
+ va_start(ap, except_fd);
+
+ i = 0;
+ if (except_fd >= 0) {
+ p[i++] = except_fd;
+
+ while ((p[i++] = va_arg(ap, int)) >= 0)
+ ;
+ }
+ p[i] = -1;
+
+ va_end(ap);
+
+ r = pa_close_allv(p);
+ free(p);
+
+ return r;
+}
+
+int pa_close_allv(const int except_fds[]) {
+ struct rlimit rl;
+ int fd;
+ int saved_errno;
+
+#ifdef __linux__
+
+ DIR *d;
+
+ if ((d = opendir("/proc/self/fd"))) {
+
+ struct dirent *de;
+
+ while ((de = readdir(d))) {
+ long l;
+ char *e = NULL;
+ int i;
+
+ if (de->d_name[0] == '.')
+ continue;
+
+ errno = 0;
+ l = strtol(de->d_name, &e, 10);
+ if (errno != 0 || !e || *e) {
+ closedir(d);
+ errno = EINVAL;
+ return -1;
+ }
+
+ fd = (int) l;
+
+ if ((long) fd != l) {
+ closedir(d);
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (fd <= 3)
+ continue;
+
+ if (fd == dirfd(d))
+ continue;
+
+ for (i = 0; except_fds[i] >= 0; i++)
+ if (except_fds[i] == fd)
+ continue;
+
+ if (close(fd) < 0) {
+ saved_errno = errno;
+ closedir(d);
+ errno = saved_errno;
+
+ return -1;
+ }
+ }
+
+ closedir(d);
+ return 0;
+ }
+
+#endif
+
+ if (getrlimit(RLIMIT_NOFILE, &rl) < 0)
+ return -1;
+
+ for (fd = 0; fd < (int) rl.rlim_max; fd++) {
+ int i;
+
+ if (fd <= 3)
+ continue;
+
+ for (i = 0; except_fds[i] >= 0; i++)
+ if (except_fds[i] == fd)
+ continue;
+
+ if (close(fd) < 0 && errno != EBADF)
+ return -1;
+ }
+
+ return 0;
+}
+
+int pa_unblock_sigs(int except, ...) {
+ va_list ap;
+ int n = 0, i, r;
+ int *p;
+
+ va_start(ap, except);
+
+ if (except >= 1)
+ for (n = 1; va_arg(ap, int) >= 0; n++)
+ ;
+
+ va_end(ap);
+
+ p = pa_xnew(int, n+1);
+
+ va_start(ap, except);
+
+ i = 0;
+ if (except >= 1) {
+ p[i++] = except;
+
+ while ((p[i++] = va_arg(ap, int)) >= 0)
+ ;
+ }
+ p[i] = -1;
+
+ va_end(ap);
+
+ r = pa_unblock_sigsv(p);
+ pa_xfree(p);
+
+ return r;
+}
+
+int pa_unblock_sigsv(const int except[]) {
+ int i;
+ sigset_t ss;
+
+ if (sigemptyset(&ss) < 0)
+ return -1;
+
+ for (i = 0; except[i] > 0; i++)
+ if (sigaddset(&ss, except[i]) < 0)
+ return -1;
+
+ return sigprocmask(SIG_SETMASK, &ss, NULL);
+}
+
+int pa_reset_sigs(int except, ...) {
+ va_list ap;
+ int n = 0, i, r;
+ int *p;
+
+ va_start(ap, except);
+
+ if (except >= 1)
+ for (n = 1; va_arg(ap, int) >= 0; n++)
+ ;
+
+ va_end(ap);
+
+ p = pa_xnew(int, n+1);
+
+ va_start(ap, except);
+
+ i = 0;
+ if (except >= 1) {
+ p[i++] = except;
+
+ while ((p[i++] = va_arg(ap, int)) >= 0)
+ ;
+ }
+ p[i] = -1;
+
+ va_end(ap);
+
+ r = pa_reset_sigsv(p);
+ pa_xfree(p);
+
+ return r;
+}
+
+int pa_reset_sigsv(const int except[]) {
+ int sig;
+
+ for (sig = 1; sig < _NSIG; sig++) {
+ int reset = 1;
+
+ switch (sig) {
+ case SIGKILL:
+ case SIGSTOP:
+ reset = 0;
+ break;
+
+ default: {
+ int i;
+
+ for (i = 0; except[i] > 0; i++) {
+ if (sig == except[i]) {
+ reset = 0;
+ break;
+ }
+ }
+ }
+ }
+
+ if (reset) {
+ struct sigaction sa;
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = SIG_DFL;
+
+ /* On Linux the first two RT signals are reserved by
+ * glibc, and sigaction() will return EINVAL for them. */
+ if ((sigaction(sig, &sa, NULL) < 0))
+ if (errno != EINVAL)
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+void pa_set_env(const char *key, const char *value) {
+ pa_assert(key);
+ pa_assert(value);
+
+ putenv(pa_sprintf_malloc("%s=%s", key, value));
+}
diff --git a/src/pulsecore/core-util.h b/src/pulsecore/core-util.h
index d5c0a3f6..49315b55 100644
--- a/src/pulsecore/core-util.h
+++ b/src/pulsecore/core-util.h
@@ -30,11 +30,27 @@
#include <stdarg.h>
#include <stdio.h>
+#ifdef HAVE_SYS_RESOURCE_H
+#include <sys/resource.h>
+#endif
+
#include <pulse/gccmacro.h>
#include <pulsecore/macro.h>
struct timeval;
+/* These resource limits are pretty new on Linux, let's define them
+ * here manually, in case the kernel is newer than the glibc */
+#if !defined(RLIMIT_NICE) && defined(__linux__)
+#define RLIMIT_NICE 13
+#endif
+#if !defined(RLIMIT_RTPRIO) && defined(__linux__)
+#define RLIMIT_RTPRIO 14
+#endif
+#if !defined(RLIMIT_RTTIME) && defined(__linux__)
+#define RLIMIT_RTTIME 15
+#endif
+
void pa_make_fd_nonblock(int fd);
void pa_make_fd_cloexec(int fd);
@@ -61,6 +77,9 @@ int pa_make_realtime(int rtprio);
int pa_raise_priority(int nice_level);
void pa_reset_priority(void);
+pa_bool_t pa_can_realtime(void);
+pa_bool_t pa_can_high_priority(void);
+
int pa_parse_boolean(const char *s) PA_GCC_PURE;
static inline const char *pa_yes_no(pa_bool_t b) {
@@ -71,6 +90,10 @@ static inline const char *pa_strnull(const char *x) {
return x ? x : "(null)";
}
+static inline const char *pa_strempty(const char *x) {
+ return x ? x : "";
+}
+
char *pa_split(const char *c, const char*delimiters, const char **state);
char *pa_split_spaces(const char *c, const char **state);
@@ -88,15 +111,17 @@ int pa_lock_fd(int fd, int b);
int pa_lock_lockfile(const char *fn);
int pa_unlock_lockfile(const char *fn, int fd);
-FILE *pa_open_config_file(const char *global, const char *local, const char *env, char **result, const char *mode);
-
char *pa_hexstr(const uint8_t* d, size_t dlength, char *s, size_t slength);
size_t pa_parsehex(const char *p, uint8_t *d, size_t dlength);
int pa_startswith(const char *s, const char *pfx) PA_GCC_PURE;
int pa_endswith(const char *s, const char *sfx) PA_GCC_PURE;
-char *pa_runtime_path(const char *fn, char *s, size_t l);
+FILE *pa_open_config_file(const char *global, const char *local, const char *env, char **result);
+char* pa_find_config_file(const char *global, const char *local, const char *env);
+
+char *pa_get_runtime_dir(void);
+char *pa_runtime_path(const char *fn);
int pa_atoi(const char *s, int32_t *ret_i);
int pa_atou(const char *s, uint32_t *ret_u);
@@ -108,6 +133,7 @@ char *pa_truncate_utf8(char *c, size_t l);
char *pa_getcwd(void);
char *pa_make_path_absolute(const char *p);
+pa_bool_t pa_is_path_absolute(const char *p);
void *pa_will_need(const void *p, size_t l);
@@ -133,6 +159,13 @@ void pa_close_pipe(int fds[2]);
char *pa_readlink(const char *p);
-char *pa_get_state_dir(void);
+int pa_close_all(int except_fd, ...);
+int pa_close_allv(const int except_fds[]);
+int pa_unblock_sigs(int except, ...);
+int pa_unblock_sigsv(const int except[]);
+int pa_reset_sigs(int except, ...);
+int pa_reset_sigsv(const int except[]);
+
+void pa_set_env(const char *key, const char *value);
#endif
diff --git a/src/pulsecore/native-common.h b/src/pulsecore/native-common.h
index 51f2b309..56f9037e 100644
--- a/src/pulsecore/native-common.h
+++ b/src/pulsecore/native-common.h
@@ -147,6 +147,9 @@ enum {
PA_COMMAND_REMOVE_PLAYBACK_STREAM_PROPLIST,
PA_COMMAND_REMOVE_CLIENT_PROPLIST,
+ /* SERVER->CLIENT */
+ PA_COMMAND_STARTED,
+
PA_COMMAND_MAX
};
diff --git a/src/pulsecore/pid.c b/src/pulsecore/pid.c
index f3c9faaa..2ff132bb 100644
--- a/src/pulsecore/pid.c
+++ b/src/pulsecore/pid.c
@@ -144,16 +144,16 @@ fail:
int pa_pid_file_create(void) {
int fd = -1;
int ret = -1;
- char fn[PATH_MAX];
char t[20];
pid_t pid;
size_t l;
+ char *fn;
#ifdef OS_IS_WIN32
HANDLE process;
#endif
- pa_runtime_path("pid", fn, sizeof(fn));
+ fn = pa_runtime_path("pid");
if ((fd = open_pid_file(fn, O_CREAT|O_RDWR)) < 0)
goto fail;
@@ -200,17 +200,19 @@ fail:
}
}
+ pa_xfree(fn);
+
return ret;
}
/* Remove the PID file, if it is ours */
int pa_pid_file_remove(void) {
int fd = -1;
- char fn[PATH_MAX];
+ char *fn;
int ret = -1;
pid_t pid;
- pa_runtime_path("pid", fn, sizeof(fn));
+ fn = pa_runtime_path("pid");
if ((fd = open_pid_file(fn, O_RDWR)) < 0) {
pa_log_warn("Failed to open PID file '%s': %s", fn, pa_cstrerror(errno));
@@ -254,6 +256,8 @@ fail:
}
}
+ pa_xfree(fn);
+
return ret;
}
@@ -272,7 +276,7 @@ int pa_pid_file_check_running(pid_t *pid, const char *binary_name) {
* process. */
int pa_pid_file_kill(int sig, pid_t *pid, const char *binary_name) {
int fd = -1;
- char fn[PATH_MAX];
+ char *fn;
int ret = -1;
pid_t _pid;
#ifdef __linux__
@@ -281,7 +285,7 @@ int pa_pid_file_kill(int sig, pid_t *pid, const char *binary_name) {
if (!pid)
pid = &_pid;
- pa_runtime_path("pid", fn, sizeof(fn));
+ fn = pa_runtime_path("pid");
if ((fd = open_pid_file(fn, O_RDONLY)) < 0)
goto fail;
@@ -296,7 +300,7 @@ int pa_pid_file_kill(int sig, pid_t *pid, const char *binary_name) {
if ((e = pa_readlink(fn))) {
char *f = pa_path_get_filename(e);
if (strcmp(f, binary_name)
-#if defined(__OPTIMIZE__)
+#if !defined(__OPTIMIZE__)
/* libtool likes to rename our binary names ... */
&& !(pa_startswith(f, "lt-") && strcmp(f+3, binary_name) == 0)
#endif
@@ -319,6 +323,8 @@ fail:
pa_xfree(e);
#endif
+ pa_xfree(fn);
+
return ret;
}
diff --git a/src/pulsecore/protocol-cli.c b/src/pulsecore/protocol-cli.c
index ceb6ae4d..2f797a14 100644
--- a/src/pulsecore/protocol-cli.c
+++ b/src/pulsecore/protocol-cli.c
@@ -82,7 +82,7 @@ pa_protocol_cli* pa_protocol_cli_new(pa_core *core, pa_socket_server *server, pa
p = pa_xnew(pa_protocol_cli, 1);
p->module = m;
p->core = core;
- p->server = server;
+ p->server = pa_socket_server_ref(server);
p->connections = pa_idxset_new(NULL, NULL);
pa_socket_server_set_callback(p->server, on_connection, p);
diff --git a/src/pulsecore/protocol-esound.c b/src/pulsecore/protocol-esound.c
index 59a4208f..388808a5 100644
--- a/src/pulsecore/protocol-esound.c
+++ b/src/pulsecore/protocol-esound.c
@@ -1433,7 +1433,7 @@ pa_protocol_esound* pa_protocol_esound_new(pa_core*core, pa_socket_server *serve
p->core = core;
p->module = m;
p->public = public;
- p->server = server;
+ p->server = pa_socket_server_ref(server);
pa_socket_server_set_callback(p->server, on_connection, p);
p->connections = pa_idxset_new(NULL, NULL);
diff --git a/src/pulsecore/protocol-http.c b/src/pulsecore/protocol-http.c
index 589eba4f..bc2e9af6 100644
--- a/src/pulsecore/protocol-http.c
+++ b/src/pulsecore/protocol-http.c
@@ -255,7 +255,7 @@ pa_protocol_http* pa_protocol_http_new(pa_core *core, pa_socket_server *server,
p = pa_xnew(pa_protocol_http, 1);
p->module = m;
p->core = core;
- p->server = server;
+ p->server = pa_socket_server_ref(server);
p->connections = pa_idxset_new(NULL, NULL);
pa_socket_server_set_callback(p->server, on_connection, p);
diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c
index ca14b955..5fee4ccf 100644
--- a/src/pulsecore/protocol-native.c
+++ b/src/pulsecore/protocol-native.c
@@ -105,7 +105,6 @@ typedef struct playback_stream {
pa_bool_t drain_request;
uint32_t drain_tag;
uint32_t syncid;
- uint64_t underrun; /* length of underrun */
pa_atomic_t missing;
size_t minreq;
@@ -193,7 +192,8 @@ enum {
PLAYBACK_STREAM_MESSAGE_REQUEST_DATA, /* data requested from sink input from the main loop */
PLAYBACK_STREAM_MESSAGE_UNDERFLOW,
PLAYBACK_STREAM_MESSAGE_OVERFLOW,
- PLAYBACK_STREAM_MESSAGE_DRAIN_ACK
+ PLAYBACK_STREAM_MESSAGE_DRAIN_ACK,
+ PLAYBACK_STREAM_MESSAGE_STARTED
};
enum {
@@ -689,10 +689,24 @@ static int playback_stream_process_msg(pa_msgobject *o, int code, void*userdata,
break;
}
+ case PLAYBACK_STREAM_MESSAGE_STARTED:
+
+ if (s->connection->version >= 13) {
+ pa_tagstruct *t;
+
+ /* Notify the user we're overflowed*/
+ t = pa_tagstruct_new(NULL, 0);
+ pa_tagstruct_putu32(t, PA_COMMAND_STARTED);
+ pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
+ pa_tagstruct_putu32(t, s->index);
+ pa_pstream_send_tagstruct(s->connection->pstream, t);
+ }
+
+ break;
+
case PLAYBACK_STREAM_MESSAGE_DRAIN_ACK:
pa_pstream_send_simple_ack(s->connection->pstream, PA_PTR_TO_UINT(userdata));
break;
-
}
return 0;
@@ -886,7 +900,6 @@ static playback_stream* playback_stream_new(
s->connection = c;
s->syncid = syncid;
s->sink_input = sink_input;
- s->underrun = (uint64_t) -1;
s->sink_input->parent.process_msg = sink_input_process_msg;
s->sink_input->pop = sink_input_pop_cb;
@@ -1091,7 +1104,7 @@ static void handle_seek(playback_stream *s, int64_t indexw) {
/* pa_log("handle_seek: %llu -- %i", (unsigned long long) s->underrun, pa_memblockq_is_readable(s->memblockq)); */
- if (s->underrun != 0) {
+ if (s->sink_input->thread_info.underrun_for > 0) {
/* pa_log("%lu vs. %lu", (unsigned long) pa_memblockq_get_length(s->memblockq), (unsigned long) pa_memblockq_get_prebuf(s->memblockq)); */
@@ -1099,13 +1112,13 @@ static void handle_seek(playback_stream *s, int64_t indexw) {
size_t u = pa_memblockq_get_length(s->memblockq);
- if (u >= s->underrun)
- u = s->underrun;
+ if (u >= s->sink_input->thread_info.underrun_for)
+ u = s->sink_input->thread_info.underrun_for;
/* We just ended an underrun, let's ask the sink
* to rewrite */
- s->sink_input->thread_info.ignore_rewind = TRUE;
- pa_sink_input_request_rewind(s->sink_input, u, TRUE);
+
+ pa_sink_input_request_rewind(s->sink_input, u, TRUE, TRUE);
}
} else {
@@ -1117,7 +1130,7 @@ static void handle_seek(playback_stream *s, int64_t indexw) {
/* OK, the sink already asked for this data, so
* let's have it usk us again */
- pa_sink_input_request_rewind(s->sink_input, indexr - indexw, FALSE);
+ pa_sink_input_request_rewind(s->sink_input, indexr - indexw, FALSE, FALSE);
}
request_bytes(s);
@@ -1272,12 +1285,9 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk
if (s->drain_request && pa_sink_input_safe_to_remove(i)) {
s->drain_request = FALSE;
pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_DRAIN_ACK, PA_UINT_TO_PTR(s->drain_tag), 0, NULL, NULL);
- } else if (s->underrun == 0)
+ } else if (i->thread_info.playing_for > 0)
pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_UNDERFLOW, NULL, 0, NULL, NULL);
- if (s->underrun != (size_t) -1)
- s->underrun += nbytes;
-
/* pa_log("added %llu bytes, total is %llu", (unsigned long long) nbytes, (unsigned long long) s->underrun); */
request_bytes(s);
@@ -1287,7 +1297,8 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk
/* pa_log("NOTUNDERRUN"); */
- s->underrun = 0;
+ 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);
pa_memblockq_drop(s->memblockq, chunk->length);
request_bytes(s);
@@ -1303,7 +1314,7 @@ static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
playback_stream_assert_ref(s);
/* If we are in an underrun, then we don't rewind */
- if (s->underrun != 0)
+ if (i->thread_info.underrun_for > 0)
return;
pa_memblockq_rewind(s->memblockq, nbytes);
@@ -2120,11 +2131,17 @@ static void command_get_playback_latency(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_
pa_tagstruct_put_usec(reply, latency);
pa_tagstruct_put_usec(reply, 0);
- pa_tagstruct_put_boolean(reply, pa_sink_input_get_state(s->sink_input) == PA_SINK_INPUT_RUNNING);
+ pa_tagstruct_put_boolean(reply, s->sink_input->thread_info.playing_for > 0);
pa_tagstruct_put_timeval(reply, &tv);
pa_tagstruct_put_timeval(reply, pa_gettimeofday(&now));
pa_tagstruct_puts64(reply, s->write_index);
pa_tagstruct_puts64(reply, s->read_index);
+
+ if (c->version >= 13) {
+ pa_tagstruct_putu64(reply, s->sink_input->thread_info.underrun_for);
+ pa_tagstruct_putu64(reply, s->sink_input->thread_info.playing_for);
+ }
+
pa_pstream_send_tagstruct(c->pstream, reply);
}
@@ -2152,7 +2169,7 @@ static void command_get_record_latency(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UN
reply = reply_new(tag);
pa_tagstruct_put_usec(reply, s->source_output->source->monitor_of ? pa_sink_get_latency(s->source_output->source->monitor_of) : 0);
pa_tagstruct_put_usec(reply, pa_source_get_latency(s->source_output->source));
- pa_tagstruct_put_boolean(reply, FALSE);
+ pa_tagstruct_put_boolean(reply, pa_source_get_state(s->source_output->source) == PA_SOURCE_RUNNING);
pa_tagstruct_put_timeval(reply, &tv);
pa_tagstruct_put_timeval(reply, pa_gettimeofday(&now));
pa_tagstruct_puts64(reply, pa_memblockq_get_write_index(s->memblockq));
@@ -3937,7 +3954,7 @@ static pa_protocol_native* protocol_new_internal(pa_core *c, pa_module *m, pa_mo
#ifdef HAVE_CREDS
{
- pa_bool_t a = 1;
+ pa_bool_t a = TRUE;
if (pa_modargs_get_value_boolean(ma, "auth-group-enabled", &a) < 0) {
pa_log("auth-group-enabled= expects a boolean argument.");
return NULL;
@@ -3982,7 +3999,7 @@ pa_protocol_native* pa_protocol_native_new(pa_core *core, pa_socket_server *serv
if (!(p = protocol_new_internal(core, m, ma)))
return NULL;
- p->server = server;
+ p->server = pa_socket_server_ref(server);
pa_socket_server_set_callback(p->server, on_connection, p);
if (pa_socket_server_get_address(p->server, t, sizeof(t))) {
diff --git a/src/pulsecore/protocol-simple.c b/src/pulsecore/protocol-simple.c
index 3ee2a058..8ec38fe4 100644
--- a/src/pulsecore/protocol-simple.c
+++ b/src/pulsecore/protocol-simple.c
@@ -587,7 +587,7 @@ pa_protocol_simple* pa_protocol_simple_new(pa_core *core, pa_socket_server *serv
p = pa_xnew0(pa_protocol_simple, 1);
p->module = m;
p->core = core;
- p->server = server;
+ p->server = pa_socket_server_ref(server);
p->connections = pa_idxset_new(NULL, NULL);
p->sample_spec = core->default_sample_spec;
diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c
index 8df36876..1da920a9 100644
--- a/src/pulsecore/sink-input.c
+++ b/src/pulsecore/sink-input.c
@@ -106,6 +106,7 @@ static void reset_callbacks(pa_sink_input *i) {
i->moved = NULL;
i->kill = NULL;
i->get_latency = NULL;
+ i->state_change = NULL;
}
pa_sink_input* pa_sink_input_new(
@@ -249,8 +250,8 @@ pa_sink_input* pa_sink_input_new(
i->thread_info.muted = i->muted;
i->thread_info.requested_sink_latency = (pa_usec_t) -1;
i->thread_info.rewrite_nbytes = 0;
- i->thread_info.since_underrun = 0;
- i->thread_info.ignore_rewind = FALSE;
+ i->thread_info.underrun_for = (uint64_t) -1;
+ i->thread_info.playing_for = 0;
i->thread_info.render_memblockq = pa_memblockq_new(
0,
@@ -328,7 +329,7 @@ void pa_sink_input_unlink(pa_sink_input *i) {
pa_sink_input_ref(i);
- linked = PA_SINK_INPUT_LINKED(i->state);
+ linked = PA_SINK_INPUT_IS_LINKED(i->state);
if (linked)
pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_UNLINK], i);
@@ -344,12 +345,11 @@ 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);
- if (linked) {
+ update_n_corked(i, PA_SINK_INPUT_UNLINKED);
+ i->state = PA_SINK_INPUT_UNLINKED;
+
+ if (linked)
pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_REMOVE_INPUT, i, 0, NULL);
- sink_input_set_state(i, PA_SINK_INPUT_UNLINKED);
- pa_sink_update_status(i->sink);
- } else
- i->state = PA_SINK_INPUT_UNLINKED;
reset_callbacks(i);
@@ -368,7 +368,7 @@ static void sink_input_free(pa_object *o) {
pa_assert(i);
pa_assert(pa_sink_input_refcnt(i) == 0);
- if (PA_SINK_INPUT_LINKED(i->state))
+ if (PA_SINK_INPUT_IS_LINKED(i->state))
pa_sink_input_unlink(i);
pa_log_info("Freeing input %u \"%s\"", i->index, pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_MEDIA_NAME)));
@@ -402,7 +402,7 @@ void pa_sink_input_put(pa_sink_input *i) {
state = i->flags & PA_SINK_INPUT_START_CORKED ? PA_SINK_INPUT_CORKED : PA_SINK_INPUT_RUNNING;
update_n_corked(i, state);
- i->thread_info.state = i->state = state;
+ i->state = state;
pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_ADD_INPUT, i, 0, NULL);
@@ -416,7 +416,7 @@ void pa_sink_input_put(pa_sink_input *i) {
void pa_sink_input_kill(pa_sink_input*i) {
pa_sink_input_assert_ref(i);
- pa_assert(PA_SINK_INPUT_LINKED(i->state));
+ pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
if (i->kill)
i->kill(i);
@@ -426,7 +426,7 @@ pa_usec_t pa_sink_input_get_latency(pa_sink_input *i) {
pa_usec_t r = 0;
pa_sink_input_assert_ref(i);
- pa_assert(PA_SINK_INPUT_LINKED(i->state));
+ 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;
@@ -445,7 +445,7 @@ int pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink frames */, pa
size_t ilength;
pa_sink_input_assert_ref(i);
- pa_assert(PA_SINK_INPUT_LINKED(i->thread_info.state));
+ pa_assert(PA_SINK_INPUT_IS_LINKED(i->thread_info.state));
pa_assert(pa_frame_aligned(slength, &i->sink->sample_spec));
pa_assert(chunk);
pa_assert(volume);
@@ -510,7 +510,9 @@ int pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink frames */, pa
pa_atomic_store(&i->thread_info.drained, 1);
pa_memblockq_seek(i->thread_info.render_memblockq, slength, PA_SEEK_RELATIVE_ON_READ);
- i->thread_info.since_underrun = 0;
+ i->thread_info.playing_for = 0;
+ if (i->thread_info.underrun_for != (uint64_t) -1)
+ i->thread_info.underrun_for += slength;
break;
}
@@ -519,7 +521,8 @@ int pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink frames */, pa
pa_assert(tchunk.length > 0);
pa_assert(tchunk.memblock);
- i->thread_info.since_underrun += tchunk.length;
+ i->thread_info.underrun_for = 0;
+ i->thread_info.playing_for += tchunk.length;
while (tchunk.length > 0) {
pa_memchunk wchunk;
@@ -590,7 +593,7 @@ int pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink frames */, pa
void pa_sink_input_drop(pa_sink_input *i, size_t nbytes /* in sink sample spec */) {
pa_sink_input_assert_ref(i);
- pa_assert(PA_SINK_INPUT_LINKED(i->thread_info.state));
+ pa_assert(PA_SINK_INPUT_IS_LINKED(i->thread_info.state));
pa_assert(pa_frame_aligned(nbytes, &i->sink->sample_spec));
pa_assert(nbytes > 0);
@@ -610,13 +613,13 @@ void pa_sink_input_drop(pa_sink_input *i, size_t nbytes /* in sink sample spec *
void pa_sink_input_process_rewind(pa_sink_input *i, size_t nbytes /* in sink sample spec */) {
pa_sink_input_assert_ref(i);
- pa_assert(PA_SINK_INPUT_LINKED(i->thread_info.state));
+ pa_assert(PA_SINK_INPUT_IS_LINKED(i->thread_info.state));
pa_assert(pa_frame_aligned(nbytes, &i->sink->sample_spec));
/* pa_log_debug("rewind(%lu, %lu)", (unsigned long) nbytes, (unsigned long) i->thread_info.rewrite_nbytes); */
- if (i->thread_info.ignore_rewind) {
- i->thread_info.ignore_rewind = FALSE;
+ if (i->thread_info.underrun_for > 0) {
+ /* We don't rewind when we are underrun */
i->thread_info.rewrite_nbytes = 0;
return;
}
@@ -668,7 +671,7 @@ void pa_sink_input_process_rewind(pa_sink_input *i, size_t nbytes /* in sink sam
/* Called from thread context */
void pa_sink_input_update_max_rewind(pa_sink_input *i, size_t nbytes /* in the sink's sample spec */) {
pa_sink_input_assert_ref(i);
- pa_assert(PA_SINK_INPUT_LINKED(i->thread_info.state));
+ pa_assert(PA_SINK_INPUT_IS_LINKED(i->thread_info.state));
pa_assert(pa_frame_aligned(nbytes, &i->sink->sample_spec));
pa_memblockq_set_maxrewind(i->thread_info.render_memblockq, nbytes);
@@ -677,21 +680,41 @@ 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);
}
-pa_usec_t pa_sink_input_set_requested_latency(pa_sink_input *i, pa_usec_t usec) {
- pa_sink_input_assert_ref(i);
+static pa_usec_t fixup_latency(pa_sink *s, pa_usec_t usec) {
+ pa_sink_assert_ref(s);
- if (usec != (pa_usec_t) -1) {
+ if (usec == (pa_usec_t) -1)
+ return usec;
- if (i->sink->max_latency > 0 && usec > i->sink->max_latency)
- usec = i->sink->max_latency;
+ if (s->max_latency > 0 && usec > s->max_latency)
+ usec = s->max_latency;
- if (i->sink->min_latency > 0 && usec < i->sink->min_latency)
- usec = i->sink->min_latency;
- }
+ if (s->min_latency > 0 && usec < s->min_latency)
+ usec = s->min_latency;
+
+ return usec;
+}
+
+pa_usec_t pa_sink_input_set_requested_latency_within_thread(pa_sink_input *i, pa_usec_t usec) {
+
+ usec = fixup_latency(i->sink, usec);
+
+ i->thread_info.requested_sink_latency = usec;
+ pa_sink_invalidate_requested_latency(i->sink);
- if (PA_SINK_INPUT_LINKED(i->state))
+ return usec;
+}
+
+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);
else {
+ /* If this sink input is not realized yet, we have to touch
+ * the thread info data directly */
i->thread_info.requested_sink_latency = usec;
i->sink->thread_info.requested_latency_valid = FALSE;
}
@@ -701,7 +724,7 @@ pa_usec_t pa_sink_input_set_requested_latency(pa_sink_input *i, pa_usec_t usec)
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_LINKED(i->state));
+ pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
if (pa_cvolume_equal(&i->volume, volume))
return;
@@ -714,7 +737,7 @@ 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) {
pa_sink_input_assert_ref(i);
- pa_assert(PA_SINK_INPUT_LINKED(i->state));
+ pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
return &i->volume;
}
@@ -722,7 +745,7 @@ 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) {
pa_assert(i);
pa_sink_input_assert_ref(i);
- pa_assert(PA_SINK_INPUT_LINKED(i->state));
+ pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
if (!i->muted == !mute)
return;
@@ -735,21 +758,21 @@ 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_sink_input_assert_ref(i);
- pa_assert(PA_SINK_INPUT_LINKED(i->state));
+ pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
return !!i->muted;
}
void pa_sink_input_cork(pa_sink_input *i, pa_bool_t b) {
pa_sink_input_assert_ref(i);
- pa_assert(PA_SINK_INPUT_LINKED(i->state));
+ pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
sink_input_set_state(i, b ? PA_SINK_INPUT_CORKED : PA_SINK_INPUT_RUNNING);
}
int pa_sink_input_set_rate(pa_sink_input *i, uint32_t rate) {
pa_sink_input_assert_ref(i);
- pa_assert(PA_SINK_INPUT_LINKED(i->state));
+ pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
pa_return_val_if_fail(i->thread_info.resampler, -1);
if (i->sample_spec.rate == rate)
@@ -780,7 +803,7 @@ void pa_sink_input_set_name(pa_sink_input *i, const char *name) {
else
pa_proplist_unset(i->proplist, PA_PROP_MEDIA_NAME);
- if (PA_SINK_INPUT_LINKED(i->state)) {
+ if (PA_SINK_INPUT_IS_LINKED(i->state)) {
pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_PROPLIST_CHANGED], i);
pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
}
@@ -792,7 +815,7 @@ pa_resample_method_t pa_sink_input_get_resample_method(pa_sink_input *i) {
return i->resample_method;
}
-int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, int immediately) {
+int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, pa_bool_t immediately) {
pa_resampler *new_resampler;
pa_sink *origin;
pa_usec_t silence_usec = 0;
@@ -800,7 +823,7 @@ int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, int immediately) {
pa_sink_input_move_hook_data hook_data;
pa_sink_input_assert_ref(i);
- pa_assert(PA_SINK_INPUT_LINKED(i->state));
+ pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
pa_sink_assert_ref(dest);
origin = i->sink;
@@ -983,7 +1006,7 @@ int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, int immediately) {
return 0;
}
-static void set_state(pa_sink_input *i, pa_sink_input_state_t state) {
+void pa_sink_input_set_state_within_thread(pa_sink_input *i, pa_sink_input_state_t state) {
pa_sink_input_assert_ref(i);
if ((state == PA_SINK_INPUT_DRAINED || state == PA_SINK_INPUT_RUNNING) &&
@@ -998,17 +1021,18 @@ static void set_state(pa_sink_input *i, pa_sink_input_state_t state) {
/* This will tell the implementing sink input driver to rewind
* so that the unplayed already mixed data is not lost */
- pa_sink_input_request_rewind(i, 0, FALSE);
+ pa_sink_input_request_rewind(i, 0, FALSE, FALSE);
} else if (i->thread_info.state == PA_SINK_INPUT_CORKED && state != PA_SINK_INPUT_CORKED) {
/* OK, we're being uncorked. Make sure we're not rewound when
* the hw buffer is remixed and request a remix. */
- i->thread_info.ignore_rewind = TRUE;
- i->thread_info.since_underrun = 0;
- pa_sink_request_rewind(i->sink, 0);
+ pa_sink_input_request_rewind(i, 0, TRUE, TRUE);
}
+ if (i->state_change)
+ i->state_change(i, state);
+
i->thread_info.state = state;
}
@@ -1017,17 +1041,17 @@ int pa_sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t
pa_sink_input *i = PA_SINK_INPUT(o);
pa_sink_input_assert_ref(i);
- pa_assert(PA_SINK_INPUT_LINKED(i->thread_info.state));
+ 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, FALSE);
+ pa_sink_input_request_rewind(i, 0, FALSE, FALSE);
return 0;
case PA_SINK_INPUT_MESSAGE_SET_MUTE:
i->thread_info.muted = PA_PTR_TO_UINT(userdata);
- pa_sink_input_request_rewind(i, 0, FALSE);
+ pa_sink_input_request_rewind(i, 0, FALSE, FALSE);
return 0;
case PA_SINK_INPUT_MESSAGE_GET_LATENCY: {
@@ -1048,22 +1072,20 @@ int pa_sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t
case PA_SINK_INPUT_MESSAGE_SET_STATE: {
pa_sink_input *ssync;
- set_state(i, PA_PTR_TO_UINT(userdata));
+ pa_sink_input_set_state_within_thread(i, PA_PTR_TO_UINT(userdata));
for (ssync = i->thread_info.sync_prev; ssync; ssync = ssync->thread_info.sync_prev)
- set_state(ssync, PA_PTR_TO_UINT(userdata));
+ pa_sink_input_set_state_within_thread(ssync, PA_PTR_TO_UINT(userdata));
for (ssync = i->thread_info.sync_next; ssync; ssync = ssync->thread_info.sync_next)
- set_state(ssync, PA_PTR_TO_UINT(userdata));
+ pa_sink_input_set_state_within_thread(ssync, PA_PTR_TO_UINT(userdata));
return 0;
}
case PA_SINK_INPUT_MESSAGE_SET_REQUESTED_LATENCY:
- i->thread_info.requested_sink_latency = (pa_usec_t) offset;
- pa_sink_invalidate_requested_latency(i->sink);
-
+ pa_sink_input_set_requested_latency_within_thread(i, (pa_usec_t) offset);
return 0;
}
@@ -1088,8 +1110,8 @@ pa_bool_t pa_sink_input_safe_to_remove(pa_sink_input *i) {
return TRUE;
}
-void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes /* in our sample spec */, pa_bool_t ignore_underruns) {
- size_t l, lbq;
+void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes /* in our sample spec */, pa_bool_t ignore_underruns, pa_bool_t not_here) {
+ size_t lbq;
pa_sink_input_assert_ref(i);
@@ -1097,9 +1119,16 @@ void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes /* in our sam
if (i->state == PA_SINK_INPUT_CORKED)
return;
- lbq = pa_memblockq_get_length(i->thread_info.render_memblockq);
+ /* Calculate how much we can rewind locally without having to
+ * touch the sink */
+ if (not_here)
+ lbq = 0;
+ else
+ lbq = pa_memblockq_get_length(i->thread_info.render_memblockq);
+ /* Check if rewinding for the maximum is requested, and if so, fix up */
if (nbytes <= 0) {
+
/* Calulate maximum number of bytes that could be rewound in theory */
nbytes = i->sink->thread_info.max_rewind + lbq;
@@ -1110,26 +1139,33 @@ void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes /* in our sam
nbytes;
}
- /* Increase the number of bytes to rewrite, never decrease */
- if (nbytes > i->thread_info.rewrite_nbytes)
- i->thread_info.rewrite_nbytes = nbytes;
+ if (not_here) {
+ i->thread_info.playing_for = 0;
+ i->thread_info.underrun_for = (uint64_t) -1;
+ } else {
+ /* Increase the number of bytes to rewrite, never decrease */
+ if (nbytes < i->thread_info.rewrite_nbytes)
+ nbytes = i->thread_info.rewrite_nbytes;
- if (!ignore_underruns) {
/* Make sure to not overwrite over underruns */
- if ((int64_t) i->thread_info.rewrite_nbytes > i->thread_info.since_underrun)
- i->thread_info.rewrite_nbytes = (size_t) i->thread_info.since_underrun;
+ if (!ignore_underruns)
+ if ((int64_t) nbytes > i->thread_info.playing_for)
+ nbytes = (size_t) i->thread_info.playing_for;
+
+ i->thread_info.rewrite_nbytes = nbytes;
}
/* Transform to sink domain */
- l = i->thread_info.resampler ?
- pa_resampler_result(i->thread_info.resampler, i->thread_info.rewrite_nbytes) :
- i->thread_info.rewrite_nbytes;
+ nbytes =
+ i->thread_info.resampler ?
+ pa_resampler_result(i->thread_info.resampler, nbytes) :
+ nbytes;
- if (l <= 0)
+ if (nbytes <= 0)
return;
- if (l > lbq)
- pa_sink_request_rewind(i->sink, l - lbq);
+ if (nbytes > lbq)
+ pa_sink_request_rewind(i->sink, nbytes - lbq);
}
pa_memchunk* pa_sink_input_get_silence(pa_sink_input *i, pa_memchunk *ret) {
diff --git a/src/pulsecore/sink-input.h b/src/pulsecore/sink-input.h
index b433edc0..b70cb0ac 100644
--- a/src/pulsecore/sink-input.h
+++ b/src/pulsecore/sink-input.h
@@ -46,7 +46,7 @@ typedef enum pa_sink_input_state {
PA_SINK_INPUT_UNLINKED /*< The stream is dead */
} pa_sink_input_state_t;
-static inline pa_bool_t PA_SINK_INPUT_LINKED(pa_sink_input_state_t x) {
+static inline pa_bool_t PA_SINK_INPUT_IS_LINKED(pa_sink_input_state_t x) {
return x == PA_SINK_INPUT_DRAINED || x == PA_SINK_INPUT_RUNNING || x == PA_SINK_INPUT_CORKED;
}
@@ -106,7 +106,7 @@ struct pa_sink_input {
void (*process_rewind) (pa_sink_input *i, size_t nbytes); /* may NOT be NULL */
/* Called whenever the maximum rewindable size of the sink
- * changes. Called from RT context. */
+ * changes. Called from IO context. */
void (*update_max_rewind) (pa_sink_input *i, size_t nbytes); /* may be NULL */
/* If non-NULL this function is called when the input is first
@@ -138,6 +138,10 @@ struct pa_sink_input {
instead. */
pa_usec_t (*get_latency) (pa_sink_input *i); /* may be NULL */
+ /* If non_NULL this function is called from thread context if the
+ * state changes. The old state is found in thread_info.state. */
+ void (*state_change) (pa_sink_input *i, pa_sink_input_state_t state); /* may be NULL */
+
struct {
pa_sink_input_state_t state;
pa_atomic_t drained, render_memblockq_is_empty;
@@ -152,7 +156,7 @@ struct pa_sink_input {
pa_memblockq *render_memblockq;
size_t rewrite_nbytes;
- int64_t since_underrun;
+ uint64_t underrun_for, playing_for;
pa_bool_t ignore_rewind;
pa_sink_input *sync_prev, *sync_next;
@@ -237,7 +241,7 @@ fully -- or at all. If the request for a rewrite was successful, the
sink driver will call ->rewind() and pass the number of bytes that
could be rewound in the HW device. This functionality is required for
implementing the "zero latency" write-through functionality. */
-void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes, pa_bool_t ignore_rewind);
+void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes, pa_bool_t ignore_rewind, pa_bool_t not_here);
/* Callable by everyone from main thread*/
@@ -257,7 +261,7 @@ int pa_sink_input_set_rate(pa_sink_input *i, uint32_t rate);
pa_resample_method_t pa_sink_input_get_resample_method(pa_sink_input *i);
-int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, int immediately);
+int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, pa_bool_t immediately);
pa_sink_input_state_t pa_sink_input_get_state(pa_sink_input *i);
@@ -269,8 +273,12 @@ 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_set_state_within_thread(pa_sink_input *i, pa_sink_input_state_t state);
+
int pa_sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk);
+pa_usec_t pa_sink_input_set_requested_latency_within_thread(pa_sink_input *i, pa_usec_t usec);
+
typedef struct pa_sink_input_move_info {
pa_sink_input *sink_input;
pa_sink_input *ghost_sink_input;
diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
index 452dab79..a2a02ebf 100644
--- a/src/pulsecore/sink.c
+++ b/src/pulsecore/sink.c
@@ -265,8 +265,8 @@ static int sink_set_state(pa_sink *s, pa_sink_state_t state) {
return 0;
suspend_change =
- (s->state == PA_SINK_SUSPENDED && PA_SINK_OPENED(state)) ||
- (PA_SINK_OPENED(s->state) && state == PA_SINK_SUSPENDED);
+ (s->state == PA_SINK_SUSPENDED && PA_SINK_IS_OPENED(state)) ||
+ (PA_SINK_IS_OPENED(s->state) && state == PA_SINK_SUSPENDED);
if (s->set_state)
if ((ret = s->set_state(s, state)) < 0)
@@ -328,7 +328,7 @@ void pa_sink_unlink(pa_sink* s) {
* may be called multiple times on the same sink without bad
* effects. */
- linked = PA_SINK_LINKED(s->state);
+ linked = PA_SINK_IS_LINKED(s->state);
if (linked)
pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_UNLINK], s);
@@ -366,7 +366,7 @@ static void sink_free(pa_object *o) {
pa_assert(s);
pa_assert(pa_sink_refcnt(s) == 0);
- if (PA_SINK_LINKED(s->state))
+ if (PA_SINK_IS_LINKED(s->state))
pa_sink_unlink(s);
pa_log_info("Freeing sink %u \"%s\"", s->index, s->name);
@@ -397,7 +397,6 @@ static void sink_free(pa_object *o) {
void pa_sink_set_asyncmsgq(pa_sink *s, pa_asyncmsgq *q) {
pa_sink_assert_ref(s);
- pa_assert(q);
s->asyncmsgq = q;
@@ -407,7 +406,6 @@ void pa_sink_set_asyncmsgq(pa_sink *s, pa_asyncmsgq *q) {
void pa_sink_set_rtpoll(pa_sink *s, pa_rtpoll *p) {
pa_sink_assert_ref(s);
- pa_assert(p);
s->rtpoll = p;
if (s->monitor_source)
@@ -416,7 +414,7 @@ void pa_sink_set_rtpoll(pa_sink *s, pa_rtpoll *p) {
int pa_sink_update_status(pa_sink*s) {
pa_sink_assert_ref(s);
- pa_assert(PA_SINK_LINKED(s->state));
+ pa_assert(PA_SINK_IS_LINKED(s->state));
if (s->state == PA_SINK_SUSPENDED)
return 0;
@@ -426,7 +424,7 @@ int pa_sink_update_status(pa_sink*s) {
int pa_sink_suspend(pa_sink *s, pa_bool_t suspend) {
pa_sink_assert_ref(s);
- pa_assert(PA_SINK_LINKED(s->state));
+ pa_assert(PA_SINK_IS_LINKED(s->state));
if (suspend)
return sink_set_state(s, PA_SINK_SUSPENDED);
@@ -438,7 +436,10 @@ 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_LINKED(s->state));
+ pa_assert(PA_SINK_IS_LINKED(s->state));
+
+ /* Make sure the sink code already reset the counter! */
+ pa_assert(s->thread_info.rewind_nbytes <= 0);
if (nbytes <= 0)
return;
@@ -450,8 +451,9 @@ void pa_sink_process_rewind(pa_sink *s, size_t nbytes) {
pa_sink_input_process_rewind(i, nbytes);
}
- if (s->monitor_source && PA_SOURCE_OPENED(pa_source_get_state(s->monitor_source)))
+ if (s->monitor_source && PA_SOURCE_IS_OPENED(pa_source_get_state(s->monitor_source)))
pa_source_process_rewind(s->monitor_source, nbytes);
+
}
static unsigned fill_mix_info(pa_sink *s, size_t *length, pa_mix_info *info, unsigned maxinfo) {
@@ -557,7 +559,7 @@ void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) {
size_t block_size_max;
pa_sink_assert_ref(s);
- pa_assert(PA_SINK_OPENED(s->thread_info.state));
+ pa_assert(PA_SINK_IS_OPENED(s->thread_info.state));
pa_assert(pa_frame_aligned(length, &s->sample_spec));
pa_assert(result);
@@ -621,7 +623,7 @@ 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_OPENED(pa_source_get_state(s->monitor_source)))
+ if (s->monitor_source && PA_SOURCE_IS_OPENED(pa_source_get_state(s->monitor_source)))
pa_source_post(s->monitor_source, result);
pa_sink_unref(s);
@@ -633,7 +635,7 @@ void pa_sink_render_into(pa_sink*s, pa_memchunk *target) {
size_t length, block_size_max;
pa_sink_assert_ref(s);
- pa_assert(PA_SINK_OPENED(s->thread_info.state));
+ pa_assert(PA_SINK_IS_OPENED(s->thread_info.state));
pa_assert(target);
pa_assert(target->memblock);
pa_assert(target->length > 0);
@@ -700,7 +702,7 @@ 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_OPENED(pa_source_get_state(s->monitor_source)))
+ if (s->monitor_source && PA_SOURCE_IS_OPENED(pa_source_get_state(s->monitor_source)))
pa_source_post(s->monitor_source, target);
pa_sink_unref(s);
@@ -711,7 +713,7 @@ void pa_sink_render_into_full(pa_sink *s, pa_memchunk *target) {
size_t l, d;
pa_sink_assert_ref(s);
- pa_assert(PA_SINK_OPENED(s->thread_info.state));
+ pa_assert(PA_SINK_IS_OPENED(s->thread_info.state));
pa_assert(target);
pa_assert(target->memblock);
pa_assert(target->length > 0);
@@ -739,7 +741,7 @@ void pa_sink_render_into_full(pa_sink *s, pa_memchunk *target) {
void pa_sink_render_full(pa_sink *s, size_t length, pa_memchunk *result) {
pa_sink_assert_ref(s);
- pa_assert(PA_SINK_OPENED(s->thread_info.state));
+ pa_assert(PA_SINK_IS_OPENED(s->thread_info.state));
pa_assert(length > 0);
pa_assert(pa_frame_aligned(length, &s->sample_spec));
pa_assert(result);
@@ -755,50 +757,15 @@ void pa_sink_render_full(pa_sink *s, size_t length, pa_memchunk *result) {
pa_sink_render_into_full(s, result);
}
-void pa_sink_skip(pa_sink *s, size_t length) {
- pa_sink_input *i;
- void *state = NULL;
-
- pa_sink_assert_ref(s);
- pa_assert(PA_SINK_OPENED(s->thread_info.state));
- pa_assert(length > 0);
- pa_assert(pa_frame_aligned(length, &s->sample_spec));
-
- s->thread_info.rewind_nbytes = 0;
-
- if (pa_source_used_by(s->monitor_source)) {
- pa_memchunk chunk;
-
- /* If something is connected to our monitor source, we have to
- * pass valid data to it */
-
- while (length > 0) {
- pa_sink_render(s, length, &chunk);
- pa_memblock_unref(chunk.memblock);
-
- pa_assert(chunk.length <= length);
- length -= chunk.length;
- }
-
- } else {
- /* Ok, noone cares about the rendered data, so let's not even render it */
-
- while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL))) {
- pa_sink_input_assert_ref(i);
- pa_sink_input_drop(i, length);
- }
- }
-}
-
pa_usec_t pa_sink_get_latency(pa_sink *s) {
pa_usec_t usec = 0;
pa_sink_assert_ref(s);
- pa_assert(PA_SINK_LINKED(s->state));
+ pa_assert(PA_SINK_IS_LINKED(s->state));
/* The returned value is supposed to be in the time domain of the sound card! */
- if (!PA_SINK_OPENED(s->state))
+ if (!PA_SINK_IS_OPENED(s->state))
return 0;
if (s->get_latency)
@@ -814,7 +781,7 @@ void pa_sink_set_volume(pa_sink *s, const pa_cvolume *volume) {
int changed;
pa_sink_assert_ref(s);
- pa_assert(PA_SINK_LINKED(s->state));
+ pa_assert(PA_SINK_IS_LINKED(s->state));
pa_assert(volume);
changed = !pa_cvolume_equal(volume, &s->volume);
@@ -834,7 +801,7 @@ const pa_cvolume *pa_sink_get_volume(pa_sink *s) {
struct pa_cvolume old_volume;
pa_sink_assert_ref(s);
- pa_assert(PA_SINK_LINKED(s->state));
+ pa_assert(PA_SINK_IS_LINKED(s->state));
old_volume = s->volume;
@@ -854,7 +821,7 @@ void pa_sink_set_mute(pa_sink *s, pa_bool_t mute) {
int changed;
pa_sink_assert_ref(s);
- pa_assert(PA_SINK_LINKED(s->state));
+ pa_assert(PA_SINK_IS_LINKED(s->state));
changed = s->muted != mute;
s->muted = mute;
@@ -873,7 +840,7 @@ pa_bool_t pa_sink_get_mute(pa_sink *s) {
pa_bool_t old_muted;
pa_sink_assert_ref(s);
- pa_assert(PA_SINK_LINKED(s->state));
+ pa_assert(PA_SINK_IS_LINKED(s->state));
old_muted = s->muted;
@@ -914,7 +881,7 @@ void pa_sink_set_description(pa_sink *s, const char *description) {
pa_xfree(n);
}
- if (PA_SINK_LINKED(s->state)) {
+ if (PA_SINK_IS_LINKED(s->state)) {
pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_PROPLIST_CHANGED], s);
}
@@ -924,7 +891,7 @@ unsigned pa_sink_linked_by(pa_sink *s) {
unsigned ret;
pa_sink_assert_ref(s);
- pa_assert(PA_SINK_LINKED(s->state));
+ pa_assert(PA_SINK_IS_LINKED(s->state));
ret = pa_idxset_size(s->inputs);
@@ -941,7 +908,7 @@ unsigned pa_sink_used_by(pa_sink *s) {
unsigned ret;
pa_sink_assert_ref(s);
- pa_assert(PA_SINK_LINKED(s->state));
+ pa_assert(PA_SINK_IS_LINKED(s->state));
ret = pa_idxset_size(s->inputs);
pa_assert(ret >= s->n_corked);
@@ -980,24 +947,26 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse
i->thread_info.sync_next->thread_info.sync_prev = i;
}
- pa_sink_input_update_max_rewind(i, s->thread_info.max_rewind);
-
pa_assert(!i->thread_info.attached);
i->thread_info.attached = TRUE;
if (i->attach)
i->attach(i);
- /* If you change anything here, make sure to change the
- * ghost sink input handling a few lines down at
- * PA_SINK_MESSAGE_REMOVE_INPUT_AND_BUFFER, too. */
+ pa_sink_input_set_state_within_thread(i, i->state);
+
+ pa_sink_input_update_max_rewind(i, s->thread_info.max_rewind);
pa_sink_invalidate_requested_latency(s);
- /* Make sure we're not rewound when the hw buffer is remixed and request a remix*/
- i->thread_info.ignore_rewind = TRUE;
- i->thread_info.since_underrun = 0;
- pa_sink_request_rewind(s, 0);
+ /* We don't rewind here automatically. This is left to the
+ * sink input implementor because some sink inputs need a
+ * slow start, i.e. need some time to buffer client
+ * samples before beginning streaming. */
+
+ /* If you change anything here, make sure to change the
+ * ghost sink input handling a few lines down at
+ * PA_SINK_MESSAGE_REMOVE_INPUT_AND_BUFFER, too. */
return 0;
}
@@ -1009,6 +978,8 @@ 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_REMOVE_INPUT_AND_BUFFER, too. */
+ pa_sink_input_set_state_within_thread(i, i->state);
+
if (i->detach)
i->detach(i);
@@ -1036,7 +1007,6 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse
pa_sink_input_unref(i);
pa_sink_invalidate_requested_latency(s);
-
pa_sink_request_rewind(s, 0);
return 0;
@@ -1117,11 +1087,9 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse
if (info->ghost_sink_input->attach)
info->ghost_sink_input->attach(info->ghost_sink_input);
-
}
pa_sink_invalidate_requested_latency(s);
-
pa_sink_request_rewind(s, 0);
return 0;
@@ -1196,14 +1164,14 @@ int pa_sink_suspend_all(pa_core *c, pa_bool_t suspend) {
void pa_sink_detach(pa_sink *s) {
pa_sink_assert_ref(s);
- pa_assert(PA_SINK_LINKED(s->state));
+ pa_assert(PA_SINK_IS_LINKED(s->state));
pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_DETACH, NULL, 0, NULL);
}
void pa_sink_attach(pa_sink *s) {
pa_sink_assert_ref(s);
- pa_assert(PA_SINK_LINKED(s->state));
+ pa_assert(PA_SINK_IS_LINKED(s->state));
pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_ATTACH, NULL, 0, NULL);
}
@@ -1213,7 +1181,7 @@ void pa_sink_detach_within_thread(pa_sink *s) {
void *state = NULL;
pa_sink_assert_ref(s);
- pa_assert(PA_SINK_LINKED(s->thread_info.state));
+ pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
if (i->detach)
@@ -1228,7 +1196,7 @@ void pa_sink_attach_within_thread(pa_sink *s) {
void *state = NULL;
pa_sink_assert_ref(s);
- pa_assert(PA_SINK_LINKED(s->thread_info.state));
+ pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
if (i->attach)
@@ -1240,7 +1208,7 @@ void pa_sink_attach_within_thread(pa_sink *s) {
void pa_sink_request_rewind(pa_sink*s, size_t nbytes) {
pa_sink_assert_ref(s);
- pa_assert(PA_SINK_LINKED(s->thread_info.state));
+ pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
if (nbytes <= 0)
nbytes = s->thread_info.max_rewind;
@@ -1290,9 +1258,9 @@ pa_usec_t pa_sink_get_requested_latency(pa_sink *s) {
pa_usec_t usec = 0;
pa_sink_assert_ref(s);
- pa_assert(PA_SINK_LINKED(s->state));
+ pa_assert(PA_SINK_IS_LINKED(s->state));
- if (!PA_SINK_OPENED(s->state))
+ 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)
@@ -1325,7 +1293,7 @@ void pa_sink_set_max_rewind(pa_sink *s, size_t max_rewind) {
void pa_sink_invalidate_requested_latency(pa_sink *s) {
pa_sink_assert_ref(s);
- pa_assert(PA_SINK_LINKED(s->thread_info.state));
+ pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
if (!s->thread_info.requested_latency_valid)
return;
diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h
index 7bc4a706..f25f48cf 100644
--- a/src/pulsecore/sink.h
+++ b/src/pulsecore/sink.h
@@ -33,7 +33,6 @@ typedef struct pa_sink pa_sink;
#include <pulse/channelmap.h>
#include <pulse/volume.h>
-#include <pulsecore/core-def.h>
#include <pulsecore/core.h>
#include <pulsecore/idxset.h>
#include <pulsecore/source.h>
@@ -52,11 +51,11 @@ typedef enum pa_sink_state {
PA_SINK_UNLINKED
} pa_sink_state_t;
-static inline pa_bool_t PA_SINK_OPENED(pa_sink_state_t x) {
+static inline pa_bool_t PA_SINK_IS_OPENED(pa_sink_state_t x) {
return x == PA_SINK_RUNNING || x == PA_SINK_IDLE;
}
-static inline pa_bool_t PA_SINK_LINKED(pa_sink_state_t x) {
+static inline pa_bool_t PA_SINK_IS_LINKED(pa_sink_state_t x) {
return x == PA_SINK_RUNNING || x == PA_SINK_IDLE || x == PA_SINK_SUSPENDED;
}
@@ -94,13 +93,42 @@ struct pa_sink {
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 */
int (*set_state)(pa_sink *s, pa_sink_state_t state); /* may be NULL */
- int (*get_volume)(pa_sink *s); /* dito */
+
+ /* 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 */
+
+ /* Called when the volume shall be changed. Called from main loop
+ * context. If this is NULL a PA_SINK_MESSAGE_SET_VOLUME message
+ * will be sent to the IO thread instead. */
int (*set_volume)(pa_sink *s); /* dito */
+
+ /* 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. */
int (*get_mute)(pa_sink *s); /* dito */
+
+ /* Called when the mute setting shall be changed. Called from main
+ * loop context. If this is NULL a PA_SINK_MESSAGE_SET_MUTE
+ * message will be sent to the IO thread instead. */
int (*set_mute)(pa_sink *s); /* dito */
- pa_usec_t (*get_latency)(pa_sink *s); /* dito */
+
+ /* Called when the latency is queried. Called from main loop
+ context. If this is NULL a PA_SINK_MESSAGE_GET_LATENCY message
+ will be sent to the IO thread instead. */
+ pa_usec_t (*get_latency)(pa_sink *s); /* dito */
+
+ /* Called when a rewind request is issued. Called from IO thread
+ * context. */
void (*request_rewind)(pa_sink *s); /* dito */
+
+ /* Called when a the requested latency is changed. Called from IO
+ * thread context. */
void (*update_requested_latency)(pa_sink *s); /* dito */
/* Contains copies of the above data so that the real-time worker
@@ -213,7 +241,6 @@ void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result);
void pa_sink_render_full(pa_sink *s, size_t length, pa_memchunk *result);
void pa_sink_render_into(pa_sink*s, pa_memchunk *target);
void pa_sink_render_into_full(pa_sink *s, pa_memchunk *target);
-void pa_sink_skip(pa_sink *s, size_t length);
void pa_sink_process_rewind(pa_sink *s, size_t nbytes);
diff --git a/src/pulsecore/sound-file-stream.c b/src/pulsecore/sound-file-stream.c
index 604723f1..918313f8 100644
--- a/src/pulsecore/sound-file-stream.c
+++ b/src/pulsecore/sound-file-stream.c
@@ -55,6 +55,8 @@ typedef struct file_stream {
SNDFILE *sndfile;
sf_count_t (*readf_function)(SNDFILE *sndfile, void *ptr, sf_count_t frames);
+ /* We need this memblockq here to easily fulfill rewind requests
+ * (even beyond the file start!) */
pa_memblockq *memblockq;
} file_stream;
@@ -66,6 +68,7 @@ PA_DECLARE_CLASS(file_stream);
#define FILE_STREAM(o) (file_stream_cast(o))
static PA_DEFINE_CHECK_TYPE(file_stream, pa_msgobject);
+/* Called from main context */
static void file_stream_unlink(file_stream *u) {
pa_assert(u);
@@ -80,6 +83,7 @@ static void file_stream_unlink(file_stream *u) {
file_stream_unref(u);
}
+/* Called from main context */
static void file_stream_free(pa_object *o) {
file_stream *u = FILE_STREAM(o);
pa_assert(u);
@@ -93,6 +97,7 @@ static void file_stream_free(pa_object *o) {
pa_xfree(u);
}
+/* Called from main context */
static int file_stream_process_msg(pa_msgobject *o, int code, void*userdata, int64_t offset, pa_memchunk *chunk) {
file_stream *u = FILE_STREAM(o);
file_stream_assert_ref(u);
@@ -106,6 +111,7 @@ static int file_stream_process_msg(pa_msgobject *o, int code, void*userdata, int
return 0;
}
+/* Called from main context */
static void sink_input_kill_cb(pa_sink_input *i) {
file_stream *u;
@@ -116,6 +122,22 @@ static void sink_input_kill_cb(pa_sink_input *i) {
file_stream_unlink(u);
}
+/* Called from IO thread context */
+static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t state) {
+ file_stream *u;
+
+ pa_sink_input_assert_ref(i);
+ u = FILE_STREAM(i->userdata);
+ file_stream_assert_ref(u);
+
+ /* 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 IO thread context */
static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) {
file_stream *u;
@@ -131,6 +153,9 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk
for (;;) {
pa_memchunk tchunk;
+ size_t fs;
+ void *p;
+ sf_count_t n;
if (pa_memblockq_peek(u->memblockq, chunk) >= 0) {
pa_memblockq_drop(u->memblockq, chunk->length);
@@ -143,36 +168,19 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk
tchunk.memblock = pa_memblock_new(i->sink->core->mempool, length);
tchunk.index = 0;
- if (u->readf_function) {
- sf_count_t n;
- void *p;
- size_t fs = pa_frame_size(&i->sample_spec);
+ p = pa_memblock_acquire(tchunk.memblock);
- p = pa_memblock_acquire(tchunk.memblock);
+ if (u->readf_function) {
+ fs = pa_frame_size(&i->sample_spec);
n = u->readf_function(u->sndfile, p, length/fs);
- pa_memblock_release(tchunk.memblock);
-
- if (n <= 0)
- n = 0;
-
- tchunk.length = n * fs;
-
} else {
- sf_count_t n;
- void *p;
-
- p = pa_memblock_acquire(tchunk.memblock);
+ fs = 1;
n = sf_read_raw(u->sndfile, p, length);
- pa_memblock_release(tchunk.memblock);
-
- if (n <= 0)
- n = 0;
-
- tchunk.length = n;
}
- if (tchunk.length <= 0) {
+ pa_memblock_release(tchunk.memblock);
+ if (n <= 0) {
pa_memblock_unref(tchunk.memblock);
sf_close(u->sndfile);
@@ -180,6 +188,8 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk
break;
}
+ tchunk.length = n * fs;
+
pa_memblockq_push(u->memblockq, &tchunk);
pa_memblock_unref(tchunk.memblock);
}
@@ -196,7 +206,7 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk
}
return -1;
-}
+ }
static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
file_stream *u;
@@ -334,6 +344,7 @@ int pa_play_file(
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->state_change = sink_input_state_change_cb;
u->sink_input->userdata = u;
pa_sink_input_get_silence(u->sink_input, &silence);
diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c
index de543a57..7f5f374e 100644
--- a/src/pulsecore/source-output.c
+++ b/src/pulsecore/source-output.c
@@ -88,6 +88,7 @@ static void reset_callbacks(pa_source_output *o) {
o->moved = NULL;
o->kill = NULL;
o->get_latency = NULL;
+ o->state_change = NULL;
}
pa_source_output* pa_source_output_new(
@@ -263,7 +264,7 @@ void pa_source_output_unlink(pa_source_output*o) {
pa_source_output_ref(o);
- linked = PA_SOURCE_OUTPUT_LINKED(o->state);
+ linked = PA_SOURCE_OUTPUT_IS_LINKED(o->state);
if (linked)
pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK], o);
@@ -295,7 +296,7 @@ static void source_output_free(pa_object* mo) {
pa_assert(pa_source_output_refcnt(o) == 0);
- if (PA_SOURCE_OUTPUT_LINKED(o->state))
+ if (PA_SOURCE_OUTPUT_IS_LINKED(o->state))
pa_source_output_unlink(o);
pa_log_info("Freeing output %u \"%s\"", o->index, pa_strnull(pa_proplist_gets(o->proplist, PA_PROP_MEDIA_NAME)));
@@ -335,7 +336,7 @@ void pa_source_output_put(pa_source_output *o) {
void pa_source_output_kill(pa_source_output*o) {
pa_source_output_assert_ref(o);
- pa_assert(PA_SOURCE_OUTPUT_LINKED(o->state));
+ pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
if (o->kill)
o->kill(o);
@@ -345,7 +346,7 @@ pa_usec_t pa_source_output_get_latency(pa_source_output *o) {
pa_usec_t r = 0;
pa_source_output_assert_ref(o);
- pa_assert(PA_SOURCE_OUTPUT_LINKED(o->state));
+ 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;
@@ -362,7 +363,7 @@ void pa_source_output_push(pa_source_output *o, const pa_memchunk *chunk) {
size_t limit, mbs = 0;
pa_source_output_assert_ref(o);
- pa_assert(PA_SOURCE_OUTPUT_LINKED(o->thread_info.state));
+ pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->thread_info.state));
pa_assert(chunk);
pa_assert(pa_frame_aligned(chunk->length, &o->source->sample_spec));
@@ -419,7 +420,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_LINKED(o->state));
+ pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
pa_assert(pa_frame_aligned(nbytes, &o->source->sample_spec));
if (nbytes <= 0)
@@ -446,28 +447,48 @@ void pa_source_output_process_rewind(pa_source_output *o, size_t nbytes /* in si
/* Called from thread context */
void pa_source_output_update_max_rewind(pa_source_output *o, size_t nbytes /* in the source's sample spec */) {
pa_source_output_assert_ref(o);
- pa_assert(PA_SOURCE_OUTPUT_LINKED(o->thread_info.state));
+ pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->thread_info.state));
pa_assert(pa_frame_aligned(nbytes, &o->source->sample_spec));
if (o->update_max_rewind)
o->update_max_rewind(o, o->thread_info.resampler ? pa_resampler_result(o->thread_info.resampler, nbytes) : nbytes);
}
-pa_usec_t pa_source_output_set_requested_latency(pa_source_output *o, pa_usec_t usec) {
- pa_source_output_assert_ref(o);
+static pa_usec_t fixup_latency(pa_source *s, pa_usec_t usec) {
+ pa_source_assert_ref(s);
- if (usec != (pa_usec_t) -1) {
+ if (usec == (pa_usec_t) -1)
+ return usec;
- if (o->source->max_latency > 0 && usec > o->source->max_latency)
- usec = o->source->max_latency;
+ if (s->max_latency > 0 && usec > s->max_latency)
+ usec = s->max_latency;
- if (o->source->min_latency > 0 && usec < o->source->min_latency)
- usec = o->source->min_latency;
- }
+ if (s->min_latency > 0 && usec < s->min_latency)
+ usec = s->min_latency;
+
+ return usec;
+}
+
+pa_usec_t pa_source_output_set_requested_latency_within_thread(pa_source_output *o, pa_usec_t usec) {
- if (PA_SOURCE_OUTPUT_LINKED(o->state))
+ usec = fixup_latency(o->source, usec);
+
+ o->thread_info.requested_source_latency = usec;
+ pa_source_invalidate_requested_latency(o->source);
+
+ return usec;
+}
+
+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);
else {
+ /* If this sink input is not realized yet, we have to touch
+ * the thread info data directly */
o->thread_info.requested_source_latency = usec;
o->source->thread_info.requested_latency_valid = FALSE;
}
@@ -477,14 +498,14 @@ pa_usec_t pa_source_output_set_requested_latency(pa_source_output *o, pa_usec_t
void pa_source_output_cork(pa_source_output *o, pa_bool_t b) {
pa_source_output_assert_ref(o);
- pa_assert(PA_SOURCE_OUTPUT_LINKED(o->state));
+ pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
source_output_set_state(o, b ? PA_SOURCE_OUTPUT_CORKED : PA_SOURCE_OUTPUT_RUNNING);
}
int pa_source_output_set_rate(pa_source_output *o, uint32_t rate) {
pa_source_output_assert_ref(o);
- pa_assert(PA_SOURCE_OUTPUT_LINKED(o->state));
+ pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
pa_return_val_if_fail(o->thread_info.resampler, -1);
if (o->sample_spec.rate == rate)
@@ -515,7 +536,7 @@ void pa_source_output_set_name(pa_source_output *o, const char *name) {
else
pa_proplist_unset(o->proplist, PA_PROP_MEDIA_NAME);
- if (PA_SOURCE_OUTPUT_LINKED(o->state)) {
+ if (PA_SOURCE_OUTPUT_IS_LINKED(o->state)) {
pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PROPLIST_CHANGED], o);
pa_subscription_post(o->source->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
}
@@ -533,7 +554,7 @@ int pa_source_output_move_to(pa_source_output *o, pa_source *dest) {
pa_source_output_move_hook_data hook_data;
pa_source_output_assert_ref(o);
- pa_assert(PA_SOURCE_OUTPUT_LINKED(o->state));
+ pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
pa_source_assert_ref(dest);
origin = o->source;
@@ -616,12 +637,21 @@ int pa_source_output_move_to(pa_source_output *o, pa_source *dest) {
return 0;
}
+void pa_source_output_set_state_within_thread(pa_source_output *o, pa_source_output_state_t state) {
+ pa_source_output_assert_ref(o);
+
+ if (o->state_change)
+ o->state_change(o, state);
+
+ o->thread_info.state = state;
+}
+
/* Called from thread context */
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_LINKED(o->thread_info.state));
+ pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->thread_info.state));
switch (code) {
@@ -633,25 +663,20 @@ int pa_source_output_process_msg(pa_msgobject *mo, int code, void *userdata, int
return 0;
}
- case PA_SOURCE_OUTPUT_MESSAGE_SET_RATE: {
+ case PA_SOURCE_OUTPUT_MESSAGE_SET_RATE:
o->thread_info.sample_spec.rate = PA_PTR_TO_UINT(userdata);
pa_resampler_set_output_rate(o->thread_info.resampler, PA_PTR_TO_UINT(userdata));
-
return 0;
- }
- case PA_SOURCE_OUTPUT_MESSAGE_SET_STATE: {
- o->thread_info.state = PA_PTR_TO_UINT(userdata);
+ case PA_SOURCE_OUTPUT_MESSAGE_SET_STATE:
+ pa_source_output_set_state_within_thread(o, PA_PTR_TO_UINT(userdata));
return 0;
- }
case PA_SOURCE_OUTPUT_MESSAGE_SET_REQUESTED_LATENCY:
- o->thread_info.requested_source_latency = (pa_usec_t) offset;
- pa_source_invalidate_requested_latency(o->source);
-
+ pa_source_output_set_requested_latency_within_thread(o, (pa_usec_t) offset);
return 0;
}
diff --git a/src/pulsecore/source-output.h b/src/pulsecore/source-output.h
index e7d8963f..67cb3761 100644
--- a/src/pulsecore/source-output.h
+++ b/src/pulsecore/source-output.h
@@ -42,7 +42,7 @@ typedef enum pa_source_output_state {
PA_SOURCE_OUTPUT_UNLINKED
} pa_source_output_state_t;
-static inline pa_bool_t PA_SOURCE_OUTPUT_LINKED(pa_source_output_state_t x) {
+static inline pa_bool_t PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_state_t x) {
return x == PA_SOURCE_OUTPUT_RUNNING || x == PA_SOURCE_OUTPUT_CORKED;
}
@@ -83,11 +83,11 @@ struct pa_source_output {
void (*push)(pa_source_output *o, const pa_memchunk *chunk);
/* Only relevant for monitor sources right now: called when the
- * recorded stream is rewound. */
+ * recorded stream is rewound. Called from IO context*/
void (*process_rewind)(pa_source_output *o, size_t nbytes);
/* Called whenever the maximum rewindable size of the source
- * changes. Called from RT context. */
+ * changes. Called from IO thread context. */
void (*update_max_rewind) (pa_source_output *o, size_t nbytes); /* may be NULL */
/* If non-NULL this function is called when the output is first
@@ -116,6 +116,10 @@ struct pa_source_output {
thread instead. */
pa_usec_t (*get_latency) (pa_source_output *o); /* may be NULL */
+ /* If non_NULL this function is called from thread context if the
+ * state changes. The old state is found in thread_info.state. */
+ void (*state_change) (pa_source_output *o, pa_source_output_state_t state); /* may be NULL */
+
struct {
pa_source_output_state_t state;
@@ -213,4 +217,8 @@ void pa_source_output_update_max_rewind(pa_source_output *o, size_t nbytes);
int pa_source_output_process_msg(pa_msgobject *mo, int code, void *userdata, int64_t offset, pa_memchunk *chunk);
+void pa_source_output_set_state_within_thread(pa_source_output *o, pa_source_output_state_t state);
+
+pa_usec_t pa_source_output_set_requested_latency_within_thread(pa_source_output *o, pa_usec_t usec);
+
#endif
diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c
index dab307e9..4a2173ca 100644
--- a/src/pulsecore/source.c
+++ b/src/pulsecore/source.c
@@ -228,8 +228,8 @@ static int source_set_state(pa_source *s, pa_source_state_t state) {
return 0;
suspend_change =
- (s->state == PA_SOURCE_SUSPENDED && PA_SOURCE_OPENED(state)) ||
- (PA_SOURCE_OPENED(s->state) && state == PA_SOURCE_SUSPENDED);
+ (s->state == PA_SOURCE_SUSPENDED && PA_SOURCE_IS_OPENED(state)) ||
+ (PA_SOURCE_IS_OPENED(s->state) && state == PA_SOURCE_SUSPENDED);
if (s->set_state)
if ((ret = s->set_state(s, state)) < 0)
@@ -284,7 +284,7 @@ void pa_source_unlink(pa_source *s) {
/* See pa_sink_unlink() for a couple of comments how this function
* works. */
- linked = PA_SOURCE_LINKED(s->state);
+ linked = PA_SOURCE_IS_LINKED(s->state);
if (linked)
pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK], s);
@@ -319,7 +319,7 @@ static void source_free(pa_object *o) {
pa_assert(s);
pa_assert(pa_source_refcnt(s) == 0);
- if (PA_SOURCE_LINKED(s->state))
+ if (PA_SOURCE_IS_LINKED(s->state))
pa_source_unlink(s);
pa_log_info("Freeing source %u \"%s\"", s->index, s->name);
@@ -345,21 +345,19 @@ static void source_free(pa_object *o) {
void pa_source_set_asyncmsgq(pa_source *s, pa_asyncmsgq *q) {
pa_source_assert_ref(s);
- pa_assert(q);
s->asyncmsgq = q;
}
void pa_source_set_rtpoll(pa_source *s, pa_rtpoll *p) {
pa_source_assert_ref(s);
- pa_assert(p);
s->rtpoll = p;
}
int pa_source_update_status(pa_source*s) {
pa_source_assert_ref(s);
- pa_assert(PA_SOURCE_LINKED(s->state));
+ pa_assert(PA_SOURCE_IS_LINKED(s->state));
if (s->state == PA_SOURCE_SUSPENDED)
return 0;
@@ -369,7 +367,7 @@ int pa_source_update_status(pa_source*s) {
int pa_source_suspend(pa_source *s, pa_bool_t suspend) {
pa_source_assert_ref(s);
- pa_assert(PA_SOURCE_LINKED(s->state));
+ pa_assert(PA_SOURCE_IS_LINKED(s->state));
if (suspend)
return source_set_state(s, PA_SOURCE_SUSPENDED);
@@ -382,7 +380,7 @@ void pa_source_process_rewind(pa_source *s, size_t nbytes) {
void *state = NULL;
pa_source_assert_ref(s);
- pa_assert(PA_SOURCE_OPENED(s->thread_info.state));
+ pa_assert(PA_SOURCE_IS_OPENED(s->thread_info.state));
if (nbytes <= 0)
return;
@@ -400,7 +398,7 @@ void pa_source_post(pa_source*s, const pa_memchunk *chunk) {
void *state = NULL;
pa_source_assert_ref(s);
- pa_assert(PA_SOURCE_OPENED(s->thread_info.state));
+ pa_assert(PA_SOURCE_IS_OPENED(s->thread_info.state));
pa_assert(chunk);
if (s->thread_info.state != PA_SOURCE_RUNNING)
@@ -436,9 +434,9 @@ pa_usec_t pa_source_get_latency(pa_source *s) {
pa_usec_t usec;
pa_source_assert_ref(s);
- pa_assert(PA_SOURCE_LINKED(s->state));
+ pa_assert(PA_SOURCE_IS_LINKED(s->state));
- if (!PA_SOURCE_OPENED(s->state))
+ if (!PA_SOURCE_IS_OPENED(s->state))
return 0;
if (s->get_latency)
@@ -454,7 +452,7 @@ void pa_source_set_volume(pa_source *s, const pa_cvolume *volume) {
int changed;
pa_source_assert_ref(s);
- pa_assert(PA_SOURCE_LINKED(s->state));
+ pa_assert(PA_SOURCE_IS_LINKED(s->state));
pa_assert(volume);
changed = !pa_cvolume_equal(volume, &s->volume);
@@ -474,7 +472,7 @@ const pa_cvolume *pa_source_get_volume(pa_source *s) {
pa_cvolume old_volume;
pa_source_assert_ref(s);
- pa_assert(PA_SOURCE_LINKED(s->state));
+ pa_assert(PA_SOURCE_IS_LINKED(s->state));
old_volume = s->volume;
@@ -494,7 +492,7 @@ void pa_source_set_mute(pa_source *s, pa_bool_t mute) {
int changed;
pa_source_assert_ref(s);
- pa_assert(PA_SOURCE_LINKED(s->state));
+ pa_assert(PA_SOURCE_IS_LINKED(s->state));
changed = s->muted != mute;
s->muted = mute;
@@ -513,7 +511,7 @@ pa_bool_t pa_source_get_mute(pa_source *s) {
pa_bool_t old_muted;
pa_source_assert_ref(s);
- pa_assert(PA_SOURCE_LINKED(s->state));
+ pa_assert(PA_SOURCE_IS_LINKED(s->state));
old_muted = s->muted;
@@ -546,7 +544,7 @@ void pa_source_set_description(pa_source *s, const char *description) {
else
pa_proplist_unset(s->proplist, PA_PROP_DEVICE_DESCRIPTION);
- if (PA_SOURCE_LINKED(s->state)) {
+ if (PA_SOURCE_IS_LINKED(s->state)) {
pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_PROPLIST_CHANGED], s);
}
@@ -554,7 +552,7 @@ void pa_source_set_description(pa_source *s, const char *description) {
unsigned pa_source_linked_by(pa_source *s) {
pa_source_assert_ref(s);
- pa_assert(PA_SOURCE_LINKED(s->state));
+ pa_assert(PA_SOURCE_IS_LINKED(s->state));
return pa_idxset_size(s->outputs);
}
@@ -563,7 +561,7 @@ unsigned pa_source_used_by(pa_source *s) {
unsigned ret;
pa_source_assert_ref(s);
- pa_assert(PA_SOURCE_LINKED(s->state));
+ pa_assert(PA_SOURCE_IS_LINKED(s->state));
ret = pa_idxset_size(s->outputs);
pa_assert(ret >= s->n_corked);
@@ -590,6 +588,8 @@ int pa_source_process_msg(pa_msgobject *object, int code, void *userdata, int64_
if (o->attach)
o->attach(o);
+ pa_source_output_set_state_within_thread(o, o->state);
+
pa_source_invalidate_requested_latency(s);
return 0;
@@ -598,6 +598,8 @@ int pa_source_process_msg(pa_msgobject *object, int code, void *userdata, int64_
case PA_SOURCE_MESSAGE_REMOVE_OUTPUT: {
pa_source_output *o = PA_SOURCE_OUTPUT(userdata);
+ pa_source_output_set_state_within_thread(o, o->state);
+
if (o->detach)
o->detach(o);
@@ -676,14 +678,14 @@ int pa_source_suspend_all(pa_core *c, pa_bool_t suspend) {
void pa_source_detach(pa_source *s) {
pa_source_assert_ref(s);
- pa_assert(PA_SOURCE_LINKED(s->state));
+ pa_assert(PA_SOURCE_IS_LINKED(s->state));
pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_DETACH, NULL, 0, NULL);
}
void pa_source_attach(pa_source *s) {
pa_source_assert_ref(s);
- pa_assert(PA_SOURCE_LINKED(s->state));
+ pa_assert(PA_SOURCE_IS_LINKED(s->state));
pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_ATTACH, NULL, 0, NULL);
}
@@ -693,7 +695,7 @@ void pa_source_detach_within_thread(pa_source *s) {
void *state = NULL;
pa_source_assert_ref(s);
- pa_assert(PA_SOURCE_LINKED(s->thread_info.state));
+ pa_assert(PA_SOURCE_IS_LINKED(s->thread_info.state));
while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL)))
if (o->detach)
@@ -705,7 +707,7 @@ void pa_source_attach_within_thread(pa_source *s) {
void *state = NULL;
pa_source_assert_ref(s);
- pa_assert(PA_SOURCE_LINKED(s->thread_info.state));
+ pa_assert(PA_SOURCE_IS_LINKED(s->thread_info.state));
while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL)))
if (o->attach)
@@ -746,9 +748,9 @@ pa_usec_t pa_source_get_requested_latency(pa_source *s) {
pa_usec_t usec;
pa_source_assert_ref(s);
- pa_assert(PA_SOURCE_LINKED(s->state));
+ pa_assert(PA_SOURCE_IS_LINKED(s->state));
- if (!PA_SOURCE_OPENED(s->state))
+ 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)
@@ -778,7 +780,7 @@ void pa_source_set_max_rewind(pa_source *s, size_t max_rewind) {
void pa_source_invalidate_requested_latency(pa_source *s) {
pa_source_assert_ref(s);
- pa_assert(PA_SOURCE_LINKED(s->thread_info.state));
+ pa_assert(PA_SOURCE_IS_LINKED(s->thread_info.state));
if (!s->thread_info.requested_latency_valid)
return;
diff --git a/src/pulsecore/source.h b/src/pulsecore/source.h
index b8859c84..cce54620 100644
--- a/src/pulsecore/source.h
+++ b/src/pulsecore/source.h
@@ -33,7 +33,6 @@ typedef struct pa_source pa_source;
#include <pulse/channelmap.h>
#include <pulse/volume.h>
-#include <pulsecore/core-def.h>
#include <pulsecore/core.h>
#include <pulsecore/idxset.h>
#include <pulsecore/memblock.h>
@@ -54,11 +53,11 @@ typedef enum pa_source_state {
PA_SOURCE_UNLINKED
} pa_source_state_t;
-static inline pa_bool_t PA_SOURCE_OPENED(pa_source_state_t x) {
+static inline pa_bool_t PA_SOURCE_IS_OPENED(pa_source_state_t x) {
return x == PA_SOURCE_RUNNING || x == PA_SOURCE_IDLE;
}
-static inline pa_bool_t PA_SOURCE_LINKED(pa_source_state_t x) {
+static inline pa_bool_t PA_SOURCE_IS_LINKED(pa_source_state_t x) {
return x == PA_SOURCE_RUNNING || x == PA_SOURCE_IDLE || x == PA_SOURCE_SUSPENDED;
}
diff --git a/src/utils/pacmd.c b/src/utils/pacmd.c
index daa6a96e..dff9af9d 100644
--- a/src/utils/pacmd.c
+++ b/src/utils/pacmd.c
@@ -36,6 +36,7 @@
#include <pulse/error.h>
#include <pulse/util.h>
+#include <pulse/xmalloc.h>
#include <pulsecore/core-util.h>
#include <pulsecore/log.h>
@@ -49,6 +50,7 @@ int main(PA_GCC_UNUSED int argc, PA_GCC_UNUSED char*argv[]) {
char ibuf[256], obuf[256];
size_t ibuf_index, ibuf_length, obuf_index, obuf_length;
fd_set ifds, ofds;
+ char *cli;
if (pa_pid_file_check_running(&pid, "pulseaudio") < 0) {
pa_log("no PulseAudio daemon running");
@@ -62,7 +64,10 @@ int main(PA_GCC_UNUSED int argc, PA_GCC_UNUSED char*argv[]) {
memset(&sa, 0, sizeof(sa));
sa.sun_family = AF_UNIX;
- pa_runtime_path("cli", sa.sun_path, sizeof(sa.sun_path));
+
+ cli = pa_runtime_path("cli");
+ pa_strlcpy(sa.sun_path, cli, sizeof(sa.sun_path));
+ pa_xfree(cli);
for (i = 0; i < 5; i++) {
int r;