<div dir="ltr">Just to follow up with the use case behind this patch:<div><br></div><div>I'm working on streaming audio over a network using a null sink (capturing its .monitor source), and it's very useful to have pulseaudio report to apps the network latency as part of the latency of the sink. This keeps audio and video in sync when watching video locally but using speakers of another computer on the network.<br><br>The same issue was discussed here some time ago:<br><a href="http://lists.freedesktop.org/archives/pulseaudio-discuss/2013-November/019135.html">http://lists.freedesktop.org/archives/pulseaudio-discuss/2013-November/019135.html</a><br></div><div>and it didn't look too hard to patch, so I've had a go at doing so.</div><div><br></div><div>I haven't contributed to pulseaudio before so let me know if I've done anything foolish.</div><div><br></div><div>Regards,</div><div><br></div><div>Chris</div></div><div class="gmail_extra"><br><div class="gmail_quote">On Wed, Jan 6, 2016 at 2:36 AM, Chris Billington <span dir="ltr"><<a href="mailto:chrisjbillington@gmail.com" target="_blank">chrisjbillington@gmail.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><span class="">This required the addition of two commands to the protocol:<br>
PA_COMMAND_SET_SINK_LATENCY_OFFSET and PA_COMMAND_SET_SOURCE_LATENCY_OFFSET.<br>
<br>
</span>Changed integer type for the offset in pa_cli_command_port_offset() to int64_t.<br>
<span class=""><br>
Bumped protocol version number to 31.<br>
---<br>
 PROTOCOL                        |  6 ++++<br>
 <a href="http://configure.ac" rel="noreferrer" target="_blank">configure.ac</a>                    |  2 +-<br>
 src/map-file                    |  2 ++<br>
</span> src/pulse/introspect.c          | 50 +++++++++++++++++++++++++++<br>
 src/pulse/introspect.h          |  6 ++++<br>
 src/pulsecore/cli-command.c     | 76 ++++++++++++++++++++++++++++++++++++++++-<br>
<span class=""> src/pulsecore/native-common.h   |  4 +++<br>
 src/pulsecore/pdispatch.c       |  4 +++<br>
</span> src/pulsecore/protocol-native.c | 75 ++++++++++++++++++++++++++++++++++++++++<br>
 src/utils/pacmd.c               |  1 +<br>
 src/utils/pactl.c               | 39 +++++++++++++++++++++<br>
 11 files changed, 263 insertions(+), 2 deletions(-)<br>
<div><div class="h5"><br>
diff --git a/PROTOCOL b/PROTOCOL<br>
index 3c08fea..b6a5db7 100644<br>
--- a/PROTOCOL<br>
+++ b/PROTOCOL<br>
@@ -371,6 +371,12 @@ PA_COMMAND_DISABLE_SRBCHANNEL<br>
 Tells the client to stop listening on the additional SHM ringbuffer channel.<br>
 Acked by client by sending PA_COMMAND_DISABLE_SRBCHANNEL back.<br>
<br>
+## v31, implemented by >= 8.0<br>
+#<br>
+New opcodes:<br>
+    PA_COMMAND_SET_SINK_LATENCY_OFFSET<br>
+    PA_COMMAND_SET_SOURCE_LATENCY_OFFSET<br>
+<br>
 #### If you just changed the protocol, read this<br>
 ## module-tunnel depends on the sink/source/sink-input/source-input protocol<br>
 ## internals, so if you changed these, you might have broken module-tunnel.<br>
diff --git a/<a href="http://configure.ac" rel="noreferrer" target="_blank">configure.ac</a> b/<a href="http://configure.ac" rel="noreferrer" target="_blank">configure.ac</a><br>
index ee1b437..e749024 100644<br>
--- a/<a href="http://configure.ac" rel="noreferrer" target="_blank">configure.ac</a><br>
+++ b/<a href="http://configure.ac" rel="noreferrer" target="_blank">configure.ac</a><br>
@@ -40,7 +40,7 @@ AC_SUBST(PA_MINOR, pa_minor)<br>
 AC_SUBST(PA_MAJORMINOR, pa_major.pa_minor)<br>
<br>
 AC_SUBST(PA_API_VERSION, 12)<br>
-AC_SUBST(PA_PROTOCOL_VERSION, 30)<br>
+AC_SUBST(PA_PROTOCOL_VERSION, 31)<br>
<br>
 # The stable ABI for client applications, for the version info x:y:z<br>
 # always will hold y=z<br>
