【Agora 征文】浅浅浅谈 Agora SDK API(新手导向)

Agora 的产品均围绕着 RTC(Real-time Communication) 展开,其中最主要的产品便是 Agora RTC SDK。它为 ToB 厂商提供一个音视频采集、编解码和传输的“工具包”,助力厂商开发自己的 RTC 相关业务的应用。这套工具包包括种类繁多的 API。本文旨在粗浅的介绍 API,使新手不至于迷失在诸多 API 的海洋中。

local and remote users

RTC 过程中必然包含着多位参与方,比如你和其他人语音聊天。因此 RTC API 名称中经常出现 local 和 remote 的表述:

  • setupLocalVideo
  • onLocalAudioStateChanged
  • setupLRemoteVideo
  • onLocalRemoteStateChanged

local 和 remote 是一组相对的概念(就好比“远和近”也是相对的):对于你而言,local 指自己,remote 指别人;对于别人而言,local 指他自己,remote 可能就指你了。每位参与者都是自己视角上的 local user。

mirror mode

分清楚了 local 和 remote 后,我们再来看看本地和远端镜像。

在用户 A 的设备上,会同时出现 A 和 remote user 的视图。在 A 的角度,A 的视图就是 local view,remote user 的视图就是 remote view。所以不要误以为 remote view 是 remote user 的设备上的视图了。

setupLocalVideo 和 setupRemoteVideo 均是作用在用户 A 的设备上。setupLocalVideo(mirrormode) 即是 A 在 A 的设备看 A 的镜像,setupRemoteVideo(mirrormode) 即是 A 在 A 的设备上看 B 的镜像。

需要注意的是,对 view 的镜像效果只局限于 A 所见,B 将不会看到 A 的镜像。如果需要实现 B 看 A 镜像,那么可以在编码 A 的视频时设置镜像,setVideoEncoderConfiguration(mirrormode);或者在 B 的设备上通过 setupRemoteVideo(mirrormode,uidofA)。

channel

集成在业务应用中的 RTC SDK 会与 Agora SD-RTN™ 取得连接。应用使用者靠着 Agora SD-RTN™相互联系。为了切合真实的业务场景,我们用 channel 的概念将应用使用者进行分类。channel 就好比使用 AVC(Agora Video Call)的“房间号”。channel 的本质是你的应用中的 SDK 与 Agora SD-RTN™取得的连接。由此可以合理如下两种设计:

  1. 不同 channel 的参与者不能相互沟通,相同 channel 的参与者可以相互沟通。
  2. 网络不好时,SDK 与 Agora SD-RTN™可能连接失败,参与者可能被提示“加入频道失败”。

如果想突破 1 的限制,比如 channel A 中的主播想让 channel B 中的参与者看到自己且不想让 A 的直播受到影响,那么 Agora 会再提供一个“中转”服务器,将 channel A 对应的服务器 A 和 channel B 对应的服务器 B 连接起来,把 A 中主播的音视频流从服务器 A “接力转发”给 服务器 B。因此我们提出了一组叫 media relay 的 API。

channel profile

自代号为 NASA 的技术在 RTC SDK 落地后,channel profile 变在底层融合为一个 profile。但是在下一代 RTC SDK(大重构) 中重新提出 channel profile 的概念,所有这里可以简单解释一下。

为了贴合真实应用的业务场景,如直播、游戏、聊天,我们提供取名类似的 channel profile。我们将市面上多样的互动场景抽象成一些特征,为每种特征而在底层设计上使用不同的技术策略,最后向开发者提供几个 channel profile。

audio and video

audio/video module

audio/video module 并不是一个实物,而是一个抽象的 module,可以简单理解为音频/视频处理的过程。

API 名中经常出现对 audio/video module 操作的一些动词,如 enable/disable、mute/unmute:

  • enable/disable:user 只能对自己的 a/v module 进行操作。
  • mute/unmute:user 对所有人均能进行的操作。简单理解为在 AVC 中主持人将你的音/视频 mute。

