📍 复源AI社区 › 开源解码 › Claude Opus 5.5 做动画讲解视频全流程拆解:代码画帧原理、配音时间轴、无头浏览器渲染、响度校验与国内发布合规清单

Claude Opus 5.5 做动画讲解视频全流程拆解:代码画帧原理、配音时间轴、无头浏览器渲染、响度校验与国内发布合规清单

摘要:Claude Opus 5.5根本不会"吐"视频,它只是写一段动画程序,靠无头浏览器逐帧截图、FFmpeg压成MP4。这条链路想稳定出片,必须抓住四件事:画面只由时间决定、先配音再排画面、渲染可中断续跑、经验写进规则文件。文章按"新手会卡在哪"讲透原理与翻车点,附国内发布合规清单,值得一读。
字号 100%
行距 2.05
当前可见 60% 的内容
本文由 莫潇羽@复源AI社区(bbs.fuyuan7.com)撰写,转载请注明出处。

快速摘要

Claude Opus 5.5 并不会直接"吐"出一个视频文件。它做的事情是写一段网页动画程序,再由无头浏览器按时间一帧一帧截图,最后交给 FFmpeg 压成 MP4。所以它擅长的是讲解片、数据动画、动态图形这类"画出来"的内容,不擅长真人实拍质感。想让这条链路稳定出片,要抓住四件事:画面只由时间决定,同一时刻渲染两次必须一模一样;先配音,再按音频的实际时长排画面;整片渲染要能中断续跑,并且等到校验通过才算完成;智能体每次开工都从零开始,经验必须写进工作目录的规则文件。在国内平台发布,还要做好 AI 生成内容声明、声音克隆授权和素材正版授权这三项。

这篇文章我按"新手会卡在哪"的顺序来讲,不按制作步骤流水账。前半部分讲原理,后半部分讲翻车点和人机分工,最后给一份国内发布前可以逐条对照的合规清单。文中的代码都是最小可用的示意,能跑通,但你需要按自己的项目改。

你关心的问题

对应章节

它到底是不是"生成视频"

第一章

为什么画面要写成"时间的函数"

第二章

先做画面还是先配音

第三章

二维、三维、原件上屏怎么选

第四章

渲染到一半断了怎么办

第五章

声音忽大忽小、破音怎么查

第六章

智能体不记事,经验怎么沉淀

第七章

哪些环节必须人来把关

第八章

国内平台发布要注意什么

第九章

想看完整拆解,往下翻。

一、先把误会说清:它做的不是"生成视频",而是"写一段会画画的程序"

1.1 一条被误读的热点

2026年9月22日,Anthropic 发布了 Claude Opus 5.5,这是 5.5 系列的首发型号,同一天就能在它的命令行编程智能体 Claude Code 里使用。发布之后的一段时间,社交平台上冒出了很多"用一句话做出来"的宣传片、讲解片、音乐短片,看得人心里发痒。

我当时的第一反应也是:文本模型什么时候能出视频了?

翻了几份公开的技术拆解之后才弄明白,这是个误读。多方的结论是一致的:模型输出的仍然是文本,只不过这段文本是一份动画程序。程序交给浏览器去执行,浏览器把画面画出来,再被一张张截下来拼成视频。模型自己从头到尾没有产生过一个像素。

这里有三个词,新手第一次见可能会懵,我先用白话解释一下:

  • 帧:视频里的一张静止画面。每秒播放 30 张,就叫 30 帧每秒。
  • 无头浏览器:没有窗口的浏览器,由命令行或脚本驱动,网页照常渲染,只是你看不见它。
  • FFmpeg:一个开源的音视频处理工具,可以把一堆图片和一条音轨压成一个 MP4。

整条链路画出来是这样的:

一句题目或一页说明
        │
        ▼
编程智能体(读资料、写稿、写动画代码、调用工具)
        │  产出:scene.html,里面有一个 seek(t) 函数
        ▼
无头浏览器(逐帧调用 seek,逐帧截图)
        │  产出:000000.png、000001.png、000002.png ……
        ▼
FFmpeg(图片序列 + 混好的音轨 → 编码)
        │
        ▼
out.mp4(再跑一遍校验)

理解了这张图,后面所有的坑都好解释。因为每一个坑,几乎都出在某两个环节的交接处。

1.2 它适合拍什么,不适合拍什么

既然画面是代码画出来的,它的长处和短处就很清楚了。代码没有摄像机,也没有从海量影像里学来的"真实世界长什么样"的直觉,它只会精确地执行你描述的几何、颜色和运动。

内容类型

是否适合用代码画

原因

带旁白的科普讲解片

很适合

文字、图表、示意图可以精确控制,改一处只需改一行

数据动画、流程演示

很适合

数据驱动,图表与数字天然一致

产品功能演示、片头动效

适合

排版和节奏可复现

低多边形风格的三维场景

可以,但更费时间

