ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Claude Opus 5.5 如何做视频?JS+Canvas+FFmpeg 全链路拆解

Claude Opus 5.5 如何做视频?JS+Canvas+FFmpeg 全链路拆解

1. 从标题说起:一个模型名字为什么会和视频制作扯上关系

第一次看到“Claude Opus 5.5 是怎么做出视频的”这个标题,我脑子里冒出来的第一个念头是:这大概率又是一个把“AI生成”和“视频制作”混为一谈的标题党。但仔细琢磨了一下热搜词——Claude Opus、视频、JS、Canvas、FFmpeg——这几个词凑在一起,其实指向的是一个非常具体、非常工程化的技术链路:用大模型负责“想”和“写”,用 JavaScript 和 Canvas 负责“画”,用 FFmpeg 负责“合成”。模型本身并不直接吐出 mp4 文件,它做的是把一段自然语言需求,翻译成一套可执行的、能驱动浏览器渲染和命令行转码的代码与指令。

这个认知很关键。很多人对“AI做视频”的想象是:输入一句话,等几分钟,一个成品视频就下载下来了。实际在工程上,这条路被拆成了好几段:脚本生成、逐帧绘制、帧序列导出、音视频编码、封装输出。Claude Opus 这类模型在其中扮演的是“总导演兼编剧兼部分分镜师”的角色,它输出的是结构化的代码和参数,真正干重活的是 Canvas 的 2D 绘图 API 和 FFmpeg 的编码管线。

所以这篇内容我想聊的,不是去吹某个模型有多神,而是把这套“模型驱动 + 前端渲染 + 后端转码”的视频生成链路完整拆开,讲清楚每一段在干什么、为什么这么设计、参数怎么定、坑在哪里。适合谁看?如果你是会一点 JavaScript、想搞明白程序化视频生成到底怎么落地的人,或者你是做内容工具、想把这套流程产品化的开发者,再或者你只是好奇“AI 到底怎么把文字变成视频”的技术爱好者,这篇都能给你一条能照着复现的路径。核心关键词我会反复提到:Claude Opus 负责编排,JS 负责逻辑,Canvas 负责绘制,FFmpeg 负责编码,视频是最终产物。

需要先说明一点:下面涉及的具体模型版本能力、API 调用方式,我会基于“一个具备代码生成能力的大语言模型在此场景下最合理的用法”来补全,因为不同版本、不同接入方式的细节会有差异,但整体架构和工程思路是通用的、可迁移的。你把这套思路套到任何一个能稳定输出代码的模型上,都能跑通。

2. 整体设计思路:为什么是“模型 + Canvas + FFmpeg”这条链路

2.1 直接让模型输出视频文件,为什么行不通

先把这个最容易被误解的点讲透。大语言模型的本质是自回归的文本生成器,它的输出单位是 token,也就是文本片段。它能写代码、写脚本、写配置,但它没有一个原生的“像素缓冲区”或者“音频采样率”的概念。你让它直接生成一个 mp4 的二进制流,理论上可以强行让它输出 base64,但那个体积、那个质量、那个可控性,完全是灾难级别的。一个 10 秒 1080p 的视频,哪怕压缩得很狠,也是几 MB 起步,让模型一个 token 一个 token 地吐出来,既慢又贵还不稳定。

所以正确的分工是:模型负责生成“如何制作视频”的完整指令集,而不是视频本身。这个指令集可以是一段 JavaScript 代码,也可以是一组 FFmpeg 命令,或者两者结合。模型的价值在于它理解自然语言需求,能把它翻译成结构化的、可执行的操作序列。这就是为什么热搜词里同时出现了 JS 和 FFmpeg——它们分别对应“绘制”和“编码”两个阶段。

2.2 Canvas 为什么是绘制阶段的首选

在浏览器环境里做程序化绘图,Canvas 是绕不开的。它提供了 2D 上下文,能画矩形、圆形、路径、文字、图片,还能做变换、裁剪、渐变、阴影。对于“用代码生成视频画面”这个需求来说,Canvas 的优势非常明显:API 直观、生态成熟、性能足够、和 JS 天然一体。你让模型生成一段 Canvas 绘制代码,它很容易就能写出“在 (100,100) 位置画一个半径 50 的红色圆”这种确定性极强的指令。

