use super::hdr_wcg_idc::HdrWcgIdc;
use crate::error::Result;
use crate::h222_bits::{BitReader, BitWriter, invalid};
use alloc::vec::Vec;
const TAG: u8 = super::mpeg2_extension::TAG;
const RES1: u8 = 0x01;
const RES3: u8 = 0x07;
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub struct LcevcVideo {
pub lcevc_stream_tag: u8,
pub profile_idc: u8,
pub level_idc: u8,
pub sublevel_idc: u8,
pub processed_planes_type_flag: bool,
pub picture_type_bit_flag: bool,
pub field_type_bit_flag: bool,
pub reserved: u8,
pub hdr_wcg_idc: HdrWcgIdc,
pub reserved_zero_2bit: u8,
pub video_properties_tag: u8,
pub trailing: Vec<u8>,
}
impl LcevcVideo {
#[must_use]
pub fn new(
lcevc_stream_tag: u8,
profile_idc: u8,
level_idc: u8,
sublevel_idc: u8,
hdr_wcg_idc: HdrWcgIdc,
video_properties_tag: u8,
) -> Self {
Self {
lcevc_stream_tag,
profile_idc,
level_idc,
sublevel_idc,
processed_planes_type_flag: false,
picture_type_bit_flag: false,
field_type_bit_flag: false,
reserved: RES3,
hdr_wcg_idc,
reserved_zero_2bit: 0,
video_properties_tag,
trailing: Vec::new(),
}
}
pub(crate) fn parse_body(body: &[u8]) -> Result<Self> {
let mut r = BitReader::new(body, "LcevcVideo");
Ok(Self {
lcevc_stream_tag: r.u8(8)?,
profile_idc: r.u8(4)?,
level_idc: r.u8(4)?,
sublevel_idc: r.u8(2)?,
processed_planes_type_flag: r.flag()?,
picture_type_bit_flag: r.flag()?,
field_type_bit_flag: r.flag()?,
reserved: r.u8(3)?,
hdr_wcg_idc: HdrWcgIdc::from_u8(r.u8(2)?),
reserved_zero_2bit: r.u8(2)?,
video_properties_tag: r.u8(4)?,
trailing: r.rest()?.to_vec(),
})
}
pub(crate) fn write_body(&self) -> Result<Vec<u8>> {
let mut w = BitWriter::new();
w.put(u64::from(self.lcevc_stream_tag), 8, "lcevc_stream_tag")?;
w.put(u64::from(self.profile_idc), 4, "profile_idc")?;
w.put(u64::from(self.level_idc), 4, "level_idc")?;
w.put(u64::from(self.sublevel_idc), 2, "sublevel_idc")?;
w.flag(
self.processed_planes_type_flag,
"processed_planes_type_flag",
)?;
w.flag(self.picture_type_bit_flag, "picture_type_bit_flag")?;
w.flag(self.field_type_bit_flag, "field_type_bit_flag")?;
w.put(u64::from(self.reserved), 3, "reserved")?;
w.put(u64::from(self.hdr_wcg_idc.to_u8()), 2, "HDR_WCG_idc")?;
w.put(u64::from(self.reserved_zero_2bit), 2, "reserved_zero_2bit")?;
w.put(
u64::from(self.video_properties_tag),
4,
"video_properties_tag",
)?;
w.bytes(&self.trailing)?;
Ok(w.into_vec())
}
}
impl LcevcVideo {
pub const RESERVED_DEFAULT: u8 = RES3;
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub struct LcevcLinkage {
pub lcevc_stream_tags: Vec<u8>,
pub trailing: Vec<u8>,
}
impl LcevcLinkage {
#[must_use]
pub fn new(lcevc_stream_tags: Vec<u8>) -> Self {
Self {
lcevc_stream_tags,
trailing: Vec::new(),
}
}
pub(crate) fn parse_body(body: &[u8]) -> Result<Self> {
let mut r = BitReader::new(body, "LcevcLinkage");
let n = r.bits(8)?;
let mut tags = Vec::new();
for _ in 0..n {
tags.push(r.u8(8)?);
}
Ok(Self {
lcevc_stream_tags: tags,
trailing: r.rest()?.to_vec(),
})
}
pub(crate) fn write_body(&self) -> Result<Vec<u8>> {
let mut w = BitWriter::new();
w.put(
self.lcevc_stream_tags.len() as u64,
8,
"num_lcevc_stream_tags",
)?;
w.bytes(&self.lcevc_stream_tags)?;
w.bytes(&self.trailing)?;
Ok(w.into_vec())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub enum MediaTypeIdc {
Unknown,
Video,
Audio,
TextData,
Reserved(u8),
}
impl MediaTypeIdc {
#[must_use]
pub const fn from_u8(v: u8) -> Self {
match v {
0 => Self::Unknown,
1 => Self::Video,
2 => Self::Audio,
3 => Self::TextData,
other => Self::Reserved(other),
}
}
#[must_use]
pub const fn to_u8(self) -> u8 {
match self {
Self::Unknown => 0,
Self::Video => 1,
Self::Audio => 2,
Self::TextData => 3,
Self::Reserved(v) => v,
}
}
#[must_use]
pub fn name(&self) -> &'static str {
match self {
Self::Unknown => "unknown",
Self::Video => "video",
Self::Audio => "audio",
Self::TextData => "text/data",
Self::Reserved(_) => "reserved",
}
}
}
broadcast_common::impl_spec_display!(MediaTypeIdc, Reserved);
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub enum ConfigurationType {
Complete,
Partial,
CompleteCombination,
Reserved(u8),
}
impl ConfigurationType {
#[must_use]
pub const fn from_u8(v: u8) -> Self {
match v {
0 => Self::Complete,
1 => Self::Partial,
2 => Self::CompleteCombination,
other => Self::Reserved(other),
}
}
#[must_use]
pub const fn to_u8(self) -> u8 {
match self {
Self::Complete => 0,
Self::Partial => 1,
Self::CompleteCombination => 2,
Self::Reserved(v) => v,
}
}
#[must_use]
pub fn name(&self) -> &'static str {
match self {
Self::Complete => "complete",
Self::Partial => "partial",
Self::CompleteCombination => "complete combination",
Self::Reserved(_) => "reserved",
}
}
}
broadcast_common::impl_spec_display!(ConfigurationType, Reserved);
pub const ID_LENGTH_CODE_EXPLICIT: u8 = 7;
pub const ID_LENGTHS: [usize; 7] = [1, 2, 4, 8, 12, 16, 20];
const LANG_LEN_EXPLICIT: u8 = 0;
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub struct MediaIdentifier {
pub id_length_code: u8,
pub id_type: u16,
pub media_id: Vec<u8>,
}
impl MediaIdentifier {
#[must_use]
pub fn new(id_length_code: u8, id_type: u16, media_id: Vec<u8>) -> Self {
Self {
id_length_code,
id_type,
media_id,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub struct MediaServiceLanguage {
pub configuration_type: ConfigurationType,
pub lang_len_idc: u8,
pub reserved: u8,
pub language_code: Vec<u8>,
pub media_service_types: Vec<u8>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub struct MediaServiceKindEntry {
pub media_description_flag: bool,
pub media_type_idc: MediaTypeIdc,
pub reserved: u8,
pub identifier: Option<MediaIdentifier>,
pub languages: Vec<MediaServiceLanguage>,
}
impl MediaServiceKindEntry {
#[must_use]
pub fn new(
media_description_flag: bool,
media_type_idc: MediaTypeIdc,
identifier: Option<MediaIdentifier>,
languages: Vec<MediaServiceLanguage>,
) -> Self {
Self {
media_description_flag,
media_type_idc,
reserved: RES1,
identifier,
languages,
}
}
}
impl MediaServiceLanguage {
#[must_use]
pub fn new(
configuration_type: ConfigurationType,
language_code: Vec<u8>,
media_service_types: Vec<u8>,
) -> Self {
let lang_len_idc = match language_code.len() {
2 => 1,
3 => 2,
_ => LANG_LEN_EXPLICIT,
};
Self {
configuration_type,
lang_len_idc,
reserved: RES1,
language_code,
media_service_types,
}
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub struct MediaServiceKind {
pub entries: Vec<MediaServiceKindEntry>,
}
impl MediaServiceKind {
#[must_use]
pub fn new(entries: Vec<MediaServiceKindEntry>) -> Self {
Self { entries }
}
pub(crate) fn parse_body(body: &[u8]) -> Result<Self> {
let mut r = BitReader::new(body, "MediaServiceKind");
let mut entries = Vec::new();
while r.remaining_bytes() > 0 {
let media_description_flag = r.flag()?;
let identifier_flag = r.flag()?;
let lang_pairs = r.bits(3)?;
let media_type_idc = MediaTypeIdc::from_u8(r.u8(2)?);
let reserved = r.u8(1)?;
let identifier = if identifier_flag {
let id_length_code = r.u8(3)?;
let id_type = r.u16(13)?;
let len = if id_length_code == ID_LENGTH_CODE_EXPLICIT {
usize::from(r.u8(8)?)
} else {
ID_LENGTHS[usize::from(id_length_code)]
};
Some(MediaIdentifier {
id_length_code,
id_type,
media_id: r.bytes(len)?.to_vec(),
})
} else {
None
};
let mut languages = Vec::new();
for _ in 0..lang_pairs {
let configuration_type = ConfigurationType::from_u8(r.u8(2)?);
let purpose_cnt = r.bits(3)?;
let lang_len_idc = r.u8(2)?;
let reserved = r.u8(1)?;
let len = match lang_len_idc {
0 => usize::from(r.u8(8)?),
1 => 2,
2 => 3,
_ => {
return Err(invalid(
TAG,
"Media_service_kind_descriptor lang_len_idc 3 is reserved",
));
}
};
let language_code = r.bytes(len)?.to_vec();
let media_service_types = r.bytes(purpose_cnt as usize)?.to_vec();
languages.push(MediaServiceLanguage {
configuration_type,
lang_len_idc,
reserved,
language_code,
media_service_types,
});
}
entries.push(MediaServiceKindEntry {
media_description_flag,
media_type_idc,
reserved,
identifier,
languages,
});
}
Ok(Self { entries })
}
pub(crate) fn write_body(&self) -> Result<Vec<u8>> {
let mut w = BitWriter::new();
for e in &self.entries {
w.flag(e.media_description_flag, "media_description_flag")?;
w.flag(e.identifier.is_some(), "identifier_flag")?;
w.put(e.languages.len() as u64, 3, "lang_pairs")?;
w.put(u64::from(e.media_type_idc.to_u8()), 2, "media_type_idc")?;
w.put(u64::from(e.reserved), 1, "reserved")?;
if let Some(id) = &e.identifier {
w.put(u64::from(id.id_length_code), 3, "ID_length_code")?;
w.put(u64::from(id.id_type), 13, "ID_type")?;
if id.id_length_code == ID_LENGTH_CODE_EXPLICIT {
w.put(id.media_id.len() as u64, 8, "ID_len")?;
} else if ID_LENGTHS.get(usize::from(id.id_length_code)) != Some(&id.media_id.len())
{
return Err(invalid(
TAG,
"Media_service_kind_descriptor media_ID_field length disagrees with ID_length_code",
));
}
w.bytes(&id.media_id)?;
}
for l in &e.languages {
w.put(
u64::from(l.configuration_type.to_u8()),
2,
"configuration_type",
)?;
w.put(l.media_service_types.len() as u64, 3, "lang_purpose_cnt")?;
w.put(u64::from(l.lang_len_idc), 2, "lang_len_idc")?;
w.put(u64::from(l.reserved), 1, "reserved")?;
match (l.lang_len_idc, l.language_code.len()) {
(0, n) => w.put(n as u64, 8, "lang_len")?,
(1, 2) | (2, 3) => {}
_ => {
return Err(invalid(
TAG,
"Media_service_kind_descriptor language code length disagrees with lang_len_idc",
));
}
}
w.bytes(&l.language_code)?;
w.bytes(&l.media_service_types)?;
}
}
Ok(w.into_vec())
}
}
#[cfg(test)]
#[allow(clippy::unusual_byte_groupings)] mod tests {
use super::*;
#[test]
fn lcevc_video_spec_vector() {
let b = [0x07, 0x35, 0b01_1_0_1_111, 0b10_00_0010];
let d = LcevcVideo::parse_body(&b).unwrap();
assert_eq!((d.profile_idc, d.level_idc, d.sublevel_idc), (3, 5, 1));
assert!(d.processed_planes_type_flag && !d.picture_type_bit_flag && d.field_type_bit_flag);
assert_eq!(d.hdr_wcg_idc, HdrWcgIdc::HdrAndWcg);
assert_eq!(d.video_properties_tag, 2);
assert_eq!(d.write_body().unwrap(), b);
let mut bad = d.clone();
bad.profile_idc = 16;
assert!(matches!(
&(bad.write_body()),
Err(crate::error::Error::FieldOverflow(_))
));
assert!(matches!(
LcevcVideo::parse_body(&b[..3]),
Err(crate::Error::BufferTooShort(..))
));
}
#[test]
fn lcevc_linkage_vectors() {
let b = [2, 0x10, 0x20, 0xFF];
let d = LcevcLinkage::parse_body(&b).unwrap();
assert_eq!(d.lcevc_stream_tags, [0x10, 0x20]);
assert_eq!(d.trailing, [0xFF]);
assert_eq!(d.write_body().unwrap(), b);
assert!(matches!(
LcevcLinkage::parse_body(&[3, 1]),
Err(crate::Error::BufferTooShort(..))
));
assert!(matches!(
&(LcevcLinkage::new(vec![0; 256]).write_body()),
Err(crate::error::Error::FieldOverflow(_))
));
}
#[test]
fn media_service_kind_round_trip() {
let id = MediaIdentifier {
id_length_code: 2,
id_type: 0x0200,
media_id: vec![1, 2, 3, 4],
};
let l1 = MediaServiceLanguage::new(ConfigurationType::Complete, b"en".to_vec(), vec![1, 9]);
let l2 = MediaServiceLanguage::new(ConfigurationType::Partial, b"zxx-x".to_vec(), vec![]);
let e1 = MediaServiceKindEntry::new(false, MediaTypeIdc::Audio, Some(id), vec![l1, l2]);
let e2 = MediaServiceKindEntry::new(true, MediaTypeIdc::Video, None, vec![]);
let d = MediaServiceKind::new(vec![e1, e2]);
let b = d.write_body().unwrap();
assert_eq!(b[0], 0b0_1_010_10_1);
assert_eq!(&b[1..3], &[0b010_00010, 0x00]);
assert_eq!(&b[3..7], &[1, 2, 3, 4]);
assert_eq!(b[7], 0b00_010_01_1);
assert_eq!(&b[8..12], &[b'e', b'n', 1, 9]);
assert_eq!(&b[12..14], &[0b01_000_00_1, 5]);
assert_eq!(MediaServiceKind::parse_body(&b).unwrap(), d);
}
#[test]
fn media_service_kind_rejects_inconsistent_and_reserved() {
let mut l = MediaServiceLanguage::new(ConfigurationType::Complete, b"en".to_vec(), vec![]);
l.lang_len_idc = 2; let e = MediaServiceKindEntry::new(false, MediaTypeIdc::Audio, None, vec![l]);
assert!(matches!(
&(MediaServiceKind::new(vec![e]).write_body()),
Err(crate::error::Error::InvalidDescriptor { .. })
));
let b = [0b0_0_001_01_1, 0b00_000_11_1];
assert!(matches!(
MediaServiceKind::parse_body(&b),
Err(crate::Error::InvalidDescriptor { .. })
));
let id = MediaIdentifier {
id_length_code: 0,
id_type: 1,
media_id: vec![1, 2],
};
let e = MediaServiceKindEntry::new(false, MediaTypeIdc::Audio, Some(id), vec![]);
assert!(matches!(
&(MediaServiceKind::new(vec![e]).write_body()),
Err(crate::error::Error::InvalidDescriptor { .. })
));
}
}