1use std::collections::{BTreeMap, VecDeque};
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
34#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
40pub enum DamagedFramePolicy {
41 #[default]
43 Drop,
44 Forward,
46}
47
48#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
50pub enum FrameDamage {
51 #[default]
53 None,
54 MissingSlice,
56 TruncatedFragment,
58}
59
60impl FrameDamage {
61 pub const fn is_damaged(self) -> bool {
63 !matches!(self, Self::None)
64 }
65
66 const fn combine(self, other: Self) -> Self {
67 match (self, other) {
68 (Self::TruncatedFragment, _) | (_, Self::TruncatedFragment) => Self::TruncatedFragment,
69 (Self::MissingSlice, _) | (_, Self::MissingSlice) => Self::MissingSlice,
70 _ => Self::None,
71 }
72 }
73}
74
75pub const RTP_PAYLOAD_TYPE_H264: u8 = 96;
77pub const RTP_PAYLOAD_TYPE_H265: u8 = 97;
79pub const RTP_PAYLOAD_TYPE_OPUS: u8 = 98;
81
82#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
89pub struct CodecConfigState {
90 pub h264_sps: bool,
92 pub h264_pps: bool,
94 pub h265_vps: bool,
96 pub h265_sps: bool,
98 pub h265_pps: bool,
100}
101
102impl CodecConfigState {
103 pub const fn is_complete_for(self, codec: Codec) -> bool {
105 match codec {
106 Codec::H264 => self.h264_sps && self.h264_pps,
107 Codec::H265 => self.h265_vps && self.h265_sps && self.h265_pps,
108 }
109 }
110}
111
112#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
114pub struct RtpDepacketizerStatus {
115 pub packets: u64,
117 pub frames_emitted: u64,
119 pub config_wait_drops: u64,
121 pub keyframes_with_prepended_config: u64,
123 pub parameter_sets_prepended: u64,
125 pub fragment_sequence_gaps: u64,
127 pub damaged_frames_forwarded: u64,
129 pub damaged_frames_dropped: u64,
131 pub fragment_overflows: u64,
133 pub unsupported_payloads: u64,
135 pub malformed_packets: u64,
137 pub last_payload_type: Option<u8>,
139 pub last_sequence_number: Option<u16>,
141 pub last_timestamp: Option<u32>,
143 pub last_codec: Option<Codec>,
145 pub last_nal_type: Option<u8>,
147 pub codec_config: CodecConfigState,
149}
150
151#[derive(Debug, Clone, Copy, PartialEq, Eq)]
153pub struct RtpHeader {
154 pub marker: bool,
156 pub payload_type: u8,
158 pub sequence_number: u16,
160 pub timestamp: u32,
162 pub ssrc: u32,
164 pub csrc_count: u8,
166 pub has_extension: bool,
168 pub header_len: usize,
170 pub payload_len: usize,
172}
173
174impl RtpHeader {
175 pub fn parse(packet: &[u8]) -> Result<Self, RtpError> {
177 if packet.len() < 12 {
178 return Err(RtpError::TooShort);
179 }
180 let version = packet[0] >> 6;
181 if version != 2 {
182 return Err(RtpError::InvalidVersion(version));
183 }
184
185 let padding = packet[0] & 0x20 != 0;
186 let extension = packet[0] & 0x10 != 0;
187 let csrc_count = packet[0] & 0x0f;
188 let mut header_len = 12 + csrc_count as usize * 4;
189 if packet.len() < header_len {
190 return Err(RtpError::TooShort);
191 }
192
193 if extension {
194 if packet.len() < header_len + 4 {
195 return Err(RtpError::InvalidExtension);
196 }
197 let ext_words =
198 u16::from_be_bytes([packet[header_len + 2], packet[header_len + 3]]) as usize;
199 header_len += 4 + ext_words * 4;
200 if packet.len() < header_len {
201 return Err(RtpError::InvalidExtension);
202 }
203 }
204
205 let padding_len = if padding {
206 let len = *packet.last().ok_or(RtpError::InvalidPadding)? as usize;
207 if len == 0 || len > packet.len() - header_len {
208 return Err(RtpError::InvalidPadding);
209 }
210 len
211 } else {
212 0
213 };
214
215 let payload_len = packet.len() - header_len - padding_len;
216 if payload_len == 0 {
217 return Err(RtpError::EmptyPayload);
218 }
219
220 Ok(Self {
221 marker: packet[1] & 0x80 != 0,
222 payload_type: packet[1] & 0x7f,
223 sequence_number: u16::from_be_bytes([packet[2], packet[3]]),
224 timestamp: u32::from_be_bytes([packet[4], packet[5], packet[6], packet[7]]),
225 ssrc: u32::from_be_bytes([packet[8], packet[9], packet[10], packet[11]]),
226 csrc_count,
227 has_extension: extension,
228 header_len,
229 payload_len,
230 })
231 }
232
233 pub fn payload<'a>(&self, packet: &'a [u8]) -> &'a [u8] {
235 &packet[self.header_len..self.header_len + self.payload_len]
236 }
237}
238
239#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
241pub struct RtpReorderStatus {
242 pub buffered_packets: usize,
244 pub reordered_packets: u64,
246 pub late_packets: u64,
248 pub forced_flushes: u64,
250}
251
252#[derive(Debug, Clone)]
258pub struct RtpReorderBuffer {
259 next_sequence: Option<u16>,
260 pending: BTreeMap<u16, Vec<u8>>,
261 max_depth: usize,
262 status: RtpReorderStatus,
263}
264
265impl Default for RtpReorderBuffer {
266 fn default() -> Self {
267 Self::new(DEFAULT_RTP_REORDER_WINDOW)
268 }
269}
270
271impl RtpReorderBuffer {
272 pub fn new(max_depth: usize) -> Self {
274 Self {
275 next_sequence: None,
276 pending: BTreeMap::new(),
277 max_depth: max_depth.max(1),
278 status: RtpReorderStatus::default(),
279 }
280 }
281
282 pub fn push(&mut self, packet: &[u8]) -> Result<Vec<Vec<u8>>, RtpError> {
284 let header = RtpHeader::parse(packet)?;
285 let sequence = header.sequence_number;
286 let mut ready = Vec::new();
287
288 let Some(expected) = self.next_sequence else {
289 self.next_sequence = Some(sequence.wrapping_add(1));
290 ready.push(packet.to_vec());
291 return Ok(ready);
292 };
293
294 if sequence == expected {
295 self.next_sequence = Some(expected.wrapping_add(1));
296 ready.push(packet.to_vec());
297 self.drain_ready(&mut ready);
298 return Ok(ready);
299 }
300
301 if sequence_is_before(sequence, expected) {
302 self.status.late_packets = self.status.late_packets.saturating_add(1);
303 return Ok(ready);
304 }
305
306 if self.pending.insert(sequence, packet.to_vec()).is_none() {
307 self.status.reordered_packets = self.status.reordered_packets.saturating_add(1);
308 }
309 if self.pending.len() >= self.max_depth {
310 self.force_flush(expected, &mut ready);
311 }
312 self.status.buffered_packets = self.pending.len();
313 Ok(ready)
314 }
315
316 pub fn status(&self) -> RtpReorderStatus {
318 RtpReorderStatus {
319 buffered_packets: self.pending.len(),
320 ..self.status
321 }
322 }
323
324 fn drain_ready(&mut self, ready: &mut Vec<Vec<u8>>) {
325 while let Some(expected) = self.next_sequence {
326 let Some(packet) = self.pending.remove(&expected) else {
327 break;
328 };
329 self.next_sequence = Some(expected.wrapping_add(1));
330 ready.push(packet);
331 }
332 self.status.buffered_packets = self.pending.len();
333 }
334
335 fn force_flush(&mut self, expected: u16, ready: &mut Vec<Vec<u8>>) {
336 let Some(sequence) = self
337 .pending
338 .keys()
339 .copied()
340 .min_by_key(|sequence| sequence.wrapping_sub(expected))
341 else {
342 return;
343 };
344 if let Some(packet) = self.pending.remove(&sequence) {
345 self.status.forced_flushes = self.status.forced_flushes.saturating_add(1);
346 self.next_sequence = Some(sequence.wrapping_add(1));
347 ready.push(packet);
348 self.drain_ready(ready);
349 }
350 }
351}
352
353fn sequence_is_before(sequence: u16, expected: u16) -> bool {
354 let backward = expected.wrapping_sub(sequence);
355 backward != 0 && backward < 0x8000
356}
357
358#[derive(Debug, Clone, PartialEq, Eq)]
360pub struct DepacketizedFrame {
361 pub data: Vec<u8>,
363 pub timestamp: u32,
365 pub is_keyframe: bool,
367 pub codec: Codec,
369 pub payload_type: u8,
371 pub sequence_number: u16,
373 pub nal_type: u8,
375 pub codec_config: CodecConfigState,
377 pub damaged: bool,
379 pub damage: FrameDamage,
381}
382
383#[derive(Debug, Default, Clone)]
384struct FragmentState {
385 data: Vec<u8>,
386 timestamp: u32,
387 next_sequence: Option<u16>,
388 corrupted: bool,
389 is_keyframe: bool,
390 payload_type: u8,
391 sequence_number: u16,
392 nal_type: u8,
393}
394
395#[derive(Debug, Default, Clone)]
396struct AccessUnitState {
397 data: Vec<u8>,
398 timestamp: Option<u32>,
399 next_sequence: Option<u16>,
400 corrupted: bool,
401 damage: FrameDamage,
402 is_keyframe: bool,
403 has_decoder_config: bool,
404 payload_type: u8,
405 sequence_number: u16,
406 nal_type: u8,
407}
408
409#[derive(Debug, Clone, Copy)]
410struct FrameMeta {
411 timestamp: u32,
412 is_keyframe: bool,
413 codec: Codec,
414 payload_type: u8,
415 sequence_number: u16,
416 nal_type: u8,
417 damage: FrameDamage,
418}
419
420#[derive(Debug, Clone)]
426pub struct RtpDepacketizer {
427 h264: FragmentState,
428 h265: FragmentState,
429 h264_access_unit: AccessUnitState,
430 h265_access_unit: AccessUnitState,
431 h264_sps: Option<Vec<u8>>,
432 h264_pps: Option<Vec<u8>>,
433 h265_vps: Option<Vec<u8>>,
434 h265_sps: Option<Vec<u8>>,
435 h265_pps: Option<Vec<u8>>,
436 codec_hint: Option<Codec>,
437 detected_codec: Option<Codec>,
438 h265_legacy_fu: bool,
439 damaged_frame_policy: DamagedFramePolicy,
440 ready_frames: VecDeque<DepacketizedFrame>,
441 max_fragment: usize,
442 status: RtpDepacketizerStatus,
443}
444
445impl Default for RtpDepacketizer {
446 fn default() -> Self {
447 Self::new()
448 }
449}
450
451impl RtpDepacketizer {
452 pub fn new() -> Self {
454 Self {
455 h264: FragmentState::default(),
456 h265: FragmentState::default(),
457 h264_access_unit: AccessUnitState::default(),
458 h265_access_unit: AccessUnitState::default(),
459 h264_sps: None,
460 h264_pps: None,
461 h265_vps: None,
462 h265_sps: None,
463 h265_pps: None,
464 codec_hint: None,
465 detected_codec: None,
466 h265_legacy_fu: false,
467 damaged_frame_policy: DamagedFramePolicy::Drop,
468 ready_frames: VecDeque::new(),
469 max_fragment: DEFAULT_MAX_ACCESS_UNIT_SIZE,
470 status: RtpDepacketizerStatus::default(),
471 }
472 }
473
474 pub fn status(&self) -> RtpDepacketizerStatus {
476 RtpDepacketizerStatus {
477 codec_config: self.codec_config(),
478 ..self.status
479 }
480 }
481
482 pub fn codec_config(&self) -> CodecConfigState {
484 CodecConfigState {
485 h264_sps: self.h264_sps.is_some(),
486 h264_pps: self.h264_pps.is_some(),
487 h265_vps: self.h265_vps.is_some(),
488 h265_sps: self.h265_sps.is_some(),
489 h265_pps: self.h265_pps.is_some(),
490 }
491 }
492
493 pub fn set_codec_hint(&mut self, codec: Option<Codec>) {
500 self.codec_hint = codec;
501 self.detected_codec = codec;
502 }
503
504 pub fn set_damaged_frame_policy(&mut self, policy: DamagedFramePolicy) {
510 self.damaged_frame_policy = policy;
511 }
512
513 pub const fn damaged_frame_policy(&self) -> DamagedFramePolicy {
515 self.damaged_frame_policy
516 }
517
518 pub fn push(&mut self, packet: &[u8]) -> Result<Option<DepacketizedFrame>, RtpError> {
520 self.status.packets = self.status.packets.saturating_add(1);
521 let header = match RtpHeader::parse(packet) {
522 Ok(header) => header,
523 Err(err) => {
524 self.record_error(err);
525 return Err(err);
526 }
527 };
528 self.status.last_payload_type = Some(header.payload_type);
529 self.status.last_sequence_number = Some(header.sequence_number);
530 self.status.last_timestamp = Some(header.timestamp);
531 let rtp_payload = header.payload(packet);
532 let mut annex_b = AnnexBNalus::new(rtp_payload);
533 let first_annex_b = annex_b.next();
534 let payload = first_annex_b.unwrap_or(rtp_payload);
535 if payload.is_empty() {
536 self.record_error(RtpError::EmptyPayload);
537 return Err(RtpError::EmptyPayload);
538 }
539 log::trace!(
540 target: "openipc_core::rtp",
541 "received RTP packet sequence={} timestamp={} pt={} marker={} bytes={}",
542 header.sequence_number,
543 header.timestamp,
544 header.payload_type,
545 header.marker,
546 payload.len()
547 );
548 if header.payload_type == RTP_PAYLOAD_TYPE_OPUS {
549 self.record_error(RtpError::UnsupportedPayload);
550 return Err(RtpError::UnsupportedPayload);
551 }
552 let Some(codec) = self.resolve_codec(header.payload_type, payload) else {
553 self.record_error(RtpError::UnsupportedPayload);
554 return Err(RtpError::UnsupportedPayload);
555 };
556 self.status.last_codec = Some(codec);
557 if let Some(frame) = self.flush_incomplete_fragment_on_timestamp(codec, header.timestamp)? {
558 self.ready_frames.push_back(frame);
559 }
560 if let Some(frame) = self.observe_access_unit_packet(codec, header) {
561 self.ready_frames.push_back(frame);
562 }
563 if first_annex_b.is_some() {
564 let mut current = payload;
565 loop {
566 let next = annex_b.next();
567 let mut unit_header = header;
568 unit_header.marker = header.marker && next.is_none();
569 self.push_codec_payload(codec, current, unit_header)?;
570 let Some(next) = next else {
571 break;
572 };
573 current = next;
574 }
575 } else {
576 self.push_codec_payload(codec, payload, header)?;
577 }
578 let frame = self.ready_frames.pop_front();
579 if frame.is_some() {
580 self.status.frames_emitted = self.status.frames_emitted.saturating_add(1);
581 }
582 Ok(frame)
583 }
584
585 fn push_codec_payload(
586 &mut self,
587 codec: Codec,
588 payload: &[u8],
589 header: RtpHeader,
590 ) -> Result<(), RtpError> {
591 let result = match codec {
592 Codec::H264 => self.push_h264(payload, header),
593 Codec::H265 => self.push_h265(payload, header),
594 };
595 match result {
596 Ok(Some(frame)) => self.ready_frames.push_back(frame),
597 Err(err) => {
598 log::debug!(
599 target: "openipc_core::rtp",
600 "RTP packet rejected sequence={}: {err:?}",
601 header.sequence_number
602 );
603 self.record_error(err);
604 return Err(err);
605 }
606 _ => {}
607 }
608 Ok(())
609 }
610
611 fn resolve_codec(&mut self, payload_type: u8, payload: &[u8]) -> Option<Codec> {
612 if let Some(codec) = self.codec_hint {
613 return Some(codec);
614 }
615 if let Some(codec) = self.detected_codec {
616 return Some(codec);
617 }
618 if let Some(codec) = codec_parameter_set_signature(payload) {
619 self.detected_codec = Some(codec);
620 return Some(codec);
621 }
622 let codec = codec_from_payload_type(payload_type).or_else(|| detect_codec(payload));
623 if payload_type == RTP_PAYLOAD_TYPE_H265 {
624 self.detected_codec = codec;
625 }
626 codec
627 }
628
629 fn push_h264(
630 &mut self,
631 payload: &[u8],
632 header: RtpHeader,
633 ) -> Result<Option<DepacketizedFrame>, RtpError> {
634 let nal_type = payload[0] & 0x1f;
635 self.status.last_nal_type = Some(nal_type);
636 match nal_type {
637 7 => {
638 self.h264_sps = Some(payload.to_vec());
639 Ok(None)
640 }
641 8 => {
642 self.h264_pps = Some(payload.to_vec());
643 Ok(None)
644 }
645 24 => self.h264_stap_a(payload, header),
646 28 => self.h264_fu_a(payload, header),
647 _ if self.has_decoder_config(Codec::H264) && is_h264_vcl_nal(nal_type) => self
648 .push_complete_nalu(
649 payload,
650 FrameMeta {
651 timestamp: header.timestamp,
652 is_keyframe: nal_type == 5,
653 codec: Codec::H264,
654 payload_type: header.payload_type,
655 sequence_number: header.sequence_number,
656 nal_type,
657 damage: FrameDamage::None,
658 },
659 header.marker,
660 ),
661 _ if !is_h264_vcl_nal(nal_type) => Ok(None),
662 _ => {
663 self.status.config_wait_drops = self.status.config_wait_drops.saturating_add(1);
664 Ok(None)
665 }
666 }
667 }
668
669 fn push_h265(
670 &mut self,
671 payload: &[u8],
672 header: RtpHeader,
673 ) -> Result<Option<DepacketizedFrame>, RtpError> {
674 if payload.len() < 2 {
675 return Err(RtpError::UnsupportedPayload);
676 }
677 let nal_type = (payload[0] >> 1) & 0x3f;
678 self.status.last_nal_type = Some(nal_type);
679 match nal_type {
680 32 => {
681 self.h265_vps = Some(payload.to_vec());
682 Ok(None)
683 }
684 33 => {
685 self.h265_sps = Some(payload.to_vec());
686 Ok(None)
687 }
688 34 => {
689 self.h265_pps = Some(payload.to_vec());
690 Ok(None)
691 }
692 48 => self.h265_ap(payload, header),
693 49 => self.h265_fu(payload, header),
694 _ if self.has_decoder_config(Codec::H265) && is_h265_vcl_nal(nal_type) => self
695 .push_complete_nalu(
696 payload,
697 FrameMeta {
698 timestamp: header.timestamp,
699 is_keyframe: (16..=23).contains(&nal_type),
700 codec: Codec::H265,
701 payload_type: header.payload_type,
702 sequence_number: header.sequence_number,
703 nal_type,
704 damage: FrameDamage::None,
705 },
706 header.marker,
707 ),
708 _ if !is_h265_vcl_nal(nal_type) => Ok(None),
709 _ => {
710 self.status.config_wait_drops = self.status.config_wait_drops.saturating_add(1);
711 Ok(None)
712 }
713 }
714 }
715
716 fn h264_fu_a(
717 &mut self,
718 payload: &[u8],
719 header: RtpHeader,
720 ) -> Result<Option<DepacketizedFrame>, RtpError> {
721 if payload.len() < 2 {
722 return Err(RtpError::UnsupportedPayload);
723 }
724 let fu_indicator = payload[0];
725 let fu_header = payload[1];
726 let start = fu_header & 0x80 != 0;
727 let end = fu_header & 0x40 != 0;
728 let nal_type = fu_header & 0x1f;
729 if start {
730 self.h264.data.clear();
731 self.h264.data.extend_from_slice(&[0, 0, 0, 1]);
732 self.h264.timestamp = header.timestamp;
733 self.h264.next_sequence = Some(header.sequence_number.wrapping_add(1));
734 self.h264.corrupted = false;
735 self.h264.is_keyframe = nal_type == 5;
736 self.h264.payload_type = header.payload_type;
737 self.h264.sequence_number = header.sequence_number;
738 self.h264.nal_type = nal_type;
739 self.h264.data.push((fu_indicator & 0xe0) | nal_type);
740 }
741 let append_payload =
742 start || self.accept_fragment_sequence(Codec::H264, header.sequence_number);
743 self.h264.sequence_number = header.sequence_number;
744 if append_payload {
745 self.append_fragment(Codec::H264, &payload[2..])?;
746 }
747 if end {
748 if self.h264.data.is_empty() {
749 self.reset_fragment(Codec::H264);
750 return Ok(None);
751 }
752 if !is_h264_vcl_nal(nal_type) {
753 self.reset_fragment(Codec::H264);
754 return Ok(None);
755 }
756 if (self.h264.corrupted && self.damaged_frame_policy == DamagedFramePolicy::Drop)
757 || !self.has_decoder_config(Codec::H264)
758 {
759 if !self.has_decoder_config(Codec::H264) {
760 self.status.config_wait_drops = self.status.config_wait_drops.saturating_add(1);
761 }
762 self.reset_fragment(Codec::H264);
763 return Ok(None);
764 }
765 let data = std::mem::take(&mut self.h264.data);
766 let meta = FrameMeta {
767 timestamp: self.h264.timestamp,
768 is_keyframe: nal_type == 5,
769 codec: Codec::H264,
770 payload_type: header.payload_type,
771 sequence_number: header.sequence_number,
772 nal_type,
773 damage: if self.h264.corrupted {
774 FrameDamage::TruncatedFragment
775 } else {
776 FrameDamage::None
777 },
778 };
779 self.reset_fragment(Codec::H264);
780 self.push_complete_owned_annex_b(data, meta, header.marker, false)
781 } else {
782 Ok(None)
783 }
784 }
785
786 fn h265_fu(
787 &mut self,
788 payload: &[u8],
789 header: RtpHeader,
790 ) -> Result<Option<DepacketizedFrame>, RtpError> {
791 if payload.len() < 3 {
792 return Err(RtpError::UnsupportedPayload);
793 }
794 let legacy_start = payload[1] & 0x80 != 0;
799 if legacy_start {
800 self.h265_legacy_fu = true;
801 }
802 let legacy = self.h265_legacy_fu;
803 let fu_header = payload[if legacy { 1 } else { 2 }];
804 let start = fu_header & 0x80 != 0;
805 let end = fu_header & 0x40 != 0;
806 let nal_type = if legacy {
807 (fu_header & 0x3f) >> 1
808 } else {
809 fu_header & 0x3f
810 };
811 if start {
812 self.h265.data.clear();
813 self.h265.data.extend_from_slice(&[0, 0, 0, 1]);
814 self.h265.timestamp = header.timestamp;
815 self.h265.next_sequence = Some(header.sequence_number.wrapping_add(1));
816 self.h265.corrupted = false;
817 self.h265.is_keyframe = (16..=23).contains(&nal_type);
818 self.h265.payload_type = header.payload_type;
819 self.h265.sequence_number = header.sequence_number;
820 self.h265.nal_type = nal_type;
821 if legacy {
822 self.h265
823 .data
824 .push((payload[0] & 0x80) | (fu_header & 0x3f));
825 self.h265.data.push(0x01);
828 } else {
829 self.h265.data.push((payload[0] & 0x81) | (nal_type << 1));
830 self.h265.data.push(payload[1]);
831 }
832 }
833 let append_payload =
834 start || self.accept_fragment_sequence(Codec::H265, header.sequence_number);
835 self.h265.sequence_number = header.sequence_number;
836 if append_payload {
837 self.append_fragment(Codec::H265, &payload[if legacy { 2 } else { 3 }..])?;
838 }
839 if end {
840 if self.h265.data.is_empty() {
841 self.reset_fragment(Codec::H265);
842 self.h265_legacy_fu = false;
843 return Ok(None);
844 }
845 if !is_h265_vcl_nal(nal_type) {
846 self.reset_fragment(Codec::H265);
847 self.h265_legacy_fu = false;
848 return Ok(None);
849 }
850 if (self.h265.corrupted && self.damaged_frame_policy == DamagedFramePolicy::Drop)
851 || !self.has_decoder_config(Codec::H265)
852 {
853 if !self.has_decoder_config(Codec::H265) {
854 self.status.config_wait_drops = self.status.config_wait_drops.saturating_add(1);
855 }
856 self.reset_fragment(Codec::H265);
857 self.h265_legacy_fu = false;
858 return Ok(None);
859 }
860 let data = std::mem::take(&mut self.h265.data);
861 let meta = FrameMeta {
862 timestamp: self.h265.timestamp,
863 is_keyframe: (16..=23).contains(&nal_type),
864 codec: Codec::H265,
865 payload_type: header.payload_type,
866 sequence_number: header.sequence_number,
867 nal_type,
868 damage: if self.h265.corrupted {
869 FrameDamage::TruncatedFragment
870 } else {
871 FrameDamage::None
872 },
873 };
874 self.reset_fragment(Codec::H265);
875 self.h265_legacy_fu = false;
876 self.push_complete_owned_annex_b(data, meta, header.marker, false)
877 } else {
878 Ok(None)
879 }
880 }
881
882 fn accept_fragment_sequence(&mut self, codec: Codec, sequence_number: u16) -> bool {
883 let state = match codec {
884 Codec::H264 => &mut self.h264,
885 Codec::H265 => &mut self.h265,
886 };
887 let Some(expected) = state.next_sequence else {
888 return false;
889 };
890 state.next_sequence = Some(sequence_number.wrapping_add(1));
891 if sequence_number != expected {
892 state.corrupted = true;
893 if self.damaged_frame_policy == DamagedFramePolicy::Drop {
894 state.data.clear();
895 self.status.damaged_frames_dropped =
896 self.status.damaged_frames_dropped.saturating_add(1);
897 }
898 return false;
899 }
900 !state.corrupted
901 }
902
903 fn reset_fragment(&mut self, codec: Codec) {
904 let state = match codec {
905 Codec::H264 => &mut self.h264,
906 Codec::H265 => &mut self.h265,
907 };
908 state.data.clear();
909 state.next_sequence = None;
910 state.corrupted = false;
911 state.is_keyframe = false;
912 state.payload_type = 0;
913 state.sequence_number = 0;
914 state.nal_type = 0;
915 }
916
917 fn h264_stap_a(
918 &mut self,
919 payload: &[u8],
920 header: RtpHeader,
921 ) -> Result<Option<DepacketizedFrame>, RtpError> {
922 let mut out = Vec::new();
923 let mut offset = 1;
924 let mut keyframe = false;
925 let mut has_slice = false;
926 let mut has_sps = false;
927 let mut has_pps = false;
928 let mut last_slice_type = 0;
929 while offset + 2 <= payload.len() {
930 let len = u16::from_be_bytes([payload[offset], payload[offset + 1]]) as usize;
931 offset += 2;
932 if len == 0 || offset.saturating_add(len) > payload.len() {
933 return Err(RtpError::UnsupportedPayload);
934 }
935 let nalu = &payload[offset..offset + len];
936 let nal_type = nalu.first().map(|b| b & 0x1f).unwrap_or(0);
937 self.status.last_nal_type = Some(nal_type);
938 match nal_type {
939 7 => {
940 has_sps = true;
941 self.h264_sps = Some(nalu.to_vec());
942 }
943 8 => {
944 has_pps = true;
945 self.h264_pps = Some(nalu.to_vec());
946 }
947 _ => {}
948 }
949 if is_h264_vcl_nal(nal_type) {
950 has_slice = true;
951 keyframe |= nal_type == 5;
952 last_slice_type = nal_type;
953 }
954 append_annex_b(&mut out, nalu);
955 offset += len;
956 }
957 if offset != payload.len() {
958 return Err(RtpError::UnsupportedPayload);
959 }
960 if !has_slice || !self.has_decoder_config(Codec::H264) {
961 if has_slice {
962 self.status.config_wait_drops = self.status.config_wait_drops.saturating_add(1);
963 }
964 return Ok(None);
965 }
966 self.push_complete_owned_annex_b(
967 out,
968 FrameMeta {
969 timestamp: header.timestamp,
970 is_keyframe: keyframe,
971 codec: Codec::H264,
972 payload_type: header.payload_type,
973 sequence_number: header.sequence_number,
974 nal_type: last_slice_type,
975 damage: FrameDamage::None,
976 },
977 header.marker,
978 has_sps && has_pps,
979 )
980 }
981
982 fn h265_ap(
983 &mut self,
984 payload: &[u8],
985 header: RtpHeader,
986 ) -> Result<Option<DepacketizedFrame>, RtpError> {
987 let mut out = Vec::new();
988 let mut offset = 2;
989 let mut keyframe = false;
990 let mut has_slice = false;
991 let mut has_vps = false;
992 let mut has_sps = false;
993 let mut has_pps = false;
994 let mut last_slice_type = 0;
995 while offset + 2 <= payload.len() {
996 let len = u16::from_be_bytes([payload[offset], payload[offset + 1]]) as usize;
997 offset += 2;
998 if len == 0 || offset.saturating_add(len) > payload.len() {
999 return Err(RtpError::UnsupportedPayload);
1000 }
1001 let nalu = &payload[offset..offset + len];
1002 let nal_type = nalu.first().map(|b| (b >> 1) & 0x3f).unwrap_or(0);
1003 self.status.last_nal_type = Some(nal_type);
1004 match nal_type {
1005 32 => {
1006 has_vps = true;
1007 self.h265_vps = Some(nalu.to_vec());
1008 }
1009 33 => {
1010 has_sps = true;
1011 self.h265_sps = Some(nalu.to_vec());
1012 }
1013 34 => {
1014 has_pps = true;
1015 self.h265_pps = Some(nalu.to_vec());
1016 }
1017 _ => {}
1018 }
1019 if is_h265_vcl_nal(nal_type) {
1020 has_slice = true;
1021 keyframe |= (16..=23).contains(&nal_type);
1022 last_slice_type = nal_type;
1023 }
1024 append_annex_b(&mut out, nalu);
1025 offset += len;
1026 }
1027 if offset != payload.len() {
1028 return Err(RtpError::UnsupportedPayload);
1029 }
1030 if !has_slice || !self.has_decoder_config(Codec::H265) {
1031 if has_slice {
1032 self.status.config_wait_drops = self.status.config_wait_drops.saturating_add(1);
1033 }
1034 return Ok(None);
1035 }
1036 self.push_complete_owned_annex_b(
1037 out,
1038 FrameMeta {
1039 timestamp: header.timestamp,
1040 is_keyframe: keyframe,
1041 codec: Codec::H265,
1042 payload_type: header.payload_type,
1043 sequence_number: header.sequence_number,
1044 nal_type: last_slice_type,
1045 damage: FrameDamage::None,
1046 },
1047 header.marker,
1048 has_vps && has_sps && has_pps,
1049 )
1050 }
1051
1052 fn append_fragment(&mut self, codec: Codec, bytes: &[u8]) -> Result<(), RtpError> {
1053 let state = match codec {
1054 Codec::H264 => &mut self.h264,
1055 Codec::H265 => &mut self.h265,
1056 };
1057 if state.data.len() + bytes.len() > self.max_fragment {
1058 self.status.fragment_overflows = self.status.fragment_overflows.saturating_add(1);
1059 return Err(RtpError::FragmentOverflow);
1060 }
1061 state.data.extend_from_slice(bytes);
1062 Ok(())
1063 }
1064
1065 fn push_complete_nalu(
1066 &mut self,
1067 nalu: &[u8],
1068 meta: FrameMeta,
1069 marker: bool,
1070 ) -> Result<Option<DepacketizedFrame>, RtpError> {
1071 let annex_b_len = nalu.len().saturating_add(4);
1072 if !self.prepare_access_unit_append(meta, marker, annex_b_len)? {
1073 return Ok(None);
1074 }
1075 let state = self.access_unit_mut(meta.codec);
1076 state.data.extend_from_slice(&[0, 0, 0, 1]);
1077 state.data.extend_from_slice(nalu);
1078 self.finish_access_unit(meta, marker, false)
1079 }
1080
1081 fn push_complete_owned_annex_b(
1082 &mut self,
1083 annex_b: Vec<u8>,
1084 meta: FrameMeta,
1085 marker: bool,
1086 has_decoder_config: bool,
1087 ) -> Result<Option<DepacketizedFrame>, RtpError> {
1088 if !self.prepare_access_unit_append(meta, marker, annex_b.len())? {
1089 return Ok(None);
1090 }
1091 let state = self.access_unit_mut(meta.codec);
1092 if state.data.is_empty() {
1093 state.data = annex_b;
1094 } else {
1095 state.data.extend_from_slice(&annex_b);
1096 }
1097 self.finish_access_unit(meta, marker, has_decoder_config)
1098 }
1099
1100 fn prepare_access_unit_append(
1101 &mut self,
1102 meta: FrameMeta,
1103 marker: bool,
1104 additional_len: usize,
1105 ) -> Result<bool, RtpError> {
1106 let max_fragment = self.max_fragment;
1107 let drop_damaged = self.damaged_frame_policy == DamagedFramePolicy::Drop;
1108 let state = self.access_unit_mut(meta.codec);
1109 debug_assert_eq!(state.timestamp, Some(meta.timestamp));
1110 if state.corrupted && drop_damaged {
1111 if marker {
1112 reset_access_unit_state(state);
1113 }
1114 return Ok(false);
1115 }
1116 if state.data.len().saturating_add(additional_len) > max_fragment {
1117 reset_access_unit_state(state);
1118 self.status.fragment_overflows = self.status.fragment_overflows.saturating_add(1);
1119 return Err(RtpError::FragmentOverflow);
1120 }
1121 state.data.reserve(additional_len);
1122 Ok(true)
1123 }
1124
1125 fn finish_access_unit(
1126 &mut self,
1127 meta: FrameMeta,
1128 marker: bool,
1129 has_decoder_config: bool,
1130 ) -> Result<Option<DepacketizedFrame>, RtpError> {
1131 let state = self.access_unit_mut(meta.codec);
1132 state.damage = state.damage.combine(meta.damage);
1133 state.corrupted = state.damage.is_damaged();
1134 state.is_keyframe |= meta.is_keyframe;
1135 state.has_decoder_config |= has_decoder_config;
1136 state.payload_type = meta.payload_type;
1137 state.sequence_number = meta.sequence_number;
1138 state.nal_type = meta.nal_type;
1139 if !marker {
1140 return Ok(None);
1141 }
1142
1143 let mut data = std::mem::take(&mut state.data);
1144 let is_keyframe = state.is_keyframe;
1145 let has_decoder_config = state.has_decoder_config;
1146 let nal_type = state.nal_type;
1147 let damage = state.damage;
1148 let damaged = damage.is_damaged();
1149 reset_access_unit_state(state);
1150 if damaged && self.damaged_frame_policy == DamagedFramePolicy::Drop {
1151 self.status.damaged_frames_dropped =
1152 self.status.damaged_frames_dropped.saturating_add(1);
1153 return Ok(None);
1154 }
1155 if damaged {
1156 self.status.damaged_frames_forwarded =
1157 self.status.damaged_frames_forwarded.saturating_add(1);
1158 }
1159 if is_keyframe && !has_decoder_config {
1160 let mut prefixed = Vec::with_capacity(data.len() + self.cached_config_len(meta.codec));
1161 self.prepend_cached_config(&mut prefixed, meta.codec);
1162 prefixed.append(&mut data);
1163 data = prefixed;
1164 }
1165 Ok(Some(DepacketizedFrame {
1166 data,
1167 timestamp: meta.timestamp,
1168 is_keyframe,
1169 codec: meta.codec,
1170 payload_type: meta.payload_type,
1171 sequence_number: meta.sequence_number,
1172 nal_type,
1173 codec_config: self.codec_config(),
1174 damaged,
1175 damage,
1176 }))
1177 }
1178
1179 fn access_unit_mut(&mut self, codec: Codec) -> &mut AccessUnitState {
1180 match codec {
1181 Codec::H264 => &mut self.h264_access_unit,
1182 Codec::H265 => &mut self.h265_access_unit,
1183 }
1184 }
1185
1186 fn observe_access_unit_packet(
1187 &mut self,
1188 codec: Codec,
1189 header: RtpHeader,
1190 ) -> Option<DepacketizedFrame> {
1191 let timestamp_changed = self
1192 .access_unit_mut(codec)
1193 .timestamp
1194 .is_some_and(|timestamp| timestamp != header.timestamp);
1195 let completed = timestamp_changed
1196 .then(|| self.take_unmarked_access_unit(codec))
1197 .flatten();
1198 let drop_damaged = self.damaged_frame_policy == DamagedFramePolicy::Drop;
1199 let (sequence_gap, discarded_data) = {
1200 let state = self.access_unit_mut(codec);
1201 if state.timestamp.is_none() {
1202 state.timestamp = Some(header.timestamp);
1203 }
1204 let sequence_gap = state
1205 .next_sequence
1206 .is_some_and(|expected| expected != header.sequence_number);
1207 let discarded_data = sequence_gap && drop_damaged && !state.data.is_empty();
1208 if sequence_gap {
1209 state.corrupted = true;
1210 state.damage = state.damage.combine(FrameDamage::MissingSlice);
1211 if drop_damaged {
1212 state.data.clear();
1213 }
1214 }
1215 state.next_sequence = Some(header.sequence_number.wrapping_add(1));
1216 (sequence_gap, discarded_data)
1217 };
1218 if sequence_gap {
1219 self.status.fragment_sequence_gaps =
1220 self.status.fragment_sequence_gaps.saturating_add(1);
1221 if discarded_data {
1222 self.status.damaged_frames_dropped =
1223 self.status.damaged_frames_dropped.saturating_add(1);
1224 }
1225 }
1226 completed
1227 }
1228
1229 fn take_unmarked_access_unit(&mut self, codec: Codec) -> Option<DepacketizedFrame> {
1230 let state = self.access_unit_mut(codec);
1231 if state.data.is_empty() {
1232 reset_access_unit_state(state);
1233 return None;
1234 }
1235 let mut data = std::mem::take(&mut state.data);
1236 let timestamp = state.timestamp.unwrap_or_default();
1237 let is_keyframe = state.is_keyframe;
1238 let has_decoder_config = state.has_decoder_config;
1239 let payload_type = state.payload_type;
1240 let sequence_number = state.sequence_number;
1241 let nal_type = state.nal_type;
1242 let damage = state.damage;
1243 let damaged = damage.is_damaged();
1244 reset_access_unit_state(state);
1245 if damaged && self.damaged_frame_policy == DamagedFramePolicy::Drop {
1246 self.status.damaged_frames_dropped =
1247 self.status.damaged_frames_dropped.saturating_add(1);
1248 return None;
1249 }
1250 if damaged {
1251 self.status.damaged_frames_forwarded =
1252 self.status.damaged_frames_forwarded.saturating_add(1);
1253 }
1254 if is_keyframe && !has_decoder_config {
1255 let mut prefixed = Vec::with_capacity(data.len() + self.cached_config_len(codec));
1256 self.prepend_cached_config(&mut prefixed, codec);
1257 prefixed.append(&mut data);
1258 data = prefixed;
1259 }
1260 Some(DepacketizedFrame {
1261 data,
1262 timestamp,
1263 is_keyframe,
1264 codec,
1265 payload_type,
1266 sequence_number,
1267 nal_type,
1268 codec_config: self.codec_config(),
1269 damaged,
1270 damage,
1271 })
1272 }
1273
1274 fn flush_incomplete_fragment_on_timestamp(
1275 &mut self,
1276 codec: Codec,
1277 next_timestamp: u32,
1278 ) -> Result<Option<DepacketizedFrame>, RtpError> {
1279 let should_flush = {
1280 let state = match codec {
1281 Codec::H264 => &self.h264,
1282 Codec::H265 => &self.h265,
1283 };
1284 state.next_sequence.is_some()
1285 && !state.data.is_empty()
1286 && state.timestamp != next_timestamp
1287 };
1288 if !should_flush {
1289 return Ok(None);
1290 }
1291
1292 if !self.has_decoder_config(codec) {
1293 self.reset_fragment(codec);
1294 if codec == Codec::H265 {
1295 self.h265_legacy_fu = false;
1296 }
1297 self.status.config_wait_drops = self.status.config_wait_drops.saturating_add(1);
1298 self.status.damaged_frames_dropped =
1299 self.status.damaged_frames_dropped.saturating_add(1);
1300 return Ok(None);
1301 }
1302
1303 if self.damaged_frame_policy == DamagedFramePolicy::Drop {
1304 self.reset_fragment(codec);
1305 if codec == Codec::H265 {
1306 self.h265_legacy_fu = false;
1307 }
1308 self.status.damaged_frames_dropped =
1309 self.status.damaged_frames_dropped.saturating_add(1);
1310 return Ok(None);
1311 }
1312
1313 let (data, meta) = {
1314 let state = match codec {
1315 Codec::H264 => &mut self.h264,
1316 Codec::H265 => &mut self.h265,
1317 };
1318 let data = std::mem::take(&mut state.data);
1319 let meta = FrameMeta {
1320 timestamp: state.timestamp,
1321 is_keyframe: state.is_keyframe,
1322 codec,
1323 payload_type: state.payload_type,
1324 sequence_number: state.sequence_number,
1325 nal_type: state.nal_type,
1326 damage: FrameDamage::TruncatedFragment,
1327 };
1328 (data, meta)
1329 };
1330 self.reset_fragment(codec);
1331 if codec == Codec::H265 {
1332 self.h265_legacy_fu = false;
1333 }
1334 self.push_complete_owned_annex_b(data, meta, true, false)
1335 }
1336
1337 fn cached_config_len(&self, codec: Codec) -> usize {
1338 match codec {
1339 Codec::H264 => {
1340 self.h264_sps.as_ref().map_or(0, Vec::len)
1341 + self.h264_pps.as_ref().map_or(0, Vec::len)
1342 + 8
1343 }
1344 Codec::H265 => {
1345 self.h265_vps.as_ref().map_or(0, Vec::len)
1346 + self.h265_sps.as_ref().map_or(0, Vec::len)
1347 + self.h265_pps.as_ref().map_or(0, Vec::len)
1348 + 12
1349 }
1350 }
1351 }
1352
1353 fn prepend_cached_config(&mut self, data: &mut Vec<u8>, codec: Codec) {
1354 let mut prepended = 0u64;
1355 match codec {
1356 Codec::H264 => {
1357 if let Some(sps) = &self.h264_sps {
1358 append_annex_b(data, sps);
1359 prepended += 1;
1360 }
1361 if let Some(pps) = &self.h264_pps {
1362 append_annex_b(data, pps);
1363 prepended += 1;
1364 }
1365 }
1366 Codec::H265 => {
1367 if let Some(vps) = &self.h265_vps {
1368 append_annex_b(data, vps);
1369 prepended += 1;
1370 }
1371 if let Some(sps) = &self.h265_sps {
1372 append_annex_b(data, sps);
1373 prepended += 1;
1374 }
1375 if let Some(pps) = &self.h265_pps {
1376 append_annex_b(data, pps);
1377 prepended += 1;
1378 }
1379 }
1380 }
1381 if prepended > 0 {
1382 self.status.keyframes_with_prepended_config = self
1383 .status
1384 .keyframes_with_prepended_config
1385 .saturating_add(1);
1386 self.status.parameter_sets_prepended = self
1387 .status
1388 .parameter_sets_prepended
1389 .saturating_add(prepended);
1390 }
1391 }
1392
1393 fn has_decoder_config(&self, codec: Codec) -> bool {
1394 match codec {
1395 Codec::H264 => self.h264_sps.is_some() && self.h264_pps.is_some(),
1396 Codec::H265 => {
1397 self.h265_vps.is_some() && self.h265_sps.is_some() && self.h265_pps.is_some()
1398 }
1399 }
1400 }
1401
1402 fn record_error(&mut self, err: RtpError) {
1403 match err {
1404 RtpError::UnsupportedPayload => {
1405 self.status.unsupported_payloads =
1406 self.status.unsupported_payloads.saturating_add(1);
1407 }
1408 RtpError::FragmentOverflow => {}
1409 _ => {
1410 self.status.malformed_packets = self.status.malformed_packets.saturating_add(1);
1411 }
1412 }
1413 }
1414}
1415
1416fn reset_access_unit_state(state: &mut AccessUnitState) {
1417 state.data.clear();
1418 state.timestamp = None;
1419 state.next_sequence = None;
1420 state.corrupted = false;
1421 state.damage = FrameDamage::None;
1422 state.is_keyframe = false;
1423 state.has_decoder_config = false;
1424 state.nal_type = 0;
1425}
1426
1427fn codec_from_payload_type(payload_type: u8) -> Option<Codec> {
1428 match payload_type {
1429 RTP_PAYLOAD_TYPE_H264 => Some(Codec::H264),
1430 RTP_PAYLOAD_TYPE_H265 => Some(Codec::H265),
1431 _ => None,
1432 }
1433}
1434
1435fn codec_parameter_set_signature(payload: &[u8]) -> Option<Codec> {
1436 let first = *payload.first()?;
1437 let h264_nal_type = first & 0x1f;
1438 if matches!(h264_nal_type, 7 | 8) {
1439 return Some(Codec::H264);
1440 }
1441 let h265_nal_type = (first >> 1) & 0x3f;
1442 let valid_h265_header =
1443 first & 0x01 == 0 && payload.get(1).is_some_and(|second| second & 0x07 != 0);
1444 (valid_h265_header && matches!(h265_nal_type, 32..=34)).then_some(Codec::H265)
1445}
1446
1447struct AnnexBNalus<'a> {
1448 bytes: &'a [u8],
1449 cursor: Option<usize>,
1450}
1451
1452impl<'a> AnnexBNalus<'a> {
1453 fn new(bytes: &'a [u8]) -> Self {
1454 Self {
1455 bytes,
1456 cursor: annex_b_start_code_len(bytes, 0),
1457 }
1458 }
1459}
1460
1461impl<'a> Iterator for AnnexBNalus<'a> {
1462 type Item = &'a [u8];
1463
1464 fn next(&mut self) -> Option<Self::Item> {
1465 let start = self.cursor?;
1466 let mut next_start = None;
1467 let mut index = start;
1468 while index + 3 <= self.bytes.len() {
1469 if let Some(length) = annex_b_start_code_len(self.bytes, index) {
1470 next_start = Some((index, length));
1471 break;
1472 }
1473 index += 1;
1474 }
1475 let end = next_start.map_or(self.bytes.len(), |(index, _)| index);
1476 self.cursor = next_start.map(|(index, length)| index + length);
1477 (end > start).then(|| &self.bytes[start..end])
1478 }
1479}
1480
1481fn annex_b_start_code_len(bytes: &[u8], offset: usize) -> Option<usize> {
1482 let tail = bytes.get(offset..)?;
1483 if tail.starts_with(&[0, 0, 0, 1]) {
1484 Some(4)
1485 } else if tail.starts_with(&[0, 0, 1]) {
1486 Some(3)
1487 } else {
1488 None
1489 }
1490}
1491
1492fn detect_codec(payload: &[u8]) -> Option<Codec> {
1493 if payload.is_empty() {
1494 return None;
1495 }
1496 if payload.len() >= 2 {
1497 let h265_nal_type = (payload[0] >> 1) & 0x3f;
1498 if h265_nal_type == 48 || h265_nal_type == 49 || (32..=40).contains(&h265_nal_type) {
1499 return Some(Codec::H265);
1500 }
1501 }
1502 let h264_nal_type = payload[0] & 0x1f;
1503 if h264_nal_type == 24 || h264_nal_type == 28 || (1..=12).contains(&h264_nal_type) {
1504 return Some(Codec::H264);
1505 }
1506 None
1507}
1508
1509fn is_h264_vcl_nal(nal_type: u8) -> bool {
1510 (1..=5).contains(&nal_type)
1511}
1512
1513fn is_h265_vcl_nal(nal_type: u8) -> bool {
1514 nal_type <= 31
1515}
1516
1517fn append_annex_b(out: &mut Vec<u8>, nalu: &[u8]) {
1518 out.extend_from_slice(&[0, 0, 0, 1]);
1519 out.extend_from_slice(nalu);
1520}
1521
1522#[cfg(test)]
1523mod tests {
1524 use super::*;
1525
1526 fn rtp(payload: &[u8], marker: bool, seq: u16, timestamp: u32) -> Vec<u8> {
1527 rtp_with_payload_type(payload, RTP_PAYLOAD_TYPE_H264, marker, seq, timestamp)
1528 }
1529
1530 fn rtp_with_payload_type(
1531 payload: &[u8],
1532 payload_type: u8,
1533 marker: bool,
1534 seq: u16,
1535 timestamp: u32,
1536 ) -> Vec<u8> {
1537 let mut packet = vec![
1538 0x80,
1539 (if marker { 0x80 } else { 0x00 }) | (payload_type & 0x7f),
1540 ];
1541 packet.extend_from_slice(&seq.to_be_bytes());
1542 packet.extend_from_slice(×tamp.to_be_bytes());
1543 packet.extend_from_slice(&0x1122_3344u32.to_be_bytes());
1544 packet.extend_from_slice(payload);
1545 packet
1546 }
1547
1548 fn stap_a(units: &[&[u8]]) -> Vec<u8> {
1549 let mut payload = vec![24];
1550 for unit in units {
1551 payload.extend_from_slice(&(unit.len() as u16).to_be_bytes());
1552 payload.extend_from_slice(unit);
1553 }
1554 payload
1555 }
1556
1557 fn h265_ap(units: &[&[u8]]) -> Vec<u8> {
1558 let mut payload = vec![0x60, 0x01];
1559 for unit in units {
1560 payload.extend_from_slice(&(unit.len() as u16).to_be_bytes());
1561 payload.extend_from_slice(unit);
1562 }
1563 payload
1564 }
1565
1566 fn prime_h264(depay: &mut RtpDepacketizer) {
1567 assert!(depay
1568 .push(&rtp(&[0x67, 0x64, 0x00, 0x1f], true, 1, 10))
1569 .unwrap()
1570 .is_none());
1571 assert!(depay
1572 .push(&rtp(&[0x68, 0xee], true, 2, 10))
1573 .unwrap()
1574 .is_none());
1575 }
1576
1577 fn prime_h265(depay: &mut RtpDepacketizer) {
1578 for (seq, payload) in [
1579 (1, &[0x40, 0x01, 0xaa][..]),
1580 (2, &[0x42, 0x01, 0xbb][..]),
1581 (3, &[0x44, 0x01, 0xcc][..]),
1582 ] {
1583 assert!(depay
1584 .push(&rtp_with_payload_type(
1585 payload,
1586 RTP_PAYLOAD_TYPE_H265,
1587 true,
1588 seq,
1589 10,
1590 ))
1591 .unwrap()
1592 .is_none());
1593 }
1594 }
1595
1596 #[test]
1597 fn parses_rtp_header() {
1598 let packet = rtp(&[0x65, 1, 2], true, 7, 1234);
1599 let header = RtpHeader::parse(&packet).unwrap();
1600 assert!(header.marker);
1601 assert_eq!(header.payload_type, 96);
1602 assert_eq!(header.sequence_number, 7);
1603 assert_eq!(header.timestamp, 1234);
1604 assert_eq!(header.payload(&packet), &[0x65, 1, 2]);
1605 }
1606
1607 #[test]
1608 fn depacketizes_h264_single_nalu_as_annex_b() {
1609 let mut depay = RtpDepacketizer::new();
1610 prime_h264(&mut depay);
1611 let frame = depay
1612 .push(&rtp(&[0x65, 0xaa], true, 1, 42))
1613 .unwrap()
1614 .unwrap();
1615 assert_eq!(frame.codec, Codec::H264);
1616 assert!(frame.is_keyframe);
1617 assert_eq!(
1618 frame.data,
1619 [
1620 &[0, 0, 0, 1, 0x67, 0x64, 0x00, 0x1f][..],
1621 &[0, 0, 0, 1, 0x68, 0xee][..],
1622 &[0, 0, 0, 1, 0x65, 0xaa][..],
1623 ]
1624 .concat()
1625 );
1626 }
1627
1628 #[test]
1629 fn combines_same_timestamp_h264_slices_until_marker() {
1630 let mut depay = RtpDepacketizer::new();
1631 prime_h264(&mut depay);
1632 assert!(depay
1633 .push(&rtp(&[0x41, 0x80, 0xaa], false, 3, 42))
1634 .unwrap()
1635 .is_none());
1636 let frame = depay
1637 .push(&rtp(&[0x41, 0x40, 0xbb], true, 4, 42))
1638 .unwrap()
1639 .unwrap();
1640
1641 assert_eq!(
1642 frame.data,
1643 [
1644 &[0, 0, 0, 1, 0x41, 0x80, 0xaa][..],
1645 &[0, 0, 0, 1, 0x41, 0x40, 0xbb][..],
1646 ]
1647 .concat()
1648 );
1649 assert_eq!(frame.timestamp, 42);
1650 assert!(!frame.is_keyframe);
1651 }
1652
1653 #[test]
1654 fn drops_access_unit_after_sequence_gap() {
1655 let mut depay = RtpDepacketizer::new();
1656 prime_h264(&mut depay);
1657 assert!(depay
1658 .push(&rtp(&[0x41, 0x80, 0xaa], false, 3, 42))
1659 .unwrap()
1660 .is_none());
1661 assert!(depay
1662 .push(&rtp(&[0x41, 0x40, 0xbb], true, 5, 42))
1663 .unwrap()
1664 .is_none());
1665
1666 assert_eq!(depay.status().fragment_sequence_gaps, 1);
1667 assert!(depay
1668 .push(&rtp(&[0x41, 0xcc], true, 6, 43))
1669 .unwrap()
1670 .is_some());
1671 }
1672
1673 #[test]
1674 fn forwards_damaged_access_unit_after_sequence_gap_in_fpv_mode() {
1675 let mut depay = RtpDepacketizer::new();
1676 depay.set_damaged_frame_policy(DamagedFramePolicy::Forward);
1677 prime_h264(&mut depay);
1678 assert!(depay
1679 .push(&rtp(&[0x41, 0x80, 0xaa], false, 3, 42))
1680 .unwrap()
1681 .is_none());
1682 let frame = depay
1683 .push(&rtp(&[0x41, 0x40, 0xbb], true, 5, 42))
1684 .unwrap()
1685 .unwrap();
1686
1687 assert!(frame.damaged);
1688 assert_eq!(frame.damage, FrameDamage::MissingSlice);
1689 assert_eq!(frame.timestamp, 42);
1690 assert_eq!(
1691 frame.data,
1692 [
1693 &[0, 0, 0, 1, 0x41, 0x80, 0xaa][..],
1694 &[0, 0, 0, 1, 0x41, 0x40, 0xbb][..],
1695 ]
1696 .concat()
1697 );
1698 assert_eq!(depay.status().damaged_frames_forwarded, 1);
1699 assert_eq!(depay.status().damaged_frames_dropped, 0);
1700 }
1701
1702 #[test]
1703 fn drops_h264_video_until_sps_and_pps_are_seen() {
1704 let mut depay = RtpDepacketizer::new();
1705 assert!(depay
1706 .push(&rtp(&[0x65, 0xaa], true, 1, 42))
1707 .unwrap()
1708 .is_none());
1709 let status = depay.status();
1710 assert_eq!(status.config_wait_drops, 1);
1711 assert!(!status.codec_config.is_complete_for(Codec::H264));
1712 assert_eq!(status.last_nal_type, Some(5));
1713 }
1714
1715 #[test]
1716 fn h264_payload_type_prevents_h265_false_positive() {
1717 let mut depay = RtpDepacketizer::new();
1718 prime_h264(&mut depay);
1719 let frame = depay
1720 .push(&rtp(&[0x41, 0xaa], true, 1, 42))
1721 .unwrap()
1722 .unwrap();
1723 assert_eq!(frame.codec, Codec::H264);
1724 assert!(!frame.is_keyframe);
1725 assert_eq!(frame.data, &[0, 0, 0, 1, 0x41, 0xaa]);
1726 }
1727
1728 #[test]
1729 fn h264_non_vcl_nal_is_not_emitted_as_video_frame() {
1730 let mut depay = RtpDepacketizer::new();
1731 prime_h264(&mut depay);
1732 assert!(depay
1733 .push(&rtp(&[0x06, 0x05, 0xff], true, 3, 42))
1734 .unwrap()
1735 .is_none());
1736 }
1737
1738 #[test]
1739 fn opus_payload_type_is_not_sniffed_as_video() {
1740 let mut depay = RtpDepacketizer::new();
1741 prime_h264(&mut depay);
1742 let err = depay
1743 .push(&rtp_with_payload_type(
1744 &[0x65, 0xaa],
1745 RTP_PAYLOAD_TYPE_OPUS,
1746 true,
1747 1,
1748 42,
1749 ))
1750 .unwrap_err();
1751 assert_eq!(err, RtpError::UnsupportedPayload);
1752 }
1753
1754 #[test]
1755 fn depacketizes_h265_single_nalu_by_payload_type() {
1756 let mut depay = RtpDepacketizer::new();
1757 prime_h265(&mut depay);
1758 let frame = depay
1759 .push(&rtp_with_payload_type(
1760 &[0x02, 0x01, 0xaa],
1761 RTP_PAYLOAD_TYPE_H265,
1762 true,
1763 1,
1764 42,
1765 ))
1766 .unwrap()
1767 .unwrap();
1768 assert_eq!(frame.codec, Codec::H265);
1769 assert!(!frame.is_keyframe);
1770 assert_eq!(frame.data, &[0, 0, 0, 1, 0x02, 0x01, 0xaa]);
1771 }
1772
1773 #[test]
1774 fn h265_non_vcl_nal_is_not_emitted_as_video_frame() {
1775 let mut depay = RtpDepacketizer::new();
1776 prime_h265(&mut depay);
1777 assert!(depay
1778 .push(&rtp_with_payload_type(
1779 &[0x4e, 0x01, 0xff],
1780 RTP_PAYLOAD_TYPE_H265,
1781 true,
1782 4,
1783 42,
1784 ))
1785 .unwrap()
1786 .is_none());
1787 }
1788
1789 #[test]
1790 fn h264_stap_a_caches_parameter_sets_for_later_keyframe() {
1791 let mut depay = RtpDepacketizer::new();
1792 let sps = &[0x67, 0x64, 0x00, 0x1f][..];
1793 let pps = &[0x68, 0xee][..];
1794 let aggregate = depay.push(&rtp(&stap_a(&[sps, pps]), true, 1, 10)).unwrap();
1795 assert!(aggregate.is_none());
1796
1797 let frame = depay
1798 .push(&rtp(&[0x65, 0xaa], true, 2, 20))
1799 .unwrap()
1800 .unwrap();
1801 assert!(frame.is_keyframe);
1802 assert_eq!(
1803 frame.data,
1804 [
1805 &[0, 0, 0, 1][..],
1806 sps,
1807 &[0, 0, 0, 1][..],
1808 pps,
1809 &[0, 0, 0, 1, 0x65, 0xaa][..],
1810 ]
1811 .concat()
1812 );
1813 }
1814
1815 #[test]
1816 fn h264_stap_a_prepends_cached_parameter_sets_for_idr_without_inband_config() {
1817 let mut depay = RtpDepacketizer::new();
1818 let sps = &[0x67, 0x64, 0x00, 0x1f][..];
1819 let pps = &[0x68, 0xee][..];
1820 depay.push(&rtp(&stap_a(&[sps, pps]), true, 1, 10)).unwrap();
1821
1822 let frame = depay
1823 .push(&rtp(&stap_a(&[&[0x65, 0xaa, 0xbb]]), true, 2, 20))
1824 .unwrap()
1825 .unwrap();
1826
1827 assert!(frame.is_keyframe);
1828 assert_eq!(
1829 frame.data,
1830 [
1831 &[0, 0, 0, 1][..],
1832 sps,
1833 &[0, 0, 0, 1][..],
1834 pps,
1835 &[0, 0, 0, 1, 0x65, 0xaa, 0xbb][..],
1836 ]
1837 .concat()
1838 );
1839 let status = depay.status();
1840 assert_eq!(status.keyframes_with_prepended_config, 1);
1841 assert_eq!(status.parameter_sets_prepended, 2);
1842 }
1843
1844 #[test]
1845 fn h264_stap_a_does_not_duplicate_inband_parameter_sets() {
1846 let mut depay = RtpDepacketizer::new();
1847 let sps = &[0x67, 0x64, 0x00, 0x1f][..];
1848 let pps = &[0x68, 0xee][..];
1849 let frame = depay
1850 .push(&rtp(&stap_a(&[sps, pps, &[0x65, 0xaa]]), true, 1, 20))
1851 .unwrap()
1852 .unwrap();
1853
1854 assert_eq!(
1855 frame.data,
1856 [
1857 &[0, 0, 0, 1][..],
1858 sps,
1859 &[0, 0, 0, 1][..],
1860 pps,
1861 &[0, 0, 0, 1, 0x65, 0xaa][..],
1862 ]
1863 .concat()
1864 );
1865 let status = depay.status();
1866 assert_eq!(status.keyframes_with_prepended_config, 0);
1867 assert_eq!(status.parameter_sets_prepended, 0);
1868 }
1869
1870 #[test]
1871 fn h264_stap_a_waits_for_the_access_unit_marker() {
1872 let mut depay = RtpDepacketizer::new();
1873 let sps = &[0x67, 0x64, 0x00, 0x1f][..];
1874 let pps = &[0x68, 0xee][..];
1875 assert!(depay
1876 .push(&rtp(&stap_a(&[sps, pps, &[0x61, 0xaa]]), false, 1, 20,))
1877 .unwrap()
1878 .is_none());
1879
1880 let frame = depay
1881 .push(&rtp(&[0x61, 0xbb], true, 2, 20))
1882 .unwrap()
1883 .unwrap();
1884 assert_eq!(
1885 frame.data,
1886 [
1887 &[0, 0, 0, 1][..],
1888 sps,
1889 &[0, 0, 0, 1][..],
1890 pps,
1891 &[0, 0, 0, 1, 0x61, 0xaa][..],
1892 &[0, 0, 0, 1, 0x61, 0xbb][..],
1893 ]
1894 .concat()
1895 );
1896 }
1897
1898 #[test]
1899 fn malformed_stap_a_length_is_rejected() {
1900 let mut depay = RtpDepacketizer::new();
1901 let malformed = [24, 0, 8, 0x67, 0x64];
1902 assert_eq!(
1903 depay.push(&rtp(&malformed, true, 1, 20)),
1904 Err(RtpError::UnsupportedPayload)
1905 );
1906 assert_eq!(depay.status().unsupported_payloads, 1);
1907 }
1908
1909 #[test]
1910 fn h265_ap_prepends_cached_parameter_sets_for_keyframe_without_inband_config() {
1911 let mut depay = RtpDepacketizer::new();
1912 prime_h265(&mut depay);
1913 let frame = depay
1914 .push(&rtp_with_payload_type(
1915 &h265_ap(&[&[0x26, 0x01, 0xaa]]),
1916 RTP_PAYLOAD_TYPE_H265,
1917 true,
1918 4,
1919 20,
1920 ))
1921 .unwrap()
1922 .unwrap();
1923
1924 assert!(frame.is_keyframe);
1925 assert_eq!(
1926 frame.data,
1927 [
1928 &[0, 0, 0, 1, 0x40, 0x01, 0xaa][..],
1929 &[0, 0, 0, 1, 0x42, 0x01, 0xbb][..],
1930 &[0, 0, 0, 1, 0x44, 0x01, 0xcc][..],
1931 &[0, 0, 0, 1, 0x26, 0x01, 0xaa][..],
1932 ]
1933 .concat()
1934 );
1935 let status = depay.status();
1936 assert_eq!(status.keyframes_with_prepended_config, 1);
1937 assert_eq!(status.parameter_sets_prepended, 3);
1938 }
1939
1940 #[test]
1941 fn waybeam_separate_parameter_sets_bootstrap_an_idr() {
1942 let packets: [&[u8]; 4] = [
1945 &[
1946 0x80, 0x61, 0x01, 0x00, 0, 0, 0x10, 0, 0, 0, 0xde, 0xad, 0x40, 0x01, 0xaa,
1947 ],
1948 &[
1949 0x80, 0x61, 0x01, 0x01, 0, 0, 0x10, 0, 0, 0, 0xde, 0xad, 0x42, 0x01, 0xbb,
1950 ],
1951 &[
1952 0x80, 0x61, 0x01, 0x02, 0, 0, 0x10, 0, 0, 0, 0xde, 0xad, 0x44, 0x01, 0xcc,
1953 ],
1954 &[
1955 0x80, 0xe1, 0x01, 0x03, 0, 0, 0x10, 0, 0, 0, 0xde, 0xad, 0x26, 0x01, 0xdd,
1956 ],
1957 ];
1958 let mut depay = RtpDepacketizer::new();
1959 let mut frame = None;
1960 for packet in packets {
1961 if let Some(output) = depay.push(packet).unwrap() {
1962 frame = Some(output);
1963 }
1964 }
1965
1966 let frame = frame.expect("Waybeam IDR should produce an access unit");
1967 assert_eq!(frame.codec, Codec::H265);
1968 assert!(frame.is_keyframe);
1969 assert_eq!(
1970 frame.data,
1971 [
1972 &[0, 0, 0, 1, 0x40, 0x01, 0xaa][..],
1973 &[0, 0, 0, 1, 0x42, 0x01, 0xbb][..],
1974 &[0, 0, 0, 1, 0x44, 0x01, 0xcc][..],
1975 &[0, 0, 0, 1, 0x26, 0x01, 0xdd][..],
1976 ]
1977 .concat()
1978 );
1979 }
1980
1981 #[test]
1982 fn divinus_h265_payload_type_96_is_detected_from_parameter_sets() {
1983 let mut depay = RtpDepacketizer::new();
1986 for (sequence, timestamp, payload) in [
1987 (1, 1_000, &[0x40, 0x01, 0xaa][..]),
1988 (2, 4_000, &[0x42, 0x01, 0xbb][..]),
1989 (3, 7_000, &[0x44, 0x01, 0xcc][..]),
1990 ] {
1991 assert!(depay
1992 .push(&rtp_with_payload_type(
1993 payload,
1994 RTP_PAYLOAD_TYPE_H264,
1995 false,
1996 sequence,
1997 timestamp,
1998 ))
1999 .unwrap()
2000 .is_none());
2001 }
2002
2003 let frame = depay
2004 .push(&rtp_with_payload_type(
2005 &[0x28, 0x01, 0xdd],
2006 RTP_PAYLOAD_TYPE_H264,
2007 true,
2008 4,
2009 10_000,
2010 ))
2011 .unwrap()
2012 .unwrap();
2013 assert_eq!(frame.codec, Codec::H265);
2014 assert!(frame.is_keyframe);
2015 assert!(frame.data.ends_with(&[0, 0, 0, 1, 0x28, 0x01, 0xdd]));
2016 }
2017
2018 #[test]
2019 fn divinus_annex_b_bundle_is_split_without_copying() {
2020 let payload = [
2021 &[0, 0, 0, 1, 0x40, 0x01, 0xaa][..],
2022 &[0, 0, 0, 1, 0x42, 0x01, 0xbb][..],
2023 &[0, 0, 0, 1, 0x44, 0x01, 0xcc][..],
2024 &[0, 0, 0, 1, 0x26, 0x01, 0xdd][..],
2025 ]
2026 .concat();
2027 let mut depay = RtpDepacketizer::new();
2028 let frame = depay
2029 .push(&rtp_with_payload_type(
2030 &payload,
2031 RTP_PAYLOAD_TYPE_H264,
2032 true,
2033 1,
2034 3_000,
2035 ))
2036 .unwrap()
2037 .unwrap();
2038
2039 assert_eq!(frame.codec, Codec::H265);
2040 assert!(frame.is_keyframe);
2041 assert_eq!(frame.data, payload);
2042 }
2043
2044 #[test]
2045 fn divinus_unmarked_picture_finishes_when_timestamp_advances() {
2046 let mut depay = RtpDepacketizer::new();
2047 prime_h264(&mut depay);
2048 assert!(depay
2049 .push(&rtp(&[0x61, 0xaa], false, 3, 3_000))
2050 .unwrap()
2051 .is_none());
2052
2053 let frame = depay
2054 .push(&rtp(&[0x61, 0xbb], false, 4, 6_000))
2055 .unwrap()
2056 .unwrap();
2057 assert_eq!(frame.codec, Codec::H264);
2058 assert_eq!(frame.timestamp, 3_000);
2059 assert_eq!(frame.data, [0, 0, 0, 1, 0x61, 0xaa]);
2060 }
2061
2062 #[test]
2063 fn divinus_legacy_two_byte_h265_fu_is_reassembled() {
2064 let mut depay = RtpDepacketizer::new();
2065 for (sequence, payload) in [
2066 (1, &[0x40, 0x01, 0xaa][..]),
2067 (2, &[0x42, 0x01, 0xbb][..]),
2068 (3, &[0x44, 0x01, 0xcc][..]),
2069 ] {
2070 depay
2071 .push(&rtp_with_payload_type(
2072 payload,
2073 RTP_PAYLOAD_TYPE_H264,
2074 false,
2075 sequence,
2076 10,
2077 ))
2078 .unwrap();
2079 }
2080
2081 assert!(depay
2084 .push(&rtp(&[0x62, 0xa6, 0xaa, 0xbb], false, 4, 3_000))
2085 .unwrap()
2086 .is_none());
2087 assert!(depay
2088 .push(&rtp(&[0x62, 0x66, 0xcc, 0xdd], false, 5, 3_000))
2089 .unwrap()
2090 .is_none());
2091
2092 let frame = depay
2093 .push(&rtp(&[0x02, 0x01, 0xee], false, 6, 6_000))
2094 .unwrap()
2095 .unwrap();
2096 assert_eq!(frame.codec, Codec::H265);
2097 assert!(frame.is_keyframe);
2098 assert!(frame
2099 .data
2100 .ends_with(&[0, 0, 0, 1, 0x26, 0x01, 0xaa, 0xbb, 0xcc, 0xdd]));
2101 }
2102
2103 #[test]
2104 fn waybeam_hevc_fu_vector_round_trips() {
2105 let mut depay = RtpDepacketizer::new();
2106 for (sequence, payload) in [
2107 (0x1ffd, &[0x40, 0x01, 0xaa][..]),
2108 (0x1ffe, &[0x42, 0x01, 0xbb][..]),
2109 (0x1fff, &[0x44, 0x01, 0xcc][..]),
2110 ] {
2111 depay
2112 .push(&rtp_with_payload_type(
2113 payload,
2114 RTP_PAYLOAD_TYPE_H265,
2115 false,
2116 sequence,
2117 0x0101_0100,
2118 ))
2119 .unwrap();
2120 }
2121
2122 let fragments: [&[u8]; 3] = [
2125 &[
2126 0x80, 0x61, 0x20, 0x00, 1, 1, 1, 1, 2, 2, 2, 2, 0x62, 0x01, 0x93, 0xaa, 0xbb,
2127 ],
2128 &[
2129 0x80, 0x61, 0x20, 0x01, 1, 1, 1, 1, 2, 2, 2, 2, 0x62, 0x01, 0x13, 0xcc, 0xdd,
2130 ],
2131 &[
2132 0x80, 0xe1, 0x20, 0x02, 1, 1, 1, 1, 2, 2, 2, 2, 0x62, 0x01, 0x53, 0xee,
2133 ],
2134 ];
2135 let mut frame = None;
2136 for packet in fragments {
2137 if let Some(output) = depay.push(packet).unwrap() {
2138 frame = Some(output);
2139 }
2140 }
2141
2142 let frame = frame.expect("Waybeam FU chain should produce an access unit");
2143 assert_eq!(frame.codec, Codec::H265);
2144 assert!(frame.is_keyframe);
2145 assert!(frame
2146 .data
2147 .ends_with(&[0, 0, 0, 1, 0x26, 0x01, 0xaa, 0xbb, 0xcc, 0xdd, 0xee]));
2148 }
2149
2150 #[test]
2151 fn waybeam_refpred_trail_n_fu_is_accepted() {
2152 let mut depay = RtpDepacketizer::new();
2153 prime_h265(&mut depay);
2154 let fragments: [&[u8]; 2] = [
2155 &[
2156 0x80, 0x61, 0x07, 0x00, 0, 0, 0x70, 0, 0, 0, 0x43, 0x21, 0x62, 0x01, 0x80, 0x55,
2157 0x66,
2158 ],
2159 &[
2160 0x80, 0xe1, 0x07, 0x01, 0, 0, 0x70, 0, 0, 0, 0x43, 0x21, 0x62, 0x01, 0x40, 0x77,
2161 0x88,
2162 ],
2163 ];
2164 let mut frame = None;
2165 for packet in fragments {
2166 if let Some(output) = depay.push(packet).unwrap() {
2167 frame = Some(output);
2168 }
2169 }
2170
2171 let frame = frame.expect("Waybeam TRAIL_N FU chain should be emitted");
2172 assert_eq!(frame.codec, Codec::H265);
2173 assert!(!frame.is_keyframe);
2174 assert_eq!(frame.nal_type, 0);
2175 assert_eq!(
2176 frame.data,
2177 &[0, 0, 0, 1, 0x00, 0x01, 0x55, 0x66, 0x77, 0x88]
2178 );
2179 }
2180
2181 #[test]
2182 fn depacketizes_h264_fu_a() {
2183 let mut depay = RtpDepacketizer::new();
2184 prime_h264(&mut depay);
2185 assert!(depay
2186 .push(&rtp(&[0x7c, 0x85, 1, 2], false, 1, 99))
2187 .unwrap()
2188 .is_none());
2189 let frame = depay
2190 .push(&rtp(&[0x7c, 0x45, 3, 4], true, 2, 99))
2191 .unwrap()
2192 .unwrap();
2193 assert_eq!(
2194 frame.data,
2195 [
2196 &[0, 0, 0, 1, 0x67, 0x64, 0x00, 0x1f][..],
2197 &[0, 0, 0, 1, 0x68, 0xee][..],
2198 &[0, 0, 0, 1, 0x65, 1, 2, 3, 4][..],
2199 ]
2200 .concat()
2201 );
2202 }
2203
2204 #[test]
2205 fn drops_h264_fu_a_after_sequence_gap() {
2206 let mut depay = RtpDepacketizer::new();
2207 prime_h264(&mut depay);
2208 assert!(depay
2209 .push(&rtp(&[0x7c, 0x85, 1, 2], false, 10, 99))
2210 .unwrap()
2211 .is_none());
2212 assert!(depay
2213 .push(&rtp(&[0x7c, 0x45, 3, 4], true, 12, 99))
2214 .unwrap()
2215 .is_none());
2216
2217 assert!(depay
2218 .push(&rtp(&[0x7c, 0x85, 5, 6], false, 13, 100))
2219 .unwrap()
2220 .is_none());
2221 let frame = depay
2222 .push(&rtp(&[0x7c, 0x45, 7, 8], true, 14, 100))
2223 .unwrap()
2224 .unwrap();
2225 assert!(frame.data.ends_with(&[0, 0, 0, 1, 0x65, 5, 6, 7, 8]));
2226 }
2227
2228 #[test]
2229 fn forwards_partial_h264_fu_after_sequence_gap_in_fpv_mode() {
2230 let mut depay = RtpDepacketizer::new();
2231 depay.set_damaged_frame_policy(DamagedFramePolicy::Forward);
2232 prime_h264(&mut depay);
2233 assert!(depay
2234 .push(&rtp(&[0x7c, 0x85, 1, 2], false, 10, 99))
2235 .unwrap()
2236 .is_none());
2237 let frame = depay
2238 .push(&rtp(&[0x7c, 0x45, 5, 6], true, 12, 99))
2239 .unwrap()
2240 .unwrap();
2241
2242 assert!(frame.damaged);
2243 assert_eq!(frame.damage, FrameDamage::TruncatedFragment);
2244 assert!(frame.data.ends_with(&[0, 0, 0, 1, 0x65, 1, 2]));
2245 assert_eq!(depay.status().fragment_sequence_gaps, 1);
2246 assert_eq!(depay.status().damaged_frames_forwarded, 1);
2247 }
2248
2249 #[test]
2250 fn forwards_partial_h265_fu_after_sequence_gap_in_fpv_mode() {
2251 let mut depay = RtpDepacketizer::new();
2252 depay.set_damaged_frame_policy(DamagedFramePolicy::Forward);
2253 prime_h265(&mut depay);
2254 assert!(depay
2255 .push(&rtp_with_payload_type(
2256 &[0x62, 0x01, 0x93, 0xaa, 0xbb],
2257 RTP_PAYLOAD_TYPE_H265,
2258 false,
2259 10,
2260 99,
2261 ))
2262 .unwrap()
2263 .is_none());
2264 let frame = depay
2265 .push(&rtp_with_payload_type(
2266 &[0x62, 0x01, 0x53, 0xee],
2267 RTP_PAYLOAD_TYPE_H265,
2268 true,
2269 12,
2270 99,
2271 ))
2272 .unwrap()
2273 .unwrap();
2274
2275 assert!(frame.damaged);
2276 assert_eq!(frame.damage, FrameDamage::TruncatedFragment);
2277 assert!(frame.data.ends_with(&[0, 0, 0, 1, 0x26, 0x01, 0xaa, 0xbb]));
2278 assert_eq!(depay.status().fragment_sequence_gaps, 1);
2279 assert_eq!(depay.status().damaged_frames_forwarded, 1);
2280 }
2281
2282 #[test]
2283 fn timestamp_transition_flushes_incomplete_fu_in_fpv_mode() {
2284 let mut depay = RtpDepacketizer::new();
2285 depay.set_damaged_frame_policy(DamagedFramePolicy::Forward);
2286 prime_h264(&mut depay);
2287 assert!(depay
2288 .push(&rtp(&[0x7c, 0x85, 1, 2], false, 3, 99))
2289 .unwrap()
2290 .is_none());
2291
2292 let frame = depay
2293 .push(&rtp(&[0x41, 0xaa], true, 4, 100))
2294 .unwrap()
2295 .unwrap();
2296 assert!(frame.damaged);
2297 assert_eq!(frame.damage, FrameDamage::TruncatedFragment);
2298 assert_eq!(frame.timestamp, 99);
2299 assert!(frame.data.ends_with(&[0, 0, 0, 1, 0x65, 1, 2]));
2300 assert_eq!(depay.status().damaged_frames_forwarded, 1);
2301 }
2302
2303 #[test]
2304 fn drops_fragment_end_without_start() {
2305 let mut depay = RtpDepacketizer::new();
2306 prime_h264(&mut depay);
2307 assert!(depay
2308 .push(&rtp(&[0x7c, 0x45, 1, 2], true, 10, 99))
2309 .unwrap()
2310 .is_none());
2311 }
2312
2313 #[test]
2314 fn status_tracks_h264_decoder_config() {
2315 let mut depay = RtpDepacketizer::new();
2316 depay
2317 .push(&rtp(&[0x67, 0x64, 0x00, 0x1f], true, 1, 10))
2318 .unwrap();
2319 let status = depay.status();
2320 assert!(status.codec_config.h264_sps);
2321 assert!(!status.codec_config.h264_pps);
2322 assert!(!status.codec_config.is_complete_for(Codec::H264));
2323
2324 depay.push(&rtp(&[0x68, 0xee], true, 2, 10)).unwrap();
2325 let status = depay.status();
2326 assert!(status.codec_config.is_complete_for(Codec::H264));
2327 }
2328
2329 #[test]
2330 fn reorder_buffer_restores_short_out_of_order_burst() {
2331 let mut reorder = RtpReorderBuffer::default();
2332 let first = rtp(&[0x61, 1], true, 10, 90);
2333 let second = rtp(&[0x61, 2], true, 11, 90);
2334 let third = rtp(&[0x61, 3], true, 12, 90);
2335
2336 assert_eq!(reorder.push(&first).unwrap(), vec![first.clone()]);
2337 assert!(reorder.push(&third).unwrap().is_empty());
2338 assert_eq!(reorder.status().buffered_packets, 1);
2339 assert_eq!(reorder.status().reordered_packets, 1);
2340
2341 let ready = reorder.push(&second).unwrap();
2342 assert_eq!(ready, vec![second, third]);
2343 assert_eq!(reorder.status().buffered_packets, 0);
2344 }
2345}