[pulseaudio-discuss] [PATCH 6/6] stream: Add a pa_stream_get_mute() API

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


From: Arun Raghavan <git at arunraghavan.net>

As with the corresponding volume API, this keeps track of the mute
status as it changes on the server side. This also makes the volume
change callback on streams be emitted when mute status changes.
---
 PROTOCOL                        |   2 +
 src/map-file                    |   1 +
 src/pulse/stream.c              |  24 +++++++-
 src/pulse/stream.h              |  11 +++-
 src/pulsecore/protocol-native.c |  17 ++++--
 src/tests/api-test.c            | 120 ++++++++++++++++++++++++++++++++++------
 6 files changed, 151 insertions(+), 24 deletions(-)

diff --git a/PROTOCOL b/PROTOCOL
index 45e3c96..e497c2d 100644
--- a/PROTOCOL
+++ b/PROTOCOL
@@ -377,6 +377,7 @@ Added return values to PA_COMMAND_CREATE_PLAYBACK_STREAM and
 PA_COMMAND_CREATE_RECORD_STREAM to provide the stream volume after creation.
 
     cvolume
+    mute
 
 A couple of new server->client messages to allow clients to track stream volume
 changes.
@@ -386,6 +387,7 @@ PA_COMMAND_RECORD_STREAM_VOLUME_CHANGED
 
     index
     cvolume
+    mute
 
 #### If you just changed the protocol, read this
 ## module-tunnel depends on the sink/source/sink-input/source-input protocol
diff --git a/src/map-file b/src/map-file
index 17e7f6b..b29ad7f 100644
--- a/src/map-file
+++ b/src/map-file
@@ -297,6 +297,7 @@ pa_stream_get_format_info;
 pa_stream_get_index;
 pa_stream_get_latency;
 pa_stream_get_monitor_stream;
+pa_stream_get_mute;
 pa_stream_get_sample_spec;
 pa_stream_get_state;
 pa_stream_get_time;
