fix(audio_backend): make sink-mapping selection order-independent
With a sink-mapping configured, sinkInfoCb could report the wrong sink's volume depending on the order in which PulseAudio enumerated sinks during a pa_context_get_sink_info_list sweep. The mapping override was keyed on the mutable current_sink_name_ and ran before the 'pick a running sink' fallback, which also mutated current_sink_name_. If the default sink was running while the mapped target was suspended, the fallback could reassign the selection to the default sink after the mapping had already matched, so the reported sink depended on enumeration order (and each sweep wrote the state twice, causing a flicker). Resolve the target up front: key the mapping on the stable default_sink_name and, when a mapping is in effect, treat the mapped target sink as the sole definitive selection - every other sink is ignored and the running-sink fallback is skipped. The default-sink + running-fallback behavior is unchanged when no mapping applies. Verified in isolation across all sink enumeration orders.
This commit is contained in:
+36
-20
@@ -239,29 +239,45 @@ void AudioBackend::sinkInfoCb(pa_context* /*context*/, const pa_sink_info* i, in
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (const auto mapping = backend->sink_mapping_.find(backend->current_sink_name_);
|
// Resolve which sink to report deterministically, independent of the order in
|
||||||
|
// which PulseAudio enumerates sinks during a sink_info_list sweep.
|
||||||
|
//
|
||||||
|
// When the default sink has an explicit mapping, the mapped target sink is the
|
||||||
|
// definitive selection ("the sink named by the value is considered to be the
|
||||||
|
// current sink instead of the one named by the key"): only that target sink may
|
||||||
|
// write the reported volume/mute state. The mapping is keyed on the stable
|
||||||
|
// default_sink_name (set once per server-info update) rather than on the mutable
|
||||||
|
// current_sink_name_. Previously the mapping override and the "pick a running
|
||||||
|
// sink" fallback both mutated current_sink_name_ mid-sweep, so the mapping could
|
||||||
|
// be overridden by whichever running sink happened to be enumerated, and the
|
||||||
|
// reported volume depended on enumeration order.
|
||||||
|
if (const auto mapping = backend->sink_mapping_.find(backend->default_sink_name);
|
||||||
mapping != backend->sink_mapping_.end()) {
|
mapping != backend->sink_mapping_.end()) {
|
||||||
if (i->name == mapping->second) {
|
if (i->name != mapping->second) {
|
||||||
backend->current_sink_name_ = i->name;
|
// A mapping is in effect but this is not the mapped target sink; ignore it so
|
||||||
|
// it can never override the target's reported volume.
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
backend->default_sink_running_ = backend->default_sink_name == i->name &&
|
|
||||||
(i->state == PA_SINK_RUNNING || i->state == PA_SINK_IDLE);
|
|
||||||
|
|
||||||
if (i->name != backend->default_sink_name && i->name != backend->current_sink_name_ &&
|
|
||||||
!backend->default_sink_running_) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (backend->current_sink_name_ == i->name) {
|
|
||||||
backend->current_sink_running_ = (i->state == PA_SINK_RUNNING || i->state == PA_SINK_IDLE);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!backend->current_sink_running_ &&
|
|
||||||
(i->state == PA_SINK_RUNNING || i->state == PA_SINK_IDLE)) {
|
|
||||||
backend->current_sink_name_ = i->name;
|
backend->current_sink_name_ = i->name;
|
||||||
backend->current_sink_running_ = true;
|
backend->current_sink_running_ = (i->state == PA_SINK_RUNNING || i->state == PA_SINK_IDLE);
|
||||||
|
} else {
|
||||||
|
backend->default_sink_running_ = backend->default_sink_name == i->name &&
|
||||||
|
(i->state == PA_SINK_RUNNING || i->state == PA_SINK_IDLE);
|
||||||
|
|
||||||
|
if (i->name != backend->default_sink_name && i->name != backend->current_sink_name_ &&
|
||||||
|
!backend->default_sink_running_) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (backend->current_sink_name_ == i->name) {
|
||||||
|
backend->current_sink_running_ = (i->state == PA_SINK_RUNNING || i->state == PA_SINK_IDLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!backend->current_sink_running_ &&
|
||||||
|
(i->state == PA_SINK_RUNNING || i->state == PA_SINK_IDLE)) {
|
||||||
|
backend->current_sink_name_ = i->name;
|
||||||
|
backend->current_sink_running_ = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (backend->current_sink_name_ == i->name) {
|
if (backend->current_sink_name_ == i->name) {
|
||||||
|
|||||||
Reference in New Issue
Block a user