几何简单时可控,复杂了渲染会变慢

真人出镜、自然口型

不适合

需要视频生成模型或实拍

要求真实光影和物理质感的镜头

不适合

代码画不出"拍出来"的感觉

业内也有混合做法:真实感镜头交给视频生成模型,字幕、图表、标注这些要求精确的部分用代码画,再合成到一起。这篇文章只讲纯代码画帧这一条路,因为它成本最可控,结果也最稳定。

1.3 "一句话出片"为什么是错觉

很多演示视频配的文案是"一个提示词搞定",但背后的工作量被藏起来了。有创作者公开记录过自己的过程:一部水彩风格的短片,前后调用模型一百六十多次,耗时接近七个小时,开工前口述的说明转成文字有好几千字,里面包含角色设定、逐场脚本、视觉参考和预算。

这才是真实的工作量分布。我自己折腾下来的体会是,一部几分钟的讲解片,真正花时间的地方按顺序是这几块:

  1. 说明文档:题目、要讲清楚的几个问题、硬性约束。
  2. 资料核实:每个日期、人名、数字从哪来。
  3. 逐字稿:写给耳朵听的稿子。
  4. 配音:切段、合成、核对。
  5. 画面代码:最长的一段。
  6. 渲染与校验:机器时间为主,但最容易出事故。

所谓"一句话",只是把第一项压缩成了一句话,后面五项一个都没少,只是由智能体代劳了。代劳得好不好,取决于你给它定的规则清不清楚。这一点第七章会展开。

1.4 动手前需要准备什么

组件

作用

说明

编程智能体

读写文件、执行命令、根据结果决定下一步

Claude Code 属于这一类;国内可用的同类命令行工具有 Qwen Code、Kimi Code CLI、CodeBuddy Code,以及 Trae 这样的 AI 编程环境

Node.js

运行抓帧脚本

用长期支持版本即可

Playwright

驱动无头浏览器的自动化库

微软开源

FFmpeg

编码、混音、校验

开源

语音合成服务

旁白配音

第三章细讲,国内有多家可选

中文字体

画面里的汉字

开源字体可用,商用字体需取得正版授权

这里要插一句合规提示:Claude Code 和 Opus 5.5 属于境外服务,本文对它们只做原理层面的科普,境外模型使用需遵守国内网络与内容管理相关规定。从工程角度看,"写动画代码、调浏览器、调 FFmpeg"这套方法并不依赖某一家的模型,国内的编程智能体同样可以按这个思路搭建,差别主要在长任务的稳定性和代码质量上。

至于费用,写稿时公开报道的 Opus 5.5 标准定价大约是每百万输入 token 4 美元、每百万输出 token 20 美元,比上一代有所下调。模型定价变动非常快,这只是写稿时的近似行情,请以官方定价页面的实时数据为准。

二、整条链路的地基:让每一帧只由"时间"决定

2.1 一个约定:给我一个时刻,我还你一张画面

如果整篇文章只能记住一句话,我希望是这一句:动画页面要对外暴露一个函数,输入一个时间,输出那一刻完整的画面。

这个函数通常叫 seek(t)。你告诉它"第 12.34 秒",它就把第 12.34 秒应该出现的所有东西画出来,不管上一次画的是第几秒,也不管电脑上的钟走到了哪里。

业内对这种做法有个很形象的概括,大意是"只定位,不播放"。老牌的编程式视频框架 Remotion 走的也是同一个思路:渲染时按帧号逐帧定位,截图,再交给 FFmpeg。有独立测试观察到,Opus 5.5 在没有特别要求时,会自己倾向于最朴素的方案,也就是一个 HTML 文件加一个 seek 函数加 Playwright 截图,哪怕项目里装着更完整的框架。朴素有朴素的好处,出了问题容易查。

2.2 为什么不能"边播边录"

新手最自然的想法是:让动画在浏览器里正常播放,同时录屏。这条路走不通,原因有两个。

一个原因是截图本身很慢。截一张 1920×1080 的图可能要几十到几百毫秒,三维场景更久。如果动画跟着真实时钟往前走,等你截完这一张,动画已经跑出去好几帧了,结果就是掉帧、卡顿。

另一个原因是不可复现。同一段动画录两次,每次掉的帧都不一样,你没法做"只重做第三段"这种局部修改,也没法验收。

换成逐帧定位之后,时间由脚本说了算。先算好要多少帧,再一帧一帧地问:

总帧数 = 片长(秒)× 帧率
第 n 帧对应的时刻 t = n ÷ 帧率

举例:一部 8 分钟的片子,帧率 30
总帧数 = 8 × 60 × 30 = 14400 帧
第 600 帧的时刻 = 600 ÷ 30 = 20 秒

截一张图花 0.1 秒还是 3 秒都无所谓,因为画面里的"时间"是脚本喂进去的数字,跟墙上的钟没有关系。

