[pulseaudio-discuss] [PATCH v6 11/25] loopback: Calculate and track minimum possible latency

Georg Chini georg at chini.tk
Sun Jun 5 19:05:14 UTC 2016


Calculate minimum possible latency for the current combination of source and sink.
The actual calculation has been put in a separate function so it can easily be
changed. To keep the values up to date, changes in the latency ranges have to be
tracked.
Use the calculated minimum latency to limit the configured latency to that value.
The minimum latency is only a "best guess", so the actual minimum may be much
larger (for example for USB devices) or much smaller (for example bluetooth HSP)
than the calculated value.

---
 src/modules/module-loopback.c | 70 +++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 68 insertions(+), 2 deletions(-)

diff --git a/src/modules/module-loopback.c b/src/modules/module-loopback.c
index 23ef7ac..f54644f 100644
--- a/src/modules/module-loopback.c
+++ b/src/modules/module-loopback.c
@@ -95,6 +95,7 @@ struct userdata {
     int64_t sink_offset;
     pa_usec_t configured_sink_latency;
     pa_usec_t configured_source_latency;
+    pa_usec_t minimum_latency;
 
     /* Various booleans */
     bool in_pop;
@@ -230,7 +231,7 @@ static void adjust_rates(struct userdata *u) {
     /* Latency at base rate */
     latency_at_optimum_rate = current_source_sink_latency + current_buffer_latency * old_rate / base_rate;
 
-    final_latency = u->latency;
+    final_latency = PA_MAX(u->latency, u->minimum_latency);
     latency_difference = (int32_t)((int64_t)latency_at_optimum_rate - final_latency);
 
     pa_log_debug("Loopback overall latency is %0.2f ms + %0.2f ms + %0.2f ms = %0.2f ms",
@@ -302,7 +303,7 @@ static void memblockq_adjust(struct userdata *u, int32_t offset, bool allow_push
     size_t buffer_offset;
     pa_memchunk silence;
 
-    requested_buffer_latency = u->latency;
+    requested_buffer_latency = PA_MAX(u->minimum_latency, u->latency);
     if (offset > 0)
         requested_buffer_latency = PA_CLIP_SUB(requested_buffer_latency, (pa_usec_t)offset);
     else
@@ -382,6 +383,24 @@ static int source_output_process_msg_cb(pa_msgobject *obj, int code, void *data,
     return pa_source_output_process_msg(obj, code, data, offset, chunk);
 }
 
+/* It has been a matter of discussion how to correctly calculate the minimum
+ * latency that module-loopback can deliver with a given source and sink.
+ * The calculation has been placed in a separate function so that the definition
+ * can easily be changed */
+static void update_minimum_latency(struct userdata *u) {
+
+    u->minimum_latency = u->min_sink_latency;
+    if (u->fixed_alsa_source)
+        u->minimum_latency += u->core->default_fragment_size_msec * PA_USEC_PER_MSEC;
+    else
+        u->minimum_latency += u->min_source_latency / 2;
+    if (-(u->sink_offset + u->source_offset) <= (int64_t) u->minimum_latency)
+        u->minimum_latency += u->sink_offset + u->source_offset;
+    else
+        u->minimum_latency = 0;
+    pa_log_debug("Minimum possible loopback latency: %0.2f", (double)u->minimum_latency / PA_USEC_PER_MSEC);
+}
+
 /* Calculates minimum and maximum possible latency for source and sink */
 static void update_latency_boundaries(struct userdata *u, pa_source *source, pa_sink *sink) {
 
@@ -419,6 +438,8 @@ static void update_latency_boundaries(struct userdata *u, pa_source *source, pa_
         if (u->max_sink_latency >= MIN_DEVICE_LATENCY)
             u->min_sink_latency = PA_MAX(u->min_sink_latency, MIN_DEVICE_LATENCY);
     }
+
+    update_minimum_latency(u);
 }
 
 /* Set source output latency to one third of the overall latency if possible */
@@ -550,6 +571,26 @@ static void source_output_suspend_cb(pa_source_output *o, bool suspended) {
     update_adjust_timer(u);
 }
 
+/* Called from input thread context */
+static void update_source_requested_latency_cb(pa_source_output *i) {
+    struct userdata *u;
+    pa_usec_t source_latency;
+
+    pa_source_output_assert_ref(i);
+    pa_source_output_assert_io_context(i);
+    pa_assert_se(u = i->userdata);
+
+    /* Source latency may have changed */
+    source_latency = pa_source_get_requested_latency_within_thread(u->source_output->source);
+    if (source_latency > u->configured_source_latency) {
+        /* The minimum latency has changed to a value larger than the configured latency */
+        pa_log_warn("Source latency increased to %0.2f ms", (double)source_latency / PA_USEC_PER_MSEC);
+        u->configured_source_latency = source_latency;
+        u->min_source_latency = source_latency;
+        update_minimum_latency(u);
+    }
+}
+
 /* Called from output thread context */
 static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) {
     struct userdata *u;
@@ -825,6 +866,26 @@ static bool sink_input_may_move_to_cb(pa_sink_input *i, pa_sink *dest) {
     return dest != u->source_output->source->monitor_of;
 }
 
+/* Called from output thread context */
+static void update_sink_requested_latency_cb(pa_sink_input *i) {
+    struct userdata *u;
+    pa_usec_t sink_latency;
+
+    pa_sink_input_assert_ref(i);
+    pa_sink_input_assert_io_context(i);
+    pa_assert_se(u = i->userdata);
+
+    /* Sink latency may have changed */
+    sink_latency = pa_sink_get_requested_latency_within_thread(u->sink_input->sink);
+    if (sink_latency > u->configured_sink_latency) {
+        /* The minimum latency has changed to a value larger than the configured latency */
+        pa_log_warn("Sink latency increased to %0.2f ms", (double)sink_latency / PA_USEC_PER_MSEC);
+        u->configured_sink_latency = sink_latency;
+        u->min_sink_latency = sink_latency;
+        update_minimum_latency(u);
+    }
+}
+
 /* Called from main thread */
 static void sink_input_suspend_cb(pa_sink_input *i, bool suspended) {
     struct userdata *u;
@@ -1013,6 +1074,7 @@ int pa__init(pa_module *m) {
     u->sink_input->may_move_to = sink_input_may_move_to_cb;
     u->sink_input->moving = sink_input_moving_cb;
     u->sink_input->suspend = sink_input_suspend_cb;
+    u->sink_input->update_sink_requested_latency = update_sink_requested_latency_cb;
     u->sink_input->userdata = u;
 
     update_latency_boundaries(u, NULL, u->sink_input->sink);
@@ -1064,11 +1126,15 @@ int pa__init(pa_module *m) {
     u->source_output->may_move_to = source_output_may_move_to_cb;
     u->source_output->moving = source_output_moving_cb;
     u->source_output->suspend = source_output_suspend_cb;
+    u->source_output->update_source_requested_latency = update_source_requested_latency_cb;
     u->source_output->userdata = u;
 
     update_latency_boundaries(u, u->source_output->source, u->sink_input->sink);
     set_source_output_latency(u, u->source_output->source);
 
+    if (u->latency < u->minimum_latency)
+       pa_log_warn("Latency limited to %0.2f ms with current combination of source and sink", (double)u->minimum_latency / PA_USEC_PER_MSEC);
+
     pa_sink_input_get_silence(u->sink_input, &silence);
     u->memblockq = pa_memblockq_new(
             "module-loopback memblockq",
-- 
2.8.1



More information about the pulseaudio-discuss mailing list