When the active profile of a card becomes unavailable and no other module changes it to a better profile (i.e. there are no available ports that module-switch-on-port-available could switch to) the card will be stuck on an unavailable profile with a non-working sink/source and any active streams connected to that sink/source will remain connected. This commit switches to a different profile when the active profile becomes unavailble, looking for a profile with availability yes or unknown with the highest priority, and ultimately fall-backing to the OFF profile. With this fix a card that only has one port can have the streams connected to its sink/source moved away by module-rescue-stream when that port becomes unavailable. This has been seen on machines with AMD graphics, where the HDMI port lives on a separate ALSA card that only has that port. --- src/modules/alsa/module-alsa-card.c | 54 +++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/src/modules/alsa/module-alsa-card.c b/src/modules/alsa/module-alsa-card.c index 041d53121..1fc5216fc 100644 --- a/src/modules/alsa/module-alsa-card.c +++ b/src/modules/alsa/module-alsa-card.c @@ -735,6 +735,57 @@ static pa_hook_result_t source_output_unlink_hook_callback(pa_core *c, pa_source return PA_HOOK_OK; } +static pa_card_profile *find_best_profile(pa_card *card) { + pa_card_profile *profile = NULL; + pa_card_profile *best_profile = NULL; + void *state; + + pa_assert(card); + best_profile = pa_hashmap_get(card->profiles, "off"); + + PA_HASHMAP_FOREACH(profile, card->profiles, state) { + if (profile->available == PA_AVAILABLE_NO) + continue; + + if (!pa_card_profile_has_available_ports(profile, PA_DIRECTION_OUTPUT, PA_AVAILABLE_YES)) + continue; + + if (profile->priority > best_profile->priority) + best_profile = profile; + } + + if (pa_safe_streq(best_profile->name, "off")) { + PA_HASHMAP_FOREACH(profile, card->profiles, state) { + if (profile->available == PA_AVAILABLE_NO) + continue; + + if (!pa_card_profile_has_available_ports(profile, PA_DIRECTION_OUTPUT, PA_AVAILABLE_UNKNOWN)) + continue; + + if (profile->priority > best_profile->priority) + best_profile = profile; + } + } + + return best_profile; +} + +static pa_hook_result_t card_profile_available_changed(pa_core *c, pa_card_profile *profile, struct userdata *u) { + pa_card *card; + + pa_assert(profile); + pa_assert_se(card = profile->card); + + if (profile->available != PA_AVAILABLE_NO) + return PA_HOOK_OK; + + if (!pa_safe_streq(profile->name, card->active_profile->name)) + return PA_HOOK_OK; + + pa_log_debug("Active profile %s on card %s became unavailable, switching to another profile", profile->name, card->name); + return pa_card_set_profile(card, find_best_profile(card), false); +} + int pa__init(pa_module *m) { pa_card_new_data data; bool ignore_dB = false; @@ -912,6 +963,9 @@ int pa__init(pa_module *m) { init_jacks(u); + pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_CARD_PROFILE_AVAILABLE_CHANGED], PA_HOOK_NORMAL, + (pa_hook_cb_t) card_profile_available_changed, u); + pa_card_choose_initial_profile(u->card); /* If the "profile" modarg is given, we have to override whatever the usual -- 2.18.0