diff options
Diffstat (limited to 'src/pulsecore')
-rw-r--r-- | src/pulsecore/core-util.c | 586 | ||||
-rw-r--r-- | src/pulsecore/core-util.h | 41 | ||||
-rw-r--r-- | src/pulsecore/native-common.h | 3 | ||||
-rw-r--r-- | src/pulsecore/pid.c | 20 | ||||
-rw-r--r-- | src/pulsecore/protocol-cli.c | 2 | ||||
-rw-r--r-- | src/pulsecore/protocol-esound.c | 2 | ||||
-rw-r--r-- | src/pulsecore/protocol-http.c | 2 | ||||
-rw-r--r-- | src/pulsecore/protocol-native.c | 57 | ||||
-rw-r--r-- | src/pulsecore/protocol-simple.c | 2 | ||||
-rw-r--r-- | src/pulsecore/sink-input.c | 170 | ||||
-rw-r--r-- | src/pulsecore/sink-input.h | 18 | ||||
-rw-r--r-- | src/pulsecore/sink.c | 128 | ||||
-rw-r--r-- | src/pulsecore/sink.h | 39 | ||||
-rw-r--r-- | src/pulsecore/sound-file-stream.c | 59 | ||||
-rw-r--r-- | src/pulsecore/source-output.c | 85 | ||||
-rw-r--r-- | src/pulsecore/source-output.h | 14 | ||||
-rw-r--r-- | src/pulsecore/source.c | 54 | ||||
-rw-r--r-- | src/pulsecore/source.h | 5 |
18 files changed, 904 insertions, 383 deletions
diff --git a/src/pulsecore/core-util.c b/src/pulsecore/core-util.c index 28885b2c..df110966 100644 --- a/src/pulsecore/core-util.c +++ b/src/pulsecore/core-util.c @@ -41,6 +41,7 @@ #include <sys/types.h> #include <sys/stat.h> #include <sys/time.h> +#include <dirent.h> #ifdef HAVE_STRTOF_L #include <locale.h> @@ -103,12 +104,6 @@ #define MSG_NOSIGNAL 0 #endif -#ifndef OS_IS_WIN32 -#define PA_USER_RUNTIME_PATH_PREFIX "/tmp/pulse-" -#else -#define PA_USER_RUNTIME_PATH_PREFIX "%TEMP%\\pulse-" -#endif - #ifdef OS_IS_WIN32 #define PULSE_ROOTENV "PULSE_ROOT" @@ -221,7 +216,7 @@ int pa_make_secure_dir(const char* dir, mode_t m, uid_t uid, gid_t gid) { goto fail; } #else - pa_log_warn("secure directory creation not supported on Win32."); + pa_log_warn("Secure directory creation not supported on Win32."); #endif return 0; @@ -557,6 +552,82 @@ int pa_make_realtime(int rtprio) { #endif } +/* This is merely used for giving the user a hint. This is not correct + * for anything security related */ +pa_bool_t pa_can_realtime(void) { + + if (geteuid() == 0) + return TRUE; + +#if defined(HAVE_SYS_RESOURCE_H) && defined(RLIMIT_RTPRIO) + { + struct rlimit rl; + + if (getrlimit(RLIMIT_RTPRIO, &rl) >= 0) + if (rl.rlim_cur > 0 || rl.rlim_cur == RLIM_INFINITY) + return TRUE; + } +#endif + +#if defined(HAVE_SYS_CAPABILITY_H) && defined(CAP_SYS_NICE) + { + cap_t cap; + + if ((cap = cap_get_proc())) { + cap_flag_value_t flag = CAP_CLEAR; + + if (cap_get_flag(cap, CAP_SYS_NICE, CAP_EFFECTIVE, &flag) >= 0) + if (flag == CAP_SET) { + cap_free(cap); + return TRUE; + } + + cap_free(cap); + } + } +#endif + + return FALSE; +} + +/* This is merely used for giving the user a hint. This is not correct + * for anything security related */ +pa_bool_t pa_can_high_priority(void) { + + if (geteuid() == 0) + return TRUE; + +#if defined(HAVE_SYS_RESOURCE_H) && defined(RLIMIT_RTPRIO) + { + struct rlimit rl; + + if (getrlimit(RLIMIT_NICE, &rl) >= 0) + if (rl.rlim_cur >= 21 || rl.rlim_cur == RLIM_INFINITY) + return TRUE; + } +#endif + +#if defined(HAVE_SYS_CAPABILITY_H) && defined(CAP_SYS_NICE) + { + cap_t cap; + + if ((cap = cap_get_proc())) { + cap_flag_value_t flag = CAP_CLEAR; + + if (cap_get_flag(cap, CAP_SYS_NICE, CAP_EFFECTIVE, &flag) >= 0) + if (flag == CAP_SET) { + cap_free(cap); + return TRUE; + } + + cap_free(cap); + } + } +#endif + + return FALSE; +} + /* Raise the priority of the current process as much as possible that * is <= the specified nice level..*/ int pa_raise_priority(int nice_level) { @@ -612,6 +683,7 @@ void pa_reset_priority(void) { /* Try to parse a boolean string value.*/ int pa_parse_boolean(const char *v) { + pa_assert(v); if (!strcmp(v, "1") || v[0] == 'y' || v[0] == 'Y' || v[0] == 't' || v[0] == 'T' || !strcasecmp(v, "on")) return 1; @@ -1093,11 +1165,11 @@ int pa_unlock_lockfile(const char *fn, int fd) { return r; } -char *pa_get_state_dir(void) { +char *pa_get_runtime_dir(void) { const char *e; char *d; - if ((e = getenv("PULSE_STATE_PATH"))) + if ((e = getenv("PULSE_RUNTIME_PATH"))) d = pa_xstrdup(e); else { char h[PATH_MAX]; @@ -1107,19 +1179,15 @@ char *pa_get_state_dir(void) { return NULL; } - d = pa_sprintf_malloc("%s/.pulse", h); + d = pa_sprintf_malloc("%s" PA_PATH_SEP ".pulse", h); } - mkdir(d, 0755); - - if (access(d, W_OK) == 0) - return d; - - pa_log_error("Failed to set up state directory %s", d); - - pa_xfree(d); + if (pa_make_secure_dir(d, 0700, (pid_t) -1, (pid_t) -1) < 0) { + pa_log_error("Failed to create secure directory: %s", pa_cstrerror(errno)); + return NULL; + } - return NULL; + return d; } /* Try to open a configuration file. If "env" is specified, open the @@ -1128,10 +1196,8 @@ char *pa_get_state_dir(void) { * file system. If "result" is non-NULL, a pointer to a newly * allocated buffer containing the used configuration file is * stored there.*/ -FILE *pa_open_config_file(const char *global, const char *local, const char *env, char **result, const char *mode) { +FILE *pa_open_config_file(const char *global, const char *local, const char *env, char **result) { const char *fn; - char h[PATH_MAX]; - #ifdef OS_IS_WIN32 char buf[PATH_MAX]; @@ -1140,75 +1206,152 @@ FILE *pa_open_config_file(const char *global, const char *local, const char *env #endif if (env && (fn = getenv(env))) { + FILE *f; + #ifdef OS_IS_WIN32 if (!ExpandEnvironmentStrings(fn, buf, PATH_MAX)) return NULL; fn = buf; #endif - if (result) - *result = pa_xstrdup(fn); + if ((f = fopen(fn, "r"))) { + if (result) + *result = pa_xstrdup(fn); + + return f; + } - return fopen(fn, mode); + pa_log_warn("Failed to open configuration file '%s': %s", fn, pa_cstrerror(errno)); + return NULL; } if (local) { const char *e; - char *lfn = NULL; + char *lfn; + char h[PATH_MAX]; + FILE *f; if ((e = getenv("PULSE_CONFIG_PATH"))) - fn = lfn = pa_sprintf_malloc("%s/%s", e, local); - else if (pa_get_home_dir(h, sizeof(h))) { - char *d; + fn = lfn = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", e, local); + else if (pa_get_home_dir(h, sizeof(h))) + fn = lfn = pa_sprintf_malloc("%s" PA_PATH_SEP ".pulse" PA_PATH_SEP "%s", h, local); - d = pa_sprintf_malloc("%s/.pulse", h); - mkdir(d, 0755); - pa_xfree(d); +#ifdef OS_IS_WIN32 + if (!ExpandEnvironmentStrings(lfn, buf, PATH_MAX)) { + pa_xfree(lfn); + return NULL; + } + fn = buf; +#endif + + if ((f = fopen(fn, "r"))) { + if (result) + *result = pa_xstrdup(fn); + + pa_xfree(lfn); + return f; + } - fn = lfn = pa_sprintf_malloc("%s/.pulse/%s", h, local); + if (errno != ENOENT) { + pa_log_warn("Failed to open configuration file '%s': %s", fn, pa_cstrerror(errno)); + pa_xfree(lfn); + return NULL; } - if (lfn) { - FILE *f; + pa_xfree(lfn); + } + + if (global) { + FILE *f; #ifdef OS_IS_WIN32 - if (!ExpandEnvironmentStrings(lfn, buf, PATH_MAX)) - return NULL; - fn = buf; + if (!ExpandEnvironmentStrings(global, buf, PATH_MAX)) + return NULL; + global = buf; #endif - f = fopen(fn, mode); - if (f != NULL) { - if (result) - *result = pa_xstrdup(fn); - pa_xfree(lfn); - return f; - } + if ((f = fopen(global, "r"))) { - if (errno != ENOENT) - pa_log_warn("Failed to open configuration file '%s': %s", lfn, pa_cstrerror(errno)); + if (result) + *result = pa_xstrdup(global); - pa_xfree(lfn); + return f; } - } - - if (!global) { - if (result) - *result = NULL; + } else errno = ENOENT; + + return NULL; +} + +char *pa_find_config_file(const char *global, const char *local, const char *env) { + const char *fn; +#ifdef OS_IS_WIN32 + char buf[PATH_MAX]; + + if (!getenv(PULSE_ROOTENV)) + pa_set_root(NULL); +#endif + + if (env && (fn = getenv(env))) { +#ifdef OS_IS_WIN32 + if (!ExpandEnvironmentStrings(fn, buf, PATH_MAX)) + return NULL; + fn = buf; +#endif + + if (access(fn, R_OK) == 0) + return pa_xstrdup(fn); + + pa_log_warn("Failed to access configuration file '%s': %s", fn, pa_cstrerror(errno)); return NULL; } + if (local) { + const char *e; + char *lfn; + char h[PATH_MAX]; + + if ((e = getenv("PULSE_CONFIG_PATH"))) + fn = lfn = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", e, local); + else if (pa_get_home_dir(h, sizeof(h))) + fn = lfn = pa_sprintf_malloc("%s" PA_PATH_SEP ".pulse" PA_PATH_SEP "%s", h, local); + #ifdef OS_IS_WIN32 - if (!ExpandEnvironmentStrings(global, buf, PATH_MAX)) - return NULL; - global = buf; + if (!ExpandEnvironmentStrings(lfn, buf, PATH_MAX)) { + pa_xfree(lfn); + return NULL; + } + fn = buf; #endif - if (result) - *result = pa_xstrdup(global); + if (access(fn, R_OK) == 0) { + char *r = pa_xstrdup(fn); + pa_xfree(lfn); + return r; + } + + if (errno != ENOENT) { + pa_log_warn("Failed to access configuration file '%s': %s", fn, pa_cstrerror(errno)); + pa_xfree(lfn); + return NULL; + } + + pa_xfree(lfn); + } + + if (global) { +#ifdef OS_IS_WIN32 + if (!ExpandEnvironmentStrings(global, buf, PATH_MAX)) + return NULL; + global = buf; +#endif + + if (access(fn, R_OK) == 0) + return pa_xstrdup(global); + } else + errno = ENOENT; - return fopen(global, mode); + return NULL; } /* Format the specified data as a hexademical string */ @@ -1299,45 +1442,51 @@ int pa_endswith(const char *s, const char *sfx) { return l1 >= l2 && strcmp(s+l1-l2, sfx) == 0; } -/* if fn is null return the PulseAudio run time path in s (/tmp/pulse) - * if fn is non-null and starts with / return fn in s - * otherwise append fn to the run time path and return it in s */ -char *pa_runtime_path(const char *fn, char *s, size_t l) { - const char *e; +pa_bool_t pa_is_path_absolute(const char *fn) { + pa_assert(fn); #ifndef OS_IS_WIN32 - if (fn && *fn == '/') + return *fn == '/'; #else - if (fn && strlen(fn) >= 3 && isalpha(fn[0]) && fn[1] == ':' && fn[2] == '\\') + return strlen(fn) >= 3 && isalpha(fn[0]) && fn[1] == ':' && fn[2] == '\\'; #endif - return pa_strlcpy(s, fn, l); +} - if ((e = getenv("PULSE_RUNTIME_PATH"))) { +char *pa_make_path_absolute(const char *p) { + char *r; + char *cwd; - if (fn) - pa_snprintf(s, l, "%s%c%s", e, PA_PATH_SEP_CHAR, fn); - else - pa_snprintf(s, l, "%s", e); + pa_assert(p); - } else { - char u[256]; + if (pa_is_path_absolute(p)) + return pa_xstrdup(p); - if (fn) - pa_snprintf(s, l, "%s%s%c%s", PA_USER_RUNTIME_PATH_PREFIX, pa_get_user_name(u, sizeof(u)), PA_PATH_SEP_CHAR, fn); - else - pa_snprintf(s, l, "%s%s", PA_USER_RUNTIME_PATH_PREFIX, pa_get_user_name(u, sizeof(u))); - } + if (!(cwd = pa_getcwd())) + return pa_xstrdup(p); + r = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", cwd, p); + pa_xfree(cwd); + return r; +} -#ifdef OS_IS_WIN32 - { - char buf[l]; - strcpy(buf, s); - ExpandEnvironmentStrings(buf, s, l); - } -#endif +/* if fn is null return the PulseAudio run time path in s (~/.pulse) + * if fn is non-null and starts with / return fn + * otherwise append fn to the run time path and return it */ +char *pa_runtime_path(const char *fn) { + char *rtp; - return s; + if (pa_is_path_absolute(fn)) + return pa_xstrdup(fn); + + rtp = pa_get_runtime_dir(); + + if (fn) { + char *r; + r = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", rtp, fn); + pa_xfree(rtp); + return r; + } else + return rtp; } /* Convert the string s to a signed integer in *ret_i */ @@ -1484,23 +1633,6 @@ char *pa_getcwd(void) { } } -char *pa_make_path_absolute(const char *p) { - char *r; - char *cwd; - - pa_assert(p); - - if (p[0] == '/') - return pa_xstrdup(p); - - if (!(cwd = pa_getcwd())) - return pa_xstrdup(p); - - r = pa_sprintf_malloc("%s/%s", cwd, p); - pa_xfree(cwd); - return r; -} - void *pa_will_need(const void *p, size_t l) { #ifdef RLIMIT_MEMLOCK struct rlimit rlim; @@ -1606,3 +1738,249 @@ char *pa_readlink(const char *p) { l *= 2; } } + +int pa_close_all(int except_fd, ...) { + va_list ap; + int n = 0, i, r; + int *p; + + va_start(ap, except_fd); + + if (except_fd >= 0) + for (n = 1; va_arg(ap, int) >= 0; n++) + ; + + va_end(ap); + + p = pa_xnew(int, n+1); + + va_start(ap, except_fd); + + i = 0; + if (except_fd >= 0) { + p[i++] = except_fd; + + while ((p[i++] = va_arg(ap, int)) >= 0) + ; + } + p[i] = -1; + + va_end(ap); + + r = pa_close_allv(p); + free(p); + + return r; +} + +int pa_close_allv(const int except_fds[]) { + struct rlimit rl; + int fd; + int saved_errno; + +#ifdef __linux__ + + DIR *d; + + if ((d = opendir("/proc/self/fd"))) { + + struct dirent *de; + + while ((de = readdir(d))) { + long l; + char *e = NULL; + int i; + + if (de->d_name[0] == '.') + continue; + + errno = 0; + l = strtol(de->d_name, &e, 10); + if (errno != 0 || !e || *e) { + closedir(d); + errno = EINVAL; + return -1; + } + + fd = (int) l; + + if ((long) fd != l) { + closedir(d); + errno = EINVAL; + return -1; + } + + if (fd <= 3) + continue; + + if (fd == dirfd(d)) + continue; + + for (i = 0; except_fds[i] >= 0; i++) + if (except_fds[i] == fd) + continue; + + if (close(fd) < 0) { + saved_errno = errno; + closedir(d); + errno = saved_errno; + + return -1; + } + } + + closedir(d); + return 0; + } + +#endif + + if (getrlimit(RLIMIT_NOFILE, &rl) < 0) + return -1; + + for (fd = 0; fd < (int) rl.rlim_max; fd++) { + int i; + + if (fd <= 3) + continue; + + for (i = 0; except_fds[i] >= 0; i++) + if (except_fds[i] == fd) + continue; + + if (close(fd) < 0 && errno != EBADF) + return -1; + } + + return 0; +} + +int pa_unblock_sigs(int except, ...) { + va_list ap; + int n = 0, i, r; + int *p; + + va_start(ap, except); + + if (except >= 1) + for (n = 1; va_arg(ap, int) >= 0; n++) + ; + + va_end(ap); + + p = pa_xnew(int, n+1); + + va_start(ap, except); + + i = 0; + if (except >= 1) { + p[i++] = except; + + while ((p[i++] = va_arg(ap, int)) >= 0) + ; + } + p[i] = -1; + + va_end(ap); + + r = pa_unblock_sigsv(p); + pa_xfree(p); + + return r; +} + +int pa_unblock_sigsv(const int except[]) { + int i; + sigset_t ss; + + if (sigemptyset(&ss) < 0) + return -1; + + for (i = 0; except[i] > 0; i++) + if (sigaddset(&ss, except[i]) < 0) + return -1; + + return sigprocmask(SIG_SETMASK, &ss, NULL); +} + +int pa_reset_sigs(int except, ...) { + va_list ap; + int n = 0, i, r; + int *p; + + va_start(ap, except); + + if (except >= 1) + for (n = 1; va_arg(ap, int) >= 0; n++) + ; + + va_end(ap); + + p = pa_xnew(int, n+1); + + va_start(ap, except); + + i = 0; + if (except >= 1) { + p[i++] = except; + + while ((p[i++] = va_arg(ap, int)) >= 0) + ; + } + p[i] = -1; + + va_end(ap); + + r = pa_reset_sigsv(p); + pa_xfree(p); + + return r; +} + +int pa_reset_sigsv(const int except[]) { + int sig; + + for (sig = 1; sig < _NSIG; sig++) { + int reset = 1; + + switch (sig) { + case SIGKILL: + case SIGSTOP: + reset = 0; + break; + + default: { + int i; + + for (i = 0; except[i] > 0; i++) { + if (sig == except[i]) { + reset = 0; + break; + } + } + } + } + + if (reset) { + struct sigaction sa; + + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = SIG_DFL; + + /* On Linux the first two RT signals are reserved by + * glibc, and sigaction() will return EINVAL for them. */ + if ((sigaction(sig, &sa, NULL) < 0)) + if (errno != EINVAL) + return -1; + } + } + + return 0; +} + +void pa_set_env(const char *key, const char *value) { + pa_assert(key); + pa_assert(value); + + putenv(pa_sprintf_malloc("%s=%s", key, value)); +} diff --git a/src/pulsecore/core-util.h b/src/pulsecore/core-util.h index d5c0a3f6..49315b55 100644 --- a/src/pulsecore/core-util.h +++ b/src/pulsecore/core-util.h @@ -30,11 +30,27 @@ #include <stdarg.h> #include <stdio.h> +#ifdef HAVE_SYS_RESOURCE_H +#include <sys/resource.h> +#endif + #include <pulse/gccmacro.h> #include <pulsecore/macro.h> struct timeval; +/* These resource limits are pretty new on Linux, let's define them + * here manually, in case the kernel is newer than the glibc */ +#if !defined(RLIMIT_NICE) && defined(__linux__) +#define RLIMIT_NICE 13 +#endif +#if !defined(RLIMIT_RTPRIO) && defined(__linux__) +#define RLIMIT_RTPRIO 14 +#endif +#if !defined(RLIMIT_RTTIME) && defined(__linux__) +#define RLIMIT_RTTIME 15 +#endif + void pa_make_fd_nonblock(int fd); void pa_make_fd_cloexec(int fd); @@ -61,6 +77,9 @@ int pa_make_realtime(int rtprio); int pa_raise_priority(int nice_level); void pa_reset_priority(void); +pa_bool_t pa_can_realtime(void); +pa_bool_t pa_can_high_priority(void); + int pa_parse_boolean(const char *s) PA_GCC_PURE; static inline const char *pa_yes_no(pa_bool_t b) { @@ -71,6 +90,10 @@ static inline const char *pa_strnull(const char *x) { return x ? x : "(null)"; } +static inline const char *pa_strempty(const char *x) { + return x ? x : ""; +} + char *pa_split(const char *c, const char*delimiters, const char **state); char *pa_split_spaces(const char *c, const char **state); @@ -88,15 +111,17 @@ int pa_lock_fd(int fd, int b); int pa_lock_lockfile(const char *fn); int pa_unlock_lockfile(const char *fn, int fd); -FILE *pa_open_config_file(const char *global, const char *local, const char *env, char **result, const char *mode); - char *pa_hexstr(const uint8_t* d, size_t dlength, char *s, size_t slength); size_t pa_parsehex(const char *p, uint8_t *d, size_t dlength); int pa_startswith(const char *s, const char *pfx) PA_GCC_PURE; int pa_endswith(const char *s, const char *sfx) PA_GCC_PURE; -char *pa_runtime_path(const char *fn, char *s, size_t l); +FILE *pa_open_config_file(const char *global, const char *local, const char *env, char **result); +char* pa_find_config_file(const char *global, const char *local, const char *env); + +char *pa_get_runtime_dir(void); +char *pa_runtime_path(const char *fn); int pa_atoi(const char *s, int32_t *ret_i); int pa_atou(const char *s, uint32_t *ret_u); @@ -108,6 +133,7 @@ char *pa_truncate_utf8(char *c, size_t l); char *pa_getcwd(void); char *pa_make_path_absolute(const char *p); +pa_bool_t pa_is_path_absolute(const char *p); void *pa_will_need(const void *p, size_t l); @@ -133,6 +159,13 @@ void pa_close_pipe(int fds[2]); char *pa_readlink(const char *p); -char *pa_get_state_dir(void); +int pa_close_all(int except_fd, ...); +int pa_close_allv(const int except_fds[]); +int pa_unblock_sigs(int except, ...); +int pa_unblock_sigsv(const int except[]); +int pa_reset_sigs(int except, ...); +int pa_reset_sigsv(const int except[]); + +void pa_set_env(const char *key, const char *value); #endif diff --git a/src/pulsecore/native-common.h b/src/pulsecore/native-common.h index 51f2b309..56f9037e 100644 --- a/src/pulsecore/native-common.h +++ b/src/pulsecore/native-common.h @@ -147,6 +147,9 @@ enum { PA_COMMAND_REMOVE_PLAYBACK_STREAM_PROPLIST, PA_COMMAND_REMOVE_CLIENT_PROPLIST, + /* SERVER->CLIENT */ + PA_COMMAND_STARTED, + PA_COMMAND_MAX }; diff --git a/src/pulsecore/pid.c b/src/pulsecore/pid.c index f3c9faaa..2ff132bb 100644 --- a/src/pulsecore/pid.c +++ b/src/pulsecore/pid.c @@ -144,16 +144,16 @@ fail: int pa_pid_file_create(void) { int fd = -1; int ret = -1; - char fn[PATH_MAX]; char t[20]; pid_t pid; size_t l; + char *fn; #ifdef OS_IS_WIN32 HANDLE process; #endif - pa_runtime_path("pid", fn, sizeof(fn)); + fn = pa_runtime_path("pid"); if ((fd = open_pid_file(fn, O_CREAT|O_RDWR)) < 0) goto fail; @@ -200,17 +200,19 @@ fail: } } + pa_xfree(fn); + return ret; } /* Remove the PID file, if it is ours */ int pa_pid_file_remove(void) { int fd = -1; - char fn[PATH_MAX]; + char *fn; int ret = -1; pid_t pid; - pa_runtime_path("pid", fn, sizeof(fn)); + fn = pa_runtime_path("pid"); if ((fd = open_pid_file(fn, O_RDWR)) < 0) { pa_log_warn("Failed to open PID file '%s': %s", fn, pa_cstrerror(errno)); @@ -254,6 +256,8 @@ fail: } } + pa_xfree(fn); + return ret; } @@ -272,7 +276,7 @@ int pa_pid_file_check_running(pid_t *pid, const char *binary_name) { * process. */ int pa_pid_file_kill(int sig, pid_t *pid, const char *binary_name) { int fd = -1; - char fn[PATH_MAX]; + char *fn; int ret = -1; pid_t _pid; #ifdef __linux__ @@ -281,7 +285,7 @@ int pa_pid_file_kill(int sig, pid_t *pid, const char *binary_name) { if (!pid) pid = &_pid; - pa_runtime_path("pid", fn, sizeof(fn)); + fn = pa_runtime_path("pid"); if ((fd = open_pid_file(fn, O_RDONLY)) < 0) goto fail; @@ -296,7 +300,7 @@ int pa_pid_file_kill(int sig, pid_t *pid, const char *binary_name) { if ((e = pa_readlink(fn))) { char *f = pa_path_get_filename(e); if (strcmp(f, binary_name) -#if defined(__OPTIMIZE__) +#if !defined(__OPTIMIZE__) /* libtool likes to rename our binary names ... */ && !(pa_startswith(f, "lt-") && strcmp(f+3, binary_name) == 0) #endif @@ -319,6 +323,8 @@ fail: pa_xfree(e); #endif + pa_xfree(fn); + return ret; } diff --git a/src/pulsecore/protocol-cli.c b/src/pulsecore/protocol-cli.c index ceb6ae4d..2f797a14 100644 --- a/src/pulsecore/protocol-cli.c +++ b/src/pulsecore/protocol-cli.c @@ -82,7 +82,7 @@ pa_protocol_cli* pa_protocol_cli_new(pa_core *core, pa_socket_server *server, pa p = pa_xnew(pa_protocol_cli, 1); p->module = m; p->core = core; - p->server = server; + p->server = pa_socket_server_ref(server); p->connections = pa_idxset_new(NULL, NULL); pa_socket_server_set_callback(p->server, on_connection, p); diff --git a/src/pulsecore/protocol-esound.c b/src/pulsecore/protocol-esound.c index 59a4208f..388808a5 100644 --- a/src/pulsecore/protocol-esound.c +++ b/src/pulsecore/protocol-esound.c @@ -1433,7 +1433,7 @@ pa_protocol_esound* pa_protocol_esound_new(pa_core*core, pa_socket_server *serve p->core = core; p->module = m; p->public = public; - p->server = server; + p->server = pa_socket_server_ref(server); pa_socket_server_set_callback(p->server, on_connection, p); p->connections = pa_idxset_new(NULL, NULL); diff --git a/src/pulsecore/protocol-http.c b/src/pulsecore/protocol-http.c index 589eba4f..bc2e9af6 100644 --- a/src/pulsecore/protocol-http.c +++ b/src/pulsecore/protocol-http.c @@ -255,7 +255,7 @@ pa_protocol_http* pa_protocol_http_new(pa_core *core, pa_socket_server *server, p = pa_xnew(pa_protocol_http, 1); p->module = m; p->core = core; - p->server = server; + p->server = pa_socket_server_ref(server); p->connections = pa_idxset_new(NULL, NULL); pa_socket_server_set_callback(p->server, on_connection, p); diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c index ca14b955..5fee4ccf 100644 --- a/src/pulsecore/protocol-native.c +++ b/src/pulsecore/protocol-native.c @@ -105,7 +105,6 @@ typedef struct playback_stream { pa_bool_t drain_request; uint32_t drain_tag; uint32_t syncid; - uint64_t underrun; /* length of underrun */ pa_atomic_t missing; size_t minreq; @@ -193,7 +192,8 @@ enum { PLAYBACK_STREAM_MESSAGE_REQUEST_DATA, /* data requested from sink input from the main loop */ PLAYBACK_STREAM_MESSAGE_UNDERFLOW, PLAYBACK_STREAM_MESSAGE_OVERFLOW, - PLAYBACK_STREAM_MESSAGE_DRAIN_ACK + PLAYBACK_STREAM_MESSAGE_DRAIN_ACK, + PLAYBACK_STREAM_MESSAGE_STARTED }; enum { @@ -689,10 +689,24 @@ static int playback_stream_process_msg(pa_msgobject *o, int code, void*userdata, break; } + case PLAYBACK_STREAM_MESSAGE_STARTED: + + if (s->connection->version >= 13) { + pa_tagstruct *t; + + /* Notify the user we're overflowed*/ + t = pa_tagstruct_new(NULL, 0); + pa_tagstruct_putu32(t, PA_COMMAND_STARTED); + pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */ + pa_tagstruct_putu32(t, s->index); + pa_pstream_send_tagstruct(s->connection->pstream, t); + } + + break; + case PLAYBACK_STREAM_MESSAGE_DRAIN_ACK: pa_pstream_send_simple_ack(s->connection->pstream, PA_PTR_TO_UINT(userdata)); break; - } return 0; @@ -886,7 +900,6 @@ static playback_stream* playback_stream_new( s->connection = c; s->syncid = syncid; s->sink_input = sink_input; - s->underrun = (uint64_t) -1; s->sink_input->parent.process_msg = sink_input_process_msg; s->sink_input->pop = sink_input_pop_cb; @@ -1091,7 +1104,7 @@ static void handle_seek(playback_stream *s, int64_t indexw) { /* pa_log("handle_seek: %llu -- %i", (unsigned long long) s->underrun, pa_memblockq_is_readable(s->memblockq)); */ - if (s->underrun != 0) { + if (s->sink_input->thread_info.underrun_for > 0) { /* pa_log("%lu vs. %lu", (unsigned long) pa_memblockq_get_length(s->memblockq), (unsigned long) pa_memblockq_get_prebuf(s->memblockq)); */ @@ -1099,13 +1112,13 @@ static void handle_seek(playback_stream *s, int64_t indexw) { size_t u = pa_memblockq_get_length(s->memblockq); - if (u >= s->underrun) - u = s->underrun; + if (u >= s->sink_input->thread_info.underrun_for) + u = s->sink_input->thread_info.underrun_for; /* We just ended an underrun, let's ask the sink * to rewrite */ - s->sink_input->thread_info.ignore_rewind = TRUE; - pa_sink_input_request_rewind(s->sink_input, u, TRUE); + + pa_sink_input_request_rewind(s->sink_input, u, TRUE, TRUE); } } else { @@ -1117,7 +1130,7 @@ static void handle_seek(playback_stream *s, int64_t indexw) { /* OK, the sink already asked for this data, so * let's have it usk us again */ - pa_sink_input_request_rewind(s->sink_input, indexr - indexw, FALSE); + pa_sink_input_request_rewind(s->sink_input, indexr - indexw, FALSE, FALSE); } request_bytes(s); @@ -1272,12 +1285,9 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk if (s->drain_request && pa_sink_input_safe_to_remove(i)) { s->drain_request = FALSE; pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_DRAIN_ACK, PA_UINT_TO_PTR(s->drain_tag), 0, NULL, NULL); - } else if (s->underrun == 0) + } else if (i->thread_info.playing_for > 0) pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_UNDERFLOW, NULL, 0, NULL, NULL); - if (s->underrun != (size_t) -1) - s->underrun += nbytes; - /* pa_log("added %llu bytes, total is %llu", (unsigned long long) nbytes, (unsigned long long) s->underrun); */ request_bytes(s); @@ -1287,7 +1297,8 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk /* pa_log("NOTUNDERRUN"); */ - s->underrun = 0; + if (i->thread_info.underrun_for > 0) + pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_STARTED, NULL, 0, NULL, NULL); pa_memblockq_drop(s->memblockq, chunk->length); request_bytes(s); @@ -1303,7 +1314,7 @@ static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) { playback_stream_assert_ref(s); /* If we are in an underrun, then we don't rewind */ - if (s->underrun != 0) + if (i->thread_info.underrun_for > 0) return; pa_memblockq_rewind(s->memblockq, nbytes); @@ -2120,11 +2131,17 @@ static void command_get_playback_latency(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_ pa_tagstruct_put_usec(reply, latency); pa_tagstruct_put_usec(reply, 0); - pa_tagstruct_put_boolean(reply, pa_sink_input_get_state(s->sink_input) == PA_SINK_INPUT_RUNNING); + pa_tagstruct_put_boolean(reply, s->sink_input->thread_info.playing_for > 0); pa_tagstruct_put_timeval(reply, &tv); pa_tagstruct_put_timeval(reply, pa_gettimeofday(&now)); pa_tagstruct_puts64(reply, s->write_index); pa_tagstruct_puts64(reply, s->read_index); + + if (c->version >= 13) { + pa_tagstruct_putu64(reply, s->sink_input->thread_info.underrun_for); + pa_tagstruct_putu64(reply, s->sink_input->thread_info.playing_for); + } + pa_pstream_send_tagstruct(c->pstream, reply); } @@ -2152,7 +2169,7 @@ static void command_get_record_latency(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UN reply = reply_new(tag); pa_tagstruct_put_usec(reply, s->source_output->source->monitor_of ? pa_sink_get_latency(s->source_output->source->monitor_of) : 0); pa_tagstruct_put_usec(reply, pa_source_get_latency(s->source_output->source)); - pa_tagstruct_put_boolean(reply, FALSE); + pa_tagstruct_put_boolean(reply, pa_source_get_state(s->source_output->source) == PA_SOURCE_RUNNING); pa_tagstruct_put_timeval(reply, &tv); pa_tagstruct_put_timeval(reply, pa_gettimeofday(&now)); pa_tagstruct_puts64(reply, pa_memblockq_get_write_index(s->memblockq)); @@ -3937,7 +3954,7 @@ static pa_protocol_native* protocol_new_internal(pa_core *c, pa_module *m, pa_mo #ifdef HAVE_CREDS { - pa_bool_t a = 1; + pa_bool_t a = TRUE; if (pa_modargs_get_value_boolean(ma, "auth-group-enabled", &a) < 0) { pa_log("auth-group-enabled= expects a boolean argument."); return NULL; @@ -3982,7 +3999,7 @@ pa_protocol_native* pa_protocol_native_new(pa_core *core, pa_socket_server *serv if (!(p = protocol_new_internal(core, m, ma))) return NULL; - p->server = server; + p->server = pa_socket_server_ref(server); pa_socket_server_set_callback(p->server, on_connection, p); if (pa_socket_server_get_address(p->server, t, sizeof(t))) { diff --git a/src/pulsecore/protocol-simple.c b/src/pulsecore/protocol-simple.c index 3ee2a058..8ec38fe4 100644 --- a/src/pulsecore/protocol-simple.c +++ b/src/pulsecore/protocol-simple.c @@ -587,7 +587,7 @@ pa_protocol_simple* pa_protocol_simple_new(pa_core *core, pa_socket_server *serv p = pa_xnew0(pa_protocol_simple, 1); p->module = m; p->core = core; - p->server = server; + p->server = pa_socket_server_ref(server); p->connections = pa_idxset_new(NULL, NULL); p->sample_spec = core->default_sample_spec; diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c index 8df36876..1da920a9 100644 --- a/src/pulsecore/sink-input.c +++ b/src/pulsecore/sink-input.c @@ -106,6 +106,7 @@ static void reset_callbacks(pa_sink_input *i) { i->moved = NULL; i->kill = NULL; i->get_latency = NULL; + i->state_change = NULL; } pa_sink_input* pa_sink_input_new( @@ -249,8 +250,8 @@ pa_sink_input* pa_sink_input_new( i->thread_info.muted = i->muted; i->thread_info.requested_sink_latency = (pa_usec_t) -1; i->thread_info.rewrite_nbytes = 0; - i->thread_info.since_underrun = 0; - i->thread_info.ignore_rewind = FALSE; + i->thread_info.underrun_for = (uint64_t) -1; + i->thread_info.playing_for = 0; i->thread_info.render_memblockq = pa_memblockq_new( 0, @@ -328,7 +329,7 @@ void pa_sink_input_unlink(pa_sink_input *i) { pa_sink_input_ref(i); - linked = PA_SINK_INPUT_LINKED(i->state); + linked = PA_SINK_INPUT_IS_LINKED(i->state); if (linked) pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_UNLINK], i); @@ -344,12 +345,11 @@ void pa_sink_input_unlink(pa_sink_input *i) { if (pa_idxset_remove_by_data(i->sink->inputs, i, NULL)) pa_sink_input_unref(i); - if (linked) { + update_n_corked(i, PA_SINK_INPUT_UNLINKED); + i->state = PA_SINK_INPUT_UNLINKED; + + if (linked) pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_REMOVE_INPUT, i, 0, NULL); - sink_input_set_state(i, PA_SINK_INPUT_UNLINKED); - pa_sink_update_status(i->sink); - } else - i->state = PA_SINK_INPUT_UNLINKED; reset_callbacks(i); @@ -368,7 +368,7 @@ static void sink_input_free(pa_object *o) { pa_assert(i); pa_assert(pa_sink_input_refcnt(i) == 0); - if (PA_SINK_INPUT_LINKED(i->state)) + if (PA_SINK_INPUT_IS_LINKED(i->state)) pa_sink_input_unlink(i); pa_log_info("Freeing input %u \"%s\"", i->index, pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_MEDIA_NAME))); @@ -402,7 +402,7 @@ void pa_sink_input_put(pa_sink_input *i) { state = i->flags & PA_SINK_INPUT_START_CORKED ? PA_SINK_INPUT_CORKED : PA_SINK_INPUT_RUNNING; update_n_corked(i, state); - i->thread_info.state = i->state = state; + i->state = state; pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_ADD_INPUT, i, 0, NULL); @@ -416,7 +416,7 @@ void pa_sink_input_put(pa_sink_input *i) { void pa_sink_input_kill(pa_sink_input*i) { pa_sink_input_assert_ref(i); - pa_assert(PA_SINK_INPUT_LINKED(i->state)); + pa_assert(PA_SINK_INPUT_IS_LINKED(i->state)); if (i->kill) i->kill(i); @@ -426,7 +426,7 @@ pa_usec_t pa_sink_input_get_latency(pa_sink_input *i) { pa_usec_t r = 0; pa_sink_input_assert_ref(i); - pa_assert(PA_SINK_INPUT_LINKED(i->state)); + pa_assert(PA_SINK_INPUT_IS_LINKED(i->state)); if (pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_GET_LATENCY, &r, 0, NULL) < 0) r = 0; @@ -445,7 +445,7 @@ int pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink frames */, pa size_t ilength; pa_sink_input_assert_ref(i); - pa_assert(PA_SINK_INPUT_LINKED(i->thread_info.state)); + pa_assert(PA_SINK_INPUT_IS_LINKED(i->thread_info.state)); pa_assert(pa_frame_aligned(slength, &i->sink->sample_spec)); pa_assert(chunk); pa_assert(volume); @@ -510,7 +510,9 @@ int pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink frames */, pa pa_atomic_store(&i->thread_info.drained, 1); pa_memblockq_seek(i->thread_info.render_memblockq, slength, PA_SEEK_RELATIVE_ON_READ); - i->thread_info.since_underrun = 0; + i->thread_info.playing_for = 0; + if (i->thread_info.underrun_for != (uint64_t) -1) + i->thread_info.underrun_for += slength; break; } @@ -519,7 +521,8 @@ int pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink frames */, pa pa_assert(tchunk.length > 0); pa_assert(tchunk.memblock); - i->thread_info.since_underrun += tchunk.length; + i->thread_info.underrun_for = 0; + i->thread_info.playing_for += tchunk.length; while (tchunk.length > 0) { pa_memchunk wchunk; @@ -590,7 +593,7 @@ int pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink frames */, pa void pa_sink_input_drop(pa_sink_input *i, size_t nbytes /* in sink sample spec */) { pa_sink_input_assert_ref(i); - pa_assert(PA_SINK_INPUT_LINKED(i->thread_info.state)); + pa_assert(PA_SINK_INPUT_IS_LINKED(i->thread_info.state)); pa_assert(pa_frame_aligned(nbytes, &i->sink->sample_spec)); pa_assert(nbytes > 0); @@ -610,13 +613,13 @@ void pa_sink_input_drop(pa_sink_input *i, size_t nbytes /* in sink sample spec * void pa_sink_input_process_rewind(pa_sink_input *i, size_t nbytes /* in sink sample spec */) { pa_sink_input_assert_ref(i); - pa_assert(PA_SINK_INPUT_LINKED(i->thread_info.state)); + pa_assert(PA_SINK_INPUT_IS_LINKED(i->thread_info.state)); pa_assert(pa_frame_aligned(nbytes, &i->sink->sample_spec)); /* pa_log_debug("rewind(%lu, %lu)", (unsigned long) nbytes, (unsigned long) i->thread_info.rewrite_nbytes); */ - if (i->thread_info.ignore_rewind) { - i->thread_info.ignore_rewind = FALSE; + if (i->thread_info.underrun_for > 0) { + /* We don't rewind when we are underrun */ i->thread_info.rewrite_nbytes = 0; return; } @@ -668,7 +671,7 @@ void pa_sink_input_process_rewind(pa_sink_input *i, size_t nbytes /* in sink sam /* Called from thread context */ void pa_sink_input_update_max_rewind(pa_sink_input *i, size_t nbytes /* in the sink's sample spec */) { pa_sink_input_assert_ref(i); - pa_assert(PA_SINK_INPUT_LINKED(i->thread_info.state)); + pa_assert(PA_SINK_INPUT_IS_LINKED(i->thread_info.state)); pa_assert(pa_frame_aligned(nbytes, &i->sink->sample_spec)); pa_memblockq_set_maxrewind(i->thread_info.render_memblockq, nbytes); @@ -677,21 +680,41 @@ void pa_sink_input_update_max_rewind(pa_sink_input *i, size_t nbytes /* in the i->update_max_rewind(i, i->thread_info.resampler ? pa_resampler_request(i->thread_info.resampler, nbytes) : nbytes); } -pa_usec_t pa_sink_input_set_requested_latency(pa_sink_input *i, pa_usec_t usec) { - pa_sink_input_assert_ref(i); +static pa_usec_t fixup_latency(pa_sink *s, pa_usec_t usec) { + pa_sink_assert_ref(s); - if (usec != (pa_usec_t) -1) { + if (usec == (pa_usec_t) -1) + return usec; - if (i->sink->max_latency > 0 && usec > i->sink->max_latency) - usec = i->sink->max_latency; + if (s->max_latency > 0 && usec > s->max_latency) + usec = s->max_latency; - if (i->sink->min_latency > 0 && usec < i->sink->min_latency) - usec = i->sink->min_latency; - } + if (s->min_latency > 0 && usec < s->min_latency) + usec = s->min_latency; + + return usec; +} + +pa_usec_t pa_sink_input_set_requested_latency_within_thread(pa_sink_input *i, pa_usec_t usec) { + + usec = fixup_latency(i->sink, usec); + + i->thread_info.requested_sink_latency = usec; + pa_sink_invalidate_requested_latency(i->sink); - if (PA_SINK_INPUT_LINKED(i->state)) + return usec; +} + +pa_usec_t pa_sink_input_set_requested_latency(pa_sink_input *i, pa_usec_t usec) { + pa_sink_input_assert_ref(i); + + usec = fixup_latency(i->sink, usec); + + if (PA_SINK_INPUT_IS_LINKED(i->state)) pa_asyncmsgq_post(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_REQUESTED_LATENCY, NULL, (int64_t) usec, NULL, NULL); else { + /* If this sink input is not realized yet, we have to touch + * the thread info data directly */ i->thread_info.requested_sink_latency = usec; i->sink->thread_info.requested_latency_valid = FALSE; } @@ -701,7 +724,7 @@ pa_usec_t pa_sink_input_set_requested_latency(pa_sink_input *i, pa_usec_t usec) void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume) { pa_sink_input_assert_ref(i); - pa_assert(PA_SINK_INPUT_LINKED(i->state)); + pa_assert(PA_SINK_INPUT_IS_LINKED(i->state)); if (pa_cvolume_equal(&i->volume, volume)) return; @@ -714,7 +737,7 @@ void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume) { const pa_cvolume *pa_sink_input_get_volume(pa_sink_input *i) { pa_sink_input_assert_ref(i); - pa_assert(PA_SINK_INPUT_LINKED(i->state)); + pa_assert(PA_SINK_INPUT_IS_LINKED(i->state)); return &i->volume; } @@ -722,7 +745,7 @@ const pa_cvolume *pa_sink_input_get_volume(pa_sink_input *i) { void pa_sink_input_set_mute(pa_sink_input *i, pa_bool_t mute) { pa_assert(i); pa_sink_input_assert_ref(i); - pa_assert(PA_SINK_INPUT_LINKED(i->state)); + pa_assert(PA_SINK_INPUT_IS_LINKED(i->state)); if (!i->muted == !mute) return; @@ -735,21 +758,21 @@ void pa_sink_input_set_mute(pa_sink_input *i, pa_bool_t mute) { int pa_sink_input_get_mute(pa_sink_input *i) { pa_sink_input_assert_ref(i); - pa_assert(PA_SINK_INPUT_LINKED(i->state)); + pa_assert(PA_SINK_INPUT_IS_LINKED(i->state)); return !!i->muted; } void pa_sink_input_cork(pa_sink_input *i, pa_bool_t b) { pa_sink_input_assert_ref(i); - pa_assert(PA_SINK_INPUT_LINKED(i->state)); + pa_assert(PA_SINK_INPUT_IS_LINKED(i->state)); sink_input_set_state(i, b ? PA_SINK_INPUT_CORKED : PA_SINK_INPUT_RUNNING); } int pa_sink_input_set_rate(pa_sink_input *i, uint32_t rate) { pa_sink_input_assert_ref(i); - pa_assert(PA_SINK_INPUT_LINKED(i->state)); + pa_assert(PA_SINK_INPUT_IS_LINKED(i->state)); pa_return_val_if_fail(i->thread_info.resampler, -1); if (i->sample_spec.rate == rate) @@ -780,7 +803,7 @@ void pa_sink_input_set_name(pa_sink_input *i, const char *name) { else pa_proplist_unset(i->proplist, PA_PROP_MEDIA_NAME); - if (PA_SINK_INPUT_LINKED(i->state)) { + if (PA_SINK_INPUT_IS_LINKED(i->state)) { pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_PROPLIST_CHANGED], i); pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index); } @@ -792,7 +815,7 @@ pa_resample_method_t pa_sink_input_get_resample_method(pa_sink_input *i) { return i->resample_method; } -int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, int immediately) { +int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, pa_bool_t immediately) { pa_resampler *new_resampler; pa_sink *origin; pa_usec_t silence_usec = 0; @@ -800,7 +823,7 @@ int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, int immediately) { pa_sink_input_move_hook_data hook_data; pa_sink_input_assert_ref(i); - pa_assert(PA_SINK_INPUT_LINKED(i->state)); + pa_assert(PA_SINK_INPUT_IS_LINKED(i->state)); pa_sink_assert_ref(dest); origin = i->sink; @@ -983,7 +1006,7 @@ int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, int immediately) { return 0; } -static void set_state(pa_sink_input *i, pa_sink_input_state_t state) { +void pa_sink_input_set_state_within_thread(pa_sink_input *i, pa_sink_input_state_t state) { pa_sink_input_assert_ref(i); if ((state == PA_SINK_INPUT_DRAINED || state == PA_SINK_INPUT_RUNNING) && @@ -998,17 +1021,18 @@ static void set_state(pa_sink_input *i, pa_sink_input_state_t state) { /* This will tell the implementing sink input driver to rewind * so that the unplayed already mixed data is not lost */ - pa_sink_input_request_rewind(i, 0, FALSE); + pa_sink_input_request_rewind(i, 0, FALSE, FALSE); } else if (i->thread_info.state == PA_SINK_INPUT_CORKED && state != PA_SINK_INPUT_CORKED) { /* OK, we're being uncorked. Make sure we're not rewound when * the hw buffer is remixed and request a remix. */ - i->thread_info.ignore_rewind = TRUE; - i->thread_info.since_underrun = 0; - pa_sink_request_rewind(i->sink, 0); + pa_sink_input_request_rewind(i, 0, TRUE, TRUE); } + if (i->state_change) + i->state_change(i, state); + i->thread_info.state = state; } @@ -1017,17 +1041,17 @@ int pa_sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t pa_sink_input *i = PA_SINK_INPUT(o); pa_sink_input_assert_ref(i); - pa_assert(PA_SINK_INPUT_LINKED(i->thread_info.state)); + pa_assert(PA_SINK_INPUT_IS_LINKED(i->thread_info.state)); switch (code) { case PA_SINK_INPUT_MESSAGE_SET_VOLUME: i->thread_info.volume = *((pa_cvolume*) userdata); - pa_sink_input_request_rewind(i, 0, FALSE); + pa_sink_input_request_rewind(i, 0, FALSE, FALSE); return 0; case PA_SINK_INPUT_MESSAGE_SET_MUTE: i->thread_info.muted = PA_PTR_TO_UINT(userdata); - pa_sink_input_request_rewind(i, 0, FALSE); + pa_sink_input_request_rewind(i, 0, FALSE, FALSE); return 0; case PA_SINK_INPUT_MESSAGE_GET_LATENCY: { @@ -1048,22 +1072,20 @@ int pa_sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t case PA_SINK_INPUT_MESSAGE_SET_STATE: { pa_sink_input *ssync; - set_state(i, PA_PTR_TO_UINT(userdata)); + pa_sink_input_set_state_within_thread(i, PA_PTR_TO_UINT(userdata)); for (ssync = i->thread_info.sync_prev; ssync; ssync = ssync->thread_info.sync_prev) - set_state(ssync, PA_PTR_TO_UINT(userdata)); + pa_sink_input_set_state_within_thread(ssync, PA_PTR_TO_UINT(userdata)); for (ssync = i->thread_info.sync_next; ssync; ssync = ssync->thread_info.sync_next) - set_state(ssync, PA_PTR_TO_UINT(userdata)); + pa_sink_input_set_state_within_thread(ssync, PA_PTR_TO_UINT(userdata)); return 0; } case PA_SINK_INPUT_MESSAGE_SET_REQUESTED_LATENCY: - i->thread_info.requested_sink_latency = (pa_usec_t) offset; - pa_sink_invalidate_requested_latency(i->sink); - + pa_sink_input_set_requested_latency_within_thread(i, (pa_usec_t) offset); return 0; } @@ -1088,8 +1110,8 @@ pa_bool_t pa_sink_input_safe_to_remove(pa_sink_input *i) { return TRUE; } -void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes /* in our sample spec */, pa_bool_t ignore_underruns) { - size_t l, lbq; +void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes /* in our sample spec */, pa_bool_t ignore_underruns, pa_bool_t not_here) { + size_t lbq; pa_sink_input_assert_ref(i); @@ -1097,9 +1119,16 @@ void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes /* in our sam if (i->state == PA_SINK_INPUT_CORKED) return; - lbq = pa_memblockq_get_length(i->thread_info.render_memblockq); + /* Calculate how much we can rewind locally without having to + * touch the sink */ + if (not_here) + lbq = 0; + else + lbq = pa_memblockq_get_length(i->thread_info.render_memblockq); + /* Check if rewinding for the maximum is requested, and if so, fix up */ if (nbytes <= 0) { + /* Calulate maximum number of bytes that could be rewound in theory */ nbytes = i->sink->thread_info.max_rewind + lbq; @@ -1110,26 +1139,33 @@ void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes /* in our sam nbytes; } - /* Increase the number of bytes to rewrite, never decrease */ - if (nbytes > i->thread_info.rewrite_nbytes) - i->thread_info.rewrite_nbytes = nbytes; + if (not_here) { + i->thread_info.playing_for = 0; + i->thread_info.underrun_for = (uint64_t) -1; + } else { + /* Increase the number of bytes to rewrite, never decrease */ + if (nbytes < i->thread_info.rewrite_nbytes) + nbytes = i->thread_info.rewrite_nbytes; - if (!ignore_underruns) { /* Make sure to not overwrite over underruns */ - if ((int64_t) i->thread_info.rewrite_nbytes > i->thread_info.since_underrun) - i->thread_info.rewrite_nbytes = (size_t) i->thread_info.since_underrun; + if (!ignore_underruns) + if ((int64_t) nbytes > i->thread_info.playing_for) + nbytes = (size_t) i->thread_info.playing_for; + + i->thread_info.rewrite_nbytes = nbytes; } /* Transform to sink domain */ - l = i->thread_info.resampler ? - pa_resampler_result(i->thread_info.resampler, i->thread_info.rewrite_nbytes) : - i->thread_info.rewrite_nbytes; + nbytes = + i->thread_info.resampler ? + pa_resampler_result(i->thread_info.resampler, nbytes) : + nbytes; - if (l <= 0) + if (nbytes <= 0) return; - if (l > lbq) - pa_sink_request_rewind(i->sink, l - lbq); + if (nbytes > lbq) + pa_sink_request_rewind(i->sink, nbytes - lbq); } pa_memchunk* pa_sink_input_get_silence(pa_sink_input *i, pa_memchunk *ret) { diff --git a/src/pulsecore/sink-input.h b/src/pulsecore/sink-input.h index b433edc0..b70cb0ac 100644 --- a/src/pulsecore/sink-input.h +++ b/src/pulsecore/sink-input.h @@ -46,7 +46,7 @@ typedef enum pa_sink_input_state { PA_SINK_INPUT_UNLINKED /*< The stream is dead */ } pa_sink_input_state_t; -static inline pa_bool_t PA_SINK_INPUT_LINKED(pa_sink_input_state_t x) { +static inline pa_bool_t PA_SINK_INPUT_IS_LINKED(pa_sink_input_state_t x) { return x == PA_SINK_INPUT_DRAINED || x == PA_SINK_INPUT_RUNNING || x == PA_SINK_INPUT_CORKED; } @@ -106,7 +106,7 @@ struct pa_sink_input { void (*process_rewind) (pa_sink_input *i, size_t nbytes); /* may NOT be NULL */ /* Called whenever the maximum rewindable size of the sink - * changes. Called from RT context. */ + * changes. Called from IO context. */ void (*update_max_rewind) (pa_sink_input *i, size_t nbytes); /* may be NULL */ /* If non-NULL this function is called when the input is first @@ -138,6 +138,10 @@ struct pa_sink_input { instead. */ pa_usec_t (*get_latency) (pa_sink_input *i); /* may be NULL */ + /* If non_NULL this function is called from thread context if the + * state changes. The old state is found in thread_info.state. */ + void (*state_change) (pa_sink_input *i, pa_sink_input_state_t state); /* may be NULL */ + struct { pa_sink_input_state_t state; pa_atomic_t drained, render_memblockq_is_empty; @@ -152,7 +156,7 @@ struct pa_sink_input { pa_memblockq *render_memblockq; size_t rewrite_nbytes; - int64_t since_underrun; + uint64_t underrun_for, playing_for; pa_bool_t ignore_rewind; pa_sink_input *sync_prev, *sync_next; @@ -237,7 +241,7 @@ fully -- or at all. If the request for a rewrite was successful, the sink driver will call ->rewind() and pass the number of bytes that could be rewound in the HW device. This functionality is required for implementing the "zero latency" write-through functionality. */ -void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes, pa_bool_t ignore_rewind); +void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes, pa_bool_t ignore_rewind, pa_bool_t not_here); /* Callable by everyone from main thread*/ @@ -257,7 +261,7 @@ int pa_sink_input_set_rate(pa_sink_input *i, uint32_t rate); pa_resample_method_t pa_sink_input_get_resample_method(pa_sink_input *i); -int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, int immediately); +int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, pa_bool_t immediately); pa_sink_input_state_t pa_sink_input_get_state(pa_sink_input *i); @@ -269,8 +273,12 @@ void pa_sink_input_drop(pa_sink_input *i, size_t length); void pa_sink_input_process_rewind(pa_sink_input *i, size_t nbytes /* in the sink's sample spec */); void pa_sink_input_update_max_rewind(pa_sink_input *i, size_t nbytes /* in the sink's sample spec */); +void pa_sink_input_set_state_within_thread(pa_sink_input *i, pa_sink_input_state_t state); + int pa_sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk); +pa_usec_t pa_sink_input_set_requested_latency_within_thread(pa_sink_input *i, pa_usec_t usec); + typedef struct pa_sink_input_move_info { pa_sink_input *sink_input; pa_sink_input *ghost_sink_input; diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c index 452dab79..a2a02ebf 100644 --- a/src/pulsecore/sink.c +++ b/src/pulsecore/sink.c @@ -265,8 +265,8 @@ static int sink_set_state(pa_sink *s, pa_sink_state_t state) { return 0; suspend_change = - (s->state == PA_SINK_SUSPENDED && PA_SINK_OPENED(state)) || - (PA_SINK_OPENED(s->state) && state == PA_SINK_SUSPENDED); + (s->state == PA_SINK_SUSPENDED && PA_SINK_IS_OPENED(state)) || + (PA_SINK_IS_OPENED(s->state) && state == PA_SINK_SUSPENDED); if (s->set_state) if ((ret = s->set_state(s, state)) < 0) @@ -328,7 +328,7 @@ void pa_sink_unlink(pa_sink* s) { * may be called multiple times on the same sink without bad * effects. */ - linked = PA_SINK_LINKED(s->state); + linked = PA_SINK_IS_LINKED(s->state); if (linked) pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_UNLINK], s); @@ -366,7 +366,7 @@ static void sink_free(pa_object *o) { pa_assert(s); pa_assert(pa_sink_refcnt(s) == 0); - if (PA_SINK_LINKED(s->state)) + if (PA_SINK_IS_LINKED(s->state)) pa_sink_unlink(s); pa_log_info("Freeing sink %u \"%s\"", s->index, s->name); @@ -397,7 +397,6 @@ static void sink_free(pa_object *o) { void pa_sink_set_asyncmsgq(pa_sink *s, pa_asyncmsgq *q) { pa_sink_assert_ref(s); - pa_assert(q); s->asyncmsgq = q; @@ -407,7 +406,6 @@ void pa_sink_set_asyncmsgq(pa_sink *s, pa_asyncmsgq *q) { void pa_sink_set_rtpoll(pa_sink *s, pa_rtpoll *p) { pa_sink_assert_ref(s); - pa_assert(p); s->rtpoll = p; if (s->monitor_source) @@ -416,7 +414,7 @@ void pa_sink_set_rtpoll(pa_sink *s, pa_rtpoll *p) { int pa_sink_update_status(pa_sink*s) { pa_sink_assert_ref(s); - pa_assert(PA_SINK_LINKED(s->state)); + pa_assert(PA_SINK_IS_LINKED(s->state)); if (s->state == PA_SINK_SUSPENDED) return 0; @@ -426,7 +424,7 @@ int pa_sink_update_status(pa_sink*s) { int pa_sink_suspend(pa_sink *s, pa_bool_t suspend) { pa_sink_assert_ref(s); - pa_assert(PA_SINK_LINKED(s->state)); + pa_assert(PA_SINK_IS_LINKED(s->state)); if (suspend) return sink_set_state(s, PA_SINK_SUSPENDED); @@ -438,7 +436,10 @@ void pa_sink_process_rewind(pa_sink *s, size_t nbytes) { pa_sink_input *i; void *state = NULL; pa_sink_assert_ref(s); - pa_assert(PA_SINK_LINKED(s->state)); + pa_assert(PA_SINK_IS_LINKED(s->state)); + + /* Make sure the sink code already reset the counter! */ + pa_assert(s->thread_info.rewind_nbytes <= 0); if (nbytes <= 0) return; @@ -450,8 +451,9 @@ void pa_sink_process_rewind(pa_sink *s, size_t nbytes) { pa_sink_input_process_rewind(i, nbytes); } - if (s->monitor_source && PA_SOURCE_OPENED(pa_source_get_state(s->monitor_source))) + if (s->monitor_source && PA_SOURCE_IS_OPENED(pa_source_get_state(s->monitor_source))) pa_source_process_rewind(s->monitor_source, nbytes); + } static unsigned fill_mix_info(pa_sink *s, size_t *length, pa_mix_info *info, unsigned maxinfo) { @@ -557,7 +559,7 @@ void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) { size_t block_size_max; pa_sink_assert_ref(s); - pa_assert(PA_SINK_OPENED(s->thread_info.state)); + pa_assert(PA_SINK_IS_OPENED(s->thread_info.state)); pa_assert(pa_frame_aligned(length, &s->sample_spec)); pa_assert(result); @@ -621,7 +623,7 @@ void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) { if (s->thread_info.state == PA_SINK_RUNNING) inputs_drop(s, info, n, result->length); - if (s->monitor_source && PA_SOURCE_OPENED(pa_source_get_state(s->monitor_source))) + if (s->monitor_source && PA_SOURCE_IS_OPENED(pa_source_get_state(s->monitor_source))) pa_source_post(s->monitor_source, result); pa_sink_unref(s); @@ -633,7 +635,7 @@ void pa_sink_render_into(pa_sink*s, pa_memchunk *target) { size_t length, block_size_max; pa_sink_assert_ref(s); - pa_assert(PA_SINK_OPENED(s->thread_info.state)); + pa_assert(PA_SINK_IS_OPENED(s->thread_info.state)); pa_assert(target); pa_assert(target->memblock); pa_assert(target->length > 0); @@ -700,7 +702,7 @@ void pa_sink_render_into(pa_sink*s, pa_memchunk *target) { if (s->thread_info.state == PA_SINK_RUNNING) inputs_drop(s, info, n, target->length); - if (s->monitor_source && PA_SOURCE_OPENED(pa_source_get_state(s->monitor_source))) + if (s->monitor_source && PA_SOURCE_IS_OPENED(pa_source_get_state(s->monitor_source))) pa_source_post(s->monitor_source, target); pa_sink_unref(s); @@ -711,7 +713,7 @@ void pa_sink_render_into_full(pa_sink *s, pa_memchunk *target) { size_t l, d; pa_sink_assert_ref(s); - pa_assert(PA_SINK_OPENED(s->thread_info.state)); + pa_assert(PA_SINK_IS_OPENED(s->thread_info.state)); pa_assert(target); pa_assert(target->memblock); pa_assert(target->length > 0); @@ -739,7 +741,7 @@ void pa_sink_render_into_full(pa_sink *s, pa_memchunk *target) { void pa_sink_render_full(pa_sink *s, size_t length, pa_memchunk *result) { pa_sink_assert_ref(s); - pa_assert(PA_SINK_OPENED(s->thread_info.state)); + pa_assert(PA_SINK_IS_OPENED(s->thread_info.state)); pa_assert(length > 0); pa_assert(pa_frame_aligned(length, &s->sample_spec)); pa_assert(result); @@ -755,50 +757,15 @@ void pa_sink_render_full(pa_sink *s, size_t length, pa_memchunk *result) { pa_sink_render_into_full(s, result); } -void pa_sink_skip(pa_sink *s, size_t length) { - pa_sink_input *i; - void *state = NULL; - - pa_sink_assert_ref(s); - pa_assert(PA_SINK_OPENED(s->thread_info.state)); - pa_assert(length > 0); - pa_assert(pa_frame_aligned(length, &s->sample_spec)); - - s->thread_info.rewind_nbytes = 0; - - if (pa_source_used_by(s->monitor_source)) { - pa_memchunk chunk; - - /* If something is connected to our monitor source, we have to - * pass valid data to it */ - - while (length > 0) { - pa_sink_render(s, length, &chunk); - pa_memblock_unref(chunk.memblock); - - pa_assert(chunk.length <= length); - length -= chunk.length; - } - - } else { - /* Ok, noone cares about the rendered data, so let's not even render it */ - - while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL))) { - pa_sink_input_assert_ref(i); - pa_sink_input_drop(i, length); - } - } -} - pa_usec_t pa_sink_get_latency(pa_sink *s) { pa_usec_t usec = 0; pa_sink_assert_ref(s); - pa_assert(PA_SINK_LINKED(s->state)); + pa_assert(PA_SINK_IS_LINKED(s->state)); /* The returned value is supposed to be in the time domain of the sound card! */ - if (!PA_SINK_OPENED(s->state)) + if (!PA_SINK_IS_OPENED(s->state)) return 0; if (s->get_latency) @@ -814,7 +781,7 @@ void pa_sink_set_volume(pa_sink *s, const pa_cvolume *volume) { int changed; pa_sink_assert_ref(s); - pa_assert(PA_SINK_LINKED(s->state)); + pa_assert(PA_SINK_IS_LINKED(s->state)); pa_assert(volume); changed = !pa_cvolume_equal(volume, &s->volume); @@ -834,7 +801,7 @@ const pa_cvolume *pa_sink_get_volume(pa_sink *s) { struct pa_cvolume old_volume; pa_sink_assert_ref(s); - pa_assert(PA_SINK_LINKED(s->state)); + pa_assert(PA_SINK_IS_LINKED(s->state)); old_volume = s->volume; @@ -854,7 +821,7 @@ void pa_sink_set_mute(pa_sink *s, pa_bool_t mute) { int changed; pa_sink_assert_ref(s); - pa_assert(PA_SINK_LINKED(s->state)); + pa_assert(PA_SINK_IS_LINKED(s->state)); changed = s->muted != mute; s->muted = mute; @@ -873,7 +840,7 @@ pa_bool_t pa_sink_get_mute(pa_sink *s) { pa_bool_t old_muted; pa_sink_assert_ref(s); - pa_assert(PA_SINK_LINKED(s->state)); + pa_assert(PA_SINK_IS_LINKED(s->state)); old_muted = s->muted; @@ -914,7 +881,7 @@ void pa_sink_set_description(pa_sink *s, const char *description) { pa_xfree(n); } - if (PA_SINK_LINKED(s->state)) { + if (PA_SINK_IS_LINKED(s->state)) { pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_PROPLIST_CHANGED], s); } @@ -924,7 +891,7 @@ unsigned pa_sink_linked_by(pa_sink *s) { unsigned ret; pa_sink_assert_ref(s); - pa_assert(PA_SINK_LINKED(s->state)); + pa_assert(PA_SINK_IS_LINKED(s->state)); ret = pa_idxset_size(s->inputs); @@ -941,7 +908,7 @@ unsigned pa_sink_used_by(pa_sink *s) { unsigned ret; pa_sink_assert_ref(s); - pa_assert(PA_SINK_LINKED(s->state)); + pa_assert(PA_SINK_IS_LINKED(s->state)); ret = pa_idxset_size(s->inputs); pa_assert(ret >= s->n_corked); @@ -980,24 +947,26 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse i->thread_info.sync_next->thread_info.sync_prev = i; } - pa_sink_input_update_max_rewind(i, s->thread_info.max_rewind); - pa_assert(!i->thread_info.attached); i->thread_info.attached = TRUE; if (i->attach) i->attach(i); - /* If you change anything here, make sure to change the - * ghost sink input handling a few lines down at - * PA_SINK_MESSAGE_REMOVE_INPUT_AND_BUFFER, too. */ + pa_sink_input_set_state_within_thread(i, i->state); + + pa_sink_input_update_max_rewind(i, s->thread_info.max_rewind); pa_sink_invalidate_requested_latency(s); - /* Make sure we're not rewound when the hw buffer is remixed and request a remix*/ - i->thread_info.ignore_rewind = TRUE; - i->thread_info.since_underrun = 0; - pa_sink_request_rewind(s, 0); + /* We don't rewind here automatically. This is left to the + * sink input implementor because some sink inputs need a + * slow start, i.e. need some time to buffer client + * samples before beginning streaming. */ + + /* If you change anything here, make sure to change the + * ghost sink input handling a few lines down at + * PA_SINK_MESSAGE_REMOVE_INPUT_AND_BUFFER, too. */ return 0; } @@ -1009,6 +978,8 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse * sink input handling a few lines down at * PA_SINK_MESSAGE_REMOVE_INPUT_AND_BUFFER, too. */ + pa_sink_input_set_state_within_thread(i, i->state); + if (i->detach) i->detach(i); @@ -1036,7 +1007,6 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse pa_sink_input_unref(i); pa_sink_invalidate_requested_latency(s); - pa_sink_request_rewind(s, 0); return 0; @@ -1117,11 +1087,9 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse if (info->ghost_sink_input->attach) info->ghost_sink_input->attach(info->ghost_sink_input); - } pa_sink_invalidate_requested_latency(s); - pa_sink_request_rewind(s, 0); return 0; @@ -1196,14 +1164,14 @@ int pa_sink_suspend_all(pa_core *c, pa_bool_t suspend) { void pa_sink_detach(pa_sink *s) { pa_sink_assert_ref(s); - pa_assert(PA_SINK_LINKED(s->state)); + pa_assert(PA_SINK_IS_LINKED(s->state)); pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_DETACH, NULL, 0, NULL); } void pa_sink_attach(pa_sink *s) { pa_sink_assert_ref(s); - pa_assert(PA_SINK_LINKED(s->state)); + pa_assert(PA_SINK_IS_LINKED(s->state)); pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_ATTACH, NULL, 0, NULL); } @@ -1213,7 +1181,7 @@ void pa_sink_detach_within_thread(pa_sink *s) { void *state = NULL; pa_sink_assert_ref(s); - pa_assert(PA_SINK_LINKED(s->thread_info.state)); + pa_assert(PA_SINK_IS_LINKED(s->thread_info.state)); while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL))) if (i->detach) @@ -1228,7 +1196,7 @@ void pa_sink_attach_within_thread(pa_sink *s) { void *state = NULL; pa_sink_assert_ref(s); - pa_assert(PA_SINK_LINKED(s->thread_info.state)); + pa_assert(PA_SINK_IS_LINKED(s->thread_info.state)); while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL))) if (i->attach) @@ -1240,7 +1208,7 @@ void pa_sink_attach_within_thread(pa_sink *s) { void pa_sink_request_rewind(pa_sink*s, size_t nbytes) { pa_sink_assert_ref(s); - pa_assert(PA_SINK_LINKED(s->thread_info.state)); + pa_assert(PA_SINK_IS_LINKED(s->thread_info.state)); if (nbytes <= 0) nbytes = s->thread_info.max_rewind; @@ -1290,9 +1258,9 @@ pa_usec_t pa_sink_get_requested_latency(pa_sink *s) { pa_usec_t usec = 0; pa_sink_assert_ref(s); - pa_assert(PA_SINK_LINKED(s->state)); + pa_assert(PA_SINK_IS_LINKED(s->state)); - if (!PA_SINK_OPENED(s->state)) + if (!PA_SINK_IS_OPENED(s->state)) return 0; if (pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_REQUESTED_LATENCY, &usec, 0, NULL) < 0) @@ -1325,7 +1293,7 @@ void pa_sink_set_max_rewind(pa_sink *s, size_t max_rewind) { void pa_sink_invalidate_requested_latency(pa_sink *s) { pa_sink_assert_ref(s); - pa_assert(PA_SINK_LINKED(s->thread_info.state)); + pa_assert(PA_SINK_IS_LINKED(s->thread_info.state)); if (!s->thread_info.requested_latency_valid) return; diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h index 7bc4a706..f25f48cf 100644 --- a/src/pulsecore/sink.h +++ b/src/pulsecore/sink.h @@ -33,7 +33,6 @@ typedef struct pa_sink pa_sink; #include <pulse/channelmap.h> #include <pulse/volume.h> -#include <pulsecore/core-def.h> #include <pulsecore/core.h> #include <pulsecore/idxset.h> #include <pulsecore/source.h> @@ -52,11 +51,11 @@ typedef enum pa_sink_state { PA_SINK_UNLINKED } pa_sink_state_t; -static inline pa_bool_t PA_SINK_OPENED(pa_sink_state_t x) { +static inline pa_bool_t PA_SINK_IS_OPENED(pa_sink_state_t x) { return x == PA_SINK_RUNNING || x == PA_SINK_IDLE; } -static inline pa_bool_t PA_SINK_LINKED(pa_sink_state_t x) { +static inline pa_bool_t PA_SINK_IS_LINKED(pa_sink_state_t x) { return x == PA_SINK_RUNNING || x == PA_SINK_IDLE || x == PA_SINK_SUSPENDED; } @@ -94,13 +93,42 @@ struct pa_sink { pa_usec_t min_latency; /* we won't go below this latency */ pa_usec_t max_latency; /* An upper limit for the latencies */ + /* Called when the main loop requests a state change. Called from + * main loop context. If returns -1 the state change will be + * inhibited */ int (*set_state)(pa_sink *s, pa_sink_state_t state); /* may be NULL */ - int (*get_volume)(pa_sink *s); /* dito */ + + /* Callled when the volume is queried. Called from main loop + * context. If this is NULL a PA_SINK_MESSAGE_GET_VOLUME message + * will be sent to the IO thread instead. */ + int (*get_volume)(pa_sink *s); /* may be null */ + + /* Called when the volume shall be changed. Called from main loop + * context. If this is NULL a PA_SINK_MESSAGE_SET_VOLUME message + * will be sent to the IO thread instead. */ int (*set_volume)(pa_sink *s); /* dito */ + + /* Called when the mute setting is queried. Called from main loop + * context. If this is NULL a PA_SINK_MESSAGE_GET_MUTE message + * will be sent to the IO thread instead. */ int (*get_mute)(pa_sink *s); /* dito */ + + /* Called when the mute setting shall be changed. Called from main + * loop context. If this is NULL a PA_SINK_MESSAGE_SET_MUTE + * message will be sent to the IO thread instead. */ int (*set_mute)(pa_sink *s); /* dito */ - pa_usec_t (*get_latency)(pa_sink *s); /* dito */ + + /* Called when the latency is queried. Called from main loop + context. If this is NULL a PA_SINK_MESSAGE_GET_LATENCY message + will be sent to the IO thread instead. */ + pa_usec_t (*get_latency)(pa_sink *s); /* dito */ + + /* Called when a rewind request is issued. Called from IO thread + * context. */ void (*request_rewind)(pa_sink *s); /* dito */ + + /* Called when a the requested latency is changed. Called from IO + * thread context. */ void (*update_requested_latency)(pa_sink *s); /* dito */ /* Contains copies of the above data so that the real-time worker @@ -213,7 +241,6 @@ void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result); void pa_sink_render_full(pa_sink *s, size_t length, pa_memchunk *result); void pa_sink_render_into(pa_sink*s, pa_memchunk *target); void pa_sink_render_into_full(pa_sink *s, pa_memchunk *target); -void pa_sink_skip(pa_sink *s, size_t length); void pa_sink_process_rewind(pa_sink *s, size_t nbytes); diff --git a/src/pulsecore/sound-file-stream.c b/src/pulsecore/sound-file-stream.c index 604723f1..918313f8 100644 --- a/src/pulsecore/sound-file-stream.c +++ b/src/pulsecore/sound-file-stream.c @@ -55,6 +55,8 @@ typedef struct file_stream { SNDFILE *sndfile; sf_count_t (*readf_function)(SNDFILE *sndfile, void *ptr, sf_count_t frames); + /* We need this memblockq here to easily fulfill rewind requests + * (even beyond the file start!) */ pa_memblockq *memblockq; } file_stream; @@ -66,6 +68,7 @@ PA_DECLARE_CLASS(file_stream); #define FILE_STREAM(o) (file_stream_cast(o)) static PA_DEFINE_CHECK_TYPE(file_stream, pa_msgobject); +/* Called from main context */ static void file_stream_unlink(file_stream *u) { pa_assert(u); @@ -80,6 +83,7 @@ static void file_stream_unlink(file_stream *u) { file_stream_unref(u); } +/* Called from main context */ static void file_stream_free(pa_object *o) { file_stream *u = FILE_STREAM(o); pa_assert(u); @@ -93,6 +97,7 @@ static void file_stream_free(pa_object *o) { pa_xfree(u); } +/* Called from main context */ static int file_stream_process_msg(pa_msgobject *o, int code, void*userdata, int64_t offset, pa_memchunk *chunk) { file_stream *u = FILE_STREAM(o); file_stream_assert_ref(u); @@ -106,6 +111,7 @@ static int file_stream_process_msg(pa_msgobject *o, int code, void*userdata, int return 0; } +/* Called from main context */ static void sink_input_kill_cb(pa_sink_input *i) { file_stream *u; @@ -116,6 +122,22 @@ static void sink_input_kill_cb(pa_sink_input *i) { file_stream_unlink(u); } +/* Called from IO thread context */ +static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t state) { + file_stream *u; + + pa_sink_input_assert_ref(i); + u = FILE_STREAM(i->userdata); + file_stream_assert_ref(u); + + /* If we are added for the first time, ask for a rewinding so that + * we are heard right-away. */ + if (PA_SINK_INPUT_IS_LINKED(state) && + i->thread_info.state == PA_SINK_INPUT_INIT) + pa_sink_input_request_rewind(i, 0, FALSE, TRUE); +} + +/* Called from IO thread context */ static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) { file_stream *u; @@ -131,6 +153,9 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk for (;;) { pa_memchunk tchunk; + size_t fs; + void *p; + sf_count_t n; if (pa_memblockq_peek(u->memblockq, chunk) >= 0) { pa_memblockq_drop(u->memblockq, chunk->length); @@ -143,36 +168,19 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk tchunk.memblock = pa_memblock_new(i->sink->core->mempool, length); tchunk.index = 0; - if (u->readf_function) { - sf_count_t n; - void *p; - size_t fs = pa_frame_size(&i->sample_spec); + p = pa_memblock_acquire(tchunk.memblock); - p = pa_memblock_acquire(tchunk.memblock); + if (u->readf_function) { + fs = pa_frame_size(&i->sample_spec); n = u->readf_function(u->sndfile, p, length/fs); - pa_memblock_release(tchunk.memblock); - - if (n <= 0) - n = 0; - - tchunk.length = n * fs; - } else { - sf_count_t n; - void *p; - - p = pa_memblock_acquire(tchunk.memblock); + fs = 1; n = sf_read_raw(u->sndfile, p, length); - pa_memblock_release(tchunk.memblock); - - if (n <= 0) - n = 0; - - tchunk.length = n; } - if (tchunk.length <= 0) { + pa_memblock_release(tchunk.memblock); + if (n <= 0) { pa_memblock_unref(tchunk.memblock); sf_close(u->sndfile); @@ -180,6 +188,8 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk break; } + tchunk.length = n * fs; + pa_memblockq_push(u->memblockq, &tchunk); pa_memblock_unref(tchunk.memblock); } @@ -196,7 +206,7 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk } return -1; -} + } static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) { file_stream *u; @@ -334,6 +344,7 @@ int pa_play_file( u->sink_input->process_rewind = sink_input_process_rewind_cb; u->sink_input->update_max_rewind = sink_input_update_max_rewind_cb; u->sink_input->kill = sink_input_kill_cb; + u->sink_input->state_change = sink_input_state_change_cb; u->sink_input->userdata = u; pa_sink_input_get_silence(u->sink_input, &silence); diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c index de543a57..7f5f374e 100644 --- a/src/pulsecore/source-output.c +++ b/src/pulsecore/source-output.c @@ -88,6 +88,7 @@ static void reset_callbacks(pa_source_output *o) { o->moved = NULL; o->kill = NULL; o->get_latency = NULL; + o->state_change = NULL; } pa_source_output* pa_source_output_new( @@ -263,7 +264,7 @@ void pa_source_output_unlink(pa_source_output*o) { pa_source_output_ref(o); - linked = PA_SOURCE_OUTPUT_LINKED(o->state); + linked = PA_SOURCE_OUTPUT_IS_LINKED(o->state); if (linked) pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK], o); @@ -295,7 +296,7 @@ static void source_output_free(pa_object* mo) { pa_assert(pa_source_output_refcnt(o) == 0); - if (PA_SOURCE_OUTPUT_LINKED(o->state)) + if (PA_SOURCE_OUTPUT_IS_LINKED(o->state)) pa_source_output_unlink(o); pa_log_info("Freeing output %u \"%s\"", o->index, pa_strnull(pa_proplist_gets(o->proplist, PA_PROP_MEDIA_NAME))); @@ -335,7 +336,7 @@ void pa_source_output_put(pa_source_output *o) { void pa_source_output_kill(pa_source_output*o) { pa_source_output_assert_ref(o); - pa_assert(PA_SOURCE_OUTPUT_LINKED(o->state)); + pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state)); if (o->kill) o->kill(o); @@ -345,7 +346,7 @@ pa_usec_t pa_source_output_get_latency(pa_source_output *o) { pa_usec_t r = 0; pa_source_output_assert_ref(o); - pa_assert(PA_SOURCE_OUTPUT_LINKED(o->state)); + pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state)); if (pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_GET_LATENCY, &r, 0, NULL) < 0) r = 0; @@ -362,7 +363,7 @@ void pa_source_output_push(pa_source_output *o, const pa_memchunk *chunk) { size_t limit, mbs = 0; pa_source_output_assert_ref(o); - pa_assert(PA_SOURCE_OUTPUT_LINKED(o->thread_info.state)); + pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->thread_info.state)); pa_assert(chunk); pa_assert(pa_frame_aligned(chunk->length, &o->source->sample_spec)); @@ -419,7 +420,7 @@ void pa_source_output_push(pa_source_output *o, const pa_memchunk *chunk) { void pa_source_output_process_rewind(pa_source_output *o, size_t nbytes /* in sink sample spec */) { pa_source_output_assert_ref(o); - pa_assert(PA_SOURCE_OUTPUT_LINKED(o->state)); + pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state)); pa_assert(pa_frame_aligned(nbytes, &o->source->sample_spec)); if (nbytes <= 0) @@ -446,28 +447,48 @@ void pa_source_output_process_rewind(pa_source_output *o, size_t nbytes /* in si /* Called from thread context */ void pa_source_output_update_max_rewind(pa_source_output *o, size_t nbytes /* in the source's sample spec */) { pa_source_output_assert_ref(o); - pa_assert(PA_SOURCE_OUTPUT_LINKED(o->thread_info.state)); + pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->thread_info.state)); pa_assert(pa_frame_aligned(nbytes, &o->source->sample_spec)); if (o->update_max_rewind) o->update_max_rewind(o, o->thread_info.resampler ? pa_resampler_result(o->thread_info.resampler, nbytes) : nbytes); } -pa_usec_t pa_source_output_set_requested_latency(pa_source_output *o, pa_usec_t usec) { - pa_source_output_assert_ref(o); +static pa_usec_t fixup_latency(pa_source *s, pa_usec_t usec) { + pa_source_assert_ref(s); - if (usec != (pa_usec_t) -1) { + if (usec == (pa_usec_t) -1) + return usec; - if (o->source->max_latency > 0 && usec > o->source->max_latency) - usec = o->source->max_latency; + if (s->max_latency > 0 && usec > s->max_latency) + usec = s->max_latency; - if (o->source->min_latency > 0 && usec < o->source->min_latency) - usec = o->source->min_latency; - } + if (s->min_latency > 0 && usec < s->min_latency) + usec = s->min_latency; + + return usec; +} + +pa_usec_t pa_source_output_set_requested_latency_within_thread(pa_source_output *o, pa_usec_t usec) { - if (PA_SOURCE_OUTPUT_LINKED(o->state)) + usec = fixup_latency(o->source, usec); + + o->thread_info.requested_source_latency = usec; + pa_source_invalidate_requested_latency(o->source); + + return usec; +} + +pa_usec_t pa_source_output_set_requested_latency(pa_source_output *o, pa_usec_t usec) { + pa_source_output_assert_ref(o); + + usec = fixup_latency(o->source, usec); + + if (PA_SOURCE_OUTPUT_IS_LINKED(o->state)) pa_asyncmsgq_post(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_SET_REQUESTED_LATENCY, NULL, (int64_t) usec, NULL, NULL); else { + /* If this sink input is not realized yet, we have to touch + * the thread info data directly */ o->thread_info.requested_source_latency = usec; o->source->thread_info.requested_latency_valid = FALSE; } @@ -477,14 +498,14 @@ pa_usec_t pa_source_output_set_requested_latency(pa_source_output *o, pa_usec_t void pa_source_output_cork(pa_source_output *o, pa_bool_t b) { pa_source_output_assert_ref(o); - pa_assert(PA_SOURCE_OUTPUT_LINKED(o->state)); + pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state)); source_output_set_state(o, b ? PA_SOURCE_OUTPUT_CORKED : PA_SOURCE_OUTPUT_RUNNING); } int pa_source_output_set_rate(pa_source_output *o, uint32_t rate) { pa_source_output_assert_ref(o); - pa_assert(PA_SOURCE_OUTPUT_LINKED(o->state)); + pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state)); pa_return_val_if_fail(o->thread_info.resampler, -1); if (o->sample_spec.rate == rate) @@ -515,7 +536,7 @@ void pa_source_output_set_name(pa_source_output *o, const char *name) { else pa_proplist_unset(o->proplist, PA_PROP_MEDIA_NAME); - if (PA_SOURCE_OUTPUT_LINKED(o->state)) { + if (PA_SOURCE_OUTPUT_IS_LINKED(o->state)) { pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PROPLIST_CHANGED], o); pa_subscription_post(o->source->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index); } @@ -533,7 +554,7 @@ int pa_source_output_move_to(pa_source_output *o, pa_source *dest) { pa_source_output_move_hook_data hook_data; pa_source_output_assert_ref(o); - pa_assert(PA_SOURCE_OUTPUT_LINKED(o->state)); + pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state)); pa_source_assert_ref(dest); origin = o->source; @@ -616,12 +637,21 @@ int pa_source_output_move_to(pa_source_output *o, pa_source *dest) { return 0; } +void pa_source_output_set_state_within_thread(pa_source_output *o, pa_source_output_state_t state) { + pa_source_output_assert_ref(o); + + if (o->state_change) + o->state_change(o, state); + + o->thread_info.state = state; +} + /* Called from thread context */ int pa_source_output_process_msg(pa_msgobject *mo, int code, void *userdata, int64_t offset, pa_memchunk* chunk) { pa_source_output *o = PA_SOURCE_OUTPUT(mo); pa_source_output_assert_ref(o); - pa_assert(PA_SOURCE_OUTPUT_LINKED(o->thread_info.state)); + pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->thread_info.state)); switch (code) { @@ -633,25 +663,20 @@ int pa_source_output_process_msg(pa_msgobject *mo, int code, void *userdata, int return 0; } - case PA_SOURCE_OUTPUT_MESSAGE_SET_RATE: { + case PA_SOURCE_OUTPUT_MESSAGE_SET_RATE: o->thread_info.sample_spec.rate = PA_PTR_TO_UINT(userdata); pa_resampler_set_output_rate(o->thread_info.resampler, PA_PTR_TO_UINT(userdata)); - return 0; - } - case PA_SOURCE_OUTPUT_MESSAGE_SET_STATE: { - o->thread_info.state = PA_PTR_TO_UINT(userdata); + case PA_SOURCE_OUTPUT_MESSAGE_SET_STATE: + pa_source_output_set_state_within_thread(o, PA_PTR_TO_UINT(userdata)); return 0; - } case PA_SOURCE_OUTPUT_MESSAGE_SET_REQUESTED_LATENCY: - o->thread_info.requested_source_latency = (pa_usec_t) offset; - pa_source_invalidate_requested_latency(o->source); - + pa_source_output_set_requested_latency_within_thread(o, (pa_usec_t) offset); return 0; } diff --git a/src/pulsecore/source-output.h b/src/pulsecore/source-output.h index e7d8963f..67cb3761 100644 --- a/src/pulsecore/source-output.h +++ b/src/pulsecore/source-output.h @@ -42,7 +42,7 @@ typedef enum pa_source_output_state { PA_SOURCE_OUTPUT_UNLINKED } pa_source_output_state_t; -static inline pa_bool_t PA_SOURCE_OUTPUT_LINKED(pa_source_output_state_t x) { +static inline pa_bool_t PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_state_t x) { return x == PA_SOURCE_OUTPUT_RUNNING || x == PA_SOURCE_OUTPUT_CORKED; } @@ -83,11 +83,11 @@ struct pa_source_output { void (*push)(pa_source_output *o, const pa_memchunk *chunk); /* Only relevant for monitor sources right now: called when the - * recorded stream is rewound. */ + * recorded stream is rewound. Called from IO context*/ void (*process_rewind)(pa_source_output *o, size_t nbytes); /* Called whenever the maximum rewindable size of the source - * changes. Called from RT context. */ + * changes. Called from IO thread context. */ void (*update_max_rewind) (pa_source_output *o, size_t nbytes); /* may be NULL */ /* If non-NULL this function is called when the output is first @@ -116,6 +116,10 @@ struct pa_source_output { thread instead. */ pa_usec_t (*get_latency) (pa_source_output *o); /* may be NULL */ + /* If non_NULL this function is called from thread context if the + * state changes. The old state is found in thread_info.state. */ + void (*state_change) (pa_source_output *o, pa_source_output_state_t state); /* may be NULL */ + struct { pa_source_output_state_t state; @@ -213,4 +217,8 @@ void pa_source_output_update_max_rewind(pa_source_output *o, size_t nbytes); int pa_source_output_process_msg(pa_msgobject *mo, int code, void *userdata, int64_t offset, pa_memchunk *chunk); +void pa_source_output_set_state_within_thread(pa_source_output *o, pa_source_output_state_t state); + +pa_usec_t pa_source_output_set_requested_latency_within_thread(pa_source_output *o, pa_usec_t usec); + #endif diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c index dab307e9..4a2173ca 100644 --- a/src/pulsecore/source.c +++ b/src/pulsecore/source.c @@ -228,8 +228,8 @@ static int source_set_state(pa_source *s, pa_source_state_t state) { return 0; suspend_change = - (s->state == PA_SOURCE_SUSPENDED && PA_SOURCE_OPENED(state)) || - (PA_SOURCE_OPENED(s->state) && state == PA_SOURCE_SUSPENDED); + (s->state == PA_SOURCE_SUSPENDED && PA_SOURCE_IS_OPENED(state)) || + (PA_SOURCE_IS_OPENED(s->state) && state == PA_SOURCE_SUSPENDED); if (s->set_state) if ((ret = s->set_state(s, state)) < 0) @@ -284,7 +284,7 @@ void pa_source_unlink(pa_source *s) { /* See pa_sink_unlink() for a couple of comments how this function * works. */ - linked = PA_SOURCE_LINKED(s->state); + linked = PA_SOURCE_IS_LINKED(s->state); if (linked) pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK], s); @@ -319,7 +319,7 @@ static void source_free(pa_object *o) { pa_assert(s); pa_assert(pa_source_refcnt(s) == 0); - if (PA_SOURCE_LINKED(s->state)) + if (PA_SOURCE_IS_LINKED(s->state)) pa_source_unlink(s); pa_log_info("Freeing source %u \"%s\"", s->index, s->name); @@ -345,21 +345,19 @@ static void source_free(pa_object *o) { void pa_source_set_asyncmsgq(pa_source *s, pa_asyncmsgq *q) { pa_source_assert_ref(s); - pa_assert(q); s->asyncmsgq = q; } void pa_source_set_rtpoll(pa_source *s, pa_rtpoll *p) { pa_source_assert_ref(s); - pa_assert(p); s->rtpoll = p; } int pa_source_update_status(pa_source*s) { pa_source_assert_ref(s); - pa_assert(PA_SOURCE_LINKED(s->state)); + pa_assert(PA_SOURCE_IS_LINKED(s->state)); if (s->state == PA_SOURCE_SUSPENDED) return 0; @@ -369,7 +367,7 @@ int pa_source_update_status(pa_source*s) { int pa_source_suspend(pa_source *s, pa_bool_t suspend) { pa_source_assert_ref(s); - pa_assert(PA_SOURCE_LINKED(s->state)); + pa_assert(PA_SOURCE_IS_LINKED(s->state)); if (suspend) return source_set_state(s, PA_SOURCE_SUSPENDED); @@ -382,7 +380,7 @@ void pa_source_process_rewind(pa_source *s, size_t nbytes) { void *state = NULL; pa_source_assert_ref(s); - pa_assert(PA_SOURCE_OPENED(s->thread_info.state)); + pa_assert(PA_SOURCE_IS_OPENED(s->thread_info.state)); if (nbytes <= 0) return; @@ -400,7 +398,7 @@ void pa_source_post(pa_source*s, const pa_memchunk *chunk) { void *state = NULL; pa_source_assert_ref(s); - pa_assert(PA_SOURCE_OPENED(s->thread_info.state)); + pa_assert(PA_SOURCE_IS_OPENED(s->thread_info.state)); pa_assert(chunk); if (s->thread_info.state != PA_SOURCE_RUNNING) @@ -436,9 +434,9 @@ pa_usec_t pa_source_get_latency(pa_source *s) { pa_usec_t usec; pa_source_assert_ref(s); - pa_assert(PA_SOURCE_LINKED(s->state)); + pa_assert(PA_SOURCE_IS_LINKED(s->state)); - if (!PA_SOURCE_OPENED(s->state)) + if (!PA_SOURCE_IS_OPENED(s->state)) return 0; if (s->get_latency) @@ -454,7 +452,7 @@ void pa_source_set_volume(pa_source *s, const pa_cvolume *volume) { int changed; pa_source_assert_ref(s); - pa_assert(PA_SOURCE_LINKED(s->state)); + pa_assert(PA_SOURCE_IS_LINKED(s->state)); pa_assert(volume); changed = !pa_cvolume_equal(volume, &s->volume); @@ -474,7 +472,7 @@ const pa_cvolume *pa_source_get_volume(pa_source *s) { pa_cvolume old_volume; pa_source_assert_ref(s); - pa_assert(PA_SOURCE_LINKED(s->state)); + pa_assert(PA_SOURCE_IS_LINKED(s->state)); old_volume = s->volume; @@ -494,7 +492,7 @@ void pa_source_set_mute(pa_source *s, pa_bool_t mute) { int changed; pa_source_assert_ref(s); - pa_assert(PA_SOURCE_LINKED(s->state)); + pa_assert(PA_SOURCE_IS_LINKED(s->state)); changed = s->muted != mute; s->muted = mute; @@ -513,7 +511,7 @@ pa_bool_t pa_source_get_mute(pa_source *s) { pa_bool_t old_muted; pa_source_assert_ref(s); - pa_assert(PA_SOURCE_LINKED(s->state)); + pa_assert(PA_SOURCE_IS_LINKED(s->state)); old_muted = s->muted; @@ -546,7 +544,7 @@ void pa_source_set_description(pa_source *s, const char *description) { else pa_proplist_unset(s->proplist, PA_PROP_DEVICE_DESCRIPTION); - if (PA_SOURCE_LINKED(s->state)) { + if (PA_SOURCE_IS_LINKED(s->state)) { pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_PROPLIST_CHANGED], s); } @@ -554,7 +552,7 @@ void pa_source_set_description(pa_source *s, const char *description) { unsigned pa_source_linked_by(pa_source *s) { pa_source_assert_ref(s); - pa_assert(PA_SOURCE_LINKED(s->state)); + pa_assert(PA_SOURCE_IS_LINKED(s->state)); return pa_idxset_size(s->outputs); } @@ -563,7 +561,7 @@ unsigned pa_source_used_by(pa_source *s) { unsigned ret; pa_source_assert_ref(s); - pa_assert(PA_SOURCE_LINKED(s->state)); + pa_assert(PA_SOURCE_IS_LINKED(s->state)); ret = pa_idxset_size(s->outputs); pa_assert(ret >= s->n_corked); @@ -590,6 +588,8 @@ int pa_source_process_msg(pa_msgobject *object, int code, void *userdata, int64_ if (o->attach) o->attach(o); + pa_source_output_set_state_within_thread(o, o->state); + pa_source_invalidate_requested_latency(s); return 0; @@ -598,6 +598,8 @@ int pa_source_process_msg(pa_msgobject *object, int code, void *userdata, int64_ case PA_SOURCE_MESSAGE_REMOVE_OUTPUT: { pa_source_output *o = PA_SOURCE_OUTPUT(userdata); + pa_source_output_set_state_within_thread(o, o->state); + if (o->detach) o->detach(o); @@ -676,14 +678,14 @@ int pa_source_suspend_all(pa_core *c, pa_bool_t suspend) { void pa_source_detach(pa_source *s) { pa_source_assert_ref(s); - pa_assert(PA_SOURCE_LINKED(s->state)); + pa_assert(PA_SOURCE_IS_LINKED(s->state)); pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_DETACH, NULL, 0, NULL); } void pa_source_attach(pa_source *s) { pa_source_assert_ref(s); - pa_assert(PA_SOURCE_LINKED(s->state)); + pa_assert(PA_SOURCE_IS_LINKED(s->state)); pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_ATTACH, NULL, 0, NULL); } @@ -693,7 +695,7 @@ void pa_source_detach_within_thread(pa_source *s) { void *state = NULL; pa_source_assert_ref(s); - pa_assert(PA_SOURCE_LINKED(s->thread_info.state)); + pa_assert(PA_SOURCE_IS_LINKED(s->thread_info.state)); while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL))) if (o->detach) @@ -705,7 +707,7 @@ void pa_source_attach_within_thread(pa_source *s) { void *state = NULL; pa_source_assert_ref(s); - pa_assert(PA_SOURCE_LINKED(s->thread_info.state)); + pa_assert(PA_SOURCE_IS_LINKED(s->thread_info.state)); while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL))) if (o->attach) @@ -746,9 +748,9 @@ pa_usec_t pa_source_get_requested_latency(pa_source *s) { pa_usec_t usec; pa_source_assert_ref(s); - pa_assert(PA_SOURCE_LINKED(s->state)); + pa_assert(PA_SOURCE_IS_LINKED(s->state)); - if (!PA_SOURCE_OPENED(s->state)) + if (!PA_SOURCE_IS_OPENED(s->state)) return 0; if (pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_REQUESTED_LATENCY, &usec, 0, NULL) < 0) @@ -778,7 +780,7 @@ void pa_source_set_max_rewind(pa_source *s, size_t max_rewind) { void pa_source_invalidate_requested_latency(pa_source *s) { pa_source_assert_ref(s); - pa_assert(PA_SOURCE_LINKED(s->thread_info.state)); + pa_assert(PA_SOURCE_IS_LINKED(s->thread_info.state)); if (!s->thread_info.requested_latency_valid) return; diff --git a/src/pulsecore/source.h b/src/pulsecore/source.h index b8859c84..cce54620 100644 --- a/src/pulsecore/source.h +++ b/src/pulsecore/source.h @@ -33,7 +33,6 @@ typedef struct pa_source pa_source; #include <pulse/channelmap.h> #include <pulse/volume.h> -#include <pulsecore/core-def.h> #include <pulsecore/core.h> #include <pulsecore/idxset.h> #include <pulsecore/memblock.h> @@ -54,11 +53,11 @@ typedef enum pa_source_state { PA_SOURCE_UNLINKED } pa_source_state_t; -static inline pa_bool_t PA_SOURCE_OPENED(pa_source_state_t x) { +static inline pa_bool_t PA_SOURCE_IS_OPENED(pa_source_state_t x) { return x == PA_SOURCE_RUNNING || x == PA_SOURCE_IDLE; } -static inline pa_bool_t PA_SOURCE_LINKED(pa_source_state_t x) { +static inline pa_bool_t PA_SOURCE_IS_LINKED(pa_source_state_t x) { return x == PA_SOURCE_RUNNING || x == PA_SOURCE_IDLE || x == PA_SOURCE_SUSPENDED; } |