Skip to content

Commit 2846716

Browse files
committed
caf: Support AAC in CAF.
Correctly extract the extra data from the ES Descriptor contained in the magic cookie. Refactor out object descriptor parsing into the common crate.
1 parent e8e986f commit 2846716

File tree

6 files changed

+427
-358
lines changed

6 files changed

+427
-358
lines changed

symphonia-common/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ edition = "2021"
1313
rust-version = "1.77"
1414

1515
[dependencies]
16+
log = "0.4"
1617
symphonia-core = { version = "0.5.4", path = "../symphonia-core" }
1718

1819
[dependencies.symphonia-metadata]
Lines changed: 368 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,368 @@
1+
// Symphonia
2+
// Copyright (c) 2019-2022 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/.
7+
8+
use symphonia_core::codecs::CodecId;
9+
use symphonia_core::errors::{decode_error, unsupported_error, Result};
10+
use symphonia_core::io::{FiniteStream, ReadBytes, ScopedStream};
11+
12+
use log::debug;
13+
14+
/// The minimum size of an object descriptor (minimum header size).
15+
pub const MIN_OBJECT_DESCRIPTOR_SIZE: u64 = 2;
16+
17+
/// Object descriptor tags as defined in ISO/IEC 14496-1.
18+
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
19+
pub enum ClassTag {
20+
ObjectDescriptor,
21+
InitialObjectDescriptor,
22+
EsDescriptor,
23+
DecoderConfigDescriptor,
24+
DecoderSpecificInfo,
25+
SlConfigDescriptor,
26+
UserPrivate(u8),
27+
Unknown(u8),
28+
}
29+
30+
/// Read an ISO/IEC 14496-1 Object Descriptor header and return the class tag and size.
31+
pub fn read_object_descriptor_header<B: ReadBytes>(reader: &mut B) -> Result<(ClassTag, u64)> {
32+
let tag = match reader.read_u8()? {
33+
0x0 | 0xff => return decode_error("common (mpeg): forbidden object descriptor tag"),
34+
0x1 => ClassTag::ObjectDescriptor,
35+
0x2 => ClassTag::InitialObjectDescriptor,
36+
0x3 => ClassTag::EsDescriptor,
37+
0x4 => ClassTag::DecoderConfigDescriptor,
38+
0x5 => ClassTag::DecoderSpecificInfo,
39+
0x6 => ClassTag::SlConfigDescriptor,
40+
user @ 0xc0..=0xfe => ClassTag::UserPrivate(user),
41+
other => ClassTag::Unknown(other),
42+
};
43+
44+
let mut size = 0;
45+
46+
for _ in 0..4 {
47+
let val = reader.read_u8()?;
48+
size = (size << 7) | u64::from(val & 0x7f);
49+
if val & 0x80 == 0 {
50+
break;
51+
}
52+
}
53+
54+
Ok((tag, size))
55+
}
56+
57+
/// Try to get a codec ID from from an object type indication.
58+
pub fn codec_id_from_object_type_indication(obj_type: u8) -> Option<CodecId> {
59+
use symphonia_core::codecs::audio::well_known::{
60+
CODEC_ID_AAC, CODEC_ID_AC3, CODEC_ID_DCA, CODEC_ID_EAC3, CODEC_ID_MP3,
61+
};
62+
use symphonia_core::codecs::video::well_known::{
63+
CODEC_ID_H264, CODEC_ID_HEVC, CODEC_ID_MPEG2, CODEC_ID_MPEG4, CODEC_ID_VP9,
64+
};
65+
66+
// AAC
67+
const OBJ_TYPE_AUDIO_MPEG4_3: u8 = 0x40; // Audio ISO/IEC 14496-3
68+
const OBJ_TYPE_AUDIO_MPEG2_7_MAIN: u8 = 0x66; // Audio ISO/IEC 13818-7 Main Profile
69+
const OBJ_TYPE_AUDIO_MPEG2_7_LC: u8 = 0x67; // Audio ISO/IEC 13818-7 Low Complexity
70+
71+
// MP3
72+
const OBJ_TYPE_AUDIO_MPEG2_3: u8 = 0x69; // Audio ISO/IEC 13818-3 (MP3)
73+
const OBJ_TYPE_AUDIO_MPEG1_3: u8 = 0x6b; // Audio ISO/IEC 11172-3 (MP3)
74+
75+
const OBJ_TYPE_AUDIO_AC3: u8 = 0xa5;
76+
const OBJ_TYPE_AUDIO_EAC3: u8 = 0xa6;
77+
const OBJ_TYPE_AUDIO_DTS: u8 = 0xa9;
78+
79+
// MPEG2 video
80+
const OBJ_TYPE_VISUAL_MPEG2_2_SP: u8 = 0x60; // Visual ISO/IEC 13818-2 Simple Profile
81+
const OBJ_TYPE_VISUAL_MPEG2_2_MP: u8 = 0x61; // Visual ISO/IEC 13818-2 Main Profile
82+
const OBJ_TYPE_VISUAL_MPEG2_2_SNR: u8 = 0x62; // Visual ISO/IEC 13818-2 SNR Profile
83+
const OBJ_TYPE_VISUAL_MPEG2_2_SPATIAL: u8 = 0x63; // Visual ISO/IEC 13818-2 Spatial Profile
84+
const OBJ_TYPE_VISUAL_MPEG2_2_HP: u8 = 0x64; // Visual ISO/IEC 13818-2 High Profile
85+
const OBJ_TYPE_VISUAL_MPEG2_2_422: u8 = 0x65; // Visual ISO/IEC 13818-2 422 Profile
86+
87+
// MPEG4 video
88+
const OBJ_TYPE_VISUAL_MPEG4_2: u8 = 0x20; // Visual ISO/IEC 14496-2
89+
90+
// H264
91+
const OBJ_TYPE_VISUAL_AVC1: u8 = 0x21; // ISO/IEC 14496-10
92+
93+
// HEVC
94+
const OBJ_TYPE_VISUAL_HEVC1: u8 = 0x23; // Visual ISO/IEC 23008-2
95+
96+
// VP9
97+
const OBJ_TYPE_VISUAL_VP09: u8 = 0xb1;
98+
99+
let codec_id = match obj_type {
100+
OBJ_TYPE_AUDIO_MPEG4_3 | OBJ_TYPE_AUDIO_MPEG2_7_LC | OBJ_TYPE_AUDIO_MPEG2_7_MAIN => {
101+
CodecId::Audio(CODEC_ID_AAC)
102+
}
103+
OBJ_TYPE_AUDIO_MPEG2_3 | OBJ_TYPE_AUDIO_MPEG1_3 => CodecId::Audio(CODEC_ID_MP3),
104+
OBJ_TYPE_AUDIO_AC3 => CodecId::Audio(CODEC_ID_AC3),
105+
OBJ_TYPE_AUDIO_EAC3 => CodecId::Audio(CODEC_ID_EAC3),
106+
OBJ_TYPE_AUDIO_DTS => CodecId::Audio(CODEC_ID_DCA),
107+
OBJ_TYPE_VISUAL_MPEG2_2_SP
108+
| OBJ_TYPE_VISUAL_MPEG2_2_MP
109+
| OBJ_TYPE_VISUAL_MPEG2_2_SNR
110+
| OBJ_TYPE_VISUAL_MPEG2_2_SPATIAL
111+
| OBJ_TYPE_VISUAL_MPEG2_2_HP
112+
| OBJ_TYPE_VISUAL_MPEG2_2_422 => CodecId::Video(CODEC_ID_MPEG2),
113+
OBJ_TYPE_VISUAL_MPEG4_2 => CodecId::Video(CODEC_ID_MPEG4),
114+
OBJ_TYPE_VISUAL_AVC1 => CodecId::Video(CODEC_ID_H264),
115+
OBJ_TYPE_VISUAL_HEVC1 => CodecId::Video(CODEC_ID_HEVC),
116+
OBJ_TYPE_VISUAL_VP09 => CodecId::Video(CODEC_ID_VP9),
117+
_ => {
118+
debug!("unknown object type indication {obj_type:#x} for decoder config descriptor");
119+
return None;
120+
}
121+
};
122+
123+
Some(codec_id)
124+
}
125+
126+
pub trait ObjectDescriptor: Sized {
127+
fn read<B: ReadBytes>(reader: &mut B, len: u64) -> Result<Self>;
128+
}
129+
130+
/*
131+
class ES_Descriptor extends BaseDescriptor : bit(8) tag=ES_DescrTag {
132+
bit(16) ES_ID;
133+
bit(1) streamDependenceFlag;
134+
bit(1) URL_Flag;
135+
bit(1) OCRstreamFlag;
136+
bit(5) streamPriority;
137+
if (streamDependenceFlag)
138+
bit(16) dependsOn_ES_ID;
139+
if (URL_Flag) {
140+
bit(8) URLlength;
141+
bit(8) URLstring[URLlength];
142+
}
143+
if (OCRstreamFlag)
144+
bit(16) OCR_ES_Id;
145+
DecoderConfigDescriptor decConfigDescr;
146+
SLConfigDescriptor slConfigDescr;
147+
IPI_DescrPointer ipiPtr[0 .. 1];
148+
IP_IdentificationDataSet ipIDS[0 .. 255];
149+
IPMP_DescriptorPointer ipmpDescrPtr[0 .. 255];
150+
LanguageDescriptor langDescr[0 .. 255];
151+
QoS_Descriptor qosDescr[0 .. 1];
152+
RegistrationDescriptor regDescr[0 .. 1];
153+
ExtensionDescriptor extDescr[0 .. 255];
154+
}
155+
*/
156+
157+
#[derive(Debug)]
158+
pub struct ESDescriptor {
159+
pub es_id: u16,
160+
pub dec_config: DecoderConfigDescriptor,
161+
pub sl_config: SLConfigDescriptor,
162+
}
163+
164+
impl ObjectDescriptor for ESDescriptor {
165+
fn read<B: ReadBytes>(reader: &mut B, len: u64) -> Result<Self> {
166+
// ES Descriptor is an expandable object descriptor. All reads must be scoped to the length
167+
// defined in the header.
168+
let mut scoped = ScopedStream::new(reader, len);
169+
170+
let es_id = scoped.read_be_u16()?;
171+
let es_flags = scoped.read_u8()?;
172+
173+
// Stream dependence flag.
174+
if es_flags & 0x80 != 0 {
175+
let _depends_on_es_id = scoped.read_u16()?;
176+
}
177+
178+
// URL flag.
179+
if es_flags & 0x40 != 0 {
180+
let url_len = scoped.read_u8()?;
181+
scoped.ignore_bytes(u64::from(url_len))?;
182+
}
183+
184+
// OCR stream flag.
185+
if es_flags & 0x20 != 0 {
186+
let _ocr_es_id = scoped.read_u16()?;
187+
}
188+
189+
let mut dec_config = None;
190+
let mut sl_config = None;
191+
192+
// Multiple descriptors follow, but only the decoder configuration descriptor is useful.
193+
while scoped.bytes_available() > MIN_OBJECT_DESCRIPTOR_SIZE {
194+
let (tag, desc_len) = read_object_descriptor_header(&mut scoped)?;
195+
196+
match tag {
197+
ClassTag::DecoderConfigDescriptor => {
198+
dec_config = Some(DecoderConfigDescriptor::read(&mut scoped, desc_len)?);
199+
}
200+
ClassTag::SlConfigDescriptor => {
201+
sl_config = Some(SLConfigDescriptor::read(&mut scoped, desc_len)?);
202+
}
203+
other => {
204+
debug!("skipping {other:?} object in es descriptor");
205+
scoped.ignore_bytes(desc_len)?;
206+
}
207+
}
208+
}
209+
210+
// Consume remaining bytes.
211+
scoped.ignore()?;
212+
213+
// Decoder configuration descriptor is mandatory.
214+
if dec_config.is_none() {
215+
return decode_error("common (mpeg): missing decoder config descriptor");
216+
}
217+
218+
// SL descriptor is mandatory.
219+
if sl_config.is_none() {
220+
return decode_error("common (mpeg): missing sl config descriptor");
221+
}
222+
223+
Ok(ESDescriptor { es_id, dec_config: dec_config.unwrap(), sl_config: sl_config.unwrap() })
224+
}
225+
}
226+
227+
/*
228+
class DecoderConfigDescriptor extends BaseDescriptor : bit(8) tag=DecoderConfigDescrTag {
229+
bit(8) objectTypeIndication;
230+
bit(6) streamType;
231+
bit(1) upStream;
232+
const bit(1) reserved=1;
233+
bit(24) bufferSizeDB;
234+
bit(32) maxBitrate;
235+
bit(32) avgBitrate;
236+
DecoderSpecificInfo decSpecificInfo[0 .. 1];
237+
profileLevelIndicationIndexDescriptor profileLevelIndicationIndexDescr [0..255];
238+
}
239+
*/
240+
241+
#[derive(Debug)]
242+
pub struct DecoderConfigDescriptor {
243+
pub object_type_indication: u8,
244+
pub dec_specific_info: Option<DecoderSpecificInfo>,
245+
}
246+
247+
impl ObjectDescriptor for DecoderConfigDescriptor {
248+
fn read<B: ReadBytes>(reader: &mut B, len: u64) -> Result<Self> {
249+
// Decoder Config Descriptor is an expandable object descriptor. All reads must be scoped to
250+
// the length defined in the header.
251+
let mut scoped = ScopedStream::new(reader, len);
252+
253+
let object_type_indication = scoped.read_u8()?;
254+
255+
let (_stream_type, _upstream) = {
256+
let val = scoped.read_u8()?;
257+
258+
if val & 0x1 != 1 {
259+
debug!("decoder config descriptor reserved bit is not 1");
260+
}
261+
262+
((val & 0xfc) >> 2, (val & 0x2) >> 1)
263+
};
264+
265+
let _buffer_size = scoped.read_be_u24()?;
266+
let _max_bitrate = scoped.read_be_u32()?;
267+
let _avg_bitrate = scoped.read_be_u32()?;
268+
269+
let mut dec_specific_config = None;
270+
271+
// Multiple descriptors follow, but only the decoder specific info descriptor is useful.
272+
while scoped.bytes_available() > MIN_OBJECT_DESCRIPTOR_SIZE {
273+
let (tag, desc_len) = read_object_descriptor_header(&mut scoped)?;
274+
275+
match tag {
276+
ClassTag::DecoderSpecificInfo => {
277+
dec_specific_config = Some(DecoderSpecificInfo::read(&mut scoped, desc_len)?);
278+
}
279+
other => {
280+
debug!("skipping {other:?} object in decoder config descriptor");
281+
scoped.ignore_bytes(desc_len)?;
282+
}
283+
}
284+
}
285+
286+
// Consume remaining bytes.
287+
scoped.ignore()?;
288+
289+
Ok(DecoderConfigDescriptor {
290+
object_type_indication,
291+
dec_specific_info: dec_specific_config,
292+
})
293+
}
294+
}
295+
296+
#[derive(Debug)]
297+
pub struct DecoderSpecificInfo {
298+
pub extra_data: Box<[u8]>,
299+
}
300+
301+
impl ObjectDescriptor for DecoderSpecificInfo {
302+
fn read<B: ReadBytes>(reader: &mut B, len: u64) -> Result<Self> {
303+
Ok(DecoderSpecificInfo { extra_data: reader.read_boxed_slice_exact(len as usize)? })
304+
}
305+
}
306+
307+
/*
308+
class SLConfigDescriptor extends BaseDescriptor : bit(8) tag=SLConfigDescrTag {
309+
bit(8) predefined;
310+
if (predefined==0) {
311+
bit(1) useAccessUnitStartFlag;
312+
bit(1) useAccessUnitEndFlag;
313+
bit(1) useRandomAccessPointFlag;
314+
bit(1) hasRandomAccessUnitsOnlyFlag;
315+
bit(1) usePaddingFlag;
316+
bit(1) useTimeStampsFlag;
317+
bit(1) useIdleFlag;
318+
bit(1) durationFlag;
319+
bit(32) timeStampResolution;
320+
bit(32) OCRResolution;
321+
bit(8) timeStampLength; // Must be <= 64.
322+
bit(8) OCRLength; // Must be <= 64.
323+
bit(8) AU_Length; // Must be <= 32.
324+
bit(8) instantBitrateLength;
325+
bit(4) degradationPriorityLength;
326+
bit(5) AU_seqNumLength; // Must be <= 16.
327+
bit(5) packetSeqNumLength; // Must be <= 16.
328+
bit(2) reserved=0b11;
329+
}
330+
if (durationFlag) {
331+
bit(32) timeScale;
332+
bit(16) accessUnitDuration;
333+
bit(16) compositionUnitDuration;
334+
}
335+
if (!useTimeStampsFlag) {
336+
bit(timeStampLength) startDecodingTimeStamp;
337+
bit(timeStampLength) startCompositionTimeStamp;
338+
}
339+
}
340+
*/
341+
342+
#[derive(Debug)]
343+
pub struct SLConfigDescriptor;
344+
345+
impl ObjectDescriptor for SLConfigDescriptor {
346+
fn read<B: ReadBytes>(reader: &mut B, len: u64) -> Result<Self> {
347+
const PREDEFINED_CUSTOM: u8 = 0x0;
348+
const PREDEFINED_NULL: u8 = 0x1;
349+
const PREDEFINED_MP4: u8 = 0x2;
350+
351+
// Ensure no reads extend beyond the SL Config Descriptor length as defined in the object
352+
// descriptor header.
353+
let mut scoped = ScopedStream::new(reader, len);
354+
355+
// Ensure the predefined field is valid.
356+
match scoped.read_u8()? {
357+
PREDEFINED_CUSTOM | PREDEFINED_NULL | PREDEFINED_MP4 => (),
358+
_ => {
359+
return unsupported_error("common (mpeg): invalid sl config descriptor predefined")
360+
}
361+
};
362+
363+
// Consume remaining bytes.
364+
scoped.ignore()?;
365+
366+
Ok(SLConfigDescriptor {})
367+
}
368+
}

symphonia-common/src/mpeg/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,5 @@
1212
#![allow(clippy::identity_op)]
1313
#![allow(clippy::manual_range_contains)]
1414

15+
pub mod formats;
1516
pub mod video;

symphonia-format-caf/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,5 @@ rust-version = "1.77"
1414

1515
[dependencies]
1616
log = "0.4"
17+
symphonia-common = { version = "0.5.4", path = "../symphonia-common" }
1718
symphonia-core = { version = "0.5.4", path = "../symphonia-core" }

0 commit comments

Comments
 (0)