1use crate::commands::AnyCommand;
19use crate::descriptors::{parse_loop, SpliceDescriptorIter};
20use crate::error::{Error, Result};
21use crate::time::PTS_MAX;
22use broadcast_common::{Parse, Serialize};
23
24pub const TABLE_ID: u8 = 0xFC;
26
27const FIXED_HEADER_LEN: usize = 14;
29
30const CRC_LEN: usize = 4;
32
33pub const TIER_IGNORE: u16 = 0x0FFF;
35
36#[derive(Debug, Clone, PartialEq, Eq)]
39#[cfg_attr(feature = "serde", derive(serde::Serialize))]
40pub struct ClearPayload<'a> {
41 pub command: AnyCommand<'a>,
43 pub descriptor_loop: &'a [u8],
46}
47
48#[derive(Debug, Clone, PartialEq, Eq)]
50#[cfg_attr(feature = "serde", derive(serde::Serialize))]
51pub struct SpliceInfoSection<'a> {
52 pub sap_type: u8,
54 pub protocol_version: u8,
56 pub encrypted_packet: bool,
58 pub encryption_algorithm: u8,
61 pub pts_adjustment: u64,
64 pub cw_index: u8,
66 pub tier: u16,
68 pub clear: Option<ClearPayload<'a>>,
71 pub encrypted_payload: Option<&'a [u8]>,
74}
75
76impl<'a> SpliceInfoSection<'a> {
77 #[must_use]
80 pub fn new_clear(command: AnyCommand<'a>, descriptor_loop: &'a [u8]) -> Self {
81 Self {
82 sap_type: 0x3,
83 protocol_version: 0,
84 encrypted_packet: false,
85 encryption_algorithm: 0,
86 pts_adjustment: 0,
87 cw_index: 0,
88 tier: 0,
89 clear: Some(ClearPayload {
90 command,
91 descriptor_loop,
92 }),
93 encrypted_payload: None,
94 }
95 }
96
97 #[must_use]
101 pub fn descriptors(&self) -> SpliceDescriptorIter<'a> {
102 match &self.clear {
103 Some(c) => parse_loop(c.descriptor_loop),
104 None => parse_loop(&[]),
105 }
106 }
107
108 #[must_use]
110 pub fn pts_adjustment_duration(&self) -> core::time::Duration {
111 crate::time::ticks_to_duration(self.pts_adjustment)
112 }
113
114 pub fn set_pts_adjustment_duration(&mut self, d: core::time::Duration) -> Result<()> {
117 self.pts_adjustment =
118 crate::time::duration_to_ticks(d, PTS_MAX).ok_or(Error::InvalidValue {
119 field: "splice_info_section.pts_adjustment",
120 reason: "duration exceeds 33-bit 90 kHz range",
121 })?;
122 Ok(())
123 }
124
125 #[must_use]
127 pub fn tier_is_ignored(&self) -> bool {
128 self.tier == TIER_IGNORE
129 }
130
131 fn payload_len(&self) -> usize {
135 match (&self.clear, self.encrypted_payload) {
136 (Some(c), _) => 1 + c.command.body_len() + 2 + c.descriptor_loop.len(),
137 (None, Some(enc)) => enc.len(),
138 (None, None) => 0,
139 }
140 }
141}
142
143impl<'a> Parse<'a> for SpliceInfoSection<'a> {
144 type Error = Error;
145 fn parse(bytes: &'a [u8]) -> Result<Self> {
146 if bytes.len() < FIXED_HEADER_LEN + CRC_LEN {
147 return Err(Error::BufferTooShort {
148 need: FIXED_HEADER_LEN + CRC_LEN,
149 have: bytes.len(),
150 what: "splice_info_section header",
151 });
152 }
153 if bytes[0] != TABLE_ID {
154 return Err(Error::UnexpectedTableId { table_id: bytes[0] });
155 }
156 let sap_type = (bytes[1] >> 4) & 0x03;
157 let section_length = (u16::from(bytes[1] & 0x0F) << 8) | u16::from(bytes[2]);
158 let total = 3 + section_length as usize;
159 if bytes.len() < total {
160 return Err(Error::LengthOverflow {
161 declared: section_length as usize,
162 available: bytes.len().saturating_sub(3),
163 what: "splice_info_section section_length",
164 });
165 }
166 if total < FIXED_HEADER_LEN + CRC_LEN {
170 return Err(Error::BufferTooShort {
171 need: FIXED_HEADER_LEN + CRC_LEN,
172 have: total,
173 what: "splice_info_section body + CRC",
174 });
175 }
176 let crc_pos = total - CRC_LEN;
177 let (crc_region, crc_bytes) =
178 bytes[..total]
179 .split_last_chunk::<CRC_LEN>()
180 .ok_or(Error::BufferTooShort {
181 need: FIXED_HEADER_LEN + CRC_LEN,
182 have: bytes.len(),
183 what: "splice_info_section header",
184 })?;
185 let expected = u32::from_be_bytes(*crc_bytes);
186 let computed = broadcast_common::crc32_mpeg2::compute(crc_region);
187 if computed != expected {
188 return Err(Error::CrcMismatch { computed, expected });
189 }
190
191 let protocol_version = bytes[3];
192 let encrypted_packet = bytes[4] & 0x80 != 0;
193 let encryption_algorithm = (bytes[4] >> 1) & 0x3F;
194 let pts_adjustment = (u64::from(bytes[4] & 0x01) << 32)
195 | (u64::from(bytes[5]) << 24)
196 | (u64::from(bytes[6]) << 16)
197 | (u64::from(bytes[7]) << 8)
198 | u64::from(bytes[8]);
199 let cw_index = bytes[9];
200 let tier = (u16::from(bytes[10]) << 4) | (u16::from(bytes[11]) >> 4);
201 let splice_command_length = (u16::from(bytes[11] & 0x0F) << 8) | u16::from(bytes[12]);
202 let splice_command_type = bytes[13];
203
204 let mut section = SpliceInfoSection {
205 sap_type,
206 protocol_version,
207 encrypted_packet,
208 encryption_algorithm,
209 pts_adjustment,
210 cw_index,
211 tier,
212 clear: None,
213 encrypted_payload: None,
214 };
215
216 let payload = &bytes[13..crc_pos];
218 if encrypted_packet {
219 section.encrypted_payload = Some(payload);
222 return Ok(section);
223 }
224
225 let scl = splice_command_length as usize;
228 if splice_command_length == 0x0FFF {
233 return Err(Error::InvalidValue {
234 field: "splice_info_section.splice_command_length",
235 reason:
236 "0xFFF backwards-compat sentinel is not supported; provide the actual length",
237 });
238 }
239 if payload.len() < 1 + scl + 2 {
241 return Err(Error::BufferTooShort {
242 need: 1 + scl + 2,
243 have: payload.len(),
244 what: "splice_info_section command + descriptor_loop_length",
245 });
246 }
247 let command_body = &payload[1..1 + scl];
248 let command = AnyCommand::dispatch(splice_command_type, command_body)?;
249
250 let dll_pos = 1 + scl;
251 let (dll_bytes, _) = payload
252 .get(dll_pos..)
253 .and_then(|s| s.split_first_chunk::<2>())
254 .ok_or(Error::BufferTooShort {
255 need: 1 + scl + 2,
256 have: payload.len(),
257 what: "splice_info_section command + descriptor_loop_length",
258 })?;
259 let descriptor_loop_length = usize::from(u16::from_be_bytes(*dll_bytes));
260 let loop_start = dll_pos + 2;
261 if payload.len() < loop_start + descriptor_loop_length {
262 return Err(Error::LengthOverflow {
263 declared: descriptor_loop_length,
264 available: payload.len().saturating_sub(loop_start),
265 what: "splice_info_section descriptor_loop_length",
266 });
267 }
268 let descriptor_loop = &payload[loop_start..loop_start + descriptor_loop_length];
269 section.clear = Some(ClearPayload {
273 command,
274 descriptor_loop,
275 });
276 Ok(section)
277 }
278}
279
280impl Serialize for SpliceInfoSection<'_> {
281 type Error = Error;
282 fn serialized_len(&self) -> usize {
283 13 + self.payload_len() + CRC_LEN
287 }
288
289 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
290 let need = self.serialized_len();
291 if buf.len() < need {
292 return Err(Error::OutputBufferTooSmall {
293 need,
294 have: buf.len(),
295 });
296 }
297 if self.pts_adjustment > PTS_MAX {
298 return Err(Error::InvalidValue {
299 field: "splice_info_section.pts_adjustment",
300 reason: "exceeds 33-bit range",
301 });
302 }
303 if self.tier > 0x0FFF {
304 return Err(Error::InvalidValue {
305 field: "splice_info_section.tier",
306 reason: "exceeds 12-bit range",
307 });
308 }
309
310 let section_length = (need - 3) as u16;
312 if section_length > 4093 {
313 return Err(Error::InvalidValue {
314 field: "splice_info_section.section_length",
315 reason: "exceeds 4093",
316 });
317 }
318
319 buf[0] = TABLE_ID;
320 buf[1] = ((self.sap_type & 0x03) << 4) | ((section_length >> 8) as u8 & 0x0F);
323 buf[2] = (section_length & 0xFF) as u8;
324 buf[3] = self.protocol_version;
325 buf[4] = (u8::from(self.encrypted_packet) << 7)
326 | ((self.encryption_algorithm & 0x3F) << 1)
327 | ((self.pts_adjustment >> 32) as u8 & 0x01);
328 buf[5] = (self.pts_adjustment >> 24) as u8;
329 buf[6] = (self.pts_adjustment >> 16) as u8;
330 buf[7] = (self.pts_adjustment >> 8) as u8;
331 buf[8] = self.pts_adjustment as u8;
332 buf[9] = self.cw_index;
333 buf[10] = (self.tier >> 4) as u8;
334
335 match (&self.clear, self.encrypted_payload) {
337 (Some(c), _) => {
338 let scl = c.command.body_len() as u16;
339 buf[11] = (((self.tier & 0x0F) as u8) << 4) | ((scl >> 8) as u8 & 0x0F);
340 buf[12] = (scl & 0xFF) as u8;
341 buf[13] = c.command.command_type();
342 let mut pos = 14;
343 pos += c.command.serialize_body_into(&mut buf[pos..])?;
344 let dll = c.descriptor_loop.len() as u16;
345 buf[pos] = (dll >> 8) as u8;
346 buf[pos + 1] = (dll & 0xFF) as u8;
347 pos += 2;
348 buf[pos..pos + c.descriptor_loop.len()].copy_from_slice(c.descriptor_loop);
349 pos += c.descriptor_loop.len();
350 debug_assert_eq!(pos, need - CRC_LEN);
351 }
352 (None, Some(enc)) => {
353 buf[11] = (((self.tier & 0x0F) as u8) << 4) | 0x0F;
357 buf[12] = 0xFF;
358 buf[13..13 + enc.len()].copy_from_slice(enc);
359 }
360 (None, None) => {
361 buf[11] = ((self.tier & 0x0F) as u8) << 4;
362 buf[12] = 0;
363 return Err(Error::InvalidValue {
365 field: "splice_info_section",
366 reason: "neither a clear command nor an encrypted payload is present",
367 });
368 }
369 }
370
371 let crc_pos = need - CRC_LEN;
372 let crc = broadcast_common::crc32_mpeg2::compute(&buf[..crc_pos]);
373 buf[crc_pos..need].copy_from_slice(&crc.to_be_bytes());
374 Ok(need)
375 }
376}