RTC SDK 默认 enable audio module,开发者无需额外调用。但是开发者需要自行 enable video module。

引入一系列容易搞混的音频组 API:

  • enableAudio
  • disableAudio
  • enableLocalAudio(true)
  • enableLocalAudio(false)
  • muteLocalAudioStream
  • muteRemoteAudioStream
  • muteAllRemoteAudioStream

再引入一系列容易搞混的视频组 API:

  • enableVideo
  • disableVideo
  • enableLocalVideo(true)
  • enableLocalVideo(false)
  • muteLocalVideoStream
  • muteRemoteVideoStream
  • muteAllRemoteVideoStream

RtcEngine 默认 enable audio,但是不默认 enable video,因此视频组和音频组的使用差异在于开发者需要调用 enableVideo,而无需调用 enableAudio。毕竟,没有音频的互动也不能称之为 RTC 互动了,为开发者默认打开音频模块是合理的行为。

接下来以视频组举例,简单区分长得很像的这些 API:

  1. 前四位的调用都会重置整个 RtcEngine 的状态,不管是否在频道内调用都会生效。但是前两位响应时间慢。因此,只需要暂时关闭 local user 的 video module 时,推荐使用 enableLocalVideo(false),避免使用 disableVideo。

  2. enableLocalVideo 的存在是为 enableLocalVideo(false) 而生。可以不去纠结 enableLocalVideo(true) 和 enableVideo 之间的关系。先来纠结更重要的 enableLocalVideo(false) 和 disableVideo 之间的关系:

    • disableVideo 的用户无法使用视频模块,即无法让别人看到自己,也无法让自己看到别人。
    • enableLocalVideo(false) 的用户禁止了本地摄像头采集视频,即无法让别人看到自己,但是自己可以看到别人。这种情况,用户也不需要拥有摄像头了。

    因此,如果某个用户突然处在比较尴尬的场景,不想让视频互动中的其他人看到自己,就可以用 enableLocalVideo(false),而不是挡住摄像头来避免尴尬。如果场景恢复正常,用 enableLocalVideo(true) 恢复自己的画面即可。

  3. disableVideo,muteLocalVideoStream,muteRemoteVideoStream,muteAllRemoteVideoStream 的对比:disable 自己,mute 自己,mute 远端,mute 远端所有人。它们之间的区别就和 disable 和 mute 之前的区别一样,就不多解释。

  4. enableLocalVideo(false) 和 muteLocalVideoStream 的对比:如果你处在尴尬的场景下,不想被远端看到自己,那么这两个方法均可以解救你。但是:

    • 前一个方法的响应速度比后一个慢,毕竟前者重置整个引擎的状态。
    • 后一个方法依然会开启摄像头采集,虽然不会将采集的视频发布到远端;前一个方法禁用了摄像头采集,听上去更安全,不会有一个小绿灯亮着提示 app 正在盯着你。
    • 后一个方法会受到频道场景和用户角色影响,前一个方法不会。举例来说,如果开发者为你 muteLocalVideoStream,然后把你设为直播场景下的主播,那么你的视频会立马发送给远端所有人。(那还不如手动遮挡摄像头,不用 API 了)

audio/video data

RTC SDK 支持将 SDK 采集后编码前,解码后渲染前的音视频数据传给开发者。这类数据常被称为裸数据,如 raw audio data,raw video data。RTC SDK 也支持开发者不用 SDK 采集用户的音视频数据,而是自定义音视频渲染器,即 custom audio source, custom video source。

raw audio data

