排错 - ZLHad/OpenVideoHarness GitHub Wiki
English · 中文
下面是仓库文档里记录过的问题:现象、原因、怎么办。遇到问题先跑 bin/vh doctor。如果是几天前装的,先 git pull:有不少修复(Linux、声音、渲染)已经在 main 上,只是还没打版本。
| 现象 | 原因 | 怎么办 |
|---|---|---|
install.sh 停下,提示"✗ node is required"(或 ffmpeg、git) |
缺少必需的工具 | 装好 git、Node.js ≥ 22 和 FFmpeg 后重跑 |
安装时打印 skip <仓库> (clone failed)
|
某个上游参考仓库连不上;现在会跳过它,不会中断安装 | 之后重跑 references/fetch.sh,或者用 references/fetch.sh <目录> 单独拉一个 |
在 styles/_swatch 里 npm ci 失败,报 registry.npmmirror.com 的错 |
旧的 lockfile 把源写死成了这个镜像 |
git pull(已修复),再 cd styles/_swatch && npm ci
|
doctor 显示"✗ chrome" |
找不到 Chrome 或 Chromium | macOS:装 Google Chrome。Linux:sudo apt install chromium 或 npx playwright install chromium。也可以设 CHROME_PATH
|
doctor 显示"! no WebGL with the GPU flags" |
无头 Chrome 用不了 GPU | 渲染时加 node render.mjs --soft-gl(软件模拟 WebGL)。如果 doctor 说加了 --soft-gl 也没有 WebGL,这台机器就渲不了 WebGL 场景 |
doctor 显示"! AGENTS.md out of sync" |
改了 CLAUDE.md,没重新生成 AGENTS.md
|
运行 bin/vh sync-agents
|
| 下载(npm、PyPI、Hugging Face、Chrome)很慢或失败 | 第一次运行要连境外的服务器 | 各自的镜像设置见国内网络;GitHub 本身连不上时,用 --no-refs 安装,参考仓库用到哪个再拉哪个 |
| 在别的目录说"做个视频",agent 找不到这里 | agent 还没重新加载 skill |
bin/vh install-skill 之后重启 Claude Code 或 Codex |
Linux 上 bin/vh new … --effort studio 仍写成 standard;样片渲完就报错退出;确定性检查卡在 md5 上 |
旧版本用了只有 macOS 才有的 sed -i ''、stat -f、md5 -q
|
git pull,main 上已修复 |
bin/vh setup -h 开始整套安装,bin/vh sync-agents -h 重写了 AGENTS.md,或者某个命令把 -h 当成了文件名 |
旧版本把 -h、--help 原样交给了命令 |
git pull:现在 bin/vh <命令> -h 只打印这个命令的用法就退出,不会真的运行 |
| 现象 | 原因 | 怎么办 |
|---|---|---|
| HyperFrames 渲染卡住不动,日志为空,也不报错 | 页面从 CDN 加载 GSAP 或 three.js,而加载时没有网 | 把依赖装进项目(npm i -D --save-exact),script 标签或 importmap 指向 node_modules。bin/vh hf-init 已经对 GSAP 这样处理,并会提示剩下的 CDN 引用。渲染外面再套一层看门狗 |
HyperFrames 渲染停在 5% 的 "Compiling composition",或者 snapshot、check 什么都不输出 |
字体栈里有没用 @font-face 声明的字体,HyperFrames 每次运行都会去 Google Fonts 请求(缓存了字体文件也照样请求),连不上就挂住。它别名表里的名字(Arial、Helvetica Neue、Menlo……)没写 @font-face 时,会被换成 Inter 或 JetBrains Mono,同样去取 |
每个字体都用 @font-face 加 src: local(…) 声明;#35 起 bin/vh hf-init 生成的脚手架已经这样写。网页字体想通过 npm 自己托管,见 README 的国内网络一节 |
第一次 hyperframes render 只显示 "Checking browser…",转了很久 |
它在下载 HyperFrames 自己管理的 chrome-headless-shell(约 100 MB,解压后约 200 MB,放在 ~/.cache/hyperframes/chrome),非交互的 shell 里不会显示进度;装了 Google Chrome 也一样要下 |
第一次渲染之前,在 HyperFrames 项目里先跑一次 npx hyperframes browser ensure:它会写明在下什么、多大。下不了就用上面的镜像设置 |
bin/vh new 提示"hyperframes init failed (offline?)" |
脚手架要用 npx 联网拉取 | 联网后重跑 bin/vh hf-init <项目目录>,画幅参数用 portrait、landscape 或 square
|
~/.claude/skills 里多出了 HyperFrames 的 skill |
它的 init 和 skills update 会装到全局 |
每次跑 npx hyperframes 之前先 export HYPERFRAMES_SKIP_SKILLS=1,也不要运行 npx hyperframes skills update。bin/vh hf-init 已经替你设好了 |
规格写了 font-weight: 800,PingFang SC 渲出来却不比 600 粗 |
macOS 的 PingFang SC 只有 6 个字重(100–600),700、800、900 都落到 600,逐像素相同,也不报错 | 写 600,并在 STYLE.md 里记下;真要 800,带一个有这个字重的字体(Noto Sans SC),或者换有更粗字重的系统字体(Songti SC 有 900) |
--format png-sequence 出的帧背景是透明的,psnr 命令读数偏高约 1.25 dB |
PNG 是 RGBA,html、body 和根元素的背景都没有画出来(alpha = 0),所以用它编出的 mp4 是黑底;psnr 把 alpha 通道当成零误差的第四个分量 |
做确定性检查时要心里有数;用 format=rgb24 逐对比较的写法在 playbook/02
|
hyperframes snapshot 把帧发给了 Gemini |
环境里有 GEMINI_API_KEY(或 GOOGLE_API_KEY)时,snapshot 默认给每一帧生成描述:帧离开你的电脑,还要计费 |
一律加 --describe false,仓库的文档和提示里本来就带着。check 不受影响 |
| HyperFrames 渲染突然都慢了 3 倍左右 | HyperFrames 把这台机器切到了较慢的截帧路径 | 设 HF_DE_PARALLEL_ROUTER=true
|
| 有一段画面一直停在 t=0,渲染却显示成功 |
renderAt 里抛了异常,比如在声明之前就读了一个 const
|
把每一帧和第 0 帧比 PSNR,或者在 draft 画面角落常驻一个时间读数;修掉异常 |
| 每个 worker 负责的那段,开头 1–2 帧是空的 | 异步构建还没完成就截了图 | 在普通 <script>(不是 module)里同步注册一个就绪 promise,第一帧画完后再 resolve |
| Three.js 里的视频屏幕渲出来是黑的 |
VideoTexture 没有更新 |
每一帧都设 texture.needsUpdate = true;视频按 t 跳转,并等跳转完成 |
| 镜头推近后字发虚 |
will-change: transform 让浏览器按小尺寸栅格化了 |
要被放大的元素不加 will-change,或者按放大后的尺寸排版 |
| 等宽字在 MP4 里变成了比例字体,快照里却是对的 |
ui-monospace, monospace 能过 lint,渲染时却不生效 |
用 @font-face 显式声明,src: local("SF Mono"), local("Menlo");判断字体要看渲出来的帧 |
| 合上音轨后画面被截短,或者丢了最后几帧 | 用了 ffmpeg 的 -shortest
|
用 bin/vh mux 合成,它会把音频补齐或截到视频的长度 |
| 饱和色在浏览器或手机上和在 ffmpeg、剪辑软件里看到的不一样 | mp4 没写色彩标签,或写的是 BT.601 的标签:旧的手绘引擎写的是 bt470bg,浏览器读错(饱和色偏 24–40 个色阶),ffmpeg 却读得对 |
编成有限范围的 BT.709,四个标签写全;不合格的 bin/vh check 会报出来。ffprobe 检查和 ffmpeg 命令见 playbook/02
|
| 看的其实是上一次的渲染结果 | 旧的输出文件还在 | 渲染前先删旧文件,或者核对文件的修改时间 |
bin/vh check 报出黑场、冻帧或静音 |
暗色背景、有意的停顿和静音片子都会触发 | 逐条看一遍再判断 |
| 现象 | 原因 | 怎么办 |
|---|---|---|
| 同一帧每次渲得不一样 | 用了 Math.random()、Date.now()、CSS transition 或 @keyframes,或者在帧之间保存了状态 |
随机用带种子的 hash(i),一切都从 t 算出来 |
用 --soft-gl 时,每次启动渲出的帧都不同(约 39 dB) |
Chrome 给 canvas 读回加了噪声 |
main 上已修复,git pull 即可 |
| 两次渲染的 MP4 一比,像是不确定(约 42 dB) | x264 会放大 GPU 光栅化的细微差异 | 比无损 PNG 帧:逐像素相同,或 PSNR ≥ 45 dB 且肉眼看不出,就算通过 |
| 同一个 HyperFrames 项目渲两次,文字边缘有几个像素不同,或者编码大小在体积上限处来回跳 | GPU 光栅化文字,每个 Chrome 进程都略有不同;带体积上限的 x264 用帧线程编码不可重复 |
hyperframes render --no-browser-gpu(走 CPU,慢一些)能让像素可重复,x264 用 -threads 1 能让码流可重复。样片渲染器两样都用了:研究笔记 03 有测量数据 |
| 手绘场景里静止的东西跟着抖 | 前面某个运动物体打乱了共用的随机数流 | 给每个元素设独立的 boilSeed()
|
| 现象 | 原因 | 怎么办 |
|---|---|---|
canvas 上的中文缺字,或者变成了系统字体,但 document.fonts.ready 已经就绪 |
中文网页字体是按分片加载的,某个字第一次画到时才去下载它所在的分片 | 每个字重调一次 document.fonts.load(…),把片中用到的所有字一次交给它;加载被拒、返回为空或超时都要大声报错 |
| 第一帧用了回退字体,排版跳了一下 | 字体还没加载完就截了图 | 第一次截图前先 await document.fonts.ready
|
| HyperFrames 的 lint 不接受中文字体 | Noto Sans SC 不在它的自动字体列表里 | 用 Google Fonts 的 <link> 引入,或者把字体文件放进 assets/ 并写 @font-face
|
来自冒烟测试和介绍片开场的实测,更多见 engines/blender.md。
| 现象 | 原因 | 怎么办 |
|---|---|---|
| Blender 一开渲就两分钟左右一行日志都没有,被看门狗杀掉 | 第一次在 Metal 上渲要静默编译内核(约 110 秒) | 给 Blender 放宽静默阈值(样片渲染器用 300 秒) |
| 套上沙箱后,Metal 每帧从 2 秒变成 13 秒 | Blender 没法把编好的着色器存进本用户缓存目录里它自己的子目录 | 放行 $(getconf DARWIN_USER_CACHE_DIR)org.blenderfoundation.blender 的写入 |
| 在一个进程里逐帧渲,某个物体有一段帧全黑,单独渲那一帧又正常 |
render.use_persistent_data 把状态带到了下一帧 |
每帧都由脚本改场景时关掉它,或者拿乱序重渲的帧来比对 |
| 长渲染挂在 agent 的后台任务上,渲到一半就断了 | Claude Code 的后台任务 30 分钟会被收掉 | 分块渲,每块一个新进程,已经存在的帧跳过,用 nohup caffeinate -i … & disown 脱离会话,showcase/04-intro-film/tools/bl_render.sh 就是这样写的 |
| 星点被抹成一团团糊 | OIDN 降噪 | 星空关掉降噪,靠采样数 |
| 叠在一起的玻璃卡片渲成黑块 | 光线穿过的层数超过了透射反弹上限 | 不需要玻璃的卡片把玻璃收掉 |
| 镜头接近垂直俯视时画面扭、抖 |
to_track_quat 在接近垂直时不稳定 |
自己算 forward 和 up 来构造旋转,相邻两帧的四元数取同号(q.dot(prev) < 0 就取反) |
| Blender 里的字字重不对 |
bpy.data.fonts.load() 只读 .ttc 的第一个字形 |
把要用的那一面写成单独的字体文件(styles/_swatch/blender_prep.py 用 fontTools 做) |
| 现象 | 原因 | 怎么办 |
|---|---|---|
qwen 念一句英文短句,念完还拖几秒含糊声 |
Qwen3-TTS 0.6B 停不下来,用 Ryan 音色时最明显 | 默认音色已换成 Aiden。加 --align gemini 把这种句子标出来,然后重跑、换 1.7B 模型(QWEN_TTS_MODEL)或改用 gemini
|
Linux 上 say 或 qwen 用不了 |
say 只有 macOS 有;qwen 需要 Apple Silicon |
改用 edge 或云端 provider |
--align gemini 停了一分钟左右又接着跑 |
Gemini Tier 1 每分钟只能转写 10 次(HTTP 429) | 不用管:它会按接口要求的时间等待,最多重试 5 次。要求等 90 秒以上的是日配额用完了,会直接报错 |
--align gemini 跑到一半停了(日配额、网络) |
有一次转写彻底失败了 | 已经合成的配音保留,命令以退出码 1 结束并说明原因。用同一条命令加 --resume:只补缺的部分,已经成功的句子不会再花一次钱 |
| 提示"set GEMINI_API_KEY" | 环境变量里没有这个 key | 在 shell 里 export GEMINI_API_KEY=…,不要写进仓库里的任何文件 |
bin/vh tts <p> --provider edge 报错"No such file or directory: 'edge-tts'" |
旧版 bin/vh 只按位置参数选依赖 |
git pull(已修复),或者把 provider 写成位置参数:bin/vh tts <p> edge
|
bin/vh qa 因为片中的数字静音不通过 |
戏剧性的停顿用了真静音 | 保留一层垫音(sub 或 pad),把滤波收下来,撤掉鼓,再用渐强拉进下一个重拍 |
bin/vh qa 报抽吸,或者音乐在句间掉下去 |
每个音效都接进了 ducker,或者 duck_ratio 太高 |
用默认值(duck=voice、duck_ratio=1.6);用 duck=on 时把 ratio 降到 2–3。给 qa 加 --voice voiceover.wav,旁白下面设计好的让位就不会被算进去 |
bin/vh qa 打印的混音报告里有 LOW、HIGH、OVER-VOICE、MASKS-VOICE 或 BURIED
|
用 profile= 混的音,某一句、某个音效或某个词出了范围;BURIED 是指卡点对上了、但听不见 |
先看"why"那一列。按标记怎么改,见 playbook/04 的"怎么读混音报告":重录那一句或让配乐在那里收一收,挪动音效或重新分类(role),提高 gain_db,低频的重击要加一层 1–4 kHz 的起音,不要靠增益 |
| AAC 编码后的文件真峰值高于 −1.5 dBTP | AAC 编码会抬高真峰值,密一点的音乐在 128k 下最多约 +1.4 dB | 用 ffmpeg -i final.mp4 -af ebur128=peak=true -f null - 量编码后的文件,用更低的 tp= 重混;样片渲染器会自己做这一步 |
| 浏览器要等整个视频下完才开始播 | 索引(moov)在文件末尾 |
用 bin/vh mux 重新封装,它现在会把索引写在开头 |
| 画面里烧了字幕,播放器却又打开了一条字幕轨 | MP4 封装器总会把第一条字幕轨标成启用 | bin/vh mux --subs-off |
bin/vh readcheck 判重复旁白的烧录字幕不合格 |
它们被按上屏文字的规则查了,那条规则假定读得更慢 | 在字幕元素或它所在的场景上标 data-read="subtitle",就按字幕的阅读速度查 |
| 一条字幕不到 1.8 秒就闪过去了 | 念的那一句太短 | 重新跑 bin/vh captions:它会把短字幕往后面的空白里延长,最多到下一条字幕或画面结束 |
bin/vh qa 列出了一些 click |
机器分不清设计好的尖锐起音和真正的故障 | 这只是警告:用耳朵把最严重的 10 处听一遍 |
bin/vh qa 提醒某个声音在重复 |
同一个声音连着三次以上一样:钉死了 variant,或者用的是同一个文件 |
去掉钉死的 variant,几个 take 轮着用,或者换一个转场家族;bin/vh sfx audition 可以先听几个选项 |
bin/vh mix 报告限幅器压了很多峰 |
音效或人声的峰值太高 | 调低 sfx_db,不要靠限幅器硬压 |
这里没有的问题,请开一个 issue,附上命令、输出和 bin/vh doctor 的结果。