Kotonia
ログイン今すぐ始める

Kotonia Articles

一行创意到40秒喜剧视频:10-beat全自动流水线

面向本地GPU开发者的AI视频喜剧量产方案。记录从一行创意,经Gemma 4、HiDream、LTX-2等模型,全自动生成40秒竖屏视频(含Hook和字幕)的10-beat流水线设计、陷阱与代码结构。

作者 4分钟阅读
#AI#视频生成#ffmpeg#LTX#HiDream#流水线
其他语言日语

TL;DR

一行创意由 Gemma 4 31B 展开为10-beat结构,HiDream 生成11张2048²图像,LTX-2 A2V/I2V 生成11个片段,Irodori-TTS 生成台词和男声旁白,ffmpeg 自动烧录字幕和Hook标题覆盖。实测25-30分钟生成40秒竖屏 (512×768) 视频。全部在单张本地GPU (96 GB Blackwell) 上运行,API成本为零。

完成版 (已发布):

@youtube

本文目标读者

面向希望在本地GPU上量产AI视频喜剧的个人开发者。本文关注点并非单个模型,而是 “将多个模型连接成一条流水线并投入运营的设计”

做了什么

将面向Z世代的黑暗喜剧讽刺(以 No Means Yes? 这一性同意困境为素材的海外短视频格式)从一行创意自动化到40秒视频。

完成效果:

  • Hook (0-5秒): 美女特写 + 旁白“回应了‘你是男孩子吧’的他,其命运——” + 大标题“No Means Yes?”
  • 正片 (5-37秒): 电影院约会 → “可以亲你吗?”→ “不要…不行啦…”→ 沮丧 → “再主动一点嘛。你是男孩子吧?”→ 原来如此 → 接吻
  • 结局 (37-40秒): 法庭“主文:被告人因不同意性交等罪,判处有期徒刑3年”+ 法槌“咚!”+ 牢房落泪

before / after:

传统制作方式本流水线
创意 → 发布视频2-3天 (手动编辑)25-30分钟 (全自动)
API 成本DALL-E + 视频生成每条数百日元0元 (仅电费)
字幕手动编写SRT按句号自动分割并烧录
Hook单独拍摄集成到流水线中

架构

[阶段 A] Gemma 4 31B (vllm, 端口 8894) → plan.json (10 beats + hook)
[阶段 B] HiDream-O1-Image (端口 8895) → 11 张 2048² 图像
          + Gemma 4 31B 多模态视觉评判 (--judge --max-retries 2)
[阶段 C] Irodori-TTS (端口 8880) + LTX-2 A2V (端口 8892) / I2V (端口 8891)
          → 11 个片段 + Hook 片段 → ffmpeg 拼接 → 字幕烧录

实现集中在 llm_server/storyboard/ 目录下 (pipeline.py / visual.py / judge.py / video.py / render.py / run.py)。

prompts.pyCONSENT_DILEMMA_SYSTEM 中作为系统提示固定:

#类型说话者渲染器内容
1挑衅bLTX-2 A2V意味深长的邀请
2询问aLTX-2 A2V认真的同意确认
3拒绝bLTX-2 A2V含蓄的拒绝 (如“不要…不行啦…”的模糊形式)
4沮丧a (沉默)LTX-2 I2V沮丧
5煤气灯效应bLTX-2 A2V矛盾的引导性发言
6停顿a (沉默)LTX-2 I2V短暂的醒悟
7接吻a (沉默)LTX-2 I2V男方亲吻的瞬间
8判决法官LTX-2 A2V快速宣判
9法槌音效法官LTX-2 I2V (保留音频)法槌 + AI 自动“咚!”音
10牢房a (沉默)LTX-2 I2V牢房落泪

关键在于 3 次反转:

  1. 拒绝不采用平淡的“不行”: 使用“不要…不行啦…”并拉长尾音,营造出表演性的“No that doesn't mean No”的微妙感。这是后续煤气灯效应矛盾成立的前提。
  2. 煤气灯效应后不立即接吻: 插入“停顿”(原来如此) 1.5秒。为了节奏和情感曲线。
  3. 判决与牢房的两段式结局: 仅判决会显得突兀。加上牢房哭泣的画面,能让观众切实感受到“他真的被判有罪了”。

Hook 的设计 (TikTok 前3秒问题)

竖屏短视频的前3秒决定了流失率。在正片10 beats之前放置Hook片段:

"hook": {
  "title_overlay": "No Means Yes?",
  "narrator_line": "回应了‘你是男孩子吧’的他,其命运——",
  "image_prompt": "ultra close-up of beautiful Japanese woman, half-lidded eyes, ...",
  "duration_sec": 3.5
}

实现中的两个陷阱:

陷阱 1: 旁白 TTS 时长超过 duration_sec 会导致音频被截断。“其命运”的“运”字处发生了音频截断。对策: 先生成 TTS → 用 ffprobe 实测时长 → 将 max(plan_duration, narrator + 0.6) 传递给 I2V 时长。

narrator_dur = _ffprobe_duration(narrator_wav)
duration = max(float(hook.get("duration_sec", 0.0)), narrator_dur + 0.6)
ltx_i2v_clip(portrait, i2v_prompt, duration, silent_video, keep_audio=False)

陷阱 2: drawtext 的 y 位置。使用 y=h*0.30 (画面1/3高度) 会与脸部重叠。使用 y=20 (绝对20像素) 将其置于最顶部。

字幕烧录 (适配静音观看)

为了应对在电车上关掉声音观看的用户,并提高跨平台可靠性,采用内嵌字幕。

style = (
    "FontName=Noto Sans CJK JP,FontSize=18,PrimaryColour=&H00FFFFFF,"
    "OutlineColour=&H00000000,Outline=2,Shadow=0,BorderStyle=1,"
    "Alignment=2,MarginV=60,Bold=1"
)
# ffmpeg -i raw.mp4 -vf "subtitles=subs.srt:force_style='..."

Alignment=2 = 底部居中。MarginV=60 确保与底部边缘保持距离。

长句分割: 单个 beat 内若有超过30个字符的行会与脸部重叠。使用 _split_subtitle 按句号 。.!? 分割句子 → 使用贪心算法分割成28字符以下的块 → 按 beat 时长均匀分配:

输入:

用语言确认什么的,一点也不浪漫嘛。喂,再主动一点啊。你是男孩子吧?

输出 (将一个8.9秒的 beat 分割为2个时间块):

时间字幕
15.16-19.63秒用语言确认什么的,一点也不浪漫嘛。
19.63-24.10秒喂,再主动一点啊。你是男孩子吧?

使用 LTX-2 I2V 生成音效 (法槌音效)

LTX-2 distilled 的 I2V 输出 mp4 会 自动包含 AI 生成的音频 (环境音/音效)。除非使用 ffmpeg -map 0:v:0 -map 1:a:0 明确丢弃,否则音频会随提示词一起生成。

将其用作音效生成器:

def render_se_tail_beat(sb_dir, beat, prior_clip, work_dir):
    # 1. 提取前一个 beat 的最后一帧
    extract_last_frame(prior_clip, last_frame_png)
    # 2. 将该图像输入 I2V,通过提示词要求生成音效
    prompt = build_gavel_se_prompt(beat)
    return ltx_i2v_clip(last_frame_png, prompt, duration, clip_path, keep_audio=True)

ltx_i2v_clip 添加了 keep_audio=True 标志,创建了一条在 ffmpeg 重新编码时不丢弃音频的路径。

法槌音效的提示词:

"Single decisive arm motion of the judge bringing the gavel down sharply "
"onto the wooden bench. Loud sharp wood-on-wood thwack impact sound. "
"Brief, contained, no other motion in the frame."

使用法官的最后一帧 + 法槌提示词即可生成“咚!”声。如果失败,则设计为回退到《逆转裁判》等音效。

陷阱列表

开发过程中踩到的主要5个陷阱:

1. Codex CLI 在 vLLM 0.20.2 上挂起

使用 codex exec -p gemma4 发送系统提示 + 创意时,在 /v1/responses 的握手阶段以0% CPU 状态挂起超过20分钟。由于子进程输出通过 tail -200 管道传输,初始的 stderr 也被阻塞。

规避: 放弃使用 codex,直接使用 urllib.request 调用 /v1/chat/completions。使用 response_format={"type":"json_object"} 强制 JSON 输出。25秒内完成 plan.json 生成。

2. HiDream 无法消除电影屏幕

即使在 setting_prompt 中明确指定 "The movie screen is BEHIND the camera and NOT VISIBLE in frame",在2048/50步下屏幕仍会留在背景中。

规避: 使用 t2i 生成 scene_base → 将同一图像输入 I2I 编辑,并提示“将屏幕替换为暗墙,角色位置不变”→ 一次消除。采用低分辨率 → I2I 修复 → 提高分辨率并重新生成所有 beat 的两步法。

