做商品展示和口播视频,一条 30 秒的片子换一次文案就得整条重录,主播档期、商品图更新、字幕调整全卡在一起,剪辑一轮半天就没了。
常规做法要么依赖模板剪辑软件,结构改不动;要么把写脚本、TTS、数字人、剪辑拆成四个互不相通的工具,中间靠手工导出导入,换一个商品基本要从头再来。
本文分享一套以 Codex 为编排核心的成片方案:分镜 JSON 契约 + 数字人驱动 + ffmpeg 合成校验,可直接落地。
一、链路切分:把成片拆成四段可复跑流水线
一条口播带货片先按可独立复跑的四段切开:
| 环节 | 输入 | 产出 | 关键约束 |
|---|---|---|---|
| 脚本分镜 | 商品卖点表 | storyboard.json | 单镜口播 ≤ 28 字 |
| 数字人驱动 | 口播稿 + 形象图 | 口播片段 | 帧率统一 25fps |
| 素材拼装 | 商品图 + B-roll | 镜头序列 | 转场时长一致 |
| 合成校验 | 片段 + SRT | 成片 MP4 | 字幕按实测时长对齐 |
- 环节解耦:每段只认输入文件和产出文件,任意一段失败可单独重跑,不动上游。
- 契约固定:段与段之间只传 JSON、WAV、MP4 三种文件,避免工具私有格式互相纠缠。
核心结论: 流水线切成四段后,改文案只重跑第一段,换主播只重跑第二段。

二、分镜脚本:Codex 按卖点产出结构化口播
把商品信息交给 Codex 之前,先固定输入与输出契约:
- 输入封装:商品名、卖点、售价、适用人群四个字段先落成
product.json,避免每次临时手写提示词。 - 输出契约:要求 Codex 只返回固定字段的 JSON 数组,解析失败直接触发重试,绝不让自由文本流进下游。
下面这段脚本读取商品 JSON,调用 codex exec 生成分镜并落盘,适配已装好 Codex CLI 的环境:
import json, subprocess, pathlib
# 读取商品卖点,交给 Codex 生成分镜脚本并写入 storyboard.json
product = json.loads(pathlib.Path("product.json").read_text(encoding="utf-8"))
prompt = f"""你是短视频编导,为下面的商品写 5 镜带货口播。
商品:{json.dumps(product, ensure_ascii=False)}
只输出 JSON 数组,字段固定为 shot/desc/line/dur,单镜口播不超过 28 字,总时长 30 秒。
"""
res = subprocess.run(["codex", "exec", prompt], capture_output=True, text=True, check=True)
story = json.loads(res.stdout[res.stdout.index("["):res.stdout.rindex("]") + 1])
pathlib.Path("storyboard.json").write_text(
json.dumps(story, ensure_ascii=False, indent=2), encoding="utf-8")
print(f"共 {len(story)} 镜,合计 {sum(s['dur'] for s in story)} 秒")
商品表 → 契约化提示词 → 结构化分镜 → 落盘复用
三、素材驱动:数字人片段与商品镜头拼装
分镜确认后进入素材生产,这一步要同时产出三类文件:
- 旁白与口型:逐镜调用 TTS 得到
lineN.wav,再用口型驱动模型生成faceN.mp4,两者必须同源同长。 - 商品图推镜:静态商品图用
zoompan做缓慢推近,补足画面运动,避免整段是死图。
下面这段脚本跑通「旁白 → 口型 → 推镜 → 拼接」四步,适配装有 ffmpeg 与 TTS 工具的 Linux 环境:
# 逐镜生成旁白、驱动数字人、给商品图加推镜,最后左右分屏合成 clipN.mp4
n=1
for line in $(jq -r '.[].line' storyboard.json); do
tts-cli --voice xiaoxiao --text "$line" -o "line$n.wav"
python wav2lip.py --face avatar.png --audio "line$n.wav" --fps 25 --out "face$n.mp4"
ffmpeg -y -loop 1 -i "p$n.png" -t 6 -r 25 \
-vf "scale=1080:1920,zoompan=z='min(zoom+0.0015,1.12)':d=150" "img$n.mp4"
ffmpeg -y -i "face$n.mp4" -i "img$n.mp4" -filter_complex \
"[0:v]scale=540:1920[a];[1:v]scale=540:1920[b];[a][b]hstack[t];[t]fade=t=out:st=5.6:d=0.4[v]" \
-map "[v]" -map 0:a "clip$n.mp4"
n=$((n+1))
done
同源音频 → 同帧率口型 → 同尺寸分屏 → 逐镜可替换

