rtp_rs/
builder.rs

1use crate::Seq;
2
3/// Reasons why `RtpPacketBuilder::build_info` fails
4#[derive(Debug, Ord, PartialOrd, Eq, PartialEq)]
5pub enum RtpPacketBuildError {
6    /// The target buffer is too small for the RTP packet
7    BufferTooSmall,
8
9    /// The given payload type is invalid or not set
10    PayloadTypeInvalid,
11
12    /// The extension payload is too large
13    ExtensionTooLarge,
14
15    /// The extension payload hasn't been padded to a four byte boundary
16    ExtensionMissingPadding,
17}
18
19// until we have https://github.com/rust-lang/rust/issues/51999 I think
20macro_rules! const_assert {
21    ($x:expr $(,)?) => {
22        #[allow(unknown_lints, clippy::eq_op)]
23        {
24            const ASSERT: [(); 1] = [()];
25            let _ = ASSERT[!($x) as usize];
26        }
27    };
28}
29
30/// Controls if and how an RTP packet should have padding appended after the payload
31///
32/// For example to have the builder add padding if required so that packet lengths are always a
33/// multiple of 4 bytes:
34///
35/// ```
36/// # use rtp_rs::{RtpPacketBuilder, Pad};
37/// let mut builder = RtpPacketBuilder::new()
38///     .padded(Pad::round_to(4));
39/// // configure the rest of the packet fields and then build the packet
40/// ```
41pub struct Pad(PadInner);
42
43impl Pad {
44    /// No padding should be added, and the `padding` flag in the header should not be set
45    pub const fn none() -> Self {
46        Pad(PadInner::None)
47    }
48    /// Add padding bytes so that the resulting packet length will be a multiple of the given
49    /// value, and set the `padding` flag in the packet header
50    ///
51    /// Panics if the given value is less than 2.
52    pub const fn round_to(pad: u8) -> Self {
53        const_assert!(pad >= 2);
54        Pad(PadInner::RoundTo(pad))
55    }
56}
57
58// we hide the enum so that calling code can't set tuple-variant values directly, bypassing our
59// checks for invalid values.
60#[derive(Clone)]
61enum PadInner {
62    None,
63    RoundTo(u8),
64}
65
66impl PadInner {
67    pub fn adjust_len(&self, initial_len: usize) -> Option<usize> {
68        match self {
69            PadInner::None => None,
70            PadInner::RoundTo(n) => {
71                let remainder = initial_len % *n as usize;
72                if remainder == 0 {
73                    None
74                } else {
75                    Some(*n as usize - remainder)
76                }
77            },
78        }
79    }
80}
81
82/// A new packet build which collects the data which should be written as RTP packet
83#[derive(Clone)]
84pub struct RtpPacketBuilder<'a> {
85    padded: PadInner,
86    marked: bool,
87    payload_type: u8,
88
89    extension: Option<(u16, &'a [u8])>,
90    payload: Option<&'a [u8]>,
91
92    sequence: Seq,
93    timestamp: u32,
94
95    ssrc: u32,
96    csrcs: [u32; 15],
97    csrc_count: u8
98}
99
100impl<'a> RtpPacketBuilder<'a> {
101    /// Create a new RTP packet builder
102    pub fn new() -> Self {
103        RtpPacketBuilder {
104            padded: PadInner::None,
105            marked: false,
106            /*
107             * Setting it to an invalid value enforces the user to set the payload type.
108             * This will cause the build method to fail if it hasn't been updated.
109             */
110            payload_type: 0xFF,
111
112            extension: None,
113            payload: None,
114
115            sequence: Seq::from(0),
116            timestamp: 0,
117
118            ssrc: 0,
119            csrcs: [0u32; 15],
120            csrc_count: 0
121        }
122    }
123
124    /// Set the payload type.
125    /// The type must be in range of [0; 127],
126    /// else `RtpPacketBuilder::build_info` will fail.
127    pub fn payload_type(mut self, payload_type: u8) -> Self {
128        self.payload_type = payload_type;
129        self
130    }
131
132    /// Control if and how bytes are appended to the packet if the headers and payload together
133    /// do not have an appropriate length (for instance if the length of the resulting RTP data
134    /// must be a multiple of 4 bytes).
135    ///
136    /// The default is `Pad::none()` - no padding bytes will be added and the padding flag will not
137    /// be set in the RTP header.
138    pub fn padded(mut self, pad: Pad) -> Self {
139        self.padded = pad.0;
140        self
141    }
142
143    /// Set the marker bit in the RTP header
144    pub fn marked(mut self, flag: bool) -> Self {
145        self.marked = flag;
146        self
147    }
148
149    /// Add a contributing source (csrc).
150    /// If added more than 15 contributing sources the rest will be discarded.
151    pub fn add_csrc(mut self, csrc: u32) -> Self {
152        if self.csrc_count == 15 {
153            /* The limit of contributing sources is 15. Any more should be discarded. */
154            self
155        } else {
156            self.csrcs[self.csrc_count as usize] = csrc;
157            self.csrc_count = self.csrc_count + 1;
158            self
159        }
160    }
161
162    /// Set the contributing sources (csrc).
163    /// If added more than 15 contributing sources the rest will be discarded.
164    pub fn set_csrc(mut self, csrcs: &[u32]) -> Self {
165        if csrcs.len() > 15 {
166            self.csrc_count = 15;
167        } else {
168            self.csrc_count = csrcs.len() as u8;
169        }
170
171        self.csrcs[0..self.csrc_count as usize].copy_from_slice(csrcs);
172        self
173    }
174
175    /// Set the sequence number
176    pub fn sequence(mut self, seq: Seq) -> Self {
177        self.sequence = seq;
178        self
179    }
180
181    /// Set the source for this packet
182    pub fn ssrc(mut self, ssrc: u32) -> Self {
183        self.ssrc = ssrc;
184        self
185    }
186
187    /// Set the timestamp
188    pub fn timestamp(mut self, timestamp: u32) -> Self {
189        self.timestamp = timestamp;
190        self
191    }
192
193    /// Add a custom extension payload.
194    /// The bytes should be aligned to a a four byte boundary,
195    /// else `RtpPacketBuilder::build_info` will fail.
196    pub fn extension(mut self, id: u16, payload: &'a [u8]) -> Self {
197        self.extension = Some((id, payload));
198        self
199    }
200
201    /// Set the payload of the packet
202    pub fn payload(mut self, payload: &'a [u8]) -> Self {
203        self.payload = Some(payload);
204        self
205    }
206
207    /// Calculate the target length of the packet.
208    /// This can be used to allocate a buffer for the `build_into` method.
209    pub fn target_length(&self) -> usize {
210        /* 12 is the length of the basic header */
211        let mut length = 12usize;
212        length += self.csrc_count as usize * 4;
213        length += if let Some((_, ext)) = self.extension { ext.len() + 4 } else { 0 };
214        length += if let Some(payload) = self.payload { payload.len() } else { 0 };
215        if let Some(adj) = self.padded.adjust_len(length) {
216            length += adj;
217        }
218        length
219    }
220
221    /// Build the packet into the target buffer but ignore all validity checks.
222    pub fn build_into_unchecked(&self, target: &mut [u8]) -> usize {
223        let first_byte = &mut target[0];
224        *first_byte = 2 << 6; /* The RTP packet version */
225        if self.extension.is_some() {
226            *first_byte |= 1 << 4; /* set the extension flag */
227        }
228        *first_byte |= self.csrc_count;
229
230        target[1] = self.payload_type;
231        if self.marked {
232            target[1] |= 0x80;
233        }
234
235        target[ 2] = (self.sequence.0 >>    8) as u8;
236        target[ 3] = (self.sequence.0 &  0xFF) as u8;
237
238        target[ 4] = (self.timestamp >> 24) as u8;
239        target[ 5] = (self.timestamp >> 16) as u8;
240        target[ 6] = (self.timestamp >>  8) as u8;
241        target[ 7] = (self.timestamp      ) as u8;
242
243        target[ 8] = (self.ssrc >> 24) as u8;
244        target[ 9] = (self.ssrc >> 16) as u8;
245        target[10] = (self.ssrc >>  8) as u8;
246        target[11] = (self.ssrc      ) as u8;
247
248        let mut write_index = 12usize;
249        for index in 0..self.csrc_count as usize {
250            let csrc = self.csrcs[index];
251            target[write_index + 0] = (csrc >> 24) as u8;
252            target[write_index + 1] = (csrc >> 16) as u8;
253            target[write_index + 2] = (csrc >>  8) as u8;
254            target[write_index + 3] = (csrc      ) as u8;
255
256            write_index = write_index + 4;
257        }
258
259        if let Some((id, payload)) = self.extension {
260            target[write_index + 0] = (id >>    8) as u8;
261            target[write_index + 1] = (id &  0xFF) as u8;
262
263            let len = payload.len() / 4;
264            target[write_index + 2] = (len >>    8) as u8;
265            target[write_index + 3] = (len &  0xFF) as u8;
266
267            write_index = write_index + 4;
268
269            /* the target buffer has been ensured to hold that many bytes */
270            target[write_index..(write_index + payload.len())].copy_from_slice(payload);
271            write_index += payload.len();
272        }
273
274        if let Some(payload) = self.payload {
275            /* the target buffer has been ensured to hold that many bytes */
276            target[write_index..(write_index + payload.len())].copy_from_slice(payload);
277            write_index += payload.len();
278        }
279
280        if let Some(padded_bytes) = self.padded.adjust_len(write_index) {
281            target[0] |= 1 << 5;  /* set the padded flag */
282
283            write_index += padded_bytes;
284            target[write_index - 1] = padded_bytes as u8;
285        }
286
287        write_index
288    }
289
290    /// Build the RTP packet on the target buffer.
291    /// The length of the final packet will be returned on success.
292    pub fn build_into(&self, target: &mut [u8]) -> Result<usize, RtpPacketBuildError> {
293        if target.len() < self.target_length() {
294            return Err(RtpPacketBuildError::BufferTooSmall);
295        }
296
297        self.validate_content()?;
298        Ok(self.build_into_unchecked(target))
299    }
300
301
302    /// Build the RTP packet.
303    /// On success it returns a buffer containing the target packet.
304    pub fn build(&self) -> Result<Vec<u8>, RtpPacketBuildError> {
305        self.validate_content()?;
306
307        let mut buffer = Vec::<u8>::new();
308        buffer.resize(self.target_length(), 0);
309
310        let length = self.build_into_unchecked(buffer.as_mut_slice());
311        assert_eq!(length, buffer.len());
312
313        Ok(buffer)
314    }
315
316    fn validate_content(&self) -> Result<(), RtpPacketBuildError> {
317        if (self.payload_type & (!0x7F)) != 0 {
318            return Err(RtpPacketBuildError::PayloadTypeInvalid);
319        }
320
321        if let Some((_, payload)) = self.extension {
322            if payload.len() > 0xFFFF {
323                return Err(RtpPacketBuildError::ExtensionTooLarge);
324            }
325
326            if (payload.len() & 0x3) != 0 {
327                return Err(RtpPacketBuildError::ExtensionMissingPadding);
328            }
329        }
330
331        Ok(())
332    }
333}
334
335#[cfg(test)]
336mod test {
337    use crate::{RtpPacketBuilder, Pad};
338
339    #[test]
340    fn test_padded() {
341        let payload = vec![1u8];
342        let packet = RtpPacketBuilder::new()
343            .payload_type(1)
344            .payload(&payload)
345            .padded(Pad::round_to(4))
346            .build().unwrap();
347
348        assert_eq!(packet.len() & 0x03, 0);
349        assert!(crate::reader::RtpReader::new(&packet).unwrap().padding().is_some());
350    }
351
352    #[test]
353    fn test_padding_not_needed() {
354        let payload = vec![1u8; 4];
355        let packet = RtpPacketBuilder::new()
356            .payload_type(1)
357            .payload(&payload)
358            .padded(Pad::round_to(4))
359            .build().unwrap();
360
361        // assert the length is not increased beyond the 12 bytes of header + the payload
362        assert_eq!(packet.len(), 12 + payload.len());
363        assert!(crate::reader::RtpReader::new(&packet).unwrap().padding().is_none());
364    }
365
366    #[test]
367    fn test_not_padded() {
368        let payload = vec![1u8];
369        let packet = RtpPacketBuilder::new()
370            .payload_type(1)
371            .payload(&payload)
372            .build().unwrap();
373
374        assert_eq!(packet.len() & 0x03, 1);
375    }
376
377    #[test]
378    fn test_would_run() {
379        let extension = vec![1u8, 2, 3, 4];
380        let builder = RtpPacketBuilder::new()
381            .payload_type(12)
382            .extension(1, &extension);
383
384        let mut buffer = [0u8; 100];
385        builder.build_into(&mut buffer).unwrap();
386    }
387}