H3长视频分段合成方法-20260914.md

H3 长视频分段合成方法(2026-09-14 实测跑通)

目标:把 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 断点续采互斥,不能同时开。


二、三步操作

1) 上传素材,拿 32 位 asset_id

h3ctl asset upload <文件> --output json        # asset 按内容哈希复用,重复上传返回同一 id
# 返回 data.uploads[0].result.asset.asset_id

2) 写 manifest(严格 v1 schema,拒绝未知字段)

完整示例(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 硬性约束:

3) 跑(两种等价路径)

# 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

三、本次踩到的 3 个坑(都在 create 阶段暴露)

坑 1:派生引用项目禁止字面媒体标签 ⭐ 最关键

{"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>:

这也解释了为什么 h3ctl generate video(直接生成)能用 <Picture 1>,而走 project 的路径全都卡在 create。

坑 2: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。

坑 3:dry-run 通过 ≠ 服务端通过

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)
profileminimax-h3-ref2va v1.3
输出768×1344 / 24fps / 372 帧 / 15.5s
总耗时约 37 分钟(21:15 → 21:52,含每段重新加载 13GB 模型)
显存峰值14480 MiB / 16311 MiB(余量很小)
成本0 积分(本地)

注意:三段串行比单段线性叠加更慢(每段都要重载模型 + 续接段要额外吃上一段视频作参考)。


五、待调优:身份漂移

本次成品最后一段的后半部分身份漂移回了原人物(出现原女孩的脸/白背心)。可能原因与对策:

  1. 段2 denoise 0.7 + references 同时挂源片段 → 源身份渗回。 → 续接段建议去掉原源片段 reference,只保留 identity 角色图 + 由 previous_video 提供动作。
  2. 续接段可提高角色图权重(目前 ref_image_size: match),或换用 motion_context(latent 级续接,不经过像素参考)。
  3. 逐段复核:先用 project run --segment <ID> 单段重跑,确认后再 merge —— 避免整条重来。

六、相关文档


七、实战经验:project 编排不稳时的替代路径(2026-09-14 实测跑通)

做 15.7s 长视频时 project run/wait/merge 连续失败两次,改用单段独立生成 + ffmpeg 拼接一次成功。

7.1 project 流程的两个坑

坑 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 其实收到并跑完了。

坑 B:链路脆弱 project 里各段由服务端串成一条链,中间任一段失败整条链就断;而本机每段失败常伴随一次 GPU 挂死,重试成本很高。

7.2 替代路径(本机推荐)

# ① 每段独立生成:同角色图 + 固定 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

7.3 本机负载红线

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 链路。

7.4 ⚠️ 时长坑:--duration 会被向上对齐到 17k+5 帧网格

踩坑实录:三段各传 --duration 5.17(期望 124 帧 = 5.17s),H3 实际生成 141 帧 = 5.875s (向上对齐到下一个合法网格值)。三段拼接 = 423 帧 = 17.625s,而源内容只有 15.7s,凭空多出约 2 秒。 我当时只看了 nb_frames=423 却没换算成时长,误报成 15.5s。

关键事实:

规矩:

  1. 拼接前必须用 ffprobe 核对实际 nb_frames / duration,绝不用"理论每段时长 × 段数"估算;
  2. 需要精确总时长时,拼接前把每段裁到目标帧数:
    ffmpeg -y -i seg.mp4 -t <秒> -c:v libx264 -crf 18 -c:a aac seg_trim.mp4
  3. 本次修正:每段裁到 124 帧(5.1667s)→ 3×124 = 372 帧 = 15.533s ✅

7.5 ⚠️ 音轨坑:generate 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 → 一致 ✅)

下载此文件