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
This commit is contained in:
Lennart Poettering 2008-05-01 19:51:05 +00:00
parent f94fae3da3
commit 52e3628c3e
48 changed files with 2408 additions and 1232 deletions

View file

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

View file

@ -33,6 +33,7 @@
#include <sched.h> #include <sched.h>
#include <pulse/xmalloc.h> #include <pulse/xmalloc.h>
#include <pulse/timeval.h>
#include <pulsecore/core-error.h> #include <pulsecore/core-error.h>
#include <pulsecore/core-util.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 PA_DEFAULT_CONFIG_DIR PA_PATH_SEP "default.pa"
#define DEFAULT_SCRIPT_FILE_USER 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 PA_DEFAULT_CONFIG_DIR PA_PATH_SEP "daemon.conf"
#define DEFAULT_CONFIG_FILE_USER 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, .auto_log_target = 1,
.script_commands = NULL, .script_commands = NULL,
.dl_search_path = NULL, .dl_search_path = NULL,
.load_default_script_file = TRUE,
.default_script_file = NULL, .default_script_file = NULL,
.log_target = PA_LOG_SYSLOG, .log_target = PA_LOG_SYSLOG,
.log_level = PA_LOG_NOTICE, .log_level = PA_LOG_NOTICE,
@ -81,34 +85,43 @@ static const pa_daemon_conf default_conf = {
.default_fragment_size_msec = 25, .default_fragment_size_msec = 25,
.default_sample_spec = { .format = PA_SAMPLE_S16NE, .rate = 44100, .channels = 2 } .default_sample_spec = { .format = PA_SAMPLE_S16NE, .rate = 44100, .channels = 2 }
#ifdef HAVE_SYS_RESOURCE_H #ifdef HAVE_SYS_RESOURCE_H
, .rlimit_as = { .value = 0, .is_set = FALSE }, ,.rlimit_fsize = { .value = 0, .is_set = FALSE },
.rlimit_core = { .value = 0, .is_set = FALSE },
.rlimit_data = { .value = 0, .is_set = FALSE }, .rlimit_data = { .value = 0, .is_set = FALSE },
.rlimit_fsize = { .value = 0, .is_set = FALSE }, .rlimit_stack = { .value = 0, .is_set = FALSE },
.rlimit_nofile = { .value = 256, .is_set = TRUE }, .rlimit_core = { .value = 0, .is_set = FALSE },
.rlimit_stack = { .value = 0, .is_set = FALSE } .rlimit_rss = { .value = 0, .is_set = FALSE }
#ifdef RLIMIT_NPROC #ifdef RLIMIT_NPROC
, .rlimit_nproc = { .value = 0, .is_set = FALSE } ,.rlimit_nproc = { .value = 0, .is_set = FALSE }
#endif #endif
,.rlimit_nofile = { .value = 256, .is_set = TRUE }
#ifdef RLIMIT_MEMLOCK #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 #endif
#ifdef RLIMIT_NICE #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 #endif
#ifdef RLIMIT_RTPRIO #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
#endif #endif
}; };
pa_daemon_conf* pa_daemon_conf_new(void) { pa_daemon_conf* pa_daemon_conf_new(void) {
FILE *f;
pa_daemon_conf *c = pa_xnewdup(pa_daemon_conf, &default_conf, 1); 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); c->dl_search_path = pa_xstrdup(PA_DLSEARCHPATH);
return c; 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 }, { "default-fragment-size-msec", parse_fragment_size_msec, NULL },
{ "nice-level", parse_nice_level, NULL }, { "nice-level", parse_nice_level, NULL },
{ "disable-remixing", pa_config_parse_bool, NULL }, { "disable-remixing", pa_config_parse_bool, NULL },
{ "load-default-script-file", pa_config_parse_bool, NULL },
#ifdef HAVE_SYS_RESOURCE_H #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-fsize", parse_rlimit, NULL },
{ "rlimit-nofile", parse_rlimit, NULL }, { "rlimit-data", parse_rlimit, NULL },
{ "rlimit-stack", 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 #ifdef RLIMIT_NPROC
{ "rlimit-nproc", parse_rlimit, NULL }, { "rlimit-nproc", parse_rlimit, NULL },
#endif #endif
#ifdef RLIMIT_MEMLOCK #ifdef RLIMIT_MEMLOCK
{ "rlimit-memlock", parse_rlimit, NULL }, { "rlimit-memlock", parse_rlimit, NULL },
#endif #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 #ifdef RLIMIT_NICE
{ "rlimit-nice", parse_rlimit, NULL }, { "rlimit-nice", parse_rlimit, NULL },
#endif #endif
#ifdef RLIMIT_RTPRIO #ifdef RLIMIT_RTPRIO
{ "rlimit-rtprio", parse_rlimit, NULL }, { "rlimit-rtprio", parse_rlimit, NULL },
#endif #endif
#ifdef RLIMIT_RTTIME
{ "rlimit-rttime", parse_rlimit, NULL },
#endif
#endif #endif
{ NULL, NULL, NULL }, { NULL, NULL, NULL },
}; };
@ -461,33 +488,66 @@ int pa_daemon_conf_load(pa_daemon_conf *c, const char *filename) {
table[23].data = c; table[23].data = c;
table[24].data = c; table[24].data = c;
table[25].data = &c->disable_remixing; table[25].data = &c->disable_remixing;
table[26].data = &c->load_default_script_file;
#ifdef HAVE_SYS_RESOURCE_H #ifdef HAVE_SYS_RESOURCE_H
table[26].data = &c->rlimit_as; table[27].data = &c->rlimit_fsize;
table[27].data = &c->rlimit_core;
table[28].data = &c->rlimit_data; table[28].data = &c->rlimit_data;
table[29].data = &c->rlimit_fsize; table[29].data = &c->rlimit_stack;
table[30].data = &c->rlimit_nofile; table[30].data = &c->rlimit_as;
table[31].data = &c->rlimit_stack; table[31].data = &c->rlimit_core;
table[32].data = &c->rlimit_nofile;
table[33].data = &c->rlimit_as;
#ifdef RLIMIT_NPROC #ifdef RLIMIT_NPROC
table[32].data = &c->rlimit_nproc; table[34].data = &c->rlimit_nproc;
#endif #endif
#ifdef RLIMIT_MEMLOCK #ifdef RLIMIT_MEMLOCK
#ifndef RLIMIT_NPROC #ifndef RLIMIT_NPROC
#error "Houston, we have a numbering problem!" #error "Houston, we have a numbering problem!"
#endif #endif
table[33].data = &c->rlimit_memlock; table[35].data = &c->rlimit_memlock;
#endif #endif
#ifdef RLIMIT_NICE
#ifdef RLIMIT_LOCKS
#ifndef RLIMIT_MEMLOCK #ifndef RLIMIT_MEMLOCK
#error "Houston, we have a numbering problem!" #error "Houston, we have a numbering problem!"
#endif #endif
table[34].data = &c->rlimit_nice; table[36].data = &c->rlimit_locks;
#endif #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 #ifdef RLIMIT_RTPRIO
#ifndef RLIMIT_NICE #ifndef RLIMIT_NICE
#error "Houston, we have a numbering problem!" #error "Houston, we have a numbering problem!"
#endif #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
#endif #endif
@ -496,10 +556,10 @@ int pa_daemon_conf_load(pa_daemon_conf *c, const char *filename) {
f = filename ? f = filename ?
fopen(c->config_file = pa_xstrdup(filename), "r") : 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) { 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; goto finish;
} }
@ -514,6 +574,7 @@ finish:
int pa_daemon_conf_env(pa_daemon_conf *c) { int pa_daemon_conf_env(pa_daemon_conf *c) {
char *e; char *e;
pa_assert(c);
if ((e = getenv(ENV_DL_SEARCH_PATH))) { if ((e = getenv(ENV_DL_SEARCH_PATH))) {
pa_xfree(c->dl_search_path); pa_xfree(c->dl_search_path);
@ -527,6 +588,35 @@ int pa_daemon_conf_env(pa_daemon_conf *c) {
return 0; 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[] = { static const char* const log_level_to_string[] = {
[PA_LOG_DEBUG] = "debug", [PA_LOG_DEBUG] = "debug",
[PA_LOG_INFO] = "info", [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, "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, "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, "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, "dl-search-path = %s\n", pa_strempty(c->dl_search_path));
pa_strbuf_printf(s, "default-script-file = %s\n", c->default_script_file); 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-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, "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)); 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-fragments = %u\n", c->default_n_fragments);
pa_strbuf_printf(s, "default-fragment-size-msec = %u\n", c->default_fragment_size_msec); pa_strbuf_printf(s, "default-fragment-size-msec = %u\n", c->default_fragment_size_msec);
#ifdef HAVE_SYS_RESOURCE_H #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-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-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 #ifdef RLIMIT_NPROC
pa_strbuf_printf(s, "rlimit-nproc = %li\n", c->rlimit_nproc.is_set ? (long int) c->rlimit_nproc.value : -1); pa_strbuf_printf(s, "rlimit-nproc = %li\n", c->rlimit_nproc.is_set ? (long int) c->rlimit_nproc.value : -1);
#endif #endif
pa_strbuf_printf(s, "rlimit-nofile = %li\n", c->rlimit_nofile.is_set ? (long int) c->rlimit_nofile.value : -1);
#ifdef RLIMIT_MEMLOCK #ifdef RLIMIT_MEMLOCK
pa_strbuf_printf(s, "rlimit-memlock = %li\n", c->rlimit_memlock.is_set ? (long int) c->rlimit_memlock.value : -1); pa_strbuf_printf(s, "rlimit-memlock = %li\n", c->rlimit_memlock.is_set ? (long int) c->rlimit_memlock.value : -1);
#endif #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 #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 #endif
#ifdef RLIMIT_RTPRIO #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
#endif #endif

View file

@ -27,6 +27,7 @@
#include <pulsecore/log.h> #include <pulsecore/log.h>
#include <pulsecore/macro.h> #include <pulsecore/macro.h>
#include <pulsecore/core-util.h>
#include <pulse/sample.h> #include <pulse/sample.h>
#ifdef HAVE_SYS_RESOURCE_H #ifdef HAVE_SYS_RESOURCE_H
@ -65,7 +66,8 @@ typedef struct pa_daemon_conf {
system_instance, system_instance,
no_cpu_limit, no_cpu_limit,
disable_shm, disable_shm,
disable_remixing; disable_remixing,
load_default_script_file;
int exit_idle_time, int exit_idle_time,
module_idle_time, module_idle_time,
scache_idle_time, scache_idle_time,
@ -79,19 +81,31 @@ typedef struct pa_daemon_conf {
char *config_file; char *config_file;
#ifdef HAVE_SYS_RESOURCE_H #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 #ifdef RLIMIT_NPROC
pa_rlimit rlimit_nproc; pa_rlimit rlimit_nproc;
#endif #endif
#ifdef RLIMIT_MEMLOCK #ifdef RLIMIT_MEMLOCK
pa_rlimit rlimit_memlock; pa_rlimit rlimit_memlock;
#endif #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 #ifdef RLIMIT_NICE
pa_rlimit rlimit_nice; pa_rlimit rlimit_nice;
#endif #endif
#ifdef RLIMIT_RTPRIO #ifdef RLIMIT_RTPRIO
pa_rlimit rlimit_rtprio; pa_rlimit rlimit_rtprio;
#endif #endif
#ifdef RLIMIT_RTTIME
pa_rlimit rlimit_rttime;
#endif
#endif #endif
unsigned default_n_fragments, default_fragment_size_msec; 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_log_level(pa_daemon_conf *c, const char *string);
int pa_daemon_conf_set_resample_method(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 #endif

View file

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

View file

@ -115,7 +115,7 @@ static void message_cb(pa_mainloop_api*a, pa_time_event*e, PA_GCC_UNUSED const s
MSG msg; MSG msg;
struct timeval tvnext; struct timeval tvnext;
while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
if (msg.message == WM_QUIT) if (msg.message == WM_QUIT)
raise(SIGTERM); raise(SIGTERM);
else { 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) #if defined(HAVE_PWD_H) && defined(HAVE_GRP_H)
static int change_user(void) { static int change_user(void) {
@ -241,14 +239,14 @@ static int change_user(void) {
return -1; return -1;
} }
set_env("USER", PA_SYSTEM_USER); pa_set_env("USER", PA_SYSTEM_USER);
set_env("USERNAME", PA_SYSTEM_USER); pa_set_env("USERNAME", PA_SYSTEM_USER);
set_env("LOGNAME", PA_SYSTEM_USER); pa_set_env("LOGNAME", PA_SYSTEM_USER);
set_env("HOME", PA_SYSTEM_RUNTIME_PATH); pa_set_env("HOME", PA_SYSTEM_RUNTIME_PATH);
/* Relevant for pa_runtime_path() */ /* Relevant for pa_runtime_path() */
set_env("PULSE_RUNTIME_PATH", PA_SYSTEM_RUNTIME_PATH); pa_set_env("PULSE_RUNTIME_PATH", PA_SYSTEM_RUNTIME_PATH);
set_env("PULSE_CONFIG_PATH", PA_SYSTEM_RUNTIME_PATH); pa_set_env("PULSE_CONFIG_PATH", PA_SYSTEM_RUNTIME_PATH);
pa_log_info("Successfully dropped root privileges."); pa_log_info("Successfully dropped root privileges.");
@ -264,23 +262,6 @@ static int change_user(void) {
#endif /* HAVE_PWD_H && HAVE_GRP_H */ #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 #ifdef HAVE_SYS_RESOURCE_H
static int set_one_rlimit(const pa_rlimit *r, int resource, const char *name) { 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; rl.rlim_cur = rl.rlim_max = r->value;
if (setrlimit(resource, &rl) < 0) { 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; return -1;
} }
@ -301,17 +282,27 @@ static int set_one_rlimit(const pa_rlimit *r, int resource, const char *name) {
} }
static void set_all_rlimits(const pa_daemon_conf *conf) { 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_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_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 #ifdef RLIMIT_NPROC
set_one_rlimit(&conf->rlimit_nproc, RLIMIT_NPROC, "RLIMIT_NPROC"); set_one_rlimit(&conf->rlimit_nproc, RLIMIT_NPROC, "RLIMIT_NPROC");
#endif #endif
set_one_rlimit(&conf->rlimit_nofile, RLIMIT_NOFILE, "RLIMIT_NOFILE");
#ifdef RLIMIT_MEMLOCK #ifdef RLIMIT_MEMLOCK
set_one_rlimit(&conf->rlimit_memlock, RLIMIT_MEMLOCK, "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 #endif
#ifdef RLIMIT_NICE #ifdef RLIMIT_NICE
set_one_rlimit(&conf->rlimit_nice, RLIMIT_NICE, "RLIMIT_NICE"); set_one_rlimit(&conf->rlimit_nice, RLIMIT_NICE, "RLIMIT_NICE");
@ -319,6 +310,9 @@ static void set_all_rlimits(const pa_daemon_conf *conf) {
#ifdef RLIMIT_RTPRIO #ifdef RLIMIT_RTPRIO
set_one_rlimit(&conf->rlimit_rtprio, RLIMIT_RTPRIO, "RLIMIT_RTPRIO"); set_one_rlimit(&conf->rlimit_rtprio, RLIMIT_RTPRIO, "RLIMIT_RTPRIO");
#endif #endif
#ifdef RLIMIT_RTTIME
set_one_rlimit(&conf->rlimit_rttime, RLIMIT_RTTIME, "RLIMIT_RTTIME");
#endif
} }
#endif #endif
@ -329,18 +323,19 @@ int main(int argc, char *argv[]) {
pa_mainloop *mainloop = NULL; pa_mainloop *mainloop = NULL;
char *s; char *s;
int r = 0, retval = 1, d = 0; int r = 0, retval = 1, d = 0;
int daemon_pipe[2] = { -1, -1 };
pa_bool_t suid_root, real_root; 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; gid_t gid = (gid_t) -1;
pa_bool_t allow_realtime, allow_high_priority;
pa_bool_t ltdl_init = FALSE; pa_bool_t ltdl_init = FALSE;
int passed_fd = -1;
#ifdef OS_IS_WIN32 const char *e;
pa_time_event *timer; #ifdef HAVE_FORK
struct timeval tv; int daemon_pipe[2] = { -1, -1 };
#endif
#ifdef OS_IS_WIN32
pa_time_event *win32_timer;
struct timeval win32_tv;
#endif #endif
#if defined(__linux__) && defined(__OPTIMIZE__) #if defined(__linux__) && defined(__OPTIMIZE__)
/* /*
@ -355,7 +350,7 @@ int main(int argc, char *argv[]) {
/* We have to execute ourselves, because the libc caches the /* We have to execute ourselves, because the libc caches the
* value of $LD_BIND_NOW on initialization. */ * 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(rp = pa_readlink("/proc/self/exe"));
pa_assert_se(execv(rp, argv) == 0); 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. */ * 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 /* 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 * we were started SUID. If we are started as normal root, than we
* still are normal root. */ * 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); pa_log_set_target(conf->auto_log_target ? PA_LOG_STDERR : conf->log_target, NULL);
if (suid_root) { if (suid_root) {
pa_bool_t allow_realtime, allow_high_priority;
/* Ok, we're suid root, so let's better not enable high prio /* Ok, we're suid root, so let's better not enable high prio
* or RT by default */ * or RT by default */
allow_high_priority = allow_realtime = FALSE; 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 #ifdef HAVE_POLKIT
if (conf->high_priority) { if (conf->high_priority && !allow_high_priority) {
if (pa_polkit_check("org.pulseaudio.acquire-high-priority") > 0) { 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; allow_high_priority = TRUE;
} else } 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) { 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; allow_realtime = TRUE;
} else } else
pa_log_info("PolicyKit refuses acquire-real-time privilige."); pa_log_info("PolicyKit refuses acquire-real-time privilege.");
} }
#endif #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) { if (!allow_high_priority && !allow_realtime) {
/* OK, there's no further need to keep CAP_NICE. Hence /* OK, there's no further need to keep CAP_NICE. Hence
* let's give it up early */ * let's give it up early */
pa_drop_caps(); pa_drop_caps();
pa_drop_root(); suid_root = FALSE;
suid_root = real_root = FALSE;
if (conf->high_priority || conf->realtime_scheduling) 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" 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" "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."); "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) { #ifdef HAVE_SYS_RESOURCE_H
pa_log_info("High-priority scheduling enabled in configuration but now allowed by policy. Disabling forcibly."); set_all_rlimits(conf);
conf->high_priority = FALSE; #endif
}
if (conf->realtime_scheduling && !allow_realtime) { if (conf->high_priority && !pa_can_high_priority())
pa_log_info("Real-time scheduling enabled in configuration but now allowed by policy. Disabling forcibly."); pa_log_warn("High-priority scheduling enabled in configuration but now allowed by policy.");
conf->realtime_scheduling = FALSE;
}
if (conf->high_priority && conf->cmd == PA_CMD_DAEMON) if (conf->high_priority && conf->cmd == PA_CMD_DAEMON)
pa_raise_priority(conf->nice_level); pa_raise_priority(conf->nice_level);
@ -482,28 +481,38 @@ int main(int argc, char *argv[]) {
#ifdef RLIMIT_RTPRIO #ifdef RLIMIT_RTPRIO
if (!drop) { if (!drop) {
struct rlimit rl;
/* At this point we still have CAP_NICE if we were loaded /* At this point we still have CAP_NICE if we were loaded
* SUID root. If possible let's acquire RLIMIT_RTPRIO * SUID root. If possible let's acquire RLIMIT_RTPRIO
* instead and give CAP_NICE up. */ * 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) { if (rl.rlim_cur >= 9)
pa_log_info("Successfully increased RLIMIT_RTPRIO, giving up CAP_NICE."); drop = TRUE;
drop = TRUE; else {
} else rl.rlim_max = rl.rlim_cur = 9;
pa_log_warn("RLIMIT_RTPRIO failed: %s", pa_cstrerror(errno));
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 #endif
if (drop) { if (drop) {
pa_log_info("Giving up CAP_NICE");
pa_drop_caps(); pa_drop_caps();
pa_drop_root(); suid_root = FALSE;
suid_root = real_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(); LTDL_SET_PRELOADED_SYMBOLS();
pa_ltdl_init(); pa_ltdl_init();
ltdl_init = TRUE; ltdl_init = TRUE;
@ -605,7 +614,7 @@ int main(int argc, char *argv[]) {
#ifdef HAVE_FORK #ifdef HAVE_FORK
if (pipe(daemon_pipe) < 0) { if (pipe(daemon_pipe) < 0) {
pa_log("Failed to create pipe."); pa_log("pipe failed: %s", pa_cstrerror(errno));
goto finish; goto finish;
} }
@ -615,20 +624,24 @@ int main(int argc, char *argv[]) {
} }
if (child != 0) { if (child != 0) {
ssize_t n;
/* Father */ /* Father */
pa_assert_se(pa_close(daemon_pipe[1]) == 0); pa_assert_se(pa_close(daemon_pipe[1]) == 0);
daemon_pipe[1] = -1; daemon_pipe[1] = -1;
if (pa_loop_read(daemon_pipe[0], &retval, sizeof(retval), NULL) != sizeof(retval)) { if ((n = pa_loop_read(daemon_pipe[0], &retval, sizeof(retval), NULL)) != sizeof(retval)) {
pa_log("read() failed: %s", pa_cstrerror(errno));
if (n < 0)
pa_log("read() failed: %s", pa_cstrerror(errno));
retval = 1; retval = 1;
} }
if (retval) if (retval)
pa_log("daemon startup failed."); pa_log("Daemon startup failed.");
else else
pa_log_info("daemon startup successful."); pa_log_info("Daemon startup successful.");
goto finish; goto finish;
} }
@ -652,9 +665,9 @@ int main(int argc, char *argv[]) {
pa_close(1); pa_close(1);
pa_close(2); pa_close(2);
open("/dev/null", O_RDONLY); pa_assert_se(open("/dev/null", O_RDONLY) == 0);
open("/dev/null", O_WRONLY); pa_assert_se(open("/dev/null", O_WRONLY) == 1);
open("/dev/null", O_WRONLY); pa_assert_se(open("/dev/null", O_WRONLY) == 2);
#else #else
FreeConsole(); FreeConsole();
#endif #endif
@ -677,39 +690,32 @@ int main(int argc, char *argv[]) {
#endif #endif
} }
pa_set_env("PULSE_INTERNAL", "1");
pa_assert_se(chdir("/") == 0); pa_assert_se(chdir("/") == 0);
umask(0022); umask(0022);
if (conf->system_instance) { if (conf->system_instance)
if (change_user() < 0) if (change_user() < 0)
goto finish; 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 (conf->use_pid_file) {
if (pa_pid_file_create() < 0) { if (pa_pid_file_create() < 0) {
pa_log("pa_pid_file_create() failed."); 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; goto finish;
} }
valid_pid_file = 1; valid_pid_file = TRUE;
} }
#ifdef HAVE_SYS_RESOURCE_H
set_all_rlimits(conf);
#endif
#ifdef SIGPIPE #ifdef SIGPIPE
signal(SIGPIPE, SIG_IGN); signal(SIGPIPE, SIG_IGN);
#endif #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()) if (pa_rtclock_hrtimer())
pa_log_info("Fresh high-resolution timers available! Bon appetit!"); pa_log_info("Fresh high-resolution timers available! Bon appetit!");
else else
@ -738,11 +744,11 @@ int main(int argc, char *argv[]) {
c->realtime_priority = conf->realtime_priority; c->realtime_priority = conf->realtime_priority;
c->realtime_scheduling = !!conf->realtime_scheduling; c->realtime_scheduling = !!conf->realtime_scheduling;
c->disable_remixing = !!conf->disable_remixing; 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_assert_se(pa_signal_init(pa_mainloop_get_api(mainloop)) == 0);
pa_signal_new(SIGINT, signal_callback, c); pa_signal_new(SIGINT, signal_callback, c);
pa_signal_new(SIGTERM, signal_callback, c); pa_signal_new(SIGTERM, signal_callback, c);
#ifdef SIGUSR1 #ifdef SIGUSR1
pa_signal_new(SIGUSR1, signal_callback, c); pa_signal_new(SIGUSR1, signal_callback, c);
#endif #endif
@ -754,23 +760,27 @@ int main(int argc, char *argv[]) {
#endif #endif
#ifdef OS_IS_WIN32 #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 #endif
if (conf->daemonize)
c->running_as_daemon = TRUE;
oil_init(); oil_init();
if (!conf->no_cpu_limit) if (!conf->no_cpu_limit)
pa_assert_se(pa_cpu_limit_init(pa_mainloop_get_api(mainloop)) == 0); pa_assert_se(pa_cpu_limit_init(pa_mainloop_get_api(mainloop)) == 0);
buf = pa_strbuf_new(); buf = pa_strbuf_new();
if (conf->default_script_file) if (conf->load_default_script_file) {
r = pa_cli_command_execute_file(c, conf->default_script_file, buf, &conf->fail); 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) if (r >= 0)
r = pa_cli_command_execute(c, conf->script_commands, buf, &conf->fail); r = pa_cli_command_execute(c, conf->script_commands, buf, &conf->fail);
pa_log_error("%s", s = pa_strbuf_tostring_free(buf)); pa_log_error("%s", s = pa_strbuf_tostring_free(buf));
pa_xfree(s); pa_xfree(s);
@ -780,53 +790,55 @@ int main(int argc, char *argv[]) {
if (r < 0 && conf->fail) { if (r < 0 && conf->fail) {
pa_log("Failed to initialize daemon."); pa_log("Failed to initialize daemon.");
#ifdef HAVE_FORK goto finish;
if (conf->daemonize)
pa_loop_write(daemon_pipe[1], &retval, sizeof(retval), NULL);
#endif
} else if (!c->modules || pa_idxset_size(c->modules) == 0) {
pa_log("daemon startup without any loaded modules, refusing to work.");
#ifdef HAVE_FORK
if (conf->daemonize)
pa_loop_write(daemon_pipe[1], &retval, sizeof(retval), NULL);
#endif
} else {
retval = 0;
if (c->default_sink_name &&
pa_namereg_get(c, c->default_sink_name, PA_NAMEREG_SINK, 1) == NULL) {
pa_log_error("%s : Default sink name (%s) does not exist in name register.", __FILE__, c->default_sink_name);
retval = !!conf->fail;
}
#ifdef HAVE_FORK
if (conf->daemonize)
pa_loop_write(daemon_pipe[1], &retval, sizeof(retval), NULL);
#endif
if (!retval) {
pa_log_info("Daemon startup complete.");
if (pa_mainloop_run(mainloop, &retval) < 0)
retval = 1;
pa_log_info("Daemon shutdown initiated.");
}
} }
#ifdef OS_IS_WIN32 if (!c->modules || pa_idxset_size(c->modules) == 0) {
pa_mainloop_get_api(mainloop)->time_free(timer); pa_log("Daemon startup without any loaded modules, refusing to work.");
goto finish;
}
if (c->default_sink_name && !pa_namereg_get(c, c->default_sink_name, PA_NAMEREG_SINK, TRUE) && conf->fail) {
pa_log_error("Default sink name (%s) does not exist in name register.", c->default_sink_name);
goto finish;
}
#ifdef HAVE_FORK
if (conf->daemonize) {
int ok = 0;
pa_loop_write(daemon_pipe[1], &ok, sizeof(ok), NULL);
}
#endif #endif
pa_core_unref(c); pa_log_info("Daemon startup complete.");
retval = 0;
if (pa_mainloop_run(mainloop, &retval) < 0)
goto finish;
pa_log_info("Daemon shutdown initiated.");
finish:
#ifdef OS_IS_WIN32
if (win32_timer)
pa_mainloop_get_api(mainloop)->time_free(win32_timer);
#endif
if (c) {
pa_core_unref(c);
pa_log_info("Daemon terminated.");
}
if (!conf->no_cpu_limit) if (!conf->no_cpu_limit)
pa_cpu_limit_done(); pa_cpu_limit_done();
pa_signal_done(); pa_signal_done();
pa_log_info("Daemon terminated."); #ifdef HAVE_FORK
pa_close_pipe(daemon_pipe);
finish: #endif
if (mainloop) if (mainloop)
pa_mainloop_free(mainloop); pa_mainloop_free(mainloop);
@ -837,8 +849,6 @@ finish:
if (valid_pid_file) if (valid_pid_file)
pa_pid_file_remove(); pa_pid_file_remove();
pa_close_pipe(daemon_pipe);
#ifdef OS_IS_WIN32 #ifdef OS_IS_WIN32
WSACleanup(); WSACleanup();
#endif #endif

View file

@ -671,8 +671,24 @@ snd_pcm_t *pa_alsa_open_by_device_string(
*dev = d; *dev = d;
if (ss->channels != map->channels) { if (ss->channels != map->channels) {
pa_assert_se(pa_channel_map_init_auto(map, ss->channels, PA_CHANNEL_MAP_AUX)); if (!pa_channel_map_init_auto(map, ss->channels, PA_CHANNEL_MAP_ALSA)) {
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; return pcm_handle;

View file

@ -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)) { switch ((pa_sink_state_t) PA_PTR_TO_UINT(data)) {
case PA_SINK_SUSPENDED: 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) if (suspend(u) < 0)
return -1; 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) { static void sink_update_requested_latency_cb(pa_sink *s) {
struct userdata *u = s->userdata; struct userdata *u = s->userdata;
snd_pcm_sframes_t before;
pa_assert(u); pa_assert(u);
before = u->hwbuf_unused_frames;
update_sw_params(u); 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) { 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; size_t rewind_nbytes, unused_nbytes, limit_nbytes;
pa_assert(u); pa_assert(u);
/* Figure out how much we shall rewind and reset the counter */
rewind_nbytes = u->sink->thread_info.rewind_nbytes; rewind_nbytes = u->sink->thread_info.rewind_nbytes;
u->sink->thread_info.rewind_nbytes = 0; u->sink->thread_info.rewind_nbytes = 0;
@ -917,7 +929,7 @@ static void thread_func(void *userdata) {
/* pa_log_debug("loop"); */ /* pa_log_debug("loop"); */
/* Render some data and write it to the dsp */ /* 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; int work_done = 0;
if (u->sink->thread_info.rewind_nbytes > 0) if (u->sink->thread_info.rewind_nbytes > 0)
@ -982,7 +994,7 @@ static void thread_func(void *userdata) {
goto finish; goto finish;
/* Tell ALSA about this and process its response */ /* 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; struct pollfd *pollfd;
unsigned short revents = 0; unsigned short revents = 0;
int err; int err;

View file

@ -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)) { switch ((pa_source_state_t) PA_PTR_TO_UINT(data)) {
case PA_SOURCE_SUSPENDED: 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) if (suspend(u) < 0)
return -1; return -1;
@ -819,7 +819,7 @@ static void thread_func(void *userdata) {
pa_log_debug("loop"); pa_log_debug("loop");
/* Read some data and pass it to the sources */ /* 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; int work_done = 0;
if (u->use_mmap) if (u->use_mmap)
@ -867,7 +867,7 @@ static void thread_func(void *userdata) {
goto finish; goto finish;
/* Tell ALSA about this and process its response */ /* 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; struct pollfd *pollfd;
unsigned short revents = 0; unsigned short revents = 0;
int err; int err;

View file

@ -162,13 +162,13 @@ static void adjust_rates(struct userdata *u) {
if (!u->master) if (!u->master)
return; return;
if (!PA_SINK_OPENED(pa_sink_get_state(u->sink))) if (!PA_SINK_IS_OPENED(pa_sink_get_state(u->sink)))
return; return;
for (o = pa_idxset_first(u->outputs, &idx); o; o = pa_idxset_next(u->outputs, &idx)) { for (o = pa_idxset_first(u->outputs, &idx); o; o = pa_idxset_next(u->outputs, &idx)) {
pa_usec_t sink_latency; 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; continue;
sink_latency = pa_sink_get_latency(o->sink); 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)) { for (o = pa_idxset_first(u->outputs, &idx); o; o = pa_idxset_next(u->outputs, &idx)) {
uint32_t r = base_rate; 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; continue;
if (o->total_latency < target_latency) if (o->total_latency < target_latency)
@ -258,7 +258,10 @@ static void thread_func(void *userdata) {
pa_rtclock_get(&now); pa_rtclock_get(&now);
if (!u->thread_info.in_null_mode || pa_timeval_cmp(&u->thread_info.timestamp, &now) <= 0) { 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) if (!u->thread_info.in_null_mode)
u->thread_info.timestamp = now; 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: 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); pa_memblockq_push_align(o->memblockq, chunk);
else else
pa_memblockq_flush(o->memblockq); pa_memblockq_flush(o->memblockq);
@ -471,7 +474,7 @@ static void enable_output(struct output *o) {
pa_sink_input_put(o->sink_input); 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); 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); 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); enable_output(o);
} }
@ -525,7 +528,7 @@ static int sink_set_state(pa_sink *sink, pa_sink_state_t state) {
switch (state) { switch (state) {
case PA_SINK_SUSPENDED: 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); suspend(u);
break; break;
@ -697,7 +700,7 @@ static void pick_master(struct userdata *u, struct output *except) {
if (u->master && if (u->master &&
u->master != except && u->master != except &&
u->master->sink_input && 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); update_master(u, u->master);
return; 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)) for (o = pa_idxset_first(u->outputs, &idx); o; o = pa_idxset_next(u->outputs, &idx))
if (o != except && if (o != except &&
o->sink_input && 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); update_master(u, o);
return; 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); 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); pa_asyncmsgq_send(u->sink->asyncmsgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_ADD_OUTPUT, o, 0, NULL);
else { else {
/* If the sink is not yet started, we need to do the activation ourselves */ /* 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); 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); 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) if (output_create_sink_input(o) < 0)
goto fail; 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); 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); enable_output(o);
pick_master(u, NULL); pick_master(u, NULL);
} }

View file

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

View file

@ -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) { int pa__init(pa_module*m) {
pa_modargs *ma = NULL; pa_modargs *ma = NULL;
struct userdata *u; struct userdata *u;
char *fname, *state_dir; char *fname, *runtime_dir;
char hn[256]; char hn[256];
pa_sink *sink; pa_sink *sink;
pa_source *source; pa_source *source;
@ -290,11 +290,11 @@ int pa__init(pa_module*m) {
if (!pa_get_host_name(hn, sizeof(hn))) if (!pa_get_host_name(hn, sizeof(hn)))
goto fail; goto fail;
if (!(state_dir = pa_get_state_dir())) if (!(runtime_dir = pa_get_runtime_dir()))
goto fail; goto fail;
fname = pa_sprintf_malloc("%s/device-volumes.%s.gdbm", state_dir, hn); fname = pa_sprintf_malloc("%s/device-volumes.%s.gdbm", runtime_dir, hn);
pa_xfree(state_dir); pa_xfree(runtime_dir);
if (!(u->gdbm_file = gdbm_open(fname, 0, GDBM_WRCREAT, 0600, NULL))) { if (!(u->gdbm_file = gdbm_open(fname, 0, GDBM_WRCREAT, 0600, NULL))) {
pa_log("Failed to open volume database '%s': %s", fname, gdbm_strerror(gdbm_errno)); pa_log("Failed to open volume database '%s': %s", fname, gdbm_strerror(gdbm_errno));
@ -316,6 +316,7 @@ int pa__init(pa_module*m) {
fail: fail:
pa__done(m); pa__done(m);
if (ma) if (ma)
pa_modargs_free(ma); pa_modargs_free(ma);

View file

@ -143,7 +143,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
switch ((pa_sink_state_t) PA_PTR_TO_UINT(data)) { switch ((pa_sink_state_t) PA_PTR_TO_UINT(data)) {
case PA_SINK_SUSPENDED: 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()); pa_smoother_pause(u->smoother, pa_rtclock_usec());
break; break;
@ -211,7 +211,7 @@ static void thread_func(void *userdata) {
pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL); pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
/* Render some data and write it to the fifo */ /* 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; pa_usec_t usec;
int64_t n; int64_t n;
@ -294,7 +294,7 @@ static void thread_func(void *userdata) {
} }
/* Hmm, nothing to do. Let's sleep */ /* 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) if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0)

View file

@ -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: { case PA_SINK_MESSAGE_GET_LATENCY: {
pa_usec_t usec = 0; 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) if (PA_MSGOBJECT(u->master)->process_msg(PA_MSGOBJECT(u->master), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0)
usec = 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; 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_sink_assert_ref(s);
pa_assert_se(u = s->userdata); 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); pa_sink_input_cork(u->sink_input, state == PA_SINK_SUSPENDED);
return 0; return 0;
@ -134,7 +141,7 @@ static void sink_request_rewind(pa_sink *s) {
pa_assert_se(u = s->userdata); pa_assert_se(u = s->userdata);
/* Just hand this one over to the master sink */ /* 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 */ /* 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); pa_assert_se(u = s->userdata);
/* Just hand this one over to the master sink */ /* 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_input_set_requested_latency_within_thread(
pa_sink_invalidate_requested_latency(u->master); u->sink_input,
} pa_sink_get_requested_latency_within_thread(s));
/* 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);
} }
/* Called from I/O thread context */ /* 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); dst = (float*) pa_memblock_acquire(chunk->memblock);
for (c = 0; c < u->channels; c++) { for (c = 0; c < u->channels; c++) {
unsigned j; pa_sample_clamp(PA_SAMPLE_FLOAT32NE, u->input, sizeof(float), src+c, u->channels*sizeof(float), n);
float *p, *q;
p = src + c;
q = u->input;
for (j = 0; j < n; j++, p += u->channels, q++)
*q = PA_CLAMP_UNLIKELY(*p, -1.0, 1.0);
u->descriptor->run(u->handle[c], n); u->descriptor->run(u->handle[c], n);
pa_sample_clamp(PA_SAMPLE_FLOAT32NE, dst+c, u->channels*sizeof(float), u->output, sizeof(float), n);
q = u->output;
p = dst + c;
for (j = 0; j < n; j++, q++, p += u->channels)
*p = PA_CLAMP_UNLIKELY(*q, -1.0, 1.0);
} }
pa_memblock_release(tchunk.memblock); pa_memblock_release(tchunk.memblock);
@ -245,6 +226,9 @@ static void sink_input_detach_cb(pa_sink_input *i) {
pa_assert_se(u = i->userdata); pa_assert_se(u = i->userdata);
pa_sink_detach_within_thread(u->sink); 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 */ /* Called from I/O thread context */
@ -648,7 +632,6 @@ int pa__init(pa_module*m) {
if (!u->sink_input) if (!u->sink_input)
goto fail; goto fail;
u->sink_input->parent.process_msg = sink_input_process_msg;
u->sink_input->pop = sink_input_pop_cb; u->sink_input->pop = sink_input_pop_cb;
u->sink_input->process_rewind = sink_input_process_rewind_cb; u->sink_input->process_rewind = sink_input_process_rewind_cb;
u->sink_input->update_max_rewind = sink_input_update_max_rewind_cb; u->sink_input->update_max_rewind = sink_input_update_max_rewind_cb;

View file

@ -82,12 +82,14 @@ static int load_rules(struct userdata *u, const char *filename) {
pa_assert(u); pa_assert(u);
f = filename ? if (filename)
fopen(fn = pa_xstrdup(filename), "r") : f = fopen(fn = pa_xstrdup(filename), "r");
pa_open_config_file(DEFAULT_MATCH_TABLE_FILE, DEFAULT_MATCH_TABLE_FILE_USER, NULL, &fn, "r"); else
f = pa_open_config_file(DEFAULT_MATCH_TABLE_FILE, DEFAULT_MATCH_TABLE_FILE_USER, NULL, &fn);
if (!f) { 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; goto finish;
} }

View file

@ -3,7 +3,7 @@
/*** /***
This file is part of PulseAudio. 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 PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published 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>"); "description=<description for the sink>");
#define DEFAULT_SINK_NAME "null" #define DEFAULT_SINK_NAME "null"
#define MAX_LATENCY_USEC (PA_USEC_PER_SEC * 2)
struct userdata { struct userdata {
pa_core *core; pa_core *core;
@ -76,7 +77,8 @@ struct userdata {
size_t block_size; size_t block_size;
struct timeval timestamp; pa_usec_t block_usec;
pa_usec_t timestamp;
}; };
static const char* const valid_modargs[] = { 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: case PA_SINK_MESSAGE_SET_STATE:
if (PA_PTR_TO_UINT(data) == PA_SINK_RUNNING) if (PA_PTR_TO_UINT(data) == PA_SINK_RUNNING)
pa_rtclock_get(&u->timestamp); u->timestamp = pa_rtclock_usec();
break; break;
case PA_SINK_MESSAGE_GET_LATENCY: { 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) return 0;
*((pa_usec_t*) data) = 0;
else
*((pa_usec_t*) data) = pa_timeval_diff(&u->timestamp, &now);
break;
} }
} }
return pa_sink_process_msg(o, code, data, offset, chunk); 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) { static void thread_func(void *userdata) {
struct userdata *u = userdata; struct userdata *u = userdata;
@ -126,23 +195,24 @@ static void thread_func(void *userdata) {
pa_thread_mq_install(&u->thread_mq); pa_thread_mq_install(&u->thread_mq);
pa_rtpoll_install(u->rtpoll); pa_rtpoll_install(u->rtpoll);
pa_rtclock_get(&u->timestamp); u->timestamp = pa_rtclock_usec();
for (;;) { for (;;) {
int ret; int ret;
/* Render some data and drop it immediately */ /* Render some data and drop it immediately */
if (u->sink->thread_info.state == PA_SINK_RUNNING) { 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) { if (u->sink->thread_info.rewind_nbytes > 0)
pa_sink_skip(u->sink, u->block_size); process_rewind(u, now);
pa_timeval_add(&u->timestamp, pa_bytes_to_usec(u->block_size, &u->sink->sample_spec));
}
pa_rtpoll_set_timer_absolute(u->rtpoll, &u->timestamp); if (u->timestamp <= now)
process_render(u, now);
pa_rtpoll_set_timer_absolute(u->rtpoll, u->timestamp);
} else } else
pa_rtpoll_set_timer_disabled(u->rtpoll); 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_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_sample_spec(&data, &ss);
pa_sink_new_data_set_channel_map(&data, &map); 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); pa_sink_new_data_done(&data);
if (!u->sink) { if (!u->sink) {
pa_log("Failed to create sink."); pa_log("Failed to create sink object.");
goto fail; goto fail;
} }
u->sink->parent.process_msg = sink_process_msg; u->sink->parent.process_msg = sink_process_msg;
u->sink->update_requested_latency = sink_update_requested_latency_cb;
u->sink->userdata = u; u->sink->userdata = u;
u->sink->flags = PA_SINK_LATENCY;
pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq); pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
pa_sink_set_rtpoll(u->sink, u->rtpoll); pa_sink_set_rtpoll(u->sink, u->rtpoll);
u->block_size = pa_bytes_per_second(&ss) / 20; /* 50 ms */ u->block_usec = u->sink->max_latency = MAX_LATENCY_USEC;
if (u->block_size <= 0)
u->block_size = pa_frame_size(&ss); 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))) { if (!(u->thread = pa_thread_new(thread_func, u))) {
pa_log("Failed to create thread."); pa_log("Failed to create thread.");

View file

@ -161,10 +161,10 @@ static void trigger(struct userdata *u, pa_bool_t quick) {
pa_log_debug("trigger"); 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; 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; enable_bits |= PCM_ENABLE_OUTPUT;
pa_log_debug("trigger: %i", enable_bits); 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. * 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); uint8_t *buf = pa_xnew(uint8_t, u->in_fragment_size);
pa_read(u->fd, buf, u->in_fragment_size, NULL); pa_read(u->fd, buf, u->in_fragment_size, NULL);
pa_xfree(buf); 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)) { switch ((pa_sink_state_t) PA_PTR_TO_UINT(data)) {
case PA_SINK_SUSPENDED: 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 (!u->source || u->source_suspended) {
if (suspend(u) < 0) 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) { if (u->sink->thread_info.state == PA_SINK_INIT) {
do_trigger = TRUE; 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) { 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)) { switch ((pa_source_state_t) PA_PTR_TO_UINT(data)) {
case PA_SOURCE_SUSPENDED: 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 (!u->sink || u->sink_suspended) {
if (suspend(u) < 0) 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) { if (u->source->thread_info.state == PA_SOURCE_INIT) {
do_trigger = TRUE; 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) { 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 */ /* 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) { 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. */ /* 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) { 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 = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
pollfd->events = pollfd->events =
((u->source && PA_SOURCE_OPENED(u->source->thread_info.state)) ? POLLIN : 0) | ((u->source && PA_SOURCE_IS_OPENED(u->source->thread_info.state)) ? POLLIN : 0) |
((u->sink && PA_SINK_OPENED(u->sink->thread_info.state)) ? POLLOUT : 0); ((u->sink && PA_SINK_IS_OPENED(u->sink->thread_info.state)) ? POLLOUT : 0);
} }
/* Hmm, nothing to do. Let's sleep */ /* Hmm, nothing to do. Let's sleep */

View file

@ -215,15 +215,6 @@ int pa__init(pa_module*m) {
#else #else
pa_socket_server *s; pa_socket_server *s;
int r; 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 #endif
pa_assert(m); pa_assert(m);
@ -255,27 +246,28 @@ int pa__init(pa_module*m) {
goto fail; goto fail;
if (s_ipv4) if (s_ipv4)
if (!(u->protocol_ipv4 = protocol_new(m->core, s_ipv4, m, ma))) u->protocol_ipv4 = protocol_new(m->core, s_ipv4, m, ma);
pa_socket_server_unref(s_ipv4);
if (s_ipv6) if (s_ipv6)
if (!(u->protocol_ipv6 = protocol_new(m->core, s_ipv6, m, ma))) u->protocol_ipv6 = protocol_new(m->core, s_ipv6, m, ma);
pa_socket_server_unref(s_ipv6);
if (!u->protocol_ipv4 && !u->protocol_ipv6) if (!u->protocol_ipv4 && !u->protocol_ipv6)
goto fail; goto fail;
if (s_ipv6)
pa_socket_server_unref(s_ipv6);
if (s_ipv6)
pa_socket_server_unref(s_ipv4);
#else #else
#if defined(USE_PROTOCOL_ESOUND) #if defined(USE_PROTOCOL_ESOUND)
#if defined(USE_PER_USER_ESOUND_SOCKET) #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 #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 /* This socket doesn't reside in our own runtime dir but in
* /tmp/.esd/, hence we have to create the dir first */ * /tmp/.esd/, hence we have to create the dir first */
@ -285,24 +277,26 @@ int pa__init(pa_module*m) {
} }
#else #else
pa_runtime_path(pa_modargs_get_value(ma, "socket", UNIX_SOCKET), tmp, sizeof(tmp)); if (!(u->socket_path = pa_runtime_path(pa_modargs_get_value(ma, "socket", UNIX_SOCKET)))) {
u->socket_path = pa_xstrdup(tmp); pa_log("Failed to generate socket path.");
#endif
if ((r = pa_unix_socket_remove_stale(tmp)) < 0) {
pa_log("Failed to remove stale UNIX socket '%s': %s", tmp, pa_cstrerror(errno));
goto fail; goto fail;
} }
#endif
if (r) if ((r = pa_unix_socket_remove_stale(u->socket_path)) < 0) {
pa_log("Removed stale UNIX socket '%s'.", tmp); 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; goto fail;
if (!(u->protocol_unix = protocol_new(m->core, s, m, ma))) if (!(u->protocol_unix = protocol_new(m->core, s, m, ma)))
goto fail; goto fail;
pa_socket_server_unref(s);
#endif #endif
m->userdata = u; m->userdata = u;
@ -325,24 +319,22 @@ fail:
#else #else
if (u->protocol_unix) if (u->protocol_unix)
protocol_free(u->protocol_unix); protocol_free(u->protocol_unix);
pa_xfree(u->socket_path);
if (u->socket_path)
pa_xfree(u->socket_path);
#endif #endif
pa_xfree(u); pa_xfree(u);
} else {
#if defined(USE_TCP_SOCKETS)
if (s_ipv4)
pa_socket_server_unref(s_ipv4);
if (s_ipv6)
pa_socket_server_unref(s_ipv6);
#else
if (s)
pa_socket_server_unref(s);
#endif
} }
#if defined(USE_TCP_SOCKETS)
if (s_ipv4)
pa_socket_server_unref(s_ipv4);
if (s_ipv6)
pa_socket_server_unref(s_ipv6);
#else
if (s)
pa_socket_server_unref(s);
#endif
goto finish; goto finish;
} }
@ -362,7 +354,7 @@ void pa__done(pa_module*m) {
if (u->protocol_unix) if (u->protocol_unix)
protocol_free(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) { if (u->socket_path) {
char *p = pa_parent_dir(u->socket_path); char *p = pa_parent_dir(u->socket_path);
rmdir(p); rmdir(p);

View file

@ -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: { case PA_SINK_MESSAGE_GET_LATENCY: {
pa_usec_t usec = 0; 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) if (PA_MSGOBJECT(u->master)->process_msg(PA_MSGOBJECT(u->master), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0)
usec = 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; 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_sink_assert_ref(s);
pa_assert_se(u = s->userdata); 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); pa_sink_input_cork(u->sink_input, state == PA_SINK_SUSPENDED);
return 0; return 0;
@ -112,7 +119,7 @@ static void sink_request_rewind(pa_sink *s) {
pa_sink_assert_ref(s); pa_sink_assert_ref(s);
pa_assert_se(u = s->userdata); 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 */ /* 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); pa_assert_se(u = s->userdata);
/* Just hand this one over to the master sink */ /* 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_input_set_requested_latency_within_thread(
pa_sink_invalidate_requested_latency(u->master); u->sink_input,
} pa_sink_get_requested_latency_within_thread(s));
/* 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);
} }
/* Called from I/O thread context */ /* 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_assert_se(u = i->userdata);
pa_sink_render(u->sink, nbytes, chunk); pa_sink_render(u->sink, nbytes, chunk);
return 0; return 0;
} }
@ -185,6 +176,9 @@ static void sink_input_detach_cb(pa_sink_input *i) {
pa_assert_se(u = i->userdata); pa_assert_se(u = i->userdata);
pa_sink_detach_within_thread(u->sink); 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 */ /* Called from I/O thread context */
@ -317,7 +311,6 @@ int pa__init(pa_module*m) {
if (!u->sink_input) if (!u->sink_input)
goto fail; goto fail;
u->sink_input->parent.process_msg = sink_input_process_msg;
u->sink_input->pop = sink_input_pop_cb; u->sink_input->pop = sink_input_pop_cb;
u->sink_input->process_rewind = sink_input_process_rewind_cb; u->sink_input->process_rewind = sink_input_process_rewind_cb;
u->sink_input->update_max_rewind = sink_input_update_max_rewind_cb; u->sink_input->update_max_rewind = sink_input_update_max_rewind_cb;

View file

@ -317,7 +317,7 @@ static pa_hook_result_t device_state_changed_hook_cb(pa_core *c, pa_object *o, s
if (pa_sink_used_by(s) <= 0) { if (pa_sink_used_by(s) <= 0) {
if (PA_SINK_OPENED(state)) if (PA_SINK_IS_OPENED(state))
restart(d); 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_used_by(s) <= 0) {
if (PA_SOURCE_OPENED(state)) if (PA_SOURCE_IS_OPENED(state))
restart(d); restart(d);
} }
} }

View file

@ -303,7 +303,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
/* First, change the state, because otherwide pa_sink_render() would fail */ /* First, change the state, because otherwide pa_sink_render() would fail */
if ((r = pa_sink_process_msg(o, code, data, offset, chunk)) >= 0) 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); send_data(u);
return r; 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); pa_assert(offset > 0);
u->requested_bytes += (size_t) offset; 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); send_data(u);
return 0; 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) { switch ((pa_sink_state_t) state) {
case PA_SINK_SUSPENDED: case PA_SINK_SUSPENDED:
pa_assert(PA_SINK_OPENED(s->state)); pa_assert(PA_SINK_IS_OPENED(s->state));
stream_cork(u, TRUE); stream_cork(u, TRUE);
break; break;
@ -369,7 +369,7 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off
switch (code) { switch (code) {
case SOURCE_MESSAGE_POST: 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); pa_source_post(u->source, chunk);
return 0; 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) { switch ((pa_source_state_t) state) {
case PA_SOURCE_SUSPENDED: case PA_SOURCE_SUSPENDED:
pa_assert(PA_SOURCE_OPENED(s->state)); pa_assert(PA_SOURCE_IS_OPENED(s->state));
stream_cork(u, TRUE); stream_cork(u, TRUE);
break; 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_putu32(reply, PA_INVALID_INDEX);
pa_tagstruct_puts(reply, u->sink_name); pa_tagstruct_puts(reply, u->sink_name);
pa_tagstruct_putu32(reply, u->maxlength); 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->tlength);
pa_tagstruct_putu32(reply, u->prebuf); pa_tagstruct_putu32(reply, u->prebuf);
pa_tagstruct_putu32(reply, u->minreq); 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_putu32(reply, PA_INVALID_INDEX);
pa_tagstruct_puts(reply, u->source_name); pa_tagstruct_puts(reply, u->source_name);
pa_tagstruct_putu32(reply, u->maxlength); 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); pa_tagstruct_putu32(reply, u->fragsize);
#endif #endif

View file

@ -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 buf_name[256], buf_volume[256], buf_sink[256], buf_source[256];
char *ln = buf_name; char *ln = buf_name;
f = u->table_file ? if (!(f = fopen(u->table_file, "r"))) {
fopen(u->table_file, "r") :
pa_open_config_file(NULL, DEFAULT_VOLUME_TABLE_FILE, NULL, &u->table_file, "r");
if (!f) {
if (errno == ENOENT) { if (errno == ENOENT) {
pa_log_info("starting with empty ruleset."); pa_log_info("Starting with empty ruleset.");
ret = 0; ret = 0;
} else } 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; goto finish;
} }
@ -236,11 +232,7 @@ static int save_rules(struct userdata *u) {
pa_log_info("Saving rules..."); pa_log_info("Saving rules...");
f = u->table_file ? if (!(f = fopen(u->table_file, "w"))) {
fopen(u->table_file, "w") :
pa_open_config_file(NULL, DEFAULT_VOLUME_TABLE_FILE, NULL, &u->table_file, "w");
if (!f) {
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; goto finish;
} }
@ -496,7 +488,7 @@ int pa__init(pa_module*m) {
u = pa_xnew(struct userdata, 1); u = pa_xnew(struct userdata, 1);
u->core = m->core; u->core = m->core;
u->hashmap = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); 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->modified = FALSE;
u->subscription = NULL; u->subscription = NULL;
u->sink_input_new_hook_slot = u->sink_input_fixate_hook_slot = u->source_output_new_hook_slot = NULL; u->sink_input_new_hook_slot = u->sink_input_fixate_hook_slot = u->source_output_new_hook_slot = NULL;

View file

@ -112,13 +112,20 @@ int pa_client_conf_load(pa_client_conf *c, const char *filename) {
table[6].data = &c->cookie_file; table[6].data = &c->cookie_file;
table[7].data = &c->disable_shm; table[7].data = &c->disable_shm;
f = filename ? if (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 (!f && errno != EINTR) { if (!(f = fopen(filename, "r"))) {
pa_log_warn("Failed to open configuration file '%s': %s", fn, pa_cstrerror(errno)); pa_log("Failed to open configuration file '%s': %s", fn, pa_cstrerror(errno));
goto finish; 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; 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) if (!r)
r = pa_client_conf_load_cookie(c); r = pa_client_conf_load_cookie(c);
finish: finish:
pa_xfree(fn); pa_xfree(fn);

View file

@ -3,7 +3,7 @@
/*** /***
This file is part of PulseAudio. 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 Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
PulseAudio is free software; you can redistribute it and/or modify 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_RECORD_STREAM_MOVED] = pa_command_stream_moved,
[PA_COMMAND_PLAYBACK_STREAM_SUSPENDED] = pa_command_stream_suspended, [PA_COMMAND_PLAYBACK_STREAM_SUSPENDED] = pa_command_stream_suspended,
[PA_COMMAND_RECORD_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 [PA_COMMAND_SUBSCRIBE_EVENT] = pa_command_subscribe_event
}; };
@ -100,10 +101,12 @@ static void unlock_autospawn_lock_file(pa_context *c) {
pa_assert(c); pa_assert(c);
if (c->autospawn_lock_fd >= 0) { if (c->autospawn_lock_fd >= 0) {
char lf[PATH_MAX]; char *lf;
pa_runtime_path(AUTOSPAWN_LOCK, lf, sizeof(lf));
lf = pa_runtime_path(AUTOSPAWN_LOCK);
pa_unlock_lockfile(lf, c->autospawn_lock_fd); pa_unlock_lockfile(lf, c->autospawn_lock_fd);
pa_xfree(lf);
c->autospawn_lock_fd = -1; 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); 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 *pa_context_new_with_proplist(pa_mainloop_api *mainloop, const char *name, pa_proplist *p) {
pa_context *c; pa_context *c;
@ -146,18 +159,14 @@ pa_context *pa_context_new_with_proplist(pa_mainloop_api *mainloop, const char *
c->ctag = 0; c->ctag = 0;
c->csyncid = 0; c->csyncid = 0;
c->state_callback = NULL; reset_callbacks(c);
c->state_userdata = NULL;
c->subscribe_callback = NULL; c->is_local = FALSE;
c->subscribe_userdata = NULL;
c->is_local = -1;
c->server_list = NULL; c->server_list = NULL;
c->server = NULL; c->server = NULL;
c->autospawn_lock_fd = -1; c->autospawn_lock_fd = -1;
memset(&c->spawn_api, 0, sizeof(c->spawn_api)); memset(&c->spawn_api, 0, sizeof(c->spawn_api));
c->do_autospawn = 0; c->do_autospawn = FALSE;
#ifndef MSG_NOSIGNAL #ifndef MSG_NOSIGNAL
#ifdef SIGPIPE #ifdef SIGPIPE
@ -186,26 +195,48 @@ pa_context *pa_context_new_with_proplist(pa_mainloop_api *mainloop, const char *
return c; return c;
} }
static void context_free(pa_context *c) { static void context_unlink(pa_context *c) {
pa_stream *s;
pa_assert(c); 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) while (c->operations)
pa_operation_cancel(c->operations); pa_operation_cancel(c->operations);
while (c->streams) if (c->pdispatch) {
pa_stream_set_state(c->streams, PA_STREAM_TERMINATED);
if (c->client)
pa_socket_client_unref(c->client);
if (c->pdispatch)
pa_pdispatch_unref(c->pdispatch); pa_pdispatch_unref(c->pdispatch);
c->pdispatch = NULL;
}
if (c->pstream) { if (c->pstream) {
pa_pstream_unlink(c->pstream); pa_pstream_unlink(c->pstream);
pa_pstream_unref(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) if (c->record_streams)
pa_dynarray_free(c->record_streams, NULL, NULL); pa_dynarray_free(c->record_streams, NULL, NULL);
if (c->playback_streams) 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); pa_context_ref(c);
c->state = st; c->state = st;
if (c->state_callback) if (c->state_callback)
c->state_callback(c, c->state_userdata); c->state_callback(c, c->state_userdata);
if (st == PA_CONTEXT_FAILED || st == PA_CONTEXT_TERMINATED) { if (st == PA_CONTEXT_FAILED || st == PA_CONTEXT_TERMINATED)
pa_stream *s; context_unlink(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, 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;
}
pa_context_unref(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) { int pa_context_set_error(pa_context *c, int error) {
pa_assert(error >= 0); pa_assert(error >= 0);
pa_assert(error < PA_ERR_MAX); pa_assert(error < PA_ERR_MAX);
@ -302,6 +303,14 @@ int pa_context_set_error(pa_context *c, int error) {
return 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) { static void pstream_die_callback(pa_pstream *p, void *userdata) {
pa_context *c = 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); 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(c);
pa_assert(PA_REFCNT_VALUE(c) >= 1); pa_assert(PA_REFCNT_VALUE(c) >= 1);
if (command == PA_COMMAND_ERROR) { if (command == PA_COMMAND_ERROR) {
pa_assert(t); 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); pa_context_fail(c, PA_ERR_PROTOCOL);
return -1; return -1;
} }
} else if (command == PA_COMMAND_TIMEOUT) } else if (command == PA_COMMAND_TIMEOUT)
c->error = PA_ERR_TIMEOUT; err = PA_ERR_TIMEOUT;
else { else {
pa_context_fail(c, PA_ERR_PROTOCOL); pa_context_fail(c, PA_ERR_PROTOCOL);
return -1; 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; return 0;
} }
@ -390,11 +415,7 @@ static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t
pa_context_ref(c); pa_context_ref(c);
if (command != PA_COMMAND_REPLY) { if (command != PA_COMMAND_REPLY) {
pa_context_handle_error(c, command, t, TRUE);
if (pa_context_handle_error(c, command, t) < 0)
pa_context_fail(c, PA_ERR_PROTOCOL);
pa_context_fail(c, c->error);
goto finish; 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 */ /* Enable shared memory support if possible */
if (c->version >= 10 && if (c->version >= 10 &&
pa_mempool_is_shared(c->mempool) && pa_mempool_is_shared(c->mempool) &&
c->is_local > 0) { c->is_local) {
/* Only enable SHM if both sides are owned by the same /* Only enable SHM if both sides are owned by the same
* user. This is a security measure because otherwise * 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); c->pdispatch = pa_pdispatch_new(c->mainloop, command_table, PA_COMMAND_MAX);
if (!c->conf->cookie_valid) 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); t = pa_tagstruct_command(c, PA_COMMAND_AUTH, &tag);
pa_tagstruct_putu32(t, PA_PROTOCOL_VERSION); pa_tagstruct_putu32(t, PA_PROTOCOL_VERSION);
@ -525,10 +546,13 @@ static int context_connect_spawn(pa_context *c) {
int fds[2] = { -1, -1} ; int fds[2] = { -1, -1} ;
pa_iochannel *io; pa_iochannel *io;
if (getuid() == 0)
return -1;
pa_context_ref(c); pa_context_ref(c);
if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) < 0) { 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); pa_context_fail(c, PA_ERR_INTERNAL);
goto fail; goto fail;
} }
@ -542,7 +566,7 @@ static int context_connect_spawn(pa_context *c) {
c->spawn_api.prefork(); c->spawn_api.prefork();
if ((pid = fork()) < 0) { 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); pa_context_fail(c, PA_ERR_INTERNAL);
if (c->spawn_api.postfork) if (c->spawn_api.postfork)
@ -557,9 +581,13 @@ static int context_connect_spawn(pa_context *c) {
#define MAX_ARGS 64 #define MAX_ARGS 64
const char * argv[MAX_ARGS+1]; const char * argv[MAX_ARGS+1];
int n; int n;
char *f;
/* Not required, since fds[0] has CLOEXEC enabled anyway */ pa_close_all(fds[1], -1);
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) if (c->spawn_api.atfork)
c->spawn_api.atfork(); c->spawn_api.atfork();
@ -592,6 +620,8 @@ static int context_connect_spawn(pa_context *c) {
/* Parent */ /* Parent */
pa_assert_se(pa_close(fds[1]) == 0);
r = waitpid(pid, &status, 0); r = waitpid(pid, &status, 0);
if (c->spawn_api.postfork) if (c->spawn_api.postfork)
@ -606,14 +636,12 @@ static int context_connect_spawn(pa_context *c) {
goto fail; 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]); io = pa_iochannel_new(c->mainloop, fds[0], fds[0]);
setup_context(c, io); setup_context(c, io);
unlock_autospawn_lock_file(c);
pa_context_unref(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))) if (!(c->client = pa_socket_client_new_string(c->mainloop, u, PA_NATIVE_DEFAULT_PORT)))
continue; 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); pa_socket_client_set_callback(c->client, on_connection, c);
break; break;
} }
@ -680,6 +708,7 @@ finish:
static void on_connection(pa_socket_client *client, pa_iochannel*io, void *userdata) { static void on_connection(pa_socket_client *client, pa_iochannel*io, void *userdata) {
pa_context *c = userdata; pa_context *c = userdata;
int saved_errno = errno;
pa_assert(client); pa_assert(client);
pa_assert(c); pa_assert(c);
@ -692,7 +721,9 @@ static void on_connection(pa_socket_client *client, pa_iochannel*io, void *userd
if (!io) { if (!io) {
/* Try the item in the list */ /* 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); try_next_connection(c);
goto finish; goto finish;
} }
@ -708,6 +739,25 @@ finish:
pa_context_unref(c); 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( int pa_context_connect(
pa_context *c, pa_context *c,
const char *server, const char *server,
@ -736,8 +786,8 @@ int pa_context_connect(
goto finish; goto finish;
} }
} else { } else {
char *d; char *d, *ufn;
char ufn[PATH_MAX]; static char *legacy_dir;
/* Prepend in reverse order */ /* Prepend in reverse order */
@ -757,25 +807,34 @@ int pa_context_connect(
c->server_list = pa_strlist_prepend(c->server_list, "tcp4:localhost"); c->server_list = pa_strlist_prepend(c->server_list, "tcp4:localhost");
/* The system wide instance */ /* 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 */ /* 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 */ /* Wrap the connection attempts in a single transaction for sane autospawn locking */
if (!(flags & PA_CONTEXT_NOAUTOSPAWN) && c->conf->autospawn) { if (!(flags & PA_CONTEXT_NOAUTOSPAWN) && c->conf->autospawn) {
char lf[PATH_MAX]; char *lf;
pa_runtime_path(AUTOSPAWN_LOCK, lf, sizeof(lf)); lf = pa_runtime_path(AUTOSPAWN_LOCK);
pa_make_secure_parent_dir(lf, 0700, (uid_t)-1, (gid_t)-1);
pa_assert(c->autospawn_lock_fd <= 0); pa_assert(c->autospawn_lock_fd <= 0);
c->autospawn_lock_fd = pa_lock_lockfile(lf); c->autospawn_lock_fd = pa_lock_lockfile(lf);
pa_xfree(lf);
if (api) if (api)
c->spawn_api = *api; c->spawn_api = *api;
c->do_autospawn = 1;
}
c->do_autospawn = TRUE;
}
} }
pa_context_set_state(c, PA_CONTEXT_CONNECTING); pa_context_set_state(c, PA_CONTEXT_CONNECTING);
@ -791,7 +850,8 @@ void pa_context_disconnect(pa_context *c) {
pa_assert(c); pa_assert(c);
pa_assert(PA_REFCNT_VALUE(c) >= 1); 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) { 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(c);
pa_assert(PA_REFCNT_VALUE(c) >= 1); 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_callback = cb;
c->state_userdata = userdata; c->state_userdata = userdata;
} }
@ -820,11 +883,7 @@ int pa_context_is_pending(pa_context *c) {
pa_assert(c); pa_assert(c);
pa_assert(PA_REFCNT_VALUE(c) >= 1); pa_assert(PA_REFCNT_VALUE(c) >= 1);
PA_CHECK_VALIDITY(c, PA_CHECK_VALIDITY(c, PA_CONTEXT_IS_GOOD(c->state), PA_ERR_BADSTATE);
c->state == PA_CONTEXT_CONNECTING ||
c->state == PA_CONTEXT_AUTHORIZING ||
c->state == PA_CONTEXT_SETTING_NAME ||
c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
return (c->pstream && pa_pstream_is_pending(c->pstream)) || return (c->pstream && pa_pstream_is_pending(c->pstream)) ||
(c->pdispatch && pa_pdispatch_is_pending(c->pdispatch)) || (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; goto finish;
if (command != PA_COMMAND_REPLY) { 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; goto finish;
success = 0; success = 0;
@ -920,25 +979,6 @@ finish:
pa_operation_unref(o); pa_operation_unref(o);
} }
pa_operation* pa_context_exit_daemon(pa_context *c, pa_context_success_cb_t cb, void *userdata) {
pa_tagstruct *t;
pa_operation *o;
uint32_t tag;
pa_assert(c);
pa_assert(PA_REFCNT_VALUE(c) >= 1);
PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
t = pa_tagstruct_command(c, PA_COMMAND_EXIT, &tag);
pa_pstream_send_tagstruct(c->pstream, t);
pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
return o;
}
pa_operation* pa_context_send_simple_command(pa_context *c, uint32_t command, pa_pdispatch_cb_t internal_cb, pa_operation_cb_t cb, void *userdata) { pa_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_tagstruct *t;
pa_operation *o; pa_operation *o;
@ -958,6 +998,13 @@ pa_operation* pa_context_send_simple_command(pa_context *c, uint32_t command, pa
return o; return o;
} }
pa_operation* pa_context_exit_daemon(pa_context *c, pa_context_success_cb_t cb, void *userdata) {
pa_assert(c);
pa_assert(PA_REFCNT_VALUE(c) >= 1);
return pa_context_send_simple_command(c, PA_COMMAND_EXIT, pa_context_simple_ack_callback, (pa_operation_cb_t) cb, userdata);
}
pa_operation* pa_context_set_default_sink(pa_context *c, const char *name, pa_context_success_cb_t cb, void *userdata) { pa_operation* pa_context_set_default_sink(pa_context *c, const char *name, pa_context_success_cb_t cb, void *userdata) {
pa_tagstruct *t; pa_tagstruct *t;
pa_operation *o; pa_operation *o;
@ -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); 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, (pa_operation_cb_t) cb, userdata);
t = pa_tagstruct_command(c, PA_COMMAND_SET_DEFAULT_SINK, &tag); t = pa_tagstruct_command(c, PA_COMMAND_SET_DEFAULT_SINK, &tag);
pa_tagstruct_puts(t, name); pa_tagstruct_puts(t, name);
pa_pstream_send_tagstruct(c->pstream, t); 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); 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, (pa_operation_cb_t) cb, userdata);
t = pa_tagstruct_command(c, PA_COMMAND_SET_DEFAULT_SOURCE, &tag); t = pa_tagstruct_command(c, PA_COMMAND_SET_DEFAULT_SOURCE, &tag);
pa_tagstruct_puts(t, name); pa_tagstruct_puts(t, name);
pa_pstream_send_tagstruct(c->pstream, t); pa_pstream_send_tagstruct(c->pstream, t);
@ -1002,15 +1047,13 @@ int pa_context_is_local(pa_context *c) {
pa_assert(c); pa_assert(c);
pa_assert(PA_REFCNT_VALUE(c) >= 1); 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_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; pa_operation *o;
uint32_t tag;
pa_assert(c); pa_assert(c);
pa_assert(PA_REFCNT_VALUE(c) >= 1); 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) { if (c->version >= 13) {
pa_proplist *p = pa_proplist_new(); pa_proplist *p = pa_proplist_new();
pa_proplist_sets(p, PA_PROP_APPLICATION_NAME, name); pa_proplist_sets(p, PA_PROP_APPLICATION_NAME, name);
o = pa_context_proplist_update(c, PA_UPDATE_REPLACE, p, cb, userdata); o = pa_context_proplist_update(c, PA_UPDATE_REPLACE, p, cb, userdata);
pa_proplist_free(p); pa_proplist_free(p);
} else { } else {
pa_tagstruct *t;
uint32_t tag;
o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
t = pa_tagstruct_command(c, PA_COMMAND_SET_CLIENT_NAME, &tag); t = pa_tagstruct_command(c, PA_COMMAND_SET_CLIENT_NAME, &tag);
pa_tagstruct_puts(t, name); 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(c);
pa_assert(PA_REFCNT_VALUE(c) >= 1); 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; return c->version;
} }

View file

@ -48,6 +48,15 @@ typedef enum pa_context_state {
PA_CONTEXT_TERMINATED /**< The connection was terminated cleanly */ PA_CONTEXT_TERMINATED /**< The connection was terminated cleanly */
} pa_context_state_t; } 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 */ /** The state of a stream */
typedef enum pa_stream_state { typedef enum pa_stream_state {
PA_STREAM_UNCONNECTED, /**< The stream is not yet connected to any sink or source */ 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_TERMINATED /**< The stream has been terminated cleanly */
} pa_stream_state_t; } 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 */ /** The state of an operation */
typedef enum pa_operation_state { typedef enum pa_operation_state {
PA_OPERATION_RUNNING, /**< The operation is still running */ PA_OPERATION_RUNNING, /**< The operation is still running */
@ -296,6 +312,7 @@ enum {
PA_ERR_VERSION, /**< Incompatible protocol version */ PA_ERR_VERSION, /**< Incompatible protocol version */
PA_ERR_TOOLARGE, /**< Data too large */ PA_ERR_TOOLARGE, /**< Data too large */
PA_ERR_NOTSUPPORTED, /**< Operation not supported \since 0.9.5 */ 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 */ 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 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. */ 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 int write_index_corrupt; /**< Non-zero if write_index is not
* up-to-date because a local write * up-to-date because a local write
@ -403,6 +428,14 @@ typedef struct pa_timing_info {
* the sink. \since 0.9.11 */ * the sink. \since 0.9.11 */
pa_usec_t configured_source_usec; /**< The static configured latency for pa_usec_t configured_source_usec; /**< The static configured latency for
* the source. \since 0.9.11 */ * 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; } pa_timing_info;
/** A structure for the spawn api. This may be used to integrate auto /** A structure for the spawn api. This may be used to integrate auto

View file

@ -42,6 +42,7 @@
#include <pulsecore/memblockq.h> #include <pulsecore/memblockq.h>
#include <pulsecore/hashmap.h> #include <pulsecore/hashmap.h>
#include <pulsecore/refcnt.h> #include <pulsecore/refcnt.h>
#include <pulsecore/time-smoother.h>
#include "client-conf.h" #include "client-conf.h"
@ -69,14 +70,13 @@ struct pa_context {
pa_context_notify_cb_t state_callback; pa_context_notify_cb_t state_callback;
void *state_userdata; void *state_userdata;
pa_context_subscribe_cb_t subscribe_callback; pa_context_subscribe_cb_t subscribe_callback;
void *subscribe_userdata; void *subscribe_userdata;
pa_mempool *mempool; pa_mempool *mempool;
int is_local; pa_bool_t is_local;
int do_autospawn; pa_bool_t do_autospawn;
int autospawn_lock_fd; int autospawn_lock_fd;
pa_spawn_api spawn_api; pa_spawn_api spawn_api;
@ -89,35 +89,39 @@ struct pa_context {
uint32_t client_index; uint32_t client_index;
}; };
#define PA_MAX_WRITE_INDEX_CORRECTIONS 10 #define PA_MAX_WRITE_INDEX_CORRECTIONS 32
typedef struct pa_index_correction { typedef struct pa_index_correction {
uint32_t tag; uint32_t tag;
int valid;
int64_t value; int64_t value;
int absolute, corrupt; pa_bool_t valid:1;
pa_bool_t absolute:1;
pa_bool_t corrupt:1;
} pa_index_correction; } pa_index_correction;
struct pa_stream { struct pa_stream {
PA_REFCNT_DECLARE; PA_REFCNT_DECLARE;
pa_context *context;
pa_mainloop_api *mainloop;
PA_LLIST_FIELDS(pa_stream); PA_LLIST_FIELDS(pa_stream);
pa_proplist *proplist; pa_context *context;
pa_bool_t manual_buffer_attr; pa_mainloop_api *mainloop;
pa_buffer_attr buffer_attr;
pa_sample_spec sample_spec;
pa_channel_map channel_map;
pa_stream_flags_t flags;
uint32_t channel;
uint32_t syncid;
int channel_valid;
uint32_t stream_index;
pa_stream_direction_t direction; pa_stream_direction_t direction;
pa_stream_state_t state; pa_stream_state_t state;
pa_stream_flags_t flags;
pa_sample_spec sample_spec;
pa_channel_map channel_map;
pa_proplist *proplist;
uint32_t channel;
pa_bool_t channel_valid;
uint32_t syncid;
uint32_t stream_index;
uint32_t requested_bytes; uint32_t requested_bytes;
pa_buffer_attr buffer_attr;
uint32_t device_index; uint32_t device_index;
char *device_name; char *device_name;
@ -127,11 +131,11 @@ struct pa_stream {
void *peek_data; void *peek_data;
pa_memblockq *record_memblockq; pa_memblockq *record_memblockq;
int corked; pa_bool_t corked;
/* Store latest latency info */ /* Store latest latency info */
pa_timing_info timing_info; pa_timing_info timing_info;
int timing_info_valid; pa_bool_t timing_info_valid;
/* Use to make sure that time advances monotonically */ /* Use to make sure that time advances monotonically */
pa_usec_t previous_time; pa_usec_t previous_time;
@ -146,10 +150,9 @@ struct pa_stream {
/* Latency interpolation stuff */ /* Latency interpolation stuff */
pa_time_event *auto_timing_update_event; pa_time_event *auto_timing_update_event;
int auto_timing_update_requested; pa_bool_t auto_timing_update_requested;
pa_usec_t cached_time; pa_smoother *smoother;
int cached_time_valid;
/* Callbacks */ /* Callbacks */
pa_stream_notify_cb_t state_callback; pa_stream_notify_cb_t state_callback;
@ -168,6 +171,8 @@ struct pa_stream {
void *moved_userdata; void *moved_userdata;
pa_stream_notify_cb_t suspended_callback; pa_stream_notify_cb_t suspended_callback;
void *suspended_userdata; void *suspended_userdata;
pa_stream_notify_cb_t started_callback;
void *started_userdata;
}; };
typedef void (*pa_operation_cb_t)(void); 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_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_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_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); 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); 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); void pa_context_fail(pa_context *c, int error);
int pa_context_set_error(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); 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); 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); void pa_stream_set_state(pa_stream *s, pa_stream_state_t st);

View file

@ -52,7 +52,7 @@ static void context_stat_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNU
goto finish; goto finish;
if (command != PA_COMMAND_REPLY) { 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; goto finish;
p = NULL; p = NULL;
@ -95,7 +95,7 @@ static void context_get_server_info_callback(pa_pdispatch *pd, uint32_t command,
goto finish; goto finish;
if (command != PA_COMMAND_REPLY) { 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; goto finish;
p = NULL; p = NULL;
@ -140,7 +140,7 @@ static void context_get_sink_info_callback(pa_pdispatch *pd, uint32_t command, P
goto finish; goto finish;
if (command != PA_COMMAND_REPLY) { 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; goto finish;
eol = -1; eol = -1;
@ -261,7 +261,7 @@ static void context_get_source_info_callback(pa_pdispatch *pd, uint32_t command,
goto finish; goto finish;
if (command != PA_COMMAND_REPLY) { 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; goto finish;
eol = -1; eol = -1;
@ -382,7 +382,7 @@ static void context_get_client_info_callback(pa_pdispatch *pd, uint32_t command,
goto finish; goto finish;
if (command != PA_COMMAND_REPLY) { 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; goto finish;
eol = -1; eol = -1;
@ -464,7 +464,7 @@ static void context_get_module_info_callback(pa_pdispatch *pd, uint32_t command,
goto finish; goto finish;
if (command != PA_COMMAND_REPLY) { 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; goto finish;
eol = -1; eol = -1;
@ -543,7 +543,7 @@ static void context_get_sink_input_info_callback(pa_pdispatch *pd, uint32_t comm
goto finish; goto finish;
if (command != PA_COMMAND_REPLY) { 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; goto finish;
eol = -1; eol = -1;
@ -637,7 +637,7 @@ static void context_get_source_output_info_callback(pa_pdispatch *pd, uint32_t c
goto finish; goto finish;
if (command != PA_COMMAND_REPLY) { 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; goto finish;
eol = -1; eol = -1;
@ -967,7 +967,7 @@ static void context_get_sample_info_callback(pa_pdispatch *pd, uint32_t command,
goto finish; goto finish;
if (command != PA_COMMAND_REPLY) { 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; goto finish;
eol = -1; eol = -1;
@ -1111,7 +1111,7 @@ static void context_index_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UN
goto finish; goto finish;
if (command != PA_COMMAND_REPLY) { 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; goto finish;
idx = PA_INVALID_INDEX; idx = PA_INVALID_INDEX;
@ -1172,7 +1172,7 @@ static void context_get_autoload_info_callback(pa_pdispatch *pd, uint32_t comman
goto finish; goto finish;
if (command != PA_COMMAND_REPLY) { 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; goto finish;
eol = -1; eol = -1;

View file

@ -108,7 +108,7 @@ static void play_sample_ack_callback(pa_pdispatch *pd, uint32_t command, uint32_
goto finish; goto finish;
if (command != PA_COMMAND_REPLY) { 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; goto finish;
success = 0; success = 0;
@ -141,7 +141,7 @@ static void play_sample_with_proplist_ack_callback(pa_pdispatch *pd, uint32_t co
goto finish; goto finish;
if (command != PA_COMMAND_REPLY) { 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; goto finish;
idx = PA_INVALID_INDEX; idx = PA_INVALID_INDEX;

File diff suppressed because it is too large Load diff

View file

@ -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 */ * server is older than 0.9.8. \since 0.9.8 */
int pa_stream_is_suspended(pa_stream *s); 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 */ /** Connect the stream to a sink */
int pa_stream_connect_playback( int pa_stream_connect_playback(
pa_stream *s /**< The stream to connect to a sink */, 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( int pa_stream_write(
pa_stream *p /**< The stream to use */, pa_stream *p /**< The stream to use */,
const void *data /**< The data to write */, 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 */, 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 */ 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 */); 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( int pa_stream_peek(
pa_stream *p /**< The stream to use */, pa_stream *p /**< The stream to use */,
const void **data /**< Pointer to pointer that will point to data */, 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 /** Remove the current fragment on record streams. It is invalid to do this without first
* calling pa_stream_peek(). */ * 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) */ /** 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); 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 /** Set the callback function that is called whenever a latency
* information update happens. Useful on PA_STREAM_AUTO_TIMING_UPDATE * information update happens. Useful on PA_STREAM_AUTO_TIMING_UPDATE
* streams only. (Only for playback streams) */ * streams only. (Only for playback streams) */

View file

@ -41,6 +41,7 @@
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/time.h> #include <sys/time.h>
#include <dirent.h>
#ifdef HAVE_STRTOF_L #ifdef HAVE_STRTOF_L
#include <locale.h> #include <locale.h>
@ -103,12 +104,6 @@
#define MSG_NOSIGNAL 0 #define MSG_NOSIGNAL 0
#endif #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 #ifdef OS_IS_WIN32
#define PULSE_ROOTENV "PULSE_ROOT" #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; goto fail;
} }
#else #else
pa_log_warn("secure directory creation not supported on Win32."); pa_log_warn("Secure directory creation not supported on Win32.");
#endif #endif
return 0; return 0;
@ -557,6 +552,82 @@ int pa_make_realtime(int rtprio) {
#endif #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 /* Raise the priority of the current process as much as possible that
* is <= the specified nice level..*/ * is <= the specified nice level..*/
int pa_raise_priority(int 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.*/ /* Try to parse a boolean string value.*/
int pa_parse_boolean(const char *v) { 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")) if (!strcmp(v, "1") || v[0] == 'y' || v[0] == 'Y' || v[0] == 't' || v[0] == 'T' || !strcasecmp(v, "on"))
return 1; return 1;
@ -1093,11 +1165,11 @@ int pa_unlock_lockfile(const char *fn, int fd) {
return r; return r;
} }
char *pa_get_state_dir(void) { char *pa_get_runtime_dir(void) {
const char *e; const char *e;
char *d; char *d;
if ((e = getenv("PULSE_STATE_PATH"))) if ((e = getenv("PULSE_RUNTIME_PATH")))
d = pa_xstrdup(e); d = pa_xstrdup(e);
else { else {
char h[PATH_MAX]; char h[PATH_MAX];
@ -1107,19 +1179,15 @@ char *pa_get_state_dir(void) {
return NULL; return NULL;
} }
d = pa_sprintf_malloc("%s/.pulse", h); d = pa_sprintf_malloc("%s" PA_PATH_SEP ".pulse", h);
} }
mkdir(d, 0755); 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;
}
if (access(d, W_OK) == 0) return d;
return d;
pa_log_error("Failed to set up state directory %s", d);
pa_xfree(d);
return NULL;
} }
/* Try to open a configuration file. If "env" is specified, open the /* Try to open a configuration file. If "env" is specified, open the
@ -1128,10 +1196,95 @@ char *pa_get_state_dir(void) {
* file system. If "result" is non-NULL, a pointer to a newly * file system. If "result" is non-NULL, a pointer to a newly
* allocated buffer containing the used configuration file is * allocated buffer containing the used configuration file is
* stored there.*/ * 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; const char *fn;
char h[PATH_MAX]; #ifdef OS_IS_WIN32
char buf[PATH_MAX];
if (!getenv(PULSE_ROOTENV))
pa_set_root(NULL);
#endif
if (env && (fn = getenv(env))) {
FILE *f;
#ifdef OS_IS_WIN32
if (!ExpandEnvironmentStrings(fn, buf, PATH_MAX))
return NULL;
fn = buf;
#endif
if ((f = fopen(fn, "r"))) {
if (result)
*result = pa_xstrdup(fn);
return f;
}
pa_log_warn("Failed to open configuration file '%s': %s", fn, pa_cstrerror(errno));
return NULL;
}
if (local) {
const char *e;
char *lfn;
char h[PATH_MAX];
FILE *f;
if ((e = getenv("PULSE_CONFIG_PATH")))
fn = lfn = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", e, local);
else if (pa_get_home_dir(h, sizeof(h)))
fn = lfn = pa_sprintf_malloc("%s" PA_PATH_SEP ".pulse" PA_PATH_SEP "%s", h, local);
#ifdef OS_IS_WIN32
if (!ExpandEnvironmentStrings(lfn, buf, PATH_MAX)) {
pa_xfree(lfn);
return NULL;
}
fn = buf;
#endif
if ((f = fopen(fn, "r"))) {
if (result)
*result = pa_xstrdup(fn);
pa_xfree(lfn);
return f;
}
if (errno != ENOENT) {
pa_log_warn("Failed to open configuration file '%s': %s", fn, pa_cstrerror(errno));
pa_xfree(lfn);
return NULL;
}
pa_xfree(lfn);
}
if (global) {
FILE *f;
#ifdef OS_IS_WIN32
if (!ExpandEnvironmentStrings(global, buf, PATH_MAX))
return NULL;
global = buf;
#endif
if ((f = fopen(global, "r"))) {
if (result)
*result = pa_xstrdup(global);
return f;
}
} else
errno = ENOENT;
return NULL;
}
char *pa_find_config_file(const char *global, const char *local, const char *env) {
const char *fn;
#ifdef OS_IS_WIN32 #ifdef OS_IS_WIN32
char buf[PATH_MAX]; char buf[PATH_MAX];
@ -1146,69 +1299,59 @@ FILE *pa_open_config_file(const char *global, const char *local, const char *env
fn = buf; fn = buf;
#endif #endif
if (result) if (access(fn, R_OK) == 0)
*result = pa_xstrdup(fn); return pa_xstrdup(fn);
return fopen(fn, mode); pa_log_warn("Failed to access configuration file '%s': %s", fn, pa_cstrerror(errno));
return NULL;
} }
if (local) { if (local) {
const char *e; const char *e;
char *lfn = NULL; char *lfn;
char h[PATH_MAX];
if ((e = getenv("PULSE_CONFIG_PATH"))) if ((e = getenv("PULSE_CONFIG_PATH")))
fn = lfn = pa_sprintf_malloc("%s/%s", e, local); fn = lfn = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", e, local);
else if (pa_get_home_dir(h, sizeof(h))) { else if (pa_get_home_dir(h, sizeof(h)))
char *d; 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);
fn = lfn = pa_sprintf_malloc("%s/.pulse/%s", h, local);
}
if (lfn) {
FILE *f;
#ifdef OS_IS_WIN32 #ifdef OS_IS_WIN32
if (!ExpandEnvironmentStrings(lfn, buf, PATH_MAX)) if (!ExpandEnvironmentStrings(lfn, buf, PATH_MAX)) {
return NULL;
fn = buf;
#endif
f = fopen(fn, mode);
if (f != NULL) {
if (result)
*result = pa_xstrdup(fn);
pa_xfree(lfn);
return f;
}
if (errno != ENOENT)
pa_log_warn("Failed to open configuration file '%s': %s", lfn, pa_cstrerror(errno));
pa_xfree(lfn); pa_xfree(lfn);
return NULL;
} }
} fn = buf;
if (!global) {
if (result)
*result = NULL;
errno = ENOENT;
return NULL;
}
#ifdef OS_IS_WIN32
if (!ExpandEnvironmentStrings(global, buf, PATH_MAX))
return NULL;
global = buf;
#endif #endif
if (result) if (access(fn, R_OK) == 0) {
*result = pa_xstrdup(global); char *r = pa_xstrdup(fn);
pa_xfree(lfn);
return r;
}
return fopen(global, mode); if (errno != ENOENT) {
pa_log_warn("Failed to access configuration file '%s': %s", fn, pa_cstrerror(errno));
pa_xfree(lfn);
return NULL;
}
pa_xfree(lfn);
}
if (global) {
#ifdef OS_IS_WIN32
if (!ExpandEnvironmentStrings(global, buf, PATH_MAX))
return NULL;
global = buf;
#endif
if (access(fn, R_OK) == 0)
return pa_xstrdup(global);
} else
errno = ENOENT;
return NULL;
} }
/* Format the specified data as a hexademical string */ /* 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; return l1 >= l2 && strcmp(s+l1-l2, sfx) == 0;
} }
/* if fn is null return the PulseAudio run time path in s (/tmp/pulse) pa_bool_t pa_is_path_absolute(const char *fn) {
* if fn is non-null and starts with / return fn in s pa_assert(fn);
* 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;
#ifndef OS_IS_WIN32 #ifndef OS_IS_WIN32
if (fn && *fn == '/') return *fn == '/';
#else #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 #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_assert(p);
pa_snprintf(s, l, "%s%c%s", e, PA_PATH_SEP_CHAR, fn);
else
pa_snprintf(s, l, "%s", e);
} else { if (pa_is_path_absolute(p))
char u[256]; return pa_xstrdup(p);
if (fn) if (!(cwd = pa_getcwd()))
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); return pa_xstrdup(p);
else
pa_snprintf(s, l, "%s%s", PA_USER_RUNTIME_PATH_PREFIX, pa_get_user_name(u, sizeof(u)));
}
r = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", cwd, p);
pa_xfree(cwd);
return r;
}
#ifdef OS_IS_WIN32 /* if fn is null return the PulseAudio run time path in s (~/.pulse)
{ * if fn is non-null and starts with / return fn
char buf[l]; * otherwise append fn to the run time path and return it */
strcpy(buf, s); char *pa_runtime_path(const char *fn) {
ExpandEnvironmentStrings(buf, s, l); char *rtp;
}
#endif
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 */ /* 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) { void *pa_will_need(const void *p, size_t l) {
#ifdef RLIMIT_MEMLOCK #ifdef RLIMIT_MEMLOCK
struct rlimit rlim; struct rlimit rlim;
@ -1606,3 +1738,249 @@ char *pa_readlink(const char *p) {
l *= 2; l *= 2;
} }
} }
int pa_close_all(int except_fd, ...) {
va_list ap;
int n = 0, i, r;
int *p;
va_start(ap, except_fd);
if (except_fd >= 0)
for (n = 1; va_arg(ap, int) >= 0; n++)
;
va_end(ap);
p = pa_xnew(int, n+1);
va_start(ap, except_fd);
i = 0;
if (except_fd >= 0) {
p[i++] = except_fd;
while ((p[i++] = va_arg(ap, int)) >= 0)
;
}
p[i] = -1;
va_end(ap);
r = pa_close_allv(p);
free(p);
return r;
}
int pa_close_allv(const int except_fds[]) {
struct rlimit rl;
int fd;
int saved_errno;
#ifdef __linux__
DIR *d;
if ((d = opendir("/proc/self/fd"))) {
struct dirent *de;
while ((de = readdir(d))) {
long l;
char *e = NULL;
int i;
if (de->d_name[0] == '.')
continue;
errno = 0;
l = strtol(de->d_name, &e, 10);
if (errno != 0 || !e || *e) {
closedir(d);
errno = EINVAL;
return -1;
}
fd = (int) l;
if ((long) fd != l) {
closedir(d);
errno = EINVAL;
return -1;
}
if (fd <= 3)
continue;
if (fd == dirfd(d))
continue;
for (i = 0; except_fds[i] >= 0; i++)
if (except_fds[i] == fd)
continue;
if (close(fd) < 0) {
saved_errno = errno;
closedir(d);
errno = saved_errno;
return -1;
}
}
closedir(d);
return 0;
}
#endif
if (getrlimit(RLIMIT_NOFILE, &rl) < 0)
return -1;
for (fd = 0; fd < (int) rl.rlim_max; fd++) {
int i;
if (fd <= 3)
continue;
for (i = 0; except_fds[i] >= 0; i++)
if (except_fds[i] == fd)
continue;
if (close(fd) < 0 && errno != EBADF)
return -1;
}
return 0;
}
int pa_unblock_sigs(int except, ...) {
va_list ap;
int n = 0, i, r;
int *p;
va_start(ap, except);
if (except >= 1)
for (n = 1; va_arg(ap, int) >= 0; n++)
;
va_end(ap);
p = pa_xnew(int, n+1);
va_start(ap, except);
i = 0;
if (except >= 1) {
p[i++] = except;
while ((p[i++] = va_arg(ap, int)) >= 0)
;
}
p[i] = -1;
va_end(ap);
r = pa_unblock_sigsv(p);
pa_xfree(p);
return r;
}
int pa_unblock_sigsv(const int except[]) {
int i;
sigset_t ss;
if (sigemptyset(&ss) < 0)
return -1;
for (i = 0; except[i] > 0; i++)
if (sigaddset(&ss, except[i]) < 0)
return -1;
return sigprocmask(SIG_SETMASK, &ss, NULL);
}
int pa_reset_sigs(int except, ...) {
va_list ap;
int n = 0, i, r;
int *p;
va_start(ap, except);
if (except >= 1)
for (n = 1; va_arg(ap, int) >= 0; n++)
;
va_end(ap);
p = pa_xnew(int, n+1);
va_start(ap, except);
i = 0;
if (except >= 1) {
p[i++] = except;
while ((p[i++] = va_arg(ap, int)) >= 0)
;
}
p[i] = -1;
va_end(ap);
r = pa_reset_sigsv(p);
pa_xfree(p);
return r;
}
int pa_reset_sigsv(const int except[]) {
int sig;
for (sig = 1; sig < _NSIG; sig++) {
int reset = 1;
switch (sig) {
case SIGKILL:
case SIGSTOP:
reset = 0;
break;
default: {
int i;
for (i = 0; except[i] > 0; i++) {
if (sig == except[i]) {
reset = 0;
break;
}
}
}
}
if (reset) {
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_handler = SIG_DFL;
/* On Linux the first two RT signals are reserved by
* glibc, and sigaction() will return EINVAL for them. */
if ((sigaction(sig, &sa, NULL) < 0))
if (errno != EINVAL)
return -1;
}
}
return 0;
}
void pa_set_env(const char *key, const char *value) {
pa_assert(key);
pa_assert(value);
putenv(pa_sprintf_malloc("%s=%s", key, value));
}

View file

@ -30,11 +30,27 @@
#include <stdarg.h> #include <stdarg.h>
#include <stdio.h> #include <stdio.h>
#ifdef HAVE_SYS_RESOURCE_H
#include <sys/resource.h>
#endif
#include <pulse/gccmacro.h> #include <pulse/gccmacro.h>
#include <pulsecore/macro.h> #include <pulsecore/macro.h>
struct timeval; 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_nonblock(int fd);
void pa_make_fd_cloexec(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); int pa_raise_priority(int nice_level);
void pa_reset_priority(void); 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; int pa_parse_boolean(const char *s) PA_GCC_PURE;
static inline const char *pa_yes_no(pa_bool_t b) { 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)"; 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(const char *c, const char*delimiters, const char **state);
char *pa_split_spaces(const char *c, 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_lock_lockfile(const char *fn);
int pa_unlock_lockfile(const char *fn, int fd); 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); 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); 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_startswith(const char *s, const char *pfx) PA_GCC_PURE;
int pa_endswith(const char *s, const char *sfx) 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_atoi(const char *s, int32_t *ret_i);
int pa_atou(const char *s, uint32_t *ret_u); 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_getcwd(void);
char *pa_make_path_absolute(const char *p); 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); 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_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 #endif

View file

@ -147,6 +147,9 @@ enum {
PA_COMMAND_REMOVE_PLAYBACK_STREAM_PROPLIST, PA_COMMAND_REMOVE_PLAYBACK_STREAM_PROPLIST,
PA_COMMAND_REMOVE_CLIENT_PROPLIST, PA_COMMAND_REMOVE_CLIENT_PROPLIST,
/* SERVER->CLIENT */
PA_COMMAND_STARTED,
PA_COMMAND_MAX PA_COMMAND_MAX
}; };

View file

@ -144,16 +144,16 @@ fail:
int pa_pid_file_create(void) { int pa_pid_file_create(void) {
int fd = -1; int fd = -1;
int ret = -1; int ret = -1;
char fn[PATH_MAX];
char t[20]; char t[20];
pid_t pid; pid_t pid;
size_t l; size_t l;
char *fn;
#ifdef OS_IS_WIN32 #ifdef OS_IS_WIN32
HANDLE process; HANDLE process;
#endif #endif
pa_runtime_path("pid", fn, sizeof(fn)); fn = pa_runtime_path("pid");
if ((fd = open_pid_file(fn, O_CREAT|O_RDWR)) < 0) if ((fd = open_pid_file(fn, O_CREAT|O_RDWR)) < 0)
goto fail; goto fail;
@ -200,17 +200,19 @@ fail:
} }
} }
pa_xfree(fn);
return ret; return ret;
} }
/* Remove the PID file, if it is ours */ /* Remove the PID file, if it is ours */
int pa_pid_file_remove(void) { int pa_pid_file_remove(void) {
int fd = -1; int fd = -1;
char fn[PATH_MAX]; char *fn;
int ret = -1; int ret = -1;
pid_t pid; pid_t pid;
pa_runtime_path("pid", fn, sizeof(fn)); fn = pa_runtime_path("pid");
if ((fd = open_pid_file(fn, O_RDWR)) < 0) { if ((fd = open_pid_file(fn, O_RDWR)) < 0) {
pa_log_warn("Failed to open PID file '%s': %s", fn, pa_cstrerror(errno)); pa_log_warn("Failed to open PID file '%s': %s", fn, pa_cstrerror(errno));
@ -254,6 +256,8 @@ fail:
} }
} }
pa_xfree(fn);
return ret; return ret;
} }
@ -272,7 +276,7 @@ int pa_pid_file_check_running(pid_t *pid, const char *binary_name) {
* process. */ * process. */
int pa_pid_file_kill(int sig, pid_t *pid, const char *binary_name) { int pa_pid_file_kill(int sig, pid_t *pid, const char *binary_name) {
int fd = -1; int fd = -1;
char fn[PATH_MAX]; char *fn;
int ret = -1; int ret = -1;
pid_t _pid; pid_t _pid;
#ifdef __linux__ #ifdef __linux__
@ -281,7 +285,7 @@ int pa_pid_file_kill(int sig, pid_t *pid, const char *binary_name) {
if (!pid) if (!pid)
pid = &_pid; pid = &_pid;
pa_runtime_path("pid", fn, sizeof(fn)); fn = pa_runtime_path("pid");
if ((fd = open_pid_file(fn, O_RDONLY)) < 0) if ((fd = open_pid_file(fn, O_RDONLY)) < 0)
goto fail; 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))) { if ((e = pa_readlink(fn))) {
char *f = pa_path_get_filename(e); char *f = pa_path_get_filename(e);
if (strcmp(f, binary_name) if (strcmp(f, binary_name)
#if defined(__OPTIMIZE__) #if !defined(__OPTIMIZE__)
/* libtool likes to rename our binary names ... */ /* libtool likes to rename our binary names ... */
&& !(pa_startswith(f, "lt-") && strcmp(f+3, binary_name) == 0) && !(pa_startswith(f, "lt-") && strcmp(f+3, binary_name) == 0)
#endif #endif
@ -319,6 +323,8 @@ fail:
pa_xfree(e); pa_xfree(e);
#endif #endif
pa_xfree(fn);
return ret; return ret;
} }

View file

@ -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 = pa_xnew(pa_protocol_cli, 1);
p->module = m; p->module = m;
p->core = core; p->core = core;
p->server = server; p->server = pa_socket_server_ref(server);
p->connections = pa_idxset_new(NULL, NULL); p->connections = pa_idxset_new(NULL, NULL);
pa_socket_server_set_callback(p->server, on_connection, p); pa_socket_server_set_callback(p->server, on_connection, p);

View file

@ -1433,7 +1433,7 @@ pa_protocol_esound* pa_protocol_esound_new(pa_core*core, pa_socket_server *serve
p->core = core; p->core = core;
p->module = m; p->module = m;
p->public = public; p->public = public;
p->server = server; p->server = pa_socket_server_ref(server);
pa_socket_server_set_callback(p->server, on_connection, p); pa_socket_server_set_callback(p->server, on_connection, p);
p->connections = pa_idxset_new(NULL, NULL); p->connections = pa_idxset_new(NULL, NULL);

View file

@ -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 = pa_xnew(pa_protocol_http, 1);
p->module = m; p->module = m;
p->core = core; p->core = core;
p->server = server; p->server = pa_socket_server_ref(server);
p->connections = pa_idxset_new(NULL, NULL); p->connections = pa_idxset_new(NULL, NULL);
pa_socket_server_set_callback(p->server, on_connection, p); pa_socket_server_set_callback(p->server, on_connection, p);

View file

@ -105,7 +105,6 @@ typedef struct playback_stream {
pa_bool_t drain_request; pa_bool_t drain_request;
uint32_t drain_tag; uint32_t drain_tag;
uint32_t syncid; uint32_t syncid;
uint64_t underrun; /* length of underrun */
pa_atomic_t missing; pa_atomic_t missing;
size_t minreq; 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_REQUEST_DATA, /* data requested from sink input from the main loop */
PLAYBACK_STREAM_MESSAGE_UNDERFLOW, PLAYBACK_STREAM_MESSAGE_UNDERFLOW,
PLAYBACK_STREAM_MESSAGE_OVERFLOW, PLAYBACK_STREAM_MESSAGE_OVERFLOW,
PLAYBACK_STREAM_MESSAGE_DRAIN_ACK PLAYBACK_STREAM_MESSAGE_DRAIN_ACK,
PLAYBACK_STREAM_MESSAGE_STARTED
}; };
enum { enum {
@ -689,10 +689,24 @@ static int playback_stream_process_msg(pa_msgobject *o, int code, void*userdata,
break; 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: case PLAYBACK_STREAM_MESSAGE_DRAIN_ACK:
pa_pstream_send_simple_ack(s->connection->pstream, PA_PTR_TO_UINT(userdata)); pa_pstream_send_simple_ack(s->connection->pstream, PA_PTR_TO_UINT(userdata));
break; break;
} }
return 0; return 0;
@ -886,7 +900,6 @@ static playback_stream* playback_stream_new(
s->connection = c; s->connection = c;
s->syncid = syncid; s->syncid = syncid;
s->sink_input = sink_input; s->sink_input = sink_input;
s->underrun = (uint64_t) -1;
s->sink_input->parent.process_msg = sink_input_process_msg; s->sink_input->parent.process_msg = sink_input_process_msg;
s->sink_input->pop = sink_input_pop_cb; 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)); */ /* 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)); */ /* 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); size_t u = pa_memblockq_get_length(s->memblockq);
if (u >= s->underrun) if (u >= s->sink_input->thread_info.underrun_for)
u = s->underrun; u = s->sink_input->thread_info.underrun_for;
/* We just ended an underrun, let's ask the sink /* We just ended an underrun, let's ask the sink
* to rewrite */ * 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 { } else {
@ -1117,7 +1130,7 @@ static void handle_seek(playback_stream *s, int64_t indexw) {
/* OK, the sink already asked for this data, so /* OK, the sink already asked for this data, so
* let's have it usk us again */ * 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); 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)) { if (s->drain_request && pa_sink_input_safe_to_remove(i)) {
s->drain_request = FALSE; 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); 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); 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); */ /* pa_log("added %llu bytes, total is %llu", (unsigned long long) nbytes, (unsigned long long) s->underrun); */
request_bytes(s); 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"); */ /* 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); pa_memblockq_drop(s->memblockq, chunk->length);
request_bytes(s); 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); playback_stream_assert_ref(s);
/* If we are in an underrun, then we don't rewind */ /* If we are in an underrun, then we don't rewind */
if (s->underrun != 0) if (i->thread_info.underrun_for > 0)
return; return;
pa_memblockq_rewind(s->memblockq, nbytes); 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, latency);
pa_tagstruct_put_usec(reply, 0); 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, &tv);
pa_tagstruct_put_timeval(reply, pa_gettimeofday(&now)); pa_tagstruct_put_timeval(reply, pa_gettimeofday(&now));
pa_tagstruct_puts64(reply, s->write_index); pa_tagstruct_puts64(reply, s->write_index);
pa_tagstruct_puts64(reply, s->read_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); 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); 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, 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_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, &tv);
pa_tagstruct_put_timeval(reply, pa_gettimeofday(&now)); pa_tagstruct_put_timeval(reply, pa_gettimeofday(&now));
pa_tagstruct_puts64(reply, pa_memblockq_get_write_index(s->memblockq)); 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 #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) { if (pa_modargs_get_value_boolean(ma, "auth-group-enabled", &a) < 0) {
pa_log("auth-group-enabled= expects a boolean argument."); pa_log("auth-group-enabled= expects a boolean argument.");
return NULL; 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))) if (!(p = protocol_new_internal(core, m, ma)))
return NULL; return NULL;
p->server = server; p->server = pa_socket_server_ref(server);
pa_socket_server_set_callback(p->server, on_connection, p); pa_socket_server_set_callback(p->server, on_connection, p);
if (pa_socket_server_get_address(p->server, t, sizeof(t))) { if (pa_socket_server_get_address(p->server, t, sizeof(t))) {

View file

@ -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 = pa_xnew0(pa_protocol_simple, 1);
p->module = m; p->module = m;
p->core = core; p->core = core;
p->server = server; p->server = pa_socket_server_ref(server);
p->connections = pa_idxset_new(NULL, NULL); p->connections = pa_idxset_new(NULL, NULL);
p->sample_spec = core->default_sample_spec; p->sample_spec = core->default_sample_spec;

View file

@ -106,6 +106,7 @@ static void reset_callbacks(pa_sink_input *i) {
i->moved = NULL; i->moved = NULL;
i->kill = NULL; i->kill = NULL;
i->get_latency = NULL; i->get_latency = NULL;
i->state_change = NULL;
} }
pa_sink_input* pa_sink_input_new( 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.muted = i->muted;
i->thread_info.requested_sink_latency = (pa_usec_t) -1; i->thread_info.requested_sink_latency = (pa_usec_t) -1;
i->thread_info.rewrite_nbytes = 0; i->thread_info.rewrite_nbytes = 0;
i->thread_info.since_underrun = 0; i->thread_info.underrun_for = (uint64_t) -1;
i->thread_info.ignore_rewind = FALSE; i->thread_info.playing_for = 0;
i->thread_info.render_memblockq = pa_memblockq_new( i->thread_info.render_memblockq = pa_memblockq_new(
0, 0,
@ -328,7 +329,7 @@ void pa_sink_input_unlink(pa_sink_input *i) {
pa_sink_input_ref(i); pa_sink_input_ref(i);
linked = PA_SINK_INPUT_LINKED(i->state); linked = PA_SINK_INPUT_IS_LINKED(i->state);
if (linked) if (linked)
pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_UNLINK], i); 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)) if (pa_idxset_remove_by_data(i->sink->inputs, i, NULL))
pa_sink_input_unref(i); 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); 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); reset_callbacks(i);
@ -368,7 +368,7 @@ static void sink_input_free(pa_object *o) {
pa_assert(i); pa_assert(i);
pa_assert(pa_sink_input_refcnt(i) == 0); 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_sink_input_unlink(i);
pa_log_info("Freeing input %u \"%s\"", i->index, pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_MEDIA_NAME))); 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; state = i->flags & PA_SINK_INPUT_START_CORKED ? PA_SINK_INPUT_CORKED : PA_SINK_INPUT_RUNNING;
update_n_corked(i, state); 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); 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) { void pa_sink_input_kill(pa_sink_input*i) {
pa_sink_input_assert_ref(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) if (i->kill)
i->kill(i); 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_usec_t r = 0;
pa_sink_input_assert_ref(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 (pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_GET_LATENCY, &r, 0, NULL) < 0) if (pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_GET_LATENCY, &r, 0, NULL) < 0)
r = 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; size_t ilength;
pa_sink_input_assert_ref(i); 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(pa_frame_aligned(slength, &i->sink->sample_spec));
pa_assert(chunk); pa_assert(chunk);
pa_assert(volume); 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_atomic_store(&i->thread_info.drained, 1);
pa_memblockq_seek(i->thread_info.render_memblockq, slength, PA_SEEK_RELATIVE_ON_READ); 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; 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.length > 0);
pa_assert(tchunk.memblock); 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) { while (tchunk.length > 0) {
pa_memchunk wchunk; 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 */) { void pa_sink_input_drop(pa_sink_input *i, size_t nbytes /* in sink sample spec */) {
pa_sink_input_assert_ref(i); 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(pa_frame_aligned(nbytes, &i->sink->sample_spec));
pa_assert(nbytes > 0); 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 */) { void pa_sink_input_process_rewind(pa_sink_input *i, size_t nbytes /* in sink sample spec */) {
pa_sink_input_assert_ref(i); 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(pa_frame_aligned(nbytes, &i->sink->sample_spec));
/* pa_log_debug("rewind(%lu, %lu)", (unsigned long) nbytes, (unsigned long) i->thread_info.rewrite_nbytes); */ /* pa_log_debug("rewind(%lu, %lu)", (unsigned long) nbytes, (unsigned long) i->thread_info.rewrite_nbytes); */
if (i->thread_info.ignore_rewind) { if (i->thread_info.underrun_for > 0) {
i->thread_info.ignore_rewind = FALSE; /* We don't rewind when we are underrun */
i->thread_info.rewrite_nbytes = 0; i->thread_info.rewrite_nbytes = 0;
return; 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 */ /* Called from thread context */
void pa_sink_input_update_max_rewind(pa_sink_input *i, size_t nbytes /* in the sink's sample spec */) { void pa_sink_input_update_max_rewind(pa_sink_input *i, size_t nbytes /* in the sink's sample spec */) {
pa_sink_input_assert_ref(i); 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(pa_frame_aligned(nbytes, &i->sink->sample_spec));
pa_memblockq_set_maxrewind(i->thread_info.render_memblockq, nbytes); 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); i->update_max_rewind(i, i->thread_info.resampler ? pa_resampler_request(i->thread_info.resampler, nbytes) : nbytes);
} }
static pa_usec_t fixup_latency(pa_sink *s, pa_usec_t usec) {
pa_sink_assert_ref(s);
if (usec == (pa_usec_t) -1)
return usec;
if (s->max_latency > 0 && usec > s->max_latency)
usec = s->max_latency;
if (s->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);
return usec;
}
pa_usec_t pa_sink_input_set_requested_latency(pa_sink_input *i, pa_usec_t usec) { pa_usec_t pa_sink_input_set_requested_latency(pa_sink_input *i, pa_usec_t usec) {
pa_sink_input_assert_ref(i); pa_sink_input_assert_ref(i);
if (usec != (pa_usec_t) -1) { usec = fixup_latency(i->sink, usec);
if (i->sink->max_latency > 0 && usec > i->sink->max_latency) if (PA_SINK_INPUT_IS_LINKED(i->state))
usec = i->sink->max_latency;
if (i->sink->min_latency > 0 && usec < i->sink->min_latency)
usec = i->sink->min_latency;
}
if (PA_SINK_INPUT_LINKED(i->state))
pa_asyncmsgq_post(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_REQUESTED_LATENCY, NULL, (int64_t) usec, NULL, NULL); pa_asyncmsgq_post(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_REQUESTED_LATENCY, NULL, (int64_t) usec, NULL, NULL);
else { 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->thread_info.requested_sink_latency = usec;
i->sink->thread_info.requested_latency_valid = FALSE; 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) { void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume) {
pa_sink_input_assert_ref(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 (pa_cvolume_equal(&i->volume, volume)) if (pa_cvolume_equal(&i->volume, volume))
return; 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) { const pa_cvolume *pa_sink_input_get_volume(pa_sink_input *i) {
pa_sink_input_assert_ref(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; 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) { void pa_sink_input_set_mute(pa_sink_input *i, pa_bool_t mute) {
pa_assert(i); pa_assert(i);
pa_sink_input_assert_ref(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) if (!i->muted == !mute)
return; 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) { int pa_sink_input_get_mute(pa_sink_input *i) {
pa_sink_input_assert_ref(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; return !!i->muted;
} }
void pa_sink_input_cork(pa_sink_input *i, pa_bool_t b) { void pa_sink_input_cork(pa_sink_input *i, pa_bool_t b) {
pa_sink_input_assert_ref(i); 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); 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) { int pa_sink_input_set_rate(pa_sink_input *i, uint32_t rate) {
pa_sink_input_assert_ref(i); 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); pa_return_val_if_fail(i->thread_info.resampler, -1);
if (i->sample_spec.rate == rate) if (i->sample_spec.rate == rate)
@ -780,7 +803,7 @@ void pa_sink_input_set_name(pa_sink_input *i, const char *name) {
else else
pa_proplist_unset(i->proplist, PA_PROP_MEDIA_NAME); 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_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); 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; 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_resampler *new_resampler;
pa_sink *origin; pa_sink *origin;
pa_usec_t silence_usec = 0; 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_move_hook_data hook_data;
pa_sink_input_assert_ref(i); 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); pa_sink_assert_ref(dest);
origin = i->sink; origin = i->sink;
@ -983,7 +1006,7 @@ int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, int immediately) {
return 0; 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); pa_sink_input_assert_ref(i);
if ((state == PA_SINK_INPUT_DRAINED || state == PA_SINK_INPUT_RUNNING) && 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 /* This will tell the implementing sink input driver to rewind
* so that the unplayed already mixed data is not lost */ * 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) { } 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 /* OK, we're being uncorked. Make sure we're not rewound when
* the hw buffer is remixed and request a remix. */ * the hw buffer is remixed and request a remix. */
i->thread_info.ignore_rewind = TRUE; pa_sink_input_request_rewind(i, 0, TRUE, TRUE);
i->thread_info.since_underrun = 0;
pa_sink_request_rewind(i->sink, 0);
} }
if (i->state_change)
i->state_change(i, state);
i->thread_info.state = 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 *i = PA_SINK_INPUT(o);
pa_sink_input_assert_ref(i); 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) { switch (code) {
case PA_SINK_INPUT_MESSAGE_SET_VOLUME: case PA_SINK_INPUT_MESSAGE_SET_VOLUME:
i->thread_info.volume = *((pa_cvolume*) userdata); 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; return 0;
case PA_SINK_INPUT_MESSAGE_SET_MUTE: case PA_SINK_INPUT_MESSAGE_SET_MUTE:
i->thread_info.muted = PA_PTR_TO_UINT(userdata); 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; return 0;
case PA_SINK_INPUT_MESSAGE_GET_LATENCY: { 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: { case PA_SINK_INPUT_MESSAGE_SET_STATE: {
pa_sink_input *ssync; 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) 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) 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; return 0;
} }
case PA_SINK_INPUT_MESSAGE_SET_REQUESTED_LATENCY: case PA_SINK_INPUT_MESSAGE_SET_REQUESTED_LATENCY:
i->thread_info.requested_sink_latency = (pa_usec_t) offset; pa_sink_input_set_requested_latency_within_thread(i, (pa_usec_t) offset);
pa_sink_invalidate_requested_latency(i->sink);
return 0; return 0;
} }
@ -1088,8 +1110,8 @@ pa_bool_t pa_sink_input_safe_to_remove(pa_sink_input *i) {
return TRUE; return TRUE;
} }
void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes /* in our sample spec */, pa_bool_t ignore_underruns) { 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 l, lbq; size_t lbq;
pa_sink_input_assert_ref(i); 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) if (i->state == PA_SINK_INPUT_CORKED)
return; 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) { if (nbytes <= 0) {
/* Calulate maximum number of bytes that could be rewound in theory */ /* Calulate maximum number of bytes that could be rewound in theory */
nbytes = i->sink->thread_info.max_rewind + lbq; 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; nbytes;
} }
/* Increase the number of bytes to rewrite, never decrease */ if (not_here) {
if (nbytes > i->thread_info.rewrite_nbytes) i->thread_info.playing_for = 0;
i->thread_info.rewrite_nbytes = nbytes; 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 */ /* Make sure to not overwrite over underruns */
if ((int64_t) i->thread_info.rewrite_nbytes > i->thread_info.since_underrun) if (!ignore_underruns)
i->thread_info.rewrite_nbytes = (size_t) i->thread_info.since_underrun; 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 */ /* Transform to sink domain */
l = i->thread_info.resampler ? nbytes =
pa_resampler_result(i->thread_info.resampler, i->thread_info.rewrite_nbytes) : i->thread_info.resampler ?
i->thread_info.rewrite_nbytes; pa_resampler_result(i->thread_info.resampler, nbytes) :
nbytes;
if (l <= 0) if (nbytes <= 0)
return; return;
if (l > lbq) if (nbytes > lbq)
pa_sink_request_rewind(i->sink, l - lbq); pa_sink_request_rewind(i->sink, nbytes - lbq);
} }
pa_memchunk* pa_sink_input_get_silence(pa_sink_input *i, pa_memchunk *ret) { pa_memchunk* pa_sink_input_get_silence(pa_sink_input *i, pa_memchunk *ret) {

View file

@ -46,7 +46,7 @@ typedef enum pa_sink_input_state {
PA_SINK_INPUT_UNLINKED /*< The stream is dead */ PA_SINK_INPUT_UNLINKED /*< The stream is dead */
} pa_sink_input_state_t; } 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; 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 */ void (*process_rewind) (pa_sink_input *i, size_t nbytes); /* may NOT be NULL */
/* Called whenever the maximum rewindable size of the sink /* 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 */ 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 /* If non-NULL this function is called when the input is first
@ -138,6 +138,10 @@ struct pa_sink_input {
instead. */ instead. */
pa_usec_t (*get_latency) (pa_sink_input *i); /* may be NULL */ 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 { struct {
pa_sink_input_state_t state; pa_sink_input_state_t state;
pa_atomic_t drained, render_memblockq_is_empty; pa_atomic_t drained, render_memblockq_is_empty;
@ -152,7 +156,7 @@ struct pa_sink_input {
pa_memblockq *render_memblockq; pa_memblockq *render_memblockq;
size_t rewrite_nbytes; size_t rewrite_nbytes;
int64_t since_underrun; uint64_t underrun_for, playing_for;
pa_bool_t ignore_rewind; pa_bool_t ignore_rewind;
pa_sink_input *sync_prev, *sync_next; 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 sink driver will call ->rewind() and pass the number of bytes that
could be rewound in the HW device. This functionality is required for could be rewound in the HW device. This functionality is required for
implementing the "zero latency" write-through functionality. */ 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*/ /* 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); 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); 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_process_rewind(pa_sink_input *i, size_t nbytes /* in the sink's sample spec */);
void pa_sink_input_update_max_rewind(pa_sink_input *i, size_t nbytes /* in the sink's sample spec */); void pa_sink_input_update_max_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); 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 { typedef struct pa_sink_input_move_info {
pa_sink_input *sink_input; pa_sink_input *sink_input;
pa_sink_input *ghost_sink_input; pa_sink_input *ghost_sink_input;

View file

@ -265,8 +265,8 @@ static int sink_set_state(pa_sink *s, pa_sink_state_t state) {
return 0; return 0;
suspend_change = suspend_change =
(s->state == PA_SINK_SUSPENDED && PA_SINK_OPENED(state)) || (s->state == PA_SINK_SUSPENDED && PA_SINK_IS_OPENED(state)) ||
(PA_SINK_OPENED(s->state) && state == PA_SINK_SUSPENDED); (PA_SINK_IS_OPENED(s->state) && state == PA_SINK_SUSPENDED);
if (s->set_state) if (s->set_state)
if ((ret = s->set_state(s, state)) < 0) 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 * may be called multiple times on the same sink without bad
* effects. */ * effects. */
linked = PA_SINK_LINKED(s->state); linked = PA_SINK_IS_LINKED(s->state);
if (linked) if (linked)
pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_UNLINK], s); 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(s);
pa_assert(pa_sink_refcnt(s) == 0); 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_sink_unlink(s);
pa_log_info("Freeing sink %u \"%s\"", s->index, s->name); 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) { void pa_sink_set_asyncmsgq(pa_sink *s, pa_asyncmsgq *q) {
pa_sink_assert_ref(s); pa_sink_assert_ref(s);
pa_assert(q);
s->asyncmsgq = 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) { void pa_sink_set_rtpoll(pa_sink *s, pa_rtpoll *p) {
pa_sink_assert_ref(s); pa_sink_assert_ref(s);
pa_assert(p);
s->rtpoll = p; s->rtpoll = p;
if (s->monitor_source) 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) { int pa_sink_update_status(pa_sink*s) {
pa_sink_assert_ref(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) if (s->state == PA_SINK_SUSPENDED)
return 0; 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) { int pa_sink_suspend(pa_sink *s, pa_bool_t suspend) {
pa_sink_assert_ref(s); pa_sink_assert_ref(s);
pa_assert(PA_SINK_LINKED(s->state)); pa_assert(PA_SINK_IS_LINKED(s->state));
if (suspend) if (suspend)
return sink_set_state(s, PA_SINK_SUSPENDED); 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; pa_sink_input *i;
void *state = NULL; void *state = NULL;
pa_sink_assert_ref(s); 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) if (nbytes <= 0)
return; return;
@ -450,8 +451,9 @@ void pa_sink_process_rewind(pa_sink *s, size_t nbytes) {
pa_sink_input_process_rewind(i, 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); 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) { 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; size_t block_size_max;
pa_sink_assert_ref(s); 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(pa_frame_aligned(length, &s->sample_spec));
pa_assert(result); 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) if (s->thread_info.state == PA_SINK_RUNNING)
inputs_drop(s, info, n, result->length); 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_source_post(s->monitor_source, result);
pa_sink_unref(s); 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; size_t length, block_size_max;
pa_sink_assert_ref(s); 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);
pa_assert(target->memblock); pa_assert(target->memblock);
pa_assert(target->length > 0); 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) if (s->thread_info.state == PA_SINK_RUNNING)
inputs_drop(s, info, n, target->length); 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_source_post(s->monitor_source, target);
pa_sink_unref(s); pa_sink_unref(s);
@ -711,7 +713,7 @@ void pa_sink_render_into_full(pa_sink *s, pa_memchunk *target) {
size_t l, d; size_t l, d;
pa_sink_assert_ref(s); 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);
pa_assert(target->memblock); pa_assert(target->memblock);
pa_assert(target->length > 0); 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) { void pa_sink_render_full(pa_sink *s, size_t length, pa_memchunk *result) {
pa_sink_assert_ref(s); 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(length > 0);
pa_assert(pa_frame_aligned(length, &s->sample_spec)); pa_assert(pa_frame_aligned(length, &s->sample_spec));
pa_assert(result); 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); 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 pa_sink_get_latency(pa_sink *s) {
pa_usec_t usec = 0; pa_usec_t usec = 0;
pa_sink_assert_ref(s); 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! */ /* 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; return 0;
if (s->get_latency) if (s->get_latency)
@ -814,7 +781,7 @@ void pa_sink_set_volume(pa_sink *s, const pa_cvolume *volume) {
int changed; int changed;
pa_sink_assert_ref(s); pa_sink_assert_ref(s);
pa_assert(PA_SINK_LINKED(s->state)); pa_assert(PA_SINK_IS_LINKED(s->state));
pa_assert(volume); pa_assert(volume);
changed = !pa_cvolume_equal(volume, &s->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; struct pa_cvolume old_volume;
pa_sink_assert_ref(s); pa_sink_assert_ref(s);
pa_assert(PA_SINK_LINKED(s->state)); pa_assert(PA_SINK_IS_LINKED(s->state));
old_volume = s->volume; old_volume = s->volume;
@ -854,7 +821,7 @@ void pa_sink_set_mute(pa_sink *s, pa_bool_t mute) {
int changed; int changed;
pa_sink_assert_ref(s); pa_sink_assert_ref(s);
pa_assert(PA_SINK_LINKED(s->state)); pa_assert(PA_SINK_IS_LINKED(s->state));
changed = s->muted != mute; changed = s->muted != mute;
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_bool_t old_muted;
pa_sink_assert_ref(s); pa_sink_assert_ref(s);
pa_assert(PA_SINK_LINKED(s->state)); pa_assert(PA_SINK_IS_LINKED(s->state));
old_muted = s->muted; old_muted = s->muted;
@ -914,7 +881,7 @@ void pa_sink_set_description(pa_sink *s, const char *description) {
pa_xfree(n); 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_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); 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; unsigned ret;
pa_sink_assert_ref(s); 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); ret = pa_idxset_size(s->inputs);
@ -941,7 +908,7 @@ unsigned pa_sink_used_by(pa_sink *s) {
unsigned ret; unsigned ret;
pa_sink_assert_ref(s); 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); ret = pa_idxset_size(s->inputs);
pa_assert(ret >= s->n_corked); 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; 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); pa_assert(!i->thread_info.attached);
i->thread_info.attached = TRUE; i->thread_info.attached = TRUE;
if (i->attach) if (i->attach)
i->attach(i); i->attach(i);
/* If you change anything here, make sure to change the pa_sink_input_set_state_within_thread(i, i->state);
* ghost sink input handling a few lines down at
* PA_SINK_MESSAGE_REMOVE_INPUT_AND_BUFFER, too. */ pa_sink_input_update_max_rewind(i, s->thread_info.max_rewind);
pa_sink_invalidate_requested_latency(s); pa_sink_invalidate_requested_latency(s);
/* Make sure we're not rewound when the hw buffer is remixed and request a remix*/ /* We don't rewind here automatically. This is left to the
i->thread_info.ignore_rewind = TRUE; * sink input implementor because some sink inputs need a
i->thread_info.since_underrun = 0; * slow start, i.e. need some time to buffer client
pa_sink_request_rewind(s, 0); * 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; 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 * sink input handling a few lines down at
* PA_SINK_MESSAGE_REMOVE_INPUT_AND_BUFFER, too. */ * PA_SINK_MESSAGE_REMOVE_INPUT_AND_BUFFER, too. */
pa_sink_input_set_state_within_thread(i, i->state);
if (i->detach) if (i->detach)
i->detach(i); 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_input_unref(i);
pa_sink_invalidate_requested_latency(s); pa_sink_invalidate_requested_latency(s);
pa_sink_request_rewind(s, 0); pa_sink_request_rewind(s, 0);
return 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) if (info->ghost_sink_input->attach)
info->ghost_sink_input->attach(info->ghost_sink_input); info->ghost_sink_input->attach(info->ghost_sink_input);
} }
pa_sink_invalidate_requested_latency(s); pa_sink_invalidate_requested_latency(s);
pa_sink_request_rewind(s, 0); pa_sink_request_rewind(s, 0);
return 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) { void pa_sink_detach(pa_sink *s) {
pa_sink_assert_ref(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); pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_DETACH, NULL, 0, NULL);
} }
void pa_sink_attach(pa_sink *s) { void pa_sink_attach(pa_sink *s) {
pa_sink_assert_ref(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); 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; void *state = NULL;
pa_sink_assert_ref(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));
while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL))) while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
if (i->detach) if (i->detach)
@ -1228,7 +1196,7 @@ void pa_sink_attach_within_thread(pa_sink *s) {
void *state = NULL; void *state = NULL;
pa_sink_assert_ref(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));
while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL))) while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
if (i->attach) 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) { void pa_sink_request_rewind(pa_sink*s, size_t nbytes) {
pa_sink_assert_ref(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 (nbytes <= 0) if (nbytes <= 0)
nbytes = s->thread_info.max_rewind; 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_usec_t usec = 0;
pa_sink_assert_ref(s); 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; return 0;
if (pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_REQUESTED_LATENCY, &usec, 0, NULL) < 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) { void pa_sink_invalidate_requested_latency(pa_sink *s) {
pa_sink_assert_ref(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) if (!s->thread_info.requested_latency_valid)
return; return;

View file

@ -33,7 +33,6 @@ typedef struct pa_sink pa_sink;
#include <pulse/channelmap.h> #include <pulse/channelmap.h>
#include <pulse/volume.h> #include <pulse/volume.h>
#include <pulsecore/core-def.h>
#include <pulsecore/core.h> #include <pulsecore/core.h>
#include <pulsecore/idxset.h> #include <pulsecore/idxset.h>
#include <pulsecore/source.h> #include <pulsecore/source.h>
@ -52,11 +51,11 @@ typedef enum pa_sink_state {
PA_SINK_UNLINKED PA_SINK_UNLINKED
} pa_sink_state_t; } 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; 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; 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 min_latency; /* we won't go below this latency */
pa_usec_t max_latency; /* An upper limit for the latencies */ 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 (*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 */ 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 */ 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 */ 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 */ 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 */ void (*update_requested_latency)(pa_sink *s); /* dito */
/* Contains copies of the above data so that the real-time worker /* 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_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(pa_sink*s, pa_memchunk *target);
void pa_sink_render_into_full(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); void pa_sink_process_rewind(pa_sink *s, size_t nbytes);

View file

@ -55,6 +55,8 @@ typedef struct file_stream {
SNDFILE *sndfile; SNDFILE *sndfile;
sf_count_t (*readf_function)(SNDFILE *sndfile, void *ptr, sf_count_t frames); 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; pa_memblockq *memblockq;
} file_stream; } file_stream;
@ -66,6 +68,7 @@ PA_DECLARE_CLASS(file_stream);
#define FILE_STREAM(o) (file_stream_cast(o)) #define FILE_STREAM(o) (file_stream_cast(o))
static PA_DEFINE_CHECK_TYPE(file_stream, pa_msgobject); static PA_DEFINE_CHECK_TYPE(file_stream, pa_msgobject);
/* Called from main context */
static void file_stream_unlink(file_stream *u) { static void file_stream_unlink(file_stream *u) {
pa_assert(u); pa_assert(u);
@ -80,6 +83,7 @@ static void file_stream_unlink(file_stream *u) {
file_stream_unref(u); file_stream_unref(u);
} }
/* Called from main context */
static void file_stream_free(pa_object *o) { static void file_stream_free(pa_object *o) {
file_stream *u = FILE_STREAM(o); file_stream *u = FILE_STREAM(o);
pa_assert(u); pa_assert(u);
@ -93,6 +97,7 @@ static void file_stream_free(pa_object *o) {
pa_xfree(u); 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) { 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 *u = FILE_STREAM(o);
file_stream_assert_ref(u); 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; return 0;
} }
/* Called from main context */
static void sink_input_kill_cb(pa_sink_input *i) { static void sink_input_kill_cb(pa_sink_input *i) {
file_stream *u; file_stream *u;
@ -116,6 +122,22 @@ static void sink_input_kill_cb(pa_sink_input *i) {
file_stream_unlink(u); 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) { static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) {
file_stream *u; file_stream *u;
@ -131,6 +153,9 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk
for (;;) { for (;;) {
pa_memchunk tchunk; pa_memchunk tchunk;
size_t fs;
void *p;
sf_count_t n;
if (pa_memblockq_peek(u->memblockq, chunk) >= 0) { if (pa_memblockq_peek(u->memblockq, chunk) >= 0) {
pa_memblockq_drop(u->memblockq, chunk->length); 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.memblock = pa_memblock_new(i->sink->core->mempool, length);
tchunk.index = 0; tchunk.index = 0;
p = pa_memblock_acquire(tchunk.memblock);
if (u->readf_function) { if (u->readf_function) {
sf_count_t n; fs = pa_frame_size(&i->sample_spec);
void *p;
size_t fs = pa_frame_size(&i->sample_spec);
p = pa_memblock_acquire(tchunk.memblock);
n = u->readf_function(u->sndfile, p, length/fs); n = u->readf_function(u->sndfile, p, length/fs);
pa_memblock_release(tchunk.memblock);
if (n <= 0)
n = 0;
tchunk.length = n * fs;
} else { } else {
sf_count_t n; fs = 1;
void *p;
p = pa_memblock_acquire(tchunk.memblock);
n = sf_read_raw(u->sndfile, p, length); 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); pa_memblock_unref(tchunk.memblock);
sf_close(u->sndfile); 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; break;
} }
tchunk.length = n * fs;
pa_memblockq_push(u->memblockq, &tchunk); pa_memblockq_push(u->memblockq, &tchunk);
pa_memblock_unref(tchunk.memblock); 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; return -1;
} }
static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) { static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
file_stream *u; file_stream *u;
@ -334,6 +344,7 @@ int pa_play_file(
u->sink_input->process_rewind = sink_input_process_rewind_cb; u->sink_input->process_rewind = sink_input_process_rewind_cb;
u->sink_input->update_max_rewind = sink_input_update_max_rewind_cb; u->sink_input->update_max_rewind = sink_input_update_max_rewind_cb;
u->sink_input->kill = sink_input_kill_cb; u->sink_input->kill = sink_input_kill_cb;
u->sink_input->state_change = sink_input_state_change_cb;
u->sink_input->userdata = u; u->sink_input->userdata = u;
pa_sink_input_get_silence(u->sink_input, &silence); pa_sink_input_get_silence(u->sink_input, &silence);

View file

@ -88,6 +88,7 @@ static void reset_callbacks(pa_source_output *o) {
o->moved = NULL; o->moved = NULL;
o->kill = NULL; o->kill = NULL;
o->get_latency = NULL; o->get_latency = NULL;
o->state_change = NULL;
} }
pa_source_output* pa_source_output_new( 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); pa_source_output_ref(o);
linked = PA_SOURCE_OUTPUT_LINKED(o->state); linked = PA_SOURCE_OUTPUT_IS_LINKED(o->state);
if (linked) if (linked)
pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK], o); 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); 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_source_output_unlink(o);
pa_log_info("Freeing output %u \"%s\"", o->index, pa_strnull(pa_proplist_gets(o->proplist, PA_PROP_MEDIA_NAME))); 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) { void pa_source_output_kill(pa_source_output*o) {
pa_source_output_assert_ref(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) if (o->kill)
o->kill(o); 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_usec_t r = 0;
pa_source_output_assert_ref(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 (pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_GET_LATENCY, &r, 0, NULL) < 0) if (pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_GET_LATENCY, &r, 0, NULL) < 0)
r = 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; size_t limit, mbs = 0;
pa_source_output_assert_ref(o); 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(chunk);
pa_assert(pa_frame_aligned(chunk->length, &o->source->sample_spec)); 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 */) { void pa_source_output_process_rewind(pa_source_output *o, size_t nbytes /* in sink sample spec */) {
pa_source_output_assert_ref(o); 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)); pa_assert(pa_frame_aligned(nbytes, &o->source->sample_spec));
if (nbytes <= 0) 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 */ /* Called from thread context */
void pa_source_output_update_max_rewind(pa_source_output *o, size_t nbytes /* in the source's sample spec */) { 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_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)); pa_assert(pa_frame_aligned(nbytes, &o->source->sample_spec));
if (o->update_max_rewind) if (o->update_max_rewind)
o->update_max_rewind(o, o->thread_info.resampler ? pa_resampler_result(o->thread_info.resampler, nbytes) : nbytes); o->update_max_rewind(o, o->thread_info.resampler ? pa_resampler_result(o->thread_info.resampler, nbytes) : nbytes);
} }
static pa_usec_t fixup_latency(pa_source *s, pa_usec_t usec) {
pa_source_assert_ref(s);
if (usec == (pa_usec_t) -1)
return usec;
if (s->max_latency > 0 && usec > s->max_latency)
usec = s->max_latency;
if (s->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) {
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_usec_t pa_source_output_set_requested_latency(pa_source_output *o, pa_usec_t usec) {
pa_source_output_assert_ref(o); pa_source_output_assert_ref(o);
if (usec != (pa_usec_t) -1) { usec = fixup_latency(o->source, usec);
if (o->source->max_latency > 0 && usec > o->source->max_latency) if (PA_SOURCE_OUTPUT_IS_LINKED(o->state))
usec = o->source->max_latency;
if (o->source->min_latency > 0 && usec < o->source->min_latency)
usec = o->source->min_latency;
}
if (PA_SOURCE_OUTPUT_LINKED(o->state))
pa_asyncmsgq_post(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_SET_REQUESTED_LATENCY, NULL, (int64_t) usec, NULL, NULL); pa_asyncmsgq_post(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_SET_REQUESTED_LATENCY, NULL, (int64_t) usec, NULL, NULL);
else { 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->thread_info.requested_source_latency = usec;
o->source->thread_info.requested_latency_valid = FALSE; 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) { void pa_source_output_cork(pa_source_output *o, pa_bool_t b) {
pa_source_output_assert_ref(o); 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); 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) { int pa_source_output_set_rate(pa_source_output *o, uint32_t rate) {
pa_source_output_assert_ref(o); 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); pa_return_val_if_fail(o->thread_info.resampler, -1);
if (o->sample_spec.rate == rate) if (o->sample_spec.rate == rate)
@ -515,7 +536,7 @@ void pa_source_output_set_name(pa_source_output *o, const char *name) {
else else
pa_proplist_unset(o->proplist, PA_PROP_MEDIA_NAME); 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_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); 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_move_hook_data hook_data;
pa_source_output_assert_ref(o); 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); pa_source_assert_ref(dest);
origin = o->source; origin = o->source;
@ -616,12 +637,21 @@ int pa_source_output_move_to(pa_source_output *o, pa_source *dest) {
return 0; 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 */ /* Called from thread context */
int pa_source_output_process_msg(pa_msgobject *mo, int code, void *userdata, int64_t offset, pa_memchunk* chunk) { 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 *o = PA_SOURCE_OUTPUT(mo);
pa_source_output_assert_ref(o); 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) { switch (code) {
@ -633,25 +663,20 @@ int pa_source_output_process_msg(pa_msgobject *mo, int code, void *userdata, int
return 0; 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); 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)); pa_resampler_set_output_rate(o->thread_info.resampler, PA_PTR_TO_UINT(userdata));
return 0; return 0;
}
case PA_SOURCE_OUTPUT_MESSAGE_SET_STATE: { case PA_SOURCE_OUTPUT_MESSAGE_SET_STATE:
o->thread_info.state = PA_PTR_TO_UINT(userdata);
pa_source_output_set_state_within_thread(o, PA_PTR_TO_UINT(userdata));
return 0; return 0;
}
case PA_SOURCE_OUTPUT_MESSAGE_SET_REQUESTED_LATENCY: case PA_SOURCE_OUTPUT_MESSAGE_SET_REQUESTED_LATENCY:
o->thread_info.requested_source_latency = (pa_usec_t) offset; pa_source_output_set_requested_latency_within_thread(o, (pa_usec_t) offset);
pa_source_invalidate_requested_latency(o->source);
return 0; return 0;
} }

View file

@ -42,7 +42,7 @@ typedef enum pa_source_output_state {
PA_SOURCE_OUTPUT_UNLINKED PA_SOURCE_OUTPUT_UNLINKED
} pa_source_output_state_t; } 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; 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); void (*push)(pa_source_output *o, const pa_memchunk *chunk);
/* Only relevant for monitor sources right now: called when the /* 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); void (*process_rewind)(pa_source_output *o, size_t nbytes);
/* Called whenever the maximum rewindable size of the source /* 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 */ 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 /* If non-NULL this function is called when the output is first
@ -116,6 +116,10 @@ struct pa_source_output {
thread instead. */ thread instead. */
pa_usec_t (*get_latency) (pa_source_output *o); /* may be NULL */ 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 { struct {
pa_source_output_state_t state; 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); 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 #endif

View file

@ -228,8 +228,8 @@ static int source_set_state(pa_source *s, pa_source_state_t state) {
return 0; return 0;
suspend_change = suspend_change =
(s->state == PA_SOURCE_SUSPENDED && PA_SOURCE_OPENED(state)) || (s->state == PA_SOURCE_SUSPENDED && PA_SOURCE_IS_OPENED(state)) ||
(PA_SOURCE_OPENED(s->state) && state == PA_SOURCE_SUSPENDED); (PA_SOURCE_IS_OPENED(s->state) && state == PA_SOURCE_SUSPENDED);
if (s->set_state) if (s->set_state)
if ((ret = s->set_state(s, state)) < 0) 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 /* See pa_sink_unlink() for a couple of comments how this function
* works. */ * works. */
linked = PA_SOURCE_LINKED(s->state); linked = PA_SOURCE_IS_LINKED(s->state);
if (linked) if (linked)
pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK], s); 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(s);
pa_assert(pa_source_refcnt(s) == 0); 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_source_unlink(s);
pa_log_info("Freeing source %u \"%s\"", s->index, s->name); 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) { void pa_source_set_asyncmsgq(pa_source *s, pa_asyncmsgq *q) {
pa_source_assert_ref(s); pa_source_assert_ref(s);
pa_assert(q);
s->asyncmsgq = q; s->asyncmsgq = q;
} }
void pa_source_set_rtpoll(pa_source *s, pa_rtpoll *p) { void pa_source_set_rtpoll(pa_source *s, pa_rtpoll *p) {
pa_source_assert_ref(s); pa_source_assert_ref(s);
pa_assert(p);
s->rtpoll = p; s->rtpoll = p;
} }
int pa_source_update_status(pa_source*s) { int pa_source_update_status(pa_source*s) {
pa_source_assert_ref(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) if (s->state == PA_SOURCE_SUSPENDED)
return 0; 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) { int pa_source_suspend(pa_source *s, pa_bool_t suspend) {
pa_source_assert_ref(s); pa_source_assert_ref(s);
pa_assert(PA_SOURCE_LINKED(s->state)); pa_assert(PA_SOURCE_IS_LINKED(s->state));
if (suspend) if (suspend)
return source_set_state(s, PA_SOURCE_SUSPENDED); 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; void *state = NULL;
pa_source_assert_ref(s); 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) if (nbytes <= 0)
return; return;
@ -400,7 +398,7 @@ void pa_source_post(pa_source*s, const pa_memchunk *chunk) {
void *state = NULL; void *state = NULL;
pa_source_assert_ref(s); 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); pa_assert(chunk);
if (s->thread_info.state != PA_SOURCE_RUNNING) 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_usec_t usec;
pa_source_assert_ref(s); 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; return 0;
if (s->get_latency) if (s->get_latency)
@ -454,7 +452,7 @@ void pa_source_set_volume(pa_source *s, const pa_cvolume *volume) {
int changed; int changed;
pa_source_assert_ref(s); pa_source_assert_ref(s);
pa_assert(PA_SOURCE_LINKED(s->state)); pa_assert(PA_SOURCE_IS_LINKED(s->state));
pa_assert(volume); pa_assert(volume);
changed = !pa_cvolume_equal(volume, &s->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_cvolume old_volume;
pa_source_assert_ref(s); pa_source_assert_ref(s);
pa_assert(PA_SOURCE_LINKED(s->state)); pa_assert(PA_SOURCE_IS_LINKED(s->state));
old_volume = s->volume; old_volume = s->volume;
@ -494,7 +492,7 @@ void pa_source_set_mute(pa_source *s, pa_bool_t mute) {
int changed; int changed;
pa_source_assert_ref(s); pa_source_assert_ref(s);
pa_assert(PA_SOURCE_LINKED(s->state)); pa_assert(PA_SOURCE_IS_LINKED(s->state));
changed = s->muted != mute; changed = s->muted != mute;
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_bool_t old_muted;
pa_source_assert_ref(s); pa_source_assert_ref(s);
pa_assert(PA_SOURCE_LINKED(s->state)); pa_assert(PA_SOURCE_IS_LINKED(s->state));
old_muted = s->muted; old_muted = s->muted;
@ -546,7 +544,7 @@ void pa_source_set_description(pa_source *s, const char *description) {
else else
pa_proplist_unset(s->proplist, PA_PROP_DEVICE_DESCRIPTION); 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_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); 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) { unsigned pa_source_linked_by(pa_source *s) {
pa_source_assert_ref(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); return pa_idxset_size(s->outputs);
} }
@ -563,7 +561,7 @@ unsigned pa_source_used_by(pa_source *s) {
unsigned ret; unsigned ret;
pa_source_assert_ref(s); 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); ret = pa_idxset_size(s->outputs);
pa_assert(ret >= s->n_corked); 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) if (o->attach)
o->attach(o); o->attach(o);
pa_source_output_set_state_within_thread(o, o->state);
pa_source_invalidate_requested_latency(s); pa_source_invalidate_requested_latency(s);
return 0; 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: { case PA_SOURCE_MESSAGE_REMOVE_OUTPUT: {
pa_source_output *o = PA_SOURCE_OUTPUT(userdata); pa_source_output *o = PA_SOURCE_OUTPUT(userdata);
pa_source_output_set_state_within_thread(o, o->state);
if (o->detach) if (o->detach)
o->detach(o); 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) { void pa_source_detach(pa_source *s) {
pa_source_assert_ref(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); pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_DETACH, NULL, 0, NULL);
} }
void pa_source_attach(pa_source *s) { void pa_source_attach(pa_source *s) {
pa_source_assert_ref(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); 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; void *state = NULL;
pa_source_assert_ref(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));
while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL))) while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL)))
if (o->detach) if (o->detach)
@ -705,7 +707,7 @@ void pa_source_attach_within_thread(pa_source *s) {
void *state = NULL; void *state = NULL;
pa_source_assert_ref(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));
while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL))) while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL)))
if (o->attach) if (o->attach)
@ -746,9 +748,9 @@ pa_usec_t pa_source_get_requested_latency(pa_source *s) {
pa_usec_t usec; pa_usec_t usec;
pa_source_assert_ref(s); 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; return 0;
if (pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_REQUESTED_LATENCY, &usec, 0, NULL) < 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) { void pa_source_invalidate_requested_latency(pa_source *s) {
pa_source_assert_ref(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) if (!s->thread_info.requested_latency_valid)
return; return;

View file

@ -33,7 +33,6 @@ typedef struct pa_source pa_source;
#include <pulse/channelmap.h> #include <pulse/channelmap.h>
#include <pulse/volume.h> #include <pulse/volume.h>
#include <pulsecore/core-def.h>
#include <pulsecore/core.h> #include <pulsecore/core.h>
#include <pulsecore/idxset.h> #include <pulsecore/idxset.h>
#include <pulsecore/memblock.h> #include <pulsecore/memblock.h>
@ -54,11 +53,11 @@ typedef enum pa_source_state {
PA_SOURCE_UNLINKED PA_SOURCE_UNLINKED
} pa_source_state_t; } 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; 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; return x == PA_SOURCE_RUNNING || x == PA_SOURCE_IDLE || x == PA_SOURCE_SUSPENDED;
} }

View file

@ -36,6 +36,7 @@
#include <pulse/error.h> #include <pulse/error.h>
#include <pulse/util.h> #include <pulse/util.h>
#include <pulse/xmalloc.h>
#include <pulsecore/core-util.h> #include <pulsecore/core-util.h>
#include <pulsecore/log.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]; char ibuf[256], obuf[256];
size_t ibuf_index, ibuf_length, obuf_index, obuf_length; size_t ibuf_index, ibuf_length, obuf_index, obuf_length;
fd_set ifds, ofds; fd_set ifds, ofds;
char *cli;
if (pa_pid_file_check_running(&pid, "pulseaudio") < 0) { if (pa_pid_file_check_running(&pid, "pulseaudio") < 0) {
pa_log("no PulseAudio daemon running"); 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)); memset(&sa, 0, sizeof(sa));
sa.sun_family = AF_UNIX; 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++) { for (i = 0; i < 5; i++) {
int r; int r;