Nodes/comfyui-obvpm-timeline/H3 Joint VAE Decode and Save (obvpm)
ComfyUI Node

H3 Joint VAE Decode and Save (obvpm)

Decode a 35 GB timeline without ever holding it all in RAM

By chanon·Created 21 days ago·Updated 2 days ago· 33
H3 Joint VAE Decode and Save (obvpm)
  • samples
  • vae
  • audio_vae
  • conditioning
  • source_audio
  • layout
  • path
base_folderproject1
filename_prefixupscale
crf19
window_seconds5.0

The end of the refine branch, and it's the node that makes the whole "sample the timeline as one latent" idea actually finishable on a consumer card. Because the sampled timeline latent is the cut - every row belongs to the clip the cut shows there - the finished video is one decode of it. No reassembly, no loop, no profile folder. But a long timeline is enormous: 1400 frames at 1920x1088 is about 35 GB of float pixels, so the frames can never all exist at once.

So it decodes a few seconds at a time and streams each block straight into the encoder.

Why the window is exact, not a hack

Core's H3 decoder already works in chunks of 5 latent steps with a 2-step lookahead and blends 5 frames into the next chunk. A window that starts and ends on 5-step boundaries, decoded with one extra chunk of run-in before it and the lookahead after it, reproduces the whole-latent decode bit for bit - the run-in's 17 frames are just dropped. The pack measured it: a 42-step clip latent decoded whole against the same latent decoded in three windows, max absolute difference 0.0 on every frame.

Which is why the window_seconds tooltip can say what it says: the output is identical at any value. Only memory changes. About 5 GB sits in RAM for a 5-second window at 1920x1088. Lower the number if your machine is tight; don't expect a quality difference.

Inputs and outputs

samples is the sampler's output - the refined joint AV latent. vae and audio_vae are the H3 video and audio VAEs. base_folder and filename_prefix decide the destination (the folder means the same thing as the Timeline's base_folder; empty means the output root), crf defaults to 19, and window_seconds is the decode window.

Three optional inputs carry real weight:

  • conditioning - the CONDITIONING the sampler ran under, i.e. H3 Joint Conditioning's output. It's stored beside the render as a .cond so the rendered take can be refined again. Leave it unwired and the render saves normally but can't be refined further. That's the on-ramp to 4x from the sources.
  • source_audio - a latent whose audio is rendered instead of the sampled one (H3 Join Latents' latent, or an audio latent from Separate AV Latent). Wire it when H3 Joint Audio Mask re-sampled the sound for lip sync. The sidecar stores whichever audio was rendered, so this is checkable later.
  • layout - from H3 Join Latents, for provenance: the source clips and sequence lines are recorded in the sidecar. It also does a safety job: a latent that isn't the timeline the joint describes is refused.

Output is path - the written MP4, with the sidecar next to it.

What you actually get on disk

A .mctx.safetensors sidecar holding the refined AV latent as a root clip (no pins, no lineage) plus, when conditioning is wired, the .cond. That means the rendered sequence is a take like any other: put it on a timeline by itself and refine it again, because H3 Joint Conditioning splices a nested conditioning table back in with its spans offset. One pass gets you there; a second gets you further, from the same sources.

Install

cd ComfyUI/custom_nodes
git clone https://github.com/chanon/comfyui-obvpm-timeline

Restart ComfyUI. No Python dependencies to install - the pack's pyproject is intentionally empty, since torch, torchaudio, numpy, safetensors and av already come with ComfyUI. Needs ComfyUI 2026-08-13 or later. In Manager, search the pack title comfyui-obvpm-timeline; every node in the pack ends in (obvpm), which makes obvpm a quick node-menu search for the set.

It uses the same encoder path as the Save node: an ffmpeg subprocess when a binary is available, else core's in-process PyAV.

Troubleshooting

  • Encode error mid-way. You're reliant on ffmpeg here. If the subprocess path fails you'll get a plain error rather than a silent fallback, and the usual cause is a missing or mismatched ffmpeg on PATH.
  • The render refuses your latent. The layout check caught a mismatch - usually the sequence changed between building the joint latent and rendering it. Re-run the pass end to end.
  • Huge RAM use. window_seconds up. It's a pure memory dial; the output is the same.
  • The take can't be refined again. No .cond beside it, because conditioning wasn't wired into this node.
  • Audio is the re-sample when you wanted the original. Wire source_audio, per H3 Joint Audio Mask's note.
  • You expected per-clip refined takes. That was the older shape of this pass, and the author removed it deliberately: it produced clips nobody edits again at the price of a loop in the graph. The sampled latent is the delivery now.
Categoryobvpm/h3

Inputs (10)

NameTypeDefaultDescription
samplesLATENTThe sampler's output: the refined joint AV latent.
vaeVAEThe H3 video VAE.
audio_vaeVAEThe H3 audio VAE.
base_folderSTRINGproject1Output-relative folder to save into. Same meaning as the Timeline's base_folder. Empty = the output root.
filename_prefixSTRINGupscaleFilename prefix within base_folder; numbering is appended automatically.
crfINT190–51H.264 quality (lower = better, bigger).
window_secondsFLOAT5.00.5–600Seconds of picture decoded at a time, rounded to whole decoder chunks. Only memory changes with it: the frames of one window sit in RAM while they are encoded (about 5 GB for 5 s at 1920x1088). The output is identical at any value.
conditioningoptCONDITIONINGThe CONDITIONING the sampler ran under (H3 Joint Conditioning's output). Stored beside the render as .cond so the rendered take can be refined again. Unwired = the render saves normally and cannot be refined further.
source_audiooptLATENTOptional: a latent whose AUDIO is rendered instead of the sampled one (H3 Join Latents' latent, or Separate AV Latent's audio_latent). Wire it when H3 Joint Audio Mask re-sampled the sound (audio_denoise above 0, for lip sync), so the finished render keeps the source soundtrack. The sidecar stores the audio that was rendered.
layoutoptOBVPM_H3_JOINTFrom H3 Join Latents, for provenance: the source clips and sequence lines are recorded in the sidecar, and a latent that is not the timeline the joint describes is refused.

Outputs (1)

NameTypeDescription
pathSTRINGPath of the written MP4; the sidecar sits next to it.