paint-brush
如何使用 Amazon IVS 创建多主机广播的动态布局经过@amazonivs
2,395 讀數
2,395 讀數

如何使用 Amazon IVS 创建多主机广播的动态布局

太長; 讀書

在上一篇文章中,我们了解了如何使用 Amazon Interactive Video Service (Amazon IVS) 创建多主机实时聊天应用程序。在那篇文章中,我做了一些作弊,并对告诉广播客户端参与者的大小和位置的值进行了硬编码。客户端上的视频。我们将研究一种可以用来更轻松地创建动态布局的方法。
featured image - 如何使用 Amazon IVS 创建多主机广播的动态布局
Amazon Interactive Video Service (IVS)  HackerNoon profile picture

我最近写了几篇有关使用 Amazon Interactive Video Service (Amazon IVS) 进行多主机直播的文章。这是一项令人兴奋的功能,它开启了直到最近才出现的可能性世界。我们首先了解了如何创建多主机实时聊天应用程序


接下来,我们了解了如何将实时聊天会话广播到 Amazon IVS 频道。


当我们在上一篇文章中考虑将聊天参与者添加到广播客户端时,您可能注意到我做了一些作弊,并对VideoComposition值进行了硬编码,这些值告诉广播客户端参与者视频在客户端上的大小和位置。


好吧 - 欺骗是一个很强烈的词 - 假设我故意简化了代码以专注于广播实时聊天会话的过程。


本质上,我们在这里要寻找的是修改广播中参与者视频的大小和位置,以便当有一个视频时,布局将如下所示:



但是当有两个视频时,布局会变成这样:



当有五个时:



您明白了——根据参与者数量而变化的动态布局。


在这篇文章中,我们将介绍一种可以用来更轻松地创建动态布局的方法。我们将在上一篇文章中构建解决方案,因此如果您还没有阅读该文章,那么现在阅读可能是个好主意。


在上一篇文章中,我们监听了一个名为STAGE_PARTICIPANT_STREAMS_ADDED的事件。在该事件的事件处理程序中,我们将参与者添加到 DOM 并将音频和视频渲染到IVSBroadcastClient实例。


