use crate::error::Result;
use crate::h222_bits::{BitReader, BitWriter, invalid};
use alloc::vec::Vec;
const TAG: u8 = super::mpeg2_extension::TAG;
const COPIED_BITS: usize = 44;
const RES1: u8 = 0x01;
const RES2: u8 = 0x03;
const RES4: u8 = 0x0F;
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub struct ProfileTierLevelInfo {
pub general_profile_space: u8,
pub general_tier_flag: bool,
pub general_profile_idc: u8,
pub general_profile_compatibility_flags: u32,
pub general_progressive_source_flag: bool,
pub general_interlaced_source_flag: bool,
pub general_non_packed_constraint_flag: bool,
pub general_frame_only_constraint_flag: bool,
pub copied_44bits: u64,
pub general_level_idc: u8,
}
impl ProfileTierLevelInfo {
#[must_use]
pub const fn new(
general_profile_space: u8,
general_tier_flag: bool,
general_profile_idc: u8,
general_profile_compatibility_flags: u32,
general_level_idc: u8,
) -> Self {
Self {
general_profile_space,
general_tier_flag,
general_profile_idc,
general_profile_compatibility_flags,
general_progressive_source_flag: false,
general_interlaced_source_flag: false,
general_non_packed_constraint_flag: false,
general_frame_only_constraint_flag: false,
copied_44bits: 0,
general_level_idc,
}
}
fn parse(r: &mut BitReader<'_>) -> Result<Self> {
let general_profile_space = r.u8(2)?;
let general_tier_flag = r.flag()?;
let general_profile_idc = r.u8(5)?;
let general_profile_compatibility_flags = r.bits(32)?;
let general_progressive_source_flag = r.flag()?;
let general_interlaced_source_flag = r.flag()?;
let general_non_packed_constraint_flag = r.flag()?;
let general_frame_only_constraint_flag = r.flag()?;
let hi = u64::from(r.bits(COPIED_BITS - 32)?);
let lo = u64::from(r.bits(32)?);
let copied_44bits = (hi << 32) | lo;
let general_level_idc = r.u8(8)?;
Ok(Self {
general_profile_space,
general_tier_flag,
general_profile_idc,
general_profile_compatibility_flags,
general_progressive_source_flag,
general_interlaced_source_flag,
general_non_packed_constraint_flag,
general_frame_only_constraint_flag,
copied_44bits,
general_level_idc,
})
}
fn write(&self, w: &mut BitWriter) -> Result<()> {
w.put(
u64::from(self.general_profile_space),
2,
"general_profile_space",
)?;
w.flag(self.general_tier_flag, "general_tier_flag")?;
w.put(
u64::from(self.general_profile_idc),
5,
"general_profile_idc",
)?;
w.put(
u64::from(self.general_profile_compatibility_flags),
32,
"general_profile_compatibility_flag",
)?;
w.flag(
self.general_progressive_source_flag,
"general_progressive_source_flag",
)?;
w.flag(
self.general_interlaced_source_flag,
"general_interlaced_source_flag",
)?;
w.flag(
self.general_non_packed_constraint_flag,
"general_non_packed_constraint_flag",
)?;
w.flag(
self.general_frame_only_constraint_flag,
"general_frame_only_constraint_flag",
)?;
w.put(self.copied_44bits, COPIED_BITS, "copied_44bits")?;
w.put(u64::from(self.general_level_idc), 8, "general_level_idc")
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub struct HevcOpEs {
pub reserved: u8,
pub prepend_dependencies: bool,
pub es_reference: u8,
}
impl HevcOpEs {
#[must_use]
pub const fn new(prepend_dependencies: bool, es_reference: u8) -> Self {
Self {
reserved: RES1,
prepend_dependencies,
es_reference,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub struct HevcOpLayer {
pub necessary_layer_flag: bool,
pub output_layer_flag: bool,
pub ptl_ref_idx: u8,
}
impl HevcOpLayer {
#[must_use]
pub const fn new(necessary_layer_flag: bool, output_layer_flag: bool, ptl_ref_idx: u8) -> Self {
Self {
necessary_layer_flag,
output_layer_flag,
ptl_ref_idx,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub struct HevcOpFrameRate {
pub constant_frame_rate_info_idc: u8,
pub reserved: u8,
pub frame_rate_indicator: u16,
}
impl HevcOpFrameRate {
#[must_use]
pub const fn new(constant_frame_rate_info_idc: u8, frame_rate_indicator: u16) -> Self {
Self {
constant_frame_rate_info_idc,
reserved: RES4,
frame_rate_indicator,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub struct HevcOperationPointEntry {
pub target_ols: u8,
pub es: Vec<HevcOpEs>,
pub reserved_before_layers: u8,
pub layers: Vec<HevcOpLayer>,
pub reserved_before_flags: u8,
pub applicable_temporal_id: u8,
pub frame_rate: Option<HevcOpFrameRate>,
pub avg_bit_rate: Option<u32>,
pub max_bit_rate: Option<u32>,
}
impl HevcOperationPointEntry {
#[must_use]
pub fn new(
target_ols: u8,
es: Vec<HevcOpEs>,
layers: Vec<HevcOpLayer>,
applicable_temporal_id: u8,
) -> Self {
Self {
target_ols,
es,
reserved_before_layers: RES2,
layers,
reserved_before_flags: RES1,
applicable_temporal_id,
frame_rate: None,
avg_bit_rate: None,
max_bit_rate: None,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub struct HevcOperationPoint {
pub reserved: u8,
pub profile_tier_levels: Vec<ProfileTierLevelInfo>,
pub operation_points: Vec<HevcOperationPointEntry>,
pub trailing: Vec<u8>,
}
impl HevcOperationPoint {
#[must_use]
pub fn new(
profile_tier_levels: Vec<ProfileTierLevelInfo>,
operation_points: Vec<HevcOperationPointEntry>,
) -> Self {
Self {
reserved: RES2,
profile_tier_levels,
operation_points,
trailing: Vec::new(),
}
}
pub(crate) fn parse_body(body: &[u8]) -> Result<Self> {
let mut r = BitReader::new(body, "HevcOperationPoint");
let reserved = r.u8(2)?;
let num_ptl = r.bits(6)?;
let mut profile_tier_levels = Vec::new();
for _ in 0..num_ptl {
profile_tier_levels.push(ProfileTierLevelInfo::parse(&mut r)?);
}
let count = r.bits(8)?;
let mut operation_points = Vec::new();
for _ in 0..count {
let target_ols = r.u8(8)?;
let es_count = r.bits(8)?;
let mut es = Vec::new();
for _ in 0..es_count {
es.push(HevcOpEs {
reserved: r.u8(1)?,
prepend_dependencies: r.flag()?,
es_reference: r.u8(6)?,
});
}
let reserved_before_layers = r.u8(2)?;
let num_es_in_op = r.bits(6)?;
let mut layers = Vec::new();
for _ in 0..num_es_in_op {
layers.push(HevcOpLayer {
necessary_layer_flag: r.flag()?,
output_layer_flag: r.flag()?,
ptl_ref_idx: r.u8(6)?,
});
}
let reserved_before_flags = r.u8(1)?;
let avg_flag = r.flag()?;
let max_flag = r.flag()?;
let idc = r.u8(2)?;
let applicable_temporal_id = r.u8(3)?;
let frame_rate = if idc > 0 {
Some(HevcOpFrameRate {
constant_frame_rate_info_idc: idc,
reserved: r.u8(4)?,
frame_rate_indicator: r.u16(12)?,
})
} else {
None
};
let avg_bit_rate = if avg_flag { Some(r.bits(24)?) } else { None };
let max_bit_rate = if max_flag { Some(r.bits(24)?) } else { None };
operation_points.push(HevcOperationPointEntry {
target_ols,
es,
reserved_before_layers,
layers,
reserved_before_flags,
applicable_temporal_id,
frame_rate,
avg_bit_rate,
max_bit_rate,
});
}
Ok(Self {
reserved,
profile_tier_levels,
operation_points,
trailing: r.rest()?.to_vec(),
})
}
pub(crate) fn write_body(&self) -> Result<Vec<u8>> {
let mut w = BitWriter::new();
w.put(u64::from(self.reserved), 2, "reserved")?;
w.put(self.profile_tier_levels.len() as u64, 6, "num_ptl")?;
for ptl in &self.profile_tier_levels {
ptl.write(&mut w)?;
}
w.put(
self.operation_points.len() as u64,
8,
"operation_points_count",
)?;
for op in &self.operation_points {
w.put(u64::from(op.target_ols), 8, "target_ols")?;
w.put(op.es.len() as u64, 8, "ES_count")?;
for e in &op.es {
w.put(u64::from(e.reserved), 1, "reserved")?;
w.flag(e.prepend_dependencies, "prepend_dependencies")?;
w.put(u64::from(e.es_reference), 6, "ES_reference")?;
}
w.put(u64::from(op.reserved_before_layers), 2, "reserved")?;
w.put(op.layers.len() as u64, 6, "numEsInOp")?;
for l in &op.layers {
w.flag(l.necessary_layer_flag, "necessary_layer_flag")?;
w.flag(l.output_layer_flag, "output_layer_flag")?;
w.put(u64::from(l.ptl_ref_idx), 6, "ptl_ref_idx")?;
}
w.put(u64::from(op.reserved_before_flags), 1, "reserved")?;
w.flag(op.avg_bit_rate.is_some(), "avg_bit_rate_info_flag")?;
w.flag(op.max_bit_rate.is_some(), "max_bit_rate_info_flag")?;
let idc = match &op.frame_rate {
None => 0,
Some(f) if (1..=3).contains(&f.constant_frame_rate_info_idc) => {
f.constant_frame_rate_info_idc
}
Some(_) => {
return Err(invalid(
TAG,
"HEVC_operation_point_descriptor constant_frame_rate_info_idc must be 1..=3 when a frame rate is present",
));
}
};
w.put(u64::from(idc), 2, "constant_frame_rate_info_idc")?;
w.put(
u64::from(op.applicable_temporal_id),
3,
"applicable_temporal_id",
)?;
if let Some(f) = &op.frame_rate {
w.put(u64::from(f.reserved), 4, "reserved")?;
w.put(
u64::from(f.frame_rate_indicator),
12,
"frame_rate_indicator",
)?;
}
if let Some(v) = op.avg_bit_rate {
w.put(u64::from(v), 24, "avg_bit_rate")?;
}
if let Some(v) = op.max_bit_rate {
w.put(u64::from(v), 24, "max_bit_rate")?;
}
}
w.bytes(&self.trailing)?;
Ok(w.into_vec())
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub struct HevcEmbeddedLayer {
pub reserved: u8,
pub index: u8,
}
impl HevcEmbeddedLayer {
#[must_use]
pub const fn new(index: u8) -> Self {
Self {
reserved: RES2,
index,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub struct HevcHierarchyExtension {
pub extension_dimension_bits: u16,
pub hierarchy_layer_index: u8,
pub temporal_id: u8,
pub nuh_layer_id: u8,
pub tref_present_flag: bool,
pub reserved_a: u8,
pub reserved_b: u8,
pub hierarchy_channel: u8,
pub embedded_layers: Vec<HevcEmbeddedLayer>,
pub trailing: Vec<u8>,
}
impl HevcHierarchyExtension {
#[must_use]
pub fn new(
extension_dimension_bits: u16,
hierarchy_layer_index: u8,
temporal_id: u8,
nuh_layer_id: u8,
hierarchy_channel: u8,
embedded_layers: Vec<HevcEmbeddedLayer>,
) -> Self {
Self {
extension_dimension_bits,
hierarchy_layer_index,
temporal_id,
nuh_layer_id,
tref_present_flag: false,
reserved_a: RES2,
reserved_b: RES2,
hierarchy_channel,
embedded_layers,
trailing: Vec::new(),
}
}
pub(crate) fn parse_body(body: &[u8]) -> Result<Self> {
let mut r = BitReader::new(body, "HevcHierarchyExtension");
let extension_dimension_bits = r.u16(16)?;
let hierarchy_layer_index = r.u8(6)?;
let temporal_id = r.u8(3)?;
let nuh_layer_id = r.u8(6)?;
let tref_present_flag = r.flag()?;
let reserved_a = r.u8(2)?;
let num = r.bits(6)?;
let reserved_b = r.u8(2)?;
let hierarchy_channel = r.u8(6)?;
let mut embedded_layers = Vec::new();
for _ in 0..num {
embedded_layers.push(HevcEmbeddedLayer {
reserved: r.u8(2)?,
index: r.u8(6)?,
});
}
Ok(Self {
extension_dimension_bits,
hierarchy_layer_index,
temporal_id,
nuh_layer_id,
tref_present_flag,
reserved_a,
reserved_b,
hierarchy_channel,
embedded_layers,
trailing: r.rest()?.to_vec(),
})
}
pub(crate) fn write_body(&self) -> Result<Vec<u8>> {
let mut w = BitWriter::new();
w.put(
u64::from(self.extension_dimension_bits),
16,
"extension_dimension_bits",
)?;
w.put(
u64::from(self.hierarchy_layer_index),
6,
"hierarchy_layer_index",
)?;
w.put(u64::from(self.temporal_id), 3, "temporal_id")?;
w.put(u64::from(self.nuh_layer_id), 6, "nuh_layer_id")?;
w.flag(self.tref_present_flag, "tref_present_flag")?;
w.put(u64::from(self.reserved_a), 2, "reserved")?;
w.put(self.embedded_layers.len() as u64, 6, "num_embedded_layers")?;
w.put(u64::from(self.reserved_b), 2, "reserved")?;
w.put(u64::from(self.hierarchy_channel), 6, "hierarchy_channel")?;
for l in &self.embedded_layers {
w.put(u64::from(l.reserved), 2, "reserved")?;
w.put(u64::from(l.index), 6, "hierarchy_ext_embedded_layer_index")?;
}
w.bytes(&self.trailing)?;
Ok(w.into_vec())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub struct AdditionalSubstream {
pub flag: bool,
pub substream_id: u8,
}
impl AdditionalSubstream {
#[must_use]
pub const fn new(flag: bool, substream_id: u8) -> Self {
Self { flag, substream_id }
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub enum HevcTileSubstreamDetail {
Single {
reserved_reference_flag: bool,
},
Pattern {
preamble_flag: bool,
pattern_reference: u8,
trailing: Vec<u8>,
},
Additional(Vec<AdditionalSubstream>),
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub struct HevcTileSubstream {
pub substream_id: u8,
pub detail: HevcTileSubstreamDetail,
}
impl HevcTileSubstream {
#[must_use]
pub fn new(substream_id: u8, detail: HevcTileSubstreamDetail) -> Self {
Self {
substream_id,
detail,
}
}
pub(crate) fn parse_body(body: &[u8]) -> Result<Self> {
let mut r = BitReader::new(body, "HevcTileSubstream");
let reference_flag = r.flag()?;
let substream_id = r.u8(7)?;
let detail = if r.remaining_bytes() == 0 {
HevcTileSubstreamDetail::Single {
reserved_reference_flag: reference_flag,
}
} else if reference_flag {
HevcTileSubstreamDetail::Pattern {
preamble_flag: r.flag()?,
pattern_reference: r.u8(7)?,
trailing: r.rest()?.to_vec(),
}
} else {
let mut v = Vec::new();
while r.remaining_bytes() > 0 {
v.push(AdditionalSubstream {
flag: r.flag()?,
substream_id: r.u8(7)?,
});
}
HevcTileSubstreamDetail::Additional(v)
};
Ok(Self {
substream_id,
detail,
})
}
pub(crate) fn write_body(&self) -> Result<Vec<u8>> {
let mut w = BitWriter::new();
let reference_flag = match &self.detail {
HevcTileSubstreamDetail::Single {
reserved_reference_flag,
} => *reserved_reference_flag,
HevcTileSubstreamDetail::Pattern { .. } => true,
HevcTileSubstreamDetail::Additional(v) => {
if v.is_empty() {
return Err(invalid(
TAG,
"HEVC_tile_substream_descriptor Additional list must not be empty (a one-byte body is the Single form)",
));
}
false
}
};
w.flag(reference_flag, "ReferenceFlag")?;
w.put(u64::from(self.substream_id), 7, "SubstreamID")?;
match &self.detail {
HevcTileSubstreamDetail::Single { .. } => {}
HevcTileSubstreamDetail::Pattern {
preamble_flag,
pattern_reference,
trailing,
} => {
w.flag(*preamble_flag, "PreambleFlag")?;
w.put(u64::from(*pattern_reference), 7, "PatternReference")?;
w.bytes(trailing)?;
}
HevcTileSubstreamDetail::Additional(v) => {
for a in v {
w.flag(a.flag, "Flag")?;
w.put(u64::from(a.substream_id), 7, "AdditionalSubstreamID")?;
}
}
}
Ok(w.into_vec())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub struct HevcSubregionPreamble {
pub reserved: bool,
pub preamble_substream_id: u8,
}
impl HevcSubregionPreamble {
#[must_use]
pub const fn new(preamble_substream_id: u8) -> Self {
Self {
reserved: true,
preamble_substream_id,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub struct HevcSubregionLayout {
pub preamble: Option<HevcSubregionPreamble>,
pub substream_count_minus_1: u8,
pub level: u8,
pub picture_size_hor: u16,
pub picture_size_vert: u16,
pub reserved: bool,
pub patterns: Vec<Vec<i8>>,
}
impl HevcSubregionLayout {
#[must_use]
pub fn new(
preamble: Option<HevcSubregionPreamble>,
substream_count_minus_1: u8,
level: u8,
picture_size_hor: u16,
picture_size_vert: u16,
patterns: Vec<Vec<i8>>,
) -> Self {
Self {
preamble,
substream_count_minus_1,
level,
picture_size_hor,
picture_size_vert,
reserved: true,
patterns,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub struct HevcSubregion {
pub substream_marking_flag: bool,
pub substream_ids_per_line: u8,
pub total_substream_ids: u8,
pub level_full_panorama: u8,
pub layouts: Vec<HevcSubregionLayout>,
}
impl HevcSubregion {
#[must_use]
pub fn new(
substream_marking_flag: bool,
substream_ids_per_line: u8,
total_substream_ids: u8,
level_full_panorama: u8,
layouts: Vec<HevcSubregionLayout>,
) -> Self {
Self {
substream_marking_flag,
substream_ids_per_line,
total_substream_ids,
level_full_panorama,
layouts,
}
}
pub(crate) fn parse_body(body: &[u8]) -> Result<Self> {
let mut r = BitReader::new(body, "HevcSubregion");
let substream_marking_flag = r.flag()?;
let substream_ids_per_line = r.u8(7)?;
let total_substream_ids = r.u8(8)?;
let level_full_panorama = r.u8(8)?;
let mut layouts = Vec::new();
while r.remaining_bytes() > 0 {
let preamble = if substream_marking_flag {
Some(HevcSubregionPreamble {
reserved: r.flag()?,
preamble_substream_id: r.u8(7)?,
})
} else {
None
};
let substream_count_minus_1 = r.u8(8)?;
let level = r.u8(8)?;
let picture_size_hor = r.u16(16)?;
let picture_size_vert = r.u16(16)?;
let reserved = r.flag()?;
let pattern_count = r.u8(7)?;
let mut patterns = Vec::new();
for _ in 0..pattern_count {
let mut offsets = Vec::new();
for _ in 0..substream_count_minus_1 {
offsets.push(i8::from_be_bytes([r.u8(8)?]));
}
patterns.push(offsets);
}
layouts.push(HevcSubregionLayout {
preamble,
substream_count_minus_1,
level,
picture_size_hor,
picture_size_vert,
reserved,
patterns,
});
}
Ok(Self {
substream_marking_flag,
substream_ids_per_line,
total_substream_ids,
level_full_panorama,
layouts,
})
}
pub(crate) fn write_body(&self) -> Result<Vec<u8>> {
let mut w = BitWriter::new();
w.flag(self.substream_marking_flag, "SubstreamMarkingFlag")?;
w.put(
u64::from(self.substream_ids_per_line),
7,
"SubstreamIDsPerLine",
)?;
w.put(u64::from(self.total_substream_ids), 8, "TotalSubstreamIDs")?;
w.put(u64::from(self.level_full_panorama), 8, "LevelFullPanorama")?;
for l in &self.layouts {
match (self.substream_marking_flag, &l.preamble) {
(true, Some(p)) => {
w.flag(p.reserved, "reserved")?;
w.put(u64::from(p.preamble_substream_id), 7, "PreambleSubstreamID")?;
}
(false, None) => {}
_ => {
return Err(invalid(
TAG,
"HEVC_subregion_descriptor PreambleSubstreamID must be present exactly when SubstreamMarkingFlag is 1",
));
}
}
w.put(
u64::from(l.substream_count_minus_1),
8,
"SubstreamCountMinus1",
)?;
w.put(u64::from(l.level), 8, "Level")?;
w.put(u64::from(l.picture_size_hor), 16, "PictureSizeHor")?;
w.put(u64::from(l.picture_size_vert), 16, "PictureSizeVert")?;
w.flag(l.reserved, "reserved")?;
w.put(l.patterns.len() as u64, 7, "PatternCount")?;
for pattern in &l.patterns {
if pattern.len() != usize::from(l.substream_count_minus_1) {
return Err(invalid(
TAG,
"HEVC_subregion_descriptor pattern length differs from SubstreamCountMinus1",
));
}
for o in pattern {
w.put(u64::from(o.to_be_bytes()[0]), 8, "SubstreamOffset")?;
}
}
}
Ok(w.into_vec())
}
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::vec;
#[test]
fn subregion_marked_two_layouts() {
let b = [
0x84, 0x10, 0x5A, 0x91, 0x02, 0x3C, 0x07, 0x80, 0x04, 0x38, 0x82, 0x01, 0xFF, 0x02,
0xFE, 0xFF, 0x00, 0x1E, 0x05, 0x00, 0x02, 0xD0, 0x81,
];
let d = HevcSubregion::parse_body(&b).unwrap();
assert!(d.substream_marking_flag);
assert_eq!(
(
d.substream_ids_per_line,
d.total_substream_ids,
d.level_full_panorama
),
(4, 0x10, 0x5A)
);
assert_eq!(d.layouts.len(), 2);
let l0 = &d.layouts[0];
assert_eq!(l0.preamble.unwrap().preamble_substream_id, 0x11);
assert_eq!((l0.substream_count_minus_1, l0.level), (2, 0x3C));
assert_eq!((l0.picture_size_hor, l0.picture_size_vert), (1920, 1080));
assert_eq!(l0.patterns, [vec![1i8, -1], vec![2, -2]]);
let l1 = &d.layouts[1];
assert_eq!(l1.preamble.unwrap().preamble_substream_id, 0x7F);
assert_eq!(l1.patterns, [Vec::<i8>::new()]);
assert_eq!(d.write_body().unwrap(), b);
}
#[test]
fn subregion_unmarked_has_no_preamble() {
let b = [
0x04, 0x10, 0x5A, 0x01, 0x3C, 0x07, 0x80, 0x04, 0x38, 0x81, 0xFB,
];
let d = HevcSubregion::parse_body(&b).unwrap();
assert!(!d.substream_marking_flag);
assert!(d.layouts[0].preamble.is_none());
assert_eq!(d.layouts[0].patterns, [vec![-5i8]]);
assert_eq!(d.write_body().unwrap(), b);
let bare = HevcSubregion::parse_body(&[0x04, 0x10, 0x5A]).unwrap();
assert!(bare.layouts.is_empty());
assert_eq!(bare.write_body().unwrap(), [0x04, 0x10, 0x5A]);
}
#[test]
fn subregion_rejects_inconsistent_values() {
assert!(matches!(
&(HevcSubregion::parse_body(&[0x04, 0x10, 0x5A, 0x01, 0x3C])),
Err(crate::error::Error::BufferTooShort(..))
));
let mut d = HevcSubregion::parse_body(&[
0x04, 0x10, 0x5A, 0x01, 0x3C, 0x07, 0x80, 0x04, 0x38, 0x81, 0xFB,
])
.unwrap();
d.layouts[0].patterns[0].push(1);
assert!(matches!(
&(d.write_body()),
Err(crate::error::Error::InvalidDescriptor { .. })
));
d.layouts[0].patterns[0].pop();
d.layouts[0].preamble = Some(HevcSubregionPreamble::new(0x11));
assert!(matches!(
d.write_body(),
Err(crate::Error::InvalidDescriptor { .. })
));
d.layouts[0].preamble = None;
d.layouts[0].substream_count_minus_1 = 0;
d.layouts[0].patterns = vec![Vec::new(); 128];
assert!(matches!(
&(d.write_body()),
Err(crate::error::Error::FieldOverflow(_))
));
}
#[test]
fn operation_point_spec_vector() {
let ptl = ProfileTierLevelInfo::new(0, false, 1, 0, 0);
let mut other = ProfileTierLevelInfo::new(2, true, 5, 0xAAAA_AAAA, 0xAA);
other.general_progressive_source_flag = true;
other.general_non_packed_constraint_flag = true;
other.copied_44bits = 0xAAA_AAAA_AAAA;
let mut e = HevcOperationPointEntry::new(
2,
vec![HevcOpEs::new(true, 5), HevcOpEs::new(false, 6)],
vec![
HevcOpLayer::new(true, true, 0),
HevcOpLayer::new(true, false, 1),
],
6,
);
e.frame_rate = Some(HevcOpFrameRate::new(2, 50));
e.avg_bit_rate = Some(8000);
e.max_bit_rate = Some(10000);
let d = HevcOperationPoint::new(vec![ptl, other], vec![e]);
let b = d.write_body().unwrap();
assert_eq!(b[0], 0xC2);
assert_eq!(&b[1..13], &[0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
assert_eq!(
&b[13..25],
&[
0xA5, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA
]
);
assert_eq!(b[25], 1); assert_eq!(b[26], 2); assert_eq!(b[27], 2); assert_eq!(&b[28..30], &[0b1100_0101, 0b1000_0110]);
assert_eq!(b[30], 0b11_000010); assert_eq!(&b[31..33], &[0b11_000000, 0b10_000001]);
assert_eq!(b[33], 0b1111_0110);
assert_eq!(&b[34..36], &[0xF0, 50]);
assert_eq!(&b[36..39], &[0, 0x1F, 0x40]);
assert_eq!(&b[39..42], &[0, 0x27, 0x10]);
assert_eq!(HevcOperationPoint::parse_body(&b).unwrap(), d);
}
#[test]
fn operation_point_rejects_bad_idc_and_truncation() {
let mut e = HevcOperationPointEntry::new(0, vec![], vec![], 0);
e.frame_rate = Some(HevcOpFrameRate::new(0, 1));
assert_eq!(
HevcOperationPoint::new(vec![], vec![e]).write_body().err(),
Some(crate::error::Error::InvalidDescriptor {
tag: 63,
reason: "HEVC_operation_point_descriptor constant_frame_rate_info_idc must be 1..=3 when a frame rate is present"
})
);
assert!(matches!(
&(HevcOperationPoint::parse_body(&[0xC1, 0x00])),
Err(crate::error::Error::BufferTooShort(..))
));
}
#[test]
fn hierarchy_extension_round_trip() {
let b = [
0x00,
0x03,
0b0001_0101,
0b0000_0010,
0b11_000010,
0b11_000100,
0xC1,
0xC3,
];
let d = HevcHierarchyExtension::parse_body(&b).unwrap();
assert_eq!(d.hierarchy_layer_index, 5);
assert_eq!(d.temporal_id, 2);
assert_eq!(d.nuh_layer_id, 1);
assert!(!d.tref_present_flag);
assert_eq!(d.embedded_layers.len(), 2);
assert_eq!(d.embedded_layers[1].index, 3);
assert_eq!(d.write_body().unwrap(), b);
}
#[test]
fn tile_substream_three_forms() {
let single = HevcTileSubstream::parse_body(&[0x85]).unwrap();
assert_eq!(
single.detail,
HevcTileSubstreamDetail::Single {
reserved_reference_flag: true
}
);
assert_eq!(single.write_body().unwrap(), [0x85]);
let pat = HevcTileSubstream::parse_body(&[0x85, 0x83]).unwrap();
assert!(matches!(
pat.detail,
HevcTileSubstreamDetail::Pattern {
preamble_flag: true,
pattern_reference: 3,
..
}
));
assert_eq!(pat.write_body().unwrap(), [0x85, 0x83]);
let add = HevcTileSubstream::parse_body(&[0x05, 0x81, 0x02]).unwrap();
let HevcTileSubstreamDetail::Additional(v) = &add.detail else {
panic!()
};
assert_eq!(
v,
&[
AdditionalSubstream::new(true, 1),
AdditionalSubstream::new(false, 2)
]
);
assert_eq!(add.write_body().unwrap(), [0x05, 0x81, 0x02]);
let empty = HevcTileSubstream {
substream_id: 1,
detail: HevcTileSubstreamDetail::Additional(vec![]),
};
assert!(matches!(
&(empty.write_body()),
Err(crate::error::Error::InvalidDescriptor { .. })
));
}
const PTL_VECTOR: [u8; 12] = [
0xA5, 0x60, 0x00, 0x00, 0x00, 0xA9, 0x12, 0x34, 0x56, 0x78, 0x9A, 0x5D,
];
type PtlCase = (fn(&mut ProfileTierLevelInfo), usize, u8);
fn parse_ptl(b: &[u8]) -> Result<ProfileTierLevelInfo> {
ProfileTierLevelInfo::parse(&mut BitReader::new(b, "ptl"))
}
fn write_ptl(p: &ProfileTierLevelInfo) -> Result<Vec<u8>> {
let mut w = BitWriter::new();
p.write(&mut w)?;
Ok(w.into_vec())
}
#[test]
fn profile_tier_level_decodes_hand_computed_vector() {
let p = parse_ptl(&PTL_VECTOR).unwrap();
assert_eq!(p.general_profile_space, 2);
assert!(p.general_tier_flag);
assert_eq!(p.general_profile_idc, 5);
assert_eq!(p.general_profile_compatibility_flags, 0x6000_0000);
assert!(p.general_progressive_source_flag);
assert!(!p.general_interlaced_source_flag);
assert!(p.general_non_packed_constraint_flag);
assert!(!p.general_frame_only_constraint_flag);
assert_eq!(p.copied_44bits, 0x0912_3456_789A);
assert_eq!(p.general_level_idc, 93);
assert_eq!(write_ptl(&p).unwrap(), PTL_VECTOR);
}
#[test]
fn profile_tier_level_each_field_lands_in_its_bit() {
let base = ProfileTierLevelInfo::new(0, false, 0, 0, 0);
assert_eq!(write_ptl(&base).unwrap(), [0u8; 12]);
let cases: [PtlCase; 12] = [
(|p| p.general_profile_space = 3, 0, 0xC0),
(|p| p.general_tier_flag = true, 0, 0x20),
(|p| p.general_profile_idc = 0x1F, 0, 0x1F),
(
|p| p.general_profile_compatibility_flags = 0x8000_0000,
1,
0x80,
),
(|p| p.general_profile_compatibility_flags = 1, 4, 0x01),
(|p| p.general_progressive_source_flag = true, 5, 0x80),
(|p| p.general_interlaced_source_flag = true, 5, 0x40),
(|p| p.general_non_packed_constraint_flag = true, 5, 0x20),
(|p| p.general_frame_only_constraint_flag = true, 5, 0x10),
(|p| p.copied_44bits = 1 << 43, 5, 0x08),
(|p| p.copied_44bits = 1, 10, 0x01),
(|p| p.general_level_idc = 0x80, 11, 0x80),
];
for (set, idx, val) in cases {
let mut p = base.clone();
set(&mut p);
let mut want = [0u8; 12];
want[idx] = val;
assert_eq!(write_ptl(&p).unwrap(), want);
assert_eq!(parse_ptl(&want).unwrap(), p);
}
}
#[test]
fn profile_tier_level_rejects_out_of_range_fields() {
let mut p = ProfileTierLevelInfo::new(0, false, 0, 0, 0);
p.copied_44bits = 1 << 44;
assert!(matches!(
&(write_ptl(&p)),
Err(crate::error::Error::FieldOverflow(_))
));
assert!(matches!(
write_ptl(&ProfileTierLevelInfo::new(4, false, 0, 0, 0)),
Err(crate::Error::FieldOverflow(..))
));
assert!(matches!(
&(write_ptl(&ProfileTierLevelInfo::new(0, false, 32, 0, 0))),
Err(crate::error::Error::FieldOverflow(_))
));
assert!(matches!(
parse_ptl(&PTL_VECTOR[..11]),
Err(crate::Error::BufferTooShort(..))
));
}
}