[pulseaudio-discuss] [PATCH 07/13] loopback: Refactor latency initialization

Georg Chini georg at chini.tk
Wed Feb 25 10:43:19 PST 2015


as well as the way of reacting to sink input or source output moving.

The goal is to make sure that the initial latency of the system matches
the configured one.

While at it, allow to set adjust_time to 0, which means "no adjustments
at all".
---
 src/modules/module-loopback.c | 329 +++++++++++++++++++++++++++++++++++-------
 1 file changed, 275 insertions(+), 54 deletions(-)

diff --git a/src/modules/module-loopback.c b/src/modules/module-loopback.c
index 79bd106..09f2f58 100644
--- a/src/modules/module-loopback.c
+++ b/src/modules/module-loopback.c
@@ -59,7 +59,9 @@ PA_MODULE_USAGE(
 
 #define DEFAULT_LATENCY_MSEC 200
 
-#define MEMBLOCKQ_MAXLENGTH (1024*1024*16)
+#define MEMBLOCKQ_MAXLENGTH (1024*1024*32)
+
+#define DEFAULT_BUFFER_MARGIN_MSEC 20
 
 #define DEFAULT_ADJUST_TIME_USEC (10*PA_USEC_PER_SEC)
 
@@ -80,11 +82,21 @@ struct userdata {
 
     int64_t recv_counter;
     int64_t send_counter;
+    uint32_t sink_adjust_counter;
+    uint32_t source_adjust_counter;
 
-    size_t skip;
     pa_usec_t latency;
+    pa_usec_t buffer_latency;
+    pa_usec_t initial_buffer_latency;
+    pa_usec_t configured_sink_latency;
+    pa_usec_t configured_source_latency;
+
+    pa_usec_t source_latency_sum;
+    pa_usec_t sink_latency_sum;
 
     bool in_pop;
+    bool pop_called;
+    bool source_sink_changed;
 
     struct {
         int64_t send_counter;
@@ -189,13 +201,20 @@ static uint32_t rate_controller(
 static void adjust_rates(struct userdata *u) {
     size_t buffer;
     uint32_t old_rate, base_rate, new_rate;
-    pa_usec_t final_latency, current_buffer_latency, current_latency, corrected_latency;
+    pa_usec_t final_latency, source_sink_latency, current_buffer_latency, current_latency, corrected_latency;
     int32_t latency_difference;
     pa_usec_t snapshot_delay;
 
     pa_assert(u);
     pa_assert_ctl_context();
 
+    u->sink_adjust_counter +=1;
+    u->source_adjust_counter +=1;
+
+    /* Latency sums */
+    u->source_latency_sum += u->latency_snapshot.source_latency;
+    u->sink_latency_sum += u->latency_snapshot.sink_latency;
+
     /* Rates and latencies*/
     old_rate = u->sink_input->sample_spec.rate;
     base_rate = u->source_output->sample_spec.rate;
@@ -210,10 +229,12 @@ static void adjust_rates(struct userdata *u) {
     snapshot_delay = u->latency_snapshot.source_timestamp - u->latency_snapshot.sink_timestamp;
     current_latency = u->latency_snapshot.sink_latency + current_buffer_latency + base_rate * u->latency_snapshot.source_latency / old_rate - snapshot_delay;
 
-    final_latency = u->latency;
-
     /* Latency and latency difference at base rate */
     corrected_latency = u->latency_snapshot.source_latency + (u->latency_snapshot.sink_latency + current_buffer_latency) * old_rate / base_rate - snapshot_delay;
+
+    source_sink_latency = u->sink_latency_sum / u->sink_adjust_counter +
+                          u->source_latency_sum / u->source_adjust_counter;
+    final_latency = PA_MAX(u->latency, source_sink_latency + u->buffer_latency);
     latency_difference = (int32_t)(corrected_latency - final_latency);
 
     pa_log_debug("Loopback overall latency is %0.2f ms + %0.2f ms + %0.2f ms = %0.2f ms (at the base rate: %0.2f ms, old estimate: %0.2f ms)",
@@ -227,6 +248,8 @@ static void adjust_rates(struct userdata *u) {
                 (double) latency_difference / PA_USEC_PER_MSEC,
                 (int32_t)(old_rate - base_rate));
 
+    u->source_sink_changed = false;
+
     /* Calculate new rate */
     new_rate = rate_controller(base_rate, u->adjust_time, latency_difference);
 
@@ -253,11 +276,14 @@ static void time_callback(pa_mainloop_api *a, pa_time_event *e, const struct tim
     adjust_rates(u);
 }
 
-/* Called from main context */
+/* Called from main context
+ * When source or sink changes, give it a third of a second to settle down, then call adjust_rates for the first time */
 static void enable_adjust_timer(struct userdata *u, bool enable) {
     if (enable) {
-        if (u->time_event || u->adjust_time <= 0)
+        if (!u->adjust_time)
             return;
+        if (u->time_event)
+            u->core->mainloop->time_free(u->time_event);
 
         u->time_event = pa_core_rttime_new(u->module->core, pa_rtclock_now() + 333 * PA_USEC_PER_MSEC, time_callback, u);
     } else {
@@ -277,29 +303,58 @@ static void update_adjust_timer(struct userdata *u) {
         enable_adjust_timer(u, true);
 }
 
+static pa_usec_t get_requested_latency(struct userdata *u) {
+
+    return PA_MAX(u->configured_sink_latency + u->buffer_latency, u->latency);
+}
+
+/* Called from all contexts */
+static void memblockq_adjust(struct userdata *u, int32_t offset, bool allow_push) {
+    size_t memblock_bytes, requested_buffer_bytes;
+    pa_usec_t requested_buffer_latency;
+    size_t buffer_offset;
+    pa_memchunk silence;
+
+    requested_buffer_latency = get_requested_latency(u);
+    if (offset > 0)
+       requested_buffer_latency = PA_CLIP_SUB(requested_buffer_latency, (pa_usec_t)offset);
+    else
+       requested_buffer_latency = requested_buffer_latency - offset;
+
+    requested_buffer_bytes = pa_usec_to_bytes(requested_buffer_latency, &u->sink_input->sample_spec);
+    memblock_bytes = pa_memblockq_get_length(u->memblockq);
+
+    /* Drop audio from queue */
+    if ((int32_t)(memblock_bytes - requested_buffer_bytes) > 0) {
+       buffer_offset = memblock_bytes - requested_buffer_bytes;
+       pa_log_info("Dropping %zd bytes from queue", buffer_offset);
+       pa_memblockq_drop(u->memblockq, buffer_offset);
+    }
+    /* Add silence to queue, will never happen from IO-thread */
+    else if ((int32_t)(memblock_bytes - requested_buffer_bytes) < 0 && allow_push) {
+       requested_buffer_bytes = requested_buffer_bytes - memblock_bytes;
+       pa_log_info("Adding %zd bytes of silence to queue", requested_buffer_bytes);
+       pa_sink_input_get_silence(u->sink_input, &silence);
+       while (requested_buffer_bytes >= silence.length) {
+          pa_memblockq_push_align(u->memblockq, &silence);
+          requested_buffer_bytes -= silence.length;
+       }
+       if (requested_buffer_bytes > 0) {
+          silence.length = requested_buffer_bytes;
+          pa_memblockq_push_align(u->memblockq, &silence);
+       }
+       pa_memblock_unref(silence.memblock);
+    }
+}
+
 /* Called from input thread context */
 static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk) {
     struct userdata *u;
-    pa_memchunk copy;
 
     pa_source_output_assert_ref(o);
     pa_source_output_assert_io_context(o);
     pa_assert_se(u = o->userdata);
 
-    if (u->skip >= chunk->length) {
-        u->skip -= chunk->length;
-        return;
-    }
-
-    if (u->skip > 0) {
-        copy = *chunk;
-        copy.index += u->skip;
-        copy.length -= u->skip;
-        u->skip = 0;
-
-        chunk = ©
-    }
-
     pa_asyncmsgq_post(u->asyncmsgq, PA_MSGOBJECT(u->sink_input), SINK_INPUT_MESSAGE_POST, NULL, 0, chunk, NULL);
     u->send_counter += (int64_t) chunk->length;
 }
@@ -339,6 +394,33 @@ 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);
 }
 
+static void set_source_output_latency(struct userdata *u, pa_source *source) {
+     pa_usec_t min_latency, max_latency, buffer_msec, latency;
+
+    /* Set lower limit of source latency to 2.333 ms */
+    latency = PA_MAX(u->latency / 3, 2.333 * PA_USEC_PER_MSEC);
+
+    if(source->flags & PA_SOURCE_DYNAMIC_LATENCY) {
+       pa_source_get_latency_range(source, &min_latency, &max_latency);
+       if (min_latency > latency) {
+          u->buffer_latency = PA_MAX(u->buffer_latency, (pa_usec_t)(min_latency * 0.75));
+          pa_log_warn("Cannot set requested source latency, adjusting buffer to %0.2f ms", (double)u->buffer_latency / PA_USEC_PER_MSEC);
+       }
+       latency = PA_CLAMP(latency, min_latency, max_latency);
+    }
+    else {
+       latency = pa_source_get_latency(source);
+       if (latency == 0)
+          latency = pa_source_get_fixed_latency(source);
+       buffer_msec = u->core->default_fragment_size_msec + DEFAULT_BUFFER_MARGIN_MSEC;
+       if (u->buffer_latency < buffer_msec * PA_USEC_PER_MSEC) {
+          pa_log_warn("Fixed latency device, setting buffer latency to %zd.00 ms", buffer_msec);
+          u->buffer_latency = buffer_msec * PA_USEC_PER_MSEC;
+       }
+    }
+    u->configured_source_latency = pa_source_output_set_requested_latency(u->source_output, latency);
+}
+
 /* Called from output thread context */
 static void source_output_attach_cb(pa_source_output *o) {
     struct userdata *u;
@@ -365,24 +447,10 @@ static void source_output_detach_cb(pa_source_output *o) {
         pa_rtpoll_item_free(u->rtpoll_item_write);
         u->rtpoll_item_write = NULL;
     }
-}
-
-/* Called from output thread context */
-static void source_output_state_change_cb(pa_source_output *o, pa_source_output_state_t state) {
-    struct userdata *u;
-
-    pa_source_output_assert_ref(o);
-    pa_source_output_assert_io_context(o);
-    pa_assert_se(u = o->userdata);
-
-    if (PA_SOURCE_OUTPUT_IS_LINKED(state) && o->thread_info.state == PA_SOURCE_OUTPUT_INIT) {
-
-        u->skip = pa_usec_to_bytes(PA_CLIP_SUB(pa_source_get_latency_within_thread(o->source),
-                                               u->latency),
-                                   &o->sample_spec);
-
-        pa_log_info("Skipping %lu bytes", (unsigned long) u->skip);
-    }
+   u->source_sink_changed = true;
+   u->source_latency_sum = 0;
+   u->source_adjust_counter = 0;
+   u->buffer_latency = u->initial_buffer_latency;
 }
 
 /* Called from main thread */
@@ -408,7 +476,12 @@ static bool source_output_may_move_to_cb(pa_source_output *o, pa_source *dest) {
     if (!u->sink_input || !u->sink_input->sink)
         return true;
 
-    return dest != u->sink_input->sink->monitor_source;
+    /* We may still be adjusting, so reset rate to default before moving the source */
+    if (dest != u->sink_input->sink->monitor_source) {
+       pa_sink_input_set_rate(u->sink_input, u->source_output->sample_spec.rate);
+       return true;
+    }
+    return false;
 }
 
 /* Called from main thread */
@@ -416,6 +489,7 @@ static void source_output_moving_cb(pa_source_output *o, pa_source *dest) {
     pa_proplist *p;
     const char *n;
     struct userdata *u;
+    pa_usec_t sink_latency;
 
     if (!dest)
         return;
@@ -433,6 +507,29 @@ static void source_output_moving_cb(pa_source_output *o, pa_source *dest) {
     pa_sink_input_update_proplist(u->sink_input, PA_UPDATE_REPLACE, p);
     pa_proplist_free(p);
 
+    /* Set latency and calculate necessary buffer length
+     * In some profile switching situations the sink will be invalid here. If so,
+     * skip the buffer adjustment, it will be done when the sink becomes valid */
+    set_source_output_latency(u, dest);
+
+    if (!u->sink_input->sink) {
+       memblockq_adjust(u, 0, true);
+       return;
+    }
+    if (u->sink_input->sink->flags & PA_SINK_DYNAMIC_LATENCY)
+       u->buffer_latency = PA_MAX(u->buffer_latency, (pa_usec_t)(u->configured_sink_latency * 0.75));
+    else
+       u->buffer_latency = PA_MAX(u->buffer_latency, (u->core->default_fragment_size_msec + DEFAULT_BUFFER_MARGIN_MSEC) * PA_USEC_PER_MSEC);
+
+    pa_sink_input_get_latency(u->sink_input, &sink_latency);
+    if (u->send_counter > u->recv_counter)
+       sink_latency += pa_bytes_to_usec(u->send_counter - u->recv_counter, &u->sink_input->sample_spec);
+    if (dest->flags & PA_SOURCE_DYNAMIC_LATENCY)
+       sink_latency += pa_source_get_latency(dest);
+    else
+       sink_latency = PA_CLIP_SUB(sink_latency, pa_source_get_latency(dest));
+    memblockq_adjust(u, sink_latency, true);
+
     if (pa_source_get_state(dest) == PA_SOURCE_SUSPENDED)
         pa_sink_input_cork(u->sink_input, true);
     else
@@ -454,6 +551,29 @@ 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) {
+       pa_log_warn("Source latency increased to %0.2f ms", (double)source_latency / PA_USEC_PER_MSEC);
+       u->configured_source_latency = source_latency;
+       if (u->buffer_latency < source_latency * 0.75)
+          u->buffer_latency = source_latency * 0.75;
+       if (!u->source_sink_changed) {
+          u->source_adjust_counter = 0;
+          u->source_latency_sum = 0;
+       }
+    }
+}
+
 /* Called from output thread context */
 static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) {
     struct userdata *u;
@@ -463,6 +583,7 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk
     pa_assert_se(u = i->userdata);
     pa_assert(chunk);
 
+    u->pop_called = true;
     u->in_pop = true;
     while (pa_asyncmsgq_process_one(u->asyncmsgq) > 0)
         ;
@@ -512,10 +633,13 @@ static int sink_input_process_msg_cb(pa_msgobject *obj, int code, void *data, in
 
             pa_sink_input_assert_io_context(u->sink_input);
 
-            if (PA_SINK_IS_OPENED(u->sink_input->sink->thread_info.state))
-                pa_memblockq_push_align(u->memblockq, chunk);
-            else
-                pa_memblockq_flush_write(u->memblockq, true);
+            pa_memblockq_push_align(u->memblockq, chunk);
+            u->recv_counter += (int64_t) chunk->length;
+
+            if (!PA_SINK_IS_OPENED(u->sink_input->sink->thread_info.state) || !u->pop_called) {
+                memblockq_adjust(u, (int32_t)(-u->configured_sink_latency / 4), false);
+                return 0;
+            }
 
             /* Is this the end of an underrun? Then let's start things
              * right-away */
@@ -529,18 +653,16 @@ static int sink_input_process_msg_cb(pa_msgobject *obj, int code, void *data, in
                                              false, true, false);
             }
 
-            u->recv_counter += (int64_t) chunk->length;
-
             return 0;
 
         case SINK_INPUT_MESSAGE_REWIND:
 
             pa_sink_input_assert_io_context(u->sink_input);
 
-            if (PA_SINK_IS_OPENED(u->sink_input->sink->thread_info.state))
+            if (PA_SINK_IS_OPENED(u->sink_input->sink->thread_info.state) && u->pop_called)
                 pa_memblockq_seek(u->memblockq, -offset, PA_SEEK_RELATIVE, true);
             else
-                pa_memblockq_flush_write(u->memblockq, true);
+                memblockq_adjust(u, (int32_t)(-u->configured_sink_latency / 4), false);
 
             u->recv_counter -= offset;
 
@@ -565,6 +687,33 @@ static int sink_input_process_msg_cb(pa_msgobject *obj, int code, void *data, in
     return pa_sink_input_process_msg(obj, code, data, offset, chunk);
 }
 
+static void set_sink_input_latency(struct userdata *u, pa_sink *sink) {
+     pa_usec_t min_latency, max_latency, buffer_msec, latency;
+
+    /* Set lower limit of sink latency to 2.333 ms */
+    latency = PA_MAX(u->latency / 3, 2.333 * PA_USEC_PER_MSEC);
+
+    if(sink->flags & PA_SINK_DYNAMIC_LATENCY) {
+       pa_sink_get_latency_range(sink, &min_latency, &max_latency);
+       if (min_latency > latency) {
+          u->buffer_latency = PA_MAX(u->buffer_latency, (pa_usec_t)(min_latency * 0.75));
+          pa_log_warn("Cannot set requested sink latency, adjusting buffer to %0.2f ms", (double)u->buffer_latency / PA_USEC_PER_MSEC);
+       }
+       latency = PA_CLAMP(latency, min_latency, max_latency);
+    }
+    else {
+       latency = pa_sink_get_latency(sink);
+       if (latency == 0)
+          latency = pa_sink_get_fixed_latency(sink);
+       buffer_msec = u->core->default_fragment_size_msec + DEFAULT_BUFFER_MARGIN_MSEC;
+       if (u->buffer_latency < buffer_msec * PA_USEC_PER_MSEC) {
+          pa_log_warn("Fixed latency device, setting buffer latency to %zd.00 ms", buffer_msec);
+          u->buffer_latency = buffer_msec * PA_USEC_PER_MSEC;
+       }
+    }
+    u->configured_sink_latency = pa_sink_input_set_requested_latency(u->sink_input, latency);
+}
+
 /* Called from output thread context */
 static void sink_input_attach_cb(pa_sink_input *i) {
     struct userdata *u;
@@ -594,6 +743,11 @@ static void sink_input_detach_cb(pa_sink_input *i) {
         pa_rtpoll_item_free(u->rtpoll_item_read);
         u->rtpoll_item_read = NULL;
     }
+    u->source_sink_changed = true;
+    u->pop_called = false;
+    u->sink_latency_sum = 0;
+    u->sink_adjust_counter = 0;
+    u->buffer_latency = u->initial_buffer_latency;
 }
 
 /* Called from output thread context */
@@ -647,6 +801,7 @@ static void sink_input_moving_cb(pa_sink_input *i, pa_sink *dest) {
     struct userdata *u;
     pa_proplist *p;
     const char *n;
+    pa_usec_t source_latency;
 
     if (!dest)
         return;
@@ -664,6 +819,25 @@ static void sink_input_moving_cb(pa_sink_input *i, pa_sink *dest) {
     pa_source_output_update_proplist(u->source_output, PA_UPDATE_REPLACE, p);
     pa_proplist_free(p);
 
+    /* Set latency and calculate necessary buffer length
+     * In some profile switching situations the source will be invalid here. If so,
+     * skip the buffer adjustment, it will be done when the source becomes valid */
+    set_sink_input_latency(u, dest);
+
+    if (!u->source_output->source) {
+       memblockq_adjust(u, 0, true);
+       return;
+    }
+    if (u->source_output->source->flags & PA_SOURCE_DYNAMIC_LATENCY)
+       u->buffer_latency = PA_MAX(u->buffer_latency, (pa_usec_t)(u->configured_source_latency * 0.75));
+    else
+       u->buffer_latency = PA_MAX(u->buffer_latency, (u->core->default_fragment_size_msec + DEFAULT_BUFFER_MARGIN_MSEC) * PA_USEC_PER_MSEC);
+
+    pa_source_output_get_latency(u->source_output, &source_latency);
+    if (u->send_counter > u->recv_counter)
+       source_latency += pa_bytes_to_usec(u->send_counter - u->recv_counter, &u->sink_input->sample_spec);
+    memblockq_adjust(u, source_latency, true);
+
     if (pa_sink_get_state(dest) == PA_SINK_SUSPENDED)
         pa_source_output_cork(u->source_output, true);
     else
@@ -683,7 +857,39 @@ static bool sink_input_may_move_to_cb(pa_sink_input *i, pa_sink *dest) {
     if (!u->source_output || !u->source_output->source)
         return true;
 
-    return dest != u->source_output->source->monitor_of;
+    if (dest == u->source_output->source->monitor_of)
+        return false;
+
+    /* We may still be adjusting, so reset rate to default before moving the sink.
+     * If the sink is not valid (profile change), only update the sink input sample spec. */
+    if (u->sink_input->sink)
+        pa_sink_input_set_rate(u->sink_input, u->source_output->sample_spec.rate);
+    else
+        u->sink_input->sample_spec.rate = u->source_output->sample_spec.rate;
+    return true;
+}
+
+/* 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) {
+       pa_log_warn("Sink latency increased to %0.2f ms", (double)sink_latency / PA_USEC_PER_MSEC);
+       u->configured_sink_latency = sink_latency;
+       if (u->buffer_latency < sink_latency * 0.75)
+          u->buffer_latency = sink_latency * 0.75;
+       if (!u->source_sink_changed) {
+          u->sink_adjust_counter = 0;
+          u->sink_latency_sum = 0;
+       }
+    }
 }
 
 /* Called from main thread */
@@ -783,7 +989,7 @@ int pa__init(pa_module *m) {
         channels_set = true;
 
     latency_msec = DEFAULT_LATENCY_MSEC;
-    if (pa_modargs_get_value_u32(ma, "latency_msec", &latency_msec) < 0 || latency_msec < 1 || latency_msec > 30000) {
+    if (pa_modargs_get_value_u32(ma, "latency_msec", &latency_msec) < 0 || latency_msec > 30000) {
         pa_log("Invalid latency specification");
         goto fail;
     }
@@ -792,6 +998,12 @@ int pa__init(pa_module *m) {
     u->core = m->core;
     u->module = m;
     u->latency = (pa_usec_t) latency_msec * PA_USEC_PER_MSEC;
+    u->initial_buffer_latency = PA_MAX(u->latency / 4, 1.667 * PA_USEC_PER_MSEC);
+    u->buffer_latency = u->initial_buffer_latency;
+    u->sink_latency_sum = 0;
+    u->source_latency_sum = 0;
+    u->pop_called = false;
+    u->source_sink_changed = true;
 
     adjust_time_sec = DEFAULT_ADJUST_TIME_USEC / PA_USEC_PER_SEC;
     if (pa_modargs_get_value_u32(ma, "adjust_time", &adjust_time_sec) < 0) {
@@ -868,9 +1080,13 @@ 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;
 
-    pa_sink_input_set_requested_latency(u->sink_input, u->latency/3);
+    if (u->latency < 4 * PA_USEC_PER_MSEC)
+       pa_log_warn("Latency limited to 4 ms");
+
+    set_sink_input_latency(u, u->sink_input->sink);
 
     pa_source_output_new_data_init(&source_output_data);
     source_output_data.driver = __FILE__;
@@ -915,13 +1131,13 @@ int pa__init(pa_module *m) {
     u->source_output->kill = source_output_kill_cb;
     u->source_output->attach = source_output_attach_cb;
     u->source_output->detach = source_output_detach_cb;
-    u->source_output->state_change = source_output_state_change_cb;
     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;
 
-    pa_source_output_set_requested_latency(u->source_output, u->latency/3);
+    set_source_output_latency(u, u->source_output->source);
 
     pa_sink_input_get_silence(u->sink_input, &silence);
     u->memblockq = pa_memblockq_new(
@@ -936,6 +1152,11 @@ int pa__init(pa_module *m) {
             &silence);              /* silence frame */
     pa_memblock_unref(silence.memblock);
 
+    if(u->sink_input->sink->flags & PA_SINK_DYNAMIC_LATENCY)
+        memblockq_adjust(u, 0, true);
+    else
+        memblockq_adjust(u, (int32_t)(-u->configured_sink_latency / 4), true);
+
     u->asyncmsgq = pa_asyncmsgq_new(0);
 
     if (!pa_proplist_contains(u->source_output->proplist, PA_PROP_MEDIA_NAME))
-- 
2.1.4



More information about the pulseaudio-discuss mailing list