use crate::annexb::{iter_annexb_nals, iter_length_prefixed_nals};
const AVC_NAL_TYPE_MASK: u8 = 0x1F;
const AVC_NAL_IDR: u8 = 5;
const HEVC_NAL_TYPE_SHIFT: u8 = 1;
const HEVC_NAL_TYPE_MASK: u8 = 0x3F;
const HEVC_IRAP_FIRST: u8 = 16;
const HEVC_IRAP_LAST: u8 = 23;
const VVC_NAL_TYPE_SHIFT: u8 = 3;
const VVC_NAL_TYPE_MASK: u8 = 0x1F;
const VVC_NAL_IDR_W_RADL: u8 = 7;
const VVC_NAL_IDR_N_LP: u8 = 8;
const VVC_NAL_CRA: u8 = 9;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub enum NalCodec {
Avc,
Hevc,
Vvc,
}
impl NalCodec {
pub fn name(&self) -> &'static str {
match self {
NalCodec::Avc => "AVC",
NalCodec::Hevc => "HEVC",
NalCodec::Vvc => "VVC",
}
}
fn header_len(self) -> usize {
match self {
NalCodec::Avc => 1,
NalCodec::Hevc | NalCodec::Vvc => 2,
}
}
}
broadcast_common::impl_spec_display!(NalCodec);
pub fn nal_unit_type(codec: NalCodec, nal: &[u8]) -> Option<u8> {
if nal.len() < codec.header_len() {
return None;
}
Some(match codec {
NalCodec::Avc => nal[0] & AVC_NAL_TYPE_MASK,
NalCodec::Hevc => (nal[0] >> HEVC_NAL_TYPE_SHIFT) & HEVC_NAL_TYPE_MASK,
NalCodec::Vvc => (nal[1] >> VVC_NAL_TYPE_SHIFT) & VVC_NAL_TYPE_MASK,
})
}
pub fn is_keyframe_nal(codec: NalCodec, nal: &[u8]) -> bool {
match nal_unit_type(codec, nal) {
None => false,
Some(t) => match codec {
NalCodec::Avc => t == AVC_NAL_IDR,
NalCodec::Hevc => (HEVC_IRAP_FIRST..=HEVC_IRAP_LAST).contains(&t),
NalCodec::Vvc => t == VVC_NAL_IDR_W_RADL || t == VVC_NAL_IDR_N_LP || t == VVC_NAL_CRA,
},
}
}
pub fn access_unit_is_keyframe(codec: NalCodec, au: &[u8], length_prefixed: bool) -> bool {
if length_prefixed {
match iter_length_prefixed_nals(au) {
Ok(nals) => nals.iter().any(|nal| is_keyframe_nal(codec, nal)),
Err(_) => false,
}
} else {
iter_annexb_nals(au).any(|nal| is_keyframe_nal(codec, nal))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn avc_nal_type_extraction() {
assert_eq!(nal_unit_type(NalCodec::Avc, &[0x67, 0x42]), Some(7));
assert_eq!(nal_unit_type(NalCodec::Avc, &[0x65, 0x88]), Some(5));
assert!(is_keyframe_nal(NalCodec::Avc, &[0x65, 0x88]));
assert!(!is_keyframe_nal(NalCodec::Avc, &[0x67, 0x42])); assert!(!is_keyframe_nal(NalCodec::Avc, &[0x41, 0x9a]));
assert_eq!(nal_unit_type(NalCodec::Avc, &[]), None);
assert!(!is_keyframe_nal(NalCodec::Avc, &[]));
}
#[test]
fn hevc_nal_type_extraction() {
assert_eq!(nal_unit_type(NalCodec::Hevc, &[0x42, 0x01]), Some(33));
assert!(!is_keyframe_nal(NalCodec::Hevc, &[0x42, 0x01]));
assert_eq!(nal_unit_type(NalCodec::Hevc, &[0x26, 0x01]), Some(19));
assert_eq!(nal_unit_type(NalCodec::Hevc, &[0x28, 0x01]), Some(20));
assert!(is_keyframe_nal(NalCodec::Hevc, &[0x26, 0x01]));
assert!(is_keyframe_nal(NalCodec::Hevc, &[0x28, 0x01]));
assert_eq!(nal_unit_type(NalCodec::Hevc, &[0x02, 0x01]), Some(1));
assert!(!is_keyframe_nal(NalCodec::Hevc, &[0x02, 0x01]));
assert_eq!(nal_unit_type(NalCodec::Hevc, &[0x26]), None);
}
#[test]
fn vvc_nal_type_extraction() {
assert_eq!(nal_unit_type(NalCodec::Vvc, &[0x00, 0x78]), Some(15));
assert!(!is_keyframe_nal(NalCodec::Vvc, &[0x00, 0x78]));
assert_eq!(nal_unit_type(NalCodec::Vvc, &[0x00, 0x38]), Some(7));
assert_eq!(nal_unit_type(NalCodec::Vvc, &[0x00, 0x40]), Some(8));
assert_eq!(nal_unit_type(NalCodec::Vvc, &[0x00, 0x48]), Some(9));
assert!(is_keyframe_nal(NalCodec::Vvc, &[0x00, 0x38]));
assert!(is_keyframe_nal(NalCodec::Vvc, &[0x00, 0x40]));
assert!(is_keyframe_nal(NalCodec::Vvc, &[0x00, 0x48]));
assert!(!is_keyframe_nal(NalCodec::Vvc, &[0x00, 0x00]));
}
#[test]
fn access_unit_annexb_and_length_prefixed_agree() {
let non_kf = [0x00, 0x00, 0x01, 0x67, 0x42, 0x00, 0x00, 0x01, 0x41, 0x9a];
assert!(!access_unit_is_keyframe(NalCodec::Avc, &non_kf, false));
let kf = [0x00, 0x00, 0x01, 0x67, 0x42, 0x00, 0x00, 0x01, 0x65, 0x88];
assert!(access_unit_is_keyframe(NalCodec::Avc, &kf, false));
let lp_non_kf = crate::annexb::annexb_to_length_prefixed(&non_kf);
let lp_kf = crate::annexb::annexb_to_length_prefixed(&kf);
assert!(!access_unit_is_keyframe(NalCodec::Avc, &lp_non_kf, true));
assert!(access_unit_is_keyframe(NalCodec::Avc, &lp_kf, true));
}
#[test]
fn malformed_length_prefixed_is_not_keyframe() {
let lp = [0x00, 0x00, 0x00, 0x63, 0x65, 0x88];
assert!(!access_unit_is_keyframe(NalCodec::Avc, &lp, true));
}
#[test]
fn nal_codec_display() {
assert_eq!(NalCodec::Avc.to_string(), "AVC");
assert_eq!(NalCodec::Hevc.to_string(), "HEVC");
assert_eq!(NalCodec::Vvc.to_string(), "VVC");
}
}