目标:把 H3 的单次短片(≤15.08s)拆成多段生成、再自动合并成长视频。 实测:3 段 × 5s → 15.5s / 768×1344 / 24fps / 372 帧,全程本地 0 积分。
docs/long-video.md)continuation)| 方式 | 作用 | 消耗参考槽 | 备注 |
|---|---|---|---|
none | 首段独立生成 | — | 每项目第一段 |
tail_frame | 服务端从上一段尾帧提取,作为本段起始帧 | 1 个像素槽 | 必须配 FL2VA profile |
previous_video | 上一段的永久视频作为参考 | 1 个像素槽 | Ref2VA 可用 |
motion_context | 复用上一段 H3 音视频 latent | 不消耗 | 仅 API spec(project create)支持,acceptance manifest 不支持;相邻段尺寸必须完全一致;保存前自动裁掉重复头部 |
Motion Context 参数:video_frames ∈ {5,22,39,56}(默认 22)、audio_frames 0–240(默认 24)。 它与 Base 断点续采互斥,不能同时开。
h3ctl asset upload <文件> --output json # asset 按内容哈希复用,重复上传返回同一 id
# 返回 data.uploads[0].result.asset.asset_id
完整示例(3 段,每段 5s,9:16):
{
"version": 1,
"project": {
"title": "Armored warrior identity migration - 3 segment long video",
"segments": [
{
"continuation": "none",
"request": {
"prompt": "<六段式提示词,见下方「提示词硬约束」>",
"parameters": {"aspect_ratio": "9:16", "duration": 5, "steps": 4,
"lora_strength": 0.75, "seed": 20260916, "mode": "auto"},
"profile_id": "minimax-h3-ref2va",
"profile_version": "1.3",
"profile_digest": "cda098452f5b138ad604289d6a4786ecef57b7f43ab12a3f3581f4eb224c2fe4",
"references": [
{"asset_id": "<角色图id>", "role": "identity"},
{"asset_id": "<源片段id>", "role": "reference"}
]
}
},
{ "continuation": "previous_video", "request": { "...同上,换 seed/prompt/denoise..." } },
{ "continuation": "previous_video", "request": { "...第三段..." } }
]
},
"acceptance": {"width": 768, "height": 1344, "expect_audio": true,
"duration_tolerance": 0.5, "output_name": "out.mp4"}
}
schema 硬性约束:
segments 最少 3 段、最多 1000(少于此数直接校验失败)continuation 只能 none / tail_frame / previous_videoreferences[].role ∈ first_frame / last_frame / identity / style / composition / reference(+ voice 类)acceptance.expect_audio 必须为 true(const)duration 最大 15.083333333333334,不要写更大的近似值acceptance 的 H3 尺寸:16:9 = 1344×768;9:16 = 768×1344profile_id/version/digest 必须取自当前 /api/capabilities# A. 一键成片(推荐)
h3ctl video compose --spec manifest.json --to final.mp4 --timeout 0
# B. 分步(可断点恢复;连接断开不影响服务端项目)
h3ctl project create --spec project-only.json # 只要 project 对象
h3ctl project run <PROJECT_ID>
h3ctl project wait <PROJECT_ID> --timeout 0
h3ctl project merge <PROJECT_ID>
h3ctl project download <PROJECT_ID> --to final.mp4
离线自检(不联网、不提交):
python3 -m scripts.long_video --manifest manifest.json --output-dir out --dry-run
create 阶段暴露){"code":"unstable_reference_tag",
"message":"derived-reference prompts cannot contain literal <Picture N>, <Video N>, or <Audio N> tags;
use a stable @{asset-id} alias"}
长视频/迁移项目里,源视频是运行时派生资产,标签要到每段提交时才分配,所以提示词里不能预写 <Picture 1> / <Video 1> / <Audio 1>:
@{asset-id} 别名(如 @{66bdff...})<Subject N> 可以保留(它是内容标签,不是媒体标签)这也解释了为什么
h3ctl generate video(直接生成)能用<Picture 1>,而走 project 的路径全都卡在create。
tail_frame 需要 FL2VA profile{"code":"continuation_profile","message":"tail_frame requires an FL2VA profile"}
尾帧续接本质是"首帧驱动",必须用 minimax-h3-fl2va(version 1.2)。若想全程用 minimax-h3-ref2va,第二段起改用 previous_video。
python3 -m scripts.long_video --dry-run 只做 manifest 校验;服务端的语义校验(标签、profile/续接匹配、尺寸一致等)更严。 建议:正式跑之前先手动 POST 一次看错误:
curl -s -X POST http://127.0.0.1:6020/api/video-projects \
-H 'Content-Type: application/json' -d @project-only.json
| 项 | 值 |
|---|---|
| 段数 / 每段 | 3 段 × 5s(none / previous_video / previous_video) |
| profile | minimax-h3-ref2va v1.3 |
| 输出 | 768×1344 / 24fps / 372 帧 / 15.5s |
| 总耗时 | 约 37 分钟(21:15 → 21:52,含每段重新加载 13GB 模型) |
| 显存峰值 | 14480 MiB / 16311 MiB(余量很小) |
| 成本 | 0 积分(本地) |
注意:三段串行比单段线性叠加更慢(每段都要重载模型 + 续接段要额外吃上一段视频作参考)。
本次成品最后一段的后半部分身份漂移回了原人物(出现原女孩的脸/白背心)。可能原因与对策:
denoise 0.7 + references 同时挂源片段 → 源身份渗回。 → 续接段建议去掉原源片段 reference,只保留 identity 角色图 + 由 previous_video 提供动作。ref_image_size: match),或换用 motion_context(latent 级续接,不经过像素参考)。project run --segment <ID> 单段重跑,确认后再 merge —— 避免整条重来。docs/long-video.md(分段编排与验收)docs/motion-context-long-video.md(Motion Context 安装与项目合同)scripts/long_video/example-manifest.json / manifest.schema.jsonskills/h3-character-migration/SKILL.md(含"运行时 prompt 不得预写媒体标签"这条硬约束)做 15.7s 长视频时 project run/wait/merge 连续失败两次,改用单段独立生成 + ffmpeg 拼接一次成功。
坑 A:提交超时误判("假失败") 段状态 = failed / generation submission failed / prompt_id: None,但 ComfyUI 日志明确写着 Prompt executed in 473.55 seconds,产物也已落盘。 根因在 server/app.py:423:
except Exception:
job.update({"status": "failed", "message": "generation submission failed", "error_code": "internal_error"})
即 comfy_tasks.schedule() 在等待 ComfyUI 的提交响应时抛异常 → 判失败;而 ComfyUI 其实收到并跑完了。
POST /prompt 响应太慢。ComfyUI/output/h3-studio/videos/<job_id>_00001_.mp4 手动捞回(本次段 0 就是这么救回来的)。坑 B:链路脆弱 project 里各段由服务端串成一条链,中间任一段失败整条链就断;而本机每段失败常伴随一次 GPU 挂死,重试成本很高。
# ① 每段独立生成:同角色图 + 固定 seed 保证身份一致;段与段之间留冷却时间
h3ctl generate video --mode rv2v --prompt-file P.txt \
--ref hero_turnaround.jpg --source-video segN_480x864.mp4 \
--duration 5.17 --aspect-ratio 9:16 --steps 4 --seed <固定值> \
--wait --wait-timeout 40m --download segN_out.mp4
# ② 无损直拼(各段编码参数一致时可 -c copy)
cat > concat.txt <<'EOF'
file 'seg1_out.mp4'
file 'seg2_out.mp4'
file 'seg3_out.mp4'
EOF
ffmpeg -f concat -safe 0 -i concat.txt -c copy final.mp4
2026-09-14 一晚发生 4 次硬件级故障(19:43 整机断电、19:57 / 22:35 / 04:43 三次 GPU 掉总线 GSP 挂死), 全部发生在 H3 高负载下(显存峰值 14.2~14.5GB / 16.3GB),SageAttention 全程关闭、nvrtc 无报错。 建议:单段任务之间留冷却;避免满档 362 帧(实测直接 OOM);优先排查 PSU / 散热 / PCIe 链路。
--duration 会被向上对齐到 17k+5 帧网格踩坑实录:三段各传 --duration 5.17(期望 124 帧 = 5.17s),H3 实际生成 141 帧 = 5.875s (向上对齐到下一个合法网格值)。三段拼接 = 423 帧 = 17.625s,而源内容只有 15.7s,凭空多出约 2 秒。 我当时只看了 nb_frames=423 却没换算成时长,误报成 15.5s。
关键事实:
17k+5:5, 22, 39, 56, 73, 90, 107, 124, 141, 158, 175, 192 … 362--duration 不是精确值,会向上取到最近合法帧数规矩:
ffprobe 核对实际 nb_frames / duration,绝不用"理论每段时长 × 段数"估算;ffmpeg -y -i seg.mp4 -t <秒> -c:v libx264 -crf 18 -c:a aac seg_trim.mp4generate video --mode rv2v 不会复制源音轨踩坑:用 rv2v 分段生成后,成品只剩提示词 overall_soundscape 里描述的生成环境音, 源视频的原唱 / 原声被丢掉了。该模式的 workflow evidence 明确写着 unsupported_audio_modes: ["source","mute"] —— 它只能生成音频,无法沿用源音轨。
| 命令 | 音频行为 |
|---|---|
h3ctl video migrate-character | 默认 --audio copy-source,直接复用源音轨 ✅ |
h3ctl generate video --mode rv2v | 只能生成音频(source / mute 不支持)❌ |
修法:按段贴回源音轨(保证音画同步)
# 每段:自己的视频(已裁到目标帧数) + 源视频对应时间段的音频
ffmpeg -y -i seg0.mp4 -ss 0 -t 5.1667 -i source.mp4 -c:v copy -map 0:v:0 -map 1:a:0 -shortest a0.mp4
ffmpeg -y -i seg2.mp4 -ss 5.2 -t 5.1667 -i source.mp4 -c:v copy -map 0:v:0 -map 1:a:0 -shortest a2.mp4
ffmpeg -y -i seg3.mp4 -ss 10.4 -t 5.1667 -i source.mp4 -c:v copy -map 0:v:0 -map 1:a:0 -shortest a3.mp4
# 再拼接
ffmpeg -y -f concat -safe 0 -i concat.txt -c copy final.mp4
校验贴回是否成功:用响度对比,二者接近即说明拿到的是源音轨
ffmpeg -i source.mp4 -af volumedetect -f null - 2>&1 | grep mean_volume
ffmpeg -i final.mp4 -af volumedetect -f null - 2>&1 | grep mean_volume
(本次实测:源 mean −7.7 dB / max 0.0 dB,成品 mean −7.9 dB / max 0.0 dB → 一致 ✅)