From 22c8cebb858012e4e9c551bb54456237e7597697 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 16 Feb 2006 22:43:59 +0000 Subject: drop polyplib- prefix from client library files git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@492 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/Makefile.am | 50 +- src/polyp/browser.c | 312 ++++++++++++ src/polyp/browser.h | 65 +++ src/polyp/context.c | 871 +++++++++++++++++++++++++++++++++ src/polyp/context.h | 117 +++++ src/polyp/def.h | 213 +++++++++ src/polyp/error.c | 54 +++ src/polyp/error.h | 38 ++ src/polyp/internal.h | 154 ++++++ src/polyp/introspect.c | 1003 +++++++++++++++++++++++++++++++++++++++ src/polyp/introspect.h | 279 +++++++++++ src/polyp/operation.c | 103 ++++ src/polyp/operation.h | 51 ++ src/polyp/polypaudio.h | 86 ++++ src/polyp/polyplib-browser.c | 312 ------------ src/polyp/polyplib-browser.h | 65 --- src/polyp/polyplib-context.c | 871 --------------------------------- src/polyp/polyplib-context.h | 117 ----- src/polyp/polyplib-def.h | 213 --------- src/polyp/polyplib-error.c | 54 --- src/polyp/polyplib-error.h | 38 -- src/polyp/polyplib-internal.h | 154 ------ src/polyp/polyplib-introspect.c | 1003 --------------------------------------- src/polyp/polyplib-introspect.h | 279 ----------- src/polyp/polyplib-operation.c | 103 ---- src/polyp/polyplib-operation.h | 51 -- src/polyp/polyplib-scache.c | 127 ----- src/polyp/polyplib-scache.h | 50 -- src/polyp/polyplib-simple.c | 393 --------------- src/polyp/polyplib-simple.h | 80 ---- src/polyp/polyplib-stream.c | 807 ------------------------------- src/polyp/polyplib-stream.h | 181 ------- src/polyp/polyplib-subscribe.c | 81 ---- src/polyp/polyplib-subscribe.h | 47 -- src/polyp/polyplib-version.h.in | 47 -- src/polyp/polyplib.h | 86 ---- src/polyp/scache.c | 127 +++++ src/polyp/scache.h | 50 ++ src/polyp/simple.c | 393 +++++++++++++++ src/polyp/simple.h | 80 ++++ src/polyp/stream.c | 807 +++++++++++++++++++++++++++++++ src/polyp/stream.h | 181 +++++++ src/polyp/subscribe.c | 81 ++++ src/polyp/subscribe.h | 47 ++ src/polyp/version.h.in | 47 ++ src/polypcore/native-common.h | 2 +- src/polypcore/sink.c | 2 +- src/tests/pacat-simple.c | 4 +- src/tests/parec-simple.c | 4 +- src/utils/pabrowse.c | 2 +- src/utils/pacat.c | 4 +- src/utils/pactl.c | 2 +- src/utils/paplay.c | 4 +- 53 files changed, 5196 insertions(+), 5196 deletions(-) create mode 100644 src/polyp/browser.c create mode 100644 src/polyp/browser.h create mode 100644 src/polyp/context.c create mode 100644 src/polyp/context.h create mode 100644 src/polyp/def.h create mode 100644 src/polyp/error.c create mode 100644 src/polyp/error.h create mode 100644 src/polyp/internal.h create mode 100644 src/polyp/introspect.c create mode 100644 src/polyp/introspect.h create mode 100644 src/polyp/operation.c create mode 100644 src/polyp/operation.h create mode 100644 src/polyp/polypaudio.h delete mode 100644 src/polyp/polyplib-browser.c delete mode 100644 src/polyp/polyplib-browser.h delete mode 100644 src/polyp/polyplib-context.c delete mode 100644 src/polyp/polyplib-context.h delete mode 100644 src/polyp/polyplib-def.h delete mode 100644 src/polyp/polyplib-error.c delete mode 100644 src/polyp/polyplib-error.h delete mode 100644 src/polyp/polyplib-internal.h delete mode 100644 src/polyp/polyplib-introspect.c delete mode 100644 src/polyp/polyplib-introspect.h delete mode 100644 src/polyp/polyplib-operation.c delete mode 100644 src/polyp/polyplib-operation.h delete mode 100644 src/polyp/polyplib-scache.c delete mode 100644 src/polyp/polyplib-scache.h delete mode 100644 src/polyp/polyplib-simple.c delete mode 100644 src/polyp/polyplib-simple.h delete mode 100644 src/polyp/polyplib-stream.c delete mode 100644 src/polyp/polyplib-stream.h delete mode 100644 src/polyp/polyplib-subscribe.c delete mode 100644 src/polyp/polyplib-subscribe.h delete mode 100644 src/polyp/polyplib-version.h.in delete mode 100644 src/polyp/polyplib.h create mode 100644 src/polyp/scache.c create mode 100644 src/polyp/scache.h create mode 100644 src/polyp/simple.c create mode 100644 src/polyp/simple.h create mode 100644 src/polyp/stream.c create mode 100644 src/polyp/stream.h create mode 100644 src/polyp/subscribe.c create mode 100644 src/polyp/subscribe.h create mode 100644 src/polyp/version.h.in (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index 7e8e73b6..cfd4b751 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -88,7 +88,7 @@ polypconf_DATA = \ client.conf BUILT_SOURCES = \ - polyp/polyplib-version.h + polyp/version.h ################################### # Main daemon # @@ -259,23 +259,23 @@ polypinclude_HEADERS = \ polyp/mainloop.h \ polyp/mainloop-api.h \ polyp/mainloop-signal.h \ - polyp/polyplib.h \ - polyp/polyplib-context.h \ - polyp/polyplib-def.h \ - polyp/polyplib-error.h \ - polyp/polyplib-introspect.h \ - polyp/polyplib-operation.h \ - polyp/polyplib-scache.h \ - polyp/polyplib-simple.h \ - polyp/polyplib-stream.h \ - polyp/polyplib-subscribe.h \ - polyp/polyplib-version.h \ + polyp/polypaudio.h \ + polyp/context.h \ + polyp/def.h \ + polyp/error.h \ + polyp/introspect.h \ + polyp/operation.h \ + polyp/scache.h \ + polyp/simple.h \ + polyp/stream.h \ + polyp/subscribe.h \ + polyp/version.h \ polyp/sample.h \ polyp/volume.h if HAVE_HOWL polypinclude_HEADERS += \ - polyp/polyplib-browser.h + polyp/browser.h endif lib_LTLIBRARIES = \ @@ -306,15 +306,15 @@ libpolyp_@PA_MAJORMINOR@_la_SOURCES = \ polyp/client-conf.c polyp/client-conf.h \ polyp/llist.h \ polyp/mainloop-api.c polyp/mainloop-api.h \ - polyp/polyplib.h \ - polyp/polyplib-context.c polyp/polyplib-context.h \ - polyp/polyplib-def.h \ - polyp/polyplib-internal.h \ - polyp/polyplib-introspect.c polyp/polyplib-introspect.h \ - polyp/polyplib-operation.c polyp/polyplib-operation.h \ - polyp/polyplib-scache.c polyp/polyplib-scache.h \ - polyp/polyplib-stream.c polyp/polyplib-stream.h \ - polyp/polyplib-subscribe.c polyp/polyplib-subscribe.h \ + polyp/polypaudio.h \ + polyp/context.c polyp/context.h \ + polyp/def.h \ + polyp/internal.h \ + polyp/introspect.c polyp/introspect.h \ + polyp/operation.c polyp/operation.h \ + polyp/scache.c polyp/scache.h \ + polyp/stream.c polyp/stream.h \ + polyp/subscribe.c polyp/subscribe.h \ polyp/sample.c polyp/sample.h \ polyp/volume.c polyp/volume.h @@ -368,7 +368,7 @@ libpolyp_@PA_MAJORMINOR@_la_CFLAGS += $(LIBASYNCNS_CFLAGS) libpolyp_@PA_MAJORMINOR@_la_LIBADD += $(LIBASYNCNS_LIBS) endif -libpolyp_error_@PA_MAJORMINOR@_la_SOURCES = polyp/polyplib-error.c polyp/polyplib-error.h +libpolyp_error_@PA_MAJORMINOR@_la_SOURCES = polyp/error.c polyp/error.h libpolyp_error_@PA_MAJORMINOR@_la_CFLAGS = $(AM_CFLAGS) libpolyp_error_@PA_MAJORMINOR@_la_LIBADD = $(AM_LIBADD) libpolyp-@PA_MAJORMINOR@.la libpolyp_error_@PA_MAJORMINOR@_la_LDFLAGS = -version-info 0:0:0 @@ -381,12 +381,12 @@ libpolyp_mainloop_@PA_MAJORMINOR@_la_CFLAGS = $(AM_CFLAGS) libpolyp_mainloop_@PA_MAJORMINOR@_la_LIBADD = $(AM_LIBADD) libpolyp-@PA_MAJORMINOR@.la $(WINSOCK_LIBS) libpolyp_mainloop_@PA_MAJORMINOR@_la_LDFLAGS = -version-info 0:0:0 -libpolyp_simple_@PA_MAJORMINOR@_la_SOURCES = polyp/polyplib-simple.c polyp/polyplib-simple.h +libpolyp_simple_@PA_MAJORMINOR@_la_SOURCES = polyp/simple.c polyp/simple.h libpolyp_simple_@PA_MAJORMINOR@_la_CFLAGS = $(AM_CFLAGS) libpolyp_simple_@PA_MAJORMINOR@_la_LIBADD = $(AM_LIBADD) libpolyp-@PA_MAJORMINOR@.la libpolyp-mainloop-@PA_MAJORMINOR@.la libpolyp_simple_@PA_MAJORMINOR@_la_LDFLAGS = -version-info 0:0:0 -libpolyp_browse_@PA_MAJORMINOR@_la_SOURCES = polyp/polyplib-browser.c polyp/polyplib-browser.h +libpolyp_browse_@PA_MAJORMINOR@_la_SOURCES = polyp/browser.c polyp/browser.h libpolyp_browse_@PA_MAJORMINOR@_la_CFLAGS = $(AM_CFLAGS) $(HOWL_CFLAGS) libpolyp_browse_@PA_MAJORMINOR@_la_LIBADD = $(AM_LIBADD) libpolyp-@PA_MAJORMINOR@.la $(HOWL_LIBS) libpolyp_browse_@PA_MAJORMINOR@_la_LDFLAGS = -version-info 0:0:0 diff --git a/src/polyp/browser.c b/src/polyp/browser.c new file mode 100644 index 00000000..80051d54 --- /dev/null +++ b/src/polyp/browser.c @@ -0,0 +1,312 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio 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. + + polypaudio 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 polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include +#include + +#include "browser.h" +#include +#include +#include + +#define SERVICE_NAME_SINK "_polypaudio-sink._tcp." +#define SERVICE_NAME_SOURCE "_polypaudio-source._tcp." +#define SERVICE_NAME_SERVER "_polypaudio-server._tcp." + +pa_browser { + int ref; + pa_mainloop_api *mainloop; + + void (*callback)(pa_browser *z, pa_browse_opcode c, const pa_browse_info *i, void *userdata); + void *callback_userdata; + + sw_discovery discovery; + pa_io_event *io_event; +}; + + +static void io_callback(pa_mainloop_api*a, pa_io_event*e, int fd, pa_io_event_flags 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.\n"); + b->mainloop->io_free(b->io_event); + b->io_event = NULL; + return; + } +} + +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]; + pa_browse_opcode opcode; + int device_found = 0; + uint32_t cookie; + pa_typeid_t typeid; + 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; + + if (!strcmp(type, SERVICE_NAME_SINK)) + opcode = PA_BROWSE_NEW_SINK; + else if (!strcmp(type, SERVICE_NAME_SOURCE)) + opcode = PA_BROWSE_NEW_SOURCE; + else if (!strcmp(type, SERVICE_NAME_SERVER)) + opcode = PA_BROWSE_NEW_SERVER; + else + goto fail; + + + 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("sw_text_record_string_iterator_init() failed.\n"); + 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, "typeid")) { + + if (pa_atou(c, &typeid) < 0) + goto fail; + + i.typeid = &typeid; + } 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->callback_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("sw_discovery_resolve() failed\n"); + + break; + } + + case SW_DISCOVERY_BROWSE_REMOVE_SERVICE: + if (b->callback) { + pa_browse_info i; + memset(&i, 0, sizeof(i)); + i.name = name; + b->callback(b, PA_BROWSE_REMOVE, &i, b->callback_userdata); + } + break; + + default: + ; + } + + return SW_OKAY; +} + +pa_browser *pa_browser_new(pa_mainloop_api *mainloop) { + pa_browser *b; + sw_discovery_oid oid; + + b = pa_xmalloc(sizeof(pa_browser)); + b->mainloop = mainloop; + b->ref = 1; + b->callback = NULL; + b->callback_userdata = NULL; + + if (sw_discovery_init(&b->discovery) != SW_OKAY) { + pa_log("sw_discovery_init() failed.\n"); + 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("sw_discovery_browse() failed.\n"); + + 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, void (*cb)(pa_browser *z, pa_browse_opcode c, const pa_browse_info *i, void* userdata), void *userdata) { + assert(b); + + b->callback = cb; + b->callback_userdata = userdata; +} diff --git a/src/polyp/browser.h b/src/polyp/browser.h new file mode 100644 index 00000000..853304d7 --- /dev/null +++ b/src/polyp/browser.h @@ -0,0 +1,65 @@ +#ifndef foopolyplibbrowserhfoo +#define foopolyplibbrowserhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio 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. + + polypaudio 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 polypaudio; 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 + +pa_browser; + +pa_browse_opcode { + PA_BROWSE_NEW_SERVER, + PA_BROWSE_NEW_SINK, + PA_BROWSE_NEW_SOURCE, + PA_BROWSE_REMOVE +}; + +pa_browser *pa_browser_new(pa_mainloop_api *mainloop); +pa_browser *pa_browser_ref(pa_browser *z); +void pa_browser_unref(pa_browser *z); + +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_typeid_t *typeid; /* optional */ + const pa_sample_spec *sample_spec; /* optional */ +}; + +void pa_browser_set_callback(pa_browser *z, void (*cb)(pa_browser *z, pa_browse_opcode c, const pa_browse_info *i, void *userdata), void *userdata); + +PA_C_DECL_END + +#endif diff --git a/src/polyp/context.c b/src/polyp/context.c new file mode 100644 index 00000000..4ac11caa --- /dev/null +++ b/src/polyp/context.c @@ -0,0 +1,871 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio 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. + + polypaudio 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 polypaudio; 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 + +#include "internal.h" +#include "context.h" +#include "version.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_X11 +#include "client-conf-x11.h" +#endif + +#define AUTOSPAWN_LOCK "autospawn.lock" + +static const pa_pdispatch_callback command_table[PA_COMMAND_MAX] = { + [PA_COMMAND_REQUEST] = pa_command_request, + [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 && name); + + c = pa_xmalloc(sizeof(pa_context)); + 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(); + assert(c->playback_streams && c->record_streams); + + PA_LLIST_HEAD_INIT(pa_stream, c->streams); + PA_LLIST_HEAD_INIT(pa_operation, c->operations); + + c->error = PA_ERROR_OK; + c->state = PA_CONTEXT_UNCONNECTED; + c->ctag = 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 && c->ref >= 1); + c->ref++; + return c; +} + +void pa_context_unref(pa_context *c) { + assert(c && 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); + + if (c->state == st) + return; + + pa_context_ref(c); + + 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; + } + + c->state = st; + if (c->state_callback) + c->state_callback(c, c->state_userdata); + + pa_context_unref(c); +} + +void pa_context_fail(pa_context *c, int error) { + assert(c); + c->error = error; + pa_context_set_state(c, PA_CONTEXT_FAILED); +} + +static void pstream_die_callback(pa_pstream *p, void *userdata) { + pa_context *c = userdata; + assert(p && c); + pa_context_fail(c, PA_ERROR_CONNECTIONTERMINATED); +} + +static void pstream_packet_callback(pa_pstream *p, pa_packet *packet, void *userdata) { + pa_context *c = userdata; + assert(p && packet && c); + + pa_context_ref(c); + + if (pa_pdispatch_run(c->pdispatch, packet, c) < 0) { + pa_log(__FILE__": invalid packet.\n"); + pa_context_fail(c, PA_ERROR_PROTOCOL); + } + + pa_context_unref(c); +} + +static void pstream_memblock_callback(pa_pstream *p, uint32_t channel, PA_GCC_UNUSED uint32_t delta, const pa_memchunk *chunk, void *userdata) { + pa_context *c = userdata; + pa_stream *s; + assert(p && chunk && c && chunk->memblock && chunk->memblock->data); + + pa_context_ref(c); + + if ((s = pa_dynarray_get(c->record_streams, channel))) { + pa_mcalign_push(s->mcalign, chunk); + + for (;;) { + pa_memchunk t; + + if (pa_mcalign_pop(s->mcalign, &t) < 0) + break; + + if (s->read_callback) { + s->read_callback(s, (uint8_t*) t.memblock->data + t.index, t.length, s->read_userdata); + s->counter += chunk->length; + } + + pa_memblock_unref(t.memblock); + } + } + + pa_context_unref(c); +} + +int pa_context_handle_error(pa_context *c, uint32_t command, pa_tagstruct *t) { + assert(c); + + if (command == PA_COMMAND_ERROR) { + assert(t); + + if (pa_tagstruct_getu32(t, &c->error) < 0) { + pa_context_fail(c, PA_ERROR_PROTOCOL); + return -1; + + } + } else if (command == PA_COMMAND_TIMEOUT) + c->error = PA_ERROR_TIMEOUT; + else { + pa_context_fail(c, PA_ERROR_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 && c && (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_ERROR_PROTOCOL); + + pa_context_fail(c, c->error); + goto finish; + } + + switch(c->state) { + case PA_CONTEXT_AUTHORIZING: { + pa_tagstruct *reply; + reply = pa_tagstruct_new(NULL, 0); + pa_tagstruct_putu32(reply, PA_COMMAND_SET_CLIENT_NAME); + pa_tagstruct_putu32(reply, tag = c->ctag++); + 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); + + 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 && io); + + pa_context_ref(c); + + assert(!c->pstream); + c->pstream = pa_pstream_new(c->mainloop, io, c->memblock_stat); + assert(c->pstream); + + 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); + assert(c->pdispatch); + + if (!c->conf->cookie_valid) { + pa_context_fail(c, PA_ERROR_AUTHKEY); + goto finish; + } + + t = pa_tagstruct_new(NULL, 0); + assert(t); + pa_tagstruct_putu32(t, PA_COMMAND_AUTH); + pa_tagstruct_putu32(t, tag = c->ctag++); + pa_tagstruct_put_arbitrary(t, c->conf->cookie, sizeof(c->conf->cookie)); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, setup_complete_callback, c); + + 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() failed: %s\n", strerror(errno)); + pa_context_fail(c, PA_ERROR_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() failed: %s\n", strerror(errno)); + pa_context_fail(c, PA_ERROR_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() failed: %s\n", strerror(errno)); + pa_context_fail(c, PA_ERROR_INTERNAL); + goto fail; + } else if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { + pa_context_fail(c, PA_ERROR_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 && !c->client); + + for (;;) { + if (u) + 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_ERROR_CONNECTIONREFUSED); + goto finish; + } + + pa_log_debug(__FILE__": Trying to connect to %s...\n", 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: + if (u) + pa_xfree(u); + + return r; +} + +static void on_connection(pa_socket_client *client, pa_iochannel*io, void *userdata) { + pa_context *c = userdata; + assert(client && c && 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_ERROR_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, int spawn, const pa_spawn_api *api) { + int r = -1; + assert(c && c->ref >= 1 && c->state == PA_CONTEXT_UNCONNECTED); + + 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_ERROR_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 (spawn && 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); + pa_context_set_state(c, PA_CONTEXT_TERMINATED); +} + +pa_context_state_t pa_context_get_state(pa_context *c) { + assert(c && c->ref >= 1); + return c->state; +} + +int pa_context_errno(pa_context *c) { + assert(c && c->ref >= 1); + return c->error; +} + +void pa_context_set_state_callback(pa_context *c, void (*cb)(pa_context *c, void *userdata), void *userdata) { + assert(c && c->ref >= 1); + c->state_callback = cb; + c->state_userdata = userdata; +} + +int pa_context_is_pending(pa_context *c) { + assert(c && c->ref >= 1); + +/* pa_log("pstream: %i\n", pa_pstream_is_pending(c->pstream)); */ +/* pa_log("pdispatch: %i\n", pa_pdispatch_is_pending(c->pdispatch)); */ + + 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 && o->context && o->context->ref >= 1 && o->ref >= 1 && 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) + pa_operation_ref(o); + else { + if (o->callback) { + void (*cb)(pa_context *c, void *userdata); + cb = (void (*)(pa_context*, void*)) o->callback; + cb(o->context, o->userdata); + } + + pa_operation_done(o); + } + + pa_operation_unref(o); +} + +pa_operation* pa_context_drain(pa_context *c, void (*cb) (pa_context*c, void *userdata), void *userdata) { + pa_operation *o; + assert(c && c->ref >= 1); + + if (c->state != PA_CONTEXT_READY) + return NULL; + + if (!pa_context_is_pending(c)) + return NULL; + + o = pa_operation_new(c, NULL); + assert(o); + o->callback = (pa_operation_callback) cb; + o->userdata = userdata; + + set_dispatch_callbacks(pa_operation_ref(o)); + + return o; +} + +void pa_context_exit_daemon(pa_context *c) { + pa_tagstruct *t; + assert(c && c->ref >= 1); + + t = pa_tagstruct_new(NULL, 0); + assert(t); + pa_tagstruct_putu32(t, PA_COMMAND_EXIT); + pa_tagstruct_putu32(t, c->ctag++); + pa_pstream_send_tagstruct(c->pstream, t); +} + +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 && o && o->context && o->ref >= 1); + + 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_ERROR_PROTOCOL); + goto finish; + } + + if (o->callback) { + void (*cb)(pa_context *c, int _success, void *_userdata) = (void (*)(pa_context *c, int _success, void *_userdata)) o->callback; + cb(o->context, success, o->userdata); + } + +finish: + pa_operation_done(o); + pa_operation_unref(o); +} + +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) { + pa_tagstruct *t; + pa_operation *o; + uint32_t tag; + assert(c && cb); + + o = pa_operation_new(c, NULL); + o->callback = cb; + o->userdata = userdata; + + t = pa_tagstruct_new(NULL, 0); + pa_tagstruct_putu32(t, command); + pa_tagstruct_putu32(t, tag = c->ctag++); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, internal_callback, o); + + return pa_operation_ref(o); +} + +pa_operation* pa_context_set_default_sink(pa_context *c, const char *name, void(*cb)(pa_context*c, int success, void *userdata), void *userdata) { + pa_tagstruct *t; + pa_operation *o; + uint32_t tag; + assert(c && cb); + + o = pa_operation_new(c, NULL); + o->callback = (pa_operation_callback) cb; + o->userdata = userdata; + + t = pa_tagstruct_new(NULL, 0); + pa_tagstruct_putu32(t, PA_COMMAND_SET_DEFAULT_SINK); + pa_tagstruct_putu32(t, tag = c->ctag++); + 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, o); + + return pa_operation_ref(o); +} + +pa_operation* pa_context_set_default_source(pa_context *c, const char *name, void(*cb)(pa_context*c, int success, void *userdata), void *userdata) { + pa_tagstruct *t; + pa_operation *o; + uint32_t tag; + assert(c && cb); + + o = pa_operation_new(c, NULL); + o->callback = (pa_operation_callback) cb; + o->userdata = userdata; + + t = pa_tagstruct_new(NULL, 0); + pa_tagstruct_putu32(t, PA_COMMAND_SET_DEFAULT_SOURCE); + pa_tagstruct_putu32(t, tag = c->ctag++); + 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, o); + + return pa_operation_ref(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, void(*cb)(pa_context*c, int success, void *userdata), void *userdata) { + pa_tagstruct *t; + pa_operation *o; + uint32_t tag; + assert(c && name && cb); + + o = pa_operation_new(c, NULL); + o->callback = (pa_operation_callback) cb; + o->userdata = userdata; + + t = pa_tagstruct_new(NULL, 0); + pa_tagstruct_putu32(t, PA_COMMAND_SET_CLIENT_NAME); + pa_tagstruct_putu32(t, tag = c->ctag++); + 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, o); + + return pa_operation_ref(o); +} + +const char* pa_get_library_version(void) { + return PACKAGE_VERSION; +} + +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; +} diff --git a/src/polyp/context.h b/src/polyp/context.h new file mode 100644 index 00000000..6496c703 --- /dev/null +++ b/src/polyp/context.h @@ -0,0 +1,117 @@ +#ifndef foopolyplibcontexthfoo +#define foopolyplibcontexthfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio 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. + + polypaudio 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 polypaudio; 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 + * Connection contexts for asynchrononous communication with a + * server. A pa_context object wraps a connection to a polypaudio + * server using its native protocol. A context may be used to issue + * commands on the server or to create playback or recording + * streams. Multiple playback streams may be piped through a single + * connection context. Operations on the contect involving + * communication with the server are executed asynchronously: i.e. the + * client function do not implicitely wait for completion of the + * operation on the server. Instead the caller specifies a call back + * function that is called when the operation is completed. Currently + * running operations may be canceled using pa_operation_cancel(). */ + +/** \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 + +/** \pa_context + * An opaque connection context to a daemon */ +typedef struct pa_context pa_context; + +/** 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); + +typedef void (*pa_context_state_callback)(pa_context *c, void *userdata); + +/** Set a callback function that is called whenever the context status changes */ +void pa_context_set_state_callback(pa_context *c, pa_context_state_callback callback, 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 spawn is non-zero +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, int spawn, 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, void (*cb) (pa_context*c, void *userdata), void *userdata); + +/** Tell the daemon to exit. No operation object is returned as the + * connection is terminated when the daemon quits, thus this operation + * would never complete. */ +void pa_context_exit_daemon(pa_context *c); + +/** Set the name of the default sink. \since 0.4 */ +pa_operation* pa_context_set_default_sink(pa_context *c, const char *name, void(*cb)(pa_context*c, int success, void *userdata), 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, void(*cb)(pa_context*c, int success, void *userdata), 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, void(*cb)(pa_context*c, int success, void *userdata), void *userdata); + +/** Return the server name this context is connected to. \since 0.7 */ +const char* pa_context_get_server(pa_context *c); + +PA_C_DECL_END + +#endif diff --git a/src/polyp/def.h b/src/polyp/def.h new file mode 100644 index 00000000..0591ce6c --- /dev/null +++ b/src/polyp/def.h @@ -0,0 +1,213 @@ +#ifndef foopolyplibdefhfoo +#define foopolyplibdefhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio 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. + + polypaudio 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 polypaudio; 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_DISCONNECTED, /**< 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) + +/** 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_LATENCY = 2 /**< Interpolate the latency for + * this stream. When enabled, + * you can use + * pa_stream_interpolated_xxx() + * for synchronization. Using + * these functions instead of + * pa_stream_get_latency() has + * the advantage of not + * requiring a whole roundtrip + * for responses. Consider using + * this option when frequently + * requesting latency + * information. This is + * especially useful on long latency + * network connections. */ +} 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_ERROR_OK, /**< No error */ + PA_ERROR_ACCESS, /**< Access failure */ + PA_ERROR_COMMAND, /**< Unknown command */ + PA_ERROR_INVALID, /**< Invalid argument */ + PA_ERROR_EXIST, /**< Entity exists */ + PA_ERROR_NOENTITY, /**< No such entity */ + PA_ERROR_CONNECTIONREFUSED, /**< Connection refused */ + PA_ERROR_PROTOCOL, /**< Protocol error */ + PA_ERROR_TIMEOUT, /**< Timeout */ + PA_ERROR_AUTHKEY, /**< No authorization key */ + PA_ERROR_INTERNAL, /**< Internal error */ + PA_ERROR_CONNECTIONTERMINATED, /**< Connection terminated */ + PA_ERROR_KILLED, /**< Entity killed */ + PA_ERROR_INVALIDSERVER, /**< Invalid server */ + PA_ERROR_INITFAILED, /**< Module initialization failed */ + PA_ERROR_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_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 latency info. See pa_stream_get_latency(). 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. The output buffer to which + * buffer_usec relates may be manipulated freely (with + * pa_stream_write()'s delta argument, pa_stream_flush() and friends), + * the buffers sink_usec/source_usec relates to is a first-in + * first-out buffer 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.*/ +typedef struct pa_latency_info { + pa_usec_t buffer_usec; /**< Time in usecs the current buffer takes to play. For both playback and record streams. */ + 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. */ + uint32_t queue_length; /**< Queue size in bytes. For both playback and record streams. */ + 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 */ + struct timeval timestamp; /**< The time when this latency info was current */ + uint64_t counter; /**< The byte counter current when the latency info was requested. \since 0.6 */ +} pa_latency_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; + +PA_C_DECL_END + +#endif diff --git a/src/polyp/error.c b/src/polyp/error.c new file mode 100644 index 00000000..a137ab49 --- /dev/null +++ b/src/polyp/error.c @@ -0,0 +1,54 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio 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. + + polypaudio 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 polypaudio; 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 "error.h" +#include + +static const char* const errortab[PA_ERROR_MAX] = { + [PA_ERROR_OK] = "OK", + [PA_ERROR_ACCESS] = "Access denied", + [PA_ERROR_COMMAND] = "Unknown command", + [PA_ERROR_INVALID] = "Invalid argument", + [PA_ERROR_EXIST] = "Entity exists", + [PA_ERROR_NOENTITY] = "No such entity", + [PA_ERROR_CONNECTIONREFUSED] = "Connection refused", + [PA_ERROR_PROTOCOL] = "Protocol error", + [PA_ERROR_TIMEOUT] = "Timeout", + [PA_ERROR_AUTHKEY] = "No authorization key", + [PA_ERROR_INTERNAL] = "Internal error", + [PA_ERROR_CONNECTIONTERMINATED] = "Connection terminated", + [PA_ERROR_KILLED] = "Entity killed", + [PA_ERROR_INVALIDSERVER] = "Invalid server", +}; + +const char*pa_strerror(uint32_t error) { + if (error >= PA_ERROR_MAX) + return NULL; + + return errortab[error]; +} diff --git a/src/polyp/error.h b/src/polyp/error.h new file mode 100644 index 00000000..1bb97822 --- /dev/null +++ b/src/polyp/error.h @@ -0,0 +1,38 @@ +#ifndef foopolypliberrorhfoo +#define foopolypliberrorhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio 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. + + polypaudio 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 polypaudio; 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(uint32_t error); + +PA_C_DECL_END + +#endif diff --git a/src/polyp/internal.h b/src/polyp/internal.h new file mode 100644 index 00000000..feb9f6f4 --- /dev/null +++ b/src/polyp/internal.h @@ -0,0 +1,154 @@ +#ifndef foopolyplibinternalhfoo +#define foopolyplibinternalhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio 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. + + polypaudio 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 polypaudio; 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 "context.h" +#include "stream.h" +#include "operation.h" +#include +#include +#include +#include +#include + +#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 ctag; + uint32_t error; + pa_context_state_t state; + + void (*state_callback)(pa_context*c, void *userdata); + void *state_userdata; + + void (*subscribe_callback)(pa_context *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata); + 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; +}; + +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; + uint32_t channel; + int channel_valid; + uint32_t device_index; + pa_stream_direction_t direction; + uint32_t requested_bytes; + uint64_t counter; + pa_usec_t previous_time; + pa_usec_t previous_ipol_time; + pa_stream_state_t state; + pa_mcalign *mcalign; + + int interpolate; + int corked; + + uint32_t ipol_usec; + struct timeval ipol_timestamp; + pa_time_event *ipol_event; + int ipol_requested; + + void (*state_callback)(pa_stream*c, void *userdata); + void *state_userdata; + + void (*read_callback)(pa_stream *p, const void*data, size_t length, void *userdata); + void *read_userdata; + + void (*write_callback)(pa_stream *p, size_t length, void *userdata); + void *write_userdata; +}; + +typedef void (*pa_operation_callback)(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_callback 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); + +pa_operation *pa_operation_new(pa_context *c, pa_stream *s); +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); +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); + +void pa_stream_trash_ipol(pa_stream *s); + + +#endif diff --git a/src/polyp/introspect.c b/src/polyp/introspect.c new file mode 100644 index 00000000..d89eb9ed --- /dev/null +++ b/src/polyp/introspect.c @@ -0,0 +1,1003 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio 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. + + polypaudio 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 polypaudio; 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 "introspect.h" +#include "context.h" +#include "internal.h" +#include +#include + +/*** 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 && o && o->context && o->ref >= 1); + + 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_ERROR_PROTOCOL); + goto finish; + } + + if (o->callback) { + void (*cb)(pa_context *s, const pa_stat_info*_i, void *_userdata) = (void (*)(pa_context *s, const pa_stat_info*_i, void *_userdata)) 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, void (*cb)(pa_context *c, const pa_stat_info*i, void *userdata), void *userdata) { + return pa_context_send_simple_command(c, PA_COMMAND_STAT, context_stat_callback, (pa_operation_callback) 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 && o && o->context && o->ref >= 1); + + 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_ERROR_PROTOCOL); + goto finish; + } + + if (o->callback) { + void (*cb)(pa_context *s, const pa_server_info*_i, void *_userdata) = (void (*)(pa_context *s, const pa_server_info*_i, void *_userdata)) 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, void (*cb)(pa_context *c, const pa_server_info*i, void *userdata), void *userdata) { + return pa_context_send_simple_command(c, PA_COMMAND_GET_SERVER_INFO, context_get_server_info_callback, (pa_operation_callback) 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 eof = 1; + assert(pd && o && o->context && o->ref >= 1); + + if (command != PA_COMMAND_REPLY) { + if (pa_context_handle_error(o->context, command, t) < 0) + goto finish; + + eof = -1; + } else { + + 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_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_context_fail(o->context, PA_ERROR_PROTOCOL); + goto finish; + } + + if (o->callback) { + void (*cb)(pa_context *s, const pa_sink_info*_i, int _eof, void *_userdata) = (void (*)(pa_context *s, const pa_sink_info*_i, int _eof, void *_userdata)) o->callback; + cb(o->context, &i, 0, o->userdata); + } + } + } + + if (o->callback) { + void (*cb)(pa_context *s, const pa_sink_info*_i, int _eof, void *_userdata) = (void (*)(pa_context *s, const pa_sink_info*_i, int _eof, void *_userdata)) o->callback; + cb(o->context, NULL, eof, o->userdata); + } + +finish: + pa_operation_done(o); + pa_operation_unref(o); +} + +pa_operation* pa_context_get_sink_info_list(pa_context *c, void (*cb)(pa_context *c, const pa_sink_info *i, int is_last, void *userdata), void *userdata) { + return pa_context_send_simple_command(c, PA_COMMAND_GET_SINK_INFO_LIST, context_get_sink_info_callback, (pa_operation_callback) cb, userdata); +} + +pa_operation* pa_context_get_sink_info_by_index(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, const pa_sink_info *i, int is_last, void *userdata), void *userdata) { + pa_tagstruct *t; + pa_operation *o; + uint32_t tag; + assert(c && cb); + + o = pa_operation_new(c, NULL); + o->callback = (pa_operation_callback) cb; + o->userdata = userdata; + + t = pa_tagstruct_new(NULL, 0); + pa_tagstruct_putu32(t, PA_COMMAND_GET_SINK_INFO); + pa_tagstruct_putu32(t, tag = c->ctag++); + 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, o); + + return pa_operation_ref(o); +} + +pa_operation* pa_context_get_sink_info_by_name(pa_context *c, const char *name, void (*cb)(pa_context *c, const pa_sink_info *i, int is_last, void *userdata), void *userdata) { + pa_tagstruct *t; + pa_operation *o; + uint32_t tag; + assert(c && cb); + + o = pa_operation_new(c, NULL); + o->callback = (pa_operation_callback) cb; + o->userdata = userdata; + + t = pa_tagstruct_new(NULL, 0); + pa_tagstruct_putu32(t, PA_COMMAND_GET_SINK_INFO); + pa_tagstruct_putu32(t, tag = c->ctag++); + 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, o); + + return pa_operation_ref(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 eof = 1; + assert(pd && o && o->context && o->ref >= 1); + + if (command != PA_COMMAND_REPLY) { + if (pa_context_handle_error(o->context, command, t) < 0) + goto finish; + + eof = -1; + } else { + + while (!pa_tagstruct_eof(t)) { + pa_source_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_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_context_fail(o->context, PA_ERROR_PROTOCOL); + goto finish; + } + + if (o->callback) { + void (*cb)(pa_context *s, const pa_source_info*_i, int _eof, void *_userdata) = (void (*)(pa_context *s, const pa_source_info*_i, int _eof, void *_userdata)) o->callback; + cb(o->context, &i, 0, o->userdata); + } + } + } + + if (o->callback) { + void (*cb)(pa_context *s, const pa_source_info*_i, int _eof, void *_userdata) = (void (*)(pa_context *s, const pa_source_info*_i, int _eof, void *_userdata)) o->callback; + cb(o->context, NULL, eof, o->userdata); + } + +finish: + pa_operation_done(o); + pa_operation_unref(o); +} + +pa_operation* pa_context_get_source_info_list(pa_context *c, void (*cb)(pa_context *c, const pa_source_info *i, int is_last, void *userdata), void *userdata) { + return pa_context_send_simple_command(c, PA_COMMAND_GET_SOURCE_INFO_LIST, context_get_source_info_callback, (pa_operation_callback) cb, userdata); +} + +pa_operation* pa_context_get_source_info_by_index(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, const pa_source_info *i, int is_last, void *userdata), void *userdata) { + pa_tagstruct *t; + pa_operation *o; + uint32_t tag; + assert(c && cb); + + o = pa_operation_new(c, NULL); + o->callback = (pa_operation_callback) cb; + o->userdata = userdata; + + t = pa_tagstruct_new(NULL, 0); + pa_tagstruct_putu32(t, PA_COMMAND_GET_SOURCE_INFO); + pa_tagstruct_putu32(t, tag = c->ctag++); + 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, o); + + return pa_operation_ref(o); +} + +pa_operation* pa_context_get_source_info_by_name(pa_context *c, const char *name, void (*cb)(pa_context *c, const pa_source_info *i, int is_last, void *userdata), void *userdata) { + pa_tagstruct *t; + pa_operation *o; + uint32_t tag; + assert(c && cb); + + o = pa_operation_new(c, NULL); + o->callback = (pa_operation_callback) cb; + o->userdata = userdata; + + t = pa_tagstruct_new(NULL, 0); + pa_tagstruct_putu32(t, PA_COMMAND_GET_SOURCE_INFO); + pa_tagstruct_putu32(t, tag = c->ctag++); + 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, o); + + return pa_operation_ref(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 eof = 1; + assert(pd && o && o->context && o->ref >= 1); + + if (command != PA_COMMAND_REPLY) { + if (pa_context_handle_error(o->context, command, t) < 0) + goto finish; + + eof = -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_ERROR_PROTOCOL); + goto finish; + } + + if (o->callback) { + void (*cb)(pa_context *s, const pa_client_info*_i, int _eof, void *_userdata) = (void (*)(pa_context *s, const pa_client_info*_i, int _eof, void *_userdata)) o->callback; + cb(o->context, &i, 0, o->userdata); + } + } + } + + if (o->callback) { + void (*cb)(pa_context *s, const pa_client_info*_i, int _eof, void *_userdata) = (void (*)(pa_context *s, const pa_client_info*_i, int _eof, void *_userdata)) o->callback; + cb(o->context, NULL, eof, o->userdata); + } + +finish: + pa_operation_done(o); + pa_operation_unref(o); +} + +pa_operation* pa_context_get_client_info(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, const pa_client_info*i, int is_last, void *userdata), void *userdata) { + pa_tagstruct *t; + pa_operation *o; + uint32_t tag; + assert(c && cb); + + o = pa_operation_new(c, NULL); + o->callback = (pa_operation_callback) cb; + o->userdata = userdata; + + t = pa_tagstruct_new(NULL, 0); + pa_tagstruct_putu32(t, PA_COMMAND_GET_CLIENT_INFO); + pa_tagstruct_putu32(t, tag = c->ctag++); + 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, o); + + return pa_operation_ref(o); +} + +pa_operation* pa_context_get_client_info_list(pa_context *c, void (*cb)(pa_context *c, const pa_client_info*i, int is_last, void *userdata), void *userdata) { + return pa_context_send_simple_command(c, PA_COMMAND_GET_CLIENT_INFO_LIST, context_get_client_info_callback, (pa_operation_callback) 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 eof = 1; + assert(pd && o && o->context && o->ref >= 1); + + if (command != PA_COMMAND_REPLY) { + if (pa_context_handle_error(o->context, command, t) < 0) + goto finish; + + eof = -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_ERROR_PROTOCOL); + goto finish; + } + + if (o->callback) { + void (*cb)(pa_context *s, const pa_module_info*_i, int _eof, void *_userdata) = (void (*)(pa_context *s, const pa_module_info*_i, int _eof, void *_userdata)) o->callback; + cb(o->context, &i, 0, o->userdata); + } + } + } + + if (o->callback) { + void (*cb)(pa_context *s, const pa_module_info*_i, int _eof, void *_userdata) = (void (*)(pa_context *s, const pa_module_info*_i, int _eof, void *_userdata)) o->callback; + cb(o->context, NULL, eof, o->userdata); + } + +finish: + pa_operation_done(o); + pa_operation_unref(o); +} + +pa_operation* pa_context_get_module_info(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, const pa_module_info*i, int is_last, void *userdata), void *userdata) { + pa_tagstruct *t; + pa_operation *o; + uint32_t tag; + assert(c && cb); + + o = pa_operation_new(c, NULL); + o->callback = (pa_operation_callback) cb; + o->userdata = userdata; + + t = pa_tagstruct_new(NULL, 0); + pa_tagstruct_putu32(t, PA_COMMAND_GET_MODULE_INFO); + pa_tagstruct_putu32(t, tag = c->ctag++); + 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, o); + + return pa_operation_ref(o); +} + +pa_operation* pa_context_get_module_info_list(pa_context *c, void (*cb)(pa_context *c, const pa_module_info*i, int is_last, void *userdata), void *userdata) { + return pa_context_send_simple_command(c, PA_COMMAND_GET_MODULE_INFO_LIST, context_get_module_info_callback, (pa_operation_callback) 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 eof = 1; + assert(pd && o && o->context && o->ref >= 1); + + if (command != PA_COMMAND_REPLY) { + if (pa_context_handle_error(o->context, command, t) < 0) + goto finish; + + eof = -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_ERROR_PROTOCOL); + goto finish; + } + + if (o->callback) { + void (*cb)(pa_context *s, const pa_sink_input_info*_i, int _eof, void *_userdata) = (void (*)(pa_context *s, const pa_sink_input_info*_i, int _eof, void *_userdata)) o->callback; + cb(o->context, &i, 0, o->userdata); + } + } + } + + if (o->callback) { + void (*cb)(pa_context *s, const pa_sink_input_info*_i, int _eof, void *_userdata) = (void (*)(pa_context *s, const pa_sink_input_info*_i, int _eof, void *_userdata)) o->callback; + cb(o->context, NULL, eof, 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, void (*cb)(pa_context *c, const pa_sink_input_info*i, int is_last, void *userdata), void *userdata) { + pa_tagstruct *t; + pa_operation *o; + uint32_t tag; + assert(c && cb); + + o = pa_operation_new(c, NULL); + o->callback = (pa_operation_callback) cb; + o->userdata = userdata; + + t = pa_tagstruct_new(NULL, 0); + pa_tagstruct_putu32(t, PA_COMMAND_GET_SINK_INPUT_INFO); + pa_tagstruct_putu32(t, tag = c->ctag++); + 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, o); + + return pa_operation_ref(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_callback) 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 eof = 1; + assert(pd && o && o->context && o->ref >= 1); + + if (command != PA_COMMAND_REPLY) { + if (pa_context_handle_error(o->context, command, t) < 0) + goto finish; + + eof = -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_ERROR_PROTOCOL); + goto finish; + } + + if (o->callback) { + void (*cb)(pa_context *s, const pa_source_output_info*_i, int _eof, void *_userdata) = (void (*)(pa_context *s, const pa_source_output_info*_i, int _eof, void *_userdata)) o->callback; + cb(o->context, &i, 0, o->userdata); + } + } + } + + if (o->callback) { + void (*cb)(pa_context *s, const pa_source_output_info*_i, int _eof, void *_userdata) = (void (*)(pa_context *s, const pa_source_output_info*_i, int _eof, void *_userdata))o->callback; + cb(o->context, NULL, eof, 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, void (*cb)(pa_context *c, const pa_source_output_info*i, int is_last, void *userdata), void *userdata) { + pa_tagstruct *t; + pa_operation *o; + uint32_t tag; + assert(c && cb); + + o = pa_operation_new(c, NULL); + o->callback = (pa_operation_callback) cb; + o->userdata = userdata; + + t = pa_tagstruct_new(NULL, 0); + pa_tagstruct_putu32(t, PA_COMMAND_GET_SOURCE_OUTPUT_INFO); + pa_tagstruct_putu32(t, tag = c->ctag++); + 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, o); + + return pa_operation_ref(o); +} + +pa_operation* pa_context_get_source_output_info_list(pa_context *c, void (*cb)(pa_context *c, const pa_source_output_info*i, int is_last, void *userdata), void *userdata) { + return pa_context_send_simple_command(c, PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST, context_get_source_output_info_callback, (pa_operation_callback) cb, userdata); +} + +/*** Volume manipulation ***/ + +pa_operation* pa_context_set_sink_volume_by_index(pa_context *c, uint32_t idx, const pa_cvolume *volume, void (*cb)(pa_context *c, int success, void *userdata), void *userdata) { + pa_operation *o; + pa_tagstruct *t; + uint32_t tag; + assert(c && idx != PA_INVALID_INDEX); + + o = pa_operation_new(c, NULL); + o->callback = (pa_operation_callback) cb; + o->userdata = userdata; + + t = pa_tagstruct_new(NULL, 0); + pa_tagstruct_putu32(t, PA_COMMAND_SET_SINK_VOLUME); + pa_tagstruct_putu32(t, tag = c->ctag++); + 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, o); + + return pa_operation_ref(o); +} + +pa_operation* pa_context_set_sink_volume_by_name(pa_context *c, const char *name, const pa_cvolume *volume, void (*cb)(pa_context *c, int success, void *userdata), void *userdata) { + pa_operation *o; + pa_tagstruct *t; + uint32_t tag; + assert(c && name); + + o = pa_operation_new(c, NULL); + o->callback = (pa_operation_callback) cb; + o->userdata = userdata; + + t = pa_tagstruct_new(NULL, 0); + pa_tagstruct_putu32(t, PA_COMMAND_SET_SINK_VOLUME); + pa_tagstruct_putu32(t, tag = c->ctag++); + 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, o); + + return pa_operation_ref(o); +} + +pa_operation* pa_context_set_sink_input_volume(pa_context *c, uint32_t idx, const pa_cvolume *volume, void (*cb)(pa_context *c, int success, void *userdata), void *userdata) { + pa_operation *o; + pa_tagstruct *t; + uint32_t tag; + assert(c && idx != PA_INVALID_INDEX); + + o = pa_operation_new(c, NULL); + o->callback = (pa_operation_callback) cb; + o->userdata = userdata; + + t = pa_tagstruct_new(NULL, 0); + pa_tagstruct_putu32(t, PA_COMMAND_SET_SINK_INPUT_VOLUME); + pa_tagstruct_putu32(t, tag = c->ctag++); + 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, o); + + return pa_operation_ref(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 eof = 1; + assert(pd && o && o->context && o->ref >= 1); + + if (command != PA_COMMAND_REPLY) { + if (pa_context_handle_error(o->context, command, t) < 0) + goto finish; + + eof = -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_ERROR_PROTOCOL); + goto finish; + } + + if (o->callback) { + void (*cb)(pa_context *s, const pa_sample_info*_i, int _eof, void *_userdata) = (void (*)(pa_context *s, const pa_sample_info*_i, int _eof, void *_userdata)) o->callback; + cb(o->context, &i, 0, o->userdata); + } + } + } + + if (o->callback) { + void (*cb)(pa_context *s, const pa_sample_info*_i, int _eof, void *_userdata) = (void (*)(pa_context *s, const pa_sample_info*_i, int _eof, void *_userdata)) o->callback; + cb(o->context, NULL, eof, 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, void (*cb)(pa_context *c, const pa_sample_info *i, int is_last, void *userdata), void *userdata) { + pa_tagstruct *t; + pa_operation *o; + uint32_t tag; + assert(c && cb && name); + + o = pa_operation_new(c, NULL); + o->callback = (pa_operation_callback) cb; + o->userdata = userdata; + + t = pa_tagstruct_new(NULL, 0); + pa_tagstruct_putu32(t, PA_COMMAND_GET_SAMPLE_INFO); + pa_tagstruct_putu32(t, tag = c->ctag++); + 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, o); + + return pa_operation_ref(o); +} + +pa_operation* pa_context_get_sample_info_by_index(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, const pa_sample_info *i, int is_last, void *userdata), void *userdata) { + pa_tagstruct *t; + pa_operation *o; + uint32_t tag; + assert(c && cb); + + o = pa_operation_new(c, NULL); + o->callback = (pa_operation_callback) cb; + o->userdata = userdata; + + t = pa_tagstruct_new(NULL, 0); + pa_tagstruct_putu32(t, PA_COMMAND_GET_SAMPLE_INFO); + pa_tagstruct_putu32(t, tag = c->ctag++); + 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, o); + + return pa_operation_ref(o); +} + +pa_operation* pa_context_get_sample_info_list(pa_context *c, void (*cb)(pa_context *c, const pa_sample_info *i, int is_last, void *userdata), void *userdata) { + return pa_context_send_simple_command(c, PA_COMMAND_GET_SAMPLE_INFO_LIST, context_get_sample_info_callback, (pa_operation_callback) cb, userdata); +} + +static pa_operation* command_kill(pa_context *c, uint32_t command, uint32_t idx, void (*cb)(pa_context *c, int success, void *userdata), void *userdata) { + pa_operation *o; + pa_tagstruct *t; + uint32_t tag; + assert(c && idx != PA_INVALID_INDEX); + + o = pa_operation_new(c, NULL); + o->callback = (pa_operation_callback) cb; + o->userdata = userdata; + + t = pa_tagstruct_new(NULL, 0); + pa_tagstruct_putu32(t, command); + pa_tagstruct_putu32(t, tag = c->ctag++); + 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, o); + + return pa_operation_ref(o); +} + +pa_operation* pa_context_kill_client(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, int success, void *userdata), 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, void (*cb)(pa_context *c, int success, void *userdata), 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, void (*cb)(pa_context *c, int success, void *userdata), void *userdata) { + return command_kill(c, PA_COMMAND_KILL_SOURCE_OUTPUT, idx, cb, userdata); +} + +static void load_module_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 = -1; + assert(pd && o && o->context && o->ref >= 1); + + if (command != PA_COMMAND_REPLY) { + if (pa_context_handle_error(o->context, command, t) < 0) + goto finish; + + } else if (pa_tagstruct_getu32(t, &idx) < 0 || + !pa_tagstruct_eof(t)) { + pa_context_fail(o->context, PA_ERROR_PROTOCOL); + goto finish; + } + + if (o->callback) { + void (*cb)(pa_context *c, uint32_t _idx, void *_userdata) = (void (*)(pa_context *c, uint32_t _idx, void *_userdata)) 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, void (*cb)(pa_context *c, uint32_t idx, void *userdata), void *userdata) { + pa_operation *o; + pa_tagstruct *t; + uint32_t tag; + assert(c && name && argument); + + o = pa_operation_new(c, NULL); + o->callback = (pa_operation_callback) cb; + o->userdata = userdata; + + t = pa_tagstruct_new(NULL, 0); + pa_tagstruct_putu32(t, PA_COMMAND_LOAD_MODULE); + pa_tagstruct_putu32(t, tag = c->ctag++); + 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, load_module_callback, o); + + return pa_operation_ref(o); +} + +pa_operation* pa_context_unload_module(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, int success, void *userdata), 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 eof = 1; + assert(pd && o && o->context && o->ref >= 1); + + if (command != PA_COMMAND_REPLY) { + if (pa_context_handle_error(o->context, command, t) < 0) + goto finish; + + eof = -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_ERROR_PROTOCOL); + goto finish; + } + + if (o->callback) { + void (*cb)(pa_context *s, const pa_autoload_info*_i, int _eof, void *_userdata) = (void (*)(pa_context *s, const pa_autoload_info*_i, int _eof, void *_userdata)) o->callback; + cb(o->context, &i, 0, o->userdata); + } + } + } + + if (o->callback) { + void (*cb)(pa_context *s, const pa_autoload_info*_i, int _eof, void *_userdata) = (void (*)(pa_context *s, const pa_autoload_info*_i, int _eof, void *_userdata)) o->callback; + cb(o->context, NULL, eof, 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, void (*cb)(pa_context *c, const pa_autoload_info *i, int is_last, void *userdata), void *userdata) { + pa_tagstruct *t; + pa_operation *o; + uint32_t tag; + assert(c && cb && name); + + o = pa_operation_new(c, NULL); + o->callback = (pa_operation_callback) cb; + o->userdata = userdata; + + t = pa_tagstruct_new(NULL, 0); + pa_tagstruct_putu32(t, PA_COMMAND_GET_AUTOLOAD_INFO); + pa_tagstruct_putu32(t, tag = c->ctag++); + 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, o); + + return pa_operation_ref(o); +} + +pa_operation* pa_context_get_autoload_info_by_index(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, const pa_autoload_info *i, int is_last, void *userdata), void *userdata) { + pa_tagstruct *t; + pa_operation *o; + uint32_t tag; + assert(c && cb && idx != PA_INVALID_INDEX); + + o = pa_operation_new(c, NULL); + o->callback = (pa_operation_callback) cb; + o->userdata = userdata; + + t = pa_tagstruct_new(NULL, 0); + pa_tagstruct_putu32(t, PA_COMMAND_GET_AUTOLOAD_INFO); + pa_tagstruct_putu32(t, tag = c->ctag++); + 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, o); + + return pa_operation_ref(o); +} + +pa_operation* pa_context_get_autoload_info_list(pa_context *c, void (*cb)(pa_context *c, const pa_autoload_info *i, int is_last, void *userdata), void *userdata) { + return pa_context_send_simple_command(c, PA_COMMAND_GET_AUTOLOAD_INFO_LIST, context_get_autoload_info_callback, (pa_operation_callback) cb, userdata); +} + +static void context_add_autoload_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 && o && o->context && o->ref >= 1); + + 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_ERROR_PROTOCOL); + goto finish; + } + + if (o->callback) { + void (*cb)(pa_context *s, uint32_t _idx, void *_userdata) = (void (*)(pa_context *s, uint32_t _idx, void *_userdata)) o->callback; + cb(o->context, idx, o->userdata); + } + + +finish: + pa_operation_done(o); + pa_operation_unref(o); +} + +pa_operation* pa_context_add_autoload(pa_context *c, const char *name, pa_autoload_type_t type, const char *module, const char*argument, void (*cb)(pa_context *c, int success, void *userdata), void* userdata) { + pa_operation *o; + pa_tagstruct *t; + uint32_t tag; + assert(c && name && module && argument); + + o = pa_operation_new(c, NULL); + o->callback = (pa_operation_callback) cb; + o->userdata = userdata; + + t = pa_tagstruct_new(NULL, 0); + pa_tagstruct_putu32(t, PA_COMMAND_ADD_AUTOLOAD); + pa_tagstruct_putu32(t, tag = c->ctag++); + 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_add_autoload_callback, o); + + return pa_operation_ref(o); +} + +pa_operation* pa_context_remove_autoload_by_name(pa_context *c, const char *name, pa_autoload_type_t type, void (*cb)(pa_context *c, int success, void *userdata), void* userdata) { + pa_operation *o; + pa_tagstruct *t; + uint32_t tag; + assert(c && name); + + o = pa_operation_new(c, NULL); + o->callback = (pa_operation_callback) cb; + o->userdata = userdata; + + t = pa_tagstruct_new(NULL, 0); + pa_tagstruct_putu32(t, PA_COMMAND_REMOVE_AUTOLOAD); + pa_tagstruct_putu32(t, tag = c->ctag++); + 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, o); + + return pa_operation_ref(o); +} + +pa_operation* pa_context_remove_autoload_by_index(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, int success, void *userdata), void* userdata) { + pa_operation *o; + pa_tagstruct *t; + uint32_t tag; + assert(c && idx != PA_INVALID_INDEX); + + o = pa_operation_new(c, NULL); + o->callback = (pa_operation_callback) cb; + o->userdata = userdata; + + t = pa_tagstruct_new(NULL, 0); + pa_tagstruct_putu32(t, PA_COMMAND_REMOVE_AUTOLOAD); + pa_tagstruct_putu32(t, tag = c->ctag++); + 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, o); + + return pa_operation_ref(o); +} diff --git a/src/polyp/introspect.h b/src/polyp/introspect.h new file mode 100644 index 00000000..75dc027f --- /dev/null +++ b/src/polyp/introspect.h @@ -0,0 +1,279 @@ +#ifndef foopolyplibintrospecthfoo +#define foopolyplibintrospecthfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio 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. + + polypaudio 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 polypaudio; 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 + +/** \file + * + * Routines for daemon introspection. When enumerating all entitites + * of a certain kind, use the pa_context_xxx_list() functions. The + * specified callback function is called once for each entry. The + * enumeration is finished by a call to the callback function with + * is_last=1 and i=NULL. Strings referenced in pa_xxx_info structures + * and the structures themselves point to internal memory that may not + * be modified. That memory is only valid during the call to the + * callback function. A deep copy is required if you need this data + * outside the callback functions. An error is signalled by a call to * the callback function with i=NULL and is_last=0. + * + * When using the routines that ask fo a single entry only, a callback + * with the same signature is used. However, no finishing call to the + * routine is issued. */ + +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.9 */ + uint32_t owner_module; /**< Index of the owning module of this sink, or PA_INVALID_INDEX */ + pa_cvolume volume; /**< Volume of the sink */ + 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.9 */ +} pa_sink_info; + +/** Get information about a sink by its name */ +pa_operation* pa_context_get_sink_info_by_name(pa_context *c, const char *name, void (*cb)(pa_context *c, const pa_sink_info *i, int is_last, void *userdata), 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, void (*cb)(pa_context *c, const pa_sink_info *i, int is_last, void *userdata), void *userdata); + +/** Get the complete sink list */ +pa_operation* pa_context_get_sink_info_list(pa_context *c, void (*cb)(pa_context *c, const pa_sink_info *i, int is_last, void *userdata), 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.9 */ + uint32_t owner_module; /**< Owning module index, or PA_INVALID_INDEX */ + 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.9 */ +} pa_source_info; + +/** Get information about a source by its name */ +pa_operation* pa_context_get_source_info_by_name(pa_context *c, const char *name, void (*cb)(pa_context *c, const pa_source_info *i, int is_last, void *userdata), 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, void (*cb)(pa_context *c, const pa_source_info *i, int is_last, void *userdata), void *userdata); + +/** Get the complete source list */ +pa_operation* pa_context_get_source_info_list(pa_context *c, void (*cb)(pa_context *c, const pa_source_info *i, int is_last, void *userdata), 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 "polypaudio") */ + 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 polypaudio. \since 0.8 */ +} pa_server_info; + +/** Get some information about the server */ +pa_operation* pa_context_get_server_info(pa_context *c, void (*cb)(pa_context *c, const pa_server_info*i, void *userdata), 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; + +/** Get some information about a module by its index */ +pa_operation* pa_context_get_module_info(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, const pa_module_info*i, int is_last, void *userdata), void *userdata); + +/** Get the complete list of currently loaded modules */ +pa_operation* pa_context_get_module_info_list(pa_context *c, void (*cb)(pa_context *c, const pa_module_info*i, int is_last, void *userdata), 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.9 */ +} pa_client_info; + +/** Get information about a client by its index */ +pa_operation* pa_context_get_client_info(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, const pa_client_info*i, int is_last, void *userdata), void *userdata); + +/** Get the complete client list */ +pa_operation* pa_context_get_client_info_list(pa_context *c, void (*cb)(pa_context *c, const pa_client_info*i, int is_last, void *userdata), 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.9 */ +} pa_sink_input_info; + +/** Get some information about a sink input by its index */ +pa_operation* pa_context_get_sink_input_info(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, const pa_sink_input_info*i, int is_last, void *userdata), void *userdata); + +/** Get the complete sink input list */ +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); + +/** 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.9 */ +} pa_source_output_info; + +/** Get information about a source output by its index */ +pa_operation* pa_context_get_source_output_info(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, const pa_source_output_info*i, int is_last, void *userdata), void *userdata); + +/** Get the complete list of source outputs */ +pa_operation* pa_context_get_source_output_info_list(pa_context *c, void (*cb)(pa_context *c, const pa_source_output_info*i, int is_last, void *userdata), 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, void (*cb)(pa_context *c, int success, void *userdata), 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, void (*cb)(pa_context *c, int success, void *userdata), 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, void (*cb)(pa_context *c, int success, void *userdata), 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; + +/** Get daemon memory block statistics */ +pa_operation* pa_context_stat(pa_context *c, void (*cb)(pa_context *c, const pa_stat_info *i, void *userdata), 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; + +/** Get information about a sample by its name */ +pa_operation* pa_context_get_sample_info_by_name(pa_context *c, const char *name, void (*cb)(pa_context *c, const pa_sample_info *i, int is_last, void *userdata), 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, void (*cb)(pa_context *c, const pa_sample_info *i, int is_last, void *userdata), void *userdata); + +/** Get the complete list of samples stored in the daemon. */ +pa_operation* pa_context_get_sample_info_list(pa_context *c, void (*cb)(pa_context *c, const pa_sample_info *i, int is_last, void *userdata), void *userdata); + +/** Kill a client. \since 0.5 */ +pa_operation* pa_context_kill_client(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, int success, void *userdata), void *userdata); + +/** Kill a sink input. \since 0.5 */ +pa_operation* pa_context_kill_sink_input(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, int success, void *userdata), void *userdata); + +/** Kill a source output. \since 0.5 */ +pa_operation* pa_context_kill_source_output(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, int success, void *userdata), void *userdata); + +/** Load a module. \since 0.5 */ +pa_operation* pa_context_load_module(pa_context *c, const char*name, const char *argument, void (*cb)(pa_context *c, uint32_t idx, void *userdata), void *userdata); + +/** Unload a module. \since 0.5 */ +pa_operation* pa_context_unload_module(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, int success, void *userdata), 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; + +/** 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, void (*cb)(pa_context *c, const pa_autoload_info *i, int is_last, void *userdata), 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, void (*cb)(pa_context *c, const pa_autoload_info *i, int is_last, void *userdata), void *userdata); + +/** Get the complete list of autoload entries. \since 0.5 */ +pa_operation* pa_context_get_autoload_info_list(pa_context *c, void (*cb)(pa_context *c, const pa_autoload_info *i, int is_last, void *userdata), 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, void (*cb)(pa_context *c, int idx, void *userdata), 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, void (*cb)(pa_context *c, int success, void *userdata), void* userdata); + +/** Remove an autoload entry. \since 0.6 */ +pa_operation* pa_context_remove_autoload_by_index(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, int success, void *userdata), void* userdata); + + +PA_C_DECL_END + +#endif diff --git a/src/polyp/operation.c b/src/polyp/operation.c new file mode 100644 index 00000000..0baa661b --- /dev/null +++ b/src/polyp/operation.c @@ -0,0 +1,103 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio 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. + + polypaudio 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 polypaudio; 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 *o; + assert(c); + + o = pa_xmalloc(sizeof(pa_operation)); + o->ref = 1; + o->context = pa_context_ref(c); + o->stream = s ? pa_stream_ref(s) : NULL; + + o->state = PA_OPERATION_RUNNING; + o->userdata = NULL; + o->callback = NULL; + + PA_LLIST_PREPEND(pa_operation, o->context->operations, o); + return pa_operation_ref(o); +} + +pa_operation *pa_operation_ref(pa_operation *o) { + assert(o && o->ref >= 1); + o->ref++; + return o; +} + +void pa_operation_unref(pa_operation *o) { + assert(o && o->ref >= 1); + + if ((--(o->ref)) == 0) { + assert(!o->context); + assert(!o->stream); + free(o); + } +} + +static void operation_set_state(pa_operation *o, pa_operation_state_t st) { + assert(o && o->ref >= 1); + + if (st == o->state) + return; + + if (!o->context) + return; + + o->state = st; + + if ((o->state == PA_OPERATION_DONE) || (o->state == PA_OPERATION_CANCELED)) { + PA_LLIST_REMOVE(pa_operation, o->context->operations, o); + pa_context_unref(o->context); + if (o->stream) + pa_stream_unref(o->stream); + o->context = NULL; + o->stream = NULL; + o->callback = NULL; + o->userdata = NULL; + + pa_operation_unref(o); + } +} + +void pa_operation_cancel(pa_operation *o) { + assert(o && o->ref >= 1); + operation_set_state(o, PA_OPERATION_CANCELED); +} + +void pa_operation_done(pa_operation *o) { + assert(o && o->ref >= 1); + operation_set_state(o, PA_OPERATION_DONE); +} + +pa_operation_state_t pa_operation_get_state(pa_operation *o) { + assert(o && o->ref >= 1); + return o->state; +} diff --git a/src/polyp/operation.h b/src/polyp/operation.h new file mode 100644 index 00000000..fea82f10 --- /dev/null +++ b/src/polyp/operation.h @@ -0,0 +1,51 @@ +#ifndef foopolypliboperationhfoo +#define foopolypliboperationhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio 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. + + polypaudio 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 polypaudio; 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 + +/** \pa_operation + * 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/polyp/polypaudio.h b/src/polyp/polypaudio.h new file mode 100644 index 00000000..23208526 --- /dev/null +++ b/src/polyp/polypaudio.h @@ -0,0 +1,86 @@ +#ifndef foopolyplibhfoo +#define foopolyplibhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio 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. + + polypaudio 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 polypaudio; 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 + +/** \file + * Include all polyplib header file 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 and \ref scache.h \ref version.h + * at once */ + +/** \mainpage + * + * \section intro_sec Introduction + * + * This document describes the client API for the polypaudio sound + * server. The API comes in two flavours: + * + * \li The complete but somewhat complicated to use asynchronous API + * \li And the simplified, easy to use, but limited synchronous API + * + * The polypaudio client libraries are thread safe as long as all + * objects created by any library function are accessed from the thread + * that created them only. + * + * \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 + * \ref simple.h for more details. + * + * \section async_api Asynchronous API + * + * Use this if you develop your programs in asynchronous, main loop + * based style or want to use advanced features of the polypaudio + * API. A good starting point is \ref context.h + * + * The asynchronous API relies on an abstract main loop API that is + * described in \ref mainloop-api.h. Two distinct implementations are + * available: + * + * \li \ref mainloop.h: a minimal but fast implementation based on poll() + * \li \ref glib-mainloop.h: a wrapper around GLIB's main loop + * + * UNIX signals may be hooked to a main loop using the functions from + * \ref mainloop-signal.h + * + * \section pkgconfig pkg-config + * + * The polypaudio libraries provide pkg-config snippets for the different modules. To use the + * asynchronous API use "polyplib" as pkg-config file. GLIB main loop + * support is available as "glib-mainloop". The simple + * synchronous API is available as "simple". + */ + +#endif diff --git a/src/polyp/polyplib-browser.c b/src/polyp/polyplib-browser.c deleted file mode 100644 index 9a389484..00000000 --- a/src/polyp/polyplib-browser.c +++ /dev/null @@ -1,312 +0,0 @@ -/* $Id$ */ - -/*** - This file is part of polypaudio. - - polypaudio 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. - - polypaudio 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 polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#include -#include - -#include "polyplib-browser.h" -#include -#include -#include - -#define SERVICE_NAME_SINK "_polypaudio-sink._tcp." -#define SERVICE_NAME_SOURCE "_polypaudio-source._tcp." -#define SERVICE_NAME_SERVER "_polypaudio-server._tcp." - -pa_browser { - int ref; - pa_mainloop_api *mainloop; - - void (*callback)(pa_browser *z, pa_browse_opcode c, const pa_browse_info *i, void *userdata); - void *callback_userdata; - - sw_discovery discovery; - pa_io_event *io_event; -}; - - -static void io_callback(pa_mainloop_api*a, pa_io_event*e, int fd, pa_io_event_flags 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.\n"); - b->mainloop->io_free(b->io_event); - b->io_event = NULL; - return; - } -} - -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]; - pa_browse_opcode opcode; - int device_found = 0; - uint32_t cookie; - pa_typeid_t typeid; - 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; - - if (!strcmp(type, SERVICE_NAME_SINK)) - opcode = PA_BROWSE_NEW_SINK; - else if (!strcmp(type, SERVICE_NAME_SOURCE)) - opcode = PA_BROWSE_NEW_SOURCE; - else if (!strcmp(type, SERVICE_NAME_SERVER)) - opcode = PA_BROWSE_NEW_SERVER; - else - goto fail; - - - 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("sw_text_record_string_iterator_init() failed.\n"); - 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, "typeid")) { - - if (pa_atou(c, &typeid) < 0) - goto fail; - - i.typeid = &typeid; - } 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->callback_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("sw_discovery_resolve() failed\n"); - - break; - } - - case SW_DISCOVERY_BROWSE_REMOVE_SERVICE: - if (b->callback) { - pa_browse_info i; - memset(&i, 0, sizeof(i)); - i.name = name; - b->callback(b, PA_BROWSE_REMOVE, &i, b->callback_userdata); - } - break; - - default: - ; - } - - return SW_OKAY; -} - -pa_browser *pa_browser_new(pa_mainloop_api *mainloop) { - pa_browser *b; - sw_discovery_oid oid; - - b = pa_xmalloc(sizeof(pa_browser)); - b->mainloop = mainloop; - b->ref = 1; - b->callback = NULL; - b->callback_userdata = NULL; - - if (sw_discovery_init(&b->discovery) != SW_OKAY) { - pa_log("sw_discovery_init() failed.\n"); - 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("sw_discovery_browse() failed.\n"); - - 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, void (*cb)(pa_browser *z, pa_browse_opcode c, const pa_browse_info *i, void* userdata), void *userdata) { - assert(b); - - b->callback = cb; - b->callback_userdata = userdata; -} diff --git a/src/polyp/polyplib-browser.h b/src/polyp/polyplib-browser.h deleted file mode 100644 index 853304d7..00000000 --- a/src/polyp/polyplib-browser.h +++ /dev/null @@ -1,65 +0,0 @@ -#ifndef foopolyplibbrowserhfoo -#define foopolyplibbrowserhfoo - -/* $Id$ */ - -/*** - This file is part of polypaudio. - - polypaudio 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. - - polypaudio 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 polypaudio; 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 - -pa_browser; - -pa_browse_opcode { - PA_BROWSE_NEW_SERVER, - PA_BROWSE_NEW_SINK, - PA_BROWSE_NEW_SOURCE, - PA_BROWSE_REMOVE -}; - -pa_browser *pa_browser_new(pa_mainloop_api *mainloop); -pa_browser *pa_browser_ref(pa_browser *z); -void pa_browser_unref(pa_browser *z); - -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_typeid_t *typeid; /* optional */ - const pa_sample_spec *sample_spec; /* optional */ -}; - -void pa_browser_set_callback(pa_browser *z, void (*cb)(pa_browser *z, pa_browse_opcode c, const pa_browse_info *i, void *userdata), void *userdata); - -PA_C_DECL_END - -#endif diff --git a/src/polyp/polyplib-context.c b/src/polyp/polyplib-context.c deleted file mode 100644 index c392f0fc..00000000 --- a/src/polyp/polyplib-context.c +++ /dev/null @@ -1,871 +0,0 @@ -/* $Id$ */ - -/*** - This file is part of polypaudio. - - polypaudio 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. - - polypaudio 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 polypaudio; 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 - -#include "polyplib-internal.h" -#include "polyplib-context.h" -#include "polyplib-version.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef HAVE_X11 -#include "client-conf-x11.h" -#endif - -#define AUTOSPAWN_LOCK "autospawn.lock" - -static const pa_pdispatch_callback command_table[PA_COMMAND_MAX] = { - [PA_COMMAND_REQUEST] = pa_command_request, - [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 && name); - - c = pa_xmalloc(sizeof(pa_context)); - 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(); - assert(c->playback_streams && c->record_streams); - - PA_LLIST_HEAD_INIT(pa_stream, c->streams); - PA_LLIST_HEAD_INIT(pa_operation, c->operations); - - c->error = PA_ERROR_OK; - c->state = PA_CONTEXT_UNCONNECTED; - c->ctag = 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 && c->ref >= 1); - c->ref++; - return c; -} - -void pa_context_unref(pa_context *c) { - assert(c && 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); - - if (c->state == st) - return; - - pa_context_ref(c); - - 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; - } - - c->state = st; - if (c->state_callback) - c->state_callback(c, c->state_userdata); - - pa_context_unref(c); -} - -void pa_context_fail(pa_context *c, int error) { - assert(c); - c->error = error; - pa_context_set_state(c, PA_CONTEXT_FAILED); -} - -static void pstream_die_callback(pa_pstream *p, void *userdata) { - pa_context *c = userdata; - assert(p && c); - pa_context_fail(c, PA_ERROR_CONNECTIONTERMINATED); -} - -static void pstream_packet_callback(pa_pstream *p, pa_packet *packet, void *userdata) { - pa_context *c = userdata; - assert(p && packet && c); - - pa_context_ref(c); - - if (pa_pdispatch_run(c->pdispatch, packet, c) < 0) { - pa_log(__FILE__": invalid packet.\n"); - pa_context_fail(c, PA_ERROR_PROTOCOL); - } - - pa_context_unref(c); -} - -static void pstream_memblock_callback(pa_pstream *p, uint32_t channel, PA_GCC_UNUSED uint32_t delta, const pa_memchunk *chunk, void *userdata) { - pa_context *c = userdata; - pa_stream *s; - assert(p && chunk && c && chunk->memblock && chunk->memblock->data); - - pa_context_ref(c); - - if ((s = pa_dynarray_get(c->record_streams, channel))) { - pa_mcalign_push(s->mcalign, chunk); - - for (;;) { - pa_memchunk t; - - if (pa_mcalign_pop(s->mcalign, &t) < 0) - break; - - if (s->read_callback) { - s->read_callback(s, (uint8_t*) t.memblock->data + t.index, t.length, s->read_userdata); - s->counter += chunk->length; - } - - pa_memblock_unref(t.memblock); - } - } - - pa_context_unref(c); -} - -int pa_context_handle_error(pa_context *c, uint32_t command, pa_tagstruct *t) { - assert(c); - - if (command == PA_COMMAND_ERROR) { - assert(t); - - if (pa_tagstruct_getu32(t, &c->error) < 0) { - pa_context_fail(c, PA_ERROR_PROTOCOL); - return -1; - - } - } else if (command == PA_COMMAND_TIMEOUT) - c->error = PA_ERROR_TIMEOUT; - else { - pa_context_fail(c, PA_ERROR_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 && c && (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_ERROR_PROTOCOL); - - pa_context_fail(c, c->error); - goto finish; - } - - switch(c->state) { - case PA_CONTEXT_AUTHORIZING: { - pa_tagstruct *reply; - reply = pa_tagstruct_new(NULL, 0); - pa_tagstruct_putu32(reply, PA_COMMAND_SET_CLIENT_NAME); - pa_tagstruct_putu32(reply, tag = c->ctag++); - 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); - - 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 && io); - - pa_context_ref(c); - - assert(!c->pstream); - c->pstream = pa_pstream_new(c->mainloop, io, c->memblock_stat); - assert(c->pstream); - - 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); - assert(c->pdispatch); - - if (!c->conf->cookie_valid) { - pa_context_fail(c, PA_ERROR_AUTHKEY); - goto finish; - } - - t = pa_tagstruct_new(NULL, 0); - assert(t); - pa_tagstruct_putu32(t, PA_COMMAND_AUTH); - pa_tagstruct_putu32(t, tag = c->ctag++); - pa_tagstruct_put_arbitrary(t, c->conf->cookie, sizeof(c->conf->cookie)); - pa_pstream_send_tagstruct(c->pstream, t); - pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, setup_complete_callback, c); - - 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() failed: %s\n", strerror(errno)); - pa_context_fail(c, PA_ERROR_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() failed: %s\n", strerror(errno)); - pa_context_fail(c, PA_ERROR_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() failed: %s\n", strerror(errno)); - pa_context_fail(c, PA_ERROR_INTERNAL); - goto fail; - } else if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { - pa_context_fail(c, PA_ERROR_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 && !c->client); - - for (;;) { - if (u) - 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_ERROR_CONNECTIONREFUSED); - goto finish; - } - - pa_log_debug(__FILE__": Trying to connect to %s...\n", 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: - if (u) - pa_xfree(u); - - return r; -} - -static void on_connection(pa_socket_client *client, pa_iochannel*io, void *userdata) { - pa_context *c = userdata; - assert(client && c && 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_ERROR_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, int spawn, const pa_spawn_api *api) { - int r = -1; - assert(c && c->ref >= 1 && c->state == PA_CONTEXT_UNCONNECTED); - - 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_ERROR_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 (spawn && 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); - pa_context_set_state(c, PA_CONTEXT_TERMINATED); -} - -pa_context_state_t pa_context_get_state(pa_context *c) { - assert(c && c->ref >= 1); - return c->state; -} - -int pa_context_errno(pa_context *c) { - assert(c && c->ref >= 1); - return c->error; -} - -void pa_context_set_state_callback(pa_context *c, void (*cb)(pa_context *c, void *userdata), void *userdata) { - assert(c && c->ref >= 1); - c->state_callback = cb; - c->state_userdata = userdata; -} - -int pa_context_is_pending(pa_context *c) { - assert(c && c->ref >= 1); - -/* pa_log("pstream: %i\n", pa_pstream_is_pending(c->pstream)); */ -/* pa_log("pdispatch: %i\n", pa_pdispatch_is_pending(c->pdispatch)); */ - - 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 && o->context && o->context->ref >= 1 && o->ref >= 1 && 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) - pa_operation_ref(o); - else { - if (o->callback) { - void (*cb)(pa_context *c, void *userdata); - cb = (void (*)(pa_context*, void*)) o->callback; - cb(o->context, o->userdata); - } - - pa_operation_done(o); - } - - pa_operation_unref(o); -} - -pa_operation* pa_context_drain(pa_context *c, void (*cb) (pa_context*c, void *userdata), void *userdata) { - pa_operation *o; - assert(c && c->ref >= 1); - - if (c->state != PA_CONTEXT_READY) - return NULL; - - if (!pa_context_is_pending(c)) - return NULL; - - o = pa_operation_new(c, NULL); - assert(o); - o->callback = (pa_operation_callback) cb; - o->userdata = userdata; - - set_dispatch_callbacks(pa_operation_ref(o)); - - return o; -} - -void pa_context_exit_daemon(pa_context *c) { - pa_tagstruct *t; - assert(c && c->ref >= 1); - - t = pa_tagstruct_new(NULL, 0); - assert(t); - pa_tagstruct_putu32(t, PA_COMMAND_EXIT); - pa_tagstruct_putu32(t, c->ctag++); - pa_pstream_send_tagstruct(c->pstream, t); -} - -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 && o && o->context && o->ref >= 1); - - 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_ERROR_PROTOCOL); - goto finish; - } - - if (o->callback) { - void (*cb)(pa_context *c, int _success, void *_userdata) = (void (*)(pa_context *c, int _success, void *_userdata)) o->callback; - cb(o->context, success, o->userdata); - } - -finish: - pa_operation_done(o); - pa_operation_unref(o); -} - -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) { - pa_tagstruct *t; - pa_operation *o; - uint32_t tag; - assert(c && cb); - - o = pa_operation_new(c, NULL); - o->callback = cb; - o->userdata = userdata; - - t = pa_tagstruct_new(NULL, 0); - pa_tagstruct_putu32(t, command); - pa_tagstruct_putu32(t, tag = c->ctag++); - pa_pstream_send_tagstruct(c->pstream, t); - pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, internal_callback, o); - - return pa_operation_ref(o); -} - -pa_operation* pa_context_set_default_sink(pa_context *c, const char *name, void(*cb)(pa_context*c, int success, void *userdata), void *userdata) { - pa_tagstruct *t; - pa_operation *o; - uint32_t tag; - assert(c && cb); - - o = pa_operation_new(c, NULL); - o->callback = (pa_operation_callback) cb; - o->userdata = userdata; - - t = pa_tagstruct_new(NULL, 0); - pa_tagstruct_putu32(t, PA_COMMAND_SET_DEFAULT_SINK); - pa_tagstruct_putu32(t, tag = c->ctag++); - 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, o); - - return pa_operation_ref(o); -} - -pa_operation* pa_context_set_default_source(pa_context *c, const char *name, void(*cb)(pa_context*c, int success, void *userdata), void *userdata) { - pa_tagstruct *t; - pa_operation *o; - uint32_t tag; - assert(c && cb); - - o = pa_operation_new(c, NULL); - o->callback = (pa_operation_callback) cb; - o->userdata = userdata; - - t = pa_tagstruct_new(NULL, 0); - pa_tagstruct_putu32(t, PA_COMMAND_SET_DEFAULT_SOURCE); - pa_tagstruct_putu32(t, tag = c->ctag++); - 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, o); - - return pa_operation_ref(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, void(*cb)(pa_context*c, int success, void *userdata), void *userdata) { - pa_tagstruct *t; - pa_operation *o; - uint32_t tag; - assert(c && name && cb); - - o = pa_operation_new(c, NULL); - o->callback = (pa_operation_callback) cb; - o->userdata = userdata; - - t = pa_tagstruct_new(NULL, 0); - pa_tagstruct_putu32(t, PA_COMMAND_SET_CLIENT_NAME); - pa_tagstruct_putu32(t, tag = c->ctag++); - 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, o); - - return pa_operation_ref(o); -} - -const char* pa_get_library_version(void) { - return PACKAGE_VERSION; -} - -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; -} diff --git a/src/polyp/polyplib-context.h b/src/polyp/polyplib-context.h deleted file mode 100644 index febb75f4..00000000 --- a/src/polyp/polyplib-context.h +++ /dev/null @@ -1,117 +0,0 @@ -#ifndef foopolyplibcontexthfoo -#define foopolyplibcontexthfoo - -/* $Id$ */ - -/*** - This file is part of polypaudio. - - polypaudio 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. - - polypaudio 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 polypaudio; 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 - * Connection contexts for asynchrononous communication with a - * server. A pa_context object wraps a connection to a polypaudio - * server using its native protocol. A context may be used to issue - * commands on the server or to create playback or recording - * streams. Multiple playback streams may be piped through a single - * connection context. Operations on the contect involving - * communication with the server are executed asynchronously: i.e. the - * client function do not implicitely wait for completion of the - * operation on the server. Instead the caller specifies a call back - * function that is called when the operation is completed. Currently - * running operations may be canceled using pa_operation_cancel(). */ - -/** \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 - -/** \pa_context - * An opaque connection context to a daemon */ -typedef struct pa_context pa_context; - -/** 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); - -typedef void (*pa_context_state_callback)(pa_context *c, void *userdata); - -/** Set a callback function that is called whenever the context status changes */ -void pa_context_set_state_callback(pa_context *c, pa_context_state_callback callback, 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 spawn is non-zero -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, int spawn, 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, void (*cb) (pa_context*c, void *userdata), void *userdata); - -/** Tell the daemon to exit. No operation object is returned as the - * connection is terminated when the daemon quits, thus this operation - * would never complete. */ -void pa_context_exit_daemon(pa_context *c); - -/** Set the name of the default sink. \since 0.4 */ -pa_operation* pa_context_set_default_sink(pa_context *c, const char *name, void(*cb)(pa_context*c, int success, void *userdata), 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, void(*cb)(pa_context*c, int success, void *userdata), 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, void(*cb)(pa_context*c, int success, void *userdata), void *userdata); - -/** Return the server name this context is connected to. \since 0.7 */ -const char* pa_context_get_server(pa_context *c); - -PA_C_DECL_END - -#endif diff --git a/src/polyp/polyplib-def.h b/src/polyp/polyplib-def.h deleted file mode 100644 index 0591ce6c..00000000 --- a/src/polyp/polyplib-def.h +++ /dev/null @@ -1,213 +0,0 @@ -#ifndef foopolyplibdefhfoo -#define foopolyplibdefhfoo - -/* $Id$ */ - -/*** - This file is part of polypaudio. - - polypaudio 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. - - polypaudio 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 polypaudio; 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_DISCONNECTED, /**< 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) - -/** 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_LATENCY = 2 /**< Interpolate the latency for - * this stream. When enabled, - * you can use - * pa_stream_interpolated_xxx() - * for synchronization. Using - * these functions instead of - * pa_stream_get_latency() has - * the advantage of not - * requiring a whole roundtrip - * for responses. Consider using - * this option when frequently - * requesting latency - * information. This is - * especially useful on long latency - * network connections. */ -} 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_ERROR_OK, /**< No error */ - PA_ERROR_ACCESS, /**< Access failure */ - PA_ERROR_COMMAND, /**< Unknown command */ - PA_ERROR_INVALID, /**< Invalid argument */ - PA_ERROR_EXIST, /**< Entity exists */ - PA_ERROR_NOENTITY, /**< No such entity */ - PA_ERROR_CONNECTIONREFUSED, /**< Connection refused */ - PA_ERROR_PROTOCOL, /**< Protocol error */ - PA_ERROR_TIMEOUT, /**< Timeout */ - PA_ERROR_AUTHKEY, /**< No authorization key */ - PA_ERROR_INTERNAL, /**< Internal error */ - PA_ERROR_CONNECTIONTERMINATED, /**< Connection terminated */ - PA_ERROR_KILLED, /**< Entity killed */ - PA_ERROR_INVALIDSERVER, /**< Invalid server */ - PA_ERROR_INITFAILED, /**< Module initialization failed */ - PA_ERROR_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_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 latency info. See pa_stream_get_latency(). 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. The output buffer to which - * buffer_usec relates may be manipulated freely (with - * pa_stream_write()'s delta argument, pa_stream_flush() and friends), - * the buffers sink_usec/source_usec relates to is a first-in - * first-out buffer 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.*/ -typedef struct pa_latency_info { - pa_usec_t buffer_usec; /**< Time in usecs the current buffer takes to play. For both playback and record streams. */ - 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. */ - uint32_t queue_length; /**< Queue size in bytes. For both playback and record streams. */ - 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 */ - struct timeval timestamp; /**< The time when this latency info was current */ - uint64_t counter; /**< The byte counter current when the latency info was requested. \since 0.6 */ -} pa_latency_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; - -PA_C_DECL_END - -#endif diff --git a/src/polyp/polyplib-error.c b/src/polyp/polyplib-error.c deleted file mode 100644 index 188d6a93..00000000 --- a/src/polyp/polyplib-error.c +++ /dev/null @@ -1,54 +0,0 @@ -/* $Id$ */ - -/*** - This file is part of polypaudio. - - polypaudio 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. - - polypaudio 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 polypaudio; 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 "polyplib-error.h" -#include - -static const char* const errortab[PA_ERROR_MAX] = { - [PA_ERROR_OK] = "OK", - [PA_ERROR_ACCESS] = "Access denied", - [PA_ERROR_COMMAND] = "Unknown command", - [PA_ERROR_INVALID] = "Invalid argument", - [PA_ERROR_EXIST] = "Entity exists", - [PA_ERROR_NOENTITY] = "No such entity", - [PA_ERROR_CONNECTIONREFUSED] = "Connection refused", - [PA_ERROR_PROTOCOL] = "Protocol error", - [PA_ERROR_TIMEOUT] = "Timeout", - [PA_ERROR_AUTHKEY] = "No authorization key", - [PA_ERROR_INTERNAL] = "Internal error", - [PA_ERROR_CONNECTIONTERMINATED] = "Connection terminated", - [PA_ERROR_KILLED] = "Entity killed", - [PA_ERROR_INVALIDSERVER] = "Invalid server", -}; - -const char*pa_strerror(uint32_t error) { - if (error >= PA_ERROR_MAX) - return NULL; - - return errortab[error]; -} diff --git a/src/polyp/polyplib-error.h b/src/polyp/polyplib-error.h deleted file mode 100644 index 1bb97822..00000000 --- a/src/polyp/polyplib-error.h +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef foopolypliberrorhfoo -#define foopolypliberrorhfoo - -/* $Id$ */ - -/*** - This file is part of polypaudio. - - polypaudio 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. - - polypaudio 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 polypaudio; 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(uint32_t error); - -PA_C_DECL_END - -#endif diff --git a/src/polyp/polyplib-internal.h b/src/polyp/polyplib-internal.h deleted file mode 100644 index b95a20f3..00000000 --- a/src/polyp/polyplib-internal.h +++ /dev/null @@ -1,154 +0,0 @@ -#ifndef foopolyplibinternalhfoo -#define foopolyplibinternalhfoo - -/* $Id$ */ - -/*** - This file is part of polypaudio. - - polypaudio 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. - - polypaudio 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 polypaudio; 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 "polyplib-context.h" -#include "polyplib-stream.h" -#include "polyplib-operation.h" -#include -#include -#include -#include -#include - -#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 ctag; - uint32_t error; - pa_context_state_t state; - - void (*state_callback)(pa_context*c, void *userdata); - void *state_userdata; - - void (*subscribe_callback)(pa_context *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata); - 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; -}; - -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; - uint32_t channel; - int channel_valid; - uint32_t device_index; - pa_stream_direction_t direction; - uint32_t requested_bytes; - uint64_t counter; - pa_usec_t previous_time; - pa_usec_t previous_ipol_time; - pa_stream_state_t state; - pa_mcalign *mcalign; - - int interpolate; - int corked; - - uint32_t ipol_usec; - struct timeval ipol_timestamp; - pa_time_event *ipol_event; - int ipol_requested; - - void (*state_callback)(pa_stream*c, void *userdata); - void *state_userdata; - - void (*read_callback)(pa_stream *p, const void*data, size_t length, void *userdata); - void *read_userdata; - - void (*write_callback)(pa_stream *p, size_t length, void *userdata); - void *write_userdata; -}; - -typedef void (*pa_operation_callback)(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_callback 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); - -pa_operation *pa_operation_new(pa_context *c, pa_stream *s); -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); -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); - -void pa_stream_trash_ipol(pa_stream *s); - - -#endif diff --git a/src/polyp/polyplib-introspect.c b/src/polyp/polyplib-introspect.c deleted file mode 100644 index 0bdffa35..00000000 --- a/src/polyp/polyplib-introspect.c +++ /dev/null @@ -1,1003 +0,0 @@ -/* $Id$ */ - -/*** - This file is part of polypaudio. - - polypaudio 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. - - polypaudio 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 polypaudio; 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 "polyplib-introspect.h" -#include "polyplib-context.h" -#include "polyplib-internal.h" -#include -#include - -/*** 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 && o && o->context && o->ref >= 1); - - 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_ERROR_PROTOCOL); - goto finish; - } - - if (o->callback) { - void (*cb)(pa_context *s, const pa_stat_info*_i, void *_userdata) = (void (*)(pa_context *s, const pa_stat_info*_i, void *_userdata)) 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, void (*cb)(pa_context *c, const pa_stat_info*i, void *userdata), void *userdata) { - return pa_context_send_simple_command(c, PA_COMMAND_STAT, context_stat_callback, (pa_operation_callback) 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 && o && o->context && o->ref >= 1); - - 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_ERROR_PROTOCOL); - goto finish; - } - - if (o->callback) { - void (*cb)(pa_context *s, const pa_server_info*_i, void *_userdata) = (void (*)(pa_context *s, const pa_server_info*_i, void *_userdata)) 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, void (*cb)(pa_context *c, const pa_server_info*i, void *userdata), void *userdata) { - return pa_context_send_simple_command(c, PA_COMMAND_GET_SERVER_INFO, context_get_server_info_callback, (pa_operation_callback) 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 eof = 1; - assert(pd && o && o->context && o->ref >= 1); - - if (command != PA_COMMAND_REPLY) { - if (pa_context_handle_error(o->context, command, t) < 0) - goto finish; - - eof = -1; - } else { - - 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_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_context_fail(o->context, PA_ERROR_PROTOCOL); - goto finish; - } - - if (o->callback) { - void (*cb)(pa_context *s, const pa_sink_info*_i, int _eof, void *_userdata) = (void (*)(pa_context *s, const pa_sink_info*_i, int _eof, void *_userdata)) o->callback; - cb(o->context, &i, 0, o->userdata); - } - } - } - - if (o->callback) { - void (*cb)(pa_context *s, const pa_sink_info*_i, int _eof, void *_userdata) = (void (*)(pa_context *s, const pa_sink_info*_i, int _eof, void *_userdata)) o->callback; - cb(o->context, NULL, eof, o->userdata); - } - -finish: - pa_operation_done(o); - pa_operation_unref(o); -} - -pa_operation* pa_context_get_sink_info_list(pa_context *c, void (*cb)(pa_context *c, const pa_sink_info *i, int is_last, void *userdata), void *userdata) { - return pa_context_send_simple_command(c, PA_COMMAND_GET_SINK_INFO_LIST, context_get_sink_info_callback, (pa_operation_callback) cb, userdata); -} - -pa_operation* pa_context_get_sink_info_by_index(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, const pa_sink_info *i, int is_last, void *userdata), void *userdata) { - pa_tagstruct *t; - pa_operation *o; - uint32_t tag; - assert(c && cb); - - o = pa_operation_new(c, NULL); - o->callback = (pa_operation_callback) cb; - o->userdata = userdata; - - t = pa_tagstruct_new(NULL, 0); - pa_tagstruct_putu32(t, PA_COMMAND_GET_SINK_INFO); - pa_tagstruct_putu32(t, tag = c->ctag++); - 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, o); - - return pa_operation_ref(o); -} - -pa_operation* pa_context_get_sink_info_by_name(pa_context *c, const char *name, void (*cb)(pa_context *c, const pa_sink_info *i, int is_last, void *userdata), void *userdata) { - pa_tagstruct *t; - pa_operation *o; - uint32_t tag; - assert(c && cb); - - o = pa_operation_new(c, NULL); - o->callback = (pa_operation_callback) cb; - o->userdata = userdata; - - t = pa_tagstruct_new(NULL, 0); - pa_tagstruct_putu32(t, PA_COMMAND_GET_SINK_INFO); - pa_tagstruct_putu32(t, tag = c->ctag++); - 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, o); - - return pa_operation_ref(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 eof = 1; - assert(pd && o && o->context && o->ref >= 1); - - if (command != PA_COMMAND_REPLY) { - if (pa_context_handle_error(o->context, command, t) < 0) - goto finish; - - eof = -1; - } else { - - while (!pa_tagstruct_eof(t)) { - pa_source_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_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_context_fail(o->context, PA_ERROR_PROTOCOL); - goto finish; - } - - if (o->callback) { - void (*cb)(pa_context *s, const pa_source_info*_i, int _eof, void *_userdata) = (void (*)(pa_context *s, const pa_source_info*_i, int _eof, void *_userdata)) o->callback; - cb(o->context, &i, 0, o->userdata); - } - } - } - - if (o->callback) { - void (*cb)(pa_context *s, const pa_source_info*_i, int _eof, void *_userdata) = (void (*)(pa_context *s, const pa_source_info*_i, int _eof, void *_userdata)) o->callback; - cb(o->context, NULL, eof, o->userdata); - } - -finish: - pa_operation_done(o); - pa_operation_unref(o); -} - -pa_operation* pa_context_get_source_info_list(pa_context *c, void (*cb)(pa_context *c, const pa_source_info *i, int is_last, void *userdata), void *userdata) { - return pa_context_send_simple_command(c, PA_COMMAND_GET_SOURCE_INFO_LIST, context_get_source_info_callback, (pa_operation_callback) cb, userdata); -} - -pa_operation* pa_context_get_source_info_by_index(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, const pa_source_info *i, int is_last, void *userdata), void *userdata) { - pa_tagstruct *t; - pa_operation *o; - uint32_t tag; - assert(c && cb); - - o = pa_operation_new(c, NULL); - o->callback = (pa_operation_callback) cb; - o->userdata = userdata; - - t = pa_tagstruct_new(NULL, 0); - pa_tagstruct_putu32(t, PA_COMMAND_GET_SOURCE_INFO); - pa_tagstruct_putu32(t, tag = c->ctag++); - 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, o); - - return pa_operation_ref(o); -} - -pa_operation* pa_context_get_source_info_by_name(pa_context *c, const char *name, void (*cb)(pa_context *c, const pa_source_info *i, int is_last, void *userdata), void *userdata) { - pa_tagstruct *t; - pa_operation *o; - uint32_t tag; - assert(c && cb); - - o = pa_operation_new(c, NULL); - o->callback = (pa_operation_callback) cb; - o->userdata = userdata; - - t = pa_tagstruct_new(NULL, 0); - pa_tagstruct_putu32(t, PA_COMMAND_GET_SOURCE_INFO); - pa_tagstruct_putu32(t, tag = c->ctag++); - 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, o); - - return pa_operation_ref(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 eof = 1; - assert(pd && o && o->context && o->ref >= 1); - - if (command != PA_COMMAND_REPLY) { - if (pa_context_handle_error(o->context, command, t) < 0) - goto finish; - - eof = -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_ERROR_PROTOCOL); - goto finish; - } - - if (o->callback) { - void (*cb)(pa_context *s, const pa_client_info*_i, int _eof, void *_userdata) = (void (*)(pa_context *s, const pa_client_info*_i, int _eof, void *_userdata)) o->callback; - cb(o->context, &i, 0, o->userdata); - } - } - } - - if (o->callback) { - void (*cb)(pa_context *s, const pa_client_info*_i, int _eof, void *_userdata) = (void (*)(pa_context *s, const pa_client_info*_i, int _eof, void *_userdata)) o->callback; - cb(o->context, NULL, eof, o->userdata); - } - -finish: - pa_operation_done(o); - pa_operation_unref(o); -} - -pa_operation* pa_context_get_client_info(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, const pa_client_info*i, int is_last, void *userdata), void *userdata) { - pa_tagstruct *t; - pa_operation *o; - uint32_t tag; - assert(c && cb); - - o = pa_operation_new(c, NULL); - o->callback = (pa_operation_callback) cb; - o->userdata = userdata; - - t = pa_tagstruct_new(NULL, 0); - pa_tagstruct_putu32(t, PA_COMMAND_GET_CLIENT_INFO); - pa_tagstruct_putu32(t, tag = c->ctag++); - 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, o); - - return pa_operation_ref(o); -} - -pa_operation* pa_context_get_client_info_list(pa_context *c, void (*cb)(pa_context *c, const pa_client_info*i, int is_last, void *userdata), void *userdata) { - return pa_context_send_simple_command(c, PA_COMMAND_GET_CLIENT_INFO_LIST, context_get_client_info_callback, (pa_operation_callback) 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 eof = 1; - assert(pd && o && o->context && o->ref >= 1); - - if (command != PA_COMMAND_REPLY) { - if (pa_context_handle_error(o->context, command, t) < 0) - goto finish; - - eof = -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_ERROR_PROTOCOL); - goto finish; - } - - if (o->callback) { - void (*cb)(pa_context *s, const pa_module_info*_i, int _eof, void *_userdata) = (void (*)(pa_context *s, const pa_module_info*_i, int _eof, void *_userdata)) o->callback; - cb(o->context, &i, 0, o->userdata); - } - } - } - - if (o->callback) { - void (*cb)(pa_context *s, const pa_module_info*_i, int _eof, void *_userdata) = (void (*)(pa_context *s, const pa_module_info*_i, int _eof, void *_userdata)) o->callback; - cb(o->context, NULL, eof, o->userdata); - } - -finish: - pa_operation_done(o); - pa_operation_unref(o); -} - -pa_operation* pa_context_get_module_info(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, const pa_module_info*i, int is_last, void *userdata), void *userdata) { - pa_tagstruct *t; - pa_operation *o; - uint32_t tag; - assert(c && cb); - - o = pa_operation_new(c, NULL); - o->callback = (pa_operation_callback) cb; - o->userdata = userdata; - - t = pa_tagstruct_new(NULL, 0); - pa_tagstruct_putu32(t, PA_COMMAND_GET_MODULE_INFO); - pa_tagstruct_putu32(t, tag = c->ctag++); - 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, o); - - return pa_operation_ref(o); -} - -pa_operation* pa_context_get_module_info_list(pa_context *c, void (*cb)(pa_context *c, const pa_module_info*i, int is_last, void *userdata), void *userdata) { - return pa_context_send_simple_command(c, PA_COMMAND_GET_MODULE_INFO_LIST, context_get_module_info_callback, (pa_operation_callback) 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 eof = 1; - assert(pd && o && o->context && o->ref >= 1); - - if (command != PA_COMMAND_REPLY) { - if (pa_context_handle_error(o->context, command, t) < 0) - goto finish; - - eof = -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_ERROR_PROTOCOL); - goto finish; - } - - if (o->callback) { - void (*cb)(pa_context *s, const pa_sink_input_info*_i, int _eof, void *_userdata) = (void (*)(pa_context *s, const pa_sink_input_info*_i, int _eof, void *_userdata)) o->callback; - cb(o->context, &i, 0, o->userdata); - } - } - } - - if (o->callback) { - void (*cb)(pa_context *s, const pa_sink_input_info*_i, int _eof, void *_userdata) = (void (*)(pa_context *s, const pa_sink_input_info*_i, int _eof, void *_userdata)) o->callback; - cb(o->context, NULL, eof, 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, void (*cb)(pa_context *c, const pa_sink_input_info*i, int is_last, void *userdata), void *userdata) { - pa_tagstruct *t; - pa_operation *o; - uint32_t tag; - assert(c && cb); - - o = pa_operation_new(c, NULL); - o->callback = (pa_operation_callback) cb; - o->userdata = userdata; - - t = pa_tagstruct_new(NULL, 0); - pa_tagstruct_putu32(t, PA_COMMAND_GET_SINK_INPUT_INFO); - pa_tagstruct_putu32(t, tag = c->ctag++); - 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, o); - - return pa_operation_ref(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_callback) 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 eof = 1; - assert(pd && o && o->context && o->ref >= 1); - - if (command != PA_COMMAND_REPLY) { - if (pa_context_handle_error(o->context, command, t) < 0) - goto finish; - - eof = -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_ERROR_PROTOCOL); - goto finish; - } - - if (o->callback) { - void (*cb)(pa_context *s, const pa_source_output_info*_i, int _eof, void *_userdata) = (void (*)(pa_context *s, const pa_source_output_info*_i, int _eof, void *_userdata)) o->callback; - cb(o->context, &i, 0, o->userdata); - } - } - } - - if (o->callback) { - void (*cb)(pa_context *s, const pa_source_output_info*_i, int _eof, void *_userdata) = (void (*)(pa_context *s, const pa_source_output_info*_i, int _eof, void *_userdata))o->callback; - cb(o->context, NULL, eof, 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, void (*cb)(pa_context *c, const pa_source_output_info*i, int is_last, void *userdata), void *userdata) { - pa_tagstruct *t; - pa_operation *o; - uint32_t tag; - assert(c && cb); - - o = pa_operation_new(c, NULL); - o->callback = (pa_operation_callback) cb; - o->userdata = userdata; - - t = pa_tagstruct_new(NULL, 0); - pa_tagstruct_putu32(t, PA_COMMAND_GET_SOURCE_OUTPUT_INFO); - pa_tagstruct_putu32(t, tag = c->ctag++); - 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, o); - - return pa_operation_ref(o); -} - -pa_operation* pa_context_get_source_output_info_list(pa_context *c, void (*cb)(pa_context *c, const pa_source_output_info*i, int is_last, void *userdata), void *userdata) { - return pa_context_send_simple_command(c, PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST, context_get_source_output_info_callback, (pa_operation_callback) cb, userdata); -} - -/*** Volume manipulation ***/ - -pa_operation* pa_context_set_sink_volume_by_index(pa_context *c, uint32_t idx, const pa_cvolume *volume, void (*cb)(pa_context *c, int success, void *userdata), void *userdata) { - pa_operation *o; - pa_tagstruct *t; - uint32_t tag; - assert(c && idx != PA_INVALID_INDEX); - - o = pa_operation_new(c, NULL); - o->callback = (pa_operation_callback) cb; - o->userdata = userdata; - - t = pa_tagstruct_new(NULL, 0); - pa_tagstruct_putu32(t, PA_COMMAND_SET_SINK_VOLUME); - pa_tagstruct_putu32(t, tag = c->ctag++); - 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, o); - - return pa_operation_ref(o); -} - -pa_operation* pa_context_set_sink_volume_by_name(pa_context *c, const char *name, const pa_cvolume *volume, void (*cb)(pa_context *c, int success, void *userdata), void *userdata) { - pa_operation *o; - pa_tagstruct *t; - uint32_t tag; - assert(c && name); - - o = pa_operation_new(c, NULL); - o->callback = (pa_operation_callback) cb; - o->userdata = userdata; - - t = pa_tagstruct_new(NULL, 0); - pa_tagstruct_putu32(t, PA_COMMAND_SET_SINK_VOLUME); - pa_tagstruct_putu32(t, tag = c->ctag++); - 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, o); - - return pa_operation_ref(o); -} - -pa_operation* pa_context_set_sink_input_volume(pa_context *c, uint32_t idx, const pa_cvolume *volume, void (*cb)(pa_context *c, int success, void *userdata), void *userdata) { - pa_operation *o; - pa_tagstruct *t; - uint32_t tag; - assert(c && idx != PA_INVALID_INDEX); - - o = pa_operation_new(c, NULL); - o->callback = (pa_operation_callback) cb; - o->userdata = userdata; - - t = pa_tagstruct_new(NULL, 0); - pa_tagstruct_putu32(t, PA_COMMAND_SET_SINK_INPUT_VOLUME); - pa_tagstruct_putu32(t, tag = c->ctag++); - 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, o); - - return pa_operation_ref(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 eof = 1; - assert(pd && o && o->context && o->ref >= 1); - - if (command != PA_COMMAND_REPLY) { - if (pa_context_handle_error(o->context, command, t) < 0) - goto finish; - - eof = -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_ERROR_PROTOCOL); - goto finish; - } - - if (o->callback) { - void (*cb)(pa_context *s, const pa_sample_info*_i, int _eof, void *_userdata) = (void (*)(pa_context *s, const pa_sample_info*_i, int _eof, void *_userdata)) o->callback; - cb(o->context, &i, 0, o->userdata); - } - } - } - - if (o->callback) { - void (*cb)(pa_context *s, const pa_sample_info*_i, int _eof, void *_userdata) = (void (*)(pa_context *s, const pa_sample_info*_i, int _eof, void *_userdata)) o->callback; - cb(o->context, NULL, eof, 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, void (*cb)(pa_context *c, const pa_sample_info *i, int is_last, void *userdata), void *userdata) { - pa_tagstruct *t; - pa_operation *o; - uint32_t tag; - assert(c && cb && name); - - o = pa_operation_new(c, NULL); - o->callback = (pa_operation_callback) cb; - o->userdata = userdata; - - t = pa_tagstruct_new(NULL, 0); - pa_tagstruct_putu32(t, PA_COMMAND_GET_SAMPLE_INFO); - pa_tagstruct_putu32(t, tag = c->ctag++); - 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, o); - - return pa_operation_ref(o); -} - -pa_operation* pa_context_get_sample_info_by_index(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, const pa_sample_info *i, int is_last, void *userdata), void *userdata) { - pa_tagstruct *t; - pa_operation *o; - uint32_t tag; - assert(c && cb); - - o = pa_operation_new(c, NULL); - o->callback = (pa_operation_callback) cb; - o->userdata = userdata; - - t = pa_tagstruct_new(NULL, 0); - pa_tagstruct_putu32(t, PA_COMMAND_GET_SAMPLE_INFO); - pa_tagstruct_putu32(t, tag = c->ctag++); - 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, o); - - return pa_operation_ref(o); -} - -pa_operation* pa_context_get_sample_info_list(pa_context *c, void (*cb)(pa_context *c, const pa_sample_info *i, int is_last, void *userdata), void *userdata) { - return pa_context_send_simple_command(c, PA_COMMAND_GET_SAMPLE_INFO_LIST, context_get_sample_info_callback, (pa_operation_callback) cb, userdata); -} - -static pa_operation* command_kill(pa_context *c, uint32_t command, uint32_t idx, void (*cb)(pa_context *c, int success, void *userdata), void *userdata) { - pa_operation *o; - pa_tagstruct *t; - uint32_t tag; - assert(c && idx != PA_INVALID_INDEX); - - o = pa_operation_new(c, NULL); - o->callback = (pa_operation_callback) cb; - o->userdata = userdata; - - t = pa_tagstruct_new(NULL, 0); - pa_tagstruct_putu32(t, command); - pa_tagstruct_putu32(t, tag = c->ctag++); - 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, o); - - return pa_operation_ref(o); -} - -pa_operation* pa_context_kill_client(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, int success, void *userdata), 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, void (*cb)(pa_context *c, int success, void *userdata), 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, void (*cb)(pa_context *c, int success, void *userdata), void *userdata) { - return command_kill(c, PA_COMMAND_KILL_SOURCE_OUTPUT, idx, cb, userdata); -} - -static void load_module_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 = -1; - assert(pd && o && o->context && o->ref >= 1); - - if (command != PA_COMMAND_REPLY) { - if (pa_context_handle_error(o->context, command, t) < 0) - goto finish; - - } else if (pa_tagstruct_getu32(t, &idx) < 0 || - !pa_tagstruct_eof(t)) { - pa_context_fail(o->context, PA_ERROR_PROTOCOL); - goto finish; - } - - if (o->callback) { - void (*cb)(pa_context *c, uint32_t _idx, void *_userdata) = (void (*)(pa_context *c, uint32_t _idx, void *_userdata)) 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, void (*cb)(pa_context *c, uint32_t idx, void *userdata), void *userdata) { - pa_operation *o; - pa_tagstruct *t; - uint32_t tag; - assert(c && name && argument); - - o = pa_operation_new(c, NULL); - o->callback = (pa_operation_callback) cb; - o->userdata = userdata; - - t = pa_tagstruct_new(NULL, 0); - pa_tagstruct_putu32(t, PA_COMMAND_LOAD_MODULE); - pa_tagstruct_putu32(t, tag = c->ctag++); - 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, load_module_callback, o); - - return pa_operation_ref(o); -} - -pa_operation* pa_context_unload_module(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, int success, void *userdata), 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 eof = 1; - assert(pd && o && o->context && o->ref >= 1); - - if (command != PA_COMMAND_REPLY) { - if (pa_context_handle_error(o->context, command, t) < 0) - goto finish; - - eof = -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_ERROR_PROTOCOL); - goto finish; - } - - if (o->callback) { - void (*cb)(pa_context *s, const pa_autoload_info*_i, int _eof, void *_userdata) = (void (*)(pa_context *s, const pa_autoload_info*_i, int _eof, void *_userdata)) o->callback; - cb(o->context, &i, 0, o->userdata); - } - } - } - - if (o->callback) { - void (*cb)(pa_context *s, const pa_autoload_info*_i, int _eof, void *_userdata) = (void (*)(pa_context *s, const pa_autoload_info*_i, int _eof, void *_userdata)) o->callback; - cb(o->context, NULL, eof, 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, void (*cb)(pa_context *c, const pa_autoload_info *i, int is_last, void *userdata), void *userdata) { - pa_tagstruct *t; - pa_operation *o; - uint32_t tag; - assert(c && cb && name); - - o = pa_operation_new(c, NULL); - o->callback = (pa_operation_callback) cb; - o->userdata = userdata; - - t = pa_tagstruct_new(NULL, 0); - pa_tagstruct_putu32(t, PA_COMMAND_GET_AUTOLOAD_INFO); - pa_tagstruct_putu32(t, tag = c->ctag++); - 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, o); - - return pa_operation_ref(o); -} - -pa_operation* pa_context_get_autoload_info_by_index(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, const pa_autoload_info *i, int is_last, void *userdata), void *userdata) { - pa_tagstruct *t; - pa_operation *o; - uint32_t tag; - assert(c && cb && idx != PA_INVALID_INDEX); - - o = pa_operation_new(c, NULL); - o->callback = (pa_operation_callback) cb; - o->userdata = userdata; - - t = pa_tagstruct_new(NULL, 0); - pa_tagstruct_putu32(t, PA_COMMAND_GET_AUTOLOAD_INFO); - pa_tagstruct_putu32(t, tag = c->ctag++); - 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, o); - - return pa_operation_ref(o); -} - -pa_operation* pa_context_get_autoload_info_list(pa_context *c, void (*cb)(pa_context *c, const pa_autoload_info *i, int is_last, void *userdata), void *userdata) { - return pa_context_send_simple_command(c, PA_COMMAND_GET_AUTOLOAD_INFO_LIST, context_get_autoload_info_callback, (pa_operation_callback) cb, userdata); -} - -static void context_add_autoload_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 && o && o->context && o->ref >= 1); - - 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_ERROR_PROTOCOL); - goto finish; - } - - if (o->callback) { - void (*cb)(pa_context *s, uint32_t _idx, void *_userdata) = (void (*)(pa_context *s, uint32_t _idx, void *_userdata)) o->callback; - cb(o->context, idx, o->userdata); - } - - -finish: - pa_operation_done(o); - pa_operation_unref(o); -} - -pa_operation* pa_context_add_autoload(pa_context *c, const char *name, pa_autoload_type_t type, const char *module, const char*argument, void (*cb)(pa_context *c, int success, void *userdata), void* userdata) { - pa_operation *o; - pa_tagstruct *t; - uint32_t tag; - assert(c && name && module && argument); - - o = pa_operation_new(c, NULL); - o->callback = (pa_operation_callback) cb; - o->userdata = userdata; - - t = pa_tagstruct_new(NULL, 0); - pa_tagstruct_putu32(t, PA_COMMAND_ADD_AUTOLOAD); - pa_tagstruct_putu32(t, tag = c->ctag++); - 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_add_autoload_callback, o); - - return pa_operation_ref(o); -} - -pa_operation* pa_context_remove_autoload_by_name(pa_context *c, const char *name, pa_autoload_type_t type, void (*cb)(pa_context *c, int success, void *userdata), void* userdata) { - pa_operation *o; - pa_tagstruct *t; - uint32_t tag; - assert(c && name); - - o = pa_operation_new(c, NULL); - o->callback = (pa_operation_callback) cb; - o->userdata = userdata; - - t = pa_tagstruct_new(NULL, 0); - pa_tagstruct_putu32(t, PA_COMMAND_REMOVE_AUTOLOAD); - pa_tagstruct_putu32(t, tag = c->ctag++); - 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, o); - - return pa_operation_ref(o); -} - -pa_operation* pa_context_remove_autoload_by_index(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, int success, void *userdata), void* userdata) { - pa_operation *o; - pa_tagstruct *t; - uint32_t tag; - assert(c && idx != PA_INVALID_INDEX); - - o = pa_operation_new(c, NULL); - o->callback = (pa_operation_callback) cb; - o->userdata = userdata; - - t = pa_tagstruct_new(NULL, 0); - pa_tagstruct_putu32(t, PA_COMMAND_REMOVE_AUTOLOAD); - pa_tagstruct_putu32(t, tag = c->ctag++); - 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, o); - - return pa_operation_ref(o); -} diff --git a/src/polyp/polyplib-introspect.h b/src/polyp/polyplib-introspect.h deleted file mode 100644 index d3489908..00000000 --- a/src/polyp/polyplib-introspect.h +++ /dev/null @@ -1,279 +0,0 @@ -#ifndef foopolyplibintrospecthfoo -#define foopolyplibintrospecthfoo - -/* $Id$ */ - -/*** - This file is part of polypaudio. - - polypaudio 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. - - polypaudio 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 polypaudio; 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 - -/** \file - * - * Routines for daemon introspection. When enumerating all entitites - * of a certain kind, use the pa_context_xxx_list() functions. The - * specified callback function is called once for each entry. The - * enumeration is finished by a call to the callback function with - * is_last=1 and i=NULL. Strings referenced in pa_xxx_info structures - * and the structures themselves point to internal memory that may not - * be modified. That memory is only valid during the call to the - * callback function. A deep copy is required if you need this data - * outside the callback functions. An error is signalled by a call to * the callback function with i=NULL and is_last=0. - * - * When using the routines that ask fo a single entry only, a callback - * with the same signature is used. However, no finishing call to the - * routine is issued. */ - -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.9 */ - uint32_t owner_module; /**< Index of the owning module of this sink, or PA_INVALID_INDEX */ - pa_cvolume volume; /**< Volume of the sink */ - 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.9 */ -} pa_sink_info; - -/** Get information about a sink by its name */ -pa_operation* pa_context_get_sink_info_by_name(pa_context *c, const char *name, void (*cb)(pa_context *c, const pa_sink_info *i, int is_last, void *userdata), 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, void (*cb)(pa_context *c, const pa_sink_info *i, int is_last, void *userdata), void *userdata); - -/** Get the complete sink list */ -pa_operation* pa_context_get_sink_info_list(pa_context *c, void (*cb)(pa_context *c, const pa_sink_info *i, int is_last, void *userdata), 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.9 */ - uint32_t owner_module; /**< Owning module index, or PA_INVALID_INDEX */ - 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.9 */ -} pa_source_info; - -/** Get information about a source by its name */ -pa_operation* pa_context_get_source_info_by_name(pa_context *c, const char *name, void (*cb)(pa_context *c, const pa_source_info *i, int is_last, void *userdata), 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, void (*cb)(pa_context *c, const pa_source_info *i, int is_last, void *userdata), void *userdata); - -/** Get the complete source list */ -pa_operation* pa_context_get_source_info_list(pa_context *c, void (*cb)(pa_context *c, const pa_source_info *i, int is_last, void *userdata), 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 "polypaudio") */ - 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 polypaudio. \since 0.8 */ -} pa_server_info; - -/** Get some information about the server */ -pa_operation* pa_context_get_server_info(pa_context *c, void (*cb)(pa_context *c, const pa_server_info*i, void *userdata), 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; - -/** Get some information about a module by its index */ -pa_operation* pa_context_get_module_info(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, const pa_module_info*i, int is_last, void *userdata), void *userdata); - -/** Get the complete list of currently loaded modules */ -pa_operation* pa_context_get_module_info_list(pa_context *c, void (*cb)(pa_context *c, const pa_module_info*i, int is_last, void *userdata), 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.9 */ -} pa_client_info; - -/** Get information about a client by its index */ -pa_operation* pa_context_get_client_info(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, const pa_client_info*i, int is_last, void *userdata), void *userdata); - -/** Get the complete client list */ -pa_operation* pa_context_get_client_info_list(pa_context *c, void (*cb)(pa_context *c, const pa_client_info*i, int is_last, void *userdata), 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.9 */ -} pa_sink_input_info; - -/** Get some information about a sink input by its index */ -pa_operation* pa_context_get_sink_input_info(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, const pa_sink_input_info*i, int is_last, void *userdata), void *userdata); - -/** Get the complete sink input list */ -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); - -/** 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.9 */ -} pa_source_output_info; - -/** Get information about a source output by its index */ -pa_operation* pa_context_get_source_output_info(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, const pa_source_output_info*i, int is_last, void *userdata), void *userdata); - -/** Get the complete list of source outputs */ -pa_operation* pa_context_get_source_output_info_list(pa_context *c, void (*cb)(pa_context *c, const pa_source_output_info*i, int is_last, void *userdata), 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, void (*cb)(pa_context *c, int success, void *userdata), 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, void (*cb)(pa_context *c, int success, void *userdata), 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, void (*cb)(pa_context *c, int success, void *userdata), 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; - -/** Get daemon memory block statistics */ -pa_operation* pa_context_stat(pa_context *c, void (*cb)(pa_context *c, const pa_stat_info *i, void *userdata), 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; - -/** Get information about a sample by its name */ -pa_operation* pa_context_get_sample_info_by_name(pa_context *c, const char *name, void (*cb)(pa_context *c, const pa_sample_info *i, int is_last, void *userdata), 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, void (*cb)(pa_context *c, const pa_sample_info *i, int is_last, void *userdata), void *userdata); - -/** Get the complete list of samples stored in the daemon. */ -pa_operation* pa_context_get_sample_info_list(pa_context *c, void (*cb)(pa_context *c, const pa_sample_info *i, int is_last, void *userdata), void *userdata); - -/** Kill a client. \since 0.5 */ -pa_operation* pa_context_kill_client(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, int success, void *userdata), void *userdata); - -/** Kill a sink input. \since 0.5 */ -pa_operation* pa_context_kill_sink_input(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, int success, void *userdata), void *userdata); - -/** Kill a source output. \since 0.5 */ -pa_operation* pa_context_kill_source_output(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, int success, void *userdata), void *userdata); - -/** Load a module. \since 0.5 */ -pa_operation* pa_context_load_module(pa_context *c, const char*name, const char *argument, void (*cb)(pa_context *c, uint32_t idx, void *userdata), void *userdata); - -/** Unload a module. \since 0.5 */ -pa_operation* pa_context_unload_module(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, int success, void *userdata), 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; - -/** 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, void (*cb)(pa_context *c, const pa_autoload_info *i, int is_last, void *userdata), 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, void (*cb)(pa_context *c, const pa_autoload_info *i, int is_last, void *userdata), void *userdata); - -/** Get the complete list of autoload entries. \since 0.5 */ -pa_operation* pa_context_get_autoload_info_list(pa_context *c, void (*cb)(pa_context *c, const pa_autoload_info *i, int is_last, void *userdata), 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, void (*cb)(pa_context *c, int idx, void *userdata), 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, void (*cb)(pa_context *c, int success, void *userdata), void* userdata); - -/** Remove an autoload entry. \since 0.6 */ -pa_operation* pa_context_remove_autoload_by_index(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, int success, void *userdata), void* userdata); - - -PA_C_DECL_END - -#endif diff --git a/src/polyp/polyplib-operation.c b/src/polyp/polyplib-operation.c deleted file mode 100644 index ea336c17..00000000 --- a/src/polyp/polyplib-operation.c +++ /dev/null @@ -1,103 +0,0 @@ -/* $Id$ */ - -/*** - This file is part of polypaudio. - - polypaudio 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. - - polypaudio 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 polypaudio; 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 "polyplib-internal.h" -#include "polyplib-operation.h" - -pa_operation *pa_operation_new(pa_context *c, pa_stream *s) { - pa_operation *o; - assert(c); - - o = pa_xmalloc(sizeof(pa_operation)); - o->ref = 1; - o->context = pa_context_ref(c); - o->stream = s ? pa_stream_ref(s) : NULL; - - o->state = PA_OPERATION_RUNNING; - o->userdata = NULL; - o->callback = NULL; - - PA_LLIST_PREPEND(pa_operation, o->context->operations, o); - return pa_operation_ref(o); -} - -pa_operation *pa_operation_ref(pa_operation *o) { - assert(o && o->ref >= 1); - o->ref++; - return o; -} - -void pa_operation_unref(pa_operation *o) { - assert(o && o->ref >= 1); - - if ((--(o->ref)) == 0) { - assert(!o->context); - assert(!o->stream); - free(o); - } -} - -static void operation_set_state(pa_operation *o, pa_operation_state_t st) { - assert(o && o->ref >= 1); - - if (st == o->state) - return; - - if (!o->context) - return; - - o->state = st; - - if ((o->state == PA_OPERATION_DONE) || (o->state == PA_OPERATION_CANCELED)) { - PA_LLIST_REMOVE(pa_operation, o->context->operations, o); - pa_context_unref(o->context); - if (o->stream) - pa_stream_unref(o->stream); - o->context = NULL; - o->stream = NULL; - o->callback = NULL; - o->userdata = NULL; - - pa_operation_unref(o); - } -} - -void pa_operation_cancel(pa_operation *o) { - assert(o && o->ref >= 1); - operation_set_state(o, PA_OPERATION_CANCELED); -} - -void pa_operation_done(pa_operation *o) { - assert(o && o->ref >= 1); - operation_set_state(o, PA_OPERATION_DONE); -} - -pa_operation_state_t pa_operation_get_state(pa_operation *o) { - assert(o && o->ref >= 1); - return o->state; -} diff --git a/src/polyp/polyplib-operation.h b/src/polyp/polyplib-operation.h deleted file mode 100644 index cac03e30..00000000 --- a/src/polyp/polyplib-operation.h +++ /dev/null @@ -1,51 +0,0 @@ -#ifndef foopolypliboperationhfoo -#define foopolypliboperationhfoo - -/* $Id$ */ - -/*** - This file is part of polypaudio. - - polypaudio 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. - - polypaudio 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 polypaudio; 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 - -/** \pa_operation - * 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/polyp/polyplib-scache.c b/src/polyp/polyplib-scache.c deleted file mode 100644 index 1315af97..00000000 --- a/src/polyp/polyplib-scache.c +++ /dev/null @@ -1,127 +0,0 @@ -/* $Id$ */ - -/*** - This file is part of polypaudio. - - polypaudio 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. - - polypaudio 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 polypaudio; 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 "polyplib-scache.h" -#include "polyplib-internal.h" -#include - -void pa_stream_connect_upload(pa_stream *s, size_t length) { - pa_tagstruct *t; - uint32_t tag; - - assert(s && length); - - pa_stream_ref(s); - - s->state = PA_STREAM_CREATING; - s->direction = PA_STREAM_UPLOAD; - - t = pa_tagstruct_new(NULL, 0); - pa_tagstruct_putu32(t, PA_COMMAND_CREATE_UPLOAD_STREAM); - pa_tagstruct_putu32(t, tag = s->context->ctag++); - pa_tagstruct_puts(t, s->name); - pa_tagstruct_put_sample_spec(t, &s->sample_spec); - 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); - - pa_stream_unref(s); -} - -void pa_stream_finish_upload(pa_stream *s) { - pa_tagstruct *t; - uint32_t tag; - assert(s); - - if (!s->channel_valid || !s->context->state == PA_CONTEXT_READY) - return; - - pa_stream_ref(s); - - t = pa_tagstruct_new(NULL, 0); - assert(t); - - pa_tagstruct_putu32(t, PA_COMMAND_FINISH_UPLOAD_STREAM); - pa_tagstruct_putu32(t, tag = s->context->ctag++); - 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); - - pa_stream_unref(s); -} - -pa_operation * pa_context_play_sample(pa_context *c, const char *name, const char *dev, uint32_t volume, void (*cb)(pa_context *c, int success, void *userdata), void *userdata) { - pa_operation *o; - pa_tagstruct *t; - uint32_t tag; - assert(c && name && *name && (!dev || *dev)); - - o = pa_operation_new(c, NULL); - o->callback = (pa_operation_callback) cb; - o->userdata = userdata; - - if (!dev) - dev = c->conf->default_sink; - - t = pa_tagstruct_new(NULL, 0); - assert(t); - pa_tagstruct_putu32(t, PA_COMMAND_PLAY_SAMPLE); - pa_tagstruct_putu32(t, tag = c->ctag++); - pa_tagstruct_putu32(t, (uint32_t) -1); - 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, o); - - return pa_operation_ref(o); -} - -pa_operation* pa_context_remove_sample(pa_context *c, const char *name, void (*cb)(pa_context *c, int success, void *userdata), void *userdata) { - pa_operation *o; - pa_tagstruct *t; - uint32_t tag; - assert(c && name); - - o = pa_operation_new(c, NULL); - o->callback = (pa_operation_callback) cb; - o->userdata = userdata; - - t = pa_tagstruct_new(NULL, 0); - assert(t); - pa_tagstruct_putu32(t, PA_COMMAND_REMOVE_SAMPLE); - pa_tagstruct_putu32(t, tag = c->ctag++); - 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, o); - - return pa_operation_ref(o); -} - diff --git a/src/polyp/polyplib-scache.h b/src/polyp/polyplib-scache.h deleted file mode 100644 index 89d27597..00000000 --- a/src/polyp/polyplib-scache.h +++ /dev/null @@ -1,50 +0,0 @@ -#ifndef foopolyplibscachehfoo -#define foopolyplibscachehfoo - -/* $Id$ */ - -/*** - This file is part of polypaudio. - - polypaudio 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. - - polypaudio 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 polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#include - -#include -#include -#include - -/** \file - * All sample cache related routines */ - -PA_C_DECL_BEGIN - -/** Make this stream a sample upload stream */ -void 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 sample upload by issuing pa_stream_disconnect() */ -void 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, const char *name, const char *dev, uint32_t volume, void (*cb)(pa_context *c, int success, void *userdata), void *userdata); - -/** 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, void (*cb)(pa_context *c, int success, void *userdata), void *userdata); - -PA_C_DECL_END - -#endif diff --git a/src/polyp/polyplib-simple.c b/src/polyp/polyplib-simple.c deleted file mode 100644 index 7436f007..00000000 --- a/src/polyp/polyplib-simple.c +++ /dev/null @@ -1,393 +0,0 @@ -/* $Id$ */ - -/*** - This file is part of polypaudio. - - polypaudio 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. - - polypaudio 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 polypaudio; 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 "polyplib-simple.h" -#include "polyplib.h" -#include "mainloop.h" -#include -#include -#include - -struct pa_simple { - pa_mainloop *mainloop; - pa_context *context; - pa_stream *stream; - pa_stream_direction_t direction; - - int dead; - - void *read_data; - size_t read_index, read_length; - pa_usec_t latency; -}; - -static void read_callback(pa_stream *s, const void*data, size_t length, void *userdata); - -static int check_error(pa_simple *p, int *rerror) { - pa_context_state_t cst; - pa_stream_state_t sst; - assert(p); - - if ((cst = pa_context_get_state(p->context)) == PA_CONTEXT_FAILED) - goto fail; - - assert(cst != PA_CONTEXT_TERMINATED); - - if (p->stream) { - if ((sst = pa_stream_get_state(p->stream)) == PA_STREAM_FAILED) - goto fail; - - assert(sst != PA_STREAM_TERMINATED); - } - - return 0; - -fail: - if (rerror) - *rerror = pa_context_errno(p->context); - - p->dead = 1; - - return -1; -} - -static int iterate(pa_simple *p, int block, int *rerror) { - assert(p && p->context && p->mainloop); - - if (check_error(p, rerror) < 0) - return -1; - - if (!block && !pa_context_is_pending(p->context)) - return 0; - - do { - if (pa_mainloop_iterate(p->mainloop, 1, NULL) < 0) { - if (rerror) - *rerror = PA_ERROR_INTERNAL; - return -1; - } - - if (check_error(p, rerror) < 0) - return -1; - - } while (pa_context_is_pending(p->context)); - - - while (pa_mainloop_deferred_pending(p->mainloop)) { - - if (pa_mainloop_iterate(p->mainloop, 0, NULL) < 0) { - if (rerror) - *rerror = PA_ERROR_INTERNAL; - return -1; - } - - if (check_error(p, rerror) < 0) - return -1; - } - - return 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_buffer_attr *attr, - int *rerror) { - - pa_simple *p; - int error = PA_ERROR_INTERNAL; - assert(ss && (dir == PA_STREAM_PLAYBACK || dir == PA_STREAM_RECORD)); - - p = pa_xmalloc(sizeof(pa_simple)); - p->context = NULL; - p->stream = NULL; - p->mainloop = pa_mainloop_new(); - assert(p->mainloop); - p->dead = 0; - p->direction = dir; - p->read_data = NULL; - p->read_index = p->read_length = 0; - p->latency = 0; - - if (!(p->context = pa_context_new(pa_mainloop_get_api(p->mainloop), name))) - goto fail; - - pa_context_connect(p->context, server, 1, NULL); - - /* Wait until the context is ready */ - while (pa_context_get_state(p->context) != PA_CONTEXT_READY) { - if (iterate(p, 1, &error) < 0) - goto fail; - } - - if (!(p->stream = pa_stream_new(p->context, stream_name, ss, NULL))) - goto fail; - - if (dir == PA_STREAM_PLAYBACK) - pa_stream_connect_playback(p->stream, dev, attr, 0, NULL); - else - pa_stream_connect_record(p->stream, dev, attr, 0); - - /* Wait until the stream is ready */ - while (pa_stream_get_state(p->stream) != PA_STREAM_READY) { - if (iterate(p, 1, &error) < 0) - goto fail; - } - - pa_stream_set_read_callback(p->stream, read_callback, p); - - return p; - -fail: - if (rerror) - *rerror = error; - pa_simple_free(p); - return NULL; -} - -void pa_simple_free(pa_simple *s) { - assert(s); - - pa_xfree(s->read_data); - - if (s->stream) - pa_stream_unref(s->stream); - - if (s->context) - pa_context_unref(s->context); - - if (s->mainloop) - pa_mainloop_free(s->mainloop); - - pa_xfree(s); -} - -int pa_simple_write(pa_simple *p, const void*data, size_t length, int *rerror) { - assert(p && data && p->direction == PA_STREAM_PLAYBACK); - - if (p->dead) { - if (rerror) - *rerror = pa_context_errno(p->context); - - return -1; - } - - while (length > 0) { - size_t l; - - while (!(l = pa_stream_writable_size(p->stream))) - if (iterate(p, 1, rerror) < 0) - return -1; - - if (l > length) - l = length; - - pa_stream_write(p->stream, data, l, NULL, 0); - data = (const uint8_t*) data + l; - length -= l; - } - - /* Make sure that no data is pending for write */ - if (iterate(p, 0, rerror) < 0) - return -1; - - return 0; -} - -static void read_callback(pa_stream *s, const void*data, size_t length, void *userdata) { - pa_simple *p = userdata; - assert(s && data && length && p); - - if (p->read_data) { - pa_log(__FILE__": Buffer overflow, dropping incoming memory blocks.\n"); - pa_xfree(p->read_data); - } - - p->read_data = pa_xmemdup(data, p->read_length = length); - p->read_index = 0; -} - -int pa_simple_read(pa_simple *p, void*data, size_t length, int *rerror) { - assert(p && data && p->direction == PA_STREAM_RECORD); - - if (p->dead) { - if (rerror) - *rerror = pa_context_errno(p->context); - - return -1; - } - - while (length > 0) { - if (p->read_data) { - size_t l = length; - - if (p->read_length <= l) - l = p->read_length; - - memcpy(data, (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) { - pa_xfree(p->read_data); - p->read_data = NULL; - p->read_index = 0; - } - - if (!length) - return 0; - - assert(!p->read_data); - } - - if (iterate(p, 1, rerror) < 0) - return -1; - } - - return 0; -} - -static void drain_or_flush_complete(pa_stream *s, int success, void *userdata) { - pa_simple *p = userdata; - assert(s && p); - if (!success) - p->dead = 1; -} - -int pa_simple_drain(pa_simple *p, int *rerror) { - pa_operation *o; - assert(p && p->direction == PA_STREAM_PLAYBACK); - - if (p->dead) { - if (rerror) - *rerror = pa_context_errno(p->context); - - return -1; - } - - o = pa_stream_drain(p->stream, drain_or_flush_complete, p); - - while (pa_operation_get_state(o) == PA_OPERATION_RUNNING) { - if (iterate(p, 1, rerror) < 0) { - pa_operation_cancel(o); - pa_operation_unref(o); - return -1; - } - } - - pa_operation_unref(o); - - if (p->dead && rerror) - *rerror = pa_context_errno(p->context); - - return p->dead ? -1 : 0; -} - -static void latency_complete(pa_stream *s, const pa_latency_info *l, void *userdata) { - pa_simple *p = userdata; - assert(s && p); - - if (!l) - p->dead = 1; - else { - int negative = 0; - p->latency = pa_stream_get_latency(s, l, &negative); - if (negative) - p->latency = 0; - } -} - -pa_usec_t pa_simple_get_playback_latency(pa_simple *p, int *rerror) { - pa_operation *o; - assert(p && p->direction == PA_STREAM_PLAYBACK); - - if (p->dead) { - if (rerror) - *rerror = pa_context_errno(p->context); - - return (pa_usec_t) -1; - } - - p->latency = 0; - o = pa_stream_get_latency_info(p->stream, latency_complete, p); - - while (pa_operation_get_state(o) == PA_OPERATION_RUNNING) { - - if (iterate(p, 1, rerror) < 0) { - pa_operation_cancel(o); - pa_operation_unref(o); - return -1; - } - } - - pa_operation_unref(o); - - if (p->dead && rerror) - *rerror = pa_context_errno(p->context); - - return p->dead ? (pa_usec_t) -1 : p->latency; -} - -int pa_simple_flush(pa_simple *p, int *rerror) { - pa_operation *o; - assert(p && p->direction == PA_STREAM_PLAYBACK); - - if (p->dead) { - if (rerror) - *rerror = pa_context_errno(p->context); - - return -1; - } - - o = pa_stream_flush(p->stream, drain_or_flush_complete, p); - - while (pa_operation_get_state(o) == PA_OPERATION_RUNNING) { - if (iterate(p, 1, rerror) < 0) { - pa_operation_cancel(o); - pa_operation_unref(o); - return -1; - } - } - - pa_operation_unref(o); - - if (p->dead && rerror) - *rerror = pa_context_errno(p->context); - - return p->dead ? -1 : 0; -} diff --git a/src/polyp/polyplib-simple.h b/src/polyp/polyplib-simple.h deleted file mode 100644 index b01f30d5..00000000 --- a/src/polyp/polyplib-simple.h +++ /dev/null @@ -1,80 +0,0 @@ -#ifndef foopolyplibsimplehfoo -#define foopolyplibsimplehfoo - -/* $Id$ */ - -/*** - This file is part of polypaudio. - - polypaudio 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. - - polypaudio 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 polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#include - -#include "sample.h" -#include "polyplib-def.h" -#include - -/** \file - * A simple but limited synchronous playback and recording - * API. This is synchronouse, 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 - -/** \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_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_playback_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/polyp/polyplib-stream.c b/src/polyp/polyplib-stream.c deleted file mode 100644 index 63c9245b..00000000 --- a/src/polyp/polyplib-stream.c +++ /dev/null @@ -1,807 +0,0 @@ -/* $Id$ */ - -/*** - This file is part of polypaudio. - - polypaudio 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. - - polypaudio 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 polypaudio; 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 "polyplib-internal.h" -#include -#include -#include -#include - -#define LATENCY_IPOL_INTERVAL_USEC (10000L) - -pa_stream *pa_stream_new(pa_context *c, const char *name, const pa_sample_spec *ss, const pa_channel_map *map) { - pa_stream *s; - assert(c); - assert(ss); - - if (!pa_sample_spec_valid(ss)) - return NULL; - - if (map && !pa_channel_map_valid(map)) - return NULL; - - 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->direction = PA_STREAM_NODIRECTION; - s->name = pa_xstrdup(name); - s->sample_spec = *ss; - - if (map) - s->channel_map = *map; - else - pa_channel_map_init_auto(&s->channel_map, ss->channels); - - s->channel = 0; - s->channel_valid = 0; - s->device_index = PA_INVALID_INDEX; - s->requested_bytes = 0; - s->state = PA_STREAM_DISCONNECTED; - memset(&s->buffer_attr, 0, sizeof(s->buffer_attr)); - - s->mcalign = pa_mcalign_new(pa_frame_size(ss), c->memblock_stat); - - s->counter = 0; - s->previous_time = 0; - s->previous_ipol_time = 0; - - s->corked = 0; - s->interpolate = 0; - - s->ipol_usec = 0; - memset(&s->ipol_timestamp, 0, sizeof(s->ipol_timestamp)); - s->ipol_event = NULL; - s->ipol_requested = 0; - - PA_LLIST_PREPEND(pa_stream, c->streams, s); - - return pa_stream_ref(s); -} - -static void stream_free(pa_stream *s) { - assert(s); - - if (s->ipol_event) { - assert(s->mainloop); - s->mainloop->time_free(s->ipol_event); - } - - pa_mcalign_free(s->mcalign); - - pa_xfree(s->name); - pa_xfree(s); -} - -void pa_stream_unref(pa_stream *s) { - assert(s && s->ref >= 1); - - if (--(s->ref) == 0) - stream_free(s); -} - -pa_stream* pa_stream_ref(pa_stream *s) { - assert(s && s->ref >= 1); - s->ref++; - return s; -} - -pa_stream_state_t pa_stream_get_state(pa_stream *s) { - assert(s && s->ref >= 1); - return s->state; -} - -pa_context* pa_stream_get_context(pa_stream *s) { - assert(s && s->ref >= 1); - return s->context; -} - -uint32_t pa_stream_get_index(pa_stream *s) { - assert(s && s->ref >= 1); - return s->device_index; -} - -void pa_stream_set_state(pa_stream *s, pa_stream_state_t st) { - assert(s && s->ref >= 1); - - if (s->state == st) - return; - - pa_stream_ref(s); - - s->state = st; - - if ((st == PA_STREAM_FAILED || st == PA_STREAM_TERMINATED) && s->context) { - 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); - } - - if (s->state_callback) - s->state_callback(s, s->state_userdata); - - 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 && (command == PA_COMMAND_PLAYBACK_STREAM_KILLED || command == PA_COMMAND_RECORD_STREAM_KILLED) && t && c); - - pa_context_ref(c); - - if (pa_tagstruct_getu32(t, &channel) < 0 || - !pa_tagstruct_eof(t)) { - pa_context_fail(c, PA_ERROR_PROTOCOL); - goto finish; - } - - if (!(s = pa_dynarray_get(command == PA_COMMAND_PLAYBACK_STREAM_KILLED ? c->playback_streams : c->record_streams, channel))) - goto finish; - - c->error = PA_ERROR_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 && command == PA_COMMAND_REQUEST && t && 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_ERROR_PROTOCOL); - goto finish; - } - - if (!(s = pa_dynarray_get(c->playback_streams, channel))) - goto finish; - - if (s->state != PA_STREAM_READY) - goto finish; - - pa_stream_ref(s); - - s->requested_bytes += bytes; - - if (s->requested_bytes && s->write_callback) - s->write_callback(s, s->requested_bytes, s->write_userdata); - - pa_stream_unref(s); - -finish: - pa_context_unref(c); -} - -static void ipol_callback(pa_mainloop_api *m, pa_time_event *e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) { - struct timeval tv2; - pa_stream *s = userdata; - - pa_stream_ref(s); - -/* pa_log("requesting new ipol data\n"); */ - - if (s->state == PA_STREAM_READY && !s->ipol_requested) { - pa_operation_unref(pa_stream_get_latency_info(s, NULL, NULL)); - s->ipol_requested = 1; - } - - pa_gettimeofday(&tv2); - pa_timeval_add(&tv2, LATENCY_IPOL_INTERVAL_USEC); - - m->time_restart(e, &tv2); - - 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 && s && 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_tagstruct_eof(t)) { - pa_context_fail(s->context, PA_ERROR_PROTOCOL); - goto finish; - } - - 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->interpolate) { - struct timeval tv; - pa_operation_unref(pa_stream_get_latency_info(s, NULL, NULL)); - - pa_gettimeofday(&tv); - tv.tv_usec += LATENCY_IPOL_INTERVAL_USEC; /* every 100 ms */ - - assert(!s->ipol_event); - s->ipol_event = s->mainloop->time_new(s->mainloop, &tv, &ipol_callback, s); - } - - if (s->requested_bytes && s->ref > 1 && s->write_callback) - s->write_callback(s, s->requested_bytes, s->write_userdata); - -finish: - pa_stream_unref(s); -} - -static void create_stream(pa_stream *s, const char *dev, const pa_buffer_attr *attr, pa_stream_flags_t flags, const pa_cvolume *volume) { - pa_tagstruct *t; - uint32_t tag; - assert(s && s->ref >= 1 && s->state == PA_STREAM_DISCONNECTED); - - pa_stream_ref(s); - - s->interpolate = !!(flags & PA_STREAM_INTERPOLATE_LATENCY); - pa_stream_trash_ipol(s); - - 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; - } - - pa_stream_set_state(s, PA_STREAM_CREATING); - - t = pa_tagstruct_new(NULL, 0); - assert(t); - - if (!dev) { - if (s->direction == PA_STREAM_PLAYBACK) - dev = s->context->conf->default_sink; - else - dev = s->context->conf->default_source; - } - - pa_tagstruct_put(t, - PA_TAG_U32, s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_CREATE_PLAYBACK_STREAM : PA_COMMAND_CREATE_RECORD_STREAM, - PA_TAG_U32, tag = s->context->ctag++, - 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_INVALID); - - if (!volume) { - pa_cvolume_reset(&cv, s->sample_spec.channels); - volume = &cv; - } - - 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); - - pa_stream_unref(s); -} - -void pa_stream_connect_playback(pa_stream *s, const char *dev, const pa_buffer_attr *attr, pa_stream_flags_t flags, pa_cvolume *volume) { - assert(s && s->context->state == PA_CONTEXT_READY && s->ref >= 1); - s->direction = PA_STREAM_PLAYBACK; - create_stream(s, dev, attr, flags, volume); -} - -void pa_stream_connect_record(pa_stream *s, const char *dev, const pa_buffer_attr *attr, pa_stream_flags_t flags) { - assert(s && s->context->state == PA_CONTEXT_READY && s->ref >= 1); - s->direction = PA_STREAM_RECORD; - create_stream(s, dev, attr, flags, 0); -} - -void pa_stream_write(pa_stream *s, const void *data, size_t length, void (*free_cb)(void *p), size_t delta) { - pa_memchunk chunk; - assert(s && s->context && data && length && s->state == PA_STREAM_READY && s->ref >= 1); - - if (free_cb) { - chunk.memblock = pa_memblock_new_user((void*) data, length, free_cb, 1, s->context->memblock_stat); - assert(chunk.memblock && chunk.memblock->data); - } else { - chunk.memblock = pa_memblock_new(length, s->context->memblock_stat); - assert(chunk.memblock && chunk.memblock->data); - memcpy(chunk.memblock->data, data, length); - } - chunk.index = 0; - chunk.length = length; - - pa_pstream_send_memblock(s->context->pstream, s->channel, delta, &chunk); - pa_memblock_unref(chunk.memblock); - - if (length < s->requested_bytes) - s->requested_bytes -= length; - else - s->requested_bytes = 0; - - s->counter += length; -} - -size_t pa_stream_writable_size(pa_stream *s) { - assert(s && s->ref >= 1); - return s->state == PA_STREAM_READY ? s->requested_bytes : 0; -} - -pa_operation * pa_stream_drain(pa_stream *s, void (*cb) (pa_stream*s, int success, void *userdata), void *userdata) { - pa_operation *o; - pa_tagstruct *t; - uint32_t tag; - assert(s && s->ref >= 1 && s->state == PA_STREAM_READY); - - o = pa_operation_new(s->context, s); - assert(o); - o->callback = (pa_operation_callback) cb; - o->userdata = userdata; - - t = pa_tagstruct_new(NULL, 0); - assert(t); - pa_tagstruct_putu32(t, PA_COMMAND_DRAIN_PLAYBACK_STREAM); - pa_tagstruct_putu32(t, tag = s->context->ctag++); - 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, o); - - return pa_operation_ref(o); -} - -static void stream_get_latency_info_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { - pa_operation *o = userdata; - pa_latency_info i, *p = NULL; - struct timeval local, remote, now; - assert(pd && o && o->stream && o->context); - - 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.buffer_usec) < 0 || - 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_getu32(t, &i.queue_length) < 0 || - pa_tagstruct_get_timeval(t, &local) < 0 || - pa_tagstruct_get_timeval(t, &remote) < 0 || - pa_tagstruct_getu64(t, &i.counter) < 0 || - !pa_tagstruct_eof(t)) { - pa_context_fail(o->context, PA_ERROR_PROTOCOL); - goto finish; - } else { - pa_gettimeofday(&now); - - 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); - } - - if (o->stream->interpolate) { -/* pa_log("new interpol data\n"); */ - o->stream->ipol_timestamp = i.timestamp; - o->stream->ipol_usec = pa_stream_get_time(o->stream, &i); - o->stream->ipol_requested = 0; - } - - p = &i; - } - - if (o->callback) { - void (*cb)(pa_stream *s, const pa_latency_info *_i, void *_userdata) = (void (*)(pa_stream *s, const pa_latency_info *_i, void *_userdata)) o->callback; - cb(o->stream, p, o->userdata); - } - -finish: - pa_operation_done(o); - pa_operation_unref(o); -} - -pa_operation* pa_stream_get_latency_info(pa_stream *s, void (*cb)(pa_stream *p, const pa_latency_info*i, void *userdata), void *userdata) { - uint32_t tag; - pa_operation *o; - pa_tagstruct *t; - struct timeval now; - assert(s && s->direction != PA_STREAM_UPLOAD); - - o = pa_operation_new(s->context, s); - assert(o); - o->callback = (pa_operation_callback) cb; - o->userdata = userdata; - - t = pa_tagstruct_new(NULL, 0); - assert(t); - pa_tagstruct_putu32(t, s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_GET_PLAYBACK_LATENCY : PA_COMMAND_GET_RECORD_LATENCY); - pa_tagstruct_putu32(t, tag = s->context->ctag++); - pa_tagstruct_putu32(t, s->channel); - - pa_gettimeofday(&now); - pa_tagstruct_put_timeval(t, &now); - pa_tagstruct_putu64(t, s->counter); - - pa_pstream_send_tagstruct(s->context->pstream, t); - pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, stream_get_latency_info_callback, o); - - return pa_operation_ref(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 && s && 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_ERROR_PROTOCOL); - goto finish; - } - - pa_stream_set_state(s, PA_STREAM_TERMINATED); - -finish: - pa_stream_unref(s); -} - -void pa_stream_disconnect(pa_stream *s) { - pa_tagstruct *t; - uint32_t tag; - assert(s && s->ref >= 1); - - if (!s->channel_valid || !s->context->state == PA_CONTEXT_READY) - return; - - pa_stream_ref(s); - - t = pa_tagstruct_new(NULL, 0); - assert(t); - - pa_tagstruct_putu32(t, 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)); - pa_tagstruct_putu32(t, tag = s->context->ctag++); - 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); - - pa_stream_unref(s); -} - -void pa_stream_set_read_callback(pa_stream *s, void (*cb)(pa_stream *p, const void*data, size_t length, void *userdata), void *userdata) { - assert(s && s->ref >= 1); - s->read_callback = cb; - s->read_userdata = userdata; -} - -void pa_stream_set_write_callback(pa_stream *s, void (*cb)(pa_stream *p, size_t length, void *userdata), void *userdata) { - assert(s && s->ref >= 1); - s->write_callback = cb; - s->write_userdata = userdata; -} - -void pa_stream_set_state_callback(pa_stream *s, void (*cb)(pa_stream *s, void *userdata), void *userdata) { - assert(s && s->ref >= 1); - s->state_callback = cb; - s->state_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 && o && o->context && o->ref >= 1); - - 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_ERROR_PROTOCOL); - goto finish; - } - - if (o->callback) { - void (*cb)(pa_stream *s, int _success, void *_userdata) = (void (*)(pa_stream *s, int _success, void *_userdata)) 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, void (*cb) (pa_stream*s, int success, void *userdata), void *userdata) { - pa_operation *o; - pa_tagstruct *t; - uint32_t tag; - assert(s && s->ref >= 1 && s->state == PA_STREAM_READY); - - if (s->interpolate) { - if (!s->corked && b) - /* Pausing */ - s->ipol_usec = pa_stream_get_interpolated_time(s); - else if (s->corked && !b) - /* Unpausing */ - pa_gettimeofday(&s->ipol_timestamp); - } - - s->corked = b; - - o = pa_operation_new(s->context, s); - assert(o); - o->callback = (pa_operation_callback) cb; - o->userdata = userdata; - - t = pa_tagstruct_new(NULL, 0); - assert(t); - pa_tagstruct_putu32(t, s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_CORK_PLAYBACK_STREAM : PA_COMMAND_CORK_RECORD_STREAM); - pa_tagstruct_putu32(t, tag = s->context->ctag++); - 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, o); - - pa_operation_unref(pa_stream_get_latency_info(s, NULL, NULL)); - - return pa_operation_ref(o); -} - -static pa_operation* stream_send_simple_command(pa_stream *s, uint32_t command, void (*cb)(pa_stream *s, int success, void *userdata), void *userdata) { - pa_tagstruct *t; - pa_operation *o; - uint32_t tag; - assert(s && s->ref >= 1 && s->state == PA_STREAM_READY); - - o = pa_operation_new(s->context, s); - o->callback = (pa_operation_callback) cb; - o->userdata = userdata; - - t = pa_tagstruct_new(NULL, 0); - pa_tagstruct_putu32(t, command); - pa_tagstruct_putu32(t, tag = s->context->ctag++); - 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, o); - - return pa_operation_ref(o); -} - -pa_operation* pa_stream_flush(pa_stream *s, void (*cb)(pa_stream *s, int success, void *userdata), void *userdata) { - pa_operation *o; - o = stream_send_simple_command(s, s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_FLUSH_PLAYBACK_STREAM : PA_COMMAND_FLUSH_RECORD_STREAM, cb, userdata); - pa_operation_unref(pa_stream_get_latency_info(s, NULL, NULL)); - return o; -} - -pa_operation* pa_stream_prebuf(pa_stream *s, void (*cb)(pa_stream *s, int success, void *userdata), void *userdata) { - pa_operation *o; - o = stream_send_simple_command(s, PA_COMMAND_PREBUF_PLAYBACK_STREAM, cb, userdata); - pa_operation_unref(pa_stream_get_latency_info(s, NULL, NULL)); - return o; -} - -pa_operation* pa_stream_trigger(pa_stream *s, void (*cb)(pa_stream *s, int success, void *userdata), void *userdata) { - pa_operation *o; - o = stream_send_simple_command(s, PA_COMMAND_TRIGGER_PLAYBACK_STREAM, cb, userdata); - pa_operation_unref(pa_stream_get_latency_info(s, NULL, NULL)); - return o; -} - -pa_operation* pa_stream_set_name(pa_stream *s, const char *name, void(*cb)(pa_stream*c, int success, void *userdata), void *userdata) { - pa_operation *o; - pa_tagstruct *t; - uint32_t tag; - assert(s && s->ref >= 1 && s->state == PA_STREAM_READY && name && s->direction != PA_STREAM_UPLOAD); - - o = pa_operation_new(s->context, s); - assert(o); - o->callback = (pa_operation_callback) cb; - o->userdata = userdata; - - t = pa_tagstruct_new(NULL, 0); - assert(t); - pa_tagstruct_putu32(t, s->direction == PA_STREAM_RECORD ? PA_COMMAND_SET_RECORD_STREAM_NAME : PA_COMMAND_SET_PLAYBACK_STREAM_NAME); - pa_tagstruct_putu32(t, tag = s->context->ctag++); - 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, o); - - return pa_operation_ref(o); -} - -uint64_t pa_stream_get_counter(pa_stream *s) { - assert(s); - return s->counter; -} - -pa_usec_t pa_stream_get_time(pa_stream *s, const pa_latency_info *i) { - pa_usec_t usec; - assert(s); - - usec = pa_bytes_to_usec(i->counter, &s->sample_spec); - - if (i) { - if (s->direction == PA_STREAM_PLAYBACK) { - pa_usec_t latency = i->transport_usec + i->buffer_usec + i->sink_usec; - if (usec < latency) - usec = 0; - else - usec -= latency; - - } else if (s->direction == PA_STREAM_RECORD) { - usec += i->source_usec + i->buffer_usec + i->transport_usec; - - if (usec > i->sink_usec) - usec -= i->sink_usec; - else - usec = 0; - } - } - - if (usec < s->previous_time) - usec = s->previous_time; - - s->previous_time = usec; - - return usec; -} - -static pa_usec_t time_counter_diff(pa_stream *s, pa_usec_t t, pa_usec_t c, int *negative) { - assert(s); - - if (negative) - *negative = 0; - - if (c < t) { - if (s->direction == PA_STREAM_RECORD) { - if (negative) - *negative = 1; - - return t-c; - } else - return 0; - } else - return c-t; -} - -pa_usec_t pa_stream_get_latency(pa_stream *s, const pa_latency_info *i, int *negative) { - pa_usec_t t, c; - assert(s && i); - - t = pa_stream_get_time(s, i); - c = pa_bytes_to_usec(s->counter, &s->sample_spec); - - return time_counter_diff(s, t, c, negative); -} - -const pa_sample_spec* pa_stream_get_sample_spec(pa_stream *s) { - assert(s); - return &s->sample_spec; -} - -void pa_stream_trash_ipol(pa_stream *s) { - assert(s); - - if (!s->interpolate) - return; - - memset(&s->ipol_timestamp, 0, sizeof(s->ipol_timestamp)); - s->ipol_usec = 0; -} - -pa_usec_t pa_stream_get_interpolated_time(pa_stream *s) { - pa_usec_t usec; - assert(s && s->interpolate); - - if (s->corked) - usec = s->ipol_usec; - else { - if (s->ipol_timestamp.tv_sec == 0) - usec = 0; - else - usec = s->ipol_usec + pa_timeval_age(&s->ipol_timestamp); - } - - if (usec < s->previous_ipol_time) - usec = s->previous_ipol_time; - - s->previous_ipol_time = usec; - - return usec; -} - -pa_usec_t pa_stream_get_interpolated_latency(pa_stream *s, int *negative) { - pa_usec_t t, c; - assert(s && s->interpolate); - - t = pa_stream_get_interpolated_time(s); - c = pa_bytes_to_usec(s->counter, &s->sample_spec); - return time_counter_diff(s, t, c, negative); -} diff --git a/src/polyp/polyplib-stream.h b/src/polyp/polyplib-stream.h deleted file mode 100644 index bc828b71..00000000 --- a/src/polyp/polyplib-stream.h +++ /dev/null @@ -1,181 +0,0 @@ -#ifndef foopolyplibstreamhfoo -#define foopolyplibstreamhfoo - -/* $Id$ */ - -/*** - This file is part of polypaudio. - - polypaudio 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. - - polypaudio 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 polypaudio; 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 - -/** \file - * Audio streams for input, output and sample upload */ - -PA_C_DECL_BEGIN - -/** \pa_stream - * An opaque stream for playback or recording */ -typedef struct pa_stream pa_stream; - -/** Create a new, unconnected stream with the specified name and sample type */ -pa_stream* pa_stream_new(pa_context *c, const char *name, const pa_sample_spec *ss, const pa_channel_map *map); - -/** 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 */ -void pa_stream_connect_playback( - pa_stream *s, - const char *dev, - const pa_buffer_attr *attr, - pa_stream_flags_t flags, - pa_cvolume *volume); - -/** Connect the stream to a source */ -void pa_stream_connect_record( - pa_stream *s, - const char *dev, - const pa_buffer_attr *attr, - pa_stream_flags_t flags); - -/** Disconnect a stream from a source/sink */ -void 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. */ -void 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 */, - void (*free_cb)(void *p) /**< A cleanup routine for the data or NULL to request an internal copy */, - size_t delta /**< Drop this many - bytes in the playback - buffer before writing - this data. Use - (size_t) -1 for - clearing the whole - playback - buffer. Normally you - will specify 0 here, - i.e. append to the - playback buffer. If - the value given here - is greater than the - buffered data length - the buffer is cleared - and the data is - written to the - buffer's start. This - value is ignored on - upload streams. */); - -/** Return the amount of bytes that may be written using pa_stream_write() */ -size_t pa_stream_writable_size(pa_stream *p); - -/** Drain a playback stream */ -pa_operation* pa_stream_drain(pa_stream *s, void (*cb) (pa_stream*s, int success, void *userdata), void *userdata); - -/** Get the playback latency of a stream */ -pa_operation* pa_stream_get_latency_info(pa_stream *p, void (*cb)(pa_stream *p, const pa_latency_info *i, void *userdata), 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, void (*cb)(pa_stream *s, void *userdata), 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, void (*cb)(pa_stream *p, size_t length, void *userdata), void *userdata); - -/** Set the callback function that is called when new data is available from the stream */ -void pa_stream_set_read_callback(pa_stream *p, void (*cb)(pa_stream *p, const void*data, size_t length, void *userdata), 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, void (*cb) (pa_stream*s, int success, void *userdata), 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, void (*cb)(pa_stream *s, int success, void *userdata), void *userdata); - -/** Reenable prebuffering. Available for playback streams only. \since 0.6 */ -pa_operation* pa_stream_prebuf(pa_stream *s, void (*cb)(pa_stream *s, int success, void *userdata), void *userdata); - -/** Request immediate start of playback on this stream. This disables - * prebuffering as specified in the pa_buffer_attr structure. Available for playback streams only. \since - * 0.3 */ -pa_operation* pa_stream_trigger(pa_stream *s, void (*cb)(pa_stream *s, int success, void *userdata), void *userdata); - -/** Rename the stream. \since 0.5 */ -pa_operation* pa_stream_set_name(pa_stream *s, const char *name, void(*cb)(pa_stream*c, int success, void *userdata), void *userdata); - -/** Return the total number of bytes written to/read from the - * stream. This counter is not reset on pa_stream_flush(), you may do - * this yourself using pa_stream_reset_counter(). \since 0.6 */ -uint64_t pa_stream_get_counter(pa_stream *s); - -/** Return the current playback/recording time. This is based on the - * counter accessible with pa_stream_get_counter(). This function - * requires a pa_latency_info structure as argument, which should be - * acquired using pa_stream_get_latency(). \since 0.6 */ -pa_usec_t pa_stream_get_time(pa_stream *s, const pa_latency_info *i); - -/** Return the total stream latency. Thus function requires a - * pa_latency_info structure as argument, which should be aquired - * using pa_stream_get_latency(). 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 */ -pa_usec_t pa_stream_get_latency(pa_stream *s, const pa_latency_info *i, int *negative); - -/** Return the interpolated playback/recording time. Requires the - * PA_STREAM_INTERPOLATE_LATENCY bit set when creating the stream. In - * contrast to pa_stream_get_latency() this function doesn't require - * a whole roundtrip for response. \since 0.6 */ -pa_usec_t pa_stream_get_interpolated_time(pa_stream *s); - -/** Return the interpolated playback/recording latency. Requires the - * PA_STREAM_INTERPOLATE_LATENCY bit set when creating the - * stream. \since 0.6 */ -pa_usec_t pa_stream_get_interpolated_latency(pa_stream *s, int *negative); - -/** Return a pointer to the streams sample specification. \since 0.6 */ -const pa_sample_spec* pa_stream_get_sample_spec(pa_stream *s); - -PA_C_DECL_END - -#endif diff --git a/src/polyp/polyplib-subscribe.c b/src/polyp/polyplib-subscribe.c deleted file mode 100644 index 13fcfb42..00000000 --- a/src/polyp/polyplib-subscribe.c +++ /dev/null @@ -1,81 +0,0 @@ -/* $Id$ */ - -/*** - This file is part of polypaudio. - - polypaudio 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. - - polypaudio 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 polypaudio; 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 "polyplib-subscribe.h" -#include "polyplib-internal.h" -#include -#include - -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 index; - assert(pd && command == PA_COMMAND_SUBSCRIBE_EVENT && t && c); - - pa_context_ref(c); - - if (pa_tagstruct_getu32(t, &e) < 0 || - pa_tagstruct_getu32(t, &index) < 0 || - !pa_tagstruct_eof(t)) { - pa_context_fail(c, PA_ERROR_PROTOCOL); - goto finish; - } - - if (c->subscribe_callback) - c->subscribe_callback(c, e, index, c->subscribe_userdata); - -finish: - pa_context_unref(c); -} - - -pa_operation* pa_context_subscribe(pa_context *c, pa_subscription_mask_t m, void (*cb)(pa_context *c, int success, void *userdata), void *userdata) { - pa_operation *o; - pa_tagstruct *t; - uint32_t tag; - assert(c); - - o = pa_operation_new(c, NULL); - o->callback = (pa_operation_callback) cb; - o->userdata = userdata; - - t = pa_tagstruct_new(NULL, 0); - pa_tagstruct_putu32(t, PA_COMMAND_SUBSCRIBE); - pa_tagstruct_putu32(t, tag = c->ctag++); - 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, o); - - return pa_operation_ref(o); -} - -void pa_context_set_subscribe_callback(pa_context *c, void (*cb)(pa_context *c, pa_subscription_event_type_t t, uint32_t index, void *userdata), void *userdata) { - assert(c); - c->subscribe_callback = cb; - c->subscribe_userdata = userdata; -} diff --git a/src/polyp/polyplib-subscribe.h b/src/polyp/polyplib-subscribe.h deleted file mode 100644 index 920c9853..00000000 --- a/src/polyp/polyplib-subscribe.h +++ /dev/null @@ -1,47 +0,0 @@ -#ifndef foopolyplibsubscribehfoo -#define foopolyplibsubscribehfoo - -/* $Id$ */ - -/*** - This file is part of polypaudio. - - polypaudio 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. - - polypaudio 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 polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#include - -#include -#include -#include - -/** \file - * Daemon introspection event subscription subsystem. Use this - * to be notified whenever the internal layout of daemon changes: - * i.e. entities such as sinks or sources are create, removed or - * modified. */ - -PA_C_DECL_BEGIN - -/** Enable event notification */ -pa_operation* pa_context_subscribe(pa_context *c, pa_subscription_mask_t m, void (*cb)(pa_context *c, int success, void *userdata), 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, void (*cb)(pa_context *c, pa_subscription_event_type_t t, uint32_t index, void *userdata), void *userdata); - -PA_C_DECL_END - -#endif diff --git a/src/polyp/polyplib-version.h.in b/src/polyp/polyplib-version.h.in deleted file mode 100644 index 89e0a0e5..00000000 --- a/src/polyp/polyplib-version.h.in +++ /dev/null @@ -1,47 +0,0 @@ -#ifndef foopolyplibversionhfoo /*-*-C-*-*/ -#define foopolyplibversionhfoo - -/* $Id$ */ - -/*** - This file is part of polypaudio. - - polypaudio 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. - - polypaudio 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 polypaudio; 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 polyplib-version.h.in! */ - -/** \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 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@ - -PA_C_DECL_END - -#endif diff --git a/src/polyp/polyplib.h b/src/polyp/polyplib.h deleted file mode 100644 index b9b9b447..00000000 --- a/src/polyp/polyplib.h +++ /dev/null @@ -1,86 +0,0 @@ -#ifndef foopolyplibhfoo -#define foopolyplibhfoo - -/* $Id$ */ - -/*** - This file is part of polypaudio. - - polypaudio 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. - - polypaudio 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 polypaudio; 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 - -/** \file - * Include all polyplib header file at once. The following files are included: \ref mainloop-api.h, \ref sample.h, - * \ref polyplib-def.h, \ref polyplib-context.h, \ref polyplib-stream.h, - * \ref polyplib-introspect.h, \ref polyplib-subscribe.h and \ref polyplib-scache.h \ref polyplib-version.h - * at once */ - -/** \mainpage - * - * \section intro_sec Introduction - * - * This document describes the client API for the polypaudio sound - * server. The API comes in two flavours: - * - * \li The complete but somewhat complicated to use asynchronous API - * \li And the simplified, easy to use, but limited synchronous API - * - * The polypaudio client libraries are thread safe as long as all - * objects created by any library function are accessed from the thread - * that created them only. - * - * \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 - * \ref polyplib-simple.h for more details. - * - * \section async_api Asynchronous API - * - * Use this if you develop your programs in asynchronous, main loop - * based style or want to use advanced features of the polypaudio - * API. A good starting point is \ref polyplib-context.h - * - * The asynchronous API relies on an abstract main loop API that is - * described in \ref mainloop-api.h. Two distinct implementations are - * available: - * - * \li \ref mainloop.h: a minimal but fast implementation based on poll() - * \li \ref glib-mainloop.h: a wrapper around GLIB's main loop - * - * UNIX signals may be hooked to a main loop using the functions from - * \ref mainloop-signal.h - * - * \section pkgconfig pkg-config - * - * The polypaudio libraries provide pkg-config snippets for the different modules. To use the - * asynchronous API use "polyplib" as pkg-config file. GLIB main loop - * support is available as "polyplib-glib-mainloop". The simple - * synchronous API is available as "polyplib-simple". - */ - -#endif diff --git a/src/polyp/scache.c b/src/polyp/scache.c new file mode 100644 index 00000000..c274e359 --- /dev/null +++ b/src/polyp/scache.c @@ -0,0 +1,127 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio 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. + + polypaudio 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 polypaudio; 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 "scache.h" +#include "internal.h" +#include + +void pa_stream_connect_upload(pa_stream *s, size_t length) { + pa_tagstruct *t; + uint32_t tag; + + assert(s && length); + + pa_stream_ref(s); + + s->state = PA_STREAM_CREATING; + s->direction = PA_STREAM_UPLOAD; + + t = pa_tagstruct_new(NULL, 0); + pa_tagstruct_putu32(t, PA_COMMAND_CREATE_UPLOAD_STREAM); + pa_tagstruct_putu32(t, tag = s->context->ctag++); + pa_tagstruct_puts(t, s->name); + pa_tagstruct_put_sample_spec(t, &s->sample_spec); + 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); + + pa_stream_unref(s); +} + +void pa_stream_finish_upload(pa_stream *s) { + pa_tagstruct *t; + uint32_t tag; + assert(s); + + if (!s->channel_valid || !s->context->state == PA_CONTEXT_READY) + return; + + pa_stream_ref(s); + + t = pa_tagstruct_new(NULL, 0); + assert(t); + + pa_tagstruct_putu32(t, PA_COMMAND_FINISH_UPLOAD_STREAM); + pa_tagstruct_putu32(t, tag = s->context->ctag++); + 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); + + pa_stream_unref(s); +} + +pa_operation * pa_context_play_sample(pa_context *c, const char *name, const char *dev, uint32_t volume, void (*cb)(pa_context *c, int success, void *userdata), void *userdata) { + pa_operation *o; + pa_tagstruct *t; + uint32_t tag; + assert(c && name && *name && (!dev || *dev)); + + o = pa_operation_new(c, NULL); + o->callback = (pa_operation_callback) cb; + o->userdata = userdata; + + if (!dev) + dev = c->conf->default_sink; + + t = pa_tagstruct_new(NULL, 0); + assert(t); + pa_tagstruct_putu32(t, PA_COMMAND_PLAY_SAMPLE); + pa_tagstruct_putu32(t, tag = c->ctag++); + pa_tagstruct_putu32(t, (uint32_t) -1); + 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, o); + + return pa_operation_ref(o); +} + +pa_operation* pa_context_remove_sample(pa_context *c, const char *name, void (*cb)(pa_context *c, int success, void *userdata), void *userdata) { + pa_operation *o; + pa_tagstruct *t; + uint32_t tag; + assert(c && name); + + o = pa_operation_new(c, NULL); + o->callback = (pa_operation_callback) cb; + o->userdata = userdata; + + t = pa_tagstruct_new(NULL, 0); + assert(t); + pa_tagstruct_putu32(t, PA_COMMAND_REMOVE_SAMPLE); + pa_tagstruct_putu32(t, tag = c->ctag++); + 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, o); + + return pa_operation_ref(o); +} + diff --git a/src/polyp/scache.h b/src/polyp/scache.h new file mode 100644 index 00000000..41b956d2 --- /dev/null +++ b/src/polyp/scache.h @@ -0,0 +1,50 @@ +#ifndef foopolyplibscachehfoo +#define foopolyplibscachehfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio 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. + + polypaudio 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 polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include + +#include +#include +#include + +/** \file + * All sample cache related routines */ + +PA_C_DECL_BEGIN + +/** Make this stream a sample upload stream */ +void 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 sample upload by issuing pa_stream_disconnect() */ +void 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, const char *name, const char *dev, uint32_t volume, void (*cb)(pa_context *c, int success, void *userdata), void *userdata); + +/** 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, void (*cb)(pa_context *c, int success, void *userdata), void *userdata); + +PA_C_DECL_END + +#endif diff --git a/src/polyp/simple.c b/src/polyp/simple.c new file mode 100644 index 00000000..6f20da89 --- /dev/null +++ b/src/polyp/simple.c @@ -0,0 +1,393 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio 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. + + polypaudio 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 polypaudio; 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 "simple.h" +#include "polyplib.h" +#include "mainloop.h" +#include +#include +#include + +struct pa_simple { + pa_mainloop *mainloop; + pa_context *context; + pa_stream *stream; + pa_stream_direction_t direction; + + int dead; + + void *read_data; + size_t read_index, read_length; + pa_usec_t latency; +}; + +static void read_callback(pa_stream *s, const void*data, size_t length, void *userdata); + +static int check_error(pa_simple *p, int *rerror) { + pa_context_state_t cst; + pa_stream_state_t sst; + assert(p); + + if ((cst = pa_context_get_state(p->context)) == PA_CONTEXT_FAILED) + goto fail; + + assert(cst != PA_CONTEXT_TERMINATED); + + if (p->stream) { + if ((sst = pa_stream_get_state(p->stream)) == PA_STREAM_FAILED) + goto fail; + + assert(sst != PA_STREAM_TERMINATED); + } + + return 0; + +fail: + if (rerror) + *rerror = pa_context_errno(p->context); + + p->dead = 1; + + return -1; +} + +static int iterate(pa_simple *p, int block, int *rerror) { + assert(p && p->context && p->mainloop); + + if (check_error(p, rerror) < 0) + return -1; + + if (!block && !pa_context_is_pending(p->context)) + return 0; + + do { + if (pa_mainloop_iterate(p->mainloop, 1, NULL) < 0) { + if (rerror) + *rerror = PA_ERROR_INTERNAL; + return -1; + } + + if (check_error(p, rerror) < 0) + return -1; + + } while (pa_context_is_pending(p->context)); + + + while (pa_mainloop_deferred_pending(p->mainloop)) { + + if (pa_mainloop_iterate(p->mainloop, 0, NULL) < 0) { + if (rerror) + *rerror = PA_ERROR_INTERNAL; + return -1; + } + + if (check_error(p, rerror) < 0) + return -1; + } + + return 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_buffer_attr *attr, + int *rerror) { + + pa_simple *p; + int error = PA_ERROR_INTERNAL; + assert(ss && (dir == PA_STREAM_PLAYBACK || dir == PA_STREAM_RECORD)); + + p = pa_xmalloc(sizeof(pa_simple)); + p->context = NULL; + p->stream = NULL; + p->mainloop = pa_mainloop_new(); + assert(p->mainloop); + p->dead = 0; + p->direction = dir; + p->read_data = NULL; + p->read_index = p->read_length = 0; + p->latency = 0; + + if (!(p->context = pa_context_new(pa_mainloop_get_api(p->mainloop), name))) + goto fail; + + pa_context_connect(p->context, server, 1, NULL); + + /* Wait until the context is ready */ + while (pa_context_get_state(p->context) != PA_CONTEXT_READY) { + if (iterate(p, 1, &error) < 0) + goto fail; + } + + if (!(p->stream = pa_stream_new(p->context, stream_name, ss, NULL))) + goto fail; + + if (dir == PA_STREAM_PLAYBACK) + pa_stream_connect_playback(p->stream, dev, attr, 0, NULL); + else + pa_stream_connect_record(p->stream, dev, attr, 0); + + /* Wait until the stream is ready */ + while (pa_stream_get_state(p->stream) != PA_STREAM_READY) { + if (iterate(p, 1, &error) < 0) + goto fail; + } + + pa_stream_set_read_callback(p->stream, read_callback, p); + + return p; + +fail: + if (rerror) + *rerror = error; + pa_simple_free(p); + return NULL; +} + +void pa_simple_free(pa_simple *s) { + assert(s); + + pa_xfree(s->read_data); + + if (s->stream) + pa_stream_unref(s->stream); + + if (s->context) + pa_context_unref(s->context); + + if (s->mainloop) + pa_mainloop_free(s->mainloop); + + pa_xfree(s); +} + +int pa_simple_write(pa_simple *p, const void*data, size_t length, int *rerror) { + assert(p && data && p->direction == PA_STREAM_PLAYBACK); + + if (p->dead) { + if (rerror) + *rerror = pa_context_errno(p->context); + + return -1; + } + + while (length > 0) { + size_t l; + + while (!(l = pa_stream_writable_size(p->stream))) + if (iterate(p, 1, rerror) < 0) + return -1; + + if (l > length) + l = length; + + pa_stream_write(p->stream, data, l, NULL, 0); + data = (const uint8_t*) data + l; + length -= l; + } + + /* Make sure that no data is pending for write */ + if (iterate(p, 0, rerror) < 0) + return -1; + + return 0; +} + +static void read_callback(pa_stream *s, const void*data, size_t length, void *userdata) { + pa_simple *p = userdata; + assert(s && data && length && p); + + if (p->read_data) { + pa_log(__FILE__": Buffer overflow, dropping incoming memory blocks.\n"); + pa_xfree(p->read_data); + } + + p->read_data = pa_xmemdup(data, p->read_length = length); + p->read_index = 0; +} + +int pa_simple_read(pa_simple *p, void*data, size_t length, int *rerror) { + assert(p && data && p->direction == PA_STREAM_RECORD); + + if (p->dead) { + if (rerror) + *rerror = pa_context_errno(p->context); + + return -1; + } + + while (length > 0) { + if (p->read_data) { + size_t l = length; + + if (p->read_length <= l) + l = p->read_length; + + memcpy(data, (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) { + pa_xfree(p->read_data); + p->read_data = NULL; + p->read_index = 0; + } + + if (!length) + return 0; + + assert(!p->read_data); + } + + if (iterate(p, 1, rerror) < 0) + return -1; + } + + return 0; +} + +static void drain_or_flush_complete(pa_stream *s, int success, void *userdata) { + pa_simple *p = userdata; + assert(s && p); + if (!success) + p->dead = 1; +} + +int pa_simple_drain(pa_simple *p, int *rerror) { + pa_operation *o; + assert(p && p->direction == PA_STREAM_PLAYBACK); + + if (p->dead) { + if (rerror) + *rerror = pa_context_errno(p->context); + + return -1; + } + + o = pa_stream_drain(p->stream, drain_or_flush_complete, p); + + while (pa_operation_get_state(o) == PA_OPERATION_RUNNING) { + if (iterate(p, 1, rerror) < 0) { + pa_operation_cancel(o); + pa_operation_unref(o); + return -1; + } + } + + pa_operation_unref(o); + + if (p->dead && rerror) + *rerror = pa_context_errno(p->context); + + return p->dead ? -1 : 0; +} + +static void latency_complete(pa_stream *s, const pa_latency_info *l, void *userdata) { + pa_simple *p = userdata; + assert(s && p); + + if (!l) + p->dead = 1; + else { + int negative = 0; + p->latency = pa_stream_get_latency(s, l, &negative); + if (negative) + p->latency = 0; + } +} + +pa_usec_t pa_simple_get_playback_latency(pa_simple *p, int *rerror) { + pa_operation *o; + assert(p && p->direction == PA_STREAM_PLAYBACK); + + if (p->dead) { + if (rerror) + *rerror = pa_context_errno(p->context); + + return (pa_usec_t) -1; + } + + p->latency = 0; + o = pa_stream_get_latency_info(p->stream, latency_complete, p); + + while (pa_operation_get_state(o) == PA_OPERATION_RUNNING) { + + if (iterate(p, 1, rerror) < 0) { + pa_operation_cancel(o); + pa_operation_unref(o); + return -1; + } + } + + pa_operation_unref(o); + + if (p->dead && rerror) + *rerror = pa_context_errno(p->context); + + return p->dead ? (pa_usec_t) -1 : p->latency; +} + +int pa_simple_flush(pa_simple *p, int *rerror) { + pa_operation *o; + assert(p && p->direction == PA_STREAM_PLAYBACK); + + if (p->dead) { + if (rerror) + *rerror = pa_context_errno(p->context); + + return -1; + } + + o = pa_stream_flush(p->stream, drain_or_flush_complete, p); + + while (pa_operation_get_state(o) == PA_OPERATION_RUNNING) { + if (iterate(p, 1, rerror) < 0) { + pa_operation_cancel(o); + pa_operation_unref(o); + return -1; + } + } + + pa_operation_unref(o); + + if (p->dead && rerror) + *rerror = pa_context_errno(p->context); + + return p->dead ? -1 : 0; +} diff --git a/src/polyp/simple.h b/src/polyp/simple.h new file mode 100644 index 00000000..31dcaef4 --- /dev/null +++ b/src/polyp/simple.h @@ -0,0 +1,80 @@ +#ifndef foopolyplibsimplehfoo +#define foopolyplibsimplehfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio 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. + + polypaudio 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 polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include + +#include "sample.h" +#include "def.h" +#include + +/** \file + * A simple but limited synchronous playback and recording + * API. This is synchronouse, 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 + +/** \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_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_playback_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/polyp/stream.c b/src/polyp/stream.c new file mode 100644 index 00000000..007d5e24 --- /dev/null +++ b/src/polyp/stream.c @@ -0,0 +1,807 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio 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. + + polypaudio 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 polypaudio; 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 +#include +#include +#include + +#define LATENCY_IPOL_INTERVAL_USEC (10000L) + +pa_stream *pa_stream_new(pa_context *c, const char *name, const pa_sample_spec *ss, const pa_channel_map *map) { + pa_stream *s; + assert(c); + assert(ss); + + if (!pa_sample_spec_valid(ss)) + return NULL; + + if (map && !pa_channel_map_valid(map)) + return NULL; + + 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->direction = PA_STREAM_NODIRECTION; + s->name = pa_xstrdup(name); + s->sample_spec = *ss; + + if (map) + s->channel_map = *map; + else + pa_channel_map_init_auto(&s->channel_map, ss->channels); + + s->channel = 0; + s->channel_valid = 0; + s->device_index = PA_INVALID_INDEX; + s->requested_bytes = 0; + s->state = PA_STREAM_DISCONNECTED; + memset(&s->buffer_attr, 0, sizeof(s->buffer_attr)); + + s->mcalign = pa_mcalign_new(pa_frame_size(ss), c->memblock_stat); + + s->counter = 0; + s->previous_time = 0; + s->previous_ipol_time = 0; + + s->corked = 0; + s->interpolate = 0; + + s->ipol_usec = 0; + memset(&s->ipol_timestamp, 0, sizeof(s->ipol_timestamp)); + s->ipol_event = NULL; + s->ipol_requested = 0; + + PA_LLIST_PREPEND(pa_stream, c->streams, s); + + return pa_stream_ref(s); +} + +static void stream_free(pa_stream *s) { + assert(s); + + if (s->ipol_event) { + assert(s->mainloop); + s->mainloop->time_free(s->ipol_event); + } + + pa_mcalign_free(s->mcalign); + + pa_xfree(s->name); + pa_xfree(s); +} + +void pa_stream_unref(pa_stream *s) { + assert(s && s->ref >= 1); + + if (--(s->ref) == 0) + stream_free(s); +} + +pa_stream* pa_stream_ref(pa_stream *s) { + assert(s && s->ref >= 1); + s->ref++; + return s; +} + +pa_stream_state_t pa_stream_get_state(pa_stream *s) { + assert(s && s->ref >= 1); + return s->state; +} + +pa_context* pa_stream_get_context(pa_stream *s) { + assert(s && s->ref >= 1); + return s->context; +} + +uint32_t pa_stream_get_index(pa_stream *s) { + assert(s && s->ref >= 1); + return s->device_index; +} + +void pa_stream_set_state(pa_stream *s, pa_stream_state_t st) { + assert(s && s->ref >= 1); + + if (s->state == st) + return; + + pa_stream_ref(s); + + s->state = st; + + if ((st == PA_STREAM_FAILED || st == PA_STREAM_TERMINATED) && s->context) { + 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); + } + + if (s->state_callback) + s->state_callback(s, s->state_userdata); + + 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 && (command == PA_COMMAND_PLAYBACK_STREAM_KILLED || command == PA_COMMAND_RECORD_STREAM_KILLED) && t && c); + + pa_context_ref(c); + + if (pa_tagstruct_getu32(t, &channel) < 0 || + !pa_tagstruct_eof(t)) { + pa_context_fail(c, PA_ERROR_PROTOCOL); + goto finish; + } + + if (!(s = pa_dynarray_get(command == PA_COMMAND_PLAYBACK_STREAM_KILLED ? c->playback_streams : c->record_streams, channel))) + goto finish; + + c->error = PA_ERROR_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 && command == PA_COMMAND_REQUEST && t && 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_ERROR_PROTOCOL); + goto finish; + } + + if (!(s = pa_dynarray_get(c->playback_streams, channel))) + goto finish; + + if (s->state != PA_STREAM_READY) + goto finish; + + pa_stream_ref(s); + + s->requested_bytes += bytes; + + if (s->requested_bytes && s->write_callback) + s->write_callback(s, s->requested_bytes, s->write_userdata); + + pa_stream_unref(s); + +finish: + pa_context_unref(c); +} + +static void ipol_callback(pa_mainloop_api *m, pa_time_event *e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) { + struct timeval tv2; + pa_stream *s = userdata; + + pa_stream_ref(s); + +/* pa_log("requesting new ipol data\n"); */ + + if (s->state == PA_STREAM_READY && !s->ipol_requested) { + pa_operation_unref(pa_stream_get_latency_info(s, NULL, NULL)); + s->ipol_requested = 1; + } + + pa_gettimeofday(&tv2); + pa_timeval_add(&tv2, LATENCY_IPOL_INTERVAL_USEC); + + m->time_restart(e, &tv2); + + 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 && s && 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_tagstruct_eof(t)) { + pa_context_fail(s->context, PA_ERROR_PROTOCOL); + goto finish; + } + + 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->interpolate) { + struct timeval tv; + pa_operation_unref(pa_stream_get_latency_info(s, NULL, NULL)); + + pa_gettimeofday(&tv); + tv.tv_usec += LATENCY_IPOL_INTERVAL_USEC; /* every 100 ms */ + + assert(!s->ipol_event); + s->ipol_event = s->mainloop->time_new(s->mainloop, &tv, &ipol_callback, s); + } + + if (s->requested_bytes && s->ref > 1 && s->write_callback) + s->write_callback(s, s->requested_bytes, s->write_userdata); + +finish: + pa_stream_unref(s); +} + +static void create_stream(pa_stream *s, const char *dev, const pa_buffer_attr *attr, pa_stream_flags_t flags, const pa_cvolume *volume) { + pa_tagstruct *t; + uint32_t tag; + assert(s && s->ref >= 1 && s->state == PA_STREAM_DISCONNECTED); + + pa_stream_ref(s); + + s->interpolate = !!(flags & PA_STREAM_INTERPOLATE_LATENCY); + pa_stream_trash_ipol(s); + + 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; + } + + pa_stream_set_state(s, PA_STREAM_CREATING); + + t = pa_tagstruct_new(NULL, 0); + assert(t); + + if (!dev) { + if (s->direction == PA_STREAM_PLAYBACK) + dev = s->context->conf->default_sink; + else + dev = s->context->conf->default_source; + } + + pa_tagstruct_put(t, + PA_TAG_U32, s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_CREATE_PLAYBACK_STREAM : PA_COMMAND_CREATE_RECORD_STREAM, + PA_TAG_U32, tag = s->context->ctag++, + 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_INVALID); + + if (!volume) { + pa_cvolume_reset(&cv, s->sample_spec.channels); + volume = &cv; + } + + 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); + + pa_stream_unref(s); +} + +void pa_stream_connect_playback(pa_stream *s, const char *dev, const pa_buffer_attr *attr, pa_stream_flags_t flags, pa_cvolume *volume) { + assert(s && s->context->state == PA_CONTEXT_READY && s->ref >= 1); + s->direction = PA_STREAM_PLAYBACK; + create_stream(s, dev, attr, flags, volume); +} + +void pa_stream_connect_record(pa_stream *s, const char *dev, const pa_buffer_attr *attr, pa_stream_flags_t flags) { + assert(s && s->context->state == PA_CONTEXT_READY && s->ref >= 1); + s->direction = PA_STREAM_RECORD; + create_stream(s, dev, attr, flags, 0); +} + +void pa_stream_write(pa_stream *s, const void *data, size_t length, void (*free_cb)(void *p), size_t delta) { + pa_memchunk chunk; + assert(s && s->context && data && length && s->state == PA_STREAM_READY && s->ref >= 1); + + if (free_cb) { + chunk.memblock = pa_memblock_new_user((void*) data, length, free_cb, 1, s->context->memblock_stat); + assert(chunk.memblock && chunk.memblock->data); + } else { + chunk.memblock = pa_memblock_new(length, s->context->memblock_stat); + assert(chunk.memblock && chunk.memblock->data); + memcpy(chunk.memblock->data, data, length); + } + chunk.index = 0; + chunk.length = length; + + pa_pstream_send_memblock(s->context->pstream, s->channel, delta, &chunk); + pa_memblock_unref(chunk.memblock); + + if (length < s->requested_bytes) + s->requested_bytes -= length; + else + s->requested_bytes = 0; + + s->counter += length; +} + +size_t pa_stream_writable_size(pa_stream *s) { + assert(s && s->ref >= 1); + return s->state == PA_STREAM_READY ? s->requested_bytes : 0; +} + +pa_operation * pa_stream_drain(pa_stream *s, void (*cb) (pa_stream*s, int success, void *userdata), void *userdata) { + pa_operation *o; + pa_tagstruct *t; + uint32_t tag; + assert(s && s->ref >= 1 && s->state == PA_STREAM_READY); + + o = pa_operation_new(s->context, s); + assert(o); + o->callback = (pa_operation_callback) cb; + o->userdata = userdata; + + t = pa_tagstruct_new(NULL, 0); + assert(t); + pa_tagstruct_putu32(t, PA_COMMAND_DRAIN_PLAYBACK_STREAM); + pa_tagstruct_putu32(t, tag = s->context->ctag++); + 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, o); + + return pa_operation_ref(o); +} + +static void stream_get_latency_info_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { + pa_operation *o = userdata; + pa_latency_info i, *p = NULL; + struct timeval local, remote, now; + assert(pd && o && o->stream && o->context); + + 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.buffer_usec) < 0 || + 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_getu32(t, &i.queue_length) < 0 || + pa_tagstruct_get_timeval(t, &local) < 0 || + pa_tagstruct_get_timeval(t, &remote) < 0 || + pa_tagstruct_getu64(t, &i.counter) < 0 || + !pa_tagstruct_eof(t)) { + pa_context_fail(o->context, PA_ERROR_PROTOCOL); + goto finish; + } else { + pa_gettimeofday(&now); + + 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); + } + + if (o->stream->interpolate) { +/* pa_log("new interpol data\n"); */ + o->stream->ipol_timestamp = i.timestamp; + o->stream->ipol_usec = pa_stream_get_time(o->stream, &i); + o->stream->ipol_requested = 0; + } + + p = &i; + } + + if (o->callback) { + void (*cb)(pa_stream *s, const pa_latency_info *_i, void *_userdata) = (void (*)(pa_stream *s, const pa_latency_info *_i, void *_userdata)) o->callback; + cb(o->stream, p, o->userdata); + } + +finish: + pa_operation_done(o); + pa_operation_unref(o); +} + +pa_operation* pa_stream_get_latency_info(pa_stream *s, void (*cb)(pa_stream *p, const pa_latency_info*i, void *userdata), void *userdata) { + uint32_t tag; + pa_operation *o; + pa_tagstruct *t; + struct timeval now; + assert(s && s->direction != PA_STREAM_UPLOAD); + + o = pa_operation_new(s->context, s); + assert(o); + o->callback = (pa_operation_callback) cb; + o->userdata = userdata; + + t = pa_tagstruct_new(NULL, 0); + assert(t); + pa_tagstruct_putu32(t, s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_GET_PLAYBACK_LATENCY : PA_COMMAND_GET_RECORD_LATENCY); + pa_tagstruct_putu32(t, tag = s->context->ctag++); + pa_tagstruct_putu32(t, s->channel); + + pa_gettimeofday(&now); + pa_tagstruct_put_timeval(t, &now); + pa_tagstruct_putu64(t, s->counter); + + pa_pstream_send_tagstruct(s->context->pstream, t); + pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, stream_get_latency_info_callback, o); + + return pa_operation_ref(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 && s && 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_ERROR_PROTOCOL); + goto finish; + } + + pa_stream_set_state(s, PA_STREAM_TERMINATED); + +finish: + pa_stream_unref(s); +} + +void pa_stream_disconnect(pa_stream *s) { + pa_tagstruct *t; + uint32_t tag; + assert(s && s->ref >= 1); + + if (!s->channel_valid || !s->context->state == PA_CONTEXT_READY) + return; + + pa_stream_ref(s); + + t = pa_tagstruct_new(NULL, 0); + assert(t); + + pa_tagstruct_putu32(t, 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)); + pa_tagstruct_putu32(t, tag = s->context->ctag++); + 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); + + pa_stream_unref(s); +} + +void pa_stream_set_read_callback(pa_stream *s, void (*cb)(pa_stream *p, const void*data, size_t length, void *userdata), void *userdata) { + assert(s && s->ref >= 1); + s->read_callback = cb; + s->read_userdata = userdata; +} + +void pa_stream_set_write_callback(pa_stream *s, void (*cb)(pa_stream *p, size_t length, void *userdata), void *userdata) { + assert(s && s->ref >= 1); + s->write_callback = cb; + s->write_userdata = userdata; +} + +void pa_stream_set_state_callback(pa_stream *s, void (*cb)(pa_stream *s, void *userdata), void *userdata) { + assert(s && s->ref >= 1); + s->state_callback = cb; + s->state_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 && o && o->context && o->ref >= 1); + + 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_ERROR_PROTOCOL); + goto finish; + } + + if (o->callback) { + void (*cb)(pa_stream *s, int _success, void *_userdata) = (void (*)(pa_stream *s, int _success, void *_userdata)) 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, void (*cb) (pa_stream*s, int success, void *userdata), void *userdata) { + pa_operation *o; + pa_tagstruct *t; + uint32_t tag; + assert(s && s->ref >= 1 && s->state == PA_STREAM_READY); + + if (s->interpolate) { + if (!s->corked && b) + /* Pausing */ + s->ipol_usec = pa_stream_get_interpolated_time(s); + else if (s->corked && !b) + /* Unpausing */ + pa_gettimeofday(&s->ipol_timestamp); + } + + s->corked = b; + + o = pa_operation_new(s->context, s); + assert(o); + o->callback = (pa_operation_callback) cb; + o->userdata = userdata; + + t = pa_tagstruct_new(NULL, 0); + assert(t); + pa_tagstruct_putu32(t, s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_CORK_PLAYBACK_STREAM : PA_COMMAND_CORK_RECORD_STREAM); + pa_tagstruct_putu32(t, tag = s->context->ctag++); + 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, o); + + pa_operation_unref(pa_stream_get_latency_info(s, NULL, NULL)); + + return pa_operation_ref(o); +} + +static pa_operation* stream_send_simple_command(pa_stream *s, uint32_t command, void (*cb)(pa_stream *s, int success, void *userdata), void *userdata) { + pa_tagstruct *t; + pa_operation *o; + uint32_t tag; + assert(s && s->ref >= 1 && s->state == PA_STREAM_READY); + + o = pa_operation_new(s->context, s); + o->callback = (pa_operation_callback) cb; + o->userdata = userdata; + + t = pa_tagstruct_new(NULL, 0); + pa_tagstruct_putu32(t, command); + pa_tagstruct_putu32(t, tag = s->context->ctag++); + 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, o); + + return pa_operation_ref(o); +} + +pa_operation* pa_stream_flush(pa_stream *s, void (*cb)(pa_stream *s, int success, void *userdata), void *userdata) { + pa_operation *o; + o = stream_send_simple_command(s, s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_FLUSH_PLAYBACK_STREAM : PA_COMMAND_FLUSH_RECORD_STREAM, cb, userdata); + pa_operation_unref(pa_stream_get_latency_info(s, NULL, NULL)); + return o; +} + +pa_operation* pa_stream_prebuf(pa_stream *s, void (*cb)(pa_stream *s, int success, void *userdata), void *userdata) { + pa_operation *o; + o = stream_send_simple_command(s, PA_COMMAND_PREBUF_PLAYBACK_STREAM, cb, userdata); + pa_operation_unref(pa_stream_get_latency_info(s, NULL, NULL)); + return o; +} + +pa_operation* pa_stream_trigger(pa_stream *s, void (*cb)(pa_stream *s, int success, void *userdata), void *userdata) { + pa_operation *o; + o = stream_send_simple_command(s, PA_COMMAND_TRIGGER_PLAYBACK_STREAM, cb, userdata); + pa_operation_unref(pa_stream_get_latency_info(s, NULL, NULL)); + return o; +} + +pa_operation* pa_stream_set_name(pa_stream *s, const char *name, void(*cb)(pa_stream*c, int success, void *userdata), void *userdata) { + pa_operation *o; + pa_tagstruct *t; + uint32_t tag; + assert(s && s->ref >= 1 && s->state == PA_STREAM_READY && name && s->direction != PA_STREAM_UPLOAD); + + o = pa_operation_new(s->context, s); + assert(o); + o->callback = (pa_operation_callback) cb; + o->userdata = userdata; + + t = pa_tagstruct_new(NULL, 0); + assert(t); + pa_tagstruct_putu32(t, s->direction == PA_STREAM_RECORD ? PA_COMMAND_SET_RECORD_STREAM_NAME : PA_COMMAND_SET_PLAYBACK_STREAM_NAME); + pa_tagstruct_putu32(t, tag = s->context->ctag++); + 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, o); + + return pa_operation_ref(o); +} + +uint64_t pa_stream_get_counter(pa_stream *s) { + assert(s); + return s->counter; +} + +pa_usec_t pa_stream_get_time(pa_stream *s, const pa_latency_info *i) { + pa_usec_t usec; + assert(s); + + usec = pa_bytes_to_usec(i->counter, &s->sample_spec); + + if (i) { + if (s->direction == PA_STREAM_PLAYBACK) { + pa_usec_t latency = i->transport_usec + i->buffer_usec + i->sink_usec; + if (usec < latency) + usec = 0; + else + usec -= latency; + + } else if (s->direction == PA_STREAM_RECORD) { + usec += i->source_usec + i->buffer_usec + i->transport_usec; + + if (usec > i->sink_usec) + usec -= i->sink_usec; + else + usec = 0; + } + } + + if (usec < s->previous_time) + usec = s->previous_time; + + s->previous_time = usec; + + return usec; +} + +static pa_usec_t time_counter_diff(pa_stream *s, pa_usec_t t, pa_usec_t c, int *negative) { + assert(s); + + if (negative) + *negative = 0; + + if (c < t) { + if (s->direction == PA_STREAM_RECORD) { + if (negative) + *negative = 1; + + return t-c; + } else + return 0; + } else + return c-t; +} + +pa_usec_t pa_stream_get_latency(pa_stream *s, const pa_latency_info *i, int *negative) { + pa_usec_t t, c; + assert(s && i); + + t = pa_stream_get_time(s, i); + c = pa_bytes_to_usec(s->counter, &s->sample_spec); + + return time_counter_diff(s, t, c, negative); +} + +const pa_sample_spec* pa_stream_get_sample_spec(pa_stream *s) { + assert(s); + return &s->sample_spec; +} + +void pa_stream_trash_ipol(pa_stream *s) { + assert(s); + + if (!s->interpolate) + return; + + memset(&s->ipol_timestamp, 0, sizeof(s->ipol_timestamp)); + s->ipol_usec = 0; +} + +pa_usec_t pa_stream_get_interpolated_time(pa_stream *s) { + pa_usec_t usec; + assert(s && s->interpolate); + + if (s->corked) + usec = s->ipol_usec; + else { + if (s->ipol_timestamp.tv_sec == 0) + usec = 0; + else + usec = s->ipol_usec + pa_timeval_age(&s->ipol_timestamp); + } + + if (usec < s->previous_ipol_time) + usec = s->previous_ipol_time; + + s->previous_ipol_time = usec; + + return usec; +} + +pa_usec_t pa_stream_get_interpolated_latency(pa_stream *s, int *negative) { + pa_usec_t t, c; + assert(s && s->interpolate); + + t = pa_stream_get_interpolated_time(s); + c = pa_bytes_to_usec(s->counter, &s->sample_spec); + return time_counter_diff(s, t, c, negative); +} diff --git a/src/polyp/stream.h b/src/polyp/stream.h new file mode 100644 index 00000000..d8409b3b --- /dev/null +++ b/src/polyp/stream.h @@ -0,0 +1,181 @@ +#ifndef foopolyplibstreamhfoo +#define foopolyplibstreamhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio 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. + + polypaudio 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 polypaudio; 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 + +/** \file + * Audio streams for input, output and sample upload */ + +PA_C_DECL_BEGIN + +/** \pa_stream + * An opaque stream for playback or recording */ +typedef struct pa_stream pa_stream; + +/** Create a new, unconnected stream with the specified name and sample type */ +pa_stream* pa_stream_new(pa_context *c, const char *name, const pa_sample_spec *ss, const pa_channel_map *map); + +/** 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 */ +void pa_stream_connect_playback( + pa_stream *s, + const char *dev, + const pa_buffer_attr *attr, + pa_stream_flags_t flags, + pa_cvolume *volume); + +/** Connect the stream to a source */ +void pa_stream_connect_record( + pa_stream *s, + const char *dev, + const pa_buffer_attr *attr, + pa_stream_flags_t flags); + +/** Disconnect a stream from a source/sink */ +void 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. */ +void 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 */, + void (*free_cb)(void *p) /**< A cleanup routine for the data or NULL to request an internal copy */, + size_t delta /**< Drop this many + bytes in the playback + buffer before writing + this data. Use + (size_t) -1 for + clearing the whole + playback + buffer. Normally you + will specify 0 here, + i.e. append to the + playback buffer. If + the value given here + is greater than the + buffered data length + the buffer is cleared + and the data is + written to the + buffer's start. This + value is ignored on + upload streams. */); + +/** Return the amount of bytes that may be written using pa_stream_write() */ +size_t pa_stream_writable_size(pa_stream *p); + +/** Drain a playback stream */ +pa_operation* pa_stream_drain(pa_stream *s, void (*cb) (pa_stream*s, int success, void *userdata), void *userdata); + +/** Get the playback latency of a stream */ +pa_operation* pa_stream_get_latency_info(pa_stream *p, void (*cb)(pa_stream *p, const pa_latency_info *i, void *userdata), 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, void (*cb)(pa_stream *s, void *userdata), 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, void (*cb)(pa_stream *p, size_t length, void *userdata), void *userdata); + +/** Set the callback function that is called when new data is available from the stream */ +void pa_stream_set_read_callback(pa_stream *p, void (*cb)(pa_stream *p, const void*data, size_t length, void *userdata), 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, void (*cb) (pa_stream*s, int success, void *userdata), 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, void (*cb)(pa_stream *s, int success, void *userdata), void *userdata); + +/** Reenable prebuffering. Available for playback streams only. \since 0.6 */ +pa_operation* pa_stream_prebuf(pa_stream *s, void (*cb)(pa_stream *s, int success, void *userdata), void *userdata); + +/** Request immediate start of playback on this stream. This disables + * prebuffering as specified in the pa_buffer_attr structure. Available for playback streams only. \since + * 0.3 */ +pa_operation* pa_stream_trigger(pa_stream *s, void (*cb)(pa_stream *s, int success, void *userdata), void *userdata); + +/** Rename the stream. \since 0.5 */ +pa_operation* pa_stream_set_name(pa_stream *s, const char *name, void(*cb)(pa_stream*c, int success, void *userdata), void *userdata); + +/** Return the total number of bytes written to/read from the + * stream. This counter is not reset on pa_stream_flush(), you may do + * this yourself using pa_stream_reset_counter(). \since 0.6 */ +uint64_t pa_stream_get_counter(pa_stream *s); + +/** Return the current playback/recording time. This is based on the + * counter accessible with pa_stream_get_counter(). This function + * requires a pa_latency_info structure as argument, which should be + * acquired using pa_stream_get_latency(). \since 0.6 */ +pa_usec_t pa_stream_get_time(pa_stream *s, const pa_latency_info *i); + +/** Return the total stream latency. Thus function requires a + * pa_latency_info structure as argument, which should be aquired + * using pa_stream_get_latency(). 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 */ +pa_usec_t pa_stream_get_latency(pa_stream *s, const pa_latency_info *i, int *negative); + +/** Return the interpolated playback/recording time. Requires the + * PA_STREAM_INTERPOLATE_LATENCY bit set when creating the stream. In + * contrast to pa_stream_get_latency() this function doesn't require + * a whole roundtrip for response. \since 0.6 */ +pa_usec_t pa_stream_get_interpolated_time(pa_stream *s); + +/** Return the interpolated playback/recording latency. Requires the + * PA_STREAM_INTERPOLATE_LATENCY bit set when creating the + * stream. \since 0.6 */ +pa_usec_t pa_stream_get_interpolated_latency(pa_stream *s, int *negative); + +/** Return a pointer to the streams sample specification. \since 0.6 */ +const pa_sample_spec* pa_stream_get_sample_spec(pa_stream *s); + +PA_C_DECL_END + +#endif diff --git a/src/polyp/subscribe.c b/src/polyp/subscribe.c new file mode 100644 index 00000000..c481f525 --- /dev/null +++ b/src/polyp/subscribe.c @@ -0,0 +1,81 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio 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. + + polypaudio 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 polypaudio; 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 "subscribe.h" +#include "internal.h" +#include +#include + +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 index; + assert(pd && command == PA_COMMAND_SUBSCRIBE_EVENT && t && c); + + pa_context_ref(c); + + if (pa_tagstruct_getu32(t, &e) < 0 || + pa_tagstruct_getu32(t, &index) < 0 || + !pa_tagstruct_eof(t)) { + pa_context_fail(c, PA_ERROR_PROTOCOL); + goto finish; + } + + if (c->subscribe_callback) + c->subscribe_callback(c, e, index, c->subscribe_userdata); + +finish: + pa_context_unref(c); +} + + +pa_operation* pa_context_subscribe(pa_context *c, pa_subscription_mask_t m, void (*cb)(pa_context *c, int success, void *userdata), void *userdata) { + pa_operation *o; + pa_tagstruct *t; + uint32_t tag; + assert(c); + + o = pa_operation_new(c, NULL); + o->callback = (pa_operation_callback) cb; + o->userdata = userdata; + + t = pa_tagstruct_new(NULL, 0); + pa_tagstruct_putu32(t, PA_COMMAND_SUBSCRIBE); + pa_tagstruct_putu32(t, tag = c->ctag++); + 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, o); + + return pa_operation_ref(o); +} + +void pa_context_set_subscribe_callback(pa_context *c, void (*cb)(pa_context *c, pa_subscription_event_type_t t, uint32_t index, void *userdata), void *userdata) { + assert(c); + c->subscribe_callback = cb; + c->subscribe_userdata = userdata; +} diff --git a/src/polyp/subscribe.h b/src/polyp/subscribe.h new file mode 100644 index 00000000..4986272d --- /dev/null +++ b/src/polyp/subscribe.h @@ -0,0 +1,47 @@ +#ifndef foopolyplibsubscribehfoo +#define foopolyplibsubscribehfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio 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. + + polypaudio 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 polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include + +#include +#include +#include + +/** \file + * Daemon introspection event subscription subsystem. Use this + * to be notified whenever the internal layout of daemon changes: + * i.e. entities such as sinks or sources are create, removed or + * modified. */ + +PA_C_DECL_BEGIN + +/** Enable event notification */ +pa_operation* pa_context_subscribe(pa_context *c, pa_subscription_mask_t m, void (*cb)(pa_context *c, int success, void *userdata), 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, void (*cb)(pa_context *c, pa_subscription_event_type_t t, uint32_t index, void *userdata), void *userdata); + +PA_C_DECL_END + +#endif diff --git a/src/polyp/version.h.in b/src/polyp/version.h.in new file mode 100644 index 00000000..36cafb70 --- /dev/null +++ b/src/polyp/version.h.in @@ -0,0 +1,47 @@ +#ifndef foopolyplibversionhfoo /*-*-C-*-*/ +#define foopolyplibversionhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio 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. + + polypaudio 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 polypaudio; 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! */ + +/** \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 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@ + +PA_C_DECL_END + +#endif diff --git a/src/polypcore/native-common.h b/src/polypcore/native-common.h index 78ae721e..ac3ea823 100644 --- a/src/polypcore/native-common.h +++ b/src/polypcore/native-common.h @@ -23,7 +23,7 @@ ***/ #include -#include +#include PA_C_DECL_BEGIN diff --git a/src/polypcore/sink.c b/src/polypcore/sink.c index 411befe7..f29afba7 100644 --- a/src/polypcore/sink.c +++ b/src/polypcore/sink.c @@ -36,7 +36,7 @@ #include "xmalloc.h" #include "subscribe.h" #include "log.h" -#include +#include #define MAX_MIX_CHANNELS 32 diff --git a/src/tests/pacat-simple.c b/src/tests/pacat-simple.c index 8b3a7b22..0382ec06 100644 --- a/src/tests/pacat-simple.c +++ b/src/tests/pacat-simple.c @@ -28,8 +28,8 @@ #include #include -#include -#include +#include +#include #include #define BUFSIZE 1024 diff --git a/src/tests/parec-simple.c b/src/tests/parec-simple.c index 10eaea8d..fc2314ac 100644 --- a/src/tests/parec-simple.c +++ b/src/tests/parec-simple.c @@ -28,8 +28,8 @@ #include #include -#include -#include +#include +#include #include #define BUFSIZE 1024 diff --git a/src/utils/pabrowse.c b/src/utils/pabrowse.c index 634c308a..290531e6 100644 --- a/src/utils/pabrowse.c +++ b/src/utils/pabrowse.c @@ -29,7 +29,7 @@ #include #include -#include +#include #include static void exit_signal_callback(pa_mainloop_api*m, pa_signal_event *e, int sig, void *userdata) { diff --git a/src/utils/pacat.c b/src/utils/pacat.c index bd2b64fd..a4f3d3af 100644 --- a/src/utils/pacat.c +++ b/src/utils/pacat.c @@ -33,10 +33,10 @@ #include #include -#include +#include #include #include -#include +#include #if PA_API_VERSION != 8 #error Invalid Polypaudio API version diff --git a/src/utils/pactl.c b/src/utils/pactl.c index 23bd924b..7d903eb8 100644 --- a/src/utils/pactl.c +++ b/src/utils/pactl.c @@ -36,7 +36,7 @@ #include #include -#include +#include #include #include #include diff --git a/src/utils/paplay.c b/src/utils/paplay.c index ddc1cbc1..1b3697fa 100644 --- a/src/utils/paplay.c +++ b/src/utils/paplay.c @@ -35,10 +35,10 @@ #include #include -#include +#include #include #include -#include +#include #if PA_API_VERSION != 8 #error Invalid Polypaudio API version -- cgit