use super::descriptor_body;
use crate::error::{Error, Result};
use dvb_common::{Parse, Serialize};
pub const TAG: u8 = 0x04;
const HEADER_LEN: usize = 2;
const BODY_LEN: u8 = 4;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub enum HierarchyType {
Reserved0,
SpatialScalability,
SnrScalability,
TemporalScalability,
DataPartitioning,
ExtensionBitstream,
PrivateStream,
MultiViewProfile,
CombinedScalabilityOrMvHevc,
MvcOrMvcdVideoSubBitstream,
AuxiliaryPictureLayer,
Reserved11To14(u8),
BaseLayerOrMvcBaseView,
}
impl HierarchyType {
#[must_use]
pub fn from_u8(v: u8) -> Self {
match v {
0 => Self::Reserved0,
1 => Self::SpatialScalability,
2 => Self::SnrScalability,
3 => Self::TemporalScalability,
4 => Self::DataPartitioning,
5 => Self::ExtensionBitstream,
6 => Self::PrivateStream,
7 => Self::MultiViewProfile,
8 => Self::CombinedScalabilityOrMvHevc,
9 => Self::MvcOrMvcdVideoSubBitstream,
10 => Self::AuxiliaryPictureLayer,
v @ 11..=14 => Self::Reserved11To14(v),
15 => Self::BaseLayerOrMvcBaseView,
_ => unreachable!("hierarchy_type is 4 bits (0–15)"),
}
}
#[must_use]
pub fn to_u8(self) -> u8 {
match self {
Self::Reserved0 => 0,
Self::SpatialScalability => 1,
Self::SnrScalability => 2,
Self::TemporalScalability => 3,
Self::DataPartitioning => 4,
Self::ExtensionBitstream => 5,
Self::PrivateStream => 6,
Self::MultiViewProfile => 7,
Self::CombinedScalabilityOrMvHevc => 8,
Self::MvcOrMvcdVideoSubBitstream => 9,
Self::AuxiliaryPictureLayer => 10,
Self::Reserved11To14(v) => v,
Self::BaseLayerOrMvcBaseView => 15,
}
}
#[must_use]
pub fn name(self) -> &'static str {
match self {
Self::Reserved0 => "reserved",
Self::SpatialScalability => "Spatial Scalability",
Self::SnrScalability => "SNR Scalability",
Self::TemporalScalability => "Temporal Scalability",
Self::DataPartitioning => "Data partitioning",
Self::ExtensionBitstream => "Extension bitstream",
Self::PrivateStream => "Private Stream",
Self::MultiViewProfile => "Multi-view Profile",
Self::CombinedScalabilityOrMvHevc => {
"Combined Scalability or MV-HEVC sub-partition"
}
Self::MvcOrMvcdVideoSubBitstream => {
"MVC video sub-bitstream or MVCD video sub-bitstream"
}
Self::AuxiliaryPictureLayer => {
"Auxiliary picture layer as defined in Annex F of Rec. ITU-T H.265 | ISO/IEC 23008-2"
}
Self::Reserved11To14(_) => "reserved",
Self::BaseLayerOrMvcBaseView => {
"Base layer or MVC base view sub-bitstream or AVC video sub-bitstream of MVC or HEVC temporal video sub-bitstream or HEVC base sub-partition or Base layer of MVCD base view sub-bitstream or AVC video sub-bitstream of MVCD or VVC temporal video sub-bitstream or EVC temporal video sub-bitstream"
}
}
}
}
dvb_common::impl_spec_display!(HierarchyType, Reserved11To14);
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[cfg_attr(feature = "yoke", derive(yoke::Yokeable))]
pub struct HierarchyDescriptor {
pub no_view_scalability_flag: bool,
pub no_temporal_scalability_flag: bool,
pub no_spatial_scalability_flag: bool,
pub no_quality_scalability_flag: bool,
pub hierarchy_type: HierarchyType,
pub hierarchy_layer_index: u8,
pub tref_present_flag: bool,
pub hierarchy_embedded_layer_index: u8,
pub hierarchy_channel: u8,
}
impl<'a> Parse<'a> for HierarchyDescriptor {
type Error = crate::error::Error;
fn parse(bytes: &'a [u8]) -> Result<Self> {
let body = descriptor_body(
bytes,
TAG,
"HierarchyDescriptor",
"unexpected tag for hierarchy_descriptor",
)?;
if body.len() != BODY_LEN as usize {
return Err(Error::InvalidDescriptor {
tag: TAG,
reason: "hierarchy_descriptor length must equal 4",
});
}
let b0 = body[0];
let no_view_scalability_flag = (b0 & 0x80) != 0;
let no_temporal_scalability_flag = (b0 & 0x40) != 0;
let no_spatial_scalability_flag = (b0 & 0x20) != 0;
let no_quality_scalability_flag = (b0 & 0x10) != 0;
let hierarchy_type = HierarchyType::from_u8(b0 & 0x0F);
let b1 = body[1];
let hierarchy_layer_index = b1 & 0x3F;
let b2 = body[2];
let tref_present_flag = (b2 & 0x80) != 0;
let hierarchy_embedded_layer_index = b2 & 0x3F;
let b3 = body[3];
let hierarchy_channel = b3 & 0x3F;
Ok(Self {
no_view_scalability_flag,
no_temporal_scalability_flag,
no_spatial_scalability_flag,
no_quality_scalability_flag,
hierarchy_type,
hierarchy_layer_index,
tref_present_flag,
hierarchy_embedded_layer_index,
hierarchy_channel,
})
}
}
impl Serialize for HierarchyDescriptor {
type Error = crate::error::Error;
fn serialized_len(&self) -> usize {
HEADER_LEN + (BODY_LEN 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(),
});
}
buf[0] = TAG;
buf[1] = BODY_LEN;
buf[HEADER_LEN] = ((self.no_view_scalability_flag as u8) << 7)
| ((self.no_temporal_scalability_flag as u8) << 6)
| ((self.no_spatial_scalability_flag as u8) << 5)
| ((self.no_quality_scalability_flag as u8) << 4)
| self.hierarchy_type.to_u8();
buf[HEADER_LEN + 1] = self.hierarchy_layer_index & 0x3F;
buf[HEADER_LEN + 2] =
((self.tref_present_flag as u8) << 7) | (self.hierarchy_embedded_layer_index & 0x3F);
buf[HEADER_LEN + 3] = self.hierarchy_channel & 0x3F;
Ok(len)
}
}
impl<'a> crate::traits::DescriptorDef<'a> for HierarchyDescriptor {
const TAG: u8 = TAG;
const NAME: &'static str = "HIERARCHY";
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse() {
let bytes = [
TAG,
4,
0b1010_0011, 0x05, 0b1001_0011, 0b00_110010, ];
let d = HierarchyDescriptor::parse(&bytes).unwrap();
assert!(d.no_view_scalability_flag);
assert!(!d.no_temporal_scalability_flag);
assert!(d.no_spatial_scalability_flag);
assert!(!d.no_quality_scalability_flag);
assert_eq!(d.hierarchy_type, HierarchyType::TemporalScalability);
assert_eq!(d.hierarchy_layer_index, 5);
assert!(d.tref_present_flag);
assert_eq!(d.hierarchy_embedded_layer_index, 19);
assert_eq!(d.hierarchy_channel, 50);
}
#[test]
fn serialize_round_trip() {
let d = HierarchyDescriptor {
no_view_scalability_flag: false,
no_temporal_scalability_flag: true,
no_spatial_scalability_flag: false,
no_quality_scalability_flag: true,
hierarchy_type: HierarchyType::DataPartitioning,
hierarchy_layer_index: 42,
tref_present_flag: false,
hierarchy_embedded_layer_index: 7,
hierarchy_channel: 63,
};
let mut buf = vec![0u8; d.serialized_len()];
d.serialize_into(&mut buf).unwrap();
let reparsed = HierarchyDescriptor::parse(&buf).unwrap();
assert_eq!(d, reparsed);
}
#[test]
fn hierarchy_type_round_trip() {
for v in 0u8..=15 {
assert_eq!(
HierarchyType::from_u8(v).to_u8(),
v,
"round-trip failed for {v:#04x}"
);
}
}
#[test]
fn hierarchy_type_name() {
assert_eq!(
HierarchyType::SpatialScalability.name(),
"Spatial Scalability"
);
assert_eq!(HierarchyType::BaseLayerOrMvcBaseView.name(), "Base layer or MVC base view sub-bitstream or AVC video sub-bitstream of MVC or HEVC temporal video sub-bitstream or HEVC base sub-partition or Base layer of MVCD base view sub-bitstream or AVC video sub-bitstream of MVCD or VVC temporal video sub-bitstream or EVC temporal video sub-bitstream");
}
#[test]
fn parse_rejects_wrong_tag() {
let err = HierarchyDescriptor::parse(&[0x05, 4, 0, 0, 0, 0]).unwrap_err();
assert!(matches!(err, Error::InvalidDescriptor { tag: 0x05, .. }));
}
#[test]
fn parse_rejects_wrong_length() {
let err = HierarchyDescriptor::parse(&[TAG, 3, 0, 0, 0]).unwrap_err();
assert!(matches!(err, Error::InvalidDescriptor { tag: TAG, .. }));
}
#[test]
fn serialize_rejects_small_buffer() {
let d = HierarchyDescriptor {
no_view_scalability_flag: false,
no_temporal_scalability_flag: false,
no_spatial_scalability_flag: false,
no_quality_scalability_flag: false,
hierarchy_type: HierarchyType::from_u8(0),
hierarchy_layer_index: 0,
tref_present_flag: false,
hierarchy_embedded_layer_index: 0,
hierarchy_channel: 0,
};
let mut tiny = vec![0u8; 3];
let err = d.serialize_into(&mut tiny).unwrap_err();
assert!(matches!(err, Error::OutputBufferTooSmall { .. }));
}
}