rtp_parse/rtp/
rtp_header.rs

1use std::io::Seek;
2
3use bit_cursor::{
4    bit_cursor::BitCursor, bit_read_exts::BitReadExts, byte_order::NetworkOrder, nsw_types::*,
5};
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/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
22pub struct RtpHeader;
23
24impl RtpHeader {
25    pub fn version(buf: &[u8]) -> u2 {
26        u2::new((buf[0] & 0b11000000) >> 6)
27    }
28
29    pub fn has_padding(buf: &[u8]) -> bool {
30        (buf[0] & 0b00100000) != 0
31    }
32
33    pub fn has_extensions(buf: &[u8]) -> bool {
34        (buf[0] & 0b00010000) != 0
35    }
36
37    pub fn csrc_count(buf: &[u8]) -> u4 {
38        u4::new(buf[0] & 0b00001111)
39    }
40
41    pub fn marked(buf: &[u8]) -> bool {
42        (buf[1] & 0b10000000) != 0
43    }
44
45    pub fn payload_type(buf: &[u8]) -> u7 {
46        u7::new(buf[1] & 0b01111111)
47    }
48
49    pub fn seq_num(buf: &[u8]) -> u16 {
50        u16::from_be_bytes(buf[3..4].try_into().unwrap())
51    }
52
53    pub fn timestamp(buf: &[u8]) -> u32 {
54        u32::from_be_bytes(buf[4..8].try_into().unwrap())
55    }
56
57    pub fn ssrc(buf: &[u8]) -> u32 {
58        u32::from_be_bytes(buf[8..12].try_into().unwrap())
59    }
60
61    /// Returns the offset into the given buffer where the top-level extensions header would
62    /// start, if this packet contains extensions.
63    pub fn extensions_start_offset(buf: &[u8]) -> usize {
64        let csrc_count: usize = RtpHeader::csrc_count(buf).into();
65        12 + csrc_count * 4
66    }
67
68    pub fn payload_offset(buf: &[u8]) -> usize {
69        RtpHeader::extensions_start_offset(buf)
70            + RtpHeader::header_extensions_length_bytes(buf) as usize
71    }
72
73    /// Returns the length of the extensions (including the extensions header) in bytes.  If
74    /// has_extensions is false, returns 0.
75    pub fn header_extensions_length_bytes(buf: &[u8]) -> u16 {
76        if RtpHeader::has_extensions(buf) {
77            let mut cursor = BitCursor::new(buf);
78            let ext_offset = RtpHeader::extensions_start_offset(buf);
79            cursor
80                // Add 2 more to get to the length field
81                .seek(std::io::SeekFrom::Start((ext_offset as u64 + 2) * 8))
82                .unwrap();
83            let length_field = cursor.read_u16::<NetworkOrder>().unwrap();
84
85            // 4 for the extensions header (type + length fields)
86            4 + length_field * 4
87        } else {
88            0
89        }
90    }
91}