对比一下其他方案:SVG 适合矢量图形但逐帧渲染和导出帧序列比较麻烦;WebGL 性能强但学习曲线陡,模型生成正确率会下降;直接用像素数组操作则太底层,代码量大且容易出错。Canvas 2D 正好卡在“表达力够用”和“模型能稳定生成”之间的甜点区。热搜词里的“canvas绘图”“canvas 2d vue”“canvas文字3d效果”其实都指向同一个事实:Canvas 是前端程序化视觉内容的主力工具。

2.3 FFmpeg 为什么必须出现在链路末端

Canvas 能画出一帧帧的画面,但它本身不知道怎么把这些帧变成视频文件。浏览器里可以用MediaRecorder录 Canvas 流,但那个方案对帧率控制、编码参数、音视频同步的支持都比较粗糙,做出来的东西很难精细调优。真正工业级的做法是:把 Canvas 每一帧导出成图片序列(比如 PNG),然后用 FFmpeg 把这些图片按指定帧率编码成视频。FFmpeg 是音视频处理领域的瑞士军刀,编码格式、码率、分辨率、帧率、音频混流,全都能用命令行参数精确控制。

热搜词里“ffmpeg命令”“ffmpeg使用教程”“ffmpeg安装”“视频转码m3u8”这些,说明大量开发者在这个环节踩过坑、查过资料。FFmpeg 的学习成本主要在于参数多、文档散,但一旦你把常用命令整理成模板,它就是一个极其稳定的编码后端。模型在这里的作用是:根据需求生成对应的 FFmpeg 命令,或者生成调用 FFmpeg 的脚本代码。

2.4 三段式链路的完整数据流

把上面三段串起来,整个数据流是这样的:

  1. 需求输入:用户用自然语言描述想要的视频,比如“做一个 5 秒的动画,蓝色背景上一个白色圆从左移到右,帧率 30”。
  2. 模型编排:Claude Opus 这类模型把需求拆解成两部分——一段 Canvas 绘制代码(定义每一帧画什么),一组 FFmpeg 编码参数(定义怎么把帧合成视频)。
  3. 逐帧渲染:JS 执行 Canvas 代码,按时间轴计算每一帧的状态,把画面画到 Canvas 上,导出为frame_0001.png、frame_0002.png这样的序列。
  4. 编码封装:FFmpeg 读取帧序列,按指定帧率编码,输出output.mp4。
  5. 可选后处理:加音频、加字幕、转码成不同分辨率或格式。

这个链路的好处是每一段都可控、可调试、可替换。Canvas 画错了,你改代码重跑;编码参数不对,你改 FFmpeg 命令重跑。模型只负责生成初版代码和参数,后续的微调完全在工程侧完成。这就是为什么我说“模型不直接做视频”,它做的是把意图翻译成可执行的工程方案。

3. 核心细节拆解:模型到底生成了什么

3.1 模型输出的第一块:Canvas 绘制脚本

模型生成的核心产物之一,是一段能驱动 Canvas 逐帧绘制的 JavaScript。这段代码通常包含几个关键部分:初始化画布、定义绘制函数、按帧号计算动画状态、导出帧图像。我拿一个最简例子来说明模型会怎么组织这段代码。

假设需求是“一个白色圆在蓝色背景上从左到右移动,5 秒,30 帧每秒”,模型生成的代码大概长这样:

const canvas = document.createElement('canvas'); canvas.width = 1280; canvas.height = 720; const ctx = canvas.getContext('2d'); const FPS = 30; const DURATION = 5; const TOTAL_FRAMES = FPS * DURATION; // 150 帧 function drawFrame(frameIndex) { const t = frameIndex / TOTAL_FRAMES; // 0 到 1 的进度 // 背景 ctx.fillStyle = '#1a3a8f'; ctx.fillRect(0, 0, canvas.width, canvas.height); // 圆的水平位置:从左边 100px 到右边 1180px const x = 100 + t * (canvas.width - 200); const y = canvas.height / 2; ctx.beginPath(); ctx.arc(x, y, 60, 0, Math.PI * 2); ctx.fillStyle = '#ffffff'; ctx.fill(); } for (let i = 0; i < TOTAL_FRAMES; i++) { drawFrame(i); // 这里导出当前帧,具体导出方式见下一节 }