2.3 哪些东西会悄悄破坏确定性

约定听起来简单,执行起来处处是陷阱。下面这张表是我整理的常见破坏项,让智能体写画面代码之前,最好把它们写进规则里。

破坏项

为什么有问题

对策

Date.now()、performance.now()

读的是真实时钟,每次渲染都不同

画面代码里禁用,时间只从 seek 的参数来

requestAnimationFrame、setTimeout

按真实时间调度

不用它们驱动动画,或用虚拟时钟接管

CSS 的 transition 和 animation

浏览器按自己的时钟播放

被截取的元素不使用,改为按 t 计算样式

Math.random()

每次结果不同

换成带种子的伪随机函数

字体、图片尚未加载完

第一次截图和第二次截图不一样

开始抓帧前等待资源就绪

<video> 标签

无头环境下解码不稳定,容易丢帧

事先用 FFmpeg 把视频拆成图片序列再引用

依赖上一帧的状态

跳着渲染时结果会错

每一帧都从 t 重新算,不累加

最后一条最隐蔽。比如粒子效果,常见写法是"每帧让位置加上速度",这就依赖了上一帧。改成"位置 = 初始位置 + 速度 × t",才是只依赖时间的写法。

下面是一个最小的画面文件,演示怎么把标题淡入和背景星点都写成 t 的函数:

<!-- scene.html:最小示例,所有画面只由 t 决定 -->
<canvas id="c" width="1920" height="1080"></canvas>
<script>
const ctx = document.getElementById('c').getContext('2d');

// 带种子的伪随机:同一个 seed 永远返回同一个数
function rand(seed) {
  const x = Math.sin(seed * 12.9898) * 43758.5453;
  return x - Math.floor(x);
}
const clamp01 = (v) => Math.min(1, Math.max(0, v));

window.seek = function (t) {
  // 1. 每一帧都从空白画起,不依赖上一帧
  ctx.globalAlpha = 1;
  ctx.fillStyle = '#10141f';
  ctx.fillRect(0, 0, 1920, 1080);

  // 2. 背景星点:位置由种子决定,闪烁由 t 决定
  for (let i = 0; i < 60; i++) {
    const x = rand(i) * 1920;
    const y = rand(i + 500) * 1080;
    const phase = rand(i + 900) * 6.283;
    ctx.globalAlpha = 0.3 + 0.3 * Math.sin(t * 2 + phase);
    ctx.fillStyle = '#ffffff';
    ctx.fillRect(x, y, 3, 3);
  }

  // 3. 标题:第 1.0 秒开始,用 0.8 秒淡入并上移
  const p = clamp01((t - 1.0) / 0.8);
  const ease = 1 - Math.pow(1 - p, 3);
  ctx.globalAlpha = ease;
  ctx.fillStyle = '#f2e9d8';
  ctx.font = '72px "Noto Sans CJK SC", sans-serif';
  ctx.fillText('二十四节气是怎么定下来的', 240, 560 - 40 * (1 - ease));
};

window.seek(0);
</script>

注意看标题那一段:没有"开始播放"这个动作,只有"在 t 这一刻,淡入进度是多少"。t 小于 1 秒时进度是 0,大于 1.8 秒时进度是 1,中间按缓动曲线插值。整部片子的所有元素都这么写。

2.4 两种落地方式:自己写 seek,或者接管浏览器的时钟

实现确定性有两条路,各有适用场景。

自己写 seek。 就是上面的写法,所有动画逻辑显式依赖 t。优点是简单、透明,出错了一眼能看出来;缺点是不能直接用现成的 CSS 动画和很多动画库。

注入虚拟时钟。 一些开源渲染项目的做法是,在页面加载前注入一段脚本,把 Date、performance.now、requestAnimationFrame、定时器全部替换成受控版本,再配合浏览器调试协议里的逐帧控制能力来推进时间。这样普通的网页动画也能被确定性地截取。优点是兼容性好,缺点是黑盒多,边角情况不少,比如预热阶段时间倒流会让合成器出错。

我的建议是新手从第一条路开始。等你把一部片子完整跑通,理解了"时间是喂进去的"这件事,再考虑要不要上虚拟时钟。莫潇羽@复源AI社区 在这里多说一句:工具越复杂,智能体写错的地方你越难发现,简单方案带来的可查性,比省下的那点代码量值钱得多。

2.5 这个约定换来了什么

守住"画面只由时间决定",会一次性换来四个好处,后面几章全都建立在它们之上。

  • 可以拆开并行:第 0 到 3600 帧和第 3600 到 7200 帧互不依赖,开几个进程同时跑。
  • 可以中断续跑:已经截好的帧不用重来,脚本从缺的地方接着截。
  • 可以局部重做:只改了第三段的画面,就只重渲染第三段对应的帧。
  • 可以验收:同一帧渲染两次,文件应该逐字节相同。

