Skip to content
Andy Xuming edited this page Jan 10, 2018 · 2 revisions

如何在 Linux 命令行上调整字幕文件时间轴

最近整理了一批BD复刻的中古二次元视频,网上搜来的字幕文件大多是DVD版甚至TV版,时间轴错位严重,需要自己调整。常用的字幕文件如srt,ass都是普通文本文件,我估计一个小型过滤程序足以应付这点事情,Linux上应该有大把脚本。上网搜了一圈工具软件,出乎意料,居然没有基于命令行的,Subtitle Workshop之类软件只支持Windows,Gnome Subtitles 又过于庞大,我只需要批处理一下时间轴,而不是编辑字幕文件,于是自己写一个程序 subsync 处理这种情况。

这是个简单的文本过滤程序,就算重新发明轮子也耽误不了太多时间,思路很简单,在字符流里面寻找诸如 1:16:34,123 这样的时间戳,加减乘除后送回字符流既可。调整时间轴有三大类场景。一是时间轴偏移,本应该在1分11秒出现的字幕,因为视频多了5秒的片花,导致提前5秒出现。处理办法是让时间轴整体推迟5秒。二是时间轴延伸,影片刚开始时字幕是同步的,越往后偏的越厉害,到片尾时字幕可能提前或滞后十几秒。这是由于不同版本的视频帧率不同造成的,处理办法是对每个时间戳乘上一个系数进行补偿。三是局部处理,视频前面字幕是同步的,从某个时刻开始忽然整体提前或滞后了。这是因为不同版本插入了不同的片花或插曲,处理办法是只对这个时刻开始的时间轴做偏移处理。subsync 支持以上三种处理方式,支持 .srt 和 .ass/.ssa 格式的文件。

安装和编译

subsync 用 C 基于标准 C 运行库写成,仅仅 600 多行,可以在任何环境编译运行。程序在 github 上开源托管,用如下命令安装和编译:

git clone https://github.com/xuminic/subsync.git
cd subsync
make

如果没有/不想装git也无妨,直接从 https://github.com/xuminic/subsync 的 Clone or download 按钮里面选 Download ZIP 下载。既然你找上了 Linux 命令行,想必编译器和DIY精神总是有的,其他细节一定OK。

时间轴偏移

时间轴偏移就是把字幕时间戳加上或减去一个指定的时间,例如

subsync +12000 < source.ass > target.ass

意思是把 source.ass 的时间轴整体加上 12000 毫秒,等于字幕滞后 12 秒。这里的 ‘+’ 号即是命令行选项的分割符,也是增加的意思。相应的如果改成 -12000,那就是时间轴整体减去 12000 毫秒,等于字幕提前 12 秒。subsync 是文本过滤程序,默认输入输出是控制台,你也可以选择 -w 参数输出到文件,或者参数区后给出文件名作为输入,例如上面的命令等价于这个命令:

subsync +12000 -w target.ass source.ass

时间戳可以是一个整数,代表毫秒,也可以是时分秒格式,例如要求字幕提前10分7秒570毫秒:

subsync -00:10:07,570 source.ass > target.ass

这里的 ‘-’ 代表字幕提前,换成 ‘+’ 则字幕滞后。时分秒格式目前支持两种风格,SRT 风格的 00:10:07,570,用逗号分隔毫秒段,单位是1毫秒;ASS 风格的 00:10:07.57,用句号分隔毫秒段,单位是10毫秒,因此 57 是 570 毫秒;注意代表小时的 00 不能省。

还有一个更简单的算术格式,用期待时间戳减去实际时间戳。例如手头已经有一个同步的英文字幕和不同步的中文字幕,随便找条对应的字幕,用英文字幕时间减去中文字幕时间:

subsync +00:00:52,570-0:11:00,140 source.ass > target.ass

注意,算术格式处理的字幕是提前还是滞后,由计算结果决定。领头的 ‘+’ 号仅仅是命令行选项的分割符,不代表正负,因此换成 -00:00:52,570-0:11:00,140 结果也是一样的。

时间轴延伸

时间轴延伸就是把字幕时间戳乘以一个指定的系数,例如

subsync -1.000955 source.ass > target.ass

