全部文章
指南· 作者:Ray Mac

VTT 文件是什么?WebVTT 格式、真实示例,以及浏览器实际显示了什么

VTT(WebVTT)是网页视频的字幕格式。本文讲清格式和真实示例,并实测 Chrome 与 Safari 对 17 项功能、18 种损坏文件的处理。

VTT 文件(扩展名 .vtt,全称 WebVTT,Web Video Text Tracks)是网页视频用的字幕格式。 它和 SRT 一样是纯文本:一串字幕条目(cue),每条有开始时间、结束时间和要显示的文字。 在此之外,它还能带定位、样式和注释。HTML5 的 <track> 元素只接受这一种字幕格式,所以网页里的视频播放器都认它。

大多数介绍写到"VTT 支持样式和定位"就结束了。我们想知道这些附加功能到底能不能显示出来,于是每个功能单独写一个测试文件,分别在 Chrome 155 和 Safari 26.2 里播放,其中 5 个还在 iPhone 上播放了一遍。 然后又把 VTT 文件按 18 种常见方式改坏,交给两个浏览器、ffmpeg 和我们自己的转换器处理。结果在格式介绍之后。

VTT 文件长什么样

下面是一个真实文件的前三条:ScribeToAny 给教程里那段 64 秒面包店采访生成的字幕。 内容和我们 SRT 示例里的完全一样,只是导出成了 VTT。

WEBVTT

00:00:00.000 --> 00:00:01.680
Welcome back to Small Shop Stories.

00:00:02.160 --> 00:00:05.320
Today I'm talking to Maya, who runs a tiny bakery on the

00:00:05.320 --> 00:00:06.320
corner of Mill Street.

VTT 文件由两类块组成,块之间用空行隔开:

  1. 文件头。 文件必须以大写的 WEBVTT 开头。同一行后面可以空一格再写说明(WEBVTT - Kitchen interview),然后是一个空行。
  2. 字幕条目。 每条依次是:可选的标识行、时间行、一行或多行文字。时间行写作 开始 --> 结束,毫秒前用点号:00:00:05.320 就是 5.32 秒。小时可以省略,所以 00:05.320 也合法。结束时间后面同一行的内容是字幕设置,比如 line:0。

文件必须是 UTF-8 编码,没有别的选项。

字幕条目之间还可以出现三种块:NOTE(注释,永远不显示)、STYLE(给字幕用的 CSS)、REGION(屏幕上一块有名字的区域,字幕可以在里面滚动)。

VTT 和 SRT 的区别

字幕文字本身通常一模一样,所以普通字幕在两者之间转换不会丢东西。区别都在文字外面:

VTTSRT
第一行WEBVTT(必需)序号 1
毫秒分隔符点号:00:00:05.320逗号:00:00:05,320
序号可选的标识,任意文字默认要有,1、2、3……
时间里的小时可省略必需
定位字幕设置(line、position、align、size、vertical)格式里没有
样式<b>、<i>、<u>、class、说话人、STYLE 块里的 CSS<b>、<i>、<u>,很多播放器还认 <font>
注释NOTE 块无
能在网页里播放能,通过 <track>不能
规范W3C无

简单说:视频在浏览器里播放就用 VTT;要进剪辑软件、电视或桌面播放器,就用 SRT。其他格式见 字幕与转录文件格式。

浏览器实际显示了什么:17 项 VTT 功能实测

每个功能写一个小 VTT 文件,用本地 Web 服务器提供,放进 <video> 的 <track> 里加载。 每次测试都跳到字幕应该出现的时间点,截下视频画面,再通过页面的 textTracks 接口读出浏览器实际解析到了什么。 Chrome 用全新的临时配置以无界面模式运行,结果异常的几项又在有窗口的 Chrome 里重测了一遍;Safari 通过 safaridriver 自动操作。系统都是 macOS 26.2。

功能Chrome 155Safari 26.2
普通字幕、NOTE 块、条目标识、不写小时的时间✅✅
line:0(顶部)、line:50%(中间)✅✅
position:10% align:start size:40%✅✅(换行框更窄)
vertical:rl(竖排)✅✅
<b>、<i>、<u>✅✅
STYLE 块:::cue 的颜色、字体、字号✅✅
class:<c.yellow> 配 ::cue(.yellow)✅✅
说话人:<v Maya> 配 ::cue(v[voice="Maya"])✅(不显示名字)✅(不显示名字)
字符引用(&amp;、&lt;、&nbsp;)✅✅
用 <lang ja> 包住的日文和中文✅✅
<ruby> 注音✅⚠️ 注音单独占一行
卡拉 OK 时间标签配 ::cue(:past) / :future❌ 不变色❌ 不变色
REGION(lines:3、scroll:up)❌ 被忽略⚠️ 画出了区域,第四行始终没滚进来
四条时间重叠的字幕,拖动进度条跳进去之后⚠️ 只显示第一条✅ 四条都显示

