3 チャット リプレイを使用した Amazon IVS ライブ ストリームの再生 |
この問題を解決しようとして、私が最初に考えたのは、ライブ ストリームに関連付けられたメタデータを調べて、その中に貴重な情報が隠されているかどうかを確認することでした。ありがたいことに、チャットの再生目的で使用できるメタデータの通常のストリームには価値があるようです.私のテストでは、各ストリームには、Amazon IVS トランスコーディング プロセスによって注入されたように見える ID3 メタデータが含まれています。これらの ID3 タグには、チャットのリプレイに役立つタイムスタンプが含まれています。これらのイベントをリッスンするために、 IVSPlayer.MetadataEventType.ID3
イベント タイプをリッスンするハンドラーをアタッチできます。このイベントの種類は文書化されていますが、それについて多くのことは書かれておらず、何が含まれているかも保証されていません。
文書化されていない機能を避けたいですか?文書化されていない機能を使用することに懸念がある場合は、新しいメッセージが Amazon IVS チャット ルームに投稿されたときに、適切なタイムできます。 API を介して
PutMetadata
イベントをがあることに注意してください。
Player SDK を使用して、記録されたストリームを再生するように Amazon IVS プレーヤーをセットアップしましょう。まず、 <script>
タグを介して最新の Amazon IVS プレーヤー SDK を含めます。
Amazon IVS は初めてですか?ブログ シリーズご覧ください。開始について質問がある場合は、そのシリーズ (または以下) のいずれかの投稿にコメントを投稿してください。
<script src="//player.live-video.net/1.16.0/amazon-ivs-player.min.js"></script>
いつものように、再生に使用する HTML マークアップに<video>
要素を含める必要があります。
<video id="video-player" muted controls autoplay playsinline></video>
const streamUrl = '//[redacted].cloudfront.net/ivs/v1/[redacted]/[redacted]/2022/11/17/18/6/[redacted]/media/hls/master.m3u8'; const videoEl = document.getElementById('video-player'); const ivsPlayer = IVSPlayer.create(); ivsPlayer.attachHTMLVideoElement(videoEl); ivsPlayer.load(streamUrl); ivsPlayer.play();
ivsPlayer.addEventListener(IVSPlayer.PlayerState.PLAYING, (evt) => { window.time = Date.now(); });
ivsPlayer.addEventListener(IVSPlayer.MetadataEventType.ID3, (evt) => { const now = Date.now(); console.log(`${(now - window.time) / 1000} seconds since last event`); window.time = now; });
window.ivsPlayer.addEventListener(IVSPlayer.MetadataEventType.ID3, (evt) => { console.log(evt); });
これは非常に興味深い情報ですが、少し不可解です。私のテストに基づいて、 transc_s
私たちが求めているタイムスタンプのようです。そのタイムスタンプを取得してログに記録するようにイベント ハンドラーを変更しましょう。
window.ivsPlayer.addEventListener(IVSPlayer.MetadataEventType.ID3, (evt) => { const segmentMetadata = evt.find((tag) => tag.desc === 'segmentmetadata'); const segmentMetadataInfo = JSON.parse(segmentMetadata.info[0]); const timestamp = segmentMetadataInfo['transc_s']; const timestampWithMs = timestamp * 1000; console.log(timestampWithMs); console.log(new Date(timestamp)); });
ページが読み込まれると、このシリーズの前回の投稿 [todo: リンク] で概説した方法を使用して、ストリームのチャット ログ全体を取得し、チャット コンテナー<div>
でレンダリングできます。ストリームの開始時にメッセージが表示されないようにする必要があるため、メッセージをユーザーから非表示にするクラスが含まれていることを確認し、適切なタイムスタンプを持つデータ属性を保存して、どのメッセージを表示する必要があるかを知ることができます。ストリーム内の任意のタイムスタンプが与えられます。
window.chatLog = await getChatLogs(logGroupName, chatArn, startTime, endTime); renderChat();
私のrenderChat()
関数は、各メッセージのチャット コンテナーへの投稿を処理します。
const renderChat = () => { const chatContainer = document.getElementById('chat'); window.chatLog.forEach(msg => { const msgTemplate = document.getElementById('chatMsgTemplate'); const msgEl = msgTemplate.content.cloneNode(true); const ts = new Date(msg.event_timestamp).getTime() * 1000; msgEl.querySelector('.msg-container').setAttribute('data-timestamp', ts); msgEl.querySelector('.chat-username').innerHTML = msg.payload.Attributes.username; msgEl.querySelector('.msg').innerHTML = msg.payload.Content; chatContainer.appendChild(msgEl); }); };
これで、ID3 リスナーを変更してreplayChat()
関数を呼び出し、現在のタイムスタンプを渡すことができます。
window.ivsPlayer.addEventListener(IVSPlayer.MetadataEventType.ID3, (evt) => { const segmentMetadata = evt.find((tag) => tag.desc === 'segmentmetadata'); const segmentMetadataInfo = JSON.parse(segmentMetadata.info[0]); const timestamp = segmentMetadataInfo['transc_s']; const timestampWithMs = timestamp * 1000; replayChat(timestampWithMs); });
replayChat()
では、記録されたストリームから現在のタイムスタンプ以下のタイムスタンプを含むすべてのチャット ノードを検索し、そのタイムスタンプに基づいてチャット メッセージを表示/非表示にすることができます。
const replayChat = (currentTimestamp) => { Array.from(document.querySelectorAll('[data-timestamp]')).forEach(node => { const chatMsgTs = Number(node.getAttribute('data-timestamp')); const isVisible = chatMsgTs <= currentTimestamp; if (isVisible) { node.classList.remove('d-none'); } else { node.classList.add('d-none'); } }); const chatContainer = document.getElementById('chat'); chatContainer.scrollTop = chatContainer.scrollHeight; }