diff --git a/src/map-file b/src/map-file<br>
index 93a62b8..7826fa4 100644<br>
--- a/src/map-file<br>
+++ b/src/map-file<br>
@@ -62,6 +62,8 @@ pa_context_get_source_info_list;<br>
 pa_context_get_source_output_info;<br>
 pa_context_get_source_output_info_list;<br>
 pa_context_set_port_latency_offset;<br>
+pa_context_set_sink_latency_offset;<br>
+pa_context_set_source_latency_offset;<br>
 pa_context_get_state;<br>
 pa_context_get_tile_size;<br>
 pa_context_is_local;<br>
diff --git a/src/pulse/introspect.c b/src/pulse/introspect.c<br>
index 510d784..7f8b0c0 100644<br>
--- a/src/pulse/introspect.c<br>
+++ b/src/pulse/introspect.c<br>
@@ -1926,6 +1926,56 @@ pa_operation* pa_context_set_port_latency_offset(pa_context *c, const char *card<br>
     return o;<br>
 }<br>
<br>
+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) {<br>
+    pa_operation *o;<br>
+    pa_tagstruct *t;<br>
+    uint32_t tag;<br>
+<br>
+    pa_assert(c);<br>
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);<br>
+<br>
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);<br>
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);<br>
+    PA_CHECK_VALIDITY_RETURN_NULL(c, sink_name && *sink_name, PA_ERR_INVALID);<br>
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 31, PA_ERR_NOTSUPPORTED);<br>
+<br>
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);<br>
+<br>
+    t = pa_tagstruct_command(c, PA_COMMAND_SET_SINK_LATENCY_OFFSET, &tag);<br>
+    pa_tagstruct_putu32(t, PA_INVALID_INDEX);<br>
+    pa_tagstruct_puts(t, sink_name);<br>
+    pa_tagstruct_puts64(t, offset);<br>
+    pa_pstream_send_tagstruct(c->pstream, t);<br>
+    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);<br>
+<br>
+    return o;<br>
+}<br>
+<br>
+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) {<br>
+    pa_operation *o;<br>
+    pa_tagstruct *t;<br>
+    uint32_t tag;<br>
+<br>
+    pa_assert(c);<br>
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);<br>
+<br>
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);<br>
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);<br>
+    PA_CHECK_VALIDITY_RETURN_NULL(c, source_name && *source_name, PA_ERR_INVALID);<br>
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 31, PA_ERR_NOTSUPPORTED);<br>
+<br>
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);<br>
+<br>
+    t = pa_tagstruct_command(c, PA_COMMAND_SET_SOURCE_LATENCY_OFFSET, &tag);<br>
+    pa_tagstruct_putu32(t, PA_INVALID_INDEX);<br>
+    pa_tagstruct_puts(t, source_name);<br>
+    pa_tagstruct_puts64(t, offset);<br>
+    pa_pstream_send_tagstruct(c->pstream, t);<br>
+    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);<br>
+<br>
+    return o;<br>
+}<br>
+<br>
 /*** Autoload stuff ***/<br>
<br>
 PA_WARN_REFERENCE(pa_context_get_autoload_info_by_name, "Module auto-loading no longer supported.");<br>
diff --git a/src/pulse/introspect.h b/src/pulse/introspect.h<br>
index 43389b7..bd23467 100644<br>
--- a/src/pulse/introspect.h<br>
+++ b/src/pulse/introspect.h<br>
@@ -550,6 +550,12 @@ pa_operation* pa_context_set_card_profile_by_name(pa_context *c, const char*name<br>
 /** Set the latency offset of a port. \since 3.0 */<br>
 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);<br>
<br>
+/** Set the latency offset of a sink. \since 8.0 */<br>
+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);<br>
+<br>
+/** Set the latency offset of a source. \since 8.0 */<br>
+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);<br>
+<br>
 /** @} */<br>
<br>
 /** @{ \name Sink Inputs */<br>
