1use std::collections::BTreeMap;
2
3const DEFAULT_RTP_REORDER_WINDOW: usize = 15;
4const DEFAULT_MAX_ACCESS_UNIT_SIZE: usize = 8 * 1024 * 1024;
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8pub enum RtpError {
9 TooShort,
11 InvalidVersion(u8),
13 InvalidExtension,
15 InvalidPadding,
17 EmptyPayload,
19 UnsupportedPayload,
21 FragmentOverflow,
23}
24
25#[derive(Debug, Clone, Copy, PartialEq, Eq)]
27pub enum Codec {
28 H264,
30 H265,
32}
33
34pub const RTP_PAYLOAD_TYPE_H264: u8 = 96;
36pub const RTP_PAYLOAD_TYPE_H265: u8 = 97;
38pub const RTP_PAYLOAD_TYPE_OPUS: u8 = 98;
40
41#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
48pub struct CodecConfigState {
49 pub h264_sps: bool,
51 pub h264_pps: bool,
53 pub h265_vps: bool,
55 pub h265_sps: bool,
57 pub h265_pps: bool,
59}
60
61impl CodecConfigState {
62 pub const fn is_complete_for(self, codec: Codec) -> bool {
64 match codec {
65 Codec::H264 => self.h264_sps && self.h264_pps,
66 Codec::H265 => self.h265_vps && self.h265_sps && self.h265_pps,
67 }
68 }
69}
70
71#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
73pub struct RtpDepacketizerStatus {
74 pub packets: u64,
76 pub frames_emitted: u64,
78 pub config_wait_drops: u64,
80 pub keyframes_with_prepended_config: u64,
82 pub parameter_sets_prepended: u64,
84 pub fragment_sequence_gaps: u64,
86 pub fragment_overflows: u64,
88 pub unsupported_payloads: u64,
90 pub malformed_packets: u64,
92 pub last_payload_type: Option<u8>,
94 pub last_sequence_number: Option<u16>,
96 pub last_timestamp: Option<u32>,
98 pub last_codec: Option<Codec>,
100 pub last_nal_type: Option<u8>,
102 pub codec_config: CodecConfigState,
104}
105
106#[derive(Debug, Clone, Copy, PartialEq, Eq)]
108pub struct RtpHeader {
109 pub marker: bool,
111 pub payload_type: u8,
113 pub sequence_number: u16,
115 pub timestamp: u32,
117 pub ssrc: u32,
119 pub csrc_count: u8,
121 pub has_extension: bool,
123 pub header_len: usize,
125 pub payload_len: usize,
127}
128
129impl RtpHeader {
130 pub fn parse(packet: &[u8]) -> Result<Self, RtpError> {
132 if packet.len() < 12 {
133 return Err(RtpError::TooShort);
134 }
135 let version = packet[0] >> 6;
136 if version != 2 {
137 return Err(RtpError::InvalidVersion(version));
138 }
139
140 let padding = packet[0] & 0x20 != 0;
141 let extension = packet[0] & 0x10 != 0;
142 let csrc_count = packet[0] & 0x0f;
143 let mut header_len = 12 + csrc_count as usize * 4;
144 if packet.len() < header_len {
145 return Err(RtpError::TooShort);
146 }
147
148 if extension {
149 if packet.len() < header_len + 4 {
150 return Err(RtpError::InvalidExtension);
151 }
152 let ext_words =
153 u16::from_be_bytes([packet[header_len + 2], packet[header_len + 3]]) as usize;
154 header_len += 4 + ext_words * 4;
155 if packet.len() < header_len {
156 return Err(RtpError::InvalidExtension);
157 }
158 }
159
160 let padding_len = if padding {
161 let len = *packet.last().ok_or(RtpError::InvalidPadding)? as usize;
162 if len == 0 || len > packet.len() - header_len {
163 return Err(RtpError::InvalidPadding);
164 }
165 len
166 } else {
167 0
168 };
169
170 let payload_len = packet.len() - header_len - padding_len;
171 if payload_len == 0 {
172 return Err(RtpError::EmptyPayload);
173 }
174
175 Ok(Self {
176 marker: packet[1] & 0x80 != 0,
177 payload_type: packet[1] & 0x7f,
178 sequence_number: u16::from_be_bytes([packet[2], packet[3]]),
179 timestamp: u32::from_be_bytes([packet[4], packet[5], packet[6], packet[7]]),
180 ssrc: u32::from_be_bytes([packet[8], packet[9], packet[10], packet[11]]),
181 csrc_count,
182 has_extension: extension,
183 header_len,
184 payload_len,
185 })
186 }
187
188 pub fn payload<'a>(&self, packet: &'a [u8]) -> &'a [u8] {
190 &packet[self.header_len..self.header_len + self.payload_len]
191 }
192}
193
194#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
196pub struct RtpReorderStatus {
197 pub buffered_packets: usize,
199 pub reordered_packets: u64,
201 pub late_packets: u64,
203 pub forced_flushes: u64,
205}
206
207#[derive(Debug, Clone)]
213pub struct RtpReorderBuffer {
214 next_sequence: Option<u16>,
215 pending: BTreeMap<u16, Vec<u8>>,
216 max_depth: usize,
217 status: RtpReorderStatus,
218}
219
220impl Default for RtpReorderBuffer {
221 fn default() -> Self {
222 Self::new(DEFAULT_RTP_REORDER_WINDOW)
223 }
224}
225
226impl RtpReorderBuffer {
227 pub fn new(max_depth: usize) -> Self {
229 Self {
230 next_sequence: None,
231 pending: BTreeMap::new(),
232 max_depth: max_depth.max(1),
233 status: RtpReorderStatus::default(),
234 }
235 }
236
237 pub fn push(&mut self, packet: &[u8]) -> Result<Vec<Vec<u8>>, RtpError> {
239 let header = RtpHeader::parse(packet)?;
240 let sequence = header.sequence_number;
241 let mut ready = Vec::new();
242
243 let Some(expected) = self.next_sequence else {
244 self.next_sequence = Some(sequence.wrapping_add(1));
245 ready.push(packet.to_vec());
246 return Ok(ready);
247 };
248
249 if sequence == expected {
250 self.next_sequence = Some(expected.wrapping_add(1));
251 ready.push(packet.to_vec());
252 self.drain_ready(&mut ready);
253 return Ok(ready);
254 }
255
256 if sequence_is_before(sequence, expected) {
257 self.status.late_packets = self.status.late_packets.saturating_add(1);
258 return Ok(ready);
259 }
260
261 if self.pending.insert(sequence, packet.to_vec()).is_none() {
262 self.status.reordered_packets = self.status.reordered_packets.saturating_add(1);
263 }
264 if self.pending.len() >= self.max_depth {
265 self.force_flush(expected, &mut ready);
266 }
267 self.status.buffered_packets = self.pending.len();
268 Ok(ready)
269 }
270
271 pub fn status(&self) -> RtpReorderStatus {
273 RtpReorderStatus {
274 buffered_packets: self.pending.len(),
275 ..self.status
276 }
277 }
278
279 fn drain_ready(&mut self, ready: &mut Vec<Vec<u8>>) {
280 while let Some(expected) = self.next_sequence {
281 let Some(packet) = self.pending.remove(&expected) else {
282 break;
283 };
284 self.next_sequence = Some(expected.wrapping_add(1));
285 ready.push(packet);
286 }
287 self.status.buffered_packets = self.pending.len();
288 }
289
290 fn force_flush(&mut self, expected: u16, ready: &mut Vec<Vec<u8>>) {
291 let Some(sequence) = self
292 .pending
293 .keys()
294 .copied()
295 .min_by_key(|sequence| sequence.wrapping_sub(expected))
296 else {
297 return;
298 };
299 if let Some(packet) = self.pending.remove(&sequence) {
300 self.status.forced_flushes = self.status.forced_flushes.saturating_add(1);
301 self.next_sequence = Some(sequence.wrapping_add(1));
302 ready.push(packet);
303 self.drain_ready(ready);
304 }
305 }
306}
307
308fn sequence_is_before(sequence: u16, expected: u16) -> bool {
309 let backward = expected.wrapping_sub(sequence);
310 backward != 0 && backward < 0x8000
311}
312
313#[derive(Debug, Clone, PartialEq, Eq)]
315pub struct DepacketizedFrame {
316 pub data: Vec<u8>,
318 pub timestamp: u32,
320 pub is_keyframe: bool,
322 pub codec: Codec,
324 pub payload_type: u8,
326 pub sequence_number: u16,
328 pub nal_type: u8,
330 pub codec_config: CodecConfigState,
332}
333
334#[derive(Debug, Default, Clone)]
335struct FragmentState {
336 data: Vec<u8>,
337 timestamp: u32,
338 next_sequence: Option<u16>,
339 corrupted: bool,
340}
341
342#[derive(Debug, Default, Clone)]
343struct AccessUnitState {
344 data: Vec<u8>,
345 timestamp: Option<u32>,
346 next_sequence: Option<u16>,
347 corrupted: bool,
348 is_keyframe: bool,
349 has_decoder_config: bool,
350 nal_type: u8,
351}
352
353#[derive(Debug, Clone, Copy)]
354struct FrameMeta {
355 timestamp: u32,
356 is_keyframe: bool,
357 codec: Codec,
358 payload_type: u8,
359 sequence_number: u16,
360 nal_type: u8,
361}
362
363#[derive(Debug, Clone)]
368pub struct RtpDepacketizer {
369 h264: FragmentState,
370 h265: FragmentState,
371 h264_access_unit: AccessUnitState,
372 h265_access_unit: AccessUnitState,
373 h264_sps: Option<Vec<u8>>,
374 h264_pps: Option<Vec<u8>>,
375 h265_vps: Option<Vec<u8>>,
376 h265_sps: Option<Vec<u8>>,
377 h265_pps: Option<Vec<u8>>,
378 max_fragment: usize,
379 status: RtpDepacketizerStatus,
380}
381
382impl Default for RtpDepacketizer {
383 fn default() -> Self {
384 Self::new()
385 }
386}
387
388impl RtpDepacketizer {
389 pub fn new() -> Self {
391 Self {
392 h264: FragmentState::default(),
393 h265: FragmentState::default(),
394 h264_access_unit: AccessUnitState::default(),
395 h265_access_unit: AccessUnitState::default(),
396 h264_sps: None,
397 h264_pps: None,
398 h265_vps: None,
399 h265_sps: None,
400 h265_pps: None,
401 max_fragment: DEFAULT_MAX_ACCESS_UNIT_SIZE,
402 status: RtpDepacketizerStatus::default(),
403 }
404 }
405
406 pub fn status(&self) -> RtpDepacketizerStatus {
408 RtpDepacketizerStatus {
409 codec_config: self.codec_config(),
410 ..self.status
411 }
412 }
413
414 pub fn codec_config(&self) -> CodecConfigState {
416 CodecConfigState {
417 h264_sps: self.h264_sps.is_some(),
418 h264_pps: self.h264_pps.is_some(),
419 h265_vps: self.h265_vps.is_some(),
420 h265_sps: self.h265_sps.is_some(),
421 h265_pps: self.h265_pps.is_some(),
422 }
423 }
424
425 pub fn push(&mut self, packet: &[u8]) -> Result<Option<DepacketizedFrame>, RtpError> {
427 self.status.packets = self.status.packets.saturating_add(1);
428 let header = match RtpHeader::parse(packet) {
429 Ok(header) => header,
430 Err(err) => {
431 self.record_error(err);
432 return Err(err);
433 }
434 };
435 self.status.last_payload_type = Some(header.payload_type);
436 self.status.last_sequence_number = Some(header.sequence_number);
437 self.status.last_timestamp = Some(header.timestamp);
438 let payload = header.payload(packet);
439 log::trace!(
440 target: "openipc_core::rtp",
441 "received RTP packet sequence={} timestamp={} pt={} marker={} bytes={}",
442 header.sequence_number,
443 header.timestamp,
444 header.payload_type,
445 header.marker,
446 payload.len()
447 );
448 if header.payload_type == RTP_PAYLOAD_TYPE_OPUS {
449 self.record_error(RtpError::UnsupportedPayload);
450 return Err(RtpError::UnsupportedPayload);
451 }
452 let Some(codec) =
453 codec_from_payload_type(header.payload_type).or_else(|| detect_codec(payload))
454 else {
455 self.record_error(RtpError::UnsupportedPayload);
456 return Err(RtpError::UnsupportedPayload);
457 };
458 self.status.last_codec = Some(codec);
459 self.observe_access_unit_packet(codec, header);
460 let result = match codec {
461 Codec::H264 => self.push_h264(payload, header),
462 Codec::H265 => self.push_h265(payload, header),
463 };
464 match &result {
465 Ok(Some(_)) => {
466 self.status.frames_emitted = self.status.frames_emitted.saturating_add(1)
467 }
468 Err(err) => {
469 log::debug!(
470 target: "openipc_core::rtp",
471 "RTP packet rejected sequence={}: {err:?}",
472 header.sequence_number
473 );
474 self.record_error(*err);
475 }
476 _ => {}
477 }
478 result
479 }
480
481 fn push_h264(
482 &mut self,
483 payload: &[u8],
484 header: RtpHeader,
485 ) -> Result<Option<DepacketizedFrame>, RtpError> {
486 let nal_type = payload[0] & 0x1f;
487 self.status.last_nal_type = Some(nal_type);
488 match nal_type {
489 7 => {
490 self.h264_sps = Some(payload.to_vec());
491 Ok(None)
492 }
493 8 => {
494 self.h264_pps = Some(payload.to_vec());
495 Ok(None)
496 }
497 24 => self.h264_stap_a(payload, header),
498 28 => self.h264_fu_a(payload, header),
499 _ if self.has_decoder_config(Codec::H264) && is_h264_vcl_nal(nal_type) => self
500 .push_complete_nalu(
501 payload,
502 FrameMeta {
503 timestamp: header.timestamp,
504 is_keyframe: nal_type == 5,
505 codec: Codec::H264,
506 payload_type: header.payload_type,
507 sequence_number: header.sequence_number,
508 nal_type,
509 },
510 header.marker,
511 ),
512 _ if !is_h264_vcl_nal(nal_type) => Ok(None),
513 _ => {
514 self.status.config_wait_drops = self.status.config_wait_drops.saturating_add(1);
515 Ok(None)
516 }
517 }
518 }
519
520 fn push_h265(
521 &mut self,
522 payload: &[u8],
523 header: RtpHeader,
524 ) -> Result<Option<DepacketizedFrame>, RtpError> {
525 if payload.len() < 2 {
526 return Err(RtpError::UnsupportedPayload);
527 }
528 let nal_type = (payload[0] >> 1) & 0x3f;
529 self.status.last_nal_type = Some(nal_type);
530 match nal_type {
531 32 => {
532 self.h265_vps = Some(payload.to_vec());
533 Ok(None)
534 }
535 33 => {
536 self.h265_sps = Some(payload.to_vec());
537 Ok(None)
538 }
539 34 => {
540 self.h265_pps = Some(payload.to_vec());
541 Ok(None)
542 }
543 48 => self.h265_ap(payload, header),
544 49 => self.h265_fu(payload, header),
545 _ if self.has_decoder_config(Codec::H265) && is_h265_vcl_nal(nal_type) => self
546 .push_complete_nalu(
547 payload,
548 FrameMeta {
549 timestamp: header.timestamp,
550 is_keyframe: (16..=23).contains(&nal_type),
551 codec: Codec::H265,
552 payload_type: header.payload_type,
553 sequence_number: header.sequence_number,
554 nal_type,
555 },
556 header.marker,
557 ),
558 _ if !is_h265_vcl_nal(nal_type) => Ok(None),
559 _ => {
560 self.status.config_wait_drops = self.status.config_wait_drops.saturating_add(1);
561 Ok(None)
562 }
563 }
564 }
565
566 fn h264_fu_a(
567 &mut self,
568 payload: &[u8],
569 header: RtpHeader,
570 ) -> Result<Option<DepacketizedFrame>, RtpError> {
571 if payload.len() < 2 {
572 return Err(RtpError::UnsupportedPayload);
573 }
574 let fu_indicator = payload[0];
575 let fu_header = payload[1];
576 let start = fu_header & 0x80 != 0;
577 let end = fu_header & 0x40 != 0;
578 let nal_type = fu_header & 0x1f;
579 if start {
580 self.h264.data.clear();
581 self.h264.data.extend_from_slice(&[0, 0, 0, 1]);
582 self.h264.timestamp = header.timestamp;
583 self.h264.next_sequence = Some(header.sequence_number.wrapping_add(1));
584 self.h264.corrupted = false;
585 self.h264.data.push((fu_indicator & 0xe0) | nal_type);
586 } else if !self.accept_fragment_sequence(Codec::H264, header.sequence_number) {
587 return Ok(None);
588 }
589 if !self.h264.corrupted {
590 self.append_fragment(Codec::H264, &payload[2..])?;
591 }
592 if end {
593 if !is_h264_vcl_nal(nal_type) {
594 self.reset_fragment(Codec::H264);
595 return Ok(None);
596 }
597 if self.h264.corrupted || !self.has_decoder_config(Codec::H264) {
598 if !self.has_decoder_config(Codec::H264) {
599 self.status.config_wait_drops = self.status.config_wait_drops.saturating_add(1);
600 }
601 self.reset_fragment(Codec::H264);
602 return Ok(None);
603 }
604 let data = std::mem::take(&mut self.h264.data);
605 let meta = FrameMeta {
606 timestamp: self.h264.timestamp,
607 is_keyframe: nal_type == 5,
608 codec: Codec::H264,
609 payload_type: header.payload_type,
610 sequence_number: header.sequence_number,
611 nal_type,
612 };
613 self.reset_fragment(Codec::H264);
614 self.push_complete_owned_annex_b(data, meta, header.marker, false)
615 } else {
616 Ok(None)
617 }
618 }
619
620 fn h265_fu(
621 &mut self,
622 payload: &[u8],
623 header: RtpHeader,
624 ) -> Result<Option<DepacketizedFrame>, RtpError> {
625 if payload.len() < 3 {
626 return Err(RtpError::UnsupportedPayload);
627 }
628 let fu_header = payload[2];
629 let start = fu_header & 0x80 != 0;
630 let end = fu_header & 0x40 != 0;
631 let nal_type = fu_header & 0x3f;
632 if start {
633 self.h265.data.clear();
634 self.h265.data.extend_from_slice(&[0, 0, 0, 1]);
635 self.h265.timestamp = header.timestamp;
636 self.h265.next_sequence = Some(header.sequence_number.wrapping_add(1));
637 self.h265.corrupted = false;
638 self.h265.data.push((payload[0] & 0x81) | (nal_type << 1));
639 self.h265.data.push(payload[1]);
640 } else if !self.accept_fragment_sequence(Codec::H265, header.sequence_number) {
641 return Ok(None);
642 }
643 if !self.h265.corrupted {
644 self.append_fragment(Codec::H265, &payload[3..])?;
645 }
646 if end {
647 if !is_h265_vcl_nal(nal_type) {
648 self.reset_fragment(Codec::H265);
649 return Ok(None);
650 }
651 if self.h265.corrupted || !self.has_decoder_config(Codec::H265) {
652 if !self.has_decoder_config(Codec::H265) {
653 self.status.config_wait_drops = self.status.config_wait_drops.saturating_add(1);
654 }
655 self.reset_fragment(Codec::H265);
656 return Ok(None);
657 }
658 let data = std::mem::take(&mut self.h265.data);
659 let meta = FrameMeta {
660 timestamp: self.h265.timestamp,
661 is_keyframe: (16..=23).contains(&nal_type),
662 codec: Codec::H265,
663 payload_type: header.payload_type,
664 sequence_number: header.sequence_number,
665 nal_type,
666 };
667 self.reset_fragment(Codec::H265);
668 self.push_complete_owned_annex_b(data, meta, header.marker, false)
669 } else {
670 Ok(None)
671 }
672 }
673
674 fn accept_fragment_sequence(&mut self, codec: Codec, sequence_number: u16) -> bool {
675 let state = match codec {
676 Codec::H264 => &mut self.h264,
677 Codec::H265 => &mut self.h265,
678 };
679 let Some(expected) = state.next_sequence else {
680 return false;
681 };
682 state.next_sequence = Some(sequence_number.wrapping_add(1));
683 if sequence_number != expected {
684 state.data.clear();
685 state.corrupted = true;
686 self.status.fragment_sequence_gaps =
687 self.status.fragment_sequence_gaps.saturating_add(1);
688 return false;
689 }
690 true
691 }
692
693 fn reset_fragment(&mut self, codec: Codec) {
694 let state = match codec {
695 Codec::H264 => &mut self.h264,
696 Codec::H265 => &mut self.h265,
697 };
698 state.data.clear();
699 state.next_sequence = None;
700 state.corrupted = false;
701 }
702
703 fn h264_stap_a(
704 &mut self,
705 payload: &[u8],
706 header: RtpHeader,
707 ) -> Result<Option<DepacketizedFrame>, RtpError> {
708 let mut out = Vec::new();
709 let mut offset = 1;
710 let mut keyframe = false;
711 let mut has_slice = false;
712 let mut has_sps = false;
713 let mut has_pps = false;
714 let mut last_slice_type = 0;
715 while offset + 2 <= payload.len() {
716 let len = u16::from_be_bytes([payload[offset], payload[offset + 1]]) as usize;
717 offset += 2;
718 if len == 0 || offset.saturating_add(len) > payload.len() {
719 return Err(RtpError::UnsupportedPayload);
720 }
721 let nalu = &payload[offset..offset + len];
722 let nal_type = nalu.first().map(|b| b & 0x1f).unwrap_or(0);
723 self.status.last_nal_type = Some(nal_type);
724 match nal_type {
725 7 => {
726 has_sps = true;
727 self.h264_sps = Some(nalu.to_vec());
728 }
729 8 => {
730 has_pps = true;
731 self.h264_pps = Some(nalu.to_vec());
732 }
733 _ => {}
734 }
735 if is_h264_vcl_nal(nal_type) {
736 has_slice = true;
737 keyframe |= nal_type == 5;
738 last_slice_type = nal_type;
739 }
740 append_annex_b(&mut out, nalu);
741 offset += len;
742 }
743 if offset != payload.len() {
744 return Err(RtpError::UnsupportedPayload);
745 }
746 if !has_slice || !self.has_decoder_config(Codec::H264) {
747 if has_slice {
748 self.status.config_wait_drops = self.status.config_wait_drops.saturating_add(1);
749 }
750 return Ok(None);
751 }
752 self.push_complete_owned_annex_b(
753 out,
754 FrameMeta {
755 timestamp: header.timestamp,
756 is_keyframe: keyframe,
757 codec: Codec::H264,
758 payload_type: header.payload_type,
759 sequence_number: header.sequence_number,
760 nal_type: last_slice_type,
761 },
762 header.marker,
763 has_sps && has_pps,
764 )
765 }
766
767 fn h265_ap(
768 &mut self,
769 payload: &[u8],
770 header: RtpHeader,
771 ) -> Result<Option<DepacketizedFrame>, RtpError> {
772 let mut out = Vec::new();
773 let mut offset = 2;
774 let mut keyframe = false;
775 let mut has_slice = false;
776 let mut has_vps = false;
777 let mut has_sps = false;
778 let mut has_pps = false;
779 let mut last_slice_type = 0;
780 while offset + 2 <= payload.len() {
781 let len = u16::from_be_bytes([payload[offset], payload[offset + 1]]) as usize;
782 offset += 2;
783 if len == 0 || offset.saturating_add(len) > payload.len() {
784 return Err(RtpError::UnsupportedPayload);
785 }
786 let nalu = &payload[offset..offset + len];
787 let nal_type = nalu.first().map(|b| (b >> 1) & 0x3f).unwrap_or(0);
788 self.status.last_nal_type = Some(nal_type);
789 match nal_type {
790 32 => {
791 has_vps = true;
792 self.h265_vps = Some(nalu.to_vec());
793 }
794 33 => {
795 has_sps = true;
796 self.h265_sps = Some(nalu.to_vec());
797 }
798 34 => {
799 has_pps = true;
800 self.h265_pps = Some(nalu.to_vec());
801 }
802 _ => {}
803 }
804 if is_h265_vcl_nal(nal_type) {
805 has_slice = true;
806 keyframe |= (16..=23).contains(&nal_type);
807 last_slice_type = nal_type;
808 }
809 append_annex_b(&mut out, nalu);
810 offset += len;
811 }
812 if offset != payload.len() {
813 return Err(RtpError::UnsupportedPayload);
814 }
815 if !has_slice || !self.has_decoder_config(Codec::H265) {
816 if has_slice {
817 self.status.config_wait_drops = self.status.config_wait_drops.saturating_add(1);
818 }
819 return Ok(None);
820 }
821 self.push_complete_owned_annex_b(
822 out,
823 FrameMeta {
824 timestamp: header.timestamp,
825 is_keyframe: keyframe,
826 codec: Codec::H265,
827 payload_type: header.payload_type,
828 sequence_number: header.sequence_number,
829 nal_type: last_slice_type,
830 },
831 header.marker,
832 has_vps && has_sps && has_pps,
833 )
834 }
835
836 fn append_fragment(&mut self, codec: Codec, bytes: &[u8]) -> Result<(), RtpError> {
837 let state = match codec {
838 Codec::H264 => &mut self.h264,
839 Codec::H265 => &mut self.h265,
840 };
841 if state.data.len() + bytes.len() > self.max_fragment {
842 self.status.fragment_overflows = self.status.fragment_overflows.saturating_add(1);
843 return Err(RtpError::FragmentOverflow);
844 }
845 state.data.extend_from_slice(bytes);
846 Ok(())
847 }
848
849 fn push_complete_nalu(
850 &mut self,
851 nalu: &[u8],
852 meta: FrameMeta,
853 marker: bool,
854 ) -> Result<Option<DepacketizedFrame>, RtpError> {
855 let annex_b_len = nalu.len().saturating_add(4);
856 if !self.prepare_access_unit_append(meta, marker, annex_b_len)? {
857 return Ok(None);
858 }
859 let state = self.access_unit_mut(meta.codec);
860 state.data.extend_from_slice(&[0, 0, 0, 1]);
861 state.data.extend_from_slice(nalu);
862 self.finish_access_unit(meta, marker, false)
863 }
864
865 fn push_complete_owned_annex_b(
866 &mut self,
867 annex_b: Vec<u8>,
868 meta: FrameMeta,
869 marker: bool,
870 has_decoder_config: bool,
871 ) -> Result<Option<DepacketizedFrame>, RtpError> {
872 if !self.prepare_access_unit_append(meta, marker, annex_b.len())? {
873 return Ok(None);
874 }
875 let state = self.access_unit_mut(meta.codec);
876 if state.data.is_empty() {
877 state.data = annex_b;
878 } else {
879 state.data.extend_from_slice(&annex_b);
880 }
881 self.finish_access_unit(meta, marker, has_decoder_config)
882 }
883
884 fn prepare_access_unit_append(
885 &mut self,
886 meta: FrameMeta,
887 marker: bool,
888 additional_len: usize,
889 ) -> Result<bool, RtpError> {
890 let max_fragment = self.max_fragment;
891 let state = self.access_unit_mut(meta.codec);
892 debug_assert_eq!(state.timestamp, Some(meta.timestamp));
893 if state.corrupted {
894 if marker {
895 reset_access_unit_state(state);
896 }
897 return Ok(false);
898 }
899 if state.data.len().saturating_add(additional_len) > max_fragment {
900 reset_access_unit_state(state);
901 self.status.fragment_overflows = self.status.fragment_overflows.saturating_add(1);
902 return Err(RtpError::FragmentOverflow);
903 }
904 state.data.reserve(additional_len);
905 Ok(true)
906 }
907
908 fn finish_access_unit(
909 &mut self,
910 meta: FrameMeta,
911 marker: bool,
912 has_decoder_config: bool,
913 ) -> Result<Option<DepacketizedFrame>, RtpError> {
914 let state = self.access_unit_mut(meta.codec);
915 state.is_keyframe |= meta.is_keyframe;
916 state.has_decoder_config |= has_decoder_config;
917 state.nal_type = meta.nal_type;
918 if !marker {
919 return Ok(None);
920 }
921
922 let mut data = std::mem::take(&mut state.data);
923 let is_keyframe = state.is_keyframe;
924 let has_decoder_config = state.has_decoder_config;
925 let nal_type = state.nal_type;
926 reset_access_unit_state(state);
927 if is_keyframe && !has_decoder_config {
928 let mut prefixed = Vec::with_capacity(data.len() + self.cached_config_len(meta.codec));
929 self.prepend_cached_config(&mut prefixed, meta.codec);
930 prefixed.append(&mut data);
931 data = prefixed;
932 }
933 Ok(Some(DepacketizedFrame {
934 data,
935 timestamp: meta.timestamp,
936 is_keyframe,
937 codec: meta.codec,
938 payload_type: meta.payload_type,
939 sequence_number: meta.sequence_number,
940 nal_type,
941 codec_config: self.codec_config(),
942 }))
943 }
944
945 fn access_unit_mut(&mut self, codec: Codec) -> &mut AccessUnitState {
946 match codec {
947 Codec::H264 => &mut self.h264_access_unit,
948 Codec::H265 => &mut self.h265_access_unit,
949 }
950 }
951
952 fn observe_access_unit_packet(&mut self, codec: Codec, header: RtpHeader) {
953 let state = match codec {
954 Codec::H264 => &mut self.h264_access_unit,
955 Codec::H265 => &mut self.h265_access_unit,
956 };
957 if state
958 .timestamp
959 .is_some_and(|timestamp| timestamp != header.timestamp)
960 {
961 if !state.data.is_empty() {
962 self.status.fragment_sequence_gaps =
963 self.status.fragment_sequence_gaps.saturating_add(1);
964 }
965 reset_access_unit_state(state);
966 }
967 if state.timestamp.is_none() {
968 state.timestamp = Some(header.timestamp);
969 } else if state
970 .next_sequence
971 .is_some_and(|expected| expected != header.sequence_number)
972 {
973 if !state.corrupted {
974 self.status.fragment_sequence_gaps =
975 self.status.fragment_sequence_gaps.saturating_add(1);
976 }
977 state.corrupted = true;
978 state.data.clear();
979 }
980 state.next_sequence = Some(header.sequence_number.wrapping_add(1));
981 }
982
983 fn cached_config_len(&self, codec: Codec) -> usize {
984 match codec {
985 Codec::H264 => {
986 self.h264_sps.as_ref().map_or(0, Vec::len)
987 + self.h264_pps.as_ref().map_or(0, Vec::len)
988 + 8
989 }
990 Codec::H265 => {
991 self.h265_vps.as_ref().map_or(0, Vec::len)
992 + self.h265_sps.as_ref().map_or(0, Vec::len)
993 + self.h265_pps.as_ref().map_or(0, Vec::len)
994 + 12
995 }
996 }
997 }
998
999 fn prepend_cached_config(&mut self, data: &mut Vec<u8>, codec: Codec) {
1000 let mut prepended = 0u64;
1001 match codec {
1002 Codec::H264 => {
1003 if let Some(sps) = &self.h264_sps {
1004 append_annex_b(data, sps);
1005 prepended += 1;
1006 }
1007 if let Some(pps) = &self.h264_pps {
1008 append_annex_b(data, pps);
1009 prepended += 1;
1010 }
1011 }
1012 Codec::H265 => {
1013 if let Some(vps) = &self.h265_vps {
1014 append_annex_b(data, vps);
1015 prepended += 1;
1016 }
1017 if let Some(sps) = &self.h265_sps {
1018 append_annex_b(data, sps);
1019 prepended += 1;
1020 }
1021 if let Some(pps) = &self.h265_pps {
1022 append_annex_b(data, pps);
1023 prepended += 1;
1024 }
1025 }
1026 }
1027 if prepended > 0 {
1028 self.status.keyframes_with_prepended_config = self
1029 .status
1030 .keyframes_with_prepended_config
1031 .saturating_add(1);
1032 self.status.parameter_sets_prepended = self
1033 .status
1034 .parameter_sets_prepended
1035 .saturating_add(prepended);
1036 }
1037 }
1038
1039 fn has_decoder_config(&self, codec: Codec) -> bool {
1040 match codec {
1041 Codec::H264 => self.h264_sps.is_some() && self.h264_pps.is_some(),
1042 Codec::H265 => {
1043 self.h265_vps.is_some() && self.h265_sps.is_some() && self.h265_pps.is_some()
1044 }
1045 }
1046 }
1047
1048 fn record_error(&mut self, err: RtpError) {
1049 match err {
1050 RtpError::UnsupportedPayload => {
1051 self.status.unsupported_payloads =
1052 self.status.unsupported_payloads.saturating_add(1);
1053 }
1054 RtpError::FragmentOverflow => {}
1055 _ => {
1056 self.status.malformed_packets = self.status.malformed_packets.saturating_add(1);
1057 }
1058 }
1059 }
1060}
1061
1062fn reset_access_unit_state(state: &mut AccessUnitState) {
1063 state.data.clear();
1064 state.timestamp = None;
1065 state.next_sequence = None;
1066 state.corrupted = false;
1067 state.is_keyframe = false;
1068 state.has_decoder_config = false;
1069 state.nal_type = 0;
1070}
1071
1072fn codec_from_payload_type(payload_type: u8) -> Option<Codec> {
1073 match payload_type {
1074 RTP_PAYLOAD_TYPE_H264 => Some(Codec::H264),
1075 RTP_PAYLOAD_TYPE_H265 => Some(Codec::H265),
1076 _ => None,
1077 }
1078}
1079
1080fn detect_codec(payload: &[u8]) -> Option<Codec> {
1081 if payload.is_empty() {
1082 return None;
1083 }
1084 if payload.len() >= 2 {
1085 let h265_nal_type = (payload[0] >> 1) & 0x3f;
1086 if h265_nal_type == 48 || h265_nal_type == 49 || (32..=40).contains(&h265_nal_type) {
1087 return Some(Codec::H265);
1088 }
1089 }
1090 let h264_nal_type = payload[0] & 0x1f;
1091 if h264_nal_type == 24 || h264_nal_type == 28 || (1..=12).contains(&h264_nal_type) {
1092 return Some(Codec::H264);
1093 }
1094 None
1095}
1096
1097fn is_h264_vcl_nal(nal_type: u8) -> bool {
1098 (1..=5).contains(&nal_type)
1099}
1100
1101fn is_h265_vcl_nal(nal_type: u8) -> bool {
1102 nal_type <= 31
1103}
1104
1105fn append_annex_b(out: &mut Vec<u8>, nalu: &[u8]) {
1106 out.extend_from_slice(&[0, 0, 0, 1]);
1107 out.extend_from_slice(nalu);
1108}
1109
1110#[cfg(test)]
1111mod tests {
1112 use super::*;
1113
1114 fn rtp(payload: &[u8], marker: bool, seq: u16, timestamp: u32) -> Vec<u8> {
1115 rtp_with_payload_type(payload, RTP_PAYLOAD_TYPE_H264, marker, seq, timestamp)
1116 }
1117
1118 fn rtp_with_payload_type(
1119 payload: &[u8],
1120 payload_type: u8,
1121 marker: bool,
1122 seq: u16,
1123 timestamp: u32,
1124 ) -> Vec<u8> {
1125 let mut packet = vec![
1126 0x80,
1127 (if marker { 0x80 } else { 0x00 }) | (payload_type & 0x7f),
1128 ];
1129 packet.extend_from_slice(&seq.to_be_bytes());
1130 packet.extend_from_slice(×tamp.to_be_bytes());
1131 packet.extend_from_slice(&0x1122_3344u32.to_be_bytes());
1132 packet.extend_from_slice(payload);
1133 packet
1134 }
1135
1136 fn stap_a(units: &[&[u8]]) -> Vec<u8> {
1137 let mut payload = vec![24];
1138 for unit in units {
1139 payload.extend_from_slice(&(unit.len() as u16).to_be_bytes());
1140 payload.extend_from_slice(unit);
1141 }
1142 payload
1143 }
1144
1145 fn h265_ap(units: &[&[u8]]) -> Vec<u8> {
1146 let mut payload = vec![0x60, 0x01];
1147 for unit in units {
1148 payload.extend_from_slice(&(unit.len() as u16).to_be_bytes());
1149 payload.extend_from_slice(unit);
1150 }
1151 payload
1152 }
1153
1154 fn prime_h264(depay: &mut RtpDepacketizer) {
1155 assert!(depay
1156 .push(&rtp(&[0x67, 0x64, 0x00, 0x1f], true, 1, 10))
1157 .unwrap()
1158 .is_none());
1159 assert!(depay
1160 .push(&rtp(&[0x68, 0xee], true, 2, 10))
1161 .unwrap()
1162 .is_none());
1163 }
1164
1165 fn prime_h265(depay: &mut RtpDepacketizer) {
1166 for (seq, payload) in [
1167 (1, &[0x40, 0x01, 0xaa][..]),
1168 (2, &[0x42, 0x01, 0xbb][..]),
1169 (3, &[0x44, 0x01, 0xcc][..]),
1170 ] {
1171 assert!(depay
1172 .push(&rtp_with_payload_type(
1173 payload,
1174 RTP_PAYLOAD_TYPE_H265,
1175 true,
1176 seq,
1177 10,
1178 ))
1179 .unwrap()
1180 .is_none());
1181 }
1182 }
1183
1184 #[test]
1185 fn parses_rtp_header() {
1186 let packet = rtp(&[0x65, 1, 2], true, 7, 1234);
1187 let header = RtpHeader::parse(&packet).unwrap();
1188 assert!(header.marker);
1189 assert_eq!(header.payload_type, 96);
1190 assert_eq!(header.sequence_number, 7);
1191 assert_eq!(header.timestamp, 1234);
1192 assert_eq!(header.payload(&packet), &[0x65, 1, 2]);
1193 }
1194
1195 #[test]
1196 fn depacketizes_h264_single_nalu_as_annex_b() {
1197 let mut depay = RtpDepacketizer::new();
1198 prime_h264(&mut depay);
1199 let frame = depay
1200 .push(&rtp(&[0x65, 0xaa], true, 1, 42))
1201 .unwrap()
1202 .unwrap();
1203 assert_eq!(frame.codec, Codec::H264);
1204 assert!(frame.is_keyframe);
1205 assert_eq!(
1206 frame.data,
1207 [
1208 &[0, 0, 0, 1, 0x67, 0x64, 0x00, 0x1f][..],
1209 &[0, 0, 0, 1, 0x68, 0xee][..],
1210 &[0, 0, 0, 1, 0x65, 0xaa][..],
1211 ]
1212 .concat()
1213 );
1214 }
1215
1216 #[test]
1217 fn combines_same_timestamp_h264_slices_until_marker() {
1218 let mut depay = RtpDepacketizer::new();
1219 prime_h264(&mut depay);
1220 assert!(depay
1221 .push(&rtp(&[0x41, 0x80, 0xaa], false, 3, 42))
1222 .unwrap()
1223 .is_none());
1224 let frame = depay
1225 .push(&rtp(&[0x41, 0x40, 0xbb], true, 4, 42))
1226 .unwrap()
1227 .unwrap();
1228
1229 assert_eq!(
1230 frame.data,
1231 [
1232 &[0, 0, 0, 1, 0x41, 0x80, 0xaa][..],
1233 &[0, 0, 0, 1, 0x41, 0x40, 0xbb][..],
1234 ]
1235 .concat()
1236 );
1237 assert_eq!(frame.timestamp, 42);
1238 assert!(!frame.is_keyframe);
1239 }
1240
1241 #[test]
1242 fn drops_access_unit_after_sequence_gap() {
1243 let mut depay = RtpDepacketizer::new();
1244 prime_h264(&mut depay);
1245 assert!(depay
1246 .push(&rtp(&[0x41, 0x80, 0xaa], false, 3, 42))
1247 .unwrap()
1248 .is_none());
1249 assert!(depay
1250 .push(&rtp(&[0x41, 0x40, 0xbb], true, 5, 42))
1251 .unwrap()
1252 .is_none());
1253
1254 assert_eq!(depay.status().fragment_sequence_gaps, 1);
1255 assert!(depay
1256 .push(&rtp(&[0x41, 0xcc], true, 6, 43))
1257 .unwrap()
1258 .is_some());
1259 }
1260
1261 #[test]
1262 fn drops_h264_video_until_sps_and_pps_are_seen() {
1263 let mut depay = RtpDepacketizer::new();
1264 assert!(depay
1265 .push(&rtp(&[0x65, 0xaa], true, 1, 42))
1266 .unwrap()
1267 .is_none());
1268 let status = depay.status();
1269 assert_eq!(status.config_wait_drops, 1);
1270 assert!(!status.codec_config.is_complete_for(Codec::H264));
1271 assert_eq!(status.last_nal_type, Some(5));
1272 }
1273
1274 #[test]
1275 fn h264_payload_type_prevents_h265_false_positive() {
1276 let mut depay = RtpDepacketizer::new();
1277 prime_h264(&mut depay);
1278 let frame = depay
1279 .push(&rtp(&[0x41, 0xaa], true, 1, 42))
1280 .unwrap()
1281 .unwrap();
1282 assert_eq!(frame.codec, Codec::H264);
1283 assert!(!frame.is_keyframe);
1284 assert_eq!(frame.data, &[0, 0, 0, 1, 0x41, 0xaa]);
1285 }
1286
1287 #[test]
1288 fn h264_non_vcl_nal_is_not_emitted_as_video_frame() {
1289 let mut depay = RtpDepacketizer::new();
1290 prime_h264(&mut depay);
1291 assert!(depay
1292 .push(&rtp(&[0x06, 0x05, 0xff], true, 3, 42))
1293 .unwrap()
1294 .is_none());
1295 }
1296
1297 #[test]
1298 fn opus_payload_type_is_not_sniffed_as_video() {
1299 let mut depay = RtpDepacketizer::new();
1300 prime_h264(&mut depay);
1301 let err = depay
1302 .push(&rtp_with_payload_type(
1303 &[0x65, 0xaa],
1304 RTP_PAYLOAD_TYPE_OPUS,
1305 true,
1306 1,
1307 42,
1308 ))
1309 .unwrap_err();
1310 assert_eq!(err, RtpError::UnsupportedPayload);
1311 }
1312
1313 #[test]
1314 fn depacketizes_h265_single_nalu_by_payload_type() {
1315 let mut depay = RtpDepacketizer::new();
1316 prime_h265(&mut depay);
1317 let frame = depay
1318 .push(&rtp_with_payload_type(
1319 &[0x02, 0x01, 0xaa],
1320 RTP_PAYLOAD_TYPE_H265,
1321 true,
1322 1,
1323 42,
1324 ))
1325 .unwrap()
1326 .unwrap();
1327 assert_eq!(frame.codec, Codec::H265);
1328 assert!(!frame.is_keyframe);
1329 assert_eq!(frame.data, &[0, 0, 0, 1, 0x02, 0x01, 0xaa]);
1330 }
1331
1332 #[test]
1333 fn h265_non_vcl_nal_is_not_emitted_as_video_frame() {
1334 let mut depay = RtpDepacketizer::new();
1335 prime_h265(&mut depay);
1336 assert!(depay
1337 .push(&rtp_with_payload_type(
1338 &[0x4e, 0x01, 0xff],
1339 RTP_PAYLOAD_TYPE_H265,
1340 true,
1341 4,
1342 42,
1343 ))
1344 .unwrap()
1345 .is_none());
1346 }
1347
1348 #[test]
1349 fn h264_stap_a_caches_parameter_sets_for_later_keyframe() {
1350 let mut depay = RtpDepacketizer::new();
1351 let sps = &[0x67, 0x64, 0x00, 0x1f][..];
1352 let pps = &[0x68, 0xee][..];
1353 let aggregate = depay.push(&rtp(&stap_a(&[sps, pps]), true, 1, 10)).unwrap();
1354 assert!(aggregate.is_none());
1355
1356 let frame = depay
1357 .push(&rtp(&[0x65, 0xaa], true, 2, 20))
1358 .unwrap()
1359 .unwrap();
1360 assert!(frame.is_keyframe);
1361 assert_eq!(
1362 frame.data,
1363 [
1364 &[0, 0, 0, 1][..],
1365 sps,
1366 &[0, 0, 0, 1][..],
1367 pps,
1368 &[0, 0, 0, 1, 0x65, 0xaa][..],
1369 ]
1370 .concat()
1371 );
1372 }
1373
1374 #[test]
1375 fn h264_stap_a_prepends_cached_parameter_sets_for_idr_without_inband_config() {
1376 let mut depay = RtpDepacketizer::new();
1377 let sps = &[0x67, 0x64, 0x00, 0x1f][..];
1378 let pps = &[0x68, 0xee][..];
1379 depay.push(&rtp(&stap_a(&[sps, pps]), true, 1, 10)).unwrap();
1380
1381 let frame = depay
1382 .push(&rtp(&stap_a(&[&[0x65, 0xaa, 0xbb]]), true, 2, 20))
1383 .unwrap()
1384 .unwrap();
1385
1386 assert!(frame.is_keyframe);
1387 assert_eq!(
1388 frame.data,
1389 [
1390 &[0, 0, 0, 1][..],
1391 sps,
1392 &[0, 0, 0, 1][..],
1393 pps,
1394 &[0, 0, 0, 1, 0x65, 0xaa, 0xbb][..],
1395 ]
1396 .concat()
1397 );
1398 let status = depay.status();
1399 assert_eq!(status.keyframes_with_prepended_config, 1);
1400 assert_eq!(status.parameter_sets_prepended, 2);
1401 }
1402
1403 #[test]
1404 fn h264_stap_a_does_not_duplicate_inband_parameter_sets() {
1405 let mut depay = RtpDepacketizer::new();
1406 let sps = &[0x67, 0x64, 0x00, 0x1f][..];
1407 let pps = &[0x68, 0xee][..];
1408 let frame = depay
1409 .push(&rtp(&stap_a(&[sps, pps, &[0x65, 0xaa]]), true, 1, 20))
1410 .unwrap()
1411 .unwrap();
1412
1413 assert_eq!(
1414 frame.data,
1415 [
1416 &[0, 0, 0, 1][..],
1417 sps,
1418 &[0, 0, 0, 1][..],
1419 pps,
1420 &[0, 0, 0, 1, 0x65, 0xaa][..],
1421 ]
1422 .concat()
1423 );
1424 let status = depay.status();
1425 assert_eq!(status.keyframes_with_prepended_config, 0);
1426 assert_eq!(status.parameter_sets_prepended, 0);
1427 }
1428
1429 #[test]
1430 fn h264_stap_a_waits_for_the_access_unit_marker() {
1431 let mut depay = RtpDepacketizer::new();
1432 let sps = &[0x67, 0x64, 0x00, 0x1f][..];
1433 let pps = &[0x68, 0xee][..];
1434 assert!(depay
1435 .push(&rtp(&stap_a(&[sps, pps, &[0x61, 0xaa]]), false, 1, 20,))
1436 .unwrap()
1437 .is_none());
1438
1439 let frame = depay
1440 .push(&rtp(&[0x61, 0xbb], true, 2, 20))
1441 .unwrap()
1442 .unwrap();
1443 assert_eq!(
1444 frame.data,
1445 [
1446 &[0, 0, 0, 1][..],
1447 sps,
1448 &[0, 0, 0, 1][..],
1449 pps,
1450 &[0, 0, 0, 1, 0x61, 0xaa][..],
1451 &[0, 0, 0, 1, 0x61, 0xbb][..],
1452 ]
1453 .concat()
1454 );
1455 }
1456
1457 #[test]
1458 fn malformed_stap_a_length_is_rejected() {
1459 let mut depay = RtpDepacketizer::new();
1460 let malformed = [24, 0, 8, 0x67, 0x64];
1461 assert_eq!(
1462 depay.push(&rtp(&malformed, true, 1, 20)),
1463 Err(RtpError::UnsupportedPayload)
1464 );
1465 assert_eq!(depay.status().unsupported_payloads, 1);
1466 }
1467
1468 #[test]
1469 fn h265_ap_prepends_cached_parameter_sets_for_keyframe_without_inband_config() {
1470 let mut depay = RtpDepacketizer::new();
1471 prime_h265(&mut depay);
1472 let frame = depay
1473 .push(&rtp_with_payload_type(
1474 &h265_ap(&[&[0x26, 0x01, 0xaa]]),
1475 RTP_PAYLOAD_TYPE_H265,
1476 true,
1477 4,
1478 20,
1479 ))
1480 .unwrap()
1481 .unwrap();
1482
1483 assert!(frame.is_keyframe);
1484 assert_eq!(
1485 frame.data,
1486 [
1487 &[0, 0, 0, 1, 0x40, 0x01, 0xaa][..],
1488 &[0, 0, 0, 1, 0x42, 0x01, 0xbb][..],
1489 &[0, 0, 0, 1, 0x44, 0x01, 0xcc][..],
1490 &[0, 0, 0, 1, 0x26, 0x01, 0xaa][..],
1491 ]
1492 .concat()
1493 );
1494 let status = depay.status();
1495 assert_eq!(status.keyframes_with_prepended_config, 1);
1496 assert_eq!(status.parameter_sets_prepended, 3);
1497 }
1498
1499 #[test]
1500 fn waybeam_separate_parameter_sets_bootstrap_an_idr() {
1501 let packets: [&[u8]; 4] = [
1504 &[
1505 0x80, 0x61, 0x01, 0x00, 0, 0, 0x10, 0, 0, 0, 0xde, 0xad, 0x40, 0x01, 0xaa,
1506 ],
1507 &[
1508 0x80, 0x61, 0x01, 0x01, 0, 0, 0x10, 0, 0, 0, 0xde, 0xad, 0x42, 0x01, 0xbb,
1509 ],
1510 &[
1511 0x80, 0x61, 0x01, 0x02, 0, 0, 0x10, 0, 0, 0, 0xde, 0xad, 0x44, 0x01, 0xcc,
1512 ],
1513 &[
1514 0x80, 0xe1, 0x01, 0x03, 0, 0, 0x10, 0, 0, 0, 0xde, 0xad, 0x26, 0x01, 0xdd,
1515 ],
1516 ];
1517 let mut depay = RtpDepacketizer::new();
1518 let mut frame = None;
1519 for packet in packets {
1520 if let Some(output) = depay.push(packet).unwrap() {
1521 frame = Some(output);
1522 }
1523 }
1524
1525 let frame = frame.expect("Waybeam IDR should produce an access unit");
1526 assert_eq!(frame.codec, Codec::H265);
1527 assert!(frame.is_keyframe);
1528 assert_eq!(
1529 frame.data,
1530 [
1531 &[0, 0, 0, 1, 0x40, 0x01, 0xaa][..],
1532 &[0, 0, 0, 1, 0x42, 0x01, 0xbb][..],
1533 &[0, 0, 0, 1, 0x44, 0x01, 0xcc][..],
1534 &[0, 0, 0, 1, 0x26, 0x01, 0xdd][..],
1535 ]
1536 .concat()
1537 );
1538 }
1539
1540 #[test]
1541 fn waybeam_hevc_fu_vector_round_trips() {
1542 let mut depay = RtpDepacketizer::new();
1543 for (sequence, payload) in [
1544 (0x1ffd, &[0x40, 0x01, 0xaa][..]),
1545 (0x1ffe, &[0x42, 0x01, 0xbb][..]),
1546 (0x1fff, &[0x44, 0x01, 0xcc][..]),
1547 ] {
1548 depay
1549 .push(&rtp_with_payload_type(
1550 payload,
1551 RTP_PAYLOAD_TYPE_H265,
1552 false,
1553 sequence,
1554 0x0101_0100,
1555 ))
1556 .unwrap();
1557 }
1558
1559 let fragments: [&[u8]; 3] = [
1562 &[
1563 0x80, 0x61, 0x20, 0x00, 1, 1, 1, 1, 2, 2, 2, 2, 0x62, 0x01, 0x93, 0xaa, 0xbb,
1564 ],
1565 &[
1566 0x80, 0x61, 0x20, 0x01, 1, 1, 1, 1, 2, 2, 2, 2, 0x62, 0x01, 0x13, 0xcc, 0xdd,
1567 ],
1568 &[
1569 0x80, 0xe1, 0x20, 0x02, 1, 1, 1, 1, 2, 2, 2, 2, 0x62, 0x01, 0x53, 0xee,
1570 ],
1571 ];
1572 let mut frame = None;
1573 for packet in fragments {
1574 if let Some(output) = depay.push(packet).unwrap() {
1575 frame = Some(output);
1576 }
1577 }
1578
1579 let frame = frame.expect("Waybeam FU chain should produce an access unit");
1580 assert_eq!(frame.codec, Codec::H265);
1581 assert!(frame.is_keyframe);
1582 assert!(frame
1583 .data
1584 .ends_with(&[0, 0, 0, 1, 0x26, 0x01, 0xaa, 0xbb, 0xcc, 0xdd, 0xee]));
1585 }
1586
1587 #[test]
1588 fn waybeam_refpred_trail_n_fu_is_accepted() {
1589 let mut depay = RtpDepacketizer::new();
1590 prime_h265(&mut depay);
1591 let fragments: [&[u8]; 2] = [
1592 &[
1593 0x80, 0x61, 0x07, 0x00, 0, 0, 0x70, 0, 0, 0, 0x43, 0x21, 0x62, 0x01, 0x80, 0x55,
1594 0x66,
1595 ],
1596 &[
1597 0x80, 0xe1, 0x07, 0x01, 0, 0, 0x70, 0, 0, 0, 0x43, 0x21, 0x62, 0x01, 0x40, 0x77,
1598 0x88,
1599 ],
1600 ];
1601 let mut frame = None;
1602 for packet in fragments {
1603 if let Some(output) = depay.push(packet).unwrap() {
1604 frame = Some(output);
1605 }
1606 }
1607
1608 let frame = frame.expect("Waybeam TRAIL_N FU chain should be emitted");
1609 assert_eq!(frame.codec, Codec::H265);
1610 assert!(!frame.is_keyframe);
1611 assert_eq!(frame.nal_type, 0);
1612 assert_eq!(
1613 frame.data,
1614 &[0, 0, 0, 1, 0x00, 0x01, 0x55, 0x66, 0x77, 0x88]
1615 );
1616 }
1617
1618 #[test]
1619 fn depacketizes_h264_fu_a() {
1620 let mut depay = RtpDepacketizer::new();
1621 prime_h264(&mut depay);
1622 assert!(depay
1623 .push(&rtp(&[0x7c, 0x85, 1, 2], false, 1, 99))
1624 .unwrap()
1625 .is_none());
1626 let frame = depay
1627 .push(&rtp(&[0x7c, 0x45, 3, 4], true, 2, 99))
1628 .unwrap()
1629 .unwrap();
1630 assert_eq!(
1631 frame.data,
1632 [
1633 &[0, 0, 0, 1, 0x67, 0x64, 0x00, 0x1f][..],
1634 &[0, 0, 0, 1, 0x68, 0xee][..],
1635 &[0, 0, 0, 1, 0x65, 1, 2, 3, 4][..],
1636 ]
1637 .concat()
1638 );
1639 }
1640
1641 #[test]
1642 fn drops_h264_fu_a_after_sequence_gap() {
1643 let mut depay = RtpDepacketizer::new();
1644 prime_h264(&mut depay);
1645 assert!(depay
1646 .push(&rtp(&[0x7c, 0x85, 1, 2], false, 10, 99))
1647 .unwrap()
1648 .is_none());
1649 assert!(depay
1650 .push(&rtp(&[0x7c, 0x45, 3, 4], true, 12, 99))
1651 .unwrap()
1652 .is_none());
1653
1654 assert!(depay
1655 .push(&rtp(&[0x7c, 0x85, 5, 6], false, 13, 100))
1656 .unwrap()
1657 .is_none());
1658 let frame = depay
1659 .push(&rtp(&[0x7c, 0x45, 7, 8], true, 14, 100))
1660 .unwrap()
1661 .unwrap();
1662 assert!(frame.data.ends_with(&[0, 0, 0, 1, 0x65, 5, 6, 7, 8]));
1663 }
1664
1665 #[test]
1666 fn drops_fragment_end_without_start() {
1667 let mut depay = RtpDepacketizer::new();
1668 prime_h264(&mut depay);
1669 assert!(depay
1670 .push(&rtp(&[0x7c, 0x45, 1, 2], true, 10, 99))
1671 .unwrap()
1672 .is_none());
1673 }
1674
1675 #[test]
1676 fn status_tracks_h264_decoder_config() {
1677 let mut depay = RtpDepacketizer::new();
1678 depay
1679 .push(&rtp(&[0x67, 0x64, 0x00, 0x1f], true, 1, 10))
1680 .unwrap();
1681 let status = depay.status();
1682 assert!(status.codec_config.h264_sps);
1683 assert!(!status.codec_config.h264_pps);
1684 assert!(!status.codec_config.is_complete_for(Codec::H264));
1685
1686 depay.push(&rtp(&[0x68, 0xee], true, 2, 10)).unwrap();
1687 let status = depay.status();
1688 assert!(status.codec_config.is_complete_for(Codec::H264));
1689 }
1690
1691 #[test]
1692 fn reorder_buffer_restores_short_out_of_order_burst() {
1693 let mut reorder = RtpReorderBuffer::default();
1694 let first = rtp(&[0x61, 1], true, 10, 90);
1695 let second = rtp(&[0x61, 2], true, 11, 90);
1696 let third = rtp(&[0x61, 3], true, 12, 90);
1697
1698 assert_eq!(reorder.push(&first).unwrap(), vec![first.clone()]);
1699 assert!(reorder.push(&third).unwrap().is_empty());
1700 assert_eq!(reorder.status().buffered_packets, 1);
1701 assert_eq!(reorder.status().reordered_packets, 1);
1702
1703 let ready = reorder.push(&second).unwrap();
1704 assert_eq!(ready, vec![second, third]);
1705 assert_eq!(reorder.status().buffered_packets, 0);
1706 }
1707}