@discordjs/voice(discordのvoice gateway)を理解する
#TODO DiscordのE2EE(DAVEプロトコル)についてとの関連がわかるようにする
Discord APIの音声周りの概形
Discordは音声接続のために3種の接続を用いている
Gateway APIとのWebSocket接続
これによって例えばユーザーのミュート状態、接続先のVCの情報、Voice Gatewayとの接続のための認証情報などが送受信される。
Voice Gateway APIとのWebSocket接続
これで例えばユーザーが発言中などの情報がやり取りされる。
また、RTP接続のための認証情報もここでやり取りされる。
RTP接続
これが実際の音声のやり取りに使用される。
API Docs
Gateway API のVoiceセクション: https://discord.com/developers/docs/topics/gateway#voice
Voice Connection
これらの接続をどのように使用するのかは以下の図を参照すると少しわかるかもしれない
あとはAPI Docsまたは、@discordjs/voiceとdiscord.jsまたはv12のソースを読む。
@discordjs/voiceについて
@discordjs/voiceは3種の接続のうち、Gateway APIとのWebSocket接続以外の以下の二種を管理する
Voice Gateway APIとのWebSocket接続
RTP接続
しかし、音声接続の確立や、管理のためにはGateway APIとのやり取りも必要なため、後述のAdapterCreatorを用いてGateway APIと間接的に(例えば、discord.jsを経由して)通信する。
初回接続の成功時の図
code:txt
Client Discord Gateway(US) Discord Voice Server(国別)
@discord.js/voice discord.js Gateway Voice Gateway RTP Server
| | | | |
---------------------------------- joinVoiceChannel ----------------------------------
----------------------- (VoiceConnectionStatus.Signalling) ---------------------------
|----------------->| sendPayload(VoiceStateUpdate)
|---------->| VoiceStateUpdate
|<----------| VoiceServerUpdate
|<----------------| onVoiceServerUpdate
-------- (VoiceConnectionStatus.Connecting,NetworkingStatusCode.OpeningWs) ------------
|---------------------------------------------------->| VoiceOpcodes.Identify
-------- (VoiceConnectionStatus.Connecting,NetworkingStatusCode.Identifying) ----------
|<----------------------------------------------------| VoiceOpcodes.Ready
------- (VoiceConnectionStatus.Connecting,NetworkingStatusCode.UdpHandshaking) --------
|--------------------------------------------------------------------->| IP Discovery
|<---------------------------------------------------------------------| IP Discovery
----- (VoiceConnectionStatus.Connecting,NetworkingStatusCode.SelectingProtocol) ------
|---------------------------------------------------->| VoiceOpcodes.SelectProtocol
|<----------------------------------------------------| VoiceOpcodes.SessionDescription
-------------- (VoiceConnectionStatus.Ready,NetworkingStatusCode.Ready) ---------------
この後は自由に音声を送受信できる。
VoiceConnection
以下の状態を持つ
Signalling
Voice Gatewayに接続するまでの状態
Disconnected
音声接続が切断された状態(再利用可能)
Connecting
Voice Gatewayへの接続の開始後、音声の送受信が可能になるまでの状態
Ready
音声の送受信が可能な状態
Destroyed
VoiceConnectionが破棄された状態(再利用不可)
https://gyazo.com/b9fa6374a1524affd431594632eca056
https://canary.discord.com/channels/391390986770710528/391394853268750337/876531004423172106
AudioPlayer
音声の再生に関する機能を持つ
以下の状態を持つ
Idle
Buffering
Playing
Paused
AutoPaused
AudioResource
音源を表す
内部的にはGraphというデータを変換する概念を連結したもの
例: ffmpeg -> @discordjs/opus
AdapterCreator
@discordjs/voiceが音声接続に必要な情報をGateway APIと接続するライブラリ(例えばdiscord.js。以下discord.jsとして説明する)とやり取りするためのメソッド(これがadapter)をやり取りするメソッド
型定義は以下のとおりである。
DiscordGatewayAdapterCreator: (methods: DiscordGatewayAdapterLibraryMethods) => DiscordGatewayAdapterImplementerMethods
DiscordGatewayAdapterLibraryMethods
@discordjs/voiceがあるギルドで特定のGateway Eventsが発生したときに呼び出してほしい複数の関数や、Gateway接続が終了された旨をdiscord.jsから@discordjs/voiceへ伝えるための関数がオブジェクトにまとめられて渡されて呼び出される。
@discordjs/voiceがAdapterCreatorをDiscordGatewayAdapterLibraryMethodsを引数として呼び出し、discord.jsはこれを保存しておきイベント発生時に呼び出す
DiscordGatewayAdapterImplementerMethods
これにはGateway Eventsを送信する関数やVoiceConnectionが破棄された旨を伝える関数が含まれている
discord.jsはAdapterCreatorの呼び出しに対してDiscordGatewayAdapterImplementerMethodsを返し、@discordjs/voiceはこれを保存しておき呼び出す
https://discordjs.github.io/voice/modules.html#discordgatewayadaptercreator
disconnect と destroyの違い
destroy
無条件で可能
VoiceChannelから切断する
実行したVoiceConnectionは再利用できない
disconnect
状態がSignallingおよびDestroyedの場合は実行してもfalse(失敗)が返ってくる。
VoiceChannelから切断する
実行したVoiceConnectionを再利用することもできる