use alloc::vec::Vec;
use crate::error::{Error, Result};
use crate::time::{BreakDuration, SpliceTime};
use crate::traits::CommandDef;
use broadcast_common::{Parse, Serialize};
pub const COMMAND_TYPE: u8 = 0x05;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct SpliceInsertComponent {
pub component_tag: u8,
pub splice_time: Option<SpliceTime>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct SpliceInsert {
pub splice_event_id: u32,
pub splice_event_cancel_indicator: bool,
pub out_of_network_indicator: bool,
pub program_splice_flag: bool,
pub splice_immediate_flag: bool,
pub event_id_compliance_flag: bool,
pub splice_time: Option<SpliceTime>,
pub components: Vec<SpliceInsertComponent>,
pub break_duration: Option<BreakDuration>,
pub unique_program_id: u16,
pub avail_num: u8,
pub avails_expected: u8,
}
impl Default for SpliceInsert {
fn default() -> Self {
Self {
splice_event_id: 0,
splice_event_cancel_indicator: false,
out_of_network_indicator: false,
program_splice_flag: true,
splice_immediate_flag: false,
event_id_compliance_flag: true,
splice_time: None,
components: Vec::new(),
break_duration: None,
unique_program_id: 0,
avail_num: 0,
avails_expected: 0,
}
}
}
impl<'a> Parse<'a> for SpliceInsert {
type Error = Error;
fn parse(bytes: &'a [u8]) -> Result<Self> {
let (hdr, _) = bytes
.split_first_chunk::<5>()
.ok_or(Error::BufferTooShort {
need: 5,
have: bytes.len(),
what: "splice_insert header",
})?;
let splice_event_id = u32::from_be_bytes([hdr[0], hdr[1], hdr[2], hdr[3]]);
let cancel = hdr[4] & 0x80 != 0;
let mut pos = 5;
let mut out = Self {
splice_event_id,
splice_event_cancel_indicator: cancel,
program_splice_flag: true,
event_id_compliance_flag: true,
..Self::default()
};
if cancel {
return Ok(out);
}
if bytes.len() < pos + 1 {
return Err(Error::BufferTooShort {
need: pos + 1,
have: bytes.len(),
what: "splice_insert flags",
});
}
let flags = bytes[pos];
pos += 1;
out.out_of_network_indicator = flags & 0x80 != 0;
out.program_splice_flag = flags & 0x40 != 0;
let duration_flag = flags & 0x20 != 0;
out.splice_immediate_flag = flags & 0x10 != 0;
out.event_id_compliance_flag = flags & 0x08 != 0;
if out.program_splice_flag {
if !out.splice_immediate_flag {
let st = SpliceTime::parse(&bytes[pos..])?;
pos += st.serialized_len();
out.splice_time = Some(st);
}
} else {
if bytes.len() < pos + 1 {
return Err(Error::BufferTooShort {
need: pos + 1,
have: bytes.len(),
what: "splice_insert component_count",
});
}
let component_count = bytes[pos] as usize;
pos += 1;
for _ in 0..component_count {
if bytes.len() < pos + 1 {
return Err(Error::BufferTooShort {
need: pos + 1,
have: bytes.len(),
what: "splice_insert component_tag",
});
}
let component_tag = bytes[pos];
pos += 1;
let splice_time = if !out.splice_immediate_flag {
let st = SpliceTime::parse(&bytes[pos..])?;
pos += st.serialized_len();
Some(st)
} else {
None
};
out.components.push(SpliceInsertComponent {
component_tag,
splice_time,
});
}
}
if duration_flag {
let bd = BreakDuration::parse(&bytes[pos..])?;
pos += BreakDuration::LEN;
out.break_duration = Some(bd);
}
let (trailer, _) = bytes[pos..]
.split_first_chunk::<4>()
.ok_or(Error::BufferTooShort {
need: pos + 4,
have: bytes.len(),
what: "splice_insert trailer",
})?;
out.unique_program_id = u16::from_be_bytes([trailer[0], trailer[1]]);
out.avail_num = trailer[2];
out.avails_expected = trailer[3];
Ok(out)
}
}
impl Serialize for SpliceInsert {
type Error = Error;
fn serialized_len(&self) -> usize {
if self.splice_event_cancel_indicator {
return 5;
}
let mut len = 6; if self.program_splice_flag {
if !self.splice_immediate_flag {
len += self.splice_time.unwrap_or_default().serialized_len();
}
} else {
len += 1; for c in &self.components {
len += 1; if !self.splice_immediate_flag {
len += c.splice_time.unwrap_or_default().serialized_len();
}
}
}
if self.break_duration.is_some() {
len += BreakDuration::LEN;
}
len += 4; len
}
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..4].copy_from_slice(&self.splice_event_id.to_be_bytes());
buf[4] = (u8::from(self.splice_event_cancel_indicator) << 7) | 0x7F;
if self.splice_event_cancel_indicator {
return Ok(5);
}
let duration_flag = self.break_duration.is_some();
buf[5] = (u8::from(self.out_of_network_indicator) << 7)
| (u8::from(self.program_splice_flag) << 6)
| (u8::from(duration_flag) << 5)
| (u8::from(self.splice_immediate_flag) << 4)
| (u8::from(self.event_id_compliance_flag) << 3)
| 0x07;
let mut pos = 6;
if self.program_splice_flag {
if !self.splice_immediate_flag {
let st = self.splice_time.unwrap_or_default();
pos += st.serialize_into(&mut buf[pos..])?;
}
} else {
buf[pos] = self.components.len() as u8;
pos += 1;
for c in &self.components {
buf[pos] = c.component_tag;
pos += 1;
if !self.splice_immediate_flag {
let st = c.splice_time.unwrap_or_default();
pos += st.serialize_into(&mut buf[pos..])?;
}
}
}
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;
Ok(need)
}
}
impl<'a> CommandDef<'a> for SpliceInsert {
const COMMAND_TYPE: u8 = COMMAND_TYPE;
const NAME: &'static str = "SPLICE_INSERT";
}
#[cfg(test)]
mod tests {
use super::*;
fn rt(cmd: &SpliceInsert) {
let bytes = cmd.to_bytes();
assert_eq!(bytes.len(), cmd.serialized_len());
let back = SpliceInsert::parse(&bytes).unwrap();
assert_eq!(*cmd, back);
assert_eq!(back.to_bytes(), bytes);
}
#[test]
fn round_trip_cancel() {
rt(&SpliceInsert {
splice_event_id: 0xDEADBEEF,
splice_event_cancel_indicator: true,
..Default::default()
});
}
#[test]
fn round_trip_program_with_time_and_duration() {
rt(&SpliceInsert {
splice_event_id: 0x4800_0001,
out_of_network_indicator: true,
program_splice_flag: true,
splice_immediate_flag: false,
event_id_compliance_flag: false,
splice_time: Some(SpliceTime::with_pts(0x0_0123_4567)),
break_duration: Some(BreakDuration {
auto_return: true,
duration: 90_000 * 30,
}),
unique_program_id: 0x1234,
avail_num: 1,
avails_expected: 4,
..Default::default()
});
}
#[test]
fn round_trip_program_immediate() {
rt(&SpliceInsert {
splice_event_id: 0x6000_0009,
program_splice_flag: true,
splice_immediate_flag: true,
splice_time: None,
unique_program_id: 7,
..Default::default()
});
}
#[test]
fn round_trip_component_mode() {
rt(&SpliceInsert {
splice_event_id: 0xC000_0002,
program_splice_flag: false,
splice_immediate_flag: false,
components: vec![
SpliceInsertComponent {
component_tag: 1,
splice_time: Some(SpliceTime::with_pts(0x1000)),
},
SpliceInsertComponent {
component_tag: 2,
splice_time: Some(SpliceTime::default()),
},
],
unique_program_id: 9,
..Default::default()
});
}
}