Yes, yet another evil all-in-one commit of intervowen changes. I suck.

* Drop "state" directory, fold that into "runtime directory"
* No longer automatically rewind when a new stream connects
* Rework sound file stream, to cause a rewind on initialisation, shorten _pop() code a bit
* Fix reference counting of pa_socket_server in the protocol implementations
* Rework daemon initialization code to be compatible with non-SUID-root setups where RLIMIT_RTPRIO is non-zero
* Print warning if RT/HP is enabled in the config, but due to missing caps, rlimits, policy we cannot enable it.
* Fix potential memory leak in pa_open_config_file()
* Add pa_find_config_file() which works much like pa_open_config_file() but doesn't actually open the config file in question. Just searches for it.
* Add portable pa_is_path_absolute()
* Add pa_close_all() and use it on daemon startup to close leaking file descriptors (inspired from what I did for libdaemon)
* Add pa_unblock_sigs() and use it on daemon startup to unblock all signals (inspired from libdaemon, too)
* Add pa_reset_sigs() and use it on daemon startup to reset all signal handlers (inspired from libdaemon as well)
* Implement pa_set_env()
* Define RLIMIT_RTTIME and friends if not defined by glibc
* Add pa_strempty()
* rename state testing macros to include _IS_, to make clearer that they are no states, but testing macros
* Implement pa_source_output_set_requested_latency_within_thread() to be able to forward latency info to sources from within the IO thread
* Similar for sink inputs
* generelize since_underrun counter in sink inputs to "playing_for" and "underrun_for". Use only this for ignore potential rewind requests over underruns
* Add new native protocol message PLAYBACK_STREAM_MESSAGE_STARTED for notification about the end of an underrun
* Port native protocol to use underrun_for/playing_for which is maintained by the sink input anyway
* Pass underrun_for/playing_for in timing info to client
* Drop pa_sink_skip() since it breaks underrun detection code
* Move PID file and unix sockets to the runtime dir (i.e. ~/.pulse). This fixes a potention DoS attack from other users stealing dirs in /tmp from us so that we cannot take them anymore)
* Allow setting of more resource limits from the config file. Set RTTIME by default
* Streamline daemon startup code
* Rework algorithm to find default configuration files
* If run in system mode use "system.pa" instead of "default.pa" as default script file
* Change ladspa sink to use pa_clamp_samples() for clamping samples
* Teach module-null-sink how to deal with rewinding
* Try to support ALSA devices with no implicit channel map. Synthesize one by padding with PA_CHANNEL_POSITION_AUX channels. This is not tested since I lack hardware with these problems.
* Make use of time smoother in the client libraries.
* Add new pa_stream_is_corked() and pa_stream_set_started_callback() functions to public API
* Since our native socket moved, add some code for finding sockets created by old versions of PA. This should ease upgrades


git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2329 fefdeb5f-60dc-0310-8127-8f9354f1896f
This commit is contained in:
Lennart Poettering 2008-05-01 19:51:05 +00:00
parent f94fae3da3
commit 52e3628c3e
48 changed files with 2408 additions and 1232 deletions

View file

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

View file

@ -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

View file

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

View file

@ -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;
}

View file

@ -82,7 +82,7 @@ pa_protocol_cli* pa_protocol_cli_new(pa_core *core, pa_socket_server *server, pa
p = pa_xnew(pa_protocol_cli, 1);
p->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);

View file

@ -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);

View file

@ -255,7 +255,7 @@ pa_protocol_http* pa_protocol_http_new(pa_core *core, pa_socket_server *server,
p = pa_xnew(pa_protocol_http, 1);
p->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);

View file