在 RTC 过程中,SDK 会流转众多音频数据,如多位参与者的人声,如参与者所在空间的环境声音。因此需要提供多种 API 向开发者传递不同种类的音频数据:

  • onRecordAudioFrame:Record 指录音。因此该接口表示 SDK 采集到的 local user 的音频数据。

    在 RTC 过程,很明显,app 对 local user 录音,app 播放 remote user 的音频。

  • onPlaybackAudioFrame:Playback 指播放,因此该接口表示所有 remote user 的音频数据。
  • onPlaybackAudioFrameBeforeMixing(uid):该接口带 uid 的参数,因为可以指代具体的 user。结合上一个接口,很明显可以理解为具体的某个 remote user 的音频数据。

    这里的 Mixing 可以理解为 Playback 的音频数据的混音,即 remote A 和 remote B 等的混音。

  • onMixedAudioFrame:这里只出现 Mixed,而没有 Record 和 Playback 出现。即是指 Record 和 Playback 的混音。 把 record audio frame 和 playback audio frame 进行了混音,即把 local user 的人声和空间的环境声,以及 remote user 的人声和空间的环境声进行了全混音。

raw video data

在 RTC 过程,SDK 会在摄像头采集到用户视频数据后,进行前处理-编码-发送-传输-接收-解码-后处理-渲染播放。开发者可以通过 API 得到编码前和解码后的 raw video data,以完成自定义的前处理和后处理。

发送侧可获取的 raw video data 有如下两种阶段:

  • onCaptureVideoFrame:采集后。
  • onPreEncodeVideoFrame:编码前。

前处理完成后,将数据传给 SDK,让 SDK 完成编码发送等后续工作即可。

接收侧可获取的 raw video data 有如下阶段:

  • onRenderVideoFrame:解码后。

后处理完成后,开发者可能直接自渲染。为了优化自渲染的体验,比如让视频帧出帧间隔更均匀,我们提供 getSmoothRenderingEnabled 接口。我们不能控制开发者自渲染时出帧行为,但是可以控制解码后的出帧行为,比如通过把onRenderVideoFrame 前的视频帧在时间轴上重新排排位置,不让部分帧间隔太小,也不让部分帧间隔太大。整理帧间隔的内部行为即是通过 getSmoothRenderingEnabled 开启。(一个不靠谱)结论就是,实际操作中自渲染会遇到很多问题,丢给 SDK 渲染最简便。

local/remote audio/video

最后,local/remote 和 audio/video module 的概念进行组合,进入报菜名环节,轻松结束这次对 API 名的脑洞。如果大家希望更深入了解 RTC SDK 的 API,可以看代码实现,或者让架构师对外公开包含更多技术内容的 API 文档。

RTC 过程中,使用者最常抱怨的问题大概有:为什么声音卡了,视频卡了,视频糊了,视频花了,视频变成黑白了,出图慢了。可以看出来,监测所有终端用户的音视频状态并让通话质量透明是非常重要的。Agora 不仅提供水晶球还提供众多回调:

  • 监测 local user 组:

    • onLocalAudioStateChanged:音频状态已改变
    • onLocalVideoStateChanged:视频状态已改变
    • onFirstLocalAudioFramePublished:音频首帧已发布
    • onFirstLocalVideoFramePublished:视频首帧已发布
    • onFirstLocalVideoFrame:视频首帧已渲染
    • onAudioPublishStateChanged:音频发布状态已改变
    • onVideoPublishStateChanged:视频发布状态已改变
    • onLocalAudioStats:音频统计信息
    • onLocalVideoStats:视频统计信息
    • onNetworkQuality:上下行 last mile 网络质量
  • 监测 remote user 组:

    • onRemoteAudioStateChanged:音频状态已改变
    • onRemoteVideoStateChanged:视频状态已改变
    • onFirstRemoteVideoFrame:视频首帧已渲染
    • onAudioSubscribeStateChanged:音频订阅状态已改变
    • onVideoSubscribeStateChanged:视频订阅状态已改变
    • onRemoteAudioStats:音频统计信息
    • onRemoteVideoStats:视频统计信息
    • onNetworkQuality:上下行 last mile 网络质量