3 Amazon IVS 实时流媒体回放与聊天回放 |
在尝试解决这个问题时,我的第一个想法是查看与直播相关的元数据,看看其中是否隐藏了任何有价值的信息。值得庆幸的是,常规元数据流中似乎确实有一个值可用于我们的聊天回放目的。在我的测试中,每个流都包含似乎由 Amazon IVS 转码过程注入的 ID3 元数据。这些 ID3 标签包含一个有用的时间戳,我们可以使用它来帮助聊天重播。要侦听这些事件,我们可以附加一个侦听IVSPlayer.MetadataEventType.ID3
事件类型的处理程序。此事件类型已记录在案,但并未对其进行过多说明或对其可能包含的内容做出任何保证。
想要避免未记录的功能?如果您担心使用未记录的功能,您可以在新消息发布到您的 Amazon IVS 聊天室时,将,并使用正确的时间戳。请记住,通过 API 发布
PutMetadata
事件。
让我们设置一个 Amazon IVS 播放器来使用播放器 SDK 播放录制的流。首先,我们将通过<script>
标签包含最新的 Amazon IVS 播放器 SDK。
亚马逊 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: link] 中概述的方法来检索流的整个聊天记录并将其呈现在聊天容器<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; }