[pulseaudio-discuss] [PATCH] virtual sources and sinks: Don't double attach a sink input or source output on filter load

Georg Chini georg at chini.tk
Sun May 7 15:19:42 UTC 2017


When a filter is loaded and module-switch-on-connect is present, switch-on-connect
will make the filter the default sink or source and move streams from the old
default to the filter. This is done from the sink/source put hook, therefore streams
are moved to the filter before the module init function of the filter calls
sink_input_put() or source_output_put(). The move succeeds because the asyncmsq
already points to the queue of the master sink or source. When the master sink or
source is attached to the sink input or source output, the attach callback will call
pa_{sink,source}_attach_within_thread(). These functions assume that all streams
are detached. Because streams were already moved to the filter by switch-on-connect,
this assumption leads to an assertion in pa_{sink_input,source_output}_attach().

This patch fixes the problem by reverting the order of the pa_{sink,source}_put()
calls and the pa_{sink_input,source_output}_put calls and creating the sink input
or source output corked. The initial rewind that is done for the master sink is
moved to the sink input message handler.

The patch also seems to improve user experience, the move of a stream to the filter
sink is now done without any audible interruption.

The patch is only tested for module-echo-cancel.

Bug-Link: https://bugs.freedesktop.org/show_bug.cgi?id=100065
---
 src/modules/echo-cancel/module-echo-cancel.c | 45 ++++++++++++++++++++--------
 src/modules/module-ladspa-sink.c             | 39 ++++++++++++------------
 src/modules/module-remap-sink.c              | 40 ++++++++++++-------------
 src/modules/module-remap-source.c            | 12 +++++---
 src/modules/module-virtual-sink.c            | 39 ++++++++++++------------
 src/modules/module-virtual-source.c          | 11 +++++--
 src/modules/module-virtual-surround-sink.c   | 39 ++++++++++++------------
 7 files changed, 129 insertions(+), 96 deletions(-)

diff --git a/src/modules/echo-cancel/module-echo-cancel.c b/src/modules/echo-cancel/module-echo-cancel.c
index 04984f32..04c8406a 100644
--- a/src/modules/echo-cancel/module-echo-cancel.c
+++ b/src/modules/echo-cancel/module-echo-cancel.c
@@ -459,6 +459,18 @@ static int sink_process_msg_cb(pa_msgobject *o, int code, void *data, int64_t of
                 pa_bytes_to_usec(pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq), &u->sink_input->sink->sample_spec);
 
             return 0;
+
+        case PA_SINK_MESSAGE_SET_STATE: {
+            pa_sink_state_t new_state = (pa_sink_state_t) PA_PTR_TO_UINT(data);
+
+            /* When set to running or idle for the first time, request a rewind
+             * of the master sink to make sure we are heard immediately */
+            if ((new_state == PA_SINK_IDLE || new_state == PA_SINK_RUNNING) && u->sink->thread_info.state == PA_SINK_INIT) {
+                pa_log_debug("Requesting rewind due to state change.");
+                pa_sink_input_request_rewind(u->sink_input, 0, false, true, true);
+            }
+        }
+
     }
 
     return pa_sink_process_msg(o, code, data, offset, chunk);
@@ -986,6 +998,10 @@ static void source_output_process_rewind_cb(pa_source_output *o, size_t nbytes)
     pa_source_output_assert_io_context(o);
     pa_assert_se(u = o->userdata);
 
+    /* If the source is not yet linked, there is nothing to rewind */
+    if (!PA_SOURCE_IS_LINKED(u->source->thread_info.state))
+        return;
+
     pa_source_process_rewind(u->source, nbytes);
 
     /* go back on read side, we need to use older sink data for this */
@@ -1005,6 +1021,10 @@ static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
     pa_sink_input_assert_ref(i);
     pa_assert_se(u = i->userdata);
 
+    /* If the sink is not yet linked, there is nothing to rewind */
+    if (!PA_SINK_IS_LINKED(u->sink->thread_info.state))
+        return;
+
     pa_log_debug("Sink process rewind %lld", (long long) nbytes);
 
     pa_sink_process_rewind(u->sink, nbytes);