最后一条可以直接写成自检命令。渲染同一帧两次,比对哈希值:

# 把第 600 帧分别渲染到两个目录,再比对
node render.mjs scene.html check_a 30 600 601
node render.mjs scene.html check_b 30 600 601
sha256sum check_a/000600.png check_b/000600.png
# 两行哈希一致 → 通过;不一致 → 画面代码里还藏着不确定因素

这个检查成本极低,却能提前拦住一大类问题。抓帧脚本 render.mjs 的完整写法放在第五章。

三、声音先行:先把旁白配出来,再让画面跟着走

3.1 为什么顺序要反过来

做过剪辑的朋友,习惯是先把画面剪好,再录旁白去贴。用代码做讲解片,我的做法正好相反:先有声音,量出每一句话从第几秒开始、到第几秒结束,画面再按这张时间表去排。

原因很实际。合成语音的时长没法提前精确预估,同一句话,这次念 4.2 秒,换个音色或者换个模型版本可能就是 5.1 秒。如果先把画面写死,再去配音,结果必然是画面等声音或者声音追画面,到处都要手工对齐。

而代码画面最不怕的就是改时间。把"标题在第 1.0 秒出现"改成"标题在第 3 段旁白的起点出现",时间表一变,全片自动跟着变。所以正确的依赖方向是:音频决定时间轴,时间轴决定画面。

逐字稿 ──切段──▶ 逐段合成 ──量时长──▶ 时间轴(每段的起止秒数)
                                         │
                                         ▼
                              画面代码读取时间轴,按段落编排

3.2 逐字稿是写给耳朵的

稿子有两种,写给眼睛看的和写给耳朵听的。讲解片要的是后一种。区别在哪?我拿一句话举例:

写法

例句

书面写法

该历法体系以太阳在黄道上的视位置为划分依据,共计二十四个划分点。

口语写法

古人盯着太阳看了一整年。太阳每走一小段,就记一个名字,一共记了二十四个。

第二种才念得出口。让智能体写稿时,我会给三条硬要求:句子要能当面对人说出来;开头说人话,别一上来就背定义;结尾要回到开头提的那个问题。

事实核对是另一条线。写稿之前,先让智能体把素材读完、把不确定的地方上网查证,整理成一份资料清单,每一个要讲的日期、人名、数字都标明出处。写稿时只允许使用清单里有出处的内容。下面是一份虚构的示意,用来说明格式:

编号

要讲的事实

出处类型

核对状态

F01

节气划分依据太阳在黄道上的位置

天文科普读物、权威百科条目

两处一致

F02

某部古代历法首次完整记载二十四节气名称

史料原文、学术综述

待补第二来源

F03

相邻两个节气之间大约相隔半个月

历法常识,可用日历验证

已验证

"待补第二来源"的条目,要么查到了再写,要么稿子里干脆不讲。宁可少讲一条,也不要讲一条站不住的。

3.3 切段、合成、量时长

稿子定下来之后,按意群切成几十段,一段一般是一到三句话。切段有两个好处:某一段出问题时只需要重配那一段;段与段之间可以人为插入停顿,控制节奏。

每一段单独送去合成,回来之后立刻量它的实际时长,然后累加成时间轴:

第 k 段的起点 = 第 1 段到第 k−1 段的时长之和 + 它们之间的停顿之和
第 k 段的终点 = 第 k 段的起点 + 第 k 段的时长

量时长用 FFmpeg 自带的 ffprobe 就行:

ffprobe -v error -show_entries format=duration -of csv=p=0 seg_007.wav

累加出来的时间轴存成一个文件,画面代码直接读它:

{
  "fps": 30,
  "segments": [
    { "id": "s01", "start": 0.80,  "end": 5.36,  "text": "古人盯着太阳看了一整年。" },
    { "id": "s02", "start": 5.76,  "end": 11.02, "text": "太阳每走一小段,就记一个名字。" },
    { "id": "s03", "start": 11.62, "end": 17.48, "text": "一共记了二十四个。" }
  ]
}

有的语音合成服务还能返回更细的对齐信息。比如境外的 ElevenLabs,官方文档里有一个带时间戳的合成接口,会在返回音频的同时给出每一个字符的起止时间,这样就能做到"念到哪个词,哪个词高亮"。国内这边,阿里云百炼平台的声音复刻文档写的是提供一段 10 到 20 秒的音频样本即可创建定制音色,平台上有 CosyVoice、MiniMax、Qwen-TTS 等多个模型系列可选;MiniMax 的异步长文本合成文档里也写明支持返回句级时间戳。具体支持到哪个粒度,各家各版本不一样,请以官方文档的实时说明为准。

就算服务不给时间戳也没关系。讲解片绝大多数画面只需要对齐到"句",自己量每段时长就够用了。

