use crate::demultiplex::DemuxError;
use crate::descriptor;
use crate::packet;
use crate::StreamType;
use log::warn;
use std::fmt;
pub struct PmtSection<'buf> {
data: &'buf [u8],
}
impl<'buf> fmt::Debug for PmtSection<'buf> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
f.debug_struct("PmtSection")
.field("pcr_pid", &self.pcr_pid())
.field("descriptors", &DescriptorsDebug(self))
.field("streams", &StreamsDebug(self))
.finish()
}
}
struct StreamsDebug<'buf>(&'buf PmtSection<'buf>);
impl<'buf> fmt::Debug for StreamsDebug<'buf> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
f.debug_list().entries(self.0.streams()).finish()
}
}
struct DescriptorsDebug<'buf>(&'buf PmtSection<'buf>);
impl<'buf> fmt::Debug for DescriptorsDebug<'buf> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
f.debug_list()
.entries(self.0.descriptors::<descriptor::CoreDescriptors<'buf>>())
.finish()
}
}
impl<'buf> PmtSection<'buf> {
pub fn from_bytes(data: &'buf [u8]) -> Result<PmtSection<'buf>, DemuxError> {
if data.len() < Self::HEADER_SIZE {
Err(DemuxError::NotEnoughData {
field: "program_map_section",
expected: Self::HEADER_SIZE,
actual: data.len(),
})
} else {
let sect = PmtSection { data };
let expected = sect.program_info_length() as usize + Self::HEADER_SIZE;
if data.len() < expected {
Err(DemuxError::NotEnoughData {
field: "descriptor",
expected,
actual: data.len(),
})
} else {
Ok(sect)
}
}
}
pub fn buffer(&self) -> &[u8] {
self.data
}
const HEADER_SIZE: usize = 4;
pub fn pcr_pid(&self) -> packet::Pid {
packet::Pid::new(u16::from(self.data[0] & 0b0001_1111) << 8 | u16::from(self.data[1]))
}
fn program_info_length(&self) -> u16 {
u16::from(self.data[2] & 0b0000_1111) << 8 | u16::from(self.data[3])
}
pub fn descriptors<Desc: descriptor::Descriptor<'buf> + 'buf>(
&self,
) -> impl Iterator<Item = Result<Desc, descriptor::DescriptorError>> + 'buf {
let descriptor_end = Self::HEADER_SIZE + self.program_info_length() as usize;
let descriptor_data = &self.data[Self::HEADER_SIZE..descriptor_end];
descriptor::DescriptorIter::new(descriptor_data)
}
pub fn streams(&self) -> impl Iterator<Item = StreamInfo<'buf>> {
let descriptor_end = Self::HEADER_SIZE + self.program_info_length() as usize;
if descriptor_end > self.data.len() {
warn!(
"program_info_length={} extends beyond end of PMT section (section_length={})",
self.program_info_length(),
self.data.len()
);
StreamInfoIter::new(&self.data[0..0])
} else {
StreamInfoIter::new(&self.data[descriptor_end..])
}
}
}
struct StreamInfoIter<'buf> {
buf: &'buf [u8],
}
impl<'buf> StreamInfoIter<'buf> {
fn new(buf: &'buf [u8]) -> StreamInfoIter<'buf> {
StreamInfoIter { buf }
}
}
impl<'buf> Iterator for StreamInfoIter<'buf> {
type Item = StreamInfo<'buf>;
fn next(&mut self) -> Option<Self::Item> {
if self.buf.is_empty() {
return None;
}
if let Some((stream_info, info_len)) = StreamInfo::from_bytes(self.buf) {
self.buf = &self.buf[info_len..];
Some(stream_info)
} else {
None
}
}
}
pub struct StreamInfo<'buf> {
data: &'buf [u8],
}
impl<'buf> StreamInfo<'buf> {
const HEADER_SIZE: usize = 5;
fn from_bytes(data: &'buf [u8]) -> Option<(StreamInfo<'buf>, usize)> {
if data.len() < Self::HEADER_SIZE {
warn!(
"only {} bytes remaining for stream info, at least {} required {:?}",
data.len(),
Self::HEADER_SIZE,
data
);
return None;
}
let result = StreamInfo { data };
let descriptor_end = Self::HEADER_SIZE + result.es_info_length() as usize;
if descriptor_end > data.len() {
warn!(
"PMT section of size {} is not large enough to contain es_info_length of {}",
data.len(),
result.es_info_length()
);
return None;
}
Some((result, descriptor_end))
}
pub fn stream_type(&self) -> StreamType {
self.data[0].into()
}
pub fn elementary_pid(&self) -> packet::Pid {
packet::Pid::new(u16::from(self.data[1] & 0b0001_1111) << 8 | u16::from(self.data[2]))
}
fn es_info_length(&self) -> u16 {
u16::from(self.data[3] & 0b0000_1111) << 8 | u16::from(self.data[4])
}
pub fn descriptors<Desc: descriptor::Descriptor<'buf> + 'buf>(
&self,
) -> impl Iterator<Item = Result<Desc, descriptor::DescriptorError>> + 'buf {
let descriptor_end = Self::HEADER_SIZE + self.es_info_length() as usize;
descriptor::DescriptorIter::new(&self.data[Self::HEADER_SIZE..descriptor_end])
}
}
impl<'buf> fmt::Debug for StreamInfo<'buf> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
f.debug_struct("StreamInfo")
.field("stream_type", &self.stream_type())
.field("elementary_pid", &self.elementary_pid())
.field("descriptors", &StreamInfoDescriptorsDebug(self))
.finish()
}
}
struct StreamInfoDescriptorsDebug<'buf>(&'buf StreamInfo<'buf>);
impl<'buf> fmt::Debug for StreamInfoDescriptorsDebug<'buf> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
f.debug_list()
.entries(self.0.descriptors::<descriptor::CoreDescriptors<'buf>>())
.finish()
}
}
#[cfg(test)]
mod test {
use crate::demultiplex::test::make_test_data;
use crate::demultiplex::DemuxError;
use crate::descriptor::CoreDescriptors;
use crate::psi::pmt::PmtSection;
use assert_matches::assert_matches;
use bitstream_io::BitWrite;
use hex_literal::hex;
#[test]
fn debug_does_not_panic() {
let data = hex!("fd4df0001bfd4df00652010b70010411fd4ef01152010c0a04656e67007c025a007f02068211fd52f01252010d0a04656e67037c035880477f02060606fd51f00d5201055908656e671000010001");
let section = PmtSection::from_bytes(&data).unwrap();
assert!(!format!("{:?}", section).is_empty());
}
#[test]
fn far_too_small() {
let data = hex!("fd4df0");
assert_matches!(
PmtSection::from_bytes(&data),
Err(DemuxError::NotEnoughData {
expected: 4,
actual: 3,
..
})
);
}
#[test]
fn descriptors_dont_fit() {
let data = make_test_data(|w| {
w.write(3, 0)?; w.write(13, 123)?; w.write(4, 0)?; w.write(12, 1) });
assert!(PmtSection::from_bytes(&data).is_err());
}
#[test]
fn truncated_descriptor() {
let data = make_test_data(|w| {
w.write(3, 0)?; w.write(13, 4)?; w.write(4, 0)?; w.write(12, 0x1)?; w.write(8, 0x1) });
let sect = PmtSection::from_bytes(&data).unwrap();
let mut iter = sect.descriptors::<CoreDescriptors<'_>>();
assert_matches!(iter.next(), Some(Err(_)));
assert_matches!(iter.next(), None);
}
}