hls.js と videojs-contrib-hls で Timed Metadata を同期テキストとして扱う
Posted: Updated:
Timed Metadata を取り出したい
HLS では MPEG-2 Transport Stream の中に、Timed Metadata として ID3 形式のデータを混ぜ込むことをサポートしています。HLS を扱う各ライブラリにおけるメタデータの取り出し方メモ。
videojs-contrib-hls
例によって v0.17 です。videojs では TextTrack や TextTrackCueList を摸したオブジェクトが実装されているので、それらからデータを順次取得できます。
現在の master にも player.textTracks()
が実装されていたので、恐らく似たような雰囲気のままと思われます。
cuechange イベントを listen するだけで OK
cuechange
イベントがくるたびに textTrack.activeCues[0]
を取り出せば、その瞬間のセグメントに含まれていた Timed Metadata を取得できます。
const textTrack = player.textTracks()[0]
textTrack.addEventListener('cuechange',() => {
if (!textTrack.activeCues[0]) {
return;
}
console.log('cuechange', textTrack.activeCues[0].text);
});
hls.js
こちらは v0.5 です。hls.js はデータを受信するたびに Timed Metadata のパースはしてくれますが、これだけだと動画の再生状態と同期してメタデータを処理することができません。
パース時のイベントは hls.on(Hls.Events.FRAG_PARSING_METADATA, (e) => {})
で得られます。videojs と同じように TextTrack の cuechange
イベントでタイミングを合わせて処理するために一手間加えます。
自力で VTTCue を作成して TextTrack に加えれば OK
video.addTextTrack('metadata')
として hls.js に attach する Video 要素に TextTrack を追加します。これに FRAG_PARSING_METADATA
のタイミングでキューを作成して追加すれば OK。あとは同じように TextTrack の cuechange
を listen すれば再生タイミングに合わせてメタデータを処理できます。
ブラウザネイティブな仕組みを使っているので、クロスブラウザの罠が潜んでいる可能性アリ
const video = document.createElement('video');
const track = video.addTextTrack('metadata');
const hls = new Hls();
hls.on(Hls.Events.FRAG_PARSING_METADATA, (event, data) => {
data.samples.map(sample => {
const cue = new (window.VTTCue || window.TextTrackCue)(-1, -1, '');
cue.text = String.fromCharCode.apply(null, [].slice.call(sample.data, 22));;
cue.pauseOnExit = true;
cue.startTime = sample.pts;
cue.endTime = Number.MAX_VALUE;
track.addCue(cue);
});
});
track.addEventListener('cuechange', function() {
const current = this.activeCues[this.activeCues.length - 1];
let cue, i = 0;
while ((cue = this.activeCues[i++])) {
if (cue === current) continue;
cue.endTime = current.startTime;
}
console.log('cuechange', current.text);
});
startTime と endTime が適切に設定されたキューを入れれば、activeCues の中身はひとつになるはずですが、上記の例では横着して PTS を startTime として代入しているだけです。
最新の有効なキューは activeCues の末尾に追加されるので、それを cuechange
の際に現在のキューとして扱います。これを特定したのち、activeCues 内に残る他のキューの endTime に現在のキューの startTime を代入することで、入れ替わりのごとく既存キューを処分しています。(これで endTime の値をみて勝手に消えていきます)
参考
- track 要素のスクリプトによる操作 (Windows)
- HTML Standard - Embedded content
- Getting Started With the Track Element - HTML5 Rocks