合规提示:声音克隆只能用于本人的声音,或者已经取得声音主体明确授权的声音。发布含合成配音的作品时,要按平台要求做 AI 生成内容声明。详细依据放在第九章。

3.4 稿子干净,不等于声音干净

这是我认为最容易被忽略的一关。很多人检查配音只看一样东西:送出去的文字有没有错。可是文字没错,回来的音频照样可能出问题。

基于大模型的语音合成会出现类似"幻觉"的现象,学术论文里对此有记录:生成的语音里可能混入文本中没有的内容,可能重复,也可能漏掉。我碰到过的典型情况是,一句话明明只送了一遍,回来的音频却把它念了两遍,时长直接翻倍。

所以交付之前要核对三样东西,而不是一样:

核对对象

它是什么

可能出的问题

定稿

最终确认的逐字稿

无

实际送出的文本

脚本真正发给合成服务的字符串

夹带了标注、提示语、多余符号

返回的音频

合成结果

重复、漏读、读错、多出内容

前两样做字符串比对就行。第三样的办法是让语音识别把音频再转回文字,逐句和定稿对比。除此之外还有一个更便宜的信号:语速。每段算一下"每秒念了多少个字",明显偏离正常区间的,大概率有问题。

# 伪代码:配音质检的两道筛子
for seg in segments:
    chars = count_chinese_chars(seg.text)
    rate = chars / seg.duration            # 每秒字数

    # 第一道:语速异常(正常区间自己先用几段样本测出来)
    if not (RATE_MIN <= rate <= RATE_MAX):
        flag(seg, "时长异常,疑似重复或漏读")

    # 第二道:识别回来的文字和定稿差太多
    heard = asr(seg.audio)
    if similarity(normalize(heard), normalize(seg.text)) < THRESHOLD:
        flag(seg, "内容不一致,需要人工听一遍")

# 处理原则:只重配被标记的那几段;同一段重配达到上限仍不通过,
# 就停下来,把情况如实写进交付说明,交给人判断。

这里有个细节要提醒:语音识别对同音字没有分辨力,"权力"和"权利"它分不出来,数字的写法也常常和定稿不同。所以相似度比对之前要先做归一化,而且识别结果只能当线索用,不能当结论。真正判断念没念错,还得人去听。

处理原则我写在了伪代码末尾,再强调一遍:哪句有问题就只重配哪句,不要因为一句不满意就把整篇打散重来。重配次数要设上限,到了上限还不行,就停手并如实记录。无休止地重试,既烧额度,也不解决问题。

3.5 两个容易自作聪明的地方

风格要求不要混进朗读文本。 我们对配音有很多期待,比如"开头要从容一点""这句要有停顿"。这些是验收标准,是你和智能体在规划、试听时的目标,它们不应该被原样塞进送给合成模型的文字里。塞进去的后果,轻则没效果,重则被当成正文念出来。想控制语气,应该用服务官方提供的参数(语速、情感、稳定度之类),或者通过切段和段间停顿来实现。

多音字不要抢着标注。 另一个常见冲动是,在容易读错的字后面加上拼音或注音,想提醒模型怎么读。结果往往是模型把标注也念了出来。更稳的做法反而简单:先发原来的汉字,等真的听到或识别到读错了,再只处理那一个字,处理方式用服务官方支持的发音标注或发音词典,不要自己发明格式。

这两条背后是同一个道理:下游模型只会处理它收到的东西,它不知道哪些是"说明",哪些是"正文"。把给人看的意图和给模型吃的输入分开放,是整条流水线里反复要用到的原则。

四、画法怎么选:按内容定,不按习惯定

4.1 三种基本画法

代码能画的东西很多,归拢起来是三类。每一类都有它最合适的内容,用错了地方就会显得别扭。

画法

适合讲什么

优点

常见风险

二维插画与数据图

概念、流程、对比、论文里的数据

清晰、渲染快、文字好排

容易做成幻灯片翻页

低多边形三维场景

空间关系、结构、有地理纵深的历史叙事

有镜头感,能表现体量和位置

渲染慢,容易粗糙

原件上屏

有真实出处的材料:论文页、古籍影印、公有领域画作

观众直接看到证据

涉及版权和授权

一个容易犯的错是"手里有锤子,看什么都是钉子"。三维看起来高级,就全片都上三维。其实讲一篇论文,观众要看的是图表和数据,二维反而更清楚,三维只在需要解释某个立体结构时出场一小段就够了。反过来,讲一段有山川地势的历史,三维场景能让人一眼看懂"谁在哪、往哪走",这时二维地图就显得单薄。

比例不需要事先规定死。让智能体逐段判断"这一段在讲什么",再决定用哪种画法,比一刀切的风格要求效果好。

4.2 把选择写成一张分镜决策表

判断可以交给智能体,但判断的结果必须落到纸面上。我要求每部片子在动手写画面代码之前,先产出一张决策表:每一段讲什么、用哪种画法、为什么、用了什么外部素材。

