mirror of
https://github.com/permissionlesstech/bitchat.git
synced 2026-07-25 23:25:20 +00:00
Harden PTT audio and the 1.7.1 release (#1423)
Centralize PTT and voice audio-session ownership, harden courier/bridge/outbox delivery and recovery, correct location and delivery-state races, add privacy/release metadata, and ship reproducible universal Arti slices with Release CI coverage. Validated by the full iOS suite, repeated audio/fragment/performance regressions, BitFoundation tests, strict lint and dead-code analysis, universal iOS Release builds, and iOS/macOS archives.
This commit is contained in:
@@ -141,6 +141,12 @@ final class ChatLiveVoiceCoordinator {
|
||||
private var fileManager: FileManager { fileStore.fileManager }
|
||||
private var assemblies: [AssemblyKey: Assembly] = [:]
|
||||
private var finishedBursts: [AssemblyKey: FinishedBurst] = [:]
|
||||
/// Players still draining after their assembly — the sole strong owner —
|
||||
/// was discarded on burst END. Without this hold the player deallocates
|
||||
/// mid-tail (or mid session-acquire, killing the whole burst's audio)
|
||||
/// and its registered session token would only be reclaimed by the
|
||||
/// deinit backstop. Entries remove themselves via `onStopped`.
|
||||
private var drainingPlayers: [ObjectIdentifier: PTTBurstPlayer] = [:]
|
||||
|
||||
/// `sweepsOnInit` exists for tests whose coordinator shares the real
|
||||
/// application-support directory: they pass `false` so parallel test
|
||||
@@ -465,7 +471,18 @@ final class ChatLiveVoiceCoordinator {
|
||||
return
|
||||
}
|
||||
|
||||
assembly.player?.finishAfterDrain()
|
||||
if let player = assembly.player, !player.stopped {
|
||||
// Park the draining player: this method just dropped the
|
||||
// assembly, and nothing else holds the player strongly. It may
|
||||
// still be playing out its tail — or still acquiring the audio
|
||||
// session off-main — so it must stay alive until it stops.
|
||||
let id = ObjectIdentifier(player)
|
||||
drainingPlayers[id] = player
|
||||
player.onStopped = { [weak self] in
|
||||
self?.drainingPlayers.removeValue(forKey: id)
|
||||
}
|
||||
player.finishAfterDrain()
|
||||
}
|
||||
// The bubble's waveform may have been computed from a partial file.
|
||||
WaveformCache.shared.purge(url: assembly.fileURL)
|
||||
// The capture is the bubble's replayable audio from here on (unless a
|
||||
|
||||
Reference in New Issue
Block a user