3 Amazon IVS 实时流媒体回放与聊天回放 |
在我们最近的几篇博文中,我们研究了如何将 Amazon Interactive Video Service (Amazon IVS) 实时流自动录制到 Amazon S3,以及如何记录发送到 Amazon IVS 聊天室的消息(参见上面的链接)。在这篇文章中,我们将把这两个概念结合在一起来完善用户体验,并创建具有完整聊天重播功能的过去直播流的点播回放。
正如我们在上一篇文章中看到的那样,记录到日志记录目标的聊天消息包括一个基于 GMT 的时间戳,代表 Amazon IVS 聊天室发布事件的日期和时间。为了实现实时流的真正按需重播,并在最接近的时间及时完成消息的交互式聊天重播,我们需要从记录的流中获取基于 GMT 的时间戳的常规流,我们可以使用它来确定什么聊天消息应该在流播放中的任何给定时间点可见。目前没有提供此信息的文档来源,但让我们仔细研究一下 Amazon IVS播放器 SDK ,看看是否可以找到可以帮助我们完成此任务的内容。
在尝试解决这个问题时,我的第一个想法是查看与直播相关的元数据,看看其中是否隐藏了任何有价值的信息。值得庆幸的是,常规元数据流中似乎确实有一个值可用于我们的聊天回放目的。在我的测试中,每个流都包含似乎由 Amazon IVS 转码过程注入的 ID3 元数据。这些 ID3 标签包含一个有用的时间戳,我们可以使用它来帮助聊天重播。要侦听这些事件,我们可以附加一个侦听IVSPlayer.MetadataEventType.ID3
事件类型的处理程序。此事件类型已记录在案,但文档并未对其进行过多说明或对其可能包含的内容做出任何保证。
想要避免未记录的功能?如果您担心使用未记录的功能,您可以在新消息发布到您的 Amazon IVS 聊天室时,将您自己的定时元数据注入到您的实时流中,并使用正确的时间戳。请记住,通过 API 发布
PutMetadata
事件的大小和频率是有限制的。
让我们设置一个 Amazon IVS 播放器来使用播放器 SDK 播放录制的流。首先,我们将通过<script>
标签包含最新的 Amazon IVS 播放器 SDK。
亚马逊 IVS 的新用户?查看博客系列Amazon Interactive Video Service 入门。如果您对入门有疑问,请对该系列(或以下)中的任何帖子发表评论!
<script src="https://player.live-video.net/1.16.0/amazon-ivs-player.min.js"></script>
像往常一样,我们需要在我们的 HTML 标记中包含一个用于播放的<video>
元素。
<video id="video-player" muted controls autoplay playsinline></video>
现在我们可以创建一个 IVS 播放器的实例。我正在对下面的 URL 进行硬编码,但您可以通过这篇文章 [todo: 链接] 中描述的方法获取此 URL。
const streamUrl = 'https://[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();
如上所述,为了达到这个目的,我们需要一个常规的时间戳流。为了计算接收 ID3 元数据的频率,让我们添加一些时间。首先,让我们在流开始播放时立即捕获时间戳。
ivsPlayer.addEventListener(IVSPlayer.PlayerState.PLAYING, (evt) => { window.time = Date.now(); });
接下来,我们将添加 ID3 事件侦听器、记录时间并重置计时器。
ivsPlayer.addEventListener(IVSPlayer.MetadataEventType.ID3, (evt) => { const now = Date.now(); console.log(`${(now - window.time) / 1000} seconds since last event`); window.time = now; });
现在我们可以开始播放并观察控制台以了解触发事件的频率。
在我的测试中,事件每 1-2 秒触发一次。它不是实时的,但对于大多数场景来说可能已经足够好了。现在让我们看一下事件,看看它包含什么数据。
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)); });
这会为我的测试生成以下输出。
如果我在视频中寻找一个随机时刻,正确的时间戳总是正确的。这意味着每隔 1-2 秒我们就会知道在流中捕获事件的那一刻的有效 GMT 时间戳。这意味着我们可以假设在此时间戳之前发送的所有聊天消息都已发布到聊天中,并且应该在聊天容器中可见。
当我的页面加载时,我可以使用本系列上一篇文章 [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; }
至此,我们已经实现了以完整聊天回放的方式回放录制的 Amazon IVS 直播流的目标。
在这篇文章中,我们研究了如何将录制的 Amazon IVS 实时流与记录的聊天消息相结合,以创建具有适当定时聊天消息的流的按需重播。