summaryrefslogtreecommitdiffstats
path: root/src/pulsecore
diff options
context:
space:
mode:
Diffstat (limited to 'src/pulsecore')
-rw-r--r--src/pulsecore/core-util.c586
-rw-r--r--src/pulsecore/core-util.h41
-rw-r--r--src/pulsecore/native-common.h3
-rw-r--r--src/pulsecore/pid.c20
-rw-r--r--src/pulsecore/protocol-cli.c2
-rw-r--r--src/pulsecore/protocol-esound.c2
-rw-r--r--src/pulsecore/protocol-http.c2
-rw-r--r--src/pulsecore/protocol-native.c57
-rw-r--r--src/pulsecore/protocol-simple.c2
-rw-r--r--src/pulsecore/sink-input.c170
-rw-r--r--src/pulsecore/sink-input.h18
-rw-r--r--src/pulsecore/sink.c128
-rw-r--r--src/pulsecore/sink.h39
-rw-r--r--src/pulsecore/sound-file-stream.c59
-rw-r--r--src/pulsecore/source-output.c85
-rw-r--r--src/pulsecore/source-output.h14
-rw-r--r--src/pulsecore/source.c54
-rw-r--r--src/pulsecore/source.h5
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;
}