音视频联合 latent 是怎么解码成一个 MP4 的
第一次打开 MiniMax H3 的 ComfyUI 官方模板,最容易愣住的地方不是模型加载那一堆,而是采样器右边:只有一根 LATENT 线出来,却分叉接进了两个解码节点,一个是 VAEDecode,一个是 VAEDecodeAudio。而且这两个解码节点各自吃的还不是同一个 VAE 文件。
看起来像是接错了。实际上这正是 H3 架构在节点图上的样子。这篇就把这一段拆开讲:模型那边发生了什么,节点这边为什么必须这么连,以及连线之外你真正需要做决策的几个点。
以下 ComfyUI 侧的事实来自 docs.comfy.org 的 H3 官方教程页与 Comfy-Org/workflow_templates 仓库里的 templates/video_minimax_h3_r2v.json(含模板内嵌的官方说明),对应 ComfyUI v0.31.0(2026-08-08);模型架构部分来自 MiniMax-AI/MiniMax-H3 仓库截至 2026-08-09 的 README 与 transformer/config.json。
一、架构那边:Omni-Transformer 预测的本来就是两种 latent
按 README 的「Model Architecture」章节,H3-Base 先用各模态对应的 encoder 或 VAE 把不同模态编码好,组织成一条统一的 packed multimodal sequence;在整条序列送进 H3-Omni-Transformer 之前,用 RoPE 捕捉 token 之间必要的空间与时间关系。
分工是明确的:文本由 H3-Encoder 编码,视觉输入由 H3-Encoder 与 H3-VisualVAE 共同编码,音频则仅由 H3-AudioVAE 编码。然后关键的一句:H3-Omni-Transformer 联合预测视频与音频的 latent,再分别解码为视频与立体声音频。
也就是说,「一个模型出画面、另一个模型配音」这种流水线的想象在这里不成立。声音和画面是同一个 Transformer 在同一次前向里一起预测出来的,分开是发生在预测之后的解码环节。
两侧 latent 的形态在 README 与 config 里都能对上:
- H3-VisualVAE 是一个时间因果(temporally causal)的视频自编码器,空间压缩 16×、时间压缩 4×、24 个 latent 通道,记作 f16t4d24;视觉 latent 送进 Omni-Transformer 前还会按
1 × 2 × 2(time, height, width)做 patchify,所以进 Transformer 的视觉 token 有效空间下采样是 32×、时间下采样仍是 4×。 - H3-AudioVAE 对每个声道把 32 kHz 音频压成时间速率 40 Hz 的 latent token 序列;左右声道共用同一套 encoder 与 decoder,但每个声道独立处理,解码后再重新合并,立体声由此而来。
transformer/config.json里in_channels是 24、audio_in_channels是 32,正好对应视觉侧的 24 通道和音频侧的 latent 维度;patch_size是[1, 2, 2],与 README 的说法一致,16 × 2 = 32 也和「有效空间下采样 32×」对得上。
这些数值只是用来相互印证「两路 latent 确实并存在同一条序列里」,不能拿去算显存、算速度——官方没给这类数据,本文也不算。
二、节点那边:为什么是两个 VAELoader
架构落到 R2V 模板上,就是下面这几个节点。只列与解码链路直接相关的部分,其余(UNETLoader、CLIPLoader、LoadImage 之类)这里不展开:
| 节点类型 | 模板里的取值 |
|---|---|
VAELoader(视频) | minimax_h3_video_vae_fp16.safetensors |
VAELoader(音频) | minimax_h3_audio_vae_fp32.safetensors |
KSamplerSelect | res_multistep |
BasicScheduler | scheduler simple、steps 20、denoise 1 |
VAEDecode / VAEDecodeAudio | 分别解码视频与音频 |
CreateVideo | fps 24、第二参数 8 |
SaveVideo | 输出前缀 video/MiniMax_H3,格式与编码均 auto |
两个 VAELoader 不是冗余。H3-VisualVAE 和 H3-AudioVAE 本来就是两个不同的自编码器,在 ComfyUI 侧也就是两个独立的文件,分别放进 ComfyUI/models/vae/。两个文件名末尾标的精度也不同,一个是 fp16,一个是 fp32——这是官方发布的文件命名事实,至于为什么这么定,官方教程没有解释,本文不替它编理由。
实践上要注意的是:这两个文件都得下,少一个链路就断在解码这一步。而且 ComfyUI 侧用的是 huggingface.co/Comfy-Org/MiniMax-H3 这一套,不是 MiniMax 官方那套仓库。
三、一根线接两个解码器,不是接错了
模板说明里写得很清楚:采样器输出的是音视频联合的 LATENT,同时喂给 VAEDecode(走 video vae fp16)与 VAEDecodeAudio(走 audio vae fp32),每个解码节点会自动从打包的 latent 里取出属于自己的那一半。
这条值得单独强调,因为它解释了两个新手常见的困惑:
一是图上没有任何拆分节点。模板里并不存在一个专门负责「把视频 latent 和音频 latent 拆开」的节点,因为按官方说明,取出各自那一半的逻辑发生在解码节点内部。看到一根线分叉到两个解码器,那就是对的,不用去找缺了哪个节点。
二是别去猜哪一路该接哪根线。既然两个解码节点吃的是同一个联合 latent,就不存在「把音频线接错到视频解码器上」这种情况——能接错的只有 VAE 输入那一侧:把 audio vae 接进 VAEDecode,或者把 video vae 接进 VAEDecodeAudio。排查的时候优先看这两根线,而不是看 latent 线。
再往后是 CreateVideo:它把视频帧和音频两路 mux 成一个带同步声音的 MP4,模板里 fps 是 24、第二参数是 8。最后 SaveVideo 落盘,输出前缀 video/MiniMax_H3,格式与编码都是 auto。
顺带一提,模板里那个 ComfyMathExpression 表达式 max(5, round(a * 24)) + (5 - (max(5, round(a * 24)) % 17)) % 17,就是官方教程说的「snaps to the model’s 17-frame-per-block (17k+5) grid」的实现:先把秒数乘 24 取整(下限 5),再补齐到 17k+5。所以你在 Duration 里填的秒数不会被原样使用,会被吸附到一个合法帧数上。看到最终帧数和自己填的对不上,先想起这个式子,别急着怀疑节点坏了。
四、这条链路上真正需要你做决策的地方
连线本身没得选,官方模板怎么给就怎么用。真正要判断的是下面这几处,按你的处境走:
如果你的提示词里参考标签很密集——就是 <Picture 1>、<Video 1>、<Audio 1> 这类标签接了好几个——那就先动调度器。模板给的 BasicScheduler 默认是 simple,但官方模板说明自己写着:对这类参考密集的提示词,beta 或 normal 调度器往往比默认的 simple 表现更好。默认值和官方建议不一致,这事有点别扭,但它是官方明写的,不是我们的猜测。所以 R2V 这种天然依赖多个参考输入的任务,把调度器从 simple 换成 beta 或 normal 试一轮,是成本最低的一次调整。采样器那边模板选的是 res_multistep,官方没有给替代建议,就先别动。步数 20、denoise 1 同理。
如果你嫌慢——教程页提到 Patch Sage Attention KJ 是可选优化,官方教程称可加速约两倍,需要单独安装依赖;教程页同时注明,过程中可能看到 “using pytorch attention instead” 之类的消息,属于正常现象。ComfyUI 侧对应的启动参数是 --use-sage-attention。这里的「约两倍」是教程页的表述,不是我们跑出来的数字,装不装、值不值得多折腾一个依赖,你自己按机器情况定。
如果你在纠结自己的机器跑不跑得动——这一点官方教程页没有给出模型文件体积,也没有给显存要求,MiniMax 的 README 同样没有给显存数字。所以本文不比、也不给任何换算。能提供的只是一个事实背景:ComfyUI 侧用的是量化/裁剪过的权重(扩散模型带 pruned_int8_convrot,文本编码器带 nvfp4_awq),与 MiniMax 官方发布的 BF16 checkpoint 不是同一套东西。两边的结果不要混着评估,这也是我们唯一能下的结论——画质谁好谁坏、量化损失多少,没有任何公开对比数据,谁都不该替你下判断。
如果你想要 2K——别在 ResolutionSelector 的 megapixels 上找答案。H3 的 2K 走的是 H3-Regenerate-2K 模块,它的做法是让基础模型以 in-context 的方式重新生成自己的低分辨率结果,从而复用原始多模态上下文、恢复小字和精细细节;但这个模块尚未开源,官方只提供 API,ComfyUI 的模板里没有它。所以把 megapixels 拉到高档并不等于「开了 2K」。
如果你还没升级 ComfyUI——官方教程页写明需要 v0.30.0 或更高(v0.30.0 发布于 2026-08-03),关联 PR 是 ComfyUI#15224。而 v0.31.0(2026-08-08)的 release notes 里有一条「fix(minimax): cast raw parameters to input device in H3 VAEs by @rivadart in PR #15268」——修的正好是 H3 VAE 的一个设备转换问题,也就是本文讲的这段解码链路。要跑 H3,建议至少到 v0.31.0。另外模板说明提醒过:Desktop 与 Cloud 跟随 stable 发布,某些「nightly 才支持」的模型在那边可能还不可用。
五、卡住了往哪儿提
模板内嵌说明给了一条官方分流规则,照着走能少绕路:跑不起来或运行时报错,去 github.com/comfyanonymous/ComfyUI/issues;界面、前端相关的问题去 github.com/Comfy-Org/ComfyUI_frontend/issues;工作流本身有问题(比如模板连线、默认值)去 github.com/Comfy-Org/workflow_templates/issues。这一段解码链路属于哪一类,取决于你的报错落在哪一层——VAE 加载和设备相关的错通常是第一类,节点默认值的疑问是第三类。
回到开头那根分叉的线:它之所以长成这样,是因为 H3-Omni-Transformer 从一开始就把视频和音频当成一件事在预测。节点图只是把这件事如实画了出来。理解了这一点,再看两个 VAELoader、两个解码节点、最后一个 mux,就不会觉得哪里多余了。
延伸阅读
本文依据 ComfyUI 官方仓库(github.com/Comfy-Org/ComfyUI)的 README、comfy/cli_args.py、release notes 整理,核对日 2026-08-09,对应版本 v0.31.0;文中引用的版本与 PR 状态为该日期的快照。本文内容为官方文档与源码口径,非本机实测。参数、默认值与功能随版本变动,请以官方文档与 python main.py --help 的实际输出为准。本文同时依据 MiniMax H3 官方仓库(github.com/MiniMax-AI/MiniMax-H3)的 README、模型配置文件与官方 h3-prompt-writing skill 文档整理,核对日 2026-08-09;本文内容为官方仓库口径,未在本机部署或调用过 H3,模型、部署方式与许可条款以官方最新说明为准。ComfyUI 侧的模型文件与工作流信息来自 docs.comfy.org 的官方教程与 Comfy-Org/workflow_templates 仓库的模板文件。