Communitygithub.com

dmtrKovalenko/fframes

使用 fframes(Rust、SVG 场景、ffmpeg 编码、Skia GPU 渲染)通过代码创建、动画、预览和渲染视频。适用于用户需要视频、动画、动态图形、解说、促销、社交剪辑、标题卡、播客视觉、歌词/字幕视频或任何通过编程生成的 .mp4/.webm 文件的场景,或要求更改、修复、加速或检查 fframes 视频时。涵盖安装 fframes、创建项目、设计好看的动态效果、3D 场景(`fframes::three_d`,three.js 风格场景)、声音(音乐、语音、音效、响度)、在实时预览窗口中查看,以及无需观看即可检查结果:PNG 格式的帧和联系表、自动问题检测、响度数值、快速草稿和范围渲染。

fframes 是什么?

fframes 是一个基于 Rust 的库,用于以代码方式生成视频。用户定义一个 Rust 结构体,实现 `render_frame` 方法,该方法为每一帧返回一个 SVG 树,通过 `svgr!` 宏构建。`timeline!` 宏和弹簧物理动画处理运动,`AudioMap` 管理音频,ffmpeg 负责编码。它支持 Skia GPU 后端以实现更快的渲染和实时预览,并提供一套 CLI 工具(`inspect`、`strip`、`frame`、`preview`、`render`、`audio`)用于开发、调试和验证。该技能适用场景广泛,从简单的标题卡到包含 3D 场景的复杂动态图形。其强调严格的设计流程:在编码前制定计划、避免模板化的“slop”(例如雷同的动画、通用文案)、优先使用预览而非渲染以加快迭代,并使用 `inspect` 命令自动检测问题。它可运行于 macOS、Linux 和 Windows,并提供了一个 HTML 编辑器用于交互式调整。

兼容平台~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/dmtrKovalenko/fframes/tree/HEAD/skills/fframes-video

在你喜欢的 AI 中提问

打开一个已预加载此 Agent Skill 的新对话。

文档

使用 fframes 制作视频

fframes 是一个用 Rust 编写的库,可以通过代码渲染视频。一个视频就是一个 Rust 结构体,它包含一个 render_frame(frame) -> Svgr 方法:为每一帧返回一个 SVG 树,该树使用 svgr! 宏(包含 {rust 表达式} 的 SVG 标记)构建。场景分割时间线,timeline! 和弹簧驱动动画值,音频映射布局声音,ffmpeg 编码最终结果。

每个项目都会获得一个命令行(来自 fframes::cli)。使用它来完成所有操作:渲染视频、打开实时预览窗口,以及查看帧、联系表和响度数值的文件。在开始设计之前,请阅读 references/design.md;编写代码时查阅 references/api.md;声音相关请参考 references/audio.md;3D 场景(fframes::three_d)请参考 references/3d.md。

当匹配提供的视频时,首先按照 references/design.md 中的参考分析指南进行操作。其剪辑、节奏和运动优先于以下设计默认值;快速剪辑和有意为之的黑帧可能是所请求结果的一部分。

在开始其他任何事情之前,有两条规则

先规划,再写代码

不要打开模板就开始制作动画。首先:

  1. 确定需求:谁在看视频以及在哪里看(平台决定格式和时长),观看者必须记住的一件事,以及现有的素材(Logo、产品截图、视频片段、音乐、画外音)。询问用户缺少哪些关键信息。切勿编造产品事实、数字或引用。
  2. 当视频需要配合音乐或人声剪辑时,先选择声音。其节奏、节拍和短语决定了其他所有内容的时机。
  3. 先规划动态设计,将动态设计与声音峰值同步,形成一个独特的流程:元素将从哪里出现,它们将如何变换。尝试为每个场景创建独特的动态设计,使其看起来独特且精致。视频不应看起来像幻灯片。
  4. 尝试思考观看最终视频的人的感受,并相应调整设计的每个部分:流程、节奏、节拍、动态和颜色。

拒绝“模板化”

“模板化”视频是由默认选择组成的:它们可以仅通过更改文本用于任何产品,观众在一秒内就能识别出来。除非用户要求,否则不要提供:

  • 模板残留:起始布局、颜色、字体或装饰、占位符文本、虚假 Logo、编造的数字。
  • 节拍轰炸:整个画面在每个节拍上缩放、抖动或闪烁。将小的元素(一个笔画、一个词、一个火花)与节拍同步,仅在少数几个剪辑点改变整个画面。
  • 一成不变的运动:每个元素都以相同的缓动和持续时间向上淡入 40 像素。
  • 静态场景:一个东西出现、保持并淡出,而其他所有东西都保持不动。
  • 通用文案:“Introducing”、“Unleash”、“Seamless”、“Next-level”、三个词的标语。以真实人类说话的方式编写屏幕文本。
  • 过多文本:文本量应该有限且切中要点。

