1#![allow(clippy::all)]
2
3use super::{CodecExtra, Depacketizer, PacketError, Packetizer};
4
5#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
7pub struct H264CodecExtra {
8 pub is_keyframe: bool,
14}
15
16#[derive(Default, Debug, Clone)]
23pub struct H264Packetizer {
24 sps_nalu: Option<Vec<u8>>,
25 pps_nalu: Option<Vec<u8>>,
26}
27
28pub const STAPA_NALU_TYPE: u8 = 24;
29pub const FUA_NALU_TYPE: u8 = 28;
30pub const FUB_NALU_TYPE: u8 = 29;
31pub const IDR_NALU_TYPE: u8 = 5;
32pub const SPS_NALU_TYPE: u8 = 7;
33pub const PPS_NALU_TYPE: u8 = 8;
34pub const AUD_NALU_TYPE: u8 = 9;
35pub const FILLER_NALU_TYPE: u8 = 12;
36
37pub const FUA_HEADER_SIZE: usize = 2;
38pub const STAPA_HEADER_SIZE: usize = 1;
39pub const STAPA_NALU_LENGTH_SIZE: usize = 2;
40
41pub const NALU_TYPE_BITMASK: u8 = 0x1F;
42pub const NALU_REF_IDC_BITMASK: u8 = 0x60;
43pub const FU_START_BITMASK: u8 = 0x80;
44pub const FU_END_BITMASK: u8 = 0x40;
45
46pub const OUTPUT_STAP_AHEADER: u8 = 0x78;
47
48pub static ANNEXB_NALUSTART_CODE: &[u8] = &[0x00, 0x00, 0x00, 0x01];
49
50pub fn detect_h264_keyframe(payload: &[u8]) -> bool {
59 if payload.is_empty() {
60 return false;
61 }
62
63 let nalu_type = payload[0] & NALU_TYPE_BITMASK;
64
65 match nalu_type {
66 1..=23 => nalu_type == IDR_NALU_TYPE,
68
69 STAPA_NALU_TYPE => {
71 let mut offset = STAPA_HEADER_SIZE;
72 while offset + STAPA_NALU_LENGTH_SIZE <= payload.len() {
73 let nalu_size = ((payload[offset] as usize) << 8) | payload[offset + 1] as usize;
74 offset += STAPA_NALU_LENGTH_SIZE;
75 if offset + nalu_size > payload.len() {
76 break;
77 }
78 if let Some(&b0) = payload.get(offset) {
79 if b0 & NALU_TYPE_BITMASK == IDR_NALU_TYPE {
80 return true;
81 }
82 }
83 offset += nalu_size;
84 }
85 false
86 }
87
88 FUA_NALU_TYPE => {
90 if payload.len() < FUA_HEADER_SIZE {
91 return false;
92 }
93 let b1 = payload[1];
94 if b1 & FU_START_BITMASK == 0 {
96 return false;
97 }
98 b1 & NALU_TYPE_BITMASK == IDR_NALU_TYPE
99 }
100
101 _ => false,
102 }
103}
104
105impl H264Packetizer {
106 fn next_ind(nalu: &[u8], start: usize) -> (isize, isize) {
107 let mut zero_count = 0;
108
109 for (i, &b) in nalu[start..].iter().enumerate() {
110 if b == 0 {
111 zero_count += 1;
112 continue;
113 } else if b == 1 && zero_count >= 2 {
114 return ((start + i - zero_count) as isize, zero_count as isize + 1);
115 }
116 zero_count = 0
117 }
118 (-1, -1)
119 }
120
121 fn emit(&mut self, nalu: &[u8], mtu: usize, payloads: &mut Vec<Vec<u8>>) {
122 if nalu.is_empty() {
123 return;
124 }
125
126 let nalu_type = nalu[0] & NALU_TYPE_BITMASK;
127 let nalu_ref_idc = nalu[0] & NALU_REF_IDC_BITMASK;
128
129 if nalu_type == AUD_NALU_TYPE || nalu_type == FILLER_NALU_TYPE {
130 return;
131 } else if nalu_type == SPS_NALU_TYPE {
132 self.sps_nalu = Some(nalu.to_vec());
133 return;
134 } else if nalu_type == PPS_NALU_TYPE {
135 self.pps_nalu = Some(nalu.to_vec());
136 return;
137 } else if let (Some(sps_nalu), Some(pps_nalu)) = (&self.sps_nalu, &self.pps_nalu) {
138 let sps_len = (sps_nalu.len() as u16).to_be_bytes();
140 let pps_len = (pps_nalu.len() as u16).to_be_bytes();
141
142 let mut stap_a_nalu = Vec::with_capacity(1 + 2 + sps_nalu.len() + 2 + pps_nalu.len());
143 stap_a_nalu.push(OUTPUT_STAP_AHEADER);
144 stap_a_nalu.extend(sps_len);
145 stap_a_nalu.extend_from_slice(sps_nalu);
146 stap_a_nalu.extend(pps_len);
147 stap_a_nalu.extend_from_slice(pps_nalu);
148 if stap_a_nalu.len() <= mtu {
149 payloads.push(stap_a_nalu);
150 }
151 }
152
153 if self.sps_nalu.is_some() && self.pps_nalu.is_some() {
154 self.sps_nalu = None;
155 self.pps_nalu = None;
156 }
157
158 if nalu.len() <= mtu {
160 payloads.push(nalu.to_vec());
161 return;
162 }
163
164 let max_fragment_size = mtu as isize - FUA_HEADER_SIZE as isize;
166
167 let nalu_data = nalu;
179 let mut nalu_data_index = 1;
181 let nalu_data_length = nalu.len() as isize - nalu_data_index;
182 let mut nalu_data_remaining = nalu_data_length;
183
184 if std::cmp::min(max_fragment_size, nalu_data_remaining) <= 0 {
185 return;
186 }
187
188 while nalu_data_remaining > 0 {
189 let current_fragment_size = std::cmp::min(max_fragment_size, nalu_data_remaining);
190 let mut out = Vec::with_capacity(FUA_HEADER_SIZE + current_fragment_size as usize);
192 let b0 = FUA_NALU_TYPE | nalu_ref_idc;
198 out.push(b0);
199
200 let mut b1 = nalu_type;
207 if nalu_data_remaining == nalu_data_length {
208 b1 |= 1 << 7;
210 } else if nalu_data_remaining - current_fragment_size == 0 {
211 b1 |= 1 << 6;
213 }
214 out.push(b1);
215
216 out.extend_from_slice(
217 &nalu_data
218 [nalu_data_index as usize..(nalu_data_index + current_fragment_size) as usize],
219 );
220 payloads.push(out);
221
222 nalu_data_remaining -= current_fragment_size;
223 nalu_data_index += current_fragment_size;
224 }
225 }
226}
227
228impl Packetizer for H264Packetizer {
229 fn packetize(&mut self, mtu: usize, payload: &[u8]) -> Result<Vec<Vec<u8>>, PacketError> {
231 if payload.is_empty() || mtu == 0 {
232 return Ok(vec![]);
233 }
234
235 let mut payloads = vec![];
236
237 let (mut next_ind_start, mut next_ind_len) = H264Packetizer::next_ind(payload, 0);
238 if next_ind_start == -1 {
239 self.emit(payload, mtu, &mut payloads);
240 } else {
241 while next_ind_start != -1 {
242 let prev_start = (next_ind_start + next_ind_len) as usize;
243 let (next_ind_start2, next_ind_len2) =
244 H264Packetizer::next_ind(payload, prev_start);
245 next_ind_start = next_ind_start2;
246 next_ind_len = next_ind_len2;
247 if next_ind_start != -1 {
248 self.emit(
249 &payload[prev_start..next_ind_start as usize],
250 mtu,
251 &mut payloads,
252 );
253 } else {
254 self.emit(&payload[prev_start..], mtu, &mut payloads);
256 }
257 }
258 }
259
260 Ok(payloads)
261 }
262
263 fn is_marker(&mut self, _data: &[u8], _previous: Option<&[u8]>, last: bool) -> bool {
264 last
265 }
266}
267
268#[derive(PartialEq, Eq, Debug, Default, Clone)]
275pub struct H264Depacketizer {
276 pub is_avc: bool,
278 fua_buffer: Option<Vec<u8>>,
279}
280
281impl Depacketizer for H264Depacketizer {
282 fn out_size_hint(&self, packets_size: usize) -> Option<usize> {
283 let estimated_packets = (packets_size / 1200).saturating_add(1);
285 Some(packets_size.saturating_add(4usize.saturating_mul(estimated_packets)))
286 }
287
288 fn depacketize(
291 &mut self,
292 packet: &[u8],
293 out: &mut Vec<u8>,
294 extra: &mut CodecExtra,
295 ) -> Result<(), PacketError> {
296 if packet.len() == 0 {
297 return Err(PacketError::ErrShortPacket);
298 }
299
300 let b0 = packet[0];
303 let nalu_type = b0 & NALU_TYPE_BITMASK;
304
305 match nalu_type {
306 t @ 1..=23 => {
307 let is_keyframe = if let CodecExtra::H264(e) = extra {
308 (t == IDR_NALU_TYPE) | e.is_keyframe
309 } else {
310 t == IDR_NALU_TYPE
311 };
312 *extra = CodecExtra::H264(H264CodecExtra { is_keyframe });
313
314 if self.is_avc {
315 out.extend_from_slice(&(packet.len() as u32).to_be_bytes());
316 } else {
317 out.extend_from_slice(ANNEXB_NALUSTART_CODE);
318 }
319 out.extend_from_slice(packet);
320 Ok(())
321 }
322 STAPA_NALU_TYPE => {
323 let mut curr_offset = STAPA_HEADER_SIZE;
324 while curr_offset + 1 < packet.len() {
325 let nalu_size =
326 ((packet[curr_offset] as usize) << 8) | packet[curr_offset + 1] as usize;
327 curr_offset += STAPA_NALU_LENGTH_SIZE;
328
329 if curr_offset + nalu_size > packet.len() {
330 return Err(PacketError::StapASizeLargerThanBuffer(
331 nalu_size,
332 packet.len() - curr_offset,
333 ));
334 }
335
336 let Some(b0) = packet.get(curr_offset) else {
337 continue;
338 };
339 let t = b0 & NALU_TYPE_BITMASK;
340 let is_keyframe = if let CodecExtra::H264(e) = extra {
341 (t == IDR_NALU_TYPE) | e.is_keyframe
342 } else {
343 t == IDR_NALU_TYPE
344 };
345 *extra = CodecExtra::H264(H264CodecExtra { is_keyframe });
346
347 if self.is_avc {
348 out.extend_from_slice(&(nalu_size as u32).to_be_bytes());
349 } else {
350 out.extend_from_slice(ANNEXB_NALUSTART_CODE);
351 }
352 out.extend_from_slice(&packet[curr_offset..curr_offset + nalu_size]);
353 curr_offset += nalu_size;
354 }
355
356 Ok(())
357 }
358 FUA_NALU_TYPE => {
359 if packet.len() < FUA_HEADER_SIZE as usize {
360 return Err(PacketError::ErrShortPacket);
361 }
362
363 if self.fua_buffer.is_none() {
364 self.fua_buffer = Some(Vec::new());
365 }
366
367 if let Some(fua_buffer) = &mut self.fua_buffer {
368 fua_buffer.extend_from_slice(&packet[FUA_HEADER_SIZE as usize..]);
369 }
370
371 let b1 = packet[1];
372 if b1 & FU_END_BITMASK != 0 {
373 let nalu_ref_idc = b0 & NALU_REF_IDC_BITMASK;
374 let fragmented_nalu_type = b1 & NALU_TYPE_BITMASK;
375
376 let is_keyframe = if let CodecExtra::H264(e) = extra {
377 (fragmented_nalu_type == IDR_NALU_TYPE) | e.is_keyframe
378 } else {
379 fragmented_nalu_type == IDR_NALU_TYPE
380 };
381 *extra = CodecExtra::H264(H264CodecExtra { is_keyframe });
382
383 if let Some(fua_buffer) = self.fua_buffer.take() {
384 if self.is_avc {
385 out.extend_from_slice(&((fua_buffer.len() + 1) as u32).to_be_bytes());
386 } else {
387 out.extend_from_slice(ANNEXB_NALUSTART_CODE);
388 }
389 out.push(nalu_ref_idc | fragmented_nalu_type);
390 out.extend_from_slice(&fua_buffer);
391 }
392
393 Ok(())
394 } else {
395 Ok(())
396 }
397 }
398 _ => Err(PacketError::NaluTypeIsNotHandled(nalu_type)),
399 }
400 }
401
402 fn is_partition_head(&self, packet: &[u8]) -> bool {
404 if packet.len() < 2 {
405 return false;
406 }
407
408 if packet[0] & NALU_TYPE_BITMASK == FUA_NALU_TYPE
409 || packet[0] & NALU_TYPE_BITMASK == FUB_NALU_TYPE
410 {
411 (packet[1] & FU_START_BITMASK) != 0
412 } else {
413 true
414 }
415 }
416
417 fn is_partition_tail(&self, marker: bool, _packet: &[u8]) -> bool {
418 marker
419 }
420}
421
422#[cfg(test)]
423mod test {
424 use super::*;
425
426 #[test]
427 fn test_h264_payload() -> Result<(), PacketError> {
428 let empty = &[];
429 let small_payload = &[0x90, 0x90, 0x90];
430 let multiple_payload = &[0x00, 0x00, 0x01, 0x90, 0x00, 0x00, 0x01, 0x90];
431 let large_payload = &[
432 0x00, 0x00, 0x01, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10,
433 0x11, 0x12, 0x13, 0x14, 0x15,
434 ];
435 let large_payload_packetized = vec![
436 &[0x1c, 0x80, 0x01, 0x02, 0x03],
437 &[0x1c, 0x00, 0x04, 0x05, 0x06],
438 &[0x1c, 0x00, 0x07, 0x08, 0x09],
439 &[0x1c, 0x00, 0x10, 0x11, 0x12],
440 &[0x1c, 0x40, 0x13, 0x14, 0x15],
441 ];
442
443 let mut pck = H264Packetizer::default();
444
445 let result = pck.packetize(1, empty)?;
447 assert!(result.is_empty(), "Generated payload should be empty");
448
449 let result = pck.packetize(0, small_payload)?;
451 assert_eq!(result.len(), 0, "Generated payload should be empty");
452
453 let result = pck.packetize(1, small_payload)?;
455 assert_eq!(result.len(), 0, "Generated payload should be empty");
456
457 let result = pck.packetize(5, small_payload)?;
459 assert_eq!(result.len(), 1, "Generated payload should be the 1");
460 assert_eq!(
461 result[0].len(),
462 small_payload.len(),
463 "Generated payload should be the same size as original payload size"
464 );
465
466 let result = pck.packetize(5, multiple_payload)?;
468 assert_eq!(result.len(), 2, "2 nal units should be broken out");
469 for i in 0..2 {
470 assert_eq!(
471 result[i].len(),
472 1,
473 "Payload {} of 2 is packed incorrectly",
474 i + 1,
475 );
476 }
477
478 let result = pck.packetize(5, large_payload)?;
480 assert_eq!(
481 result, large_payload_packetized,
482 "FU-A packetization failed"
483 );
484
485 let small_payload2 = &[0x09, 0x00, 0x00];
487 let result = pck.packetize(5, small_payload2)?;
488 assert_eq!(result.len(), 0, "Generated payload should be empty");
489
490 Ok(())
491 }
492
493 macro_rules! test_h264 {
494 ($name:tt, $is_avc:expr, $is_ok: expr, $payload:expr, $err:tt) => {
495 #[test]
496 fn $name() -> Result<(), PacketError> {
497 let mut pkt = H264Depacketizer::default();
498 pkt.is_avc = $is_avc;
499 let mut extra = CodecExtra::None;
500 let mut out: Vec<u8> = Vec::new();
501 let result = pkt.depacketize($payload, &mut out, &mut extra);
502 if $is_ok {
503 assert!(result.is_ok(), $err);
504 } else {
505 assert!(result.is_err(), $err);
506 }
507 Ok(())
508 }
509 };
510 }
511
512 test_h264!(
513 nil_payload,
514 false,
515 false,
516 &[],
517 "Unmarshal did not fail on nil payload"
518 );
519 test_h264!(
520 unit_delimiter,
521 false,
522 true,
523 &[0x09, 0x30],
524 "Unmarshal should accept minimal h.264 access unit delimiter"
525 );
526 test_h264!(
527 end_of_sequence_nalu,
528 false,
529 true,
530 &[0x0A],
531 "Unmarshal should accept end of sequence NALU"
532 );
533 test_h264!(
534 not_handled,
535 false,
536 false,
537 &[0xFF, 0x00, 0x00],
538 "Unmarshal accepted a packet with a NALU Type we don't handle"
539 );
540 test_h264!(
541 incomplete_single_payload_multi_nalu,
542 false,
543 false,
544 &[
545 0x78, 0x00, 0x0f, 0x67, 0x42, 0xc0, 0x1f, 0x1a, 0x32, 0x35, 0x01, 0x40, 0x7a, 0x40,
546 0x3c, 0x22, 0x11,
547 ],
548 "Unmarshal accepted a STAP-A packet with insufficient data"
549 );
550
551 #[test]
552 fn single_payload() -> Result<(), PacketError> {
553 let mut pkt = H264Depacketizer::default();
554 let mut extra = CodecExtra::None;
555 let mut out: Vec<u8> = Vec::new();
556 let single_payload = &[0x90, 0x90, 0x90];
557 let _ = pkt.depacketize(single_payload, &mut out, &mut extra);
558 let single_payload_unmarshaled = &[0x00, 0x00, 0x00, 0x01, 0x90, 0x90, 0x90];
559 assert_eq!(
560 out, single_payload_unmarshaled,
561 "Unmarshaling a single payload shouldn't modify the payload"
562 );
563 Ok(())
564 }
565
566 #[test]
567 fn single_payload_avc() -> Result<(), PacketError> {
568 let mut pkt = H264Depacketizer::default();
569 pkt.is_avc = true;
570 let mut extra = CodecExtra::None;
571 let mut out: Vec<u8> = Vec::new();
572 let single_payload = &[0x90, 0x90, 0x90];
573 let _ = pkt.depacketize(single_payload, &mut out, &mut extra);
574 let single_payload_unmarshaled_avc = &[0x00, 0x00, 0x00, 0x03, 0x90, 0x90, 0x90];
575 assert_eq!(
576 out, single_payload_unmarshaled_avc,
577 "Unmarshaling a single payload into avc stream shouldn't modify the payload"
578 );
579 Ok(())
580 }
581
582 #[test]
583 fn h264_large_out() -> Result<(), PacketError> {
584 let large_payload_packetized = vec![
585 &[0x1c, 0x80, 0x01, 0x02, 0x03],
586 &[0x1c, 0x00, 0x04, 0x05, 0x06],
587 &[0x1c, 0x00, 0x07, 0x08, 0x09],
588 &[0x1c, 0x00, 0x10, 0x11, 0x12],
589 &[0x1c, 0x40, 0x13, 0x14, 0x15],
590 ];
591
592 let large_payload = &[
593 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
594 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
595 ];
596
597 let mut pkt = H264Depacketizer::default();
598 let mut extra = CodecExtra::None;
599
600 let mut large_out = Vec::new();
601 for p in &large_payload_packetized {
602 pkt.depacketize(*p, &mut large_out, &mut extra)?;
603 }
604 assert_eq!(
605 large_out, large_payload,
606 "Failed to unmarshal a large payload"
607 );
608
609 Ok(())
610 }
611
612 #[test]
613 fn h264_large_out_avc() -> Result<(), PacketError> {
614 let large_payload_packetized = vec![
615 &[0x1c, 0x80, 0x01, 0x02, 0x03],
616 &[0x1c, 0x00, 0x04, 0x05, 0x06],
617 &[0x1c, 0x00, 0x07, 0x08, 0x09],
618 &[0x1c, 0x00, 0x10, 0x11, 0x12],
619 &[0x1c, 0x40, 0x13, 0x14, 0x15],
620 ];
621
622 let large_payload_avc = &[
623 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
624 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
625 ];
626
627 let mut avc_pkt = H264Depacketizer {
628 is_avc: true,
629 ..Default::default()
630 };
631
632 let mut extra = CodecExtra::None;
633
634 let mut large_out_avc = Vec::new();
635 for p in &large_payload_packetized {
636 avc_pkt.depacketize(*p, &mut large_out_avc, &mut extra)?;
637 }
638 assert_eq!(
639 large_out_avc, large_payload_avc,
640 "Failed to unmarshal a large payload into avc stream"
641 );
642
643 Ok(())
644 }
645
646 #[test]
647 fn single_payload_multi_nalu() -> Result<(), PacketError> {
648 let single_payload_multi_nalu = &[
649 0x78, 0x00, 0x0f, 0x67, 0x42, 0xc0, 0x1f, 0x1a, 0x32, 0x35, 0x01, 0x40, 0x7a, 0x40,
650 0x3c, 0x22, 0x11, 0xa8, 0x00, 0x05, 0x68, 0x1a, 0x34, 0xe3, 0xc8, 0x00,
651 ];
652 let single_payload_multi_nalu_unmarshaled = &[
653 0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0xc0, 0x1f, 0x1a, 0x32, 0x35, 0x01, 0x40, 0x7a,
654 0x40, 0x3c, 0x22, 0x11, 0xa8, 0x00, 0x00, 0x00, 0x01, 0x68, 0x1a, 0x34, 0xe3, 0xc8,
655 ];
656
657 let mut pkt = H264Depacketizer::default();
658
659 let mut extra = CodecExtra::None;
660
661 let mut out = Vec::new();
662 pkt.depacketize(single_payload_multi_nalu, &mut out, &mut extra)?;
663 assert_eq!(
664 out, single_payload_multi_nalu_unmarshaled,
665 "Failed to unmarshal a single packet with multiple NALUs"
666 );
667
668 Ok(())
669 }
670
671 #[test]
672 fn single_payload_multi_nalu_avc() -> Result<(), PacketError> {
673 let single_payload_multi_nalu = &[
674 0x78, 0x00, 0x0f, 0x67, 0x42, 0xc0, 0x1f, 0x1a, 0x32, 0x35, 0x01, 0x40, 0x7a, 0x40,
675 0x3c, 0x22, 0x11, 0xa8, 0x00, 0x05, 0x68, 0x1a, 0x34, 0xe3, 0xc8, 0x00,
676 ];
677 let single_payload_multi_nalu_unmarshaled_avc = &[
678 0x00, 0x00, 0x00, 0x0f, 0x67, 0x42, 0xc0, 0x1f, 0x1a, 0x32, 0x35, 0x01, 0x40, 0x7a,
679 0x40, 0x3c, 0x22, 0x11, 0xa8, 0x00, 0x00, 0x00, 0x05, 0x68, 0x1a, 0x34, 0xe3, 0xc8,
680 ];
681
682 let mut avc_pkt = H264Depacketizer::default();
683 avc_pkt.is_avc = true;
684
685 let mut extra = CodecExtra::None;
686
687 let mut out = Vec::new();
688 avc_pkt.depacketize(single_payload_multi_nalu, &mut out, &mut extra)?;
689 assert_eq!(
690 out, single_payload_multi_nalu_unmarshaled_avc,
691 "Failed to unmarshal a single packet with multiple NALUs into avc stream"
692 );
693
694 Ok(())
695 }
696
697 #[test]
698 fn test_h264_partition_head_checker_is_partition_head() -> Result<(), PacketError> {
699 let h264 = H264Depacketizer::default();
700 let empty_nalu = &[];
701 assert!(
702 !h264.is_partition_head(empty_nalu),
703 "empty nalu must not be a partition head"
704 );
705
706 let single_nalu = &[1, 0];
707 assert!(
708 h264.is_partition_head(single_nalu),
709 "single nalu must be a partition head"
710 );
711
712 let stapa_nalu = &[STAPA_NALU_TYPE, 0];
713 assert!(
714 h264.is_partition_head(stapa_nalu),
715 "stapa nalu must be a partition head"
716 );
717
718 let fua_start_nalu = &[FUA_NALU_TYPE, FU_START_BITMASK];
719 assert!(
720 h264.is_partition_head(fua_start_nalu),
721 "fua start nalu must be a partition head"
722 );
723
724 let fua_end_nalu = &[FUA_NALU_TYPE, FU_END_BITMASK];
725 assert!(
726 !h264.is_partition_head(fua_end_nalu),
727 "fua end nalu must not be a partition head"
728 );
729
730 let fub_start_nalu = &[FUB_NALU_TYPE, FU_START_BITMASK];
731 assert!(
732 h264.is_partition_head(fub_start_nalu),
733 "fub start nalu must be a partition head"
734 );
735
736 let fub_end_nalu = &[FUB_NALU_TYPE, FU_END_BITMASK];
737 assert!(
738 !h264.is_partition_head(fub_end_nalu),
739 "fub end nalu must not be a partition head"
740 );
741
742 Ok(())
743 }
744
745 #[test]
746 fn test_h264_packetizer_payload_sps_and_pps_handling() -> Result<(), PacketError> {
747 let mut pck = H264Packetizer::default();
748 let expected: Vec<&[u8]> = vec![
749 &[
750 0x78, 0x00, 0x03, 0x07, 0x00, 0x01, 0x00, 0x03, 0x08, 0x02, 0x03,
751 ],
752 &[0x05, 0x04, 0x05],
753 ];
754
755 let res = pck.packetize(1500, &[0x07, 0x00, 0x01])?;
757 assert!(res.is_empty(), "Generated payload should be empty");
758
759 let res = pck.packetize(1500, &[0x08, 0x02, 0x03])?;
760 assert!(res.is_empty(), "Generated payload should be empty");
761
762 let actual = pck.packetize(1500, &[0x05, 0x04, 0x05])?;
763 assert_eq!(actual, expected, "SPS and PPS aren't packed together");
764
765 Ok(())
766 }
767
768 #[test]
769 fn test_h264_depacketizer_idr_handling() -> Result<(), PacketError> {
770 let mut pck = H264Depacketizer::default();
771 let mut extra = CodecExtra::None;
772 let mut out = vec![];
773
774 let packet = [0x85];
776 pck.depacketize(&packet, &mut out, &mut extra)?;
777 let CodecExtra::H264(e) = extra else {
778 panic!("Expected CodecExtra::H264");
779 };
780 assert!(e.is_keyframe);
781
782 let packet = vec![
784 vec![
785 120, 0, 15, 103, 66, 192, 21, 140, 141, 64, 160, 203, 207, 0, 240, 136, 70, 160, 0,
786 4, 104, 206, 60, 128, 1, 20, 101,
787 ],
788 vec![0; 276],
789 ]
790 .into_iter()
791 .flatten()
792 .collect::<Vec<_>>();
793 pck.depacketize(packet.as_slice(), &mut out, &mut extra)?;
794 let CodecExtra::H264(e) = extra else {
795 panic!("Expected CodecExtra::H264");
796 };
797 assert!(e.is_keyframe);
798
799 let packet = [124, 69];
801 pck.depacketize(&packet, &mut out, &mut extra)?;
802 let CodecExtra::H264(e) = extra else {
803 panic!("Expected CodecExtra::H264");
804 };
805 assert!(e.is_keyframe);
806 Ok(())
807 }
808
809 #[test]
810 fn parse_first_packet() {
811 const PACKET: &[u8] = &[
812 120, 000, 015, 103, 066, 192, 021, 140, 141, 064, 160, 203, 207, 000, 240, 136, 070,
813 160, 000, 004, 104, 206, 060, 128, 000, 204, 101, 184, 000, 004, 000, 000, 005, 057,
814 049, 064, 000, 064, 222, 078, 078, 078, 078, 078, 078, 078, 078, 078, 078, 078, 078,
815 078, 078, 078, 078, 078, 078, 078, 186, 235, 174, 186, 235, 174, 186, 235, 174, 186,
816 235, 174, 186, 235, 174, 186, 235, 174, 186, 235, 174, 186, 235, 174, 186, 235, 174,
817 186, 235, 174, 186, 235, 174, 186, 235, 174, 186, 235, 173, 223, 039, 125, 247, 223,
818 125, 245, 215, 093, 117, 215, 093, 117, 214, 239, 174, 187, 235, 174, 186, 235, 174,
819 186, 235, 174, 186, 235, 174, 186, 235, 174, 186, 235, 174, 186, 235, 174, 186, 235,
820 174, 186, 235, 174, 183, 093, 117, 215, 093, 117, 215, 093, 117, 215, 093, 117, 215,
821 093, 117, 215, 093, 117, 215, 092, 189, 117, 215, 093, 117, 215, 093, 117, 215, 093,
822 117, 215, 093, 117, 215, 093, 117, 215, 093, 117, 214, 239, 190, 251, 239, 190, 186,
823 235, 174, 186, 235, 174, 186, 235, 174, 186, 235, 174, 186, 235, 174, 186, 235, 174,
824 186, 235, 174, 186, 235, 175, 227, 255, 240, 247, 021, 223, 125, 247, 223, 125, 247,
825 223, 125, 247, 223, 125, 247, 223, 125, 248,
826 ];
827
828 let mut pck = H264Depacketizer::default();
829 let mut extra = CodecExtra::None;
830 let mut out = vec![];
831 pck.depacketize(PACKET, &mut out, &mut extra).unwrap();
832 }
833
834 #[test]
835 fn test_out_of_bounds_access() {
836 const PACKET: &[u8] = &[STAPA_NALU_TYPE, 0x00, 0x00];
837
838 let mut pck = H264Depacketizer::default();
839 let mut extra = CodecExtra::None;
840 let mut out = vec![];
841 pck.depacketize(PACKET, &mut out, &mut extra).unwrap();
842 }
843
844 #[test]
845 fn test_detect_h264_keyframe() {
846 assert!(!detect_h264_keyframe(&[]));
848
849 assert!(detect_h264_keyframe(&[0x65, 0x00, 0x00])); assert!(!detect_h264_keyframe(&[0x41, 0x00, 0x00])); assert!(!detect_h264_keyframe(&[0x67, 0x00, 0x00]));
857
858 assert!(!detect_h264_keyframe(&[0x68, 0x00, 0x00]));
860
861 let stapa_with_idr = [
866 0x18, 0x00, 0x02, 0x67, 0xAA, 0x00, 0x02, 0x65, 0xBB, ];
870 assert!(detect_h264_keyframe(&stapa_with_idr));
871
872 let stapa_no_idr = [
874 0x18, 0x00, 0x02, 0x67, 0xAA, 0x00, 0x02, 0x68, 0xBB, ];
878 assert!(!detect_h264_keyframe(&stapa_no_idr));
879
880 let fua_start_idr = [
882 0x7C, 0x85, 0x00, 0x00,
885 ];
886 assert!(detect_h264_keyframe(&fua_start_idr));
887
888 let fua_start_non_idr = [
890 0x7C, 0x81, 0x00, 0x00,
893 ];
894 assert!(!detect_h264_keyframe(&fua_start_non_idr));
895
896 let fua_continuation = [
898 0x7C, 0x05, 0x00, 0x00,
901 ];
902 assert!(!detect_h264_keyframe(&fua_continuation));
903
904 assert!(!detect_h264_keyframe(&[0x7C]));
906 }
907}