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}