四、合成校验:字幕时长与音轨的对齐
片段齐了再谈合成,先把时间轴测准:
- 实测代替估算:字幕块的入点出点来自
ffprobe实测片段时长,而不是分镜里写的预估秒数,否则字幕会整体漂移。 - 先校验后封装:总时长对不上就直接报错中断,别把错误带进最终压制。
下面这段脚本按分镜生成 SRT,并核对片段实测总时长,适配 Python 标准库 + ffmpeg 环境:
import json, subprocess
from pathlib import Path
def dur(p): # 实测片段时长
r = subprocess.run(["ffprobe", "-v", "quiet", "-print_format", "json",
"-show_format", str(p)], capture_output=True, text=True, check=True)
return float(json.loads(r.stdout)["format"]["duration"])
def ts(s):
h, rem = divmod(s, 3600); m, sec = divmod(rem, 60)
return f"{int(h):02d}:{int(m):02d}:{sec:06.3f}".replace(".", ",")
story = json.loads(Path("storyboard.json").read_text(encoding="utf-8"))
real = sum(dur(p) for p in sorted(Path(".").glob("clip*.mp4")))
blocks, t = [], 0.0
for i, s in enumerate(story, 1):
blocks.append(f"{i}\n{ts(t)} --> {ts(t + s['dur'])}\n{s['line']}\n")
t += s["dur"]
assert abs(t - real) < 0.4, f"时长不一致 预估{t:.2f}s 实测{real:.2f}s"
Path("final.srt").write_text("\n".join(blocks), encoding="utf-8")
print(f"字幕块 {len(blocks)},实测总长 {real:.2f}s")
实测时长 → 断言校验 → 生成字幕 → 一次性压制
五、排错清单:四类高频翻车与治理对照
下面四条是跑通全链路后最常见的翻车点:
| 现象 | 原因 | 治理 |
|---|---|---|
| 口型与语音错拍 | 各环节帧率不统一 | 全链路锁定 25fps 再驱动 |
| 字幕整体提前半秒 | 用预估时长而非实测 | 用 ffprobe 实测每段时长 |
| 成片音量忽大忽小 | 旁白与 BGM 未归一 | loudnorm 两段式响度归一 |
| 镜头切换处闪黑 | 片段短于转场时长 | 尾部补帧长度 ≥ 转场时长 |
问题: 音画不齐、字幕漂移、切换闪黑,本质都是「各段各自为政」。
治理: 统一帧率、实测时长、锁定响度,把约束写进每段的出口检查。
核心结论: 90% 的成片翻车不是模型不行,而是缺一道出口校验。

六、批量复跑:多商品参数化生成成片
单品跑通之后,把变量抽到配置里就能批量复跑:
- 参数外置:商品 ID、形象图路径、背景音乐、投放尺寸(9:16 / 1:1)集中放一份
batch.yaml,脚本只读配置不写死。 - 失败重试:单个商品失败只标红该行并继续,跑完统一汇总失败清单,第二轮只补失败项。
商品表 → Codex 批量分镜 → 逐镜驱动 → 合成校验 → 归档发布
核心结论: 一条链路的价值不在生成一次,而在第 100 个商品时仍然一键复跑。
结语
这套流水线把数字人带货视频从「剪辑手艺」变成了「可复跑的工程」:分镜交给 Codex 按契约产出,素材驱动与拼装交给脚本,合成前用实测时长和断言把关,任何一段出问题都能单独重跑而不动全局。
换商品、换主播、换尺寸都只是改一行配置,团队真正省下的是每条片子反复对齐音画与字幕的人力,可以更多放在选品和卖点打磨上。
把约束写进流水线,视频生成才从演示变成产能。
转载自 CSDN-专业IT技术社区
原文链接:https://blog.csdn.net/2301_78967866/article/details/168550087




