#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub enum RunningStatus {
Undefined,
NotRunning,
StartsInAFewSeconds,
Pausing,
Running,
ServiceOffAir,
Reserved(u8),
}
impl RunningStatus {
#[must_use]
pub fn from_u8(v: u8) -> Self {
match v & 0x07 {
0 => Self::Undefined,
1 => Self::NotRunning,
2 => Self::StartsInAFewSeconds,
3 => Self::Pausing,
4 => Self::Running,
5 => Self::ServiceOffAir,
r => Self::Reserved(r),
}
}
#[must_use]
pub const fn to_u8(self) -> u8 {
match self {
Self::Undefined => 0,
Self::NotRunning => 1,
Self::StartsInAFewSeconds => 2,
Self::Pausing => 3,
Self::Running => 4,
Self::ServiceOffAir => 5,
Self::Reserved(v) => v & 0x07,
}
}
#[must_use]
pub fn name(self) -> &'static str {
match self {
Self::Undefined => "undefined",
Self::NotRunning => "not running",
Self::StartsInAFewSeconds => "starts in a few seconds",
Self::Pausing => "pausing",
Self::Running => "running",
Self::ServiceOffAir => "service off-air",
Self::Reserved(_) => "reserved",
}
}
}
broadcast_common::impl_spec_display!(RunningStatus, Reserved);
#[cfg(test)]
mod running_status_tests {
use super::*;
#[test]
fn from_u8_maps_known_values() {
assert_eq!(RunningStatus::from_u8(0), RunningStatus::Undefined);
assert_eq!(RunningStatus::from_u8(1), RunningStatus::NotRunning);
assert_eq!(
RunningStatus::from_u8(2),
RunningStatus::StartsInAFewSeconds
);
assert_eq!(RunningStatus::from_u8(3), RunningStatus::Pausing);
assert_eq!(RunningStatus::from_u8(4), RunningStatus::Running);
assert_eq!(RunningStatus::from_u8(5), RunningStatus::ServiceOffAir);
assert_eq!(RunningStatus::from_u8(6), RunningStatus::Reserved(6));
assert_eq!(RunningStatus::from_u8(7), RunningStatus::Reserved(7));
}
#[test]
fn to_u8_from_u8_round_trips_all_byte_values() {
for v in 0u8..=0xFFu8 {
assert_eq!(RunningStatus::to_u8(RunningStatus::from_u8(v)), v & 0x07);
}
}
#[test]
fn name_returns_known_strings() {
assert_eq!(RunningStatus::Undefined.name(), "undefined");
assert_eq!(RunningStatus::NotRunning.name(), "not running");
assert_eq!(RunningStatus::Running.name(), "running");
assert_eq!(RunningStatus::ServiceOffAir.name(), "service off-air");
assert_eq!(RunningStatus::Reserved(6).name(), "reserved");
}
#[test]
fn running_status_wire_to_name() {
assert_eq!(RunningStatus::from_u8(4).name(), "running");
assert_eq!(RunningStatus::from_u8(2).name(), "starts in a few seconds");
assert_eq!(RunningStatus::from_u8(0).name(), "undefined");
}
}
#[cfg(test)]
pub(crate) const SECTION_B1_FLAGS_PSI: u8 = 0xB0;
#[cfg(test)]
pub(crate) const SECTION_B1_FLAGS_DVB: u8 = 0xF0;
pub(crate) const SECTION_B1_SSI: u8 = 0x80;
pub(crate) const SECTION_B1_PRIVATE_INDICATOR: u8 = 0x40;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub struct SectionBits {
pub section_syntax_indicator: bool,
pub private_indicator: bool,
pub reserved_b1: u8,
pub reserved_b5: u8,
}
pub(crate) const RESERVED_2: u8 = 0x03;
impl SectionBits {
#[must_use]
pub const fn psi() -> Self {
Self {
section_syntax_indicator: true,
private_indicator: false,
reserved_b1: RESERVED_2,
reserved_b5: RESERVED_2,
}
}
#[must_use]
pub const fn dvb() -> Self {
Self {
section_syntax_indicator: true,
private_indicator: true,
reserved_b1: RESERVED_2,
reserved_b5: RESERVED_2,
}
}
#[must_use]
pub const fn short_form(mut self) -> Self {
self.section_syntax_indicator = false;
self
}
#[must_use]
pub(crate) fn parse(b1: u8, b5: Option<u8>) -> Self {
Self {
section_syntax_indicator: b1 & SECTION_B1_SSI != 0,
private_indicator: b1 & B1_PRIVATE_INDICATOR != 0,
reserved_b1: (b1 >> B1_RESERVED_SHIFT) & RESERVED_2,
reserved_b5: b5.map_or(RESERVED_2, |b| b >> B5_RESERVED_SHIFT),
}
}
pub(crate) fn b1(&self) -> crate::Result<u8> {
b1_of(
self.section_syntax_indicator,
self.private_indicator,
self.reserved_b1,
)
}
pub(crate) fn b5(&self, version_number: u8, current_next_indicator: bool) -> crate::Result<u8> {
b5_of(self.reserved_b5, version_number, current_next_indicator)
}
}
pub(crate) fn b1_of(
section_syntax_indicator: bool,
private_indicator: bool,
reserved_b1: u8,
) -> crate::Result<u8> {
Ok((u8::from(section_syntax_indicator) * SECTION_B1_SSI)
| (u8::from(private_indicator) * B1_PRIVATE_INDICATOR)
| (broadcast_common::len::fit_mask(reserved_b1, RESERVED_2, "reserved")?
<< B1_RESERVED_SHIFT))
}
pub(crate) fn b5_of(
reserved_b5: u8,
version_number: u8,
current_next_indicator: bool,
) -> crate::Result<u8> {
Ok(
(broadcast_common::len::fit_mask(reserved_b5, RESERVED_2, "reserved")?
<< B5_RESERVED_SHIFT)
| (broadcast_common::len::fit_mask(version_number, B5_VERSION_MASK, "version_number")?
<< 1)
| u8::from(current_next_indicator),
)
}
#[must_use]
pub(crate) fn b1_reserved_of(b1: u8) -> u8 {
(b1 >> B1_RESERVED_SHIFT) & RESERVED_2
}
#[must_use]
pub(crate) fn b5_reserved_of(b5: u8) -> u8 {
b5 >> B5_RESERVED_SHIFT
}
impl Default for SectionBits {
fn default() -> Self {
Self::dvb()
}
}
const B1_PRIVATE_INDICATOR: u8 = 0x40;
const B1_RESERVED_SHIFT: u32 = 4;
const B5_RESERVED_SHIFT: u32 = 6;
pub(crate) fn check_section_length(
bytes_len: usize,
header_len: usize,
section_length: usize,
min_total: usize,
) -> crate::Result<usize> {
let total = header_len + section_length;
if bytes_len < total || total < min_total {
return Err(crate::error::Error::SectionLengthOverflow {
declared: section_length,
available: bytes_len.saturating_sub(header_len),
});
}
Ok(total)
}
pub(crate) const CRC_LEN: usize = 4;
pub(crate) fn check_loop_reaches_crc(
loop_end: usize,
crc_pos: usize,
what: &'static str,
) -> crate::Result<()> {
if loop_end != crc_pos {
return Err(crate::error::Error::BufferTooShort(
broadcast_common::error::BufferTooShort {
need: crc_pos.saturating_sub(loop_end),
have: 0,
what,
},
));
}
Ok(())
}
pub(crate) fn write_crc(buf: &mut [u8], crc_pos: usize) {
let crc = broadcast_common::crc32_mpeg2::compute(&buf[..crc_pos]);
buf[crc_pos..crc_pos + CRC_LEN].copy_from_slice(&crc.to_be_bytes());
}
pub(crate) fn write_section_length(buf: &mut [u8], body_len: usize) -> crate::Result<()> {
let fitted = broadcast_common::len::fit_bits(body_len as u64, 12, "section_length")?;
buf[1] = (buf[1] & 0xF0) | ((fitted >> 8) as u8);
buf[2] = fitted as u8;
Ok(())
}
pub(crate) const SECTION_LENGTH_HI_MASK: u8 = 0x0F;
pub(crate) const B5_VERSION_MASK: u8 = 0x1F;
pub(crate) const B5_CNI: u8 = 0x01;
pub(crate) const PID_LO_MASK: u8 = 0x1F;
pub(crate) const DESC_LOOP_LEN_HI_RESERVED_BITS: u8 = 0xF0;
pub(crate) const RUNNING_STATUS_MASK: u8 = 0x07;
pub(crate) const FREE_CA_MODE: u8 = 0x10;
#[must_use]
pub(crate) fn running_status_of(status_and_len_hi: u8) -> u8 {
(status_and_len_hi >> 5) & RUNNING_STATUS_MASK
}
#[must_use]
pub(crate) fn free_ca_mode_of(status_and_len_hi: u8) -> bool {
(status_and_len_hi & FREE_CA_MODE) != 0
}
#[must_use]
pub(crate) fn dll_from_status(status_and_len_hi: u8, lo: u8) -> usize {
(((status_and_len_hi & SECTION_LENGTH_HI_MASK) as usize) << 8) | usize::from(lo)
}
#[must_use]
pub(crate) fn status_byte(running_status: u8, free_ca_mode: bool, dll_hi: u8) -> u8 {
((running_status & RUNNING_STATUS_MASK) << 5)
| (u8::from(free_ca_mode) * FREE_CA_MODE)
| (dll_hi & SECTION_LENGTH_HI_MASK)
}
#[cfg(test)]
pub(crate) fn version_byte(version_number: u8, current_next_indicator: bool) -> u8 {
b5_of(RESERVED_2, version_number, current_next_indicator).expect("test version fits 5 bits")
}
#[must_use]
pub(crate) fn section_length_of(bytes: &[u8]) -> usize {
((usize::from(bytes[1] & SECTION_LENGTH_HI_MASK)) << 8) | usize::from(bytes[2])
}
#[must_use]
pub(crate) fn version_number_of(flags_byte: u8) -> u8 {
(flags_byte >> 1) & B5_VERSION_MASK
}
#[must_use]
pub(crate) fn current_next_of(flags_byte: u8) -> bool {
(flags_byte & B5_CNI) != 0
}
#[must_use]
pub(crate) fn pid_of(hi: u8, lo: u8) -> mpeg_ts::pid::Pid {
mpeg_ts::pid::Pid::new(((u16::from(hi & PID_LO_MASK)) << 8) | u16::from(lo))
}
#[cfg(test)]
pub(crate) fn write_pid(buf: &mut [u8], pid: mpeg_ts::pid::Pid) {
write_pid_r(buf, pid, RESERVED_3).expect("RESERVED_3 fits 3 bits");
}
pub const RESERVED_3: u8 = 0x07;
pub const RESERVED_4: u8 = 0x0F;
pub(crate) const RESERVED_5: u8 = 0x1F;
pub(crate) const RESERVED_6: u8 = 0x3F;
const PID_RESERVED_SHIFT: u32 = 5;
const LEN_RESERVED_SHIFT: u32 = 4;
#[must_use]
pub(crate) fn pid_reserved_of(hi: u8) -> u8 {
hi >> PID_RESERVED_SHIFT
}
pub(crate) fn write_pid_r(
buf: &mut [u8],
pid: mpeg_ts::pid::Pid,
reserved: u8,
) -> crate::Result<()> {
let pid = pid.value();
buf[0] = (broadcast_common::len::fit_mask(reserved, RESERVED_3, "reserved")?
<< PID_RESERVED_SHIFT)
| ((pid >> 8) as u8 & PID_LO_MASK);
buf[1] = (pid & 0xFF) as u8;
Ok(())
}
#[must_use]
pub(crate) fn len_reserved_of(hi: u8) -> u8 {
hi >> LEN_RESERVED_SHIFT
}
pub(crate) fn write_desc_loop_len_r(buf: &mut [u8], len: usize, reserved: u8) -> crate::Result<()> {
let fitted = broadcast_common::len::fit_bits(len as u64, 12, "descriptors_length")?;
buf[0] = (broadcast_common::len::fit_mask(reserved, RESERVED_4, "reserved")?
<< LEN_RESERVED_SHIFT)
| ((fitted >> 8) as u8);
buf[1] = fitted as u8;
Ok(())
}
#[must_use]
pub(crate) fn desc_loop_len_of(hi: u8, lo: u8) -> usize {
(((hi & !DESC_LOOP_LEN_HI_RESERVED_BITS) as usize) << 8) | usize::from(lo)
}
pub(crate) const LOW_NIBBLE_MASK: u8 = 0x0F;
#[cfg(test)]
pub(crate) fn push_section_header(v: &mut Vec<u8>, flags: u8, len: usize) {
v.push(flags | ((len >> 8) as u8 & SECTION_LENGTH_HI_MASK));
v.push((len & 0xFF) as u8);
}
#[cfg(test)]
pub(crate) fn push_desc_loop_len(v: &mut Vec<u8>, len: usize) {
v.push(DESC_LOOP_LEN_HI_RESERVED_BITS | ((len >> 8) as u8 & SECTION_LENGTH_HI_MASK));
v.push((len & 0xFF) as u8);
}
#[cfg(test)]
pub(crate) fn push_status_and_len(v: &mut Vec<u8>, running_status: u8, free_ca: bool, len: usize) {
v.push(status_byte(running_status, free_ca, (len >> 8) as u8));
v.push((len & 0xFF) as u8);
}
pub mod any;
pub use any::AnyTableSection;
pub mod registry;
pub use registry::{TableObject, TableRegistry};
mod descriptor_section;
mod ts_loop_section;
pub use ts_loop_section::TransportStreamEntry;
mod real_time_parameters;
mod target_operational_loop;
pub use target_operational_loop::TargetOperationalLoop;
pub mod ait;
pub mod bat;
pub mod cat;
pub mod cit;
pub mod container;
pub mod dit;
pub mod downloadable_font_info;
pub mod dsmcc;
pub mod eit;
pub mod green_access_unit_section;
pub mod int;
pub mod iso14496_section;
pub mod metadata_section;
pub mod mpe;
pub mod mpe_fec;
pub mod mpe_ifec;
pub mod nit;
pub mod pat;
pub mod pmt;
pub mod protection_message;
pub mod rct;
pub mod rnt;
pub mod rst;
pub mod sat;
pub mod sdt;
pub mod sit;
pub mod st;
pub mod tdt;
pub mod tot;
pub mod tsdt;
pub mod unt;