这段代码的逻辑非常清晰:drawFrame接收帧号,把它换算成 0 到 1 的时间进度t,然后用t去插值圆的位置。这种“帧号 → 时间进度 → 状态插值 → 绘制”的模式,是程序化动画的通用骨架。模型只要理解了需求里的“从左到右”“5 秒”“30 帧”,就能自动算出总帧数、插值公式和坐标范围。

这里有个细节值得说:为什么用t = frameIndex / TOTAL_FRAMES而不是直接用帧号算像素?因为用归一化的进度t做插值,动画逻辑和分辨率、帧率解耦了。你后面想把 1280x720 改成 1920x1080,或者把 30 帧改成 60 帧,只需要改常量,插值逻辑不用动。这是模型生成代码时应该遵循的好习惯,也是你审查模型输出时要重点看的地方。

3.2 模型输出的第二块:帧导出与 FFmpeg 命令

Canvas 画完一帧,得把它变成文件。浏览器里最直接的方式是canvas.toDataURL('image/png'),拿到 base64 数据,再转成二进制写文件。但在纯前端环境里写文件受限,所以实际工程中常见两种做法:一种是在 Node.js 环境里用canvas这个 npm 包(服务端 Canvas 实现)直接渲染并写 PNG;另一种是在浏览器里渲染,通过接口把帧数据传给后端落盘。热搜词里“canvas 2d vue”暗示了前端框架集成的场景,而“ffmpeg c++封装”“ffmpeg sdk下载”则说明有人在做更深度的集成。

不管哪种方式,最终你都会得到一堆按序号命名的 PNG 文件。接下来就是 FFmpeg 的活。把帧序列编码成视频的标准命令是:

ffmpeg -framerate 30 -i frame_%04d.png -c:v libx264 -pix_fmt yuv420p -crf 18 output.mp4

这条命令里每个参数都有讲究,我逐个拆:

  • -framerate 30:输入帧序列的帧率,必须和生成帧时的 FPS 一致,否则视频速度会不对。
  • -i frame_%04d.png:输入模式,%04d表示 4 位数字序号,对应frame_0001.png到frame_9999.png。序号位数不够会导致读取失败,这是新手最常踩的坑之一。
  • -c:v libx264:视频编码器,H.264 兼容性最好,几乎所有播放器和平台都支持。
  • -pix_fmt yuv420p:像素格式,这个参数极其重要。不加它,很多播放器(尤其是浏览器和移动端)会显示黑屏或无法播放,因为默认可能是 yuv444p 这种不被广泛支持的格式。
  • -crf 18:恒定质量因子,数值越小质量越高、文件越大。18 是视觉上接近无损的常用值,范围一般 0-51。

模型在生成这条命令时,需要根据需求里的帧率、输出格式、质量要求来填参数。如果需求里说“要小一点的文件”,模型应该把-crf调大,比如 23 或 28;如果说“要最高质量”,就调到 15 甚至更低。这种参数映射能力,正是模型编排价值的体现。

3.3 模型输出的第三块:时间轴与动画状态管理

稍微复杂一点的视频,不是单一物体的简单移动,而是多个元素、多个阶段、多种缓动的组合。这时候模型需要生成一套时间轴管理逻辑。常见做法是定义一个关键帧数组,每个关键帧记录时间点和对应的状态,然后在绘制时根据当前时间在关键帧之间插值。

const keyframes = [ { time: 0.0, x: 100, opacity: 0 }, { time: 0.2, x: 300, opacity: 1 }, { time: 0.8, x: 900, opacity: 1 }, { time: 1.0, x: 1100, opacity: 0 }, ]; function lerp(a, b, t) { return a + (b - a) * t; } function getStateAt(progress) { for (let i = 0; i < keyframes.length - 1; i++) { const kf0 = keyframes[i]; const kf1 = keyframes[i + 1]; if (progress >= kf0.time && progress <= kf1.time) { const localT = (progress - kf0.time) / (kf1.time - kf0.time); return { x: lerp(kf0.x, kf1.x, localT), opacity: lerp(kf0.opacity, kf1.opacity, localT), }; } } return keyframes[keyframes.length - 1]; }

