rtp_parse/rtp/
rtp_packet.rs

1use std::{
2    collections::HashMap,
3    fmt::{Debug, Display},
4};
5
6use anyhow::Result;
7use bit_cursor::nsw_types::u7;
8use bytes::BytesMut;
9
10use super::{
11    header_extensions::{read_header_extensions, SomeHeaderExtension},
12    rtp_header::RtpHeader,
13};
14
15/// https://tools.ietf.org/html/rfc3550#section-5.1
16///  0                   1                   2                   3
17///  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
18/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
19/// |V=2|P|X|  CC   |M|     PT      |       sequence number         |
20/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
21/// |                           timestamp                           |
22/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
23/// |           synchronization source (SSRC) identifier            |
24/// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
25/// |            contributing source (CSRC) identifiers             |
26/// |                             ....                              |
27/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
28/// |              ...extensions (if present)...                    |
29/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
30/// |                   payload                                     |
31/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
32#[derive(Debug)]
33pub struct RtpPacket {
34    // Includes the fixed header and csrcs
35    header: BytesMut,
36    header_exts_buf: BytesMut,
37    parsed_header_extensions: HashMap<u8, SomeHeaderExtension>,
38    payload: BytesMut,
39}
40
41impl Display for RtpPacket {
42    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
43        write!(f, "{:x?}", self.payload.as_ref())
44    }
45}
46
47impl RtpPacket {
48    pub fn payload_type(&self) -> u7 {
49        RtpHeader::payload_type(&self.header)
50    }
51
52    pub fn ssrc(&self) -> u32 {
53        RtpHeader::ssrc(&self.header)
54    }
55
56    pub fn get_extension_by_id(&self, id: u8) -> Option<&SomeHeaderExtension> {
57        self.parsed_header_extensions.get(&id)
58    }
59
60    // TODO: this will give the "original" size of the packet, is that best? It's what we want for
61    // incoming stats, but at other point we'll want the "actual" size of the packet (which may
62    // have changed)
63    pub fn size_bytes(&self) -> usize {
64        self.header.len() + self.header_exts_buf.len() + self.payload.len()
65    }
66}
67
68pub fn read_rtp_packet(buf: Vec<u8>) -> Result<RtpPacket> {
69    // TODO: eventaully I think we'll have it where this was already a BytesMut type and we don't
70    // have to copy it here
71    let mut bytes = BytesMut::with_capacity(buf.len());
72    bytes.extend_from_slice(&buf);
73    let csrc_count = Into::<usize>::into(RtpHeader::csrc_count(&bytes));
74    let header_length_bytes = 12 + 4 * csrc_count;
75    let header = bytes.split_to(header_length_bytes);
76    let header_extensions_length_bytes = ((((bytes[2] as u16) << 8) + bytes[3] as u16) + 1) * 4;
77
78    let header_exts = bytes.split_to(header_extensions_length_bytes as usize);
79    let parsed_header_extensions = read_header_extensions(header_exts.clone().into());
80
81    Ok(RtpPacket {
82        header,
83        header_exts_buf: header_exts,
84        parsed_header_extensions,
85        payload: bytes,
86    })
87}
88
89#[cfg(test)]
90mod test {
91    use super::read_rtp_packet;
92
93    #[test]
94    fn test_read_rtp_packet2() {
95        #[rustfmt::skip]
96        let data: Vec<u8> = vec![
97            0x90, 0xef, 0x16, 0xad, 0x65, 0xf3, 0xe1, 0x4e, 0x32, 0x0f, 0x22, 0x3a, 0xbe, 0xde,
98            0x00, 0x01, 0x10, 0xff, 0x00, 0x00, 0x78, 0x0b, 0xe4, 0xc1, 0x36, 0xec, 0xc5, 0x8d,
99            0x8c, 0x49, 0x46, 0x99, 0x04, 0xc5, 0xaa, 0xed, 0x92, 0xe7, 0x63, 0x4a, 0x3a, 0x18,
100            0x98, 0xee, 0x62, 0xcb, 0x60, 0xff, 0x6c, 0x1b, 0x29, 0x00,
101        ];
102
103        let packet = read_rtp_packet(data).unwrap();
104        println!("{:x?}", packet.header.as_ref());
105        println!("{:x?}", packet.header_exts_buf.as_ref());
106        println!("{:?}", packet.parsed_header_extensions);
107        println!("{:x?}", packet.payload.as_ref());
108        // dbg!(packet);
109    }
110}