MediaUtilities_SaveVideo
The node that routes around your codec problems and actually muxes audio
- frames
- audio
Core ComfyUI will happily save your frames as WEBP or PNG sequences, but it won't write an H.264 mp4 and it definitely won't put audio on one. That's this node's job. It's the heavy lifter in ComfyUI-MediaUtilities: feed it IMAGE frames, optionally an AUDIO wire, and it writes a real video file into your output folder - mp4, webm, avi, mov, or gif - with looping and pingpong built in.
Why you'd reach for it
You've generated a clip (Wan, LTX, Hunyuan, AnimateDiff - pick your poison) and you want something you can actually send to a group chat or drop into an edit. It's also the natural endpoint for this pack's chain: load audio from a URL, generate frames to it, and save the two together with the sound intact.
How it works
The mp4 path is a small pipeline, and it's the part people don't expect. Instead of encoding H.264 directly, it writes a GIF first (via imageio, falling back to Pillow), then shells out to the ffmpeg binary to transcode that GIF to libx264 (crf 23, yuv420p). If you passed an audio wire, it runs ffmpeg again to mux AAC in. Webm, avi, and mov skip the GIF detour and go through OpenCV's VideoWriter with a fallback chain of fourcc encoders - VP90 for webm, XVID then MJPG for avi, avc1 then mp4v for mov. If every encoder refuses, it falls back to a GIF, and with convert_from_gif set to True it then tries ffmpeg to convert back into the format you actually asked for.
Inputs that matter
- frames (IMAGE) and audio (AUDIO, optional) - the only wires in.
- format (mp4/webm/avi/mov/gif) and codec (auto, h264, vp9, avc1, xvid, mjpg) - leave codec on
autounless you know exactly why you're overriding it. - fps (default 30) - match your generation's frame rate or the playback speed will lie to you.
- filename_prefix (default
video/media_url_loader) - output lands inComfyUI/output/video/. - loop_count (0–10) and pingpong (True/False) - preprocess the frames into a loopable clip with no extra nodes.
Outputs: none - it's an output node. It saves the file and returns preview info to the UI.
Installing it
Part of ComfyUI-MediaUtilities. ComfyUI Manager: Manager → Install Custom Nodes → search "ComfyUI-MediaUtilities". Or manually:
cd ComfyUI/custom_nodes
git clone https://github.com/ThanaritKanjanametawatAU/ComfyUI-MediaUtilities
cd ComfyUI-MediaUtilities
pip install -r requirements.txt
Then restart ComfyUI. No model files to download. The requirements list requests, numpy, Pillow, torch, torchaudio, and opencv-python; the first two and torch/torchaudio are usually already in your environment. The two things that aren't in requirements and will bite you later: the ffmpeg binary (needed for mp4 and for muxing audio) and imageio (used for GIF writing, with a Pillow fallback).
Gotchas
- mp4 needs ffmpeg on your PATH, and it leaves litter. Every mp4 save writes an intermediate GIF and never cleans it up, so you'll find a stray
.gifin your output folder next to each.mp4. Annoying, harmless, and easy to miss until your folder looks like a screensaver. - pip-built opencv wheels often lack H.264 and VP9 encoders - which is exactly why the mp4 path routes around OpenCV through ffmpeg. If avi works but mp4 doesn't, the node's own error message tells you the fix: install a conda build of OpenCV.
- Failures are quiet. If ffmpeg is missing, the node returns an empty result rather than a loud exception. The pack logs with DEBUG on by default, so watch the console - "ffmpeg not found" will be sitting right there.
For the common case - generated frames out, playable mp4 with audio back - it just works. Just remember the GIFs and the ffmpeg dependency.
Inputs (10)
| Name | Type | Default | Description |
|---|---|---|---|
| frames | IMAGE | — | |
| fps | INT | 301–120 | — |
| filename_prefix | STRING | video/media_url_loader | — |
| format | COMBO | mp4 | 5 options: mp4, webm, avi, mov, gif |
| codec | COMBO | auto | 6 options: auto, h264, vp9, avc1, xvid, mjpg |
| quality | INT | 851–100 | — |
| convert_from_gif | COMBO | True | 2 options: True, False |
| audioopt | AUDIO | — | |
| loop_countopt | INT | 00–10 | — |
| pingpongopt | COMBO | False | 2 options: True, False |
Outputs (0)
No outputs