Skip to content

Commit e990244

Browse files
committed
fix: audio decode against FFmpeg 6.x (channel count for layout stamping)
Frames decoded from PCM WAV carry an unspecified channel layout (RIFF has no channel mask), so the layout is stamped with the default one for the frame's channel count before the resampler is built. The count was read back from the unspecified layout itself, which works on FFmpeg >= 7.0 (AVChannelLayout keeps nb_channels) but not on 6.x, where ffmpeg-next maps ChannelLayout onto the legacy AVFrame.channel_layout bitmask: "unspecified" is 0 there and reports 0 channels, the default layout for 0 channels is empty again, and swr_init rejects it with EINVAL. Opus/Vorbis were unaffected because their decoders report a native layout. Take the channel count from the frame (falling back to the decoder), which is populated on every FFmpeg version. Verified on Ubuntu 24.04 with FFmpeg 6.1 system libs (the GitHub CI image) and on FFmpeg 8.0.
1 parent 969e672 commit e990244

1 file changed

Lines changed: 17 additions & 2 deletions

File tree

‎src/audio/mod.rs‎

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,9 +90,24 @@ fn decode_via_ffmpeg(data: &[u8]) -> Result<Vec<f32>, AudioError> {
9090
|decoder: &mut ff::decoder::Audio, pcm: &mut Vec<f32>| -> Result<(), AudioError> {
9191
let mut frame = ff::frame::Audio::empty();
9292
while decoder.receive_frame(&mut frame).is_ok() {
93+
// swresample needs a concrete channel layout, and PCM in RIFF/WAV
94+
// carries no channel mask, so libavcodec hands out frames with an
95+
// unspecified layout that has to be stamped with the default one
96+
// for the channel count. That count must come from the frame (or
97+
// the decoder), never from the unspecified layout itself: on
98+
// FFmpeg < 7.0 ffmpeg-next maps ChannelLayout onto the legacy
99+
// AVFrame.channel_layout bitmask, where "unspecified" is 0 and so
100+
// reports 0 channels; the default layout for 0 is empty again and
101+
// swr_init then fails with EINVAL. FFmpeg >= 7.0 keeps the channel
102+
// count in the layout even when unspecified, which is why this only
103+
// broke against 6.x system libs.
93104
if frame.channel_layout().is_empty() {
94-
let n = frame.channel_layout().channels();
95-
frame.set_channel_layout(ff::channel_layout::ChannelLayout::default(n));
105+
let n = match frame.channels() {
106+
0 => decoder.channels(),
107+
n => n,
108+
};
109+
let layout = ff::channel_layout::ChannelLayout::default(i32::from(n));
110+
frame.set_channel_layout(layout);
96111
}
97112
let resampler = match resampler.as_mut() {
98113
Some(r) => r,

0 commit comments

Comments
 (0)