use super::hdr_wcg_idc::HdrWcgIdc;
use crate::error::Result;
use crate::h222_bits::{BitReader, BitWriter, impl_h222_descriptor};
use alloc::vec::Vec;
pub const TAG: u8 = 0x39;
const RESERVED_5_DEFAULT: u8 = 0x1F;
const RESERVED_2_DEFAULT: u8 = 0x03;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub struct TemporalIdRange {
pub reserved_min: u8,
pub temporal_id_min: u8,
pub reserved_max: u8,
pub temporal_id_max: u8,
}
impl TemporalIdRange {
#[must_use]
pub const fn new(temporal_id_min: u8, temporal_id_max: u8) -> Self {
Self {
reserved_min: RESERVED_5_DEFAULT,
temporal_id_min,
reserved_max: RESERVED_5_DEFAULT,
temporal_id_max,
}
}
pub(crate) fn read(r: &mut BitReader<'_>) -> Result<Self> {
Ok(Self {
reserved_min: r.u8(5)?,
temporal_id_min: r.u8(3)?,
reserved_max: r.u8(5)?,
temporal_id_max: r.u8(3)?,
})
}
pub(crate) fn write(&self, w: &mut BitWriter) -> Result<()> {
w.put(u64::from(self.reserved_min), 5, "reserved")?;
w.put(u64::from(self.temporal_id_min), 3, "temporal_id_min")?;
w.put(u64::from(self.reserved_max), 5, "reserved")?;
w.put(u64::from(self.temporal_id_max), 3, "temporal_id_max")
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[cfg_attr(feature = "yoke", derive(yoke::Yokeable))]
#[non_exhaustive]
pub struct VvcVideoDescriptor {
pub profile_idc: u8,
pub tier_flag: bool,
pub sub_profile_idcs: Vec<u32>,
pub progressive_source_flag: bool,
pub interlaced_source_flag: bool,
pub non_packed_constraint_flag: bool,
pub frame_only_constraint_flag: bool,
pub reserved_zero_4bits: u8,
pub level_idc: u8,
pub still_present_flag: bool,
pub picture_24hr_present_flag: bool,
pub reserved_5: u8,
pub hdr_wcg_idc: HdrWcgIdc,
pub reserved_2: u8,
pub video_properties_tag: u8,
pub temporal_layer_subset: Option<TemporalIdRange>,
pub trailing: Vec<u8>,
}
impl VvcVideoDescriptor {
#[must_use]
pub fn new(
profile_idc: u8,
tier_flag: bool,
level_idc: u8,
hdr_wcg_idc: HdrWcgIdc,
video_properties_tag: u8,
) -> Self {
Self {
profile_idc,
tier_flag,
sub_profile_idcs: Vec::new(),
progressive_source_flag: false,
interlaced_source_flag: false,
non_packed_constraint_flag: false,
frame_only_constraint_flag: false,
reserved_zero_4bits: 0,
level_idc,
still_present_flag: false,
picture_24hr_present_flag: false,
reserved_5: RESERVED_5_DEFAULT,
hdr_wcg_idc,
reserved_2: RESERVED_2_DEFAULT,
video_properties_tag,
temporal_layer_subset: None,
trailing: Vec::new(),
}
}
pub(crate) fn parse_body(body: &[u8]) -> Result<Self> {
let mut r = BitReader::new(body, "VvcVideoDescriptor");
let profile_idc = r.u8(7)?;
let tier_flag = r.flag()?;
let n = r.bits(8)?;
let mut sub_profile_idcs = Vec::new();
for _ in 0..n {
sub_profile_idcs.push(r.bits(32)?);
}
let progressive_source_flag = r.flag()?;
let interlaced_source_flag = r.flag()?;
let non_packed_constraint_flag = r.flag()?;
let frame_only_constraint_flag = r.flag()?;
let reserved_zero_4bits = r.u8(4)?;
let level_idc = r.u8(8)?;
let subset = r.flag()?;
let still_present_flag = r.flag()?;
let picture_24hr_present_flag = r.flag()?;
let reserved_5 = r.u8(5)?;
let hdr_wcg_idc = HdrWcgIdc::from_u8(r.u8(2)?);
let reserved_2 = r.u8(2)?;
let video_properties_tag = r.u8(4)?;
let temporal_layer_subset = if subset {
Some(TemporalIdRange::read(&mut r)?)
} else {
None
};
Ok(Self {
profile_idc,
tier_flag,
sub_profile_idcs,
progressive_source_flag,
interlaced_source_flag,
non_packed_constraint_flag,
frame_only_constraint_flag,
reserved_zero_4bits,
level_idc,
still_present_flag,
picture_24hr_present_flag,
reserved_5,
hdr_wcg_idc,
reserved_2,
video_properties_tag,
temporal_layer_subset,
trailing: r.rest()?.to_vec(),
})
}
pub(crate) fn write_body(&self) -> Result<Vec<u8>> {
let mut w = BitWriter::new();
w.put(u64::from(self.profile_idc), 7, "profile_idc")?;
w.flag(self.tier_flag, "tier_flag")?;
w.put(self.sub_profile_idcs.len() as u64, 8, "num_sub_profiles")?;
for s in &self.sub_profile_idcs {
w.put(u64::from(*s), 32, "sub_profile_idc")?;
}
w.flag(self.progressive_source_flag, "progressive_source_flag")?;
w.flag(self.interlaced_source_flag, "interlaced_source_flag")?;
w.flag(
self.non_packed_constraint_flag,
"non_packed_constraint_flag",
)?;
w.flag(
self.frame_only_constraint_flag,
"frame_only_constraint_flag",
)?;
w.put(
u64::from(self.reserved_zero_4bits),
4,
"reserved_zero_4bits",
)?;
w.put(u64::from(self.level_idc), 8, "level_idc")?;
w.flag(
self.temporal_layer_subset.is_some(),
"temporal_layer_subset_flag",
)?;
w.flag(self.still_present_flag, "VVC_still_present_flag")?;
w.flag(
self.picture_24hr_present_flag,
"VVC_24hr_picture_present_flag",
)?;
w.put(u64::from(self.reserved_5), 5, "reserved")?;
w.put(u64::from(self.hdr_wcg_idc.to_u8()), 2, "HDR_WCG_idc")?;
w.put(u64::from(self.reserved_2), 2, "reserved")?;
w.put(
u64::from(self.video_properties_tag),
4,
"video_properties_tag",
)?;
if let Some(t) = &self.temporal_layer_subset {
t.write(&mut w)?;
}
w.bytes(&self.trailing)?;
Ok(w.into_vec())
}
pub const RESERVED_5_DEFAULT: u8 = RESERVED_5_DEFAULT;
pub const RESERVED_2_DEFAULT: u8 = RESERVED_2_DEFAULT;
}
impl_h222_descriptor!(VvcVideoDescriptor, TAG, "VvcVideoDescriptor", "VVC_VIDEO");
#[cfg(test)]
#[allow(clippy::unusual_byte_groupings)] mod tests {
use super::*;
use broadcast_common::{Parse, Serialize};
#[test]
fn spec_vector_with_subprofile_and_temporal_subset() {
let b = [
TAG,
12,
0b0000001_0,
1,
0,
0,
0,
2,
0b1001_0000,
0x33,
0b1_0_0_11111,
0b10_11_0010,
0b11111_001,
0b11111_011,
];
let d = VvcVideoDescriptor::parse(&b).unwrap();
assert_eq!(d.profile_idc, 1);
assert_eq!(d.sub_profile_idcs, [2]);
assert!(d.progressive_source_flag && d.frame_only_constraint_flag);
assert_eq!(d.hdr_wcg_idc, HdrWcgIdc::HdrAndWcg);
assert_eq!(d.video_properties_tag, 2);
assert_eq!(d.temporal_layer_subset, Some(TemporalIdRange::new(1, 3)));
let mut out = vec![0u8; d.serialized_len()];
d.serialize_into(&mut out).unwrap();
assert_eq!(out, &b[..]);
}
#[test]
fn without_subset_keeps_nonzero_reserved_zero_bits() {
let body = [
0b0000010_1,
0,
0b0000_1010,
0x40,
0b0_1_1_00000,
0b11_00_0000,
];
let mut full = vec![TAG, body.len() as u8];
full.extend_from_slice(&body);
let d = VvcVideoDescriptor::parse(&full).unwrap();
assert!(d.tier_flag);
assert_eq!(d.reserved_zero_4bits, 0b1010);
assert!(d.temporal_layer_subset.is_none());
let mut out = vec![0u8; d.serialized_len()];
d.serialize_into(&mut out).unwrap();
assert_eq!(out, full);
let mut bad = d.clone();
bad.profile_idc = 0x80;
assert!(matches!(
&(bad.serialize_into(&mut [0u8; 32])),
Err(crate::error::Error::FieldOverflow(_))
));
assert!(matches!(
&(VvcVideoDescriptor::parse(&full[..full.len() - 1])),
Err(crate::error::Error::BufferTooShort(..))
));
}
}