Feedback

优化多人视频通话

在多人视频通话场景中,随着推流用户数量的增加,订阅流的用户带宽消耗和设备性能消耗都会增加。如果不进行优化,低端设备上的体验将大打折扣。
本教程主要介绍多人视频通话场景的最佳实践。通过以下两种方案,您可以降低带宽消耗并确保最佳的通话体验。
开启小流
开启"receiveWhenViewVisible"

开启小流

小流指在开启摄像头时同时编码两路视频流。一路是正常分辨率的视频(称为大流),另一路是低分辨率的视频(称为小流)。接收端可以选择接收大流或小流以节省带宽。
适用场景:小流适用于多人视频通话场景。通常在多人视频通话的页面布局中,会有"一个大画面和多个小画面"。大画面中的视频是大流,小画面中的视频是小流。此功能会略微增加上行带宽,但可以大幅节省下行带宽。
大小流
大小流

主流、辅流、小流的关系
主流、辅流、小流的关系


推流端发布小流

调用 trtc.startLocalVideo 时,可以设置 small 参数来开启小流编码。
await trtc.startLocalVideo({ option: { small: '120p' }});
自 v5.3.0+ 起,您可以动态开启/关闭小流。
await trtc.startLocalVideo();

// 开启小流
await trtc.updateLocalVideo({ option: { small: '120p' }});

// 关闭小流
await trtc.updateLocalVideo({ option: { small: false }});
注意:
在不支持开启小流的环境中,small 参数不会生效,也不会报错。

订阅端订阅小流

您可以通过设置 trtc.startRemoteVideo 的 small 参数来订阅小流。否则默认订阅大流。
trtc.on(TRTC.EVENT.REMOTE_VIDEO_AVAILABLE, ({ userId, streamType }) => {
trtc.startRemoteVideo({
view: '', // 传入视频容器的 elementId 或元素实例对象。
userId,
streamType,
option: { small: true }
});
})
注意:
订阅小流时无需提前检查远端用户是否已发布小流。如果远端没有发布小流,SDK 会自动订阅大流。
您还可以通过设置 trtc.updateRemoteVideo 的 small 参数来选择订阅大流或小流。常用于实现以下功能:
用户点击"小画面"时,在"大画面"中播放远端视频。
用户点击"大画面"时,在"小画面"中播放远端视频。
// 用户点击"小画面"时,切换到"大画面"播放。
await trtc.updateRemoteVideo({
view: '', // 传入"大画面"视频容器的 elementId 或元素实例对象。
userId: '', // 传入远端用户的 userId。
streamType: TRTC.TYPE.STREAM_TYPE_MAIN,
option: { small: false }
})
// 用户点击"大画面"时,切换到"小画面"播放。
await trtc.updateRemoteVideo({
view: '', // 传入"小画面"视频容器的 elementId 或元素实例对象。
userId: '', // 传入远端用户的 userId。
streamType: TRTC.TYPE.STREAM_TYPE_MAIN,
option: { small: true }})
注意:
TRTC 的视频流有主路和辅路。主路包含大流和小流,辅路通常用于屏幕共享。
订阅视频流时,主路默认订阅大流,可通过 small 参数订阅小流。
TRTC 支持订阅大流 + 辅路、小流 + 辅路,不支持同时订阅大流 + 小流。
大流和小流在同一个视频 view 中播放。只有当推流端开启小流时,远端主路才会在指定 small 参数时切换到小流。
如果没有小流,SDK 会自动订阅大流。目前没有大流小流切换成功的事件通知。

开启"receiveWhenViewVisible"

通常情况下,在多人视频场景中,由于网页尺寸的限制,订阅端不会在页面上显示所有视频。因此,对于那些不需要在页面上显示的远端视频,不需要进行订阅。只有当 view 可见时,才应订阅视频流。这样可以节省流量并减少性能开销。
然而,实现此功能对您来说并不容易。自 v5.4.0 起,SDK 内置支持此功能。您只需在调用 trtc.startRemoteVideo 时设置 receiveWhenViewVisible 和 viewRoot 参数即可开启此功能,非常方便快捷。
SDK 会自动检查 trtc.startRemoteVideo 中传入的 view 的可见性状态。当 view 可见时开始拉流,view 不可见时停止拉流。

示例代码

假设您的视频播放 DOM 结构如下:
<div id='view-list'>
<div id='view1_main'></div>
<div id='view2_main'></div>
<div id='view3_main'></div>
</div>
trtc.on(TRTC.EVENT.REMOTE_VIDEO_AVAILABLE, ({ userId, streamType }) => {
trtc.startRemoteVideo({
view: 'view1_main',
userId,
streamType,
option: {
small: true,
// 开启"receiveWhenViewVisible"
receiveWhenViewVisible: true,
// 传入所有 view 列表的父级元素,
// 用于计算 view 与 viewRoot 的引用关系
// 并检查 view 是否可见。默认值为 document.body。
viewRoot: document.getElementById('view-list')
}
});
})

添加加载效果以获得更好的用户体验

开启此功能后,当用户滚动视频列表时:
当 view 从可见变为不可见时,SDK 将停止拉流。
当 view 从不可见变为可见时,SDK 将恢复拉流。
由于恢复拉流到视频播放成功是一个异步过程,此过程中可能会出现黑屏。为了提供更好的用户体验,建议在此异步过程中为视频区域添加加载效果。
您可以参考以下代码检查是否需要显示加载效果。
let isLoading = true;

trtc.on(TRTC.EVENT.VIDEO_PLAY_STATE_CHANGED, ({ userId, streamType, state }) => {
// '' 表示本地视频
if (userId !== '') return;

// 远端视频播放时将 isLoading 设为 false
isLoading = state !== 'PLAYING';

// 您可以通过 userId 和 streamType 判断是哪个远端用户的主/辅路播放状态发生了变化
});

await trtc.stopRemoteVideo({ userId, streamType })
// 停止远端视频后将 isLoading 设为 false。
isLoading = false;

常见问题

SDK 如何检测 view 是否可见?

当 view 的任意像素点出现在 viewRoot 区域内时,view 被视为可见。反之,当 view 的所有像素点都不在 viewRoot 区域内时,view 被视为不可见。

大流小流是否需要考虑浏览器兼容性?

虽然部分浏览器不支持开启大流小流(如 iOS 系统上所有浏览器和 Chrome 63 以下版本),但开启小流没有副作用。如果环境不支持,SDK 将仅使用大流,调用可以正常进行。您无需编写额外的 if-else 判断。
您可以调用 TRTC.isSupported 获取 checkResult.detail.isSmallStreamSupported 来确认当前环境是否支持开启小流。