use super::hdr_wcg_idc::HdrWcgIdc;
use super::vvc_video::TemporalIdRange;
use crate::error::Result;
use crate::h222_bits::{BitReader, BitWriter, impl_h222_descriptor};
use alloc::vec::Vec;
pub const TAG: u8 = 0x3A;
const RESERVED_1_DEFAULT: u8 = 0x01;
const RESERVED_2_DEFAULT: u8 = 0x03;
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[cfg_attr(feature = "yoke", derive(yoke::Yokeable))]
#[non_exhaustive]
pub struct EvcVideoDescriptor {
pub profile_idc: u8,
pub level_idc: u8,
pub toolset_idc_h: u32,
pub toolset_idc_l: 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_1: u8,
pub still_present_flag: bool,
pub picture_24hr_present_flag: bool,
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 EvcVideoDescriptor {
#[must_use]
pub fn new(
profile_idc: u8,
level_idc: u8,
toolset_idc_h: u32,
toolset_idc_l: u32,
hdr_wcg_idc: HdrWcgIdc,
video_properties_tag: u8,
) -> Self {
Self {
profile_idc,
level_idc,
toolset_idc_h,
toolset_idc_l,
progressive_source_flag: false,
interlaced_source_flag: false,
non_packed_constraint_flag: false,
frame_only_constraint_flag: false,
reserved_1: RESERVED_1_DEFAULT,
still_present_flag: false,
picture_24hr_present_flag: false,
hdr_wcg_idc,
reserved_2: RESERVED_2_DEFAULT,
video_properties_tag,
temporal_layer_subset: None,
trailing: Vec::new(),
}
}
pub const RESERVED_1_DEFAULT: u8 = RESERVED_1_DEFAULT;
pub const RESERVED_2_DEFAULT: u8 = RESERVED_2_DEFAULT;
fn parse_body(body: &[u8]) -> Result<Self> {
let mut r = BitReader::new(body, "EvcVideoDescriptor");
let profile_idc = r.u8(8)?;
let level_idc = r.u8(8)?;
let toolset_idc_h = r.bits(32)?;
let toolset_idc_l = 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_1 = r.u8(1)?;
let subset = r.flag()?;
let still_present_flag = r.flag()?;
let picture_24hr_present_flag = r.flag()?;
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,
level_idc,
toolset_idc_h,
toolset_idc_l,
progressive_source_flag,
interlaced_source_flag,
non_packed_constraint_flag,
frame_only_constraint_flag,
reserved_1,
still_present_flag,
picture_24hr_present_flag,
hdr_wcg_idc,
reserved_2,
video_properties_tag,
temporal_layer_subset,
trailing: r.rest()?.to_vec(),
})
}
fn write_body(&self) -> Result<Vec<u8>> {
let mut w = BitWriter::new();
w.put(u64::from(self.profile_idc), 8, "profile_idc")?;
w.put(u64::from(self.level_idc), 8, "level_idc")?;
w.put(u64::from(self.toolset_idc_h), 32, "toolset_idc_h")?;
w.put(u64::from(self.toolset_idc_l), 32, "toolset_idc_l")?;
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_1), 1, "reserved")?;
w.flag(
self.temporal_layer_subset.is_some(),
"temporal_layer_subset_flag",
)?;
w.flag(self.still_present_flag, "EVC_still_present_flag")?;
w.flag(
self.picture_24hr_present_flag,
"EVC_24hr_picture_present_flag",
)?;
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())
}
}
impl_h222_descriptor!(EvcVideoDescriptor, TAG, "EvcVideoDescriptor", "EVC_VIDEO");
#[cfg(test)]
#[allow(clippy::unusual_byte_groupings)] mod tests {
use super::*;
use broadcast_common::{Parse, Serialize};
#[test]
fn spec_vector_round_trip() {
let body = [
0,
0x33,
0,
0,
0,
1,
0,
0,
0,
2,
0b1001_1_1_0_1,
0b01_11_0001,
0b11111_000,
0b11111_010,
];
let mut b = vec![TAG, body.len() as u8];
b.extend_from_slice(&body);
let d = EvcVideoDescriptor::parse(&b).unwrap();
assert_eq!((d.toolset_idc_h, d.toolset_idc_l), (1, 2));
assert!(d.progressive_source_flag && d.frame_only_constraint_flag);
assert_eq!(d.hdr_wcg_idc, HdrWcgIdc::WcgOnly);
assert_eq!(d.temporal_layer_subset, Some(TemporalIdRange::new(0, 2)));
let mut out = vec![0u8; d.serialized_len()];
d.serialize_into(&mut out).unwrap();
assert_eq!(out, b);
assert!(EvcVideoDescriptor::parse(&b[..b.len() - 1]).is_err());
let mut bad = d.clone();
bad.video_properties_tag = 16;
assert!(bad.serialize_into(&mut [0u8; 32]).is_err());
}
}