diff options
Diffstat (limited to 'src/pulsecore')
-rw-r--r-- | src/pulsecore/cli-command.c | 104 | ||||
-rw-r--r-- | src/pulsecore/conf-parser.c | 18 | ||||
-rw-r--r-- | src/pulsecore/conf-parser.h | 1 | ||||
-rw-r--r-- | src/pulsecore/core.h | 2 | ||||
-rw-r--r-- | src/pulsecore/log.c | 133 | ||||
-rw-r--r-- | src/pulsecore/log.h | 7 | ||||
-rw-r--r-- | src/pulsecore/macro.h | 12 | ||||
-rw-r--r-- | src/pulsecore/module.c | 10 | ||||
-rw-r--r-- | src/pulsecore/module.h | 6 | ||||
-rw-r--r-- | src/pulsecore/once.c | 11 | ||||
-rw-r--r-- | src/pulsecore/once.h | 2 | ||||
-rw-r--r-- | src/pulsecore/pid.c | 11 | ||||
-rw-r--r-- | src/pulsecore/proplist-util.c | 35 | ||||
-rw-r--r-- | src/pulsecore/protocol-native.c | 44 | ||||
-rw-r--r-- | src/pulsecore/resampler.c | 96 | ||||
-rw-r--r-- | src/pulsecore/sample-util.c | 2 | ||||
-rw-r--r-- | src/pulsecore/sample-util.h | 2 | ||||
-rw-r--r-- | src/pulsecore/shm.c | 8 | ||||
-rw-r--r-- | src/pulsecore/sink-input.c | 144 | ||||
-rw-r--r-- | src/pulsecore/sink-input.h | 22 | ||||
-rw-r--r-- | src/pulsecore/sink.c | 54 | ||||
-rw-r--r-- | src/pulsecore/sink.h | 6 | ||||
-rw-r--r-- | src/pulsecore/source-output.c | 50 | ||||
-rw-r--r-- | src/pulsecore/source-output.h | 11 | ||||
-rw-r--r-- | src/pulsecore/source.c | 31 | ||||
-rw-r--r-- | src/pulsecore/source.h | 1 | ||||
-rw-r--r-- | src/pulsecore/time-smoother.c | 6 |
27 files changed, 703 insertions, 126 deletions
diff --git a/src/pulsecore/cli-command.c b/src/pulsecore/cli-command.c index 1624165d..b5ff98db 100644 --- a/src/pulsecore/cli-command.c +++ b/src/pulsecore/cli-command.c @@ -117,6 +117,10 @@ static int pa_cli_command_vacuum(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa static int pa_cli_command_suspend_sink(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail); static int pa_cli_command_suspend_source(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail); static int pa_cli_command_suspend(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail); +static int pa_cli_command_log_level(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail); +static int pa_cli_command_log_meta(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail); +static int pa_cli_command_log_time(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail); +static int pa_cli_command_log_backtrace(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail); /* A method table for all available commands */ @@ -167,6 +171,10 @@ static const struct command commands[] = { { "suspend-sink", pa_cli_command_suspend_sink, "Suspend sink (args: index|name, bool)", 3}, { "suspend-source", pa_cli_command_suspend_source, "Suspend source (args: index|name, bool)", 3}, { "suspend", pa_cli_command_suspend, "Suspend all sinks and all sources (args: bool)", 2}, + { "set-log-level", pa_cli_command_log_level, "Change the log level (args: numeric level)", 2}, + { "set-log-meta", pa_cli_command_log_meta, "Show source code location in log messages (args: bool)", 2}, + { "set-log-time", pa_cli_command_log_time, "Show timestamps in log messages (args: bool)", 2}, + { "set-log-backtrace", pa_cli_command_log_backtrace, "Show bakctrace in log messages (args: frames)", 2}, { NULL, NULL, NULL, 0 } }; @@ -1203,6 +1211,102 @@ static int pa_cli_command_suspend(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, p return 0; } +static int pa_cli_command_log_level(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) { + const char *m; + uint32_t level; + + pa_core_assert_ref(c); + pa_assert(t); + pa_assert(buf); + pa_assert(fail); + + if (!(m = pa_tokenizer_get(t, 1))) { + pa_strbuf_puts(buf, "You need to specify a log level (0..4).\n"); + return -1; + } + + if (pa_atou(m, &level) < 0 || level >= PA_LOG_LEVEL_MAX) { + pa_strbuf_puts(buf, "Failed to parse log level.\n"); + return -1; + } + + pa_log_set_maximal_level(level); + + return 0; +} + +static int pa_cli_command_log_meta(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) { + const char *m; + pa_bool_t b; + + pa_core_assert_ref(c); + pa_assert(t); + pa_assert(buf); + pa_assert(fail); + + if (!(m = pa_tokenizer_get(t, 1))) { + pa_strbuf_puts(buf, "You need to specify a boolean.\n"); + return -1; + } + + if ((b = pa_parse_boolean(m)) < 0) { + pa_strbuf_puts(buf, "Failed to parse log meta switch.\n"); + return -1; + } + + pa_log_set_show_meta(b); + + return 0; +} + +static int pa_cli_command_log_time(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) { + const char *m; + pa_bool_t b; + + pa_core_assert_ref(c); + pa_assert(t); + pa_assert(buf); + pa_assert(fail); + + if (!(m = pa_tokenizer_get(t, 1))) { + pa_strbuf_puts(buf, "You need to specify a boolean.\n"); + return -1; + } + + if ((b = pa_parse_boolean(m)) < 0) { + pa_strbuf_puts(buf, "Failed to parse log meta switch.\n"); + return -1; + } + + pa_log_set_show_time(b); + + return 0; +} + +static int pa_cli_command_log_backtrace(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) { + const char *m; + uint32_t nframes; + + pa_core_assert_ref(c); + pa_assert(t); + pa_assert(buf); + pa_assert(fail); + + if (!(m = pa_tokenizer_get(t, 1))) { + pa_strbuf_puts(buf, "You need to specify a backtrace level.\n"); + return -1; + } + + if (pa_atou(m, &nframes) < 0 || nframes >= 1000) { + pa_strbuf_puts(buf, "Failed to parse backtrace level.\n"); + return -1; + } + + pa_log_set_show_backtrace(nframes); + + return 0; +} + static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) { pa_module *m; pa_sink *sink; diff --git a/src/pulsecore/conf-parser.c b/src/pulsecore/conf-parser.c index 58ceab91..ef6d6bb6 100644 --- a/src/pulsecore/conf-parser.c +++ b/src/pulsecore/conf-parser.c @@ -166,6 +166,24 @@ int pa_config_parse_int(const char *filename, unsigned line, const char *lvalue, return 0; } +int pa_config_parse_unsigned(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata) { + unsigned *u = data; + uint32_t k; + + pa_assert(filename); + pa_assert(lvalue); + pa_assert(rvalue); + pa_assert(data); + + if (pa_atou(rvalue, &k) < 0) { + pa_log("[%s:%u] Failed to parse numeric value: %s", filename, line, rvalue); + return -1; + } + + *u = (unsigned) k; + return 0; +} + int pa_config_parse_size(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata) { size_t *i = data; uint32_t k; diff --git a/src/pulsecore/conf-parser.h b/src/pulsecore/conf-parser.h index a5174fce..48a0fd26 100644 --- a/src/pulsecore/conf-parser.h +++ b/src/pulsecore/conf-parser.h @@ -41,6 +41,7 @@ int pa_config_parse(const char *filename, FILE *f, const pa_config_item *t, void /* Generic parsers for integers, size_t, booleans and strings */ int pa_config_parse_int(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata); +int pa_config_parse_unsigned(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata); int pa_config_parse_size(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata); int pa_config_parse_bool(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata); int pa_config_parse_string(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata); diff --git a/src/pulsecore/core.h b/src/pulsecore/core.h index 39559082..f796fb93 100644 --- a/src/pulsecore/core.h +++ b/src/pulsecore/core.h @@ -49,6 +49,7 @@ typedef enum pa_core_hook { PA_CORE_HOOK_SINK_UNLINK_POST, PA_CORE_HOOK_SINK_STATE_CHANGED, PA_CORE_HOOK_SINK_PROPLIST_CHANGED, + PA_CORE_HOOK_SINK_SET_VOLUME, PA_CORE_HOOK_SOURCE_NEW, PA_CORE_HOOK_SOURCE_FIXATE, PA_CORE_HOOK_SOURCE_PUT, @@ -65,6 +66,7 @@ typedef enum pa_core_hook { PA_CORE_HOOK_SINK_INPUT_MOVE_POST, PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED, PA_CORE_HOOK_SINK_INPUT_PROPLIST_CHANGED, + PA_CORE_HOOK_SINK_INPUT_SET_VOLUME, PA_CORE_HOOK_SOURCE_OUTPUT_NEW, PA_CORE_HOOK_SOURCE_OUTPUT_FIXATE, PA_CORE_HOOK_SOURCE_OUTPUT_PUT, diff --git a/src/pulsecore/log.c b/src/pulsecore/log.c index b1de6966..adf2f112 100644 --- a/src/pulsecore/log.c +++ b/src/pulsecore/log.c @@ -30,6 +30,10 @@ #include <string.h> #include <errno.h> +#ifdef HAVE_EXECINFO_H +#include <execinfo.h> +#endif + #ifdef HAVE_SYSLOG_H #include <syslog.h> #endif @@ -49,11 +53,15 @@ #define ENV_LOGLEVEL "PULSE_LOG" #define ENV_LOGMETA "PULSE_LOG_META" #define ENV_LOGTIME "PULSE_LOG_TIME" +#define ENV_LOGBACKTRACE "PULSE_LOG_BACKTRACE" static char *log_ident = NULL, *log_ident_local = NULL; static pa_log_target_t log_target = PA_LOG_STDERR; static pa_log_func_t user_log_func = NULL; static pa_log_level_t maximal_level = PA_LOG_ERROR; +static unsigned show_backtrace = 0; +static pa_bool_t show_meta = FALSE; +static pa_bool_t show_time = FALSE; #ifdef HAVE_SYSLOG_H static const int level_to_syslog[] = { @@ -105,6 +113,74 @@ void pa_log_set_target(pa_log_target_t t, pa_log_func_t func) { user_log_func = func; } +void pa_log_set_show_meta(pa_bool_t b) { + show_meta = b; +} + +void pa_log_set_show_time(pa_bool_t b) { + show_time = b; +} + +void pa_log_set_show_backtrace(unsigned nlevels) { + show_backtrace = nlevels; +} + +#ifdef HAVE_EXECINFO_H + +static char* get_backtrace(unsigned show_nframes) { + void* trace[32]; + int n_frames; + char **symbols, *e, *r; + unsigned j, n; + size_t a; + + if (show_nframes <= 0) + return NULL; + + n_frames = backtrace(trace, PA_ELEMENTSOF(trace)); + + if (n_frames <= 0) + return NULL; + + symbols = backtrace_symbols(trace, n_frames); + + if (!symbols) + return NULL; + + n = PA_MIN((unsigned) n_frames, show_nframes); + + a = 4; + + for (j = 0; j < n; j++) { + if (j > 0) + a += 2; + a += strlen(symbols[j]); + } + + r = pa_xnew(char, a); + + strcpy(r, " ("); + e = r + 2; + + for (j = 0; j < n; j++) { + if (j > 0) { + strcpy(e, "<<"); + e += 2; + } + + strcpy(e, symbols[j]); + e += strlen(symbols[j]); + } + + strcpy(e, ")"); + + free(symbols); + + return r; +} + +#endif + void pa_log_levelv_meta( pa_log_level_t level, const char*file, @@ -116,32 +192,43 @@ void pa_log_levelv_meta( const char *e; char *t, *n; int saved_errno = errno; + char *bt = NULL; + pa_log_level_t ml; +#ifdef HAVE_EXECINFO_H + unsigned show_bt; +#endif /* We don't use dynamic memory allocation here to minimize the hit * in RT threads */ - char text[1024], location[128], timestamp[32]; + char text[4096], location[128], timestamp[32]; pa_assert(level < PA_LOG_LEVEL_MAX); pa_assert(format); - if ((e = getenv(ENV_LOGLEVEL))) - maximal_level = atoi(e); + ml = maximal_level; + + if (PA_UNLIKELY((e = getenv(ENV_LOGLEVEL)))) { + pa_log_level_t eml = (pa_log_level_t) atoi(e); - if (level > maximal_level) { + if (eml > ml) + ml = eml; + } + + if (PA_LIKELY(level > ml)) { errno = saved_errno; return; } pa_vsnprintf(text, sizeof(text), format, ap); - if (getenv(ENV_LOGMETA) && file && line > 0 && func) + if ((show_meta || getenv(ENV_LOGMETA)) && file && line > 0 && func) pa_snprintf(location, sizeof(location), "[%s:%i %s()] ", file, line, func); else if (file) pa_snprintf(location, sizeof(location), "%s: ", pa_path_get_filename(file)); else location[0] = 0; - if (getenv(ENV_LOGTIME)) { + if (show_time || getenv(ENV_LOGTIME)) { static pa_usec_t start, last; pa_usec_t u, a, r; @@ -168,6 +255,19 @@ void pa_log_levelv_meta( } else timestamp[0] = 0; +#ifdef HAVE_EXECINFO_H + show_bt = show_backtrace; + + if ((e = getenv(ENV_LOGBACKTRACE))) { + unsigned ebt = (unsigned) atoi(e); + + if (ebt > show_bt) + show_bt = ebt; + } + + bt = get_backtrace(show_bt); +#endif + if (!pa_utf8_valid(text)) pa_log_level(level, __FILE__": invalid UTF-8 string following below:"); @@ -182,19 +282,22 @@ void pa_log_levelv_meta( switch (log_target) { case PA_LOG_STDERR: { - const char *prefix = "", *suffix = ""; + const char *prefix = "", *suffix = "", *grey = ""; char *local_t; #ifndef OS_IS_WIN32 /* Yes indeed. Useless, but fun! */ if (isatty(STDERR_FILENO)) { - if (level <= PA_LOG_ERROR) { + if (level <= PA_LOG_ERROR) prefix = "\x1B[1;31m"; - suffix = "\x1B[0m"; - } else if (level <= PA_LOG_WARN) { + else if (level <= PA_LOG_WARN) prefix = "\x1B[1m"; + + if (bt) + grey = "\x1B[2m"; + + if (grey[0] || prefix[0]) suffix = "\x1B[0m"; - } } #endif @@ -202,9 +305,9 @@ void pa_log_levelv_meta( * minimize the hit in RT threads */ local_t = pa_utf8_to_locale(t); if (!local_t) - fprintf(stderr, "%s%c: %s%s%s%s\n", timestamp, level_to_char[level], location, prefix, t, suffix); + fprintf(stderr, "%s%c: %s%s%s%s%s%s\n", timestamp, level_to_char[level], location, prefix, t, grey, pa_strempty(bt), suffix); else { - fprintf(stderr, "%s%c: %s%s%s%s\n", timestamp, level_to_char[level], location, prefix, local_t, suffix); + fprintf(stderr, "%s%c: %s%s%s%s%s%s\n", timestamp, level_to_char[level], location, prefix, local_t, grey, pa_strempty(bt), suffix); pa_xfree(local_t); } @@ -219,9 +322,9 @@ void pa_log_levelv_meta( local_t = pa_utf8_to_locale(t); if (!local_t) - syslog(level_to_syslog[level], "%s%s%s", timestamp, location, t); + syslog(level_to_syslog[level], "%s%s%s%s", timestamp, location, t, pa_strempty(bt)); else { - syslog(level_to_syslog[level], "%s%s%s", timestamp, location, local_t); + syslog(level_to_syslog[level], "%s%s%s%s", timestamp, location, local_t, pa_strempty(bt)); pa_xfree(local_t); } diff --git a/src/pulsecore/log.h b/src/pulsecore/log.h index 633227f3..3d66e903 100644 --- a/src/pulsecore/log.h +++ b/src/pulsecore/log.h @@ -25,6 +25,8 @@ #include <stdarg.h> #include <stdlib.h> + +#include <pulsecore/macro.h> #include <pulse/gccmacro.h> /* A simple logging subsystem */ @@ -54,8 +56,11 @@ typedef void (*pa_log_func_t)(pa_log_level_t t, const char*s); /* Set another log target. If t is PA_LOG_USER you may specify a function that is called every log string */ void pa_log_set_target(pa_log_target_t t, pa_log_func_t func); -/* Minimal log level */ +/* Maximal log level */ void pa_log_set_maximal_level(pa_log_level_t l); +void pa_log_set_show_meta(pa_bool_t b); +void pa_log_set_show_time(pa_bool_t b); +void pa_log_set_show_backtrace(unsigned nlevels); void pa_log_level_meta( pa_log_level_t level, diff --git a/src/pulsecore/macro.h b/src/pulsecore/macro.h index 39e9b587..f9ce949a 100644 --- a/src/pulsecore/macro.h +++ b/src/pulsecore/macro.h @@ -30,7 +30,6 @@ #include <stdio.h> #include <stdlib.h> -#include <pulsecore/log.h> #include <pulse/gccmacro.h> #ifndef PACKAGE @@ -40,7 +39,7 @@ #ifndef PA_LIKELY #ifdef __GNUC__ #define PA_LIKELY(x) (__builtin_expect(!!(x),1)) -#define PA_UNLIKELY(x) (__builtin_expect((x),0)) +#define PA_UNLIKELY(x) (__builtin_expect(!!(x),0)) #else #define PA_LIKELY(x) (x) #define PA_UNLIKELY(x) (x) @@ -221,4 +220,13 @@ typedef int pa_bool_t; #endif +#if defined(__i386__) || defined(__x86_64__) +#define PA_DEBUG_TRAP __asm__("int $3") +#else +#define PA_DEBUG_TRAP raise(SIGTRAP) +#endif + +/* We include this at the very last place */ +#include <pulsecore/log.h> + #endif diff --git a/src/pulsecore/module.c b/src/pulsecore/module.c index 9b17cb91..56ed2c5d 100644 --- a/src/pulsecore/module.c +++ b/src/pulsecore/module.c @@ -280,6 +280,16 @@ void pa_module_unload_request(pa_module *m, pa_bool_t force) { m->core->mainloop->defer_enable(m->core->module_defer_unload_event, 1); } +void pa_module_unload_request_by_index(pa_core *c, uint32_t idx, pa_bool_t force) { + pa_module *m; + pa_assert(c); + + if (!(m = pa_idxset_get_by_index(c->modules, idx))) + return; + + pa_module_unload_request(m, force); +} + void pa_module_set_used(pa_module*m, int used) { pa_assert(m); diff --git a/src/pulsecore/module.h b/src/pulsecore/module.h index 365ab67e..661b2dd6 100644 --- a/src/pulsecore/module.h +++ b/src/pulsecore/module.h @@ -52,14 +52,16 @@ struct pa_module { }; pa_module* pa_module_load(pa_core *c, const char *name, const char*argument); + void pa_module_unload(pa_core *c, pa_module *m, pa_bool_t force); void pa_module_unload_by_index(pa_core *c, uint32_t idx, pa_bool_t force); +void pa_module_unload_request(pa_module *m, pa_bool_t force); +void pa_module_unload_request_by_index(pa_core *c, uint32_t idx, pa_bool_t force); + void pa_module_unload_all(pa_core *c); void pa_module_unload_unused(pa_core *c); -void pa_module_unload_request(pa_module *m, pa_bool_t force); - void pa_module_set_used(pa_module*m, int used); #define PA_MODULE_AUTHOR(s) \ diff --git a/src/pulsecore/once.c b/src/pulsecore/once.c index 989741dc..3d4543cb 100644 --- a/src/pulsecore/once.c +++ b/src/pulsecore/once.c @@ -28,13 +28,13 @@ #include "once.h" -int pa_once_begin(pa_once *control) { +pa_bool_t pa_once_begin(pa_once *control) { pa_mutex *m; pa_assert(control); if (pa_atomic_load(&control->done)) - return 0; + return FALSE; pa_atomic_inc(&control->ref); @@ -50,15 +50,17 @@ int pa_once_begin(pa_once *control) { * wait until it is unlocked */ pa_mutex_lock(m); + pa_assert(pa_atomic_load(&control->done)); + pa_once_end(control); - return 0; + return FALSE; } pa_assert_se(m = pa_mutex_new(FALSE, FALSE)); pa_mutex_lock(m); if (pa_atomic_ptr_cmpxchg(&control->mutex, NULL, m)) - return 1; + return TRUE; pa_mutex_unlock(m); pa_mutex_free(m); @@ -91,4 +93,3 @@ void pa_run_once(pa_once *control, pa_once_func_t func) { pa_once_end(control); } } - diff --git a/src/pulsecore/once.h b/src/pulsecore/once.h index 576d40fa..c0191ef0 100644 --- a/src/pulsecore/once.h +++ b/src/pulsecore/once.h @@ -38,7 +38,7 @@ typedef struct pa_once { } /* Not to be called directly, use the macros defined below instead */ -int pa_once_begin(pa_once *o); +pa_bool_t pa_once_begin(pa_once *o); void pa_once_end(pa_once *o); #define PA_ONCE_BEGIN \ diff --git a/src/pulsecore/pid.c b/src/pulsecore/pid.c index ce8ef19b..bf9ba983 100644 --- a/src/pulsecore/pid.c +++ b/src/pulsecore/pid.c @@ -171,14 +171,14 @@ static int proc_name_ours(pid_t pid, const char *procname) { good = pa_startswith(stored, expected); pa_xfree(expected); -#if !defined(__OPTIMIZE__) +/*#if !defined(__OPTIMIZE__)*/ if (!good) { /* libtool likes to rename our binary names ... */ expected = pa_sprintf_malloc("%lu (lt-%s)", (unsigned long) pid, procname); good = pa_startswith(stored, expected); pa_xfree(expected); } -#endif +/*#endif*/ return !!good; } @@ -211,6 +211,7 @@ int pa_pid_file_create(const char *procname) { if ((pid = read_pid(fn, fd)) == (pid_t) -1) pa_log_warn("Corrupt PID file, overwriting."); else if (pid > 0) { + int ours = 1; #ifdef OS_IS_WIN32 if ((process = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid)) != NULL) { @@ -218,11 +219,13 @@ int pa_pid_file_create(const char *procname) { #else if (kill(pid, 0) >= 0 || errno != ESRCH) { #endif - int ours = 1; if (procname) - if ((ours = proc_name_ours(pid, procname)) < 0) + if ((ours = proc_name_ours(pid, procname)) < 0) { + pa_log_warn("Could not check to see if pid %lu is a pulseaudio process. " + "Asssuming it is and the daemon is already running.", (unsigned long) pid); goto fail; + } if (ours) { pa_log("Daemon already running."); diff --git a/src/pulsecore/proplist-util.c b/src/pulsecore/proplist-util.c index 4d505f57..35c9985a 100644 --- a/src/pulsecore/proplist-util.c +++ b/src/pulsecore/proplist-util.c @@ -44,27 +44,34 @@ void pa_init_proplist(pa_proplist *p) { pa_assert(p); - for (e = environ; *e; e++) { + if (environ) { - if (pa_startswith(*e, "PULSE_PROP_")) { - size_t kl = strcspn(*e+11, "="); - char *k; + /* Some applications seem to reset environ to NULL for various + * reasons, hence we need to check for this explicitly. See + * rhbz #473080 */ - if ((*e)[11+kl] != '=') - continue; + for (e = environ; *e; e++) { - if (!pa_utf8_valid(*e+11+kl+1)) - continue; + if (pa_startswith(*e, "PULSE_PROP_")) { + size_t kl = strcspn(*e+11, "="); + char *k; - k = pa_xstrndup(*e+11, kl); + if ((*e)[11+kl] != '=') + continue; - if (pa_proplist_contains(p, k)) { + if (!pa_utf8_valid(*e+11+kl+1)) + continue; + + k = pa_xstrndup(*e+11, kl); + + if (pa_proplist_contains(p, k)) { + pa_xfree(k); + continue; + } + + pa_proplist_sets(p, k, *e+11+kl+1); pa_xfree(k); - continue; } - - pa_proplist_sets(p, k, *e+11+kl+1); - pa_xfree(k); } } diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c index 778aab57..56e86cb4 100644 --- a/src/pulsecore/protocol-native.c +++ b/src/pulsecore/protocol-native.c @@ -959,6 +959,7 @@ static playback_stream* playback_stream_new( uint32_t *minreq, pa_cvolume *volume, pa_bool_t muted, + pa_bool_t muted_set, uint32_t syncid, uint32_t *missing, pa_sink_input_flags_t flags, @@ -1013,7 +1014,8 @@ static playback_stream* playback_stream_new( pa_sink_input_new_data_set_channel_map(&data, map); if (volume) pa_sink_input_new_data_set_volume(&data, volume); - pa_sink_input_new_data_set_muted(&data, muted); + if (muted_set) + pa_sink_input_new_data_set_muted(&data, muted); data.sync_base = ssync ? ssync->sink_input : NULL; sink_input = pa_sink_input_new(c->protocol->core, &data, flags); @@ -1688,7 +1690,9 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u variable_rate = FALSE, muted = FALSE, adjust_latency = FALSE, - early_requests = FALSE; + early_requests = FALSE, + dont_inhibit_auto_suspend = FALSE, + muted_set = FALSE; pa_sink_input_flags_t flags = 0; pa_proplist *p; @@ -1769,6 +1773,16 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u } } + if (c->version >= 15) { + + if (pa_tagstruct_get_boolean(t, &muted_set) < 0 || + pa_tagstruct_get_boolean(t, &dont_inhibit_auto_suspend) < 0) { + protocol_error(c); + pa_proplist_free(p); + return; + } + } + if (!pa_tagstruct_eof(t)) { protocol_error(c); pa_proplist_free(p); @@ -1800,9 +1814,14 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u (fix_rate ? PA_SINK_INPUT_FIX_RATE : 0) | (fix_channels ? PA_SINK_INPUT_FIX_CHANNELS : 0) | (no_move ? PA_SINK_INPUT_DONT_MOVE : 0) | - (variable_rate ? PA_SINK_INPUT_VARIABLE_RATE : 0); + (variable_rate ? PA_SINK_INPUT_VARIABLE_RATE : 0) | + (dont_inhibit_auto_suspend ? PA_SINK_INPUT_DONT_INHIBIT_AUTO_SUSPEND : 0); + + /* Only since protocol version 15 there's a seperate muted_set + * flag. For older versions we synthesize it here */ + muted_set = muted_set || muted; - s = playback_stream_new(c, sink, &ss, &map, &maxlength, &tlength, &prebuf, &minreq, volume_set ? &volume : NULL, muted, syncid, &missing, flags, p, adjust_latency, early_requests); + s = playback_stream_new(c, sink, &ss, &map, &maxlength, &tlength, &prebuf, &minreq, volume_set ? &volume : NULL, muted, muted_set, syncid, &missing, flags, p, adjust_latency, early_requests); pa_proplist_free(p); CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_INVALID); @@ -1923,7 +1942,8 @@ static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uin variable_rate = FALSE, adjust_latency = FALSE, peak_detect = FALSE, - early_requests = FALSE; + early_requests = FALSE, + dont_inhibit_auto_suspend = FALSE; pa_source_output_flags_t flags = 0; pa_proplist *p; uint32_t direct_on_input_idx = PA_INVALID_INDEX; @@ -1995,6 +2015,15 @@ static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uin } } + if (c->version >= 15) { + + if (pa_tagstruct_get_boolean(t, &dont_inhibit_auto_suspend) < 0) { + protocol_error(c); + pa_proplist_free(p); + return; + } + } + if (!pa_tagstruct_eof(t)) { protocol_error(c); pa_proplist_free(p); @@ -2035,7 +2064,8 @@ static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uin (fix_rate ? PA_SOURCE_OUTPUT_FIX_RATE : 0) | (fix_channels ? PA_SOURCE_OUTPUT_FIX_CHANNELS : 0) | (no_move ? PA_SOURCE_OUTPUT_DONT_MOVE : 0) | - (variable_rate ? PA_SOURCE_OUTPUT_VARIABLE_RATE : 0); + (variable_rate ? PA_SOURCE_OUTPUT_VARIABLE_RATE : 0) | + (dont_inhibit_auto_suspend ? PA_SOURCE_OUTPUT_DONT_INHIBIT_AUTO_SUSPEND : 0); s = record_stream_new(c, source, &ss, &map, peak_detect, &maxlength, &fragment_size, flags, p, adjust_latency, direct_on_input, early_requests); pa_proplist_free(p); @@ -2722,7 +2752,7 @@ static void sink_input_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_tagstruct_putu32(t, s->sink->index); pa_tagstruct_put_sample_spec(t, &fixed_ss); pa_tagstruct_put_channel_map(t, &s->channel_map); - pa_tagstruct_put_cvolume(t, &s->volume); + pa_tagstruct_put_cvolume(t, pa_sink_input_get_volume(s)); pa_tagstruct_put_usec(t, pa_sink_input_get_latency(s, &sink_latency)); pa_tagstruct_put_usec(t, sink_latency); pa_tagstruct_puts(t, pa_resample_method_to_string(pa_sink_input_get_resample_method(s))); diff --git a/src/pulsecore/resampler.c b/src/pulsecore/resampler.c index b2d512c8..f0515ebe 100644 --- a/src/pulsecore/resampler.c +++ b/src/pulsecore/resampler.c @@ -510,6 +510,52 @@ static pa_bool_t on_lfe(pa_channel_position_t p) { p == PA_CHANNEL_POSITION_LFE; } +static pa_bool_t on_front(pa_channel_position_t p) { + return + p == PA_CHANNEL_POSITION_FRONT_LEFT || + p == PA_CHANNEL_POSITION_FRONT_RIGHT || + p == PA_CHANNEL_POSITION_FRONT_CENTER || + p == PA_CHANNEL_POSITION_TOP_FRONT_LEFT || + p == PA_CHANNEL_POSITION_TOP_FRONT_RIGHT || + p == PA_CHANNEL_POSITION_TOP_FRONT_CENTER || + p == PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER || + p == PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER; +} + +static pa_bool_t on_rear(pa_channel_position_t p) { + return + p == PA_CHANNEL_POSITION_REAR_LEFT || + p == PA_CHANNEL_POSITION_REAR_RIGHT || + p == PA_CHANNEL_POSITION_REAR_CENTER || + p == PA_CHANNEL_POSITION_TOP_REAR_LEFT || + p == PA_CHANNEL_POSITION_TOP_REAR_RIGHT || + p == PA_CHANNEL_POSITION_TOP_REAR_CENTER; +} + +static pa_bool_t on_side(pa_channel_position_t p) { + return + p == PA_CHANNEL_POSITION_SIDE_LEFT || + p == PA_CHANNEL_POSITION_SIDE_RIGHT || + p == PA_CHANNEL_POSITION_TOP_CENTER; +} + +enum { + ON_FRONT, + ON_REAR, + ON_SIDE, + ON_OTHER +}; + +static int front_rear_side(pa_channel_position_t p) { + if (on_front(p)) + return ON_FRONT; + if (on_rear(p)) + return ON_REAR; + if (on_side(p)) + return ON_SIDE; + return ON_OTHER; +} + static void calc_map_table(pa_resampler *r) { unsigned oc, ic; pa_bool_t ic_connected[PA_CHANNELS_MAX]; @@ -601,7 +647,9 @@ static void calc_map_table(pa_resampler *r) { * D:left, all D:right, all D:center channels, gain is * 0.375. The current (as result of 1..6) factors * should be multiplied by 0.75. (Alt. suggestion: 0.25 - * vs. 0.5) + * vs. 0.5) If C-front is only mixed into + * L-front/R-front if available, otherwise into all L/R + * channels. Similarly for C-rear. * * S: and D: shall relate to the source resp. destination channels. * @@ -629,6 +677,8 @@ static void calc_map_table(pa_resampler *r) { if (!oc_connected && remix) { /* OK, we shall remix */ + /* Try to find matching input ports for this output port */ + if (on_left(b)) { unsigned n = 0; @@ -830,17 +880,54 @@ static void calc_map_table(pa_resampler *r) { } if (!mixed_in) { + unsigned ncenter[PA_CHANNELS_MAX]; + pa_bool_t found_frs[PA_CHANNELS_MAX]; + + memset(ncenter, 0, sizeof(ncenter)); + memset(found_frs, 0, sizeof(found_frs)); /* Hmm, as it appears there was no center channel we could mix our center channel in. In this case, mix it into left and right. Using .375 and 0.75 as factors. */ + for (ic = 0; ic < r->i_ss.channels; ic++) { + + if (ic_connected[ic]) + continue; + + if (!on_center(r->i_cm.map[ic])) + continue; + + for (oc = 0; oc < r->o_ss.channels; oc++) { + + if (!on_left(r->o_cm.map[oc]) && !on_right(r->o_cm.map[oc])) + continue; + + if (front_rear_side(r->i_cm.map[ic]) == front_rear_side(r->o_cm.map[oc])) { + found_frs[ic] = TRUE; + break; + } + } + + for (oc = 0; oc < r->o_ss.channels; oc++) { + + if (!on_left(r->o_cm.map[oc]) && !on_right(r->o_cm.map[oc])) + continue; + + if (!found_frs[ic] || front_rear_side(r->i_cm.map[ic]) == front_rear_side(r->o_cm.map[oc])) + ncenter[oc]++; + } + } + for (oc = 0; oc < r->o_ss.channels; oc++) { if (!on_left(r->o_cm.map[oc]) && !on_right(r->o_cm.map[oc])) continue; + if (ncenter[oc] <= 0) + continue; + for (ic = 0; ic < r->i_ss.channels; ic++) { if (ic_connected[ic]) { @@ -848,8 +935,11 @@ static void calc_map_table(pa_resampler *r) { continue; } - if (on_center(r->i_cm.map[ic])) - r->map_table[oc][ic] = .375f / (float) ic_unconnected_center; + if (!on_center(r->i_cm.map[ic])) + continue; + + if (!found_frs[ic] || front_rear_side(r->i_cm.map[ic]) == front_rear_side(r->o_cm.map[oc])) + r->map_table[oc][ic] = .375f / (float) ncenter[oc]; } } } diff --git a/src/pulsecore/sample-util.c b/src/pulsecore/sample-util.c index 7b9ac7bc..9f0f795c 100644 --- a/src/pulsecore/sample-util.c +++ b/src/pulsecore/sample-util.c @@ -777,7 +777,7 @@ size_t pa_frame_align(size_t l, const pa_sample_spec *ss) { return (l/fs) * fs; } -int pa_frame_aligned(size_t l, const pa_sample_spec *ss) { +pa_bool_t pa_frame_aligned(size_t l, const pa_sample_spec *ss) { size_t fs; pa_assert(ss); diff --git a/src/pulsecore/sample-util.h b/src/pulsecore/sample-util.h index 06ecb724..2fe2c81d 100644 --- a/src/pulsecore/sample-util.h +++ b/src/pulsecore/sample-util.h @@ -71,7 +71,7 @@ void pa_volume_memchunk( size_t pa_frame_align(size_t l, const pa_sample_spec *ss) PA_GCC_PURE; -int pa_frame_aligned(size_t l, const pa_sample_spec *ss) PA_GCC_PURE; +pa_bool_t pa_frame_aligned(size_t l, const pa_sample_spec *ss) PA_GCC_PURE; void pa_interleave(const void *src[], unsigned channels, void *dst, size_t ss, unsigned n); void pa_deinterleave(const void *src, void *dst[], unsigned channels, size_t ss, unsigned n); diff --git a/src/pulsecore/shm.c b/src/pulsecore/shm.c index b2997575..c59d247c 100644 --- a/src/pulsecore/shm.c +++ b/src/pulsecore/shm.c @@ -73,10 +73,10 @@ struct shm_marker PA_GCC_PACKED { pa_atomic_t marker; /* 0xbeefcafe */ pa_atomic_t pid; - uint64_t *_reserverd1; - uint64_t *_reserverd2; - uint64_t *_reserverd3; - uint64_t *_reserverd4; + uint64_t _reserved1; + uint64_t _reserved2; + uint64_t _reserved3; + uint64_t _reserved4; }; static char *segment_name(char *fn, size_t l, unsigned id) { diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c index 4f70347f..d25cd797 100644 --- a/src/pulsecore/sink-input.c +++ b/src/pulsecore/sink-input.c @@ -75,7 +75,7 @@ void pa_sink_input_new_data_set_volume(pa_sink_input_new_data *data, const pa_cv pa_assert(data); if ((data->volume_is_set = !!volume)) - data->volume = *volume; + data->volume = data->virtual_volume = *volume; } void pa_sink_input_new_data_set_muted(pa_sink_input_new_data *data, pa_bool_t mute) { @@ -108,6 +108,7 @@ static void reset_callbacks(pa_sink_input *i) { i->kill = NULL; i->get_latency = NULL; i->state_change = NULL; + i->may_move_to = NULL; } /* Called from main context */ @@ -119,6 +120,7 @@ pa_sink_input* pa_sink_input_new( pa_sink_input *i; pa_resampler *resampler = NULL; char st[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX]; + pa_channel_map original_cm; pa_assert(core); pa_assert(data); @@ -141,20 +143,25 @@ pa_sink_input* pa_sink_input_new( pa_return_null_if_fail(pa_sample_spec_valid(&data->sample_spec)); if (!data->channel_map_is_set) { - if (data->sink->channel_map.channels == data->sample_spec.channels) + if (pa_channel_map_compatible(&data->sink->channel_map, &data->sample_spec)) data->channel_map = data->sink->channel_map; else - pa_return_null_if_fail(pa_channel_map_init_auto(&data->channel_map, data->sample_spec.channels, PA_CHANNEL_MAP_DEFAULT)); + pa_channel_map_init_extend(&data->channel_map, data->sample_spec.channels, PA_CHANNEL_MAP_DEFAULT); } pa_return_null_if_fail(pa_channel_map_valid(&data->channel_map)); - pa_return_null_if_fail(data->channel_map.channels == data->sample_spec.channels); + pa_return_null_if_fail(pa_channel_map_compatible(&data->channel_map, &data->sample_spec)); - if (!data->volume_is_set) + if (!data->volume_is_set) { pa_cvolume_reset(&data->volume, data->sample_spec.channels); + pa_cvolume_reset(&data->virtual_volume, data->sample_spec.channels); + } pa_return_null_if_fail(pa_cvolume_valid(&data->volume)); - pa_return_null_if_fail(data->volume.channels == data->sample_spec.channels); + pa_return_null_if_fail(pa_cvolume_compatible(&data->volume, &data->sample_spec)); + + pa_return_null_if_fail(pa_cvolume_valid(&data->virtual_volume)); + pa_return_null_if_fail(pa_cvolume_compatible(&data->virtual_volume, &data->sample_spec)); if (!data->muted_is_set) data->muted = FALSE; @@ -165,6 +172,8 @@ pa_sink_input* pa_sink_input_new( if (flags & PA_SINK_INPUT_FIX_RATE) data->sample_spec.rate = data->sink->sample_spec.rate; + original_cm = data->channel_map; + if (flags & PA_SINK_INPUT_FIX_CHANNELS) { data->sample_spec.channels = data->sink->sample_spec.channels; data->channel_map = data->sink->channel_map; @@ -174,8 +183,7 @@ pa_sink_input* pa_sink_input_new( pa_assert(pa_channel_map_valid(&data->channel_map)); /* Due to the fixing of the sample spec the volume might not match anymore */ - if (data->volume.channels != data->sample_spec.channels) - pa_cvolume_set(&data->volume, data->sample_spec.channels, pa_cvolume_avg(&data->volume)); + pa_cvolume_remap(&data->volume, &original_cm, &data->channel_map); if (data->resample_method == PA_RESAMPLER_INVALID) data->resample_method = core->resample_method; @@ -227,7 +235,9 @@ pa_sink_input* pa_sink_input_new( i->sample_spec = data->sample_spec; i->channel_map = data->channel_map; + i->virtual_volume = data->virtual_volume; i->volume = data->volume; + i->muted = data->muted; if (data->sync_base) { @@ -292,8 +302,6 @@ static void update_n_corked(pa_sink_input *i, pa_sink_input_state_t state) { pa_assert_se(i->sink->n_corked -- >= 1); else if (i->state != PA_SINK_INPUT_CORKED && state == PA_SINK_INPUT_CORKED) i->sink->n_corked++; - - pa_sink_update_status(i->sink); } /* Called from main context */ @@ -331,6 +339,8 @@ static int sink_input_set_state(pa_sink_input *i, pa_sink_input_state_t state) { pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED], ssync); } + pa_sink_update_status(i->sink); + return 0; } @@ -381,6 +391,8 @@ void pa_sink_input_unlink(pa_sink_input *i) { pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_UNLINK_POST], i); } + pa_sink_update_status(i->sink); + i->sink = NULL; pa_sink_input_unref(i); } @@ -442,6 +454,8 @@ void pa_sink_input_put(pa_sink_input *i) { pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW, i->index); pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_PUT], i); + + pa_sink_update_status(i->sink); } /* Called from main context */ @@ -784,17 +798,34 @@ pa_usec_t pa_sink_input_get_requested_latency(pa_sink_input *i) { /* Called from main context */ void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume) { + pa_sink_input_set_volume_data data; + pa_sink_input_assert_ref(i); pa_assert(PA_SINK_INPUT_IS_LINKED(i->state)); pa_assert(volume); + pa_assert(pa_cvolume_valid(volume)); + pa_assert(pa_cvolume_compatible(volume, &i->sample_spec)); + + data.sink_input = i; + data.virtual_volume = *volume; + data.volume = *volume; + + /* If you change something here, consider looking into + * module-flat-volume.c as well since it uses very similar + * code. */ - if (pa_cvolume_equal(&i->volume, volume)) + if (pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_SET_VOLUME], &data) < 0) return; - i->volume = *volume; + if (!pa_cvolume_equal(&i->volume, &data.volume)) { + i->volume = data.volume; + pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_VOLUME, &data.volume, 0, NULL) == 0); + } - pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_VOLUME, &i->volume, 0, NULL) == 0); - pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index); + if (!pa_cvolume_equal(&i->virtual_volume, &data.virtual_volume)) { + i->virtual_volume = data.virtual_volume; + pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index); + } } /* Called from main context */ @@ -802,7 +833,7 @@ const pa_cvolume *pa_sink_input_get_volume(pa_sink_input *i) { pa_sink_input_assert_ref(i); pa_assert(PA_SINK_INPUT_IS_LINKED(i->state)); - return &i->volume; + return &i->virtual_volume; } /* Called from main context */ @@ -885,6 +916,35 @@ pa_resample_method_t pa_sink_input_get_resample_method(pa_sink_input *i) { } /* Called from main context */ +pa_bool_t pa_sink_input_may_move_to(pa_sink_input *i, pa_sink *dest) { + pa_sink_input_assert_ref(i); + pa_assert(PA_SINK_INPUT_IS_LINKED(i->state)); + pa_sink_assert_ref(dest); + + if (dest == i->sink) + return TRUE; + + if (i->flags & PA_SINK_INPUT_DONT_MOVE) + return FALSE; + + if (i->sync_next || i->sync_prev) { + pa_log_warn("Moving synchronised streams not supported."); + return FALSE; + } + + if (pa_idxset_size(dest->inputs) >= PA_MAX_INPUTS_PER_SINK) { + pa_log_warn("Failed to move sink input: too many inputs per sink."); + return FALSE; + } + + if (i->may_move_to) + if (!i->may_move_to(i, dest)) + return FALSE; + + return TRUE; +} + +/* Called from main context */ int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest) { pa_resampler *new_resampler; pa_sink *origin; @@ -900,19 +960,9 @@ int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest) { if (dest == origin) return 0; - if (i->flags & PA_SINK_INPUT_DONT_MOVE) + if (!pa_sink_input_may_move_to(i, dest)) return -1; - if (i->sync_next || i->sync_prev) { - pa_log_warn("Moving synchronised streams not supported."); - return -1; - } - - if (pa_idxset_size(dest->inputs) >= PA_MAX_INPUTS_PER_SINK) { - pa_log_warn("Failed to move sink input: too many inputs per sink."); - return -1; - } - /* Kill directly connected outputs */ while ((o = pa_idxset_first(i->direct_outputs, NULL))) { pa_assert(o != p); @@ -1144,7 +1194,8 @@ void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes /* in our sam * implementor. This implies 'flush' is TRUE. */ pa_sink_input_assert_ref(i); - pa_assert(i->thread_info.rewrite_nbytes == 0); + + nbytes = PA_MAX(i->thread_info.rewrite_nbytes, nbytes); /* pa_log_debug("request rewrite %lu", (unsigned long) nbytes); */ @@ -1172,26 +1223,33 @@ void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes /* in our sam nbytes = pa_resampler_request(i->thread_info.resampler, nbytes); } - if (rewrite) { - /* Make sure to not overwrite over underruns */ - if (nbytes > i->thread_info.playing_for) - nbytes = (size_t) i->thread_info.playing_for; + if (i->thread_info.rewrite_nbytes != (size_t) -1) { + if (rewrite) { + /* Make sure to not overwrite over underruns */ + if (nbytes > i->thread_info.playing_for) + nbytes = (size_t) i->thread_info.playing_for; - i->thread_info.rewrite_nbytes = nbytes; - } else - i->thread_info.rewrite_nbytes = (size_t) -1; + i->thread_info.rewrite_nbytes = nbytes; + } else + i->thread_info.rewrite_nbytes = (size_t) -1; + } - i->thread_info.rewrite_flush = flush && i->thread_info.rewrite_nbytes != 0; + i->thread_info.rewrite_flush = + i->thread_info.rewrite_flush || + (flush && i->thread_info.rewrite_nbytes != 0); - /* Transform to sink domain */ - if (i->thread_info.resampler) - nbytes = pa_resampler_result(i->thread_info.resampler, nbytes); + if (nbytes != (size_t) -1) { - if (nbytes > lbq) - pa_sink_request_rewind(i->sink, nbytes - lbq); - else - /* This call will make sure process_rewind() is called later */ - pa_sink_request_rewind(i->sink, 0); + /* Transform to sink domain */ + if (i->thread_info.resampler) + nbytes = pa_resampler_result(i->thread_info.resampler, nbytes); + + if (nbytes > lbq) + pa_sink_request_rewind(i->sink, nbytes - lbq); + else + /* This call will make sure process_rewind() is called later */ + pa_sink_request_rewind(i->sink, 0); + } } /* Called from main context */ diff --git a/src/pulsecore/sink-input.h b/src/pulsecore/sink-input.h index 7663f22c..27125988 100644 --- a/src/pulsecore/sink-input.h +++ b/src/pulsecore/sink-input.h @@ -56,7 +56,8 @@ typedef enum pa_sink_input_flags { PA_SINK_INPUT_NO_REMIX = 16, PA_SINK_INPUT_FIX_FORMAT = 32, PA_SINK_INPUT_FIX_RATE = 64, - PA_SINK_INPUT_FIX_CHANNELS = 128 + PA_SINK_INPUT_FIX_CHANNELS = 128, + PA_SINK_INPUT_DONT_INHIBIT_AUTO_SUSPEND = 256, } pa_sink_input_flags_t; struct pa_sink_input { @@ -89,6 +90,8 @@ struct pa_sink_input { pa_sink_input *sync_prev, *sync_next; + pa_cvolume virtual_volume; + pa_cvolume volume; pa_bool_t muted; @@ -154,10 +157,15 @@ struct pa_sink_input { returns */ pa_usec_t (*get_latency) (pa_sink_input *i); /* may be NULL */ - /* If non_NULL this function is called from thread context if the + /* 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 */ + /* If non-NULL this function is called before this sink input is + * move to a sink and if it returns FALSE the move will not + * be allowed */ + pa_bool_t (*may_move_to) (pa_sink_input *i, pa_sink *s); /* may be NULL */ + struct { pa_sink_input_state_t state; pa_atomic_t drained; @@ -218,6 +226,9 @@ typedef struct pa_sink_input_new_data { pa_sample_spec sample_spec; pa_channel_map channel_map; + + pa_cvolume virtual_volume; + pa_cvolume volume; pa_bool_t muted:1; @@ -239,6 +250,12 @@ typedef struct pa_sink_input_move_hook_data { pa_sink *destination; } pa_sink_input_move_hook_data; +typedef struct pa_sink_set_input_volume_data { + pa_sink_input *sink_input; + pa_cvolume virtual_volume; + pa_cvolume volume; +} pa_sink_input_set_volume_data; + /* To be called by the implementing module only */ pa_sink_input* pa_sink_input_new( @@ -281,6 +298,7 @@ pa_bool_t pa_sink_input_get_mute(pa_sink_input *i); pa_resample_method_t pa_sink_input_get_resample_method(pa_sink_input *i); int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest); +pa_bool_t pa_sink_input_may_move_to(pa_sink_input *i, pa_sink *dest); pa_sink_input_state_t pa_sink_input_get_state(pa_sink_input *i); diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c index e04fc08a..1580cf2e 100644 --- a/src/pulsecore/sink.c +++ b/src/pulsecore/sink.c @@ -843,13 +843,27 @@ pa_usec_t pa_sink_get_latency(pa_sink *s) { /* Called from main thread */ void pa_sink_set_volume(pa_sink *s, const pa_cvolume *volume) { pa_bool_t changed; + pa_sink_set_volume_data data; pa_sink_assert_ref(s); pa_assert(PA_SINK_IS_LINKED(s->state)); pa_assert(volume); + pa_assert(pa_cvolume_valid(volume)); + pa_assert(pa_cvolume_compatible(volume, &s->sample_spec)); - changed = !pa_cvolume_equal(volume, &s->volume); - s->volume = *volume; + data.sink = s; + data.volume = *volume; + + changed = !pa_cvolume_equal(&data.volume, &s->volume); + + if (changed) { + if (pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_SET_VOLUME], &data) < 0) + return; + + changed = !pa_cvolume_equal(&data.volume, &s->volume); + } + + s->volume = data.volume; if (s->set_volume && s->set_volume(s) < 0) s->set_volume = NULL; @@ -977,7 +991,7 @@ unsigned pa_sink_linked_by(pa_sink *s) { ret = pa_idxset_size(s->inputs); /* We add in the number of streams connected to us here. Please - * not the asymmmetry to pa_sink_used_by()! */ + * note the asymmmetry to pa_sink_used_by()! */ if (s->monitor_source) ret += pa_source_linked_by(s->monitor_source); @@ -1001,6 +1015,40 @@ unsigned pa_sink_used_by(pa_sink *s) { return ret - s->n_corked; } +/* Called from main thread */ +unsigned pa_sink_check_suspend(pa_sink *s) { + unsigned ret; + pa_sink_input *i; + uint32_t idx; + + pa_sink_assert_ref(s); + + if (!PA_SINK_IS_LINKED(s->state)) + return 0; + + ret = 0; + + for (i = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); i; i = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx))) { + pa_sink_input_state_t st; + + st = pa_sink_input_get_state(i); + pa_assert(PA_SINK_INPUT_IS_LINKED(st)); + + if (st == PA_SINK_INPUT_CORKED) + continue; + + if (i->flags & PA_SINK_INPUT_DONT_INHIBIT_AUTO_SUSPEND) + continue; + + ret ++; + } + + if (s->monitor_source) + ret += pa_source_check_suspend(s->monitor_source); + + return ret; +} + /* Called from IO thread, except when it is not */ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk) { pa_sink *s = PA_SINK(o); diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h index 672bdd39..c5a73214 100644 --- a/src/pulsecore/sink.h +++ b/src/pulsecore/sink.h @@ -206,6 +206,11 @@ void pa_sink_new_data_set_volume(pa_sink_new_data *data, const pa_cvolume *volum void pa_sink_new_data_set_muted(pa_sink_new_data *data, pa_bool_t mute); void pa_sink_new_data_done(pa_sink_new_data *data); +typedef struct pa_sink_set_volume_data { + pa_sink *sink; + pa_cvolume volume; +} pa_sink_set_volume_data; + /* To be called exclusively by the sink driver, from main context */ pa_sink* pa_sink_new( @@ -247,6 +252,7 @@ pa_bool_t pa_sink_get_mute(pa_sink *sink, pa_bool_t force_refres); unsigned pa_sink_linked_by(pa_sink *s); /* Number of connected streams */ unsigned pa_sink_used_by(pa_sink *s); /* Number of connected streams which are not corked */ +unsigned pa_sink_check_suspend(pa_sink *s); /* Returns how many streams are active that don't allow suspensions */ #define pa_sink_get_state(s) ((s)->state) /* To be called exclusively by the sink driver, from IO context */ diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c index d76f6e4e..c92c5ab7 100644 --- a/src/pulsecore/source-output.c +++ b/src/pulsecore/source-output.c @@ -90,6 +90,7 @@ static void reset_callbacks(pa_source_output *o) { o->kill = NULL; o->get_latency = NULL; o->state_change = NULL; + o->may_move_to = NULL; } /* Called from main context */ @@ -124,14 +125,14 @@ pa_source_output* pa_source_output_new( pa_return_null_if_fail(pa_sample_spec_valid(&data->sample_spec)); if (!data->channel_map_is_set) { - if (data->source->channel_map.channels == data->sample_spec.channels) + if (pa_channel_map_compatible(&data->source->channel_map, &data->sample_spec)) data->channel_map = data->source->channel_map; else - pa_return_null_if_fail(pa_channel_map_init_auto(&data->channel_map, data->sample_spec.channels, PA_CHANNEL_MAP_DEFAULT)); + pa_channel_map_init_extend(&data->channel_map, data->sample_spec.channels, PA_CHANNEL_MAP_DEFAULT); } pa_return_null_if_fail(pa_channel_map_valid(&data->channel_map)); - pa_return_null_if_fail(data->channel_map.channels == data->sample_spec.channels); + pa_return_null_if_fail(pa_channel_map_compatible(&data->channel_map, &data->sample_spec)); if (flags & PA_SOURCE_OUTPUT_FIX_FORMAT) data->sample_spec.format = data->source->sample_spec.format; @@ -246,7 +247,6 @@ static void update_n_corked(pa_source_output *o, pa_source_output_state_t state) else if (o->state != PA_SOURCE_OUTPUT_CORKED && state == PA_SOURCE_OUTPUT_CORKED) o->source->n_corked++; - pa_source_update_status(o->source); } /* Called from main context */ @@ -264,6 +264,8 @@ static int source_output_set_state(pa_source_output *o, pa_source_output_state_t if (state != PA_SOURCE_OUTPUT_UNLINKED) pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_STATE_CHANGED], o); + pa_source_update_status(o->source); + return 0; } @@ -302,6 +304,8 @@ void pa_source_output_unlink(pa_source_output*o) { pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK_POST], o); } + pa_source_update_status(o->source); + o->source = NULL; pa_source_output_unref(o); } @@ -353,6 +357,8 @@ void pa_source_output_put(pa_source_output *o) { pa_subscription_post(o->source->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_NEW, o->index); pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PUT], o); + + pa_source_update_status(o->source); } /* Called from main context */ @@ -593,6 +599,32 @@ pa_resample_method_t pa_source_output_get_resample_method(pa_source_output *o) { return o->resample_method; } +pa_bool_t pa_source_output_may_move_to(pa_source_output *o, pa_source *dest) { + pa_source_output_assert_ref(o); + pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state)); + pa_source_assert_ref(dest); + + if (dest == o->source) + return TRUE; + + if (o->flags & PA_SOURCE_OUTPUT_DONT_MOVE) + return FALSE; + + if (o->direct_on_input) + return FALSE; + + if (pa_idxset_size(dest->outputs) >= PA_MAX_OUTPUTS_PER_SOURCE) { + pa_log_warn("Failed to move source output: too many outputs per source."); + return FALSE; + } + + if (o->may_move_to) + if (!o->may_move_to(o, dest)) + return FALSE; + + return TRUE; +} + /* Called from main context */ int pa_source_output_move_to(pa_source_output *o, pa_source *dest) { pa_source *origin; @@ -608,16 +640,8 @@ int pa_source_output_move_to(pa_source_output *o, pa_source *dest) { if (dest == origin) return 0; - if (o->flags & PA_SOURCE_OUTPUT_DONT_MOVE) - return -1; - - if (o->direct_on_input) - return -1; - - if (pa_idxset_size(dest->outputs) >= PA_MAX_OUTPUTS_PER_SOURCE) { - pa_log_warn("Failed to move source output: too many outputs per source."); + if (!pa_source_output_may_move_to(o, dest)) return -1; - } if (o->thread_info.resampler && pa_sample_spec_equal(&origin->sample_spec, &dest->sample_spec) && diff --git a/src/pulsecore/source-output.h b/src/pulsecore/source-output.h index a7aac814..f011f9bd 100644 --- a/src/pulsecore/source-output.h +++ b/src/pulsecore/source-output.h @@ -52,7 +52,8 @@ typedef enum pa_source_output_flags { PA_SOURCE_OUTPUT_NO_REMIX = 16, PA_SOURCE_OUTPUT_FIX_FORMAT = 32, PA_SOURCE_OUTPUT_FIX_RATE = 64, - PA_SOURCE_OUTPUT_FIX_CHANNELS = 128 + PA_SOURCE_OUTPUT_FIX_CHANNELS = 128, + PA_SOURCE_OUTPUT_DONT_INHIBIT_AUTO_SUSPEND = 256 } pa_source_output_flags_t; struct pa_source_output { @@ -126,10 +127,15 @@ struct pa_source_output { returns */ pa_usec_t (*get_latency) (pa_source_output *o); /* may be NULL */ - /* If non_NULL this function is called from thread context if the + /* 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 */ + /* If non-NULL this function is called before this source output + * is moved to a source and if it returns FALSE the move + * will not be allowed */ + pa_bool_t (*may_move_to) (pa_source_output *o, pa_source *s); /* may be NULL */ + struct { pa_source_output_state_t state; @@ -220,6 +226,7 @@ pa_usec_t pa_source_output_get_latency(pa_source_output *i, pa_usec_t *source_la pa_resample_method_t pa_source_output_get_resample_method(pa_source_output *o); +pa_bool_t pa_source_output_may_move_to(pa_source_output *o, pa_source *dest); int pa_source_output_move_to(pa_source_output *o, pa_source *dest); #define pa_source_output_get_state(o) ((o)->state) diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c index edbbf017..f113e295 100644 --- a/src/pulsecore/source.c +++ b/src/pulsecore/source.c @@ -645,6 +645,37 @@ unsigned pa_source_used_by(pa_source *s) { return ret - s->n_corked; } +/* Called from main thread */ +unsigned pa_source_check_suspend(pa_source *s) { + unsigned ret; + pa_source_output *o; + uint32_t idx; + + pa_source_assert_ref(s); + + if (!PA_SOURCE_IS_LINKED(s->state)) + return 0; + + ret = 0; + + for (o = PA_SOURCE_OUTPUT(pa_idxset_first(s->outputs, &idx)); o; o = PA_SOURCE_OUTPUT(pa_idxset_next(s->outputs, &idx))) { + pa_source_output_state_t st; + + st = pa_source_output_get_state(o); + pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(st)); + + if (st == PA_SOURCE_OUTPUT_CORKED) + continue; + + if (o->flags & PA_SOURCE_OUTPUT_DONT_INHIBIT_AUTO_SUSPEND) + continue; + + ret ++; + } + + return ret; +} + /* Called from IO thread, except when it is not */ int pa_source_process_msg(pa_msgobject *object, int code, void *userdata, int64_t offset, pa_memchunk *chunk) { pa_source *s = PA_SOURCE(object); diff --git a/src/pulsecore/source.h b/src/pulsecore/source.h index cae78693..aaf904b4 100644 --- a/src/pulsecore/source.h +++ b/src/pulsecore/source.h @@ -234,6 +234,7 @@ pa_bool_t pa_source_get_mute(pa_source *source, pa_bool_t force_refresh); unsigned pa_source_linked_by(pa_source *s); /* Number of connected streams */ unsigned pa_source_used_by(pa_source *s); /* Number of connected streams that are not corked */ +unsigned pa_source_check_suspend(pa_source *s); /* Returns how many streams are active that don't allow suspensions */ #define pa_source_get_state(s) ((pa_source_state_t) (s)->state) /* To be called exclusively by the source driver, from IO context */ diff --git a/src/pulsecore/time-smoother.c b/src/pulsecore/time-smoother.c index 6a2ffaa6..65621948 100644 --- a/src/pulsecore/time-smoother.c +++ b/src/pulsecore/time-smoother.c @@ -313,7 +313,7 @@ static void estimate(pa_smoother *s, pa_usec_t x, pa_usec_t *y, double *deriv) { /* Move back from origin */ ty += (double) s->ey; - *y = ty >= 0 ? (pa_usec_t) lrint(ty) : 0; + *y = ty >= 0 ? (pa_usec_t) llrint(ty) : 0; /* Horner scheme */ if (deriv) @@ -360,7 +360,7 @@ void pa_smoother_put(pa_smoother *s, pa_usec_t x, pa_usec_t y) { /* And calculate when we want to be on track again */ s->px = s->ex + s->adjust_time; - s->py = s->ry + (pa_usec_t) lrint(s->dp * (double) s->adjust_time); + s->py = s->ry + (pa_usec_t) llrint(s->dp * (double) s->adjust_time); s->abc_valid = FALSE; @@ -456,7 +456,7 @@ pa_usec_t pa_smoother_translate(pa_smoother *s, pa_usec_t x, pa_usec_t y_delay) /* pa_log_debug("translate(%llu) = %llu (%0.2f)", (unsigned long long) y_delay, (unsigned long long) ((double) y_delay / nde), nde); */ - return (pa_usec_t) lrint((double) y_delay / nde); + return (pa_usec_t) llrint((double) y_delay / nde); } void pa_smoother_reset(pa_smoother *s) { |