我最近写了一些有关Multi主机实时流的文章,其中包括Amazon Interactive Video Service(Amazon IVS)。这是一个令人兴奋的功能,它打开了直到最近才能提供的可能性世界。我们首先研究了如何create a multi host live chat application。接下来,我们看到了如何将broadcast that live chat session到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个参与者,我们可以参考外部阵列中的第三个元素,并且participantIds
数组中参与者ID的位置将确定哪个构图将适用于该视频。我们可以修改我们的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
中获取适当的构图,并使用broadcastClient
的updateVideoDeviceComposition()
(docs)方法。
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 IV广播多主舞台时学到了一种动态布局的方法。在以后的帖子中,我们将研究与多个主机广播的其他选项。与往常一样,如果您有任何疑问或评论,请留在下面。