[pulseaudio-discuss] [PATCH 3/6] stream: Implement a pa_stream_get_volume() API

arun at accosted.net arun at accosted.net
Mon Dec 28 19:33:42 PST 2015


From: Arun Raghavan <git at arunraghavan.net>

To allow the API to be synchronous, we keep a copy of the current volume
on the stream at all times.
---
 PROTOCOL                        | 16 ++++++++++++
 configure.ac                    |  2 +-
 src/map-file                    |  1 +
 src/pulse/context.c             |  2 ++
 src/pulse/internal.h            |  1 +
 src/pulse/stream.c              | 55 +++++++++++++++++++++++++++++++++++++++++
 src/pulse/stream.h              |  7 ++++++
 src/pulsecore/native-common.h   |  5 ++++
 src/pulsecore/protocol-native.c | 54 ++++++++++++++++++++++++++++++++++++++++
 src/tests/api-test.c            |  3 +++
 10 files changed, 145 insertions(+), 1 deletion(-)

diff --git a/PROTOCOL b/PROTOCOL
index 3c08fea..45e3c96 100644
--- a/PROTOCOL
+++ b/PROTOCOL
@@ -371,6 +371,22 @@ 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 >= 8.0
+
+Added return values to PA_COMMAND_CREATE_PLAYBACK_STREAM and
+PA_COMMAND_CREATE_RECORD_STREAM to provide the stream volume after creation.
+
+    cvolume
+
+A couple of new server->client messages to allow clients to track stream volume
+changes.
+
+PA_COMMAND_PLAYBACK_STREAM_VOLUME_CHANGED
+PA_COMMAND_RECORD_STREAM_VOLUME_CHANGED
+
+    index
+    cvolume
+
 #### 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 2dbf7f2..a4e3c21 100644
--- a/configure.ac
+++ b/configure.ac
@@ -40,7 +40,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/map-file b/src/map-file
index 93f7178..8e61f53 100644
--- a/src/map-file
+++ b/src/map-file
@@ -302,6 +302,7 @@ pa_stream_get_state;
 pa_stream_get_time;
 pa_stream_get_timing_info;
 pa_stream_get_underflow_index;
+pa_stream_get_volume;
 pa_stream_is_corked;
 pa_stream_is_suspended;
 pa_stream_new;
diff --git a/src/pulse/context.c b/src/pulse/context.c
index 4f084e8..63a0fdb 100644
--- a/src/pulse/context.c
+++ b/src/pulse/context.c
@@ -90,6 +90,8 @@ static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {
     [PA_COMMAND_RECORD_BUFFER_ATTR_CHANGED] = pa_command_stream_buffer_attr,
     [PA_COMMAND_ENABLE_SRBCHANNEL] = pa_command_enable_srbchannel,
     [PA_COMMAND_DISABLE_SRBCHANNEL] = pa_command_disable_srbchannel,
+    [PA_COMMAND_PLAYBACK_STREAM_VOLUME_CHANGED] = pa_command_stream_volume_changed,
+    [PA_COMMAND_RECORD_STREAM_VOLUME_CHANGED] = pa_command_stream_volume_changed,
 };
 static void context_free(pa_context *c);
 
