feat: protocol improvements — live trunking, mini-frames, noise suppression, adaptive jitter
T6 wiring: Trunking in relay hot path - TrunkedForwarder wraps transport with TrunkBatcher - run_participant uses 5ms flush timer when trunking enabled - send_trunk/recv_trunk on QuinnTransport - --trunking flag on relay config - 2 new tests: forwarder batches, auto-flush on full T7 wiring: Mini-frames in encoder/decoder - MediaPacket::encode_compact/decode_compact with MiniFrameContext - CallEncoder sends mini-headers for consecutive frames (full every 50th) - CallDecoder auto-detects full vs mini on receive - mini_frames_enabled in CallConfig (default true) - 3 new tests: encode/decode sequence, periodic full, disabled mode Noise suppression (nnnoiseless/RNNoise) - NoiseSupressor in wzp-codec: pure Rust ML-based noise removal - Processes 960-sample frames as two 480-sample halves - Integrated in CallEncoder before silence detection - noise_suppression in CallConfig (default true) - 4 new tests: creation, processing, SNR improvement, passthrough T1-S4: Adaptive playout delay - AdaptivePlayoutDelay: EMA-based jitter tracking (NetEq-inspired) - Computes target_delay from observed inter-arrival jitter - JitterBuffer::new_adaptive() uses adaptive delay - adaptive_jitter in CallConfig (default true) - 5 new tests: stable, jitter increase, recovery, clamping, estimate 272 tests passing across all crates. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -6,6 +6,7 @@
|
||||
use async_trait::async_trait;
|
||||
use std::sync::Mutex;
|
||||
|
||||
use wzp_proto::packet::TrunkFrame;
|
||||
use wzp_proto::{MediaPacket, MediaTransport, PathQuality, SignalMessage, TransportError};
|
||||
|
||||
use crate::datagram;
|
||||
@@ -36,6 +37,47 @@ impl QuinnTransport {
|
||||
pub fn max_datagram_size(&self) -> Option<usize> {
|
||||
datagram::max_datagram_payload(&self.connection)
|
||||
}
|
||||
|
||||
/// Send an encoded [`TrunkFrame`] as a single QUIC datagram.
|
||||
pub fn send_trunk(&self, frame: &TrunkFrame) -> Result<(), TransportError> {
|
||||
let data = frame.encode();
|
||||
|
||||
if let Some(max_size) = self.connection.max_datagram_size() {
|
||||
if data.len() > max_size {
|
||||
return Err(TransportError::DatagramTooLarge {
|
||||
size: data.len(),
|
||||
max: max_size,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
self.connection.send_datagram(data).map_err(|e| {
|
||||
TransportError::Internal(format!("send trunk datagram error: {e}"))
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Receive a single QUIC datagram and decode it as a [`TrunkFrame`].
|
||||
///
|
||||
/// Returns `Ok(None)` on connection close, `Ok(Some(frame))` on success,
|
||||
/// or an error on malformed data / transport failure.
|
||||
pub async fn recv_trunk(&self) -> Result<Option<TrunkFrame>, TransportError> {
|
||||
let data = match self.connection.read_datagram().await {
|
||||
Ok(data) => data,
|
||||
Err(quinn::ConnectionError::ApplicationClosed(_)) => return Ok(None),
|
||||
Err(quinn::ConnectionError::LocallyClosed) => return Ok(None),
|
||||
Err(e) => {
|
||||
return Err(TransportError::Internal(format!(
|
||||
"recv trunk datagram error: {e}"
|
||||
)))
|
||||
}
|
||||
};
|
||||
|
||||
TrunkFrame::decode(&data)
|
||||
.map(Some)
|
||||
.ok_or_else(|| TransportError::Internal("malformed trunk frame".into()))
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
|
||||
Reference in New Issue
Block a user