Skip to main content

scte35_splice/commands/
splice_schedule.rs

1//! splice_schedule() — ANSI/SCTE 35 2023r1 §9.7.2, Table 9
2//! (splice_command_type 0x04).
3//!
4//! A schedule of future splice events, each timed by a 32-bit `utc_splice_time`
5//! (GPS-epoch seconds, §9.7.2.1). Component Splice Mode
6//! (`program_splice_flag == 0`) is deprecated but parsed/serialized losslessly.
7
8use alloc::vec::Vec;
9
10use crate::error::{Error, Result};
11use crate::time::BreakDuration;
12use crate::traits::CommandDef;
13use broadcast_common::{Parse, Serialize};
14
15/// `splice_command_type` for splice_schedule (§9.6.1, Table 7).
16pub const COMMAND_TYPE: u8 = 0x04;
17
18/// One component entry in a scheduled event's deprecated component loop.
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20#[cfg_attr(feature = "serde", derive(serde::Serialize))]
21pub struct SpliceScheduleComponent {
22    /// 8-bit `component_tag`.
23    pub component_tag: u8,
24    /// 32-bit `utc_splice_time` (GPS-epoch seconds).
25    pub utc_splice_time: u32,
26}
27
28/// One scheduled splice event.
29#[derive(Debug, Clone, PartialEq, Eq)]
30#[cfg_attr(feature = "serde", derive(serde::Serialize))]
31pub struct SpliceScheduleEvent {
32    /// 32-bit unique `splice_event_id` (§9.9.3).
33    pub splice_event_id: u32,
34    /// When `true`, the named event has been cancelled.
35    pub splice_event_cancel_indicator: bool,
36    /// `event_id_compliance_flag`: `false` = event id complies with §9.9.3.
37    pub event_id_compliance_flag: bool,
38    /// `out_of_network_indicator` (present only when not cancelled).
39    pub out_of_network_indicator: bool,
40    /// `program_splice_flag`: `true` = Program Splice Mode.
41    pub program_splice_flag: bool,
42    /// Program-mode `utc_splice_time`, present when `program_splice_flag == 1`.
43    pub utc_splice_time: Option<u32>,
44    /// Component-mode entries, present when `program_splice_flag == 0`
45    /// (deprecated).
46    pub components: Vec<SpliceScheduleComponent>,
47    /// `break_duration()`, present when `duration_flag == 1`.
48    pub break_duration: Option<BreakDuration>,
49    /// `unique_program_id`.
50    pub unique_program_id: u16,
51    /// `avail_num`.
52    pub avail_num: u8,
53    /// `avails_expected`.
54    pub avails_expected: u8,
55}
56
57impl Default for SpliceScheduleEvent {
58    fn default() -> Self {
59        Self {
60            splice_event_id: 0,
61            splice_event_cancel_indicator: false,
62            event_id_compliance_flag: true,
63            out_of_network_indicator: false,
64            program_splice_flag: true,
65            utc_splice_time: None,
66            components: Vec::new(),
67            break_duration: None,
68            unique_program_id: 0,
69            avail_num: 0,
70            avails_expected: 0,
71        }
72    }
73}
74
75impl SpliceScheduleEvent {
76    fn serialized_len(&self) -> usize {
77        // splice_event_id (4) + flags byte (1).
78        let mut len = 5;
79        if self.splice_event_cancel_indicator {
80            return len;
81        }
82        len += 1; // out_of_network/program/duration/reserved byte
83        if self.program_splice_flag {
84            len += 4; // utc_splice_time
85        } else {
86            len += 1; // component_count
87            len += self.components.len() * 5; // tag (1) + utc_splice_time (4)
88        }
89        if self.break_duration.is_some() {
90            len += BreakDuration::LEN;
91        }
92        len += 4; // unique_program_id (2) + avail_num (1) + avails_expected (1)
93        len
94    }
95
96    fn parse_at(bytes: &[u8], pos: &mut usize) -> Result<Self> {
97        let (hdr, _) = bytes[*pos..]
98            .split_first_chunk::<5>()
99            .ok_or(Error::BufferTooShort {
100                need: *pos + 5,
101                have: bytes.len(),
102                what: "splice_schedule event header",
103            })?;
104        let splice_event_id = u32::from_be_bytes([hdr[0], hdr[1], hdr[2], hdr[3]]);
105        let b = hdr[4];
106        *pos += 5;
107        let cancel = b & 0x80 != 0;
108        let compliance = b & 0x40 != 0;
109
110        let mut ev = Self {
111            splice_event_id,
112            splice_event_cancel_indicator: cancel,
113            event_id_compliance_flag: compliance,
114            ..Self::default()
115        };
116        if cancel {
117            return Ok(ev);
118        }
119
120        if bytes.len() < *pos + 1 {
121            return Err(Error::BufferTooShort {
122                need: *pos + 1,
123                have: bytes.len(),
124                what: "splice_schedule event flags",
125            });
126        }
127        let flags = bytes[*pos];
128        *pos += 1;
129        ev.out_of_network_indicator = flags & 0x80 != 0;
130        ev.program_splice_flag = flags & 0x40 != 0;
131        let duration_flag = flags & 0x20 != 0;
132
133        if ev.program_splice_flag {
134            let (t, _) = bytes[*pos..]
135                .split_first_chunk::<4>()
136                .ok_or(Error::BufferTooShort {
137                    need: *pos + 4,
138                    have: bytes.len(),
139                    what: "splice_schedule utc_splice_time",
140                })?;
141            ev.utc_splice_time = Some(u32::from_be_bytes(*t));
142            *pos += 4;
143        } else {
144            if bytes.len() < *pos + 1 {
145                return Err(Error::BufferTooShort {
146                    need: *pos + 1,
147                    have: bytes.len(),
148                    what: "splice_schedule component_count",
149                });
150            }
151            let count = bytes[*pos] as usize;
152            *pos += 1;
153            for _ in 0..count {
154                let (comp, _) =
155                    bytes[*pos..]
156                        .split_first_chunk::<5>()
157                        .ok_or(Error::BufferTooShort {
158                            need: *pos + 5,
159                            have: bytes.len(),
160                            what: "splice_schedule component",
161                        })?;
162                let component_tag = comp[0];
163                let utc_splice_time = u32::from_be_bytes([comp[1], comp[2], comp[3], comp[4]]);
164                *pos += 5;
165                ev.components.push(SpliceScheduleComponent {
166                    component_tag,
167                    utc_splice_time,
168                });
169            }
170        }
171
172        if duration_flag {
173            ev.break_duration = Some(BreakDuration::parse(&bytes[*pos..])?);
174            *pos += BreakDuration::LEN;
175        }
176
177        let (trailer, _) = bytes[*pos..]
178            .split_first_chunk::<4>()
179            .ok_or(Error::BufferTooShort {
180                need: *pos + 4,
181                have: bytes.len(),
182                what: "splice_schedule event trailer",
183            })?;
184        ev.unique_program_id = u16::from_be_bytes([trailer[0], trailer[1]]);
185        ev.avail_num = trailer[2];
186        ev.avails_expected = trailer[3];
187        *pos += 4;
188        Ok(ev)
189    }
190
191    fn serialize_at(&self, buf: &mut [u8], pos: &mut usize) -> Result<()> {
192        buf[*pos..*pos + 4].copy_from_slice(&self.splice_event_id.to_be_bytes());
193        // cancel (1) + compliance (1) + 6 reserved bits = 1.
194        buf[*pos + 4] = (u8::from(self.splice_event_cancel_indicator) << 7)
195            | (u8::from(self.event_id_compliance_flag) << 6)
196            | 0x3F;
197        *pos += 5;
198        if self.splice_event_cancel_indicator {
199            return Ok(());
200        }
201
202        let duration_flag = self.break_duration.is_some();
203        // out_of_network (1) + program_splice (1) + duration (1) + 5 reserved.
204        buf[*pos] = (u8::from(self.out_of_network_indicator) << 7)
205            | (u8::from(self.program_splice_flag) << 6)
206            | (u8::from(duration_flag) << 5)
207            | 0x1F;
208        *pos += 1;
209
210        if self.program_splice_flag {
211            buf[*pos..*pos + 4].copy_from_slice(&self.utc_splice_time.unwrap_or(0).to_be_bytes());
212            *pos += 4;
213        } else {
214            buf[*pos] = self.components.len() as u8;
215            *pos += 1;
216            for c in &self.components {
217                buf[*pos] = c.component_tag;
218                buf[*pos + 1..*pos + 5].copy_from_slice(&c.utc_splice_time.to_be_bytes());
219                *pos += 5;
220            }
221        }
222
223        if let Some(bd) = self.break_duration {
224            *pos += bd.serialize_into(&mut buf[*pos..])?;
225        }
226
227        buf[*pos..*pos + 2].copy_from_slice(&self.unique_program_id.to_be_bytes());
228        buf[*pos + 2] = self.avail_num;
229        buf[*pos + 3] = self.avails_expected;
230        *pos += 4;
231        Ok(())
232    }
233}
234
235/// splice_schedule() — §9.7.2, Table 9.
236#[derive(Debug, Clone, PartialEq, Eq, Default)]
237#[cfg_attr(feature = "serde", derive(serde::Serialize))]
238pub struct SpliceSchedule {
239    /// The scheduled events (the `splice_count` loop).
240    pub events: Vec<SpliceScheduleEvent>,
241}
242
243impl<'a> Parse<'a> for SpliceSchedule {
244    type Error = Error;
245    fn parse(bytes: &'a [u8]) -> Result<Self> {
246        if bytes.is_empty() {
247            return Err(Error::BufferTooShort {
248                need: 1,
249                have: 0,
250                what: "splice_schedule splice_count",
251            });
252        }
253        let count = bytes[0] as usize;
254        let mut pos = 1;
255        let mut events = Vec::with_capacity(count);
256        for _ in 0..count {
257            events.push(SpliceScheduleEvent::parse_at(bytes, &mut pos)?);
258        }
259        Ok(Self { events })
260    }
261}
262
263impl Serialize for SpliceSchedule {
264    type Error = Error;
265    fn serialized_len(&self) -> usize {
266        1 + self
267            .events
268            .iter()
269            .map(SpliceScheduleEvent::serialized_len)
270            .sum::<usize>()
271    }
272    fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
273        let need = self.serialized_len();
274        if buf.len() < need {
275            return Err(Error::OutputBufferTooSmall {
276                need,
277                have: buf.len(),
278            });
279        }
280        buf[0] = self.events.len() as u8;
281        let mut pos = 1;
282        for ev in &self.events {
283            ev.serialize_at(buf, &mut pos)?;
284        }
285        Ok(need)
286    }
287}
288
289impl<'a> CommandDef<'a> for SpliceSchedule {
290    const COMMAND_TYPE: u8 = COMMAND_TYPE;
291    const NAME: &'static str = "SPLICE_SCHEDULE";
292}
293
294#[cfg(test)]
295mod tests {
296    use super::*;
297
298    fn rt(cmd: &SpliceSchedule) {
299        let bytes = cmd.to_bytes();
300        assert_eq!(bytes.len(), cmd.serialized_len());
301        let back = SpliceSchedule::parse(&bytes).unwrap();
302        assert_eq!(*cmd, back);
303        assert_eq!(back.to_bytes(), bytes);
304    }
305
306    #[test]
307    fn round_trip_empty_schedule() {
308        rt(&SpliceSchedule::default());
309    }
310
311    #[test]
312    fn round_trip_program_events() {
313        rt(&SpliceSchedule {
314            events: vec![
315                SpliceScheduleEvent {
316                    splice_event_id: 1,
317                    out_of_network_indicator: true,
318                    utc_splice_time: Some(0x1234_5678),
319                    break_duration: Some(BreakDuration {
320                        auto_return: false,
321                        duration: 100,
322                    }),
323                    unique_program_id: 5,
324                    avail_num: 1,
325                    avails_expected: 2,
326                    ..Default::default()
327                },
328                SpliceScheduleEvent {
329                    splice_event_id: 2,
330                    splice_event_cancel_indicator: true,
331                    ..Default::default()
332                },
333            ],
334        });
335    }
336
337    #[test]
338    fn round_trip_component_event() {
339        rt(&SpliceSchedule {
340            events: vec![SpliceScheduleEvent {
341                splice_event_id: 3,
342                program_splice_flag: false,
343                components: vec![
344                    SpliceScheduleComponent {
345                        component_tag: 1,
346                        utc_splice_time: 0xAABBCCDD,
347                    },
348                    SpliceScheduleComponent {
349                        component_tag: 2,
350                        utc_splice_time: 0x11223344,
351                    },
352                ],
353                unique_program_id: 9,
354                ..Default::default()
355            }],
356        });
357    }
358}