sink-input/source-output: Don't crash when cork() is called without valid sink or source

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.
This commit is contained in:
Georg Chini 2017-03-29 07:10:28 +02:00
parent cb78d6f57c
commit 3650346f70
15 changed files with 57 additions and 43 deletions

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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),

View file

@ -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);
}

View file

@ -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);

View file

@ -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);
}

View file

@ -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)

View file

@ -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);
}

View file

@ -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),

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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);
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);
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;

View file

@ -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);
}

View file

@ -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;