Revolt Chat란?
Revolt Chat는 쉽게 말하면 오픈소스 디스코드입니다. 여기서는 음성 채팅 구현에 초점을 맞춥니다.
Vortex
Vortex가 Revolt의 음성 채팅 서버의 역할을 합니다. 23년 1월 기준 rewrite가 진행중입니다(의존성 문제로 기존 코드 환경 구성도 제대로 되지 않습니다). 새 구현을 기다려야겠지만 당장은 Rust를 사용하며 Mediasoup를 통하여 WebRTC를 사용하고 있습니다. Rust 사용 자체는 프로젝트 성향이라 바뀌지 않겠지만 미들웨어는 바뀔 가능성이 있습니다.
채팅 시작 시퀀스
POST /channels/{ROOM_ID}/join_call
방 접근을 위한 토큰 획득 (ex: Lr4Qhq37zkpPTd98aTUGjtZw7QusaXze)
wss://vortex.revolt.chat
접속
아래 통신 내역에서 SEND는 클라이언트가 보낸 것이고 RECV는 서버의 응답입니다. 다른 유저의 인터랙션 이외에 서버가 먼저 메시지를 보내는 경우는 없는 요청-응답의 구조를 가집니다.
인증
SEND: {"id":10,"type":"Authenticate","data":{"token":"Lr4Qhq37zkpPTd98aTUGjtZw7QusaXze","roomId":"{ROOM_ID}"}}
RECV: {
"id": 10,
"type": "Authenticate",
"data": {
"version":"0.3.0-alpha.1",
"userId":"{MY_USERID}",
"roomId":"{ROOM_ID}",
"rtpCapabilities":{
"codecs":[
{"kind":"audio","mimeType":"audio/opus","preferredPayloadType":100,"clockRate":48000,"channels":2,"parameters":{},"rtcpFeedback":[{"type":"transport-cc","parameter":""}]}
],
"headerExtensions":[
{"kind":"audio","uri":"urn:ietf:params:rtp-hdrext:sdes:mid","preferredId":1,"preferredEncrypt":false,"direction":"sendrecv"},
{"kind":"video","uri":"urn:ietf:params:rtp-hdrext:sdes:mid","preferredId":1,"preferredEncrypt":false,"direction":"sendrecv"},
{"kind":"video","uri":"urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id","preferredId":2,"preferredEncrypt":false,"direction":"recvonly"},
{"kind":"video","uri":"urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id","preferredId":3,"preferredEncrypt":false,"direction":"recvonly"},
{"kind":"audio","uri":"http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time","preferredId":4,"preferredEncrypt":false,"direction":"sendrecv"},
{"kind":"video","uri":"http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time","preferredId":4,"preferredEncrypt":false,"direction":"sendrecv"},
{"kind":"audio","uri":"http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01","preferredId":5,"preferredEncrypt":false,"direction":"recvonly"},
{"kind":"video","uri":"http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01","preferredId":5,"preferredEncrypt":false,"direction":"sendrecv"},
{"kind":"video","uri":"http://tools.ietf.org/html/draft-ietf-avtext-framemarking-07","preferredId":6,"preferredEncrypt":false,"direction":"sendrecv"},
{"kind":"video","uri":"urn:ietf:params:rtp-hdrext:framemarking","preferredId":7,"preferredEncrypt":false,"direction":"sendrecv"},
{"kind":"audio","uri":"urn:ietf:params:rtp-hdrext:ssrc-audio-level","preferredId":10,"preferredEncrypt":false,"direction":"sendrecv"},
{"kind":"video","uri":"urn:3gpp:video-orientation","preferredId":11,"preferredEncrypt":false,"direction":"sendrecv"},
{"kind":"video","uri":"urn:ietf:params:rtp-hdrext:toffset","preferredId":12,"preferredEncrypt":false,"direction":"sendrecv"}
]
}
}
}
방 정보 요청
SEND: {"id":11,"type":"RoomInfo"}
RECV: {
"id":11,
"type":"RoomInfo",
"data":{
"id":"{ROOM_ID}",
"videoAllowed":false,
"users":{
"{MY_USERID}":{"audio":false},
"{FRIEND_USERID}":{"audio":false}
}
}
}
트랜스포트 초기화
SEND: {
"id":12,
"type":"InitializeTransports",
"data":{
"mode":"SplitWebRTC",
"rtpCapabilities":{"codecs":[{"mimeType":"audio/opus","kind":"audio","preferredPayloadType":100,"clockRate":48000,"channels":2,"parameters":{"minptime":10,"useinbandfec":1},"rtcpFeedback":[{"type":"transport-cc","parameter":""}]}],"headerExtensions":[{"kind":"audio","uri":"urn:ietf:params:rtp-hdrext:sdes:mid","preferredId":1,"preferredEncrypt":false,"direction":"sendrecv"},{"kind":"video","uri":"urn:ietf:params:rtp-hdrext:sdes:mid","preferredId":1,"preferredEncrypt":false,"direction":"sendrecv"},{"kind":"audio","uri":"http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time","preferredId":4,"preferredEncrypt":false,"direction":"sendrecv"},{"kind":"video","uri":"http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time","preferredId":4,"preferredEncrypt":false,"direction":"sendrecv"},{"kind":"video","uri":"http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01","preferredId":5,"preferredEncrypt":false,"direction":"sendrecv"},{"kind":"audio","uri":"urn:ietf:params:rtp-hdrext:ssrc-audio-level","preferredId":10,"preferredEncrypt":false,"direction":"sendrecv"},{"kind":"video","uri":"urn:3gpp:video-orientation","preferredId":11,"preferredEncrypt":false,"direction":"sendrecv"},{"kind":"video","uri":"urn:ietf:params:rtp-hdrext:toffset","preferredId":12,"preferredEncrypt":false,"direction":"sendrecv"}]}
}
}
RECV: {
"id":12,
"type":"InitializeTransports",
"data":{
"sendTransport":{
"id":"{SEND_TRANSPORT_ID}",
"iceParameters":{"usernameFragment":"mk6xebob3rqkb6op","password":"z9nm7cpos871wjdewu4v8hald18utg6b","iceLite":true},
"iceCandidates":[{"foundation":"udpcandidate","priority":1076558079,"ip":"51.15.123.10","protocol":"udp","port":10137,"type":"host"},{"foundation":"tcpcandidate","priority":1076302079,"ip":"51.15.123.10","protocol":"tcp","port":10207,"type":"host","tcpType":"passive"}],
"dtlsParameters":{"role":"auto","fingerprints":[{"algorithm":"sha-1","value":"35:99:94:99:F8:F4:A2:02:06:F3:BE:17:AF:76:68:04:9A:81:C9:74"},{"algorithm":"sha-224","value":"51:4C:B1:03:0E:1B:50:53:CB:64:6E:B4:4E:18:40:E0:96:C8:F5:08:42:60:7A:C4:28:BD:6A:72"},{"algorithm":"sha-256","value":"92:DB:6F:16:DA:36:8F:D1:12:8F:0E:13:D2:22:39:82:7D:63:A1:FA:A8:D5:3F:69:F1:18:15:26:3F:22:F4:2A"},{"algorithm":"sha-384","value":"65:F8:D9:C5:A5:0E:D4:AF:98:79:9B:1A:61:73:4F:DA:21:5E:E0:10:01:51:7A:A1:23:B2:E1:B8:F8:11:AC:09:82:6D:2B:EC:26:AD:D3:F1:33:3B:DB:5D:3D:CC:9A:10"},{"algorithm":"sha-512","value":"BB:78:53:30:B6:E1:9C:E0:A0:CD:15:8D:55:3E:67:AC:26:B8:40:50:C2:58:46:8D:8D:DF:CF:91:14:4D:D1:4C:DD:C0:17:EA:AF:33:05:6D:E8:F0:81:AF:B6:CB:99:12:65:14:FC:2D:18:F8:0A:7D:FF:B4:80:BE:8D:71:D0:3E"}]},
"sctpParameters":null
},
"recvTransport":{
"id":"{RECV_TRANSPORT_ID}",
"iceParameters":{"usernameFragment":"pg1qf22mdn65boag","password":"apaf0njnegjikxf1nib85gcgnl8ntis6","iceLite":true},
"iceCandidates":[{"foundation":"udpcandidate","priority":1076558079,"ip":"51.15.123.10","protocol":"udp","port":10611,"type":"host"},{"foundation":"tcpcandidate","priority":1076302079,"ip":"51.15.123.10","protocol":"tcp","port":10667,"type":"host","tcpType":"passive"}],
"dtlsParameters":{"role":"auto","fingerprints":[{"algorithm":"sha-1","value":"35:99:94:99:F8:F4:A2:02:06:F3:BE:17:AF:76:68:04:9A:81:C9:74"},{"algorithm":"sha-224","value":"51:4C:B1:03:0E:1B:50:53:CB:64:6E:B4:4E:18:40:E0:96:C8:F5:08:42:60:7A:C4:28:BD:6A:72"},{"algorithm":"sha-256","value":"92:DB:6F:16:DA:36:8F:D1:12:8F:0E:13:D2:22:39:82:7D:63:A1:FA:A8:D5:3F:69:F1:18:15:26:3F:22:F4:2A"},{"algorithm":"sha-384","value":"65:F8:D9:C5:A5:0E:D4:AF:98:79:9B:1A:61:73:4F:DA:21:5E:E0:10:01:51:7A:A1:23:B2:E1:B8:F8:11:AC:09:82:6D:2B:EC:26:AD:D3:F1:33:3B:DB:5D:3D:CC:9A:10"},{"algorithm":"sha-512","value":"BB:78:53:30:B6:E1:9C:E0:A0:CD:15:8D:55:3E:67:AC:26:B8:40:50:C2:58:46:8D:8D:DF:CF:91:14:4D:D1:4C:DD:C0:17:EA:AF:33:05:6D:E8:F0:81:AF:B6:CB:99:12:65:14:FC:2D:18:F8:0A:7D:FF:B4:80:BE:8D:71:D0:3E"}]},
"sctpParameters":null
}
}
}
상대방 유저 보이스 킴
RECV: {"type":"UserStartProduce","data":{"id":"{FRIEND_USERID}","type":"audio"}}
SEND: {"id":13,"type":"StartConsume","data":{"type":"audio","userId":"{FRIEND_USERID}"}}
RECV: {
"id":13,
"type":"StartConsume",
"data":{
"id":"f95cbc2c-dcc4-40f2-aafd-0fc1daf223d9",
"producerId":"5dabe193-a650-4faf-9a54-511f47bdf0e9",
"kind":"audio",
"rtpParameters":{
"mid":"0",
"codecs":[{"mimeType":"audio/opus","payloadType":100,"clockRate":48000,"channels":2,"parameters":{"minptime":10,"useinbandfec":1},"rtcpFeedback":[]}],
"headerExtensions":[{"uri":"urn:ietf:params:rtp-hdrext:sdes:mid","id":1,"encrypt":false},{"uri":"http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time","id":4,"encrypt":false},{"uri":"urn:ietf:params:rtp-hdrext:ssrc-audio-level","id":10,"encrypt":false}],
"encodings":[{"ssrc":459376258}],
"rtcp":{"cname":"dmSJC6zSPHuI49B1","reducedSize":true,"mux":true}
}
}
}
SEND: {
"id":14,
"type":"ConnectTransport",
"data":{
"id":"{RECV_TRANSPORT_ID}",
"dtlsParameters":{"role":"client","fingerprints":[{"algorithm":"sha-256","value":"41:4B:8D:CA:3B:39:B0:8F:80:D8:61:01:4C:94:49:D6:67:BD:00:35:A6:33:E6:DF:0A:7B:1E:50:28:34:47:2F"}]}
}
}
RECV: {"id":14,"type":"ConnectTransport"}
SEND: {"id":15,"type":"SetConsumerPause","data":{"id":"f95cbc2c-dcc4-40f2-aafd-0fc1daf223d9","paused":false}}
RECV: {"id":15,"type":"SetConsumerPause"}
상대방 유저 보이스 끔
RECV: {"type":"UserStopProduce","data":{"id":"{FRIEND_USERID}","type":"audio"}}
SEND: {"id":16,"type":"StopConsume","data":{"id":"f95cbc2c-dcc4-40f2-aafd-0fc1daf223d9"}}
RECV: {"id":16,"type":"StopConsume"}
내가 보이스 킴
SEND: {
"id":29,
"type":"ConnectTransport",
"data":{
"id":"{SEND_TRANSPORT_ID}",
"dtlsParameters":{"role":"server","fingerprints":[{"algorithm":"sha-256","value":"00:27:DE:32:0B:15:9D:D3:D3:B7:9C:A2:A2:C3:B5:90:71:12:D2:C0:27:E1:02:74:72:91:F5:01:E3:00:52:5C"}]
}
}
}
RECV: {"id":29,"type":"ConnectTransport"}
SEND: {
"id":30,
"type":"StartProduce",
"data":{
"type":"audio",
"rtpParameters":{
"codecs":[{"mimeType":"audio/opus","payloadType":111,"clockRate":48000,"channels":2,"parameters":{"minptime":10,"useinbandfec":1},"rtcpFeedback":[{"type":"transport-cc","parameter":""}]}],
"headerExtensions":[{"uri":"urn:ietf:params:rtp-hdrext:sdes:mid","id":4,"encrypt":false,"parameters":{}},{"uri":"http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time","id":2,"encrypt":false,"parameters":{}},{"uri":"http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01","id":3,"encrypt":false,"parameters":{}},{"uri":"urn:ietf:params:rtp-hdrext:ssrc-audio-level","id":1,"encrypt":false,"parameters":{}}],
"encodings":[{"ssrc":3721393752,"dtx":false}],
"rtcp":{"cname":"k5nYiwpghxZp4XHa","reducedSize":true},
"mid":"0"
}
}
}
RECV: {
"id":30,
"type":"StartProduce",
"data":{"producerId":"{MY_USERID}"}
}
내가 보이스 끔
SEND: {"id":31,"type":"StopProduce","data":{"type":"audio"}}
RECV: {"id":31,"type":"StopProduce"}
'Programming > Server' 카테고리의 다른 글
AWS Athena 테이블 이름 변경하는 방법 (0) | 2023.03.29 |
---|