In the future, when port changes on a device, streams connected to that device will be killed, unless a router module rescues the streams first. This patch implements that rescue logic in module-skoa-router. --- src/modules/module-skoa-router.c | 59 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/src/modules/module-skoa-router.c b/src/modules/module-skoa-router.c index d532e54..23796cf 100644 --- a/src/modules/module-skoa-router.c +++ b/src/modules/module-skoa-router.c @@ -35,6 +35,7 @@ PA_MODULE_LOAD_ONCE(true); struct userdata { pa_hook_slot *card_set_profile_slot; + pa_hook_slot *device_set_port_slot; }; static pa_hook_result_t card_set_profile_cb(void *hook_data, void *call_data, void *userdata) { @@ -140,6 +141,59 @@ static pa_hook_result_t card_set_profile_cb(void *hook_data, void *call_data, vo return PA_HOOK_OK; } +static pa_hook_result_t device_set_port_cb(void *hook_data, void *call_data, void *userdata) { + pa_device_set_port_hook_data *data = call_data; + pa_sink *sink = NULL; + pa_source *source = NULL; + const char *device_name; + pa_queue *streams = NULL; + + pa_assert(data); + + /* When the port changes, streams connected to the device are killed. Here + * we prevent that by detaching the streams before the port switch, and + * attaching them to the same device after the port switch. */ + + if (data->port->direction == PA_DIRECTION_OUTPUT) { + sink = data->port->device; + device_name = sink->name; + streams = pa_sink_move_all_start(sink, streams); + } else { + source = data->port->device; + device_name = source->name; + streams = pa_source_move_all_start(source, streams); + } + + if (!pa_queue_isempty(streams)) + pa_log_debug("Started to move streams away from device %s.", device_name); + + if (sink) + data->ret = pa_sink_set_port(sink, data->port, data->save, true); + else + data->ret = pa_source_set_port(source, data->port, data->save, true); + + data->ret_valid = true; + + if (data->ret >= 0) { + if (sink) + pa_sink_move_all_finish(sink, streams, false); + else + pa_source_move_all_finish(source, streams, false); + + pa_log_debug("Finished moving streams to device %s.", device_name); + } else { + if (!pa_queue_isempty(streams)) + pa_log("Port change failed, have to fail the stream rescue operation."); + + if (sink) + pa_sink_move_all_fail(streams); + else + pa_source_move_all_fail(streams); + } + + return PA_HOOK_OK; +} + int pa__init(pa_module *module) { struct userdata *u; @@ -148,6 +202,8 @@ int pa__init(pa_module *module) { module->userdata = u = pa_xnew0(struct userdata, 1); u->card_set_profile_slot = pa_hook_connect(&module->core->hooks[PA_CORE_HOOK_CARD_SET_PROFILE], PA_HOOK_NORMAL, card_set_profile_cb, NULL); + u->device_set_port_slot = pa_hook_connect(&module->core->hooks[PA_CORE_HOOK_DEVICE_SET_PORT], PA_HOOK_NORMAL, + device_set_port_cb, NULL); return 0; } @@ -160,6 +216,9 @@ void pa__done(pa_module *module) { if (!(u = module->userdata)) return; + if (u->device_set_port_slot) + pa_hook_slot_free(u->device_set_port_slot); + if (u->card_set_profile_slot) pa_hook_slot_free(u->card_set_profile_slot); -- 1.8.3.1