[pulseaudio-discuss] [PATCH 1/3] stream-interaction: Use LLIST for groups.
Juho Hämäläinen
jusa at hilvi.org
Fri Jul 13 08:21:03 UTC 2018
Use LLIST instead of array for interaction groups, and refactor parsing.
Side effect from refactoring is that module-role-cork also now supports
interaction groups.
Signed-off-by: Juho Hämäläinen <jusa at hilvi.org>
---
src/modules/stream-interaction.c | 337 +++++++++++++++++++++------------------
1 file changed, 180 insertions(+), 157 deletions(-)
diff --git a/src/modules/stream-interaction.c b/src/modules/stream-interaction.c
index bf7134a..d538e88 100644
--- a/src/modules/stream-interaction.c
+++ b/src/modules/stream-interaction.c
@@ -31,21 +31,28 @@
#include <pulsecore/core-util.h>
#include <pulsecore/sink-input.h>
#include <pulsecore/modargs.h>
+#include <pulsecore/llist.h>
+#include <pulsecore/idxset.h>
#include "stream-interaction.h"
+#define STREAM_INTERACTION_DEFAULT_DUCK_VOLUME_DB (-20)
+#define STREAM_INTERACTION_ANY_ROLE "any_role"
+#define STREAM_INTERACTION_NO_ROLE "no_role"
+
struct group {
char *name;
pa_idxset *trigger_roles;
pa_idxset *interaction_roles;
pa_hashmap *interaction_state;
pa_volume_t volume;
+
+ PA_LLIST_FIELDS(struct group);
};
struct userdata {
pa_core *core;
- uint32_t n_groups;
- struct group **groups;
+ PA_LLIST_HEAD(struct group, groups);
bool global:1;
bool duck:1;
pa_hook_slot
@@ -63,13 +70,12 @@ static const char *get_trigger_role(struct userdata *u, pa_sink_input *i, struct
uint32_t role_idx;
if (!(role = pa_proplist_gets(i->proplist, PA_PROP_MEDIA_ROLE)))
- role = "no_role";
+ role = STREAM_INTERACTION_NO_ROLE;
if (g == NULL) {
/* get it from all groups */
- uint32_t j;
- for (j = 0; j < u->n_groups; j++) {
- PA_IDXSET_FOREACH(trigger_role, u->groups[j]->trigger_roles, role_idx) {
+ PA_LLIST_FOREACH(g, u->groups) {
+ PA_IDXSET_FOREACH(trigger_role, g->trigger_roles, role_idx) {
if (pa_streq(role, trigger_role))
return trigger_role;
}
@@ -170,12 +176,13 @@ static inline void apply_interaction_to_sink(struct userdata *u, pa_sink *s, con
continue;
if (!(role = pa_proplist_gets(j->proplist, PA_PROP_MEDIA_ROLE)))
- role = "no_role";
+ role = STREAM_INTERACTION_NO_ROLE;
PA_IDXSET_FOREACH(interaction_role, g->interaction_roles, role_idx) {
if ((trigger = pa_streq(role, interaction_role)))
break;
- if ((trigger = (pa_streq(interaction_role, "any_role") && !get_trigger_role(u, j, g))))
+ if ((trigger = (pa_streq(interaction_role, STREAM_INTERACTION_ANY_ROLE) &&
+ !get_trigger_role(u, j, g))))
break;
}
if (!trigger)
@@ -229,7 +236,7 @@ static void remove_interactions(struct userdata *u, struct group *g) {
if(!!pa_hashmap_get(g->interaction_state, j)) {
corked = (j->state == PA_SINK_INPUT_CORKED);
if (!(role = pa_proplist_gets(j->proplist, PA_PROP_MEDIA_ROLE)))
- role = "no_role";
+ role = STREAM_INTERACTION_NO_ROLE;
uncork_or_unduck(u, j, role, corked, g);
}
}
@@ -238,21 +245,21 @@ static void remove_interactions(struct userdata *u, struct group *g) {
static pa_hook_result_t process(struct userdata *u, pa_sink_input *i, bool create, bool new_stream) {
const char *trigger_role;
- uint32_t j;
+ struct group *g;
pa_assert(u);
pa_sink_input_assert_ref(i);
if (!create)
- for (j = 0; j < u->n_groups; j++)
- pa_hashmap_remove(u->groups[j]->interaction_state, i);
+ PA_LLIST_FOREACH(g, u->groups)
+ pa_hashmap_remove(g->interaction_state, i);
if (!i->sink)
return PA_HOOK_OK;
- for (j = 0; j < u->n_groups; j++) {
- trigger_role = find_global_trigger_stream(u, i->sink, create ? NULL : i, u->groups[j]);
- apply_interaction(u, i->sink, trigger_role, create ? NULL : i, new_stream, u->groups[j]);
+ PA_LLIST_FOREACH(g, u->groups) {
+ trigger_role = find_global_trigger_stream(u, i->sink, create ? NULL : i, g);
+ apply_interaction(u, i->sink, trigger_role, create ? NULL : i, new_stream, g);
}
return PA_HOOK_OK;
@@ -315,13 +322,127 @@ static pa_hook_result_t sink_input_proplist_changed_cb(pa_core *core, pa_sink_in
return PA_HOOK_OK;
}
+static struct group *group_new(const char *prefix, uint32_t id) {
+ struct group *g = pa_xnew0(struct group, 1);
+ PA_LLIST_INIT(struct group, g);
+ g->trigger_roles = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func,
+ pa_xfree, NULL);
+ g->interaction_roles = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func,
+ pa_xfree, NULL);
+ g->interaction_state = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+ g->volume = pa_sw_volume_from_dB(STREAM_INTERACTION_DEFAULT_DUCK_VOLUME_DB);
+ g->name = pa_sprintf_malloc("%s_group_%u", prefix, id);
+
+ return g;
+}
+
+static void group_free(struct group *g) {
+ pa_idxset_free(g->trigger_roles, pa_xfree);
+ pa_idxset_free(g->interaction_roles, pa_xfree);
+ pa_hashmap_free(g->interaction_state);
+ pa_xfree(g->name);
+ pa_xfree(g);
+}
+
+typedef bool (*group_value_add_t)(struct group *g, const char *data);
+
+static bool group_value_add_trigger_roles(struct group *g, const char *data) {
+ pa_idxset_put(g->trigger_roles, pa_xstrdup(data), NULL);
+ return true;
+}
+
+static bool group_value_add_interaction_roles(struct group *g, const char *data) {
+ pa_idxset_put(g->interaction_roles, pa_xstrdup(data), NULL);
+ return true;
+}
+
+static bool group_value_add_volume(struct group *g, const char *data) {
+ if (pa_parse_volume(data, &(g->volume)) < 0) {
+ pa_log("Failed to parse volume");
+ return false;
+ }
+ return true;
+}
+
+static bool group_parse_roles(pa_modargs *ma, const char *name, group_value_add_t add_cb, struct group *groups) {
+ const char *roles;
+ char *roles_in_group;
+ struct group *g;
+
+ pa_assert(ma);
+ pa_assert(name);
+ pa_assert(add_cb);
+ pa_assert(groups);
+
+ g = groups;
+ roles = pa_modargs_get_value(ma, name, NULL);
+
+ if (roles) {
+ const char *group_split_state = NULL;
+
+ while ((roles_in_group = pa_split(roles, "/", &group_split_state))) {
+ pa_assert(g);
+
+ if (roles_in_group[0] != '\0') {
+ const char *split_state = NULL;
+ char *n = NULL;
+ while ((n = pa_split(roles_in_group, ",", &split_state))) {
+ bool ret = true;
+
+ if (n[0] != '\0')
+ ret = add_cb(g, n);
+ else {
+ ret = false;
+ pa_log("Empty %s", name);
+ }
+
+ pa_xfree(n);
+ if (!ret)
+ goto fail;
+ }
+ } else {
+ pa_log("Empty %s", name);
+ goto fail;
+ }
+
+ g = g->next;
+ pa_xfree(roles_in_group);
+ }
+ }
+
+ return true;
+
+fail:
+ return false;
+}
+
+static int count_groups(pa_modargs *ma, const char *module_argument) {
+ const char *val;
+ int count = 0;
+
+ pa_assert(ma);
+ pa_assert(module_argument);
+
+ val = pa_modargs_get_value(ma, module_argument, NULL);
+ if (val) {
+ const char *split_state = NULL;
+ int len = 0;
+ /* Count empty ones as well, empty groups will fail later
+ * when parsing the groups. */
+ while (pa_split_in_place(val, "/", &len, &split_state))
+ count++;
+ }
+
+ return count;
+}
+
int pa_stream_interaction_init(pa_module *m, const char* const v_modargs[]) {
pa_modargs *ma = NULL;
struct userdata *u;
- const char *roles;
- char *roles_in_group = NULL;
bool global = false;
uint32_t i = 0;
+ uint32_t group_count_tr = 0;
+ struct group *last = NULL;
pa_assert(m);
@@ -333,155 +454,66 @@ int pa_stream_interaction_init(pa_module *m, const char* const v_modargs[]) {
m->userdata = u = pa_xnew0(struct userdata, 1);
u->core = m->core;
+ u->duck = pa_streq(m->name, "module-role-ducking");
+ PA_LLIST_HEAD_INIT(struct group, u->groups);
- u->duck = false;
- if (pa_streq(m->name, "module-role-ducking"))
- u->duck = true;
-
- u->n_groups = 1;
+ group_count_tr = count_groups(ma, "trigger_roles");
if (u->duck) {
- const char *volumes;
- uint32_t group_count_tr = 0;
uint32_t group_count_du = 0;
uint32_t group_count_vol = 0;
-
- roles = pa_modargs_get_value(ma, "trigger_roles", NULL);
- if (roles) {
- const char *split_state = NULL;
- char *n = NULL;
- while ((n = pa_split(roles, "/", &split_state))) {
- group_count_tr++;
- pa_xfree(n);
- }
- }
- roles = pa_modargs_get_value(ma, "ducking_roles", NULL);
- if (roles) {
- const char *split_state = NULL;
- char *n = NULL;
- while ((n = pa_split(roles, "/", &split_state))) {
- group_count_du++;
- pa_xfree(n);
- }
- }
- volumes = pa_modargs_get_value(ma, "volume", NULL);
- if (volumes) {
- const char *split_state = NULL;
- char *n = NULL;
- while ((n = pa_split(volumes, "/", &split_state))) {
- group_count_vol++;
- pa_xfree(n);
- }
- }
+ group_count_du = count_groups(ma, "ducking_roles");
+ group_count_vol = count_groups(ma, "volume");
if ((group_count_tr > 1 || group_count_du > 1 || group_count_vol > 1) &&
((group_count_tr != group_count_du) || (group_count_tr != group_count_vol))) {
pa_log("Invalid number of groups");
goto fail;
}
+ } else {
+ uint32_t group_count_co;
+ group_count_co = count_groups(ma, "cork_roles");
- if (group_count_tr > 0)
- u->n_groups = group_count_tr;
+ if ((group_count_tr > 1 || group_count_co > 1) &&
+ (group_count_tr != group_count_co)) {
+ pa_log("Invalid number of groups");
+ goto fail;
+ }
}
- u->groups = pa_xnew0(struct group*, u->n_groups);
- for (i = 0; i < u->n_groups; i++) {
- u->groups[i] = pa_xnew0(struct group, 1);
- u->groups[i]->trigger_roles = pa_idxset_new(NULL, NULL);
- u->groups[i]->interaction_roles = pa_idxset_new(NULL, NULL);
- u->groups[i]->interaction_state = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
- if (u->duck)
- u->groups[i]->name = pa_sprintf_malloc("ducking_group_%u", i);
- }
+ /* create at least one group */
+ if (group_count_tr == 0)
+ group_count_tr = 1;
- roles = pa_modargs_get_value(ma, "trigger_roles", NULL);
- if (roles) {
- const char *group_split_state = NULL;
- i = 0;
+ for (i = 0; i < group_count_tr; i++) {
+ struct group *g = group_new(u->duck ? "ducking" : "cork", i);
+ PA_LLIST_INSERT_AFTER(struct group, u->groups, last, g);
+ last = g;
+ }
- while ((roles_in_group = pa_split(roles, "/", &group_split_state))) {
- if (roles_in_group[0] != '\0') {
- const char *split_state = NULL;
- char *n = NULL;
- while ((n = pa_split(roles_in_group, ",", &split_state))) {
- if (n[0] != '\0')
- pa_idxset_put(u->groups[i]->trigger_roles, n, NULL);
- else {
- pa_log("empty trigger role");
- pa_xfree(n);
- goto fail;
- }
- }
- i++;
- } else {
- pa_log("empty trigger roles");
- goto fail;
- }
+ if (!group_parse_roles(ma, "trigger_roles", group_value_add_trigger_roles, u->groups))
+ goto fail;
- pa_xfree(roles_in_group);
- }
- }
- if (pa_idxset_isempty(u->groups[0]->trigger_roles)) {
+ if (pa_idxset_isempty(u->groups->trigger_roles)) {
pa_log_debug("Using role 'phone' as trigger role.");
- pa_idxset_put(u->groups[0]->trigger_roles, pa_xstrdup("phone"), NULL);
+ pa_idxset_put(u->groups->trigger_roles, pa_xstrdup("phone"), NULL);
}
- roles = pa_modargs_get_value(ma, u->duck ? "ducking_roles" : "cork_roles", NULL);
- if (roles) {
- const char *group_split_state = NULL;
- i = 0;
-
- while ((roles_in_group = pa_split(roles, "/", &group_split_state))) {
- if (roles_in_group[0] != '\0') {
- const char *split_state = NULL;
- char *n = NULL;
- while ((n = pa_split(roles_in_group, ",", &split_state))) {
- if (n[0] != '\0')
- pa_idxset_put(u->groups[i]->interaction_roles, n, NULL);
- else {
- pa_log("empty ducking role");
- pa_xfree(n);
- goto fail;
- }
- }
- i++;
- } else {
- pa_log("empty ducking roles");
- goto fail;
- }
+ if (!group_parse_roles(ma,
+ u->duck ? "ducking_roles" : "cork_roles",
+ group_value_add_interaction_roles,
+ u->groups))
+ goto fail;
- pa_xfree(roles_in_group);
- }
- }
- if (pa_idxset_isempty(u->groups[0]->interaction_roles)) {
- pa_log_debug("Using roles 'music' and 'video' as %s roles.", u->duck ? "ducking" : "cork");
- pa_idxset_put(u->groups[0]->interaction_roles, pa_xstrdup("music"), NULL);
- pa_idxset_put(u->groups[0]->interaction_roles, pa_xstrdup("video"), NULL);
+ if (pa_idxset_isempty(u->groups->interaction_roles)) {
+ pa_log_debug("Using roles 'music' and 'video' as %s_roles.", u->duck ? "ducking" : "cork");
+ pa_idxset_put(u->groups->interaction_roles, pa_xstrdup("music"), NULL);
+ pa_idxset_put(u->groups->interaction_roles, pa_xstrdup("video"), NULL);
}
- if (u->duck) {
- const char *volumes;
- u->groups[0]->volume = pa_sw_volume_from_dB(-20);
- if ((volumes = pa_modargs_get_value(ma, "volume", NULL))) {
- const char *group_split_state = NULL;
- char *n = NULL;
- i = 0;
- while ((n = pa_split(volumes, "/", &group_split_state))) {
- if (n[0] != '\0') {
- if (pa_parse_volume(n, &(u->groups[i++]->volume)) < 0) {
- pa_log("Failed to parse volume");
- pa_xfree(n);
- goto fail;
- }
- } else {
- pa_log("empty volume");
- pa_xfree(n);
- goto fail;
- }
- pa_xfree(n);
- }
- }
- }
+ if (u->duck)
+ if (!group_parse_roles(ma, "volume", group_value_add_volume, u->groups))
+ goto fail;
if (pa_modargs_get_value_boolean(ma, "global", &global) < 0) {
pa_log("Invalid boolean parameter: global");
@@ -506,8 +538,6 @@ fail:
if (ma)
pa_modargs_free(ma);
- if (roles_in_group)
- pa_xfree(roles_in_group);
return -1;
@@ -521,18 +551,11 @@ void pa_stream_interaction_done(pa_module *m) {
if (!(u = m->userdata))
return;
- if (u->groups) {
- uint32_t j;
- for (j = 0; j < u->n_groups; j++) {
- remove_interactions(u, u->groups[j]);
- pa_idxset_free(u->groups[j]->trigger_roles, pa_xfree);
- pa_idxset_free(u->groups[j]->interaction_roles, pa_xfree);
- pa_hashmap_free(u->groups[j]->interaction_state);
- if (u->duck)
- pa_xfree(u->groups[j]->name);
- pa_xfree(u->groups[j]);
- }
- pa_xfree(u->groups);
+ while (u->groups) {
+ struct group *g = u->groups;
+ PA_LLIST_REMOVE(struct group, u->groups, g);
+ remove_interactions(u, g);
+ group_free(g);
}
if (u->sink_input_put_slot)
--
2.7.4
More information about the pulseaudio-discuss
mailing list