@ -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))) {

View file

@ -587,7 +587,7 @@ pa_protocol_simple* pa_protocol_simple_new(pa_core *core, pa_socket_server *serv
p = pa_xnew0(pa_protocol_simple, 1);
p->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;

View file

@ -106,6 +106,7 @@ static void reset_callbacks(pa_sink_input *i) {
i->moved = NULL;
i->kill = NULL;
i->get_latency = NULL;
i->state_change = NULL;
}
pa_sink_input* pa_sink_input_new(
@ -249,8 +250,8 @@ pa_sink_input* pa_sink_input_new(
i->thread_info.muted = i->muted;
i->thread_info.requested_sink_latency = (pa_usec_t) -1;
i->thread_info.rewrite_nbytes = 0;
i->thread_info.since_underrun = 0;
i->thread_info.ignore_rewind = FALSE;
i->thread_info.underrun_for = (uint64_t) -1;
i->thread_info.playing_for = 0;
i->thread_info.render_memblockq = pa_memblockq_new(
0,
@ -328,7 +329,7 @@ void pa_sink_input_unlink(pa_sink_input *i) {
pa_sink_input_ref(i);
linked = PA_SINK_INPUT_LINKED(i->state);
linked = PA_SINK_INPUT_IS_LINKED(i->state);
if (linked)
pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_UNLINK], i);
@ -344,12 +345,11 @@ void pa_sink_input_unlink(pa_sink_input *i) {
if (pa_idxset_remove_by_data(i->sink->inputs, i, NULL))
pa_sink_input_unref(i);
if (linked) {
update_n_corked(i, PA_SINK_INPUT_UNLINKED);
i->state = PA_SINK_INPUT_UNLINKED;
if (linked)
pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_REMOVE_INPUT, i, 0, NULL);
sink_input_set_state(i, PA_SINK_INPUT_UNLINKED);
pa_sink_update_status(i->sink);
} else
i->state = PA_SINK_INPUT_UNLINKED;
reset_callbacks(i);
@ -368,7 +368,7 @@ static void sink_input_free(pa_object *o) {
pa_assert(i);
pa_assert(pa_sink_input_refcnt(i) == 0);
if (PA_SINK_INPUT_LINKED(i->state))
if (PA_SINK_INPUT_IS_LINKED(i->state))
pa_sink_input_unlink(i);
pa_log_info("Freeing input %u \"%s\"", i->index, pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_MEDIA_NAME)));
@ -402,7 +402,7 @@ void pa_sink_input_put(pa_sink_input *i) {
state = i->flags & PA_SINK_INPUT_START_CORKED ? PA_SINK_INPUT_CORKED : PA_SINK_INPUT_RUNNING;
update_n_corked(i, state);
i->thread_info.state = i->state = state;
i->state = state;
pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_ADD_INPUT, i, 0, NULL);
@ -416,7 +416,7 @@ void pa_sink_input_put(pa_sink_input *i) {
void pa_sink_input_kill(pa_sink_input*i) {
pa_sink_input_assert_ref(i);
pa_assert(PA_SINK_INPUT_LINKED(i->state));
pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
if (i->kill)
i->kill(i);
@ -426,7 +426,7 @@ pa_usec_t pa_sink_input_get_latency(pa_sink_input *i) {
pa_usec_t r = 0;
pa_sink_input_assert_ref(i);
pa_assert(PA_SINK_INPUT_LINKED(i->state));
pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
if (pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_GET_LATENCY, &r, 0, NULL) < 0)
r = 0;
@ -445,7 +445,7 @@ int pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink frames */, pa
size_t ilength;
pa_sink_input_assert_ref(i);
pa_assert(PA_SINK_INPUT_LINKED(i->thread_info.state));
pa_assert(PA_SINK_INPUT_IS_LINKED(i->thread_info.state));
pa_assert(pa_frame_aligned(slength, &i->sink->sample_spec));
pa_assert(chunk);
pa_assert(volume);
@ -510,7 +510,9 @@ int pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink frames */, pa
pa_atomic_store(&i->thread_info.drained, 1);
pa_memblockq_seek(i->thread_info.render_memblockq, slength, PA_SEEK_RELATIVE_ON_READ);
i->thread_info.since_underrun = 0;
i->thread_info.playing_for = 0;
if (i->thread_info.underrun_for != (uint64_t) -1)
i->thread_info.underrun_for += slength;
break;
}
@ -519,7 +521,8 @@ int pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink frames */, pa
pa_assert(tchunk.length > 0);
pa_assert(tchunk.memblock);
i->thread_info.since_underrun += tchunk.length;
i->thread_info.underrun_for = 0;
i->thread_info.playing_for += tchunk.length;
while (tchunk.length > 0) {
pa_memchunk wchunk;
@ -590,7 +593,7 @@ int pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink frames */, pa
void pa_sink_input_drop(pa_sink_input *i, size_t nbytes /* in sink sample spec */) {
pa_sink_input_assert_ref(i);
pa_assert(PA_SINK_INPUT_LINKED(i->thread_info.state));
pa_assert(PA_SINK_INPUT_IS_LINKED(i->thread_info.state));
pa_assert(pa_frame_aligned(nbytes, &i->sink->sample_spec));
pa_assert(nbytes > 0);
@ -610,13 +613,13 @@ void pa_sink_input_drop(pa_sink_input *i, size_t nbytes /* in sink sample spec *
void pa_sink_input_process_rewind(pa_sink_input *i, size_t nbytes /* in sink sample spec */) {
pa_sink_input_assert_ref(i);
pa_assert(PA_SINK_INPUT_LINKED(i->thread_info.state));
pa_assert(PA_SINK_INPUT_IS_LINKED(i->thread_info.state));
pa_assert(pa_frame_aligned(nbytes, &i->sink->sample_spec));
/* pa_log_debug("rewind(%lu, %lu)", (unsigned long) nbytes, (unsigned long) i->thread_info.rewrite_nbytes); */
if (i->thread_info.ignore_rewind) {
i->thread_info.ignore_rewind = FALSE;
if (i->thread_info.underrun_for > 0) {
/* We don't rewind when we are underrun */
i->thread_info.rewrite_nbytes = 0;
return;
}
@ -668,7 +671,7 @@ void pa_sink_input_process_rewind(pa_sink_input *i, size_t nbytes /* in sink sam
/* Called from thread context */
void pa_sink_input_update_max_rewind(pa_sink_input *i, size_t nbytes /* in the sink's sample spec */) {
pa_sink_input_assert_ref(i);
pa_assert(PA_SINK_INPUT_LINKED(i->thread_info.state));
pa_assert(PA_SINK_INPUT_IS_LINKED(i->thread_info.state));
pa_assert(pa_frame_aligned(nbytes, &i->sink->sample_spec));
pa_memblockq_set_maxrewind(i->thread_info.render_memblockq, nbytes);
@ -677,21 +680,41 @@ void pa_sink_input_update_max_rewind(pa_sink_input *i, size_t nbytes /* in the
i->update_max_rewind(i, i->thread_info.resampler ? pa_resampler_request(i->thread_info.resampler, nbytes) : nbytes);
}
static pa_usec_t fixup_latency(pa_sink *s, pa_usec_t usec) {
pa_sink_assert_ref(s);
if (usec == (pa_usec_t) -1)
return usec;
if (s->max_latency > 0 && usec > s->max_latency)
usec = s->max_latency;
if (s->min_latency > 0 && usec < s->min_latency)
usec = s->min_latency;
return usec;
}
pa_usec_t pa_sink_input_set_requested_latency_within_thread(pa_sink_input *i, pa_usec_t usec) {
usec = fixup_latency(i->sink, usec);
i->thread_info.requested_sink_latency = usec;
pa_sink_invalidate_requested_latency(i->sink);
return usec;
}
pa_usec_t pa_sink_input_set_requested_latency(pa_sink_input *i, pa_usec_t usec) {
pa_sink_input_assert_ref(i);
if (usec != (pa_usec_t) -1) {
usec = fixup_latency(i->sink, usec);
if (i->sink->max_latency > 0 && usec > i->sink->max_latency)
usec = i->sink->max_latency;
if (i->sink->min_latency > 0 && usec < i->sink->min_latency)
usec = i->sink->min_latency;
}
if (PA_SINK_INPUT_LINKED(i->state))
if (PA_SINK_INPUT_IS_LINKED(i->state))
pa_asyncmsgq_post(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_REQUESTED_LATENCY, NULL, (int64_t) usec, NULL, NULL);
else {
/* If this sink input is not realized yet, we have to touch
* the thread info data directly */
i->thread_info.requested_sink_latency = usec;
i->sink->thread_info.requested_latency_valid = FALSE;
}
@ -701,7 +724,7 @@ pa_usec_t pa_sink_input_set_requested_latency(pa_sink_input *i, pa_usec_t usec)
void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume) {
pa_sink_input_assert_ref(i);
pa_assert(PA_SINK_INPUT_LINKED(i->state));
pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
if (pa_cvolume_equal(&i->volume, volume))
return;
@ -714,7 +737,7 @@ void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume) {
const pa_cvolume *pa_sink_input_get_volume(pa_sink_input *i) {
pa_sink_input_assert_ref(i);
pa_assert(PA_SINK_INPUT_LINKED(i->state));
pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
return &i->volume;
}
@ -722,7 +745,7 @@ const pa_cvolume *pa_sink_input_get_volume(pa_sink_input *i) {
void pa_sink_input_set_mute(pa_sink_input *i, pa_bool_t mute) {
pa_assert(i);
pa_sink_input_assert_ref(i);
pa_assert(PA_SINK_INPUT_LINKED(i->state));
pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
if (!i->muted == !mute)
return;
@ -735,21 +758,21 @@ void pa_sink_input_set_mute(pa_sink_input *i, pa_bool_t mute) {
int pa_sink_input_get_mute(pa_sink_input *i) {
pa_sink_input_assert_ref(i);
pa_assert(PA_SINK_INPUT_LINKED(i->state));
pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
return !!i->muted;
}
void pa_sink_input_cork(pa_sink_input *i, pa_bool_t b) {
pa_sink_input_assert_ref(i);
pa_assert(PA_SINK_INPUT_LINKED(i->state));
pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
sink_input_set_state(i, b ? PA_SINK_INPUT_CORKED : PA_SINK_INPUT_RUNNING);
}
int pa_sink_input_set_rate(pa_sink_input *i, uint32_t rate) {
pa_sink_input_assert_ref(i);
pa_assert(PA_SINK_INPUT_LINKED(i->state));
pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
pa_return_val_if_fail(i->thread_info.resampler, -1);
if (i->sample_spec.rate == rate)
@ -780,7 +803,7 @@ void pa_sink_input_set_name(pa_sink_input *i, const char *name) {
else
pa_proplist_unset(i->proplist, PA_PROP_MEDIA_NAME);
if (PA_SINK_INPUT_LINKED(i->state)) {
if (PA_SINK_INPUT_IS_LINKED(i->state)) {
pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_PROPLIST_CHANGED], i);
pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
}
@ -792,7 +815,7 @@ pa_resample_method_t pa_sink_input_get_resample_method(pa_sink_input *i) {
return i->resample_method;
}
int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, int immediately) {
int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, pa_bool_t immediately) {
pa_resampler *new_resampler;
pa_sink *origin;
pa_usec_t silence_usec = 0;
@ -800,7 +823,7 @@ int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, int immediately) {
pa_sink_input_move_hook_data hook_data;
pa_sink_input_assert_ref(i);
pa_assert(PA_SINK_INPUT_LINKED(i->state));
pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
pa_sink_assert_ref(dest);
origin = i->sink;
@ -983,7 +1006,7 @@ int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, int immediately) {
return 0;
}
static void set_state(pa_sink_input *i, pa_sink_input_state_t state) {
void pa_sink_input_set_state_within_thread(pa_sink_input *i, pa_sink_input_state_t state) {
pa_sink_input_assert_ref(i);
if ((state == PA_SINK_INPUT_DRAINED || state == PA_SINK_INPUT_RUNNING) &&
@ -998,17 +1021,18 @@ static void set_state(pa_sink_input *i, pa_sink_input_state_t state) {
/* This will tell the implementing sink input driver to rewind
* so that the unplayed already mixed data is not lost */
pa_sink_input_request_rewind(i, 0, FALSE);
pa_sink_input_request_rewind(i, 0, FALSE, FALSE);
} else if (i->thread_info.state == PA_SINK_INPUT_CORKED && state != PA_SINK_INPUT_CORKED) {
/* OK, we're being uncorked. Make sure we're not rewound when
* the hw buffer is remixed and request a remix. */
i->thread_info.ignore_rewind = TRUE;
i->thread_info.since_underrun = 0;
pa_sink_request_rewind(i->sink, 0);
pa_sink_input_request_rewind(i, 0, TRUE, TRUE);
}
if (i->state_change)
i->state_change(i, state);
i->thread_info.state = state;
}
@ -1017,17 +1041,17 @@ int pa_sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t
pa_sink_input *i = PA_SINK_INPUT(o);
pa_sink_input_assert_ref(i);
pa_assert(PA_SINK_INPUT_LINKED(i->thread_info.state));
pa_assert(PA_SINK_INPUT_IS_LINKED(i->thread_info.state));
switch (code) {
case PA_SINK_INPUT_MESSAGE_SET_VOLUME:
i->thread_info.volume = *((pa_cvolume*) userdata);
pa_sink_input_request_rewind(i, 0, FALSE);
pa_sink_input_request_rewind(i, 0, FALSE, FALSE);
return 0;
case PA_SINK_INPUT_MESSAGE_SET_MUTE:
i->thread_info.muted = PA_PTR_TO_UINT(userdata);
pa_sink_input_request_rewind(i, 0, FALSE);
pa_sink_input_request_rewind(i, 0, FALSE, FALSE);
return 0;
case PA_SINK_INPUT_MESSAGE_GET_LATENCY: {
@ -1048,22 +1072,20 @@ int pa_sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t
case PA_SINK_INPUT_MESSAGE_SET_STATE: {
pa_sink_input *ssync;
set_state(i, PA_PTR_TO_UINT(userdata));
pa_sink_input_set_state_within_thread(i, PA_PTR_TO_UINT(userdata));
for (ssync = i->thread_info.sync_prev; ssync; ssync = ssync->thread_info.sync_prev)
set_state(ssync, PA_PTR_TO_UINT(userdata));
pa_sink_input_set_state_within_thread(ssync, PA_PTR_TO_UINT(userdata));
for (ssync = i->thread_info.sync_next; ssync; ssync = ssync->thread_info.sync_next)
set_state(ssync, PA_PTR_TO_UINT(userdata));
pa_sink_input_set_state_within_thread(ssync, PA_PTR_TO_UINT(userdata));
return 0;
}
case PA_SINK_INPUT_MESSAGE_SET_REQUESTED_LATENCY:
i->thread_info.requested_sink_latency = (pa_usec_t) offset;
pa_sink_invalidate_requested_latency(i->sink);
pa_sink_input_set_requested_latency_within_thread(i, (pa_usec_t) offset);
return 0;
}
@ -1088,8 +1110,8 @@ pa_bool_t pa_sink_input_safe_to_remove(pa_sink_input *i) {
return TRUE;
}
void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes /* in our sample spec */, pa_bool_t ignore_underruns) {
size_t l, lbq;
void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes /* in our sample spec */, pa_bool_t ignore_underruns, pa_bool_t not_here) {
size_t lbq;
pa_sink_input_assert_ref(i);
@ -1097,9 +1119,16 @@ void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes /* in our sam
if (i->state == PA_SINK_INPUT_CORKED)
return;
lbq = pa_memblockq_get_length(i->thread_info.render_memblockq);
/* Calculate how much we can rewind locally without having to
* touch the sink */
if (not_here)
lbq = 0;
else
lbq = pa_memblockq_get_length(i->thread_info.render_memblockq);
/* Check if rewinding for the maximum is requested, and if so, fix up */
if (nbytes <= 0) {
/* Calulate maximum number of bytes that could be rewound in theory */
nbytes = i->sink->thread_info.max_rewind + lbq;
@ -1110,26 +1139,33 @@ void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes /* in our sam
nbytes;
}
/* Increase the number of bytes to rewrite, never decrease */
if (nbytes > i->thread_info.rewrite_nbytes)
i->thread_info.rewrite_nbytes = nbytes;
if (not_here) {
i->thread_info.playing_for = 0;
i->thread_info.underrun_for = (uint64_t) -1;
} else {
/* Increase the number of bytes to rewrite, never decrease */
if (nbytes < i->thread_info.rewrite_nbytes)
nbytes = i->thread_info.rewrite_nbytes;
if (!ignore_underruns) {
/* Make sure to not overwrite over underruns */
if ((int64_t) i->thread_info.rewrite_nbytes > i->thread_info.since_underrun)
i->thread_info.rewrite_nbytes = (size_t) i->thread_info.since_underrun;
if (!ignore_underruns)
if ((int64_t) nbytes > i->thread_info.playing_for)
nbytes = (size_t) i->thread_info.playing_for;
i->thread_info.rewrite_nbytes = nbytes;
}
/* Transform to sink domain */
l = i->thread_info.resampler ?
pa_resampler_result(i->thread_info.resampler, i->thread_info.rewrite_nbytes) :
i->thread_info.rewrite_nbytes;
nbytes =
i->thread_info.resampler ?
pa_resampler_result(i->thread_info.resampler, nbytes) :
nbytes;
if (l <= 0)
if (nbytes <= 0)
return;
if (l > lbq)
pa_sink_request_rewind(i->sink, l - lbq);
if (nbytes > lbq)
pa_sink_request_rewind(i->sink, nbytes - lbq);
}
pa_memchunk* pa_sink_input_get_silence(pa_sink_input *i, pa_memchunk *ret) {

View file

@ -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;

View file

@ -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;

View file

@ -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);

View file

@ -55,6 +55,8 @@ typedef struct file_stream {
SNDFILE *sndfile;
sf_count_t (*readf_function)(SNDFILE *sndfile, void *ptr, sf_count_t frames);
/* We need this memblockq here to easily fulfill rewind requests
* (even beyond the file start!) */
pa_memblockq *memblockq;
} file_stream;
@ -66,6 +68,7 @@ PA_DECLARE_CLASS(file_stream);
#define FILE_STREAM(o) (file_stream_cast(o))
static PA_DEFINE_CHECK_TYPE(file_stream, pa_msgobject);
/* Called from main context */
static void file_stream_unlink(file_stream *u) {
pa_assert(u);
@ -80,6 +83,7 @@ static void file_stream_unlink(file_stream *u) {
file_stream_unref(u);
}
/* Called from main context */
static void file_stream_free(pa_object *o) {
file_stream *u = FILE_STREAM(o);
pa_assert(u);
@ -93,6 +97,7 @@ static void file_stream_free(pa_object *o) {
pa_xfree(u);
}
/* Called from main context */
static int file_stream_process_msg(pa_msgobject *o, int code, void*userdata, int64_t offset, pa_memchunk *chunk) {
file_stream *u = FILE_STREAM(o);
file_stream_assert_ref(u);
@ -106,6 +111,7 @@ static int file_stream_process_msg(pa_msgobject *o, int code, void*userdata, int
return 0;
}
/* Called from main context */
static void sink_input_kill_cb(pa_sink_input *i) {
file_stream *u;
@ -116,6 +122,22 @@ static void sink_input_kill_cb(pa_sink_input *i) {
file_stream_unlink(u);
}
/* Called from IO thread context */
static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t state) {
file_stream *u;
pa_sink_input_assert_ref(i);
u = FILE_STREAM(i->userdata);
file_stream_assert_ref(u);
/* If we are added for the first time, ask for a rewinding so that
* we are heard right-away. */
if (PA_SINK_INPUT_IS_LINKED(state) &&
i->thread_info.state == PA_SINK_INPUT_INIT)
pa_sink_input_request_rewind(i, 0, FALSE, TRUE);
}
/* Called from IO thread context */
static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) {
file_stream *u;
@ -131,6 +153,9 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk
for (;;) {
pa_memchunk tchunk;
size_t fs;
void *p;
sf_count_t n;
if (pa_memblockq_peek(u->memblockq, chunk) >= 0) {
pa_memblockq_drop(u->memblockq, chunk->length);
@ -143,36 +168,19 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk
tchunk.memblock = pa_memblock_new(i->sink->core->mempool, length);
tchunk.index = 0;
p = pa_memblock_acquire(tchunk.memblock);
if (u->readf_function) {
sf_count_t n;
void *p;
size_t fs = pa_frame_size(&i->sample_spec);
p = pa_memblock_acquire(tchunk.memblock);
fs = pa_frame_size(&i->sample_spec);
n = u->readf_function(u->sndfile, p, length/fs);
pa_memblock_release(tchunk.memblock);
if (n <= 0)
n = 0;
tchunk.length = n * fs;
} else {
sf_count_t n;
void *p;
p = pa_memblock_acquire(tchunk.memblock);
fs = 1;
n = sf_read_raw(u->sndfile, p, length);
pa_memblock_release(tchunk.memblock);
if (n <= 0)
n = 0;
tchunk.length = n;
}
if (tchunk.length <= 0) {
pa_memblock_release(tchunk.memblock);
if (n <= 0) {
pa_memblock_unref(tchunk.memblock);
sf_close(u->sndfile);
@ -180,6 +188,8 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk
break;
}
tchunk.length = n * fs;
pa_memblockq_push(u->memblockq, &tchunk);
pa_memblock_unref(tchunk.memblock);
}
@ -196,7 +206,7 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk
}
return -1;
}
}
static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
file_stream *u;
@ -334,6 +344,7 @@ int pa_play_file(
u->sink_input->process_rewind = sink_input_process_rewind_cb;
u->sink_input->update_max_rewind = sink_input_update_max_rewind_cb;
u->sink_input->kill = sink_input_kill_cb;
u->sink_input->state_change = sink_input_state_change_cb;
u->sink_input->userdata = u;
pa_sink_input_get_silence(u->sink_input, &silence);

View file

@ -88,6 +88,7 @@ static void reset_callbacks(pa_source_output *o) {
o->moved = NULL;
o->kill = NULL;
o->get_latency = NULL;
o->state_change = NULL;
}
pa_source_output* pa_source_output_new(
@ -263,7 +264,7 @@ void pa_source_output_unlink(pa_source_output*o) {
pa_source_output_ref(o);
linked = PA_SOURCE_OUTPUT_LINKED(o->state);
linked = PA_SOURCE_OUTPUT_IS_LINKED(o->state);
if (linked)
pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK], o);
@ -295,7 +296,7 @@ static void source_output_free(pa_object* mo) {
pa_assert(pa_source_output_refcnt(o) == 0);
if (PA_SOURCE_OUTPUT_LINKED(o->state))
if (PA_SOURCE_OUTPUT_IS_LINKED(o->state))
pa_source_output_unlink(o);
pa_log_info("Freeing output %u \"%s\"", o->index, pa_strnull(pa_proplist_gets(o->proplist, PA_PROP_MEDIA_NAME)));
@ -335,7 +336,7 @@ void pa_source_output_put(pa_source_output *o) {
void pa_source_output_kill(pa_source_output*o) {
pa_source_output_assert_ref(o);
pa_assert(PA_SOURCE_OUTPUT_LINKED(o->state));
pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
if (o->kill)
o->kill(o);
@ -345,7 +346,7 @@ pa_usec_t pa_source_output_get_latency(pa_source_output *o) {
pa_usec_t r = 0;
pa_source_output_assert_ref(o);
pa_assert(PA_SOURCE_OUTPUT_LINKED(o->state));
pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
if (pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_GET_LATENCY, &r, 0, NULL) < 0)
r = 0;
@ -362,7 +363,7 @@ void pa_source_output_push(pa_source_output *o, const pa_memchunk *chunk) {
size_t limit, mbs = 0;
pa_source_output_assert_ref(o);
pa_assert(PA_SOURCE_OUTPUT_LINKED(o->thread_info.state));
pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->thread_info.state));
pa_assert(chunk);
pa_assert(pa_frame_aligned(chunk->length, &o->source->sample_spec));
@ -419,7 +420,7 @@ void pa_source_output_push(pa_source_output *o, const pa_memchunk *chunk) {
void pa_source_output_process_rewind(pa_source_output *o, size_t nbytes /* in sink sample spec */) {
pa_source_output_assert_ref(o);
pa_assert(PA_SOURCE_OUTPUT_LINKED(o->state));
pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
pa_assert(pa_frame_aligned(nbytes, &o->source->sample_spec));
if (nbytes <= 0)
@ -446,28 +447,48 @@ void pa_source_output_process_rewind(pa_source_output *o, size_t nbytes /* in si
/* Called from thread context */
void pa_source_output_update_max_rewind(pa_source_output *o, size_t nbytes /* in the source's sample spec */) {
pa_source_output_assert_ref(o);
pa_assert(PA_SOURCE_OUTPUT_LINKED(o->thread_info.state));
pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->thread_info.state));
pa_assert(pa_frame_aligned(nbytes, &o->source->sample_spec));
if (o->update_max_rewind)
o->update_max_rewind(o, o->thread_info.resampler ? pa_resampler_result(o->thread_info.resampler, nbytes) : nbytes);
}
static pa_usec_t fixup_latency(pa_source *s, pa_usec_t usec) {
pa_source_assert_ref(s);
if (usec == (pa_usec_t) -1)
return usec;
if (s->max_latency > 0 && usec > s->max_latency)
usec = s->max_latency;
if (s->min_latency > 0 && usec < s->min_latency)
usec = s->min_latency;
return usec;
}
pa_usec_t pa_source_output_set_requested_latency_within_thread(pa_source_output *o, pa_usec_t usec) {
usec = fixup_latency(o->source, usec);
o->thread_info.requested_source_latency = usec;
pa_source_invalidate_requested_latency(o->source);
return usec;
}
pa_usec_t pa_source_output_set_requested_latency(pa_source_output *o, pa_usec_t usec) {
pa_source_output_assert_ref(o);
if (usec != (pa_usec_t) -1) {
usec = fixup_latency(o->source, usec);
if (o->source->max_latency > 0 && usec > o->source->max_latency)
usec = o->source->max_latency;
if (o->source->min_latency > 0 && usec < o->source->min_latency)
usec = o->source->min_latency;
}
if (PA_SOURCE_OUTPUT_LINKED(o->state))
if (PA_SOURCE_OUTPUT_IS_LINKED(o->state))
pa_asyncmsgq_post(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_SET_REQUESTED_LATENCY, NULL, (int64_t) usec, NULL, NULL);
else {
/* If this sink input is not realized yet, we have to touch
* the thread info data directly */
o->thread_info.requested_source_latency = usec;
o->source->thread_info.requested_latency_valid = FALSE;
}
@ -477,14 +498,14 @@ pa_usec_t pa_source_output_set_requested_latency(pa_source_output *o, pa_usec_t
void pa_source_output_cork(pa_source_output *o, pa_bool_t b) {
pa_source_output_assert_ref(o);
pa_assert(PA_SOURCE_OUTPUT_LINKED(o->state));
pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
source_output_set_state(o, b ? PA_SOURCE_OUTPUT_CORKED : PA_SOURCE_OUTPUT_RUNNING);
}
int pa_source_output_set_rate(pa_source_output *o, uint32_t rate) {
pa_source_output_assert_ref(o);
pa_assert(PA_SOURCE_OUTPUT_LINKED(o->state));
pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
pa_return_val_if_fail(o->thread_info.resampler, -1);
if (o->sample_spec.rate == rate)
@ -515,7 +536,7 @@ void pa_source_output_set_name(pa_source_output *o, const char *name) {
else
pa_proplist_unset(o->proplist, PA_PROP_MEDIA_NAME);
if (PA_SOURCE_OUTPUT_LINKED(o->state)) {
if (PA_SOURCE_OUTPUT_IS_LINKED(o->state)) {
pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PROPLIST_CHANGED], o);
pa_subscription_post(o->source->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
}
@ -533,7 +554,7 @@ int pa_source_output_move_to(pa_source_output *o, pa_source *dest) {
pa_source_output_move_hook_data hook_data;
pa_source_output_assert_ref(o);
pa_assert(PA_SOURCE_OUTPUT_LINKED(o->state));
pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
pa_source_assert_ref(dest);
origin = o->source;
@ -616,12 +637,21 @@ int pa_source_output_move_to(pa_source_output *o, pa_source *dest) {
return 0;
}
void pa_source_output_set_state_within_thread(pa_source_output *o, pa_source_output_state_t state) {
pa_source_output_assert_ref(o);
if (o->state_change)
o->state_change(o, state);
o->thread_info.state = state;
}
/* Called from thread context */
int pa_source_output_process_msg(pa_msgobject *mo, int code, void *userdata, int64_t offset, pa_memchunk* chunk) {
pa_source_output *o = PA_SOURCE_OUTPUT(mo);
pa_source_output_assert_ref(o);
pa_assert(PA_SOURCE_OUTPUT_LINKED(o->thread_info.state));
pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->thread_info.state));
switch (code) {
@ -633,25 +663,20 @@ int pa_source_output_process_msg(pa_msgobject *mo, int code, void *userdata, int
return 0;
}
case PA_SOURCE_OUTPUT_MESSAGE_SET_RATE: {
case PA_SOURCE_OUTPUT_MESSAGE_SET_RATE:
o->thread_info.sample_spec.rate = PA_PTR_TO_UINT(userdata);
pa_resampler_set_output_rate(o->thread_info.resampler, PA_PTR_TO_UINT(userdata));
return 0;
}
case PA_SOURCE_OUTPUT_MESSAGE_SET_STATE: {
o->thread_info.state = PA_PTR_TO_UINT(userdata);
case PA_SOURCE_OUTPUT_MESSAGE_SET_STATE:
pa_source_output_set_state_within_thread(o, PA_PTR_TO_UINT(userdata));
return 0;
}
case PA_SOURCE_OUTPUT_MESSAGE_SET_REQUESTED_LATENCY:
o->thread_info.requested_source_latency = (pa_usec_t) offset;
pa_source_invalidate_requested_latency(o->source);
pa_source_output_set_requested_latency_within_thread(o, (pa_usec_t) offset);
return 0;
}

View file

@ -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

View file

@ -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;

View file

@ -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;
}