MasterAudioMux (terminal mux-LAST, -c:a copy)
MasterAudioMux
- final_video_path
- report
OTR_MasterAudioMux is the terminal node - the last thing that runs before a finished episode lands in output/otr/obs/. Its job is deliberately narrow, and the narrowness is the point: it's the only node in the pack that adds audio to the final video, and it does it with -c:a copy - the audio stream is copied byte-for-byte, never re-encoded.
Why does that matter? The pack's whole architecture is "audio is the source of truth": a frozen 48 kHz master defines the episode timeline, and the video is rendered to fit it. If the mux re-encoded the audio, that frozen master would silently drift - the very thing the pipeline's determinism discipline exists to prevent. So the rule is structural: video renders silent (OTR_SilentComposite), captions and credits burn in, and only here does the master WAV get muxed onto the video, untouched.
How it works
Two required inputs: silent_video_path (the always-silent composite from OTR_SilentComposite) and master_audio_path - the frozen master WAV from OTR_EpisodeAssembler.output_path, copied with -c:a copy. No -shortest flag, and a byte-identical assert after the mux: the pack verifies the audio that went in is the audio that came out.
The ordering gate is audio_done (EpisodeAssembler's out3) - wire it and the mux is guaranteed to run after the audio freezes. And declared_credits_tail_s is the credits-aware guard: OTR_CreditsRoll declares how long its silent tail runs, and the mux's tail guard permits a video that's v <= audio + declared + tolerance. If you skip that wire, the guard falls back to the OTR_MAX_CREDITS_TAIL_S env ceiling - which is how you'd get a legitimate render refused for "video too long."
One retired connector to ignore: clip_manifest_json is kept only for topology compatibility - it's hashed by IS_CHANGED but has no effect on the output. Setting it does nothing.
Inputs and output
Also optional: fps (25), ffmpeg (binary on PATH), and output_path - empty defaults to <output>/otr/episodes/<stem>_final.mp4. Outputs: final_video_path and report.
Install and gotchas
Pack-level install (ComfyUI Manager → ComfyUI-OldTimeRadio, restart; v2.0-alpha). Needs ffmpeg on PATH - that's the one system dependency the whole video side leans on. The realistic gotcha: if your final MP4 has no audio, check you fed it the master WAV and not some intermediate clip - this node is the single point where audio joins, so a silent final almost always means the wrong path got wired into master_audio_path. And if the render refuses over a too-long video with credits, wire declared_credits_tail_s from the credits node instead of fighting the guard.
Inputs (11)
| Name | Type | Default | Description |
|---|---|---|---|
| silent_video_path | STRING | Always-silent composite (OTR_SilentComposite). Audio is added HERE only. | |
| master_audio_path | STRING | Frozen master mix from OTR_EpisodeAssembler.output_path (the master WAV). Copied with -c:a copy. | |
| audio_doneopt | STRING | Audio-done gate (EpisodeAssembler out3). Orders the mux AFTER the audio freezes. Opaque. | |
| declared_credits_tail_sopt | FLOAT | 0.00 | OTR_CreditsRoll's declared silent-credits tail duration. Makes the tail guard credits-aware (v <= a + declared + tol); 0 -> the OTR_MAX_CREDITS_TAIL_S env ceiling. |
| clip_manifest_jsonopt | STRING | Retired connector kept for topology compatibility (rip-sfx 2026-08-06). The manifest is hashed by IS_CHANGED but has no effect on the output. | |
| fpsopt | INT | 251–120 | Frame rate declared to the mux for timing math. Must match the silent video's real rate (the composite's manifest fps); the mux never resamples frames. |
| ffmpegopt | STRING | ffmpeg | DEPRECATED and IGNORED (2026-09-04). A workflow value cannot name the binary this pack runs -- it arrives over an unauthenticated /prompt request. Set the OTR_FFMPEG environment variable to pin a build. |
| output_pathopt | STRING | Final mp4 path. Empty -> <output>/otr/episodes/<stem>_final.mp4. | |
| video_policy_jsonopt | STRING | Video policy JSON from OTR_VideoDirector. Read for ONE question: is any role on the LTX 2.5 foley lane? If so this node mixes the foley bed under the provisional master and performs the single delivery loudness pass. | |
| foley_receipts_jsonopt | STRING | Clip manifest carrying the per-beat foley receipts (foley_path / foley_sha256 / start_s). The bed the mix is built from. Ignored unless video_policy_json names a foley route. | |
| script_jsonopt | STRING | Writer ledger JSON (OTR_LedgerScriptWriter script_json). Read for ONE question: must this episode reach otr/obs to count as delivered? A story the listener wrote must; the other banks keep their existing withheld-but-successful behaviour. |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| final_video_path | STRING | — |
| report | STRING | — |