use super::descriptor_body;
use super::hdr_wcg_idc::HdrWcgIdc;
use crate::error::{Error, Result};
use dvb_common::{Parse, Serialize};
pub const TAG: u8 = 0x38;
const HEADER_LEN: usize = 2;
const COPIED_44_MASK: u64 = (1 << 44) - 1;
const FIXED_BODY_LEN: u8 = 12;
const TEMPORAL_SUB_LEN: u8 = 2;
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct HevcTemporalSub {
pub temporal_id_min: u8,
pub temporal_id_max: u8,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[cfg_attr(feature = "yoke", derive(yoke::Yokeable))]
pub struct HevcVideoDescriptor {
pub profile_space: u8,
pub tier_flag: bool,
pub profile_idc: u8,
pub profile_compatibility_indication: u32,
pub progressive_source_flag: bool,
pub interlaced_source_flag: bool,
pub non_packed_constraint_flag: bool,
pub frame_only_constraint_flag: bool,
pub copied_44bits: u64,
pub level_idc: u8,
pub temporal_layer_subset_flag: bool,
pub hevc_still_present_flag: bool,
pub hevc_24hr_picture_present_flag: bool,
pub sub_pic_hrd_params_not_present_flag: bool,
pub hdr_wcg_idc: HdrWcgIdc,
pub temporal_sub: Option<HevcTemporalSub>,
}
impl<'a> Parse<'a> for HevcVideoDescriptor {
type Error = crate::error::Error;
fn parse(bytes: &'a [u8]) -> Result<Self> {
let body = descriptor_body(
bytes,
TAG,
"HevcVideoDescriptor",
"unexpected tag for HEVC_video_descriptor",
)?;
if body.len() < (FIXED_BODY_LEN as usize) {
return Err(Error::InvalidDescriptor {
tag: TAG,
reason: "HEVC_video_descriptor too short",
});
}
let b0 = body[0]; let profile_space = b0 >> 6;
let tier_flag = (b0 & 0x20) != 0;
let profile_idc = b0 & 0x1F;
let profile_compatibility_indication =
u32::from_be_bytes([body[1], body[2], body[3], body[4]]);
let b5 = body[5]; let progressive_source_flag = (b5 & 0x80) != 0;
let interlaced_source_flag = (b5 & 0x40) != 0;
let non_packed_constraint_flag = (b5 & 0x20) != 0;
let frame_only_constraint_flag = (b5 & 0x10) != 0;
let copied_44bits_hi: u64 = (b5 as u64 & 0x0F) << 40;
let copied_44bits_lo: u64 = ((body[6] as u64) << 32)
| ((body[7] as u64) << 24)
| ((body[8] as u64) << 16)
| ((body[9] as u64) << 8)
| (body[10] as u64);
let copied_44bits = (copied_44bits_hi | copied_44bits_lo) & COPIED_44_MASK;
let b11 = body[11];
let temporal_layer_subset_flag;
let hevc_still_present_flag;
let hevc_24hr_picture_present_flag;
let sub_pic_hrd_params_not_present_flag;
let hdr_wcg_idc;
let temporal_sub;
if body.len() > (FIXED_BODY_LEN as usize) {
let b12 = body[12];
temporal_layer_subset_flag = (b12 & 0x80) != 0;
hevc_still_present_flag = (b12 & 0x40) != 0;
hevc_24hr_picture_present_flag = (b12 & 0x20) != 0;
sub_pic_hrd_params_not_present_flag = (b12 & 0x10) != 0;
hdr_wcg_idc = HdrWcgIdc::from_u8(b12 & 0x03);
if temporal_layer_subset_flag {
if body.len() < (FIXED_BODY_LEN + TEMPORAL_SUB_LEN) as usize {
return Err(Error::InvalidDescriptor {
tag: TAG,
reason: "HEVC_video_descriptor too short for temporal sub-block",
});
}
let b13 = body[13];
let b14 = body[14];
temporal_sub = Some(HevcTemporalSub {
temporal_id_min: b13 >> 5,
temporal_id_max: b14 >> 5,
});
} else {
temporal_sub = None;
}
} else {
temporal_layer_subset_flag = false;
hevc_still_present_flag = false;
hevc_24hr_picture_present_flag = false;
sub_pic_hrd_params_not_present_flag = false;
hdr_wcg_idc = HdrWcgIdc::NoIndication;
temporal_sub = None;
}
Ok(Self {
profile_space,
tier_flag,
profile_idc,
profile_compatibility_indication,
progressive_source_flag,
interlaced_source_flag,
non_packed_constraint_flag,
frame_only_constraint_flag,
copied_44bits,
level_idc: b11,
temporal_layer_subset_flag,
hevc_still_present_flag,
hevc_24hr_picture_present_flag,
sub_pic_hrd_params_not_present_flag,
hdr_wcg_idc,
temporal_sub,
})
}
}
impl Serialize for HevcVideoDescriptor {
type Error = crate::error::Error;
fn serialized_len(&self) -> usize {
let extra = if self.temporal_layer_subset_flag {
TEMPORAL_SUB_LEN
} else {
0
};
HEADER_LEN + (FIXED_BODY_LEN as usize) + 1 + (extra as usize)
}
fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
let len = self.serialized_len();
if buf.len() < len {
return Err(Error::OutputBufferTooSmall {
need: len,
have: buf.len(),
});
}
let body_len = (len - HEADER_LEN) as u8;
buf[0] = TAG;
buf[1] = body_len;
buf[HEADER_LEN] =
(self.profile_space << 6) | ((self.tier_flag as u8) << 5) | (self.profile_idc & 0x1F);
buf[HEADER_LEN + 1..HEADER_LEN + 5]
.copy_from_slice(&self.profile_compatibility_indication.to_be_bytes());
let copied = self.copied_44bits & COPIED_44_MASK;
buf[HEADER_LEN + 5] = ((self.progressive_source_flag as u8) << 7)
| ((self.interlaced_source_flag as u8) << 6)
| ((self.non_packed_constraint_flag as u8) << 5)
| ((self.frame_only_constraint_flag as u8) << 4)
| (((copied >> 40) & 0x0F) as u8);
buf[HEADER_LEN + 6] = ((copied >> 32) & 0xFF) as u8;
buf[HEADER_LEN + 7] = ((copied >> 24) & 0xFF) as u8;
buf[HEADER_LEN + 8] = ((copied >> 16) & 0xFF) as u8;
buf[HEADER_LEN + 9] = ((copied >> 8) & 0xFF) as u8;
buf[HEADER_LEN + 10] = (copied & 0xFF) as u8;
buf[HEADER_LEN + 11] = self.level_idc;
buf[HEADER_LEN + 12] = ((self.temporal_layer_subset_flag as u8) << 7)
| ((self.hevc_still_present_flag as u8) << 6)
| ((self.hevc_24hr_picture_present_flag as u8) << 5)
| ((self.sub_pic_hrd_params_not_present_flag as u8) << 4)
| (self.hdr_wcg_idc.to_u8() & 0x03);
if self.temporal_layer_subset_flag {
if let Some(ref ts) = self.temporal_sub {
buf[HEADER_LEN + 13] = ts.temporal_id_min << 5;
buf[HEADER_LEN + 14] = ts.temporal_id_max << 5;
}
}
Ok(len)
}
}
impl<'a> crate::traits::DescriptorDef<'a> for HevcVideoDescriptor {
const TAG: u8 = TAG;
const NAME: &'static str = "HEVC_VIDEO";
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn round_trip_no_temporal() {
let orig = HevcVideoDescriptor {
profile_space: 1,
tier_flag: true,
profile_idc: 2,
profile_compatibility_indication: 0xDEADBEEF,
progressive_source_flag: true,
interlaced_source_flag: false,
non_packed_constraint_flag: true,
frame_only_constraint_flag: false,
copied_44bits: 0x123456789AB & COPIED_44_MASK,
level_idc: 0x99,
temporal_layer_subset_flag: false,
hevc_still_present_flag: true,
hevc_24hr_picture_present_flag: false,
sub_pic_hrd_params_not_present_flag: true,
hdr_wcg_idc: HdrWcgIdc::HdrAndWcg,
temporal_sub: None,
};
let mut buf = vec![0u8; orig.serialized_len()];
orig.serialize_into(&mut buf).unwrap();
let reparsed = HevcVideoDescriptor::parse(&buf).unwrap();
assert_eq!(orig, reparsed);
}
#[test]
fn hdr_wcg_idc_is_low_two_bits_of_byte12() {
let body12 = 0b0000_1110u8; let buf = [
TAG, 13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, body12,
];
let d = HevcVideoDescriptor::parse(&buf).unwrap();
assert_eq!(d.hdr_wcg_idc, HdrWcgIdc::HdrAndWcg);
let mut out = vec![0u8; d.serialized_len()];
d.serialize_into(&mut out).unwrap();
assert_eq!(out[14] & 0x03, 0b10, "HDR_WCG_idc must occupy bits [1:0]");
assert_eq!(
out[14] & 0x0C,
0,
"reserved bits [3:2] must serialize as zero"
);
}
#[test]
fn round_trip_with_temporal() {
let orig = HevcVideoDescriptor {
profile_space: 3,
tier_flag: false,
profile_idc: 0x0A,
profile_compatibility_indication: 0xCAFEBABE,
progressive_source_flag: false,
interlaced_source_flag: true,
non_packed_constraint_flag: false,
frame_only_constraint_flag: true,
copied_44bits: 0xABCDEF01234 & COPIED_44_MASK,
level_idc: 0x5A,
temporal_layer_subset_flag: true,
hevc_still_present_flag: false,
hevc_24hr_picture_present_flag: true,
sub_pic_hrd_params_not_present_flag: false,
hdr_wcg_idc: HdrWcgIdc::Sdr,
temporal_sub: Some(HevcTemporalSub {
temporal_id_min: 5,
temporal_id_max: 7,
}),
};
let mut buf = vec![0u8; orig.serialized_len()];
orig.serialize_into(&mut buf).unwrap();
let reparsed = HevcVideoDescriptor::parse(&buf).unwrap();
assert_eq!(orig, reparsed);
}
#[test]
fn round_trip_nonzero_copied_44bits() {
let orig = HevcVideoDescriptor {
profile_space: 0,
tier_flag: false,
profile_idc: 1,
profile_compatibility_indication: 0,
progressive_source_flag: false,
interlaced_source_flag: false,
non_packed_constraint_flag: false,
frame_only_constraint_flag: false,
copied_44bits: COPIED_44_MASK, level_idc: 0x3C,
temporal_layer_subset_flag: false,
hevc_still_present_flag: false,
hevc_24hr_picture_present_flag: false,
sub_pic_hrd_params_not_present_flag: false,
hdr_wcg_idc: HdrWcgIdc::NoIndication,
temporal_sub: None,
};
let mut buf = vec![0u8; orig.serialized_len()];
orig.serialize_into(&mut buf).unwrap();
let reparsed = HevcVideoDescriptor::parse(&buf).unwrap();
assert_eq!(orig, reparsed);
assert_eq!(reparsed.copied_44bits, COPIED_44_MASK);
}
#[test]
fn parse_rejects_wrong_tag() {
let buf = [
0x02, 13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
];
let err = HevcVideoDescriptor::parse(&buf).unwrap_err();
assert!(matches!(err, Error::InvalidDescriptor { tag: 0x02, .. }));
}
#[test]
fn parse_rejects_too_short() {
let err = HevcVideoDescriptor::parse(&[TAG, 2, 0x00, 0x00]).unwrap_err();
assert!(matches!(err, Error::InvalidDescriptor { tag: TAG, .. }));
}
}