use alloc::vec::Vec;
use crate::error::{Error, Result};
use broadcast_common::{Parse, Serialize, crc32_mpeg2};
pub const PACKET_START_CODE_PREFIX: u32 = 0x00_0001;
pub const MAP_STREAM_ID: u8 = 0xBC;
pub const PSM_START_CODE: u32 = (PACKET_START_CODE_PREFIX << 8) | MAP_STREAM_ID as u32;
const PREFIX_LEN: usize = 6;
const MAP_LENGTH_MAX: usize = 1018;
const FIXED_BODY_LEN: usize = 6;
const CRC_LEN: usize = 4;
const ES_ENTRY_HEADER_LEN: usize = 4;
const PSEUDO_DESCRIPTOR_LEN: usize = 3;
const PSEUDO_DESCRIPTOR_TAG: u8 = 0x00;
const MARKER_BIT: u8 = 0x80;
const EXTENSION_MASK: u8 = 0x7F;
const FLAGS_RESERVED_BIT: u8 = 0x20;
const VERSION_BITS: u32 = 5;
const VERSION_MASK: u8 = 0x1F;
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct EsMapEntry<'a> {
pub stream_type: u8,
pub elementary_stream_id: u8,
pub stream_id_extension: Option<u8>,
#[cfg_attr(feature = "serde", serde(skip))]
pub descriptors: &'a [u8],
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct ProgramStreamMap<'a> {
pub current_next_indicator: bool,
pub single_extension_stream_flag: bool,
pub version: u8,
#[cfg_attr(feature = "serde", serde(skip))]
pub program_stream_info: &'a [u8],
pub elementary_stream_map: Vec<EsMapEntry<'a>>,
pub crc: u32,
}
impl<'a> Parse<'a> for ProgramStreamMap<'a> {
type Error = Error;
fn parse(b: &'a [u8]) -> Result<Self> {
if b.len() < PREFIX_LEN + 2 + 4 {
return Err(Error::BufferTooShort {
need: PREFIX_LEN + 2 + 4,
have: b.len(),
what: "program_stream_map",
});
}
let start = u32::from_be_bytes([0, b[0], b[1], b[2]]);
if start != PACKET_START_CODE_PREFIX {
return Err(Error::BadMapStreamId(b[3]));
}
if b[3] != MAP_STREAM_ID {
return Err(Error::BadMapStreamId(b[3]));
}
let map_length = u16::from_be_bytes([b[4], b[5]]) as usize;
let crc_end = PREFIX_LEN + map_length + 4;
if b.len() < crc_end {
return Err(Error::MapLengthOverflow {
map_length,
available: b.len().saturating_sub(PREFIX_LEN),
});
}
let crc_data_end = crc_end - 4;
let crc_data = &b[0..crc_data_end];
let stored_crc = u32::from_be_bytes([
b[crc_data_end],
b[crc_data_end + 1],
b[crc_data_end + 2],
b[crc_data_end + 3],
]);
let computed_crc = crc32_mpeg2::compute(crc_data);
if computed_crc != stored_crc {
return Err(Error::BadCrc {
computed: computed_crc,
stored: stored_crc,
});
}
let flags = b[6];
let current_next_indicator = flags & 0x80 != 0;
let single_extension_stream_flag = flags & 0x40 != 0;
let version = flags & VERSION_MASK;
if b[7] & 0x01 == 0 {
return Err(Error::BadMarker("program_stream_map marker_bit"));
}
let program_stream_info_length = u16::from_be_bytes([b[8], b[9]]) as usize;
let info_start = PREFIX_LEN + 4; let info_end = info_start + program_stream_info_length;
if crc_data_end < info_end + 2 {
return Err(Error::MapLengthOverflow {
map_length,
available: b.len().saturating_sub(PREFIX_LEN),
});
}
let elementary_stream_map_length =
u16::from_be_bytes([b[info_end], b[info_end + 1]]) as usize;
let es_start = info_end + 2; let es_end = es_start + elementary_stream_map_length;
if crc_data_end < es_end {
return Err(Error::MapLengthOverflow {
map_length,
available: b.len().saturating_sub(PREFIX_LEN),
});
}
let program_stream_info = &b[info_start..info_end];
let es_data = &b[es_start..es_end];
let elementary_stream_map = parse_es_loop(es_data, single_extension_stream_flag)?;
Ok(ProgramStreamMap {
current_next_indicator,
single_extension_stream_flag,
version,
program_stream_info,
elementary_stream_map,
crc: stored_crc,
})
}
}
fn parse_es_loop(data: &[u8], single_flag: bool) -> Result<Vec<EsMapEntry<'_>>> {
let mut entries = Vec::new();
let mut pos = 0;
while pos + 4 <= data.len() {
let stream_type = data[pos];
let elementary_stream_id = data[pos + 1];
let es_info_length = u16::from_be_bytes([data[pos + 2], data[pos + 3]]) as usize;
let entry_hdr_end = pos + 4;
let entry_end = entry_hdr_end + es_info_length;
if entry_end > data.len() {
return Err(Error::BufferTooShort {
need: entry_end,
have: data.len(),
what: "elementary_stream_map entry descriptors",
});
}
let (stream_id_extension, descriptors) = if elementary_stream_id == 0xFD && !single_flag {
if es_info_length < 3 {
return Err(Error::BufferTooShort {
need: 3,
have: es_info_length,
what: "pseudo descriptor for stream_id_extension",
});
}
if data[entry_hdr_end + 2] & 0x80 == 0 {
return Err(Error::BadMarker("elementary_stream_id_extension marker"));
}
let ext = data[entry_hdr_end + 2] & 0x7F;
let desc = &data[entry_hdr_end + 3..entry_end];
(Some(ext), desc)
} else {
let desc = &data[entry_hdr_end..entry_end];
(None, desc)
};
entries.push(EsMapEntry {
stream_type,
elementary_stream_id,
stream_id_extension,
descriptors,
});
pos = entry_end;
}
Ok(entries)
}
fn es_entry_len(e: &EsMapEntry<'_>) -> usize {
ES_ENTRY_HEADER_LEN
+ if e.stream_id_extension.is_some() {
PSEUDO_DESCRIPTOR_LEN
} else {
0
}
+ e.descriptors.len()
}
fn write_es_loop(entries: &[EsMapEntry<'_>], out: &mut [u8]) -> Result<()> {
let mut at = 0usize;
for e in entries {
let entry_len = es_entry_len(e);
let dst = &mut out[at..at + entry_len];
dst[0] = e.stream_type;
dst[1] = e.elementary_stream_id;
let info_len = entry_len - ES_ENTRY_HEADER_LEN;
let info_len =
broadcast_common::len::fit_u16(info_len, "elementary_stream_map.ES_info_length")?;
dst[2..4].copy_from_slice(&info_len.to_be_bytes());
let mut c = ES_ENTRY_HEADER_LEN;
if let Some(ext) = e.stream_id_extension {
dst[c] = PSEUDO_DESCRIPTOR_TAG;
dst[c + 1] = broadcast_common::len::fit_u8(
1 + e.descriptors.len(),
"elementary_stream_map.pseudo_descriptor_length",
)?;
dst[c + 2] = MARKER_BIT | (ext & EXTENSION_MASK);
c += PSEUDO_DESCRIPTOR_LEN;
}
dst[c..].copy_from_slice(e.descriptors);
at += entry_len;
}
Ok(())
}
impl Serialize for ProgramStreamMap<'_> {
type Error = Error;
fn serialized_len(&self) -> usize {
let prog_info_len = self.program_stream_info.len();
let es_loop_len: usize = self.elementary_stream_map.iter().map(es_entry_len).sum();
PREFIX_LEN + FIXED_BODY_LEN + prog_info_len + es_loop_len + CRC_LEN
}
fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
let total = self.serialized_len();
if buf.len() < total {
return Err(Error::BufferTooShort {
need: total,
have: buf.len(),
what: "program_stream_map serialize output",
});
}
buf[0..3].copy_from_slice(&PACKET_START_CODE_PREFIX.to_be_bytes()[1..]);
buf[3] = MAP_STREAM_ID;
let prog_info_len = self.program_stream_info.len();
let es_loop_len: usize = self.elementary_stream_map.iter().map(es_entry_len).sum();
let map_length = FIXED_BODY_LEN + prog_info_len + es_loop_len;
if map_length > MAP_LENGTH_MAX {
return Err(broadcast_common::len::FieldOverflow {
field: "program_stream_map.program_stream_map_length",
value: map_length as u64,
max: MAP_LENGTH_MAX as u64,
}
.into());
}
let map_length = broadcast_common::len::fit_u16(
map_length,
"program_stream_map.program_stream_map_length",
)?;
buf[4..6].copy_from_slice(&map_length.to_be_bytes());
let version = broadcast_common::len::fit_bits(
u64::from(self.version),
VERSION_BITS,
"program_stream_map.program_stream_map_version",
)? as u8;
buf[6] = (u8::from(self.current_next_indicator) << 7)
| (u8::from(self.single_extension_stream_flag) << 6)
| FLAGS_RESERVED_BIT
| version;
buf[7] = 0xFF;
let prog_info_len_u16 = broadcast_common::len::fit_u16(
prog_info_len,
"program_stream_map.program_stream_info_length",
)?;
let es_loop_len_u16 = broadcast_common::len::fit_u16(
es_loop_len,
"program_stream_map.elementary_stream_map_length",
)?;
buf[8..10].copy_from_slice(&prog_info_len_u16.to_be_bytes());
buf[10..10 + prog_info_len].copy_from_slice(self.program_stream_info);
let es_len_start = 10 + prog_info_len;
buf[es_len_start..es_len_start + 2].copy_from_slice(&es_loop_len_u16.to_be_bytes());
let es_start = es_len_start + 2;
write_es_loop(
&self.elementary_stream_map,
&mut buf[es_start..es_start + es_loop_len],
)?;
let crc_offset = es_start + es_loop_len;
let crc = crc32_mpeg2::compute(&buf[0..crc_offset]);
buf[crc_offset..crc_offset + 4].copy_from_slice(&crc.to_be_bytes());
Ok(total)
}
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::vec;
#[test]
fn serialize_rejects_version_over_5_bits() {
let psm = ProgramStreamMap {
current_next_indicator: true,
single_extension_stream_flag: false,
version: 0x20,
program_stream_info: &[],
elementary_stream_map: vec![],
crc: 0,
};
let mut buf = vec![0u8; 64];
assert!(matches!(
psm.serialize_into(&mut buf),
Err(Error::FieldOverflow(_))
));
}
#[test]
fn psm_build_and_round_trip() {
let entries = vec![EsMapEntry {
stream_type: 0x02, elementary_stream_id: 0xE0,
stream_id_extension: None,
descriptors: &[0x0A, 0x04, b'H', b'E', b'L', b'L'], }];
let psm = ProgramStreamMap {
current_next_indicator: true,
single_extension_stream_flag: false,
version: 3,
program_stream_info: &[],
elementary_stream_map: entries,
crc: 0, };
let mut buf = vec![0u8; psm.serialized_len()];
psm.serialize_into(&mut buf).unwrap();
let parsed = ProgramStreamMap::parse(&buf).unwrap();
assert!(parsed.current_next_indicator);
assert!(!parsed.single_extension_stream_flag);
assert_eq!(parsed.version, 3);
assert!(parsed.program_stream_info.is_empty());
assert_eq!(parsed.elementary_stream_map.len(), 1);
assert_eq!(parsed.elementary_stream_map[0].stream_type, 0x02);
assert_eq!(parsed.elementary_stream_map[0].elementary_stream_id, 0xE0);
assert!(
parsed.elementary_stream_map[0]
.stream_id_extension
.is_none()
);
assert_eq!(
parsed.elementary_stream_map[0].descriptors,
&[0x0A, 0x04, b'H', b'E', b'L', b'L']
);
let mut out2 = vec![0u8; parsed.serialized_len()];
parsed.serialize_into(&mut out2).unwrap();
assert_eq!(&out2[..], &buf[..], "byte-exact round-trip mismatch");
let psm_mut = ProgramStreamMap {
current_next_indicator: true,
single_extension_stream_flag: false,
version: 7,
program_stream_info: &[],
elementary_stream_map: vec![EsMapEntry {
stream_type: 0x02,
elementary_stream_id: 0xE0,
stream_id_extension: None,
descriptors: &[0x0A, 0x04, b'H', b'E', b'L', b'L'],
}],
crc: 0,
};
let mut out3 = vec![0u8; psm_mut.serialized_len()];
psm_mut.serialize_into(&mut out3).unwrap();
assert_ne!(&buf[..], &out3[..]);
}
#[test]
fn psm_with_stream_id_extension() {
let entries = vec![EsMapEntry {
stream_type: 0x06, elementary_stream_id: 0xFD,
stream_id_extension: Some(0x0F),
descriptors: &[0x59, 0x02, 0x01, 0x02], }];
let psm = ProgramStreamMap {
current_next_indicator: true,
single_extension_stream_flag: false,
version: 1,
program_stream_info: &[],
elementary_stream_map: entries,
crc: 0,
};
let mut buf = vec![0u8; psm.serialized_len()];
psm.serialize_into(&mut buf).unwrap();
let parsed = ProgramStreamMap::parse(&buf).unwrap();
assert_eq!(parsed.elementary_stream_map.len(), 1);
assert_eq!(
parsed.elementary_stream_map[0].stream_id_extension,
Some(0x0F)
);
assert_eq!(
parsed.elementary_stream_map[0].descriptors,
&[0x59, 0x02, 0x01, 0x02]
);
let mut out2 = vec![0u8; parsed.serialized_len()];
parsed.serialize_into(&mut out2).unwrap();
assert_eq!(&out2[..], &buf[..]);
}
#[test]
fn pseudo_descriptor_length_overflow_is_rejected_not_wrapped() {
let entries = vec![EsMapEntry {
stream_type: 0x06,
elementary_stream_id: 0xFD,
stream_id_extension: Some(0x01),
descriptors: &[0u8; 255],
}];
let psm = ProgramStreamMap {
current_next_indicator: true,
single_extension_stream_flag: false,
version: 0,
program_stream_info: &[],
elementary_stream_map: entries,
crc: 0,
};
let mut buf = vec![0u8; psm.serialized_len()];
assert!(psm.serialize_into(&mut buf).is_err());
}
#[test]
fn program_stream_map_length_over_spec_max_is_rejected() {
let psm = ProgramStreamMap {
current_next_indicator: true,
single_extension_stream_flag: false,
version: 0,
program_stream_info: &[0u8; 1019],
elementary_stream_map: vec![],
crc: 0,
};
let mut buf = vec![0u8; psm.serialized_len()];
assert!(psm.serialize_into(&mut buf).is_err());
}
#[test]
fn program_stream_map_length_at_spec_max_round_trips() {
let psm = ProgramStreamMap {
current_next_indicator: true,
single_extension_stream_flag: false,
version: 0,
program_stream_info: &[0u8; 1012],
elementary_stream_map: vec![],
crc: 0,
};
let mut buf = vec![0u8; psm.serialized_len()];
psm.serialize_into(&mut buf).unwrap();
let parsed = ProgramStreamMap::parse(&buf).unwrap();
assert_eq!(parsed.program_stream_info.len(), 1012);
}
#[test]
fn psm_with_nonempty_program_descriptor_loop() {
let prog_descriptor: &[u8] = &[0x05, 0x04, b'T', b'E', b'S', b'T'];
let entries = vec![EsMapEntry {
stream_type: 0x02, elementary_stream_id: 0xE0,
stream_id_extension: None,
descriptors: &[],
}];
let psm = ProgramStreamMap {
current_next_indicator: true,
single_extension_stream_flag: false,
version: 5,
program_stream_info: prog_descriptor,
elementary_stream_map: entries,
crc: 0,
};
let mut buf = vec![0u8; psm.serialized_len()];
psm.serialize_into(&mut buf).unwrap();
assert_eq!(&buf[10..16], prog_descriptor);
let es_map_len = u16::from_be_bytes([buf[16], buf[17]]);
assert_eq!(es_map_len, 4, "one ES entry: stream_type+id+info_len(0)");
let parsed = ProgramStreamMap::parse(&buf).unwrap();
assert_eq!(parsed.program_stream_info, prog_descriptor);
assert_eq!(parsed.elementary_stream_map.len(), 1);
assert_eq!(parsed.elementary_stream_map[0].stream_type, 0x02);
assert_eq!(parsed.elementary_stream_map[0].elementary_stream_id, 0xE0);
let mut out2 = vec![0u8; parsed.serialized_len()];
parsed.serialize_into(&mut out2).unwrap();
assert_eq!(&out2[..], &buf[..]);
}
#[test]
fn reserved_bits_are_set_by_convention() {
let psm = ProgramStreamMap {
current_next_indicator: false,
single_extension_stream_flag: false,
version: 0,
program_stream_info: &[],
elementary_stream_map: vec![],
crc: 0,
};
let mut buf = vec![0u8; psm.serialized_len()];
psm.serialize_into(&mut buf).unwrap();
assert_eq!(
buf[6], 0x20,
"flags byte reserved bit (0x20) must be set even with both flags clear"
);
assert_eq!(buf[7], 0xFF, "reserved(7)+marker_bit(1) byte must be 0xFF");
}
#[test]
fn psm_bad_crc_rejected() {
let psm = ProgramStreamMap {
current_next_indicator: true,
single_extension_stream_flag: false,
version: 0,
program_stream_info: &[],
elementary_stream_map: vec![],
crc: 0,
};
let mut buf = vec![0u8; psm.serialized_len()];
psm.serialize_into(&mut buf).unwrap();
let crc_off = buf.len() - 4;
buf[crc_off] ^= 0xFF;
assert!(matches!(
ProgramStreamMap::parse(&buf),
Err(Error::BadCrc { .. })
));
}
}