这个页面是给 2026-08-13 那次 768p+15s 容量修复过程留的完整证据链,不加修饰,包括失败的(OOM 报错原样贴出)。核心问题:用户当时的原话是"压缩到和输出视频一样的分辨率",我实际部署的是压到约 120px(0.02 百万像素),比这个激进很多,需要拿真实产物出来对比让用户自己判断值不值。
MiniMaxH3ReferenceToVideo 节点内部有个写死的 adapt_canvas():不管你选 480p/768p/1080p 哪个输出档,参考视频永远按"短边768、面积上限768×1344"这个固定预算编码(除非源视频本身更小,那就不放大)。这些参考 token 会贯穿全部采样步。所以"压缩到和输出一样"这个思路,对 768p 输出来说约等于什么都不做——而"什么都不做"正是下面第一组测试里 OOM 的原因。要把 5 张图 + 2 条视频撑到 768p/15s,必须把参考视频压到比 768p 明显更小。
同一张参考图、同一条参考视频、同一个 seed(4242)、768p、15秒、8步,只有参考视频的压缩程度不同。这是真正意义上的"同一个东西"对比,不是不同 case 之间的感觉。
Allocation on device 0 would exceed allowed memory. (out of memory) Currently allocated: 23.45 GiB Requested: 8.17 GiB peak_vram: 30584 MiB elapsed: 234.2s(跑到失败为止)
原始素材:参考图 · 参考视频原片(480×704)
这条是直接打生产 API 跑出来的(image0..4 + video0/1 全部 5 图 2 视频),而且是在 GPU4 已经因为之前任务残留显存碎片的真实状态下测的,不是干净重启的卡——更接近实际排队场景。同样 0.035MP(256px) 在这个最重组合上第一次是失败的(碎片化显存下 OOM),说明重的组合下 256px 余量不够,这也是最终选 120px 而不是 256px 的直接原因。
| 参考图 \ 参考视频 | 0条 | 1条 | 2条 |
|---|---|---|---|
| 0张 | 387s | 378s | 429s |
| 1张 | 357s | 381s | 426s |
| 2张 | 362s | 405s | 436s |
| 3张 | 366s | 408s | OOM |
| 4张 | 378s | 465s | OOM |
| 5张 | 396s | 471s | OOM |
这张表用的是 int8_convrot(生产同款底模)。同一批还测了 nvfp4,结果反而更差更慢(nvfp4 在 3-5张图+2视频也 OOM,比 int8 覆盖范围更小),所以没有换底模。表里的 0.035MP 是这次压测用的固定值,最后 4/5 张图+2条视频这两格在真实碎片化显存下失败(见上面的对照),所以生产环境最终统一用了更保守的 0.02MP,不是按格子分别调的。
用户原话:"如果保留和输出视频相同的比例,也就占一倍的空间吧应该?那768p应该能跑的呀?" ——这个实验就是直接验证这句话:参考视频帧数完全不变(362帧,和输出等长),只把每一帧的分辨率强制拉伸到跟 768p 输出画布同一个比例/规格(不是缩小,是精确匹配),然后看能不能跑。
| 组合 | 参考视频规格 | 结果 | 报错 |
|---|---|---|---|
| 0图 + 1视频 | 768×1126,362帧,和输出画布同比例 | ❌ OOM | Requested 8.56 GiB, allocated 26.30 GiB, limit 31.36 GiB |
| 1图 + 1视频 | 同上 | ❌ OOM | Requested 8.64 GiB, allocated 26.54 GiB, limit 31.36 GiB |
| 0图 + 2视频 | 768×1126 + 768×972,各362帧 | ❌ OOM | Requested 12.16 GiB, allocated 24.31 GiB, limit 31.36 GiB |
结论:哪怕只挂 1 条参考视频、0 张参考图,只要这条视频跟输出视频"同比例同规格",362帧 15秒的 768p 输出就直接 OOM。"占一倍空间"的直觉不成立——原因见下面的计算拆解:参考视频贵在帧数,不是分辨率,一条和输出等长的参考视频,哪怕分辨率再小,光时间维度的开销就摊掉了输出本身 3 成以上的预算;分辨率再拉到跟输出一样大,直接干到 85% 以上,几乎是又生成了一份输出。
公式直接从 comfy_extras/nodes_minimax_h3.py 源码扣出来,不是拍脑袋估的。
每个"视频类"输入(输出视频本身、或一条参考视频)占用的 token 数:
token = 时间维度(latent_t) × 空间维度(spatial)
latent_t(帧数 F) = 2 若 F ≤ 5
= ((F-5)//17)×5 + 2 否则
(VAE 时间压缩约3.4倍:362帧 → 107个latent时间步)
spatial(像素面积 A) = A / 256
(VAE 空间压缩 16×16 = 256倍)
参考图片("match"模式):缩放到不超过输出画布的像素面积,时间维度按最小值 2 估算(图片开销本身很小,这个近似不太影响结论)。
参考视频的帧数会先按 min(源视频帧数, 输出帧数) 截断,再往下取整吸附到 17k+5 网格——如果源视频比输出还长,也不会超过输出帧数,但只要源视频≥输出时长(我们这次源视频恰好也是362帧,跟15秒输出一样长),这个截断不会帮你省任何东西。
base = latent_t(362) × spatial(1344×768)
= 107 × 4032
= 431,424 token
| 参考视频规格 | token | 占 base 比例 |
|---|---|---|
| 120px 短边(0.02MP,生产现在用这个) | 8,359 | 1.9% |
| 256px 短边(0.035MP) | 14,629 | 3.4% |
| 160px 短边 | 15,649 | 3.6% |
| 原生 ~480p(不特意压缩,源视频本身大小) | 141,240 | 32.7% |
| 768 原比例(跟输出画布完全一致,用户这次要求的实验) | 361,446 | 83.8% |
同一条视频,从120px压到"跟输出一样大",token 从 1.9% 暴涨到 83.8%——差了 43 倍。这就是为什么"保持原比例"和"压小"之间画质/容量的取舍这么剧烈,不是线性的、也不是"多一倍"这种量级。
我最初按"额外token占base的百分比"去校准分界线,用的是 480p/768p/1080p 三档的旧压测数据。结果发现同一个百分比在不同分辨率下判断不一致——追查下去发现是压测脚本自己的探测逻辑有问题:它用一个"跨档共享的显存上限"做剪枝,如果同一档里先测到的、比较轻的组合在 362 帧失败了,后面更重的组合会直接被跳过 362 帧、只测更小的候选值——不是真的测过 362 帧失败,是被跳过没测。我一开始把这些"没测过"的当成了"测过且失败",得出的百分比结论是错的。改用只看真正测过的成功/失败边界之后,结论完全不一样,见下面。
把 480p/768p/1080p 三档里真正测到失败-成功边界的组合(不是被剪枝跳过的)、加上这次专门测的几组,全部换算成绝对 token 数:
| 组合 | 状态 | 绝对token |
|---|---|---|
| 768p 5图2视频 @160px | ✅成功 | 503,042 |
| 768p 2图2视频 @256px | ✅成功 | 476,810 |
| 768p 0图2视频 @243帧(原生~480p视频) | ✅成功 | 467,424 |
| 1080p 0图2视频 @124帧(原生~480p视频) | ✅成功 | 392,940 |
| 768p 2图2视频 @243帧(原生~480p视频) | ❌OOM | 483,552 |
| 768p 1图1视频 @原生~480p、362帧 | ❌OOM | 580,728 |
| 768p 0图1视频 @362帧(原生~480p视频) | ❌OOM | 563,034 |
| 1080p 0图0视频 @362帧(纯输出,不含任何参考) | ❌OOM | 873,120 |
最高的确认成功案例(503,042)和最低的确认失败案例(483,552)有交叉——差距只有约4%,这个量级的重叠就是显存碎片化造成的真实噪声(同一个配置在干净卡上成功、在跑过其他任务的卡上失败,我们已经在生产上真实遇到过一次)。
结论:绝对 token 数是比"占base百分比"更可靠的预测指标,且不随分辨率档位漂移(横跨480p/768p/1080p验证一致)。安全阈值建议定在 ≤450,000 token(留够碎片化余量);450,000~500,000 之间是灰色地带,可能成功可能失败;超过500,000 基本必炸。
以"768p输出、15秒(362帧)、1张参考图、1条参考视频(源视频原本也是362帧,压到120px)"为例,一步步algorithm:
第1步 输出本身的 token:
latent_t(362) = ((362-5)//17)×5+2 = 21×5+2 = 107
spatial(1344×768) = 1344×768/256 = 4032
base = 107 × 4032 = 431,424
第2步 参考图的 token(1张,"match"模式缩到输出画布面积):
spatial(1344×768) = 4032,时间维度按最小值2估
img_tok = 4032 × 2 = 8,064
第3步 参考视频的 token(1条,源视频362帧,压到120px/0.02MP):
帧数先截断: min(362源视频帧数, 362输出帧数) = 362,再吸附17k+5网格,仍是362
latent_t(362) = 107 (和输出一样,因为帧数一样)
spatial(0.02MP) = 20,000/256 = 78.1
vid_tok = 107 × 78.1 = 8,359
第4步 总和:
总token = base + img_tok + vid_tok
= 431,424 + 8,064 + 8,359
= 447,847 ← 低于 450,000 安全阈值,应该稳
(这正好对应我们实测的"1图1视频@120px"那组,实测确实成功,总token算出来是447,847,和上面手算一致)
要推广到别的规格,把第1步换成你要的输出分辨率/时长;第2步按参考图数量重复;第3步是关键——每条参考视频要单独算,且要用那条视频自己的(帧数,压缩后像素面积),帧数是最敏感的变量,压缩分辨率对token是线性的,但"视频比输出短"这件事能省下的更多(下面有专门验证)。
从公式能看出来:token = 帧数决定的latent_t × 分辨率决定的spatial,是乘法关系。目前生产的做法是保留参考视频全部362帧、疯狂压分辨率(到120px)。另一个思路:把参考视频裁短(比如只取前5秒=124帧),省下来的token预算换成更高的分辨率——同样的token价格,362帧配120px,vs 124帧配约200px(124帧的latent_t=37,是362帧的107的约1/3,所以分辨率预算能扩大约3倍,短边从120px提到约200px)。
代价:只保留参考视频的前5秒,如果视频后半段有主要动作,会丢失那部分动作信息——这是"空间清晰度"和"时间覆盖度"之间的真实取舍,不是免费午餐。
⚠ 老实说这组对比有个瑕疵:测试用的 prompt 统一写的是"A person walks across a sunlit room",但参考图其实是两只猫(橘猫+灰猫)——prompt 说"人",模型就真的生成了人,没有照参考图的猫身份走。这个不一致在今天所有对比里都一样(120px/256px/裁帧版全用的这同一个 prompt),所以几组之间的相对对比依然公平,但没有一组真实体现"参考图身份保留得好不好",这一点如果要严谨评估画质还需要重新用匹配的 prompt 补测。
现在生产环境所有 case 统一用 0.02MP(约120px),这样能保证最重的组合(5图+2视频)在显存碎片化的真实条件下也稳定成功。代价是所有 case(包括只有1张图1条视频的轻量组合)的参考视频画质都被压到这个程度,尽管上面的对比看起来肉眼差异很小。
如果你觉得没必要"一刀切",可选的方向: