✂️ Decompose Video
Rip a video into frames, audio, and the numbers you need
- video
- images
- audio
- duration
- fps
- frame_count
- width
- height
Video workflows start with a problem ComfyUI doesn't solve out of the box: how do you get a video into the graph? The ✂️ Decompose Video node is the intake valve. You give it a video - a ComfyUI VideoInput or just a file path - and it hands back an IMAGE tensor of frames, an AUDIO track, and a full set of metadata: duration (FLOAT), fps (FLOAT), frame_count (INT), width (INT), height (INT). That's seven outputs, and they're the seven things every downstream video step needs.
The decode path is worth understanding because it explains both the speed and the caveats. When PyAV is available it uses a single-pass, multi-threaded decode: the video stream is opened with thread_type = "AUTO" (so the decoder uses your CPU cores), frames land into a pre-allocated tensor instead of a growing list, and video and audio are demuxed in one sweep. The frames come out as float32 in 0–1 range, RGB - the native format every ComfyUI image node expects, so you can feed them straight into an upscaler or a fixer. If PyAV isn't installed, it falls back to an OpenCV decode, which gets you frames but no audio - you get a silent placeholder track instead, and that's the single most common "why is my audio gone" moment with this node.
Inputs: video (required, wildcard - it accepts a ComfyUI VideoInput object, a string file path, or a dict with a path/file key), max_frames (INT, default 0, tooltip "0 = no limit"), and frame_skip (INT, default 1, "Take every Nth frame, 1 = all"). The last two are your performance levers: a long 4K video will happily eat RAM and time, so cap it with max_frames or thin it out with frame_skip = 2 to grab every other frame. Note that frame_skip changes how many frames you get but not the reported duration - the duration is the original clip's, which is correct for metadata but means your frame count no longer equals duration × fps. If you're re-composing with Compose Video afterwards, feed it the same fps you got here and it'll reconstruct roughly the original timing.
The frame tensor is pre-allocated to an expected size and trimmed to what was actually decoded, so you don't get a batch full of black padding at the end - a small thing, but it shows the author thought about the common failure modes.
What's it actually for? Three jobs, mostly. Round-trip processing: Decompose → process frames (upscale, inpaint, interpolate) → Compose Video, audio carried through, which is the flagship workflow of this pack's video pair. Frame analysis: pull frames out to feed a frame-based model or just to inspect what a clip contains. And metadata extraction: sometimes you only need the fps and frame count to drive another calculation, and this node hands you all of it without a decoder dump.
Caveats, honestly stated. It's an output node that always runs, so it re-decodes every queue - for a big clip that's real cost, and there's no caching to lean on. IS_CHANGED returns float("NaN") like the rest of the pack, which is what forces that fresh decode. And because it's a pure Python + PyAV decoder, it's not a transcoder - you can't ask it to re-encode or filter; that's downstream work. If the video has no video stream at all (an audio-only file), it raises rather than returning empty frames, which is the right behavior but worth knowing.
Installation is pack-level:
cd ComfyUI/custom_nodes
git clone https://github.com/playboy-dongan/ComfyUI-Logic-nodes
or ComfyUI Manager → "ComfyUI-Logic-nodes" → restart. No model downloads. PyAV is preferred and usually present in stock ComfyUI; if the node falls back to OpenCV and you lose audio, pip install av in ComfyUI's environment restores the full path.
If you've been exporting frames to a folder and re-importing them like it's 1995, this node is the upgrade - everything stays inside the graph.
Inputs (3)
| Name | Type | Default | Description |
|---|---|---|---|
| video | * | — | |
| max_framesopt | INT | 00–99999 | 0 = no limit |
| frame_skipopt | INT | 11–100 | Take every Nth frame, 1 = all |
Outputs (7)
| Name | Type | Description |
|---|---|---|
| images | IMAGE | — |
| audio | AUDIO | — |
| duration | FLOAT | — |
| fps | FLOAT | — |
| frame_count | INT | — |
| width | INT | — |
| height | INT | — |