[pulseaudio-discuss,4/6] PROTOCOL: Added PA_COMMAND_SET_(SINK|SOURCE)_LATENCY_OFFSET

Submitted by Chris Billington on Jan. 23, 2016, 1:31 a.m.

Details

Message ID 1453512698-4055-4-git-send-email-chrisjbillington@gmail.com
State New
Headers show
Series "Series without cover letter" ( rev: 1 ) in PulseAudio

Not browsing as part of any series.

Commit Message

Chris Billington Jan. 23, 2016, 1:31 a.m.
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(-)

Patch hide | download patch | download mbox

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) {

Comments

On Sat, 2016-01-23 at 12:31 +1100, Chris Billington wrote:
> Bumped protocol version number to 31.

As time has passed by, the version should now be bumped to 32.

> 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.

I noticed that neither this patch nor any other patch implements
querying the sink/source latency offset. I think it's pretty important
to be able to also query the current offsets, if there's a mechanism
for setting the offsets.

Adding the querying support should be pretty simple. Basically you just
need to add a new field to the pa_sink/source_info struct and send the
value in the GET_SINK/SOURCE_INFO command. pactl should be updated too
to print the value in the "list sinks/sources" commands.

This patch looks good otherwise.

-- 
Tanu
Thanks Tanu,

I'm on something of a deadline writing a thesis before moving country, so I
won't be able to get back to this for probably about two months. But I
don't plan on abandoning it since I use this functionality daily to stream
audio in my house.

I'll implement querying of latency offsets and bump the version number to
whatever it is at the time I get back to it.

Thanks for looking at it,

Chris

On Fri, Jul 15, 2016 at 4:29 AM, Tanu Kaskinen <tanuk@iki.fi> wrote:

> On Sat, 2016-01-23 at 12:31 +1100, Chris Billington wrote:
> > Bumped protocol version number to 31.
>
> As time has passed by, the version should now be bumped to 32.
>
> > 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.
>
> I noticed that neither this patch nor any other patch implements
> querying the sink/source latency offset. I think it's pretty important
> to be able to also query the current offsets, if there's a mechanism
> for setting the offsets.
>
> Adding the querying support should be pretty simple. Basically you just
> need to add a new field to the pa_sink/source_info struct and send the
> value in the GET_SINK/SOURCE_INFO command. pactl should be updated too
> to print the value in the "list sinks/sources" commands.
>
> This patch looks good otherwise.
>
> --
> Tanu
>
On Fri, 2016-07-15 at 11:09 +1000, Chris Billington wrote:
> Thanks Tanu,
> 
> I'm on something of a deadline writing a thesis before moving country, so I
> won't be able to get back to this for probably about two months. But I
> don't plan on abandoning it since I use this functionality daily to stream
> audio in my house.
> 
> I'll implement querying of latency offsets and bump the version number to
> whatever it is at the time I get back to it.

Ok, no problem. Good luck with the thesis!