1-
21//! Room-level Top-N audio selector.
32//!
43//! A single async task per room that:
@@ -50,12 +49,14 @@ use std::{
5049use futures_concurrency:: stream:: StreamGroup ;
5150use futures_concurrency:: stream:: stream_group:: Key ;
5251use futures_lite:: stream:: Stream as _;
53- use pulsebeam_runtime:: sync:: spmc;
52+ use pulsebeam_runtime:: sync:: { Arc , spmc} ;
5453use tokio:: { sync:: mpsc, time:: Instant } ;
5554
5655use crate :: {
57- controller:: MAX_SEND_AUDIO_SLOTS , entity:: TrackId , rtp:: RtpPacket , rtp:: monitor:: StreamState ,
58- track:: TrackReceiver ,
56+ controller:: MAX_SEND_AUDIO_SLOTS ,
57+ entity:: TrackId ,
58+ rtp:: { AudioRtpPacket , RtpPacket , monitor:: StreamState } ,
59+ track:: { TrackMeta , TrackReceiver } ,
5960} ;
6061
6162/// Number of output slots produced by the selector; matches the controller's
@@ -84,15 +85,15 @@ pub struct AudioSelectorSubscription {
8485 /// slots. Slot 0 is the loudest speaker, slot 1 the second-loudest, etc.
8586 /// (assignments shift after each re-rank, but the slot ordering is stable
8687 /// within a re-rank window).
87- pub receivers : Vec < spmc:: Receiver < RtpPacket > > ,
88+ pub receivers : Vec < spmc:: Receiver < AudioRtpPacket > > ,
8889}
8990
9091/// Room-side handle: send track commands and create per-participant subscriptions.
9192pub struct AudioSelectorHandle {
9293 /// Send [`AudioSelectorCmd`]s to the background task.
9394 pub cmd_tx : mpsc:: Sender < AudioSelectorCmd > ,
9495 /// One prototype receiver per slot; cloned for every subscriber.
95- receivers : Vec < spmc:: Receiver < RtpPacket > > ,
96+ receivers : Vec < spmc:: Receiver < AudioRtpPacket > > ,
9697}
9798
9899impl AudioSelectorHandle {
@@ -129,7 +130,7 @@ pub fn create(
129130 for _ in 0 ..SELECTOR_SLOTS {
130131 // 256-packet ring per slot. At 50 pkt/s that is 5 s of runway;
131132 // enough to absorb any downstream stall without dropping audio.
132- let ( tx, rx) = spmc:: channel :: < RtpPacket > ( 256 ) ;
133+ let ( tx, rx) = spmc:: channel :: < AudioRtpPacket > ( 256 ) ;
133134 senders. push ( tx) ;
134135 receivers. push ( rx) ;
135136 }
@@ -162,6 +163,7 @@ struct InputTrackMeta {
162163 /// Shared speech-intensity state maintained by the upstream `StreamMonitor`.
163164 /// Atomic loads — zero overhead on the hot path.
164165 state : StreamState ,
166+ meta : Arc < TrackMeta > ,
165167}
166168
167169/// One `spmc` receiver wrapped as a `Stream<Item=(TrackId, RtpPacket)>` so it
@@ -213,7 +215,7 @@ impl futures_lite::stream::Stream for InputStream {
213215/// One of the N output slots produced by the selector.
214216struct OutputSlot {
215217 /// Broadcast channel; all participant subscriptions share the same ring.
216- sender : spmc:: Sender < RtpPacket > ,
218+ sender : spmc:: Sender < AudioRtpPacket > ,
217219 /// Which input track is currently assigned to this slot.
218220 track_id : Option < TrackId > ,
219221}
@@ -310,12 +312,13 @@ impl TopNAudioSelector {
310312 }
311313 let sim = track. lowest_quality ( ) ;
312314 let state = sim. state . clone ( ) ;
315+ let meta = track. meta . clone ( ) ;
313316 let receiver = sim. channel . clone ( ) ;
314317 let key = self . inputs . insert ( InputStream {
315318 track_id : id,
316319 receiver,
317320 } ) ;
318- self . tracks . insert ( id, InputTrackMeta { key, state } ) ;
321+ self . tracks . insert ( id, InputTrackMeta { key, state, meta } ) ;
319322 }
320323 AudioSelectorCmd :: RemoveTrack ( id) => {
321324 self . remove_track ( id) ;
@@ -342,9 +345,19 @@ impl TopNAudioSelector {
342345 /// only occurs in the ≤200 ms window between a speaker becoming active and
343346 /// the next re-rank pass assigning them a slot.
344347 fn forward ( & mut self , track_id : TrackId , packet : RtpPacket ) {
345- if let Some ( slot) = self . slots . iter_mut ( ) . find ( |s| s. track_id == Some ( track_id) ) {
346- slot. sender . send ( packet) ;
347- }
348+ let Some ( slot) = self . slots . iter_mut ( ) . find ( |s| s. track_id == Some ( track_id) ) else {
349+ return ;
350+ } ;
351+
352+ let Some ( track) = self . tracks . get ( & track_id) else {
353+ return ;
354+ } ;
355+
356+ slot. sender . send ( AudioRtpPacket {
357+ participant_id : track. meta . origin_participant ,
358+ track_id,
359+ packet,
360+ } ) ;
348361 }
349362
350363 // ── Re-ranking ────────────────────────────────────────────────────────────
0 commit comments