[pulseaudio-discuss] [PATCH 12/21 v2] loopback: Track and use average adjust time

Georg Chini georg at chini.tk
Sun Feb 19 16:15:20 UTC 2017


The configured adjust time does not match exactly the real adjust time. Also
the adjust time varies. To improve latency estimation use an average of the
measured adjust times instead of the configured value in all calculations.

---
 src/modules/module-loopback.c | 36 ++++++++++++++++++++++++++++++++----
 1 file changed, 32 insertions(+), 4 deletions(-)

diff --git a/src/modules/module-loopback.c b/src/modules/module-loopback.c
index fd8afdb..fb3be22 100644
--- a/src/modules/module-loopback.c
+++ b/src/modules/module-loopback.c
@@ -83,6 +83,12 @@ struct userdata {
 
     pa_time_event *time_event;
 
+    /* Variables used to calculate the average time between
+     * subsequent calls of adjust_rates() */
+    pa_usec_t adjust_time_stamp;
+    pa_usec_t real_adjust_time;
+    pa_usec_t real_adjust_time_sum;
+
     /* Values from command line configuration */
     pa_usec_t latency;
     pa_usec_t adjust_time;
@@ -104,8 +110,10 @@ struct userdata {
     /* Various counters */
     uint32_t iteration_counter;
     uint32_t underrun_counter;
+    uint32_t adjust_counter;
 
     bool fixed_alsa_source;
+    bool source_sink_changed;
 
     /* Used for sink input and source output snapshots */
     struct {
@@ -320,14 +328,14 @@ static void adjust_rates(struct userdata *u) {
     int32_t latency_difference;
     pa_usec_t current_buffer_latency, snapshot_delay;
     int64_t current_source_sink_latency, current_latency, latency_at_optimum_rate;
-    pa_usec_t final_latency;
+    pa_usec_t final_latency, now;
 
     pa_assert(u);
     pa_assert_ctl_context();
 
     /* Runtime and counters since last change of source or sink
      * or source/sink latency */
-    run_hours = u->iteration_counter * u->adjust_time / PA_USEC_PER_SEC / 3600;
+    run_hours = u->iteration_counter * u->real_adjust_time / PA_USEC_PER_SEC / 3600;
     u->iteration_counter +=1;
 
     /* If we are seeing underruns then the latency is too small */
@@ -340,11 +348,20 @@ static void adjust_rates(struct userdata *u) {
     }
 
     /* Allow one underrun per hour */
-    if (u->iteration_counter * u->adjust_time / PA_USEC_PER_SEC / 3600 > run_hours) {
+    if (u->iteration_counter * u->real_adjust_time / PA_USEC_PER_SEC / 3600 > run_hours) {
         u->underrun_counter = PA_CLIP_SUB(u->underrun_counter, 1u);
         pa_log_info("Underrun counter: %u", u->underrun_counter);
     }
 
+    /* Calculate real adjust time */
+    now = pa_rtclock_now();
+    if (!u->source_sink_changed) {
+        u->adjust_counter++;
+        u->real_adjust_time_sum += now - u->adjust_time_stamp;
+        u->real_adjust_time = u->real_adjust_time_sum / u->adjust_counter;
+    }
+    u->adjust_time_stamp = now;
+
     /* Rates and latencies*/
     old_rate = u->sink_input->sample_spec.rate;
     base_rate = u->source_output->sample_spec.rate;
@@ -377,7 +394,9 @@ static void adjust_rates(struct userdata *u) {
     pa_log_debug("Loopback latency at base rate is %0.2f ms", (double)latency_at_optimum_rate / PA_USEC_PER_MSEC);
 
     /* Calculate new rate */
-    new_rate = rate_controller(base_rate, u->adjust_time, latency_difference);
+    new_rate = rate_controller(base_rate, u->real_adjust_time, latency_difference);
+
+    u->source_sink_changed = false;
 
     /* Set rate */
     pa_sink_input_set_rate(u->sink_input, new_rate);
@@ -699,6 +718,8 @@ static void source_output_moving_cb(pa_source_output *o, pa_source *dest) {
     u->iteration_counter = 0;
     u->underrun_counter = 0;
 
+    u->source_sink_changed = true;
+
     /* Send a mesage to the output thread that the source and offset have changed.
      * If the sink is invalid here during a profile switching situation
      * we can safely set push_called to false directly.  Also, the current
@@ -1089,6 +1110,8 @@ static void sink_input_moving_cb(pa_sink_input *i, pa_sink *dest) {
     u->iteration_counter = 0;
     u->underrun_counter = 0;
 
+    u->source_sink_changed = true;
+
     /* Send a message to the output thread that the sink and offset have changed */
     pa_asyncmsgq_send(dest->asyncmsgq, PA_MSGOBJECT(u->sink_input), SINK_INPUT_MESSAGE_SINK_CHANGED, NULL, 0, NULL);
     pa_asyncmsgq_send(dest->asyncmsgq, PA_MSGOBJECT(u->sink_input), SINK_INPUT_MESSAGE_SINK_LATENCY_OFFSET_CHANGED, NULL, u->sink_latency_offset, NULL);
@@ -1348,6 +1371,9 @@ int pa__init(pa_module *m) {
     u->iteration_counter = 0;
     u->underrun_counter = 0;
     u->underrun_latency_limit = 0;
+    u->source_sink_changed = true;
+    u->real_adjust_time_sum = 0;
+    u->adjust_counter = 0;
 
     adjust_time_sec = DEFAULT_ADJUST_TIME_USEC / PA_USEC_PER_SEC;
     if (pa_modargs_get_value_u32(ma, "adjust_time", &adjust_time_sec) < 0) {
@@ -1360,6 +1386,8 @@ int pa__init(pa_module *m) {
     else
         u->adjust_time = DEFAULT_ADJUST_TIME_USEC;
 
+    u->real_adjust_time = u->adjust_time;
+
     pa_sink_input_new_data_init(&sink_input_data);
     sink_input_data.driver = __FILE__;
     sink_input_data.module = m;
-- 
2.10.1



More information about the pulseaudio-discuss mailing list