Skip to content

Commit f86ec03

Browse files
authored
Merge pull request #314 from uklotzde/decoding
Fix and extend (AAC/M4A/ALAC) decoding
2 parents 23057ef + e95cdb8 commit f86ec03

File tree

5 files changed

+99
-56
lines changed

5 files changed

+99
-56
lines changed

Cargo.toml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,17 @@ name = "my_benchmark"
5050
harness = false
5151

5252
[features]
53-
default = ["mp3", "ogg", "flac", "wav", "cpal"]
53+
default = ["mp3", "ogg", "flac", "wav", "m4a", "alac", "cpal"]
5454
mp3 = ["symphonia/mp3", "creek/decode-mp3"]
5555
ogg = ["symphonia/ogg", "symphonia/vorbis", "creek/decode-ogg", "creek/decode-vorbis"]
5656
flac = ["symphonia/flac", "creek/decode-flac"]
5757
wav = ["symphonia/wav", "symphonia/pcm", "creek/decode-wav", "creek/decode-pcm"]
58+
# TODO(m4a): Add "creek/decode-aac" after <https://github.com/MeadowlarkDAW/creek/pull/22> has been merged
59+
aac = ["symphonia/aac"]
60+
# TODO(m4a): Add "creek/decode-isomp4" after <https://github.com/MeadowlarkDAW/creek/pull/22> has been merged
61+
m4a = ["aac", "symphonia/isomp4"]
62+
# TODO(alac): Add "creek/decode-alac" and "creek/decode-isomp4" after <https://github.com/MeadowlarkDAW/creek/pull/22> has been merged
63+
alac = ["symphonia/alac", "symphonia/isomp4"]
5864
cpal = ["dep:cpal"]
5965
cubeb = ["dep:cubeb"]
6066
cpal-jack = ["cpal", "cpal/jack"]

examples/decoding.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,23 @@ fn main() {
2222
"samples/sample.flac",
2323
"samples/sample.ogg",
2424
"samples/sample.mp3",
25+
"samples/sample-aac.m4a",
26+
"samples/sample-alac.m4a",
2527
// cannot decode, format not supported or file corrupted
2628
"samples/empty_2c.wav",
2729
"samples/corrupt.wav",
2830
"samples/sample.aiff",
2931
"samples/sample.webm", // 48kHz,
3032
];
3133

