1use bytes::Bytes;
31
32const ANNEXB_START: [u8; 4] = [0, 0, 0, 1];
34
35#[derive(Debug, Clone, Copy, PartialEq, Eq)]
37pub struct RtpHeader {
38 pub payload_type: u8,
40 pub marker: bool,
42 pub sequence: u16,
44 pub timestamp: u32,
46 pub ssrc: u32,
48 pub payload_offset: usize,
50}
51
52impl RtpHeader {
53 pub fn parse(buf: &[u8]) -> Option<RtpHeader> {
57 if buf.len() < 12 {
58 return None;
59 }
60 let version = buf[0] >> 6;
61 if version != 2 {
62 return None;
63 }
64 let has_extension = buf[0] & 0x10 != 0;
65 let csrc_count = (buf[0] & 0x0F) as usize;
66 let marker = buf[1] & 0x80 != 0;
67 let payload_type = buf[1] & 0x7F;
68 let sequence = u16::from_be_bytes([buf[2], buf[3]]);
69 let timestamp = u32::from_be_bytes([buf[4], buf[5], buf[6], buf[7]]);
70 let ssrc = u32::from_be_bytes([buf[8], buf[9], buf[10], buf[11]]);
71
72 let mut offset = 12 + csrc_count * 4;
73 if has_extension {
74 if buf.len() < offset + 4 {
76 return None;
77 }
78 let ext_words = u16::from_be_bytes([buf[offset + 2], buf[offset + 3]]) as usize;
79 offset += 4 + ext_words * 4;
80 }
81 if buf.len() < offset {
82 return None;
83 }
84 Some(RtpHeader {
85 payload_type,
86 marker,
87 sequence,
88 timestamp,
89 ssrc,
90 payload_offset: offset,
91 })
92 }
93}
94
95#[derive(Debug, Clone, Copy, PartialEq, Eq)]
97#[non_exhaustive]
98pub enum DepacketizeError {
99 Truncated,
101 OutOfOrder,
104 Unsupported(u8),
106}
107
108#[derive(Debug, Default)]
116pub struct H264Depacketizer {
117 au: Vec<u8>,
119 fua: Vec<u8>,
121 in_fragment: bool,
123 fua_header: u8,
125 current_ts: Option<u32>,
127 last_seq: Option<u16>,
129}
130
131impl H264Depacketizer {
132 pub fn new() -> Self {
134 Self::default()
135 }
136
137 fn append_nal(&mut self, nal: &[u8]) {
139 self.au.extend_from_slice(&ANNEXB_START);
140 self.au.extend_from_slice(nal);
141 }
142
143 fn pending_is_keyframe(&self) -> bool {
145 let mut i = 0;
147 while i + 4 < self.au.len() {
148 if self.au[i..i + 4] == ANNEXB_START {
149 let nal_type = self.au[i + 4] & 0x1F;
150 if nal_type == 5 {
151 return true;
152 }
153 }
154 i += 1;
155 }
156 false
157 }
158
159 fn take_au(&mut self) -> Option<AccessUnit> {
161 if self.au.is_empty() {
162 return None;
163 }
164 let keyframe = self.pending_is_keyframe();
165 let timestamp = self.current_ts.unwrap_or(0);
166 let data = Bytes::from(std::mem::take(&mut self.au));
167 self.current_ts = None;
168 Some(AccessUnit {
169 data,
170 timestamp,
171 keyframe,
172 })
173 }
174
175 pub fn push(
178 &mut self,
179 payload: &[u8],
180 marker: bool,
181 timestamp: u32,
182 sequence: u16,
183 ) -> Result<Option<AccessUnit>, DepacketizeError> {
184 if payload.is_empty() {
185 return Err(DepacketizeError::Truncated);
186 }
187
188 let mut completed = None;
191 if let Some(ts) = self.current_ts {
192 if ts != timestamp && !self.in_fragment {
193 completed = self.take_au();
194 }
195 }
196 self.current_ts = Some(timestamp);
197
198 let nal_type = payload[0] & 0x1F;
199 match nal_type {
200 1..=23 => {
201 self.append_nal(payload);
203 }
204 24 => {
205 let mut i = 1;
207 while i + 2 <= payload.len() {
208 let size = u16::from_be_bytes([payload[i], payload[i + 1]]) as usize;
209 i += 2;
210 if i + size > payload.len() {
211 return Err(DepacketizeError::Truncated);
212 }
213 self.append_nal(&payload[i..i + size]);
214 i += size;
215 }
216 }
217 28 => {
218 if payload.len() < 2 {
220 return Err(DepacketizeError::Truncated);
221 }
222 let fu_header = payload[1];
223 let start = fu_header & 0x80 != 0;
224 let end = fu_header & 0x40 != 0;
225 let frag_type = fu_header & 0x1F;
226
227 if start {
228 self.fua_header = (payload[0] & 0xE0) | frag_type;
231 self.fua.clear();
232 self.fua.push(self.fua_header);
233 self.in_fragment = true;
234 } else if !self.in_fragment {
235 return Err(DepacketizeError::OutOfOrder);
237 } else if self.seq_gap(sequence) {
238 self.in_fragment = false;
239 self.fua.clear();
240 return Err(DepacketizeError::OutOfOrder);
241 }
242 self.fua.extend_from_slice(&payload[2..]);
243
244 if end && self.in_fragment {
245 let nal = std::mem::take(&mut self.fua);
246 self.append_nal(&nal);
247 self.in_fragment = false;
248 }
249 }
250 other => return Err(DepacketizeError::Unsupported(other)),
251 }
252
253 self.last_seq = Some(sequence);
254
255 if completed.is_some() {
256 return Ok(completed);
257 }
258 if marker {
259 return Ok(self.take_au());
260 }
261 Ok(None)
262 }
263
264 fn seq_gap(&self, sequence: u16) -> bool {
266 match self.last_seq {
267 Some(prev) => sequence.wrapping_sub(prev) != 1,
268 None => false,
269 }
270 }
271}
272
273#[derive(Debug, Clone, PartialEq, Eq)]
275pub struct AccessUnit {
276 pub data: Bytes,
278 pub timestamp: u32,
280 pub keyframe: bool,
282}
283
284#[cfg(test)]
285mod tests {
286 use super::*;
287
288 fn rtp(seq: u16, ts: u32, marker: bool, payload: &[u8]) -> Vec<u8> {
290 let mut p = vec![0x80, if marker { 0x80 | 96 } else { 96 }];
291 p.extend_from_slice(&seq.to_be_bytes());
292 p.extend_from_slice(&ts.to_be_bytes());
293 p.extend_from_slice(&[0, 0, 0, 1]); p.extend_from_slice(payload);
295 p
296 }
297
298 #[test]
299 fn parses_fixed_header_and_payload_offset() {
300 let pkt = rtp(7, 9000, true, &[0x65, 0xAA]);
301 let h = RtpHeader::parse(&pkt).unwrap();
302 assert_eq!(h.sequence, 7);
303 assert_eq!(h.timestamp, 9000);
304 assert!(h.marker);
305 assert_eq!(h.payload_type, 96);
306 assert_eq!(h.payload_offset, 12);
307 assert_eq!(&pkt[h.payload_offset..], &[0x65, 0xAA]);
308 }
309
310 #[test]
311 fn rejects_wrong_version_and_short_buffers() {
312 assert!(RtpHeader::parse(&[0x00; 12]).is_none()); assert!(RtpHeader::parse(&[0x80; 4]).is_none()); }
315
316 #[test]
317 fn honors_csrc_count_in_payload_offset() {
318 let mut pkt = rtp(1, 0, false, &[0x41]);
319 pkt[0] = 0x82; let mut with_csrc = pkt[..12].to_vec();
321 with_csrc.extend_from_slice(&[0xDE, 0xAD, 0xBE, 0xEF, 0, 0, 0, 0]); with_csrc.push(0x41);
323 let h = RtpHeader::parse(&with_csrc).unwrap();
324 assert_eq!(h.payload_offset, 20);
325 }
326
327 #[test]
328 fn single_nal_packet_emits_annexb_on_marker() {
329 let mut d = H264Depacketizer::new();
330 let out = d.push(&[0x41, 0x9A, 0xBC], true, 3000, 1).unwrap().unwrap();
332 assert_eq!(&out.data[..], &[0, 0, 0, 1, 0x41, 0x9A, 0xBC]);
333 assert!(!out.keyframe);
334 assert_eq!(out.timestamp, 3000);
335 }
336
337 #[test]
338 fn idr_single_nal_is_flagged_keyframe() {
339 let mut d = H264Depacketizer::new();
340 let out = d.push(&[0x65, 0x01], true, 0, 1).unwrap().unwrap();
341 assert!(out.keyframe);
342 }
343
344 #[test]
345 fn stap_a_splits_aggregated_nals() {
346 let payload = [24, 0, 2, 0xAA, 0xBB, 0, 3, 0xCC, 0xDD, 0xEE];
348 let mut d = H264Depacketizer::new();
349 let out = d.push(&payload, true, 0, 1).unwrap().unwrap();
350 assert_eq!(
351 &out.data[..],
352 &[0, 0, 0, 1, 0xAA, 0xBB, 0, 0, 0, 1, 0xCC, 0xDD, 0xEE]
353 );
354 }
355
356 #[test]
357 fn fu_a_reassembles_fragmented_nal() {
358 let mut d = H264Depacketizer::new();
359 assert!(d
361 .push(&[0x7C, 0x85, 0x11, 0x22], false, 0, 1)
362 .unwrap()
363 .is_none());
364 assert!(d.push(&[0x7C, 0x05, 0x33], false, 0, 2).unwrap().is_none());
366 let out = d.push(&[0x7C, 0x45, 0x44], true, 0, 3).unwrap().unwrap();
368 assert_eq!(&out.data[..], &[0, 0, 0, 1, 0x65, 0x11, 0x22, 0x33, 0x44]);
370 assert!(out.keyframe);
371 }
372
373 #[test]
374 fn fu_a_sequence_gap_reports_out_of_order() {
375 let mut d = H264Depacketizer::new();
376 d.push(&[0x7C, 0x85, 0x11], false, 0, 1).unwrap();
377 assert_eq!(
379 d.push(&[0x7C, 0x05, 0x22], false, 0, 5),
380 Err(DepacketizeError::OutOfOrder)
381 );
382 }
383
384 #[test]
385 fn timestamp_change_flushes_previous_au_without_marker() {
386 let mut d = H264Depacketizer::new();
387 assert!(d.push(&[0x41, 0x01], false, 1000, 1).unwrap().is_none());
389 let out = d.push(&[0x41, 0x02], false, 2000, 2).unwrap().unwrap();
391 assert_eq!(out.timestamp, 1000);
392 assert_eq!(&out.data[..], &[0, 0, 0, 1, 0x41, 0x01]);
393 }
394}