这个系数大于1时,时间轴整体延长,小于1时,时间轴整体缩短,负系数没有意义,因此这里的 ‘-’ 号仅仅是命令行选项的分割符,换成 +1.000955 结果也是一样的。时间轴延伸和时间轴偏移用同样的命令行开关,区别是时间轴延伸的参数是浮点数,而时间轴偏移的参数是整数。

系数是这样计算的。一部视频的开始字幕同步,结尾字幕不同步,表明时间轴需要延伸,开始字幕不用管,找到结尾字幕,例如字幕出现在 01:35:32,160,而对应的场景出现在 1:35:26,690,字幕滞后了6秒。01:35:32,160 换成毫秒是 5732160,1:35:26,690 换成毫秒是 5726690,用 5732160 除以 5726690 得到系数为 1.00095517655050299562,取 1.000955 即可。

由于时间轴延伸系数计算麻烦,subsync 同样支持算术格式,用期待时间戳除以实际时间戳。以上面系数为例:

subsync -01:35:32,160/1:35:26,690 source.ass > target.ass

注意领头的 ‘-’ 号仅仅是命令行选项的分割符,换成 +01:35:32,160/1:35:26,690 结果也是一样的。

题外话,这个时间轴飘移的非常小,不像是 NTSC 30 帧 和 PAL 25 帧的比值,有可能是24帧和23.976的差别,24/23.976=1.001001,和 1.000955 非常接近,用 1.001001 作为命令行参数送进去,效果和 1.000955 看不出差别。

如果出现既需要偏移,又需要延伸的字幕(我就碰到过一回),可以在一个命令行解决,subsync 的处理顺序是先偏移,再延伸,由于偏移值通常很小,乘以系数后的误差可以忽略不计:

subsync +00:00:52,570-0:11:00,140 -01:35:32,160/1:35:26,690 source.ass > target.ass

注意领头的 ‘+’ 和 ‘-’ 号仅仅是命令行选项的分割符,不代表加减,可以互换使用。

局部处理

局部处理比较简单,用 -s 参数选择一个时间范围,格式是

-s 起始时间 [结束时间]

时间戳可以是一个整数,代表毫秒,如 607570。也可以是时分秒格式,如 00:10:07,570。结束时间是可选参数,如果没有给出,就默认为处理到片尾。例如:

subsync -s 0:01:15.00 -00:01:38,880-0:03:02.50 source.ass > target.ass

意思是从1分15开始,后面的时间戳全部提前 83.62 秒。包含结束时间的示例如下:

subsync -s 0:01:15.00 1:23:34.00 -00:01:38,880-0:03:02.50 source.ass > target.ass

批处理

批处理是 subsync 的附加功能,对于连续剧之类的场景,大部分情况下,第一集的时间轴调校参数可以应用到其他集,批处理可以把所有字幕文件一次性全部校正。

用 shell 脚本也可以实行批处理,例如:

 for i in *.srt; do subsync +12000 $i > $i.new; done

但是 shell 脚本留下一堆中间文件,需要仔细调整才能消除。对应的 subsync 提供 -o 参数,输出文件可以直接覆盖输入文件,例如:

 subsync -00:00:01,710-00:01:25,510 -o  *.srt

当然这样一来原文件就丢失了,如果想保留原文件,或者需要反复调校的,请做好备份。

案例

EVA3.3 剧场版

以 EVA3.3 剧场版为例,该剧场版是1小时32分52秒版本。抓到一个中文ASS文件00002.v1.11_FINAL.ass,翻译质量很好,但是时间轴对不上。仔细看了看ASS文件,最后一行是

Dialogue: 0,1:45:42.51,1:45:46.48,Comment,,0,0,0,,♫Peace in time we've never had it so good\N安享和平 生活从未如此美好

多出10分钟,是带巨神兵的版本。好在该剧场版外挂了英文字幕(不然只能靠耳朵听,手工找到对应字幕了),找到第一个有效字幕:

2
00:00:52,570 --> 00:00:55,740
Tracking team,
report current Eva unit positions.

对应的00002.v1.11_FINAL.ass里面是

Dialogue: 0,0:11:00.14,0:11:02.98,Default,,0,0,0,,追踪班 报告两机体现在的位置

用subsync计算一下偏差:

$ subsync --help-sub 00:00:52,570 0:11:00.14
Time difference is -00:10:07,570 (-607570 ms)

用这个差值测试一下效果(当然先删掉巨神兵的10分钟字幕):

$ subsync -00:10:07,570 00002.v1.11_FINAL.ass > 001.ass

