1use bytes::Bytes;
34
35const ANNEXB_START: [u8; 4] = [0, 0, 0, 1];
37
38#[derive(Debug, Clone, Copy, PartialEq, Eq)]
40pub struct RtpHeader {
41 pub payload_type: u8,
43 pub marker: bool,
45 pub sequence: u16,
47 pub timestamp: u32,
49 pub ssrc: u32,
51 pub payload_offset: usize,
53}
54
55impl RtpHeader {
56 pub fn parse(buf: &[u8]) -> Option<RtpHeader> {
60 use super::byteops::ByteReader;
61 let mut r = ByteReader::new(buf);
62 let b0 = r.u8()?;
63 if b0 >> 6 != 2 {
64 return None; }
66 let has_extension = b0 & 0x10 != 0;
67 let csrc_count = (b0 & 0x0F) as usize;
68 let b1 = r.u8()?;
69 let marker = b1 & 0x80 != 0;
70 let payload_type = b1 & 0x7F;
71 let sequence = r.u16_be()?;
72 let timestamp = r.u32_be()?;
73 let ssrc = r.u32_be()?;
74 r.skip(csrc_count * 4)?; if has_extension {
77 r.skip(2)?;
79 let ext_words = r.u16_be()? as usize;
80 r.skip(ext_words * 4)?;
81 }
82 Some(RtpHeader {
83 payload_type,
84 marker,
85 sequence,
86 timestamp,
87 ssrc,
88 payload_offset: r.position(),
89 })
90 }
91}
92
93pub fn rtp_extension_value(buf: &[u8], ext_id: u8) -> Option<&[u8]> {
100 if buf.len() < 12 || buf[0] >> 6 != 2 || buf[0] & 0x10 == 0 {
101 return None; }
103 let csrc = (buf[0] & 0x0F) as usize;
104 let mut p = 12 + csrc * 4;
105 if buf.len() < p + 4 {
106 return None;
107 }
108 let profile = u16::from_be_bytes([buf[p], buf[p + 1]]);
109 let words = u16::from_be_bytes([buf[p + 2], buf[p + 3]]) as usize;
110 p += 4;
111 let end = p.checked_add(words * 4)?;
112 let ext = buf.get(p..end)?;
113
114 if profile == 0xBEDE {
115 let mut i = 0;
117 while i < ext.len() {
118 let b = ext[i];
119 if b == 0 {
120 i += 1; continue;
122 }
123 let id = b >> 4;
124 let len = (b & 0x0F) as usize + 1;
125 i += 1;
126 let value = ext.get(i..i + len)?;
127 if id == ext_id {
128 return Some(value);
129 }
130 i += len;
131 }
132 } else if profile & 0xFFF0 == 0x1000 {
133 let mut i = 0;
135 while i + 1 < ext.len() {
136 let id = ext[i];
137 let len = ext[i + 1] as usize;
138 i += 2;
139 if id == 0 {
140 continue; }
142 let value = ext.get(i..i + len)?;
143 if id == ext_id {
144 return Some(value);
145 }
146 i += len;
147 }
148 }
149 None
150}
151
152pub fn audio_level(buf: &[u8], ext_id: u8) -> Option<(u8, bool)> {
161 let b = *rtp_extension_value(buf, ext_id)?.first()?;
162 Some((b & 0x7F, b & 0x80 != 0))
163}
164
165#[derive(Debug, Clone, Copy, PartialEq, Eq)]
167#[non_exhaustive]
168pub enum DepacketizeError {
169 Truncated,
171 OutOfOrder,
174 Unsupported(u8),
176}
177
178#[derive(Debug, Clone, Copy, Default)]
187pub struct AacDepacketizer {
188 size_length: u8,
190 index_length: u8,
192}
193
194impl AacDepacketizer {
195 pub fn new() -> Self {
198 Self {
199 size_length: 13,
200 index_length: 3,
201 }
202 }
203
204 pub fn with_lengths(size_length: u8, index_length: u8) -> Self {
206 Self {
207 size_length,
208 index_length,
209 }
210 }
211
212 pub fn push(&self, payload: &[u8]) -> Result<Vec<Bytes>, DepacketizeError> {
214 if payload.len() < 2 {
215 return Err(DepacketizeError::Truncated);
216 }
217 if self.size_length == 0 || self.size_length > 16 {
220 return Err(DepacketizeError::Unsupported(self.size_length));
221 }
222 let header_bits = u16::from_be_bytes([payload[0], payload[1]]) as usize;
223 let au_header_bits = self.size_length as usize + self.index_length as usize;
224 if au_header_bits == 0 {
225 return Err(DepacketizeError::Unsupported(0));
226 }
227 let header_bytes = header_bits.div_ceil(8);
228 let au_count = header_bits / au_header_bits;
229 let headers = payload
230 .get(2..2 + header_bytes)
231 .ok_or(DepacketizeError::Truncated)?;
232 let mut data_off = 2 + header_bytes;
233 let mut out = Vec::with_capacity(au_count);
234 for i in 0..au_count {
235 let bit = i * au_header_bits;
238 let byte = bit / 8;
239 let hdr = headers
240 .get(byte..byte + 2)
241 .ok_or(DepacketizeError::Truncated)?;
242 let size = (u16::from_be_bytes([hdr[0], hdr[1]]) >> (16 - self.size_length)) as usize;
243 let end = data_off + size;
244 let au = payload
245 .get(data_off..end)
246 .ok_or(DepacketizeError::Truncated)?;
247 out.push(Bytes::copy_from_slice(au));
248 data_off = end;
249 }
250 Ok(out)
251 }
252}
253
254#[derive(Debug, Clone)]
264pub struct RtpFragment {
265 pub marker: bool,
267 pub payload: Vec<u8>,
269}
270
271#[derive(Debug, Clone)]
278pub struct RtpPacketizer {
279 payload_type: u8,
280 ssrc: u32,
281 sequence: u16,
282 max_payload: usize,
284 codec: NalCodec,
287}
288
289#[derive(Debug, Clone, Copy, PartialEq, Eq)]
293enum NalCodec {
294 H264,
295 H265,
296}
297
298fn for_each_nal_packet(
309 codec: NalCodec,
310 max_payload: usize,
311 access_unit: &[u8],
312 mut emit: impl FnMut(bool, &dyn Fn(&mut Vec<u8>)),
313) {
314 let nals: Vec<&[u8]> = crate::codec::nal::iter_nals(access_unit)
317 .filter(|n| !n.is_empty())
318 .collect();
319 for (i, nal) in nals.iter().enumerate() {
320 let last_nal = i + 1 == nals.len();
321 if nal.len() <= max_payload {
322 emit(last_nal, &|b| b.extend_from_slice(nal));
324 continue;
325 }
326 match codec {
327 NalCodec::H264 => {
328 let fu_indicator = (nal[0] & 0xE0) | 28; let nal_type = nal[0] & 0x1F;
330 let body = &nal[1..];
331 let chunk = max_payload.saturating_sub(2).max(1);
333 let n = body.len().div_ceil(chunk);
334 for (idx, part) in body.chunks(chunk).enumerate() {
335 let (start, end) = (idx == 0, idx + 1 == n);
336 let fu_header =
337 nal_type | if start { 0x80 } else { 0 } | if end { 0x40 } else { 0 };
338 emit(last_nal && end, &|pkt| {
340 pkt.push(fu_indicator);
341 pkt.push(fu_header);
342 pkt.extend_from_slice(part);
343 });
344 }
345 }
346 NalCodec::H265 => {
347 if nal.len() < 2 {
350 emit(last_nal, &|pkt| pkt.extend_from_slice(nal));
351 continue;
352 }
353 let nal_type = (nal[0] >> 1) & 0x3F;
354 let payload_hdr0 = (nal[0] & 0x81) | (49 << 1);
357 let payload_hdr1 = nal[1];
358 let body = &nal[2..];
359 let chunk = max_payload.saturating_sub(3).max(1);
361 let n = body.len().div_ceil(chunk);
362 for (idx, part) in body.chunks(chunk).enumerate() {
363 let (start, end) = (idx == 0, idx + 1 == n);
364 let fu_header =
365 nal_type | if start { 0x80 } else { 0 } | if end { 0x40 } else { 0 };
366 emit(last_nal && end, &|pkt| {
367 pkt.push(payload_hdr0);
368 pkt.push(payload_hdr1);
369 pkt.push(fu_header);
370 pkt.extend_from_slice(part);
371 });
372 }
373 }
374 }
375 }
376}
377
378impl RtpPacketizer {
379 pub fn new(payload_type: u8, ssrc: u32, mtu: usize) -> Self {
383 Self::with_codec(payload_type, ssrc, mtu, NalCodec::H264)
384 }
385
386 pub fn new_h265(payload_type: u8, ssrc: u32, mtu: usize) -> Self {
389 Self::with_codec(payload_type, ssrc, mtu, NalCodec::H265)
390 }
391
392 fn with_codec(payload_type: u8, ssrc: u32, mtu: usize, codec: NalCodec) -> Self {
393 Self {
394 payload_type,
395 ssrc,
396 sequence: 0,
397 max_payload: mtu.saturating_sub(12).max(1),
398 codec,
399 }
400 }
401
402 fn emit(
406 &mut self,
407 recycle: &mut Vec<Vec<u8>>,
408 out: &mut Vec<Vec<u8>>,
409 marker: bool,
410 timestamp: u32,
411 fill: &dyn Fn(&mut Vec<u8>),
412 ) {
413 let mut buf = recycle.pop().unwrap_or_default();
414 buf.clear();
415 write_rtp_header(
416 &mut buf,
417 self.payload_type,
418 marker,
419 self.sequence,
420 timestamp,
421 self.ssrc,
422 );
423 self.sequence = self.sequence.wrapping_add(1);
424 fill(&mut buf);
425 out.push(buf);
426 }
427
428 pub fn packetize(&mut self, access_unit: &[u8], timestamp: u32) -> Vec<Vec<u8>> {
437 let mut out = Vec::new();
438 self.packetize_into(access_unit, timestamp, &mut out);
439 out
440 }
441
442 pub fn packetize_into(&mut self, access_unit: &[u8], timestamp: u32, out: &mut Vec<Vec<u8>>) {
450 let mut recycle = std::mem::take(out);
452 for_each_nal_packet(self.codec, self.max_payload, access_unit, |marker, fill| {
456 self.emit(&mut recycle, out, marker, timestamp, fill)
457 });
458 }
459
460 pub fn fragment_into(&self, access_unit: &[u8], out: &mut Vec<RtpFragment>) {
471 let mut recycle = std::mem::take(out);
472 for_each_nal_packet(self.codec, self.max_payload, access_unit, |marker, fill| {
473 let mut payload = recycle.pop().map(|f| f.payload).unwrap_or_default();
474 payload.clear();
475 fill(&mut payload);
476 out.push(RtpFragment { marker, payload });
477 });
478 }
479
480 pub fn stamp_into(&mut self, frag: &RtpFragment, timestamp: u32, out: &mut Vec<u8>) {
485 out.clear();
486 write_rtp_header(
487 out,
488 self.payload_type,
489 frag.marker,
490 self.sequence,
491 timestamp,
492 self.ssrc,
493 );
494 self.sequence = self.sequence.wrapping_add(1);
495 out.extend_from_slice(&frag.payload);
496 }
497}
498
499#[derive(Debug, Default)]
507pub struct H264Depacketizer {
508 au: Vec<u8>,
510 fua: Vec<u8>,
512 in_fragment: bool,
514 fua_header: u8,
516 current_ts: Option<u32>,
518 last_seq: Option<u16>,
520}
521
522impl H264Depacketizer {
523 pub fn new() -> Self {
525 Self::default()
526 }
527
528 fn append_nal(&mut self, nal: &[u8]) {
530 self.au.extend_from_slice(&ANNEXB_START);
531 self.au.extend_from_slice(nal);
532 }
533
534 fn pending_is_keyframe(&self) -> bool {
536 let mut i = 0;
538 while i + 4 < self.au.len() {
539 if self.au[i..i + 4] == ANNEXB_START {
540 let nal_type = self.au[i + 4] & 0x1F;
541 if nal_type == 5 {
542 return true;
543 }
544 }
545 i += 1;
546 }
547 false
548 }
549
550 fn take_au(&mut self) -> Option<AccessUnit> {
552 if self.au.is_empty() {
553 return None;
554 }
555 let keyframe = self.pending_is_keyframe();
556 let timestamp = self.current_ts.unwrap_or(0);
557 let data = Bytes::from(std::mem::take(&mut self.au));
558 self.current_ts = None;
559 Some(AccessUnit {
560 data,
561 timestamp,
562 keyframe,
563 })
564 }
565
566 pub fn push(
569 &mut self,
570 payload: &[u8],
571 marker: bool,
572 timestamp: u32,
573 sequence: u16,
574 ) -> Result<Option<AccessUnit>, DepacketizeError> {
575 if payload.is_empty() {
576 return Err(DepacketizeError::Truncated);
577 }
578
579 let mut completed = None;
582 if let Some(ts) = self.current_ts {
583 if ts != timestamp && !self.in_fragment {
584 completed = self.take_au();
585 }
586 }
587 self.current_ts = Some(timestamp);
588
589 let nal_type = payload[0] & 0x1F;
590 match nal_type {
591 1..=23 => {
592 self.append_nal(payload);
594 }
595 24 => {
596 let mut i = 1;
598 while i + 2 <= payload.len() {
599 let size = u16::from_be_bytes([payload[i], payload[i + 1]]) as usize;
600 i += 2;
601 if i + size > payload.len() {
602 return Err(DepacketizeError::Truncated);
603 }
604 self.append_nal(&payload[i..i + size]);
605 i += size;
606 }
607 }
608 28 => {
609 if payload.len() < 2 {
611 return Err(DepacketizeError::Truncated);
612 }
613 let fu_header = payload[1];
614 let start = fu_header & 0x80 != 0;
615 let end = fu_header & 0x40 != 0;
616 let frag_type = fu_header & 0x1F;
617
618 if start {
619 self.fua_header = (payload[0] & 0xE0) | frag_type;
622 self.fua.clear();
623 self.fua.push(self.fua_header);
624 self.in_fragment = true;
625 } else if !self.in_fragment {
626 return Err(DepacketizeError::OutOfOrder);
628 } else if self.seq_gap(sequence) {
629 self.in_fragment = false;
630 self.fua.clear();
631 return Err(DepacketizeError::OutOfOrder);
632 }
633 self.fua.extend_from_slice(&payload[2..]);
634
635 if end && self.in_fragment {
636 let nal = std::mem::take(&mut self.fua);
637 self.append_nal(&nal);
638 self.in_fragment = false;
639 }
640 }
641 other => return Err(DepacketizeError::Unsupported(other)),
642 }
643
644 self.last_seq = Some(sequence);
645
646 if completed.is_some() {
647 return Ok(completed);
648 }
649 if marker {
650 return Ok(self.take_au());
651 }
652 Ok(None)
653 }
654
655 fn seq_gap(&self, sequence: u16) -> bool {
657 match self.last_seq {
658 Some(prev) => sequence.wrapping_sub(prev) != 1,
659 None => false,
660 }
661 }
662}
663
664#[derive(Debug, Default)]
675pub struct H265Depacketizer {
676 au: Vec<u8>,
678 fu: Vec<u8>,
680 in_fragment: bool,
682 current_ts: Option<u32>,
684 last_seq: Option<u16>,
686}
687
688impl H265Depacketizer {
689 pub fn new() -> Self {
691 Self::default()
692 }
693
694 fn append_nal(&mut self, nal: &[u8]) {
696 self.au.extend_from_slice(&ANNEXB_START);
697 self.au.extend_from_slice(nal);
698 }
699
700 fn pending_is_keyframe(&self) -> bool {
703 let mut i = 0;
704 while i + 4 < self.au.len() {
705 if self.au[i..i + 4] == ANNEXB_START {
706 let nal_type = (self.au[i + 4] >> 1) & 0x3F;
707 if (16..=23).contains(&nal_type) {
708 return true;
709 }
710 }
711 i += 1;
712 }
713 false
714 }
715
716 fn take_au(&mut self) -> Option<AccessUnit> {
718 if self.au.is_empty() {
719 return None;
720 }
721 let keyframe = self.pending_is_keyframe();
722 let timestamp = self.current_ts.unwrap_or(0);
723 let data = Bytes::from(std::mem::take(&mut self.au));
724 self.current_ts = None;
725 Some(AccessUnit {
726 data,
727 timestamp,
728 keyframe,
729 })
730 }
731
732 fn seq_gap(&self, sequence: u16) -> bool {
734 match self.last_seq {
735 Some(prev) => sequence.wrapping_sub(prev) != 1,
736 None => false,
737 }
738 }
739
740 pub fn push(
743 &mut self,
744 payload: &[u8],
745 marker: bool,
746 timestamp: u32,
747 sequence: u16,
748 ) -> Result<Option<AccessUnit>, DepacketizeError> {
749 if payload.len() < 2 {
751 return Err(DepacketizeError::Truncated);
752 }
753
754 let mut completed = None;
757 if let Some(ts) = self.current_ts {
758 if ts != timestamp && !self.in_fragment {
759 completed = self.take_au();
760 }
761 }
762 self.current_ts = Some(timestamp);
763
764 let nal_type = (payload[0] >> 1) & 0x3F;
765 match nal_type {
766 0..=47 => self.append_nal(payload),
768 48 => {
769 let mut i = 2;
771 while i + 2 <= payload.len() {
772 let size = u16::from_be_bytes([payload[i], payload[i + 1]]) as usize;
773 i += 2;
774 if i + size > payload.len() {
775 return Err(DepacketizeError::Truncated);
776 }
777 self.append_nal(&payload[i..i + size]);
778 i += size;
779 }
780 }
781 49 => {
782 if payload.len() < 3 {
784 return Err(DepacketizeError::Truncated);
785 }
786 let fu_header = payload[2];
787 let start = fu_header & 0x80 != 0;
788 let end = fu_header & 0x40 != 0;
789 let fu_type = fu_header & 0x3F;
790
791 if start {
792 let hdr0 = (payload[0] & 0x81) | (fu_type << 1);
795 let hdr1 = payload[1];
796 self.fu.clear();
797 self.fu.push(hdr0);
798 self.fu.push(hdr1);
799 self.in_fragment = true;
800 } else if !self.in_fragment {
801 return Err(DepacketizeError::OutOfOrder);
802 } else if self.seq_gap(sequence) {
803 self.in_fragment = false;
804 self.fu.clear();
805 return Err(DepacketizeError::OutOfOrder);
806 }
807 self.fu.extend_from_slice(&payload[3..]);
808
809 if end && self.in_fragment {
810 let nal = std::mem::take(&mut self.fu);
811 self.append_nal(&nal);
812 self.in_fragment = false;
813 }
814 }
815 other => return Err(DepacketizeError::Unsupported(other)),
816 }
817
818 self.last_seq = Some(sequence);
819
820 if completed.is_some() {
821 return Ok(completed);
822 }
823 if marker {
824 return Ok(self.take_au());
825 }
826 Ok(None)
827 }
828}
829
830#[derive(Debug, Clone, PartialEq, Eq)]
836pub struct AccessUnit {
837 pub data: Bytes,
839 pub timestamp: u32,
841 pub keyframe: bool,
843}
844
845fn write_rtp_header(out: &mut Vec<u8>, pt: u8, marker: bool, seq: u16, ts: u32, ssrc: u32) {
848 out.push(0x80); out.push(if marker { 0x80 } else { 0 } | (pt & 0x7F));
850 out.extend_from_slice(&seq.to_be_bytes());
851 out.extend_from_slice(&ts.to_be_bytes());
852 out.extend_from_slice(&ssrc.to_be_bytes());
853}
854
855#[derive(Debug, Clone)]
862pub struct OpusPacketizer {
863 payload_type: u8,
864 ssrc: u32,
865 sequence: u16,
866}
867
868impl OpusPacketizer {
869 pub fn new(payload_type: u8, ssrc: u32) -> Self {
871 Self {
872 payload_type,
873 ssrc,
874 sequence: 0,
875 }
876 }
877
878 pub fn packetize_into(&mut self, opus: &[u8], timestamp: u32, out: &mut Vec<Vec<u8>>) {
882 let mut recycle = std::mem::take(out);
883 let mut pkt = recycle.pop().unwrap_or_default();
884 pkt.clear();
885 write_rtp_header(
886 &mut pkt,
887 self.payload_type,
888 false,
889 self.sequence,
890 timestamp,
891 self.ssrc,
892 );
893 self.sequence = self.sequence.wrapping_add(1);
894 pkt.extend_from_slice(opus);
895 out.push(pkt);
896 }
897}
898
899#[derive(Debug, Clone)]
910pub struct Vp9Packetizer {
911 payload_type: u8,
912 ssrc: u32,
913 sequence: u16,
914 max_payload: usize,
915 picture_id: u16,
916}
917
918impl Vp9Packetizer {
919 pub fn new(payload_type: u8, ssrc: u32, mtu: usize) -> Self {
922 Self {
923 payload_type,
924 ssrc,
925 sequence: 0,
926 max_payload: mtu.saturating_sub(12 + 3).max(1),
928 picture_id: 0,
929 }
930 }
931
932 pub fn packetize(&mut self, frame: &[u8], timestamp: u32, keyframe: bool) -> Vec<Vec<u8>> {
935 let mut out = Vec::new();
936 self.packetize_into(frame, timestamp, keyframe, &mut out);
937 out
938 }
939
940 pub fn packetize_into(
943 &mut self,
944 frame: &[u8],
945 timestamp: u32,
946 keyframe: bool,
947 out: &mut Vec<Vec<u8>>,
948 ) {
949 let pid = self.picture_id & 0x7FFF;
950 self.picture_id = self.picture_id.wrapping_add(1);
951
952 let mut recycle = std::mem::take(out);
953 let chunks: Vec<&[u8]> = if frame.is_empty() {
954 vec![&[]]
955 } else {
956 frame.chunks(self.max_payload).collect()
957 };
958 let n = chunks.len();
959 for (i, chunk) in chunks.into_iter().enumerate() {
960 let begin = i == 0;
961 let end = i + 1 == n;
962 let mut pkt = recycle.pop().unwrap_or_default();
963 pkt.clear();
964 write_rtp_header(
965 &mut pkt,
966 self.payload_type,
967 end,
968 self.sequence,
969 timestamp,
970 self.ssrc,
971 );
972 self.sequence = self.sequence.wrapping_add(1);
973
974 let mut desc0 = 0x80; if !keyframe {
977 desc0 |= 0x40; }
979 if begin {
980 desc0 |= 0x08; }
982 if end {
983 desc0 |= 0x04; }
985 pkt.push(desc0);
986 pkt.push(0x80 | (pid >> 8) as u8);
988 pkt.push((pid & 0xFF) as u8);
989 pkt.extend_from_slice(chunk);
990 out.push(pkt);
991 }
992 }
993}
994
995#[derive(Debug, Default)]
998pub struct Vp9Depacketizer {
999 frame: Vec<u8>,
1000 in_frame: bool,
1001 keyframe: bool,
1002 current_ts: Option<u32>,
1003}
1004
1005impl Vp9Depacketizer {
1006 pub fn new() -> Self {
1008 Self::default()
1009 }
1010
1011 pub fn push(
1014 &mut self,
1015 payload: &[u8],
1016 marker: bool,
1017 timestamp: u32,
1018 ) -> Result<Option<AccessUnit>, DepacketizeError> {
1019 if payload.is_empty() {
1020 return Err(DepacketizeError::Truncated);
1021 }
1022 let desc0 = payload[0];
1023 let has_pid = desc0 & 0x80 != 0;
1024 let has_layer = desc0 & 0x20 != 0;
1025 let flexible = desc0 & 0x10 != 0;
1026 let begin = desc0 & 0x08 != 0;
1027 let end = desc0 & 0x04 != 0;
1028 let predicted = desc0 & 0x40 != 0;
1029
1030 let mut off = 1;
1032 if has_pid {
1033 let m = payload.get(off).ok_or(DepacketizeError::Truncated)? & 0x80 != 0;
1035 off += if m { 2 } else { 1 };
1036 }
1037 if has_layer {
1038 off += 1; if !flexible {
1040 off += 1; }
1042 }
1043 if off > payload.len() {
1044 return Err(DepacketizeError::Truncated);
1045 }
1046
1047 if begin {
1048 self.frame.clear();
1049 self.in_frame = true;
1050 self.keyframe = !predicted;
1051 self.current_ts = Some(timestamp);
1052 } else if !self.in_frame {
1053 return Err(DepacketizeError::OutOfOrder);
1054 }
1055 self.frame.extend_from_slice(&payload[off..]);
1056
1057 if end && marker && self.in_frame {
1058 self.in_frame = false;
1059 return Ok(Some(AccessUnit {
1060 data: Bytes::from(std::mem::take(&mut self.frame)),
1061 timestamp: self.current_ts.unwrap_or(timestamp),
1062 keyframe: self.keyframe,
1063 }));
1064 }
1065 Ok(None)
1066 }
1067}
1068
1069#[cfg(feature = "codec-av1")]
1073fn leb128_encode(mut v: u64, out: &mut Vec<u8>) {
1074 loop {
1075 let mut byte = (v & 0x7F) as u8;
1076 v >>= 7;
1077 if v != 0 {
1078 byte |= 0x80;
1079 }
1080 out.push(byte);
1081 if v == 0 {
1082 break;
1083 }
1084 }
1085}
1086
1087#[cfg(feature = "codec-av1")]
1088const AV1_OBU_SEQUENCE_HEADER: u8 = 1;
1089#[cfg(feature = "codec-av1")]
1090const AV1_OBU_TEMPORAL_DELIMITER: u8 = 2;
1091
1092#[cfg(feature = "codec-av1")]
1102#[derive(Debug, Clone)]
1103pub struct Av1Packetizer {
1104 payload_type: u8,
1105 ssrc: u32,
1106 sequence: u16,
1107 max_payload: usize,
1108}
1109
1110#[cfg(feature = "codec-av1")]
1111impl Av1Packetizer {
1112 pub fn new(payload_type: u8, ssrc: u32, mtu: usize) -> Self {
1115 Self {
1116 payload_type,
1117 ssrc,
1118 sequence: 0,
1119 max_payload: mtu.saturating_sub(12 + 1).max(1),
1120 }
1121 }
1122
1123 pub fn packetize(&mut self, temporal_unit: &[u8], timestamp: u32) -> Vec<Vec<u8>> {
1125 let mut out = Vec::new();
1126 self.packetize_into(temporal_unit, timestamp, &mut out);
1127 out
1128 }
1129
1130 pub fn packetize_into(&mut self, temporal_unit: &[u8], timestamp: u32, out: &mut Vec<Vec<u8>>) {
1133 let mut stream = Vec::with_capacity(temporal_unit.len());
1136 let mut new_cvs = false;
1137 for obu in crate::codec::obu::iter_obus(temporal_unit) {
1138 if obu.obu_type == AV1_OBU_TEMPORAL_DELIMITER {
1139 continue;
1140 }
1141 if obu.obu_type == AV1_OBU_SEQUENCE_HEADER {
1142 new_cvs = true;
1143 }
1144 let header_len = 1 + obu.has_extension as usize;
1145 let mut element = Vec::with_capacity(header_len + obu.payload.len());
1146 element.push(obu.raw[0] & !0x02); if obu.has_extension {
1148 element.push(obu.raw[1]);
1149 }
1150 element.extend_from_slice(obu.payload);
1151 leb128_encode(element.len() as u64, &mut stream);
1152 stream.extend_from_slice(&element);
1153 }
1154
1155 let mut recycle = std::mem::take(out);
1156 let chunks: Vec<&[u8]> = if stream.is_empty() {
1157 vec![&[]]
1158 } else {
1159 stream.chunks(self.max_payload).collect()
1160 };
1161 let n = chunks.len();
1162 for (i, chunk) in chunks.into_iter().enumerate() {
1163 let last = i + 1 == n;
1164 let mut pkt = recycle.pop().unwrap_or_default();
1165 pkt.clear();
1166 write_rtp_header(
1167 &mut pkt,
1168 self.payload_type,
1169 last,
1170 self.sequence,
1171 timestamp,
1172 self.ssrc,
1173 );
1174 self.sequence = self.sequence.wrapping_add(1);
1175
1176 let mut agg = 0u8;
1179 if i > 0 {
1180 agg |= 0x80; }
1182 if !last {
1183 agg |= 0x40; }
1185 if i == 0 && new_cvs {
1186 agg |= 0x08; }
1188 pkt.push(agg);
1189 pkt.extend_from_slice(chunk);
1190 out.push(pkt);
1191 }
1192 }
1193}
1194
1195#[cfg(feature = "codec-av1")]
1200#[derive(Debug, Default)]
1201pub struct Av1Depacketizer {
1202 stream: Vec<u8>,
1203 new_cvs: bool,
1204 current_ts: Option<u32>,
1205}
1206
1207#[cfg(feature = "codec-av1")]
1208impl Av1Depacketizer {
1209 pub fn new() -> Self {
1211 Self::default()
1212 }
1213
1214 pub fn push(
1217 &mut self,
1218 payload: &[u8],
1219 marker: bool,
1220 timestamp: u32,
1221 ) -> Result<Option<AccessUnit>, DepacketizeError> {
1222 if payload.is_empty() {
1223 return Err(DepacketizeError::Truncated);
1224 }
1225 let agg = payload[0];
1226 if agg & 0x08 != 0 {
1227 self.new_cvs = true; }
1229 if self.current_ts.is_none() {
1230 self.current_ts = Some(timestamp);
1231 }
1232 self.stream.extend_from_slice(&payload[1..]);
1233
1234 if !marker {
1235 return Ok(None);
1236 }
1237
1238 let stream = std::mem::take(&mut self.stream);
1240 let mut tu = Vec::with_capacity(stream.len() + 8);
1241 let mut pos = 0;
1242 while pos < stream.len() {
1243 let len = leb128_decode(&stream, &mut pos).ok_or(DepacketizeError::Truncated)?;
1244 let end = pos.checked_add(len).ok_or(DepacketizeError::Truncated)?;
1245 let element = stream.get(pos..end).ok_or(DepacketizeError::Truncated)?;
1246 pos = end;
1247 let hdr0 = *element.first().ok_or(DepacketizeError::Truncated)?;
1249 let has_ext = (hdr0 >> 2) & 1 == 1;
1250 let header_len = 1 + has_ext as usize;
1251 let obu_payload = element
1252 .get(header_len..)
1253 .ok_or(DepacketizeError::Truncated)?;
1254 tu.push(hdr0 | 0x02);
1255 if has_ext {
1256 tu.push(element[1]);
1257 }
1258 leb128_encode(obu_payload.len() as u64, &mut tu);
1259 tu.extend_from_slice(obu_payload);
1260 }
1261
1262 let keyframe = std::mem::take(&mut self.new_cvs);
1263 let ts = self.current_ts.take().unwrap_or(timestamp);
1264 Ok(Some(AccessUnit {
1265 data: Bytes::from(tu),
1266 timestamp: ts,
1267 keyframe,
1268 }))
1269 }
1270}
1271
1272#[cfg(feature = "codec-av1")]
1275fn leb128_decode(data: &[u8], pos: &mut usize) -> Option<usize> {
1276 let mut value: u64 = 0;
1277 for i in 0..8 {
1278 let byte = *data.get(*pos)?;
1279 *pos += 1;
1280 value |= ((byte & 0x7F) as u64) << (i * 7);
1281 if byte & 0x80 == 0 {
1282 return usize::try_from(value).ok();
1283 }
1284 }
1285 None
1286}
1287
1288#[cfg(test)]
1289mod tests {
1290 use super::*;
1291
1292 fn rtp(seq: u16, ts: u32, marker: bool, payload: &[u8]) -> Vec<u8> {
1294 let mut p = vec![0x80, if marker { 0x80 | 96 } else { 96 }];
1295 p.extend_from_slice(&seq.to_be_bytes());
1296 p.extend_from_slice(&ts.to_be_bytes());
1297 p.extend_from_slice(&[0, 0, 0, 1]); p.extend_from_slice(payload);
1299 p
1300 }
1301
1302 #[test]
1303 fn parses_fixed_header_and_payload_offset() {
1304 let pkt = rtp(7, 9000, true, &[0x65, 0xAA]);
1305 let h = RtpHeader::parse(&pkt).unwrap();
1306 assert_eq!(h.sequence, 7);
1307 assert_eq!(h.timestamp, 9000);
1308 assert!(h.marker);
1309 assert_eq!(h.payload_type, 96);
1310 assert_eq!(h.payload_offset, 12);
1311 assert_eq!(&pkt[h.payload_offset..], &[0x65, 0xAA]);
1312 }
1313
1314 #[test]
1315 fn rejects_wrong_version_and_short_buffers() {
1316 assert!(RtpHeader::parse(&[0x00; 12]).is_none()); assert!(RtpHeader::parse(&[0x80; 4]).is_none()); }
1319
1320 fn rtp_with_ext(ext_id: u8, value: &[u8], payload: &[u8]) -> Vec<u8> {
1323 let mut p = vec![0x90, 96]; p.extend_from_slice(&1u16.to_be_bytes()); p.extend_from_slice(&0u32.to_be_bytes()); p.extend_from_slice(&7u32.to_be_bytes()); p.extend_from_slice(&0xBEDEu16.to_be_bytes()); let mut ext = vec![(ext_id << 4) | (value.len() as u8 - 1)];
1330 ext.extend_from_slice(value);
1331 while ext.len() % 4 != 0 {
1332 ext.push(0);
1333 }
1334 p.extend_from_slice(&((ext.len() / 4) as u16).to_be_bytes());
1335 p.extend_from_slice(&ext);
1336 p.extend_from_slice(payload);
1337 p
1338 }
1339
1340 #[test]
1341 fn extracts_rid_header_extension() {
1342 let pkt = rtp_with_ext(4, b"hi", &[0xAA, 0xBB]);
1343 assert_eq!(rtp_extension_value(&pkt, 4), Some(&b"hi"[..]));
1344 assert_eq!(rtp_extension_value(&pkt, 5), None, "unknown id");
1345 let h = RtpHeader::parse(&pkt).unwrap();
1347 assert_eq!(&pkt[h.payload_offset..], &[0xAA, 0xBB]);
1348 }
1349
1350 #[test]
1351 fn extension_value_none_without_extension_flag() {
1352 let pkt = rtp(1, 0, false, &[1, 2, 3]);
1353 assert_eq!(rtp_extension_value(&pkt, 4), None);
1354 }
1355
1356 #[test]
1357 fn decodes_rfc6464_audio_level() {
1358 let pkt = rtp_with_ext(3, &[0x80 | 20], &[0xAA]);
1360 assert_eq!(audio_level(&pkt, 3), Some((20, true)));
1361 let pkt = rtp_with_ext(3, &[90], &[0xAA]);
1363 assert_eq!(audio_level(&pkt, 3), Some((90, false)));
1364 assert_eq!(audio_level(&pkt, 7), None);
1366 }
1367
1368 #[test]
1369 fn honors_csrc_count_in_payload_offset() {
1370 let mut pkt = rtp(1, 0, false, &[0x41]);
1371 pkt[0] = 0x82; let mut with_csrc = pkt[..12].to_vec();
1373 with_csrc.extend_from_slice(&[0xDE, 0xAD, 0xBE, 0xEF, 0, 0, 0, 0]); with_csrc.push(0x41);
1375 let h = RtpHeader::parse(&with_csrc).unwrap();
1376 assert_eq!(h.payload_offset, 20);
1377 }
1378
1379 #[test]
1380 fn aac_hbr_splits_two_access_units() {
1381 let mut p = Vec::new();
1384 p.extend_from_slice(&32u16.to_be_bytes()); p.extend_from_slice(&((3u16) << 3).to_be_bytes()); p.extend_from_slice(&((2u16) << 3).to_be_bytes()); p.extend_from_slice(&[0xA1, 0xA2, 0xA3]); p.extend_from_slice(&[0xB1, 0xB2]); let aus = AacDepacketizer::new().push(&p).unwrap();
1390 assert_eq!(aus.len(), 2);
1391 assert_eq!(&aus[0][..], &[0xA1, 0xA2, 0xA3]);
1392 assert_eq!(&aus[1][..], &[0xB1, 0xB2]);
1393 }
1394
1395 #[test]
1396 fn aac_hbr_single_au() {
1397 let mut p = Vec::new();
1398 p.extend_from_slice(&16u16.to_be_bytes()); p.extend_from_slice(&((4u16) << 3).to_be_bytes()); p.extend_from_slice(&[1, 2, 3, 4]);
1401 let aus = AacDepacketizer::new().push(&p).unwrap();
1402 assert_eq!(aus.len(), 1);
1403 assert_eq!(&aus[0][..], &[1, 2, 3, 4]);
1404 }
1405
1406 #[test]
1407 fn aac_truncated_payload_errors() {
1408 assert_eq!(
1409 AacDepacketizer::new().push(&[0x00]),
1410 Err(DepacketizeError::Truncated)
1411 );
1412 let mut p = 16u16.to_be_bytes().to_vec();
1414 p.extend_from_slice(&((8u16) << 3).to_be_bytes());
1415 p.extend_from_slice(&[1, 2]);
1416 assert_eq!(
1417 AacDepacketizer::new().push(&p),
1418 Err(DepacketizeError::Truncated)
1419 );
1420 }
1421
1422 #[test]
1423 fn single_nal_packet_emits_annexb_on_marker() {
1424 let mut d = H264Depacketizer::new();
1425 let out = d.push(&[0x41, 0x9A, 0xBC], true, 3000, 1).unwrap().unwrap();
1427 assert_eq!(&out.data[..], &[0, 0, 0, 1, 0x41, 0x9A, 0xBC]);
1428 assert!(!out.keyframe);
1429 assert_eq!(out.timestamp, 3000);
1430 }
1431
1432 #[test]
1433 fn idr_single_nal_is_flagged_keyframe() {
1434 let mut d = H264Depacketizer::new();
1435 let out = d.push(&[0x65, 0x01], true, 0, 1).unwrap().unwrap();
1436 assert!(out.keyframe);
1437 }
1438
1439 #[test]
1440 fn packetizer_single_nal_round_trips_through_depacketizer() {
1441 let au = [0, 0, 0, 1, 0x67, 0x42, 0x00, 0, 0, 0, 1, 0x65, 0x88, 0x99];
1443 let mut pkt = RtpPacketizer::new(96, 0xABCD, 1200);
1444 let packets = pkt.packetize(&au, 3000);
1445 assert_eq!(packets.len(), 2, "one packet per NAL");
1446
1447 let mut depack = H264Depacketizer::new();
1448 let mut out = None;
1449 for p in &packets {
1450 let h = RtpHeader::parse(p).unwrap();
1451 if let Some(au) = depack
1452 .push(&p[h.payload_offset..], h.marker, h.timestamp, h.sequence)
1453 .unwrap()
1454 {
1455 out = Some(au);
1456 }
1457 }
1458 let out = out.expect("AU completed on the marker packet");
1459 assert_eq!(&out.data[..], &au);
1460 assert!(out.keyframe);
1461 assert_eq!(out.timestamp, 3000);
1462 }
1463
1464 #[test]
1465 fn packetize_into_recycles_buffers_without_changing_output() {
1466 let au1 = [0, 0, 0, 1, 0x67, 0x42, 0x00, 0, 0, 0, 1, 0x65, 0x88, 0x99];
1470 let au2 = [0, 0, 0, 1, 0x65, 0x11, 0x22, 0x33];
1471
1472 let mut a = RtpPacketizer::new(96, 0xABCD, 1200);
1473 let mut b = RtpPacketizer::new(96, 0xABCD, 1200);
1474 let mut reused: Vec<Vec<u8>> = Vec::new();
1475
1476 for au in [&au1[..], &au2[..], &au1[..]] {
1477 let expected = a.packetize(au, 3000);
1478 b.packetize_into(au, 3000, &mut reused);
1480 assert_eq!(
1481 reused, expected,
1482 "recycled output matches allocating output"
1483 );
1484 }
1485 }
1486
1487 #[test]
1488 fn shared_fragment_then_stamp_equals_packetize() {
1489 let small = [0, 0, 0, 1, 0x67, 0x42, 0x00, 0, 0, 0, 1, 0x65, 0x88, 0x99];
1494 let mut big = vec![0, 0, 0, 1, 0x65];
1495 big.extend((0..600u16).map(|i| i as u8));
1496
1497 for (mk, mtu, au) in [
1498 (
1499 RtpPacketizer::new as fn(u8, u32, usize) -> RtpPacketizer,
1500 1200,
1501 &small[..],
1502 ),
1503 (RtpPacketizer::new, 100, &big[..]), (RtpPacketizer::new_h265, 100, &big[..]),
1505 ] {
1506 let mut direct = mk(96, 0xABCD, mtu);
1508 let expected = direct.packetize(au, 3000);
1509
1510 let template = mk(96, 0xABCD, mtu);
1513 let mut frags = Vec::new();
1514 template.fragment_into(au, &mut frags);
1515 let mut viewer = mk(96, 0xABCD, mtu);
1516 let mut pkt = Vec::new();
1517 let stamped: Vec<Vec<u8>> = frags
1518 .iter()
1519 .map(|f| {
1520 viewer.stamp_into(f, 3000, &mut pkt);
1521 pkt.clone()
1522 })
1523 .collect();
1524
1525 assert_eq!(stamped, expected, "fragment+stamp == packetize (mtu {mtu})");
1526 assert_eq!(template.sequence, 0, "fragment_into is sequence-stateless");
1528 }
1529 }
1530
1531 #[test]
1532 fn packetizer_fragments_oversized_nal_and_round_trips() {
1533 let mut nal = vec![0, 0, 0, 1, 0x65]; nal.extend((0..600u16).map(|i| i as u8)); let mut pkt = RtpPacketizer::new(96, 1, 100); let packets = pkt.packetize(&nal, 90);
1538 assert!(packets.len() > 1, "oversized NAL is fragmented");
1539 let markers: Vec<bool> = packets
1541 .iter()
1542 .map(|p| RtpHeader::parse(p).unwrap().marker)
1543 .collect();
1544 assert_eq!(markers.iter().filter(|m| **m).count(), 1);
1545 assert!(markers.last().unwrap());
1546
1547 let mut depack = H264Depacketizer::new();
1548 let mut out = None;
1549 for p in &packets {
1550 let h = RtpHeader::parse(p).unwrap();
1551 if let Some(au) = depack
1552 .push(&p[h.payload_offset..], h.marker, h.timestamp, h.sequence)
1553 .unwrap()
1554 {
1555 out = Some(au);
1556 }
1557 }
1558 assert_eq!(&out.unwrap().data[..], &nal[..]);
1559 }
1560
1561 #[test]
1562 fn stap_a_splits_aggregated_nals() {
1563 let payload = [24, 0, 2, 0xAA, 0xBB, 0, 3, 0xCC, 0xDD, 0xEE];
1565 let mut d = H264Depacketizer::new();
1566 let out = d.push(&payload, true, 0, 1).unwrap().unwrap();
1567 assert_eq!(
1568 &out.data[..],
1569 &[0, 0, 0, 1, 0xAA, 0xBB, 0, 0, 0, 1, 0xCC, 0xDD, 0xEE]
1570 );
1571 }
1572
1573 #[test]
1574 fn fu_a_reassembles_fragmented_nal() {
1575 let mut d = H264Depacketizer::new();
1576 assert!(d
1578 .push(&[0x7C, 0x85, 0x11, 0x22], false, 0, 1)
1579 .unwrap()
1580 .is_none());
1581 assert!(d.push(&[0x7C, 0x05, 0x33], false, 0, 2).unwrap().is_none());
1583 let out = d.push(&[0x7C, 0x45, 0x44], true, 0, 3).unwrap().unwrap();
1585 assert_eq!(&out.data[..], &[0, 0, 0, 1, 0x65, 0x11, 0x22, 0x33, 0x44]);
1587 assert!(out.keyframe);
1588 }
1589
1590 #[test]
1591 fn fu_a_sequence_gap_reports_out_of_order() {
1592 let mut d = H264Depacketizer::new();
1593 d.push(&[0x7C, 0x85, 0x11], false, 0, 1).unwrap();
1594 assert_eq!(
1596 d.push(&[0x7C, 0x05, 0x22], false, 0, 5),
1597 Err(DepacketizeError::OutOfOrder)
1598 );
1599 }
1600
1601 #[test]
1602 fn timestamp_change_flushes_previous_au_without_marker() {
1603 let mut d = H264Depacketizer::new();
1604 assert!(d.push(&[0x41, 0x01], false, 1000, 1).unwrap().is_none());
1606 let out = d.push(&[0x41, 0x02], false, 2000, 2).unwrap().unwrap();
1608 assert_eq!(out.timestamp, 1000);
1609 assert_eq!(&out.data[..], &[0, 0, 0, 1, 0x41, 0x01]);
1610 }
1611
1612 #[test]
1617 fn h265_single_nal_round_trips_through_depacketizer() {
1618 let au = [
1620 0, 0, 0, 1, 0x40, 0x01, 0xAA, 0, 0, 0, 1, 0x26, 0x01, 0x88, 0x99, ];
1623 let mut pkt = RtpPacketizer::new_h265(96, 0xABCD, 1200);
1624 let packets = pkt.packetize(&au, 3000);
1625 assert_eq!(packets.len(), 2, "one packet per NAL");
1626
1627 let mut depack = H265Depacketizer::new();
1628 let mut out = None;
1629 for p in &packets {
1630 let h = RtpHeader::parse(p).unwrap();
1631 if let Some(au) = depack
1632 .push(&p[h.payload_offset..], h.marker, h.timestamp, h.sequence)
1633 .unwrap()
1634 {
1635 out = Some(au);
1636 }
1637 }
1638 let out = out.expect("AU completed on the marker packet");
1639 assert_eq!(&out.data[..], &au);
1640 assert!(out.keyframe, "IRAP type 19 is a keyframe");
1641 assert_eq!(out.timestamp, 3000);
1642 }
1643
1644 #[test]
1645 fn h265_fragments_oversized_nal_and_round_trips() {
1646 let mut nal = vec![0, 0, 0, 1, 0x26, 0x01]; nal.extend((0..600u16).map(|i| i as u8));
1649 let mut pkt = RtpPacketizer::new_h265(96, 1, 100); let packets = pkt.packetize(&nal, 90);
1651 assert!(packets.len() > 1, "oversized NAL is fragmented");
1652 let markers: Vec<bool> = packets
1654 .iter()
1655 .map(|p| RtpHeader::parse(p).unwrap().marker)
1656 .collect();
1657 assert_eq!(markers.iter().filter(|m| **m).count(), 1);
1658 assert!(markers.last().unwrap());
1659 for p in &packets {
1661 let h = RtpHeader::parse(p).unwrap();
1662 let pt = (p[h.payload_offset] >> 1) & 0x3F;
1663 assert_eq!(pt, 49, "FU payload type");
1664 }
1665
1666 let mut depack = H265Depacketizer::new();
1667 let mut out = None;
1668 for p in &packets {
1669 let h = RtpHeader::parse(p).unwrap();
1670 if let Some(au) = depack
1671 .push(&p[h.payload_offset..], h.marker, h.timestamp, h.sequence)
1672 .unwrap()
1673 {
1674 out = Some(au);
1675 }
1676 }
1677 assert_eq!(&out.unwrap().data[..], &nal[..]);
1678 }
1679
1680 #[test]
1681 fn h265_ap_splits_aggregated_nals() {
1682 let payload = [0x60, 0x01, 0, 2, 0xAA, 0xBB, 0, 3, 0xCC, 0xDD, 0xEE];
1684 let mut d = H265Depacketizer::new();
1685 let out = d.push(&payload, true, 0, 1).unwrap().unwrap();
1686 assert_eq!(
1687 &out.data[..],
1688 &[0, 0, 0, 1, 0xAA, 0xBB, 0, 0, 0, 1, 0xCC, 0xDD, 0xEE]
1689 );
1690 }
1691
1692 #[test]
1693 fn h265_rejects_truncated_and_unsupported() {
1694 let mut d = H265Depacketizer::new();
1695 assert_eq!(
1697 d.push(&[0x26], true, 0, 1),
1698 Err(DepacketizeError::Truncated)
1699 );
1700 assert_eq!(
1702 d.push(&[50 << 1, 0x01, 0x00], true, 0, 2),
1703 Err(DepacketizeError::Unsupported(50))
1704 );
1705 }
1706
1707 fn vp9_depacketize(packets: &[Vec<u8>]) -> Option<AccessUnit> {
1710 let mut d = Vp9Depacketizer::new();
1711 let mut out = None;
1712 for p in packets {
1713 let h = RtpHeader::parse(p).unwrap();
1714 if let Some(f) = d
1715 .push(&p[h.payload_offset..], h.marker, h.timestamp)
1716 .unwrap()
1717 {
1718 out = Some(f);
1719 }
1720 }
1721 out
1722 }
1723
1724 #[test]
1725 fn vp9_fragmented_frame_round_trips() {
1726 let frame: Vec<u8> = (0..500u16).map(|i| i as u8).collect();
1727 let mut pkt = Vp9Packetizer::new(98, 0x1234, 100); let packets = pkt.packetize(&frame, 9000, true);
1729 assert!(packets.len() > 1, "frame fragmented");
1730
1731 let markers: Vec<bool> = packets
1733 .iter()
1734 .map(|p| RtpHeader::parse(p).unwrap().marker)
1735 .collect();
1736 assert_eq!(markers.iter().filter(|m| **m).count(), 1);
1737 assert!(markers.last().unwrap());
1738
1739 let out = vp9_depacketize(&packets).expect("frame completed");
1740 assert_eq!(&out.data[..], &frame[..]);
1741 assert!(out.keyframe, "keyframe → P bit clear");
1742 assert_eq!(out.timestamp, 9000);
1743 }
1744
1745 #[test]
1746 fn vp9_inter_frame_is_not_a_keyframe() {
1747 let mut pkt = Vp9Packetizer::new(98, 1, 1200);
1748 let packets = pkt.packetize(&[1, 2, 3], 0, false);
1749 assert_eq!(packets.len(), 1);
1750 let out = vp9_depacketize(&packets).expect("frame");
1751 assert_eq!(&out.data[..], &[1, 2, 3]);
1752 assert!(!out.keyframe, "P bit set → inter frame");
1753 }
1754
1755 #[cfg(feature = "codec-av1")]
1758 fn av1_depacketize(packets: &[Vec<u8>]) -> Option<AccessUnit> {
1759 let mut d = Av1Depacketizer::new();
1760 let mut out = None;
1761 for p in packets {
1762 let h = RtpHeader::parse(p).unwrap();
1763 if let Some(f) = d
1764 .push(&p[h.payload_offset..], h.marker, h.timestamp)
1765 .unwrap()
1766 {
1767 out = Some(f);
1768 }
1769 }
1770 out
1771 }
1772
1773 #[cfg(feature = "codec-av1")]
1774 #[test]
1775 fn av1_temporal_unit_round_trips_without_delimiter() {
1776 let td = [0x12u8, 0x00];
1778 let seq = [0x0Au8, 0x02, 0xAA, 0xBB];
1779 let frame = [0x32u8, 0x03, 0x11, 0x22, 0x33];
1780 let mut tu = Vec::new();
1781 tu.extend_from_slice(&td);
1782 tu.extend_from_slice(&seq);
1783 tu.extend_from_slice(&frame);
1784
1785 let mut pkt = Av1Packetizer::new(99, 7, 1200);
1786 let packets = pkt.packetize(&tu, 1000);
1787 let out = av1_depacketize(&packets).expect("TU completed");
1788
1789 let mut expected = Vec::new();
1791 expected.extend_from_slice(&seq);
1792 expected.extend_from_slice(&frame);
1793 assert_eq!(&out.data[..], &expected[..]);
1794 assert!(out.keyframe, "sequence header → new coded video sequence");
1795 assert_eq!(out.timestamp, 1000);
1796 }
1797
1798 #[cfg(feature = "codec-av1")]
1799 #[test]
1800 fn av1_large_temporal_unit_fragments_and_round_trips() {
1801 let mut frame = vec![0x32u8, 0xAC, 0x02];
1803 frame.extend((0..300u16).map(|i| i as u8));
1804 let mut tu = vec![0x12u8, 0x00]; tu.extend_from_slice(&frame);
1806
1807 let mut pkt = Av1Packetizer::new(99, 1, 64); let packets = pkt.packetize(&tu, 0);
1809 assert!(packets.len() > 1, "large TU fragmented");
1810 for (i, p) in packets.iter().enumerate() {
1812 let agg = p[RtpHeader::parse(p).unwrap().payload_offset];
1813 assert_eq!((agg & 0x80 != 0), i > 0, "Z continuation bit");
1814 assert_eq!(
1815 (agg & 0x40 != 0),
1816 i + 1 < packets.len(),
1817 "Y continuation bit"
1818 );
1819 }
1820
1821 let out = av1_depacketize(&packets).expect("TU completed");
1822 assert_eq!(&out.data[..], &frame[..], "frame OBU reconstructed");
1823 }
1824}