diff --git a/src/pulsecore/cli-command.c b/src/pulsecore/cli-command.c<br>
</div></div>index 9a73605..94a016c 100644<br>
<span class="">--- a/src/pulsecore/cli-command.c<br>
+++ b/src/pulsecore/cli-command.c<br>
@@ -134,6 +134,8 @@ static int pa_cli_command_card_profile(pa_core *c, pa_tokenizer *t, pa_strbuf *b<br>
 static int pa_cli_command_sink_port(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);<br>
 static int pa_cli_command_source_port(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);<br>
 static int pa_cli_command_port_offset(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);<br>
+static int pa_cli_command_sink_offset(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);<br>
+static int pa_cli_command_source_offset(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);<br>
 static int pa_cli_command_dump_volumes(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);<br>
<br>
 /* A method table for all available commands */<br>
@@ -168,6 +170,8 @@ static const struct command commands[] = {<br>
     { "set-sink-port",           pa_cli_command_sink_port,          "Change the port of a sink (args: index|name, port-name)", 3},<br>
     { "set-source-port",         pa_cli_command_source_port,        "Change the port of a source (args: index|name, port-name)", 3},<br>
     { "set-port-latency-offset", pa_cli_command_port_offset,        "Change the latency of a port (args: card-index|card-name, port-name, latency-offset)", 4},<br>
+    { "set-sink-latency-offset", pa_cli_command_sink_offset,        "Change the latency of a sink (args: sink-index|sink-name, latency-offset)", 3},<br>
+    { "set-source-latency-offset", pa_cli_command_source_offset,    "Change the latency of a source (args: source-index|source-name, latency-offset)", 3},<br>
     { "suspend-sink",            pa_cli_command_suspend_sink,       "Suspend sink (args: index|name, bool)", 3},<br>
     { "suspend-source",          pa_cli_command_suspend_source,     "Suspend source (args: index|name, bool)", 3},<br>
     { "suspend",                 pa_cli_command_suspend,            "Suspend all sinks and all sources (args: bool)", 2},<br>
</span>@@ -1735,7 +1739,7 @@ static int pa_cli_command_port_offset(pa_core *c, pa_tokenizer *t, pa_strbuf *bu<br>
     const char *n, *p, *l;<br>
     pa_device_port *port;<br>
     pa_card *card;<br>
-    int32_t offset;<br>
+    int64_t offset;<br>
<br>
     pa_core_assert_ref(c);<br>
     pa_assert(t);<br>
<span class="">@@ -1777,6 +1781,76 @@ static int pa_cli_command_port_offset(pa_core *c, pa_tokenizer *t, pa_strbuf *bu<br>
     return 0;<br>
 }<br>
<br>
+static int pa_cli_command_sink_offset(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {<br>
+    const char *n, *l;<br>
+    pa_sink *sink;<br>
</span>+    int64_t offset;<br>
<div><div class="h5">+<br>
+    pa_core_assert_ref(c);<br>
+    pa_assert(t);<br>
+    pa_assert(buf);<br>
+    pa_assert(fail);<br>
+<br>
+    if (!(n = pa_tokenizer_get(t, 1))) {<br>
+           pa_strbuf_puts(buf, "You need to specify a sink either by its name or its index.\n");<br>
+           return -1;<br>
+    }<br>
+<br>
+    if (!(l = pa_tokenizer_get(t, 2))) {<br>
+        pa_strbuf_puts(buf, "You need to specify a latency offset.\n");<br>
+        return -1;<br>
+    }<br>
+<br>
+    if (pa_atoi(l, &offset) < 0) {<br>
+        pa_strbuf_puts(buf, "Failed to parse the latency offset.\n");<br>
+        return -1;<br>
+    }<br>
+<br>
+    if (!(sink = pa_namereg_get(c, n, PA_NAMEREG_SINK))) {<br>
+        pa_strbuf_puts(buf, "No sink found by this name or index.\n");<br>
+        return -1;<br>
+    }<br>
+<br>
+    pa_sink_set_latency_offset(sink, offset);<br>
+<br>
+    return 0;<br>
+}<br>
+<br>
+static int pa_cli_command_source_offset(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {<br>
+    const char *n, *l;<br>
+    pa_source *source;<br>
</div></div>+    int64_t offset;<br>
<div><div class="h5">+<br>
+    pa_core_assert_ref(c);<br>
+    pa_assert(t);<br>
+    pa_assert(buf);<br>
+    pa_assert(fail);<br>
+<br>
+    if (!(n = pa_tokenizer_get(t, 1))) {<br>
+           pa_strbuf_puts(buf, "You need to specify a source either by its name or its index.\n");<br>
+           return -1;<br>
+    }<br>
+<br>
+    if (!(l = pa_tokenizer_get(t, 2))) {<br>
+        pa_strbuf_puts(buf, "You need to specify a latency offset.\n");<br>
+        return -1;<br>
+    }<br>
+<br>
+    if (pa_atoi(l, &offset) < 0) {<br>
+        pa_strbuf_puts(buf, "Failed to parse the latency offset.\n");<br>
+        return -1;<br>
+    }<br>
+<br>
+    if (!(source = pa_namereg_get(c, n, PA_NAMEREG_SOURCE))) {<br>
+        pa_strbuf_puts(buf, "No source found by this name or index.\n");<br>
+        return -1;<br>
+    }<br>
+<br>
+    pa_source_set_latency_offset(source, offset);<br>
+<br>
+    return 0;<br>
+}<br>
+<br>
 static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {<br>
     pa_module *m;<br>
     pa_sink *sink;<br>
diff --git a/src/pulsecore/native-common.h b/src/pulsecore/native-common.h<br>
index dc62895..b32a235 100644<br>
--- a/src/pulsecore/native-common.h<br>
+++ b/src/pulsecore/native-common.h<br>
@@ -179,6 +179,10 @@ enum {<br>
     PA_COMMAND_ENABLE_SRBCHANNEL,<br>
     PA_COMMAND_DISABLE_SRBCHANNEL,<br>
<br>
+    /* Supported since protocol v31 (8.0) */<br>
+    PA_COMMAND_SET_SINK_LATENCY_OFFSET,<br>
+    PA_COMMAND_SET_SOURCE_LATENCY_OFFSET,<br>
+<br>
     PA_COMMAND_MAX<br>
 };<br>
<br>
diff --git a/src/pulsecore/pdispatch.c b/src/pulsecore/pdispatch.c<br>
index f136875..5b58a3f 100644<br>
--- a/src/pulsecore/pdispatch.c<br>
+++ b/src/pulsecore/pdispatch.c<br>
@@ -195,6 +195,10 @@ static const char *command_names[PA_COMMAND_MAX] = {<br>
     /* BOTH DIRECTIONS */<br>
     [PA_COMMAND_ENABLE_SRBCHANNEL] = "ENABLE_SRBCHANNEL",<br>
     [PA_COMMAND_DISABLE_SRBCHANNEL] = "DISABLE_SRBCHANNEL",<br>
+<br>
+    /* Supported since protocol v31 (8.0) */<br>
+    [PA_COMMAND_SET_SINK_LATENCY_OFFSET] = "SET_SINK_LATENCY_OFFSET",<br>
+    [PA_COMMAND_SET_SOURCE_LATENCY_OFFSET] = "SET_SOURCE_LATENCY_OFFSET",<br>
 };<br>
<br>
 #endif<br>
diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c<br>
</div></div>index 145db04..43372ac 100644<br>
<span class="">--- a/src/pulsecore/protocol-native.c<br>
+++ b/src/pulsecore/protocol-native.c<br>
@@ -293,6 +293,8 @@ static void command_extension(pa_pdispatch *pd, uint32_t command, uint32_t tag,<br>
 static void command_set_card_profile(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);<br>
 static void command_set_sink_or_source_port(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);<br>
 static void command_set_port_latency_offset(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);<br>
+static void command_set_sink_latency_offset(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);<br>
+static void command_set_source_latency_offset(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);<br>
 static void command_enable_srbchannel(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);<br>
<br>
 static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {<br>
@@ -397,6 +399,9 @@ static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {<br>
<br>
     [PA_COMMAND_SET_PORT_LATENCY_OFFSET] = command_set_port_latency_offset,<br>
<br>
+    [PA_COMMAND_SET_SINK_LATENCY_OFFSET] = command_set_sink_latency_offset,<br>
+    [PA_COMMAND_SET_SOURCE_LATENCY_OFFSET] = command_set_source_latency_offset,<br>
+<br>
     [PA_COMMAND_ENABLE_SRBCHANNEL] = command_enable_srbchannel,<br>
<br>
     [PA_COMMAND_EXTENSION] = command_extension<br>
</span>@@ -4887,6 +4892,76 @@ static void command_set_port_latency_offset(pa_pdispatch *pd, uint32_t command,<br>
<div><div class="h5">     pa_pstream_send_simple_ack(c->pstream, tag);<br>
 }<br>
<br>
+static void command_set_sink_latency_offset(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {<br>
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);<br>
+    const char *sink_name;<br>
+    uint32_t idx = PA_INVALID_INDEX;<br>
+    int64_t offset;<br>
+    pa_sink *sink = NULL;<br>
+<br>
+    pa_native_connection_assert_ref(c);<br>
+    pa_assert(t);<br>
+<br>
+    if (pa_tagstruct_getu32(t, &idx) < 0 ||<br>
+        pa_tagstruct_gets(t, &sink_name) < 0 ||<br>
+        pa_tagstruct_gets64(t, &offset) < 0 ||<br>
+        !pa_tagstruct_eof(t)) {<br>
+        protocol_error(c);<br>
+        return;<br>
+    }<br>
+<br>
+    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);<br>
+    CHECK_VALIDITY(c->pstream, !sink_name || pa_namereg_is_valid_name_or_wildcard(sink_name, PA_NAMEREG_SINK), tag, PA_ERR_INVALID);<br>
+    CHECK_VALIDITY(c->pstream, (idx != PA_INVALID_INDEX) ^ (sink_name != NULL), tag, PA_ERR_INVALID);<br>
+    CHECK_VALIDITY(c->pstream, sink_name, tag, PA_ERR_INVALID);<br>
+<br>
+    if (idx != PA_INVALID_INDEX)<br>
+        sink = pa_idxset_get_by_index(c->protocol->core->sinks, idx);<br>
+    else<br>
+        sink = pa_namereg_get(c->protocol->core, sink_name, PA_NAMEREG_SINK);<br>
+<br>
+    CHECK_VALIDITY(c->pstream, sink, tag, PA_ERR_NOENTITY);<br>
+<br>
</div></div>+    pa_sink_set_latency_offset(sink, offset);<br>
<div><div class="h5">+<br>
+    pa_pstream_send_simple_ack(c->pstream, tag);<br>
+}<br>
+<br>
+static void command_set_source_latency_offset(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {<br>
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);<br>
+    const char *source_name;<br>
+    uint32_t idx = PA_INVALID_INDEX;<br>
+    int64_t offset;<br>
+    pa_source *source = NULL;<br>
+<br>
+    pa_native_connection_assert_ref(c);<br>
+    pa_assert(t);<br>
+<br>
+    if (pa_tagstruct_getu32(t, &idx) < 0 ||<br>
+        pa_tagstruct_gets(t, &source_name) < 0 ||<br>
+        pa_tagstruct_gets64(t, &offset) < 0 ||<br>
+        !pa_tagstruct_eof(t)) {<br>
+        protocol_error(c);<br>
+        return;<br>
+    }<br>
+<br>
+    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);<br>
+    CHECK_VALIDITY(c->pstream, !source_name || pa_namereg_is_valid_name_or_wildcard(source_name, PA_NAMEREG_SOURCE), tag, PA_ERR_INVALID);<br>
+    CHECK_VALIDITY(c->pstream, (idx != PA_INVALID_INDEX) ^ (source_name != NULL), tag, PA_ERR_INVALID);<br>
+    CHECK_VALIDITY(c->pstream, source_name, tag, PA_ERR_INVALID);<br>
+<br>
+    if (idx != PA_INVALID_INDEX)<br>
+        source = pa_idxset_get_by_index(c->protocol->core->sources, idx);<br>
+    else<br>
+        source = pa_namereg_get(c->protocol->core, source_name, PA_NAMEREG_SOURCE);<br>
+<br>
+    CHECK_VALIDITY(c->pstream, source, tag, PA_ERR_NOENTITY);<br>
+<br>
</div></div>+    pa_source_set_latency_offset(source, offset);<br>
<div class="HOEnZb"><div class="h5">+<br>
+    pa_pstream_send_simple_ack(c->pstream, tag);<br>
+}<br>
+<br>
 /*** pstream callbacks ***/<br>
<br>
 static void pstream_packet_callback(pa_pstream *p, pa_packet *packet, const pa_cmsg_ancil_data *ancil_data, void *userdata) {<br>
diff --git a/src/utils/pacmd.c b/src/utils/pacmd.c<br>
index 616573c..e0ea919 100644<br>
--- a/src/utils/pacmd.c<br>
+++ b/src/utils/pacmd.c<br>
@@ -72,6 +72,7 @@ static void help(const char *argv0) {<br>
     printf("%s %s %s\n", argv0, "set-card-profile", _("CARD PROFILE"));<br>
     printf("%s %s %s\n", argv0, "set-(sink|source)-port", _("NAME|#N PORT"));<br>
     printf("%s %s %s\n", argv0, "set-port-latency-offset", _("CARD-NAME|CARD-#N PORT OFFSET"));<br>
+    printf("%s %s %s\n", argv0, "set-sink-(sink|source)-latency-offset", _("NAME|#N OFFSET"));<br>
     printf("%s %s %s\n", argv0, "set-log-target", _("TARGET"));<br>
     printf("%s %s %s\n", argv0, "set-log-level", _("NUMERIC-LEVEL"));<br>
     printf("%s %s %s\n", argv0, "set-log-meta", _("1|0"));<br>
diff --git a/src/utils/pactl.c b/src/utils/pactl.c<br>
index e9bf005..f04bef4 100644<br>
--- a/src/utils/pactl.c<br>
+++ b/src/utils/pactl.c<br>
@@ -130,6 +130,8 @@ static enum {<br>
     SET_SOURCE_OUTPUT_MUTE,<br>
     SET_SINK_FORMATS,<br>
     SET_PORT_LATENCY_OFFSET,<br>
+    SET_SINK_LATENCY_OFFSET,<br>
+    SET_SOURCE_LATENCY_OFFSET,<br>
     SUBSCRIBE<br>
 } action = NONE;<br>
<br>
@@ -1404,6 +1406,14 @@ static void context_state_callback(pa_context *c, void *userdata) {<br>
                     o = pa_context_set_port_latency_offset(c, card_name, port_name, latency_offset, simple_callback, NULL);<br>
                     break;<br>
<br>
+                case SET_SINK_LATENCY_OFFSET:<br>
+                    o = pa_context_set_sink_latency_offset(c, sink_name, latency_offset, simple_callback, NULL);<br>
+                    break;<br>
+<br>
+                case SET_SOURCE_LATENCY_OFFSET:<br>
+                    o = pa_context_set_source_latency_offset(c, source_name, latency_offset, simple_callback, NULL);<br>
+                    break;<br>
+<br>
                 case SUBSCRIBE:<br>
                     pa_context_set_subscribe_callback(c, context_subscribe_callback, NULL);<br>
<br>
@@ -1580,6 +1590,7 @@ static void help(const char *argv0) {<br>
     printf("%s %s %s %s\n", argv0, _("[options]"), "set-(sink-input|source-output)-mute", _("#N 1|0|toggle"));<br>
     printf("%s %s %s %s\n", argv0, _("[options]"), "set-sink-formats", _("#N FORMATS"));<br>
     printf("%s %s %s %s\n", argv0, _("[options]"), "set-port-latency-offset", _("CARD-NAME|CARD-#N PORT OFFSET"));<br>
+    printf("%s %s %s %s\n", argv0, _("[options]"), "set-(sink|source)-latency-offset", _("NAME|#N OFFSET"));<br>
     printf("%s %s %s\n",    argv0, _("[options]"), "subscribe");<br>
     printf(_("\nThe special names @DEFAULT_SINK@, @DEFAULT_SOURCE@ and @DEFAULT_MONITOR@\n"<br>
              "can be used to specify the default sink, source and monitor.\n"));<br>
@@ -2046,6 +2057,34 @@ int main(int argc, char *argv[]) {<br>
                 goto quit;<br>
             }<br>
<br>
+        } else if (pa_streq(argv[optind], "set-sink-latency-offset")) {<br>
+            action = SET_SINK_LATENCY_OFFSET;<br>
+<br>
+            if (argc != optind+3) {<br>
+                pa_log(_("You have to specify a sink name/index and a latency offset"));<br>
+                goto quit;<br>
+            }<br>
+<br>
+            sink_name = pa_xstrdup(argv[optind+1]);<br>
+            if (pa_atoi(argv[optind + 2], &latency_offset) < 0) {<br>
+                pa_log(_("Could not parse latency offset"));<br>
+                goto quit;<br>
+            }<br>
+<br>
+        } else if (pa_streq(argv[optind], "set-source-latency-offset")) {<br>
+            action = SET_SOURCE_LATENCY_OFFSET;<br>
+<br>
+            if (argc != optind+3) {<br>
+                pa_log(_("You have to specify a source name/index and a latency offset"));<br>
+                goto quit;<br>
+            }<br>
+<br>
+            source_name = pa_xstrdup(argv[optind+1]);<br>
+            if (pa_atoi(argv[optind + 2], &latency_offset) < 0) {<br>
+                pa_log(_("Could not parse latency offset"));<br>
+                goto quit;<br>
+            }<br>
+<br>
         } else if (pa_streq(argv[optind], "help")) {<br>
             help(bn);<br>
             ret = 0;<br>
--<br>
2.5.0<br>
<br>
</div></div></blockquote></div><br></div>