diff options
Diffstat (limited to 'src/pulsecore')
-rw-r--r-- | src/pulsecore/cli-command.c | 2 | ||||
-rw-r--r-- | src/pulsecore/core-util.c | 2 | ||||
-rw-r--r-- | src/pulsecore/dbus-shared.c | 111 | ||||
-rw-r--r-- | src/pulsecore/dbus-shared.h | 42 | ||||
-rw-r--r-- | src/pulsecore/dbus-util.c | 397 | ||||
-rw-r--r-- | src/pulsecore/dbus-util.h | 63 | ||||
-rw-r--r-- | src/pulsecore/memblockq.c | 20 | ||||
-rw-r--r-- | src/pulsecore/memblockq.h | 5 | ||||
-rw-r--r-- | src/pulsecore/native-common.h | 6 | ||||
-rw-r--r-- | src/pulsecore/protocol-native.c | 441 | ||||
-rw-r--r-- | src/pulsecore/sink-input.c | 64 | ||||
-rw-r--r-- | src/pulsecore/sink-input.h | 2 | ||||
-rw-r--r-- | src/pulsecore/sink.c | 2 | ||||
-rw-r--r-- | src/pulsecore/source-output.c | 64 | ||||
-rw-r--r-- | src/pulsecore/source-output.h | 2 |
15 files changed, 938 insertions, 285 deletions
diff --git a/src/pulsecore/cli-command.c b/src/pulsecore/cli-command.c index d4d407c6..4017cb68 100644 --- a/src/pulsecore/cli-command.c +++ b/src/pulsecore/cli-command.c @@ -175,7 +175,7 @@ static const struct command commands[] = { { "suspend-sink", pa_cli_command_suspend_sink, "Suspend sink (args: index|name, bool)", 3}, { "suspend-source", pa_cli_command_suspend_source, "Suspend source (args: index|name, bool)", 3}, { "suspend", pa_cli_command_suspend, "Suspend all sinks and all sources (args: bool)", 2}, - { "set-card-profile", pa_cli_command_card_profile, "Change the profile of a card (aargs: index, name)", 3}, + { "set-card-profile", pa_cli_command_card_profile, "Change the profile of a card (args: index, name)", 3}, { "set-log-level", pa_cli_command_log_level, "Change the log level (args: numeric level)", 2}, { "set-log-meta", pa_cli_command_log_meta, "Show source code location in log messages (args: bool)", 2}, { "set-log-time", pa_cli_command_log_time, "Show timestamps in log messages (args: bool)", 2}, diff --git a/src/pulsecore/core-util.c b/src/pulsecore/core-util.c index 0d243ee6..e5d8a2f4 100644 --- a/src/pulsecore/core-util.c +++ b/src/pulsecore/core-util.c @@ -2617,7 +2617,7 @@ char *pa_realpath(const char *path) { return NULL; } -#ifndef __GLIBC__ +#if !defined(__GLIBC__) && !defined(__APPLE__) #error "It's not clear whether this system supports realpath(..., NULL) like GNU libc does. If it doesn't we need a private version of realpath() here." #endif diff --git a/src/pulsecore/dbus-shared.c b/src/pulsecore/dbus-shared.c new file mode 100644 index 00000000..b52c14cb --- /dev/null +++ b/src/pulsecore/dbus-shared.c @@ -0,0 +1,111 @@ +/*** + This file is part of PulseAudio. + + Copyright 2004-2006, 2009 Lennart Poettering + Copyright 2006 Shams E. King + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdarg.h> + +#include <pulse/xmalloc.h> +#include <pulse/timeval.h> +#include <pulsecore/log.h> +#include <pulsecore/shared.h> + +#include "dbus-shared.h" + +struct pa_dbus_connection { + PA_REFCNT_DECLARE; + + pa_dbus_wrap_connection *connection; + pa_core *core; + const char *property_name; +}; + +static pa_dbus_connection* pa_dbus_connection_new(pa_core *c, pa_dbus_wrap_connection *conn, const char *name) { + pa_dbus_connection *pconn; + + pconn = pa_xnew(pa_dbus_connection, 1); + PA_REFCNT_INIT(pconn); + pconn->core = c; + pconn->property_name = name; + pconn->connection = conn; + + pa_shared_set(c, name, pconn); + + return pconn; +} + +pa_dbus_connection* pa_dbus_bus_get(pa_core *c, DBusBusType type, DBusError *error) { + + static const char *const prop_name[] = { + [DBUS_BUS_SESSION] = "dbus-connection-session", + [DBUS_BUS_SYSTEM] = "dbus-connection-system", + [DBUS_BUS_STARTER] = "dbus-connection-starter" + }; + pa_dbus_wrap_connection *conn; + pa_dbus_connection *pconn; + + pa_assert(type == DBUS_BUS_SYSTEM || type == DBUS_BUS_SESSION || type == DBUS_BUS_STARTER); + + if ((pconn = pa_shared_get(c, prop_name[type]))) + return pa_dbus_connection_ref(pconn); + + if (!(conn = pa_dbus_wrap_connection_new(c->mainloop, type, error))) + return NULL; + + pconn = pa_dbus_connection_new(c, conn, prop_name[type]); + + return pconn; +} + +DBusConnection* pa_dbus_connection_get(pa_dbus_connection *c){ + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) > 0); + pa_assert(c->connection); + + return pa_dbus_wrap_connection_get(c->connection); +} + +void pa_dbus_connection_unref(pa_dbus_connection *c) { + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) > 0); + + if (PA_REFCNT_DEC(c) > 0) + return; + + /* already disconnected, just free */ + pa_shared_remove(c->core, c->property_name); + pa_xfree(c); +} + +pa_dbus_connection* pa_dbus_connection_ref(pa_dbus_connection *c) { + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) > 0); + + PA_REFCNT_INC(c); + + return c; +} + + + diff --git a/src/pulsecore/dbus-shared.h b/src/pulsecore/dbus-shared.h new file mode 100644 index 00000000..4c154552 --- /dev/null +++ b/src/pulsecore/dbus-shared.h @@ -0,0 +1,42 @@ +#ifndef foodbussharedhfoo +#define foodbussharedhfoo + +/*** + This file is part of PulseAudio. + + Copyright 2004-2006, 2009 Lennart Poettering + Copyright 2006 Shams E. King + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include <dbus/dbus.h> + +#include <pulsecore/core.h> +#include <pulsecore/dbus-util.h> + +typedef struct pa_dbus_connection pa_dbus_connection; + +/* return a pa_dbus_connection of the specified type for the given core, + * like dbus_bus_get(), but integrates the connection with the pa_core */ +pa_dbus_connection* pa_dbus_bus_get(pa_core *c, DBusBusType type, DBusError *error); + +DBusConnection* pa_dbus_connection_get(pa_dbus_connection *conn); + +pa_dbus_connection* pa_dbus_connection_ref(pa_dbus_connection *conn); +void pa_dbus_connection_unref(pa_dbus_connection *conn); + +#endif diff --git a/src/pulsecore/dbus-util.c b/src/pulsecore/dbus-util.c new file mode 100644 index 00000000..d712bff3 --- /dev/null +++ b/src/pulsecore/dbus-util.c @@ -0,0 +1,397 @@ +/*** + This file is part of PulseAudio. + + Copyright 2006 Lennart Poettering + Copyright 2006 Shams E. King + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdarg.h> + +#include <pulse/xmalloc.h> +#include <pulse/timeval.h> +#include <pulsecore/log.h> + +#include "dbus-util.h" + +struct pa_dbus_wrap_connection { + pa_mainloop_api *mainloop; + DBusConnection *connection; + pa_defer_event* dispatch_event; +}; + +static void dispatch_cb(pa_mainloop_api *ea, pa_defer_event *ev, void *userdata) { + DBusConnection *conn = userdata; + + if (dbus_connection_dispatch(conn) == DBUS_DISPATCH_COMPLETE) { + /* no more data to process, disable the deferred */ + ea->defer_enable(ev, 0); + } +} + +/* DBusDispatchStatusFunction callback for the pa mainloop */ +static void dispatch_status(DBusConnection *conn, DBusDispatchStatus status, void *userdata) { + pa_dbus_wrap_connection *c = userdata; + + pa_assert(c); + + switch(status) { + + case DBUS_DISPATCH_COMPLETE: + c->mainloop->defer_enable(c->dispatch_event, 0); + break; + + case DBUS_DISPATCH_DATA_REMAINS: + case DBUS_DISPATCH_NEED_MEMORY: + default: + c->mainloop->defer_enable(c->dispatch_event, 1); + break; + } +} + +static pa_io_event_flags_t get_watch_flags(DBusWatch *watch) { + unsigned int flags; + pa_io_event_flags_t events = 0; + + pa_assert(watch); + + flags = dbus_watch_get_flags(watch); + + /* no watch flags for disabled watches */ + if (!dbus_watch_get_enabled(watch)) + return PA_IO_EVENT_NULL; + + if (flags & DBUS_WATCH_READABLE) + events |= PA_IO_EVENT_INPUT; + if (flags & DBUS_WATCH_WRITABLE) + events |= PA_IO_EVENT_OUTPUT; + + return events | PA_IO_EVENT_HANGUP | PA_IO_EVENT_ERROR; +} + +/* pa_io_event_cb_t IO event handler */ +static void handle_io_event(pa_mainloop_api *ea, pa_io_event *e, int fd, pa_io_event_flags_t events, void *userdata) { + unsigned int flags = 0; + DBusWatch *watch = userdata; + +#if HAVE_DBUS_WATCH_GET_UNIX_FD + pa_assert(fd == dbus_watch_get_unix_fd(watch)); +#else + pa_assert(fd == dbus_watch_get_fd(watch)); +#endif + + if (!dbus_watch_get_enabled(watch)) { + pa_log_warn("Asked to handle disabled watch: %p %i", (void*) watch, fd); + return; + } + + if (events & PA_IO_EVENT_INPUT) + flags |= DBUS_WATCH_READABLE; + if (events & PA_IO_EVENT_OUTPUT) + flags |= DBUS_WATCH_WRITABLE; + if (events & PA_IO_EVENT_HANGUP) + flags |= DBUS_WATCH_HANGUP; + if (events & PA_IO_EVENT_ERROR) + flags |= DBUS_WATCH_ERROR; + + dbus_watch_handle(watch, flags); +} + +/* pa_time_event_cb_t timer event handler */ +static void handle_time_event(pa_mainloop_api *ea, pa_time_event* e, const struct timeval *tv, void *userdata) { + DBusTimeout *timeout = userdata; + + if (dbus_timeout_get_enabled(timeout)) { + struct timeval next = *tv; + dbus_timeout_handle(timeout); + + /* restart it for the next scheduled time */ + pa_timeval_add(&next, (pa_usec_t) dbus_timeout_get_interval(timeout) * 1000); + ea->time_restart(e, &next); + } +} + +/* DBusAddWatchFunction callback for pa mainloop */ +static dbus_bool_t add_watch(DBusWatch *watch, void *data) { + pa_dbus_wrap_connection *c = data; + pa_io_event *ev; + + pa_assert(watch); + pa_assert(c); + + ev = c->mainloop->io_new( + c->mainloop, +#if HAVE_DBUS_WATCH_GET_UNIX_FD + dbus_watch_get_unix_fd(watch), +#else + dbus_watch_get_fd(watch), +#endif + get_watch_flags(watch), handle_io_event, watch); + + dbus_watch_set_data(watch, ev, NULL); + + return TRUE; +} + +/* DBusRemoveWatchFunction callback for pa mainloop */ +static void remove_watch(DBusWatch *watch, void *data) { + pa_dbus_wrap_connection *c = data; + pa_io_event *ev; + + pa_assert(watch); + pa_assert(c); + + if ((ev = dbus_watch_get_data(watch))) + c->mainloop->io_free(ev); +} + +/* DBusWatchToggledFunction callback for pa mainloop */ +static void toggle_watch(DBusWatch *watch, void *data) { + pa_dbus_wrap_connection *c = data; + pa_io_event *ev; + + pa_assert(watch); + pa_assert(c); + + pa_assert_se(ev = dbus_watch_get_data(watch)); + + /* get_watch_flags() checks if the watch is enabled */ + c->mainloop->io_enable(ev, get_watch_flags(watch)); +} + +/* DBusAddTimeoutFunction callback for pa mainloop */ +static dbus_bool_t add_timeout(DBusTimeout *timeout, void *data) { + pa_dbus_wrap_connection *c = data; + pa_time_event *ev; + struct timeval tv; + + pa_assert(timeout); + pa_assert(c); + + if (!dbus_timeout_get_enabled(timeout)) + return FALSE; + + pa_gettimeofday(&tv); + pa_timeval_add(&tv, (pa_usec_t) dbus_timeout_get_interval(timeout) * 1000); + + ev = c->mainloop->time_new(c->mainloop, &tv, handle_time_event, timeout); + + dbus_timeout_set_data(timeout, ev, NULL); + + return TRUE; +} + +/* DBusRemoveTimeoutFunction callback for pa mainloop */ +static void remove_timeout(DBusTimeout *timeout, void *data) { + pa_dbus_wrap_connection *c = data; + pa_time_event *ev; + + pa_assert(timeout); + pa_assert(c); + + if ((ev = dbus_timeout_get_data(timeout))) + c->mainloop->time_free(ev); +} + +/* DBusTimeoutToggledFunction callback for pa mainloop */ +static void toggle_timeout(DBusTimeout *timeout, void *data) { + pa_dbus_wrap_connection *c = data; + pa_time_event *ev; + + pa_assert(timeout); + pa_assert(c); + + pa_assert_se(ev = dbus_timeout_get_data(timeout)); + + if (dbus_timeout_get_enabled(timeout)) { + struct timeval tv; + + pa_gettimeofday(&tv); + pa_timeval_add(&tv, (pa_usec_t) dbus_timeout_get_interval(timeout) * 1000); + + c->mainloop->time_restart(ev, &tv); + } else + c->mainloop->time_restart(ev, NULL); +} + +static void wakeup_main(void *userdata) { + pa_dbus_wrap_connection *c = userdata; + + pa_assert(c); + + /* this will wakeup the mainloop and dispatch events, although + * it may not be the cleanest way of accomplishing it */ + c->mainloop->defer_enable(c->dispatch_event, 1); +} + +pa_dbus_wrap_connection* pa_dbus_wrap_connection_new(pa_mainloop_api *m, DBusBusType type, DBusError *error) { + DBusConnection *conn; + pa_dbus_wrap_connection *pconn = NULL; + + pa_assert(type == DBUS_BUS_SYSTEM || type == DBUS_BUS_SESSION || type == DBUS_BUS_STARTER); + + if (!(conn = dbus_bus_get_private(type, error))) + return NULL; + + pconn = pa_xnew(pa_dbus_wrap_connection, 1); + pconn->mainloop = m; + pconn->connection = conn; + + dbus_connection_set_exit_on_disconnect(conn, FALSE); + dbus_connection_set_dispatch_status_function(conn, dispatch_status, pconn, NULL); + dbus_connection_set_watch_functions(conn, add_watch, remove_watch, toggle_watch, pconn, NULL); + dbus_connection_set_timeout_functions(conn, add_timeout, remove_timeout, toggle_timeout, pconn, NULL); + dbus_connection_set_wakeup_main_function(conn, wakeup_main, pconn, NULL); + + pconn->dispatch_event = pconn->mainloop->defer_new(pconn->mainloop, dispatch_cb, conn); + + return pconn; +} + +void pa_dbus_wrap_connection_free(pa_dbus_wrap_connection* c) { + pa_assert(c); + + if (dbus_connection_get_is_connected(c->connection)) { + dbus_connection_close(c->connection); + /* must process remaining messages, bit of a kludge to handle + * both unload and shutdown */ + while (dbus_connection_read_write_dispatch(c->connection, -1)); + } + + c->mainloop->defer_free(c->dispatch_event); + dbus_connection_unref(c->connection); + pa_xfree(c); +} + +DBusConnection* pa_dbus_wrap_connection_get(pa_dbus_wrap_connection *c) { + pa_assert(c); + pa_assert(c->connection); + + return c->connection; +} + +int pa_dbus_add_matches(DBusConnection *c, DBusError *error, ...) { + const char *t; + va_list ap; + unsigned k = 0; + + pa_assert(c); + pa_assert(error); + + va_start(ap, error); + while ((t = va_arg(ap, const char*))) { + dbus_bus_add_match(c, t, error); + + if (dbus_error_is_set(error)) + goto fail; + + k++; + } + va_end(ap); + return 0; + +fail: + + va_end(ap); + va_start(ap, error); + for (; k > 0; k--) { + DBusError e; + + pa_assert_se(t = va_arg(ap, const char*)); + + dbus_error_init(&e); + dbus_bus_remove_match(c, t, &e); + dbus_error_free(&e); + } + va_end(ap); + + return -1; +} + +void pa_dbus_remove_matches(DBusConnection *c, ...) { + const char *t; + va_list ap; + DBusError error; + + pa_assert(c); + + dbus_error_init(&error); + + va_start(ap, c); + while ((t = va_arg(ap, const char*))) { + dbus_bus_remove_match(c, t, &error); + dbus_error_free(&error); + } + va_end(ap); +} + +pa_dbus_pending *pa_dbus_pending_new( + DBusConnection *c, + DBusMessage *m, + DBusPendingCall *pending, + void *context_data, + void *call_data) { + + pa_dbus_pending *p; + + pa_assert(pending); + + p = pa_xnew(pa_dbus_pending, 1); + p->connection = c; + p->message = m; + p->pending = pending; + p->context_data = context_data; + p->call_data = call_data; + + PA_LLIST_INIT(pa_dbus_pending, p); + + return p; +} + +void pa_dbus_pending_free(pa_dbus_pending *p) { + pa_assert(p); + + if (p->pending) + dbus_pending_call_cancel(p->pending); /* p->pending is freed by cancel() */ + + if (p->message) + dbus_message_unref(p->message); + + pa_xfree(p); +} + +void pa_dbus_sync_pending_list(pa_dbus_pending **p) { + pa_assert(p); + + while (*p && dbus_connection_read_write_dispatch((*p)->connection, -1)) + ; +} + +void pa_dbus_free_pending_list(pa_dbus_pending **p) { + pa_dbus_pending *i; + + pa_assert(p); + + while ((i = *p)) { + PA_LLIST_REMOVE(pa_dbus_pending, *p, i); + pa_dbus_pending_free(i); + } +} diff --git a/src/pulsecore/dbus-util.h b/src/pulsecore/dbus-util.h new file mode 100644 index 00000000..55cda7a0 --- /dev/null +++ b/src/pulsecore/dbus-util.h @@ -0,0 +1,63 @@ +#ifndef foodbusutilhfoo +#define foodbusutilhfoo + +/*** + This file is part of PulseAudio. + + Copyright 2006 Shams E. King + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include <dbus/dbus.h> + +#include <pulsecore/llist.h> +#include <pulse/mainloop-api.h> + +/* A wrap connection is not shared or refcounted, it is available in client side */ +typedef struct pa_dbus_wrap_connection pa_dbus_wrap_connection; + +pa_dbus_wrap_connection* pa_dbus_wrap_connection_new(pa_mainloop_api *mainloop, DBusBusType type, DBusError *error); +void pa_dbus_wrap_connection_free(pa_dbus_wrap_connection* conn); + +DBusConnection* pa_dbus_wrap_connection_get(pa_dbus_wrap_connection *conn); + +int pa_dbus_add_matches(DBusConnection *c, DBusError *error, ...) PA_GCC_SENTINEL; +void pa_dbus_remove_matches(DBusConnection *c, ...) PA_GCC_SENTINEL; + +typedef struct pa_dbus_pending pa_dbus_pending; + +struct pa_dbus_pending { + DBusConnection *connection; + DBusMessage *message; + DBusPendingCall *pending; + + void *context_data; + void *call_data; + + PA_LLIST_FIELDS(pa_dbus_pending); +}; + +pa_dbus_pending *pa_dbus_pending_new(DBusConnection *c, DBusMessage *m, DBusPendingCall *pending, void *context_data, void *call_data); +void pa_dbus_pending_free(pa_dbus_pending *p); + +/* Sync up a list of pa_dbus_pending_call objects */ +void pa_dbus_sync_pending_list(pa_dbus_pending **p); + +/* Free up a list of pa_dbus_pending_call objects */ +void pa_dbus_free_pending_list(pa_dbus_pending **p); + +#endif diff --git a/src/pulsecore/memblockq.c b/src/pulsecore/memblockq.c index e2be42b3..e6e7b736 100644 --- a/src/pulsecore/memblockq.c +++ b/src/pulsecore/memblockq.c @@ -851,6 +851,26 @@ void pa_memblockq_set_maxrewind(pa_memblockq *bq, size_t maxrewind) { bq->maxrewind = (maxrewind/bq->base)*bq->base; } +void pa_memblockq_apply_attr(pa_memblockq *bq, const pa_buffer_attr *a) { + pa_assert(bq); + pa_assert(a); + + pa_memblockq_set_maxlength(bq, a->maxlength); + pa_memblockq_set_tlength(bq, a->tlength); + pa_memblockq_set_prebuf(bq, a->prebuf); + pa_memblockq_set_minreq(bq, a->minreq); +} + +void pa_memblockq_get_attr(pa_memblockq *bq, pa_buffer_attr *a) { + pa_assert(bq); + pa_assert(a); + + a->maxlength = (uint32_t) pa_memblockq_get_maxlength(bq); + a->tlength = (uint32_t) pa_memblockq_get_tlength(bq); + a->prebuf = (uint32_t) pa_memblockq_get_prebuf(bq); + a->minreq = (uint32_t) pa_memblockq_get_minreq(bq); +} + int pa_memblockq_splice(pa_memblockq *bq, pa_memblockq *source) { pa_assert(bq); diff --git a/src/pulsecore/memblockq.h b/src/pulsecore/memblockq.h index 0a74aa37..e315b831 100644 --- a/src/pulsecore/memblockq.h +++ b/src/pulsecore/memblockq.h @@ -158,6 +158,10 @@ void pa_memblockq_set_minreq(pa_memblockq *memblockq, size_t minreq); void pa_memblockq_set_maxrewind(pa_memblockq *memblockq, size_t maxrewind); /* Set the maximum history size */ void pa_memblockq_set_silence(pa_memblockq *memblockq, pa_memchunk *silence); +/* Apply the data from pa_buffer_attr */ +void pa_memblockq_apply_attr(pa_memblockq *memblockq, const pa_buffer_attr *a); +void pa_memblockq_get_attr(pa_memblockq *bq, pa_buffer_attr *a); + /* Call pa_memchunk_willneed() for every chunk in the queue from the current read pointer to the end */ void pa_memblockq_willneed(pa_memblockq *bq); @@ -175,5 +179,4 @@ pa_bool_t pa_memblockq_prebuf_active(pa_memblockq *bq); /* Return how many items are currently stored in the queue */ unsigned pa_memblockq_get_nblocks(pa_memblockq *bq); - #endif diff --git a/src/pulsecore/native-common.h b/src/pulsecore/native-common.h index 6951e10a..d4d7f3ee 100644 --- a/src/pulsecore/native-common.h +++ b/src/pulsecore/native-common.h @@ -152,7 +152,7 @@ enum { /* Supported since protocol v14 (0.9.12) */ PA_COMMAND_EXTENSION, - /* Supported since protocol v15 (0.9.15*/ + /* Supported since protocol v15 (0.9.15) */ PA_COMMAND_GET_CARD_INFO, PA_COMMAND_GET_CARD_INFO_LIST, PA_COMMAND_SET_CARD_PROFILE, @@ -161,6 +161,10 @@ enum { PA_COMMAND_PLAYBACK_STREAM_EVENT, PA_COMMAND_RECORD_STREAM_EVENT, + /* SERVER->CLIENT */ + PA_COMMAND_PLAYBACK_BUFFER_ATTR_CHANGED, + PA_COMMAND_RECORD_BUFFER_ATTR_CHANGED, + PA_COMMAND_MAX }; diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c index ff49e696..3e75cab8 100644 --- a/src/pulsecore/protocol-native.c +++ b/src/pulsecore/protocol-native.c @@ -81,7 +81,11 @@ typedef struct record_stream { pa_source_output *source_output; pa_memblockq *memblockq; - size_t fragment_size; + + pa_bool_t adjust_latency:1; + pa_bool_t early_requests:1; + + pa_buffer_attr buffer_attr; pa_usec_t source_latency; } record_stream; @@ -105,14 +109,18 @@ typedef struct playback_stream { pa_sink_input *sink_input; pa_memblockq *memblockq; + + pa_bool_t adjust_latency:1; + pa_bool_t early_requests:1; + pa_bool_t is_underrun:1; pa_bool_t drain_request:1; uint32_t drain_tag; uint32_t syncid; pa_atomic_t missing; - size_t minreq; pa_usec_t sink_latency; + pa_buffer_attr buffer_attr; /* Only updated after SINK_INPUT_MESSAGE_UPDATE_LATENCY */ int64_t read_index, write_index; @@ -180,7 +188,8 @@ enum { SINK_INPUT_MESSAGE_TRIGGER, SINK_INPUT_MESSAGE_SEEK, SINK_INPUT_MESSAGE_PREBUF_FORCE, - SINK_INPUT_MESSAGE_UPDATE_LATENCY + SINK_INPUT_MESSAGE_UPDATE_LATENCY, + SINK_INPUT_MESSAGE_UPDATE_BUFFER_ATTR }; enum { @@ -188,7 +197,8 @@ enum { PLAYBACK_STREAM_MESSAGE_UNDERFLOW, PLAYBACK_STREAM_MESSAGE_OVERFLOW, PLAYBACK_STREAM_MESSAGE_DRAIN_ACK, - PLAYBACK_STREAM_MESSAGE_STARTED + PLAYBACK_STREAM_MESSAGE_STARTED, + PLAYBACK_STREAM_MESSAGE_UPDATE_TLENGTH }; enum { @@ -203,7 +213,7 @@ enum { static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk); static void sink_input_kill_cb(pa_sink_input *i); static void sink_input_suspend_cb(pa_sink_input *i, pa_bool_t suspend); -static void sink_input_moved_cb(pa_sink_input *i); +static void sink_input_moving_cb(pa_sink_input *i); static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes); static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes); static void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes); @@ -215,7 +225,7 @@ static void playback_stream_request_bytes(struct playback_stream*s); static void source_output_kill_cb(pa_source_output *o); static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk); static void source_output_suspend_cb(pa_source_output *o, pa_bool_t suspend); -static void source_output_moved_cb(pa_source_output *o); +static void source_output_moving_cb(pa_source_output *o); static pa_usec_t source_output_get_latency_cb(pa_source_output *o); static void source_output_send_event_cb(pa_source_output *o, const char *event, pa_proplist *pl); @@ -360,6 +370,7 @@ static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = { /* structure management */ +/* Called from main context */ static void upload_stream_unlink(upload_stream *s) { pa_assert(s); @@ -371,6 +382,7 @@ static void upload_stream_unlink(upload_stream *s) { upload_stream_unref(s); } +/* Called from main context */ static void upload_stream_free(pa_object *o) { upload_stream *s = UPLOAD_STREAM(o); pa_assert(s); @@ -388,6 +400,7 @@ static void upload_stream_free(pa_object *o) { pa_xfree(s); } +/* Called from main context */ static upload_stream* upload_stream_new( pa_native_connection *c, const pa_sample_spec *ss, @@ -420,6 +433,7 @@ static upload_stream* upload_stream_new( return s; } +/* Called from main context */ static void record_stream_unlink(record_stream *s) { pa_assert(s); @@ -437,6 +451,7 @@ static void record_stream_unlink(record_stream *s) { record_stream_unref(s); } +/* Called from main context */ static void record_stream_free(pa_object *o) { record_stream *s = RECORD_STREAM(o); pa_assert(s); @@ -447,6 +462,7 @@ static void record_stream_free(pa_object *o) { pa_xfree(s); } +/* Called from main context */ static int record_stream_process_msg(pa_msgobject *o, int code, void*userdata, int64_t offset, pa_memchunk *chunk) { record_stream *s = RECORD_STREAM(o); record_stream_assert_ref(s); @@ -472,35 +488,34 @@ static int record_stream_process_msg(pa_msgobject *o, int code, void*userdata, i return 0; } -static void fix_record_buffer_attr_pre( - record_stream *s, - pa_bool_t adjust_latency, - pa_bool_t early_requests, - uint32_t *maxlength, - uint32_t *fragsize) { +/* Called from main context */ +static void fix_record_buffer_attr_pre(record_stream *s) { size_t frame_size; pa_usec_t orig_fragsize_usec, fragsize_usec, source_usec; pa_assert(s); - pa_assert(maxlength); - pa_assert(fragsize); + + /* This function will be called from the main thread, before as + * well as after the source output has been activated using + * pa_source_output_put()! That means it may not touch any + * ->thread_info data! */ frame_size = pa_frame_size(&s->source_output->sample_spec); - if (*maxlength == (uint32_t) -1 || *maxlength > MAX_MEMBLOCKQ_LENGTH) - *maxlength = MAX_MEMBLOCKQ_LENGTH; - if (*maxlength <= 0) - *maxlength = (uint32_t) frame_size; + if (s->buffer_attr.maxlength == (uint32_t) -1 || s->buffer_attr.maxlength > MAX_MEMBLOCKQ_LENGTH) + s->buffer_attr.maxlength = MAX_MEMBLOCKQ_LENGTH; + if (s->buffer_attr.maxlength <= 0) + s->buffer_attr.maxlength = (uint32_t) frame_size; - if (*fragsize == (uint32_t) -1) - *fragsize = (uint32_t) pa_usec_to_bytes(DEFAULT_FRAGSIZE_MSEC*PA_USEC_PER_MSEC, &s->source_output->sample_spec); - if (*fragsize <= 0) - *fragsize = (uint32_t) frame_size; + if (s->buffer_attr.fragsize == (uint32_t) -1) + s->buffer_attr.fragsize = (uint32_t) pa_usec_to_bytes(DEFAULT_FRAGSIZE_MSEC*PA_USEC_PER_MSEC, &s->source_output->sample_spec); + if (s->buffer_attr.fragsize <= 0) + s->buffer_attr.fragsize = (uint32_t) frame_size; - orig_fragsize_usec = fragsize_usec = pa_bytes_to_usec(*fragsize, &s->source_output->sample_spec); + orig_fragsize_usec = fragsize_usec = pa_bytes_to_usec(s->buffer_attr.fragsize, &s->source_output->sample_spec); - if (early_requests) { + if (s->early_requests) { /* In early request mode we need to emulate the classic * fragment-based playback model. We do this setting the source @@ -508,7 +523,7 @@ static void fix_record_buffer_attr_pre( source_usec = fragsize_usec; - } else if (adjust_latency) { + } else if (s->adjust_latency) { /* So, the user asked us to adjust the latency according to * what the source can provide. Half the latency will be @@ -530,14 +545,14 @@ static void fix_record_buffer_attr_pre( else s->source_latency = 0; - if (early_requests) { + if (s->early_requests) { /* Ok, we didn't necessarily get what we were asking for, so * let's tell the user */ fragsize_usec = s->source_latency; - } else if (adjust_latency) { + } else if (s->adjust_latency) { /* Now subtract what we actually got */ @@ -550,45 +565,41 @@ static void fix_record_buffer_attr_pre( if (pa_usec_to_bytes(orig_fragsize_usec, &s->source_output->sample_spec) != pa_usec_to_bytes(fragsize_usec, &s->source_output->sample_spec)) - *fragsize = (uint32_t) pa_usec_to_bytes(fragsize_usec, &s->source_output->sample_spec); + s->buffer_attr.fragsize = (uint32_t) pa_usec_to_bytes(fragsize_usec, &s->source_output->sample_spec); - if (*fragsize <= 0) - *fragsize = (uint32_t) frame_size; + if (s->buffer_attr.fragsize <= 0) + s->buffer_attr.fragsize = (uint32_t) frame_size; } -static void fix_record_buffer_attr_post( - record_stream *s, - uint32_t *maxlength, - uint32_t *fragsize) { - +/* Called from main context */ +static void fix_record_buffer_attr_post(record_stream *s) { size_t base; pa_assert(s); - pa_assert(maxlength); - pa_assert(fragsize); - *maxlength = (uint32_t) pa_memblockq_get_maxlength(s->memblockq); + /* This function will be called from the main thread, before as + * well as after the source output has been activated using + * pa_source_output_put()! That means it may not touch and + * ->thread_info data! */ base = pa_frame_size(&s->source_output->sample_spec); - s->fragment_size = (*fragsize/base)*base; - if (s->fragment_size <= 0) - s->fragment_size = base; + s->buffer_attr.fragsize = (s->buffer_attr.fragsize/base)*base; + if (s->buffer_attr.fragsize <= 0) + s->buffer_attr.fragsize = base; - if (s->fragment_size > *maxlength) - s->fragment_size = *maxlength; - - *fragsize = (uint32_t) s->fragment_size; + if (s->buffer_attr.fragsize > s->buffer_attr.maxlength) + s->buffer_attr.fragsize = s->buffer_attr.maxlength; } +/* Called from main context */ static record_stream* record_stream_new( pa_native_connection *c, pa_source *source, pa_sample_spec *ss, pa_channel_map *map, pa_bool_t peak_detect, - uint32_t *maxlength, - uint32_t *fragsize, + pa_buffer_attr *attr, pa_source_output_flags_t flags, pa_proplist *p, pa_bool_t adjust_latency, @@ -603,7 +614,6 @@ static record_stream* record_stream_new( pa_assert(c); pa_assert(ss); - pa_assert(maxlength); pa_assert(p); pa_assert(ret); @@ -632,20 +642,23 @@ static record_stream* record_stream_new( s->parent.process_msg = record_stream_process_msg; s->connection = c; s->source_output = source_output; + s->buffer_attr = *attr; + s->adjust_latency = adjust_latency; + s->early_requests = early_requests; s->source_output->push = source_output_push_cb; s->source_output->kill = source_output_kill_cb; s->source_output->get_latency = source_output_get_latency_cb; - s->source_output->moved = source_output_moved_cb; + s->source_output->moving = source_output_moving_cb; s->source_output->suspend = source_output_suspend_cb; s->source_output->send_event = source_output_send_event_cb; s->source_output->userdata = s; - fix_record_buffer_attr_pre(s, adjust_latency, early_requests, maxlength, fragsize); + fix_record_buffer_attr_pre(s); s->memblockq = pa_memblockq_new( 0, - *maxlength, + s->buffer_attr.maxlength, 0, base = pa_frame_size(&source_output->sample_spec), 1, @@ -653,7 +666,8 @@ static record_stream* record_stream_new( 0, NULL); - fix_record_buffer_attr_post(s, maxlength, fragsize); + pa_memblockq_get_attr(s->memblockq, &s->buffer_attr); + fix_record_buffer_attr_post(s); *ss = s->source_output->sample_spec; *map = s->source_output->channel_map; @@ -661,14 +675,15 @@ static record_stream* record_stream_new( pa_idxset_put(c->record_streams, s, &s->index); pa_log_info("Final latency %0.2f ms = %0.2f ms + %0.2f ms", - ((double) pa_bytes_to_usec(s->fragment_size, &source_output->sample_spec) + (double) s->source_latency) / PA_USEC_PER_MSEC, - (double) pa_bytes_to_usec(s->fragment_size, &source_output->sample_spec) / PA_USEC_PER_MSEC, + ((double) pa_bytes_to_usec(s->buffer_attr.fragsize, &source_output->sample_spec) + (double) s->source_latency) / PA_USEC_PER_MSEC, + (double) pa_bytes_to_usec(s->buffer_attr.fragsize, &source_output->sample_spec) / PA_USEC_PER_MSEC, (double) s->source_latency / PA_USEC_PER_MSEC); pa_source_output_put(s->source_output); return s; } +/* Called from main context */ static void record_stream_send_killed(record_stream *r) { pa_tagstruct *t; record_stream_assert_ref(r); @@ -680,6 +695,7 @@ static void record_stream_send_killed(record_stream *r) { pa_pstream_send_tagstruct(r->connection->pstream, t); } +/* Called from main context */ static void playback_stream_unlink(playback_stream *s) { pa_assert(s); @@ -700,6 +716,7 @@ static void playback_stream_unlink(playback_stream *s) { playback_stream_unref(s); } +/* Called from main context */ static void playback_stream_free(pa_object* o) { playback_stream *s = PLAYBACK_STREAM(o); pa_assert(s); @@ -710,6 +727,7 @@ static void playback_stream_free(pa_object* o) { pa_xfree(s); } +/* Called from main context */ static int playback_stream_process_msg(pa_msgobject *o, int code, void*userdata, int64_t offset, pa_memchunk *chunk) { playback_stream *s = PLAYBACK_STREAM(o); playback_stream_assert_ref(s); @@ -788,67 +806,79 @@ static int playback_stream_process_msg(pa_msgobject *o, int code, void*userdata, case PLAYBACK_STREAM_MESSAGE_DRAIN_ACK: pa_pstream_send_simple_ack(s->connection->pstream, PA_PTR_TO_UINT(userdata)); break; + + case PLAYBACK_STREAM_MESSAGE_UPDATE_TLENGTH: { + pa_tagstruct *t; + + s->buffer_attr.tlength = (uint32_t) offset; + + t = pa_tagstruct_new(NULL, 0); + pa_tagstruct_putu32(t, PA_COMMAND_PLAYBACK_BUFFER_ATTR_CHANGED); + pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */ + pa_tagstruct_putu32(t, s->index); + pa_tagstruct_putu32(t, s->buffer_attr.maxlength); + pa_tagstruct_putu32(t, s->buffer_attr.tlength); + pa_tagstruct_putu32(t, s->buffer_attr.prebuf); + pa_tagstruct_putu32(t, s->buffer_attr.minreq); + pa_tagstruct_put_usec(t, s->sink_latency); + pa_pstream_send_tagstruct(s->connection->pstream, t); + + break; + } } return 0; } -static void fix_playback_buffer_attr_pre( - playback_stream *s, - pa_bool_t adjust_latency, - pa_bool_t early_requests, - uint32_t *maxlength, - uint32_t *tlength, - uint32_t* prebuf, - uint32_t* minreq) { - +/* Called from main context */ +static void fix_playback_buffer_attr(playback_stream *s) { size_t frame_size; pa_usec_t orig_tlength_usec, tlength_usec, orig_minreq_usec, minreq_usec, sink_usec; pa_assert(s); - pa_assert(maxlength); - pa_assert(tlength); - pa_assert(prebuf); - pa_assert(minreq); + + /* This function will be called from the main thread, before as + * well as after the sink input has been activated using + * pa_sink_input_put()! That means it may not touch any + * ->thread_info data, such as the memblockq! */ frame_size = pa_frame_size(&s->sink_input->sample_spec); - if (*maxlength == (uint32_t) -1 || *maxlength > MAX_MEMBLOCKQ_LENGTH) - *maxlength = MAX_MEMBLOCKQ_LENGTH; - if (*maxlength <= 0) - *maxlength = (uint32_t) frame_size; + if (s->buffer_attr.maxlength == (uint32_t) -1 || s->buffer_attr.maxlength > MAX_MEMBLOCKQ_LENGTH) + s->buffer_attr.maxlength = MAX_MEMBLOCKQ_LENGTH; + if (s->buffer_attr.maxlength <= 0) + s->buffer_attr.maxlength = (uint32_t) frame_size; - if (*tlength == (uint32_t) -1) - *tlength = (uint32_t) pa_usec_to_bytes_round_up(DEFAULT_TLENGTH_MSEC*PA_USEC_PER_MSEC, &s->sink_input->sample_spec); - if (*tlength <= 0) - *tlength = (uint32_t) frame_size; + if (s->buffer_attr.tlength == (uint32_t) -1) + s->buffer_attr.tlength = (uint32_t) pa_usec_to_bytes_round_up(DEFAULT_TLENGTH_MSEC*PA_USEC_PER_MSEC, &s->sink_input->sample_spec); + if (s->buffer_attr.tlength <= 0) + s->buffer_attr.tlength = (uint32_t) frame_size; - if (*minreq == (uint32_t) -1) - *minreq = (uint32_t) pa_usec_to_bytes_round_up(DEFAULT_PROCESS_MSEC*PA_USEC_PER_MSEC, &s->sink_input->sample_spec); - if (*minreq <= 0) - *minreq = (uint32_t) frame_size; + if (s->buffer_attr.minreq == (uint32_t) -1) + s->buffer_attr.minreq = (uint32_t) pa_usec_to_bytes_round_up(DEFAULT_PROCESS_MSEC*PA_USEC_PER_MSEC, &s->sink_input->sample_spec); + if (s->buffer_attr.minreq <= 0) + s->buffer_attr.minreq = (uint32_t) frame_size; - if (*tlength < *minreq+frame_size) - *tlength = *minreq+(uint32_t) frame_size; + if (s->buffer_attr.tlength < s->buffer_attr.minreq+frame_size) + s->buffer_attr.tlength = s->buffer_attr.minreq+(uint32_t) frame_size; - orig_tlength_usec = tlength_usec = pa_bytes_to_usec(*tlength, &s->sink_input->sample_spec); - orig_minreq_usec = minreq_usec = pa_bytes_to_usec(*minreq, &s->sink_input->sample_spec); + orig_tlength_usec = tlength_usec = pa_bytes_to_usec(s->buffer_attr.tlength, &s->sink_input->sample_spec); + orig_minreq_usec = minreq_usec = pa_bytes_to_usec(s->buffer_attr.minreq, &s->sink_input->sample_spec); pa_log_info("Requested tlength=%0.2f ms, minreq=%0.2f ms", (double) tlength_usec / PA_USEC_PER_MSEC, (double) minreq_usec / PA_USEC_PER_MSEC); - if (early_requests) { + if (s->early_requests) { /* In early request mode we need to emulate the classic * fragment-based playback model. We do this setting the sink * latency to the fragment size. */ sink_usec = minreq_usec; - pa_log_debug("Early requests mode enabled, configuring sink latency to minreq."); - } else if (adjust_latency) { + } else if (s->adjust_latency) { /* So, the user asked us to adjust the latency of the stream * buffer according to the what the sink can provide. The @@ -890,14 +920,14 @@ static void fix_playback_buffer_attr_pre( s->sink_latency = pa_sink_input_set_requested_latency(s->sink_input, sink_usec); - if (early_requests) { + if (s->early_requests) { /* Ok, we didn't necessarily get what we were asking for, so * let's tell the user */ minreq_usec = s->sink_latency; - } else if (adjust_latency) { + } else if (s->adjust_latency) { /* Ok, we didn't necessarily get what we were asking for, so * let's subtract from what we asked for for the remaining @@ -914,54 +944,31 @@ static void fix_playback_buffer_attr_pre( if (pa_usec_to_bytes_round_up(orig_tlength_usec, &s->sink_input->sample_spec) != pa_usec_to_bytes_round_up(tlength_usec, &s->sink_input->sample_spec)) - *tlength = (uint32_t) pa_usec_to_bytes_round_up(tlength_usec, &s->sink_input->sample_spec); + s->buffer_attr.tlength = (uint32_t) pa_usec_to_bytes_round_up(tlength_usec, &s->sink_input->sample_spec); if (pa_usec_to_bytes(orig_minreq_usec, &s->sink_input->sample_spec) != pa_usec_to_bytes(minreq_usec, &s->sink_input->sample_spec)) - *minreq = (uint32_t) pa_usec_to_bytes(minreq_usec, &s->sink_input->sample_spec); + s->buffer_attr.minreq = (uint32_t) pa_usec_to_bytes(minreq_usec, &s->sink_input->sample_spec); - if (*minreq <= 0) { - *minreq = (uint32_t) frame_size; - *tlength += (uint32_t) frame_size*2; + if (s->buffer_attr.minreq <= 0) { + s->buffer_attr.minreq = (uint32_t) frame_size; + s->buffer_attr.tlength += (uint32_t) frame_size*2; } - if (*tlength <= *minreq) - *tlength = *minreq*2 + (uint32_t) frame_size; - - if (*prebuf == (uint32_t) -1 || *prebuf > *tlength) - *prebuf = *tlength; -} - -static void fix_playback_buffer_attr_post( - playback_stream *s, - uint32_t *maxlength, - uint32_t *tlength, - uint32_t* prebuf, - uint32_t* minreq) { - - pa_assert(s); - pa_assert(maxlength); - pa_assert(tlength); - pa_assert(prebuf); - pa_assert(minreq); - - *maxlength = (uint32_t) pa_memblockq_get_maxlength(s->memblockq); - *tlength = (uint32_t) pa_memblockq_get_tlength(s->memblockq); - *prebuf = (uint32_t) pa_memblockq_get_prebuf(s->memblockq); - *minreq = (uint32_t) pa_memblockq_get_minreq(s->memblockq); + if (s->buffer_attr.tlength <= s->buffer_attr.minreq) + s->buffer_attr.tlength = s->buffer_attr.minreq*2 + (uint32_t) frame_size; - s->minreq = *minreq; + if (s->buffer_attr.prebuf == (uint32_t) -1 || s->buffer_attr.prebuf > s->buffer_attr.tlength) + s->buffer_attr.prebuf = s->buffer_attr.tlength; } +/* Called from main context */ static playback_stream* playback_stream_new( pa_native_connection *c, pa_sink *sink, pa_sample_spec *ss, pa_channel_map *map, - uint32_t *maxlength, - uint32_t *tlength, - uint32_t *prebuf, - uint32_t *minreq, + pa_buffer_attr *a, pa_cvolume *volume, pa_bool_t muted, pa_bool_t muted_set, @@ -982,10 +989,6 @@ static playback_stream* playback_stream_new( pa_assert(c); pa_assert(ss); - pa_assert(maxlength); - pa_assert(tlength); - pa_assert(prebuf); - pa_assert(minreq); pa_assert(missing); pa_assert(p); pa_assert(ret); @@ -1042,6 +1045,9 @@ static playback_stream* playback_stream_new( s->is_underrun = TRUE; s->drain_request = FALSE; pa_atomic_store(&s->missing, 0); + s->buffer_attr = *a; + s->adjust_latency = adjust_latency; + s->early_requests = early_requests; s->sink_input->parent.process_msg = sink_input_process_msg; s->sink_input->pop = sink_input_pop_cb; @@ -1049,28 +1055,28 @@ static playback_stream* playback_stream_new( s->sink_input->update_max_rewind = sink_input_update_max_rewind_cb; s->sink_input->update_max_request = sink_input_update_max_request_cb; s->sink_input->kill = sink_input_kill_cb; - s->sink_input->moved = sink_input_moved_cb; + s->sink_input->moving = sink_input_moving_cb; s->sink_input->suspend = sink_input_suspend_cb; s->sink_input->send_event = sink_input_send_event_cb; s->sink_input->userdata = s; start_index = ssync ? pa_memblockq_get_read_index(ssync->memblockq) : 0; - fix_playback_buffer_attr_pre(s, adjust_latency, early_requests, maxlength, tlength, prebuf, minreq); - pa_sink_input_get_silence(sink_input, &silence); + fix_playback_buffer_attr(s); + pa_sink_input_get_silence(sink_input, &silence); s->memblockq = pa_memblockq_new( start_index, - *maxlength, - *tlength, + s->buffer_attr.maxlength, + s->buffer_attr.tlength, pa_frame_size(&sink_input->sample_spec), - *prebuf, - *minreq, + s->buffer_attr.prebuf, + s->buffer_attr.minreq, 0, &silence); - pa_memblock_unref(silence.memblock); - fix_playback_buffer_attr_post(s, maxlength, tlength, prebuf, minreq); + + pa_memblockq_get_attr(s->memblockq, &s->buffer_attr); *missing = (uint32_t) pa_memblockq_pop_missing(s->memblockq); @@ -1080,18 +1086,18 @@ static playback_stream* playback_stream_new( pa_idxset_put(c->output_streams, s, &s->index); pa_log_info("Final latency %0.2f ms = %0.2f ms + 2*%0.2f ms + %0.2f ms", - ((double) pa_bytes_to_usec(*tlength, &sink_input->sample_spec) + (double) s->sink_latency) / PA_USEC_PER_MSEC, - (double) pa_bytes_to_usec(*tlength-*minreq*2, &sink_input->sample_spec) / PA_USEC_PER_MSEC, - (double) pa_bytes_to_usec(*minreq, &sink_input->sample_spec) / PA_USEC_PER_MSEC, + ((double) pa_bytes_to_usec(s->buffer_attr.tlength, &sink_input->sample_spec) + (double) s->sink_latency) / PA_USEC_PER_MSEC, + (double) pa_bytes_to_usec(s->buffer_attr.tlength-s->buffer_attr.minreq*2, &sink_input->sample_spec) / PA_USEC_PER_MSEC, + (double) pa_bytes_to_usec(s->buffer_attr.minreq, &sink_input->sample_spec) / PA_USEC_PER_MSEC, (double) s->sink_latency / PA_USEC_PER_MSEC); pa_sink_input_put(s->sink_input); return s; } -/* Called from thread context */ +/* Called from IO context */ static void playback_stream_request_bytes(playback_stream *s) { - size_t m, previous_missing; + size_t m, previous_missing, minreq; playback_stream_assert_ref(s); @@ -1103,13 +1109,15 @@ static void playback_stream_request_bytes(playback_stream *s) { /* pa_log("request_bytes(%lu)", (unsigned long) m); */ previous_missing = (size_t) pa_atomic_add(&s->missing, (int) m); + minreq = pa_memblockq_get_minreq(s->memblockq); if (pa_memblockq_prebuf_active(s->memblockq) || - (previous_missing < s->minreq && previous_missing+m >= s->minreq)) + (previous_missing < minreq && previous_missing+m >= minreq)) pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_REQUEST_DATA, NULL, 0, NULL, NULL); } +/* Called from main context */ static void playback_stream_send_killed(playback_stream *p) { pa_tagstruct *t; playback_stream_assert_ref(p); @@ -1121,6 +1129,7 @@ static void playback_stream_send_killed(playback_stream *p) { pa_pstream_send_tagstruct(p->connection->pstream, t); } +/* Called from main context */ static int native_connection_process_msg(pa_msgobject *o, int code, void*userdata, int64_t offset, pa_memchunk *chunk) { pa_native_connection *c = PA_NATIVE_CONNECTION(o); pa_native_connection_assert_ref(c); @@ -1142,6 +1151,7 @@ static int native_connection_process_msg(pa_msgobject *o, int code, void*userdat return 0; } +/* Called from main context */ static void native_connection_unlink(pa_native_connection *c) { record_stream *r; output_stream *o; @@ -1181,6 +1191,7 @@ static void native_connection_unlink(pa_native_connection *c) { pa_native_connection_unref(c); } +/* Called from main context */ static void native_connection_free(pa_object *o) { pa_native_connection *c = PA_NATIVE_CONNECTION(o); @@ -1198,6 +1209,7 @@ static void native_connection_free(pa_object *o) { pa_xfree(c); } +/* Called from main context */ static void native_connection_send_memblock(pa_native_connection *c) { uint32_t start; record_stream *r; @@ -1217,8 +1229,8 @@ static void native_connection_send_memblock(pa_native_connection *c) { if (pa_memblockq_peek(r->memblockq, &chunk) >= 0) { pa_memchunk schunk = chunk; - if (schunk.length > r->fragment_size) - schunk.length = r->fragment_size; + if (schunk.length > r->buffer_attr.fragsize) + schunk.length = r->buffer_attr.fragsize; pa_pstream_send_memblock(c->pstream, r->index, 0, PA_SEEK_RELATIVE, &schunk); @@ -1232,6 +1244,7 @@ static void native_connection_send_memblock(pa_native_connection *c) { /*** sink input callbacks ***/ +/* Called from thread context */ static void handle_seek(playback_stream *s, int64_t indexw) { playback_stream_assert_ref(s); @@ -1399,6 +1412,12 @@ static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int * latency added by the resampler */ break; } + + case SINK_INPUT_MESSAGE_UPDATE_BUFFER_ATTR: { + pa_memblockq_apply_attr(s->memblockq, &s->buffer_attr); + pa_memblockq_get_attr(s->memblockq, &s->buffer_attr); + return 0; + } } return pa_sink_input_process_msg(o, code, userdata, offset, chunk); @@ -1447,6 +1466,7 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk return 0; } +/* Called from thread context */ static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) { playback_stream *s; @@ -1461,6 +1481,7 @@ static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) { pa_memblockq_rewind(s->memblockq, nbytes); } +/* Called from thread context */ static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) { playback_stream *s; @@ -1471,6 +1492,7 @@ static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) { pa_memblockq_set_maxrewind(s->memblockq, nbytes); } +/* Called from thread context */ static void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes) { playback_stream *s; size_t tlength; @@ -1481,8 +1503,10 @@ static void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes) { tlength = nbytes+2*pa_memblockq_get_minreq(s->memblockq); - if (pa_memblockq_get_tlength(s->memblockq) < tlength) + if (pa_memblockq_get_tlength(s->memblockq) < tlength) { pa_memblockq_set_tlength(s->memblockq, tlength); + pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_UPDATE_TLENGTH, NULL, pa_memblockq_get_tlength(s->memblockq), NULL, NULL); + } } /* Called from main context */ @@ -1539,26 +1563,17 @@ static void sink_input_suspend_cb(pa_sink_input *i, pa_bool_t suspend) { } /* Called from main context */ -static void sink_input_moved_cb(pa_sink_input *i) { +static void sink_input_moving_cb(pa_sink_input *i) { playback_stream *s; pa_tagstruct *t; - uint32_t maxlength, tlength, prebuf, minreq; pa_sink_input_assert_ref(i); s = PLAYBACK_STREAM(i->userdata); playback_stream_assert_ref(s); - maxlength = (uint32_t) pa_memblockq_get_maxlength(s->memblockq); - tlength = (uint32_t) pa_memblockq_get_tlength(s->memblockq); - prebuf = (uint32_t) pa_memblockq_get_prebuf(s->memblockq); - minreq = (uint32_t) pa_memblockq_get_minreq(s->memblockq); - - fix_playback_buffer_attr_pre(s, TRUE, FALSE, &maxlength, &tlength, &prebuf, &minreq); - pa_memblockq_set_maxlength(s->memblockq, maxlength); - pa_memblockq_set_tlength(s->memblockq, tlength); - pa_memblockq_set_prebuf(s->memblockq, prebuf); - pa_memblockq_set_minreq(s->memblockq, minreq); - fix_playback_buffer_attr_post(s, &maxlength, &tlength, &prebuf, &minreq); + fix_playback_buffer_attr(s); + pa_memblockq_apply_attr(s->memblockq, &s->buffer_attr); + pa_memblockq_get_attr(s->memblockq, &s->buffer_attr); if (s->connection->version < 12) return; @@ -1572,10 +1587,10 @@ static void sink_input_moved_cb(pa_sink_input *i) { pa_tagstruct_put_boolean(t, pa_sink_get_state(i->sink) == PA_SINK_SUSPENDED); if (s->connection->version >= 13) { - pa_tagstruct_putu32(t, maxlength); - pa_tagstruct_putu32(t, tlength); - pa_tagstruct_putu32(t, prebuf); - pa_tagstruct_putu32(t, minreq); + pa_tagstruct_putu32(t, s->buffer_attr.maxlength); + pa_tagstruct_putu32(t, s->buffer_attr.tlength); + pa_tagstruct_putu32(t, s->buffer_attr.prebuf); + pa_tagstruct_putu32(t, s->buffer_attr.minreq); pa_tagstruct_put_usec(t, s->sink_latency); } @@ -1661,21 +1676,18 @@ static void source_output_suspend_cb(pa_source_output *o, pa_bool_t suspend) { } /* Called from main context */ -static void source_output_moved_cb(pa_source_output *o) { +static void source_output_moving_cb(pa_source_output *o) { record_stream *s; pa_tagstruct *t; - uint32_t maxlength, fragsize; pa_source_output_assert_ref(o); s = RECORD_STREAM(o->userdata); record_stream_assert_ref(s); - fragsize = (uint32_t) s->fragment_size; - maxlength = (uint32_t) pa_memblockq_get_length(s->memblockq); - - fix_record_buffer_attr_pre(s, TRUE, FALSE, &maxlength, &fragsize); - pa_memblockq_set_maxlength(s->memblockq, maxlength); - fix_record_buffer_attr_post(s, &maxlength, &fragsize); + fix_record_buffer_attr_pre(s); + pa_memblockq_set_maxlength(s->memblockq, s->buffer_attr.maxlength); + pa_memblockq_get_attr(s->memblockq, &s->buffer_attr); + fix_record_buffer_attr_post(s); if (s->connection->version < 12) return; @@ -1689,8 +1701,8 @@ static void source_output_moved_cb(pa_source_output *o) { pa_tagstruct_put_boolean(t, pa_source_get_state(o->source) == PA_SOURCE_SUSPENDED); if (s->connection->version >= 13) { - pa_tagstruct_putu32(t, maxlength); - pa_tagstruct_putu32(t, fragsize); + pa_tagstruct_putu32(t, s->buffer_attr.maxlength); + pa_tagstruct_putu32(t, s->buffer_attr.fragsize); pa_tagstruct_put_usec(t, s->source_latency); } @@ -1723,7 +1735,8 @@ static pa_tagstruct *reply_new(uint32_t tag) { static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { pa_native_connection *c = PA_NATIVE_CONNECTION(userdata); playback_stream *s; - uint32_t maxlength, tlength, prebuf, minreq, sink_index, syncid, missing; + uint32_t sink_index, syncid, missing; + pa_buffer_attr attr; const char *name = NULL, *sink_name; pa_sample_spec ss; pa_channel_map map; @@ -1752,6 +1765,7 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u pa_native_connection_assert_ref(c); pa_assert(t); + memset(&attr, 0, sizeof(attr)); if ((c->version < 13 && (pa_tagstruct_gets(t, &name) < 0 || !name)) || pa_tagstruct_get( @@ -1760,11 +1774,11 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u PA_TAG_CHANNEL_MAP, &map, PA_TAG_U32, &sink_index, PA_TAG_STRING, &sink_name, - PA_TAG_U32, &maxlength, + PA_TAG_U32, &attr.maxlength, PA_TAG_BOOLEAN, &corked, - PA_TAG_U32, &tlength, - PA_TAG_U32, &prebuf, - PA_TAG_U32, &minreq, + PA_TAG_U32, &attr.tlength, + PA_TAG_U32, &attr.prebuf, + PA_TAG_U32, &attr.minreq, PA_TAG_U32, &syncid, PA_TAG_CVOLUME, &volume, PA_TAG_INVALID) < 0) { @@ -1875,7 +1889,7 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u * flag. For older versions we synthesize it here */ muted_set = muted_set || muted; - s = playback_stream_new(c, sink, &ss, &map, &maxlength, &tlength, &prebuf, &minreq, volume_set ? &volume : NULL, muted, muted_set, syncid, &missing, flags, p, adjust_latency, early_requests, &ret); + s = playback_stream_new(c, sink, &ss, &map, &attr, volume_set ? &volume : NULL, muted, muted_set, syncid, &missing, flags, p, adjust_latency, early_requests, &ret); pa_proplist_free(p); CHECK_VALIDITY(c->pstream, s, tag, ret); @@ -1891,10 +1905,10 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u if (c->version >= 9) { /* Since 0.9.0 we support sending the buffer metrics back to the client */ - pa_tagstruct_putu32(reply, (uint32_t) maxlength); - pa_tagstruct_putu32(reply, (uint32_t) tlength); - pa_tagstruct_putu32(reply, (uint32_t) prebuf); - pa_tagstruct_putu32(reply, (uint32_t) minreq); + pa_tagstruct_putu32(reply, (uint32_t) s->buffer_attr.maxlength); + pa_tagstruct_putu32(reply, (uint32_t) s->buffer_attr.tlength); + pa_tagstruct_putu32(reply, (uint32_t) s->buffer_attr.prebuf); + pa_tagstruct_putu32(reply, (uint32_t) s->buffer_attr.minreq); } if (c->version >= 12) { @@ -1978,7 +1992,7 @@ static void command_delete_stream(pa_pdispatch *pd, uint32_t command, uint32_t t static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { pa_native_connection *c = PA_NATIVE_CONNECTION(userdata); record_stream *s; - uint32_t maxlength, fragment_size; + pa_buffer_attr attr; uint32_t source_index; const char *name = NULL, *source_name; pa_sample_spec ss; @@ -2008,14 +2022,16 @@ static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uin pa_native_connection_assert_ref(c); pa_assert(t); + memset(&attr, 0, sizeof(attr)); + if ((c->version < 13 && (pa_tagstruct_gets(t, &name) < 0 || !name)) || pa_tagstruct_get_sample_spec(t, &ss) < 0 || pa_tagstruct_get_channel_map(t, &map) < 0 || pa_tagstruct_getu32(t, &source_index) < 0 || pa_tagstruct_gets(t, &source_name) < 0 || - pa_tagstruct_getu32(t, &maxlength) < 0 || + pa_tagstruct_getu32(t, &attr.maxlength) < 0 || pa_tagstruct_get_boolean(t, &corked) < 0 || - pa_tagstruct_getu32(t, &fragment_size) < 0) { + pa_tagstruct_getu32(t, &attr.fragsize) < 0) { protocol_error(c); return; } @@ -2125,7 +2141,7 @@ static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uin (dont_inhibit_auto_suspend ? PA_SOURCE_OUTPUT_DONT_INHIBIT_AUTO_SUSPEND : 0) | (fail_on_suspend ? PA_SOURCE_OUTPUT_FAIL_ON_SUSPEND : 0); - s = record_stream_new(c, source, &ss, &map, peak_detect, &maxlength, &fragment_size, flags, p, adjust_latency, direct_on_input, early_requests, &ret); + s = record_stream_new(c, source, &ss, &map, peak_detect, &attr, flags, p, adjust_latency, direct_on_input, early_requests, &ret); pa_proplist_free(p); CHECK_VALIDITY(c->pstream, s, tag, ret); @@ -2138,8 +2154,8 @@ static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uin if (c->version >= 9) { /* Since 0.9 we support sending the buffer metrics back to the client */ - pa_tagstruct_putu32(reply, (uint32_t) maxlength); - pa_tagstruct_putu32(reply, (uint32_t) fragment_size); + pa_tagstruct_putu32(reply, (uint32_t) s->buffer_attr.maxlength); + pa_tagstruct_putu32(reply, (uint32_t) s->buffer_attr.fragsize); } if (c->version >= 12) { @@ -3435,12 +3451,14 @@ static void command_flush_record_stream(pa_pdispatch *pd, uint32_t command, uint static void command_set_stream_buffer_attr(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { pa_native_connection *c = PA_NATIVE_CONNECTION(userdata); uint32_t idx; - uint32_t maxlength, tlength, prebuf, minreq, fragsize; + pa_buffer_attr a; pa_tagstruct *reply; pa_native_connection_assert_ref(c); pa_assert(t); + memset(&a, 0, sizeof(a)); + if (pa_tagstruct_getu32(t, &idx) < 0) { protocol_error(c); return; @@ -3458,10 +3476,10 @@ static void command_set_stream_buffer_attr(pa_pdispatch *pd, uint32_t command, u if (pa_tagstruct_get( t, - PA_TAG_U32, &maxlength, - PA_TAG_U32, &tlength, - PA_TAG_U32, &prebuf, - PA_TAG_U32, &minreq, + PA_TAG_U32, &a.maxlength, + PA_TAG_U32, &a.tlength, + PA_TAG_U32, &a.prebuf, + PA_TAG_U32, &a.minreq, PA_TAG_INVALID) < 0 || (c->version >= 13 && pa_tagstruct_get_boolean(t, &adjust_latency) < 0) || (c->version >= 14 && pa_tagstruct_get_boolean(t, &early_requests) < 0) || @@ -3470,18 +3488,18 @@ static void command_set_stream_buffer_attr(pa_pdispatch *pd, uint32_t command, u return; } - fix_playback_buffer_attr_pre(s, adjust_latency, early_requests, &maxlength, &tlength, &prebuf, &minreq); - pa_memblockq_set_maxlength(s->memblockq, maxlength); - pa_memblockq_set_tlength(s->memblockq, tlength); - pa_memblockq_set_prebuf(s->memblockq, prebuf); - pa_memblockq_set_minreq(s->memblockq, minreq); - fix_playback_buffer_attr_post(s, &maxlength, &tlength, &prebuf, &minreq); + s->adjust_latency = adjust_latency; + s->early_requests = early_requests; + s->buffer_attr = a; + + fix_playback_buffer_attr(s); + pa_assert_se(pa_asyncmsgq_send(s->sink_input->sink->asyncmsgq, PA_MSGOBJECT(s->sink_input), SINK_INPUT_MESSAGE_UPDATE_BUFFER_ATTR, NULL, 0, NULL) == 0); reply = reply_new(tag); - pa_tagstruct_putu32(reply, maxlength); - pa_tagstruct_putu32(reply, tlength); - pa_tagstruct_putu32(reply, prebuf); - pa_tagstruct_putu32(reply, minreq); + pa_tagstruct_putu32(reply, s->buffer_attr.maxlength); + pa_tagstruct_putu32(reply, s->buffer_attr.tlength); + pa_tagstruct_putu32(reply, s->buffer_attr.prebuf); + pa_tagstruct_putu32(reply, s->buffer_attr.minreq); if (c->version >= 13) pa_tagstruct_put_usec(reply, s->sink_latency); @@ -3496,8 +3514,8 @@ static void command_set_stream_buffer_attr(pa_pdispatch *pd, uint32_t command, u if (pa_tagstruct_get( t, - PA_TAG_U32, &maxlength, - PA_TAG_U32, &fragsize, + PA_TAG_U32, &a.maxlength, + PA_TAG_U32, &a.fragsize, PA_TAG_INVALID) < 0 || (c->version >= 13 && pa_tagstruct_get_boolean(t, &adjust_latency) < 0) || (c->version >= 14 && pa_tagstruct_get_boolean(t, &early_requests) < 0) || @@ -3506,13 +3524,18 @@ static void command_set_stream_buffer_attr(pa_pdispatch *pd, uint32_t command, u return; } - fix_record_buffer_attr_pre(s, adjust_latency, early_requests, &maxlength, &fragsize); - pa_memblockq_set_maxlength(s->memblockq, maxlength); - fix_record_buffer_attr_post(s, &maxlength, &fragsize); + s->adjust_latency = adjust_latency; + s->early_requests = early_requests; + s->buffer_attr = a; + + fix_record_buffer_attr_pre(s); + pa_memblockq_set_maxlength(s->memblockq, s->buffer_attr.maxlength); + pa_memblockq_get_attr(s->memblockq, &s->buffer_attr); + fix_record_buffer_attr_post(s); reply = reply_new(tag); - pa_tagstruct_putu32(reply, maxlength); - pa_tagstruct_putu32(reply, fragsize); + pa_tagstruct_putu32(reply, s->buffer_attr.maxlength); + pa_tagstruct_putu32(reply, s->buffer_attr.fragsize); if (c->version >= 13) pa_tagstruct_put_usec(reply, s->source_latency); diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c index ae2c6f54..da422428 100644 --- a/src/pulsecore/sink-input.c +++ b/src/pulsecore/sink-input.c @@ -117,7 +117,7 @@ static void reset_callbacks(pa_sink_input *i) { i->attach = NULL; i->detach = NULL; i->suspend = NULL; - i->moved = NULL; + i->moving = NULL; i->kill = NULL; i->get_latency = NULL; i->state_change = NULL; @@ -819,26 +819,12 @@ void pa_sink_input_update_max_request(pa_sink_input *i, size_t nbytes /* in the } /* Called from thread context */ -static pa_usec_t fixup_latency(pa_sink *s, pa_usec_t usec) { - pa_sink_assert_ref(s); - - if (usec == (pa_usec_t) -1) - return usec; - - if (s->thread_info.max_latency > 0 && usec > s->thread_info.max_latency) - usec = s->thread_info.max_latency; - - if (s->thread_info.min_latency > 0 && usec < s->thread_info.min_latency) - usec = s->thread_info.min_latency; - - return usec; -} - -/* Called from thread context */ pa_usec_t pa_sink_input_set_requested_latency_within_thread(pa_sink_input *i, pa_usec_t usec) { pa_sink_input_assert_ref(i); - usec = fixup_latency(i->sink, usec); + if (usec != (pa_usec_t) -1) + usec = PA_CLAMP(usec, i->sink->thread_info.min_latency, i->sink->thread_info.max_latency); + i->thread_info.requested_sink_latency = usec; pa_sink_invalidate_requested_latency(i->sink); @@ -847,33 +833,42 @@ pa_usec_t pa_sink_input_set_requested_latency_within_thread(pa_sink_input *i, pa /* Called from main context */ pa_usec_t pa_sink_input_set_requested_latency(pa_sink_input *i, pa_usec_t usec) { + pa_usec_t min_latency, max_latency; + pa_sink_input_assert_ref(i); - if (PA_SINK_INPUT_IS_LINKED(i->state)) + if (PA_SINK_INPUT_IS_LINKED(i->state) && i->sink) { pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_REQUESTED_LATENCY, &usec, 0, NULL) == 0); - else - /* If this sink input is not realized yet, we have to touch - * the thread info data directly */ + return usec; + } - i->thread_info.requested_sink_latency = usec; + /* If this sink input is not realized yet or we are being moved, + * we have to touch the thread info data directly */ + + pa_sink_get_latency_range(i->sink, &min_latency, &max_latency); + + if (usec != (pa_usec_t) -1) + usec = PA_CLAMP(usec, min_latency, max_latency); + + i->thread_info.requested_sink_latency = usec; return usec; } /* Called from main context */ pa_usec_t pa_sink_input_get_requested_latency(pa_sink_input *i) { - pa_usec_t usec = 0; - pa_sink_input_assert_ref(i); - if (PA_SINK_INPUT_IS_LINKED(i->state)) + if (PA_SINK_INPUT_IS_LINKED(i->state) && i->sink) { + pa_usec_t usec = 0; pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_GET_REQUESTED_LATENCY, &usec, 0, NULL) == 0); - else - /* If this sink input is not realized yet, we have to touch - * the thread info data directly */ - usec = i->thread_info.requested_sink_latency; + return usec; + } - return usec; + /* If this sink input is not realized yet or we are being moved, + * we have to touch the thread info data directly */ + + return i->thread_info.requested_sink_latency; } /* Called from main context */ @@ -1169,6 +1164,9 @@ int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest, pa_bool_t save) { } else new_resampler = NULL; + if (i->moving) + i->moving(i); + i->sink = dest; i->save_sink = save; pa_idxset_put(dest->inputs, i, NULL); @@ -1195,7 +1193,6 @@ int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest, pa_bool_t save) { 0, &i->sink->silence); } - pa_sink_update_status(dest); if (i->sink->flags & PA_SINK_FLAT_VOLUME) { @@ -1216,9 +1213,6 @@ int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest, pa_bool_t save) { pa_log_debug("Successfully moved sink input %i to %s.", i->index, dest->name); /* Notify everyone */ - if (i->moved) - i->moved(i); - pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_FINISH], i); pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index); diff --git a/src/pulsecore/sink-input.h b/src/pulsecore/sink-input.h index 0bcb9d56..4e29be67 100644 --- a/src/pulsecore/sink-input.h +++ b/src/pulsecore/sink-input.h @@ -150,7 +150,7 @@ struct pa_sink_input { /* If non-NULL called whenever the the sink this input is attached * to changes. Called from main context */ - void (*moved) (pa_sink_input *i); /* may be NULL */ + void (*moving) (pa_sink_input *i); /* may be NULL */ /* Supposed to unlink and destroy this stream. Called from main * context. */ diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c index 1fe8f592..73ad247d 100644 --- a/src/pulsecore/sink.c +++ b/src/pulsecore/sink.c @@ -1798,7 +1798,7 @@ void pa_sink_set_max_rewind_within_thread(pa_sink *s, size_t max_rewind) { } if (s->monitor_source) - pa_source_set_max_rewind(s->monitor_source, s->thread_info.max_rewind); + pa_source_set_max_rewind_within_thread(s->monitor_source, s->thread_info.max_rewind); } /* Called from main thread */ diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c index 373d5637..550b6571 100644 --- a/src/pulsecore/source-output.c +++ b/src/pulsecore/source-output.c @@ -87,7 +87,7 @@ static void reset_callbacks(pa_source_output *o) { o->attach = NULL; o->detach = NULL; o->suspend = NULL; - o->moved = NULL; + o->moving = NULL; o->kill = NULL; o->get_latency = NULL; o->state_change = NULL; @@ -516,26 +516,12 @@ void pa_source_output_update_max_rewind(pa_source_output *o, size_t nbytes /* i } /* Called from thread context */ -static pa_usec_t fixup_latency(pa_source *s, pa_usec_t usec) { - pa_source_assert_ref(s); - - if (usec == (pa_usec_t) -1) - return usec; - - if (s->thread_info.max_latency > 0 && usec > s->thread_info.max_latency) - usec = s->thread_info.max_latency; - - if (s->thread_info.min_latency > 0 && usec < s->thread_info.min_latency) - usec = s->thread_info.min_latency; - - return usec; -} - -/* Called from thread context */ pa_usec_t pa_source_output_set_requested_latency_within_thread(pa_source_output *o, pa_usec_t usec) { pa_source_output_assert_ref(o); - usec = fixup_latency(o->source, usec); + if (usec != (pa_usec_t) -1) + usec = PA_CLAMP(usec, o->source->thread_info.min_latency, o->source->thread_info.max_latency); + o->thread_info.requested_source_latency = usec; pa_source_invalidate_requested_latency(o->source); @@ -544,33 +530,42 @@ pa_usec_t pa_source_output_set_requested_latency_within_thread(pa_source_output /* Called from main context */ pa_usec_t pa_source_output_set_requested_latency(pa_source_output *o, pa_usec_t usec) { + pa_usec_t min_latency, max_latency; + pa_source_output_assert_ref(o); - if (PA_SOURCE_OUTPUT_IS_LINKED(o->state)) + if (PA_SOURCE_OUTPUT_IS_LINKED(o->state) && o->source) { pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_SET_REQUESTED_LATENCY, &usec, 0, NULL) == 0); - else - /* If this source output is not realized yet, we have to touch - * the thread info data directly */ + return usec; + } - o->thread_info.requested_source_latency = usec; + /* If this source output is not realized yet or is being moved, we + * have to touch the thread info data directly */ + + pa_source_get_latency_range(o->source, &min_latency, &max_latency); + + if (usec != (pa_usec_t) -1) + usec = PA_CLAMP(usec, min_latency, max_latency); + + o->thread_info.requested_source_latency = usec; return usec; } /* Called from main context */ pa_usec_t pa_source_output_get_requested_latency(pa_source_output *o) { - pa_usec_t usec = 0; - pa_source_output_assert_ref(o); - if (PA_SOURCE_OUTPUT_IS_LINKED(o->state)) + if (PA_SOURCE_OUTPUT_IS_LINKED(o->state) && o->source) { + pa_usec_t usec = 0; pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_GET_REQUESTED_LATENCY, &usec, 0, NULL) == 0); - else - /* If this source output is not realized yet, we have to touch - * the thread info data directly */ - usec = o->thread_info.requested_source_latency; + return usec; + } - return usec; + /* If this source output is not realized yet or is being moved, we + * have to touch the thread info data directly */ + + return o->thread_info.requested_source_latency; } /* Called from main context */ @@ -749,6 +744,9 @@ int pa_source_output_finish_move(pa_source_output *o, pa_source *dest, pa_bool_t } else new_resampler = NULL; + if (o->moving) + o->moving(o); + o->source = dest; o->save_source = save; pa_idxset_put(o->source->outputs, o, NULL); @@ -776,14 +774,12 @@ int pa_source_output_finish_move(pa_source_output *o, pa_source *dest, pa_bool_t } pa_source_update_status(dest); + pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_ADD_OUTPUT, o, 0, NULL) == 0); pa_log_debug("Successfully moved source output %i to %s.", o->index, dest->name); /* Notify everyone */ - if (o->moved) - o->moved(o); - pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_FINISH], o); pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index); diff --git a/src/pulsecore/source-output.h b/src/pulsecore/source-output.h index 018ec886..8d57ded4 100644 --- a/src/pulsecore/source-output.h +++ b/src/pulsecore/source-output.h @@ -122,7 +122,7 @@ struct pa_source_output { /* If non-NULL called whenever the the source this output is attached * to changes. Called from main context */ - void (*moved) (pa_source_output *o); /* may be NULL */ + void (*moving) (pa_source_output *o); /* may be NULL */ /* Supposed to unlink and destroy this stream. Called from main * context. */ |