use std::io::{Cursor, Read};
use media_codec_bitstream::{BigEndian, ByteRead, ByteReader};
use media_core::{invalid_data_error, Result};
use crate::NalUnitType;
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct HvccNaluArray {
pub array_completeness: bool,
pub nal_unit_type: u8,
pub nalus: Vec<Vec<u8>>,
}
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct Hvcc {
pub configuration_version: u8,
pub general_profile_space: u8,
pub general_tier_flag: bool,
pub general_profile_idc: u8,
pub general_profile_compatibility_flags: u32,
pub general_constraint_indicator_flags: u64,
pub general_level_idc: u8,
pub min_spatial_segmentation_idc: u16,
pub parallelism_type: u8,
pub chroma_format_idc: u8,
pub bit_depth_luma: u8,
pub bit_depth_chroma: u8,
pub avg_frame_rate: u16,
pub constant_frame_rate: u8,
pub num_temporal_layers: u8,
pub temporal_id_nested: bool,
pub length_size: u8,
pub nalu_arrays: Vec<HvccNaluArray>,
}
impl Hvcc {
pub fn parse(data: &[u8]) -> Result<Self> {
let cursor = Cursor::new(data);
Self::parse_from_reader(cursor)
}
pub fn parse_from_reader<R: Read>(reader: R) -> Result<Self> {
let mut reader = ByteReader::endian(reader, BigEndian);
Self::parse_from_byte_reader(&mut reader)
}
pub fn parse_from_byte_reader<R: Read>(reader: &mut ByteReader<R, BigEndian>) -> Result<Self> {
let configuration_version = reader.read::<u8>()?;
if configuration_version != 1 {
return Err(invalid_data_error!("configuration_version", configuration_version));
}
let profile_byte = reader.read::<u8>()?;
let general_profile_space = (profile_byte >> 6) & 0b11;
let general_tier_flag = (profile_byte >> 5) & 0b1 != 0;
let general_profile_idc = profile_byte & 0b0001_1111;
let general_profile_compatibility_flags = reader.read::<u32>()?;
let constraint_high = reader.read::<u32>()? as u64;
let constraint_low = reader.read::<u16>()? as u64;
let general_constraint_indicator_flags = (constraint_high << 16) | constraint_low;
let general_level_idc = reader.read::<u8>()?;
let min_spatial_segmentation_idc = reader.read::<u16>()? & 0x0FFF;
let parallelism_type = reader.read::<u8>()? & 0b11;
let chroma_format_idc = reader.read::<u8>()? & 0b11;
let bit_depth_luma = (reader.read::<u8>()? & 0b111) + 8;
let bit_depth_chroma = (reader.read::<u8>()? & 0b111) + 8;
let avg_frame_rate = reader.read::<u16>()?;
let rate_byte = reader.read::<u8>()?;
let constant_frame_rate = (rate_byte >> 6) & 0b11;
let num_temporal_layers = (rate_byte >> 3) & 0b111;
let temporal_id_nested = (rate_byte >> 2) & 0b1 != 0;
let length_size = (rate_byte & 0b11) + 1;
if length_size != 1 && length_size != 2 && length_size != 4 {
return Err(invalid_data_error!("NAL unit length size", length_size));
}
let num_of_arrays = reader.read::<u8>()?;
let mut nalu_arrays = Vec::with_capacity(num_of_arrays as usize);
for _ in 0..num_of_arrays {
let array_header = reader.read::<u8>()?;
let array_completeness = (array_header >> 7) & 0b1 != 0;
let nal_unit_type = array_header & 0b0011_1111;
let num_nalus = reader.read::<u16>()?;
let mut nalus = Vec::with_capacity(num_nalus as usize);
for _ in 0..num_nalus {
let nalu_length = reader.read::<u16>()? as usize;
let mut nalu_data = vec![0u8; nalu_length];
reader.read_bytes(&mut nalu_data)?;
nalus.push(nalu_data);
}
nalu_arrays.push(HvccNaluArray {
array_completeness,
nal_unit_type,
nalus,
});
}
Ok(Self {
configuration_version,
general_profile_space,
general_tier_flag,
general_profile_idc,
general_profile_compatibility_flags,
general_constraint_indicator_flags,
general_level_idc,
min_spatial_segmentation_idc,
parallelism_type,
chroma_format_idc,
bit_depth_luma,
bit_depth_chroma,
avg_frame_rate,
constant_frame_rate,
num_temporal_layers,
temporal_id_nested,
length_size,
nalu_arrays,
})
}
pub fn vps(&self) -> impl Iterator<Item = &[u8]> {
self.nalus(NalUnitType::VpsNut as u8)
}
pub fn sps(&self) -> impl Iterator<Item = &[u8]> {
self.nalus(NalUnitType::SpsNut as u8)
}
pub fn pps(&self) -> impl Iterator<Item = &[u8]> {
self.nalus(NalUnitType::PpsNut as u8)
}
pub fn nalus(&self, nal_unit_type: u8) -> impl Iterator<Item = &[u8]> {
self.nalu_arrays
.iter()
.filter(move |array| array.nal_unit_type == nal_unit_type)
.flat_map(|array| array.nalus.iter().map(|nalu| nalu.as_slice()))
}
}