Chrome 155 与 Safari 26.2 渲染同一组 VTT 测试文件:定位、注音、区域和重叠字幕

功能测试说明了什么

日常用到的功能都靠得住。 定位、粗体斜体、STYLE 块里的颜色和字体、class 和说话人样式,在两个浏览器里显示一致。 如果字幕要放到画面顶部、避免挡住下方的字幕条,line:0 可以放心用。

iPhone 和 Mac 结果一致。 我们在 iPhone 的 Safari 里播放了其中 5 个测试文件,页面内和全屏各看一次:定位、STYLE 块的颜色和字体、class、注音,以及四条重叠字幕。全屏也保留了样式,注音和 Mac 上的 Safari 一样单独占一行。

区域和卡拉 OK 还不能用。 Chrome 完全不管区域:textTracks 接口里每条字幕的区域都是空的,字幕照常堆在底部。 卡拉 OK 的逐词变色,两个浏览器都没有生效,暂停和播放时都一样。

Chrome 在拖动进度条后会漏掉重叠字幕。 这一条出乎我们意料。两个人同时说话时,字幕文件里常有时间重叠的条目。 从头播放四条重叠字幕,Chrome 能全部显示;但像观众拖动进度条那样直接跳到它们中间,Chrome 只显示开始最早的那一条,它的 activeCues 列表也只有这一条。 只有两条重叠、或者各条结束时间不同,结果都一样。Safari 在每种情况下都显示了全部字幕。 如果你的字幕有重叠,请在 Chrome 里拖动进度条测一测,或者把重叠部分拆成前后相接的独立条目。

YouTube 更严格。 YouTube 帮助文档把 WebVTT 列为"高级"格式:支持定位,但样式只认 <b>、<i>、<u>。class 和 STYLE 块上传后不会保留。

VTT 字幕为什么不显示:我们把文件改坏了 18 种方式

报错的文件会有人去修。危险的是另一种:视频照常播放,什么提示都没有,字幕就是不出来。 我们拿一个正常的两条字幕文件,按 18 种方式改坏,每个版本都交给 Chrome、Safari、ffmpeg 8.1.1(转成 SRT)和 ScribeToAny 的字幕转换器处理。后者是我们免费 VTT 工具背后的解析器。

"报错"指浏览器触发了字幕轨的 error 事件,开发者至少还能捕获到。"静默"指字幕轨显示加载成功、里面却什么都没有,或者丢了内容却没有任何提示。

文件的问题Chrome 155Safari 26.2ffmpeg 8.1.1ScribeToAny
没问题(正常文件)✅✅✅✅
开头有 BOM✅✅✅✅
Windows 换行(CRLF)✅✅✅✅
文件头写成 WEBVTT - 标题✅✅✅✅
箭头两边没有空格✅✅✅✅
小时只有一位(0:00:05.000)✅✅✅✅
条目之间没有空行✅✅✅✅
没有 WEBVTT 文件头❌ 报错❌ 报错✅(有警告)✅
文件头写成小写 webvtt❌ 报错❌ 报错❌ 什么都没读到✅
SRT 文件直接改名成 .vtt❌ 报错❌ 报错✅ 当作 SRT 读✅
毫秒前用了逗号❌ 静默:0 条字幕❌ 静默:0 条字幕❌ 什么都没读到✅
毫秒只写两位(05.32)❌ 静默:0 条字幕❌ 静默:0 条字幕✅✅
WEBVTT 后面没有空行✅✅⚠️ 静默:第一条丢失✅
字幕文字中间夹了一个空行⚠️ 静默:空行后的文字丢失⚠️ 静默:空行后的文字丢失⚠️ 静默:连下一条也丢了⚠️ 与浏览器相同
结束时间早于开始时间⚠️ 这条永远不显示⚠️ 这条永远不显示❌ 结束时间写成了 1193:03:02,296⚠️ 原样保留,编辑器的质量检查会标出
存成 Latin-1 编码("café")⚠️ 乱码:caf�⚠️ 乱码:caf�⚠️ 静默:这条被丢弃✅ 自动识别编码
SRT 的标签 <font color>、{\an8}{\an8} 当文字显示{\an8} 当文字显示{\an8} 被改乱{\an8} 被移除
以 text/plain 而不是 text/vtt 返回✅✅不适用不适用
放在别的域名、没有 CORS 头❌ 报错✅ 能播放不适用不适用

