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