写点什么

【音视频】基于声网的多人视频通话功能建设

用户头像
轻口味
关注
发布于: 2021 年 06 月 13 日
【音视频】基于声网的多人视频通话功能建设

背景

随着越来越多的人们习惯于有事直接发起微信语音通话/视频通话,实时音视频在 IM 场景扮演了越来越重要的角色。IM 的核心服务功能除了有消息传输、会话/消息管理、群组管理、推送服务外,还有包含短语音消息、短视频消息、一对一实时语音通话、一对一实时视频通话、语音呼叫电话能力、多对对实时音视频通话的音视频能力。最近几年一直在建设 IM 的音视频能力,今天以声网视频通话 SDK 为实时音视频通道,分享 IM 场景的多人视频通话功能建设。

需求

功能上包含两大块:

  1. 可发起/邀请多人通话

  2. 多人音视频通话保持

UI 交互上核心能力:

  1. 可九宫格展示用户画面,可左右滑动展示更多;

  2. 可点击大屏展示某个用户画面;

  3. 可随着用户加入/退出通话,动态增删画面;

  4. 用户摄像头未开启,显示用户头像,并不可点击切换到大屏模式;

  5. 被大屏观看的用户退出通话,自动切换到九宫格模式。






实现

实现部分主要分三块:

  1. 发起呼叫信令通道

  2. 实时音视频通道

  3. UI 实现


通话建立信令通道

通道类型

发起视频通话,需要将邀请通话的指令分别发送给被邀请用户。这就要求我们的 APP 本身均在线(系统 push 触达的先不讨论),并且有和服务器保持连接的长连接通道。常用的方式有信令通道有:

  1. 基于 TCP 的自定义协议的 Socket 通道;

  2. 基于 WebSocket 的通道;

  3. 基于 Http2 的通道;

  4. 基于 Http 轮询通道

  5. 基于 UDP 自定义协议的 Socket 通道

因为是基于 IM 的音视频通话,直接复用了 IM 的信令通道能力,以减少应用端口占用。


通话信令

一个完整的通话过程需要哪些信令支撑呢?

基础功能信令
  1. 发起呼叫/邀请其他人信令:将通话信令从主叫通知到被叫;

  2. 接听信令:被叫接受呼叫信令;

  3. 拒接信令:被叫拒绝接听信令;

  4. 终止呼叫信令:主叫取消通话邀请;

  5. 挂断信令:主叫/被叫任意一方中断通话。

异常保证信令

基础功能信令只能保证在正常 case 下跑通,如何保证通信过程健壮性呢?比如:

  1. 如何确保主叫的呼叫信令已经发送到 server?

  2. 发起呼叫指令如果没有到达被叫怎么办?如果使用增加服务端持续呼叫手段怎么避免重复信令?

  3. 如何确保双方都能正常加入多媒体房间?

  4. 呼叫过程对方已经再跟别人通话怎么处理?

  5. 如何保证在通话过程中某一方多媒体通道掉线,对方无效等待?

为了解决上述问题我们需要引入一些辅助信令:

  1. 增加呼叫响应信令,只有主叫接收到呼叫响应信令才算是呼叫请求信令成功发送到 server;

  2. 增加呼叫确认信令,server 相隔固定时间持续发送主叫请求信令给被叫,直到收到被叫呼叫确认信令。被叫需要根据呼叫 ID 过滤掉重复的呼叫请求信令。主叫收到呼叫确认信令后可以在呼叫页面做 UI 更新,比如刷新提示文字“呼叫中”为“等待被叫接听”。

  3. 增加主叫接通信令:为了避免呼叫过程中过早加入多媒体房间产生无效内容,在主叫收到被叫接听信令后再加入多媒体通道,加入多媒体通道成功后,发送主叫接听信令给被叫,被叫收到后再加入多媒体通道。主叫先加入多媒体通道,而不是被叫点击接听后就加入多媒体通道,可以避免接听信令发送失败被叫产生的额外多媒体房间无效内容。

  4. 增加忙线信令:如果被叫正在通话中,收到其他呼叫请求信令,则发送忙线信令。

  5. 增加心跳信令与心跳异常信令:通话建立后,每个 t 秒持续向 server 发送心跳信令,并持续接收 server 返回的心跳信令,如果持续 n 次发送或者接收心跳失败,则判断通话异常,退出通话。

除此之外,还需要一些额外的定时器处理超时任务。

UI 关联信令

