Video Merge Node
Finishing frames with subtitles and audio
- video_frames
- voice_audio
- background_audio
- output_video_path
- Filenames
Generating frames is the easy half. After Wan, LTX or AnimateDiff hands you a decoded IMAGE batch, you're looking at raw frames - no subtitles, no voice, no music, no muxed file you can actually post. Most people drag that clip into CapCut and finish it there. VideoMergeNode from the comfyui_merge_video pack wants that last mile to happen inside ComfyUI instead: one output node that takes your frame batch, an SRT subtitle string, and two audio tracks, and writes a finished .mp4 straight into your output folder. No API, no key, no manual ffmpeg step.
It's a small pack - a single node, a single commit - so don't expect a maintained ecosystem around it. But the thing it does is exactly the boring glue that most video workflows lack, and it does it in one click.
How it works
The mechanism is ffmpeg doing what ffmpeg does, but driven from the graph. Your video_frames tensor is piped in as raw frames at the fps you set, the SRT is burned in with ffmpeg's subtitles filter using an ASS-style force_style, and that gets encoded as H.264. Then the two AUDIO tracks are resampled to 48 kHz stereo, padded or trimmed to the exact video duration, volume-mixed, and muxed in. Because it writes exactly frame_count frames and trims audio to duration, you don't get the classic bug of every naive merge: a black tail tacked onto the end of your clip.
The inputs that actually matter
- video_frames (IMAGE) - your decoded video frame batch, straight out of the VAE.
- voice_audio and background_audio (AUDIO) - both use ComfyUI's native AUDIO type, so feed them whatever your TTS or audio nodes emit. Voice defaults to full volume, BGM to 0.35, which is a sane starting mix.
- srt_text - a multiline STRING that must be valid SRT (with
-->timestamps). It validates this, so a typo gets you a clean error instead of silent garbage. Heads up: the default is a Chinese sample subtitle - swap it for your own or every render will say 示例字幕. - fps - this is the sync lever. Because the input is a frame batch, there's no framerate metadata, so the node defines one: duration = frame_count / fps. If your clip was generated at 16 fps and you leave this at 25, your subtitles and audio will drift off the action. Match it to your generation rate.
- subtitle_style - six baked-in presets (classic_white, short_video_yellow, cinematic_box, news_blue, minimal_clean, karaoke_glow). Pick one, don't expect to fine-tune it.
Worth touching in optional land: font_name defaults to Microsoft YaHei, a Windows font - on Linux or macOS change it or subtitles may fall back to a boxy default. font_size of 0 means auto (about 4.6% of frame height). The codec fields (video_codec, crf, encoder_preset, audio_bitrate_k) are there if you care; the defaults are fine.
The outputs are output_video_path (a STRING with the full path to the new mp4) and a VHS_FILENAMES pair for chaining with VideoHelperSuite nodes. Since this is an output node, you'll see the result in the UI preview.
Installing it
No model downloads, no heavy deps - the real requirements are ffmpeg and torchaudio. Install via ComfyUI Manager (search "comfyui_merge_video") or:
cd ComfyUI/custom_nodes
git clone https://github.com/DEMOUSL/comfyui_merge_video
# restart ComfyUI
Then make sure ffmpeg is on your PATH with subtitle filter support (libass). The code checks COMFYUI_FFMPEG/FFMPEG env vars, falls back to an imageio-ffmpeg binary, then PATH - but that imageio fallback build often lacks libass, so don't rely on it for subtitles.
Where people get burned
The two real failure modes, both grounded in the source: the node doesn't show up at all because torchaudio isn't in your ComfyUI environment (it's imported at module load - pip install torchaudio matching your torch version fixes it), and subtitles fail or vanish because ffmpeg was built without libass (grab a full build - gyan.dev on Windows, brew install ffmpeg on macOS - and put it on PATH). Everything else is the usual: out-of-sync subs means your fps doesn't match the generation rate, and missing glyphs mean your font_name isn't installed. For a zero-impression node this is solidly useful - it just wants a proper ffmpeg and a matching fps.
Inputs (16)
| Name | Type | Default | Description |
|---|---|---|---|
| video_frames | IMAGE | — | |
| voice_audio | AUDIO | — | |
| background_audio | AUDIO | — | |
| srt_text | STRING | 1 00:00:00,000 --> 00:00:02,000 示例字幕 | — |
| fps | FLOAT | 25.01–120 | — |
| subtitle_style | COMBO | classic_white | 6 options: classic_white, short_video_yellow, cinematic_box, news_blue, minimal_clean, karaoke_glow |
| filename_prefixopt | STRING | MergeVideo | — |
| font_nameopt | STRING | Microsoft YaHei | — |
| font_sizeopt | INT | 00–200 | — |
| voice_volumeopt | FLOAT | 1.000–4 | — |
| bgm_volumeopt | FLOAT | 0.350–4 | — |
| video_codecopt | STRING | libx264 | — |
| audio_codecopt | STRING | aac | — |
| encoder_presetopt | STRING | veryfast | — |
| audio_bitrate_kopt | INT | 19264–512 | — |
| crfopt | INT | 180–51 | — |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| output_video_path | STRING | — |
| Filenames | VHS_FILENAMES | — |