bdsub
杰科 G5300 · 物理碟 · 无光驱

网上下载的字幕
怎么对齐到碟片

手里只有蓝光碟,电脑读不了碟,字幕时间轴又对不上——这个场景不需要光驱。 用一部片 10 分钟、三个时间点、一条命令,把偏移和漂移一次算清。 下面这套流程已经在 G5300 上跑通,工具是单文件 Python,无依赖、离线可用。

01

先判类型,再决定要不要动手

错位分三种,只有前两种能靠算法解决。判错了会白忙一场。

最省事的判法:比片长

碟片总时长(碟盒背面或碟机信息页)和字幕末条时间比一下。

现象判定怎么办
两者相差 < 1 分钟基本对齐,或轻微平移1 个锚点
碟片比字幕长 4%~5%B 类 · PAL 加速--ratio 1.0416667 一键解
碟片比字幕长很多(>10 分钟)大概率不同剪辑版先换字幕,别调
片长几乎相同但对不上A 类 · 整体平移1 个锚点
字幕末条通常停在正片最后一句对白,比碟片总时长早 1~3 分钟(后面是 credits)。 所以「碟片时长 − 字幕末条」落在 1~3 分钟之间是正常的,别当成错位。

最准的判法:三点法

记三个点,看差值怎么变——差值恒定是 A 类,线性变大是 B 类,某处突然跳变是 C 类。

A整体平移
现象
全片每句都差同一个量
成因
片头厂标、logo 长度不同;字幕组自带偏移
可解
100%
1 个锚点 → fit
B速率漂移
现象
片头差得少,片尾差得多
成因
24↔25 帧率转换(+4.167%)、23.976↔24
可解
100%
3 个锚点 → fit
C结构错位
现象
差值在某处突然跳变,残差 RMS > 1s
成因
加长版 / 剧场版 / 不同剪辑
可解
≈ 0
换字幕,别调
C 类不存在一个线性变换能对齐。工具判定为 C 时会直接拒绝写文件(确需强行套用加 --force)—— 这时候正确的做法是换一份对应版本的字幕,而不是继续拟合。
02

完整流程 · 一部片约 10 分钟

顺序很重要:先在字幕里挑句子,再去看片。

  1. 准备 1 分钟

    字幕放 U 盘根目录(物理碟只认根目录)。Mac 上留一份同样的文件,命名如 星际穿越.chs.srt。

  2. 在字幕里挑三句台词 1 分钟

    片头 3~6 分钟一句、片中一句(避开打斗和纯音乐)、最后一段对白一句。 注意是最后一段对白,不是字幕文件的最后一条——最后一条往往已经进了 credits,碟上根本没有对应画面。

    ./bdsub.py find 星际穿越.srt 库珀
    # → [ 128] 00:04:38,000 --> 00:04:40,500

    把三句的字幕时间码抄下来:① 04:38 ② 52:30 ③ 1:51:40

  3. 播碟采点 5~8 分钟

    碟机进正片后,听到台词出口的瞬间按暂停 → 调出 OSD 读已播时间 → 记成 碟=字 的形式。 三个点重复三次,得到 --anchor 4:47=4:38 这样的三行。

    按暂停会慢 0.3~0.8 秒,但三次都慢同样的量,这个恒定误差会被偏移量吸收。 所以单点误差 1 秒以内都无所谓,别为了 0.1 秒反复倒带。

  4. 拟合,先只看不写 10 秒

    ./bdsub.py fit 星际穿越.srt \
      --anchor 4:47=4:38 \
      --anchor 54:38=52:30 \
      --anchor 1:56:16=1:51:40 \
      --dry-run
    锚点数        3
    缩放 a        1.041580  (+4.1580%)
    偏移 b        -2.708 s
    残差          +0.15s  -0.27s  +0.12s   (RMS 0.191s)
    判定          B 类 · 速率漂移 ≈ PAL 加速 24→25 (+4.167%)

    残差 RMS < 1 秒 → 可用。确认无误后去掉 --dry-run,加 --out 星际穿越.chs.fixed.srt 出文件。

    方向只有一种正确写法:碟片时刻=字幕时刻。 写反了 a 会变成 0.77 这种数字,工具会直接提示。

  5. 回盘验证 3 分钟,别跳过

    拷到 U 盘根目录,把原件移走(不移走的话字幕菜单里两个文件并排,容易选错),重放检查三点:片头、片中、片尾。 任一点偏差 > 2 秒,说明那个锚点采错了,重采那一个点再跑一次。