这套关键帧加线性插值的机制,是动画系统的核心。模型理解“先淡入、再移动、最后淡出”这种描述后,能自动把它转成关键帧数组。如果要加缓动效果,比如“先快后慢”,就在localT上套一个缓动函数,比如localT * localT或者1 - Math.pow(1 - localT, 3)。热搜词里“canvas文字3d效果”这类需求,本质上也是在这套时间轴基础上,对文字的变换矩阵做插值。

3.4 参数计算:帧数、时长、码率的换算关系

这里必须把几个基础换算讲清楚,因为模型生成参数、你审查参数,都依赖这些关系。

总帧数 = 帧率 × 时长(秒)。5 秒 30 帧就是 150 帧,10 秒 60 帧就是 600 帧。帧数直接决定渲染时间和磁盘占用,150 张 1280x720 的 PNG 大概几十 MB,600 张就上百 MB 了。所以做长视频时,要么降低帧率,要么用 JPEG 代替 PNG 减小体积,要么边渲染边编码不落盘。

码率估算。H.264 在 1080p 下,CRF 18 大概对应 8-15 Mbps 的码率,CRF 23 大概 4-8 Mbps。文件大小约等于码率 × 时长 / 8。比如 10 秒、8 Mbps,就是8 × 10 / 8 = 10 MB。这个估算能帮你在生成前就判断文件大小是否合理。

分辨率与渲染成本。像素总数是宽乘高,1920x1080 是 207 万像素,1280x720 是 92 万像素,前者是后者的 2.25 倍。Canvas 每帧要填充这么多像素,帧数一多,渲染时间会明显上升。如果只是做草稿预览,先用 640x360 渲染,确认动画逻辑对了再上高分辨率,这是很实用的工作流。

4. 实操过程:从一句话需求到 mp4 文件

4.1 环境准备:Node.js、Canvas 包和 FFmpeg

要完整跑通这条链路,我建议在 Node.js 环境里做,因为文件读写、进程调用都方便。需要准备三样东西:Node.js 运行时、服务端 Canvas 实现、FFmpeg 可执行文件。

Node.js 直接去官网下 LTS 版本就行。Canvas 包用npm install canvas,它依赖一些系统库,在 Linux 上可能需要先装libcairo2-dev、libpango1.0-dev这类依赖,macOS 上用 Homebrew 装pkg-config和cairo通常就够了。Windows 上这个包有预编译版本,一般npm install直接能过,过不了就装 Visual Studio Build Tools。

FFmpeg 的安装,Windows 用户去官网下编译好的压缩包,解压后把bin目录加到 PATH 环境变量;macOS 用brew install ffmpeg;Linux 用包管理器,比如apt install ffmpeg。装完在终端敲ffmpeg -version,能打印版本信息就说明成了。热搜词里“ffmpeg下载官网”“ffmpeg安装”之所以高频,就是因为这一步卡住了不少人,尤其是 PATH 没配好导致命令找不到。

提示:FFmpeg 的 Windows 版本分 static、shared、dev 几种,做命令行转码用 static 版最省事,单个 exe 不依赖额外 dll。dev 版是给要编译集成的人用的,普通使用没必要。

4.2 第一步:让模型生成绘制脚本

把你的需求整理成一段清晰的描述,交给模型。描述里最好包含这几个要素:时长、帧率、分辨率、画面元素、动画行为、配色。比如:

生成一段 Node.js 代码,用 canvas 包创建一个 1280x720 的画布,帧率 30,时长 5 秒。背景深蓝色 #1a3a8f,一个白色圆形半径 60,从左侧 x=100 匀速移动到右侧 x=1180,垂直居中。每一帧导出为 PNG,文件名格式 frame_0001.png,存到 frames 目录。

模型会输出一段完整的、可运行的代码。拿到代码后,先别急着跑全量,把总帧数临时改成 5 帧,跑一遍看看有没有报错、目录有没有生成、图片内容对不对。这个“小批量验证”的习惯能帮你快速定位问题,而不是等 150 帧跑完才发现文件名格式错了。

4.3 第二步:逐帧渲染并落盘

渲染循环的核心逻辑是:清空画布、计算当前帧状态、绘制、导出。用 Node.js 的 canvas 包,导出 PNG 的代码大概是这样:

