use crate::bytes;
use crate::error::BdError;
use crate::stream::{
StreamKind, TsAspectRatio, TsAudioStream, TsChannelLayout, TsFrameRate, TsGraphicsStream,
TsSampleRate, TsStream, TsStreamType, TsTextStream, TsVideoFormat, TsVideoStream,
};
pub mod chapters;
pub mod clpi;
pub mod disc;
pub mod interleaved;
pub mod m2ts;
pub mod measured;
pub mod mpls;
pub mod order;
pub mod progress;
pub mod shortfall;
pub(crate) fn byte(buf: &[u8], off: usize) -> Result<u8, BdError> {
bytes::read_u8(buf, off).ok_or(BdError::UnexpectedEof)
}
pub(crate) fn u16_be(buf: &[u8], off: usize) -> Result<u16, BdError> {
bytes::read_u16_be(buf, off).ok_or(BdError::UnexpectedEof)
}
pub(crate) fn u32_be(buf: &[u8], off: usize) -> Result<u32, BdError> {
bytes::read_u32_be(buf, off).ok_or(BdError::UnexpectedEof)
}
pub(crate) fn u32_off(buf: &[u8], off: usize) -> Result<usize, BdError> {
let value = bytes::read_u32_be(buf, off).ok_or(BdError::UnexpectedEof)?;
Ok(usize::try_from(value).unwrap_or(usize::MAX))
}
pub(crate) fn ascii(buf: &[u8], off: usize, count: usize) -> Result<String, BdError> {
bytes::read_ascii(buf, off, count).ok_or(BdError::UnexpectedEof)
}
pub(crate) fn build_coded_stream(
data: &[u8],
coding: usize,
stream_type: TsStreamType,
aspect: Option<usize>,
) -> Result<Option<TsStream>, BdError> {
Ok(match stream_type.kind() {
StreamKind::Video => {
let format = byte(data, coding)?;
let mut stream = TsVideoStream::default();
stream.set_video_format(TsVideoFormat::from_u8(format.wrapping_shr(4)));
stream.set_frame_rate(TsFrameRate::from_u8(format & 0x0F));
if let Some(aspect) = aspect {
stream.aspect_ratio = TsAspectRatio::from_u8(byte(data, aspect)?.wrapping_shr(4));
}
Some(TsStream::Video(stream))
}
StreamKind::Audio => {
let format = byte(data, coding)?;
let language = ascii(data, coding.saturating_add(1), 3)?;
let mut stream = TsAudioStream {
channel_layout: TsChannelLayout::from_u8(format.wrapping_shr(4)),
sample_rate: TsAudioStream::convert_sample_rate(TsSampleRate::from_u8(
format & 0x0F,
)),
..TsAudioStream::default()
};
stream.base.set_language_code(&language);
Some(TsStream::Audio(stream))
}
StreamKind::Graphics => {
let language = ascii(data, coding, 3)?;
let mut stream = TsGraphicsStream::default();
stream.base.set_language_code(&language);
Some(TsStream::Graphics(stream))
}
StreamKind::Text => {
let language = ascii(data, coding.saturating_add(1), 3)?;
let mut stream = TsTextStream::default();
stream.base.set_language_code(&language);
Some(TsStream::Text(stream))
}
StreamKind::Unknown => None,
})
}
#[cfg(test)]
mod tests {
use super::{ascii, byte, u16_be, u32_be, u32_off};
#[test]
fn helpers_read_in_bounds_values() {
let buf = [0x12_u8, 0x34, 0x56, 0x78, b'e', b'n', b'g'];
assert_eq!(byte(&buf, 0).ok(), Some(0x12));
assert_eq!(u16_be(&buf, 0).ok(), Some(0x1234));
assert_eq!(u32_be(&buf, 0).ok(), Some(0x1234_5678));
assert_eq!(u32_off(&buf, 0).ok(), Some(0x1234_5678));
assert_eq!(ascii(&buf, 4, 3).ok().as_deref(), Some("eng"));
}
#[test]
fn helpers_map_short_reads_to_eof() {
let buf = [0x12_u8, 0x34];
let eof = "unexpected end of input";
assert_eq!(byte(&buf, 2).unwrap_err().to_string(), eof);
assert_eq!(u16_be(&buf, 1).unwrap_err().to_string(), eof);
assert_eq!(u32_be(&buf, 0).unwrap_err().to_string(), eof);
assert_eq!(u32_off(&buf, 0).unwrap_err().to_string(), eof);
assert_eq!(ascii(&buf, 1, 3).unwrap_err().to_string(), eof);
}
}