diff --git a/src/pulse/internal.h b/src/pulse/internal.h
index 74b2b32..ac9d07e 100644
--- a/src/pulse/internal.h
+++ b/src/pulse/internal.h
@@ -254,6 +254,7 @@ void pa_command_stream_started(pa_pdispatch *pd, uint32_t command, uint32_t tag,
 void pa_command_stream_event(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
 void pa_command_client_event(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
 void pa_command_stream_buffer_attr(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+void pa_command_stream_volume_changed(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
 
 pa_operation *pa_operation_new(pa_context *c, pa_stream *s, pa_operation_cb_t callback, void *userdata);
 void pa_operation_done(pa_operation *o);
diff --git a/src/pulse/stream.c b/src/pulse/stream.c
index 0f5dd73..bc49cbb 100644
--- a/src/pulse/stream.c
+++ b/src/pulse/stream.c
@@ -914,6 +914,42 @@ finish:
     pa_context_unref(c);
 }
 
+void pa_command_stream_volume_changed(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_context *c = userdata;
+    pa_stream *s;
+    uint32_t index;
+    pa_cvolume volume;
+
+    pa_assert(pd);
+    pa_assert(command == PA_COMMAND_PLAYBACK_STREAM_VOLUME_CHANGED || command == PA_COMMAND_RECORD_STREAM_VOLUME_CHANGED);
+    pa_assert(t);
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    pa_context_ref(c);
+
+    if (c->version < 31) {
+        pa_context_fail(c, PA_ERR_PROTOCOL);
+        goto finish;
+    }
+
+    if (pa_tagstruct_getu32(t, &index) < 0 ||
+        pa_tagstruct_get_cvolume(t, &volume) < 0 ||
+        !pa_tagstruct_eof(t)) {
+        pa_context_fail(c, PA_ERR_PROTOCOL);
+        goto finish;
+    }
+
+    if (!(s = pa_hashmap_get(command == PA_COMMAND_PLAYBACK_STREAM_VOLUME_CHANGED ? c->playback_streams : c->record_streams,
+                    PA_UINT32_TO_PTR(index))))
+        goto finish;
+
+    s->volume = volume;
+
+finish:
+    pa_context_unref(c);
+}
+
 static void invalidate_indexes(pa_stream *s, bool r, bool w) {
     pa_assert(s);
     pa_assert(PA_REFCNT_VALUE(s) >= 1);
@@ -1154,6 +1190,9 @@ void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag,
         }
     }
 
+    if (s->context->version >= 31)
+        pa_tagstruct_get_cvolume(t, &s->volume);
+
     if (!pa_tagstruct_eof(t)) {
         pa_context_fail(s->context, PA_ERR_PROTOCOL);
         goto finish;
@@ -2968,3 +3007,19 @@ int pa_stream_set_volume(pa_stream *s, pa_cvolume *v, pa_stream_success_cb_t cb,
 
     return 0;
 }
+
+int pa_stream_get_volume(pa_stream *s, pa_cvolume *v) {
+    pa_assert(s);
+    pa_assert(v);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    PA_CHECK_VALIDITY(s->context, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY(s->context, s->state != PA_STREAM_FAILED && s->state != PA_STREAM_TERMINATED, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY || s->volume_set, PA_ERR_NODATA);
+    PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_INVALID); /* TODO: do we want to support this? */
+    PA_CHECK_VALIDITY(s->context, s->context->version >= 31, PA_ERR_NOTSUPPORTED);
+
+    *v = s->volume;
+
+    return 0;
+}
diff --git a/src/pulse/stream.h b/src/pulse/stream.h
index cc74629..b4e6ff9 100644
--- a/src/pulse/stream.h
+++ b/src/pulse/stream.h
@@ -828,6 +828,13 @@ uint32_t pa_stream_get_monitor_stream(pa_stream *s);
  * \since 9.0 */
 int pa_stream_set_volume(pa_stream *s, pa_cvolume *v, pa_stream_success_cb_t cb, void *userdata);
 
+/** Get the volume on the given stream.
+ *
+ * Returns 0 on success, negative error value on failure.
+ *
+ * \since 9.0 */
+int pa_stream_get_volume(pa_stream *s, pa_cvolume *v);
+
 PA_C_DECL_END
 
 #endif
diff --git a/src/pulsecore/native-common.h b/src/pulsecore/native-common.h
index dc62895..73aded1 100644
--- a/src/pulsecore/native-common.h
+++ b/src/pulsecore/native-common.h
@@ -179,6 +179,11 @@ enum {
     PA_COMMAND_ENABLE_SRBCHANNEL,
     PA_COMMAND_DISABLE_SRBCHANNEL,
 
+    /* Supported since protocol v31 (8.0) */
+    /* SERVER->CLIENT */
+    PA_COMMAND_PLAYBACK_STREAM_VOLUME_CHANGED,
+    PA_COMMAND_RECORD_STREAM_VOLUME_CHANGED,
+
     PA_COMMAND_MAX
 };
 
diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c
index 145db04..69264f6 100644
--- a/src/pulsecore/protocol-native.c
+++ b/src/pulsecore/protocol-native.c
@@ -239,6 +239,7 @@ static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes);
 static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes);
 static void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes);
 static void sink_input_send_event_cb(pa_sink_input *i, const char *event, pa_proplist *pl);
+static void sink_input_volume_changed_cb(pa_sink_input *o);
 
 static void native_connection_send_memblock(pa_native_connection *c);
 static void playback_stream_request_bytes(struct playback_stream*s);
@@ -249,6 +250,7 @@ static void source_output_suspend_cb(pa_source_output *o, bool suspend);
 static void source_output_moving_cb(pa_source_output *o, pa_source *dest);
 static pa_usec_t source_output_get_latency_cb(pa_source_output *o);
 static void source_output_send_event_cb(pa_source_output *o, const char *event, pa_proplist *pl);
+static void source_output_volume_changed_cb(pa_source_output *o);
 
 static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk);
 static int source_output_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk);
