1use super::descriptor_body;
27use crate::error::{Error, Result};
28use dvb_common::{Parse, Serialize};
29
30pub const TAG: u8 = 0x79;
32const HEADER_LEN: usize = 2;
33const FLAGS_LEN: usize = 1;
34const SCRAMBLING_FIELD_LEN: usize = 3;
35const ISI_FIELD_LEN: usize = 1;
36const TIMESLICE_FIELD_LEN: usize = 1;
37
38const FLAG_SCRAMBLING_SELECTOR: u8 = 0x80;
39const FLAG_MULTIPLE_INPUT_STREAM: u8 = 0x40;
40const FLAG_NOT_TIMESLICE: u8 = 0x10;
41const TS_GS_MODE_MASK: u8 = 0x03;
43const FLAG_RESERVED_BITS: u8 = 0x0C;
47
48const SCRAMBLING_RESERVED_MASK: u8 = 0xFC;
49const SCRAMBLING_INDEX_HI_MASK: u8 = 0x03;
50const SCRAMBLING_INDEX_MAX: u32 = 0x3FFFF;
51
52#[derive(Debug, Clone, Copy, PartialEq, Eq)]
54#[cfg_attr(feature = "serde", derive(serde::Serialize))]
55#[non_exhaustive]
56pub enum TsGsMode {
57 GenericPacketized,
59 Gse,
61 DvbTransportStream,
63 Reserved(u8),
65}
66
67impl TsGsMode {
68 #[must_use]
69 pub fn from_u8(v: u8) -> Self {
71 match v {
72 0 => TsGsMode::GenericPacketized,
73 1 => TsGsMode::Gse,
74 2 => TsGsMode::DvbTransportStream,
75 other => TsGsMode::Reserved(other),
76 }
77 }
78
79 #[must_use]
80 pub fn to_u8(self) -> u8 {
82 match self {
83 TsGsMode::GenericPacketized => 0,
84 TsGsMode::Gse => 1,
85 TsGsMode::DvbTransportStream => 2,
86 TsGsMode::Reserved(v) => v,
87 }
88 }
89
90 #[must_use]
91 pub fn name(self) -> &'static str {
93 match self {
94 TsGsMode::GenericPacketized => "Generic Packetized",
95 TsGsMode::Gse => "Generic Stream Encapsulation (GSE)",
96 TsGsMode::DvbTransportStream => "DVB transport stream",
97 TsGsMode::Reserved(_) => "reserved",
98 }
99 }
100}
101dvb_common::impl_spec_display!(TsGsMode, Reserved);
102
103#[derive(Debug, Clone, PartialEq, Eq)]
105#[cfg_attr(feature = "serde", derive(serde::Serialize))]
106pub struct S2SatelliteDeliverySystemDescriptor {
107 pub scrambling_sequence_selector: bool,
109 pub multiple_input_stream_flag: bool,
111 pub not_timeslice_flag: bool,
113 pub ts_gs_mode: TsGsMode,
115 pub scrambling_sequence_index: Option<u32>,
117 pub input_stream_identifier: Option<u8>,
119 pub timeslice_number: Option<u8>,
121}
122
123impl<'a> Parse<'a> for S2SatelliteDeliverySystemDescriptor {
124 type Error = crate::error::Error;
125 fn parse(bytes: &'a [u8]) -> Result<Self> {
126 if bytes.len() < HEADER_LEN + FLAGS_LEN {
127 return Err(Error::BufferTooShort {
128 need: HEADER_LEN + FLAGS_LEN,
129 have: bytes.len(),
130 what: "S2SatelliteDeliverySystemDescriptor header",
131 });
132 }
133 let body = descriptor_body(
134 bytes,
135 TAG,
136 "S2SatelliteDeliverySystemDescriptor",
137 "unexpected tag for s2_satellite_delivery_system_descriptor",
138 )?;
139 if body.len() < FLAGS_LEN {
140 return Err(Error::InvalidDescriptor {
141 tag: TAG,
142 reason: "body must contain at least the flags byte",
143 });
144 }
145
146 let flags = body[0];
148 let scrambling_sequence_selector = (flags & FLAG_SCRAMBLING_SELECTOR) != 0;
149 let multiple_input_stream_flag = (flags & FLAG_MULTIPLE_INPUT_STREAM) != 0;
150 let not_timeslice_flag = (flags & FLAG_NOT_TIMESLICE) != 0;
151 let ts_gs_mode = TsGsMode::from_u8(flags & TS_GS_MODE_MASK);
152
153 let mut pos = FLAGS_LEN;
154
155 let scrambling_sequence_index = if scrambling_sequence_selector {
156 if pos + SCRAMBLING_FIELD_LEN > body.len() {
157 return Err(Error::BufferTooShort {
158 need: pos + SCRAMBLING_FIELD_LEN,
159 have: body.len(),
160 what: "S2SatelliteDeliverySystemDescriptor scrambling_sequence_index",
161 });
162 }
163 let b0 = body[pos];
164 let index = (u32::from(b0 & SCRAMBLING_INDEX_HI_MASK) << 16)
165 | (u32::from(body[pos + 1]) << 8)
166 | u32::from(body[pos + 2]);
167 pos += SCRAMBLING_FIELD_LEN;
168 Some(index)
169 } else {
170 None
171 };
172
173 let input_stream_identifier = if multiple_input_stream_flag {
174 if pos + ISI_FIELD_LEN > body.len() {
175 return Err(Error::BufferTooShort {
176 need: pos + ISI_FIELD_LEN,
177 have: body.len(),
178 what: "S2SatelliteDeliverySystemDescriptor input_stream_identifier",
179 });
180 }
181 let isi = body[pos];
182 pos += ISI_FIELD_LEN;
183 Some(isi)
184 } else {
185 None
186 };
187
188 let timeslice_number = if !not_timeslice_flag {
190 if pos + TIMESLICE_FIELD_LEN > body.len() {
191 return Err(Error::BufferTooShort {
192 need: pos + TIMESLICE_FIELD_LEN,
193 have: body.len(),
194 what: "S2SatelliteDeliverySystemDescriptor timeslice_number",
195 });
196 }
197 Some(body[pos])
198 } else {
199 None
200 };
201
202 Ok(Self {
203 scrambling_sequence_selector,
204 multiple_input_stream_flag,
205 not_timeslice_flag,
206 ts_gs_mode,
207 scrambling_sequence_index,
208 input_stream_identifier,
209 timeslice_number,
210 })
211 }
212}
213
214impl Serialize for S2SatelliteDeliverySystemDescriptor {
215 type Error = crate::error::Error;
216 fn serialized_len(&self) -> usize {
217 HEADER_LEN
218 + FLAGS_LEN
219 + if self.scrambling_sequence_selector {
220 SCRAMBLING_FIELD_LEN
221 } else {
222 0
223 }
224 + if self.multiple_input_stream_flag {
225 ISI_FIELD_LEN
226 } else {
227 0
228 }
229 + if self.not_timeslice_flag {
230 0
231 } else {
232 TIMESLICE_FIELD_LEN
233 }
234 }
235
236 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
237 let len = self.serialized_len();
238 if buf.len() < len {
239 return Err(Error::OutputBufferTooSmall {
240 need: len,
241 have: buf.len(),
242 });
243 }
244 buf[0] = TAG;
245 buf[1] = (len - HEADER_LEN) as u8;
246
247 let mut flags = FLAG_RESERVED_BITS | (self.ts_gs_mode.to_u8() & TS_GS_MODE_MASK);
248 if self.scrambling_sequence_selector {
249 flags |= FLAG_SCRAMBLING_SELECTOR;
250 }
251 if self.multiple_input_stream_flag {
252 flags |= FLAG_MULTIPLE_INPUT_STREAM;
253 }
254 if self.not_timeslice_flag {
255 flags |= FLAG_NOT_TIMESLICE;
256 }
257 buf[HEADER_LEN] = flags;
258
259 let mut pos = HEADER_LEN + FLAGS_LEN;
260 if self.scrambling_sequence_selector {
261 let index = self.scrambling_sequence_index.unwrap_or(0) & SCRAMBLING_INDEX_MAX;
262 buf[pos] = SCRAMBLING_RESERVED_MASK | ((index >> 16) as u8 & SCRAMBLING_INDEX_HI_MASK);
263 buf[pos + 1] = (index >> 8) as u8;
264 buf[pos + 2] = index as u8;
265 pos += SCRAMBLING_FIELD_LEN;
266 }
267 if self.multiple_input_stream_flag {
268 buf[pos] = self.input_stream_identifier.unwrap_or(0);
269 pos += ISI_FIELD_LEN;
270 }
271 if !self.not_timeslice_flag {
272 buf[pos] = self.timeslice_number.unwrap_or(0);
273 }
274 Ok(len)
275 }
276}
277impl<'a> crate::traits::DescriptorDef<'a> for S2SatelliteDeliverySystemDescriptor {
278 const TAG: u8 = TAG;
279 const NAME: &'static str = "S2_SATELLITE_DELIVERY_SYSTEM";
280}
281
282#[cfg(test)]
283mod tests {
284 use super::*;
285
286 #[test]
287 fn ts_gs_mode_from_u8_to_u8_roundtrip() {
288 for b in 0..=0xFFu8 {
289 assert_eq!(
290 TsGsMode::from_u8(b).to_u8(),
291 b,
292 "round-trip fail for {b:#04x}"
293 );
294 }
295 }
296
297 #[test]
300 fn parse_minimal_with_timeslice() {
301 let raw = [TAG, 2, 0x2C, 0x07];
302 let d = S2SatelliteDeliverySystemDescriptor::parse(&raw).unwrap();
303 assert!(!d.scrambling_sequence_selector);
304 assert!(!d.multiple_input_stream_flag);
305 assert!(!d.not_timeslice_flag);
306 assert_eq!(d.ts_gs_mode, TsGsMode::GenericPacketized);
307 assert_eq!(d.timeslice_number, Some(0x07));
308 assert_eq!(d.scrambling_sequence_index, None);
309 assert_eq!(d.input_stream_identifier, None);
310 }
311
312 #[test]
313 fn parse_not_timeslice_omits_timeslice_number() {
314 let raw = [TAG, 1, 0x3E];
316 let d = S2SatelliteDeliverySystemDescriptor::parse(&raw).unwrap();
317 assert!(d.not_timeslice_flag);
318 assert_eq!(d.ts_gs_mode, TsGsMode::DvbTransportStream);
319 assert_eq!(d.timeslice_number, None);
320 }
321
322 #[test]
323 fn parse_extracts_isi_and_timeslice() {
324 let raw = [TAG, 3, 0x6C, 0x05, 0x09];
326 let d = S2SatelliteDeliverySystemDescriptor::parse(&raw).unwrap();
327 assert!(d.multiple_input_stream_flag);
328 assert_eq!(d.input_stream_identifier, Some(5));
329 assert_eq!(d.timeslice_number, Some(9));
330 }
331
332 #[test]
333 fn parse_extracts_scrambling_index() {
334 let raw = [TAG, 4, 0xBC, 0xFD, 0x23, 0x45];
336 let d = S2SatelliteDeliverySystemDescriptor::parse(&raw).unwrap();
337 assert!(d.scrambling_sequence_selector);
338 assert_eq!(d.scrambling_sequence_index, Some(0x12345));
339 assert!(d.not_timeslice_flag);
340 assert_eq!(d.timeslice_number, None);
341 }
342
343 #[test]
344 fn parse_full_18_bit_index() {
345 let raw = [TAG, 4, 0xBC, 0xFF, 0xFF, 0xFF];
346 let d = S2SatelliteDeliverySystemDescriptor::parse(&raw).unwrap();
347 assert_eq!(d.scrambling_sequence_index, Some(0x3FFFF));
348 }
349
350 #[test]
351 fn parse_rejects_wrong_tag() {
352 let raw = [0x44, 1, 0x3E];
353 assert!(matches!(
354 S2SatelliteDeliverySystemDescriptor::parse(&raw).unwrap_err(),
355 Error::InvalidDescriptor { tag: 0x44, .. }
356 ));
357 }
358
359 #[test]
360 fn parse_rejects_truncated_scrambling() {
361 let raw = [TAG, 1, 0x9C]; assert!(matches!(
363 S2SatelliteDeliverySystemDescriptor::parse(&raw).unwrap_err(),
364 Error::BufferTooShort { .. }
365 ));
366 }
367
368 #[test]
369 fn serialize_round_trip_all_fields() {
370 let d = S2SatelliteDeliverySystemDescriptor {
371 scrambling_sequence_selector: true,
372 multiple_input_stream_flag: true,
373 not_timeslice_flag: false,
374 ts_gs_mode: TsGsMode::DvbTransportStream,
375 scrambling_sequence_index: Some(0x2BCDE),
376 input_stream_identifier: Some(0x42),
377 timeslice_number: Some(0x11),
378 };
379 let mut buf = vec![0u8; d.serialized_len()];
380 d.serialize_into(&mut buf).unwrap();
381 assert_eq!(S2SatelliteDeliverySystemDescriptor::parse(&buf).unwrap(), d);
382 }
383
384 #[test]
385 fn serialize_round_trip_not_timeslice() {
386 let d = S2SatelliteDeliverySystemDescriptor {
387 scrambling_sequence_selector: false,
388 multiple_input_stream_flag: false,
389 not_timeslice_flag: true,
390 ts_gs_mode: TsGsMode::Gse,
391 scrambling_sequence_index: None,
392 input_stream_identifier: None,
393 timeslice_number: None,
394 };
395 let mut buf = vec![0u8; d.serialized_len()];
396 d.serialize_into(&mut buf).unwrap();
397 assert_eq!(S2SatelliteDeliverySystemDescriptor::parse(&buf).unwrap(), d);
398 }
399}