dvb_si/descriptors/extension/
audio_preselection.rs1use super::*;
3use alloc::vec::Vec;
4
5impl<'a> ExtensionBodyDef<'a> for AudioPreselection<'a> {
6 const TAG_EXTENSION: u8 = 0x19;
7 const NAME: &'static str = "AUDIO_PRESELECTION";
8}
9#[derive(Debug, Clone, PartialEq, Eq)]
11#[cfg_attr(feature = "serde", derive(serde::Serialize))]
12#[cfg_attr(feature = "yoke", derive(yoke::Yokeable))]
13pub struct AudioPreselection<'a> {
14 pub preselections: Vec<Preselection<'a>>,
16}
17
18#[derive(Debug, Clone, PartialEq, Eq)]
21#[cfg_attr(feature = "serde", derive(serde::Serialize))]
22#[cfg_attr(feature = "yoke", derive(yoke::Yokeable))]
23pub struct Preselection<'a> {
24 pub preselection_id: u8,
26 pub audio_rendering_indication: u8,
28 pub audio_description: bool,
30 pub spoken_subtitles: bool,
32 pub dialogue_enhancement: bool,
34 pub interactivity_enabled: bool,
36 pub language_code: Option<LangCode>,
38 pub message_id: Option<u8>,
40 pub aux_component_tags: Option<&'a [u8]>,
42 pub future_extension: Option<&'a [u8]>,
44}
45
46impl<'a> Parse<'a> for AudioPreselection<'a> {
47 type Error = crate::error::Error;
48 fn parse(sel: &'a [u8]) -> Result<Self> {
49 if sel.is_empty() {
50 return Err(Error::BufferTooShort {
51 need: 1,
52 have: sel.len(),
53 what: "audio_preselection body",
54 });
55 }
56 let num_preselections = sel[0] >> 3;
57 let mut pos = 1;
58 let mut preselections = Vec::with_capacity(num_preselections as usize);
59 for _ in 0..num_preselections {
60 if pos + 2 > sel.len() {
61 return Err(Error::BufferTooShort {
62 need: pos + 2,
63 have: sel.len(),
64 what: "audio_preselection body",
65 });
66 }
67 let byte_a = sel[pos];
68 let byte_b = sel[pos + 1];
69 pos += 2;
70 let preselection_id = byte_a >> 3;
71 let audio_rendering_indication = byte_a & 0x07;
72 let audio_description = (byte_b >> 7) & 1 != 0;
73 let spoken_subtitles = (byte_b >> 6) & 1 != 0;
74 let dialogue_enhancement = (byte_b >> 5) & 1 != 0;
75 let interactivity_enabled = (byte_b >> 4) & 1 != 0;
76 let language_code_present = (byte_b >> 3) & 1 != 0;
77 let text_label_present = (byte_b >> 2) & 1 != 0;
78 let multi_stream_info_present = (byte_b >> 1) & 1 != 0;
79 let future_extension = byte_b & 1 != 0;
80
81 let language_code = if language_code_present {
82 if pos + ISO_639_LEN > sel.len() {
83 return Err(Error::BufferTooShort {
84 need: pos + ISO_639_LEN,
85 have: sel.len(),
86 what: "audio_preselection body",
87 });
88 }
89 let lc = LangCode([sel[pos], sel[pos + 1], sel[pos + 2]]);
90 pos += ISO_639_LEN;
91 Some(lc)
92 } else {
93 None
94 };
95
96 let message_id = if text_label_present {
97 if pos >= sel.len() {
98 return Err(Error::BufferTooShort {
99 need: pos + 1,
100 have: sel.len(),
101 what: "audio_preselection body",
102 });
103 }
104 let id = sel[pos];
105 pos += 1;
106 Some(id)
107 } else {
108 None
109 };
110
111 let aux_component_tags = if multi_stream_info_present {
112 if pos >= sel.len() {
113 return Err(Error::BufferTooShort {
114 need: pos + 1,
115 have: sel.len(),
116 what: "audio_preselection body",
117 });
118 }
119 let num_aux = sel[pos] >> 5;
120 pos += 1;
121 if pos + num_aux as usize > sel.len() {
122 return Err(Error::BufferTooShort {
123 need: pos + num_aux as usize,
124 have: sel.len(),
125 what: "audio_preselection body",
126 });
127 }
128 let tags = &sel[pos..pos + num_aux as usize];
129 pos += num_aux as usize;
130 Some(tags)
131 } else {
132 None
133 };
134
135 let future_ext = if future_extension {
136 if pos >= sel.len() {
137 return Err(Error::BufferTooShort {
138 need: pos + 1,
139 have: sel.len(),
140 what: "audio_preselection body",
141 });
142 }
143 let ext_len = (sel[pos] & 0x1F) as usize;
144 pos += 1;
145 if pos + ext_len > sel.len() {
146 return Err(Error::BufferTooShort {
147 need: pos + ext_len,
148 have: sel.len(),
149 what: "audio_preselection body",
150 });
151 }
152 let ext = &sel[pos..pos + ext_len];
153 pos += ext_len;
154 Some(ext)
155 } else {
156 None
157 };
158
159 preselections.push(Preselection {
160 preselection_id,
161 audio_rendering_indication,
162 audio_description,
163 spoken_subtitles,
164 dialogue_enhancement,
165 interactivity_enabled,
166 language_code,
167 message_id,
168 aux_component_tags,
169 future_extension: future_ext,
170 });
171 }
172 Ok(AudioPreselection { preselections })
173 }
174}
175
176impl Serialize for AudioPreselection<'_> {
177 type Error = crate::error::Error;
178 fn serialized_len(&self) -> usize {
179 let body: usize = self
180 .preselections
181 .iter()
182 .map(|p| {
183 2 + p.language_code.map_or(0, |_| ISO_639_LEN)
184 + p.message_id.map_or(0, |_| 1)
185 + p.aux_component_tags.map_or(0, |t| 1 + t.len())
186 + p.future_extension.map_or(0, |e| 1 + e.len())
187 })
188 .sum();
189 1 + body
190 }
191 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
192 let len = self.serialized_len();
193 if buf.len() < len {
194 return Err(Error::OutputBufferTooSmall {
195 need: len,
196 have: buf.len(),
197 });
198 }
199 buf[0] = ((self.preselections.len() as u8) & 0x1F) << 3;
200 let mut p = 1;
201 for s in &self.preselections {
202 buf[p] = ((s.preselection_id & 0x1F) << 3) | (s.audio_rendering_indication & 0x07);
203 buf[p + 1] = (u8::from(s.audio_description) << 7)
204 | (u8::from(s.spoken_subtitles) << 6)
205 | (u8::from(s.dialogue_enhancement) << 5)
206 | (u8::from(s.interactivity_enabled) << 4)
207 | (u8::from(s.language_code.is_some()) << 3)
208 | (u8::from(s.message_id.is_some()) << 2)
209 | (u8::from(s.aux_component_tags.is_some()) << 1)
210 | u8::from(s.future_extension.is_some());
211 p += 2;
212 if let Some(ref lc) = s.language_code {
213 buf[p..p + ISO_639_LEN].copy_from_slice(&lc.0);
214 p += ISO_639_LEN;
215 }
216 if let Some(id) = s.message_id {
217 buf[p] = id;
218 p += 1;
219 }
220 if let Some(tags) = s.aux_component_tags {
221 buf[p] = ((tags.len() as u8) & 0x07) << 5;
222 p += 1;
223 buf[p..p + tags.len()].copy_from_slice(tags);
224 p += tags.len();
225 }
226 if let Some(ext) = s.future_extension {
227 buf[p] = ext.len() as u8 & 0x1F;
228 p += 1;
229 buf[p..p + ext.len()].copy_from_slice(ext);
230 p += ext.len();
231 }
232 }
233 Ok(len)
234 }
235}
236
237#[cfg(test)]
238mod tests {
239 use super::*;
240 use crate::descriptors::extension::test_support::*;
241 use crate::descriptors::extension::{ExtensionBody, ExtensionDescriptor};
242 use crate::text::LangCode;
243
244 #[test]
245 fn parse_structured_round_trip_two_preselections() {
246 let num_pre = 0x02 << 3;
247 let pre0_a = (1 << 3) | 2; let pre0_b = (1 << 6) | (1 << 4); let pre1_a = (2 << 3) | 3; let pre1_b = (1 << 7) | (1 << 5) | (1 << 3) | (1 << 2) | (1 << 1) | 1;
252 let lang = b"foo";
253 let msg_id = 0xABu8;
254 let aux_tags: &[u8] = &[0x01, 0x02];
255 let aux_byte = ((aux_tags.len() as u8) & 0x07) << 5;
256 let fut_ext: &[u8] = &[0x25, 0x89, 0x63, 0x21, 0x47];
257 let fut_byte = fut_ext.len() as u8 & 0x1F;
258
259 let mut sel = vec![num_pre];
260 sel.push(pre0_a);
261 sel.push(pre0_b);
262 sel.push(pre1_a);
263 sel.push(pre1_b);
264 sel.extend_from_slice(lang);
265 sel.push(msg_id);
266 sel.push(aux_byte);
267 sel.extend_from_slice(aux_tags);
268 sel.push(fut_byte);
269 sel.extend_from_slice(fut_ext);
270
271 let bytes = wrap(0x19, &sel);
272 let d = ExtensionDescriptor::parse(&bytes).unwrap();
273 match &d.body {
274 ExtensionBody::AudioPreselection(b) => {
275 assert_eq!(b.preselections.len(), 2);
276
277 let p0 = &b.preselections[0];
278 assert_eq!(p0.preselection_id, 1);
279 assert_eq!(p0.audio_rendering_indication, 2);
280 assert!(!p0.audio_description);
281 assert!(p0.spoken_subtitles);
282 assert!(!p0.dialogue_enhancement);
283 assert!(p0.interactivity_enabled);
284 assert_eq!(p0.language_code, None);
285 assert_eq!(p0.message_id, None);
286 assert_eq!(p0.aux_component_tags, None);
287 assert_eq!(p0.future_extension, None);
288
289 let p1 = &b.preselections[1];
290 assert_eq!(p1.preselection_id, 2);
291 assert_eq!(p1.audio_rendering_indication, 3);
292 assert!(p1.audio_description);
293 assert!(!p1.spoken_subtitles);
294 assert!(p1.dialogue_enhancement);
295 assert!(!p1.interactivity_enabled);
296 assert_eq!(p1.language_code, Some(LangCode(*b"foo")));
297 assert_eq!(p1.message_id, Some(0xAB));
298 assert_eq!(p1.aux_component_tags, Some(aux_tags as &[u8]));
299 assert_eq!(p1.future_extension, Some(fut_ext as &[u8]));
300 }
301 other => panic!("expected AudioPreselection, got {other:?}"),
302 }
303 round_trip(&d);
304 }
305
306 #[test]
307 fn tsduck_byte_exact_test_015() {
308 let hex = "7f1319100a5013af666f6fab400102052589632147";
312 let bytes = from_hex(hex);
313 let d = ExtensionDescriptor::parse(&bytes).unwrap_or_else(|e| panic!("parse {hex}: {e:?}"));
314 assert_eq!(
315 d.kind(),
316 Some(super::super::ExtensionTag::AudioPreselection)
317 );
318 match &d.body {
319 ExtensionBody::AudioPreselection(b) => {
320 assert_eq!(b.preselections.len(), 2);
321
322 let p0 = &b.preselections[0];
323 assert_eq!(p0.preselection_id, 1);
324 assert_eq!(p0.audio_rendering_indication, 2);
325 assert!(!p0.audio_description);
326 assert!(p0.spoken_subtitles);
327 assert!(!p0.dialogue_enhancement);
328 assert!(p0.interactivity_enabled);
329 assert_eq!(p0.language_code, None);
330 assert_eq!(p0.message_id, None);
331 assert_eq!(p0.aux_component_tags, None);
332 assert_eq!(p0.future_extension, None);
333
334 let p1 = &b.preselections[1];
335 assert_eq!(p1.preselection_id, 2);
336 assert_eq!(p1.audio_rendering_indication, 3);
337 assert!(p1.audio_description);
338 assert!(!p1.spoken_subtitles);
339 assert!(p1.dialogue_enhancement);
340 assert!(!p1.interactivity_enabled);
341 assert_eq!(p1.language_code, Some(LangCode(*b"foo")));
342 assert_eq!(p1.message_id, Some(0xAB));
343 assert_eq!(p1.aux_component_tags, Some(&[0x01u8, 0x02u8][..]));
344 assert_eq!(
345 p1.future_extension,
346 Some(&[0x25u8, 0x89, 0x63, 0x21, 0x47][..])
347 );
348 }
349 other => panic!("expected AudioPreselection, got {other:?}"),
350 }
351 round_trip(&d);
352 }
353}