[pulseaudio-discuss] [PATCH] core: Add a per-stream flat-volumes flag
Arun Raghavan
arun at accosted.net
Tue Aug 5 11:54:43 PDT 2014
This is not expected to be used by most clients - the default
system-wide setting is preferred to provide a consistent user
experience.
However certain clients (at the moment, this is just web browsers), need
to be able to disable clients from modifying system-wide volume, so
this provides such a mechanism.
---
PROTOCOL | 6 ++++++
configure.ac | 2 +-
src/modules/module-tunnel.c | 5 +++++
src/pulse/def.h | 9 ++++++++-
src/pulse/stream.c | 11 ++++++++++-
src/pulsecore/cli-text.c | 3 ++-
src/pulsecore/protocol-native.c | 14 ++++++++++++--
src/pulsecore/sink-input.c | 22 +++++++++++++++-------
src/pulsecore/sink-input.h | 4 +++-
src/pulsecore/sink.c | 18 ++++++++++++++++++
10 files changed, 80 insertions(+), 14 deletions(-)
diff --git a/PROTOCOL b/PROTOCOL
index 3c08fea..3fba5eb 100644
--- a/PROTOCOL
+++ b/PROTOCOL
@@ -371,6 +371,12 @@ PA_COMMAND_DISABLE_SRBCHANNEL
Tells the client to stop listening on the additional SHM ringbuffer channel.
Acked by client by sending PA_COMMAND_DISABLE_SRBCHANNEL back.
+## v31, implemented by >= 6.0
+
+new flag at end of CREATE_PLAYBACK_STREAM:
+
+ bool no_flat_volume
+
#### If you just changed the protocol, read this
## module-tunnel depends on the sink/source/sink-input/source-input protocol
## internals, so if you changed these, you might have broken module-tunnel.
diff --git a/configure.ac b/configure.ac
index 837e81e..255212e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -41,7 +41,7 @@ AC_SUBST(PA_MINOR, pa_minor)
AC_SUBST(PA_MAJORMINOR, pa_major.pa_minor)
AC_SUBST(PA_API_VERSION, 12)
-AC_SUBST(PA_PROTOCOL_VERSION, 30)
+AC_SUBST(PA_PROTOCOL_VERSION, 31)
# The stable ABI for client applications, for the version info x:y:z
# always will hold y=z
diff --git a/src/modules/module-tunnel.c b/src/modules/module-tunnel.c
index 193d091..bd45702 100644
--- a/src/modules/module-tunnel.c
+++ b/src/modules/module-tunnel.c
@@ -1757,6 +1757,11 @@ static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t
}
#endif
+#ifdef TUNNEL_SINK
+ if (u->version >= 31)
+ pa_tagstruct_put_boolean(reply, false); /* allow flat-volumes on this stream */
+#endif
+
pa_pstream_send_tagstruct(u->pstream, reply);
pa_pdispatch_register_reply(u->pdispatch, tag, DEFAULT_TIMEOUT, create_stream_callback, u, NULL);
diff --git a/src/pulse/def.h b/src/pulse/def.h
index dfc0c10..d0a37f8 100644
--- a/src/pulse/def.h
+++ b/src/pulse/def.h
@@ -349,11 +349,17 @@ typedef enum pa_stream_flags {
* consider absolute when the sink is in flat volume mode,
* relative otherwise. \since 0.9.20 */
- PA_STREAM_PASSTHROUGH = 0x80000U
+ PA_STREAM_PASSTHROUGH = 0x80000U,
/**< Used to tag content that will be rendered by passthrough sinks.
* The data will be left as is and not reformatted, resampled.
* \since 1.0 */
+ PA_STREAM_NO_FLAT_VOLUME = 0x100000U,
+ /**< Used to disable "flat-volumes" behaviour on an individual stream.
+ * Changes to this stream's volume will not affect device volume, even
+ * if the flat-volumes mode is enabled on the server.
+ * \since 6.0 */
+
} pa_stream_flags_t;
/** \cond fulldocs */
@@ -382,6 +388,7 @@ typedef enum pa_stream_flags {
#define PA_STREAM_FAIL_ON_SUSPEND PA_STREAM_FAIL_ON_SUSPEND
#define PA_STREAM_RELATIVE_VOLUME PA_STREAM_RELATIVE_VOLUME
#define PA_STREAM_PASSTHROUGH PA_STREAM_PASSTHROUGH
+#define PA_STREAM_NO_FLAT_VOLUME PA_STREAM_NO_FLAT_VOLUME
/** \endcond */
diff --git a/src/pulse/stream.c b/src/pulse/stream.c
index 8e35c29..ba5e7fb 100644
--- a/src/pulse/stream.c
+++ b/src/pulse/stream.c
@@ -1213,7 +1213,8 @@ static int create_stream(
PA_STREAM_START_UNMUTED|
PA_STREAM_FAIL_ON_SUSPEND|
PA_STREAM_RELATIVE_VOLUME|
- PA_STREAM_PASSTHROUGH)), PA_ERR_INVALID);
+ PA_STREAM_PASSTHROUGH|
+ PA_STREAM_NO_FLAT_VOLUME)), PA_ERR_INVALID);
PA_CHECK_VALIDITY(s->context, s->context->version >= 12 || !(flags & PA_STREAM_VARIABLE_RATE), PA_ERR_NOTSUPPORTED);
PA_CHECK_VALIDITY(s->context, s->context->version >= 13 || !(flags & PA_STREAM_PEAK_DETECT), PA_ERR_NOTSUPPORTED);
@@ -1372,6 +1373,9 @@ static int create_stream(
pa_tagstruct_put_boolean(t, flags & (PA_STREAM_PASSTHROUGH));
}
+ if (s->context->version >= 31 && s->direction == PA_STREAM_PLAYBACK)
+ pa_tagstruct_put_boolean(t, flags & (PA_STREAM_NO_FLAT_VOLUME));
+
pa_pstream_send_tagstruct(s->context->pstream, t);
pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_create_stream_callback, s, NULL);
@@ -1389,9 +1393,14 @@ int pa_stream_connect_playback(
const pa_cvolume *volume,
pa_stream *sync_stream) {
+ char *e;
+
pa_assert(s);
pa_assert(PA_REFCNT_VALUE(s) >= 1);
+ if ((e = getenv("PULSE_DISABLE_FLAT_VOLUME")))
+ flags |= PA_STREAM_NO_FLAT_VOLUME;
+
return create_stream(PA_STREAM_PLAYBACK, s, dev, attr, flags, volume, sync_stream);
}
diff --git a/src/pulsecore/cli-text.c b/src/pulsecore/cli-text.c
index 2992ae8..00226cd 100644
--- a/src/pulsecore/cli-text.c
+++ b/src/pulsecore/cli-text.c
@@ -607,7 +607,7 @@ char *pa_sink_input_list_to_string(pa_core *c) {
s,
" index: %u\n"
"\tdriver: <%s>\n"
- "\tflags: %s%s%s%s%s%s%s%s%s%s%s%s\n"
+ "\tflags: %s%s%s%s%s%s%s%s%s%s%s%s%s\n"
"\tstate: %s\n"
"\tsink: %u <%s>\n"
"\tvolume: %s\n"
@@ -631,6 +631,7 @@ char *pa_sink_input_list_to_string(pa_core *c) {
i->flags & PA_SINK_INPUT_NO_CREATE_ON_SUSPEND ? "NO_CREATE_SUSPEND " : "",
i->flags & PA_SINK_INPUT_KILL_ON_SUSPEND ? "KILL_ON_SUSPEND " : "",
i->flags & PA_SINK_INPUT_PASSTHROUGH ? "PASSTHROUGH " : "",
+ i->flags & PA_SINK_INPUT_NO_FLAT_VOLUME ? "NO_FLAT_VOLUME " : "",
state_table[pa_sink_input_get_state(i)],
i->sink->index, i->sink->name,
volume_str,
diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c
index 5f2c35d..df45a99 100644
--- a/src/pulsecore/protocol-native.c
+++ b/src/pulsecore/protocol-native.c
@@ -2011,7 +2011,8 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
muted_set = false,
fail_on_suspend = false,
relative_volume = false,
- passthrough = false;
+ passthrough = false,
+ no_flat_volume = false;
pa_sink_input_flags_t flags = 0;
pa_proplist *p = NULL;
@@ -2151,6 +2152,14 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
}
}
+ if (c->version >= 31) {
+
+ if (pa_tagstruct_get_boolean(t, &no_flat_volume) < 0 ) {
+ protocol_error(c);
+ goto finish;
+ }
+ }
+
if (!pa_tagstruct_eof(t)) {
protocol_error(c);
goto finish;
@@ -2182,7 +2191,8 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
(variable_rate ? PA_SINK_INPUT_VARIABLE_RATE : 0) |
(dont_inhibit_auto_suspend ? PA_SINK_INPUT_DONT_INHIBIT_AUTO_SUSPEND : 0) |
(fail_on_suspend ? PA_SINK_INPUT_NO_CREATE_ON_SUSPEND|PA_SINK_INPUT_KILL_ON_SUSPEND : 0) |
- (passthrough ? PA_SINK_INPUT_PASSTHROUGH : 0);
+ (passthrough ? PA_SINK_INPUT_PASSTHROUGH : 0) |
+ (no_flat_volume ? PA_SINK_INPUT_NO_FLAT_VOLUME : 0);
/* Only since protocol version 15 there's a separate muted_set
* flag. For older versions we synthesize it here */
diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c
index 6169d47..81dcea3 100644
--- a/src/pulsecore/sink-input.c
+++ b/src/pulsecore/sink-input.c
@@ -1240,7 +1240,7 @@ void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume, bool s
pa_assert(volume->channels == 1 || pa_cvolume_compatible(volume, &i->sample_spec));
pa_assert(i->volume_writable);
- if (!absolute && pa_sink_flat_volume_enabled(i->sink)) {
+ if (!absolute && pa_sink_input_is_flat_volume(i)) {
v = i->sink->reference_volume;
pa_cvolume_remap(&v, &i->sink->channel_map, &i->channel_map);
@@ -1263,7 +1263,7 @@ void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume, bool s
pa_sink_input_set_volume_direct(i, volume);
i->save_volume = save;
- if (pa_sink_flat_volume_enabled(i->sink)) {
+ if (pa_sink_input_is_flat_volume(i)) {
/* We are in flat volume mode, so let's update all sink input
* volumes and update the flat volume of the sink */
@@ -1383,13 +1383,21 @@ bool pa_sink_input_is_volume_readable(pa_sink_input *i) {
}
/* Called from main context */
+bool pa_sink_input_is_flat_volume(pa_sink_input *i) {
+ pa_sink_input_assert_ref(i);
+ pa_assert_ctl_context();
+
+ return PA_LIKELY(((i->flags & PA_SINK_INPUT_NO_FLAT_VOLUME) == 0) && pa_sink_flat_volume_enabled(i->sink));
+}
+
+/* Called from main context */
pa_cvolume *pa_sink_input_get_volume(pa_sink_input *i, pa_cvolume *volume, bool absolute) {
pa_sink_input_assert_ref(i);
pa_assert_ctl_context();
pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
pa_assert(pa_sink_input_is_volume_readable(i));
- if (absolute || !pa_sink_flat_volume_enabled(i->sink))
+ if (absolute || !pa_sink_input_is_flat_volume(i))
*volume = i->volume;
else
*volume = i->reference_ratio;
@@ -1597,7 +1605,7 @@ int pa_sink_input_start_move(pa_sink_input *i) {
if (pa_sink_input_is_passthrough(i))
pa_sink_leave_passthrough(i->sink);
- if (pa_sink_flat_volume_enabled(i->sink))
+ if (pa_sink_input_is_flat_volume(i))
/* We might need to update the sink's volume if we are in flat
* volume mode. */
pa_sink_set_volume(i->sink, NULL, false, false);
@@ -1636,7 +1644,7 @@ static void update_volume_due_to_moving(pa_sink_input *i, pa_sink *dest) {
if (PA_UNLIKELY(!root_sink))
return;
- if (pa_sink_flat_volume_enabled(i->sink)) {
+ if (pa_sink_input_is_flat_volume(i)) {
/* Ok, so the origin sink uses volume sharing, and flat volume is
* enabled. The volume will have to be updated as follows:
*
@@ -1707,7 +1715,7 @@ static void update_volume_due_to_moving(pa_sink_input *i, pa_sink *dest) {
update_volume_due_to_moving(origin_sink_input, dest);
} else {
- if (pa_sink_flat_volume_enabled(i->sink)) {
+ if (pa_sink_input_is_flat_volume(i)) {
/* Ok, so this is a regular stream, and flat volume is enabled. The
* volume will have to be updated as follows:
*
@@ -1740,7 +1748,7 @@ static void update_volume_due_to_moving(pa_sink_input *i, pa_sink *dest) {
/* If i->sink == dest, then recursion has finished, and we can finally call
* pa_sink_set_volume(), which will do the rest of the updates. */
- if ((i->sink == dest) && pa_sink_flat_volume_enabled(i->sink))
+ if ((i->sink == dest) && pa_sink_input_is_flat_volume(i))
pa_sink_set_volume(i->sink, NULL, false, i->save_volume);
}
diff --git a/src/pulsecore/sink-input.h b/src/pulsecore/sink-input.h
index a48476c..e840ae6 100644
--- a/src/pulsecore/sink-input.h
+++ b/src/pulsecore/sink-input.h
@@ -61,7 +61,8 @@ typedef enum pa_sink_input_flags {
PA_SINK_INPUT_DONT_INHIBIT_AUTO_SUSPEND = 256,
PA_SINK_INPUT_NO_CREATE_ON_SUSPEND = 512,
PA_SINK_INPUT_KILL_ON_SUSPEND = 1024,
- PA_SINK_INPUT_PASSTHROUGH = 2048
+ PA_SINK_INPUT_PASSTHROUGH = 2048,
+ PA_SINK_INPUT_NO_FLAT_VOLUME = 4096,
} pa_sink_input_flags_t;
struct pa_sink_input {
@@ -369,6 +370,7 @@ pa_usec_t pa_sink_input_get_latency(pa_sink_input *i, pa_usec_t *sink_latency);
bool pa_sink_input_is_passthrough(pa_sink_input *i);
bool pa_sink_input_is_volume_readable(pa_sink_input *i);
+bool pa_sink_input_is_flat_volume(pa_sink_input *i);
void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume, bool save, bool absolute);
void pa_sink_input_add_volume_factor(pa_sink_input *i, const char *key, const pa_cvolume *volume_factor);
int pa_sink_input_remove_volume_factor(pa_sink_input *i, const char *key);
diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
index ccf6ea1..f7716c9 100644
--- a/src/pulsecore/sink.c
+++ b/src/pulsecore/sink.c
@@ -1656,6 +1656,9 @@ static void compute_reference_ratios(pa_sink *s) {
pa_assert(pa_sink_flat_volume_enabled(s));
PA_IDXSET_FOREACH(i, s->inputs, idx) {
+ if (!pa_sink_input_is_flat_volume(i))
+ continue;
+
compute_reference_ratio(i);
if (i->origin_sink && (i->origin_sink->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER))
@@ -1678,6 +1681,9 @@ static void compute_real_ratios(pa_sink *s) {
unsigned c;
pa_cvolume remapped;
+ if (!pa_sink_input_is_flat_volume(i))
+ continue;
+
if (i->origin_sink && (i->origin_sink->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER)) {
/* The origin sink uses volume sharing, so this input's real ratio
* is handled as a special case - the real ratio must be 0 dB, and
@@ -1783,6 +1789,9 @@ static void get_maximum_input_volume(pa_sink *s, pa_cvolume *max_volume, const p
PA_IDXSET_FOREACH(i, s->inputs, idx) {
pa_cvolume remapped;
+ if (!pa_sink_input_is_flat_volume(i))
+ continue;
+
if (i->origin_sink && (i->origin_sink->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER)) {
get_maximum_input_volume(i->origin_sink, max_volume, channel_map);
@@ -1829,6 +1838,9 @@ static void update_real_volume(pa_sink *s, const pa_cvolume *new_volume, pa_chan
pa_cvolume_remap(&s->real_volume, channel_map, &s->channel_map);
PA_IDXSET_FOREACH(i, s->inputs, idx) {
+ if (!pa_sink_input_is_flat_volume(i))
+ continue;
+
if (i->origin_sink && (i->origin_sink->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER)) {
if (pa_sink_flat_volume_enabled(s)) {
pa_cvolume new_input_volume;
@@ -1894,6 +1906,9 @@ static void propagate_reference_volume(pa_sink *s) {
PA_IDXSET_FOREACH(i, s->inputs, idx) {
pa_cvolume new_volume;
+ if (!pa_sink_input_is_flat_volume(i))
+ continue;
+
if (i->origin_sink && (i->origin_sink->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER)) {
propagate_reference_volume(i->origin_sink);
@@ -2107,6 +2122,9 @@ static void propagate_real_volume(pa_sink *s, const pa_cvolume *old_real_volume)
PA_IDXSET_FOREACH(i, s->inputs, idx) {
pa_cvolume new_volume;
+ if (!pa_sink_input_is_flat_volume(i))
+ continue;
+
/* 2. Since the sink's reference and real volumes are equal
* now our ratios should be too. */
i->reference_ratio = i->real_ratio;
--
1.9.3
More information about the pulseaudio-discuss
mailing list