还是用那部虚构的节气讲解片举例:

段落

讲什么

画法

选择理由

外部素材与授权

开场

抛出问题:节气是谁定的

二维插画

需要快速进入,文字为主

无

第二段

太阳在一年里的位置变化

三维场景

空间关系,二维说不清

无,纯代码建模

第三段

古籍中的最早记载

原件上屏

观众应当看到原文

古籍影印件,需确认馆藏方的使用条款

第四段

相邻节气间隔的规律

数据图

数字对比

数据来自资料清单 F03

结尾

回到开头的问题

二维插画

与开场呼应

无

这张表有三个用处。写代码时不会跑偏;返工时能快速定位"哪一段要改";最后一列逼着你在制作前就把授权问题想清楚,而不是发布后被投诉了才想起来。

除了分段的决定,全片还要定一个统一的风格档位:主色和辅色、标题字体和正文字体、字幕的位置和样式。定了就从头用到尾,不要中途换。风格不统一是"看起来像拼凑的"最主要的原因。

4.3 证据能上屏就上屏

示意图干净好看,但它有一个天然的弱点:观众看不到证据。你说"某篇论文指出",屏幕上是你自己画的一张图,观众只能选择信或者不信。

所以我的原则是,只要手里有真实的原件,并且有权使用,就把原件请上屏幕,旁边写清楚出处。讲论文就放论文页,高亮你正在讲的那一段;讲一幅画,就放那幅画本身。

这里必须提版权。公有领域的作品可以直接用,但"公有领域"要查证,不能凭感觉;论文页面、新闻截图这类材料,要注明来源并控制在合理范围内;图片、字体、音乐只要涉及商用,都需要取得正版授权。拿不准的,就退回到自己画示意图,并在画面上标注"示意"。

4.4 三维场景最常见的两个毛病

让智能体写三维场景,默认产出往往有两个问题,我每次都要在规则里专门压一压。

全程远景。 一台相机远远地挂在天上,下面一堆小方块在动,从头拍到尾。这样的画面有信息,没有情绪。要求远景、中景、近景混着用:交代环境用远景,讲关系用中景,关键动作和关键人物要推近。

白模感。 所有物体一个材质一个颜色,看着像没上色的模型。几何可以简单,低多边形本来就是风格,但木头、金属、布料至少要看得出区别;场景里要有一盏明确的主光,要有柔和的阴影。有了光影,简单的几何也立得住。

这两条我会写成可检查的要求,比如"任意连续 20 秒内至少出现两种景别""场景中至少区分三种材质"。模糊的要求("画面要有电影感")对智能体几乎没用,能数出来的要求才管用。

4.5 历史与严肃题材的分寸

讲历史、讲灾害、讲战争,会遇到一个两难:画得太干净,像沙盘推演,旁白在说伤亡惨重,画面上却一尘不染,这是失真;画得太用力,又会变成渲染暴力。

我给自己定的尺度是两句话。该有的代价要如实呈现,旁白讲到了,画面就不能假装没发生;但绝不把它当成刺激观众的卖点去夸大,不做特写式的渲染。低多边形的抽象风格在这里反而是优势,它能表达"发生了什么",又不会过于具象。

另外要留意平台的治理方向。公开报道显示,国内视频平台在整治 AI 技术滥用时,把 AI 生成的虚假信息、低俗猎奇内容,以及虚构历史人物言行的"穿越"类视频列为重点。做历史题材,史实必须有出处,演绎和推测要明确标注,这既是对观众负责,也是对自己的账号负责。

五、渲染这一关,最容易前功尽弃

5.1 先跑十几秒,别急着跑全片

整片渲染是个大工程。一部几分钟的片子有上万帧,简单的二维画面可能十几分钟跑完,复杂的三维场景跑上一个小时也不稀奇。如果跑完才发现汉字全是方框,那一个小时就白费了。

所以正式渲染之前,一定先做冒烟测试。这是软件测试里的老办法,意思是先通个电,看看冒不冒烟,再谈别的。具体到这里,就是从全片里挑出十几秒最难的片段先渲染一遍,重点看四件事:

检查项

怎么看

不通过说明什么

汉字显示

抽几帧肉眼看

字体没装或没加载完

三维材质和光影

抽关键帧看

材质或灯光设置有问题

同一帧渲染两次是否相同

比对哈希值(第二章的命令)

画面代码里有不确定因素

单帧耗时

记录平均每帧多少秒

用来估算全片要跑多久

最后一项直接决定你怎么安排时间。估算方法很简单:

全片渲染耗时 ≈ 总帧数 × 平均单帧耗时 ÷ 并行路数

示意:14400 帧 × 0.4 秒 ÷ 4 路 = 1440 秒,也就是 24 分钟
(单帧耗时因画面复杂度和机器配置差别很大,这里的数字只是演示算法)

