mirror of
https://github.com/shtorm-7/sing-box-extended.git
synced 2026-08-07 22:35:16 +03:00
709 lines
15 KiB
Go
709 lines
15 KiB
Go
package livekit
|
|
|
|
import "fmt"
|
|
|
|
const (
|
|
signalReqOffer = 1
|
|
signalReqAnswer = 2
|
|
signalReqTrickle = 3
|
|
signalReqAddTrack = 4
|
|
signalReqLeave = 8
|
|
signalReqPingLegacy = 14
|
|
signalReqPingReq = 16
|
|
|
|
signalRespJoin = 1
|
|
signalRespAnswer = 2
|
|
signalRespOffer = 3
|
|
signalRespTrickle = 4
|
|
signalRespUpdate = 5
|
|
signalRespTrackPublished = 6
|
|
signalRespLeave = 8
|
|
signalRespRoomUpdate = 11
|
|
signalRespRefreshToken = 16
|
|
signalRespPongResp = 20
|
|
signalRespRequestResponse = 22
|
|
signalRespTrackSubscribed = 23
|
|
|
|
sdpFieldType = 1
|
|
sdpFieldSDP = 2
|
|
sdpFieldID = 3
|
|
|
|
trickleFieldCandidate = 1
|
|
trickleFieldTarget = 2
|
|
trickleFieldFinal = 3
|
|
|
|
addTrackFieldCID = 1
|
|
addTrackFieldName = 2
|
|
addTrackFieldType = 3
|
|
addTrackFieldWidth = 4
|
|
addTrackFieldHeight = 5
|
|
addTrackFieldSource = 8
|
|
addTrackFieldLayers = 9
|
|
|
|
videoLayerFieldQuality = 1
|
|
videoLayerFieldWidth = 2
|
|
videoLayerFieldHeight = 3
|
|
|
|
videoQualityHigh = 2
|
|
|
|
joinFieldRoom = 1
|
|
joinFieldParticipant = 2
|
|
joinFieldOtherParticipants = 3
|
|
joinFieldServerVersion = 4
|
|
joinFieldICEServers = 5
|
|
joinFieldSubscriberPrimary = 6
|
|
joinFieldServerRegion = 9
|
|
joinFieldPingTimeout = 10
|
|
joinFieldPingInterval = 11
|
|
|
|
iceServerFieldURLs = 1
|
|
iceServerFieldUsername = 2
|
|
iceServerFieldCredential = 3
|
|
|
|
pingFieldTimestamp = 1
|
|
pingFieldRTT = 2
|
|
|
|
dataPacketFieldKind = 1
|
|
dataPacketFieldUser = 2
|
|
|
|
userPacketFieldPayload = 2
|
|
|
|
DataPacketKindReliable = 0
|
|
DataPacketKindLossy = 1
|
|
|
|
leaveFieldCanReconnect = 1
|
|
leaveFieldReason = 2
|
|
leaveFieldAction = 3
|
|
|
|
roomFieldSID = 1
|
|
roomFieldName = 2
|
|
|
|
participantFieldSID = 1
|
|
participantFieldIdentity = 2
|
|
participantFieldState = 3
|
|
participantFieldName = 9
|
|
|
|
trackTypeAudio = 0
|
|
trackTypeVideo = 1
|
|
trackTypeData = 2
|
|
|
|
signalTargetPublisher = 0
|
|
signalTargetSubscriber = 1
|
|
|
|
trackSourceCamera = 1
|
|
trackSourceScreenShare = 3
|
|
)
|
|
|
|
const (
|
|
ParticipantStateJoining int32 = 0
|
|
ParticipantStateJoined int32 = 1
|
|
ParticipantStateActive int32 = 2
|
|
ParticipantStateDisconnected int32 = 3
|
|
)
|
|
|
|
var disconnectReasonNames = map[int]string{
|
|
0: "UNKNOWN",
|
|
1: "CLIENT_INITIATED",
|
|
2: "DUPLICATE_IDENTITY",
|
|
3: "SERVER_SHUTDOWN",
|
|
4: "PARTICIPANT_REMOVED",
|
|
5: "ROOM_DELETED",
|
|
6: "STATE_MISMATCH",
|
|
7: "JOIN_FAILURE",
|
|
8: "MIGRATION",
|
|
9: "SIGNAL_CLOSE",
|
|
10: "ROOM_CLOSED",
|
|
11: "USER_UNAVAILABLE",
|
|
12: "USER_REJECTED",
|
|
13: "SIP_TRUNK_FAILURE",
|
|
14: "CONNECTION_TIMEOUT",
|
|
15: "MEDIA_FAILURE",
|
|
16: "AGENT_ERROR",
|
|
}
|
|
|
|
var leaveActionNames = map[int]string{
|
|
0: "DISCONNECT",
|
|
1: "RESUME",
|
|
2: "RECONNECT",
|
|
}
|
|
|
|
type LeaveInfo struct {
|
|
Reason int
|
|
Action int
|
|
}
|
|
|
|
type sessionDescription struct {
|
|
Type string
|
|
SDP string
|
|
ID uint32
|
|
}
|
|
|
|
type trickleMsg struct {
|
|
CandidateInit string
|
|
Target int
|
|
Final bool
|
|
}
|
|
|
|
type iceServer struct {
|
|
URLs []string
|
|
Username string
|
|
Credential string
|
|
}
|
|
|
|
type joinResponse struct {
|
|
RoomSID string
|
|
RoomName string
|
|
ParticipantSID string
|
|
ParticipantID string
|
|
ServerVersion string
|
|
ServerRegion string
|
|
ICEServers []iceServer
|
|
SubscriberPrimary bool
|
|
PingTimeoutSec int32
|
|
PingIntervalSec int32
|
|
}
|
|
|
|
type signalResponse struct {
|
|
Kind int
|
|
Join *joinResponse
|
|
SDP *sessionDescription
|
|
Trickle *trickleMsg
|
|
Token string
|
|
PongTime int64
|
|
Leave *LeaveInfo
|
|
Participants []ParticipantInfo
|
|
}
|
|
|
|
type ParticipantInfo struct {
|
|
SID string
|
|
Identity string
|
|
State int32
|
|
Name string
|
|
}
|
|
|
|
func DisconnectReasonName(code int) string {
|
|
if name, ok := disconnectReasonNames[code]; ok {
|
|
return name
|
|
}
|
|
return fmt.Sprintf("CODE_%d", code)
|
|
}
|
|
|
|
func LeaveActionName(code int) string {
|
|
if name, ok := leaveActionNames[code]; ok {
|
|
return name
|
|
}
|
|
return fmt.Sprintf("CODE_%d", code)
|
|
}
|
|
|
|
func DecodeLeaveRequest(data []byte) LeaveInfo {
|
|
r := pbReader{buf: data}
|
|
var li LeaveInfo
|
|
for !r.eof() {
|
|
field, wire, err := r.tag()
|
|
if err != nil {
|
|
return li
|
|
}
|
|
switch {
|
|
case field == leaveFieldReason && wire == wireVarint:
|
|
v, _ := r.varint()
|
|
li.Reason = int(v)
|
|
case field == leaveFieldAction && wire == wireVarint:
|
|
v, _ := r.varint()
|
|
li.Action = int(v)
|
|
default:
|
|
if err := r.skipWire(wire); err != nil {
|
|
return li
|
|
}
|
|
}
|
|
}
|
|
return li
|
|
}
|
|
|
|
func EncodeDataPacketUser(payload []byte, kind int) []byte {
|
|
w := pbWriter{}
|
|
if kind != 0 {
|
|
w.int32(dataPacketFieldKind, int32(kind))
|
|
}
|
|
w.message(dataPacketFieldUser, encUserPacket(payload))
|
|
return w.buf
|
|
}
|
|
|
|
func DecodeDataPacketUser(data []byte) ([]byte, bool) {
|
|
r := pbReader{buf: data}
|
|
for !r.eof() {
|
|
field, wire, err := r.tag()
|
|
if err != nil {
|
|
return nil, false
|
|
}
|
|
if field == dataPacketFieldUser && wire == wireBytes {
|
|
inner, err := r.bytes()
|
|
if err != nil {
|
|
return nil, false
|
|
}
|
|
ur := pbReader{buf: inner}
|
|
for !ur.eof() {
|
|
ufield, uwire, uerr := ur.tag()
|
|
if uerr != nil {
|
|
return nil, false
|
|
}
|
|
if ufield == userPacketFieldPayload && uwire == wireBytes {
|
|
payload, perr := ur.bytes()
|
|
if perr != nil {
|
|
return nil, false
|
|
}
|
|
out := make([]byte, len(payload))
|
|
copy(out, payload)
|
|
return out, true
|
|
}
|
|
if err := ur.skipWire(uwire); err != nil {
|
|
return nil, false
|
|
}
|
|
}
|
|
return nil, false
|
|
}
|
|
if err := r.skipWire(wire); err != nil {
|
|
return nil, false
|
|
}
|
|
}
|
|
return nil, false
|
|
}
|
|
|
|
func DecodeParticipantInfo(data []byte) ParticipantInfo {
|
|
r := pbReader{buf: data}
|
|
var info ParticipantInfo
|
|
for !r.eof() {
|
|
field, wire, err := r.tag()
|
|
if err != nil {
|
|
return info
|
|
}
|
|
switch {
|
|
case field == participantFieldSID && wire == wireBytes:
|
|
b, err := r.bytes()
|
|
if err != nil {
|
|
return info
|
|
}
|
|
info.SID = string(b)
|
|
case field == participantFieldIdentity && wire == wireBytes:
|
|
b, err := r.bytes()
|
|
if err != nil {
|
|
return info
|
|
}
|
|
info.Identity = string(b)
|
|
case field == participantFieldState && wire == wireVarint:
|
|
v, err := r.varint()
|
|
if err != nil {
|
|
return info
|
|
}
|
|
info.State = int32(v)
|
|
case field == participantFieldName && wire == wireBytes:
|
|
b, err := r.bytes()
|
|
if err != nil {
|
|
return info
|
|
}
|
|
info.Name = string(b)
|
|
default:
|
|
if err := r.skipWire(wire); err != nil {
|
|
return info
|
|
}
|
|
}
|
|
}
|
|
return info
|
|
}
|
|
|
|
func DecodeParticipantUpdate(data []byte) []ParticipantInfo {
|
|
r := pbReader{buf: data}
|
|
var out []ParticipantInfo
|
|
for !r.eof() {
|
|
field, wire, err := r.tag()
|
|
if err != nil {
|
|
return out
|
|
}
|
|
if field == 1 && wire == wireBytes {
|
|
b, err := r.bytes()
|
|
if err != nil {
|
|
return out
|
|
}
|
|
out = append(out, DecodeParticipantInfo(b))
|
|
} else {
|
|
if err := r.skipWire(wire); err != nil {
|
|
return out
|
|
}
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
func encSessionDescription(sd sessionDescription) []byte {
|
|
w := pbWriter{}
|
|
if sd.Type != "" {
|
|
w.string(sdpFieldType, sd.Type)
|
|
}
|
|
if sd.SDP != "" {
|
|
w.string(sdpFieldSDP, sd.SDP)
|
|
}
|
|
if sd.ID != 0 {
|
|
w.uint32(sdpFieldID, sd.ID)
|
|
}
|
|
return w.buf
|
|
}
|
|
|
|
func encTrickle(m trickleMsg) []byte {
|
|
w := pbWriter{}
|
|
w.string(trickleFieldCandidate, m.CandidateInit)
|
|
w.int32(trickleFieldTarget, int32(m.Target))
|
|
if m.Final {
|
|
w.bool(trickleFieldFinal, true)
|
|
}
|
|
return w.buf
|
|
}
|
|
|
|
func encVideoLayer(quality, width, height uint32) []byte {
|
|
w := pbWriter{}
|
|
if quality != 0 {
|
|
w.uint32(videoLayerFieldQuality, quality)
|
|
}
|
|
if width != 0 {
|
|
w.uint32(videoLayerFieldWidth, width)
|
|
}
|
|
if height != 0 {
|
|
w.uint32(videoLayerFieldHeight, height)
|
|
}
|
|
return w.buf
|
|
}
|
|
|
|
func encAddTrack(cid, name string, trackType, source int, width, height uint32) []byte {
|
|
w := pbWriter{}
|
|
w.string(addTrackFieldCID, cid)
|
|
w.string(addTrackFieldName, name)
|
|
w.int32(addTrackFieldType, int32(trackType))
|
|
if width != 0 {
|
|
w.uint32(addTrackFieldWidth, width)
|
|
}
|
|
if height != 0 {
|
|
w.uint32(addTrackFieldHeight, height)
|
|
}
|
|
w.int32(addTrackFieldSource, int32(source))
|
|
if trackType == trackTypeVideo {
|
|
w.message(addTrackFieldLayers, encVideoLayer(videoQualityHigh, width, height))
|
|
}
|
|
return w.buf
|
|
}
|
|
|
|
func encPing(timestamp int64) []byte {
|
|
w := pbWriter{}
|
|
w.int64(pingFieldTimestamp, timestamp)
|
|
return w.buf
|
|
}
|
|
|
|
func encUserPacket(payload []byte) []byte {
|
|
w := pbWriter{}
|
|
w.bytes(userPacketFieldPayload, payload)
|
|
return w.buf
|
|
}
|
|
|
|
func encSignalRequestOffer(sd sessionDescription) []byte {
|
|
w := pbWriter{}
|
|
w.message(signalReqOffer, encSessionDescription(sd))
|
|
return w.buf
|
|
}
|
|
|
|
func encSignalRequestAnswer(sd sessionDescription) []byte {
|
|
w := pbWriter{}
|
|
w.message(signalReqAnswer, encSessionDescription(sd))
|
|
return w.buf
|
|
}
|
|
|
|
func encSignalRequestTrickle(m trickleMsg) []byte {
|
|
w := pbWriter{}
|
|
w.message(signalReqTrickle, encTrickle(m))
|
|
return w.buf
|
|
}
|
|
|
|
func encSignalRequestAddTrack(cid, name string, trackType, source int, width, height uint32) []byte {
|
|
w := pbWriter{}
|
|
w.message(signalReqAddTrack, encAddTrack(cid, name, trackType, source, width, height))
|
|
return w.buf
|
|
}
|
|
|
|
func encSignalRequestLeave() []byte {
|
|
w := pbWriter{}
|
|
w.message(signalReqLeave, []byte{})
|
|
return w.buf
|
|
}
|
|
|
|
func encSignalRequestPing(timestamp int64) []byte {
|
|
w := pbWriter{}
|
|
w.int64(signalReqPingLegacy, timestamp)
|
|
w.message(signalReqPingReq, encPing(timestamp))
|
|
return w.buf
|
|
}
|
|
|
|
func decSessionDescription(data []byte) (sessionDescription, error) {
|
|
r := pbReader{buf: data}
|
|
var sd sessionDescription
|
|
for !r.eof() {
|
|
field, wire, err := r.tag()
|
|
if err != nil {
|
|
return sd, err
|
|
}
|
|
switch {
|
|
case field == sdpFieldType && wire == wireBytes:
|
|
b, err := r.bytes()
|
|
if err != nil {
|
|
return sd, err
|
|
}
|
|
sd.Type = string(b)
|
|
case field == sdpFieldSDP && wire == wireBytes:
|
|
b, err := r.bytes()
|
|
if err != nil {
|
|
return sd, err
|
|
}
|
|
sd.SDP = string(b)
|
|
case field == sdpFieldID && wire == wireVarint:
|
|
v, err := r.varint()
|
|
if err != nil {
|
|
return sd, err
|
|
}
|
|
sd.ID = uint32(v)
|
|
default:
|
|
if err := r.skipWire(wire); err != nil {
|
|
return sd, err
|
|
}
|
|
}
|
|
}
|
|
return sd, nil
|
|
}
|
|
|
|
func decTrickle(data []byte) (trickleMsg, error) {
|
|
r := pbReader{buf: data}
|
|
var m trickleMsg
|
|
for !r.eof() {
|
|
field, wire, err := r.tag()
|
|
if err != nil {
|
|
return m, err
|
|
}
|
|
switch {
|
|
case field == trickleFieldCandidate && wire == wireBytes:
|
|
b, err := r.bytes()
|
|
if err != nil {
|
|
return m, err
|
|
}
|
|
m.CandidateInit = string(b)
|
|
case field == trickleFieldTarget && wire == wireVarint:
|
|
v, err := r.varint()
|
|
if err != nil {
|
|
return m, err
|
|
}
|
|
m.Target = int(v)
|
|
case field == trickleFieldFinal && wire == wireVarint:
|
|
v, err := r.varint()
|
|
if err != nil {
|
|
return m, err
|
|
}
|
|
m.Final = v != 0
|
|
default:
|
|
if err := r.skipWire(wire); err != nil {
|
|
return m, err
|
|
}
|
|
}
|
|
}
|
|
return m, nil
|
|
}
|
|
|
|
func decICEServer(data []byte) (iceServer, error) {
|
|
r := pbReader{buf: data}
|
|
var s iceServer
|
|
for !r.eof() {
|
|
field, wire, err := r.tag()
|
|
if err != nil {
|
|
return s, err
|
|
}
|
|
switch {
|
|
case field == iceServerFieldURLs && wire == wireBytes:
|
|
b, err := r.bytes()
|
|
if err != nil {
|
|
return s, err
|
|
}
|
|
s.URLs = append(s.URLs, string(b))
|
|
case field == iceServerFieldUsername && wire == wireBytes:
|
|
b, err := r.bytes()
|
|
if err != nil {
|
|
return s, err
|
|
}
|
|
s.Username = string(b)
|
|
case field == iceServerFieldCredential && wire == wireBytes:
|
|
b, err := r.bytes()
|
|
if err != nil {
|
|
return s, err
|
|
}
|
|
s.Credential = string(b)
|
|
default:
|
|
if err := r.skipWire(wire); err != nil {
|
|
return s, err
|
|
}
|
|
}
|
|
}
|
|
return s, nil
|
|
}
|
|
|
|
func decRoom(data []byte) (string, string, error) {
|
|
r := pbReader{buf: data}
|
|
var sid, name string
|
|
for !r.eof() {
|
|
field, wire, err := r.tag()
|
|
if err != nil {
|
|
return "", "", err
|
|
}
|
|
switch {
|
|
case field == roomFieldSID && wire == wireBytes:
|
|
b, err := r.bytes()
|
|
if err != nil {
|
|
return "", "", err
|
|
}
|
|
sid = string(b)
|
|
case field == roomFieldName && wire == wireBytes:
|
|
b, err := r.bytes()
|
|
if err != nil {
|
|
return "", "", err
|
|
}
|
|
name = string(b)
|
|
default:
|
|
if err := r.skipWire(wire); err != nil {
|
|
return "", "", err
|
|
}
|
|
}
|
|
}
|
|
return sid, name, nil
|
|
}
|
|
|
|
func decParticipant(data []byte) (string, string, error) {
|
|
info := DecodeParticipantInfo(data)
|
|
return info.SID, info.Identity, nil
|
|
}
|
|
|
|
func decJoinResponse(data []byte) (joinResponse, error) {
|
|
r := pbReader{buf: data}
|
|
var jr joinResponse
|
|
for !r.eof() {
|
|
field, wire, err := r.tag()
|
|
if err != nil {
|
|
return jr, err
|
|
}
|
|
switch {
|
|
case field == joinFieldRoom && wire == wireBytes:
|
|
b, err := r.bytes()
|
|
if err != nil {
|
|
return jr, err
|
|
}
|
|
sid, name, _ := decRoom(b)
|
|
jr.RoomSID = sid
|
|
jr.RoomName = name
|
|
case field == joinFieldParticipant && wire == wireBytes:
|
|
b, err := r.bytes()
|
|
if err != nil {
|
|
return jr, err
|
|
}
|
|
sid, identity, _ := decParticipant(b)
|
|
jr.ParticipantSID = sid
|
|
jr.ParticipantID = identity
|
|
case field == joinFieldServerVersion && wire == wireBytes:
|
|
b, err := r.bytes()
|
|
if err != nil {
|
|
return jr, err
|
|
}
|
|
jr.ServerVersion = string(b)
|
|
case field == joinFieldICEServers && wire == wireBytes:
|
|
b, err := r.bytes()
|
|
if err != nil {
|
|
return jr, err
|
|
}
|
|
s, _ := decICEServer(b)
|
|
jr.ICEServers = append(jr.ICEServers, s)
|
|
case field == joinFieldSubscriberPrimary && wire == wireVarint:
|
|
v, err := r.varint()
|
|
if err != nil {
|
|
return jr, err
|
|
}
|
|
jr.SubscriberPrimary = v != 0
|
|
case field == joinFieldServerRegion && wire == wireBytes:
|
|
b, err := r.bytes()
|
|
if err != nil {
|
|
return jr, err
|
|
}
|
|
jr.ServerRegion = string(b)
|
|
case field == joinFieldPingTimeout && wire == wireVarint:
|
|
v, err := r.varint()
|
|
if err != nil {
|
|
return jr, err
|
|
}
|
|
jr.PingTimeoutSec = int32(v)
|
|
case field == joinFieldPingInterval && wire == wireVarint:
|
|
v, err := r.varint()
|
|
if err != nil {
|
|
return jr, err
|
|
}
|
|
jr.PingIntervalSec = int32(v)
|
|
case field == joinFieldOtherParticipants && wire == wireBytes:
|
|
if _, err := r.bytes(); err != nil {
|
|
return jr, err
|
|
}
|
|
default:
|
|
if err := r.skipWire(wire); err != nil {
|
|
return jr, err
|
|
}
|
|
}
|
|
}
|
|
return jr, nil
|
|
}
|
|
|
|
func decSignalResponse(data []byte) (signalResponse, error) {
|
|
r := pbReader{buf: data}
|
|
var sr signalResponse
|
|
for !r.eof() {
|
|
field, wire, err := r.tag()
|
|
if err != nil {
|
|
return sr, err
|
|
}
|
|
if wire != wireBytes {
|
|
if err := r.skipWire(wire); err != nil {
|
|
return sr, err
|
|
}
|
|
continue
|
|
}
|
|
inner, err := r.bytes()
|
|
if err != nil {
|
|
return sr, err
|
|
}
|
|
sr.Kind = int(field)
|
|
switch field {
|
|
case signalRespJoin:
|
|
jr, err := decJoinResponse(inner)
|
|
if err != nil {
|
|
return sr, err
|
|
}
|
|
sr.Join = &jr
|
|
case signalRespAnswer, signalRespOffer:
|
|
sd, err := decSessionDescription(inner)
|
|
if err != nil {
|
|
return sr, err
|
|
}
|
|
sr.SDP = &sd
|
|
case signalRespTrickle:
|
|
tm, err := decTrickle(inner)
|
|
if err != nil {
|
|
return sr, err
|
|
}
|
|
sr.Trickle = &tm
|
|
case signalRespRefreshToken:
|
|
sr.Token = string(inner)
|
|
case signalRespLeave:
|
|
li := DecodeLeaveRequest(inner)
|
|
sr.Leave = &li
|
|
case signalRespUpdate:
|
|
sr.Participants = DecodeParticipantUpdate(inner)
|
|
}
|
|
return sr, nil
|
|
}
|
|
return sr, nil
|
|
}
|