测试下来一开始是同步的,后面字幕越漂越远,影片后面半小时完全靠脑补,估计是帧率问题。打开英文字幕,找到最后一个有效字幕:

988
01:35:32,160 --> 01:35:33,700
The <i>Wunder</i> streaks
through the sky.

在001.ass里面找到对应的字幕,幸好有一个Wunder,比较好找:

Dialogue: 0,1:35:26.69,1:35:28.07,Default,,0,0,0,,划破天际的Wunder

可以看出来大概漂了6秒。处理办法就是给每条字幕的时间乘以一个系数,这个系数也可以用subsync算:

$ subsync --help-div 01:35:32,160 1:35:26.69
Time scale factor is 1.000955

用这个系数测试一下效果(这个系数和24/23.976=1.001001非常接近,估计帧率差是24比23.976,所以用1.001应该也可以)

$ subsync -1.000955 001.ass > 002.ass

测试下来一切正常。上述步骤也可以用一条命令直接产生,误差对实际效果的影响可以忽略。

subsync +00:00:52,570-0:11:00,140 -01:35:32,160/1:35:26,690 00002.v1.11_FINAL.ass > 002.ass

注意用+00:00:52,570-0:11:00,140格式产生差值,或用-01:35:32,160/1:35:26,690格式产生系数的时候,都是用期待时间减/除以实际时间。

不可思议的海之娜蒂亚

另外一个稍微复杂的例子,不可思议的海之娜蒂亚,39集的动画。片源来自西人网站,字幕文件来自中文网站,片头的叙述段字幕同步,正片段则完全不同步。比较有趣的是片源自带英文字幕,正好用ffmpeg剥离分析:

ffmpeg -i "Nadia Ep 01.mkv" -map 0:s:0 subs.srt

-map 0:s:0 是剥离第一个字幕,剥离第二个字幕是 0:s:1,以此类推。打开subs.srt和中文字幕文件比较,第一句是一致的:

1
00:00:03,200 --> 00:00:05,900
<font face="InfoDispBoldTf" size="46" color="#fffde1"><i>Are you adventurers,</i></font>

而ASS字幕

Dialogue: 0,0:00:03.50,0:00:05.50,*Default,,0000,0000,0000,,你是一位冒险家吗

但是从第18句开始就错位: 18 00:01:09,630 --> 00:01:12,460 as the threat of a world war loomed ever closer.

19
00:01:38,880 --> 00:01:41,040
<font face="InfoDispBoldTf" size="46" color="#fffde1">Paris... Paris...</font>

而ASS字幕

Dialogue: 0,0:01:08.00,0:01:11.80,*Default,,0000,0000,0000,,但是人们生活在 即将来临的世界阴影中
Dialogue: 0,0:03:02.50,0:03:04.90,*Default,,0000,0000,0000,,巴黎…巴黎…

影片中18和19两句间隔30秒,而中文字幕间隔了2分钟,可能中间多个片首曲。

用subsync的-s参数,可以局部处理字幕文件,格式是

-s 起始时间 [结束时间]

结束时间是可选参数,如果没有给出,就默认为处理到片尾。根据两个字幕文件的比较,参数已经齐全,从 0:01:15.00 开始,字幕时间提前 00:01:38,880-0:03:02.50,命令行格式为:

subsync -s 0:01:15.00 -00:01:38,880-0:03:02.50 "Nadia Ep 01.ass" > 01.ass

测试下来已经同步了,说明帧率是一致的。

其他字幕文件没有局部处理,简单提前时间轴即可。首先打开字幕文件,删除前47行,注意字幕文件是基于utf-8编码,文件头部隐含了EF BB BF三个字节,不可删去。用同样的方法处理,可以得知时间轴所需偏移是 -00:00:01,710-00:01:25,510,因此用

subsync -00:00:01,710-00:01:25,510  "Nadia Ep 02.ass" > 02.ass

即可。如果是SRT字幕则用 -r 参数恢复序列号:

subsync -00:00:01,710-00:01:25,510 -r "Nadia Ep 02.srt" > 02.srt

如果预先处理好了字幕文件,已经删除前47行,也可以执行批处理:

subsync -00:00:01,710-00:01:25,510 -r -o  *.srt

注意 -o 参数直接覆盖原有文件,如果想留备份文件,则应该使用 -w 代替。