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