3. HiDream 将嘴唇对嘴唇的亲吻变成脸颊亲吻

在标准放大器下,HiDream 倾向于将亲吻解释为脸颊亲吻。必须使用 "CRITICAL: their LIPS meet directly — mouth-to-mouth contact at the CENTER of the frame. NOT a cheek kiss" 级别的强指令。在 _beat_edit_prompt 中为亲吻 beat 准备了专用的提前返回块。

4. CAST / CROP_BOX / SPEAKER_A2V_PROMPT 硬编码为两人

CAST 字典 / CROP_BOX 字典 / SPEAKER_A2V_PROMPT 字典仅知道 a (健太) 和 b (美咲),分布在3个位置。若要添加法官/旁白,需要同时更新3个字典 (通过 KeyError 发现)。对于具有 setting_override 的 beat,render_speech_beat_ltx_a2v 也添加了分支,使其从 beat 自身的图像而非 scene_base 进行肖像裁剪。

5. Gemma 4 多模态评判器误报过多

storyboard/judge.py 中实现了视觉评判器,将 beat 图像 + 预期表情发送给 Gemma 4 31B 进行 YES/NO 判定。确实能捕获“手指数量异常”、“张嘴对话姿势”、“场景几何不匹配”等 明显 失败,但在“微妙害羞表情”等模糊领域会频繁返回 FAIL。

实用策略: 设置 max-retries 为2,若连续3次 FAIL 则接受并继续。切换到前沿审查器 (Gemini 3.1 Pro) 的阈值尚未自动化。

VRAM 共存设计

96 GB Blackwell Max-Q 的分配:

进程空闲 (GiB)峰值 (GiB)
Gemma 4 31B (NVFP4)3838
HiDream-O1-Image1633
TTS 服务器33
Ditto33
LTX-2 A2V (冷启动 fp8-cast)124
LTX-2 T2V/I2V (冷启动)18

全部峰值合计 109 GiB → 内存不足。运行流程:

  1. 阶段 A: Gemma 31B + HiDream 空闲,峰值约 62 GiB
  2. 带评判的阶段 B: Gemma 31B + HiDream 峰值约 73 GiB
  3. 定稿前 pkill -f "vllm.*gemma" 杀死 Gemma → 释放 38 GiB
  4. 阶段 B 定稿 (2048/50): HiDream 峰值约 33 GiB
  5. 阶段 C 前 lsof -ti tcp:8895 | xargs kill 杀死 HiDream → 释放 16 GiB
  6. 阶段 C: LTX-2 + TTS + Ditto 峰值约 32 GiB

只需在阶段切换时显式杀死进程,即可将所有步骤容纳在一张卡上。

迭代循环 (缓存策略)

部分重新生成而非“全部重做”是加速的关键:

# 仅重新生成1个 beat 的图像 (仅 HiDream)
python -m storyboard.visual --plan ... --out ... --only-beat 7 --steps 50 --resolution 2048

# 部分视频重新生成 (TTS + LTX-2)
python -m storyboard.video --dir ... --regen-beats 5,6,7 --skip-review

# 仅调整字幕或 Hook 标题位置
rm _video_work/clip_00_hook.mp4 _video_work/subs_irodori.srt
python -m storyboard.video --dir ... --regen-beats none --skip-review   # ~30 秒

缓存层级:

  • HiDream beat 图像 (beat_NN_<type>.png) — 使用 --only-beat 单独处理,80秒
  • A2V / I2V 片段 (clip_NN_*.mp4) — 当 beat 类型/说话者/台词变更时失效
  • Hook 完成版 (clip_00_hook.mp4) — 仅想更改标题位置时删除此文件即可 (可重用 LTX-2 I2V 的 hook_silent.mp4)
  • 字幕 SRT — 每次重新生成 (10秒)

标题位置/字幕样式/Hook 内文案调整可在30秒内重新烧录。LTX-2 I2V 的100秒部分可直接复用。

在 Kotonia 中的运用

此流水线生成的视频面向社交媒体分发 (TikTok / YouTube Shorts / IG Reels),是 Kotonia (kotonia.ai) 获取注意力并引导付费的上游环节。

技术上,这是对 /studio/ (HiDream 图像生成) 相同栈向视频方向的扩展。未来计划作为 /video-studio/,通过 Web UI 一键调用相同的流水线 (目前仅 CLI)。

相关文章 / 给想尝试的人

Kotonia 将语音 AI、AI 聊天、图像生成和团队协作整合到一个 AI 工作区中。

试用 Kotonia