<div dir="ltr">Hi all,<div><br></div><div>Just to bump this thread, what are the odds of getting this patch included in pulseaudio? It would be useful to many of the network audio streaming services that use pulseaudio (specifically my one :p).</div><div><br></div><div>Cheers,</div><div><br></div><div>Chris</div></div><div class="gmail_extra"><br><div class="gmail_quote">On Wed, Jan 6, 2016 at 11:51 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>
Changed integer type for the offset in pa_cli_command_port_offset()<br>
</span>in cli_command.c to int64_t.<br>
Changed integer type for latency_offset in pactl.c 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>
</span> man/<a href="http://pactl.1.xml.in" rel="noreferrer" target="_blank">pactl.1.xml.in</a> | 12 +++++++<br>
man/<a href="http://pulse-cli-syntax.5.xml.in" rel="noreferrer" target="_blank">pulse-cli-syntax.5.xml.in</a> | 10 ++++++<br>
src/map-file | 2 ++<br>
src/pulse/introspect.c | 50 ++++++++++++++++++++++++++<br>
src/pulse/introspect.h | 6 ++++<br>
src/pulsecore/cli-command.c | 78 +++++++++++++++++++++++++++++++++++++++--<br>
<span class=""> src/pulsecore/native-common.h | 4 +++<br>
src/pulsecore/pdispatch.c | 4 +++<br>
src/pulsecore/protocol-native.c | 75 +++++++++++++++++++++++++++++++++++++++<br>
</span> src/utils/pacmd.c | 1 +<br>
src/utils/pactl.c | 43 +++++++++++++++++++++--<br>
13 files changed, 288 insertions(+), 5 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>
</div></div>diff --git a/man/<a href="http://pactl.1.xml.in" rel="noreferrer" target="_blank">pactl.1.xml.in</a> b/man/<a href="http://pactl.1.xml.in" rel="noreferrer" target="_blank">pactl.1.xml.in</a><br>
index c2064ca..530fb5d 100644<br>
--- a/man/<a href="http://pactl.1.xml.in" rel="noreferrer" target="_blank">pactl.1.xml.in</a><br>
+++ b/man/<a href="http://pactl.1.xml.in" rel="noreferrer" target="_blank">pactl.1.xml.in</a><br>
@@ -184,6 +184,18 @@ License along with PulseAudio; if not, see <<a href="http://www.gnu.org/licenses/" rel="noreferrer" target="_blank">http://www.gnu.org/licenses/</a>>.<br>
</option><br>
<br>
<option><br>
+ <p><opt>set-sink-latency-offset</opt> <arg>SINK</arg> <arg>OFFSET</arg></p><br>
+ <optdesc><p>Set a latency offset to a specified sink (identified by its symbolic name or numerical index).<br>
+ <arg>OFFSET</arg> is a number which represents the latency offset in microseconds</p></optdesc><br>
+ </option><br>
+<br>
+ <option><br>
+ <p><opt>set-source-latency-offset</opt> <arg>SOURCE</arg> <arg>OFFSET</arg></p><br>
+ <optdesc><p>Set a latency offset to a specified source (identified by its symbolic name or numerical index).<br>
+ <arg>OFFSET</arg> is a number which represents the latency offset in microseconds</p></optdesc><br>
+ </option><br>
+<br>
+ <option><br>
<p><opt>set-sink-volume</opt> <arg>SINK</arg> <arg>VOLUME [VOLUME ...]</arg></p><br>
<optdesc><p>Set the volume of the specified sink (identified by its symbolic name or numerical index).<br>
<arg>VOLUME</arg> can be specified as an integer (e.g. 2000, 16384), a linear factor (e.g. 0.4, 1.100), a percentage<br>
diff --git a/man/<a href="http://pulse-cli-syntax.5.xml.in" rel="noreferrer" target="_blank">pulse-cli-syntax.5.xml.in</a> b/man/<a href="http://pulse-cli-syntax.5.xml.in" rel="noreferrer" target="_blank">pulse-cli-syntax.5.xml.in</a><br>
index 0a0faba..1b976cc 100644<br>
--- a/man/<a href="http://pulse-cli-syntax.5.xml.in" rel="noreferrer" target="_blank">pulse-cli-syntax.5.xml.in</a><br>
+++ b/man/<a href="http://pulse-cli-syntax.5.xml.in" rel="noreferrer" target="_blank">pulse-cli-syntax.5.xml.in</a><br>
@@ -163,6 +163,16 @@ License along with PulseAudio; if not, see <<a href="http://www.gnu.org/licenses/" rel="noreferrer" target="_blank">http://www.gnu.org/licenses/</a>>.<br>
</option><br>
<br>
<option><br>
+ <p><opt>set-sink-latency-offset</opt> <arg>sink-index|sink-name</arg> <arg>offset</arg> </p><br>
+ <optdesc><p>Change the latency offset of a sink</p></optdesc><br>
+ </option><br>
+<br>
+ <option><br>
+ <p><opt>set-source-latency-offset</opt> <arg>source-index|source-name</arg> <arg>offset</arg> </p><br>
+ <optdesc><p>Change the latency offset of a source</p></optdesc><br>
+ </option><br>
+<br>
+ <option><br>
<p><opt>suspend-sink|suspend-source</opt> <arg>index|name</arg> <arg>boolean</arg></p><br>
<optdesc><p>Suspend (i.e. disconnect from the underlying hardware) a sink<br>
(resp. source).</p></optdesc><br>
<div><div class="h5">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..6e694db 100644<br>
<div><div class="h5">--- 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>
@@ -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>
</div></div>@@ -1757,7 +1761,7 @@ static int pa_cli_command_port_offset(pa_core *c, pa_tokenizer *t, pa_strbuf *bu<br>
return -1;<br>
}<br>
<br>
- if (pa_atoi(l, &offset) < 0) {<br>
+ if (pa_atol(l, &offset) < 0) {<br>
<span class=""> pa_strbuf_puts(buf, "Failed to parse the latency offset.\n");<br>
</span> return -1;<br>
<span class=""> }<br>
@@ -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>
+ int64_t offset;<br>
+<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>
</span>+ if (pa_atol(l, &offset) < 0) {<br>
<div><div class="h5">+ 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>
+ int64_t offset;<br>
+<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>
</div></div>+ if (pa_atol(l, &offset) < 0) {<br>
<div><div class="h5">+ 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>
index 145db04..43372ac 100644<br>
--- 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>
@@ -4887,6 +4892,76 @@ static void command_set_port_latency_offset(pa_pdispatch *pd, uint32_t command,<br>
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>
+ pa_sink_set_latency_offset(sink, offset);<br>
+<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>
+ pa_source_set_latency_offset(source, offset);<br>
+<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>
</div></div>index e9bf005..b627971 100644<br>
--- a/src/utils/pactl.c<br>
+++ b/src/utils/pactl.c<br>
@@ -65,7 +65,7 @@ static uint32_t<br>
<br>
static bool short_list_format = false;<br>
static uint32_t module_index;<br>
-static int32_t latency_offset;<br>
+static int64_t latency_offset;<br>
static bool suspend;<br>
static pa_cvolume volume;<br>
static enum volume_flags {<br>
<div><div class="h5">@@ -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>
</div></div>@@ -2041,7 +2052,35 @@ int main(int argc, char *argv[]) {<br>
<br>
card_name = pa_xstrdup(argv[optind+1]);<br>
port_name = pa_xstrdup(argv[optind+2]);<br>
- if (pa_atoi(argv[optind + 3], &latency_offset) < 0) {<br>
+ if (pa_atol(argv[optind + 3], &latency_offset) < 0) {<br>
<span class="">+ pa_log(_("Could not parse latency offset"));<br>
+ goto quit;<br>
+ }<br>
+<br>
</span>+ } else if (pa_streq(argv[optind], "set-sink-latency-offset")) {<br>
<span class="">+ 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>
</span>+ if (pa_atol(argv[optind + 2], &latency_offset) < 0) {<br>
<span class="">+ 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>
</span>+ if (pa_atol(argv[optind + 2], &latency_offset) < 0) {<br>
<span class=""> pa_log(_("Could not parse latency offset"));<br>
</span> goto quit;<br>
}<br>
<span class="HOEnZb"><font color="#888888">--<br>
2.5.0<br>
<br>
</font></span></blockquote></div><br></div>