1use crate::descriptors::DescriptorLoop;
9use crate::error::{Error, Result};
10use crate::traits::Table;
11use dvb_common::{Parse, Serialize};
12
13pub const TABLE_ID_ACTUAL: u8 = 0x42;
15pub const TABLE_ID_OTHER: u8 = 0x46;
17pub const PID: u16 = 0x0011;
19
20const MIN_HEADER_LEN: usize = 3;
21const EXTENSION_HEADER_LEN: usize = 5;
22const POST_EXTENSION_LEN: usize = 3;
25const CRC_LEN: usize = 4;
26const SERVICE_HEADER_LEN: usize = 5;
27
28#[derive(Debug, Clone, Copy, PartialEq, Eq)]
30#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
31pub enum SdtKind {
32 Actual,
34 Other,
36}
37
38#[derive(Debug, Clone, PartialEq, Eq)]
40#[cfg_attr(feature = "serde", derive(serde::Serialize))]
41pub struct SdtService<'a> {
42 pub service_id: u16,
44 pub eit_schedule_flag: bool,
46 pub eit_present_following_flag: bool,
48 pub running_status: u8,
50 pub free_ca_mode: bool,
52 pub descriptors: DescriptorLoop<'a>,
55}
56
57#[derive(Debug, Clone, PartialEq, Eq)]
59#[cfg_attr(feature = "serde", derive(serde::Serialize))]
60pub struct Sdt<'a> {
61 pub kind: SdtKind,
63 pub transport_stream_id: u16,
65 pub version_number: u8,
67 pub current_next_indicator: bool,
69 pub section_number: u8,
71 pub last_section_number: u8,
73 pub original_network_id: u16,
75 #[cfg_attr(feature = "serde", serde(borrow))]
77 pub services: Vec<SdtService<'a>>,
78}
79
80impl<'a> Parse<'a> for Sdt<'a> {
81 type Error = crate::error::Error;
82 fn parse(bytes: &'a [u8]) -> Result<Self> {
83 let min_len = MIN_HEADER_LEN + EXTENSION_HEADER_LEN + POST_EXTENSION_LEN + CRC_LEN;
84 if bytes.len() < min_len {
85 return Err(Error::BufferTooShort {
86 need: min_len,
87 have: bytes.len(),
88 what: "Sdt",
89 });
90 }
91 let kind = match bytes[0] {
92 TABLE_ID_ACTUAL => SdtKind::Actual,
93 TABLE_ID_OTHER => SdtKind::Other,
94 other => {
95 return Err(Error::UnexpectedTableId {
96 table_id: other,
97 what: "Sdt",
98 expected: &[TABLE_ID_ACTUAL, TABLE_ID_OTHER],
99 });
100 }
101 };
102
103 let section_length = ((bytes[1] & 0x0F) as u16) << 8 | bytes[2] as u16;
104 let total = MIN_HEADER_LEN + section_length as usize;
105 if bytes.len() < total {
106 return Err(Error::SectionLengthOverflow {
107 declared: section_length as usize,
108 available: bytes.len() - MIN_HEADER_LEN,
109 });
110 }
111
112 let transport_stream_id = u16::from_be_bytes([bytes[3], bytes[4]]);
113 let version_number = (bytes[5] >> 1) & 0x1F;
114 let current_next_indicator = (bytes[5] & 0x01) != 0;
115 let section_number = bytes[6];
116 let last_section_number = bytes[7];
117 let original_network_id = u16::from_be_bytes([bytes[8], bytes[9]]);
118
119 let services_start = MIN_HEADER_LEN + EXTENSION_HEADER_LEN + POST_EXTENSION_LEN;
120 let services_end = total - CRC_LEN;
121 let mut services = Vec::new();
122 let mut pos = services_start;
123 while pos + SERVICE_HEADER_LEN <= services_end {
124 let service_id = u16::from_be_bytes([bytes[pos], bytes[pos + 1]]);
125 let flags = bytes[pos + 2];
126 let eit_schedule_flag = (flags & 0x02) != 0;
127 let eit_present_following_flag = (flags & 0x01) != 0;
128 let status_and_len_hi = bytes[pos + 3];
129 let running_status = (status_and_len_hi >> 5) & 0x07;
130 let free_ca_mode = (status_and_len_hi & 0x10) != 0;
131 let descriptors_loop_length =
132 (((status_and_len_hi & 0x0F) as usize) << 8) | bytes[pos + 4] as usize;
133 let desc_start = pos + SERVICE_HEADER_LEN;
134 let desc_end = desc_start + descriptors_loop_length;
135 if desc_end > services_end {
136 return Err(Error::SectionLengthOverflow {
137 declared: descriptors_loop_length,
138 available: services_end - desc_start,
139 });
140 }
141 services.push(SdtService {
142 service_id,
143 eit_schedule_flag,
144 eit_present_following_flag,
145 running_status,
146 free_ca_mode,
147 descriptors: DescriptorLoop::new(&bytes[desc_start..desc_end]),
148 });
149 pos = desc_end;
150 }
151
152 Ok(Sdt {
153 kind,
154 transport_stream_id,
155 version_number,
156 current_next_indicator,
157 section_number,
158 last_section_number,
159 original_network_id,
160 services,
161 })
162 }
163}
164
165impl Serialize for Sdt<'_> {
166 type Error = crate::error::Error;
167 fn serialized_len(&self) -> usize {
168 let svc_bytes: usize = self
169 .services
170 .iter()
171 .map(|s| SERVICE_HEADER_LEN + s.descriptors.len())
172 .sum();
173 MIN_HEADER_LEN + EXTENSION_HEADER_LEN + POST_EXTENSION_LEN + svc_bytes + CRC_LEN
174 }
175
176 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
177 let len = self.serialized_len();
178 if buf.len() < len {
179 return Err(Error::OutputBufferTooSmall {
180 need: len,
181 have: buf.len(),
182 });
183 }
184 let section_length: u16 = (len - MIN_HEADER_LEN) as u16;
185 buf[0] = match self.kind {
186 SdtKind::Actual => TABLE_ID_ACTUAL,
187 SdtKind::Other => TABLE_ID_OTHER,
188 };
189 buf[1] = 0xB0 | ((section_length >> 8) as u8 & 0x0F);
190 buf[2] = (section_length & 0xFF) as u8;
191 buf[3..5].copy_from_slice(&self.transport_stream_id.to_be_bytes());
192 buf[5] = 0xC0 | ((self.version_number & 0x1F) << 1) | u8::from(self.current_next_indicator);
193 buf[6] = self.section_number;
194 buf[7] = self.last_section_number;
195 buf[8..10].copy_from_slice(&self.original_network_id.to_be_bytes());
196 buf[10] = 0xFF; let mut pos = MIN_HEADER_LEN + EXTENSION_HEADER_LEN + POST_EXTENSION_LEN;
199 for svc in &self.services {
200 buf[pos..pos + 2].copy_from_slice(&svc.service_id.to_be_bytes());
201 let flags = 0xFC
202 | (u8::from(svc.eit_schedule_flag) << 1)
203 | u8::from(svc.eit_present_following_flag);
204 buf[pos + 2] = flags;
205 let dll = svc.descriptors.len() as u16;
206 buf[pos + 3] = ((svc.running_status & 0x07) << 5)
207 | (u8::from(svc.free_ca_mode) << 4)
208 | ((dll >> 8) as u8 & 0x0F);
209 buf[pos + 4] = (dll & 0xFF) as u8;
210 let desc_start = pos + SERVICE_HEADER_LEN;
211 buf[desc_start..desc_start + svc.descriptors.len()]
212 .copy_from_slice(svc.descriptors.raw());
213 pos = desc_start + svc.descriptors.len();
214 }
215
216 let crc_pos = len - CRC_LEN;
217 let crc = dvb_common::crc32_mpeg2::compute(&buf[..crc_pos]);
218 buf[crc_pos..len].copy_from_slice(&crc.to_be_bytes());
219 Ok(len)
220 }
221}
222
223impl<'a> Table<'a> for Sdt<'a> {
224 const TABLE_ID: u8 = TABLE_ID_ACTUAL;
225 const PID: u16 = PID;
226}
227
228impl<'a> crate::traits::TableDef<'a> for Sdt<'a> {
229 const TABLE_ID_RANGES: &'static [(u8, u8)] = &[
230 (TABLE_ID_ACTUAL, TABLE_ID_ACTUAL),
231 (TABLE_ID_OTHER, TABLE_ID_OTHER),
232 ];
233 const NAME: &'static str = "SERVICE_DESCRIPTION";
234}
235
236#[cfg(test)]
237mod tests {
238 use super::*;
239
240 type TestService = (u16, bool, bool, u8, bool, Vec<u8>);
241
242 fn build_sdt(
243 kind: SdtKind,
244 tsid: u16,
245 version: u8,
246 original_network_id: u16,
247 services: &[TestService],
248 ) -> Vec<u8> {
249 let svc_bytes: usize = services
250 .iter()
251 .map(|(_, _, _, _, _, d)| SERVICE_HEADER_LEN + d.len())
252 .sum();
253 let section_length: u16 =
254 (EXTENSION_HEADER_LEN + POST_EXTENSION_LEN + svc_bytes + CRC_LEN) as u16;
255 let mut v = Vec::new();
256 v.push(match kind {
257 SdtKind::Actual => TABLE_ID_ACTUAL,
258 SdtKind::Other => TABLE_ID_OTHER,
259 });
260 v.push(0xB0 | ((section_length >> 8) as u8 & 0x0F));
261 v.push((section_length & 0xFF) as u8);
262 v.extend_from_slice(&tsid.to_be_bytes());
263 v.push(0xC0 | ((version & 0x1F) << 1) | 0x01);
264 v.push(0);
265 v.push(0);
266 v.extend_from_slice(&original_network_id.to_be_bytes());
267 v.push(0xFF);
268 for (sid, eit_s, eit_pf, rs, fca, desc) in services {
269 v.extend_from_slice(&sid.to_be_bytes());
270 let flags = 0xFC | (u8::from(*eit_s) << 1) | u8::from(*eit_pf);
271 v.push(flags);
272 let dll = desc.len() as u16;
273 v.push(((*rs & 0x07) << 5) | (u8::from(*fca) << 4) | ((dll >> 8) as u8 & 0x0F));
274 v.push((dll & 0xFF) as u8);
275 v.extend_from_slice(desc);
276 }
277 v.extend_from_slice(&[0, 0, 0, 0]);
278 v
279 }
280
281 #[test]
282 fn parse_actual_and_other_tables_distinguished_by_table_id() {
283 let a = build_sdt(SdtKind::Actual, 1, 0, 0x20, &[]);
284 let o = build_sdt(SdtKind::Other, 1, 0, 0x20, &[]);
285 assert!(matches!(Sdt::parse(&a).unwrap().kind, SdtKind::Actual));
286 assert!(matches!(Sdt::parse(&o).unwrap().kind, SdtKind::Other));
287 }
288
289 #[test]
290 fn parse_services_with_descriptor_bytes() {
291 let bytes = build_sdt(
292 SdtKind::Actual,
293 1,
294 0,
295 0x20,
296 &[(
297 100,
298 true,
299 true,
300 4,
301 false,
302 vec![0x48, 0x05, 0x01, 0x02, 0x03, 0x04, 0x05],
303 )],
304 );
305 let sdt = Sdt::parse(&bytes).unwrap();
306 assert_eq!(sdt.services.len(), 1);
307 assert_eq!(sdt.services[0].service_id, 100);
308 assert!(sdt.services[0].eit_schedule_flag);
309 assert!(sdt.services[0].eit_present_following_flag);
310 assert_eq!(sdt.services[0].running_status, 4);
311 assert!(!sdt.services[0].free_ca_mode);
312 assert_eq!(
313 sdt.services[0].descriptors.raw(),
314 &[0x48, 0x05, 0x01, 0x02, 0x03, 0x04, 0x05][..]
315 );
316 }
317
318 #[test]
319 fn service_free_ca_mode_flag_extracted() {
320 let bytes = build_sdt(
321 SdtKind::Actual,
322 1,
323 0,
324 0x20,
325 &[(1, false, false, 0, true, vec![])],
326 );
327 let sdt = Sdt::parse(&bytes).unwrap();
328 assert!(sdt.services[0].free_ca_mode);
329 }
330
331 #[test]
332 fn service_running_status_extracted() {
333 let bytes = build_sdt(
334 SdtKind::Actual,
335 1,
336 0,
337 0x20,
338 &[(1, false, false, 2, false, vec![])],
339 );
340 let sdt = Sdt::parse(&bytes).unwrap();
341 assert_eq!(sdt.services[0].running_status, 2);
342 }
343
344 #[test]
345 fn parse_rejects_short_buffer() {
346 let err = Sdt::parse(&[0x42, 0x00]).unwrap_err();
347 assert!(matches!(err, Error::BufferTooShort { .. }));
348 }
349
350 #[test]
351 fn parse_rejects_wrong_table_id() {
352 let mut bytes = build_sdt(SdtKind::Actual, 1, 0, 0x20, &[]);
353 bytes[0] = 0x00;
354 let err = Sdt::parse(&bytes).unwrap_err();
355 assert!(matches!(
356 err,
357 Error::UnexpectedTableId { table_id: 0x00, .. }
358 ));
359 }
360
361 #[test]
362 fn serialize_round_trip() {
363 let desc1: [u8; 4] = [0x48, 0x02, 0xAA, 0xBB];
364 let sdt = Sdt {
365 kind: SdtKind::Actual,
366 transport_stream_id: 0x1234,
367 version_number: 5,
368 current_next_indicator: true,
369 section_number: 0,
370 last_section_number: 0,
371 original_network_id: 0x0020,
372 services: vec![
373 SdtService {
374 service_id: 100,
375 eit_schedule_flag: true,
376 eit_present_following_flag: false,
377 running_status: 4,
378 free_ca_mode: false,
379 descriptors: DescriptorLoop::new(&desc1),
380 },
381 SdtService {
382 service_id: 101,
383 eit_schedule_flag: false,
384 eit_present_following_flag: true,
385 running_status: 2,
386 free_ca_mode: true,
387 descriptors: DescriptorLoop::new(&[]),
388 },
389 ],
390 };
391 let mut buf = vec![0u8; sdt.serialized_len()];
392 sdt.serialize_into(&mut buf).unwrap();
393 let re = Sdt::parse(&buf).unwrap();
394 assert_eq!(sdt, re);
395 }
396
397 #[test]
398 fn zero_services_is_valid() {
399 let bytes = build_sdt(SdtKind::Actual, 1, 0, 0x20, &[]);
400 let sdt = Sdt::parse(&bytes).unwrap();
401 assert_eq!(sdt.services.len(), 0);
402 }
403}