RealtimeKit のインタラクティブ録画では、ビデオストリームにタイムドメタデータ(timed metadata)を追加できます。タイムドメタデータは、クライアントが情報を表示し、時間に合わせて処理を起動するためのキューポイントです。メタデータは、再生タイムライン上の ID3 ↗ タグとしてクライアントから利用できます。
Netflix のシークバーに小さな画像が出たり、Hotstar でクリケット中継を見ているときに追加コンテンツが出たりするのはなぜか、と思ったことはありませんか。これはすべて、映像フィード自体の特定時刻に挿入されたメタデータで、タイムドメタデータと呼ばれます。
タイムドメタデータは、タイムスタンプ付きのメタデータです。動画ファイルにデジタルマーカーを追加し、コンテンツ範囲の特定地点で追加の文脈と情報を提供します。これらのデータポイントは、Start recording a meeting API の interactive_config を使って、プログラムからストリームへ挿入できます。
RealtimeKit がストリームを処理すると、タイムドメタデータは音声フレームと映像フレームに同期します。再生中、すべての視聴者はストリームに対して同じ相対時刻でこのメタデータを利用できます。タイムコードはキューポイントとして機能し、データに基づいて特定の処理を起動できます。たとえば次のような使い方ができます。
これらの機能は、動画セグメントに埋め込まれた ID3 タグによって実現され、録画した動画でも利用できます。
RealtimeKit の録画にインタラクティブ性を追加するには、次の手順を実行します。
- Start recording a meeting API で、
interactive_configパラメーターを渡します。
このパラメーターで録画にタイムドメタデータを追加できます。クライアントは HLS 形式で、ID3 タグとして利用できます。出力ファイルは tar ファイルとしてパッケージされます。
- RealtimeKitClient ↗ で、participants ↗ オブジェクトに対して
broadcastMessageメソッドを呼び出します。引数はID3(文字列)とyourData(タイムドメタデータとして送りたいデータ)です。
meeting.participants.broadcastMessage(“ID3Data”, yourData);- データの送信を止めるには、次のメソッドを呼び出します。この呼び出しのあと、追加の ID3 データは送れません。
meeting.participants.broadcastMessage(“ID3Data”,”CLOSE_TRANSPORT”)このパラメーターを渡さない場合、ID3 メタデータストリームは録画停止時に自動で閉じます。
- 録画が完了したら、動画セグメントとプレイリストファイルを含む tar ファイルを取得できます。
download_urlが tar ファイルへのリンクです。tar ファイルのスクリーンショット例は次のとおりです。
各セグメントの長さは映像のフレームに依存します。そのため、セグメント長は同じとは限りませんが、録画開始時に指定したセグメント長に近いことが一般的です。デフォルトのセグメント長は 10 秒です。
- ストリームは
hls.js↗ で再生できます。
const onFragChanged = (_) => {
// We first try to find the right metadata track.
// https://developer.mozilla.org/en-US/docs/Web/API/TextTrack
const textTrackListCount = videoEl.textTracks.length;
let metaTextTrack;
for (let trackIndex = 0; trackIndex < textTrackListCount; trackIndex++) {
const textTrack = videoEl.textTracks[trackIndex];
if (textTrack.kind !== 'metadata') {
continue;
}
textTrack.mode = 'showing';
metaTextTrack = textTrack;
break;
}
if (!metaTextTrack) {
return;
}
// Add an oncuechange listener on that track.
metaTextTrack.oncuechange = (event) => {
let cue = metaTextTrack.activeCues[metaTextTrack.activeCues.length - 1];
console.log(cue.value.data);
};
};
// listen on Hls.Events.FRAG_CHANGED from hls.js
hls.on(Hls.Events.FRAG_CHANGED, onFragChanged);