[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