1pub const VERSION: u8 = 1;
10pub const HEADER_LEN: usize = 10;
11pub const MAX_CHUNK_PAYLOAD: usize = 1000;
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14pub struct Header {
15 pub keyframe: bool,
16 pub fec_type: u8,
17 pub frame_id: u16,
18 pub chunk_idx: u16,
19 pub n: u16,
20 pub k: u8,
21 pub last_len: u16,
22}
23
24pub fn parse(buf: &[u8]) -> Option<(Header, &[u8])> {
26 if buf.len() < HEADER_LEN {
27 return None;
28 }
29 let flags = buf[0];
30 if flags & 0x0f != VERSION {
31 return None;
32 }
33 let be16 = |i: usize| u16::from_be_bytes([buf[i], buf[i + 1]]);
34 Some((
35 Header {
36 keyframe: flags & 0x80 != 0,
37 fec_type: (flags >> 4) & 0x07,
38 frame_id: be16(1),
39 chunk_idx: be16(3),
40 n: be16(5),
41 k: buf[7],
42 last_len: be16(8),
43 },
44 &buf[HEADER_LEN..],
45 ))
46}
47
48#[cfg(test)]
49mod tests {
50 use super::*;
51
52 #[test]
53 fn parses_reference_layout() {
54 let buf = [
56 0xa1, 0x12, 0x34, 0x00, 0x03, 0x00, 0x05, 0x02, 0x03, 0xe7, 0xaa,
57 ];
58 let (h, payload) = parse(&buf).unwrap();
59 assert_eq!(
60 h,
61 Header {
62 keyframe: true,
63 fec_type: 2,
64 frame_id: 0x1234,
65 chunk_idx: 3,
66 n: 5,
67 k: 2,
68 last_len: 999
69 }
70 );
71 assert_eq!(payload, &[0xaa]);
72 assert!(parse(&[0xa2; 10]).is_none(), "wrong version rejected");
73 assert!(parse(&[0xa1; 9]).is_none(), "short buffer rejected");
74 }
75}