Skip to content

Commit b7b6053

Browse files
Merge pull request #2895 from yashranaway/fix/aac-high-input-rate
fix(enc-ffmpeg): preserve AAC quality for high sample rate inputs
2 parents a46118f + 77ef1a3 commit b7b6053

1 file changed

Lines changed: 69 additions & 1 deletion

File tree

  • crates/enc-ffmpeg/src/audio

‎crates/enc-ffmpeg/src/audio/aac.rs‎

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ impl AACEncoder {
162162
let Some(&rate) = rates
163163
.iter()
164164
.find(|r| **r >= input_config.rate())
165-
.or(rates.first())
165+
.or(rates.last())
166166
else {
167167
return Err(AACEncoderError::RateNotSupported(input_config.rate()));
168168
};
@@ -279,6 +279,74 @@ mod tests {
279279
use super::*;
280280
use ffmpeg::ChannelLayout;
281281

282+
#[test]
283+
fn sample_rate_selection_preserves_supported_rates_and_clamps_outliers() {
284+
let directory = tempfile::tempdir().unwrap();
285+
for (input_rate, expected_rate) in [
286+
(4_000, 7_350),
287+
(44_100, 44_100),
288+
(45_000, 48_000),
289+
(96_000, 96_000),
290+
(192_000, 96_000),
291+
] {
292+
let mut output =
293+
format::output(&directory.path().join(format!("{input_rate}.mp4"))).unwrap();
294+
let encoder = AACEncoder::init(
295+
AudioInfo::new_raw(Sample::F32(Type::Packed), input_rate, 1),
296+
&mut output,
297+
)
298+
.unwrap();
299+
let stream = output.stream(encoder.stream_index()).unwrap();
300+
let decoder = context::Context::from_parameters(stream.parameters())
301+
.unwrap()
302+
.decoder()
303+
.audio()
304+
.unwrap();
305+
assert_eq!(decoder.rate(), expected_rate, "input rate {input_rate}");
306+
}
307+
}
308+
309+
#[test]
310+
fn high_rate_input_keeps_high_frequency_audio_through_encode_decode() {
311+
let directory = tempfile::tempdir().unwrap();
312+
let path = directory.path().join("high-rate.mp4");
313+
let mut output = format::output(&path).unwrap();
314+
let mut encoder = AACEncoder::init(
315+
AudioInfo::new_raw(Sample::F32(Type::Packed), 192_000, 1),
316+
&mut output,
317+
)
318+
.unwrap();
319+
output.write_header().unwrap();
320+
321+
let mut audio = frame::Audio::new(Sample::F32(Type::Packed), 192_000, ChannelLayout::MONO);
322+
audio.set_rate(192_000);
323+
audio.set_pts(Some(0));
324+
for (index, sample) in audio.plane_mut::<f32>(0).iter_mut().enumerate() {
325+
*sample = 0.5 * (std::f32::consts::TAU * 12_000.0 * index as f32 / 192_000.0).sin();
326+
}
327+
encoder
328+
.send_frame(audio, Duration::MAX, &mut output)
329+
.unwrap();
330+
encoder.flush(&mut output).unwrap();
331+
output.write_trailer().unwrap();
332+
drop(encoder);
333+
drop(output);
334+
335+
let (samples, track_end) = decode_first_channel(&path);
336+
assert_eq!(track_end, 48_000);
337+
assert!(samples.len() >= 96_000, "decoded {} samples", samples.len());
338+
let mut sine = 0.0;
339+
let mut cosine = 0.0;
340+
let signal = &samples[9_600..86_400];
341+
for (index, sample) in signal.iter().enumerate() {
342+
let phase = std::f64::consts::TAU * 12_000.0 * index as f64 / 96_000.0;
343+
sine += f64::from(*sample) * phase.sin();
344+
cosine += f64::from(*sample) * phase.cos();
345+
}
346+
let amplitude = 2.0 * sine.hypot(cosine) / signal.len() as f64;
347+
assert!(amplitude > 0.4, "12 kHz tone amplitude {amplitude}");
348+
}
349+
282350
fn input_frame(start: i64, samples: usize) -> frame::Audio {
283351
let mut audio =
284352
frame::Audio::new(Sample::F32(Type::Packed), samples, ChannelLayout::STEREO);

0 commit comments

Comments
 (0)