diff options
Diffstat (limited to 'src/pulse')
-rw-r--r-- | src/pulse/context.c | 68 | ||||
-rw-r--r-- | src/pulse/context.h | 8 | ||||
-rw-r--r-- | src/pulse/internal.h | 8 | ||||
-rw-r--r-- | src/pulse/introspect.c | 132 | ||||
-rw-r--r-- | src/pulse/mainloop-api.h | 1 | ||||
-rw-r--r-- | src/pulse/mainloop.c | 88 | ||||
-rw-r--r-- | src/pulse/rtclock.c | 35 | ||||
-rw-r--r-- | src/pulse/rtclock.h | 41 | ||||
-rw-r--r-- | src/pulse/stream.c | 169 | ||||
-rw-r--r-- | src/pulse/stream.h | 70 | ||||
-rw-r--r-- | src/pulse/thread-mainloop.c | 4 | ||||
-rw-r--r-- | src/pulse/timeval.h | 9 |
12 files changed, 470 insertions, 163 deletions
diff --git a/src/pulse/context.c b/src/pulse/context.c index 3b7bf08d..4ded5565 100644 --- a/src/pulse/context.c +++ b/src/pulse/context.c @@ -54,6 +54,8 @@ #include <pulse/utf8.h> #include <pulse/util.h> #include <pulse/i18n.h> +#include <pulse/mainloop.h> +#include <pulse/timeval.h> #include <pulsecore/winsock.h> #include <pulsecore/core-error.h> @@ -64,6 +66,7 @@ #include <pulsecore/dynarray.h> #include <pulsecore/socket-client.h> #include <pulsecore/pstream-util.h> +#include <pulsecore/core-rtclock.h> #include <pulsecore/core-util.h> #include <pulsecore/log.h> #include <pulsecore/socket-util.h> @@ -157,6 +160,7 @@ pa_context *pa_context_new_with_proplist(pa_mainloop_api *mainloop, const char * c->playback_streams = pa_dynarray_new(); c->record_streams = pa_dynarray_new(); c->client_index = PA_INVALID_INDEX; + c->use_rtclock = pa_mainloop_is_our_api(mainloop); PA_LLIST_HEAD_INIT(pa_stream, c->streams); PA_LLIST_HEAD_INIT(pa_operation, c->operations); @@ -540,7 +544,7 @@ static void setup_context(pa_context *c, pa_iochannel *io) { pa_pstream_set_recieve_memblock_callback(c->pstream, pstream_memblock_callback, c); pa_assert(!c->pdispatch); - c->pdispatch = pa_pdispatch_new(c->mainloop, command_table, PA_COMMAND_MAX); + c->pdispatch = pa_pdispatch_new(c->mainloop, c->use_rtclock, command_table, PA_COMMAND_MAX); if (!c->conf->cookie_valid) pa_log_info(_("No cookie loaded. Attempting to connect without.")); @@ -757,22 +761,33 @@ static void track_pulseaudio_on_dbus(pa_context *c, DBusBusType type, pa_dbus_wr pa_assert(conn); dbus_error_init(&error); - if (!(*conn = pa_dbus_wrap_connection_new(c->mainloop, type, &error)) || dbus_error_is_set(&error)) { + + if (!(*conn = pa_dbus_wrap_connection_new(c->mainloop, c->use_rtclock, type, &error)) || dbus_error_is_set(&error)) { pa_log_warn("Unable to contact DBUS: %s: %s", error.name, error.message); - goto finish; + goto fail; } if (!dbus_connection_add_filter(pa_dbus_wrap_connection_get(*conn), filter_cb, c, NULL)) { pa_log_warn("Failed to add filter function"); - goto finish; + goto fail; } if (pa_dbus_add_matches( pa_dbus_wrap_connection_get(*conn), &error, - "type='signal',sender='" DBUS_SERVICE_DBUS "',interface='" DBUS_INTERFACE_DBUS "',member='NameOwnerChanged',arg0='org.pulseaudio.Server',arg1=''", NULL) < 0) + "type='signal',sender='" DBUS_SERVICE_DBUS "',interface='" DBUS_INTERFACE_DBUS "',member='NameOwnerChanged',arg0='org.pulseaudio.Server',arg1=''", NULL) < 0) { + pa_log_warn("Unable to track org.pulseaudio.Server: %s: %s", error.name, error.message); + goto fail; + } + + return; + +fail: + if (*conn) { + pa_dbus_wrap_connection_free(*conn); + *conn = NULL; + } - finish: dbus_error_free(&error); } #endif @@ -827,7 +842,7 @@ static int try_next_connection(pa_context *c) { pa_xfree(c->server); c->server = pa_xstrdup(u); - if (!(c->client = pa_socket_client_new_string(c->mainloop, u, PA_NATIVE_DEFAULT_PORT))) + if (!(c->client = pa_socket_client_new_string(c->mainloop, c->use_rtclock, u, PA_NATIVE_DEFAULT_PORT))) continue; c->is_local = !!pa_socket_client_is_local(c->client); @@ -857,7 +872,7 @@ static void on_connection(pa_socket_client *client, pa_iochannel*io, void *userd c->client = NULL; if (!io) { - /* Try the item in the list */ + /* Try the next item in the list */ if (saved_errno == ECONNREFUSED || saved_errno == ETIMEDOUT || saved_errno == EHOSTUNREACH) { @@ -893,7 +908,7 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *message, vo /* FIXME: We probably should check if this is actually the NameOwnerChanged we were looking for */ is_session = c->session_bus && bus == pa_dbus_wrap_connection_get(c->session_bus); - pa_log_debug("Rock!! PulseAudio is back on %s bus", is_session ? "session" : "system"); + pa_log_debug("Rock!! PulseAudio might be back on %s bus", is_session ? "session" : "system"); if (is_session) /* The user instance via PF_LOCAL */ @@ -933,7 +948,7 @@ int pa_context_connect( pa_context_ref(c); - c->no_fail = flags & PA_CONTEXT_NOFAIL; + c->no_fail = !!(flags & PA_CONTEXT_NOFAIL); c->server_specified = !!server; pa_assert(!c->server_list); @@ -950,10 +965,7 @@ int pa_context_connect( /* Follow the X display */ if ((d = getenv("DISPLAY"))) { - char *e; - d = pa_xstrdup(d); - if ((e = strchr(d, ':'))) - *e = 0; + d = pa_xstrndup(d, strcspn(d, ":")); if (*d) c->server_list = pa_strlist_prepend(c->server_list, d); @@ -1443,3 +1455,31 @@ finish: if (pl) pa_proplist_free(pl); } + +pa_time_event* pa_context_rttime_new(pa_context *c, pa_usec_t usec, pa_time_event_cb_t cb, void *userdata) { + struct timeval tv; + + pa_assert(c); + pa_assert(c->mainloop); + + if (usec == PA_USEC_INVALID) + return c->mainloop->time_new(c->mainloop, NULL, cb, userdata); + + pa_timeval_rtstore(&tv, usec, c->use_rtclock); + + return c->mainloop->time_new(c->mainloop, &tv, cb, userdata); +} + +void pa_context_rttime_restart(pa_context *c, pa_time_event *e, pa_usec_t usec) { + struct timeval tv; + + pa_assert(c); + pa_assert(c->mainloop); + + if (usec == PA_USEC_INVALID) + c->mainloop->time_restart(e, NULL); + else { + pa_timeval_rtstore(&tv, usec, c->use_rtclock); + c->mainloop->time_restart(e, &tv); + } +} diff --git a/src/pulse/context.h b/src/pulse/context.h index 139d0e0b..cd129313 100644 --- a/src/pulse/context.h +++ b/src/pulse/context.h @@ -260,6 +260,14 @@ pa_operation *pa_context_proplist_remove(pa_context *c, const char *const keys[] * introspection functions, such as pa_context_get_client_info(). \since 0.9.11 */ uint32_t pa_context_get_index(pa_context *s); +/** Create a new timer event source for the specified time (wrapper + for mainloop->time_new). \since 0.9.16 */ +pa_time_event* pa_context_rttime_new(pa_context *c, pa_usec_t usec, pa_time_event_cb_t cb, void *userdata); +/** Restart a running or expired timer event source (wrapper + for mainloop->time_restart). \since 0.9.16 */ +void pa_context_rttime_restart(pa_context *c, pa_time_event *e, pa_usec_t usec); + + PA_C_DECL_END #endif diff --git a/src/pulse/internal.h b/src/pulse/internal.h index 28a989b3..e069c9e9 100644 --- a/src/pulse/internal.h +++ b/src/pulse/internal.h @@ -89,6 +89,7 @@ struct pa_context { pa_bool_t server_specified:1; pa_bool_t no_fail:1; pa_bool_t do_autospawn:1; + pa_bool_t use_rtclock:1; pa_spawn_api spawn_api; pa_strlist *server_list; @@ -150,6 +151,11 @@ struct pa_stream { uint32_t device_index; char *device_name; + /* playback */ + pa_memblock *write_memblock; + void *write_data; + + /* recording */ pa_memchunk peek_memchunk; void *peek_data; pa_memblockq *record_memblockq; @@ -279,4 +285,6 @@ pa_tagstruct *pa_tagstruct_command(pa_context *c, uint32_t command, uint32_t *ta void pa_ext_stream_restore_command(pa_context *c, uint32_t tag, pa_tagstruct *t); +pa_bool_t pa_mainloop_is_our_api(pa_mainloop_api*m); + #endif diff --git a/src/pulse/introspect.c b/src/pulse/introspect.c index ab67f596..27a587cb 100644 --- a/src/pulse/introspect.c +++ b/src/pulse/introspect.c @@ -201,42 +201,44 @@ static void context_get_sink_info_callback(pa_pdispatch *pd, uint32_t command, u goto finish; } - if (i.n_ports > 0) { - i.ports = pa_xnew(pa_sink_port_info*, i.n_ports+1); - i.ports[0] = pa_xnew(pa_sink_port_info, i.n_ports); - - for (j = 0; j < i.n_ports; j++) { - if (pa_tagstruct_gets(t, &i.ports[0][j].name) < 0 || - pa_tagstruct_gets(t, &i.ports[0][j].description) < 0 || - pa_tagstruct_getu32(t, &i.ports[0][j].priority) < 0) { - - pa_context_fail(o->context, PA_ERR_PROTOCOL); - pa_xfree(i.ports); - pa_xfree(i.ports[0]); - pa_proplist_free(i.proplist); - goto finish; + if (o->context->version >= 16) { + if (i.n_ports > 0) { + i.ports = pa_xnew(pa_sink_port_info*, i.n_ports+1); + i.ports[0] = pa_xnew(pa_sink_port_info, i.n_ports); + + for (j = 0; j < i.n_ports; j++) { + if (pa_tagstruct_gets(t, &i.ports[0][j].name) < 0 || + pa_tagstruct_gets(t, &i.ports[0][j].description) < 0 || + pa_tagstruct_getu32(t, &i.ports[0][j].priority) < 0) { + + pa_context_fail(o->context, PA_ERR_PROTOCOL); + pa_xfree(i.ports[0]); + pa_xfree(i.ports); + pa_proplist_free(i.proplist); + goto finish; + } + + i.ports[j] = &i.ports[0][j]; } - i.ports[j] = &i.ports[0][j]; + i.ports[j] = NULL; } - i.ports[j] = NULL; - } - - if (pa_tagstruct_gets(t, &ap) < 0) { - pa_context_fail(o->context, PA_ERR_PROTOCOL); - pa_xfree(i.ports[0]); - pa_xfree(i.ports); - pa_proplist_free(i.proplist); - goto finish; - } + if (pa_tagstruct_gets(t, &ap) < 0) { + pa_context_fail(o->context, PA_ERR_PROTOCOL); + pa_xfree(i.ports[0]); + pa_xfree(i.ports); + pa_proplist_free(i.proplist); + goto finish; + } - if (ap) { - for (j = 0; j < i.n_ports; j++) - if (pa_streq(i.ports[j]->name, ap)) { - i.active_port = i.ports[j]; - break; - } + if (ap) { + for (j = 0; j < i.n_ports; j++) + if (pa_streq(i.ports[j]->name, ap)) { + i.active_port = i.ports[j]; + break; + } + } } i.mute = (int) mute; @@ -248,6 +250,10 @@ static void context_get_sink_info_callback(pa_pdispatch *pd, uint32_t command, u cb(o->context, &i, 0, o->userdata); } + if (i.ports) { + pa_xfree(i.ports[0]); + pa_xfree(i.ports); + } pa_proplist_free(i.proplist); } } @@ -428,42 +434,44 @@ static void context_get_source_info_callback(pa_pdispatch *pd, uint32_t command, goto finish; } - if (i.n_ports > 0) { - i.ports = pa_xnew(pa_source_port_info*, i.n_ports+1); - i.ports[0] = pa_xnew(pa_source_port_info, i.n_ports); + if (o->context->version >= 16) { + if (i.n_ports > 0) { + i.ports = pa_xnew(pa_source_port_info*, i.n_ports+1); + i.ports[0] = pa_xnew(pa_source_port_info, i.n_ports); - for (j = 0; j < i.n_ports; j++) { - if (pa_tagstruct_gets(t, &i.ports[0][j].name) < 0 || - pa_tagstruct_gets(t, &i.ports[0][j].description) < 0 || - pa_tagstruct_getu32(t, &i.ports[0][j].priority) < 0) { + for (j = 0; j < i.n_ports; j++) { + if (pa_tagstruct_gets(t, &i.ports[0][j].name) < 0 || + pa_tagstruct_gets(t, &i.ports[0][j].description) < 0 || + pa_tagstruct_getu32(t, &i.ports[0][j].priority) < 0) { - pa_context_fail(o->context, PA_ERR_PROTOCOL); - pa_xfree(i.ports[0]); - pa_xfree(i.ports); - pa_proplist_free(i.proplist); - goto finish; + pa_context_fail(o->context, PA_ERR_PROTOCOL); + pa_xfree(i.ports[0]); + pa_xfree(i.ports); + pa_proplist_free(i.proplist); + goto finish; + } + + i.ports[j] = &i.ports[0][j]; } - i.ports[j] = &i.ports[0][j]; + i.ports[j] = NULL; } - i.ports[j] = NULL; - } - - if (pa_tagstruct_gets(t, &ap) < 0) { - pa_context_fail(o->context, PA_ERR_PROTOCOL); - pa_xfree(i.ports[0]); - pa_xfree(i.ports); - pa_proplist_free(i.proplist); - goto finish; - } + if (pa_tagstruct_gets(t, &ap) < 0) { + pa_context_fail(o->context, PA_ERR_PROTOCOL); + pa_xfree(i.ports[0]); + pa_xfree(i.ports); + pa_proplist_free(i.proplist); + goto finish; + } - if (ap) { - for (j = 0; j < i.n_ports; j++) - if (pa_streq(i.ports[j]->name, ap)) { - i.active_port = i.ports[j]; - break; - } + if (ap) { + for (j = 0; j < i.n_ports; j++) + if (pa_streq(i.ports[j]->name, ap)) { + i.active_port = i.ports[j]; + break; + } + } } i.mute = (int) mute; @@ -475,6 +483,10 @@ static void context_get_source_info_callback(pa_pdispatch *pd, uint32_t command, cb(o->context, &i, 0, o->userdata); } + if (i.ports) { + pa_xfree(i.ports[0]); + pa_xfree(i.ports); + } pa_proplist_free(i.proplist); } } diff --git a/src/pulse/mainloop-api.h b/src/pulse/mainloop-api.h index e353ed96..aa0d5e73 100644 --- a/src/pulse/mainloop-api.h +++ b/src/pulse/mainloop-api.h @@ -27,6 +27,7 @@ #include <time.h> #include <pulse/cdecl.h> +#include <pulse/sample.h> #include <pulse/version.h> /** \file diff --git a/src/pulse/mainloop.c b/src/pulse/mainloop.c index 225fd098..c418d108 100644 --- a/src/pulse/mainloop.c +++ b/src/pulse/mainloop.c @@ -42,10 +42,12 @@ #include <pulsecore/pipe.h> #endif +#include <pulse/i18n.h> +#include <pulse/rtclock.h> #include <pulse/timeval.h> #include <pulse/xmalloc.h> -#include <pulse/i18n.h> +#include <pulsecore/core-rtclock.h> #include <pulsecore/core-util.h> #include <pulsecore/llist.h> #include <pulsecore/log.h> @@ -54,6 +56,7 @@ #include <pulsecore/macro.h> #include "mainloop.h" +#include "internal.h" struct pa_io_event { pa_mainloop *mainloop; @@ -75,7 +78,7 @@ struct pa_time_event { pa_bool_t dead:1; pa_bool_t enabled:1; - struct timeval timeval; + pa_usec_t time; pa_time_event_cb_t callback; void *userdata; @@ -317,6 +320,23 @@ static void mainloop_defer_set_destroy(pa_defer_event *e, pa_defer_event_destroy } /* Time events */ +static pa_usec_t timeval_load(const struct timeval *tv) { + pa_bool_t is_rtclock; + struct timeval ttv; + + if (!tv) + return PA_USEC_INVALID; + + ttv = *tv; + is_rtclock = !!(ttv.tv_usec & PA_TIMEVAL_RTCLOCK); + ttv.tv_usec &= ~PA_TIMEVAL_RTCLOCK; + + if (!is_rtclock) + pa_rtclock_from_wallclock(&ttv); + + return pa_timeval_load(&ttv); +} + static pa_time_event* mainloop_time_new( pa_mainloop_api*a, const struct timeval *tv, @@ -325,11 +345,14 @@ static pa_time_event* mainloop_time_new( pa_mainloop *m; pa_time_event *e; + pa_usec_t t; pa_assert(a); pa_assert(a->userdata); pa_assert(callback); + t = timeval_load(tv); + m = a->userdata; pa_assert(a == &m->api); @@ -337,15 +360,15 @@ static pa_time_event* mainloop_time_new( e->mainloop = m; e->dead = FALSE; - if ((e->enabled = !!tv)) { - e->timeval = *tv; + if ((e->enabled = (t != PA_USEC_INVALID))) { + e->time = t; m->n_enabled_time_events++; if (m->cached_next_time_event) { pa_assert(m->cached_next_time_event->enabled); - if (pa_timeval_cmp(tv, &m->cached_next_time_event->timeval) < 0) + if (t < m->cached_next_time_event->time) m->cached_next_time_event = e; } } @@ -363,24 +386,30 @@ static pa_time_event* mainloop_time_new( } static void mainloop_time_restart(pa_time_event *e, const struct timeval *tv) { + pa_bool_t valid; + pa_usec_t t; + pa_assert(e); pa_assert(!e->dead); - if (e->enabled && !tv) { + t = timeval_load(tv); + + valid = (t != PA_USEC_INVALID); + if (e->enabled && !valid) { pa_assert(e->mainloop->n_enabled_time_events > 0); e->mainloop->n_enabled_time_events--; - } else if (!e->enabled && tv) + } else if (!e->enabled && valid) e->mainloop->n_enabled_time_events++; - if ((e->enabled = !!tv)) { - e->timeval = *tv; + if ((e->enabled = valid)) { + e->time = t; pa_mainloop_wakeup(e->mainloop); } if (e->mainloop->cached_next_time_event && e->enabled) { pa_assert(e->mainloop->cached_next_time_event->enabled); - if (pa_timeval_cmp(tv, &e->mainloop->cached_next_time_event->timeval) < 0) + if (t < e->mainloop->cached_next_time_event->time) e->mainloop->cached_next_time_event = e; } else if (e->mainloop->cached_next_time_event == e) e->mainloop->cached_next_time_event = NULL; @@ -428,10 +457,10 @@ static void mainloop_quit(pa_mainloop_api*a, int retval) { static const pa_mainloop_api vtable = { .userdata = NULL, - .io_new= mainloop_io_new, - .io_enable= mainloop_io_enable, - .io_free= mainloop_io_free, - .io_set_destroy= mainloop_io_set_destroy, + .io_new = mainloop_io_new, + .io_enable = mainloop_io_enable, + .io_free = mainloop_io_free, + .io_set_destroy = mainloop_io_set_destroy, .time_new = mainloop_time_new, .time_restart = mainloop_time_restart, @@ -721,11 +750,11 @@ static pa_time_event* find_next_time_event(pa_mainloop *m) { if (t->dead || !t->enabled) continue; - if (!n || pa_timeval_cmp(&t->timeval, &n->timeval) < 0) { + if (!n || t->time < n->time) { n = t; - /* Shortcut for tv = { 0, 0 } */ - if (n->timeval.tv_sec <= 0) + /* Shortcut for time == 0 */ + if (n->time == 0) break; } } @@ -736,7 +765,6 @@ static pa_time_event* find_next_time_event(pa_mainloop *m) { static int calc_next_timeout(pa_mainloop *m) { pa_time_event *t; - struct timeval now; pa_usec_t usec; if (!m->n_enabled_time_events) @@ -745,41 +773,41 @@ static int calc_next_timeout(pa_mainloop *m) { t = find_next_time_event(m); pa_assert(t); - if (t->timeval.tv_sec <= 0) + if (t->time == 0) return 0; - pa_gettimeofday(&now); + usec = t->time - pa_rtclock_now(); - if (pa_timeval_cmp(&t->timeval, &now) <= 0) + if (usec <= 0) return 0; - usec = pa_timeval_diff(&t->timeval, &now); - return (int) (usec / 1000); + return (int) (usec / 1000); /* in milliseconds */ } static int dispatch_timeout(pa_mainloop *m) { pa_time_event *e; - struct timeval now; + pa_usec_t now; int r = 0; pa_assert(m); if (m->n_enabled_time_events <= 0) return 0; - pa_gettimeofday(&now); + now = pa_rtclock_now(); for (e = m->time_events; e && !m->quit; e = e->next) { if (e->dead || !e->enabled) continue; - if (pa_timeval_cmp(&e->timeval, &now) <= 0) { + if (e->time <= now) { + struct timeval tv; pa_assert(e->callback); /* Disable time event */ mainloop_time_restart(e, NULL); - e->callback(&m->api, e, &e->timeval, e->userdata); + e->callback(&m->api, e, pa_timeval_rtstore(&tv, e->time, TRUE), e->userdata); r++; } @@ -967,3 +995,9 @@ void pa_mainloop_set_poll_func(pa_mainloop *m, pa_poll_func poll_func, void *use m->poll_func = poll_func; m->poll_func_userdata = userdata; } + +pa_bool_t pa_mainloop_is_our_api(pa_mainloop_api*m) { + pa_assert(m); + + return m->io_new == mainloop_io_new; +} diff --git a/src/pulse/rtclock.c b/src/pulse/rtclock.c new file mode 100644 index 00000000..49ff6aae --- /dev/null +++ b/src/pulse/rtclock.c @@ -0,0 +1,35 @@ +/*** + This file is part of PulseAudio. + + Copyright 2004-2006 Lennart Poettering + + 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 + Lesser 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 <pulsecore/core-rtclock.h> + +#include "rtclock.h" +#include "timeval.h" + +pa_usec_t pa_rtclock_now(void) { + struct timeval tv; + + return pa_timeval_load(pa_rtclock_get(&tv)); +} diff --git a/src/pulse/rtclock.h b/src/pulse/rtclock.h new file mode 100644 index 00000000..6459d92d --- /dev/null +++ b/src/pulse/rtclock.h @@ -0,0 +1,41 @@ +#ifndef foortclockfoo +#define foortclockfoo + +/*** + This file is part of PulseAudio. + + Copyright 2004-2009 Lennart Poettering + + 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 + Lesser 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 <pulse/cdecl.h> +#include <pulse/def.h> +#include <pulse/gccmacro.h> + +/** \file + * Monotonic clock utilities. */ + +PA_C_DECL_BEGIN + +/** Return the current monotonic system time in usec, if such a clock + * is available. If it is not available this will return the + * wallclock time instead. \since 0.9.16 */ +pa_usec_t pa_rtclock_now(void); + +PA_C_DECL_END + +#endif diff --git a/src/pulse/stream.c b/src/pulse/stream.c index 339a89e5..5baf5c2c 100644 --- a/src/pulse/stream.c +++ b/src/pulse/stream.c @@ -30,13 +30,14 @@ #include <pulse/def.h> #include <pulse/timeval.h> +#include <pulse/rtclock.h> #include <pulse/xmalloc.h> #include <pulsecore/pstream-util.h> #include <pulsecore/log.h> #include <pulsecore/hashmap.h> #include <pulsecore/macro.h> -#include <pulsecore/rtclock.h> +#include <pulsecore/core-rtclock.h> #include "fork-detect.h" #include "internal.h" @@ -143,12 +144,13 @@ pa_stream *pa_stream_new_with_proplist( s->suspended = FALSE; s->corked = FALSE; + s->write_memblock = NULL; + s->write_data = NULL; + pa_memchunk_reset(&s->peek_memchunk); s->peek_data = NULL; - s->record_memblockq = NULL; - memset(&s->timing_info, 0, sizeof(s->timing_info)); s->timing_info_valid = FALSE; @@ -220,6 +222,11 @@ static void stream_free(pa_stream *s) { stream_unlink(s); + if (s->write_memblock) { + pa_memblock_release(s->write_memblock); + pa_memblock_unref(s->write_data); + } + if (s->peek_memchunk.memblock) { if (s->peek_data) pa_memblock_release(s->peek_memchunk.memblock); @@ -319,14 +326,10 @@ static void request_auto_timing_update(pa_stream *s, pa_bool_t force) { } if (s->auto_timing_update_event) { - struct timeval next; - if (force) s->auto_timing_interval_usec = AUTO_TIMING_INTERVAL_START_USEC; - pa_gettimeofday(&next); - pa_timeval_add(&next, s->auto_timing_interval_usec); - s->mainloop->time_restart(s->auto_timing_update_event, &next); + pa_context_rttime_restart(s->context, s->auto_timing_update_event, pa_rtclock_now() + s->auto_timing_interval_usec); s->auto_timing_interval_usec = PA_MIN(AUTO_TIMING_INTERVAL_END_USEC, s->auto_timing_interval_usec*2); } @@ -373,7 +376,7 @@ static void check_smoother_status(pa_stream *s, pa_bool_t aposteriori, pa_bool_t if (!s->smoother) return; - x = pa_rtclock_usec(); + x = pa_rtclock_now(); if (s->timing_info_valid) { if (aposteriori) @@ -800,7 +803,7 @@ static void invalidate_indexes(pa_stream *s, pa_bool_t r, pa_bool_t w) { request_auto_timing_update(s, TRUE); } -static void auto_timing_update_callback(pa_mainloop_api *m, pa_time_event *e, const struct timeval *tv, void *userdata) { +static void auto_timing_update_callback(pa_mainloop_api *m, pa_time_event *e, const struct timeval *t, void *userdata) { pa_stream *s = userdata; pa_assert(s); @@ -822,12 +825,9 @@ static void create_stream_complete(pa_stream *s) { s->write_callback(s, (size_t) s->requested_bytes, s->write_userdata); if (s->flags & PA_STREAM_AUTO_TIMING_UPDATE) { - struct timeval tv; - pa_gettimeofday(&tv); s->auto_timing_interval_usec = AUTO_TIMING_INTERVAL_START_USEC; - pa_timeval_add(&tv, s->auto_timing_interval_usec); pa_assert(!s->auto_timing_update_event); - s->auto_timing_update_event = s->mainloop->time_new(s->mainloop, &tv, &auto_timing_update_callback, s); + s->auto_timing_update_event = pa_context_rttime_new(s->context, pa_rtclock_now() + s->auto_timing_interval_usec, &auto_timing_update_callback, s); request_auto_timing_update(s, TRUE); } @@ -1057,7 +1057,7 @@ static int create_stream( if (flags & PA_STREAM_INTERPOLATE_TIMING) { pa_usec_t x; - x = pa_rtclock_usec(); + x = pa_rtclock_now(); pa_assert(!s->smoother); s->smoother = pa_smoother_new( @@ -1193,20 +1193,60 @@ int pa_stream_connect_record( return create_stream(PA_STREAM_RECORD, s, dev, attr, flags, NULL, NULL); } +int pa_stream_begin_write( + pa_stream *s, + void **data, + size_t *nbytes) { + + pa_assert(s); + pa_assert(PA_REFCNT_VALUE(s) >= 1); + + PA_CHECK_VALIDITY(s->context, !pa_detect_fork(), PA_ERR_FORKED); + PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY(s->context, s->direction == PA_STREAM_PLAYBACK || s->direction == PA_STREAM_UPLOAD, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY(s->context, data, PA_ERR_INVALID); + PA_CHECK_VALIDITY(s->context, nbytes && *nbytes != 0, PA_ERR_INVALID); + + if (!s->write_memblock) { + s->write_memblock = pa_memblock_new(s->context->mempool, *nbytes); + s->write_data = pa_memblock_acquire(s->write_memblock); + } + + *data = s->write_data; + *nbytes = pa_memblock_get_length(s->write_memblock); + + return 0; +} + +int pa_stream_cancel_write( + pa_stream *s) { + + pa_assert(s); + pa_assert(PA_REFCNT_VALUE(s) >= 1); + + PA_CHECK_VALIDITY(s->context, !pa_detect_fork(), PA_ERR_FORKED); + PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY(s->context, s->direction == PA_STREAM_PLAYBACK || s->direction == PA_STREAM_UPLOAD, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY(s->context, s->write_memblock, PA_ERR_BADSTATE); + + pa_assert(s->write_data); + + pa_memblock_release(s->write_memblock); + pa_memblock_unref(s->write_memblock); + s->write_memblock = NULL; + s->write_data = NULL; + + return 0; +} + int pa_stream_write( pa_stream *s, const void *data, size_t length, - void (*free_cb)(void *p), + pa_free_cb_t free_cb, int64_t offset, pa_seek_mode_t seek) { - pa_memchunk chunk; - pa_seek_mode_t t_seek; - int64_t t_offset; - size_t t_length; - const void *t_data; - pa_assert(s); pa_assert(PA_REFCNT_VALUE(s) >= 1); pa_assert(data); @@ -1216,46 +1256,71 @@ int pa_stream_write( PA_CHECK_VALIDITY(s->context, s->direction == PA_STREAM_PLAYBACK || s->direction == PA_STREAM_UPLOAD, PA_ERR_BADSTATE); PA_CHECK_VALIDITY(s->context, seek <= PA_SEEK_RELATIVE_END, PA_ERR_INVALID); PA_CHECK_VALIDITY(s->context, s->direction == PA_STREAM_PLAYBACK || (seek == PA_SEEK_RELATIVE && offset == 0), PA_ERR_INVALID); + PA_CHECK_VALIDITY(s->context, + !s->write_memblock || + ((data >= s->write_data) && + ((const char*) data + length <= (const char*) s->write_data + pa_memblock_get_length(s->write_memblock))), + PA_ERR_INVALID); + PA_CHECK_VALIDITY(s->context, !free_cb || !s->write_memblock, PA_ERR_INVALID); - if (length <= 0) - return 0; + if (s->write_memblock) { + pa_memchunk chunk; - t_seek = seek; - t_offset = offset; - t_length = length; - t_data = data; + /* pa_stream_write_begin() was called before */ - while (t_length > 0) { + pa_memblock_release(s->write_memblock); - chunk.index = 0; + chunk.memblock = s->write_memblock; + chunk.index = (const char *) data - (const char *) s->write_data; + chunk.length = length; - if (free_cb && !pa_pstream_get_shm(s->context->pstream)) { - chunk.memblock = pa_memblock_new_user(s->context->mempool, (void*) t_data, t_length, free_cb, 1); - chunk.length = t_length; - } else { - void *d; + s->write_memblock = NULL; + s->write_data = NULL; - chunk.length = PA_MIN(t_length, pa_mempool_block_size_max(s->context->mempool)); - chunk.memblock = pa_memblock_new(s->context->mempool, chunk.length); + pa_pstream_send_memblock(s->context->pstream, s->channel, offset, seek, &chunk); + pa_memblock_unref(chunk.memblock); - d = pa_memblock_acquire(chunk.memblock); - memcpy(d, t_data, chunk.length); - pa_memblock_release(chunk.memblock); - } + } else { + pa_seek_mode_t t_seek = seek; + int64_t t_offset = offset; + size_t t_length = length; + const void *t_data = data; - pa_pstream_send_memblock(s->context->pstream, s->channel, t_offset, t_seek, &chunk); + /* pa_stream_write_begin() was not called before */ - t_offset = 0; - t_seek = PA_SEEK_RELATIVE; + while (t_length > 0) { + pa_memchunk chunk; - t_data = (const uint8_t*) t_data + chunk.length; - t_length -= chunk.length; + chunk.index = 0; - pa_memblock_unref(chunk.memblock); - } + if (free_cb && !pa_pstream_get_shm(s->context->pstream)) { + chunk.memblock = pa_memblock_new_user(s->context->mempool, (void*) t_data, t_length, free_cb, 1); + chunk.length = t_length; + } else { + void *d; + + chunk.length = PA_MIN(t_length, pa_mempool_block_size_max(s->context->mempool)); + chunk.memblock = pa_memblock_new(s->context->mempool, chunk.length); + + d = pa_memblock_acquire(chunk.memblock); + memcpy(d, t_data, chunk.length); + pa_memblock_release(chunk.memblock); + } - if (free_cb && pa_pstream_get_shm(s->context->pstream)) - free_cb((void*) data); + pa_pstream_send_memblock(s->context->pstream, s->channel, t_offset, t_seek, &chunk); + + t_offset = 0; + t_seek = PA_SEEK_RELATIVE; + + t_data = (const uint8_t*) t_data + chunk.length; + t_length -= chunk.length; + + pa_memblock_unref(chunk.memblock); + } + + if (free_cb && pa_pstream_get_shm(s->context->pstream)) + free_cb((void*) data); + } /* This is obviously wrong since we ignore the seeking index . But * that's OK, the server side applies the same error */ @@ -1594,7 +1659,7 @@ static void stream_get_timing_info_callback(pa_pdispatch *pd, uint32_t command, if (o->stream->smoother) { pa_usec_t u, x; - u = x = pa_rtclock_usec() - i->transport_usec; + u = x = pa_rtclock_now() - i->transport_usec; if (o->stream->direction == PA_STREAM_PLAYBACK && o->context->version >= 13) { pa_usec_t su; @@ -2103,7 +2168,7 @@ int pa_stream_get_time(pa_stream *s, pa_usec_t *r_usec) { PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_RECORD || !s->timing_info.write_index_corrupt, PA_ERR_NODATA); if (s->smoother) - usec = pa_smoother_get(s->smoother, pa_rtclock_usec()); + usec = pa_smoother_get(s->smoother, pa_rtclock_now()); else usec = calc_time(s, FALSE); diff --git a/src/pulse/stream.h b/src/pulse/stream.h index 49c132a2..fecc5870 100644 --- a/src/pulse/stream.h +++ b/src/pulse/stream.h @@ -418,15 +418,71 @@ int pa_stream_connect_record( /** Disconnect a stream from a source/sink */ int pa_stream_disconnect(pa_stream *s); -/** Write some data to the server (for playback sinks), if free_cb is - * non-NULL this routine is called when all data has been written out - * and an internal reference to the specified data is kept, the data - * is not copied. If NULL, the data is copied into an internal - * buffer. The client my freely seek around in the output buffer. For +/** Prepare writing data to the server (for playback streams). This + * function may be used to optimize the number of memory copies when + * doing playback ("zero-copy"). It is recommended to call this + * function before each call to pa_stream_write(). Pass in the address + * to a pointer and an address of the number of bytes you want to + * write. On return the two values will contain a pointer where you + * can place the data to write and the maximum number of bytes you can + * write. On return *nbytes can be larger or have the same value as + * you passed in. You need to be able to handle both cases. Accessing + * memory beyond the returned *nbytes value is invalid. Acessing the + * memory returned after the following pa_stream_write() or + * pa_stream_cancel_write() is invalid. On invocation only *nbytes + * needs to be initialized, on return both *data and *nbytes will be + * valid. If you place (size_t) -1 in *nbytes on invocation the memory + * size will be chosen automatically (which is recommended to + * do). After placing your data in the memory area returned call + * pa_stream_write() with data set to an address within this memory + * area and an nbytes value that is smaller or equal to what was + * returned by this function to actually execute the write. An + * invocation of pa_stream_write() should follow "quickly" on + * pa_stream_begin_write(). It is not recommended letting an unbounded + * amount of time pass after calling pa_stream_begin_write() and + * before calling pa_stream_write(). If you want to cancel a + * previously called pa_stream_begin_write() without calling + * pa_stream_write() use pa_stream_cancel_write() instead. Calling + * pa_stream_begin_write() twice without calling pa_stream_write() or + * pa_stream_cancel_write() in between will return exactly the same + * pointer/nbytes values.\since 0.9.16 */ +int pa_stream_begin_write( + pa_stream *p, + void **data, + size_t *nbytes); + +/** Reverses the effect of pa_stream_begin_write() dropping all data + * that has already been placed in the memory area returned by + * pa_stream_begin_write(). Only valid to call if + * pa_stream_begin_write() was called before and neither + * pa_stream_cancel_write() nor pa_stream_write() have been called + * yet. Accessing the memory previously returned by + * pa_stream_begin_write() after this call is invalid. Any further + * explicit freeing of the memory area is not necessary. \since + * 0.9.16 */ +int pa_stream_cancel_write( + pa_stream *p); + +/** Write some data to the server (for playback streams), if free_cb + * is non-NULL this routine is called when all data has been written + * out and an internal reference to the specified data is kept, the + * data is not copied. If NULL, the data is copied into an internal + * buffer. The client may freely seek around in the output buffer. For * most applications passing 0 and PA_SEEK_RELATIVE as arguments for * offset and seek should be useful. Afte ther write call succeeded * the write index will be a the position after where this chunk of - * data has been written to. */ + * data has been written to. + * + * As an optimization for avoiding needless memory copies you may call + * pa_stream_begin_write() before this call and then place your audio + * data directly in the memory area returned by that call. Then, pass + * a pointer to that memory area to pa_stream_write(). After the + * invocation of pa_stream_write() the memory area may no longer be + * accessed. Any further explicit freeing of the memory area is not + * necessary. It is OK to write the memory area returned by + * pa_stream_begin_write() only partially with this call, skipping + * bytes both at the end and at the beginning of the reserved memory + * area.*/ int pa_stream_write( pa_stream *p /**< The stream to use */, const void *data /**< The data to write */, @@ -435,7 +491,7 @@ int pa_stream_write( 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 */); -/** Read the next fragment from the buffer (for recording). +/** Read the next fragment from the buffer (for recording streams). * data will point to the actual data and length will contain the size * of the data in bytes (which can be less than a complete framgnet). * Use pa_stream_drop() to actually remove the data from the diff --git a/src/pulse/thread-mainloop.c b/src/pulse/thread-mainloop.c index c77cc64e..6916d867 100644 --- a/src/pulse/thread-mainloop.c +++ b/src/pulse/thread-mainloop.c @@ -24,6 +24,10 @@ #include <config.h> #endif +#ifndef OS_IS_WIN32 +#include <pthread.h> +#endif + #include <signal.h> #include <stdio.h> diff --git a/src/pulse/timeval.h b/src/pulse/timeval.h index 651da953..48c6cdb3 100644 --- a/src/pulse/timeval.h +++ b/src/pulse/timeval.h @@ -40,16 +40,19 @@ PA_C_DECL_BEGIN #define PA_USEC_PER_SEC ((pa_usec_t) 1000000ULL) /** The number of nanoseconds in a second */ -#define PA_NSEC_PER_SEC ((pa_usec_t) 1000000000ULL) +#define PA_NSEC_PER_SEC ((unsigned long long) 1000000000ULL) /** The number of microseconds in a millisecond */ #define PA_USEC_PER_MSEC ((pa_usec_t) 1000ULL) /** The number of nanoseconds in a millisecond */ -#define PA_NSEC_PER_MSEC ((pa_usec_t) 1000000ULL) +#define PA_NSEC_PER_MSEC ((unsigned long long) 1000000ULL) /** The number of nanoseconds in a microsecond */ -#define PA_NSEC_PER_USEC ((pa_usec_t) 1000ULL) +#define PA_NSEC_PER_USEC ((unsigned long long) 1000ULL) + +/** Invalid time in usec */ +#define PA_USEC_INVALID ((pa_usec_t) -1) struct timeval; |