Skip to content

Re-encode plain WAV sources with an unspecified channel layout - #92

Merged
rubas-agent[bot] merged 3 commits into
mainfrom
fix-unspecified-wav-layout
Oct 16, 2026
Merged

rubas-agent[bot] merged 3 commits into
mainfrom
fix-unspecified-wav-layout

Conversation

@rubas-agent

@rubas-agent rubas-agent Bot commented Oct 16, 2026

Copy link
Copy Markdown
Contributor

Before

A plain mono WAV from ffmpeg -f lavfi -i sine=d=1:sample_rate=44100 a.wav has channel_layout=unknown in ffprobe. On main, every call that resamples or re-encodes it fails:

Exmpeg.extract_audio("a.wav", "x.mp3")      # also .m4a, .opus, .flac with sample_rate: 16_000
Exmpeg.transcode("a.wav", "y.m4a", audio_codec: "aac")
{:error, %Exmpeg.Error{reason: :io_error, message: "AVERROR(-1668179713): `Input changed`"}}

The new tests, run without the Rust fix:

1) test transcode re-encodes a mono 44.1 kHz WAV to aac in m4a
   message: "AVERROR(-1668179713): `Input changed`"
2) test extract_audio re-encodes a mono 44.1 kHz WAV to mp3 (same error)
3) test extract_audio resamples a mono 44.1 kHz WAV to 48 kHz opus (same error)
Result: 61/64 passed

Problem

ffmpeg writes mono and stereo WAV files without a channel mask. The decoder keeps the unspecified channel order. swr_init sets its input to the default layout for the channel count. Then swr_convert_frame sees the unspecified layout of each decoded frame as a changed input and rejects it. An AAC source works, because its layout is set.

After

All calls above return {:ok, _}. task test:integration: 107 passed (104 existing tests and 3 new ones). The WAV-to-WAV passthrough test still passes. task check exits 0.

main this PR
Integration tests 104 107
WAV re-encode calls that fail all none
Details

Cause. native/exmpeg_native/src/extract_audio.rs:extract_audio and native/exmpeg_native/src/transcode.rs:build_audio_pipeline open the decoder with AV_CHANNEL_ORDER_UNSPEC from codecpar. The resampler normalises its input layout, so the frames no longer match it.

Design. The new helper audio::default_unspecified_layout gives an unspecified decoder layout the default layout for its channel count (AVChannelLayout::from_nb_channels), before decoder.open. The ffmpeg CLI does the same in guess_input_channel_layout (fftools/ffmpeg_demux.c). Both pipelines call the one helper. After it, the decoder layout is never unspecified, so I removed the unspecified branch of the passthrough check in extract_audio. A WAV with the default layout still matches the target and skips the resampler. No unsafe.

Tests. Three integration tests use the existing mono 44.1 kHz WAV fixture:

  • extract_audio to .mp3 keeps 44 100 Hz and 1 channel.
  • extract_audio to .opus resamples to 48 000 Hz.
  • transcode to .m4a with audio_codec: "aac" re-encodes 1 stream.

All three fail on main with the error above and pass with the fix.

rubas added 3 commits October 16, 2026 15:59
A plain mono or stereo WAV leaves its channel order unspecified.
swr_init normalises the resampler input to the default layout, so
swr_convert_frame rejected every decoded frame with
AVERROR_INPUT_CHANGED. Set the default layout before the decoder
opens, as the ffmpeg CLI does, in extract_audio and transcode. The
passthrough check no longer needs its unspecified-order branch.
@rubas-agent
rubas-agent Bot merged commit d62702e into main Oct 16, 2026
1 check passed
@rubas-agent
rubas-agent Bot deleted the fix-unspecified-wav-layout branch October 16, 2026 17:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant