YouTubeの字幕機能の限界に迫る

# YouTubeの字幕機能の限界に迫る

YouTubeには、**字幕機能**というものがある。

一般的にこの字幕機能では、表示をONにすると通常黒い背景に白い文字が浮かび上がってくる。(視聴者が多少表示設定を変更することもできる。)

![Screenshot_2020-02-10 【紫咲シオン】難しい話をしてしまいハーバード大学卒であることがバレてしまうシオン.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449776/b8fe28e2-8a2b-8999-3793-bee42cacf13a.png)

筆者も今までに膨大な数の動画を視聴してきたが、先日初めて**画期的な字幕**を使用している動画を発見した。

---
![Screenshot_2020-02-10 にじさんじ(1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449776/f6493860-7d10-0814-79d6-5016c54eb1bc.png)

>_[にじさんじ - Virtual to LIVE [Official Music Video]](https://www.youtube.com/watch?v=zPMWAj54Seg)_
 
---

画面上部および下部に背景がなく文字がカラフルな歌詞が表示されている。
しかしこれは動画として埋め込まれている文字(映像)ではない。
現に字幕表示をOFFにすると歌詞も表示されなくなる。
![Screenshot_2020-02-10 にじさんじ(2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449776/73671d9f-4b26-46dd-16e0-4d780ec0b516.png)

そして、[実際の動画](https://www.youtube.com/watch?v=zPMWAj54Seg)で確認してほしいのだが、驚くべきことに**カラオケ字幕**になっている(歌ったところの文字が変わる)。

というわけでこの字幕のシステムが意味不明すぎたので調べてみたところ、**yttファイル**というものを使っているのだろうということが判明した。

**追記:ホロライブの不定期アニメも一部文字色つくようになったみたいですね**。


![Screenshot_2020-02-15 【アニメ】APEX【荒野行動】.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449776/f9ddb9b0-f656-b421-43da-3de927e0cf62.png)
![Screenshot_2020-02-15 【アニメ】APEX【荒野行動】(1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449776/15e683a8-d7d8-e777-fd81-aa5fa4febba6.png)


## YouTubeの字幕仕様

### 字幕をつける方法

そもそもYouTubeにおいて動画に字幕をつけるのには、大別して二通りの方法がある。

- **文字起こし機能を使う**
- **ファイルをアップロードする**

この二つである。

おそらくほとんどの動画は前者である文字起こし機能を利用して作成されている。
文字起こし機能は、ブラウザ上で動作するGUIの字幕製作エディタである。
しかしこの文字起こし機能では必要最低限の機能しか使えない。具体的にいうと

- 文字色は白固定
- 背景色は黒固定
- フォント固定
- サイズ固定
- 画面上同時に表示できる字幕は1つまで

といったものである。
もちろん動画に字幕をつけるだけであればこれだけで十分だが、先ほど紹介した動画のような字幕は到底作成できない。

まあつまり、件の動画は字幕ファイルをアップロードして作成されている、ということが確実にいえる。

### ファイル形式

[YouTubeの公式ヘルプ](https://support.google.com/youtube/answer/2734698)にあるYouTubeが対応していると明記されている字幕ファイル形式について実際に利用してみた。

|ファイル形式|拡張子|スタイリング対応|実用性|
|:----:|:---:|:------:|:--:|
|SubRip|.srt|複|△|
|SubViewer|.sbv/.sub|-|△|
|MPsub|.mpsub|-|△(?)|
|LRC|.lrc|-|△(?)|
|Videotron Lambda|.cap|-|△(?)|
|SAMI|.smi/.sami|色太イ下影|○|
|RealText|.rt|-|×|
|WebVTT|.vtt|複太イ下座|○|
|TTML|.ttml|複|○|
|DFXP|.ttml/.dfxp|複|○|
|テレビ用字幕形式各種|-|-|△|

*注:
　　複:同時複数箇所表示可能
　　色:文字色指定可能
　　太:太字指定可能
　　イ:イタリック指定可能
　　下:下線付き文字指定可能
　　座:表示座標指定可能
　　影:影付き文字指定可能
　　フ:フォント利用可能
　　背:背景色変更可能
　　サ:文字サイズ指定可能*

みてもらえればわかると思うが、**なんと全機能を利用できる形式がない。** そもそもRealTextのように対応していると書いてあるのにアップロードすらできないファイル形式もあった。
また、**すべての形式を見ても、フォント、背景色、文字サイズなどは指定ができない**。
しかし実際には件の動画のように表示ができるわけだ。
これはいったいどういうことなのか？

詳しく調べたところ、YouTubeの内部仕様には、すべての機能を使用可能な**TTMLをベースに構築されたオリジナルの字幕ファイル形式**があるようだということがわかった。

以降はこのオリジナルの形式を**YTT形式**(YouTube Timed Text)と呼ぶことにする。

|ファイル形式|拡張子|スタイリング対応|実用性|
|:----:|:---:|:------:|:--:|
|YTT|.ytt|複色太イ下座影フ背サ|◎|

## YTT形式の仕様
ここまでがめちゃめちゃ長かったが、このYTT形式の仕様を解説したい。
まず前提として、YouTubeはYTT形式の存在をサポート等の情報としてどこにも明記していない。
また、YTTの詳細な記法についても、日本語・英語のサイトではまったく見つけられなかった。
見つかったのは、YTTファイルが存在するということと、そのYTTファイルの例だけである。
今回の記事はそれをまとめたものなので、実はもっと機能があるかもしれない。

### ファイル形式
YTTは、前述したようにTTMLをベースに構築された字幕用マークアップ言語である。
そしてそのベースになったTTMLは、XMLをベースに字幕表示をサポートするために構築された言語だ。
つまり、**YTTファイルはXMLベース**である。
この先の記事を読む場合、XMLを知っている人であることが望ましい。

YTTファイルの拡張子は **.ytt** である。
また、テキストコーデックは**UTF-8**だ。

# YTT記法

まあxmlなので雑に書く。

## お約束
お約束です。先頭に書く。

```xml:お約束
<?xml version="1.0" encoding="utf-8" ?>
<timedtext format="3">
   <head>
    <!--ヘッダ-->
   </head>
   <body>
    <!--ボディ-->
   </body>
</timedtext>
```
## ヘッダ
YTTではスタイリング情報はすべてヘッダに記述する。
また、head内に記述されたすべてのエレメントはidを紐付けなくてはならない。

まずは例をみてみよう。

```html:example.ytt
<?xml version="1.0" encoding="utf-8" ?>
<timedtext format="3">
<head>
  <!--文字情報-->
    <pen id="1" b="1"/> <!--太字-->
    <pen id="2" fc="#FF0000" fo="40"  bo="0" /> <!--色と不透明度-->
  <!--座標情報-->
    <wp id="0"  ap="0" ah="0"   av="0" /> <!--左上-->
    <wp id="1"  ap="4" ah="50"   av="50" /> <!--ど真ん中-->
    <wp id="2"  ap="8" ah="100"   av="100" /> <!--右下-->
</head>
</timedtext>
```

例にあるように、head内にpenやwpというエレメントを作成し、その各エレメントにIDを付与することで、bodyからidを指定することでスタイルを指定できる。言ってしまえばスタイルシートである。

各エレメントについて解説する

---
```html:penエレメント
<pen />
```

最も基本的なエレメント。
文字や背景自体の装飾をするときに使用する。
引数に指定するものによって効果が変わる

|引数|効果|
|:-------:|:--------:|
|id="*int*"|必須|
|b="1"|太字になる|
|i="1"|イタリックになる|
|u="1"|下線付き文字になる|
|fc="#FFFFFF"|文字色の指定(カラーコード)|
|fo="*int(0~255)*"|文字の不透明度|
|bc="#000000"|背景色の指定(カラーコード)|
|bo="*int(0~255)*"|背景の不透明度|
|ec="#FF0000"|影・縁色の指定(カラーコード)|
|et="*int(1~4)*"|影・縁のタイプ(スマホ非対応)|
|fs="*int(0~7)*"|フォント(スマホ非対応)|
|fs="*int*"|フォントサイズ(Android非対応)|
|rb="*int(1,4,5)*"|ルビ|
|of="*int(0~2)*"|上・下付き文字|
|te="*int(1~2)*"|Text emphasis|
|hg="1"|Text combinations|
フォントサイズ計算式: 100 + (fs - 100) / 4 (%)

---
```html:wsエレメント
<ws />
```

文字記法エレメント。
縦書き横書きの変更。
引数に指定するものによって効果が変わる

|引数|効果|
|:-------:|:--------:|
|ju="*int(1~2)*"|水平方向のテキストの配置|
|pd="*int(1~3)*"|縦書きでの文字回転|
|sd="*int(1~3)*"|縦書きでの回転角|

---
```html:wpエレメント
<wp />
```

文字座標エレメント。
文字を書く座標を指定する。
引数に指定するものによって効果が変わる

|引数|効果|
|:-------:|:--------:|
|ap="*int(0~8)*"|アンカーポイント|
|ah="*int(0~100)*"|X座標|
|av="*int(0~100)*"|Y座標|

アンカーポイント↓
＿＿＿＿
|0　1　2|
|3　4　5|
|6　7　8|
ーーーー

## ボディ

まずは例をみてみよう。

```html:example.ytt
<?xml version="1.0" encoding="utf-8" ?>
<timedtext format="3">
<head>
  <!--文字情報-->
    <pen id="1" b="1"/> <!--太字-->
    <pen id="2" fc="#FF0000" fo="40"  bo="0" /> <!--色と不透明度-->
  <!--座標情報-->
    <wp id="0"  ap="0" ah="0"   av="0" /> <!--左上-->
    <wp id="1"  ap="4" ah="50"   av="50" /> <!--ど真ん中-->
    <wp id="2"  ap="8" ah="100"   av="100" /> <!--右下-->
</head>
<body>
   <p t="0" d="4000" wp="1" p="1">Bold</p>
   <p t="4000" d="4000" wp="0" p="2">左上</p>
   <p t="4000" d="4000" wp="2" p="3">右下</p>
</body>
</timedtext>
```

bodyに関してはとてもシンプルだ。
pやsで囲って、そこに引数として表示開始時間、表示する長さ、読み込むpen、読み込むws、読み込むwpを指定するだけである。

|使えるエレメント|意味|
|:-:|:--:|
|p|Paragraph|
|s|Span|

|引数|効果|
|:-------:|:--------:|
|t="*int*"|表示開始時刻(動画開始からのミリ秒)|
|d="*int*"|表示する長さ(ミリ秒)|
|p="*int(id)*"|headで指定したpenを読み込む|
|ws="*int(id)*"|headで指定したwsを読み込む|
|wp="*int(id)*"|headで指定したwpを読み込む|

## 件の動画を再現してみる

限界に迫る、ということで件の動画の字幕を一部だけでも再現してみたい。
最初はyttファイル自体をダウンロードしたりできないかなぁと思っていたのだが、さすがに無理だったようで...
しかしXMLファイルはダウンロードできた。YouTubeの文字起こし等からは表示時間が整数でしかわからないがこのXMLファイルではミリ秒まで表記されているのでこれを元に再現する。
http://video.google.com/timedtext?hl=ja&lang=ja&v=zPMWAj54Seg


以下が完成した動画(最初の2フレーズのみだが)。
https://youtu.be/6dl416-x1ig

そしてこれがコード

```html:nijisanji.ytt
<?xml version="1.0" encoding="utf-8" ?>
<timedtext format="3">
<head>
<pen id="1" ec="#B22222" fc="#FEFEFE" fo="255" bo="0" b="1" et="3"/>
<pen id="2" ec="#FFA07A" fc="#FEFEFE" fo="255" bo="0" b="1" et="3"/>
<pen id="3" ec="#FFFF50" fc="#FEFEFE" fo="255" bo="0"  b="1" et="3"/>
<pen id="4" ec="#FFDAB9" fc="#FEFEFE" fo="255" bo="0"  b="1" et="31"/>
<pen id="5" ec="#DDFF50" fc="#FEFEFE" fo="255" bo="0"  b="1" et="3"/>
<pen id="6" ec="#90EE90" fc="#FEFEFE" fo="255" bo="0"  b="1" et="3"/>
<pen id="7" ec="#00FF7F" fc="#FEFEFE" fo="255" bo="0"  b="1" et="3"/>
<pen id="8" ec="#7FFFD4" fc="#FEFEFE" fo="255" bo="0"  b="1" et="3"/>
<pen id="9" ec="#87CEEB" fc="#FEFEFE" fo="255" bo="0"  b="1" et="3"/>
<pen id="10" ec="#6495ED" fc="#FEFEFE" fo="255" bo="0" b="1"  et="3"/>
<pen id="11" ec="#DA70D6" fc="#FEFEFE" fo="255" bo="0"  b="1" et="3"/>
<pen id="12" ec="#FF50FF" fc="#FEFEFE" fo="255" bo="0"  b="1" et="3"/>

<pen id="20" b="1" bo="0" />
<pen id="21" bo="0" />
<pen id="22" bo="0" />

<pen id="30" fc="#FEFEFE" fo="200" bo="0" />
<pen id="31" fc="#686A7A" fo="255" bo="0" />

<wp id="1" ap="4" ah="50" av="90" />

</head>
<body>
<p t="5413" d="467" p="20" wp="1"><s p="1" >どう
<s p="2" >し
<s p="3" >よ
<s p="4" >う
<s p="5" >も
<s p="6" >な
<s p="7" >く今
<s p="8" >を<s p="9" >生
<s p="10" >き
<s p="11" >て
<s p="12" >る
</p>
<p t="5413" d="467" p="21" wp="1"><s p="30">どうしようもなく今を生きてる
</p>

<p t="5880" d="333" p="20" wp="1"><s p="1" >どう
<s p="2" >し
<s p="3" >よ
<s p="4" >う
<s p="5" >も
<s p="6" >な
<s p="7" >く今
<s p="8" >を<s p="9" >生
<s p="10" >き
<s p="11" >て
<s p="12" >る
</p>
<p t="5880" d="333" p="21" wp="1"><s p="31">どうし
<s p="30">ようもなく今を生きてる
</p>

<p t="6213" d="401" p="20" wp="1"><s p="1" >どう
<s p="2" >し
<s p="3" >よ
<s p="4" >う
<s p="5" >も
<s p="6" >な
<s p="7" >く今
<s p="8" >を<s p="9" >生
<s p="10" >き
<s p="11" >て
<s p="12" >る
</p>
<p t="6213" d="401" p="21" wp="1"><s p="31">どうしよう
<s p="30">もなく今を生きてる
</p>

<p t="6614" d="167" p="20" wp="1"><s p="1" >どう
<s p="2" >し
<s p="3" >よ
<s p="4" >う
<s p="5" >も
<s p="6" >な
<s p="7" >く今
<s p="8" >を<s p="9" >生
<s p="10" >き
<s p="11" >て
<s p="12" >る
</p>
<p t="6614" d="167" p="21" wp="1"><s p="31">どうしようも
<s p="30">なく今を生きてる
</p>

<p t="6781" d="200" p="20" wp="1"><s p="1" >どう
<s p="2" >し
<s p="3" >よ
<s p="4" >う
<s p="5" >も
<s p="6" >な
<s p="7" >く今
<s p="8" >を<s p="9" >生
<s p="10" >き
<s p="11" >て
<s p="12" >る
</p>
<p t="6781" d="200" p="21" wp="1"><s p="31">どうしようもな
<s p="30">く今を生きてる
</p>

<p t="6981" d="400" p="20" wp="1"><s p="1" >どう
<s p="2" >し
<s p="3" >よ
<s p="4" >う
<s p="5" >も
<s p="6" >な
<s p="7" >く今
<s p="8" >を<s p="9" >生
<s p="10" >き
<s p="11" >て
<s p="12" >る
</p>
<p t="6981" d="400" p="21" wp="1"><s p="31">どうしようもなく
<s p="30">今を生きてる
</p>

<p t="7381" d="401" p="20" wp="1"><s p="1" >どう
<s p="2" >し
<s p="3" >よ
<s p="4" >う
<s p="5" >も
<s p="6" >な
<s p="7" >く今
<s p="8" >を<s p="9" >生
<s p="10" >き
<s p="11" >て
<s p="12" >る
</p>
<p t="7381" d="401" p="21" wp="1"><s p="31">どうしようもなく今
<s p="30">を生きてる
</p>

<p t="7782" d="200" p="20" wp="1"><s p="1" >どう
<s p="2" >し
<s p="3" >よ
<s p="4" >う
<s p="5" >も
<s p="6" >な
<s p="7" >く今
<s p="8" >を<s p="9" >生
<s p="10" >き
<s p="11" >て
<s p="12" >る
</p>
<p t="7782" d="200" p="21" wp="1"><s p="31">どうしようもなく今を
<s p="30">生きてる
</p>

<p t="7982" d="167" p="20" wp="1"><s p="1" >どう
<s p="2" >し
<s p="3" >よ
<s p="4" >う
<s p="5" >も
<s p="6" >な
<s p="7" >く今
<s p="8" >を<s p="9" >生
<s p="10" >き
<s p="11" >て
<s p="12" >る
</p>
<p t="7982" d="167" p="21" wp="1"><s p="31">どうしようもなく今を生
<s p="30">きてる
</p>

<p t="8149" d="200" p="20" wp="1"><s p="1" >どう
<s p="2" >し
<s p="3" >よ
<s p="4" >う
<s p="5" >も
<s p="6" >な
<s p="7" >く今
<s p="8" >を<s p="9" >生
<s p="10" >き
<s p="11" >て
<s p="12" >る
</p>
<p t="8149" d="200" p="21" wp="1"><s p="31">どうしようもなく今を生き
<s p="30">てる
</p>

<p t="8349" d="200" p="20" wp="1"><s p="1" >どう
<s p="2" >し
<s p="3" >よ
<s p="4" >う
<s p="5" >も
<s p="6" >な
<s p="7" >く今
<s p="8" >を<s p="9" >生
<s p="10" >き
<s p="11" >て
<s p="12" >る
</p>
<p t="8349" d="200" p="21" wp="1"><s p="31">どうしようもなく今を生きて
<s p="30">る
</p>

<p t="8549" d="567" p="20" wp="1"><s p="1" >どう
<s p="2" >し
<s p="3" >よ
<s p="4" >う
<s p="5" >も
<s p="6" >な
<s p="7" >く今
<s p="8" >を<s p="9" >生
<s p="10" >き
<s p="11" >て
<s p="12" >る
</p>
<p t="8549" d="567" p="21" wp="1"><s p="31">どうしようもなく今を生きてる
</p>

<p t="9116" d="201" p="20" wp="1"><s p="1" >こ
<s p="2" >の
<s p="3" >声
<s p="5" >が
<s p="7" >届
<s p="8" >く<s p="10" >未
<s p="11" >来
<s p="12" >が
</p>
<p t="9116" d="201" p="21" wp="1"><s p="31">こ
<s p="30">の声が届く未来が
</p>

<p t="9317" d="400" p="20" wp="1"><s p="1" >こ
<s p="2" >の
<s p="3" >声
<s p="5" >が
<s p="7" >届
<s p="8" >く<s p="10" >未
<s p="11" >来
<s p="12" >が
</p>
<p t="9317" d="400" p="21" wp="1"><s p="31">この
<s p="30">声が届く未来が
</p>

<p t="9717" d="363" p="20" wp="1"><s p="1" >こ
<s p="2" >の
<s p="3" >声
<s p="5" >が
<s p="7" >届
<s p="8" >く<s p="10" >未
<s p="11" >来
<s p="12" >が
</p>
<p t="9717" d="363" p="21" wp="1"><s p="31">この声
<s p="30">が届く未来が
</p>

<p t="10084" d="400" p="20" wp="1"><s p="1" >こ
<s p="2" >の
<s p="3" >声
<s p="5" >が
<s p="7" >届
<s p="8" >く<s p="10" >未
<s p="11" >来
<s p="12" >が
</p>
<p t="10084" d="400" p="21" wp="1"><s p="31">この声が
<s p="30">届く未来が
</p>

<p t="10484" d="367" p="20" wp="1"><s p="1" >こ
<s p="2" >の
<s p="3" >声
<s p="5" >が
<s p="7" >届
<s p="8" >く<s p="10" >未
<s p="11" >来
<s p="12" >が
</p>
<p t="10484" d="367" p="21" wp="1"><s p="31">この声が届
<s p="30">く未来が
</p>

<p t="10851" d="201" p="20" wp="1"><s p="1" >こ
<s p="2" >の
<s p="3" >声
<s p="5" >が
<s p="7" >届
<s p="8" >く<s p="10" >未
<s p="11" >来
<s p="12" >が
</p>
<p t="10851" d="201" p="21" wp="1"><s p="31">この声が届く
<s p="30">未来が
</p>

<p t="11052" d="200" p="20" wp="1"><s p="1" >こ
<s p="2" >の
<s p="3" >声
<s p="5" >が
<s p="7" >届
<s p="8" >く<s p="10" >未
<s p="11" >来
<s p="12" >が
</p>
<p t="11052" d="200" p="21" wp="1"><s p="31">この声が届く未
<s p="30">来が
</p>

<p t="11252" d="367" p="20" wp="1"><s p="1" >こ
<s p="2" >の
<s p="3" >声
<s p="5" >が
<s p="7" >届
<s p="8" >く<s p="10" >未
<s p="11" >来
<s p="12" >が
</p>
<p t="11252" d="367" p="21" wp="1"><s p="31">この声が届く未来
<s p="30">が
</p>

<p t="11619" d="567" p="20" wp="1"><s p="1" >こ
<s p="2" >の
<s p="3" >声
<s p="5" >が
<s p="7" >届
<s p="8" >く<s p="10" >未
<s p="11" >来
<s p="12" >が
</p>
<p t="11619" d="567" p="21" wp="1"><s p="31">この声が届く未来が
</p>



</body>
</timedtext>
```

手動だとめちゃめちゃ大変である。

# 最後に

yttデータはYouTubeの字幕の中で最も応用性が高い、というかまともに使えるのはこれしかないのに文献がまったくないので記事にしてみました。
参考にしたファイルの全文を以下においておきます。

```html:ytt.ytt
<?xml version="1.0" encoding="utf-8" ?>
<timedtext format="3">
<head>
    <!-- The elements in the <head> *must* appear in order of increasing ID's or the server will renumber them and screw everything up. -->
    
    <!-- Text styles -->
    <pen id="1" b="1" />            <!-- Bold       -->
    <pen id="2" i="1" />            <!-- Italic     -->
    <pen id="3" u="1" />            <!-- Underline  -->
    
    <!-- Foreground color/opacity. The iOS app supports both, while the Android app only supports color. -->
    <pen id="4" fc="#FF0000" fo="40"  bo="0" />
    <pen id="5" fc="#00FF00" fo="127" bo="0" />
    <pen id="6" fc="#0000FF" fo="255" bo="0" />             <!-- fo="255" is the default and will get removed after uploading,          -->
                                                            <!-- meaning the viewer's custom opacity setting (if any) will take         -->
                                                            <!-- effect. Because most viewers set this custom opacity by accident       -->
                                                            <!-- rather than intentionally (all it takes is to press O), it's           -->
                                                            <!-- recommended to go no higher than fo="254", thereby always              -->
                                                            <!-- overruling the custom setting and forcing subtitles to be readable.    -->
    
    <!-- Background color/opacity. In theory, the iOS app supports both, but only if the        -->
    <!-- well-hidden "Video Override" option is turned on in the iOS accessibility settings.    -->
    <!-- In practice, neither Android nor iOS support custom backgrounds.                       -->
    <pen id="7"  fc="#FEFEFE" bc="#FF0000" bo="40"  />
    <pen id="8"  fc="#FEFEFE" bc="#00FF00" bo="127" />
    <pen id="9"  fc="#FEFEFE" bc="#0000FF" bo="255" />
    <pen id="10" fc="#000000" bc="#FFFFFF" bo="255" />      <!-- In practice, bo should be limited to 254 for the same reason as fo.    -->
    
    <!-- Edge color/type (not supported on mobile)  -->
    <pen id="11" fc="#FEFEFE" ec="#FF0000" et="1" />        <!-- Hard shadow    -->
    <pen id="12" fc="#FEFEFE" ec="#00FF00" et="2" />        <!-- Bevel          -->
    <pen id="13" fc="#FEFEFE" ec="#0000FF" et="3" />        <!-- Glow/Outline   -->
    <pen id="14" fc="#FEFEFE" ec="#FF00FF" et="4" />        <!-- Soft shadow    -->
    
    <!-- Font styles (not supported on mobile)      -->
    <pen id="15" fc="#FEFEFE" fs="0" />                     <!-- Default (same as 4).                               -->
    <pen id="16" fc="#FEFEFE" fs="1" />                     <!-- Monospaced Serif           (Courier New)           -->
    <pen id="17" fc="#FEFEFE" fs="2" />                     <!-- Proportional Serif         (Times New Roman)       -->
    <pen id="18" fc="#FEFEFE" fs="3" />                     <!-- Monospaced Sans-Serif      (Deja Vu Sans Mono)     -->
    <pen id="19" fc="#FEFEFE" fs="4" />                     <!-- Proportional Sans-Serif    (Roboto)                -->
    <pen id="20" fc="#FEFEFE" fs="5" />                     <!-- Casual                     (Comic Saaans!)         -->
    <pen id="21" fc="#FEFEFE" fs="6" />                     <!-- Cursive                    (Monotype Corsiva)      -->
    <pen id="22" fc="#FEFEFE" fs="7" />                     <!-- Small Capitals             (Carrois Gothic SC)     -->
    
    <!-- Font sizes (supported on iOS but not Android)                              -->
    <!-- The value is a virtual percentage of the default size; the real            -->
    <!-- percentage is 100 + (sz - 100) / 4, meaning sz="200" will result           -->
    <!-- in text that's *not* twice as big as the default but only 25% bigger.      -->
    <!-- The values can't be negative, meaning the smallest you can go is a         -->
    <!-- virtual percentage of 0 which equates to a real percentage of 75.          -->
    <pen id="23" sz="30"  />
    <pen id="24" sz="100" />        <!-- Default -->
    <pen id="25" sz="300" />
    
    <!-- Ruby (not supported on mobile) -->
    <pen id="26" rb="1" />              <!-- Ruby enable (for kanji spans)      -->
    <pen id="27" rb="4" />              <!-- Ruby over   (for furigana spans)   -->
    <pen id="28" rb="5" />              <!-- Ruby under  (for furigana spans)   -->
    
    <!-- Subscript/superscript ("of" = "offset"). Not supported on mobile -->
    <pen id="29" fc="#FEFEFE" of="0" />              <!-- Subscript      -->
    <pen id="30" fc="#FEFEFE" of="1" />              <!-- Regular        -->
    <pen id="31" fc="#FEFEFE" of="2" />              <!-- Superscript    -->
    
    <!-- Text emphasis (dots above text; not currently allowed by YouTube subtitle upload) -->
    <pen id="32" fc="#FEFEFE" te="1" />     <!-- Allows bold text       -->
    <pen id="33" fc="#FEFEFE" te="2" />     <!-- Disallows bold text    -->
    
    <!-- Text combination (\ytpack in YTSubConverter). Not supported on mobile -->
    <pen id="34" fc="#FEFEFE" hg="1" />
    
    <!-- Horizontal text alignment -->
    <ws id="1" ju="0" />                <!-- Left   -->
    <ws id="2" ju="1" />                <!-- Right  -->
    <ws id="3" ju="2" />                <!-- Center -->
    
    <!-- Vertical text alignment (not supported on mobile) --> 
    <ws id="4" pd="2" sd="0" />         <!-- Characters positioned above each other but not rotated; columns going from right to left   -->
    <ws id="5" pd="2" sd="1" />         <!-- Same as above, but columns going from left to right                                        -->
    <ws id="6" pd="3" sd="0" />         <!-- Entire subtitle rotated 90° CCW, columns going from left to right                          -->
    <ws id="7" pd="3" sd="1" />         <!-- Same as above, but columns going from right to left                                        -->
    <!-- pd="1" sets the paragraph to RTL mode, making the spans appear horizontally but in the opposite order. -->
    
    <!-- Positions (ap: anchor point, ah: X coordinate (0 = left, 100 = right), av: Y coordinate (0 = top, 100 = bottom) -->
    <wp id="0"  ap="0" ah="0"   av="10" />     <!-- "ap" determines which point on the subtitle box gets placed at the specified position:             -->
    <wp id="1"  ap="0" ah="0"   av="10" />     <!--   0 ======== 1 ======== 2                                                                          -->
    <wp id="2"  ap="1" ah="50"  av="10" />     <!--   |                     |                                                                          -->
    <wp id="3"  ap="2" ah="100" av="10" />     <!--   3          4          5                                                                          -->
    <wp id="4"  ap="3" ah="0"   av="50" />     <!--   |                     |                                                                          -->
    <wp id="5"  ap="4" ah="50"  av="50" />     <!--   6 ======== 7 ======== 8                                                                          -->
    <wp id="6"  ap="5" ah="100" av="50" />
    <wp id="7"  ap="6" ah="0"   av="90" />     <!-- The server only accepts integers for "ah" and "av", even though the player accepts floating point. -->
    <wp id="8"  ap="7" ah="50"  av="90" />     <!-- In theater mode, the width covered by "ah" includes the black bars on the sides, meaning the       -->
    <wp id="9"  ap="8" ah="100" av="90" />     <!-- subtitles move towards the sides and even out of the video.                                        -->
    <wp id="10" ap="1" ah="50"  av="70" />     <!-- The player transforms the coordinates according to effectiveCoord = (specifiedCoord * 0.96) + 2,   -->
</head>                                        <!-- meaning subtitles don't appear *quite* where you want them to.                                     -->
<body>
    <!-- Text styles -->
    <p t="0" d="4000" wp="1" p="1">Bold</p>             <!-- t = start time in ms, d = duration in ms, wp = position ID, p = pen ID.                -->
    <p t="0" d="4000" wp="2" p="2">Italic</p>           <!-- t="0" should be avoided as it causes the Android app to ignore positioning             -->
    <p t="0" d="4000" wp="3" p="3">Underline</p>        <!-- and sometimes not display the subtitle at all. (t="1" works fine, however)             -->
    <p t="0" d="4000" wp="5"><s p="1">Bold
 <s p="2">Italic
 <s p="3">Underline
</p>
    
    <!--
        Usage of multiple spans has a bug: unless there's *some* text that's not part of any span, the server will remove
        the first span's "p" attribute, causing it to lose its formatting. Completely random, but that's how it is.
        The best workaround is to place a zero-width space after the first span, as this way there are no visual
        changes to the subtitle. (This workaround is used in all of the following examples)
     -->
    
    <!-- Foreground color/transparency -->
    <p t="4000" d="4000" wp="1" p="4">Red (a = 40)</p>
    <p t="4000" d="4000" wp="2" p="5">Green (a = 127)</p>
    <p t="4000" d="4000" wp="3" p="6">Blue (a = 255)</p>
    <p t="4000" d="4000" wp="5"><s p="4">Red 
&#8203;<s p="5">Green 
<s p="6">Blue
</p>
    
    <!-- Background color/transparency -->
    <p t="8000" d="4000" wp="1" p="7">Red (a = 40)</p>
    <p t="8000" d="4000" wp="2" p="8">Green (a = 127)</p>
    <p t="8000" d="4000" wp="3" p="9">Blue (a = 255)</p>
    <p t="8000" d="4000" wp="10" p="10">Opaque</p>
    <p t="8000" d="4000" wp="8"><s p="7">Red 
&#8203;<s p="8">Green 
<s p="9">Blue
</p>
    
    <!-- Edges -->
    <p t="12000" d="4000" wp="1" p="11">Edge type 1</p>
    <p t="12000" d="4000" wp="2" p="12">Edge type 2</p>
    <p t="12000" d="4000" wp="3" p="13">Edge type 3</p>
    <p t="12000" d="4000" wp="4" p="14">Edge type 4</p>
    <p t="12000" d="4000" wp="5"><s p="11">One 
&#8203;<s p="12">Two 
<s p="13">Three 
<s p="14">Four
</p>
    
    <!-- Fonts (one per paragraph) -->
    <p t="16000" d="4000" wp="1" p="15">Font 0</p>
    <p t="16000" d="4000" wp="2" p="16">Font 1</p>
    <p t="16000" d="4000" wp="3" p="17">Font 2</p>
    <p t="16000" d="4000" wp="4" p="18">Font 3</p>
    <p t="16000" d="4000" wp="5" p="19">Font 4</p>
    <p t="16000" d="4000" wp="6" p="20">Font 5</p>
    <p t="16000" d="4000" wp="7" p="21">Font 6</p>
    <p t="16000" d="4000" wp="8" p="22">Font 7</p>
    
    <!-- Fonts (multiple) -->
    <p t="20000" d="4000" wp="5"><s p="15">Zero 
&#8203;<s p="16">One 
<s p="17">Two 
<s p="18">Three 
<s p="19">Four 
<s p="20">Five 
<s p="21">Six 
<s p="22">Seven
</p>
    
    <!-- Font sizes -->
    <p t="24000" d="4000" wp="1" p="23">30%</p>
    <p t="24000" d="4000" wp="2" p="24">100%</p>
    <p t="24000" d="4000" wp="3" p="25">300%</p>
    <p t="24000" d="4000" wp="5"><s p="23">30% 
&#8203;<s p="24">100% 
<s p="25">300%
</p>
    
    <!-- Positioning -->
    <p t="28000" d="4000" wp="1">Top left</p>
    <p t="28000" d="4000" wp="2">Top center</p>
    <p t="28000" d="4000" wp="3">Top right</p>
    <p t="28000" d="4000" wp="4">Middle left</p>
    <p t="28000" d="4000" wp="5">Middle center</p>
    <p t="28000" d="4000" wp="6">Middle right</p>
    <p t="28000" d="4000" wp="7">Bottom left</p>
    <p t="28000" d="4000" wp="8">Bottom center</p>
    <p t="28000" d="4000" wp="9">Bottom right</p>
    
    <!-- Alignment -->
    <p t="32000" d="4000" wp="4" ws="1">Left-
aligned line</p>
    <p t="32000" d="4000" wp="5" ws="3">Centered
line</p>
    <p t="32000" d="4000" wp="6" ws="2">Right-
aligned line</p>

    <!-- Karaoke. Each span's "t" attribute gives its relative appearance time in ms.   -->
    <p t="36000" d="4000" wp="5" p="15">
Ka
<s t="1000">ra
<s t="2000">o
<s t="3000">ke
</p>
    
    <!-- Line breaks at section start/end (note the missing rounded corners - YouTube bug) -->
    <p t="40000" d="4000" wp="4"><s p="11">Line 
&#8203;<s p="12">break 
<s p="13">
at start 
<s p="14">of span
</p>
    <p t="40000" d="4000" wp="5"><s p="11">Line 
&#8203;<s p="12">break
in 
<s p="13">middle 
<s p="14">of span
</p>
    <p t="40000" d="4000" wp="6"><s p="11">Line 
&#8203;<s p="12">break

<s p="13">at end 
<s p="14">of span
</p>

    <!--
        Ruby text works by creating four spans for each ruby'd word:
        * A span for the kanji text, referring to a pen with rb="1"
        * A span with an opening parenthesis (fallback for clients without ruby support)
        * A span with the furigana text, optionally referring to a pen with rb="4"/"6" (above kanji) or "5"/"7" (below kanji)
        * A span with a closing parenthesis
        The ruby text always uses the same formatting as the base text (i.e. the other attributes of the furigana pen are ignored).
        
        The mobile apps don't support ruby text. They'll display the below subtitle as 漢(かん)字(じ).
    -->
    <p t="44000" d="4000" wp="5"><s p="26">漢

(
<s p="27">かん

)
<s p="26">字

(
<s p="28">じ

)
</p>
    
    <!-- Subscript/superscript -->
    <p t="48000" d="4000" wp="5"><s p="29">Subscript 
&#8203;<s p="30">Regular 
<s p="31">Superscript
</p>
    
    <!-- Vertical text -->
    <p t="52000" d="4000" wp="1" ws="4">Vertical
text (RTL)</p>
    <p t="52000" d="4000" wp="3" ws="5">Vertical
text (LTR)</p>
    <p t="52000" d="4000" wp="7" ws="7">Rotated
text (RTL)</p>
    <p t="52000" d="4000" wp="9" ws="6">Rotated
text (LTR)</p>
</body>
</timedtext>
```

# 参考文献

### YTSubConverter - GitHub

- Aegisubのデータをyttファイルに変換してくれるツール。

https://github.com/arcusmaximus/YTSubConverter

### Timed Text - Wikimedia Commons
https://commons.wikimedia.org/wiki/Commons:Timed_Text

### にじさんじ - Virtual to LIVE [Official Music Video]
https://www.youtube.com/watch?v=zPMWAj54Seg

### XMLファイル - にじさんじ Virtual to LIVE
http://video.google.com/timedtext?hl=ja&lang=ja&v=zPMWAj54Seg