先不要急着去调整 LibTV Agent 的口型参数。配音和画面之所以对不上,多数情况是因为音频采样率和视频帧率这两个基础参数没有统一,或者生成顺序把节奏定在了一条错误的时间线上。建议先把导出素材用 ffprobe 检查一遍,再把偏移量放到剪辑时间线上测量清楚,然后判断这是整段整体位移,还是每句逐渐累积的漂移。前者通常只需要修正起始时间和重采样就能处理,后者要回到句子切分和生成顺序上解决。
适用场景:LibTV Agent 导出的数字人视频,声音和口型整段或某几句对不齐。操作动作:先用 ffprobe 核对音频采样率、视频帧率与容器时间基,再在时间线上测量偏移量。验证方式:看每句偏移量是否恒定;恒定就按整体位移处理,逐句递增则按时间基准或断句问题处理。风险边界:统一这些参数只能排除基础错位,如果语音断句本身就和镜头脚本不一致,仍需重切句子并重新生成。
用 ffprobe 查看音频采样率和视频帧率
ffprobe 是排查的起点,因为它能把“猜”变成“看”。分别查询音频流和视频流,不要只看封装格式;mp4、mov 只说明容器,不说明内部参数。把下面两条命令在导出文件上执行一遍,原始配音文件和成品视频都要查。
# 音频流参数
ffprobe -v error -select_streams a:0 \
-show_entries stream=codec_name,sample_rate,channels,time_base,duration \
-of default=noprint_wrappers=1 input.mp4
# 视频流参数
ffprobe -v error -select_streams v:0 \
-show_entries stream=codec_name,r_frame_rate,avg_frame_rate,time_base,nb_frames,duration \
-of default=noprint_wrappers=1 result.mp4
字段含义可以这样理解:sample_rate 是音频每秒采样点数,常见 48000 和 44100;r_frame_rate 是标称帧率,形如 25/1;avg_frame_rate 是平均帧率,两个值不一致通常说明是可变帧率;time_base 是时间基,例如 1/12800 表示时间戳以 1/12800 秒为单位;duration 用来交叉核对音视频时长是否一致。
- 音频采样率:配音、背景音和 LibTV Agent 输出建议统一用 48000 或 44100 中的一种,不要混用。采样率不一致时,部分工具会按错误速率解释音频,听感上音调偏移,画面上表现为口型整体提前或滞后。
- 视频帧率:优先使用恒定帧率,判断标准是 r_frame_rate 与 avg_frame_rate 输出相同;混用 25/1 与 30000/1001 这类接近但不相等的帧率,逐句漂移的概率会明显变高。
- 时长对齐:音频 duration 与视频 duration 的差值,建议控制在一帧以内,例如 25fps 下约 40ms;差值远超一帧时,先处理时长差异,再谈口型。
- 需要更细的定位时,可以加 -show_frames 查看音频或视频帧的 pts_time,确认第一帧起始时间是否从 0 开始。
对比 LibTV Agent 生成口型的时间线偏移
参数确认之后,下一步是测量偏移。把导出视频放进剪辑软件的时间线,同时放入原始配音波形,选中第一句、中间一句、最后一句三个采样点,逐帧步进找到口型开始张开的时刻,和音频波形上对应字头的位置做差。记录的是“差值”,不是“感觉快了一点”。
| 采样句 | 音频起始时间码 | 口型起始时间码 | 偏移量 | 方向 |
|---|---|---|---|---|
| 第 1 句 | 00:00:01:05 | 00:00:01:10 | 5 帧 | 口型滞后 |
| 中间句 | 00:00:18:12 | 00:00:18:17 | 5 帧 | 口型滞后 |
| 最后一句 | 00:00:42:03 | 00:00:42:08 | 5 帧 | 口型滞后 |
判断规则可以按这个顺序:三个采样点偏移量基本相同,属于整体位移,一般来自起始时间差、采样率解释不一致或合成时的时间偏移;偏移量随句数递增,属于逐句漂移,通常和帧率不恒定、音频时长被拉伸、句子边界跨镜头有关;偏移量忽大忽小且没有规律,往往是个别句子断句错误,需要回到第 3 节处理。偏移量统一用帧或毫秒记录,同一批素材用同一种单位,便于横向比较。
把配音按句子切分并重新对齐镜头
逐句漂移的根因多在句子边界。整段音频一次性驱动口型时,任何一处断句误差都会往后累积;按句切分后,每句都有自己的对齐起点,误差被限制在单句内。切分点建议落在句间静音段,先看波形找静音,再定切点,不要切在字中间。
# 按句切分,start/end 用秒或时间码换算
ffmpeg -i voice.wav -af atrim=start=3.120:end=7.480,asetpts=PTS-STARTPTS \
-ar 48000 -ac 1 line_03.wav
切分后重新整理一份对齐表,把句子编号、句子切分点、对应镜头编号和对齐后的时间码写在同一个表里,交给 LibTV Agent 逐句生成或逐段驱动口型。同一镜头内可以合并成一段,跨镜头的句子建议拆开,避免一句话的口型被拉进两个镜头。
| 句子编号 | 切分点后缀 | 镜头编号 | 对齐后起始时间码 |
|---|---|---|---|
| S03 | line_03.wav | SH02 | 00:00:14:00 |
| S04 | line_04.wav | SH02 | 00:00:18:06 |
| S05 | line_05.wav | SH03 | 00:00:23:11 |
调整生成顺序先音频后视频
先出画面再补配音,等于让口型去追一段后来才确定节奏的音频,错位几乎是必然的。建议调整为:先定稿配音并完成切分,再让 LibTV Agent 基于每句音频生成口型或数字人片段,最后统一合成。如果当前工程已经是先出画面,可以只重跑受影响的句子,不必整片重生成,但重跑顺序仍要按音频在前。
- 把定稿配音按句子切分,确认每句采样率、声道一致,输出待用音频。
- 在 LibTV Agent 中导入音频并建立句子与镜头的对应关系,先生成或先驱动口型,再确认单句时长。
- 基于单句口型结果再合成镜头,避免用长音频驱动长镜头。
- 导出前在预览里逐句核对,重点是句子切换和镜头切换这两个位置。
- 预览检查点一:每个镜头的第一句,口型开口是否和音频字头同时出现。
- 预览检查点二:每句结尾,口型是否在声音结束后闭合并停住,而不是继续动。
- 预览检查点三:镜头切换处,是否存在上一句尾巴被切掉、或下一句提前进入的情况。
导出后逐句检查口型同步
重新生成后必须再查一遍,因为切分和对齐本身可能引入新的偏移。检查方式可以沿用第 2 节的逐帧对比,但改成对每一句都记录,而不是抽样。偏差在半帧以内可以视为通过,例如 25fps 下约 20ms;超过一帧建议回到对应句子重新对齐,不要靠整体平移掩盖局部误差。
| 句子编号 | 起始时间码 | 口型偏差 | 是否通过 |
|---|---|---|---|
| S01 | 00:00:01:00 | 0 帧 | 通过 |
| S02 | 00:00:06:12 | 1 帧 | 需复查,确认是否为软辅音起点 |
| S03 | 00:00:14:00 | 0 帧 | 通过 |
| S04 | 00:00:18:06 | 2 帧 | 不通过,重新对齐后重导出 |
整轮检查结束后,再对成品跑一次第 1 节的 ffprobe,确认调整过程中没有把采样率或帧率改回去。参数一致、偏移量恒定、逐句偏差都在半帧以内,这三条同时成立时,才算把配音和口型对齐这件事处理完。