const fs = require('fs'); const path = require('path'); const { createCanvas } = require('canvas'); const canvas = createCanvas(1280, 720); const ctx = canvas.getContext('2d'); const FPS = 30; const DURATION = 5; const TOTAL_FRAMES = FPS * DURATION; const OUT_DIR = path.join(__dirname, 'frames'); if (!fs.existsSync(OUT_DIR)) fs.mkdirSync(OUT_DIR, { recursive: true }); for (let i = 0; i < TOTAL_FRAMES; i++) { const t = i / TOTAL_FRAMES; ctx.fillStyle = '#1a3a8f'; ctx.fillRect(0, 0, 1280, 720); const x = 100 + t * 1080; ctx.beginPath(); ctx.arc(x, 360, 60, 0, Math.PI * 2); ctx.fillStyle = '#ffffff'; ctx.fill(); const fileName = `frame_${String(i + 1).padStart(4, '0')}.png`; const buffer = canvas.toBuffer('image/png'); fs.writeFileSync(path.join(OUT_DIR, fileName), buffer); if ((i + 1) % 30 === 0) { console.log(`已渲染 ${i + 1} / ${TOTAL_FRAMES} 帧`); } }

注意String(i + 1).padStart(4, '0')这一句,它保证文件名是frame_0001这种固定 4 位格式,和 FFmpeg 的%04d严格对应。序号从 1 开始还是从 0 开始也要注意,FFmpeg 默认从 0 开始找,如果你从 1 开始,要么改文件名,要么在命令里加-start_number 1。这个细节不注意,FFmpeg 会报“找不到文件”或者只编码了一部分。

4.4 第三步:FFmpeg 编码合成

帧序列齐了,进到 frames 目录的上一级,执行:

ffmpeg -framerate 30 -start_number 1 -i frames/frame_%04d.png -c:v libx264 -pix_fmt yuv420p -crf 18 -y output.mp4

-start_number 1对应我们从 1 开始的命名,-y表示输出文件已存在时直接覆盖不询问。跑完之后用播放器打开output.mp4,检查动画是否流畅、颜色是否正确、时长是否准确。如果发现视频比预期快或慢,八成是-framerate和生成帧时的 FPS 不一致;如果发现画面颜色发灰或偏色,检查-pix_fmt是不是设成了yuv420p。

4.5 第四步:加音频和字幕(可选扩展)

纯画面视频做出来后,加音频是常见需求。假设你有一个bgm.mp3,用这条命令混流:

ffmpeg -i output.mp4 -i bgm.mp3 -c:v copy -c:a aac -shortest final.mp4

-c:v copy表示视频流不重新编码,直接复制,速度快、无质量损失;-c:a aac把音频编码成 AAC;-shortest表示以较短的流为准截断,避免音频比视频长导致尾部黑屏。加字幕的话,可以用-vf subtitles=sub.srt把字幕烧进画面,或者用-c:s mov_text封装成软字幕。热搜词里“视频转码m3u8”对应的则是把 mp4 切片成 HLS 格式,命令是ffmpeg -i final.mp4 -c copy -f hls -hls_time 10 playlist.m3u8,这个在需要流式播放的场景很有用。

5. 常见问题与排查技巧实录

5.1 帧序列读取失败:序号格式对不上

这是最高频的问题。表现是 FFmpeg 报No such file or directory或者只处理了第一帧。原因几乎都是文件名序号位数和%0Nd不匹配。比如你生成的是frame_1.png、frame_2.png,但命令写的是frame_%04d.png,FFmpeg 会去找frame_0001.png,自然找不到。解决办法是统一用padStart补零,或者把命令里的位数改成实际位数。我个人的习惯是永远用 4 位,因为 4 位能撑到 9999 帧,30 帧下就是 333 秒,够绝大多数短视频用了。

5.2 视频在浏览器里黑屏:像素格式的锅

用 FFmpeg 默认参数编码出来的视频,在本地播放器能放,但放到网页<video>标签里就是黑屏或者只有声音。这几乎可以肯定是像素格式不是yuv420p。H.264 编码器默认可能输出yuv444p或yuvj420p,这些格式浏览器支持不好。加上-pix_fmt yuv420p就能解决。这个坑我踩过不止一次,现在写命令时已经条件反射地带上这个参数了。

5.3 动画速度不对:帧率不一致

视频看起来比预期快一倍或慢一半,检查两个地方:生成帧时的 FPS 常量和 FFmpeg 的-framerate参数。这两个必须相等。如果你生成了 150 帧,本意是 5 秒 30 帧,但 FFmpeg 用了-framerate 60,那视频就变成 2.5 秒了,速度翻倍。反过来用 15,就变成 10 秒,速度减半。这个换算关系很简单,但忙起来真的容易忘。

