Skip to content

Commit f75ceb6

Browse files
thinking-towera1phyr
authored andcommitted
opus: Read identification header. (#59)
Port from existing opus branch.
1 parent 1cb82d8 commit f75ceb6

File tree

1 file changed

+314
-0
lines changed
  • symphonia-codec-opus/src

1 file changed

+314
-0
lines changed

symphonia-codec-opus/src/lib.rs

Lines changed: 314 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,315 @@
1+
// Symphonia
2+
// Copyright (c) 2019-2021 The Project Symphonia Developers.
3+
//
4+
// This Source Code Form is subject to the terms of the Mozilla Public
5+
// License, v. 2.0. If a copy of the MPL was not distributed with this
6+
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
17

8+
#![warn(rust_2018_idioms)]
9+
#![forbid(unsafe_code)]
10+
// The following lints are allowed in all Symphonia crates. Please see clippy.toml for their
11+
// justification.
12+
#![allow(clippy::comparison_chain)]
13+
#![allow(clippy::excessive_precision)]
14+
#![allow(clippy::identity_op)]
15+
#![allow(clippy::manual_range_contains)]
16+
// Disable to better express the specification.
17+
#![allow(clippy::collapsible_else_if)]
18+
19+
use symphonia_core::audio::GenericAudioBufferRef;
20+
use symphonia_core::codecs::audio::well_known::CODEC_ID_OPUS;
21+
use symphonia_core::codecs::audio::{
22+
AudioCodecParameters, AudioDecoder, AudioDecoderOptions, FinalizeResult,
23+
};
24+
use symphonia_core::codecs::registry::{RegisterableAudioDecoder, SupportedAudioCodec};
25+
use symphonia_core::codecs::CodecInfo;
26+
use symphonia_core::errors::{decode_error, unsupported_error, Result};
27+
use symphonia_core::formats::Packet;
28+
use symphonia_core::io::{BufReader, ReadBytes};
29+
use symphonia_core::support_audio_codec;
30+
31+
#[allow(dead_code)]
32+
pub struct OpusDecoder {
33+
ident_header: IdentificationHeader,
34+
params: AudioCodecParameters,
35+
}
36+
37+
impl OpusDecoder {
38+
fn try_new(params: &AudioCodecParameters, _: &AudioDecoderOptions) -> Result<Self> {
39+
let extra_data = match params.extra_data.as_ref() {
40+
Some(buf) => buf,
41+
_ => return unsupported_error("opus: missing extra data"),
42+
};
43+
44+
let mut reader = BufReader::new(extra_data);
45+
46+
let ident_header = read_ident_header(&mut reader)?;
47+
Ok(OpusDecoder { ident_header, params: params.clone() })
48+
}
49+
}
50+
51+
impl AudioDecoder for OpusDecoder {
52+
fn reset(&mut self) {
53+
unimplemented!()
54+
}
55+
56+
fn codec_info(&self) -> &CodecInfo {
57+
&Self::supported_codecs()[0].info
58+
}
59+
60+
fn codec_params(&self) -> &AudioCodecParameters {
61+
&self.params
62+
}
63+
64+
#[allow(unused_variables)]
65+
fn decode(&mut self, packet: &Packet) -> Result<GenericAudioBufferRef<'_>> {
66+
unimplemented!()
67+
}
68+
69+
fn finalize(&mut self) -> FinalizeResult {
70+
unimplemented!()
71+
}
72+
73+
fn last_decoded(&self) -> GenericAudioBufferRef<'_> {
74+
unimplemented!()
75+
}
76+
}
77+
78+
impl RegisterableAudioDecoder for OpusDecoder {
79+
fn try_registry_new(
80+
params: &AudioCodecParameters,
81+
opts: &AudioDecoderOptions,
82+
) -> Result<Box<dyn AudioDecoder>>
83+
where
84+
Self: Sized,
85+
{
86+
Ok(Box::new(OpusDecoder::try_new(params, opts)?))
87+
}
88+
89+
fn supported_codecs() -> &'static [SupportedAudioCodec] {
90+
&[support_audio_codec!(CODEC_ID_OPUS, "opus", "Opus")]
91+
}
92+
}
93+
94+
#[derive(Debug)]
95+
pub struct IdentificationHeader {
96+
pub output_channel_count: u8,
97+
pub pre_skip: u16,
98+
pub input_sample_rate: u32,
99+
pub output_gain: u16,
100+
pub channel_mapping_family: u8,
101+
pub stream_count: u8,
102+
pub coupled_stream_count: u8,
103+
pub channel_mapping: [u8; 8],
104+
}
105+
106+
/** Create an IdentificationHeader from \a reader.
107+
*
108+
* If the header is invalid, a DecodeError is returned.
109+
*
110+
* See RFC 7845 Section 5.1, https://tools.ietf.org/pdf/rfc7845.pdf.
111+
*/
112+
fn read_ident_header<B: ReadBytes>(reader: &mut B) -> Result<IdentificationHeader> {
113+
// The first 8 bytes are the magic signature ASCII bytes.
114+
const OGG_OPUS_MAGIC_SIGNATURE: &[u8] = b"OpusHead";
115+
116+
let mut magic_signature = [0; 8];
117+
reader.read_buf_exact(&mut magic_signature)?;
118+
119+
if magic_signature != *OGG_OPUS_MAGIC_SIGNATURE {
120+
return decode_error("incorrect opus signature");
121+
}
122+
123+
// The next byte is the OGG Opus encapsulation version.
124+
const OGG_OPUS_VERSION: u8 = 0x01;
125+
126+
let mut version = [0; 1];
127+
reader.read_buf_exact(&mut version)?;
128+
129+
// TODO: Allow version numbers that are < 15 and disallow all > 16.
130+
// See RFC 7845 Section 5.1 (Version).
131+
if version[0] != OGG_OPUS_VERSION {
132+
return decode_error("incorrect opus version");
133+
}
134+
135+
// The next byte is the number of channels/
136+
let output_channel_count = reader.read_byte()?;
137+
138+
if output_channel_count == 0 {
139+
return decode_error("output channel count is 0");
140+
}
141+
142+
// The next 16-bit integer is the pre-skip padding.
143+
let pre_skip = reader.read_u16()?;
144+
145+
// The next 32-bit integer is the sample rate of the original audio.
146+
let input_sample_rate = reader.read_u32()?;
147+
148+
// Next, the 16-bit gain value.
149+
let output_gain = reader.read_u16()?;
150+
151+
// The next byte indicates the channel mapping. Most of these values are reserved.
152+
let channel_mapping_family = reader.read_byte()?;
153+
154+
let (stream_count, coupled_stream_count) = match channel_mapping_family {
155+
// RTP mapping. Supports up-to 2 channels.
156+
0 => {
157+
if output_channel_count > 2 {
158+
return decode_error("invalid output channel count");
159+
}
160+
161+
(1, output_channel_count - 1)
162+
}
163+
// Vorbis mapping. Supports 1 to 8 channels.
164+
1 => {
165+
if output_channel_count > 8 {
166+
return decode_error("invalid output channel count");
167+
}
168+
169+
let stream_count = reader.read_u8()?;
170+
if stream_count == 0 {
171+
return decode_error("stream count is 0");
172+
}
173+
174+
let coupled_stream_count = reader.read_u8()?;
175+
(stream_count, coupled_stream_count)
176+
}
177+
_ => return decode_error("reserved mapping family"),
178+
};
179+
180+
if stream_count.checked_add(coupled_stream_count).is_none() {
181+
return decode_error("stream count + coupled stream count > 255");
182+
}
183+
184+
let mut channel_mapping = [0; 8];
185+
186+
// The channel mapping table is only read if not using the RTP mapping.
187+
if channel_mapping_family != 0 {
188+
for mapping in &mut channel_mapping[..output_channel_count as usize] {
189+
*mapping = reader.read_u8()?;
190+
}
191+
}
192+
193+
Ok(IdentificationHeader {
194+
output_channel_count,
195+
pre_skip,
196+
input_sample_rate,
197+
output_gain,
198+
channel_mapping_family,
199+
stream_count,
200+
coupled_stream_count,
201+
channel_mapping,
202+
})
203+
}
204+
205+
#[cfg(test)]
206+
mod tests {
207+
use super::*;
208+
209+
#[test]
210+
fn verify_err_if_no_magic_signature() {
211+
let bytes: [u8; 23] = [
212+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x09, 0x38, 0x01, 0x80, 0xbb,
213+
0x00, 0x00, 0x00, 0x00, 0x01, 0x4f, 0x67, 0x67, 0x53,
214+
];
215+
let mut reader = BufReader::new(&bytes);
216+
let result = read_ident_header(&mut reader);
217+
assert!(result.is_err());
218+
assert_eq!(result.unwrap_err().to_string(), "malformed stream: incorrect opus signature");
219+
}
220+
221+
#[test]
222+
fn verify_err_if_version_number_neq_1() {
223+
let bytes: [u8; 23] = [
224+
0x4f, 0x70, 0x75, 0x73, 0x48, 0x65, 0x61, 0x64, 0x02, 0x09, 0x38, 0x01, 0x80, 0xbb,
225+
0x00, 0x00, 0x00, 0x00, 0x01, 0x4f, 0x67, 0x67, 0x53,
226+
];
227+
let mut reader = BufReader::new(&bytes);
228+
let result = read_ident_header(&mut reader);
229+
assert!(result.is_err());
230+
assert_eq!(result.unwrap_err().to_string(), "malformed stream: incorrect opus version");
231+
}
232+
233+
#[test]
234+
fn verify_err_if_channel_count_eq_0() {
235+
let bytes: [u8; 23] = [
236+
0x4f, 0x70, 0x75, 0x73, 0x48, 0x65, 0x61, 0x64, 0x01, 0x00, 0x38, 0x01, 0x80, 0xbb,
237+
0x00, 0x00, 0x00, 0x00, 0x01, 0x4f, 0x67, 0x67, 0x53,
238+
];
239+
let mut reader = BufReader::new(&bytes);
240+
let result = read_ident_header(&mut reader);
241+
assert!(result.is_err());
242+
assert_eq!(result.unwrap_err().to_string(), "malformed stream: output channel count is 0");
243+
}
244+
245+
#[test]
246+
fn verify_err_if_channel_family_gt_2() {
247+
let bytes: [u8; 23] = [
248+
0x4f, 0x70, 0x75, 0x73, 0x48, 0x65, 0x61, 0x64, 0x01, 0x02, 0x38, 0x01, 0x80, 0xbb,
249+
0x00, 0x00, 0x00, 0x00, 0x02, 0x4f, 0x67, 0x67, 0x53,
250+
];
251+
let mut reader = BufReader::new(&bytes);
252+
let result = read_ident_header(&mut reader);
253+
assert!(result.is_err());
254+
assert_eq!(result.unwrap_err().to_string(), "malformed stream: reserved mapping family");
255+
}
256+
257+
#[test]
258+
fn verify_err_if_channel_family_eq_0_and_channel_count_gt_2() {
259+
let bytes: [u8; 23] = [
260+
0x4f, 0x70, 0x75, 0x73, 0x48, 0x65, 0x61, 0x64, 0x01, 0x03, 0x38, 0x01, 0x80, 0xbb,
261+
0x00, 0x00, 0x00, 0x00, 0x00, 0x4f, 0x67, 0x67, 0x53,
262+
];
263+
264+
let mut reader = BufReader::new(&bytes);
265+
let result = read_ident_header(&mut reader);
266+
assert!(result.is_err());
267+
assert_eq!(
268+
result.unwrap_err().to_string(),
269+
"malformed stream: invalid output channel count"
270+
);
271+
}
272+
273+
#[test]
274+
fn verify_err_if_channel_family_eq_1_and_channel_count_gt_8() {
275+
let bytes: [u8; 23] = [
276+
0x4f, 0x70, 0x75, 0x73, 0x48, 0x65, 0x61, 0x64, 0x01, 0x09, 0x38, 0x01, 0x80, 0xbb,
277+
0x00, 0x00, 0x00, 0x00, 0x01, 0x4f, 0x67, 0x67, 0x53,
278+
];
279+
280+
let mut reader = BufReader::new(&bytes);
281+
let result = read_ident_header(&mut reader);
282+
assert!(result.is_err());
283+
assert_eq!(
284+
result.unwrap_err().to_string(),
285+
"malformed stream: invalid output channel count"
286+
);
287+
}
288+
289+
#[test]
290+
fn verify_err_if_channel_family_eq_1_and_stream_count_eq_0() {
291+
let bytes: [u8; 23] = [
292+
0x4f, 0x70, 0x75, 0x73, 0x48, 0x65, 0x61, 0x64, 0x01, 0x02, 0x38, 0x01, 0x80, 0xbb,
293+
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x67, 0x67, 0x53,
294+
];
295+
let mut reader = BufReader::new(&bytes);
296+
let result = read_ident_header(&mut reader);
297+
assert!(result.is_err());
298+
assert_eq!(result.unwrap_err().to_string(), "malformed stream: stream count is 0");
299+
}
300+
301+
#[test]
302+
fn verify_err_if_channel_family_eq_1_and_stream_counts_sum_gt_255() {
303+
let bytes: [u8; 23] = [
304+
0x4f, 0x70, 0x75, 0x73, 0x48, 0x65, 0x61, 0x64, 0x01, 0x02, 0x38, 0x01, 0x80, 0xbb,
305+
0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0xFF, 0x67, 0x53,
306+
];
307+
let mut reader = BufReader::new(&bytes);
308+
let result = read_ident_header(&mut reader);
309+
assert!(result.is_err());
310+
assert_eq!(
311+
result.unwrap_err().to_string(),
312+
"malformed stream: stream count + coupled stream count > 255"
313+
);
314+
}
315+
}

0 commit comments

Comments
 (0)