在 UI 上的一些交互也需要我们借助信令通道触达对方:

  1. 状态广播信令:退出房间用户通知到其他用户;

  2. 通知信令:开关摄像头通知其他用户。

信令状态实现

上面介绍了要实现一个完整通话需要十多个信令指令,维护主被叫各种状态下的信令变成了一个令人头疼的事情。我们先将通话建立过程抽象为空闲态、接通态、主叫呼叫发送中态、主叫等待 1、主叫等待 2、被叫待接听、被叫连接中七种状态,我们用一张图来描述引起状态变化的信令:



捋清楚状态之间的逻辑,我们可以基于状态+命令设计模式进行实现。状态接口定义各状态行为:

  1. 发送呼叫信令

  2. 接收呼叫请求信令

  3. 收到呼叫响应信令

  4. 发送呼叫确认信令

  5. 接收呼叫确认信令

  6. ...



实时音视频通道

多媒体通道我们基于声网视频通话 SDK 实现:


我们主要关心:

  1. 设置本地视频

  2. 加入频道

  3. 监听远端用户进入

setupRemoteVideo 与 setupLocalVideo 都需要传入 VideoCanvas 类,VideoCanvas 封装了 SurfaceView,我们需要背本地视频与每个远端用户画面创建一个 SurfaceView。RtcEngine 为我们提供了 CreateRendererView 方法用于构建 SurfaceView,也可以使用 CreateTextureView 创建 TextureView。


总结一下,我们用到的 API:

  1. RtcEngine.create

  2. RtcEngine.joinChannel

  3. RtcEngine.leaveChannel

  4. RtcEngine.enableLocalAudio

  5. RtcEngine.setupLocalVideo

  6. RtcEngine.setupRemoteVideo

  7. RtcEngine.createRenderView

  8. RtcEngine.createTextureView

  9. RtcEngine.startPreview

  10. IRtcEngineEventHandler.onJoinChannelSuccess

  11. IRtcEngineEventHandler.onUserJoined

详细可查阅声网视频通话API参考

UI 实现

UI 层主要负责管理多人画面的 SurfaceView、全屏和九宫格切换逻辑、以及开关麦克风/摄像头引起的 UI 刷新。这里使用 Android 系统提供的 RecyclerView 实现九宫格及九宫格画面滑动。堆叠模式时隐藏 RecyclerView,并将要显示对方 SurfaceView 与自己画面 SurfaceView 拿出进行绘制。UI 实现因为一些交互实现,逻辑还是比较复杂,这里不进行展开讨论。

问题

走到这里终于可以完成多人视频通话了,跑起来后效果还不错。QA 测试时却出来一个头疼的问题:容易掉线。而且是在视频画面正常情况下突然掉线。定位日志后发现,是心跳信令发送/接收失败引起的。为什么视频画面还正常的情况下信令连接却先除了问题呢?

分析 QA 复现问题的环境:工位摆了十几个手机同时进入同一个房间,时间不长就与用户连接失败;把十几个手机分散的放到各个地方,情况有了明显好转。初步定位是网络引起的,工位所有手机连到同一个热点,导致网络环境变差。

但是为什么视频画面是可以的呢?因为音视频通道基于 UDP,而我们的信令通道基于 TCP,UDP 经过封装优化,增加了弱网对抗能力,在弱网环境下 UDP 的视频通道会抢占整体带宽,导致 TCP 通道更差,甚至连不上。

定位到问题后就要着手解决了,这里提供几个思路:

  1. UI 不展示的用户画面不拉流,降低下行带宽压力;

  2. 限制最多通话人数;

  3. 降低视频码率;

  4. 替换 TCP 通道为 UDP 通道。

声网云信令 SDK 有基于 UDP 的通道,帮助提升 RTM 弱网对抗。具体参考云信令文档.特别是在 1.4.0 版本优化了弱网对抗能力,提高了弱网环境下的登录成功率和消息投递成功率,优化了重连机制。

总结

本文介绍了基于声网视频通话 SDK 实现的 IM 场景多人视频通话功能。主要介绍了信令通道、多人视频通话用到的信令、声网视频 SDK 接口以及弱网情况下信令通道掉线问题解决。

发布于: 2021 年 06 月 13 日阅读数: 47
用户头像

轻口味

关注

Android音视频、AI相关领域 2017.10.17 加入

Android多媒体开发从业者~

评论

发布
暂无评论
【音视频】基于声网的多人视频通话功能建设