相反,要给每个元素一个与主题相关的理由,并移除没有理由的元素。将精力放在细节和动态上,而不是文字上:次要运动、交错入场、绘制笔画、相互反应的对象、跨剪辑传递的元素。在向用户展示任何内容之前,查看一下帧条带,并思考如果只更改文本,这个视频是否仍然适用于不同的产品。如果是,那就更改创意,而不是颜色。

1. 安装

从 https://rustup.rs 安装 Rust,然后安装 ffmpeg 构建所需的系统库:

# macOS
brew install pkg-config ffmpeg x264 x265 opus nasm ninja
# Debian / Ubuntu
sudo apt-get install -y yasm nasm ffmpeg libx264-dev libx265-dev libopus-dev libclang-dev clang ninja-build libvpx-dev libasound2-dev
# Arch
sudo pacman -S ninja yasm nasm ffmpeg x264 x265 opus clang

Windows 使用预编译的 FFmpeg 9 共享构建(设置 FFMPEG_DIR 指向从 BtbN/FFmpeg-Builds 解压的 ffmpeg-n9.0-latest-win64-gpl-shared 构建,将其 bin 目录添加到 PATH,使用 winget install LLVM.LLVM 安装 LLVM,并设置 LIBCLANG_PATH)。详细信息在 fframes 的 README 中。

2. 创建项目

curl -fsSL https://raw.githubusercontent.com/dmtrKovalenko/fframes/main/scripts/new-video.sh | bash -s -- my-video --yes
# 或者一次性安装生成器并直接使用:
cargo install --locked cargo-fframes
cargo fframes new my-video --format landscape --fps 30 --yes

选项:--template single-scene|multi-scene,--format landscape|portrait|square|uhd,--fps,--title,--backend,--dir。项目依赖于与已安装的 cargo-fframes 匹配的 fframes 版本;--git 改用仓库的 main 分支。始终传递 --yes 以避免等待输入。

模板:

  • single-scene(默认):单个场景。它适用于所有格式;从这里开始制作单镜头剪辑、标题卡和竖屏视频。
  • multi-scene:两个场景。从这里开始制作包含多个场景的视频;它只接受 landscape 和 uhd 格式。

模板内容是展示 API 的占位符代码。请用专门为用户请求的视频而设计的布局、颜色、字体和装饰来替换。

使用 Skia GPU 后端(默认)。 cargo fframes new 会选择 Metal(macOS)或 Vulkan(Linux、Windows)上的 Skia。它的渲染速度比 CPU 后端快约 10 倍,并为你提供实时 preview 窗口。在 macOS 和 Linux 上,首次构建会下载预编译的 Skia 和 ffmpeg,并且只编译 Rust 依赖项(大约一分钟;其他目标,或同时编译 metal 和 vulkan,则会从源代码编译 Skia,耗时约 20 分钟)。立即开始构建,并在构建运行期间编写视频逻辑:

cd my-video && cargo build --release        # 在后台运行,首次构建是最慢的

--backend cpu 不需要 Skia,但没有预览窗口;仅在确实没有 GPU 时选择它。

项目渲染后的结构如下:

my-video/
  src/lib.rs        # 视频(来自模板)
  src/main.rs       # 命令行
  media/            # 编译到二进制文件中的字体、图像和音频(一个起始字体)
  README.md         # 下面的命令

以下所有命令都是 cargo run --release -- <command>。定义一个简短的 shell 函数(像 R="cargo run --release --" 这样的变量在 zsh(macOS 的默认 shell)中不会被分割成单词):

R() { cargo run --release -- "$@"; }

3. 循环工作流

使用此技能时,根本不要编写测试。 不要创建单元测试、集成测试、快照测试或视觉回归测试、测试模块或测试框架。使用 inspect、渲染的帧、联系表、音频检查和原生预览来验证视频。

优先预览,而非渲染。 如果视频使用 GPU 渲染后端并且此机器上有 GPU 可用,则运行原生预览(或向用户显示命令),而不是在每次更改后重新渲染视频。一旦更改准备好审查:

  1. 在可复制的 shell 代码块中显示完整的命令,使用实际的项目路径和包名;不要要求用户定义了 $R 辅助工具。例如:cd /absolute/path/to/fframes && cargo run --release -p my-video -- preview 0s。
  2. 明确表示可以帮用户运行该预览。如果用户已授权打开或刷新预览,则显示命令并在该授权范围内继续。

