use super::common::AnnexBNalIter;
pub mod nal_type {
pub const BLA_W_LP: u8 = 16;
pub const BLA_W_RADL: u8 = 17;
pub const BLA_N_LP: u8 = 18;
pub const IDR_W_RADL: u8 = 19;
pub const IDR_N_LP: u8 = 20;
pub const CRA_NUT: u8 = 21;
pub const VPS: u8 = 32;
pub const SPS: u8 = 33;
pub const PPS: u8 = 34;
pub const AUD: u8 = 35;
pub const EOS: u8 = 36;
pub const EOB: u8 = 37;
pub const FD: u8 = 38;
pub const PREFIX_SEI: u8 = 39;
pub const SUFFIX_SEI: u8 = 40;
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct HevcConfig {
pub vps: Vec<u8>,
pub sps: Vec<u8>,
pub pps: Vec<u8>,
}
impl HevcConfig {
pub fn new(vps: Vec<u8>, sps: Vec<u8>, pps: Vec<u8>) -> Self {
Self { vps, sps, pps }
}
pub fn general_profile_space(&self) -> u8 {
self.sps.get(3).map(|b| (b >> 6) & 0x03).unwrap_or(0)
}
pub fn general_tier_flag(&self) -> bool {
self.sps
.get(3)
.map(|b| (b >> 5) & 0x01 != 0)
.unwrap_or(false)
}
pub fn general_profile_idc(&self) -> u8 {
self.sps.get(3).map(|b| b & 0x1f).unwrap_or(1)
}
pub fn general_level_idc(&self) -> u8 {
self.sps.get(14).copied().unwrap_or(93)
}
}
#[inline]
pub fn hevc_nal_type(nal: &[u8]) -> u8 {
if nal.is_empty() {
return 0;
}
(nal[0] >> 1) & 0x3f
}
#[inline]
pub fn is_hevc_keyframe_nal_type(nal_type: u8) -> bool {
matches!(
nal_type,
nal_type::BLA_W_LP
| nal_type::BLA_W_RADL
| nal_type::BLA_N_LP
| nal_type::IDR_W_RADL
| nal_type::IDR_N_LP
| nal_type::CRA_NUT
)
}
pub fn extract_hevc_config(data: &[u8]) -> Option<HevcConfig> {
let mut vps: Option<&[u8]> = None;
let mut sps: Option<&[u8]> = None;
let mut pps: Option<&[u8]> = None;
for nal in AnnexBNalIter::new(data) {
if nal.is_empty() {
continue;
}
let nal_type = hevc_nal_type(nal);
match nal_type {
nal_type::VPS if vps.is_none() => vps = Some(nal),
nal_type::SPS if sps.is_none() => sps = Some(nal),
nal_type::PPS if pps.is_none() => pps = Some(nal),
_ => {}
}
if vps.is_some() && sps.is_some() && pps.is_some() {
break;
}
}
Some(HevcConfig {
vps: vps?.to_vec(),
sps: sps?.to_vec(),
pps: pps?.to_vec(),
})
}
pub fn hevc_annexb_to_hvcc(data: &[u8]) -> Vec<u8> {
let mut out = Vec::new();
for nal in AnnexBNalIter::new(data) {
if nal.is_empty() {
continue;
}
let len = nal.len() as u32;
out.extend_from_slice(&len.to_be_bytes());
out.extend_from_slice(nal);
}
if out.is_empty() && !data.is_empty() {
let len = data.len() as u32;
out.extend_from_slice(&len.to_be_bytes());
out.extend_from_slice(data);
}
out
}
pub fn is_hevc_keyframe(data: &[u8]) -> bool {
for nal in AnnexBNalIter::new(data) {
if nal.is_empty() {
continue;
}
let nal_type = hevc_nal_type(nal);
if is_hevc_keyframe_nal_type(nal_type) {
return true;
}
}
false
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_hevc_nal_type_extraction() {
let vps_nal = [0x40, 0x01, 0x0c];
assert_eq!(hevc_nal_type(&vps_nal), 32);
let sps_nal = [0x42, 0x01, 0x01];
assert_eq!(hevc_nal_type(&sps_nal), 33);
}
#[test]
fn test_is_hevc_keyframe_nal_type() {
assert!(is_hevc_keyframe_nal_type(nal_type::IDR_W_RADL));
assert!(is_hevc_keyframe_nal_type(nal_type::IDR_N_LP));
assert!(is_hevc_keyframe_nal_type(nal_type::CRA_NUT));
assert!(is_hevc_keyframe_nal_type(nal_type::BLA_W_LP));
assert!(!is_hevc_keyframe_nal_type(nal_type::VPS));
assert!(!is_hevc_keyframe_nal_type(nal_type::SPS));
assert!(!is_hevc_keyframe_nal_type(nal_type::PPS));
}
#[test]
fn test_extract_hevc_config_empty() {
assert!(extract_hevc_config(&[]).is_none());
}
#[test]
fn test_extract_hevc_config_success() {
let data = [
0x00, 0x00, 0x00, 0x01, 0x40, 0x01, 0x0c, 0x01, 0x00, 0x00, 0x00, 0x01, 0x42, 0x01, 0x01, 0x21, 0x00, 0x00, 0x00, 0x01, 0x44, 0x01, 0xc0, 0x73, ];
let config = extract_hevc_config(&data).unwrap();
assert_eq!(hevc_nal_type(&config.vps), nal_type::VPS);
assert_eq!(hevc_nal_type(&config.sps), nal_type::SPS);
assert_eq!(hevc_nal_type(&config.pps), nal_type::PPS);
}
#[test]
fn test_extract_hevc_config_missing_vps() {
let data = [
0x00, 0x00, 0x00, 0x01, 0x42, 0x01, 0x01, 0x21, 0x00, 0x00, 0x00, 0x01, 0x44, 0x01, 0xc0, 0x73, ];
assert!(extract_hevc_config(&data).is_none());
}
#[test]
fn test_extract_hevc_config_missing_sps() {
let data = [
0x00, 0x00, 0x00, 0x01, 0x40, 0x01, 0x0c, 0x01, 0x00, 0x00, 0x00, 0x01, 0x44, 0x01, 0xc0, 0x73, ];
assert!(extract_hevc_config(&data).is_none());
}
#[test]
fn test_extract_hevc_config_missing_pps() {
let data = [
0x00, 0x00, 0x00, 0x01, 0x40, 0x01, 0x0c, 0x01, 0x00, 0x00, 0x00, 0x01, 0x42, 0x01, 0x01, 0x21, ];
assert!(extract_hevc_config(&data).is_none());
}
#[test]
fn test_hevc_annexb_to_hvcc() {
let annexb = [
0x00, 0x00, 0x00, 0x01, 0x40, 0x01, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x42, 0x01, ];
let hvcc = hevc_annexb_to_hvcc(&annexb);
assert_eq!(&hvcc[0..4], &[0x00, 0x00, 0x00, 0x03]);
assert_eq!(&hvcc[4..7], &[0x40, 0x01, 0x0c]);
assert_eq!(&hvcc[7..11], &[0x00, 0x00, 0x00, 0x02]);
assert_eq!(&hvcc[11..13], &[0x42, 0x01]);
}
#[test]
fn test_is_hevc_keyframe() {
let idr = [
0x00, 0x00, 0x00, 0x01, 0x40, 0x01, 0x00, 0x00, 0x00, 0x01, 0x26, 0x01, ];
assert!(is_hevc_keyframe(&idr));
let trail = [
0x00, 0x00, 0x00, 0x01, 0x02, 0x01, ];
assert!(!is_hevc_keyframe(&trail));
}
#[test]
fn test_hevc_config_accessors() {
let config = HevcConfig::new(
vec![0x40, 0x01], vec![
0x42, 0x01, 0x01, 0x21, 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03,
0x00, 0x5d,
], vec![0x44, 0x01], );
assert_eq!(config.general_profile_space(), 0);
assert!(config.general_tier_flag());
assert_eq!(config.general_profile_idc(), 1);
let level = config.general_level_idc();
assert!(level <= 186); }
}