zvidlib_opus_syntax/
lib.rs1#[allow(unused_imports)]
16use zvidlib_core::*;
17
18use crate::{EncodedAudioSample, Error, ErrorKind, Result};
19
20pub const OPUS_SAMPLE_RATE: u32 = 48_000;
22
23pub const OPUS_PREROLL_SAMPLES: u32 = 15_360;
35
36#[derive(Clone, Debug, Eq, PartialEq)]
45pub struct OpusHead {
46 pub channels: u8,
48 pub pre_skip: u16,
51 pub input_sample_rate: u32,
55 pub output_gain: i16,
57 pub channel_mapping_family: u8,
60 pub stream_count: u8,
62 pub coupled_count: u8,
64 pub channel_mapping: Vec<u8>,
67}
68
69impl OpusHead {
70 pub fn new(channels: u8, pre_skip: u16, input_sample_rate: u32) -> Result<Self> {
72 if channels != 1 && channels != 2 {
73 return Err(invalid("an Opus mapping family 0 stream is mono or stereo"));
74 }
75 Ok(Self {
76 channels,
77 pre_skip,
78 input_sample_rate,
79 output_gain: 0,
80 channel_mapping_family: 0,
81 stream_count: 1,
82 coupled_count: channels - 1,
83 channel_mapping: (0..channels).collect(),
84 })
85 }
86
87 pub fn from_dops(dops: &[u8]) -> Result<Self> {
91 let payload = crate::codec_config::box_payload(dops, b"dOps")?;
92 let declared = u32::from_be_bytes(dops[..4].try_into().expect("checked by box_payload"));
93 if usize::try_from(declared).ok() != Some(dops.len()) {
94 return Err(malformed("Opus dOps box size is inconsistent"));
95 }
96 let header = payload
97 .get(..11)
98 .ok_or_else(|| malformed("Opus dOps box is truncated"))?;
99 if header[0] != 0 {
100 return Err(unsupported(format!(
101 "Opus dOps version {} is unsupported",
102 header[0]
103 )));
104 }
105 let head = Self::from_fields(
106 header[1],
107 u16::from_be_bytes([header[2], header[3]]),
108 u32::from_be_bytes([header[4], header[5], header[6], header[7]]),
109 i16::from_be_bytes([header[8], header[9]]),
110 header[10],
111 &payload[11..],
112 )?;
113 Ok(head)
114 }
115
116 pub fn to_dops(&self) -> Vec<u8> {
118 let mut payload = vec![0, self.channels];
119 payload.extend_from_slice(&self.pre_skip.to_be_bytes());
120 payload.extend_from_slice(&self.input_sample_rate.to_be_bytes());
121 payload.extend_from_slice(&self.output_gain.to_be_bytes());
122 self.push_mapping(&mut payload);
123 let mut whole = u32::try_from(payload.len() + 8)
124 .expect("a dOps box is at most a few hundred bytes")
125 .to_be_bytes()
126 .to_vec();
127 whole.extend_from_slice(b"dOps");
128 whole.extend_from_slice(&payload);
129 whole
130 }
131
132 pub fn from_identification_header(packet: &[u8]) -> Result<Self> {
135 let header = packet
136 .get(..19)
137 .ok_or_else(|| malformed("Opus identification header is truncated"))?;
138 if &header[..8] != b"OpusHead" {
139 return Err(malformed(
140 "Opus identification header does not start with OpusHead",
141 ));
142 }
143 if header[8] >> 4 != 0 {
146 return Err(unsupported(format!(
147 "Opus identification header version {} is unsupported",
148 header[8]
149 )));
150 }
151 Self::from_fields(
152 header[9],
153 u16::from_le_bytes([header[10], header[11]]),
154 u32::from_le_bytes([header[12], header[13], header[14], header[15]]),
155 i16::from_le_bytes([header[16], header[17]]),
156 header[18],
157 &packet[19..],
158 )
159 }
160
161 pub fn to_identification_header(&self) -> Vec<u8> {
164 let mut packet = b"OpusHead".to_vec();
165 packet.push(1);
166 packet.push(self.channels);
167 packet.extend_from_slice(&self.pre_skip.to_le_bytes());
168 packet.extend_from_slice(&self.input_sample_rate.to_le_bytes());
169 packet.extend_from_slice(&self.output_gain.to_le_bytes());
170 self.push_mapping(&mut packet);
171 packet
172 }
173
174 pub fn output_gain_factor(&self) -> f32 {
176 10_f32.powf(f32::from(self.output_gain) / (20.0 * 256.0))
177 }
178
179 fn from_fields(
180 channels: u8,
181 pre_skip: u16,
182 input_sample_rate: u32,
183 output_gain: i16,
184 channel_mapping_family: u8,
185 mapping_table: &[u8],
186 ) -> Result<Self> {
187 if channels == 0 {
188 return Err(malformed("an Opus stream has at least one channel"));
189 }
190 if channel_mapping_family == 0 {
191 if channels > 2 {
192 return Err(malformed(
193 "an Opus mapping family 0 stream is mono or stereo",
194 ));
195 }
196 return Ok(Self {
197 output_gain,
198 ..Self::new(channels, pre_skip, input_sample_rate)?
199 });
200 }
201 let table = mapping_table
202 .get(..2 + usize::from(channels))
203 .ok_or_else(|| malformed("Opus channel mapping table is truncated"))?;
204 let (stream_count, coupled_count) = (table[0], table[1]);
205 if stream_count == 0 || coupled_count > stream_count {
206 return Err(malformed("Opus channel mapping has invalid stream counts"));
207 }
208 let decoded_channels = u16::from(stream_count) + u16::from(coupled_count);
209 let channel_mapping = table[2..].to_vec();
210 if channel_mapping
211 .iter()
212 .any(|&index| index != 255 && u16::from(index) >= decoded_channels)
213 {
214 return Err(malformed(
215 "Opus channel mapping names a channel no stream decodes",
216 ));
217 }
218 Ok(Self {
219 channels,
220 pre_skip,
221 input_sample_rate,
222 output_gain,
223 channel_mapping_family,
224 stream_count,
225 coupled_count,
226 channel_mapping,
227 })
228 }
229
230 fn push_mapping(&self, out: &mut Vec<u8>) {
231 out.push(self.channel_mapping_family);
232 if self.channel_mapping_family != 0 {
233 out.push(self.stream_count);
234 out.push(self.coupled_count);
235 out.extend_from_slice(&self.channel_mapping);
236 }
237 }
238}
239
240pub fn opus_packet_samples(packet: &[u8]) -> Result<u32> {
243 let &toc = packet
244 .first()
245 .ok_or_else(|| invalid_packet("packet is empty"))?;
246 let frames = match toc & 0x03 {
247 0 => 1,
248 1 | 2 => 2,
249 _ => {
250 let count = packet
251 .get(1)
252 .ok_or_else(|| invalid_packet("code 3 packet has no frame count"))?
253 & 0x3F;
254 if count == 0 {
255 return Err(invalid_packet("code 3 packet declares zero frames"));
256 }
257 u32::from(count)
258 }
259 };
260 let samples = frames * frame_samples(toc);
261 if samples > MAX_PACKET_SAMPLES {
264 return Err(invalid_packet("packet claims more than 120 ms"));
265 }
266 Ok(samples)
267}
268
269fn frame_samples(toc: u8) -> u32 {
273 let size = u32::from((toc >> 3) & 0x03);
274 if toc & 0x80 != 0 {
275 (OPUS_SAMPLE_RATE << size) / 400
276 } else if toc & 0x60 == 0x60 {
277 if toc & 0x08 != 0 {
278 OPUS_SAMPLE_RATE / 50
279 } else {
280 OPUS_SAMPLE_RATE / 100
281 }
282 } else if size == 3 {
283 OPUS_SAMPLE_RATE * 60 / 1000
284 } else {
285 (OPUS_SAMPLE_RATE << size) / 100
286 }
287}
288
289pub fn opus_preroll_packets(packets: &[EncodedAudioSample]) -> usize {
293 let shortest = packets
294 .iter()
295 .map(|packet| packet.decoded_range.len())
296 .filter(|&length| length > 0)
297 .min()
298 .unwrap_or(u64::from(OPUS_PREROLL_SAMPLES));
299 usize::try_from(u64::from(OPUS_PREROLL_SAMPLES).div_ceil(shortest)).unwrap_or(usize::MAX)
300}
301
302const MAX_PACKET_SAMPLES: u32 = 5_760;
304
305fn invalid_packet(what: &str) -> Error {
308 codec(format!("Opus: invalid packet: {what}"))
309}
310
311fn codec(message: impl Into<String>) -> Error {
312 Error::new(ErrorKind::Codec, message)
313}
314
315fn invalid(message: impl Into<String>) -> Error {
316 Error::new(ErrorKind::InvalidInput, message)
317}
318
319fn malformed(message: impl Into<String>) -> Error {
320 Error::new(ErrorKind::MalformedMedia, message)
321}
322
323fn unsupported(message: impl Into<String>) -> Error {
324 Error::new(ErrorKind::Unsupported, message)
325}