Tutorial: 多人视频通话

多人视频通话

功能描述

在多人视频通话场景中,随着推流用户的增加,对于拉流的用户来说,带宽流量消耗及设备性能消耗都会随之上升,如果不做优化,在中低端机型的体验会大打折扣。

本文主要介绍多人视频场景的最佳实践,通过使用 大小流 + 只拉可视区域的视频流 功能,以降低带宽消耗及保障最佳的通话体验。

开启大小流

大小流是指在开启摄像头时,同时编码两路视频流,一路是正常分辨率的视频(称为大流),另一路是低分辨率的视频(称为小流)。在拉流端可以选择拉大流或者拉小流,以达到节省流量的目的。

适用场景:大小流适用于多人视频通话场景,一般在多人通话场景的页面中,页面布局会有一个大画面 + n个小画面,位于大画面的视频拉大流,位于小画面内的视频拉小流。该功能会少量增加上行带宽,但可以大大节省下行带宽。

实现步骤

1. 推流端开启大小流

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 }});

2. 拉流端选择拉大流或小流

startRemoteVideo 时,填写 small 参数,可以拉小流,否则默认订阅大流。

拉小流时,不需要提前判断远端用户是否有推小流,若远端没有推小流,则 SDK 会自动拉大流。后续若该远端用户推小流了,SDK 会自动切换到拉小流。

trtc.on(TRTC.EVENT.REMOTE_VIDEO_AVAILABLE, ({ userId, streamType }) => {
  trtc.startRemoteVideo({ 
    view: '', // 传入视频容器的 elementId 或者 element 实例对象。
    userId, 
    streamType, 
    option: { small: true }
  });
})

updateRemoteVideo 时,填写 small 参数,可以切换指定 userId 的大小流。常用于实现这样的功能:用户点击“小画面”时,切换到“大画面”播放;用户点击“大画面”时,切换到“小画面”播放。

// 用户点击“小画面”时,切换到“大画面”播放
await trtc.updateRemoteVideo({ 
  view: '', // 传入“大画面”视频容器的 elementId 或者 element 实例对象。
  userId: '', // 传入远端用户的 userId 
  streamType: TRTC.TYPE.STREAM_TYPE_MAIN, 
  option: { small: false }
})
// 用户点击“大画面”时,切换到“小画面”播放。
await trtc.updateRemoteVideo({ 
  view: '', // 传入“小画面”视频容器的 elementId 或者 element 实例对象。
  userId: '', // 传入远端用户的 userId 
  streamType: TRTC.TYPE.STREAM_TYPE_MAIN, 
  option: { small: true }})

注意事项

  1. TRTC 的视频流有主流(主路视频流)和辅流(辅路视频流)之分。主流包括了大流和小流,辅流一般用于屏幕分享。
  2. 拉流时,主流默认拉大流,通过 small 参数可以指定拉小流。支持同时拉大流 + 辅流,小流 + 辅流,不支持同时拉大流 + 小流。大流和小流在同一个视频容器内播放。
  3. 只有推流端开启了小流,拉流端将远端流切换成小流才会生效。若没有小流,则 SDK 会自动拉大流。
  4. 切换大小流成功目前没有事件通知。

开启【只拉可视区域的视频流】功能

通常来说,多人视频场景下,由于页面空间有限,拉流端不会将所有的视频都展示在页面上。因此,对于那些不需要在页面上展示的远端视频,就可以不用拉流。只有 view 可视时,才拉视频流。以达到节省流量和降低性能开销的目的。

而实现这个功能,并不是一件容易的事情。在 v5.4.0 版本开始,SDK 内置了该功能,您只需要在 startRemoteVideo 时,设置 receiveWhenViewVisibleviewRoot 参数,即可开启该功能,非常方便快捷。SDK 会自动判断您在 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: true // 开启【只订阅可视区域的视频流】功能
      viewRoot:  document.getElementById('view-list') // 传入所有 view list 的第一级父级元素,用于计算 view 和 viewRoot 的参考关系,判断 view 是否处于可视状态。默认值是 document.body
    }
  });
})

增加 Loading 效果

开启该功能后,在用户滚动视频列表时:

  • view 从可视变为不可视时,SDK 会停止拉流。
  • view 从不可视变为可视时,SDK 会恢复拉流。

由于恢复拉流到视频播放成功,是异步的过程,在这个过程中会出现黑屏,一般为了更好的交互体验,建议您在这个异步过程中,给视频区域增加 Loading 的效果。您可以参考如下代码判断是否要展示 Loading。

let isLoading = true;
// 监听视频播放状态,根据视频播放状态来判断是否显示 Loading 效果。
trtc.on(TRTC.EVENT.VIDEO_PLAY_STATE_CHANGED, ({ userId, streamType, state }) => {
  // '' 代表本地的视频播放状态,此处需要判断远端视频流的播放状态。
  if (userId !== '') return;
  // 远端视频播放成功,则停止 Loading 效果
  isLoading = state !== 'PLAYING';
  // 通过 userId 和 streamType 判断是哪个远端用户的主流/辅流 播放状态变更。
});
await trtc.startRemoteVideo({ ... });
// 停止远端视频后,停止远端 Loading 效果。
trtc.stopRemoteVideo()
isLoading = false;

常见问题

  1. SDK 如何判断 view 是否处于可视状态。

    当 view 有任意像素点出现在 viewRoot 区域时,即认为 view 是可视的;同时当 view 所有像素点都不在 viewRoot 区域时,则认为 view 是不可视的。

  2. 大小流需要考虑浏览器兼容性吗?

    虽然在部分浏览器下不支持开启大小流(例如 iOS 系统的所有浏览器、Chrome 63 以下版本),但是开启大小流是没有副作用的,如果环境不支持,SDK 只会使用大流,通话可以正常进行。您无需写额外的 if else 判断。

    您可以调用 TRTC.isSupported 获取 checkResult.detail.isSmallStreamSupported 来判断当前环境是否支持开启大小流。