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