优化多人视频通话
在多人视频通话场景中,随着推流用户数量的增加,订阅流的用户带宽消耗和设备性能消耗都会增加。如果不进行优化,低端设备上的体验将大打折扣。
本教程主要介绍多人视频通话场景的最佳实践。通过以下两种方案,您可以降低带宽消耗并确保最佳的通话体验。
开启小流
开启"receiveWhenViewVisible"
开启小流
小流指在开启摄像头时同时编码两路视频流。一路是正常分辨率的视频(称为大流),另一路是低分辨率的视频(称为小流)。接收端可以选择接收大流或小流以节省带宽。
适用场景:小流适用于多人视频通话场景。通常在多人视频通话的页面布局中,会有"一个大画面和多个小画面"。大画面中的视频是大流,小画面中的视频是小流。此功能会略微增加上行带宽,但可以大幅节省下行带宽。


推流端发布小流
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.on(TRTC.EVENT.REMOTE_VIDEO_AVAILABLE, ({ userId, streamType }) => {trtc.startRemoteVideo({view: '', // 传入视频容器的 elementId 或元素实例对象。userId,streamType,option: { small: true }});})
注意:
订阅小流时无需提前检查远端用户是否已发布小流。如果远端没有发布小流,SDK 会自动订阅大流。
用户点击"小画面"时,在"大画面"中播放远端视频。
用户点击"大画面"时,在"小画面"中播放远端视频。
// 用户点击"小画面"时,切换到"大画面"播放。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 参数即可开启此功能,非常方便快捷。
示例代码
假设您的视频播放 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 设为 falseisLoading = 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 判断。
