1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
//! The two mandatory Opus header packets, `OpusHead` and `OpusTags`
//! (RFC 7845 §5).
use crate::{ChannelLayout, Error, OpusDecoder, OpusEncoder, OpusMSEncoder, Result};
/// Magic signature of the identification header.
pub(crate) const OPUS_HEAD_MAGIC: &[u8; 8] = b"OpusHead";
/// Magic signature of the comment header.
pub(crate) const OPUS_TAGS_MAGIC: &[u8; 8] = b"OpusTags";
/// Opus is always framed at 48 kHz in Ogg, whatever rate the encoder ran at:
/// granule positions and pre-skip are counted in 48 kHz samples (RFC 7845 §4).
pub const GRANULE_RATE: u32 = 48_000;
/// The identification header — RFC 7845 §5.1.
///
/// This is the first packet of the stream and must sit alone on the first page.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct OpusHead {
/// Channels in the decoded stream, 1..=255.
pub channel_count: u8,
/// Samples at 48 kHz to discard from the decoder's output — the encoder's
/// algorithmic delay. See [`OpusHead::RECOMMENDED_PRE_SKIP`].
pub pre_skip: u16,
/// Sample rate of the *original* input, purely informational: it does not
/// change how the stream decodes. 0 means unspecified.
pub input_sample_rate: u32,
/// Gain to apply on playback, in Q7.8 dB. 0 = unity.
pub output_gain_q8: i16,
/// Channel mapping family: 0 = mono/stereo, 1 = Vorbis surround order,
/// 255 = discrete channels.
pub mapping_family: u8,
/// Opus streams in the packet. Always 1 for family 0.
pub stream_count: u8,
/// How many of those streams are coupled (stereo) pairs.
pub coupled_count: u8,
/// Which decoded channel feeds each output channel. Empty for family 0,
/// where the mapping is implicit.
pub channel_mapping: Vec<u8>,
}
impl OpusHead {
/// libopus's algorithmic delay at 48 kHz, for an encoder using
/// [`Application::Audio`](crate::Application::Audio) or
/// [`Voip`](crate::Application::Voip).
///
/// It is **not** right for every encoder.
/// [`RestrictedLowDelay`](crate::Application::RestrictedLowDelay) gives up
/// the 4 ms the other two spend keeping SILK and CELT aligned, and its real
/// delay is 120 samples rather than 312 — so a header built with this
/// constant over that encoder tells players to discard 192 samples of
/// genuine audio. Prefer [`for_encoder`](Self::for_encoder), which asks the
/// encoder instead of assuming.
pub const RECOMMENDED_PRE_SKIP: u16 = 312;
/// A mono or stereo header (mapping family 0) with the recommended pre-skip.
///
/// `input_sample_rate` is recorded for information only; pass the rate the
/// PCM you encoded was sampled at, or 0 if you would rather not say.
///
/// This is the right constructor when you are muxing packets you did not
/// encode yourself. When you do have the encoder, use
/// [`for_encoder`](Self::for_encoder): it takes the pre-skip from the
/// encoder's own delay rather than from
/// [`RECOMMENDED_PRE_SKIP`](Self::RECOMMENDED_PRE_SKIP), which is wrong for
/// [`RestrictedLowDelay`](crate::Application::RestrictedLowDelay).
pub fn new(channel_count: u8, input_sample_rate: u32) -> Result<Self> {
if channel_count == 0 || channel_count > 2 {
return Err(Error::InvalidArgument(
"mapping family 0 supports only 1 or 2 channels",
));
}
Ok(OpusHead {
channel_count,
pre_skip: Self::RECOMMENDED_PRE_SKIP,
input_sample_rate,
output_gain_q8: 0,
mapping_family: 0,
stream_count: 1,
coupled_count: channel_count - 1,
channel_mapping: Vec::new(),
})
}
/// A header describing what `encoder` produces, with the pre-skip taken
/// from the encoder's own algorithmic delay.
///
/// This is the constructor to reach for when you are encoding and muxing in
/// one pass. [`new`](Self::new) assumes
/// [`RECOMMENDED_PRE_SKIP`](Self::RECOMMENDED_PRE_SKIP), which is correct
/// for two of the three [`Application`](crate::Application)s and four
/// milliseconds wrong for the third; this one asks
/// [`OpusEncoder::lookahead`] and scales it to the 48 kHz that
/// [`pre_skip`](Self::pre_skip) is counted in.
///
/// ```
/// use opus_pure::{Application, OggOpusWriter, OpusEncoder, OpusHead};
///
/// let encoder = OpusEncoder::new(48_000, 2, Application::RestrictedLowDelay)?;
/// let head = OpusHead::for_encoder(&encoder, 48_000);
/// assert_eq!(head.pre_skip, 120); // not the 312 a fixed constant would give
///
/// let writer = OggOpusWriter::new(Vec::new(), head)?;
/// # let _ = writer;
/// # Ok::<(), opus_pure::Error>(())
/// ```
pub fn for_encoder(encoder: &OpusEncoder, input_sample_rate: u32) -> Self {
let channel_count = encoder.channels() as u8;
OpusHead {
channel_count,
pre_skip: pre_skip_48k(encoder.lookahead(), encoder.sample_rate()),
input_sample_rate,
output_gain_q8: 0,
mapping_family: 0,
stream_count: 1,
coupled_count: channel_count - 1,
channel_mapping: Vec::new(),
}
}
/// A header describing what a multistream `encoder` produces.
///
/// The surround counterpart of [`for_encoder`](Self::for_encoder), and the
/// missing half of writing a surround `.opus` file: it takes the stream
/// count, the coupled count and the channel mapping from the encoder's own
/// [`ChannelLayout`], so they cannot disagree with what is actually in the
/// packets.
///
/// ```
/// use opus_pure::{Application, OggOpusWriter, OpusHead, OpusMSEncoder};
///
/// let encoder = OpusMSEncoder::new(48_000, 6, 1, Application::Audio)?;
/// let head = OpusHead::for_ms_encoder(&encoder, 48_000);
/// assert_eq!(head.channel_count, 6);
/// assert_eq!(head.stream_count, 4);
/// assert_eq!(head.coupled_count, 2);
///
/// let writer = OggOpusWriter::new(Vec::new(), head)?;
/// # let _ = writer;
/// # Ok::<(), opus_pure::Error>(())
/// ```
pub fn for_ms_encoder(encoder: &OpusMSEncoder, input_sample_rate: u32) -> Self {
let stream = &encoder.streams()[0];
let mut head = Self::for_layout(encoder.layout(), input_sample_rate);
head.pre_skip = pre_skip_48k(stream.lookahead(), stream.sample_rate());
head
}
/// A header for a channel `layout`, with the recommended pre-skip.
///
/// Use it when you have a layout but not the encoder that goes with it —
/// remuxing, say. With an encoder in hand,
/// [`for_ms_encoder`](Self::for_ms_encoder) is exact about the pre-skip
/// where this is only conventional.
pub fn for_layout(layout: &ChannelLayout, input_sample_rate: u32) -> Self {
OpusHead {
channel_count: layout.nb_channels as u8,
pre_skip: Self::RECOMMENDED_PRE_SKIP,
input_sample_rate,
output_gain_q8: 0,
mapping_family: layout.mapping_family,
stream_count: layout.nb_streams as u8,
coupled_count: layout.nb_coupled_streams as u8,
// Family 0's mapping is implicit and the header carries none.
channel_mapping: if layout.mapping_family == 0 {
Vec::new()
} else {
layout.mapping.clone()
},
}
}
/// A decoder configured for this stream, at `sample_rate`.
///
/// Three facts have to travel from the header into the decoder, and only
/// one of them announces itself when it is missed. The channel count is
/// checked — a decoder built for the wrong one fails or interleaves
/// visibly. The pre-skip is what [`Trim`](super::Trim) needs, and it takes
/// the header directly. [`output_gain_q8`](Self::output_gain_q8) is the
/// silent one: RFC 7845 §5.1 says a player SHOULD apply it, and a stream
/// that carries a non-zero gain plays at the wrong level with no other
/// symptom if it does not. This constructor carries all three of those
/// decisions so none of them is a thing to remember.
///
/// `sample_rate` is the rate you want *out*, not one stored in the file:
/// Opus decodes to whichever of 8/12/16/24/48 kHz you ask for.
///
/// ```no_run
/// use opus_pure::OggOpusReader;
///
/// let mut reader = OggOpusReader::new(std::fs::File::open("in.opus")?)?;
/// let mut decoder = reader.head().decoder(48_000)?; // gain already set
/// # let _ = &mut decoder;
/// # Ok::<(), opus_pure::Error>(())
/// ```
///
/// Only mapping family 0 (mono and stereo) has a plain [`OpusDecoder`]
/// behind it; a surround stream needs
/// [`OpusMSDecoder`](crate::OpusMSDecoder), built from
/// [`channel_count`](Self::channel_count) and
/// [`mapping_family`](Self::mapping_family), and this reports that rather
/// than quietly decoding one stream of several. Rendering a stream to a
/// channel count that is not its own is likewise a case for
/// [`OpusDecoder::new`] plus setting
/// [`gain_q8`](OpusDecoder::gain_q8) by hand.
pub fn decoder(&self, sample_rate: i32) -> Result<OpusDecoder> {
if self.mapping_family != 0 {
return Err(Error::InvalidArgument(
"mapping family is not 0; a surround stream needs an OpusMSDecoder",
));
}
let mut decoder = OpusDecoder::new(sample_rate, self.channel_count as usize)?;
decoder.gain_q8 = i32::from(self.output_gain_q8);
Ok(decoder)
}
/// Serialize to the wire format.
pub(crate) fn to_packet(&self) -> Vec<u8> {
let mut v = Vec::with_capacity(19 + self.channel_mapping.len());
v.extend_from_slice(OPUS_HEAD_MAGIC);
v.push(1); // version
v.push(self.channel_count);
v.extend_from_slice(&self.pre_skip.to_le_bytes());
v.extend_from_slice(&self.input_sample_rate.to_le_bytes());
v.extend_from_slice(&self.output_gain_q8.to_le_bytes());
v.push(self.mapping_family);
if self.mapping_family != 0 {
v.push(self.stream_count);
v.push(self.coupled_count);
v.extend_from_slice(&self.channel_mapping);
}
v
}
/// Parse the wire format, rejecting anything RFC 7845 forbids.
pub(crate) fn parse(data: &[u8]) -> Result<Self> {
if data.len() < 19 || &data[..8] != OPUS_HEAD_MAGIC {
return Err(Error::InvalidStream("not an OpusHead packet"));
}
// §5.1: "the major version number is the upper four bits"; a decoder must
// reject major versions above 0 and tolerate unknown minor versions.
if data[8] >> 4 != 0 {
return Err(Error::InvalidStream("unsupported OpusHead version"));
}
let channel_count = data[9];
if channel_count == 0 {
return Err(Error::InvalidStream("OpusHead declares zero channels"));
}
let mapping_family = data[18];
let (stream_count, coupled_count, channel_mapping) = if mapping_family == 0 {
if channel_count > 2 {
return Err(Error::InvalidStream(
"mapping family 0 allows at most 2 channels",
));
}
(1u8, channel_count - 1, Vec::new())
} else {
let need = 21 + channel_count as usize;
if data.len() < need {
return Err(Error::InvalidStream(
"OpusHead channel mapping is truncated",
));
}
let stream_count = data[19];
let coupled_count = data[20];
if stream_count == 0 {
return Err(Error::InvalidStream("OpusHead declares zero streams"));
}
if coupled_count > stream_count {
return Err(Error::InvalidStream(
"OpusHead couples more streams than it declares",
));
}
// Every output channel must name a decoded channel that exists, or
// 255 for silence (§5.1.1).
let decoded = stream_count as usize + coupled_count as usize;
let mapping = data[21..need].to_vec();
if mapping.iter().any(|&m| m != 255 && (m as usize) >= decoded) {
return Err(Error::InvalidStream(
"OpusHead channel mapping references a stream that does not exist",
));
}
(stream_count, coupled_count, mapping)
};
Ok(OpusHead {
channel_count,
pre_skip: u16::from_le_bytes(data[10..12].try_into().unwrap()),
input_sample_rate: u32::from_le_bytes(data[12..16].try_into().unwrap()),
output_gain_q8: i16::from_le_bytes(data[16..18].try_into().unwrap()),
mapping_family,
stream_count,
coupled_count,
channel_mapping,
})
}
/// Playback gain in dB, from the Q7.8 fixed-point field.
pub fn output_gain_db(&self) -> f32 {
self.output_gain_q8 as f32 / 256.0
}
}
/// The comment header — RFC 7845 §5.2.
///
/// Comments are held exactly as they appear on the wire, so a stream survives a
/// read/write round trip byte-for-byte even when it carries entries this crate
/// does not understand. Use [`OpusTags::get`] and [`OpusTags::push`] for the
/// usual `NAME=value` access.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct OpusTags {
/// Identifies the encoder that produced the stream.
pub vendor: String,
/// Raw `NAME=value` entries, in file order.
pub comments: Vec<String>,
}
impl Default for OpusTags {
fn default() -> Self {
OpusTags {
vendor: concat!("opus-pure ", env!("CARGO_PKG_VERSION")).to_string(),
comments: Vec::new(),
}
}
}
impl OpusTags {
/// Empty tags carrying this crate's vendor string.
pub fn new() -> Self {
Self::default()
}
/// First value for `name`, matched case-insensitively as the spec requires.
pub fn get(&self, name: &str) -> Option<&str> {
self.get_all(name).next()
}
/// Every value stored under `name`, in file order.
///
/// A Vorbis comment name may legitimately repeat, so this is the honest
/// form of [`get`](Self::get) for anything that can have more than one
/// value — `ARTIST`, `PERFORMER`, `GENRE`.
pub fn get_all<'a, 'n>(&'a self, name: &'n str) -> impl Iterator<Item = &'a str> + use<'a, 'n> {
self.comments.iter().filter_map(move |c| {
let (k, v) = c.split_once('=')?;
k.eq_ignore_ascii_case(name).then_some(v)
})
}
/// Append a `NAME=value` entry.
///
/// Named `push` rather than `insert` because that is what it does: Vorbis
/// comments allow a name to appear more than once — several `ARTIST` lines
/// are how a collaboration is spelled — so this adds an entry and never
/// replaces one. [`get`](Self::get) then returns the first;
/// [`get_all`](Self::get_all) returns every one.
///
/// Comment names are ASCII 0x20..=0x7D excluding `=`; a name outside that set
/// is rejected rather than written out to produce a file no other tool reads.
pub fn push(&mut self, name: &str, value: &str) -> Result<()> {
if name.is_empty()
|| !name
.bytes()
.all(|b| (0x20..=0x7d).contains(&b) && b != b'=')
{
return Err(Error::InvalidArgument(
"comment name must be ASCII 0x20..=0x7D excluding '='",
));
}
self.comments.push(format!("{name}={value}"));
Ok(())
}
pub(crate) fn to_packet(&self) -> Vec<u8> {
let mut v = Vec::new();
v.extend_from_slice(OPUS_TAGS_MAGIC);
v.extend_from_slice(&(self.vendor.len() as u32).to_le_bytes());
v.extend_from_slice(self.vendor.as_bytes());
v.extend_from_slice(&(self.comments.len() as u32).to_le_bytes());
for c in &self.comments {
v.extend_from_slice(&(c.len() as u32).to_le_bytes());
v.extend_from_slice(c.as_bytes());
}
v
}
pub(crate) fn parse(data: &[u8]) -> Result<Self> {
if data.len() < 12 || &data[..8] != OPUS_TAGS_MAGIC {
return Err(Error::InvalidStream("not an OpusTags packet"));
}
let mut pos = 8;
let take_len = |pos: &mut usize| -> Result<usize> {
if data.len() < *pos + 4 {
return Err(Error::InvalidStream("OpusTags is truncated"));
}
let n = u32::from_le_bytes(data[*pos..*pos + 4].try_into().unwrap()) as usize;
*pos += 4;
// Check against the buffer, not just as a length: a hostile 4 GiB
// length must not become an allocation.
if data.len() < *pos + n {
return Err(Error::InvalidStream("OpusTags string runs past the packet"));
}
Ok(n)
};
let vlen = take_len(&mut pos)?;
let vendor = String::from_utf8_lossy(&data[pos..pos + vlen]).into_owned();
pos += vlen;
let count = take_len(&mut pos)?;
// Each remaining comment needs at least its own 4-byte length prefix, so
// a count that cannot fit is malformed regardless of the strings.
if count.saturating_mul(4) > data.len() - pos {
return Err(Error::InvalidStream(
"OpusTags comment count exceeds the packet",
));
}
let mut comments = Vec::with_capacity(count);
for _ in 0..count {
let n = take_len(&mut pos)?;
comments.push(String::from_utf8_lossy(&data[pos..pos + n]).into_owned());
pos += n;
}
Ok(OpusTags { vendor, comments })
}
}
/// An encoder's lookahead, expressed in the 48 kHz samples
/// [`OpusHead::pre_skip`] is counted in (RFC 7845 §5.1).
fn pre_skip_48k(lookahead: usize, sample_rate: i32) -> u16 {
let scaled = lookahead as u64 * GRANULE_RATE as u64 / sample_rate as u64;
scaled.min(u16::MAX as u64) as u16
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn head_round_trips_family_0() {
let head = OpusHead::new(2, 48_000).unwrap();
assert_eq!(OpusHead::parse(&head.to_packet()).unwrap(), head);
}
#[test]
fn head_round_trips_family_1_surround() {
let head = OpusHead {
channel_count: 6,
pre_skip: 312,
input_sample_rate: 48_000,
output_gain_q8: -512,
mapping_family: 1,
stream_count: 4,
coupled_count: 2,
channel_mapping: vec![0, 4, 1, 2, 3, 5],
};
let parsed = OpusHead::parse(&head.to_packet()).unwrap();
assert_eq!(parsed, head);
assert_eq!(parsed.output_gain_db(), -2.0);
}
#[test]
fn head_rejects_malformed_input() {
assert!(matches!(
OpusHead::parse(b"nope"),
Err(Error::InvalidStream(_))
));
let mut p = OpusHead::new(2, 48_000).unwrap().to_packet();
p[8] = 0x10; // major version 1
assert!(matches!(OpusHead::parse(&p), Err(Error::InvalidStream(_))));
let mut p = OpusHead::new(2, 48_000).unwrap().to_packet();
p[9] = 0; // zero channels
assert!(matches!(OpusHead::parse(&p), Err(Error::InvalidStream(_))));
let mut p = OpusHead::new(2, 48_000).unwrap().to_packet();
p[9] = 3; // family 0 caps at 2
assert!(matches!(OpusHead::parse(&p), Err(Error::InvalidStream(_))));
}
/// An unknown *minor* version must still parse — §5.1 requires forward
/// compatibility within major version 0.
#[test]
fn head_accepts_unknown_minor_version() {
let mut p = OpusHead::new(1, 16_000).unwrap().to_packet();
p[8] = 0x0f;
assert!(OpusHead::parse(&p).is_ok());
}
#[test]
fn head_rejects_mapping_that_names_a_missing_stream() {
let head = OpusHead {
channel_count: 2,
pre_skip: 312,
input_sample_rate: 48_000,
output_gain_q8: 0,
mapping_family: 1,
stream_count: 1,
coupled_count: 0,
channel_mapping: vec![0, 7], // only decoded channel 0 exists
};
assert!(matches!(
OpusHead::parse(&head.to_packet()),
Err(Error::InvalidStream(_))
));
}
#[test]
fn head_rejects_more_coupled_than_streams() {
let head = OpusHead {
channel_count: 2,
pre_skip: 312,
input_sample_rate: 48_000,
output_gain_q8: 0,
mapping_family: 1,
stream_count: 1,
coupled_count: 2,
channel_mapping: vec![0, 1],
};
assert!(matches!(
OpusHead::parse(&head.to_packet()),
Err(Error::InvalidStream(_))
));
}
#[test]
fn tags_round_trip() {
let mut tags = OpusTags::new();
tags.push("TITLE", "A Song").unwrap();
tags.push("ARTIST", "Someone").unwrap();
let parsed = OpusTags::parse(&tags.to_packet()).unwrap();
assert_eq!(parsed, tags);
assert_eq!(parsed.get("title"), Some("A Song"));
assert_eq!(parsed.get("MISSING"), None);
}
#[test]
fn tags_reject_invalid_names() {
let mut tags = OpusTags::new();
assert!(tags.push("BAD=NAME", "x").is_err());
assert!(tags.push("", "x").is_err());
assert!(tags.push("nul\0", "x").is_err());
assert!(tags.push("Ünicode", "x").is_err());
assert!(tags.comments.is_empty());
}
/// A length field must be validated against the buffer before it is used to
/// size an allocation.
#[test]
fn tags_reject_oversized_lengths() {
let mut p = OpusTags::new().to_packet();
p[8..12].copy_from_slice(&u32::MAX.to_le_bytes());
assert!(matches!(OpusTags::parse(&p), Err(Error::InvalidStream(_))));
let mut p = OpusTags::new().to_packet();
let vlen = u32::from_le_bytes(p[8..12].try_into().unwrap()) as usize;
let at = 12 + vlen;
p[at..at + 4].copy_from_slice(&1_000_000u32.to_le_bytes());
assert!(matches!(OpusTags::parse(&p), Err(Error::InvalidStream(_))));
}
#[test]
fn tags_preserve_unknown_entries_verbatim() {
let tags = OpusTags {
vendor: "someone else".into(),
comments: vec!["WEIRD".into(), "X=y=z".into(), "=empty-name".into()],
};
assert_eq!(OpusTags::parse(&tags.to_packet()).unwrap(), tags);
}
}