source: Implement dynamic source.

When new source-output is connecting to our droid source
we check if its sample specification or channel map differ
from currently configured source. If they differ close the
input stream and try to open with the new values. If opening
new input stream fails retry opening with previously used
values.

When there already is one or more connected source-outputs
and a new one connects, source is always reconfigured based
on the latest connecting source-output. Already connected
source-outputs will then reattach so that if need be they
will use resampler to get what they originally requested.

[modules-droid] Source is reconfigured dynamically based on input. Fixes JB#47579
This commit is contained in:
Juho Hämäläinen 2019-10-03 17:03:20 +03:00
parent 185fb1add0
commit 70b624d663

View file

@ -473,6 +473,64 @@ static void update_latency(struct userdata *u) {
pa_log_debug("Set fixed latency %" PRIu64 " usec", pa_bytes_to_usec(u->buffer_size, &u->stream->input->sample_spec));
}
static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_output_new_data *new_data, struct userdata *u) {
pa_channel_map old_channel_map;
pa_sample_spec old_sample_spec;
pa_channel_map new_channel_map;
pa_sample_spec new_sample_spec;
pa_queue *source_outputs = NULL;
/* Not meant for us */
if (new_data->source != u->source)
return PA_HOOK_OK;
if (pa_sample_spec_equal(&new_data->sample_spec, pa_droid_stream_sample_spec(u->stream)) &&
pa_channel_map_equal(&new_data->channel_map, pa_droid_stream_channel_map(u->stream)))
return PA_HOOK_OK;
pa_log_info("New source-output connecting and our source needs to be reconfigured.");
if (pa_source_used_by(u->source)) {
/* If we already have connected source outputs detach those
* so that when re-attaching them to our source resampling etc.
* is renegotiated correctly. */
source_outputs = pa_source_move_all_start(u->source, NULL);
}
pa_source_suspend(u->source, true, PA_SUSPEND_UNAVAILABLE);
old_channel_map = *pa_droid_stream_channel_map(u->stream);
old_sample_spec = *pa_droid_stream_sample_spec(u->stream);
new_channel_map = new_data->channel_map;
new_sample_spec = new_data->sample_spec;
pa_droid_stream_unref(u->stream);
if (!(u->stream = pa_droid_open_input_stream(u->hw_module, &new_sample_spec, &new_channel_map)))
u->stream = pa_droid_open_input_stream(u->hw_module, &old_sample_spec, &old_channel_map);
if (u->stream) {
/* We need to be really careful here as we are modifying
* quite profound internal structures. */
new_sample_spec = *pa_droid_stream_sample_spec(u->stream);
new_channel_map = *pa_droid_stream_channel_map(u->stream);
u->source->channel_map = new_channel_map;
u->source->sample_spec = new_sample_spec;
pa_assert_se(pa_cvolume_remap(&u->source->reference_volume, &old_channel_map, &new_channel_map));
pa_assert_se(pa_cvolume_remap(&u->source->real_volume, &old_channel_map, &new_channel_map));
pa_assert_se(pa_cvolume_remap(&u->source->soft_volume, &old_channel_map, &new_channel_map));
pa_log_info("Source reconfigured.");
}
update_latency(u);
pa_source_suspend(u->source, false, PA_SUSPEND_UNAVAILABLE);
if (source_outputs && u->source) {
pa_source_move_all_finish(u->source, source_outputs, false);
}
return PA_HOOK_OK;
}
pa_source *pa_droid_source_new(pa_module *m,
pa_modargs *ma,
const char *driver,
@ -686,6 +744,9 @@ pa_source *pa_droid_source_new(pa_module *m,
pa_droid_stream_set_data(u->stream, u->source);
pa_source_put(u->source);
/* As late as possible */
pa_module_hook_connect(u->module, &u->module->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_NEW], PA_HOOK_LATE * 2, (pa_hook_cb_t) source_output_new_hook_callback, u);
return u->source;
fail: