[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