[pulseaudio-discuss] [PATCH 4/6] PROTOCOL: Added PA_COMMAND_SET_(SINK|SOURCE)_LATENCY_OFFSET
Chris Billington
chrisjbillington at gmail.com
Fri Jan 22 17:31:36 PST 2016
Bumped protocol version number to 31.
These commands allow applications to set the latency offset of individual
sources and sinks, using functionality introduced in the last commit.
This is useful for setting the latency offset of a source or sink that is not
tied to a particular port, such as a null sink being used to capture output
and stream it over a network. If the application doing this knows the network
latency, then it can use these new protocol commands to inform PulseAudio of
it, so that video and audio may remain in sync.
---
PROTOCOL | 6 ++++
configure.ac | 2 +-
src/map-file | 2 ++
src/pulse/introspect.c | 50 +++++++++++++++++++++++++++
src/pulse/introspect.h | 6 ++++
src/pulsecore/native-common.h | 4 +++
src/pulsecore/pdispatch.c | 4 +++
src/pulsecore/protocol-native.c | 75 +++++++++++++++++++++++++++++++++++++++++
8 files changed, 148 insertions(+), 1 deletion(-)
diff --git a/PROTOCOL b/PROTOCOL
index 3c08fea..690b658 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 >= 9.0
+#
+New opcodes:
+ PA_COMMAND_SET_SINK_LATENCY_OFFSET
+ PA_COMMAND_SET_SOURCE_LATENCY_OFFSET
+
#### 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 dcd0110..8b807fc 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 93a62b8..7826fa4 100644
--- a/src/map-file
+++ b/src/map-file
@@ -62,6 +62,8 @@ pa_context_get_source_info_list;
pa_context_get_source_output_info;
pa_context_get_source_output_info_list;
pa_context_set_port_latency_offset;
+pa_context_set_sink_latency_offset;
+pa_context_set_source_latency_offset;
pa_context_get_state;
pa_context_get_tile_size;
pa_context_is_local;
diff --git a/src/pulse/introspect.c b/src/pulse/introspect.c
index 510d784..7f8b0c0 100644
--- a/src/pulse/introspect.c
+++ b/src/pulse/introspect.c
@@ -1926,6 +1926,56 @@ pa_operation* pa_context_set_port_latency_offset(pa_context *c, const char *card
return o;
}
+pa_operation* pa_context_set_sink_latency_offset(pa_context *c, const char *sink_name, int64_t offset, pa_context_success_cb_t cb, void *userdata) {
+ pa_operation *o;
+ pa_tagstruct *t;
+ uint32_t tag;
+
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+ PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, sink_name && *sink_name, PA_ERR_INVALID);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 31, PA_ERR_NOTSUPPORTED);
+
+ o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+ t = pa_tagstruct_command(c, PA_COMMAND_SET_SINK_LATENCY_OFFSET, &tag);
+ pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+ pa_tagstruct_puts(t, sink_name);
+ pa_tagstruct_puts64(t, offset);
+ pa_pstream_send_tagstruct(c->pstream, t);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+ return o;
+}
+
+pa_operation* pa_context_set_source_latency_offset(pa_context *c, const char *source_name, int64_t offset, pa_context_success_cb_t cb, void *userdata) {
+ pa_operation *o;
+ pa_tagstruct *t;
+ uint32_t tag;
+
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+ PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, source_name && *source_name, PA_ERR_INVALID);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 31, PA_ERR_NOTSUPPORTED);
+
+ o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+ t = pa_tagstruct_command(c, PA_COMMAND_SET_SOURCE_LATENCY_OFFSET, &tag);
+ pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+ pa_tagstruct_puts(t, source_name);
+ pa_tagstruct_puts64(t, offset);
+ pa_pstream_send_tagstruct(c->pstream, t);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+ return o;
+}
+
/*** Autoload stuff ***/
PA_WARN_REFERENCE(pa_context_get_autoload_info_by_name, "Module auto-loading no longer supported.");
diff --git a/src/pulse/introspect.h b/src/pulse/introspect.h
index 43389b7..15e00b0 100644
--- a/src/pulse/introspect.h
+++ b/src/pulse/introspect.h
@@ -550,6 +550,12 @@ pa_operation* pa_context_set_card_profile_by_name(pa_context *c, const char*name
/** Set the latency offset of a port. \since 3.0 */
pa_operation* pa_context_set_port_latency_offset(pa_context *c, const char *card_name, const char *port_name, int64_t offset, pa_context_success_cb_t cb, void *userdata);
+/** Set the latency offset of a sink. \since 9.0 */
+pa_operation* pa_context_set_sink_latency_offset(pa_context *c, const char *sink_name, int64_t offset, pa_context_success_cb_t cb, void *userdata);
+
+/** Set the latency offset of a source. \since 9.0 */
+pa_operation* pa_context_set_source_latency_offset(pa_context *c, const char *source_name, int64_t offset, pa_context_success_cb_t cb, void *userdata);
+
/** @} */
/** @{ \name Sink Inputs */
diff --git a/src/pulsecore/native-common.h b/src/pulsecore/native-common.h
index dc62895..c4ffb2e 100644
--- a/src/pulsecore/native-common.h
+++ b/src/pulsecore/native-common.h
@@ -179,6 +179,10 @@ enum {
PA_COMMAND_ENABLE_SRBCHANNEL,
PA_COMMAND_DISABLE_SRBCHANNEL,
+ /* Supported since protocol v31 (9.0) */
+ PA_COMMAND_SET_SINK_LATENCY_OFFSET,
+ PA_COMMAND_SET_SOURCE_LATENCY_OFFSET,
+
PA_COMMAND_MAX
};
diff --git a/src/pulsecore/pdispatch.c b/src/pulsecore/pdispatch.c
index f136875..3381985 100644
--- a/src/pulsecore/pdispatch.c
+++ b/src/pulsecore/pdispatch.c
@@ -195,6 +195,10 @@ static const char *command_names[PA_COMMAND_MAX] = {
/* BOTH DIRECTIONS */
[PA_COMMAND_ENABLE_SRBCHANNEL] = "ENABLE_SRBCHANNEL",
[PA_COMMAND_DISABLE_SRBCHANNEL] = "DISABLE_SRBCHANNEL",
+
+ /* Supported since protocol v31 (9.0) */
+ [PA_COMMAND_SET_SINK_LATENCY_OFFSET] = "SET_SINK_LATENCY_OFFSET",
+ [PA_COMMAND_SET_SOURCE_LATENCY_OFFSET] = "SET_SOURCE_LATENCY_OFFSET",
};
#endif
diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c
index 145db04..43372ac 100644
--- a/src/pulsecore/protocol-native.c
+++ b/src/pulsecore/protocol-native.c
@@ -293,6 +293,8 @@ static void command_extension(pa_pdispatch *pd, uint32_t command, uint32_t tag,
static void command_set_card_profile(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
static void command_set_sink_or_source_port(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
static void command_set_port_latency_offset(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_set_sink_latency_offset(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_set_source_latency_offset(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
static void command_enable_srbchannel(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {
@@ -397,6 +399,9 @@ static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {
[PA_COMMAND_SET_PORT_LATENCY_OFFSET] = command_set_port_latency_offset,
+ [PA_COMMAND_SET_SINK_LATENCY_OFFSET] = command_set_sink_latency_offset,
+ [PA_COMMAND_SET_SOURCE_LATENCY_OFFSET] = command_set_source_latency_offset,
+
[PA_COMMAND_ENABLE_SRBCHANNEL] = command_enable_srbchannel,
[PA_COMMAND_EXTENSION] = command_extension
@@ -4887,6 +4892,76 @@ static void command_set_port_latency_offset(pa_pdispatch *pd, uint32_t command,
pa_pstream_send_simple_ack(c->pstream, tag);
}
+static void command_set_sink_latency_offset(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
+ const char *sink_name;
+ uint32_t idx = PA_INVALID_INDEX;
+ int64_t offset;
+ pa_sink *sink = NULL;
+
+ pa_native_connection_assert_ref(c);
+ pa_assert(t);
+
+ if (pa_tagstruct_getu32(t, &idx) < 0 ||
+ pa_tagstruct_gets(t, &sink_name) < 0 ||
+ pa_tagstruct_gets64(t, &offset) < 0 ||
+ !pa_tagstruct_eof(t)) {
+ protocol_error(c);
+ return;
+ }
+
+ CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+ CHECK_VALIDITY(c->pstream, !sink_name || pa_namereg_is_valid_name_or_wildcard(sink_name, PA_NAMEREG_SINK), tag, PA_ERR_INVALID);
+ CHECK_VALIDITY(c->pstream, (idx != PA_INVALID_INDEX) ^ (sink_name != NULL), tag, PA_ERR_INVALID);
+ CHECK_VALIDITY(c->pstream, sink_name, tag, PA_ERR_INVALID);
+
+ if (idx != PA_INVALID_INDEX)
+ sink = pa_idxset_get_by_index(c->protocol->core->sinks, idx);
+ else
+ sink = pa_namereg_get(c->protocol->core, sink_name, PA_NAMEREG_SINK);
+
+ CHECK_VALIDITY(c->pstream, sink, tag, PA_ERR_NOENTITY);
+
+ pa_sink_set_latency_offset(sink, offset);
+
+ pa_pstream_send_simple_ack(c->pstream, tag);
+}
+
+static void command_set_source_latency_offset(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
+ const char *source_name;
+ uint32_t idx = PA_INVALID_INDEX;
+ int64_t offset;
+ pa_source *source = NULL;
+
+ pa_native_connection_assert_ref(c);
+ pa_assert(t);
+
+ if (pa_tagstruct_getu32(t, &idx) < 0 ||
+ pa_tagstruct_gets(t, &source_name) < 0 ||
+ pa_tagstruct_gets64(t, &offset) < 0 ||
+ !pa_tagstruct_eof(t)) {
+ protocol_error(c);
+ return;
+ }
+
+ CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+ CHECK_VALIDITY(c->pstream, !source_name || pa_namereg_is_valid_name_or_wildcard(source_name, PA_NAMEREG_SOURCE), tag, PA_ERR_INVALID);
+ CHECK_VALIDITY(c->pstream, (idx != PA_INVALID_INDEX) ^ (source_name != NULL), tag, PA_ERR_INVALID);
+ CHECK_VALIDITY(c->pstream, source_name, tag, PA_ERR_INVALID);
+
+ if (idx != PA_INVALID_INDEX)
+ source = pa_idxset_get_by_index(c->protocol->core->sources, idx);
+ else
+ source = pa_namereg_get(c->protocol->core, source_name, PA_NAMEREG_SOURCE);
+
+ CHECK_VALIDITY(c->pstream, source, tag, PA_ERR_NOENTITY);
+
+ pa_source_set_latency_offset(source, offset);
+
+ pa_pstream_send_simple_ack(c->pstream, tag);
+}
+
/*** pstream callbacks ***/
static void pstream_packet_callback(pa_pstream *p, pa_packet *packet, const pa_cmsg_ancil_data *ancil_data, void *userdata) {
--
2.5.0
More information about the pulseaudio-discuss
mailing list