Nodes/ComfyUi-MpiNodes/Mpi H3 Encode AV
ComfyUI Node

Mpi H3 Encode AV

One latent that carries picture and sound together

By MadPonyInteractive·Created 11 months ago·Updated 4 days ago· 3
Mpi H3 Encode AV
  • vae
  • images
  • audio_vae
  • audio
  • mask
  • latent
  • info
mask_start0
mask_end-1
audio_start0
audio_end0
audio_ranges

MiniMax H3's party trick is that audio and picture are born together - one omni-modal model producing synced stereo sound and video in a single pass, and the first serious open-weights answer to the native-audio gap that Veo used to own alone. So it's a genuine surprise that core ComfyUI won't let you treat the two streams as one thing.

Core encodes video with VAEEncode and audio with VAEEncodeAudio, and there's no built-in way to join the results into the single joint AV latent that the interesting H3 workflows want. The only third-party route used to be a fork that monkey-patches two ComfyUI internals at import time - fragile, and a pinning headache every time ComfyUI updates. MpiH3EncodeAV is the clean version of that hack: give it a clip and its soundtrack and it hands you one LATENT with both halves packed inside.

That single output is exactly what MpiH3MaskedPrefix takes as its context, and it's what any "carry this clip forward" workflow is built on. Skip this node and you're holding a video latent and an audio latent that nothing will zip together for you.

What goes in

Four inputs, all required, and all things you already have once H3 is running:

  • vae - the H3 video VAE.
  • images - the clip's frames at 24 fps, as ONE batch. This is the field that burns people: encoding them in separate calls throws away the motion the temporal packing carries, because the VAE packs frames per latent step on a period-5 cycle (1, 4, 4, 4, 4). Feed the whole clip in one IMAGE tensor, or the seam you get later won't be where you think it is.
  • audio_vae - the H3 audio VAE.
  • audio - the soundtrack. If its sample rate doesn't match the audio VAE's own (32 kHz), the node resamples it with torchaudio before encoding, so you don't need to match rates by hand.

The one output, latent, is the packed AV pair as a ComfyUI NestedTensor - the same structure the H3 nodes themselves emit, ready for the sampler or for MpiH3MaskedPrefix.

How it works

Honest and boring, in the best way: vae.encode() on the frames, audio_vae.encode() on the (resampled) waveform, then both go into comfy.nested_tensor.NestedTensor((video_z, audio_z)). One deliberate constraint: batch size 1 only. H3 itself is batch 1, and a second item wouldn't error - it would silently pair the wrong soundtrack with the wrong picture - so the node refuses instead of guessing.

Installing it

This is one node in Mad Pony Interactive's ComfyUi-MpiNodes pack - over a hundred utilities from the team behind the Cubric Vision app, AGPL-3.0 since 1.2.7 - and it installs the same way regardless of which Mpi node you're after. ComfyUI Manager: search "ComfyUi-MpiNodes" and hit install. Or do it by hand:

cd ComfyUI/custom_nodes
git clone https://github.com/MadPonyInteractive/ComfyUi-MpiNodes

Restart ComfyUI and you're done - the pack ships no requirements.txt, so there's no pip step. torchaudio is imported lazily only when this node actually runs, and ComfyUI's own audio nodes already depend on it, so on a normal H3-capable install you never think about it.

None of the Mpi H3 nodes download models. You need the H3 weights (~42.5 GB, from MiniMaxAI/MiniMax-H3) plus the H3 video VAE and audio VAE sitting on the usual ComfyUI model paths. Worth a pause before you pull them: the H3 Community License excludes the US, EU, UK and South Korea from its Applicable Territory - running the local weights there isn't licensed, and the hosted Hailuo API is the legal route in those regions.

Common issues

  • Frames split across multiple batches - motion is lost and the later seam lands early. One encode call for the whole clip, period.
  • "latent carries video only" - that's MpiH3MaskedPrefix refusing a context latent with no audio half, which is what you get if you hand it a plain VAEEncode output or leave audio_vae/audio unwired. The context has to come out of this node, complete.
  • ModuleNotFoundError: torchaudio - appears only at runtime because the import is lazy. On a normal H3-capable ComfyUI this never happens (core audio nodes already need torchaudio); if it does, install it into your ComfyUI Python environment.
  • Nothing here downloads H3. This node is glue; the weights and the two VAEs are on you.
CategoryMpiNodes/Utils

Inputs (10)

NameTypeDefaultDescription
vaeVAEThe H3 video VAE.
imagesIMAGEThe clip's frames at 24 fps, as ONE batch - encoding them in separate calls throws away the motion the temporal packing carries.
audio_vaeVAEThe H3 audio VAE.
audioAUDIOThe clip's soundtrack. Resampled to the audio VAE's own rate if it does not already match.
maskoptMASKPer-frame inpainting mask at image resolution, e.g. from SAM3 (1 = regenerate, 0 = keep). It may cover the WHOLE clip or just a range of it - say where its first frame sits with mask_start. Left unconnected this is a plain encode with no mask, exactly as before.
mask_startoptINT00–100000The clip frame the mask's FIRST frame lands on. Leave at 0 when the mask covers the whole clip; set it to the range start when the mask came from a segmenter run over part of the clip. Frames outside the mask are kept.
mask_endoptINT-1-1–100000Clip frame to stop masking at, exclusive. -1 uses all of the supplied mask, so the default range is whatever the mask covers.
audio_startoptINT00–100000First clip frame of the AUDIO window to regenerate, at 24 fps. Snapped DOWN to a multiple of 3 - audio's 40 Hz clock only lands on a whole step every 3 frames, and an off-grid edge drifts the sound against the picture with no error. Only used when audio_end is not 0.
audio_endoptINT0-1–100000Clip frame to stop regenerating audio at, exclusive; snapped UP to a multiple of 3. 0 - the default - keeps the whole soundtrack, which is the plain inpaint. -1 runs to the end of the clip. Unlike the picture mask this selects a TIME range, not a source: everything sounding in the window is regenerated together, so it gives 'redo this window to match the video', never 'add a sound under the dialogue' - for that, decode in mix mode.
audio_rangesoptSTRINGSeveral disjoint stretches in one pass: `0-10, 40-50, 90-100`. Start inclusive, end EXCLUSIVE, so `0-10` is the first ten frames; a bare number is one frame and an end of -1 runs to the clip end. Overrides audio_start/audio_end when it is not empty, and MUST be typed identically on MpiH3DecodeAV. Leave the whole soundtrack unmasked SOMEWHERE - the model matches the room, the mic and the ambience by listening to what you did not mask, so masking all of it is the one case where it has nothing to match and comes back with its own score.

Outputs (2)

NameTypeDescription
latentLATENT
infoSTRING