Revolt Chat 분석

Programming/Server 2023. 1. 8. 22:28 by 빠재

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
Nav
1" /> 2" /> 3" /> 4" /> 5" /> 6" /> 7" /> ···" /> 76" />