[feat](trx-rs): persistent multi-channel virtual channels with OOB eviction

Allow users to allocate multiple virtual channels independently of
browser tab count. Channels survive SDR center-frequency retuning as
long as they stay within the capture bandwidth; channels that fall
outside the SDR span are automatically destroyed.

Changes:
- trx-core: add AUDIO_MSG_VCHAN_DESTROYED (0x12) wire constant;
  add default subscribe_destroyed() to VirtualChannelManager trait
- trx-backend-soapysdr: update_center_hz() detects OOB channels,
  removes them, fires destroyed_tx broadcast; add destroyed_sender()
  and subscribe_destroyed() override
- trx-server/audio: recv_destroyed() helper avoids select! busy-loop
  for non-SDR backends; send AUDIO_MSG_VCHAN_DESTROYED to client when
  a channel is evicted server-side
- trx-client/audio_client: persist active_subs across TCP reconnects,
  re-subscribe on reconnect; handle AUDIO_MSG_VCHAN_DESTROYED by
  pruning vchan_audio map and forwarding UUID via vchan_destroyed_tx
- trx-frontend/lib: add vchan_destroyed broadcast field to
  FrontendRuntimeContext
- trx-client/main: wire vchan_destroyed_tx into audio client and
  frontend runtime context
- trx-frontend-http/vchan: remove per-session one-channel limit in
  allocate(); replace auto-evict in release_session_on_rig() with
  subscriber-count-only update; add remove_by_uuid() for server-
  triggered OOB destruction (skips redundant VChanAudioCmd::Remove)
- trx-frontend-http/server: spawn background task that forwards
  vchan_destroyed broadcast to ClientChannelManager.remove_by_uuid()

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
This commit is contained in:
2026-03-11 20:50:49 +01:00
parent 4e93dcc82a
commit 60267d450b
9 changed files with 215 additions and 45 deletions
+3
View File
@@ -56,6 +56,9 @@ pub const AUDIO_MSG_VCHAN_MODE: u8 = 0x10;
/// Client → server: remove a virtual channel (stops encoding and destroys the DSP pipeline).
/// Payload: 16-byte UUID of the virtual channel on the server.
pub const AUDIO_MSG_VCHAN_REMOVE: u8 = 0x11;
/// Server → client: a virtual channel was destroyed server-side (e.g. went out of bandwidth).
/// Payload: 16-byte UUID of the destroyed channel.
pub const AUDIO_MSG_VCHAN_DESTROYED: u8 = 0x12;
/// Maximum payload size for normal messages (1 MB).
const MAX_PAYLOAD_SIZE: u32 = 1_048_576;
+12
View File
@@ -117,6 +117,18 @@ pub trait VirtualChannelManager: Send + Sync {
/// Maximum number of channels (including the primary channel).
fn max_channels(&self) -> usize;
/// Subscribe to server-side channel destruction events.
///
/// Returns a `broadcast::Receiver<Uuid>` that fires whenever the manager
/// destroys a channel (e.g. because it went out of the SDR capture
/// bandwidth). The default implementation returns an immediately-closed
/// receiver so non-SDR backends do not need to override this.
fn subscribe_destroyed(&self) -> broadcast::Receiver<Uuid> {
// Drop the sender immediately; the receiver will resolve to
// `Err(RecvError::Closed)` on first poll, signalling "no events".
broadcast::channel::<Uuid>(1).1
}
}
/// Convenience alias used in `RigHandle`.