mirror of
https://github.com/AsamK/signal-cli.git
synced 2026-08-24 05:16:45 +00:00
Clean up PulseAudio virtual devices after tunnel exits
signal-call-tunnel creates PulseAudio module-null-sink and module-remap-source modules via virtual_audio.sh and relies on VirtualAudioDevicePair::drop() to unload them on exit. However, the tunnel is terminated with SIGTERM (Process.destroy()), and Rust Drop implementations do not run on signal termination, so the modules are never unloaded and accumulate across calls. Fix: run teardown in the process.onExit() callback, after the tunnel has exited, by calling pactl list modules short and unloading any module whose arguments contain the stored inputDeviceName or outputDeviceName for the call.
This commit is contained in:
parent
6bef205b3f
commit
09d5f93c6c
@ -409,6 +409,9 @@ public class CallManager implements AutoCloseable {
|
||||
// Monitor process exit
|
||||
process.onExit().thenAcceptAsync(p -> {
|
||||
logger.debug("Tunnel for call {} exited with code {}", callIdUnsigned(state.callId), p.exitValue());
|
||||
// SIGTERM (code 143) kills the Rust process without running Drop,
|
||||
// so VirtualAudioDevicePair::drop() never fires. Clean up here.
|
||||
teardownVirtualAudioDevices(state);
|
||||
if (activeCalls.containsKey(state.callId)) {
|
||||
endCall(state.callId, "tunnel_exit");
|
||||
}
|
||||
@ -748,6 +751,41 @@ public class CallManager implements AutoCloseable {
|
||||
}
|
||||
}
|
||||
|
||||
private void teardownVirtualAudioDevices(CallState state) {
|
||||
var inputDevice = state.inputDeviceName;
|
||||
var outputDevice = state.outputDeviceName;
|
||||
if (inputDevice == null && outputDevice == null) {
|
||||
logger.debug("No virtual audio device names for call {}, skipping teardown", callIdUnsigned(state.callId));
|
||||
return;
|
||||
}
|
||||
try {
|
||||
var listProc = new ProcessBuilder("pactl", "list", "modules", "short")
|
||||
.redirectErrorStream(true)
|
||||
.start();
|
||||
var output = new String(listProc.getInputStream().readAllBytes(), StandardCharsets.UTF_8);
|
||||
listProc.waitFor(5, TimeUnit.SECONDS);
|
||||
for (var line : output.lines().toList()) {
|
||||
var parts = line.split("\t");
|
||||
if (parts.length < 1) continue;
|
||||
var moduleId = parts[0].trim();
|
||||
if (moduleId.isEmpty()) continue;
|
||||
var matches = (inputDevice != null && line.contains(inputDevice))
|
||||
|| (outputDevice != null && line.contains(outputDevice));
|
||||
if (!matches) continue;
|
||||
try {
|
||||
new ProcessBuilder("pactl", "unload-module", moduleId)
|
||||
.start()
|
||||
.waitFor(5, TimeUnit.SECONDS);
|
||||
logger.debug("Unloaded PulseAudio module {} for call {}", moduleId, callIdUnsigned(state.callId));
|
||||
} catch (Exception e) {
|
||||
logger.debug("Failed to unload PulseAudio module {} for call {}: {}", moduleId, callIdUnsigned(state.callId), e.getMessage());
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.debug("Failed to list PulseAudio modules for call {}", callIdUnsigned(state.callId), e);
|
||||
}
|
||||
}
|
||||
|
||||
private static long generateCallId() {
|
||||
return new BigInteger(64, new SecureRandom()).longValue();
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user