citadel_sdk/media/config.rs
1use super::error::MediaResultExt;
2use citadel_io::time::Duration;
3use citadel_media::{MediaConfig, FRAGMENT_HEADER_LEN};
4use citadel_proto::prelude::NetworkError;
5
6/// Recommended upper bound on the plaintext bytes handed to the UDP channel per
7/// datagram across every transport. Measured floors: the P2P QUIC connection
8/// reports a 1162-byte datagram ceiling at spawn (=> 1066 B payload after the
9/// 64-byte HDP header + 32-byte AEAD at `SecurityLevel::Standard`); the raw
10/// hole-punched socket allows 1232 B datagrams (=> 1136 B). 1024 fits both
11/// with headroom for one extra AEAD layer.
12///
13/// The endpoint verifies the budget against the live transport's
14/// `OutboundUdpSender::max_payload_len()`; this constant is only a safe
15/// starting point for callers.
16pub const RECOMMENDED_UDP_PAYLOAD_BUDGET: usize = 1024;
17
18/// Recommended [`MediaConfig::max_fragment_payload`]:
19/// `RECOMMENDED_UDP_PAYLOAD_BUDGET - FRAGMENT_HEADER_LEN` = 1004, rounded down.
20pub const RECOMMENDED_FRAGMENT_PAYLOAD: usize = 1000;
21
22/// Transport tunables for a [`super::MediaEndpoint`]. Every field is mandatory
23/// (no `Default`); call [`MediaTransportConfig::validate`] before use.
24#[derive(Debug, Clone, Copy, PartialEq, Eq)]
25pub struct MediaTransportConfig {
26 /// Packetizer / reassembler / jitter-buffer settings.
27 pub media: MediaConfig,
28 /// Capacity of the outbound [`citadel_media::SendQueue`] in frames.
29 pub send_queue_frames: usize,
30 /// Maximum plaintext bytes per datagram handed to the unreliable sink.
31 /// Must be at least `media.max_fragment_payload + FRAGMENT_HEADER_LEN`.
32 pub udp_payload_budget: usize,
33 /// How long to wait for the session's UDP channel before falling back to
34 /// the ordered-reliable channel.
35 pub udp_wait: Duration,
36}
37
38impl MediaTransportConfig {
39 pub fn validate(&self) -> Result<(), NetworkError> {
40 self.media.validate().net()?;
41 if self.send_queue_frames == 0 {
42 return Err(citadel_io::error!(
43 citadel_io::ErrorCode::MediaConfigInvalid,
44 "send_queue_frames must be > 0"
45 ));
46 }
47 let datagram = self
48 .media
49 .max_fragment_payload
50 .saturating_add(FRAGMENT_HEADER_LEN);
51 if datagram > self.udp_payload_budget {
52 return Err(citadel_io::error!(
53 citadel_io::ErrorCode::MediaConfigInvalid,
54 format!(
55 "max_fragment_payload + {FRAGMENT_HEADER_LEN} = {datagram} exceeds udp_payload_budget {}",
56 self.udp_payload_budget
57 )
58 ));
59 }
60 Ok(())
61 }
62
63 /// The largest datagram this config will ever emit.
64 pub const fn max_datagram_len(&self) -> usize {
65 self.media.max_fragment_payload + FRAGMENT_HEADER_LEN
66 }
67}