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

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


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

View file

@ -112,13 +112,20 @@ int pa_client_conf_load(pa_client_conf *c, const char *filename) {
table[6].data = &c->cookie_file;
table[7].data = &c->disable_shm;
f = filename ?
fopen((fn = pa_xstrdup(filename)), "r") :
pa_open_config_file(DEFAULT_CLIENT_CONFIG_FILE, DEFAULT_CLIENT_CONFIG_FILE_USER, ENV_CLIENT_CONFIG_FILE, &fn, "r");
if (filename) {
if (!f && errno != EINTR) {
pa_log_warn("Failed to open configuration file '%s': %s", fn, pa_cstrerror(errno));
goto finish;
if (!(f = fopen(filename, "r"))) {
pa_log("Failed to open configuration file '%s': %s", fn, pa_cstrerror(errno));
goto finish;
}
fn = pa_xstrdup(fn);
} else {
if (!(f = pa_open_config_file(DEFAULT_CLIENT_CONFIG_FILE, DEFAULT_CLIENT_CONFIG_FILE_USER, ENV_CLIENT_CONFIG_FILE, &fn)))
if (errno != ENOENT)
goto finish;
}
r = f ? pa_config_parse(fn, f, table, NULL) : 0;
@ -126,7 +133,6 @@ int pa_client_conf_load(pa_client_conf *c, const char *filename) {
if (!r)
r = pa_client_conf_load_cookie(c);
finish:
pa_xfree(fn);

View file

@ -3,7 +3,7 @@
/***
This file is part of PulseAudio.
Copyright 2004-2006 Lennart Poettering
Copyright 2004-2008 Lennart Poettering
Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
PulseAudio is free software; you can redistribute it and/or modify
@ -93,6 +93,7 @@ static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {
[PA_COMMAND_RECORD_STREAM_MOVED] = pa_command_stream_moved,
[PA_COMMAND_PLAYBACK_STREAM_SUSPENDED] = pa_command_stream_suspended,
[PA_COMMAND_RECORD_STREAM_SUSPENDED] = pa_command_stream_suspended,
[PA_COMMAND_STARTED] = pa_command_stream_started,
[PA_COMMAND_SUBSCRIBE_EVENT] = pa_command_subscribe_event
};
@ -100,10 +101,12 @@ static void unlock_autospawn_lock_file(pa_context *c) {
pa_assert(c);
if (c->autospawn_lock_fd >= 0) {
char lf[PATH_MAX];
pa_runtime_path(AUTOSPAWN_LOCK, lf, sizeof(lf));
char *lf;
lf = pa_runtime_path(AUTOSPAWN_LOCK);
pa_unlock_lockfile(lf, c->autospawn_lock_fd);
pa_xfree(lf);
c->autospawn_lock_fd = -1;
}
}
@ -114,6 +117,16 @@ pa_context *pa_context_new(pa_mainloop_api *mainloop, const char *name) {
return pa_context_new_with_proplist(mainloop, name, NULL);
}
static void reset_callbacks(pa_context *c) {
pa_assert(c);
c->state_callback = NULL;
c->state_userdata = NULL;
c->subscribe_callback = NULL;
c->subscribe_userdata = NULL;
}
pa_context *pa_context_new_with_proplist(pa_mainloop_api *mainloop, const char *name, pa_proplist *p) {
pa_context *c;
@ -146,18 +159,14 @@ pa_context *pa_context_new_with_proplist(pa_mainloop_api *mainloop, const char *
c->ctag = 0;
c->csyncid = 0;
c->state_callback = NULL;
c->state_userdata = NULL;
reset_callbacks(c);
c->subscribe_callback = NULL;
c->subscribe_userdata = NULL;
c->is_local = -1;
c->is_local = FALSE;
c->server_list = NULL;
c->server = NULL;
c->autospawn_lock_fd = -1;
memset(&c->spawn_api, 0, sizeof(c->spawn_api));
c->do_autospawn = 0;
c->do_autospawn = FALSE;
#ifndef MSG_NOSIGNAL
#ifdef SIGPIPE
@ -186,26 +195,48 @@ pa_context *pa_context_new_with_proplist(pa_mainloop_api *mainloop, const char *
return c;
}
static void context_free(pa_context *c) {
static void context_unlink(pa_context *c) {
pa_stream *s;
pa_assert(c);
unlock_autospawn_lock_file(c);
s = c->streams ? pa_stream_ref(c->streams) : NULL;
while (s) {
pa_stream *n = s->next ? pa_stream_ref(s->next) : NULL;
pa_stream_set_state(s, c->state == PA_CONTEXT_FAILED ? PA_STREAM_FAILED : PA_STREAM_TERMINATED);
pa_stream_unref(s);
s = n;
}
while (c->operations)
pa_operation_cancel(c->operations);
while (c->streams)
pa_stream_set_state(c->streams, PA_STREAM_TERMINATED);
if (c->client)
pa_socket_client_unref(c->client);
if (c->pdispatch)
if (c->pdispatch) {
pa_pdispatch_unref(c->pdispatch);
c->pdispatch = NULL;
}
if (c->pstream) {
pa_pstream_unlink(c->pstream);
pa_pstream_unref(c->pstream);
c->pstream = NULL;
}
if (c->client) {
pa_socket_client_unref(c->client);
c->client = NULL;
}
reset_callbacks(c);
}
static void context_free(pa_context *c) {
pa_assert(c);
context_unlink(c);
unlock_autospawn_lock_file(c);
if (c->record_streams)
pa_dynarray_free(c->record_streams, NULL, NULL);
if (c->playback_streams)
@ -252,46 +283,16 @@ void pa_context_set_state(pa_context *c, pa_context_state_t st) {
pa_context_ref(c);
c->state = st;
if (c->state_callback)
c->state_callback(c, c->state_userdata);
if (st == PA_CONTEXT_FAILED || st == PA_CONTEXT_TERMINATED) {
pa_stream *s;
s = c->streams ? pa_stream_ref(c->streams) : NULL;
while (s) {
pa_stream *n = s->next ? pa_stream_ref(s->next) : NULL;
pa_stream_set_state(s, st == PA_CONTEXT_FAILED ? PA_STREAM_FAILED : PA_STREAM_TERMINATED);
pa_stream_unref(s);
s = n;
}
if (c->pdispatch)
pa_pdispatch_unref(c->pdispatch);
c->pdispatch = NULL;
if (c->pstream) {
pa_pstream_unlink(c->pstream);
pa_pstream_unref(c->pstream);
}
c->pstream = NULL;
if (c->client)
pa_socket_client_unref(c->client);
c->client = NULL;
}
if (st == PA_CONTEXT_FAILED || st == PA_CONTEXT_TERMINATED)
context_unlink(c);
pa_context_unref(c);
}
void pa_context_fail(pa_context *c, int error) {
pa_assert(c);
pa_assert(PA_REFCNT_VALUE(c) >= 1);
pa_context_set_error(c, error);
pa_context_set_state(c, PA_CONTEXT_FAILED);
}
int pa_context_set_error(pa_context *c, int error) {
pa_assert(error >= 0);
pa_assert(error < PA_ERR_MAX);
@ -302,6 +303,14 @@ int pa_context_set_error(pa_context *c, int error) {
return error;
}
void pa_context_fail(pa_context *c, int error) {
pa_assert(c);
pa_assert(PA_REFCNT_VALUE(c) >= 1);
pa_context_set_error(c, error);
pa_context_set_state(c, PA_CONTEXT_FAILED);
}
static void pstream_die_callback(pa_pstream *p, void *userdata) {
pa_context *c = userdata;
@ -358,25 +367,41 @@ static void pstream_memblock_callback(pa_pstream *p, uint32_t channel, int64_t o
pa_context_unref(c);
}
int pa_context_handle_error(pa_context *c, uint32_t command, pa_tagstruct *t) {
int pa_context_handle_error(pa_context *c, uint32_t command, pa_tagstruct *t, pa_bool_t fail) {
uint32_t err;
pa_assert(c);
pa_assert(PA_REFCNT_VALUE(c) >= 1);
if (command == PA_COMMAND_ERROR) {
pa_assert(t);
if (pa_tagstruct_getu32(t, &c->error) < 0) {
if (pa_tagstruct_getu32(t, &err) < 0) {
pa_context_fail(c, PA_ERR_PROTOCOL);
return -1;
}
} else if (command == PA_COMMAND_TIMEOUT)
c->error = PA_ERR_TIMEOUT;
err = PA_ERR_TIMEOUT;
else {
pa_context_fail(c, PA_ERR_PROTOCOL);
return -1;
}
if (err == PA_OK) {
pa_context_fail(c, PA_ERR_PROTOCOL);
return -1;
}
if (err >= PA_ERR_MAX)
err = PA_ERR_UNKNOWN;
if (fail) {
pa_context_fail(c, err);
return -1;
}
pa_context_set_error(c, err);
return 0;
}
@ -390,11 +415,7 @@ static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t
pa_context_ref(c);
if (command != PA_COMMAND_REPLY) {
if (pa_context_handle_error(c, command, t) < 0)
pa_context_fail(c, PA_ERR_PROTOCOL);
pa_context_fail(c, c->error);
pa_context_handle_error(c, command, t, TRUE);
goto finish;
}
@ -417,7 +438,7 @@ static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t
/* Enable shared memory support if possible */
if (c->version >= 10 &&
pa_mempool_is_shared(c->mempool) &&
c->is_local > 0) {
c->is_local) {
/* Only enable SHM if both sides are owned by the same
* user. This is a security measure because otherwise
@ -486,7 +507,7 @@ static void setup_context(pa_context *c, pa_iochannel *io) {
c->pdispatch = pa_pdispatch_new(c->mainloop, command_table, PA_COMMAND_MAX);
if (!c->conf->cookie_valid)
pa_log_warn("No cookie loaded. Attempting to connect without.");
pa_log_info("No cookie loaded. Attempting to connect without.");
t = pa_tagstruct_command(c, PA_COMMAND_AUTH, &tag);
pa_tagstruct_putu32(t, PA_PROTOCOL_VERSION);
@ -525,10 +546,13 @@ static int context_connect_spawn(pa_context *c) {
int fds[2] = { -1, -1} ;
pa_iochannel *io;
if (getuid() == 0)
return -1;
pa_context_ref(c);
if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) < 0) {
pa_log("socketpair(): %s", pa_cstrerror(errno));
pa_log_error("socketpair(): %s", pa_cstrerror(errno));
pa_context_fail(c, PA_ERR_INTERNAL);
goto fail;
}
@ -542,7 +566,7 @@ static int context_connect_spawn(pa_context *c) {
c->spawn_api.prefork();
if ((pid = fork()) < 0) {
pa_log("fork(): %s", pa_cstrerror(errno));
pa_log_error("fork(): %s", pa_cstrerror(errno));
pa_context_fail(c, PA_ERR_INTERNAL);
if (c->spawn_api.postfork)
@ -557,9 +581,13 @@ static int context_connect_spawn(pa_context *c) {
#define MAX_ARGS 64
const char * argv[MAX_ARGS+1];
int n;
char *f;
/* Not required, since fds[0] has CLOEXEC enabled anyway */
pa_assert_se(pa_close(fds[0]) == 0);
pa_close_all(fds[1], -1);
f = pa_sprintf_malloc("%i", fds[1]);
pa_set_env("PULSE_PASSED_FD", f);
pa_xfree(f);
if (c->spawn_api.atfork)
c->spawn_api.atfork();
@ -592,6 +620,8 @@ static int context_connect_spawn(pa_context *c) {
/* Parent */
pa_assert_se(pa_close(fds[1]) == 0);
r = waitpid(pid, &status, 0);
if (c->spawn_api.postfork)
@ -606,14 +636,12 @@ static int context_connect_spawn(pa_context *c) {
goto fail;
}
pa_assert_se(pa_close(fds[1]) == 0);
c->is_local = TRUE;
c->is_local = 1;
unlock_autospawn_lock_file(c);
io = pa_iochannel_new(c->mainloop, fds[0], fds[0]);
setup_context(c, io);
unlock_autospawn_lock_file(c);
pa_context_unref(c);
@ -665,7 +693,7 @@ static int try_next_connection(pa_context *c) {
if (!(c->client = pa_socket_client_new_string(c->mainloop, u, PA_NATIVE_DEFAULT_PORT)))
continue;
c->is_local = pa_socket_client_is_local(c->client);
c->is_local = !!pa_socket_client_is_local(c->client);
pa_socket_client_set_callback(c->client, on_connection, c);
break;
}
@ -680,6 +708,7 @@ finish:
static void on_connection(pa_socket_client *client, pa_iochannel*io, void *userdata) {
pa_context *c = userdata;
int saved_errno = errno;
pa_assert(client);
pa_assert(c);
@ -692,7 +721,9 @@ static void on_connection(pa_socket_client *client, pa_iochannel*io, void *userd
if (!io) {
/* Try the item in the list */
if (errno == ECONNREFUSED || errno == ETIMEDOUT || errno == EHOSTUNREACH) {
if (saved_errno == ECONNREFUSED ||
saved_errno == ETIMEDOUT ||
saved_errno == EHOSTUNREACH) {
try_next_connection(c);
goto finish;
}
@ -708,6 +739,25 @@ finish:
pa_context_unref(c);
}
static char *get_legacy_runtime_dir(void) {
char *p, u[128];
struct stat st;
if (!pa_get_user_name(u, sizeof(u)))
return NULL;
p = pa_sprintf_malloc("/tmp/pulse-%s", u);
if (stat(p, &st) < 0)
return NULL;
if (st.st_uid != getuid())
return NULL;
return p;
}
int pa_context_connect(
pa_context *c,
const char *server,
@ -736,8 +786,8 @@ int pa_context_connect(
goto finish;
}
} else {
char *d;
char ufn[PATH_MAX];
char *d, *ufn;
static char *legacy_dir;
/* Prepend in reverse order */
@ -757,25 +807,34 @@ int pa_context_connect(
c->server_list = pa_strlist_prepend(c->server_list, "tcp4:localhost");
/* The system wide instance */
c->server_list = pa_strlist_prepend(c->server_list, PA_SYSTEM_RUNTIME_PATH "/" PA_NATIVE_DEFAULT_UNIX_SOCKET);
c->server_list = pa_strlist_prepend(c->server_list, PA_SYSTEM_RUNTIME_PATH PA_PATH_SEP PA_NATIVE_DEFAULT_UNIX_SOCKET);
/* The old per-user instance path. This is supported only to easy upgrades */
if ((legacy_dir = get_legacy_runtime_dir())) {
char *p = pa_sprintf_malloc("%s" PA_PATH_SEP PA_NATIVE_DEFAULT_UNIX_SOCKET, legacy_dir);
c->server_list = pa_strlist_prepend(c->server_list, p);
pa_xfree(p);
pa_xfree(legacy_dir);
}
/* The per-user instance */
c->server_list = pa_strlist_prepend(c->server_list, pa_runtime_path(PA_NATIVE_DEFAULT_UNIX_SOCKET, ufn, sizeof(ufn)));
c->server_list = pa_strlist_prepend(c->server_list, ufn = pa_runtime_path(PA_NATIVE_DEFAULT_UNIX_SOCKET));
pa_xfree(ufn);
/* Wrap the connection attempts in a single transaction for sane autospawn locking */
if (!(flags & PA_CONTEXT_NOAUTOSPAWN) && c->conf->autospawn) {
char lf[PATH_MAX];
char *lf;
pa_runtime_path(AUTOSPAWN_LOCK, lf, sizeof(lf));
pa_make_secure_parent_dir(lf, 0700, (uid_t)-1, (gid_t)-1);
lf = pa_runtime_path(AUTOSPAWN_LOCK);
pa_assert(c->autospawn_lock_fd <= 0);
c->autospawn_lock_fd = pa_lock_lockfile(lf);
pa_xfree(lf);
if (api)
c->spawn_api = *api;
c->do_autospawn = 1;
}
c->do_autospawn = TRUE;
}
}
pa_context_set_state(c, PA_CONTEXT_CONNECTING);
@ -791,7 +850,8 @@ void pa_context_disconnect(pa_context *c) {
pa_assert(c);
pa_assert(PA_REFCNT_VALUE(c) >= 1);
pa_context_set_state(c, PA_CONTEXT_TERMINATED);
if (PA_CONTEXT_IS_GOOD(c->state))
pa_context_set_state(c, PA_CONTEXT_TERMINATED);
}
pa_context_state_t pa_context_get_state(pa_context *c) {
@ -812,6 +872,9 @@ void pa_context_set_state_callback(pa_context *c, pa_context_notify_cb_t cb, voi
pa_assert(c);
pa_assert(PA_REFCNT_VALUE(c) >= 1);
if (c->state == PA_CONTEXT_TERMINATED || c->state == PA_CONTEXT_FAILED)
return;
c->state_callback = cb;
c->state_userdata = userdata;
}
@ -820,11 +883,7 @@ int pa_context_is_pending(pa_context *c) {
pa_assert(c);
pa_assert(PA_REFCNT_VALUE(c) >= 1);
PA_CHECK_VALIDITY(c,
c->state == PA_CONTEXT_CONNECTING ||
c->state == PA_CONTEXT_AUTHORIZING ||
c->state == PA_CONTEXT_SETTING_NAME ||
c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY(c, PA_CONTEXT_IS_GOOD(c->state), PA_ERR_BADSTATE);
return (c->pstream && pa_pstream_is_pending(c->pstream)) ||
(c->pdispatch && pa_pdispatch_is_pending(c->pdispatch)) ||
@ -901,7 +960,7 @@ void pa_context_simple_ack_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_U
goto finish;
if (command != PA_COMMAND_REPLY) {
if (pa_context_handle_error(o->context, command, t) < 0)
if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
goto finish;
success = 0;
@ -920,25 +979,6 @@ finish:
pa_operation_unref(o);
}
pa_operation* pa_context_exit_daemon(pa_context *c, pa_context_success_cb_t cb, void *userdata) {
pa_tagstruct *t;
pa_operation *o;
uint32_t tag;
pa_assert(c);
pa_assert(PA_REFCNT_VALUE(c) >= 1);
PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
t = pa_tagstruct_command(c, PA_COMMAND_EXIT, &tag);
pa_pstream_send_tagstruct(c->pstream, t);
pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
return o;
}
pa_operation* pa_context_send_simple_command(pa_context *c, uint32_t command, pa_pdispatch_cb_t internal_cb, pa_operation_cb_t cb, void *userdata) {
pa_tagstruct *t;
pa_operation *o;
@ -958,6 +998,13 @@ pa_operation* pa_context_send_simple_command(pa_context *c, uint32_t command, pa
return o;
}
pa_operation* pa_context_exit_daemon(pa_context *c, pa_context_success_cb_t cb, void *userdata) {
pa_assert(c);
pa_assert(PA_REFCNT_VALUE(c) >= 1);
return pa_context_send_simple_command(c, PA_COMMAND_EXIT, pa_context_simple_ack_callback, (pa_operation_cb_t) cb, userdata);
}
pa_operation* pa_context_set_default_sink(pa_context *c, const char *name, pa_context_success_cb_t cb, void *userdata) {
pa_tagstruct *t;
pa_operation *o;
@ -969,7 +1016,6 @@ pa_operation* pa_context_set_default_sink(pa_context *c, const char *name, pa_co
PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
t = pa_tagstruct_command(c, PA_COMMAND_SET_DEFAULT_SINK, &tag);
pa_tagstruct_puts(t, name);
pa_pstream_send_tagstruct(c->pstream, t);
@ -989,7 +1035,6 @@ pa_operation* pa_context_set_default_source(pa_context *c, const char *name, pa_
PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
t = pa_tagstruct_command(c, PA_COMMAND_SET_DEFAULT_SOURCE, &tag);
pa_tagstruct_puts(t, name);
pa_pstream_send_tagstruct(c->pstream, t);
@ -1002,15 +1047,13 @@ int pa_context_is_local(pa_context *c) {
pa_assert(c);
pa_assert(PA_REFCNT_VALUE(c) >= 1);
PA_CHECK_VALIDITY(c, c->is_local >= 0, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY_RETURN_ANY(c, PA_CONTEXT_IS_GOOD(c->state), PA_ERR_BADSTATE, -1);
return c->is_local;
return !!c->is_local;
}
pa_operation* pa_context_set_name(pa_context *c, const char *name, pa_context_success_cb_t cb, void *userdata) {
pa_tagstruct *t;
pa_operation *o;
uint32_t tag;
pa_assert(c);
pa_assert(PA_REFCNT_VALUE(c) >= 1);
@ -1020,11 +1063,14 @@ pa_operation* pa_context_set_name(pa_context *c, const char *name, pa_context_su
if (c->version >= 13) {
pa_proplist *p = pa_proplist_new();
pa_proplist_sets(p, PA_PROP_APPLICATION_NAME, name);
o = pa_context_proplist_update(c, PA_UPDATE_REPLACE, p, cb, userdata);
pa_proplist_free(p);
} else {
pa_tagstruct *t;
uint32_t tag;
o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
t = pa_tagstruct_command(c, PA_COMMAND_SET_CLIENT_NAME, &tag);
pa_tagstruct_puts(t, name);
@ -1062,7 +1108,7 @@ uint32_t pa_context_get_server_protocol_version(pa_context *c) {
pa_assert(c);
pa_assert(PA_REFCNT_VALUE(c) >= 1);
PA_CHECK_VALIDITY_RETURN_ANY(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE, PA_INVALID_INDEX);
PA_CHECK_VALIDITY_RETURN_ANY(c, PA_CONTEXT_IS_GOOD(c->state), PA_ERR_BADSTATE, PA_INVALID_INDEX);
return c->version;
}

View file

@ -48,6 +48,15 @@ typedef enum pa_context_state {
PA_CONTEXT_TERMINATED /**< The connection was terminated cleanly */
} pa_context_state_t;
/** Return non-zero if the passed state is one of the connected states */
static inline int PA_CONTEXT_IS_GOOD(pa_context_state_t x) {
return
x == PA_CONTEXT_CONNECTING ||
x == PA_CONTEXT_AUTHORIZING ||
x == PA_CONTEXT_SETTING_NAME ||
x == PA_CONTEXT_READY;
}
/** The state of a stream */
typedef enum pa_stream_state {
PA_STREAM_UNCONNECTED, /**< The stream is not yet connected to any sink or source */
@ -57,6 +66,13 @@ typedef enum pa_stream_state {
PA_STREAM_TERMINATED /**< The stream has been terminated cleanly */
} pa_stream_state_t;
/** Return non-zero if the passed state is one of the connected states */
static inline int PA_STREAM_IS_GOOD(pa_stream_state_t x) {
return
x == PA_STREAM_CREATING ||
x == PA_STREAM_READY;
}
/** The state of an operation */
typedef enum pa_operation_state {
PA_OPERATION_RUNNING, /**< The operation is still running */
@ -296,6 +312,7 @@ enum {
PA_ERR_VERSION, /**< Incompatible protocol version */
PA_ERR_TOOLARGE, /**< Data too large */
PA_ERR_NOTSUPPORTED, /**< Operation not supported \since 0.9.5 */
PA_ERR_UNKNOWN, /**< The error code was unknown to the client */
PA_ERR_MAX /**< Not really an error but the first invalid error code */
};
@ -368,7 +385,15 @@ typedef struct pa_timing_info {
pa_usec_t source_usec; /**< Time in usecs a sample takes from being recorded to being delivered to the application. Only for record streams. */
pa_usec_t transport_usec; /**< Estimated time in usecs a sample takes to be transferred to/from the daemon. For both playback and record streams. */
int playing; /**< Non-zero when the stream is currently playing. Only for playback streams. */
int playing; /**< Non-zero when the stream is
* currently not underrun and data is
* being passed on to the device. Only
* for playback streams. This field does
* not say whether the data is actually
* already being played. To determine
* this check whether since_underrun
* (converted to usec) is larger than
* sink_usec.*/
int write_index_corrupt; /**< Non-zero if write_index is not
* up-to-date because a local write
@ -403,6 +428,14 @@ typedef struct pa_timing_info {
* the sink. \since 0.9.11 */
pa_usec_t configured_source_usec; /**< The static configured latency for
* the source. \since 0.9.11 */
int64_t since_underrun; /**< Bytes that were handed to the sink
since the last underrun happened, or
since playback started again after
the last underrun. playing will tell
you which case it is. \since
0.9.11 */
} pa_timing_info;
/** A structure for the spawn api. This may be used to integrate auto

View file

@ -42,6 +42,7 @@
#include <pulsecore/memblockq.h>
#include <pulsecore/hashmap.h>
#include <pulsecore/refcnt.h>
#include <pulsecore/time-smoother.h>
#include "client-conf.h"
@ -69,14 +70,13 @@ struct pa_context {
pa_context_notify_cb_t state_callback;
void *state_userdata;
pa_context_subscribe_cb_t subscribe_callback;
void *subscribe_userdata;
pa_mempool *mempool;
int is_local;
int do_autospawn;
pa_bool_t is_local;
pa_bool_t do_autospawn;
int autospawn_lock_fd;
pa_spawn_api spawn_api;
@ -89,35 +89,39 @@ struct pa_context {
uint32_t client_index;
};
#define PA_MAX_WRITE_INDEX_CORRECTIONS 10
#define PA_MAX_WRITE_INDEX_CORRECTIONS 32
typedef struct pa_index_correction {
uint32_t tag;
int valid;
int64_t value;
int absolute, corrupt;
pa_bool_t valid:1;
pa_bool_t absolute:1;
pa_bool_t corrupt:1;
} pa_index_correction;
struct pa_stream {
PA_REFCNT_DECLARE;
pa_context *context;
pa_mainloop_api *mainloop;
PA_LLIST_FIELDS(pa_stream);
pa_proplist *proplist;
pa_bool_t manual_buffer_attr;
pa_buffer_attr buffer_attr;
pa_sample_spec sample_spec;
pa_channel_map channel_map;
pa_stream_flags_t flags;
uint32_t channel;
uint32_t syncid;
int channel_valid;
uint32_t stream_index;
pa_context *context;
pa_mainloop_api *mainloop;
pa_stream_direction_t direction;
pa_stream_state_t state;
pa_stream_flags_t flags;
pa_sample_spec sample_spec;
pa_channel_map channel_map;
pa_proplist *proplist;
uint32_t channel;
pa_bool_t channel_valid;
uint32_t syncid;
uint32_t stream_index;
uint32_t requested_bytes;
pa_buffer_attr buffer_attr;
uint32_t device_index;
char *device_name;
@ -127,11 +131,11 @@ struct pa_stream {
void *peek_data;
pa_memblockq *record_memblockq;
int corked;
pa_bool_t corked;
/* Store latest latency info */
pa_timing_info timing_info;
int timing_info_valid;
pa_bool_t timing_info_valid;
/* Use to make sure that time advances monotonically */
pa_usec_t previous_time;
@ -146,10 +150,9 @@ struct pa_stream {
/* Latency interpolation stuff */
pa_time_event *auto_timing_update_event;
int auto_timing_update_requested;
pa_bool_t auto_timing_update_requested;
pa_usec_t cached_time;
int cached_time_valid;
pa_smoother *smoother;
/* Callbacks */
pa_stream_notify_cb_t state_callback;
@ -168,6 +171,8 @@ struct pa_stream {
void *moved_userdata;
pa_stream_notify_cb_t suspended_callback;
void *suspended_userdata;
pa_stream_notify_cb_t started_callback;
void *started_userdata;
};
typedef void (*pa_operation_cb_t)(void);
@ -193,7 +198,7 @@ void pa_command_subscribe_event(pa_pdispatch *pd, uint32_t command, uint32_t tag
void pa_command_overflow_or_underflow(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
void pa_command_stream_suspended(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
void pa_command_stream_moved(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
void pa_command_stream_started(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
pa_operation *pa_operation_new(pa_context *c, pa_stream *s, pa_operation_cb_t callback, void *userdata);
void pa_operation_done(pa_operation *o);
@ -205,7 +210,7 @@ void pa_stream_simple_ack_callback(pa_pdispatch *pd, uint32_t command, uint32_t
void pa_context_fail(pa_context *c, int error);
int pa_context_set_error(pa_context *c, int error);
void pa_context_set_state(pa_context *c, pa_context_state_t st);
int pa_context_handle_error(pa_context *c, uint32_t command, pa_tagstruct *t);
int pa_context_handle_error(pa_context *c, uint32_t command, pa_tagstruct *t, pa_bool_t fail);
pa_operation* pa_context_send_simple_command(pa_context *c, uint32_t command, void (*internal_callback)(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata), void (*cb)(void), void *userdata);
void pa_stream_set_state(pa_stream *s, pa_stream_state_t st);

View file

@ -52,7 +52,7 @@ static void context_stat_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNU
goto finish;
if (command != PA_COMMAND_REPLY) {
if (pa_context_handle_error(o->context, command, t) < 0)
if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
goto finish;
p = NULL;
@ -95,7 +95,7 @@ static void context_get_server_info_callback(pa_pdispatch *pd, uint32_t command,
goto finish;
if (command != PA_COMMAND_REPLY) {
if (pa_context_handle_error(o->context, command, t) < 0)
if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
goto finish;
p = NULL;
@ -140,7 +140,7 @@ static void context_get_sink_info_callback(pa_pdispatch *pd, uint32_t command, P
goto finish;
if (command != PA_COMMAND_REPLY) {
if (pa_context_handle_error(o->context, command, t) < 0)
if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
goto finish;
eol = -1;
@ -261,7 +261,7 @@ static void context_get_source_info_callback(pa_pdispatch *pd, uint32_t command,
goto finish;
if (command != PA_COMMAND_REPLY) {
if (pa_context_handle_error(o->context, command, t) < 0)
if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
goto finish;
eol = -1;
@ -382,7 +382,7 @@ static void context_get_client_info_callback(pa_pdispatch *pd, uint32_t command,
goto finish;
if (command != PA_COMMAND_REPLY) {
if (pa_context_handle_error(o->context, command, t) < 0)
if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
goto finish;
eol = -1;
@ -464,7 +464,7 @@ static void context_get_module_info_callback(pa_pdispatch *pd, uint32_t command,
goto finish;
if (command != PA_COMMAND_REPLY) {
if (pa_context_handle_error(o->context, command, t) < 0)
if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
goto finish;
eol = -1;
@ -543,7 +543,7 @@ static void context_get_sink_input_info_callback(pa_pdispatch *pd, uint32_t comm
goto finish;
if (command != PA_COMMAND_REPLY) {
if (pa_context_handle_error(o->context, command, t) < 0)
if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
goto finish;
eol = -1;
@ -637,7 +637,7 @@ static void context_get_source_output_info_callback(pa_pdispatch *pd, uint32_t c
goto finish;
if (command != PA_COMMAND_REPLY) {
if (pa_context_handle_error(o->context, command, t) < 0)
if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
goto finish;
eol = -1;
@ -967,7 +967,7 @@ static void context_get_sample_info_callback(pa_pdispatch *pd, uint32_t command,
goto finish;
if (command != PA_COMMAND_REPLY) {
if (pa_context_handle_error(o->context, command, t) < 0)
if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
goto finish;
eol = -1;
@ -1111,7 +1111,7 @@ static void context_index_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UN
goto finish;
if (command != PA_COMMAND_REPLY) {
if (pa_context_handle_error(o->context, command, t) < 0)
if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
goto finish;
idx = PA_INVALID_INDEX;
@ -1172,7 +1172,7 @@ static void context_get_autoload_info_callback(pa_pdispatch *pd, uint32_t comman
goto finish;
if (command != PA_COMMAND_REPLY) {
if (pa_context_handle_error(o->context, command, t) < 0)
if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
goto finish;
eol = -1;

View file

@ -108,7 +108,7 @@ static void play_sample_ack_callback(pa_pdispatch *pd, uint32_t command, uint32_
goto finish;
if (command != PA_COMMAND_REPLY) {
if (pa_context_handle_error(o->context, command, t) < 0)
if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
goto finish;
success = 0;
@ -141,7 +141,7 @@ static void play_sample_with_proplist_ack_callback(pa_pdispatch *pd, uint32_t co
goto finish;
if (command != PA_COMMAND_REPLY) {
if (pa_context_handle_error(o->context, command, t) < 0)
if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
goto finish;
idx = PA_INVALID_INDEX;

File diff suppressed because it is too large Load diff

View file

@ -339,6 +339,10 @@ const char *pa_stream_get_device_name(pa_stream *s);
* server is older than 0.9.8. \since 0.9.8 */
int pa_stream_is_suspended(pa_stream *s);
/** Return 1 if the this stream has been corked. This will return 0 if
* not, and negative on error. \since 0.9.11 */
int pa_stream_is_corked(pa_stream *s);
/** Connect the stream to a sink */
int pa_stream_connect_playback(
pa_stream *s /**< The stream to connect to a sink */,
@ -368,7 +372,7 @@ int pa_stream_disconnect(pa_stream *s);
int pa_stream_write(
pa_stream *p /**< The stream to use */,
const void *data /**< The data to write */,
size_t bytes /**< The length of the data to write in bytes*/,
size_t nbytes /**< The length of the data to write in bytes*/,
pa_free_cb_t free_cb /**< A cleanup routine for the data or NULL to request an internal copy */,
int64_t offset, /**< Offset for seeking, must be 0 for upload streams */
pa_seek_mode_t seek /**< Seek mode, must be PA_SEEK_RELATIVE for upload streams */);
@ -381,7 +385,7 @@ int pa_stream_write(
int pa_stream_peek(
pa_stream *p /**< The stream to use */,
const void **data /**< Pointer to pointer that will point to data */,
size_t *bytes /**< The length of the data read in bytes */);
size_t *nbytes /**< The length of the data read in bytes */);
/** Remove the current fragment on record streams. It is invalid to do this without first
* calling pa_stream_peek(). */
@ -419,6 +423,13 @@ void pa_stream_set_overflow_callback(pa_stream *p, pa_stream_notify_cb_t cb, voi
/** Set the callback function that is called when a buffer underflow happens. (Only for playback streams) */
void pa_stream_set_underflow_callback(pa_stream *p, pa_stream_notify_cb_t cb, void *userdata);
/** Set the callback function that is called when a the server starts
* playback after an underrun or on initial startup. This only informs
* that audio is flowing again, it is no indication that audio startet
* to reach the speakers already. (Only for playback streams). \since
* 0.9.11 */
void pa_stream_set_started_callback(pa_stream *p, pa_stream_notify_cb_t cb, void *userdata);
/** Set the callback function that is called whenever a latency
* information update happens. Useful on PA_STREAM_AUTO_TIMING_UPDATE
* streams only. (Only for playback streams) */