Table of Contents

Interface IWebRtcAudioClient

Namespace
Kook.Audio.WebRtc
Assembly
Kook.Net.Experimental.dll

表示一个基于 WebRTC 的实验性音频客户端。

public interface IWebRtcAudioClient : IAudioClient, IDisposable
Inherited Members

Remarks

此客户端通过 KOOK 非官方文档化的 WebRTC 语音通道(protoo 信令加 mediasoup)收发音频, 与 ConnectAsync(bool, bool, string?) 所使用的基于 UDP 的官方语音接口是两套不同的机制。该机制随时可能变化。

客户端连接成功后默认处于静音状态,需要调用 SetMicrophoneMutedAsync(bool) 取消静音后,通过 CreatePcmStream(AudioApplication, int?, int, int) 等方法所创建的音频流写入的音频才会被发送。

同时存在多个语音连接时的行为。

warning

下列行为均来自对同一个 Bot 账号的实测,每种组合只测过少数几次,不是 KOOK 的文档化保证,之后可能变化。

  • 同一个账号同一时间只能有一个 WebRTC 会话。用 WebRTC 连接第二个频道(即使在另一个服务器)时, 先连接的客户端会被服务端断开,它会先引发 ServerDisconnected(错误码 41106), 随后引发 Disconnected。
  • 官方的基于 UDP 的语音方案(ConnectAsync(bool, bool, string?)) 可以让同一个账号同时连接多个频道,不受上一条限制。需要让一个 Bot 同时在多个频道里推流时,请使用 UDP 方案。
  • UDP 与 WebRTC 在不同的频道里可以同时存在,互不影响。在同一个频道里则会冲突,UDP 优先: UDP 已经连接时,WebRTC 的加入会被服务端拒绝;WebRTC 已经连接时,随后加入的 UDP 会让它被断开 (ServerDisconnected 的错误码为 41106,错误原因为空)。

权限。 Bot 需要被授权才能连接语音频道。实测中,一个没有任何服务器角色的 Bot 获取语音网关令牌时会被服务端拒绝(HTTP 403), 授予角色后可以正常加入。Bot 无法访问频道所属的服务器时(例如不在该服务器,或者该服务器已不可用), 服务端返回 HTTP 400,错误信息为 guild_id does not exist or you do not have permission to access., 不区分「不存在」和「没有权限」。

发送音频。

  • 写入音频流的 PCM 应为 48 kHz、16 位、双声道交错。
  • 编码码率默认取服务端在加入时下发的上限,实测在不同频道里见过 32 kbps 和 64 kbps。每个 Opus 包的时长为 60 毫秒。
  • CreatePcmStream(AudioApplication, int?, int, int) 的 packetLoss 参数默认为 30。此客户端在 packetLoss 大于 0 时会启用 Opus 带内 FEC, 并为丢包补偿预留码率。在 32 kbps 下对同一段音乐做的离线对比(Concentus 编码后再解码)中, 预期丢包 10% 以上并启用 FEC 会使信噪比下降约 2.4 dB,高频能量只剩约一半。网络状况良好时,建议传入 packetLoss: 0,此时不会启用 FEC。
  • 麦克风处于静音状态时,写入音频流的音频会被丢弃,不会缓存到取消静音之后。

接收音频。

  • 只有开着麦克风的成员才有音频流。成员静音入会、还没有开过麦时没有音频流,他第一次开麦时才会创建。
  • 成员开关麦克风时,服务端下发的是静音状态变化的通知(PeerMicrophoneMuteChanged),实测中他的音频流并没有被销毁。 成员离开频道后再加入,会得到新的音频流和新的 SSRC,同一个用户在两次会话里的 SSRC 不同。 因此应当以用户 ID(TryGetStreamUserId(uint, out ulong))而不是 SSRC 来识别说话的人。
  • AudioInStream 提供的是解码后的 48 kHz、16 位、双声道 PCM; 对方使用单声道的设备(实测的 Android 客户端)时,同样会被解码为双声道。每一帧的时长取决于对方的设备。
  • 没有被读取的音频流最多缓存 100 帧(约 1 到 2 秒),缓冲区满之后新到的帧会被直接丢弃,所以不会无限占用内存; 需要这些音频的话,请及时读取。

不支持自动重连。 服务端断开(ServerDisconnected、ClosedByOtherDevice、ReloadRequested) 或信令连接关闭后,客户端会引发 Disconnected,需要调用方自行重新连接。 通过 ConnectWebRtcAsync 创建的客户端不会赋给 AudioClient, 需要调用方自己持有,并在不再使用时释放。

Properties

ChannelId

获取此客户端所连接的语音频道的 ID。

ulong ChannelId { get; }

Property Value

ulong

IsMicrophoneMuted

获取一个值,指示此客户端的麦克风是否处于静音状态。

bool IsMicrophoneMuted { get; }

Property Value

bool

Methods

SetMicrophoneMutedAsync(bool)

设置此客户端的麦克风是否静音。

Task SetMicrophoneMutedAsync(bool muted)

Parameters

muted bool

是否静音。

Returns

Task

一个表示异步设置操作的任务。

TryGetStreamUserId(uint, out ulong)

尝试获取指定 SSRC 的音频输入流所属的用户 ID。

bool TryGetStreamUserId(uint ssrc, out ulong userId)

Parameters

ssrc uint

RTP 流的 SSRC 同步信源标识符。

userId ulong

如果找到,则为音频输入流所属的用户 ID;否则为 0。

Returns

bool

是否找到了该 SSRC 所属的用户。

Remarks