@@ -1248,7 +1268,8 @@ static void source_output_attach_cb(pa_source_output *o) {
 
     pa_log_debug("Source output %d attach", o->index);
 
-    pa_source_attach_within_thread(u->source);
+    if (PA_SOURCE_IS_LINKED(u->source->thread_info.state))
+        pa_source_attach_within_thread(u->source);
 
     u->rtpoll_item_read = pa_rtpoll_item_new_asyncmsgq_read(
             o->source->thread_info.rtpoll,
@@ -1286,7 +1307,8 @@ static void sink_input_attach_cb(pa_sink_input *i) {
             PA_RTPOLL_LATE,
             u->asyncmsgq);
 
-    pa_sink_attach_within_thread(u->sink);
+    if (PA_SINK_IS_LINKED(u->sink->thread_info.state))
+        pa_sink_attach_within_thread(u->sink);
 }
 
 /* Called from source I/O thread context. */
@@ -1345,14 +1367,6 @@ static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t s
     pa_assert_se(u = i->userdata);
 
     pa_log_debug("Sink input %d state %d", i->index, state);
-
-    /* If we are added for the first time, ask for a rewinding so that
-     * we are heard right-away. */
-    if (PA_SINK_INPUT_IS_LINKED(state) &&
-        i->thread_info.state == PA_SINK_INPUT_INIT && i->sink) {
-        pa_log_debug("Requesting rewind due to state change.");
-        pa_sink_input_request_rewind(i, 0, false, true, true);
-    }
 }
 
 /* Called from main context. */