32-
let latency_hint = match std::env::var("WEB_AUDIO_LATENCY").as_deref() {
34+
let latency_hint = match std::env::var("WEB_AUDIO_LATENCY")
35+
.as_deref()
36+
.map(str::trim)
37+
.map(str::to_ascii_lowercase)
38+
.as_deref()
39+
{
40+
Ok("interactive") => AudioContextLatencyCategory::Interactive,
41+
Ok("balanced") => AudioContextLatencyCategory::Balanced,
3342
Ok("playback") => AudioContextLatencyCategory::Playback,
3443
_ => AudioContextLatencyCategory::default(),
3544
};

samples/sample-aac.m4a

51.7 KB
Binary file not shown.

samples/sample-alac.m4a

215 KB
Binary file not shown.

src/decoding.rs

Lines changed: 82 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use crate::buffer::{AudioBuffer, ChannelData};
55

66
use symphonia::core::audio::AudioBufferRef;
77
use symphonia::core::audio::Signal;
8-
use symphonia::core::codecs::{Decoder, DecoderOptions};
8+
use symphonia::core::codecs::{Decoder, DecoderOptions, FinalizeResult};
99
use symphonia::core::conv::FromSample;
1010
use symphonia::core::errors::Error as SymphoniaError;
1111
use symphonia::core::formats::{FormatOptions, FormatReader};
@@ -53,6 +53,8 @@ impl<R: Read + Send + Sync> symphonia::core::io::MediaSource for MediaInput<R> {
5353
pub(crate) struct MediaDecoder {
5454
format: Box<dyn FormatReader>,
5555
decoder: Box<dyn Decoder>,
56+
track_index: usize,
57+
packet_count: usize,
5658
}
5759

5860
impl MediaDecoder {
@@ -64,24 +66,27 @@ impl MediaDecoder {
6466
pub fn try_new<R: std::io::Read + Send + Sync + 'static>(
6567
input: R,
6668
) -> Result<Self, Box<dyn std::error::Error + Send + Sync>> {
67-
// Symfonia lib needs a Box<dyn MediaSource> - use our own MediaInput
69+
// Symphonia lib needs a Box<dyn MediaSource> - use our own MediaInput
6870
let input = Box::new(MediaInput::new(input));
6971

7072
// Create the media source stream using the boxed media source from above.
71-
let mss = symphonia::core::io::MediaSourceStream::new(input, Default::default());
73+
let stream = symphonia::core::io::MediaSourceStream::new(input, Default::default());
7274

7375
// Create a hint to help the format registry guess what format reader is appropriate. In this
7476
// function we'll leave it empty.
7577
let hint = Hint::new();
7678

77-
// Use the default options when reading and decoding.
79+
// TODO: Allow to customize some options.
7880
let format_opts: FormatOptions = Default::default();
7981
let metadata_opts: MetadataOptions = Default::default();
80-
let decoder_opts: DecoderOptions = Default::default();
82+
let decoder_opts = DecoderOptions {
83+
// Opt-in to verify the decoded data against the checksums in the container.
84+
verify: true,
85+
};
8186

8287
// Probe the media source stream for a format.
8388
let probed =
84-
symphonia::default::get_probe().format(&hint, mss, &format_opts, &metadata_opts)?;
89+
symphonia::default::get_probe().format(&hint, stream, &format_opts, &metadata_opts)?;
8590

8691
// Get the format reader yielded by the probe operation.
8792
let format = probed.format;
@@ -90,117 +95,140 @@ impl MediaDecoder {
9095
let track = format.default_track().ok_or(SymphoniaError::Unsupported(
9196
"no default media track available",
9297
))?;
93-
98+
let track_index = format
99+
.tracks()
100+
.iter()
101+
.position(|t| t.id == track.id)
102+
.unwrap();
94103
// Create a (stateful) decoder for the track.
95104
let decoder = symphonia::default::get_codecs().make(&track.codec_params, &decoder_opts)?;
96105

97-
Ok(Self { format, decoder })
106+
Ok(Self {
107+
format,
108+
decoder,
109+
track_index,
110+
packet_count: 0,
111+
})
98112
}
99113
}
100114

101115
impl Iterator for MediaDecoder {
102116
type Item = Result<AudioBuffer, Box<dyn Error + Send + Sync>>;
103117

104118
fn next(&mut self) -> Option<Self::Item> {
105-
let format = &mut self.format;
106-
let decoder = &mut self.decoder;
107-
108-
// Get the default track.
109-
let track = format.default_track().unwrap();
110-
let number_of_channels = track.codec_params.channels.unwrap().count();
111-
let input_sample_rate = track.codec_params.sample_rate.unwrap() as f32;
112-
113-
// Store the track identifier, we'll use it to filter packets.
119+
let Self {
120+
format,
121+
decoder,
122+
track_index,
123+
packet_count,
124+
} = self;
125+
126+
// Get the track.
127+
let track = format.tracks().get(*track_index)?;
114128
let track_id = track.id;
115129

116130
loop {
117131
// Get the next packet from the format reader.
118132
let packet = match format.next_packet() {
119-
Err(e) => {
120-
log::error!("next packet err {:?}", e);
121-
return None;
133+
Err(err) => {
134+
if let SymphoniaError::IoError(err) = &err {
135+
if err.kind() == std::io::ErrorKind::UnexpectedEof {
136+
// End of stream
137+
log::debug!("Decoding finished after {packet_count} packet(s)");
138+
let FinalizeResult { verify_ok } = decoder.finalize();
139+
if verify_ok == Some(false) {
140+
log::warn!("Verification of decoded data failed");
141+
}
142+
return None;
143+
}
144+
}
145+
log::warn!(
146+
"Failed to fetch next packet following packet #{packet_count}: {err}"
147+
);
148+
return Some(Err(Box::new(err)));
149+
}
150+
Ok(packet) => {
151+
*packet_count += 1;
152+
packet
122153
}
123-
Ok(p) => p,
124154
};
125155

126156
// If the packet does not belong to the selected track, skip it.
127-
if packet.track_id() != track_id {
157+
let packet_track_id = packet.track_id();
158+
if packet_track_id != track_id {
159+
log::debug!(
160+
"Skipping packet from other track {packet_track_id} while decoding track {track_id}"
161+
);
128162
continue;
129163
}
130164

131-
// Decode the packet into audio samples, ignoring any decode errors.
165+
// Decode the packet into audio samples.
132166
match decoder.decode(&packet) {
133-
Ok(audio_buf) => {
134-
let output = convert_buf(audio_buf, number_of_channels, input_sample_rate);
167+
Ok(input) => {
168+
let output = convert_buf(input);
135169
return Some(Ok(output));
136170
}
137-
Err(SymphoniaError::DecodeError(e)) => {
138-
// Todo: treat decoding errors as fatal or move to next packet? Context:
139-
// https://github.com/RustAudio/rodio/issues/401#issuecomment-974747404
140-
log::error!("Symphonia DecodeError {:?} - abort stream", e);
141-
return Some(Err(Box::new(SymphoniaError::DecodeError(e))));
171+
Err(SymphoniaError::DecodeError(err)) => {
172+
// Recoverable error, continue with the next packet.
173+
log::warn!("Failed to decode packet #{packet_count}: {err}");
142174
}
143-
Err(SymphoniaError::IoError(e))
144-
if e.kind() == std::io::ErrorKind::UnexpectedEof =>
145-
{
146-
// this happens for Wav-files, running into EOF is expected
175+
Err(SymphoniaError::IoError(err)) => {
176+
// Recoverable error, continue with the next packet.
177+
log::warn!("I/O error while decoding packet #{packet_count}: {err}");
147178
}
148-
Err(e) => {
149-
// do not continue processing, return error result
150-
return Some(Err(Box::new(e)));
179+
Err(err) => {
180+
// All other errors are considered fatal and decoding must be aborted.
181+
return Some(Err(Box::new(err)));
151182
}
152183
};
153184
}
154185
}
155186
}
156187

157188
/// Convert a Symphonia AudioBufferRef to our own AudioBuffer
158-
fn convert_buf(
159-
input: AudioBufferRef<'_>,
160-
number_of_channels: usize,
161-
input_sample_rate: f32,
162-
) -> AudioBuffer {
163-
let chans = 0..number_of_channels;
189+
fn convert_buf(input: AudioBufferRef<'_>) -> AudioBuffer {
190+
let channels = 0..input.spec().channels.count();
191+
let sample_rate = input.spec().rate as f32;
164192

165193
// This looks a bit awkward but this may be the only way to get the f32 samples
166194
// out without making double copies.
167195
use symphonia::core::audio::AudioBufferRef::*;
168196

169197
let data: Vec<Vec<f32>> = match input {
170-
U8(buf) => chans
198+
U8(buf) => channels
171199
.map(|i| buf.chan(i).iter().copied().map(f32::from_sample).collect())
172200
.collect(),
173-
U16(buf) => chans
201+
U16(buf) => channels
174202
.map(|i| buf.chan(i).iter().copied().map(f32::from_sample).collect())
175203
.collect(),
176-
U24(buf) => chans
204+
U24(buf) => channels
177205
.map(|i| buf.chan(i).iter().copied().map(f32::from_sample).collect())
178206
.collect(),
179-
U32(buf) => chans
207+
U32(buf) => channels
180208
.map(|i| buf.chan(i).iter().copied().map(f32::from_sample).collect())
181209
.collect(),
182-
S8(buf) => chans
210+
S8(buf) => channels
183211
.map(|i| buf.chan(i).iter().copied().map(f32::from_sample).collect())
184212
.collect(),
185-
S16(buf) => chans
213+
S16(buf) => channels
186214
.map(|i| buf.chan(i).iter().copied().map(f32::from_sample).collect())
187215
.collect(),
188-
S24(buf) => chans
216+
S24(buf) => channels
189217
.map(|i| buf.chan(i).iter().copied().map(f32::from_sample).collect())
190218
.collect(),
191-
S32(buf) => chans
219+
S32(buf) => channels
192220
.map(|i| buf.chan(i).iter().copied().map(f32::from_sample).collect())
193221
.collect(),
194-
F32(buf) => chans
222+
F32(buf) => channels
195223
.map(|i| buf.chan(i).iter().copied().map(f32::from_sample).collect())
196224
.collect(),
197-
F64(buf) => chans
225+
F64(buf) => channels
198226
.map(|i| buf.chan(i).iter().copied().map(f32::from_sample).collect())
199227
.collect(),
200228
};
201229

202230
let channels = data.into_iter().map(ChannelData::from).collect();
203-
AudioBuffer::from_channels(channels, input_sample_rate)
231+
AudioBuffer::from_channels(channels, sample_rate)
204232
}
205233

206234
#[cfg(test)]

0 commit comments

Comments
 (0)