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
152#[derive(Debug, Clone, Copy, PartialEq, Eq)]
154#[non_exhaustive]
155pub enum DepacketizeError {
156 Truncated,
158 OutOfOrder,
161 Unsupported(u8),
163}
164
165#[derive(Debug, Clone, Copy, Default)]
174pub struct AacDepacketizer {
175 size_length: u8,
177 index_length: u8,
179}
180
181impl AacDepacketizer {
182 pub fn new() -> Self {
185 Self {
186 size_length: 13,
187 index_length: 3,
188 }
189 }
190
191 pub fn with_lengths(size_length: u8, index_length: u8) -> Self {
193 Self {
194 size_length,
195 index_length,
196 }
197 }
198
199 pub fn push(&self, payload: &[u8]) -> Result<Vec<Bytes>, DepacketizeError> {
201 if payload.len() < 2 {
202 return Err(DepacketizeError::Truncated);
203 }
204 if self.size_length == 0 || self.size_length > 16 {
207 return Err(DepacketizeError::Unsupported(self.size_length));
208 }
209 let header_bits = u16::from_be_bytes([payload[0], payload[1]]) as usize;
210 let au_header_bits = self.size_length as usize + self.index_length as usize;
211 if au_header_bits == 0 {
212 return Err(DepacketizeError::Unsupported(0));
213 }
214 let header_bytes = header_bits.div_ceil(8);
215 let au_count = header_bits / au_header_bits;
216 let headers = payload
217 .get(2..2 + header_bytes)
218 .ok_or(DepacketizeError::Truncated)?;
219 let mut data_off = 2 + header_bytes;
220 let mut out = Vec::with_capacity(au_count);
221 for i in 0..au_count {
222 let bit = i * au_header_bits;
225 let byte = bit / 8;
226 let hdr = headers
227 .get(byte..byte + 2)
228 .ok_or(DepacketizeError::Truncated)?;
229 let size = (u16::from_be_bytes([hdr[0], hdr[1]]) >> (16 - self.size_length)) as usize;
230 let end = data_off + size;
231 let au = payload
232 .get(data_off..end)
233 .ok_or(DepacketizeError::Truncated)?;
234 out.push(Bytes::copy_from_slice(au));
235 data_off = end;
236 }
237 Ok(out)
238 }
239}
240
241#[derive(Debug, Clone)]
248pub struct RtpPacketizer {
249 payload_type: u8,
250 ssrc: u32,
251 sequence: u16,
252 max_payload: usize,
254 codec: NalCodec,
257}
258
259#[derive(Debug, Clone, Copy, PartialEq, Eq)]
263enum NalCodec {
264 H264,
265 H265,
266}
267
268impl RtpPacketizer {
269 pub fn new(payload_type: u8, ssrc: u32, mtu: usize) -> Self {
273 Self::with_codec(payload_type, ssrc, mtu, NalCodec::H264)
274 }
275
276 pub fn new_h265(payload_type: u8, ssrc: u32, mtu: usize) -> Self {
279 Self::with_codec(payload_type, ssrc, mtu, NalCodec::H265)
280 }
281
282 fn with_codec(payload_type: u8, ssrc: u32, mtu: usize, codec: NalCodec) -> Self {
283 Self {
284 payload_type,
285 ssrc,
286 sequence: 0,
287 max_payload: mtu.saturating_sub(12).max(1),
288 codec,
289 }
290 }
291
292 fn emit(
296 &mut self,
297 recycle: &mut Vec<Vec<u8>>,
298 out: &mut Vec<Vec<u8>>,
299 marker: bool,
300 timestamp: u32,
301 fill: impl FnOnce(&mut Vec<u8>),
302 ) {
303 let mut buf = recycle.pop().unwrap_or_default();
304 buf.clear();
305 write_rtp_header(
306 &mut buf,
307 self.payload_type,
308 marker,
309 self.sequence,
310 timestamp,
311 self.ssrc,
312 );
313 self.sequence = self.sequence.wrapping_add(1);
314 fill(&mut buf);
315 out.push(buf);
316 }
317
318 pub fn packetize(&mut self, access_unit: &[u8], timestamp: u32) -> Vec<Vec<u8>> {
327 let mut out = Vec::new();
328 self.packetize_into(access_unit, timestamp, &mut out);
329 out
330 }
331
332 pub fn packetize_into(&mut self, access_unit: &[u8], timestamp: u32, out: &mut Vec<Vec<u8>>) {
340 let mut recycle = std::mem::take(out);
342 let nals: Vec<&[u8]> = crate::codec::nal::iter_nals(access_unit)
345 .filter(|n| !n.is_empty())
346 .collect();
347 for (i, nal) in nals.iter().enumerate() {
348 let last_nal = i + 1 == nals.len();
349 if nal.len() <= self.max_payload {
350 self.emit(&mut recycle, out, last_nal, timestamp, |b| {
352 b.extend_from_slice(nal)
353 });
354 } else {
355 match self.codec {
356 NalCodec::H264 => {
357 self.fragment_fua(nal, timestamp, last_nal, &mut recycle, out)
358 }
359 NalCodec::H265 => {
360 self.fragment_fu_h265(nal, timestamp, last_nal, &mut recycle, out)
361 }
362 }
363 }
364 }
365 }
366
367 fn fragment_fua(
369 &mut self,
370 nal: &[u8],
371 timestamp: u32,
372 last_nal: bool,
373 recycle: &mut Vec<Vec<u8>>,
374 out: &mut Vec<Vec<u8>>,
375 ) {
376 let nal_header = nal[0];
377 let fu_indicator = (nal_header & 0xE0) | 28; let nal_type = nal_header & 0x1F;
379 let body = &nal[1..];
380 let chunk = self.max_payload.saturating_sub(2).max(1);
382 let n_chunks = body.len().div_ceil(chunk);
383 for (idx, part) in body.chunks(chunk).enumerate() {
384 let start = idx == 0;
385 let end = idx + 1 == n_chunks;
386 let mut fu_header = nal_type;
387 if start {
388 fu_header |= 0x80;
389 }
390 if end {
391 fu_header |= 0x40;
392 }
393 self.emit(recycle, out, last_nal && end, timestamp, |pkt| {
395 pkt.push(fu_indicator);
396 pkt.push(fu_header);
397 pkt.extend_from_slice(part);
398 });
399 }
400 }
401
402 fn fragment_fu_h265(
408 &mut self,
409 nal: &[u8],
410 timestamp: u32,
411 last_nal: bool,
412 recycle: &mut Vec<Vec<u8>>,
413 out: &mut Vec<Vec<u8>>,
414 ) {
415 if nal.len() < 2 {
418 self.emit(recycle, out, last_nal, timestamp, |pkt| {
419 pkt.extend_from_slice(nal)
420 });
421 return;
422 }
423 let nal_type = (nal[0] >> 1) & 0x3F;
424 let payload_hdr0 = (nal[0] & 0x81) | (49 << 1);
427 let payload_hdr1 = nal[1];
428 let body = &nal[2..];
429 let chunk = self.max_payload.saturating_sub(3).max(1);
431 let n_chunks = body.len().div_ceil(chunk);
432 for (idx, part) in body.chunks(chunk).enumerate() {
433 let start = idx == 0;
434 let end = idx + 1 == n_chunks;
435 let mut fu_header = nal_type;
436 if start {
437 fu_header |= 0x80;
438 }
439 if end {
440 fu_header |= 0x40;
441 }
442 self.emit(recycle, out, last_nal && end, timestamp, |pkt| {
443 pkt.push(payload_hdr0);
444 pkt.push(payload_hdr1);
445 pkt.push(fu_header);
446 pkt.extend_from_slice(part);
447 });
448 }
449 }
450}
451
452#[derive(Debug, Default)]
460pub struct H264Depacketizer {
461 au: Vec<u8>,
463 fua: Vec<u8>,
465 in_fragment: bool,
467 fua_header: u8,
469 current_ts: Option<u32>,
471 last_seq: Option<u16>,
473}
474
475impl H264Depacketizer {
476 pub fn new() -> Self {
478 Self::default()
479 }
480
481 fn append_nal(&mut self, nal: &[u8]) {
483 self.au.extend_from_slice(&ANNEXB_START);
484 self.au.extend_from_slice(nal);
485 }
486
487 fn pending_is_keyframe(&self) -> bool {
489 let mut i = 0;
491 while i + 4 < self.au.len() {
492 if self.au[i..i + 4] == ANNEXB_START {
493 let nal_type = self.au[i + 4] & 0x1F;
494 if nal_type == 5 {
495 return true;
496 }
497 }
498 i += 1;
499 }
500 false
501 }
502
503 fn take_au(&mut self) -> Option<AccessUnit> {
505 if self.au.is_empty() {
506 return None;
507 }
508 let keyframe = self.pending_is_keyframe();
509 let timestamp = self.current_ts.unwrap_or(0);
510 let data = Bytes::from(std::mem::take(&mut self.au));
511 self.current_ts = None;
512 Some(AccessUnit {
513 data,
514 timestamp,
515 keyframe,
516 })
517 }
518
519 pub fn push(
522 &mut self,
523 payload: &[u8],
524 marker: bool,
525 timestamp: u32,
526 sequence: u16,
527 ) -> Result<Option<AccessUnit>, DepacketizeError> {
528 if payload.is_empty() {
529 return Err(DepacketizeError::Truncated);
530 }
531
532 let mut completed = None;
535 if let Some(ts) = self.current_ts {
536 if ts != timestamp && !self.in_fragment {
537 completed = self.take_au();
538 }
539 }
540 self.current_ts = Some(timestamp);
541
542 let nal_type = payload[0] & 0x1F;
543 match nal_type {
544 1..=23 => {
545 self.append_nal(payload);
547 }
548 24 => {
549 let mut i = 1;
551 while i + 2 <= payload.len() {
552 let size = u16::from_be_bytes([payload[i], payload[i + 1]]) as usize;
553 i += 2;
554 if i + size > payload.len() {
555 return Err(DepacketizeError::Truncated);
556 }
557 self.append_nal(&payload[i..i + size]);
558 i += size;
559 }
560 }
561 28 => {
562 if payload.len() < 2 {
564 return Err(DepacketizeError::Truncated);
565 }
566 let fu_header = payload[1];
567 let start = fu_header & 0x80 != 0;
568 let end = fu_header & 0x40 != 0;
569 let frag_type = fu_header & 0x1F;
570
571 if start {
572 self.fua_header = (payload[0] & 0xE0) | frag_type;
575 self.fua.clear();
576 self.fua.push(self.fua_header);
577 self.in_fragment = true;
578 } else if !self.in_fragment {
579 return Err(DepacketizeError::OutOfOrder);
581 } else if self.seq_gap(sequence) {
582 self.in_fragment = false;
583 self.fua.clear();
584 return Err(DepacketizeError::OutOfOrder);
585 }
586 self.fua.extend_from_slice(&payload[2..]);
587
588 if end && self.in_fragment {
589 let nal = std::mem::take(&mut self.fua);
590 self.append_nal(&nal);
591 self.in_fragment = false;
592 }
593 }
594 other => return Err(DepacketizeError::Unsupported(other)),
595 }
596
597 self.last_seq = Some(sequence);
598
599 if completed.is_some() {
600 return Ok(completed);
601 }
602 if marker {
603 return Ok(self.take_au());
604 }
605 Ok(None)
606 }
607
608 fn seq_gap(&self, sequence: u16) -> bool {
610 match self.last_seq {
611 Some(prev) => sequence.wrapping_sub(prev) != 1,
612 None => false,
613 }
614 }
615}
616
617#[derive(Debug, Default)]
628pub struct H265Depacketizer {
629 au: Vec<u8>,
631 fu: Vec<u8>,
633 in_fragment: bool,
635 current_ts: Option<u32>,
637 last_seq: Option<u16>,
639}
640
641impl H265Depacketizer {
642 pub fn new() -> Self {
644 Self::default()
645 }
646
647 fn append_nal(&mut self, nal: &[u8]) {
649 self.au.extend_from_slice(&ANNEXB_START);
650 self.au.extend_from_slice(nal);
651 }
652
653 fn pending_is_keyframe(&self) -> bool {
656 let mut i = 0;
657 while i + 4 < self.au.len() {
658 if self.au[i..i + 4] == ANNEXB_START {
659 let nal_type = (self.au[i + 4] >> 1) & 0x3F;
660 if (16..=23).contains(&nal_type) {
661 return true;
662 }
663 }
664 i += 1;
665 }
666 false
667 }
668
669 fn take_au(&mut self) -> Option<AccessUnit> {
671 if self.au.is_empty() {
672 return None;
673 }
674 let keyframe = self.pending_is_keyframe();
675 let timestamp = self.current_ts.unwrap_or(0);
676 let data = Bytes::from(std::mem::take(&mut self.au));
677 self.current_ts = None;
678 Some(AccessUnit {
679 data,
680 timestamp,
681 keyframe,
682 })
683 }
684
685 fn seq_gap(&self, sequence: u16) -> bool {
687 match self.last_seq {
688 Some(prev) => sequence.wrapping_sub(prev) != 1,
689 None => false,
690 }
691 }
692
693 pub fn push(
696 &mut self,
697 payload: &[u8],
698 marker: bool,
699 timestamp: u32,
700 sequence: u16,
701 ) -> Result<Option<AccessUnit>, DepacketizeError> {
702 if payload.len() < 2 {
704 return Err(DepacketizeError::Truncated);
705 }
706
707 let mut completed = None;
710 if let Some(ts) = self.current_ts {
711 if ts != timestamp && !self.in_fragment {
712 completed = self.take_au();
713 }
714 }
715 self.current_ts = Some(timestamp);
716
717 let nal_type = (payload[0] >> 1) & 0x3F;
718 match nal_type {
719 0..=47 => self.append_nal(payload),
721 48 => {
722 let mut i = 2;
724 while i + 2 <= payload.len() {
725 let size = u16::from_be_bytes([payload[i], payload[i + 1]]) as usize;
726 i += 2;
727 if i + size > payload.len() {
728 return Err(DepacketizeError::Truncated);
729 }
730 self.append_nal(&payload[i..i + size]);
731 i += size;
732 }
733 }
734 49 => {
735 if payload.len() < 3 {
737 return Err(DepacketizeError::Truncated);
738 }
739 let fu_header = payload[2];
740 let start = fu_header & 0x80 != 0;
741 let end = fu_header & 0x40 != 0;
742 let fu_type = fu_header & 0x3F;
743
744 if start {
745 let hdr0 = (payload[0] & 0x81) | (fu_type << 1);
748 let hdr1 = payload[1];
749 self.fu.clear();
750 self.fu.push(hdr0);
751 self.fu.push(hdr1);
752 self.in_fragment = true;
753 } else if !self.in_fragment {
754 return Err(DepacketizeError::OutOfOrder);
755 } else if self.seq_gap(sequence) {
756 self.in_fragment = false;
757 self.fu.clear();
758 return Err(DepacketizeError::OutOfOrder);
759 }
760 self.fu.extend_from_slice(&payload[3..]);
761
762 if end && self.in_fragment {
763 let nal = std::mem::take(&mut self.fu);
764 self.append_nal(&nal);
765 self.in_fragment = false;
766 }
767 }
768 other => return Err(DepacketizeError::Unsupported(other)),
769 }
770
771 self.last_seq = Some(sequence);
772
773 if completed.is_some() {
774 return Ok(completed);
775 }
776 if marker {
777 return Ok(self.take_au());
778 }
779 Ok(None)
780 }
781}
782
783#[derive(Debug, Clone, PartialEq, Eq)]
789pub struct AccessUnit {
790 pub data: Bytes,
792 pub timestamp: u32,
794 pub keyframe: bool,
796}
797
798fn write_rtp_header(out: &mut Vec<u8>, pt: u8, marker: bool, seq: u16, ts: u32, ssrc: u32) {
801 out.push(0x80); out.push(if marker { 0x80 } else { 0 } | (pt & 0x7F));
803 out.extend_from_slice(&seq.to_be_bytes());
804 out.extend_from_slice(&ts.to_be_bytes());
805 out.extend_from_slice(&ssrc.to_be_bytes());
806}
807
808#[derive(Debug, Clone)]
815pub struct OpusPacketizer {
816 payload_type: u8,
817 ssrc: u32,
818 sequence: u16,
819}
820
821impl OpusPacketizer {
822 pub fn new(payload_type: u8, ssrc: u32) -> Self {
824 Self {
825 payload_type,
826 ssrc,
827 sequence: 0,
828 }
829 }
830
831 pub fn packetize_into(&mut self, opus: &[u8], timestamp: u32, out: &mut Vec<Vec<u8>>) {
835 let mut recycle = std::mem::take(out);
836 let mut pkt = recycle.pop().unwrap_or_default();
837 pkt.clear();
838 write_rtp_header(
839 &mut pkt,
840 self.payload_type,
841 false,
842 self.sequence,
843 timestamp,
844 self.ssrc,
845 );
846 self.sequence = self.sequence.wrapping_add(1);
847 pkt.extend_from_slice(opus);
848 out.push(pkt);
849 }
850}
851
852#[derive(Debug, Clone)]
863pub struct Vp9Packetizer {
864 payload_type: u8,
865 ssrc: u32,
866 sequence: u16,
867 max_payload: usize,
868 picture_id: u16,
869}
870
871impl Vp9Packetizer {
872 pub fn new(payload_type: u8, ssrc: u32, mtu: usize) -> Self {
875 Self {
876 payload_type,
877 ssrc,
878 sequence: 0,
879 max_payload: mtu.saturating_sub(12 + 3).max(1),
881 picture_id: 0,
882 }
883 }
884
885 pub fn packetize(&mut self, frame: &[u8], timestamp: u32, keyframe: bool) -> Vec<Vec<u8>> {
888 let mut out = Vec::new();
889 self.packetize_into(frame, timestamp, keyframe, &mut out);
890 out
891 }
892
893 pub fn packetize_into(
896 &mut self,
897 frame: &[u8],
898 timestamp: u32,
899 keyframe: bool,
900 out: &mut Vec<Vec<u8>>,
901 ) {
902 let pid = self.picture_id & 0x7FFF;
903 self.picture_id = self.picture_id.wrapping_add(1);
904
905 let mut recycle = std::mem::take(out);
906 let chunks: Vec<&[u8]> = if frame.is_empty() {
907 vec![&[]]
908 } else {
909 frame.chunks(self.max_payload).collect()
910 };
911 let n = chunks.len();
912 for (i, chunk) in chunks.into_iter().enumerate() {
913 let begin = i == 0;
914 let end = i + 1 == n;
915 let mut pkt = recycle.pop().unwrap_or_default();
916 pkt.clear();
917 write_rtp_header(
918 &mut pkt,
919 self.payload_type,
920 end,
921 self.sequence,
922 timestamp,
923 self.ssrc,
924 );
925 self.sequence = self.sequence.wrapping_add(1);
926
927 let mut desc0 = 0x80; if !keyframe {
930 desc0 |= 0x40; }
932 if begin {
933 desc0 |= 0x08; }
935 if end {
936 desc0 |= 0x04; }
938 pkt.push(desc0);
939 pkt.push(0x80 | (pid >> 8) as u8);
941 pkt.push((pid & 0xFF) as u8);
942 pkt.extend_from_slice(chunk);
943 out.push(pkt);
944 }
945 }
946}
947
948#[derive(Debug, Default)]
951pub struct Vp9Depacketizer {
952 frame: Vec<u8>,
953 in_frame: bool,
954 keyframe: bool,
955 current_ts: Option<u32>,
956}
957
958impl Vp9Depacketizer {
959 pub fn new() -> Self {
961 Self::default()
962 }
963
964 pub fn push(
967 &mut self,
968 payload: &[u8],
969 marker: bool,
970 timestamp: u32,
971 ) -> Result<Option<AccessUnit>, DepacketizeError> {
972 if payload.is_empty() {
973 return Err(DepacketizeError::Truncated);
974 }
975 let desc0 = payload[0];
976 let has_pid = desc0 & 0x80 != 0;
977 let has_layer = desc0 & 0x20 != 0;
978 let flexible = desc0 & 0x10 != 0;
979 let begin = desc0 & 0x08 != 0;
980 let end = desc0 & 0x04 != 0;
981 let predicted = desc0 & 0x40 != 0;
982
983 let mut off = 1;
985 if has_pid {
986 let m = payload.get(off).ok_or(DepacketizeError::Truncated)? & 0x80 != 0;
988 off += if m { 2 } else { 1 };
989 }
990 if has_layer {
991 off += 1; if !flexible {
993 off += 1; }
995 }
996 if off > payload.len() {
997 return Err(DepacketizeError::Truncated);
998 }
999
1000 if begin {
1001 self.frame.clear();
1002 self.in_frame = true;
1003 self.keyframe = !predicted;
1004 self.current_ts = Some(timestamp);
1005 } else if !self.in_frame {
1006 return Err(DepacketizeError::OutOfOrder);
1007 }
1008 self.frame.extend_from_slice(&payload[off..]);
1009
1010 if end && marker && self.in_frame {
1011 self.in_frame = false;
1012 return Ok(Some(AccessUnit {
1013 data: Bytes::from(std::mem::take(&mut self.frame)),
1014 timestamp: self.current_ts.unwrap_or(timestamp),
1015 keyframe: self.keyframe,
1016 }));
1017 }
1018 Ok(None)
1019 }
1020}
1021
1022#[cfg(feature = "codec-av1")]
1026fn leb128_encode(mut v: u64, out: &mut Vec<u8>) {
1027 loop {
1028 let mut byte = (v & 0x7F) as u8;
1029 v >>= 7;
1030 if v != 0 {
1031 byte |= 0x80;
1032 }
1033 out.push(byte);
1034 if v == 0 {
1035 break;
1036 }
1037 }
1038}
1039
1040#[cfg(feature = "codec-av1")]
1041const AV1_OBU_SEQUENCE_HEADER: u8 = 1;
1042#[cfg(feature = "codec-av1")]
1043const AV1_OBU_TEMPORAL_DELIMITER: u8 = 2;
1044
1045#[cfg(feature = "codec-av1")]
1055#[derive(Debug, Clone)]
1056pub struct Av1Packetizer {
1057 payload_type: u8,
1058 ssrc: u32,
1059 sequence: u16,
1060 max_payload: usize,
1061}
1062
1063#[cfg(feature = "codec-av1")]
1064impl Av1Packetizer {
1065 pub fn new(payload_type: u8, ssrc: u32, mtu: usize) -> Self {
1068 Self {
1069 payload_type,
1070 ssrc,
1071 sequence: 0,
1072 max_payload: mtu.saturating_sub(12 + 1).max(1),
1073 }
1074 }
1075
1076 pub fn packetize(&mut self, temporal_unit: &[u8], timestamp: u32) -> Vec<Vec<u8>> {
1078 let mut out = Vec::new();
1079 self.packetize_into(temporal_unit, timestamp, &mut out);
1080 out
1081 }
1082
1083 pub fn packetize_into(&mut self, temporal_unit: &[u8], timestamp: u32, out: &mut Vec<Vec<u8>>) {
1086 let mut stream = Vec::with_capacity(temporal_unit.len());
1089 let mut new_cvs = false;
1090 for obu in crate::codec::obu::iter_obus(temporal_unit) {
1091 if obu.obu_type == AV1_OBU_TEMPORAL_DELIMITER {
1092 continue;
1093 }
1094 if obu.obu_type == AV1_OBU_SEQUENCE_HEADER {
1095 new_cvs = true;
1096 }
1097 let header_len = 1 + obu.has_extension as usize;
1098 let mut element = Vec::with_capacity(header_len + obu.payload.len());
1099 element.push(obu.raw[0] & !0x02); if obu.has_extension {
1101 element.push(obu.raw[1]);
1102 }
1103 element.extend_from_slice(obu.payload);
1104 leb128_encode(element.len() as u64, &mut stream);
1105 stream.extend_from_slice(&element);
1106 }
1107
1108 let mut recycle = std::mem::take(out);
1109 let chunks: Vec<&[u8]> = if stream.is_empty() {
1110 vec![&[]]
1111 } else {
1112 stream.chunks(self.max_payload).collect()
1113 };
1114 let n = chunks.len();
1115 for (i, chunk) in chunks.into_iter().enumerate() {
1116 let last = i + 1 == n;
1117 let mut pkt = recycle.pop().unwrap_or_default();
1118 pkt.clear();
1119 write_rtp_header(
1120 &mut pkt,
1121 self.payload_type,
1122 last,
1123 self.sequence,
1124 timestamp,
1125 self.ssrc,
1126 );
1127 self.sequence = self.sequence.wrapping_add(1);
1128
1129 let mut agg = 0u8;
1132 if i > 0 {
1133 agg |= 0x80; }
1135 if !last {
1136 agg |= 0x40; }
1138 if i == 0 && new_cvs {
1139 agg |= 0x08; }
1141 pkt.push(agg);
1142 pkt.extend_from_slice(chunk);
1143 out.push(pkt);
1144 }
1145 }
1146}
1147
1148#[cfg(feature = "codec-av1")]
1153#[derive(Debug, Default)]
1154pub struct Av1Depacketizer {
1155 stream: Vec<u8>,
1156 new_cvs: bool,
1157 current_ts: Option<u32>,
1158}
1159
1160#[cfg(feature = "codec-av1")]
1161impl Av1Depacketizer {
1162 pub fn new() -> Self {
1164 Self::default()
1165 }
1166
1167 pub fn push(
1170 &mut self,
1171 payload: &[u8],
1172 marker: bool,
1173 timestamp: u32,
1174 ) -> Result<Option<AccessUnit>, DepacketizeError> {
1175 if payload.is_empty() {
1176 return Err(DepacketizeError::Truncated);
1177 }
1178 let agg = payload[0];
1179 if agg & 0x08 != 0 {
1180 self.new_cvs = true; }
1182 if self.current_ts.is_none() {
1183 self.current_ts = Some(timestamp);
1184 }
1185 self.stream.extend_from_slice(&payload[1..]);
1186
1187 if !marker {
1188 return Ok(None);
1189 }
1190
1191 let stream = std::mem::take(&mut self.stream);
1193 let mut tu = Vec::with_capacity(stream.len() + 8);
1194 let mut pos = 0;
1195 while pos < stream.len() {
1196 let len = leb128_decode(&stream, &mut pos).ok_or(DepacketizeError::Truncated)?;
1197 let end = pos.checked_add(len).ok_or(DepacketizeError::Truncated)?;
1198 let element = stream.get(pos..end).ok_or(DepacketizeError::Truncated)?;
1199 pos = end;
1200 let hdr0 = *element.first().ok_or(DepacketizeError::Truncated)?;
1202 let has_ext = (hdr0 >> 2) & 1 == 1;
1203 let header_len = 1 + has_ext as usize;
1204 let obu_payload = element
1205 .get(header_len..)
1206 .ok_or(DepacketizeError::Truncated)?;
1207 tu.push(hdr0 | 0x02);
1208 if has_ext {
1209 tu.push(element[1]);
1210 }
1211 leb128_encode(obu_payload.len() as u64, &mut tu);
1212 tu.extend_from_slice(obu_payload);
1213 }
1214
1215 let keyframe = std::mem::take(&mut self.new_cvs);
1216 let ts = self.current_ts.take().unwrap_or(timestamp);
1217 Ok(Some(AccessUnit {
1218 data: Bytes::from(tu),
1219 timestamp: ts,
1220 keyframe,
1221 }))
1222 }
1223}
1224
1225#[cfg(feature = "codec-av1")]
1228fn leb128_decode(data: &[u8], pos: &mut usize) -> Option<usize> {
1229 let mut value: u64 = 0;
1230 for i in 0..8 {
1231 let byte = *data.get(*pos)?;
1232 *pos += 1;
1233 value |= ((byte & 0x7F) as u64) << (i * 7);
1234 if byte & 0x80 == 0 {
1235 return usize::try_from(value).ok();
1236 }
1237 }
1238 None
1239}
1240
1241#[cfg(test)]
1242mod tests {
1243 use super::*;
1244
1245 fn rtp(seq: u16, ts: u32, marker: bool, payload: &[u8]) -> Vec<u8> {
1247 let mut p = vec![0x80, if marker { 0x80 | 96 } else { 96 }];
1248 p.extend_from_slice(&seq.to_be_bytes());
1249 p.extend_from_slice(&ts.to_be_bytes());
1250 p.extend_from_slice(&[0, 0, 0, 1]); p.extend_from_slice(payload);
1252 p
1253 }
1254
1255 #[test]
1256 fn parses_fixed_header_and_payload_offset() {
1257 let pkt = rtp(7, 9000, true, &[0x65, 0xAA]);
1258 let h = RtpHeader::parse(&pkt).unwrap();
1259 assert_eq!(h.sequence, 7);
1260 assert_eq!(h.timestamp, 9000);
1261 assert!(h.marker);
1262 assert_eq!(h.payload_type, 96);
1263 assert_eq!(h.payload_offset, 12);
1264 assert_eq!(&pkt[h.payload_offset..], &[0x65, 0xAA]);
1265 }
1266
1267 #[test]
1268 fn rejects_wrong_version_and_short_buffers() {
1269 assert!(RtpHeader::parse(&[0x00; 12]).is_none()); assert!(RtpHeader::parse(&[0x80; 4]).is_none()); }
1272
1273 fn rtp_with_ext(ext_id: u8, value: &[u8], payload: &[u8]) -> Vec<u8> {
1276 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)];
1283 ext.extend_from_slice(value);
1284 while ext.len() % 4 != 0 {
1285 ext.push(0);
1286 }
1287 p.extend_from_slice(&((ext.len() / 4) as u16).to_be_bytes());
1288 p.extend_from_slice(&ext);
1289 p.extend_from_slice(payload);
1290 p
1291 }
1292
1293 #[test]
1294 fn extracts_rid_header_extension() {
1295 let pkt = rtp_with_ext(4, b"hi", &[0xAA, 0xBB]);
1296 assert_eq!(rtp_extension_value(&pkt, 4), Some(&b"hi"[..]));
1297 assert_eq!(rtp_extension_value(&pkt, 5), None, "unknown id");
1298 let h = RtpHeader::parse(&pkt).unwrap();
1300 assert_eq!(&pkt[h.payload_offset..], &[0xAA, 0xBB]);
1301 }
1302
1303 #[test]
1304 fn extension_value_none_without_extension_flag() {
1305 let pkt = rtp(1, 0, false, &[1, 2, 3]);
1306 assert_eq!(rtp_extension_value(&pkt, 4), None);
1307 }
1308
1309 #[test]
1310 fn honors_csrc_count_in_payload_offset() {
1311 let mut pkt = rtp(1, 0, false, &[0x41]);
1312 pkt[0] = 0x82; let mut with_csrc = pkt[..12].to_vec();
1314 with_csrc.extend_from_slice(&[0xDE, 0xAD, 0xBE, 0xEF, 0, 0, 0, 0]); with_csrc.push(0x41);
1316 let h = RtpHeader::parse(&with_csrc).unwrap();
1317 assert_eq!(h.payload_offset, 20);
1318 }
1319
1320 #[test]
1321 fn aac_hbr_splits_two_access_units() {
1322 let mut p = Vec::new();
1325 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();
1331 assert_eq!(aus.len(), 2);
1332 assert_eq!(&aus[0][..], &[0xA1, 0xA2, 0xA3]);
1333 assert_eq!(&aus[1][..], &[0xB1, 0xB2]);
1334 }
1335
1336 #[test]
1337 fn aac_hbr_single_au() {
1338 let mut p = Vec::new();
1339 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]);
1342 let aus = AacDepacketizer::new().push(&p).unwrap();
1343 assert_eq!(aus.len(), 1);
1344 assert_eq!(&aus[0][..], &[1, 2, 3, 4]);
1345 }
1346
1347 #[test]
1348 fn aac_truncated_payload_errors() {
1349 assert_eq!(
1350 AacDepacketizer::new().push(&[0x00]),
1351 Err(DepacketizeError::Truncated)
1352 );
1353 let mut p = 16u16.to_be_bytes().to_vec();
1355 p.extend_from_slice(&((8u16) << 3).to_be_bytes());
1356 p.extend_from_slice(&[1, 2]);
1357 assert_eq!(
1358 AacDepacketizer::new().push(&p),
1359 Err(DepacketizeError::Truncated)
1360 );
1361 }
1362
1363 #[test]
1364 fn single_nal_packet_emits_annexb_on_marker() {
1365 let mut d = H264Depacketizer::new();
1366 let out = d.push(&[0x41, 0x9A, 0xBC], true, 3000, 1).unwrap().unwrap();
1368 assert_eq!(&out.data[..], &[0, 0, 0, 1, 0x41, 0x9A, 0xBC]);
1369 assert!(!out.keyframe);
1370 assert_eq!(out.timestamp, 3000);
1371 }
1372
1373 #[test]
1374 fn idr_single_nal_is_flagged_keyframe() {
1375 let mut d = H264Depacketizer::new();
1376 let out = d.push(&[0x65, 0x01], true, 0, 1).unwrap().unwrap();
1377 assert!(out.keyframe);
1378 }
1379
1380 #[test]
1381 fn packetizer_single_nal_round_trips_through_depacketizer() {
1382 let au = [0, 0, 0, 1, 0x67, 0x42, 0x00, 0, 0, 0, 1, 0x65, 0x88, 0x99];
1384 let mut pkt = RtpPacketizer::new(96, 0xABCD, 1200);
1385 let packets = pkt.packetize(&au, 3000);
1386 assert_eq!(packets.len(), 2, "one packet per NAL");
1387
1388 let mut depack = H264Depacketizer::new();
1389 let mut out = None;
1390 for p in &packets {
1391 let h = RtpHeader::parse(p).unwrap();
1392 if let Some(au) = depack
1393 .push(&p[h.payload_offset..], h.marker, h.timestamp, h.sequence)
1394 .unwrap()
1395 {
1396 out = Some(au);
1397 }
1398 }
1399 let out = out.expect("AU completed on the marker packet");
1400 assert_eq!(&out.data[..], &au);
1401 assert!(out.keyframe);
1402 assert_eq!(out.timestamp, 3000);
1403 }
1404
1405 #[test]
1406 fn packetize_into_recycles_buffers_without_changing_output() {
1407 let au1 = [0, 0, 0, 1, 0x67, 0x42, 0x00, 0, 0, 0, 1, 0x65, 0x88, 0x99];
1411 let au2 = [0, 0, 0, 1, 0x65, 0x11, 0x22, 0x33];
1412
1413 let mut a = RtpPacketizer::new(96, 0xABCD, 1200);
1414 let mut b = RtpPacketizer::new(96, 0xABCD, 1200);
1415 let mut reused: Vec<Vec<u8>> = Vec::new();
1416
1417 for au in [&au1[..], &au2[..], &au1[..]] {
1418 let expected = a.packetize(au, 3000);
1419 b.packetize_into(au, 3000, &mut reused);
1421 assert_eq!(
1422 reused, expected,
1423 "recycled output matches allocating output"
1424 );
1425 }
1426 }
1427
1428 #[test]
1429 fn packetizer_fragments_oversized_nal_and_round_trips() {
1430 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);
1435 assert!(packets.len() > 1, "oversized NAL is fragmented");
1436 let markers: Vec<bool> = packets
1438 .iter()
1439 .map(|p| RtpHeader::parse(p).unwrap().marker)
1440 .collect();
1441 assert_eq!(markers.iter().filter(|m| **m).count(), 1);
1442 assert!(markers.last().unwrap());
1443
1444 let mut depack = H264Depacketizer::new();
1445 let mut out = None;
1446 for p in &packets {
1447 let h = RtpHeader::parse(p).unwrap();
1448 if let Some(au) = depack
1449 .push(&p[h.payload_offset..], h.marker, h.timestamp, h.sequence)
1450 .unwrap()
1451 {
1452 out = Some(au);
1453 }
1454 }
1455 assert_eq!(&out.unwrap().data[..], &nal[..]);
1456 }
1457
1458 #[test]
1459 fn stap_a_splits_aggregated_nals() {
1460 let payload = [24, 0, 2, 0xAA, 0xBB, 0, 3, 0xCC, 0xDD, 0xEE];
1462 let mut d = H264Depacketizer::new();
1463 let out = d.push(&payload, true, 0, 1).unwrap().unwrap();
1464 assert_eq!(
1465 &out.data[..],
1466 &[0, 0, 0, 1, 0xAA, 0xBB, 0, 0, 0, 1, 0xCC, 0xDD, 0xEE]
1467 );
1468 }
1469
1470 #[test]
1471 fn fu_a_reassembles_fragmented_nal() {
1472 let mut d = H264Depacketizer::new();
1473 assert!(d
1475 .push(&[0x7C, 0x85, 0x11, 0x22], false, 0, 1)
1476 .unwrap()
1477 .is_none());
1478 assert!(d.push(&[0x7C, 0x05, 0x33], false, 0, 2).unwrap().is_none());
1480 let out = d.push(&[0x7C, 0x45, 0x44], true, 0, 3).unwrap().unwrap();
1482 assert_eq!(&out.data[..], &[0, 0, 0, 1, 0x65, 0x11, 0x22, 0x33, 0x44]);
1484 assert!(out.keyframe);
1485 }
1486
1487 #[test]
1488 fn fu_a_sequence_gap_reports_out_of_order() {
1489 let mut d = H264Depacketizer::new();
1490 d.push(&[0x7C, 0x85, 0x11], false, 0, 1).unwrap();
1491 assert_eq!(
1493 d.push(&[0x7C, 0x05, 0x22], false, 0, 5),
1494 Err(DepacketizeError::OutOfOrder)
1495 );
1496 }
1497
1498 #[test]
1499 fn timestamp_change_flushes_previous_au_without_marker() {
1500 let mut d = H264Depacketizer::new();
1501 assert!(d.push(&[0x41, 0x01], false, 1000, 1).unwrap().is_none());
1503 let out = d.push(&[0x41, 0x02], false, 2000, 2).unwrap().unwrap();
1505 assert_eq!(out.timestamp, 1000);
1506 assert_eq!(&out.data[..], &[0, 0, 0, 1, 0x41, 0x01]);
1507 }
1508
1509 #[test]
1514 fn h265_single_nal_round_trips_through_depacketizer() {
1515 let au = [
1517 0, 0, 0, 1, 0x40, 0x01, 0xAA, 0, 0, 0, 1, 0x26, 0x01, 0x88, 0x99, ];
1520 let mut pkt = RtpPacketizer::new_h265(96, 0xABCD, 1200);
1521 let packets = pkt.packetize(&au, 3000);
1522 assert_eq!(packets.len(), 2, "one packet per NAL");
1523
1524 let mut depack = H265Depacketizer::new();
1525 let mut out = None;
1526 for p in &packets {
1527 let h = RtpHeader::parse(p).unwrap();
1528 if let Some(au) = depack
1529 .push(&p[h.payload_offset..], h.marker, h.timestamp, h.sequence)
1530 .unwrap()
1531 {
1532 out = Some(au);
1533 }
1534 }
1535 let out = out.expect("AU completed on the marker packet");
1536 assert_eq!(&out.data[..], &au);
1537 assert!(out.keyframe, "IRAP type 19 is a keyframe");
1538 assert_eq!(out.timestamp, 3000);
1539 }
1540
1541 #[test]
1542 fn h265_fragments_oversized_nal_and_round_trips() {
1543 let mut nal = vec![0, 0, 0, 1, 0x26, 0x01]; nal.extend((0..600u16).map(|i| i as u8));
1546 let mut pkt = RtpPacketizer::new_h265(96, 1, 100); let packets = pkt.packetize(&nal, 90);
1548 assert!(packets.len() > 1, "oversized NAL is fragmented");
1549 let markers: Vec<bool> = packets
1551 .iter()
1552 .map(|p| RtpHeader::parse(p).unwrap().marker)
1553 .collect();
1554 assert_eq!(markers.iter().filter(|m| **m).count(), 1);
1555 assert!(markers.last().unwrap());
1556 for p in &packets {
1558 let h = RtpHeader::parse(p).unwrap();
1559 let pt = (p[h.payload_offset] >> 1) & 0x3F;
1560 assert_eq!(pt, 49, "FU payload type");
1561 }
1562
1563 let mut depack = H265Depacketizer::new();
1564 let mut out = None;
1565 for p in &packets {
1566 let h = RtpHeader::parse(p).unwrap();
1567 if let Some(au) = depack
1568 .push(&p[h.payload_offset..], h.marker, h.timestamp, h.sequence)
1569 .unwrap()
1570 {
1571 out = Some(au);
1572 }
1573 }
1574 assert_eq!(&out.unwrap().data[..], &nal[..]);
1575 }
1576
1577 #[test]
1578 fn h265_ap_splits_aggregated_nals() {
1579 let payload = [0x60, 0x01, 0, 2, 0xAA, 0xBB, 0, 3, 0xCC, 0xDD, 0xEE];
1581 let mut d = H265Depacketizer::new();
1582 let out = d.push(&payload, true, 0, 1).unwrap().unwrap();
1583 assert_eq!(
1584 &out.data[..],
1585 &[0, 0, 0, 1, 0xAA, 0xBB, 0, 0, 0, 1, 0xCC, 0xDD, 0xEE]
1586 );
1587 }
1588
1589 #[test]
1590 fn h265_rejects_truncated_and_unsupported() {
1591 let mut d = H265Depacketizer::new();
1592 assert_eq!(
1594 d.push(&[0x26], true, 0, 1),
1595 Err(DepacketizeError::Truncated)
1596 );
1597 assert_eq!(
1599 d.push(&[50 << 1, 0x01, 0x00], true, 0, 2),
1600 Err(DepacketizeError::Unsupported(50))
1601 );
1602 }
1603
1604 fn vp9_depacketize(packets: &[Vec<u8>]) -> Option<AccessUnit> {
1607 let mut d = Vp9Depacketizer::new();
1608 let mut out = None;
1609 for p in packets {
1610 let h = RtpHeader::parse(p).unwrap();
1611 if let Some(f) = d
1612 .push(&p[h.payload_offset..], h.marker, h.timestamp)
1613 .unwrap()
1614 {
1615 out = Some(f);
1616 }
1617 }
1618 out
1619 }
1620
1621 #[test]
1622 fn vp9_fragmented_frame_round_trips() {
1623 let frame: Vec<u8> = (0..500u16).map(|i| i as u8).collect();
1624 let mut pkt = Vp9Packetizer::new(98, 0x1234, 100); let packets = pkt.packetize(&frame, 9000, true);
1626 assert!(packets.len() > 1, "frame fragmented");
1627
1628 let markers: Vec<bool> = packets
1630 .iter()
1631 .map(|p| RtpHeader::parse(p).unwrap().marker)
1632 .collect();
1633 assert_eq!(markers.iter().filter(|m| **m).count(), 1);
1634 assert!(markers.last().unwrap());
1635
1636 let out = vp9_depacketize(&packets).expect("frame completed");
1637 assert_eq!(&out.data[..], &frame[..]);
1638 assert!(out.keyframe, "keyframe → P bit clear");
1639 assert_eq!(out.timestamp, 9000);
1640 }
1641
1642 #[test]
1643 fn vp9_inter_frame_is_not_a_keyframe() {
1644 let mut pkt = Vp9Packetizer::new(98, 1, 1200);
1645 let packets = pkt.packetize(&[1, 2, 3], 0, false);
1646 assert_eq!(packets.len(), 1);
1647 let out = vp9_depacketize(&packets).expect("frame");
1648 assert_eq!(&out.data[..], &[1, 2, 3]);
1649 assert!(!out.keyframe, "P bit set → inter frame");
1650 }
1651
1652 #[cfg(feature = "codec-av1")]
1655 fn av1_depacketize(packets: &[Vec<u8>]) -> Option<AccessUnit> {
1656 let mut d = Av1Depacketizer::new();
1657 let mut out = None;
1658 for p in packets {
1659 let h = RtpHeader::parse(p).unwrap();
1660 if let Some(f) = d
1661 .push(&p[h.payload_offset..], h.marker, h.timestamp)
1662 .unwrap()
1663 {
1664 out = Some(f);
1665 }
1666 }
1667 out
1668 }
1669
1670 #[cfg(feature = "codec-av1")]
1671 #[test]
1672 fn av1_temporal_unit_round_trips_without_delimiter() {
1673 let td = [0x12u8, 0x00];
1675 let seq = [0x0Au8, 0x02, 0xAA, 0xBB];
1676 let frame = [0x32u8, 0x03, 0x11, 0x22, 0x33];
1677 let mut tu = Vec::new();
1678 tu.extend_from_slice(&td);
1679 tu.extend_from_slice(&seq);
1680 tu.extend_from_slice(&frame);
1681
1682 let mut pkt = Av1Packetizer::new(99, 7, 1200);
1683 let packets = pkt.packetize(&tu, 1000);
1684 let out = av1_depacketize(&packets).expect("TU completed");
1685
1686 let mut expected = Vec::new();
1688 expected.extend_from_slice(&seq);
1689 expected.extend_from_slice(&frame);
1690 assert_eq!(&out.data[..], &expected[..]);
1691 assert!(out.keyframe, "sequence header → new coded video sequence");
1692 assert_eq!(out.timestamp, 1000);
1693 }
1694
1695 #[cfg(feature = "codec-av1")]
1696 #[test]
1697 fn av1_large_temporal_unit_fragments_and_round_trips() {
1698 let mut frame = vec![0x32u8, 0xAC, 0x02];
1700 frame.extend((0..300u16).map(|i| i as u8));
1701 let mut tu = vec![0x12u8, 0x00]; tu.extend_from_slice(&frame);
1703
1704 let mut pkt = Av1Packetizer::new(99, 1, 64); let packets = pkt.packetize(&tu, 0);
1706 assert!(packets.len() > 1, "large TU fragmented");
1707 for (i, p) in packets.iter().enumerate() {
1709 let agg = p[RtpHeader::parse(p).unwrap().payload_offset];
1710 assert_eq!((agg & 0x80 != 0), i > 0, "Z continuation bit");
1711 assert_eq!(
1712 (agg & 0x40 != 0),
1713 i + 1 < packets.len(),
1714 "Y continuation bit"
1715 );
1716 }
1717
1718 let out = av1_depacketize(&packets).expect("TU completed");
1719 assert_eq!(&out.data[..], &frame[..], "frame OBU reconstructed");
1720 }
1721}