From f44ba092651aa75055e109e04b4164ea92ae7fdc Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 19 Jun 2006 21:53:48 +0000 Subject: big s/polyp/pulse/g git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@1033 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/Makefile | 13 + src/pulse/browser.c | 334 +++++++++++ src/pulse/browser.h | 68 +++ src/pulse/cdecl.h | 42 ++ src/pulse/channelmap.c | 445 ++++++++++++++ src/pulse/channelmap.h | 191 ++++++ src/pulse/client-conf-x11.c | 93 +++ src/pulse/client-conf-x11.h | 31 + src/pulse/client-conf.c | 193 ++++++ src/pulse/client-conf.h | 52 ++ src/pulse/client.conf.in | 39 ++ src/pulse/context.c | 980 +++++++++++++++++++++++++++++++ src/pulse/context.h | 230 ++++++++ src/pulse/def.h | 312 ++++++++++ src/pulse/error.c | 66 +++ src/pulse/error.h | 38 ++ src/pulse/glib-mainloop.c | 541 +++++++++++++++++ src/pulse/glib-mainloop.h | 66 +++ src/pulse/glib12-mainloop.c | 503 ++++++++++++++++ src/pulse/internal.h | 210 +++++++ src/pulse/introspect.c | 1240 +++++++++++++++++++++++++++++++++++++++ src/pulse/introspect.h | 490 ++++++++++++++++ src/pulse/mainloop-api.c | 70 +++ src/pulse/mainloop-api.h | 118 ++++ src/pulse/mainloop-signal.c | 210 +++++++ src/pulse/mainloop-signal.h | 59 ++ src/pulse/mainloop.c | 839 ++++++++++++++++++++++++++ src/pulse/mainloop.h | 127 ++++ src/pulse/operation.c | 116 ++++ src/pulse/operation.h | 50 ++ src/pulse/polypaudio.h | 115 ++++ src/pulse/sample.c | 156 +++++ src/pulse/sample.h | 189 ++++++ src/pulse/scache.c | 131 +++++ src/pulse/scache.h | 100 ++++ src/pulse/simple.c | 455 ++++++++++++++ src/pulse/simple.h | 146 +++++ src/pulse/stream.c | 1366 +++++++++++++++++++++++++++++++++++++++++++ src/pulse/stream.h | 449 ++++++++++++++ src/pulse/subscribe.c | 89 +++ src/pulse/subscribe.h | 61 ++ src/pulse/thread-mainloop.c | 466 +++++++++++++++ src/pulse/thread-mainloop.h | 299 ++++++++++ src/pulse/timeval.c | 142 +++++ src/pulse/timeval.h | 53 ++ src/pulse/utf8.c | 237 ++++++++ src/pulse/utf8.h | 47 ++ src/pulse/util.c | 227 +++++++ src/pulse/util.h | 59 ++ src/pulse/version.h.in | 53 ++ src/pulse/volume.c | 176 ++++++ src/pulse/volume.h | 172 ++++++ src/pulse/xmalloc.c | 128 ++++ src/pulse/xmalloc.h | 78 +++ 54 files changed, 13160 insertions(+) create mode 100644 src/pulse/Makefile create mode 100644 src/pulse/browser.c create mode 100644 src/pulse/browser.h create mode 100644 src/pulse/cdecl.h create mode 100644 src/pulse/channelmap.c create mode 100644 src/pulse/channelmap.h create mode 100644 src/pulse/client-conf-x11.c create mode 100644 src/pulse/client-conf-x11.h create mode 100644 src/pulse/client-conf.c create mode 100644 src/pulse/client-conf.h create mode 100644 src/pulse/client.conf.in create mode 100644 src/pulse/context.c create mode 100644 src/pulse/context.h create mode 100644 src/pulse/def.h create mode 100644 src/pulse/error.c create mode 100644 src/pulse/error.h create mode 100644 src/pulse/glib-mainloop.c create mode 100644 src/pulse/glib-mainloop.h create mode 100644 src/pulse/glib12-mainloop.c create mode 100644 src/pulse/internal.h create mode 100644 src/pulse/introspect.c create mode 100644 src/pulse/introspect.h create mode 100644 src/pulse/mainloop-api.c create mode 100644 src/pulse/mainloop-api.h create mode 100644 src/pulse/mainloop-signal.c create mode 100644 src/pulse/mainloop-signal.h create mode 100644 src/pulse/mainloop.c create mode 100644 src/pulse/mainloop.h create mode 100644 src/pulse/operation.c create mode 100644 src/pulse/operation.h create mode 100644 src/pulse/polypaudio.h create mode 100644 src/pulse/sample.c create mode 100644 src/pulse/sample.h create mode 100644 src/pulse/scache.c create mode 100644 src/pulse/scache.h create mode 100644 src/pulse/simple.c create mode 100644 src/pulse/simple.h create mode 100644 src/pulse/stream.c create mode 100644 src/pulse/stream.h create mode 100644 src/pulse/subscribe.c create mode 100644 src/pulse/subscribe.h create mode 100644 src/pulse/thread-mainloop.c create mode 100644 src/pulse/thread-mainloop.h create mode 100644 src/pulse/timeval.c create mode 100644 src/pulse/timeval.h create mode 100644 src/pulse/utf8.c create mode 100644 src/pulse/utf8.h create mode 100644 src/pulse/util.c create mode 100644 src/pulse/util.h create mode 100644 src/pulse/version.h.in create mode 100644 src/pulse/volume.c create mode 100644 src/pulse/volume.h create mode 100644 src/pulse/xmalloc.c create mode 100644 src/pulse/xmalloc.h (limited to 'src/pulse') diff --git a/src/pulse/Makefile b/src/pulse/Makefile new file mode 100644 index 00000000..7c8875f3 --- /dev/null +++ b/src/pulse/Makefile @@ -0,0 +1,13 @@ +# This is a dirty trick just to ease compilation with emacs +# +# This file is not intended to be distributed or anything +# +# So: don't touch it, even better ignore it! + +all: + $(MAKE) -C .. + +clean: + $(MAKE) -C .. clean + +.PHONY: all clean diff --git a/src/pulse/browser.c b/src/pulse/browser.c new file mode 100644 index 00000000..d063465d --- /dev/null +++ b/src/pulse/browser.c @@ -0,0 +1,334 @@ +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + 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 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 +#include + +#include + +#include +#include + +#include "browser.h" + +#define SERVICE_NAME_SINK "_pulseaudio-sink._tcp." +#define SERVICE_NAME_SOURCE "_pulseaudio-source._tcp." +#define SERVICE_NAME_SERVER "_pulseaudio-server._tcp." + +struct pa_browser { + int ref; + pa_mainloop_api *mainloop; + + pa_browse_cb_t callback; + void *userdata; + + sw_discovery discovery; + pa_io_event *io_event; +}; + +static void io_callback(pa_mainloop_api*a, PA_GCC_UNUSED pa_io_event*e, PA_GCC_UNUSED int fd, pa_io_event_flags_t events, void *userdata) { + pa_browser *b = userdata; + assert(a && b && b->mainloop == a); + + if (events != PA_IO_EVENT_INPUT || sw_discovery_read_socket(b->discovery) != SW_OKAY) { + pa_log(__FILE__": connection to HOWL daemon failed."); + b->mainloop->io_free(b->io_event); + b->io_event = NULL; + return; + } +} + +static int type_equal(const char *a, const char *b) { + size_t la, lb; + + if (strcasecmp(a, b) == 0) + return 1; + + la = strlen(a); + lb = strlen(b); + + if (la > 0 && a[la-1] == '.' && la == lb+1 && strncasecmp(a, b, la-1) == 0) + return 1; + + if (lb > 0 && b[lb-1] == '.' && lb == la+1 && strncasecmp(a, b, lb-1) == 0) + return 1; + + return 0; +} + +static int map_to_opcode(const char *type, int new) { + if (type_equal(type, SERVICE_NAME_SINK)) + return new ? PA_BROWSE_NEW_SINK : PA_BROWSE_REMOVE_SINK; + else if (type_equal(type, SERVICE_NAME_SOURCE)) + return new ? PA_BROWSE_NEW_SOURCE : PA_BROWSE_REMOVE_SOURCE; + else if (type_equal(type, SERVICE_NAME_SERVER)) + return new ? PA_BROWSE_NEW_SERVER : PA_BROWSE_REMOVE_SERVER; + + return -1; +} + +static sw_result resolve_reply( + sw_discovery discovery, + sw_discovery_oid oid, + sw_uint32 interface_index, + sw_const_string name, + sw_const_string type, + sw_const_string domain, + sw_ipv4_address address, + sw_port port, + sw_octets text_record, + sw_ulong text_record_len, + sw_opaque extra) { + + pa_browser *b = extra; + pa_browse_info i; + char ip[256], a[256]; + int opcode; + int device_found = 0; + uint32_t cookie; + pa_sample_spec ss; + int ss_valid = 0; + sw_text_record_iterator iterator; + int free_iterator = 0; + char *c = NULL; + + assert(b); + + sw_discovery_cancel(discovery, oid); + + memset(&i, 0, sizeof(i)); + i.name = name; + + if (!b->callback) + goto fail; + + opcode = map_to_opcode(type, 1); + assert(opcode >= 0); + + snprintf(a, sizeof(a), "tcp:%s:%u", sw_ipv4_address_name(address, ip, sizeof(ip)), port); + i.server = a; + + if (text_record && text_record_len) { + char key[SW_TEXT_RECORD_MAX_LEN]; + uint8_t val[SW_TEXT_RECORD_MAX_LEN]; + uint32_t val_len; + + if (sw_text_record_iterator_init(&iterator, text_record, text_record_len) != SW_OKAY) { + pa_log_error(__FILE__": sw_text_record_string_iterator_init() failed."); + goto fail; + } + + free_iterator = 1; + + while (sw_text_record_iterator_next(iterator, key, val, &val_len) == SW_OKAY) { + c = pa_xstrndup((char*) val, val_len); + + if (!strcmp(key, "device")) { + device_found = 1; + pa_xfree((char*) i.device); + i.device = c; + c = NULL; + } else if (!strcmp(key, "server-version")) { + pa_xfree((char*) i.server_version); + i.server_version = c; + c = NULL; + } else if (!strcmp(key, "user-name")) { + pa_xfree((char*) i.user_name); + i.user_name = c; + c = NULL; + } else if (!strcmp(key, "fqdn")) { + size_t l; + + pa_xfree((char*) i.fqdn); + i.fqdn = c; + c = NULL; + + l = strlen(a); + assert(l+1 <= sizeof(a)); + strncat(a, " ", sizeof(a)-l-1); + strncat(a, i.fqdn, sizeof(a)-l-2); + } else if (!strcmp(key, "cookie")) { + + if (pa_atou(c, &cookie) < 0) + goto fail; + + i.cookie = &cookie; + } else if (!strcmp(key, "description")) { + pa_xfree((char*) i.description); + i.description = c; + c = NULL; + } else if (!strcmp(key, "channels")) { + uint32_t ch; + + if (pa_atou(c, &ch) < 0 || ch <= 0 || ch > 255) + goto fail; + + ss.channels = (uint8_t) ch; + ss_valid |= 1; + + } else if (!strcmp(key, "rate")) { + if (pa_atou(c, &ss.rate) < 0) + goto fail; + ss_valid |= 2; + } else if (!strcmp(key, "format")) { + + if ((ss.format = pa_parse_sample_format(c)) == PA_SAMPLE_INVALID) + goto fail; + + ss_valid |= 4; + } + + pa_xfree(c); + c = NULL; + } + + } + + /* No device txt record was sent for a sink or source service */ + if (opcode != PA_BROWSE_NEW_SERVER && !device_found) + goto fail; + + if (ss_valid == 7) + i.sample_spec = &ss; + + + b->callback(b, opcode, &i, b->userdata); + +fail: + pa_xfree((void*) i.device); + pa_xfree((void*) i.fqdn); + pa_xfree((void*) i.server_version); + pa_xfree((void*) i.user_name); + pa_xfree((void*) i.description); + pa_xfree(c); + + if (free_iterator) + sw_text_record_iterator_fina(iterator); + + + return SW_OKAY; +} + +static sw_result browse_reply( + sw_discovery discovery, + sw_discovery_oid id, + sw_discovery_browse_status status, + sw_uint32 interface_index, + sw_const_string name, + sw_const_string type, + sw_const_string domain, + sw_opaque extra) { + + pa_browser *b = extra; + assert(b); + + switch (status) { + case SW_DISCOVERY_BROWSE_ADD_SERVICE: { + sw_discovery_oid oid; + + if (sw_discovery_resolve(b->discovery, 0, name, type, domain, resolve_reply, b, &oid) != SW_OKAY) + pa_log_error(__FILE__": sw_discovery_resolve() failed"); + + break; + } + + case SW_DISCOVERY_BROWSE_REMOVE_SERVICE: + if (b->callback) { + pa_browse_info i; + int opcode; + + memset(&i, 0, sizeof(i)); + i.name = name; + + opcode = map_to_opcode(type, 0); + assert(opcode >= 0); + + b->callback(b, opcode, &i, b->userdata); + } + break; + + default: + ; + } + + return SW_OKAY; +} + +pa_browser *pa_browser_new(pa_mainloop_api *mainloop) { + pa_browser *b; + sw_discovery_oid oid; + + b = pa_xnew(pa_browser, 1); + b->mainloop = mainloop; + b->ref = 1; + b->callback = NULL; + b->userdata = NULL; + + if (sw_discovery_init(&b->discovery) != SW_OKAY) { + pa_log_error(__FILE__": sw_discovery_init() failed."); + pa_xfree(b); + return NULL; + } + + if (sw_discovery_browse(b->discovery, 0, SERVICE_NAME_SERVER, NULL, browse_reply, b, &oid) != SW_OKAY || + sw_discovery_browse(b->discovery, 0, SERVICE_NAME_SINK, NULL, browse_reply, b, &oid) != SW_OKAY || + sw_discovery_browse(b->discovery, 0, SERVICE_NAME_SOURCE, NULL, browse_reply, b, &oid) != SW_OKAY) { + + pa_log_error(__FILE__": sw_discovery_browse() failed."); + + sw_discovery_fina(b->discovery); + pa_xfree(b); + return NULL; + } + + b->io_event = mainloop->io_new(mainloop, sw_discovery_socket(b->discovery), PA_IO_EVENT_INPUT, io_callback, b); + return b; +} + +static void browser_free(pa_browser *b) { + assert(b && b->mainloop); + + if (b->io_event) + b->mainloop->io_free(b->io_event); + + sw_discovery_fina(b->discovery); + pa_xfree(b); +} + +pa_browser *pa_browser_ref(pa_browser *b) { + assert(b && b->ref >= 1); + b->ref++; + return b; +} + +void pa_browser_unref(pa_browser *b) { + assert(b && b->ref >= 1); + + if ((-- (b->ref)) <= 0) + browser_free(b); +} + +void pa_browser_set_callback(pa_browser *b, pa_browse_cb_t cb, void *userdata) { + assert(b); + + b->callback = cb; + b->userdata = userdata; +} diff --git a/src/pulse/browser.h b/src/pulse/browser.h new file mode 100644 index 00000000..2d20c6c0 --- /dev/null +++ b/src/pulse/browser.h @@ -0,0 +1,68 @@ +#ifndef foobrowserhfoo +#define foobrowserhfoo + +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + 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 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 +#include +#include +#include + +PA_C_DECL_BEGIN + +typedef struct pa_browser pa_browser; + +typedef enum pa_browse_opcode { + PA_BROWSE_NEW_SERVER = 0, + PA_BROWSE_NEW_SINK, + PA_BROWSE_NEW_SOURCE, + PA_BROWSE_REMOVE_SERVER, + PA_BROWSE_REMOVE_SINK, + PA_BROWSE_REMOVE_SOURCE +} pa_browse_opcode_t; + +pa_browser *pa_browser_new(pa_mainloop_api *mainloop); +pa_browser *pa_browser_ref(pa_browser *z); +void pa_browser_unref(pa_browser *z); + +typedef struct pa_browse_info { + /* Unique service name */ + const char *name; /* always available */ + + /* Server info */ + const char *server; /* always available */ + const char *server_version, *user_name, *fqdn; /* optional */ + const uint32_t *cookie; /* optional */ + + /* Device info */ + const char *device; /* always available when this information is of a sink/source */ + const char *description; /* optional */ + const pa_sample_spec *sample_spec; /* optional */ +} pa_browse_info; + +typedef void (*pa_browse_cb_t)(pa_browser *z, pa_browse_opcode_t c, const pa_browse_info *i, void *userdata); + +void pa_browser_set_callback(pa_browser *z, pa_browse_cb_t cb, void *userdata); + +PA_C_DECL_END + +#endif diff --git a/src/pulse/cdecl.h b/src/pulse/cdecl.h new file mode 100644 index 00000000..6ac96687 --- /dev/null +++ b/src/pulse/cdecl.h @@ -0,0 +1,42 @@ +#ifndef foocdeclhfoo +#define foocdeclhfoo + +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + 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 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. +***/ + +/** \file + * C++ compatibility support */ + +#ifdef __cplusplus +/** If using C++ this macro enables C mode, otherwise does nothing */ +#define PA_C_DECL_BEGIN extern "C" { +/** If using C++ this macros switches back to C++ mode, otherwise does nothing */ +#define PA_C_DECL_END } + +#else +/** If using C++ this macro enables C mode, otherwise does nothing */ +#define PA_C_DECL_BEGIN +/** If using C++ this macros switches back to C++ mode, otherwise does nothing */ +#define PA_C_DECL_END + +#endif + +#endif diff --git a/src/pulse/channelmap.c b/src/pulse/channelmap.c new file mode 100644 index 00000000..a4f13372 --- /dev/null +++ b/src/pulse/channelmap.c @@ -0,0 +1,445 @@ +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + 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 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 +#endif + +#include +#include +#include +#include + +#include +#include + +#include "channelmap.h" + +const char *const table[] = { + [PA_CHANNEL_POSITION_MONO] = "mono", + + [PA_CHANNEL_POSITION_FRONT_CENTER] = "front-center", + [PA_CHANNEL_POSITION_FRONT_LEFT] = "front-left", + [PA_CHANNEL_POSITION_FRONT_RIGHT] = "front-right", + + [PA_CHANNEL_POSITION_REAR_CENTER] = "rear-center", + [PA_CHANNEL_POSITION_REAR_LEFT] = "rear-left", + [PA_CHANNEL_POSITION_REAR_RIGHT] = "rear-right", + + [PA_CHANNEL_POSITION_LFE] = "lfe", + + [PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER] = "front-left-of-center", + [PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER] = "front-right-of-center", + + [PA_CHANNEL_POSITION_SIDE_LEFT] = "side-left", + [PA_CHANNEL_POSITION_SIDE_RIGHT] = "side-right", + + [PA_CHANNEL_POSITION_AUX0] = "aux0", + [PA_CHANNEL_POSITION_AUX1] = "aux1", + [PA_CHANNEL_POSITION_AUX2] = "aux2", + [PA_CHANNEL_POSITION_AUX3] = "aux3", + [PA_CHANNEL_POSITION_AUX4] = "aux4", + [PA_CHANNEL_POSITION_AUX5] = "aux5", + [PA_CHANNEL_POSITION_AUX6] = "aux6", + [PA_CHANNEL_POSITION_AUX7] = "aux7", + [PA_CHANNEL_POSITION_AUX8] = "aux8", + [PA_CHANNEL_POSITION_AUX9] = "aux9", + [PA_CHANNEL_POSITION_AUX10] = "aux10", + [PA_CHANNEL_POSITION_AUX11] = "aux11", + [PA_CHANNEL_POSITION_AUX12] = "aux12", + [PA_CHANNEL_POSITION_AUX13] = "aux13", + [PA_CHANNEL_POSITION_AUX14] = "aux14", + [PA_CHANNEL_POSITION_AUX15] = "aux15", + + [PA_CHANNEL_POSITION_TOP_CENTER] = "top-center", + + [PA_CHANNEL_POSITION_TOP_FRONT_LEFT] = "top-front-left", + [PA_CHANNEL_POSITION_TOP_FRONT_RIGHT] = "top-front-right", + [PA_CHANNEL_POSITION_TOP_FRONT_CENTER] = "top-front-center", + + [PA_CHANNEL_POSITION_TOP_REAR_LEFT] = "top-rear-left", + [PA_CHANNEL_POSITION_TOP_REAR_RIGHT] = "top-rear-right", + [PA_CHANNEL_POSITION_TOP_REAR_CENTER] = "top-rear-center" +}; + +pa_channel_map* pa_channel_map_init(pa_channel_map *m) { + unsigned c; + assert(m); + + m->channels = 0; + + for (c = 0; c < PA_CHANNELS_MAX; c++) + m->map[c] = PA_CHANNEL_POSITION_INVALID; + + return m; +} + +pa_channel_map* pa_channel_map_init_mono(pa_channel_map *m) { + assert(m); + + pa_channel_map_init(m); + + m->channels = 1; + m->map[0] = PA_CHANNEL_POSITION_MONO; + return m; +} + +pa_channel_map* pa_channel_map_init_stereo(pa_channel_map *m) { + assert(m); + + pa_channel_map_init(m); + + m->channels = 2; + m->map[0] = PA_CHANNEL_POSITION_LEFT; + m->map[1] = PA_CHANNEL_POSITION_RIGHT; + return m; +} + +pa_channel_map* pa_channel_map_init_auto(pa_channel_map *m, unsigned channels, pa_channel_map_def_t def) { + assert(m); + assert(channels > 0); + assert(channels <= PA_CHANNELS_MAX); + + pa_channel_map_init(m); + + m->channels = channels; + + switch (def) { + case PA_CHANNEL_MAP_AIFF: + + /* This is somewhat compatible with RFC3551 */ + + switch (channels) { + case 1: + m->map[0] = PA_CHANNEL_POSITION_MONO; + return m; + + case 6: + m->map[0] = PA_CHANNEL_POSITION_FRONT_LEFT; + m->map[1] = PA_CHANNEL_POSITION_SIDE_LEFT; + m->map[2] = PA_CHANNEL_POSITION_FRONT_CENTER; + m->map[3] = PA_CHANNEL_POSITION_FRONT_RIGHT; + m->map[4] = PA_CHANNEL_POSITION_SIDE_RIGHT; + m->map[5] = PA_CHANNEL_POSITION_LFE; + return m; + + case 5: + m->map[2] = PA_CHANNEL_POSITION_FRONT_CENTER; + m->map[3] = PA_CHANNEL_POSITION_REAR_LEFT; + m->map[4] = PA_CHANNEL_POSITION_REAR_RIGHT; + /* Fall through */ + + case 2: + m->map[0] = PA_CHANNEL_POSITION_FRONT_LEFT; + m->map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT; + return m; + + case 3: + m->map[0] = PA_CHANNEL_POSITION_LEFT; + m->map[1] = PA_CHANNEL_POSITION_RIGHT; + m->map[2] = PA_CHANNEL_POSITION_CENTER; + return m; + + case 4: + m->map[0] = PA_CHANNEL_POSITION_LEFT; + m->map[1] = PA_CHANNEL_POSITION_CENTER; + m->map[2] = PA_CHANNEL_POSITION_RIGHT; + m->map[3] = PA_CHANNEL_POSITION_LFE; + return m; + + default: + return NULL; + } + + case PA_CHANNEL_MAP_ALSA: + + switch (channels) { + case 1: + m->map[0] = PA_CHANNEL_POSITION_MONO; + return m; + + case 8: + m->map[6] = PA_CHANNEL_POSITION_SIDE_LEFT; + m->map[7] = PA_CHANNEL_POSITION_SIDE_RIGHT; + /* Fall through */ + + case 6: + m->map[5] = PA_CHANNEL_POSITION_LFE; + /* Fall through */ + + case 5: + m->map[4] = PA_CHANNEL_POSITION_FRONT_CENTER; + /* Fall through */ + + case 4: + m->map[2] = PA_CHANNEL_POSITION_REAR_LEFT; + m->map[3] = PA_CHANNEL_POSITION_REAR_RIGHT; + /* Fall through */ + + case 2: + m->map[0] = PA_CHANNEL_POSITION_FRONT_LEFT; + m->map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT; + return m; + + default: + return NULL; + } + + case PA_CHANNEL_MAP_AUX: { + unsigned i; + + if (channels >= PA_CHANNELS_MAX) + return NULL; + + for (i = 0; i < channels; i++) + m->map[i] = PA_CHANNEL_POSITION_AUX0 + i; + + return m; + } + + case PA_CHANNEL_MAP_WAVEEX: + + switch (channels) { + case 1: + m->map[0] = PA_CHANNEL_POSITION_MONO; + return m; + + case 18: + m->map[15] = PA_CHANNEL_POSITION_TOP_REAR_LEFT; + m->map[16] = PA_CHANNEL_POSITION_TOP_REAR_CENTER; + m->map[17] = PA_CHANNEL_POSITION_TOP_REAR_RIGHT; + /* Fall through */ + + case 15: + m->map[12] = PA_CHANNEL_POSITION_TOP_FRONT_LEFT; + m->map[13] = PA_CHANNEL_POSITION_TOP_FRONT_CENTER; + m->map[14] = PA_CHANNEL_POSITION_TOP_FRONT_RIGHT; + /* Fall through */ + + case 12: + m->map[11] = PA_CHANNEL_POSITION_TOP_CENTER; + /* Fall through */ + + case 11: + m->map[9] = PA_CHANNEL_POSITION_SIDE_LEFT; + m->map[10] = PA_CHANNEL_POSITION_SIDE_RIGHT; + /* Fall through */ + + case 9: + m->map[8] = PA_CHANNEL_POSITION_REAR_CENTER; + /* Fall through */ + + case 8: + m->map[6] = PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER; + m->map[7] = PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER; + /* Fall through */ + + case 6: + m->map[4] = PA_CHANNEL_POSITION_REAR_LEFT; + m->map[5] = PA_CHANNEL_POSITION_REAR_RIGHT; + /* Fall through */ + + case 4: + m->map[3] = PA_CHANNEL_POSITION_LFE; + /* Fall through */ + + case 3: + m->map[2] = PA_CHANNEL_POSITION_FRONT_CENTER; + /* Fall through */ + + case 2: + m->map[0] = PA_CHANNEL_POSITION_FRONT_LEFT; + m->map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT; + return m; + + default: + return NULL; + } + + case PA_CHANNEL_MAP_OSS: + + switch (channels) { + case 1: + m->map[0] = PA_CHANNEL_POSITION_MONO; + return m; + + case 8: + m->map[6] = PA_CHANNEL_POSITION_REAR_LEFT; + m->map[7] = PA_CHANNEL_POSITION_REAR_RIGHT; + /* Fall through */ + + case 6: + m->map[4] = PA_CHANNEL_POSITION_SIDE_LEFT; + m->map[5] = PA_CHANNEL_POSITION_SIDE_RIGHT; + /* Fall through */ + + case 4: + m->map[3] = PA_CHANNEL_POSITION_LFE; + /* Fall through */ + + case 3: + m->map[2] = PA_CHANNEL_POSITION_FRONT_CENTER; + /* Fall through */ + + case 2: + m->map[0] = PA_CHANNEL_POSITION_FRONT_LEFT; + m->map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT; + return m; + + default: + return NULL; + } + + + default: + return NULL; + } +} + + +const char* pa_channel_position_to_string(pa_channel_position_t pos) { + + if (pos < 0 || pos >= PA_CHANNEL_POSITION_MAX) + return NULL; + + return table[pos]; +} + +int pa_channel_map_equal(const pa_channel_map *a, const pa_channel_map *b) { + unsigned c; + + assert(a); + assert(b); + + if (a->channels != b->channels) + return 0; + + for (c = 0; c < a->channels; c++) + if (a->map[c] != b->map[c]) + return 0; + + return 1; +} + +char* pa_channel_map_snprint(char *s, size_t l, const pa_channel_map *map) { + unsigned channel; + int first = 1; + char *e; + + assert(s); + assert(l > 0); + assert(map); + + *(e = s) = 0; + + for (channel = 0; channel < map->channels && l > 1; channel++) { + l -= snprintf(e, l, "%s%s", + first ? "" : ",", + pa_channel_position_to_string(map->map[channel])); + + e = strchr(e, 0); + first = 0; + } + + return s; +} + +pa_channel_map *pa_channel_map_parse(pa_channel_map *rmap, const char *s) { + const char *state; + pa_channel_map map; + char *p; + + assert(rmap); + assert(s); + + memset(&map, 0, sizeof(map)); + + if (strcmp(s, "stereo") == 0) { + map.channels = 2; + map.map[0] = PA_CHANNEL_POSITION_LEFT; + map.map[1] = PA_CHANNEL_POSITION_RIGHT; + goto finish; + } + + state = NULL; + map.channels = 0; + + while ((p = pa_split(s, ",", &state))) { + + if (map.channels >= PA_CHANNELS_MAX) { + pa_xfree(p); + return NULL; + } + + /* Some special aliases */ + if (strcmp(p, "left") == 0) + map.map[map.channels++] = PA_CHANNEL_POSITION_LEFT; + else if (strcmp(p, "right") == 0) + map.map[map.channels++] = PA_CHANNEL_POSITION_RIGHT; + else if (strcmp(p, "center") == 0) + map.map[map.channels++] = PA_CHANNEL_POSITION_CENTER; + else if (strcmp(p, "subwoofer") == 0) + map.map[map.channels++] = PA_CHANNEL_POSITION_SUBWOOFER; + else { + pa_channel_position_t i; + + for (i = 0; i < PA_CHANNEL_POSITION_MAX; i++) + if (strcmp(p, table[i]) == 0) { + map.map[map.channels++] = i; + break; + } + + if (i >= PA_CHANNEL_POSITION_MAX) { + pa_xfree(p); + return NULL; + } + } + + pa_xfree(p); + } + +finish: + + if (!pa_channel_map_valid(&map)) + return NULL; + + *rmap = map; + return rmap; +} + +int pa_channel_map_valid(const pa_channel_map *map) { + unsigned c; + + assert(map); + + if (map->channels <= 0 || map->channels > PA_CHANNELS_MAX) + return 0; + + for (c = 0; c < map->channels; c++) { + + if (map->map[c] < 0 ||map->map[c] >= PA_CHANNEL_POSITION_MAX) + return 0; + + } + + return 1; +} + diff --git a/src/pulse/channelmap.h b/src/pulse/channelmap.h new file mode 100644 index 00000000..8a39ade8 --- /dev/null +++ b/src/pulse/channelmap.h @@ -0,0 +1,191 @@ +#ifndef foochannelmaphfoo +#define foochannelmaphfoo + +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + 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 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 +#include + +/** \page channelmap Channel Maps + * + * \section overv_sec Overview + * + * Channel maps provide a way to associate channels in a stream with a + * specific speaker position. This relieves applications of having to + * make sure their channel order is identical to the final output. + * + * \section init_sec Initialisation + * + * A channel map consists of an array of \ref pa_channel_position values, + * one for each channel. This array is stored together with a channel count + * in a pa_channel_map structure. + * + * Before filling the structure, the application must initialise it using + * pa_channel_map_init(). There are also a number of convenience functions + * for standard channel mappings: + * + * \li pa_channel_map_init_mono() - Create a channel map with only mono audio. + * \li pa_channel_map_init_stereo() - Create a standard stereo mapping. + * \li pa_channel_map_init_auto() - Create a standard channel map for up to + * six channels. + * + * \section conv_sec Convenience Functions + * + * The library contains a number of convenience functions for dealing with + * channel maps: + * + * \li pa_channel_map_valid() - Tests if a channel map is valid. + * \li pa_channel_map_equal() - Tests if two channel maps are identical. + * \li pa_channel_map_snprint() - Creates a textual description of a channel + * map. + */ + +/** \file + * Constants and routines for channel mapping handling */ + +PA_C_DECL_BEGIN + +/** A list of channel labels */ +typedef enum pa_channel_position { + PA_CHANNEL_POSITION_INVALID = -1, + PA_CHANNEL_POSITION_MONO = 0, + + PA_CHANNEL_POSITION_LEFT, + PA_CHANNEL_POSITION_RIGHT, + PA_CHANNEL_POSITION_CENTER, + + PA_CHANNEL_POSITION_FRONT_LEFT = PA_CHANNEL_POSITION_LEFT, + PA_CHANNEL_POSITION_FRONT_RIGHT = PA_CHANNEL_POSITION_RIGHT, + PA_CHANNEL_POSITION_FRONT_CENTER = PA_CHANNEL_POSITION_CENTER, + + PA_CHANNEL_POSITION_REAR_CENTER, + PA_CHANNEL_POSITION_REAR_LEFT, + PA_CHANNEL_POSITION_REAR_RIGHT, + + PA_CHANNEL_POSITION_LFE, + PA_CHANNEL_POSITION_SUBWOOFER = PA_CHANNEL_POSITION_LFE, + + PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER, + PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER, + + PA_CHANNEL_POSITION_SIDE_LEFT, + PA_CHANNEL_POSITION_SIDE_RIGHT, + + PA_CHANNEL_POSITION_AUX0, + PA_CHANNEL_POSITION_AUX1, + PA_CHANNEL_POSITION_AUX2, + PA_CHANNEL_POSITION_AUX3, + PA_CHANNEL_POSITION_AUX4, + PA_CHANNEL_POSITION_AUX5, + PA_CHANNEL_POSITION_AUX6, + PA_CHANNEL_POSITION_AUX7, + PA_CHANNEL_POSITION_AUX8, + PA_CHANNEL_POSITION_AUX9, + PA_CHANNEL_POSITION_AUX10, + PA_CHANNEL_POSITION_AUX11, + PA_CHANNEL_POSITION_AUX12, + PA_CHANNEL_POSITION_AUX13, + PA_CHANNEL_POSITION_AUX14, + PA_CHANNEL_POSITION_AUX15, + PA_CHANNEL_POSITION_AUX16, + PA_CHANNEL_POSITION_AUX17, + PA_CHANNEL_POSITION_AUX18, + PA_CHANNEL_POSITION_AUX19, + PA_CHANNEL_POSITION_AUX20, + PA_CHANNEL_POSITION_AUX21, + PA_CHANNEL_POSITION_AUX22, + PA_CHANNEL_POSITION_AUX23, + PA_CHANNEL_POSITION_AUX24, + PA_CHANNEL_POSITION_AUX25, + PA_CHANNEL_POSITION_AUX26, + PA_CHANNEL_POSITION_AUX27, + PA_CHANNEL_POSITION_AUX28, + PA_CHANNEL_POSITION_AUX29, + PA_CHANNEL_POSITION_AUX30, + PA_CHANNEL_POSITION_AUX31, + + PA_CHANNEL_POSITION_TOP_CENTER, + + PA_CHANNEL_POSITION_TOP_FRONT_LEFT, + PA_CHANNEL_POSITION_TOP_FRONT_RIGHT, + PA_CHANNEL_POSITION_TOP_FRONT_CENTER, + + PA_CHANNEL_POSITION_TOP_REAR_LEFT, + PA_CHANNEL_POSITION_TOP_REAR_RIGHT, + PA_CHANNEL_POSITION_TOP_REAR_CENTER, + + PA_CHANNEL_POSITION_MAX +} pa_channel_position_t; + +/** A list of channel mapping definitions for pa_channel_map_init_auto() */ +typedef enum pa_channel_map_def { + PA_CHANNEL_MAP_AIFF, /**< The mapping from RFC3551, which is based on AIFF-C */ + PA_CHANNEL_MAP_ALSA, /**< The default mapping used by ALSA */ + PA_CHANNEL_MAP_AUX, /**< Only aux channels */ + PA_CHANNEL_MAP_WAVEEX, /**< Microsoft's WAVEFORMATEXTENSIBLE mapping */ + PA_CHANNEL_MAP_OSS, /**< The default channel mapping used by OSS as defined in the OSS 4.0 API specs */ + + PA_CHANNEL_MAP_DEFAULT = PA_CHANNEL_MAP_AIFF /**< The default channel map */ +} pa_channel_map_def_t; + +/** A channel map which can be used to attach labels to specific + * channels of a stream. These values are relevant for conversion and + * mixing of streams */ +typedef struct pa_channel_map { + uint8_t channels; /**< Number of channels */ + pa_channel_position_t map[PA_CHANNELS_MAX]; /**< Channel labels */ +} pa_channel_map; + +/** Initialize the specified channel map and return a pointer to it */ +pa_channel_map* pa_channel_map_init(pa_channel_map *m); + +/** Initialize the specified channel map for monoaural audio and return a pointer to it */ +pa_channel_map* pa_channel_map_init_mono(pa_channel_map *m); + +/** Initialize the specified channel map for stereophonic audio and return a pointer to it */ +pa_channel_map* pa_channel_map_init_stereo(pa_channel_map *m); + +/** Initialize the specified channel map for the specified number + * of channels using default labels and return a pointer to it. */ +pa_channel_map* pa_channel_map_init_auto(pa_channel_map *m, unsigned channels, pa_channel_map_def_t def); + +/** Return a text label for the specified channel position */ +const char* pa_channel_position_to_string(pa_channel_position_t pos); + +/** The maximum length of strings returned by pa_channel_map_snprint() */ +#define PA_CHANNEL_MAP_SNPRINT_MAX 336 + +/** Make a humand readable string from the specified channel map */ +char* pa_channel_map_snprint(char *s, size_t l, const pa_channel_map *map); + +/** Parse a channel position list into a channel map structure. \since 0.8.1 */ +pa_channel_map *pa_channel_map_parse(pa_channel_map *map, const char *s); + +/** Compare two channel maps. Return 1 if both match. */ +int pa_channel_map_equal(const pa_channel_map *a, const pa_channel_map *b); + +/** Return non-zero of the specified channel map is considered valid */ +int pa_channel_map_valid(const pa_channel_map *map); + +PA_C_DECL_END + +#endif diff --git a/src/pulse/client-conf-x11.c b/src/pulse/client-conf-x11.c new file mode 100644 index 00000000..7eea5ae3 --- /dev/null +++ b/src/pulse/client-conf-x11.c @@ -0,0 +1,93 @@ +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + 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 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-13071 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include +#include + +#include + +#include +#include +#include + +#include "client-conf-x11.h" + +int pa_client_conf_from_x11(pa_client_conf *c, const char *dname) { + Display *d = NULL; + int ret = -1; + char t[1024]; + + if (!dname && !getenv("DISPLAY")) + goto finish; + + if (!(d = XOpenDisplay(dname))) { + pa_log(__FILE__": XOpenDisplay() failed"); + goto finish; + } + + if (pa_x11_get_prop(d, "POLYP_SERVER", t, sizeof(t))) { + pa_xfree(c->default_server); + c->default_server = pa_xstrdup(t); + } + + if (pa_x11_get_prop(d, "POLYP_SINK", t, sizeof(t))) { + pa_xfree(c->default_sink); + c->default_sink = pa_xstrdup(t); + } + + if (pa_x11_get_prop(d, "POLYP_SOURCE", t, sizeof(t))) { + pa_xfree(c->default_source); + c->default_source = pa_xstrdup(t); + } + + if (pa_x11_get_prop(d, "POLYP_COOKIE", t, sizeof(t))) { + uint8_t cookie[PA_NATIVE_COOKIE_LENGTH]; + + if (pa_parsehex(t, cookie, sizeof(cookie)) != sizeof(cookie)) { + pa_log(__FILE__": failed to parse cookie data"); + goto finish; + } + + assert(sizeof(cookie) == sizeof(c->cookie)); + memcpy(c->cookie, cookie, sizeof(cookie)); + + c->cookie_valid = 1; + + pa_xfree(c->cookie_file); + c->cookie_file = NULL; + } + + ret = 0; + +finish: + if (d) + XCloseDisplay(d); + + return ret; + +} diff --git a/src/pulse/client-conf-x11.h b/src/pulse/client-conf-x11.h new file mode 100644 index 00000000..02e808be --- /dev/null +++ b/src/pulse/client-conf-x11.h @@ -0,0 +1,31 @@ +#ifndef fooclientconfx11hfoo +#define fooclientconfx11hfoo + +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + 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 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 "client-conf.h" + +/* Load client configuration data from the specified X11 display, + * overwriting the current settings in *c */ +int pa_client_conf_from_x11(pa_client_conf *c, const char *display); + +#endif diff --git a/src/pulse/client-conf.c b/src/pulse/client-conf.c new file mode 100644 index 00000000..752d0134 --- /dev/null +++ b/src/pulse/client-conf.c @@ -0,0 +1,193 @@ +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + 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 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 +#endif + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include "client-conf.h" + +#ifndef DEFAULT_CONFIG_DIR +# ifndef OS_IS_WIN32 +# define DEFAULT_CONFIG_DIR "/etc/pulseaudio" +# else +# define DEFAULT_CONFIG_DIR "%POLYP_ROOT%" +# endif +#endif + +#ifndef OS_IS_WIN32 +# define PATH_SEP "/" +#else +# define PATH_SEP "\\" +#endif + +#define DEFAULT_CLIENT_CONFIG_FILE DEFAULT_CONFIG_DIR PATH_SEP "client.conf" +#define DEFAULT_CLIENT_CONFIG_FILE_USER ".pulseaudio" PATH_SEP "client.conf" + +#define ENV_CLIENT_CONFIG_FILE "POLYP_CLIENTCONFIG" +#define ENV_DEFAULT_SINK "POLYP_SINK" +#define ENV_DEFAULT_SOURCE "POLYP_SOURCE" +#define ENV_DEFAULT_SERVER "POLYP_SERVER" +#define ENV_DAEMON_BINARY "POLYP_BINARY" +#define ENV_COOKIE_FILE "POLYP_COOKIE" + +static const pa_client_conf default_conf = { + .daemon_binary = NULL, + .extra_arguments = NULL, + .default_sink = NULL, + .default_source = NULL, + .default_server = NULL, + .autospawn = 0, + .cookie_file = NULL, + .cookie_valid = 0 +}; + +pa_client_conf *pa_client_conf_new(void) { + pa_client_conf *c = pa_xmemdup(&default_conf, sizeof(default_conf)); + + c->daemon_binary = pa_xstrdup(POLYPAUDIO_BINARY); + c->extra_arguments = pa_xstrdup("--log-target=syslog --exit-idle-time=5"); + c->cookie_file = pa_xstrdup(PA_NATIVE_COOKIE_FILE); + + return c; +} + +void pa_client_conf_free(pa_client_conf *c) { + assert(c); + pa_xfree(c->daemon_binary); + pa_xfree(c->extra_arguments); + pa_xfree(c->default_sink); + pa_xfree(c->default_source); + pa_xfree(c->default_server); + pa_xfree(c->cookie_file); + pa_xfree(c); +} +int pa_client_conf_load(pa_client_conf *c, const char *filename) { + FILE *f = NULL; + char *fn = NULL; + int r = -1; + + /* Prepare the configuration parse table */ + pa_config_item table[] = { + { "daemon-binary", pa_config_parse_string, NULL }, + { "extra-arguments", pa_config_parse_string, NULL }, + { "default-sink", pa_config_parse_string, NULL }, + { "default-source", pa_config_parse_string, NULL }, + { "default-server", pa_config_parse_string, NULL }, + { "autospawn", pa_config_parse_bool, NULL }, + { "cookie-file", pa_config_parse_string, NULL }, + { NULL, NULL, NULL }, + }; + + table[0].data = &c->daemon_binary; + table[1].data = &c->extra_arguments; + table[2].data = &c->default_sink; + table[3].data = &c->default_source; + table[4].data = &c->default_server; + table[5].data = &c->autospawn; + table[6].data = &c->cookie_file; + + f = filename ? + fopen((fn = pa_xstrdup(filename)), "r") : + pa_open_config_file(DEFAULT_CLIENT_CONFIG_FILE, DEFAULT_CLIENT_CONFIG_FILE_USER, ENV_CLIENT_CONFIG_FILE, &fn, "r"); + + if (!f && errno != EINTR) { + pa_log(__FILE__": WARNING: failed to open configuration file '%s': %s", filename, pa_cstrerror(errno)); + goto finish; + } + + r = f ? pa_config_parse(fn, f, table, NULL) : 0; + + if (!r) + r = pa_client_conf_load_cookie(c); + + +finish: + pa_xfree(fn); + + if (f) + fclose(f); + + return r; +} + +int pa_client_conf_env(pa_client_conf *c) { + char *e; + + if ((e = getenv(ENV_DEFAULT_SINK))) { + pa_xfree(c->default_sink); + c->default_sink = pa_xstrdup(e); + } + + if ((e = getenv(ENV_DEFAULT_SOURCE))) { + pa_xfree(c->default_source); + c->default_source = pa_xstrdup(e); + } + + if ((e = getenv(ENV_DEFAULT_SERVER))) { + pa_xfree(c->default_server); + c->default_server = pa_xstrdup(e); + } + + if ((e = getenv(ENV_DAEMON_BINARY))) { + pa_xfree(c->daemon_binary); + c->daemon_binary = pa_xstrdup(e); + } + + if ((e = getenv(ENV_COOKIE_FILE))) { + pa_xfree(c->cookie_file); + c->cookie_file = pa_xstrdup(e); + + return pa_client_conf_load_cookie(c); + } + + return 0; +} + +int pa_client_conf_load_cookie(pa_client_conf* c) { + assert(c); + + c->cookie_valid = 0; + + if (!c->cookie_file) + return -1; + + if (pa_authkey_load_auto(c->cookie_file, c->cookie, sizeof(c->cookie)) < 0) + return -1; + + c->cookie_valid = 1; + return 0; +} + diff --git a/src/pulse/client-conf.h b/src/pulse/client-conf.h new file mode 100644 index 00000000..9d45af47 --- /dev/null +++ b/src/pulse/client-conf.h @@ -0,0 +1,52 @@ +#ifndef fooclientconfhfoo +#define fooclientconfhfoo + +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + 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 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 + +/* A structure containing configuration data for pulseaudio clients. */ + +typedef struct pa_client_conf { + char *daemon_binary, *extra_arguments, *default_sink, *default_source, *default_server, *cookie_file; + int autospawn; + uint8_t cookie[PA_NATIVE_COOKIE_LENGTH]; + int cookie_valid; /* non-zero, when cookie is valid */ +} pa_client_conf; + +/* Create a new configuration data object and reset it to defaults */ +pa_client_conf *pa_client_conf_new(void); +void pa_client_conf_free(pa_client_conf *c); + +/* Load the configuration data from the speicified file, overwriting + * the current settings in *c. When the filename is NULL, the + * default client configuration file name is used. */ +int pa_client_conf_load(pa_client_conf *c, const char *filename); + +/* Load the configuration data from the environment of the current + process, overwriting the current settings in *c. */ +int pa_client_conf_env(pa_client_conf *c); + +/* Load cookie data from c->cookie_file into c->cookie */ +int pa_client_conf_load_cookie(pa_client_conf* c); + +#endif diff --git a/src/pulse/client.conf.in b/src/pulse/client.conf.in new file mode 100644 index 00000000..d881c44e --- /dev/null +++ b/src/pulse/client.conf.in @@ -0,0 +1,39 @@ +# $Id$ +# +# This file is part of PulseAudio. +# +# 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 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. + +## Configuration file for pulseaudio clients. Default values are +## commented out. Use either ; or # for commenting + +## Path to the pulseaudio daemon to run when autospawning. +; daemon-binary = @POLYPAUDIO_BINARY@ + +## Extra arguments to pass to the pulseaudio daemon +; extra-arguments = --log-target=syslog --exit-idle-time=5 + +## The default sink to connect to +; default-sink = + +## The default source to connect to +; default-source = + +## The default sever to connect to +; default-server = + +## Autospawn daemons? +; autospawn = 0 diff --git a/src/pulse/context.c b/src/pulse/context.c new file mode 100644 index 00000000..648024c3 --- /dev/null +++ b/src/pulse/context.c @@ -0,0 +1,980 @@ +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + 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 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 +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_SYS_WAIT_H +#include +#endif + +#ifdef HAVE_SYS_SOCKET_H +#include +#endif +#ifdef HAVE_NETDB_H +#include +#endif + +#include "../pulsecore/winsock.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "internal.h" + +#include "client-conf.h" + +#ifdef HAVE_X11 +#include "client-conf-x11.h" +#endif + +#include "context.h" + +#define AUTOSPAWN_LOCK "autospawn.lock" + +static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = { + [PA_COMMAND_REQUEST] = pa_command_request, + [PA_COMMAND_OVERFLOW] = pa_command_overflow_or_underflow, + [PA_COMMAND_UNDERFLOW] = pa_command_overflow_or_underflow, + [PA_COMMAND_PLAYBACK_STREAM_KILLED] = pa_command_stream_killed, + [PA_COMMAND_RECORD_STREAM_KILLED] = pa_command_stream_killed, + [PA_COMMAND_SUBSCRIBE_EVENT] = pa_command_subscribe_event +}; + +static void unlock_autospawn_lock_file(pa_context *c) { + assert(c); + + if (c->autospawn_lock_fd >= 0) { + char lf[PATH_MAX]; + pa_runtime_path(AUTOSPAWN_LOCK, lf, sizeof(lf)); + + pa_unlock_lockfile(lf, c->autospawn_lock_fd); + c->autospawn_lock_fd = -1; + } +} + +pa_context *pa_context_new(pa_mainloop_api *mainloop, const char *name) { + pa_context *c; + + assert(mainloop); + assert(name); + + c = pa_xnew(pa_context, 1); + c->ref = 1; + c->name = pa_xstrdup(name); + c->mainloop = mainloop; + c->client = NULL; + c->pstream = NULL; + c->pdispatch = NULL; + c->playback_streams = pa_dynarray_new(); + c->record_streams = pa_dynarray_new(); + + PA_LLIST_HEAD_INIT(pa_stream, c->streams); + PA_LLIST_HEAD_INIT(pa_operation, c->operations); + + c->error = PA_OK; + c->state = PA_CONTEXT_UNCONNECTED; + c->ctag = 0; + c->csyncid = 0; + + c->state_callback = NULL; + c->state_userdata = NULL; + + c->subscribe_callback = NULL; + c->subscribe_userdata = NULL; + + c->memblock_stat = pa_memblock_stat_new(); + c->local = -1; + c->server_list = NULL; + c->server = NULL; + c->autospawn_lock_fd = -1; + memset(&c->spawn_api, 0, sizeof(c->spawn_api)); + c->do_autospawn = 0; + +#ifdef SIGPIPE + pa_check_signal_is_blocked(SIGPIPE); +#endif + + c->conf = pa_client_conf_new(); + pa_client_conf_load(c->conf, NULL); +#ifdef HAVE_X11 + pa_client_conf_from_x11(c->conf, NULL); +#endif + pa_client_conf_env(c->conf); + + return c; +} + +static void context_free(pa_context *c) { + assert(c); + + unlock_autospawn_lock_file(c); + + while (c->operations) + pa_operation_cancel(c->operations); + + while (c->streams) + pa_stream_set_state(c->streams, PA_STREAM_TERMINATED); + + if (c->client) + pa_socket_client_unref(c->client); + if (c->pdispatch) + pa_pdispatch_unref(c->pdispatch); + if (c->pstream) { + pa_pstream_close(c->pstream); + pa_pstream_unref(c->pstream); + } + + if (c->record_streams) + pa_dynarray_free(c->record_streams, NULL, NULL); + if (c->playback_streams) + pa_dynarray_free(c->playback_streams, NULL, NULL); + + pa_memblock_stat_unref(c->memblock_stat); + + if (c->conf) + pa_client_conf_free(c->conf); + + pa_strlist_free(c->server_list); + + pa_xfree(c->name); + pa_xfree(c->server); + pa_xfree(c); +} + +pa_context* pa_context_ref(pa_context *c) { + assert(c); + assert(c->ref >= 1); + + c->ref++; + return c; +} + +void pa_context_unref(pa_context *c) { + assert(c); + assert(c->ref >= 1); + + if (--c->ref <= 0) + context_free(c); +} + +void pa_context_set_state(pa_context *c, pa_context_state_t st) { + assert(c); + assert(c->ref >= 1); + + if (c->state == st) + return; + + pa_context_ref(c); + + c->state = st; + if (c->state_callback) + c->state_callback(c, c->state_userdata); + + if (st == PA_CONTEXT_FAILED || st == PA_CONTEXT_TERMINATED) { + pa_stream *s; + + s = c->streams ? pa_stream_ref(c->streams) : NULL; + while (s) { + pa_stream *n = s->next ? pa_stream_ref(s->next) : NULL; + pa_stream_set_state(s, st == PA_CONTEXT_FAILED ? PA_STREAM_FAILED : PA_STREAM_TERMINATED); + pa_stream_unref(s); + s = n; + } + + if (c->pdispatch) + pa_pdispatch_unref(c->pdispatch); + c->pdispatch = NULL; + + if (c->pstream) { + pa_pstream_close(c->pstream); + pa_pstream_unref(c->pstream); + } + c->pstream = NULL; + + if (c->client) + pa_socket_client_unref(c->client); + c->client = NULL; + } + + pa_context_unref(c); +} + +void pa_context_fail(pa_context *c, int error) { + assert(c); + assert(c->ref >= 1); + + pa_context_set_error(c, error); + pa_context_set_state(c, PA_CONTEXT_FAILED); +} + +int pa_context_set_error(pa_context *c, int error) { + assert(error >= 0); + assert(error < PA_ERR_MAX); + + if (c) + c->error = error; + + return error; +} + +static void pstream_die_callback(pa_pstream *p, void *userdata) { + pa_context *c = userdata; + + assert(p); + assert(c); + + pa_context_fail(c, PA_ERR_CONNECTIONTERMINATED); +} + +static void pstream_packet_callback(pa_pstream *p, pa_packet *packet, const void *creds, void *userdata) { + pa_context *c = userdata; + + assert(p); + assert(packet); + assert(c); + + pa_context_ref(c); + + if (pa_pdispatch_run(c->pdispatch, packet, creds, c) < 0) + pa_context_fail(c, PA_ERR_PROTOCOL); + + pa_context_unref(c); +} + +static void pstream_memblock_callback(pa_pstream *p, uint32_t channel, int64_t offset, pa_seek_mode_t seek, const pa_memchunk *chunk, void *userdata) { + pa_context *c = userdata; + pa_stream *s; + + assert(p); + assert(chunk); + assert(chunk->memblock); + assert(chunk->length); + assert(c); + assert(c->ref >= 1); + + pa_context_ref(c); + + if ((s = pa_dynarray_get(c->record_streams, channel))) { + + assert(seek == PA_SEEK_RELATIVE && offset == 0); + + pa_memblockq_seek(s->record_memblockq, offset, seek); + pa_memblockq_push_align(s->record_memblockq, chunk); + + if (s->read_callback) { + size_t l; + + if ((l = pa_memblockq_get_length(s->record_memblockq)) > 0) + s->read_callback(s, l, s->read_userdata); + } + } + + pa_context_unref(c); +} + +int pa_context_handle_error(pa_context *c, uint32_t command, pa_tagstruct *t) { + assert(c); + assert(c->ref >= 1); + + if (command == PA_COMMAND_ERROR) { + assert(t); + + if (pa_tagstruct_getu32(t, &c->error) < 0) { + pa_context_fail(c, PA_ERR_PROTOCOL); + return -1; + + } + } else if (command == PA_COMMAND_TIMEOUT) + c->error = PA_ERR_TIMEOUT; + else { + pa_context_fail(c, PA_ERR_PROTOCOL); + return -1; + } + + return 0; +} + +static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { + pa_context *c = userdata; + + assert(pd); + assert(c); + assert(c->state == PA_CONTEXT_AUTHORIZING || c->state == PA_CONTEXT_SETTING_NAME); + + pa_context_ref(c); + + if (command != PA_COMMAND_REPLY) { + + if (pa_context_handle_error(c, command, t) < 0) + pa_context_fail(c, PA_ERR_PROTOCOL); + + pa_context_fail(c, c->error); + goto finish; + } + + switch(c->state) { + case PA_CONTEXT_AUTHORIZING: { + pa_tagstruct *reply; + + if (pa_tagstruct_getu32(t, &c->version) < 0 || + !pa_tagstruct_eof(t)) { + pa_context_fail(c, PA_ERR_PROTOCOL); + goto finish; + } + + /* Minimum supported version */ + if (c->version < 8) { + pa_context_fail(c, PA_ERR_VERSION); + goto finish; + } + + reply = pa_tagstruct_command(c, PA_COMMAND_SET_CLIENT_NAME, &tag); + pa_tagstruct_puts(reply, c->name); + pa_pstream_send_tagstruct(c->pstream, reply); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, setup_complete_callback, c, NULL); + + pa_context_set_state(c, PA_CONTEXT_SETTING_NAME); + break; + } + + case PA_CONTEXT_SETTING_NAME : + pa_context_set_state(c, PA_CONTEXT_READY); + break; + + default: + assert(0); + } + +finish: + pa_context_unref(c); +} + +static void setup_context(pa_context *c, pa_iochannel *io) { + pa_tagstruct *t; + uint32_t tag; + + assert(c); + assert(io); + + pa_context_ref(c); + + assert(!c->pstream); + c->pstream = pa_pstream_new(c->mainloop, io, c->memblock_stat); + + pa_pstream_set_die_callback(c->pstream, pstream_die_callback, c); + pa_pstream_set_recieve_packet_callback(c->pstream, pstream_packet_callback, c); + pa_pstream_set_recieve_memblock_callback(c->pstream, pstream_memblock_callback, c); + + assert(!c->pdispatch); + c->pdispatch = pa_pdispatch_new(c->mainloop, command_table, PA_COMMAND_MAX); + + if (!c->conf->cookie_valid) { + pa_context_fail(c, PA_ERR_AUTHKEY); + goto finish; + } + + t = pa_tagstruct_command(c, PA_COMMAND_AUTH, &tag); + pa_tagstruct_putu32(t, PA_PROTOCOL_VERSION); + pa_tagstruct_put_arbitrary(t, c->conf->cookie, sizeof(c->conf->cookie)); + pa_pstream_send_tagstruct_with_creds(c->pstream, t, 1); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, setup_complete_callback, c, NULL); + + pa_context_set_state(c, PA_CONTEXT_AUTHORIZING); + +finish: + + pa_context_unref(c); +} + +static void on_connection(pa_socket_client *client, pa_iochannel*io, void *userdata); + +#ifndef OS_IS_WIN32 + +static int context_connect_spawn(pa_context *c) { + pid_t pid; + int status, r; + int fds[2] = { -1, -1} ; + pa_iochannel *io; + + pa_context_ref(c); + + if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) < 0) { + pa_log(__FILE__": socketpair(): %s", pa_cstrerror(errno)); + pa_context_fail(c, PA_ERR_INTERNAL); + goto fail; + } + + pa_fd_set_cloexec(fds[0], 1); + + pa_socket_low_delay(fds[0]); + pa_socket_low_delay(fds[1]); + + if (c->spawn_api.prefork) + c->spawn_api.prefork(); + + if ((pid = fork()) < 0) { + pa_log(__FILE__": fork(): %s", pa_cstrerror(errno)); + pa_context_fail(c, PA_ERR_INTERNAL); + + if (c->spawn_api.postfork) + c->spawn_api.postfork(); + + goto fail; + } else if (!pid) { + /* Child */ + + char t[128]; + const char *state = NULL; +#define MAX_ARGS 64 + const char * argv[MAX_ARGS+1]; + int n; + + /* Not required, since fds[0] has CLOEXEC enabled anyway */ + close(fds[0]); + + if (c->spawn_api.atfork) + c->spawn_api.atfork(); + + /* Setup argv */ + + n = 0; + + argv[n++] = c->conf->daemon_binary; + argv[n++] = "--daemonize=yes"; + + snprintf(t, sizeof(t), "-Lmodule-native-protocol-fd fd=%i", fds[1]); + argv[n++] = strdup(t); + + while (n < MAX_ARGS) { + char *a; + + if (!(a = pa_split_spaces(c->conf->extra_arguments, &state))) + break; + + argv[n++] = a; + } + + argv[n++] = NULL; + + execv(argv[0], (char * const *) argv); + _exit(1); +#undef MAX_ARGS + } + + /* Parent */ + + r = waitpid(pid, &status, 0); + + if (c->spawn_api.postfork) + c->spawn_api.postfork(); + + if (r < 0) { + pa_log(__FILE__": waitpid(): %s", pa_cstrerror(errno)); + pa_context_fail(c, PA_ERR_INTERNAL); + goto fail; + } else if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { + pa_context_fail(c, PA_ERR_CONNECTIONREFUSED); + goto fail; + } + + close(fds[1]); + + c->local = 1; + + io = pa_iochannel_new(c->mainloop, fds[0], fds[0]); + + setup_context(c, io); + unlock_autospawn_lock_file(c); + + pa_context_unref(c); + + return 0; + +fail: + if (fds[0] != -1) + close(fds[0]); + if (fds[1] != -1) + close(fds[1]); + + unlock_autospawn_lock_file(c); + + pa_context_unref(c); + + return -1; +} + +#endif /* OS_IS_WIN32 */ + +static int try_next_connection(pa_context *c) { + char *u = NULL; + int r = -1; + + assert(c); + assert(!c->client); + + for (;;) { + pa_xfree(u); + u = NULL; + + c->server_list = pa_strlist_pop(c->server_list, &u); + + if (!u) { + +#ifndef OS_IS_WIN32 + if (c->do_autospawn) { + r = context_connect_spawn(c); + goto finish; + } +#endif + + pa_context_fail(c, PA_ERR_CONNECTIONREFUSED); + goto finish; + } + + pa_log_debug(__FILE__": Trying to connect to %s...", u); + + pa_xfree(c->server); + c->server = pa_xstrdup(u); + + if (!(c->client = pa_socket_client_new_string(c->mainloop, u, PA_NATIVE_DEFAULT_PORT))) + continue; + + c->local = pa_socket_client_is_local(c->client); + pa_socket_client_set_callback(c->client, on_connection, c); + break; + } + + r = 0; + +finish: + pa_xfree(u); + + return r; +} + +static void on_connection(pa_socket_client *client, pa_iochannel*io, void *userdata) { + pa_context *c = userdata; + + assert(client); + assert(c); + assert(c->state == PA_CONTEXT_CONNECTING); + + pa_context_ref(c); + + pa_socket_client_unref(client); + c->client = NULL; + + if (!io) { + /* Try the item in the list */ + if (errno == ECONNREFUSED || errno == ETIMEDOUT || errno == EHOSTUNREACH) { + try_next_connection(c); + goto finish; + } + + pa_context_fail(c, PA_ERR_CONNECTIONREFUSED); + goto finish; + } + + unlock_autospawn_lock_file(c); + setup_context(c, io); + +finish: + pa_context_unref(c); +} + +int pa_context_connect( + pa_context *c, + const char *server, + pa_context_flags_t flags, + const pa_spawn_api *api) { + + int r = -1; + + assert(c); + assert(c->ref >= 1); + + PA_CHECK_VALIDITY(c, c->state == PA_CONTEXT_UNCONNECTED, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY(c, !(flags & ~PA_CONTEXT_NOAUTOSPAWN), PA_ERR_INVALID); + PA_CHECK_VALIDITY(c, !server || *server, PA_ERR_INVALID); + + if (!server) + server = c->conf->default_server; + + pa_context_ref(c); + + assert(!c->server_list); + + if (server) { + if (!(c->server_list = pa_strlist_parse(server))) { + pa_context_fail(c, PA_ERR_INVALIDSERVER); + goto finish; + } + } else { + char *d; + char ufn[PATH_MAX]; + + /* Prepend in reverse order */ + + if ((d = getenv("DISPLAY"))) { + char *e; + d = pa_xstrdup(d); + if ((e = strchr(d, ':'))) + *e = 0; + + if (*d) + c->server_list = pa_strlist_prepend(c->server_list, d); + + pa_xfree(d); + } + + c->server_list = pa_strlist_prepend(c->server_list, "tcp6:localhost"); + c->server_list = pa_strlist_prepend(c->server_list, "localhost"); + c->server_list = pa_strlist_prepend(c->server_list, pa_runtime_path(PA_NATIVE_DEFAULT_UNIX_SOCKET, ufn, sizeof(ufn))); + + /* Wrap the connection attempts in a single transaction for sane autospawn locking */ + if (!(flags & PA_CONTEXT_NOAUTOSPAWN) && c->conf->autospawn) { + char lf[PATH_MAX]; + + pa_runtime_path(AUTOSPAWN_LOCK, lf, sizeof(lf)); + pa_make_secure_parent_dir(lf); + assert(c->autospawn_lock_fd <= 0); + c->autospawn_lock_fd = pa_lock_lockfile(lf); + + if (api) + c->spawn_api = *api; + c->do_autospawn = 1; + } + + } + + pa_context_set_state(c, PA_CONTEXT_CONNECTING); + r = try_next_connection(c); + +finish: + pa_context_unref(c); + + return r; +} + +void pa_context_disconnect(pa_context *c) { + assert(c); + assert(c->ref >= 1); + + pa_context_set_state(c, PA_CONTEXT_TERMINATED); +} + +pa_context_state_t pa_context_get_state(pa_context *c) { + assert(c); + assert(c->ref >= 1); + + return c->state; +} + +int pa_context_errno(pa_context *c) { + assert(c); + assert(c->ref >= 1); + + return c->error; +} + +void pa_context_set_state_callback(pa_context *c, pa_context_notify_cb_t cb, void *userdata) { + assert(c); + assert(c->ref >= 1); + + c->state_callback = cb; + c->state_userdata = userdata; +} + +int pa_context_is_pending(pa_context *c) { + assert(c); + assert(c->ref >= 1); + + PA_CHECK_VALIDITY(c, + c->state == PA_CONTEXT_CONNECTING || + c->state == PA_CONTEXT_AUTHORIZING || + c->state == PA_CONTEXT_SETTING_NAME || + c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + + return (c->pstream && pa_pstream_is_pending(c->pstream)) || + (c->pdispatch && pa_pdispatch_is_pending(c->pdispatch)) || + c->client; +} + +static void set_dispatch_callbacks(pa_operation *o); + +static void pdispatch_drain_callback(PA_GCC_UNUSED pa_pdispatch*pd, void *userdata) { + set_dispatch_callbacks(userdata); +} + +static void pstream_drain_callback(PA_GCC_UNUSED pa_pstream *s, void *userdata) { + set_dispatch_callbacks(userdata); +} + +static void set_dispatch_callbacks(pa_operation *o) { + int done = 1; + + assert(o); + assert(o->ref >= 1); + assert(o->context); + assert(o->context->ref >= 1); + assert(o->context->state == PA_CONTEXT_READY); + + pa_pstream_set_drain_callback(o->context->pstream, NULL, NULL); + pa_pdispatch_set_drain_callback(o->context->pdispatch, NULL, NULL); + + if (pa_pdispatch_is_pending(o->context->pdispatch)) { + pa_pdispatch_set_drain_callback(o->context->pdispatch, pdispatch_drain_callback, o); + done = 0; + } + + if (pa_pstream_is_pending(o->context->pstream)) { + pa_pstream_set_drain_callback(o->context->pstream, pstream_drain_callback, o); + done = 0; + } + + if (done) { + if (o->callback) { + pa_context_notify_cb_t cb = (pa_context_notify_cb_t) o->callback; + cb(o->context, o->userdata); + } + + pa_operation_done(o); + pa_operation_unref(o); + } +} + +pa_operation* pa_context_drain(pa_context *c, pa_context_notify_cb_t cb, void *userdata) { + pa_operation *o; + + assert(c); + assert(c->ref >= 1); + + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(c, pa_context_is_pending(c), PA_ERR_BADSTATE); + + o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); + set_dispatch_callbacks(pa_operation_ref(o)); + + return o; +} + +void pa_context_simple_ack_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { + pa_operation *o = userdata; + int success = 1; + + assert(pd); + assert(o); + assert(o->ref >= 1); + + if (!o->context) + goto finish; + + if (command != PA_COMMAND_REPLY) { + if (pa_context_handle_error(o->context, command, t) < 0) + goto finish; + + success = 0; + } else if (!pa_tagstruct_eof(t)) { + pa_context_fail(o->context, PA_ERR_PROTOCOL); + goto finish; + } + + if (o->callback) { + pa_context_success_cb_t cb = (pa_context_success_cb_t) o->callback; + cb(o->context, success, o->userdata); + } + +finish: + pa_operation_done(o); + pa_operation_unref(o); +} + +pa_operation* pa_context_exit_daemon(pa_context *c, pa_context_success_cb_t cb, void *userdata) { + pa_tagstruct *t; + pa_operation *o; + uint32_t tag; + + assert(c); + assert(c->ref >= 1); + + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + + o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); + + t = pa_tagstruct_command(c, PA_COMMAND_EXIT, &tag); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); + + return o; +} + +pa_operation* pa_context_send_simple_command(pa_context *c, uint32_t command, pa_pdispatch_cb_t internal_cb, pa_operation_cb_t cb, void *userdata) { + pa_tagstruct *t; + pa_operation *o; + uint32_t tag; + + assert(c); + assert(c->ref >= 1); + + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + + o = pa_operation_new(c, NULL, cb, userdata); + + t = pa_tagstruct_command(c, command, &tag); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, internal_cb, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); + + return o; +} + +pa_operation* pa_context_set_default_sink(pa_context *c, const char *name, pa_context_success_cb_t cb, void *userdata) { + pa_tagstruct *t; + pa_operation *o; + uint32_t tag; + + assert(c); + assert(c->ref >= 1); + + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + + o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); + + t = pa_tagstruct_command(c, PA_COMMAND_SET_DEFAULT_SINK, &tag); + pa_tagstruct_puts(t, name); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); + + return o; +} + +pa_operation* pa_context_set_default_source(pa_context *c, const char *name, pa_context_success_cb_t cb, void *userdata) { + pa_tagstruct *t; + pa_operation *o; + uint32_t tag; + + assert(c); + assert(c->ref >= 1); + + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + + o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); + + t = pa_tagstruct_command(c, PA_COMMAND_SET_DEFAULT_SOURCE, &tag); + pa_tagstruct_puts(t, name); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); + + return o; +} + +int pa_context_is_local(pa_context *c) { + assert(c); + + return c->local; +} + +pa_operation* pa_context_set_name(pa_context *c, const char *name, pa_context_success_cb_t cb, void *userdata) { + pa_tagstruct *t; + pa_operation *o; + uint32_t tag; + + assert(c); + assert(c->ref >= 1); + assert(name); + + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + + o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); + + t = pa_tagstruct_command(c, PA_COMMAND_SET_CLIENT_NAME, &tag); + pa_tagstruct_puts(t, name); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); + + return o; +} + +const char* pa_get_library_version(void) { + return PACKAGE_VERSION; +} + +const char* pa_context_get_server(pa_context *c) { + assert(c); + assert(c->ref >= 1); + + if (!c->server) + return NULL; + + if (*c->server == '{') { + char *e = strchr(c->server+1, '}'); + return e ? e+1 : c->server; + } + + return c->server; +} + +uint32_t pa_context_get_protocol_version(PA_GCC_UNUSED pa_context *c) { + return PA_PROTOCOL_VERSION; +} + +uint32_t pa_context_get_server_protocol_version(pa_context *c) { + assert(c); + assert(c->ref >= 1); + + return c->version; +} + +pa_tagstruct *pa_tagstruct_command(pa_context *c, uint32_t command, uint32_t *tag) { + pa_tagstruct *t; + + assert(c); + assert(tag); + + t = pa_tagstruct_new(NULL, 0); + pa_tagstruct_putu32(t, command); + pa_tagstruct_putu32(t, *tag = c->ctag++); + + return t; +} diff --git a/src/pulse/context.h b/src/pulse/context.h new file mode 100644 index 00000000..65d70e8b --- /dev/null +++ b/src/pulse/context.h @@ -0,0 +1,230 @@ +#ifndef foocontexthfoo +#define foocontexthfoo + +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + 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 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 +#include +#include +#include +#include + +/** \page async Asynchronous API + * + * \section overv_sec Overview + * + * The asynchronous API is the native interface to the pulseaudio library. + * It allows full access to all available functions. This also means that + * it is rather complex and can take some time to fully master. + * + * \section mainloop_sec Main Loop Abstraction + * + * The API is based around an asynchronous event loop, or main loop, + * abstraction. This abstraction contains three basic elements: + * + * \li Deferred events - Events that will trigger as soon as possible. Note + * that some implementations may block all other events + * when a deferred event is active. + * \li I/O events - Events that trigger on file descriptor activities. + * \li Times events - Events that trigger after a fixed ammount of time. + * + * The abstraction is represented as a number of function pointers in the + * pa_mainloop_api structure. + * + * To actually be able to use these functions, an implementation needs to + * be coupled to the abstraction. There are three of these shipped with + * pulseaudio, but any other can be used with a minimal ammount of work, + * provided it supports the three basic events listed above. + * + * The implementations shipped with pulseaudio are: + * + * \li \subpage mainloop - A minimal but fast implementation based on poll(). + * \li \subpage threaded_mainloop - A special version of the previous + * implementation where all of Polypaudio's + * internal handling runs in a separate + * thread. + * \li \subpage glib-mainloop - A wrapper around GLIB's main loop. Available + * for both GLIB 1.2 and GLIB 2.x. + * + * UNIX signals may be hooked to a main loop using the functions from + * \ref mainloop-signal.h. These rely only on the main loop abstraction + * and can therefore be used with any of the implementations. + * + * \section refcnt_sec Reference Counting + * + * Almost all objects in pulseaudio are reference counted. What that means + * is that you rarely malloc() or free() any objects. Instead you increase + * and decrease their reference counts. Whenever an object's reference + * count reaches zero, that object gets destroy and any resources it uses + * get freed. + * + * The benefit of this design is that an application need not worry about + * whether or not it needs to keep an object around in case the library is + * using it internally. If it is, then it has made sure it has its own + * reference to it. + * + * Whenever the library creates an object, it will have an initial + * reference count of one. Most of the time, this single reference will be + * sufficient for the application, so all required reference count + * interaction will be a single call to the objects unref function. + * + * \section context_sec Context + * + * A context is the basic object for a connection to a pulseaudio server. + * It multiplexes commands, data streams and events through a single + * channel. + * + * There is no need for more than one context per application, unless + * connections to multiple servers are needed. + * + * \subsection ops_subsec Operations + * + * All operations on the context are performed asynchronously. I.e. the + * client will not wait for the server to complete the request. To keep + * track of all these in-flight operations, the application is given a + * pa_operation object for each asynchronous operation. + * + * There are only two actions (besides reference counting) that can be + * performed on a pa_operation: querying its state with + * pa_operation_get_state() and aborting it with pa_operation_cancel(). + * + * A pa_operation object is reference counted, so an application must + * make sure to unreference it, even if it has no intention of using it. + * + * \subsection conn_subsec Connecting + * + * A context must be connected to a server before any operation can be + * issued. Calling pa_context_connect() will initiate the connection + * procedure. Unlike most asynchronous operations, connecting does not + * result in a pa_operation object. Instead, the application should + * register a callback using pa_context_set_state_callback(). + * + * \subsection disc_subsec Disconnecting + * + * When the sound support is no longer needed, the connection needs to be + * closed using pa_context_disconnect(). This is an immediate function that + * works synchronously. + * + * Since the context object has references to other objects it must be + * disconnected after use or there is a high risk of memory leaks. If the + * connection has terminated by itself, then there is no need to explicitly + * disconnect the context using pa_context_disconnect(). + * + * \section Functions + * + * The sound server's functionality can be divided into a number of + * subsections: + * + * \li \subpage streams + * \li \subpage scache + * \li \subpage introspect + * \li \subpage subscribe + */ + +/** \file + * Connection contexts for asynchrononous communication with a + * server. A pa_context object wraps a connection to a pulseaudio + * server using its native protocol. */ + +/** \example pacat.c + * A playback and recording tool using the asynchronous API */ + +/** \example paplay.c + * A sound file playback tool using the asynchronous API, based on libsndfile */ + +PA_C_DECL_BEGIN + +/** An opaque connection context to a daemon */ +typedef struct pa_context pa_context; + +/** Generic notification callback prototype */ +typedef void (*pa_context_notify_cb_t)(pa_context *c, void *userdata); + +/** A generic callback for operation completion */ +typedef void (*pa_context_success_cb_t) (pa_context *c, int success, void *userdata); + +/** Instantiate a new connection context with an abstract mainloop API + * and an application name */ +pa_context *pa_context_new(pa_mainloop_api *mainloop, const char *name); + +/** Decrease the reference counter of the context by one */ +void pa_context_unref(pa_context *c); + +/** Increase the reference counter of the context by one */ +pa_context* pa_context_ref(pa_context *c); + +/** Set a callback function that is called whenever the context status changes */ +void pa_context_set_state_callback(pa_context *c, pa_context_notify_cb_t cb, void *userdata); + +/** Return the error number of the last failed operation */ +int pa_context_errno(pa_context *c); + +/** Return non-zero if some data is pending to be written to the connection */ +int pa_context_is_pending(pa_context *c); + +/** Return the current context status */ +pa_context_state_t pa_context_get_state(pa_context *c); + +/** Connect the context to the specified server. If server is NULL, +connect to the default server. This routine may but will not always +return synchronously on error. Use pa_context_set_state_callback() to +be notified when the connection is established. If flags doesn't have +PA_NOAUTOSPAWN set and no specific server is specified or accessible a +new daemon is spawned. If api is non-NULL, the functions specified in +the structure are used when forking a new child process. */ +int pa_context_connect(pa_context *c, const char *server, pa_context_flags_t flags, const pa_spawn_api *api); + +/** Terminate the context connection immediately */ +void pa_context_disconnect(pa_context *c); + +/** Drain the context. If there is nothing to drain, the function returns NULL */ +pa_operation* pa_context_drain(pa_context *c, pa_context_notify_cb_t cb, void *userdata); + +/** Tell the daemon to exit. The returned operation is unlikely to + * complete succesfully, since the daemon probably died before + * returning a success notification */ +pa_operation* pa_context_exit_daemon(pa_context *c, pa_context_success_cb_t cb, void *userdata); + +/** Set the name of the default sink. \since 0.4 */ +pa_operation* pa_context_set_default_sink(pa_context *c, const char *name, pa_context_success_cb_t cb, void *userdata); + +/** Set the name of the default source. \since 0.4 */ +pa_operation* pa_context_set_default_source(pa_context *c, const char *name, pa_context_success_cb_t cb, void *userdata); + +/** Returns 1 when the connection is to a local daemon. Returns negative when no connection has been made yet. \since 0.5 */ +int pa_context_is_local(pa_context *c); + +/** Set a different application name for context on the server. \since 0.5 */ +pa_operation* pa_context_set_name(pa_context *c, const char *name, pa_context_success_cb_t cb, void *userdata); + +/** Return the server name this context is connected to. \since 0.7 */ +const char* pa_context_get_server(pa_context *c); + +/** Return the protocol version of the library. \since 0.8 */ +uint32_t pa_context_get_protocol_version(pa_context *c); + +/** Return the protocol version of the connected server. \since 0.8 */ +uint32_t pa_context_get_server_protocol_version(pa_context *c); + +PA_C_DECL_END + +#endif diff --git a/src/pulse/def.h b/src/pulse/def.h new file mode 100644 index 00000000..3a17f43b --- /dev/null +++ b/src/pulse/def.h @@ -0,0 +1,312 @@ +#ifndef foodefhfoo +#define foodefhfoo + +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + 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 +#include +#include + +#include +#include + +/** \file + * Global definitions */ + +PA_C_DECL_BEGIN + +/** The state of a connection context */ +typedef enum pa_context_state { + PA_CONTEXT_UNCONNECTED, /**< The context hasn't been connected yet */ + PA_CONTEXT_CONNECTING, /**< A connection is being established */ + PA_CONTEXT_AUTHORIZING, /**< The client is authorizing itself to the daemon */ + PA_CONTEXT_SETTING_NAME, /**< The client is passing its application name to the daemon */ + PA_CONTEXT_READY, /**< The connection is established, the context is ready to execute operations */ + PA_CONTEXT_FAILED, /**< The connection failed or was disconnected */ + PA_CONTEXT_TERMINATED /**< The connection was terminated cleanly */ +} pa_context_state_t; + +/** The state of a stream */ +typedef enum pa_stream_state { + PA_STREAM_UNCONNECTED, /**< The stream is not yet connected to any sink or source */ + PA_STREAM_CREATING, /**< The stream is being created */ + PA_STREAM_READY, /**< The stream is established, you may pass audio data to it now */ + PA_STREAM_FAILED, /**< An error occured that made the stream invalid */ + PA_STREAM_TERMINATED /**< The stream has been terminated cleanly */ +} pa_stream_state_t; + +/** The state of an operation */ +typedef enum pa_operation_state { + PA_OPERATION_RUNNING, /**< The operation is still running */ + PA_OPERATION_DONE, /**< The operation has been completed */ + PA_OPERATION_CANCELED /**< The operation has been canceled */ +} pa_operation_state_t; + +/** An invalid index */ +#define PA_INVALID_INDEX ((uint32_t) -1) + +/** Some special flags for contexts. \since 0.8 */ +typedef enum pa_context_flags { + PA_CONTEXT_NOAUTOSPAWN = 1 /**< Disabled autospawning of the pulseaudio daemon if required */ +} pa_context_flags_t; + +/** The direction of a pa_stream object */ +typedef enum pa_stream_direction { + PA_STREAM_NODIRECTION, /**< Invalid direction */ + PA_STREAM_PLAYBACK, /**< Playback stream */ + PA_STREAM_RECORD, /**< Record stream */ + PA_STREAM_UPLOAD /**< Sample upload stream */ +} pa_stream_direction_t; + +/** Some special flags for stream connections. \since 0.6 */ +typedef enum pa_stream_flags { + PA_STREAM_START_CORKED = 1, /**< Create the stream corked, requiring an explicit pa_stream_cork() call to uncork it. */ + PA_STREAM_INTERPOLATE_TIMING = 2, /**< Interpolate the latency for + * this stream. When enabled, + * pa_stream_get_latency() and + * pa_stream_get_time() will try + * to estimate the current + * record/playback time based on + * the local time that passed + * since the last timing info + * update. Using this option + * has the advantage of not + * requiring a whole roundtrip + * when the current + * playback/recording time is + * needed. Consider using this + * option when requesting + * latency information + * frequently. This is + * especially useful on long + * latency network + * connections. It makes a lot + * of sense to combine this + * option with + * PA_STREAM_AUTO_TIMING_UPDATE. */ + PA_STREAM_NOT_MONOTONOUS = 4, /**< Don't force the time to + * increase monotonically. If + * this option is enabled, + * pa_stream_get_time() will not + * necessarily return always + * monotonically increasing time + * values on each call. This may + * confuse applications which + * cannot deal with time going + * 'backwards', but has the + * advantage that bad transport + * latency estimations that + * caused the time to to jump + * ahead can be corrected + * quickly, without the need to + * wait. */ + PA_STREAM_AUTO_TIMING_UPDATE = 8 /**< If set timing update requests + * are issued periodically + * automatically. Combined with + * PA_STREAM_INTERPOLATE_TIMING + * you will be able to query the + * current time and latency with + * pa_stream_get_time() and + * pa_stream_get_latency() at + * all times without a packet + * round trip.*/ +} pa_stream_flags_t; + +/** Playback and record buffer metrics */ +typedef struct pa_buffer_attr { + uint32_t maxlength; /**< Maximum length of the buffer */ + uint32_t tlength; /**< Playback only: target length of the buffer. The server tries to assure that at least tlength bytes are always available in the buffer */ + uint32_t prebuf; /**< Playback only: pre-buffering. The server does not start with playback before at least prebug bytes are available in the buffer */ + uint32_t minreq; /**< Playback only: minimum request. The server does not request less than minreq bytes from the client, instead waints until the buffer is free enough to request more bytes at once */ + uint32_t fragsize; /**< Recording only: fragment size. The server sends data in blocks of fragsize bytes size. Large values deminish interactivity with other operations on the connection context but decrease control overhead. */ +} pa_buffer_attr; + +/** Error values as used by pa_context_errno(). Use pa_strerror() to convert these values to human readable strings */ +enum { + PA_OK = 0, /**< No error */ + PA_ERR_ACCESS, /**< Access failure */ + PA_ERR_COMMAND, /**< Unknown command */ + PA_ERR_INVALID, /**< Invalid argument */ + PA_ERR_EXIST, /**< Entity exists */ + PA_ERR_NOENTITY, /**< No such entity */ + PA_ERR_CONNECTIONREFUSED, /**< Connection refused */ + PA_ERR_PROTOCOL, /**< Protocol error */ + PA_ERR_TIMEOUT, /**< Timeout */ + PA_ERR_AUTHKEY, /**< No authorization key */ + PA_ERR_INTERNAL, /**< Internal error */ + PA_ERR_CONNECTIONTERMINATED, /**< Connection terminated */ + PA_ERR_KILLED, /**< Entity killed */ + PA_ERR_INVALIDSERVER, /**< Invalid server */ + PA_ERR_MODINITFAILED, /**< Module initialization failed */ + PA_ERR_BADSTATE, /**< Bad state */ + PA_ERR_NODATA, /**< No data */ + PA_ERR_VERSION, /**< Incompatible protocol version \since 0.8 */ + PA_ERR_TOOLARGE, /**< Data too large \since 0.8.1 */ + PA_ERR_MAX /**< Not really an error but the first invalid error code */ +}; + +/** Subscription event mask, as used by pa_context_subscribe() */ +typedef enum pa_subscription_mask { + PA_SUBSCRIPTION_MASK_NULL = 0, /**< No events */ + PA_SUBSCRIPTION_MASK_SINK = 1, /**< Sink events */ + PA_SUBSCRIPTION_MASK_SOURCE = 2, /**< Source events */ + PA_SUBSCRIPTION_MASK_SINK_INPUT = 4, /**< Sink input events */ + PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT = 8, /**< Source output events */ + PA_SUBSCRIPTION_MASK_MODULE = 16, /**< Module events */ + PA_SUBSCRIPTION_MASK_CLIENT = 32, /**< Client events */ + PA_SUBSCRIPTION_MASK_SAMPLE_CACHE = 64, /**< Sample cache events */ + PA_SUBSCRIPTION_MASK_SERVER = 128, /**< Other global server changes. \since 0.4 */ + PA_SUBSCRIPTION_MASK_AUTOLOAD = 256, /**< Autoload table events. \since 0.5 */ + PA_SUBSCRIPTION_MASK_ALL = 511 /**< Catch all events \since 0.8 */ +} pa_subscription_mask_t; + +/** Subscription event types, as used by pa_context_subscribe() */ +typedef enum pa_subscription_event_type { + PA_SUBSCRIPTION_EVENT_SINK = 0, /**< Event type: Sink */ + PA_SUBSCRIPTION_EVENT_SOURCE = 1, /**< Event type: Source */ + PA_SUBSCRIPTION_EVENT_SINK_INPUT = 2, /**< Event type: Sink input */ + PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT = 3, /**< Event type: Source output */ + PA_SUBSCRIPTION_EVENT_MODULE = 4, /**< Event type: Module */ + PA_SUBSCRIPTION_EVENT_CLIENT = 5, /**< Event type: Client */ + PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE = 6, /**< Event type: Sample cache item */ + PA_SUBSCRIPTION_EVENT_SERVER = 7, /**< Event type: Global server change, only occuring with PA_SUBSCRIPTION_EVENT_CHANGE. \since 0.4 */ + PA_SUBSCRIPTION_EVENT_AUTOLOAD = 8, /**< Event type: Autoload table changes. \since 0.5 */ + PA_SUBSCRIPTION_EVENT_FACILITY_MASK = 15, /**< A mask to extract the event type from an event value */ + + PA_SUBSCRIPTION_EVENT_NEW = 0, /**< A new object was created */ + PA_SUBSCRIPTION_EVENT_CHANGE = 16, /**< A property of the object was modified */ + PA_SUBSCRIPTION_EVENT_REMOVE = 32, /**< An object was removed */ + PA_SUBSCRIPTION_EVENT_TYPE_MASK = 16+32 /**< A mask to extract the event operation from an event value */ +} pa_subscription_event_type_t; + +/** Return one if an event type t matches an event mask bitfield */ +#define pa_subscription_match_flags(m, t) (!!((m) & (1 << ((t) & PA_SUBSCRIPTION_EVENT_FACILITY_MASK)))) + +/** A structure for all kinds of timing information of a stream. See + * pa_stream_update_timing_info() and pa_stream_get_timing_info(). The + * total output latency a sample that is written with + * pa_stream_write() takes to be played may be estimated by + * sink_usec+buffer_usec+transport_usec. (where buffer_usec is defined + * as pa_bytes_to_usec(write_index-read_index)) The output buffer + * which buffer_usec relates to may be manipulated freely (with + * pa_stream_write()'s seek argument, pa_stream_flush() and friends), + * the buffers sink_usec and source_usec relate to are first-in + * first-out (FIFO) buffers which cannot be flushed or manipulated in + * any way. The total input latency a sample that is recorded takes to + * be delivered to the application is: + * source_usec+buffer_usec+transport_usec-sink_usec. (Take care of + * sign issues!) When connected to a monitor source sink_usec contains + * the latency of the owning sink. The two latency estimations + * described here are implemented in pa_stream_get_latency().*/ +typedef struct pa_timing_info { + struct timeval timestamp; /**< The time when this timing info structure was current */ + int synchronized_clocks; /**< Non-zero if the local and the + * remote machine have synchronized + * clocks. If synchronized clocks are + * detected transport_usec becomes much + * more reliable. However, the code that + * detects synchronized clocks is very + * limited und unreliable itself. \since + * 0.5 */ + + pa_usec_t sink_usec; /**< Time in usecs a sample takes to be played on the sink. For playback streams and record streams connected to a monitor source. */ + pa_usec_t source_usec; /**< Time in usecs a sample takes from being recorded to being delivered to the application. Only for record streams. \since 0.5*/ + pa_usec_t transport_usec; /**< Estimated time in usecs a sample takes to be transferred to/from the daemon. For both playback and record streams. \since 0.5 */ + + int playing; /**< Non-zero when the stream is currently playing. Only for playback streams. */ + + int write_index_corrupt; /**< Non-zero if write_index is not + * up-to-date because a local write + * command that corrupted it has been + * issued in the time since this latency + * info was current . Only write + * commands with SEEK_RELATIVE_ON_READ + * and SEEK_RELATIVE_END can corrupt + * write_index. \since 0.8 */ + int64_t write_index; /**< Current write index into the + * playback buffer in bytes. Think twice before + * using this for seeking purposes: it + * might be out of date a the time you + * want to use it. Consider using + * PA_SEEK_RELATIVE instead. \since + * 0.8 */ + + int read_index_corrupt; /**< Non-zero if read_index is not + * up-to-date because a local pause or + * flush request that corrupted it has + * been issued in the time since this + * latency info was current. \since 0.8 */ + + int64_t read_index; /**< Current read index into the + * playback buffer in bytes. Think twice before + * using this for seeking purposes: it + * might be out of date a the time you + * want to use it. Consider using + * PA_SEEK_RELATIVE_ON_READ + * instead. \since 0.8 */ +} pa_timing_info; + +/** A structure for the spawn api. This may be used to integrate auto + * spawned daemons into your application. For more information see + * pa_context_connect(). When spawning a new child process the + * waitpid() is used on the child's PID. The spawn routine will not + * block or ignore SIGCHLD signals, since this cannot be done in a + * thread compatible way. You might have to do this in + * prefork/postfork. \since 0.4 */ +typedef struct pa_spawn_api { + void (*prefork)(void); /**< Is called just before the fork in the parent process. May be NULL. */ + void (*postfork)(void); /**< Is called immediately after the fork in the parent process. May be NULL.*/ + void (*atfork)(void); /**< Is called immediately after the + * fork in the child process. May be + * NULL. It is not safe to close all + * file descriptors in this function + * unconditionally, since a UNIX socket + * (created using socketpair()) is + * passed to the new process. */ +} pa_spawn_api; + +/** Seek type for pa_stream_write(). \since 0.8*/ +typedef enum pa_seek_mode { + PA_SEEK_RELATIVE = 0, /**< Seek relatively to the write index */ + PA_SEEK_ABSOLUTE = 1, /**< Seek relatively to the start of the buffer queue */ + PA_SEEK_RELATIVE_ON_READ = 2, /**< Seek relatively to the read index. */ + PA_SEEK_RELATIVE_END = 3 /**< Seek relatively to the current end of the buffer queue. */ +} pa_seek_mode_t; + +/** Special sink flags. \since 0.8 */ +typedef enum pa_sink_flags { + PA_SINK_HW_VOLUME_CTRL = 1, /**< Supports hardware volume control */ + PA_SINK_LATENCY = 2 /**< Supports latency querying */ +} pa_sink_flags_t; + +/** Special source flags. \since 0.8 */ +typedef enum pa_source_flags { + PA_SOURCE_HW_VOLUME_CTRL = 1, /**< Supports hardware volume control */ + PA_SOURCE_LATENCY = 2 /**< Supports latency querying */ +} pa_source_flags_t; + +/** A generic free() like callback prototype */ +typedef void (*pa_free_cb_t)(void *p); + +PA_C_DECL_END + +#endif diff --git a/src/pulse/error.c b/src/pulse/error.c new file mode 100644 index 00000000..7bd31ead --- /dev/null +++ b/src/pulse/error.c @@ -0,0 +1,66 @@ +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + 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 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 +#endif + +#include +#include +#include +#include + +#include + +#include +#include + +#include "error.h" + +const char*pa_strerror(int error) { + + static const char* const errortab[PA_ERR_MAX] = { + [PA_OK] = "OK", + [PA_ERR_ACCESS] = "Access denied", + [PA_ERR_COMMAND] = "Unknown command", + [PA_ERR_INVALID] = "Invalid argument", + [PA_ERR_EXIST] = "Entity exists", + [PA_ERR_NOENTITY] = "No such entity", + [PA_ERR_CONNECTIONREFUSED] = "Connection refused", + [PA_ERR_PROTOCOL] = "Protocol error", + [PA_ERR_TIMEOUT] = "Timeout", + [PA_ERR_AUTHKEY] = "No authorization key", + [PA_ERR_INTERNAL] = "Internal error", + [PA_ERR_CONNECTIONTERMINATED] = "Connection terminated", + [PA_ERR_KILLED] = "Entity killed", + [PA_ERR_INVALIDSERVER] = "Invalid server", + [PA_ERR_MODINITFAILED] = "Module initalization failed", + [PA_ERR_BADSTATE] = "Bad state", + [PA_ERR_NODATA] = "No data", + [PA_ERR_VERSION] = "Incompatible protocol version", + [PA_ERR_TOOLARGE] = "Too large" + }; + + if (error < 0 || error >= PA_ERR_MAX) + return NULL; + + return errortab[error]; +} diff --git a/src/pulse/error.h b/src/pulse/error.h new file mode 100644 index 00000000..bfce023c --- /dev/null +++ b/src/pulse/error.h @@ -0,0 +1,38 @@ +#ifndef fooerrorhfoo +#define fooerrorhfoo + +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + 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 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 +#include + +/** \file + * Error management */ + +PA_C_DECL_BEGIN + +/** Return a human readable error message for the specified numeric error code */ +const char* pa_strerror(int error); + +PA_C_DECL_END + +#endif diff --git a/src/pulse/glib-mainloop.c b/src/pulse/glib-mainloop.c new file mode 100644 index 00000000..25848ece --- /dev/null +++ b/src/pulse/glib-mainloop.c @@ -0,0 +1,541 @@ +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + 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 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 +#endif + +#include + +#include +#include + +#include +#include + +#include "glib.h" +#include "glib-mainloop.h" + +struct pa_io_event { + pa_glib_mainloop *mainloop; + int dead; + GIOChannel *io_channel; + GSource *source; + GIOCondition io_condition; + int fd; + void (*callback) (pa_mainloop_api*m, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata); + void *userdata; + void (*destroy_callback) (pa_mainloop_api *m, pa_io_event *e, void *userdata); + pa_io_event *next, *prev; +}; + +struct pa_time_event { + pa_glib_mainloop *mainloop; + int dead; + GSource *source; + struct timeval timeval; + void (*callback) (pa_mainloop_api*m, pa_time_event *e, const struct timeval *tv, void *userdata); + void *userdata; + void (*destroy_callback) (pa_mainloop_api *m, pa_time_event*e, void *userdata); + pa_time_event *next, *prev; +}; + +struct pa_defer_event { + pa_glib_mainloop *mainloop; + int dead; + GSource *source; + void (*callback) (pa_mainloop_api*m, pa_defer_event *e, void *userdata); + void *userdata; + void (*destroy_callback) (pa_mainloop_api *m, pa_defer_event*e, void *userdata); + pa_defer_event *next, *prev; +}; + +struct pa_glib_mainloop { + GMainContext *glib_main_context; + pa_mainloop_api api; + GSource *cleanup_source; + pa_io_event *io_events, *dead_io_events; + pa_time_event *time_events, *dead_time_events; + pa_defer_event *defer_events, *dead_defer_events; +}; + +static void schedule_free_dead_events(pa_glib_mainloop *g); + +static void glib_io_enable(pa_io_event*e, pa_io_event_flags_t f); + +static pa_io_event* glib_io_new(pa_mainloop_api*m, int fd, pa_io_event_flags_t f, void (*callback) (pa_mainloop_api*m, pa_io_event*e, int fd, pa_io_event_flags_t f, void *userdata), void *userdata) { + pa_io_event *e; + pa_glib_mainloop *g; + + assert(m && m->userdata && fd >= 0 && callback); + g = m->userdata; + + e = pa_xmalloc(sizeof(pa_io_event)); + e->mainloop = m->userdata; + e->dead = 0; + e->fd = fd; + e->callback = callback; + e->userdata = userdata; + e->destroy_callback = NULL; + + e->io_channel = g_io_channel_unix_new(e->fd); + assert(e->io_channel); + e->source = NULL; + e->io_condition = 0; + + glib_io_enable(e, f); + + e->next = g->io_events; + if (e->next) e->next->prev = e; + g->io_events = e; + e->prev = NULL; + + return e; +} + +/* The callback GLIB calls whenever an IO condition is met */ +static gboolean io_cb(GIOChannel *source, GIOCondition condition, gpointer data) { + pa_io_event *e = data; + pa_io_event_flags_t f; + assert(source && e && e->io_channel == source); + + f = (condition & G_IO_IN ? PA_IO_EVENT_INPUT : 0) | + (condition & G_IO_OUT ? PA_IO_EVENT_OUTPUT : 0) | + (condition & G_IO_ERR ? PA_IO_EVENT_ERROR : 0) | + (condition & G_IO_HUP ? PA_IO_EVENT_HANGUP : 0); + + e->callback(&e->mainloop->api, e, e->fd, f, e->userdata); + return TRUE; +} + +static void glib_io_enable(pa_io_event*e, pa_io_event_flags_t f) { + GIOCondition c; + assert(e && !e->dead); + + c = (f & PA_IO_EVENT_INPUT ? G_IO_IN : 0) | (f & PA_IO_EVENT_OUTPUT ? G_IO_OUT : 0); + + if (c == e->io_condition) + return; + + if (e->source) { + g_source_destroy(e->source); + g_source_unref(e->source); + } + + e->source = g_io_create_watch(e->io_channel, c | G_IO_ERR | G_IO_HUP); + assert(e->source); + + g_source_set_callback(e->source, (GSourceFunc) io_cb, e, NULL); + g_source_attach(e->source, e->mainloop->glib_main_context); + g_source_set_priority(e->source, G_PRIORITY_DEFAULT); + + e->io_condition = c; +} + +static void glib_io_free(pa_io_event*e) { + assert(e && !e->dead); + + if (e->source) { + g_source_destroy(e->source); + g_source_unref(e->source); + e->source = NULL; + } + + if (e->prev) + e->prev->next = e->next; + else + e->mainloop->io_events = e->next; + + if (e->next) + e->next->prev = e->prev; + + if ((e->next = e->mainloop->dead_io_events)) + e->next->prev = e; + + e->mainloop->dead_io_events = e; + e->prev = NULL; + + e->dead = 1; + schedule_free_dead_events(e->mainloop); +} + +static void glib_io_set_destroy(pa_io_event*e, void (*callback)(pa_mainloop_api*m, pa_io_event *e, void *userdata)) { + assert(e); + e->destroy_callback = callback; +} + +/* Time sources */ + +static void glib_time_restart(pa_time_event*e, const struct timeval *tv); + +static pa_time_event* glib_time_new(pa_mainloop_api*m, const struct timeval *tv, void (*callback) (pa_mainloop_api*m, pa_time_event*e, const struct timeval *tv, void *userdata), void *userdata) { + pa_glib_mainloop *g; + pa_time_event *e; + + assert(m && m->userdata && tv && callback); + g = m->userdata; + + e = pa_xmalloc(sizeof(pa_time_event)); + e->mainloop = g; + e->dead = 0; + e->callback = callback; + e->userdata = userdata; + e->destroy_callback = NULL; + e->source = NULL; + + glib_time_restart(e, tv); + + e->next = g->time_events; + if (e->next) e->next->prev = e; + g->time_events = e; + e->prev = NULL; + + return e; +} + +static guint msec_diff(const struct timeval *a, const struct timeval *b) { + guint r; + assert(a && b); + + if (a->tv_sec < b->tv_sec) + return 0; + + if (a->tv_sec == b->tv_sec && a->tv_sec <= b->tv_sec) + return 0; + + r = (a->tv_sec-b->tv_sec)*1000; + + if (a->tv_usec >= b->tv_usec) + r += (a->tv_usec - b->tv_usec) / 1000; + else + r -= (b->tv_usec - a->tv_usec) / 1000; + + return r; +} + +static gboolean time_cb(gpointer data) { + pa_time_event* e = data; + assert(e && e->mainloop && e->source); + + g_source_unref(e->source); + e->source = NULL; + + e->callback(&e->mainloop->api, e, &e->timeval, e->userdata); + return FALSE; +} + +static void glib_time_restart(pa_time_event*e, const struct timeval *tv) { + struct timeval now; + assert(e && e->mainloop && !e->dead); + + pa_gettimeofday(&now); + if (e->source) { + g_source_destroy(e->source); + g_source_unref(e->source); + } + + if (tv) { + e->timeval = *tv; + e->source = g_timeout_source_new(msec_diff(tv, &now)); + assert(e->source); + g_source_set_callback(e->source, time_cb, e, NULL); + g_source_set_priority(e->source, G_PRIORITY_DEFAULT); + g_source_attach(e->source, e->mainloop->glib_main_context); + } else + e->source = NULL; + } + +static void glib_time_free(pa_time_event *e) { + assert(e && e->mainloop && !e->dead); + + if (e->source) { + g_source_destroy(e->source); + g_source_unref(e->source); + e->source = NULL; + } + + if (e->prev) + e->prev->next = e->next; + else + e->mainloop->time_events = e->next; + + if (e->next) + e->next->prev = e->prev; + + if ((e->next = e->mainloop->dead_time_events)) + e->next->prev = e; + + e->mainloop->dead_time_events = e; + e->prev = NULL; + + e->dead = 1; + schedule_free_dead_events(e->mainloop); +} + +static void glib_time_set_destroy(pa_time_event *e, void (*callback)(pa_mainloop_api*m, pa_time_event*e, void *userdata)) { + assert(e); + e->destroy_callback = callback; +} + +/* Deferred sources */ + +static void glib_defer_enable(pa_defer_event *e, int b); + +static pa_defer_event* glib_defer_new(pa_mainloop_api*m, void (*callback) (pa_mainloop_api*m, pa_defer_event *e, void *userdata), void *userdata) { + pa_defer_event *e; + pa_glib_mainloop *g; + + assert(m && m->userdata && callback); + g = m->userdata; + + e = pa_xmalloc(sizeof(pa_defer_event)); + e->mainloop = g; + e->dead = 0; + e->callback = callback; + e->userdata = userdata; + e->destroy_callback = NULL; + e->source = NULL; + + glib_defer_enable(e, 1); + + e->next = g->defer_events; + if (e->next) e->next->prev = e; + g->defer_events = e; + e->prev = NULL; + return e; +} + +static gboolean idle_cb(gpointer data) { + pa_defer_event* e = data; + assert(e && e->mainloop && e->source); + + e->callback(&e->mainloop->api, e, e->userdata); + return TRUE; +} + +static void glib_defer_enable(pa_defer_event *e, int b) { + assert(e && e->mainloop); + + if (e->source && !b) { + g_source_destroy(e->source); + g_source_unref(e->source); + e->source = NULL; + } else if (!e->source && b) { + e->source = g_idle_source_new(); + assert(e->source); + g_source_set_callback(e->source, idle_cb, e, NULL); + g_source_attach(e->source, e->mainloop->glib_main_context); + g_source_set_priority(e->source, G_PRIORITY_HIGH); + } +} + +static void glib_defer_free(pa_defer_event *e) { + assert(e && e->mainloop && !e->dead); + + if (e->source) { + g_source_destroy(e->source); + g_source_unref(e->source); + e->source = NULL; + } + + if (e->prev) + e->prev->next = e->next; + else + e->mainloop->defer_events = e->next; + + if (e->next) + e->next->prev = e->prev; + + if ((e->next = e->mainloop->dead_defer_events)) + e->next->prev = e; + + e->mainloop->dead_defer_events = e; + e->prev = NULL; + + e->dead = 1; + schedule_free_dead_events(e->mainloop); +} + +static void glib_defer_set_destroy(pa_defer_event *e, void (*callback)(pa_mainloop_api *m, pa_defer_event *e, void *userdata)) { + assert(e); + e->destroy_callback = callback; +} + +/* quit() */ + +static void glib_quit(pa_mainloop_api*a, PA_GCC_UNUSED int retval) { + pa_glib_mainloop *g; + assert(a && a->userdata); + g = a->userdata; + + /* NOOP */ +} + +static const pa_mainloop_api vtable = { + .userdata = NULL, + + .io_new = glib_io_new, + .io_enable = glib_io_enable, + .io_free = glib_io_free, + .io_set_destroy= glib_io_set_destroy, + + .time_new = glib_time_new, + .time_restart = glib_time_restart, + .time_free = glib_time_free, + .time_set_destroy = glib_time_set_destroy, + + .defer_new = glib_defer_new, + .defer_enable = glib_defer_enable, + .defer_free = glib_defer_free, + .defer_set_destroy = glib_defer_set_destroy, + + .quit = glib_quit, +}; + +pa_glib_mainloop *pa_glib_mainloop_new(GMainContext *c) { + pa_glib_mainloop *g; + + g = pa_xmalloc(sizeof(pa_glib_mainloop)); + if (c) { + g->glib_main_context = c; + g_main_context_ref(c); + } else + g->glib_main_context = g_main_context_default(); + + g->api = vtable; + g->api.userdata = g; + + g->io_events = g->dead_io_events = NULL; + g->time_events = g->dead_time_events = NULL; + g->defer_events = g->dead_defer_events = NULL; + + g->cleanup_source = NULL; + return g; +} + +static void free_io_events(pa_io_event *e) { + while (e) { + pa_io_event *r = e; + e = r->next; + + if (r->source) { + g_source_destroy(r->source); + g_source_unref(r->source); + } + + if (r->io_channel) + g_io_channel_unref(r->io_channel); + + if (r->destroy_callback) + r->destroy_callback(&r->mainloop->api, r, r->userdata); + + pa_xfree(r); + } +} + +static void free_time_events(pa_time_event *e) { + while (e) { + pa_time_event *r = e; + e = r->next; + + if (r->source) { + g_source_destroy(r->source); + g_source_unref(r->source); + } + + if (r->destroy_callback) + r->destroy_callback(&r->mainloop->api, r, r->userdata); + + pa_xfree(r); + } +} + +static void free_defer_events(pa_defer_event *e) { + while (e) { + pa_defer_event *r = e; + e = r->next; + + if (r->source) { + g_source_destroy(r->source); + g_source_unref(r->source); + } + + if (r->destroy_callback) + r->destroy_callback(&r->mainloop->api, r, r->userdata); + + pa_xfree(r); + } +} + +void pa_glib_mainloop_free(pa_glib_mainloop* g) { + assert(g); + + free_io_events(g->io_events); + free_io_events(g->dead_io_events); + free_defer_events(g->defer_events); + free_defer_events(g->dead_defer_events); + free_time_events(g->time_events); + free_time_events(g->dead_time_events); + + if (g->cleanup_source) { + g_source_destroy(g->cleanup_source); + g_source_unref(g->cleanup_source); + } + + g_main_context_unref(g->glib_main_context); + pa_xfree(g); +} + +pa_mainloop_api* pa_glib_mainloop_get_api(pa_glib_mainloop *g) { + assert(g); + return &g->api; +} + +static gboolean free_dead_events(gpointer p) { + pa_glib_mainloop *g = p; + assert(g); + + free_io_events(g->dead_io_events); + free_defer_events(g->dead_defer_events); + free_time_events(g->dead_time_events); + + g->dead_io_events = NULL; + g->dead_defer_events = NULL; + g->dead_time_events = NULL; + + g_source_destroy(g->cleanup_source); + g_source_unref(g->cleanup_source); + g->cleanup_source = NULL; + + return FALSE; +} + +static void schedule_free_dead_events(pa_glib_mainloop *g) { + assert(g && g->glib_main_context); + + if (g->cleanup_source) + return; + + g->cleanup_source = g_idle_source_new(); + assert(g->cleanup_source); + g_source_set_callback(g->cleanup_source, free_dead_events, g, NULL); + g_source_attach(g->cleanup_source, g->glib_main_context); +} diff --git a/src/pulse/glib-mainloop.h b/src/pulse/glib-mainloop.h new file mode 100644 index 00000000..75de1cc7 --- /dev/null +++ b/src/pulse/glib-mainloop.h @@ -0,0 +1,66 @@ +#ifndef fooglibmainloophfoo +#define fooglibmainloophfoo + +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + 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 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 + +#include +#include + +/** \page glib-mainloop GLIB Main Loop Bindings + * + * \section overv_sec Overview + * + * The GLIB main loop bindings are extremely easy to use. All that is + * required is to create a pa_glib_mainloop object using + * pa_glib_mainloop_new(). When the main loop abstraction is needed, it is + * provided by pa_glib_mainloop_get_api(). + * + */ + +/** \file + * GLIB main loop support */ + +PA_C_DECL_BEGIN + +/** An opaque GLIB main loop object */ +typedef struct pa_glib_mainloop pa_glib_mainloop; + +/** Create a new GLIB main loop object for the specified GLIB main + * loop context. The GLIB 2.0 version takes an argument c for the + * GMainContext to use. If c is NULL the default context is used. */ +#if GLIB_MAJOR_VERSION >= 2 +pa_glib_mainloop *pa_glib_mainloop_new(GMainContext *c); +#else +pa_glib_mainloop *pa_glib_mainloop_new(void); +#endif + +/** Free the GLIB main loop object */ +void pa_glib_mainloop_free(pa_glib_mainloop* g); + +/** Return the abstract main loop API vtable for the GLIB main loop object */ +pa_mainloop_api* pa_glib_mainloop_get_api(pa_glib_mainloop *g); + +PA_C_DECL_END + +#endif diff --git a/src/pulse/glib12-mainloop.c b/src/pulse/glib12-mainloop.c new file mode 100644 index 00000000..ebaf87fc --- /dev/null +++ b/src/pulse/glib12-mainloop.c @@ -0,0 +1,503 @@ +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + 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 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 +#endif + +#include + +#include +#include + +#include +#include + +#include "glib-mainloop.h" + +/* A mainloop implementation based on GLIB 1.2 */ + +struct pa_io_event { + pa_glib_mainloop *mainloop; + int dead; + GIOChannel *io_channel; + guint source; + GIOCondition io_condition; + int fd; + void (*callback) (pa_mainloop_api*m, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata); + void *userdata; + void (*destroy_callback) (pa_mainloop_api *m, pa_io_event*e, void *userdata); + pa_io_event *next, *prev; +}; + +struct pa_time_event { + pa_glib_mainloop *mainloop; + int dead; + guint source; + struct timeval timeval; + void (*callback) (pa_mainloop_api*m, pa_time_event *e, const struct timeval *tv, void *userdata); + void *userdata; + void (*destroy_callback) (pa_mainloop_api *m, pa_time_event*e, void *userdata); + pa_time_event *next, *prev; +}; + +struct pa_defer_event { + pa_glib_mainloop *mainloop; + int dead; + guint source; + void (*callback) (pa_mainloop_api*m, pa_defer_event *e, void *userdata); + void *userdata; + void (*destroy_callback) (pa_mainloop_api *m, pa_defer_event*e, void *userdata); + pa_defer_event *next, *prev; +}; + +struct pa_glib_mainloop { + pa_mainloop_api api; + guint cleanup_source; + pa_io_event *io_events, *dead_io_events; + pa_time_event *time_events, *dead_time_events; + pa_defer_event *defer_events, *dead_defer_events; +}; + +static void schedule_free_dead_events(pa_glib_mainloop *g); + +static void glib_io_enable(pa_io_event*e, pa_io_event_flags_t f); + +static pa_io_event* glib_io_new(pa_mainloop_api*m, int fd, pa_io_event_flags_t f, void (*callback) (pa_mainloop_api*m, pa_io_event*e, int fd, pa_io_event_flags_t f, void *userdata), void *userdata) { + pa_io_event *e; + pa_glib_mainloop *g; + + assert(m && m->userdata && fd >= 0 && callback); + g = m->userdata; + + e = pa_xmalloc(sizeof(pa_io_event)); + e->mainloop = m->userdata; + e->dead = 0; + e->fd = fd; + e->callback = callback; + e->userdata = userdata; + e->destroy_callback = NULL; + + e->io_channel = g_io_channel_unix_new(e->fd); + assert(e->io_channel); + e->source = (guint) -1; + e->io_condition = 0; + + glib_io_enable(e, f); + + e->next = g->io_events; + if (e->next) e->next->prev = e; + g->io_events = e; + e->prev = NULL; + + return e; +} + +static gboolean io_cb(GIOChannel *source, GIOCondition condition, gpointer data) { + pa_io_event *e = data; + pa_io_event_flags_t f; + assert(source && e && e->io_channel == source); + + f = (condition & G_IO_IN ? PA_IO_EVENT_INPUT : 0) | + (condition & G_IO_OUT ? PA_IO_EVENT_OUTPUT : 0) | + (condition & G_IO_ERR ? PA_IO_EVENT_ERROR : 0) | + (condition & G_IO_HUP ? PA_IO_EVENT_HANGUP : 0); + + e->callback(&e->mainloop->api, e, e->fd, f, e->userdata); + return TRUE; +} + +static void glib_io_enable(pa_io_event*e, pa_io_event_flags_t f) { + GIOCondition c; + assert(e && !e->dead); + + c = (f & PA_IO_EVENT_INPUT ? G_IO_IN : 0) | (f & PA_IO_EVENT_OUTPUT ? G_IO_OUT : 0); + + if (c == e->io_condition) + return; + + if (e->source != (guint) -1) + g_source_remove(e->source); + + e->source = g_io_add_watch_full(e->io_channel, G_PRIORITY_DEFAULT, c | G_IO_ERR | G_IO_HUP, io_cb, e, NULL); + assert(e->source != (guint) -1); + e->io_condition = c; +} + +static void glib_io_free(pa_io_event*e) { + assert(e && !e->dead); + + if (e->source != (guint) -1) { + g_source_remove(e->source); + e->source = (guint) -1; + } + + if (e->prev) + e->prev->next = e->next; + else + e->mainloop->io_events = e->next; + + if (e->next) + e->next->prev = e->prev; + + if ((e->next = e->mainloop->dead_io_events)) + e->next->prev = e; + + e->mainloop->dead_io_events = e; + e->prev = NULL; + + e->dead = 1; + schedule_free_dead_events(e->mainloop); +} + +static void glib_io_set_destroy(pa_io_event*e, void (*callback)(pa_mainloop_api*m, pa_io_event *e, void *userdata)) { + assert(e); + e->destroy_callback = callback; +} + +/* Time sources */ + +static void glib_time_restart(pa_time_event*e, const struct timeval *tv); + +static pa_time_event* glib_time_new(pa_mainloop_api*m, const struct timeval *tv, void (*callback) (pa_mainloop_api*m, pa_time_event*e, const struct timeval *tv, void *userdata), void *userdata) { + pa_glib_mainloop *g; + pa_time_event *e; + + assert(m && m->userdata && tv && callback); + g = m->userdata; + + e = pa_xmalloc(sizeof(pa_time_event)); + e->mainloop = g; + e->dead = 0; + e->callback = callback; + e->userdata = userdata; + e->destroy_callback = NULL; + e->source = (guint) -1; + + glib_time_restart(e, tv); + + e->next = g->time_events; + if (e->next) e->next->prev = e; + g->time_events = e; + e->prev = NULL; + + return e; +} + +static guint msec_diff(const struct timeval *a, const struct timeval *b) { + guint r; + assert(a && b); + + if (a->tv_sec < b->tv_sec) + return 0; + + if (a->tv_sec == b->tv_sec && a->tv_sec <= b->tv_sec) + return 0; + + r = (a->tv_sec-b->tv_sec)*1000; + + if (a->tv_usec >= b->tv_usec) + r += (a->tv_usec - b->tv_usec) / 1000; + else + r -= (b->tv_usec - a->tv_usec) / 1000; + + return r; +} + +static gboolean time_cb(gpointer data) { + pa_time_event* e = data; + assert(e && e->mainloop && e->source != (guint) -1); + + g_source_remove(e->source); + e->source = (guint) -1; + + e->callback(&e->mainloop->api, e, &e->timeval, e->userdata); + return FALSE; +} + +static void glib_time_restart(pa_time_event*e, const struct timeval *tv) { + struct timeval now; + assert(e && e->mainloop && !e->dead); + + pa_gettimeofday(&now); + if (e->source != (guint) -1) + g_source_remove(e->source); + + if (tv) { + e->timeval = *tv; + e->source = g_timeout_add_full(G_PRIORITY_DEFAULT, msec_diff(tv, &now), time_cb, e, NULL); + assert(e->source != (guint) -1); + } else + e->source = (guint) -1; + } + +static void glib_time_free(pa_time_event *e) { + assert(e && e->mainloop && !e->dead); + + if (e->source != (guint) -1) { + g_source_remove(e->source); + e->source = (guint) -1; + } + + if (e->prev) + e->prev->next = e->next; + else + e->mainloop->time_events = e->next; + + if (e->next) + e->next->prev = e->prev; + + if ((e->next = e->mainloop->dead_time_events)) + e->next->prev = e; + + e->mainloop->dead_time_events = e; + e->prev = NULL; + + e->dead = 1; + schedule_free_dead_events(e->mainloop); +} + +static void glib_time_set_destroy(pa_time_event *e, void (*callback)(pa_mainloop_api*m, pa_time_event*e, void *userdata)) { + assert(e); + e->destroy_callback = callback; +} + +/* Deferred sources */ + +static void glib_defer_enable(pa_defer_event *e, int b); + +static pa_defer_event* glib_defer_new(pa_mainloop_api*m, void (*callback) (pa_mainloop_api*m, pa_defer_event *e, void *userdata), void *userdata) { + pa_defer_event *e; + pa_glib_mainloop *g; + + assert(m && m->userdata && callback); + g = m->userdata; + + e = pa_xmalloc(sizeof(pa_defer_event)); + e->mainloop = g; + e->dead = 0; + e->callback = callback; + e->userdata = userdata; + e->destroy_callback = NULL; + e->source = (guint) -1; + + glib_defer_enable(e, 1); + + e->next = g->defer_events; + if (e->next) e->next->prev = e; + g->defer_events = e; + e->prev = NULL; + return e; +} + +static gboolean idle_cb(gpointer data) { + pa_defer_event* e = data; + assert(e && e->mainloop && e->source != (guint) -1); + + e->callback(&e->mainloop->api, e, e->userdata); + return TRUE; +} + +static void glib_defer_enable(pa_defer_event *e, int b) { + assert(e && e->mainloop); + + if (e->source != (guint) -1 && !b) { + g_source_remove(e->source); + e->source = (guint) -1; + } else if (e->source == (guint) -1 && b) { + e->source = g_idle_add_full(G_PRIORITY_HIGH, idle_cb, e, NULL); + assert(e->source != (guint) -1); + } +} + +static void glib_defer_free(pa_defer_event *e) { + assert(e && e->mainloop && !e->dead); + + if (e->source != (guint) -1) { + g_source_remove(e->source); + e->source = (guint) -1; + } + + if (e->prev) + e->prev->next = e->next; + else + e->mainloop->defer_events = e->next; + + if (e->next) + e->next->prev = e->prev; + + if ((e->next = e->mainloop->dead_defer_events)) + e->next->prev = e; + + e->mainloop->dead_defer_events = e; + e->prev = NULL; + + e->dead = 1; + schedule_free_dead_events(e->mainloop); +} + +static void glib_defer_set_destroy(pa_defer_event *e, void (*callback)(pa_mainloop_api *m, pa_defer_event *e, void *userdata)) { + assert(e); + e->destroy_callback = callback; +} + +/* quit() */ + +static void glib_quit(pa_mainloop_api*a, PA_GCC_UNUSED int retval) { + pa_glib_mainloop *g; + assert(a && a->userdata); + g = a->userdata; + + /* NOOP */ +} + +static const pa_mainloop_api vtable = { + .userdata = NULL, + + .io_new = glib_io_new, + .io_enable = glib_io_enable, + .io_free = glib_io_free, + .io_set_destroy= glib_io_set_destroy, + + .time_new = glib_time_new, + .time_restart = glib_time_restart, + .time_free = glib_time_free, + .time_set_destroy = glib_time_set_destroy, + + .defer_new = glib_defer_new, + .defer_enable = glib_defer_enable, + .defer_free = glib_defer_free, + .defer_set_destroy = glib_defer_set_destroy, + + .quit = glib_quit, +}; + +pa_glib_mainloop *pa_glib_mainloop_new(void) { + pa_glib_mainloop *g; + + g = pa_xmalloc(sizeof(pa_glib_mainloop)); + + g->api = vtable; + g->api.userdata = g; + + g->io_events = g->dead_io_events = NULL; + g->time_events = g->dead_time_events = NULL; + g->defer_events = g->dead_defer_events = NULL; + + g->cleanup_source = (guint) -1; + return g; +} + +static void free_io_events(pa_io_event *e) { + while (e) { + pa_io_event *r = e; + e = r->next; + + if (r->source != (guint) -1) + g_source_remove(r->source); + + if (r->io_channel) + g_io_channel_unref(r->io_channel); + + if (r->destroy_callback) + r->destroy_callback(&r->mainloop->api, r, r->userdata); + + pa_xfree(r); + } +} + +static void free_time_events(pa_time_event *e) { + while (e) { + pa_time_event *r = e; + e = r->next; + + if (r->source != (guint) -1) + g_source_remove(r->source); + + if (r->destroy_callback) + r->destroy_callback(&r->mainloop->api, r, r->userdata); + + pa_xfree(r); + } +} + +static void free_defer_events(pa_defer_event *e) { + while (e) { + pa_defer_event *r = e; + e = r->next; + + if (r->source != (guint) -1) + g_source_remove(r->source); + + if (r->destroy_callback) + r->destroy_callback(&r->mainloop->api, r, r->userdata); + + pa_xfree(r); + } +} + +void pa_glib_mainloop_free(pa_glib_mainloop* g) { + assert(g); + + free_io_events(g->io_events); + free_io_events(g->dead_io_events); + free_defer_events(g->defer_events); + free_defer_events(g->dead_defer_events); + free_time_events(g->time_events); + free_time_events(g->dead_time_events); + + if (g->cleanup_source != (guint) -1) + g_source_remove(g->cleanup_source); + + pa_xfree(g); +} + +pa_mainloop_api* pa_glib_mainloop_get_api(pa_glib_mainloop *g) { + assert(g); + return &g->api; +} + +static gboolean free_dead_events(gpointer p) { + pa_glib_mainloop *g = p; + assert(g); + + free_io_events(g->dead_io_events); + free_defer_events(g->dead_defer_events); + free_time_events(g->dead_time_events); + + g->dead_io_events = NULL; + g->dead_defer_events = NULL; + g->dead_time_events = NULL; + + g_source_remove(g->cleanup_source); + g->cleanup_source = (guint) -1; + + return FALSE; +} + +static void schedule_free_dead_events(pa_glib_mainloop *g) { + assert(g); + + if (g->cleanup_source != (guint) -1) + return; + + g->cleanup_source = g_idle_add_full(G_PRIORITY_HIGH, free_dead_events, g, NULL); +} diff --git a/src/pulse/internal.h b/src/pulse/internal.h new file mode 100644 index 00000000..96028d83 --- /dev/null +++ b/src/pulse/internal.h @@ -0,0 +1,210 @@ +#ifndef foointernalhfoo +#define foointernalhfoo + +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + 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 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 +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "client-conf.h" + +#define DEFAULT_TIMEOUT (10) + +struct pa_context { + int ref; + + char *name; + pa_mainloop_api* mainloop; + + pa_socket_client *client; + pa_pstream *pstream; + pa_pdispatch *pdispatch; + + pa_dynarray *record_streams, *playback_streams; + PA_LLIST_HEAD(pa_stream, streams); + PA_LLIST_HEAD(pa_operation, operations); + + uint32_t version; + uint32_t ctag; + uint32_t csyncid; + uint32_t error; + pa_context_state_t state; + + pa_context_notify_cb_t state_callback; + void *state_userdata; + + pa_context_subscribe_cb_t subscribe_callback; + void *subscribe_userdata; + + pa_memblock_stat *memblock_stat; + + int local; + int do_autospawn; + int autospawn_lock_fd; + pa_spawn_api spawn_api; + + pa_strlist *server_list; + + char *server; + + pa_client_conf *conf; +}; + +#define PA_MAX_WRITE_INDEX_CORRECTIONS 10 + +typedef struct pa_index_correction { + uint32_t tag; + int valid; + int64_t value; + int absolute, corrupt; +} pa_index_correction; + +struct pa_stream { + int ref; + pa_context *context; + pa_mainloop_api *mainloop; + PA_LLIST_FIELDS(pa_stream); + + char *name; + pa_buffer_attr buffer_attr; + pa_sample_spec sample_spec; + pa_channel_map channel_map; + pa_stream_flags_t flags; + uint32_t channel; + uint32_t syncid; + int channel_valid; + uint32_t device_index; + pa_stream_direction_t direction; + pa_stream_state_t state; + + uint32_t requested_bytes; + + pa_memchunk peek_memchunk; + pa_memblockq *record_memblockq; + + int corked; + + /* Store latest latency info */ + pa_timing_info timing_info; + int timing_info_valid; + + /* Use to make sure that time advances monotonically */ + pa_usec_t previous_time; + + /* time updates with tags older than these are invalid */ + uint32_t write_index_not_before; + uint32_t read_index_not_before; + + /* Data about individual timing update correctoins */ + pa_index_correction write_index_corrections[PA_MAX_WRITE_INDEX_CORRECTIONS]; + int current_write_index_correction; + + /* Latency interpolation stuff */ + pa_time_event *auto_timing_update_event; + int auto_timing_update_requested; + + pa_usec_t cached_time; + int cached_time_valid; + + /* Callbacks */ + pa_stream_notify_cb_t state_callback; + void *state_userdata; + pa_stream_request_cb_t read_callback; + void *read_userdata; + pa_stream_request_cb_t write_callback; + void *write_userdata; + pa_stream_notify_cb_t overflow_callback; + void *overflow_userdata; + pa_stream_notify_cb_t underflow_callback; + void *underflow_userdata; + pa_stream_notify_cb_t latency_update_callback; + void *latency_update_userdata; +}; + +typedef void (*pa_operation_cb_t)(void); + +struct pa_operation { + int ref; + pa_context *context; + pa_stream *stream; + + PA_LLIST_FIELDS(pa_operation); + + pa_operation_state_t state; + void *userdata; + pa_operation_cb_t callback; +}; + +void pa_command_request(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); +void pa_command_stream_killed(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); +void pa_command_subscribe_event(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); +void pa_command_overflow_or_underflow(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); + +pa_operation *pa_operation_new(pa_context *c, pa_stream *s, pa_operation_cb_t callback, void *userdata); +void pa_operation_done(pa_operation *o); + +void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); +void pa_stream_disconnect_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); +void pa_context_simple_ack_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); +void pa_stream_simple_ack_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); + +void pa_context_fail(pa_context *c, int error); +int pa_context_set_error(pa_context *c, int error); +void pa_context_set_state(pa_context *c, pa_context_state_t st); +int pa_context_handle_error(pa_context *c, uint32_t command, pa_tagstruct *t); +pa_operation* pa_context_send_simple_command(pa_context *c, uint32_t command, void (*internal_callback)(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata), void (*cb)(void), void *userdata); + +void pa_stream_set_state(pa_stream *s, pa_stream_state_t st); + +pa_tagstruct *pa_tagstruct_command(pa_context *c, uint32_t command, uint32_t *tag); + +#define PA_CHECK_VALIDITY(context, expression, error) do { \ + if (!(expression)) \ + return -pa_context_set_error((context), (error)); \ +} while(0) + + +#define PA_CHECK_VALIDITY_RETURN_ANY(context, expression, error, value) do { \ + if (!(expression)) { \ + pa_context_set_error((context), (error)); \ + return value; \ + } \ +} while(0) + +#define PA_CHECK_VALIDITY_RETURN_NULL(context, expression, error) PA_CHECK_VALIDITY_RETURN_ANY(context, expression, error, NULL) + + +#endif diff --git a/src/pulse/introspect.c b/src/pulse/introspect.c new file mode 100644 index 00000000..ed40c53d --- /dev/null +++ b/src/pulse/introspect.c @@ -0,0 +1,1240 @@ +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + 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 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 +#endif + +#include + +#include + +#include +#include + +#include "internal.h" + +#include "introspect.h" + +/*** Statistics ***/ + +static void context_stat_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { + pa_operation *o = userdata; + pa_stat_info i, *p = &i; + + assert(pd); + assert(o); + assert(o->ref >= 1); + + if (!o->context) + goto finish; + + if (command != PA_COMMAND_REPLY) { + if (pa_context_handle_error(o->context, command, t) < 0) + goto finish; + + p = NULL; + } else if (pa_tagstruct_getu32(t, &i.memblock_total) < 0 || + pa_tagstruct_getu32(t, &i.memblock_total_size) < 0 || + pa_tagstruct_getu32(t, &i.memblock_allocated) < 0 || + pa_tagstruct_getu32(t, &i.memblock_allocated_size) < 0 || + pa_tagstruct_getu32(t, &i.scache_size) < 0 || + !pa_tagstruct_eof(t)) { + pa_context_fail(o->context, PA_ERR_PROTOCOL); + goto finish; + } + + if (o->callback) { + pa_stat_info_cb_t cb = (pa_stat_info_cb_t) o->callback; + cb(o->context, p, o->userdata); + } + +finish: + pa_operation_done(o); + pa_operation_unref(o); +} + +pa_operation* pa_context_stat(pa_context *c, pa_stat_info_cb_t cb, void *userdata) { + return pa_context_send_simple_command(c, PA_COMMAND_STAT, context_stat_callback, (pa_operation_cb_t) cb, userdata); +} + +/*** Server Info ***/ + +static void context_get_server_info_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { + pa_operation *o = userdata; + pa_server_info i, *p = &i; + + assert(pd); + assert(o); + assert(o->ref >= 1); + + if (!o->context) + goto finish; + + if (command != PA_COMMAND_REPLY) { + if (pa_context_handle_error(o->context, command, t) < 0) + goto finish; + + p = NULL; + } else if (pa_tagstruct_gets(t, &i.server_name) < 0 || + pa_tagstruct_gets(t, &i.server_version) < 0 || + pa_tagstruct_gets(t, &i.user_name) < 0 || + pa_tagstruct_gets(t, &i.host_name) < 0 || + pa_tagstruct_get_sample_spec(t, &i.sample_spec) < 0 || + pa_tagstruct_gets(t, &i.default_sink_name) < 0 || + pa_tagstruct_gets(t, &i.default_source_name) < 0 || + pa_tagstruct_getu32(t, &i.cookie) < 0 || + !pa_tagstruct_eof(t)) { + + pa_context_fail(o->context, PA_ERR_PROTOCOL); + goto finish; + } + + if (o->callback) { + pa_server_info_cb_t cb = (pa_server_info_cb_t) o->callback; + cb(o->context, p, o->userdata); + } + +finish: + pa_operation_done(o); + pa_operation_unref(o); +} + +pa_operation* pa_context_get_server_info(pa_context *c, pa_server_info_cb_t cb, void *userdata) { + return pa_context_send_simple_command(c, PA_COMMAND_GET_SERVER_INFO, context_get_server_info_callback, (pa_operation_cb_t) cb, userdata); +} + +/*** Sink Info ***/ + +static void context_get_sink_info_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { + pa_operation *o = userdata; + int eol = 1; + + assert(pd); + assert(o); + assert(o->ref >= 1); + + if (!o->context) + goto finish; + + if (command != PA_COMMAND_REPLY) { + if (pa_context_handle_error(o->context, command, t) < 0) + goto finish; + + eol = -1; + } else { + uint32_t flags; + + while (!pa_tagstruct_eof(t)) { + pa_sink_info i; + + if (pa_tagstruct_getu32(t, &i.index) < 0 || + pa_tagstruct_gets(t, &i.name) < 0 || + pa_tagstruct_gets(t, &i.description) < 0 || + pa_tagstruct_get_sample_spec(t, &i.sample_spec) < 0 || + pa_tagstruct_get_channel_map(t, &i.channel_map) < 0 || + pa_tagstruct_getu32(t, &i.owner_module) < 0 || + pa_tagstruct_get_cvolume(t, &i.volume) < 0 || + pa_tagstruct_get_boolean(t, &i.mute) < 0 || + pa_tagstruct_getu32(t, &i.monitor_source) < 0 || + pa_tagstruct_gets(t, &i.monitor_source_name) < 0 || + pa_tagstruct_get_usec(t, &i.latency) < 0 || + pa_tagstruct_gets(t, &i.driver) < 0 || + pa_tagstruct_getu32(t, &flags) < 0) { + + pa_context_fail(o->context, PA_ERR_PROTOCOL); + goto finish; + } + + i.flags = (pa_sink_flags_t) flags; + + if (o->callback) { + pa_sink_info_cb_t cb = (pa_sink_info_cb_t) o->callback; + cb(o->context, &i, 0, o->userdata); + } + } + } + + if (o->callback) { + pa_sink_info_cb_t cb = (pa_sink_info_cb_t) o->callback; + cb(o->context, NULL, eol, o->userdata); + } + +finish: + pa_operation_done(o); + pa_operation_unref(o); +} + +pa_operation* pa_context_get_sink_info_list(pa_context *c, pa_sink_info_cb_t cb, void *userdata) { + return pa_context_send_simple_command(c, PA_COMMAND_GET_SINK_INFO_LIST, context_get_sink_info_callback, (pa_operation_cb_t) cb, userdata); +} + +pa_operation* pa_context_get_sink_info_by_index(pa_context *c, uint32_t idx, pa_sink_info_cb_t cb, void *userdata) { + pa_tagstruct *t; + pa_operation *o; + uint32_t tag; + + assert(c); + assert(c->ref >= 1); + assert(cb); + + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + + o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); + + t = pa_tagstruct_command(c, PA_COMMAND_GET_SINK_INFO, &tag); + pa_tagstruct_putu32(t, idx); + pa_tagstruct_puts(t, NULL); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_sink_info_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); + + return o; +} + +pa_operation* pa_context_get_sink_info_by_name(pa_context *c, const char *name, pa_sink_info_cb_t cb, void *userdata) { + pa_tagstruct *t; + pa_operation *o; + uint32_t tag; + + assert(c); + assert(c->ref >= 1); + assert(cb); + + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(c, !name || *name, PA_ERR_INVALID); + + o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); + + t = pa_tagstruct_command(c, PA_COMMAND_GET_SINK_INFO, &tag); + pa_tagstruct_putu32(t, PA_INVALID_INDEX); + pa_tagstruct_puts(t, name); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_sink_info_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); + + return o; +} + +/*** Source info ***/ + +static void context_get_source_info_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { + pa_operation *o = userdata; + int eol = 1; + + assert(pd); + assert(o); + assert(o->ref >= 1); + + if (!o->context) + goto finish; + + if (command != PA_COMMAND_REPLY) { + if (pa_context_handle_error(o->context, command, t) < 0) + goto finish; + + eol = -1; + } else { + + while (!pa_tagstruct_eof(t)) { + pa_source_info i; + uint32_t flags; + + if (pa_tagstruct_getu32(t, &i.index) < 0 || + pa_tagstruct_gets(t, &i.name) < 0 || + pa_tagstruct_gets(t, &i.description) < 0 || + pa_tagstruct_get_sample_spec(t, &i.sample_spec) < 0 || + pa_tagstruct_get_channel_map(t, &i.channel_map) < 0 || + pa_tagstruct_getu32(t, &i.owner_module) < 0 || + pa_tagstruct_get_cvolume(t, &i.volume) < 0 || + pa_tagstruct_get_boolean(t, &i.mute) < 0 || + pa_tagstruct_getu32(t, &i.monitor_of_sink) < 0 || + pa_tagstruct_gets(t, &i.monitor_of_sink_name) < 0 || + pa_tagstruct_get_usec(t, &i.latency) < 0 || + pa_tagstruct_gets(t, &i.driver) < 0 || + pa_tagstruct_getu32(t, &flags) < 0) { + + pa_context_fail(o->context, PA_ERR_PROTOCOL); + goto finish; + } + + i.flags = (pa_source_flags_t) flags; + + if (o->callback) { + pa_source_info_cb_t cb = (pa_source_info_cb_t) o->callback; + cb(o->context, &i, 0, o->userdata); + } + } + } + + if (o->callback) { + pa_source_info_cb_t cb = (pa_source_info_cb_t) o->callback; + cb(o->context, NULL, eol, o->userdata); + } + +finish: + pa_operation_done(o); + pa_operation_unref(o); +} + +pa_operation* pa_context_get_source_info_list(pa_context *c, pa_source_info_cb_t cb, void *userdata) { + return pa_context_send_simple_command(c, PA_COMMAND_GET_SOURCE_INFO_LIST, context_get_source_info_callback, (pa_operation_cb_t) cb, userdata); +} + +pa_operation* pa_context_get_source_info_by_index(pa_context *c, uint32_t idx, pa_source_info_cb_t cb, void *userdata) { + pa_tagstruct *t; + pa_operation *o; + uint32_t tag; + + assert(c); + assert(c->ref >= 1); + assert(cb); + + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + + o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); + + t = pa_tagstruct_command(c, PA_COMMAND_GET_SOURCE_INFO, &tag); + pa_tagstruct_putu32(t, idx); + pa_tagstruct_puts(t, NULL); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_source_info_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); + + return o; +} + +pa_operation* pa_context_get_source_info_by_name(pa_context *c, const char *name, pa_source_info_cb_t cb, void *userdata) { + pa_tagstruct *t; + pa_operation *o; + uint32_t tag; + + assert(c); + assert(c->ref >= 1); + assert(cb); + + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(c, !name || *name, PA_ERR_INVALID); + + o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); + + t = pa_tagstruct_command(c, PA_COMMAND_GET_SOURCE_INFO, &tag); + pa_tagstruct_putu32(t, PA_INVALID_INDEX); + pa_tagstruct_puts(t, name); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_source_info_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); + + return o; +} + +/*** Client info ***/ + +static void context_get_client_info_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { + pa_operation *o = userdata; + int eol = 1; + + assert(pd); + assert(o); + assert(o->ref >= 1); + + if (!o->context) + goto finish; + + if (command != PA_COMMAND_REPLY) { + if (pa_context_handle_error(o->context, command, t) < 0) + goto finish; + + eol = -1; + } else { + + while (!pa_tagstruct_eof(t)) { + pa_client_info i; + + if (pa_tagstruct_getu32(t, &i.index) < 0 || + pa_tagstruct_gets(t, &i.name) < 0 || + pa_tagstruct_getu32(t, &i.owner_module) < 0 || + pa_tagstruct_gets(t, &i.driver) < 0 ) { + pa_context_fail(o->context, PA_ERR_PROTOCOL); + goto finish; + } + + if (o->callback) { + pa_client_info_cb_t cb = (pa_client_info_cb_t) o->callback; + cb(o->context, &i, 0, o->userdata); + } + } + } + + if (o->callback) { + pa_client_info_cb_t cb = (pa_client_info_cb_t) o->callback; + cb(o->context, NULL, eol, o->userdata); + } + +finish: + pa_operation_done(o); + pa_operation_unref(o); +} + +pa_operation* pa_context_get_client_info(pa_context *c, uint32_t idx, pa_client_info_cb_t cb, void *userdata) { + pa_tagstruct *t; + pa_operation *o; + uint32_t tag; + + assert(c); + assert(c->ref >= 1); + assert(cb); + + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID); + + o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); + + t = pa_tagstruct_command(c, PA_COMMAND_GET_CLIENT_INFO, &tag); + pa_tagstruct_putu32(t, idx); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_client_info_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); + + return o; +} + +pa_operation* pa_context_get_client_info_list(pa_context *c, pa_client_info_cb_t cb, void *userdata) { + return pa_context_send_simple_command(c, PA_COMMAND_GET_CLIENT_INFO_LIST, context_get_client_info_callback, (pa_operation_cb_t) cb, userdata); +} + +/*** Module info ***/ + +static void context_get_module_info_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { + pa_operation *o = userdata; + int eol = 1; + + assert(pd); + assert(o); + assert(o->ref >= 1); + + if (!o->context) + goto finish; + + if (command != PA_COMMAND_REPLY) { + if (pa_context_handle_error(o->context, command, t) < 0) + goto finish; + + eol = -1; + } else { + + while (!pa_tagstruct_eof(t)) { + pa_module_info i; + + if (pa_tagstruct_getu32(t, &i.index) < 0 || + pa_tagstruct_gets(t, &i.name) < 0 || + pa_tagstruct_gets(t, &i.argument) < 0 || + pa_tagstruct_getu32(t, &i.n_used) < 0 || + pa_tagstruct_get_boolean(t, &i.auto_unload) < 0) { + pa_context_fail(o->context, PA_ERR_PROTOCOL); + goto finish; + } + + if (o->callback) { + pa_module_info_cb_t cb = (pa_module_info_cb_t) o->callback; + cb(o->context, &i, 0, o->userdata); + } + } + } + + if (o->callback) { + pa_module_info_cb_t cb = (pa_module_info_cb_t) o->callback; + cb(o->context, NULL, eol, o->userdata); + } + +finish: + pa_operation_done(o); + pa_operation_unref(o); +} + +pa_operation* pa_context_get_module_info(pa_context *c, uint32_t idx, pa_module_info_cb_t cb, void *userdata) { + pa_tagstruct *t; + pa_operation *o; + uint32_t tag; + + assert(c); + assert(c->ref >= 1); + assert(cb); + + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID); + + o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); + + t = pa_tagstruct_command(c, PA_COMMAND_GET_MODULE_INFO, &tag); + pa_tagstruct_putu32(t, idx); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_module_info_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); + + return o; +} + +pa_operation* pa_context_get_module_info_list(pa_context *c, pa_module_info_cb_t cb, void *userdata) { + return pa_context_send_simple_command(c, PA_COMMAND_GET_MODULE_INFO_LIST, context_get_module_info_callback, (pa_operation_cb_t) cb, userdata); +} + +/*** Sink input info ***/ + +static void context_get_sink_input_info_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { + pa_operation *o = userdata; + int eol = 1; + + assert(pd); + assert(o); + assert(o->ref >= 1); + + if (!o->context) + goto finish; + + if (command != PA_COMMAND_REPLY) { + if (pa_context_handle_error(o->context, command, t) < 0) + goto finish; + + eol = -1; + } else { + + while (!pa_tagstruct_eof(t)) { + pa_sink_input_info i; + + if (pa_tagstruct_getu32(t, &i.index) < 0 || + pa_tagstruct_gets(t, &i.name) < 0 || + pa_tagstruct_getu32(t, &i.owner_module) < 0 || + pa_tagstruct_getu32(t, &i.client) < 0 || + pa_tagstruct_getu32(t, &i.sink) < 0 || + pa_tagstruct_get_sample_spec(t, &i.sample_spec) < 0 || + pa_tagstruct_get_channel_map(t, &i.channel_map) < 0 || + pa_tagstruct_get_cvolume(t, &i.volume) < 0 || + pa_tagstruct_get_usec(t, &i.buffer_usec) < 0 || + pa_tagstruct_get_usec(t, &i.sink_usec) < 0 || + pa_tagstruct_gets(t, &i.resample_method) < 0 || + pa_tagstruct_gets(t, &i.driver) < 0) { + + pa_context_fail(o->context, PA_ERR_PROTOCOL); + goto finish; + } + + if (o->callback) { + pa_sink_input_info_cb_t cb = (pa_sink_input_info_cb_t) o->callback; + cb(o->context, &i, 0, o->userdata); + } + } + } + + if (o->callback) { + pa_sink_input_info_cb_t cb = (pa_sink_input_info_cb_t) o->callback; + cb(o->context, NULL, eol, o->userdata); + } + +finish: + pa_operation_done(o); + pa_operation_unref(o); +} + +pa_operation* pa_context_get_sink_input_info(pa_context *c, uint32_t idx, pa_sink_input_info_cb_t cb, void *userdata) { + pa_tagstruct *t; + pa_operation *o; + uint32_t tag; + + assert(c); + assert(c->ref >= 1); + assert(cb); + + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID); + + o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); + + t = pa_tagstruct_command(c, PA_COMMAND_GET_SINK_INPUT_INFO, &tag); + pa_tagstruct_putu32(t, idx); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_sink_input_info_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); + + return o; +} + +pa_operation* pa_context_get_sink_input_info_list(pa_context *c, void (*cb)(pa_context *c, const pa_sink_input_info*i, int is_last, void *userdata), void *userdata) { + return pa_context_send_simple_command(c, PA_COMMAND_GET_SINK_INPUT_INFO_LIST, context_get_sink_input_info_callback, (pa_operation_cb_t) cb, userdata); +} + +/*** Source output info ***/ + +static void context_get_source_output_info_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { + pa_operation *o = userdata; + int eol = 1; + + assert(pd); + assert(o); + assert(o->ref >= 1); + + if (!o->context) + goto finish; + + if (command != PA_COMMAND_REPLY) { + if (pa_context_handle_error(o->context, command, t) < 0) + goto finish; + + eol = -1; + } else { + + while (!pa_tagstruct_eof(t)) { + pa_source_output_info i; + + if (pa_tagstruct_getu32(t, &i.index) < 0 || + pa_tagstruct_gets(t, &i.name) < 0 || + pa_tagstruct_getu32(t, &i.owner_module) < 0 || + pa_tagstruct_getu32(t, &i.client) < 0 || + pa_tagstruct_getu32(t, &i.source) < 0 || + pa_tagstruct_get_sample_spec(t, &i.sample_spec) < 0 || + pa_tagstruct_get_channel_map(t, &i.channel_map) < 0 || + pa_tagstruct_get_usec(t, &i.buffer_usec) < 0 || + pa_tagstruct_get_usec(t, &i.source_usec) < 0 || + pa_tagstruct_gets(t, &i.resample_method) < 0 || + pa_tagstruct_gets(t, &i.driver) < 0) { + + pa_context_fail(o->context, PA_ERR_PROTOCOL); + goto finish; + } + + if (o->callback) { + pa_source_output_info_cb_t cb = (pa_source_output_info_cb_t) o->callback; + cb(o->context, &i, 0, o->userdata); + } + } + } + + if (o->callback) { + pa_source_output_info_cb_t cb = (pa_source_output_info_cb_t) o->callback; + cb(o->context, NULL, eol, o->userdata); + } + +finish: + pa_operation_done(o); + pa_operation_unref(o); +} + +pa_operation* pa_context_get_source_output_info(pa_context *c, uint32_t idx, pa_source_output_info_cb_t cb, void *userdata) { + pa_tagstruct *t; + pa_operation *o; + uint32_t tag; + + assert(c); + assert(c->ref >= 1); + assert(cb); + + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID); + + o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); + + t = pa_tagstruct_command(c, PA_COMMAND_GET_SOURCE_OUTPUT_INFO, &tag); + pa_tagstruct_putu32(t, idx); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_source_output_info_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); + + return o; +} + +pa_operation* pa_context_get_source_output_info_list(pa_context *c, pa_source_output_info_cb_t cb, void *userdata) { + return pa_context_send_simple_command(c, PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST, context_get_source_output_info_callback, (pa_operation_cb_t) cb, userdata); +} + +/*** Volume manipulation ***/ + +pa_operation* pa_context_set_sink_volume_by_index(pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata) { + pa_operation *o; + pa_tagstruct *t; + uint32_t tag; + + assert(c); + assert(c->ref >= 1); + assert(volume); + + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(c, pa_cvolume_valid(volume), PA_ERR_INVALID); + + o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); + + t = pa_tagstruct_command(c, PA_COMMAND_SET_SINK_VOLUME, &tag); + pa_tagstruct_putu32(t, idx); + pa_tagstruct_puts(t, NULL); + pa_tagstruct_put_cvolume(t, volume); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); + + return o; +} + +pa_operation* pa_context_set_sink_volume_by_name(pa_context *c, const char *name, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata) { + pa_operation *o; + pa_tagstruct *t; + uint32_t tag; + + assert(c); + assert(c->ref >= 1); + assert(name); + assert(volume); + + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(c, pa_cvolume_valid(volume), PA_ERR_INVALID); + PA_CHECK_VALIDITY_RETURN_NULL(c, !name || *name, PA_ERR_INVALID); + + o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); + + t = pa_tagstruct_command(c, PA_COMMAND_SET_SINK_VOLUME, &tag); + pa_tagstruct_putu32(t, PA_INVALID_INDEX); + pa_tagstruct_puts(t, name); + pa_tagstruct_put_cvolume(t, volume); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); + + return o; +} + +pa_operation* pa_context_set_sink_mute_by_index(pa_context *c, uint32_t idx, int mute, pa_context_success_cb_t cb, void *userdata) { + pa_operation *o; + pa_tagstruct *t; + uint32_t tag; + + assert(c); + assert(c->ref >= 1); + + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + + o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); + + t = pa_tagstruct_command(c, PA_COMMAND_SET_SINK_MUTE, &tag); + pa_tagstruct_putu32(t, idx); + pa_tagstruct_puts(t, NULL); + pa_tagstruct_put_boolean(t, mute); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); + + return o; +} + +pa_operation* pa_context_set_sink_mute_by_name(pa_context *c, const char *name, int mute, pa_context_success_cb_t cb, void *userdata) { + pa_operation *o; + pa_tagstruct *t; + uint32_t tag; + + assert(c); + assert(c->ref >= 1); + assert(name); + + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(c, !name || *name, PA_ERR_INVALID); + + o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); + + t = pa_tagstruct_command(c, PA_COMMAND_SET_SINK_MUTE, &tag); + pa_tagstruct_putu32(t, PA_INVALID_INDEX); + pa_tagstruct_puts(t, name); + pa_tagstruct_put_boolean(t, mute); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); + + return o; +} + +pa_operation* pa_context_set_sink_input_volume(pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata) { + pa_operation *o; + pa_tagstruct *t; + uint32_t tag; + + assert(c); + assert(c->ref >= 1); + assert(volume); + + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID); + PA_CHECK_VALIDITY_RETURN_NULL(c, pa_cvolume_valid(volume), PA_ERR_INVALID); + + o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); + + t = pa_tagstruct_command(c, PA_COMMAND_SET_SINK_INPUT_VOLUME, &tag); + pa_tagstruct_putu32(t, idx); + pa_tagstruct_put_cvolume(t, volume); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); + + return o; +} + +pa_operation* pa_context_set_source_volume_by_index(pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata) { + pa_operation *o; + pa_tagstruct *t; + uint32_t tag; + + assert(c); + assert(c->ref >= 1); + assert(volume); + + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(c, pa_cvolume_valid(volume), PA_ERR_INVALID); + + o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); + + t = pa_tagstruct_command(c, PA_COMMAND_SET_SOURCE_VOLUME, &tag); + pa_tagstruct_putu32(t, idx); + pa_tagstruct_puts(t, NULL); + pa_tagstruct_put_cvolume(t, volume); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); + + return o; +} + +pa_operation* pa_context_set_source_volume_by_name(pa_context *c, const char *name, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata) { + pa_operation *o; + pa_tagstruct *t; + uint32_t tag; + + assert(c); + assert(c->ref >= 1); + assert(name); + assert(volume); + + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(c, pa_cvolume_valid(volume), PA_ERR_INVALID); + PA_CHECK_VALIDITY_RETURN_NULL(c, !name || *name, PA_ERR_INVALID); + + o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); + + t = pa_tagstruct_command(c, PA_COMMAND_SET_SOURCE_VOLUME, &tag); + pa_tagstruct_putu32(t, PA_INVALID_INDEX); + pa_tagstruct_puts(t, name); + pa_tagstruct_put_cvolume(t, volume); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); + + return o; +} + +pa_operation* pa_context_set_source_mute_by_index(pa_context *c, uint32_t idx, int mute, pa_context_success_cb_t cb, void *userdata) { + pa_operation *o; + pa_tagstruct *t; + uint32_t tag; + + assert(c); + assert(c->ref >= 1); + + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + + o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); + + t = pa_tagstruct_command(c, PA_COMMAND_SET_SOURCE_MUTE, &tag); + pa_tagstruct_putu32(t, idx); + pa_tagstruct_puts(t, NULL); + pa_tagstruct_put_boolean(t, mute); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); + + return o; +} + +pa_operation* pa_context_set_source_mute_by_name(pa_context *c, const char *name, int mute, pa_context_success_cb_t cb, void *userdata) { + pa_operation *o; + pa_tagstruct *t; + uint32_t tag; + + assert(c); + assert(c->ref >= 1); + assert(name); + + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(c, !name || *name, PA_ERR_INVALID); + + o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); + + t = pa_tagstruct_command(c, PA_COMMAND_SET_SOURCE_MUTE, &tag); + pa_tagstruct_putu32(t, PA_INVALID_INDEX); + pa_tagstruct_puts(t, name); + pa_tagstruct_put_boolean(t, mute); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); + + return o; +} + +/** Sample Cache **/ + +static void context_get_sample_info_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { + pa_operation *o = userdata; + int eol = 1; + + assert(pd); + assert(o); + assert(o->ref >= 1); + + if (!o->context) + goto finish; + + if (command != PA_COMMAND_REPLY) { + if (pa_context_handle_error(o->context, command, t) < 0) + goto finish; + + eol = -1; + } else { + + while (!pa_tagstruct_eof(t)) { + pa_sample_info i; + + if (pa_tagstruct_getu32(t, &i.index) < 0 || + pa_tagstruct_gets(t, &i.name) < 0 || + pa_tagstruct_get_cvolume(t, &i.volume) < 0 || + pa_tagstruct_get_usec(t, &i.duration) < 0 || + pa_tagstruct_get_sample_spec(t, &i.sample_spec) < 0 || + pa_tagstruct_get_channel_map(t, &i.channel_map) < 0 || + pa_tagstruct_getu32(t, &i.bytes) < 0 || + pa_tagstruct_get_boolean(t, &i.lazy) < 0 || + pa_tagstruct_gets(t, &i.filename) < 0) { + + pa_context_fail(o->context, PA_ERR_PROTOCOL); + goto finish; + } + + if (o->callback) { + pa_sample_info_cb_t cb = (pa_sample_info_cb_t) o->callback; + cb(o->context, &i, 0, o->userdata); + } + } + } + + if (o->callback) { + pa_sample_info_cb_t cb = (pa_sample_info_cb_t) o->callback; + cb(o->context, NULL, eol, o->userdata); + } + +finish: + pa_operation_done(o); + pa_operation_unref(o); +} + +pa_operation* pa_context_get_sample_info_by_name(pa_context *c, const char *name, pa_sample_info_cb_t cb, void *userdata) { + pa_tagstruct *t; + pa_operation *o; + uint32_t tag; + + assert(c); + assert(c->ref >= 1); + assert(cb); + + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(c, name && *name, PA_ERR_INVALID); + + o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); + + t = pa_tagstruct_command(c, PA_COMMAND_GET_SAMPLE_INFO, &tag); + pa_tagstruct_putu32(t, PA_INVALID_INDEX); + pa_tagstruct_puts(t, name); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_sample_info_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); + + return o; +} + +pa_operation* pa_context_get_sample_info_by_index(pa_context *c, uint32_t idx, pa_sample_info_cb_t cb, void *userdata) { + pa_tagstruct *t; + pa_operation *o; + uint32_t tag; + + assert(c); + assert(c->ref >= 1); + assert(cb); + + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID); + + o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); + + t = pa_tagstruct_command(c, PA_COMMAND_GET_SAMPLE_INFO, &tag); + pa_tagstruct_putu32(t, idx); + pa_tagstruct_puts(t, NULL); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_sample_info_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); + + return o; +} + +pa_operation* pa_context_get_sample_info_list(pa_context *c, pa_sample_info_cb_t cb, void *userdata) { + return pa_context_send_simple_command(c, PA_COMMAND_GET_SAMPLE_INFO_LIST, context_get_sample_info_callback, (pa_operation_cb_t) cb, userdata); +} + +static pa_operation* command_kill(pa_context *c, uint32_t command, uint32_t idx, pa_context_success_cb_t cb, void *userdata) { + pa_operation *o; + pa_tagstruct *t; + uint32_t tag; + + assert(c); + assert(c->ref >= 1); + + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID); + + o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); + + t = pa_tagstruct_command(c, command, &tag); + pa_tagstruct_putu32(t, idx); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); + + return o; +} + +pa_operation* pa_context_kill_client(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata) { + return command_kill(c, PA_COMMAND_KILL_CLIENT, idx, cb, userdata); +} + +pa_operation* pa_context_kill_sink_input(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata) { + return command_kill(c, PA_COMMAND_KILL_SINK_INPUT, idx, cb, userdata); +} + +pa_operation* pa_context_kill_source_output(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata) { + return command_kill(c, PA_COMMAND_KILL_SOURCE_OUTPUT, idx, cb, userdata); +} + +static void context_index_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { + pa_operation *o = userdata; + uint32_t idx; + + assert(pd); + assert(o); + assert(o->ref >= 1); + + if (!o->context) + goto finish; + + if (command != PA_COMMAND_REPLY) { + if (pa_context_handle_error(o->context, command, t) < 0) + goto finish; + + idx = PA_INVALID_INDEX; + } else if (pa_tagstruct_getu32(t, &idx) || + !pa_tagstruct_eof(t)) { + pa_context_fail(o->context, PA_ERR_PROTOCOL); + goto finish; + } + + if (o->callback) { + pa_context_index_cb_t cb = (pa_context_index_cb_t) o->callback; + cb(o->context, idx, o->userdata); + } + + +finish: + pa_operation_done(o); + pa_operation_unref(o); +} + +pa_operation* pa_context_load_module(pa_context *c, const char*name, const char *argument, pa_context_index_cb_t cb, void *userdata) { + pa_operation *o; + pa_tagstruct *t; + uint32_t tag; + + assert(c); + assert(c->ref >= 1); + + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(c, name && *name, PA_ERR_INVALID); + + o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); + + t = pa_tagstruct_command(c, PA_COMMAND_LOAD_MODULE, &tag); + pa_tagstruct_puts(t, name); + pa_tagstruct_puts(t, argument); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_index_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); + + return o; +} + +pa_operation* pa_context_unload_module(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata) { + return command_kill(c, PA_COMMAND_UNLOAD_MODULE, idx, cb, userdata); +} + +/*** Autoload stuff ***/ + +static void context_get_autoload_info_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { + pa_operation *o = userdata; + int eol = 1; + + assert(pd); + assert(o); + assert(o->ref >= 1); + + if (!o->context) + goto finish; + + if (command != PA_COMMAND_REPLY) { + if (pa_context_handle_error(o->context, command, t) < 0) + goto finish; + + eol = -1; + } else { + + while (!pa_tagstruct_eof(t)) { + pa_autoload_info i; + + if (pa_tagstruct_getu32(t, &i.index) < 0 || + pa_tagstruct_gets(t, &i.name) < 0 || + pa_tagstruct_getu32(t, &i.type) < 0 || + pa_tagstruct_gets(t, &i.module) < 0 || + pa_tagstruct_gets(t, &i.argument) < 0) { + pa_context_fail(o->context, PA_ERR_PROTOCOL); + goto finish; + } + + if (o->callback) { + pa_autoload_info_cb_t cb = (pa_autoload_info_cb_t) o->callback; + cb(o->context, &i, 0, o->userdata); + } + } + } + + if (o->callback) { + pa_autoload_info_cb_t cb = (pa_autoload_info_cb_t) o->callback; + cb(o->context, NULL, eol, o->userdata); + } + +finish: + pa_operation_done(o); + pa_operation_unref(o); +} + +pa_operation* pa_context_get_autoload_info_by_name(pa_context *c, const char *name, pa_autoload_type_t type, pa_autoload_info_cb_t cb, void *userdata) { + pa_tagstruct *t; + pa_operation *o; + uint32_t tag; + + assert(c); + assert(c->ref >= 1); + assert(cb); + + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(c, name && *name, PA_ERR_INVALID); + PA_CHECK_VALIDITY_RETURN_NULL(c, type == PA_AUTOLOAD_SINK || type == PA_AUTOLOAD_SOURCE, PA_ERR_INVALID); + + o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); + + t = pa_tagstruct_command(c, PA_COMMAND_GET_AUTOLOAD_INFO, &tag); + pa_tagstruct_puts(t, name); + pa_tagstruct_putu32(t, type); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_autoload_info_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); + + return o; +} + +pa_operation* pa_context_get_autoload_info_by_index(pa_context *c, uint32_t idx, pa_autoload_info_cb_t cb, void *userdata) { + pa_tagstruct *t; + pa_operation *o; + uint32_t tag; + + assert(c); + assert(c->ref >= 1); + assert(cb); + + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID); + + o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); + + t = pa_tagstruct_command(c, PA_COMMAND_GET_AUTOLOAD_INFO, &tag); + pa_tagstruct_putu32(t, idx); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_autoload_info_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); + + return o; +} + +pa_operation* pa_context_get_autoload_info_list(pa_context *c, pa_autoload_info_cb_t cb, void *userdata) { + return pa_context_send_simple_command(c, PA_COMMAND_GET_AUTOLOAD_INFO_LIST, context_get_autoload_info_callback, (pa_operation_cb_t) cb, userdata); +} + +pa_operation* pa_context_add_autoload(pa_context *c, const char *name, pa_autoload_type_t type, const char *module, const char*argument, pa_context_index_cb_t cb, void* userdata) { + pa_operation *o; + pa_tagstruct *t; + uint32_t tag; + + assert(c); + assert(c->ref >= 1); + + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(c, name && *name, PA_ERR_INVALID); + PA_CHECK_VALIDITY_RETURN_NULL(c, type == PA_AUTOLOAD_SINK || type == PA_AUTOLOAD_SOURCE, PA_ERR_INVALID); + PA_CHECK_VALIDITY_RETURN_NULL(c, module && *module, PA_ERR_INVALID); + + o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); + + t = pa_tagstruct_command(c, PA_COMMAND_ADD_AUTOLOAD, &tag); + pa_tagstruct_puts(t, name); + pa_tagstruct_putu32(t, type); + pa_tagstruct_puts(t, module); + pa_tagstruct_puts(t, argument); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_index_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); + + return o; +} + +pa_operation* pa_context_remove_autoload_by_name(pa_context *c, const char *name, pa_autoload_type_t type, pa_context_success_cb_t cb, void* userdata) { + pa_operation *o; + pa_tagstruct *t; + uint32_t tag; + + assert(c); + assert(c->ref >= 1); + + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(c, name && *name, PA_ERR_INVALID); + PA_CHECK_VALIDITY_RETURN_NULL(c, type == PA_AUTOLOAD_SINK || type == PA_AUTOLOAD_SOURCE, PA_ERR_INVALID); + + o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); + + t = pa_tagstruct_command(c, PA_COMMAND_REMOVE_AUTOLOAD, &tag); + pa_tagstruct_puts(t, name); + pa_tagstruct_putu32(t, type); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); + + return o; +} + +pa_operation* pa_context_remove_autoload_by_index(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void* userdata) { + pa_operation *o; + pa_tagstruct *t; + uint32_t tag; + + assert(c); + assert(c->ref >= 1); + + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID); + + o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); + + t = pa_tagstruct_command(c, PA_COMMAND_REMOVE_AUTOLOAD, &tag); + pa_tagstruct_putu32(t, idx); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); + + return o; +} diff --git a/src/pulse/introspect.h b/src/pulse/introspect.h new file mode 100644 index 00000000..8fe218bc --- /dev/null +++ b/src/pulse/introspect.h @@ -0,0 +1,490 @@ +#ifndef foointrospecthfoo +#define foointrospecthfoo + +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + 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 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 + +#include +#include +#include +#include +#include + +/** \page introspect Server Query and Control + * + * \section overv_sec Overview + * + * Sometimes it is necessary to query and modify global settings in the + * server. For this, Polypaudio has the introspection API. It can list sinks, + * sources, samples and other aspects of the server. It can also modify the + * attributes of the server that will affect operations on a global level, + * and not just the application's context. + * + * \section query_sec Querying + * + * All querying is done through callbacks. This design is necessary to + * maintain an asynchronous design. The client will request the information + * and some time later, the server will respond with the desired data. + * + * Some objects can have multiple entries at the server. When requesting all + * of these at once, the callback will be called multiple times, once for + * each object. When the list has been exhausted, the callback will be called + * without an information structure and the eol parameter set to a non-zero + * value. + * + * Note that even if a single object is requested, and not the entire list, + * the terminating call will still be made. + * + * If an error occurs, the callback will be called without and information + * structure and eol set to zero. + * + * Data members in the information structures are only valid during the + * duration of the callback. If they are required after the callback is + * finished, a deep copy must be performed. + * + * \subsection server_subsec Server Information + * + * The server can be queried about its name, the environment it's running on + * and the currently active global defaults. Calling + * pa_context_get_server_info() will get access to a pa_server_info structure + * containing all of these. + * + * \subsection memstat_subsec Memory Usage + * + * Statistics about memory usage can be fetched using pa_context_stat(), + * giving a pa_stat_info structure. + * + * \subsection sinksrc_subsec Sinks and Sources + * + * The server can have an arbitrary number of sinks and sources. Each sink + * and source have both an index and a name associated with it. As such + * there are three ways to get access to them: + * + * \li By index - pa_context_get_sink_info_by_index() / + * pa_context_get_source_info_by_index() + * \li By name - pa_context_get_sink_info_by_name() / + * pa_context_get_source_info_by_name() + * \li All - pa_context_get_sink_info_list() / + * pa_context_get_source_info_list() + * + * All three method use the same callback and will provide a pa_sink_info or + * pa_source_info structure. + * + * \subsection siso_subsec Sink Inputs and Source Outputs + * + * Sink inputs and source outputs are the representations of the client ends + * of streams inside the server. I.e. they connect a client stream to one of + * the global sinks or sources. + * + * Sink inputs and source outputs only have an index to identify them. As + * such, there are only two ways to get information about them: + * + * \li By index - pa_context_get_sink_input_info() / + * pa_context_get_source_output_info() + * \li All - pa_context_get_sink_input_info_list() / + * pa_context_get_source_output_info_list() + * + * The structure returned is the pa_sink_input_info or pa_source_output_info + * structure. + * + * \subsection samples_subsec Samples + * + * The list of cached samples can be retrieved from the server. Three methods + * exist for querying the sample cache list: + * + * \li By index - pa_context_get_sample_info_by_index() + * \li By name - pa_context_get_sample_info_by_name() + * \li All - pa_context_get_sample_info_list() + * + * Note that this only retrieves information about the sample, not the sample + * data itself. + * + * \subsection module_subsec Driver Modules + * + * Polypaudio driver modules are identified by index and are retrieved using either + * pa_context_get_module_info() or pa_context_get_module_info_list(). The + * information structure is called pa_module_info. + * + * \subsection autoload_subsec Autoload Entries + * + * Modules can be autoloaded as a result of a client requesting a certain + * sink or source. This mapping between sink/source names and modules can be + * queried from the server: + * + * \li By index - pa_context_get_autoload_info_by_index() + * \li By sink/source name - pa_context_get_autoload_info_by_name() + * \li All - pa_context_get_autoload_info_list() + * + * \subsection client_subsec Clients + * + * Polypaudio clients are also identified by index and are retrieved using + * either pa_context_get_client_info() or pa_context_get_client_info_list(). + * The information structure is called pa_client_info. + * + * \section ctrl_sec Control + * + * Some parts of the server are only possible to read, but most can also be + * modified in different ways. Note that these changes will affect all + * connected clients and not just the one issuing the request. + * + * \subsection sinksrc_subsec Sinks and Sources + * + * The most common change one would want to do to sinks and sources is to + * modify the volume of the audio. Identical to how sinks and sources can + * be queried, there are two ways of identifying them: + * + * \li By index - pa_context_set_sink_volume_by_index() / + * pa_context_set_source_volume_by_index() + * \li By name - pa_context_set_sink_volume_by_name() / + * pa_context_set_source_volume_by_name() + * + * It is also possible to mute a sink or source: + * + * \li By index - pa_context_set_sink_mute_by_index() / + * pa_context_set_source_mute_by_index() + * \li By name - pa_context_set_sink_mute_by_name() / + * pa_context_set_source_mute_by_name() + * + * \subsection siso_subsec Sink Inputs and Source Outputs + * + * If an application desires to modify the volume of just a single stream + * (commonly one of its own streams), this can be done by setting the volume + * of its associated sink input, using pa_context_set_sink_input_volume(). + * + * There is no support for modifying the volume of source outputs. + * + * It is also possible to remove sink inputs and source outputs, terminating + * the streams associated with them: + * + * \li Sink input - pa_context_kill_sink_input() + * \li Source output - pa_context_kill_source_output() + * + * \subsection module_subsec Modules + * + * Server modules can be remotely loaded and unloaded using + * pa_context_load_module() and pa_context_unload_module(). + * + * \subsection autoload_subsec Autoload Entries + * + * New module autoloading rules can be added, and existing can be removed + * using pa_context_add_autoload() and pa_context_remove_autoload_by_index() + * / pa_context_remove_autoload_by_name(). + * + * \subsection client_subsec Clients + * + * The only operation supported on clients, is the possibility of kicking + * them off the server using pa_context_kill_client(). + */ + +/** \file + * + * Routines for daemon introspection. + */ + +PA_C_DECL_BEGIN + +/** Stores information about sinks */ +typedef struct pa_sink_info { + const char *name; /**< Name of the sink */ + uint32_t index; /**< Index of the sink */ + const char *description; /**< Description of this sink */ + pa_sample_spec sample_spec; /**< Sample spec of this sink */ + pa_channel_map channel_map; /**< Channel map \since 0.8 */ + uint32_t owner_module; /**< Index of the owning module of this sink, or PA_INVALID_INDEX */ + pa_cvolume volume; /**< Volume of the sink */ + int mute; /**< Mute switch of the sink \since 0.8 */ + uint32_t monitor_source; /**< Index of the monitor source connected to this sink */ + const char *monitor_source_name; /**< The name of the monitor source */ + pa_usec_t latency; /**< Length of filled playback buffer of this sink */ + const char *driver; /**< Driver name. \since 0.8 */ + pa_sink_flags_t flags; /**< Flags \since 0.8 */ +} pa_sink_info; + +/** Callback prototype for pa_context_get_sink_info_by_name() and friends */ +typedef void (*pa_sink_info_cb_t)(pa_context *c, const pa_sink_info *i, int eol, void *userdata); + +/** Get information about a sink by its name */ +pa_operation* pa_context_get_sink_info_by_name(pa_context *c, const char *name, pa_sink_info_cb_t cb, void *userdata); + +/** Get information about a sink by its index */ +pa_operation* pa_context_get_sink_info_by_index(pa_context *c, uint32_t id, pa_sink_info_cb_t cb, void *userdata); + +/** Get the complete sink list */ +pa_operation* pa_context_get_sink_info_list(pa_context *c, pa_sink_info_cb_t cb, void *userdata); + +/** Stores information about sources */ +typedef struct pa_source_info { + const char *name ; /**< Name of the source */ + uint32_t index; /**< Index of the source */ + const char *description; /**< Description of this source */ + pa_sample_spec sample_spec; /**< Sample spec of this source */ + pa_channel_map channel_map; /**< Channel map \since 0.8 */ + uint32_t owner_module; /**< Owning module index, or PA_INVALID_INDEX */ + pa_cvolume volume; /**< Volume of the source \since 0.8 */ + int mute; /**< Mute switch of the sink \since 0.8 */ + uint32_t monitor_of_sink; /**< If this is a monitor source the index of the owning sink, otherwise PA_INVALID_INDEX */ + const char *monitor_of_sink_name; /**< Name of the owning sink, or PA_INVALID_INDEX */ + pa_usec_t latency; /**< Length of filled record buffer of this source. \since 0.5 */ + const char *driver; /**< Driver name \since 0.8 */ + pa_source_flags_t flags; /**< Flags \since 0.8 */ +} pa_source_info; + +/** Callback prototype for pa_context_get_source_info_by_name() and friends */ +typedef void (*pa_source_info_cb_t)(pa_context *c, const pa_source_info *i, int eol, void *userdata); + +/** Get information about a source by its name */ +pa_operation* pa_context_get_source_info_by_name(pa_context *c, const char *name, pa_source_info_cb_t cb, void *userdata); + +/** Get information about a source by its index */ +pa_operation* pa_context_get_source_info_by_index(pa_context *c, uint32_t id, pa_source_info_cb_t cb, void *userdata); + +/** Get the complete source list */ +pa_operation* pa_context_get_source_info_list(pa_context *c, pa_source_info_cb_t cb, void *userdata); + +/** Server information */ +typedef struct pa_server_info { + const char *user_name; /**< User name of the daemon process */ + const char *host_name; /**< Host name the daemon is running on */ + const char *server_version; /**< Version string of the daemon */ + const char *server_name; /**< Server package name (usually "pulseaudio") */ + pa_sample_spec sample_spec; /**< Default sample specification */ + const char *default_sink_name; /**< Name of default sink. \since 0.4 */ + const char *default_source_name; /**< Name of default sink. \since 0.4*/ + uint32_t cookie; /**< A random cookie for identifying this instance of pulseaudio. \since 0.8 */ +} pa_server_info; + +/** Callback prototype for pa_context_get_server_info() */ +typedef void (*pa_server_info_cb_t) (pa_context *c, const pa_server_info*i, void *userdata); + +/** Get some information about the server */ +pa_operation* pa_context_get_server_info(pa_context *c, pa_server_info_cb_t cb, void *userdata); + +/** Stores information about modules */ +typedef struct pa_module_info { + uint32_t index; /**< Index of the module */ + const char*name, /**< Name of the module */ + *argument; /**< Argument string of the module */ + uint32_t n_used; /**< Usage counter or PA_INVALID_INDEX */ + int auto_unload; /**< Non-zero if this is an autoloaded module */ +} pa_module_info; + +/** Callback prototype for pa_context_get_module_info() and firends*/ +typedef void (*pa_module_info_cb_t) (pa_context *c, const pa_module_info*i, int eol, void *userdata); + +/** Get some information about a module by its index */ +pa_operation* pa_context_get_module_info(pa_context *c, uint32_t idx, pa_module_info_cb_t cb, void *userdata); + +/** Get the complete list of currently loaded modules */ +pa_operation* pa_context_get_module_info_list(pa_context *c, pa_module_info_cb_t cb, void *userdata); + +/** Stores information about clients */ +typedef struct pa_client_info { + uint32_t index; /**< Index of this client */ + const char *name; /**< Name of this client */ + uint32_t owner_module; /**< Index of the owning module, or PA_INVALID_INDEX */ + const char *driver; /**< Driver name \since 0.8 */ +} pa_client_info; + +/** Callback prototype for pa_context_get_client_info() and firends*/ +typedef void (*pa_client_info_cb_t) (pa_context *c, const pa_client_info*i, int eol, void *userdata); + +/** Get information about a client by its index */ +pa_operation* pa_context_get_client_info(pa_context *c, uint32_t idx, pa_client_info_cb_t cb, void *userdata); + +/** Get the complete client list */ +pa_operation* pa_context_get_client_info_list(pa_context *c, pa_client_info_cb_t cb, void *userdata); + +/** Stores information about sink inputs */ +typedef struct pa_sink_input_info { + uint32_t index; /**< Index of the sink input */ + const char *name; /**< Name of the sink input */ + uint32_t owner_module; /**< Index of the module this sink input belongs to, or PA_INVALID_INDEX when it does not belong to any module */ + uint32_t client; /**< Index of the client this sink input belongs to, or PA_INVALID_INDEX when it does not belong to any client */ + uint32_t sink; /**< Index of the connected sink */ + pa_sample_spec sample_spec; /**< The sample specification of the sink input */ + pa_channel_map channel_map; /**< Channel map */ + pa_cvolume volume; /**< The volume of this sink input */ + pa_usec_t buffer_usec; /**< Latency due to buffering in sink input, see pa_latency_info for details */ + pa_usec_t sink_usec; /**< Latency of the sink device, see pa_latency_info for details */ + const char *resample_method; /**< Thre resampling method used by this sink input. \since 0.7 */ + const char *driver; /**< Driver name \since 0.8 */ +} pa_sink_input_info; + +/** Callback prototype for pa_context_get_sink_input_info() and firends*/ +typedef void (*pa_sink_input_info_cb_t) (pa_context *c, const pa_sink_input_info *i, int eol, void *userdata); + +/** Get some information about a sink input by its index */ +pa_operation* pa_context_get_sink_input_info(pa_context *c, uint32_t idx, pa_sink_input_info_cb_t cb, void *userdata); + +/** Get the complete sink input list */ +pa_operation* pa_context_get_sink_input_info_list(pa_context *c, pa_sink_input_info_cb_t cb, void *userdata); + +/** Stores information about source outputs */ +typedef struct pa_source_output_info { + uint32_t index; /**< Index of the sink input */ + const char *name; /**< Name of the sink input */ + uint32_t owner_module; /**< Index of the module this sink input belongs to, or PA_INVALID_INDEX when it does not belong to any module */ + uint32_t client; /**< Index of the client this sink input belongs to, or PA_INVALID_INDEX when it does not belong to any client */ + uint32_t source; /**< Index of the connected source */ + pa_sample_spec sample_spec; /**< The sample specification of the source output */ + pa_channel_map channel_map; /**< Channel map */ + pa_usec_t buffer_usec; /**< Latency due to buffering in the source output, see pa_latency_info for details. \since 0.5 */ + pa_usec_t source_usec; /**< Latency of the source device, see pa_latency_info for details. \since 0.5 */ + const char *resample_method; /**< Thre resampling method used by this source output. \since 0.7 */ + const char *driver; /**< Driver name \since 0.8 */ +} pa_source_output_info; + +/** Callback prototype for pa_context_get_source_output_info() and firends*/ +typedef void (*pa_source_output_info_cb_t) (pa_context *c, const pa_source_output_info *i, int eol, void *userdata); + +/** Get information about a source output by its index */ +pa_operation* pa_context_get_source_output_info(pa_context *c, uint32_t idx, pa_source_output_info_cb_t cb, void *userdata); + +/** Get the complete list of source outputs */ +pa_operation* pa_context_get_source_output_info_list(pa_context *c, pa_source_output_info_cb_t cb, void *userdata); + +/** Set the volume of a sink device specified by its index */ +pa_operation* pa_context_set_sink_volume_by_index(pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata); + +/** Set the volume of a sink device specified by its name */ +pa_operation* pa_context_set_sink_volume_by_name(pa_context *c, const char *name, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata); + +/** Set the mute switch of a sink device specified by its index \since 0.8 */ +pa_operation* pa_context_set_sink_mute_by_index(pa_context *c, uint32_t idx, int mute, pa_context_success_cb_t cb, void *userdata); + +/** Set the mute switch of a sink device specified by its name \since 0.8 */ +pa_operation* pa_context_set_sink_mute_by_name(pa_context *c, const char *name, int mute, pa_context_success_cb_t cb, void *userdata); + +/** Set the volume of a sink input stream */ +pa_operation* pa_context_set_sink_input_volume(pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata); + +/** Set the volume of a source device specified by its index \since 0.8 */ +pa_operation* pa_context_set_source_volume_by_index(pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata); + +/** Set the volume of a source device specified by its name \since 0.8 */ +pa_operation* pa_context_set_source_volume_by_name(pa_context *c, const char *name, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata); + +/** Set the mute switch of a source device specified by its index \since 0.8 */ +pa_operation* pa_context_set_source_mute_by_index(pa_context *c, uint32_t idx, int mute, pa_context_success_cb_t cb, void *userdata); + +/** Set the mute switch of a source device specified by its name \since 0.8 */ +pa_operation* pa_context_set_source_mute_by_name(pa_context *c, const char *name, int mute, pa_context_success_cb_t cb, void *userdata); + +/** Memory block statistics */ +typedef struct pa_stat_info { + uint32_t memblock_total; /**< Currently allocated memory blocks */ + uint32_t memblock_total_size; /**< Currentl total size of allocated memory blocks */ + uint32_t memblock_allocated; /**< Allocated memory blocks during the whole lifetime of the daemon */ + uint32_t memblock_allocated_size; /**< Total size of all memory blocks allocated during the whole lifetime of the daemon */ + uint32_t scache_size; /**< Total size of all sample cache entries. \since 0.4 */ +} pa_stat_info; + +/** Callback prototype for pa_context_stat() */ +typedef void (*pa_stat_info_cb_t) (pa_context *c, const pa_stat_info *i, void *userdata); + +/** Get daemon memory block statistics */ +pa_operation* pa_context_stat(pa_context *c, pa_stat_info_cb_t cb, void *userdata); + +/** Stores information about sample cache entries */ +typedef struct pa_sample_info { + uint32_t index; /**< Index of this entry */ + const char *name; /**< Name of this entry */ + pa_cvolume volume; /**< Default volume of this entry */ + pa_sample_spec sample_spec; /**< Sample specification of the sample */ + pa_channel_map channel_map; /**< The channel map */ + pa_usec_t duration; /**< Duration of this entry */ + uint32_t bytes; /**< Length of this sample in bytes. \since 0.4 */ + int lazy; /**< Non-zero when this is a lazy cache entry. \since 0.5 */ + const char *filename; /**< In case this is a lazy cache entry, the filename for the sound file to be loaded on demand. \since 0.5 */ +} pa_sample_info; + +/** Callback prototype for pa_context_get_sample_info_by_name() and firends */ +typedef void (*pa_sample_info_cb_t)(pa_context *c, const pa_sample_info *i, int eol, void *userdata); + +/** Get information about a sample by its name */ +pa_operation* pa_context_get_sample_info_by_name(pa_context *c, const char *name, pa_sample_info_cb_t cb, void *userdata); + +/** Get information about a sample by its index */ +pa_operation* pa_context_get_sample_info_by_index(pa_context *c, uint32_t idx, pa_sample_info_cb_t cb, void *userdata); + +/** Get the complete list of samples stored in the daemon. */ +pa_operation* pa_context_get_sample_info_list(pa_context *c, pa_sample_info_cb_t cb, void *userdata); + +/** Kill a client. \since 0.5 */ +pa_operation* pa_context_kill_client(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata); + +/** Kill a sink input. \since 0.5 */ +pa_operation* pa_context_kill_sink_input(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata); + +/** Kill a source output. \since 0.5 */ +pa_operation* pa_context_kill_source_output(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata); + +/** Callback prototype for pa_context_load_module() and pa_context_add_autoload() */ +typedef void (*pa_context_index_cb_t)(pa_context *c, uint32_t idx, void *userdata); + +/** Load a module. \since 0.5 */ +pa_operation* pa_context_load_module(pa_context *c, const char*name, const char *argument, pa_context_index_cb_t cb, void *userdata); + +/** Unload a module. \since 0.5 */ +pa_operation* pa_context_unload_module(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata); + +/** Type of an autoload entry. \since 0.5 */ +typedef enum pa_autoload_type { + PA_AUTOLOAD_SINK = 0, + PA_AUTOLOAD_SOURCE = 1 +} pa_autoload_type_t; + +/** Stores information about autoload entries. \since 0.5 */ +typedef struct pa_autoload_info { + uint32_t index; /**< Index of this autoload entry */ + const char *name; /**< Name of the sink or source */ + pa_autoload_type_t type; /**< Type of the autoload entry */ + const char *module; /**< Module name to load */ + const char *argument; /**< Argument string for module */ +} pa_autoload_info; + +/** Callback prototype for pa_context_get_autoload_info_by_name() and firends */ +typedef void (*pa_autoload_info_cb_t)(pa_context *c, const pa_autoload_info *i, int eol, void *userdata); + +/** Get info about a specific autoload entry. \since 0.6 */ +pa_operation* pa_context_get_autoload_info_by_name(pa_context *c, const char *name, pa_autoload_type_t type, pa_autoload_info_cb_t cb, void *userdata); + +/** Get info about a specific autoload entry. \since 0.6 */ +pa_operation* pa_context_get_autoload_info_by_index(pa_context *c, uint32_t idx, pa_autoload_info_cb_t cb, void *userdata); + +/** Get the complete list of autoload entries. \since 0.5 */ +pa_operation* pa_context_get_autoload_info_list(pa_context *c, pa_autoload_info_cb_t cb, void *userdata); + +/** Add a new autoload entry. \since 0.5 */ +pa_operation* pa_context_add_autoload(pa_context *c, const char *name, pa_autoload_type_t type, const char *module, const char*argument, pa_context_index_cb_t, void* userdata); + +/** Remove an autoload entry. \since 0.6 */ +pa_operation* pa_context_remove_autoload_by_name(pa_context *c, const char *name, pa_autoload_type_t type, pa_context_success_cb_t cb, void* userdata); + +/** Remove an autoload entry. \since 0.6 */ +pa_operation* pa_context_remove_autoload_by_index(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void* userdata); + + +PA_C_DECL_END + +#endif diff --git a/src/pulse/mainloop-api.c b/src/pulse/mainloop-api.c new file mode 100644 index 00000000..2e20b446 --- /dev/null +++ b/src/pulse/mainloop-api.c @@ -0,0 +1,70 @@ +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + 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 +#endif + +#include +#include + +#include + +#include + +#include "mainloop-api.h" + +struct once_info { + void (*callback)(pa_mainloop_api*m, void *userdata); + void *userdata; +}; + +static void once_callback(pa_mainloop_api *m, pa_defer_event *e, void *userdata) { + struct once_info *i = userdata; + assert(m && i && i->callback); + + i->callback(m, i->userdata); + + assert(m->defer_free); + m->defer_free(e); +} + +static void free_callback(pa_mainloop_api *m, PA_GCC_UNUSED pa_defer_event *e, void *userdata) { + struct once_info *i = userdata; + assert(m && i); + pa_xfree(i); +} + +void pa_mainloop_api_once(pa_mainloop_api* m, void (*callback)(pa_mainloop_api *m, void *userdata), void *userdata) { + struct once_info *i; + pa_defer_event *e; + assert(m && callback); + + i = pa_xnew(struct once_info, 1); + i->callback = callback; + i->userdata = userdata; + + assert(m->defer_new); + e = m->defer_new(m, once_callback, i); + assert(e); + m->defer_set_destroy(e, free_callback); +} + diff --git a/src/pulse/mainloop-api.h b/src/pulse/mainloop-api.h new file mode 100644 index 00000000..a732b215 --- /dev/null +++ b/src/pulse/mainloop-api.h @@ -0,0 +1,118 @@ +#ifndef foomainloopapihfoo +#define foomainloopapihfoo + +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + 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 +#include + +#include + +/** \file + * + * Main loop abstraction layer. Both the pulseaudio core and the + * pulseaudio client library use a main loop abstraction layer. Due to + * this it is possible to embed pulseaudio into other + * applications easily. Two main loop implemenations are + * currently available: + * \li A minimal implementation based on the C library's poll() function (See \ref mainloop.h) + * \li A wrapper around the GLIB main loop. Use this to embed pulseaudio into your GLIB/GTK+/GNOME programs (See \ref glib-mainloop.h) + * + * The structure pa_mainloop_api is used as vtable for the main loop abstraction. + * + * This mainloop abstraction layer has no direct support for UNIX signals. Generic, mainloop implementation agnostic support is available throught \ref mainloop-signal.h. + * */ + +PA_C_DECL_BEGIN + +/** A bitmask for IO events */ +typedef enum pa_io_event_flags { + PA_IO_EVENT_NULL = 0, /**< No event */ + PA_IO_EVENT_INPUT = 1, /**< Input event */ + PA_IO_EVENT_OUTPUT = 2, /**< Output event */ + PA_IO_EVENT_HANGUP = 4, /**< Hangup event */ + PA_IO_EVENT_ERROR = 8 /**< Error event */ +} pa_io_event_flags_t; + +/** An opaque IO event source object */ +typedef struct pa_io_event pa_io_event; + +/** An opaque deferred event source object. Events of this type are triggered once in every main loop iteration */ +typedef struct pa_defer_event pa_defer_event; + +/** An opaque timer event source object */ +typedef struct pa_time_event pa_time_event; + +/** An abstract mainloop API vtable */ +typedef struct pa_mainloop_api pa_mainloop_api; + +/** An abstract mainloop API vtable */ +struct pa_mainloop_api { + /** A pointer to some private, arbitrary data of the main loop implementation */ + void *userdata; + + /** Create a new IO event source object */ + pa_io_event* (*io_new)(pa_mainloop_api*a, int fd, pa_io_event_flags_t events, void (*callback) (pa_mainloop_api*a, pa_io_event* e, int fd, pa_io_event_flags_t events, void *userdata), void *userdata); + + /** Enable or disable IO events on this object */ + void (*io_enable)(pa_io_event* e, pa_io_event_flags_t events); + + /** Free a IO event source object */ + void (*io_free)(pa_io_event* e); + + /** Set a function that is called when the IO event source is destroyed. Use this to free the userdata argument if required */ + void (*io_set_destroy)(pa_io_event *e, void (*callback) (pa_mainloop_api*a, pa_io_event *e, void *userdata)); + + /** Create a new timer event source object for the specified Unix time */ + pa_time_event* (*time_new)(pa_mainloop_api*a, const struct timeval *tv, void (*callback) (pa_mainloop_api*a, pa_time_event* e, const struct timeval *tv, void *userdata), void *userdata); + + /** Restart a running or expired timer event source with a new Unix time */ + void (*time_restart)(pa_time_event* e, const struct timeval *tv); + + /** Free a deferred timer event source object */ + void (*time_free)(pa_time_event* e); + + /** Set a function that is called when the timer event source is destroyed. Use this to free the userdata argument if required */ + void (*time_set_destroy)(pa_time_event *e, void (*callback) (pa_mainloop_api*a, pa_time_event *e, void *userdata)); + + /** Create a new deferred event source object */ + pa_defer_event* (*defer_new)(pa_mainloop_api*a, void (*callback) (pa_mainloop_api*a, pa_defer_event* e, void *userdata), void *userdata); + + /** Enable or disable a deferred event source temporarily */ + void (*defer_enable)(pa_defer_event* e, int b); + + /** Free a deferred event source object */ + void (*defer_free)(pa_defer_event* e); + + /** Set a function that is called when the deferred event source is destroyed. Use this to free the userdata argument if required */ + void (*defer_set_destroy)(pa_defer_event *e, void (*callback) (pa_mainloop_api*a, pa_defer_event *e, void *userdata)); + + /** Exit the main loop and return the specfied retval*/ + void (*quit)(pa_mainloop_api*a, int retval); +}; + +/** Run the specified callback function once from the main loop using an anonymous defer event. */ +void pa_mainloop_api_once(pa_mainloop_api*m, void (*callback)(pa_mainloop_api*m, void *userdata), void *userdata); + +PA_C_DECL_END + +#endif diff --git a/src/pulse/mainloop-signal.c b/src/pulse/mainloop-signal.c new file mode 100644 index 00000000..c3cf362d --- /dev/null +++ b/src/pulse/mainloop-signal.c @@ -0,0 +1,210 @@ +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + 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 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 +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_WINDOWS_H +#include +#endif + +#include +#include + +#include +#include +#include + +#include "mainloop-signal.h" + +struct pa_signal_event { + int sig; +#ifdef HAVE_SIGACTION + struct sigaction saved_sigaction; +#else + void (*saved_handler)(int sig); +#endif + void (*callback) (pa_mainloop_api*a, pa_signal_event *e, int sig, void *userdata); + void *userdata; + void (*destroy_callback) (pa_mainloop_api*a, pa_signal_event*e, void *userdata); + pa_signal_event *previous, *next; +}; + +static pa_mainloop_api *api = NULL; +static int signal_pipe[2] = { -1, -1 }; +static pa_io_event* io_event = NULL; +static pa_signal_event *signals = NULL; + +static void signal_handler(int sig) { +#ifndef HAVE_SIGACTION + signal(sig, signal_handler); +#endif + pa_write(signal_pipe[1], &sig, sizeof(sig)); +} + +static void dispatch(pa_mainloop_api*a, int sig) { + pa_signal_event*s; + + for (s = signals; s; s = s->next) + if (s->sig == sig) { + assert(s->callback); + s->callback(a, s, sig, s->userdata); + break; + } +} + +static void callback(pa_mainloop_api*a, pa_io_event*e, int fd, pa_io_event_flags_t f, PA_GCC_UNUSED void *userdata) { + ssize_t r; + int sig; + assert(a && e && f == PA_IO_EVENT_INPUT && e == io_event && fd == signal_pipe[0]); + + if ((r = pa_read(signal_pipe[0], &sig, sizeof(sig))) < 0) { + if (errno == EAGAIN) + return; + + pa_log(__FILE__": read(): %s", pa_cstrerror(errno)); + return; + } + + if (r != sizeof(sig)) { + pa_log(__FILE__": short read()"); + return; + } + + dispatch(a, sig); +} + +int pa_signal_init(pa_mainloop_api *a) { + + assert(!api && a && signal_pipe[0] == -1 && signal_pipe[1] == -1 && !io_event); + + if (pipe(signal_pipe) < 0) { + pa_log(__FILE__": pipe(): %s", pa_cstrerror(errno)); + return -1; + } + + pa_make_nonblock_fd(signal_pipe[0]); + pa_make_nonblock_fd(signal_pipe[1]); + pa_fd_set_cloexec(signal_pipe[0], 1); + pa_fd_set_cloexec(signal_pipe[1], 1); + + api = a; + + io_event = api->io_new(api, signal_pipe[0], PA_IO_EVENT_INPUT, callback, NULL); + assert(io_event); + + return 0; +} + +void pa_signal_done(void) { + assert(api && signal_pipe[0] >= 0 && signal_pipe[1] >= 0 && io_event); + + while (signals) + pa_signal_free(signals); + + api->io_free(io_event); + io_event = NULL; + + close(signal_pipe[0]); + close(signal_pipe[1]); + signal_pipe[0] = signal_pipe[1] = -1; + + api = NULL; +} + +pa_signal_event* pa_signal_new(int sig, void (*_callback) (pa_mainloop_api *api, pa_signal_event*e, int sig, void *userdata), void *userdata) { + pa_signal_event *e = NULL; + +#ifdef HAVE_SIGACTION + struct sigaction sa; +#endif + + assert(sig > 0 && _callback); + + for (e = signals; e; e = e->next) + if (e->sig == sig) + goto fail; + + e = pa_xmalloc(sizeof(pa_signal_event)); + e->sig = sig; + e->callback = _callback; + e->userdata = userdata; + e->destroy_callback = NULL; + +#ifdef HAVE_SIGACTION + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = signal_handler; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_RESTART; + + if (sigaction(sig, &sa, &e->saved_sigaction) < 0) +#else + if ((e->saved_handler = signal(sig, signal_handler)) == SIG_ERR) +#endif + goto fail; + + e->previous = NULL; + e->next = signals; + signals = e; + + return e; +fail: + if (e) + pa_xfree(e); + return NULL; +} + +void pa_signal_free(pa_signal_event *e) { + assert(e); + + if (e->next) + e->next->previous = e->previous; + if (e->previous) + e->previous->next = e->next; + else + signals = e->next; + +#ifdef HAVE_SIGACTION + sigaction(e->sig, &e->saved_sigaction, NULL); +#else + signal(e->sig, e->saved_handler); +#endif + + if (e->destroy_callback) + e->destroy_callback(api, e, e->userdata); + + pa_xfree(e); +} + +void pa_signal_set_destroy(pa_signal_event *e, void (*_callback) (pa_mainloop_api *api, pa_signal_event*e, void *userdata)) { + assert(e); + e->destroy_callback = _callback; +} diff --git a/src/pulse/mainloop-signal.h b/src/pulse/mainloop-signal.h new file mode 100644 index 00000000..0721c1f5 --- /dev/null +++ b/src/pulse/mainloop-signal.h @@ -0,0 +1,59 @@ +#ifndef foomainloopsignalhfoo +#define foomainloopsignalhfoo + +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + 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 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 +#include + +PA_C_DECL_BEGIN + +/** \file + * UNIX signal support for main loops. In contrast to other + * main loop event sources such as timer and IO events, UNIX signal + * support requires modification of the global process + * environment. Due to this the generic main loop abstraction layer as + * defined in \ref mainloop-api.h doesn't have direct support for UNIX + * signals. However, you may hook signal support into an abstract main loop via the routines defined herein. + */ + +/** Initialize the UNIX signal subsystem and bind it to the specified main loop */ +int pa_signal_init(pa_mainloop_api *api); + +/** Cleanup the signal subsystem */ +void pa_signal_done(void); + +/** An opaque UNIX signal event source object */ +typedef struct pa_signal_event pa_signal_event; + +/** Create a new UNIX signal event source object */ +pa_signal_event* pa_signal_new(int sig, void (*callback) (pa_mainloop_api *api, pa_signal_event*e, int sig, void *userdata), void *userdata); + +/** Free a UNIX signal event source object */ +void pa_signal_free(pa_signal_event *e); + +/** Set a function that is called when the signal event source is destroyed. Use this to free the userdata argument if required */ +void pa_signal_set_destroy(pa_signal_event *e, void (*callback) (pa_mainloop_api *api, pa_signal_event*e, void *userdata)); + +PA_C_DECL_END + +#endif diff --git a/src/pulse/mainloop.c b/src/pulse/mainloop.c new file mode 100644 index 00000000..32f1a845 --- /dev/null +++ b/src/pulse/mainloop.c @@ -0,0 +1,839 @@ +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + 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 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 +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_SYS_POLL_H +#include +#else +#include "../pulsecore/poll.h" +#endif + +#include "../pulsecore/winsock.h" + +#ifndef HAVE_PIPE +#include "../pulsecore/pipe.h" +#endif + +#include +#include +#include + +#include +#include +#include + +#include "mainloop.h" + +struct pa_io_event { + pa_mainloop *mainloop; + int dead; + int fd; + pa_io_event_flags_t events; + void (*callback) (pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata); + struct pollfd *pollfd; + void *userdata; + void (*destroy_callback) (pa_mainloop_api*a, pa_io_event *e, void *userdata); +}; + +struct pa_time_event { + pa_mainloop *mainloop; + int dead; + int enabled; + struct timeval timeval; + void (*callback)(pa_mainloop_api*a, pa_time_event *e, const struct timeval*tv, void *userdata); + void *userdata; + void (*destroy_callback) (pa_mainloop_api*a, pa_time_event *e, void *userdata); +}; + +struct pa_defer_event { + pa_mainloop *mainloop; + int dead; + int enabled; + void (*callback)(pa_mainloop_api*a, pa_defer_event*e, void *userdata); + void *userdata; + void (*destroy_callback) (pa_mainloop_api*a, pa_defer_event *e, void *userdata); +}; + +struct pa_mainloop { + pa_idxset *io_events, *time_events, *defer_events; + int io_events_scan_dead, defer_events_scan_dead, time_events_scan_dead; + + struct pollfd *pollfds; + unsigned max_pollfds, n_pollfds; + int rebuild_pollfds; + + int prepared_timeout; + + int quit, retval; + pa_mainloop_api api; + + int deferred_pending; + + int wakeup_pipe[2]; + + enum { + STATE_PASSIVE, + STATE_PREPARED, + STATE_POLLING, + STATE_POLLED, + STATE_QUIT + } state; + + pa_poll_func poll_func; + void *poll_func_userdata; +}; + +/* IO events */ +static pa_io_event* mainloop_io_new( + pa_mainloop_api*a, + int fd, + pa_io_event_flags_t events, + void (*callback) (pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_event_flags_t events, void *userdata), + void *userdata) { + + pa_mainloop *m; + pa_io_event *e; + + assert(a && a->userdata && fd >= 0 && callback); + m = a->userdata; + assert(a == &m->api); + + e = pa_xmalloc(sizeof(pa_io_event)); + e->mainloop = m; + e->dead = 0; + + e->fd = fd; + e->events = events; + e->callback = callback; + e->userdata = userdata; + e->destroy_callback = NULL; + e->pollfd = NULL; + +#ifdef OS_IS_WIN32 + { + fd_set xset; + struct timeval tv; + + tv.tv_sec = 0; + tv.tv_usec = 0; + + FD_ZERO (&xset); + FD_SET (fd, &xset); + + if ((select((SELECT_TYPE_ARG1) fd, NULL, NULL, SELECT_TYPE_ARG234 &xset, + SELECT_TYPE_ARG5 &tv) == -1) && + (WSAGetLastError() == WSAENOTSOCK)) { + pa_log_warn(__FILE__": WARNING: cannot monitor non-socket file descriptors."); + e->dead = 1; + } + } +#endif + + pa_idxset_put(m->io_events, e, NULL); + m->rebuild_pollfds = 1; + + pa_mainloop_wakeup(m); + + return e; +} + +static void mainloop_io_enable(pa_io_event *e, pa_io_event_flags_t events) { + assert(e && e->mainloop); + + e->events = events; + e->mainloop->rebuild_pollfds = 1; + + pa_mainloop_wakeup(e->mainloop); +} + +static void mainloop_io_free(pa_io_event *e) { + assert(e && e->mainloop); + + e->dead = e->mainloop->io_events_scan_dead = e->mainloop->rebuild_pollfds = 1; + + pa_mainloop_wakeup(e->mainloop); +} + +static void mainloop_io_set_destroy(pa_io_event *e, void (*callback)(pa_mainloop_api*a, pa_io_event *e, void *userdata)) { + assert(e); + e->destroy_callback = callback; +} + +/* Defer events */ +static pa_defer_event* mainloop_defer_new(pa_mainloop_api*a, void (*callback) (pa_mainloop_api*a, pa_defer_event *e, void *userdata), void *userdata) { + pa_mainloop *m; + pa_defer_event *e; + + assert(a && a->userdata && callback); + m = a->userdata; + assert(a == &m->api); + + e = pa_xmalloc(sizeof(pa_defer_event)); + e->mainloop = m; + e->dead = 0; + + e->enabled = 1; + e->callback = callback; + e->userdata = userdata; + e->destroy_callback = NULL; + + pa_idxset_put(m->defer_events, e, NULL); + + m->deferred_pending++; + + pa_mainloop_wakeup(e->mainloop); + + return e; +} + +static void mainloop_defer_enable(pa_defer_event *e, int b) { + assert(e); + + if (e->enabled && !b) { + assert(e->mainloop->deferred_pending > 0); + e->mainloop->deferred_pending--; + } else if (!e->enabled && b) { + e->mainloop->deferred_pending++; + pa_mainloop_wakeup(e->mainloop); + } + + e->enabled = b; +} + +static void mainloop_defer_free(pa_defer_event *e) { + assert(e); + e->dead = e->mainloop->defer_events_scan_dead = 1; + + if (e->enabled) { + e->enabled = 0; + assert(e->mainloop->deferred_pending > 0); + e->mainloop->deferred_pending--; + } +} + +static void mainloop_defer_set_destroy(pa_defer_event *e, void (*callback)(pa_mainloop_api*a, pa_defer_event *e, void *userdata)) { + assert(e); + e->destroy_callback = callback; +} + +/* Time events */ +static pa_time_event* mainloop_time_new(pa_mainloop_api*a, const struct timeval *tv, void (*callback) (pa_mainloop_api*a, pa_time_event*e, const struct timeval *tv, void *userdata), void *userdata) { + pa_mainloop *m; + pa_time_event *e; + + assert(a && a->userdata && callback); + m = a->userdata; + assert(a == &m->api); + + e = pa_xmalloc(sizeof(pa_time_event)); + e->mainloop = m; + e->dead = 0; + + e->enabled = !!tv; + if (tv) + e->timeval = *tv; + + e->callback = callback; + e->userdata = userdata; + e->destroy_callback = NULL; + + pa_idxset_put(m->time_events, e, NULL); + + if (e->enabled) + pa_mainloop_wakeup(m); + + return e; +} + +static void mainloop_time_restart(pa_time_event *e, const struct timeval *tv) { + assert(e); + + if (tv) { + e->enabled = 1; + e->timeval = *tv; + + pa_mainloop_wakeup(e->mainloop); + } else + e->enabled = 0; +} + +static void mainloop_time_free(pa_time_event *e) { + assert(e); + + e->dead = e->mainloop->time_events_scan_dead = 1; + + /* no wakeup needed here. Think about it! */ +} + +static void mainloop_time_set_destroy(pa_time_event *e, void (*callback)(pa_mainloop_api*a, pa_time_event *e, void *userdata)) { + assert(e); + e->destroy_callback = callback; +} + +/* quit() */ + +static void mainloop_quit(pa_mainloop_api*a, int retval) { + pa_mainloop *m; + assert(a && a->userdata); + m = a->userdata; + assert(a == &m->api); + + pa_mainloop_quit(m, 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, + + .time_new = mainloop_time_new, + .time_restart = mainloop_time_restart, + .time_free = mainloop_time_free, + .time_set_destroy = mainloop_time_set_destroy, + + .defer_new = mainloop_defer_new, + .defer_enable = mainloop_defer_enable, + .defer_free = mainloop_defer_free, + .defer_set_destroy = mainloop_defer_set_destroy, + + .quit = mainloop_quit, +}; + +pa_mainloop *pa_mainloop_new(void) { + pa_mainloop *m; + + m = pa_xmalloc(sizeof(pa_mainloop)); + + if (pipe(m->wakeup_pipe) < 0) { + pa_log_error(__FILE__": ERROR: cannot create wakeup pipe"); + pa_xfree(m); + return NULL; + } + + pa_make_nonblock_fd(m->wakeup_pipe[0]); + pa_make_nonblock_fd(m->wakeup_pipe[1]); + + m->io_events = pa_idxset_new(NULL, NULL); + m->defer_events = pa_idxset_new(NULL, NULL); + m->time_events = pa_idxset_new(NULL, NULL); + + assert(m->io_events && m->defer_events && m->time_events); + + m->io_events_scan_dead = m->defer_events_scan_dead = m->time_events_scan_dead = 0; + + m->pollfds = NULL; + m->max_pollfds = m->n_pollfds = 0; + m->rebuild_pollfds = 1; + + m->quit = m->retval = 0; + + m->api = vtable; + m->api.userdata = m; + + m->deferred_pending = 0; + + m->state = STATE_PASSIVE; + + m->poll_func = NULL; + m->poll_func_userdata = NULL; + + m->retval = -1; + + return m; +} + +static int io_foreach(void *p, uint32_t PA_GCC_UNUSED idx, int *del, void*userdata) { + pa_io_event *e = p; + int *all = userdata; + assert(e && del && all); + + if (!*all && !e->dead) + return 0; + + if (e->destroy_callback) + e->destroy_callback(&e->mainloop->api, e, e->userdata); + pa_xfree(e); + *del = 1; + return 0; +} + +static int time_foreach(void *p, uint32_t PA_GCC_UNUSED idx, int *del, void*userdata) { + pa_time_event *e = p; + int *all = userdata; + assert(e && del && all); + + if (!*all && !e->dead) + return 0; + + if (e->destroy_callback) + e->destroy_callback(&e->mainloop->api, e, e->userdata); + pa_xfree(e); + *del = 1; + return 0; +} + +static int defer_foreach(void *p, PA_GCC_UNUSED uint32_t idx, int *del, void*userdata) { + pa_defer_event *e = p; + int *all = userdata; + assert(e && del && all); + + if (!*all && !e->dead) + return 0; + + if (e->destroy_callback) + e->destroy_callback(&e->mainloop->api, e, e->userdata); + pa_xfree(e); + *del = 1; + return 0; +} + +void pa_mainloop_free(pa_mainloop* m) { + int all = 1; + assert(m); + + pa_idxset_foreach(m->io_events, io_foreach, &all); + pa_idxset_foreach(m->time_events, time_foreach, &all); + pa_idxset_foreach(m->defer_events, defer_foreach, &all); + + pa_idxset_free(m->io_events, NULL, NULL); + pa_idxset_free(m->time_events, NULL, NULL); + pa_idxset_free(m->defer_events, NULL, NULL); + + pa_xfree(m->pollfds); + + if (m->wakeup_pipe[0] >= 0) + close(m->wakeup_pipe[0]); + if (m->wakeup_pipe[1] >= 0) + close(m->wakeup_pipe[1]); + + pa_xfree(m); +} + +static void scan_dead(pa_mainloop *m) { + int all = 0; + assert(m); + + if (m->io_events_scan_dead) + pa_idxset_foreach(m->io_events, io_foreach, &all); + if (m->time_events_scan_dead) + pa_idxset_foreach(m->time_events, time_foreach, &all); + if (m->defer_events_scan_dead) + pa_idxset_foreach(m->defer_events, defer_foreach, &all); + + m->io_events_scan_dead = m->time_events_scan_dead = m->defer_events_scan_dead = 0; +} + +static void rebuild_pollfds(pa_mainloop *m) { + pa_io_event*e; + struct pollfd *p; + uint32_t idx = PA_IDXSET_INVALID; + unsigned l; + + l = pa_idxset_size(m->io_events) + 1; + if (m->max_pollfds < l) { + m->pollfds = pa_xrealloc(m->pollfds, sizeof(struct pollfd)*l); + m->max_pollfds = l; + } + + m->n_pollfds = 0; + p = m->pollfds; + + if (m->wakeup_pipe[0] >= 0) { + m->pollfds[0].fd = m->wakeup_pipe[0]; + m->pollfds[0].events = POLLIN; + m->pollfds[0].revents = 0; + p++; + m->n_pollfds++; + } + + for (e = pa_idxset_first(m->io_events, &idx); e; e = pa_idxset_next(m->io_events, &idx)) { + if (e->dead) { + e->pollfd = NULL; + continue; + } + + e->pollfd = p; + p->fd = e->fd; + p->events = + ((e->events & PA_IO_EVENT_INPUT) ? POLLIN : 0) | + ((e->events & PA_IO_EVENT_OUTPUT) ? POLLOUT : 0) | + POLLHUP | + POLLERR; + p->revents = 0; + + p++; + m->n_pollfds++; + } + + m->rebuild_pollfds = 0; +} + +static int dispatch_pollfds(pa_mainloop *m) { + uint32_t idx = PA_IDXSET_INVALID; + pa_io_event *e; + int r = 0; + + for (e = pa_idxset_first(m->io_events, &idx); e && !m->quit; e = pa_idxset_next(m->io_events, &idx)) { + if (e->dead || !e->pollfd || !e->pollfd->revents) + continue; + + assert(e->pollfd->fd == e->fd && e->callback); + e->callback(&m->api, e, e->fd, + (e->pollfd->revents & POLLHUP ? PA_IO_EVENT_HANGUP : 0) | + (e->pollfd->revents & POLLIN ? PA_IO_EVENT_INPUT : 0) | + (e->pollfd->revents & POLLOUT ? PA_IO_EVENT_OUTPUT : 0) | + (e->pollfd->revents & POLLERR ? PA_IO_EVENT_ERROR : 0), + e->userdata); + e->pollfd->revents = 0; + r++; + } + + return r; +} + +static int dispatch_defer(pa_mainloop *m) { + uint32_t idx; + pa_defer_event *e; + int r = 0; + + if (!m->deferred_pending) + return 0; + + for (e = pa_idxset_first(m->defer_events, &idx); e && !m->quit; e = pa_idxset_next(m->defer_events, &idx)) { + if (e->dead || !e->enabled) + continue; + + assert(e->callback); + e->callback(&m->api, e, e->userdata); + r++; + } + + return r; +} + +static int calc_next_timeout(pa_mainloop *m) { + uint32_t idx; + pa_time_event *e; + struct timeval now; + int t = -1; + int got_time = 0; + + if (pa_idxset_isempty(m->time_events)) + return -1; + + for (e = pa_idxset_first(m->time_events, &idx); e; e = pa_idxset_next(m->time_events, &idx)) { + int tmp; + + if (e->dead || !e->enabled) + continue; + + /* Let's save a system call */ + if (!got_time) { + pa_gettimeofday(&now); + got_time = 1; + } + + if (e->timeval.tv_sec < now.tv_sec || (e->timeval.tv_sec == now.tv_sec && e->timeval.tv_usec <= now.tv_usec)) + return 0; + + tmp = (e->timeval.tv_sec - now.tv_sec)*1000; + + if (e->timeval.tv_usec > now.tv_usec) + tmp += (e->timeval.tv_usec - now.tv_usec)/1000; + else + tmp -= (now.tv_usec - e->timeval.tv_usec)/1000; + + if (tmp == 0) + return 0; + else if (t == -1 || tmp < t) + t = tmp; + } + + return t; +} + +static int dispatch_timeout(pa_mainloop *m) { + uint32_t idx; + pa_time_event *e; + struct timeval now; + int got_time = 0; + int r = 0; + assert(m); + + if (pa_idxset_isempty(m->time_events)) + return 0; + + for (e = pa_idxset_first(m->time_events, &idx); e && !m->quit; e = pa_idxset_next(m->time_events, &idx)) { + + if (e->dead || !e->enabled) + continue; + + /* Let's save a system call */ + if (!got_time) { + pa_gettimeofday(&now); + got_time = 1; + } + + if (e->timeval.tv_sec < now.tv_sec || (e->timeval.tv_sec == now.tv_sec && e->timeval.tv_usec <= now.tv_usec)) { + assert(e->callback); + + e->enabled = 0; + e->callback(&m->api, e, &e->timeval, e->userdata); + + r++; + } + } + + return r; +} + +void pa_mainloop_wakeup(pa_mainloop *m) { + char c = 'W'; + assert(m); + + if (m->wakeup_pipe[1] >= 0) + pa_write(m->wakeup_pipe[1], &c, sizeof(c)); +} + +static void clear_wakeup(pa_mainloop *m) { + char c[10]; + + assert(m); + + if (m->wakeup_pipe[0] < 0) + return; + + while (pa_read(m->wakeup_pipe[0], &c, sizeof(c)) == sizeof(c)); +} + +int pa_mainloop_prepare(pa_mainloop *m, int timeout) { + assert(m); + assert(m->state == STATE_PASSIVE); + + clear_wakeup(m); + scan_dead(m); + + if (m->quit) + goto quit; + + if (!m->deferred_pending) { + + if (m->rebuild_pollfds) + rebuild_pollfds(m); + + m->prepared_timeout = calc_next_timeout(m); + if (timeout >= 0 && (timeout < m->prepared_timeout || m->prepared_timeout < 0)) + m->prepared_timeout = timeout; + } + + m->state = STATE_PREPARED; + return 0; + +quit: + m->state = STATE_QUIT; + return -2; +} + +int pa_mainloop_poll(pa_mainloop *m) { + int r; + + assert(m); + assert(m->state == STATE_PREPARED); + + if (m->quit) + goto quit; + + m->state = STATE_POLLING; + + if (m->deferred_pending) + r = 0; + else { + if (m->poll_func) + r = m->poll_func(m->pollfds, m->n_pollfds, m->prepared_timeout, m->poll_func_userdata); + else + r = poll(m->pollfds, m->n_pollfds, m->prepared_timeout); + + if (r < 0) { + if (errno == EINTR) + r = 0; + else + pa_log(__FILE__": poll(): %s", pa_cstrerror(errno)); + } + } + + m->state = r < 0 ? STATE_PASSIVE : STATE_POLLED; + return r; + +quit: + m->state = STATE_QUIT; + return -2; +} + +int pa_mainloop_dispatch(pa_mainloop *m) { + int dispatched = 0; + + assert(m); + assert(m->state == STATE_POLLED); + + if (m->quit) + goto quit; + + if (m->deferred_pending) + dispatched += dispatch_defer(m); + else { + dispatched += dispatch_timeout(m); + + if (m->quit) + goto quit; + + dispatched += dispatch_pollfds(m); + + } + + if (m->quit) + goto quit; + + m->state = STATE_PASSIVE; + + return dispatched; + +quit: + m->state = STATE_QUIT; + return -2; +} + +int pa_mainloop_get_retval(pa_mainloop *m) { + assert(m); + return m->retval; +} + +int pa_mainloop_iterate(pa_mainloop *m, int block, int *retval) { + int r; + assert(m); + + if ((r = pa_mainloop_prepare(m, block ? -1 : 0)) < 0) + goto quit; + + if ((r = pa_mainloop_poll(m)) < 0) + goto quit; + + if ((r = pa_mainloop_dispatch(m)) < 0) + goto quit; + + return r; + +quit: + + if ((r == -2) && retval) + *retval = pa_mainloop_get_retval(m); + return r; +} + +int pa_mainloop_run(pa_mainloop *m, int *retval) { + int r; + + while ((r = pa_mainloop_iterate(m, 1, retval)) >= 0); + + if (r == -2) + return 1; + else if (r < 0) + return -1; + else + return 0; +} + +void pa_mainloop_quit(pa_mainloop *m, int retval) { + assert(m); + + m->quit = 1; + m->retval = retval; + pa_mainloop_wakeup(m); +} + +pa_mainloop_api* pa_mainloop_get_api(pa_mainloop*m) { + assert(m); + return &m->api; +} + +void pa_mainloop_set_poll_func(pa_mainloop *m, pa_poll_func poll_func, void *userdata) { + assert(m); + + m->poll_func = poll_func; + m->poll_func_userdata = userdata; +} + + +#if 0 +void pa_mainloop_dump(pa_mainloop *m) { + assert(m); + + pa_log(__FILE__": Dumping mainloop sources START"); + + { + uint32_t idx = PA_IDXSET_INVALID; + pa_io_event *e; + for (e = pa_idxset_first(m->io_events, &idx); e; e = pa_idxset_next(m->io_events, &idx)) { + if (e->dead) + continue; + + pa_log(__FILE__": kind=io fd=%i events=%i callback=%p userdata=%p", e->fd, (int) e->events, (void*) e->callback, (void*) e->userdata); + } + } + { + uint32_t idx = PA_IDXSET_INVALID; + pa_defer_event *e; + for (e = pa_idxset_first(m->defer_events, &idx); e; e = pa_idxset_next(m->defer_events, &idx)) { + if (e->dead) + continue; + + pa_log(__FILE__": kind=defer enabled=%i callback=%p userdata=%p", e->enabled, (void*) e->callback, (void*) e->userdata); + } + } + { + uint32_t idx = PA_IDXSET_INVALID; + pa_time_event *e; + for (e = pa_idxset_first(m->time_events, &idx); e; e = pa_idxset_next(m->time_events, &idx)) { + if (e->dead) + continue; + + pa_log(__FILE__": kind=time enabled=%i time=%lu.%lu callback=%p userdata=%p", e->enabled, (unsigned long) e->timeval.tv_sec, (unsigned long) e->timeval.tv_usec, (void*) e->callback, (void*) e->userdata); + } + } + + pa_log(__FILE__": Dumping mainloop sources STOP"); + +} +#endif diff --git a/src/pulse/mainloop.h b/src/pulse/mainloop.h new file mode 100644 index 00000000..8abd8fe4 --- /dev/null +++ b/src/pulse/mainloop.h @@ -0,0 +1,127 @@ +#ifndef foomainloophfoo +#define foomainloophfoo + +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + 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 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 +#include + +PA_C_DECL_BEGIN + +struct pollfd; + +/** \page mainloop Main Loop + * + * \section overv_sec Overview + * + * The built-in main loop implementation is based on the poll() system call. + * It supports the functions defined in the main loop abstraction and very + * little else. + * + * The main loop is created using pa_mainloop_new() and destroyed using + * pa_mainloop_free(). To get access to the main loop abstraction, + * pa_mainloop_get_api() is used. + * + * \section iter_sec Iteration + * + * The main loop is designed around the concept of iterations. Each iteration + * consists of three steps that repeat during the application's entire + * lifetime: + * + * -# Prepare - Build a list of file descriptors + * that need to be monitored and calculate the next timeout. + * -# Poll - Execute the actuall poll() system call. + * -# Dispatch - Dispatch any events that have fired. + * + * When using the main loop, the application can either execute each + * iteration, one at a time, using pa_mainloop_iterate(), or let the library + * iterate automatically using pa_mainloop_run(). + * + * \section thread_sec Threads + * + * The main loop functions are designed to be thread safe, but the objects + * are not. What this means is that multiple main loops can be used, but only + * one object per thread. + * + */ + +/** \file + * + * A minimal main loop implementation based on the C library's poll() + * function. Using the routines defined herein you may create a simple + * main loop supporting the generic main loop abstraction layer as + * defined in \ref mainloop-api.h. This implementation is thread safe + * as long as you access the main loop object from a single thread only.*/ + +/** An opaque main loop object */ +typedef struct pa_mainloop pa_mainloop; + +/** Allocate a new main loop object */ +pa_mainloop *pa_mainloop_new(void); + +/** Free a main loop object */ +void pa_mainloop_free(pa_mainloop* m); + +/** Prepare for a single iteration of the main loop. Returns a negative value +on error or exit request. timeout specifies a maximum timeout for the subsequent +poll, or -1 for blocking behaviour. .*/ +int pa_mainloop_prepare(pa_mainloop *m, int timeout); + +/** Execute the previously prepared poll. Returns a negative value on error.*/ +int pa_mainloop_poll(pa_mainloop *m); + +/** Dispatch timeout, io and deferred events from the previously executed poll. Returns +a negative value on error. On success returns the number of source dispatched. */ +int pa_mainloop_dispatch(pa_mainloop *m); + +/** Return the return value as specified with the main loop's quit() routine. */ +int pa_mainloop_get_retval(pa_mainloop *m); + +/** Run a single iteration of the main loop. This is a convenience function +for pa_mainloop_prepare(), pa_mainloop_poll() and pa_mainloop_dispatch(). +Returns a negative value on error or exit request. If block is nonzero, +block for events if none are queued. Optionally return the return value as +specified with the main loop's quit() routine in the integer variable retval points +to. On success returns the number of sources dispatched in this iteration. */ +int pa_mainloop_iterate(pa_mainloop *m, int block, int *retval); + +/** Run unlimited iterations of the main loop object until the main loop's quit() routine is called. */ +int pa_mainloop_run(pa_mainloop *m, int *retval); + +/** Return the abstract main loop abstraction layer vtable for this main loop. */ +pa_mainloop_api* pa_mainloop_get_api(pa_mainloop*m); + +/** Shutdown the main loop */ +void pa_mainloop_quit(pa_mainloop *m, int r); + +/** Interrupt a running poll (for threaded systems) */ +void pa_mainloop_wakeup(pa_mainloop *m); + +/** Generic prototype of a poll() like function */ +typedef int (*pa_poll_func)(struct pollfd *ufds, unsigned long nfds, int timeout, void*userdata); + +/** Change the poll() implementation */ +void pa_mainloop_set_poll_func(pa_mainloop *m, pa_poll_func poll_func, void *userdata); + +PA_C_DECL_END + +#endif diff --git a/src/pulse/operation.c b/src/pulse/operation.c new file mode 100644 index 00000000..24ddf69f --- /dev/null +++ b/src/pulse/operation.c @@ -0,0 +1,116 @@ +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + 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 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 +#endif + +#include + +#include + +#include "internal.h" +#include "operation.h" + +pa_operation *pa_operation_new(pa_context *c, pa_stream *s, pa_operation_cb_t cb, void *userdata) { + pa_operation *o; + assert(c); + + o = pa_xnew(pa_operation, 1); + o->ref = 1; + o->context = c; + o->stream = s; + + o->state = PA_OPERATION_RUNNING; + o->callback = cb; + o->userdata = userdata; + + /* Refcounting is strictly one-way: from the "bigger" to the "smaller" object. */ + PA_LLIST_PREPEND(pa_operation, c->operations, o); + pa_operation_ref(o); + + return o; +} + +pa_operation *pa_operation_ref(pa_operation *o) { + assert(o); + assert(o->ref >= 1); + + o->ref++; + return o; +} + +void pa_operation_unref(pa_operation *o) { + assert(o); + assert(o->ref >= 1); + + if ((--(o->ref)) == 0) { + assert(!o->context); + assert(!o->stream); + pa_xfree(o); + } +} + +static void operation_set_state(pa_operation *o, pa_operation_state_t st) { + assert(o); + assert(o->ref >= 1); + + if (st == o->state) + return; + + o->state = st; + + if ((o->state == PA_OPERATION_DONE) || (o->state == PA_OPERATION_CANCELED)) { + + if (o->context) { + assert(o->ref >= 2); + + PA_LLIST_REMOVE(pa_operation, o->context->operations, o); + pa_operation_unref(o); + } + + o->context = NULL; + o->stream = NULL; + o->callback = NULL; + o->userdata = NULL; + } +} + +void pa_operation_cancel(pa_operation *o) { + assert(o); + assert(o->ref >= 1); + + operation_set_state(o, PA_OPERATION_CANCELED); +} + +void pa_operation_done(pa_operation *o) { + assert(o); + assert(o->ref >= 1); + + operation_set_state(o, PA_OPERATION_DONE); +} + +pa_operation_state_t pa_operation_get_state(pa_operation *o) { + assert(o); + assert(o->ref >= 1); + + return o->state; +} diff --git a/src/pulse/operation.h b/src/pulse/operation.h new file mode 100644 index 00000000..b544e08e --- /dev/null +++ b/src/pulse/operation.h @@ -0,0 +1,50 @@ +#ifndef foooperationhfoo +#define foooperationhfoo + +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + 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 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 +#include + +/** \file + * Asynchronous operations */ + +PA_C_DECL_BEGIN + +/** An asynchronous operation object */ +typedef struct pa_operation pa_operation; + +/** Increase the reference count by one */ +pa_operation *pa_operation_ref(pa_operation *o); + +/** Decrease the reference count by one */ +void pa_operation_unref(pa_operation *o); + +/** Cancel the operation. Beware! This will not necessarily cancel the execution of the operation on the server side. */ +void pa_operation_cancel(pa_operation *o); + +/** Return the current status of the operation */ +pa_operation_state_t pa_operation_get_state(pa_operation *o); + +PA_C_DECL_END + +#endif diff --git a/src/pulse/polypaudio.h b/src/pulse/polypaudio.h new file mode 100644 index 00000000..5bbd4cc5 --- /dev/null +++ b/src/pulse/polypaudio.h @@ -0,0 +1,115 @@ +#ifndef foopulseaudiohfoo +#define foopulseaudiohfoo + +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** \file + * Include all pulselib header files at once. The following + * files are included: \ref mainloop-api.h, \ref sample.h, \ref def.h, + * \ref context.h, \ref stream.h, \ref introspect.h, \ref subscribe.h, + * \ref scache.h, \ref version.h, \ref error.h, \ref channelmap.h, + * \ref operation.h,\ref volume.h, \ref xmalloc.h, \ref utf8.h, \ref + * thread-mainloop.h, \ref mainloop.h, \ref util.h, \ref timeval.h and + * \ref mainloop-signal.h at once */ + +/** \mainpage + * + * \section intro_sec Introduction + * + * This document describes the client API for the pulseaudio sound + * server. The API comes in two flavours to accomodate different styles + * of applications and different needs in complexity: + * + * \li The complete but somewhat complicated to use asynchronous API + * \li The simplified, easy to use, but limited synchronous API + * + * All strings in Polypaudio are in the UTF-8 encoding, regardless of current + * locale. Some functions will filter invalid sequences from the string, some + * will simply fail. To ensure reliable behaviour, make sure everything you + * pass to the API is already in UTF-8. + + * \section simple_sec Simple API + * + * Use this if you develop your program in synchronous style and just + * need a way to play or record data on the sound server. See + * \subpage simple for more details. + * + * \section async_sec Asynchronous API + * + * Use this if you develop your programs in asynchronous, event loop + * based style or if you want to use the advanced features of the + * pulseaudio API. A guide can be found in \subpage async. + * + * By using the built-in threaded main loop, it is possible to acheive a + * pseudo-synchronous API, which can be useful in synchronous applications + * where the simple API is insufficient. See the \ref async page for + * details. + * + * \section thread_sec Threads + * + * The pulseaudio client libraries are not designed to be used in a + * heavily threaded environment. They are however designed to be reentrant + * safe. + * + * To use a the libraries in a threaded environment, you must assure that + * all objects are only used in one thread at a time. Normally, this means + * that all objects belonging to a single context must be accessed from the + * same thread. + * + * The included main loop implementation is also not thread safe. Take care + * to make sure event lists are not manipulated when any other code is + * using the main loop. + * + * \section pkgconfig pkg-config + * + * The pulseaudio libraries provide pkg-config snippets for the different + * modules: + * + * \li pulselib - The asynchronous API and the internal main loop + * implementation. + * \li pulselib-glib12-mainloop - GLIB 1.2 main loop bindings. + * \li pulselib-glib-mainloop - GLIB 2.x main loop bindings. + * \li pulselib-simple - The simple pulseaudio API. + */ + +#endif diff --git a/src/pulse/sample.c b/src/pulse/sample.c new file mode 100644 index 00000000..2e055bf1 --- /dev/null +++ b/src/pulse/sample.c @@ -0,0 +1,156 @@ +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + 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 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 +#endif + +#include +#include +#include +#include + +#include "sample.h" + +size_t pa_sample_size(const pa_sample_spec *spec) { + assert(spec); + + switch (spec->format) { + case PA_SAMPLE_U8: + case PA_SAMPLE_ULAW: + case PA_SAMPLE_ALAW: + return 1; + case PA_SAMPLE_S16LE: + case PA_SAMPLE_S16BE: + return 2; + case PA_SAMPLE_FLOAT32LE: + case PA_SAMPLE_FLOAT32BE: + return 4; + default: + assert(0); + return 0; + } +} + +size_t pa_frame_size(const pa_sample_spec *spec) { + assert(spec); + + return pa_sample_size(spec) * spec->channels; +} + +size_t pa_bytes_per_second(const pa_sample_spec *spec) { + assert(spec); + return spec->rate*pa_frame_size(spec); +} + +pa_usec_t pa_bytes_to_usec(uint64_t length, const pa_sample_spec *spec) { + assert(spec); + + return (pa_usec_t) (((double) length/pa_frame_size(spec)*1000000)/spec->rate); +} + +size_t pa_usec_to_bytes(pa_usec_t t, const pa_sample_spec *spec) { + assert(spec); + + return ((double) t * spec->rate / 1000000)*pa_frame_size(spec); +} + +int pa_sample_spec_valid(const pa_sample_spec *spec) { + assert(spec); + + if (spec->rate <= 0 || + spec->channels <= 0 || + spec->channels > PA_CHANNELS_MAX || + spec->format >= PA_SAMPLE_MAX || + spec->format < 0) + return 0; + + return 1; +} + +int pa_sample_spec_equal(const pa_sample_spec*a, const pa_sample_spec*b) { + assert(a && b); + + return (a->format == b->format) && (a->rate == b->rate) && (a->channels == b->channels); +} + +const char *pa_sample_format_to_string(pa_sample_format_t f) { + static const char* const table[]= { + [PA_SAMPLE_U8] = "u8", + [PA_SAMPLE_ALAW] = "aLaw", + [PA_SAMPLE_ULAW] = "uLaw", + [PA_SAMPLE_S16LE] = "s16le", + [PA_SAMPLE_S16BE] = "s16be", + [PA_SAMPLE_FLOAT32LE] = "float32le", + [PA_SAMPLE_FLOAT32BE] = "float32be", + }; + + if (f >= PA_SAMPLE_MAX) + return NULL; + + return table[f]; +} + +char *pa_sample_spec_snprint(char *s, size_t l, const pa_sample_spec *spec) { + assert(s && l && spec); + + if (!pa_sample_spec_valid(spec)) + snprintf(s, l, "Invalid"); + else + snprintf(s, l, "%s %uch %uHz", pa_sample_format_to_string(spec->format), spec->channels, spec->rate); + + return s; +} + +void pa_bytes_snprint(char *s, size_t l, unsigned v) { + if (v >= ((unsigned) 1024)*1024*1024) + snprintf(s, l, "%0.1f GiB", ((double) v)/1024/1024/1024); + else if (v >= ((unsigned) 1024)*1024) + snprintf(s, l, "%0.1f MiB", ((double) v)/1024/1024); + else if (v >= (unsigned) 1024) + snprintf(s, l, "%0.1f KiB", ((double) v)/1024); + else + snprintf(s, l, "%u B", (unsigned) v); +} + +pa_sample_format_t pa_parse_sample_format(const char *format) { + + if (strcasecmp(format, "s16le") == 0) + return PA_SAMPLE_S16LE; + else if (strcasecmp(format, "s16be") == 0) + return PA_SAMPLE_S16BE; + else if (strcasecmp(format, "s16ne") == 0 || strcasecmp(format, "s16") == 0 || strcasecmp(format, "16") == 0) + return PA_SAMPLE_S16NE; + else if (strcasecmp(format, "u8") == 0 || strcasecmp(format, "8") == 0) + return PA_SAMPLE_U8; + else if (strcasecmp(format, "float32") == 0 || strcasecmp(format, "float32ne") == 0) + return PA_SAMPLE_FLOAT32; + else if (strcasecmp(format, "float32le") == 0) + return PA_SAMPLE_FLOAT32LE; + else if (strcasecmp(format, "float32be") == 0) + return PA_SAMPLE_FLOAT32BE; + else if (strcasecmp(format, "ulaw") == 0) + return PA_SAMPLE_ULAW; + else if (strcasecmp(format, "alaw") == 0) + return PA_SAMPLE_ALAW; + + return -1; +} diff --git a/src/pulse/sample.h b/src/pulse/sample.h new file mode 100644 index 00000000..848fd16d --- /dev/null +++ b/src/pulse/sample.h @@ -0,0 +1,189 @@ +#ifndef foosamplehfoo +#define foosamplehfoo + +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + 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 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 +#include +#include + +#include + +/** \page sample Sample Format Specifications + * + * \section overv_sec Overview + * + * Polypaudio is capable of handling a multitude of sample formats, rates + * and channels, transparently converting and mixing them as needed. + * + * \section format_sec Sample Format + * + * Polypaudio supports the following sample formats: + * + * \li PA_SAMPLE_U8 - Unsigned 8 bit PCM. + * \li PA_SAMPLE_S16LE - Signed 16 bit PCM, little endian. + * \li PA_SAMPLE_S16BE - Signed 16 bit PCM, big endian. + * \li PA_SAMPLE_FLOAT32LE - 32 bit IEEE floating point PCM, little endian. + * \li PA_SAMPLE_FLOAT32BE - 32 bit IEEE floating point PCM, big endian. + * \li PA_SAMPLE_ALAW - 8 bit a-Law. + * \li PA_SAMPLE_ULAW - 8 bit mu-Law. + * + * The floating point sample formats have the range from -1 to 1. + * + * The sample formats that are sensitive to endianness have convenience + * macros for native endian (NE), and reverse endian (RE). + * + * \section rate_sec Sample Rates + * + * Polypaudio supports any sample rate between 1 Hz and 4 GHz. There is no + * point trying to exceed the sample rate of the output device though as the + * signal will only get downsampled, consuming CPU on the machine running the + * server. + * + * \section chan_sec Channels + * + * Polypaudio supports up to 16 individiual channels. The order of the + * channels is up to the application, but they must be continous. To map + * channels to speakers, see \ref channelmap. + * + * \section calc_sec Calculations + * + * The Polypaudio library contains a number of convenience functions to do + * calculations on sample formats: + * + * \li pa_bytes_per_second() - The number of bytes one second of audio will + * take given a sample format. + * \li pa_frame_size() - The size, in bytes, of one frame (i.e. one set of + * samples, one for each channel). + * \li pa_sample_size() - The size, in bytes, of one sample. + * \li pa_bytes_to_usec() - Calculate the time it would take to play a buffer + * of a certain size. + * + * \section util_sec Convenience Functions + * + * The library also contains a couple of other convenience functions: + * + * \li pa_sample_spec_valid() - Tests if a sample format specification is + * valid. + * \li pa_sample_spec_equal() - Tests if the sample format specifications are + * identical. + * \li pa_sample_format_to_string() - Return a textual description of a + * sample format. + * \li pa_parse_sample_format() - Parse a text string into a sample format. + * \li pa_sample_spec_snprint() - Create a textual description of a complete + * sample format specification. + * \li pa_bytes_snprint() - Pretty print a byte value (e.g. 2.5 MiB). + */ + +/** \file + * Constants and routines for sample type handling */ + +PA_C_DECL_BEGIN + +/** Maximum number of allowed channels */ +#define PA_CHANNELS_MAX 32 + +/** Sample format */ +typedef enum pa_sample_format { + PA_SAMPLE_U8, /**< Unsigned 8 Bit PCM */ + PA_SAMPLE_ALAW, /**< 8 Bit a-Law */ + PA_SAMPLE_ULAW, /**< 8 Bit mu-Law */ + PA_SAMPLE_S16LE, /**< Signed 16 Bit PCM, little endian (PC) */ + PA_SAMPLE_S16BE, /**< Signed 16 Bit PCM, big endian */ + PA_SAMPLE_FLOAT32LE, /**< 32 Bit IEEE floating point, little endian, range -1 to 1 */ + PA_SAMPLE_FLOAT32BE, /**< 32 Bit IEEE floating point, big endian, range -1 to 1 */ + PA_SAMPLE_MAX, /**< Upper limit of valid sample types */ + PA_SAMPLE_INVALID = -1 /**< An invalid value */ +} pa_sample_format_t; + +#ifdef WORDS_BIGENDIAN +/** Signed 16 Bit PCM, native endian */ +#define PA_SAMPLE_S16NE PA_SAMPLE_S16BE +/** 32 Bit IEEE floating point, native endian */ +#define PA_SAMPLE_FLOAT32NE PA_SAMPLE_FLOAT32BE +/** Signed 16 Bit PCM reverse endian */ +#define PA_SAMPLE_S16RE PA_SAMPLE_S16LE +/** 32 Bit IEEE floating point, reverse endian */ +#define PA_SAMPLE_FLOAT32RE PA_SAMPLE_FLOAT32LE +#else +/** Signed 16 Bit PCM, native endian */ +#define PA_SAMPLE_S16NE PA_SAMPLE_S16LE +/** 32 Bit IEEE floating point, native endian */ +#define PA_SAMPLE_FLOAT32NE PA_SAMPLE_FLOAT32LE +/** Signed 16 Bit PCM reverse endian */ +#define PA_SAMPLE_S16RE PA_SAMPLE_S16BE +/** 32 Bit IEEE floating point, reverse endian */ +#define PA_SAMPLE_FLOAT32RE PA_SAMPLE_FLOAT32BE +#endif + +/** A Shortcut for PA_SAMPLE_FLOAT32NE */ +#define PA_SAMPLE_FLOAT32 PA_SAMPLE_FLOAT32NE + +/** A sample format and attribute specification */ +typedef struct pa_sample_spec { + pa_sample_format_t format; /**< The sample format */ + uint32_t rate; /**< The sample rate. (e.g. 44100) */ + uint8_t channels; /**< Audio channels. (1 for mono, 2 for stereo, ...) */ +} pa_sample_spec; + +/** Type for usec specifications (unsigned). May be either 32 or 64 bit, depending on the architecture */ +typedef uint64_t pa_usec_t; + +/** Return the amount of bytes playback of a second of audio with the specified sample type takes */ +size_t pa_bytes_per_second(const pa_sample_spec *spec); + +/** Return the size of a frame with the specific sample type */ +size_t pa_frame_size(const pa_sample_spec *spec); + +/** Return the size of a sample with the specific sample type */ +size_t pa_sample_size(const pa_sample_spec *spec); + +/** Calculate the time the specified bytes take to play with the specified sample type */ +pa_usec_t pa_bytes_to_usec(uint64_t length, const pa_sample_spec *spec); + +/** Calculates the number of bytes that are required for the specified time. \since 0.9 */ +size_t pa_usec_to_bytes(pa_usec_t t, const pa_sample_spec *spec); + +/** Return non-zero when the sample type specification is valid */ +int pa_sample_spec_valid(const pa_sample_spec *spec); + +/** Return non-zero when the two sample type specifications match */ +int pa_sample_spec_equal(const pa_sample_spec*a, const pa_sample_spec*b); + +/** Return a descriptive string for the specified sample format. \since 0.8 */ +const char *pa_sample_format_to_string(pa_sample_format_t f); + +/** Parse a sample format text. Inverse of pa_sample_format_to_string() */ +pa_sample_format_t pa_parse_sample_format(const char *format); + +/** Maximum required string length for pa_sample_spec_snprint() */ +#define PA_SAMPLE_SPEC_SNPRINT_MAX 32 + +/** Pretty print a sample type specification to a string */ +char* pa_sample_spec_snprint(char *s, size_t l, const pa_sample_spec *spec); + +/** Pretty print a byte size value. (i.e. "2.5 MiB") */ +void pa_bytes_snprint(char *s, size_t l, unsigned v); + +PA_C_DECL_END + +#endif diff --git a/src/pulse/scache.c b/src/pulse/scache.c new file mode 100644 index 00000000..5d29c5b3 --- /dev/null +++ b/src/pulse/scache.c @@ -0,0 +1,131 @@ +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + 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 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 +#endif + +#include +#include +#include +#include + +#include + +#include "internal.h" + +#include "scache.h" + +int pa_stream_connect_upload(pa_stream *s, size_t length) { + pa_tagstruct *t; + uint32_t tag; + + assert(s); + + PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_UNCONNECTED, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY(s->context, length > 0, PA_ERR_INVALID); + + pa_stream_ref(s); + + s->direction = PA_STREAM_UPLOAD; + + t = pa_tagstruct_command(s->context, PA_COMMAND_CREATE_UPLOAD_STREAM, &tag); + pa_tagstruct_puts(t, s->name); + pa_tagstruct_put_sample_spec(t, &s->sample_spec); + pa_tagstruct_put_channel_map(t, &s->channel_map); + pa_tagstruct_putu32(t, length); + pa_pstream_send_tagstruct(s->context->pstream, t); + pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_create_stream_callback, s, NULL); + + pa_stream_set_state(s, PA_STREAM_CREATING); + + pa_stream_unref(s); + return 0; +} + +int pa_stream_finish_upload(pa_stream *s) { + pa_tagstruct *t; + uint32_t tag; + assert(s); + + PA_CHECK_VALIDITY(s->context, s->channel_valid, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY(s->context, s->context->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + + pa_stream_ref(s); + + t = pa_tagstruct_command(s->context, PA_COMMAND_FINISH_UPLOAD_STREAM, &tag); + pa_tagstruct_putu32(t, s->channel); + pa_pstream_send_tagstruct(s->context->pstream, t); + pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_disconnect_callback, s, NULL); + + pa_stream_unref(s); + return 0; +} + +pa_operation *pa_context_play_sample(pa_context *c, const char *name, const char *dev, pa_volume_t volume, pa_context_success_cb_t cb, void *userdata) { + pa_operation *o; + pa_tagstruct *t; + uint32_t tag; + + assert(c); + assert(c->ref >= 1); + + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(c, name && *name, PA_ERR_INVALID); + PA_CHECK_VALIDITY_RETURN_NULL(c, !dev || *dev, PA_ERR_INVALID); + + o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); + + if (!dev) + dev = c->conf->default_sink; + + t = pa_tagstruct_command(c, PA_COMMAND_PLAY_SAMPLE, &tag); + pa_tagstruct_putu32(t, PA_INVALID_INDEX); + pa_tagstruct_puts(t, dev); + pa_tagstruct_putu32(t, volume); + pa_tagstruct_puts(t, name); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); + + return o; +} + +pa_operation* pa_context_remove_sample(pa_context *c, const char *name, pa_context_success_cb_t cb, void *userdata) { + pa_operation *o; + pa_tagstruct *t; + uint32_t tag; + + assert(c); + assert(c->ref >= 1); + + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(c, name && *name, PA_ERR_INVALID); + + o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); + + t = pa_tagstruct_command(c, PA_COMMAND_REMOVE_SAMPLE, &tag); + pa_tagstruct_puts(t, name); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); + + return o; +} + diff --git a/src/pulse/scache.h b/src/pulse/scache.h new file mode 100644 index 00000000..e32703d4 --- /dev/null +++ b/src/pulse/scache.h @@ -0,0 +1,100 @@ +#ifndef fooscachehfoo +#define fooscachehfoo + +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + 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 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 + +#include +#include +#include + +/** \page scache Sample Cache + * + * \section overv_sec Overview + * + * The sample cache provides a simple way of overcoming high network latencies + * and reducing bandwidth. Instead of streaming a sound precisely when it + * should be played, it is stored on the server and only the command to start + * playing it needs to be sent. + * + * \section create_sec Creation + * + * To create a sample, the normal stream API is used (see \ref streams). The + * function pa_stream_connect_upload() will make sure the stream is stored as + * a sample on the server. + * + * To complete the upload, pa_stream_finish_upload() is called and the sample + * will receive the same name as the stream. If the upload should be aborted, + * simply call pa_stream_disconnect(). + * + * \section play_sec Playing samples + * + * To play back a sample, simply call pa_context_play_sample(): + * + * \code + * pa_operation *o; + * + * o = pa_context_play_sample(my_context, + * "sample2", // Name of my sample + * NULL, // Use default sink + * PA_VOLUME_NORM, // Full volume + * NULL, // Don't need a callback + * NULL + * ); + * if (o) + * pa_operation_unref(o); + * \endcode + * + * \section rem_sec Removing samples + * + * When a sample is no longer needed, it should be removed on the server to + * save resources. The sample is deleted using pa_context_remove_sample(). + */ + +/** \file + * All sample cache related routines */ + +PA_C_DECL_BEGIN + +/** Make this stream a sample upload stream */ +int pa_stream_connect_upload(pa_stream *s, size_t length); + +/** Finish the sample upload, the stream name will become the sample name. You cancel a samp + * le upload by issuing pa_stream_disconnect() */ +int pa_stream_finish_upload(pa_stream *s); + +/** Play a sample from the sample cache to the specified device. If the latter is NULL use the default sink. Returns an operation object */ +pa_operation* pa_context_play_sample( + pa_context *c /**< Context */, + const char *name /**< Name of the sample to play */, + const char *dev /**< Sink to play this sample on */, + pa_volume_t volume /**< Volume to play this sample with */ , + pa_context_success_cb_t cb /**< Call this function after successfully starting playback, or NULL */, + void *userdata /**< Userdata to pass to the callback */); + +/** Remove a sample from the sample cache. Returns an operation object which may be used to cancel the operation while it is running */ +pa_operation* pa_context_remove_sample(pa_context *c, const char *name, pa_context_success_cb_t, void *userdata); + +PA_C_DECL_END + +#endif diff --git a/src/pulse/simple.c b/src/pulse/simple.c new file mode 100644 index 00000000..a41881bb --- /dev/null +++ b/src/pulse/simple.c @@ -0,0 +1,455 @@ +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + 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 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 +#endif + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include "simple.h" + +struct pa_simple { + pa_threaded_mainloop *mainloop; + pa_context *context; + pa_stream *stream; + pa_stream_direction_t direction; + + const void *read_data; + size_t read_index, read_length; + + int operation_success; +}; + +#define CHECK_VALIDITY_RETURN_ANY(rerror, expression, error, ret) do { \ +if (!(expression)) { \ + if (rerror) \ + *(rerror) = error; \ + return (ret); \ + } \ +} while(0); + +#define CHECK_SUCCESS_GOTO(p, rerror, expression, label) do { \ +if (!(expression)) { \ + if (rerror) \ + *(rerror) = pa_context_errno((p)->context); \ + goto label; \ + } \ +} while(0); + +#define CHECK_DEAD_GOTO(p, rerror, label) do { \ +if (!(p)->context || pa_context_get_state((p)->context) != PA_CONTEXT_READY || \ + !(p)->stream || pa_stream_get_state((p)->stream) != PA_STREAM_READY) { \ + if (((p)->context && pa_context_get_state((p)->context) == PA_CONTEXT_FAILED) || \ + ((p)->stream && pa_stream_get_state((p)->stream) == PA_STREAM_FAILED)) { \ + if (rerror) \ + *(rerror) = pa_context_errno((p)->context); \ + } else \ + if (rerror) \ + *(rerror) = PA_ERR_BADSTATE; \ + goto label; \ + } \ +} while(0); + +static void context_state_cb(pa_context *c, void *userdata) { + pa_simple *p = userdata; + assert(c); + assert(p); + + switch (pa_context_get_state(c)) { + case PA_CONTEXT_READY: + case PA_CONTEXT_TERMINATED: + case PA_CONTEXT_FAILED: + pa_threaded_mainloop_signal(p->mainloop, 0); + break; + + case PA_CONTEXT_UNCONNECTED: + case PA_CONTEXT_CONNECTING: + case PA_CONTEXT_AUTHORIZING: + case PA_CONTEXT_SETTING_NAME: + break; + } +} + +static void stream_state_cb(pa_stream *s, void * userdata) { + pa_simple *p = userdata; + assert(s); + assert(p); + + switch (pa_stream_get_state(s)) { + + case PA_STREAM_READY: + case PA_STREAM_FAILED: + case PA_STREAM_TERMINATED: + pa_threaded_mainloop_signal(p->mainloop, 0); + break; + + case PA_STREAM_UNCONNECTED: + case PA_STREAM_CREATING: + break; + } +} + +static void stream_request_cb(pa_stream *s, size_t length, void *userdata) { + pa_simple *p = userdata; + assert(p); + + pa_threaded_mainloop_signal(p->mainloop, 0); +} + +static void stream_latency_update_cb(pa_stream *s, void *userdata) { + pa_simple *p = userdata; + + assert(p); + + pa_threaded_mainloop_signal(p->mainloop, 0); +} + +pa_simple* pa_simple_new( + const char *server, + const char *name, + pa_stream_direction_t dir, + const char *dev, + const char *stream_name, + const pa_sample_spec *ss, + const pa_channel_map *map, + const pa_buffer_attr *attr, + int *rerror) { + + pa_simple *p; + int error = PA_ERR_INTERNAL, r; + + CHECK_VALIDITY_RETURN_ANY(rerror, !server || *server, PA_ERR_INVALID, NULL); + CHECK_VALIDITY_RETURN_ANY(rerror, dir == PA_STREAM_PLAYBACK || dir == PA_STREAM_RECORD, PA_ERR_INVALID, NULL); + CHECK_VALIDITY_RETURN_ANY(rerror, !dev || *dev, PA_ERR_INVALID, NULL); + CHECK_VALIDITY_RETURN_ANY(rerror, ss && pa_sample_spec_valid(ss), PA_ERR_INVALID, NULL); + CHECK_VALIDITY_RETURN_ANY(rerror, !map || (pa_channel_map_valid(map) && map->channels == ss->channels), PA_ERR_INVALID, NULL) + + p = pa_xnew(pa_simple, 1); + p->context = NULL; + p->stream = NULL; + p->direction = dir; + p->read_data = NULL; + p->read_index = p->read_length = 0; + + if (!(p->mainloop = pa_threaded_mainloop_new())) + goto fail; + + if (!(p->context = pa_context_new(pa_threaded_mainloop_get_api(p->mainloop), name))) + goto fail; + + pa_context_set_state_callback(p->context, context_state_cb, p); + + if (pa_context_connect(p->context, server, 0, NULL) < 0) { + error = pa_context_errno(p->context); + goto fail; + } + + pa_threaded_mainloop_lock(p->mainloop); + + if (pa_threaded_mainloop_start(p->mainloop) < 0) + goto unlock_and_fail; + + /* Wait until the context is ready */ + pa_threaded_mainloop_wait(p->mainloop); + + if (pa_context_get_state(p->context) != PA_CONTEXT_READY) { + error = pa_context_errno(p->context); + goto unlock_and_fail; + } + + if (!(p->stream = pa_stream_new(p->context, stream_name, ss, map))) { + error = pa_context_errno(p->context); + goto unlock_and_fail; + } + + pa_stream_set_state_callback(p->stream, stream_state_cb, p); + pa_stream_set_read_callback(p->stream, stream_request_cb, p); + pa_stream_set_write_callback(p->stream, stream_request_cb, p); + pa_stream_set_latency_update_callback(p->stream, stream_latency_update_cb, p); + + if (dir == PA_STREAM_PLAYBACK) + r = pa_stream_connect_playback(p->stream, dev, attr, PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_AUTO_TIMING_UPDATE, NULL, NULL); + else + r = pa_stream_connect_record(p->stream, dev, attr, PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_AUTO_TIMING_UPDATE); + + if (r < 0) { + error = pa_context_errno(p->context); + goto unlock_and_fail; + } + + /* Wait until the stream is ready */ + pa_threaded_mainloop_wait(p->mainloop); + + /* Wait until the stream is ready */ + if (pa_stream_get_state(p->stream) != PA_STREAM_READY) { + error = pa_context_errno(p->context); + goto unlock_and_fail; + } + + pa_threaded_mainloop_unlock(p->mainloop); + + return p; + +unlock_and_fail: + pa_threaded_mainloop_unlock(p->mainloop); + +fail: + if (rerror) + *rerror = error; + pa_simple_free(p); + return NULL; +} + +void pa_simple_free(pa_simple *s) { + assert(s); + + if (s->mainloop) + pa_threaded_mainloop_stop(s->mainloop); + + if (s->stream) + pa_stream_unref(s->stream); + + if (s->context) + pa_context_unref(s->context); + + if (s->mainloop) + pa_threaded_mainloop_free(s->mainloop); + + pa_xfree(s); +} + +int pa_simple_write(pa_simple *p, const void*data, size_t length, int *rerror) { + assert(p); + + CHECK_VALIDITY_RETURN_ANY(rerror, p->direction == PA_STREAM_PLAYBACK, PA_ERR_BADSTATE, -1); + CHECK_VALIDITY_RETURN_ANY(rerror, data && length, PA_ERR_INVALID, -1); + + pa_threaded_mainloop_lock(p->mainloop); + + CHECK_DEAD_GOTO(p, rerror, unlock_and_fail); + + while (length > 0) { + size_t l; + int r; + + while (!(l = pa_stream_writable_size(p->stream))) { + pa_threaded_mainloop_wait(p->mainloop); + CHECK_DEAD_GOTO(p, rerror, unlock_and_fail); + } + + CHECK_SUCCESS_GOTO(p, rerror, l != (size_t) -1, unlock_and_fail); + + if (l > length) + l = length; + + r = pa_stream_write(p->stream, data, l, NULL, 0, PA_SEEK_RELATIVE); + CHECK_SUCCESS_GOTO(p, rerror, r >= 0, unlock_and_fail); + + data = (const uint8_t*) data + l; + length -= l; + } + + pa_threaded_mainloop_unlock(p->mainloop); + return 0; + +unlock_and_fail: + pa_threaded_mainloop_unlock(p->mainloop); + return -1; +} + +int pa_simple_read(pa_simple *p, void*data, size_t length, int *rerror) { + assert(p); + + CHECK_VALIDITY_RETURN_ANY(rerror, p->direction == PA_STREAM_RECORD, PA_ERR_BADSTATE, -1); + CHECK_VALIDITY_RETURN_ANY(rerror, data && length, PA_ERR_INVALID, -1); + + pa_threaded_mainloop_lock(p->mainloop); + + CHECK_DEAD_GOTO(p, rerror, unlock_and_fail); + + while (length > 0) { + size_t l; + + while (!p->read_data) { + int r; + + r = pa_stream_peek(p->stream, &p->read_data, &p->read_length); + CHECK_SUCCESS_GOTO(p, rerror, r == 0, unlock_and_fail); + + if (!p->read_data) { + pa_threaded_mainloop_wait(p->mainloop); + CHECK_DEAD_GOTO(p, rerror, unlock_and_fail); + } else + p->read_index = 0; + } + + l = p->read_length < length ? p->read_length : length; + memcpy(data, (const uint8_t*) p->read_data+p->read_index, l); + + data = (uint8_t*) data + l; + length -= l; + + p->read_index += l; + p->read_length -= l; + + if (!p->read_length) { + int r; + + r = pa_stream_drop(p->stream); + p->read_data = NULL; + p->read_length = 0; + p->read_index = 0; + + CHECK_SUCCESS_GOTO(p, rerror, r == 0, unlock_and_fail); + } + } + + pa_threaded_mainloop_unlock(p->mainloop); + return 0; + +unlock_and_fail: + pa_threaded_mainloop_unlock(p->mainloop); + return -1; +} + +static void success_cb(pa_stream *s, int success, void *userdata) { + pa_simple *p = userdata; + + assert(s); + assert(p); + + p->operation_success = success; + pa_threaded_mainloop_signal(p->mainloop, 0); +} + +int pa_simple_drain(pa_simple *p, int *rerror) { + pa_operation *o = NULL; + + assert(p); + + CHECK_VALIDITY_RETURN_ANY(rerror, p->direction == PA_STREAM_PLAYBACK, PA_ERR_BADSTATE, -1); + + pa_threaded_mainloop_lock(p->mainloop); + CHECK_DEAD_GOTO(p, rerror, unlock_and_fail); + + o = pa_stream_drain(p->stream, success_cb, p); + CHECK_SUCCESS_GOTO(p, rerror, o, unlock_and_fail); + + p->operation_success = 0; + while (pa_operation_get_state(o) != PA_OPERATION_DONE) { + pa_threaded_mainloop_wait(p->mainloop); + CHECK_DEAD_GOTO(p, rerror, unlock_and_fail); + } + CHECK_SUCCESS_GOTO(p, rerror, p->operation_success, unlock_and_fail); + + pa_operation_unref(o); + pa_threaded_mainloop_unlock(p->mainloop); + + return 0; + +unlock_and_fail: + + if (o) { + pa_operation_cancel(o); + pa_operation_unref(o); + } + + pa_threaded_mainloop_unlock(p->mainloop); + return -1; +} + +int pa_simple_flush(pa_simple *p, int *rerror) { + pa_operation *o = NULL; + + assert(p); + + CHECK_VALIDITY_RETURN_ANY(rerror, p->direction == PA_STREAM_PLAYBACK, PA_ERR_BADSTATE, -1); + + pa_threaded_mainloop_lock(p->mainloop); + CHECK_DEAD_GOTO(p, rerror, unlock_and_fail); + + o = pa_stream_flush(p->stream, success_cb, p); + CHECK_SUCCESS_GOTO(p, rerror, o, unlock_and_fail); + + p->operation_success = 0; + while (pa_operation_get_state(o) != PA_OPERATION_DONE) { + pa_threaded_mainloop_wait(p->mainloop); + CHECK_DEAD_GOTO(p, rerror, unlock_and_fail); + } + CHECK_SUCCESS_GOTO(p, rerror, p->operation_success, unlock_and_fail); + + pa_operation_unref(o); + pa_threaded_mainloop_unlock(p->mainloop); + + return 0; + +unlock_and_fail: + + if (o) { + pa_operation_cancel(o); + pa_operation_unref(o); + } + + pa_threaded_mainloop_unlock(p->mainloop); + return -1; +} + +pa_usec_t pa_simple_get_latency(pa_simple *p, int *rerror) { + pa_usec_t t; + int negative; + + assert(p); + + pa_threaded_mainloop_lock(p->mainloop); + + for (;;) { + CHECK_DEAD_GOTO(p, rerror, unlock_and_fail); + + if (pa_stream_get_latency(p->stream, &t, &negative) >= 0) + break; + + CHECK_SUCCESS_GOTO(p, rerror, pa_context_errno(p->context) == PA_ERR_NODATA, unlock_and_fail); + + /* Wait until latency data is available again */ + pa_threaded_mainloop_wait(p->mainloop); + } + + pa_threaded_mainloop_unlock(p->mainloop); + + return negative ? 0 : t; + +unlock_and_fail: + + pa_threaded_mainloop_unlock(p->mainloop); + return (pa_usec_t) -1; +} + diff --git a/src/pulse/simple.h b/src/pulse/simple.h new file mode 100644 index 00000000..0438d319 --- /dev/null +++ b/src/pulse/simple.h @@ -0,0 +1,146 @@ +#ifndef foosimplehfoo +#define foosimplehfoo + +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + 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 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 + +#include +#include +#include +#include + +/** \page simple Simple API + * + * \section overv_sec Overview + * + * The simple API is designed for applications with very basic sound + * playback or capture needs. It can only support a single stream per + * connection and has no handling of complex features like events, channel + * mappings and volume control. It is, however, very simple to use and + * quite sufficent for many programs. + * + * \section conn_sec Connecting + * + * The first step before using the sound system is to connect to the + * server. This is normally done this way: + * + * \code + * pa_simple *s; + * pa_sample_spec ss; + * + * ss.format = PA_SAMPLE_S16_NE; + * ss.channels = 2; + * ss.rate = 44100; + * + * s = pa_simple_new(NULL, // Use the default server. + * "Fooapp", // Our application's name. + * PA_STREAM_PLAYBACK, + * NULL, // Use the default device. + * "Music", // Description of our stream. + * &ss, // Our sample format. + * NULL, // Use default channel map + * NULL, // Use default buffering attributes. + * NULL, // Ignore error code. + * ); + * \endcode + * + * At this point a connected object is returned, or NULL if there was a + * problem connecting. + * + * \section transfer_sec Transferring data + * + * Once the connection is established to the server, data can start flowing. + * Using the connection is very similar to the normal read() and write() + * system calls. The main difference is that they're call pa_simple_read() + * and pa_simple_write(). Note that these operations always block. + * + * \section ctrl_sec Buffer control + * + * If a playback stream is used then a few other operations are available: + * + * \li pa_simple_drain() - Will wait for all sent data to finish playing. + * \li pa_simple_flush() - Will throw away all data currently in buffers. + * \li pa_simple_get_playback_latency() - Will return the total latency of + * the playback pipeline. + * + * \section cleanup_sec Cleanup + * + * Once playback or capture is complete, the connection should be closed + * and resources freed. This is done through: + * + * \code + * pa_simple_free(s); + * \endcode + */ + +/** \file + * A simple but limited synchronous playback and recording + * API. This is a synchronous, simplified wrapper around the standard + * asynchronous API. */ + +/** \example pacat-simple.c + * A simple playback tool using the simple API */ + +/** \example parec-simple.c + * A simple recording tool using the simple API */ + +PA_C_DECL_BEGIN + +/** \struct pa_simple + * An opaque simple connection object */ +typedef struct pa_simple pa_simple; + +/** Create a new connection to the server */ +pa_simple* pa_simple_new( + const char *server, /**< Server name, or NULL for default */ + const char *name, /**< A descriptive name for this client (application name, ...) */ + pa_stream_direction_t dir, /**< Open this stream for recording or playback? */ + const char *dev, /**< Sink (resp. source) name, or NULL for default */ + const char *stream_name, /**< A descriptive name for this client (application name, song title, ...) */ + const pa_sample_spec *ss, /**< The sample type to use */ + const pa_channel_map *map, /**< The channel map to use, or NULL for default */ + const pa_buffer_attr *attr, /**< Buffering attributes, or NULL for default */ + int *error /**< A pointer where the error code is stored when the routine returns NULL. It is OK to pass NULL here. */ + ); + +/** Close and free the connection to the server. The connection objects becomes invalid when this is called. */ +void pa_simple_free(pa_simple *s); + +/** Write some data to the server */ +int pa_simple_write(pa_simple *s, const void*data, size_t length, int *error); + +/** Wait until all data already written is played by the daemon */ +int pa_simple_drain(pa_simple *s, int *error); + +/** Read some data from the server */ +int pa_simple_read(pa_simple *s, void*data, size_t length, int *error); + +/** Return the playback latency. \since 0.5 */ +pa_usec_t pa_simple_get_latency(pa_simple *s, int *error); + +/** Flush the playback buffer. \since 0.5 */ +int pa_simple_flush(pa_simple *s, int *error); + +PA_C_DECL_END + +#endif diff --git a/src/pulse/stream.c b/src/pulse/stream.c new file mode 100644 index 00000000..c8fc09d2 --- /dev/null +++ b/src/pulse/stream.c @@ -0,0 +1,1366 @@ +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + 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 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 +#endif + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include "internal.h" + +#define LATENCY_IPOL_INTERVAL_USEC (100000L) + +pa_stream *pa_stream_new(pa_context *c, const char *name, const pa_sample_spec *ss, const pa_channel_map *map) { + pa_stream *s; + int i; + + assert(c); + + PA_CHECK_VALIDITY_RETURN_NULL(c, ss && pa_sample_spec_valid(ss), PA_ERR_INVALID); + PA_CHECK_VALIDITY_RETURN_NULL(c, !map || (pa_channel_map_valid(map) && map->channels == ss->channels), PA_ERR_INVALID); + + s = pa_xnew(pa_stream, 1); + s->ref = 1; + s->context = c; + s->mainloop = c->mainloop; + + s->read_callback = NULL; + s->read_userdata = NULL; + s->write_callback = NULL; + s->write_userdata = NULL; + s->state_callback = NULL; + s->state_userdata = NULL; + s->overflow_callback = NULL; + s->overflow_userdata = NULL; + s->underflow_callback = NULL; + s->underflow_userdata = NULL; + s->latency_update_callback = NULL; + s->latency_update_userdata = NULL; + + s->direction = PA_STREAM_NODIRECTION; + s->name = pa_xstrdup(name); + s->sample_spec = *ss; + s->flags = 0; + + if (map) + s->channel_map = *map; + else + pa_channel_map_init_auto(&s->channel_map, ss->channels, PA_CHANNEL_MAP_DEFAULT); + + s->channel = 0; + s->channel_valid = 0; + s->syncid = c->csyncid++; + s->device_index = PA_INVALID_INDEX; + s->requested_bytes = 0; + s->state = PA_STREAM_UNCONNECTED; + memset(&s->buffer_attr, 0, sizeof(s->buffer_attr)); + + s->peek_memchunk.index = 0; + s->peek_memchunk.length = 0; + s->peek_memchunk.memblock = NULL; + + s->record_memblockq = NULL; + + s->previous_time = 0; + s->timing_info_valid = 0; + s->read_index_not_before = 0; + s->write_index_not_before = 0; + + for (i = 0; i < PA_MAX_WRITE_INDEX_CORRECTIONS; i++) + s->write_index_corrections[i].valid = 0; + s->current_write_index_correction = 0; + + s->corked = 0; + + s->cached_time_valid = 0; + + s->auto_timing_update_event = NULL; + s->auto_timing_update_requested = 0; + + /* Refcounting is strictly one-way: from the "bigger" to the "smaller" object. */ + PA_LLIST_PREPEND(pa_stream, c->streams, s); + pa_stream_ref(s); + + return s; +} + +static void stream_free(pa_stream *s) { + assert(s && !s->context && !s->channel_valid); + + if (s->auto_timing_update_event) { + assert(s->mainloop); + s->mainloop->time_free(s->auto_timing_update_event); + } + + if (s->peek_memchunk.memblock) + pa_memblock_unref(s->peek_memchunk.memblock); + + if (s->record_memblockq) + pa_memblockq_free(s->record_memblockq); + + pa_xfree(s->name); + pa_xfree(s); +} + +void pa_stream_unref(pa_stream *s) { + assert(s); + assert(s->ref >= 1); + + if (--(s->ref) == 0) + stream_free(s); +} + +pa_stream* pa_stream_ref(pa_stream *s) { + assert(s); + assert(s->ref >= 1); + + s->ref++; + return s; +} + +pa_stream_state_t pa_stream_get_state(pa_stream *s) { + assert(s); + assert(s->ref >= 1); + + return s->state; +} + +pa_context* pa_stream_get_context(pa_stream *s) { + assert(s); + assert(s->ref >= 1); + + return s->context; +} + +uint32_t pa_stream_get_index(pa_stream *s) { + assert(s); + assert(s->ref >= 1); + + PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE, PA_INVALID_INDEX); + + return s->device_index; +} + +void pa_stream_set_state(pa_stream *s, pa_stream_state_t st) { + assert(s); + assert(s->ref >= 1); + + if (s->state == st) + return; + + pa_stream_ref(s); + + s->state = st; + if (s->state_callback) + s->state_callback(s, s->state_userdata); + + if ((st == PA_STREAM_FAILED || st == PA_STREAM_TERMINATED) && s->context) { + + /* Detach from context */ + pa_operation *o, *n; + + /* Unref all operatio object that point to us */ + for (o = s->context->operations; o; o = n) { + n = o->next; + + if (o->stream == s) + pa_operation_cancel(o); + } + + /* Drop all outstanding replies for this stream */ + if (s->context->pdispatch) + pa_pdispatch_unregister_reply(s->context->pdispatch, s); + + if (s->channel_valid) + pa_dynarray_put((s->direction == PA_STREAM_PLAYBACK) ? s->context->playback_streams : s->context->record_streams, s->channel, NULL); + + PA_LLIST_REMOVE(pa_stream, s->context->streams, s); + pa_stream_unref(s); + + s->channel = 0; + s->channel_valid = 0; + + s->context = NULL; + } + + pa_stream_unref(s); +} + +void pa_command_stream_killed(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { + pa_context *c = userdata; + pa_stream *s; + uint32_t channel; + + assert(pd); + assert(command == PA_COMMAND_PLAYBACK_STREAM_KILLED || command == PA_COMMAND_RECORD_STREAM_KILLED); + assert(t); + assert(c); + + pa_context_ref(c); + + if (pa_tagstruct_getu32(t, &channel) < 0 || + !pa_tagstruct_eof(t)) { + pa_context_fail(c, PA_ERR_PROTOCOL); + goto finish; + } + + if (!(s = pa_dynarray_get(command == PA_COMMAND_PLAYBACK_STREAM_KILLED ? c->playback_streams : c->record_streams, channel))) + goto finish; + + pa_context_set_error(c, PA_ERR_KILLED); + pa_stream_set_state(s, PA_STREAM_FAILED); + +finish: + pa_context_unref(c); +} + +void pa_command_request(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { + pa_stream *s; + pa_context *c = userdata; + uint32_t bytes, channel; + + assert(pd); + assert(command == PA_COMMAND_REQUEST); + assert(t); + assert(c); + + pa_context_ref(c); + + if (pa_tagstruct_getu32(t, &channel) < 0 || + pa_tagstruct_getu32(t, &bytes) < 0 || + !pa_tagstruct_eof(t)) { + pa_context_fail(c, PA_ERR_PROTOCOL); + goto finish; + } + + if (!(s = pa_dynarray_get(c->playback_streams, channel))) + goto finish; + + if (s->state == PA_STREAM_READY) { + s->requested_bytes += bytes; + + if (s->requested_bytes > 0 && s->write_callback) + s->write_callback(s, s->requested_bytes, s->write_userdata); + } + +finish: + pa_context_unref(c); +} + +void pa_command_overflow_or_underflow(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { + pa_stream *s; + pa_context *c = userdata; + uint32_t channel; + + assert(pd); + assert(command == PA_COMMAND_OVERFLOW || command == PA_COMMAND_UNDERFLOW); + assert(t); + assert(c); + + pa_context_ref(c); + + if (pa_tagstruct_getu32(t, &channel) < 0 || + !pa_tagstruct_eof(t)) { + pa_context_fail(c, PA_ERR_PROTOCOL); + goto finish; + } + + if (!(s = pa_dynarray_get(c->playback_streams, channel))) + goto finish; + + if (s->state == PA_STREAM_READY) { + + if (command == PA_COMMAND_OVERFLOW) { + if (s->overflow_callback) + s->overflow_callback(s, s->overflow_userdata); + } else if (command == PA_COMMAND_UNDERFLOW) { + if (s->underflow_callback) + s->underflow_callback(s, s->underflow_userdata); + } + } + + finish: + pa_context_unref(c); +} + +static void request_auto_timing_update(pa_stream *s, int force) { + struct timeval next; + assert(s); + + if (!(s->flags & PA_STREAM_AUTO_TIMING_UPDATE)) + return; + + if (s->state == PA_STREAM_READY && + (force || !s->auto_timing_update_requested)) { + pa_operation *o; + +/* pa_log("automatically requesting new timing data"); */ + + if ((o = pa_stream_update_timing_info(s, NULL, NULL))) { + pa_operation_unref(o); + s->auto_timing_update_requested = 1; + } + } + + pa_gettimeofday(&next); + pa_timeval_add(&next, LATENCY_IPOL_INTERVAL_USEC); + s->mainloop->time_restart(s->auto_timing_update_event, &next); +} + +static void invalidate_indexes(pa_stream *s, int r, int w) { + assert(s); + +/* pa_log("invalidate r:%u w:%u tag:%u", r, w, s->context->ctag); */ + + if (s->state != PA_STREAM_READY) + return; + + if (w) { + s->write_index_not_before = s->context->ctag; + + if (s->timing_info_valid) + s->timing_info.write_index_corrupt = 1; + +/* pa_log("write_index invalidated"); */ + } + + if (r) { + s->read_index_not_before = s->context->ctag; + + if (s->timing_info_valid) + s->timing_info.read_index_corrupt = 1; + +/* pa_log("read_index invalidated"); */ + } + + if ((s->direction == PA_STREAM_PLAYBACK && r) || + (s->direction == PA_STREAM_RECORD && w)) + s->cached_time_valid = 0; + + request_auto_timing_update(s, 1); +} + +static void auto_timing_update_callback(PA_GCC_UNUSED pa_mainloop_api *m, PA_GCC_UNUSED pa_time_event *e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) { + pa_stream *s = userdata; + +/* pa_log("time event"); */ + + pa_stream_ref(s); + request_auto_timing_update(s, 0); + pa_stream_unref(s); +} + +void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { + pa_stream *s = userdata; + + assert(pd); + assert(s); + assert(s->state == PA_STREAM_CREATING); + + pa_stream_ref(s); + + if (command != PA_COMMAND_REPLY) { + if (pa_context_handle_error(s->context, command, t) < 0) + goto finish; + + pa_stream_set_state(s, PA_STREAM_FAILED); + goto finish; + } + + if (pa_tagstruct_getu32(t, &s->channel) < 0 || + ((s->direction != PA_STREAM_UPLOAD) && pa_tagstruct_getu32(t, &s->device_index) < 0) || + ((s->direction != PA_STREAM_RECORD) && pa_tagstruct_getu32(t, &s->requested_bytes) < 0)) { + pa_context_fail(s->context, PA_ERR_PROTOCOL); + goto finish; + } + + if (pa_context_get_server_protocol_version(s->context) >= 9) { + if (s->direction == PA_STREAM_PLAYBACK) { + if (pa_tagstruct_getu32(t, &s->buffer_attr.maxlength) < 0 || + pa_tagstruct_getu32(t, &s->buffer_attr.tlength) < 0 || + pa_tagstruct_getu32(t, &s->buffer_attr.prebuf) < 0 || + pa_tagstruct_getu32(t, &s->buffer_attr.minreq) < 0) { + pa_context_fail(s->context, PA_ERR_PROTOCOL); + goto finish; + } + } else if (s->direction == PA_STREAM_RECORD) { + if (pa_tagstruct_getu32(t, &s->buffer_attr.maxlength) < 0 || + pa_tagstruct_getu32(t, &s->buffer_attr.fragsize) < 0) { + pa_context_fail(s->context, PA_ERR_PROTOCOL); + goto finish; + } + } + } + + if (!pa_tagstruct_eof(t)) { + pa_context_fail(s->context, PA_ERR_PROTOCOL); + goto finish; + } + + if (s->direction == PA_STREAM_RECORD) { + assert(!s->record_memblockq); + + s->record_memblockq = pa_memblockq_new( + 0, + s->buffer_attr.maxlength, + 0, + pa_frame_size(&s->sample_spec), + 1, + 0, + NULL, + s->context->memblock_stat); + } + + s->channel_valid = 1; + pa_dynarray_put((s->direction == PA_STREAM_RECORD) ? s->context->record_streams : s->context->playback_streams, s->channel, s); + + pa_stream_set_state(s, PA_STREAM_READY); + + if (s->direction != PA_STREAM_UPLOAD && + s->flags & PA_STREAM_AUTO_TIMING_UPDATE) { + struct timeval tv; + + pa_gettimeofday(&tv); + tv.tv_usec += LATENCY_IPOL_INTERVAL_USEC; /* every 100 ms */ + + assert(!s->auto_timing_update_event); + s->auto_timing_update_event = s->mainloop->time_new(s->mainloop, &tv, &auto_timing_update_callback, s); + + request_auto_timing_update(s, 1); + } + + if (s->requested_bytes > 0 && s->ref > 1 && s->write_callback) + s->write_callback(s, s->requested_bytes, s->write_userdata); + +finish: + pa_stream_unref(s); +} + +static int create_stream( + pa_stream_direction_t direction, + pa_stream *s, + const char *dev, + const pa_buffer_attr *attr, + pa_stream_flags_t flags, + const pa_cvolume *volume, + pa_stream *sync_stream) { + + pa_tagstruct *t; + uint32_t tag; + + assert(s); + assert(s->ref >= 1); + + PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_UNCONNECTED, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY(s->context, !(flags & ~((direction != PA_STREAM_UPLOAD ? + PA_STREAM_START_CORKED| + PA_STREAM_INTERPOLATE_TIMING| + PA_STREAM_NOT_MONOTONOUS| + PA_STREAM_AUTO_TIMING_UPDATE : 0))), PA_ERR_INVALID); + PA_CHECK_VALIDITY(s->context, !volume || volume->channels == s->sample_spec.channels, PA_ERR_INVALID); + PA_CHECK_VALIDITY(s->context, !sync_stream || (direction == PA_STREAM_PLAYBACK && sync_stream->direction == PA_STREAM_PLAYBACK), PA_ERR_INVALID); + + pa_stream_ref(s); + + s->direction = direction; + s->flags = flags; + + if (sync_stream) + s->syncid = sync_stream->syncid; + + if (attr) + s->buffer_attr = *attr; + else { + /* half a second */ + s->buffer_attr.tlength = pa_bytes_per_second(&s->sample_spec)/2; + s->buffer_attr.maxlength = (s->buffer_attr.tlength*3)/2; + s->buffer_attr.minreq = s->buffer_attr.tlength/100; + s->buffer_attr.prebuf = s->buffer_attr.tlength - s->buffer_attr.minreq; + s->buffer_attr.fragsize = s->buffer_attr.tlength/100; + } + + if (!dev) + dev = s->direction == PA_STREAM_PLAYBACK ? s->context->conf->default_sink : s->context->conf->default_source; + + t = pa_tagstruct_command( + s->context, + s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_CREATE_PLAYBACK_STREAM : PA_COMMAND_CREATE_RECORD_STREAM, + &tag); + + pa_tagstruct_put( + t, + PA_TAG_STRING, s->name, + PA_TAG_SAMPLE_SPEC, &s->sample_spec, + PA_TAG_CHANNEL_MAP, &s->channel_map, + PA_TAG_U32, PA_INVALID_INDEX, + PA_TAG_STRING, dev, + PA_TAG_U32, s->buffer_attr.maxlength, + PA_TAG_BOOLEAN, !!(flags & PA_STREAM_START_CORKED), + PA_TAG_INVALID); + + if (s->direction == PA_STREAM_PLAYBACK) { + pa_cvolume cv; + + pa_tagstruct_put( + t, + PA_TAG_U32, s->buffer_attr.tlength, + PA_TAG_U32, s->buffer_attr.prebuf, + PA_TAG_U32, s->buffer_attr.minreq, + PA_TAG_U32, s->syncid, + PA_TAG_INVALID); + + if (!volume) + volume = pa_cvolume_reset(&cv, s->sample_spec.channels); + + pa_tagstruct_put_cvolume(t, volume); + } else + pa_tagstruct_putu32(t, s->buffer_attr.fragsize); + + pa_pstream_send_tagstruct(s->context->pstream, t); + pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_create_stream_callback, s, NULL); + + pa_stream_set_state(s, PA_STREAM_CREATING); + + pa_stream_unref(s); + return 0; +} + +int pa_stream_connect_playback( + pa_stream *s, + const char *dev, + const pa_buffer_attr *attr, + pa_stream_flags_t flags, + pa_cvolume *volume, + pa_stream *sync_stream) { + + assert(s); + assert(s->ref >= 1); + + return create_stream(PA_STREAM_PLAYBACK, s, dev, attr, flags, volume, sync_stream); +} + +int pa_stream_connect_record( + pa_stream *s, + const char *dev, + const pa_buffer_attr *attr, + pa_stream_flags_t flags) { + + assert(s); + assert(s->ref >= 1); + + return create_stream(PA_STREAM_RECORD, s, dev, attr, flags, NULL, NULL); +} + +int pa_stream_write( + pa_stream *s, + const void *data, + size_t length, + void (*free_cb)(void *p), + int64_t offset, + pa_seek_mode_t seek) { + + pa_memchunk chunk; + + assert(s); + assert(s->ref >= 1); + assert(data); + + 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, 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); + + if (length <= 0) + return 0; + + if (free_cb) + chunk.memblock = pa_memblock_new_user((void*) data, length, free_cb, 1, s->context->memblock_stat); + else { + chunk.memblock = pa_memblock_new(length, s->context->memblock_stat); + memcpy(chunk.memblock->data, data, length); + } + + chunk.index = 0; + chunk.length = length; + + pa_pstream_send_memblock(s->context->pstream, s->channel, offset, seek, &chunk); + pa_memblock_unref(chunk.memblock); + + if (length < s->requested_bytes) + s->requested_bytes -= length; + else + s->requested_bytes = 0; + + if (s->direction == PA_STREAM_PLAYBACK) { + + /* Update latency request correction */ + if (s->write_index_corrections[s->current_write_index_correction].valid) { + + if (seek == PA_SEEK_ABSOLUTE) { + s->write_index_corrections[s->current_write_index_correction].corrupt = 0; + s->write_index_corrections[s->current_write_index_correction].absolute = 1; + s->write_index_corrections[s->current_write_index_correction].value = offset + length; + } else if (seek == PA_SEEK_RELATIVE) { + if (!s->write_index_corrections[s->current_write_index_correction].corrupt) + s->write_index_corrections[s->current_write_index_correction].value += offset + length; + } else + s->write_index_corrections[s->current_write_index_correction].corrupt = 1; + } + + /* Update the write index in the already available latency data */ + if (s->timing_info_valid) { + + if (seek == PA_SEEK_ABSOLUTE) { + s->timing_info.write_index_corrupt = 0; + s->timing_info.write_index = offset + length; + } else if (seek == PA_SEEK_RELATIVE) { + if (!s->timing_info.write_index_corrupt) + s->timing_info.write_index += offset + length; + } else + s->timing_info.write_index_corrupt = 1; + } + + if (!s->timing_info_valid || s->timing_info.write_index_corrupt) + request_auto_timing_update(s, 1); + } + + return 0; +} + +int pa_stream_peek(pa_stream *s, const void **data, size_t *length) { + assert(s); + assert(s->ref >= 1); + assert(data); + assert(length); + + PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY(s->context, s->direction == PA_STREAM_RECORD, PA_ERR_BADSTATE); + + if (!s->peek_memchunk.memblock) { + + if (pa_memblockq_peek(s->record_memblockq, &s->peek_memchunk) < 0) { + *data = NULL; + *length = 0; + return 0; + } + } + + *data = (const char*) s->peek_memchunk.memblock->data + s->peek_memchunk.index; + *length = s->peek_memchunk.length; + return 0; +} + +int pa_stream_drop(pa_stream *s) { + assert(s); + assert(s->ref >= 1); + + PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY(s->context, s->direction == PA_STREAM_RECORD, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY(s->context, s->peek_memchunk.memblock, PA_ERR_BADSTATE); + + pa_memblockq_drop(s->record_memblockq, &s->peek_memchunk, s->peek_memchunk.length); + + /* Fix the simulated local read index */ + if (s->timing_info_valid && !s->timing_info.read_index_corrupt) + s->timing_info.read_index += s->peek_memchunk.length; + + pa_memblock_unref(s->peek_memchunk.memblock); + s->peek_memchunk.length = 0; + s->peek_memchunk.index = 0; + s->peek_memchunk.memblock = NULL; + + return 0; +} + +size_t pa_stream_writable_size(pa_stream *s) { + assert(s); + assert(s->ref >= 1); + + PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE, (size_t) -1); + PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->direction != PA_STREAM_RECORD, PA_ERR_BADSTATE, (size_t) -1); + + return s->requested_bytes; +} + +size_t pa_stream_readable_size(pa_stream *s) { + assert(s); + assert(s->ref >= 1); + + PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE, (size_t) -1); + PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->direction == PA_STREAM_RECORD, PA_ERR_BADSTATE, (size_t) -1); + + return pa_memblockq_get_length(s->record_memblockq); +} + +pa_operation * pa_stream_drain(pa_stream *s, pa_stream_success_cb_t cb, void *userdata) { + pa_operation *o; + pa_tagstruct *t; + uint32_t tag; + + assert(s); + assert(s->ref >= 1); + + PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction == PA_STREAM_PLAYBACK, PA_ERR_BADSTATE); + + o = pa_operation_new(s->context, s, (pa_operation_cb_t) cb, userdata); + + t = pa_tagstruct_command(s->context, PA_COMMAND_DRAIN_PLAYBACK_STREAM, &tag); + pa_tagstruct_putu32(t, s->channel); + pa_pstream_send_tagstruct(s->context->pstream, t); + pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); + + return o; +} + +static void stream_get_timing_info_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { + pa_operation *o = userdata; + struct timeval local, remote, now; + pa_timing_info *i; + + assert(pd); + assert(o); + + if (!o->context || !o->stream) + goto finish; + + i = &o->stream->timing_info; + +/* pa_log("pre corrupt w:%u r:%u\n", !o->stream->timing_info_valid || i->write_index_corrupt,!o->stream->timing_info_valid || i->read_index_corrupt); */ + + o->stream->timing_info_valid = 0; + i->write_index_corrupt = 0; + i->read_index_corrupt = 0; + +/* pa_log("timing update %u\n", tag); */ + + if (command != PA_COMMAND_REPLY) { + if (pa_context_handle_error(o->context, command, t) < 0) + goto finish; + + } else if (pa_tagstruct_get_usec(t, &i->sink_usec) < 0 || + pa_tagstruct_get_usec(t, &i->source_usec) < 0 || + pa_tagstruct_get_boolean(t, &i->playing) < 0 || + pa_tagstruct_get_timeval(t, &local) < 0 || + pa_tagstruct_get_timeval(t, &remote) < 0 || + pa_tagstruct_gets64(t, &i->write_index) < 0 || + pa_tagstruct_gets64(t, &i->read_index) < 0 || + !pa_tagstruct_eof(t)) { + pa_context_fail(o->context, PA_ERR_PROTOCOL); + goto finish; + + } else { + o->stream->timing_info_valid = 1; + + pa_gettimeofday(&now); + + /* Calculcate timestamps */ + if (pa_timeval_cmp(&local, &remote) <= 0 && pa_timeval_cmp(&remote, &now) <= 0) { + /* local and remote seem to have synchronized clocks */ + + if (o->stream->direction == PA_STREAM_PLAYBACK) + i->transport_usec = pa_timeval_diff(&remote, &local); + else + i->transport_usec = pa_timeval_diff(&now, &remote); + + i->synchronized_clocks = 1; + i->timestamp = remote; + } else { + /* clocks are not synchronized, let's estimate latency then */ + i->transport_usec = pa_timeval_diff(&now, &local)/2; + i->synchronized_clocks = 0; + i->timestamp = local; + pa_timeval_add(&i->timestamp, i->transport_usec); + } + + /* Invalidate read and write indexes if necessary */ + if (tag < o->stream->read_index_not_before) + i->read_index_corrupt = 1; + + if (tag < o->stream->write_index_not_before) + i->write_index_corrupt = 1; + + if (o->stream->direction == PA_STREAM_PLAYBACK) { + /* Write index correction */ + + int n, j; + uint32_t ctag = tag; + + /* Go through the saved correction values and add up the total correction.*/ + + for (n = 0, j = o->stream->current_write_index_correction+1; + n < PA_MAX_WRITE_INDEX_CORRECTIONS; + n++, j = (j + 1) % PA_MAX_WRITE_INDEX_CORRECTIONS) { + + /* Step over invalid data or out-of-date data */ + if (!o->stream->write_index_corrections[j].valid || + o->stream->write_index_corrections[j].tag < ctag) + continue; + + /* Make sure that everything is in order */ + ctag = o->stream->write_index_corrections[j].tag+1; + + /* Now fix the write index */ + if (o->stream->write_index_corrections[j].corrupt) { + /* A corrupting seek was made */ + i->write_index = 0; + i->write_index_corrupt = 1; + } else if (o->stream->write_index_corrections[j].absolute) { + /* An absolute seek was made */ + i->write_index = o->stream->write_index_corrections[j].value; + i->write_index_corrupt = 0; + } else if (!i->write_index_corrupt) { + /* A relative seek was made */ + i->write_index += o->stream->write_index_corrections[j].value; + } + } + } + + if (o->stream->direction == PA_STREAM_RECORD) { + /* Read index correction */ + + if (!i->read_index_corrupt) + i->read_index -= pa_memblockq_get_length(o->stream->record_memblockq); + } + + o->stream->cached_time_valid = 0; + } + + o->stream->auto_timing_update_requested = 0; +/* pa_log("post corrupt w:%u r:%u\n", i->write_index_corrupt || !o->stream->timing_info_valid, i->read_index_corrupt || !o->stream->timing_info_valid); */ + + /* Clear old correction entries */ + if (o->stream->direction == PA_STREAM_PLAYBACK) { + int n; + + for (n = 0; n < PA_MAX_WRITE_INDEX_CORRECTIONS; n++) { + if (!o->stream->write_index_corrections[n].valid) + continue; + + if (o->stream->write_index_corrections[n].tag <= tag) + o->stream->write_index_corrections[n].valid = 0; + } + } + + if (o->stream->latency_update_callback) + o->stream->latency_update_callback(o->stream, o->stream->latency_update_userdata); + + if (o->callback && o->stream && o->stream->state == PA_STREAM_READY) { + pa_stream_success_cb_t cb = (pa_stream_success_cb_t) o->callback; + cb(o->stream, o->stream->timing_info_valid, o->userdata); + } + +finish: + + pa_operation_done(o); + pa_operation_unref(o); +} + +pa_operation* pa_stream_update_timing_info(pa_stream *s, pa_stream_success_cb_t cb, void *userdata) { + uint32_t tag; + pa_operation *o; + pa_tagstruct *t; + struct timeval now; + int cidx = 0; + + assert(s); + assert(s->ref >= 1); + + PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE); + + if (s->direction == PA_STREAM_PLAYBACK) { + /* Find a place to store the write_index correction data for this entry */ + cidx = (s->current_write_index_correction + 1) % PA_MAX_WRITE_INDEX_CORRECTIONS; + + /* Check if we could allocate a correction slot. If not, there are too many outstanding queries */ + PA_CHECK_VALIDITY_RETURN_NULL(s->context, !s->write_index_corrections[cidx].valid, PA_ERR_INTERNAL); + } + o = pa_operation_new(s->context, s, (pa_operation_cb_t) cb, userdata); + + t = pa_tagstruct_command( + s->context, + s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_GET_PLAYBACK_LATENCY : PA_COMMAND_GET_RECORD_LATENCY, + &tag); + pa_tagstruct_putu32(t, s->channel); + pa_tagstruct_put_timeval(t, pa_gettimeofday(&now)); + + pa_pstream_send_tagstruct(s->context->pstream, t); + pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, stream_get_timing_info_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); + + if (s->direction == PA_STREAM_PLAYBACK) { + /* Fill in initial correction data */ + o->stream->current_write_index_correction = cidx; + o->stream->write_index_corrections[cidx].valid = 1; + o->stream->write_index_corrections[cidx].tag = tag; + o->stream->write_index_corrections[cidx].absolute = 0; + o->stream->write_index_corrections[cidx].value = 0; + o->stream->write_index_corrections[cidx].corrupt = 0; + } + +/* pa_log("requesting update %u\n", tag); */ + + return o; +} + +void pa_stream_disconnect_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { + pa_stream *s = userdata; + + assert(pd); + assert(s); + assert(s->ref >= 1); + + pa_stream_ref(s); + + if (command != PA_COMMAND_REPLY) { + if (pa_context_handle_error(s->context, command, t) < 0) + goto finish; + + pa_stream_set_state(s, PA_STREAM_FAILED); + goto finish; + } else if (!pa_tagstruct_eof(t)) { + pa_context_fail(s->context, PA_ERR_PROTOCOL); + goto finish; + } + + pa_stream_set_state(s, PA_STREAM_TERMINATED); + +finish: + pa_stream_unref(s); +} + +int pa_stream_disconnect(pa_stream *s) { + pa_tagstruct *t; + uint32_t tag; + + assert(s); + assert(s->ref >= 1); + + PA_CHECK_VALIDITY(s->context, s->channel_valid, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY(s->context, s->context->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + + pa_stream_ref(s); + + t = pa_tagstruct_command( + s->context, + s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_DELETE_PLAYBACK_STREAM : + (s->direction == PA_STREAM_RECORD ? PA_COMMAND_DELETE_RECORD_STREAM : PA_COMMAND_DELETE_UPLOAD_STREAM), + &tag); + pa_tagstruct_putu32(t, s->channel); + pa_pstream_send_tagstruct(s->context->pstream, t); + pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_disconnect_callback, s, NULL); + + pa_stream_unref(s); + return 0; +} + +void pa_stream_set_read_callback(pa_stream *s, pa_stream_request_cb_t cb, void *userdata) { + assert(s); + assert(s->ref >= 1); + + s->read_callback = cb; + s->read_userdata = userdata; +} + +void pa_stream_set_write_callback(pa_stream *s, pa_stream_request_cb_t cb, void *userdata) { + assert(s); + assert(s->ref >= 1); + + s->write_callback = cb; + s->write_userdata = userdata; +} + +void pa_stream_set_state_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata) { + assert(s); + assert(s->ref >= 1); + + s->state_callback = cb; + s->state_userdata = userdata; +} + +void pa_stream_set_overflow_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata) { + assert(s); + assert(s->ref >= 1); + + s->overflow_callback = cb; + s->overflow_userdata = userdata; +} + +void pa_stream_set_underflow_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata) { + assert(s); + assert(s->ref >= 1); + + s->underflow_callback = cb; + s->underflow_userdata = userdata; +} + +void pa_stream_set_latency_update_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata) { + assert(s); + assert(s->ref >= 1); + + s->latency_update_callback = cb; + s->latency_update_userdata = userdata; +} + +void pa_stream_simple_ack_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { + pa_operation *o = userdata; + int success = 1; + + assert(pd); + assert(o); + assert(o->ref >= 1); + + if (!o->context) + goto finish; + + if (command != PA_COMMAND_REPLY) { + if (pa_context_handle_error(o->context, command, t) < 0) + goto finish; + + success = 0; + } else if (!pa_tagstruct_eof(t)) { + pa_context_fail(o->context, PA_ERR_PROTOCOL); + goto finish; + } + + if (o->callback) { + pa_stream_success_cb_t cb = (pa_stream_success_cb_t) o->callback; + cb(o->stream, success, o->userdata); + } + +finish: + pa_operation_done(o); + pa_operation_unref(o); +} + +pa_operation* pa_stream_cork(pa_stream *s, int b, pa_stream_success_cb_t cb, void *userdata) { + pa_operation *o; + pa_tagstruct *t; + uint32_t tag; + + assert(s); + assert(s->ref >= 1); + + PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE); + + s->corked = b; + + o = pa_operation_new(s->context, s, (pa_operation_cb_t) cb, userdata); + + t = pa_tagstruct_command( + s->context, + s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_CORK_PLAYBACK_STREAM : PA_COMMAND_CORK_RECORD_STREAM, + &tag); + pa_tagstruct_putu32(t, s->channel); + pa_tagstruct_put_boolean(t, !!b); + pa_pstream_send_tagstruct(s->context->pstream, t); + pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); + + if (s->direction == PA_STREAM_PLAYBACK) + invalidate_indexes(s, 1, 0); + + return o; +} + +static pa_operation* stream_send_simple_command(pa_stream *s, uint32_t command, pa_stream_success_cb_t cb, void *userdata) { + pa_tagstruct *t; + pa_operation *o; + uint32_t tag; + + assert(s); + assert(s->ref >= 1); + + PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE); + + o = pa_operation_new(s->context, s, (pa_operation_cb_t) cb, userdata); + + t = pa_tagstruct_command(s->context, command, &tag); + pa_tagstruct_putu32(t, s->channel); + pa_pstream_send_tagstruct(s->context->pstream, t); + pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); + + return o; +} + +pa_operation* pa_stream_flush(pa_stream *s, pa_stream_success_cb_t cb, void *userdata) { + pa_operation *o; + + PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE); + + if ((o = stream_send_simple_command(s, s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_FLUSH_PLAYBACK_STREAM : PA_COMMAND_FLUSH_RECORD_STREAM, cb, userdata))) { + + if (s->direction == PA_STREAM_PLAYBACK) { + if (s->write_index_corrections[s->current_write_index_correction].valid) + s->write_index_corrections[s->current_write_index_correction].corrupt = 1; + + if (s->timing_info_valid) + s->timing_info.write_index_corrupt = 1; + + if (s->buffer_attr.prebuf > 0) + invalidate_indexes(s, 1, 0); + else + request_auto_timing_update(s, 1); + } else + invalidate_indexes(s, 0, 1); + } + + return o; +} + +pa_operation* pa_stream_prebuf(pa_stream *s, pa_stream_success_cb_t cb, void *userdata) { + pa_operation *o; + + PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction == PA_STREAM_PLAYBACK, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->buffer_attr.prebuf > 0, PA_ERR_BADSTATE); + + if ((o = stream_send_simple_command(s, PA_COMMAND_PREBUF_PLAYBACK_STREAM, cb, userdata))) + invalidate_indexes(s, 1, 0); + + return o; +} + +pa_operation* pa_stream_trigger(pa_stream *s, pa_stream_success_cb_t cb, void *userdata) { + pa_operation *o; + + PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction == PA_STREAM_PLAYBACK, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->buffer_attr.prebuf > 0, PA_ERR_BADSTATE); + + if ((o = stream_send_simple_command(s, PA_COMMAND_TRIGGER_PLAYBACK_STREAM, cb, userdata))) + invalidate_indexes(s, 1, 0); + + return o; +} + +pa_operation* pa_stream_set_name(pa_stream *s, const char *name, pa_stream_success_cb_t cb, void *userdata) { + pa_operation *o; + pa_tagstruct *t; + uint32_t tag; + + assert(s); + assert(s->ref >= 1); + assert(name); + + PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE); + + o = pa_operation_new(s->context, s, (pa_operation_cb_t) cb, userdata); + + t = pa_tagstruct_command( + s->context, + s->direction == PA_STREAM_RECORD ? PA_COMMAND_SET_RECORD_STREAM_NAME : PA_COMMAND_SET_PLAYBACK_STREAM_NAME, + &tag); + pa_tagstruct_putu32(t, s->channel); + pa_tagstruct_puts(t, name); + pa_pstream_send_tagstruct(s->context->pstream, t); + pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); + + return o; +} + +int pa_stream_get_time(pa_stream *s, pa_usec_t *r_usec) { + pa_usec_t usec = 0; + + assert(s); + assert(s->ref >= 1); + + PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY(s->context, s->timing_info_valid, PA_ERR_NODATA); + PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_PLAYBACK || !s->timing_info.read_index_corrupt, PA_ERR_NODATA); + PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_RECORD || !s->timing_info.write_index_corrupt, PA_ERR_NODATA); + + if (s->cached_time_valid) + /* We alredy calculated the time value for this timing info, so let's reuse it */ + usec = s->cached_time; + else { + if (s->direction == PA_STREAM_PLAYBACK) { + /* The last byte that was written into the output device + * had this time value associated */ + usec = pa_bytes_to_usec(s->timing_info.read_index < 0 ? 0 : (uint64_t) s->timing_info.read_index, &s->sample_spec); + + if (!s->corked) { + /* Because the latency info took a little time to come + * to us, we assume that the real output time is actually + * a little ahead */ + usec += s->timing_info.transport_usec; + + /* However, the output device usually maintains a buffer + too, hence the real sample currently played is a little + back */ + if (s->timing_info.sink_usec >= usec) + usec = 0; + else + usec -= s->timing_info.sink_usec; + } + + } else if (s->direction == PA_STREAM_RECORD) { + /* The last byte written into the server side queue had + * this time value associated */ + usec = pa_bytes_to_usec(s->timing_info.write_index < 0 ? 0 : (uint64_t) s->timing_info.write_index, &s->sample_spec); + + if (!s->corked) { + /* Add transport latency */ + usec += s->timing_info.transport_usec; + + /* Add latency of data in device buffer */ + usec += s->timing_info.source_usec; + + /* If this is a monitor source, we need to correct the + * time by the playback device buffer */ + if (s->timing_info.sink_usec >= usec) + usec = 0; + else + usec -= s->timing_info.sink_usec; + } + } + + s->cached_time = usec; + s->cached_time_valid = 1; + } + + /* Interpolate if requested */ + if (s->flags & PA_STREAM_INTERPOLATE_TIMING) { + + /* We just add the time that passed since the latency info was + * current */ + if (!s->corked) { + struct timeval now; + usec += pa_timeval_diff(pa_gettimeofday(&now), &s->timing_info.timestamp); + } + } + + /* Make sure the time runs monotonically */ + if (!(s->flags & PA_STREAM_NOT_MONOTONOUS)) { + if (usec < s->previous_time) + usec = s->previous_time; + else + s->previous_time = usec; + } + + if (r_usec) + *r_usec = usec; + + return 0; +} + +static pa_usec_t time_counter_diff(pa_stream *s, pa_usec_t a, pa_usec_t b, int *negative) { + assert(s); + assert(s->ref >= 1); + + if (negative) + *negative = 0; + + if (a >= b) + return a-b; + else { + if (negative && s->direction == PA_STREAM_RECORD) { + *negative = 1; + return b-a; + } else + return 0; + } +} + +int pa_stream_get_latency(pa_stream *s, pa_usec_t *r_usec, int *negative) { + pa_usec_t t, c; + int r; + int64_t cindex; + + assert(s); + assert(s->ref >= 1); + assert(r_usec); + + PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY(s->context, s->timing_info_valid, PA_ERR_NODATA); + PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_PLAYBACK || !s->timing_info.write_index_corrupt, PA_ERR_NODATA); + PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_RECORD || !s->timing_info.read_index_corrupt, PA_ERR_NODATA); + + if ((r = pa_stream_get_time(s, &t)) < 0) + return r; + + if (s->direction == PA_STREAM_PLAYBACK) + cindex = s->timing_info.write_index; + else + cindex = s->timing_info.read_index; + + if (cindex < 0) + cindex = 0; + + c = pa_bytes_to_usec(cindex, &s->sample_spec); + + if (s->direction == PA_STREAM_PLAYBACK) + *r_usec = time_counter_diff(s, c, t, negative); + else + *r_usec = time_counter_diff(s, t, c, negative); + + return 0; +} + +const pa_timing_info* pa_stream_get_timing_info(pa_stream *s) { + assert(s); + assert(s->ref >= 1); + + PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->timing_info_valid, PA_ERR_BADSTATE); + + return &s->timing_info; +} + +const pa_sample_spec* pa_stream_get_sample_spec(pa_stream *s) { + assert(s); + assert(s->ref >= 1); + + return &s->sample_spec; +} + +const pa_channel_map* pa_stream_get_channel_map(pa_stream *s) { + assert(s); + assert(s->ref >= 1); + + return &s->channel_map; +} + +const pa_buffer_attr* pa_stream_get_buffer_attr(pa_stream *s) { + assert(s); + assert(s->ref >= 1); + + PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(s->context, + pa_context_get_server_protocol_version(s->context) >= 9, PA_ERR_NODATA); + + return &s->buffer_attr; +} diff --git a/src/pulse/stream.h b/src/pulse/stream.h new file mode 100644 index 00000000..d117ce4a --- /dev/null +++ b/src/pulse/stream.h @@ -0,0 +1,449 @@ +#ifndef foostreamhfoo +#define foostreamhfoo + +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + 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 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 + +#include +#include +#include +#include +#include +#include + +/** \page streams Audio Streams + * + * \section overv_sec Overview + * + * Audio streams form the central functionality of the sound server. Data is + * routed, converted and mixed from several sources before it is passed along + * to a final output. Currently, there are three forms of audio streams: + * + * \li Playback streams - Data flows from the client to the server. + * \li Record streams - Data flows from the server to the client. + * \li Upload streams - Similar to playback streams, but the data is stored in + * the sample cache. See \ref scache for more information + * about controlling the sample cache. + * + * \section create_sec Creating + * + * To access a stream, a pa_stream object must be created using + * pa_stream_new(). At this point the audio sample format and mapping of + * channels must be specified. See \ref sample and \ref channelmap for more + * information about those structures. + * + * This first step will only create a client-side object, representing the + * stream. To use the stream, a server-side object must be created and + * associated with the local object. Depending on which type of stream is + * desired, a different function is needed: + * + * \li Playback stream - pa_stream_connect_playback() + * \li Record stream - pa_stream_connect_record() + * \li Upload stream - pa_stream_connect_upload() (see \ref scache) + * + * Similar to how connections are done in contexts, connecting a stream will + * not generate a pa_operation object. Also like contexts, the application + * should register a state change callback, using + * pa_stream_set_state_callback(), and wait for the stream to enter an active + * state. + * + * \subsection bufattr_subsec Buffer Attributes + * + * Playback and record streams always have a server side buffer as + * part of the data flow. The size of this buffer strikes a + * compromise between low latency and sensitivity for buffer + * overflows/underruns. + * + * The buffer metrics may be controlled by the application. They are + * described with a pa_buffer_attr structure which contains a number + * of fields: + * + * \li maxlength - The absolute maximum number of bytes that can be stored in + * the buffer. If this value is exceeded then data will be + * lost. + * \li tlength - The target length of a playback buffer. The server will only + * send requests for more data as long as the buffer has less + * than this number of bytes of data. + * \li prebuf - Number of bytes that need to be in the buffer before + * playback will commence. Start of playback can be forced using + * pa_stream_trigger() even though the prebuffer size hasn't been + * reached. If a buffer underrun occurs, this prebuffering will be + * again enabled. If the playback shall never stop in case of a buffer + * underrun, this value should be set to 0. In that case the read + * index of the output buffer overtakes the write index, and hence the + * fill level of the buffer is negative. + * \li minreq - Minimum free number of the bytes in the playback buffer before + * the server will request more data. + * \li fragsize - Maximum number of bytes that the server will push in one + * chunk for record streams. + * + * The server side playback buffers are indexed by a write and a read + * index. The application writes to the write index and the sound + * device reads from the read index. The read index is increased + * monotonically, while the write index may be freely controlled by + * the application. Substracting the read index from the write index + * will give you the current fill level of the buffer. The read/write + * indexes are 64bit values and measured in bytes, they will never + * wrap. The current read/write index may be queried using + * pa_stream_get_timing_info() (see below for more information). In + * case of a buffer underrun the read index is equal or larger than + * the write index. Unless the prebuf value is 0, Polypaudio will + * temporarily pause playback in such a case, and wait until the + * buffer is filled up to prebuf bytes again. If prebuf is 0, the + * read index may be larger than the write index, in which case + * silence is played. If the application writes data to indexes lower + * than the read index, the data is immediately lost. + * + * \section transfer_sec Transferring Data + * + * Once the stream is up, data can start flowing between the client and the + * server. Two different access models can be used to transfer the data: + * + * \li Asynchronous - The application register a callback using + * pa_stream_set_write_callback() and + * pa_stream_set_read_callback() to receive notifications + * that data can either be written or read. + * \li Polled - Query the library for available data/space using + * pa_stream_writable_size() and pa_stream_readable_size() and + * transfer data as needed. The sizes are stored locally, in the + * client end, so there is no delay when reading them. + * + * It is also possible to mix the two models freely. + * + * Once there is data/space available, it can be transferred using either + * pa_stream_write() for playback, or pa_stream_peek() / pa_stream_drop() for + * record. Make sure you do not overflow the playback buffers as data will be + * dropped. + * + * \section bufctl_sec Buffer Control + * + * The transfer buffers can be controlled through a number of operations: + * + * \li pa_stream_cork() - Start or stop the playback or recording. + * \li pa_stream_trigger() - Start playback immediatly and do not wait for + * the buffer to fill up to the set trigger level. + * \li pa_stream_prebuf() - Reenable the playback trigger level. + * \li pa_stream_drain() - Wait for the playback buffer to go empty. Will + * return a pa_operation object that will indicate when + * the buffer is completely drained. + * \li pa_stream_flush() - Drop all data from the playback buffer and do not + * wait for it to finish playing. + * + * \section seek_modes Seeking in the Playback Buffer + * + * A client application may freely seek in the playback buffer. To + * accomplish that the pa_stream_write() function takes a seek mode + * and an offset argument. The seek mode is one of: + * + * \li PA_SEEK_RELATIVE - seek relative to the current write index + * \li PA_SEEK_ABSOLUTE - seek relative to the beginning of the playback buffer, (i.e. the first that was ever played in the stream) + * \li PA_SEEK_RELATIVE_ON_READ - seek relative to the current read index. Use this to write data to the output buffer that should be played as soon as possible + * \li PA_SEEK_RELATIVE_END - seek relative to the last byte ever written. + * + * If an application just wants to append some data to the output + * buffer, PA_SEEK_RELATIVE and an offset of 0 should be used. + * + * After a call to pa_stream_write() the write index will be left at + * the position right after the last byte of the written data. + * + * \section latency_sec Latency + * + * A major problem with networked audio is the increased latency caused by + * the network. To remedy this, Polypaudio supports an advanced system of + * monitoring the current latency. + * + * To get the raw data needed to calculate latencies, call + * pa_stream_get_timing_info(). This will give you a pa_timing_info + * structure that contains everything that is known about the server + * side buffer transport delays and the backend active in the + * server. (Besides other things it contains the write and read index + * values mentioned above.) + * + * This structure is updated every time a + * pa_stream_update_timing_info() operation is executed. (i.e. before + * the first call to this function the timing information structure is + * not available!) Since it is a lot of work to keep this structure + * up-to-date manually, Polypaudio can do that automatically for you: + * if PA_STREAM_AUTO_TIMING_UPDATE is passed when connecting the + * stream Polypaudio will automatically update the structure every + * 100ms and every time a function is called that might invalidate the + * previously known timing data (such as pa_stream_write() or + * pa_stream_flush()). Please note however, that there always is a + * short time window when the data in the timing information structure + * is out-of-date. Polypaudio tries to mark these situations by + * setting the write_index_corrupt and read_index_corrupt fields + * accordingly. + * + * The raw timing data in the pa_timing_info structure is usually hard + * to deal with. Therefore a more simplistic interface is available: + * you can call pa_stream_get_time() or pa_stream_get_latency(). The + * former will return the current playback time of the hardware since + * the stream has been started. The latter returns the time a sample + * that you write now takes to be played by the hardware. These two + * functions base their calculations on the same data that is returned + * by pa_stream_get_timing_info(). Hence the same rules for keeping + * the timing data up-to-date apply here. In case the write or read + * index is corrupted, these two functions will fail with + * PA_ERR_NODATA set. + * + * Since updating the timing info structure usually requires a full + * network round trip and some applications monitor the timing very + * often Polypaudio offers a timing interpolation system. If + * PA_STREAM_INTERPOLATE_TIMING is passed when connecting the stream, + * pa_stream_get_time() and pa_stream_get_latency() will try to + * interpolate the current playback time/latency by estimating the + * number of samples that have been played back by the hardware since + * the last regular timing update. It is espcially useful to combine + * this option with PA_STREAM_AUTO_TIMING_UPDATE, which will enable + * you to monitor the current playback time/latency very precisely and + * very frequently without requiring a network round trip every time. + * + * \section flow_sec Overflow and underflow + * + * Even with the best precautions, buffers will sometime over - or + * underflow. To handle this gracefully, the application can be + * notified when this happens. Callbacks are registered using + * pa_stream_set_overflow_callback() and + * pa_stream_set_underflow_callback(). + * + * \section sync_streams Sychronizing Multiple Playback Streams + * + * Polypaudio allows applications to fully synchronize multiple + * playback streams that are connected to the same output device. That + * means the streams will always be played back sample-by-sample + * synchronously. If stream operations like pa_stream_cork() are + * issued on one of the synchronized streams, they are simultaneously + * issued on the others. + * + * To synchronize a stream to another, just pass the "master" stream + * as last argument to pa_stream_connect_playack(). To make sure that + * the freshly created stream doesn't start playback right-away, make + * sure to pass PA_STREAM_START_CORKED and - after all streams have + * been created - uncork them all with a single call to + * pa_stream_cork() for the master stream. + * + * To make sure that a particular stream doesn't stop to play when a + * server side buffer underrun happens on it while the other + * synchronized streams continue playing and hence deviate you need to + * pass a "prebuf" pa_buffer_attr of 0 when connecting it. + * + * \section disc_sec Disconnecting + * + * When a stream has served is purpose it must be disconnected with + * pa_stream_disconnect(). If you only unreference it, then it will live on + * and eat resources both locally and on the server until you disconnect the + * context. + * + */ + +/** \file + * Audio streams for input, output and sample upload */ + +PA_C_DECL_BEGIN + +/** An opaque stream for playback or recording */ +typedef struct pa_stream pa_stream; + +/** A generic callback for operation completion */ +typedef void (*pa_stream_success_cb_t) (pa_stream*s, int success, void *userdata); + +/** A generic request callback */ +typedef void (*pa_stream_request_cb_t)(pa_stream *p, size_t length, void *userdata); + +/** A generic notification callback */ +typedef void (*pa_stream_notify_cb_t)(pa_stream *p, void *userdata); + +/** Create a new, unconnected stream with the specified name and sample type */ +pa_stream* pa_stream_new( + pa_context *c /**< The context to create this stream in */, + const char *name /**< A name for this stream */, + const pa_sample_spec *ss /**< The desired sample format */, + const pa_channel_map *map /**< The desired channel map, or NULL for default */); + +/** Decrease the reference counter by one */ +void pa_stream_unref(pa_stream *s); + +/** Increase the reference counter by one */ +pa_stream *pa_stream_ref(pa_stream *s); + +/** Return the current state of the stream */ +pa_stream_state_t pa_stream_get_state(pa_stream *p); + +/** Return the context this stream is attached to */ +pa_context* pa_stream_get_context(pa_stream *p); + +/** Return the device (sink input or source output) index this stream is connected to */ +uint32_t pa_stream_get_index(pa_stream *s); + +/** Connect the stream to a sink */ +int pa_stream_connect_playback( + pa_stream *s /**< The stream to connect to a sink */, + const char *dev /**< Name of the sink to connect to, or NULL for default */ , + const pa_buffer_attr *attr /**< Buffering attributes, or NULL for default */, + pa_stream_flags_t flags /**< Additional flags, or 0 for default */, + pa_cvolume *volume /**< Initial volume, or NULL for default */, + pa_stream *sync_stream /**< Synchronize this stream with the specified one, or NULL for a standalone stream*/); + +/** Connect the stream to a source */ +int pa_stream_connect_record( + pa_stream *s /**< The stream to connect to a source */ , + const char *dev /**< Name of the source to connect to, or NULL for default */, + const pa_buffer_attr *attr /**< Buffer attributes, or NULL for default */, + pa_stream_flags_t flags /**< Additional flags, or 0 for default */); + +/** 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 + * most applications passing 0 and PA_SEEK_RELATIVE as arguments for + * offset and seek should be useful.*/ +int pa_stream_write( + pa_stream *p /**< The stream to use */, + const void *data /**< The data to write */, + size_t length /**< The length of the data to write */, + pa_free_cb_t free_cb /**< A cleanup routine for the data or NULL to request an internal copy */, + int64_t offset, /**< Offset for seeking, must be 0 for upload streams */ + pa_seek_mode_t seek /**< Seek mode, must be PA_SEEK_RELATIVE for upload streams */); + +/** Read the next fragment from the buffer (for recording). + * 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 + * buffer. If no data is available will return a NULL pointer \since 0.8 */ +int pa_stream_peek( + pa_stream *p /**< The stream to use */, + const void **data /**< Pointer to pointer that will point to data */, + size_t *length /**< The length of the data read */); + +/** Remove the current fragment on record streams. It is invalid to do this without first + * calling pa_stream_peek(). \since 0.8 */ +int pa_stream_drop(pa_stream *p); + +/** Return the nember of bytes that may be written using pa_stream_write() */ +size_t pa_stream_writable_size(pa_stream *p); + +/** Return the number of bytes that may be read using pa_stream_read() \since 0.8 */ +size_t pa_stream_readable_size(pa_stream *p); + +/** Drain a playback stream. Use this for notification when the buffer is empty */ +pa_operation* pa_stream_drain(pa_stream *s, pa_stream_success_cb_t cb, void *userdata); + +/** Request a timing info structure update for a stream. Use + * pa_stream_get_timing_info() to get access to the raw timing data, + * or pa_stream_get_time() or pa_stream_get_latency() to get cleaned + * up values. */ +pa_operation* pa_stream_update_timing_info(pa_stream *p, pa_stream_success_cb_t cb, void *userdata); + +/** Set the callback function that is called whenever the state of the stream changes */ +void pa_stream_set_state_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata); + +/** Set the callback function that is called when new data may be + * written to the stream. */ +void pa_stream_set_write_callback(pa_stream *p, pa_stream_request_cb_t cb, void *userdata); + +/** Set the callback function that is called when new data is available from the stream. + * Return the number of bytes read. \since 0.8 */ +void pa_stream_set_read_callback(pa_stream *p, pa_stream_request_cb_t cb, void *userdata); + +/** Set the callback function that is called when a buffer overflow happens. (Only for playback streams) \since 0.8 */ +void pa_stream_set_overflow_callback(pa_stream *p, pa_stream_notify_cb_t cb, void *userdata); + +/** Set the callback function that is called when a buffer underflow happens. (Only for playback streams) \since 0.8 */ +void pa_stream_set_underflow_callback(pa_stream *p, pa_stream_notify_cb_t cb, void *userdata); + +/** Set the callback function that is called whenever a latency information update happens. Useful on PA_STREAM_AUTO_TIMING_UPDATE streams only. (Only for playback streams) \since 0.8.2 */ +void pa_stream_set_latency_update_callback(pa_stream *p, pa_stream_notify_cb_t cb, void *userdata); + +/** Pause (or resume) playback of this stream temporarily. Available on both playback and recording streams. \since 0.3 */ +pa_operation* pa_stream_cork(pa_stream *s, int b, pa_stream_success_cb_t cb, void *userdata); + +/** Flush the playback buffer of this stream. Most of the time you're + * better off using the parameter delta of pa_stream_write() instead of this + * function. Available on both playback and recording streams. \since 0.3 */ +pa_operation* pa_stream_flush(pa_stream *s, pa_stream_success_cb_t cb, void *userdata); + +/** Reenable prebuffering as specified in the pa_buffer_attr + * structure. Available for playback streams only. \since 0.6 */ +pa_operation* pa_stream_prebuf(pa_stream *s, pa_stream_success_cb_t cb, void *userdata); + +/** Request immediate start of playback on this stream. This disables + * prebuffering as specified in the pa_buffer_attr + * structure, temporarily. Available for playback streams only. \since 0.3 */ +pa_operation* pa_stream_trigger(pa_stream *s, pa_stream_success_cb_t cb, void *userdata); + +/** Rename the stream. \since 0.5 */ +pa_operation* pa_stream_set_name(pa_stream *s, const char *name, pa_stream_success_cb_t cb, void *userdata); + +/** Return the current playback/recording time. This is based on the + * data in the timing info structure returned by + * pa_stream_get_timing_info(). This function will usually only return + * new data if a timing info update has been recieved. Only if timing + * interpolation has been requested (PA_STREAM_INTERPOLATE_TIMING) + * the data from the last timing update is used for an estimation of + * the current playback/recording time based on the local time that + * passed since the timing info structure has been acquired. The time + * value returned by this function is guaranteed to increase + * monotonically. (that means: the returned value is always greater or + * equal to the value returned on the last call) This behaviour can + * be disabled by using PA_STREAM_NOT_MONOTONOUS. This may be + * desirable to deal better with bad estimations of transport + * latencies, but may have strange effects if the application is not + * able to deal with time going 'backwards'. \since 0.6 */ +int pa_stream_get_time(pa_stream *s, pa_usec_t *r_usec); + +/** Return the total stream latency. This function is based on + * pa_stream_get_time(). In case the stream is a monitoring stream the + * result can be negative, i.e. the captured samples are not yet + * played. In this case *negative is set to 1. \since 0.6 */ +int pa_stream_get_latency(pa_stream *s, pa_usec_t *r_usec, int *negative); + +/** Return the latest raw timing data structure. The returned pointer + * points to an internal read-only instance of the timing + * structure. The user should make a copy of this structure if he + * wants to modify it. An in-place update to this data structure may + * be requested using pa_stream_update_timing_info(). If no + * pa_stream_update_timing_info() call was issued before, this + * function will fail with PA_ERR_NODATA. Please note that the + * write_index member field (and only this field) is updated on each + * pa_stream_write() call, not just when a timing update has been + * recieved. \since 0.8 */ +const pa_timing_info* pa_stream_get_timing_info(pa_stream *s); + +/** Return a pointer to the stream's sample specification. \since 0.6 */ +const pa_sample_spec* pa_stream_get_sample_spec(pa_stream *s); + +/** Return a pointer to the stream's channel map. \since 0.8 */ +const pa_channel_map* pa_stream_get_channel_map(pa_stream *s); + +/** Return the buffer metrics of the stream. Only valid after the + * stream has been connected successfuly and if the server is at least + * Polypaudio 0.9. \since 0.9.0 */ +const pa_buffer_attr* pa_stream_get_buffer_attr(pa_stream *s); + +PA_C_DECL_END + +#endif diff --git a/src/pulse/subscribe.c b/src/pulse/subscribe.c new file mode 100644 index 00000000..c1d88912 --- /dev/null +++ b/src/pulse/subscribe.c @@ -0,0 +1,89 @@ +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + 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 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 +#endif + +#include +#include + +#include +#include + +#include "internal.h" + +#include "subscribe.h" + +void pa_command_subscribe_event(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { + pa_context *c = userdata; + pa_subscription_event_type_t e; + uint32_t idx; + + assert(pd); + assert(command == PA_COMMAND_SUBSCRIBE_EVENT); + assert(t); + assert(c); + + pa_context_ref(c); + + if (pa_tagstruct_getu32(t, &e) < 0 || + pa_tagstruct_getu32(t, &idx) < 0 || + !pa_tagstruct_eof(t)) { + pa_context_fail(c, PA_ERR_PROTOCOL); + goto finish; + } + + if (c->subscribe_callback) + c->subscribe_callback(c, e, idx, c->subscribe_userdata); + +finish: + pa_context_unref(c); +} + + +pa_operation* pa_context_subscribe(pa_context *c, pa_subscription_mask_t m, pa_context_success_cb_t cb, void *userdata) { + pa_operation *o; + pa_tagstruct *t; + uint32_t tag; + + assert(c); + assert(c->ref >= 1); + + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + + o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); + + t = pa_tagstruct_command(c, PA_COMMAND_SUBSCRIBE, &tag); + pa_tagstruct_putu32(t, m); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); + + return o; +} + +void pa_context_set_subscribe_callback(pa_context *c, pa_context_subscribe_cb_t cb, void *userdata) { + assert(c); + assert(c->ref >= 1); + + c->subscribe_callback = cb; + c->subscribe_userdata = userdata; +} diff --git a/src/pulse/subscribe.h b/src/pulse/subscribe.h new file mode 100644 index 00000000..adbea680 --- /dev/null +++ b/src/pulse/subscribe.h @@ -0,0 +1,61 @@ +#ifndef foosubscribehfoo +#define foosubscribehfoo + +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + 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 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 + +#include +#include +#include + +/** \page subscribe Event Subscription + * + * \section overv_sec Overview + * + * The application can be notified, asynchronously, whenever the internal + * layout of the server changes. Possible notifications are desribed in the + * \ref pa_subscription_event_type and \ref pa_subscription_mask + * enumerations. + * + * The application sets the notification mask using pa_context_subscribe() + * and the function that will be called whenever a notification occurs using + * pa_context_set_subscribe_callback(). + */ + +/** \file + * Daemon introspection event subscription subsystem. */ + +PA_C_DECL_BEGIN + +/** Subscription event callback prototype */ +typedef void (*pa_context_subscribe_cb_t)(pa_context *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata); + +/** Enable event notification */ +pa_operation* pa_context_subscribe(pa_context *c, pa_subscription_mask_t m, pa_context_success_cb_t cb, void *userdata); + +/** Set the context specific call back function that is called whenever the state of the daemon changes */ +void pa_context_set_subscribe_callback(pa_context *c, pa_context_subscribe_cb_t cb, void *userdata); + +PA_C_DECL_END + +#endif diff --git a/src/pulse/thread-mainloop.c b/src/pulse/thread-mainloop.c new file mode 100644 index 00000000..34f0f250 --- /dev/null +++ b/src/pulse/thread-mainloop.c @@ -0,0 +1,466 @@ +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + 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 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 +#endif + +#include +#include +#include + +#ifdef HAVE_SYS_POLL_H +#include +#else +#include "../pulsecore/poll.h" +#endif + +#ifdef HAVE_PTHREAD +#include +#endif + +#ifdef HAVE_WINDOWS_H +#include +#endif + +#include + +#include +#include + +#include "mainloop.h" +#include "thread-mainloop.h" + +#if defined(HAVE_PTHREAD) || defined(OS_IS_WIN32) + +struct pa_threaded_mainloop { + pa_mainloop *real_mainloop; + int n_waiting; + int thread_running; + +#ifdef OS_IS_WIN32 + DWORD thread_id; + HANDLE thread; + CRITICAL_SECTION mutex; + pa_hashmap *cond_events; + HANDLE accept_cond; +#else + pthread_t thread_id; + pthread_mutex_t mutex; + pthread_cond_t cond, accept_cond; +#endif +}; + +static inline int in_worker(pa_threaded_mainloop *m) { +#ifdef OS_IS_WIN32 + return GetCurrentThreadId() == m->thread_id; +#else + return pthread_equal(pthread_self(), m->thread_id); +#endif +} + +static int poll_func(struct pollfd *ufds, unsigned long nfds, int timeout, void *userdata) { +#ifdef OS_IS_WIN32 + CRITICAL_SECTION *mutex = userdata; +#else + pthread_mutex_t *mutex = userdata; +#endif + + int r; + + assert(mutex); + + /* Before entering poll() we unlock the mutex, so that + * avahi_simple_poll_quit() can succeed from another thread. */ + +#ifdef OS_IS_WIN32 + LeaveCriticalSection(mutex); +#else + pthread_mutex_unlock(mutex); +#endif + + r = poll(ufds, nfds, timeout); + +#ifdef OS_IS_WIN32 + EnterCriticalSection(mutex); +#else + pthread_mutex_lock(mutex); +#endif + + return r; +} + +#ifdef OS_IS_WIN32 +static DWORD WINAPI thread(void *userdata) { +#else +static void* thread(void *userdata) { +#endif + pa_threaded_mainloop *m = userdata; + +#ifndef OS_IS_WIN32 + sigset_t mask; + + /* Make sure that signals are delivered to the main thread */ + sigfillset(&mask); + pthread_sigmask(SIG_BLOCK, &mask, NULL); +#endif + +#ifdef OS_IS_WIN32 + EnterCriticalSection(&m->mutex); +#else + pthread_mutex_lock(&m->mutex); +#endif + + pa_mainloop_run(m->real_mainloop, NULL); + +#ifdef OS_IS_WIN32 + LeaveCriticalSection(&m->mutex); +#else + pthread_mutex_unlock(&m->mutex); +#endif + +#ifdef OS_IS_WIN32 + return 0; +#else + return NULL; +#endif +} + +pa_threaded_mainloop *pa_threaded_mainloop_new(void) { + pa_threaded_mainloop *m; +#ifndef OS_IS_WIN32 + pthread_mutexattr_t a; +#endif + + m = pa_xnew(pa_threaded_mainloop, 1); + + if (!(m->real_mainloop = pa_mainloop_new())) { + pa_xfree(m); + return NULL; + } + + pa_mainloop_set_poll_func(m->real_mainloop, poll_func, &m->mutex); + +#ifdef OS_IS_WIN32 + InitializeCriticalSection(&m->mutex); + + m->cond_events = pa_hashmap_new(NULL, NULL); + assert(m->cond_events); + m->accept_cond = CreateEvent(NULL, FALSE, FALSE, NULL); + assert(m->accept_cond); +#else + pthread_mutexattr_init(&a); + pthread_mutexattr_settype(&a, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(&m->mutex, &a); + pthread_mutexattr_destroy(&a); + + pthread_cond_init(&m->cond, NULL); + pthread_cond_init(&m->accept_cond, NULL); +#endif + + m->thread_running = 0; + m->n_waiting = 0; + + return m; +} + +void pa_threaded_mainloop_free(pa_threaded_mainloop* m) { + assert(m); + + /* Make sure that this function is not called from the helper thread */ + assert(!m->thread_running || !in_worker(m)); + + if (m->thread_running) + pa_threaded_mainloop_stop(m); + + if (m->real_mainloop) + pa_mainloop_free(m->real_mainloop); + +#ifdef OS_IS_WIN32 + pa_hashmap_free(m->cond_events, NULL, NULL); + CloseHandle(m->accept_cond); +#else + pthread_mutex_destroy(&m->mutex); + pthread_cond_destroy(&m->cond); + pthread_cond_destroy(&m->accept_cond); +#endif + + pa_xfree(m); +} + +int pa_threaded_mainloop_start(pa_threaded_mainloop *m) { + assert(m); + + assert(!m->thread_running); + +#ifdef OS_IS_WIN32 + + EnterCriticalSection(&m->mutex); + + m->thread = CreateThread(NULL, 0, thread, m, 0, &m->thread_id); + if (!m->thread) { + LeaveCriticalSection(&m->mutex); + return -1; + } + +#else + + pthread_mutex_lock(&m->mutex); + + if (pthread_create(&m->thread_id, NULL, thread, m) < 0) { + pthread_mutex_unlock(&m->mutex); + return -1; + } + +#endif + + m->thread_running = 1; + +#ifdef OS_IS_WIN32 + LeaveCriticalSection(&m->mutex); +#else + pthread_mutex_unlock(&m->mutex); +#endif + + return 0; +} + +void pa_threaded_mainloop_stop(pa_threaded_mainloop *m) { + assert(m); + + if (!m->thread_running) + return; + + /* Make sure that this function is not called from the helper thread */ + assert(!in_worker(m)); + +#ifdef OS_IS_WIN32 + EnterCriticalSection(&m->mutex); +#else + pthread_mutex_lock(&m->mutex); +#endif + + pa_mainloop_quit(m->real_mainloop, 0); + +#ifdef OS_IS_WIN32 + LeaveCriticalSection(&m->mutex); +#else + pthread_mutex_unlock(&m->mutex); +#endif + +#ifdef OS_IS_WIN32 + WaitForSingleObject(m->thread, INFINITE); + CloseHandle(m->thread); +#else + pthread_join(m->thread_id, NULL); +#endif + + m->thread_running = 0; + + return; +} + +void pa_threaded_mainloop_lock(pa_threaded_mainloop *m) { + assert(m); + + /* Make sure that this function is not called from the helper thread */ + assert(!m->thread_running || !in_worker(m)); + +#ifdef OS_IS_WIN32 + EnterCriticalSection(&m->mutex); +#else + pthread_mutex_lock(&m->mutex); +#endif +} + +void pa_threaded_mainloop_unlock(pa_threaded_mainloop *m) { + assert(m); + + /* Make sure that this function is not called from the helper thread */ + assert(!m->thread_running || !in_worker(m)); + +#ifdef OS_IS_WIN32 + LeaveCriticalSection(&m->mutex); +#else + pthread_mutex_unlock(&m->mutex); +#endif +} + +void pa_threaded_mainloop_signal(pa_threaded_mainloop *m, int wait_for_accept) { +#ifdef OS_IS_WIN32 + void *iter; + const void *key; + HANDLE event; +#endif + + assert(m); + +#ifdef OS_IS_WIN32 + + iter = NULL; + while (1) { + pa_hashmap_iterate(m->cond_events, &iter, &key); + if (key == NULL) + break; + event = (HANDLE)pa_hashmap_get(m->cond_events, key); + SetEvent(event); + } + +#else + + pthread_cond_broadcast(&m->cond); + +#endif + + if (wait_for_accept && m->n_waiting > 0) { + +#ifdef OS_IS_WIN32 + + /* This is just to make sure it's unsignaled */ + WaitForSingleObject(m->accept_cond, 0); + + LeaveCriticalSection(&m->mutex); + + WaitForSingleObject(m->accept_cond, INFINITE); + + EnterCriticalSection(&m->mutex); + +#else + + pthread_cond_wait(&m->accept_cond, &m->mutex); + +#endif + + } +} + +void pa_threaded_mainloop_wait(pa_threaded_mainloop *m) { +#ifdef OS_IS_WIN32 + HANDLE event; + DWORD result; +#endif + + assert(m); + + /* Make sure that this function is not called from the helper thread */ + assert(!m->thread_running || !in_worker(m)); + + m->n_waiting ++; + +#ifdef OS_IS_WIN32 + + event = CreateEvent(NULL, FALSE, FALSE, NULL); + assert(event); + + pa_hashmap_put(m->cond_events, event, event); + + LeaveCriticalSection(&m->mutex); + + result = WaitForSingleObject(event, INFINITE); + assert(result == WAIT_OBJECT_0); + + EnterCriticalSection(&m->mutex); + + pa_hashmap_remove(m->cond_events, event); + + CloseHandle(event); + +#else + + pthread_cond_wait(&m->cond, &m->mutex); + +#endif + + assert(m->n_waiting > 0); + m->n_waiting --; +} + +void pa_threaded_mainloop_accept(pa_threaded_mainloop *m) { + assert(m); + + /* Make sure that this function is not called from the helper thread */ + assert(!m->thread_running || !in_worker(m)); + +#ifdef OS_IS_WIN32 + SetEvent(m->accept_cond); +#else + pthread_cond_signal(&m->accept_cond); +#endif +} + +int pa_threaded_mainloop_get_retval(pa_threaded_mainloop *m) { + assert(m); + + return pa_mainloop_get_retval(m->real_mainloop); +} + +pa_mainloop_api* pa_threaded_mainloop_get_api(pa_threaded_mainloop*m) { + assert(m); + + return pa_mainloop_get_api(m->real_mainloop); +} + +#else /* defined(OS_IS_WIN32) || defined(HAVE_PTHREAD) */ + +pa_threaded_mainloop *pa_threaded_mainloop_new(void) { + pa_log_error(__FILE__": Threaded main loop not supported on this platform"); + return NULL; +} + +void pa_threaded_mainloop_free(pa_threaded_mainloop* m) { + assert(0); +} + +int pa_threaded_mainloop_start(pa_threaded_mainloop *m) { + assert(0); + return -1; +} + +void pa_threaded_mainloop_stop(pa_threaded_mainloop *m) { + assert(0); +} + +void pa_threaded_mainloop_lock(pa_threaded_mainloop *m) { + assert(0); +} + +void pa_threaded_mainloop_unlock(pa_threaded_mainloop *m) { + assert(0); +} + +void pa_threaded_mainloop_wait(pa_threaded_mainloop *m) { + assert(0); +} + +void pa_threaded_mainloop_signal(pa_threaded_mainloop *m, int wait_for_release) { + assert(0); +} + +int pa_threaded_mainloop_get_retval(pa_threaded_mainloop *m) { + assert(0); +} + +pa_mainloop_api* pa_threaded_mainloop_get_api(pa_threaded_mainloop*m) { + assert(0); + return NULL; +} + +#endif /* defined(OS_IS_WIN32) || defined(HAVE_PTHREAD) */ diff --git a/src/pulse/thread-mainloop.h b/src/pulse/thread-mainloop.h new file mode 100644 index 00000000..fb216c5a --- /dev/null +++ b/src/pulse/thread-mainloop.h @@ -0,0 +1,299 @@ +#ifndef foothreadmainloophfoo +#define foothreadmainloophfoo + +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + 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 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 +#include + +PA_C_DECL_BEGIN + +/** \page threaded_mainloop Threaded Main Loop + * + * \section overv_sec Overview + * + * The threaded main loop implementation is a special version of the primary + * main loop implementation (see \ref mainloop). For the basic design, see + * its documentation. + * + * The added feature in the threaded main loop is that it spawns a new thread + * that runs the real main loop. This allows a synchronous application to use + * the asynchronous API without risking to stall the Polypaudio library. + * + * \section creat_sec Creation + * + * A pa_threaded_mainloop object is created using pa_threaded_mainloop_new(). + * This will only allocate the required structures though, so to use it the + * thread must also be started. This is done through + * pa_threaded_mainloop_start(), after which you can start using the main loop. + * + * \section destr_sec Destruction + * + * When the Polypaudio connection has been terminated, the thread must be + * stopped and the resources freed. Stopping the thread is done using + * pa_threaded_mainloop_stop(), which must be called without the lock (see + * below) held. When that function returns, the thread is stopped and the + * pa_threaded_mainloop object can be freed using pa_threaded_mainloop_free(). + * + * \section lock_sec Locking + * + * Since the Polypaudio API doesn't allow concurrent accesses to objects, + * a locking scheme must be used to guarantee safe usage. The threaded main + * loop API provides such a scheme through the functions + * pa_threaded_mainloop_lock() and pa_threaded_mainloop_unlock(). + * + * The lock is recursive, so it's safe to use it multiple times from the same + * thread. Just make sure you call pa_threaded_mainloop_unlock() the same + * number of times you called pa_threaded_mainloop_lock(). + * + * The lock needs to be held whenever you call any Polypaudio function that + * uses an object associated with this main loop. Make sure you do not hold + * on to the lock more than necessary though, as the threaded main loop stops + * while the lock is held. + * + * Example: + * + * \code + * void my_check_stream_func(pa_threaded_mainloop *m, pa_stream *s) { + * pa_stream_state_t state; + * + * pa_threaded_mainloop_lock(m); + * + * state = pa_stream_get_state(s); + * + * pa_threaded_mainloop_unlock(m); + * + * if (state == PA_STREAM_READY) + * printf("Stream is ready!"); + * else + * printf("Stream is not ready!"); + * } + * \endcode + * + * \section cb_sec Callbacks + * + * Callbacks in Polypaudio are asynchronous, so they require extra care when + * using them together with a threaded main loop. + * + * The easiest way to turn the callback based operations into synchronous + * ones, is to simply wait for the callback to be called and continue from + * there. This is the approach chosen in Polypaudio's threaded API. + * + * \subsection basic_subsec Basic callbacks + * + * For the basic case, where all that is required is to wait for the callback + * to be invoked, the code should look something like this: + * + * Example: + * + * \code + * static void my_drain_callback(pa_stream*s, int success, void *userdata) { + * pa_threaded_mainloop *m; + * + * m = (pa_threaded_mainloop*)userdata; + * assert(m); + * + * pa_threaded_mainloop_signal(m, 0); + * } + * + * void my_drain_stream_func(pa_threaded_mainloop *m, pa_stream *s) { + * pa_operation *o; + * + * pa_threaded_mainloop_lock(m); + * + * o = pa_stream_drain(s, my_drain_callback, m); + * assert(o); + * + * while (pa_operation_get_state(o) != OPERATION_DONE) + * pa_threaded_mainloop_wait(m); + * + * pa_operation_unref(o); + * + * pa_threaded_mainloop_unlock(m); + * } + * \endcode + * + * The main function, my_drain_stream_func(), will wait for the callback to + * be called using pa_threaded_mainloop_wait(). + * + * If your application is multi-threaded, then this waiting must be done + * inside a while loop. The reason for this is that multiple threads might be + * using pa_threaded_mainloop_wait() at the same time. Each thread must + * therefore verify that it was its callback that was invoked. + * + * The callback, my_drain_callback(), indicates to the main function that it + * has been called using pa_threaded_mainloop_signal(). + * + * As you can see, both pa_threaded_mainloop_wait() may only be called with + * the lock held. The same thing is true for pa_threaded_mainloop_signal(), + * but as the lock is held before the callback is invoked, you do not have to + * deal with that. + * + * The functions will not dead lock because the wait function will release + * the lock before waiting and then regrab it once it has been signaled. + * For those of you familiar with threads, the behaviour is that of a + * condition variable. + * + * \subsection data_subsec Data callbacks + * + * For many callbacks, simply knowing that they have been called is + * insufficient. The callback also receives some data that is desired. To + * access this data safely, we must extend our example a bit: + * + * \code + * static int *drain_result; + * + * static void my_drain_callback(pa_stream*s, int success, void *userdata) { + * pa_threaded_mainloop *m; + * + * m = (pa_threaded_mainloop*)userdata; + * assert(m); + * + * drain_result = &success; + * + * pa_threaded_mainloop_signal(m, 1); + * } + * + * void my_drain_stream_func(pa_threaded_mainloop *m, pa_stream *s) { + * pa_operation *o; + * + * pa_threaded_mainloop_lock(m); + * + * o = pa_stream_drain(s, my_drain_callback, m); + * assert(o); + * + * while (pa_operation_get_state(o) != OPERATION_DONE) + * pa_threaded_mainloop_wait(m); + * + * pa_operation_unref(o); + * + * if (*drain_result) + * printf("Success!"); + * else + * printf("Bitter defeat..."); + * + * pa_threaded_mainloop_accept(m); + * + * pa_threaded_mainloop_unlock(m); + * } + * \endcode + * + * The example is a bit silly as it would probably have been easier to just + * copy the contents of success, but for larger data structures this can be + * wasteful. + * + * The difference here compared to the basic callback is the 1 sent to + * pa_threaded_mainloop_signal() and the call to + * pa_threaded_mainloop_accept(). What will happen is that + * pa_threaded_mainloop_signal() will signal the main function and then stop. + * The main function is then free to use the data in the callback until + * pa_threaded_mainloop_accept() is called, which will allow the callback + * to continue. + * + * Note that pa_threaded_mainloop_accept() must be called some time between + * exiting the while loop and unlocking the main loop! Failure to do so will + * result in a race condition. I.e. it is not ok to release the lock and + * regrab it before calling pa_threaded_mainloop_accept(). + * + * \subsection async_subsec Asynchronous callbacks + * + * Polypaudio also has callbacks that are completely asynchronous, meaning + * that they can be called at any time. The threading main loop API provides + * the locking mechanism to handle concurrent accesses, but nothing else. + * Applications will have to handle communication from the callback to the + * main program through some own system. + * + * The callbacks that are completely asynchronous are: + * + * \li State callbacks for contexts, streams, etc. + * \li Subscription notifications + */ + +/** \file + * + * A thread based event loop implementation based on pa_mainloop. The + * event loop is run in a helper thread in the background. A few + * synchronization primitives are available to access the objects + * attached to the event loop safely. */ + +/** An opaque threaded main loop object */ +typedef struct pa_threaded_mainloop pa_threaded_mainloop; + +/** Allocate a new threaded main loop object. You have to call + * pa_threaded_mainloop_start() before the event loop thread starts + * running. */ +pa_threaded_mainloop *pa_threaded_mainloop_new(void); + +/** Free a threaded main loop object. If the event loop thread is + * still running, it is terminated using pa_threaded_mainloop_stop() + * first. */ +void pa_threaded_mainloop_free(pa_threaded_mainloop* m); + +/** Start the event loop thread. */ +int pa_threaded_mainloop_start(pa_threaded_mainloop *m); + +/** Terminate the event loop thread cleanly. Make sure to unlock the + * mainloop object before calling this function. */ +void pa_threaded_mainloop_stop(pa_threaded_mainloop *m); + +/** Lock the event loop object, effectively blocking the event loop + * thread from processing events. You can use this to enforce + * exclusive access to all objects attached to the event loop. This + * lock is recursive. This function may not be called inside the event + * loop thread. Events that are dispatched from the event loop thread + * are executed with this lock held. */ +void pa_threaded_mainloop_lock(pa_threaded_mainloop *m); + +/** Unlock the event loop object, inverse of pa_threaded_mainloop_lock() */ +void pa_threaded_mainloop_unlock(pa_threaded_mainloop *m); + +/** Wait for an event to be signalled by the event loop thread. You + * can use this to pass data from the event loop thread to the main + * thread in synchronized fashion. This function may not be called + * inside the event loop thread. Prior to this call the event loop + * object needs to be locked using pa_threaded_mainloop_lock(). While + * waiting the lock will be released, immediately before returning it + * will be acquired again. */ +void pa_threaded_mainloop_wait(pa_threaded_mainloop *m); + +/** Signal all threads waiting for a signalling event in + * pa_threaded_mainloop_wait(). If wait_for_release is non-zero, do + * not return before the signal was accepted by a + * pa_threaded_mainloop_accept() call. While waiting for that condition + * the event loop object is unlocked. */ +void pa_threaded_mainloop_signal(pa_threaded_mainloop *m, int wait_for_accept); + +/** Accept a signal from the event thread issued with + * pa_threaded_mainloop_signal(). This call should only be used in + * conjunction with pa_threaded_mainloop_signal() with a non-zero + * wait_for_accept value. */ +void pa_threaded_mainloop_accept(pa_threaded_mainloop *m); + +/** Return the return value as specified with the main loop's quit() routine. */ +int pa_threaded_mainloop_get_retval(pa_threaded_mainloop *m); + +/** Return the abstract main loop abstraction layer vtable for this main loop. */ +pa_mainloop_api* pa_threaded_mainloop_get_api(pa_threaded_mainloop*m); + +PA_C_DECL_END + +#endif diff --git a/src/pulse/timeval.c b/src/pulse/timeval.c new file mode 100644 index 00000000..11285230 --- /dev/null +++ b/src/pulse/timeval.c @@ -0,0 +1,142 @@ +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + 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 +#endif + +#include +#include +#include + +#ifdef HAVE_WINDOWS_H +#include +#endif + +#include "../pulsecore/winsock.h" + +#include "timeval.h" + +struct timeval *pa_gettimeofday(struct timeval *tv) { +#ifdef HAVE_GETTIMEOFDAY + assert(tv); + + return gettimeofday(tv, NULL) < 0 ? NULL : tv; +#elif defined(OS_IS_WIN32) + /* + * Copied from implementation by Steven Edwards (LGPL). + * Found on wine mailing list. + */ + +#if defined(_MSC_VER) || defined(__BORLANDC__) +#define EPOCHFILETIME (116444736000000000i64) +#else +#define EPOCHFILETIME (116444736000000000LL) +#endif + + FILETIME ft; + LARGE_INTEGER li; + __int64 t; + + assert(tv); + + GetSystemTimeAsFileTime(&ft); + li.LowPart = ft.dwLowDateTime; + li.HighPart = ft.dwHighDateTime; + t = li.QuadPart; /* In 100-nanosecond intervals */ + t -= EPOCHFILETIME; /* Offset to the Epoch time */ + t /= 10; /* In microseconds */ + tv->tv_sec = (long)(t / 1000000); + tv->tv_usec = (long)(t % 1000000); + + return tv; +#else +#error "Platform lacks gettimeofday() or equivalent function." +#endif +} + +pa_usec_t pa_timeval_diff(const struct timeval *a, const struct timeval *b) { + pa_usec_t r; + assert(a && b); + + /* Check which whan is the earlier time and swap the two arguments if reuqired. */ + if (pa_timeval_cmp(a, b) < 0) { + const struct timeval *c; + c = a; + a = b; + b = c; + } + + /* Calculate the second difference*/ + r = ((pa_usec_t) a->tv_sec - b->tv_sec)* 1000000; + + /* Calculate the microsecond difference */ + if (a->tv_usec > b->tv_usec) + r += ((pa_usec_t) a->tv_usec - b->tv_usec); + else if (a->tv_usec < b->tv_usec) + r -= ((pa_usec_t) b->tv_usec - a->tv_usec); + + return r; +} + +int pa_timeval_cmp(const struct timeval *a, const struct timeval *b) { + assert(a && b); + + if (a->tv_sec < b->tv_sec) + return -1; + + if (a->tv_sec > b->tv_sec) + return 1; + + if (a->tv_usec < b->tv_usec) + return -1; + + if (a->tv_usec > b->tv_usec) + return 1; + + return 0; +} + +pa_usec_t pa_timeval_age(const struct timeval *tv) { + struct timeval now; + assert(tv); + + return pa_timeval_diff(pa_gettimeofday(&now), tv); +} + +struct timeval* pa_timeval_add(struct timeval *tv, pa_usec_t v) { + unsigned long secs; + assert(tv); + + secs = (v/1000000); + tv->tv_sec += (unsigned long) secs; + v -= secs*1000000; + + tv->tv_usec += v; + + /* Normalize */ + while (tv->tv_usec >= 1000000) { + tv->tv_sec++; + tv->tv_usec -= 1000000; + } + + return tv; +} diff --git a/src/pulse/timeval.h b/src/pulse/timeval.h new file mode 100644 index 00000000..e2dbbadb --- /dev/null +++ b/src/pulse/timeval.h @@ -0,0 +1,53 @@ +#ifndef footimevalhfoo +#define footimevalhfoo + +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + 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 +#include + +/** \file + * Utility functions for handling timeval calculations */ + +PA_C_DECL_BEGIN + +struct timeval; + +/** Return the current timestamp, just like UNIX gettimeofday() */ +struct timeval *pa_gettimeofday(struct timeval *tv); + +/** Calculate the difference between the two specified timeval + * structs. */ +pa_usec_t pa_timeval_diff(const struct timeval *a, const struct timeval *b); + +/** Compare the two timeval structs and return 0 when equal, negative when a < b, positive otherwse */ +int pa_timeval_cmp(const struct timeval *a, const struct timeval *b); + +/** Return the time difference between now and the specified timestamp */ +pa_usec_t pa_timeval_age(const struct timeval *tv); + +/** Add the specified time inmicroseconds to the specified timeval structure */ +struct timeval* pa_timeval_add(struct timeval *tv, pa_usec_t v); + +PA_C_DECL_END + +#endif diff --git a/src/pulse/utf8.c b/src/pulse/utf8.c new file mode 100644 index 00000000..33fa7214 --- /dev/null +++ b/src/pulse/utf8.c @@ -0,0 +1,237 @@ +/* $Id$ */ + +/* This file is based on the GLIB utf8 validation functions. The + * original license text follows. */ + +/* gutf8.c - Operations on UTF-8 strings. + * + * Copyright (C) 1999 Tom Tromey + * Copyright (C) 2000 Red Hat, Inc. + * + * This library 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 of the License, or (at your option) any later version. + * + * This library 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 this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +#ifdef HAVE_ICONV +#include +#endif + +#include "utf8.h" +#include "xmalloc.h" + +#define FILTER_CHAR '_' + +static inline int is_unicode_valid(uint32_t ch) { + if (ch >= 0x110000) /* End of unicode space */ + return 0; + if ((ch & 0xFFFFF800) == 0xD800) /* Reserved area for UTF-16 */ + return 0; + if ((ch >= 0xFDD0) && (ch <= 0xFDEF)) /* Reserved */ + return 0; + if ((ch & 0xFFFE) == 0xFFFE) /* BOM (Byte Order Mark) */ + return 0; + return 1; +} + +static inline int is_continuation_char(uint8_t ch) { + if ((ch & 0xc0) != 0x80) /* 10xxxxxx */ + return 0; + return 1; +} + +static inline void merge_continuation_char(uint32_t *u_ch, uint8_t ch) { + *u_ch <<= 6; + *u_ch |= ch & 0x3f; +} + +static char* utf8_validate(const char *str, char *output) { + uint32_t val = 0; + uint32_t min = 0; + const uint8_t *p, *last; + int size; + uint8_t *o; + + o = (uint8_t*) output; + for (p = (const uint8_t*) str; *p; p++) { + if (*p < 128) { + if (o) + *o = *p; + } else { + last = p; + + if ((*p & 0xe0) == 0xc0) { /* 110xxxxx two-char seq. */ + size = 2; + min = 128; + val = *p & 0x1e; + goto ONE_REMAINING; + } else if ((*p & 0xf0) == 0xe0) { /* 1110xxxx three-char seq.*/ + size = 3; + min = (1 << 11); + val = *p & 0x0f; + goto TWO_REMAINING; + } else if ((*p & 0xf8) == 0xf0) { /* 11110xxx four-char seq */ + size = 4; + min = (1 << 16); + val = *p & 0x07; + } else { + size = 1; + goto error; + } + + p++; + if (!is_continuation_char(*p)) + goto error; + merge_continuation_char(&val, *p); + +TWO_REMAINING: + p++; + if (!is_continuation_char(*p)) + goto error; + merge_continuation_char(&val, *p); + +ONE_REMAINING: + p++; + if (!is_continuation_char(*p)) + goto error; + merge_continuation_char(&val, *p); + + if (val < min) + goto error; + + if (!is_unicode_valid(val)) + goto error; + + if (o) { + memcpy(o, last, size); + o += size - 1; + } + + if (o) + o++; + + continue; + +error: + if (o) { + *o = FILTER_CHAR; + p = last; /* We retry at the next character */ + } else + goto failure; + } + + if (o) + o++; + } + + if (o) { + *o = '\0'; + return output; + } + + return (char*) str; + +failure: + return NULL; +} + +const char* pa_utf8_valid (const char *str) { + return utf8_validate(str, NULL); +} + +char* pa_utf8_filter (const char *str) { + char *new_str; + + new_str = pa_xnew(char, strlen(str) + 1); + + return utf8_validate(str, new_str); +} + +#ifdef HAVE_ICONV + +static char* iconv_simple(const char *str, const char *to, const char *from) { + char *new_str; + size_t len, inlen; + + iconv_t cd; + ICONV_CONST char *inbuf; + char *outbuf; + size_t res, inbytes, outbytes; + + cd = iconv_open(to, from); + if (cd == (iconv_t)-1) + return NULL; + + inlen = len = strlen(str) + 1; + new_str = pa_xmalloc(len); + assert(new_str); + + while (1) { + inbuf = (ICONV_CONST char*)str; /* Brain dead prototype for iconv() */ + inbytes = inlen; + outbuf = new_str; + outbytes = len; + + res = iconv(cd, &inbuf, &inbytes, &outbuf, &outbytes); + + if (res != (size_t)-1) + break; + + if (errno != E2BIG) { + pa_xfree(new_str); + new_str = NULL; + break; + } + + assert(inbytes != 0); + + len += inbytes; + new_str = pa_xrealloc(new_str, len); + assert(new_str); + } + + iconv_close(cd); + + return new_str; +} + +char* pa_utf8_to_locale (const char *str) { + return iconv_simple(str, "", "UTF-8"); +} + +char* pa_locale_to_utf8 (const char *str) { + return iconv_simple(str, "UTF-8", ""); +} + +#else + +char* pa_utf8_to_locale (const char *str) { + return NULL; +} + +char* pa_locale_to_utf8 (const char *str) { + return NULL; +} + +#endif diff --git a/src/pulse/utf8.h b/src/pulse/utf8.h new file mode 100644 index 00000000..2eac724d --- /dev/null +++ b/src/pulse/utf8.h @@ -0,0 +1,47 @@ +#ifndef fooutf8hfoo +#define fooutf8hfoo + +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + 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 + +/** \file + * UTF8 Validation functions + */ + +PA_C_DECL_BEGIN + +/** Test if the specified strings qualifies as valid UTF8. Return the string if so, otherwise NULL */ +const char *pa_utf8_valid(const char *str); + +/** Filter all invalid UTF8 characters from the specified string, returning a new fully UTF8 valid string. Don't forget to free the returned string with pa_xfree() */ +char *pa_utf8_filter(const char *str); + +/** Convert a UTF-8 string to the current locale. Free the string using pa_xfree(). */ +char* pa_utf8_to_locale (const char *str); + +/** Convert a string in the current locale to UTF-8. Free the string using pa_xfree(). */ +char* pa_locale_to_utf8 (const char *str); + +PA_C_DECL_END + +#endif diff --git a/src/pulse/util.c b/src/pulse/util.c new file mode 100644 index 00000000..338607c4 --- /dev/null +++ b/src/pulse/util.c @@ -0,0 +1,227 @@ +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + 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 +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_PWD_H +#include +#endif + +#ifdef HAVE_SYS_SOCKET_H +#include +#endif + +#ifdef HAVE_NETDB_H +#include +#endif + +#ifdef HAVE_WINDOWS_H +#include +#endif + +#include "../pulsecore/winsock.h" + +#include +#include +#include + +#include "util.h" + +#ifndef OS_IS_WIN32 +#define PATH_SEP '/' +#else +#define PATH_SEP '\\' +#endif + +char *pa_get_user_name(char *s, size_t l) { + char *p; + char buf[1024]; + +#ifdef HAVE_PWD_H + struct passwd pw, *r; +#endif + + assert(s && l > 0); + + if (!(p = getenv("USER")) && !(p = getenv("LOGNAME")) && !(p = getenv("USERNAME"))) { +#ifdef HAVE_PWD_H + +#ifdef HAVE_GETPWUID_R + if (getpwuid_r(getuid(), &pw, buf, sizeof(buf), &r) != 0 || !r) { +#else + /* XXX Not thread-safe, but needed on OSes (e.g. FreeBSD 4.X) + * that do not support getpwuid_r. */ + if ((r = getpwuid(getuid())) == NULL) { +#endif + snprintf(s, l, "%lu", (unsigned long) getuid()); + return s; + } + + p = r->pw_name; + +#elif defined(OS_IS_WIN32) /* HAVE_PWD_H */ + DWORD size = sizeof(buf); + + if (!GetUserName(buf, &size)) + return NULL; + + p = buf; + +#else /* HAVE_PWD_H */ + return NULL; +#endif /* HAVE_PWD_H */ + } + + return pa_strlcpy(s, p, l); +} + +char *pa_get_host_name(char *s, size_t l) { + assert(s && l > 0); + if (gethostname(s, l) < 0) { + pa_log(__FILE__": gethostname(): %s", pa_cstrerror(errno)); + return NULL; + } + s[l-1] = 0; + return s; +} + +char *pa_get_home_dir(char *s, size_t l) { + char *e; + +#ifdef HAVE_PWD_H + char buf[1024]; + struct passwd pw, *r; +#endif + + assert(s && l); + + if ((e = getenv("HOME"))) + return pa_strlcpy(s, e, l); + + if ((e = getenv("USERPROFILE"))) + return pa_strlcpy(s, e, l); + +#ifdef HAVE_PWD_H +#ifdef HAVE_GETPWUID_R + if (getpwuid_r(getuid(), &pw, buf, sizeof(buf), &r) != 0 || !r) { + pa_log(__FILE__": getpwuid_r() failed"); +#else + /* XXX Not thread-safe, but needed on OSes (e.g. FreeBSD 4.X) + * that do not support getpwuid_r. */ + if ((r = getpwuid(getuid())) == NULL) { + pa_log(__FILE__": getpwuid_r() failed"); +#endif + return NULL; + } + + return pa_strlcpy(s, r->pw_dir, l); +#else /* HAVE_PWD_H */ + return NULL; +#endif +} + +char *pa_get_binary_name(char *s, size_t l) { + +#ifdef HAVE_READLINK + char path[PATH_MAX]; + int i; + assert(s && l); + + /* This works on Linux only */ + + snprintf(path, sizeof(path), "/proc/%u/exe", (unsigned) getpid()); + if ((i = readlink(path, s, l-1)) < 0) + return NULL; + + s[i] = 0; + return s; +#elif defined(OS_IS_WIN32) + char path[PATH_MAX]; + if (!GetModuleFileName(NULL, path, PATH_MAX)) + return NULL; + pa_strlcpy(s, pa_path_get_filename(path), l); + return s; +#else + return NULL; +#endif +} + +const char *pa_path_get_filename(const char *p) { + char *fn; + + if ((fn = strrchr(p, PATH_SEP))) + return fn+1; + + return (const char*) p; +} + +char *pa_get_fqdn(char *s, size_t l) { + char hn[256]; +#ifdef HAVE_GETADDRINFO + struct addrinfo *a, hints; +#endif + + if (!pa_get_host_name(hn, sizeof(hn))) + return NULL; + +#ifdef HAVE_GETADDRINFO + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_flags = AI_CANONNAME; + + if (getaddrinfo(hn, NULL, &hints, &a) < 0 || !a || !a->ai_canonname || !*a->ai_canonname) + return pa_strlcpy(s, hn, l); + + pa_strlcpy(s, a->ai_canonname, l); + freeaddrinfo(a); + return s; +#else + return pa_strlcpy(s, hn, l); +#endif +} + +int pa_msleep(unsigned long t) { +#ifdef OS_IS_WIN32 + Sleep(t); + return 0; +#elif defined(HAVE_NANOSLEEP) + struct timespec ts; + + ts.tv_sec = t/1000; + ts.tv_nsec = (t % 1000) * 1000000; + + return nanosleep(&ts, NULL); +#else +#error "Platform lacks a sleep function." +#endif +} diff --git a/src/pulse/util.h b/src/pulse/util.h new file mode 100644 index 00000000..5c03b0a9 --- /dev/null +++ b/src/pulse/util.h @@ -0,0 +1,59 @@ +#ifndef fooutilhfoo +#define fooutilhfoo + +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + 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 + +#include + +/** \file + * Assorted utility functions */ + +PA_C_DECL_BEGIN + +/** Return the current username in the specified string buffer. */ +char *pa_get_user_name(char *s, size_t l); + +/** Return the current hostname in the specified buffer. */ +char *pa_get_host_name(char *s, size_t l); + +/** Return the fully qualified domain name in s */ +char *pa_get_fqdn(char *s, size_t l); + +/** Return the home directory of the current user */ +char *pa_get_home_dir(char *s, size_t l); + +/** Return the binary file name of the current process. This is not + * supported on all architectures, in which case NULL is returned. */ +char *pa_get_binary_name(char *s, size_t l); + +/** Return a pointer to the filename inside a path (which is the last + * component). */ +const char *pa_path_get_filename(const char *p); + +/** Wait t milliseconds */ +int pa_msleep(unsigned long t); + +PA_C_DECL_END + +#endif diff --git a/src/pulse/version.h.in b/src/pulse/version.h.in new file mode 100644 index 00000000..6c5492e8 --- /dev/null +++ b/src/pulse/version.h.in @@ -0,0 +1,53 @@ +#ifndef fooversionhfoo /*-*-C-*-*/ +#define fooversionhfoo + +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + 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 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. +***/ + +/* WARNING: Make sure to edit the real source file version.h.in! */ + +#include + +/** \file + * Define header version */ + +PA_C_DECL_BEGIN + +/** Return the version of the header files. Keep in mind that this is +a macro and not a function, so it is impossible to get the pointer of +it. */ +#define pa_get_headers_version() ("@PACKAGE_VERSION@") + +/** Return the version of the library the current application is linked to. */ +const char* pa_get_library_version(void); + +/** The current API version. Version 6 relates to pulseaudio + * 0.6. Prior versions (i.e. Polypaudio 0.5.1 and older) have + * PA_API_VERSION undefined. */ +#define PA_API_VERSION @PA_API_VERSION@ + +/** The current protocol version. Version 8 relates to pulseaudio 0.8. + * \since 0.8 */ +#define PA_PROTOCOL_VERSION @PA_PROTOCOL_VERSION@ + +PA_C_DECL_END + +#endif diff --git a/src/pulse/volume.c b/src/pulse/volume.c new file mode 100644 index 00000000..530814e0 --- /dev/null +++ b/src/pulse/volume.c @@ -0,0 +1,176 @@ +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + 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 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 +#endif + +#include +#include +#include + +#include "volume.h" + +int pa_cvolume_equal(const pa_cvolume *a, const pa_cvolume *b) { + int i; + assert(a); + assert(b); + + if (a->channels != b->channels) + return 0; + + for (i = 0; i < a->channels; i++) + if (a->values[i] != b->values[i]) + return 0; + + return 1; +} + +pa_cvolume* pa_cvolume_set(pa_cvolume *a, unsigned channels, pa_volume_t v) { + int i; + + assert(a); + assert(channels > 0); + assert(channels <= PA_CHANNELS_MAX); + + a->channels = channels; + + for (i = 0; i < a->channels; i++) + a->values[i] = v; + + return a; +} + +pa_volume_t pa_cvolume_avg(const pa_cvolume *a) { + uint64_t sum = 0; + int i; + assert(a); + + for (i = 0; i < a->channels; i++) + sum += a->values[i]; + + sum /= a->channels; + + return (pa_volume_t) sum; +} + +pa_volume_t pa_sw_volume_multiply(pa_volume_t a, pa_volume_t b) { + return pa_sw_volume_from_linear(pa_sw_volume_to_linear(a)* pa_sw_volume_to_linear(b)); +} + +#define USER_DECIBEL_RANGE 30 + +pa_volume_t pa_sw_volume_from_dB(double dB) { + if (dB <= -USER_DECIBEL_RANGE) + return PA_VOLUME_MUTED; + + return (pa_volume_t) ((dB/USER_DECIBEL_RANGE+1)*PA_VOLUME_NORM); +} + +double pa_sw_volume_to_dB(pa_volume_t v) { + if (v == PA_VOLUME_MUTED) + return PA_DECIBEL_MININFTY; + + return ((double) v/PA_VOLUME_NORM-1)*USER_DECIBEL_RANGE; +} + +pa_volume_t pa_sw_volume_from_linear(double v) { + + if (v <= 0) + return PA_VOLUME_MUTED; + + if (v > .999 && v < 1.001) + return PA_VOLUME_NORM; + + return pa_sw_volume_from_dB(20*log10(v)); +} + +double pa_sw_volume_to_linear(pa_volume_t v) { + + if (v == PA_VOLUME_MUTED) + return 0; + + return pow(10, pa_sw_volume_to_dB(v)/20); +} + +char *pa_cvolume_snprint(char *s, size_t l, const pa_cvolume *c) { + unsigned channel; + int first = 1; + char *e; + + assert(s); + assert(l > 0); + assert(c); + + *(e = s) = 0; + + for (channel = 0; channel < c->channels && l > 1; channel++) { + l -= snprintf(e, l, "%s%u: %3u%%", + first ? "" : " ", + channel, + (c->values[channel]*100)/PA_VOLUME_NORM); + + e = strchr(e, 0); + first = 0; + } + + return s; +} + +/** Return non-zero if the volume of all channels is equal to the specified value */ +int pa_cvolume_channels_equal_to(const pa_cvolume *a, pa_volume_t v) { + unsigned c; + assert(a); + + for (c = 0; c < a->channels; c++) + if (a->values[c] != v) + return 0; + + return 1; +} + +pa_cvolume *pa_sw_cvolume_multiply(pa_cvolume *dest, const pa_cvolume *a, const pa_cvolume *b) { + unsigned i; + + assert(dest); + assert(a); + assert(b); + + for (i = 0; i < a->channels && i < b->channels && i < PA_CHANNELS_MAX; i++) { + + dest->values[i] = pa_sw_volume_multiply( + i < a->channels ? a->values[i] : PA_VOLUME_NORM, + i < b->channels ? b->values[i] : PA_VOLUME_NORM); + } + + dest->channels = i; + + return dest; +} + +int pa_cvolume_valid(const pa_cvolume *v) { + assert(v); + + if (v->channels <= 0 || v->channels > PA_CHANNELS_MAX) + return 0; + + return 1; +} diff --git a/src/pulse/volume.h b/src/pulse/volume.h new file mode 100644 index 00000000..d403a09e --- /dev/null +++ b/src/pulse/volume.h @@ -0,0 +1,172 @@ +#ifndef foovolumehfoo +#define foovolumehfoo + +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + 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 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 +#include +#include + +/** \page volume Volume Control + * + * \section overv_sec Overview + * + * Sinks, sources, sink inputs and samples can all have their own volumes. + * To deal with these, The Polypaudio libray contains a number of functions + * that ease handling. + * + * The basic volume type in Polypaudio is the \ref pa_volume_t type. Most of + * the time, applications will use the aggregated pa_cvolume structure that + * can store the volume of all channels at once. + * + * Volumes commonly span between muted (0%), and normal (100%). It is possible + * to set volumes to higher than 100%, but clipping might occur. + * + * \section calc_sec Calculations + * + * The volumes in Polypaudio are logarithmic in nature and applications + * shouldn't perform calculations with them directly. Instead, they should + * be converted to and from either dB or a linear scale: + * + * \li dB - pa_sw_volume_from_dB() / pa_sw_volume_to_dB() + * \li Linear - pa_sw_volume_from_linear() / pa_sw_volume_to_linear() + * + * For simple multiplication, pa_sw_volume_multiply() and + * pa_sw_cvolume_multiply() can be used. + * + * Calculations can only be reliably performed on software volumes + * as it is commonly unknown what scale hardware volumes relate to. + * + * The functions described above are only valid when used with + * software volumes. Hence it is usually a better idea to treat all + * volume values as opaque with a range from PA_VOLUME_MUTE (0%) to + * PA_VOLUME_NORM (100%) and to refrain from any calculations with + * them. + * + * \section conv_sec Convenience Functions + * + * To handle the pa_cvolume structure, the Polypaudio library provides a + * number of convenienc functions: + * + * \li pa_cvolume_valid() - Tests if a pa_cvolume structure is valid. + * \li pa_cvolume_equal() - Tests if two pa_cvolume structures are identical. + * \li pa_cvolume_channels_equal_to() - Tests if all channels of a pa_cvolume + * structure have a given volume. + * \li pa_cvolume_is_muted() - Tests if all channels of a pa_cvolume + * structure are muted. + * \li pa_cvolume_is_norm() - Tests if all channels of a pa_cvolume structure + * are at a normal volume. + * \li pa_cvolume_set() - Set all channels of a pa_cvolume structure to a + * certain volume. + * \li pa_cvolume_reset() - Set all channels of a pa_cvolume structure to a + * normal volume. + * \li pa_cvolume_mute() - Set all channels of a pa_cvolume structure to a + * muted volume. + * \li pa_cvolume_avg() - Return the average volume of all channels. + * \li pa_cvolume_snprint() - Pretty print a pa_cvolume structure. + */ + +/** \file + * Constants and routines for volume handling */ + +PA_C_DECL_BEGIN + +/** Volume specification: + * PA_VOLUME_MUTED: silence; + * < PA_VOLUME_NORM: decreased volume; + * PA_VOLUME_NORM: normal volume; + * > PA_VOLUME_NORM: increased volume */ +typedef uint32_t pa_volume_t; + +/** Normal volume (100%) */ +#define PA_VOLUME_NORM (0x10000) + +/** Muted volume (0%) */ +#define PA_VOLUME_MUTED (0) + +/** A structure encapsulating a per-channel volume */ +typedef struct pa_cvolume { + uint8_t channels; /**< Number of channels */ + pa_volume_t values[PA_CHANNELS_MAX]; /**< Per-channel volume */ +} pa_cvolume; + +/** Return non-zero when *a == *b */ +int pa_cvolume_equal(const pa_cvolume *a, const pa_cvolume *b); + +/** Set the volume of all channels to PA_VOLUME_NORM */ +#define pa_cvolume_reset(a, n) pa_cvolume_set((a), (n), PA_VOLUME_NORM) + +/** Set the volume of all channels to PA_VOLUME_MUTED */ +#define pa_cvolume_mute(a, n) pa_cvolume_set((a), (n), PA_VOLUME_MUTED) + +/** Set the volume of all channels to the specified parameter */ +pa_cvolume* pa_cvolume_set(pa_cvolume *a, unsigned channels, pa_volume_t v); + +/** Maximum length of the strings returned by pa_cvolume_snprint() */ +#define PA_CVOLUME_SNPRINT_MAX 64 + +/** Pretty print a volume structure */ +char *pa_cvolume_snprint(char *s, size_t l, const pa_cvolume *c); + +/** Return the average volume of all channels */ +pa_volume_t pa_cvolume_avg(const pa_cvolume *a); + +/** Return TRUE when the passed cvolume structure is valid, FALSE otherwise */ +int pa_cvolume_valid(const pa_cvolume *v); + +/** Return non-zero if the volume of all channels is equal to the specified value */ +int pa_cvolume_channels_equal_to(const pa_cvolume *a, pa_volume_t v); + +/** Return 1 if the specified volume has all channels muted */ +#define pa_cvolume_is_muted(a) pa_cvolume_channels_equal_to((a), PA_VOLUME_MUTED) + +/** Return 1 if the specified volume has all channels on normal level */ +#define pa_cvolume_is_norm(a) pa_cvolume_channels_equal_to((a), PA_VOLUME_NORM) + +/** Multiply two volumes specifications, return the result. This uses PA_VOLUME_NORM as neutral element of multiplication. This is only valid for software volumes! */ +pa_volume_t pa_sw_volume_multiply(pa_volume_t a, pa_volume_t b); + +/** Multiply to per-channel volumes and return the result in *dest. This is only valid for software volumes! */ +pa_cvolume *pa_sw_cvolume_multiply(pa_cvolume *dest, const pa_cvolume *a, const pa_cvolume *b); + +/** Convert a decibel value to a volume. This is only valid for software volumes! \since 0.4 */ +pa_volume_t pa_sw_volume_from_dB(double f); + +/** Convert a volume to a decibel value. This is only valid for software volumes! \since 0.4 */ +double pa_sw_volume_to_dB(pa_volume_t v); + +/** Convert a linear factor to a volume. This is only valid for software volumes! \since 0.8 */ +pa_volume_t pa_sw_volume_from_linear(double v); + +/** Convert a volume to a linear factor. This is only valid for software volumes! \since 0.8 */ +double pa_sw_volume_to_linear(pa_volume_t v); + +#ifdef INFINITY +#define PA_DECIBEL_MININFTY (-INFINITY) +#else +/** This value is used as minus infinity when using pa_volume_{to,from}_dB(). \since 0.4 */ +#define PA_DECIBEL_MININFTY (-200) +#endif + +PA_C_DECL_END + +#endif diff --git a/src/pulse/xmalloc.c b/src/pulse/xmalloc.c new file mode 100644 index 00000000..46871aeb --- /dev/null +++ b/src/pulse/xmalloc.c @@ -0,0 +1,128 @@ +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + 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 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 +#endif + +#include +#include +#include +#include +#include + +#include +#include + +#include "xmalloc.h" + +/* Make sure not to allocate more than this much memory. */ +#define MAX_ALLOC_SIZE (1024*1024*20) /* 20MB */ + +/* #undef malloc */ +/* #undef free */ +/* #undef realloc */ +/* #undef strndup */ +/* #undef strdup */ + +static void oom(void) PA_GCC_NORETURN; + +/** called in case of an OOM situation. Prints an error message and + * exits */ +static void oom(void) { + static const char e[] = "Not enough memory\n"; + pa_loop_write(STDERR_FILENO, e, sizeof(e)-1); +#ifdef SIGQUIT + raise(SIGQUIT); +#endif + _exit(1); +} + +void* pa_xmalloc(size_t size) { + void *p; + assert(size > 0); + assert(size < MAX_ALLOC_SIZE); + + if (!(p = malloc(size))) + oom(); + + return p; +} + +void* pa_xmalloc0(size_t size) { + void *p; + assert(size > 0); + assert(size < MAX_ALLOC_SIZE); + + if (!(p = calloc(1, size))) + oom(); + + return p; +} + +void *pa_xrealloc(void *ptr, size_t size) { + void *p; + assert(size > 0); + assert(size < MAX_ALLOC_SIZE); + + if (!(p = realloc(ptr, size))) + oom(); + return p; +} + +void* pa_xmemdup(const void *p, size_t l) { + if (!p) + return NULL; + else { + char *r = pa_xmalloc(l); + memcpy(r, p, l); + return r; + } +} + +char *pa_xstrdup(const char *s) { + if (!s) + return NULL; + + return pa_xmemdup(s, strlen(s)+1); +} + +char *pa_xstrndup(const char *s, size_t l) { + char *e, *r; + + if (!s) + return NULL; + + if ((e = memchr(s, 0, l))) + return pa_xmemdup(s, e-s+1); + + r = pa_xmalloc(l+1); + memcpy(r, s, l); + r[l] = 0; + return r; +} + +void pa_xfree(void *p) { + if (!p) + return; + + free(p); +} diff --git a/src/pulse/xmalloc.h b/src/pulse/xmalloc.h new file mode 100644 index 00000000..126c495c --- /dev/null +++ b/src/pulse/xmalloc.h @@ -0,0 +1,78 @@ +#ifndef foomemoryhfoo +#define foomemoryhfoo + +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + 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 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 +#include +#include +#include +#include + +/** \file + * Memory allocation functions. + */ + +PA_C_DECL_BEGIN + +/** Allocate the specified number of bytes, just like malloc() does. However, in case of OOM, terminate */ +void* pa_xmalloc(size_t l); + +/** Same as pa_xmalloc(), but initialize allocated memory to 0 */ +void *pa_xmalloc0(size_t l); + +/** The combination of pa_xmalloc() and realloc() */ +void *pa_xrealloc(void *ptr, size_t size); + +/** Free allocated memory */ +void pa_xfree(void *p); + +/** Duplicate the specified string, allocating memory with pa_xmalloc() */ +char *pa_xstrdup(const char *s); + +/** Duplicate the specified string, but truncate after l characters */ +char *pa_xstrndup(const char *s, size_t l); + +/** Duplicate the specified memory block */ +void* pa_xmemdup(const void *p, size_t l); + +/** Internal helper for pa_xnew() */ +static inline void* pa_xnew_internal(unsigned n, size_t k) { + assert(n < INT_MAX/k); + return pa_xmalloc(n*k); +} + +/** Allocate n new structures of the specified type. */ +#define pa_xnew(type, n) ((type*) pa_xnew_internal((n), sizeof(type))) + +/** Internal helper for pa_xnew0() */ +static inline void* pa_xnew0_internal(unsigned n, size_t k) { + assert(n < INT_MAX/k); + return pa_xmalloc0(n*k); +} + +/** Same as pa_xnew() but set the memory to zero */ +#define pa_xnew0(type, n) ((type*) pa_xnew0_internal((n), sizeof(type))) + +PA_C_DECL_END + +#endif -- cgit From 25f79693b150eb157a92d48036b904d3f7132a08 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 19 Jun 2006 21:55:07 +0000 Subject: rename polypaudio.h to pulseaudio.h git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@1034 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/polypaudio.h | 115 ------------------------------------------------- src/pulse/pulseaudio.h | 115 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 115 insertions(+), 115 deletions(-) delete mode 100644 src/pulse/polypaudio.h create mode 100644 src/pulse/pulseaudio.h (limited to 'src/pulse') diff --git a/src/pulse/polypaudio.h b/src/pulse/polypaudio.h deleted file mode 100644 index 5bbd4cc5..00000000 --- a/src/pulse/polypaudio.h +++ /dev/null @@ -1,115 +0,0 @@ -#ifndef foopulseaudiohfoo -#define foopulseaudiohfoo - -/* $Id$ */ - -/*** - This file is part of PulseAudio. - - 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/** \file - * Include all pulselib header files at once. The following - * files are included: \ref mainloop-api.h, \ref sample.h, \ref def.h, - * \ref context.h, \ref stream.h, \ref introspect.h, \ref subscribe.h, - * \ref scache.h, \ref version.h, \ref error.h, \ref channelmap.h, - * \ref operation.h,\ref volume.h, \ref xmalloc.h, \ref utf8.h, \ref - * thread-mainloop.h, \ref mainloop.h, \ref util.h, \ref timeval.h and - * \ref mainloop-signal.h at once */ - -/** \mainpage - * - * \section intro_sec Introduction - * - * This document describes the client API for the pulseaudio sound - * server. The API comes in two flavours to accomodate different styles - * of applications and different needs in complexity: - * - * \li The complete but somewhat complicated to use asynchronous API - * \li The simplified, easy to use, but limited synchronous API - * - * All strings in Polypaudio are in the UTF-8 encoding, regardless of current - * locale. Some functions will filter invalid sequences from the string, some - * will simply fail. To ensure reliable behaviour, make sure everything you - * pass to the API is already in UTF-8. - - * \section simple_sec Simple API - * - * Use this if you develop your program in synchronous style and just - * need a way to play or record data on the sound server. See - * \subpage simple for more details. - * - * \section async_sec Asynchronous API - * - * Use this if you develop your programs in asynchronous, event loop - * based style or if you want to use the advanced features of the - * pulseaudio API. A guide can be found in \subpage async. - * - * By using the built-in threaded main loop, it is possible to acheive a - * pseudo-synchronous API, which can be useful in synchronous applications - * where the simple API is insufficient. See the \ref async page for - * details. - * - * \section thread_sec Threads - * - * The pulseaudio client libraries are not designed to be used in a - * heavily threaded environment. They are however designed to be reentrant - * safe. - * - * To use a the libraries in a threaded environment, you must assure that - * all objects are only used in one thread at a time. Normally, this means - * that all objects belonging to a single context must be accessed from the - * same thread. - * - * The included main loop implementation is also not thread safe. Take care - * to make sure event lists are not manipulated when any other code is - * using the main loop. - * - * \section pkgconfig pkg-config - * - * The pulseaudio libraries provide pkg-config snippets for the different - * modules: - * - * \li pulselib - The asynchronous API and the internal main loop - * implementation. - * \li pulselib-glib12-mainloop - GLIB 1.2 main loop bindings. - * \li pulselib-glib-mainloop - GLIB 2.x main loop bindings. - * \li pulselib-simple - The simple pulseaudio API. - */ - -#endif diff --git a/src/pulse/pulseaudio.h b/src/pulse/pulseaudio.h new file mode 100644 index 00000000..5bbd4cc5 --- /dev/null +++ b/src/pulse/pulseaudio.h @@ -0,0 +1,115 @@ +#ifndef foopulseaudiohfoo +#define foopulseaudiohfoo + +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** \file + * Include all pulselib header files at once. The following + * files are included: \ref mainloop-api.h, \ref sample.h, \ref def.h, + * \ref context.h, \ref stream.h, \ref introspect.h, \ref subscribe.h, + * \ref scache.h, \ref version.h, \ref error.h, \ref channelmap.h, + * \ref operation.h,\ref volume.h, \ref xmalloc.h, \ref utf8.h, \ref + * thread-mainloop.h, \ref mainloop.h, \ref util.h, \ref timeval.h and + * \ref mainloop-signal.h at once */ + +/** \mainpage + * + * \section intro_sec Introduction + * + * This document describes the client API for the pulseaudio sound + * server. The API comes in two flavours to accomodate different styles + * of applications and different needs in complexity: + * + * \li The complete but somewhat complicated to use asynchronous API + * \li The simplified, easy to use, but limited synchronous API + * + * All strings in Polypaudio are in the UTF-8 encoding, regardless of current + * locale. Some functions will filter invalid sequences from the string, some + * will simply fail. To ensure reliable behaviour, make sure everything you + * pass to the API is already in UTF-8. + + * \section simple_sec Simple API + * + * Use this if you develop your program in synchronous style and just + * need a way to play or record data on the sound server. See + * \subpage simple for more details. + * + * \section async_sec Asynchronous API + * + * Use this if you develop your programs in asynchronous, event loop + * based style or if you want to use the advanced features of the + * pulseaudio API. A guide can be found in \subpage async. + * + * By using the built-in threaded main loop, it is possible to acheive a + * pseudo-synchronous API, which can be useful in synchronous applications + * where the simple API is insufficient. See the \ref async page for + * details. + * + * \section thread_sec Threads + * + * The pulseaudio client libraries are not designed to be used in a + * heavily threaded environment. They are however designed to be reentrant + * safe. + * + * To use a the libraries in a threaded environment, you must assure that + * all objects are only used in one thread at a time. Normally, this means + * that all objects belonging to a single context must be accessed from the + * same thread. + * + * The included main loop implementation is also not thread safe. Take care + * to make sure event lists are not manipulated when any other code is + * using the main loop. + * + * \section pkgconfig pkg-config + * + * The pulseaudio libraries provide pkg-config snippets for the different + * modules: + * + * \li pulselib - The asynchronous API and the internal main loop + * implementation. + * \li pulselib-glib12-mainloop - GLIB 1.2 main loop bindings. + * \li pulselib-glib-mainloop - GLIB 2.x main loop bindings. + * \li pulselib-simple - The simple pulseaudio API. + */ + +#endif -- cgit From 10b5e997d7a8a4e955ce49cc816fdcd36225ff6e Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 19 Jun 2006 22:11:49 +0000 Subject: replace a few remaining uppercase "Polypaudio" occurences with "PulseAudio" git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@1036 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/context.h | 2 +- src/pulse/introspect.h | 6 +++--- src/pulse/pulseaudio.h | 2 +- src/pulse/sample.h | 10 +++++----- src/pulse/stream.h | 16 ++++++++-------- src/pulse/thread-mainloop.h | 14 +++++++------- src/pulse/version.h.in | 4 ++-- src/pulse/volume.h | 8 ++++---- 8 files changed, 31 insertions(+), 31 deletions(-) (limited to 'src/pulse') diff --git a/src/pulse/context.h b/src/pulse/context.h index 65d70e8b..ad8c9f3f 100644 --- a/src/pulse/context.h +++ b/src/pulse/context.h @@ -59,7 +59,7 @@ * * \li \subpage mainloop - A minimal but fast implementation based on poll(). * \li \subpage threaded_mainloop - A special version of the previous - * implementation where all of Polypaudio's + * implementation where all of PulseAudio's * internal handling runs in a separate * thread. * \li \subpage glib-mainloop - A wrapper around GLIB's main loop. Available diff --git a/src/pulse/introspect.h b/src/pulse/introspect.h index 8fe218bc..23d736c6 100644 --- a/src/pulse/introspect.h +++ b/src/pulse/introspect.h @@ -35,7 +35,7 @@ * \section overv_sec Overview * * Sometimes it is necessary to query and modify global settings in the - * server. For this, Polypaudio has the introspection API. It can list sinks, + * server. For this, PulseAudio has the introspection API. It can list sinks, * sources, samples and other aspects of the server. It can also modify the * attributes of the server that will affect operations on a global level, * and not just the application's context. @@ -121,7 +121,7 @@ * * \subsection module_subsec Driver Modules * - * Polypaudio driver modules are identified by index and are retrieved using either + * PulseAudio driver modules are identified by index and are retrieved using either * pa_context_get_module_info() or pa_context_get_module_info_list(). The * information structure is called pa_module_info. * @@ -137,7 +137,7 @@ * * \subsection client_subsec Clients * - * Polypaudio clients are also identified by index and are retrieved using + * PulseAudio clients are also identified by index and are retrieved using * either pa_context_get_client_info() or pa_context_get_client_info_list(). * The information structure is called pa_client_info. * diff --git a/src/pulse/pulseaudio.h b/src/pulse/pulseaudio.h index 5bbd4cc5..5c526dc7 100644 --- a/src/pulse/pulseaudio.h +++ b/src/pulse/pulseaudio.h @@ -63,7 +63,7 @@ * \li The complete but somewhat complicated to use asynchronous API * \li The simplified, easy to use, but limited synchronous API * - * All strings in Polypaudio are in the UTF-8 encoding, regardless of current + * All strings in PulseAudio are in the UTF-8 encoding, regardless of current * locale. Some functions will filter invalid sequences from the string, some * will simply fail. To ensure reliable behaviour, make sure everything you * pass to the API is already in UTF-8. diff --git a/src/pulse/sample.h b/src/pulse/sample.h index 848fd16d..03965a99 100644 --- a/src/pulse/sample.h +++ b/src/pulse/sample.h @@ -32,12 +32,12 @@ * * \section overv_sec Overview * - * Polypaudio is capable of handling a multitude of sample formats, rates + * PulseAudio is capable of handling a multitude of sample formats, rates * and channels, transparently converting and mixing them as needed. * * \section format_sec Sample Format * - * Polypaudio supports the following sample formats: + * PulseAudio supports the following sample formats: * * \li PA_SAMPLE_U8 - Unsigned 8 bit PCM. * \li PA_SAMPLE_S16LE - Signed 16 bit PCM, little endian. @@ -54,20 +54,20 @@ * * \section rate_sec Sample Rates * - * Polypaudio supports any sample rate between 1 Hz and 4 GHz. There is no + * PulseAudio supports any sample rate between 1 Hz and 4 GHz. There is no * point trying to exceed the sample rate of the output device though as the * signal will only get downsampled, consuming CPU on the machine running the * server. * * \section chan_sec Channels * - * Polypaudio supports up to 16 individiual channels. The order of the + * PulseAudio supports up to 16 individiual channels. The order of the * channels is up to the application, but they must be continous. To map * channels to speakers, see \ref channelmap. * * \section calc_sec Calculations * - * The Polypaudio library contains a number of convenience functions to do + * The PulseAudio library contains a number of convenience functions to do * calculations on sample formats: * * \li pa_bytes_per_second() - The number of bytes one second of audio will diff --git a/src/pulse/stream.h b/src/pulse/stream.h index d117ce4a..ad15125a 100644 --- a/src/pulse/stream.h +++ b/src/pulse/stream.h @@ -107,7 +107,7 @@ * wrap. The current read/write index may be queried using * pa_stream_get_timing_info() (see below for more information). In * case of a buffer underrun the read index is equal or larger than - * the write index. Unless the prebuf value is 0, Polypaudio will + * the write index. Unless the prebuf value is 0, PulseAudio will * temporarily pause playback in such a case, and wait until the * buffer is filled up to prebuf bytes again. If prebuf is 0, the * read index may be larger than the write index, in which case @@ -169,7 +169,7 @@ * \section latency_sec Latency * * A major problem with networked audio is the increased latency caused by - * the network. To remedy this, Polypaudio supports an advanced system of + * the network. To remedy this, PulseAudio supports an advanced system of * monitoring the current latency. * * To get the raw data needed to calculate latencies, call @@ -183,14 +183,14 @@ * pa_stream_update_timing_info() operation is executed. (i.e. before * the first call to this function the timing information structure is * not available!) Since it is a lot of work to keep this structure - * up-to-date manually, Polypaudio can do that automatically for you: + * up-to-date manually, PulseAudio can do that automatically for you: * if PA_STREAM_AUTO_TIMING_UPDATE is passed when connecting the - * stream Polypaudio will automatically update the structure every + * stream PulseAudio will automatically update the structure every * 100ms and every time a function is called that might invalidate the * previously known timing data (such as pa_stream_write() or * pa_stream_flush()). Please note however, that there always is a * short time window when the data in the timing information structure - * is out-of-date. Polypaudio tries to mark these situations by + * is out-of-date. PulseAudio tries to mark these situations by * setting the write_index_corrupt and read_index_corrupt fields * accordingly. * @@ -208,7 +208,7 @@ * * Since updating the timing info structure usually requires a full * network round trip and some applications monitor the timing very - * often Polypaudio offers a timing interpolation system. If + * often PulseAudio offers a timing interpolation system. If * PA_STREAM_INTERPOLATE_TIMING is passed when connecting the stream, * pa_stream_get_time() and pa_stream_get_latency() will try to * interpolate the current playback time/latency by estimating the @@ -228,7 +228,7 @@ * * \section sync_streams Sychronizing Multiple Playback Streams * - * Polypaudio allows applications to fully synchronize multiple + * PulseAudio allows applications to fully synchronize multiple * playback streams that are connected to the same output device. That * means the streams will always be played back sample-by-sample * synchronously. If stream operations like pa_stream_cork() are @@ -441,7 +441,7 @@ const pa_channel_map* pa_stream_get_channel_map(pa_stream *s); /** Return the buffer metrics of the stream. Only valid after the * stream has been connected successfuly and if the server is at least - * Polypaudio 0.9. \since 0.9.0 */ + * PulseAudio 0.9. \since 0.9.0 */ const pa_buffer_attr* pa_stream_get_buffer_attr(pa_stream *s); PA_C_DECL_END diff --git a/src/pulse/thread-mainloop.h b/src/pulse/thread-mainloop.h index fb216c5a..44eff5a3 100644 --- a/src/pulse/thread-mainloop.h +++ b/src/pulse/thread-mainloop.h @@ -37,7 +37,7 @@ PA_C_DECL_BEGIN * * The added feature in the threaded main loop is that it spawns a new thread * that runs the real main loop. This allows a synchronous application to use - * the asynchronous API without risking to stall the Polypaudio library. + * the asynchronous API without risking to stall the PulseAudio library. * * \section creat_sec Creation * @@ -48,7 +48,7 @@ PA_C_DECL_BEGIN * * \section destr_sec Destruction * - * When the Polypaudio connection has been terminated, the thread must be + * When the PulseAudio connection has been terminated, the thread must be * stopped and the resources freed. Stopping the thread is done using * pa_threaded_mainloop_stop(), which must be called without the lock (see * below) held. When that function returns, the thread is stopped and the @@ -56,7 +56,7 @@ PA_C_DECL_BEGIN * * \section lock_sec Locking * - * Since the Polypaudio API doesn't allow concurrent accesses to objects, + * Since the PulseAudio API doesn't allow concurrent accesses to objects, * a locking scheme must be used to guarantee safe usage. The threaded main * loop API provides such a scheme through the functions * pa_threaded_mainloop_lock() and pa_threaded_mainloop_unlock(). @@ -65,7 +65,7 @@ PA_C_DECL_BEGIN * thread. Just make sure you call pa_threaded_mainloop_unlock() the same * number of times you called pa_threaded_mainloop_lock(). * - * The lock needs to be held whenever you call any Polypaudio function that + * The lock needs to be held whenever you call any PulseAudio function that * uses an object associated with this main loop. Make sure you do not hold * on to the lock more than necessary though, as the threaded main loop stops * while the lock is held. @@ -91,12 +91,12 @@ PA_C_DECL_BEGIN * * \section cb_sec Callbacks * - * Callbacks in Polypaudio are asynchronous, so they require extra care when + * Callbacks in PulseAudio are asynchronous, so they require extra care when * using them together with a threaded main loop. * * The easiest way to turn the callback based operations into synchronous * ones, is to simply wait for the callback to be called and continue from - * there. This is the approach chosen in Polypaudio's threaded API. + * there. This is the approach chosen in PulseAudio's threaded API. * * \subsection basic_subsec Basic callbacks * @@ -216,7 +216,7 @@ PA_C_DECL_BEGIN * * \subsection async_subsec Asynchronous callbacks * - * Polypaudio also has callbacks that are completely asynchronous, meaning + * PulseAudio also has callbacks that are completely asynchronous, meaning * that they can be called at any time. The threading main loop API provides * the locking mechanism to handle concurrent accesses, but nothing else. * Applications will have to handle communication from the callback to the diff --git a/src/pulse/version.h.in b/src/pulse/version.h.in index 6c5492e8..748541a1 100644 --- a/src/pulse/version.h.in +++ b/src/pulse/version.h.in @@ -39,12 +39,12 @@ it. */ /** Return the version of the library the current application is linked to. */ const char* pa_get_library_version(void); -/** The current API version. Version 6 relates to pulseaudio +/** The current API version. Version 6 relates to Polypaudio * 0.6. Prior versions (i.e. Polypaudio 0.5.1 and older) have * PA_API_VERSION undefined. */ #define PA_API_VERSION @PA_API_VERSION@ -/** The current protocol version. Version 8 relates to pulseaudio 0.8. +/** The current protocol version. Version 8 relates to Polypaudio 0.8/PulseAudio 0.9. * \since 0.8 */ #define PA_PROTOCOL_VERSION @PA_PROTOCOL_VERSION@ diff --git a/src/pulse/volume.h b/src/pulse/volume.h index d403a09e..6c60223a 100644 --- a/src/pulse/volume.h +++ b/src/pulse/volume.h @@ -31,10 +31,10 @@ * \section overv_sec Overview * * Sinks, sources, sink inputs and samples can all have their own volumes. - * To deal with these, The Polypaudio libray contains a number of functions + * To deal with these, The PulseAudio libray contains a number of functions * that ease handling. * - * The basic volume type in Polypaudio is the \ref pa_volume_t type. Most of + * The basic volume type in PulseAudio is the \ref pa_volume_t type. Most of * the time, applications will use the aggregated pa_cvolume structure that * can store the volume of all channels at once. * @@ -43,7 +43,7 @@ * * \section calc_sec Calculations * - * The volumes in Polypaudio are logarithmic in nature and applications + * The volumes in PulseAudio are logarithmic in nature and applications * shouldn't perform calculations with them directly. Instead, they should * be converted to and from either dB or a linear scale: * @@ -64,7 +64,7 @@ * * \section conv_sec Convenience Functions * - * To handle the pa_cvolume structure, the Polypaudio library provides a + * To handle the pa_cvolume structure, the PulseAudio library provides a * number of convenienc functions: * * \li pa_cvolume_valid() - Tests if a pa_cvolume structure is valid. -- cgit From 0d97ac6d2b8616073a3b047bfe22416a452950a5 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 19 Jun 2006 23:00:21 +0000 Subject: name the pkg-config files after the library names git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@1037 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/pulseaudio.h | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) (limited to 'src/pulse') diff --git a/src/pulse/pulseaudio.h b/src/pulse/pulseaudio.h index 5c526dc7..71262eaa 100644 --- a/src/pulse/pulseaudio.h +++ b/src/pulse/pulseaudio.h @@ -44,7 +44,7 @@ #include /** \file - * Include all pulselib header files at once. The following + * Include all libpulse header files at once. The following * files are included: \ref mainloop-api.h, \ref sample.h, \ref def.h, * \ref context.h, \ref stream.h, \ref introspect.h, \ref subscribe.h, * \ref scache.h, \ref version.h, \ref error.h, \ref channelmap.h, @@ -56,7 +56,7 @@ * * \section intro_sec Introduction * - * This document describes the client API for the pulseaudio sound + * This document describes the client API for the PulseAudio sound * server. The API comes in two flavours to accomodate different styles * of applications and different needs in complexity: * @@ -78,7 +78,7 @@ * * Use this if you develop your programs in asynchronous, event loop * based style or if you want to use the advanced features of the - * pulseaudio API. A guide can be found in \subpage async. + * PulseAudio API. A guide can be found in \subpage async. * * By using the built-in threaded main loop, it is possible to acheive a * pseudo-synchronous API, which can be useful in synchronous applications @@ -87,7 +87,7 @@ * * \section thread_sec Threads * - * The pulseaudio client libraries are not designed to be used in a + * The PulseAudio client libraries are not designed to be used in a * heavily threaded environment. They are however designed to be reentrant * safe. * @@ -102,14 +102,13 @@ * * \section pkgconfig pkg-config * - * The pulseaudio libraries provide pkg-config snippets for the different + * The PulseAudio libraries provide pkg-config snippets for the different * modules: * - * \li pulselib - The asynchronous API and the internal main loop - * implementation. - * \li pulselib-glib12-mainloop - GLIB 1.2 main loop bindings. - * \li pulselib-glib-mainloop - GLIB 2.x main loop bindings. - * \li pulselib-simple - The simple pulseaudio API. + * \li pulselib - The asynchronous API and the internal main loop implementation. + * \li pulselib-mainloop-glib12 - GLIB 1.2 main loop bindings. + * \li pulselib-mainloop-glib - GLIB 2.x main loop bindings. + * \li pulselib-simple - The simple PulseAudio API. */ #endif -- cgit From fe1dadbadd9f694d533dc2191451d2a020d99c2f Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 19 Jun 2006 23:06:50 +0000 Subject: update references to the pkg-config files in the docs git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@1038 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/pulseaudio.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/pulse') diff --git a/src/pulse/pulseaudio.h b/src/pulse/pulseaudio.h index 71262eaa..88cc326b 100644 --- a/src/pulse/pulseaudio.h +++ b/src/pulse/pulseaudio.h @@ -105,10 +105,10 @@ * The PulseAudio libraries provide pkg-config snippets for the different * modules: * - * \li pulselib - The asynchronous API and the internal main loop implementation. - * \li pulselib-mainloop-glib12 - GLIB 1.2 main loop bindings. - * \li pulselib-mainloop-glib - GLIB 2.x main loop bindings. - * \li pulselib-simple - The simple PulseAudio API. + * \li libpulse - The asynchronous API and the internal main loop implementation. + * \li libpulse-mainloop-glib12 - GLIB 1.2 main loop bindings. + * \li libpulse-mainloop-glib - GLIB 2.x main loop bindings. + * \li libpulse-simple - The simple PulseAudio API. */ #endif -- cgit From 3cf16214334b4a1c51e56b0536abd8223d6813dd Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 19 Jun 2006 23:51:58 +0000 Subject: * more s/pulseaudio/PulseAudio/ replacements * name the per-user dir ~/.pulse (instead of .pulseaudio), just like /etc/pulse/ git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@1039 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/browser.c | 6 +++--- src/pulse/client-conf.c | 4 ++-- src/pulse/client-conf.h | 2 +- src/pulse/context.h | 12 ++++++------ src/pulse/def.h | 2 +- src/pulse/introspect.h | 2 +- src/pulse/mainloop-api.h | 8 ++++---- 7 files changed, 18 insertions(+), 18 deletions(-) (limited to 'src/pulse') diff --git a/src/pulse/browser.c b/src/pulse/browser.c index d063465d..96625869 100644 --- a/src/pulse/browser.c +++ b/src/pulse/browser.c @@ -29,9 +29,9 @@ #include "browser.h" -#define SERVICE_NAME_SINK "_pulseaudio-sink._tcp." -#define SERVICE_NAME_SOURCE "_pulseaudio-source._tcp." -#define SERVICE_NAME_SERVER "_pulseaudio-server._tcp." +#define SERVICE_NAME_SINK "_pulse-sink._tcp." +#define SERVICE_NAME_SOURCE "_pulse-source._tcp." +#define SERVICE_NAME_SERVER "_pulse-server._tcp." struct pa_browser { int ref; diff --git a/src/pulse/client-conf.c b/src/pulse/client-conf.c index 752d0134..9f5cf53d 100644 --- a/src/pulse/client-conf.c +++ b/src/pulse/client-conf.c @@ -41,7 +41,7 @@ #ifndef DEFAULT_CONFIG_DIR # ifndef OS_IS_WIN32 -# define DEFAULT_CONFIG_DIR "/etc/pulseaudio" +# define DEFAULT_CONFIG_DIR "/etc/pulse" # else # define DEFAULT_CONFIG_DIR "%POLYP_ROOT%" # endif @@ -54,7 +54,7 @@ #endif #define DEFAULT_CLIENT_CONFIG_FILE DEFAULT_CONFIG_DIR PATH_SEP "client.conf" -#define DEFAULT_CLIENT_CONFIG_FILE_USER ".pulseaudio" PATH_SEP "client.conf" +#define DEFAULT_CLIENT_CONFIG_FILE_USER ".pulse" PATH_SEP "client.conf" #define ENV_CLIENT_CONFIG_FILE "POLYP_CLIENTCONFIG" #define ENV_DEFAULT_SINK "POLYP_SINK" diff --git a/src/pulse/client-conf.h b/src/pulse/client-conf.h index 9d45af47..a532f0df 100644 --- a/src/pulse/client-conf.h +++ b/src/pulse/client-conf.h @@ -24,7 +24,7 @@ #include -/* A structure containing configuration data for pulseaudio clients. */ +/* A structure containing configuration data for PulseAudio clients. */ typedef struct pa_client_conf { char *daemon_binary, *extra_arguments, *default_sink, *default_source, *default_server, *cookie_file; diff --git a/src/pulse/context.h b/src/pulse/context.h index ad8c9f3f..661ff617 100644 --- a/src/pulse/context.h +++ b/src/pulse/context.h @@ -32,7 +32,7 @@ * * \section overv_sec Overview * - * The asynchronous API is the native interface to the pulseaudio library. + * The asynchronous API is the native interface to the PulseAudio library. * It allows full access to all available functions. This also means that * it is rather complex and can take some time to fully master. * @@ -52,10 +52,10 @@ * * To actually be able to use these functions, an implementation needs to * be coupled to the abstraction. There are three of these shipped with - * pulseaudio, but any other can be used with a minimal ammount of work, + * PulseAudio, but any other can be used with a minimal ammount of work, * provided it supports the three basic events listed above. * - * The implementations shipped with pulseaudio are: + * The implementations shipped with PulseAudio are: * * \li \subpage mainloop - A minimal but fast implementation based on poll(). * \li \subpage threaded_mainloop - A special version of the previous @@ -71,7 +71,7 @@ * * \section refcnt_sec Reference Counting * - * Almost all objects in pulseaudio are reference counted. What that means + * Almost all objects in PulseAudio are reference counted. What that means * is that you rarely malloc() or free() any objects. Instead you increase * and decrease their reference counts. Whenever an object's reference * count reaches zero, that object gets destroy and any resources it uses @@ -89,7 +89,7 @@ * * \section context_sec Context * - * A context is the basic object for a connection to a pulseaudio server. + * A context is the basic object for a connection to a PulseAudio server. * It multiplexes commands, data streams and events through a single * channel. * @@ -142,7 +142,7 @@ /** \file * Connection contexts for asynchrononous communication with a - * server. A pa_context object wraps a connection to a pulseaudio + * server. A pa_context object wraps a connection to a PulseAudio * server using its native protocol. */ /** \example pacat.c diff --git a/src/pulse/def.h b/src/pulse/def.h index 3a17f43b..b98337d2 100644 --- a/src/pulse/def.h +++ b/src/pulse/def.h @@ -66,7 +66,7 @@ typedef enum pa_operation_state { /** Some special flags for contexts. \since 0.8 */ typedef enum pa_context_flags { - PA_CONTEXT_NOAUTOSPAWN = 1 /**< Disabled autospawning of the pulseaudio daemon if required */ + PA_CONTEXT_NOAUTOSPAWN = 1 /**< Disabled autospawning of the PulseAudio daemon if required */ } pa_context_flags_t; /** The direction of a pa_stream object */ diff --git a/src/pulse/introspect.h b/src/pulse/introspect.h index 23d736c6..e9a14490 100644 --- a/src/pulse/introspect.h +++ b/src/pulse/introspect.h @@ -270,7 +270,7 @@ typedef struct pa_server_info { pa_sample_spec sample_spec; /**< Default sample specification */ const char *default_sink_name; /**< Name of default sink. \since 0.4 */ const char *default_source_name; /**< Name of default sink. \since 0.4*/ - uint32_t cookie; /**< A random cookie for identifying this instance of pulseaudio. \since 0.8 */ + uint32_t cookie; /**< A random cookie for identifying this instance of PulseAudio. \since 0.8 */ } pa_server_info; /** Callback prototype for pa_context_get_server_info() */ diff --git a/src/pulse/mainloop-api.h b/src/pulse/mainloop-api.h index a732b215..4aaeccf5 100644 --- a/src/pulse/mainloop-api.h +++ b/src/pulse/mainloop-api.h @@ -29,13 +29,13 @@ /** \file * - * Main loop abstraction layer. Both the pulseaudio core and the - * pulseaudio client library use a main loop abstraction layer. Due to - * this it is possible to embed pulseaudio into other + * Main loop abstraction layer. Both the PulseAudio core and the + * PulseAudio client library use a main loop abstraction layer. Due to + * this it is possible to embed PulseAudio into other * applications easily. Two main loop implemenations are * currently available: * \li A minimal implementation based on the C library's poll() function (See \ref mainloop.h) - * \li A wrapper around the GLIB main loop. Use this to embed pulseaudio into your GLIB/GTK+/GNOME programs (See \ref glib-mainloop.h) + * \li A wrapper around the GLIB main loop. Use this to embed PulseAudio into your GLIB/GTK+/GNOME programs (See \ref glib-mainloop.h) * * The structure pa_mainloop_api is used as vtable for the main loop abstraction. * -- cgit From 230f97a4a4dc22510a19add8b2df0533a359846c Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 19 Jun 2006 23:56:54 +0000 Subject: s/POLYP/PULSE/g git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@1041 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/client-conf-x11.c | 8 ++++---- src/pulse/client-conf.c | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) (limited to 'src/pulse') diff --git a/src/pulse/client-conf-x11.c b/src/pulse/client-conf-x11.c index 7eea5ae3..e4a90017 100644 --- a/src/pulse/client-conf-x11.c +++ b/src/pulse/client-conf-x11.c @@ -50,22 +50,22 @@ int pa_client_conf_from_x11(pa_client_conf *c, const char *dname) { goto finish; } - if (pa_x11_get_prop(d, "POLYP_SERVER", t, sizeof(t))) { + if (pa_x11_get_prop(d, "PULSE_SERVER", t, sizeof(t))) { pa_xfree(c->default_server); c->default_server = pa_xstrdup(t); } - if (pa_x11_get_prop(d, "POLYP_SINK", t, sizeof(t))) { + if (pa_x11_get_prop(d, "PULSE_SINK", t, sizeof(t))) { pa_xfree(c->default_sink); c->default_sink = pa_xstrdup(t); } - if (pa_x11_get_prop(d, "POLYP_SOURCE", t, sizeof(t))) { + if (pa_x11_get_prop(d, "PULSE_SOURCE", t, sizeof(t))) { pa_xfree(c->default_source); c->default_source = pa_xstrdup(t); } - if (pa_x11_get_prop(d, "POLYP_COOKIE", t, sizeof(t))) { + if (pa_x11_get_prop(d, "PULSE_COOKIE", t, sizeof(t))) { uint8_t cookie[PA_NATIVE_COOKIE_LENGTH]; if (pa_parsehex(t, cookie, sizeof(cookie)) != sizeof(cookie)) { diff --git a/src/pulse/client-conf.c b/src/pulse/client-conf.c index 9f5cf53d..e150a9cc 100644 --- a/src/pulse/client-conf.c +++ b/src/pulse/client-conf.c @@ -43,7 +43,7 @@ # ifndef OS_IS_WIN32 # define DEFAULT_CONFIG_DIR "/etc/pulse" # else -# define DEFAULT_CONFIG_DIR "%POLYP_ROOT%" +# define DEFAULT_CONFIG_DIR "%PULSE_ROOT%" # endif #endif @@ -56,12 +56,12 @@ #define DEFAULT_CLIENT_CONFIG_FILE DEFAULT_CONFIG_DIR PATH_SEP "client.conf" #define DEFAULT_CLIENT_CONFIG_FILE_USER ".pulse" PATH_SEP "client.conf" -#define ENV_CLIENT_CONFIG_FILE "POLYP_CLIENTCONFIG" -#define ENV_DEFAULT_SINK "POLYP_SINK" -#define ENV_DEFAULT_SOURCE "POLYP_SOURCE" -#define ENV_DEFAULT_SERVER "POLYP_SERVER" -#define ENV_DAEMON_BINARY "POLYP_BINARY" -#define ENV_COOKIE_FILE "POLYP_COOKIE" +#define ENV_CLIENT_CONFIG_FILE "PULSE_CLIENTCONFIG" +#define ENV_DEFAULT_SINK "PULSE_SINK" +#define ENV_DEFAULT_SOURCE "PULSE_SOURCE" +#define ENV_DEFAULT_SERVER "PULSE_SERVER" +#define ENV_DAEMON_BINARY "PULSE_BINARY" +#define ENV_COOKIE_FILE "PULSE_COOKIE" static const pa_client_conf default_conf = { .daemon_binary = NULL, @@ -77,7 +77,7 @@ static const pa_client_conf default_conf = { pa_client_conf *pa_client_conf_new(void) { pa_client_conf *c = pa_xmemdup(&default_conf, sizeof(default_conf)); - c->daemon_binary = pa_xstrdup(POLYPAUDIO_BINARY); + c->daemon_binary = pa_xstrdup(PULSEAUDIO_BINARY); c->extra_arguments = pa_xstrdup("--log-target=syslog --exit-idle-time=5"); c->cookie_file = pa_xstrdup(PA_NATIVE_COOKIE_FILE); -- cgit From 3ff68bd7a57186c6c6def975be3fc7e826c8bbcd Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Tue, 20 Jun 2006 13:02:34 +0000 Subject: Fix the final few occurences of polyp. git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@1042 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/client.conf.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/pulse') diff --git a/src/pulse/client.conf.in b/src/pulse/client.conf.in index d881c44e..21786ebf 100644 --- a/src/pulse/client.conf.in +++ b/src/pulse/client.conf.in @@ -21,7 +21,7 @@ ## commented out. Use either ; or # for commenting ## Path to the pulseaudio daemon to run when autospawning. -; daemon-binary = @POLYPAUDIO_BINARY@ +; daemon-binary = @PULSEAUDIO_BINARY@ ## Extra arguments to pass to the pulseaudio daemon ; extra-arguments = --log-target=syslog --exit-idle-time=5 -- cgit From 1710041eaffc917d45eaeb6143db24a9773842b3 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 21 Jun 2006 00:18:43 +0000 Subject: only interpolate when the last timing info told us the stream is indeed playing git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@1051 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/stream.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/pulse') diff --git a/src/pulse/stream.c b/src/pulse/stream.c index c8fc09d2..677df009 100644 --- a/src/pulse/stream.c +++ b/src/pulse/stream.c @@ -1254,7 +1254,7 @@ int pa_stream_get_time(pa_stream *s, pa_usec_t *r_usec) { /* We just add the time that passed since the latency info was * current */ - if (!s->corked) { + if (!s->corked && s->timing_info.playing) { struct timeval now; usec += pa_timeval_diff(pa_gettimeofday(&now), &s->timing_info.timestamp); } -- cgit From 045b05cd91aa313d9a3cb496f8a0e664d8bceea3 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 21 Jun 2006 14:05:15 +0000 Subject: include config.h in browser.c (closes #20) git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@1052 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/browser.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/pulse') diff --git a/src/pulse/browser.c b/src/pulse/browser.c index 96625869..60c71090 100644 --- a/src/pulse/browser.c +++ b/src/pulse/browser.c @@ -19,6 +19,10 @@ USA. ***/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #include #include -- cgit From 8b0d1346022d0eb3f3b4bf3eb956f9757ff57156 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Fri, 30 Jun 2006 08:16:14 +0000 Subject: Make sure we print the file name we actually use. git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@1054 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/client-conf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/pulse') diff --git a/src/pulse/client-conf.c b/src/pulse/client-conf.c index e150a9cc..5cebcf46 100644 --- a/src/pulse/client-conf.c +++ b/src/pulse/client-conf.c @@ -124,7 +124,7 @@ int pa_client_conf_load(pa_client_conf *c, const char *filename) { pa_open_config_file(DEFAULT_CLIENT_CONFIG_FILE, DEFAULT_CLIENT_CONFIG_FILE_USER, ENV_CLIENT_CONFIG_FILE, &fn, "r"); if (!f && errno != EINTR) { - pa_log(__FILE__": WARNING: failed to open configuration file '%s': %s", filename, pa_cstrerror(errno)); + pa_log(__FILE__": WARNING: failed to open configuration file '%s': %s", fn, pa_cstrerror(errno)); goto finish; } -- cgit From 10f7a6457545320595574ad249a3e9e1dd56c481 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 13 Jul 2006 15:52:34 +0000 Subject: make sure gccmacro.h and cdecl.h may be included at the same time as those headers from the avahi project git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@1067 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/cdecl.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/pulse') diff --git a/src/pulse/cdecl.h b/src/pulse/cdecl.h index 6ac96687..a3ec231c 100644 --- a/src/pulse/cdecl.h +++ b/src/pulse/cdecl.h @@ -1,5 +1,5 @@ -#ifndef foocdeclhfoo -#define foocdeclhfoo +#ifndef foopulsecdeclhfoo +#define foopulsecdeclhfoo /* $Id$ */ -- cgit From 76f93a07f9d683c3484ff3a71857fe30bedcfd46 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 13 Jul 2006 17:33:44 +0000 Subject: * port libpulse-browse to use the native avahi API instead of the HOWL cruft * add new function pa_browser_set_error_callback() * add doxygen docs to browser.h git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@1069 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/browser.c | 443 ++++++++++++++++++++++++++++++++-------------------- src/pulse/browser.h | 66 +++++--- 2 files changed, 319 insertions(+), 190 deletions(-) (limited to 'src/pulse') diff --git a/src/pulse/browser.c b/src/pulse/browser.c index 60c71090..dae8e3d5 100644 --- a/src/pulse/browser.c +++ b/src/pulse/browser.c @@ -24,85 +24,68 @@ #endif #include -#include +#include + +#include +#include +#include #include #include #include +#include + #include "browser.h" -#define SERVICE_NAME_SINK "_pulse-sink._tcp." -#define SERVICE_NAME_SOURCE "_pulse-source._tcp." -#define SERVICE_NAME_SERVER "_pulse-server._tcp." +#define SERVICE_TYPE_SINK "_pulse-sink._tcp." +#define SERVICE_TYPE_SOURCE "_pulse-source._tcp." +#define SERVICE_TYPE_SERVER "_pulse-server._tcp." struct pa_browser { int ref; pa_mainloop_api *mainloop; + AvahiPoll* avahi_poll; pa_browse_cb_t callback; void *userdata; - - sw_discovery discovery; - pa_io_event *io_event; -}; - -static void io_callback(pa_mainloop_api*a, PA_GCC_UNUSED pa_io_event*e, PA_GCC_UNUSED int fd, pa_io_event_flags_t events, void *userdata) { - pa_browser *b = userdata; - assert(a && b && b->mainloop == a); - if (events != PA_IO_EVENT_INPUT || sw_discovery_read_socket(b->discovery) != SW_OKAY) { - pa_log(__FILE__": connection to HOWL daemon failed."); - b->mainloop->io_free(b->io_event); - b->io_event = NULL; - return; - } -} - -static int type_equal(const char *a, const char *b) { - size_t la, lb; + pa_browser_error_cb_t error_callback; + void *error_userdata; - if (strcasecmp(a, b) == 0) - return 1; - - la = strlen(a); - lb = strlen(b); - - if (la > 0 && a[la-1] == '.' && la == lb+1 && strncasecmp(a, b, la-1) == 0) - return 1; - - if (lb > 0 && b[lb-1] == '.' && lb == la+1 && strncasecmp(a, b, lb-1) == 0) - return 1; - - return 0; -} + AvahiClient *client; + AvahiServiceBrowser *server_browser, *sink_browser, *source_browser; + +}; static int map_to_opcode(const char *type, int new) { - if (type_equal(type, SERVICE_NAME_SINK)) + if (avahi_domain_equal(type, SERVICE_TYPE_SINK)) return new ? PA_BROWSE_NEW_SINK : PA_BROWSE_REMOVE_SINK; - else if (type_equal(type, SERVICE_NAME_SOURCE)) + else if (avahi_domain_equal(type, SERVICE_TYPE_SOURCE)) return new ? PA_BROWSE_NEW_SOURCE : PA_BROWSE_REMOVE_SOURCE; - else if (type_equal(type, SERVICE_NAME_SERVER)) + else if (avahi_domain_equal(type, SERVICE_TYPE_SERVER)) return new ? PA_BROWSE_NEW_SERVER : PA_BROWSE_REMOVE_SERVER; return -1; } -static sw_result resolve_reply( - sw_discovery discovery, - sw_discovery_oid oid, - sw_uint32 interface_index, - sw_const_string name, - sw_const_string type, - sw_const_string domain, - sw_ipv4_address address, - sw_port port, - sw_octets text_record, - sw_ulong text_record_len, - sw_opaque extra) { +static void resolve_callback( + AvahiServiceResolver *r, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiResolverEvent event, + const char *name, + const char *type, + const char *domain, + const char *host_name, + const AvahiAddress *aa, + uint16_t port, + AvahiStringList *txt, + AvahiLookupResultFlags flags, + void *userdata) { - pa_browser *b = extra; + pa_browser *b = userdata; pa_browse_info i; char ip[256], a[256]; int opcode; @@ -110,100 +93,96 @@ static sw_result resolve_reply( uint32_t cookie; pa_sample_spec ss; int ss_valid = 0; - sw_text_record_iterator iterator; - int free_iterator = 0; - char *c = NULL; + char *key = NULL, *value = NULL; assert(b); - sw_discovery_cancel(discovery, oid); - memset(&i, 0, sizeof(i)); i.name = name; - + + if (event != AVAHI_RESOLVER_FOUND) + goto fail; + if (!b->callback) goto fail; opcode = map_to_opcode(type, 1); assert(opcode >= 0); - - snprintf(a, sizeof(a), "tcp:%s:%u", sw_ipv4_address_name(address, ip, sizeof(ip)), port); + + if (aa->proto == AVAHI_PROTO_INET) + snprintf(a, sizeof(a), "tcp:%s:%u", avahi_address_snprint(ip, sizeof(ip), aa), port); + else { + assert(aa->proto == AVAHI_PROTO_INET6); + snprintf(a, sizeof(a), "tcp6:%s:%u", avahi_address_snprint(ip, sizeof(ip), aa), port); + } i.server = a; - - if (text_record && text_record_len) { - char key[SW_TEXT_RECORD_MAX_LEN]; - uint8_t val[SW_TEXT_RECORD_MAX_LEN]; - uint32_t val_len; - - if (sw_text_record_iterator_init(&iterator, text_record, text_record_len) != SW_OKAY) { - pa_log_error(__FILE__": sw_text_record_string_iterator_init() failed."); - goto fail; - } - free_iterator = 1; + + while (txt) { - while (sw_text_record_iterator_next(iterator, key, val, &val_len) == SW_OKAY) { - c = pa_xstrndup((char*) val, val_len); + if (avahi_string_list_get_pair(txt, &key, &value, NULL) < 0) + break; + + if (!strcmp(key, "device")) { + device_found = 1; + pa_xfree((char*) i.device); + i.device = value; + value = NULL; + } else if (!strcmp(key, "server-version")) { + pa_xfree((char*) i.server_version); + i.server_version = value; + value = NULL; + } else if (!strcmp(key, "user-name")) { + pa_xfree((char*) i.user_name); + i.user_name = value; + value = NULL; + } else if (!strcmp(key, "fqdn")) { + size_t l; - if (!strcmp(key, "device")) { - device_found = 1; - pa_xfree((char*) i.device); - i.device = c; - c = NULL; - } else if (!strcmp(key, "server-version")) { - pa_xfree((char*) i.server_version); - i.server_version = c; - c = NULL; - } else if (!strcmp(key, "user-name")) { - pa_xfree((char*) i.user_name); - i.user_name = c; - c = NULL; - } else if (!strcmp(key, "fqdn")) { - size_t l; + pa_xfree((char*) i.fqdn); + i.fqdn = value; + value = NULL; - pa_xfree((char*) i.fqdn); - i.fqdn = c; - c = NULL; - - l = strlen(a); - assert(l+1 <= sizeof(a)); - strncat(a, " ", sizeof(a)-l-1); - strncat(a, i.fqdn, sizeof(a)-l-2); - } else if (!strcmp(key, "cookie")) { - - if (pa_atou(c, &cookie) < 0) - goto fail; - - i.cookie = &cookie; - } else if (!strcmp(key, "description")) { - pa_xfree((char*) i.description); - i.description = c; - c = NULL; - } else if (!strcmp(key, "channels")) { - uint32_t ch; - - if (pa_atou(c, &ch) < 0 || ch <= 0 || ch > 255) - goto fail; - - ss.channels = (uint8_t) ch; - ss_valid |= 1; - - } else if (!strcmp(key, "rate")) { - if (pa_atou(c, &ss.rate) < 0) - goto fail; - ss_valid |= 2; - } else if (!strcmp(key, "format")) { + l = strlen(a); + assert(l+1 <= sizeof(a)); + strncat(a, " ", sizeof(a)-l-1); + strncat(a, i.fqdn, sizeof(a)-l-2); + } else if (!strcmp(key, "cookie")) { + + if (pa_atou(value, &cookie) < 0) + goto fail; + + i.cookie = &cookie; + } else if (!strcmp(key, "description")) { + pa_xfree((char*) i.description); + i.description = value; + value = NULL; + } else if (!strcmp(key, "channels")) { + uint32_t ch; + + if (pa_atou(value, &ch) < 0 || ch <= 0 || ch > 255) + goto fail; + + ss.channels = (uint8_t) ch; + ss_valid |= 1; + + } else if (!strcmp(key, "rate")) { + if (pa_atou(value, &ss.rate) < 0) + goto fail; + ss_valid |= 2; + } else if (!strcmp(key, "format")) { + + if ((ss.format = pa_parse_sample_format(value)) == PA_SAMPLE_INVALID) + goto fail; + + ss_valid |= 4; + } - if ((ss.format = pa_parse_sample_format(c)) == PA_SAMPLE_INVALID) - goto fail; - - ss_valid |= 4; - } + pa_xfree(key); + pa_xfree(value); + key = value = NULL; - pa_xfree(c); - c = NULL; - } - + txt = avahi_string_list_get_next(txt); } /* No device txt record was sent for a sink or source service */ @@ -212,7 +191,6 @@ static sw_result resolve_reply( if (ss_valid == 7) i.sample_spec = &ss; - b->callback(b, opcode, &i, b->userdata); @@ -222,39 +200,72 @@ fail: pa_xfree((void*) i.server_version); pa_xfree((void*) i.user_name); pa_xfree((void*) i.description); - pa_xfree(c); - - if (free_iterator) - sw_text_record_iterator_fina(iterator); + pa_xfree(key); + pa_xfree(value); - return SW_OKAY; + avahi_service_resolver_free(r); } -static sw_result browse_reply( - sw_discovery discovery, - sw_discovery_oid id, - sw_discovery_browse_status status, - sw_uint32 interface_index, - sw_const_string name, - sw_const_string type, - sw_const_string domain, - sw_opaque extra) { - - pa_browser *b = extra; +static void handle_failure(pa_browser *b) { + const char *e = NULL; assert(b); - switch (status) { - case SW_DISCOVERY_BROWSE_ADD_SERVICE: { - sw_discovery_oid oid; + if (b->sink_browser) + avahi_service_browser_free(b->sink_browser); + if (b->source_browser) + avahi_service_browser_free(b->source_browser); + if (b->server_browser) + avahi_service_browser_free(b->server_browser); - if (sw_discovery_resolve(b->discovery, 0, name, type, domain, resolve_reply, b, &oid) != SW_OKAY) - pa_log_error(__FILE__": sw_discovery_resolve() failed"); + b->sink_browser = b->source_browser = b->server_browser = NULL; + + if (b->client) { + e = avahi_strerror(avahi_client_errno(b->client)); + avahi_client_free(b->client); + } + + b->client = NULL; + + if (b->error_callback) + b->error_callback(b, e, b->error_userdata); +} + +static void browse_callback( + AvahiServiceBrowser *sb, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiBrowserEvent event, + const char *name, + const char *type, + const char *domain, + AvahiLookupResultFlags flags, + void *userdata) { + + pa_browser *b = userdata; + assert(b); + + switch (event) { + case AVAHI_BROWSER_NEW: { + + if (!avahi_service_resolver_new( + b->client, + interface, + protocol, + name, + type, + domain, + AVAHI_PROTO_UNSPEC, + 0, + resolve_callback, + b)) + handle_failure(b); break; } - case SW_DISCOVERY_BROWSE_REMOVE_SERVICE: + case AVAHI_BROWSER_REMOVE: { + if (b->callback) { pa_browse_info i; int opcode; @@ -268,63 +279,144 @@ static sw_result browse_reply( b->callback(b, opcode, &i, b->userdata); } break; + } + case AVAHI_BROWSER_FAILURE: { + handle_failure(b); + break; + } + default: ; } +} + +static void client_callback(AvahiClient *s, AvahiClientState state, void *userdata) { + pa_browser *b = userdata; + assert(s); - return SW_OKAY; + if (state == AVAHI_CLIENT_FAILURE) + handle_failure(b); } +static void browser_free(pa_browser *b); + pa_browser *pa_browser_new(pa_mainloop_api *mainloop) { + return pa_browser_new_full(mainloop, PA_BROWSE_FOR_SERVERS|PA_BROWSE_FOR_SINKS|PA_BROWSE_FOR_SOURCES, NULL); +} + +pa_browser *pa_browser_new_full(pa_mainloop_api *mainloop, pa_browse_flags_t flags, const char **error_string) { pa_browser *b; - sw_discovery_oid oid; + int error; + assert(mainloop); + + if (flags & ~(PA_BROWSE_FOR_SERVERS|PA_BROWSE_FOR_SINKS|PA_BROWSE_FOR_SOURCES) || flags == 0) + return NULL; + b = pa_xnew(pa_browser, 1); b->mainloop = mainloop; b->ref = 1; b->callback = NULL; b->userdata = NULL; + b->error_callback = NULL; + b->error_userdata = NULL; + b->sink_browser = b->source_browser = b->server_browser = NULL; - if (sw_discovery_init(&b->discovery) != SW_OKAY) { - pa_log_error(__FILE__": sw_discovery_init() failed."); - pa_xfree(b); - return NULL; + b->avahi_poll = pa_avahi_poll_new(mainloop); + + if (!(b->client = avahi_client_new(b->avahi_poll, 0, client_callback, b, &error))) { + if (error_string) + *error_string = avahi_strerror(error); + goto fail; + } + + if ((flags & PA_BROWSE_FOR_SERVERS) && + !(b->server_browser = avahi_service_browser_new( + b->client, + AVAHI_IF_UNSPEC, + AVAHI_PROTO_UNSPEC, + SERVICE_TYPE_SERVER, + NULL, + 0, + browse_callback, + b))) { + + if (error_string) + *error_string = avahi_strerror(avahi_client_errno(b->client)); + goto fail; } - if (sw_discovery_browse(b->discovery, 0, SERVICE_NAME_SERVER, NULL, browse_reply, b, &oid) != SW_OKAY || - sw_discovery_browse(b->discovery, 0, SERVICE_NAME_SINK, NULL, browse_reply, b, &oid) != SW_OKAY || - sw_discovery_browse(b->discovery, 0, SERVICE_NAME_SOURCE, NULL, browse_reply, b, &oid) != SW_OKAY) { + if ((flags & PA_BROWSE_FOR_SINKS) && + !(b->sink_browser = avahi_service_browser_new( + b->client, + AVAHI_IF_UNSPEC, + AVAHI_PROTO_UNSPEC, + SERVICE_TYPE_SINK, + NULL, + 0, + browse_callback, + b))) { + + if (error_string) + *error_string = avahi_strerror(avahi_client_errno(b->client)); + goto fail; + } - pa_log_error(__FILE__": sw_discovery_browse() failed."); - - sw_discovery_fina(b->discovery); - pa_xfree(b); - return NULL; + if ((flags & PA_BROWSE_FOR_SOURCES) && + !(b->source_browser = avahi_service_browser_new( + b->client, + AVAHI_IF_UNSPEC, + AVAHI_PROTO_UNSPEC, + SERVICE_TYPE_SOURCE, + NULL, + 0, + browse_callback, + b))) { + + if (error_string) + *error_string = avahi_strerror(avahi_client_errno(b->client)); + goto fail; } - b->io_event = mainloop->io_new(mainloop, sw_discovery_socket(b->discovery), PA_IO_EVENT_INPUT, io_callback, b); return b; + +fail: + if (b) + browser_free(b); + + return NULL; } static void browser_free(pa_browser *b) { assert(b && b->mainloop); - if (b->io_event) - b->mainloop->io_free(b->io_event); + if (b->sink_browser) + avahi_service_browser_free(b->sink_browser); + if (b->source_browser) + avahi_service_browser_free(b->source_browser); + if (b->server_browser) + avahi_service_browser_free(b->server_browser); + + if (b->client) + avahi_client_free(b->client); + + if (b->avahi_poll) + pa_avahi_poll_free(b->avahi_poll); - sw_discovery_fina(b->discovery); pa_xfree(b); } pa_browser *pa_browser_ref(pa_browser *b) { - assert(b && b->ref >= 1); + assert(b); + assert(b->ref >= 1); b->ref++; return b; } void pa_browser_unref(pa_browser *b) { - assert(b && b->ref >= 1); + assert(b); + assert(b->ref >= 1); if ((-- (b->ref)) <= 0) browser_free(b); @@ -336,3 +428,10 @@ void pa_browser_set_callback(pa_browser *b, pa_browse_cb_t cb, void *userdata) { b->callback = cb; b->userdata = userdata; } + +void pa_browser_set_error_callback(pa_browser *b, pa_browser_error_cb_t cb, void *userdata) { + assert(b); + + b->error_callback = cb; + b->error_userdata = userdata; +} diff --git a/src/pulse/browser.h b/src/pulse/browser.h index 2d20c6c0..fc57a4d5 100644 --- a/src/pulse/browser.h +++ b/src/pulse/browser.h @@ -27,42 +27,72 @@ #include #include +/** \file + * An abstract interface for Zeroconf browsing of PulseAudio servers */ + PA_C_DECL_BEGIN +/** An opaque Zeroconf service browser object */ typedef struct pa_browser pa_browser; +/** Opcodes for pa_browser_cb_t callbacks */ typedef enum pa_browse_opcode { - PA_BROWSE_NEW_SERVER = 0, - PA_BROWSE_NEW_SINK, - PA_BROWSE_NEW_SOURCE, - PA_BROWSE_REMOVE_SERVER, - PA_BROWSE_REMOVE_SINK, - PA_BROWSE_REMOVE_SOURCE + PA_BROWSE_NEW_SERVER = 0, /**< New server found */ + PA_BROWSE_NEW_SINK, /**< New sink found */ + PA_BROWSE_NEW_SOURCE, /**< New source found */ + PA_BROWSE_REMOVE_SERVER, /**< Server disappeared */ + PA_BROWSE_REMOVE_SINK, /**< Sink disappeared */ + PA_BROWSE_REMOVE_SOURCE /**< Source disappeared */ } pa_browse_opcode_t; +typedef enum pa_browse_flags { + PA_BROWSE_FOR_SERVERS = 1, /**< Browse for servers */ + PA_BROWSE_FOR_SINKS = 2, /**< Browse for sinks */ + PA_BROWSE_FOR_SOURCES = 4 /** Browse for sources */ +} pa_browse_flags_t; + +/** Create a new browser object on the specified main loop */ pa_browser *pa_browser_new(pa_mainloop_api *mainloop); + +/** Same pa_browser_new, but pass additional flags parameter. */ +pa_browser *pa_browser_new_full(pa_mainloop_api *mainloop, pa_browse_flags_t flags, const char **error_string); + +/** Increase reference counter of the specified browser object */ pa_browser *pa_browser_ref(pa_browser *z); + +/** Decrease reference counter of the specified browser object */ void pa_browser_unref(pa_browser *z); +/** Information about a sink/source/server found with Zeroconf */ typedef struct pa_browse_info { - /* Unique service name */ - const char *name; /* always available */ - - /* Server info */ - const char *server; /* always available */ - const char *server_version, *user_name, *fqdn; /* optional */ - const uint32_t *cookie; /* optional */ - - /* Device info */ - const char *device; /* always available when this information is of a sink/source */ - const char *description; /* optional */ - const pa_sample_spec *sample_spec; /* optional */ + const char *name; /**< Unique service name; always available */ + + const char *server; /**< Server name; always available */ + const char *server_version; /**< Server version string; optional */ + const char *user_name; /**< User name of the server process; optional */ + const char *fqdn; /* Server version; optional */ + const uint32_t *cookie; /* Server cookie; optional */ + + const char *device; /* Device name; always available when this information is of a sink/source */ + const char *description; /* Device description; optional */ + const pa_sample_spec *sample_spec; /* Sample specification of the device; optional */ } pa_browse_info; +/** Callback prototype */ typedef void (*pa_browse_cb_t)(pa_browser *z, pa_browse_opcode_t c, const pa_browse_info *i, void *userdata); +/** Set the callback pointer for the browser object */ void pa_browser_set_callback(pa_browser *z, pa_browse_cb_t cb, void *userdata); +/** Callback prototype for errors */ +typedef void (*pa_browser_error_cb_t)(pa_browser *z, const char *error_string, void *userdata); + +/** Set a callback function that is called whenever the browser object + * becomes invalid due to an error. After this function has been + * called the browser object has become invalid and should be + * freed. */ +void pa_browser_set_error_callback(pa_browser *z, pa_browser_error_cb_t, void *userdata); + PA_C_DECL_END #endif -- cgit From 881d4ddd397a829930b6800f1ecc438439cdc766 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 14 Jul 2006 00:17:31 +0000 Subject: * fall back to prctl(PR_GET_NAME) in pa_get_binary_name() if readlink() fails * call pa_path_get_filename() in all cases before returning in pa_get_binary_name(). We already did so on Win32, but didn't on Linux. git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@1077 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/util.c | 63 ++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 45 insertions(+), 18 deletions(-) (limited to 'src/pulse') diff --git a/src/pulse/util.c b/src/pulse/util.c index 338607c4..b041fec8 100644 --- a/src/pulse/util.c +++ b/src/pulse/util.c @@ -49,6 +49,10 @@ #include #endif +#ifdef HAVE_SYS_PRCTL_H +#include +#endif + #include "../pulsecore/winsock.h" #include @@ -152,28 +156,51 @@ char *pa_get_home_dir(char *s, size_t l) { char *pa_get_binary_name(char *s, size_t l) { -#ifdef HAVE_READLINK - char path[PATH_MAX]; - int i; - assert(s && l); + assert(s); + assert(l); - /* This works on Linux only */ +#if defined(OS_IS_WIN32) + { + char path[PATH_MAX]; + + if (GetModuleFileName(NULL, path, PATH_MAX)) + return pa_strlcpy(s, pa_path_get_filename(path), l); + } +#endif - snprintf(path, sizeof(path), "/proc/%u/exe", (unsigned) getpid()); - if ((i = readlink(path, s, l-1)) < 0) - return NULL; +#ifdef HAVE_READLINK + { + int i; + char path[PATH_MAX]; + /* This works on Linux only */ + + if ((i = readlink("/proc/self/exe", path, sizeof(path)-1)) >= 0) { + path[i] = 0; + return pa_strlcpy(s, pa_path_get_filename(path), l); + } + } + +#endif + +#if defined(HAVE_SYS_PRCTL_H) && defined(PR_GET_NAME) + { - s[i] = 0; - return s; -#elif defined(OS_IS_WIN32) - char path[PATH_MAX]; - if (!GetModuleFileName(NULL, path, PATH_MAX)) - return NULL; - pa_strlcpy(s, pa_path_get_filename(path), l); - return s; -#else - return NULL; + #ifndef TASK_COMM_LEN + /* Actually defined in linux/sched.h */ + #define TASK_COMM_LEN 16 + #endif + + char tcomm[TASK_COMM_LEN+1]; + memset(tcomm, 0, sizeof(tcomm)); + + /* This works on Linux only */ + if (prctl(PR_GET_NAME, (unsigned long) tcomm, 0, 0, 0) == 0) + return pa_strlcpy(s, tcomm, l); + + } #endif + + return NULL; } const char *pa_path_get_filename(const char *p) { -- cgit From 860be2e70b33ff5eeb9130f80c4b1c096a2a8f27 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 14 Jul 2006 22:42:01 +0000 Subject: try to use send(,,MSG_NOSIGNAL) instead of write() wherever possible (which will allow us to drop the SIGPIPE check). Cache the results of the last write()/send() to make sure that we do not issue more than necessary system calls. git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@1083 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/mainloop-signal.c | 4 ++-- src/pulse/mainloop.c | 6 ++++-- src/pulse/xmalloc.c | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) (limited to 'src/pulse') diff --git a/src/pulse/mainloop-signal.c b/src/pulse/mainloop-signal.c index c3cf362d..11a27cd5 100644 --- a/src/pulse/mainloop-signal.c +++ b/src/pulse/mainloop-signal.c @@ -67,7 +67,7 @@ static void signal_handler(int sig) { #ifndef HAVE_SIGACTION signal(sig, signal_handler); #endif - pa_write(signal_pipe[1], &sig, sizeof(sig)); + pa_write(signal_pipe[1], &sig, sizeof(sig), NULL); } static void dispatch(pa_mainloop_api*a, int sig) { @@ -86,7 +86,7 @@ static void callback(pa_mainloop_api*a, pa_io_event*e, int fd, pa_io_event_flags int sig; assert(a && e && f == PA_IO_EVENT_INPUT && e == io_event && fd == signal_pipe[0]); - if ((r = pa_read(signal_pipe[0], &sig, sizeof(sig))) < 0) { + if ((r = pa_read(signal_pipe[0], &sig, sizeof(sig), NULL)) < 0) { if (errno == EAGAIN) return; diff --git a/src/pulse/mainloop.c b/src/pulse/mainloop.c index 32f1a845..dfbc337b 100644 --- a/src/pulse/mainloop.c +++ b/src/pulse/mainloop.c @@ -100,6 +100,7 @@ struct pa_mainloop { int deferred_pending; int wakeup_pipe[2]; + int wakeup_pipe_type; enum { STATE_PASSIVE, @@ -337,6 +338,7 @@ pa_mainloop *pa_mainloop_new(void) { m = pa_xmalloc(sizeof(pa_mainloop)); + m->wakeup_pipe_type = 0; if (pipe(m->wakeup_pipe) < 0) { pa_log_error(__FILE__": ERROR: cannot create wakeup pipe"); pa_xfree(m); @@ -625,7 +627,7 @@ void pa_mainloop_wakeup(pa_mainloop *m) { assert(m); if (m->wakeup_pipe[1] >= 0) - pa_write(m->wakeup_pipe[1], &c, sizeof(c)); + pa_write(m->wakeup_pipe[1], &c, sizeof(c), &m->wakeup_pipe_type); } static void clear_wakeup(pa_mainloop *m) { @@ -636,7 +638,7 @@ static void clear_wakeup(pa_mainloop *m) { if (m->wakeup_pipe[0] < 0) return; - while (pa_read(m->wakeup_pipe[0], &c, sizeof(c)) == sizeof(c)); + while (pa_read(m->wakeup_pipe[0], &c, sizeof(c), &m->wakeup_pipe_type) == sizeof(c)); } int pa_mainloop_prepare(pa_mainloop *m, int timeout) { diff --git a/src/pulse/xmalloc.c b/src/pulse/xmalloc.c index 46871aeb..36755166 100644 --- a/src/pulse/xmalloc.c +++ b/src/pulse/xmalloc.c @@ -49,7 +49,7 @@ static void oom(void) PA_GCC_NORETURN; * exits */ static void oom(void) { static const char e[] = "Not enough memory\n"; - pa_loop_write(STDERR_FILENO, e, sizeof(e)-1); + pa_loop_write(STDERR_FILENO, e, sizeof(e)-1, NULL); #ifdef SIGQUIT raise(SIGQUIT); #endif -- cgit From b8f9ae00f341c936eb27dd51d307f99682a09685 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 14 Jul 2006 23:06:44 +0000 Subject: remove checking for SIGPIPE blocking from client code. Because we use send(,,MSG_NOSIGNAL) for most socket writes now the reason for SIGPIPE blocking is no longer give. We keep this check for the server side however, because pipes create SIGPIPE too but cannot be used with MSG_NOSIGNAL. Some modules use pipes for internal and external communication. git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@1086 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/context.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'src/pulse') diff --git a/src/pulse/context.c b/src/pulse/context.c index 648024c3..5724765b 100644 --- a/src/pulse/context.c +++ b/src/pulse/context.c @@ -132,10 +132,6 @@ pa_context *pa_context_new(pa_mainloop_api *mainloop, const char *name) { memset(&c->spawn_api, 0, sizeof(c->spawn_api)); c->do_autospawn = 0; -#ifdef SIGPIPE - pa_check_signal_is_blocked(SIGPIPE); -#endif - c->conf = pa_client_conf_new(); pa_client_conf_load(c->conf, NULL); #ifdef HAVE_X11 -- cgit From 494fa68327fb52276d68437e8467886ce81de297 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sun, 16 Jul 2006 17:28:10 +0000 Subject: add new PA_SOURCE_HARDWARE/PA_SINK_HARDWARE flag git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@1091 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/def.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'src/pulse') diff --git a/src/pulse/def.h b/src/pulse/def.h index b98337d2..06d4c7c2 100644 --- a/src/pulse/def.h +++ b/src/pulse/def.h @@ -295,13 +295,15 @@ typedef enum pa_seek_mode { /** Special sink flags. \since 0.8 */ typedef enum pa_sink_flags { PA_SINK_HW_VOLUME_CTRL = 1, /**< Supports hardware volume control */ - PA_SINK_LATENCY = 2 /**< Supports latency querying */ + PA_SINK_LATENCY = 2, /**< Supports latency querying */ + PA_SINK_HARDWARE = 4 /**< Is a hardware sink of some kind, in contrast to "virtual"/software sinks */ } pa_sink_flags_t; /** Special source flags. \since 0.8 */ typedef enum pa_source_flags { PA_SOURCE_HW_VOLUME_CTRL = 1, /**< Supports hardware volume control */ - PA_SOURCE_LATENCY = 2 /**< Supports latency querying */ + PA_SOURCE_LATENCY = 2, /**< Supports latency querying */ + PA_SOURCE_HARDWARE = 4 /**< Is a hardware source of some kind, in contrast to "virtual"/software source */ } pa_source_flags_t; /** A generic free() like callback prototype */ -- cgit From 4b352e5fac5ff546315139f7b791074261544f66 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Mon, 17 Jul 2006 11:26:29 +0000 Subject: Restore SIGPIPE warning when the platform doesn't have MSG_NOSIGNAL. git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@1097 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/context.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'src/pulse') diff --git a/src/pulse/context.c b/src/pulse/context.c index 5724765b..228053bc 100644 --- a/src/pulse/context.c +++ b/src/pulse/context.c @@ -132,6 +132,12 @@ pa_context *pa_context_new(pa_mainloop_api *mainloop, const char *name) { memset(&c->spawn_api, 0, sizeof(c->spawn_api)); c->do_autospawn = 0; +#ifndef MSG_NOSIGNAL +#ifdef SIGPIPE + pa_check_signal_is_blocked(SIGPIPE); +#endif +#endif + c->conf = pa_client_conf_new(); pa_client_conf_load(c->conf, NULL); #ifdef HAVE_X11 -- cgit From ddd5acf5535b3a97af56987bb26ce152bdd772a3 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 18 Jul 2006 18:51:35 +0000 Subject: define proper typdefs for callback prototypes git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@1100 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/mainloop-api.h | 43 +++++++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 20 deletions(-) (limited to 'src/pulse') diff --git a/src/pulse/mainloop-api.h b/src/pulse/mainloop-api.h index 4aaeccf5..7b7075ae 100644 --- a/src/pulse/mainloop-api.h +++ b/src/pulse/mainloop-api.h @@ -44,6 +44,9 @@ PA_C_DECL_BEGIN +/** An abstract mainloop API vtable */ +typedef struct pa_mainloop_api pa_mainloop_api; + /** A bitmask for IO events */ typedef enum pa_io_event_flags { PA_IO_EVENT_NULL = 0, /**< No event */ @@ -55,15 +58,24 @@ typedef enum pa_io_event_flags { /** An opaque IO event source object */ typedef struct pa_io_event pa_io_event; - -/** An opaque deferred event source object. Events of this type are triggered once in every main loop iteration */ -typedef struct pa_defer_event pa_defer_event; +/** An IO event callback protoype \since 0.9.3 */ +typedef void (*pa_io_event_cb_t)(pa_mainloop_api*ea, pa_io_event* e, int fd, pa_io_event_flags_t events, void *userdata); +/** A IO event destroy callback prototype \ since 0.9.3 */ +typedef void (*pa_io_event_destroy_cb_t)(pa_mainloop_api*a, pa_io_event *e, void *userdata); /** An opaque timer event source object */ typedef struct pa_time_event pa_time_event; +/** A time event callback prototype \since 0.9.3 */ +typedef void (*pa_time_event_cb_t)(pa_mainloop_api*a, pa_time_event* e, const struct timeval *tv, void *userdata); +/** A time event destroy callback prototype \ since 0.9.3 */ +typedef void (*pa_time_event_destroy_cb_t)(pa_mainloop_api*a, pa_time_event *e, void *userdata); -/** An abstract mainloop API vtable */ -typedef struct pa_mainloop_api pa_mainloop_api; +/** An opaque deferred event source object. Events of this type are triggered once in every main loop iteration */ +typedef struct pa_defer_event pa_defer_event; +/** A defer event callback protoype \since 0.9.3 */ +typedef void (*pa_defer_event_cb_t)(pa_mainloop_api*a, pa_defer_event* e, void *userdata); +/** A defer event destroy callback prototype \ since 0.9.3 */ +typedef void (*pa_defer_event_destroy_cb_t)(pa_mainloop_api*a, pa_defer_event *e, void *userdata); /** An abstract mainloop API vtable */ struct pa_mainloop_api { @@ -71,40 +83,31 @@ struct pa_mainloop_api { void *userdata; /** Create a new IO event source object */ - pa_io_event* (*io_new)(pa_mainloop_api*a, int fd, pa_io_event_flags_t events, void (*callback) (pa_mainloop_api*a, pa_io_event* e, int fd, pa_io_event_flags_t events, void *userdata), void *userdata); - + pa_io_event* (*io_new)(pa_mainloop_api*a, int fd, pa_io_event_flags_t events, pa_io_event_cb_t cb, void *userdata); /** Enable or disable IO events on this object */ void (*io_enable)(pa_io_event* e, pa_io_event_flags_t events); - /** Free a IO event source object */ void (*io_free)(pa_io_event* e); - /** Set a function that is called when the IO event source is destroyed. Use this to free the userdata argument if required */ - void (*io_set_destroy)(pa_io_event *e, void (*callback) (pa_mainloop_api*a, pa_io_event *e, void *userdata)); + void (*io_set_destroy)(pa_io_event *e, pa_io_event_destroy_cb_t cb); /** Create a new timer event source object for the specified Unix time */ - pa_time_event* (*time_new)(pa_mainloop_api*a, const struct timeval *tv, void (*callback) (pa_mainloop_api*a, pa_time_event* e, const struct timeval *tv, void *userdata), void *userdata); - + pa_time_event* (*time_new)(pa_mainloop_api*a, const struct timeval *tv, pa_time_event_cb_t cb, void *userdata); /** Restart a running or expired timer event source with a new Unix time */ void (*time_restart)(pa_time_event* e, const struct timeval *tv); - /** Free a deferred timer event source object */ void (*time_free)(pa_time_event* e); - /** Set a function that is called when the timer event source is destroyed. Use this to free the userdata argument if required */ - void (*time_set_destroy)(pa_time_event *e, void (*callback) (pa_mainloop_api*a, pa_time_event *e, void *userdata)); + void (*time_set_destroy)(pa_time_event *e, pa_time_event_destroy_cb_t cb); /** Create a new deferred event source object */ - pa_defer_event* (*defer_new)(pa_mainloop_api*a, void (*callback) (pa_mainloop_api*a, pa_defer_event* e, void *userdata), void *userdata); - + pa_defer_event* (*defer_new)(pa_mainloop_api*a, pa_defer_event_cb_t cb, void *userdata); /** Enable or disable a deferred event source temporarily */ void (*defer_enable)(pa_defer_event* e, int b); - /** Free a deferred event source object */ void (*defer_free)(pa_defer_event* e); - /** Set a function that is called when the deferred event source is destroyed. Use this to free the userdata argument if required */ - void (*defer_set_destroy)(pa_defer_event *e, void (*callback) (pa_mainloop_api*a, pa_defer_event *e, void *userdata)); + void (*defer_set_destroy)(pa_defer_event *e, pa_defer_event_destroy_cb_t cb); /** Exit the main loop and return the specfied retval*/ void (*quit)(pa_mainloop_api*a, int retval); -- cgit From 2c2abbb1698c6fa0607d4db2a20efce141a0f8c5 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 18 Jul 2006 18:52:13 +0000 Subject: turn the glib adapter into a single GSource instead of creating a bunch of seperate GSources for each event git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@1101 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/glib-mainloop.c | 788 ++++++++++++++++++++++++++-------------------- 1 file changed, 444 insertions(+), 344 deletions(-) (limited to 'src/pulse') diff --git a/src/pulse/glib-mainloop.c b/src/pulse/glib-mainloop.c index 25848ece..cdaecdf8 100644 --- a/src/pulse/glib-mainloop.c +++ b/src/pulse/glib-mainloop.c @@ -30,363 +30,544 @@ #include #include +#include +#include -#include "glib.h" +#include #include "glib-mainloop.h" struct pa_io_event { pa_glib_mainloop *mainloop; int dead; - GIOChannel *io_channel; - GSource *source; - GIOCondition io_condition; - int fd; - void (*callback) (pa_mainloop_api*m, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata); + + GPollFD poll_fd; + int poll_fd_added; + + pa_io_event_cb_t callback; void *userdata; - void (*destroy_callback) (pa_mainloop_api *m, pa_io_event *e, void *userdata); - pa_io_event *next, *prev; + pa_io_event_destroy_cb_t destroy_callback; + + PA_LLIST_FIELDS(pa_io_event); }; struct pa_time_event { pa_glib_mainloop *mainloop; int dead; - GSource *source; + + int enabled; struct timeval timeval; - void (*callback) (pa_mainloop_api*m, pa_time_event *e, const struct timeval *tv, void *userdata); + + pa_time_event_cb_t callback; void *userdata; - void (*destroy_callback) (pa_mainloop_api *m, pa_time_event*e, void *userdata); - pa_time_event *next, *prev; + pa_time_event_destroy_cb_t destroy_callback; + + PA_LLIST_FIELDS(pa_time_event); }; struct pa_defer_event { pa_glib_mainloop *mainloop; int dead; - GSource *source; - void (*callback) (pa_mainloop_api*m, pa_defer_event *e, void *userdata); + + int enabled; + + pa_defer_event_cb_t callback; void *userdata; - void (*destroy_callback) (pa_mainloop_api *m, pa_defer_event*e, void *userdata); - pa_defer_event *next, *prev; + pa_defer_event_destroy_cb_t destroy_callback; + + PA_LLIST_FIELDS(pa_defer_event); }; struct pa_glib_mainloop { - GMainContext *glib_main_context; + GSource source; + pa_mainloop_api api; - GSource *cleanup_source; - pa_io_event *io_events, *dead_io_events; - pa_time_event *time_events, *dead_time_events; - pa_defer_event *defer_events, *dead_defer_events; -}; + GMainContext *context; -static void schedule_free_dead_events(pa_glib_mainloop *g); + PA_LLIST_HEAD(pa_io_event, io_events); + PA_LLIST_HEAD(pa_time_event, time_events); + PA_LLIST_HEAD(pa_defer_event, defer_events); -static void glib_io_enable(pa_io_event*e, pa_io_event_flags_t f); + int n_enabled_defer_events, n_enabled_time_events; + int io_events_please_scan, time_events_please_scan, defer_events_please_scan; -static pa_io_event* glib_io_new(pa_mainloop_api*m, int fd, pa_io_event_flags_t f, void (*callback) (pa_mainloop_api*m, pa_io_event*e, int fd, pa_io_event_flags_t f, void *userdata), void *userdata) { - pa_io_event *e; - pa_glib_mainloop *g; + pa_time_event *cached_next_time_event; +}; - assert(m && m->userdata && fd >= 0 && callback); - g = m->userdata; +static void cleanup_io_events(pa_glib_mainloop *g, int force) { + pa_io_event *e; - e = pa_xmalloc(sizeof(pa_io_event)); - e->mainloop = m->userdata; - e->dead = 0; - e->fd = fd; - e->callback = callback; - e->userdata = userdata; - e->destroy_callback = NULL; + e = g->io_events; + while (e) { + pa_io_event *n = e->next; - e->io_channel = g_io_channel_unix_new(e->fd); - assert(e->io_channel); - e->source = NULL; - e->io_condition = 0; + if (!force && g->io_events_please_scan <= 0) + break; + + if (force || e->dead) { + PA_LLIST_REMOVE(pa_io_event, g->io_events, e); + + if (e->dead) + g->io_events_please_scan--; + + if (e->poll_fd_added) + g_source_remove_poll(&g->source, &e->poll_fd); + + if (e->destroy_callback) + e->destroy_callback(&g->api, e, e->userdata); + + pa_xfree(e); + } - glib_io_enable(e, f); + e = n; + } - e->next = g->io_events; - if (e->next) e->next->prev = e; - g->io_events = e; - e->prev = NULL; - - return e; + assert(g->io_events_please_scan == 0); } -/* The callback GLIB calls whenever an IO condition is met */ -static gboolean io_cb(GIOChannel *source, GIOCondition condition, gpointer data) { - pa_io_event *e = data; - pa_io_event_flags_t f; - assert(source && e && e->io_channel == source); +static void cleanup_time_events(pa_glib_mainloop *g, int force) { + pa_time_event *e; - f = (condition & G_IO_IN ? PA_IO_EVENT_INPUT : 0) | - (condition & G_IO_OUT ? PA_IO_EVENT_OUTPUT : 0) | - (condition & G_IO_ERR ? PA_IO_EVENT_ERROR : 0) | - (condition & G_IO_HUP ? PA_IO_EVENT_HANGUP : 0); - - e->callback(&e->mainloop->api, e, e->fd, f, e->userdata); - return TRUE; -} + e = g->time_events; + while (e) { + pa_time_event *n = e->next; -static void glib_io_enable(pa_io_event*e, pa_io_event_flags_t f) { - GIOCondition c; - assert(e && !e->dead); + if (!force && g->time_events_please_scan <= 0) + break; + + if (force || e->dead) { + PA_LLIST_REMOVE(pa_time_event, g->time_events, e); + + if (e->dead) + g->time_events_please_scan--; + + if (!e->dead && e->enabled) + g->n_enabled_time_events--; + + if (e->destroy_callback) + e->destroy_callback(&g->api, e, e->userdata); + + pa_xfree(e); + } - c = (f & PA_IO_EVENT_INPUT ? G_IO_IN : 0) | (f & PA_IO_EVENT_OUTPUT ? G_IO_OUT : 0); - - if (c == e->io_condition) - return; - - if (e->source) { - g_source_destroy(e->source); - g_source_unref(e->source); + e = n; } - - e->source = g_io_create_watch(e->io_channel, c | G_IO_ERR | G_IO_HUP); - assert(e->source); - - g_source_set_callback(e->source, (GSourceFunc) io_cb, e, NULL); - g_source_attach(e->source, e->mainloop->glib_main_context); - g_source_set_priority(e->source, G_PRIORITY_DEFAULT); - - e->io_condition = c; -} -static void glib_io_free(pa_io_event*e) { - assert(e && !e->dead); + assert(g->time_events_please_scan == 0); +} - if (e->source) { - g_source_destroy(e->source); - g_source_unref(e->source); - e->source = NULL; - } - - if (e->prev) - e->prev->next = e->next; - else - e->mainloop->io_events = e->next; +static void cleanup_defer_events(pa_glib_mainloop *g, int force) { + pa_defer_event *e; - if (e->next) - e->next->prev = e->prev; + e = g->defer_events; + while (e) { + pa_defer_event *n = e->next; - if ((e->next = e->mainloop->dead_io_events)) - e->next->prev = e; + if (!force && g->defer_events_please_scan <= 0) + break; + + if (force || e->dead) { + PA_LLIST_REMOVE(pa_defer_event, g->defer_events, e); + + if (e->dead) + g->defer_events_please_scan--; + + if (!e->dead && e->enabled) + g->n_enabled_defer_events--; + + if (e->destroy_callback) + e->destroy_callback(&g->api, e, e->userdata); + + pa_xfree(e); + } - e->mainloop->dead_io_events = e; - e->prev = NULL; + e = n; + } - e->dead = 1; - schedule_free_dead_events(e->mainloop); + assert(g->defer_events_please_scan == 0); } -static void glib_io_set_destroy(pa_io_event*e, void (*callback)(pa_mainloop_api*m, pa_io_event *e, void *userdata)) { - assert(e); - e->destroy_callback = callback; +static gushort map_flags_to_glib(pa_io_event_flags_t flags) { + return + (flags & PA_IO_EVENT_INPUT ? G_IO_IN : 0) | + (flags & PA_IO_EVENT_OUTPUT ? G_IO_OUT : 0) | + (flags & PA_IO_EVENT_ERROR ? G_IO_ERR : 0) | + (flags & PA_IO_EVENT_HANGUP ? G_IO_HUP : 0); } -/* Time sources */ - -static void glib_time_restart(pa_time_event*e, const struct timeval *tv); +static pa_io_event_flags_t map_flags_from_glib(gushort flags) { + return + (flags & G_IO_IN ? PA_IO_EVENT_INPUT : 0) | + (flags & G_IO_OUT ? PA_IO_EVENT_OUTPUT : 0) | + (flags & G_IO_ERR ? PA_IO_EVENT_ERROR : 0) | + (flags & G_IO_HUP ? PA_IO_EVENT_HANGUP : 0); +} -static pa_time_event* glib_time_new(pa_mainloop_api*m, const struct timeval *tv, void (*callback) (pa_mainloop_api*m, pa_time_event*e, const struct timeval *tv, void *userdata), void *userdata) { +static pa_io_event* glib_io_new( + pa_mainloop_api*m, + int fd, + pa_io_event_flags_t f, + pa_io_event_cb_t cb, + void *userdata) { + + pa_io_event *e; pa_glib_mainloop *g; - pa_time_event *e; + + assert(m); + assert(m->userdata); + assert(fd >= 0); + assert(cb); - assert(m && m->userdata && tv && callback); g = m->userdata; - e = pa_xmalloc(sizeof(pa_time_event)); + e = pa_xnew(pa_io_event, 1); e->mainloop = g; e->dead = 0; - e->callback = callback; + + e->poll_fd.fd = fd; + e->poll_fd.events = map_flags_to_glib(f); + e->poll_fd.revents = 0; + + e->callback = cb; e->userdata = userdata; e->destroy_callback = NULL; - e->source = NULL; - glib_time_restart(e, tv); + PA_LLIST_PREPEND(pa_io_event, g->io_events, e); - e->next = g->time_events; - if (e->next) e->next->prev = e; - g->time_events = e; - e->prev = NULL; + g_source_add_poll(&g->source, &e->poll_fd); + e->poll_fd_added = 1; return e; } -static guint msec_diff(const struct timeval *a, const struct timeval *b) { - guint r; - assert(a && b); - - if (a->tv_sec < b->tv_sec) - return 0; +static void glib_io_enable(pa_io_event*e, pa_io_event_flags_t f) { + assert(e); + assert(!e->dead); - if (a->tv_sec == b->tv_sec && a->tv_sec <= b->tv_sec) - return 0; + e->poll_fd.events = map_flags_to_glib(f); +} - r = (a->tv_sec-b->tv_sec)*1000; +static void glib_io_free(pa_io_event*e) { + assert(e); + assert(!e->dead); - if (a->tv_usec >= b->tv_usec) - r += (a->tv_usec - b->tv_usec) / 1000; - else - r -= (b->tv_usec - a->tv_usec) / 1000; + e->dead = 1; + e->mainloop->io_events_please_scan++; + + if (e->poll_fd_added) { + g_source_remove_poll(&e->mainloop->source, &e->poll_fd); + e->poll_fd_added = 0; + } +} + +static void glib_io_set_destroy(pa_io_event*e, pa_io_event_destroy_cb_t cb) { + assert(e); + assert(!e->dead); - return r; + e->destroy_callback = cb; } -static gboolean time_cb(gpointer data) { - pa_time_event* e = data; - assert(e && e->mainloop && e->source); +/* Time sources */ - g_source_unref(e->source); - e->source = NULL; +static pa_time_event* glib_time_new( + pa_mainloop_api*m, + const struct timeval *tv, + pa_time_event_cb_t cb, + void *userdata) { + + pa_glib_mainloop *g; + pa_time_event *e; + + assert(m); + assert(m->userdata); + assert(cb); + + g = m->userdata; - e->callback(&e->mainloop->api, e, &e->timeval, e->userdata); - return FALSE; -} + e = pa_xnew(pa_time_event, 1); + e->mainloop = g; + e->dead = 0; -static void glib_time_restart(pa_time_event*e, const struct timeval *tv) { - struct timeval now; - assert(e && e->mainloop && !e->dead); + if ((e->enabled = !!tv)) { + e->timeval = *tv; + g->n_enabled_time_events++; - pa_gettimeofday(&now); - if (e->source) { - g_source_destroy(e->source); - g_source_unref(e->source); + if (g->cached_next_time_event) { + g_assert(g->cached_next_time_event->enabled); + + if (pa_timeval_cmp(tv, &g->cached_next_time_event->timeval) < 0) + g->cached_next_time_event = e; + } } + + e->callback = cb; + e->userdata = userdata; + e->destroy_callback = NULL; - if (tv) { - e->timeval = *tv; - e->source = g_timeout_source_new(msec_diff(tv, &now)); - assert(e->source); - g_source_set_callback(e->source, time_cb, e, NULL); - g_source_set_priority(e->source, G_PRIORITY_DEFAULT); - g_source_attach(e->source, e->mainloop->glib_main_context); - } else - e->source = NULL; - } + PA_LLIST_PREPEND(pa_time_event, g->time_events, e); + + return e; +} -static void glib_time_free(pa_time_event *e) { - assert(e && e->mainloop && !e->dead); +static void glib_time_restart(pa_time_event*e, const struct timeval *tv) { + assert(e); + assert(!e->dead); - if (e->source) { - g_source_destroy(e->source); - g_source_unref(e->source); - e->source = NULL; - } + if (e->enabled && !!tv) + e->mainloop->n_enabled_time_events--; + else if (!e->enabled && tv) + e->mainloop->n_enabled_time_events++; - if (e->prev) - e->prev->next = e->next; - else - e->mainloop->time_events = e->next; + if ((e->enabled = !!tv)) + e->timeval = *tv; - if (e->next) - e->next->prev = e->prev; + if (e->mainloop->cached_next_time_event && e->enabled) { + g_assert(e->mainloop->cached_next_time_event->enabled); - if ((e->next = e->mainloop->dead_time_events)) - e->next->prev = e; + if (pa_timeval_cmp(tv, &e->mainloop->cached_next_time_event->timeval) < 0) + e->mainloop->cached_next_time_event = e; + } else if (e->mainloop->cached_next_time_event == e) + e->mainloop->cached_next_time_event = NULL; + } - e->mainloop->dead_time_events = e; - e->prev = NULL; +static void glib_time_free(pa_time_event *e) { + assert(e); + assert(!e->dead); e->dead = 1; - schedule_free_dead_events(e->mainloop); + e->mainloop->time_events_please_scan++; + + if (e->enabled) + e->mainloop->n_enabled_time_events--; + + if (e->mainloop->cached_next_time_event == e) + e->mainloop->cached_next_time_event = NULL; } -static void glib_time_set_destroy(pa_time_event *e, void (*callback)(pa_mainloop_api*m, pa_time_event*e, void *userdata)) { +static void glib_time_set_destroy(pa_time_event *e, pa_time_event_destroy_cb_t cb) { assert(e); - e->destroy_callback = callback; + assert(!e->dead); + + e->destroy_callback = cb; } /* Deferred sources */ -static void glib_defer_enable(pa_defer_event *e, int b); - -static pa_defer_event* glib_defer_new(pa_mainloop_api*m, void (*callback) (pa_mainloop_api*m, pa_defer_event *e, void *userdata), void *userdata) { +static pa_defer_event* glib_defer_new( + pa_mainloop_api*m, + pa_defer_event_cb_t cb, + void *userdata) { + pa_defer_event *e; pa_glib_mainloop *g; - assert(m && m->userdata && callback); + assert(m); + assert(m->userdata); + assert(cb); + g = m->userdata; - e = pa_xmalloc(sizeof(pa_defer_event)); + e = pa_xnew(pa_defer_event, 1); e->mainloop = g; e->dead = 0; - e->callback = callback; + + e->enabled = 1; + g->n_enabled_defer_events++; + + e->callback = cb; e->userdata = userdata; e->destroy_callback = NULL; - e->source = NULL; + + PA_LLIST_PREPEND(pa_defer_event, g->defer_events, e); + return e; +} + +static void glib_defer_enable(pa_defer_event *e, int b) { + assert(e); + assert(!e->dead); - glib_defer_enable(e, 1); + if (e->enabled && !b) + e->mainloop->n_enabled_defer_events--; + else if (!e->enabled && b) + e->mainloop->n_enabled_defer_events++; - e->next = g->defer_events; - if (e->next) e->next->prev = e; - g->defer_events = e; - e->prev = NULL; - return e; + e->enabled = b; } -static gboolean idle_cb(gpointer data) { - pa_defer_event* e = data; - assert(e && e->mainloop && e->source); +static void glib_defer_free(pa_defer_event *e) { + assert(e); + assert(!e->dead); + + e->dead = 1; + e->mainloop->defer_events_please_scan++; - e->callback(&e->mainloop->api, e, e->userdata); - return TRUE; + if (e->enabled) + e->mainloop->n_enabled_defer_events--; } -static void glib_defer_enable(pa_defer_event *e, int b) { - assert(e && e->mainloop); - - if (e->source && !b) { - g_source_destroy(e->source); - g_source_unref(e->source); - e->source = NULL; - } else if (!e->source && b) { - e->source = g_idle_source_new(); - assert(e->source); - g_source_set_callback(e->source, idle_cb, e, NULL); - g_source_attach(e->source, e->mainloop->glib_main_context); - g_source_set_priority(e->source, G_PRIORITY_HIGH); - } +static void glib_defer_set_destroy(pa_defer_event *e, pa_defer_event_destroy_cb_t cb) { + assert(e); + assert(!e->dead); + + e->destroy_callback = cb; } -static void glib_defer_free(pa_defer_event *e) { - assert(e && e->mainloop && !e->dead); +/* quit() */ + +static void glib_quit(pa_mainloop_api*a, PA_GCC_UNUSED int retval) { - if (e->source) { - g_source_destroy(e->source); - g_source_unref(e->source); - e->source = NULL; + g_warning("quit() ignored"); + + /* NOOP */ +} + +static pa_time_event* find_next_time_event(pa_glib_mainloop *g) { + pa_time_event *t, *n = NULL; + assert(g); + + if (g->cached_next_time_event) + return g->cached_next_time_event; + + for (t = g->time_events; t; t = t->next) { + + if (t->dead || !t->enabled) + continue; + + if (!n || pa_timeval_cmp(&t->timeval, &n->timeval) < 0) { + n = t; + + /* Shortcut for tv = { 0, 0 } */ + if (n->timeval.tv_sec <= 0) + break; + } } - if (e->prev) - e->prev->next = e->next; - else - e->mainloop->defer_events = e->next; + g->cached_next_time_event = n; + return n; +} - if (e->next) - e->next->prev = e->prev; +static gboolean prepare_func(GSource *source, gint *timeout) { + pa_glib_mainloop *g = (pa_glib_mainloop*) source; - if ((e->next = e->mainloop->dead_defer_events)) - e->next->prev = e; + g_assert(g); + g_assert(timeout); - e->mainloop->dead_defer_events = e; - e->prev = NULL; + if (g->io_events_please_scan) + cleanup_io_events(g, 0); - e->dead = 1; - schedule_free_dead_events(e->mainloop); + if (g->time_events_please_scan) + cleanup_time_events(g, 0); + + if (g->defer_events_please_scan) + cleanup_defer_events(g, 0); + + if (g->n_enabled_defer_events) { + *timeout = 0; + return TRUE; + } else if (g->n_enabled_time_events) { + pa_time_event *t; + GTimeVal now; + struct timeval tvnow; + pa_usec_t usec; + + t = find_next_time_event(g); + g_assert(t); + + g_source_get_current_time(source, &now); + tvnow.tv_sec = now.tv_sec; + tvnow.tv_usec = now.tv_usec; + + usec = pa_timeval_diff(&t->timeval, &tvnow); + + if (usec <= 0) { + *timeout = 0; + return TRUE; + } + + *timeout = (gint) (usec / 1000); + } else + *timeout = -1; + + return FALSE; } +static gboolean check_func(GSource *source) { + pa_glib_mainloop *g = (pa_glib_mainloop*) source; + pa_io_event *e; -static void glib_defer_set_destroy(pa_defer_event *e, void (*callback)(pa_mainloop_api *m, pa_defer_event *e, void *userdata)) { - assert(e); - e->destroy_callback = callback; + g_assert(g); + + if (g->n_enabled_defer_events) + return TRUE; + else if (g->n_enabled_time_events) { + pa_time_event *t; + GTimeVal now; + struct timeval tvnow; + + t = find_next_time_event(g); + g_assert(t); + + g_source_get_current_time(source, &now); + tvnow.tv_sec = now.tv_sec; + tvnow.tv_usec = now.tv_usec; + + if (pa_timeval_cmp(&t->timeval, &tvnow) <= 0) + return TRUE; + } + + for (e = g->io_events; e; e = e->next) + if (!e->dead && e->poll_fd.revents != 0) + return TRUE; + + return FALSE; } -/* quit() */ +static gboolean dispatch_func(GSource *source, PA_GCC_UNUSED GSourceFunc callback, PA_GCC_UNUSED gpointer userdata) { + pa_glib_mainloop *g = (pa_glib_mainloop*) source; + pa_io_event *e; -static void glib_quit(pa_mainloop_api*a, PA_GCC_UNUSED int retval) { - pa_glib_mainloop *g; - assert(a && a->userdata); - g = a->userdata; + g_assert(g); - /* NOOP */ + if (g->n_enabled_defer_events) { + pa_defer_event *d; + + for (d = g->defer_events; d; d = d->next) { + if (d->dead || !d->enabled) + continue; + + break; + } + + assert(d); + + d->callback(&g->api, d, d->userdata); + return TRUE; + } + + if (g->n_enabled_time_events) { + GTimeVal now; + struct timeval tvnow; + pa_time_event *t; + + t = find_next_time_event(g); + g_assert(t); + + g_source_get_current_time(source, &now); + tvnow.tv_sec = now.tv_sec; + tvnow.tv_usec = now.tv_usec; + + if (pa_timeval_cmp(&t->timeval, &tvnow) < 0) { + t->callback(&g->api, t, &t->timeval, t->userdata); + return TRUE; + } + } + + for (e = g->io_events; e; e = e->next) + if (!e->dead && e->poll_fd.revents != 0) { + e->callback(&g->api, e, e->poll_fd.fd, map_flags_from_glib(e->poll_fd.revents), e->userdata); + e->poll_fd.revents = 0; + return TRUE; + } + + return FALSE; } static const pa_mainloop_api vtable = { @@ -412,130 +593,49 @@ static const pa_mainloop_api vtable = { pa_glib_mainloop *pa_glib_mainloop_new(GMainContext *c) { pa_glib_mainloop *g; + + static GSourceFuncs source_funcs = { + prepare_func, + check_func, + dispatch_func, + NULL, + NULL, + NULL + }; - g = pa_xmalloc(sizeof(pa_glib_mainloop)); - if (c) { - g->glib_main_context = c; - g_main_context_ref(c); - } else - g->glib_main_context = g_main_context_default(); + g = (pa_glib_mainloop*) g_source_new(&source_funcs, sizeof(pa_glib_mainloop)); + g_main_context_ref(g->context = c ? c : g_main_context_default()); g->api = vtable; g->api.userdata = g; - g->io_events = g->dead_io_events = NULL; - g->time_events = g->dead_time_events = NULL; - g->defer_events = g->dead_defer_events = NULL; + PA_LLIST_HEAD_INIT(pa_io_event, g->io_events); + PA_LLIST_HEAD_INIT(pa_time_event, g->time_events); + PA_LLIST_HEAD_INIT(pa_defer_event, g->defer_events); - g->cleanup_source = NULL; + g->n_enabled_defer_events = g->n_enabled_time_events = 0; + g->io_events_please_scan = g->time_events_please_scan = g->defer_events_please_scan = 0; + + g_source_attach(&g->source, g->context); + g_source_set_can_recurse(&g->source, FALSE); + return g; } -static void free_io_events(pa_io_event *e) { - while (e) { - pa_io_event *r = e; - e = r->next; - - if (r->source) { - g_source_destroy(r->source); - g_source_unref(r->source); - } - - if (r->io_channel) - g_io_channel_unref(r->io_channel); - - if (r->destroy_callback) - r->destroy_callback(&r->mainloop->api, r, r->userdata); - - pa_xfree(r); - } -} - -static void free_time_events(pa_time_event *e) { - while (e) { - pa_time_event *r = e; - e = r->next; - - if (r->source) { - g_source_destroy(r->source); - g_source_unref(r->source); - } - - if (r->destroy_callback) - r->destroy_callback(&r->mainloop->api, r, r->userdata); - - pa_xfree(r); - } -} - -static void free_defer_events(pa_defer_event *e) { - while (e) { - pa_defer_event *r = e; - e = r->next; - - if (r->source) { - g_source_destroy(r->source); - g_source_unref(r->source); - } - - if (r->destroy_callback) - r->destroy_callback(&r->mainloop->api, r, r->userdata); - - pa_xfree(r); - } -} - void pa_glib_mainloop_free(pa_glib_mainloop* g) { assert(g); - free_io_events(g->io_events); - free_io_events(g->dead_io_events); - free_defer_events(g->defer_events); - free_defer_events(g->dead_defer_events); - free_time_events(g->time_events); - free_time_events(g->dead_time_events); - - if (g->cleanup_source) { - g_source_destroy(g->cleanup_source); - g_source_unref(g->cleanup_source); - } + cleanup_io_events(g, 1); + cleanup_defer_events(g, 1); + cleanup_time_events(g, 1); - g_main_context_unref(g->glib_main_context); - pa_xfree(g); + g_main_context_unref(g->context); + g_source_destroy(&g->source); + g_source_unref(&g->source); } pa_mainloop_api* pa_glib_mainloop_get_api(pa_glib_mainloop *g) { assert(g); - return &g->api; -} - -static gboolean free_dead_events(gpointer p) { - pa_glib_mainloop *g = p; - assert(g); - - free_io_events(g->dead_io_events); - free_defer_events(g->dead_defer_events); - free_time_events(g->dead_time_events); - - g->dead_io_events = NULL; - g->dead_defer_events = NULL; - g->dead_time_events = NULL; - - g_source_destroy(g->cleanup_source); - g_source_unref(g->cleanup_source); - g->cleanup_source = NULL; - - return FALSE; -} - -static void schedule_free_dead_events(pa_glib_mainloop *g) { - assert(g && g->glib_main_context); - - if (g->cleanup_source) - return; - g->cleanup_source = g_idle_source_new(); - assert(g->cleanup_source); - g_source_set_callback(g->cleanup_source, free_dead_events, g, NULL); - g_source_attach(g->cleanup_source, g->glib_main_context); + return &g->api; } -- cgit From d7cdaf22a199e4b6710e6912b0378f0c919ff4d1 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 18 Jul 2006 19:50:09 +0000 Subject: add two more \since git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@1103 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/def.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/pulse') diff --git a/src/pulse/def.h b/src/pulse/def.h index 06d4c7c2..01ed0f6d 100644 --- a/src/pulse/def.h +++ b/src/pulse/def.h @@ -296,14 +296,14 @@ typedef enum pa_seek_mode { typedef enum pa_sink_flags { PA_SINK_HW_VOLUME_CTRL = 1, /**< Supports hardware volume control */ PA_SINK_LATENCY = 2, /**< Supports latency querying */ - PA_SINK_HARDWARE = 4 /**< Is a hardware sink of some kind, in contrast to "virtual"/software sinks */ + PA_SINK_HARDWARE = 4 /**< Is a hardware sink of some kind, in contrast to "virtual"/software sinks \since 0.9.3 */ } pa_sink_flags_t; /** Special source flags. \since 0.8 */ typedef enum pa_source_flags { PA_SOURCE_HW_VOLUME_CTRL = 1, /**< Supports hardware volume control */ PA_SOURCE_LATENCY = 2, /**< Supports latency querying */ - PA_SOURCE_HARDWARE = 4 /**< Is a hardware source of some kind, in contrast to "virtual"/software source */ + PA_SOURCE_HARDWARE = 4 /**< Is a hardware source of some kind, in contrast to "virtual"/software source \since 0.9.3 */ } pa_source_flags_t; /** A generic free() like callback prototype */ -- cgit From 9db70682d68cc4fef9314677b6427582e5d5c8f2 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 18 Jul 2006 19:53:29 +0000 Subject: remove glib 1.2 adapter. It started to bitrot and wasn't used by anything anyway. git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@1104 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/glib-mainloop.h | 6 +- src/pulse/glib12-mainloop.c | 503 -------------------------------------------- 2 files changed, 1 insertion(+), 508 deletions(-) delete mode 100644 src/pulse/glib12-mainloop.c (limited to 'src/pulse') diff --git a/src/pulse/glib-mainloop.h b/src/pulse/glib-mainloop.h index 75de1cc7..af7cc0e9 100644 --- a/src/pulse/glib-mainloop.h +++ b/src/pulse/glib-mainloop.h @@ -47,13 +47,9 @@ PA_C_DECL_BEGIN typedef struct pa_glib_mainloop pa_glib_mainloop; /** Create a new GLIB main loop object for the specified GLIB main - * loop context. The GLIB 2.0 version takes an argument c for the + * loop context. Takes an argument c for the * GMainContext to use. If c is NULL the default context is used. */ -#if GLIB_MAJOR_VERSION >= 2 pa_glib_mainloop *pa_glib_mainloop_new(GMainContext *c); -#else -pa_glib_mainloop *pa_glib_mainloop_new(void); -#endif /** Free the GLIB main loop object */ void pa_glib_mainloop_free(pa_glib_mainloop* g); diff --git a/src/pulse/glib12-mainloop.c b/src/pulse/glib12-mainloop.c deleted file mode 100644 index ebaf87fc..00000000 --- a/src/pulse/glib12-mainloop.c +++ /dev/null @@ -1,503 +0,0 @@ -/* $Id$ */ - -/*** - This file is part of PulseAudio. - - 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 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 -#endif - -#include - -#include -#include - -#include -#include - -#include "glib-mainloop.h" - -/* A mainloop implementation based on GLIB 1.2 */ - -struct pa_io_event { - pa_glib_mainloop *mainloop; - int dead; - GIOChannel *io_channel; - guint source; - GIOCondition io_condition; - int fd; - void (*callback) (pa_mainloop_api*m, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata); - void *userdata; - void (*destroy_callback) (pa_mainloop_api *m, pa_io_event*e, void *userdata); - pa_io_event *next, *prev; -}; - -struct pa_time_event { - pa_glib_mainloop *mainloop; - int dead; - guint source; - struct timeval timeval; - void (*callback) (pa_mainloop_api*m, pa_time_event *e, const struct timeval *tv, void *userdata); - void *userdata; - void (*destroy_callback) (pa_mainloop_api *m, pa_time_event*e, void *userdata); - pa_time_event *next, *prev; -}; - -struct pa_defer_event { - pa_glib_mainloop *mainloop; - int dead; - guint source; - void (*callback) (pa_mainloop_api*m, pa_defer_event *e, void *userdata); - void *userdata; - void (*destroy_callback) (pa_mainloop_api *m, pa_defer_event*e, void *userdata); - pa_defer_event *next, *prev; -}; - -struct pa_glib_mainloop { - pa_mainloop_api api; - guint cleanup_source; - pa_io_event *io_events, *dead_io_events; - pa_time_event *time_events, *dead_time_events; - pa_defer_event *defer_events, *dead_defer_events; -}; - -static void schedule_free_dead_events(pa_glib_mainloop *g); - -static void glib_io_enable(pa_io_event*e, pa_io_event_flags_t f); - -static pa_io_event* glib_io_new(pa_mainloop_api*m, int fd, pa_io_event_flags_t f, void (*callback) (pa_mainloop_api*m, pa_io_event*e, int fd, pa_io_event_flags_t f, void *userdata), void *userdata) { - pa_io_event *e; - pa_glib_mainloop *g; - - assert(m && m->userdata && fd >= 0 && callback); - g = m->userdata; - - e = pa_xmalloc(sizeof(pa_io_event)); - e->mainloop = m->userdata; - e->dead = 0; - e->fd = fd; - e->callback = callback; - e->userdata = userdata; - e->destroy_callback = NULL; - - e->io_channel = g_io_channel_unix_new(e->fd); - assert(e->io_channel); - e->source = (guint) -1; - e->io_condition = 0; - - glib_io_enable(e, f); - - e->next = g->io_events; - if (e->next) e->next->prev = e; - g->io_events = e; - e->prev = NULL; - - return e; -} - -static gboolean io_cb(GIOChannel *source, GIOCondition condition, gpointer data) { - pa_io_event *e = data; - pa_io_event_flags_t f; - assert(source && e && e->io_channel == source); - - f = (condition & G_IO_IN ? PA_IO_EVENT_INPUT : 0) | - (condition & G_IO_OUT ? PA_IO_EVENT_OUTPUT : 0) | - (condition & G_IO_ERR ? PA_IO_EVENT_ERROR : 0) | - (condition & G_IO_HUP ? PA_IO_EVENT_HANGUP : 0); - - e->callback(&e->mainloop->api, e, e->fd, f, e->userdata); - return TRUE; -} - -static void glib_io_enable(pa_io_event*e, pa_io_event_flags_t f) { - GIOCondition c; - assert(e && !e->dead); - - c = (f & PA_IO_EVENT_INPUT ? G_IO_IN : 0) | (f & PA_IO_EVENT_OUTPUT ? G_IO_OUT : 0); - - if (c == e->io_condition) - return; - - if (e->source != (guint) -1) - g_source_remove(e->source); - - e->source = g_io_add_watch_full(e->io_channel, G_PRIORITY_DEFAULT, c | G_IO_ERR | G_IO_HUP, io_cb, e, NULL); - assert(e->source != (guint) -1); - e->io_condition = c; -} - -static void glib_io_free(pa_io_event*e) { - assert(e && !e->dead); - - if (e->source != (guint) -1) { - g_source_remove(e->source); - e->source = (guint) -1; - } - - if (e->prev) - e->prev->next = e->next; - else - e->mainloop->io_events = e->next; - - if (e->next) - e->next->prev = e->prev; - - if ((e->next = e->mainloop->dead_io_events)) - e->next->prev = e; - - e->mainloop->dead_io_events = e; - e->prev = NULL; - - e->dead = 1; - schedule_free_dead_events(e->mainloop); -} - -static void glib_io_set_destroy(pa_io_event*e, void (*callback)(pa_mainloop_api*m, pa_io_event *e, void *userdata)) { - assert(e); - e->destroy_callback = callback; -} - -/* Time sources */ - -static void glib_time_restart(pa_time_event*e, const struct timeval *tv); - -static pa_time_event* glib_time_new(pa_mainloop_api*m, const struct timeval *tv, void (*callback) (pa_mainloop_api*m, pa_time_event*e, const struct timeval *tv, void *userdata), void *userdata) { - pa_glib_mainloop *g; - pa_time_event *e; - - assert(m && m->userdata && tv && callback); - g = m->userdata; - - e = pa_xmalloc(sizeof(pa_time_event)); - e->mainloop = g; - e->dead = 0; - e->callback = callback; - e->userdata = userdata; - e->destroy_callback = NULL; - e->source = (guint) -1; - - glib_time_restart(e, tv); - - e->next = g->time_events; - if (e->next) e->next->prev = e; - g->time_events = e; - e->prev = NULL; - - return e; -} - -static guint msec_diff(const struct timeval *a, const struct timeval *b) { - guint r; - assert(a && b); - - if (a->tv_sec < b->tv_sec) - return 0; - - if (a->tv_sec == b->tv_sec && a->tv_sec <= b->tv_sec) - return 0; - - r = (a->tv_sec-b->tv_sec)*1000; - - if (a->tv_usec >= b->tv_usec) - r += (a->tv_usec - b->tv_usec) / 1000; - else - r -= (b->tv_usec - a->tv_usec) / 1000; - - return r; -} - -static gboolean time_cb(gpointer data) { - pa_time_event* e = data; - assert(e && e->mainloop && e->source != (guint) -1); - - g_source_remove(e->source); - e->source = (guint) -1; - - e->callback(&e->mainloop->api, e, &e->timeval, e->userdata); - return FALSE; -} - -static void glib_time_restart(pa_time_event*e, const struct timeval *tv) { - struct timeval now; - assert(e && e->mainloop && !e->dead); - - pa_gettimeofday(&now); - if (e->source != (guint) -1) - g_source_remove(e->source); - - if (tv) { - e->timeval = *tv; - e->source = g_timeout_add_full(G_PRIORITY_DEFAULT, msec_diff(tv, &now), time_cb, e, NULL); - assert(e->source != (guint) -1); - } else - e->source = (guint) -1; - } - -static void glib_time_free(pa_time_event *e) { - assert(e && e->mainloop && !e->dead); - - if (e->source != (guint) -1) { - g_source_remove(e->source); - e->source = (guint) -1; - } - - if (e->prev) - e->prev->next = e->next; - else - e->mainloop->time_events = e->next; - - if (e->next) - e->next->prev = e->prev; - - if ((e->next = e->mainloop->dead_time_events)) - e->next->prev = e; - - e->mainloop->dead_time_events = e; - e->prev = NULL; - - e->dead = 1; - schedule_free_dead_events(e->mainloop); -} - -static void glib_time_set_destroy(pa_time_event *e, void (*callback)(pa_mainloop_api*m, pa_time_event*e, void *userdata)) { - assert(e); - e->destroy_callback = callback; -} - -/* Deferred sources */ - -static void glib_defer_enable(pa_defer_event *e, int b); - -static pa_defer_event* glib_defer_new(pa_mainloop_api*m, void (*callback) (pa_mainloop_api*m, pa_defer_event *e, void *userdata), void *userdata) { - pa_defer_event *e; - pa_glib_mainloop *g; - - assert(m && m->userdata && callback); - g = m->userdata; - - e = pa_xmalloc(sizeof(pa_defer_event)); - e->mainloop = g; - e->dead = 0; - e->callback = callback; - e->userdata = userdata; - e->destroy_callback = NULL; - e->source = (guint) -1; - - glib_defer_enable(e, 1); - - e->next = g->defer_events; - if (e->next) e->next->prev = e; - g->defer_events = e; - e->prev = NULL; - return e; -} - -static gboolean idle_cb(gpointer data) { - pa_defer_event* e = data; - assert(e && e->mainloop && e->source != (guint) -1); - - e->callback(&e->mainloop->api, e, e->userdata); - return TRUE; -} - -static void glib_defer_enable(pa_defer_event *e, int b) { - assert(e && e->mainloop); - - if (e->source != (guint) -1 && !b) { - g_source_remove(e->source); - e->source = (guint) -1; - } else if (e->source == (guint) -1 && b) { - e->source = g_idle_add_full(G_PRIORITY_HIGH, idle_cb, e, NULL); - assert(e->source != (guint) -1); - } -} - -static void glib_defer_free(pa_defer_event *e) { - assert(e && e->mainloop && !e->dead); - - if (e->source != (guint) -1) { - g_source_remove(e->source); - e->source = (guint) -1; - } - - if (e->prev) - e->prev->next = e->next; - else - e->mainloop->defer_events = e->next; - - if (e->next) - e->next->prev = e->prev; - - if ((e->next = e->mainloop->dead_defer_events)) - e->next->prev = e; - - e->mainloop->dead_defer_events = e; - e->prev = NULL; - - e->dead = 1; - schedule_free_dead_events(e->mainloop); -} - -static void glib_defer_set_destroy(pa_defer_event *e, void (*callback)(pa_mainloop_api *m, pa_defer_event *e, void *userdata)) { - assert(e); - e->destroy_callback = callback; -} - -/* quit() */ - -static void glib_quit(pa_mainloop_api*a, PA_GCC_UNUSED int retval) { - pa_glib_mainloop *g; - assert(a && a->userdata); - g = a->userdata; - - /* NOOP */ -} - -static const pa_mainloop_api vtable = { - .userdata = NULL, - - .io_new = glib_io_new, - .io_enable = glib_io_enable, - .io_free = glib_io_free, - .io_set_destroy= glib_io_set_destroy, - - .time_new = glib_time_new, - .time_restart = glib_time_restart, - .time_free = glib_time_free, - .time_set_destroy = glib_time_set_destroy, - - .defer_new = glib_defer_new, - .defer_enable = glib_defer_enable, - .defer_free = glib_defer_free, - .defer_set_destroy = glib_defer_set_destroy, - - .quit = glib_quit, -}; - -pa_glib_mainloop *pa_glib_mainloop_new(void) { - pa_glib_mainloop *g; - - g = pa_xmalloc(sizeof(pa_glib_mainloop)); - - g->api = vtable; - g->api.userdata = g; - - g->io_events = g->dead_io_events = NULL; - g->time_events = g->dead_time_events = NULL; - g->defer_events = g->dead_defer_events = NULL; - - g->cleanup_source = (guint) -1; - return g; -} - -static void free_io_events(pa_io_event *e) { - while (e) { - pa_io_event *r = e; - e = r->next; - - if (r->source != (guint) -1) - g_source_remove(r->source); - - if (r->io_channel) - g_io_channel_unref(r->io_channel); - - if (r->destroy_callback) - r->destroy_callback(&r->mainloop->api, r, r->userdata); - - pa_xfree(r); - } -} - -static void free_time_events(pa_time_event *e) { - while (e) { - pa_time_event *r = e; - e = r->next; - - if (r->source != (guint) -1) - g_source_remove(r->source); - - if (r->destroy_callback) - r->destroy_callback(&r->mainloop->api, r, r->userdata); - - pa_xfree(r); - } -} - -static void free_defer_events(pa_defer_event *e) { - while (e) { - pa_defer_event *r = e; - e = r->next; - - if (r->source != (guint) -1) - g_source_remove(r->source); - - if (r->destroy_callback) - r->destroy_callback(&r->mainloop->api, r, r->userdata); - - pa_xfree(r); - } -} - -void pa_glib_mainloop_free(pa_glib_mainloop* g) { - assert(g); - - free_io_events(g->io_events); - free_io_events(g->dead_io_events); - free_defer_events(g->defer_events); - free_defer_events(g->dead_defer_events); - free_time_events(g->time_events); - free_time_events(g->dead_time_events); - - if (g->cleanup_source != (guint) -1) - g_source_remove(g->cleanup_source); - - pa_xfree(g); -} - -pa_mainloop_api* pa_glib_mainloop_get_api(pa_glib_mainloop *g) { - assert(g); - return &g->api; -} - -static gboolean free_dead_events(gpointer p) { - pa_glib_mainloop *g = p; - assert(g); - - free_io_events(g->dead_io_events); - free_defer_events(g->dead_defer_events); - free_time_events(g->dead_time_events); - - g->dead_io_events = NULL; - g->dead_defer_events = NULL; - g->dead_time_events = NULL; - - g_source_remove(g->cleanup_source); - g->cleanup_source = (guint) -1; - - return FALSE; -} - -static void schedule_free_dead_events(pa_glib_mainloop *g) { - assert(g); - - if (g->cleanup_source != (guint) -1) - return; - - g->cleanup_source = g_idle_add_full(G_PRIORITY_HIGH, free_dead_events, g, NULL); -} -- cgit From 9c87a65ce91c38b60c19ae108a51a2e8ce46a85c Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 19 Jul 2006 17:44:19 +0000 Subject: * add new --system command line parameter to the daemon for running PulseAudio as system-wide instance * add PA_ prefixes to all global #defines * modify auth-by-creds: define a new group "pulse-access" which is used for authentication * add proper privilige dropping when running in --system mode * create runtime directory once on startup and not by each module seperately git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@1105 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/client-conf.c | 19 ++++++++----------- src/pulse/client-conf.h | 2 +- src/pulse/context.c | 24 +++++++++++++++++++++--- 3 files changed, 30 insertions(+), 15 deletions(-) (limited to 'src/pulse') diff --git a/src/pulse/client-conf.c b/src/pulse/client-conf.c index 5cebcf46..28b4f2d1 100644 --- a/src/pulse/client-conf.c +++ b/src/pulse/client-conf.c @@ -39,21 +39,13 @@ #include "client-conf.h" -#ifndef DEFAULT_CONFIG_DIR -# ifndef OS_IS_WIN32 -# define DEFAULT_CONFIG_DIR "/etc/pulse" -# else -# define DEFAULT_CONFIG_DIR "%PULSE_ROOT%" -# endif -#endif - #ifndef OS_IS_WIN32 # define PATH_SEP "/" #else # define PATH_SEP "\\" #endif -#define DEFAULT_CLIENT_CONFIG_FILE DEFAULT_CONFIG_DIR PATH_SEP "client.conf" +#define DEFAULT_CLIENT_CONFIG_FILE PA_DEFAULT_CONFIG_DIR PATH_SEP "client.conf" #define DEFAULT_CLIENT_CONFIG_FILE_USER ".pulse" PATH_SEP "client.conf" #define ENV_CLIENT_CONFIG_FILE "PULSE_CLIENTCONFIG" @@ -71,15 +63,17 @@ static const pa_client_conf default_conf = { .default_server = NULL, .autospawn = 0, .cookie_file = NULL, - .cookie_valid = 0 + .cookie_valid = 0, + .access_group = NULL }; pa_client_conf *pa_client_conf_new(void) { pa_client_conf *c = pa_xmemdup(&default_conf, sizeof(default_conf)); - c->daemon_binary = pa_xstrdup(PULSEAUDIO_BINARY); + c->daemon_binary = pa_xstrdup(PA_BINARY); c->extra_arguments = pa_xstrdup("--log-target=syslog --exit-idle-time=5"); c->cookie_file = pa_xstrdup(PA_NATIVE_COOKIE_FILE); + c->access_group = pa_xstrdup(PA_ACCESS_GROUP); return c; } @@ -92,6 +86,7 @@ void pa_client_conf_free(pa_client_conf *c) { pa_xfree(c->default_source); pa_xfree(c->default_server); pa_xfree(c->cookie_file); + pa_xfree(c->access_group); pa_xfree(c); } int pa_client_conf_load(pa_client_conf *c, const char *filename) { @@ -108,6 +103,7 @@ int pa_client_conf_load(pa_client_conf *c, const char *filename) { { "default-server", pa_config_parse_string, NULL }, { "autospawn", pa_config_parse_bool, NULL }, { "cookie-file", pa_config_parse_string, NULL }, + { "access-group", pa_config_parse_string, NULL }, { NULL, NULL, NULL }, }; @@ -118,6 +114,7 @@ int pa_client_conf_load(pa_client_conf *c, const char *filename) { table[4].data = &c->default_server; table[5].data = &c->autospawn; table[6].data = &c->cookie_file; + table[7].data = &c->access_group; f = filename ? fopen((fn = pa_xstrdup(filename)), "r") : diff --git a/src/pulse/client-conf.h b/src/pulse/client-conf.h index a532f0df..dfb1148d 100644 --- a/src/pulse/client-conf.h +++ b/src/pulse/client-conf.h @@ -27,7 +27,7 @@ /* A structure containing configuration data for PulseAudio clients. */ typedef struct pa_client_conf { - char *daemon_binary, *extra_arguments, *default_sink, *default_source, *default_server, *cookie_file; + char *daemon_binary, *extra_arguments, *default_sink, *default_source, *default_server, *cookie_file, *access_group; int autospawn; uint8_t cookie[PA_NATIVE_COOKIE_LENGTH]; int cookie_valid; /* non-zero, when cookie is valid */ diff --git a/src/pulse/context.c b/src/pulse/context.c index 228053bc..a25e2f78 100644 --- a/src/pulse/context.c +++ b/src/pulse/context.c @@ -33,6 +33,8 @@ #include #include #include +#include +#include #ifdef HAVE_SYS_WAIT_H #include @@ -270,7 +272,7 @@ static void pstream_die_callback(pa_pstream *p, void *userdata) { pa_context_fail(c, PA_ERR_CONNECTIONTERMINATED); } -static void pstream_packet_callback(pa_pstream *p, pa_packet *packet, const void *creds, void *userdata) { +static void pstream_packet_callback(pa_pstream *p, pa_packet *packet, const struct ucred *creds, void *userdata) { pa_context *c = userdata; assert(p); @@ -420,7 +422,23 @@ static void setup_context(pa_context *c, pa_iochannel *io) { t = pa_tagstruct_command(c, PA_COMMAND_AUTH, &tag); pa_tagstruct_putu32(t, PA_PROTOCOL_VERSION); pa_tagstruct_put_arbitrary(t, c->conf->cookie, sizeof(c->conf->cookie)); - pa_pstream_send_tagstruct_with_creds(c->pstream, t, 1); + +#ifdef SCM_CREDENTIALS +{ + struct ucred ucred; + + ucred.pid = getpid(); + ucred.uid = getuid(); + + if ((ucred.gid = pa_get_gid_of_group(PA_ACCESS_GROUP)) == (gid_t) -1) + ucred.gid = getgid(); + + pa_pstream_send_tagstruct_with_creds(c->pstream, t, &ucred); +} +#else + pa_pstream_send_tagstruct_with_creds(c->pstream, t, NULL); +#endif + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, setup_complete_callback, c, NULL); pa_context_set_state(c, PA_CONTEXT_AUTHORIZING); @@ -680,7 +698,7 @@ int pa_context_connect( char lf[PATH_MAX]; pa_runtime_path(AUTOSPAWN_LOCK, lf, sizeof(lf)); - pa_make_secure_parent_dir(lf); + pa_make_secure_parent_dir(lf, 0700, getuid(), getgid()); assert(c->autospawn_lock_fd <= 0); c->autospawn_lock_fd = pa_lock_lockfile(lf); -- cgit From 2b31a900d67506592aecd9c0d59ad997d7977deb Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 19 Jul 2006 18:11:12 +0000 Subject: update @@ tokens according to recent Makefile.am change git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@1107 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/client.conf.in | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'src/pulse') diff --git a/src/pulse/client.conf.in b/src/pulse/client.conf.in index 21786ebf..3e008766 100644 --- a/src/pulse/client.conf.in +++ b/src/pulse/client.conf.in @@ -21,7 +21,7 @@ ## commented out. Use either ; or # for commenting ## Path to the pulseaudio daemon to run when autospawning. -; daemon-binary = @PULSEAUDIO_BINARY@ +; daemon-binary = @PA_BINARY@ ## Extra arguments to pass to the pulseaudio daemon ; extra-arguments = --log-target=syslog --exit-idle-time=5 @@ -37,3 +37,9 @@ ## Autospawn daemons? ; autospawn = 0 + +### Cookie file +; cookie-file = + +### Access group +; access-group = -- cgit From 340803b30c154ead29795454416592ff9d0e0df2 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 19 Jul 2006 18:14:14 +0000 Subject: use access group dedclared in ~/.pulse/client.conf instead of PA_ACCESS_GROUP git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@1108 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/context.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/pulse') diff --git a/src/pulse/context.c b/src/pulse/context.c index a25e2f78..f6452d4e 100644 --- a/src/pulse/context.c +++ b/src/pulse/context.c @@ -430,7 +430,7 @@ static void setup_context(pa_context *c, pa_iochannel *io) { ucred.pid = getpid(); ucred.uid = getuid(); - if ((ucred.gid = pa_get_gid_of_group(PA_ACCESS_GROUP)) == (gid_t) -1) + if ((ucred.gid = pa_get_gid_of_group(c->conf->access_group)) == (gid_t) -1) ucred.gid = getgid(); pa_pstream_send_tagstruct_with_creds(c->pstream, t, &ucred); -- cgit From a382492204ad3588c0c837e120e5bc31578df72a Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 19 Jul 2006 21:48:35 +0000 Subject: * add new function pa_check_in_group() * abstract credential APis a little bit by introducing HAVE_CREDS and a structure pa_creds * rework credential authentication * fix module-volume-restore and friends for usage in system-wide instance * remove loopback= argument from moulde-*-protocol-tcp since it is a superset of listen= and usually a bad idea anyway since the user shouldn't load the TCP module at all if he doesn't want remote access * rename a few variables in the jack modules to make sure they don't conflict with symbols defined in the system headers * add server address for system-wide daemons to the default server list for the the client libs * update todo git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@1109 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/client-conf.c | 2 +- src/pulse/context.c | 22 +++++++++++++++------- 2 files changed, 16 insertions(+), 8 deletions(-) (limited to 'src/pulse') diff --git a/src/pulse/client-conf.c b/src/pulse/client-conf.c index 28b4f2d1..21917597 100644 --- a/src/pulse/client-conf.c +++ b/src/pulse/client-conf.c @@ -46,7 +46,7 @@ #endif #define DEFAULT_CLIENT_CONFIG_FILE PA_DEFAULT_CONFIG_DIR PATH_SEP "client.conf" -#define DEFAULT_CLIENT_CONFIG_FILE_USER ".pulse" PATH_SEP "client.conf" +#define DEFAULT_CLIENT_CONFIG_FILE_USER "client.conf" #define ENV_CLIENT_CONFIG_FILE "PULSE_CLIENTCONFIG" #define ENV_DEFAULT_SINK "PULSE_SINK" diff --git a/src/pulse/context.c b/src/pulse/context.c index f6452d4e..0150204c 100644 --- a/src/pulse/context.c +++ b/src/pulse/context.c @@ -62,6 +62,7 @@ #include #include #include +#include #include "internal.h" @@ -272,7 +273,7 @@ static void pstream_die_callback(pa_pstream *p, void *userdata) { pa_context_fail(c, PA_ERR_CONNECTIONTERMINATED); } -static void pstream_packet_callback(pa_pstream *p, pa_packet *packet, const struct ucred *creds, void *userdata) { +static void pstream_packet_callback(pa_pstream *p, pa_packet *packet, const pa_creds *creds, void *userdata) { pa_context *c = userdata; assert(p); @@ -423,15 +424,17 @@ static void setup_context(pa_context *c, pa_iochannel *io) { pa_tagstruct_putu32(t, PA_PROTOCOL_VERSION); pa_tagstruct_put_arbitrary(t, c->conf->cookie, sizeof(c->conf->cookie)); -#ifdef SCM_CREDENTIALS +#ifdef HAVE_CREDS { - struct ucred ucred; + pa_creds ucred; + gid_t g; - ucred.pid = getpid(); ucred.uid = getuid(); + ucred.gid = getgid(); - if ((ucred.gid = pa_get_gid_of_group(c->conf->access_group)) == (gid_t) -1) - ucred.gid = getgid(); + if ((g = pa_get_gid_of_group(c->conf->access_group)) != (gid_t) -1) + if (pa_check_in_group(g) > 0) + ucred.gid = g; pa_pstream_send_tagstruct_with_creds(c->pstream, t, &ucred); } @@ -690,7 +693,12 @@ int pa_context_connect( } c->server_list = pa_strlist_prepend(c->server_list, "tcp6:localhost"); - c->server_list = pa_strlist_prepend(c->server_list, "localhost"); + c->server_list = pa_strlist_prepend(c->server_list, "tcp4:localhost"); + + /* The system wide instance */ + c->server_list = pa_strlist_prepend(c->server_list, PA_SYSTEM_RUNTIME_PATH "/" PA_NATIVE_DEFAULT_UNIX_SOCKET); + + /* The per-user instance */ c->server_list = pa_strlist_prepend(c->server_list, pa_runtime_path(PA_NATIVE_DEFAULT_UNIX_SOCKET, ufn, sizeof(ufn))); /* Wrap the connection attempts in a single transaction for sane autospawn locking */ -- cgit From 7ba93ebae21742522bfe430e229a859370a888d1 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Thu, 20 Jul 2006 00:13:12 +0000 Subject: Protect platform dependent headers with ifdefs. git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@1112 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/context.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src/pulse') diff --git a/src/pulse/context.c b/src/pulse/context.c index 0150204c..efc1685b 100644 --- a/src/pulse/context.c +++ b/src/pulse/context.c @@ -33,8 +33,6 @@ #include #include #include -#include -#include #ifdef HAVE_SYS_WAIT_H #include @@ -43,6 +41,9 @@ #ifdef HAVE_SYS_SOCKET_H #include #endif +#ifdef HAVE_SYS_UN_H +#include +#endif #ifdef HAVE_NETDB_H #include #endif -- cgit From a3e7595ac179ca32bc5c876b25a4e80171c3d917 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Thu, 20 Jul 2006 00:21:50 +0000 Subject: Make -1 mean "current group/user" so that some platform dependent calls can be centralised. git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@1113 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/context.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/pulse') diff --git a/src/pulse/context.c b/src/pulse/context.c index efc1685b..30a257fe 100644 --- a/src/pulse/context.c +++ b/src/pulse/context.c @@ -707,7 +707,7 @@ int pa_context_connect( char lf[PATH_MAX]; pa_runtime_path(AUTOSPAWN_LOCK, lf, sizeof(lf)); - pa_make_secure_parent_dir(lf, 0700, getuid(), getgid()); + pa_make_secure_parent_dir(lf, 0700, (uid_t)-1, (gid_t)-1); assert(c->autospawn_lock_fd <= 0); c->autospawn_lock_fd = pa_lock_lockfile(lf); -- cgit From da1ec271bbc1907c32811cd61f41390a7d3ac1e8 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 20 Jul 2006 21:28:44 +0000 Subject: remove configurable client access group, since can never work on Linux anway, since SCM_CREDENTAILS doesn't allow sending supplementary GIDs git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@1127 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/client-conf.c | 5 ----- src/pulse/client-conf.h | 2 +- src/pulse/context.c | 5 ----- 3 files changed, 1 insertion(+), 11 deletions(-) (limited to 'src/pulse') diff --git a/src/pulse/client-conf.c b/src/pulse/client-conf.c index 21917597..c3f58ec2 100644 --- a/src/pulse/client-conf.c +++ b/src/pulse/client-conf.c @@ -64,7 +64,6 @@ static const pa_client_conf default_conf = { .autospawn = 0, .cookie_file = NULL, .cookie_valid = 0, - .access_group = NULL }; pa_client_conf *pa_client_conf_new(void) { @@ -73,7 +72,6 @@ pa_client_conf *pa_client_conf_new(void) { c->daemon_binary = pa_xstrdup(PA_BINARY); c->extra_arguments = pa_xstrdup("--log-target=syslog --exit-idle-time=5"); c->cookie_file = pa_xstrdup(PA_NATIVE_COOKIE_FILE); - c->access_group = pa_xstrdup(PA_ACCESS_GROUP); return c; } @@ -86,7 +84,6 @@ void pa_client_conf_free(pa_client_conf *c) { pa_xfree(c->default_source); pa_xfree(c->default_server); pa_xfree(c->cookie_file); - pa_xfree(c->access_group); pa_xfree(c); } int pa_client_conf_load(pa_client_conf *c, const char *filename) { @@ -103,7 +100,6 @@ int pa_client_conf_load(pa_client_conf *c, const char *filename) { { "default-server", pa_config_parse_string, NULL }, { "autospawn", pa_config_parse_bool, NULL }, { "cookie-file", pa_config_parse_string, NULL }, - { "access-group", pa_config_parse_string, NULL }, { NULL, NULL, NULL }, }; @@ -114,7 +110,6 @@ int pa_client_conf_load(pa_client_conf *c, const char *filename) { table[4].data = &c->default_server; table[5].data = &c->autospawn; table[6].data = &c->cookie_file; - table[7].data = &c->access_group; f = filename ? fopen((fn = pa_xstrdup(filename)), "r") : diff --git a/src/pulse/client-conf.h b/src/pulse/client-conf.h index dfb1148d..a532f0df 100644 --- a/src/pulse/client-conf.h +++ b/src/pulse/client-conf.h @@ -27,7 +27,7 @@ /* A structure containing configuration data for PulseAudio clients. */ typedef struct pa_client_conf { - char *daemon_binary, *extra_arguments, *default_sink, *default_source, *default_server, *cookie_file, *access_group; + char *daemon_binary, *extra_arguments, *default_sink, *default_source, *default_server, *cookie_file; int autospawn; uint8_t cookie[PA_NATIVE_COOKIE_LENGTH]; int cookie_valid; /* non-zero, when cookie is valid */ diff --git a/src/pulse/context.c b/src/pulse/context.c index 30a257fe..34f517f0 100644 --- a/src/pulse/context.c +++ b/src/pulse/context.c @@ -428,14 +428,9 @@ static void setup_context(pa_context *c, pa_iochannel *io) { #ifdef HAVE_CREDS { pa_creds ucred; - gid_t g; ucred.uid = getuid(); ucred.gid = getgid(); - - if ((g = pa_get_gid_of_group(c->conf->access_group)) != (gid_t) -1) - if (pa_check_in_group(g) > 0) - ucred.gid = g; pa_pstream_send_tagstruct_with_creds(c->pstream, t, &ucred); } -- cgit From 40b408990ae4d52df33a88508b02954a67b6841b Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 20 Jul 2006 23:12:18 +0000 Subject: remove access group setting from default client.conf git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@1133 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/client.conf.in | 3 --- 1 file changed, 3 deletions(-) (limited to 'src/pulse') diff --git a/src/pulse/client.conf.in b/src/pulse/client.conf.in index 3e008766..c970be56 100644 --- a/src/pulse/client.conf.in +++ b/src/pulse/client.conf.in @@ -40,6 +40,3 @@ ### Cookie file ; cookie-file = - -### Access group -; access-group = -- cgit From 07a1c45cc1bd647d9a9c579a0c2bffd4c3d5fc1b Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 22 Jul 2006 00:54:23 +0000 Subject: fix horribly broken glib timeout event handling git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@1138 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/glib-mainloop.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) (limited to 'src/pulse') diff --git a/src/pulse/glib-mainloop.c b/src/pulse/glib-mainloop.c index cdaecdf8..77f93450 100644 --- a/src/pulse/glib-mainloop.c +++ b/src/pulse/glib-mainloop.c @@ -312,7 +312,7 @@ static void glib_time_restart(pa_time_event*e, const struct timeval *tv) { assert(e); assert(!e->dead); - if (e->enabled && !!tv) + if (e->enabled && !tv) e->mainloop->n_enabled_time_events--; else if (!e->enabled && tv) e->mainloop->n_enabled_time_events++; @@ -476,13 +476,11 @@ static gboolean prepare_func(GSource *source, gint *timeout) { tvnow.tv_sec = now.tv_sec; tvnow.tv_usec = now.tv_usec; - usec = pa_timeval_diff(&t->timeval, &tvnow); - - if (usec <= 0) { + if (pa_timeval_cmp(&t->timeval, &tvnow) <= 0) { *timeout = 0; return TRUE; - } - + } + usec = pa_timeval_diff(&t->timeval, &tvnow); *timeout = (gint) (usec / 1000); } else *timeout = -1; @@ -554,7 +552,7 @@ static gboolean dispatch_func(GSource *source, PA_GCC_UNUSED GSourceFunc callbac tvnow.tv_sec = now.tv_sec; tvnow.tv_usec = now.tv_usec; - if (pa_timeval_cmp(&t->timeval, &tvnow) < 0) { + if (pa_timeval_cmp(&t->timeval, &tvnow) <= 0) { t->callback(&g->api, t, &t->timeval, t->userdata); return TRUE; } -- cgit From c41d7498d3edc66c05297874cd106adb3961e8a4 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 25 Jul 2006 20:09:22 +0000 Subject: add a few more g_assert()s and change all assert()s to g_assert()s git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@1145 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/glib-mainloop.c | 125 +++++++++++++++++++++++++++------------------- 1 file changed, 73 insertions(+), 52 deletions(-) (limited to 'src/pulse') diff --git a/src/pulse/glib-mainloop.c b/src/pulse/glib-mainloop.c index 77f93450..76767552 100644 --- a/src/pulse/glib-mainloop.c +++ b/src/pulse/glib-mainloop.c @@ -106,8 +106,10 @@ static void cleanup_io_events(pa_glib_mainloop *g, int force) { if (force || e->dead) { PA_LLIST_REMOVE(pa_io_event, g->io_events, e); - if (e->dead) + if (e->dead) { + g_assert(g->io_events_please_scan > 0); g->io_events_please_scan--; + } if (e->poll_fd_added) g_source_remove_poll(&g->source, &e->poll_fd); @@ -121,7 +123,7 @@ static void cleanup_io_events(pa_glib_mainloop *g, int force) { e = n; } - assert(g->io_events_please_scan == 0); + g_assert(g->io_events_please_scan == 0); } static void cleanup_time_events(pa_glib_mainloop *g, int force) { @@ -137,11 +139,15 @@ static void cleanup_time_events(pa_glib_mainloop *g, int force) { if (force || e->dead) { PA_LLIST_REMOVE(pa_time_event, g->time_events, e); - if (e->dead) + if (e->dead) { + g_assert(g->time_events_please_scan > 0); g->time_events_please_scan--; + } - if (!e->dead && e->enabled) + if (!e->dead && e->enabled) { + g_assert(g->n_enabled_time_events > 0); g->n_enabled_time_events--; + } if (e->destroy_callback) e->destroy_callback(&g->api, e, e->userdata); @@ -152,7 +158,7 @@ static void cleanup_time_events(pa_glib_mainloop *g, int force) { e = n; } - assert(g->time_events_please_scan == 0); + g_assert(g->time_events_please_scan == 0); } static void cleanup_defer_events(pa_glib_mainloop *g, int force) { @@ -168,11 +174,15 @@ static void cleanup_defer_events(pa_glib_mainloop *g, int force) { if (force || e->dead) { PA_LLIST_REMOVE(pa_defer_event, g->defer_events, e); - if (e->dead) + if (e->dead) { + g_assert(g->defer_events_please_scan > 0); g->defer_events_please_scan--; + } - if (!e->dead && e->enabled) + if (!e->dead && e->enabled) { + g_assert(g->n_enabled_defer_events > 0); g->n_enabled_defer_events--; + } if (e->destroy_callback) e->destroy_callback(&g->api, e, e->userdata); @@ -183,7 +193,7 @@ static void cleanup_defer_events(pa_glib_mainloop *g, int force) { e = n; } - assert(g->defer_events_please_scan == 0); + g_assert(g->defer_events_please_scan == 0); } static gushort map_flags_to_glib(pa_io_event_flags_t flags) { @@ -212,10 +222,10 @@ static pa_io_event* glib_io_new( pa_io_event *e; pa_glib_mainloop *g; - assert(m); - assert(m->userdata); - assert(fd >= 0); - assert(cb); + g_assert(m); + g_assert(m->userdata); + g_assert(fd >= 0); + g_assert(cb); g = m->userdata; @@ -240,15 +250,15 @@ static pa_io_event* glib_io_new( } static void glib_io_enable(pa_io_event*e, pa_io_event_flags_t f) { - assert(e); - assert(!e->dead); + g_assert(e); + g_assert(!e->dead); e->poll_fd.events = map_flags_to_glib(f); } static void glib_io_free(pa_io_event*e) { - assert(e); - assert(!e->dead); + g_assert(e); + g_assert(!e->dead); e->dead = 1; e->mainloop->io_events_please_scan++; @@ -260,8 +270,8 @@ static void glib_io_free(pa_io_event*e) { } static void glib_io_set_destroy(pa_io_event*e, pa_io_event_destroy_cb_t cb) { - assert(e); - assert(!e->dead); + g_assert(e); + g_assert(!e->dead); e->destroy_callback = cb; } @@ -277,9 +287,9 @@ static pa_time_event* glib_time_new( pa_glib_mainloop *g; pa_time_event *e; - assert(m); - assert(m->userdata); - assert(cb); + g_assert(m); + g_assert(m->userdata); + g_assert(cb); g = m->userdata; @@ -309,20 +319,21 @@ static pa_time_event* glib_time_new( } static void glib_time_restart(pa_time_event*e, const struct timeval *tv) { - assert(e); - assert(!e->dead); + g_assert(e); + g_assert(!e->dead); - if (e->enabled && !tv) + if (e->enabled && !tv) { + g_assert(e->mainloop->n_enabled_time_events > 0); e->mainloop->n_enabled_time_events--; - else if (!e->enabled && tv) + } else if (!e->enabled && tv) e->mainloop->n_enabled_time_events++; - if ((e->enabled = !!tv)) + if ((e->enabled = !!tv)) e->timeval = *tv; if (e->mainloop->cached_next_time_event && e->enabled) { g_assert(e->mainloop->cached_next_time_event->enabled); - + if (pa_timeval_cmp(tv, &e->mainloop->cached_next_time_event->timeval) < 0) e->mainloop->cached_next_time_event = e; } else if (e->mainloop->cached_next_time_event == e) @@ -330,8 +341,8 @@ static void glib_time_restart(pa_time_event*e, const struct timeval *tv) { } static void glib_time_free(pa_time_event *e) { - assert(e); - assert(!e->dead); + g_assert(e); + g_assert(!e->dead); e->dead = 1; e->mainloop->time_events_please_scan++; @@ -344,8 +355,8 @@ static void glib_time_free(pa_time_event *e) { } static void glib_time_set_destroy(pa_time_event *e, pa_time_event_destroy_cb_t cb) { - assert(e); - assert(!e->dead); + g_assert(e); + g_assert(!e->dead); e->destroy_callback = cb; } @@ -360,9 +371,9 @@ static pa_defer_event* glib_defer_new( pa_defer_event *e; pa_glib_mainloop *g; - assert(m); - assert(m->userdata); - assert(cb); + g_assert(m); + g_assert(m->userdata); + g_assert(cb); g = m->userdata; @@ -382,31 +393,34 @@ static pa_defer_event* glib_defer_new( } static void glib_defer_enable(pa_defer_event *e, int b) { - assert(e); - assert(!e->dead); + g_assert(e); + g_assert(!e->dead); - if (e->enabled && !b) + if (e->enabled && !b) { + g_assert(e->mainloop->n_enabled_defer_events > 0); e->mainloop->n_enabled_defer_events--; - else if (!e->enabled && b) + } else if (!e->enabled && b) e->mainloop->n_enabled_defer_events++; e->enabled = b; } static void glib_defer_free(pa_defer_event *e) { - assert(e); - assert(!e->dead); + g_assert(e); + g_assert(!e->dead); e->dead = 1; e->mainloop->defer_events_please_scan++; - if (e->enabled) + if (e->enabled) { + g_assert(e->mainloop->n_enabled_defer_events > 0); e->mainloop->n_enabled_defer_events--; + } } static void glib_defer_set_destroy(pa_defer_event *e, pa_defer_event_destroy_cb_t cb) { - assert(e); - assert(!e->dead); + g_assert(e); + g_assert(!e->dead); e->destroy_callback = cb; } @@ -422,7 +436,7 @@ static void glib_quit(pa_mainloop_api*a, PA_GCC_UNUSED int retval) { static pa_time_event* find_next_time_event(pa_glib_mainloop *g) { pa_time_event *t, *n = NULL; - assert(g); + g_assert(g); if (g->cached_next_time_event) return g->cached_next_time_event; @@ -445,12 +459,9 @@ static pa_time_event* find_next_time_event(pa_glib_mainloop *g) { return n; } -static gboolean prepare_func(GSource *source, gint *timeout) { - pa_glib_mainloop *g = (pa_glib_mainloop*) source; - +static void scan_dead(pa_glib_mainloop *g) { g_assert(g); - g_assert(timeout); - + if (g->io_events_please_scan) cleanup_io_events(g, 0); @@ -459,6 +470,15 @@ static gboolean prepare_func(GSource *source, gint *timeout) { if (g->defer_events_please_scan) cleanup_defer_events(g, 0); +} + +static gboolean prepare_func(GSource *source, gint *timeout) { + pa_glib_mainloop *g = (pa_glib_mainloop*) source; + + g_assert(g); + g_assert(timeout); + + scan_dead(g); if (g->n_enabled_defer_events) { *timeout = 0; @@ -534,7 +554,7 @@ static gboolean dispatch_func(GSource *source, PA_GCC_UNUSED GSourceFunc callbac break; } - assert(d); + g_assert(d); d->callback(&g->api, d, d->userdata); return TRUE; @@ -553,6 +573,7 @@ static gboolean dispatch_func(GSource *source, PA_GCC_UNUSED GSourceFunc callbac tvnow.tv_usec = now.tv_usec; if (pa_timeval_cmp(&t->timeval, &tvnow) <= 0) { + t->enabled = 0; t->callback(&g->api, t, &t->timeval, t->userdata); return TRUE; } @@ -621,7 +642,7 @@ pa_glib_mainloop *pa_glib_mainloop_new(GMainContext *c) { } void pa_glib_mainloop_free(pa_glib_mainloop* g) { - assert(g); + g_assert(g); cleanup_io_events(g, 1); cleanup_defer_events(g, 1); @@ -633,7 +654,7 @@ void pa_glib_mainloop_free(pa_glib_mainloop* g) { } pa_mainloop_api* pa_glib_mainloop_get_api(pa_glib_mainloop *g) { - assert(g); + g_assert(g); return &g->api; } -- cgit From 563fab983f065fdad14696ce3f13d3df6a2b0370 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 25 Jul 2006 20:39:34 +0000 Subject: Results of profiling PulseAudio with valgrind's callgrind module: rework the default event loop implementation to use PA_LLIST_xxx instead of pa_idxset; don't generate weakeup events if we aren't in STATE_POLLING; minimize dispatching of io events; cache next time event instead of traversing the list of time events on every event loop iteration; other optimizations git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@1148 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/mainloop.c | 547 +++++++++++++++++++++++++++++++++------------------ 1 file changed, 353 insertions(+), 194 deletions(-) (limited to 'src/pulse') diff --git a/src/pulse/mainloop.c b/src/pulse/mainloop.c index dfbc337b..682b2ccd 100644 --- a/src/pulse/mainloop.c +++ b/src/pulse/mainloop.c @@ -49,7 +49,7 @@ #include #include -#include +#include #include #include "mainloop.h" @@ -57,50 +57,66 @@ struct pa_io_event { pa_mainloop *mainloop; int dead; + int fd; pa_io_event_flags_t events; - void (*callback) (pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata); struct pollfd *pollfd; + + pa_io_event_cb_t callback; void *userdata; - void (*destroy_callback) (pa_mainloop_api*a, pa_io_event *e, void *userdata); + pa_io_event_destroy_cb_t destroy_callback; + + PA_LLIST_FIELDS(pa_io_event); }; struct pa_time_event { pa_mainloop *mainloop; int dead; + int enabled; struct timeval timeval; - void (*callback)(pa_mainloop_api*a, pa_time_event *e, const struct timeval*tv, void *userdata); + + pa_time_event_cb_t callback; void *userdata; - void (*destroy_callback) (pa_mainloop_api*a, pa_time_event *e, void *userdata); + pa_time_event_destroy_cb_t destroy_callback; + + PA_LLIST_FIELDS(pa_time_event); }; struct pa_defer_event { pa_mainloop *mainloop; int dead; + int enabled; - void (*callback)(pa_mainloop_api*a, pa_defer_event*e, void *userdata); + + pa_defer_event_cb_t callback; void *userdata; - void (*destroy_callback) (pa_mainloop_api*a, pa_defer_event *e, void *userdata); + pa_defer_event_destroy_cb_t destroy_callback; + + PA_LLIST_FIELDS(pa_defer_event); }; struct pa_mainloop { - pa_idxset *io_events, *time_events, *defer_events; - int io_events_scan_dead, defer_events_scan_dead, time_events_scan_dead; + PA_LLIST_HEAD(pa_io_event, io_events); + PA_LLIST_HEAD(pa_time_event, time_events); + PA_LLIST_HEAD(pa_defer_event, defer_events); + + int n_enabled_defer_events, n_enabled_time_events, n_io_events; + int io_events_please_scan, time_events_please_scan, defer_events_please_scan; struct pollfd *pollfds; unsigned max_pollfds, n_pollfds; int rebuild_pollfds; int prepared_timeout; + pa_time_event *cached_next_time_event; int quit, retval; pa_mainloop_api api; - int deferred_pending; - int wakeup_pipe[2]; int wakeup_pipe_type; + int wakeup_requested; enum { STATE_PASSIVE, @@ -112,33 +128,55 @@ struct pa_mainloop { pa_poll_func poll_func; void *poll_func_userdata; + int poll_func_ret; }; +static short map_flags_to_libc(pa_io_event_flags_t flags) { + return + (flags & PA_IO_EVENT_INPUT ? POLLIN : 0) | + (flags & PA_IO_EVENT_OUTPUT ? POLLOUT : 0) | + (flags & PA_IO_EVENT_ERROR ? POLLERR : 0) | + (flags & PA_IO_EVENT_HANGUP ? POLLHUP : 0); +} + +static pa_io_event_flags_t map_flags_from_libc(short flags) { + return + (flags & POLLIN ? PA_IO_EVENT_INPUT : 0) | + (flags & POLLOUT ? PA_IO_EVENT_OUTPUT : 0) | + (flags & POLLERR ? PA_IO_EVENT_ERROR : 0) | + (flags & POLLHUP ? PA_IO_EVENT_HANGUP : 0); +} + /* IO events */ static pa_io_event* mainloop_io_new( - pa_mainloop_api*a, - int fd, - pa_io_event_flags_t events, - void (*callback) (pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_event_flags_t events, void *userdata), - void *userdata) { + pa_mainloop_api*a, + int fd, + pa_io_event_flags_t events, + pa_io_event_cb_t callback, + void *userdata) { pa_mainloop *m; pa_io_event *e; - assert(a && a->userdata && fd >= 0 && callback); + assert(a); + assert(a->userdata); + assert(fd >= 0); + assert(callback); + m = a->userdata; assert(a == &m->api); - e = pa_xmalloc(sizeof(pa_io_event)); + e = pa_xnew(pa_io_event, 1); e->mainloop = m; e->dead = 0; e->fd = fd; e->events = events; + e->pollfd = NULL; + e->callback = callback; e->userdata = userdata; e->destroy_callback = NULL; - e->pollfd = NULL; #ifdef OS_IS_WIN32 { @@ -160,8 +198,9 @@ static pa_io_event* mainloop_io_new( } #endif - pa_idxset_put(m->io_events, e, NULL); + PA_LLIST_PREPEND(pa_io_event, m->io_events, e); m->rebuild_pollfds = 1; + m->n_io_events ++; pa_mainloop_wakeup(m); @@ -169,48 +208,69 @@ static pa_io_event* mainloop_io_new( } static void mainloop_io_enable(pa_io_event *e, pa_io_event_flags_t events) { - assert(e && e->mainloop); + assert(e); + assert(!e->dead); + if (e->events == events) + return; + e->events = events; - e->mainloop->rebuild_pollfds = 1; + + if (e->pollfd) + e->pollfd->events = map_flags_to_libc(events); + else + e->mainloop->rebuild_pollfds = 1; pa_mainloop_wakeup(e->mainloop); } static void mainloop_io_free(pa_io_event *e) { - assert(e && e->mainloop); + assert(e); + assert(!e->dead); - e->dead = e->mainloop->io_events_scan_dead = e->mainloop->rebuild_pollfds = 1; + e->dead = 1; + e->mainloop->io_events_please_scan ++; + + e->mainloop->n_io_events --; + e->mainloop->rebuild_pollfds = 1; pa_mainloop_wakeup(e->mainloop); } -static void mainloop_io_set_destroy(pa_io_event *e, void (*callback)(pa_mainloop_api*a, pa_io_event *e, void *userdata)) { +static void mainloop_io_set_destroy(pa_io_event *e, pa_io_event_destroy_cb_t callback) { assert(e); + e->destroy_callback = callback; } /* Defer events */ -static pa_defer_event* mainloop_defer_new(pa_mainloop_api*a, void (*callback) (pa_mainloop_api*a, pa_defer_event *e, void *userdata), void *userdata) { +static pa_defer_event* mainloop_defer_new( + pa_mainloop_api*a, + pa_defer_event_cb_t callback, + void *userdata) { + pa_mainloop *m; pa_defer_event *e; - assert(a && a->userdata && callback); + assert(a); + assert(a->userdata); + assert(callback); + m = a->userdata; assert(a == &m->api); - e = pa_xmalloc(sizeof(pa_defer_event)); + e = pa_xnew(pa_defer_event, 1); e->mainloop = m; e->dead = 0; e->enabled = 1; + m->n_enabled_defer_events++; + e->callback = callback; e->userdata = userdata; e->destroy_callback = NULL; - pa_idxset_put(m->defer_events, e, NULL); - - m->deferred_pending++; + PA_LLIST_PREPEND(pa_defer_event, m->defer_events, e); pa_mainloop_wakeup(e->mainloop); @@ -219,12 +279,13 @@ static pa_defer_event* mainloop_defer_new(pa_mainloop_api*a, void (*callback) (p static void mainloop_defer_enable(pa_defer_event *e, int b) { assert(e); + assert(!e->dead); if (e->enabled && !b) { - assert(e->mainloop->deferred_pending > 0); - e->mainloop->deferred_pending--; + assert(e->mainloop->n_enabled_defer_events > 0); + e->mainloop->n_enabled_defer_events--; } else if (!e->enabled && b) { - e->mainloop->deferred_pending++; + e->mainloop->n_enabled_defer_events++; pa_mainloop_wakeup(e->mainloop); } @@ -233,42 +294,63 @@ static void mainloop_defer_enable(pa_defer_event *e, int b) { static void mainloop_defer_free(pa_defer_event *e) { assert(e); - e->dead = e->mainloop->defer_events_scan_dead = 1; + assert(!e->dead); + + e->dead = 1; + e->mainloop->defer_events_please_scan ++; if (e->enabled) { - e->enabled = 0; - assert(e->mainloop->deferred_pending > 0); - e->mainloop->deferred_pending--; + assert(e->mainloop->n_enabled_defer_events > 0); + e->mainloop->n_enabled_defer_events--; } } -static void mainloop_defer_set_destroy(pa_defer_event *e, void (*callback)(pa_mainloop_api*a, pa_defer_event *e, void *userdata)) { +static void mainloop_defer_set_destroy(pa_defer_event *e, pa_defer_event_destroy_cb_t callback) { assert(e); + assert(!e->dead); + e->destroy_callback = callback; } /* Time events */ -static pa_time_event* mainloop_time_new(pa_mainloop_api*a, const struct timeval *tv, void (*callback) (pa_mainloop_api*a, pa_time_event*e, const struct timeval *tv, void *userdata), void *userdata) { +static pa_time_event* mainloop_time_new( + pa_mainloop_api*a, + const struct timeval *tv, + pa_time_event_cb_t callback, + void *userdata) { + pa_mainloop *m; pa_time_event *e; - assert(a && a->userdata && callback); + assert(a); + assert(a->userdata); + assert(callback); + m = a->userdata; assert(a == &m->api); - e = pa_xmalloc(sizeof(pa_time_event)); + e = pa_xnew(pa_time_event, 1); e->mainloop = m; e->dead = 0; - e->enabled = !!tv; - if (tv) + if ((e->enabled = !!tv)) { e->timeval = *tv; + m->n_enabled_time_events++; + + if (m->cached_next_time_event) { + assert(m->cached_next_time_event->enabled); + + if (pa_timeval_cmp(tv, &m->cached_next_time_event->timeval) < 0) + m->cached_next_time_event = e; + } + } + e->callback = callback; e->userdata = userdata; e->destroy_callback = NULL; - pa_idxset_put(m->time_events, e, NULL); + PA_LLIST_PREPEND(pa_time_event, m->time_events, e); if (e->enabled) pa_mainloop_wakeup(m); @@ -278,26 +360,50 @@ static pa_time_event* mainloop_time_new(pa_mainloop_api*a, const struct timeval static void mainloop_time_restart(pa_time_event *e, const struct timeval *tv) { assert(e); + assert(!e->dead); - if (tv) { - e->enabled = 1; - e->timeval = *tv; + if (e->enabled && !tv) { + assert(e->mainloop->n_enabled_time_events > 0); + e->mainloop->n_enabled_time_events--; + } else if (!e->enabled && tv) + e->mainloop->n_enabled_time_events++; + if ((e->enabled = !!tv)) { + e->timeval = *tv; pa_mainloop_wakeup(e->mainloop); - } else - e->enabled = 0; + } + + if (e->mainloop->cached_next_time_event && e->enabled) { + assert(e->mainloop->cached_next_time_event->enabled); + + if (pa_timeval_cmp(tv, &e->mainloop->cached_next_time_event->timeval) < 0) + e->mainloop->cached_next_time_event = e; + } else if (e->mainloop->cached_next_time_event == e) + e->mainloop->cached_next_time_event = NULL; } static void mainloop_time_free(pa_time_event *e) { assert(e); + assert(!e->dead); - e->dead = e->mainloop->time_events_scan_dead = 1; + e->dead = 1; + e->mainloop->time_events_please_scan ++; + if (e->enabled) { + assert(e->mainloop->n_enabled_time_events > 0); + e->mainloop->n_enabled_time_events--; + } + + if (e->mainloop->cached_next_time_event == e) + e->mainloop->cached_next_time_event = NULL; + /* no wakeup needed here. Think about it! */ } -static void mainloop_time_set_destroy(pa_time_event *e, void (*callback)(pa_mainloop_api*a, pa_time_event *e, void *userdata)) { +static void mainloop_time_set_destroy(pa_time_event *e, pa_time_event_destroy_cb_t callback) { assert(e); + assert(!e->dead); + e->destroy_callback = callback; } @@ -305,7 +411,9 @@ static void mainloop_time_set_destroy(pa_time_event *e, void (*callback)(pa_main static void mainloop_quit(pa_mainloop_api*a, int retval) { pa_mainloop *m; - assert(a && a->userdata); + + assert(a); + assert(a->userdata); m = a->userdata; assert(a == &m->api); @@ -336,7 +444,7 @@ static const pa_mainloop_api vtable = { pa_mainloop *pa_mainloop_new(void) { pa_mainloop *m; - m = pa_xmalloc(sizeof(pa_mainloop)); + m = pa_xnew(pa_mainloop, 1); m->wakeup_pipe_type = 0; if (pipe(m->wakeup_pipe) < 0) { @@ -347,14 +455,17 @@ pa_mainloop *pa_mainloop_new(void) { pa_make_nonblock_fd(m->wakeup_pipe[0]); pa_make_nonblock_fd(m->wakeup_pipe[1]); + m->wakeup_requested = 0; - m->io_events = pa_idxset_new(NULL, NULL); - m->defer_events = pa_idxset_new(NULL, NULL); - m->time_events = pa_idxset_new(NULL, NULL); + PA_LLIST_HEAD_INIT(pa_io_event, m->io_events); + PA_LLIST_HEAD_INIT(pa_time_event, m->time_events); + PA_LLIST_HEAD_INIT(pa_defer_event, m->defer_events); - assert(m->io_events && m->defer_events && m->time_events); + m->n_enabled_defer_events = m->n_enabled_time_events = m->n_io_events = 0; + m->io_events_please_scan = m->time_events_please_scan = m->defer_events_please_scan = 0; - m->io_events_scan_dead = m->defer_events_scan_dead = m->time_events_scan_dead = 0; + m->cached_next_time_event = NULL; + m->prepared_timeout = 0; m->pollfds = NULL; m->max_pollfds = m->n_pollfds = 0; @@ -365,74 +476,124 @@ pa_mainloop *pa_mainloop_new(void) { m->api = vtable; m->api.userdata = m; - m->deferred_pending = 0; - m->state = STATE_PASSIVE; m->poll_func = NULL; m->poll_func_userdata = NULL; - - m->retval = -1; + m->poll_func_ret = -1; return m; } -static int io_foreach(void *p, uint32_t PA_GCC_UNUSED idx, int *del, void*userdata) { - pa_io_event *e = p; - int *all = userdata; - assert(e && del && all); +static void cleanup_io_events(pa_mainloop *m, int force) { + pa_io_event *e; - if (!*all && !e->dead) - return 0; - - if (e->destroy_callback) - e->destroy_callback(&e->mainloop->api, e, e->userdata); - pa_xfree(e); - *del = 1; - return 0; + e = m->io_events; + while (e) { + pa_io_event *n = e->next; + + if (!force && m->io_events_please_scan <= 0) + break; + + if (force || e->dead) { + PA_LLIST_REMOVE(pa_io_event, m->io_events, e); + + if (e->dead) { + assert(m->io_events_please_scan > 0); + m->io_events_please_scan--; + } + + if (e->destroy_callback) + e->destroy_callback(&m->api, e, e->userdata); + + pa_xfree(e); + + m->rebuild_pollfds = 1; + } + + e = n; + } + + assert(m->io_events_please_scan == 0); } -static int time_foreach(void *p, uint32_t PA_GCC_UNUSED idx, int *del, void*userdata) { - pa_time_event *e = p; - int *all = userdata; - assert(e && del && all); +static void cleanup_time_events(pa_mainloop *m, int force) { + pa_time_event *e; - if (!*all && !e->dead) - return 0; - - if (e->destroy_callback) - e->destroy_callback(&e->mainloop->api, e, e->userdata); - pa_xfree(e); - *del = 1; - return 0; + e = m->time_events; + while (e) { + pa_time_event *n = e->next; + + if (!force && m->time_events_please_scan <= 0) + break; + + if (force || e->dead) { + PA_LLIST_REMOVE(pa_time_event, m->time_events, e); + + if (e->dead) { + assert(m->time_events_please_scan > 0); + m->time_events_please_scan--; + } + + if (!e->dead && e->enabled) { + assert(m->n_enabled_time_events > 0); + m->n_enabled_time_events--; + } + + if (e->destroy_callback) + e->destroy_callback(&m->api, e, e->userdata); + + pa_xfree(e); + } + + e = n; + } + + assert(m->time_events_please_scan == 0); } -static int defer_foreach(void *p, PA_GCC_UNUSED uint32_t idx, int *del, void*userdata) { - pa_defer_event *e = p; - int *all = userdata; - assert(e && del && all); +static void cleanup_defer_events(pa_mainloop *m, int force) { + pa_defer_event *e; - if (!*all && !e->dead) - return 0; - - if (e->destroy_callback) - e->destroy_callback(&e->mainloop->api, e, e->userdata); - pa_xfree(e); - *del = 1; - return 0; + e = m->defer_events; + while (e) { + pa_defer_event *n = e->next; + + if (!force && m->defer_events_please_scan <= 0) + break; + + if (force || e->dead) { + PA_LLIST_REMOVE(pa_defer_event, m->defer_events, e); + + if (e->dead) { + assert(m->defer_events_please_scan > 0); + m->defer_events_please_scan--; + } + + if (!e->dead && e->enabled) { + assert(m->n_enabled_defer_events > 0); + m->n_enabled_defer_events--; + } + + if (e->destroy_callback) + e->destroy_callback(&m->api, e, e->userdata); + + pa_xfree(e); + } + + e = n; + } + + assert(m->defer_events_please_scan == 0); } + void pa_mainloop_free(pa_mainloop* m) { - int all = 1; assert(m); - pa_idxset_foreach(m->io_events, io_foreach, &all); - pa_idxset_foreach(m->time_events, time_foreach, &all); - pa_idxset_foreach(m->defer_events, defer_foreach, &all); - - pa_idxset_free(m->io_events, NULL, NULL); - pa_idxset_free(m->time_events, NULL, NULL); - pa_idxset_free(m->defer_events, NULL, NULL); + cleanup_io_events(m, 1); + cleanup_defer_events(m, 1); + cleanup_time_events(m, 1); pa_xfree(m->pollfds); @@ -445,27 +606,26 @@ void pa_mainloop_free(pa_mainloop* m) { } static void scan_dead(pa_mainloop *m) { - int all = 0; assert(m); - if (m->io_events_scan_dead) - pa_idxset_foreach(m->io_events, io_foreach, &all); - if (m->time_events_scan_dead) - pa_idxset_foreach(m->time_events, time_foreach, &all); - if (m->defer_events_scan_dead) - pa_idxset_foreach(m->defer_events, defer_foreach, &all); + if (m->io_events_please_scan) + cleanup_io_events(m, 0); - m->io_events_scan_dead = m->time_events_scan_dead = m->defer_events_scan_dead = 0; + if (m->time_events_please_scan) + cleanup_time_events(m, 0); + + if (m->defer_events_please_scan) + cleanup_defer_events(m, 0); } static void rebuild_pollfds(pa_mainloop *m) { pa_io_event*e; struct pollfd *p; - uint32_t idx = PA_IDXSET_INVALID; unsigned l; - l = pa_idxset_size(m->io_events) + 1; + l = m->n_io_events + 1; if (m->max_pollfds < l) { + l *= 2; m->pollfds = pa_xrealloc(m->pollfds, sizeof(struct pollfd)*l); m->max_pollfds = l; } @@ -481,7 +641,7 @@ static void rebuild_pollfds(pa_mainloop *m) { m->n_pollfds++; } - for (e = pa_idxset_first(m->io_events, &idx); e; e = pa_idxset_next(m->io_events, &idx)) { + for (e = m->io_events; e; e = e->next) { if (e->dead) { e->pollfd = NULL; continue; @@ -489,11 +649,7 @@ static void rebuild_pollfds(pa_mainloop *m) { e->pollfd = p; p->fd = e->fd; - p->events = - ((e->events & PA_IO_EVENT_INPUT) ? POLLIN : 0) | - ((e->events & PA_IO_EVENT_OUTPUT) ? POLLOUT : 0) | - POLLHUP | - POLLERR; + p->events = map_flags_to_libc(e->events); p->revents = 0; p++; @@ -504,37 +660,34 @@ static void rebuild_pollfds(pa_mainloop *m) { } static int dispatch_pollfds(pa_mainloop *m) { - uint32_t idx = PA_IDXSET_INVALID; pa_io_event *e; - int r = 0; + int r = 0, k; - for (e = pa_idxset_first(m->io_events, &idx); e && !m->quit; e = pa_idxset_next(m->io_events, &idx)) { + assert(m->poll_func_ret > 0); + + for (e = m->io_events, k = m->poll_func_ret; e && !m->quit && k > 0; e = e->next) { if (e->dead || !e->pollfd || !e->pollfd->revents) continue; assert(e->pollfd->fd == e->fd && e->callback); - e->callback(&m->api, e, e->fd, - (e->pollfd->revents & POLLHUP ? PA_IO_EVENT_HANGUP : 0) | - (e->pollfd->revents & POLLIN ? PA_IO_EVENT_INPUT : 0) | - (e->pollfd->revents & POLLOUT ? PA_IO_EVENT_OUTPUT : 0) | - (e->pollfd->revents & POLLERR ? PA_IO_EVENT_ERROR : 0), - e->userdata); + e->callback(&m->api, e, e->fd, map_flags_from_libc(e->pollfd->revents), e->userdata); e->pollfd->revents = 0; r++; + + k--; } return r; } static int dispatch_defer(pa_mainloop *m) { - uint32_t idx; pa_defer_event *e; int r = 0; - if (!m->deferred_pending) + if (m->n_enabled_defer_events <= 0) return 0; - for (e = pa_idxset_first(m->defer_events, &idx); e && !m->quit; e = pa_idxset_next(m->defer_events, &idx)) { + for (e = m->defer_events; e && !m->quit; e = e->next) { if (e->dead || !e->enabled) continue; @@ -546,70 +699,71 @@ static int dispatch_defer(pa_mainloop *m) { return r; } -static int calc_next_timeout(pa_mainloop *m) { - uint32_t idx; - pa_time_event *e; - struct timeval now; - int t = -1; - int got_time = 0; +static pa_time_event* find_next_time_event(pa_mainloop *m) { + pa_time_event *t, *n = NULL; + assert(m); - if (pa_idxset_isempty(m->time_events)) - return -1; + if (m->cached_next_time_event) + return m->cached_next_time_event; + + for (t = m->time_events; t; t = t->next) { - for (e = pa_idxset_first(m->time_events, &idx); e; e = pa_idxset_next(m->time_events, &idx)) { - int tmp; - - if (e->dead || !e->enabled) + if (t->dead || !t->enabled) continue; - /* Let's save a system call */ - if (!got_time) { - pa_gettimeofday(&now); - got_time = 1; + if (!n || pa_timeval_cmp(&t->timeval, &n->timeval) < 0) { + n = t; + + /* Shortcut for tv = { 0, 0 } */ + if (n->timeval.tv_sec <= 0) + break; } + } - if (e->timeval.tv_sec < now.tv_sec || (e->timeval.tv_sec == now.tv_sec && e->timeval.tv_usec <= now.tv_usec)) - return 0; + m->cached_next_time_event = n; + return n; +} - tmp = (e->timeval.tv_sec - now.tv_sec)*1000; - - if (e->timeval.tv_usec > now.tv_usec) - tmp += (e->timeval.tv_usec - now.tv_usec)/1000; - else - tmp -= (now.tv_usec - e->timeval.tv_usec)/1000; +static int calc_next_timeout(pa_mainloop *m) { + pa_time_event *t; + struct timeval now; + pa_usec_t usec; - if (tmp == 0) - return 0; - else if (t == -1 || tmp < t) - t = tmp; - } + if (!m->n_enabled_time_events) + return -1; - return t; + t = find_next_time_event(m); + assert(t); + + if (t->timeval.tv_sec <= 0) + return 0; + + pa_gettimeofday(&now); + + if (pa_timeval_cmp(&t->timeval, &now) <= 0) + return 0; + + usec = pa_timeval_diff(&t->timeval, &now); + return (int) (usec / 1000); } static int dispatch_timeout(pa_mainloop *m) { - uint32_t idx; pa_time_event *e; struct timeval now; - int got_time = 0; int r = 0; assert(m); - if (pa_idxset_isempty(m->time_events)) + if (m->n_enabled_time_events <= 0) return 0; - for (e = pa_idxset_first(m->time_events, &idx); e && !m->quit; e = pa_idxset_next(m->time_events, &idx)) { + pa_gettimeofday(&now); + + for (e = m->time_events; e && !m->quit; e = e->next) { if (e->dead || !e->enabled) continue; - /* Let's save a system call */ - if (!got_time) { - pa_gettimeofday(&now); - got_time = 1; - } - - if (e->timeval.tv_sec < now.tv_sec || (e->timeval.tv_sec == now.tv_sec && e->timeval.tv_usec <= now.tv_usec)) { + if (pa_timeval_cmp(&e->timeval, &now) <= 0) { assert(e->callback); e->enabled = 0; @@ -626,8 +780,10 @@ void pa_mainloop_wakeup(pa_mainloop *m) { char c = 'W'; assert(m); - if (m->wakeup_pipe[1] >= 0) + if (m->wakeup_pipe[1] >= 0 && m->state == STATE_POLLING) { pa_write(m->wakeup_pipe[1], &c, sizeof(c), &m->wakeup_pipe_type); + m->wakeup_requested++; + } } static void clear_wakeup(pa_mainloop *m) { @@ -638,7 +794,10 @@ static void clear_wakeup(pa_mainloop *m) { if (m->wakeup_pipe[0] < 0) return; - while (pa_read(m->wakeup_pipe[0], &c, sizeof(c), &m->wakeup_pipe_type) == sizeof(c)); + if (m->wakeup_requested) { + while (pa_read(m->wakeup_pipe[0], &c, sizeof(c), &m->wakeup_pipe_type) == sizeof(c)); + m->wakeup_requested = 0; + } } int pa_mainloop_prepare(pa_mainloop *m, int timeout) { @@ -651,8 +810,7 @@ int pa_mainloop_prepare(pa_mainloop *m, int timeout) { if (m->quit) goto quit; - if (!m->deferred_pending) { - + if (m->n_enabled_defer_events <= 0) { if (m->rebuild_pollfds) rebuild_pollfds(m); @@ -670,8 +828,6 @@ quit: } int pa_mainloop_poll(pa_mainloop *m) { - int r; - assert(m); assert(m->state == STATE_PREPARED); @@ -680,24 +836,26 @@ int pa_mainloop_poll(pa_mainloop *m) { m->state = STATE_POLLING; - if (m->deferred_pending) - r = 0; + if (m->n_enabled_defer_events ) + m->poll_func_ret = 0; else { + assert(!m->rebuild_pollfds); + if (m->poll_func) - r = m->poll_func(m->pollfds, m->n_pollfds, m->prepared_timeout, m->poll_func_userdata); + m->poll_func_ret = m->poll_func(m->pollfds, m->n_pollfds, m->prepared_timeout, m->poll_func_userdata); else - r = poll(m->pollfds, m->n_pollfds, m->prepared_timeout); + m->poll_func_ret = poll(m->pollfds, m->n_pollfds, m->prepared_timeout); - if (r < 0) { + if (m->poll_func_ret < 0) { if (errno == EINTR) - r = 0; + m->poll_func_ret = 0; else pa_log(__FILE__": poll(): %s", pa_cstrerror(errno)); } } - m->state = r < 0 ? STATE_PASSIVE : STATE_POLLED; - return r; + m->state = m->poll_func_ret < 0 ? STATE_PASSIVE : STATE_POLLED; + return m->poll_func_ret; quit: m->state = STATE_QUIT; @@ -713,16 +871,17 @@ int pa_mainloop_dispatch(pa_mainloop *m) { if (m->quit) goto quit; - if (m->deferred_pending) + if (m->n_enabled_defer_events) dispatched += dispatch_defer(m); else { - dispatched += dispatch_timeout(m); + if (m->n_enabled_time_events) + dispatched += dispatch_timeout(m); if (m->quit) goto quit; - - dispatched += dispatch_pollfds(m); + if (m->poll_func_ret > 0) + dispatched += dispatch_pollfds(m); } if (m->quit) -- cgit From 0d7be3148e872d0d2a68ecd740a596a3bcf4db5a Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 26 Jul 2006 22:39:06 +0000 Subject: mainloop fixes: when disabling time events when dispatching them, make sure to adjust the cache time event and enabled time event counters git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@1153 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/glib-mainloop.c | 7 ++++++- src/pulse/mainloop.c | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) (limited to 'src/pulse') diff --git a/src/pulse/glib-mainloop.c b/src/pulse/glib-mainloop.c index 76767552..201b6e23 100644 --- a/src/pulse/glib-mainloop.c +++ b/src/pulse/glib-mainloop.c @@ -573,7 +573,10 @@ static gboolean dispatch_func(GSource *source, PA_GCC_UNUSED GSourceFunc callbac tvnow.tv_usec = now.tv_usec; if (pa_timeval_cmp(&t->timeval, &tvnow) <= 0) { - t->enabled = 0; + + /* Disable time event */ + glib_time_restart(t, NULL); + t->callback(&g->api, t, &t->timeval, t->userdata); return TRUE; } @@ -634,6 +637,8 @@ pa_glib_mainloop *pa_glib_mainloop_new(GMainContext *c) { g->n_enabled_defer_events = g->n_enabled_time_events = 0; g->io_events_please_scan = g->time_events_please_scan = g->defer_events_please_scan = 0; + + g->cached_next_time_event = NULL; g_source_attach(&g->source, g->context); g_source_set_can_recurse(&g->source, FALSE); diff --git a/src/pulse/mainloop.c b/src/pulse/mainloop.c index 682b2ccd..699c0ee7 100644 --- a/src/pulse/mainloop.c +++ b/src/pulse/mainloop.c @@ -766,7 +766,12 @@ static int dispatch_timeout(pa_mainloop *m) { if (pa_timeval_cmp(&e->timeval, &now) <= 0) { assert(e->callback); - e->enabled = 0; + /* Disable time event */ + mainloop_time_restart(e, NULL); + + if (m->cached_next_time_event == e) + m->cached_next_time_event = NULL; + e->callback(&m->api, e, &e->timeval, e->userdata); r++; -- cgit From 358e577403f081efa1e838df58617a86c1eaacb7 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 26 Jul 2006 22:39:56 +0000 Subject: remove two superfluous lines git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@1154 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/mainloop.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'src/pulse') diff --git a/src/pulse/mainloop.c b/src/pulse/mainloop.c index 699c0ee7..5aa72706 100644 --- a/src/pulse/mainloop.c +++ b/src/pulse/mainloop.c @@ -769,9 +769,6 @@ static int dispatch_timeout(pa_mainloop *m) { /* Disable time event */ mainloop_time_restart(e, NULL); - if (m->cached_next_time_event == e) - m->cached_next_time_event = NULL; - e->callback(&m->api, e, &e->timeval, e->userdata); r++; -- cgit From 6afb61efdc53be232b345b293ac0cd8943966beb Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 27 Jul 2006 16:49:44 +0000 Subject: remove superfluous code git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@1155 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/mainloop.c | 43 ------------------------------------------- 1 file changed, 43 deletions(-) (limited to 'src/pulse') diff --git a/src/pulse/mainloop.c b/src/pulse/mainloop.c index 5aa72706..5ebb9bc8 100644 --- a/src/pulse/mainloop.c +++ b/src/pulse/mainloop.c @@ -957,46 +957,3 @@ 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; } - - -#if 0 -void pa_mainloop_dump(pa_mainloop *m) { - assert(m); - - pa_log(__FILE__": Dumping mainloop sources START"); - - { - uint32_t idx = PA_IDXSET_INVALID; - pa_io_event *e; - for (e = pa_idxset_first(m->io_events, &idx); e; e = pa_idxset_next(m->io_events, &idx)) { - if (e->dead) - continue; - - pa_log(__FILE__": kind=io fd=%i events=%i callback=%p userdata=%p", e->fd, (int) e->events, (void*) e->callback, (void*) e->userdata); - } - } - { - uint32_t idx = PA_IDXSET_INVALID; - pa_defer_event *e; - for (e = pa_idxset_first(m->defer_events, &idx); e; e = pa_idxset_next(m->defer_events, &idx)) { - if (e->dead) - continue; - - pa_log(__FILE__": kind=defer enabled=%i callback=%p userdata=%p", e->enabled, (void*) e->callback, (void*) e->userdata); - } - } - { - uint32_t idx = PA_IDXSET_INVALID; - pa_time_event *e; - for (e = pa_idxset_first(m->time_events, &idx); e; e = pa_idxset_next(m->time_events, &idx)) { - if (e->dead) - continue; - - pa_log(__FILE__": kind=time enabled=%i time=%lu.%lu callback=%p userdata=%p", e->enabled, (unsigned long) e->timeval.tv_sec, (unsigned long) e->timeval.tv_usec, (void*) e->callback, (void*) e->userdata); - } - } - - pa_log(__FILE__": Dumping mainloop sources STOP"); - -} -#endif -- cgit From 9310a2e3b88286fdb60f52c4b8a8c51848ffca53 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 29 Jul 2006 15:02:53 +0000 Subject: fix calculation of pa_usec_to_bytes, to make sure that it never returns fractions of a frame size git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@1165 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/sample.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/pulse') diff --git a/src/pulse/sample.c b/src/pulse/sample.c index 2e055bf1..87b2d7a0 100644 --- a/src/pulse/sample.c +++ b/src/pulse/sample.c @@ -70,7 +70,7 @@ pa_usec_t pa_bytes_to_usec(uint64_t length, const pa_sample_spec *spec) { size_t pa_usec_to_bytes(pa_usec_t t, const pa_sample_spec *spec) { assert(spec); - return ((double) t * spec->rate / 1000000)*pa_frame_size(spec); + return (size_t) (((double) t * spec->rate / 1000000))*pa_frame_size(spec); } int pa_sample_spec_valid(const pa_sample_spec *spec) { -- cgit From 646deeaee69b2a3e5b3dedbf6bf4cccdd9255957 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 29 Jul 2006 17:20:08 +0000 Subject: don't hit an assetr if there are operations outstanding when the pa_context is destroyed git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@1171 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/operation.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/pulse') diff --git a/src/pulse/operation.c b/src/pulse/operation.c index 24ddf69f..8d896d7d 100644 --- a/src/pulse/operation.c +++ b/src/pulse/operation.c @@ -76,6 +76,8 @@ static void operation_set_state(pa_operation *o, pa_operation_state_t st) { if (st == o->state) return; + pa_operation_ref(o); + o->state = st; if ((o->state == PA_OPERATION_DONE) || (o->state == PA_OPERATION_CANCELED)) { @@ -92,6 +94,8 @@ static void operation_set_state(pa_operation *o, pa_operation_state_t st) { o->callback = NULL; o->userdata = NULL; } + + pa_operation_unref(o); } void pa_operation_cancel(pa_operation *o) { -- cgit From bb9b08758e0eb1447f9891adccabda5334923b7b Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 31 Jul 2006 21:55:09 +0000 Subject: wrap PA_COMMAND_MOVE_SINK_INPUT for libpulse git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@1177 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/introspect.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ src/pulse/introspect.h | 5 +++++ 2 files changed, 53 insertions(+) (limited to 'src/pulse') diff --git a/src/pulse/introspect.c b/src/pulse/introspect.c index ed40c53d..8aaf8b3f 100644 --- a/src/pulse/introspect.c +++ b/src/pulse/introspect.c @@ -1238,3 +1238,51 @@ pa_operation* pa_context_remove_autoload_by_index(pa_context *c, uint32_t idx, p return o; } + +pa_operation* pa_context_move_sink_input_by_name(pa_context *c, uint32_t idx, char *sink_name, pa_context_success_cb_t cb, void* userdata) { + pa_operation *o; + pa_tagstruct *t; + uint32_t tag; + + assert(c); + assert(c->ref >= 1); + + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID); + PA_CHECK_VALIDITY_RETURN_NULL(c, sink_name && *sink_name, PA_ERR_INVALID); + + o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); + + t = pa_tagstruct_command(c, PA_COMMAND_MOVE_SINK_INPUT, &tag); + pa_tagstruct_putu32(t, idx); + pa_tagstruct_putu32(t, PA_INVALID_INDEX); + pa_tagstruct_puts(t, sink_name); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); + + return o; +} + +pa_operation* pa_context_move_sink_input_by_index(pa_context *c, uint32_t idx, uint32_t sink_idx, pa_context_success_cb_t cb, void* userdata) { + pa_operation *o; + pa_tagstruct *t; + uint32_t tag; + + assert(c); + assert(c->ref >= 1); + + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID); + PA_CHECK_VALIDITY_RETURN_NULL(c, sink_idx != PA_INVALID_INDEX, PA_ERR_INVALID); + + o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); + + t = pa_tagstruct_command(c, PA_COMMAND_MOVE_SINK_INPUT, &tag); + pa_tagstruct_putu32(t, idx); + pa_tagstruct_putu32(t, sink_idx); + pa_tagstruct_puts(t, NULL); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); + + return o; +} diff --git a/src/pulse/introspect.h b/src/pulse/introspect.h index e9a14490..529945fd 100644 --- a/src/pulse/introspect.h +++ b/src/pulse/introspect.h @@ -484,6 +484,11 @@ pa_operation* pa_context_remove_autoload_by_name(pa_context *c, const char *name /** Remove an autoload entry. \since 0.6 */ pa_operation* pa_context_remove_autoload_by_index(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void* userdata); +/** Move the specified sink input to a different sink. \since 0.9.5 */ +pa_operation* pa_context_move_sink_input_by_name(pa_context *c, uint32_t idx, char *sink_name, pa_context_success_cb_t cb, void* userdata); + +/** Move the specified sink input to a different sink. \since 0.9.5 */ +pa_operation* pa_context_move_sink_input_by_index(pa_context *c, uint32_t idx, uint32_t sink_idx, pa_context_success_cb_t cb, void* userdata); PA_C_DECL_END -- cgit From 7f93d08d4014cc68965611068c47834c1e5547ef Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 1 Aug 2006 21:04:43 +0000 Subject: bump API and protocol version. Return PA_ERR_NOTSUPPORTED if pa_context_move_sink_input_by_*()is called for servers that don't support it git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@1179 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/def.h | 1 + src/pulse/introspect.c | 2 ++ 2 files changed, 3 insertions(+) (limited to 'src/pulse') diff --git a/src/pulse/def.h b/src/pulse/def.h index 01ed0f6d..a22e3c19 100644 --- a/src/pulse/def.h +++ b/src/pulse/def.h @@ -161,6 +161,7 @@ enum { PA_ERR_NODATA, /**< No data */ PA_ERR_VERSION, /**< Incompatible protocol version \since 0.8 */ PA_ERR_TOOLARGE, /**< Data too large \since 0.8.1 */ + PA_ERR_NOTSUPPORTED, /**< Operation not supported \since 0.9.5 */ PA_ERR_MAX /**< Not really an error but the first invalid error code */ }; diff --git a/src/pulse/introspect.c b/src/pulse/introspect.c index 8aaf8b3f..d750bbde 100644 --- a/src/pulse/introspect.c +++ b/src/pulse/introspect.c @@ -1248,6 +1248,7 @@ pa_operation* pa_context_move_sink_input_by_name(pa_context *c, uint32_t idx, ch assert(c->ref >= 1); PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 10, PA_ERR_NOTSUPPORTED); PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID); PA_CHECK_VALIDITY_RETURN_NULL(c, sink_name && *sink_name, PA_ERR_INVALID); @@ -1272,6 +1273,7 @@ pa_operation* pa_context_move_sink_input_by_index(pa_context *c, uint32_t idx, u assert(c->ref >= 1); PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 10, PA_ERR_NOTSUPPORTED); PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID); PA_CHECK_VALIDITY_RETURN_NULL(c, sink_idx != PA_INVALID_INDEX, PA_ERR_INVALID); -- cgit From e52436b6c4ff50906b4fbf2e9e5cdb8fb2eb683b Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 3 Aug 2006 22:33:11 +0000 Subject: implement pa_context_move_source_output_by_{name,index}() git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@1184 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/introspect.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/pulse/introspect.h | 6 ++++++ 2 files changed, 56 insertions(+) (limited to 'src/pulse') diff --git a/src/pulse/introspect.c b/src/pulse/introspect.c index d750bbde..b926e4e4 100644 --- a/src/pulse/introspect.c +++ b/src/pulse/introspect.c @@ -1288,3 +1288,53 @@ pa_operation* pa_context_move_sink_input_by_index(pa_context *c, uint32_t idx, u return o; } + +pa_operation* pa_context_move_source_output_by_name(pa_context *c, uint32_t idx, char *source_name, pa_context_success_cb_t cb, void* userdata) { + pa_operation *o; + pa_tagstruct *t; + uint32_t tag; + + assert(c); + assert(c->ref >= 1); + + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 10, PA_ERR_NOTSUPPORTED); + PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID); + PA_CHECK_VALIDITY_RETURN_NULL(c, source_name && *source_name, PA_ERR_INVALID); + + o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); + + t = pa_tagstruct_command(c, PA_COMMAND_MOVE_SOURCE_OUTPUT, &tag); + pa_tagstruct_putu32(t, idx); + pa_tagstruct_putu32(t, PA_INVALID_INDEX); + pa_tagstruct_puts(t, source_name); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); + + return o; +} + +pa_operation* pa_context_move_source_output_by_index(pa_context *c, uint32_t idx, uint32_t source_idx, pa_context_success_cb_t cb, void* userdata) { + pa_operation *o; + pa_tagstruct *t; + uint32_t tag; + + assert(c); + assert(c->ref >= 1); + + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 10, PA_ERR_NOTSUPPORTED); + PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID); + PA_CHECK_VALIDITY_RETURN_NULL(c, source_idx != PA_INVALID_INDEX, PA_ERR_INVALID); + + o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); + + t = pa_tagstruct_command(c, PA_COMMAND_MOVE_SOURCE_OUTPUT, &tag); + pa_tagstruct_putu32(t, idx); + pa_tagstruct_putu32(t, source_idx); + pa_tagstruct_puts(t, NULL); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); + + return o; +} diff --git a/src/pulse/introspect.h b/src/pulse/introspect.h index 529945fd..28d22cd7 100644 --- a/src/pulse/introspect.h +++ b/src/pulse/introspect.h @@ -490,6 +490,12 @@ pa_operation* pa_context_move_sink_input_by_name(pa_context *c, uint32_t idx, ch /** Move the specified sink input to a different sink. \since 0.9.5 */ pa_operation* pa_context_move_sink_input_by_index(pa_context *c, uint32_t idx, uint32_t sink_idx, pa_context_success_cb_t cb, void* userdata); +/** Move the specified source output to a different source. \since 0.9.5 */ +pa_operation* pa_context_move_source_output_by_name(pa_context *c, uint32_t idx, char *source_name, pa_context_success_cb_t cb, void* userdata); + +/** Move the specified source output to a different source. \since 0.9.5 */ +pa_operation* pa_context_move_source_output_by_index(pa_context *c, uint32_t idx, uint32_t source_idx, pa_context_success_cb_t cb, void* userdata); + PA_C_DECL_END #endif -- cgit From d89066036ba2b6d62a728540baff3c1269515c22 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 17 Aug 2006 20:02:46 +0000 Subject: modify pa_bytes_snprint() to return the string we just wrote to. This should be binary compat with older versions which returned void git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@1259 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/sample.c | 4 +++- src/pulse/sample.h | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'src/pulse') diff --git a/src/pulse/sample.c b/src/pulse/sample.c index 87b2d7a0..7ca418e1 100644 --- a/src/pulse/sample.c +++ b/src/pulse/sample.c @@ -120,7 +120,7 @@ char *pa_sample_spec_snprint(char *s, size_t l, const pa_sample_spec *spec) { return s; } -void pa_bytes_snprint(char *s, size_t l, unsigned v) { +char* pa_bytes_snprint(char *s, size_t l, unsigned v) { if (v >= ((unsigned) 1024)*1024*1024) snprintf(s, l, "%0.1f GiB", ((double) v)/1024/1024/1024); else if (v >= ((unsigned) 1024)*1024) @@ -129,6 +129,8 @@ void pa_bytes_snprint(char *s, size_t l, unsigned v) { snprintf(s, l, "%0.1f KiB", ((double) v)/1024); else snprintf(s, l, "%u B", (unsigned) v); + + return s; } pa_sample_format_t pa_parse_sample_format(const char *format) { diff --git a/src/pulse/sample.h b/src/pulse/sample.h index 03965a99..da32fdf0 100644 --- a/src/pulse/sample.h +++ b/src/pulse/sample.h @@ -182,7 +182,7 @@ pa_sample_format_t pa_parse_sample_format(const char *format); char* pa_sample_spec_snprint(char *s, size_t l, const pa_sample_spec *spec); /** Pretty print a byte size value. (i.e. "2.5 MiB") */ -void pa_bytes_snprint(char *s, size_t l, unsigned v); +char* pa_bytes_snprint(char *s, size_t l, unsigned v); PA_C_DECL_END -- cgit From 0e436a6926af56f37a74a03bb5e143e078ca0d55 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 18 Aug 2006 19:55:18 +0000 Subject: Rework memory management to allow shared memory data transfer. The central idea is to allocate all audio memory blocks from a per-process memory pool which is available as read-only SHM segment to other local processes. Then, instead of writing the actual audio data to the socket just write references to this shared memory pool. To work optimally all memory blocks should now be of type PA_MEMBLOCK_POOL or PA_MEMBLOCK_POOL_EXTERNAL. The function pa_memblock_new() now generates memory blocks of this type by default. git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@1266 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/context.c | 8 +++++--- src/pulse/internal.h | 2 +- src/pulse/stream.c | 7 +++---- 3 files changed, 9 insertions(+), 8 deletions(-) (limited to 'src/pulse') diff --git a/src/pulse/context.c b/src/pulse/context.c index 34f517f0..b3530542 100644 --- a/src/pulse/context.c +++ b/src/pulse/context.c @@ -128,7 +128,7 @@ pa_context *pa_context_new(pa_mainloop_api *mainloop, const char *name) { c->subscribe_callback = NULL; c->subscribe_userdata = NULL; - c->memblock_stat = pa_memblock_stat_new(); + c->mempool = pa_mempool_new(1); c->local = -1; c->server_list = NULL; c->server = NULL; @@ -177,7 +177,7 @@ static void context_free(pa_context *c) { if (c->playback_streams) pa_dynarray_free(c->playback_streams, NULL, NULL); - pa_memblock_stat_unref(c->memblock_stat); + pa_mempool_free(c->mempool); if (c->conf) pa_client_conf_free(c->conf); @@ -407,7 +407,9 @@ static void setup_context(pa_context *c, pa_iochannel *io) { pa_context_ref(c); assert(!c->pstream); - c->pstream = pa_pstream_new(c->mainloop, io, c->memblock_stat); + c->pstream = pa_pstream_new(c->mainloop, io, c->mempool); + + pa_pstream_use_shm(c->pstream, 1); pa_pstream_set_die_callback(c->pstream, pstream_die_callback, c); pa_pstream_set_recieve_packet_callback(c->pstream, pstream_packet_callback, c); diff --git a/src/pulse/internal.h b/src/pulse/internal.h index 96028d83..afcfaeff 100644 --- a/src/pulse/internal.h +++ b/src/pulse/internal.h @@ -69,7 +69,7 @@ struct pa_context { pa_context_subscribe_cb_t subscribe_callback; void *subscribe_userdata; - pa_memblock_stat *memblock_stat; + pa_mempool *mempool; int local; int do_autospawn; diff --git a/src/pulse/stream.c b/src/pulse/stream.c index 677df009..180cd096 100644 --- a/src/pulse/stream.c +++ b/src/pulse/stream.c @@ -437,8 +437,7 @@ void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED pa_frame_size(&s->sample_spec), 1, 0, - NULL, - s->context->memblock_stat); + NULL); } s->channel_valid = 1; @@ -604,9 +603,9 @@ int pa_stream_write( return 0; if (free_cb) - chunk.memblock = pa_memblock_new_user((void*) data, length, free_cb, 1, s->context->memblock_stat); + chunk.memblock = pa_memblock_new_user(s->context->mempool, (void*) data, length, free_cb, 1); else { - chunk.memblock = pa_memblock_new(length, s->context->memblock_stat); + chunk.memblock = pa_memblock_new(s->context->mempool, length); memcpy(chunk.memblock->data, data, length); } -- cgit From e385d93e5aad6a6fce754c00c804ff1d6a6746d4 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 18 Aug 2006 21:38:40 +0000 Subject: remove all occurences of pa_logXXX(__FILE__": and replace them by pa_logXXX(" git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@1272 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/client-conf-x11.c | 4 ++-- src/pulse/client-conf.c | 2 +- src/pulse/context.c | 8 ++++---- src/pulse/mainloop-signal.c | 6 +++--- src/pulse/mainloop.c | 6 +++--- src/pulse/thread-mainloop.c | 2 +- src/pulse/util.c | 6 +++--- 7 files changed, 17 insertions(+), 17 deletions(-) (limited to 'src/pulse') diff --git a/src/pulse/client-conf-x11.c b/src/pulse/client-conf-x11.c index e4a90017..8cedc48b 100644 --- a/src/pulse/client-conf-x11.c +++ b/src/pulse/client-conf-x11.c @@ -46,7 +46,7 @@ int pa_client_conf_from_x11(pa_client_conf *c, const char *dname) { goto finish; if (!(d = XOpenDisplay(dname))) { - pa_log(__FILE__": XOpenDisplay() failed"); + pa_log("XOpenDisplay() failed"); goto finish; } @@ -69,7 +69,7 @@ int pa_client_conf_from_x11(pa_client_conf *c, const char *dname) { uint8_t cookie[PA_NATIVE_COOKIE_LENGTH]; if (pa_parsehex(t, cookie, sizeof(cookie)) != sizeof(cookie)) { - pa_log(__FILE__": failed to parse cookie data"); + pa_log("failed to parse cookie data"); goto finish; } diff --git a/src/pulse/client-conf.c b/src/pulse/client-conf.c index c3f58ec2..619b11c3 100644 --- a/src/pulse/client-conf.c +++ b/src/pulse/client-conf.c @@ -116,7 +116,7 @@ int pa_client_conf_load(pa_client_conf *c, const char *filename) { pa_open_config_file(DEFAULT_CLIENT_CONFIG_FILE, DEFAULT_CLIENT_CONFIG_FILE_USER, ENV_CLIENT_CONFIG_FILE, &fn, "r"); if (!f && errno != EINTR) { - pa_log(__FILE__": WARNING: failed to open configuration file '%s': %s", fn, pa_cstrerror(errno)); + pa_log("WARNING: failed to open configuration file '%s': %s", fn, pa_cstrerror(errno)); goto finish; } diff --git a/src/pulse/context.c b/src/pulse/context.c index b3530542..df627ee6 100644 --- a/src/pulse/context.c +++ b/src/pulse/context.c @@ -462,7 +462,7 @@ static int context_connect_spawn(pa_context *c) { pa_context_ref(c); if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) < 0) { - pa_log(__FILE__": socketpair(): %s", pa_cstrerror(errno)); + pa_log("socketpair(): %s", pa_cstrerror(errno)); pa_context_fail(c, PA_ERR_INTERNAL); goto fail; } @@ -476,7 +476,7 @@ static int context_connect_spawn(pa_context *c) { c->spawn_api.prefork(); if ((pid = fork()) < 0) { - pa_log(__FILE__": fork(): %s", pa_cstrerror(errno)); + pa_log("fork(): %s", pa_cstrerror(errno)); pa_context_fail(c, PA_ERR_INTERNAL); if (c->spawn_api.postfork) @@ -532,7 +532,7 @@ static int context_connect_spawn(pa_context *c) { c->spawn_api.postfork(); if (r < 0) { - pa_log(__FILE__": waitpid(): %s", pa_cstrerror(errno)); + pa_log("waitpid(): %s", pa_cstrerror(errno)); pa_context_fail(c, PA_ERR_INTERNAL); goto fail; } else if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { @@ -594,7 +594,7 @@ static int try_next_connection(pa_context *c) { goto finish; } - pa_log_debug(__FILE__": Trying to connect to %s...", u); + pa_log_debug("Trying to connect to %s...", u); pa_xfree(c->server); c->server = pa_xstrdup(u); diff --git a/src/pulse/mainloop-signal.c b/src/pulse/mainloop-signal.c index 11a27cd5..d651462b 100644 --- a/src/pulse/mainloop-signal.c +++ b/src/pulse/mainloop-signal.c @@ -90,12 +90,12 @@ static void callback(pa_mainloop_api*a, pa_io_event*e, int fd, pa_io_event_flags if (errno == EAGAIN) return; - pa_log(__FILE__": read(): %s", pa_cstrerror(errno)); + pa_log("read(): %s", pa_cstrerror(errno)); return; } if (r != sizeof(sig)) { - pa_log(__FILE__": short read()"); + pa_log("short read()"); return; } @@ -107,7 +107,7 @@ int pa_signal_init(pa_mainloop_api *a) { assert(!api && a && signal_pipe[0] == -1 && signal_pipe[1] == -1 && !io_event); if (pipe(signal_pipe) < 0) { - pa_log(__FILE__": pipe(): %s", pa_cstrerror(errno)); + pa_log("pipe(): %s", pa_cstrerror(errno)); return -1; } diff --git a/src/pulse/mainloop.c b/src/pulse/mainloop.c index 5ebb9bc8..f7b15537 100644 --- a/src/pulse/mainloop.c +++ b/src/pulse/mainloop.c @@ -192,7 +192,7 @@ static pa_io_event* mainloop_io_new( if ((select((SELECT_TYPE_ARG1) fd, NULL, NULL, SELECT_TYPE_ARG234 &xset, SELECT_TYPE_ARG5 &tv) == -1) && (WSAGetLastError() == WSAENOTSOCK)) { - pa_log_warn(__FILE__": WARNING: cannot monitor non-socket file descriptors."); + pa_log_warn("WARNING: cannot monitor non-socket file descriptors."); e->dead = 1; } } @@ -448,7 +448,7 @@ pa_mainloop *pa_mainloop_new(void) { m->wakeup_pipe_type = 0; if (pipe(m->wakeup_pipe) < 0) { - pa_log_error(__FILE__": ERROR: cannot create wakeup pipe"); + pa_log_error("ERROR: cannot create wakeup pipe"); pa_xfree(m); return NULL; } @@ -852,7 +852,7 @@ int pa_mainloop_poll(pa_mainloop *m) { if (errno == EINTR) m->poll_func_ret = 0; else - pa_log(__FILE__": poll(): %s", pa_cstrerror(errno)); + pa_log("poll(): %s", pa_cstrerror(errno)); } } diff --git a/src/pulse/thread-mainloop.c b/src/pulse/thread-mainloop.c index 34f0f250..b1b180a0 100644 --- a/src/pulse/thread-mainloop.c +++ b/src/pulse/thread-mainloop.c @@ -421,7 +421,7 @@ pa_mainloop_api* pa_threaded_mainloop_get_api(pa_threaded_mainloop*m) { #else /* defined(OS_IS_WIN32) || defined(HAVE_PTHREAD) */ pa_threaded_mainloop *pa_threaded_mainloop_new(void) { - pa_log_error(__FILE__": Threaded main loop not supported on this platform"); + pa_log_error("Threaded main loop not supported on this platform"); return NULL; } diff --git a/src/pulse/util.c b/src/pulse/util.c index b041fec8..c13951da 100644 --- a/src/pulse/util.c +++ b/src/pulse/util.c @@ -112,7 +112,7 @@ char *pa_get_user_name(char *s, size_t l) { char *pa_get_host_name(char *s, size_t l) { assert(s && l > 0); if (gethostname(s, l) < 0) { - pa_log(__FILE__": gethostname(): %s", pa_cstrerror(errno)); + pa_log("gethostname(): %s", pa_cstrerror(errno)); return NULL; } s[l-1] = 0; @@ -138,12 +138,12 @@ char *pa_get_home_dir(char *s, size_t l) { #ifdef HAVE_PWD_H #ifdef HAVE_GETPWUID_R if (getpwuid_r(getuid(), &pw, buf, sizeof(buf), &r) != 0 || !r) { - pa_log(__FILE__": getpwuid_r() failed"); + pa_log("getpwuid_r() failed"); #else /* XXX Not thread-safe, but needed on OSes (e.g. FreeBSD 4.X) * that do not support getpwuid_r. */ if ((r = getpwuid(getuid())) == NULL) { - pa_log(__FILE__": getpwuid_r() failed"); + pa_log("getpwuid_r() failed"); #endif return NULL; } -- cgit From 40875d6d05d00eb54265907be36b76ed7fc590ea Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 18 Aug 2006 23:45:23 +0000 Subject: enable SHM support on the client side only if both the client and the server run as the same user and the server supports it git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@1278 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/context.c | 30 +++++++++++++++++++++++------- src/pulse/internal.h | 2 +- 2 files changed, 24 insertions(+), 8 deletions(-) (limited to 'src/pulse') diff --git a/src/pulse/context.c b/src/pulse/context.c index df627ee6..55b49a79 100644 --- a/src/pulse/context.c +++ b/src/pulse/context.c @@ -129,7 +129,7 @@ pa_context *pa_context_new(pa_mainloop_api *mainloop, const char *name) { c->subscribe_userdata = NULL; c->mempool = pa_mempool_new(1); - c->local = -1; + c->is_local = -1; c->server_list = NULL; c->server = NULL; c->autospawn_lock_fd = -1; @@ -376,6 +376,21 @@ static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t goto finish; } + /* Enable shared memory support if possible */ + if (c->version >= 10 && + pa_mempool_is_shared(c->mempool) && + c->is_local) { + + /* Only enable SHM if both sides are owned by the same + * user. This is a security measure because otherwise + * data private to the user might leak. */ + + const pa_creds *creds; + if ((creds = pa_pdispatch_creds(pd))) + if (getuid() == creds->uid) + pa_pstream_use_shm(c->pstream, 1); + } + reply = pa_tagstruct_command(c, PA_COMMAND_SET_CLIENT_NAME, &tag); pa_tagstruct_puts(reply, c->name); pa_pstream_send_tagstruct(c->pstream, reply); @@ -409,8 +424,6 @@ static void setup_context(pa_context *c, pa_iochannel *io) { assert(!c->pstream); c->pstream = pa_pstream_new(c->mainloop, io, c->mempool); - pa_pstream_use_shm(c->pstream, 1); - pa_pstream_set_die_callback(c->pstream, pstream_die_callback, c); pa_pstream_set_recieve_packet_callback(c->pstream, pstream_packet_callback, c); pa_pstream_set_recieve_memblock_callback(c->pstream, pstream_memblock_callback, c); @@ -431,13 +444,16 @@ static void setup_context(pa_context *c, pa_iochannel *io) { { pa_creds ucred; + if (pa_iochannel_creds_supported(io)) + pa_iochannel_creds_enable(io); + ucred.uid = getuid(); ucred.gid = getgid(); pa_pstream_send_tagstruct_with_creds(c->pstream, t, &ucred); } #else - pa_pstream_send_tagstruct_with_creds(c->pstream, t, NULL); + pa_pstream_send_tagstruct(c->pstream, t, NULL); #endif pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, setup_complete_callback, c, NULL); @@ -542,7 +558,7 @@ static int context_connect_spawn(pa_context *c) { close(fds[1]); - c->local = 1; + c->is_local = 1; io = pa_iochannel_new(c->mainloop, fds[0], fds[0]); @@ -602,7 +618,7 @@ static int try_next_connection(pa_context *c) { if (!(c->client = pa_socket_client_new_string(c->mainloop, u, PA_NATIVE_DEFAULT_PORT))) continue; - c->local = pa_socket_client_is_local(c->client); + c->is_local = pa_socket_client_is_local(c->client); pa_socket_client_set_callback(c->client, on_connection, c); break; } @@ -938,7 +954,7 @@ pa_operation* pa_context_set_default_source(pa_context *c, const char *name, pa_ int pa_context_is_local(pa_context *c) { assert(c); - return c->local; + return c->is_local; } pa_operation* pa_context_set_name(pa_context *c, const char *name, pa_context_success_cb_t cb, void *userdata) { diff --git a/src/pulse/internal.h b/src/pulse/internal.h index afcfaeff..fa7d5bbd 100644 --- a/src/pulse/internal.h +++ b/src/pulse/internal.h @@ -71,7 +71,7 @@ struct pa_context { pa_mempool *mempool; - int local; + int is_local; int do_autospawn; int autospawn_lock_fd; pa_spawn_api spawn_api; -- cgit From d785b8fa877b829d9d38082968b35117f7c9d9ec Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 19 Aug 2006 01:18:30 +0000 Subject: add new "disable-shm" option to client.conf git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@1284 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/client-conf.c | 3 +++ src/pulse/client-conf.h | 2 +- src/pulse/context.c | 3 ++- 3 files changed, 6 insertions(+), 2 deletions(-) (limited to 'src/pulse') diff --git a/src/pulse/client-conf.c b/src/pulse/client-conf.c index 619b11c3..5cd7e3ed 100644 --- a/src/pulse/client-conf.c +++ b/src/pulse/client-conf.c @@ -62,6 +62,7 @@ static const pa_client_conf default_conf = { .default_source = NULL, .default_server = NULL, .autospawn = 0, + .disable_shm = 0, .cookie_file = NULL, .cookie_valid = 0, }; @@ -100,6 +101,7 @@ int pa_client_conf_load(pa_client_conf *c, const char *filename) { { "default-server", pa_config_parse_string, NULL }, { "autospawn", pa_config_parse_bool, NULL }, { "cookie-file", pa_config_parse_string, NULL }, + { "disable-shm", pa_config_parse_bool, NULL }, { NULL, NULL, NULL }, }; @@ -110,6 +112,7 @@ int pa_client_conf_load(pa_client_conf *c, const char *filename) { table[4].data = &c->default_server; table[5].data = &c->autospawn; table[6].data = &c->cookie_file; + table[7].data = &c->disable_shm; f = filename ? fopen((fn = pa_xstrdup(filename)), "r") : diff --git a/src/pulse/client-conf.h b/src/pulse/client-conf.h index a532f0df..35728aeb 100644 --- a/src/pulse/client-conf.h +++ b/src/pulse/client-conf.h @@ -28,7 +28,7 @@ typedef struct pa_client_conf { char *daemon_binary, *extra_arguments, *default_sink, *default_source, *default_server, *cookie_file; - int autospawn; + int autospawn, disable_shm; uint8_t cookie[PA_NATIVE_COOKIE_LENGTH]; int cookie_valid; /* non-zero, when cookie is valid */ } pa_client_conf; diff --git a/src/pulse/context.c b/src/pulse/context.c index 55b49a79..b4641590 100644 --- a/src/pulse/context.c +++ b/src/pulse/context.c @@ -128,7 +128,6 @@ pa_context *pa_context_new(pa_mainloop_api *mainloop, const char *name) { c->subscribe_callback = NULL; c->subscribe_userdata = NULL; - c->mempool = pa_mempool_new(1); c->is_local = -1; c->server_list = NULL; c->server = NULL; @@ -149,6 +148,8 @@ pa_context *pa_context_new(pa_mainloop_api *mainloop, const char *name) { #endif pa_client_conf_env(c->conf); + c->mempool = pa_mempool_new(!c->conf->disable_shm); + return c; } -- cgit From c9b6d55090a22256443b0a0f4b24acfa94bf4422 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 19 Aug 2006 01:18:57 +0000 Subject: add default "disable-shm" option to client.conf git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@1285 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/client.conf.in | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src/pulse') diff --git a/src/pulse/client.conf.in b/src/pulse/client.conf.in index c970be56..3cfd9760 100644 --- a/src/pulse/client.conf.in +++ b/src/pulse/client.conf.in @@ -40,3 +40,6 @@ ### Cookie file ; cookie-file = + +### Disable shared memory data transfer +; disable-shm = 0 -- cgit From 25c0640ac2206872f76df70368180c74153581e3 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Tue, 22 Aug 2006 07:12:50 +0000 Subject: Add an ifdef for when we do not have creds. git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@1307 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/context.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/pulse') diff --git a/src/pulse/context.c b/src/pulse/context.c index b4641590..f2af4727 100644 --- a/src/pulse/context.c +++ b/src/pulse/context.c @@ -385,11 +385,13 @@ static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t /* Only enable SHM if both sides are owned by the same * user. This is a security measure because otherwise * data private to the user might leak. */ - + +#ifdef HAVE_CREDS const pa_creds *creds; if ((creds = pa_pdispatch_creds(pd))) if (getuid() == creds->uid) pa_pstream_use_shm(c->pstream, 1); +#endif } reply = pa_tagstruct_command(c, PA_COMMAND_SET_CLIENT_NAME, &tag); -- cgit From 8a16c731514a7d25e37ea70080e4c3d42fb5e5d4 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Tue, 22 Aug 2006 07:18:07 +0000 Subject: Fix call to pa_pstream_send_tagstruct(). git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@1308 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/context.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/pulse') diff --git a/src/pulse/context.c b/src/pulse/context.c index f2af4727..2f9106a5 100644 --- a/src/pulse/context.c +++ b/src/pulse/context.c @@ -456,7 +456,7 @@ static void setup_context(pa_context *c, pa_iochannel *io) { pa_pstream_send_tagstruct_with_creds(c->pstream, t, &ucred); } #else - pa_pstream_send_tagstruct(c->pstream, t, NULL); + pa_pstream_send_tagstruct(c->pstream, t); #endif pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, setup_complete_callback, c, NULL); -- cgit From 095f35725d644e0284c4b2d99b6fab71290eaeaa Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Tue, 22 Aug 2006 15:36:37 +0000 Subject: Proceed with connect even when no cookie is loaded. Allows you to connect to server which do not require a cookie under all circumstances. git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@1324 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/context.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'src/pulse') diff --git a/src/pulse/context.c b/src/pulse/context.c index 2f9106a5..b700657b 100644 --- a/src/pulse/context.c +++ b/src/pulse/context.c @@ -434,10 +434,8 @@ static void setup_context(pa_context *c, pa_iochannel *io) { assert(!c->pdispatch); c->pdispatch = pa_pdispatch_new(c->mainloop, command_table, PA_COMMAND_MAX); - if (!c->conf->cookie_valid) { - pa_context_fail(c, PA_ERR_AUTHKEY); - goto finish; - } + if (!c->conf->cookie_valid) + pa_log_warn("No cookie loaded. Attempting to connect without."); t = pa_tagstruct_command(c, PA_COMMAND_AUTH, &tag); pa_tagstruct_putu32(t, PA_PROTOCOL_VERSION); @@ -463,8 +461,6 @@ static void setup_context(pa_context *c, pa_iochannel *io) { pa_context_set_state(c, PA_CONTEXT_AUTHORIZING); -finish: - pa_context_unref(c); } -- cgit From 1ed33478e531eba112dd421cbcaa42202d314135 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sun, 27 Aug 2006 13:04:56 +0000 Subject: increase operation timeout git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@1336 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/internal.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/pulse') diff --git a/src/pulse/internal.h b/src/pulse/internal.h index fa7d5bbd..4eef4b4a 100644 --- a/src/pulse/internal.h +++ b/src/pulse/internal.h @@ -41,7 +41,7 @@ #include "client-conf.h" -#define DEFAULT_TIMEOUT (10) +#define DEFAULT_TIMEOUT (30) struct pa_context { int ref; -- cgit From 813e95f3b88a8345f82a205126d94bbffeda57b9 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 4 Sep 2006 21:29:17 +0000 Subject: port the threaded mainloop to our new abstract mutex/thread API git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@1369 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/thread-mainloop.c | 331 ++++++-------------------------------------- 1 file changed, 43 insertions(+), 288 deletions(-) (limited to 'src/pulse') diff --git a/src/pulse/thread-mainloop.c b/src/pulse/thread-mainloop.c index b1b180a0..060782b5 100644 --- a/src/pulse/thread-mainloop.c +++ b/src/pulse/thread-mainloop.c @@ -33,57 +33,31 @@ #include "../pulsecore/poll.h" #endif -#ifdef HAVE_PTHREAD -#include -#endif - -#ifdef HAVE_WINDOWS_H -#include -#endif - #include #include #include +#include +#include #include "mainloop.h" #include "thread-mainloop.h" -#if defined(HAVE_PTHREAD) || defined(OS_IS_WIN32) - struct pa_threaded_mainloop { pa_mainloop *real_mainloop; int n_waiting; - int thread_running; - -#ifdef OS_IS_WIN32 - DWORD thread_id; - HANDLE thread; - CRITICAL_SECTION mutex; - pa_hashmap *cond_events; - HANDLE accept_cond; -#else - pthread_t thread_id; - pthread_mutex_t mutex; - pthread_cond_t cond, accept_cond; -#endif + + pa_thread* thread; + pa_mutex* mutex; + pa_cond* cond, *accept_cond; }; static inline int in_worker(pa_threaded_mainloop *m) { -#ifdef OS_IS_WIN32 - return GetCurrentThreadId() == m->thread_id; -#else - return pthread_equal(pthread_self(), m->thread_id); -#endif + return pa_thread_self() == m->thread; } static int poll_func(struct pollfd *ufds, unsigned long nfds, int timeout, void *userdata) { -#ifdef OS_IS_WIN32 - CRITICAL_SECTION *mutex = userdata; -#else - pthread_mutex_t *mutex = userdata; -#endif - + pa_mutex *mutex = userdata; int r; assert(mutex); @@ -91,28 +65,14 @@ static int poll_func(struct pollfd *ufds, unsigned long nfds, int timeout, void /* Before entering poll() we unlock the mutex, so that * avahi_simple_poll_quit() can succeed from another thread. */ -#ifdef OS_IS_WIN32 - LeaveCriticalSection(mutex); -#else - pthread_mutex_unlock(mutex); -#endif - + pa_mutex_unlock(mutex); r = poll(ufds, nfds, timeout); - -#ifdef OS_IS_WIN32 - EnterCriticalSection(mutex); -#else - pthread_mutex_lock(mutex); -#endif + pa_mutex_lock(mutex); return r; } -#ifdef OS_IS_WIN32 -static DWORD WINAPI thread(void *userdata) { -#else -static void* thread(void *userdata) { -#endif +static void thread(void *userdata) { pa_threaded_mainloop *m = userdata; #ifndef OS_IS_WIN32 @@ -123,32 +83,15 @@ static void* thread(void *userdata) { pthread_sigmask(SIG_BLOCK, &mask, NULL); #endif -#ifdef OS_IS_WIN32 - EnterCriticalSection(&m->mutex); -#else - pthread_mutex_lock(&m->mutex); -#endif + pa_mutex_lock(m->mutex); pa_mainloop_run(m->real_mainloop, NULL); -#ifdef OS_IS_WIN32 - LeaveCriticalSection(&m->mutex); -#else - pthread_mutex_unlock(&m->mutex); -#endif - -#ifdef OS_IS_WIN32 - return 0; -#else - return NULL; -#endif + pa_mutex_unlock(m->mutex); } pa_threaded_mainloop *pa_threaded_mainloop_new(void) { pa_threaded_mainloop *m; -#ifndef OS_IS_WIN32 - pthread_mutexattr_t a; -#endif m = pa_xnew(pa_threaded_mainloop, 1); @@ -157,26 +100,13 @@ pa_threaded_mainloop *pa_threaded_mainloop_new(void) { return NULL; } - pa_mainloop_set_poll_func(m->real_mainloop, poll_func, &m->mutex); + m->mutex = pa_mutex_new(1); + m->cond = pa_cond_new(); + m->accept_cond = pa_cond_new(); + m->thread = NULL; -#ifdef OS_IS_WIN32 - InitializeCriticalSection(&m->mutex); - - m->cond_events = pa_hashmap_new(NULL, NULL); - assert(m->cond_events); - m->accept_cond = CreateEvent(NULL, FALSE, FALSE, NULL); - assert(m->accept_cond); -#else - pthread_mutexattr_init(&a); - pthread_mutexattr_settype(&a, PTHREAD_MUTEX_RECURSIVE); - pthread_mutex_init(&m->mutex, &a); - pthread_mutexattr_destroy(&a); - - pthread_cond_init(&m->cond, NULL); - pthread_cond_init(&m->accept_cond, NULL); -#endif + pa_mainloop_set_poll_func(m->real_mainloop, poll_func, m->mutex); - m->thread_running = 0; m->n_waiting = 0; return m; @@ -186,22 +116,17 @@ void pa_threaded_mainloop_free(pa_threaded_mainloop* m) { assert(m); /* Make sure that this function is not called from the helper thread */ - assert(!m->thread_running || !in_worker(m)); + assert((m->thread && !pa_thread_is_running(m->thread)) || !in_worker(m)); - if (m->thread_running) - pa_threaded_mainloop_stop(m); + pa_threaded_mainloop_stop(m); - if (m->real_mainloop) - pa_mainloop_free(m->real_mainloop); + pa_thread_free(m->thread); -#ifdef OS_IS_WIN32 - pa_hashmap_free(m->cond_events, NULL, NULL); - CloseHandle(m->accept_cond); -#else - pthread_mutex_destroy(&m->mutex); - pthread_cond_destroy(&m->cond); - pthread_cond_destroy(&m->accept_cond); -#endif + pa_mainloop_free(m->real_mainloop); + + pa_mutex_free(m->mutex); + pa_cond_free(m->cond); + pa_cond_free(m->accept_cond); pa_xfree(m); } @@ -209,36 +134,10 @@ void pa_threaded_mainloop_free(pa_threaded_mainloop* m) { int pa_threaded_mainloop_start(pa_threaded_mainloop *m) { assert(m); - assert(!m->thread_running); - -#ifdef OS_IS_WIN32 - - EnterCriticalSection(&m->mutex); + assert(!m->thread || !pa_thread_is_running(m->thread)); - m->thread = CreateThread(NULL, 0, thread, m, 0, &m->thread_id); - if (!m->thread) { - LeaveCriticalSection(&m->mutex); + if (!(m->thread = pa_thread_new(thread, m))) return -1; - } - -#else - - pthread_mutex_lock(&m->mutex); - - if (pthread_create(&m->thread_id, NULL, thread, m) < 0) { - pthread_mutex_unlock(&m->mutex); - return -1; - } - -#endif - - m->thread_running = 1; - -#ifdef OS_IS_WIN32 - LeaveCriticalSection(&m->mutex); -#else - pthread_mutex_unlock(&m->mutex); -#endif return 0; } @@ -246,148 +145,55 @@ int pa_threaded_mainloop_start(pa_threaded_mainloop *m) { void pa_threaded_mainloop_stop(pa_threaded_mainloop *m) { assert(m); - if (!m->thread_running) + if (!m->thread || !pa_thread_is_running(m->thread)) return; /* Make sure that this function is not called from the helper thread */ assert(!in_worker(m)); -#ifdef OS_IS_WIN32 - EnterCriticalSection(&m->mutex); -#else - pthread_mutex_lock(&m->mutex); -#endif - + pa_mutex_lock(m->mutex); pa_mainloop_quit(m->real_mainloop, 0); + pa_mutex_unlock(m->mutex); -#ifdef OS_IS_WIN32 - LeaveCriticalSection(&m->mutex); -#else - pthread_mutex_unlock(&m->mutex); -#endif - -#ifdef OS_IS_WIN32 - WaitForSingleObject(m->thread, INFINITE); - CloseHandle(m->thread); -#else - pthread_join(m->thread_id, NULL); -#endif - - m->thread_running = 0; - - return; + pa_thread_join(m->thread); } void pa_threaded_mainloop_lock(pa_threaded_mainloop *m) { assert(m); /* Make sure that this function is not called from the helper thread */ - assert(!m->thread_running || !in_worker(m)); + assert(!m->thread || !pa_thread_is_running(m->thread) || !in_worker(m)); -#ifdef OS_IS_WIN32 - EnterCriticalSection(&m->mutex); -#else - pthread_mutex_lock(&m->mutex); -#endif + pa_mutex_lock(m->mutex); } void pa_threaded_mainloop_unlock(pa_threaded_mainloop *m) { assert(m); /* Make sure that this function is not called from the helper thread */ - assert(!m->thread_running || !in_worker(m)); + assert(!m->thread || !pa_thread_is_running(m->thread) || !in_worker(m)); -#ifdef OS_IS_WIN32 - LeaveCriticalSection(&m->mutex); -#else - pthread_mutex_unlock(&m->mutex); -#endif + pa_mutex_unlock(m->mutex); } void pa_threaded_mainloop_signal(pa_threaded_mainloop *m, int wait_for_accept) { -#ifdef OS_IS_WIN32 - void *iter; - const void *key; - HANDLE event; -#endif - assert(m); -#ifdef OS_IS_WIN32 - - iter = NULL; - while (1) { - pa_hashmap_iterate(m->cond_events, &iter, &key); - if (key == NULL) - break; - event = (HANDLE)pa_hashmap_get(m->cond_events, key); - SetEvent(event); - } - -#else - - pthread_cond_broadcast(&m->cond); - -#endif - - if (wait_for_accept && m->n_waiting > 0) { - -#ifdef OS_IS_WIN32 - - /* This is just to make sure it's unsignaled */ - WaitForSingleObject(m->accept_cond, 0); - - LeaveCriticalSection(&m->mutex); - - WaitForSingleObject(m->accept_cond, INFINITE); - - EnterCriticalSection(&m->mutex); - -#else - - pthread_cond_wait(&m->accept_cond, &m->mutex); + pa_cond_signal(m->cond, 1); -#endif - - } + if (wait_for_accept && m->n_waiting > 0) + pa_cond_wait(m->accept_cond, m->mutex); } void pa_threaded_mainloop_wait(pa_threaded_mainloop *m) { -#ifdef OS_IS_WIN32 - HANDLE event; - DWORD result; -#endif - assert(m); /* Make sure that this function is not called from the helper thread */ - assert(!m->thread_running || !in_worker(m)); + assert(!m->thread || !pa_thread_is_running(m->thread) || !in_worker(m)); m->n_waiting ++; -#ifdef OS_IS_WIN32 - - event = CreateEvent(NULL, FALSE, FALSE, NULL); - assert(event); - - pa_hashmap_put(m->cond_events, event, event); - - LeaveCriticalSection(&m->mutex); - - result = WaitForSingleObject(event, INFINITE); - assert(result == WAIT_OBJECT_0); - - EnterCriticalSection(&m->mutex); - - pa_hashmap_remove(m->cond_events, event); - - CloseHandle(event); - -#else - - pthread_cond_wait(&m->cond, &m->mutex); - -#endif + pa_cond_wait(m->cond, m->mutex); assert(m->n_waiting > 0); m->n_waiting --; @@ -397,13 +203,9 @@ void pa_threaded_mainloop_accept(pa_threaded_mainloop *m) { assert(m); /* Make sure that this function is not called from the helper thread */ - assert(!m->thread_running || !in_worker(m)); + assert(!m->thread || !pa_thread_is_running(m->thread) || !in_worker(m)); -#ifdef OS_IS_WIN32 - SetEvent(m->accept_cond); -#else - pthread_cond_signal(&m->accept_cond); -#endif + pa_cond_signal(m->accept_cond, 0); } int pa_threaded_mainloop_get_retval(pa_threaded_mainloop *m) { @@ -417,50 +219,3 @@ pa_mainloop_api* pa_threaded_mainloop_get_api(pa_threaded_mainloop*m) { return pa_mainloop_get_api(m->real_mainloop); } - -#else /* defined(OS_IS_WIN32) || defined(HAVE_PTHREAD) */ - -pa_threaded_mainloop *pa_threaded_mainloop_new(void) { - pa_log_error("Threaded main loop not supported on this platform"); - return NULL; -} - -void pa_threaded_mainloop_free(pa_threaded_mainloop* m) { - assert(0); -} - -int pa_threaded_mainloop_start(pa_threaded_mainloop *m) { - assert(0); - return -1; -} - -void pa_threaded_mainloop_stop(pa_threaded_mainloop *m) { - assert(0); -} - -void pa_threaded_mainloop_lock(pa_threaded_mainloop *m) { - assert(0); -} - -void pa_threaded_mainloop_unlock(pa_threaded_mainloop *m) { - assert(0); -} - -void pa_threaded_mainloop_wait(pa_threaded_mainloop *m) { - assert(0); -} - -void pa_threaded_mainloop_signal(pa_threaded_mainloop *m, int wait_for_release) { - assert(0); -} - -int pa_threaded_mainloop_get_retval(pa_threaded_mainloop *m) { - assert(0); -} - -pa_mainloop_api* pa_threaded_mainloop_get_api(pa_threaded_mainloop*m) { - assert(0); - return NULL; -} - -#endif /* defined(OS_IS_WIN32) || defined(HAVE_PTHREAD) */ -- cgit From 40ecf869d0f77a3af2ce59bc060c4bccd2baec25 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 6 Sep 2006 22:19:54 +0000 Subject: don't hit an assert in the client if posix shm is not available git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@1376 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/context.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) (limited to 'src/pulse') diff --git a/src/pulse/context.c b/src/pulse/context.c index b700657b..a458c6b1 100644 --- a/src/pulse/context.c +++ b/src/pulse/context.c @@ -98,6 +98,8 @@ static void unlock_autospawn_lock_file(pa_context *c) { } } +static void context_free(pa_context *c); + pa_context *pa_context_new(pa_mainloop_api *mainloop, const char *name) { pa_context *c; @@ -148,7 +150,16 @@ pa_context *pa_context_new(pa_mainloop_api *mainloop, const char *name) { #endif pa_client_conf_env(c->conf); - c->mempool = pa_mempool_new(!c->conf->disable_shm); + if (!(c->mempool = pa_mempool_new(!c->conf->disable_shm))) { + + if (!c->conf->disable_shm) + c->mempool = pa_mempool_new(0); + + if (!c->mempool) { + context_free(c); + return NULL; + } + } return c; } @@ -178,7 +189,8 @@ static void context_free(pa_context *c) { if (c->playback_streams) pa_dynarray_free(c->playback_streams, NULL, NULL); - pa_mempool_free(c->mempool); + if (c->mempool) + pa_mempool_free(c->mempool); if (c->conf) pa_client_conf_free(c->conf); -- cgit From 0669c99fb6fedcd43af43466b7967b30b5af4f1d Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 7 Sep 2006 13:31:53 +0000 Subject: add missing channel names (fixes a segfault when parsing invalid channel maps) git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@1378 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/channelmap.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'src/pulse') diff --git a/src/pulse/channelmap.c b/src/pulse/channelmap.c index a4f13372..69b09089 100644 --- a/src/pulse/channelmap.c +++ b/src/pulse/channelmap.c @@ -68,6 +68,22 @@ const char *const table[] = { [PA_CHANNEL_POSITION_AUX13] = "aux13", [PA_CHANNEL_POSITION_AUX14] = "aux14", [PA_CHANNEL_POSITION_AUX15] = "aux15", + [PA_CHANNEL_POSITION_AUX16] = "aux16", + [PA_CHANNEL_POSITION_AUX17] = "aux17", + [PA_CHANNEL_POSITION_AUX18] = "aux18", + [PA_CHANNEL_POSITION_AUX19] = "aux19", + [PA_CHANNEL_POSITION_AUX20] = "aux20", + [PA_CHANNEL_POSITION_AUX21] = "aux21", + [PA_CHANNEL_POSITION_AUX22] = "aux22", + [PA_CHANNEL_POSITION_AUX23] = "aux23", + [PA_CHANNEL_POSITION_AUX24] = "aux24", + [PA_CHANNEL_POSITION_AUX25] = "aux25", + [PA_CHANNEL_POSITION_AUX26] = "aux26", + [PA_CHANNEL_POSITION_AUX27] = "aux27", + [PA_CHANNEL_POSITION_AUX28] = "aux28", + [PA_CHANNEL_POSITION_AUX29] = "aux29", + [PA_CHANNEL_POSITION_AUX30] = "aux30", + [PA_CHANNEL_POSITION_AUX31] = "aux31", [PA_CHANNEL_POSITION_TOP_CENTER] = "top-center", -- cgit From d210ebbb09daddb2c8c8e8e77243e088b0b19c4d Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 26 Sep 2006 23:50:56 +0000 Subject: rework memory block management to be thread-safe and mostly lock-free. pa_memblock is now an opaque structure. Access to its fields is now done through various accessor functions in a thread-safe manner. pa_memblock_acquire() and pa_memblock_release() are now used to access the attached audio data. Why? To allow safe manipulation of the memory pointer maintained by the memory block. Internally _acquire() and _release() maintain a reference counter. Please do not confuse this reference counter whith the one maintained by pa_memblock_ref()/_unref()! As a side effect this patch removes all direct usages of AO_t and replaces it with pa_atomic_xxx based code. This stuff needs some serious testing love. Especially if threads are actively used. git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@1404 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/internal.h | 1 + src/pulse/stream.c | 20 ++++++++++++++++---- 2 files changed, 17 insertions(+), 4 deletions(-) (limited to 'src/pulse') diff --git a/src/pulse/internal.h b/src/pulse/internal.h index 4eef4b4a..76d80d83 100644 --- a/src/pulse/internal.h +++ b/src/pulse/internal.h @@ -113,6 +113,7 @@ struct pa_stream { uint32_t requested_bytes; pa_memchunk peek_memchunk; + void *peek_data; pa_memblockq *record_memblockq; int corked; diff --git a/src/pulse/stream.c b/src/pulse/stream.c index 180cd096..d31127d8 100644 --- a/src/pulse/stream.c +++ b/src/pulse/stream.c @@ -88,6 +88,7 @@ pa_stream *pa_stream_new(pa_context *c, const char *name, const pa_sample_spec * s->peek_memchunk.index = 0; s->peek_memchunk.length = 0; s->peek_memchunk.memblock = NULL; + s->peek_data = NULL; s->record_memblockq = NULL; @@ -122,8 +123,11 @@ static void stream_free(pa_stream *s) { s->mainloop->time_free(s->auto_timing_update_event); } - if (s->peek_memchunk.memblock) + if (s->peek_memchunk.memblock) { + if (s->peek_data) + pa_memblock_release(s->peek_memchunk.memblock); pa_memblock_unref(s->peek_memchunk.memblock); + } if (s->record_memblockq) pa_memblockq_free(s->record_memblockq); @@ -605,8 +609,11 @@ int pa_stream_write( if (free_cb) chunk.memblock = pa_memblock_new_user(s->context->mempool, (void*) data, length, free_cb, 1); else { + void *tdata; chunk.memblock = pa_memblock_new(s->context->mempool, length); - memcpy(chunk.memblock->data, data, length); + tdata = pa_memblock_acquire(chunk.memblock); + memcpy(tdata, data, length); + pa_memblock_release(chunk.memblock); } chunk.index = 0; @@ -672,9 +679,12 @@ int pa_stream_peek(pa_stream *s, const void **data, size_t *length) { *length = 0; return 0; } + + s->peek_data = pa_memblock_acquire(s->peek_memchunk.memblock); } - *data = (const char*) s->peek_memchunk.memblock->data + s->peek_memchunk.index; + assert(s->peek_data); + *data = (uint8_t*) s->peek_data + s->peek_memchunk.index; *length = s->peek_memchunk.length; return 0; } @@ -692,7 +702,9 @@ int pa_stream_drop(pa_stream *s) { /* Fix the simulated local read index */ if (s->timing_info_valid && !s->timing_info.read_index_corrupt) s->timing_info.read_index += s->peek_memchunk.length; - + + assert(s->peek_data); + pa_memblock_release(s->peek_memchunk.memblock); pa_memblock_unref(s->peek_memchunk.memblock); s->peek_memchunk.length = 0; s->peek_memchunk.index = 0; -- cgit From 977659684855d30d48958910f92593e94dbb8da8 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Mon, 6 Nov 2006 10:14:16 +0000 Subject: Handle when threaded mainloop is freed before it is started. git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@1405 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/thread-mainloop.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/pulse') diff --git a/src/pulse/thread-mainloop.c b/src/pulse/thread-mainloop.c index 060782b5..7e1ccfaa 100644 --- a/src/pulse/thread-mainloop.c +++ b/src/pulse/thread-mainloop.c @@ -120,7 +120,8 @@ void pa_threaded_mainloop_free(pa_threaded_mainloop* m) { pa_threaded_mainloop_stop(m); - pa_thread_free(m->thread); + if (m->thread) + pa_thread_free(m->thread); pa_mainloop_free(m->real_mainloop); -- cgit From 8dc62142765249addf131b058c27f931ede1776b Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Mon, 6 Nov 2006 13:06:01 +0000 Subject: Revert r1404 and keep it on a development branch until it is fully tested. git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@1409 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/internal.h | 1 - src/pulse/stream.c | 20 ++++---------------- 2 files changed, 4 insertions(+), 17 deletions(-) (limited to 'src/pulse') diff --git a/src/pulse/internal.h b/src/pulse/internal.h index 76d80d83..4eef4b4a 100644 --- a/src/pulse/internal.h +++ b/src/pulse/internal.h @@ -113,7 +113,6 @@ struct pa_stream { uint32_t requested_bytes; pa_memchunk peek_memchunk; - void *peek_data; pa_memblockq *record_memblockq; int corked; diff --git a/src/pulse/stream.c b/src/pulse/stream.c index d31127d8..180cd096 100644 --- a/src/pulse/stream.c +++ b/src/pulse/stream.c @@ -88,7 +88,6 @@ pa_stream *pa_stream_new(pa_context *c, const char *name, const pa_sample_spec * s->peek_memchunk.index = 0; s->peek_memchunk.length = 0; s->peek_memchunk.memblock = NULL; - s->peek_data = NULL; s->record_memblockq = NULL; @@ -123,11 +122,8 @@ static void stream_free(pa_stream *s) { s->mainloop->time_free(s->auto_timing_update_event); } - if (s->peek_memchunk.memblock) { - if (s->peek_data) - pa_memblock_release(s->peek_memchunk.memblock); + if (s->peek_memchunk.memblock) pa_memblock_unref(s->peek_memchunk.memblock); - } if (s->record_memblockq) pa_memblockq_free(s->record_memblockq); @@ -609,11 +605,8 @@ int pa_stream_write( if (free_cb) chunk.memblock = pa_memblock_new_user(s->context->mempool, (void*) data, length, free_cb, 1); else { - void *tdata; chunk.memblock = pa_memblock_new(s->context->mempool, length); - tdata = pa_memblock_acquire(chunk.memblock); - memcpy(tdata, data, length); - pa_memblock_release(chunk.memblock); + memcpy(chunk.memblock->data, data, length); } chunk.index = 0; @@ -679,12 +672,9 @@ int pa_stream_peek(pa_stream *s, const void **data, size_t *length) { *length = 0; return 0; } - - s->peek_data = pa_memblock_acquire(s->peek_memchunk.memblock); } - assert(s->peek_data); - *data = (uint8_t*) s->peek_data + s->peek_memchunk.index; + *data = (const char*) s->peek_memchunk.memblock->data + s->peek_memchunk.index; *length = s->peek_memchunk.length; return 0; } @@ -702,9 +692,7 @@ int pa_stream_drop(pa_stream *s) { /* Fix the simulated local read index */ if (s->timing_info_valid && !s->timing_info.read_index_corrupt) s->timing_info.read_index += s->peek_memchunk.length; - - assert(s->peek_data); - pa_memblock_release(s->peek_memchunk.memblock); + pa_memblock_unref(s->peek_memchunk.memblock); s->peek_memchunk.length = 0; s->peek_memchunk.index = 0; -- cgit From 521daf6f0ac4fa6a2fbfb5d523c0c743342dca2b Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Thu, 4 Jan 2007 13:43:45 +0000 Subject: Huge trailing whitespace cleanup. Let's keep the tree pure from here on, mmmkay? git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@1418 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/browser.c | 62 ++++++------ src/pulse/browser.h | 8 +- src/pulse/cdecl.h | 6 +- src/pulse/channelmap.c | 110 ++++++++++---------- src/pulse/channelmap.h | 20 ++-- src/pulse/client-conf-x11.c | 4 +- src/pulse/client-conf.c | 16 +-- src/pulse/context.c | 156 ++++++++++++++--------------- src/pulse/context.h | 10 +- src/pulse/def.h | 16 +-- src/pulse/error.c | 6 +- src/pulse/error.h | 6 +- src/pulse/glib-mainloop.c | 98 +++++++++--------- src/pulse/glib-mainloop.h | 6 +- src/pulse/internal.h | 20 ++-- src/pulse/introspect.c | 118 +++++++++++----------- src/pulse/introspect.h | 24 ++--- src/pulse/mainloop-api.c | 6 +- src/pulse/mainloop-api.h | 8 +- src/pulse/mainloop-signal.c | 20 ++-- src/pulse/mainloop-signal.h | 6 +- src/pulse/mainloop.c | 96 +++++++++--------- src/pulse/mainloop.h | 8 +- src/pulse/operation.c | 20 ++-- src/pulse/operation.h | 6 +- src/pulse/pulseaudio.h | 12 +-- src/pulse/sample.c | 12 +-- src/pulse/sample.h | 6 +- src/pulse/scache.c | 24 ++--- src/pulse/scache.h | 6 +- src/pulse/simple.c | 68 ++++++------- src/pulse/simple.h | 6 +- src/pulse/stream.c | 238 ++++++++++++++++++++++---------------------- src/pulse/stream.h | 20 ++-- src/pulse/subscribe.c | 10 +- src/pulse/subscribe.h | 6 +- src/pulse/thread-mainloop.c | 16 +-- src/pulse/thread-mainloop.h | 10 +- src/pulse/timeval.c | 12 +-- src/pulse/timeval.h | 6 +- src/pulse/utf8.c | 2 +- src/pulse/utf8.h | 6 +- src/pulse/util.c | 30 +++--- src/pulse/util.h | 6 +- src/pulse/volume.c | 14 +-- src/pulse/volume.h | 6 +- src/pulse/xmalloc.c | 20 ++-- src/pulse/xmalloc.h | 6 +- 48 files changed, 699 insertions(+), 699 deletions(-) (limited to 'src/pulse') diff --git a/src/pulse/browser.c b/src/pulse/browser.c index dae8e3d5..4b0de029 100644 --- a/src/pulse/browser.c +++ b/src/pulse/browser.c @@ -2,26 +2,26 @@ /*** This file is part of PulseAudio. - + 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 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 +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif #include #include @@ -53,10 +53,10 @@ struct pa_browser { pa_browser_error_cb_t error_callback; void *error_userdata; - + AvahiClient *client; AvahiServiceBrowser *server_browser, *sink_browser, *source_browser; - + }; static int map_to_opcode(const char *type, int new) { @@ -84,7 +84,7 @@ static void resolve_callback( AvahiStringList *txt, AvahiLookupResultFlags flags, void *userdata) { - + pa_browser *b = userdata; pa_browse_info i; char ip[256], a[256]; @@ -94,7 +94,7 @@ static void resolve_callback( pa_sample_spec ss; int ss_valid = 0; char *key = NULL, *value = NULL; - + assert(b); memset(&i, 0, sizeof(i)); @@ -102,7 +102,7 @@ static void resolve_callback( if (event != AVAHI_RESOLVER_FOUND) goto fail; - + if (!b->callback) goto fail; @@ -119,10 +119,10 @@ static void resolve_callback( while (txt) { - + if (avahi_string_list_get_pair(txt, &key, &value, NULL) < 0) break; - + if (!strcmp(key, "device")) { device_found = 1; pa_xfree((char*) i.device); @@ -138,11 +138,11 @@ static void resolve_callback( value = NULL; } else if (!strcmp(key, "fqdn")) { size_t l; - + pa_xfree((char*) i.fqdn); i.fqdn = value; value = NULL; - + l = strlen(a); assert(l+1 <= sizeof(a)); strncat(a, " ", sizeof(a)-l-1); @@ -151,7 +151,7 @@ static void resolve_callback( if (pa_atou(value, &cookie) < 0) goto fail; - + i.cookie = &cookie; } else if (!strcmp(key, "description")) { pa_xfree((char*) i.description); @@ -159,13 +159,13 @@ static void resolve_callback( value = NULL; } else if (!strcmp(key, "channels")) { uint32_t ch; - + if (pa_atou(value, &ch) < 0 || ch <= 0 || ch > 255) goto fail; - + ss.channels = (uint8_t) ch; ss_valid |= 1; - + } else if (!strcmp(key, "rate")) { if (pa_atou(value, &ss.rate) < 0) goto fail; @@ -174,7 +174,7 @@ static void resolve_callback( if ((ss.format = pa_parse_sample_format(value)) == PA_SAMPLE_INVALID) goto fail; - + ss_valid |= 4; } @@ -186,7 +186,7 @@ static void resolve_callback( } /* No device txt record was sent for a sink or source service */ - if (opcode != PA_BROWSE_NEW_SERVER && !device_found) + if (opcode != PA_BROWSE_NEW_SERVER && !device_found) goto fail; if (ss_valid == 7) @@ -203,7 +203,7 @@ fail: pa_xfree(key); pa_xfree(value); - + avahi_service_resolver_free(r); } @@ -263,19 +263,19 @@ static void browse_callback( break; } - + case AVAHI_BROWSER_REMOVE: { if (b->callback) { pa_browse_info i; int opcode; - + memset(&i, 0, sizeof(i)); i.name = name; opcode = map_to_opcode(type, 0); assert(opcode >= 0); - + b->callback(b, opcode, &i, b->userdata); } break; @@ -285,7 +285,7 @@ static void browse_callback( handle_failure(b); break; } - + default: ; } @@ -313,7 +313,7 @@ pa_browser *pa_browser_new_full(pa_mainloop_api *mainloop, pa_browse_flags_t fla if (flags & ~(PA_BROWSE_FOR_SERVERS|PA_BROWSE_FOR_SINKS|PA_BROWSE_FOR_SOURCES) || flags == 0) return NULL; - + b = pa_xnew(pa_browser, 1); b->mainloop = mainloop; b->ref = 1; @@ -346,7 +346,7 @@ pa_browser *pa_browser_new_full(pa_mainloop_api *mainloop, pa_browse_flags_t fla *error_string = avahi_strerror(avahi_client_errno(b->client)); goto fail; } - + if ((flags & PA_BROWSE_FOR_SINKS) && !(b->sink_browser = avahi_service_browser_new( b->client, @@ -378,13 +378,13 @@ pa_browser *pa_browser_new_full(pa_mainloop_api *mainloop, pa_browse_flags_t fla *error_string = avahi_strerror(avahi_client_errno(b->client)); goto fail; } - + return b; fail: if (b) browser_free(b); - + return NULL; } @@ -403,7 +403,7 @@ static void browser_free(pa_browser *b) { if (b->avahi_poll) pa_avahi_poll_free(b->avahi_poll); - + pa_xfree(b); } diff --git a/src/pulse/browser.h b/src/pulse/browser.h index fc57a4d5..7b9aae8d 100644 --- a/src/pulse/browser.h +++ b/src/pulse/browser.h @@ -5,17 +5,17 @@ /*** This file is part of PulseAudio. - + 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 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 @@ -41,7 +41,7 @@ typedef enum pa_browse_opcode { PA_BROWSE_NEW_SINK, /**< New sink found */ PA_BROWSE_NEW_SOURCE, /**< New source found */ PA_BROWSE_REMOVE_SERVER, /**< Server disappeared */ - PA_BROWSE_REMOVE_SINK, /**< Sink disappeared */ + PA_BROWSE_REMOVE_SINK, /**< Sink disappeared */ PA_BROWSE_REMOVE_SOURCE /**< Source disappeared */ } pa_browse_opcode_t; diff --git a/src/pulse/cdecl.h b/src/pulse/cdecl.h index a3ec231c..09b9b84a 100644 --- a/src/pulse/cdecl.h +++ b/src/pulse/cdecl.h @@ -5,17 +5,17 @@ /*** This file is part of PulseAudio. - + 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 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 diff --git a/src/pulse/channelmap.c b/src/pulse/channelmap.c index 69b09089..40655cf5 100644 --- a/src/pulse/channelmap.c +++ b/src/pulse/channelmap.c @@ -2,17 +2,17 @@ /*** This file is part of PulseAudio. - + 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 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 @@ -35,23 +35,23 @@ const char *const table[] = { [PA_CHANNEL_POSITION_MONO] = "mono", - + [PA_CHANNEL_POSITION_FRONT_CENTER] = "front-center", [PA_CHANNEL_POSITION_FRONT_LEFT] = "front-left", [PA_CHANNEL_POSITION_FRONT_RIGHT] = "front-right", - + [PA_CHANNEL_POSITION_REAR_CENTER] = "rear-center", [PA_CHANNEL_POSITION_REAR_LEFT] = "rear-left", [PA_CHANNEL_POSITION_REAR_RIGHT] = "rear-right", - + [PA_CHANNEL_POSITION_LFE] = "lfe", - + [PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER] = "front-left-of-center", [PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER] = "front-right-of-center", - + [PA_CHANNEL_POSITION_SIDE_LEFT] = "side-left", [PA_CHANNEL_POSITION_SIDE_RIGHT] = "side-right", - + [PA_CHANNEL_POSITION_AUX0] = "aux0", [PA_CHANNEL_POSITION_AUX1] = "aux1", [PA_CHANNEL_POSITION_AUX2] = "aux2", @@ -86,7 +86,7 @@ const char *const table[] = { [PA_CHANNEL_POSITION_AUX31] = "aux31", [PA_CHANNEL_POSITION_TOP_CENTER] = "top-center", - + [PA_CHANNEL_POSITION_TOP_FRONT_LEFT] = "top-front-left", [PA_CHANNEL_POSITION_TOP_FRONT_RIGHT] = "top-front-right", [PA_CHANNEL_POSITION_TOP_FRONT_CENTER] = "top-front-center", @@ -140,14 +140,14 @@ pa_channel_map* pa_channel_map_init_auto(pa_channel_map *m, unsigned channels, p switch (def) { case PA_CHANNEL_MAP_AIFF: - + /* This is somewhat compatible with RFC3551 */ - + switch (channels) { case 1: m->map[0] = PA_CHANNEL_POSITION_MONO; return m; - + case 6: m->map[0] = PA_CHANNEL_POSITION_FRONT_LEFT; m->map[1] = PA_CHANNEL_POSITION_SIDE_LEFT; @@ -156,31 +156,31 @@ pa_channel_map* pa_channel_map_init_auto(pa_channel_map *m, unsigned channels, p m->map[4] = PA_CHANNEL_POSITION_SIDE_RIGHT; m->map[5] = PA_CHANNEL_POSITION_LFE; return m; - + case 5: m->map[2] = PA_CHANNEL_POSITION_FRONT_CENTER; m->map[3] = PA_CHANNEL_POSITION_REAR_LEFT; m->map[4] = PA_CHANNEL_POSITION_REAR_RIGHT; /* Fall through */ - + case 2: m->map[0] = PA_CHANNEL_POSITION_FRONT_LEFT; m->map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT; return m; - + case 3: m->map[0] = PA_CHANNEL_POSITION_LEFT; m->map[1] = PA_CHANNEL_POSITION_RIGHT; m->map[2] = PA_CHANNEL_POSITION_CENTER; return m; - + case 4: m->map[0] = PA_CHANNEL_POSITION_LEFT; m->map[1] = PA_CHANNEL_POSITION_CENTER; m->map[2] = PA_CHANNEL_POSITION_RIGHT; m->map[3] = PA_CHANNEL_POSITION_LFE; return m; - + default: return NULL; } @@ -191,43 +191,43 @@ pa_channel_map* pa_channel_map_init_auto(pa_channel_map *m, unsigned channels, p case 1: m->map[0] = PA_CHANNEL_POSITION_MONO; return m; - + case 8: m->map[6] = PA_CHANNEL_POSITION_SIDE_LEFT; m->map[7] = PA_CHANNEL_POSITION_SIDE_RIGHT; /* Fall through */ - + case 6: m->map[5] = PA_CHANNEL_POSITION_LFE; /* Fall through */ - + case 5: m->map[4] = PA_CHANNEL_POSITION_FRONT_CENTER; /* Fall through */ - + case 4: m->map[2] = PA_CHANNEL_POSITION_REAR_LEFT; m->map[3] = PA_CHANNEL_POSITION_REAR_RIGHT; /* Fall through */ - + case 2: m->map[0] = PA_CHANNEL_POSITION_FRONT_LEFT; m->map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT; return m; - + default: return NULL; } case PA_CHANNEL_MAP_AUX: { unsigned i; - + if (channels >= PA_CHANNELS_MAX) return NULL; for (i = 0; i < channels; i++) m->map[i] = PA_CHANNEL_POSITION_AUX0 + i; - + return m; } @@ -237,55 +237,55 @@ pa_channel_map* pa_channel_map_init_auto(pa_channel_map *m, unsigned channels, p case 1: m->map[0] = PA_CHANNEL_POSITION_MONO; return m; - + case 18: m->map[15] = PA_CHANNEL_POSITION_TOP_REAR_LEFT; m->map[16] = PA_CHANNEL_POSITION_TOP_REAR_CENTER; m->map[17] = PA_CHANNEL_POSITION_TOP_REAR_RIGHT; /* Fall through */ - + case 15: m->map[12] = PA_CHANNEL_POSITION_TOP_FRONT_LEFT; m->map[13] = PA_CHANNEL_POSITION_TOP_FRONT_CENTER; m->map[14] = PA_CHANNEL_POSITION_TOP_FRONT_RIGHT; /* Fall through */ - + case 12: m->map[11] = PA_CHANNEL_POSITION_TOP_CENTER; /* Fall through */ - + case 11: m->map[9] = PA_CHANNEL_POSITION_SIDE_LEFT; m->map[10] = PA_CHANNEL_POSITION_SIDE_RIGHT; /* Fall through */ - + case 9: m->map[8] = PA_CHANNEL_POSITION_REAR_CENTER; /* Fall through */ - + case 8: m->map[6] = PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER; m->map[7] = PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER; /* Fall through */ - + case 6: m->map[4] = PA_CHANNEL_POSITION_REAR_LEFT; m->map[5] = PA_CHANNEL_POSITION_REAR_RIGHT; /* Fall through */ - + case 4: m->map[3] = PA_CHANNEL_POSITION_LFE; /* Fall through */ - + case 3: m->map[2] = PA_CHANNEL_POSITION_FRONT_CENTER; /* Fall through */ - + case 2: m->map[0] = PA_CHANNEL_POSITION_FRONT_LEFT; m->map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT; return m; - + default: return NULL; } @@ -296,12 +296,12 @@ pa_channel_map* pa_channel_map_init_auto(pa_channel_map *m, unsigned channels, p case 1: m->map[0] = PA_CHANNEL_POSITION_MONO; return m; - + case 8: m->map[6] = PA_CHANNEL_POSITION_REAR_LEFT; m->map[7] = PA_CHANNEL_POSITION_REAR_RIGHT; /* Fall through */ - + case 6: m->map[4] = PA_CHANNEL_POSITION_SIDE_LEFT; m->map[5] = PA_CHANNEL_POSITION_SIDE_RIGHT; @@ -310,20 +310,20 @@ pa_channel_map* pa_channel_map_init_auto(pa_channel_map *m, unsigned channels, p case 4: m->map[3] = PA_CHANNEL_POSITION_LFE; /* Fall through */ - + case 3: m->map[2] = PA_CHANNEL_POSITION_FRONT_CENTER; /* Fall through */ - + case 2: m->map[0] = PA_CHANNEL_POSITION_FRONT_LEFT; m->map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT; return m; - + default: return NULL; } - + default: return NULL; @@ -341,13 +341,13 @@ const char* pa_channel_position_to_string(pa_channel_position_t pos) { int pa_channel_map_equal(const pa_channel_map *a, const pa_channel_map *b) { unsigned c; - + assert(a); assert(b); if (a->channels != b->channels) return 0; - + for (c = 0; c < a->channels; c++) if (a->map[c] != b->map[c]) return 0; @@ -359,7 +359,7 @@ char* pa_channel_map_snprint(char *s, size_t l, const pa_channel_map *map) { unsigned channel; int first = 1; char *e; - + assert(s); assert(l > 0); assert(map); @@ -382,7 +382,7 @@ pa_channel_map *pa_channel_map_parse(pa_channel_map *rmap, const char *s) { const char *state; pa_channel_map map; char *p; - + assert(rmap); assert(s); @@ -397,14 +397,14 @@ pa_channel_map *pa_channel_map_parse(pa_channel_map *rmap, const char *s) { state = NULL; map.channels = 0; - + while ((p = pa_split(s, ",", &state))) { if (map.channels >= PA_CHANNELS_MAX) { pa_xfree(p); return NULL; } - + /* Some special aliases */ if (strcmp(p, "left") == 0) map.map[map.channels++] = PA_CHANNEL_POSITION_LEFT; @@ -416,13 +416,13 @@ pa_channel_map *pa_channel_map_parse(pa_channel_map *rmap, const char *s) { map.map[map.channels++] = PA_CHANNEL_POSITION_SUBWOOFER; else { pa_channel_position_t i; - + for (i = 0; i < PA_CHANNEL_POSITION_MAX; i++) if (strcmp(p, table[i]) == 0) { map.map[map.channels++] = i; break; } - + if (i >= PA_CHANNEL_POSITION_MAX) { pa_xfree(p); return NULL; @@ -433,24 +433,24 @@ pa_channel_map *pa_channel_map_parse(pa_channel_map *rmap, const char *s) { } finish: - + if (!pa_channel_map_valid(&map)) return NULL; - + *rmap = map; return rmap; } int pa_channel_map_valid(const pa_channel_map *map) { unsigned c; - + assert(map); if (map->channels <= 0 || map->channels > PA_CHANNELS_MAX) return 0; for (c = 0; c < map->channels; c++) { - + if (map->map[c] < 0 ||map->map[c] >= PA_CHANNEL_POSITION_MAX) return 0; diff --git a/src/pulse/channelmap.h b/src/pulse/channelmap.h index 8a39ade8..20380251 100644 --- a/src/pulse/channelmap.h +++ b/src/pulse/channelmap.h @@ -5,17 +5,17 @@ /*** This file is part of PulseAudio. - + 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 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 @@ -72,7 +72,7 @@ typedef enum pa_channel_position { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT, PA_CHANNEL_POSITION_CENTER, - + PA_CHANNEL_POSITION_FRONT_LEFT = PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT = PA_CHANNEL_POSITION_RIGHT, PA_CHANNEL_POSITION_FRONT_CENTER = PA_CHANNEL_POSITION_CENTER, @@ -80,13 +80,13 @@ typedef enum pa_channel_position { PA_CHANNEL_POSITION_REAR_CENTER, PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT, - + PA_CHANNEL_POSITION_LFE, PA_CHANNEL_POSITION_SUBWOOFER = PA_CHANNEL_POSITION_LFE, - + PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER, PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER, - + PA_CHANNEL_POSITION_SIDE_LEFT, PA_CHANNEL_POSITION_SIDE_RIGHT, @@ -124,7 +124,7 @@ typedef enum pa_channel_position { PA_CHANNEL_POSITION_AUX31, PA_CHANNEL_POSITION_TOP_CENTER, - + PA_CHANNEL_POSITION_TOP_FRONT_LEFT, PA_CHANNEL_POSITION_TOP_FRONT_RIGHT, PA_CHANNEL_POSITION_TOP_FRONT_CENTER, @@ -132,7 +132,7 @@ typedef enum pa_channel_position { PA_CHANNEL_POSITION_TOP_REAR_LEFT, PA_CHANNEL_POSITION_TOP_REAR_RIGHT, PA_CHANNEL_POSITION_TOP_REAR_CENTER, - + PA_CHANNEL_POSITION_MAX } pa_channel_position_t; @@ -143,7 +143,7 @@ typedef enum pa_channel_map_def { PA_CHANNEL_MAP_AUX, /**< Only aux channels */ PA_CHANNEL_MAP_WAVEEX, /**< Microsoft's WAVEFORMATEXTENSIBLE mapping */ PA_CHANNEL_MAP_OSS, /**< The default channel mapping used by OSS as defined in the OSS 4.0 API specs */ - + PA_CHANNEL_MAP_DEFAULT = PA_CHANNEL_MAP_AIFF /**< The default channel map */ } pa_channel_map_def_t; diff --git a/src/pulse/client-conf-x11.c b/src/pulse/client-conf-x11.c index 8cedc48b..78e190e5 100644 --- a/src/pulse/client-conf-x11.c +++ b/src/pulse/client-conf-x11.c @@ -44,7 +44,7 @@ int pa_client_conf_from_x11(pa_client_conf *c, const char *dname) { if (!dname && !getenv("DISPLAY")) goto finish; - + if (!(d = XOpenDisplay(dname))) { pa_log("XOpenDisplay() failed"); goto finish; @@ -89,5 +89,5 @@ finish: XCloseDisplay(d); return ret; - + } diff --git a/src/pulse/client-conf.c b/src/pulse/client-conf.c index 5cd7e3ed..b652a25b 100644 --- a/src/pulse/client-conf.c +++ b/src/pulse/client-conf.c @@ -69,11 +69,11 @@ static const pa_client_conf default_conf = { pa_client_conf *pa_client_conf_new(void) { pa_client_conf *c = pa_xmemdup(&default_conf, sizeof(default_conf)); - + c->daemon_binary = pa_xstrdup(PA_BINARY); c->extra_arguments = pa_xstrdup("--log-target=syslog --exit-idle-time=5"); c->cookie_file = pa_xstrdup(PA_NATIVE_COOKIE_FILE); - + return c; } @@ -122,25 +122,25 @@ int pa_client_conf_load(pa_client_conf *c, const char *filename) { pa_log("WARNING: failed to open configuration file '%s': %s", fn, pa_cstrerror(errno)); goto finish; } - + r = f ? pa_config_parse(fn, f, table, NULL) : 0; if (!r) r = pa_client_conf_load_cookie(c); - + finish: pa_xfree(fn); if (f) fclose(f); - + return r; } int pa_client_conf_env(pa_client_conf *c) { char *e; - + if ((e = getenv(ENV_DEFAULT_SINK))) { pa_xfree(c->default_sink); c->default_sink = pa_xstrdup(e); @@ -155,7 +155,7 @@ int pa_client_conf_env(pa_client_conf *c) { pa_xfree(c->default_server); c->default_server = pa_xstrdup(e); } - + if ((e = getenv(ENV_DAEMON_BINARY))) { pa_xfree(c->daemon_binary); c->daemon_binary = pa_xstrdup(e); @@ -167,7 +167,7 @@ int pa_client_conf_env(pa_client_conf *c) { return pa_client_conf_load_cookie(c); } - + return 0; } diff --git a/src/pulse/context.c b/src/pulse/context.c index a458c6b1..7ef43b30 100644 --- a/src/pulse/context.c +++ b/src/pulse/context.c @@ -2,17 +2,17 @@ /*** This file is part of PulseAudio. - + 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 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 @@ -88,11 +88,11 @@ static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = { static void unlock_autospawn_lock_file(pa_context *c) { assert(c); - + if (c->autospawn_lock_fd >= 0) { char lf[PATH_MAX]; pa_runtime_path(AUTOSPAWN_LOCK, lf, sizeof(lf)); - + pa_unlock_lockfile(lf, c->autospawn_lock_fd); c->autospawn_lock_fd = -1; } @@ -102,10 +102,10 @@ static void context_free(pa_context *c); pa_context *pa_context_new(pa_mainloop_api *mainloop, const char *name) { pa_context *c; - + assert(mainloop); assert(name); - + c = pa_xnew(pa_context, 1); c->ref = 1; c->name = pa_xstrdup(name); @@ -118,7 +118,7 @@ pa_context *pa_context_new(pa_mainloop_api *mainloop, const char *name) { PA_LLIST_HEAD_INIT(pa_stream, c->streams); PA_LLIST_HEAD_INIT(pa_operation, c->operations); - + c->error = PA_OK; c->state = PA_CONTEXT_UNCONNECTED; c->ctag = 0; @@ -138,7 +138,7 @@ pa_context *pa_context_new(pa_mainloop_api *mainloop, const char *name) { c->do_autospawn = 0; #ifndef MSG_NOSIGNAL -#ifdef SIGPIPE +#ifdef SIGPIPE pa_check_signal_is_blocked(SIGPIPE); #endif #endif @@ -183,7 +183,7 @@ static void context_free(pa_context *c) { pa_pstream_close(c->pstream); pa_pstream_unref(c->pstream); } - + if (c->record_streams) pa_dynarray_free(c->record_streams, NULL, NULL); if (c->playback_streams) @@ -196,7 +196,7 @@ static void context_free(pa_context *c) { pa_client_conf_free(c->conf); pa_strlist_free(c->server_list); - + pa_xfree(c->name); pa_xfree(c->server); pa_xfree(c); @@ -205,7 +205,7 @@ static void context_free(pa_context *c) { pa_context* pa_context_ref(pa_context *c) { assert(c); assert(c->ref >= 1); - + c->ref++; return c; } @@ -221,7 +221,7 @@ void pa_context_unref(pa_context *c) { void pa_context_set_state(pa_context *c, pa_context_state_t st) { assert(c); assert(c->ref >= 1); - + if (c->state == st) return; @@ -233,7 +233,7 @@ void pa_context_set_state(pa_context *c, pa_context_state_t st) { if (st == PA_CONTEXT_FAILED || st == PA_CONTEXT_TERMINATED) { pa_stream *s; - + s = c->streams ? pa_stream_ref(c->streams) : NULL; while (s) { pa_stream *n = s->next ? pa_stream_ref(s->next) : NULL; @@ -245,13 +245,13 @@ void pa_context_set_state(pa_context *c, pa_context_state_t st) { if (c->pdispatch) pa_pdispatch_unref(c->pdispatch); c->pdispatch = NULL; - + if (c->pstream) { pa_pstream_close(c->pstream); pa_pstream_unref(c->pstream); } c->pstream = NULL; - + if (c->client) pa_socket_client_unref(c->client); c->client = NULL; @@ -263,7 +263,7 @@ void pa_context_set_state(pa_context *c, pa_context_state_t st) { void pa_context_fail(pa_context *c, int error) { assert(c); assert(c->ref >= 1); - + pa_context_set_error(c, error); pa_context_set_state(c, PA_CONTEXT_FAILED); } @@ -283,19 +283,19 @@ static void pstream_die_callback(pa_pstream *p, void *userdata) { assert(p); assert(c); - + pa_context_fail(c, PA_ERR_CONNECTIONTERMINATED); } static void pstream_packet_callback(pa_pstream *p, pa_packet *packet, const pa_creds *creds, void *userdata) { pa_context *c = userdata; - + assert(p); assert(packet); assert(c); pa_context_ref(c); - + if (pa_pdispatch_run(c->pdispatch, packet, creds, c) < 0) pa_context_fail(c, PA_ERR_PROTOCOL); @@ -305,7 +305,7 @@ static void pstream_packet_callback(pa_pstream *p, pa_packet *packet, const pa_c static void pstream_memblock_callback(pa_pstream *p, uint32_t channel, int64_t offset, pa_seek_mode_t seek, const pa_memchunk *chunk, void *userdata) { pa_context *c = userdata; pa_stream *s; - + assert(p); assert(chunk); assert(chunk->memblock); @@ -321,7 +321,7 @@ static void pstream_memblock_callback(pa_pstream *p, uint32_t channel, int64_t o pa_memblockq_seek(s->record_memblockq, offset, seek); pa_memblockq_push_align(s->record_memblockq, chunk); - + if (s->read_callback) { size_t l; @@ -339,11 +339,11 @@ int pa_context_handle_error(pa_context *c, uint32_t command, pa_tagstruct *t) { if (command == PA_COMMAND_ERROR) { assert(t); - + if (pa_tagstruct_getu32(t, &c->error) < 0) { pa_context_fail(c, PA_ERR_PROTOCOL); return -1; - + } } else if (command == PA_COMMAND_TIMEOUT) c->error = PA_ERR_TIMEOUT; @@ -357,15 +357,15 @@ int pa_context_handle_error(pa_context *c, uint32_t command, pa_tagstruct *t) { static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { pa_context *c = userdata; - + assert(pd); assert(c); assert(c->state == PA_CONTEXT_AUTHORIZING || c->state == PA_CONTEXT_SETTING_NAME); pa_context_ref(c); - + if (command != PA_COMMAND_REPLY) { - + if (pa_context_handle_error(c, command, t) < 0) pa_context_fail(c, PA_ERR_PROTOCOL); @@ -398,7 +398,7 @@ static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t * user. This is a security measure because otherwise * data private to the user might leak. */ -#ifdef HAVE_CREDS +#ifdef HAVE_CREDS const pa_creds *creds; if ((creds = pa_pdispatch_creds(pd))) if (getuid() == creds->uid) @@ -418,7 +418,7 @@ static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t case PA_CONTEXT_SETTING_NAME : pa_context_set_state(c, PA_CONTEXT_READY); break; - + default: assert(0); } @@ -430,12 +430,12 @@ finish: static void setup_context(pa_context *c, pa_iochannel *io) { pa_tagstruct *t; uint32_t tag; - + assert(c); assert(io); pa_context_ref(c); - + assert(!c->pstream); c->pstream = pa_pstream_new(c->mainloop, io, c->mempool); @@ -462,13 +462,13 @@ static void setup_context(pa_context *c, pa_iochannel *io) { ucred.uid = getuid(); ucred.gid = getgid(); - + pa_pstream_send_tagstruct_with_creds(c->pstream, t, &ucred); } #else pa_pstream_send_tagstruct(c->pstream, t); #endif - + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, setup_complete_callback, c, NULL); pa_context_set_state(c, PA_CONTEXT_AUTHORIZING); @@ -487,7 +487,7 @@ static int context_connect_spawn(pa_context *c) { pa_iochannel *io; pa_context_ref(c); - + if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) < 0) { pa_log("socketpair(): %s", pa_cstrerror(errno)); pa_context_fail(c, PA_ERR_INTERNAL); @@ -495,7 +495,7 @@ static int context_connect_spawn(pa_context *c) { } pa_fd_set_cloexec(fds[0], 1); - + pa_socket_low_delay(fds[0]); pa_socket_low_delay(fds[1]); @@ -508,7 +508,7 @@ static int context_connect_spawn(pa_context *c) { if (c->spawn_api.postfork) c->spawn_api.postfork(); - + goto fail; } else if (!pid) { /* Child */ @@ -521,17 +521,17 @@ static int context_connect_spawn(pa_context *c) { /* Not required, since fds[0] has CLOEXEC enabled anyway */ close(fds[0]); - + if (c->spawn_api.atfork) c->spawn_api.atfork(); /* Setup argv */ n = 0; - + argv[n++] = c->conf->daemon_binary; argv[n++] = "--daemonize=yes"; - + snprintf(t, sizeof(t), "-Lmodule-native-protocol-fd fd=%i", fds[1]); argv[n++] = strdup(t); @@ -540,7 +540,7 @@ static int context_connect_spawn(pa_context *c) { if (!(a = pa_split_spaces(c->conf->extra_arguments, &state))) break; - + argv[n++] = a; } @@ -549,7 +549,7 @@ static int context_connect_spawn(pa_context *c) { execv(argv[0], (char * const *) argv); _exit(1); #undef MAX_ARGS - } + } /* Parent */ @@ -557,7 +557,7 @@ static int context_connect_spawn(pa_context *c) { if (c->spawn_api.postfork) c->spawn_api.postfork(); - + if (r < 0) { pa_log("waitpid(): %s", pa_cstrerror(errno)); pa_context_fail(c, PA_ERR_INTERNAL); @@ -570,7 +570,7 @@ static int context_connect_spawn(pa_context *c) { close(fds[1]); c->is_local = 1; - + io = pa_iochannel_new(c->mainloop, fds[0], fds[0]); setup_context(c, io); @@ -598,16 +598,16 @@ fail: static int try_next_connection(pa_context *c) { char *u = NULL; int r = -1; - + assert(c); assert(!c->client); for (;;) { pa_xfree(u); u = NULL; - + c->server_list = pa_strlist_pop(c->server_list, &u); - + if (!u) { #ifndef OS_IS_WIN32 @@ -616,19 +616,19 @@ static int try_next_connection(pa_context *c) { goto finish; } #endif - + pa_context_fail(c, PA_ERR_CONNECTIONREFUSED); goto finish; } - - pa_log_debug("Trying to connect to %s...", u); + + pa_log_debug("Trying to connect to %s...", u); pa_xfree(c->server); c->server = pa_xstrdup(u); if (!(c->client = pa_socket_client_new_string(c->mainloop, u, PA_NATIVE_DEFAULT_PORT))) continue; - + c->is_local = pa_socket_client_is_local(c->client); pa_socket_client_set_callback(c->client, on_connection, c); break; @@ -638,13 +638,13 @@ static int try_next_connection(pa_context *c) { finish: pa_xfree(u); - + return r; } static void on_connection(pa_socket_client *client, pa_iochannel*io, void *userdata) { pa_context *c = userdata; - + assert(client); assert(c); assert(c->state == PA_CONTEXT_CONNECTING); @@ -677,9 +677,9 @@ int pa_context_connect( const char *server, pa_context_flags_t flags, const pa_spawn_api *api) { - + int r = -1; - + assert(c); assert(c->ref >= 1); @@ -693,7 +693,7 @@ int pa_context_connect( pa_context_ref(c); assert(!c->server_list); - + if (server) { if (!(c->server_list = pa_strlist_parse(server))) { pa_context_fail(c, PA_ERR_INVALIDSERVER); @@ -704,7 +704,7 @@ int pa_context_connect( char ufn[PATH_MAX]; /* Prepend in reverse order */ - + if ((d = getenv("DISPLAY"))) { char *e; d = pa_xstrdup(d); @@ -716,7 +716,7 @@ int pa_context_connect( pa_xfree(d); } - + c->server_list = pa_strlist_prepend(c->server_list, "tcp6:localhost"); c->server_list = pa_strlist_prepend(c->server_list, "tcp4:localhost"); @@ -744,38 +744,38 @@ int pa_context_connect( pa_context_set_state(c, PA_CONTEXT_CONNECTING); r = try_next_connection(c); - + finish: pa_context_unref(c); - + return r; } void pa_context_disconnect(pa_context *c) { assert(c); assert(c->ref >= 1); - + pa_context_set_state(c, PA_CONTEXT_TERMINATED); } pa_context_state_t pa_context_get_state(pa_context *c) { assert(c); assert(c->ref >= 1); - + return c->state; } int pa_context_errno(pa_context *c) { assert(c); assert(c->ref >= 1); - + return c->error; } void pa_context_set_state_callback(pa_context *c, pa_context_notify_cb_t cb, void *userdata) { assert(c); assert(c->ref >= 1); - + c->state_callback = cb; c->state_userdata = userdata; } @@ -807,7 +807,7 @@ static void pstream_drain_callback(PA_GCC_UNUSED pa_pstream *s, void *userdata) static void set_dispatch_callbacks(pa_operation *o) { int done = 1; - + assert(o); assert(o->ref >= 1); assert(o->context); @@ -816,7 +816,7 @@ static void set_dispatch_callbacks(pa_operation *o) { pa_pstream_set_drain_callback(o->context->pstream, NULL, NULL); pa_pdispatch_set_drain_callback(o->context->pdispatch, NULL, NULL); - + if (pa_pdispatch_is_pending(o->context->pdispatch)) { pa_pdispatch_set_drain_callback(o->context->pdispatch, pdispatch_drain_callback, o); done = 0; @@ -832,7 +832,7 @@ static void set_dispatch_callbacks(pa_operation *o) { pa_context_notify_cb_t cb = (pa_context_notify_cb_t) o->callback; cb(o->context, o->userdata); } - + pa_operation_done(o); pa_operation_unref(o); } @@ -840,13 +840,13 @@ static void set_dispatch_callbacks(pa_operation *o) { pa_operation* pa_context_drain(pa_context *c, pa_context_notify_cb_t cb, void *userdata) { pa_operation *o; - + assert(c); assert(c->ref >= 1); PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(c, pa_context_is_pending(c), PA_ERR_BADSTATE); - + o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); set_dispatch_callbacks(pa_operation_ref(o)); @@ -856,7 +856,7 @@ pa_operation* pa_context_drain(pa_context *c, pa_context_notify_cb_t cb, void *u void pa_context_simple_ack_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { pa_operation *o = userdata; int success = 1; - + assert(pd); assert(o); assert(o->ref >= 1); @@ -907,12 +907,12 @@ pa_operation* pa_context_send_simple_command(pa_context *c, uint32_t command, pa pa_tagstruct *t; pa_operation *o; uint32_t tag; - + assert(c); assert(c->ref >= 1); PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); - + o = pa_operation_new(c, NULL, cb, userdata); t = pa_tagstruct_command(c, command, &tag); @@ -926,7 +926,7 @@ pa_operation* pa_context_set_default_sink(pa_context *c, const char *name, pa_co pa_tagstruct *t; pa_operation *o; uint32_t tag; - + assert(c); assert(c->ref >= 1); @@ -951,7 +951,7 @@ pa_operation* pa_context_set_default_source(pa_context *c, const char *name, pa_ assert(c->ref >= 1); PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); - + o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); t = pa_tagstruct_command(c, PA_COMMAND_SET_DEFAULT_SOURCE, &tag); @@ -964,7 +964,7 @@ pa_operation* pa_context_set_default_source(pa_context *c, const char *name, pa_ int pa_context_is_local(pa_context *c) { assert(c); - + return c->is_local; } @@ -978,7 +978,7 @@ pa_operation* pa_context_set_name(pa_context *c, const char *name, pa_context_su assert(name); PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); - + o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); t = pa_tagstruct_command(c, PA_COMMAND_SET_CLIENT_NAME, &tag); @@ -999,12 +999,12 @@ const char* pa_context_get_server(pa_context *c) { if (!c->server) return NULL; - + if (*c->server == '{') { char *e = strchr(c->server+1, '}'); return e ? e+1 : c->server; } - + return c->server; } @@ -1024,7 +1024,7 @@ pa_tagstruct *pa_tagstruct_command(pa_context *c, uint32_t command, uint32_t *ta assert(c); assert(tag); - + t = pa_tagstruct_new(NULL, 0); pa_tagstruct_putu32(t, command); pa_tagstruct_putu32(t, *tag = c->ctag++); diff --git a/src/pulse/context.h b/src/pulse/context.h index 661ff617..048ed17f 100644 --- a/src/pulse/context.h +++ b/src/pulse/context.h @@ -5,17 +5,17 @@ /*** This file is part of PulseAudio. - + 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 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 @@ -50,7 +50,7 @@ * The abstraction is represented as a number of function pointers in the * pa_mainloop_api structure. * - * To actually be able to use these functions, an implementation needs to + * To actually be able to use these functions, an implementation needs to * be coupled to the abstraction. There are three of these shipped with * PulseAudio, but any other can be used with a minimal ammount of work, * provided it supports the three basic events listed above. @@ -76,7 +76,7 @@ * and decrease their reference counts. Whenever an object's reference * count reaches zero, that object gets destroy and any resources it uses * get freed. - * + * * The benefit of this design is that an application need not worry about * whether or not it needs to keep an object around in case the library is * using it internally. If it is, then it has made sure it has its own diff --git a/src/pulse/def.h b/src/pulse/def.h index a22e3c19..fb04de50 100644 --- a/src/pulse/def.h +++ b/src/pulse/def.h @@ -5,17 +5,17 @@ /*** This file is part of PulseAudio. - + 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 @@ -69,7 +69,7 @@ typedef enum pa_context_flags { PA_CONTEXT_NOAUTOSPAWN = 1 /**< Disabled autospawning of the PulseAudio daemon if required */ } pa_context_flags_t; -/** The direction of a pa_stream object */ +/** The direction of a pa_stream object */ typedef enum pa_stream_direction { PA_STREAM_NODIRECTION, /**< Invalid direction */ PA_STREAM_PLAYBACK, /**< Playback stream */ @@ -149,7 +149,7 @@ enum { PA_ERR_EXIST, /**< Entity exists */ PA_ERR_NOENTITY, /**< No such entity */ PA_ERR_CONNECTIONREFUSED, /**< Connection refused */ - PA_ERR_PROTOCOL, /**< Protocol error */ + PA_ERR_PROTOCOL, /**< Protocol error */ PA_ERR_TIMEOUT, /**< Timeout */ PA_ERR_AUTHKEY, /**< No authorization key */ PA_ERR_INTERNAL, /**< Internal error */ @@ -207,7 +207,7 @@ typedef enum pa_subscription_event_type { * total output latency a sample that is written with * pa_stream_write() takes to be played may be estimated by * sink_usec+buffer_usec+transport_usec. (where buffer_usec is defined - * as pa_bytes_to_usec(write_index-read_index)) The output buffer + * as pa_bytes_to_usec(write_index-read_index)) The output buffer * which buffer_usec relates to may be manipulated freely (with * pa_stream_write()'s seek argument, pa_stream_flush() and friends), * the buffers sink_usec and source_usec relate to are first-in @@ -256,7 +256,7 @@ typedef struct pa_timing_info { * flush request that corrupted it has * been issued in the time since this * latency info was current. \since 0.8 */ - + int64_t read_index; /**< Current read index into the * playback buffer in bytes. Think twice before * using this for seeking purposes: it @@ -288,7 +288,7 @@ typedef struct pa_spawn_api { /** Seek type for pa_stream_write(). \since 0.8*/ typedef enum pa_seek_mode { PA_SEEK_RELATIVE = 0, /**< Seek relatively to the write index */ - PA_SEEK_ABSOLUTE = 1, /**< Seek relatively to the start of the buffer queue */ + PA_SEEK_ABSOLUTE = 1, /**< Seek relatively to the start of the buffer queue */ PA_SEEK_RELATIVE_ON_READ = 2, /**< Seek relatively to the read index. */ PA_SEEK_RELATIVE_END = 3 /**< Seek relatively to the current end of the buffer queue. */ } pa_seek_mode_t; diff --git a/src/pulse/error.c b/src/pulse/error.c index 7bd31ead..3b9a60a4 100644 --- a/src/pulse/error.c +++ b/src/pulse/error.c @@ -2,17 +2,17 @@ /*** This file is part of PulseAudio. - + 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 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 diff --git a/src/pulse/error.h b/src/pulse/error.h index bfce023c..c96349a6 100644 --- a/src/pulse/error.h +++ b/src/pulse/error.h @@ -5,17 +5,17 @@ /*** This file is part of PulseAudio. - + 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 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 diff --git a/src/pulse/glib-mainloop.c b/src/pulse/glib-mainloop.c index 201b6e23..1669acdd 100644 --- a/src/pulse/glib-mainloop.c +++ b/src/pulse/glib-mainloop.c @@ -2,17 +2,17 @@ /*** This file is part of PulseAudio. - + 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 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 @@ -69,7 +69,7 @@ struct pa_defer_event { int dead; int enabled; - + pa_defer_event_cb_t callback; void *userdata; pa_defer_event_destroy_cb_t destroy_callback; @@ -79,7 +79,7 @@ struct pa_defer_event { struct pa_glib_mainloop { GSource source; - + pa_mainloop_api api; GMainContext *context; @@ -102,7 +102,7 @@ static void cleanup_io_events(pa_glib_mainloop *g, int force) { if (!force && g->io_events_please_scan <= 0) break; - + if (force || e->dead) { PA_LLIST_REMOVE(pa_io_event, g->io_events, e); @@ -110,13 +110,13 @@ static void cleanup_io_events(pa_glib_mainloop *g, int force) { g_assert(g->io_events_please_scan > 0); g->io_events_please_scan--; } - + if (e->poll_fd_added) g_source_remove_poll(&g->source, &e->poll_fd); - + if (e->destroy_callback) e->destroy_callback(&g->api, e, e->userdata); - + pa_xfree(e); } @@ -135,7 +135,7 @@ static void cleanup_time_events(pa_glib_mainloop *g, int force) { if (!force && g->time_events_please_scan <= 0) break; - + if (force || e->dead) { PA_LLIST_REMOVE(pa_time_event, g->time_events, e); @@ -148,10 +148,10 @@ static void cleanup_time_events(pa_glib_mainloop *g, int force) { g_assert(g->n_enabled_time_events > 0); g->n_enabled_time_events--; } - + if (e->destroy_callback) e->destroy_callback(&g->api, e, e->userdata); - + pa_xfree(e); } @@ -170,7 +170,7 @@ static void cleanup_defer_events(pa_glib_mainloop *g, int force) { if (!force && g->defer_events_please_scan <= 0) break; - + if (force || e->dead) { PA_LLIST_REMOVE(pa_defer_event, g->defer_events, e); @@ -183,10 +183,10 @@ static void cleanup_defer_events(pa_glib_mainloop *g, int force) { g_assert(g->n_enabled_defer_events > 0); g->n_enabled_defer_events--; } - + if (e->destroy_callback) e->destroy_callback(&g->api, e, e->userdata); - + pa_xfree(e); } @@ -218,7 +218,7 @@ static pa_io_event* glib_io_new( pa_io_event_flags_t f, pa_io_event_cb_t cb, void *userdata) { - + pa_io_event *e; pa_glib_mainloop *g; @@ -226,7 +226,7 @@ static pa_io_event* glib_io_new( g_assert(m->userdata); g_assert(fd >= 0); g_assert(cb); - + g = m->userdata; e = pa_xnew(pa_io_event, 1); @@ -236,7 +236,7 @@ static pa_io_event* glib_io_new( e->poll_fd.fd = fd; e->poll_fd.events = map_flags_to_glib(f); e->poll_fd.revents = 0; - + e->callback = cb; e->userdata = userdata; e->destroy_callback = NULL; @@ -245,7 +245,7 @@ static pa_io_event* glib_io_new( g_source_add_poll(&g->source, &e->poll_fd); e->poll_fd_added = 1; - + return e; } @@ -272,7 +272,7 @@ static void glib_io_free(pa_io_event*e) { static void glib_io_set_destroy(pa_io_event*e, pa_io_event_destroy_cb_t cb) { g_assert(e); g_assert(!e->dead); - + e->destroy_callback = cb; } @@ -283,14 +283,14 @@ static pa_time_event* glib_time_new( const struct timeval *tv, pa_time_event_cb_t cb, void *userdata) { - + pa_glib_mainloop *g; pa_time_event *e; - + g_assert(m); g_assert(m->userdata); g_assert(cb); - + g = m->userdata; e = pa_xnew(pa_time_event, 1); @@ -308,13 +308,13 @@ static pa_time_event* glib_time_new( g->cached_next_time_event = e; } } - + e->callback = cb; e->userdata = userdata; e->destroy_callback = NULL; PA_LLIST_PREPEND(pa_time_event, g->time_events, e); - + return e; } @@ -328,12 +328,12 @@ static void glib_time_restart(pa_time_event*e, const struct timeval *tv) { } else if (!e->enabled && tv) e->mainloop->n_enabled_time_events++; - if ((e->enabled = !!tv)) + if ((e->enabled = !!tv)) e->timeval = *tv; if (e->mainloop->cached_next_time_event && e->enabled) { g_assert(e->mainloop->cached_next_time_event->enabled); - + if (pa_timeval_cmp(tv, &e->mainloop->cached_next_time_event->timeval) < 0) e->mainloop->cached_next_time_event = e; } else if (e->mainloop->cached_next_time_event == e) @@ -357,7 +357,7 @@ static void glib_time_free(pa_time_event *e) { static void glib_time_set_destroy(pa_time_event *e, pa_time_event_destroy_cb_t cb) { g_assert(e); g_assert(!e->dead); - + e->destroy_callback = cb; } @@ -367,27 +367,27 @@ static pa_defer_event* glib_defer_new( pa_mainloop_api*m, pa_defer_event_cb_t cb, void *userdata) { - + pa_defer_event *e; pa_glib_mainloop *g; g_assert(m); g_assert(m->userdata); g_assert(cb); - + g = m->userdata; - + e = pa_xnew(pa_defer_event, 1); e->mainloop = g; e->dead = 0; e->enabled = 1; g->n_enabled_defer_events++; - + e->callback = cb; e->userdata = userdata; e->destroy_callback = NULL; - + PA_LLIST_PREPEND(pa_defer_event, g->defer_events, e); return e; } @@ -430,7 +430,7 @@ static void glib_defer_set_destroy(pa_defer_event *e, pa_defer_event_destroy_cb_ static void glib_quit(pa_mainloop_api*a, PA_GCC_UNUSED int retval) { g_warning("quit() ignored"); - + /* NOOP */ } @@ -440,7 +440,7 @@ static pa_time_event* find_next_time_event(pa_glib_mainloop *g) { if (g->cached_next_time_event) return g->cached_next_time_event; - + for (t = g->time_events; t; t = t->next) { if (t->dead || !t->enabled) @@ -461,7 +461,7 @@ static pa_time_event* find_next_time_event(pa_glib_mainloop *g) { static void scan_dead(pa_glib_mainloop *g) { g_assert(g); - + if (g->io_events_please_scan) cleanup_io_events(g, 0); @@ -499,7 +499,7 @@ static gboolean prepare_func(GSource *source, gint *timeout) { if (pa_timeval_cmp(&t->timeval, &tvnow) <= 0) { *timeout = 0; return TRUE; - } + } usec = pa_timeval_diff(&t->timeval, &tvnow); *timeout = (gint) (usec / 1000); } else @@ -519,10 +519,10 @@ static gboolean check_func(GSource *source) { pa_time_event *t; GTimeVal now; struct timeval tvnow; - + t = find_next_time_event(g); g_assert(t); - + g_source_get_current_time(source, &now); tvnow.tv_sec = now.tv_sec; tvnow.tv_usec = now.tv_usec; @@ -555,7 +555,7 @@ static gboolean dispatch_func(GSource *source, PA_GCC_UNUSED GSourceFunc callbac } g_assert(d); - + d->callback(&g->api, d, d->userdata); return TRUE; } @@ -567,7 +567,7 @@ static gboolean dispatch_func(GSource *source, PA_GCC_UNUSED GSourceFunc callbac t = find_next_time_event(g); g_assert(t); - + g_source_get_current_time(source, &now); tvnow.tv_sec = now.tv_sec; tvnow.tv_usec = now.tv_usec; @@ -576,7 +576,7 @@ static gboolean dispatch_func(GSource *source, PA_GCC_UNUSED GSourceFunc callbac /* Disable time event */ glib_time_restart(t, NULL); - + t->callback(&g->api, t, &t->timeval, t->userdata); return TRUE; } @@ -604,12 +604,12 @@ static const pa_mainloop_api vtable = { .time_restart = glib_time_restart, .time_free = glib_time_free, .time_set_destroy = glib_time_set_destroy, - + .defer_new = glib_defer_new, .defer_enable = glib_defer_enable, .defer_free = glib_defer_free, .defer_set_destroy = glib_defer_set_destroy, - + .quit = glib_quit, }; @@ -624,10 +624,10 @@ pa_glib_mainloop *pa_glib_mainloop_new(GMainContext *c) { NULL, NULL }; - + g = (pa_glib_mainloop*) g_source_new(&source_funcs, sizeof(pa_glib_mainloop)); g_main_context_ref(g->context = c ? c : g_main_context_default()); - + g->api = vtable; g->api.userdata = g; @@ -639,10 +639,10 @@ pa_glib_mainloop *pa_glib_mainloop_new(GMainContext *c) { g->io_events_please_scan = g->time_events_please_scan = g->defer_events_please_scan = 0; g->cached_next_time_event = NULL; - + g_source_attach(&g->source, g->context); g_source_set_can_recurse(&g->source, FALSE); - + return g; } @@ -660,6 +660,6 @@ void pa_glib_mainloop_free(pa_glib_mainloop* g) { pa_mainloop_api* pa_glib_mainloop_get_api(pa_glib_mainloop *g) { g_assert(g); - + return &g->api; } diff --git a/src/pulse/glib-mainloop.h b/src/pulse/glib-mainloop.h index af7cc0e9..5f8093a1 100644 --- a/src/pulse/glib-mainloop.h +++ b/src/pulse/glib-mainloop.h @@ -5,17 +5,17 @@ /*** This file is part of PulseAudio. - + 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 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 diff --git a/src/pulse/internal.h b/src/pulse/internal.h index 4eef4b4a..8cdbf84a 100644 --- a/src/pulse/internal.h +++ b/src/pulse/internal.h @@ -5,17 +5,17 @@ /*** This file is part of PulseAudio. - + 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 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 @@ -45,7 +45,7 @@ struct pa_context { int ref; - + char *name; pa_mainloop_api* mainloop; @@ -75,7 +75,7 @@ struct pa_context { int do_autospawn; int autospawn_lock_fd; pa_spawn_api spawn_api; - + pa_strlist *server_list; char *server; @@ -109,7 +109,7 @@ struct pa_stream { uint32_t device_index; pa_stream_direction_t direction; pa_stream_state_t state; - + uint32_t requested_bytes; pa_memchunk peek_memchunk; @@ -120,10 +120,10 @@ struct pa_stream { /* Store latest latency info */ pa_timing_info timing_info; int timing_info_valid; - + /* Use to make sure that time advances monotonically */ pa_usec_t previous_time; - + /* time updates with tags older than these are invalid */ uint32_t write_index_not_before; uint32_t read_index_not_before; @@ -135,7 +135,7 @@ struct pa_stream { /* Latency interpolation stuff */ pa_time_event *auto_timing_update_event; int auto_timing_update_requested; - + pa_usec_t cached_time; int cached_time_valid; @@ -160,7 +160,7 @@ struct pa_operation { int ref; pa_context *context; pa_stream *stream; - + PA_LLIST_FIELDS(pa_operation); pa_operation_state_t state; diff --git a/src/pulse/introspect.c b/src/pulse/introspect.c index b926e4e4..5a2c8fb6 100644 --- a/src/pulse/introspect.c +++ b/src/pulse/introspect.c @@ -2,17 +2,17 @@ /*** This file is part of PulseAudio. - + 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 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 @@ -39,7 +39,7 @@ static void context_stat_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { pa_operation *o = userdata; pa_stat_info i, *p = &i; - + assert(pd); assert(o); assert(o->ref >= 1); @@ -81,11 +81,11 @@ pa_operation* pa_context_stat(pa_context *c, pa_stat_info_cb_t cb, void *userdat static void context_get_server_info_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { pa_operation *o = userdata; pa_server_info i, *p = &i; - + assert(pd); assert(o); assert(o->ref >= 1); - + if (!o->context) goto finish; @@ -107,7 +107,7 @@ static void context_get_server_info_callback(pa_pdispatch *pd, uint32_t command, pa_context_fail(o->context, PA_ERR_PROTOCOL); goto finish; } - + if (o->callback) { pa_server_info_cb_t cb = (pa_server_info_cb_t) o->callback; cb(o->context, p, o->userdata); @@ -127,14 +127,14 @@ pa_operation* pa_context_get_server_info(pa_context *c, pa_server_info_cb_t cb, static void context_get_sink_info_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { pa_operation *o = userdata; int eol = 1; - + assert(pd); assert(o); assert(o->ref >= 1); if (!o->context) goto finish; - + if (command != PA_COMMAND_REPLY) { if (pa_context_handle_error(o->context, command, t) < 0) goto finish; @@ -142,10 +142,10 @@ static void context_get_sink_info_callback(pa_pdispatch *pd, uint32_t command, P eol = -1; } else { uint32_t flags; - + while (!pa_tagstruct_eof(t)) { pa_sink_info i; - + if (pa_tagstruct_getu32(t, &i.index) < 0 || pa_tagstruct_gets(t, &i.name) < 0 || pa_tagstruct_gets(t, &i.description) < 0 || @@ -159,7 +159,7 @@ static void context_get_sink_info_callback(pa_pdispatch *pd, uint32_t command, P pa_tagstruct_get_usec(t, &i.latency) < 0 || pa_tagstruct_gets(t, &i.driver) < 0 || pa_tagstruct_getu32(t, &flags) < 0) { - + pa_context_fail(o->context, PA_ERR_PROTOCOL); goto finish; } @@ -172,7 +172,7 @@ static void context_get_sink_info_callback(pa_pdispatch *pd, uint32_t command, P } } } - + if (o->callback) { pa_sink_info_cb_t cb = (pa_sink_info_cb_t) o->callback; cb(o->context, NULL, eol, o->userdata); @@ -191,7 +191,7 @@ pa_operation* pa_context_get_sink_info_by_index(pa_context *c, uint32_t idx, pa_ pa_tagstruct *t; pa_operation *o; uint32_t tag; - + assert(c); assert(c->ref >= 1); assert(cb); @@ -213,7 +213,7 @@ pa_operation* pa_context_get_sink_info_by_name(pa_context *c, const char *name, pa_tagstruct *t; pa_operation *o; uint32_t tag; - + assert(c); assert(c->ref >= 1); assert(cb); @@ -251,11 +251,11 @@ static void context_get_source_info_callback(pa_pdispatch *pd, uint32_t command, eol = -1; } else { - + while (!pa_tagstruct_eof(t)) { pa_source_info i; uint32_t flags; - + if (pa_tagstruct_getu32(t, &i.index) < 0 || pa_tagstruct_gets(t, &i.name) < 0 || pa_tagstruct_gets(t, &i.description) < 0 || @@ -269,7 +269,7 @@ static void context_get_source_info_callback(pa_pdispatch *pd, uint32_t command, pa_tagstruct_get_usec(t, &i.latency) < 0 || pa_tagstruct_gets(t, &i.driver) < 0 || pa_tagstruct_getu32(t, &flags) < 0) { - + pa_context_fail(o->context, PA_ERR_PROTOCOL); goto finish; } @@ -282,7 +282,7 @@ static void context_get_source_info_callback(pa_pdispatch *pd, uint32_t command, } } } - + if (o->callback) { pa_source_info_cb_t cb = (pa_source_info_cb_t) o->callback; cb(o->context, NULL, eol, o->userdata); @@ -309,7 +309,7 @@ pa_operation* pa_context_get_source_info_by_index(pa_context *c, uint32_t idx, p PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); - + t = pa_tagstruct_command(c, PA_COMMAND_GET_SOURCE_INFO, &tag); pa_tagstruct_putu32(t, idx); pa_tagstruct_puts(t, NULL); @@ -361,10 +361,10 @@ static void context_get_client_info_callback(pa_pdispatch *pd, uint32_t command, eol = -1; } else { - + while (!pa_tagstruct_eof(t)) { pa_client_info i; - + if (pa_tagstruct_getu32(t, &i.index) < 0 || pa_tagstruct_gets(t, &i.name) < 0 || pa_tagstruct_getu32(t, &i.owner_module) < 0 || @@ -379,7 +379,7 @@ static void context_get_client_info_callback(pa_pdispatch *pd, uint32_t command, } } } - + if (o->callback) { pa_client_info_cb_t cb = (pa_client_info_cb_t) o->callback; cb(o->context, NULL, eol, o->userdata); @@ -428,17 +428,17 @@ static void context_get_module_info_callback(pa_pdispatch *pd, uint32_t command, if (!o->context) goto finish; - + if (command != PA_COMMAND_REPLY) { if (pa_context_handle_error(o->context, command, t) < 0) goto finish; eol = -1; } else { - + while (!pa_tagstruct_eof(t)) { pa_module_info i; - + if (pa_tagstruct_getu32(t, &i.index) < 0 || pa_tagstruct_gets(t, &i.name) < 0 || pa_tagstruct_gets(t, &i.argument) < 0 || @@ -454,7 +454,7 @@ static void context_get_module_info_callback(pa_pdispatch *pd, uint32_t command, } } } - + if (o->callback) { pa_module_info_cb_t cb = (pa_module_info_cb_t) o->callback; cb(o->context, NULL, eol, o->userdata); @@ -476,7 +476,7 @@ pa_operation* pa_context_get_module_info(pa_context *c, uint32_t idx, pa_module_ PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID); - + o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); t = pa_tagstruct_command(c, PA_COMMAND_GET_MODULE_INFO, &tag); @@ -503,17 +503,17 @@ static void context_get_sink_input_info_callback(pa_pdispatch *pd, uint32_t comm if (!o->context) goto finish; - + if (command != PA_COMMAND_REPLY) { if (pa_context_handle_error(o->context, command, t) < 0) goto finish; eol = -1; } else { - + while (!pa_tagstruct_eof(t)) { pa_sink_input_info i; - + if (pa_tagstruct_getu32(t, &i.index) < 0 || pa_tagstruct_gets(t, &i.name) < 0 || pa_tagstruct_getu32(t, &i.owner_module) < 0 || @@ -526,7 +526,7 @@ static void context_get_sink_input_info_callback(pa_pdispatch *pd, uint32_t comm pa_tagstruct_get_usec(t, &i.sink_usec) < 0 || pa_tagstruct_gets(t, &i.resample_method) < 0 || pa_tagstruct_gets(t, &i.driver) < 0) { - + pa_context_fail(o->context, PA_ERR_PROTOCOL); goto finish; } @@ -537,7 +537,7 @@ static void context_get_sink_input_info_callback(pa_pdispatch *pd, uint32_t comm } } } - + if (o->callback) { pa_sink_input_info_cb_t cb = (pa_sink_input_info_cb_t) o->callback; cb(o->context, NULL, eol, o->userdata); @@ -559,7 +559,7 @@ pa_operation* pa_context_get_sink_input_info(pa_context *c, uint32_t idx, pa_sin PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID); - + o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); t = pa_tagstruct_command(c, PA_COMMAND_GET_SINK_INPUT_INFO, &tag); @@ -593,10 +593,10 @@ static void context_get_source_output_info_callback(pa_pdispatch *pd, uint32_t c eol = -1; } else { - + while (!pa_tagstruct_eof(t)) { pa_source_output_info i; - + if (pa_tagstruct_getu32(t, &i.index) < 0 || pa_tagstruct_gets(t, &i.name) < 0 || pa_tagstruct_getu32(t, &i.owner_module) < 0 || @@ -608,7 +608,7 @@ static void context_get_source_output_info_callback(pa_pdispatch *pd, uint32_t c pa_tagstruct_get_usec(t, &i.source_usec) < 0 || pa_tagstruct_gets(t, &i.resample_method) < 0 || pa_tagstruct_gets(t, &i.driver) < 0) { - + pa_context_fail(o->context, PA_ERR_PROTOCOL); goto finish; } @@ -619,7 +619,7 @@ static void context_get_source_output_info_callback(pa_pdispatch *pd, uint32_t c } } } - + if (o->callback) { pa_source_output_info_cb_t cb = (pa_source_output_info_cb_t) o->callback; cb(o->context, NULL, eol, o->userdata); @@ -641,7 +641,7 @@ pa_operation* pa_context_get_source_output_info(pa_context *c, uint32_t idx, pa_ PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID); - + o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); t = pa_tagstruct_command(c, PA_COMMAND_GET_SOURCE_OUTPUT_INFO, &tag); @@ -695,7 +695,7 @@ pa_operation* pa_context_set_sink_volume_by_name(pa_context *c, const char *name PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(c, pa_cvolume_valid(volume), PA_ERR_INVALID); PA_CHECK_VALIDITY_RETURN_NULL(c, !name || *name, PA_ERR_INVALID); - + o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); t = pa_tagstruct_command(c, PA_COMMAND_SET_SINK_VOLUME, &tag); @@ -741,7 +741,7 @@ pa_operation* pa_context_set_sink_mute_by_name(pa_context *c, const char *name, PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(c, !name || *name, PA_ERR_INVALID); - + o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); t = pa_tagstruct_command(c, PA_COMMAND_SET_SINK_MUTE, &tag); @@ -766,7 +766,7 @@ pa_operation* pa_context_set_sink_input_volume(pa_context *c, uint32_t idx, cons PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID); PA_CHECK_VALIDITY_RETURN_NULL(c, pa_cvolume_valid(volume), PA_ERR_INVALID); - + o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); t = pa_tagstruct_command(c, PA_COMMAND_SET_SINK_INPUT_VOLUME, &tag); @@ -815,7 +815,7 @@ pa_operation* pa_context_set_source_volume_by_name(pa_context *c, const char *na PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(c, pa_cvolume_valid(volume), PA_ERR_INVALID); PA_CHECK_VALIDITY_RETURN_NULL(c, !name || *name, PA_ERR_INVALID); - + o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); t = pa_tagstruct_command(c, PA_COMMAND_SET_SOURCE_VOLUME, &tag); @@ -861,7 +861,7 @@ pa_operation* pa_context_set_source_mute_by_name(pa_context *c, const char *name PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(c, !name || *name, PA_ERR_INVALID); - + o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); t = pa_tagstruct_command(c, PA_COMMAND_SET_SOURCE_MUTE, &tag); @@ -886,17 +886,17 @@ static void context_get_sample_info_callback(pa_pdispatch *pd, uint32_t command, if (!o->context) goto finish; - + if (command != PA_COMMAND_REPLY) { if (pa_context_handle_error(o->context, command, t) < 0) goto finish; eol = -1; } else { - + while (!pa_tagstruct_eof(t)) { pa_sample_info i; - + if (pa_tagstruct_getu32(t, &i.index) < 0 || pa_tagstruct_gets(t, &i.name) < 0 || pa_tagstruct_get_cvolume(t, &i.volume) < 0 || @@ -906,7 +906,7 @@ static void context_get_sample_info_callback(pa_pdispatch *pd, uint32_t command, pa_tagstruct_getu32(t, &i.bytes) < 0 || pa_tagstruct_get_boolean(t, &i.lazy) < 0 || pa_tagstruct_gets(t, &i.filename) < 0) { - + pa_context_fail(o->context, PA_ERR_PROTOCOL); goto finish; } @@ -917,7 +917,7 @@ static void context_get_sample_info_callback(pa_pdispatch *pd, uint32_t command, } } } - + if (o->callback) { pa_sample_info_cb_t cb = (pa_sample_info_cb_t) o->callback; cb(o->context, NULL, eol, o->userdata); @@ -932,7 +932,7 @@ pa_operation* pa_context_get_sample_info_by_name(pa_context *c, const char *name pa_tagstruct *t; pa_operation *o; uint32_t tag; - + assert(c); assert(c->ref >= 1); assert(cb); @@ -1002,7 +1002,7 @@ static pa_operation* command_kill(pa_context *c, uint32_t command, uint32_t idx, pa_operation* pa_context_kill_client(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata) { return command_kill(c, PA_COMMAND_KILL_CLIENT, idx, cb, userdata); } - + pa_operation* pa_context_kill_sink_input(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata) { return command_kill(c, PA_COMMAND_KILL_SINK_INPUT, idx, cb, userdata); } @@ -1021,7 +1021,7 @@ static void context_index_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UN if (!o->context) goto finish; - + if (command != PA_COMMAND_REPLY) { if (pa_context_handle_error(o->context, command, t) < 0) goto finish; @@ -1048,7 +1048,7 @@ pa_operation* pa_context_load_module(pa_context *c, const char*name, const char pa_operation *o; pa_tagstruct *t; uint32_t tag; - + assert(c); assert(c->ref >= 1); @@ -1089,10 +1089,10 @@ static void context_get_autoload_info_callback(pa_pdispatch *pd, uint32_t comman eol = -1; } else { - + while (!pa_tagstruct_eof(t)) { pa_autoload_info i; - + if (pa_tagstruct_getu32(t, &i.index) < 0 || pa_tagstruct_gets(t, &i.name) < 0 || pa_tagstruct_getu32(t, &i.type) < 0 || @@ -1108,7 +1108,7 @@ static void context_get_autoload_info_callback(pa_pdispatch *pd, uint32_t comman } } } - + if (o->callback) { pa_autoload_info_cb_t cb = (pa_autoload_info_cb_t) o->callback; cb(o->context, NULL, eol, o->userdata); @@ -1147,7 +1147,7 @@ pa_operation* pa_context_get_autoload_info_by_index(pa_context *c, uint32_t idx, pa_tagstruct *t; pa_operation *o; uint32_t tag; - + assert(c); assert(c->ref >= 1); assert(cb); @@ -1176,7 +1176,7 @@ pa_operation* pa_context_add_autoload(pa_context *c, const char *name, pa_autolo assert(c); assert(c->ref >= 1); - + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(c, name && *name, PA_ERR_INVALID); PA_CHECK_VALIDITY_RETURN_NULL(c, type == PA_AUTOLOAD_SINK || type == PA_AUTOLOAD_SOURCE, PA_ERR_INVALID); @@ -1202,7 +1202,7 @@ pa_operation* pa_context_remove_autoload_by_name(pa_context *c, const char *name assert(c); assert(c->ref >= 1); - + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(c, name && *name, PA_ERR_INVALID); PA_CHECK_VALIDITY_RETURN_NULL(c, type == PA_AUTOLOAD_SINK || type == PA_AUTOLOAD_SOURCE, PA_ERR_INVALID); diff --git a/src/pulse/introspect.h b/src/pulse/introspect.h index 28d22cd7..6de7bc71 100644 --- a/src/pulse/introspect.h +++ b/src/pulse/introspect.h @@ -5,17 +5,17 @@ /*** This file is part of PulseAudio. - + 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 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 @@ -206,7 +206,7 @@ PA_C_DECL_BEGIN /** Stores information about sinks */ typedef struct pa_sink_info { const char *name; /**< Name of the sink */ - uint32_t index; /**< Index of the sink */ + uint32_t index; /**< Index of the sink */ const char *description; /**< Description of this sink */ pa_sample_spec sample_spec; /**< Sample spec of this sink */ pa_channel_map channel_map; /**< Channel map \since 0.8 */ @@ -233,7 +233,7 @@ pa_operation* pa_context_get_sink_info_by_index(pa_context *c, uint32_t id, pa_s pa_operation* pa_context_get_sink_info_list(pa_context *c, pa_sink_info_cb_t cb, void *userdata); /** Stores information about sources */ -typedef struct pa_source_info { +typedef struct pa_source_info { const char *name ; /**< Name of the source */ uint32_t index; /**< Index of the source */ const char *description; /**< Description of this source */ @@ -316,7 +316,7 @@ pa_operation* pa_context_get_client_info_list(pa_context *c, pa_client_info_cb_t /** Stores information about sink inputs */ typedef struct pa_sink_input_info { - uint32_t index; /**< Index of the sink input */ + uint32_t index; /**< Index of the sink input */ const char *name; /**< Name of the sink input */ uint32_t owner_module; /**< Index of the module this sink input belongs to, or PA_INVALID_INDEX when it does not belong to any module */ uint32_t client; /**< Index of the client this sink input belongs to, or PA_INVALID_INDEX when it does not belong to any client */ @@ -341,11 +341,11 @@ pa_operation* pa_context_get_sink_input_info_list(pa_context *c, pa_sink_input_i /** Stores information about source outputs */ typedef struct pa_source_output_info { - uint32_t index; /**< Index of the sink input */ + uint32_t index; /**< Index of the sink input */ const char *name; /**< Name of the sink input */ - uint32_t owner_module; /**< Index of the module this sink input belongs to, or PA_INVALID_INDEX when it does not belong to any module */ - uint32_t client; /**< Index of the client this sink input belongs to, or PA_INVALID_INDEX when it does not belong to any client */ - uint32_t source; /**< Index of the connected source */ + uint32_t owner_module; /**< Index of the module this sink input belongs to, or PA_INVALID_INDEX when it does not belong to any module */ + uint32_t client; /**< Index of the client this sink input belongs to, or PA_INVALID_INDEX when it does not belong to any client */ + uint32_t source; /**< Index of the connected source */ pa_sample_spec sample_spec; /**< The sample specification of the source output */ pa_channel_map channel_map; /**< Channel map */ pa_usec_t buffer_usec; /**< Latency due to buffering in the source output, see pa_latency_info for details. \since 0.5 */ @@ -396,7 +396,7 @@ typedef struct pa_stat_info { uint32_t memblock_total_size; /**< Currentl total size of allocated memory blocks */ uint32_t memblock_allocated; /**< Allocated memory blocks during the whole lifetime of the daemon */ uint32_t memblock_allocated_size; /**< Total size of all memory blocks allocated during the whole lifetime of the daemon */ - uint32_t scache_size; /**< Total size of all sample cache entries. \since 0.4 */ + uint32_t scache_size; /**< Total size of all sample cache entries. \since 0.4 */ } pa_stat_info; /** Callback prototype for pa_context_stat() */ @@ -432,7 +432,7 @@ pa_operation* pa_context_get_sample_info_list(pa_context *c, pa_sample_info_cb_t /** Kill a client. \since 0.5 */ pa_operation* pa_context_kill_client(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata); - + /** Kill a sink input. \since 0.5 */ pa_operation* pa_context_kill_sink_input(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata); diff --git a/src/pulse/mainloop-api.c b/src/pulse/mainloop-api.c index 2e20b446..bf3ef37e 100644 --- a/src/pulse/mainloop-api.c +++ b/src/pulse/mainloop-api.c @@ -2,17 +2,17 @@ /*** This file is part of PulseAudio. - + 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 diff --git a/src/pulse/mainloop-api.h b/src/pulse/mainloop-api.h index 7b7075ae..b88bf125 100644 --- a/src/pulse/mainloop-api.h +++ b/src/pulse/mainloop-api.h @@ -5,17 +5,17 @@ /*** This file is part of PulseAudio. - + 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 @@ -28,7 +28,7 @@ #include /** \file - * + * * Main loop abstraction layer. Both the PulseAudio core and the * PulseAudio client library use a main loop abstraction layer. Due to * this it is possible to embed PulseAudio into other diff --git a/src/pulse/mainloop-signal.c b/src/pulse/mainloop-signal.c index d651462b..4dd42796 100644 --- a/src/pulse/mainloop-signal.c +++ b/src/pulse/mainloop-signal.c @@ -2,17 +2,17 @@ /*** This file is part of PulseAudio. - + 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 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 @@ -73,7 +73,7 @@ static void signal_handler(int sig) { static void dispatch(pa_mainloop_api*a, int sig) { pa_signal_event*s; - for (s = signals; s; s = s->next) + for (s = signals; s; s = s->next) if (s->sig == sig) { assert(s->callback); s->callback(a, s, sig, s->userdata); @@ -93,7 +93,7 @@ static void callback(pa_mainloop_api*a, pa_io_event*e, int fd, pa_io_event_flags pa_log("read(): %s", pa_cstrerror(errno)); return; } - + if (r != sizeof(sig)) { pa_log("short read()"); return; @@ -129,7 +129,7 @@ void pa_signal_done(void) { while (signals) pa_signal_free(signals); - + api->io_free(io_event); io_event = NULL; @@ -148,11 +148,11 @@ pa_signal_event* pa_signal_new(int sig, void (*_callback) (pa_mainloop_api *api, #endif assert(sig > 0 && _callback); - + for (e = signals; e; e = e->next) if (e->sig == sig) goto fail; - + e = pa_xmalloc(sizeof(pa_signal_event)); e->sig = sig; e->callback = _callback; @@ -164,7 +164,7 @@ pa_signal_event* pa_signal_new(int sig, void (*_callback) (pa_mainloop_api *api, sa.sa_handler = signal_handler; sigemptyset(&sa.sa_mask); sa.sa_flags = SA_RESTART; - + if (sigaction(sig, &sa, &e->saved_sigaction) < 0) #else if ((e->saved_handler = signal(sig, signal_handler)) == SIG_ERR) @@ -200,7 +200,7 @@ void pa_signal_free(pa_signal_event *e) { if (e->destroy_callback) e->destroy_callback(api, e, e->userdata); - + pa_xfree(e); } diff --git a/src/pulse/mainloop-signal.h b/src/pulse/mainloop-signal.h index 0721c1f5..e991cf1c 100644 --- a/src/pulse/mainloop-signal.h +++ b/src/pulse/mainloop-signal.h @@ -5,17 +5,17 @@ /*** This file is part of PulseAudio. - + 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 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 diff --git a/src/pulse/mainloop.c b/src/pulse/mainloop.c index f7b15537..04461b30 100644 --- a/src/pulse/mainloop.c +++ b/src/pulse/mainloop.c @@ -2,17 +2,17 @@ /*** This file is part of PulseAudio. - + 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 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 @@ -57,11 +57,11 @@ struct pa_io_event { pa_mainloop *mainloop; int dead; - + int fd; pa_io_event_flags_t events; struct pollfd *pollfd; - + pa_io_event_cb_t callback; void *userdata; pa_io_event_destroy_cb_t destroy_callback; @@ -154,7 +154,7 @@ static pa_io_event* mainloop_io_new( pa_io_event_flags_t events, pa_io_event_cb_t callback, void *userdata) { - + pa_mainloop *m; pa_io_event *e; @@ -162,7 +162,7 @@ static pa_io_event* mainloop_io_new( assert(a->userdata); assert(fd >= 0); assert(callback); - + m = a->userdata; assert(a == &m->api); @@ -173,7 +173,7 @@ static pa_io_event* mainloop_io_new( e->fd = fd; e->events = events; e->pollfd = NULL; - + e->callback = callback; e->userdata = userdata; e->destroy_callback = NULL; @@ -213,7 +213,7 @@ static void mainloop_io_enable(pa_io_event *e, pa_io_event_flags_t events) { if (e->events == events) return; - + e->events = events; if (e->pollfd) @@ -239,7 +239,7 @@ static void mainloop_io_free(pa_io_event *e) { static void mainloop_io_set_destroy(pa_io_event *e, pa_io_event_destroy_cb_t callback) { assert(e); - + e->destroy_callback = callback; } @@ -255,7 +255,7 @@ static pa_defer_event* mainloop_defer_new( assert(a); assert(a->userdata); assert(callback); - + m = a->userdata; assert(a == &m->api); @@ -265,7 +265,7 @@ static pa_defer_event* mainloop_defer_new( e->enabled = 1; m->n_enabled_defer_events++; - + e->callback = callback; e->userdata = userdata; e->destroy_callback = NULL; @@ -288,7 +288,7 @@ static void mainloop_defer_enable(pa_defer_event *e, int b) { e->mainloop->n_enabled_defer_events++; pa_mainloop_wakeup(e->mainloop); } - + e->enabled = b; } @@ -318,14 +318,14 @@ static pa_time_event* mainloop_time_new( const struct timeval *tv, pa_time_event_cb_t callback, void *userdata) { - + pa_mainloop *m; pa_time_event *e; assert(a); assert(a->userdata); assert(callback); - + m = a->userdata; assert(a == &m->api); @@ -354,7 +354,7 @@ static pa_time_event* mainloop_time_new( if (e->enabled) pa_mainloop_wakeup(m); - + return e; } @@ -375,7 +375,7 @@ static void mainloop_time_restart(pa_time_event *e, const struct timeval *tv) { if (e->mainloop->cached_next_time_event && e->enabled) { assert(e->mainloop->cached_next_time_event->enabled); - + if (pa_timeval_cmp(tv, &e->mainloop->cached_next_time_event->timeval) < 0) e->mainloop->cached_next_time_event = e; } else if (e->mainloop->cached_next_time_event == e) @@ -396,7 +396,7 @@ static void mainloop_time_free(pa_time_event *e) { if (e->mainloop->cached_next_time_event == e) e->mainloop->cached_next_time_event = NULL; - + /* no wakeup needed here. Think about it! */ } @@ -411,7 +411,7 @@ static void mainloop_time_set_destroy(pa_time_event *e, pa_time_event_destroy_cb static void mainloop_quit(pa_mainloop_api*a, int retval) { pa_mainloop *m; - + assert(a); assert(a->userdata); m = a->userdata; @@ -419,7 +419,7 @@ static void mainloop_quit(pa_mainloop_api*a, int retval) { pa_mainloop_quit(m, retval); } - + static const pa_mainloop_api vtable = { .userdata = NULL, @@ -432,12 +432,12 @@ static const pa_mainloop_api vtable = { .time_restart = mainloop_time_restart, .time_free = mainloop_time_free, .time_set_destroy = mainloop_time_set_destroy, - + .defer_new = mainloop_defer_new, .defer_enable = mainloop_defer_enable, .defer_free = mainloop_defer_free, .defer_set_destroy = mainloop_defer_set_destroy, - + .quit = mainloop_quit, }; @@ -466,7 +466,7 @@ pa_mainloop *pa_mainloop_new(void) { m->cached_next_time_event = NULL; m->prepared_timeout = 0; - + m->pollfds = NULL; m->max_pollfds = m->n_pollfds = 0; m->rebuild_pollfds = 1; @@ -481,7 +481,7 @@ pa_mainloop *pa_mainloop_new(void) { m->poll_func = NULL; m->poll_func_userdata = NULL; m->poll_func_ret = -1; - + return m; } @@ -494,7 +494,7 @@ static void cleanup_io_events(pa_mainloop *m, int force) { if (!force && m->io_events_please_scan <= 0) break; - + if (force || e->dead) { PA_LLIST_REMOVE(pa_io_event, m->io_events, e); @@ -502,10 +502,10 @@ static void cleanup_io_events(pa_mainloop *m, int force) { assert(m->io_events_please_scan > 0); m->io_events_please_scan--; } - + if (e->destroy_callback) e->destroy_callback(&m->api, e, e->userdata); - + pa_xfree(e); m->rebuild_pollfds = 1; @@ -526,7 +526,7 @@ static void cleanup_time_events(pa_mainloop *m, int force) { if (!force && m->time_events_please_scan <= 0) break; - + if (force || e->dead) { PA_LLIST_REMOVE(pa_time_event, m->time_events, e); @@ -539,10 +539,10 @@ static void cleanup_time_events(pa_mainloop *m, int force) { assert(m->n_enabled_time_events > 0); m->n_enabled_time_events--; } - + if (e->destroy_callback) e->destroy_callback(&m->api, e, e->userdata); - + pa_xfree(e); } @@ -561,7 +561,7 @@ static void cleanup_defer_events(pa_mainloop *m, int force) { if (!force && m->defer_events_please_scan <= 0) break; - + if (force || e->dead) { PA_LLIST_REMOVE(pa_defer_event, m->defer_events, e); @@ -574,10 +574,10 @@ static void cleanup_defer_events(pa_mainloop *m, int force) { assert(m->n_enabled_defer_events > 0); m->n_enabled_defer_events--; } - + if (e->destroy_callback) e->destroy_callback(&m->api, e, e->userdata); - + pa_xfree(e); } @@ -664,11 +664,11 @@ static int dispatch_pollfds(pa_mainloop *m) { int r = 0, k; assert(m->poll_func_ret > 0); - + for (e = m->io_events, k = m->poll_func_ret; e && !m->quit && k > 0; e = e->next) { if (e->dead || !e->pollfd || !e->pollfd->revents) continue; - + assert(e->pollfd->fd == e->fd && e->callback); e->callback(&m->api, e, e->fd, map_flags_from_libc(e->pollfd->revents), e->userdata); e->pollfd->revents = 0; @@ -690,7 +690,7 @@ static int dispatch_defer(pa_mainloop *m) { for (e = m->defer_events; e && !m->quit; e = e->next) { if (e->dead || !e->enabled) continue; - + assert(e->callback); e->callback(&m->api, e, e->userdata); r++; @@ -705,7 +705,7 @@ static pa_time_event* find_next_time_event(pa_mainloop *m) { if (m->cached_next_time_event) return m->cached_next_time_event; - + for (t = m->time_events; t; t = t->next) { if (t->dead || !t->enabled) @@ -737,7 +737,7 @@ static int calc_next_timeout(pa_mainloop *m) { if (t->timeval.tv_sec <= 0) return 0; - + pa_gettimeofday(&now); if (pa_timeval_cmp(&t->timeval, &now) <= 0) @@ -759,7 +759,7 @@ static int dispatch_timeout(pa_mainloop *m) { pa_gettimeofday(&now); for (e = m->time_events; e && !m->quit; e = e->next) { - + if (e->dead || !e->enabled) continue; @@ -815,7 +815,7 @@ int pa_mainloop_prepare(pa_mainloop *m, int timeout) { if (m->n_enabled_defer_events <= 0) { if (m->rebuild_pollfds) rebuild_pollfds(m); - + m->prepared_timeout = calc_next_timeout(m); if (timeout >= 0 && (timeout < m->prepared_timeout || m->prepared_timeout < 0)) m->prepared_timeout = timeout; @@ -842,7 +842,7 @@ int pa_mainloop_poll(pa_mainloop *m) { m->poll_func_ret = 0; else { assert(!m->rebuild_pollfds); - + if (m->poll_func) m->poll_func_ret = m->poll_func(m->pollfds, m->n_pollfds, m->prepared_timeout, m->poll_func_userdata); else @@ -872,23 +872,23 @@ int pa_mainloop_dispatch(pa_mainloop *m) { if (m->quit) goto quit; - + if (m->n_enabled_defer_events) dispatched += dispatch_defer(m); else { - if (m->n_enabled_time_events) + if (m->n_enabled_time_events) dispatched += dispatch_timeout(m); - + if (m->quit) goto quit; if (m->poll_func_ret > 0) dispatched += dispatch_pollfds(m); } - + if (m->quit) goto quit; - + m->state = STATE_PASSIVE; return dispatched; @@ -919,7 +919,7 @@ int pa_mainloop_iterate(pa_mainloop *m, int block, int *retval) { return r; quit: - + if ((r == -2) && retval) *retval = pa_mainloop_get_retval(m); return r; @@ -927,7 +927,7 @@ quit: int pa_mainloop_run(pa_mainloop *m, int *retval) { int r; - + while ((r = pa_mainloop_iterate(m, 1, retval)) >= 0); if (r == -2) diff --git a/src/pulse/mainloop.h b/src/pulse/mainloop.h index 8abd8fe4..18be1f2b 100644 --- a/src/pulse/mainloop.h +++ b/src/pulse/mainloop.h @@ -5,17 +5,17 @@ /*** This file is part of PulseAudio. - + 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 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 @@ -65,7 +65,7 @@ struct pollfd; */ /** \file - * + * * A minimal main loop implementation based on the C library's poll() * function. Using the routines defined herein you may create a simple * main loop supporting the generic main loop abstraction layer as diff --git a/src/pulse/operation.c b/src/pulse/operation.c index 8d896d7d..e039e8c9 100644 --- a/src/pulse/operation.c +++ b/src/pulse/operation.c @@ -2,17 +2,17 @@ /*** This file is part of PulseAudio. - + 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 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 @@ -46,14 +46,14 @@ pa_operation *pa_operation_new(pa_context *c, pa_stream *s, pa_operation_cb_t cb /* Refcounting is strictly one-way: from the "bigger" to the "smaller" object. */ PA_LLIST_PREPEND(pa_operation, c->operations, o); pa_operation_ref(o); - + return o; } pa_operation *pa_operation_ref(pa_operation *o) { assert(o); assert(o->ref >= 1); - + o->ref++; return o; } @@ -81,14 +81,14 @@ static void operation_set_state(pa_operation *o, pa_operation_state_t st) { o->state = st; if ((o->state == PA_OPERATION_DONE) || (o->state == PA_OPERATION_CANCELED)) { - + if (o->context) { assert(o->ref >= 2); - + PA_LLIST_REMOVE(pa_operation, o->context->operations, o); pa_operation_unref(o); } - + o->context = NULL; o->stream = NULL; o->callback = NULL; @@ -101,14 +101,14 @@ static void operation_set_state(pa_operation *o, pa_operation_state_t st) { void pa_operation_cancel(pa_operation *o) { assert(o); assert(o->ref >= 1); - + operation_set_state(o, PA_OPERATION_CANCELED); } void pa_operation_done(pa_operation *o) { assert(o); assert(o->ref >= 1); - + operation_set_state(o, PA_OPERATION_DONE); } diff --git a/src/pulse/operation.h b/src/pulse/operation.h index b544e08e..adfd5f2d 100644 --- a/src/pulse/operation.h +++ b/src/pulse/operation.h @@ -5,17 +5,17 @@ /*** This file is part of PulseAudio. - + 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 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 diff --git a/src/pulse/pulseaudio.h b/src/pulse/pulseaudio.h index 88cc326b..5543d7cd 100644 --- a/src/pulse/pulseaudio.h +++ b/src/pulse/pulseaudio.h @@ -5,17 +5,17 @@ /*** This file is part of PulseAudio. - + 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 @@ -55,11 +55,11 @@ /** \mainpage * * \section intro_sec Introduction - * + * * This document describes the client API for the PulseAudio sound * server. The API comes in two flavours to accomodate different styles * of applications and different needs in complexity: - * + * * \li The complete but somewhat complicated to use asynchronous API * \li The simplified, easy to use, but limited synchronous API * @@ -67,7 +67,7 @@ * locale. Some functions will filter invalid sequences from the string, some * will simply fail. To ensure reliable behaviour, make sure everything you * pass to the API is already in UTF-8. - + * \section simple_sec Simple API * * Use this if you develop your program in synchronous style and just diff --git a/src/pulse/sample.c b/src/pulse/sample.c index 7ca418e1..dd68ac17 100644 --- a/src/pulse/sample.c +++ b/src/pulse/sample.c @@ -2,17 +2,17 @@ /*** This file is part of PulseAudio. - + 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 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 @@ -70,7 +70,7 @@ pa_usec_t pa_bytes_to_usec(uint64_t length, const pa_sample_spec *spec) { size_t pa_usec_to_bytes(pa_usec_t t, const pa_sample_spec *spec) { assert(spec); - return (size_t) (((double) t * spec->rate / 1000000))*pa_frame_size(spec); + return (size_t) (((double) t * spec->rate / 1000000))*pa_frame_size(spec); } int pa_sample_spec_valid(const pa_sample_spec *spec) { @@ -111,7 +111,7 @@ const char *pa_sample_format_to_string(pa_sample_format_t f) { char *pa_sample_spec_snprint(char *s, size_t l, const pa_sample_spec *spec) { assert(s && l && spec); - + if (!pa_sample_spec_valid(spec)) snprintf(s, l, "Invalid"); else @@ -134,7 +134,7 @@ char* pa_bytes_snprint(char *s, size_t l, unsigned v) { } pa_sample_format_t pa_parse_sample_format(const char *format) { - + if (strcasecmp(format, "s16le") == 0) return PA_SAMPLE_S16LE; else if (strcasecmp(format, "s16be") == 0) diff --git a/src/pulse/sample.h b/src/pulse/sample.h index da32fdf0..b7c537e4 100644 --- a/src/pulse/sample.h +++ b/src/pulse/sample.h @@ -5,17 +5,17 @@ /*** This file is part of PulseAudio. - + 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 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 diff --git a/src/pulse/scache.c b/src/pulse/scache.c index 5d29c5b3..5abaf845 100644 --- a/src/pulse/scache.c +++ b/src/pulse/scache.c @@ -2,17 +2,17 @@ /*** This file is part of PulseAudio. - + 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 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 @@ -37,14 +37,14 @@ int pa_stream_connect_upload(pa_stream *s, size_t length) { pa_tagstruct *t; uint32_t tag; - + assert(s); PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_UNCONNECTED, PA_ERR_BADSTATE); PA_CHECK_VALIDITY(s->context, length > 0, PA_ERR_INVALID); - + pa_stream_ref(s); - + s->direction = PA_STREAM_UPLOAD; t = pa_tagstruct_command(s->context, PA_COMMAND_CREATE_UPLOAD_STREAM, &tag); @@ -56,7 +56,7 @@ int pa_stream_connect_upload(pa_stream *s, size_t length) { pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_create_stream_callback, s, NULL); pa_stream_set_state(s, PA_STREAM_CREATING); - + pa_stream_unref(s); return 0; } @@ -87,16 +87,16 @@ pa_operation *pa_context_play_sample(pa_context *c, const char *name, const char assert(c); assert(c->ref >= 1); - + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(c, name && *name, PA_ERR_INVALID); PA_CHECK_VALIDITY_RETURN_NULL(c, !dev || *dev, PA_ERR_INVALID); - + o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); if (!dev) dev = c->conf->default_sink; - + t = pa_tagstruct_command(c, PA_COMMAND_PLAY_SAMPLE, &tag); pa_tagstruct_putu32(t, PA_INVALID_INDEX); pa_tagstruct_puts(t, dev); @@ -118,9 +118,9 @@ pa_operation* pa_context_remove_sample(pa_context *c, const char *name, pa_conte PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(c, name && *name, PA_ERR_INVALID); - + o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); - + t = pa_tagstruct_command(c, PA_COMMAND_REMOVE_SAMPLE, &tag); pa_tagstruct_puts(t, name); pa_pstream_send_tagstruct(c->pstream, t); diff --git a/src/pulse/scache.h b/src/pulse/scache.h index e32703d4..2293dec4 100644 --- a/src/pulse/scache.h +++ b/src/pulse/scache.h @@ -5,17 +5,17 @@ /*** This file is part of PulseAudio. - + 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 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 diff --git a/src/pulse/simple.c b/src/pulse/simple.c index a41881bb..1c632fa7 100644 --- a/src/pulse/simple.c +++ b/src/pulse/simple.c @@ -2,17 +2,17 @@ /*** This file is part of PulseAudio. - + 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 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 @@ -64,7 +64,7 @@ if (!(expression)) { \ goto label; \ } \ } while(0); - + #define CHECK_DEAD_GOTO(p, rerror, label) do { \ if (!(p)->context || pa_context_get_state((p)->context) != PA_CONTEXT_READY || \ !(p)->stream || pa_stream_get_state((p)->stream) != PA_STREAM_READY) { \ @@ -143,7 +143,7 @@ pa_simple* pa_simple_new( const pa_channel_map *map, const pa_buffer_attr *attr, int *rerror) { - + pa_simple *p; int error = PA_ERR_INTERNAL, r; @@ -162,12 +162,12 @@ pa_simple* pa_simple_new( if (!(p->mainloop = pa_threaded_mainloop_new())) goto fail; - + if (!(p->context = pa_context_new(pa_threaded_mainloop_get_api(p->mainloop), name))) goto fail; pa_context_set_state_callback(p->context, context_state_cb, p); - + if (pa_context_connect(p->context, server, 0, NULL) < 0) { error = pa_context_errno(p->context); goto fail; @@ -180,7 +180,7 @@ pa_simple* pa_simple_new( /* Wait until the context is ready */ pa_threaded_mainloop_wait(p->mainloop); - + if (pa_context_get_state(p->context) != PA_CONTEXT_READY) { error = pa_context_errno(p->context); goto unlock_and_fail; @@ -216,12 +216,12 @@ pa_simple* pa_simple_new( } pa_threaded_mainloop_unlock(p->mainloop); - + return p; unlock_and_fail: pa_threaded_mainloop_unlock(p->mainloop); - + fail: if (rerror) *rerror = error; @@ -234,10 +234,10 @@ void pa_simple_free(pa_simple *s) { if (s->mainloop) pa_threaded_mainloop_stop(s->mainloop); - + if (s->stream) pa_stream_unref(s->stream); - + if (s->context) pa_context_unref(s->context); @@ -249,38 +249,38 @@ void pa_simple_free(pa_simple *s) { int pa_simple_write(pa_simple *p, const void*data, size_t length, int *rerror) { assert(p); - + CHECK_VALIDITY_RETURN_ANY(rerror, p->direction == PA_STREAM_PLAYBACK, PA_ERR_BADSTATE, -1); CHECK_VALIDITY_RETURN_ANY(rerror, data && length, PA_ERR_INVALID, -1); pa_threaded_mainloop_lock(p->mainloop); - + CHECK_DEAD_GOTO(p, rerror, unlock_and_fail); while (length > 0) { size_t l; int r; - + while (!(l = pa_stream_writable_size(p->stream))) { pa_threaded_mainloop_wait(p->mainloop); CHECK_DEAD_GOTO(p, rerror, unlock_and_fail); } CHECK_SUCCESS_GOTO(p, rerror, l != (size_t) -1, unlock_and_fail); - + if (l > length) l = length; r = pa_stream_write(p->stream, data, l, NULL, 0, PA_SEEK_RELATIVE); CHECK_SUCCESS_GOTO(p, rerror, r >= 0, unlock_and_fail); - + data = (const uint8_t*) data + l; length -= l; } pa_threaded_mainloop_unlock(p->mainloop); return 0; - + unlock_and_fail: pa_threaded_mainloop_unlock(p->mainloop); return -1; @@ -293,15 +293,15 @@ int pa_simple_read(pa_simple *p, void*data, size_t length, int *rerror) { CHECK_VALIDITY_RETURN_ANY(rerror, data && length, PA_ERR_INVALID, -1); pa_threaded_mainloop_lock(p->mainloop); - + CHECK_DEAD_GOTO(p, rerror, unlock_and_fail); while (length > 0) { size_t l; - + while (!p->read_data) { int r; - + r = pa_stream_peek(p->stream, &p->read_data, &p->read_length); CHECK_SUCCESS_GOTO(p, rerror, r == 0, unlock_and_fail); @@ -311,31 +311,31 @@ int pa_simple_read(pa_simple *p, void*data, size_t length, int *rerror) { } else p->read_index = 0; } - + l = p->read_length < length ? p->read_length : length; memcpy(data, (const uint8_t*) p->read_data+p->read_index, l); data = (uint8_t*) data + l; length -= l; - + p->read_index += l; p->read_length -= l; if (!p->read_length) { int r; - + r = pa_stream_drop(p->stream); p->read_data = NULL; p->read_length = 0; p->read_index = 0; - + CHECK_SUCCESS_GOTO(p, rerror, r == 0, unlock_and_fail); } } pa_threaded_mainloop_unlock(p->mainloop); return 0; - + unlock_and_fail: pa_threaded_mainloop_unlock(p->mainloop); return -1; @@ -353,7 +353,7 @@ static void success_cb(pa_stream *s, int success, void *userdata) { int pa_simple_drain(pa_simple *p, int *rerror) { pa_operation *o = NULL; - + assert(p); CHECK_VALIDITY_RETURN_ANY(rerror, p->direction == PA_STREAM_PLAYBACK, PA_ERR_BADSTATE, -1); @@ -370,7 +370,7 @@ int pa_simple_drain(pa_simple *p, int *rerror) { CHECK_DEAD_GOTO(p, rerror, unlock_and_fail); } CHECK_SUCCESS_GOTO(p, rerror, p->operation_success, unlock_and_fail); - + pa_operation_unref(o); pa_threaded_mainloop_unlock(p->mainloop); @@ -389,7 +389,7 @@ unlock_and_fail: int pa_simple_flush(pa_simple *p, int *rerror) { pa_operation *o = NULL; - + assert(p); CHECK_VALIDITY_RETURN_ANY(rerror, p->direction == PA_STREAM_PLAYBACK, PA_ERR_BADSTATE, -1); @@ -399,7 +399,7 @@ int pa_simple_flush(pa_simple *p, int *rerror) { o = pa_stream_flush(p->stream, success_cb, p); CHECK_SUCCESS_GOTO(p, rerror, o, unlock_and_fail); - + p->operation_success = 0; while (pa_operation_get_state(o) != PA_OPERATION_DONE) { pa_threaded_mainloop_wait(p->mainloop); @@ -426,14 +426,14 @@ unlock_and_fail: pa_usec_t pa_simple_get_latency(pa_simple *p, int *rerror) { pa_usec_t t; int negative; - + assert(p); - + pa_threaded_mainloop_lock(p->mainloop); for (;;) { CHECK_DEAD_GOTO(p, rerror, unlock_and_fail); - + if (pa_stream_get_latency(p->stream, &t, &negative) >= 0) break; @@ -442,7 +442,7 @@ pa_usec_t pa_simple_get_latency(pa_simple *p, int *rerror) { /* Wait until latency data is available again */ pa_threaded_mainloop_wait(p->mainloop); } - + pa_threaded_mainloop_unlock(p->mainloop); return negative ? 0 : t; diff --git a/src/pulse/simple.h b/src/pulse/simple.h index 0438d319..a97875f9 100644 --- a/src/pulse/simple.h +++ b/src/pulse/simple.h @@ -5,17 +5,17 @@ /*** This file is part of PulseAudio. - + 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 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 diff --git a/src/pulse/stream.c b/src/pulse/stream.c index 180cd096..009eb3cf 100644 --- a/src/pulse/stream.c +++ b/src/pulse/stream.c @@ -2,17 +2,17 @@ /*** This file is part of PulseAudio. - + 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 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 @@ -43,7 +43,7 @@ pa_stream *pa_stream_new(pa_context *c, const char *name, const pa_sample_spec *ss, const pa_channel_map *map) { pa_stream *s; int i; - + assert(c); PA_CHECK_VALIDITY_RETURN_NULL(c, ss && pa_sample_spec_valid(ss), PA_ERR_INVALID); @@ -76,7 +76,7 @@ pa_stream *pa_stream_new(pa_context *c, const char *name, const pa_sample_spec * s->channel_map = *map; else pa_channel_map_init_auto(&s->channel_map, ss->channels, PA_CHANNEL_MAP_DEFAULT); - + s->channel = 0; s->channel_valid = 0; s->syncid = c->csyncid++; @@ -99,11 +99,11 @@ pa_stream *pa_stream_new(pa_context *c, const char *name, const pa_sample_spec * for (i = 0; i < PA_MAX_WRITE_INDEX_CORRECTIONS; i++) s->write_index_corrections[i].valid = 0; s->current_write_index_correction = 0; - + s->corked = 0; s->cached_time_valid = 0; - + s->auto_timing_update_event = NULL; s->auto_timing_update_requested = 0; @@ -143,7 +143,7 @@ void pa_stream_unref(pa_stream *s) { pa_stream* pa_stream_ref(pa_stream *s) { assert(s); assert(s->ref >= 1); - + s->ref++; return s; } @@ -151,14 +151,14 @@ pa_stream* pa_stream_ref(pa_stream *s) { pa_stream_state_t pa_stream_get_state(pa_stream *s) { assert(s); assert(s->ref >= 1); - + return s->state; } pa_context* pa_stream_get_context(pa_stream *s) { assert(s); assert(s->ref >= 1); - + return s->context; } @@ -167,17 +167,17 @@ uint32_t pa_stream_get_index(pa_stream *s) { assert(s->ref >= 1); PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE, PA_INVALID_INDEX); - + return s->device_index; } - + void pa_stream_set_state(pa_stream *s, pa_stream_state_t st) { assert(s); assert(s->ref >= 1); if (s->state == st) return; - + pa_stream_ref(s); s->state = st; @@ -192,24 +192,24 @@ void pa_stream_set_state(pa_stream *s, pa_stream_state_t st) { /* Unref all operatio object that point to us */ for (o = s->context->operations; o; o = n) { n = o->next; - + if (o->stream == s) pa_operation_cancel(o); } - + /* Drop all outstanding replies for this stream */ if (s->context->pdispatch) pa_pdispatch_unregister_reply(s->context->pdispatch, s); if (s->channel_valid) pa_dynarray_put((s->direction == PA_STREAM_PLAYBACK) ? s->context->playback_streams : s->context->record_streams, s->channel, NULL); - + PA_LLIST_REMOVE(pa_stream, s->context->streams, s); pa_stream_unref(s); s->channel = 0; s->channel_valid = 0; - + s->context = NULL; } @@ -220,20 +220,20 @@ void pa_command_stream_killed(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED pa_context *c = userdata; pa_stream *s; uint32_t channel; - + assert(pd); assert(command == PA_COMMAND_PLAYBACK_STREAM_KILLED || command == PA_COMMAND_RECORD_STREAM_KILLED); assert(t); assert(c); pa_context_ref(c); - + if (pa_tagstruct_getu32(t, &channel) < 0 || !pa_tagstruct_eof(t)) { pa_context_fail(c, PA_ERR_PROTOCOL); goto finish; } - + if (!(s = pa_dynarray_get(command == PA_COMMAND_PLAYBACK_STREAM_KILLED ? c->playback_streams : c->record_streams, channel))) goto finish; @@ -248,27 +248,27 @@ void pa_command_request(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32 pa_stream *s; pa_context *c = userdata; uint32_t bytes, channel; - + assert(pd); assert(command == PA_COMMAND_REQUEST); assert(t); assert(c); pa_context_ref(c); - + if (pa_tagstruct_getu32(t, &channel) < 0 || pa_tagstruct_getu32(t, &bytes) < 0 || !pa_tagstruct_eof(t)) { pa_context_fail(c, PA_ERR_PROTOCOL); goto finish; } - + if (!(s = pa_dynarray_get(c->playback_streams, channel))) goto finish; if (s->state == PA_STREAM_READY) { s->requested_bytes += bytes; - + if (s->requested_bytes > 0 && s->write_callback) s->write_callback(s, s->requested_bytes, s->write_userdata); } @@ -288,13 +288,13 @@ void pa_command_overflow_or_underflow(pa_pdispatch *pd, uint32_t command, PA_GCC assert(c); pa_context_ref(c); - + if (pa_tagstruct_getu32(t, &channel) < 0 || !pa_tagstruct_eof(t)) { pa_context_fail(c, PA_ERR_PROTOCOL); goto finish; } - + if (!(s = pa_dynarray_get(c->playback_streams, channel))) goto finish; @@ -319,11 +319,11 @@ static void request_auto_timing_update(pa_stream *s, int force) { if (!(s->flags & PA_STREAM_AUTO_TIMING_UPDATE)) return; - + if (s->state == PA_STREAM_READY && (force || !s->auto_timing_update_requested)) { pa_operation *o; - + /* pa_log("automatically requesting new timing data"); */ if ((o = pa_stream_update_timing_info(s, NULL, NULL))) { @@ -341,32 +341,32 @@ static void invalidate_indexes(pa_stream *s, int r, int w) { assert(s); /* pa_log("invalidate r:%u w:%u tag:%u", r, w, s->context->ctag); */ - + if (s->state != PA_STREAM_READY) return; if (w) { s->write_index_not_before = s->context->ctag; - + if (s->timing_info_valid) s->timing_info.write_index_corrupt = 1; - + /* pa_log("write_index invalidated"); */ } - + if (r) { s->read_index_not_before = s->context->ctag; - + if (s->timing_info_valid) s->timing_info.read_index_corrupt = 1; - + /* pa_log("read_index invalidated"); */ } - + if ((s->direction == PA_STREAM_PLAYBACK && r) || (s->direction == PA_STREAM_RECORD && w)) s->cached_time_valid = 0; - + request_auto_timing_update(s, 1); } @@ -382,17 +382,17 @@ static void auto_timing_update_callback(PA_GCC_UNUSED pa_mainloop_api *m, PA_GCC void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { pa_stream *s = userdata; - + assert(pd); assert(s); assert(s->state == PA_STREAM_CREATING); - + pa_stream_ref(s); - + if (command != PA_COMMAND_REPLY) { if (pa_context_handle_error(s->context, command, t) < 0) goto finish; - + pa_stream_set_state(s, PA_STREAM_FAILED); goto finish; } @@ -429,9 +429,9 @@ void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED if (s->direction == PA_STREAM_RECORD) { assert(!s->record_memblockq); - + s->record_memblockq = pa_memblockq_new( - 0, + 0, s->buffer_attr.maxlength, 0, pa_frame_size(&s->sample_spec), @@ -444,7 +444,7 @@ void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED pa_dynarray_put((s->direction == PA_STREAM_RECORD) ? s->context->record_streams : s->context->playback_streams, s->channel, s); pa_stream_set_state(s, PA_STREAM_READY); - + if (s->direction != PA_STREAM_UPLOAD && s->flags & PA_STREAM_AUTO_TIMING_UPDATE) { struct timeval tv; @@ -473,13 +473,13 @@ static int create_stream( pa_stream_flags_t flags, const pa_cvolume *volume, pa_stream *sync_stream) { - + pa_tagstruct *t; uint32_t tag; - + assert(s); assert(s->ref >= 1); - + PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_UNCONNECTED, PA_ERR_BADSTATE); PA_CHECK_VALIDITY(s->context, !(flags & ~((direction != PA_STREAM_UPLOAD ? PA_STREAM_START_CORKED| @@ -493,10 +493,10 @@ static int create_stream( s->direction = direction; s->flags = flags; - + if (sync_stream) s->syncid = sync_stream->syncid; - + if (attr) s->buffer_attr = *attr; else { @@ -515,7 +515,7 @@ static int create_stream( s->context, s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_CREATE_PLAYBACK_STREAM : PA_COMMAND_CREATE_RECORD_STREAM, &tag); - + pa_tagstruct_put( t, PA_TAG_STRING, s->name, @@ -526,7 +526,7 @@ static int create_stream( PA_TAG_U32, s->buffer_attr.maxlength, PA_TAG_BOOLEAN, !!(flags & PA_STREAM_START_CORKED), PA_TAG_INVALID); - + if (s->direction == PA_STREAM_PLAYBACK) { pa_cvolume cv; @@ -540,7 +540,7 @@ static int create_stream( if (!volume) volume = pa_cvolume_reset(&cv, s->sample_spec.channels); - + pa_tagstruct_put_cvolume(t, volume); } else pa_tagstruct_putu32(t, s->buffer_attr.fragsize); @@ -549,7 +549,7 @@ static int create_stream( pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_create_stream_callback, s, NULL); pa_stream_set_state(s, PA_STREAM_CREATING); - + pa_stream_unref(s); return 0; } @@ -561,10 +561,10 @@ int pa_stream_connect_playback( pa_stream_flags_t flags, pa_cvolume *volume, pa_stream *sync_stream) { - + assert(s); assert(s->ref >= 1); - + return create_stream(PA_STREAM_PLAYBACK, s, dev, attr, flags, volume, sync_stream); } @@ -573,7 +573,7 @@ int pa_stream_connect_record( const char *dev, const pa_buffer_attr *attr, pa_stream_flags_t flags) { - + assert(s); assert(s->ref >= 1); @@ -587,9 +587,9 @@ int pa_stream_write( void (*free_cb)(void *p), int64_t offset, pa_seek_mode_t seek) { - + pa_memchunk chunk; - + assert(s); assert(s->ref >= 1); assert(data); @@ -602,29 +602,29 @@ int pa_stream_write( if (length <= 0) return 0; - if (free_cb) + if (free_cb) chunk.memblock = pa_memblock_new_user(s->context->mempool, (void*) data, length, free_cb, 1); else { chunk.memblock = pa_memblock_new(s->context->mempool, length); memcpy(chunk.memblock->data, data, length); } - + chunk.index = 0; chunk.length = length; pa_pstream_send_memblock(s->context->pstream, s->channel, offset, seek, &chunk); pa_memblock_unref(chunk.memblock); - + if (length < s->requested_bytes) s->requested_bytes -= length; else s->requested_bytes = 0; if (s->direction == PA_STREAM_PLAYBACK) { - + /* Update latency request correction */ if (s->write_index_corrections[s->current_write_index_correction].valid) { - + if (seek == PA_SEEK_ABSOLUTE) { s->write_index_corrections[s->current_write_index_correction].corrupt = 0; s->write_index_corrections[s->current_write_index_correction].absolute = 1; @@ -635,10 +635,10 @@ int pa_stream_write( } else s->write_index_corrections[s->current_write_index_correction].corrupt = 1; } - + /* Update the write index in the already available latency data */ if (s->timing_info_valid) { - + if (seek == PA_SEEK_ABSOLUTE) { s->timing_info.write_index_corrupt = 0; s->timing_info.write_index = offset + length; @@ -652,7 +652,7 @@ int pa_stream_write( if (!s->timing_info_valid || s->timing_info.write_index_corrupt) request_auto_timing_update(s, 1); } - + return 0; } @@ -664,7 +664,7 @@ int pa_stream_peek(pa_stream *s, const void **data, size_t *length) { PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE); PA_CHECK_VALIDITY(s->context, s->direction == PA_STREAM_RECORD, PA_ERR_BADSTATE); - + if (!s->peek_memchunk.memblock) { if (pa_memblockq_peek(s->record_memblockq, &s->peek_memchunk) < 0) { @@ -686,13 +686,13 @@ int pa_stream_drop(pa_stream *s) { PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE); PA_CHECK_VALIDITY(s->context, s->direction == PA_STREAM_RECORD, PA_ERR_BADSTATE); PA_CHECK_VALIDITY(s->context, s->peek_memchunk.memblock, PA_ERR_BADSTATE); - + pa_memblockq_drop(s->record_memblockq, &s->peek_memchunk, s->peek_memchunk.length); /* Fix the simulated local read index */ if (s->timing_info_valid && !s->timing_info.read_index_corrupt) s->timing_info.read_index += s->peek_memchunk.length; - + pa_memblock_unref(s->peek_memchunk.memblock); s->peek_memchunk.length = 0; s->peek_memchunk.index = 0; @@ -707,7 +707,7 @@ size_t pa_stream_writable_size(pa_stream *s) { PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE, (size_t) -1); PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->direction != PA_STREAM_RECORD, PA_ERR_BADSTATE, (size_t) -1); - + return s->requested_bytes; } @@ -725,7 +725,7 @@ pa_operation * pa_stream_drain(pa_stream *s, pa_stream_success_cb_t cb, void *us pa_operation *o; pa_tagstruct *t; uint32_t tag; - + assert(s); assert(s->ref >= 1); @@ -746,7 +746,7 @@ static void stream_get_timing_info_callback(pa_pdispatch *pd, uint32_t command, pa_operation *o = userdata; struct timeval local, remote, now; pa_timing_info *i; - + assert(pd); assert(o); @@ -756,7 +756,7 @@ static void stream_get_timing_info_callback(pa_pdispatch *pd, uint32_t command, i = &o->stream->timing_info; /* pa_log("pre corrupt w:%u r:%u\n", !o->stream->timing_info_valid || i->write_index_corrupt,!o->stream->timing_info_valid || i->read_index_corrupt); */ - + o->stream->timing_info_valid = 0; i->write_index_corrupt = 0; i->read_index_corrupt = 0; @@ -777,21 +777,21 @@ static void stream_get_timing_info_callback(pa_pdispatch *pd, uint32_t command, !pa_tagstruct_eof(t)) { pa_context_fail(o->context, PA_ERR_PROTOCOL); goto finish; - + } else { o->stream->timing_info_valid = 1; pa_gettimeofday(&now); - + /* Calculcate timestamps */ if (pa_timeval_cmp(&local, &remote) <= 0 && pa_timeval_cmp(&remote, &now) <= 0) { /* local and remote seem to have synchronized clocks */ - + if (o->stream->direction == PA_STREAM_PLAYBACK) i->transport_usec = pa_timeval_diff(&remote, &local); else i->transport_usec = pa_timeval_diff(&now, &remote); - + i->synchronized_clocks = 1; i->timestamp = remote; } else { @@ -816,7 +816,7 @@ static void stream_get_timing_info_callback(pa_pdispatch *pd, uint32_t command, uint32_t ctag = tag; /* Go through the saved correction values and add up the total correction.*/ - + for (n = 0, j = o->stream->current_write_index_correction+1; n < PA_MAX_WRITE_INDEX_CORRECTIONS; n++, j = (j + 1) % PA_MAX_WRITE_INDEX_CORRECTIONS) { @@ -851,21 +851,21 @@ static void stream_get_timing_info_callback(pa_pdispatch *pd, uint32_t command, if (!i->read_index_corrupt) i->read_index -= pa_memblockq_get_length(o->stream->record_memblockq); } - + o->stream->cached_time_valid = 0; } o->stream->auto_timing_update_requested = 0; /* pa_log("post corrupt w:%u r:%u\n", i->write_index_corrupt || !o->stream->timing_info_valid, i->read_index_corrupt || !o->stream->timing_info_valid); */ - + /* Clear old correction entries */ if (o->stream->direction == PA_STREAM_PLAYBACK) { int n; - + for (n = 0; n < PA_MAX_WRITE_INDEX_CORRECTIONS; n++) { if (!o->stream->write_index_corrections[n].valid) continue; - + if (o->stream->write_index_corrections[n].tag <= tag) o->stream->write_index_corrections[n].valid = 0; } @@ -873,12 +873,12 @@ static void stream_get_timing_info_callback(pa_pdispatch *pd, uint32_t command, if (o->stream->latency_update_callback) o->stream->latency_update_callback(o->stream, o->stream->latency_update_userdata); - + if (o->callback && o->stream && o->stream->state == PA_STREAM_READY) { pa_stream_success_cb_t cb = (pa_stream_success_cb_t) o->callback; cb(o->stream, o->stream->timing_info_valid, o->userdata); } - + finish: pa_operation_done(o); @@ -891,29 +891,29 @@ pa_operation* pa_stream_update_timing_info(pa_stream *s, pa_stream_success_cb_t pa_tagstruct *t; struct timeval now; int cidx = 0; - + assert(s); assert(s->ref >= 1); PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE); - + if (s->direction == PA_STREAM_PLAYBACK) { /* Find a place to store the write_index correction data for this entry */ cidx = (s->current_write_index_correction + 1) % PA_MAX_WRITE_INDEX_CORRECTIONS; - + /* Check if we could allocate a correction slot. If not, there are too many outstanding queries */ PA_CHECK_VALIDITY_RETURN_NULL(s->context, !s->write_index_corrections[cidx].valid, PA_ERR_INTERNAL); } o = pa_operation_new(s->context, s, (pa_operation_cb_t) cb, userdata); - + t = pa_tagstruct_command( s->context, s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_GET_PLAYBACK_LATENCY : PA_COMMAND_GET_RECORD_LATENCY, &tag); pa_tagstruct_putu32(t, s->channel); pa_tagstruct_put_timeval(t, pa_gettimeofday(&now)); - + pa_pstream_send_tagstruct(s->context->pstream, t); pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, stream_get_timing_info_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); @@ -926,15 +926,15 @@ pa_operation* pa_stream_update_timing_info(pa_stream *s, pa_stream_success_cb_t o->stream->write_index_corrections[cidx].value = 0; o->stream->write_index_corrections[cidx].corrupt = 0; } - + /* pa_log("requesting update %u\n", tag); */ - + return o; } void pa_stream_disconnect_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { pa_stream *s = userdata; - + assert(pd); assert(s); assert(s->ref >= 1); @@ -961,7 +961,7 @@ finish: int pa_stream_disconnect(pa_stream *s) { pa_tagstruct *t; uint32_t tag; - + assert(s); assert(s->ref >= 1); @@ -1010,7 +1010,7 @@ void pa_stream_set_state_callback(pa_stream *s, pa_stream_notify_cb_t cb, void * void pa_stream_set_overflow_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata) { assert(s); assert(s->ref >= 1); - + s->overflow_callback = cb; s->overflow_userdata = userdata; } @@ -1018,7 +1018,7 @@ void pa_stream_set_overflow_callback(pa_stream *s, pa_stream_notify_cb_t cb, voi void pa_stream_set_underflow_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata) { assert(s); assert(s->ref >= 1); - + s->underflow_callback = cb; s->underflow_userdata = userdata; } @@ -1026,7 +1026,7 @@ void pa_stream_set_underflow_callback(pa_stream *s, pa_stream_notify_cb_t cb, vo void pa_stream_set_latency_update_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata) { assert(s); assert(s->ref >= 1); - + s->latency_update_callback = cb; s->latency_update_userdata = userdata; } @@ -1034,14 +1034,14 @@ void pa_stream_set_latency_update_callback(pa_stream *s, pa_stream_notify_cb_t c void pa_stream_simple_ack_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { pa_operation *o = userdata; int success = 1; - + assert(pd); assert(o); assert(o->ref >= 1); if (!o->context) goto finish; - + if (command != PA_COMMAND_REPLY) { if (pa_context_handle_error(o->context, command, t) < 0) goto finish; @@ -1066,15 +1066,15 @@ pa_operation* pa_stream_cork(pa_stream *s, int b, pa_stream_success_cb_t cb, voi pa_operation *o; pa_tagstruct *t; uint32_t tag; - + assert(s); assert(s->ref >= 1); - + PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE); s->corked = b; - + o = pa_operation_new(s->context, s, (pa_operation_cb_t) cb, userdata); t = pa_tagstruct_command( @@ -1096,12 +1096,12 @@ static pa_operation* stream_send_simple_command(pa_stream *s, uint32_t command, pa_tagstruct *t; pa_operation *o; uint32_t tag; - + assert(s); assert(s->ref >= 1); PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE); - + o = pa_operation_new(s->context, s, (pa_operation_cb_t) cb, userdata); t = pa_tagstruct_command(s->context, command, &tag); @@ -1114,26 +1114,26 @@ static pa_operation* stream_send_simple_command(pa_stream *s, uint32_t command, pa_operation* pa_stream_flush(pa_stream *s, pa_stream_success_cb_t cb, void *userdata) { pa_operation *o; - + PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE); - + if ((o = stream_send_simple_command(s, s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_FLUSH_PLAYBACK_STREAM : PA_COMMAND_FLUSH_RECORD_STREAM, cb, userdata))) { if (s->direction == PA_STREAM_PLAYBACK) { if (s->write_index_corrections[s->current_write_index_correction].valid) s->write_index_corrections[s->current_write_index_correction].corrupt = 1; - + if (s->timing_info_valid) s->timing_info.write_index_corrupt = 1; - if (s->buffer_attr.prebuf > 0) + if (s->buffer_attr.prebuf > 0) invalidate_indexes(s, 1, 0); else request_auto_timing_update(s, 1); } else invalidate_indexes(s, 0, 1); } - + return o; } @@ -1151,13 +1151,13 @@ pa_operation* pa_stream_prebuf(pa_stream *s, pa_stream_success_cb_t cb, void *us pa_operation* pa_stream_trigger(pa_stream *s, pa_stream_success_cb_t cb, void *userdata) { pa_operation *o; - + PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction == PA_STREAM_PLAYBACK, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->buffer_attr.prebuf > 0, PA_ERR_BADSTATE); if ((o = stream_send_simple_command(s, PA_COMMAND_TRIGGER_PLAYBACK_STREAM, cb, userdata))) invalidate_indexes(s, 1, 0); - + return o; } @@ -1165,7 +1165,7 @@ pa_operation* pa_stream_set_name(pa_stream *s, const char *name, pa_stream_succe pa_operation *o; pa_tagstruct *t; uint32_t tag; - + assert(s); assert(s->ref >= 1); assert(name); @@ -1213,7 +1213,7 @@ int pa_stream_get_time(pa_stream *s, pa_usec_t *r_usec) { * to us, we assume that the real output time is actually * a little ahead */ usec += s->timing_info.transport_usec; - + /* However, the output device usually maintains a buffer too, hence the real sample currently played is a little back */ @@ -1222,7 +1222,7 @@ int pa_stream_get_time(pa_stream *s, pa_usec_t *r_usec) { else usec -= s->timing_info.sink_usec; } - + } else if (s->direction == PA_STREAM_RECORD) { /* The last byte written into the server side queue had * this time value associated */ @@ -1231,10 +1231,10 @@ int pa_stream_get_time(pa_stream *s, pa_usec_t *r_usec) { if (!s->corked) { /* Add transport latency */ usec += s->timing_info.transport_usec; - + /* Add latency of data in device buffer */ usec += s->timing_info.source_usec; - + /* If this is a monitor source, we need to correct the * time by the playback device buffer */ if (s->timing_info.sink_usec >= usec) @@ -1269,18 +1269,18 @@ int pa_stream_get_time(pa_stream *s, pa_usec_t *r_usec) { if (r_usec) *r_usec = usec; - + return 0; } static pa_usec_t time_counter_diff(pa_stream *s, pa_usec_t a, pa_usec_t b, int *negative) { assert(s); assert(s->ref >= 1); - + if (negative) *negative = 0; - if (a >= b) + if (a >= b) return a-b; else { if (negative && s->direction == PA_STREAM_RECORD) { @@ -1305,7 +1305,7 @@ int pa_stream_get_latency(pa_stream *s, pa_usec_t *r_usec, int *negative) { PA_CHECK_VALIDITY(s->context, s->timing_info_valid, PA_ERR_NODATA); PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_PLAYBACK || !s->timing_info.write_index_corrupt, PA_ERR_NODATA); PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_RECORD || !s->timing_info.read_index_corrupt, PA_ERR_NODATA); - + if ((r = pa_stream_get_time(s, &t)) < 0) return r; @@ -1316,7 +1316,7 @@ int pa_stream_get_latency(pa_stream *s, pa_usec_t *r_usec, int *negative) { if (cindex < 0) cindex = 0; - + c = pa_bytes_to_usec(cindex, &s->sample_spec); if (s->direction == PA_STREAM_PLAYBACK) diff --git a/src/pulse/stream.h b/src/pulse/stream.h index ad15125a..2ce53458 100644 --- a/src/pulse/stream.h +++ b/src/pulse/stream.h @@ -5,17 +5,17 @@ /*** This file is part of PulseAudio. - + 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 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 @@ -113,7 +113,7 @@ * read index may be larger than the write index, in which case * silence is played. If the application writes data to indexes lower * than the read index, the data is immediately lost. - * + * * \section transfer_sec Transferring Data * * Once the stream is up, data can start flowing between the client and the @@ -159,13 +159,13 @@ * \li PA_SEEK_ABSOLUTE - seek relative to the beginning of the playback buffer, (i.e. the first that was ever played in the stream) * \li PA_SEEK_RELATIVE_ON_READ - seek relative to the current read index. Use this to write data to the output buffer that should be played as soon as possible * \li PA_SEEK_RELATIVE_END - seek relative to the last byte ever written. - * + * * If an application just wants to append some data to the output * buffer, PA_SEEK_RELATIVE and an offset of 0 should be used. * * After a call to pa_stream_write() the write index will be left at * the position right after the last byte of the written data. - * + * * \section latency_sec Latency * * A major problem with networked audio is the increased latency caused by @@ -217,7 +217,7 @@ * this option with PA_STREAM_AUTO_TIMING_UPDATE, which will enable * you to monitor the current playback time/latency very precisely and * very frequently without requiring a network round trip every time. - * + * * \section flow_sec Overflow and underflow * * Even with the best precautions, buffers will sometime over - or @@ -275,7 +275,7 @@ typedef void (*pa_stream_notify_cb_t)(pa_stream *p, void *userdata); /** Create a new, unconnected stream with the specified name and sample type */ pa_stream* pa_stream_new( - pa_context *c /**< The context to create this stream in */, + pa_context *c /**< The context to create this stream in */, const char *name /**< A name for this stream */, const pa_sample_spec *ss /**< The desired sample format */, const pa_channel_map *map /**< The desired channel map, or NULL for default */); @@ -320,7 +320,7 @@ int pa_stream_disconnect(pa_stream *s); * is not copied. If NULL, the data is copied into an internal * buffer. The client my 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.*/ + * offset and seek should be useful.*/ int pa_stream_write( pa_stream *p /**< The stream to use */, const void *data /**< The data to write */, @@ -333,7 +333,7 @@ int pa_stream_write( * 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 - * buffer. If no data is available will return a NULL pointer \since 0.8 */ + * buffer. If no data is available will return a NULL pointer \since 0.8 */ int pa_stream_peek( pa_stream *p /**< The stream to use */, const void **data /**< Pointer to pointer that will point to data */, diff --git a/src/pulse/subscribe.c b/src/pulse/subscribe.c index c1d88912..d9abf6f8 100644 --- a/src/pulse/subscribe.c +++ b/src/pulse/subscribe.c @@ -2,17 +2,17 @@ /*** This file is part of PulseAudio. - + 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 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 @@ -69,7 +69,7 @@ pa_operation* pa_context_subscribe(pa_context *c, pa_subscription_mask_t m, pa_c assert(c->ref >= 1); PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); - + o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); t = pa_tagstruct_command(c, PA_COMMAND_SUBSCRIBE, &tag); @@ -83,7 +83,7 @@ pa_operation* pa_context_subscribe(pa_context *c, pa_subscription_mask_t m, pa_c void pa_context_set_subscribe_callback(pa_context *c, pa_context_subscribe_cb_t cb, void *userdata) { assert(c); assert(c->ref >= 1); - + c->subscribe_callback = cb; c->subscribe_userdata = userdata; } diff --git a/src/pulse/subscribe.h b/src/pulse/subscribe.h index adbea680..35d5a31c 100644 --- a/src/pulse/subscribe.h +++ b/src/pulse/subscribe.h @@ -5,17 +5,17 @@ /*** This file is part of PulseAudio. - + 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 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 diff --git a/src/pulse/thread-mainloop.c b/src/pulse/thread-mainloop.c index 7e1ccfaa..ad29f5ba 100644 --- a/src/pulse/thread-mainloop.c +++ b/src/pulse/thread-mainloop.c @@ -2,17 +2,17 @@ /*** This file is part of PulseAudio. - + 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 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 @@ -128,7 +128,7 @@ void pa_threaded_mainloop_free(pa_threaded_mainloop* m) { pa_mutex_free(m->mutex); pa_cond_free(m->cond); pa_cond_free(m->accept_cond); - + pa_xfree(m); } @@ -161,7 +161,7 @@ void pa_threaded_mainloop_stop(pa_threaded_mainloop *m) { void pa_threaded_mainloop_lock(pa_threaded_mainloop *m) { assert(m); - + /* Make sure that this function is not called from the helper thread */ assert(!m->thread || !pa_thread_is_running(m->thread) || !in_worker(m)); @@ -170,7 +170,7 @@ void pa_threaded_mainloop_lock(pa_threaded_mainloop *m) { void pa_threaded_mainloop_unlock(pa_threaded_mainloop *m) { assert(m); - + /* Make sure that this function is not called from the helper thread */ assert(!m->thread || !pa_thread_is_running(m->thread) || !in_worker(m)); @@ -188,7 +188,7 @@ void pa_threaded_mainloop_signal(pa_threaded_mainloop *m, int wait_for_accept) { void pa_threaded_mainloop_wait(pa_threaded_mainloop *m) { assert(m); - + /* Make sure that this function is not called from the helper thread */ assert(!m->thread || !pa_thread_is_running(m->thread) || !in_worker(m)); @@ -202,7 +202,7 @@ void pa_threaded_mainloop_wait(pa_threaded_mainloop *m) { void pa_threaded_mainloop_accept(pa_threaded_mainloop *m) { assert(m); - + /* Make sure that this function is not called from the helper thread */ assert(!m->thread || !pa_thread_is_running(m->thread) || !in_worker(m)); diff --git a/src/pulse/thread-mainloop.h b/src/pulse/thread-mainloop.h index 44eff5a3..48048b35 100644 --- a/src/pulse/thread-mainloop.h +++ b/src/pulse/thread-mainloop.h @@ -5,17 +5,17 @@ /*** This file is part of PulseAudio. - + 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 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 @@ -149,7 +149,7 @@ PA_C_DECL_BEGIN * deal with that. * * The functions will not dead lock because the wait function will release - * the lock before waiting and then regrab it once it has been signaled. + * the lock before waiting and then regrab it once it has been signaled. * For those of you familiar with threads, the behaviour is that of a * condition variable. * @@ -229,7 +229,7 @@ PA_C_DECL_BEGIN */ /** \file - * + * * A thread based event loop implementation based on pa_mainloop. The * event loop is run in a helper thread in the background. A few * synchronization primitives are available to access the objects diff --git a/src/pulse/timeval.c b/src/pulse/timeval.c index 11285230..e5a86a63 100644 --- a/src/pulse/timeval.c +++ b/src/pulse/timeval.c @@ -2,17 +2,17 @@ /*** This file is part of PulseAudio. - + 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 @@ -38,7 +38,7 @@ struct timeval *pa_gettimeofday(struct timeval *tv) { #ifdef HAVE_GETTIMEOFDAY assert(tv); - + return gettimeofday(tv, NULL) < 0 ? NULL : tv; #elif defined(OS_IS_WIN32) /* @@ -118,14 +118,14 @@ int pa_timeval_cmp(const struct timeval *a, const struct timeval *b) { pa_usec_t pa_timeval_age(const struct timeval *tv) { struct timeval now; assert(tv); - + return pa_timeval_diff(pa_gettimeofday(&now), tv); } struct timeval* pa_timeval_add(struct timeval *tv, pa_usec_t v) { unsigned long secs; assert(tv); - + secs = (v/1000000); tv->tv_sec += (unsigned long) secs; v -= secs*1000000; diff --git a/src/pulse/timeval.h b/src/pulse/timeval.h index e2dbbadb..c10ec9f6 100644 --- a/src/pulse/timeval.h +++ b/src/pulse/timeval.h @@ -5,17 +5,17 @@ /*** This file is part of PulseAudio. - + 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 diff --git a/src/pulse/utf8.c b/src/pulse/utf8.c index 33fa7214..2708c518 100644 --- a/src/pulse/utf8.c +++ b/src/pulse/utf8.c @@ -130,7 +130,7 @@ ONE_REMAINING: if (o) o++; - + continue; error: diff --git a/src/pulse/utf8.h b/src/pulse/utf8.h index 2eac724d..7225471f 100644 --- a/src/pulse/utf8.h +++ b/src/pulse/utf8.h @@ -5,17 +5,17 @@ /*** This file is part of PulseAudio. - + 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 diff --git a/src/pulse/util.c b/src/pulse/util.c index c13951da..63f54e3b 100644 --- a/src/pulse/util.c +++ b/src/pulse/util.c @@ -2,17 +2,17 @@ /*** This file is part of PulseAudio. - + 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 @@ -79,7 +79,7 @@ char *pa_get_user_name(char *s, size_t l) { if (!(p = getenv("USER")) && !(p = getenv("LOGNAME")) && !(p = getenv("USERNAME"))) { #ifdef HAVE_PWD_H - + #ifdef HAVE_GETPWUID_R if (getpwuid_r(getuid(), &pw, buf, sizeof(buf), &r) != 0 || !r) { #else @@ -90,7 +90,7 @@ char *pa_get_user_name(char *s, size_t l) { snprintf(s, l, "%lu", (unsigned long) getuid()); return s; } - + p = r->pw_name; #elif defined(OS_IS_WIN32) /* HAVE_PWD_H */ @@ -162,26 +162,26 @@ char *pa_get_binary_name(char *s, size_t l) { #if defined(OS_IS_WIN32) { char path[PATH_MAX]; - + if (GetModuleFileName(NULL, path, PATH_MAX)) return pa_strlcpy(s, pa_path_get_filename(path), l); } #endif - + #ifdef HAVE_READLINK { int i; char path[PATH_MAX]; /* This works on Linux only */ - + if ((i = readlink("/proc/self/exe", path, sizeof(path)-1)) >= 0) { path[i] = 0; return pa_strlcpy(s, pa_path_get_filename(path), l); } } - + #endif - + #if defined(HAVE_SYS_PRCTL_H) && defined(PR_GET_NAME) { @@ -192,14 +192,14 @@ char *pa_get_binary_name(char *s, size_t l) { char tcomm[TASK_COMM_LEN+1]; memset(tcomm, 0, sizeof(tcomm)); - + /* This works on Linux only */ if (prctl(PR_GET_NAME, (unsigned long) tcomm, 0, 0, 0) == 0) return pa_strlcpy(s, tcomm, l); - + } #endif - + return NULL; } @@ -214,7 +214,7 @@ const char *pa_path_get_filename(const char *p) { char *pa_get_fqdn(char *s, size_t l) { char hn[256]; -#ifdef HAVE_GETADDRINFO +#ifdef HAVE_GETADDRINFO struct addrinfo *a, hints; #endif @@ -225,7 +225,7 @@ char *pa_get_fqdn(char *s, size_t l) { memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_flags = AI_CANONNAME; - + if (getaddrinfo(hn, NULL, &hints, &a) < 0 || !a || !a->ai_canonname || !*a->ai_canonname) return pa_strlcpy(s, hn, l); diff --git a/src/pulse/util.h b/src/pulse/util.h index 5c03b0a9..0be169fb 100644 --- a/src/pulse/util.h +++ b/src/pulse/util.h @@ -5,17 +5,17 @@ /*** This file is part of PulseAudio. - + 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 diff --git a/src/pulse/volume.c b/src/pulse/volume.c index 530814e0..aa7ddba2 100644 --- a/src/pulse/volume.c +++ b/src/pulse/volume.c @@ -2,17 +2,17 @@ /*** This file is part of PulseAudio. - + 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 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 @@ -36,7 +36,7 @@ int pa_cvolume_equal(const pa_cvolume *a, const pa_cvolume *b) { if (a->channels != b->channels) return 0; - + for (i = 0; i < a->channels; i++) if (a->values[i] != b->values[i]) return 0; @@ -46,7 +46,7 @@ int pa_cvolume_equal(const pa_cvolume *a, const pa_cvolume *b) { pa_cvolume* pa_cvolume_set(pa_cvolume *a, unsigned channels, pa_volume_t v) { int i; - + assert(a); assert(channels > 0); assert(channels <= PA_CHANNELS_MAX); @@ -115,7 +115,7 @@ char *pa_cvolume_snprint(char *s, size_t l, const pa_cvolume *c) { unsigned channel; int first = 1; char *e; - + assert(s); assert(l > 0); assert(c); @@ -149,7 +149,7 @@ int pa_cvolume_channels_equal_to(const pa_cvolume *a, pa_volume_t v) { pa_cvolume *pa_sw_cvolume_multiply(pa_cvolume *dest, const pa_cvolume *a, const pa_cvolume *b) { unsigned i; - + assert(dest); assert(a); assert(b); diff --git a/src/pulse/volume.h b/src/pulse/volume.h index 6c60223a..62d2867a 100644 --- a/src/pulse/volume.h +++ b/src/pulse/volume.h @@ -5,17 +5,17 @@ /*** This file is part of PulseAudio. - + 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 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 diff --git a/src/pulse/xmalloc.c b/src/pulse/xmalloc.c index 36755166..dda42bdc 100644 --- a/src/pulse/xmalloc.c +++ b/src/pulse/xmalloc.c @@ -2,17 +2,17 @@ /*** This file is part of PulseAudio. - + 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 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 @@ -60,10 +60,10 @@ void* pa_xmalloc(size_t size) { void *p; assert(size > 0); assert(size < MAX_ALLOC_SIZE); - + if (!(p = malloc(size))) oom(); - + return p; } @@ -71,18 +71,18 @@ void* pa_xmalloc0(size_t size) { void *p; assert(size > 0); assert(size < MAX_ALLOC_SIZE); - + if (!(p = calloc(1, size))) oom(); - + return p; } - + void *pa_xrealloc(void *ptr, size_t size) { void *p; assert(size > 0); assert(size < MAX_ALLOC_SIZE); - + if (!(p = realloc(ptr, size))) oom(); return p; @@ -107,7 +107,7 @@ char *pa_xstrdup(const char *s) { char *pa_xstrndup(const char *s, size_t l) { char *e, *r; - + if (!s) return NULL; diff --git a/src/pulse/xmalloc.h b/src/pulse/xmalloc.h index 126c495c..2fb1c5f0 100644 --- a/src/pulse/xmalloc.h +++ b/src/pulse/xmalloc.h @@ -5,17 +5,17 @@ /*** This file is part of PulseAudio. - + 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 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 -- cgit From 06211b7c8fd329137ae9003818543912a87d9898 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Tue, 13 Feb 2007 15:35:19 +0000 Subject: Add copyright notices to all relevant files. (based on svn log) git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@1426 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/browser.c | 2 ++ src/pulse/browser.h | 2 ++ src/pulse/cdecl.h | 2 ++ src/pulse/channelmap.c | 3 +++ src/pulse/channelmap.h | 3 +++ src/pulse/client-conf-x11.c | 2 ++ src/pulse/client-conf-x11.h | 2 ++ src/pulse/client-conf.c | 3 +++ src/pulse/client-conf.h | 2 ++ src/pulse/context.c | 3 +++ src/pulse/context.h | 3 +++ src/pulse/def.h | 3 +++ src/pulse/error.c | 3 +++ src/pulse/error.h | 3 +++ src/pulse/glib-mainloop.c | 2 ++ src/pulse/glib-mainloop.h | 3 +++ src/pulse/internal.h | 3 +++ src/pulse/introspect.c | 3 +++ src/pulse/introspect.h | 3 +++ src/pulse/mainloop-api.c | 2 ++ src/pulse/mainloop-api.h | 3 +++ src/pulse/mainloop-signal.c | 3 +++ src/pulse/mainloop-signal.h | 3 +++ src/pulse/mainloop.c | 3 +++ src/pulse/mainloop.h | 3 +++ src/pulse/operation.c | 2 ++ src/pulse/operation.h | 2 ++ src/pulse/pulseaudio.h | 3 +++ src/pulse/sample.c | 3 +++ src/pulse/sample.h | 3 +++ src/pulse/scache.c | 2 ++ src/pulse/scache.h | 3 +++ src/pulse/simple.c | 2 ++ src/pulse/simple.h | 3 +++ src/pulse/stream.c | 3 +++ src/pulse/stream.h | 3 +++ src/pulse/subscribe.c | 2 ++ src/pulse/subscribe.h | 3 +++ src/pulse/thread-mainloop.c | 3 +++ src/pulse/thread-mainloop.h | 3 +++ src/pulse/timeval.c | 3 +++ src/pulse/timeval.h | 3 +++ src/pulse/utf8.c | 22 ++++++++++++++++++++++ src/pulse/utf8.h | 3 +++ src/pulse/util.c | 3 +++ src/pulse/util.h | 3 +++ src/pulse/version.h.in | 3 +++ src/pulse/volume.c | 2 ++ src/pulse/volume.h | 3 +++ src/pulse/xmalloc.c | 2 ++ src/pulse/xmalloc.h | 2 ++ 51 files changed, 156 insertions(+) (limited to 'src/pulse') diff --git a/src/pulse/browser.c b/src/pulse/browser.c index 4b0de029..27c5a2ea 100644 --- a/src/pulse/browser.c +++ b/src/pulse/browser.c @@ -3,6 +3,8 @@ /*** 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 of the diff --git a/src/pulse/browser.h b/src/pulse/browser.h index 7b9aae8d..b039ca33 100644 --- a/src/pulse/browser.h +++ b/src/pulse/browser.h @@ -6,6 +6,8 @@ /*** 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 of the diff --git a/src/pulse/cdecl.h b/src/pulse/cdecl.h index 09b9b84a..922ad276 100644 --- a/src/pulse/cdecl.h +++ b/src/pulse/cdecl.h @@ -6,6 +6,8 @@ /*** 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 of the License, diff --git a/src/pulse/channelmap.c b/src/pulse/channelmap.c index 40655cf5..d5b8f743 100644 --- a/src/pulse/channelmap.c +++ b/src/pulse/channelmap.c @@ -3,6 +3,9 @@ /*** This file is part of PulseAudio. + Copyright 2005-2006 Lennart Poettering + Copyright 2006 Pierre Ossman for Cendio AB + 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 of the License, diff --git a/src/pulse/channelmap.h b/src/pulse/channelmap.h index 20380251..f0c8f475 100644 --- a/src/pulse/channelmap.h +++ b/src/pulse/channelmap.h @@ -6,6 +6,9 @@ /*** This file is part of PulseAudio. + Copyright 2005-2006 Lennart Poettering + Copyright 2006 Pierre Ossman for Cendio AB + 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 of the License, diff --git a/src/pulse/client-conf-x11.c b/src/pulse/client-conf-x11.c index 78e190e5..b5ac8d9f 100644 --- a/src/pulse/client-conf-x11.c +++ b/src/pulse/client-conf-x11.c @@ -3,6 +3,8 @@ /*** 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 of the License, diff --git a/src/pulse/client-conf-x11.h b/src/pulse/client-conf-x11.h index 02e808be..56cd406d 100644 --- a/src/pulse/client-conf-x11.h +++ b/src/pulse/client-conf-x11.h @@ -6,6 +6,8 @@ /*** 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 of the License, diff --git a/src/pulse/client-conf.c b/src/pulse/client-conf.c index b652a25b..bb912335 100644 --- a/src/pulse/client-conf.c +++ b/src/pulse/client-conf.c @@ -3,6 +3,9 @@ /*** This file is part of PulseAudio. + Copyright 2004-2006 Lennart Poettering + Copyright 2006 Pierre Ossman for Cendio AB + 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 of the License, diff --git a/src/pulse/client-conf.h b/src/pulse/client-conf.h index 35728aeb..6de64582 100644 --- a/src/pulse/client-conf.h +++ b/src/pulse/client-conf.h @@ -6,6 +6,8 @@ /*** 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 of the License, diff --git a/src/pulse/context.c b/src/pulse/context.c index 7ef43b30..58a5a879 100644 --- a/src/pulse/context.c +++ b/src/pulse/context.c @@ -3,6 +3,9 @@ /*** This file is part of PulseAudio. + Copyright 2004-2006 Lennart Poettering + Copyright 2006 Pierre Ossman for Cendio AB + 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 of the License, diff --git a/src/pulse/context.h b/src/pulse/context.h index 048ed17f..1de3abad 100644 --- a/src/pulse/context.h +++ b/src/pulse/context.h @@ -6,6 +6,9 @@ /*** This file is part of PulseAudio. + Copyright 2004-2006 Lennart Poettering + Copyright 2006 Pierre Ossman for Cendio AB + 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 of the License, diff --git a/src/pulse/def.h b/src/pulse/def.h index fb04de50..c2816234 100644 --- a/src/pulse/def.h +++ b/src/pulse/def.h @@ -6,6 +6,9 @@ /*** This file is part of PulseAudio. + Copyright 2004-2006 Lennart Poettering + Copyright 2006 Pierre Ossman for Cendio AB + 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 diff --git a/src/pulse/error.c b/src/pulse/error.c index 3b9a60a4..78f0da95 100644 --- a/src/pulse/error.c +++ b/src/pulse/error.c @@ -3,6 +3,9 @@ /*** This file is part of PulseAudio. + Copyright 2004-2006 Lennart Poettering + Copyright 2006 Pierre Ossman for Cendio AB + 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 of the License, diff --git a/src/pulse/error.h b/src/pulse/error.h index c96349a6..44a2e5ec 100644 --- a/src/pulse/error.h +++ b/src/pulse/error.h @@ -6,6 +6,9 @@ /*** This file is part of PulseAudio. + Copyright 2004-2006 Lennart Poettering + Copyright 2006 Pierre Ossman for Cendio AB + 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 of the License, diff --git a/src/pulse/glib-mainloop.c b/src/pulse/glib-mainloop.c index 1669acdd..5b399aa2 100644 --- a/src/pulse/glib-mainloop.c +++ b/src/pulse/glib-mainloop.c @@ -3,6 +3,8 @@ /*** 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 of the License, diff --git a/src/pulse/glib-mainloop.h b/src/pulse/glib-mainloop.h index 5f8093a1..a4e06ea0 100644 --- a/src/pulse/glib-mainloop.h +++ b/src/pulse/glib-mainloop.h @@ -6,6 +6,9 @@ /*** This file is part of PulseAudio. + Copyright 2004-2006 Lennart Poettering + Copyright 2006 Pierre Ossman for Cendio AB + 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 of the License, diff --git a/src/pulse/internal.h b/src/pulse/internal.h index 8cdbf84a..52354fdc 100644 --- a/src/pulse/internal.h +++ b/src/pulse/internal.h @@ -6,6 +6,9 @@ /*** This file is part of PulseAudio. + Copyright 2004-2006 Lennart Poettering + Copyright 2006 Pierre Ossman for Cendio AB + 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 of the License, diff --git a/src/pulse/introspect.c b/src/pulse/introspect.c index 5a2c8fb6..7f6406cf 100644 --- a/src/pulse/introspect.c +++ b/src/pulse/introspect.c @@ -3,6 +3,9 @@ /*** This file is part of PulseAudio. + Copyright 2004-2006 Lennart Poettering + Copyright 2006 Pierre Ossman for Cendio AB + 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 of the License, diff --git a/src/pulse/introspect.h b/src/pulse/introspect.h index 6de7bc71..43e430b2 100644 --- a/src/pulse/introspect.h +++ b/src/pulse/introspect.h @@ -6,6 +6,9 @@ /*** This file is part of PulseAudio. + Copyright 2004-2006 Lennart Poettering + Copyright 2006 Pierre Ossman for Cendio AB + 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 of the License, diff --git a/src/pulse/mainloop-api.c b/src/pulse/mainloop-api.c index bf3ef37e..001ff314 100644 --- a/src/pulse/mainloop-api.c +++ b/src/pulse/mainloop-api.c @@ -3,6 +3,8 @@ /*** 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 diff --git a/src/pulse/mainloop-api.h b/src/pulse/mainloop-api.h index b88bf125..985806e6 100644 --- a/src/pulse/mainloop-api.h +++ b/src/pulse/mainloop-api.h @@ -6,6 +6,9 @@ /*** This file is part of PulseAudio. + Copyright 2004-2006 Lennart Poettering + Copyright 2006 Pierre Ossman for Cendio AB + 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 diff --git a/src/pulse/mainloop-signal.c b/src/pulse/mainloop-signal.c index 4dd42796..28ddec49 100644 --- a/src/pulse/mainloop-signal.c +++ b/src/pulse/mainloop-signal.c @@ -3,6 +3,9 @@ /*** This file is part of PulseAudio. + Copyright 2004-2006 Lennart Poettering + Copyright 2006 Pierre Ossman for Cendio AB + 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 of the License, diff --git a/src/pulse/mainloop-signal.h b/src/pulse/mainloop-signal.h index e991cf1c..50aa99ce 100644 --- a/src/pulse/mainloop-signal.h +++ b/src/pulse/mainloop-signal.h @@ -6,6 +6,9 @@ /*** This file is part of PulseAudio. + Copyright 2004-2006 Lennart Poettering + Copyright 2006 Pierre Ossman for Cendio AB + 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 of the License, diff --git a/src/pulse/mainloop.c b/src/pulse/mainloop.c index 04461b30..43cbb19f 100644 --- a/src/pulse/mainloop.c +++ b/src/pulse/mainloop.c @@ -3,6 +3,9 @@ /*** This file is part of PulseAudio. + Copyright 2004-2006 Lennart Poettering + Copyright 2006 Pierre Ossman for Cendio AB + 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 of the License, diff --git a/src/pulse/mainloop.h b/src/pulse/mainloop.h index 18be1f2b..db2797fb 100644 --- a/src/pulse/mainloop.h +++ b/src/pulse/mainloop.h @@ -6,6 +6,9 @@ /*** This file is part of PulseAudio. + Copyright 2004-2006 Lennart Poettering + Copyright 2006 Pierre Ossman for Cendio AB + 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 of the License, diff --git a/src/pulse/operation.c b/src/pulse/operation.c index e039e8c9..f23def50 100644 --- a/src/pulse/operation.c +++ b/src/pulse/operation.c @@ -3,6 +3,8 @@ /*** 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 of the License, diff --git a/src/pulse/operation.h b/src/pulse/operation.h index adfd5f2d..97d1c6b8 100644 --- a/src/pulse/operation.h +++ b/src/pulse/operation.h @@ -6,6 +6,8 @@ /*** 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 of the License, diff --git a/src/pulse/pulseaudio.h b/src/pulse/pulseaudio.h index 5543d7cd..88d1275b 100644 --- a/src/pulse/pulseaudio.h +++ b/src/pulse/pulseaudio.h @@ -6,6 +6,9 @@ /*** This file is part of PulseAudio. + Copyright 2004-2006 Lennart Poettering + Copyright 2006 Pierre Ossman for Cendio AB + 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 diff --git a/src/pulse/sample.c b/src/pulse/sample.c index dd68ac17..aafafc83 100644 --- a/src/pulse/sample.c +++ b/src/pulse/sample.c @@ -3,6 +3,9 @@ /*** This file is part of PulseAudio. + Copyright 2004-2006 Lennart Poettering + Copyright 2006 Pierre Ossman for Cendio AB + 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 of the License, diff --git a/src/pulse/sample.h b/src/pulse/sample.h index b7c537e4..5e603685 100644 --- a/src/pulse/sample.h +++ b/src/pulse/sample.h @@ -6,6 +6,9 @@ /*** This file is part of PulseAudio. + Copyright 2004-2006 Lennart Poettering + Copyright 2006 Pierre Ossman for Cendio AB + 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 of the License, diff --git a/src/pulse/scache.c b/src/pulse/scache.c index 5abaf845..09bc1078 100644 --- a/src/pulse/scache.c +++ b/src/pulse/scache.c @@ -3,6 +3,8 @@ /*** 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 of the License, diff --git a/src/pulse/scache.h b/src/pulse/scache.h index 2293dec4..31fd8956 100644 --- a/src/pulse/scache.h +++ b/src/pulse/scache.h @@ -6,6 +6,9 @@ /*** This file is part of PulseAudio. + Copyright 2004-2006 Lennart Poettering + Copyright 2006 Pierre Ossman for Cendio AB + 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 of the License, diff --git a/src/pulse/simple.c b/src/pulse/simple.c index 1c632fa7..3cf862d2 100644 --- a/src/pulse/simple.c +++ b/src/pulse/simple.c @@ -3,6 +3,8 @@ /*** 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 of the License, diff --git a/src/pulse/simple.h b/src/pulse/simple.h index a97875f9..128d2716 100644 --- a/src/pulse/simple.h +++ b/src/pulse/simple.h @@ -6,6 +6,9 @@ /*** This file is part of PulseAudio. + Copyright 2004-2006 Lennart Poettering + Copyright 2006 Pierre Ossman for Cendio AB + 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 of the License, diff --git a/src/pulse/stream.c b/src/pulse/stream.c index 009eb3cf..f20c17ae 100644 --- a/src/pulse/stream.c +++ b/src/pulse/stream.c @@ -3,6 +3,9 @@ /*** This file is part of PulseAudio. + Copyright 2004-2006 Lennart Poettering + Copyright 2006 Pierre Ossman for Cendio AB + 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 of the License, diff --git a/src/pulse/stream.h b/src/pulse/stream.h index 2ce53458..65603262 100644 --- a/src/pulse/stream.h +++ b/src/pulse/stream.h @@ -6,6 +6,9 @@ /*** This file is part of PulseAudio. + Copyright 2004-2006 Lennart Poettering + Copyright 2006 Pierre Ossman for Cendio AB + 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 of the License, diff --git a/src/pulse/subscribe.c b/src/pulse/subscribe.c index d9abf6f8..5d8f1252 100644 --- a/src/pulse/subscribe.c +++ b/src/pulse/subscribe.c @@ -3,6 +3,8 @@ /*** 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 of the License, diff --git a/src/pulse/subscribe.h b/src/pulse/subscribe.h index 35d5a31c..c37ead57 100644 --- a/src/pulse/subscribe.h +++ b/src/pulse/subscribe.h @@ -6,6 +6,9 @@ /*** This file is part of PulseAudio. + Copyright 2004-2006 Lennart Poettering + Copyright 2006 Pierre Ossman for Cendio AB + 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 of the License, diff --git a/src/pulse/thread-mainloop.c b/src/pulse/thread-mainloop.c index ad29f5ba..4f3cacc9 100644 --- a/src/pulse/thread-mainloop.c +++ b/src/pulse/thread-mainloop.c @@ -3,6 +3,9 @@ /*** This file is part of PulseAudio. + Copyright 2006 Lennart Poettering + Copyright 2006 Pierre Ossman for Cendio AB + 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 of the License, diff --git a/src/pulse/thread-mainloop.h b/src/pulse/thread-mainloop.h index 48048b35..b78c1583 100644 --- a/src/pulse/thread-mainloop.h +++ b/src/pulse/thread-mainloop.h @@ -6,6 +6,9 @@ /*** This file is part of PulseAudio. + Copyright 2006 Lennart Poettering + Copyright 2006 Pierre Ossman for Cendio AB + 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 of the License, diff --git a/src/pulse/timeval.c b/src/pulse/timeval.c index e5a86a63..78ece061 100644 --- a/src/pulse/timeval.c +++ b/src/pulse/timeval.c @@ -3,6 +3,9 @@ /*** This file is part of PulseAudio. + Copyright 2004-2006 Lennart Poettering + Copyright 2006 Pierre Ossman for Cendio AB + 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 diff --git a/src/pulse/timeval.h b/src/pulse/timeval.h index c10ec9f6..1e5627e3 100644 --- a/src/pulse/timeval.h +++ b/src/pulse/timeval.h @@ -6,6 +6,9 @@ /*** This file is part of PulseAudio. + Copyright 2004-2006 Lennart Poettering + Copyright 2006 Pierre Ossman for Cendio AB + 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 diff --git a/src/pulse/utf8.c b/src/pulse/utf8.c index 2708c518..2ac2d106 100644 --- a/src/pulse/utf8.c +++ b/src/pulse/utf8.c @@ -1,5 +1,27 @@ /* $Id$ */ +/*** + This file is part of PulseAudio. + + Copyright 2006 Lennart Poettering + Copyright 2006 Pierre Ossman for Cendio AB + + 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. +***/ + /* This file is based on the GLIB utf8 validation functions. The * original license text follows. */ diff --git a/src/pulse/utf8.h b/src/pulse/utf8.h index 7225471f..ff8dc215 100644 --- a/src/pulse/utf8.h +++ b/src/pulse/utf8.h @@ -6,6 +6,9 @@ /*** This file is part of PulseAudio. + Copyright 2006 Lennart Poettering + Copyright 2006 Pierre Ossman for Cendio AB + 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 diff --git a/src/pulse/util.c b/src/pulse/util.c index 63f54e3b..d561329c 100644 --- a/src/pulse/util.c +++ b/src/pulse/util.c @@ -3,6 +3,9 @@ /*** This file is part of PulseAudio. + Copyright 2004-2006 Lennart Poettering + Copyright 2006 Pierre Ossman for Cendio AB + 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 diff --git a/src/pulse/util.h b/src/pulse/util.h index 0be169fb..95bd86f3 100644 --- a/src/pulse/util.h +++ b/src/pulse/util.h @@ -6,6 +6,9 @@ /*** This file is part of PulseAudio. + Copyright 2004-2006 Lennart Poettering + Copyright 2006 Pierre Ossman for Cendio AB + 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 diff --git a/src/pulse/version.h.in b/src/pulse/version.h.in index 748541a1..20c7a9c0 100644 --- a/src/pulse/version.h.in +++ b/src/pulse/version.h.in @@ -5,6 +5,9 @@ /*** This file is part of PulseAudio. + + Copyright 2004-2006 Lennart Poettering + Copyright 2006 Pierre Ossman for Cendio AB PulseAudio is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published diff --git a/src/pulse/volume.c b/src/pulse/volume.c index aa7ddba2..feb33f07 100644 --- a/src/pulse/volume.c +++ b/src/pulse/volume.c @@ -3,6 +3,8 @@ /*** 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 of the License, diff --git a/src/pulse/volume.h b/src/pulse/volume.h index 62d2867a..a928ff71 100644 --- a/src/pulse/volume.h +++ b/src/pulse/volume.h @@ -6,6 +6,9 @@ /*** This file is part of PulseAudio. + Copyright 2004-2006 Lennart Poettering + Copyright 2006 Pierre Ossman for Cendio AB + 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 of the License, diff --git a/src/pulse/xmalloc.c b/src/pulse/xmalloc.c index dda42bdc..1f0734c2 100644 --- a/src/pulse/xmalloc.c +++ b/src/pulse/xmalloc.c @@ -3,6 +3,8 @@ /*** 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 of the License, diff --git a/src/pulse/xmalloc.h b/src/pulse/xmalloc.h index 2fb1c5f0..2f6399c5 100644 --- a/src/pulse/xmalloc.h +++ b/src/pulse/xmalloc.h @@ -6,6 +6,8 @@ /*** 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 of the License, -- cgit From 0e53f939c011a5f2ebc15658029f0a578e0bd68f Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 23 May 2007 12:32:37 +0000 Subject: Treat empty :0.0 identically to unset :0.0 when trying to find a PA server. (Closes #87) git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@1443 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/client-conf-x11.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/pulse') diff --git a/src/pulse/client-conf-x11.c b/src/pulse/client-conf-x11.c index b5ac8d9f..e8de9553 100644 --- a/src/pulse/client-conf-x11.c +++ b/src/pulse/client-conf-x11.c @@ -44,7 +44,7 @@ int pa_client_conf_from_x11(pa_client_conf *c, const char *dname) { int ret = -1; char t[1024]; - if (!dname && !getenv("DISPLAY")) + if (!dname && (!(dname = getenv("DISPLAY")) || *dname == '\0')) goto finish; if (!(d = XOpenDisplay(dname))) { -- cgit From 4a05bc9bdcb4404f3e23cef367ff378b9e39d220 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 23 May 2007 16:57:35 +0000 Subject: don't allow excessively high sample rates git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@1449 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/sample.c | 1 + src/pulse/sample.h | 3 +++ 2 files changed, 4 insertions(+) (limited to 'src/pulse') diff --git a/src/pulse/sample.c b/src/pulse/sample.c index aafafc83..ffdeedf7 100644 --- a/src/pulse/sample.c +++ b/src/pulse/sample.c @@ -80,6 +80,7 @@ int pa_sample_spec_valid(const pa_sample_spec *spec) { assert(spec); if (spec->rate <= 0 || + spec->rate > PA_RATE_MAX || spec->channels <= 0 || spec->channels > PA_CHANNELS_MAX || spec->format >= PA_SAMPLE_MAX || diff --git a/src/pulse/sample.h b/src/pulse/sample.h index 5e603685..683167cc 100644 --- a/src/pulse/sample.h +++ b/src/pulse/sample.h @@ -105,6 +105,9 @@ PA_C_DECL_BEGIN /** Maximum number of allowed channels */ #define PA_CHANNELS_MAX 32 +/** Maximum allowed sample rate */ +#define PA_RATE_MAX (48000*4) + /** Sample format */ typedef enum pa_sample_format { PA_SAMPLE_U8, /**< Unsigned 8 Bit PCM */ -- cgit From 003264213cd8e5e6535ed3cf672a78b27055bf28 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 23 May 2007 23:38:28 +0000 Subject: only browse for ipv4 pa servers for now. Needs better fixing which however is not trivial and probably breaks the API git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@1453 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/browser.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/pulse') diff --git a/src/pulse/browser.c b/src/pulse/browser.c index 27c5a2ea..ea2706e4 100644 --- a/src/pulse/browser.c +++ b/src/pulse/browser.c @@ -337,7 +337,7 @@ pa_browser *pa_browser_new_full(pa_mainloop_api *mainloop, pa_browse_flags_t fla !(b->server_browser = avahi_service_browser_new( b->client, AVAHI_IF_UNSPEC, - AVAHI_PROTO_UNSPEC, + AVAHI_PROTO_INET, SERVICE_TYPE_SERVER, NULL, 0, -- cgit From a67c21f093202f142438689d3f7cfbdf4ea82eea Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sun, 28 Oct 2007 19:13:50 +0000 Subject: merge 'lennart' branch back into trunk. git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@1971 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/browser.c | 62 +++++--- src/pulse/cdecl.h | 18 +++ src/pulse/channelmap.c | 108 ++++++++++--- src/pulse/channelmap.h | 9 +- src/pulse/client-conf-x11.c | 6 +- src/pulse/client-conf.c | 19 +-- src/pulse/context.c | 198 ++++++++++++----------- src/pulse/glib-mainloop.c | 2 - src/pulse/internal.h | 9 +- src/pulse/introspect.c | 372 ++++++++++++++++++++++++++++++-------------- src/pulse/introspect.h | 16 ++ src/pulse/mainloop-api.c | 22 ++- src/pulse/mainloop-signal.c | 55 ++++--- src/pulse/mainloop.c | 173 ++++++++++---------- src/pulse/operation.c | 41 +++-- src/pulse/sample.c | 79 ++++++---- src/pulse/sample.h | 18 +-- src/pulse/scache.c | 17 +- src/pulse/simple.c | 49 +++--- src/pulse/simple.h | 2 +- src/pulse/stream.c | 319 +++++++++++++++++++++---------------- src/pulse/subscribe.c | 19 +-- src/pulse/thread-mainloop.c | 56 ++++--- src/pulse/thread-mainloop.h | 3 + src/pulse/timeval.c | 59 ++++--- src/pulse/timeval.h | 17 +- src/pulse/utf8.c | 32 ++-- src/pulse/utf8.h | 2 +- src/pulse/util.c | 42 ++--- src/pulse/util.h | 2 +- src/pulse/volume.c | 34 ++-- src/pulse/volume.h | 20 +-- src/pulse/xmalloc.c | 14 +- src/pulse/xmalloc.h | 9 ++ 34 files changed, 1154 insertions(+), 749 deletions(-) (limited to 'src/pulse') diff --git a/src/pulse/browser.c b/src/pulse/browser.c index ea2706e4..55e0b2cd 100644 --- a/src/pulse/browser.c +++ b/src/pulse/browser.c @@ -25,7 +25,6 @@ #include "config.h" #endif -#include #include #include @@ -36,8 +35,9 @@ #include #include - #include +#include +#include #include "browser.h" @@ -46,7 +46,8 @@ #define SERVICE_TYPE_SERVER "_pulse-server._tcp." struct pa_browser { - int ref; + PA_REFCNT_DECLARE; + pa_mainloop_api *mainloop; AvahiPoll* avahi_poll; @@ -62,6 +63,7 @@ struct pa_browser { }; static int map_to_opcode(const char *type, int new) { + if (avahi_domain_equal(type, SERVICE_TYPE_SINK)) return new ? PA_BROWSE_NEW_SINK : PA_BROWSE_REMOVE_SINK; else if (avahi_domain_equal(type, SERVICE_TYPE_SOURCE)) @@ -97,7 +99,8 @@ static void resolve_callback( int ss_valid = 0; char *key = NULL, *value = NULL; - assert(b); + pa_assert(b); + pa_assert(PA_REFCNT_VALUE(b) >= 1); memset(&i, 0, sizeof(i)); i.name = name; @@ -109,13 +112,13 @@ static void resolve_callback( goto fail; opcode = map_to_opcode(type, 1); - assert(opcode >= 0); + pa_assert(opcode >= 0); if (aa->proto == AVAHI_PROTO_INET) - snprintf(a, sizeof(a), "tcp:%s:%u", avahi_address_snprint(ip, sizeof(ip), aa), port); + pa_snprintf(a, sizeof(a), "tcp:%s:%u", avahi_address_snprint(ip, sizeof(ip), aa), port); else { - assert(aa->proto == AVAHI_PROTO_INET6); - snprintf(a, sizeof(a), "tcp6:%s:%u", avahi_address_snprint(ip, sizeof(ip), aa), port); + pa_assert(aa->proto == AVAHI_PROTO_INET6); + pa_snprintf(a, sizeof(a), "tcp6:%s:%u", avahi_address_snprint(ip, sizeof(ip), aa), port); } i.server = a; @@ -146,7 +149,7 @@ static void resolve_callback( value = NULL; l = strlen(a); - assert(l+1 <= sizeof(a)); + pa_assert(l+1 <= sizeof(a)); strncat(a, " ", sizeof(a)-l-1); strncat(a, i.fqdn, sizeof(a)-l-2); } else if (!strcmp(key, "cookie")) { @@ -211,7 +214,9 @@ fail: static void handle_failure(pa_browser *b) { const char *e = NULL; - assert(b); + + pa_assert(b); + pa_assert(PA_REFCNT_VALUE(b) >= 1); if (b->sink_browser) avahi_service_browser_free(b->sink_browser); @@ -245,7 +250,9 @@ static void browse_callback( void *userdata) { pa_browser *b = userdata; - assert(b); + + pa_assert(b); + pa_assert(PA_REFCNT_VALUE(b) >= 1); switch (event) { case AVAHI_BROWSER_NEW: { @@ -276,7 +283,7 @@ static void browse_callback( i.name = name; opcode = map_to_opcode(type, 0); - assert(opcode >= 0); + pa_assert(opcode >= 0); b->callback(b, opcode, &i, b->userdata); } @@ -295,7 +302,10 @@ static void browse_callback( static void client_callback(AvahiClient *s, AvahiClientState state, void *userdata) { pa_browser *b = userdata; - assert(s); + + pa_assert(s); + pa_assert(b); + pa_assert(PA_REFCNT_VALUE(b) >= 1); if (state == AVAHI_CLIENT_FAILURE) handle_failure(b); @@ -311,14 +321,14 @@ pa_browser *pa_browser_new_full(pa_mainloop_api *mainloop, pa_browse_flags_t fla pa_browser *b; int error; - assert(mainloop); + pa_assert(mainloop); if (flags & ~(PA_BROWSE_FOR_SERVERS|PA_BROWSE_FOR_SINKS|PA_BROWSE_FOR_SOURCES) || flags == 0) return NULL; b = pa_xnew(pa_browser, 1); b->mainloop = mainloop; - b->ref = 1; + PA_REFCNT_INIT(b); b->callback = NULL; b->userdata = NULL; b->error_callback = NULL; @@ -391,7 +401,8 @@ fail: } static void browser_free(pa_browser *b) { - assert(b && b->mainloop); + pa_assert(b); + pa_assert(b->mainloop); if (b->sink_browser) avahi_service_browser_free(b->sink_browser); @@ -410,29 +421,32 @@ static void browser_free(pa_browser *b) { } pa_browser *pa_browser_ref(pa_browser *b) { - assert(b); - assert(b->ref >= 1); - b->ref++; + pa_assert(b); + pa_assert(PA_REFCNT_VALUE(b) >= 1); + + PA_REFCNT_INC(b); return b; } void pa_browser_unref(pa_browser *b) { - assert(b); - assert(b->ref >= 1); + pa_assert(b); + pa_assert(PA_REFCNT_VALUE(b) >= 1); - if ((-- (b->ref)) <= 0) + if (PA_REFCNT_DEC(b) <= 0) browser_free(b); } void pa_browser_set_callback(pa_browser *b, pa_browse_cb_t cb, void *userdata) { - assert(b); + pa_assert(b); + pa_assert(PA_REFCNT_VALUE(b) >= 1); b->callback = cb; b->userdata = userdata; } void pa_browser_set_error_callback(pa_browser *b, pa_browser_error_cb_t cb, void *userdata) { - assert(b); + pa_assert(b); + pa_assert(PA_REFCNT_VALUE(b) >= 1); b->error_callback = cb; b->error_userdata = userdata; diff --git a/src/pulse/cdecl.h b/src/pulse/cdecl.h index 922ad276..e1f23d25 100644 --- a/src/pulse/cdecl.h +++ b/src/pulse/cdecl.h @@ -41,4 +41,22 @@ #endif +#ifndef PA_GCC_PURE +#ifdef __GNUCC__ +#define PA_GCC_PURE __attribute__ ((pure)) +#else +/** This function's return value depends only the arguments list and global state **/ +#define PA_GCC_PURE +#endif +#endif + +#ifndef PA_GCC_CONST +#ifdef __GNUCC__ +#define PA_GCC_CONST __attribute__ ((pure)) +#else +/** This function's return value depends only the arguments list (stricter version of PA_GCC_CONST) **/ +#define PA_GCC_CONST +#endif +#endif + #endif diff --git a/src/pulse/channelmap.c b/src/pulse/channelmap.c index d5b8f743..2b8ef2b0 100644 --- a/src/pulse/channelmap.c +++ b/src/pulse/channelmap.c @@ -27,12 +27,12 @@ #endif #include -#include #include #include #include #include +#include #include "channelmap.h" @@ -90,18 +90,81 @@ const char *const table[] = { [PA_CHANNEL_POSITION_TOP_CENTER] = "top-center", + [PA_CHANNEL_POSITION_TOP_FRONT_CENTER] = "top-front-center", [PA_CHANNEL_POSITION_TOP_FRONT_LEFT] = "top-front-left", [PA_CHANNEL_POSITION_TOP_FRONT_RIGHT] = "top-front-right", - [PA_CHANNEL_POSITION_TOP_FRONT_CENTER] = "top-front-center", + [PA_CHANNEL_POSITION_TOP_REAR_CENTER] = "top-rear-center", [PA_CHANNEL_POSITION_TOP_REAR_LEFT] = "top-rear-left", - [PA_CHANNEL_POSITION_TOP_REAR_RIGHT] = "top-rear-right", - [PA_CHANNEL_POSITION_TOP_REAR_CENTER] = "top-rear-center" + [PA_CHANNEL_POSITION_TOP_REAR_RIGHT] = "top-rear-right" +}; + +const char *const pretty_table[] = { + [PA_CHANNEL_POSITION_MONO] = "Mono", + + [PA_CHANNEL_POSITION_FRONT_CENTER] = "Front Center", + [PA_CHANNEL_POSITION_FRONT_LEFT] = "Front Left", + [PA_CHANNEL_POSITION_FRONT_RIGHT] = "Front Right", + + [PA_CHANNEL_POSITION_REAR_CENTER] = "Rear Center", + [PA_CHANNEL_POSITION_REAR_LEFT] = "Rear Left", + [PA_CHANNEL_POSITION_REAR_RIGHT] = "Rear Right", + + [PA_CHANNEL_POSITION_LFE] = "Low Frequency Emmiter", + + [PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER] = "Front Left-of-center", + [PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER] = "Front Right-of-center", + + [PA_CHANNEL_POSITION_SIDE_LEFT] = "Side Left", + [PA_CHANNEL_POSITION_SIDE_RIGHT] = "Side Right", + + [PA_CHANNEL_POSITION_AUX0] = "Auxiliary 0", + [PA_CHANNEL_POSITION_AUX1] = "Auxiliary 1", + [PA_CHANNEL_POSITION_AUX2] = "Auxiliary 2", + [PA_CHANNEL_POSITION_AUX3] = "Auxiliary 3", + [PA_CHANNEL_POSITION_AUX4] = "Auxiliary 4", + [PA_CHANNEL_POSITION_AUX5] = "Auxiliary 5", + [PA_CHANNEL_POSITION_AUX6] = "Auxiliary 6", + [PA_CHANNEL_POSITION_AUX7] = "Auxiliary 7", + [PA_CHANNEL_POSITION_AUX8] = "Auxiliary 8", + [PA_CHANNEL_POSITION_AUX9] = "Auxiliary 9", + [PA_CHANNEL_POSITION_AUX10] = "Auxiliary 10", + [PA_CHANNEL_POSITION_AUX11] = "Auxiliary 11", + [PA_CHANNEL_POSITION_AUX12] = "Auxiliary 12", + [PA_CHANNEL_POSITION_AUX13] = "Auxiliary 13", + [PA_CHANNEL_POSITION_AUX14] = "Auxiliary 14", + [PA_CHANNEL_POSITION_AUX15] = "Auxiliary 15", + [PA_CHANNEL_POSITION_AUX16] = "Auxiliary 16", + [PA_CHANNEL_POSITION_AUX17] = "Auxiliary 17", + [PA_CHANNEL_POSITION_AUX18] = "Auxiliary 18", + [PA_CHANNEL_POSITION_AUX19] = "Auxiliary 19", + [PA_CHANNEL_POSITION_AUX20] = "Auxiliary 20", + [PA_CHANNEL_POSITION_AUX21] = "Auxiliary 21", + [PA_CHANNEL_POSITION_AUX22] = "Auxiliary 22", + [PA_CHANNEL_POSITION_AUX23] = "Auxiliary 23", + [PA_CHANNEL_POSITION_AUX24] = "Auxiliary 24", + [PA_CHANNEL_POSITION_AUX25] = "Auxiliary 25", + [PA_CHANNEL_POSITION_AUX26] = "Auxiliary 26", + [PA_CHANNEL_POSITION_AUX27] = "Auxiliary 27", + [PA_CHANNEL_POSITION_AUX28] = "Auxiliary 28", + [PA_CHANNEL_POSITION_AUX29] = "Auxiliary 29", + [PA_CHANNEL_POSITION_AUX30] = "Auxiliary 30", + [PA_CHANNEL_POSITION_AUX31] = "Auxiliary 31", + + [PA_CHANNEL_POSITION_TOP_CENTER] = "Top Center", + + [PA_CHANNEL_POSITION_TOP_FRONT_CENTER] = "Top Front Center", + [PA_CHANNEL_POSITION_TOP_FRONT_LEFT] = "Top Front Left", + [PA_CHANNEL_POSITION_TOP_FRONT_RIGHT] = "Top Front Right", + + [PA_CHANNEL_POSITION_TOP_REAR_CENTER] = "Top Rear Center", + [PA_CHANNEL_POSITION_TOP_REAR_LEFT] = "Top Rear left", + [PA_CHANNEL_POSITION_TOP_REAR_RIGHT] = "Top Rear Right" }; pa_channel_map* pa_channel_map_init(pa_channel_map *m) { unsigned c; - assert(m); + pa_assert(m); m->channels = 0; @@ -112,7 +175,7 @@ pa_channel_map* pa_channel_map_init(pa_channel_map *m) { } pa_channel_map* pa_channel_map_init_mono(pa_channel_map *m) { - assert(m); + pa_assert(m); pa_channel_map_init(m); @@ -122,7 +185,7 @@ pa_channel_map* pa_channel_map_init_mono(pa_channel_map *m) { } pa_channel_map* pa_channel_map_init_stereo(pa_channel_map *m) { - assert(m); + pa_assert(m); pa_channel_map_init(m); @@ -133,9 +196,9 @@ pa_channel_map* pa_channel_map_init_stereo(pa_channel_map *m) { } pa_channel_map* pa_channel_map_init_auto(pa_channel_map *m, unsigned channels, pa_channel_map_def_t def) { - assert(m); - assert(channels > 0); - assert(channels <= PA_CHANNELS_MAX); + pa_assert(m); + pa_assert(channels > 0); + pa_assert(channels <= PA_CHANNELS_MAX); pa_channel_map_init(m); @@ -342,11 +405,18 @@ const char* pa_channel_position_to_string(pa_channel_position_t pos) { return table[pos]; } +const char* pa_channel_position_to_pretty_string(pa_channel_position_t pos) { + if (pos < 0 || pos >= PA_CHANNEL_POSITION_MAX) + return NULL; + + return pretty_table[pos]; +} + int pa_channel_map_equal(const pa_channel_map *a, const pa_channel_map *b) { unsigned c; - assert(a); - assert(b); + pa_assert(a); + pa_assert(b); if (a->channels != b->channels) return 0; @@ -363,14 +433,14 @@ char* pa_channel_map_snprint(char *s, size_t l, const pa_channel_map *map) { int first = 1; char *e; - assert(s); - assert(l > 0); - assert(map); + pa_assert(s); + pa_assert(l > 0); + pa_assert(map); *(e = s) = 0; for (channel = 0; channel < map->channels && l > 1; channel++) { - l -= snprintf(e, l, "%s%s", + l -= pa_snprintf(e, l, "%s%s", first ? "" : ",", pa_channel_position_to_string(map->map[channel])); @@ -386,8 +456,8 @@ pa_channel_map *pa_channel_map_parse(pa_channel_map *rmap, const char *s) { pa_channel_map map; char *p; - assert(rmap); - assert(s); + pa_assert(rmap); + pa_assert(s); memset(&map, 0, sizeof(map)); @@ -447,7 +517,7 @@ finish: int pa_channel_map_valid(const pa_channel_map *map) { unsigned c; - assert(map); + pa_assert(map); if (map->channels <= 0 || map->channels > PA_CHANNELS_MAX) return 0; diff --git a/src/pulse/channelmap.h b/src/pulse/channelmap.h index f0c8f475..a05e1911 100644 --- a/src/pulse/channelmap.h +++ b/src/pulse/channelmap.h @@ -172,7 +172,10 @@ pa_channel_map* pa_channel_map_init_stereo(pa_channel_map *m); pa_channel_map* pa_channel_map_init_auto(pa_channel_map *m, unsigned channels, pa_channel_map_def_t def); /** Return a text label for the specified channel position */ -const char* pa_channel_position_to_string(pa_channel_position_t pos); +const char* pa_channel_position_to_string(pa_channel_position_t pos) PA_GCC_PURE; + +/** Return a human readable text label for the specified channel position. \since 0.9.7 */ +const char* pa_channel_position_to_pretty_string(pa_channel_position_t pos); /** The maximum length of strings returned by pa_channel_map_snprint() */ #define PA_CHANNEL_MAP_SNPRINT_MAX 336 @@ -184,10 +187,10 @@ char* pa_channel_map_snprint(char *s, size_t l, const pa_channel_map *map); pa_channel_map *pa_channel_map_parse(pa_channel_map *map, const char *s); /** Compare two channel maps. Return 1 if both match. */ -int pa_channel_map_equal(const pa_channel_map *a, const pa_channel_map *b); +int pa_channel_map_equal(const pa_channel_map *a, const pa_channel_map *b) PA_GCC_PURE; /** Return non-zero of the specified channel map is considered valid */ -int pa_channel_map_valid(const pa_channel_map *map); +int pa_channel_map_valid(const pa_channel_map *map) PA_GCC_PURE; PA_C_DECL_END diff --git a/src/pulse/client-conf-x11.c b/src/pulse/client-conf-x11.c index e8de9553..e240ba88 100644 --- a/src/pulse/client-conf-x11.c +++ b/src/pulse/client-conf-x11.c @@ -26,7 +26,6 @@ #endif #include -#include #include #include @@ -36,6 +35,7 @@ #include #include #include +#include #include "client-conf-x11.h" @@ -44,6 +44,8 @@ int pa_client_conf_from_x11(pa_client_conf *c, const char *dname) { int ret = -1; char t[1024]; + pa_assert(c); + if (!dname && (!(dname = getenv("DISPLAY")) || *dname == '\0')) goto finish; @@ -75,7 +77,7 @@ int pa_client_conf_from_x11(pa_client_conf *c, const char *dname) { goto finish; } - assert(sizeof(cookie) == sizeof(c->cookie)); + pa_assert(sizeof(cookie) == sizeof(c->cookie)); memcpy(c->cookie, cookie, sizeof(cookie)); c->cookie_valid = 1; diff --git a/src/pulse/client-conf.c b/src/pulse/client-conf.c index bb912335..abd277a6 100644 --- a/src/pulse/client-conf.c +++ b/src/pulse/client-conf.c @@ -27,14 +27,14 @@ #endif #include -#include #include #include #include -#include #include +#include +#include #include #include #include @@ -42,13 +42,7 @@ #include "client-conf.h" -#ifndef OS_IS_WIN32 -# define PATH_SEP "/" -#else -# define PATH_SEP "\\" -#endif - -#define DEFAULT_CLIENT_CONFIG_FILE PA_DEFAULT_CONFIG_DIR PATH_SEP "client.conf" +#define DEFAULT_CLIENT_CONFIG_FILE PA_DEFAULT_CONFIG_DIR PA_PATH_SEP "client.conf" #define DEFAULT_CLIENT_CONFIG_FILE_USER "client.conf" #define ENV_CLIENT_CONFIG_FILE "PULSE_CLIENTCONFIG" @@ -81,7 +75,7 @@ pa_client_conf *pa_client_conf_new(void) { } void pa_client_conf_free(pa_client_conf *c) { - assert(c); + pa_assert(c); pa_xfree(c->daemon_binary); pa_xfree(c->extra_arguments); pa_xfree(c->default_sink); @@ -90,6 +84,7 @@ void pa_client_conf_free(pa_client_conf *c) { pa_xfree(c->cookie_file); pa_xfree(c); } + int pa_client_conf_load(pa_client_conf *c, const char *filename) { FILE *f = NULL; char *fn = NULL; @@ -122,7 +117,7 @@ int pa_client_conf_load(pa_client_conf *c, const char *filename) { pa_open_config_file(DEFAULT_CLIENT_CONFIG_FILE, DEFAULT_CLIENT_CONFIG_FILE_USER, ENV_CLIENT_CONFIG_FILE, &fn, "r"); if (!f && errno != EINTR) { - pa_log("WARNING: failed to open configuration file '%s': %s", fn, pa_cstrerror(errno)); + pa_log_warn("Failed to open configuration file '%s': %s", fn, pa_cstrerror(errno)); goto finish; } @@ -175,7 +170,7 @@ int pa_client_conf_env(pa_client_conf *c) { } int pa_client_conf_load_cookie(pa_client_conf* c) { - assert(c); + pa_assert(c); c->cookie_valid = 0; diff --git a/src/pulse/context.c b/src/pulse/context.c index 58a5a879..805cd44e 100644 --- a/src/pulse/context.c +++ b/src/pulse/context.c @@ -27,7 +27,6 @@ #endif #include -#include #include #include #include @@ -67,6 +66,7 @@ #include #include #include +#include #include "internal.h" @@ -90,7 +90,7 @@ static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = { }; static void unlock_autospawn_lock_file(pa_context *c) { - assert(c); + pa_assert(c); if (c->autospawn_lock_fd >= 0) { char lf[PATH_MAX]; @@ -106,11 +106,11 @@ static void context_free(pa_context *c); pa_context *pa_context_new(pa_mainloop_api *mainloop, const char *name) { pa_context *c; - assert(mainloop); - assert(name); + pa_assert(mainloop); + pa_assert(name); c = pa_xnew(pa_context, 1); - c->ref = 1; + PA_REFCNT_INIT(c); c->name = pa_xstrdup(name); c->mainloop = mainloop; c->client = NULL; @@ -168,7 +168,7 @@ pa_context *pa_context_new(pa_mainloop_api *mainloop, const char *name) { } static void context_free(pa_context *c) { - assert(c); + pa_assert(c); unlock_autospawn_lock_file(c); @@ -183,7 +183,7 @@ static void context_free(pa_context *c) { if (c->pdispatch) pa_pdispatch_unref(c->pdispatch); if (c->pstream) { - pa_pstream_close(c->pstream); + pa_pstream_unlink(c->pstream); pa_pstream_unref(c->pstream); } @@ -206,24 +206,24 @@ static void context_free(pa_context *c) { } pa_context* pa_context_ref(pa_context *c) { - assert(c); - assert(c->ref >= 1); + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); - c->ref++; + PA_REFCNT_INC(c); return c; } void pa_context_unref(pa_context *c) { - assert(c); - assert(c->ref >= 1); + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); - if (--c->ref <= 0) + if (PA_REFCNT_DEC(c) <= 0) context_free(c); } void pa_context_set_state(pa_context *c, pa_context_state_t st) { - assert(c); - assert(c->ref >= 1); + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); if (c->state == st) return; @@ -250,7 +250,7 @@ void pa_context_set_state(pa_context *c, pa_context_state_t st) { c->pdispatch = NULL; if (c->pstream) { - pa_pstream_close(c->pstream); + pa_pstream_unlink(c->pstream); pa_pstream_unref(c->pstream); } c->pstream = NULL; @@ -264,16 +264,16 @@ void pa_context_set_state(pa_context *c, pa_context_state_t st) { } void pa_context_fail(pa_context *c, int error) { - assert(c); - assert(c->ref >= 1); + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); pa_context_set_error(c, error); pa_context_set_state(c, PA_CONTEXT_FAILED); } int pa_context_set_error(pa_context *c, int error) { - assert(error >= 0); - assert(error < PA_ERR_MAX); + pa_assert(error >= 0); + pa_assert(error < PA_ERR_MAX); if (c) c->error = error; @@ -284,8 +284,8 @@ int pa_context_set_error(pa_context *c, int error) { static void pstream_die_callback(pa_pstream *p, void *userdata) { pa_context *c = userdata; - assert(p); - assert(c); + pa_assert(p); + pa_assert(c); pa_context_fail(c, PA_ERR_CONNECTIONTERMINATED); } @@ -293,9 +293,9 @@ static void pstream_die_callback(pa_pstream *p, void *userdata) { static void pstream_packet_callback(pa_pstream *p, pa_packet *packet, const pa_creds *creds, void *userdata) { pa_context *c = userdata; - assert(p); - assert(packet); - assert(c); + pa_assert(p); + pa_assert(packet); + pa_assert(c); pa_context_ref(c); @@ -309,18 +309,19 @@ static void pstream_memblock_callback(pa_pstream *p, uint32_t channel, int64_t o pa_context *c = userdata; pa_stream *s; - assert(p); - assert(chunk); - assert(chunk->memblock); - assert(chunk->length); - assert(c); - assert(c->ref >= 1); + pa_assert(p); + pa_assert(chunk); + pa_assert(chunk->memblock); + pa_assert(chunk->length); + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); pa_context_ref(c); if ((s = pa_dynarray_get(c->record_streams, channel))) { - assert(seek == PA_SEEK_RELATIVE && offset == 0); + pa_assert(seek == PA_SEEK_RELATIVE); + pa_assert(offset == 0); pa_memblockq_seek(s->record_memblockq, offset, seek); pa_memblockq_push_align(s->record_memblockq, chunk); @@ -337,11 +338,11 @@ static void pstream_memblock_callback(pa_pstream *p, uint32_t channel, int64_t o } int pa_context_handle_error(pa_context *c, uint32_t command, pa_tagstruct *t) { - assert(c); - assert(c->ref >= 1); + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); if (command == PA_COMMAND_ERROR) { - assert(t); + pa_assert(t); if (pa_tagstruct_getu32(t, &c->error) < 0) { pa_context_fail(c, PA_ERR_PROTOCOL); @@ -361,9 +362,9 @@ int pa_context_handle_error(pa_context *c, uint32_t command, pa_tagstruct *t) { static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { pa_context *c = userdata; - assert(pd); - assert(c); - assert(c->state == PA_CONTEXT_AUTHORIZING || c->state == PA_CONTEXT_SETTING_NAME); + pa_assert(pd); + pa_assert(c); + pa_assert(c->state == PA_CONTEXT_AUTHORIZING || c->state == PA_CONTEXT_SETTING_NAME); pa_context_ref(c); @@ -423,7 +424,7 @@ static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t break; default: - assert(0); + pa_assert(0); } finish: @@ -434,19 +435,19 @@ static void setup_context(pa_context *c, pa_iochannel *io) { pa_tagstruct *t; uint32_t tag; - assert(c); - assert(io); + pa_assert(c); + pa_assert(io); pa_context_ref(c); - assert(!c->pstream); + pa_assert(!c->pstream); c->pstream = pa_pstream_new(c->mainloop, io, c->mempool); pa_pstream_set_die_callback(c->pstream, pstream_die_callback, c); pa_pstream_set_recieve_packet_callback(c->pstream, pstream_packet_callback, c); pa_pstream_set_recieve_memblock_callback(c->pstream, pstream_memblock_callback, c); - assert(!c->pdispatch); + pa_assert(!c->pdispatch); c->pdispatch = pa_pdispatch_new(c->mainloop, command_table, PA_COMMAND_MAX); if (!c->conf->cookie_valid) @@ -497,10 +498,10 @@ static int context_connect_spawn(pa_context *c) { goto fail; } - pa_fd_set_cloexec(fds[0], 1); + pa_make_fd_cloexec(fds[0]); - pa_socket_low_delay(fds[0]); - pa_socket_low_delay(fds[1]); + pa_make_socket_low_delay(fds[0]); + pa_make_socket_low_delay(fds[1]); if (c->spawn_api.prefork) c->spawn_api.prefork(); @@ -523,7 +524,7 @@ static int context_connect_spawn(pa_context *c) { int n; /* Not required, since fds[0] has CLOEXEC enabled anyway */ - close(fds[0]); + pa_assert_se(pa_close(fds[0]) == 0); if (c->spawn_api.atfork) c->spawn_api.atfork(); @@ -535,7 +536,7 @@ static int context_connect_spawn(pa_context *c) { argv[n++] = c->conf->daemon_binary; argv[n++] = "--daemonize=yes"; - snprintf(t, sizeof(t), "-Lmodule-native-protocol-fd fd=%i", fds[1]); + pa_snprintf(t, sizeof(t), "-Lmodule-native-protocol-fd fd=%i", fds[1]); argv[n++] = strdup(t); while (n < MAX_ARGS) { @@ -570,7 +571,7 @@ static int context_connect_spawn(pa_context *c) { goto fail; } - close(fds[1]); + pa_assert_se(pa_close(fds[1]) == 0); c->is_local = 1; @@ -584,10 +585,7 @@ static int context_connect_spawn(pa_context *c) { return 0; fail: - if (fds[0] != -1) - close(fds[0]); - if (fds[1] != -1) - close(fds[1]); + pa_close_pipe(fds); unlock_autospawn_lock_file(c); @@ -602,8 +600,8 @@ static int try_next_connection(pa_context *c) { char *u = NULL; int r = -1; - assert(c); - assert(!c->client); + pa_assert(c); + pa_assert(!c->client); for (;;) { pa_xfree(u); @@ -648,9 +646,9 @@ finish: static void on_connection(pa_socket_client *client, pa_iochannel*io, void *userdata) { pa_context *c = userdata; - assert(client); - assert(c); - assert(c->state == PA_CONTEXT_CONNECTING); + pa_assert(client); + pa_assert(c); + pa_assert(c->state == PA_CONTEXT_CONNECTING); pa_context_ref(c); @@ -683,8 +681,8 @@ int pa_context_connect( int r = -1; - assert(c); - assert(c->ref >= 1); + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); PA_CHECK_VALIDITY(c, c->state == PA_CONTEXT_UNCONNECTED, PA_ERR_BADSTATE); PA_CHECK_VALIDITY(c, !(flags & ~PA_CONTEXT_NOAUTOSPAWN), PA_ERR_INVALID); @@ -695,7 +693,7 @@ int pa_context_connect( pa_context_ref(c); - assert(!c->server_list); + pa_assert(!c->server_list); if (server) { if (!(c->server_list = pa_strlist_parse(server))) { @@ -735,7 +733,7 @@ int pa_context_connect( pa_runtime_path(AUTOSPAWN_LOCK, lf, sizeof(lf)); pa_make_secure_parent_dir(lf, 0700, (uid_t)-1, (gid_t)-1); - assert(c->autospawn_lock_fd <= 0); + pa_assert(c->autospawn_lock_fd <= 0); c->autospawn_lock_fd = pa_lock_lockfile(lf); if (api) @@ -755,37 +753,37 @@ finish: } void pa_context_disconnect(pa_context *c) { - assert(c); - assert(c->ref >= 1); + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); pa_context_set_state(c, PA_CONTEXT_TERMINATED); } pa_context_state_t pa_context_get_state(pa_context *c) { - assert(c); - assert(c->ref >= 1); + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); return c->state; } int pa_context_errno(pa_context *c) { - assert(c); - assert(c->ref >= 1); + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); return c->error; } void pa_context_set_state_callback(pa_context *c, pa_context_notify_cb_t cb, void *userdata) { - assert(c); - assert(c->ref >= 1); + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); c->state_callback = cb; c->state_userdata = userdata; } int pa_context_is_pending(pa_context *c) { - assert(c); - assert(c->ref >= 1); + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); PA_CHECK_VALIDITY(c, c->state == PA_CONTEXT_CONNECTING || @@ -811,11 +809,11 @@ static void pstream_drain_callback(PA_GCC_UNUSED pa_pstream *s, void *userdata) static void set_dispatch_callbacks(pa_operation *o) { int done = 1; - assert(o); - assert(o->ref >= 1); - assert(o->context); - assert(o->context->ref >= 1); - assert(o->context->state == PA_CONTEXT_READY); + pa_assert(o); + pa_assert(PA_REFCNT_VALUE(o) >= 1); + pa_assert(o->context); + pa_assert(PA_REFCNT_VALUE(o->context) >= 1); + pa_assert(o->context->state == PA_CONTEXT_READY); pa_pstream_set_drain_callback(o->context->pstream, NULL, NULL); pa_pdispatch_set_drain_callback(o->context->pdispatch, NULL, NULL); @@ -844,8 +842,8 @@ static void set_dispatch_callbacks(pa_operation *o) { pa_operation* pa_context_drain(pa_context *c, pa_context_notify_cb_t cb, void *userdata) { pa_operation *o; - assert(c); - assert(c->ref >= 1); + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(c, pa_context_is_pending(c), PA_ERR_BADSTATE); @@ -860,9 +858,9 @@ void pa_context_simple_ack_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_U pa_operation *o = userdata; int success = 1; - assert(pd); - assert(o); - assert(o->ref >= 1); + pa_assert(pd); + pa_assert(o); + pa_assert(PA_REFCNT_VALUE(o) >= 1); if (!o->context) goto finish; @@ -892,8 +890,8 @@ pa_operation* pa_context_exit_daemon(pa_context *c, pa_context_success_cb_t cb, pa_operation *o; uint32_t tag; - assert(c); - assert(c->ref >= 1); + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); @@ -911,8 +909,8 @@ pa_operation* pa_context_send_simple_command(pa_context *c, uint32_t command, pa pa_operation *o; uint32_t tag; - assert(c); - assert(c->ref >= 1); + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); @@ -930,8 +928,8 @@ pa_operation* pa_context_set_default_sink(pa_context *c, const char *name, pa_co pa_operation *o; uint32_t tag; - assert(c); - assert(c->ref >= 1); + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); @@ -950,8 +948,8 @@ pa_operation* pa_context_set_default_source(pa_context *c, const char *name, pa_ pa_operation *o; uint32_t tag; - assert(c); - assert(c->ref >= 1); + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); @@ -966,7 +964,7 @@ pa_operation* pa_context_set_default_source(pa_context *c, const char *name, pa_ } int pa_context_is_local(pa_context *c) { - assert(c); + pa_assert(c); return c->is_local; } @@ -976,9 +974,9 @@ pa_operation* pa_context_set_name(pa_context *c, const char *name, pa_context_su pa_operation *o; uint32_t tag; - assert(c); - assert(c->ref >= 1); - assert(name); + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); + pa_assert(name); PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); @@ -997,8 +995,8 @@ const char* pa_get_library_version(void) { } const char* pa_context_get_server(pa_context *c) { - assert(c); - assert(c->ref >= 1); + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); if (!c->server) return NULL; @@ -1016,8 +1014,8 @@ uint32_t pa_context_get_protocol_version(PA_GCC_UNUSED pa_context *c) { } uint32_t pa_context_get_server_protocol_version(pa_context *c) { - assert(c); - assert(c->ref >= 1); + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); return c->version; } @@ -1025,8 +1023,8 @@ uint32_t pa_context_get_server_protocol_version(pa_context *c) { pa_tagstruct *pa_tagstruct_command(pa_context *c, uint32_t command, uint32_t *tag) { pa_tagstruct *t; - assert(c); - assert(tag); + pa_assert(c); + pa_assert(tag); t = pa_tagstruct_new(NULL, 0); pa_tagstruct_putu32(t, command); diff --git a/src/pulse/glib-mainloop.c b/src/pulse/glib-mainloop.c index 5b399aa2..b7a7537a 100644 --- a/src/pulse/glib-mainloop.c +++ b/src/pulse/glib-mainloop.c @@ -25,8 +25,6 @@ #include #endif -#include - #include #include diff --git a/src/pulse/internal.h b/src/pulse/internal.h index 52354fdc..95593adb 100644 --- a/src/pulse/internal.h +++ b/src/pulse/internal.h @@ -41,13 +41,14 @@ #include #include #include +#include #include "client-conf.h" #define DEFAULT_TIMEOUT (30) struct pa_context { - int ref; + PA_REFCNT_DECLARE; char *name; pa_mainloop_api* mainloop; @@ -96,7 +97,7 @@ typedef struct pa_index_correction { } pa_index_correction; struct pa_stream { - int ref; + PA_REFCNT_DECLARE; pa_context *context; pa_mainloop_api *mainloop; PA_LLIST_FIELDS(pa_stream); @@ -116,6 +117,7 @@ struct pa_stream { uint32_t requested_bytes; pa_memchunk peek_memchunk; + void *peek_data; pa_memblockq *record_memblockq; int corked; @@ -160,7 +162,8 @@ struct pa_stream { typedef void (*pa_operation_cb_t)(void); struct pa_operation { - int ref; + PA_REFCNT_DECLARE; + pa_context *context; pa_stream *stream; diff --git a/src/pulse/introspect.c b/src/pulse/introspect.c index 7f6406cf..6610a724 100644 --- a/src/pulse/introspect.c +++ b/src/pulse/introspect.c @@ -26,11 +26,10 @@ #include #endif -#include - #include #include +#include #include #include "internal.h" @@ -43,9 +42,11 @@ static void context_stat_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNU pa_operation *o = userdata; pa_stat_info i, *p = &i; - assert(pd); - assert(o); - assert(o->ref >= 1); + pa_assert(pd); + pa_assert(o); + pa_assert(PA_REFCNT_VALUE(o) >= 1); + + memset(&i, 0, sizeof(i)); if (!o->context) goto finish; @@ -59,8 +60,7 @@ static void context_stat_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNU pa_tagstruct_getu32(t, &i.memblock_total_size) < 0 || pa_tagstruct_getu32(t, &i.memblock_allocated) < 0 || pa_tagstruct_getu32(t, &i.memblock_allocated_size) < 0 || - pa_tagstruct_getu32(t, &i.scache_size) < 0 || - !pa_tagstruct_eof(t)) { + pa_tagstruct_getu32(t, &i.scache_size) < 0) { pa_context_fail(o->context, PA_ERR_PROTOCOL); goto finish; } @@ -85,9 +85,11 @@ static void context_get_server_info_callback(pa_pdispatch *pd, uint32_t command, pa_operation *o = userdata; pa_server_info i, *p = &i; - assert(pd); - assert(o); - assert(o->ref >= 1); + pa_assert(pd); + pa_assert(o); + pa_assert(PA_REFCNT_VALUE(o) >= 1); + + memset(&i, 0, sizeof(i)); if (!o->context) goto finish; @@ -104,8 +106,7 @@ static void context_get_server_info_callback(pa_pdispatch *pd, uint32_t command, pa_tagstruct_get_sample_spec(t, &i.sample_spec) < 0 || pa_tagstruct_gets(t, &i.default_sink_name) < 0 || pa_tagstruct_gets(t, &i.default_source_name) < 0 || - pa_tagstruct_getu32(t, &i.cookie) < 0 || - !pa_tagstruct_eof(t)) { + pa_tagstruct_getu32(t, &i.cookie) < 0) { pa_context_fail(o->context, PA_ERR_PROTOCOL); goto finish; @@ -131,9 +132,9 @@ static void context_get_sink_info_callback(pa_pdispatch *pd, uint32_t command, P pa_operation *o = userdata; int eol = 1; - assert(pd); - assert(o); - assert(o->ref >= 1); + pa_assert(pd); + pa_assert(o); + pa_assert(PA_REFCNT_VALUE(o) >= 1); if (!o->context) goto finish; @@ -148,6 +149,7 @@ static void context_get_sink_info_callback(pa_pdispatch *pd, uint32_t command, P while (!pa_tagstruct_eof(t)) { pa_sink_info i; + memset(&i, 0, sizeof(i)); if (pa_tagstruct_getu32(t, &i.index) < 0 || pa_tagstruct_gets(t, &i.name) < 0 || @@ -195,9 +197,9 @@ pa_operation* pa_context_get_sink_info_by_index(pa_context *c, uint32_t idx, pa_ pa_operation *o; uint32_t tag; - assert(c); - assert(c->ref >= 1); - assert(cb); + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); + pa_assert(cb); PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); @@ -217,9 +219,9 @@ pa_operation* pa_context_get_sink_info_by_name(pa_context *c, const char *name, pa_operation *o; uint32_t tag; - assert(c); - assert(c->ref >= 1); - assert(cb); + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); + pa_assert(cb); PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(c, !name || *name, PA_ERR_INVALID); @@ -241,9 +243,9 @@ static void context_get_source_info_callback(pa_pdispatch *pd, uint32_t command, pa_operation *o = userdata; int eol = 1; - assert(pd); - assert(o); - assert(o->ref >= 1); + pa_assert(pd); + pa_assert(o); + pa_assert(PA_REFCNT_VALUE(o) >= 1); if (!o->context) goto finish; @@ -258,6 +260,7 @@ static void context_get_source_info_callback(pa_pdispatch *pd, uint32_t command, while (!pa_tagstruct_eof(t)) { pa_source_info i; uint32_t flags; + memset(&i, 0, sizeof(i)); if (pa_tagstruct_getu32(t, &i.index) < 0 || pa_tagstruct_gets(t, &i.name) < 0 || @@ -305,9 +308,9 @@ pa_operation* pa_context_get_source_info_by_index(pa_context *c, uint32_t idx, p pa_operation *o; uint32_t tag; - assert(c); - assert(c->ref >= 1); - assert(cb); + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); + pa_assert(cb); PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); @@ -327,9 +330,9 @@ pa_operation* pa_context_get_source_info_by_name(pa_context *c, const char *name pa_operation *o; uint32_t tag; - assert(c); - assert(c->ref >= 1); - assert(cb); + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); + pa_assert(cb); PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(c, !name || *name, PA_ERR_INVALID); @@ -351,9 +354,9 @@ static void context_get_client_info_callback(pa_pdispatch *pd, uint32_t command, pa_operation *o = userdata; int eol = 1; - assert(pd); - assert(o); - assert(o->ref >= 1); + pa_assert(pd); + pa_assert(o); + pa_assert(PA_REFCNT_VALUE(o) >= 1); if (!o->context) goto finish; @@ -367,6 +370,7 @@ static void context_get_client_info_callback(pa_pdispatch *pd, uint32_t command, while (!pa_tagstruct_eof(t)) { pa_client_info i; + memset(&i, 0, sizeof(i)); if (pa_tagstruct_getu32(t, &i.index) < 0 || pa_tagstruct_gets(t, &i.name) < 0 || @@ -398,9 +402,9 @@ pa_operation* pa_context_get_client_info(pa_context *c, uint32_t idx, pa_client_ pa_operation *o; uint32_t tag; - assert(c); - assert(c->ref >= 1); - assert(cb); + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); + pa_assert(cb); PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID); @@ -425,9 +429,9 @@ static void context_get_module_info_callback(pa_pdispatch *pd, uint32_t command, pa_operation *o = userdata; int eol = 1; - assert(pd); - assert(o); - assert(o->ref >= 1); + pa_assert(pd); + pa_assert(o); + pa_assert(PA_REFCNT_VALUE(o) >= 1); if (!o->context) goto finish; @@ -441,6 +445,7 @@ static void context_get_module_info_callback(pa_pdispatch *pd, uint32_t command, while (!pa_tagstruct_eof(t)) { pa_module_info i; + memset(&i, 0, sizeof(i)); if (pa_tagstruct_getu32(t, &i.index) < 0 || pa_tagstruct_gets(t, &i.name) < 0 || @@ -473,9 +478,9 @@ pa_operation* pa_context_get_module_info(pa_context *c, uint32_t idx, pa_module_ pa_operation *o; uint32_t tag; - assert(c); - assert(c->ref >= 1); - assert(cb); + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); + pa_assert(cb); PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID); @@ -500,9 +505,9 @@ static void context_get_sink_input_info_callback(pa_pdispatch *pd, uint32_t comm pa_operation *o = userdata; int eol = 1; - assert(pd); - assert(o); - assert(o->ref >= 1); + pa_assert(pd); + pa_assert(o); + pa_assert(PA_REFCNT_VALUE(o) >= 1); if (!o->context) goto finish; @@ -516,6 +521,7 @@ static void context_get_sink_input_info_callback(pa_pdispatch *pd, uint32_t comm while (!pa_tagstruct_eof(t)) { pa_sink_input_info i; + memset(&i, 0, sizeof(i)); if (pa_tagstruct_getu32(t, &i.index) < 0 || pa_tagstruct_gets(t, &i.name) < 0 || @@ -528,7 +534,8 @@ static void context_get_sink_input_info_callback(pa_pdispatch *pd, uint32_t comm pa_tagstruct_get_usec(t, &i.buffer_usec) < 0 || pa_tagstruct_get_usec(t, &i.sink_usec) < 0 || pa_tagstruct_gets(t, &i.resample_method) < 0 || - pa_tagstruct_gets(t, &i.driver) < 0) { + pa_tagstruct_gets(t, &i.driver) < 0 || + (o->context->version >= 11 && pa_tagstruct_get_boolean(t, &i.mute) < 0)) { pa_context_fail(o->context, PA_ERR_PROTOCOL); goto finish; @@ -556,9 +563,9 @@ pa_operation* pa_context_get_sink_input_info(pa_context *c, uint32_t idx, pa_sin pa_operation *o; uint32_t tag; - assert(c); - assert(c->ref >= 1); - assert(cb); + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); + pa_assert(cb); PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID); @@ -583,9 +590,9 @@ static void context_get_source_output_info_callback(pa_pdispatch *pd, uint32_t c pa_operation *o = userdata; int eol = 1; - assert(pd); - assert(o); - assert(o->ref >= 1); + pa_assert(pd); + pa_assert(o); + pa_assert(PA_REFCNT_VALUE(o) >= 1); if (!o->context) goto finish; @@ -600,6 +607,8 @@ static void context_get_source_output_info_callback(pa_pdispatch *pd, uint32_t c while (!pa_tagstruct_eof(t)) { pa_source_output_info i; + memset(&i, 0, sizeof(i)); + if (pa_tagstruct_getu32(t, &i.index) < 0 || pa_tagstruct_gets(t, &i.name) < 0 || pa_tagstruct_getu32(t, &i.owner_module) < 0 || @@ -638,9 +647,9 @@ pa_operation* pa_context_get_source_output_info(pa_context *c, uint32_t idx, pa_ pa_operation *o; uint32_t tag; - assert(c); - assert(c->ref >= 1); - assert(cb); + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); + pa_assert(cb); PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID); @@ -666,9 +675,9 @@ pa_operation* pa_context_set_sink_volume_by_index(pa_context *c, uint32_t idx, c pa_tagstruct *t; uint32_t tag; - assert(c); - assert(c->ref >= 1); - assert(volume); + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); + pa_assert(volume); PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(c, pa_cvolume_valid(volume), PA_ERR_INVALID); @@ -690,10 +699,10 @@ pa_operation* pa_context_set_sink_volume_by_name(pa_context *c, const char *name pa_tagstruct *t; uint32_t tag; - assert(c); - assert(c->ref >= 1); - assert(name); - assert(volume); + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); + pa_assert(name); + pa_assert(volume); PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(c, pa_cvolume_valid(volume), PA_ERR_INVALID); @@ -716,8 +725,8 @@ pa_operation* pa_context_set_sink_mute_by_index(pa_context *c, uint32_t idx, int pa_tagstruct *t; uint32_t tag; - assert(c); - assert(c->ref >= 1); + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); @@ -738,9 +747,9 @@ pa_operation* pa_context_set_sink_mute_by_name(pa_context *c, const char *name, pa_tagstruct *t; uint32_t tag; - assert(c); - assert(c->ref >= 1); - assert(name); + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); + pa_assert(name); PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(c, !name || *name, PA_ERR_INVALID); @@ -762,9 +771,9 @@ pa_operation* pa_context_set_sink_input_volume(pa_context *c, uint32_t idx, cons pa_tagstruct *t; uint32_t tag; - assert(c); - assert(c->ref >= 1); - assert(volume); + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); + pa_assert(volume); PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID); @@ -781,14 +790,37 @@ pa_operation* pa_context_set_sink_input_volume(pa_context *c, uint32_t idx, cons return o; } +pa_operation* pa_context_set_sink_input_mute(pa_context *c, uint32_t idx, int mute, pa_context_success_cb_t cb, void *userdata) { + pa_operation *o; + pa_tagstruct *t; + uint32_t tag; + + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); + + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID); + PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 11, PA_ERR_NOTSUPPORTED); + + o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); + + t = pa_tagstruct_command(c, PA_COMMAND_SET_SINK_INPUT_MUTE, &tag); + pa_tagstruct_putu32(t, idx); + pa_tagstruct_put_boolean(t, mute); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); + + return o; +} + pa_operation* pa_context_set_source_volume_by_index(pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata) { pa_operation *o; pa_tagstruct *t; uint32_t tag; - assert(c); - assert(c->ref >= 1); - assert(volume); + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); + pa_assert(volume); PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(c, pa_cvolume_valid(volume), PA_ERR_INVALID); @@ -810,10 +842,10 @@ pa_operation* pa_context_set_source_volume_by_name(pa_context *c, const char *na pa_tagstruct *t; uint32_t tag; - assert(c); - assert(c->ref >= 1); - assert(name); - assert(volume); + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); + pa_assert(name); + pa_assert(volume); PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(c, pa_cvolume_valid(volume), PA_ERR_INVALID); @@ -836,8 +868,8 @@ pa_operation* pa_context_set_source_mute_by_index(pa_context *c, uint32_t idx, i pa_tagstruct *t; uint32_t tag; - assert(c); - assert(c->ref >= 1); + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); @@ -858,9 +890,9 @@ pa_operation* pa_context_set_source_mute_by_name(pa_context *c, const char *name pa_tagstruct *t; uint32_t tag; - assert(c); - assert(c->ref >= 1); - assert(name); + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); + pa_assert(name); PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(c, !name || *name, PA_ERR_INVALID); @@ -883,9 +915,9 @@ static void context_get_sample_info_callback(pa_pdispatch *pd, uint32_t command, pa_operation *o = userdata; int eol = 1; - assert(pd); - assert(o); - assert(o->ref >= 1); + pa_assert(pd); + pa_assert(o); + pa_assert(PA_REFCNT_VALUE(o) >= 1); if (!o->context) goto finish; @@ -900,6 +932,8 @@ static void context_get_sample_info_callback(pa_pdispatch *pd, uint32_t command, while (!pa_tagstruct_eof(t)) { pa_sample_info i; + memset(&i, 0, sizeof(i)); + if (pa_tagstruct_getu32(t, &i.index) < 0 || pa_tagstruct_gets(t, &i.name) < 0 || pa_tagstruct_get_cvolume(t, &i.volume) < 0 || @@ -936,9 +970,9 @@ pa_operation* pa_context_get_sample_info_by_name(pa_context *c, const char *name pa_operation *o; uint32_t tag; - assert(c); - assert(c->ref >= 1); - assert(cb); + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); + pa_assert(cb); PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(c, name && *name, PA_ERR_INVALID); @@ -959,9 +993,9 @@ pa_operation* pa_context_get_sample_info_by_index(pa_context *c, uint32_t idx, p pa_operation *o; uint32_t tag; - assert(c); - assert(c->ref >= 1); - assert(cb); + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); + pa_assert(cb); PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID); @@ -986,8 +1020,8 @@ static pa_operation* command_kill(pa_context *c, uint32_t command, uint32_t idx, pa_tagstruct *t; uint32_t tag; - assert(c); - assert(c->ref >= 1); + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID); @@ -1018,9 +1052,9 @@ static void context_index_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UN pa_operation *o = userdata; uint32_t idx; - assert(pd); - assert(o); - assert(o->ref >= 1); + pa_assert(pd); + pa_assert(o); + pa_assert(PA_REFCNT_VALUE(o) >= 1); if (!o->context) goto finish; @@ -1052,8 +1086,8 @@ pa_operation* pa_context_load_module(pa_context *c, const char*name, const char pa_tagstruct *t; uint32_t tag; - assert(c); - assert(c->ref >= 1); + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(c, name && *name, PA_ERR_INVALID); @@ -1079,9 +1113,9 @@ static void context_get_autoload_info_callback(pa_pdispatch *pd, uint32_t comman pa_operation *o = userdata; int eol = 1; - assert(pd); - assert(o); - assert(o->ref >= 1); + pa_assert(pd); + pa_assert(o); + pa_assert(PA_REFCNT_VALUE(o) >= 1); if (!o->context) goto finish; @@ -1096,6 +1130,8 @@ static void context_get_autoload_info_callback(pa_pdispatch *pd, uint32_t comman while (!pa_tagstruct_eof(t)) { pa_autoload_info i; + memset(&i, 0, sizeof(i)); + if (pa_tagstruct_getu32(t, &i.index) < 0 || pa_tagstruct_gets(t, &i.name) < 0 || pa_tagstruct_getu32(t, &i.type) < 0 || @@ -1127,9 +1163,9 @@ pa_operation* pa_context_get_autoload_info_by_name(pa_context *c, const char *na pa_operation *o; uint32_t tag; - assert(c); - assert(c->ref >= 1); - assert(cb); + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); + pa_assert(cb); PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(c, name && *name, PA_ERR_INVALID); @@ -1151,9 +1187,9 @@ pa_operation* pa_context_get_autoload_info_by_index(pa_context *c, uint32_t idx, pa_operation *o; uint32_t tag; - assert(c); - assert(c->ref >= 1); - assert(cb); + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); + pa_assert(cb); PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID); @@ -1177,8 +1213,8 @@ pa_operation* pa_context_add_autoload(pa_context *c, const char *name, pa_autolo pa_tagstruct *t; uint32_t tag; - assert(c); - assert(c->ref >= 1); + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(c, name && *name, PA_ERR_INVALID); @@ -1203,8 +1239,8 @@ pa_operation* pa_context_remove_autoload_by_name(pa_context *c, const char *name pa_tagstruct *t; uint32_t tag; - assert(c); - assert(c->ref >= 1); + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(c, name && *name, PA_ERR_INVALID); @@ -1226,8 +1262,8 @@ pa_operation* pa_context_remove_autoload_by_index(pa_context *c, uint32_t idx, p pa_tagstruct *t; uint32_t tag; - assert(c); - assert(c->ref >= 1); + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID); @@ -1247,8 +1283,8 @@ pa_operation* pa_context_move_sink_input_by_name(pa_context *c, uint32_t idx, ch pa_tagstruct *t; uint32_t tag; - assert(c); - assert(c->ref >= 1); + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 10, PA_ERR_NOTSUPPORTED); @@ -1272,8 +1308,8 @@ pa_operation* pa_context_move_sink_input_by_index(pa_context *c, uint32_t idx, u pa_tagstruct *t; uint32_t tag; - assert(c); - assert(c->ref >= 1); + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 10, PA_ERR_NOTSUPPORTED); @@ -1297,8 +1333,8 @@ pa_operation* pa_context_move_source_output_by_name(pa_context *c, uint32_t idx, pa_tagstruct *t; uint32_t tag; - assert(c); - assert(c->ref >= 1); + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 10, PA_ERR_NOTSUPPORTED); @@ -1322,8 +1358,8 @@ pa_operation* pa_context_move_source_output_by_index(pa_context *c, uint32_t idx pa_tagstruct *t; uint32_t tag; - assert(c); - assert(c->ref >= 1); + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 10, PA_ERR_NOTSUPPORTED); @@ -1341,3 +1377,97 @@ pa_operation* pa_context_move_source_output_by_index(pa_context *c, uint32_t idx return o; } + +pa_operation* pa_context_suspend_sink_by_name(pa_context *c, char *sink_name, int suspend, pa_context_success_cb_t cb, void* userdata) { + pa_operation *o; + pa_tagstruct *t; + uint32_t tag; + + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); + + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 11, PA_ERR_NOTSUPPORTED); + PA_CHECK_VALIDITY_RETURN_NULL(c, !sink_name || *sink_name, PA_ERR_INVALID); + + o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); + + t = pa_tagstruct_command(c, PA_COMMAND_SUSPEND_SINK, &tag); + pa_tagstruct_putu32(t, PA_INVALID_INDEX); + pa_tagstruct_puts(t, sink_name); + pa_tagstruct_put_boolean(t, suspend); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); + + return o; +} + +pa_operation* pa_context_suspend_sink_by_index(pa_context *c, uint32_t idx, int suspend, pa_context_success_cb_t cb, void* userdata) { + pa_operation *o; + pa_tagstruct *t; + uint32_t tag; + + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); + + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 11, PA_ERR_NOTSUPPORTED); + + o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); + + t = pa_tagstruct_command(c, PA_COMMAND_SUSPEND_SINK, &tag); + pa_tagstruct_putu32(t, idx); + pa_tagstruct_puts(t, idx == PA_INVALID_INDEX ? "" : NULL); + pa_tagstruct_put_boolean(t, suspend); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); + + return o; +} + +pa_operation* pa_context_suspend_source_by_name(pa_context *c, char *source_name, int suspend, pa_context_success_cb_t cb, void* userdata) { + pa_operation *o; + pa_tagstruct *t; + uint32_t tag; + + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); + + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 11, PA_ERR_NOTSUPPORTED); + PA_CHECK_VALIDITY_RETURN_NULL(c, !source_name || *source_name, PA_ERR_INVALID); + + o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); + + t = pa_tagstruct_command(c, PA_COMMAND_SUSPEND_SOURCE, &tag); + pa_tagstruct_putu32(t, PA_INVALID_INDEX); + pa_tagstruct_puts(t, source_name); + pa_tagstruct_put_boolean(t, suspend); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); + + return o; +} + +pa_operation* pa_context_suspend_source_by_index(pa_context *c, uint32_t idx, int suspend, pa_context_success_cb_t cb, void* userdata) { + pa_operation *o; + pa_tagstruct *t; + uint32_t tag; + + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); + + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 11, PA_ERR_NOTSUPPORTED); + + o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); + + t = pa_tagstruct_command(c, PA_COMMAND_SUSPEND_SOURCE, &tag); + pa_tagstruct_putu32(t, idx); + pa_tagstruct_puts(t, idx == PA_INVALID_INDEX ? "" : NULL); + pa_tagstruct_put_boolean(t, suspend); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); + + return o; +} diff --git a/src/pulse/introspect.h b/src/pulse/introspect.h index 43e430b2..c148ee5e 100644 --- a/src/pulse/introspect.h +++ b/src/pulse/introspect.h @@ -331,6 +331,7 @@ typedef struct pa_sink_input_info { pa_usec_t sink_usec; /**< Latency of the sink device, see pa_latency_info for details */ const char *resample_method; /**< Thre resampling method used by this sink input. \since 0.7 */ const char *driver; /**< Driver name \since 0.8 */ + int mute; /**< Stream muted \since 0.9.7 */ } pa_sink_input_info; /** Callback prototype for pa_context_get_sink_input_info() and firends*/ @@ -381,6 +382,9 @@ pa_operation* pa_context_set_sink_mute_by_name(pa_context *c, const char *name, /** Set the volume of a sink input stream */ pa_operation* pa_context_set_sink_input_volume(pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata); +/** Set the mute switch of a sink input stream \since 0.9.7 */ +pa_operation* pa_context_set_sink_input_mute(pa_context *c, uint32_t idx, int mute, pa_context_success_cb_t cb, void *userdata); + /** Set the volume of a source device specified by its index \since 0.8 */ pa_operation* pa_context_set_source_volume_by_index(pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata); @@ -499,6 +503,18 @@ pa_operation* pa_context_move_source_output_by_name(pa_context *c, uint32_t idx, /** Move the specified source output to a different source. \since 0.9.5 */ pa_operation* pa_context_move_source_output_by_index(pa_context *c, uint32_t idx, uint32_t source_idx, pa_context_success_cb_t cb, void* userdata); +/** Suspend/Resume a sink. \since 0.9.7 */ +pa_operation* pa_context_suspend_sink_by_name(pa_context *c, char *sink_name, int suspend, pa_context_success_cb_t cb, void* userdata); + +/** Suspend/Resume a sink. If idx is PA_INVALID_INDEX all sinks will be suspended. \since 0.9.7 */ +pa_operation* pa_context_suspend_sink_by_index(pa_context *c, uint32_t idx, int suspend, pa_context_success_cb_t cb, void* userdata); + +/** Suspend/Resume a source. \since 0.9.7 */ +pa_operation* pa_context_suspend_source_by_name(pa_context *c, char *source_name, int suspend, pa_context_success_cb_t cb, void* userdata); + +/** Suspend/Resume a source. If idx is PA_INVALID_INDEX all sources will be suspended. \since 0.9.7 */ +pa_operation* pa_context_suspend_source_by_index(pa_context *c, uint32_t idx, int suspend, pa_context_success_cb_t cb, void* userdata); + PA_C_DECL_END #endif diff --git a/src/pulse/mainloop-api.c b/src/pulse/mainloop-api.c index 001ff314..b2ed3434 100644 --- a/src/pulse/mainloop-api.c +++ b/src/pulse/mainloop-api.c @@ -25,12 +25,12 @@ #include #endif -#include #include #include #include +#include #include "mainloop-api.h" @@ -41,32 +41,38 @@ struct once_info { static void once_callback(pa_mainloop_api *m, pa_defer_event *e, void *userdata) { struct once_info *i = userdata; - assert(m && i && i->callback); + pa_assert(m); + pa_assert(i); + + pa_assert(i->callback); i->callback(m, i->userdata); - assert(m->defer_free); + pa_assert(m->defer_free); m->defer_free(e); } static void free_callback(pa_mainloop_api *m, PA_GCC_UNUSED pa_defer_event *e, void *userdata) { struct once_info *i = userdata; - assert(m && i); + + pa_assert(m); + pa_assert(i); pa_xfree(i); } void pa_mainloop_api_once(pa_mainloop_api* m, void (*callback)(pa_mainloop_api *m, void *userdata), void *userdata) { struct once_info *i; pa_defer_event *e; - assert(m && callback); + + pa_assert(m); + pa_assert(callback); i = pa_xnew(struct once_info, 1); i->callback = callback; i->userdata = userdata; - assert(m->defer_new); - e = m->defer_new(m, once_callback, i); - assert(e); + pa_assert(m->defer_new); + pa_assert_se(e = m->defer_new(m, once_callback, i)); m->defer_set_destroy(e, free_callback); } diff --git a/src/pulse/mainloop-signal.c b/src/pulse/mainloop-signal.c index 28ddec49..7d3017e2 100644 --- a/src/pulse/mainloop-signal.c +++ b/src/pulse/mainloop-signal.c @@ -27,7 +27,6 @@ #endif #include -#include #include #include #include @@ -39,12 +38,13 @@ #include #endif -#include #include +#include #include #include #include +#include #include "mainloop-signal.h" @@ -74,11 +74,11 @@ static void signal_handler(int sig) { } static void dispatch(pa_mainloop_api*a, int sig) { - pa_signal_event*s; + pa_signal_event *s; for (s = signals; s; s = s->next) if (s->sig == sig) { - assert(s->callback); + pa_assert(s->callback); s->callback(a, s, sig, s->userdata); break; } @@ -87,7 +87,12 @@ static void dispatch(pa_mainloop_api*a, int sig) { static void callback(pa_mainloop_api*a, pa_io_event*e, int fd, pa_io_event_flags_t f, PA_GCC_UNUSED void *userdata) { ssize_t r; int sig; - assert(a && e && f == PA_IO_EVENT_INPUT && e == io_event && fd == signal_pipe[0]); + + pa_assert(a); + pa_assert(e); + pa_assert(f == PA_IO_EVENT_INPUT); + pa_assert(e == io_event); + pa_assert(fd == signal_pipe[0]); if ((r = pa_read(signal_pipe[0], &sig, sizeof(sig), NULL)) < 0) { if (errno == EAGAIN) @@ -107,28 +112,34 @@ static void callback(pa_mainloop_api*a, pa_io_event*e, int fd, pa_io_event_flags int pa_signal_init(pa_mainloop_api *a) { - assert(!api && a && signal_pipe[0] == -1 && signal_pipe[1] == -1 && !io_event); + pa_assert(a); + pa_assert(!api); + pa_assert(signal_pipe[0] == -1); + pa_assert(signal_pipe[1] == -1); + pa_assert(!io_event); if (pipe(signal_pipe) < 0) { pa_log("pipe(): %s", pa_cstrerror(errno)); return -1; } - pa_make_nonblock_fd(signal_pipe[0]); - pa_make_nonblock_fd(signal_pipe[1]); - pa_fd_set_cloexec(signal_pipe[0], 1); - pa_fd_set_cloexec(signal_pipe[1], 1); + pa_make_fd_nonblock(signal_pipe[0]); + pa_make_fd_nonblock(signal_pipe[1]); + pa_make_fd_cloexec(signal_pipe[0]); + pa_make_fd_cloexec(signal_pipe[1]); api = a; - io_event = api->io_new(api, signal_pipe[0], PA_IO_EVENT_INPUT, callback, NULL); - assert(io_event); + pa_assert_se(io_event = api->io_new(api, signal_pipe[0], PA_IO_EVENT_INPUT, callback, NULL)); return 0; } void pa_signal_done(void) { - assert(api && signal_pipe[0] >= 0 && signal_pipe[1] >= 0 && io_event); + pa_assert(api); + pa_assert(signal_pipe[0] >= 0); + pa_assert(signal_pipe[1] >= 0); + pa_assert(io_event); while (signals) pa_signal_free(signals); @@ -136,9 +147,7 @@ void pa_signal_done(void) { api->io_free(io_event); io_event = NULL; - close(signal_pipe[0]); - close(signal_pipe[1]); - signal_pipe[0] = signal_pipe[1] = -1; + pa_close_pipe(signal_pipe); api = NULL; } @@ -150,13 +159,14 @@ pa_signal_event* pa_signal_new(int sig, void (*_callback) (pa_mainloop_api *api, struct sigaction sa; #endif - assert(sig > 0 && _callback); + pa_assert(sig > 0); + pa_assert(_callback); for (e = signals; e; e = e->next) if (e->sig == sig) goto fail; - e = pa_xmalloc(sizeof(pa_signal_event)); + e = pa_xnew(pa_signal_event, 1); e->sig = sig; e->callback = _callback; e->userdata = userdata; @@ -186,7 +196,7 @@ fail: } void pa_signal_free(pa_signal_event *e) { - assert(e); + pa_assert(e); if (e->next) e->next->previous = e->previous; @@ -196,9 +206,9 @@ void pa_signal_free(pa_signal_event *e) { signals = e->next; #ifdef HAVE_SIGACTION - sigaction(e->sig, &e->saved_sigaction, NULL); + pa_assert_se(sigaction(e->sig, &e->saved_sigaction, NULL) == 0); #else - signal(e->sig, e->saved_handler); + pa_assert_se(signal(e->sig, e->saved_handler) == signal_handler); #endif if (e->destroy_callback) @@ -208,6 +218,7 @@ void pa_signal_free(pa_signal_event *e) { } void pa_signal_set_destroy(pa_signal_event *e, void (*_callback) (pa_mainloop_api *api, pa_signal_event*e, void *userdata)) { - assert(e); + pa_assert(e); + e->destroy_callback = _callback; } diff --git a/src/pulse/mainloop.c b/src/pulse/mainloop.c index 43cbb19f..ad4e4e97 100644 --- a/src/pulse/mainloop.c +++ b/src/pulse/mainloop.c @@ -31,29 +31,28 @@ #include #include #include -#include #include #include -#ifdef HAVE_SYS_POLL_H -#include +#ifdef HAVE_POLL_H +#include #else -#include "../pulsecore/poll.h" +#include #endif -#include "../pulsecore/winsock.h" - #ifndef HAVE_PIPE -#include "../pulsecore/pipe.h" +#include #endif -#include #include #include #include #include #include +#include +#include +#include #include "mainloop.h" @@ -161,13 +160,13 @@ static pa_io_event* mainloop_io_new( pa_mainloop *m; pa_io_event *e; - assert(a); - assert(a->userdata); - assert(fd >= 0); - assert(callback); + pa_assert(a); + pa_assert(a->userdata); + pa_assert(fd >= 0); + pa_assert(callback); m = a->userdata; - assert(a == &m->api); + pa_assert(a == &m->api); e = pa_xnew(pa_io_event, 1); e->mainloop = m; @@ -195,7 +194,7 @@ static pa_io_event* mainloop_io_new( if ((select((SELECT_TYPE_ARG1) fd, NULL, NULL, SELECT_TYPE_ARG234 &xset, SELECT_TYPE_ARG5 &tv) == -1) && (WSAGetLastError() == WSAENOTSOCK)) { - pa_log_warn("WARNING: cannot monitor non-socket file descriptors."); + pa_log_warn("Cannot monitor non-socket file descriptors."); e->dead = 1; } } @@ -211,8 +210,8 @@ static pa_io_event* mainloop_io_new( } static void mainloop_io_enable(pa_io_event *e, pa_io_event_flags_t events) { - assert(e); - assert(!e->dead); + pa_assert(e); + pa_assert(!e->dead); if (e->events == events) return; @@ -228,8 +227,8 @@ static void mainloop_io_enable(pa_io_event *e, pa_io_event_flags_t events) { } static void mainloop_io_free(pa_io_event *e) { - assert(e); - assert(!e->dead); + pa_assert(e); + pa_assert(!e->dead); e->dead = 1; e->mainloop->io_events_please_scan ++; @@ -241,7 +240,7 @@ static void mainloop_io_free(pa_io_event *e) { } static void mainloop_io_set_destroy(pa_io_event *e, pa_io_event_destroy_cb_t callback) { - assert(e); + pa_assert(e); e->destroy_callback = callback; } @@ -255,12 +254,12 @@ static pa_defer_event* mainloop_defer_new( pa_mainloop *m; pa_defer_event *e; - assert(a); - assert(a->userdata); - assert(callback); + pa_assert(a); + pa_assert(a->userdata); + pa_assert(callback); m = a->userdata; - assert(a == &m->api); + pa_assert(a == &m->api); e = pa_xnew(pa_defer_event, 1); e->mainloop = m; @@ -281,11 +280,11 @@ static pa_defer_event* mainloop_defer_new( } static void mainloop_defer_enable(pa_defer_event *e, int b) { - assert(e); - assert(!e->dead); + pa_assert(e); + pa_assert(!e->dead); if (e->enabled && !b) { - assert(e->mainloop->n_enabled_defer_events > 0); + pa_assert(e->mainloop->n_enabled_defer_events > 0); e->mainloop->n_enabled_defer_events--; } else if (!e->enabled && b) { e->mainloop->n_enabled_defer_events++; @@ -296,21 +295,22 @@ static void mainloop_defer_enable(pa_defer_event *e, int b) { } static void mainloop_defer_free(pa_defer_event *e) { - assert(e); - assert(!e->dead); + pa_assert(e); + pa_assert(!e->dead); e->dead = 1; e->mainloop->defer_events_please_scan ++; if (e->enabled) { - assert(e->mainloop->n_enabled_defer_events > 0); + pa_assert(e->mainloop->n_enabled_defer_events > 0); e->mainloop->n_enabled_defer_events--; + e->enabled = 0; } } static void mainloop_defer_set_destroy(pa_defer_event *e, pa_defer_event_destroy_cb_t callback) { - assert(e); - assert(!e->dead); + pa_assert(e); + pa_assert(!e->dead); e->destroy_callback = callback; } @@ -325,12 +325,12 @@ static pa_time_event* mainloop_time_new( pa_mainloop *m; pa_time_event *e; - assert(a); - assert(a->userdata); - assert(callback); + pa_assert(a); + pa_assert(a->userdata); + pa_assert(callback); m = a->userdata; - assert(a == &m->api); + pa_assert(a == &m->api); e = pa_xnew(pa_time_event, 1); e->mainloop = m; @@ -342,7 +342,7 @@ static pa_time_event* mainloop_time_new( m->n_enabled_time_events++; if (m->cached_next_time_event) { - assert(m->cached_next_time_event->enabled); + pa_assert(m->cached_next_time_event->enabled); if (pa_timeval_cmp(tv, &m->cached_next_time_event->timeval) < 0) m->cached_next_time_event = e; @@ -362,11 +362,11 @@ static pa_time_event* mainloop_time_new( } static void mainloop_time_restart(pa_time_event *e, const struct timeval *tv) { - assert(e); - assert(!e->dead); + pa_assert(e); + pa_assert(!e->dead); if (e->enabled && !tv) { - assert(e->mainloop->n_enabled_time_events > 0); + pa_assert(e->mainloop->n_enabled_time_events > 0); e->mainloop->n_enabled_time_events--; } else if (!e->enabled && tv) e->mainloop->n_enabled_time_events++; @@ -377,7 +377,7 @@ static void mainloop_time_restart(pa_time_event *e, const struct timeval *tv) { } if (e->mainloop->cached_next_time_event && e->enabled) { - assert(e->mainloop->cached_next_time_event->enabled); + pa_assert(e->mainloop->cached_next_time_event->enabled); if (pa_timeval_cmp(tv, &e->mainloop->cached_next_time_event->timeval) < 0) e->mainloop->cached_next_time_event = e; @@ -386,15 +386,16 @@ static void mainloop_time_restart(pa_time_event *e, const struct timeval *tv) { } static void mainloop_time_free(pa_time_event *e) { - assert(e); - assert(!e->dead); + pa_assert(e); + pa_assert(!e->dead); e->dead = 1; e->mainloop->time_events_please_scan ++; if (e->enabled) { - assert(e->mainloop->n_enabled_time_events > 0); + pa_assert(e->mainloop->n_enabled_time_events > 0); e->mainloop->n_enabled_time_events--; + e->enabled = 0; } if (e->mainloop->cached_next_time_event == e) @@ -404,8 +405,8 @@ static void mainloop_time_free(pa_time_event *e) { } static void mainloop_time_set_destroy(pa_time_event *e, pa_time_event_destroy_cb_t callback) { - assert(e); - assert(!e->dead); + pa_assert(e); + pa_assert(!e->dead); e->destroy_callback = callback; } @@ -415,10 +416,10 @@ static void mainloop_time_set_destroy(pa_time_event *e, pa_time_event_destroy_cb static void mainloop_quit(pa_mainloop_api*a, int retval) { pa_mainloop *m; - assert(a); - assert(a->userdata); + pa_assert(a); + pa_assert(a->userdata); m = a->userdata; - assert(a == &m->api); + pa_assert(a == &m->api); pa_mainloop_quit(m, retval); } @@ -456,8 +457,10 @@ pa_mainloop *pa_mainloop_new(void) { return NULL; } - pa_make_nonblock_fd(m->wakeup_pipe[0]); - pa_make_nonblock_fd(m->wakeup_pipe[1]); + pa_make_fd_nonblock(m->wakeup_pipe[0]); + pa_make_fd_nonblock(m->wakeup_pipe[1]); + pa_make_fd_cloexec(m->wakeup_pipe[0]); + pa_make_fd_cloexec(m->wakeup_pipe[1]); m->wakeup_requested = 0; PA_LLIST_HEAD_INIT(pa_io_event, m->io_events); @@ -502,7 +505,7 @@ static void cleanup_io_events(pa_mainloop *m, int force) { PA_LLIST_REMOVE(pa_io_event, m->io_events, e); if (e->dead) { - assert(m->io_events_please_scan > 0); + pa_assert(m->io_events_please_scan > 0); m->io_events_please_scan--; } @@ -517,7 +520,7 @@ static void cleanup_io_events(pa_mainloop *m, int force) { e = n; } - assert(m->io_events_please_scan == 0); + pa_assert(m->io_events_please_scan == 0); } static void cleanup_time_events(pa_mainloop *m, int force) { @@ -534,13 +537,14 @@ static void cleanup_time_events(pa_mainloop *m, int force) { PA_LLIST_REMOVE(pa_time_event, m->time_events, e); if (e->dead) { - assert(m->time_events_please_scan > 0); + pa_assert(m->time_events_please_scan > 0); m->time_events_please_scan--; } if (!e->dead && e->enabled) { - assert(m->n_enabled_time_events > 0); + pa_assert(m->n_enabled_time_events > 0); m->n_enabled_time_events--; + e->enabled = 0; } if (e->destroy_callback) @@ -552,7 +556,7 @@ static void cleanup_time_events(pa_mainloop *m, int force) { e = n; } - assert(m->time_events_please_scan == 0); + pa_assert(m->time_events_please_scan == 0); } static void cleanup_defer_events(pa_mainloop *m, int force) { @@ -569,13 +573,14 @@ static void cleanup_defer_events(pa_mainloop *m, int force) { PA_LLIST_REMOVE(pa_defer_event, m->defer_events, e); if (e->dead) { - assert(m->defer_events_please_scan > 0); + pa_assert(m->defer_events_please_scan > 0); m->defer_events_please_scan--; } if (!e->dead && e->enabled) { - assert(m->n_enabled_defer_events > 0); + pa_assert(m->n_enabled_defer_events > 0); m->n_enabled_defer_events--; + e->enabled = 0; } if (e->destroy_callback) @@ -587,12 +592,12 @@ static void cleanup_defer_events(pa_mainloop *m, int force) { e = n; } - assert(m->defer_events_please_scan == 0); + pa_assert(m->defer_events_please_scan == 0); } void pa_mainloop_free(pa_mainloop* m) { - assert(m); + pa_assert(m); cleanup_io_events(m, 1); cleanup_defer_events(m, 1); @@ -600,16 +605,13 @@ void pa_mainloop_free(pa_mainloop* m) { pa_xfree(m->pollfds); - if (m->wakeup_pipe[0] >= 0) - close(m->wakeup_pipe[0]); - if (m->wakeup_pipe[1] >= 0) - close(m->wakeup_pipe[1]); + pa_close_pipe(m->wakeup_pipe); pa_xfree(m); } static void scan_dead(pa_mainloop *m) { - assert(m); + pa_assert(m); if (m->io_events_please_scan) cleanup_io_events(m, 0); @@ -666,13 +668,14 @@ static int dispatch_pollfds(pa_mainloop *m) { pa_io_event *e; int r = 0, k; - assert(m->poll_func_ret > 0); + pa_assert(m->poll_func_ret > 0); for (e = m->io_events, k = m->poll_func_ret; e && !m->quit && k > 0; e = e->next) { if (e->dead || !e->pollfd || !e->pollfd->revents) continue; - assert(e->pollfd->fd == e->fd && e->callback); + pa_assert(e->pollfd->fd == e->fd); + pa_assert(e->callback); e->callback(&m->api, e, e->fd, map_flags_from_libc(e->pollfd->revents), e->userdata); e->pollfd->revents = 0; r++; @@ -694,7 +697,7 @@ static int dispatch_defer(pa_mainloop *m) { if (e->dead || !e->enabled) continue; - assert(e->callback); + pa_assert(e->callback); e->callback(&m->api, e, e->userdata); r++; } @@ -704,7 +707,7 @@ static int dispatch_defer(pa_mainloop *m) { static pa_time_event* find_next_time_event(pa_mainloop *m) { pa_time_event *t, *n = NULL; - assert(m); + pa_assert(m); if (m->cached_next_time_event) return m->cached_next_time_event; @@ -736,7 +739,7 @@ static int calc_next_timeout(pa_mainloop *m) { return -1; t = find_next_time_event(m); - assert(t); + pa_assert(t); if (t->timeval.tv_sec <= 0) return 0; @@ -754,7 +757,7 @@ static int dispatch_timeout(pa_mainloop *m) { pa_time_event *e; struct timeval now; int r = 0; - assert(m); + pa_assert(m); if (m->n_enabled_time_events <= 0) return 0; @@ -767,7 +770,7 @@ static int dispatch_timeout(pa_mainloop *m) { continue; if (pa_timeval_cmp(&e->timeval, &now) <= 0) { - assert(e->callback); + pa_assert(e->callback); /* Disable time event */ mainloop_time_restart(e, NULL); @@ -783,7 +786,7 @@ static int dispatch_timeout(pa_mainloop *m) { void pa_mainloop_wakeup(pa_mainloop *m) { char c = 'W'; - assert(m); + pa_assert(m); if (m->wakeup_pipe[1] >= 0 && m->state == STATE_POLLING) { pa_write(m->wakeup_pipe[1], &c, sizeof(c), &m->wakeup_pipe_type); @@ -794,7 +797,7 @@ void pa_mainloop_wakeup(pa_mainloop *m) { static void clear_wakeup(pa_mainloop *m) { char c[10]; - assert(m); + pa_assert(m); if (m->wakeup_pipe[0] < 0) return; @@ -806,8 +809,8 @@ static void clear_wakeup(pa_mainloop *m) { } int pa_mainloop_prepare(pa_mainloop *m, int timeout) { - assert(m); - assert(m->state == STATE_PASSIVE); + pa_assert(m); + pa_assert(m->state == STATE_PASSIVE); clear_wakeup(m); scan_dead(m); @@ -833,8 +836,8 @@ quit: } int pa_mainloop_poll(pa_mainloop *m) { - assert(m); - assert(m->state == STATE_PREPARED); + pa_assert(m); + pa_assert(m->state == STATE_PREPARED); if (m->quit) goto quit; @@ -844,7 +847,7 @@ int pa_mainloop_poll(pa_mainloop *m) { if (m->n_enabled_defer_events ) m->poll_func_ret = 0; else { - assert(!m->rebuild_pollfds); + pa_assert(!m->rebuild_pollfds); if (m->poll_func) m->poll_func_ret = m->poll_func(m->pollfds, m->n_pollfds, m->prepared_timeout, m->poll_func_userdata); @@ -870,8 +873,8 @@ quit: int pa_mainloop_dispatch(pa_mainloop *m) { int dispatched = 0; - assert(m); - assert(m->state == STATE_POLLED); + pa_assert(m); + pa_assert(m->state == STATE_POLLED); if (m->quit) goto quit; @@ -902,13 +905,13 @@ quit: } int pa_mainloop_get_retval(pa_mainloop *m) { - assert(m); + pa_assert(m); return m->retval; } int pa_mainloop_iterate(pa_mainloop *m, int block, int *retval) { int r; - assert(m); + pa_assert(m); if ((r = pa_mainloop_prepare(m, block ? -1 : 0)) < 0) goto quit; @@ -942,7 +945,7 @@ int pa_mainloop_run(pa_mainloop *m, int *retval) { } void pa_mainloop_quit(pa_mainloop *m, int retval) { - assert(m); + pa_assert(m); m->quit = 1; m->retval = retval; @@ -950,12 +953,12 @@ void pa_mainloop_quit(pa_mainloop *m, int retval) { } pa_mainloop_api* pa_mainloop_get_api(pa_mainloop*m) { - assert(m); + pa_assert(m); return &m->api; } void pa_mainloop_set_poll_func(pa_mainloop *m, pa_poll_func poll_func, void *userdata) { - assert(m); + pa_assert(m); m->poll_func = poll_func; m->poll_func_userdata = userdata; diff --git a/src/pulse/operation.c b/src/pulse/operation.c index f23def50..ed5eb4aa 100644 --- a/src/pulse/operation.c +++ b/src/pulse/operation.c @@ -25,19 +25,18 @@ #include #endif -#include - #include +#include #include "internal.h" #include "operation.h" pa_operation *pa_operation_new(pa_context *c, pa_stream *s, pa_operation_cb_t cb, void *userdata) { pa_operation *o; - assert(c); + pa_assert(c); o = pa_xnew(pa_operation, 1); - o->ref = 1; + PA_REFCNT_INIT(o); o->context = c; o->stream = s; @@ -53,27 +52,27 @@ pa_operation *pa_operation_new(pa_context *c, pa_stream *s, pa_operation_cb_t cb } pa_operation *pa_operation_ref(pa_operation *o) { - assert(o); - assert(o->ref >= 1); + pa_assert(o); + pa_assert(PA_REFCNT_VALUE(o) >= 1); - o->ref++; + PA_REFCNT_INC(o); return o; } void pa_operation_unref(pa_operation *o) { - assert(o); - assert(o->ref >= 1); + pa_assert(o); + pa_assert(PA_REFCNT_VALUE(o) >= 1); - if ((--(o->ref)) == 0) { - assert(!o->context); - assert(!o->stream); + if (PA_REFCNT_DEC(o) <= 0) { + pa_assert(!o->context); + pa_assert(!o->stream); pa_xfree(o); } } static void operation_set_state(pa_operation *o, pa_operation_state_t st) { - assert(o); - assert(o->ref >= 1); + pa_assert(o); + pa_assert(PA_REFCNT_VALUE(o) >= 1); if (st == o->state) return; @@ -85,7 +84,7 @@ static void operation_set_state(pa_operation *o, pa_operation_state_t st) { if ((o->state == PA_OPERATION_DONE) || (o->state == PA_OPERATION_CANCELED)) { if (o->context) { - assert(o->ref >= 2); + pa_assert(PA_REFCNT_VALUE(o) >= 2); PA_LLIST_REMOVE(pa_operation, o->context->operations, o); pa_operation_unref(o); @@ -101,22 +100,22 @@ static void operation_set_state(pa_operation *o, pa_operation_state_t st) { } void pa_operation_cancel(pa_operation *o) { - assert(o); - assert(o->ref >= 1); + pa_assert(o); + pa_assert(PA_REFCNT_VALUE(o) >= 1); operation_set_state(o, PA_OPERATION_CANCELED); } void pa_operation_done(pa_operation *o) { - assert(o); - assert(o->ref >= 1); + pa_assert(o); + pa_assert(PA_REFCNT_VALUE(o) >= 1); operation_set_state(o, PA_OPERATION_DONE); } pa_operation_state_t pa_operation_get_state(pa_operation *o) { - assert(o); - assert(o->ref >= 1); + pa_assert(o); + pa_assert(PA_REFCNT_VALUE(o) >= 1); return o->state; } diff --git a/src/pulse/sample.c b/src/pulse/sample.c index ffdeedf7..ae2a0b9f 100644 --- a/src/pulse/sample.c +++ b/src/pulse/sample.c @@ -27,57 +27,58 @@ #endif #include -#include #include #include +#include +#include + #include "sample.h" size_t pa_sample_size(const pa_sample_spec *spec) { - assert(spec); - - switch (spec->format) { - case PA_SAMPLE_U8: - case PA_SAMPLE_ULAW: - case PA_SAMPLE_ALAW: - return 1; - case PA_SAMPLE_S16LE: - case PA_SAMPLE_S16BE: - return 2; - case PA_SAMPLE_FLOAT32LE: - case PA_SAMPLE_FLOAT32BE: - return 4; - default: - assert(0); - return 0; - } + + static const size_t table[] = { + [PA_SAMPLE_U8] = 1, + [PA_SAMPLE_ULAW] = 1, + [PA_SAMPLE_ALAW] = 1, + [PA_SAMPLE_S16LE] = 2, + [PA_SAMPLE_S16BE] = 2, + [PA_SAMPLE_FLOAT32LE] = 4, + [PA_SAMPLE_FLOAT32BE] = 4 + }; + + pa_assert(spec); + pa_assert(spec->format >= 0); + pa_assert(spec->format < PA_SAMPLE_MAX); + + return table[spec->format]; } size_t pa_frame_size(const pa_sample_spec *spec) { - assert(spec); + pa_assert(spec); return pa_sample_size(spec) * spec->channels; } size_t pa_bytes_per_second(const pa_sample_spec *spec) { - assert(spec); + pa_assert(spec); return spec->rate*pa_frame_size(spec); } pa_usec_t pa_bytes_to_usec(uint64_t length, const pa_sample_spec *spec) { - assert(spec); + pa_assert(spec); return (pa_usec_t) (((double) length/pa_frame_size(spec)*1000000)/spec->rate); } size_t pa_usec_to_bytes(pa_usec_t t, const pa_sample_spec *spec) { - assert(spec); + pa_assert(spec); return (size_t) (((double) t * spec->rate / 1000000))*pa_frame_size(spec); } int pa_sample_spec_valid(const pa_sample_spec *spec) { - assert(spec); + pa_assert(spec); if (spec->rate <= 0 || spec->rate > PA_RATE_MAX || @@ -91,7 +92,8 @@ int pa_sample_spec_valid(const pa_sample_spec *spec) { } int pa_sample_spec_equal(const pa_sample_spec*a, const pa_sample_spec*b) { - assert(a && b); + pa_assert(a); + pa_assert(b); return (a->format == b->format) && (a->rate == b->rate) && (a->channels == b->channels); } @@ -107,37 +109,42 @@ const char *pa_sample_format_to_string(pa_sample_format_t f) { [PA_SAMPLE_FLOAT32BE] = "float32be", }; - if (f >= PA_SAMPLE_MAX) + if (f < 0 || f >= PA_SAMPLE_MAX) return NULL; return table[f]; } char *pa_sample_spec_snprint(char *s, size_t l, const pa_sample_spec *spec) { - assert(s && l && spec); + pa_assert(s); + pa_assert(l); + pa_assert(spec); if (!pa_sample_spec_valid(spec)) - snprintf(s, l, "Invalid"); + pa_snprintf(s, l, "Invalid"); else - snprintf(s, l, "%s %uch %uHz", pa_sample_format_to_string(spec->format), spec->channels, spec->rate); + pa_snprintf(s, l, "%s %uch %uHz", pa_sample_format_to_string(spec->format), spec->channels, spec->rate); return s; } char* pa_bytes_snprint(char *s, size_t l, unsigned v) { + pa_assert(s); + if (v >= ((unsigned) 1024)*1024*1024) - snprintf(s, l, "%0.1f GiB", ((double) v)/1024/1024/1024); + pa_snprintf(s, l, "%0.1f GiB", ((double) v)/1024/1024/1024); else if (v >= ((unsigned) 1024)*1024) - snprintf(s, l, "%0.1f MiB", ((double) v)/1024/1024); + pa_snprintf(s, l, "%0.1f MiB", ((double) v)/1024/1024); else if (v >= (unsigned) 1024) - snprintf(s, l, "%0.1f KiB", ((double) v)/1024); + pa_snprintf(s, l, "%0.1f KiB", ((double) v)/1024); else - snprintf(s, l, "%u B", (unsigned) v); + pa_snprintf(s, l, "%u B", (unsigned) v); return s; } pa_sample_format_t pa_parse_sample_format(const char *format) { + pa_assert(format); if (strcasecmp(format, "s16le") == 0) return PA_SAMPLE_S16LE; @@ -145,15 +152,19 @@ pa_sample_format_t pa_parse_sample_format(const char *format) { return PA_SAMPLE_S16BE; else if (strcasecmp(format, "s16ne") == 0 || strcasecmp(format, "s16") == 0 || strcasecmp(format, "16") == 0) return PA_SAMPLE_S16NE; + else if (strcasecmp(format, "s16re") == 0) + return PA_SAMPLE_S16RE; else if (strcasecmp(format, "u8") == 0 || strcasecmp(format, "8") == 0) return PA_SAMPLE_U8; else if (strcasecmp(format, "float32") == 0 || strcasecmp(format, "float32ne") == 0) - return PA_SAMPLE_FLOAT32; + return PA_SAMPLE_FLOAT32NE; + else if (strcasecmp(format, "float32re") == 0) + return PA_SAMPLE_FLOAT32RE; else if (strcasecmp(format, "float32le") == 0) return PA_SAMPLE_FLOAT32LE; else if (strcasecmp(format, "float32be") == 0) return PA_SAMPLE_FLOAT32BE; - else if (strcasecmp(format, "ulaw") == 0) + else if (strcasecmp(format, "ulaw") == 0 || strcasecmp(format, "mulaw") == 0) return PA_SAMPLE_ULAW; else if (strcasecmp(format, "alaw") == 0) return PA_SAMPLE_ALAW; diff --git a/src/pulse/sample.h b/src/pulse/sample.h index 683167cc..b307621e 100644 --- a/src/pulse/sample.h +++ b/src/pulse/sample.h @@ -155,31 +155,31 @@ typedef struct pa_sample_spec { typedef uint64_t pa_usec_t; /** Return the amount of bytes playback of a second of audio with the specified sample type takes */ -size_t pa_bytes_per_second(const pa_sample_spec *spec); +size_t pa_bytes_per_second(const pa_sample_spec *spec) PA_GCC_PURE; /** Return the size of a frame with the specific sample type */ -size_t pa_frame_size(const pa_sample_spec *spec); +size_t pa_frame_size(const pa_sample_spec *spec) PA_GCC_PURE; /** Return the size of a sample with the specific sample type */ -size_t pa_sample_size(const pa_sample_spec *spec); +size_t pa_sample_size(const pa_sample_spec *spec) PA_GCC_PURE; /** Calculate the time the specified bytes take to play with the specified sample type */ -pa_usec_t pa_bytes_to_usec(uint64_t length, const pa_sample_spec *spec); +pa_usec_t pa_bytes_to_usec(uint64_t length, const pa_sample_spec *spec) PA_GCC_PURE; /** Calculates the number of bytes that are required for the specified time. \since 0.9 */ -size_t pa_usec_to_bytes(pa_usec_t t, const pa_sample_spec *spec); +size_t pa_usec_to_bytes(pa_usec_t t, const pa_sample_spec *spec) PA_GCC_PURE; /** Return non-zero when the sample type specification is valid */ -int pa_sample_spec_valid(const pa_sample_spec *spec); +int pa_sample_spec_valid(const pa_sample_spec *spec) PA_GCC_PURE; /** Return non-zero when the two sample type specifications match */ -int pa_sample_spec_equal(const pa_sample_spec*a, const pa_sample_spec*b); +int pa_sample_spec_equal(const pa_sample_spec*a, const pa_sample_spec*b) PA_GCC_PURE; /** Return a descriptive string for the specified sample format. \since 0.8 */ -const char *pa_sample_format_to_string(pa_sample_format_t f); +const char *pa_sample_format_to_string(pa_sample_format_t f) PA_GCC_PURE; /** Parse a sample format text. Inverse of pa_sample_format_to_string() */ -pa_sample_format_t pa_parse_sample_format(const char *format); +pa_sample_format_t pa_parse_sample_format(const char *format) PA_GCC_PURE; /** Maximum required string length for pa_sample_spec_snprint() */ #define PA_SAMPLE_SPEC_SNPRINT_MAX 32 diff --git a/src/pulse/scache.c b/src/pulse/scache.c index 09bc1078..186b0a3e 100644 --- a/src/pulse/scache.c +++ b/src/pulse/scache.c @@ -25,12 +25,12 @@ #include #endif -#include #include #include #include #include +#include #include "internal.h" @@ -40,7 +40,8 @@ int pa_stream_connect_upload(pa_stream *s, size_t length) { pa_tagstruct *t; uint32_t tag; - assert(s); + pa_assert(s); + pa_assert(PA_REFCNT_VALUE(s) >= 1); PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_UNCONNECTED, PA_ERR_BADSTATE); PA_CHECK_VALIDITY(s->context, length > 0, PA_ERR_INVALID); @@ -66,7 +67,9 @@ int pa_stream_connect_upload(pa_stream *s, size_t length) { int pa_stream_finish_upload(pa_stream *s) { pa_tagstruct *t; uint32_t tag; - assert(s); + + pa_assert(s); + pa_assert(PA_REFCNT_VALUE(s) >= 1); PA_CHECK_VALIDITY(s->context, s->channel_valid, PA_ERR_BADSTATE); PA_CHECK_VALIDITY(s->context, s->context->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); @@ -87,8 +90,8 @@ pa_operation *pa_context_play_sample(pa_context *c, const char *name, const char pa_tagstruct *t; uint32_t tag; - assert(c); - assert(c->ref >= 1); + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(c, name && *name, PA_ERR_INVALID); @@ -115,8 +118,8 @@ pa_operation* pa_context_remove_sample(pa_context *c, const char *name, pa_conte pa_tagstruct *t; uint32_t tag; - assert(c); - assert(c->ref >= 1); + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(c, name && *name, PA_ERR_INVALID); diff --git a/src/pulse/simple.c b/src/pulse/simple.c index 3cf862d2..1072fb4d 100644 --- a/src/pulse/simple.c +++ b/src/pulse/simple.c @@ -1,3 +1,4 @@ + /* $Id$ */ /*** @@ -27,7 +28,6 @@ #include #include -#include #include #include @@ -36,6 +36,7 @@ #include #include +#include #include "simple.h" @@ -83,8 +84,8 @@ if (!(p)->context || pa_context_get_state((p)->context) != PA_CONTEXT_READY || \ static void context_state_cb(pa_context *c, void *userdata) { pa_simple *p = userdata; - assert(c); - assert(p); + pa_assert(c); + pa_assert(p); switch (pa_context_get_state(c)) { case PA_CONTEXT_READY: @@ -103,8 +104,8 @@ static void context_state_cb(pa_context *c, void *userdata) { static void stream_state_cb(pa_stream *s, void * userdata) { pa_simple *p = userdata; - assert(s); - assert(p); + pa_assert(s); + pa_assert(p); switch (pa_stream_get_state(s)) { @@ -122,7 +123,7 @@ static void stream_state_cb(pa_stream *s, void * userdata) { static void stream_request_cb(pa_stream *s, size_t length, void *userdata) { pa_simple *p = userdata; - assert(p); + pa_assert(p); pa_threaded_mainloop_signal(p->mainloop, 0); } @@ -130,21 +131,21 @@ static void stream_request_cb(pa_stream *s, size_t length, void *userdata) { static void stream_latency_update_cb(pa_stream *s, void *userdata) { pa_simple *p = userdata; - assert(p); + pa_assert(p); pa_threaded_mainloop_signal(p->mainloop, 0); } pa_simple* pa_simple_new( - const char *server, - const char *name, - pa_stream_direction_t dir, - const char *dev, - const char *stream_name, - const pa_sample_spec *ss, - const pa_channel_map *map, - const pa_buffer_attr *attr, - int *rerror) { + const char *server, + const char *name, + pa_stream_direction_t dir, + const char *dev, + const char *stream_name, + const pa_sample_spec *ss, + const pa_channel_map *map, + const pa_buffer_attr *attr, + int *rerror) { pa_simple *p; int error = PA_ERR_INTERNAL, r; @@ -232,7 +233,7 @@ fail: } void pa_simple_free(pa_simple *s) { - assert(s); + pa_assert(s); if (s->mainloop) pa_threaded_mainloop_stop(s->mainloop); @@ -250,7 +251,7 @@ void pa_simple_free(pa_simple *s) { } int pa_simple_write(pa_simple *p, const void*data, size_t length, int *rerror) { - assert(p); + pa_assert(p); CHECK_VALIDITY_RETURN_ANY(rerror, p->direction == PA_STREAM_PLAYBACK, PA_ERR_BADSTATE, -1); CHECK_VALIDITY_RETURN_ANY(rerror, data && length, PA_ERR_INVALID, -1); @@ -289,7 +290,7 @@ unlock_and_fail: } int pa_simple_read(pa_simple *p, void*data, size_t length, int *rerror) { - assert(p); + pa_assert(p); CHECK_VALIDITY_RETURN_ANY(rerror, p->direction == PA_STREAM_RECORD, PA_ERR_BADSTATE, -1); CHECK_VALIDITY_RETURN_ANY(rerror, data && length, PA_ERR_INVALID, -1); @@ -346,8 +347,8 @@ unlock_and_fail: static void success_cb(pa_stream *s, int success, void *userdata) { pa_simple *p = userdata; - assert(s); - assert(p); + pa_assert(s); + pa_assert(p); p->operation_success = success; pa_threaded_mainloop_signal(p->mainloop, 0); @@ -356,7 +357,7 @@ static void success_cb(pa_stream *s, int success, void *userdata) { int pa_simple_drain(pa_simple *p, int *rerror) { pa_operation *o = NULL; - assert(p); + pa_assert(p); CHECK_VALIDITY_RETURN_ANY(rerror, p->direction == PA_STREAM_PLAYBACK, PA_ERR_BADSTATE, -1); @@ -392,7 +393,7 @@ unlock_and_fail: int pa_simple_flush(pa_simple *p, int *rerror) { pa_operation *o = NULL; - assert(p); + pa_assert(p); CHECK_VALIDITY_RETURN_ANY(rerror, p->direction == PA_STREAM_PLAYBACK, PA_ERR_BADSTATE, -1); @@ -429,7 +430,7 @@ pa_usec_t pa_simple_get_latency(pa_simple *p, int *rerror) { pa_usec_t t; int negative; - assert(p); + pa_assert(p); pa_threaded_mainloop_lock(p->mainloop); diff --git a/src/pulse/simple.h b/src/pulse/simple.h index 128d2716..f76c1d67 100644 --- a/src/pulse/simple.h +++ b/src/pulse/simple.h @@ -51,7 +51,7 @@ * pa_simple *s; * pa_sample_spec ss; * - * ss.format = PA_SAMPLE_S16_NE; + * ss.format = PA_SAMPLE_S16NE; * ss.channels = 2; * ss.rate = 44100; * diff --git a/src/pulse/stream.c b/src/pulse/stream.c index f20c17ae..47906a5c 100644 --- a/src/pulse/stream.c +++ b/src/pulse/stream.c @@ -26,7 +26,6 @@ #include #endif -#include #include #include #include @@ -38,6 +37,7 @@ #include #include #include +#include #include "internal.h" @@ -47,13 +47,14 @@ pa_stream *pa_stream_new(pa_context *c, const char *name, const pa_sample_spec * pa_stream *s; int i; - assert(c); + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); PA_CHECK_VALIDITY_RETURN_NULL(c, ss && pa_sample_spec_valid(ss), PA_ERR_INVALID); PA_CHECK_VALIDITY_RETURN_NULL(c, !map || (pa_channel_map_valid(map) && map->channels == ss->channels), PA_ERR_INVALID); s = pa_xnew(pa_stream, 1); - s->ref = 1; + PA_REFCNT_INIT(s); s->context = c; s->mainloop = c->mainloop; @@ -91,6 +92,7 @@ pa_stream *pa_stream_new(pa_context *c, const char *name, const pa_sample_spec * s->peek_memchunk.index = 0; s->peek_memchunk.length = 0; s->peek_memchunk.memblock = NULL; + s->peek_data = NULL; s->record_memblockq = NULL; @@ -118,15 +120,20 @@ pa_stream *pa_stream_new(pa_context *c, const char *name, const pa_sample_spec * } static void stream_free(pa_stream *s) { - assert(s && !s->context && !s->channel_valid); + pa_assert(s); + pa_assert(!s->context); + pa_assert(!s->channel_valid); if (s->auto_timing_update_event) { - assert(s->mainloop); + pa_assert(s->mainloop); s->mainloop->time_free(s->auto_timing_update_event); } - if (s->peek_memchunk.memblock) + if (s->peek_memchunk.memblock) { + if (s->peek_data) + pa_memblock_release(s->peek_memchunk.memblock); pa_memblock_unref(s->peek_memchunk.memblock); + } if (s->record_memblockq) pa_memblockq_free(s->record_memblockq); @@ -136,38 +143,38 @@ static void stream_free(pa_stream *s) { } void pa_stream_unref(pa_stream *s) { - assert(s); - assert(s->ref >= 1); + pa_assert(s); + pa_assert(PA_REFCNT_VALUE(s) >= 1); - if (--(s->ref) == 0) + if (PA_REFCNT_DEC(s) <= 0) stream_free(s); } pa_stream* pa_stream_ref(pa_stream *s) { - assert(s); - assert(s->ref >= 1); + pa_assert(s); + pa_assert(PA_REFCNT_VALUE(s) >= 1); - s->ref++; + PA_REFCNT_INC(s); return s; } pa_stream_state_t pa_stream_get_state(pa_stream *s) { - assert(s); - assert(s->ref >= 1); + pa_assert(s); + pa_assert(PA_REFCNT_VALUE(s) >= 1); return s->state; } pa_context* pa_stream_get_context(pa_stream *s) { - assert(s); - assert(s->ref >= 1); + pa_assert(s); + pa_assert(PA_REFCNT_VALUE(s) >= 1); return s->context; } uint32_t pa_stream_get_index(pa_stream *s) { - assert(s); - assert(s->ref >= 1); + pa_assert(s); + pa_assert(PA_REFCNT_VALUE(s) >= 1); PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE, PA_INVALID_INDEX); @@ -175,8 +182,8 @@ uint32_t pa_stream_get_index(pa_stream *s) { } void pa_stream_set_state(pa_stream *s, pa_stream_state_t st) { - assert(s); - assert(s->ref >= 1); + pa_assert(s); + pa_assert(PA_REFCNT_VALUE(s) >= 1); if (s->state == st) return; @@ -214,6 +221,13 @@ void pa_stream_set_state(pa_stream *s, pa_stream_state_t st) { s->channel_valid = 0; s->context = NULL; + + s->read_callback = NULL; + s->write_callback = NULL; + s->state_callback = NULL; + s->overflow_callback = NULL; + s->underflow_callback = NULL; + s->latency_update_callback = NULL; } pa_stream_unref(s); @@ -224,10 +238,11 @@ void pa_command_stream_killed(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED pa_stream *s; uint32_t channel; - assert(pd); - assert(command == PA_COMMAND_PLAYBACK_STREAM_KILLED || command == PA_COMMAND_RECORD_STREAM_KILLED); - assert(t); - assert(c); + pa_assert(pd); + pa_assert(command == PA_COMMAND_PLAYBACK_STREAM_KILLED || command == PA_COMMAND_RECORD_STREAM_KILLED); + pa_assert(t); + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); pa_context_ref(c); @@ -252,10 +267,11 @@ void pa_command_request(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32 pa_context *c = userdata; uint32_t bytes, channel; - assert(pd); - assert(command == PA_COMMAND_REQUEST); - assert(t); - assert(c); + pa_assert(pd); + pa_assert(command == PA_COMMAND_REQUEST); + pa_assert(t); + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); pa_context_ref(c); @@ -285,10 +301,11 @@ void pa_command_overflow_or_underflow(pa_pdispatch *pd, uint32_t command, PA_GCC pa_context *c = userdata; uint32_t channel; - assert(pd); - assert(command == PA_COMMAND_OVERFLOW || command == PA_COMMAND_UNDERFLOW); - assert(t); - assert(c); + pa_assert(pd); + pa_assert(command == PA_COMMAND_OVERFLOW || command == PA_COMMAND_UNDERFLOW); + pa_assert(t); + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); pa_context_ref(c); @@ -317,8 +334,8 @@ void pa_command_overflow_or_underflow(pa_pdispatch *pd, uint32_t command, PA_GCC } static void request_auto_timing_update(pa_stream *s, int force) { - struct timeval next; - assert(s); + pa_assert(s); + pa_assert(PA_REFCNT_VALUE(s) >= 1); if (!(s->flags & PA_STREAM_AUTO_TIMING_UPDATE)) return; @@ -335,13 +352,17 @@ static void request_auto_timing_update(pa_stream *s, int force) { } } - pa_gettimeofday(&next); - pa_timeval_add(&next, LATENCY_IPOL_INTERVAL_USEC); - s->mainloop->time_restart(s->auto_timing_update_event, &next); + if (s->auto_timing_update_event) { + struct timeval next; + pa_gettimeofday(&next); + pa_timeval_add(&next, LATENCY_IPOL_INTERVAL_USEC); + s->mainloop->time_restart(s->auto_timing_update_event, &next); + } } static void invalidate_indexes(pa_stream *s, int r, int w) { - assert(s); + pa_assert(s); + pa_assert(PA_REFCNT_VALUE(s) >= 1); /* pa_log("invalidate r:%u w:%u tag:%u", r, w, s->context->ctag); */ @@ -376,6 +397,9 @@ static void invalidate_indexes(pa_stream *s, int r, int w) { static void auto_timing_update_callback(PA_GCC_UNUSED pa_mainloop_api *m, PA_GCC_UNUSED pa_time_event *e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) { pa_stream *s = userdata; + pa_assert(s); + pa_assert(PA_REFCNT_VALUE(s) >= 1); + /* pa_log("time event"); */ pa_stream_ref(s); @@ -383,12 +407,32 @@ static void auto_timing_update_callback(PA_GCC_UNUSED pa_mainloop_api *m, PA_GCC pa_stream_unref(s); } +static void create_stream_complete(pa_stream *s) { + pa_assert(s); + pa_assert(PA_REFCNT_VALUE(s) >= 1); + pa_assert(s->state == PA_STREAM_CREATING); + + pa_stream_set_state(s, PA_STREAM_READY); + + if (s->requested_bytes > 0 && s->write_callback) + s->write_callback(s, s->requested_bytes, s->write_userdata); + + if (s->flags & PA_STREAM_AUTO_TIMING_UPDATE) { + struct timeval tv; + pa_gettimeofday(&tv); + tv.tv_usec += LATENCY_IPOL_INTERVAL_USEC; /* every 100 ms */ + pa_assert(!s->auto_timing_update_event); + s->auto_timing_update_event = s->mainloop->time_new(s->mainloop, &tv, &auto_timing_update_callback, s); + } +} + void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { pa_stream *s = userdata; - assert(pd); - assert(s); - assert(s->state == PA_STREAM_CREATING); + pa_assert(pd); + pa_assert(s); + pa_assert(PA_REFCNT_VALUE(s) >= 1); + pa_assert(s->state == PA_STREAM_CREATING); pa_stream_ref(s); @@ -431,7 +475,7 @@ void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED } if (s->direction == PA_STREAM_RECORD) { - assert(!s->record_memblockq); + pa_assert(!s->record_memblockq); s->record_memblockq = pa_memblockq_new( 0, @@ -446,23 +490,16 @@ void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED s->channel_valid = 1; pa_dynarray_put((s->direction == PA_STREAM_RECORD) ? s->context->record_streams : s->context->playback_streams, s->channel, s); - pa_stream_set_state(s, PA_STREAM_READY); - - if (s->direction != PA_STREAM_UPLOAD && - s->flags & PA_STREAM_AUTO_TIMING_UPDATE) { - struct timeval tv; - - pa_gettimeofday(&tv); - tv.tv_usec += LATENCY_IPOL_INTERVAL_USEC; /* every 100 ms */ - - assert(!s->auto_timing_update_event); - s->auto_timing_update_event = s->mainloop->time_new(s->mainloop, &tv, &auto_timing_update_callback, s); - + if (s->direction != PA_STREAM_UPLOAD && s->flags & PA_STREAM_AUTO_TIMING_UPDATE) { + /* If automatic timing updates are active, we wait for the + * first timing update before going to PA_STREAM_READY + * state */ + s->state = PA_STREAM_READY; request_auto_timing_update(s, 1); - } + s->state = PA_STREAM_CREATING; - if (s->requested_bytes > 0 && s->ref > 1 && s->write_callback) - s->write_callback(s, s->requested_bytes, s->write_userdata); + } else + create_stream_complete(s); finish: pa_stream_unref(s); @@ -480,8 +517,8 @@ static int create_stream( pa_tagstruct *t; uint32_t tag; - assert(s); - assert(s->ref >= 1); + pa_assert(s); + pa_assert(PA_REFCNT_VALUE(s) >= 1); PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_UNCONNECTED, PA_ERR_BADSTATE); PA_CHECK_VALIDITY(s->context, !(flags & ~((direction != PA_STREAM_UPLOAD ? @@ -503,12 +540,12 @@ static int create_stream( if (attr) s->buffer_attr = *attr; else { - /* half a second */ + /* half a second, with minimum request of 10 ms */ s->buffer_attr.tlength = pa_bytes_per_second(&s->sample_spec)/2; s->buffer_attr.maxlength = (s->buffer_attr.tlength*3)/2; - s->buffer_attr.minreq = s->buffer_attr.tlength/100; + s->buffer_attr.minreq = s->buffer_attr.tlength/50; s->buffer_attr.prebuf = s->buffer_attr.tlength - s->buffer_attr.minreq; - s->buffer_attr.fragsize = s->buffer_attr.tlength/100; + s->buffer_attr.fragsize = s->buffer_attr.tlength/50; } if (!dev) @@ -565,8 +602,8 @@ int pa_stream_connect_playback( pa_cvolume *volume, pa_stream *sync_stream) { - assert(s); - assert(s->ref >= 1); + pa_assert(s); + pa_assert(PA_REFCNT_VALUE(s) >= 1); return create_stream(PA_STREAM_PLAYBACK, s, dev, attr, flags, volume, sync_stream); } @@ -577,8 +614,8 @@ int pa_stream_connect_record( const pa_buffer_attr *attr, pa_stream_flags_t flags) { - assert(s); - assert(s->ref >= 1); + pa_assert(s); + pa_assert(PA_REFCNT_VALUE(s) >= 1); return create_stream(PA_STREAM_RECORD, s, dev, attr, flags, NULL, NULL); } @@ -593,9 +630,9 @@ int pa_stream_write( pa_memchunk chunk; - assert(s); - assert(s->ref >= 1); - assert(data); + pa_assert(s); + pa_assert(PA_REFCNT_VALUE(s) >= 1); + pa_assert(data); 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); @@ -608,8 +645,11 @@ int pa_stream_write( if (free_cb) chunk.memblock = pa_memblock_new_user(s->context->mempool, (void*) data, length, free_cb, 1); else { + void *tdata; chunk.memblock = pa_memblock_new(s->context->mempool, length); - memcpy(chunk.memblock->data, data, length); + tdata = pa_memblock_acquire(chunk.memblock); + memcpy(tdata, data, length); + pa_memblock_release(chunk.memblock); } chunk.index = 0; @@ -660,10 +700,10 @@ int pa_stream_write( } int pa_stream_peek(pa_stream *s, const void **data, size_t *length) { - assert(s); - assert(s->ref >= 1); - assert(data); - assert(length); + pa_assert(s); + pa_assert(PA_REFCNT_VALUE(s) >= 1); + pa_assert(data); + pa_assert(length); PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE); PA_CHECK_VALIDITY(s->context, s->direction == PA_STREAM_RECORD, PA_ERR_BADSTATE); @@ -675,27 +715,32 @@ int pa_stream_peek(pa_stream *s, const void **data, size_t *length) { *length = 0; return 0; } + + s->peek_data = pa_memblock_acquire(s->peek_memchunk.memblock); } - *data = (const char*) s->peek_memchunk.memblock->data + s->peek_memchunk.index; + pa_assert(s->peek_data); + *data = (uint8_t*) s->peek_data + s->peek_memchunk.index; *length = s->peek_memchunk.length; return 0; } int pa_stream_drop(pa_stream *s) { - assert(s); - assert(s->ref >= 1); + pa_assert(s); + pa_assert(PA_REFCNT_VALUE(s) >= 1); PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE); PA_CHECK_VALIDITY(s->context, s->direction == PA_STREAM_RECORD, PA_ERR_BADSTATE); PA_CHECK_VALIDITY(s->context, s->peek_memchunk.memblock, PA_ERR_BADSTATE); - pa_memblockq_drop(s->record_memblockq, &s->peek_memchunk, s->peek_memchunk.length); + pa_memblockq_drop(s->record_memblockq, s->peek_memchunk.length); /* Fix the simulated local read index */ if (s->timing_info_valid && !s->timing_info.read_index_corrupt) s->timing_info.read_index += s->peek_memchunk.length; + pa_assert(s->peek_data); + pa_memblock_release(s->peek_memchunk.memblock); pa_memblock_unref(s->peek_memchunk.memblock); s->peek_memchunk.length = 0; s->peek_memchunk.index = 0; @@ -705,8 +750,8 @@ int pa_stream_drop(pa_stream *s) { } size_t pa_stream_writable_size(pa_stream *s) { - assert(s); - assert(s->ref >= 1); + pa_assert(s); + pa_assert(PA_REFCNT_VALUE(s) >= 1); PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE, (size_t) -1); PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->direction != PA_STREAM_RECORD, PA_ERR_BADSTATE, (size_t) -1); @@ -715,8 +760,8 @@ size_t pa_stream_writable_size(pa_stream *s) { } size_t pa_stream_readable_size(pa_stream *s) { - assert(s); - assert(s->ref >= 1); + pa_assert(s); + pa_assert(PA_REFCNT_VALUE(s) >= 1); PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE, (size_t) -1); PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->direction == PA_STREAM_RECORD, PA_ERR_BADSTATE, (size_t) -1); @@ -729,8 +774,8 @@ pa_operation * pa_stream_drain(pa_stream *s, pa_stream_success_cb_t cb, void *us pa_tagstruct *t; uint32_t tag; - assert(s); - assert(s->ref >= 1); + pa_assert(s); + pa_assert(PA_REFCNT_VALUE(s) >= 1); PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction == PA_STREAM_PLAYBACK, PA_ERR_BADSTATE); @@ -750,8 +795,9 @@ static void stream_get_timing_info_callback(pa_pdispatch *pd, uint32_t command, struct timeval local, remote, now; pa_timing_info *i; - assert(pd); - assert(o); + pa_assert(pd); + pa_assert(o); + pa_assert(PA_REFCNT_VALUE(o) >= 1); if (!o->context || !o->stream) goto finish; @@ -874,6 +920,10 @@ static void stream_get_timing_info_callback(pa_pdispatch *pd, uint32_t command, } } + /* First, let's complete the initialization, if necessary. */ + if (o->stream->state == PA_STREAM_CREATING) + create_stream_complete(o->stream); + if (o->stream->latency_update_callback) o->stream->latency_update_callback(o->stream, o->stream->latency_update_userdata); @@ -895,8 +945,8 @@ pa_operation* pa_stream_update_timing_info(pa_stream *s, pa_stream_success_cb_t struct timeval now; int cidx = 0; - assert(s); - assert(s->ref >= 1); + pa_assert(s); + pa_assert(PA_REFCNT_VALUE(s) >= 1); PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE); @@ -938,9 +988,9 @@ pa_operation* pa_stream_update_timing_info(pa_stream *s, pa_stream_success_cb_t void pa_stream_disconnect_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { pa_stream *s = userdata; - assert(pd); - assert(s); - assert(s->ref >= 1); + pa_assert(pd); + pa_assert(s); + pa_assert(PA_REFCNT_VALUE(s) >= 1); pa_stream_ref(s); @@ -965,8 +1015,8 @@ int pa_stream_disconnect(pa_stream *s) { pa_tagstruct *t; uint32_t tag; - assert(s); - assert(s->ref >= 1); + pa_assert(s); + pa_assert(PA_REFCNT_VALUE(s) >= 1); PA_CHECK_VALIDITY(s->context, s->channel_valid, PA_ERR_BADSTATE); PA_CHECK_VALIDITY(s->context, s->context->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); @@ -987,48 +1037,48 @@ int pa_stream_disconnect(pa_stream *s) { } void pa_stream_set_read_callback(pa_stream *s, pa_stream_request_cb_t cb, void *userdata) { - assert(s); - assert(s->ref >= 1); + pa_assert(s); + pa_assert(PA_REFCNT_VALUE(s) >= 1); s->read_callback = cb; s->read_userdata = userdata; } void pa_stream_set_write_callback(pa_stream *s, pa_stream_request_cb_t cb, void *userdata) { - assert(s); - assert(s->ref >= 1); + pa_assert(s); + pa_assert(PA_REFCNT_VALUE(s) >= 1); s->write_callback = cb; s->write_userdata = userdata; } void pa_stream_set_state_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata) { - assert(s); - assert(s->ref >= 1); + pa_assert(s); + pa_assert(PA_REFCNT_VALUE(s) >= 1); s->state_callback = cb; s->state_userdata = userdata; } void pa_stream_set_overflow_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata) { - assert(s); - assert(s->ref >= 1); + pa_assert(s); + pa_assert(PA_REFCNT_VALUE(s) >= 1); s->overflow_callback = cb; s->overflow_userdata = userdata; } void pa_stream_set_underflow_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata) { - assert(s); - assert(s->ref >= 1); + pa_assert(s); + pa_assert(PA_REFCNT_VALUE(s) >= 1); s->underflow_callback = cb; s->underflow_userdata = userdata; } void pa_stream_set_latency_update_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata) { - assert(s); - assert(s->ref >= 1); + pa_assert(s); + pa_assert(PA_REFCNT_VALUE(s) >= 1); s->latency_update_callback = cb; s->latency_update_userdata = userdata; @@ -1038,9 +1088,9 @@ void pa_stream_simple_ack_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UN pa_operation *o = userdata; int success = 1; - assert(pd); - assert(o); - assert(o->ref >= 1); + pa_assert(pd); + pa_assert(o); + pa_assert(PA_REFCNT_VALUE(o) >= 1); if (!o->context) goto finish; @@ -1070,8 +1120,8 @@ pa_operation* pa_stream_cork(pa_stream *s, int b, pa_stream_success_cb_t cb, voi pa_tagstruct *t; uint32_t tag; - assert(s); - assert(s->ref >= 1); + pa_assert(s); + pa_assert(PA_REFCNT_VALUE(s) >= 1); PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE); @@ -1100,8 +1150,8 @@ static pa_operation* stream_send_simple_command(pa_stream *s, uint32_t command, pa_operation *o; uint32_t tag; - assert(s); - assert(s->ref >= 1); + pa_assert(s); + pa_assert(PA_REFCNT_VALUE(s) >= 1); PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE); @@ -1118,6 +1168,9 @@ static pa_operation* stream_send_simple_command(pa_stream *s, uint32_t command, pa_operation* pa_stream_flush(pa_stream *s, pa_stream_success_cb_t cb, void *userdata) { pa_operation *o; + pa_assert(s); + pa_assert(PA_REFCNT_VALUE(s) >= 1); + PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE); if ((o = stream_send_simple_command(s, s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_FLUSH_PLAYBACK_STREAM : PA_COMMAND_FLUSH_RECORD_STREAM, cb, userdata))) { @@ -1143,6 +1196,9 @@ pa_operation* pa_stream_flush(pa_stream *s, pa_stream_success_cb_t cb, void *use pa_operation* pa_stream_prebuf(pa_stream *s, pa_stream_success_cb_t cb, void *userdata) { pa_operation *o; + pa_assert(s); + pa_assert(PA_REFCNT_VALUE(s) >= 1); + PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction == PA_STREAM_PLAYBACK, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->buffer_attr.prebuf > 0, PA_ERR_BADSTATE); @@ -1155,6 +1211,9 @@ pa_operation* pa_stream_prebuf(pa_stream *s, pa_stream_success_cb_t cb, void *us pa_operation* pa_stream_trigger(pa_stream *s, pa_stream_success_cb_t cb, void *userdata) { pa_operation *o; + pa_assert(s); + pa_assert(PA_REFCNT_VALUE(s) >= 1); + PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction == PA_STREAM_PLAYBACK, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->buffer_attr.prebuf > 0, PA_ERR_BADSTATE); @@ -1169,9 +1228,9 @@ pa_operation* pa_stream_set_name(pa_stream *s, const char *name, pa_stream_succe pa_tagstruct *t; uint32_t tag; - assert(s); - assert(s->ref >= 1); - assert(name); + pa_assert(s); + pa_assert(PA_REFCNT_VALUE(s) >= 1); + pa_assert(name); PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE); @@ -1193,8 +1252,8 @@ pa_operation* pa_stream_set_name(pa_stream *s, const char *name, pa_stream_succe int pa_stream_get_time(pa_stream *s, pa_usec_t *r_usec) { pa_usec_t usec = 0; - assert(s); - assert(s->ref >= 1); + pa_assert(s); + pa_assert(PA_REFCNT_VALUE(s) >= 1); PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE); PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE); @@ -1277,8 +1336,8 @@ int pa_stream_get_time(pa_stream *s, pa_usec_t *r_usec) { } static pa_usec_t time_counter_diff(pa_stream *s, pa_usec_t a, pa_usec_t b, int *negative) { - assert(s); - assert(s->ref >= 1); + pa_assert(s); + pa_assert(PA_REFCNT_VALUE(s) >= 1); if (negative) *negative = 0; @@ -1299,9 +1358,9 @@ int pa_stream_get_latency(pa_stream *s, pa_usec_t *r_usec, int *negative) { int r; int64_t cindex; - assert(s); - assert(s->ref >= 1); - assert(r_usec); + pa_assert(s); + pa_assert(PA_REFCNT_VALUE(s) >= 1); + pa_assert(r_usec); PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE); PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE); @@ -1331,8 +1390,8 @@ int pa_stream_get_latency(pa_stream *s, pa_usec_t *r_usec, int *negative) { } const pa_timing_info* pa_stream_get_timing_info(pa_stream *s) { - assert(s); - assert(s->ref >= 1); + pa_assert(s); + pa_assert(PA_REFCNT_VALUE(s) >= 1); PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE); @@ -1342,22 +1401,22 @@ const pa_timing_info* pa_stream_get_timing_info(pa_stream *s) { } const pa_sample_spec* pa_stream_get_sample_spec(pa_stream *s) { - assert(s); - assert(s->ref >= 1); + pa_assert(s); + pa_assert(PA_REFCNT_VALUE(s) >= 1); return &s->sample_spec; } const pa_channel_map* pa_stream_get_channel_map(pa_stream *s) { - assert(s); - assert(s->ref >= 1); + pa_assert(s); + pa_assert(PA_REFCNT_VALUE(s) >= 1); return &s->channel_map; } const pa_buffer_attr* pa_stream_get_buffer_attr(pa_stream *s) { - assert(s); - assert(s->ref >= 1); + pa_assert(s); + pa_assert(PA_REFCNT_VALUE(s) >= 1); PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE); diff --git a/src/pulse/subscribe.c b/src/pulse/subscribe.c index 5d8f1252..580038cc 100644 --- a/src/pulse/subscribe.c +++ b/src/pulse/subscribe.c @@ -25,10 +25,10 @@ #include #endif -#include #include #include +#include #include #include "internal.h" @@ -40,10 +40,11 @@ void pa_command_subscribe_event(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSE pa_subscription_event_type_t e; uint32_t idx; - assert(pd); - assert(command == PA_COMMAND_SUBSCRIBE_EVENT); - assert(t); - assert(c); + pa_assert(pd); + pa_assert(command == PA_COMMAND_SUBSCRIBE_EVENT); + pa_assert(t); + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); pa_context_ref(c); @@ -67,8 +68,8 @@ pa_operation* pa_context_subscribe(pa_context *c, pa_subscription_mask_t m, pa_c pa_tagstruct *t; uint32_t tag; - assert(c); - assert(c->ref >= 1); + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); @@ -83,8 +84,8 @@ pa_operation* pa_context_subscribe(pa_context *c, pa_subscription_mask_t m, pa_c } void pa_context_set_subscribe_callback(pa_context *c, pa_context_subscribe_cb_t cb, void *userdata) { - assert(c); - assert(c->ref >= 1); + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); c->subscribe_callback = cb; c->subscribe_userdata = userdata; diff --git a/src/pulse/thread-mainloop.c b/src/pulse/thread-mainloop.c index 4f3cacc9..9dd47ae3 100644 --- a/src/pulse/thread-mainloop.c +++ b/src/pulse/thread-mainloop.c @@ -26,24 +26,24 @@ #include #endif -#include #include #include -#ifdef HAVE_SYS_POLL_H -#include +#ifdef HAVE_POLL_H +#include #else -#include "../pulsecore/poll.h" +#include #endif #include +#include #include #include #include #include +#include -#include "mainloop.h" #include "thread-mainloop.h" struct pa_threaded_mainloop { @@ -63,7 +63,7 @@ static int poll_func(struct pollfd *ufds, unsigned long nfds, int timeout, void pa_mutex *mutex = userdata; int r; - assert(mutex); + pa_assert(mutex); /* Before entering poll() we unlock the mutex, so that * avahi_simple_poll_quit() can succeed from another thread. */ @@ -103,7 +103,7 @@ pa_threaded_mainloop *pa_threaded_mainloop_new(void) { return NULL; } - m->mutex = pa_mutex_new(1); + m->mutex = pa_mutex_new(TRUE, FALSE); m->cond = pa_cond_new(); m->accept_cond = pa_cond_new(); m->thread = NULL; @@ -116,10 +116,10 @@ pa_threaded_mainloop *pa_threaded_mainloop_new(void) { } void pa_threaded_mainloop_free(pa_threaded_mainloop* m) { - assert(m); + pa_assert(m); /* Make sure that this function is not called from the helper thread */ - assert((m->thread && !pa_thread_is_running(m->thread)) || !in_worker(m)); + pa_assert((m->thread && !pa_thread_is_running(m->thread)) || !in_worker(m)); pa_threaded_mainloop_stop(m); @@ -136,9 +136,9 @@ void pa_threaded_mainloop_free(pa_threaded_mainloop* m) { } int pa_threaded_mainloop_start(pa_threaded_mainloop *m) { - assert(m); + pa_assert(m); - assert(!m->thread || !pa_thread_is_running(m->thread)); + pa_assert(!m->thread || !pa_thread_is_running(m->thread)); if (!(m->thread = pa_thread_new(thread, m))) return -1; @@ -147,13 +147,13 @@ int pa_threaded_mainloop_start(pa_threaded_mainloop *m) { } void pa_threaded_mainloop_stop(pa_threaded_mainloop *m) { - assert(m); + pa_assert(m); if (!m->thread || !pa_thread_is_running(m->thread)) return; /* Make sure that this function is not called from the helper thread */ - assert(!in_worker(m)); + pa_assert(!in_worker(m)); pa_mutex_lock(m->mutex); pa_mainloop_quit(m->real_mainloop, 0); @@ -163,25 +163,25 @@ void pa_threaded_mainloop_stop(pa_threaded_mainloop *m) { } void pa_threaded_mainloop_lock(pa_threaded_mainloop *m) { - assert(m); + pa_assert(m); /* Make sure that this function is not called from the helper thread */ - assert(!m->thread || !pa_thread_is_running(m->thread) || !in_worker(m)); + pa_assert(!m->thread || !pa_thread_is_running(m->thread) || !in_worker(m)); pa_mutex_lock(m->mutex); } void pa_threaded_mainloop_unlock(pa_threaded_mainloop *m) { - assert(m); + pa_assert(m); /* Make sure that this function is not called from the helper thread */ - assert(!m->thread || !pa_thread_is_running(m->thread) || !in_worker(m)); + pa_assert(!m->thread || !pa_thread_is_running(m->thread) || !in_worker(m)); pa_mutex_unlock(m->mutex); } void pa_threaded_mainloop_signal(pa_threaded_mainloop *m, int wait_for_accept) { - assert(m); + pa_assert(m); pa_cond_signal(m->cond, 1); @@ -190,36 +190,42 @@ void pa_threaded_mainloop_signal(pa_threaded_mainloop *m, int wait_for_accept) { } void pa_threaded_mainloop_wait(pa_threaded_mainloop *m) { - assert(m); + pa_assert(m); /* Make sure that this function is not called from the helper thread */ - assert(!m->thread || !pa_thread_is_running(m->thread) || !in_worker(m)); + pa_assert(!m->thread || !pa_thread_is_running(m->thread) || !in_worker(m)); m->n_waiting ++; pa_cond_wait(m->cond, m->mutex); - assert(m->n_waiting > 0); + pa_assert(m->n_waiting > 0); m->n_waiting --; } void pa_threaded_mainloop_accept(pa_threaded_mainloop *m) { - assert(m); + pa_assert(m); /* Make sure that this function is not called from the helper thread */ - assert(!m->thread || !pa_thread_is_running(m->thread) || !in_worker(m)); + pa_assert(!m->thread || !pa_thread_is_running(m->thread) || !in_worker(m)); pa_cond_signal(m->accept_cond, 0); } int pa_threaded_mainloop_get_retval(pa_threaded_mainloop *m) { - assert(m); + pa_assert(m); return pa_mainloop_get_retval(m->real_mainloop); } pa_mainloop_api* pa_threaded_mainloop_get_api(pa_threaded_mainloop*m) { - assert(m); + pa_assert(m); return pa_mainloop_get_api(m->real_mainloop); } + +int pa_threaded_mainloop_in_thread(pa_threaded_mainloop *m) { + pa_assert(m); + + return m->thread && pa_thread_self() == m->thread; +} diff --git a/src/pulse/thread-mainloop.h b/src/pulse/thread-mainloop.h index b78c1583..ea08f72a 100644 --- a/src/pulse/thread-mainloop.h +++ b/src/pulse/thread-mainloop.h @@ -297,6 +297,9 @@ int pa_threaded_mainloop_get_retval(pa_threaded_mainloop *m); /** Return the abstract main loop abstraction layer vtable for this main loop. */ pa_mainloop_api* pa_threaded_mainloop_get_api(pa_threaded_mainloop*m); +/** Returns non-zero when called from withing the event loop thread. \since 0.9.7 */ +int pa_threaded_mainloop_in_thread(pa_threaded_mainloop *m); + PA_C_DECL_END #endif diff --git a/src/pulse/timeval.c b/src/pulse/timeval.c index 78ece061..70ceb71e 100644 --- a/src/pulse/timeval.c +++ b/src/pulse/timeval.c @@ -26,7 +26,6 @@ #include #endif -#include #include #include @@ -34,15 +33,17 @@ #include #endif -#include "../pulsecore/winsock.h" +#include +#include #include "timeval.h" struct timeval *pa_gettimeofday(struct timeval *tv) { #ifdef HAVE_GETTIMEOFDAY - assert(tv); + pa_assert(tv); - return gettimeofday(tv, NULL) < 0 ? NULL : tv; + pa_assert_se(gettimeofday(tv, NULL) == 0); + return tv; #elif defined(OS_IS_WIN32) /* * Copied from implementation by Steven Edwards (LGPL). @@ -59,7 +60,7 @@ struct timeval *pa_gettimeofday(struct timeval *tv) { LARGE_INTEGER li; __int64 t; - assert(tv); + pa_assert(tv); GetSystemTimeAsFileTime(&ft); li.LowPart = ft.dwLowDateTime; @@ -67,8 +68,8 @@ struct timeval *pa_gettimeofday(struct timeval *tv) { t = li.QuadPart; /* In 100-nanosecond intervals */ t -= EPOCHFILETIME; /* Offset to the Epoch time */ t /= 10; /* In microseconds */ - tv->tv_sec = (long)(t / 1000000); - tv->tv_usec = (long)(t % 1000000); + tv->tv_sec = (time_t) (t / PA_USEC_PER_SEC); + tv->tv_usec = (suseconds_t) (t % PA_USEC_PER_SEC); return tv; #else @@ -78,9 +79,11 @@ struct timeval *pa_gettimeofday(struct timeval *tv) { pa_usec_t pa_timeval_diff(const struct timeval *a, const struct timeval *b) { pa_usec_t r; - assert(a && b); - /* Check which whan is the earlier time and swap the two arguments if reuqired. */ + pa_assert(a); + pa_assert(b); + + /* Check which whan is the earlier time and swap the two arguments if required. */ if (pa_timeval_cmp(a, b) < 0) { const struct timeval *c; c = a; @@ -89,7 +92,7 @@ pa_usec_t pa_timeval_diff(const struct timeval *a, const struct timeval *b) { } /* Calculate the second difference*/ - r = ((pa_usec_t) a->tv_sec - b->tv_sec)* 1000000; + r = ((pa_usec_t) a->tv_sec - b->tv_sec) * PA_USEC_PER_SEC; /* Calculate the microsecond difference */ if (a->tv_usec > b->tv_usec) @@ -101,7 +104,8 @@ pa_usec_t pa_timeval_diff(const struct timeval *a, const struct timeval *b) { } int pa_timeval_cmp(const struct timeval *a, const struct timeval *b) { - assert(a && b); + pa_assert(a); + pa_assert(b); if (a->tv_sec < b->tv_sec) return -1; @@ -120,26 +124,43 @@ int pa_timeval_cmp(const struct timeval *a, const struct timeval *b) { pa_usec_t pa_timeval_age(const struct timeval *tv) { struct timeval now; - assert(tv); + pa_assert(tv); return pa_timeval_diff(pa_gettimeofday(&now), tv); } struct timeval* pa_timeval_add(struct timeval *tv, pa_usec_t v) { unsigned long secs; - assert(tv); + pa_assert(tv); - secs = (v/1000000); - tv->tv_sec += (unsigned long) secs; - v -= secs*1000000; + secs = (unsigned long) (v/PA_USEC_PER_SEC); + tv->tv_sec += secs; + v -= ((pa_usec_t) secs) * PA_USEC_PER_SEC; - tv->tv_usec += v; + tv->tv_usec += (suseconds_t) v; /* Normalize */ - while (tv->tv_usec >= 1000000) { + while (tv->tv_usec >= PA_USEC_PER_SEC) { tv->tv_sec++; - tv->tv_usec -= 1000000; + tv->tv_usec -= PA_USEC_PER_SEC; } return tv; } + +struct timeval* pa_timeval_store(struct timeval *tv, pa_usec_t v) { + pa_assert(tv); + + tv->tv_sec = v / PA_USEC_PER_SEC; + tv->tv_usec = v % PA_USEC_PER_SEC; + + return tv; +} + +pa_usec_t pa_timeval_load(const struct timeval *tv) { + pa_assert(tv); + + return + (pa_usec_t) tv->tv_sec * PA_USEC_PER_SEC + + (pa_usec_t) tv->tv_usec; +} diff --git a/src/pulse/timeval.h b/src/pulse/timeval.h index 1e5627e3..65a0e513 100644 --- a/src/pulse/timeval.h +++ b/src/pulse/timeval.h @@ -33,6 +33,11 @@ PA_C_DECL_BEGIN +#define PA_MSEC_PER_SEC 1000 +#define PA_USEC_PER_SEC 1000000 +#define PA_NSEC_PER_SEC 1000000000 +#define PA_USEC_PER_MSEC 1000 + struct timeval; /** Return the current timestamp, just like UNIX gettimeofday() */ @@ -40,16 +45,22 @@ struct timeval *pa_gettimeofday(struct timeval *tv); /** Calculate the difference between the two specified timeval * structs. */ -pa_usec_t pa_timeval_diff(const struct timeval *a, const struct timeval *b); +pa_usec_t pa_timeval_diff(const struct timeval *a, const struct timeval *b) PA_GCC_PURE; /** Compare the two timeval structs and return 0 when equal, negative when a < b, positive otherwse */ -int pa_timeval_cmp(const struct timeval *a, const struct timeval *b); +int pa_timeval_cmp(const struct timeval *a, const struct timeval *b) PA_GCC_PURE; /** Return the time difference between now and the specified timestamp */ pa_usec_t pa_timeval_age(const struct timeval *tv); /** Add the specified time inmicroseconds to the specified timeval structure */ -struct timeval* pa_timeval_add(struct timeval *tv, pa_usec_t v); +struct timeval* pa_timeval_add(struct timeval *tv, pa_usec_t v) PA_GCC_PURE; + +/** Store the specified uec value in the timeval struct. \since 0.9.7 */ +struct timeval* pa_timeval_store(struct timeval *tv, pa_usec_t v); + +/** Load the specified tv value and return it in usec. \since 0.9.7 */ +pa_usec_t pa_timeval_load(const struct timeval *tv); PA_C_DECL_END diff --git a/src/pulse/utf8.c b/src/pulse/utf8.c index 2ac2d106..b2f6c3bd 100644 --- a/src/pulse/utf8.c +++ b/src/pulse/utf8.c @@ -37,7 +37,7 @@ * * This library 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 + * 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 @@ -50,7 +50,6 @@ #include #endif -#include #include #include #include @@ -60,12 +59,15 @@ #include #endif +#include +#include + #include "utf8.h" -#include "xmalloc.h" #define FILTER_CHAR '_' static inline int is_unicode_valid(uint32_t ch) { + if (ch >= 0x110000) /* End of unicode space */ return 0; if ((ch & 0xFFFFF800) == 0xD800) /* Reserved area for UTF-16 */ @@ -74,6 +76,7 @@ static inline int is_unicode_valid(uint32_t ch) { return 0; if ((ch & 0xFFFE) == 0xFFFE) /* BOM (Byte Order Mark) */ return 0; + return 1; } @@ -95,6 +98,8 @@ static char* utf8_validate(const char *str, char *output) { int size; uint8_t *o; + pa_assert(str); + o = (uint8_t*) output; for (p = (const uint8_t*) str; *p; p++) { if (*p < 128) { @@ -178,15 +183,15 @@ failure: return NULL; } -const char* pa_utf8_valid (const char *str) { +char* pa_utf8_valid (const char *str) { return utf8_validate(str, NULL); } char* pa_utf8_filter (const char *str) { char *new_str; + pa_assert(str); new_str = pa_xnew(char, strlen(str) + 1); - return utf8_validate(str, new_str); } @@ -195,22 +200,24 @@ char* pa_utf8_filter (const char *str) { static char* iconv_simple(const char *str, const char *to, const char *from) { char *new_str; size_t len, inlen; - iconv_t cd; ICONV_CONST char *inbuf; char *outbuf; size_t res, inbytes, outbytes; + pa_assert(str); + pa_assert(to); + pa_assert(from); + cd = iconv_open(to, from); if (cd == (iconv_t)-1) return NULL; inlen = len = strlen(str) + 1; - new_str = pa_xmalloc(len); - assert(new_str); + new_str = pa_xnew(char, len); - while (1) { - inbuf = (ICONV_CONST char*)str; /* Brain dead prototype for iconv() */ + for (;;) { + inbuf = (ICONV_CONST char*) str; /* Brain dead prototype for iconv() */ inbytes = inlen; outbuf = new_str; outbytes = len; @@ -226,11 +233,10 @@ static char* iconv_simple(const char *str, const char *to, const char *from) { break; } - assert(inbytes != 0); + pa_assert(inbytes != 0); len += inbytes; new_str = pa_xrealloc(new_str, len); - assert(new_str); } iconv_close(cd); @@ -249,10 +255,12 @@ char* pa_locale_to_utf8 (const char *str) { #else char* pa_utf8_to_locale (const char *str) { + pa_assert(str); return NULL; } char* pa_locale_to_utf8 (const char *str) { + pa_assert(str); return NULL; } diff --git a/src/pulse/utf8.h b/src/pulse/utf8.h index ff8dc215..1e08047c 100644 --- a/src/pulse/utf8.h +++ b/src/pulse/utf8.h @@ -34,7 +34,7 @@ PA_C_DECL_BEGIN /** Test if the specified strings qualifies as valid UTF8. Return the string if so, otherwise NULL */ -const char *pa_utf8_valid(const char *str); +char *pa_utf8_valid(const char *str) PA_GCC_PURE; /** Filter all invalid UTF8 characters from the specified string, returning a new fully UTF8 valid string. Don't forget to free the returned string with pa_xfree() */ char *pa_utf8_filter(const char *str); diff --git a/src/pulse/util.c b/src/pulse/util.c index d561329c..5dbb670b 100644 --- a/src/pulse/util.c +++ b/src/pulse/util.c @@ -26,7 +26,6 @@ #include #endif -#include #include #include #include @@ -56,20 +55,14 @@ #include #endif -#include "../pulsecore/winsock.h" - +#include #include #include #include +#include #include "util.h" -#ifndef OS_IS_WIN32 -#define PATH_SEP '/' -#else -#define PATH_SEP '\\' -#endif - char *pa_get_user_name(char *s, size_t l) { char *p; char buf[1024]; @@ -78,7 +71,8 @@ char *pa_get_user_name(char *s, size_t l) { struct passwd pw, *r; #endif - assert(s && l > 0); + pa_assert(s); + pa_assert(l > 0); if (!(p = getenv("USER")) && !(p = getenv("LOGNAME")) && !(p = getenv("USERNAME"))) { #ifdef HAVE_PWD_H @@ -90,7 +84,7 @@ char *pa_get_user_name(char *s, size_t l) { * that do not support getpwuid_r. */ if ((r = getpwuid(getuid())) == NULL) { #endif - snprintf(s, l, "%lu", (unsigned long) getuid()); + pa_snprintf(s, l, "%lu", (unsigned long) getuid()); return s; } @@ -113,11 +107,15 @@ char *pa_get_user_name(char *s, size_t l) { } char *pa_get_host_name(char *s, size_t l) { - assert(s && l > 0); + + pa_assert(s); + pa_assert(l > 0); + if (gethostname(s, l) < 0) { pa_log("gethostname(): %s", pa_cstrerror(errno)); return NULL; } + s[l-1] = 0; return s; } @@ -130,7 +128,8 @@ char *pa_get_home_dir(char *s, size_t l) { struct passwd pw, *r; #endif - assert(s && l); + pa_assert(s); + pa_assert(l > 0); if ((e = getenv("HOME"))) return pa_strlcpy(s, e, l); @@ -159,8 +158,8 @@ char *pa_get_home_dir(char *s, size_t l) { char *pa_get_binary_name(char *s, size_t l) { - assert(s); - assert(l); + pa_assert(s); + pa_assert(l > 0); #if defined(OS_IS_WIN32) { @@ -171,7 +170,7 @@ char *pa_get_binary_name(char *s, size_t l) { } #endif -#ifdef HAVE_READLINK +#ifdef __linux__ { int i; char path[PATH_MAX]; @@ -206,13 +205,15 @@ char *pa_get_binary_name(char *s, size_t l) { return NULL; } -const char *pa_path_get_filename(const char *p) { +char *pa_path_get_filename(const char *p) { char *fn; - if ((fn = strrchr(p, PATH_SEP))) + pa_assert(p); + + if ((fn = strrchr(p, PA_PATH_SEP_CHAR))) return fn+1; - return (const char*) p; + return (char*) p; } char *pa_get_fqdn(char *s, size_t l) { @@ -221,6 +222,9 @@ char *pa_get_fqdn(char *s, size_t l) { struct addrinfo *a, hints; #endif + pa_assert(s); + pa_assert(l > 0); + if (!pa_get_host_name(hn, sizeof(hn))) return NULL; diff --git a/src/pulse/util.h b/src/pulse/util.h index 95bd86f3..764678e5 100644 --- a/src/pulse/util.h +++ b/src/pulse/util.h @@ -52,7 +52,7 @@ char *pa_get_binary_name(char *s, size_t l); /** Return a pointer to the filename inside a path (which is the last * component). */ -const char *pa_path_get_filename(const char *p); +char *pa_path_get_filename(const char *p); /** Wait t milliseconds */ int pa_msleep(unsigned long t); diff --git a/src/pulse/volume.c b/src/pulse/volume.c index feb33f07..3688b847 100644 --- a/src/pulse/volume.c +++ b/src/pulse/volume.c @@ -25,16 +25,18 @@ #include #endif -#include #include #include +#include +#include + #include "volume.h" int pa_cvolume_equal(const pa_cvolume *a, const pa_cvolume *b) { int i; - assert(a); - assert(b); + pa_assert(a); + pa_assert(b); if (a->channels != b->channels) return 0; @@ -49,9 +51,9 @@ int pa_cvolume_equal(const pa_cvolume *a, const pa_cvolume *b) { pa_cvolume* pa_cvolume_set(pa_cvolume *a, unsigned channels, pa_volume_t v) { int i; - assert(a); - assert(channels > 0); - assert(channels <= PA_CHANNELS_MAX); + pa_assert(a); + pa_assert(channels > 0); + pa_assert(channels <= PA_CHANNELS_MAX); a->channels = channels; @@ -64,7 +66,7 @@ pa_cvolume* pa_cvolume_set(pa_cvolume *a, unsigned channels, pa_volume_t v) { pa_volume_t pa_cvolume_avg(const pa_cvolume *a) { uint64_t sum = 0; int i; - assert(a); + pa_assert(a); for (i = 0; i < a->channels; i++) sum += a->values[i]; @@ -118,14 +120,14 @@ char *pa_cvolume_snprint(char *s, size_t l, const pa_cvolume *c) { int first = 1; char *e; - assert(s); - assert(l > 0); - assert(c); + pa_assert(s); + pa_assert(l > 0); + pa_assert(c); *(e = s) = 0; for (channel = 0; channel < c->channels && l > 1; channel++) { - l -= snprintf(e, l, "%s%u: %3u%%", + l -= pa_snprintf(e, l, "%s%u: %3u%%", first ? "" : " ", channel, (c->values[channel]*100)/PA_VOLUME_NORM); @@ -140,7 +142,7 @@ char *pa_cvolume_snprint(char *s, size_t l, const pa_cvolume *c) { /** Return non-zero if the volume of all channels is equal to the specified value */ int pa_cvolume_channels_equal_to(const pa_cvolume *a, pa_volume_t v) { unsigned c; - assert(a); + pa_assert(a); for (c = 0; c < a->channels; c++) if (a->values[c] != v) @@ -152,9 +154,9 @@ int pa_cvolume_channels_equal_to(const pa_cvolume *a, pa_volume_t v) { pa_cvolume *pa_sw_cvolume_multiply(pa_cvolume *dest, const pa_cvolume *a, const pa_cvolume *b) { unsigned i; - assert(dest); - assert(a); - assert(b); + pa_assert(dest); + pa_assert(a); + pa_assert(b); for (i = 0; i < a->channels && i < b->channels && i < PA_CHANNELS_MAX; i++) { @@ -169,7 +171,7 @@ pa_cvolume *pa_sw_cvolume_multiply(pa_cvolume *dest, const pa_cvolume *a, const } int pa_cvolume_valid(const pa_cvolume *v) { - assert(v); + pa_assert(v); if (v->channels <= 0 || v->channels > PA_CHANNELS_MAX) return 0; diff --git a/src/pulse/volume.h b/src/pulse/volume.h index a928ff71..22e5b8a4 100644 --- a/src/pulse/volume.h +++ b/src/pulse/volume.h @@ -113,7 +113,7 @@ typedef struct pa_cvolume { } pa_cvolume; /** Return non-zero when *a == *b */ -int pa_cvolume_equal(const pa_cvolume *a, const pa_cvolume *b); +int pa_cvolume_equal(const pa_cvolume *a, const pa_cvolume *b) PA_GCC_PURE; /** Set the volume of all channels to PA_VOLUME_NORM */ #define pa_cvolume_reset(a, n) pa_cvolume_set((a), (n), PA_VOLUME_NORM) @@ -131,13 +131,13 @@ pa_cvolume* pa_cvolume_set(pa_cvolume *a, unsigned channels, pa_volume_t v); char *pa_cvolume_snprint(char *s, size_t l, const pa_cvolume *c); /** Return the average volume of all channels */ -pa_volume_t pa_cvolume_avg(const pa_cvolume *a); +pa_volume_t pa_cvolume_avg(const pa_cvolume *a) PA_GCC_PURE; /** Return TRUE when the passed cvolume structure is valid, FALSE otherwise */ -int pa_cvolume_valid(const pa_cvolume *v); +int pa_cvolume_valid(const pa_cvolume *v) PA_GCC_PURE; /** Return non-zero if the volume of all channels is equal to the specified value */ -int pa_cvolume_channels_equal_to(const pa_cvolume *a, pa_volume_t v); +int pa_cvolume_channels_equal_to(const pa_cvolume *a, pa_volume_t v) PA_GCC_PURE; /** Return 1 if the specified volume has all channels muted */ #define pa_cvolume_is_muted(a) pa_cvolume_channels_equal_to((a), PA_VOLUME_MUTED) @@ -146,22 +146,22 @@ int pa_cvolume_channels_equal_to(const pa_cvolume *a, pa_volume_t v); #define pa_cvolume_is_norm(a) pa_cvolume_channels_equal_to((a), PA_VOLUME_NORM) /** Multiply two volumes specifications, return the result. This uses PA_VOLUME_NORM as neutral element of multiplication. This is only valid for software volumes! */ -pa_volume_t pa_sw_volume_multiply(pa_volume_t a, pa_volume_t b); +pa_volume_t pa_sw_volume_multiply(pa_volume_t a, pa_volume_t b) PA_GCC_CONST; /** Multiply to per-channel volumes and return the result in *dest. This is only valid for software volumes! */ -pa_cvolume *pa_sw_cvolume_multiply(pa_cvolume *dest, const pa_cvolume *a, const pa_cvolume *b); +pa_cvolume *pa_sw_cvolume_multiply(pa_cvolume *dest, const pa_cvolume *a, const pa_cvolume *b) PA_GCC_PURE; /** Convert a decibel value to a volume. This is only valid for software volumes! \since 0.4 */ -pa_volume_t pa_sw_volume_from_dB(double f); +pa_volume_t pa_sw_volume_from_dB(double f) PA_GCC_CONST; /** Convert a volume to a decibel value. This is only valid for software volumes! \since 0.4 */ -double pa_sw_volume_to_dB(pa_volume_t v); +double pa_sw_volume_to_dB(pa_volume_t v) PA_GCC_CONST; /** Convert a linear factor to a volume. This is only valid for software volumes! \since 0.8 */ -pa_volume_t pa_sw_volume_from_linear(double v); +pa_volume_t pa_sw_volume_from_linear(double v) PA_GCC_CONST; /** Convert a volume to a linear factor. This is only valid for software volumes! \since 0.8 */ -double pa_sw_volume_to_linear(pa_volume_t v); +double pa_sw_volume_to_linear(pa_volume_t v) PA_GCC_CONST; #ifdef INFINITY #define PA_DECIBEL_MININFTY (-INFINITY) diff --git a/src/pulse/xmalloc.c b/src/pulse/xmalloc.c index 1f0734c2..5348dda4 100644 --- a/src/pulse/xmalloc.c +++ b/src/pulse/xmalloc.c @@ -27,12 +27,12 @@ #include #include -#include #include #include #include #include +#include #include "xmalloc.h" @@ -60,8 +60,8 @@ static void oom(void) { void* pa_xmalloc(size_t size) { void *p; - assert(size > 0); - assert(size < MAX_ALLOC_SIZE); + pa_assert(size > 0); + pa_assert(size < MAX_ALLOC_SIZE); if (!(p = malloc(size))) oom(); @@ -71,8 +71,8 @@ void* pa_xmalloc(size_t size) { void* pa_xmalloc0(size_t size) { void *p; - assert(size > 0); - assert(size < MAX_ALLOC_SIZE); + pa_assert(size > 0); + pa_assert(size < MAX_ALLOC_SIZE); if (!(p = calloc(1, size))) oom(); @@ -82,8 +82,8 @@ void* pa_xmalloc0(size_t size) { void *pa_xrealloc(void *ptr, size_t size) { void *p; - assert(size > 0); - assert(size < MAX_ALLOC_SIZE); + pa_assert(size > 0); + pa_assert(size < MAX_ALLOC_SIZE); if (!(p = realloc(ptr, size))) oom(); diff --git a/src/pulse/xmalloc.h b/src/pulse/xmalloc.h index 2f6399c5..62a450dc 100644 --- a/src/pulse/xmalloc.h +++ b/src/pulse/xmalloc.h @@ -75,6 +75,15 @@ static inline void* pa_xnew0_internal(unsigned n, size_t k) { /** Same as pa_xnew() but set the memory to zero */ #define pa_xnew0(type, n) ((type*) pa_xnew0_internal((n), sizeof(type))) +/** Internal helper for pa_xnew0() */ +static inline void* pa_xnewdup_internal(const void *p, unsigned n, size_t k) { + assert(n < INT_MAX/k); + return pa_xmemdup(p, n*k); +} + +/** Same as pa_xnew() but set the memory to zero */ +#define pa_xnewdup(type, p, n) ((type*) pa_xnewdup_internal((p), (n), sizeof(type))) + PA_C_DECL_END #endif -- cgit From 27d6b7b4732f3678ecb6f1e5e53d440f1e8b2547 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 29 Oct 2007 15:32:22 +0000 Subject: make use of new pa_readlink() where applicable git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@1975 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/util.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'src/pulse') diff --git a/src/pulse/util.c b/src/pulse/util.c index 5dbb670b..d3ac9f66 100644 --- a/src/pulse/util.c +++ b/src/pulse/util.c @@ -55,6 +55,7 @@ #include #endif +#include #include #include #include @@ -172,13 +173,13 @@ char *pa_get_binary_name(char *s, size_t l) { #ifdef __linux__ { - int i; - char path[PATH_MAX]; + char *rp; /* This works on Linux only */ - if ((i = readlink("/proc/self/exe", path, sizeof(path)-1)) >= 0) { - path[i] = 0; - return pa_strlcpy(s, pa_path_get_filename(path), l); + if ((rp = pa_readlink("/proc/self/exe"))) { + pa_strlcpy(s, pa_path_get_filename(rp), l); + pa_xfree(rp); + return s; } } -- cgit From 33c238b7ef4b6c00f46714f14b3c6e1f29af6fde Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 29 Oct 2007 21:23:08 +0000 Subject: ignore network sinks/sources git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@1988 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/def.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'src/pulse') diff --git a/src/pulse/def.h b/src/pulse/def.h index c2816234..a7c475db 100644 --- a/src/pulse/def.h +++ b/src/pulse/def.h @@ -300,14 +300,16 @@ typedef enum pa_seek_mode { typedef enum pa_sink_flags { PA_SINK_HW_VOLUME_CTRL = 1, /**< Supports hardware volume control */ PA_SINK_LATENCY = 2, /**< Supports latency querying */ - PA_SINK_HARDWARE = 4 /**< Is a hardware sink of some kind, in contrast to "virtual"/software sinks \since 0.9.3 */ + PA_SINK_HARDWARE = 4, /**< Is a hardware sink of some kind, in contrast to "virtual"/software sinks \since 0.9.3 */ + PA_SINK_NETWORK = 8 /**< Is a networked sink of some kind. \since 0.9.7 */ } pa_sink_flags_t; /** Special source flags. \since 0.8 */ typedef enum pa_source_flags { PA_SOURCE_HW_VOLUME_CTRL = 1, /**< Supports hardware volume control */ PA_SOURCE_LATENCY = 2, /**< Supports latency querying */ - PA_SOURCE_HARDWARE = 4 /**< Is a hardware source of some kind, in contrast to "virtual"/software source \since 0.9.3 */ + PA_SOURCE_HARDWARE = 4, /**< Is a hardware source of some kind, in contrast to "virtual"/software source \since 0.9.3 */ + PA_SOURCE_NETWORK = 8 /**< Is a networked sink of some kind. \since 0.9.7 */ } pa_source_flags_t; /** A generic free() like callback prototype */ -- cgit From b03b5741ea689b3bb08b7fef9cc905cae3a5ad99 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 30 Oct 2007 14:17:41 +0000 Subject: rename 'length' parameters in the API to 'bytes', to make their unit clear git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@1999 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/simple.h | 4 ++-- src/pulse/stream.h | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) (limited to 'src/pulse') diff --git a/src/pulse/simple.h b/src/pulse/simple.h index f76c1d67..0ddd57e0 100644 --- a/src/pulse/simple.h +++ b/src/pulse/simple.h @@ -130,13 +130,13 @@ pa_simple* pa_simple_new( void pa_simple_free(pa_simple *s); /** Write some data to the server */ -int pa_simple_write(pa_simple *s, const void*data, size_t length, int *error); +int pa_simple_write(pa_simple *s, const void*data, size_t bytes, int *error); /** Wait until all data already written is played by the daemon */ int pa_simple_drain(pa_simple *s, int *error); /** Read some data from the server */ -int pa_simple_read(pa_simple *s, void*data, size_t length, int *error); +int pa_simple_read(pa_simple *s, void*data, size_t bytes, int *error); /** Return the playback latency. \since 0.5 */ pa_usec_t pa_simple_get_latency(pa_simple *s, int *error); diff --git a/src/pulse/stream.h b/src/pulse/stream.h index 65603262..8c6a90c1 100644 --- a/src/pulse/stream.h +++ b/src/pulse/stream.h @@ -271,7 +271,7 @@ typedef struct pa_stream pa_stream; typedef void (*pa_stream_success_cb_t) (pa_stream*s, int success, void *userdata); /** A generic request callback */ -typedef void (*pa_stream_request_cb_t)(pa_stream *p, size_t length, void *userdata); +typedef void (*pa_stream_request_cb_t)(pa_stream *p, size_t bytes, void *userdata); /** A generic notification callback */ typedef void (*pa_stream_notify_cb_t)(pa_stream *p, void *userdata); @@ -327,7 +327,7 @@ int pa_stream_disconnect(pa_stream *s); int pa_stream_write( pa_stream *p /**< The stream to use */, const void *data /**< The data to write */, - size_t length /**< The length of the data to write */, + size_t bytes /**< The length of the data to write in bytes*/, pa_free_cb_t free_cb /**< A cleanup routine for the data or NULL to request an internal copy */, int64_t offset, /**< Offset for seeking, must be 0 for upload streams */ pa_seek_mode_t seek /**< Seek mode, must be PA_SEEK_RELATIVE for upload streams */); @@ -340,16 +340,16 @@ int pa_stream_write( int pa_stream_peek( pa_stream *p /**< The stream to use */, const void **data /**< Pointer to pointer that will point to data */, - size_t *length /**< The length of the data read */); + size_t *bytes /**< The length of the data read in bytes */); /** Remove the current fragment on record streams. It is invalid to do this without first * calling pa_stream_peek(). \since 0.8 */ int pa_stream_drop(pa_stream *p); -/** Return the nember of bytes that may be written using pa_stream_write() */ +/** Return the nember of bytes that may be written using pa_stream_write(), in bytes */ size_t pa_stream_writable_size(pa_stream *p); -/** Return the number of bytes that may be read using pa_stream_read() \since 0.8 */ +/** Return the number of bytes that may be read using pa_stream_read(), in bytes \since 0.8 */ size_t pa_stream_readable_size(pa_stream *p); /** Drain a playback stream. Use this for notification when the buffer is empty */ -- cgit From 5058a1e9ed700264df78409e0d629fb18a0263ef Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 30 Oct 2007 18:35:08 +0000 Subject: save and restore errno in the sig handler git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@2004 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/mainloop-signal.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'src/pulse') diff --git a/src/pulse/mainloop-signal.c b/src/pulse/mainloop-signal.c index 7d3017e2..e41ed14c 100644 --- a/src/pulse/mainloop-signal.c +++ b/src/pulse/mainloop-signal.c @@ -67,10 +67,16 @@ static pa_io_event* io_event = NULL; static pa_signal_event *signals = NULL; static void signal_handler(int sig) { + int saved_errno; + + saved_errno = errno; + #ifndef HAVE_SIGACTION signal(sig, signal_handler); #endif pa_write(signal_pipe[1], &sig, sizeof(sig), NULL); + + errno = saved_errno; } static void dispatch(pa_mainloop_api*a, int sig) { -- cgit From e706f7bed759165573c9ec7e5e4f79a2f9b74228 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 1 Nov 2007 00:33:14 +0000 Subject: pa_boolize the client config git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@2009 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/client-conf.c | 11 +++++------ src/pulse/client-conf.h | 4 ++-- 2 files changed, 7 insertions(+), 8 deletions(-) (limited to 'src/pulse') diff --git a/src/pulse/client-conf.c b/src/pulse/client-conf.c index abd277a6..c054f663 100644 --- a/src/pulse/client-conf.c +++ b/src/pulse/client-conf.c @@ -58,10 +58,10 @@ static const pa_client_conf default_conf = { .default_sink = NULL, .default_source = NULL, .default_server = NULL, - .autospawn = 0, - .disable_shm = 0, + .autospawn = FALSE, + .disable_shm = FALSE, .cookie_file = NULL, - .cookie_valid = 0, + .cookie_valid = FALSE, }; pa_client_conf *pa_client_conf_new(void) { @@ -172,7 +172,7 @@ int pa_client_conf_env(pa_client_conf *c) { int pa_client_conf_load_cookie(pa_client_conf* c) { pa_assert(c); - c->cookie_valid = 0; + c->cookie_valid = FALSE; if (!c->cookie_file) return -1; @@ -180,7 +180,6 @@ int pa_client_conf_load_cookie(pa_client_conf* c) { if (pa_authkey_load_auto(c->cookie_file, c->cookie, sizeof(c->cookie)) < 0) return -1; - c->cookie_valid = 1; + c->cookie_valid = TRUE; return 0; } - diff --git a/src/pulse/client-conf.h b/src/pulse/client-conf.h index 6de64582..7cc975e6 100644 --- a/src/pulse/client-conf.h +++ b/src/pulse/client-conf.h @@ -30,9 +30,9 @@ typedef struct pa_client_conf { char *daemon_binary, *extra_arguments, *default_sink, *default_source, *default_server, *cookie_file; - int autospawn, disable_shm; + pa_bool_t autospawn, disable_shm; uint8_t cookie[PA_NATIVE_COOKIE_LENGTH]; - int cookie_valid; /* non-zero, when cookie is valid */ + pa_bool_t cookie_valid; /* non-zero, when cookie is valid */ } pa_client_conf; /* Create a new configuration data object and reset it to defaults */ -- cgit From a2121d5e6f33b31481a014012b7eef43b8e500db Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 8 Nov 2007 22:31:30 +0000 Subject: strip most comments from the default configuration files, since the man page is now more elaborate and we don't want to maintain those docs redundantly at two places git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@2030 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/client.conf.in | 29 +++++++++-------------------- 1 file changed, 9 insertions(+), 20 deletions(-) (limited to 'src/pulse') diff --git a/src/pulse/client.conf.in b/src/pulse/client.conf.in index 3cfd9760..2bc8a7c8 100644 --- a/src/pulse/client.conf.in +++ b/src/pulse/client.conf.in @@ -17,29 +17,18 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # USA. -## Configuration file for pulseaudio clients. Default values are -## commented out. Use either ; or # for commenting +## Configuration file for PulseAudio clients. See pulse-client.conf(5) for +## more information. Default values a commented out. Use either ; or # for +## commenting. -## Path to the pulseaudio daemon to run when autospawning. -; daemon-binary = @PA_BINARY@ - -## Extra arguments to pass to the pulseaudio daemon -; extra-arguments = --log-target=syslog --exit-idle-time=5 - -## The default sink to connect to -; default-sink = - -## The default source to connect to +; default-sink = ; default-source = - -## The default sever to connect to ; default-server = -## Autospawn daemons? -; autospawn = 0 +; autospawn = no +; daemon-binary = @PA_BINARY@ +; extra-arguments = --log-target=syslog --exit-idle-time=5 -### Cookie file -; cookie-file = +; cookie-file = -### Disable shared memory data transfer -; disable-shm = 0 +; disable-shm = no -- cgit From 7e0f547f2fd8ddfae1d807334dbc3428a3dfe374 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 9 Nov 2007 02:45:07 +0000 Subject: add support for 32bit integer samples git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@2037 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/sample.c | 16 ++++++++++++++-- src/pulse/sample.h | 18 +++++++++++++++--- 2 files changed, 29 insertions(+), 5 deletions(-) (limited to 'src/pulse') diff --git a/src/pulse/sample.c b/src/pulse/sample.c index ae2a0b9f..27c0df03 100644 --- a/src/pulse/sample.c +++ b/src/pulse/sample.c @@ -44,7 +44,9 @@ size_t pa_sample_size(const pa_sample_spec *spec) { [PA_SAMPLE_S16LE] = 2, [PA_SAMPLE_S16BE] = 2, [PA_SAMPLE_FLOAT32LE] = 4, - [PA_SAMPLE_FLOAT32BE] = 4 + [PA_SAMPLE_FLOAT32BE] = 4, + [PA_SAMPLE_S32LE] = 4, + [PA_SAMPLE_S32BE] = 4, }; pa_assert(spec); @@ -107,6 +109,8 @@ const char *pa_sample_format_to_string(pa_sample_format_t f) { [PA_SAMPLE_S16BE] = "s16be", [PA_SAMPLE_FLOAT32LE] = "float32le", [PA_SAMPLE_FLOAT32BE] = "float32be", + [PA_SAMPLE_S32LE] = "s32le", + [PA_SAMPLE_S32BE] = "s32be", }; if (f < 0 || f >= PA_SAMPLE_MAX) @@ -156,7 +160,7 @@ pa_sample_format_t pa_parse_sample_format(const char *format) { return PA_SAMPLE_S16RE; else if (strcasecmp(format, "u8") == 0 || strcasecmp(format, "8") == 0) return PA_SAMPLE_U8; - else if (strcasecmp(format, "float32") == 0 || strcasecmp(format, "float32ne") == 0) + else if (strcasecmp(format, "float32") == 0 || strcasecmp(format, "float32ne") == 0 || strcasecmp(format, "float") == 0) return PA_SAMPLE_FLOAT32NE; else if (strcasecmp(format, "float32re") == 0) return PA_SAMPLE_FLOAT32RE; @@ -168,6 +172,14 @@ pa_sample_format_t pa_parse_sample_format(const char *format) { return PA_SAMPLE_ULAW; else if (strcasecmp(format, "alaw") == 0) return PA_SAMPLE_ALAW; + else if (strcasecmp(format, "s32le") == 0) + return PA_SAMPLE_S32LE; + else if (strcasecmp(format, "s32be") == 0) + return PA_SAMPLE_S32BE; + else if (strcasecmp(format, "s32ne") == 0 || strcasecmp(format, "s32") == 0 || strcasecmp(format, "32") == 0) + return PA_SAMPLE_S32NE; + else if (strcasecmp(format, "s32re") == 0) + return PA_SAMPLE_S32RE; return -1; } diff --git a/src/pulse/sample.h b/src/pulse/sample.h index b307621e..2c13f57d 100644 --- a/src/pulse/sample.h +++ b/src/pulse/sample.h @@ -42,13 +42,15 @@ * * PulseAudio supports the following sample formats: * - * \li PA_SAMPLE_U8 - Unsigned 8 bit PCM. - * \li PA_SAMPLE_S16LE - Signed 16 bit PCM, little endian. - * \li PA_SAMPLE_S16BE - Signed 16 bit PCM, big endian. + * \li PA_SAMPLE_U8 - Unsigned 8 bit integer PCM. + * \li PA_SAMPLE_S16LE - Signed 16 integer bit PCM, little endian. + * \li PA_SAMPLE_S16BE - Signed 16 integer bit PCM, big endian. * \li PA_SAMPLE_FLOAT32LE - 32 bit IEEE floating point PCM, little endian. * \li PA_SAMPLE_FLOAT32BE - 32 bit IEEE floating point PCM, big endian. * \li PA_SAMPLE_ALAW - 8 bit a-Law. * \li PA_SAMPLE_ULAW - 8 bit mu-Law. + * \li PA_SAMPLE_S32LE - Signed 32 bit integer PCM, little endian. + * \li PA_SAMPLE_S32BE - Signed 32 bit integer PCM, big endian. * * The floating point sample formats have the range from -1 to 1. * @@ -117,6 +119,8 @@ typedef enum pa_sample_format { PA_SAMPLE_S16BE, /**< Signed 16 Bit PCM, big endian */ PA_SAMPLE_FLOAT32LE, /**< 32 Bit IEEE floating point, little endian, range -1 to 1 */ PA_SAMPLE_FLOAT32BE, /**< 32 Bit IEEE floating point, big endian, range -1 to 1 */ + PA_SAMPLE_S32LE, /**< Signed 32 Bit PCM, little endian (PC) */ + PA_SAMPLE_S32BE, /**< Signed 32 Bit PCM, big endian (PC) */ PA_SAMPLE_MAX, /**< Upper limit of valid sample types */ PA_SAMPLE_INVALID = -1 /**< An invalid value */ } pa_sample_format_t; @@ -126,19 +130,27 @@ typedef enum pa_sample_format { #define PA_SAMPLE_S16NE PA_SAMPLE_S16BE /** 32 Bit IEEE floating point, native endian */ #define PA_SAMPLE_FLOAT32NE PA_SAMPLE_FLOAT32BE +/** Signed 32 Bit PCM, native endian */ +#define PA_SAMPLE_S32NE PA_SAMPLE_S32BE /** Signed 16 Bit PCM reverse endian */ #define PA_SAMPLE_S16RE PA_SAMPLE_S16LE /** 32 Bit IEEE floating point, reverse endian */ #define PA_SAMPLE_FLOAT32RE PA_SAMPLE_FLOAT32LE +/** Signed 32 Bit PCM reverse endian */ +#define PA_SAMPLE_S32RE PA_SAMPLE_S32LE #else /** Signed 16 Bit PCM, native endian */ #define PA_SAMPLE_S16NE PA_SAMPLE_S16LE /** 32 Bit IEEE floating point, native endian */ #define PA_SAMPLE_FLOAT32NE PA_SAMPLE_FLOAT32LE +/** Signed 32 Bit PCM, native endian */ +#define PA_SAMPLE_S32NE PA_SAMPLE_S32LE /** Signed 16 Bit PCM reverse endian */ #define PA_SAMPLE_S16RE PA_SAMPLE_S16BE /** 32 Bit IEEE floating point, reverse endian */ #define PA_SAMPLE_FLOAT32RE PA_SAMPLE_FLOAT32BE +/** Signed 32 Bit PCM reverse endian */ +#define PA_SAMPLE_S32RE PA_SAMPLE_S32BE #endif /** A Shortcut for PA_SAMPLE_FLOAT32NE */ -- cgit From d8e0c1c6bdb4fbfcc69d080ca78d76403f9e8760 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 9 Nov 2007 18:24:09 +0000 Subject: minor typo git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@2042 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/sample.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/pulse') diff --git a/src/pulse/sample.h b/src/pulse/sample.h index 2c13f57d..8f9f1733 100644 --- a/src/pulse/sample.h +++ b/src/pulse/sample.h @@ -50,7 +50,7 @@ * \li PA_SAMPLE_ALAW - 8 bit a-Law. * \li PA_SAMPLE_ULAW - 8 bit mu-Law. * \li PA_SAMPLE_S32LE - Signed 32 bit integer PCM, little endian. - * \li PA_SAMPLE_S32BE - Signed 32 bit integer PCM, big endian. + * \li PA_SAMPLE_S32BE - Signed 32 bit integer PCM, big endian. * * The floating point sample formats have the range from -1 to 1. * -- cgit From 4c4761731df479a65f38ef0f4e9a079cd789ad3f Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 13 Nov 2007 23:41:17 +0000 Subject: add array size to increase chance of detecting missing updates git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@2054 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/channelmap.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'src/pulse') diff --git a/src/pulse/channelmap.c b/src/pulse/channelmap.c index 2b8ef2b0..2b35ee75 100644 --- a/src/pulse/channelmap.c +++ b/src/pulse/channelmap.c @@ -36,7 +36,7 @@ #include "channelmap.h" -const char *const table[] = { +const char *const table[PA_CHANNEL_POSITION_MAX] = { [PA_CHANNEL_POSITION_MONO] = "mono", [PA_CHANNEL_POSITION_FRONT_CENTER] = "front-center", @@ -99,7 +99,7 @@ const char *const table[] = { [PA_CHANNEL_POSITION_TOP_REAR_RIGHT] = "top-rear-right" }; -const char *const pretty_table[] = { +const char *const pretty_table[PA_CHANNEL_POSITION_MAX] = { [PA_CHANNEL_POSITION_MONO] = "Mono", [PA_CHANNEL_POSITION_FRONT_CENTER] = "Front Center", @@ -531,4 +531,3 @@ int pa_channel_map_valid(const pa_channel_map *map) { return 1; } - -- cgit From 461e36910a20a5cfa7ed333b718b705f9ec9d0fd Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 14 Nov 2007 16:11:09 +0000 Subject: use a free list for allocation pa_operation objects git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@2058 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/operation.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'src/pulse') diff --git a/src/pulse/operation.c b/src/pulse/operation.c index ed5eb4aa..8a782fd1 100644 --- a/src/pulse/operation.c +++ b/src/pulse/operation.c @@ -27,15 +27,20 @@ #include #include +#include #include "internal.h" #include "operation.h" +PA_STATIC_FLIST_DECLARE(operations, 0, pa_xfree); + pa_operation *pa_operation_new(pa_context *c, pa_stream *s, pa_operation_cb_t cb, void *userdata) { pa_operation *o; pa_assert(c); - o = pa_xnew(pa_operation, 1); + if (!(o = pa_flist_pop(PA_STATIC_FLIST_GET(operations)))) + o = pa_xnew(pa_operation, 1); + PA_REFCNT_INIT(o); o->context = c; o->stream = s; @@ -66,7 +71,9 @@ void pa_operation_unref(pa_operation *o) { if (PA_REFCNT_DEC(o) <= 0) { pa_assert(!o->context); pa_assert(!o->stream); - pa_xfree(o); + + if (pa_flist_push(PA_STATIC_FLIST_GET(operations), o) < 0) + pa_xfree(o); } } -- cgit From 413a8f8917256ef44a58fc8519bcc93644423373 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 14 Nov 2007 16:11:51 +0000 Subject: use a prio inheriting mutex for the threaded mainloop, to ease writing of RT clients git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@2059 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/thread-mainloop.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/pulse') diff --git a/src/pulse/thread-mainloop.c b/src/pulse/thread-mainloop.c index 9dd47ae3..e8c956bb 100644 --- a/src/pulse/thread-mainloop.c +++ b/src/pulse/thread-mainloop.c @@ -103,7 +103,7 @@ pa_threaded_mainloop *pa_threaded_mainloop_new(void) { return NULL; } - m->mutex = pa_mutex_new(TRUE, FALSE); + m->mutex = pa_mutex_new(TRUE, TRUE); m->cond = pa_cond_new(); m->accept_cond = pa_cond_new(); m->thread = NULL; -- cgit From 14a9b80afbb0bddc216462b72156f14e032e1b5e Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 21 Nov 2007 01:30:40 +0000 Subject: - Check process name when dealing with PID files - Add new PA_STREAM_FIX_CHANNELS, FIX_RATE, FIX_FORMAT, DONT_MOVE, VARIABLE_RATES to pa_sream_flags_t adn implement it - Expose those flags in pacat - Add notifications about device suspend/resume to the protocol and expose them in libpulse - Allow changing of buffer_attr during playback - allow disabling for remixing globally - hookup polkit support git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@2067 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/context.c | 9 +- src/pulse/def.h | 79 ++++++++- src/pulse/internal.h | 16 +- src/pulse/operation.c | 2 +- src/pulse/stream.c | 436 ++++++++++++++++++++++++++++++++++++++++++++++++-- src/pulse/stream.h | 71 +++++++- 6 files changed, 593 insertions(+), 20 deletions(-) (limited to 'src/pulse') diff --git a/src/pulse/context.c b/src/pulse/context.c index 805cd44e..bc960e21 100644 --- a/src/pulse/context.c +++ b/src/pulse/context.c @@ -86,6 +86,10 @@ static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = { [PA_COMMAND_UNDERFLOW] = pa_command_overflow_or_underflow, [PA_COMMAND_PLAYBACK_STREAM_KILLED] = pa_command_stream_killed, [PA_COMMAND_RECORD_STREAM_KILLED] = pa_command_stream_killed, + [PA_COMMAND_PLAYBACK_STREAM_MOVED] = pa_command_stream_moved, + [PA_COMMAND_RECORD_STREAM_MOVED] = pa_command_stream_moved, + [PA_COMMAND_PLAYBACK_STREAM_SUSPENDED] = pa_command_stream_suspended, + [PA_COMMAND_RECORD_STREAM_SUSPENDED] = pa_command_stream_suspended, [PA_COMMAND_SUBSCRIBE_EVENT] = pa_command_subscribe_event }; @@ -396,7 +400,7 @@ static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t /* Enable shared memory support if possible */ if (c->version >= 10 && pa_mempool_is_shared(c->mempool) && - c->is_local) { + c->is_local > 0) { /* Only enable SHM if both sides are owned by the same * user. This is a security measure because otherwise @@ -965,6 +969,9 @@ pa_operation* pa_context_set_default_source(pa_context *c, const char *name, pa_ int pa_context_is_local(pa_context *c) { pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); + + PA_CHECK_VALIDITY(c, c->is_local >= 0, PA_ERR_BADSTATE); return c->is_local; } diff --git a/src/pulse/def.h b/src/pulse/def.h index a7c475db..dabbc5eb 100644 --- a/src/pulse/def.h +++ b/src/pulse/def.h @@ -122,7 +122,7 @@ typedef enum pa_stream_flags { * ahead can be corrected * quickly, without the need to * wait. */ - PA_STREAM_AUTO_TIMING_UPDATE = 8 /**< If set timing update requests + PA_STREAM_AUTO_TIMING_UPDATE = 8, /**< If set timing update requests * are issued periodically * automatically. Combined with * PA_STREAM_INTERPOLATE_TIMING @@ -132,6 +132,83 @@ typedef enum pa_stream_flags { * pa_stream_get_latency() at * all times without a packet * round trip.*/ + PA_STREAM_NO_REMAP_CHANNELS = 16, /**< Don't remap channels by + * their name, instead map them + * simply by their + * index. Implies + * PA_STREAM_NO_REMIX_CHANNELS. Only + * supported when the server is + * at least PA 0.9.8. It is + * ignored on older + * servers.\since 0.9.8 */ + PA_STREAM_NO_REMIX_CHANNELS = 32, /**< When remapping channels by + * name, don't upmix or downmix + * them to related + * channels. Copy them into + * matching channels of the + * device 1:1. Only supported + * when the server is at least + * PA 0.9.8. It is ignored on + * older servers. \since + * 0.9.8 */ + PA_STREAM_FIX_FORMAT = 64, /**< Use the sample format of the + * sink/device this stream is being + * connected to, and possibly ignore + * the format the sample spec contains + * -- but you still have to pass a + * valid value in it as a hint to + * PulseAudio what would suit your + * stream best. If this is used you + * should query the used sample format + * after creating the stream by using + * pa_stream_get_sample_spec(). Also, + * if you specified manual buffer + * metrics it is recommended to update + * them with + * pa_stream_set_buffer_attr() to + * compensate for the changed frame + * sizes. Only supported when the + * server is at least PA 0.9.8. It is + * ignored on older servers. \since + * 0.9.8 */ + + PA_STREAM_FIX_RATE = 128, /**< Use the sample rate of the sink, + * and possibly ignore the rate the + * sample spec contains. Usage similar + * to PA_STREAM_FIX_FORMAT.Only + * supported when the server is at least + * PA 0.9.8. It is ignored on older + * servers. \since 0.9.8 */ + + PA_STREAM_FIX_CHANNELS = 256, /**< Use the number of channels and + * the channel map of the sink, and + * possibly ignore the number of + * channels and the map the sample spec + * and the passed channel map + * contains. Usage similar to + * PA_STREAM_FIX_FORMAT. Only supported + * when the server is at least PA + * 0.9.8. It is ignored on older + * servers. \since 0.9.8 */ + PA_STREAM_DONT_MOVE = 512, /**< Don't allow moving of this stream to + * another sink/device. Useful if you use + * any of the PA_STREAM_FIX_ flags and + * want to make sure that resampling + * never takes place -- which might + * happen if the stream is moved to + * another sink/source whith a different + * sample spec/channel map. Only + * supported when the server is at least + * PA 0.9.8. It is ignored on older + * servers. \since 0.9.8 */ + PA_STREAM_VARIABLE_RATE = 1024, /**< Allow dynamic changing of the + * sampling rate during playback + * with + * pa_stream_update_sample_rate(). Only + * supported when the server is at + * least PA 0.9.8. It is ignored + * on older servers. \since + * 0.9.8 */ } pa_stream_flags_t; /** Playback and record buffer metrics */ diff --git a/src/pulse/internal.h b/src/pulse/internal.h index 95593adb..873f1363 100644 --- a/src/pulse/internal.h +++ b/src/pulse/internal.h @@ -103,6 +103,7 @@ struct pa_stream { PA_LLIST_FIELDS(pa_stream); char *name; + pa_bool_t manual_buffer_attr; pa_buffer_attr buffer_attr; pa_sample_spec sample_spec; pa_channel_map channel_map; @@ -110,12 +111,17 @@ struct pa_stream { uint32_t channel; uint32_t syncid; int channel_valid; - uint32_t device_index; + uint32_t stream_index; pa_stream_direction_t direction; pa_stream_state_t state; + pa_bool_t buffer_attr_not_ready, timing_info_not_ready; uint32_t requested_bytes; + uint32_t device_index; + char *device_name; + pa_bool_t suspended; + pa_memchunk peek_memchunk; void *peek_data; pa_memblockq *record_memblockq; @@ -157,6 +163,10 @@ struct pa_stream { void *underflow_userdata; pa_stream_notify_cb_t latency_update_callback; void *latency_update_userdata; + pa_stream_notify_cb_t moved_callback; + void *moved_userdata; + pa_stream_notify_cb_t suspended_callback; + void *suspended_userdata; }; typedef void (*pa_operation_cb_t)(void); @@ -172,12 +182,16 @@ struct pa_operation { pa_operation_state_t state; void *userdata; pa_operation_cb_t callback; + + void *private; /* some operations might need this */ }; void pa_command_request(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); void pa_command_stream_killed(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); void pa_command_subscribe_event(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); void pa_command_overflow_or_underflow(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); +void pa_command_stream_suspended(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); +void pa_command_stream_moved(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); pa_operation *pa_operation_new(pa_context *c, pa_stream *s, pa_operation_cb_t callback, void *userdata); void pa_operation_done(pa_operation *o); diff --git a/src/pulse/operation.c b/src/pulse/operation.c index 8a782fd1..5d2da5b8 100644 --- a/src/pulse/operation.c +++ b/src/pulse/operation.c @@ -44,6 +44,7 @@ pa_operation *pa_operation_new(pa_context *c, pa_stream *s, pa_operation_cb_t cb PA_REFCNT_INIT(o); o->context = c; o->stream = s; + o->private = NULL; o->state = PA_OPERATION_RUNNING; o->callback = cb; @@ -63,7 +64,6 @@ pa_operation *pa_operation_ref(pa_operation *o) { PA_REFCNT_INC(o); return o; } - void pa_operation_unref(pa_operation *o) { pa_assert(o); pa_assert(PA_REFCNT_VALUE(o) >= 1); diff --git a/src/pulse/stream.c b/src/pulse/stream.c index 47906a5c..92420825 100644 --- a/src/pulse/stream.c +++ b/src/pulse/stream.c @@ -51,6 +51,7 @@ pa_stream *pa_stream_new(pa_context *c, const char *name, const pa_sample_spec * pa_assert(PA_REFCNT_VALUE(c) >= 1); PA_CHECK_VALIDITY_RETURN_NULL(c, ss && pa_sample_spec_valid(ss), PA_ERR_INVALID); + PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 12 || (ss->format != PA_SAMPLE_S32LE || ss->format != PA_SAMPLE_S32NE), PA_ERR_NOTSUPPORTED); PA_CHECK_VALIDITY_RETURN_NULL(c, !map || (pa_channel_map_valid(map) && map->channels == ss->channels), PA_ERR_INVALID); s = pa_xnew(pa_stream, 1); @@ -58,6 +59,8 @@ pa_stream *pa_stream_new(pa_context *c, const char *name, const pa_sample_spec * s->context = c; s->mainloop = c->mainloop; + s->buffer_attr_not_ready = s->timing_info_not_ready = FALSE; + s->read_callback = NULL; s->read_userdata = NULL; s->write_callback = NULL; @@ -70,6 +73,10 @@ pa_stream *pa_stream_new(pa_context *c, const char *name, const pa_sample_spec * s->underflow_userdata = NULL; s->latency_update_callback = NULL; s->latency_update_userdata = NULL; + s->moved_callback = NULL; + s->moved_userdata = NULL; + s->suspended_callback = NULL; + s->suspended_userdata = NULL; s->direction = PA_STREAM_NODIRECTION; s->name = pa_xstrdup(name); @@ -84,11 +91,17 @@ pa_stream *pa_stream_new(pa_context *c, const char *name, const pa_sample_spec * s->channel = 0; s->channel_valid = 0; s->syncid = c->csyncid++; - s->device_index = PA_INVALID_INDEX; + s->stream_index = PA_INVALID_INDEX; s->requested_bytes = 0; s->state = PA_STREAM_UNCONNECTED; + + s->manual_buffer_attr = FALSE; memset(&s->buffer_attr, 0, sizeof(s->buffer_attr)); + s->device_index = PA_INVALID_INDEX; + s->device_name = NULL; + s->suspended = FALSE; + s->peek_memchunk.index = 0; s->peek_memchunk.length = 0; s->peek_memchunk.memblock = NULL; @@ -139,6 +152,7 @@ static void stream_free(pa_stream *s) { pa_memblockq_free(s->record_memblockq); pa_xfree(s->name); + pa_xfree(s->device_name); pa_xfree(s); } @@ -178,7 +192,7 @@ uint32_t pa_stream_get_index(pa_stream *s) { PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE, PA_INVALID_INDEX); - return s->device_index; + return s->stream_index; } void pa_stream_set_state(pa_stream *s, pa_stream_state_t st) { @@ -262,6 +276,95 @@ finish: pa_context_unref(c); } +void pa_command_stream_moved(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { + pa_context *c = userdata; + pa_stream *s; + uint32_t channel; + const char *dn; + int suspended; + uint32_t di; + + pa_assert(pd); + pa_assert(command == PA_COMMAND_PLAYBACK_STREAM_MOVED || command == PA_COMMAND_RECORD_STREAM_MOVED); + pa_assert(t); + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); + + pa_context_ref(c); + + if (c->version < 12) { + pa_context_fail(c, PA_ERR_PROTOCOL); + goto finish; + } + + if (pa_tagstruct_getu32(t, &channel) < 0 || + pa_tagstruct_getu32(t, &di) < 0 || + pa_tagstruct_gets(t, &dn) < 0 || + pa_tagstruct_get_boolean(t, &suspended) < 0 || + !pa_tagstruct_eof(t)) { + pa_context_fail(c, PA_ERR_PROTOCOL); + goto finish; + } + + if (!dn || di == PA_INVALID_INDEX) { + pa_context_fail(c, PA_ERR_PROTOCOL); + goto finish; + } + + if (!(s = pa_dynarray_get(command == PA_COMMAND_PLAYBACK_STREAM_MOVED ? c->playback_streams : c->record_streams, channel))) + goto finish; + + pa_xfree(s->device_name); + s->device_name = pa_xstrdup(dn); + s->device_index = di; + + s->suspended = suspended; + + if (s->moved_callback) + s->moved_callback(s, s->moved_userdata); + +finish: + pa_context_unref(c); +} + +void pa_command_stream_suspended(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { + pa_context *c = userdata; + pa_stream *s; + uint32_t channel; + int suspended; + + pa_assert(pd); + pa_assert(command == PA_COMMAND_PLAYBACK_STREAM_SUSPENDED || command == PA_COMMAND_RECORD_STREAM_SUSPENDED); + pa_assert(t); + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); + + pa_context_ref(c); + + if (c->version < 12) { + pa_context_fail(c, PA_ERR_PROTOCOL); + goto finish; + } + + if (pa_tagstruct_getu32(t, &channel) < 0 || + pa_tagstruct_get_boolean(t, &suspended) < 0 || + !pa_tagstruct_eof(t)) { + pa_context_fail(c, PA_ERR_PROTOCOL); + goto finish; + } + + if (!(s = pa_dynarray_get(command == PA_COMMAND_PLAYBACK_STREAM_SUSPENDED ? c->playback_streams : c->record_streams, channel))) + goto finish; + + s->suspended = suspended; + + if (s->suspended_callback) + s->suspended_callback(s, s->suspended_userdata); + +finish: + pa_context_unref(c); +} + void pa_command_request(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { pa_stream *s; pa_context *c = userdata; @@ -412,6 +515,9 @@ static void create_stream_complete(pa_stream *s) { pa_assert(PA_REFCNT_VALUE(s) >= 1); pa_assert(s->state == PA_STREAM_CREATING); + if (s->buffer_attr_not_ready || s->timing_info_not_ready) + return; + pa_stream_set_state(s, PA_STREAM_READY); if (s->requested_bytes > 0 && s->write_callback) @@ -426,6 +532,17 @@ static void create_stream_complete(pa_stream *s) { } } +static void automatic_buffer_attr(pa_buffer_attr *attr, pa_sample_spec *ss) { + pa_assert(attr); + pa_assert(ss); + + attr->tlength = pa_bytes_per_second(ss)/2; + attr->maxlength = (attr->tlength*3)/2; + attr->minreq = attr->tlength/50; + attr->prebuf = attr->tlength - attr->minreq; + attr->fragsize = attr->tlength/50; +} + void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { pa_stream *s = userdata; @@ -445,13 +562,13 @@ void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED } if (pa_tagstruct_getu32(t, &s->channel) < 0 || - ((s->direction != PA_STREAM_UPLOAD) && pa_tagstruct_getu32(t, &s->device_index) < 0) || + ((s->direction != PA_STREAM_UPLOAD) && pa_tagstruct_getu32(t, &s->stream_index) < 0) || ((s->direction != PA_STREAM_RECORD) && pa_tagstruct_getu32(t, &s->requested_bytes) < 0)) { pa_context_fail(s->context, PA_ERR_PROTOCOL); goto finish; } - if (pa_context_get_server_protocol_version(s->context) >= 9) { + if (s->context->version >= 9) { if (s->direction == PA_STREAM_PLAYBACK) { if (pa_tagstruct_getu32(t, &s->buffer_attr.maxlength) < 0 || pa_tagstruct_getu32(t, &s->buffer_attr.tlength) < 0 || @@ -469,6 +586,58 @@ void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED } } + if (s->context->version >= 12) { + pa_sample_spec ss; + pa_channel_map cm; + const char *dn = NULL; + int suspended; + + if (pa_tagstruct_get_sample_spec(t, &ss) < 0 || + pa_tagstruct_get_channel_map(t, &cm) < 0 || + pa_tagstruct_getu32(t, &s->device_index) < 0 || + pa_tagstruct_gets(t, &dn) < 0 || + pa_tagstruct_get_boolean(t, &suspended) < 0) { + pa_context_fail(s->context, PA_ERR_PROTOCOL); + goto finish; + } + + if (!dn || s->device_index == PA_INVALID_INDEX || + ss.channels != cm.channels || + !pa_channel_map_valid(&cm) || + !pa_sample_spec_valid(&ss) || + (!(s->flags & PA_STREAM_FIX_FORMAT) && ss.format != s->sample_spec.format) || + (!(s->flags & PA_STREAM_FIX_RATE) && ss.rate != s->sample_spec.rate) || + (!(s->flags & PA_STREAM_FIX_CHANNELS) && !pa_channel_map_equal(&cm, &s->channel_map))) { + pa_context_fail(s->context, PA_ERR_PROTOCOL); + goto finish; + } + + pa_xfree(s->device_name); + s->device_name = pa_xstrdup(dn); + s->suspended = suspended; + + if (!s->manual_buffer_attr && pa_bytes_per_second(&ss) != pa_bytes_per_second(&s->sample_spec)) { + pa_buffer_attr attr; + pa_operation *o; + + automatic_buffer_attr(&attr, &ss); + + /* If we need to update the buffer metrics, we wait for + * the the OK for that call before we go to + * PA_STREAM_READY */ + + s->state = PA_STREAM_READY; + pa_assert_se(o = pa_stream_set_buffer_attr(s, &attr, NULL, NULL)); + pa_operation_unref(o); + s->state = PA_STREAM_CREATING; + + s->buffer_attr_not_ready = TRUE; + } + + s->channel_map = cm; + s->sample_spec = ss; + } + if (!pa_tagstruct_eof(t)) { pa_context_fail(s->context, PA_ERR_PROTOCOL); goto finish; @@ -491,15 +660,19 @@ void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED pa_dynarray_put((s->direction == PA_STREAM_RECORD) ? s->context->record_streams : s->context->playback_streams, s->channel, s); if (s->direction != PA_STREAM_UPLOAD && s->flags & PA_STREAM_AUTO_TIMING_UPDATE) { + /* If automatic timing updates are active, we wait for the * first timing update before going to PA_STREAM_READY * state */ + s->state = PA_STREAM_READY; request_auto_timing_update(s, 1); s->state = PA_STREAM_CREATING; - } else - create_stream_complete(s); + s->timing_info_not_ready = TRUE; + } + + create_stream_complete(s); finish: pa_stream_unref(s); @@ -525,7 +698,13 @@ static int create_stream( PA_STREAM_START_CORKED| PA_STREAM_INTERPOLATE_TIMING| PA_STREAM_NOT_MONOTONOUS| - PA_STREAM_AUTO_TIMING_UPDATE : 0))), PA_ERR_INVALID); + PA_STREAM_AUTO_TIMING_UPDATE| + PA_STREAM_NO_REMAP_CHANNELS| + PA_STREAM_NO_REMIX_CHANNELS| + PA_STREAM_FIX_FORMAT| + PA_STREAM_FIX_RATE| + PA_STREAM_FIX_CHANNELS| + PA_STREAM_DONT_MOVE : 0))), PA_ERR_INVALID); PA_CHECK_VALIDITY(s->context, !volume || volume->channels == s->sample_spec.channels, PA_ERR_INVALID); PA_CHECK_VALIDITY(s->context, !sync_stream || (direction == PA_STREAM_PLAYBACK && sync_stream->direction == PA_STREAM_PLAYBACK), PA_ERR_INVALID); @@ -537,15 +716,17 @@ static int create_stream( if (sync_stream) s->syncid = sync_stream->syncid; - if (attr) + if (attr) { s->buffer_attr = *attr; - else { + s->manual_buffer_attr = TRUE; + } else { /* half a second, with minimum request of 10 ms */ s->buffer_attr.tlength = pa_bytes_per_second(&s->sample_spec)/2; s->buffer_attr.maxlength = (s->buffer_attr.tlength*3)/2; s->buffer_attr.minreq = s->buffer_attr.tlength/50; s->buffer_attr.prebuf = s->buffer_attr.tlength - s->buffer_attr.minreq; s->buffer_attr.fragsize = s->buffer_attr.tlength/50; + s->manual_buffer_attr = FALSE; } if (!dev) @@ -585,6 +766,19 @@ static int create_stream( } else pa_tagstruct_putu32(t, s->buffer_attr.fragsize); + if (s->context->version >= 12 && s->direction != PA_STREAM_UPLOAD) { + pa_tagstruct_put( + t, + PA_TAG_BOOLEAN, flags & PA_STREAM_NO_REMAP_CHANNELS, + PA_TAG_BOOLEAN, flags & PA_STREAM_NO_REMIX_CHANNELS, + PA_TAG_BOOLEAN, flags & PA_STREAM_FIX_FORMAT, + PA_TAG_BOOLEAN, flags & PA_STREAM_FIX_RATE, + PA_TAG_BOOLEAN, flags & PA_STREAM_FIX_CHANNELS, + PA_TAG_BOOLEAN, flags & PA_STREAM_DONT_MOVE, + PA_TAG_BOOLEAN, flags & PA_STREAM_VARIABLE_RATE, + PA_TAG_INVALID); + } + pa_pstream_send_tagstruct(s->context->pstream, t); pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_create_stream_callback, s, NULL); @@ -921,8 +1115,10 @@ static void stream_get_timing_info_callback(pa_pdispatch *pd, uint32_t command, } /* First, let's complete the initialization, if necessary. */ - if (o->stream->state == PA_STREAM_CREATING) + if (o->stream->state == PA_STREAM_CREATING) { + o->stream->timing_info_not_ready = FALSE; create_stream_complete(o->stream); + } if (o->stream->latency_update_callback) o->stream->latency_update_callback(o->stream, o->stream->latency_update_userdata); @@ -1084,6 +1280,22 @@ void pa_stream_set_latency_update_callback(pa_stream *s, pa_stream_notify_cb_t c s->latency_update_userdata = userdata; } +void pa_stream_set_moved_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata) { + pa_assert(s); + pa_assert(PA_REFCNT_VALUE(s) >= 1); + + s->moved_callback = cb; + s->moved_userdata = userdata; +} + +void pa_stream_set_suspended_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata) { + pa_assert(s); + pa_assert(PA_REFCNT_VALUE(s) >= 1); + + s->suspended_callback = cb; + s->suspended_userdata = userdata; +} + void pa_stream_simple_ack_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { pa_operation *o = userdata; int success = 1; @@ -1420,8 +1632,208 @@ const pa_buffer_attr* pa_stream_get_buffer_attr(pa_stream *s) { PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE); - PA_CHECK_VALIDITY_RETURN_NULL(s->context, - pa_context_get_server_protocol_version(s->context) >= 9, PA_ERR_NODATA); + PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->context->version >= 9, PA_ERR_NODATA); return &s->buffer_attr; } + +static void stream_set_buffer_attr_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { + pa_operation *o = userdata; + int success = 1; + + pa_assert(pd); + pa_assert(o); + pa_assert(PA_REFCNT_VALUE(o) >= 1); + + if (!o->context) + goto finish; + + if (command != PA_COMMAND_REPLY) { + if (pa_context_handle_error(o->context, command, t) < 0) + goto finish; + + success = 0; + } else { + + if (o->stream->direction == PA_STREAM_PLAYBACK) { + if (pa_tagstruct_getu32(t, &o->stream->buffer_attr.maxlength) < 0 || + pa_tagstruct_getu32(t, &o->stream->buffer_attr.tlength) < 0 || + pa_tagstruct_getu32(t, &o->stream->buffer_attr.prebuf) < 0 || + pa_tagstruct_getu32(t, &o->stream->buffer_attr.minreq) < 0) { + pa_context_fail(o->context, PA_ERR_PROTOCOL); + goto finish; + } + } else if (o->stream->direction == PA_STREAM_RECORD) { + if (pa_tagstruct_getu32(t, &o->stream->buffer_attr.maxlength) < 0 || + pa_tagstruct_getu32(t, &o->stream->buffer_attr.fragsize) < 0) { + pa_context_fail(o->context, PA_ERR_PROTOCOL); + goto finish; + } + } + + if (!pa_tagstruct_eof(t)) { + pa_context_fail(o->context, PA_ERR_PROTOCOL); + goto finish; + } + + o->stream->manual_buffer_attr = TRUE; + } + + if (o->stream->state == PA_STREAM_CREATING) { + o->stream->buffer_attr_not_ready = FALSE; + create_stream_complete(o->stream); + } + + if (o->callback) { + pa_stream_success_cb_t cb = (pa_stream_success_cb_t) o->callback; + cb(o->stream, success, o->userdata); + } + +finish: + pa_operation_done(o); + pa_operation_unref(o); +} + + +pa_operation* pa_stream_set_buffer_attr(pa_stream *s, const pa_buffer_attr *attr, pa_stream_success_cb_t cb, void *userdata) { + pa_operation *o; + pa_tagstruct *t; + uint32_t tag; + + pa_assert(s); + pa_assert(PA_REFCNT_VALUE(s) >= 1); + pa_assert(attr); + + PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->context->version >= 12, PA_ERR_NOTSUPPORTED); + + o = pa_operation_new(s->context, s, (pa_operation_cb_t) cb, userdata); + + t = pa_tagstruct_command( + s->context, + s->direction == PA_STREAM_RECORD ? PA_COMMAND_SET_RECORD_STREAM_BUFFER_ATTR : PA_COMMAND_SET_PLAYBACK_STREAM_BUFFER_ATTR, + &tag); + pa_tagstruct_putu32(t, s->channel); + + pa_tagstruct_putu32(t, attr->maxlength); + + if (s->direction == PA_STREAM_PLAYBACK) + pa_tagstruct_put( + t, + PA_TAG_U32, attr->tlength, + PA_TAG_U32, attr->prebuf, + PA_TAG_U32, attr->minreq, + PA_TAG_INVALID); + else + pa_tagstruct_putu32(t, attr->fragsize); + + pa_pstream_send_tagstruct(s->context->pstream, t); + pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, stream_set_buffer_attr_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); + + return o; +} + +uint32_t pa_stream_get_device_index(pa_stream *s) { + pa_assert(s); + pa_assert(PA_REFCNT_VALUE(s) >= 1); + + PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE, PA_INVALID_INDEX); + PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE, PA_INVALID_INDEX); + PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->context->version >= 12, PA_ERR_NOTSUPPORTED, PA_INVALID_INDEX); + PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->device_index != PA_INVALID_INDEX, PA_ERR_BADSTATE, PA_INVALID_INDEX); + + return s->device_index; +} + +const char *pa_stream_get_device_name(pa_stream *s) { + pa_assert(s); + pa_assert(PA_REFCNT_VALUE(s) >= 1); + + PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->context->version >= 12, PA_ERR_NOTSUPPORTED); + PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->device_name, PA_ERR_BADSTATE); + + return s->device_name; +} + +int pa_stream_is_suspended(pa_stream *s) { + pa_assert(s); + pa_assert(PA_REFCNT_VALUE(s) >= 1); + + PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY(s->context, s->context->version >= 12, PA_ERR_NOTSUPPORTED); + + return s->suspended; +} + +static void stream_update_sample_rate_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { + pa_operation *o = userdata; + int success = 1; + + pa_assert(pd); + pa_assert(o); + pa_assert(PA_REFCNT_VALUE(o) >= 1); + + if (!o->context) + goto finish; + + if (command != PA_COMMAND_REPLY) { + if (pa_context_handle_error(o->context, command, t) < 0) + goto finish; + + success = 0; + } else { + + if (!pa_tagstruct_eof(t)) { + pa_context_fail(o->context, PA_ERR_PROTOCOL); + goto finish; + } + } + + o->stream->sample_spec.rate = PA_PTR_TO_UINT(o->private); + pa_assert(pa_sample_spec_valid(&o->stream->sample_spec)); + + if (o->callback) { + pa_stream_success_cb_t cb = (pa_stream_success_cb_t) o->callback; + cb(o->stream, success, o->userdata); + } + +finish: + pa_operation_done(o); + pa_operation_unref(o); +} + + +pa_operation *pa_stream_update_sample_rate(pa_stream *s, uint32_t rate, pa_stream_success_cb_t cb, void *userdata) { + pa_operation *o; + pa_tagstruct *t; + uint32_t tag; + + pa_assert(s); + pa_assert(PA_REFCNT_VALUE(s) >= 1); + + PA_CHECK_VALIDITY_RETURN_NULL(s->context, rate > 0 && rate <= PA_RATE_MAX, PA_ERR_INVALID); + PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->flags & PA_STREAM_VARIABLE_RATE, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->context->version >= 12, PA_ERR_NOTSUPPORTED); + + o = pa_operation_new(s->context, s, (pa_operation_cb_t) cb, userdata); + o->private = PA_UINT_TO_PTR(rate); + + t = pa_tagstruct_command( + s->context, + s->direction == PA_STREAM_RECORD ? PA_COMMAND_UPDATE_RECORD_STREAM_SAMPLE_RATE : PA_COMMAND_UPDATE_PLAYBACK_STREAM_SAMPLE_RATE, + &tag); + pa_tagstruct_putu32(t, s->channel); + pa_tagstruct_putu32(t, rate); + + pa_pstream_send_tagstruct(s->context->pstream, t); + pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, stream_update_sample_rate_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); + + return o; + +} diff --git a/src/pulse/stream.h b/src/pulse/stream.h index 8c6a90c1..85473227 100644 --- a/src/pulse/stream.h +++ b/src/pulse/stream.h @@ -295,9 +295,38 @@ pa_stream_state_t pa_stream_get_state(pa_stream *p); /** Return the context this stream is attached to */ pa_context* pa_stream_get_context(pa_stream *p); -/** Return the device (sink input or source output) index this stream is connected to */ +/** Return the sink input resp. source output index this stream is + * identified in the server with. This is useful for usage with the + * introspection functions, such as pa_context_get_sink_input_info() + * resp. pa_context_get_source_output_info(). */ uint32_t pa_stream_get_index(pa_stream *s); +/** Return the index of the sink or source this stream is connected to + * in the server. This is useful for usage with the introspection + * functions, such as pa_context_get_sink_info_by_index() + * resp. pa_context_get_source_info_by_index(). Please note that + * streams may be moved between sinks/sources and thus it is + * recommended to use pa_stream_set_moved_callback() to be notified + * about this. This function will return with PA_ERR_NOTSUPPORTED when the + * server is older than 0.9.8. \since 0.9.8 */ +uint32_t pa_stream_get_device_index(pa_stream *s); + +/** Return the name of the sink or source this stream is connected to + * in the server. This is useful for usage with the introspection + * functions, such as pa_context_get_sink_info_by_name() + * resp. pa_context_get_source_info_by_name(). Please note that + * streams may be moved between sinks/sources and thus it is + * recommended to use pa_stream_set_moved_callback() to be notified + * about this. This function will return with PA_ERR_NOTSUPPORTED when the + * server is older than 0.9.8. \since 0.9.8 */ +const char *pa_stream_get_device_name(pa_stream *s); + +/** Return 1 if the sink or source this stream is connected to has + * been suspended. This will return 0 if not, and negative on + * error. This function will return with PA_ERR_NOTSUPPORTED when the + * server is older than 0.9.8. \since 0.9.8 */ +int pa_stream_is_suspended(pa_stream *s); + /** Connect the stream to a sink */ int pa_stream_connect_playback( pa_stream *s /**< The stream to connect to a sink */, @@ -346,10 +375,10 @@ int pa_stream_peek( * calling pa_stream_peek(). \since 0.8 */ int pa_stream_drop(pa_stream *p); -/** Return the nember of bytes that may be written using pa_stream_write(), in bytes */ +/** Return the number of bytes that may be written using pa_stream_write() */ size_t pa_stream_writable_size(pa_stream *p); -/** Return the number of bytes that may be read using pa_stream_read(), in bytes \since 0.8 */ +/** Return the number of bytes that may be read using pa_stream_read() \since 0.8 */ size_t pa_stream_readable_size(pa_stream *p); /** Drain a playback stream. Use this for notification when the buffer is empty */ @@ -378,9 +407,28 @@ void pa_stream_set_overflow_callback(pa_stream *p, pa_stream_notify_cb_t cb, voi /** Set the callback function that is called when a buffer underflow happens. (Only for playback streams) \since 0.8 */ void pa_stream_set_underflow_callback(pa_stream *p, pa_stream_notify_cb_t cb, void *userdata); -/** Set the callback function that is called whenever a latency information update happens. Useful on PA_STREAM_AUTO_TIMING_UPDATE streams only. (Only for playback streams) \since 0.8.2 */ +/** Set the callback function that is called whenever a latency + * information update happens. Useful on PA_STREAM_AUTO_TIMING_UPDATE + * streams only. (Only for playback streams) \since 0.8.2 */ void pa_stream_set_latency_update_callback(pa_stream *p, pa_stream_notify_cb_t cb, void *userdata); +/** Set the callback function that is called whenever the stream is + * moved to a different sink/source. Use pa_stream_get_device_name()or + * pa_stream_get_device_index() to query the new sink/source. This + * notification is only generated when the server is at least + * 0.9.8. \since 0.9.8 */ +void pa_stream_set_moved_callback(pa_stream *p, pa_stream_notify_cb_t cb, void *userdata); + +/** Set the callback function that is called whenever the sink/source + * this stream is connected to is suspended or resumed. Use + * pa_stream_is_suspended() to query the new suspend status. Please + * note that the suspend status might also change when the stream is + * moved between devices. Thus if you call this function you very + * likely want to call pa_stream_set_moved_callback, too. This + * notification is only generated when the server is at least + * 0.9.8. \since 0.9.8 */ +void pa_stream_set_suspended_callback(pa_stream *p, pa_stream_notify_cb_t cb, void *userdata); + /** Pause (or resume) playback of this stream temporarily. Available on both playback and recording streams. \since 0.3 */ pa_operation* pa_stream_cork(pa_stream *s, int b, pa_stream_success_cb_t cb, void *userdata); @@ -447,6 +495,21 @@ const pa_channel_map* pa_stream_get_channel_map(pa_stream *s); * PulseAudio 0.9. \since 0.9.0 */ const pa_buffer_attr* pa_stream_get_buffer_attr(pa_stream *s); +/** Change the buffer metrics of the stream during playback. The + * server might have chosen different buffer metrics then + * requested. The selected metrics may be queried with + * pa_stream_get_buffer_attr() as soon as the callback is called. Only + * valid after the stream has been connected successfully and if the + * server is at least PulseAudio 0.9.8. \since 0.9.8 */ +pa_operation *pa_stream_set_buffer_attr(pa_stream *s, const pa_buffer_attr *attr, pa_stream_success_cb_t cb, void *userdata); + +/* Change the stream sampling rate during playback. You need to pass + * PA_STREAM_VARIABLE_RATE in the flags parameter of + * pa_stream_connect() if you plan to use this function. Only valid + * after the stream has been connected successfully and if the server + * is at least PulseAudio 0.9.8. \since 0.9.8 */ +pa_operation *pa_stream_update_sample_rate(pa_stream *s, uint32_t rate, pa_stream_success_cb_t cb, void *userdata); + PA_C_DECL_END #endif -- cgit From 9d2255d4e5c2fcecee4bf654cc35fba2a5b6a399 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 22 Nov 2007 15:09:00 +0000 Subject: fix uploading of samples into PA. Problem discovered by Colin Guthrie git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@2074 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/stream.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/pulse') diff --git a/src/pulse/stream.c b/src/pulse/stream.c index 92420825..cd70cdcb 100644 --- a/src/pulse/stream.c +++ b/src/pulse/stream.c @@ -586,7 +586,7 @@ void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED } } - if (s->context->version >= 12) { + if (s->context->version >= 12 && s->direction != PA_STREAM_UPLOAD) { pa_sample_spec ss; pa_channel_map cm; const char *dn = NULL; -- cgit From 2d34bca4a13bc43eeda54535630e37703806d53f Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 24 Nov 2007 16:24:54 +0000 Subject: rearrange #includes git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@2081 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/context.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/pulse') diff --git a/src/pulse/context.c b/src/pulse/context.c index bc960e21..7243a29d 100644 --- a/src/pulse/context.c +++ b/src/pulse/context.c @@ -50,12 +50,12 @@ #include #endif -#include "../pulsecore/winsock.h" - -#include #include #include +#include +#include + #include #include #include -- cgit From 63c616eeccdea593a1c8dc65405e02c2a71038fe Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sun, 23 Dec 2007 20:12:37 +0000 Subject: add new property list implementation git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@2085 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/proplist.c | 244 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/pulse/proplist.h | 88 +++++++++++++++++++ 2 files changed, 332 insertions(+) create mode 100644 src/pulse/proplist.c create mode 100644 src/pulse/proplist.h (limited to 'src/pulse') diff --git a/src/pulse/proplist.c b/src/pulse/proplist.c new file mode 100644 index 00000000..1c2614a0 --- /dev/null +++ b/src/pulse/proplist.c @@ -0,0 +1,244 @@ +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + Copyright 2007 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 +#endif + +#include + +#include +#include + +#include +#include +#include + +#include "proplist.h" + +struct property { + char *key; + void *value; + size_t nbytes; +}; + +#define MAKE_HASHMAP(p) ((pa_hashmap*) (p)) +#define MAKE_PROPLIST(p) ((pa_proplist*) (p)) + +static pa_bool_t property_name_valid(const char *key) { + + if (!pa_utf8_valid(key)) + return FALSE; + + if (strlen(key) <= 0) + return FALSE; + + return TRUE; +} + +static void property_free(struct property *prop) { + pa_assert(prop); + + pa_xfree(prop->key); + pa_xfree(prop->value); + pa_xfree(prop); +} + +pa_proplist* pa_proplist_new(void) { + return MAKE_PROPLIST(pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func)); +} + +void pa_proplist_free(pa_proplist* p) { + struct property *prop; + + while ((prop = pa_hashmap_steal_first(MAKE_HASHMAP(p)))) + property_free(prop); + + pa_hashmap_free(MAKE_HASHMAP(p), NULL, NULL); +} + +/** Will accept only valid UTF-8 */ +int pa_proplist_puts(pa_proplist *p, const char *key, const char *value) { + struct property *prop; + pa_bool_t add = FALSE; + + pa_assert(p); + pa_assert(key); + + if (!property_name_valid(key) || !pa_utf8_valid(value)) + return -1; + + if (!(prop = pa_hashmap_get(MAKE_HASHMAP(p), key))) { + prop = pa_xnew(struct property, 1); + prop->key = pa_xstrdup(key); + add = TRUE; + } else + pa_xfree(prop->value); + + prop->value = pa_xstrdup(value); + prop->nbytes = strlen(value)+1; + + if (add) + pa_hashmap_put(MAKE_HASHMAP(p), prop->key, prop); + + return 0; +} + +int pa_proplist_put(pa_proplist *p, const char *key, const void *data, size_t nbytes) { + struct property *prop; + pa_bool_t add = FALSE; + + pa_assert(p); + pa_assert(key); + + if (!property_name_valid(key)) + return -1; + + if (!(prop = pa_hashmap_get(MAKE_HASHMAP(p), key))) { + prop = pa_xnew(struct property, 1); + prop->key = pa_xstrdup(key); + add = TRUE; + } else + pa_xfree(prop->value); + + prop->value = pa_xmemdup(data, nbytes); + prop->nbytes = nbytes; + + if (add) + pa_hashmap_put(MAKE_HASHMAP(p), prop->key, prop); + + return 0; +} + +const char *pa_proplist_gets(pa_proplist *p, const char *key) { + struct property *prop; + + pa_assert(p); + pa_assert(key); + + if (!property_name_valid(key)) + return NULL; + + if (!(prop = pa_hashmap_get(MAKE_HASHMAP(p), key))) + return NULL; + + if (prop->nbytes <= 0) + return NULL; + + if (((char*) prop->value)[prop->nbytes-1] != 0) + return NULL; + + if (strlen((char*) prop->value) != prop->nbytes-1) + return NULL; + + if (!pa_utf8_valid((char*) prop->value)) + return NULL; + + return (char*) prop->value; +} + +int pa_proplist_get(pa_proplist *p, const char *key, const void **data, size_t *nbytes) { + struct property *prop; + + pa_assert(p); + pa_assert(key); + + if (!property_name_valid(key)) + return -1; + + if (!(prop = pa_hashmap_get(MAKE_HASHMAP(p), key))) + return -1; + + *data = prop->value; + *nbytes = prop->nbytes; + + return 0; +} + +void pa_proplist_merge(pa_proplist *p, pa_proplist *other) { + struct property *prop; + void *state = NULL; + + pa_assert(p); + pa_assert(other); + + while ((prop = pa_hashmap_iterate(MAKE_HASHMAP(other), &state, NULL))) + pa_assert_se(pa_proplist_put(p, prop->key, prop->value, prop->nbytes) == 0); +} + +int pa_proplist_remove(pa_proplist *p, const char *key) { + struct property *prop; + + pa_assert(p); + pa_assert(key); + + if (!property_name_valid(key)) + return -1; + + if (!(prop = pa_hashmap_remove(MAKE_HASHMAP(p), key))) + return -1; + + property_free(prop); + return 0; +} + +const char *pa_proplist_iterate(pa_proplist *p, void **state) { + struct property *prop; + + if (!(prop = pa_hashmap_iterate(MAKE_HASHMAP(p), state, NULL))) + return NULL; + + return prop->key; +} + +char *pa_proplist_to_string(pa_proplist *p) { + const char *key; + void *state = NULL; + pa_strbuf *buf; + + pa_assert(p); + + buf = pa_strbuf_new(); + + while ((key = pa_proplist_iterate(p, &state))) { + + const char *v; + + if ((v = pa_proplist_gets(p, key))) + pa_strbuf_printf(buf, "%s = \"%s\"\n", key, v); + else { + const void *value; + size_t nbytes; + char *c; + + pa_assert_se(pa_proplist_get(p, key, &value, &nbytes) == 0); + c = pa_xnew(char, nbytes*2+1); + pa_hexstr((const uint8_t*) value, nbytes, c, nbytes*2+1); + + pa_strbuf_printf(buf, "%s = hex:%s\n", key, c); + pa_xfree(c); + } + } + + return pa_strbuf_tostring_free(buf); +} diff --git a/src/pulse/proplist.h b/src/pulse/proplist.h new file mode 100644 index 00000000..f74b3aa1 --- /dev/null +++ b/src/pulse/proplist.h @@ -0,0 +1,88 @@ +#ifndef foopulseproplisthfoo +#define foopulseproplisthfoo + +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + Copyright 2007 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 + +/* Defined properties: + * + * x11.xid + * x11.display + * x11.x_pointer + * x11.y_pointer + * x11.button + * media.name + * media.title + * media.artist + * media.language + * media.filename + * media.icon + * media.icon_name + * media.role video, music, game, event, phone, production + * application.name + * application.version + * application.icon + * application.icon_name + */ + +#define PA_PROP_X11_XID "x11.xid" +#define PA_PROP_X11_DISPLAY "x11.display" +#define PA_PROP_X11_X_POINTER "x11.x_pointer" +#define PA_PROP_X11_Y_POINTER "x11.y_pointer" +#define PA_PROP_X11_BUTTON "x11.button" +#define PA_PROP_MEDIA_NAME "media.name" +#define PA_PROP_MEDIA_TITLE "media.title" +#define PA_PROP_MEDIA_ARTIST "media.artist" +#define PA_PROP_MEDIA_LANGUAGE "media.language" +#define PA_PROP_MEDIA_FILENAME "media.filename" +#define PA_PROP_MEDIA_ICON "media.icon" +#define PA_PROP_MEDIA_ICON_NAME "media.icon_name" +#define PA_PROP_MEDIA_ROLE "media.role" +#define PA_PROP_APPLICATION_NAME "application.name" +#define PA_PROP_APPLICATION_VERSION "application.version" +#define PA_PROP_APPLICATION_ICON "application.icon" +#define PA_PROP_APPLICATION_ICON_NAME "application.icon_name" + +typedef struct pa_proplist pa_proplist; + +pa_proplist* pa_proplist_new(void); +void pa_proplist_free(pa_proplist* p); + +/** Will accept only valid UTF-8 */ +int pa_proplist_puts(pa_proplist *p, const char *key, const char *value); +int pa_proplist_put(pa_proplist *p, const char *key, const void *data, size_t nbytes); + +/* Will return NULL if the data is not valid UTF-8 */ +const char *pa_proplist_gets(pa_proplist *p, const char *key); +int pa_proplist_get(pa_proplist *p, const char *key, const void **data, size_t *nbytes); + +void pa_proplist_merge(pa_proplist *p, pa_proplist *other); +int pa_proplist_remove(pa_proplist *p, const char *key); + +const char *pa_proplist_iterate(pa_proplist *p, void **state); + +char *pa_proplist_to_string(pa_proplist *p); + +#endif -- cgit From 2cb1b2c06766745d58412e238497f9cd1af0d312 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 29 Dec 2007 18:03:53 +0000 Subject: add new function pa_proplist_contains() git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@2089 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/proplist.c | 13 +++++++++++++ src/pulse/proplist.h | 2 ++ 2 files changed, 15 insertions(+) (limited to 'src/pulse') diff --git a/src/pulse/proplist.c b/src/pulse/proplist.c index 1c2614a0..c27c9d84 100644 --- a/src/pulse/proplist.c +++ b/src/pulse/proplist.c @@ -242,3 +242,16 @@ char *pa_proplist_to_string(pa_proplist *p) { return pa_strbuf_tostring_free(buf); } + +int pa_proplist_contains(pa_proplist *p, const char *key) { + pa_assert(p); + pa_assert(key); + + if (!property_name_valid(key)) + return -1; + + if (!(pa_hashmap_get(MAKE_HASHMAP(p), key))) + return 0; + + return 1; +} diff --git a/src/pulse/proplist.h b/src/pulse/proplist.h index f74b3aa1..c4cf9ac9 100644 --- a/src/pulse/proplist.h +++ b/src/pulse/proplist.h @@ -85,4 +85,6 @@ const char *pa_proplist_iterate(pa_proplist *p, void **state); char *pa_proplist_to_string(pa_proplist *p); +int pa_proplist_contains(pa_proplist *p, const char *key); + #endif -- cgit From d36a1b8333081dcaad2b77209913253a9703d88f Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sun, 6 Jan 2008 20:38:11 +0000 Subject: use __BYTE_ORDER macro for detecting byte order, as suggested on http://unixpapa.com/incnote/byteorder.html git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@2098 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/sample.h | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'src/pulse') diff --git a/src/pulse/sample.h b/src/pulse/sample.h index 8f9f1733..f0b839fd 100644 --- a/src/pulse/sample.h +++ b/src/pulse/sample.h @@ -27,6 +27,7 @@ #include #include +#include #include #include @@ -104,6 +105,14 @@ PA_C_DECL_BEGIN +#if !defined(WORDS_BIGENDIAN) +#if defined(__BYTE_ORDER) +#if __BYTE_ORDER == __BIG_ENDIAN +#define WORDS_BIGENDIAN +#endif +#endif +#endif + /** Maximum number of allowed channels */ #define PA_CHANNELS_MAX 32 -- cgit From 86b9ef8c961bed9d3a65f044741bb423c26d8005 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 13 Feb 2008 22:13:44 +0000 Subject: deal with a possibly failing pa_channel_map_init_auto() correctly git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@2105 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/stream.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'src/pulse') diff --git a/src/pulse/stream.c b/src/pulse/stream.c index cd70cdcb..c44323fc 100644 --- a/src/pulse/stream.c +++ b/src/pulse/stream.c @@ -46,6 +46,7 @@ pa_stream *pa_stream_new(pa_context *c, const char *name, const pa_sample_spec *ss, const pa_channel_map *map) { pa_stream *s; int i; + pa_channel_map tmap; pa_assert(c); pa_assert(PA_REFCNT_VALUE(c) >= 1); @@ -54,6 +55,9 @@ pa_stream *pa_stream_new(pa_context *c, const char *name, const pa_sample_spec * PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 12 || (ss->format != PA_SAMPLE_S32LE || ss->format != PA_SAMPLE_S32NE), PA_ERR_NOTSUPPORTED); PA_CHECK_VALIDITY_RETURN_NULL(c, !map || (pa_channel_map_valid(map) && map->channels == ss->channels), PA_ERR_INVALID); + if (!map) + PA_CHECK_VALIDITY_RETURN_NULL(c, map = pa_channel_map_init_auto(&tmap, ss->channels, PA_CHANNEL_MAP_DEFAULT), PA_ERR_INVALID); + s = pa_xnew(pa_stream, 1); PA_REFCNT_INIT(s); s->context = c; @@ -81,13 +85,9 @@ pa_stream *pa_stream_new(pa_context *c, const char *name, const pa_sample_spec * s->direction = PA_STREAM_NODIRECTION; s->name = pa_xstrdup(name); s->sample_spec = *ss; + s->channel_map = *map; s->flags = 0; - if (map) - s->channel_map = *map; - else - pa_channel_map_init_auto(&s->channel_map, ss->channels, PA_CHANNEL_MAP_DEFAULT); - s->channel = 0; s->channel_valid = 0; s->syncid = c->csyncid++; -- cgit