rtc_interceptor/packet.rs
1use shared::TransportMessage;
2use std::any::Any;
3use std::sync::Arc;
4
5/// RTP/RTCP Packet
6///
7/// An enum representing either an RTP or RTCP packet that can be processed
8/// by interceptors in the chain.
9#[derive(Debug, Clone, PartialEq)]
10#[non_exhaustive]
11pub enum Packet {
12 /// RTP (Real-time Transport Protocol) packet containing media data
13 Rtp(rtp::Packet),
14 /// RTCP (RTP Control Protocol) packets for feedback and statistics
15 Rtcp(Vec<Box<dyn rtcp::Packet>>),
16}
17
18/// A fact about a packet, attached by one interceptor and readable by the rest.
19///
20/// # Why it rides with the packet
21///
22/// Because it has to survive the journey. An interceptor that queues a packet and emits it later
23/// puts it back on the belt *behind* itself, where every interceptor ahead sees it again — so what
24/// was learned about that packet has to travel with it or be worked out twice. A side channel
25/// cannot manage that: it has no way to say *which* packet it refers to once the packet has been
26/// held, reordered or duplicated.
27///
28/// With `Ein`/`Eout` left as `()`, this is the only way information crosses interceptors.
29///
30/// # Cost
31///
32/// [`AttributedPacket`] holds a `Vec`, which does not allocate while empty — and most packets carry
33/// nothing. Lookup is a linear scan of a handful of words, cheaper than hashing.
34#[derive(Clone)]
35#[non_exhaustive]
36pub enum Attribute {
37 /// Rebuilt by FEC rather than received: these bytes never arrived on the wire.
38 ///
39 /// A NACK generator that sees this must not ask for the packet again.
40 RecoveredByFec,
41
42 /// A retransmission answering a NACK, not a first transmission.
43 ///
44 /// Still new bytes on the wire, which is why a send history has to count it — counting it as
45 /// an original tells a bandwidth estimator the path is carrying less than it is.
46 Retransmission,
47
48 /// Inbound RTCP an interceptor has decided the application should see.
49 ///
50 /// [`NoopInterceptor`](crate::NoopInterceptor) ends the inbound RTCP path unless the packet
51 /// carries this, which makes forwarding a per-packet judgement by whichever interceptor is
52 /// qualified to make it, rather than a chain-wide setting.
53 DeliverToApplication,
54
55 /// The congestion controller's estimate, in bits per second.
56 ///
57 /// Rides outbound so the pacer reads it on the way past. A bitrate is connection state rather
58 /// than a property of the packet carrying it — it travels this way because with no event
59 /// channel there is nowhere else for it to go.
60 TargetBitrateChanged {
61 /// The new target, in bits per second.
62 bits_per_second: f64,
63 },
64
65 /// Ask the keyframe generator to send a Picture Loss Indication now.
66 ForcePli {
67 /// Which streams to request a keyframe for, or `None` for all of them.
68 ssrcs: Option<Vec<u32>>,
69 },
70
71 /// Anything an application defines, so this enum is never a bottleneck on it.
72 ///
73 /// `Arc` rather than `Box` so an attributed packet stays cheap to clone — the NACK responder
74 /// clones into its send buffer and the FEC encoder into its block, and neither should deep-copy
75 /// an application's payload. Reached by type: [`AttributedPacket::custom`].
76 Custom(Arc<dyn Any + Send + Sync>),
77}
78
79impl std::fmt::Debug for Attribute {
80 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
81 match self {
82 Self::RecoveredByFec => f.write_str("RecoveredByFec"),
83 Self::Retransmission => f.write_str("Retransmission"),
84 Self::DeliverToApplication => f.write_str("DeliverToApplication"),
85 Self::TargetBitrateChanged { bits_per_second } => f
86 .debug_struct("TargetBitrateChanged")
87 .field("bits_per_second", bits_per_second)
88 .finish(),
89 Self::ForcePli { ssrcs } => f.debug_struct("ForcePli").field("ssrcs", ssrcs).finish(),
90 // The payload is `dyn Any`, which is not `Debug`.
91 Self::Custom(_) => f.write_str("Custom(..)"),
92 }
93 }
94}
95
96/// A packet together with what the interceptors have learned about it.
97#[derive(Clone, Debug)]
98pub struct AttributedPacket {
99 /// What the interceptors have attached on the way, in the order they attached it.
100 pub attributes: Vec<Attribute>,
101 /// The packet itself.
102 pub packet: Packet,
103}
104
105impl AttributedPacket {
106 /// A packet with nothing attached.
107 pub fn new(packet: Packet) -> Self {
108 Self {
109 attributes: Vec::new(),
110 packet,
111 }
112 }
113 /// Attach `attribute`, taking ownership — for building a packet in one expression.
114 pub fn with(mut self, attribute: Attribute) -> Self {
115 self.attributes.push(attribute);
116 self
117 }
118 /// Attach `attribute`.
119 ///
120 /// Attaching the same one twice is allowed and means nothing extra; [`has`](Self::has) answers
121 /// the only question anyone asks of it.
122 pub fn add(&mut self, attribute: Attribute) -> &mut Self {
123 self.attributes.push(attribute);
124 self
125 }
126
127 /// Whether this packet carries `attribute`.
128 ///
129 /// Compares by variant, not by value, so `has(&Attribute::TargetBitrateChanged { .. })` finds
130 /// one whatever the rate is, and [`Attribute::Custom`] never satisfies a query for a built-in.
131 pub fn has(&self, attribute: &Attribute) -> bool {
132 let wanted = std::mem::discriminant(attribute);
133 self.attributes
134 .iter()
135 .any(|held| std::mem::discriminant(held) == wanted)
136 }
137
138 /// The first attribute matching `attribute`'s variant, for reading a value out of it.
139 pub fn get(&self, attribute: &Attribute) -> Option<&Attribute> {
140 let wanted = std::mem::discriminant(attribute);
141 self.attributes
142 .iter()
143 .find(|held| std::mem::discriminant(*held) == wanted)
144 }
145
146 /// The first [`Attribute::Custom`] payload of type `T`, if this packet carries one.
147 pub fn custom<T: Any + Send + Sync>(&self) -> Option<&T> {
148 self.attributes
149 .iter()
150 .find_map(|attribute| match attribute {
151 Attribute::Custom(value) => value.downcast_ref::<T>(),
152 _ => None,
153 })
154 }
155}
156
157impl From<Packet> for AttributedPacket {
158 fn from(packet: Packet) -> Self {
159 Self::new(packet)
160 }
161}
162
163/// Tagged packet with transport metadata.
164///
165/// A [`TransportMessage`] wrapping a [`Packet`], which includes transport-level
166/// context such as source/destination addresses and protocol information.
167/// This is the primary message type passed through interceptor chains.
168pub type TaggedPacket = TransportMessage<AttributedPacket>;