summaryrefslogtreecommitdiffstats
path: root/src/pulse
diff options
context:
space:
mode:
Diffstat (limited to 'src/pulse')
-rw-r--r--src/pulse/client-conf.c20
-rw-r--r--src/pulse/context.c274
-rw-r--r--src/pulse/def.h35
-rw-r--r--src/pulse/internal.h47
-rw-r--r--src/pulse/introspect.c22
-rw-r--r--src/pulse/scache.c4
-rw-r--r--src/pulse/stream.c759
-rw-r--r--src/pulse/stream.h15
8 files changed, 762 insertions, 414 deletions
diff --git a/src/pulse/client-conf.c b/src/pulse/client-conf.c
index c054f663..75f44182 100644
--- a/src/pulse/client-conf.c
+++ b/src/pulse/client-conf.c
@@ -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);
diff --git a/src/pulse/context.c b/src/pulse/context.c
index 7806e88c..f9f021af 100644
--- a/src/pulse/context.c
+++ b/src/pulse/context.c
@@ -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;
-
- c->subscribe_callback = NULL;
- c->subscribe_userdata = NULL;
+ reset_callbacks(c);
- 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;
+
+ pa_close_all(fds[1], -1);
- /* Not required, since fds[0] has CLOEXEC enabled anyway */
- pa_assert_se(pa_close(fds[0]) == 0);
+ 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,7 +979,7 @@ finish:
pa_operation_unref(o);
}
-pa_operation* pa_context_exit_daemon(pa_context *c, pa_context_success_cb_t cb, void *userdata) {
+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;
uint32_t tag;
@@ -930,32 +989,20 @@ pa_operation* pa_context_exit_daemon(pa_context *c, pa_context_success_cb_t cb,
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);
+ o = pa_operation_new(c, NULL, cb, userdata);
- t = pa_tagstruct_command(c, PA_COMMAND_EXIT, &tag);
+ t = pa_tagstruct_command(c, command, &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);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, internal_cb, 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;
- uint32_t tag;
-
+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);
- PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
-
- o = pa_operation_new(c, NULL, cb, userdata);
-
- t = pa_tagstruct_command(c, command, &tag);
- pa_pstream_send_tagstruct(c->pstream, t);
- pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, internal_cb, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
-
- return o;
+ 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) {
@@ -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;
}
diff --git a/src/pulse/def.h b/src/pulse/def.h
index 8a83d7a9..1a0b9cb6 100644
--- a/src/pulse/def.h
+++ b/src/pulse/def.h
@@ -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
diff --git a/src/pulse/internal.h b/src/pulse/internal.h
index f15c69c3..d346e945 100644
--- a/src/pulse/internal.h
+++ b/src/pulse/internal.h
@@ -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_LLIST_FIELDS(pa_stream);
+
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_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_stream_flags_t flags;
+
+ pa_proplist *proplist;
+
uint32_t channel;
+ pa_bool_t channel_valid;
uint32_t syncid;
- int channel_valid;
uint32_t stream_index;
- pa_stream_direction_t direction;
- pa_stream_state_t state;
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);
diff --git a/src/pulse/introspect.c b/src/pulse/introspect.c
index 49f93463..857e82b4 100644
--- a/src/pulse/introspect.c
+++ b/src/pulse/introspect.c
@@ -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;
diff --git a/src/pulse/scache.c b/src/pulse/scache.c
index 24f340ea..e43a0b9f 100644
--- a/src/pulse/scache.c
+++ b/src/pulse/scache.c
@@ -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;
diff --git a/src/pulse/stream.c b/src/pulse/stream.c
index ccbabb55..297e9d7f 100644
--- a/src/pulse/stream.c
+++ b/src/pulse/stream.c
@@ -38,16 +38,47 @@
#include <pulsecore/log.h>
#include <pulsecore/hashmap.h>
#include <pulsecore/macro.h>
+#include <pulsecore/rtclock.h>
#include "internal.h"
-#define LATENCY_IPOL_INTERVAL_USEC (100000L)
+#define LATENCY_IPOL_INTERVAL_USEC (500*PA_USEC_PER_MSEC)
+
+#define SMOOTHER_ADJUST_TIME (1000*PA_USEC_PER_MSEC)
+#define SMOOTHER_HISTORY_TIME (5000*PA_USEC_PER_MSEC)
pa_stream *pa_stream_new(pa_context *c, const char *name, const pa_sample_spec *ss, const pa_channel_map *map) {
return pa_stream_new_with_proplist(c, name, ss, map, NULL);
}
-pa_stream *pa_stream_new_with_proplist(pa_context *c, const char *name, const pa_sample_spec *ss, const pa_channel_map *map, pa_proplist *p) {
+static void reset_callbacks(pa_stream *s) {
+ s->read_callback = NULL;
+ s->read_userdata = NULL;
+ s->write_callback = NULL;
+ s->write_userdata = NULL;
+ s->state_callback = NULL;
+ s->state_userdata = NULL;
+ s->overflow_callback = NULL;
+ s->overflow_userdata = NULL;
+ s->underflow_callback = NULL;
+ s->underflow_userdata = NULL;
+ s->latency_update_callback = NULL;
+ s->latency_update_userdata = NULL;
+ s->moved_callback = NULL;
+ s->moved_userdata = NULL;
+ s->suspended_callback = NULL;
+ s->suspended_userdata = NULL;
+ s->started_callback = NULL;
+ s->started_userdata = NULL;
+}
+
+pa_stream *pa_stream_new_with_proplist(
+ pa_context *c,
+ const char *name,
+ const pa_sample_spec *ss,
+ const pa_channel_map *map,
+ pa_proplist *p) {
+
pa_stream *s;
int i;
pa_channel_map tmap;
@@ -58,7 +89,7 @@ pa_stream *pa_stream_new_with_proplist(pa_context *c, const char *name, const pa
PA_CHECK_VALIDITY_RETURN_NULL(c, ss && pa_sample_spec_valid(ss), PA_ERR_INVALID);
PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 12 || (ss->format != PA_SAMPLE_S32LE || ss->format != PA_SAMPLE_S32NE), PA_ERR_NOTSUPPORTED);
PA_CHECK_VALIDITY_RETURN_NULL(c, !map || (pa_channel_map_valid(map) && map->channels == ss->channels), PA_ERR_INVALID);
- PA_CHECK_VALIDITY_RETURN_NULL(c, name || pa_proplist_contains(p, PA_PROP_MEDIA_NAME), PA_ERR_INVALID);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, name || (p && pa_proplist_contains(p, PA_PROP_MEDIA_NAME)), PA_ERR_INVALID);
if (!map)
PA_CHECK_VALIDITY_RETURN_NULL(c, map = pa_channel_map_init_auto(&tmap, ss->channels, PA_CHANNEL_MAP_DEFAULT), PA_ERR_INVALID);
@@ -68,70 +99,53 @@ pa_stream *pa_stream_new_with_proplist(pa_context *c, const char *name, const pa
s->context = c;
s->mainloop = c->mainloop;
- s->read_callback = NULL;
- s->read_userdata = NULL;
- s->write_callback = NULL;
- s->write_userdata = NULL;
- s->state_callback = NULL;
- s->state_userdata = NULL;
- s->overflow_callback = NULL;
- s->overflow_userdata = NULL;
- s->underflow_callback = NULL;
- s->underflow_userdata = NULL;
- s->latency_update_callback = NULL;
- s->latency_update_userdata = NULL;
- s->moved_callback = NULL;
- s->moved_userdata = NULL;
- s->suspended_callback = NULL;
- s->suspended_userdata = NULL;
-
s->direction = PA_STREAM_NODIRECTION;
+ s->state = PA_STREAM_UNCONNECTED;
+ s->flags = 0;
+
s->sample_spec = *ss;
s->channel_map = *map;
- s->flags = 0;
s->proplist = p ? pa_proplist_copy(p) : pa_proplist_new();
-
if (name)
pa_proplist_sets(s->proplist, PA_PROP_MEDIA_NAME, name);
s->channel = 0;
- s->channel_valid = 0;
+ s->channel_valid = FALSE;
s->syncid = c->csyncid++;
s->stream_index = PA_INVALID_INDEX;
- s->requested_bytes = 0;
- s->state = PA_STREAM_UNCONNECTED;
- s->manual_buffer_attr = FALSE;
+ s->requested_bytes = 0;
memset(&s->buffer_attr, 0, sizeof(s->buffer_attr));
s->device_index = PA_INVALID_INDEX;
s->device_name = NULL;
s->suspended = FALSE;
- s->peek_memchunk.index = 0;
- s->peek_memchunk.length = 0;
- s->peek_memchunk.memblock = NULL;
+ pa_memchunk_reset(&s->peek_memchunk);
s->peek_data = NULL;
s->record_memblockq = NULL;
- s->previous_time = 0;
+ s->corked = FALSE;
+
memset(&s->timing_info, 0, sizeof(s->timing_info));
s->timing_info_valid = FALSE;
+
+ s->previous_time = 0;
+
s->read_index_not_before = 0;
s->write_index_not_before = 0;
-
for (i = 0; i < PA_MAX_WRITE_INDEX_CORRECTIONS; i++)
s->write_index_corrections[i].valid = 0;
s->current_write_index_correction = 0;
- s->corked = 0;
+ s->auto_timing_update_event = NULL;
+ s->auto_timing_update_requested = FALSE;
- s->cached_time_valid = 0;
+ reset_callbacks(s);
- s->auto_timing_update_event = NULL;
- s->auto_timing_update_requested = 0;
+ s->smoother = NULL;
/* Refcounting is strictly one-way: from the "bigger" to the "smaller" object. */
PA_LLIST_PREPEND(pa_stream, c->streams, s);
@@ -140,16 +154,51 @@ pa_stream *pa_stream_new_with_proplist(pa_context *c, const char *name, const pa
return s;
}
-static void stream_free(pa_stream *s) {
+static void stream_unlink(pa_stream *s) {
+ pa_operation *o, *n;
pa_assert(s);
- pa_assert(!s->context);
- pa_assert(!s->channel_valid);
+
+ if (!s->context)
+ return;
+
+ /* Detach from context */
+
+ /* Unref all operatio object that point to us */
+ for (o = s->context->operations; o; o = n) {
+ n = o->next;
+
+ if (o->stream == s)
+ pa_operation_cancel(o);
+ }
+
+ /* Drop all outstanding replies for this stream */
+ if (s->context->pdispatch)
+ pa_pdispatch_unregister_reply(s->context->pdispatch, s);
+
+ if (s->channel_valid) {
+ pa_dynarray_put((s->direction == PA_STREAM_PLAYBACK) ? s->context->playback_streams : s->context->record_streams, s->channel, NULL);
+ s->channel = 0;
+ s->channel_valid = FALSE;
+ }
+
+ PA_LLIST_REMOVE(pa_stream, s->context->streams, s);
+ pa_stream_unref(s);
+
+ s->context = NULL;
if (s->auto_timing_update_event) {
pa_assert(s->mainloop);
s->mainloop->time_free(s->auto_timing_update_event);
}
+ reset_callbacks(s);
+}
+
+static void stream_free(pa_stream *s) {
+ pa_assert(s);
+
+ stream_unlink(s);
+
if (s->peek_memchunk.memblock) {
if (s->peek_data)
pa_memblock_release(s->peek_memchunk.memblock);
@@ -162,6 +211,9 @@ static void stream_free(pa_stream *s) {
if (s->proplist)
pa_proplist_free(s->proplist);
+ if (s->smoother)
+ pa_smoother_free(s->smoother);
+
pa_xfree(s->device_name);
pa_xfree(s);
}
@@ -215,46 +267,41 @@ void pa_stream_set_state(pa_stream *s, pa_stream_state_t st) {
pa_stream_ref(s);
s->state = st;
+
if (s->state_callback)
s->state_callback(s, s->state_userdata);
- if ((st == PA_STREAM_FAILED || st == PA_STREAM_TERMINATED) && s->context) {
-
- /* Detach from context */
- pa_operation *o, *n;
+ if ((st == PA_STREAM_FAILED || st == PA_STREAM_TERMINATED))
+ stream_unlink(s);
- /* Unref all operatio object that point to us */
- for (o = s->context->operations; o; o = n) {
- n = o->next;
-
- if (o->stream == s)
- pa_operation_cancel(o);
- }
-
- /* Drop all outstanding replies for this stream */
- if (s->context->pdispatch)
- pa_pdispatch_unregister_reply(s->context->pdispatch, s);
+ pa_stream_unref(s);
+}
- if (s->channel_valid)
- pa_dynarray_put((s->direction == PA_STREAM_PLAYBACK) ? s->context->playback_streams : s->context->record_streams, s->channel, NULL);
+static void request_auto_timing_update(pa_stream *s, pa_bool_t force) {
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
- PA_LLIST_REMOVE(pa_stream, s->context->streams, s);
- pa_stream_unref(s);
+ if (!(s->flags & PA_STREAM_AUTO_TIMING_UPDATE))
+ return;
- s->channel = 0;
- s->channel_valid = 0;
+ if (s->state == PA_STREAM_READY &&
+ (force || !s->auto_timing_update_requested)) {
+ pa_operation *o;
- s->context = NULL;
+/* pa_log("automatically requesting new timing data"); */
- s->read_callback = NULL;
- s->write_callback = NULL;
- s->state_callback = NULL;
- s->overflow_callback = NULL;
- s->underflow_callback = NULL;
- s->latency_update_callback = NULL;
+ if ((o = pa_stream_update_timing_info(s, NULL, NULL))) {
+ pa_operation_unref(o);
+ s->auto_timing_update_requested = TRUE;
+ }
}
- pa_stream_unref(s);
+ if (s->auto_timing_update_event) {
+ struct timeval next;
+ pa_gettimeofday(&next);
+ pa_timeval_add(&next, LATENCY_IPOL_INTERVAL_USEC);
+ s->mainloop->time_restart(s->auto_timing_update_event, &next);
+ }
}
void pa_command_stream_killed(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
@@ -279,6 +326,9 @@ void pa_command_stream_killed(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED
if (!(s = pa_dynarray_get(command == PA_COMMAND_PLAYBACK_STREAM_KILLED ? c->playback_streams : c->record_streams, channel)))
goto finish;
+ if (s->state != PA_STREAM_READY)
+ goto finish;
+
pa_context_set_error(c, PA_ERR_KILLED);
pa_stream_set_state(s, PA_STREAM_FAILED);
@@ -293,6 +343,7 @@ void pa_command_stream_moved(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED u
const char *dn;
pa_bool_t suspended;
uint32_t di;
+ pa_usec_t usec;
pa_assert(pd);
pa_assert(command == PA_COMMAND_PLAYBACK_STREAM_MOVED || command == PA_COMMAND_RECORD_STREAM_MOVED);
@@ -310,12 +361,23 @@ void pa_command_stream_moved(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED u
if (pa_tagstruct_getu32(t, &channel) < 0 ||
pa_tagstruct_getu32(t, &di) < 0 ||
pa_tagstruct_gets(t, &dn) < 0 ||
- pa_tagstruct_get_boolean(t, &suspended) < 0 ||
- !pa_tagstruct_eof(t)) {
+ pa_tagstruct_get_boolean(t, &suspended) < 0) {
pa_context_fail(c, PA_ERR_PROTOCOL);
goto finish;
}
+ if (c->version >= 13) {
+ if (pa_tagstruct_get_usec(t, &usec) < 0) {
+ pa_context_fail(s->context, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+ }
+
+ if (!pa_tagstruct_eof(t)) {
+ pa_context_fail(s->context, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+
if (!dn || di == PA_INVALID_INDEX) {
pa_context_fail(c, PA_ERR_PROTOCOL);
goto finish;
@@ -324,12 +386,24 @@ void pa_command_stream_moved(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED u
if (!(s = pa_dynarray_get(command == PA_COMMAND_PLAYBACK_STREAM_MOVED ? c->playback_streams : c->record_streams, channel)))
goto finish;
+ if (s->state != PA_STREAM_READY)
+ goto finish;
+
+ if (c->version >= 13) {
+ if (s->direction == PA_STREAM_RECORD)
+ s->timing_info.configured_source_usec = usec;
+ else
+ s->timing_info.configured_sink_usec = usec;
+ }
+
pa_xfree(s->device_name);
s->device_name = pa_xstrdup(dn);
s->device_index = di;
s->suspended = suspended;
+ request_auto_timing_update(s, TRUE);
+
if (s->moved_callback)
s->moved_callback(s, s->moved_userdata);
@@ -366,8 +440,23 @@ void pa_command_stream_suspended(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUS
if (!(s = pa_dynarray_get(command == PA_COMMAND_PLAYBACK_STREAM_SUSPENDED ? c->playback_streams : c->record_streams, channel)))
goto finish;
+ if (s->state != PA_STREAM_READY)
+ goto finish;
+
s->suspended = suspended;
+ if (s->smoother) {
+ pa_usec_t x = pa_rtclock_usec();
+
+ if (s->timing_info_valid)
+ x -= s->timing_info.transport_usec;
+
+ if (s->suspended || s->corked)
+ pa_smoother_pause(s->smoother, x);
+ }
+
+ request_auto_timing_update(s, TRUE);
+
if (s->suspended_callback)
s->suspended_callback(s, s->suspended_userdata);
@@ -375,6 +464,45 @@ finish:
pa_context_unref(c);
}
+void pa_command_stream_started(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_context *c = userdata;
+ pa_stream *s;
+ uint32_t channel;
+
+ pa_assert(pd);
+ pa_assert(command == PA_COMMAND_STARTED);
+ pa_assert(t);
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+ pa_context_ref(c);
+
+ if (c->version < 13) {
+ pa_context_fail(c, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+
+ if (pa_tagstruct_getu32(t, &channel) < 0 ||
+ !pa_tagstruct_eof(t)) {
+ pa_context_fail(c, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+
+ if (!(s = pa_dynarray_get(c->playback_streams, channel)))
+ goto finish;
+
+ if (s->state != PA_STREAM_READY)
+ goto finish;
+
+ request_auto_timing_update(s, TRUE);
+
+ if (s->started_callback)
+ s->started_callback(s, s->suspended_userdata);
+
+finish:
+ pa_context_unref(c);
+}
+
void pa_command_request(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
pa_stream *s;
pa_context *c = userdata;
@@ -398,12 +526,13 @@ void pa_command_request(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32
if (!(s = pa_dynarray_get(c->playback_streams, channel)))
goto finish;
- if (s->state == PA_STREAM_READY) {
- s->requested_bytes += bytes;
+ if (s->state != PA_STREAM_READY)
+ goto finish;
- if (s->requested_bytes > 0 && s->write_callback)
- s->write_callback(s, s->requested_bytes, s->write_userdata);
- }
+ s->requested_bytes += bytes;
+
+ if (s->requested_bytes > 0 && s->write_callback)
+ s->write_callback(s, s->requested_bytes, s->write_userdata);
finish:
pa_context_unref(c);
@@ -431,6 +560,21 @@ void pa_command_overflow_or_underflow(pa_pdispatch *pd, uint32_t command, PA_GCC
if (!(s = pa_dynarray_get(c->playback_streams, channel)))
goto finish;
+ if (s->state != PA_STREAM_READY)
+ goto finish;
+
+ if (s->smoother)
+ if (s->direction == PA_STREAM_PLAYBACK && s->buffer_attr.prebuf > 0) {
+ pa_usec_t x = pa_rtclock_usec();
+
+ if (s->timing_info_valid)
+ x -= s->timing_info.transport_usec;
+
+ pa_smoother_pause(s->smoother, x);
+ }
+
+ request_auto_timing_update(s, TRUE);
+
if (s->state == PA_STREAM_READY) {
if (command == PA_COMMAND_OVERFLOW) {
@@ -446,34 +590,7 @@ void pa_command_overflow_or_underflow(pa_pdispatch *pd, uint32_t command, PA_GCC
pa_context_unref(c);
}
-static void request_auto_timing_update(pa_stream *s, int force) {
- pa_assert(s);
- pa_assert(PA_REFCNT_VALUE(s) >= 1);
-
- if (!(s->flags & PA_STREAM_AUTO_TIMING_UPDATE))
- return;
-
- if (s->state == PA_STREAM_READY &&
- (force || !s->auto_timing_update_requested)) {
- pa_operation *o;
-
-/* pa_log("automatically requesting new timing data"); */
-
- if ((o = pa_stream_update_timing_info(s, NULL, NULL))) {
- pa_operation_unref(o);
- s->auto_timing_update_requested = TRUE;
- }
- }
-
- if (s->auto_timing_update_event) {
- struct timeval next;
- pa_gettimeofday(&next);
- pa_timeval_add(&next, LATENCY_IPOL_INTERVAL_USEC);
- s->mainloop->time_restart(s->auto_timing_update_event, &next);
- }
-}
-
-static void invalidate_indexes(pa_stream *s, int r, int w) {
+static void invalidate_indexes(pa_stream *s, pa_bool_t r, pa_bool_t w) {
pa_assert(s);
pa_assert(PA_REFCNT_VALUE(s) >= 1);
@@ -500,11 +617,7 @@ static void invalidate_indexes(pa_stream *s, int r, int w) {
/* pa_log("read_index invalidated"); */
}
- if ((s->direction == PA_STREAM_PLAYBACK && r) ||
- (s->direction == PA_STREAM_RECORD && w))
- s->cached_time_valid = 0;
-
- request_auto_timing_update(s, 1);
+ request_auto_timing_update(s, TRUE);
}
static void auto_timing_update_callback(PA_GCC_UNUSED pa_mainloop_api *m, PA_GCC_UNUSED pa_time_event *e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) {
@@ -513,10 +626,8 @@ static void auto_timing_update_callback(PA_GCC_UNUSED pa_mainloop_api *m, PA_GCC
pa_assert(s);
pa_assert(PA_REFCNT_VALUE(s) >= 1);
-/* pa_log("time event"); */
-
pa_stream_ref(s);
- request_auto_timing_update(s, 0);
+ request_auto_timing_update(s, FALSE);
pa_stream_unref(s);
}
@@ -536,6 +647,8 @@ static void create_stream_complete(pa_stream *s) {
tv.tv_usec += LATENCY_IPOL_INTERVAL_USEC; /* every 100 ms */
pa_assert(!s->auto_timing_update_event);
s->auto_timing_update_event = s->mainloop->time_new(s->mainloop, &tv, &auto_timing_update_callback, s);
+
+ request_auto_timing_update(s, TRUE);
}
}
@@ -577,7 +690,7 @@ void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED
pa_stream_ref(s);
if (command != PA_COMMAND_REPLY) {
- if (pa_context_handle_error(s->context, command, t) < 0)
+ if (pa_context_handle_error(s->context, command, t, FALSE) < 0)
goto finish;
pa_stream_set_state(s, PA_STREAM_FAILED);
@@ -585,7 +698,8 @@ void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED
}
if (pa_tagstruct_getu32(t, &s->channel) < 0 ||
- ((s->direction != PA_STREAM_UPLOAD) && pa_tagstruct_getu32(t, &s->stream_index) < 0) ||
+ s->channel == PA_INVALID_INDEX ||
+ ((s->direction != PA_STREAM_UPLOAD) && (pa_tagstruct_getu32(t, &s->stream_index) < 0 || s->stream_index == PA_INVALID_INDEX)) ||
((s->direction != PA_STREAM_RECORD) && pa_tagstruct_getu32(t, &s->requested_bytes) < 0)) {
pa_context_fail(s->context, PA_ERR_PROTOCOL);
goto finish;
@@ -676,7 +790,7 @@ void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED
NULL);
}
- s->channel_valid = 1;
+ s->channel_valid = TRUE;
pa_dynarray_put((s->direction == PA_STREAM_RECORD) ? s->context->record_streams : s->context->playback_streams, s->channel, s);
create_stream_complete(s);
@@ -737,16 +851,23 @@ static int create_stream(
if (sync_stream)
s->syncid = sync_stream->syncid;
- if (attr) {
+ if (attr)
s->buffer_attr = *attr;
- s->manual_buffer_attr = TRUE;
- } else {
- memset(&s->buffer_attr, 0, sizeof(s->buffer_attr));
- s->manual_buffer_attr = FALSE;
- }
-
automatic_buffer_attr(s, &s->buffer_attr, &s->sample_spec);
+ if (flags & PA_STREAM_INTERPOLATE_TIMING) {
+ pa_usec_t x;
+
+ if (s->smoother)
+ pa_smoother_free(s->smoother);
+
+ s->smoother = pa_smoother_new(SMOOTHER_ADJUST_TIME, SMOOTHER_HISTORY_TIME, !(flags & PA_STREAM_NOT_MONOTONOUS));
+
+ x = pa_rtclock_usec();
+ pa_smoother_set_time_offset(s->smoother, x);
+ pa_smoother_pause(s->smoother, x);
+ }
+
if (!dev)
dev = s->direction == PA_STREAM_PLAYBACK ? s->context->conf->default_sink : s->context->conf->default_source;
@@ -922,31 +1043,31 @@ int pa_stream_write(
if (s->write_index_corrections[s->current_write_index_correction].valid) {
if (seek == PA_SEEK_ABSOLUTE) {
- s->write_index_corrections[s->current_write_index_correction].corrupt = 0;
- s->write_index_corrections[s->current_write_index_correction].absolute = 1;
+ s->write_index_corrections[s->current_write_index_correction].corrupt = FALSE;
+ s->write_index_corrections[s->current_write_index_correction].absolute = TRUE;
s->write_index_corrections[s->current_write_index_correction].value = offset + length;
} else if (seek == PA_SEEK_RELATIVE) {
if (!s->write_index_corrections[s->current_write_index_correction].corrupt)
s->write_index_corrections[s->current_write_index_correction].value += offset + length;
} else
- s->write_index_corrections[s->current_write_index_correction].corrupt = 1;
+ s->write_index_corrections[s->current_write_index_correction].corrupt = TRUE;
}
/* Update the write index in the already available latency data */
if (s->timing_info_valid) {
if (seek == PA_SEEK_ABSOLUTE) {
- s->timing_info.write_index_corrupt = 0;
+ s->timing_info.write_index_corrupt = FALSE;
s->timing_info.write_index = offset + length;
} else if (seek == PA_SEEK_RELATIVE) {
if (!s->timing_info.write_index_corrupt)
s->timing_info.write_index += offset + length;
} else
- s->timing_info.write_index_corrupt = 1;
+ s->timing_info.write_index_corrupt = TRUE;
}
if (!s->timing_info_valid || s->timing_info.write_index_corrupt)
- request_auto_timing_update(s, 1);
+ request_auto_timing_update(s, TRUE);
}
return 0;
@@ -995,9 +1116,7 @@ int pa_stream_drop(pa_stream *s) {
pa_assert(s->peek_data);
pa_memblock_release(s->peek_memchunk.memblock);
pa_memblock_unref(s->peek_memchunk.memblock);
- s->peek_memchunk.length = 0;
- s->peek_memchunk.index = 0;
- s->peek_memchunk.memblock = NULL;
+ pa_memchunk_reset(&s->peek_memchunk);
return 0;
}
@@ -1043,11 +1162,71 @@ pa_operation * pa_stream_drain(pa_stream *s, pa_stream_success_cb_t cb, void *us
return o;
}
+static pa_usec_t calc_time(pa_stream *s, pa_bool_t ignore_transport) {
+ pa_usec_t usec;
+
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
+ pa_assert(s->state == PA_STREAM_READY);
+ pa_assert(s->direction != PA_STREAM_UPLOAD);
+ pa_assert(s->timing_info_valid);
+ pa_assert(s->direction != PA_STREAM_PLAYBACK || !s->timing_info.read_index_corrupt);
+ pa_assert(s->direction != PA_STREAM_RECORD || !s->timing_info.write_index_corrupt);
+
+ if (s->direction == PA_STREAM_PLAYBACK) {
+ /* The last byte that was written into the output device
+ * had this time value associated */
+ usec = pa_bytes_to_usec(s->timing_info.read_index < 0 ? 0 : (uint64_t) s->timing_info.read_index, &s->sample_spec);
+
+ if (!s->corked && !s->suspended) {
+
+ if (!ignore_transport)
+ /* Because the latency info took a little time to come
+ * to us, we assume that the real output time is actually
+ * a little ahead */
+ usec += s->timing_info.transport_usec;
+
+ /* However, the output device usually maintains a buffer
+ too, hence the real sample currently played is a little
+ back */
+ if (s->timing_info.sink_usec >= usec)
+ usec = 0;
+ else
+ usec -= s->timing_info.sink_usec;
+ }
+
+ } else if (s->direction == PA_STREAM_RECORD) {
+ /* The last byte written into the server side queue had
+ * this time value associated */
+ usec = pa_bytes_to_usec(s->timing_info.write_index < 0 ? 0 : (uint64_t) s->timing_info.write_index, &s->sample_spec);
+
+ if (!s->corked && !s->suspended) {
+
+ if (!ignore_transport)
+ /* Add transport latency */
+ usec += s->timing_info.transport_usec;
+
+ /* Add latency of data in device buffer */
+ usec += s->timing_info.source_usec;
+
+ /* If this is a monitor source, we need to correct the
+ * time by the playback device buffer */
+ if (s->timing_info.sink_usec >= usec)
+ usec = 0;
+ else
+ usec -= s->timing_info.sink_usec;
+ }
+ }
+
+ return usec;
+}
+
static void stream_get_timing_info_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
pa_operation *o = userdata;
struct timeval local, remote, now;
pa_timing_info *i;
pa_bool_t playing = FALSE;
+ uint64_t underrun_for = 0, playing_for = 0;
pa_assert(pd);
pa_assert(o);
@@ -1061,29 +1240,46 @@ static void stream_get_timing_info_callback(pa_pdispatch *pd, uint32_t command,
/* pa_log("pre corrupt w:%u r:%u\n", !o->stream->timing_info_valid || i->write_index_corrupt,!o->stream->timing_info_valid || i->read_index_corrupt); */
o->stream->timing_info_valid = FALSE;
- i->write_index_corrupt = 0;
- i->read_index_corrupt = 0;
+ i->write_index_corrupt = FALSE;
+ i->read_index_corrupt = FALSE;
/* pa_log("timing update %u\n", tag); */
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;
- } else if (pa_tagstruct_get_usec(t, &i->sink_usec) < 0 ||
- pa_tagstruct_get_usec(t, &i->source_usec) < 0 ||
- pa_tagstruct_get_boolean(t, &playing) < 0 ||
- pa_tagstruct_get_timeval(t, &local) < 0 ||
- pa_tagstruct_get_timeval(t, &remote) < 0 ||
- pa_tagstruct_gets64(t, &i->write_index) < 0 ||
- pa_tagstruct_gets64(t, &i->read_index) < 0 ||
- !pa_tagstruct_eof(t)) {
- pa_context_fail(o->context, PA_ERR_PROTOCOL);
- goto finish;
-
} else {
- o->stream->timing_info_valid = 1;
+
+ if (pa_tagstruct_get_usec(t, &i->sink_usec) < 0 ||
+ pa_tagstruct_get_usec(t, &i->source_usec) < 0 ||
+ pa_tagstruct_get_boolean(t, &playing) < 0 ||
+ pa_tagstruct_get_timeval(t, &local) < 0 ||
+ pa_tagstruct_get_timeval(t, &remote) < 0 ||
+ pa_tagstruct_gets64(t, &i->write_index) < 0 ||
+ pa_tagstruct_gets64(t, &i->read_index) < 0) {
+
+ pa_context_fail(o->context, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+
+ if (o->context->version >= 13)
+ if (pa_tagstruct_getu64(t, &underrun_for) < 0 ||
+ pa_tagstruct_getu64(t, &playing_for) < 0) {
+
+ pa_context_fail(o->context, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+
+
+ if (!pa_tagstruct_eof(t)) {
+ pa_context_fail(o->context, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+
+ o->stream->timing_info_valid = TRUE;
i->playing = (int) playing;
+ i->since_underrun = playing ? playing_for : underrun_for;
pa_gettimeofday(&now);
@@ -1096,22 +1292,22 @@ static void stream_get_timing_info_callback(pa_pdispatch *pd, uint32_t command,
else
i->transport_usec = pa_timeval_diff(&now, &remote);
- i->synchronized_clocks = 1;
+ i->synchronized_clocks = TRUE;
i->timestamp = remote;
} else {
/* clocks are not synchronized, let's estimate latency then */
i->transport_usec = pa_timeval_diff(&now, &local)/2;
- i->synchronized_clocks = 0;
+ i->synchronized_clocks = FALSE;
i->timestamp = local;
pa_timeval_add(&i->timestamp, i->transport_usec);
}
/* Invalidate read and write indexes if necessary */
if (tag < o->stream->read_index_not_before)
- i->read_index_corrupt = 1;
+ i->read_index_corrupt = TRUE;
if (tag < o->stream->write_index_not_before)
- i->write_index_corrupt = 1;
+ i->write_index_corrupt = TRUE;
if (o->stream->direction == PA_STREAM_PLAYBACK) {
/* Write index correction */
@@ -1137,11 +1333,11 @@ static void stream_get_timing_info_callback(pa_pdispatch *pd, uint32_t command,
if (o->stream->write_index_corrections[j].corrupt) {
/* A corrupting seek was made */
i->write_index = 0;
- i->write_index_corrupt = 1;
+ i->write_index_corrupt = TRUE;
} else if (o->stream->write_index_corrections[j].absolute) {
/* An absolute seek was made */
i->write_index = o->stream->write_index_corrections[j].value;
- i->write_index_corrupt = 0;
+ i->write_index_corrupt = FALSE;
} else if (!i->write_index_corrupt) {
/* A relative seek was made */
i->write_index += o->stream->write_index_corrections[j].value;
@@ -1156,25 +1352,57 @@ static void stream_get_timing_info_callback(pa_pdispatch *pd, uint32_t command,
i->read_index -= pa_memblockq_get_length(o->stream->record_memblockq);
}
- o->stream->cached_time_valid = 0;
- }
-
- o->stream->auto_timing_update_requested = 0;
/* pa_log("post corrupt w:%u r:%u\n", i->write_index_corrupt || !o->stream->timing_info_valid, i->read_index_corrupt || !o->stream->timing_info_valid); */
- /* Clear old correction entries */
- if (o->stream->direction == PA_STREAM_PLAYBACK) {
- int n;
+ /* Clear old correction entries */
+ if (o->stream->direction == PA_STREAM_PLAYBACK) {
+ int n;
+
+ for (n = 0; n < PA_MAX_WRITE_INDEX_CORRECTIONS; n++) {
+ if (!o->stream->write_index_corrections[n].valid)
+ continue;
+
+ if (o->stream->write_index_corrections[n].tag <= tag)
+ o->stream->write_index_corrections[n].valid = FALSE;
+ }
+ }
+
+ /* Update smoother */
+ if (o->stream->smoother) {
+ pa_usec_t u, x;
+
+ u = x = pa_rtclock_usec() - i->transport_usec;
- for (n = 0; n < PA_MAX_WRITE_INDEX_CORRECTIONS; n++) {
- if (!o->stream->write_index_corrections[n].valid)
- continue;
+ if (o->stream->direction == PA_STREAM_PLAYBACK &&
+ o->context->version >= 13) {
+ pa_usec_t su;
- if (o->stream->write_index_corrections[n].tag <= tag)
- o->stream->write_index_corrections[n].valid = 0;
+ /* If we weren't playing then it will take some time
+ * until the audio will actually come out through the
+ * speakers. Since we follow that timing here, we need
+ * to try to fix this up */
+
+ su = pa_bytes_to_usec(i->since_underrun, &o->stream->sample_spec);
+
+ if (su < i->sink_usec)
+ x += i->sink_usec - su;
+ }
+
+ if (!i->playing)
+ pa_smoother_pause(o->stream->smoother, x);
+
+ /* Update the smoother */
+ if ((o->stream->direction == PA_STREAM_PLAYBACK && !i->read_index_corrupt) ||
+ (o->stream->direction == PA_STREAM_RECORD && !i->write_index_corrupt))
+ pa_smoother_put(o->stream->smoother, u, calc_time(o->stream, TRUE));
+
+ if (i->playing)
+ pa_smoother_resume(o->stream->smoother, x);
}
}
+ o->stream->auto_timing_update_requested = FALSE;
+
if (o->stream->latency_update_callback)
o->stream->latency_update_callback(o->stream, o->stream->latency_update_userdata);
@@ -1223,15 +1451,15 @@ pa_operation* pa_stream_update_timing_info(pa_stream *s, pa_stream_success_cb_t
if (s->direction == PA_STREAM_PLAYBACK) {
/* Fill in initial correction data */
- o->stream->current_write_index_correction = cidx;
- o->stream->write_index_corrections[cidx].valid = 1;
- o->stream->write_index_corrections[cidx].tag = tag;
- o->stream->write_index_corrections[cidx].absolute = 0;
- o->stream->write_index_corrections[cidx].value = 0;
- o->stream->write_index_corrections[cidx].corrupt = 0;
- }
-/* pa_log("requesting update %u\n", tag); */
+ s->current_write_index_correction = cidx;
+
+ s->write_index_corrections[cidx].valid = TRUE;
+ s->write_index_corrections[cidx].absolute = FALSE;
+ s->write_index_corrections[cidx].corrupt = FALSE;
+ s->write_index_corrections[cidx].tag = tag;
+ s->write_index_corrections[cidx].value = 0;
+ }
return o;
}
@@ -1246,7 +1474,7 @@ void pa_stream_disconnect_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UN
pa_stream_ref(s);
if (command != PA_COMMAND_REPLY) {
- if (pa_context_handle_error(s->context, command, t) < 0)
+ if (pa_context_handle_error(s->context, command, t, FALSE) < 0)
goto finish;
pa_stream_set_state(s, PA_STREAM_FAILED);
@@ -1291,6 +1519,9 @@ void pa_stream_set_read_callback(pa_stream *s, pa_stream_request_cb_t cb, void *
pa_assert(s);
pa_assert(PA_REFCNT_VALUE(s) >= 1);
+ if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
+ return;
+
s->read_callback = cb;
s->read_userdata = userdata;
}
@@ -1299,6 +1530,9 @@ void pa_stream_set_write_callback(pa_stream *s, pa_stream_request_cb_t cb, void
pa_assert(s);
pa_assert(PA_REFCNT_VALUE(s) >= 1);
+ if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
+ return;
+
s->write_callback = cb;
s->write_userdata = userdata;
}
@@ -1307,6 +1541,9 @@ void pa_stream_set_state_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *
pa_assert(s);
pa_assert(PA_REFCNT_VALUE(s) >= 1);
+ if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
+ return;
+
s->state_callback = cb;
s->state_userdata = userdata;
}
@@ -1315,6 +1552,9 @@ void pa_stream_set_overflow_callback(pa_stream *s, pa_stream_notify_cb_t cb, voi
pa_assert(s);
pa_assert(PA_REFCNT_VALUE(s) >= 1);
+ if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
+ return;
+
s->overflow_callback = cb;
s->overflow_userdata = userdata;
}
@@ -1323,6 +1563,9 @@ void pa_stream_set_underflow_callback(pa_stream *s, pa_stream_notify_cb_t cb, vo
pa_assert(s);
pa_assert(PA_REFCNT_VALUE(s) >= 1);
+ if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
+ return;
+
s->underflow_callback = cb;
s->underflow_userdata = userdata;
}
@@ -1331,6 +1574,9 @@ void pa_stream_set_latency_update_callback(pa_stream *s, pa_stream_notify_cb_t c
pa_assert(s);
pa_assert(PA_REFCNT_VALUE(s) >= 1);
+ if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
+ return;
+
s->latency_update_callback = cb;
s->latency_update_userdata = userdata;
}
@@ -1339,6 +1585,9 @@ void pa_stream_set_moved_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *
pa_assert(s);
pa_assert(PA_REFCNT_VALUE(s) >= 1);
+ if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
+ return;
+
s->moved_callback = cb;
s->moved_userdata = userdata;
}
@@ -1347,10 +1596,24 @@ void pa_stream_set_suspended_callback(pa_stream *s, pa_stream_notify_cb_t cb, vo
pa_assert(s);
pa_assert(PA_REFCNT_VALUE(s) >= 1);
+ if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
+ return;
+
s->suspended_callback = cb;
s->suspended_userdata = userdata;
}
+void pa_stream_set_started_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata) {
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+ if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
+ return;
+
+ s->started_callback = cb;
+ s->started_userdata = userdata;
+}
+
void pa_stream_simple_ack_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
pa_operation *o = userdata;
int success = 1;
@@ -1363,7 +1626,7 @@ void pa_stream_simple_ack_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;
success = 0;
@@ -1406,8 +1669,18 @@ pa_operation* pa_stream_cork(pa_stream *s, int b, pa_stream_success_cb_t cb, voi
pa_pstream_send_tagstruct(s->context->pstream, t);
pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+ if (s->smoother) {
+ pa_usec_t x = pa_rtclock_usec();
+
+ if (s->timing_info_valid)
+ x += s->timing_info.transport_usec;
+
+ if (s->suspended || s->corked)
+ pa_smoother_pause(s->smoother, x);
+ }
+
if (s->direction == PA_STREAM_PLAYBACK)
- invalidate_indexes(s, 1, 0);
+ invalidate_indexes(s, TRUE, FALSE);
return o;
}
@@ -1438,23 +1711,34 @@ pa_operation* pa_stream_flush(pa_stream *s, pa_stream_success_cb_t cb, void *use
pa_assert(s);
pa_assert(PA_REFCNT_VALUE(s) >= 1);
+ PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
if ((o = stream_send_simple_command(s, s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_FLUSH_PLAYBACK_STREAM : PA_COMMAND_FLUSH_RECORD_STREAM, cb, userdata))) {
if (s->direction == PA_STREAM_PLAYBACK) {
if (s->write_index_corrections[s->current_write_index_correction].valid)
- s->write_index_corrections[s->current_write_index_correction].corrupt = 1;
+ s->write_index_corrections[s->current_write_index_correction].corrupt = TRUE;
if (s->timing_info_valid)
- s->timing_info.write_index_corrupt = 1;
+ s->timing_info.write_index_corrupt = TRUE;
if (s->buffer_attr.prebuf > 0)
- invalidate_indexes(s, 1, 0);
+ invalidate_indexes(s, TRUE, FALSE);
else
- request_auto_timing_update(s, 1);
+ request_auto_timing_update(s, TRUE);
+
+ if (s->smoother && s->buffer_attr.prebuf > 0) {
+ pa_usec_t x = pa_rtclock_usec();
+
+ if (s->timing_info_valid)
+ x += s->timing_info.transport_usec;
+
+ pa_smoother_pause(s->smoother, x);
+ }
+
} else
- invalidate_indexes(s, 0, 1);
+ invalidate_indexes(s, FALSE, TRUE);
}
return o;
@@ -1466,11 +1750,12 @@ pa_operation* pa_stream_prebuf(pa_stream *s, pa_stream_success_cb_t cb, void *us
pa_assert(s);
pa_assert(PA_REFCNT_VALUE(s) >= 1);
+ PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction == PA_STREAM_PLAYBACK, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->buffer_attr.prebuf > 0, PA_ERR_BADSTATE);
if ((o = stream_send_simple_command(s, PA_COMMAND_PREBUF_PLAYBACK_STREAM, cb, userdata)))
- invalidate_indexes(s, 1, 0);
+ invalidate_indexes(s, TRUE, FALSE);
return o;
}
@@ -1481,19 +1766,18 @@ pa_operation* pa_stream_trigger(pa_stream *s, pa_stream_success_cb_t cb, void *u
pa_assert(s);
pa_assert(PA_REFCNT_VALUE(s) >= 1);
+ PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction == PA_STREAM_PLAYBACK, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->buffer_attr.prebuf > 0, PA_ERR_BADSTATE);
if ((o = stream_send_simple_command(s, PA_COMMAND_TRIGGER_PLAYBACK_STREAM, cb, userdata)))
- invalidate_indexes(s, 1, 0);
+ invalidate_indexes(s, TRUE, FALSE);
return o;
}
pa_operation* pa_stream_set_name(pa_stream *s, const char *name, pa_stream_success_cb_t cb, void *userdata) {
pa_operation *o;
- pa_tagstruct *t;
- uint32_t tag;
pa_assert(s);
pa_assert(PA_REFCNT_VALUE(s) >= 1);
@@ -1502,22 +1786,32 @@ pa_operation* pa_stream_set_name(pa_stream *s, const char *name, pa_stream_succe
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
- o = pa_operation_new(s->context, s, (pa_operation_cb_t) cb, userdata);
+ if (s->context->version >= 13) {
+ pa_proplist *p = pa_proplist_new();
- t = pa_tagstruct_command(
- s->context,
- s->direction == PA_STREAM_RECORD ? PA_COMMAND_SET_RECORD_STREAM_NAME : PA_COMMAND_SET_PLAYBACK_STREAM_NAME,
- &tag);
- pa_tagstruct_putu32(t, s->channel);
- pa_tagstruct_puts(t, name);
- pa_pstream_send_tagstruct(s->context->pstream, t);
- pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+ pa_proplist_sets(p, PA_PROP_APPLICATION_NAME, name);
+ o = pa_stream_proplist_update(s, PA_UPDATE_REPLACE, p, cb, userdata);
+ pa_proplist_free(p);
+ } else {
+ pa_tagstruct *t;
+ uint32_t tag;
+
+ o = pa_operation_new(s->context, s, (pa_operation_cb_t) cb, userdata);
+ t = pa_tagstruct_command(
+ s->context,
+ s->direction == PA_STREAM_RECORD ? PA_COMMAND_SET_RECORD_STREAM_NAME : PA_COMMAND_SET_PLAYBACK_STREAM_NAME,
+ &tag);
+ pa_tagstruct_putu32(t, s->channel);
+ pa_tagstruct_puts(t, name);
+ pa_pstream_send_tagstruct(s->context->pstream, t);
+ pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+ }
return o;
}
int pa_stream_get_time(pa_stream *s, pa_usec_t *r_usec) {
- pa_usec_t usec = 0;
+ pa_usec_t usec;
pa_assert(s);
pa_assert(PA_REFCNT_VALUE(s) >= 1);
@@ -1528,65 +1822,10 @@ int pa_stream_get_time(pa_stream *s, pa_usec_t *r_usec) {
PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_PLAYBACK || !s->timing_info.read_index_corrupt, PA_ERR_NODATA);
PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_RECORD || !s->timing_info.write_index_corrupt, PA_ERR_NODATA);
- if (s->cached_time_valid)
- /* We alredy calculated the time value for this timing info, so let's reuse it */
- usec = s->cached_time;
- else {
- if (s->direction == PA_STREAM_PLAYBACK) {
- /* The last byte that was written into the output device
- * had this time value associated */
- usec = pa_bytes_to_usec(s->timing_info.read_index < 0 ? 0 : (uint64_t) s->timing_info.read_index, &s->sample_spec);
-
- if (!s->corked) {
- /* Because the latency info took a little time to come
- * to us, we assume that the real output time is actually
- * a little ahead */
- usec += s->timing_info.transport_usec;
-
- /* However, the output device usually maintains a buffer
- too, hence the real sample currently played is a little
- back */
- if (s->timing_info.sink_usec >= usec)
- usec = 0;
- else
- usec -= s->timing_info.sink_usec;
- }
-
- } else if (s->direction == PA_STREAM_RECORD) {
- /* The last byte written into the server side queue had
- * this time value associated */
- usec = pa_bytes_to_usec(s->timing_info.write_index < 0 ? 0 : (uint64_t) s->timing_info.write_index, &s->sample_spec);
-
- if (!s->corked) {
- /* Add transport latency */
- usec += s->timing_info.transport_usec;
-
- /* Add latency of data in device buffer */
- usec += s->timing_info.source_usec;
-
- /* If this is a monitor source, we need to correct the
- * time by the playback device buffer */
- if (s->timing_info.sink_usec >= usec)
- usec = 0;
- else
- usec -= s->timing_info.sink_usec;
- }
- }
-
- s->cached_time = usec;
- s->cached_time_valid = 1;
- }
-
- /* Interpolate if requested */
- if (s->flags & PA_STREAM_INTERPOLATE_TIMING) {
-
- /* We just add the time that passed since the latency info was
- * current */
- if (!s->corked && s->timing_info.playing) {
- struct timeval now;
- usec += pa_timeval_diff(pa_gettimeofday(&now), &s->timing_info.timestamp);
- }
- }
+ if (s->smoother)
+ usec = pa_smoother_get(s->smoother, pa_rtclock_usec());
+ else
+ usec = calc_time(s, FALSE);
/* Make sure the time runs monotonically */
if (!(s->flags & PA_STREAM_NOT_MONOTONOUS)) {
@@ -1687,7 +1926,7 @@ const pa_buffer_attr* pa_stream_get_buffer_attr(pa_stream *s) {
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
- PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->context->version >= 9, PA_ERR_NODATA);
+ PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->context->version >= 9, PA_ERR_NOTSUPPORTED);
return &s->buffer_attr;
}
@@ -1704,7 +1943,7 @@ static void stream_set_buffer_attr_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;
success = 0;
@@ -1730,8 +1969,6 @@ static void stream_set_buffer_attr_callback(pa_pdispatch *pd, uint32_t command,
pa_context_fail(o->context, PA_ERR_PROTOCOL);
goto finish;
}
-
- o->stream->manual_buffer_attr = TRUE;
}
if (o->callback) {
@@ -1822,6 +2059,16 @@ int pa_stream_is_suspended(pa_stream *s) {
return s->suspended;
}
+int pa_stream_is_corked(pa_stream *s) {
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+ PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
+
+ return s->corked;
+}
+
static void stream_update_sample_rate_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
pa_operation *o = userdata;
int success = 1;
@@ -1834,7 +2081,7 @@ static void stream_update_sample_rate_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;
success = 0;
diff --git a/src/pulse/stream.h b/src/pulse/stream.h
index 69943a70..ebb45f2b 100644
--- a/src/pulse/stream.h
+++ b/src/pulse/stream.h
@@ -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) */