rtp_parse/rtp2/
rtp_header.rs

1use parsely_rs::*;
2
3use super::header_extensions::HeaderExtensions;
4
5/// An RTP header
6///
7/// https://tools.ietf.org/html/rfc3550#section-5.1
8///  0                   1                   2                   3
9///  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
10/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
11/// |V=2|P|X|  CC   |M|     PT      |       sequence number         |
12/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
13/// |                           timestamp                           |
14/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
15/// |           synchronization source (SSRC) identifier            |
16/// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
17/// |            contributing source (CSRC) identifiers             |
18/// |                             ....                              |
19/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
20/// |              ...extensions (if present)...                    |
21/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
22#[derive(Debug, ParselyRead, ParselyWrite, PartialEq)]
23pub struct RtpHeader {
24    pub version: u2,
25    pub has_padding: bool,
26    pub has_extensions: bool,
27    pub csrc_count: u4,
28    pub marked: bool,
29    pub payload_type: u7,
30    pub seq_num: u16,
31    pub timestamp: u32,
32    pub ssrc: u32,
33    #[parsely_read(count = "csrc_count.into()")]
34    pub csrcs: Vec<u32>,
35    pub extensions: HeaderExtensions,
36}
37
38impl Default for RtpHeader {
39    fn default() -> Self {
40        Self {
41            version: u2::new(2),
42            has_padding: false,
43            has_extensions: false,
44            csrc_count: u4::new(0),
45            marked: false,
46            payload_type: u7::new(0),
47            seq_num: 0,
48            timestamp: 0,
49            ssrc: 0,
50            csrcs: Vec::new(),
51            extensions: HeaderExtensions::default(),
52        }
53    }
54}
55
56#[cfg(test)]
57mod tests {
58    use super::*;
59
60    #[test]
61    fn test_read_fixed_header() {
62        #[rustfmt::skip]
63        let mut data = Bits::from_static_bytes(&[
64            // V=2,P=false,X=true,CC=2,M=false,PT=100,SeqNum=16535
65            0x92, 0x64, 0x40, 0x97,
66            // Timestamp: 3899068446
67            0xe8, 0x67, 0x10, 0x1e,
68            // SSRC: 2828806853
69            0xa8, 0x9c, 0x2a, 0xc5,
70            // CSRC 1: 123456
71            0x00, 0x01, 0xE2, 0x40,
72            // CSRC 2: 45678
73            0x00, 0x00, 0xB2, 0x6E,
74            // 1 extension
75            0xbe, 0xde, 0x00, 0x01,
76            0x51, 0x00, 0x02, 0x00
77        ]);
78        let header = RtpHeader::read::<NetworkOrder>(&mut data, ()).unwrap();
79        assert_eq!(header.version, 2);
80        assert!(!header.has_padding);
81        assert!(header.has_extensions);
82        assert_eq!(header.csrc_count, 2);
83        assert!(!header.marked);
84        assert_eq!(header.payload_type, 100);
85        assert_eq!(header.seq_num, 16535);
86        assert_eq!(header.timestamp, 3899068446);
87        assert_eq!(header.ssrc, 2828806853);
88        assert_eq!(header.extensions.len(), 1);
89        let ext = header.extensions.get_by_id(5).unwrap();
90        assert_eq!(ext.data(), &[0, 2]);
91    }
92}