Skip to main content

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}