与基于 UDP 的官方语音接口不同,WebRTC 语音通道会在下发音频流时同时下发其所属的用户, 因此在 StreamCreated 事件被引发时,即可通过此方法获取对应的用户。

Events

AccompanimentStarted

当有成员开始播放伴奏时引发。

event Func<ulong, VoiceAccompaniment, Task> AccompanimentStarted

Event Type

Func<ulong, VoiceAccompaniment, Task>

Remarks

事件参数:

  1. ulong 参数是播放伴奏的成员的用户 ID。
  2. VoiceAccompaniment 参数是伴奏的信息,其属性在服务端没有给出时为 null。

AccompanimentStopped

当有成员停止播放伴奏时引发。

event Func<ulong, Task> AccompanimentStopped

Event Type

Func<ulong, Task>

Remarks

此通知在实测中尚未出现过,其载荷格式是假定的,尚未验证。
事件参数:

  1. ulong 参数是停止播放伴奏的成员的用户 ID。

AtmosphereReceived

当有成员使用氛围道具时引发。

event Func<ulong, ulong?, Task> AtmosphereReceived

Event Type

Func<ulong, ulong?, Task>

Remarks

事件参数:

  1. ulong 参数是使用氛围道具的成员的用户 ID。
  2. Nullable<T> 的 ulong 参数是氛围道具的 ID,服务端没有给出时为 null。

ClosedByOtherDevice

当服务端通知此客户端已在另一台设备上被关闭时引发,随后会引发 Disconnected 事件。

event Func<Task> ClosedByOtherDevice

Event Type

Func<Task>

Remarks

此通知在实测中尚未出现过,它的触发条件未知。名称提示它对应「同一账号在别处加入」,但实测中同一个账号用 WebRTC 连接另一个频道、或者用官方 UDP 方案加入同一个频道时,服务端走的都是 disconnect 通知, 会引发 ServerDisconnected 而不是此事件。

PeerHeadsetMuteChanged

当有成员的耳机静音状态变化时引发。

event Func<ulong, bool, Task> PeerHeadsetMuteChanged

Event Type

Func<ulong, bool, Task>

Remarks

与 PeerMicrophoneMuteChanged 一样,此事件有最长约 2 秒的延迟,并且可能晚于该成员离开频道的通知。
事件参数:

  1. ulong 参数是该成员的用户 ID。
  2. bool 参数指示该成员的耳机是否已静音。

PeerJoined

当有成员加入此语音频道时引发。

event Func<VoicePeer, Task> PeerJoined

Event Type

Func<VoicePeer, Task>

Remarks

客户端连接时已经在频道内的成员不会引发此事件。
事件参数:

  1. VoicePeer 参数是加入频道的成员的信息快照。

PeerLeft

当有成员离开此语音频道时引发。

event Func<ulong, Task> PeerLeft

Event Type

Func<ulong, Task>

Remarks

important

成员主动离开与被管理员移出语音频道,服务端下发的通知完全相同,无法区分。


如果该成员有音频输入流,StreamDestroyed 事件会先于此事件引发。
事件参数:
  1. ulong 参数是离开频道的成员的用户 ID。

PeerMicrophoneMuteChanged

当有成员的麦克风静音状态变化时引发。

event Func<ulong, bool, Task> PeerMicrophoneMuteChanged

Event Type

Func<ulong, bool, Task>

Remarks

warning

服务端会把这类状态通知攒成大约每 2 秒一批下发,所以事件会有最长约 2 秒的延迟; 同时,某个成员的状态通知可能晚于他离开频道的通知到达,此时事件仍然会引发。 这两点是根据实测推断的,服务端并未在文档中说明。


事件参数:
  1. ulong 参数是该成员的用户 ID。
  2. bool 参数指示该成员的麦克风是否已静音。

PeerPermissionChanged

当有成员的语音权限或服务端静音状态变化时引发,成员可以是此客户端自己。

event Func<ulong, VoicePeerPermissions, Task> PeerPermissionChanged

Event Type

Func<ulong, VoicePeerPermissions, Task>

Remarks

管理员禁麦、禁听,或者变更角色权限时都会引发此事件。实测中,收回权限并不会导致客户端被断开。
事件参数:

  1. ulong 参数是该成员的用户 ID。
  2. VoicePeerPermissions 参数是该成员变化后的权限。

ReloadRequested

当服务端要求此客户端重新加入语音频道时引发,随后会引发 Disconnected 事件。

event Func<Task> ReloadRequested

Event Type

Func<Task>

Remarks

此客户端不支持自动重新加入,请在 Disconnected 事件之后重新连接。 此通知在实测中尚未出现过。

ServerDisconnected

当服务端通知此客户端被断开时引发,随后会引发 Disconnected 事件。

event Func<int?, string?, Task> ServerDisconnected

Event Type

Func<int?, string, Task>

Remarks

important

实测中,下列三种情形服务端给出的 errorCode 都是 41106,原因只能靠 errorReason 区分, 而且其中一种的 errorReason 是空字符串:被管理员移出语音频道;同一个账号用 WebRTC 又连接了另一个频道, 先连上的 WebRTC 会话被断开;同一个账号用官方 UDP 方案加入了同一个频道,WebRTC 会话被断开(errorReason 为空)。 errorReason 是中文原文,不保证长期稳定,不要依赖它做精确匹配。 errorCode 还可能对应没有测试过的其他原因。


事件参数:
  1. Nullable<T> 的 int 参数是服务端给出的错误码,没有给出时为 null。
  2. string 参数是服务端给出的错误原因,没有给出时为 null。