Wjs Burning Subtitles — Wjs 烧录字幕
v1当用户拥有一个视频和一个SRT文件,并希望字幕被烧录到像素中(libass,始终可见)或作为可切换的轨道进行软混流时使用。同时,也处理本地化流水线的最终合成步骤——烧录字幕,混合配音轨,并将原始音频作为低音量的底层音频,都在一个ffmpeg编码中完成(无级联)。验证libass的可用性,并在Homebrew的剥离二进制文件缺少它时自动下载一个静态的evermeet ffmpeg构建。触发器 — "烧字幕", "硬字幕", "burn subtitles", "burn-in subs", "embed subtitle", "soft mux SRT", "把字幕烧进视频", "做最终合成"。
运行时依赖
版本
使用curl命令下载ffmpeg的zip包:curl -fsSL -o /tmp/ff.zip https://evermeet.cx/ffmpeg/getrelease/zip
安装命令
点击复制技能文档
wjs-burning-subtitles 视频 + SRT → 带字幕的视频。同时也是本地化流水线的最终编码阶段:它接受一个视频、一个可选的配音轨道(来自 /wjs-dubbing-video)和一个可选的 SRT 文件,并在一次 ffmpeg 传递中生成上传准备好的 MP4 文件。没有解码/重新编码的级联。当使用 用户有一个 SRT 文件并希望它始终可见于视频上(微信视频号 / 抖音 / WeChat 等播放器不支持嵌入式字幕轨道)。 用户希望有一个可切换的字幕轨道(软混)用于 QuickTime / VLC / IINA / 支持 mov_text 的移动播放器。 /wjs-dubbing-video 的最终合成:一次编码中烧录目标语言字幕 + 混合配音到原始音频中。 何时不使用 没有 SRT 文件 → 先运行 /wjs-transcribing-audio 然后 /wjs-translating-subtitles。 HTML/CSS 字幕(动态、逐字高亮、自定义字体)在 HyperFrames 中组合的片段 → 使用 /wjs-overlaying-video 代替。 不要在同一个输出中混合 libass 烧录和 HyperFrames 字幕。 “字幕”实际上是动态图形(动画呼叫、带有标志的下三分之一、动态字体)→ 这是 /wjs-overlaying-video,而不是这个技能。 render.py 脚本的 3 种模式 render.py 自动检测模式从标志: 字幕仅 — —video + —srt → 重新编码视频以烧录字幕,原始音频通过。 配音仅 — —video + —dub → 保留原始视频流;替换或混合音频轨道。 全部本地化剪辑 — —video + —srt + —dub → 烧录字幕并混合配音。 默认情况下,在配音下保留原始音频作为低音量的“床”(设置 —bed-volume 0 或 —no-original-audio 以删除它)。 烧录需要一个带有 libass 的 ffmpeg 构建。 脚本自动下载一个静态 libass 启用的构建到 /tmp/ff_bin/ 中,如果需要的话,在第一次使用时。 软混(可切换字幕轨道) 播放器应用程序可以显示/隐藏。 与任何 ffmpeg 构建一起工作 — 不需要 libass: ffmpeg -i input.mp4 -i input.zh-CN.srt \ -map 0:v -map 0:a -map 1:0 \ -c:v copy -c:a copy -c:s mov_text \ -metadata:s:s:0 language=zho -metadata:s:s:0 title="中文" \ output.mp4 这是快速(流复制)且可逆的。 当使用 目标平台支持嵌入式字幕(YouTube 自动检测;VLC/QuickTime 支持)。 用户希望查看者能够切换字幕。 您不想重新编码视频。 render.py —video IN.mp4 —srt SUB.srt —soft-mux 运行此路径。 硬编码烧录(始终可见,libass) 适用于 WeChat/抖音/朋友圈 等播放器不支持嵌入式字幕轨道。 在承诺烧录之前验证 libass 是否可用 ffmpeg -filters 2>&1 | grep -E "subtitles|^.. ass " 如果没有显示字幕或 ass,则构建缺少 libass。 Homebrew 的默认 ffmpeg 公式通常被剥离(没有 —enable-libass,没有 —enable-libfreetype,没有 drawtext)。 不要浪费时间与 force_style 内部的逗号转义作斗争 — 它将以 No such filter: 'subtitles' 失败,无论 shell 如何引用它。 macOS 上最快的解决方案 — 放入一个静态构建,不进行系统更改 curl -fsSL -o /tmp/ff.zip https://evermeet.cx/ffmpeg/getrelease/zip unzip -o /tmp/ff.zip -d /tmp/ff_bin >/dev/null FF=/tmp/ff_bin/ffmpeg $FF -version | grep -oE —“enable-(libass|libfreetype)” 然后使用 $FF 代替 ffmpeg 进行渲染。 brew 二进制文件适用于所有其他内容(探测、音频提取、软混)。 render.py 执行此自动回退,如果其默认 ffmpeg 缺少 libass。 带样式覆盖的烧录渲染 检查点 — 确认之前的全渲染。 烧录重新编码整个视频(5 分钟片段的 CPU 上需要几分钟)。 在启动之前: 渲染前 30 秒的视频,使用 -t 30 进行快速预览。 从最长行提示中提取一个帧(见下面的字体大小校准)。 阅读它。 向用户显示预览帧 + 提示文本,询问:“字号/字体/边距 OK 吗?OK 才跑全片。” 等待显式确认。 仅在用户已经在同一对话中批准了此确切视频的全渲染并使用相同的字体配置时,才跳过检查点。 $FF -i input.mp4 \ -vf “subtitles=input.zh-CN.srt:force_style='Fontname=PingFang SC\,Fontsize=12\,PrimaryColour=&H00FFFFFF\,OutlineColour=&H00000000\,BorderStyle=1\,Outline=2\,Shadow=1\,MarginL=20\,MarginR=20\,MarginV=40'” \ -c:v libx264 -crf 18 -preset medium -pix_fmt yuv420p \ -c:a copy output.mp4 在 force_style 内部,转义每个逗号为 \,(过滤器图形解析器将裸逗号作为链式分隔符)。 所有其他特殊字符都可以。 字体大小校准 — 关键 libass 将其内部 PlayRes 缩放到实际视频分辨率。 传递的数字不是输出中的像素。 作为 544×960 垂直手机视频的起始校准,Fontsize=22 将每个中文字符渲染为 ~55px 宽并溢出帧,而 Fontsize=12 将渲染为 ~30–35px 宽并且干净地适合 15 个字符的行。 经验法则:从 Fontsize=12 开始,渲染,然后始终提取一个帧并查看: $FF -ss 30 -i output.mp4 -frames:v 1 /tmp/frame.png -y # 然后阅读 /tmp/frame.png 以验证最长行