@@ -719,6 +721,7 @@ static record_stream* record_stream_new(
     s->source_output->moving = source_output_moving_cb;
     s->source_output->suspend = source_output_suspend_cb;
     s->source_output->send_event = source_output_send_event_cb;
+    s->source_output->volume_changed = source_output_volume_changed_cb;
     s->source_output->userdata = s;
 
     fix_record_buffer_attr_pre(s);
@@ -1201,6 +1204,7 @@ static playback_stream* playback_stream_new(
     s->sink_input->moving = sink_input_moving_cb;
     s->sink_input->suspend = sink_input_suspend_cb;
     s->sink_input->send_event = sink_input_send_event_cb;
+    s->sink_input->volume_changed = sink_input_volume_changed_cb;
     s->sink_input->userdata = s;
 
     start_index = ssync ? pa_memblockq_get_read_index(ssync->memblockq) : 0;
@@ -1815,6 +1819,34 @@ static void sink_input_moving_cb(pa_sink_input *i, pa_sink *dest) {
     pa_pstream_send_tagstruct(s->connection->pstream, t);
 }
 
+static void send_volume_changed(pa_pstream *pstream, bool playback, int32_t index, pa_cvolume *volume) {
+    pa_tagstruct *t;
+
+    t = pa_tagstruct_new();
+    pa_tagstruct_putu32(t, playback ? PA_COMMAND_PLAYBACK_STREAM_VOLUME_CHANGED : PA_COMMAND_RECORD_STREAM_VOLUME_CHANGED);
+    pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
+    pa_tagstruct_putu32(t, index);
+    pa_tagstruct_put_cvolume(t, volume);
+
+    pa_pstream_send_tagstruct(pstream, t);
+}
+
+/* Called from main context */
+static void sink_input_volume_changed_cb(pa_sink_input *i) {
+    playback_stream *s;
+
+    pa_sink_input_assert_ref(i);
+    s = PLAYBACK_STREAM(i->userdata);
+    playback_stream_assert_ref(s);
+
+    if (s->connection->version < 31) {
+        pa_log_debug("Skipping volume update because client protocol version is old");
+        return;
+    }
+
+    send_volume_changed(s->connection->pstream, true, s->index, &i->volume);
+}
+
 /*** source_output callbacks ***/
 
 /* Called from thread context */
@@ -1952,6 +1984,22 @@ static void source_output_moving_cb(pa_source_output *o, pa_source *dest) {
     pa_pstream_send_tagstruct(s->connection->pstream, t);
 }
 
+/* Called from main context */
+static void source_output_volume_changed_cb(pa_source_output *o) {
+    record_stream *s;
+
+    pa_source_output_assert_ref(o);
+    s = RECORD_STREAM(o->userdata);
+    record_stream_assert_ref(s);
+
+    if (s->connection->version < 31) {
+        pa_log_debug("Skipping volume update because client protocol version is old");
+        return;
+    }
+
+    send_volume_changed(s->connection->pstream, false, s->index, &o->volume);
+}
+
 /*** pdispatch callbacks ***/
 
 static void protocol_error(pa_native_connection *c) {
@@ -2240,6 +2288,9 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
         }
     }
 
+    if (c->version >= 31)
+        pa_tagstruct_put_cvolume(reply, &s->sink_input->volume);
+
     pa_pstream_send_tagstruct(c->pstream, reply);
 
 finish:
@@ -2554,6 +2605,9 @@ static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uin
         }
     }
 
+    if (c->version >= 31)
+        pa_tagstruct_put_cvolume(reply, &s->source_output->volume);
+
     pa_pstream_send_tagstruct(c->pstream, reply);
 
 finish:
diff --git a/src/tests/api-test.c b/src/tests/api-test.c
index 5d75607..e7ae3df 100644
--- a/src/tests/api-test.c
+++ b/src/tests/api-test.c
@@ -63,6 +63,9 @@ static void stream_state_callback(pa_stream *s, void *userdata) {
             break;
 
         case PA_STREAM_READY:
+            fail_unless(pa_stream_get_volume(s, &v) == 0);
+            fail_unless(pa_cvolume_avg(&v) == (PA_VOLUME_NORM / 2));
+
             pa_cvolume_set(&v, CHANNELS, PA_VOLUME_NORM / 3);
             fail_unless(pa_stream_set_volume(s, &v, stream_volume_callback, PA_UINT_TO_PTR(true)) == 0);
             break;
-- 
2.5.0



More information about the pulseaudio-discuss mailing list