diff options
Diffstat (limited to 'src/modules')
-rw-r--r-- | src/modules/alsa/alsa-sink.c | 4 | ||||
-rw-r--r-- | src/modules/alsa/alsa-source.c | 3 | ||||
-rw-r--r-- | src/modules/bluetooth/module-bluetooth-device.c | 3 | ||||
-rw-r--r-- | src/modules/module-augment-properties.c | 286 | ||||
-rw-r--r-- | src/modules/module-card-restore.c | 13 | ||||
-rw-r--r-- | src/modules/module-cork-music-on-phone.c | 224 | ||||
-rw-r--r-- | src/modules/module-device-restore.c | 15 | ||||
-rw-r--r-- | src/modules/module-match.c | 1 | ||||
-rw-r--r-- | src/modules/module-position-event-sounds.c | 15 | ||||
-rw-r--r-- | src/modules/module-raop-sink.c | 65 | ||||
-rw-r--r-- | src/modules/module-stream-restore.c | 91 | ||||
-rw-r--r-- | src/modules/module-tunnel.c | 2 | ||||
-rw-r--r-- | src/modules/rtp/module-rtp-recv.c | 2 |
13 files changed, 632 insertions, 92 deletions
diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c index 22460bb0..c56614c8 100644 --- a/src/modules/alsa/alsa-sink.c +++ b/src/modules/alsa/alsa-sink.c @@ -190,7 +190,9 @@ static size_t check_left_to_play(struct userdata *u, snd_pcm_sframes_t n) { if (left_to_play > 0) { /* pa_log_debug("%0.2f ms left to play", (double) pa_bytes_to_usec(left_to_play, &u->sink->sample_spec) / PA_USEC_PER_MSEC); */ } else if (!u->first && !u->after_rewind) { - pa_log_info("Underrun!"); + + if (pa_log_ratelimit()) + pa_log_info("Underrun!"); if (u->use_tsched) { size_t old_watermark = u->tsched_watermark; diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c index 0fd9838c..2b42d3f9 100644 --- a/src/modules/alsa/alsa-source.c +++ b/src/modules/alsa/alsa-source.c @@ -187,7 +187,8 @@ static size_t check_left_to_record(struct userdata *u, snd_pcm_sframes_t n) { if (left_to_record > 0) { /* pa_log_debug("%0.2f ms left to record", (double) pa_bytes_to_usec(left_to_record, &u->source->sample_spec) / PA_USEC_PER_MSEC); */ } else { - pa_log_info("Overrun!"); + if (pa_log_ratelimit()) + pa_log_info("Overrun!"); if (u->use_tsched) { size_t old_watermark = u->tsched_watermark; diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c index 666dc6a7..94195957 100644 --- a/src/modules/bluetooth/module-bluetooth-device.c +++ b/src/modules/bluetooth/module-bluetooth-device.c @@ -1095,6 +1095,9 @@ finish: /* dbus_error_init(&err); */ +/* if (!dbus_message_has_path(msg, u->path)) */ +/* goto done; */ + /* if (dbus_message_is_signal(msg, "org.bluez.Headset", "PropertyChanged") || */ /* dbus_message_is_signal(msg, "org.bluez.AudioSink", "PropertyChanged")) { */ diff --git a/src/modules/module-augment-properties.c b/src/modules/module-augment-properties.c new file mode 100644 index 00000000..90bfbe7d --- /dev/null +++ b/src/modules/module-augment-properties.c @@ -0,0 +1,286 @@ +/*** + 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 <sys/stat.h> + +#include <pulse/xmalloc.h> +#include <pulse/volume.h> +#include <pulse/channelmap.h> + +#include <pulsecore/core-error.h> +#include <pulsecore/module.h> +#include <pulsecore/core-util.h> +#include <pulsecore/modargs.h> +#include <pulsecore/log.h> +#include <pulsecore/client.h> +#include <pulsecore/conf-parser.h> + +#include "module-augment-properties-symdef.h" + +PA_MODULE_AUTHOR("Lennart Poettering"); +PA_MODULE_DESCRIPTION("Augment the property sets of streams with additional static information"); +PA_MODULE_VERSION(PACKAGE_VERSION); +PA_MODULE_LOAD_ONCE(TRUE); + +#define STAT_INTERVAL 30 +#define MAX_CACHE_SIZE 50 + +static const char* const valid_modargs[] = { + NULL +}; + +struct rule { + time_t timestamp; + pa_bool_t good; + time_t mtime; + char *process_name; + char *application_name; + char *icon_name; + pa_proplist *proplist; +}; + +struct userdata { + pa_hashmap *cache; + pa_hook_slot *client_new_slot, *client_proplist_changed_slot; +}; + +static void rule_free(struct rule *r) { + pa_assert(r); + + pa_xfree(r->process_name); + pa_xfree(r->application_name); + pa_xfree(r->icon_name); + if (r->proplist) + pa_proplist_free(r->proplist); + pa_xfree(r); +} + +static int parse_properties(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata) { + struct rule *r = userdata; + pa_proplist *n; + + if (!(n = pa_proplist_from_string(rvalue))) + return -1; + + if (r->proplist) { + pa_proplist_update(r->proplist, PA_UPDATE_MERGE, n); + pa_proplist_free(n); + } else + r->proplist = n; + + return 0; +} + +static int check_type(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata) { + return pa_streq(rvalue, "Application") ? 0 : -1; +} + +static int catch_all(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata) { + return 0; +} + +static void update_rule(struct rule *r) { + char *fn; + struct stat st; + static pa_config_item table[] = { + { "Name", pa_config_parse_string, NULL, "Desktop Entry" }, + { "Icon", pa_config_parse_string, NULL, "Desktop Entry" }, + { "Type", check_type, NULL, "Desktop Entry" }, + { "X-PulseAudio-Properties", parse_properties, NULL, "Desktop Entry" }, + { NULL, catch_all, NULL, NULL }, + { NULL, NULL, NULL, NULL }, + }; + + pa_assert(r); + fn = pa_sprintf_malloc(DESKTOPFILEDIR PA_PATH_SEP "%s.desktop", r->process_name); + + if (stat(fn, &st) < 0) { + r->good = FALSE; + pa_xfree(fn); + return; + } + + if (r->good && st.st_mtime == r->mtime) { + pa_xfree(fn); + return; + } + + r->good = TRUE; + r->mtime = st.st_mtime; + pa_xfree(r->application_name); + pa_xfree(r->icon_name); + r->application_name = r->icon_name = NULL; + if (r->proplist) + pa_proplist_clear(r->proplist); + + table[0].data = &r->application_name; + table[1].data = &r->icon_name; + + if (pa_config_parse(fn, NULL, table, r) < 0) + pa_log_warn("Failed to parse .desktop file %s.", fn); + + pa_xfree(fn); +} + +static void apply_rule(struct rule *r, pa_proplist *p) { + pa_assert(r); + pa_assert(p); + + if (!r->good) + return; + + if (r->icon_name) + if (!pa_proplist_contains(p, PA_PROP_APPLICATION_ICON_NAME)) + pa_proplist_sets(p, PA_PROP_APPLICATION_ICON_NAME, r->icon_name); + + if (r->application_name) { + const char *t; + + t = pa_proplist_gets(p, PA_PROP_APPLICATION_NAME); + + if (!t || pa_streq(t, r->process_name)) + pa_proplist_sets(p, PA_PROP_APPLICATION_NAME, r->application_name); + } + + if (r->proplist) + pa_proplist_update(p, PA_UPDATE_MERGE, r->proplist); +} + +static void make_room(pa_hashmap *cache) { + pa_assert(cache); + + while (pa_hashmap_size(cache) >= MAX_CACHE_SIZE) { + struct rule *r; + + pa_assert_se(r = pa_hashmap_steal_first(cache)); + rule_free(r); + } +} + +static pa_hook_result_t process(struct userdata *u, pa_proplist *p) { + struct rule *r; + time_t now; + const char *pn; + + pa_assert(u); + pa_assert(p); + + if (!(pn = pa_proplist_gets(p, PA_PROP_APPLICATION_PROCESS_BINARY))) + return PA_HOOK_OK; + + if (*pn == '.' || strchr(pn, '/')) + return PA_HOOK_OK; + + time(&now); + + if ((r = pa_hashmap_get(u->cache, pn))) { + if (now-r->timestamp > STAT_INTERVAL) { + r->timestamp = now; + update_rule(r); + } + } else { + make_room(u->cache); + + r = pa_xnew0(struct rule, 1); + r->process_name = pa_xstrdup(pn); + r->timestamp = now; + pa_hashmap_put(u->cache, r->process_name, r); + update_rule(r); + } + + apply_rule(r, p); + return PA_HOOK_OK; +} + +static pa_hook_result_t client_new_cb(pa_core *core, pa_client_new_data *data, struct userdata *u) { + pa_core_assert_ref(core); + pa_assert(data); + pa_assert(u); + + return process(u, data->proplist); +} + +static pa_hook_result_t client_proplist_changed_cb(pa_core *core, pa_client *client, struct userdata *u) { + pa_core_assert_ref(core); + pa_assert(client); + pa_assert(u); + + return process(u, client->proplist); +} + +int pa__init(pa_module *m) { + pa_modargs *ma = NULL; + struct userdata *u; + + pa_assert(m); + + if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { + pa_log("Failed to parse module arguments"); + goto fail; + } + + m->userdata = u = pa_xnew(struct userdata, 1); + + u->cache = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); + u->client_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_CLIENT_NEW], PA_HOOK_EARLY, (pa_hook_cb_t) client_new_cb, u); + u->client_proplist_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_CLIENT_PROPLIST_CHANGED], PA_HOOK_EARLY, (pa_hook_cb_t) client_proplist_changed_cb, u); + + pa_modargs_free(ma); + + return 0; + +fail: + pa__done(m); + + if (ma) + pa_modargs_free(ma); + + return -1; +} + +void pa__done(pa_module *m) { + struct userdata* u; + + pa_assert(m); + + if (!(u = m->userdata)) + return; + + if (u->client_new_slot) + pa_hook_slot_free(u->client_new_slot); + if (u->client_proplist_changed_slot) + pa_hook_slot_free(u->client_proplist_changed_slot); + + if (u->cache) { + struct rule *r; + + while ((r = pa_hashmap_steal_first(u->cache))) + rule_free(r); + + pa_hashmap_free(u->cache, NULL, NULL); + } + + pa_xfree(u); +} diff --git a/src/modules/module-card-restore.c b/src/modules/module-card-restore.c index 02e973c4..4dffd365 100644 --- a/src/modules/module-card-restore.c +++ b/src/modules/module-card-restore.c @@ -68,7 +68,10 @@ struct userdata { GDBM_FILE gdbm_file; }; -struct entry { +#define ENTRY_VERSION 1 + +struct entry PA_GCC_PACKED { + uint8_t version; char profile[PA_NAME_MAX]; }; @@ -104,12 +107,17 @@ static struct entry* read_entry(struct userdata *u, const char *name) { goto fail; if (data.dsize != sizeof(struct entry)) { - pa_log_warn("Database contains entry for card %s of wrong size %lu != %lu", name, (unsigned long) data.dsize, (unsigned long) sizeof(struct entry)); + pa_log_debug("Database contains entry for card %s of wrong size %lu != %lu. Probably due to upgrade, ignoring.", name, (unsigned long) data.dsize, (unsigned long) sizeof(struct entry)); goto fail; } e = (struct entry*) data.dptr; + if (e->version != ENTRY_VERSION) { + pa_log_debug("Version of database entry for card %s doesn't match our version. Probably due to upgrade, ignoring.", name); + goto fail; + } + if (!memchr(e->profile, 0, sizeof(e->profile))) { pa_log_warn("Database contains entry for card %s with missing NUL byte in profile name", name); goto fail; @@ -148,6 +156,7 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3 return; memset(&entry, 0, sizeof(entry)); + entry.version = ENTRY_VERSION; if (!(card = pa_idxset_get_by_index(c->cards, idx))) return; diff --git a/src/modules/module-cork-music-on-phone.c b/src/modules/module-cork-music-on-phone.c new file mode 100644 index 00000000..fb90cf34 --- /dev/null +++ b/src/modules/module-cork-music-on-phone.c @@ -0,0 +1,224 @@ +/*** + 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 <pulsecore/macro.h> +#include <pulsecore/hashmap.h> +#include <pulsecore/hook-list.h> +#include <pulsecore/core.h> +#include <pulsecore/core-util.h> +#include <pulsecore/sink-input.h> +#include <pulsecore/modargs.h> + +#include "module-cork-music-on-phone-symdef.h" + +PA_MODULE_AUTHOR("Lennart Poettering"); +PA_MODULE_DESCRIPTION("Mute or cork music while a phone stream exists"); +PA_MODULE_VERSION(PACKAGE_VERSION); +PA_MODULE_LOAD_ONCE(TRUE); + +static const char* const valid_modargs[] = { + NULL +}; + +struct userdata { + pa_core *core; + pa_hashmap *cork_state; + pa_hook_slot + *sink_input_put_slot, + *sink_input_unlink_slot, + *sink_input_move_start_slot, + *sink_input_move_finish_slot; +}; + +static pa_bool_t shall_cork(pa_sink *s, pa_sink_input *ignore) { + pa_sink_input *j; + uint32_t idx; + pa_sink_assert_ref(s); + + for (j = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); j; j = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx))) { + const char *role; + + if (j == ignore) + continue; + + if (!(role = pa_proplist_gets(j->proplist, PA_PROP_MEDIA_ROLE))) + continue; + + if (pa_streq(role, "phone")) + return TRUE; + } + + return FALSE; +} + +static void apply_cork(struct userdata *u, pa_sink *s, pa_sink_input *ignore, pa_bool_t cork) { + pa_sink_input *j; + uint32_t idx; + + pa_assert(u); + pa_sink_assert_ref(s); + + for (j = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); j; j = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx))) { + pa_bool_t corked; + const char *role; + + if (j == ignore) + continue; + + if (!(role = pa_proplist_gets(j->proplist, PA_PROP_MEDIA_ROLE))) + continue; + + if (!pa_streq(role, "video") && + !pa_streq(role, "music")) + continue; + + corked = !!pa_hashmap_get(u->cork_state, j); + + if (cork && !corked) { + pa_hashmap_put(u->cork_state, j, PA_INT_TO_PTR(1)); + pa_sink_input_set_mute(j, TRUE, FALSE); + pa_sink_input_send_event(j, PA_STREAM_EVENT_REQUEST_CORK, NULL); + } else if (!cork) { + pa_hashmap_remove(u->cork_state, j); + + if (corked) { + pa_sink_input_set_mute(j, FALSE, FALSE); + pa_sink_input_send_event(j, PA_STREAM_EVENT_REQUEST_UNCORK, NULL); + } + } + } +} + +static pa_hook_result_t process(struct userdata *u, pa_sink_input *i, pa_bool_t create) { + pa_bool_t cork = FALSE; + const char *role; + + pa_assert(u); + pa_sink_input_assert_ref(i); + + if (!create) + pa_hashmap_remove(u->cork_state, i); + + if (!(role = pa_proplist_gets(i->proplist, PA_PROP_MEDIA_ROLE))) + return PA_HOOK_OK; + + if (!pa_streq(role, "phone") && + !pa_streq(role, "music") && + !pa_streq(role, "video")) + return PA_HOOK_OK; + + cork = shall_cork(i->sink, create ? NULL : i); + apply_cork(u, i->sink, create ? NULL : i, cork); + + return PA_HOOK_OK; +} + +static pa_hook_result_t sink_input_put_cb(pa_core *core, pa_sink_input *i, struct userdata *u) { + pa_core_assert_ref(core); + pa_sink_input_assert_ref(i); + + return process(u, i, TRUE); +} + +static pa_hook_result_t sink_input_unlink_cb(pa_core *core, pa_sink_input *i, struct userdata *u) { + pa_core_assert_ref(core); + pa_sink_input_assert_ref(i); + + return process(u, i, FALSE); +} + +static pa_hook_result_t sink_input_move_start_cb(pa_core *core, pa_sink_input *i, struct userdata *u) { + pa_core_assert_ref(core); + pa_sink_input_assert_ref(i); + + return process(u, i, FALSE); +} + +static pa_hook_result_t sink_input_move_finish_cb(pa_core *core, pa_sink_input *i, struct userdata *u) { + pa_core_assert_ref(core); + pa_sink_input_assert_ref(i); + + return process(u, i, TRUE); +} + +int pa__init(pa_module *m) { + pa_modargs *ma = NULL; + struct userdata *u; + + pa_assert(m); + + if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { + pa_log("Failed to parse module arguments"); + goto fail; + } + + m->userdata = u = pa_xnew(struct userdata, 1); + + u->core = m->core; + u->cork_state = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); + + u->sink_input_put_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_PUT], PA_HOOK_LATE, (pa_hook_cb_t) sink_input_put_cb, u); + u->sink_input_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_UNLINK], PA_HOOK_LATE, (pa_hook_cb_t) sink_input_unlink_cb, u); + u->sink_input_move_start_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_START], PA_HOOK_LATE, (pa_hook_cb_t) sink_input_move_start_cb, u); + u->sink_input_move_finish_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_FINISH], PA_HOOK_LATE, (pa_hook_cb_t) sink_input_move_finish_cb, u); + + pa_modargs_free(ma); + + return 0; + +fail: + pa__done(m); + + if (ma) + pa_modargs_free(ma); + + return -1; + + +} + +void pa__done(pa_module *m) { + struct userdata* u; + + pa_assert(m); + + if (!(u = m->userdata)) + return; + + if (u->sink_input_put_slot) + pa_hook_slot_free(u->sink_input_put_slot); + if (u->sink_input_unlink_slot) + pa_hook_slot_free(u->sink_input_unlink_slot); + if (u->sink_input_move_start_slot) + pa_hook_slot_free(u->sink_input_move_start_slot); + if (u->sink_input_move_finish_slot) + pa_hook_slot_free(u->sink_input_move_finish_slot); + + if (u->cork_state) + pa_hashmap_free(u->cork_state, NULL, NULL); + + pa_xfree(u); + +} diff --git a/src/modules/module-device-restore.c b/src/modules/module-device-restore.c index 8e0cf92b..7c56c240 100644 --- a/src/modules/module-device-restore.c +++ b/src/modules/module-device-restore.c @@ -79,10 +79,13 @@ struct userdata { pa_bool_t restore_muted:1; }; -struct entry { +#define ENTRY_VERSION 1 + +struct entry PA_GCC_PACKED { + uint8_t version; + pa_bool_t muted:1; pa_channel_map channel_map; pa_cvolume volume; - pa_bool_t muted:1; }; static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *tv, void *userdata) { @@ -117,12 +120,17 @@ static struct entry* read_entry(struct userdata *u, const char *name) { goto fail; if (data.dsize != sizeof(struct entry)) { - pa_log_warn("Database contains entry for device %s of wrong size %lu != %lu", name, (unsigned long) data.dsize, (unsigned long) sizeof(struct entry)); + pa_log_debug("Database contains entry for device %s of wrong size %lu != %lu. Probably due to upgrade, ignoring.", name, (unsigned long) data.dsize, (unsigned long) sizeof(struct entry)); goto fail; } e = (struct entry*) data.dptr; + if (e->version != ENTRY_VERSION) { + pa_log_debug("Version of database entry for device %s doesn't match our version. Probably due to upgrade, ignoring.", name); + goto fail; + } + if (!(pa_cvolume_valid(&e->volume))) { pa_log_warn("Invalid volume stored in database for device %s", name); goto fail; @@ -173,6 +181,7 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3 return; memset(&entry, 0, sizeof(entry)); + entry.version = ENTRY_VERSION; if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK) { pa_sink *sink; diff --git a/src/modules/module-match.c b/src/modules/module-match.c index cbf62687..17936110 100644 --- a/src/modules/module-match.c +++ b/src/modules/module-match.c @@ -233,7 +233,6 @@ int pa__init(pa_module*m) { goto fail; } - u = pa_xnew(struct userdata, 1); u->rules = NULL; u->subscription = NULL; diff --git a/src/modules/module-position-event-sounds.c b/src/modules/module-position-event-sounds.c index 8e4f4c32..6252ebab 100644 --- a/src/modules/module-position-event-sounds.c +++ b/src/modules/module-position-event-sounds.c @@ -54,7 +54,6 @@ static const char* const valid_modargs[] = { }; struct userdata { - pa_core *core; pa_hook_slot *sink_input_fixate_hook_slot; }; @@ -62,6 +61,7 @@ static pa_hook_result_t sink_input_fixate_hook_callback(pa_core *core, pa_sink_i const char *hpos; double f; char t[PA_CVOLUME_SNPRINT_MAX]; + pa_cvolume v; pa_assert(data); @@ -80,16 +80,12 @@ static pa_hook_result_t sink_input_fixate_hook_callback(pa_core *core, pa_sink_i pa_log_debug("Positioning event sound '%s' at %0.2f.", pa_strnull(pa_proplist_gets(data->proplist, PA_PROP_EVENT_ID)), f); - if (!data->virtual_volume_is_set) { - pa_cvolume_reset(&data->virtual_volume, data->sample_spec.channels); - data->virtual_volume_is_set = TRUE; - data->virtual_volume_is_absolute = FALSE; - } + pa_cvolume_reset(&v, data->sample_spec.channels); + pa_cvolume_set_balance(&v, &data->channel_map, f*2.0-1.0); - pa_cvolume_set_balance(&data->virtual_volume, &data->channel_map, f*2.0-1.0); - data->save_volume = FALSE; + pa_log_debug("Final volume factor %s.", pa_cvolume_snprint(t, sizeof(t), &v)); - pa_log_debug("Final volume %s.", pa_cvolume_snprint(t, sizeof(t), &data->virtual_volume)); + pa_sink_input_new_data_apply_volume_factor(data, &v); return PA_HOOK_OK; } @@ -106,7 +102,6 @@ int pa__init(pa_module*m) { } m->userdata = u = pa_xnew(struct userdata, 1); - u->core = m->core; u->sink_input_fixate_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_FIXATE], PA_HOOK_EARLY, (pa_hook_cb_t) sink_input_fixate_hook_callback, u); pa_modargs_free(ma); diff --git a/src/modules/module-raop-sink.c b/src/modules/module-raop-sink.c index 74ee6122..1784b2cc 100644 --- a/src/modules/module-raop-sink.c +++ b/src/modules/module-raop-sink.c @@ -100,9 +100,6 @@ struct userdata { pa_usec_t latency; - pa_volume_t volume; - pa_bool_t muted; - /*esd_format_t format;*/ int32_t rate; @@ -133,6 +130,9 @@ enum { SINK_MESSAGE_RIP_SOCKET }; +/* Forward declaration */ +static void sink_set_volume_cb(pa_sink *); + static void on_connection(PA_GCC_UNUSED int fd, void*userdata) { struct userdata *u = userdata; pa_assert(u); @@ -141,7 +141,7 @@ static void on_connection(PA_GCC_UNUSED int fd, void*userdata) { u->fd = fd; /* Set the initial volume */ - pa_raop_client_set_volume(u->raop, u->volume); + sink_set_volume_cb(u->sink); pa_log_debug("Connection authenticated, handing fd to IO thread..."); @@ -255,43 +255,36 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse return pa_sink_process_msg(o, code, data, offset, chunk); } -static void sink_get_volume_cb(pa_sink *s) { - struct userdata *u = s->userdata; - int i; - - pa_assert(u); - - for (i = 0; i < s->sample_spec.channels; i++) - s->virtual_volume.values[i] = u->volume; -} - static void sink_set_volume_cb(pa_sink *s) { struct userdata *u = s->userdata; - int rv; + pa_cvolume hw; + pa_volume_t v; + char t[PA_CVOLUME_SNPRINT_MAX]; pa_assert(u); - /* If we're muted, we fake it */ - if (u->muted) + /* If we're muted we don't need to do anything */ + if (s->muted) return; - pa_assert(s->sample_spec.channels > 0); + /* Calculate the max volume of all channels. + We'll use this as our (single) volume on the APEX device and emulate + any variation in channel volumes in software */ + v = pa_cvolume_max(&s->virtual_volume); - /* Avoid pointless volume sets */ - if (u->volume == s->virtual_volume.values[0]) - return; + /* Create a pa_cvolume version of our single value */ + pa_cvolume_set(&hw, s->sample_spec.channels, v); - rv = pa_raop_client_set_volume(u->raop, s->virtual_volume.values[0]); - if (0 == rv) - u->volume = s->virtual_volume.values[0]; -} + /* Perform any software manipulation of the volume needed */ + pa_sw_cvolume_divide(&s->soft_volume, &s->virtual_volume, &hw); -static void sink_get_mute_cb(pa_sink *s) { - struct userdata *u = s->userdata; + pa_log_debug("Requested volume: %s", pa_cvolume_snprint(t, sizeof(t), &s->virtual_volume)); + pa_log_debug("Got hardware volume: %s", pa_cvolume_snprint(t, sizeof(t), &hw)); + pa_log_debug("Calculated software volume: %s", pa_cvolume_snprint(t, sizeof(t), &s->soft_volume)); - pa_assert(u); - - s->muted = u->muted; + /* Any necessary software volume manipulateion is done so set + our hw volume (or v as a single value) on the device */ + pa_raop_client_set_volume(u->raop, v); } static void sink_set_mute_cb(pa_sink *s) { @@ -299,8 +292,11 @@ static void sink_set_mute_cb(pa_sink *s) { pa_assert(u); - pa_raop_client_set_volume(u->raop, (s->muted ? PA_VOLUME_MUTED : u->volume)); - u->muted = s->muted; + if (s->muted) { + pa_raop_client_set_volume(u->raop, PA_VOLUME_MUTED); + } else { + sink_set_volume_cb(s); + } } static void thread_func(void *userdata) { @@ -541,9 +537,6 @@ int pa__init(pa_module*m) { u->next_encoding_overhead = 0; u->encoding_ratio = 1.0; - u->volume = roundf(0.7 * PA_VOLUME_NORM); - u->muted = FALSE; - u->rtpoll = pa_rtpoll_new(); pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll); u->rtpoll_item = NULL; @@ -583,9 +576,7 @@ int pa__init(pa_module*m) { u->sink->parent.process_msg = sink_process_msg; u->sink->userdata = u; - u->sink->get_volume = sink_get_volume_cb; u->sink->set_volume = sink_set_volume_cb; - u->sink->get_mute = sink_get_mute_cb; u->sink->set_mute = sink_set_mute_cb; u->sink->flags = PA_SINK_LATENCY|PA_SINK_NETWORK|PA_SINK_HW_VOLUME_CTRL; diff --git a/src/modules/module-stream-restore.c b/src/modules/module-stream-restore.c index 0c9bd4f9..2dd2045e 100644 --- a/src/modules/module-stream-restore.c +++ b/src/modules/module-stream-restore.c @@ -62,6 +62,7 @@ PA_MODULE_USAGE( "restore_muted=<Save/restore muted states?>"); #define SAVE_INTERVAL 10 +#define IDENTIFICATION_PROPERTY "module-stream-restore.id" static const char* const valid_modargs[] = { "restore_device", @@ -90,16 +91,18 @@ struct userdata { pa_idxset *subscribed; }; -struct entry { +#define ENTRY_VERSION 1 + +struct entry PA_GCC_PACKED { + uint8_t version; + pa_bool_t muted_valid:1, relative_volume_valid:1, absolute_volume_valid:1, device_valid:1; + pa_bool_t muted:1; pa_channel_map channel_map; - char device[PA_NAME_MAX]; pa_cvolume relative_volume; pa_cvolume absolute_volume; - pa_bool_t muted:1; - pa_bool_t device_valid:1, relative_volume_valid:1, absolute_volume_valid:1, muted_valid:1; + char device[PA_NAME_MAX]; }; - enum { SUBCOMMAND_TEST, SUBCOMMAND_READ, @@ -127,20 +130,27 @@ static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct static char *get_name(pa_proplist *p, const char *prefix) { const char *r; + char *t; if (!p) return NULL; + if ((r = pa_proplist_gets(p, IDENTIFICATION_PROPERTY))) + return pa_xstrdup(r); + if ((r = pa_proplist_gets(p, PA_PROP_MEDIA_ROLE))) - return pa_sprintf_malloc("%s-by-media-role:%s", prefix, r); + t = pa_sprintf_malloc("%s-by-media-role:%s", prefix, r); else if ((r = pa_proplist_gets(p, PA_PROP_APPLICATION_ID))) - return pa_sprintf_malloc("%s-by-application-id:%s", prefix, r); + t = pa_sprintf_malloc("%s-by-application-id:%s", prefix, r); else if ((r = pa_proplist_gets(p, PA_PROP_APPLICATION_NAME))) - return pa_sprintf_malloc("%s-by-application-name:%s", prefix, r); + t = pa_sprintf_malloc("%s-by-application-name:%s", prefix, r); else if ((r = pa_proplist_gets(p, PA_PROP_MEDIA_NAME))) - return pa_sprintf_malloc("%s-by-media-name:%s", prefix, r); + t = pa_sprintf_malloc("%s-by-media-name:%s", prefix, r); + else + t = pa_sprintf_malloc("%s-fallback:%s", prefix, r); - return pa_sprintf_malloc("%s-fallback:%s", prefix, r); + pa_proplist_sets(p, IDENTIFICATION_PROPERTY, t); + return t; } static struct entry* read_entry(struct userdata *u, const char *name) { @@ -161,19 +171,19 @@ static struct entry* read_entry(struct userdata *u, const char *name) { if (data.dsize != sizeof(struct entry)) { /* This is probably just a database upgrade, hence let's not * consider this more than a debug message */ - pa_log_debug("Database contains entry for stream %s of wrong size %lu != %lu", name, (unsigned long) data.dsize, (unsigned long) sizeof(struct entry)); + pa_log_debug("Database contains entry for stream %s of wrong size %lu != %lu. Probably due to uprade, ignoring.", name, (unsigned long) data.dsize, (unsigned long) sizeof(struct entry)); goto fail; } e = (struct entry*) data.dptr; - if (!memchr(e->device, 0, sizeof(e->device))) { - pa_log_warn("Database contains entry for stream %s with missing NUL byte in device name", name); + if (e->version != ENTRY_VERSION) { + pa_log_debug("Version of database entry for stream %s doesn't match our version. Probably due to upgrade, ignoring.", name); goto fail; } - if (!(pa_channel_map_valid(&e->channel_map))) { - pa_log_warn("Invalid channel map stored in database for stream %s", name); + if (!memchr(e->device, 0, sizeof(e->device))) { + pa_log_warn("Database contains entry for stream %s with missing NUL byte in device name", name); goto fail; } @@ -182,6 +192,11 @@ static struct entry* read_entry(struct userdata *u, const char *name) { goto fail; } + if ((e->relative_volume_valid || e->absolute_volume_valid) && !(pa_channel_map_valid(&e->channel_map))) { + pa_log_warn("Invalid channel map stored in database for stream %s", name); + goto fail; + } + if ((e->relative_volume_valid && (!pa_cvolume_valid(&e->relative_volume) || e->relative_volume.channels != e->channel_map.channels)) || (e->absolute_volume_valid && (!pa_cvolume_valid(&e->absolute_volume) || e->absolute_volume.channels != e->channel_map.channels))) { pa_log_warn("Invalid volume stored in database for stream %s", name); @@ -233,7 +248,7 @@ static pa_bool_t entries_equal(const struct entry *a, const struct entry *b) { return FALSE; if (a->muted_valid != b->muted_valid || - (a->muted && (a->muted != b->muted))) + (a->muted_valid && (a->muted != b->muted))) return FALSE; t = b->relative_volume; @@ -265,6 +280,7 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3 return; memset(&entry, 0, sizeof(entry)); + entry.version = ENTRY_VERSION; if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK_INPUT) { pa_sink_input *sink_input; @@ -388,32 +404,33 @@ static pa_hook_result_t sink_input_fixate_hook_callback(pa_core *c, pa_sink_inpu if (u->restore_volume) { - if (!new_data->virtual_volume_is_set) { + if (!new_data->volume_is_set) { pa_cvolume v; pa_cvolume_init(&v); if (new_data->sink->flags & PA_SINK_FLAT_VOLUME) { + /* We don't check for e->device_valid here because + that bit marks whether it is a good choice for + restoring, not just if the data is filled in. */ if (e->absolute_volume_valid && - e->device_valid && - pa_streq(new_data->sink->name, e->device)) { + (e->device[0] == 0 || pa_streq(new_data->sink->name, e->device))) { v = e->absolute_volume; - new_data->virtual_volume_is_absolute = TRUE; + new_data->volume_is_absolute = TRUE; } else if (e->relative_volume_valid) { - v = e->relative_volume; - new_data->virtual_volume_is_absolute = FALSE; + new_data->volume_is_absolute = FALSE; } } else if (e->relative_volume_valid) { v = e->relative_volume; - new_data->virtual_volume_is_absolute = FALSE; + new_data->volume_is_absolute = FALSE; } if (v.channels > 0) { pa_log_info("Restoring volume for sink input %s.", name); - pa_sink_input_new_data_set_virtual_volume(new_data, pa_cvolume_remap(&v, &e->channel_map, &new_data->channel_map)); + pa_sink_input_new_data_set_volume(new_data, pa_cvolume_remap(&v, &e->channel_map, &new_data->channel_map)); new_data->save_volume = TRUE; } } else @@ -523,11 +540,10 @@ static void apply_entry(struct userdata *u, const char *name, struct entry *e) { if (si->sink->flags & PA_SINK_FLAT_VOLUME) { if (e->absolute_volume_valid && - e->device_valid && - pa_streq(e->device, si->sink->name)) + (e->device[0] == 0 || pa_streq(e->device, si->sink->name))) v = e->absolute_volume; else if (e->relative_volume_valid) { - pa_cvolume t = si->sink->virtual_volume; + pa_cvolume t = *pa_sink_get_volume(si->sink, FALSE); pa_sw_cvolume_multiply(&v, &e->relative_volume, pa_cvolume_remap(&t, &si->sink->channel_map, &e->channel_map)); } } else if (e->relative_volume_valid) @@ -655,10 +671,11 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio if ((e = read_entry(u, name))) { pa_cvolume r; + pa_channel_map cm; pa_tagstruct_puts(reply, name); - pa_tagstruct_put_channel_map(reply, &e->channel_map); - pa_tagstruct_put_cvolume(reply, e->relative_volume_valid ? &e->relative_volume : pa_cvolume_init(&r)); + pa_tagstruct_put_channel_map(reply, (e->relative_volume_valid || e->absolute_volume_valid) ? &e->channel_map : pa_channel_map_init(&cm)); + pa_tagstruct_put_cvolume(reply, e->absolute_volume_valid ? &e->absolute_volume : (e->relative_volume_valid ? &e->relative_volume : pa_cvolume_init(&r))); pa_tagstruct_puts(reply, e->device_valid ? e->device : NULL); pa_tagstruct_put_boolean(reply, e->muted_valid ? e->muted : FALSE); @@ -697,21 +714,25 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio int k; memset(&entry, 0, sizeof(entry)); + entry.version = ENTRY_VERSION; if (pa_tagstruct_gets(t, &name) < 0 || pa_tagstruct_get_channel_map(t, &entry.channel_map) || - pa_tagstruct_get_cvolume(t, &entry.relative_volume) < 0 || + pa_tagstruct_get_cvolume(t, &entry.absolute_volume) < 0 || pa_tagstruct_gets(t, &device) < 0 || pa_tagstruct_get_boolean(t, &muted) < 0) goto fail; - entry.absolute_volume_valid = FALSE; - entry.relative_volume_valid = entry.relative_volume.channels > 0; - - if (entry.relative_volume_valid && - entry.channel_map.channels != entry.relative_volume.channels) + if (!name || !*name) goto fail; + entry.relative_volume = entry.absolute_volume; + entry.absolute_volume_valid = entry.relative_volume_valid = entry.relative_volume.channels > 0; + + if (entry.relative_volume_valid) + if (!pa_cvolume_compatible_with_channel_map(&entry.relative_volume, &entry.channel_map)) + goto fail; + entry.muted = muted; entry.muted_valid = TRUE; diff --git a/src/modules/module-tunnel.c b/src/modules/module-tunnel.c index 5c7a6e55..26da2575 100644 --- a/src/modules/module-tunnel.c +++ b/src/modules/module-tunnel.c @@ -662,7 +662,7 @@ fail: /* Called from main context */ static void stream_get_latency_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { struct userdata *u = userdata; - pa_usec_t sink_usec, source_usec, transport_usec; + pa_usec_t sink_usec, source_usec, transport_usec = 0; pa_bool_t playing; int64_t write_index, read_index; struct timeval local, remote, now; diff --git a/src/modules/rtp/module-rtp-recv.c b/src/modules/rtp/module-rtp-recv.c index baf3532f..c118b5c6 100644 --- a/src/modules/rtp/module-rtp-recv.c +++ b/src/modules/rtp/module-rtp-recv.c @@ -453,7 +453,7 @@ static struct session *session_new(struct userdata *u, const pa_sdp_info *sdp_in data.module = u->module; pa_sink_input_new_data_set_sample_spec(&data, &sdp_info->sample_spec); - pa_sink_input_new(&s->sink_input, u->module->core, &data, 0); + pa_sink_input_new(&s->sink_input, u->module->core, &data, PA_SINK_INPUT_VARIABLE_RATE); pa_sink_input_new_data_done(&data); if (!s->sink_input) { |