坏文件测试说明了什么

时间里的逗号是最经典的坑。 把 SRT 改个扩展名,或者只补了文件头没改时间,就会这样。 两个浏览器都接受了文件、报告加载成功,然后一条字幕都不显示。毫秒只写两位也是同样的结果。 如果 VTT 字幕轨"加载了"但什么都不出来,先查时间行:00:00:05.320,点号,加三位毫秒。

浏览器对文件头很严格,对其他地方都很宽松。 没有文件头或写成小写,字幕轨直接失败。 条目间缺空行、BOM、CRLF 换行、一位数的小时、text/plain 类型,都没有问题。

跨域文件在不同浏览器里表现不同。 同一个正常文件放在别的域名、不带 CORS 头,Safari 能播放,Chrome 失败。 也就是说,页面在你测试用的 Mac 上一切正常,Chrome 用户却看不到字幕。 解决办法是把 .vtt 和页面放在同一个域名下;或者让服务器返回 Access-Control-Allow-Origin 头,同时给 <video> 加上 crossorigin 属性。两样都做了以后,Chrome 就能加载。

转换 VTT 可能会悄悄改掉内容。 ffmpeg 把一个早于开始时间的结束时间,在输出的 SRT 里写成了 1193:03:02,296; 文件头后面缺空行时,它丢掉了第一条;遇到字幕文字中间的空行,它把后面整条字幕都丢了。转换完记得对一下输入和输出的条目数。

我们自己的转换器也有 VTT 的问题,这次测试把它抓了出来。 上面每一个坏文件它都能读进去,但它会把 VTT 专用的标记留在文字里。 把说话人测试文件转成 TXT,得到的是 <v Maya>Voice tag…</v>,&amp; 也原样输出,没有还原成 &。 卡拉 OK 时间标签和 <ruby> 注音也以同样方式漏进了 TXT、Word、PDF 和 SRT 输出。 我们当天修好了:VTT 字幕文字现在会去掉只有浏览器才懂的标签(<c>、<v>、<lang>、<ruby> 的注音、卡拉 OK 时间标签),保留 SRT 同样支持的 <b>、<i>、<u>,并还原字符引用。这些情况都加进了我们的回归检查。

我们没测的: 只测了一台 Mac 上的两个桌面浏览器和一个 ffmpeg 版本,用的是很小的合成文件。 Firefox、Android、智能电视和 VLC 这类播放器都有自己的 WebVTT 实现,结果可能不同。 iPhone 上只测了 17 项里的 5 项,也没有测拖动进度条。

怎么打开、制作和转换 VTT 文件

  • 查看内容: 任何纯文本编辑器都行,比如记事本或 TextEdit。
  • 跟视频一起看: 大多数桌面播放器能在视频旁边加载它。在网页里,把 <track kind="subtitles" srclang="en" src="file.vtt"> 放进 <video> 元素。如果字幕包含声音描述,用 kind="captions";两者的区别见隐藏式字幕与普通字幕。
  • 从录音生成: 把音频或视频上传到字幕生成器,导出 VTT。
  • 格式转换: SRT 转 VTT 会补上文件头、把逗号换成点号;VTT 转 SRT 反过来;VTT 转文本 只保留文字。三个工具都在浏览器里运行,文件不会上传。
  • 修复文件: 用 SRT 编辑器打开,它也能读 VTT,会标出结束早于开始、或者快得来不及读的字幕。改好后另存一份干净的文件。

常见问题

VTT 的 MIME 类型是什么? text/vtt。服务器上还是应该设好,尽管在我们的测试里,Chrome 155 和 Safari 26.2 对以 text/plain 返回的文件也能正常播放。

VTT 比 SRT 好吗? 网页视频用 VTT 更好,因为浏览器只认 VTT。其他场合 SRT 更稳妥:支持它的剪辑软件、电视和上传表单更多。

能直接把 .srt 改名成 .vtt 吗? 不能。两个浏览器都拒绝了改名后的文件。你需要加上 WEBVTT 文件头,并把逗号换成点号,SRT 转 VTT 做的就是这件事。


VTT 文件就是加了文件头、把逗号换成点号、还能放附加功能的 SRT。 基础功能在我们测过的地方都正常;附加功能要看浏览器,而最糟的故障都是静默的:时间里的一个逗号、放在别的域名上的文件、跳进重叠字幕的一次拖动。 时间行写准确,文件放在自己的域名下,上线前在不止一个浏览器里播一遍。