可以参考一个公开的数据点:有开源的短片渲染项目自述,简单画面下的渲染速度接近实时,30 秒的片子大约渲染 30 到 40 秒。画面一旦复杂,这个比例会成倍拉长。所以别拿别人的速度当预期,自己测一遍最可靠。

5.2 中文字体:容器里的"豆腐块"

汉字变方框,是在服务器或容器里做渲染时最常见的翻车。原因并不神秘:精简版的 Linux 镜像几乎不带中日韩字体,浏览器找不到能画这个字的字体,就只能画一个空心方框。圈内管这个叫"豆腐块"。

还有一种更隐蔽的情况:字能显示,但用的是系统随便找来的替补字体,字形和字宽都不对,标题本来一行,变成了两行,下面的排版全乱。

解决分三步。安装字体,确认浏览器真的能找到它,抓帧之前等字体加载完。

# 以 Debian / Ubuntu 系为例:安装开源的思源系列(Noto CJK)并刷新缓存
sudo apt-get install -y fonts-noto-cjk fontconfig
fc-cache -fv

# 确认系统里确实有中文字体,而不只是"装过了"
fc-list :lang=zh | head

第三步在抓帧脚本里做,下一节的代码里有一行 document.fonts.ready,就是在等字体就绪。

顺带一提字体授权。思源系列这类开源字体可以放心用;很多好看的商业字体,个人学习没问题,但用在公开发布、尤其是商业用途的视频里,需要取得正版授权。字体侵权是内容创作者被索赔的高发区,别在这上面省事。

5.3 抓帧脚本要能"从断点接着跑"

抓帧脚本的核心逻辑只有三行:定位到某个时刻,截图,存盘。但要让它在实战里可靠,还得补上两个能力:支持指定帧范围,这样可以分段并行;已经存在的帧直接跳过,这样中断之后可以续跑。

// render.mjs
// 用法:node render.mjs <画面文件> <输出目录> <帧率> <起始帧> <结束帧(不含)>
// 例子:node render.mjs scene.html frames 30 0 14400
import { chromium } from 'playwright';
import fs from 'node:fs';
import path from 'node:path';

const [scene, outDir, fpsArg, startArg, endArg] = process.argv.slice(2);
const fps = Number(fpsArg);
const start = Number(startArg);
const end = Number(endArg);
fs.mkdirSync(outDir, { recursive: true });

const browser = await chromium.launch();
const page = await browser.newPage({
  viewport: { width: 1920, height: 1080 },
  deviceScaleFactor: 1,
});
await page.goto('file://' + path.resolve(scene));
await page.evaluate(() => document.fonts.ready);   // 等字体就绪再开始

let done = 0;
for (let n = start; n < end; n++) {
  const file = path.join(outDir, String(n).padStart(6, '0') + '.png');
  if (fs.existsSync(file)) continue;               // 已有的帧跳过,实现续跑

  await page.evaluate((t) => window.seek(t), n / fps);
  const buf = await page.screenshot({ type: 'png' });

  // 先写临时文件再改名:避免进程被杀时留下半张图,却被当成"已完成"
  fs.writeFileSync(file + '.tmp', buf);
  fs.renameSync(file + '.tmp', file);

  if (++done % 300 === 0) console.log(`已完成 ${n + 1 - start} / ${end - start}`);
}
await browser.close();
console.log('本段抓帧结束');

第一次使用 Playwright 时,它会提示你安装与当前版本配套的浏览器内核,按提示装好再运行即可。

代码里"先写临时文件再改名"那两行,是从教训里来的。如果直接往最终文件名里写,进程恰好在写到一半时被终止,磁盘上就会留下一张残缺的图片。下次续跑时脚本看到文件存在,会跳过它,于是成片里就多了一帧花屏。改名是一个瞬间完成的操作,要么成功要么没发生,用它来标记"这一帧真的写完了"才可靠。

并行也很直接,把帧范围切成几段,各开一个进程:

# 四路并行:每一路负责 3600 帧
node render.mjs scene.html frames 30 0     3600  &
node render.mjs scene.html frames 30 3600  7200  &
node render.mjs scene.html frames 30 7200  10800 &
node render.mjs scene.html frames 30 10800 14400 &
wait    # 关键:等所有子进程都结束,再往下走

最后那个 wait 不是可有可无的,下一节专门讲它。

5.4 长任务不要托付给"后台"

这是我认为整条流水线里最值得警惕的一个坑,而且它不是代码问题,是智能体的工作方式带来的。

编程智能体是一轮一轮工作的:你给任务,它执行一串操作,然后结束这一轮,把结果汇报给你。问题出在"结束这一轮"上。如果它把整片渲染丢到后台,自己随即宣布"已启动渲染"并结束回合,那么会话一旦关闭,后台进程很可能跟着被清理掉。

