If pa_sink_input_cork() or pa_source_output_cork() were called without a sink or source attached, the calls would crash pulseaudio. This patch fixes the problem, so that a source output or sink input can still be corked or uncorked while source or sink are invalid. This is needed to correct the corking logic in module-loopback. --- src/modules/echo-cancel/module-echo-cancel.c | 5 ++-- src/modules/module-equalizer-sink.c | 2 +- src/modules/module-ladspa-sink.c | 2 +- src/modules/module-loopback.c | 5 ++-- src/modules/module-remap-sink.c | 2 +- src/modules/module-remap-source.c | 3 +-- src/modules/module-sine.c | 2 +- src/modules/module-suspend-on-idle.c | 4 +-- src/modules/module-virtual-sink.c | 2 +- src/modules/module-virtual-source.c | 5 ++-- src/modules/module-virtual-surround-sink.c | 2 +- src/pulsecore/play-memblockq.c | 2 +- src/pulsecore/sink-input.c | 38 +++++++++++++++++++--------- src/pulsecore/sound-file-stream.c | 2 +- src/pulsecore/source-output.c | 24 ++++++++++-------- 15 files changed, 57 insertions(+), 43 deletions(-) diff --git a/src/modules/echo-cancel/module-echo-cancel.c b/src/modules/echo-cancel/module-echo-cancel.c index 706b302..14a148a 100644 --- a/src/modules/echo-cancel/module-echo-cancel.c +++ b/src/modules/echo-cancel/module-echo-cancel.c @@ -1322,12 +1322,11 @@ static void sink_input_detach_cb(pa_sink_input *i) { } } -/* Called from source I/O thread context. */ +/* Called from source I/O thread context except when cork() is called without valid source. */ 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); pa_log_debug("Source output %d state %d", o->index, state); @@ -1345,7 +1344,7 @@ static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t s /* 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->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); } diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c index 9c25f3f..eb46181 100644 --- a/src/modules/module-equalizer-sink.c +++ b/src/modules/module-equalizer-sink.c @@ -860,7 +860,7 @@ static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t s /* 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->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); } diff --git a/src/modules/module-ladspa-sink.c b/src/modules/module-ladspa-sink.c index c11fa5e..6dd2987 100644 --- a/src/modules/module-ladspa-sink.c +++ b/src/modules/module-ladspa-sink.c @@ -633,7 +633,7 @@ static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t s /* 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->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); } diff --git a/src/modules/module-loopback.c b/src/modules/module-loopback.c index 12ab120..4456b55 100644 --- a/src/modules/module-loopback.c +++ b/src/modules/module-loopback.c @@ -496,15 +496,14 @@ static void source_output_detach_cb(pa_source_output *o) { } } -/* Called from input thread context */ +/* Called from input thread context except when cork() is called without valid source. */ 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) { + if (PA_SOURCE_OUTPUT_IS_LINKED(state) && o->thread_info.state == PA_SOURCE_OUTPUT_INIT && o->source) { u->skip = pa_usec_to_bytes(PA_CLIP_SUB(pa_source_get_latency_within_thread(o->source), u->latency), diff --git a/src/modules/module-remap-sink.c b/src/modules/module-remap-sink.c index 8a21c3c..1b1c5e1 100644 --- a/src/modules/module-remap-sink.c +++ b/src/modules/module-remap-sink.c @@ -283,7 +283,7 @@ static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t s /* 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->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); } diff --git a/src/modules/module-remap-source.c b/src/modules/module-remap-source.c index 9b62c51..3aa8f11 100644 --- a/src/modules/module-remap-source.c +++ b/src/modules/module-remap-source.c @@ -222,12 +222,11 @@ static void source_output_kill_cb(pa_source_output *o) { pa_module_unload_request(u->module, true); } -/* Called from output thread context */ +/* Called from output thread context except when cork() is called without valid source. */ 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); pa_log_debug("Source output %d state %d.", o->index, state); diff --git a/src/modules/module-sine.c b/src/modules/module-sine.c index d56fae5..4592071 100644 --- a/src/modules/module-sine.c +++ b/src/modules/module-sine.c @@ -112,7 +112,7 @@ static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t s /* 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->thread_info.state == PA_SINK_INPUT_INIT && i->sink) pa_sink_input_request_rewind(i, 0, false, true, true); } diff --git a/src/modules/module-suspend-on-idle.c b/src/modules/module-suspend-on-idle.c index 268f0ae..90d356f 100644 --- a/src/modules/module-suspend-on-idle.c +++ b/src/modules/module-suspend-on-idle.c @@ -284,7 +284,7 @@ static pa_hook_result_t sink_input_state_changed_hook_cb(pa_core *c, pa_sink_inp pa_assert(u); state = pa_sink_input_get_state(s); - if (state == PA_SINK_INPUT_RUNNING || state == PA_SINK_INPUT_DRAINED) + if ((state == PA_SINK_INPUT_RUNNING || state == PA_SINK_INPUT_DRAINED) && s->sink) if ((d = pa_hashmap_get(u->device_infos, s->sink))) resume(d); @@ -296,7 +296,7 @@ static pa_hook_result_t source_output_state_changed_hook_cb(pa_core *c, pa_sourc pa_source_output_assert_ref(s); pa_assert(u); - if (pa_source_output_get_state(s) == PA_SOURCE_OUTPUT_RUNNING) { + if (pa_source_output_get_state(s) == PA_SOURCE_OUTPUT_RUNNING && s->source) { struct device_info *d; if (s->source->monitor_of) diff --git a/src/modules/module-virtual-sink.c b/src/modules/module-virtual-sink.c index 02cc1ac..3316e93 100644 --- a/src/modules/module-virtual-sink.c +++ b/src/modules/module-virtual-sink.c @@ -409,7 +409,7 @@ static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t s /* 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->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); } diff --git a/src/modules/module-virtual-source.c b/src/modules/module-virtual-source.c index 36edf78..0fab31e 100644 --- a/src/modules/module-virtual-source.c +++ b/src/modules/module-virtual-source.c @@ -391,17 +391,16 @@ static void source_output_detach_cb(pa_source_output *o) { pa_source_set_rtpoll(u->source, NULL); } -/* Called from output thread context */ +/* Called from output thread context except when cork() is called without valid source.*/ 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); /* FIXME */ #if 0 - if (PA_SOURCE_OUTPUT_IS_LINKED(state) && o->thread_info.state == PA_SOURCE_OUTPUT_INIT) { + if (PA_SOURCE_OUTPUT_IS_LINKED(state) && o->thread_info.state == PA_SOURCE_OUTPUT_INIT && o->source) { u->skip = pa_usec_to_bytes(PA_CLIP_SUB(pa_source_get_latency_within_thread(o->source), u->latency), diff --git a/src/modules/module-virtual-surround-sink.c b/src/modules/module-virtual-surround-sink.c index 6c7120a..4a53623 100644 --- a/src/modules/module-virtual-surround-sink.c +++ b/src/modules/module-virtual-surround-sink.c @@ -421,7 +421,7 @@ static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t s /* 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->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); } diff --git a/src/pulsecore/play-memblockq.c b/src/pulsecore/play-memblockq.c index e087047..c86bd15 100644 --- a/src/pulsecore/play-memblockq.c +++ b/src/pulsecore/play-memblockq.c @@ -102,7 +102,7 @@ static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t s /* 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->thread_info.state == PA_SINK_INPUT_INIT && i->sink) pa_sink_input_request_rewind(i, 0, false, true, true); } diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c index b937383..d6ef57d 100644 --- a/src/pulsecore/sink-input.c +++ b/src/pulsecore/sink-input.c @@ -604,14 +604,26 @@ static void sink_input_set_state(pa_sink_input *i, pa_sink_input_state_t state) if (i->state == state) return; - if (i->state == PA_SINK_INPUT_CORKED && state == PA_SINK_INPUT_RUNNING && pa_sink_used_by(i->sink) == 0 && - !pa_sample_spec_equal(&i->sample_spec, &i->sink->sample_spec)) { - /* We were uncorked and the sink was not playing anything -- let's try - * to update the sample rate to avoid resampling */ - pa_sink_update_rate(i->sink, i->sample_spec.rate, pa_sink_input_is_passthrough(i)); - } + if (i->sink) { + if (i->state == PA_SINK_INPUT_CORKED && state == PA_SINK_INPUT_RUNNING && pa_sink_used_by(i->sink) == 0 && + !pa_sample_spec_equal(&i->sample_spec, &i->sink->sample_spec)) { + /* We were uncorked and the sink was not playing anything -- let's try + * to update the sample rate to avoid resampling */ + pa_sink_update_rate(i->sink, i->sample_spec.rate, pa_sink_input_is_passthrough(i)); + } + + pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_STATE, PA_UINT_TO_PTR(state), 0, NULL) == 0); + } else { + /* If the sink is not valid, pa_sink_input_set_state_within_thread() must be called directly */ + + pa_sink_input_set_state_within_thread(i, state); - pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_STATE, PA_UINT_TO_PTR(state), 0, NULL) == 0); + for (ssync = i->thread_info.sync_prev; ssync; ssync = ssync->thread_info.sync_prev) + pa_sink_input_set_state_within_thread(ssync, state); + + for (ssync = i->thread_info.sync_next; ssync; ssync = ssync->thread_info.sync_next) + pa_sink_input_set_state_within_thread(ssync, state); + } update_n_corked(i, state); i->state = state; @@ -638,7 +650,8 @@ static void sink_input_set_state(pa_sink_input *i, pa_sink_input_state_t state) pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index); } - pa_sink_update_status(i->sink); + if (i->sink) + pa_sink_update_status(i->sink); } /* Called from main context */ @@ -1952,12 +1965,11 @@ int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, bool save) { return 0; } -/* Called from IO thread context */ +/* Called from IO thread context except when cork() is called without a valid sink. */ void pa_sink_input_set_state_within_thread(pa_sink_input *i, pa_sink_input_state_t state) { bool corking, uncorking; pa_sink_input_assert_ref(i); - pa_sink_input_assert_io_context(i); if (state == i->thread_info.state) return; @@ -1978,7 +1990,8 @@ void pa_sink_input_set_state_within_thread(pa_sink_input *i, pa_sink_input_state /* This will tell the implementing sink input driver to rewind * so that the unplayed already mixed data is not lost */ - pa_sink_input_request_rewind(i, 0, true, true, false); + if (i->sink) + pa_sink_input_request_rewind(i, 0, true, true, false); /* Set the corked state *after* requesting rewind */ i->thread_info.state = state; @@ -1996,7 +2009,8 @@ void pa_sink_input_set_state_within_thread(pa_sink_input *i, pa_sink_input_state /* OK, we're being uncorked. Make sure we're not rewound when * the hw buffer is remixed and request a remix. */ - pa_sink_input_request_rewind(i, 0, false, true, true); + if (i->sink) + pa_sink_input_request_rewind(i, 0, false, true, true); } else /* We may not be corking or uncorking, but we still need to set the state. */ i->thread_info.state = state; diff --git a/src/pulsecore/sound-file-stream.c b/src/pulsecore/sound-file-stream.c index ddda456..aeaa608 100644 --- a/src/pulsecore/sound-file-stream.c +++ b/src/pulsecore/sound-file-stream.c @@ -129,7 +129,7 @@ static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t s /* 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->thread_info.state == PA_SINK_INPUT_INIT && i->sink) pa_sink_input_request_rewind(i, 0, false, true, true); } diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c index 0ba19c8..7cbc5ad 100644 --- a/src/pulsecore/source-output.c +++ b/src/pulsecore/source-output.c @@ -524,14 +524,18 @@ static void source_output_set_state(pa_source_output *o, pa_source_output_state_ if (o->state == state) return; - if (o->state == PA_SOURCE_OUTPUT_CORKED && state == PA_SOURCE_OUTPUT_RUNNING && pa_source_used_by(o->source) == 0 && - !pa_sample_spec_equal(&o->sample_spec, &o->source->sample_spec)) { - /* We were uncorked and the source was not playing anything -- let's try - * to update the sample rate to avoid resampling */ - pa_source_update_rate(o->source, o->sample_spec.rate, pa_source_output_is_passthrough(o)); - } + if (o->source) { + if (o->state == PA_SOURCE_OUTPUT_CORKED && state == PA_SOURCE_OUTPUT_RUNNING && pa_source_used_by(o->source) == 0 && + !pa_sample_spec_equal(&o->sample_spec, &o->source->sample_spec)) { + /* We were uncorked and the source was not playing anything -- let's try + * to update the sample rate to avoid resampling */ + pa_source_update_rate(o->source, o->sample_spec.rate, pa_source_output_is_passthrough(o)); + } - pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_SET_STATE, PA_UINT_TO_PTR(state), 0, NULL) == 0); + pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_SET_STATE, PA_UINT_TO_PTR(state), 0, NULL) == 0); + } else + /* If the source is not valid, pa_source_output_set_state_within_thread() must be called directly */ + pa_source_output_set_state_within_thread(o, state); update_n_corked(o, state); o->state = state; @@ -543,7 +547,8 @@ static void source_output_set_state(pa_source_output *o, pa_source_output_state_ pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index); } - pa_source_update_status(o->source); + if (o->source) + pa_source_update_status(o->source); } /* Called from main context */ @@ -1584,10 +1589,9 @@ int pa_source_output_move_to(pa_source_output *o, pa_source *dest, bool save) { return 0; } -/* Called from IO thread context */ +/* Called from IO thread context except when cork() is called without a valid source. */ void pa_source_output_set_state_within_thread(pa_source_output *o, pa_source_output_state_t state) { pa_source_output_assert_ref(o); - pa_source_output_assert_io_context(o); if (state == o->thread_info.state) return; -- 2.10.1