已经确定是 PAL 加速的话,比率是精确值,不用测,一个锚点就够: --anchor 4:47=4:38 --ratio 1.0416667
03

不想开终端?在手机上直接算

和 bdsub.py 同一套算法、同一个阈值,结果应完全一致。算完可直接复制命令。

①
②
③
至少填 ① 一组;要算漂移必须两组以上

为什么只填一组不够?一个点只能算出「整体差多少」,算不出「差距随时间怎么变」。 判断有没有漂移、是不是 C 类,至少要有两组、最好三组。跨度 < 20 分钟时算出的 a 不可靠,工具也会提示。
04

零点几秒的偏差:不能测,只能挑

这一类有物理上限,方法也必须换。

锚点法的误差来自「按暂停的反应延迟」,约 ±0.5 秒。要修的量若只有 0.2~0.4 秒, 测量误差比被测量还大,算出来的是噪声——一个「看起来精确」的错数,改完反而更糟。

第一步:先判断要不要改

情况处理
字幕比声音早 0.3 秒内不要改。观众是听着声音读字,眼睛需要提前量,字幕略早是行业惯例
字幕比声音晚 0.2 秒内多数人可接受
字幕比声音晚超过 0.3 秒值得改
方向在不同段落不一致不是固定偏移,是字幕本身的质量问题,整体平移会拆东墙补西墙

参考阈值:唇音同步的可察觉窗口是「声音超前 45 ms ~ 滞后 125 ms」(ITU-R BT.1359-1)。 字幕比唇音同步更宽容,因为它走阅读通道,不是唇形匹配。

第二步:扫值,一次生成全部候选

./bdsub.py sweep 星际穿越.srt --from -0.4 --to 0.4 --step 0.1

生成 9 个文件,一次全拷到 U 盘根目录:

星际穿越_0.0s.srt   ← 原时间轴,留作基准
星际穿越_m0.1s.srt  m0.2s  m0.3s  m0.4s   ← 字幕往前提
星际穿越_p0.1s.srt  p0.2s  p0.3s  p0.4s   ← 字幕往后推

播放到选定片段,用遥控器【字幕】键在菜单里逐一切换,挑最贴的那个。 这一步的全部价值,是把 N 次「改文件 → 拷 U 盘 → 插碟 → 重播」压成 1 次。 单次往返 3 分钟,9 次就是半小时。

为什么试错反而比测量准:人眼判断「先后顺序」的精度远高于判断「绝对时间差」。 「这句字幕和这个人开口,哪个先?」你答得对,误差 < 100 ms;「差了多少毫秒?」答不出。 把测量换成比较,等于把高误差任务换成了低误差任务。

第三步:知道什么时候停

23.976 fps 一帧 = 41.7 ms,碟机字幕渲染还有 1~2 帧延迟。 所以 ±0.1 秒(约 2.4 帧)就是实际可达的极限,做到这儿就收工。

命令行的一个坑:负数要写成独立的参数值——--from -0.4 可以, --range -0.4:0.4 会被当成选项名而报错。
05

ASS / SSA 与 Windows

两件事都不是简单的「改个后缀」。

ASS 能直接用,但写法有讲究

