TTML 字幕格式
这种格式能表达什么
- 文本样式
- 字体、颜色、描边、阴影
- 屏幕定位
- 是
- 卡拉 OK 计时
- 否
- 字幕条内换行
- 是
- 时间轴
- 每条字幕都有开始与结束时间
- 时间精度
- 1 毫秒
- 时间以帧号存储
- 否
- 从右到左的文字
- 是
示例
一份典型的 TTML 文件,以及从里面读出了什么
文件
<?xml version="1.0" encoding="UTF-8"?>
<tt xmlns="http://www.w3.org/ns/ttml" xmlns:tts="http://www.w3.org/ns/ttml#styling" xml:lang="en">
<head>
<styling>
<style xml:id="s1" tts:color="#FFFF00" tts:fontWeight="bold"/>
</styling>
<layout>
<region xml:id="bottom" tts:origin="10% 80%" tts:extent="80% 20%"/>
</layout>
</head>
<body>
<div>
<p begin="00:00:01.000" end="00:00:03.500" region="bottom" style="s1">Hello <span tts:fontStyle="italic">there</span>.</p>
<p begin="00:00:03.500" end="00:00:06.000" region="bottom">How are you?<br/>Fine, thanks.</p>
</div>
</body>
</tt>
从中读出了什么
2 cues, 3 lines of text
00:00:01.000 → 00:00:03.500
Hello there. [italic ×1]
[region="bottom" style="s1"]
00:00:03.500 → 00:00:06.000
How are you?
Fine, thanks.
[region="bottom"]
Outside the cues:
prolog an XML declaration and the namespace bindings, kept as they came in
styling 1 × <style> — "s1", the colour and weight a paragraph points at by name
layout 1 × <region> — "bottom", the origin and extent a paragraph points at by name
resolved the head is kept as it was written and is not applied here — a paragraph's style="s1" region="bottom" stays an attribute, not a colour or a screen position右栏不是转换后的文件,而是本站从左栏读出来的东西:cue 的时间与正文、段落内的强调、以及头部保留了什么。
一份脏的 TTML 文件,仍然读得出什么
文件
<?xml version="1.0" encoding="UTF-8"?>
<tt xmlns="http://www.w3.org/ns/ttml" xmlns:tts="http://www.w3.org/ns/ttml#styling">
<body>
<div begin="00:00:01.000" end="00:00:05.000">
<p>Inherits its timing from the div, which we do not resolve.</p>
<p begin="00:00:06.000" end="00:00:07.000">This one has its own timing.</p>
</div>
<!-- <p begin="00:00:08.000" end="00:00:09.000">commented out</p> -->
<div>
<p begin="00:00:10.000"/>
<p begin="00:00:11.000" end="00:00:12.000">Last one.</p>
</div>
</body>
</tt>
从中读出了什么
3 cues, 2 lines of text
00:00:06.000 → 00:00:07.000
This one has its own timing.
00:00:10.000 → 00:00:12.000
(no text)
00:00:11.000 → 00:00:12.000
Last one.
Outside the cues:
prolog an XML declaration and the namespace bindings, kept as they came in
head none — no <styling> and no <layout>, and no paragraph points at one either
comments the commented-out <p> stays a comment, in the place it was written
divs the timings on an enclosing <div> are not pushed down onto the paragraphs inside it
Reported while reading — repaired or skipped, never silently:
1 × paragraphs with no readable begin time of their own (they were skipped — see the note about timing inherited from an enclosing div)
1 × paragraphs with neither an end time nor a duration of their own (their end times were generated)继承时间、注释掉的段落、没有正文的 `<p>`、没有结束时间的 cue 都在这一份里。解析不抛异常:读不出来的如实上报,能读出的照常读出。
把 TTML (Timed Text) 转成其他格式
每一个都完全在你的浏览器里运行。标签标明了那个方向要付出什么代价,打开之前就能看到。
转换为此格式
- SubRip (SRT) → TTML (Timed Text)
- WebVTT (VTT) → TTML (Timed Text)
- Advanced SubStation Alpha (ASS) → TTML (Timed Text)
- SubStation Alpha (SSA) → TTML (Timed Text)
- YouTube SBV → TTML (Timed Text)
- SAMI (SMI) → TTML (Timed Text)
- LRC Lyrics → TTML (Timed Text)
- MicroDVD → TTML (Timed Text)
- SubtitleMatrix JSON → TTML (Timed Text)
- Plain Text (TXT) → TTML (Timed Text)
- CSV Spreadsheet → TTML (Timed Text)
TTML(Timed Text Markup Language)是广播与流媒体那套 XML 字幕:Netflix、EBU-TT-D、IMSC 都建在它上面。一条字幕是一个 `<p>`,起止时间写在 `begin` / `end` 上;整份文件是一个 XML 文档,`<p>` 外面还有 `<tt>`、`<body>`、`<div>` 这几层。
它比大多数格式多一层“头部”。`<styling>` 定义具名样式(`tts:color`、`tts:fontFamily`),`<layout>` 定义具名区域(`tts:origin`),而 `<p style="s1" region="bottom">` 只是按名字引用它们。改一处头部就能改全片,这也是转成 cue 模型时丢得最多的部分。
时间有四种写法:时钟 `00:00:04.126`、偏移 `1.5s` / `250ms`、帧号 `00:00:01:12`、帧偏移 `36f`。后两种要帧率,帧率从 `<tt>` 的 `ttp:frameRate` 与 `ttp:frameRateMultiplier` 读;读不到就不猜 30 fps,而是把那条记成“时间读不出来”。规范里的 `tickRate`、`subFrameRate` 与 `ttp:timeBase` 都属于文档头部,本站按 cue 解析,头部原样保留。
它不表达逐字卡拉OK计时:一条 cue 只记开始与结束,不记某个音节什么时候亮。下表每一行“否”就是转成 TTML 时会丢的东西,每条转换页都会先列出来。所有转换都在浏览器里完成,不上传、不注册,也可以批量处理。
常见问题
TTML 的 `<p begin="…" end="…">` 之外还有什么?
还有 XML 声明与命名空间绑定、`<head>` 里的 `<styling>` 与 `<layout>` 两张表、可能存在的 `<metadata>`,以及 `<body>`/`<div>` 这层结构。`<p>` 通过 `style` 与 `region` 按名字引用头部里的定义。
TTML 的 `tickRate` 与 `subFrameRate` 是做什么的?
它们是 TTML 规范里按 tick 计时的参数:`tickRate` 表示每秒有多少个 tick,`subFrameRate` 把一个帧再细分。它们属于文档头部的时间基准配置;本站读 cue 时用的是时钟、偏移与 `frameRate`,不按 tick 重算时间轴。
TTML 的 `ttp:timeBase` 取 `media` 还是 `smpte` 有区别吗?
有。`media` 是相对媒体时间的时钟基准,`smpte` 是时间码基准(可以带丢帧约定),同一条时间串在两种基准下的含义不同。本站按时钟、偏移、帧号三种写法解析 cue,并且不解析父级 `<div>` 上的时间继承。
TTML 支持逐字卡拉OK计时吗?
不支持。一条 `<p>` 只有开始与结束,没有记录音节逐个亮起的字段;所以从 ASS 转成 TTML 时,卡拉OK计时会进入损失清单。
TTML 的样式与区域在文件里怎么组织?
`<styling>` 里是一张具名样式表,`<layout>` 里是一张具名区域表,`<p>` 用 `style="s1"` 与 `region="bottom"` 引用。定义与使用分开,所以引用本身在转成 cue 模型时会消失,定义也一起丢。
TTML 文件里写不出自己的开始时间会怎样?
本站不解析 `<div>`/`<body>` 上的时间继承:一个自己没有 `begin` 的 `<p>` 会被记为“时间读不出来”并跳过,同时上报条数。这是有意的——猜继承会让整份字幕悄悄偏移,而上报能让你当场看见。