2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

TVerで字幕出るけどあれどうなってるんだろう

TVerで字幕のある動画を見たりするのですが、あれはどうなっているのか気になったので調べてみました。

HTTP Live Streaming (HLS) における字幕

RFC8216にはHLS全般についての定義がされており、字幕についても定義されています。
TVerで配信しているデータもこの形式に沿っていました。

プレイリストの種類

HLSでは、以下のようなプレイリストが使用されます。
それぞれUTF-8エンコードのファイルです。

マスタープレイリスト

マスタープレイリストは、複数のビデオ、オーディオ、字幕のプレイリストを関連付けるために使用されます。

ビデオプレイリスト

ビデオプレイリストは、ビデオコンテンツのセグメントを含むプレイリストです。

オーディオプレイリスト

オーディオプレイリストは、オーディオコンテンツのセグメントを含むプレイリストです。

字幕プレイリスト

字幕プレイリストは、字幕のセグメントを含むプレイリストです。
字幕はWebVTT形式で提供されることが多いようです。

プレイリストの関連

マスタープレイリストと各種プレイリストの関連は以下のような感じです:

マスタープレイリスト
├── ビデオプレイリスト1
│   ├── video_segment1.ts
│   ├── video_segment2.ts
│   └── video_segment3.ts
├── ビデオプレイリスト2
│   ├── video_segment1.ts
│   ├── video_segment2.ts
│   └── video_segment3.ts
├── オーディオプレイリスト1
│   ├── audio_segment1.ts
│   ├── audio_segment2.ts
│   └── audio_segment3.ts
└── 字幕プレイリスト1
    ├── subtitle_segment1.vtt
    ├── subtitle_segment2.vtt
    └── subtitle_segment3.vtt

このように、マスタープレイリストが各種ビデオプレイリスト、オーディオプレイリスト、および字幕プレイリストを関連付けています。

TVerの字幕を読み解いてみる

TVerで、さがすから字幕ありをクリックすると、字幕がある動画がリストアップされます。
この中から適当なデータを選んで見ていきます。
動画のページを開いて、開発者ツールでネットワークタブを表示し、m3u8でフィルタします。
一番最初に検出されるファイルがおそらくマスタープレイリストだと思われるので、最初に検出されたパスのデータを選んで表示してみるとこのようなデータが入っていました:

#EXTM3U
#EXT-X-VERSION:4
#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="audio-0",NAME="en (Main)",DEFAULT=YES,AUTOSELECT=YES,LANGUAGE="en",URI="https://example.com/hls/rendition.m3u8"
#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="subtitles-0",NAME="字幕",DEFAULT=YES,AUTOSELECT=YES,LANGUAGE="ja-JP",URI="https://example.com/hls/rendition.m3u8"
#EXT-X-STREAM-INF:PROGRAM-ID=0,BANDWIDTH=1039500,CODECS="mp4a.40.2,avc1.4d001f",RESOLUTION=960x540,AUDIO="audio-0",CLOSED-CAPTIONS=NONE,SUBTITLES="subtitles-0"
https://example.com/hls/rendition.m3u8
#EXT-X-I-FRAME-STREAM-INF:PROGRAM-ID=0,BANDWIDTH=1039500,CODECS="mp4a.40.2,avc1.4d001f",RESOLUTION=960x540,URI="https://example.com/hls/iframe.m3u8"
#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="audio-1",NAME="en (Main)",DEFAULT=YES,AUTOSELECT=YES,LANGUAGE="en",URI="https://example.com/hls/rendition.m3u8"
#EXT-X-STREAM-INF:PROGRAM-ID=0,BANDWIDTH=334400,CODECS="mp4a.40.2,avc1.420015",RESOLUTION=480x270,AUDIO="audio-1",CLOSED-CAPTIONS=NONE,SUBTITLES="subtitles-0"
https://example.com/hls/rendition.m3u8
#EXT-X-I-FRAME-STREAM-INF:PROGRAM-ID=0,BANDWIDTH=334400,CODECS="mp4a.40.2,avc1.420015",RESOLUTION=480x270,URI="https://example.com/hls/iframe.m3u8"
#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="audio-2",NAME="en (Main)",DEFAULT=YES,AUTOSELECT=YES,LANGUAGE="en",URI="https://example.com/hls/rendition.m3u8"
#EXT-X-STREAM-INF:PROGRAM-ID=0,BANDWIDTH=570900,CODECS="mp4a.40.2,avc1.4d001e",RESOLUTION=640x360,AUDIO="audio-2",CLOSED-CAPTIONS=NONE,SUBTITLES="subtitles-0"
https://example.com/hls/rendition.m3u8
#EXT-X-I-FRAME-STREAM-INF:PROGRAM-ID=0,BANDWIDTH=570900,CODECS="mp4a.40.2,avc1.4d001e",RESOLUTION=640x360,URI="https://example.com/hls/iframe.m3u8"
#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="audio-3",NAME="en (Main)",DEFAULT=YES,AUTOSELECT=YES,LANGUAGE="en",URI="https://example.com/hls/rendition.m3u8"
#EXT-X-STREAM-INF:PROGRAM-ID=0,BANDWIDTH=1669800,CODECS="mp4a.40.2,avc1.4d001f",RESOLUTION=1280x720,AUDIO="audio-3",CLOSED-CAPTIONS=NONE,SUBTITLES="subtitles-0"
https://example.com/hls/rendition.m3u8
#EXT-X-I-FRAME-STREAM-INF:PROGRAM-ID=0,BANDWIDTH=1669800,CODECS="mp4a.40.2,avc1.4d001f",RESOLUTION=1280x720,URI="https://example.com/hls/iframe.m3u8"
#EXT-X-STREAM-INF:PROGRAM-ID=0,BANDWIDTH=3163600,CODECS="mp4a.40.2,avc1.640028",RESOLUTION=1920x1080,AUDIO="audio-3",CLOSED-CAPTIONS=NONE,SUBTITLES="subtitles-0"
https://example.com/hls/rendition.m3u8
#EXT-X-I-FRAME-STREAM-INF:PROGRAM-ID=0,BANDWIDTH=3163600,CODECS="mp4a.40.2,avc1.640028",RESOLUTION=1920x1080,URI="https://example.com/hls/iframe.m3u8"

#EXT-X-MEDIA:TYPE=SUBTITLESかつ、LANGUAGE="ja-JP"のURLのデータが日本語の字幕のようです。
このURLのデータを取得してみます。

#EXTM3U
#EXT-X-VERSION:3
#EXT-X-PLAYLIST-TYPE:VOD
#EXT-X-MEDIA-SEQUENCE:0
#EXT-X-TARGETDURATION:30
#EXTINF:30.000,
https://example.com/hls/segment0.vtt
#EXTINF:30.000,
https://example.com/hls/segment1.vtt
#EXTINF:30.000,
https://example.com/hls/segment2.vtt
#EXTINF:30.000,
https://example.com/hls/segment3.vtt
#EXTINF:30.000,
https://example.com/hls/segment4.vtt
#EXTINF:30.000,
https://example.com/hls/segment5.vtt
#EXTINF:30.000,
https://example.com/hls/segment6.vtt
#EXTINF:30.000,
https://example.com/hls/segment7.vtt
#EXTINF:30.000,
https://example.com/hls/segment8.vtt
#EXTINF:30.000,
https://example.com/hls/segment9.vtt
#EXTINF:30.000,
https://example.com/hls/segment10.vtt
:
:
#EXT-X-ENDLIST

WebVTTの字幕のURLがリストアップされていました。
EXTINFを見ると、30秒ごとに区切られているようです。
この中身を見てみます。

WEBVTT
X-TIMESTAMP-MAP=LOCAL:00:00:00.000,MPEGTS:0

00:02.033 --> 00:06.561 line:60% position:17.71% align:start
<c.fgc-7.bgc-65.sdc-0>生字幕放送です。一部、字幕で</c>

00:02.033 --> 00:06.561 line:72% position:17.71% align:start
<c.fgc-7.bgc-65.sdc-0>表現しきれない場合があります。</c>
:
:

放送局によりますが、linepositionalignなどの属性があります。
これらはRFCに定義されていないのでTVer独自の属性だと思われます。
lineはtopからの位置、positionalignからの位置のような感じだと思います。

また、<c.fgc-7.bgc-65.sdc-0> のようなタグっぽいものもTVer独自のもののようで、スピーカーを区別するためのタグのような感じでした。

まとめ

字幕のデータはWebVTT形式で提供されていることが多いようです。
NHK+の字幕もWebVTT形式でしたがかなりクセが強かったです。
NetflixやAmazon Prime等の動画配信サービスは暗号化が施されているので、字幕のデータを取得することはできませんでした。
普段何気なく見ている字幕も、HLSという技術を使って配信されていることがわかりました。

参考

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?