use alloc::vec::Vec;
use crate::error::{Error, Result};
use crate::time::BreakDuration;
use crate::traits::CommandDef;
use dvb_common::{Parse, Serialize};
pub const COMMAND_TYPE: u8 = 0x04;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct SpliceScheduleComponent {
pub component_tag: u8,
pub utc_splice_time: u32,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct SpliceScheduleEvent {
pub splice_event_id: u32,
pub splice_event_cancel_indicator: bool,
pub event_id_compliance_flag: bool,
pub out_of_network_indicator: bool,
pub program_splice_flag: bool,
pub utc_splice_time: Option<u32>,
pub components: Vec<SpliceScheduleComponent>,
pub break_duration: Option<BreakDuration>,
pub unique_program_id: u16,
pub avail_num: u8,
pub avails_expected: u8,
}
impl Default for SpliceScheduleEvent {
fn default() -> Self {
Self {
splice_event_id: 0,
splice_event_cancel_indicator: false,
event_id_compliance_flag: true,
out_of_network_indicator: false,
program_splice_flag: true,
utc_splice_time: None,
components: Vec::new(),
break_duration: None,
unique_program_id: 0,
avail_num: 0,
avails_expected: 0,
}
}
}
impl SpliceScheduleEvent {
fn serialized_len(&self) -> usize {
let mut len = 5;
if self.splice_event_cancel_indicator {
return len;
}
len += 1; if self.program_splice_flag {
len += 4; } else {
len += 1; len += self.components.len() * 5; }
if self.break_duration.is_some() {
len += BreakDuration::LEN;
}
len += 4; len
}
fn parse_at(bytes: &[u8], pos: &mut usize) -> Result<Self> {
let (hdr, _) = bytes[*pos..]
.split_first_chunk::<5>()
.ok_or(Error::BufferTooShort {
need: *pos + 5,
have: bytes.len(),
what: "splice_schedule event header",
})?;
let splice_event_id = u32::from_be_bytes([hdr[0], hdr[1], hdr[2], hdr[3]]);
let b = hdr[4];
*pos += 5;
let cancel = b & 0x80 != 0;
let compliance = b & 0x40 != 0;
let mut ev = Self {
splice_event_id,
splice_event_cancel_indicator: cancel,
event_id_compliance_flag: compliance,
..Self::default()
};
if cancel {
return Ok(ev);
}
if bytes.len() < *pos + 1 {
return Err(Error::BufferTooShort {
need: *pos + 1,
have: bytes.len(),
what: "splice_schedule event flags",
});
}
let flags = bytes[*pos];
*pos += 1;
ev.out_of_network_indicator = flags & 0x80 != 0;
ev.program_splice_flag = flags & 0x40 != 0;
let duration_flag = flags & 0x20 != 0;
if ev.program_splice_flag {
let (t, _) = bytes[*pos..]
.split_first_chunk::<4>()
.ok_or(Error::BufferTooShort {
need: *pos + 4,
have: bytes.len(),
what: "splice_schedule utc_splice_time",
})?;
ev.utc_splice_time = Some(u32::from_be_bytes(*t));
*pos += 4;
} else {
if bytes.len() < *pos + 1 {
return Err(Error::BufferTooShort {
need: *pos + 1,
have: bytes.len(),
what: "splice_schedule component_count",
});
}
let count = bytes[*pos] as usize;
*pos += 1;
for _ in 0..count {
let (comp, _) =
bytes[*pos..]
.split_first_chunk::<5>()
.ok_or(Error::BufferTooShort {
need: *pos + 5,
have: bytes.len(),
what: "splice_schedule component",
})?;
let component_tag = comp[0];
let utc_splice_time = u32::from_be_bytes([comp[1], comp[2], comp[3], comp[4]]);
*pos += 5;
ev.components.push(SpliceScheduleComponent {
component_tag,
utc_splice_time,
});
}
}
if duration_flag {
ev.break_duration = Some(BreakDuration::parse(&bytes[*pos..])?);
*pos += BreakDuration::LEN;
}
let (trailer, _) = bytes[*pos..]
.split_first_chunk::<4>()
.ok_or(Error::BufferTooShort {
need: *pos + 4,
have: bytes.len(),
what: "splice_schedule event trailer",
})?;
ev.unique_program_id = u16::from_be_bytes([trailer[0], trailer[1]]);
ev.avail_num = trailer[2];
ev.avails_expected = trailer[3];
*pos += 4;
Ok(ev)
}
fn serialize_at(&self, buf: &mut [u8], pos: &mut usize) -> Result<()> {
buf[*pos..*pos + 4].copy_from_slice(&self.splice_event_id.to_be_bytes());
buf[*pos + 4] = (u8::from(self.splice_event_cancel_indicator) << 7)
| (u8::from(self.event_id_compliance_flag) << 6)
| 0x3F;
*pos += 5;
if self.splice_event_cancel_indicator {
return Ok(());
}
let duration_flag = self.break_duration.is_some();
buf[*pos] = (u8::from(self.out_of_network_indicator) << 7)
| (u8::from(self.program_splice_flag) << 6)
| (u8::from(duration_flag) << 5)
| 0x1F;
*pos += 1;
if self.program_splice_flag {
buf[*pos..*pos + 4].copy_from_slice(&self.utc_splice_time.unwrap_or(0).to_be_bytes());
*pos += 4;
} else {
buf[*pos] = self.components.len() as u8;
*pos += 1;
for c in &self.components {
buf[*pos] = c.component_tag;
buf[*pos + 1..*pos + 5].copy_from_slice(&c.utc_splice_time.to_be_bytes());
*pos += 5;
}
}
if let Some(bd) = self.break_duration {
*pos += bd.serialize_into(&mut buf[*pos..])?;
}
buf[*pos..*pos + 2].copy_from_slice(&self.unique_program_id.to_be_bytes());
buf[*pos + 2] = self.avail_num;
buf[*pos + 3] = self.avails_expected;
*pos += 4;
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct SpliceSchedule {
pub events: Vec<SpliceScheduleEvent>,
}
impl<'a> Parse<'a> for SpliceSchedule {
type Error = Error;
fn parse(bytes: &'a [u8]) -> Result<Self> {
if bytes.is_empty() {
return Err(Error::BufferTooShort {
need: 1,
have: 0,
what: "splice_schedule splice_count",
});
}
let count = bytes[0] as usize;
let mut pos = 1;
let mut events = Vec::with_capacity(count);
for _ in 0..count {
events.push(SpliceScheduleEvent::parse_at(bytes, &mut pos)?);
}
Ok(Self { events })
}
}
impl Serialize for SpliceSchedule {
type Error = Error;
fn serialized_len(&self) -> usize {
1 + self
.events
.iter()
.map(SpliceScheduleEvent::serialized_len)
.sum::<usize>()
}
fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
let need = self.serialized_len();
if buf.len() < need {
return Err(Error::OutputBufferTooSmall {
need,
have: buf.len(),
});
}
buf[0] = self.events.len() as u8;
let mut pos = 1;
for ev in &self.events {
ev.serialize_at(buf, &mut pos)?;
}
Ok(need)
}
}
impl<'a> CommandDef<'a> for SpliceSchedule {
const COMMAND_TYPE: u8 = COMMAND_TYPE;
const NAME: &'static str = "SPLICE_SCHEDULE";
}
#[cfg(test)]
mod tests {
use super::*;
fn rt(cmd: &SpliceSchedule) {
let bytes = cmd.to_bytes();
assert_eq!(bytes.len(), cmd.serialized_len());
let back = SpliceSchedule::parse(&bytes).unwrap();
assert_eq!(*cmd, back);
assert_eq!(back.to_bytes(), bytes);
}
#[test]
fn round_trip_empty_schedule() {
rt(&SpliceSchedule::default());
}
#[test]
fn round_trip_program_events() {
rt(&SpliceSchedule {
events: vec![
SpliceScheduleEvent {
splice_event_id: 1,
out_of_network_indicator: true,
utc_splice_time: Some(0x1234_5678),
break_duration: Some(BreakDuration {
auto_return: false,
duration: 100,
}),
unique_program_id: 5,
avail_num: 1,
avails_expected: 2,
..Default::default()
},
SpliceScheduleEvent {
splice_event_id: 2,
splice_event_cancel_indicator: true,
..Default::default()
},
],
});
}
#[test]
fn round_trip_component_event() {
rt(&SpliceSchedule {
events: vec![SpliceScheduleEvent {
splice_event_id: 3,
program_splice_flag: false,
components: vec![
SpliceScheduleComponent {
component_tag: 1,
utc_splice_time: 0xAABBCCDD,
},
SpliceScheduleComponent {
component_tag: 2,
utc_splice_time: 0x11223344,
},
],
unique_program_id: 9,
..Default::default()
}],
});
}
}