在请求更改后,将命令的开始时间设置为更改部分之前或正好开始的时间,并留有足够的提前量以查看其进入或过渡。例如,preview 8.5s 允许用户审阅大约 9 秒处的更改,而无需重播开头部分。说明他们将看到哪个部分。当用户请求最终导出时,再渲染整个视频;如果该请求已经明确,则无需再次确认。

每次更改后:

  1. $R timeline 列出场景及其帧和秒范围,以及每个音轨。在查看像素之前,先在这里检查结构和节奏。
  2. $R inspect 检查每 0.25 秒一帧以及每个场景的第一帧和最后一帧。它会报告缺失的图像、字体或字形、被画布边缘裁剪的文本、无效的 SVG(零尺寸矩形、不良半径)、损坏的变换和 panic,每个问题都带有时间和场景。出现错误时,它以退出码 2 退出。修复它报告的所有问题。仅出现在场景前几帧的警告通常是入场动画;使用帧条带进行检查。
  3. $R strip <scene or range> -n 12 写入一个联系表(strip.png):在一张带标签的图像中显示均匀分布的帧。使用你的图像查看工具打开它。这是判断布局、节奏和运动最快的方法。$R strip 2s,5s,9s 将所选时间并排放置。
  4. $R frame Intro@end,Outro@50% 将全尺寸 PNG 写入 frames/ 目录,用于细节检查(排版、对齐、对比度),并打印在这些帧中发现的问题。在锐利的放大裁剪图上判断小细节(元素之间的接触、边缘、小形状),切勿在缩小后的帧上判断:$R frame 3s --crop 1100,520,240,120(视频像素,放大到约 960 像素宽)。--ids 用于勾勒并命名每个具有 id 的元素,以识别哪个形状是哪个。
  5. $R onion "[email protected]@1s" -n 6 将多帧混合成一张图像(onion.png),以显示运动的路径和间距:缓动、过冲、交错。
  6. $R preview Intro 打开一个带有声音的实时窗口供用户观看(空格 播放/暂停,h/l 前进/后退一秒,j/k 步进一帧,q 退出)。它会阻塞直到窗口关闭,因此在为用户运行时请在后台启动它。如上所述显示命令并提供启动它的选项;在编辑后,使用靠近更改的特定秒数。

当需要编码样本时,$R render Intro --draft 以半分辨率编码一个场景。$R render 为请求的最终导出写入完整的视频。

在展示完成的视频之前,运行 $R inspect --pops:它会以小尺寸渲染每一帧,并警告“跳变(pops)”,即画面中在两个静止帧之间于一帧内发生变化的部分,例如一个元素突然出现、消失或跳到另一个元素前面,而不是平滑移动过去。每个警告都会给出 frame ... --crop ... 命令来查看它;同一区域的重复事件(计数器、闪烁的灯)会分组为一个警告。

规则:

  • 在看 PNG 图像之前,不要说任何东西看起来不错。
  • 优先使用 strip 而不是多次 frame 调用;当只需要构图时,添加 --scale 0.5。
  • 在解析输出时添加 --json:结果输出到 stdout,进度信息输出到 stderr。
  • 始终使用 --release:debug 构建的渲染速度要慢得多。

时间表示法

120(帧),3.2s,500ms,1:05.5,50%,start,end;按结构体名称引用场景,例如 Intro(也匹配 IntroScene,不区分大小写),#3(场景索引),Intro[1](该类型的第二个场景);在场景内部使用 [email protected],Intro@12,Intro@50%,Intro@end。范围:a..b(不包含结束),a..,..b,all,或场景名称表示整个场景。使用逗号分隔多个时间点。

所有命令

