Skip to content

Merge discrete mono audio tracks into the rendition instead of dropping them - #46

Open
bjay-wk wants to merge 1 commit into
mainfrom
claude/merge-mono-audio-tracks
Open

Merge discrete mono audio tracks into the rendition instead of dropping them#46
bjay-wk wants to merge 1 commit into
mainfrom
claude/merge-mono-audio-tracks

Conversation

@bjay-wk

@bjay-wk bjay-wk commented Jul 28, 2026

Copy link
Copy Markdown
Collaborator

What

create_rendition built its ffmpeg command with .codec_audio("aac") and no -map arguments, relying on ffmpeg's default stream selection — which writes exactly one audio stream to the output.

Avid/broadcast MXF masters conventionally carry the stereo program as two discrete mono tracks (Track 3 / Track 4), so half the program audio was silently lost in every rendition made from one.

This mirrors the backend fix in video_processing_worker's downscale_video (tellers-backend #1454).

Repro

Ran the real create_rendition on a 1080p25 DNxHD MXF with two pcm_s24le mono streams (440 Hz / 880 Hz):

audio stream in rendition
before aac, 1ch, mono — half the program lost
after aac, 2ch, stereo

Channel-splitting the fixed output and running a DFT confirms L = 440 Hz (track 3), R = 880 Hz (track 4) — both tracks present, in the right positions.

How

New build_mono_merge_filter(probe) returns a -filter_complex graph only when there is more than one audio stream and every one is mono:

-map 0:v:0
-filter_complex "[0:a:0][0:a:1]amerge=inputs=2,aformat=channel_layouts=stereo[aout]"
-map [aout]

Two implementation notes:

  • -map 0:v:0 is required. A complex filtergraph disables ffmpeg's automatic output stream selection, so the video stream must be mapped explicitly once -filter_complex is present.
  • aformat=channel_layouts=stereo rather than -ac 2. -ac on a filtergraph output is unreliable, and the in-graph form also downmixes correctly with more than two mono tracks (capped at MAX_MERGED_AUDIO_STREAMS = 8).

The TUI shows an info line ("Source carries discrete mono audio tracks; merging them to stereo") when the merge kicks in.

Deliberately narrow

  • Merge only when every stream is mono. A master with a stereo mix plus an alternate-language stereo pair would otherwise get two programs blended together. ffmpeg's default already picks the stream with the most channels there, which is correct — left alone.
  • Single-stream sources are untouched.
  • Streams missing a channels field are not merged — without it we can't distinguish discrete mono tracks from alternate mixes, so we keep the default.
  • Speed-corrected (half-rate MXF) path skips the probe, since extract_video_essence writes -an and there's no audio to merge.

Testing

  • 8 new unit tests: the merge, input cap, single-track, multi-stereo, mixed-channel, audioless, no-streams, and missing-channels cases. All 15 tests pass.
  • Verified end-to-end against real ffmpeg (single stereo aac copy-through path and the two-mono merge both checked).
  • cargo fmt/clippy findings in this diff's file are all pre-existing drift outside the changed hunks.

🤖 Generated with Claude Code

…ng them

create_rendition built its ffmpeg command with .codec_audio("aac") and no
-map arguments, relying on ffmpeg's default stream selection, which writes
exactly one audio stream to the output. Avid/broadcast MXF masters
conventionally carry the stereo program as two discrete mono tracks
(Track 3 / Track 4), so half the program audio was silently lost in the
rendition.

Reproduced on a 1080p25 DNxHD MXF with two pcm_s24le mono streams: the
rendition came out with a single mono AAC stream. With the fix it is stereo
AAC, and a DFT of the split channels confirms track 3 lands left and track 4
right.

build_mono_merge_filter inspects the ffprobe streams and returns a
filter_complex graph only when there is more than one audio stream and every
one of them is mono:

  -map 0:v:0 -filter_complex "[0:a:0][0:a:1]amerge=inputs=2,
  aformat=channel_layouts=stereo[aout]" -map [aout]

A complex filtergraph disables automatic output stream selection, so the video
stream is mapped explicitly too. aformat is used rather than -ac 2, which is
unreliable on a filtergraph output and would not handle more than two tracks.

Merging is restricted to the all-mono case. A master carrying a stereo mix plus
an alternate-language stereo pair would otherwise have two different programs
blended together; ffmpeg's default selection already picks the stream with the
most channels there, which is the correct one. Single-stream sources keep the
existing default behaviour. The speed-corrected (half-rate MXF) path feeds
extract_video_essence output, written with -an, so there is no audio to merge
there and the probe is skipped.

Mirrors the tellers-backend fix in video_processing_worker downscale_video.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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