工具直接支持 .ass / .ssa,所有命令都能用,不需要先转换。 关键是写回方式:只替换 [Events] 里每条 Dialogue: 的 Start / End 两个字段, 其余字节原样保留——样式表、字体、颜色、Comment: 行、BOM、CRLF 都不动。 如果按「重新生成」的方式写回,样式全丢。

实测:一次校正后全文件仅 4 行变动,且变动只发生在时间字段上。时间格式自动按 ASS 规范输出 H:MM:SS.cc。

字幕类型建议
纯对白,只有字体/颜色保留 ASS,样式能用就用
带 \pos 强行定位、\move 滚动convert 成 SRT,碟机渲染常常不认,位置会飘
动漫特效字幕、屏幕字转 SRT 后这些内容会混进对白里,不如换一份普通字幕
./bdsub.py convert 字幕.ass 字幕.srt

Windows

纯标准库单文件,macOS / Linux / Windows 通用,不需要装任何依赖。把 python3 换成 py -3 即可:

py -3 bdsub.py list 字幕.srt --tail 5
py -3 bdsub.py fit 字幕.srt --anchor 4:47=4:38 --anchor 54:38=52:30 --dry-run

路径含空格或中文要加引号:py -3 bdsub.py fit "D:\字幕\星际穿越.srt" ... 极老的英文控制台可能把中文显示成 ?,那只是显示问题,不影响计算和输出文件。

06

边界与已知限制

知道它做不到什么,比知道它能做什么更重要。

项说明
支持格式SRT / ASS / SSA。SUB(VobSub 图形)、IDX、PGS 不在范围内
编码自动识别 UTF-8 / GBK / BIG5(按常用字命中率择优),输出保持原编码,--enc 可强制
不改帧率标记只动时间戳,这正是我们需要的
遥控器做不到的时移能做整体平移;速率缩放和分段只能靠本工具
结构性错位无解。不同剪辑版之间不存在线性变换
片源必须有字幕文件。这里全程不需要读碟、不需要光驱

什么时候才该考虑买光驱

出现以下任一情况,说明手工锚点已经不划算,再考虑 UHD 光驱 + MakeMKV:

信号含义
要校正的片子 > 20 部人工成本超过硬件成本
对白极稀疏(纯音乐、纪录片)锚点难找,误采率高
遇到 C 类且必须救需要拿碟内 PGS 时间轴当 reference,走 pgs2ref.py + alass

否则,一部片 10 分钟的人工成本,比千元光驱加每部片一小时的读盘时间划算得多。 物理碟版本是永久固定的,调好一次终身复用——这是相对流媒体的实质优势。

07

下载

三个文件都是纯文本加标准库,下载后可直接离线使用。

curl -O https://geziyichen.fun/bdsub/bdsub.py
curl -O https://geziyichen.fun/bdsub/pgs2ref.py
curl -O https://geziyichen.fun/bdsub/bdsub-sop.md

命令速查

./bdsub.py find  字幕.srt 台词片段        # 查台词在字幕里的时间码
./bdsub.py list  字幕.srt --tail 5        # 看结尾几条(确认末条是否已进 credits)
./bdsub.py list  字幕.srt --from 5:00 --to 6:30
./bdsub.py fit   字幕.srt --anchor 碟=字 --anchor 碟=字 --dry-run
./bdsub.py fit   字幕.srt --anchor 碟=字 --ratio 1.0416667
./bdsub.py fit   字幕.srt --anchor ... --keep-duration   # 只移起点不改显示时长
./bdsub.py check a.srt b.srt              # 对比两份字幕,判断是否同版本
./bdsub.py convert 字幕.ass 字幕.srt      # ASS → SRT(去特效标签)
./bdsub.py sweep 字幕.srt --from -0.4 --to 0.4 --step 0.1

时间格式接受 1:51:40、01:51:40,000、111:40,会自动补零。 把 .srt 换成 .ass / .ssa,其余命令完全一样。

无依赖 离线可用 保留原编码 ASS 结构不动 拒绝覆盖源文件