use super::descriptor_body;
use crate::descriptors::decoder_config_flags::DecoderConfigFlags;
use crate::descriptors::metadata_format::MetadataFormat;
use crate::error::{Error, Result};
use dvb_common::{Parse, Serialize};
pub const TAG: u8 = 0x26;
const HEADER_LEN: usize = 2;
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct DecoderConfigInDescriptor<'a> {
#[cfg_attr(feature = "serde", serde(borrow))]
pub decoder_config: &'a [u8],
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct DecoderConfigDsmcc<'a> {
#[cfg_attr(feature = "serde", serde(borrow))]
pub dec_config_identification: &'a [u8],
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct DecoderConfigOtherService {
pub decoder_config_metadata_service_id: u8,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct ReservedData<'a> {
#[cfg_attr(feature = "serde", serde(borrow))]
pub data: &'a [u8],
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub enum DecoderConfig<'a> {
None,
InDescriptor(DecoderConfigInDescriptor<'a>),
SameService,
DsmccCarousel(DecoderConfigDsmcc<'a>),
OtherService(DecoderConfigOtherService),
ReservedData(ReservedData<'a>),
Private,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[cfg_attr(feature = "yoke", derive(yoke::Yokeable))]
pub struct MetadataDescriptor<'a> {
pub metadata_application_format: u16,
pub metadata_application_format_identifier: Option<u32>,
pub metadata_format: MetadataFormat,
pub metadata_format_identifier: Option<u32>,
pub metadata_service_id: u8,
pub decoder_config_flags: DecoderConfigFlags,
pub dsmcc_flag: bool,
#[cfg_attr(feature = "serde", serde(borrow))]
pub service_identification: Option<&'a [u8]>,
pub decoder_config: DecoderConfig<'a>,
#[cfg_attr(feature = "serde", serde(borrow))]
pub private_data: &'a [u8],
}
impl<'a> Parse<'a> for MetadataDescriptor<'a> {
type Error = crate::error::Error;
fn parse(bytes: &'a [u8]) -> Result<Self> {
let body = descriptor_body(
bytes,
TAG,
"MetadataDescriptor",
"unexpected tag for metadata_descriptor",
)?;
if body.len() < 5 {
return Err(Error::InvalidDescriptor {
tag: TAG,
reason: "metadata_descriptor too short (< 5 body bytes)",
});
}
let metadata_application_format = u16::from_be_bytes([body[0], body[1]]);
let mut pos = 2;
let metadata_application_format_identifier = if metadata_application_format == 0xFFFF {
if body.len() < pos + 4 {
return Err(Error::InvalidDescriptor {
tag: TAG,
reason:
"metadata_descriptor too short for metadata_application_format_identifier",
});
}
let id = u32::from_be_bytes([body[pos], body[pos + 1], body[pos + 2], body[pos + 3]]);
pos += 4;
Some(id)
} else {
None
};
if body.len() < pos + 1 {
return Err(Error::InvalidDescriptor {
tag: TAG,
reason: "metadata_descriptor too short for metadata_format",
});
}
let metadata_format = MetadataFormat::from_u8(body[pos]);
pos += 1;
let metadata_format_identifier = if body[pos - 1] == 0xFF {
if body.len() < pos + 4 {
return Err(Error::InvalidDescriptor {
tag: TAG,
reason: "metadata_descriptor too short for metadata_format_identifier",
});
}
let id = u32::from_be_bytes([body[pos], body[pos + 1], body[pos + 2], body[pos + 3]]);
pos += 4;
Some(id)
} else {
None
};
if body.len() < pos + 2 {
return Err(Error::InvalidDescriptor {
tag: TAG,
reason: "metadata_descriptor too short for service_id + flags",
});
}
let metadata_service_id = body[pos];
pos += 1;
let flags = body[pos];
let decoder_config_raw = (flags & 0xE0) >> 5;
let decoder_config_flags = DecoderConfigFlags::from_u8(decoder_config_raw);
let dsmcc_flag = (flags & 0x10) != 0;
let _reserved = flags & 0x0F;
pos += 1;
let (service_identification, mut pos) = if dsmcc_flag {
if body.len() < pos + 1 {
return Err(Error::InvalidDescriptor {
tag: TAG,
reason: "metadata_descriptor too short for service_identification_length",
});
}
let si_len = body[pos] as usize;
pos += 1;
if body.len() < pos + si_len {
return Err(Error::InvalidDescriptor {
tag: TAG,
reason: "metadata_descriptor too short for service_identification_record",
});
}
let si = &body[pos..pos + si_len];
pos += si_len;
(Some(si), pos)
} else {
(None, pos)
};
let decoder_config = match decoder_config_raw {
0 => DecoderConfig::None,
1 => {
if body.len() < pos + 1 {
return Err(Error::InvalidDescriptor {
tag: TAG,
reason: "metadata_descriptor too short for decoder_config_length",
});
}
let dc_len = body[pos] as usize;
pos += 1;
if body.len() < pos + dc_len {
return Err(Error::InvalidDescriptor {
tag: TAG,
reason: "metadata_descriptor too short for decoder_config bytes",
});
}
let dc = &body[pos..pos + dc_len];
pos += dc_len;
DecoderConfig::InDescriptor(DecoderConfigInDescriptor { decoder_config: dc })
}
2 => DecoderConfig::SameService,
3 => {
if body.len() < pos + 1 {
return Err(Error::InvalidDescriptor {
tag: TAG,
reason: "metadata_descriptor too short for dec_config_identification_record_length",
});
}
let dci_len = body[pos] as usize;
pos += 1;
if body.len() < pos + dci_len {
return Err(Error::InvalidDescriptor {
tag: TAG,
reason:
"metadata_descriptor too short for dec_config_identification_record",
});
}
let dci = &body[pos..pos + dci_len];
pos += dci_len;
DecoderConfig::DsmccCarousel(DecoderConfigDsmcc {
dec_config_identification: dci,
})
}
4 => {
if body.len() < pos + 1 {
return Err(Error::InvalidDescriptor {
tag: TAG,
reason:
"metadata_descriptor too short for decoder_config_metadata_service_id",
});
}
let dcm = body[pos];
pos += 1;
DecoderConfig::OtherService(DecoderConfigOtherService {
decoder_config_metadata_service_id: dcm,
})
}
5 | 6 => {
if body.len() < pos + 1 {
return Err(Error::InvalidDescriptor {
tag: TAG,
reason: "metadata_descriptor too short for reserved_data_length",
});
}
let rd_len = body[pos] as usize;
pos += 1;
if body.len() < pos + rd_len {
return Err(Error::InvalidDescriptor {
tag: TAG,
reason: "metadata_descriptor too short for reserved_data",
});
}
let rd = &body[pos..pos + rd_len];
pos += rd_len;
DecoderConfig::ReservedData(ReservedData { data: rd })
}
_ => DecoderConfig::Private,
};
let private_data = &body[pos..];
Ok(Self {
metadata_application_format,
metadata_application_format_identifier,
metadata_format,
metadata_format_identifier,
metadata_service_id,
decoder_config_flags,
dsmcc_flag,
service_identification,
decoder_config,
private_data,
})
}
}
impl Serialize for MetadataDescriptor<'_> {
type Error = crate::error::Error;
fn serialized_len(&self) -> usize {
let mut len: usize = HEADER_LEN + 5; if self.metadata_application_format_identifier.is_some() {
len += 4;
}
if self.metadata_format_identifier.is_some() {
len += 4;
}
if let Some(si) = self.service_identification {
len += 1 + si.len();
}
match &self.decoder_config {
DecoderConfig::InDescriptor(d) => len += 1 + d.decoder_config.len(),
DecoderConfig::DsmccCarousel(d) => len += 1 + d.dec_config_identification.len(),
DecoderConfig::OtherService(_) => len += 1,
DecoderConfig::ReservedData(d) => len += 1 + d.data.len(),
DecoderConfig::None | DecoderConfig::SameService | DecoderConfig::Private => {}
}
len += self.private_data.len();
len
}
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] = (len - HEADER_LEN) as u8;
buf[HEADER_LEN] = (self.metadata_application_format >> 8) as u8;
buf[HEADER_LEN + 1] = self.metadata_application_format as u8;
let mut pos = HEADER_LEN + 2;
if let Some(id) = self.metadata_application_format_identifier {
buf[pos..pos + 4].copy_from_slice(&id.to_be_bytes());
pos += 4;
}
buf[pos] = self.metadata_format.to_u8();
pos += 1;
if let Some(id) = self.metadata_format_identifier {
buf[pos..pos + 4].copy_from_slice(&id.to_be_bytes());
pos += 4;
}
buf[pos] = self.metadata_service_id;
pos += 1;
let mut flags = (self.decoder_config_flags.to_u8() & 0x07) << 5;
if self.dsmcc_flag {
flags |= 0x10;
}
buf[pos] = flags;
pos += 1;
if let Some(si) = self.service_identification {
buf[pos] = si.len() as u8;
pos += 1;
buf[pos..pos + si.len()].copy_from_slice(si);
pos += si.len();
}
match &self.decoder_config {
DecoderConfig::InDescriptor(d) => {
buf[pos] = d.decoder_config.len() as u8;
pos += 1;
buf[pos..pos + d.decoder_config.len()].copy_from_slice(d.decoder_config);
pos += d.decoder_config.len();
}
DecoderConfig::DsmccCarousel(d) => {
buf[pos] = d.dec_config_identification.len() as u8;
pos += 1;
buf[pos..pos + d.dec_config_identification.len()]
.copy_from_slice(d.dec_config_identification);
pos += d.dec_config_identification.len();
}
DecoderConfig::OtherService(d) => {
buf[pos] = d.decoder_config_metadata_service_id;
pos += 1;
}
DecoderConfig::ReservedData(d) => {
buf[pos] = d.data.len() as u8;
pos += 1;
buf[pos..pos + d.data.len()].copy_from_slice(d.data);
pos += d.data.len();
}
DecoderConfig::None | DecoderConfig::SameService | DecoderConfig::Private => {}
}
buf[pos..pos + self.private_data.len()].copy_from_slice(self.private_data);
Ok(len)
}
}
impl<'a> crate::traits::DescriptorDef<'a> for MetadataDescriptor<'a> {
const TAG: u8 = TAG;
const NAME: &'static str = "METADATA";
}
#[cfg(test)]
mod tests {
use super::*;
fn serialize_round_trip(d: &MetadataDescriptor<'_>) {
let mut buf = vec![0u8; d.serialized_len()];
let written = d.serialize_into(&mut buf).unwrap();
assert_eq!(written, d.serialized_len());
let reparsed = MetadataDescriptor::parse(&buf).unwrap();
assert_eq!(*d, reparsed, "round-trip mismatch");
}
#[test]
fn round_trip_decoder_config_none() {
let d = MetadataDescriptor {
metadata_application_format: 0x0010,
metadata_application_format_identifier: None,
metadata_format: MetadataFormat::TeM,
metadata_format_identifier: None,
metadata_service_id: 5,
decoder_config_flags: DecoderConfigFlags::None,
dsmcc_flag: false,
service_identification: None,
decoder_config: DecoderConfig::None,
private_data: &[],
};
serialize_round_trip(&d);
}
#[test]
fn round_trip_decoder_config_in_descriptor() {
let d = MetadataDescriptor {
metadata_application_format: 0x0011,
metadata_application_format_identifier: None,
metadata_format: MetadataFormat::BiM,
metadata_format_identifier: None,
metadata_service_id: 3,
decoder_config_flags: DecoderConfigFlags::InDescriptor,
dsmcc_flag: false,
service_identification: None,
decoder_config: DecoderConfig::InDescriptor(DecoderConfigInDescriptor {
decoder_config: &[0x01, 0x02, 0x03],
}),
private_data: &[0xFF],
};
serialize_round_trip(&d);
}
#[test]
fn round_trip_decoder_config_same_service() {
let d = MetadataDescriptor {
metadata_application_format: 0x0100,
metadata_application_format_identifier: None,
metadata_format: MetadataFormat::AppFormat,
metadata_format_identifier: None,
metadata_service_id: 1,
decoder_config_flags: DecoderConfigFlags::SameService,
dsmcc_flag: true,
service_identification: Some(&[0xAA, 0xBB]),
decoder_config: DecoderConfig::SameService,
private_data: &[],
};
serialize_round_trip(&d);
}
#[test]
fn round_trip_decoder_config_dsmcc() {
let d = MetadataDescriptor {
metadata_application_format: 0x0010,
metadata_application_format_identifier: None,
metadata_format: MetadataFormat::Reserved1(0x30),
metadata_format_identifier: None,
metadata_service_id: 7,
decoder_config_flags: DecoderConfigFlags::DsmccCarousel,
dsmcc_flag: false,
service_identification: None,
decoder_config: DecoderConfig::DsmccCarousel(DecoderConfigDsmcc {
dec_config_identification: &[0x11, 0x22, 0x33, 0x44],
}),
private_data: &[],
};
serialize_round_trip(&d);
}
#[test]
fn round_trip_decoder_config_other_service() {
let d = MetadataDescriptor {
metadata_application_format: 0x0010,
metadata_application_format_identifier: None,
metadata_format: MetadataFormat::Private(0x50),
metadata_format_identifier: None,
metadata_service_id: 2,
decoder_config_flags: DecoderConfigFlags::OtherService,
dsmcc_flag: false,
service_identification: None,
decoder_config: DecoderConfig::OtherService(DecoderConfigOtherService {
decoder_config_metadata_service_id: 99,
}),
private_data: &[],
};
serialize_round_trip(&d);
}
#[test]
fn round_trip_decoder_config_reserved_data() {
let d = MetadataDescriptor {
metadata_application_format: 0x0010,
metadata_application_format_identifier: None,
metadata_format: MetadataFormat::Identifier,
metadata_format_identifier: Some(0xDEADBEEF),
metadata_service_id: 4,
decoder_config_flags: DecoderConfigFlags::Reserved(5),
dsmcc_flag: false,
service_identification: None,
decoder_config: DecoderConfig::ReservedData(ReservedData {
data: &[0x99, 0x88, 0x77],
}),
private_data: &[],
};
serialize_round_trip(&d);
}
#[test]
fn round_trip_decoder_config_private() {
let d = MetadataDescriptor {
metadata_application_format: 0x0010,
metadata_application_format_identifier: None,
metadata_format: MetadataFormat::TeM,
metadata_format_identifier: None,
metadata_service_id: 6,
decoder_config_flags: DecoderConfigFlags::Private,
dsmcc_flag: false,
service_identification: None,
decoder_config: DecoderConfig::Private,
private_data: &[0x01],
};
serialize_round_trip(&d);
}
#[test]
fn round_trip_with_ffff_and_ff() {
let d = MetadataDescriptor {
metadata_application_format: 0xFFFF,
metadata_application_format_identifier: Some(0x11223344),
metadata_format: MetadataFormat::Identifier,
metadata_format_identifier: Some(0x55667788),
metadata_service_id: 8,
decoder_config_flags: DecoderConfigFlags::None,
dsmcc_flag: true,
service_identification: Some(&[0xCA, 0xFE]),
decoder_config: DecoderConfig::None,
private_data: &[],
};
serialize_round_trip(&d);
}
#[test]
fn parse_rejects_wrong_tag() {
let err = MetadataDescriptor::parse(&[0x02, 5, 0, 0, 0, 0, 0]).unwrap_err();
assert!(matches!(err, Error::InvalidDescriptor { tag: 0x02, .. }));
}
#[test]
fn parse_rejects_too_short() {
let err = MetadataDescriptor::parse(&[TAG, 0]).unwrap_err();
assert!(matches!(err, Error::InvalidDescriptor { tag: TAG, .. }));
}
}