dvb_si/descriptors/extension/
ac4.rs1use super::*;
3
4impl<'a> ExtensionBodyDef<'a> for Ac4<'a> {
5 const TAG_EXTENSION: u8 = 0x15;
6 const NAME: &'static str = "AC4";
7}
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11#[cfg_attr(feature = "serde", derive(serde::Serialize))]
12#[non_exhaustive]
13pub enum Ac4ChannelMode {
14 Mono,
16 Stereo,
18 Multichannel,
20 Reserved(u8),
22}
23
24impl Ac4ChannelMode {
25 #[must_use]
27 pub fn from_u8(v: u8) -> Self {
28 match v {
29 0 => Ac4ChannelMode::Mono,
30 1 => Ac4ChannelMode::Stereo,
31 2 => Ac4ChannelMode::Multichannel,
32 other => Ac4ChannelMode::Reserved(other),
33 }
34 }
35
36 #[must_use]
38 pub const fn to_u8(self) -> u8 {
39 match self {
40 Ac4ChannelMode::Mono => 0,
41 Ac4ChannelMode::Stereo => 1,
42 Ac4ChannelMode::Multichannel => 2,
43 Ac4ChannelMode::Reserved(v) => v,
44 }
45 }
46
47 #[must_use]
49 pub fn name(self) -> &'static str {
50 match self {
51 Ac4ChannelMode::Mono => "Mono content",
52 Ac4ChannelMode::Stereo => "Stereo content",
53 Ac4ChannelMode::Multichannel => "Multichannel content",
54 Ac4ChannelMode::Reserved(_) => "reserved for future use",
55 }
56 }
57}
58broadcast_common::impl_spec_display!(Ac4ChannelMode, Reserved);
59
60#[derive(Debug, Clone, PartialEq, Eq)]
62#[cfg_attr(feature = "serde", derive(serde::Serialize))]
63#[cfg_attr(feature = "yoke", derive(yoke::Yokeable))]
64pub struct Ac4<'a> {
65 pub ac4_config_flag: bool,
67 pub ac4_toc_flag: bool,
69 pub ac4_dialog_enhancement_enabled: Option<bool>,
71 pub ac4_channel_mode: Option<Ac4ChannelMode>,
73 pub toc: Option<&'a [u8]>,
81 pub additional_info: &'a [u8],
83}
84
85impl<'a> Parse<'a> for Ac4<'a> {
86 type Error = crate::error::Error;
87 fn parse(sel: &'a [u8]) -> Result<Self> {
88 if sel.is_empty() {
89 return Err(Error::BufferTooShort {
90 need: 1,
91 have: sel.len(),
92 what: "AC-4 body",
93 });
94 }
95 let flags = sel[0];
96 let ac4_config_flag = (flags & 0x80) != 0;
97 let ac4_toc_flag = (flags & 0x40) != 0;
98 let mut pos = 1;
99 let (ac4_dialog_enhancement_enabled, ac4_channel_mode) = if ac4_config_flag {
100 if sel.len() < pos + 1 {
101 return Err(Error::BufferTooShort {
102 need: pos + 1,
103 have: sel.len(),
104 what: "AC-4 body",
105 });
106 }
107 let c = sel[pos];
108 pos += 1;
109 (
110 Some((c & 0x80) != 0),
111 Some(Ac4ChannelMode::from_u8((c >> 5) & 0x03)),
112 )
113 } else {
114 (None, None)
115 };
116 let toc = if ac4_toc_flag {
117 if sel.len() < pos + 1 {
118 return Err(Error::BufferTooShort {
119 need: pos + 1,
120 have: sel.len(),
121 what: "AC-4 body",
122 });
123 }
124 let toc_len = sel[pos] as usize;
125 pos += 1;
126 if sel.len() < pos + toc_len {
127 return Err(Error::BufferTooShort {
128 need: pos + toc_len,
129 have: sel.len(),
130 what: "AC-4 body",
131 });
132 }
133 let t = &sel[pos..pos + toc_len];
134 pos += toc_len;
135 Some(t)
136 } else {
137 None
138 };
139 Ok(Ac4 {
140 ac4_config_flag,
141 ac4_toc_flag,
142 ac4_dialog_enhancement_enabled,
143 ac4_channel_mode,
144 toc,
145 additional_info: &sel[pos..],
146 })
147 }
148}
149
150impl Serialize for Ac4<'_> {
151 type Error = crate::error::Error;
152 fn serialized_len(&self) -> usize {
153 1 + usize::from(self.ac4_config_flag)
154 + self.toc.map_or(0, |t| 1 + t.len())
155 + self.additional_info.len()
156 }
157 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
158 let len = self.serialized_len();
159 if buf.len() < len {
160 return Err(Error::OutputBufferTooSmall {
161 need: len,
162 have: buf.len(),
163 });
164 }
165 buf[0] = (u8::from(self.ac4_config_flag) << 7) | (u8::from(self.ac4_toc_flag) << 6);
166 let mut p = 1;
167 if self.ac4_config_flag {
168 let de = self.ac4_dialog_enhancement_enabled.unwrap_or(false);
169 let cm = self
170 .ac4_channel_mode
171 .unwrap_or(Ac4ChannelMode::Mono)
172 .to_u8()
173 & 0x03;
174 buf[p] = (u8::from(de) << 7) | (cm << 5);
175 p += 1;
176 }
177 if let Some(t) = self.toc {
178 buf[p] = t.len() as u8;
179 p += 1;
180 buf[p..p + t.len()].copy_from_slice(t);
181 p += t.len();
182 }
183 buf[p..p + self.additional_info.len()].copy_from_slice(self.additional_info);
184 Ok(len)
185 }
186}
187
188#[cfg(test)]
189mod tests {
190 use super::*;
191 use crate::descriptors::extension::test_support::*;
192 use crate::descriptors::extension::{ExtensionBody, ExtensionDescriptor};
193
194 #[test]
195 fn parse_ac4_full() {
196 let sel = [0xC0, 0x80 | (0x02 << 5), 0x02, 0x11, 0x22, 0x33];
198 let bytes = wrap(0x15, &sel);
199 let d = ExtensionDescriptor::parse(&bytes).unwrap();
200 match &d.body {
201 ExtensionBody::Ac4(b) => {
202 assert!(b.ac4_config_flag);
203 assert!(b.ac4_toc_flag);
204 assert_eq!(b.ac4_dialog_enhancement_enabled, Some(true));
205 assert_eq!(b.ac4_channel_mode, Some(Ac4ChannelMode::Multichannel));
206 assert_eq!(b.toc, Some([0x11u8, 0x22].as_slice()));
207 assert_eq!(b.additional_info, &[0x33]);
208 }
209 other => panic!("expected Ac4, got {other:?}"),
210 }
211 round_trip(&d);
212 }
213
214 #[test]
215 fn parse_ac4_minimal() {
216 let sel = [0x00]; let bytes = wrap(0x15, &sel);
218 let d = ExtensionDescriptor::parse(&bytes).unwrap();
219 match &d.body {
220 ExtensionBody::Ac4(b) => {
221 assert!(!b.ac4_config_flag);
222 assert!(!b.ac4_toc_flag);
223 assert_eq!(b.toc, None);
224 assert!(b.additional_info.is_empty());
225 }
226 other => panic!("expected Ac4, got {other:?}"),
227 }
228 round_trip(&d);
229 }
230
231 #[test]
232 fn ac4_channel_mode_roundtrip() {
233 for b in 0u8..=3 {
234 assert_eq!(Ac4ChannelMode::from_u8(b).to_u8(), b);
235 }
236 assert_eq!(Ac4ChannelMode::Reserved(4).to_u8(), 4);
237 }
238
239 #[test]
240 fn ac4_channel_mode_name() {
241 assert_eq!(Ac4ChannelMode::Mono.name(), "Mono content");
242 assert_eq!(Ac4ChannelMode::Stereo.name(), "Stereo content");
243 assert_eq!(Ac4ChannelMode::Multichannel.name(), "Multichannel content");
244 assert_eq!(
245 Ac4ChannelMode::Reserved(3).name(),
246 "reserved for future use"
247 );
248 }
249}