如何使用 Agora Web SDK 合并视频流

该博客的作者是Agora的 Super Star 阿迪亚·拉托雷 。Agora Super Star *计划使世界各地的开发人员能够分享他们的热情和技术专长,并使用 Agora 可定制的 SDK 新颖的实时通信APP和项目。

你好,各位网上冲浪者!你是否花了无数小时只为找到一种组合多个视频流的方法?或者你只是想为基于 Agora 的视频通话APP添加一个很酷的新功能?

简介

在本教程中,我们将试图通过 Agora SDK 将摄像头叠加添加到屏幕共享源并将其作为单个视频轨道流式传输的方法,这有助于在视频通话应用程序中提供更加流畅且可靠的演示体验。

那我们接下来该怎么做?

要求

项目设置

首先创建一个 HTML 文件并使用 CDN 链接获取 Agora Web SDK:

然后,创建一个 JS 文件并初始化一个变量来存储从Agora 控制台生成的 App ID :

创建基本用户界面

我们需要一个简单的表单来获取带有两个按钮的频道和用户名:演示者的“流”按钮和参会者的加入按钮:

此外,我们需要两个 div 来显示视频流:

注意: transform:rotateY(180deg) 用于垂直翻转远程视频流

基本条款

由于我们将与底层浏览器 API 交互以获取媒体提要,因此将几个相似的术语加以区分来避免混淆非常重要:

Track / MediaStreamTrack — 由硬件或软件生成的字节流的包装器对象,可以包含例如视频、音频和屏幕共享数据

Stream / MediaStream — 一个可以包含各种轨道和一些事件侦听器和辅助函数的对象

你可以在https://developer.mozilla.org/en-US/docs/Web/API/MediaStream阅读更多信息

设置 AgoraRTC

我们现在可以开始初始化 Agora 客户端了。

如果你已经熟悉这个过程并想跳到下一节,我们基本上用直播模式初始化客户端并绑定流初始化,“流”按钮只有音频,而加入按钮有视频和音频以及必要的离开按钮绑定。

我们首先将模式设置为 live 的 Agora RTC 客户端对象。你可以随意使用自己喜欢的编解码器,但对于这个项目,我将使用 H264:

由于我们将在多个函数中处理流,因此我们会初始化一些全局变量,并将在其中存储初始化的流。

我们初始化客户端对象并声明在处理函数过程中出现的错误,该函数将在项目中重复使用:

现在我们添加事件侦听器和配套的处理程序函数来定义APP的基本行为:

最后,我们定义加入和离开行为并将它们绑定到相关按钮:

createStream() 初始化一个流对象。但是,这是在 Agora SDK 的背景中,因此这与浏览器 API 略有不同。但从逻辑上讲,它是具有相似功能的同一实体。单击“加入”按钮时,媒体流将使用视频和音频轨道进行初始化,因为这是面向观众的。当点击“流”按钮时,一个流被初始化,没有任何视频轨道,因为这就是我们将在下一节中添加的有关sectioncreateStream()) 初始化流对象的内容。但是, Agora SDK 的背景与浏览器 API 略有不同。从逻辑上讲,它是具有相似功能的同一实体。单击“加入”按钮时,媒体流将使用视频和音频轨道进行初始化,因为这是面向观众的。单击“流”按钮时……

注意: 由于本教程的重点是流复用,因此我不会具体介绍基础知识。你可以阅读 https://www.agora.io/en/blog/building-a-group-video-chat-web-app/ 以更好地了解基本的 Agora Web SDK 工作流程以及上面的代码片段。

完成基本设置后,我们就能进入有趣的部分了:

实现视频复用

为了复用这两个流,我们必须将它们初始化。我们声明了一个新函数,它将为我们处理多路复用。在其中,我们调用重要的 browserAPI 函数来获取用户视频和屏幕流:

顾名思义,getUserVideo() 和 getUserScreen 函数将为我们提供具有所需视频轨道的必要媒体流。然而,这些轨道是原始数据,我们需要一种方法来对其解码。目前,我们用于合并两个流的 HTML5 画布无法解码视频流。因此,我们将使用两个离屏视频元素将数据流转换为视频。它们在文件开头初始化,具体如下所示:

让我们还初始化将用于合并两个视频的画布。这也将是屏幕外的,因为我们将使用 Agora SDK 来显示最终流:

此时,你可能想知道拥有三个屏幕外元素对性能的影响。答案是让它们离开屏幕并不会神奇地消除负载。然而,这种做法并非完全多余,因为大部分工作负载包括解码两个视频轨道,无论如何都必须完成解码才能显示它们。并且由于它们在屏幕外且位置固定,因此它们不会导致 DOM 重绘并有效地充当背景视频解码器。但仍然可以做到实现一些优化,我们将在本教程的后面进行介绍。

回到我们的多路复用功能,现在我们有了轨道,我们可以将它们传递给我们的视频元素并开始定义必要的参数。我们可以在画布上绘制一个基本形状并将其附加到 DOM:

有很多参数,并且这些参数都很重要。这张图可以帮助你理解到底发生了什么:

除了参数之外,我们还有一个 drawInterval,它是我们将在下一节中介绍的 drawVideo 函数(负责绘制视频的每一帧)将被调用所需的时间(以毫秒为单位)。它采用最高每秒帧数 (FPS),因此为了获得时间间隔,我们只需按每帧秒计算。

尺寸因素确定相机圆半径应采用的屏幕宽度百分比。

现在让我们转向要实现的核心,它是一个控制它们的函数,即 drawVideo 函数。以下是它的工作原理。

首先,我们从 video 元素的 screenStream 中取出一个帧并将其绘制在画布上。然后我们保存这个状态:

接下来,我们绘制一个圆弧(圆)并使用它来剪辑后续的摄像机流。因为有了剪辑功能,这为我们提供了相机流的圆形区域和其他地方的空白画布:


然后我们恢复最初保存的状态以恢复屏幕提要,除了已经绘制的东西 - 即圆形相机提要:

这是代码

现在,我们在 StreamMultiplexer 函数中调用此函数,并计算出绘制帧的间隔。浏览器 API 提供了捕获画布数据并将其转换为视频流的功能。使用 captureStream(),我们将视频轨道从生成的流传递到 globalStream:

最后在”流”按钮行为函数下调用client init后的函数

现在看看它是否真的有效

测试

为了进行测试,我们启动了一个 Web 服务器。我将使用 Live Server npm 包,其命令是:

cd ./project-directory
Live-server .

打开网站后,我们可以输入频道名称和用户名,然后单击“流”按钮,允许任何所需的权限并选择我们想要共享的屏幕。我们现在应该能够通过网络摄像头覆盖看到我们的流,然后可以复制我们的选项卡并使用更改的用户名单击加入按钮,以查看流是否已成功通过 Agora SDK 跨频道共享。

结论

这就是创建演示模式并将其集成到 Agora SDK 中所需的全部内容,我觉得它突出了 SDK 附带的灵活性和使用自由。仍然可以实现一些优化。例如,在像 Web APP的场景中,可以在工作线程中使用 offscreenCanvas,这可能有助于提高性能,以从主线程卸载渲染。有关更多信息,请参阅 https://developers.google.com/web/updates/2018/08/offscreen-canvas。

将视频流卸载到画布为各种视频操作打开了大门,相同的概念可用于为你的视频通话APP添加色度键控或自定义背景,可能性不止,力量永无限!

原文作者 Author:Aditya Rathore
原文链接 https://www.agora.io/en/blog/how-to-combine-video-streams-using-agora-web-sdk/

推荐阅读
相关专栏
SDK 教程
164 文章
本专栏仅用于分享音视频相关的技术文章,与其他开发者和声网 研发团队交流、分享行业前沿技术、资讯。发帖前,请参考「社区发帖指南」,方便您更好的展示所发表的文章和内容。