这不是我的猜测。Claude Code 的代码仓库里有多条公开的问题反馈,描述的都是类似现象:通过智能体启动并被它跟踪的后台任务,在会话结束、上下文压缩或者界面长时间闲置之后被终止,有时任务列表里还显示"运行中"。不同版本、不同运行环境下的具体表现不一样,但结论是一致的:不要假设后台任务能活过当前会话。

落到视频制作上,典型的事故现场是这样的:智能体汇报"渲染已在后台进行",你第二天来看,上万帧只截出来一小部分,没有 MP4,也没有任何报错。

我的应对是把"什么叫完成"在规则里写死:

条件

说明

最后一帧已写入磁盘

帧文件数量等于总帧数

MP4 编码完成

编码命令正常退出

校验全部通过

第六章的检查清单逐项通过

以上三条在同一轮内确认

不允许"已启动,稍后完成"这种汇报

也就是说,整片渲染必须在前台等到底。上一节脚本里的 wait,作用就是让当前命令一直等到所有并行进程结束。如果单次命令有时长上限,就分批跑,每一批都在前台等完,靠"已有帧跳过"的续跑能力把几批接起来。

还有一个更省心的办法:超长的渲染干脆由你自己在一个独立终端里启动,智能体只负责在它之前写好脚本、在它之后做校验。人和智能体各管一段,谁也不用赌。

5.5 返工时只重做受影响的那一段

渲染好的片子被打回来修改是常态。这时候"画面只由时间决定"和"声音先行"两个约定的价值就体现出来了。

如果问题只出在画面上,旁白本身没讲错,那声音一秒都不用动。声音不动,时间轴就不变;时间轴不变,其他段落的帧就全部有效。你只需要删掉受影响那几段对应的帧文件,重新运行抓帧脚本,它会自动只补缺的部分,然后重新编码。

返工判断:
  旁白讲错了? ──是──▶ 重配相关段落 → 时间轴变化 → 该段之后的画面全部重渲染
       │
       否
       ▼
  只是画面问题 ──▶ 声音不动 → 只删除并重渲染受影响的帧区间 → 重新编码

反过来,如果动了旁白,哪怕只改了一句,那一句之后的所有时间点都会平移,后面的帧都得重来。所以稿子和配音的把关要尽量放在前面做足,画面是最后才定的东西。

六、混音与出片:听得舒服,查得过关

6.1 响度和峰值是两回事

声音问题有两种,新手常常混在一起。一种是"整体太小声或太大声",另一种是"某个瞬间爆了,听着刺耳"。它们对应两个不同的指标。

指标

单位

衡量什么

白话解释

综合响度

LUFS

整段音频的平均响度

听起来总体有多响

真峰值

dBTP

瞬间最高电平

最响的那一下冲到了多高

响度范围

LU

最轻和最响段落的差距

音量起伏有多大

克隆出来的人声有一个容易被忽略的特点:平均响度可能并不高,但个别音节的瞬时峰值很冲。如果只看平均响度,觉得偏小就整体放大,峰值就会冲过上限,结果就是破音。真峰值一旦超过 0 dBTP,失真基本就听得出来了。

标准可以参考欧洲广播联盟的 EBU R128 建议书:广播场景的目标响度是 -23 LUFS,真峰值不超过 -1 dBTP。网络视频普遍做得更响一些,业内常见的目标落在 -16 到 -14 LUFS 之间,真峰值留 1 到 2 个 dB 的余量。各平台会按自己的规则再做一次响度归一,所以不必追求某个精确数字,守住"峰值不爆、响度别离谱"就行。

6.2 用两遍 loudnorm 把人声调到位

FFmp

🔒
该内容仅对更高等级社区用户开放
请谨慎解锁时效性强且发布日期较早的文章
单篇解锁后若未显示全文请刷新页面
您当前:游客 · 可见 60% 内容 · 升级至 注册用户 可见 70%
👀
游客
可见 60%
✓ 当前
注册用户
注册用户
可见 70%
社区精英
社区精英
可见 100%
社区守护
社区守护
可见 100%
仅解锁本文,永久有效。如需PDF珍藏版,请联系站长获取。 当前单篇价格 ¥9.9
✏️ 发表评论

请先登录后发表评论

前往登录
📊 站点统计
今日发布0 篇
文章总数1107 篇
昨日发布0 篇
本月发布31 篇
建站时间457 天
🔍 搜索
站长微语

联系站长

QQ:2805463528
AIGC 技术社区
致力于解码 AI前沿技术 与经验分享
纯粹的技术应用社区

💡 欢迎您的建议与反馈,让社区变得更好

快速通道
联系站长
站长QQ二维码
AI交流群
AI交流群
仍在路上

那些寒夜里追赶过的方向

那些冷眼下没放弃的理想

一篇一篇写到现在

仍在路上

"不羁放纵爱自由"

—— 致敬 Beyond
持续创作中 莫潇羽 · 复源AI社区
▲