diff --git a/src/pulse/stream.c b/src/pulse/stream.c
index 934a747..73b425e 100644
--- a/src/pulse/stream.c
+++ b/src/pulse/stream.c
@@ -921,6 +921,7 @@ void pa_command_stream_volume_changed(pa_pdispatch *pd, uint32_t command, uint32
     pa_stream *s;
     uint32_t index;
     pa_cvolume volume;
+    bool mute;
 
     pa_assert(pd);
     pa_assert(command == PA_COMMAND_PLAYBACK_STREAM_VOLUME_CHANGED || command == PA_COMMAND_RECORD_STREAM_VOLUME_CHANGED);
@@ -937,6 +938,7 @@ void pa_command_stream_volume_changed(pa_pdispatch *pd, uint32_t command, uint32
 
     if (pa_tagstruct_getu32(t, &index) < 0 ||
         pa_tagstruct_get_cvolume(t, &volume) < 0 ||
+        pa_tagstruct_get_boolean(t, &mute) < 0 ||
         !pa_tagstruct_eof(t)) {
         pa_context_fail(c, PA_ERR_PROTOCOL);
         goto finish;
@@ -947,6 +949,7 @@ void pa_command_stream_volume_changed(pa_pdispatch *pd, uint32_t command, uint32
         goto finish;
 
     s->volume = volume;
+    s->mute = mute;
 
     if (s->volume_changed_callback)
         s->volume_changed_callback(s, s->volume_changed_userdata);
@@ -1195,8 +1198,10 @@ void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag,
         }
     }
 
-    if (s->context->version >= 31)
+    if (s->context->version >= 31) {
         pa_tagstruct_get_cvolume(t, &s->volume);
+        pa_tagstruct_get_boolean(t, &s->mute);
+    }
 
     if (!pa_tagstruct_eof(t)) {
         pa_context_fail(s->context, PA_ERR_PROTOCOL);
@@ -3057,6 +3062,23 @@ int pa_stream_get_volume(pa_stream *s, pa_cvolume *v) {
     return 0;
 }
 
+int pa_stream_get_mute(pa_stream *s, int *mute) {
+    pa_assert(s);
+    pa_assert(mute);
+    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_READY, PA_ERR_BADSTATE);
+    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, 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);
+
+    *mute = s->mute;
+
+    return 0;
+}
+
 void pa_stream_set_volume_changed_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata) {
     pa_assert(s);
     pa_assert(PA_REFCNT_VALUE(s) >= 1);
diff --git a/src/pulse/stream.h b/src/pulse/stream.h
index 7adebe0..98706f9 100644
--- a/src/pulse/stream.h
+++ b/src/pulse/stream.h
@@ -844,8 +844,15 @@ int pa_stream_set_mute(pa_stream *s, int mute, pa_stream_success_cb_t cb, void *
  * \since 9.0 */
 int pa_stream_get_volume(pa_stream *s, pa_cvolume *v);
 
-/** Set the callback function that is called whenever the volume of the stream
- * changes. \since 9.0 */
+/** Get the mute status on the given stream.
+ *
+ * Returns 0 on success, negative error value on failure.
+ *
+ * \since 9.0 */
+int pa_stream_get_mute(pa_stream *s, int *mute);
+
+/** Set the callback function that is called whenever the volume or mute status
+ * of the stream changes. \since 9.0 */
 void pa_stream_set_volume_changed_callback(pa_stream *p, pa_stream_notify_cb_t cb, void *userdata);
 
 PA_C_DECL_END
diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c
index 69264f6..8f5f63e 100644
--- a/src/pulsecore/protocol-native.c
+++ b/src/pulsecore/protocol-native.c
@@ -722,6 +722,7 @@ static record_stream* record_stream_new(
     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->mute_changed = source_output_volume_changed_cb;
     s->source_output->userdata = s;
 
     fix_record_buffer_attr_pre(s);
@@ -1205,6 +1206,7 @@ static playback_stream* playback_stream_new(
     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->mute_changed = sink_input_volume_changed_cb;
     s->sink_input->userdata = s;
 
     start_index = ssync ? pa_memblockq_get_read_index(ssync->memblockq) : 0;
@@ -1819,7 +1821,7 @@ 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) {
+static void send_volume_changed(pa_pstream *pstream, bool playback, int32_t index, pa_cvolume *volume, bool mute) {
     pa_tagstruct *t;
 
     t = pa_tagstruct_new();
@@ -1827,6 +1829,7 @@ static void send_volume_changed(pa_pstream *pstream, bool playback, int32_t inde
     pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
     pa_tagstruct_putu32(t, index);
     pa_tagstruct_put_cvolume(t, volume);
+    pa_tagstruct_put_boolean(t, mute);
 
     pa_pstream_send_tagstruct(pstream, t);
 }
@@ -1844,7 +1847,7 @@ static void sink_input_volume_changed_cb(pa_sink_input *i) {
         return;
     }
 
-    send_volume_changed(s->connection->pstream, true, s->index, &i->volume);
+    send_volume_changed(s->connection->pstream, true, s->index, &i->volume, i->muted);
 }
 
 /*** source_output callbacks ***/
@@ -1997,7 +2000,7 @@ static void source_output_volume_changed_cb(pa_source_output *o) {
         return;
     }
 
-    send_volume_changed(s->connection->pstream, false, s->index, &o->volume);
+    send_volume_changed(s->connection->pstream, false, s->index, &o->volume, o->muted);
 }
 
 /*** pdispatch callbacks ***/
@@ -2288,8 +2291,10 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
         }
     }
 
-    if (c->version >= 31)
+    if (c->version >= 31) {
         pa_tagstruct_put_cvolume(reply, &s->sink_input->volume);
+        pa_tagstruct_put_boolean(reply, s->sink_input->muted);
+    }
 
     pa_pstream_send_tagstruct(c->pstream, reply);
 
@@ -2605,8 +2610,10 @@ static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uin
         }
     }
 
