rtsp_runtime/
interleaved.rs1use crate::error::{Error, Result};
17
18pub const MAGIC: u8 = 0x24;
20
21pub const HEADER_LEN: usize = 4;
23
24#[derive(Debug, Clone, PartialEq, Eq)]
27#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
28pub struct InterleavedFrame {
29 pub channel: u8,
32 pub payload: Vec<u8>,
34}
35
36impl InterleavedFrame {
37 pub fn new(channel: u8, payload: impl Into<Vec<u8>>) -> Self {
39 InterleavedFrame {
40 channel,
41 payload: payload.into(),
42 }
43 }
44
45 pub fn serialized_len(&self) -> usize {
47 HEADER_LEN + self.payload.len()
48 }
49
50 pub fn to_bytes(&self) -> Result<Vec<u8>> {
55 let mut out = Vec::with_capacity(self.serialized_len());
56 self.serialize_into(&mut out)?;
57 Ok(out)
58 }
59
60 pub fn serialize_into(&self, out: &mut Vec<u8>) -> Result<()> {
64 let len: u16 = u16::try_from(self.payload.len()).map_err(|_| {
65 Error::InterleavedFrame(format!(
66 "payload of {} bytes exceeds 16-bit length field",
67 self.payload.len()
68 ))
69 })?;
70 out.push(MAGIC);
71 out.push(self.channel);
72 out.extend_from_slice(&len.to_be_bytes());
73 out.extend_from_slice(&self.payload);
74 Ok(())
75 }
76
77 pub fn parse(buf: &[u8]) -> Result<Option<(InterleavedFrame, usize)>> {
84 if buf.len() < HEADER_LEN {
85 return Ok(None);
86 }
87 if buf[0] != MAGIC {
88 return Err(Error::InterleavedFrame(format!(
89 "expected '$' (0x{MAGIC:02X}), found 0x{:02X}",
90 buf[0]
91 )));
92 }
93 let channel = buf[1];
94 let len = u16::from_be_bytes([buf[2], buf[3]]) as usize;
95 let total = HEADER_LEN + len;
96 if buf.len() < total {
97 return Ok(None);
98 }
99 let frame = InterleavedFrame {
100 channel,
101 payload: buf[HEADER_LEN..total].to_vec(),
102 };
103 Ok(Some((frame, total)))
104 }
105}
106
107pub fn parse_frames(buf: &[u8]) -> Result<(Vec<InterleavedFrame>, usize)> {
118 let mut frames = Vec::new();
119 let mut offset = 0usize;
120 while offset < buf.len() {
121 match InterleavedFrame::parse(&buf[offset..])? {
122 Some((frame, consumed)) => {
123 frames.push(frame);
124 offset += consumed;
125 }
126 None => break, }
128 }
129 Ok((frames, buf.len() - offset))
130}
131
132#[cfg(test)]
133mod tests {
134 use super::*;
135
136 #[test]
137 fn single_frame_round_trip() {
138 let payload: Vec<u8> = (0u8..17).collect();
139 let frame = InterleavedFrame::new(0, payload.clone());
140 let bytes = frame.to_bytes().unwrap();
141 assert_eq!(bytes[0], MAGIC);
142 assert_eq!(bytes[1], 0);
143 assert_eq!(&bytes[2..4], &(payload.len() as u16).to_be_bytes());
144 let (parsed, consumed) = InterleavedFrame::parse(&bytes).unwrap().unwrap();
145 assert_eq!(parsed, frame);
146 assert_eq!(consumed, bytes.len());
147 }
148
149 #[test]
150 fn frame_channel_byte_reflects_channel() {
151 let f = InterleavedFrame::new(1u8, vec![9u8, 8, 7]);
153 let bytes = f.to_bytes().unwrap();
154 assert_eq!(bytes[0], MAGIC);
155 assert_eq!(bytes[1], 1, "channel byte must reflect channel 1");
156 assert_eq!(u16::from_be_bytes([bytes[2], bytes[3]]), 3);
157 let (parsed, consumed) = InterleavedFrame::parse(&bytes).unwrap().unwrap();
158 assert_eq!(consumed, bytes.len());
159 assert_eq!(parsed.channel, 1);
160 assert_eq!(parsed.payload, vec![9, 8, 7]);
161 }
162
163 #[test]
164 fn two_frames_plus_partial_returns_two_and_remainder() {
165 let f0 = InterleavedFrame::new(0, vec![0xAA; 10]);
166 let f1 = InterleavedFrame::new(1, vec![0xBB; 4]);
167 let mut buf = Vec::new();
168 buf.extend_from_slice(&f0.to_bytes().unwrap());
169 buf.extend_from_slice(&f1.to_bytes().unwrap());
170 let partial = [MAGIC, 0x00, 0x00, 0x08, 1, 2, 3];
172 buf.extend_from_slice(&partial);
173
174 let (frames, remainder) = parse_frames(&buf).unwrap();
175 assert_eq!(frames.len(), 2);
176 assert_eq!(frames[0], f0);
177 assert_eq!(frames[1], f1);
178 assert_eq!(remainder, partial.len());
179 assert_eq!(&buf[buf.len() - remainder..], &partial);
180 }
181
182 #[test]
183 fn header_only_partial_is_remainder() {
184 let buf = [MAGIC, 0x00];
185 let (frames, remainder) = parse_frames(&buf).unwrap();
186 assert!(frames.is_empty());
187 assert_eq!(remainder, 2);
188 }
189
190 #[test]
191 fn wrong_magic_bites() {
192 let buf = [b'R', 0, 0, 1, 9];
193 assert!(InterleavedFrame::parse(&buf).is_err());
194 }
195}