命令用途
timeline查看场景、时长、音轨及其混音设置
inspect [RANGE] [--every 0.1s | --all-frames] [--fail-on warning] [--pops]在不渲染像素的情况下发现问题;--pops 还会发现突然跳入或跳出的元素
strip [RANGE | TIMES] -n N [--columns 4] [--width 480] [--crop x,y,w,h]在一张图像中审查流程和动态
frame TIMES [-o dir] [--svg] [--crop x,y,w,h] [--zoom N] [--ids]选择帧的全尺寸 PNG(以及布局好的 SVG)、锐利裁剪、带标签的元素
onion RANGE -n N查看运动的轨迹和缓动
svg TIME将帧的最终 SVG 作为文本读取(位置、文本、颜色)
preview [TIME] [--paused] [--mute]带声音的实时窗口,供用户使用
render [RANGE] [-o out.mp4] [--draft]编码视频或其一部分(音频会相应裁剪)
audio analyze [RANGE] [--waveform w.png]响度(LUFS)、真实峰值、削波、静音,按场景显示
audio at TIMES在某个时刻播放哪些声音、在文件中的位置以及响度
audio render [RANGE] -o a.wav将混音输出为 WAV 文件

浏览器编辑器

fframes 还有一个带有时间线和逐帧浏览功能的 Web 编辑器,当用户想要交互式调整视频时非常有用。它运行编译为 WebAssembly 的视频,需要 Node.js 和 wasm-pack;设置方法是 fframes 仓库中 hello-world 示例的 editor/ 文件夹(https://github.com/dmtrKovalenko/fframes/tree/main/examples/hello-world)。当用户询问 GUI 编辑器时提及它。对于观看,请使用 preview;对于你自己的审查,请使用 CLI。

4. 编写视频

impl Video for MyVideo<'_> {
    const FPS: usize = 30; const WIDTH: usize = 1920; const HEIGHT: usize = 1080;
    fn duration(&self) -> Duration<'_> { Duration::Auto }            // 所有场景的总时长
    fn audio(&self) -> AudioMap<'_> { AudioMap::none() }              // 参见 references/audio.md
    fn define_scenes(&self) -> Scenes<'_> { Scenes::from(vec![&self.intro as &dyn Scene, &self.main]) }
    fn render_frame<'a>(&'a self, frame: Frame, ctx: &FFramesContext<'a, '_>) -> Svgr<'a> {
        fframes::svgr!(<svg xmlns="http://www.w3.org/2000/svg" width={Self::WIDTH} height={Self::HEIGHT}>
            <rect width={Self::WIDTH} height={Self::HEIGHT} fill={BACKGROUND} />
            {ctx.render_scenes(&frame)}
        </svg>)
    }
}
  • 一个场景对应一个想法,每个场景持续 2-6 秒。在场景内部,frame.seconds() 从场景开始处计数。
  • 默认在 svgr! 属性中直接使用 frame.animate(fframes::timeline!(...)),以实现具有固定值的动画。svgr! 将这些动画提升为缓存中的静态值。在 time line 中内联定义缓动函数,包括 Easing::Spring { mass: 1.0, stiffness: 180.0, damping: 20.0 };命名缓动常量是可选的。省略弹簧动画的结束时间,以便它自然衰减到静止状态。
  • 使用 AudioMap 添加音频轨道。它接受文件路径、循环设置、音量包络和同步点。audio analyze 命令帮助你设置合理的响度水平。
  • 3D 场景使用 fframes::three_d 模块,它提供了类似于 three.js 的 API 来构建具有摄像机、灯光和网格对象的场景。查看 references/3d.md 获取详细信息。

5. 查看结果

在最终导出之前,始终运行完整的检查套件:

$R timeline
$R inspect --all-frames
$R inspect --pops
$R audio analyze

修复所有报告的问题。在最终渲染之前,使用 $R preview 或 $R strip 获得最终的视觉认同。

6. 导出最终视频

$R render -o final_video.mp4

这将以全分辨率编码完整视频,包含所有音频。文件将被写入当前目录。

7. 故障排除

  • 渲染速度慢: 确保你正在使用 --release 标志进行构建,并且(如果适用)正在使用 Skia GPU 后端。CPU 后端要慢得多。
  • 预览窗口无响应: 预览窗口在关闭前会阻塞终端。确保在后台启动它(例如,在命令末尾添加 &)。
  • 字体问题: FFrames 会自动将 media/ 目录中的字体编译到二进制文件中。确保你的字体文件位于那里,并在你的 SVG 代码中正确引用。
  • 音频未同步: 检查音频文件的长度。audio analyze 命令可以向你显示每个音轨的时长。
  • 内存不足: 非常长的视频或高分辨率输出可能会消耗大量内存。尝试减少 --format 分辨率或将视频分成更小的部分进行渲染。

8. 进一步阅读

  • FFrames 文档
  • FFrames GitHub 仓库
  • references/design.md - 视频设计指南
  • references/api.md - 完整的 API 参考
  • references/audio.md - 音频详细参考
  • references/3d.md - 3D 场景参考

相关技能