-    if (c->version >= 31)
+    if (c->version >= 31) {
         pa_tagstruct_put_cvolume(reply, &s->source_output->volume);
+        pa_tagstruct_put_boolean(reply, s->source_output->muted);
+    }
 
     pa_pstream_send_tagstruct(c->pstream, reply);
 
diff --git a/src/tests/api-test.c b/src/tests/api-test.c
index c88d00b..c43d000 100644
--- a/src/tests/api-test.c
+++ b/src/tests/api-test.c
@@ -32,11 +32,40 @@
 
 #define CHANNELS 2
 
+typedef struct {
+    bool playback;
+    bool mute;
+    int mute_val;
+} api_test_context;
+
+static void set_stream_mute_callback(pa_stream *s, int success, void *userdata) {
+    int mute;
+    api_test_context *u = (api_test_context *) userdata;
+
+    fail_unless(pa_stream_get_mute(s, &mute) == 0);
+    fail_unless(mute == !u->mute_val);
+
+    pa_stream_disconnect(s);
+}
+
 static void volume_changed_callback(pa_stream *s, void *userdata) {
     pa_cvolume v;
+    int mute;
+    api_test_context *u = (api_test_context *) userdata;
 
-    fail_unless(pa_stream_get_volume(s, &v) == 0);
-    fail_unless(pa_cvolume_avg(&v) == (PA_VOLUME_NORM / 3));
+    if (u->mute) {
+        fail_unless(pa_stream_get_mute(s, &mute) == 0);
+        fail_unless(mute == !u->mute_val);
+
+        u->mute_val = mute;
+        pa_stream_set_volume_changed_callback(s, NULL, NULL);
+
+        /* We got a volume change callback just fine. Now make sure the set() callback works */
+        fail_unless(pa_stream_set_mute(s, !u->mute_val, set_stream_mute_callback, u) == 0);
+    } else {
+        fail_unless(pa_stream_get_volume(s, &v) == 0);
+        fail_unless(pa_cvolume_avg(&v) == (PA_VOLUME_NORM / 3));
+    }
 }
 
 static void set_stream_volume_callback(pa_stream *s, int success, void *userdata) {
@@ -56,6 +85,7 @@ static void set_stream_volume_callback(pa_stream *s, int success, void *userdata
 
 static void stream_state_callback(pa_stream *s, void *userdata) {
     pa_cvolume v;
+    api_test_context *u = (api_test_context *) userdata;
 
     fail_unless(s != NULL);
 
@@ -70,12 +100,25 @@ 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));
+            if (!u->mute) {
+                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);
+            }
+
+            pa_stream_set_volume_changed_callback(s, volume_changed_callback, u);
+
+            if (u->mute) {
+                /* Test whether we trigger a volume change callback when changing mute status */
+                fail_unless(pa_stream_get_mute(s, &u->mute_val) == 0);
+                fail_unless(pa_stream_set_mute(s, !u->mute_val, NULL, NULL) == 0);
+            } else {
+                /* Try setting the volume after connecting, and see if we get bot the change callback,
+                 * as well as the completion callback */
+                fail_unless(pa_stream_set_volume(s, &v, set_stream_volume_callback, PA_UINT_TO_PTR(true)) == 0);
+            }
 
-            pa_cvolume_set(&v, CHANNELS, PA_VOLUME_NORM / 3);
-            pa_stream_set_volume_changed_callback(s, volume_changed_callback, NULL);
-            fail_unless(pa_stream_set_volume(s, &v, set_stream_volume_callback, PA_UINT_TO_PTR(true)) == 0);
             break;
 
         default:
@@ -93,7 +136,7 @@ static void context_state_callback(pa_context *c, void *userdata) {
         .rate = 44100,
         .channels = CHANNELS,
     };