为了呈现动态布局,我们需要跟踪会话中当前有多少参与者,因此我们将添加一个名为participantIds的数组作为全局变量。让我们修改事件处理程序以将当前参与者 ID 推送到该数组。


 stage.on(StageEvents.STAGE_PARTICIPANT_STREAMS_ADDED, (participant, streams) => { //add participant id to array participantIds.push(participant.id); renderParticipant(participant, streams); renderVideosToClient(participant, streams.find(s => s.streamType === StreamType.VIDEO)); renderAudioToClient(participant, streams.find(s => s.streamType === StreamType.AUDIO)); updateVideoCompositions(); });


在上一篇文章中,我提到未显示updateVideoCompositions()方法,因为实现会有所不同。我们稍后将讨论一种可能的实现。


现在,让我们看看如何获取动态布局配置,而不是像上一篇文章中那样对其进行硬编码。


获取动态大小和位置的一种方法是循环访问参与者数组,并根据参与者的数量、 <canvas>的大小以及所需的行、列和填充量来计算它们。但是,为什么


当您意识到这些值永远不会改变时,这听起来像是很多困难的代码和不必要的工作。如果您有一名参与者,视频将具有固定大小并位于<canvas>中心。


添加多少参与者并不重要 - 对于给定数量的参与者,每个视频的布局将始终相同。那么,当我们可以预先计算这些值并将它们存储在数组的数组中时,为什么还要浪费时间和 CPU 周期呢?


在我的演示中,我花了 30 分钟的时间,用笔、纸和计算器密集地确定了最佳值,以确定每种可能布局的构图值。请注意:我不是数学或艺术专业的,如下图所示。



对于此演示,我将直播限制为仅向前 6 位参与者显示视频。您的用例可能会规定一些不同的内容,但根据我的经验,在一个直播流中包含超过 6 个参与者的视频会变得有点过于繁忙。


这是我的计算结果:


 const layouts = [ [{ height: 720, width: 1280, x: 320, y: 180 }], [{ height: 450, width: 800, x: 80, y: 315 }, { height: 450, width: 800, x: 1040, y: 315 }], [{ height: 450, width: 800, x: 80, y: 45 }, { height: 450, width: 800, x: 1040, y: 45 }, { height: 450, width: 800, x: 560, y: 585 }], [{ height: 450, width: 800, x: 80, y: 45 }, { height: 450, width: 800, x: 1040, y: 45 }, { height: 450, width: 800, x: 80, y: 585 }, { height: 450, width: 800, x: 1040, y: 585 }], [{ height: 337, width: 600, x: 20, y: 100 }, { height: 337, width: 600, x: 650, y: 100 }, { height: 337, width: 600, x: 1280, y: 100 }, { height: 337, width: 600, x: 340, y: 640 }, { height: 337, width: 600, x: 980, y: 640 }], [{ height: 337, width: 600, x: 20, y: 100 }, { height: 337, width: 600, x: 650, y: 100 }, { height: 337, width: 600, x: 1280, y: 100 }, { height: 337, width: 600, x: 20, y: 640 }, { height: 337, width: 600, x: 650, y: 640 }, { height: 337, width: 600, x: 1280, y: 640 }] ];


这可能看起来令人难以承受,但请考虑外部数组元素中的每个元素都包含每个视频的组成数组。


如果有 3 个参与者,我们可以引用外部数组中的第三个元素,参与者 id 在participantIds数组中的位置将决定哪个合成将应用于该视频。


我们可以修改renderVideosToClient()函数以获取正确的组合,并在将视频添加到广播客户端时使用这些值。


 const renderVideosToClient = async (participant, stream) => { const participantId = participant.id; const videoId = `video-${participantId}`; // get the index of this participantId const pIdx = participantIds.indexOf(participantId); let composition = layouts[participantIds.length - 1][pIdx]; config.index = 2; const mediaStream = new MediaStream(); mediaStream.addTrack(stream.mediaStreamTrack); broadcastClient.addVideoInputDevice(mediaStream, videoId, composition); };


但请记住 - 如果我们仅在添加参与者时执行此操作,则之前的视频合成仍将反映添加时应用的合成。这就是updateVideoCompositions()函数发挥作用的地方。


在这里,我们循环访问participantIds数组,从layouts中获取正确的组合,并使用updateVideoDeviceComposition() 文档broadcastClient的方法。


 const updateVideoCompositions = async () => { let idx = 0; for (const p of participantIds) { const videoId = `video-${p}`; let config = layouts[filteredParticipantIds.length - 1][idx]; config.index = 2; broadcastClient.updateVideoDeviceComposition(videoId, config); idx = idx + 1; } };


我们还应该确保当参与者离开舞台时,我们从数组中删除参与者 ID,并再次更新所有视频的合成。


 stage.on(StageEvents.STAGE_PARTICIPANT_STREAMS_REMOVED, (participant, streams) => { const participantId = participant.id; // remove participant id from array const pIdx = participantIds.findIndex(id => id === participantId); participantIds.splice(pIdx, 1); const videoTrackId = `video-${participantId}`; const audioTrackId = `audio-${participantId}`; if (broadcastClient.getVideoInputDevice(videoTrackId)) broadcastClient.removeVideoInputDevice(videoTrackId); if (broadcastClient.getAudioInputDevice(audioTrackId)) broadcastClient.removeAudioInputDevice(audioTrackId); const videoId = `${participantId}-video`; document.getElementById(videoId).closest('.participant-col').remove(); updateVideoCompositions(); });


如上所述,您很可能希望限制通过广播客户端添加到直播流的视频量。您可能想要添加静态图像而不是最终视频,以显示参与者数量多于显示的数量:


概括

在这篇文章中,我们学习了使用 Amazon IVS 广播多主机舞台时动态布局的一种方法。在以后的文章中,我们将研究使用多个主机进行广播的其他选项。一如既往,如果您有任何问题或意见,请在下面留下。


也发布在这里