From 3e7e901ba0a8e368b3ac99febebdd3918495e3c6 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Fri, 11 Mar 2016 12:02:22 +0200 Subject: sink-input, source-output: rework property setting pa_sink_input_update_proplist() is inconvenient in many cases, because it requires allocating a new proplist, even if the goal is to just set one property. pa_sink_input_update_properties also can't properly log property changes, because it has to assume that all values are arbitrary binary data. This patch adds pa_sink_input_set_property() for setting a string value for a single property, and pa_sink_input_set_property_arbitrary() for setting a binary value for a single property. pa_sink_input_update_properties() is reimplemented as a wrapper around pa_sink_input_set_property_arbitrary() to centralize logging and sending change notifications. (The above mentions only sink input functions for brevity, but the same changes are implemented for source outputs too.) --- src/pulsecore/sink-input.c | 117 ++++++++++++++++++++++++++++++++++++++++-- src/pulsecore/sink-input.h | 2 + src/pulsecore/source-output.c | 117 ++++++++++++++++++++++++++++++++++++++++-- src/pulsecore/source-output.h | 2 + 4 files changed, 228 insertions(+), 10 deletions(-) diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c index 843297f6c..42d0b32f5 100644 --- a/src/pulsecore/sink-input.c +++ b/src/pulsecore/sink-input.c @@ -1426,17 +1426,124 @@ void pa_sink_input_set_mute(pa_sink_input *i, bool mute, bool save) { pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_MUTE_CHANGED], i); } +void pa_sink_input_set_property(pa_sink_input *i, const char *key, const char *value) { + char *old_value = NULL; + const char *new_value; + + pa_assert(i); + pa_assert(key); + + if (pa_proplist_contains(i->proplist, key)) { + old_value = pa_xstrdup(pa_proplist_gets(i->proplist, key)); + if (old_value) { + if (pa_streq(value, old_value)) + goto finish; + } else + old_value = pa_xstrdup("(data)"); + } else { + if (!value) + goto finish; + + old_value = pa_xstrdup("(unset)"); + } + + if (value) { + pa_proplist_sets(i->proplist, key, value); + new_value = value; + } else { + pa_proplist_unset(i->proplist, key); + new_value = "(unset)"; + } + + if (PA_SINK_INPUT_IS_LINKED(i->state)) { + pa_log_debug("Sink input %u: proplist[%s]: %s -> %s", i->index, key, old_value, new_value); + pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_PROPLIST_CHANGED], i); + pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT | PA_SUBSCRIPTION_EVENT_CHANGE, i->index); + } + +finish: + pa_xfree(old_value); +} + +void pa_sink_input_set_property_arbitrary(pa_sink_input *i, const char *key, const uint8_t *value, size_t nbytes) { + const uint8_t *old_value; + size_t old_nbytes; + const char *old_value_str; + const char *new_value_str; + + pa_assert(i); + pa_assert(key); + + if (pa_proplist_get(i->proplist, key, (const void **) &old_value, &old_nbytes) >= 0) { + if (value && nbytes == old_nbytes && !memcmp(value, old_value, nbytes)) + return; + + old_value_str = "(data)"; + + } else { + if (!value) + return; + + old_value_str = "(unset)"; + } + + if (value) { + pa_proplist_set(i->proplist, key, value, nbytes); + new_value_str = "(data)"; + } else { + pa_proplist_unset(i->proplist, key); + new_value_str = "(unset)"; + } + + if (PA_SINK_INPUT_IS_LINKED(i->state)) { + pa_log_debug("Sink input %u: proplist[%s]: %s -> %s", i->index, key, old_value_str, new_value_str); + pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_PROPLIST_CHANGED], i); + pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT | PA_SUBSCRIPTION_EVENT_CHANGE, i->index); + } +} + /* Called from main thread */ void pa_sink_input_update_proplist(pa_sink_input *i, pa_update_mode_t mode, pa_proplist *p) { + void *state; + const char *key; + const uint8_t *value; + size_t nbytes; + pa_sink_input_assert_ref(i); + pa_assert(p); pa_assert_ctl_context(); - if (p) - pa_proplist_update(i->proplist, mode, p); + switch (mode) { + case PA_UPDATE_SET: { + /* Delete everything that is not in p. */ + for (state = NULL; (key = pa_proplist_iterate(i->proplist, &state));) { + if (!pa_proplist_contains(p, key)) + pa_sink_input_set_property(i, key, NULL); + } - if (PA_SINK_INPUT_IS_LINKED(i->state)) { - pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_PROPLIST_CHANGED], i); - pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index); + /* Fall through. */ + } + + case PA_UPDATE_REPLACE: { + for (state = NULL; (key = pa_proplist_iterate(p, &state));) { + pa_proplist_get(p, key, (const void **) &value, &nbytes); + pa_sink_input_set_property_arbitrary(i, key, value, nbytes); + } + + break; + } + + case PA_UPDATE_MERGE: { + for (state = NULL; (key = pa_proplist_iterate(p, &state));) { + if (pa_proplist_contains(i->proplist, key)) + continue; + + pa_proplist_get(p, key, (const void **) &value, &nbytes); + pa_sink_input_set_property_arbitrary(i, key, value, nbytes); + } + + break; + } } } diff --git a/src/pulsecore/sink-input.h b/src/pulsecore/sink-input.h index 86deab278..3485d6e03 100644 --- a/src/pulsecore/sink-input.h +++ b/src/pulsecore/sink-input.h @@ -373,6 +373,8 @@ pa_cvolume *pa_sink_input_get_volume(pa_sink_input *i, pa_cvolume *volume, bool void pa_sink_input_set_mute(pa_sink_input *i, bool mute, bool save); +void pa_sink_input_set_property(pa_sink_input *i, const char *key, const char *value); +void pa_sink_input_set_property_arbitrary(pa_sink_input *i, const char *key, const uint8_t *value, size_t nbytes); void pa_sink_input_update_proplist(pa_sink_input *i, pa_update_mode_t mode, pa_proplist *p); pa_resample_method_t pa_sink_input_get_resample_method(pa_sink_input *i); diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c index 6d54ae826..f3910c58d 100644 --- a/src/pulsecore/source-output.c +++ b/src/pulsecore/source-output.c @@ -1078,17 +1078,124 @@ void pa_source_output_set_mute(pa_source_output *o, bool mute, bool save) { pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MUTE_CHANGED], o); } +void pa_source_output_set_property(pa_source_output *o, const char *key, const char *value) { + char *old_value = NULL; + const char *new_value; + + pa_assert(o); + pa_assert(key); + + if (pa_proplist_contains(o->proplist, key)) { + old_value = pa_xstrdup(pa_proplist_gets(o->proplist, key)); + if (old_value) { + if (pa_streq(value, old_value)) + goto finish; + } else + old_value = pa_xstrdup("(data)"); + } else { + if (!value) + goto finish; + + old_value = pa_xstrdup("(unset)"); + } + + if (value) { + pa_proplist_sets(o->proplist, key, value); + new_value = value; + } else { + pa_proplist_unset(o->proplist, key); + new_value = "(unset)"; + } + + if (PA_SINK_INPUT_IS_LINKED(o->state)) { + pa_log_debug("Source output %u: proplist[%s]: %s -> %s", o->index, key, old_value, new_value); + pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PROPLIST_CHANGED], o); + pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT | PA_SUBSCRIPTION_EVENT_CHANGE, o->index); + } + +finish: + pa_xfree(old_value); +} + +void pa_source_output_set_property_arbitrary(pa_source_output *o, const char *key, const uint8_t *value, size_t nbytes) { + const uint8_t *old_value; + size_t old_nbytes; + const char *old_value_str; + const char *new_value_str; + + pa_assert(o); + pa_assert(key); + + if (pa_proplist_get(o->proplist, key, (const void **) &old_value, &old_nbytes) >= 0) { + if (value && nbytes == old_nbytes && !memcmp(value, old_value, nbytes)) + return; + + old_value_str = "(data)"; + + } else { + if (!value) + return; + + old_value_str = "(unset)"; + } + + if (value) { + pa_proplist_set(o->proplist, key, value, nbytes); + new_value_str = "(data)"; + } else { + pa_proplist_unset(o->proplist, key); + new_value_str = "(unset)"; + } + + if (PA_SOURCE_OUTPUT_IS_LINKED(o->state)) { + pa_log_debug("Source output %u: proplist[%s]: %s -> %s", o->index, key, old_value_str, new_value_str); + pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PROPLIST_CHANGED], o); + pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT | PA_SUBSCRIPTION_EVENT_CHANGE, o->index); + } +} + /* Called from main thread */ void pa_source_output_update_proplist(pa_source_output *o, pa_update_mode_t mode, pa_proplist *p) { + void *state; + const char *key; + const uint8_t *value; + size_t nbytes; + pa_source_output_assert_ref(o); + pa_assert(p); pa_assert_ctl_context(); - if (p) - pa_proplist_update(o->proplist, mode, p); + switch (mode) { + case PA_UPDATE_SET: { + /* Delete everything that is not in p. */ + for (state = NULL; (key = pa_proplist_iterate(o->proplist, &state));) { + if (!pa_proplist_contains(p, key)) + pa_source_output_set_property(o, key, NULL); + } - if (PA_SOURCE_OUTPUT_IS_LINKED(o->state)) { - pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PROPLIST_CHANGED], o); - pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index); + /* Fall through. */ + } + + case PA_UPDATE_REPLACE: { + for (state = NULL; (key = pa_proplist_iterate(p, &state));) { + pa_proplist_get(p, key, (const void **) &value, &nbytes); + pa_source_output_set_property_arbitrary(o, key, value, nbytes); + } + + break; + } + + case PA_UPDATE_MERGE: { + for (state = NULL; (key = pa_proplist_iterate(p, &state));) { + if (pa_proplist_contains(o->proplist, key)) + continue; + + pa_proplist_get(p, key, (const void **) &value, &nbytes); + pa_source_output_set_property_arbitrary(o, key, value, nbytes); + } + + break; + } } } diff --git a/src/pulsecore/source-output.h b/src/pulsecore/source-output.h index 26be4846c..b804092ba 100644 --- a/src/pulsecore/source-output.h +++ b/src/pulsecore/source-output.h @@ -316,6 +316,8 @@ pa_cvolume *pa_source_output_get_volume(pa_source_output *o, pa_cvolume *volume, void pa_source_output_set_mute(pa_source_output *o, bool mute, bool save); +void pa_source_output_set_property(pa_source_output *o, const char *key, const char *value); +void pa_source_output_set_property_arbitrary(pa_source_output *o, const char *key, const uint8_t *value, size_t nbytes); void pa_source_output_update_proplist(pa_source_output *o, pa_update_mode_t mode, pa_proplist *p); pa_resample_method_t pa_source_output_get_resample_method(pa_source_output *o); -- cgit v1.2.1