-    bool playback = PA_PTR_TO_UINT(userdata);
+    api_test_context *u = (api_test_context *) userdata;
 
     fail_unless(c != NULL);
 
@@ -107,11 +150,14 @@ static void context_state_callback(pa_context *c, void *userdata) {
             s = pa_stream_new(c, "api-test", &ss, NULL);
             fail_unless(s != NULL);
 
-            pa_cvolume_set(&v, CHANNELS, PA_VOLUME_NORM / 2);
-            fail_unless(pa_stream_set_volume(s, &v, set_stream_volume_callback, PA_UINT_TO_PTR(false)) == 0);
+            if (!u->mute) {
+                /* Try setting the volume before we've connected */
+                pa_cvolume_set(&v, CHANNELS, PA_VOLUME_NORM / 2);
+                fail_unless(pa_stream_set_volume(s, &v, set_stream_volume_callback, PA_UINT_TO_PTR(false)) == 0);
+            }
 
-            pa_stream_set_state_callback(s, stream_state_callback, NULL);
-            if (playback)
+            pa_stream_set_state_callback(s, stream_state_callback, u);
+            if (u->playback)
                 fail_unless(pa_stream_connect_playback(s, NULL, NULL, PA_STREAM_START_CORKED, NULL, NULL) == 0);
             else
                 fail_unless(pa_stream_connect_record(s, NULL, NULL, PA_STREAM_START_CORKED) == 0);
@@ -129,7 +175,7 @@ static void context_state_callback(pa_context *c, void *userdata) {
     }
 }
 
-static void stream_volume_test(bool playback) {
+static void stream_volume_test(api_test_context *u) {
     pa_mainloop *m;
     pa_mainloop_api *api;
     pa_context *context;
@@ -143,7 +189,7 @@ static void stream_volume_test(bool playback) {
     context = pa_context_new(api, NULL);
     fail_unless(context != NULL);
 
-    pa_context_set_state_callback(context, context_state_callback, PA_UINT_TO_PTR(playback));
+    pa_context_set_state_callback(context, context_state_callback, u);
 
     /* Connect the context */
     if (pa_context_connect(context, NULL, 0, NULL) < 0) {
@@ -164,13 +210,53 @@ quit:
 
 START_TEST (playback_stream_volume_test)
 {
-    stream_volume_test(true);
+    api_test_context *u = pa_xnew0(api_test_context, 1);
+
+    u->playback = true;
+    u->mute = false;
+
+    stream_volume_test(u);
+
+    pa_xfree(u);
 }
 END_TEST
 
 START_TEST (record_stream_volume_test)
 {
-    stream_volume_test(false);
+    api_test_context *u = pa_xnew0(api_test_context, 1);
+
+    u->playback = false;
+    u->mute = false;
+
+    stream_volume_test(u);
+
+    pa_xfree(u);
+}
+END_TEST
+
+START_TEST (playback_stream_mute_test)
+{
+    api_test_context *u = pa_xnew0(api_test_context, 1);
+
+    u->playback = true;
+    u->mute = true;
+
+    stream_volume_test(u);
+
+    pa_xfree(u);
+}
+END_TEST
+
+START_TEST (record_stream_mute_test)
+{
+    api_test_context *u = pa_xnew0(api_test_context, 1);
+
+    u->playback = false;
+    u->mute = true;
+
+    stream_volume_test(u);
+
+    pa_xfree(u);
 }
 END_TEST
 
@@ -184,6 +270,8 @@ int main(int argc, char *argv[]) {
     tc = tcase_create("streamvolume");
     tcase_add_test(tc, playback_stream_volume_test);
     tcase_add_test(tc, record_stream_volume_test);
+    tcase_add_test(tc, playback_stream_mute_test);
+    tcase_add_test(tc, record_stream_mute_test);
     suite_add_tcase(s, tc);
 
     sr = srunner_create(s);
-- 
2.5.0



More information about the pulseaudio-discuss mailing list