@@ -1910,6 +1924,7 @@ int pa__init(pa_module*m) {
     pa_proplist_sets(source_output_data.proplist, PA_PROP_MEDIA_ROLE, "filter");
     pa_source_output_new_data_set_sample_spec(&source_output_data, &source_output_ss);
     pa_source_output_new_data_set_channel_map(&source_output_data, &source_output_map);
+    source_output_data.flags |= PA_SOURCE_OUTPUT_START_CORKED;
 
     if (autoloaded)
         source_output_data.flags |= PA_SOURCE_OUTPUT_DONT_MOVE;
@@ -1947,7 +1962,7 @@ int pa__init(pa_module*m) {
     pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "filter");
     pa_sink_input_new_data_set_sample_spec(&sink_input_data, &sink_ss);
     pa_sink_input_new_data_set_channel_map(&sink_input_data, &sink_map);
-    sink_input_data.flags = PA_SINK_INPUT_VARIABLE_RATE;
+    sink_input_data.flags = PA_SINK_INPUT_VARIABLE_RATE | PA_SINK_INPUT_START_CORKED;
 
     if (autoloaded)
         sink_input_data.flags |= PA_SINK_INPUT_DONT_MOVE;
@@ -2035,11 +2050,15 @@ int pa__init(pa_module*m) {
     pa_sink_set_latency_range(u->sink, blocksize_usec, blocksize_usec * MAX_LATENCY_BLOCKS);
     pa_sink_input_set_requested_latency(u->sink_input, blocksize_usec * MAX_LATENCY_BLOCKS);
 
+    pa_sink_input_put(u->sink_input);
+    pa_source_output_put(u->source_output);
+
     pa_sink_put(u->sink);
     pa_source_put(u->source);
 
-    pa_sink_input_put(u->sink_input);
-    pa_source_output_put(u->source_output);
+    pa_source_output_cork(u->source_output, false);
+    pa_sink_input_cork(u->sink_input, false);
+
     pa_modargs_free(ma);
 
     return 0;
diff --git a/src/modules/module-ladspa-sink.c b/src/modules/module-ladspa-sink.c
index 549ceca6..dc474fe0 100644
--- a/src/modules/module-ladspa-sink.c
+++ b/src/modules/module-ladspa-sink.c
@@ -373,6 +373,17 @@ static int sink_process_msg_cb(pa_msgobject *o, int code, void *data, int64_t of
         connect_control_ports(u);
 
         return 0;
+
+        case PA_SINK_MESSAGE_SET_STATE: {
+            pa_sink_state_t new_state = (pa_sink_state_t) PA_PTR_TO_UINT(data);
+
+            /* When set to running or idle for the first time, request a rewind
+             * of the master sink to make sure we are heard immediately */
+            if ((new_state == PA_SINK_IDLE || new_state == PA_SINK_RUNNING) && u->sink->thread_info.state == PA_SINK_INIT) {
+                pa_log_debug("Requesting rewind due to state change.");
+                pa_sink_input_request_rewind(u->sink_input, 0, false, true, true);
+            }
+        }
     }
 
     return pa_sink_process_msg(o, code, data, offset, chunk);
@@ -505,6 +516,10 @@ static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
     pa_sink_input_assert_ref(i);
     pa_assert_se(u = i->userdata);
 
+    /* If the sink is not yet linked, there is nothing to rewind */
+    if (!PA_SINK_IS_LINKED(u->sink->thread_info.state))
+        return;
+
     if (u->sink->thread_info.rewind_nbytes > 0) {
         size_t max_rewrite;
 
@@ -604,7 +619,8 @@ static void sink_input_attach_cb(pa_sink_input *i) {
      * https://bugs.freedesktop.org/show_bug.cgi?id=53709 */
     pa_sink_set_max_rewind_within_thread(u->sink, pa_sink_input_get_max_rewind(i));
 
-    pa_sink_attach_within_thread(u->sink);
+    if (PA_SINK_IS_LINKED(u->sink->thread_info.state))
+        pa_sink_attach_within_thread(u->sink);
 }
 
 /* Called from main context */
@@ -629,22 +645,6 @@ static void sink_input_kill_cb(pa_sink_input *i) {
     pa_module_unload_request(u->module, true);
 }
 
-/* Called from IO thread context */
-static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t state) {
-    struct userdata *u;
-
-    pa_sink_input_assert_ref(i);
-    pa_assert_se(u = i->userdata);
-
-    /* If we are added for the first time, ask for a rewinding so that
-     * we are heard right-away. */
-    if (PA_SINK_INPUT_IS_LINKED(state) &&
-            i->thread_info.state == PA_SINK_INPUT_INIT && i->sink) {
-        pa_log_debug("Requesting rewind due to state change.");
-        pa_sink_input_request_rewind(i, 0, false, true, true);
-    }
-}
-
 /* Called from main context */
 static bool sink_input_may_move_to_cb(pa_sink_input *i, pa_sink *dest) {
     struct userdata *u;
@@ -1301,6 +1301,7 @@ int pa__init(pa_module*m) {
     pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "filter");
     pa_sink_input_new_data_set_sample_spec(&sink_input_data, &ss);
     pa_sink_input_new_data_set_channel_map(&sink_input_data, &map);
+    sink_input_data.flags |= PA_SINK_INPUT_START_CORKED;
 
     pa_sink_input_new(&u->sink_input, m->core, &sink_input_data);
     pa_sink_input_new_data_done(&sink_input_data);
@@ -1317,7 +1318,6 @@ int pa__init(pa_module*m) {
     u->sink_input->kill = sink_input_kill_cb;
     u->sink_input->attach = sink_input_attach_cb;
     u->sink_input->detach = sink_input_detach_cb;
-    u->sink_input->state_change = sink_input_state_change_cb;
     u->sink_input->may_move_to = sink_input_may_move_to_cb;
     u->sink_input->moving = sink_input_moving_cb;
     u->sink_input->mute_changed = sink_input_mute_changed_cb;
@@ -1329,8 +1329,9 @@ int pa__init(pa_module*m) {
     u->memblockq = pa_memblockq_new("module-ladspa-sink memblockq", 0, MEMBLOCKQ_MAXLENGTH, 0, &ss, 1, 1, 0, &silence);
     pa_memblock_unref(silence.memblock);
 
-    pa_sink_put(u->sink);
     pa_sink_input_put(u->sink_input);
+    pa_sink_put(u->sink);
+    pa_sink_input_cork(u->sink_input, false);
 
 #ifdef HAVE_DBUS
     dbus_init(u);
diff --git a/src/modules/module-remap-sink.c b/src/modules/module-remap-sink.c
index 37f4f56c..01632163 100644
--- a/src/modules/module-remap-sink.c
+++ b/src/modules/module-remap-sink.c
@@ -96,6 +96,17 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
                 pa_bytes_to_usec(pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq), &u->sink_input->sink->sample_spec);
 
             return 0;
+
+        case PA_SINK_MESSAGE_SET_STATE: {
+            pa_sink_state_t new_state = (pa_sink_state_t) PA_PTR_TO_UINT(data);
+
+            /* When set to running or idle for the first time, request a rewind
+             * of the master sink to make sure we are heard immediately */
+            if ((new_state == PA_SINK_IDLE || new_state == PA_SINK_RUNNING) && u->sink->thread_info.state == PA_SINK_INIT) {
+                pa_log_debug("Requesting rewind due to state change.");
+                pa_sink_input_request_rewind(u->sink_input, 0, false, true, true);
+            }
+        }
     }
 
     return pa_sink_process_msg(o, code, data, offset, chunk);
@@ -170,6 +181,10 @@ static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
     pa_sink_input_assert_ref(i);
     pa_assert_se(u = i->userdata);
 
+    /* If the sink is not yet linked, there is nothing to rewind */
+    if (!PA_SINK_IS_LINKED(u->sink->thread_info.state))
+        return;
+
     if (u->sink->thread_info.rewind_nbytes > 0) {
         amount = PA_MIN(u->sink->thread_info.rewind_nbytes, nbytes);
         u->sink->thread_info.rewind_nbytes = 0;
@@ -248,7 +263,8 @@ static void sink_input_attach_cb(pa_sink_input *i) {
      * https://bugs.freedesktop.org/show_bug.cgi?id=53709 */
     pa_sink_set_max_rewind_within_thread(u->sink, pa_sink_input_get_max_rewind(i));
 
-    pa_sink_attach_within_thread(u->sink);
+    if (PA_SINK_IS_LINKED(u->sink->thread_info.state))
+        pa_sink_attach_within_thread(u->sink);
 }
 
 /* Called from main context */
@@ -273,22 +289,6 @@ static void sink_input_kill_cb(pa_sink_input *i) {
     pa_module_unload_request(u->module, true);
 }
 
-/* Called from IO thread context */
-static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t state) {
-    struct userdata *u;
-
-    pa_sink_input_assert_ref(i);
-    pa_assert_se(u = i->userdata);
-
-    /* If we are added for the first time, ask for a rewinding so that
-     * we are heard right-away. */
-    if (PA_SINK_INPUT_IS_LINKED(state) &&
-        i->thread_info.state == PA_SINK_INPUT_INIT && i->sink) {
-        pa_log_debug("Requesting rewind due to state change.");
-        pa_sink_input_request_rewind(i, 0, false, true, true);
-    }
-}
-
 /* Called from main context */
 static void sink_input_moving_cb(pa_sink_input *i, pa_sink *dest) {
     struct userdata *u;
@@ -423,7 +423,7 @@ int pa__init(pa_module*m) {
     pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "filter");
     pa_sink_input_new_data_set_sample_spec(&sink_input_data, &ss);
     pa_sink_input_new_data_set_channel_map(&sink_input_data, &stream_map);
-    sink_input_data.flags = (remix ? 0 : PA_SINK_INPUT_NO_REMIX);
+    sink_input_data.flags = (remix ? 0 : PA_SINK_INPUT_NO_REMIX) | PA_SINK_INPUT_START_CORKED;
     sink_input_data.resample_method = resample_method;
 
     pa_sink_input_new(&u->sink_input, m->core, &sink_input_data);
@@ -441,14 +441,14 @@ int pa__init(pa_module*m) {
     u->sink_input->attach = sink_input_attach_cb;
     u->sink_input->detach = sink_input_detach_cb;
     u->sink_input->kill = sink_input_kill_cb;
-    u->sink_input->state_change = sink_input_state_change_cb;
     u->sink_input->moving = sink_input_moving_cb;
     u->sink_input->userdata = u;
 
     u->sink->input_to_master = u->sink_input;
 
-    pa_sink_put(u->sink);
     pa_sink_input_put(u->sink_input);
+    pa_sink_put(u->sink);
+    pa_sink_input_cork(u->sink_input, false);
 
     pa_modargs_free(ma);
 
diff --git a/src/modules/module-remap-source.c b/src/modules/module-remap-source.c
index 0bdeb381..508740a8 100644
--- a/src/modules/module-remap-source.c
+++ b/src/modules/module-remap-source.c
@@ -167,7 +167,9 @@ static void source_output_process_rewind_cb(pa_source_output *o, size_t nbytes)
     pa_source_output_assert_io_context(o);
     pa_assert_se(u = o->userdata);
 
-    pa_source_process_rewind(u->source, nbytes);
+    /* If the source is not yet linked, there is nothing to rewind */
+    if (PA_SOURCE_IS_LINKED(u->source->thread_info.state))
+        pa_source_process_rewind(u->source, nbytes);
 }
 
 /* Called from output thread context */
@@ -196,7 +198,8 @@ static void source_output_attach_cb(pa_source_output *o) {
     pa_source_set_fixed_latency_within_thread(u->source, o->source->thread_info.fixed_latency);
     pa_source_set_max_rewind_within_thread(u->source, pa_source_output_get_max_rewind(o));
 
-    pa_source_attach_within_thread(u->source);
+    if (PA_SOURCE_IS_LINKED(u->source->thread_info.state))
+        pa_source_attach_within_thread(u->source);
 }
 
 /* Called from main thread */
@@ -368,7 +371,7 @@ int pa__init(pa_module*m) {
     pa_proplist_sets(source_output_data.proplist, PA_PROP_MEDIA_ROLE, "filter");
     pa_source_output_new_data_set_sample_spec(&source_output_data, &ss);
     pa_source_output_new_data_set_channel_map(&source_output_data, &stream_map);
-    source_output_data.flags = remix ? 0 : PA_SOURCE_OUTPUT_NO_REMIX;
+    source_output_data.flags = (remix ? 0 : PA_SOURCE_OUTPUT_NO_REMIX) | PA_SOURCE_OUTPUT_START_CORKED;
     source_output_data.resample_method = resample_method;
 
     pa_source_output_new(&u->source_output, m->core, &source_output_data);
@@ -388,8 +391,9 @@ int pa__init(pa_module*m) {
 
     u->source->output_from_master = u->source_output;
 
-    pa_source_put(u->source);
     pa_source_output_put(u->source_output);
+    pa_source_put(u->source);
+    pa_source_output_cork(u->source_output, false);
 
     pa_modargs_free(ma);
 
diff --git a/src/modules/module-virtual-sink.c b/src/modules/module-virtual-sink.c
index 4fa4a56e..5f406e11 100644
--- a/src/modules/module-virtual-sink.c
+++ b/src/modules/module-virtual-sink.c
@@ -108,6 +108,17 @@ static int sink_process_msg_cb(pa_msgobject *o, int code, void *data, int64_t of
                 pa_bytes_to_usec(pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq), &u->sink_input->sink->sample_spec);
 
             return 0;
+
+        case PA_SINK_MESSAGE_SET_STATE: {
+            pa_sink_state_t new_state = (pa_sink_state_t) PA_PTR_TO_UINT(data);
+
+            /* When set to running or idle for the first time, request a rewind
+             * of the master sink to make sure we are heard immediately */
+            if ((new_state == PA_SINK_IDLE || new_state == PA_SINK_RUNNING) && u->sink->thread_info.state == PA_SINK_INIT) {
+                pa_log_debug("Requesting rewind due to state change.");
+                pa_sink_input_request_rewind(u->sink_input, 0, false, true, true);
+            }
+        }
     }
 
     return pa_sink_process_msg(o, code, data, offset, chunk);
@@ -271,6 +282,10 @@ static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
     pa_sink_input_assert_ref(i);
     pa_assert_se(u = i->userdata);
 
+    /* If the sink is not yet linked, there is nothing to rewind */
+    if (!PA_SINK_IS_LINKED(u->sink->thread_info.state))
+        return;
+
     if (u->sink->thread_info.rewind_nbytes > 0) {
         size_t max_rewrite;
 
@@ -374,7 +389,8 @@ static void sink_input_attach_cb(pa_sink_input *i) {
      * https://bugs.freedesktop.org/show_bug.cgi?id=53709 */
     pa_sink_set_max_rewind_within_thread(u->sink, pa_sink_input_get_max_rewind(i));
 
-    pa_sink_attach_within_thread(u->sink);
+    if (PA_SINK_IS_LINKED(u->sink->thread_info.state))
+        pa_sink_attach_within_thread(u->sink);
 }
 
 /* Called from main context */
@@ -399,22 +415,6 @@ static void sink_input_kill_cb(pa_sink_input *i) {
     pa_module_unload_request(u->module, true);
 }
 
-/* Called from IO thread context */
-static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t state) {
-    struct userdata *u;
-
-    pa_sink_input_assert_ref(i);
-    pa_assert_se(u = i->userdata);
-
-    /* If we are added for the first time, ask for a rewinding so that
-     * we are heard right-away. */
-    if (PA_SINK_INPUT_IS_LINKED(state) &&
-        i->thread_info.state == PA_SINK_INPUT_INIT && i->sink) {
-        pa_log_debug("Requesting rewind due to state change.");
-        pa_sink_input_request_rewind(i, 0, false, true, true);
-    }
-}
-
 /* Called from main context */
 static void sink_input_moving_cb(pa_sink_input *i, pa_sink *dest) {
     struct userdata *u;
@@ -576,6 +576,7 @@ int pa__init(pa_module*m) {
     pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "filter");
     pa_sink_input_new_data_set_sample_spec(&sink_input_data, &ss);
     pa_sink_input_new_data_set_channel_map(&sink_input_data, &map);
+    sink_input_data.flags |= PA_SINK_INPUT_START_CORKED;
 
     pa_sink_input_new(&u->sink_input, m->core, &sink_input_data);
     pa_sink_input_new_data_done(&sink_input_data);
@@ -592,7 +593,6 @@ int pa__init(pa_module*m) {
     u->sink_input->kill = sink_input_kill_cb;
     u->sink_input->attach = sink_input_attach_cb;
     u->sink_input->detach = sink_input_detach_cb;
-    u->sink_input->state_change = sink_input_state_change_cb;
     u->sink_input->moving = sink_input_moving_cb;
     u->sink_input->volume_changed = use_volume_sharing ? NULL : sink_input_volume_changed_cb;
     u->sink_input->mute_changed = sink_input_mute_changed_cb;
@@ -606,8 +606,9 @@ int pa__init(pa_module*m) {
 
     /* (9) INITIALIZE ANYTHING ELSE YOU NEED HERE */
 
-    pa_sink_put(u->sink);
     pa_sink_input_put(u->sink_input);
+    pa_sink_put(u->sink);
+    pa_sink_input_cork(u->sink_input, false);
 
     pa_modargs_free(ma);
 
diff --git a/src/modules/module-virtual-source.c b/src/modules/module-virtual-source.c
index 42aefd05..50b89c5f 100644
--- a/src/modules/module-virtual-source.c
+++ b/src/modules/module-virtual-source.c
@@ -356,6 +356,10 @@ static void source_output_process_rewind_cb(pa_source_output *o, size_t nbytes)
     pa_source_output_assert_io_context(o);
     pa_assert_se(u = o->userdata);
 
+    /* If the source is not yet linked, there is nothing to rewind */
+    if (PA_SOURCE_IS_LINKED(u->source->thread_info.state))
+        pa_source_process_rewind(u->source, nbytes);
+
     /* FIXME, no idea what I am doing here */
 #if 0
     pa_asyncmsgq_post(u->asyncmsgq, PA_MSGOBJECT(u->sink_input), SINK_INPUT_MESSAGE_REWIND, NULL, (int64_t) nbytes, NULL, NULL);
@@ -376,7 +380,8 @@ static void source_output_attach_cb(pa_source_output *o) {
     pa_source_set_fixed_latency_within_thread(u->source, o->source->thread_info.fixed_latency);
     pa_source_set_max_rewind_within_thread(u->source, pa_source_output_get_max_rewind(o));
 
-    pa_source_attach_within_thread(u->source);
+    if (PA_SOURCE_IS_LINKED(u->source->thread_info.state))
+        pa_source_attach_within_thread(u->source);
 }
 
 /* Called from output thread context */
@@ -585,6 +590,7 @@ int pa__init(pa_module*m) {
     pa_proplist_sets(source_output_data.proplist, PA_PROP_MEDIA_ROLE, "filter");
     pa_source_output_new_data_set_sample_spec(&source_output_data, &ss);
     pa_source_output_new_data_set_channel_map(&source_output_data, &map);
+    source_output_data.flags |= PA_SOURCE_OUTPUT_START_CORKED;
 
     pa_source_output_new(&u->source_output, m->core, &source_output_data);
     pa_source_output_new_data_done(&source_output_data);
@@ -603,8 +609,9 @@ int pa__init(pa_module*m) {
 
     u->source->output_from_master = u->source_output;
 
-    pa_source_put(u->source);
     pa_source_output_put(u->source_output);
+    pa_source_put(u->source);
+    pa_source_output_cork(u->source_output, false);
 
     /* Create optional uplink sink */
     pa_sink_new_data_init(&sink_data);
diff --git a/src/modules/module-virtual-surround-sink.c b/src/modules/module-virtual-surround-sink.c
index 23c6bdc5..51bfc5e8 100644
--- a/src/modules/module-virtual-surround-sink.c
+++ b/src/modules/module-virtual-surround-sink.c
@@ -136,6 +136,17 @@ static int sink_process_msg_cb(pa_msgobject *o, int code, void *data, int64_t of
                 pa_bytes_to_usec(pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq), &u->sink_input->sink->sample_spec);
 
             return 0;
+
+        case PA_SINK_MESSAGE_SET_STATE: {
+            pa_sink_state_t new_state = (pa_sink_state_t) PA_PTR_TO_UINT(data);
+
+            /* When set to running or idle for the first time, request a rewind
+             * of the master sink to make sure we are heard immediately */
+            if ((new_state == PA_SINK_IDLE || new_state == PA_SINK_RUNNING) && u->sink->thread_info.state == PA_SINK_INIT) {
+                pa_log_debug("Requesting rewind due to state change.");
+                pa_sink_input_request_rewind(u->sink_input, 0, false, true, true);
+            }
+        }
     }
 
     return pa_sink_process_msg(o, code, data, offset, chunk);
@@ -300,6 +311,10 @@ static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
     pa_sink_input_assert_ref(i);
     pa_assert_se(u = i->userdata);
 
+    /* If the sink is not yet linked, there is nothing to rewind */
+    if (!PA_SINK_IS_LINKED(u->sink->thread_info.state))
+        return;
+
     if (u->sink->thread_info.rewind_nbytes > 0) {
         size_t max_rewrite;
 
@@ -393,7 +408,8 @@ static void sink_input_attach_cb(pa_sink_input *i) {
      * https://bugs.freedesktop.org/show_bug.cgi?id=53709 */
     pa_sink_set_max_rewind_within_thread(u->sink, pa_sink_input_get_max_rewind(i) * u->sink_fs / u->fs);
 
-    pa_sink_attach_within_thread(u->sink);
+    if (PA_SINK_IS_LINKED(u->sink->thread_info.state))
+        pa_sink_attach_within_thread(u->sink);
 }
 
 /* Called from main context */
@@ -418,22 +434,6 @@ static void sink_input_kill_cb(pa_sink_input *i) {
     pa_module_unload_request(u->module, true);
 }
 
-/* Called from IO thread context */
-static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t state) {
-    struct userdata *u;
-
-    pa_sink_input_assert_ref(i);
-    pa_assert_se(u = i->userdata);
-
-    /* If we are added for the first time, ask for a rewinding so that
-     * we are heard right-away. */
-    if (PA_SINK_INPUT_IS_LINKED(state) &&
-        i->thread_info.state == PA_SINK_INPUT_INIT && i->sink) {
-        pa_log_debug("Requesting rewind due to state change.");
-        pa_sink_input_request_rewind(i, 0, false, true, true);
-    }
-}
-
 /* Called from main context */
 static bool sink_input_may_move_to_cb(pa_sink_input *i, pa_sink *dest) {
     struct userdata *u;
@@ -750,6 +750,7 @@ int pa__init(pa_module*m) {
     pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "filter");
     pa_sink_input_new_data_set_sample_spec(&sink_input_data, &sink_input_ss);
     pa_sink_input_new_data_set_channel_map(&sink_input_data, &sink_input_map);
+    sink_input_data.flags |= PA_SINK_INPUT_START_CORKED;
 
     pa_sink_input_new(&u->sink_input, m->core, &sink_input_data);
     pa_sink_input_new_data_done(&sink_input_data);
@@ -766,7 +767,6 @@ int pa__init(pa_module*m) {
     u->sink_input->kill = sink_input_kill_cb;
     u->sink_input->attach = sink_input_attach_cb;
     u->sink_input->detach = sink_input_detach_cb;
-    u->sink_input->state_change = sink_input_state_change_cb;
     u->sink_input->may_move_to = sink_input_may_move_to_cb;
     u->sink_input->moving = sink_input_moving_cb;
     u->sink_input->volume_changed = use_volume_sharing ? NULL : sink_input_volume_changed_cb;
@@ -865,8 +865,9 @@ int pa__init(pa_module*m) {
     u->input_buffer = pa_xmalloc0(u->hrir_samples * u->sink_fs);
     u->input_buffer_offset = 0;
 
-    pa_sink_put(u->sink);
     pa_sink_input_put(u->sink_input);
+    pa_sink_put(u->sink);
+    pa_sink_input_cork(u->sink_input, false);
 
     pa_modargs_free(ma);
     return 0;
-- 
2.11.0



More information about the pulseaudio-discuss mailing list