Skip to main content

scte35_splice/commands/
splice_insert.rs

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