5.4 渲染太慢:分辨率和帧数的取舍

150 帧 1080p 的 PNG 渲染,在普通机器上可能要一两分钟,如果动画复杂、元素多,时间会更长。优化思路有几个:一是先用低分辨率草稿验证逻辑,确认后再上高分辨率;二是用 JPEG 代替 PNG,体积小、编码快,画质损失在动画场景里通常可接受;三是减少不必要的重绘,比如静态背景可以只画一次然后复用;四是如果只是预览,把帧率降到 15 甚至 10,人眼对动画流畅度的感知在 15 帧以上就不太明显了。

5.5 常见问题速查表

问题现象可能原因排查方向解决办法
FFmpeg 找不到帧文件序号位数不匹配对比文件名和%0Nd统一补零位数或加-start_number
网页播放黑屏像素格式不支持查-pix_fmt参数加-pix_fmt yuv420p
视频速度异常帧率不一致对比生成 FPS 和-framerate两处改成相同值
渲染耗时过长分辨率/帧数过高算总帧数和像素量降分辨率、降帧率、改 JPEG
输出文件过大CRF 值太小查-crf参数调大到 23-28
音频视频不同步时长不匹配对比两条流时长加-shortest截断
颜色偏灰偏暗色彩空间转换问题查输入输出色彩参数显式指定-colorspace bt709

5.6 几个我踩过的坑和私房技巧

第一个坑是目录不存在导致写入失败。Node.js 的writeFileSync不会自动创建父目录,如果frames目录不存在,第一帧就报错。所以渲染前一定要mkdirSync加recursive: true。

第二个坑是Canvas 字体在服务端不生效。服务端 Canvas 的字体依赖系统安装的字体,如果你用了某个特殊字体但系统里没有,文字会回退成默认字体,甚至不显示。解决办法是显式注册字体文件,或者干脆用系统自带的无衬线字体。

第三个技巧是用进度日志判断卡死。渲染循环里每 30 帧打一条日志,能让你知道程序是在正常跑还是卡住了。如果日志停在某一帧不动,大概率是那一帧的绘制逻辑有死循环或者内存问题。

第四个技巧是先出低质量预览再出成品。我通常先用-crf 28和 640x360 快速出一版,确认动画节奏、配色、时长都对了,再用-crf 18和全分辨率出成品。这样迭代速度快很多,不会在细节没定的时候浪费时间渲染高清。

6. 这条链路还能怎么扩展

把基础链路跑通之后,能玩的花样其实很多。模型负责编排这个特性,意味着你可以把越来越复杂的视觉需求交给它翻译成代码。比如做数据可视化动画,模型可以根据一组数据生成柱状图的增长动画;做文字特效,模型可以生成逐字出现、缩放、旋转的关键帧;做多场景拼接,模型可以生成一个场景管理器,按时间切换不同的绘制函数。

热搜词里“文生视频技术”“ai视频”这些概念,落到工程上其实就是这条链路的不断复杂化。模型的能力在提升,能生成的代码越来越复杂,但底层的“生成代码 → Canvas 渲染 → FFmpeg 编码”这个骨架不会变。你把这套骨架吃透,后面不管模型怎么迭代,你都能快速把新能力接进来。

另外一个值得关注的方向是实时预览。现在这套流程是离线渲染,改一次代码要重新跑一遍。如果做成浏览器里实时预览 Canvas 动画,调好参数再导出帧序列,体验会好很多。热搜词里“canvas 2d vue”暗示的就是这种前端集成的思路——用 Vue 管理参数面板,Canvas 实时渲染,用户拖滑块调参数,满意了再点导出。这种工具化方向,是把这套技术从“开发者玩具”变成“生产力工具”的关键一步。

最后分享一个我在实际项目里的体会:模型生成的代码永远要过一遍人工审查。不是不信任模型,而是视频这种产物,参数错一个,可能整段都废了。审查的重点就三个:帧率对不对、像素格式带没带、文件名序号和 FFmpeg 模式匹配不匹配。这三项过了,基本就不会出大问题。剩下的就是审美和创意的事了,那部分,模型帮你想,你来做决定。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进