diff options
37 files changed, 605 insertions, 92 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index dd9035b5..e570a6d2 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -716,6 +716,7 @@ libpulsecore_@PA_MAJORMINORMICRO@_la_SOURCES = \ pulsecore/cli-command.c pulsecore/cli-command.h \ pulsecore/cli-text.c pulsecore/cli-text.h \ pulsecore/client.c pulsecore/client.h \ + pulsecore/card.c pulsecore/card.h \ pulsecore/core-scache.c pulsecore/core-scache.h \ pulsecore/core-subscribe.c pulsecore/core-subscribe.h \ pulsecore/core.c pulsecore/core.h \ diff --git a/src/modules/module-alsa-sink.c b/src/modules/module-alsa-sink.c index 6fa45e32..3fa0b5db 100644 --- a/src/modules/module-alsa-sink.c +++ b/src/modules/module-alsa-sink.c @@ -287,7 +287,8 @@ static int mmap_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polle if (polled) pa_log("ALSA woke us up to write new data to the device, but there was actually nothing to write! " - "Most likely this is an ALSA driver bug. Please report this issue to the PulseAudio developers."); + "Most likely this is an ALSA driver bug. Please report this issue to the ALSA developers. " + "We were woken up with POLLOUT set -- however a subsequent snd_pcm_avail_update() returned 0."); break; } @@ -409,7 +410,8 @@ static int unix_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polle if (polled) pa_log("ALSA woke us up to write new data to the device, but there was actually nothing to write! " - "Most likely this is an ALSA driver bug. Please report this issue to the PulseAudio developers."); + "Most likely this is an ALSA driver bug. Please report this issue to the ALSA developers. " + "We were woken up with POLLOUT set -- however a subsequent snd_pcm_avail_update() returned 0."); break; } diff --git a/src/modules/module-alsa-source.c b/src/modules/module-alsa-source.c index 768c8c19..22e9ebfc 100644 --- a/src/modules/module-alsa-source.c +++ b/src/modules/module-alsa-source.c @@ -274,7 +274,8 @@ static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled if (polled) pa_log("ALSA woke us up to read new data from the device, but there was actually nothing to read! " - "Most likely this is an ALSA driver bug. Please report this issue to the PulseAudio device."); + "Most likely this is an ALSA driver bug. Please report this issue to the ALSA developers. " + "We were woken up with POLLIN set -- however a subsequent snd_pcm_avail_update() returned 0."); break; } @@ -381,7 +382,8 @@ static int unix_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled if (polled) pa_log("ALSA woke us up to read new data from the device, but there was actually nothing to read! " - "Most likely this is an ALSA driver bug. Please report this issue to the PulseAudio developers."); + "Most likely this is an ALSA driver bug. Please report this issue to the ALSA developers. " + "We were woken up with POLLIN set -- however a subsequent snd_pcm_avail_update() returned 0."); return work_done; } diff --git a/src/modules/module-combine.c b/src/modules/module-combine.c index d61d127a..fa807815 100644 --- a/src/modules/module-combine.c +++ b/src/modules/module-combine.c @@ -504,7 +504,7 @@ static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t s * we are heard right-away. */ if (PA_SINK_INPUT_IS_LINKED(state) && i->thread_info.state == PA_SINK_INPUT_INIT) - pa_sink_input_request_rewind(i, 0, FALSE, TRUE); + pa_sink_input_request_rewind(i, 0, FALSE, TRUE, TRUE); } /* Called from thread context */ diff --git a/src/modules/module-console-kit.c b/src/modules/module-console-kit.c index e1933c28..4f3ed8dd 100644 --- a/src/modules/module-console-kit.c +++ b/src/modules/module-console-kit.c @@ -63,6 +63,7 @@ struct session { }; struct userdata { + pa_module *module; pa_core *core; pa_dbus_connection *connection; pa_hashmap *sessions; @@ -73,7 +74,7 @@ static void add_session(struct userdata *u, const char *id) { DBusMessage *m = NULL, *reply = NULL; uint32_t uid; struct session *session; - char *t; + pa_client_new_data data; dbus_error_init (&error); @@ -109,11 +110,19 @@ static void add_session(struct userdata *u, const char *id) { session = pa_xnew(struct session, 1); session->id = pa_xstrdup(id); - t = pa_sprintf_malloc("ConsoleKit Session %s", id); - session->client = pa_client_new(u->core, __FILE__, t); - pa_xfree(t); - - pa_proplist_sets(session->client->proplist, "console-kit.session", id); + pa_client_new_data_init(&data); + data.module = u->module; + data.driver = __FILE__; + pa_proplist_setf(data.proplist, PA_PROP_APPLICATION_NAME, "ConsoleKit Session %s", id); + pa_proplist_sets(data.proplist, "console-kit.session", id); + session->client = pa_client_new(u->core, &data); + pa_client_new_data_done(&data); + + if (!session->client) { + pa_xfree(session->id); + pa_xfree(session); + goto fail; + } pa_hashmap_put(u->sessions, session->id, session); @@ -295,6 +304,7 @@ int pa__init(pa_module*m) { m->userdata = u = pa_xnew(struct userdata, 1); u->core = m->core; + u->module = m; u->connection = connection; u->sessions = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); diff --git a/src/modules/module-ladspa-sink.c b/src/modules/module-ladspa-sink.c index a27ed712..4209e991 100644 --- a/src/modules/module-ladspa-sink.c +++ b/src/modules/module-ladspa-sink.c @@ -144,7 +144,7 @@ static void sink_request_rewind(pa_sink *s) { pa_assert_se(u = s->userdata); /* Just hand this one over to the master sink */ - pa_sink_input_request_rewind(u->sink_input, s->thread_info.rewind_nbytes + pa_memblockq_get_length(u->memblockq), TRUE, FALSE); + pa_sink_input_request_rewind(u->sink_input, s->thread_info.rewind_nbytes + pa_memblockq_get_length(u->memblockq), TRUE, FALSE, FALSE); } /* Called from I/O thread context */ @@ -355,7 +355,7 @@ static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t s if (PA_SINK_INPUT_IS_LINKED(state) && i->thread_info.state == PA_SINK_INPUT_INIT) { pa_log_debug("Requesting rewind due to state change."); - pa_sink_input_request_rewind(i, 0, FALSE, TRUE); + pa_sink_input_request_rewind(i, 0, FALSE, TRUE, TRUE); } } diff --git a/src/modules/module-remap-sink.c b/src/modules/module-remap-sink.c index 976a8ce5..ae05f078 100644 --- a/src/modules/module-remap-sink.c +++ b/src/modules/module-remap-sink.c @@ -119,7 +119,7 @@ static void sink_request_rewind(pa_sink *s) { pa_sink_assert_ref(s); pa_assert_se(u = s->userdata); - pa_sink_input_request_rewind(u->sink_input, s->thread_info.rewind_nbytes, TRUE, FALSE); + pa_sink_input_request_rewind(u->sink_input, s->thread_info.rewind_nbytes, TRUE, FALSE, FALSE); } /* Called from I/O thread context */ @@ -270,7 +270,7 @@ static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t s if (PA_SINK_INPUT_IS_LINKED(state) && i->thread_info.state == PA_SINK_INPUT_INIT) { pa_log_debug("Requesting rewind due to state change."); - pa_sink_input_request_rewind(i, 0, FALSE, TRUE); + pa_sink_input_request_rewind(i, 0, FALSE, TRUE, TRUE); } } diff --git a/src/modules/module-sine.c b/src/modules/module-sine.c index 3b0dc352..2ea78da2 100644 --- a/src/modules/module-sine.c +++ b/src/modules/module-sine.c @@ -115,7 +115,7 @@ static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t s * we are heard right-away. */ if (PA_SINK_INPUT_IS_LINKED(state) && i->thread_info.state == PA_SINK_INPUT_INIT) - pa_sink_input_request_rewind(i, 0, FALSE, TRUE); + pa_sink_input_request_rewind(i, 0, FALSE, TRUE, TRUE); } int pa__init(pa_module*m) { diff --git a/src/modules/module-x11-xsmp.c b/src/modules/module-x11-xsmp.c index 57d182fd..5fc8047d 100644 --- a/src/modules/module-x11-xsmp.c +++ b/src/modules/module-x11-xsmp.c @@ -117,13 +117,14 @@ static void new_ice_connection(IceConn connection, IcePointer client_data, Bool int pa__init(pa_module*m) { pa_modargs *ma = NULL; - char t[256], *vendor, *client_id, *k; + char t[256], *vendor, *client_id; SmcCallbacks callbacks; SmProp prop_program, prop_user; SmProp *prop_list[2]; SmPropValue val_program, val_user; struct userdata *u; const char *e; + pa_client_new_data data; pa_assert(m); @@ -198,16 +199,22 @@ int pa__init(pa_module*m) { SmcSetProperties(u->connection, PA_ELEMENTSOF(prop_list), prop_list); pa_log_info("Connected to session manager '%s' as '%s'.", vendor = SmcVendor(u->connection), client_id); - k = pa_sprintf_malloc("XSMP Session on %s as %s", vendor, client_id); - u->client = pa_client_new(u->core, __FILE__, k); - pa_xfree(k); - pa_proplist_sets(u->client->proplist, "xsmp.vendor", vendor); - pa_proplist_sets(u->client->proplist, "xsmp.client.id", client_id); + pa_client_new_data_init(&data); + data.module = m; + data.driver = __FILE__; + pa_proplist_setf(data.proplist, PA_PROP_APPLICATION_NAME, "XSMP Session on %s as %s", vendor, client_id); + pa_proplist_sets(data.proplist, "xsmp.vendor", vendor); + pa_proplist_sets(data.proplist, "xsmp.client.id", client_id); + u->client = pa_client_new(u->core, &data); + pa_client_new_data_done(&data); free(vendor); free(client_id); + if (!u->client) + goto fail; + pa_modargs_free(ma); return 0; diff --git a/src/modules/rtp/module-rtp-recv.c b/src/modules/rtp/module-rtp-recv.c index e35773cc..478e0a32 100644 --- a/src/modules/rtp/module-rtp-recv.c +++ b/src/modules/rtp/module-rtp-recv.c @@ -313,7 +313,7 @@ static int rtpoll_work_cb(pa_rtpoll_item *i) { if (pa_memblockq_is_readable(s->memblockq) && s->sink_input->thread_info.underrun_for > 0) { pa_log_debug("Requesting rewind due to end of underrun"); - pa_sink_input_request_rewind(s->sink_input, 0, FALSE, TRUE); + pa_sink_input_request_rewind(s->sink_input, 0, FALSE, TRUE, FALSE); } return 1; diff --git a/src/pulse/def.h b/src/pulse/def.h index 7a715b66..03e8416e 100644 --- a/src/pulse/def.h +++ b/src/pulse/def.h @@ -46,7 +46,7 @@ typedef enum pa_context_state { PA_CONTEXT_TERMINATED /**< The connection was terminated cleanly */ } pa_context_state_t; -/** Return non-zero if the passed state is one of the connected states */ +/** Return non-zero if the passed state is one of the connected states. \since 0.9.11 */ static inline int PA_CONTEXT_IS_GOOD(pa_context_state_t x) { return x == PA_CONTEXT_CONNECTING || @@ -55,6 +55,10 @@ static inline int PA_CONTEXT_IS_GOOD(pa_context_state_t x) { x == PA_CONTEXT_READY; } +/** \cond fulldocs */ +#define PA_CONTEXT_IS_GOOD PA_CONTEXT_IS_GOOD +/** \endcond */ + /** The state of a stream */ typedef enum pa_stream_state { PA_STREAM_UNCONNECTED, /**< The stream is not yet connected to any sink or source */ @@ -64,13 +68,17 @@ typedef enum pa_stream_state { PA_STREAM_TERMINATED /**< The stream has been terminated cleanly */ } pa_stream_state_t; -/** Return non-zero if the passed state is one of the connected states */ +/** Return non-zero if the passed state is one of the connected states. \since 0.9.11 */ static inline int PA_STREAM_IS_GOOD(pa_stream_state_t x) { return x == PA_STREAM_CREATING || x == PA_STREAM_READY; } +/** \cond fulldocs */ +#define PA_STREAM_IS_GOOD PA_STREAM_IS_GOOD +/** \endcond */ + /** The state of an operation */ typedef enum pa_operation_state { PA_OPERATION_RUNNING, /**< The operation is still running */ @@ -383,7 +391,10 @@ typedef enum pa_subscription_mask { PA_SUBSCRIPTION_MASK_AUTOLOAD = 0x0100U, /**< Autoload table events. */ - PA_SUBSCRIPTION_MASK_ALL = 0x01ffU + PA_SUBSCRIPTION_MASK_CARD = 0x0200U, + /**< Card events. \since 0.9.15 */ + + PA_SUBSCRIPTION_MASK_ALL = 0x03ffU /**< Catch all events */ } pa_subscription_mask_t; @@ -416,6 +427,9 @@ typedef enum pa_subscription_event_type { PA_SUBSCRIPTION_EVENT_AUTOLOAD = 0x0008U, /**< Event type: Autoload table changes. */ + PA_SUBSCRIPTION_EVENT_CARD = 0x0009U, + /**< Event type: Card \since 0.9.15 */ + PA_SUBSCRIPTION_EVENT_FACILITY_MASK = 0x000FU, /**< A mask to extract the event type from an event value */ diff --git a/src/pulse/version.h.in b/src/pulse/version.h.in index e6226c44..0e37f983 100644 --- a/src/pulse/version.h.in +++ b/src/pulse/version.h.in @@ -43,7 +43,8 @@ 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. */ + * PA_API_VERSION undefined. Please note that this is only ever + * increased on incompatible API changes! */ #define PA_API_VERSION @PA_API_VERSION@ /** The current protocol version. Version 8 relates to Polypaudio diff --git a/src/pulsecore/card.c b/src/pulsecore/card.c new file mode 100644 index 00000000..03b9ebd7 --- /dev/null +++ b/src/pulsecore/card.c @@ -0,0 +1,198 @@ +/*** + This file is part of PulseAudio. + + Copyright 2009 Lennart Poettering + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <pulse/xmalloc.h> + +#include <pulsecore/log.h> +#include <pulsecore/macro.h> +#include <pulsecore/core-util.h> +#include <pulsecore/namereg.h> + +#include "card.h" + +pa_card_config *pa_card_config_new(const char *name) { + pa_card_config *c; + + pa_assert(name); + + c = pa_xnew0(pa_card_config, 1); + c->name = pa_xstrdup(name); + + return c; +} + +void pa_card_config_free(pa_card_config *c) { + pa_assert(c); + + pa_xfree(c->name); + pa_xfree(c); +} + +pa_card_new_data* pa_card_new_data_init(pa_card_new_data *data) { + pa_assert(data); + + memset(data, 0, sizeof(*data)); + data->proplist = pa_proplist_new(); + + return data; +} + +void pa_card_new_data_set_name(pa_card_new_data *data, const char *name) { + pa_assert(data); + + pa_xfree(data->name); + data->name = pa_xstrdup(name); +} + +void pa_card_new_data_done(pa_card_new_data *data) { + + pa_assert(data); + + pa_proplist_free(data->proplist); + + if (data->configs) { + pa_card_config *c; + + while ((c = pa_hashmap_steal_first(data->configs))) + pa_card_config_free(c); + + pa_hashmap_free(data->configs, NULL, NULL); + } + + pa_xfree(data->name); +} + +pa_card *pa_card_new(pa_core *core, pa_card_new_data *data) { + pa_card *c; + const char *name; + + pa_core_assert_ref(core); + pa_assert(data); + pa_assert(data->name); + + c = pa_xnew(pa_card, 1); + + if (!(name = pa_namereg_register(core, data->name, PA_NAMEREG_CARD, c, data->namereg_fail))) { + pa_xfree(c); + return NULL; + } + + pa_card_new_data_set_name(data, name); + + if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_CARD_NEW], data) < 0) { + pa_xfree(c); + pa_namereg_unregister(core, name); + return NULL; + } + + c->core = core; + c->name = pa_xstrdup(data->name); + c->proplist = pa_proplist_copy(data->proplist); + c->driver = pa_xstrdup(data->driver); + c->module = data->module; + + c->sinks = pa_idxset_new(NULL, NULL); + c->sources = pa_idxset_new(NULL, NULL); + + c->configs = data->configs; + data->configs = NULL; + c->active_config = data->active_config; + data->active_config = NULL; + + c->userdata = NULL; + c->set_config = NULL; + + pa_assert_se(pa_idxset_put(core->cards, c, &c->index) >= 0); + + pa_log_info("Created %u \"%s\"", c->index, c->name); + pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_CARD|PA_SUBSCRIPTION_EVENT_NEW, c->index); + + pa_hook_fire(&core->hooks[PA_CORE_HOOK_CARD_PUT], c); + return c; +} + +void pa_card_free(pa_card *c) { + pa_core *core; + pa_card_config *config; + + pa_assert(c); + pa_assert(c->core); + + core = c->core; + + pa_hook_fire(&core->hooks[PA_CORE_HOOK_CARD_UNLINK], c); + + pa_namereg_unregister(core, c->name); + + pa_idxset_remove_by_data(c->core->cards, c, NULL); + + pa_log_info("Freed %u \"%s\"", c->index, c->name); + + pa_subscription_post(c->core, PA_SUBSCRIPTION_EVENT_CARD|PA_SUBSCRIPTION_EVENT_REMOVE, c->index); + + pa_assert(pa_idxset_isempty(c->sinks)); + pa_idxset_free(c->sinks, NULL, NULL); + pa_assert(pa_idxset_isempty(c->sources)); + pa_idxset_free(c->sources, NULL, NULL); + + while ((config = pa_hashmap_steal_first(c->configs))) + pa_card_config_free(config); + + pa_hashmap_free(c->configs, NULL, NULL); + + pa_proplist_free(c->proplist); + pa_xfree(c->driver); + pa_xfree(c->name); + pa_xfree(c); + + pa_core_check_idle(core); +} + +int pa_card_set_config(pa_card *c, const char *name) { + pa_card_config *config; + pa_assert(c); + + if (!c->set_config) { + pa_log_warn("set_config() operation not implemented for card %u", c->index); + return -1; + } + + if (!c->configs) + return -1; + + if (!(config = pa_hashmap_get(c->configs, name))) + return -1; + + if (c->set_config(c, config) < 0) + return -1; + + pa_subscription_post(c->core, PA_SUBSCRIPTION_EVENT_CARD|PA_SUBSCRIPTION_EVENT_CHANGE, c->index); + + return 0; +} diff --git a/src/pulsecore/card.h b/src/pulsecore/card.h new file mode 100644 index 00000000..40e4a3ee --- /dev/null +++ b/src/pulsecore/card.h @@ -0,0 +1,91 @@ +#ifndef foopulsecardhfoo +#define foopulsecardhfoo + +/*** + This file is part of PulseAudio. + + Copyright 2009 Lennart Poettering + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +typedef struct pa_card pa_card; + +#include <pulse/proplist.h> +#include <pulsecore/core.h> +#include <pulsecore/module.h> +#include <pulsecore/idxset.h> + +typedef struct pa_card_config { + char *name; + + pa_bool_t optical_sink:1; + pa_bool_t optical_source:1; + + unsigned n_sinks; + unsigned n_sources; + + unsigned max_sink_channels; + unsigned max_source_channels; +} pa_card_config; + +struct pa_card { + uint32_t index; + pa_core *core; + + char *name; + + pa_proplist *proplist; + pa_module *module; + char *driver; + + pa_idxset *sinks; + pa_idxset *sources; + + pa_hashmap *configs; + pa_card_config *active_config; + + void *userdata; + + int (*set_config)(pa_card *c, pa_card_config *config); +}; + +typedef struct pa_card_new_data { + char *name; + + pa_proplist *proplist; + const char *driver; + pa_module *module; + + pa_hashmap *configs; + pa_card_config *active_config; + + pa_bool_t namereg_fail:1; +} pa_card_new_data; + +pa_card_config *pa_card_config_new(const char *name); +void pa_card_config_free(pa_card_config *c); + +pa_card_new_data *pa_card_new_data_init(pa_card_new_data *data); +void pa_card_new_data_set_name(pa_card_new_data *data, const char *name); +void pa_card_new_data_done(pa_card_new_data *data); + +pa_card *pa_card_new(pa_core *c, pa_card_new_data *data); +void pa_card_free(pa_card *c); + +int pa_card_set_config(pa_card *c, const char *name); + +#endif diff --git a/src/pulsecore/cli-command.c b/src/pulsecore/cli-command.c index 07d55d07..93d6bbe6 100644 --- a/src/pulsecore/cli-command.c +++ b/src/pulsecore/cli-command.c @@ -80,6 +80,7 @@ static int pa_cli_command_exit(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_b static int pa_cli_command_help(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail); static int pa_cli_command_modules(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail); static int pa_cli_command_clients(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail); +static int pa_cli_command_cards(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail); static int pa_cli_command_sinks(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail); static int pa_cli_command_sources(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail); static int pa_cli_command_sink_inputs(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail); @@ -137,6 +138,7 @@ static const struct command commands[] = { { "list-clients", pa_cli_command_clients, "List loaded clients", 1 }, { "list-sink-inputs", pa_cli_command_sink_inputs, "List sink inputs", 1 }, { "list-source-outputs", pa_cli_command_source_outputs, "List source outputs", 1 }, + { "list-cards", pa_cli_command_cards, "List cards", 1 }, { "stat", pa_cli_command_stat, "Show memory block statistics", 1 }, { "info", pa_cli_command_info, "Show comprehensive status", 1 }, { "ls", pa_cli_command_info, NULL, 1 }, @@ -254,6 +256,20 @@ static int pa_cli_command_clients(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, p return 0; } +static int pa_cli_command_cards(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) { + char *s; + + pa_core_assert_ref(c); + pa_assert(t); + pa_assert(buf); + pa_assert(fail); + + pa_assert_se(s = pa_card_list_to_string(c)); + pa_strbuf_puts(buf, s); + pa_xfree(s); + return 0; +} + static int pa_cli_command_sinks(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) { char *s; @@ -382,6 +398,7 @@ static int pa_cli_command_info(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_b pa_cli_command_sinks(c, t, buf, fail); pa_cli_command_sources(c, t, buf, fail); pa_cli_command_clients(c, t, buf, fail); + pa_cli_command_cards(c, t, buf, fail); pa_cli_command_sink_inputs(c, t, buf, fail); pa_cli_command_source_outputs(c, t, buf, fail); pa_cli_command_scache_list(c, t, buf, fail); diff --git a/src/pulsecore/cli-text.c b/src/pulsecore/cli-text.c index 362a9791..5d78ce64 100644 --- a/src/pulsecore/cli-text.c +++ b/src/pulsecore/cli-text.c @@ -97,6 +97,38 @@ char *pa_client_list_to_string(pa_core *c) { return pa_strbuf_tostring_free(s); } +char *pa_card_list_to_string(pa_core *c) { + pa_strbuf *s; + pa_card *card; + uint32_t idx = PA_IDXSET_INVALID; + pa_assert(c); + + s = pa_strbuf_new(); + + pa_strbuf_printf(s, "%u card(s) available in.\n", pa_idxset_size(c->cards)); + + for (card = pa_idxset_first(c->cards, &idx); card; card = pa_idxset_next(c->cards, &idx)) { + char *t; + pa_strbuf_printf( + s, + " index: %u\n" + "\tname: <%s>\n" + "\tdriver: <%s>\n", + card->index, + card->name, + card->driver); + + if (card->module) + pa_strbuf_printf(s, "\towner module: %u\n", card->module->index); + + t = pa_proplist_to_string(card->proplist); + pa_strbuf_printf(s, "\tproperties:\n%s", t); + pa_xfree(t); + } + + return pa_strbuf_tostring_free(s); +} + char *pa_sink_list_to_string(pa_core *c) { pa_strbuf *s; pa_sink *sink; @@ -174,6 +206,8 @@ char *pa_sink_list_to_string(pa_core *c) { pa_sink_used_by(sink), pa_sink_linked_by(sink)); + if (sink->card) + pa_strbuf_printf(s, "\tcard: %u <%s>\n", sink->card->index, sink->card->name); if (sink->module) pa_strbuf_printf(s, "\tmodule: %u\n", sink->module->index); @@ -260,6 +294,8 @@ char *pa_source_list_to_string(pa_core *c) { if (source->monitor_of) pa_strbuf_printf(s, "\tmonitor_of: %u\n", source->monitor_of->index); + if (source->card) + pa_strbuf_printf(s, "\tcard: %u <%s>\n", source->card->index, source->card->name); if (source->module) pa_strbuf_printf(s, "\tmodule: %u\n", source->module->index); @@ -508,7 +544,7 @@ char *pa_full_status_string(pa_core *c) { s = pa_strbuf_new(); - for (i = 0; i < 8; i++) { + for (i = 0; i < 9; i++) { char *t = NULL; switch (i) { @@ -528,12 +564,15 @@ char *pa_full_status_string(pa_core *c) { t = pa_client_list_to_string(c); break; case 5: - t = pa_module_list_to_string(c); + t = pa_card_list_to_string(c); break; case 6: - t = pa_scache_list_to_string(c); + t = pa_module_list_to_string(c); break; case 7: + t = pa_scache_list_to_string(c); + break; + case 8: t = pa_autoload_list_to_string(c); break; } diff --git a/src/pulsecore/cli-text.h b/src/pulsecore/cli-text.h index f4cb97a5..167565e5 100644 --- a/src/pulsecore/cli-text.h +++ b/src/pulsecore/cli-text.h @@ -31,6 +31,7 @@ char *pa_sink_input_list_to_string(pa_core *c); char *pa_source_output_list_to_string(pa_core *c); char *pa_sink_list_to_string(pa_core *core); char *pa_source_list_to_string(pa_core *c); +char *pa_card_list_to_string(pa_core *c); char *pa_client_list_to_string(pa_core *c); char *pa_module_list_to_string(pa_core *c); char *pa_scache_list_to_string(pa_core *c); @@ -39,4 +40,3 @@ char *pa_autoload_list_to_string(pa_core *c); char *pa_full_status_string(pa_core *c); #endif - diff --git a/src/pulsecore/cli.c b/src/pulsecore/cli.c index 67bf1e73..25a4f748 100644 --- a/src/pulsecore/cli.c +++ b/src/pulsecore/cli.c @@ -67,20 +67,33 @@ static void client_kill(pa_client *c); pa_cli* pa_cli_new(pa_core *core, pa_iochannel *io, pa_module *m) { char cname[256]; pa_cli *c; + pa_client_new_data data; + pa_client *client; + pa_assert(io); + pa_iochannel_socket_peer_to_string(io, cname, sizeof(cname)); + + pa_client_new_data_init(&data); + data.driver = __FILE__; + data.module = m; + pa_proplist_sets(data.proplist, PA_PROP_APPLICATION_NAME, cname); + client = pa_client_new(core, &data); + pa_client_new_data_done(&data); + + if (!client) + return NULL; + c = pa_xnew(pa_cli, 1); c->core = core; + c->client = client; pa_assert_se(c->line = pa_ioline_new(io)); c->userdata = NULL; c->eof_callback = NULL; - pa_iochannel_socket_peer_to_string(io, cname, sizeof(cname)); - pa_assert_se(c->client = pa_client_new(core, __FILE__, cname)); c->client->kill = client_kill; c->client->userdata = c; - c->client->module = m; pa_ioline_set_callback(c->line, line_callback, c); pa_ioline_puts(c->line, "Welcome to PulseAudio! Use \"help\" for usage information.\n"PROMPT); diff --git a/src/pulsecore/client.c b/src/pulsecore/client.c index ab6e5df4..1e65fcd9 100644 --- a/src/pulsecore/client.c +++ b/src/pulsecore/client.c @@ -37,27 +37,49 @@ #include "client.h" -pa_client *pa_client_new(pa_core *core, const char *driver, const char *name) { +pa_client_new_data* pa_client_new_data_init(pa_client_new_data *data) { + pa_assert(data); + + memset(data, 0, sizeof(*data)); + data->proplist = pa_proplist_new(); + + return data; +} + +void pa_client_new_data_done(pa_client_new_data *data) { + pa_assert(data); + + pa_proplist_free(data->proplist); +} + +pa_client *pa_client_new(pa_core *core, pa_client_new_data *data) { pa_client *c; pa_core_assert_ref(core); + pa_assert(data); + + if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_CLIENT_NEW], data) < 0) + return NULL; c = pa_xnew(pa_client, 1); c->core = core; - c->proplist = pa_proplist_new(); - if (name) - pa_proplist_sets(c->proplist, PA_PROP_APPLICATION_NAME, name); - c->driver = pa_xstrdup(driver); - c->module = NULL; + c->proplist = pa_proplist_copy(data->proplist); + c->driver = pa_xstrdup(data->driver); + c->module = data->module; + + c->sink_inputs = pa_idxset_new(NULL, NULL); + c->source_outputs = pa_idxset_new(NULL, NULL); - c->kill = NULL; c->userdata = NULL; + c->kill = NULL; pa_assert_se(pa_idxset_put(core->clients, c, &c->index) >= 0); - pa_log_info("Created %u \"%s\"", c->index, pa_strnull(name)); + pa_log_info("Created %u \"%s\"", c->index, pa_strnull(pa_proplist_gets(c->proplist, PA_PROP_APPLICATION_NAME))); pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_NEW, c->index); + pa_hook_fire(&core->hooks[PA_CORE_HOOK_CLIENT_PUT], c); + pa_core_check_idle(core); return c; @@ -70,10 +92,19 @@ void pa_client_free(pa_client *c) { pa_assert(c->core); core = c->core; + + pa_hook_fire(&core->hooks[PA_CORE_HOOK_CLIENT_UNLINK], c); + pa_idxset_remove_by_data(c->core->clients, c, NULL); pa_log_info("Freed %u \"%s\"", c->index, pa_strnull(pa_proplist_gets(c->proplist, PA_PROP_APPLICATION_NAME))); pa_subscription_post(c->core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_REMOVE, c->index); + + pa_assert(pa_idxset_isempty(c->sink_inputs)); + pa_idxset_free(c->sink_inputs, NULL, NULL); + pa_assert(pa_idxset_isempty(c->source_outputs)); + pa_idxset_free(c->source_outputs, NULL, NULL); + pa_proplist_free(c->proplist); pa_xfree(c->driver); pa_xfree(c); diff --git a/src/pulsecore/client.h b/src/pulsecore/client.h index 28d1fe5f..48e9bc7a 100644 --- a/src/pulsecore/client.h +++ b/src/pulsecore/client.h @@ -42,11 +42,24 @@ struct pa_client { pa_module *module; char *driver; - void (*kill)(pa_client *c); + pa_idxset *sink_inputs; + pa_idxset *source_outputs; + void *userdata; + + void (*kill)(pa_client *c); }; -pa_client *pa_client_new(pa_core *c, const char *driver, const char *name); +typedef struct pa_client_new_data { + pa_proplist *proplist; + const char *driver; + pa_module *module; +} pa_client_new_data; + +pa_client_new_data *pa_client_new_data_init(pa_client_new_data *data); +void pa_client_new_data_done(pa_client_new_data *data); + +pa_client *pa_client_new(pa_core *c, pa_client_new_data *data); /* This function should be called only by the code that created the client */ void pa_client_free(pa_client *c); diff --git a/src/pulsecore/core.c b/src/pulsecore/core.c index 5761bbc7..20764325 100644 --- a/src/pulsecore/core.c +++ b/src/pulsecore/core.c @@ -97,6 +97,7 @@ pa_core* pa_core_new(pa_mainloop_api *m, pa_bool_t shared, size_t shm_size) { c->sources = pa_idxset_new(NULL, NULL); c->source_outputs = pa_idxset_new(NULL, NULL); c->sink_inputs = pa_idxset_new(NULL, NULL); + c->cards = pa_idxset_new(NULL, NULL); c->default_source_name = c->default_sink_name = NULL; @@ -167,6 +168,9 @@ static void core_free(pa_object *o) { pa_assert(pa_idxset_isempty(c->clients)); pa_idxset_free(c->clients, NULL, NULL); + pa_assert(pa_idxset_isempty(c->cards)); + pa_idxset_free(c->cards, NULL, NULL); + pa_assert(pa_idxset_isempty(c->sinks)); pa_idxset_free(c->sinks, NULL, NULL); diff --git a/src/pulsecore/core.h b/src/pulsecore/core.h index f796fb93..87ea4ab6 100644 --- a/src/pulsecore/core.h +++ b/src/pulsecore/core.h @@ -76,6 +76,12 @@ typedef enum pa_core_hook { PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_POST, PA_CORE_HOOK_SOURCE_OUTPUT_STATE_CHANGED, PA_CORE_HOOK_SOURCE_OUTPUT_PROPLIST_CHANGED, + PA_CORE_HOOK_CLIENT_NEW, + PA_CORE_HOOK_CLIENT_PUT, + PA_CORE_HOOK_CLIENT_UNLINK, + PA_CORE_HOOK_CARD_NEW, + PA_CORE_HOOK_CARD_PUT, + PA_CORE_HOOK_CARD_UNLINK, PA_CORE_HOOK_MAX } pa_core_hook_t; @@ -93,7 +99,7 @@ struct pa_core { pa_mainloop_api *mainloop; /* idxset of all kinds of entities */ - pa_idxset *clients, *sinks, *sources, *sink_inputs, *source_outputs, *modules, *scache, *autoload_idxset; + pa_idxset *clients, *cards, *sinks, *sources, *sink_inputs, *source_outputs, *modules, *scache, *autoload_idxset; /* Some hashmaps for all sorts of entities */ pa_hashmap *namereg, *autoload_hashmap, *shared; diff --git a/src/pulsecore/namereg.c b/src/pulsecore/namereg.c index ecd8def8..c1a434ae 100644 --- a/src/pulsecore/namereg.c +++ b/src/pulsecore/namereg.c @@ -109,7 +109,7 @@ const char *pa_namereg_register(pa_core *c, const char *name, pa_namereg_type_t if (!*name) return NULL; - if ((type == PA_NAMEREG_SINK || type == PA_NAMEREG_SOURCE) && + if ((type == PA_NAMEREG_SINK || type == PA_NAMEREG_SOURCE || type == PA_NAMEREG_CARD) && !pa_namereg_is_valid_name(name)) { if (fail) diff --git a/src/pulsecore/namereg.h b/src/pulsecore/namereg.h index f4581006..8ce548a7 100644 --- a/src/pulsecore/namereg.h +++ b/src/pulsecore/namereg.h @@ -30,7 +30,8 @@ typedef enum pa_namereg_type { PA_NAMEREG_SINK, PA_NAMEREG_SOURCE, - PA_NAMEREG_SAMPLE + PA_NAMEREG_SAMPLE, + PA_NAMEREG_CARD } pa_namereg_type_t; void pa_namereg_free(pa_core *c); diff --git a/src/pulsecore/play-memblockq.c b/src/pulsecore/play-memblockq.c index 86edfe98..758c0dee 100644 --- a/src/pulsecore/play-memblockq.c +++ b/src/pulsecore/play-memblockq.c @@ -109,7 +109,7 @@ static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t s * we are heard right-away. */ if (PA_SINK_INPUT_IS_LINKED(state) && i->thread_info.state == PA_SINK_INPUT_INIT) - pa_sink_input_request_rewind(i, 0, FALSE, TRUE); + pa_sink_input_request_rewind(i, 0, FALSE, TRUE, TRUE); } static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) { diff --git a/src/pulsecore/protocol-esound.c b/src/pulsecore/protocol-esound.c index 460119a9..6524b684 100644 --- a/src/pulsecore/protocol-esound.c +++ b/src/pulsecore/protocol-esound.c @@ -1238,7 +1238,7 @@ static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int if (pa_memblockq_is_readable(c->input_memblockq) && c->playback.underrun) { pa_log_debug("Requesting rewind due to end of underrun."); - pa_sink_input_request_rewind(c->sink_input, 0, FALSE, TRUE); + pa_sink_input_request_rewind(c->sink_input, 0, FALSE, TRUE, FALSE); } /* pa_log("got data, %u", pa_memblockq_get_length(c->input_memblockq)); */ @@ -1377,7 +1377,9 @@ static void auth_timeout(pa_mainloop_api*m, pa_time_event *e, const struct timev void pa_esound_protocol_connect(pa_esound_protocol *p, pa_iochannel *io, pa_esound_options *o) { connection *c; - char cname[256], pname[128]; + char pname[128]; + pa_client_new_data data; + pa_client *client; pa_assert(p); pa_assert(io); @@ -1389,6 +1391,18 @@ void pa_esound_protocol_connect(pa_esound_protocol *p, pa_iochannel *io, pa_esou return; } + pa_client_new_data_init(&data); + data.module = o->module; + data.driver = __FILE__; + pa_iochannel_socket_peer_to_string(io, pname, sizeof(pname)); + pa_proplist_setf(data.proplist, PA_PROP_APPLICATION_NAME, "EsounD client (%s)", pname); + pa_proplist_sets(data.proplist, "esound-protocol.peer", pname); + client = pa_client_new(p->core, &data); + pa_client_new_data_done(&data); + + if (!client) + return; + c = pa_msgobject_new(connection); c->parent.parent.free = connection_free; c->parent.process_msg = connection_process_msg; @@ -1396,11 +1410,7 @@ void pa_esound_protocol_connect(pa_esound_protocol *p, pa_iochannel *io, pa_esou c->io = io; pa_iochannel_set_callback(c->io, io_callback, c); - pa_iochannel_socket_peer_to_string(io, pname, sizeof(pname)); - pa_snprintf(cname, sizeof(cname), "EsounD client (%s)", pname); - c->client = pa_client_new(p->core, __FILE__, cname); - pa_proplist_sets(c->client->proplist, "esound-protocol.peer", pname); - c->client->module = o->module; + c->client = client; c->client->kill = client_kill_cb; c->client->userdata = c; diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c index 3c1e5761..d99e212f 100644 --- a/src/pulsecore/protocol-native.c +++ b/src/pulsecore/protocol-native.c @@ -1240,7 +1240,7 @@ static void handle_seek(playback_stream *s, int64_t indexw) { pa_log_debug("Requesting rewind due to end of underrun."); pa_sink_input_request_rewind(s->sink_input, (size_t) (s->sink_input->thread_info.underrun_for == (size_t) -1 ? 0 : s->sink_input->thread_info.underrun_for), - FALSE, TRUE); + FALSE, TRUE, FALSE); } } else { @@ -1253,7 +1253,7 @@ static void handle_seek(playback_stream *s, int64_t indexw) { * let's have it usk us again */ pa_log_debug("Requesting rewind due to rewrite."); - pa_sink_input_request_rewind(s->sink_input, (size_t) (indexr - indexw), TRUE, FALSE); + pa_sink_input_request_rewind(s->sink_input, (size_t) (indexr - indexw), TRUE, FALSE, FALSE); } } @@ -4214,7 +4214,9 @@ static void auth_timeout(pa_mainloop_api*m, pa_time_event *e, const struct timev void pa_native_protocol_connect(pa_native_protocol *p, pa_iochannel *io, pa_native_options *o) { pa_native_connection *c; - char cname[256], pname[128]; + char pname[128]; + pa_client *client; + pa_client_new_data data; pa_assert(p); pa_assert(io); @@ -4226,6 +4228,18 @@ void pa_native_protocol_connect(pa_native_protocol *p, pa_iochannel *io, pa_nati return; } + pa_client_new_data_init(&data); + data.module = o->module; + data.driver = __FILE__; + pa_iochannel_socket_peer_to_string(io, pname, sizeof(pname)); + pa_proplist_setf(data.proplist, PA_PROP_APPLICATION_NAME, "Native client (%s)", pname); + pa_proplist_sets(data.proplist, "native-protocol.peer", pname); + client = pa_client_new(p->core, &data); + pa_client_new_data_done(&data); + + if (!client) + return; + c = pa_msgobject_new(pa_native_connection); c->parent.parent.free = native_connection_free; c->parent.process_msg = native_connection_process_msg; @@ -4257,13 +4271,9 @@ void pa_native_protocol_connect(pa_native_protocol *p, pa_iochannel *io, pa_nati c->is_local = pa_iochannel_socket_is_local(io); c->version = 8; - pa_iochannel_socket_peer_to_string(io, pname, sizeof(pname)); - pa_snprintf(cname, sizeof(cname), "Native client (%s)", pname); - c->client = pa_client_new(p->core, __FILE__, cname); - pa_proplist_sets(c->client->proplist, "native-protocol.peer", pname); + c->client = client; c->client->kill = client_kill_cb; c->client->userdata = c; - c->client->module = o->module; c->pstream = pa_pstream_new(p->core->mainloop, io, p->core->mempool); pa_pstream_set_recieve_packet_callback(c->pstream, pstream_packet_callback, c); diff --git a/src/pulsecore/protocol-simple.c b/src/pulsecore/protocol-simple.c index 743bf2ee..a754669c 100644 --- a/src/pulsecore/protocol-simple.c +++ b/src/pulsecore/protocol-simple.c @@ -323,7 +323,7 @@ static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int if (pa_memblockq_is_readable(c->input_memblockq) && c->playback.underrun) { pa_log_debug("Requesting rewind due to end of underrun."); - pa_sink_input_request_rewind(c->sink_input, 0, FALSE, TRUE); + pa_sink_input_request_rewind(c->sink_input, 0, FALSE, TRUE, FALSE); } /* pa_log("got data, %u", pa_memblockq_get_length(c->input_memblockq)); */ @@ -476,7 +476,8 @@ static void io_callback(pa_iochannel*io, void *userdata) { void pa_simple_protocol_connect(pa_simple_protocol *p, pa_iochannel *io, pa_simple_options *o) { connection *c = NULL; - char cname[256], pname[128]; + char pname[128]; + pa_client_new_data client_data; pa_assert(p); pa_assert(io); @@ -505,11 +506,18 @@ void pa_simple_protocol_connect(pa_simple_protocol *p, pa_iochannel *io, pa_simp c->playback.underrun = TRUE; pa_atomic_store(&c->playback.missing, 0); + pa_client_new_data_init(&client_data); + client_data.module = o->module; + client_data.driver = __FILE__; pa_iochannel_socket_peer_to_string(io, pname, sizeof(pname)); - pa_snprintf(cname, sizeof(cname), "Simple client (%s)", pname); - pa_assert_se(c->client = pa_client_new(p->core, __FILE__, cname)); - pa_proplist_sets(c->client->proplist, "simple-protocol.peer", pname); - c->client->module = o->module; + pa_proplist_setf(client_data.proplist, PA_PROP_APPLICATION_NAME, "Simple client (%s)", pname); + pa_proplist_sets(client_data.proplist, "simple-protocol.peer", pname); + c->client = pa_client_new(p->core, &client_data); + pa_client_new_data_done(&client_data); + + if (!c->client) + goto fail; + c->client->kill = client_kill_cb; c->client->userdata = c; diff --git a/src/pulsecore/shared.c b/src/pulsecore/shared.c index 4c1ad80a..d6e42dd8 100644 --- a/src/pulsecore/shared.c +++ b/src/pulsecore/shared.c @@ -115,7 +115,7 @@ void pa_shared_cleanup(pa_core *c) { pa_strbuf *s = pa_strbuf_new(); pa_shared_dump(c, s); - pa_log_debug(pa_strbuf_tostring(s)); + pa_log_debug("%s", pa_strbuf_tostring(s)); pa_strbuf_free(s); pa_assert(pa_hashmap_isempty(c->shared)); } diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c index 33490cc6..185350fa 100644 --- a/src/pulsecore/sink-input.c +++ b/src/pulsecore/sink-input.c @@ -265,6 +265,7 @@ pa_sink_input* pa_sink_input_new( i->thread_info.requested_sink_latency = (pa_usec_t) -1; i->thread_info.rewrite_nbytes = 0; i->thread_info.rewrite_flush = FALSE; + i->thread_info.dont_rewind_render = FALSE; i->thread_info.underrun_for = (uint64_t) -1; i->thread_info.playing_for = 0; i->thread_info.direct_outputs = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); @@ -282,6 +283,9 @@ pa_sink_input* pa_sink_input_new( pa_assert_se(pa_idxset_put(core->sink_inputs, pa_sink_input_ref(i), &i->index) == 0); pa_assert_se(pa_idxset_put(i->sink->inputs, i, NULL) == 0); + if (i->client) + pa_assert_se(pa_idxset_put(i->client->sink_inputs, i, NULL) >= 0); + pa_log_info("Created input %u \"%s\" on %s with sample spec %s and channel map %s", i->index, pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_MEDIA_NAME)), @@ -371,6 +375,9 @@ void pa_sink_input_unlink(pa_sink_input *i) { if (pa_idxset_remove_by_data(i->sink->inputs, i, NULL)) pa_sink_input_unref(i); + if (i->client) + pa_idxset_remove_by_data(i->client->sink_inputs, i, NULL); + while ((o = pa_idxset_first(i->direct_outputs, NULL))) { pa_assert(o != p); pa_source_output_kill(o); @@ -658,7 +665,7 @@ void pa_sink_input_process_rewind(pa_sink_input *i, size_t nbytes /* in sink sam lbq = pa_memblockq_get_length(i->thread_info.render_memblockq); - if (nbytes > 0) { + if (nbytes > 0 && !i->thread_info.dont_rewind_render) { pa_log_debug("Have to rewind %lu bytes on render memblockq.", (unsigned long) nbytes); pa_memblockq_rewind(i->thread_info.render_memblockq, nbytes); } @@ -714,6 +721,7 @@ void pa_sink_input_process_rewind(pa_sink_input *i, size_t nbytes /* in sink sam i->thread_info.rewrite_nbytes = 0; i->thread_info.rewrite_flush = FALSE; + i->thread_info.dont_rewind_render = FALSE; } /* Called from thread context */ @@ -1091,7 +1099,7 @@ void pa_sink_input_set_state_within_thread(pa_sink_input *i, pa_sink_input_state /* This will tell the implementing sink input driver to rewind * so that the unplayed already mixed data is not lost */ - pa_sink_input_request_rewind(i, 0, TRUE, TRUE); + pa_sink_input_request_rewind(i, 0, TRUE, TRUE, FALSE); } else if (uncorking) { @@ -1102,7 +1110,7 @@ void pa_sink_input_set_state_within_thread(pa_sink_input *i, pa_sink_input_state /* OK, we're being uncorked. Make sure we're not rewound when * the hw buffer is remixed and request a remix. */ - pa_sink_input_request_rewind(i, 0, FALSE, TRUE); + pa_sink_input_request_rewind(i, 0, FALSE, TRUE, TRUE); } } @@ -1115,12 +1123,12 @@ int pa_sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t case PA_SINK_INPUT_MESSAGE_SET_VOLUME: i->thread_info.volume = *((pa_cvolume*) userdata); - pa_sink_input_request_rewind(i, 0, TRUE, FALSE); + pa_sink_input_request_rewind(i, 0, TRUE, FALSE, FALSE); return 0; case PA_SINK_INPUT_MESSAGE_SET_MUTE: i->thread_info.muted = PA_PTR_TO_UINT(userdata); - pa_sink_input_request_rewind(i, 0, TRUE, FALSE); + pa_sink_input_request_rewind(i, 0, TRUE, FALSE, FALSE); return 0; case PA_SINK_INPUT_MESSAGE_GET_LATENCY: { @@ -1195,7 +1203,7 @@ pa_bool_t pa_sink_input_safe_to_remove(pa_sink_input *i) { } /* Called from IO context */ -void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes /* in our sample spec */, pa_bool_t rewrite, pa_bool_t flush) { +void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes /* in our sample spec */, pa_bool_t rewrite, pa_bool_t flush, pa_bool_t dont_rewind_render) { size_t lbq; /* If 'rewrite' is TRUE the sink is rewound as far as requested @@ -1206,7 +1214,9 @@ void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes /* in our sam * If 'rewrite' is FALSE the sink is rewound as far as requested * and possible and the already rendered data is dropped so that * in the next iteration we read new data from the - * implementor. This implies 'flush' is TRUE. */ + * implementor. This implies 'flush' is TRUE. If + * dont_rewind_render is TRUE then the render memblockq is not + * rewound. */ pa_sink_input_assert_ref(i); @@ -1219,6 +1229,7 @@ void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes /* in our sam return; pa_assert(rewrite || flush); + pa_assert(!dont_rewind_render || !rewrite); /* Calculate how much we can rewind locally without having to * touch the sink */ @@ -1253,6 +1264,10 @@ void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes /* in our sam i->thread_info.rewrite_flush || (flush && i->thread_info.rewrite_nbytes != 0); + i->thread_info.dont_rewind_render = + i->thread_info.dont_rewind_render || + dont_rewind_render; + if (nbytes != (size_t) -1) { /* Transform to sink domain */ diff --git a/src/pulsecore/sink-input.h b/src/pulsecore/sink-input.h index 8cfe32bc..3d2a8c0d 100644 --- a/src/pulsecore/sink-input.h +++ b/src/pulsecore/sink-input.h @@ -179,8 +179,9 @@ struct pa_sink_input { /* We maintain a history of resampled audio data here. */ pa_memblockq *render_memblockq; + /* 0: rewrite nothing, (size_t) -1: rewrite everything, otherwise how many bytes to rewrite */ size_t rewrite_nbytes; - pa_bool_t rewrite_flush; + pa_bool_t rewrite_flush, dont_rewind_render; uint64_t underrun_for, playing_for; pa_sink_input *sync_prev, *sync_next; @@ -277,7 +278,7 @@ fully -- or at all. If the request for a rewrite was successful, the sink driver will call ->rewind() and pass the number of bytes that could be rewound in the HW device. This functionality is required for implementing the "zero latency" write-through functionality. */ -void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes, pa_bool_t rewrite, pa_bool_t flush); +void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes, pa_bool_t rewrite, pa_bool_t flush, pa_bool_t dont_rewind_render); void pa_sink_input_cork(pa_sink_input *i, pa_bool_t b); diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c index 809e8273..d1d68cdd 100644 --- a/src/pulsecore/sink.c +++ b/src/pulsecore/sink.c @@ -183,6 +183,7 @@ pa_sink* pa_sink_new( s->proplist = pa_proplist_copy(data->proplist); s->driver = pa_xstrdup(data->driver); s->module = data->module; + s->card = data->card; s->sample_spec = data->sample_spec; s->channel_map = data->channel_map; @@ -225,6 +226,9 @@ pa_sink* pa_sink_new( pa_assert_se(pa_idxset_put(core->sinks, s, &s->index) >= 0); + if (s->card) + pa_assert_se(pa_idxset_put(s->card->sinks, s, NULL) >= 0); + pa_log_info("Created sink %u \"%s\" with sample spec %s and channel map %s", s->index, s->name, @@ -237,6 +241,7 @@ pa_sink* pa_sink_new( source_data.name = pa_sprintf_malloc("%s.monitor", name); source_data.driver = data->driver; source_data.module = data->module; + source_data.card = data->card; dn = pa_proplist_gets(s->proplist, PA_PROP_DEVICE_DESCRIPTION); pa_proplist_setf(source_data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Monitor of %s", dn ? dn : s->name); @@ -360,6 +365,9 @@ void pa_sink_unlink(pa_sink* s) { pa_namereg_unregister(s->core, s->name); pa_idxset_remove_by_data(s->core->sinks, s, NULL); + if (s->card) + pa_idxset_remove_by_data(s->card->sinks, s, NULL); + while ((i = pa_idxset_first(s->inputs, NULL))) { pa_assert(i != j); pa_sink_input_kill(i); @@ -644,7 +652,7 @@ void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) { pa_assert(length > 0); - n = s->thread_info.state == PA_SINK_RUNNING ? fill_mix_info(s, &length, info, MAX_MIX_CHANNELS) : 0; + n = fill_mix_info(s, &length, info, MAX_MIX_CHANNELS); if (n == 0) { @@ -687,8 +695,7 @@ void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) { result->index = 0; } - if (s->thread_info.state == PA_SINK_RUNNING) - inputs_drop(s, info, n, result); + inputs_drop(s, info, n, result); pa_sink_unref(s); } @@ -718,7 +725,7 @@ void pa_sink_render_into(pa_sink*s, pa_memchunk *target) { pa_assert(length > 0); - n = s->thread_info.state == PA_SINK_RUNNING ? fill_mix_info(s, &length, info, MAX_MIX_CHANNELS) : 0; + n = fill_mix_info(s, &length, info, MAX_MIX_CHANNELS); if (n == 0) { if (target->length > length) @@ -767,8 +774,7 @@ void pa_sink_render_into(pa_sink*s, pa_memchunk *target) { pa_memblock_release(target->memblock); } - if (s->thread_info.state == PA_SINK_RUNNING) - inputs_drop(s, info, n, target); + inputs_drop(s, info, n, target); pa_sink_unref(s); } diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h index fb5e1e89..507c1603 100644 --- a/src/pulsecore/sink.h +++ b/src/pulsecore/sink.h @@ -38,6 +38,7 @@ typedef struct pa_sink pa_sink; #include <pulsecore/refcnt.h> #include <pulsecore/msgobject.h> #include <pulsecore/rtpoll.h> +#include <pulsecore/card.h> #define PA_MAX_INPUTS_PER_SINK 32 @@ -70,6 +71,7 @@ struct pa_sink { pa_proplist *proplist; pa_module *module; /* may be NULL */ + pa_card *card; /* may be NULL */ pa_sample_spec sample_spec; pa_channel_map channel_map; @@ -187,6 +189,7 @@ typedef struct pa_sink_new_data { const char *driver; pa_module *module; + pa_card *card; pa_sample_spec sample_spec; pa_channel_map channel_map; diff --git a/src/pulsecore/sound-file-stream.c b/src/pulsecore/sound-file-stream.c index c30c16eb..b78afca8 100644 --- a/src/pulsecore/sound-file-stream.c +++ b/src/pulsecore/sound-file-stream.c @@ -133,7 +133,7 @@ static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t s * we are heard right-away. */ if (PA_SINK_INPUT_IS_LINKED(state) && i->thread_info.state == PA_SINK_INPUT_INIT) - pa_sink_input_request_rewind(i, 0, FALSE, TRUE); + pa_sink_input_request_rewind(i, 0, FALSE, TRUE, TRUE); } /* Called from IO thread context */ diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c index e3d0d8b2..b1c65d1c 100644 --- a/src/pulsecore/source-output.c +++ b/src/pulsecore/source-output.c @@ -223,6 +223,9 @@ pa_source_output* pa_source_output_new( pa_assert_se(pa_idxset_put(core->source_outputs, o, &o->index) == 0); pa_assert_se(pa_idxset_put(o->source->outputs, pa_source_output_ref(o), NULL) == 0); + if (o->client) + pa_assert_se(pa_idxset_put(o->client->source_outputs, o, NULL) >= 0); + if (o->direct_on_input) pa_assert_se(pa_idxset_put(o->direct_on_input->direct_outputs, o, NULL) == 0); @@ -290,6 +293,9 @@ void pa_source_output_unlink(pa_source_output*o) { if (pa_idxset_remove_by_data(o->source->outputs, o, NULL)) pa_source_output_unref(o); + if (o->client) + pa_idxset_remove_by_data(o->client->source_outputs, o, NULL); + update_n_corked(o, PA_SOURCE_OUTPUT_UNLINKED); o->state = PA_SOURCE_OUTPUT_UNLINKED; diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c index e65c5ce7..676a6b40 100644 --- a/src/pulsecore/source.c +++ b/src/pulsecore/source.c @@ -174,6 +174,7 @@ pa_source* pa_source_new( s->proplist = pa_proplist_copy(data->proplist); s->driver = pa_xstrdup(data->driver); s->module = data->module; + s->card = data->card; s->sample_spec = data->sample_spec; s->channel_map = data->channel_map; @@ -212,6 +213,9 @@ pa_source* pa_source_new( pa_assert_se(pa_idxset_put(core->sources, s, &s->index) >= 0); + if (s->card) + pa_assert_se(pa_idxset_put(s->card->sources, s, NULL) >= 0); + pa_log_info("Created source %u \"%s\" with sample spec %s and channel map %s", s->index, s->name, @@ -314,6 +318,9 @@ void pa_source_unlink(pa_source *s) { pa_namereg_unregister(s->core, s->name); pa_idxset_remove_by_data(s->core->sources, s, NULL); + if (s->card) + pa_idxset_remove_by_data(s->card->sinks, s, NULL); + while ((o = pa_idxset_first(s->outputs, NULL))) { pa_assert(o != j); pa_source_output_kill(o); @@ -429,9 +436,6 @@ void pa_source_post(pa_source*s, const pa_memchunk *chunk) { pa_assert(PA_SOURCE_IS_OPENED(s->thread_info.state)); pa_assert(chunk); - if (s->thread_info.state != PA_SOURCE_RUNNING) - return; - if (s->thread_info.soft_muted || !pa_cvolume_is_norm(&s->thread_info.soft_volume)) { pa_memchunk vchunk = *chunk; @@ -470,9 +474,6 @@ void pa_source_post_direct(pa_source*s, pa_source_output *o, const pa_memchunk * pa_assert(o->thread_info.direct_on_input); pa_assert(chunk); - if (s->thread_info.state != PA_SOURCE_RUNNING) - return; - if (s->thread_info.soft_muted || !pa_cvolume_is_norm(&s->thread_info.soft_volume)) { pa_memchunk vchunk = *chunk; diff --git a/src/pulsecore/source.h b/src/pulsecore/source.h index fd8c4bd6..48240996 100644 --- a/src/pulsecore/source.h +++ b/src/pulsecore/source.h @@ -41,6 +41,7 @@ typedef struct pa_source pa_source; #include <pulsecore/msgobject.h> #include <pulsecore/rtpoll.h> #include <pulsecore/source-output.h> +#include <pulsecore/card.h> #define PA_MAX_OUTPUTS_PER_SOURCE 32 @@ -73,6 +74,7 @@ struct pa_source { pa_proplist *proplist; pa_module *module; /* may be NULL */ + pa_card *card; /* may be NULL */ pa_sample_spec sample_spec; pa_channel_map channel_map; @@ -174,6 +176,7 @@ typedef struct pa_source_new_data { const char *driver; pa_module *module; + pa_card *card; pa_sample_spec sample_spec; pa_channel_map channel_map; |