From 229c80ac17a1cb40a97c5130b70ff0e629431d94 Mon Sep 17 00:00:00 2001 From: Paul Wells Date: Thu, 9 Nov 2023 00:29:21 -0800 Subject: [PATCH 01/18] retry jobs when agent ws write fails (#2230) * retry jobs when agent ws write fails * release lock before io --- pkg/service/agentservice.go | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/pkg/service/agentservice.go b/pkg/service/agentservice.go index baaf40da4..8a7a6160f 100644 --- a/pkg/service/agentservice.go +++ b/pkg/service/agentservice.go @@ -199,8 +199,6 @@ func (s *AgentHandler) HandleConnection(conn *websocket.Conn) { func (s *AgentHandler) handleRegister(worker *worker, msg *livekit.RegisterWorkerRequest) { s.mu.Lock() - defer s.mu.Unlock() - switch msg.Type { case livekit.JobType_JT_ROOM: worker.id = msg.WorkerId @@ -230,6 +228,7 @@ func (s *AgentHandler) handleRegister(worker *worker, msg *livekit.RegisterWorke } } } + s.mu.Unlock() _, err := worker.sigConn.WriteServerMessage(&livekit.ServerMessage{ Message: &livekit.ServerMessage_Register{ @@ -366,8 +365,7 @@ func (s *AgentHandler) JobRequest(ctx context.Context, job *livekit.Job) (*empty Availability: &livekit.AvailabilityRequest{Job: job}, }}) if err != nil { - logger.Errorw("failed to send availability request", err) - return nil, err + logger.Errorw("failed to send availability request", err, "workerID", selected.id) } select { @@ -379,7 +377,7 @@ func (s *AgentHandler) JobRequest(ctx context.Context, job *livekit.Job) (*empty Assignment: &livekit.JobAssignment{Job: job}, }}) if err != nil { - logger.Errorw("failed to assign job", err) + logger.Errorw("failed to assign job", err, "workerID", selected.id) } else { selected.mu.Lock() selected.activeJobs++ From aa797c749c61e731f3b8aaa24a3040b3a80f0516 Mon Sep 17 00:00:00 2001 From: Paul Wells Date: Thu, 9 Nov 2023 00:58:59 -0800 Subject: [PATCH 02/18] validate input to agent worker register (#2231) * validate input to agent worker register * prevent multiple registrations * cleanup --- pkg/service/agentservice.go | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/pkg/service/agentservice.go b/pkg/service/agentservice.go index 8a7a6160f..f2ba672b2 100644 --- a/pkg/service/agentservice.go +++ b/pkg/service/agentservice.go @@ -198,10 +198,27 @@ func (s *AgentHandler) HandleConnection(conn *websocket.Conn) { } func (s *AgentHandler) handleRegister(worker *worker, msg *livekit.RegisterWorkerRequest) { + if err := s.doHandleRegister(worker, msg); err != nil { + logger.Errorw("failed to register worker", err, "workerID", msg.WorkerId, "jobType", msg.Type) + worker.conn.Close() + } +} + +func (s *AgentHandler) doHandleRegister(worker *worker, msg *livekit.RegisterWorkerRequest) error { + if msg.WorkerId == "" { + return errors.New("invalid worker id") + } + s.mu.Lock() + if worker.id != "" { + s.mu.Unlock() + return errors.New("worker already registered") + } + switch msg.Type { case livekit.JobType_JT_ROOM: worker.id = msg.WorkerId + worker.jobType = msg.Type delete(s.unregistered, worker.conn) s.roomWorkers[worker.id] = worker @@ -216,6 +233,7 @@ func (s *AgentHandler) handleRegister(worker *worker, msg *livekit.RegisterWorke case livekit.JobType_JT_PUBLISHER: worker.id = msg.WorkerId + worker.jobType = msg.Type delete(s.unregistered, worker.conn) s.publisherWorkers[worker.id] = worker @@ -227,6 +245,9 @@ func (s *AgentHandler) handleRegister(worker *worker, msg *livekit.RegisterWorke s.publisherRegistered = true } } + default: + s.mu.Unlock() + return errors.New("invalid job type") } s.mu.Unlock() @@ -241,6 +262,8 @@ func (s *AgentHandler) handleRegister(worker *worker, msg *livekit.RegisterWorke if err != nil { logger.Errorw("failed to write server message", err) } + + return nil } func (s *AgentHandler) handleAvailability(w *worker, msg *livekit.AvailabilityResponse) { From 5dda31d42dc0bcf7c474add7305fd1290f82493c Mon Sep 17 00:00:00 2001 From: Paul Wells Date: Thu, 9 Nov 2023 01:31:00 -0800 Subject: [PATCH 03/18] register one job per test agent worker (#2232) --- test/agent.go | 50 +++++++++++++++++++++++----------------------- test/agent_test.go | 23 +++++++++++++++------ 2 files changed, 42 insertions(+), 31 deletions(-) diff --git a/test/agent.go b/test/agent.go index 76f608314..65b4952b6 100644 --- a/test/agent.go +++ b/test/agent.go @@ -48,38 +48,38 @@ func newAgentClient(token string) (*agentClient, error) { }, nil } -func (c *agentClient) Run() error { +func (c *agentClient) Run(jobType livekit.JobType) (err error) { go c.read() workerID := utils.NewGuid("W_") - if err := c.write(&livekit.WorkerMessage{ - Message: &livekit.WorkerMessage_Register{ - Register: &livekit.RegisterWorkerRequest{ - Type: livekit.JobType_JT_ROOM, - WorkerId: workerID, - Version: "version", - Name: "name", + switch jobType { + case livekit.JobType_JT_ROOM: + err = c.write(&livekit.WorkerMessage{ + Message: &livekit.WorkerMessage_Register{ + Register: &livekit.RegisterWorkerRequest{ + Type: livekit.JobType_JT_ROOM, + WorkerId: workerID, + Version: "version", + Name: "name", + }, }, - }, - }); err != nil { - return err + }) + + case livekit.JobType_JT_PUBLISHER: + err = c.write(&livekit.WorkerMessage{ + Message: &livekit.WorkerMessage_Register{ + Register: &livekit.RegisterWorkerRequest{ + Type: livekit.JobType_JT_PUBLISHER, + WorkerId: workerID, + Version: "version", + Name: "name", + }, + }, + }) } - if err := c.write(&livekit.WorkerMessage{ - Message: &livekit.WorkerMessage_Register{ - Register: &livekit.RegisterWorkerRequest{ - Type: livekit.JobType_JT_PUBLISHER, - WorkerId: workerID, - Version: "version", - Name: "name", - }, - }, - }); err != nil { - return err - } - - return nil + return err } func (c *agentClient) read() { diff --git a/test/agent_test.go b/test/agent_test.go index 0a45fa9ae..a8825577b 100644 --- a/test/agent_test.go +++ b/test/agent_test.go @@ -21,6 +21,7 @@ import ( "github.com/stretchr/testify/require" "github.com/livekit/protocol/auth" + "github.com/livekit/protocol/livekit" ) func TestAgents(t *testing.T) { @@ -31,15 +32,25 @@ func TestAgents(t *testing.T) { require.NoError(t, err) ac2, err := newAgentClient(agentToken()) require.NoError(t, err) + ac3, err := newAgentClient(agentToken()) + require.NoError(t, err) + ac4, err := newAgentClient(agentToken()) + require.NoError(t, err) defer ac1.close() defer ac2.close() - ac1.Run() - ac2.Run() + defer ac3.close() + defer ac4.close() + ac1.Run(livekit.JobType_JT_ROOM) + ac2.Run(livekit.JobType_JT_ROOM) + ac3.Run(livekit.JobType_JT_PUBLISHER) + ac4.Run(livekit.JobType_JT_PUBLISHER) time.Sleep(time.Second * 3) - require.Equal(t, int32(2), ac1.registered.Load()) - require.Equal(t, int32(2), ac2.registered.Load()) + require.Equal(t, int32(1), ac1.registered.Load()) + require.Equal(t, int32(1), ac2.registered.Load()) + require.Equal(t, int32(1), ac3.registered.Load()) + require.Equal(t, int32(1), ac4.registered.Load()) c1 := createRTCClient("c1", defaultServerPort, nil) c2 := createRTCClient("c2", defaultServerPort, nil) @@ -56,7 +67,7 @@ func TestAgents(t *testing.T) { time.Sleep(time.Second * 3) require.Equal(t, int32(1), ac1.roomJobs.Load()+ac2.roomJobs.Load()) - require.Equal(t, int32(1), ac1.participantJobs.Load()+ac2.participantJobs.Load()) + require.Equal(t, int32(1), ac3.participantJobs.Load()+ac4.participantJobs.Load()) // publish 2 tracks t3, err := c2.AddStaticTrack("audio/opus", "audio", "webcam") @@ -69,7 +80,7 @@ func TestAgents(t *testing.T) { time.Sleep(time.Second * 3) require.Equal(t, int32(1), ac1.roomJobs.Load()+ac2.roomJobs.Load()) - require.Equal(t, int32(2), ac1.participantJobs.Load()+ac2.participantJobs.Load()) + require.Equal(t, int32(2), ac3.participantJobs.Load()+ac4.participantJobs.Load()) } func agentToken() string { From bcce9c26ebcc5b89e8cb307a9f1870f631cf1879 Mon Sep 17 00:00:00 2001 From: Raja Subramanian Date: Thu, 9 Nov 2023 17:15:23 +0530 Subject: [PATCH 04/18] Helpers accessible from other packages. (#2233) --- pkg/rtc/room_test.go | 16 ++++++++-------- pkg/rtc/{helper_test.go => testutils.go} | 12 ++++++++++-- 2 files changed, 18 insertions(+), 10 deletions(-) rename pkg/rtc/{helper_test.go => testutils.go} (85%) diff --git a/pkg/rtc/room_test.go b/pkg/rtc/room_test.go index b86527583..36322cbc1 100644 --- a/pkg/rtc/room_test.go +++ b/pkg/rtc/room_test.go @@ -83,7 +83,7 @@ func TestJoinedState(t *testing.T) { func TestRoomJoin(t *testing.T) { t.Run("joining returns existing participant data", func(t *testing.T) { rm := newRoomWithParticipants(t, testRoomOpts{num: numParticipants}) - pNew := newMockParticipant("new", types.CurrentProtocol, false, false) + pNew := NewMockParticipant("new", types.CurrentProtocol, false, false) _ = rm.Join(pNew, nil, nil, iceServersForRoom) @@ -98,7 +98,7 @@ func TestRoomJoin(t *testing.T) { t.Run("subscribe to existing channels upon join", func(t *testing.T) { numExisting := 3 rm := newRoomWithParticipants(t, testRoomOpts{num: numExisting}) - p := newMockParticipant("new", types.CurrentProtocol, false, false) + p := NewMockParticipant("new", types.CurrentProtocol, false, false) err := rm.Join(p, nil, &ParticipantOptions{AutoSubscribe: true}, iceServersForRoom) require.NoError(t, err) @@ -154,7 +154,7 @@ func TestRoomJoin(t *testing.T) { rm.lock.Lock() rm.protoRoom.MaxParticipants = 1 rm.lock.Unlock() - p := newMockParticipant("second", types.ProtocolVersion(0), false, false) + p := NewMockParticipant("second", types.ProtocolVersion(0), false, false) err := rm.Join(p, nil, nil, iceServersForRoom) require.Equal(t, ErrMaxParticipantsExceeded, err) @@ -414,7 +414,7 @@ func TestNewTrack(t *testing.T) { pub := participants[2].(*typesfakes.FakeLocalParticipant) // pub adds track - track := newMockTrack(livekit.TrackType_VIDEO, "webcam") + track := NewMockTrack(livekit.TrackType_VIDEO, "webcam") trackCB := pub.OnTrackPublishedArgsForCall(0) require.NotNil(t, trackCB) trackCB(pub, track) @@ -653,7 +653,7 @@ func TestHiddenParticipants(t *testing.T) { rm := newRoomWithParticipants(t, testRoomOpts{num: 2, numHidden: 1}) defer rm.Close() - pNew := newMockParticipant("new", types.CurrentProtocol, false, false) + pNew := NewMockParticipant("new", types.CurrentProtocol, false, false) rm.Join(pNew, nil, nil, iceServersForRoom) // expect new participant to get a JoinReply @@ -667,7 +667,7 @@ func TestHiddenParticipants(t *testing.T) { t.Run("hidden participant subscribes to tracks", func(t *testing.T) { rm := newRoomWithParticipants(t, testRoomOpts{num: 2}) - hidden := newMockParticipant("hidden", types.CurrentProtocol, true, false) + hidden := NewMockParticipant("hidden", types.CurrentProtocol, true, false) err := rm.Join(hidden, nil, &ParticipantOptions{AutoSubscribe: true}, iceServersForRoom) require.NoError(t, err) @@ -689,7 +689,7 @@ func TestRoomUpdate(t *testing.T) { p1 := rm.GetParticipants()[0].(*typesfakes.FakeLocalParticipant) require.Equal(t, 0, p1.SendRoomUpdateCallCount()) - p2 := newMockParticipant("p2", types.CurrentProtocol, false, false) + p2 := NewMockParticipant("p2", types.CurrentProtocol, false, false) require.NoError(t, rm.Join(p2, nil, nil, iceServersForRoom)) // p1 should have received an update @@ -743,7 +743,7 @@ func newRoomWithParticipants(t *testing.T, opts testRoomOpts) *Room { ) for i := 0; i < opts.num+opts.numHidden; i++ { identity := livekit.ParticipantIdentity(fmt.Sprintf("p%d", i)) - participant := newMockParticipant(identity, opts.protocol, i >= opts.num, true) + participant := NewMockParticipant(identity, opts.protocol, i >= opts.num, true) err := rm.Join(participant, nil, &ParticipantOptions{AutoSubscribe: true}, iceServersForRoom) require.NoError(t, err) participant.StateReturns(livekit.ParticipantInfo_ACTIVE) diff --git a/pkg/rtc/helper_test.go b/pkg/rtc/testutils.go similarity index 85% rename from pkg/rtc/helper_test.go rename to pkg/rtc/testutils.go index 4a0087ea1..fe5c10ad6 100644 --- a/pkg/rtc/helper_test.go +++ b/pkg/rtc/testutils.go @@ -16,6 +16,7 @@ package rtc import ( "github.com/livekit/protocol/livekit" + "github.com/livekit/protocol/logger" "github.com/livekit/protocol/utils" "github.com/livekit/livekit-server/pkg/rtc/types" @@ -27,7 +28,7 @@ func init() { prometheus.Init("test", livekit.NodeType_SERVER, "test") } -func newMockParticipant(identity livekit.ParticipantIdentity, protocol types.ProtocolVersion, hidden bool, publisher bool) *typesfakes.FakeLocalParticipant { +func NewMockParticipant(identity livekit.ParticipantIdentity, protocol types.ProtocolVersion, hidden bool, publisher bool) *typesfakes.FakeLocalParticipant { p := &typesfakes.FakeLocalParticipant{} sid := utils.NewGuid(utils.ParticipantPrefix) p.IDReturns(livekit.ParticipantID(sid)) @@ -44,6 +45,12 @@ func newMockParticipant(identity livekit.ParticipantIdentity, protocol types.Pro State: livekit.ParticipantInfo_JOINED, IsPublisher: publisher, }) + p.ToProtoWithVersionReturns(&livekit.ParticipantInfo{ + Sid: sid, + Identity: string(identity), + State: livekit.ParticipantInfo_JOINED, + IsPublisher: publisher, + }, utils.TimedVersion{}) p.SetMetadataCalls(func(m string) { var f func(participant types.LocalParticipant) @@ -71,11 +78,12 @@ func newMockParticipant(identity livekit.ParticipantIdentity, protocol types.Pro p.AddTrackCalls(func(req *livekit.AddTrackRequest) { updateTrack() }) + p.GetLoggerReturns(logger.GetLogger()) return p } -func newMockTrack(kind livekit.TrackType, name string) *typesfakes.FakeMediaTrack { +func NewMockTrack(kind livekit.TrackType, name string) *typesfakes.FakeMediaTrack { t := &typesfakes.FakeMediaTrack{} t.IDReturns(livekit.TrackID(utils.NewGuid(utils.TrackPrefix))) t.KindReturns(kind) From 744208021fca3fe249a3deec189b34c300571a79 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 9 Nov 2023 12:03:55 -0800 Subject: [PATCH 05/18] Update pion deps (#2234) Generated by renovateBot Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- go.mod | 4 ++-- go.sum | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index df947357d..90df29949 100644 --- a/go.mod +++ b/go.mod @@ -28,8 +28,8 @@ require ( github.com/pion/dtls/v2 v2.2.7 github.com/pion/ice/v2 v2.3.11 github.com/pion/interceptor v0.1.25 - github.com/pion/rtcp v1.2.10 - github.com/pion/rtp v1.8.2 + github.com/pion/rtcp v1.2.12 + github.com/pion/rtp v1.8.3 github.com/pion/sctp v1.8.9 github.com/pion/sdp/v3 v3.0.6 github.com/pion/transport/v2 v2.2.4 diff --git a/go.sum b/go.sum index c59b38f48..1407ca329 100644 --- a/go.sum +++ b/go.sum @@ -193,11 +193,13 @@ github.com/pion/mdns v0.0.8 h1:HhicWIg7OX5PVilyBO6plhMetInbzkVJAhbdJiAeVaI= github.com/pion/mdns v0.0.8/go.mod h1:hYE72WX8WDveIhg7fmXgMKivD3Puklk0Ymzog0lSyaI= github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA= github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8= -github.com/pion/rtcp v1.2.10 h1:nkr3uj+8Sp97zyItdN60tE/S6vk4al5CPRR6Gejsdjc= github.com/pion/rtcp v1.2.10/go.mod h1:ztfEwXZNLGyF1oQDttz/ZKIBaeeg/oWbRYqzBM9TL1I= +github.com/pion/rtcp v1.2.12 h1:bKWiX93XKgDZENEXCijvHRU/wRifm6JV5DGcH6twtSM= +github.com/pion/rtcp v1.2.12/go.mod h1:sn6qjxvnwyAkkPzPULIbVqSKI5Dv54Rv7VG0kNxh9L4= github.com/pion/rtp v1.8.1/go.mod h1:pBGHaFt/yW7bf1jjWAoUjpSNoDnw98KTMg+jWWvziqU= -github.com/pion/rtp v1.8.2 h1:oKMM0K1/QYQ5b5qH+ikqDSZRipP5mIxPJcgcvw5sH0w= github.com/pion/rtp v1.8.2/go.mod h1:pBGHaFt/yW7bf1jjWAoUjpSNoDnw98KTMg+jWWvziqU= +github.com/pion/rtp v1.8.3 h1:VEHxqzSVQxCkKDSHro5/4IUUG1ea+MFdqR2R3xSpNU8= +github.com/pion/rtp v1.8.3/go.mod h1:pBGHaFt/yW7bf1jjWAoUjpSNoDnw98KTMg+jWWvziqU= github.com/pion/sctp v1.8.5/go.mod h1:SUFFfDpViyKejTAdwD1d/HQsCu+V/40cCs2nZIvC3s0= github.com/pion/sctp v1.8.8/go.mod h1:igF9nZBrjh5AtmKc7U30jXltsFHicFCXSmWA2GWRaWs= github.com/pion/sctp v1.8.9 h1:TP5ZVxV5J7rz7uZmbyvnUvsn7EJ2x/5q9uhsTtXbI3g= From a24b9b840d27c252f6a8209b02610f2c4822c8d8 Mon Sep 17 00:00:00 2001 From: David Zhao Date: Thu, 9 Nov 2023 15:41:45 -0800 Subject: [PATCH 06/18] v1.5.1 (#2235) --- CHANGELOG | 34 ++++++++++++++++++++++++++++++++++ version/version.go | 2 +- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index cac84e150..1947140a6 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -2,6 +2,40 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [1.5.1] - 2023-11-09 + +Support for the Agent framework. + +### Added +- PSRPC based room and participant service. disabled by default (#2171 #2205) +- Add configuration to limit MaxBufferedAmount for data channel (#2170) +- Agent framework worker support (#2203 #2227 #2230 #2231 #2232) + +### Fixed +- Fixed panic in StreamTracker when SVC is used (#2147) +- fix CreateEgress not completing (#2156) +- Do not update highest time on padding packet. (#2157) +- Clear flags in packet metadata cache before setting them. (#2160) +- Drop not relevant packet only if contiguous. (#2167) +- Fixed edge cases in SVC codec support (#2176 #2185 #2191 #2196 #2197 #2214 #2215 #2216 #2218 #2219) +- Do not post to closed channels. (#2179) +- Only launch room egress once (#2175) +- Remove un-preferred codecs for android firefox (#2183) +- Fix pre-extended value on wrap back restart. (#2202) +- Declare audio inactive if stale. (#2229) + +### Changed +- Defer close of source and sink to prevent error logs. (#2149) +- Continued AV Sync improvements (#2150 #2153) +- Egress store/IO cleanup (required for Egress 1.8.0) (#2152) +- More fine grained filtering NACKs after a key frame. (#2159) +- Don't filter out ipv6 address for client don't support prflx over relay (#2193) +- Disable h264 for android firefox (#2190) +- Do not block on down track close with flush. (#2201) +- Separate publish and subscribe enabled codecs for finer grained control. (#2217) +- improve participant hidden (#2220) +- Reject migration if codec mismatch with published tracks (#2225) + ## [1.5.0] - 2023-10-15 ### Added diff --git a/version/version.go b/version/version.go index 41403002b..cbd85195e 100644 --- a/version/version.go +++ b/version/version.go @@ -14,4 +14,4 @@ package version -const Version = "1.5.0" +const Version = "1.5.1" From ca3884bebc4d0a6c07a17a6819c111519759c9b6 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 9 Nov 2023 16:01:41 -0800 Subject: [PATCH 07/18] Update go deps (#2223) Generated by renovateBot Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- go.mod | 16 ++++++++-------- go.sum | 32 ++++++++++++++++---------------- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/go.mod b/go.mod index 90df29949..735c7e9c1 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/gammazero/deque v0.2.1 github.com/gammazero/workerpool v1.1.3 github.com/google/wire v0.5.0 - github.com/gorilla/websocket v1.5.0 + github.com/gorilla/websocket v1.5.1 github.com/hashicorp/go-version v1.6.0 github.com/hashicorp/golang-lru/v2 v2.0.7 github.com/jxskiss/base62 v1.1.0 @@ -46,7 +46,7 @@ require ( github.com/urfave/cli/v2 v2.25.7 github.com/urfave/negroni/v3 v3.0.0 go.uber.org/atomic v1.11.0 - golang.org/x/exp v0.0.0-20231006140011-7918f672742d + golang.org/x/exp v0.0.0-20231108232855-2478ac86f678 golang.org/x/sync v0.5.0 google.golang.org/protobuf v1.31.0 gopkg.in/yaml.v3 v3.0.1 @@ -95,12 +95,12 @@ require ( github.com/zeebo/xxh3 v1.0.2 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.26.0 // indirect - golang.org/x/crypto v0.14.0 // indirect - golang.org/x/mod v0.13.0 // indirect - golang.org/x/net v0.17.0 // indirect - golang.org/x/sys v0.13.0 // indirect - golang.org/x/text v0.13.0 // indirect - golang.org/x/tools v0.14.0 // indirect + golang.org/x/crypto v0.15.0 // indirect + golang.org/x/mod v0.14.0 // indirect + golang.org/x/net v0.18.0 // indirect + golang.org/x/sys v0.14.0 // indirect + golang.org/x/text v0.14.0 // indirect + golang.org/x/tools v0.15.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405 // indirect google.golang.org/grpc v1.59.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect diff --git a/go.sum b/go.sum index 1407ca329..d442ef52c 100644 --- a/go.sum +++ b/go.sum @@ -76,8 +76,8 @@ github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/wire v0.5.0 h1:I7ELFeVBr3yfPIcc8+MWvrjk+3VjbcSzoXm3JVa+jD8= github.com/google/wire v0.5.0/go.mod h1:ngWDr9Qvq3yZA10YrxfyGELY/AFWGVpy9c1LTRi1EoU= -github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= -github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= +github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= github.com/hashicorp/go-hclog v0.9.2 h1:CG6TE5H9/JXsFWJCfoIVpKFIkFe6ysEuHirp4DxCsHI= @@ -292,15 +292,15 @@ golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I= golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= -golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= -golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= -golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= -golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= +golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA= +golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g= +golang.org/x/exp v0.0.0-20231108232855-2478ac86f678 h1:mchzmB1XO2pMaKFRqk/+MV3mgGG96aqaPXaMifQU47w= +golang.org/x/exp v0.0.0-20231108232855-2478ac86f678/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY= -golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= +golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -328,8 +328,8 @@ golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ= golang.org/x/net v0.13.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= -golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= -golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg= +golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -379,8 +379,8 @@ golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= +golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -400,16 +400,16 @@ golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190422233926-fe54fb35175b/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc= -golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg= +golang.org/x/tools v0.15.0 h1:zdAyfUGbYmuVokhzVmghFl2ZJh5QhcfebBgmVPFYA+8= +golang.org/x/tools v0.15.0/go.mod h1:hpksKq4dtpQWS1uQ61JkdqWM3LscIS6Slf+VVkm+wQk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= From 9e3c64117dd2ddfe5aaf84fa1d6e9af9f376b8ad Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 10 Nov 2023 07:43:11 -0800 Subject: [PATCH 08/18] Update module github.com/pion/webrtc/v3 to v3.2.22 (#2236) --- go.mod | 4 ++-- go.sum | 10 ++++------ 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/go.mod b/go.mod index 735c7e9c1..0aee74ef3 100644 --- a/go.mod +++ b/go.mod @@ -34,7 +34,7 @@ require ( github.com/pion/sdp/v3 v3.0.6 github.com/pion/transport/v2 v2.2.4 github.com/pion/turn/v2 v2.1.4 - github.com/pion/webrtc/v3 v3.2.21 + github.com/pion/webrtc/v3 v3.2.22 github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.17.0 github.com/redis/go-redis/v9 v9.3.0 @@ -84,7 +84,7 @@ require ( github.com/pion/logging v0.2.2 // indirect github.com/pion/mdns v0.0.8 // indirect github.com/pion/randutil v0.1.0 // indirect - github.com/pion/srtp/v2 v2.0.17 // indirect + github.com/pion/srtp/v2 v2.0.18 // indirect github.com/pion/stun v0.6.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 // indirect diff --git a/go.sum b/go.sum index d442ef52c..2a71d2204 100644 --- a/go.sum +++ b/go.sum @@ -184,7 +184,6 @@ github.com/pion/dtls/v2 v2.2.7 h1:cSUBsETxepsCSFSxC3mc/aDo14qQLMSL+O6IjG28yV8= github.com/pion/dtls/v2 v2.2.7/go.mod h1:8WiMkebSHFD0T+dIU+UeBaoV7kDhOW5oDCzZ7WZ/F9s= github.com/pion/ice/v2 v2.3.11 h1:rZjVmUwyT55cmN8ySMpL7rsS8KYsJERsrxJLLxpKhdw= github.com/pion/ice/v2 v2.3.11/go.mod h1:hPcLC3kxMa+JGRzMHqQzjoSj3xtE9F+eoncmXLlCL4E= -github.com/pion/interceptor v0.1.18/go.mod h1:tpvvF4cPM6NGxFA1DUMbhabzQBxdWMATDGEUYOR9x6I= github.com/pion/interceptor v0.1.25 h1:pwY9r7P6ToQ3+IF0bajN0xmk/fNw/suTgaTdlwTDmhc= github.com/pion/interceptor v0.1.25/go.mod h1:wkbPYAak5zKsfpVDYMtEfWEy8D4zL+rpxCxPImLOg3Y= github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY= @@ -196,7 +195,6 @@ github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TB github.com/pion/rtcp v1.2.10/go.mod h1:ztfEwXZNLGyF1oQDttz/ZKIBaeeg/oWbRYqzBM9TL1I= github.com/pion/rtcp v1.2.12 h1:bKWiX93XKgDZENEXCijvHRU/wRifm6JV5DGcH6twtSM= github.com/pion/rtcp v1.2.12/go.mod h1:sn6qjxvnwyAkkPzPULIbVqSKI5Dv54Rv7VG0kNxh9L4= -github.com/pion/rtp v1.8.1/go.mod h1:pBGHaFt/yW7bf1jjWAoUjpSNoDnw98KTMg+jWWvziqU= github.com/pion/rtp v1.8.2/go.mod h1:pBGHaFt/yW7bf1jjWAoUjpSNoDnw98KTMg+jWWvziqU= github.com/pion/rtp v1.8.3 h1:VEHxqzSVQxCkKDSHro5/4IUUG1ea+MFdqR2R3xSpNU8= github.com/pion/rtp v1.8.3/go.mod h1:pBGHaFt/yW7bf1jjWAoUjpSNoDnw98KTMg+jWWvziqU= @@ -206,8 +204,8 @@ github.com/pion/sctp v1.8.9 h1:TP5ZVxV5J7rz7uZmbyvnUvsn7EJ2x/5q9uhsTtXbI3g= github.com/pion/sctp v1.8.9/go.mod h1:cMLT45jqw3+jiJCrtHVwfQLnfR0MGZ4rgOJwUOIqLkI= github.com/pion/sdp/v3 v3.0.6 h1:WuDLhtuFUUVpTfus9ILC4HRyHsW6TdugjEX/QY9OiUw= github.com/pion/sdp/v3 v3.0.6/go.mod h1:iiFWFpQO8Fy3S5ldclBkpXqmWy02ns78NOKoLLL0YQw= -github.com/pion/srtp/v2 v2.0.17 h1:ECuOk+7uIpY6HUlTb0nXhfvu4REG2hjtC4ronYFCZE4= -github.com/pion/srtp/v2 v2.0.17/go.mod h1:y5WSHcJY4YfNB/5r7ca5YjHeIr1H3LM1rKArGGs8jMc= +github.com/pion/srtp/v2 v2.0.18 h1:vKpAXfawO9RtTRKZJbG4y0v1b11NZxQnxRl85kGuUlo= +github.com/pion/srtp/v2 v2.0.18/go.mod h1:0KJQjA99A6/a0DOVTu1PhDSw0CXF2jTkqOoMg3ODqdA= github.com/pion/stun v0.6.1 h1:8lp6YejULeHBF8NmV8e2787BogQhduZugh5PdhDyyN4= github.com/pion/stun v0.6.1/go.mod h1:/hO7APkX4hZKu/D0f2lHzNyvdkTGtIy3NDmLR7kSz/8= github.com/pion/transport v0.14.1 h1:XSM6olwW+o8J4SCmOBb/BpwZypkHeyM0PGFCxNQBr40= @@ -222,8 +220,8 @@ github.com/pion/transport/v3 v3.0.1/go.mod h1:UY7kiITrlMv7/IKgd5eTUcaahZx5oUN3l9 github.com/pion/turn/v2 v2.1.3/go.mod h1:huEpByKKHix2/b9kmTAM3YoX6MKP+/D//0ClgUYR2fY= github.com/pion/turn/v2 v2.1.4 h1:2xn8rduI5W6sCZQkEnIUDAkrBQNl2eYIBCHMZ3QMmP8= github.com/pion/turn/v2 v2.1.4/go.mod h1:huEpByKKHix2/b9kmTAM3YoX6MKP+/D//0ClgUYR2fY= -github.com/pion/webrtc/v3 v3.2.21 h1:c8fy5JcqJkAQBwwy3Sk9huQLTBUSqaggyRlv9Lnh2zY= -github.com/pion/webrtc/v3 v3.2.21/go.mod h1:vVURQTBOG5BpWKOJz3nlr23NfTDeyKVmubRNqzQp+Tg= +github.com/pion/webrtc/v3 v3.2.22 h1:Hno262T7+V56MgUO30O0ZirZmVSvbXtnau31SB0WSpc= +github.com/pion/webrtc/v3 v3.2.22/go.mod h1:1CaT2fcZzZ6VZA+O1i9yK2DU4EOcXVvSbWG9pr5jefs= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= From 5ec692bb03bb2f1d6b980db7c5ae4fd52efe2017 Mon Sep 17 00:00:00 2001 From: Paul Wells Date: Mon, 13 Nov 2023 01:11:04 -0800 Subject: [PATCH 09/18] update psrpc (#2239) * update psrpc * tidy --- go.mod | 6 +++--- go.sum | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index 0aee74ef3..6b590d2e9 100644 --- a/go.mod +++ b/go.mod @@ -19,7 +19,7 @@ require ( github.com/livekit/mageutil v0.0.0-20230125210925-54e8a70427c1 github.com/livekit/mediatransportutil v0.0.0-20231017082622-43f077b4e60e github.com/livekit/protocol v1.9.1-0.20231107185101-e230ee2d840e - github.com/livekit/psrpc v0.5.0 + github.com/livekit/psrpc v0.5.1 github.com/mackerelio/go-osstat v0.2.4 github.com/magefile/mage v1.15.0 github.com/maxbrunsfeld/counterfeiter/v6 v6.7.0 @@ -46,7 +46,7 @@ require ( github.com/urfave/cli/v2 v2.25.7 github.com/urfave/negroni/v3 v3.0.0 go.uber.org/atomic v1.11.0 - golang.org/x/exp v0.0.0-20231108232855-2478ac86f678 + golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa golang.org/x/sync v0.5.0 google.golang.org/protobuf v1.31.0 gopkg.in/yaml.v3 v3.0.1 @@ -101,7 +101,7 @@ require ( golang.org/x/sys v0.14.0 // indirect golang.org/x/text v0.14.0 // indirect golang.org/x/tools v0.15.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17 // indirect google.golang.org/grpc v1.59.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect ) diff --git a/go.sum b/go.sum index 2a71d2204..1d5a2db31 100644 --- a/go.sum +++ b/go.sum @@ -127,8 +127,8 @@ github.com/livekit/mediatransportutil v0.0.0-20231017082622-43f077b4e60e h1:yNeI github.com/livekit/mediatransportutil v0.0.0-20231017082622-43f077b4e60e/go.mod h1:+WIOYwiBMive5T81V8B2wdAc2zQNRjNQiJIcPxMTILY= github.com/livekit/protocol v1.9.1-0.20231107185101-e230ee2d840e h1:YShBpEjkEBY7yil2gjMWlkVkxs3OI58LIIYsBdb8aBU= github.com/livekit/protocol v1.9.1-0.20231107185101-e230ee2d840e/go.mod h1:l2WjlZWErS6vBlQaQyCGwWLt1aOx10XfQTsmvLjJWFQ= -github.com/livekit/psrpc v0.5.0 h1:g+yYNSs6Y1/vM7UlFkB2s/ARe2y3RKWZhX8ata5j+eo= -github.com/livekit/psrpc v0.5.0/go.mod h1:1XYH1LLoD/YbvBvt6xg2KQ/J3InLXSJK6PL/+DKmuAU= +github.com/livekit/psrpc v0.5.1 h1:ihN5uKIvbU69UsFS4HdYmou5GuK0Dt4hix4eOmRS7o8= +github.com/livekit/psrpc v0.5.1/go.mod h1:cQjxg1oCxYHhxxv6KJH1gSvdtCHQoRZCHgPdm5N8v2g= github.com/mackerelio/go-osstat v0.2.4 h1:qxGbdPkFo65PXOb/F/nhDKpF2nGmGaCFDLXoZjJTtUs= github.com/mackerelio/go-osstat v0.2.4/go.mod h1:Zy+qzGdZs3A9cuIqmgbJvwbmLQH9dJvtio5ZjJTbdlQ= github.com/magefile/mage v1.15.0 h1:BvGheCMAsG3bWUDbZ8AyXXpCNwU9u5CB6sM+HNb9HYg= @@ -292,8 +292,8 @@ golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIi golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA= golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g= -golang.org/x/exp v0.0.0-20231108232855-2478ac86f678 h1:mchzmB1XO2pMaKFRqk/+MV3mgGG96aqaPXaMifQU47w= -golang.org/x/exp v0.0.0-20231108232855-2478ac86f678/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE= +golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ= +golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= @@ -412,8 +412,8 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405 h1:AB/lmRny7e2pLhFEYIbl5qkDAUt2h0ZRO4wGPhZf+ik= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405/go.mod h1:67X1fPuzjcrkymZzZV1vvkFeTn2Rvc6lYF9MYFGCcwE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17 h1:Jyp0Hsi0bmHXG6k9eATXoYtjd6e2UzZ1SCn/wIupY14= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:oQ5rr10WTTMvP4A36n8JpR1OrO1BEiV4f78CneXZxkA= google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk= google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= From 702e562f9ff760cf786f8bb6eb792c9533c74f26 Mon Sep 17 00:00:00 2001 From: Sean DuBois Date: Tue, 14 Nov 2023 14:24:54 -0500 Subject: [PATCH 10/18] Add SIP Support (#2240) --- go.mod | 6 +- go.sum | 12 +-- pkg/config/config.go | 4 + pkg/service/errors.go | 4 + pkg/service/interfaces.go | 18 ++++ pkg/service/ioservice.go | 29 ++++++ pkg/service/redisstore.go | 130 ++++++++++++++++++++++++ pkg/service/server.go | 3 + pkg/service/sip.go | 207 ++++++++++++++++++++++++++++++++++++++ pkg/service/wire.go | 17 ++++ pkg/service/wire_gen.go | 24 ++++- 11 files changed, 443 insertions(+), 11 deletions(-) create mode 100644 pkg/service/sip.go diff --git a/go.mod b/go.mod index 6b590d2e9..79543f407 100644 --- a/go.mod +++ b/go.mod @@ -18,7 +18,7 @@ require ( github.com/jxskiss/base62 v1.1.0 github.com/livekit/mageutil v0.0.0-20230125210925-54e8a70427c1 github.com/livekit/mediatransportutil v0.0.0-20231017082622-43f077b4e60e - github.com/livekit/protocol v1.9.1-0.20231107185101-e230ee2d840e + github.com/livekit/protocol v1.9.1 github.com/livekit/psrpc v0.5.1 github.com/mackerelio/go-osstat v0.2.4 github.com/magefile/mage v1.15.0 @@ -60,14 +60,14 @@ require ( github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/eapache/channels v1.1.0 // indirect github.com/eapache/queue v1.1.0 // indirect - github.com/go-jose/go-jose/v3 v3.0.0 // indirect + github.com/go-jose/go-jose/v3 v3.0.1 // indirect github.com/go-logr/logr v1.3.0 // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/google/go-cmp v0.5.9 // indirect github.com/google/subcommands v1.2.0 // indirect github.com/google/uuid v1.3.1 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect - github.com/hashicorp/go-retryablehttp v0.7.4 // indirect + github.com/hashicorp/go-retryablehttp v0.7.5 // indirect github.com/hashicorp/golang-lru v0.5.4 // indirect github.com/josharian/native v1.1.0 // indirect github.com/klauspost/compress v1.17.2 // indirect diff --git a/go.sum b/go.sum index 1d5a2db31..3715a2edf 100644 --- a/go.sum +++ b/go.sum @@ -40,8 +40,8 @@ github.com/gammazero/deque v0.2.1 h1:qSdsbG6pgp6nL7A0+K/B7s12mcCY/5l5SIUpMOl+dC0 github.com/gammazero/deque v0.2.1/go.mod h1:LFroj8x4cMYCukHJDbxFCkT+r9AndaJnFMuZDV34tuU= github.com/gammazero/workerpool v1.1.3 h1:WixN4xzukFoN0XSeXF6puqEqFTl2mECI9S6W44HWy9Q= github.com/gammazero/workerpool v1.1.3/go.mod h1:wPjyBLDbyKnUn2XwwyD3EEwo9dHutia9/fwNmSHWACc= -github.com/go-jose/go-jose/v3 v3.0.0 h1:s6rrhirfEP/CGIoc6p+PZAeogN2SxKav6Wp7+dyMWVo= -github.com/go-jose/go-jose/v3 v3.0.0/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8= +github.com/go-jose/go-jose/v3 v3.0.1 h1:pWmKFVtt+Jl0vBZTIpz/eAKwsm6LkIxDVVbFHKkchhA= +github.com/go-jose/go-jose/v3 v3.0.1/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8= github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY= github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= @@ -82,8 +82,8 @@ github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9n github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= github.com/hashicorp/go-hclog v0.9.2 h1:CG6TE5H9/JXsFWJCfoIVpKFIkFe6ysEuHirp4DxCsHI= github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= -github.com/hashicorp/go-retryablehttp v0.7.4 h1:ZQgVdpTdAL7WpMIwLzCfbalOcSUdkDZnpUv3/+BxzFA= -github.com/hashicorp/go-retryablehttp v0.7.4/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8= +github.com/hashicorp/go-retryablehttp v0.7.5 h1:bJj+Pj19UZMIweq/iie+1u5YCdGrnxCT9yvm0e+Nd5M= +github.com/hashicorp/go-retryablehttp v0.7.5/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8= github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= @@ -125,8 +125,8 @@ github.com/livekit/mageutil v0.0.0-20230125210925-54e8a70427c1 h1:jm09419p0lqTkD github.com/livekit/mageutil v0.0.0-20230125210925-54e8a70427c1/go.mod h1:Rs3MhFwutWhGwmY1VQsygw28z5bWcnEYmS1OG9OxjOQ= github.com/livekit/mediatransportutil v0.0.0-20231017082622-43f077b4e60e h1:yNeIo7MSMUWgoLu7LkNKnBYnJBFPFH9Wq4S6h1kS44M= github.com/livekit/mediatransportutil v0.0.0-20231017082622-43f077b4e60e/go.mod h1:+WIOYwiBMive5T81V8B2wdAc2zQNRjNQiJIcPxMTILY= -github.com/livekit/protocol v1.9.1-0.20231107185101-e230ee2d840e h1:YShBpEjkEBY7yil2gjMWlkVkxs3OI58LIIYsBdb8aBU= -github.com/livekit/protocol v1.9.1-0.20231107185101-e230ee2d840e/go.mod h1:l2WjlZWErS6vBlQaQyCGwWLt1aOx10XfQTsmvLjJWFQ= +github.com/livekit/protocol v1.9.1 h1:RTycWwtGUoYLb+7WYosJ3IP8f0e8j4lLxC/osy/dQi0= +github.com/livekit/protocol v1.9.1/go.mod h1:kqDGmx+WZ6WMZ5V/T9lF8DgnuThAjetwjHq1nd7moSE= github.com/livekit/psrpc v0.5.1 h1:ihN5uKIvbU69UsFS4HdYmou5GuK0Dt4hix4eOmRS7o8= github.com/livekit/psrpc v0.5.1/go.mod h1:cQjxg1oCxYHhxxv6KJH1gSvdtCHQoRZCHgPdm5N8v2g= github.com/mackerelio/go-osstat v0.2.4 h1:qxGbdPkFo65PXOb/F/nhDKpF2nGmGaCFDLXoZjJTtUs= diff --git a/pkg/config/config.go b/pkg/config/config.go index a87caf55b..a0c882f14 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -66,6 +66,7 @@ type Config struct { Room RoomConfig `yaml:"room,omitempty"` TURN TURNConfig `yaml:"turn,omitempty"` Ingress IngressConfig `yaml:"ingress,omitempty"` + SIP SIPConfig `yaml:"sip,omitempty"` WebHook WebHookConfig `yaml:"webhook,omitempty"` NodeSelector NodeSelectorConfig `yaml:"node_selector,omitempty"` KeyFile string `yaml:"key_file,omitempty"` @@ -294,6 +295,9 @@ type IngressConfig struct { WHIPBaseURL string `yaml:"whip_base_url,omitempty"` } +type SIPConfig struct { +} + // not exposed to YAML type APIConfig struct { // amount of time to wait for API to execute, default 2s diff --git a/pkg/service/errors.go b/pkg/service/errors.go index 7c27d0dac..9a77f5fde 100644 --- a/pkg/service/errors.go +++ b/pkg/service/errors.go @@ -34,4 +34,8 @@ var ( ErrRemoteUnmuteNoteEnabled = psrpc.NewErrorf(psrpc.FailedPrecondition, "remote unmute not enabled") ErrTrackNotFound = psrpc.NewErrorf(psrpc.NotFound, "track is not found") ErrWebHookMissingAPIKey = psrpc.NewErrorf(psrpc.InvalidArgument, "api_key is required to use webhooks") + ErrSIPNotConnected = psrpc.NewErrorf(psrpc.Internal, "sip not connected (redis required)") + ErrSIPTrunkNotFound = psrpc.NewErrorf(psrpc.NotFound, "requested sip trunk does not exist") + ErrSIPDispatchRuleNotFound = psrpc.NewErrorf(psrpc.NotFound, "requested sip dispatch rule does not exist") + ErrSIPParticipantNotFound = psrpc.NewErrorf(psrpc.NotFound, "requested sip participant does not exist") ) diff --git a/pkg/service/interfaces.go b/pkg/service/interfaces.go index 6c32d7c5c..5dd0cb20b 100644 --- a/pkg/service/interfaces.go +++ b/pkg/service/interfaces.go @@ -76,3 +76,21 @@ type RoomAllocator interface { CreateRoom(ctx context.Context, req *livekit.CreateRoomRequest) (*livekit.Room, bool, error) ValidateCreateRoom(ctx context.Context, roomName livekit.RoomName) error } + +//counterfeiter:generate . SIPStore +type SIPStore interface { + StoreSIPTrunk(ctx context.Context, info *livekit.SIPTrunkInfo) error + LoadSIPTrunk(ctx context.Context, sipTrunkID string) (*livekit.SIPTrunkInfo, error) + ListSIPTrunk(ctx context.Context) ([]*livekit.SIPTrunkInfo, error) + DeleteSIPTrunk(ctx context.Context, info *livekit.SIPTrunkInfo) error + + StoreSIPDispatchRule(ctx context.Context, info *livekit.SIPDispatchRuleInfo) error + LoadSIPDispatchRule(ctx context.Context, sipDispatchRuleID string) (*livekit.SIPDispatchRuleInfo, error) + ListSIPDispatchRule(ctx context.Context) ([]*livekit.SIPDispatchRuleInfo, error) + DeleteSIPDispatchRule(ctx context.Context, info *livekit.SIPDispatchRuleInfo) error + + StoreSIPParticipant(ctx context.Context, info *livekit.SIPParticipantInfo) error + LoadSIPParticipant(ctx context.Context, sipParticipantID string) (*livekit.SIPParticipantInfo, error) + ListSIPParticipant(ctx context.Context) ([]*livekit.SIPParticipantInfo, error) + DeleteSIPParticipant(ctx context.Context, info *livekit.SIPParticipantInfo) error +} diff --git a/pkg/service/ioservice.go b/pkg/service/ioservice.go index b957e8b15..2d9070cca 100644 --- a/pkg/service/ioservice.go +++ b/pkg/service/ioservice.go @@ -17,6 +17,7 @@ package service import ( "context" "errors" + "fmt" "google.golang.org/protobuf/types/known/emptypb" @@ -32,6 +33,7 @@ type IOInfoService struct { es EgressStore is IngressStore + ss SIPStore telemetry telemetry.TelemetryService shutdown chan struct{} @@ -41,11 +43,13 @@ func NewIOInfoService( bus psrpc.MessageBus, es EgressStore, is IngressStore, + ss SIPStore, ts telemetry.TelemetryService, ) (*IOInfoService, error) { s := &IOInfoService{ es: es, is: is, + ss: ss, telemetry: ts, shutdown: make(chan struct{}), } @@ -211,6 +215,31 @@ func (s *IOInfoService) UpdateIngressState(ctx context.Context, req *rpc.UpdateI return &emptypb.Empty{}, nil } +func (s *IOInfoService) EvaluateSIPDispatchRules(ctx context.Context, req *rpc.EvaluateSIPDispatchRulesRequest) (*rpc.EvaluateSIPDispatchRulesResponse, error) { + dispatchRules, err := s.ss.ListSIPDispatchRule(ctx) + if err != nil { + return nil, err + } + + if len(dispatchRules) == 0 { + return nil, fmt.Errorf("No SIP Dispatch Rule Found") + } + + directDispatchRule := dispatchRules[0].Rule.GetDispatchRuleDirect() + if directDispatchRule == nil { + return nil, fmt.Errorf("No SIP Direct Dispatch Rule Found") + } + + return &rpc.EvaluateSIPDispatchRulesResponse{ + RoomName: directDispatchRule.RoomName, + ParticipantIdentity: req.CallingNumber, + }, nil +} + +func (s *IOInfoService) GetSIPTrunkAuthentication(ctx context.Context, req *rpc.GetSIPTrunkAuthenticationRequest) (*rpc.GetSIPTrunkAuthenticationResponse, error) { + return nil, nil +} + func (s *IOInfoService) Stop() { close(s.shutdown) diff --git a/pkg/service/redisstore.go b/pkg/service/redisstore.go index cc8c2e146..dfb4c3626 100644 --- a/pkg/service/redisstore.go +++ b/pkg/service/redisstore.go @@ -51,6 +51,10 @@ const ( IngressStatePrefix = "{ingress}_state:" RoomIngressPrefix = "room_{ingress}:" + SIPTrunkKey = "sip_trunk" + SIPDispatchRuleKey = "sip_dispatch_rule" + SIPParticipantKey = "sip_participant" + // RoomParticipantsPrefix is hash of participant_name => ParticipantInfo RoomParticipantsPrefix = "room_participants:" @@ -812,3 +816,129 @@ func (s *RedisStore) DeleteIngress(_ context.Context, info *livekit.IngressInfo) return nil } + +func (s *RedisStore) loadOne(ctx context.Context, key, id string, info proto.Message, notFoundErr error) error { + data, err := s.rc.HGet(s.ctx, key, id).Result() + switch err { + case nil: + return proto.Unmarshal([]byte(data), info) + case redis.Nil: + return notFoundErr + default: + return err + } +} + +func (s *RedisStore) loadMany(ctx context.Context, key string, onResult func() proto.Message) error { + data, err := s.rc.HGetAll(s.ctx, key).Result() + if err != nil { + if err == redis.Nil { + return nil + } + return err + } + + for _, d := range data { + if err = proto.Unmarshal([]byte(d), onResult()); err != nil { + return err + } + } + + return nil +} + +func (s *RedisStore) StoreSIPTrunk(ctx context.Context, info *livekit.SIPTrunkInfo) error { + data, err := proto.Marshal(info) + if err != nil { + return err + } + + return s.rc.HSet(s.ctx, SIPTrunkKey, info.SipTrunkId, data).Err() +} + +func (s *RedisStore) LoadSIPTrunk(ctx context.Context, sipTrunkId string) (*livekit.SIPTrunkInfo, error) { + info := &livekit.SIPTrunkInfo{} + if err := s.loadOne(ctx, SIPTrunkKey, sipTrunkId, info, ErrSIPTrunkNotFound); err != nil { + return nil, err + } + + return info, nil +} + +func (s *RedisStore) DeleteSIPTrunk(ctx context.Context, info *livekit.SIPTrunkInfo) error { + return s.rc.HDel(s.ctx, SIPTrunkKey, info.SipTrunkId).Err() +} + +func (s *RedisStore) ListSIPTrunk(ctx context.Context) (infos []*livekit.SIPTrunkInfo, err error) { + err = s.loadMany(ctx, SIPTrunkKey, func() proto.Message { + infos = append(infos, &livekit.SIPTrunkInfo{}) + return infos[len(infos)-1] + }) + + return infos, err +} + +func (s *RedisStore) StoreSIPDispatchRule(ctx context.Context, info *livekit.SIPDispatchRuleInfo) error { + data, err := proto.Marshal(info) + if err != nil { + return err + } + + return s.rc.HSet(s.ctx, SIPDispatchRuleKey, info.SipDispatchRuleId, data).Err() +} + +func (s *RedisStore) LoadSIPDispatchRule(ctx context.Context, sipDispatchRuleId string) (*livekit.SIPDispatchRuleInfo, error) { + info := &livekit.SIPDispatchRuleInfo{} + if err := s.loadOne(ctx, SIPDispatchRuleKey, sipDispatchRuleId, info, ErrSIPDispatchRuleNotFound); err != nil { + return nil, err + } + + return info, nil +} + +func (s *RedisStore) DeleteSIPDispatchRule(ctx context.Context, info *livekit.SIPDispatchRuleInfo) error { + return s.rc.HDel(s.ctx, SIPDispatchRuleKey, info.SipDispatchRuleId).Err() +} + +func (s *RedisStore) ListSIPDispatchRule(ctx context.Context) (infos []*livekit.SIPDispatchRuleInfo, err error) { + err = s.loadMany(ctx, SIPDispatchRuleKey, func() proto.Message { + infos = append(infos, &livekit.SIPDispatchRuleInfo{}) + return infos[len(infos)-1] + }) + + return infos, err +} + +func (s *RedisStore) StoreSIPParticipant(ctx context.Context, info *livekit.SIPParticipantInfo) error { + data, err := proto.Marshal(info) + if err != nil { + return err + } + + return s.rc.HSet(s.ctx, SIPParticipantKey, info.SipParticipantId, data).Err() +} +func (s *RedisStore) LoadSIPParticipant(ctx context.Context, sipParticipantId string) (*livekit.SIPParticipantInfo, error) { + info := &livekit.SIPParticipantInfo{} + if err := s.loadOne(ctx, SIPParticipantKey, sipParticipantId, info, ErrSIPParticipantNotFound); err != nil { + return nil, err + } + + return info, nil +} + +func (s *RedisStore) DeleteSIPParticipant(ctx context.Context, info *livekit.SIPParticipantInfo) error { + return s.rc.HDel(s.ctx, SIPParticipantKey, info.SipParticipantId).Err() +} + +func (s *RedisStore) ListSIPParticipant(ctx context.Context) (infos []*livekit.SIPParticipantInfo, err error) { + err = s.loadMany(ctx, SIPParticipantKey, func() proto.Message { + infos = append(infos, &livekit.SIPParticipantInfo{}) + return infos[len(infos)-1] + }) + + return infos, err +} + +func (s *RedisStore) SendSIPParticipantDTMF(ctx context.Context, info *livekit.SendSIPParticipantDTMFRequest) (*livekit.SIPParticipantDTMFInfo, error) { + return nil, fmt.Errorf("TODO") +} diff --git a/pkg/service/server.go b/pkg/service/server.go index f7ade50d2..f66d848b5 100644 --- a/pkg/service/server.go +++ b/pkg/service/server.go @@ -65,6 +65,7 @@ func NewLivekitServer(conf *config.Config, roomService livekit.RoomService, egressService *EgressService, ingressService *IngressService, + sipService *SIPService, ioService *IOInfoService, rtcService *RTCService, agentService *AgentService, @@ -116,6 +117,7 @@ func NewLivekitServer(conf *config.Config, ), )) ingressServer := livekit.NewIngressServer(ingressService, twirpLoggingHook) + sipServer := livekit.NewSIPServer(sipService, twirpLoggingHook) mux := http.NewServeMux() if conf.Development { @@ -127,6 +129,7 @@ func NewLivekitServer(conf *config.Config, mux.Handle(roomServer.PathPrefix(), roomServer) mux.Handle(egressServer.PathPrefix(), egressServer) mux.Handle(ingressServer.PathPrefix(), ingressServer) + mux.Handle(sipServer.PathPrefix(), sipServer) mux.Handle("/rtc", rtcService) mux.Handle("/agent", agentService) mux.HandleFunc("/rtc/validate", rtcService.Validate) diff --git a/pkg/service/sip.go b/pkg/service/sip.go new file mode 100644 index 000000000..4a7fe411a --- /dev/null +++ b/pkg/service/sip.go @@ -0,0 +1,207 @@ +// Copyright 2023 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package service + +import ( + "context" + "fmt" + + "github.com/livekit/livekit-server/pkg/config" + "github.com/livekit/livekit-server/pkg/telemetry" + "github.com/livekit/protocol/livekit" + "github.com/livekit/protocol/rpc" + "github.com/livekit/protocol/utils" + "github.com/livekit/psrpc" +) + +type SIPService struct { + conf *config.SIPConfig + nodeID livekit.NodeID + bus psrpc.MessageBus + psrpcClient rpc.SIPClient + store SIPStore + roomService livekit.RoomService +} + +func NewSIPService( + conf *config.SIPConfig, + nodeID livekit.NodeID, + bus psrpc.MessageBus, + psrpcClient rpc.SIPClient, + store SIPStore, + rs livekit.RoomService, + ts telemetry.TelemetryService, +) *SIPService { + return &SIPService{ + conf: conf, + nodeID: nodeID, + bus: bus, + psrpcClient: psrpcClient, + store: store, + roomService: rs, + } +} + +func (s *SIPService) CreateSIPTrunk(ctx context.Context, req *livekit.CreateSIPTrunkRequest) (*livekit.SIPTrunkInfo, error) { + if s.store == nil { + return nil, ErrSIPNotConnected + } + + info := &livekit.SIPTrunkInfo{ + SipTrunkId: utils.NewGuid(utils.SIPTrunkPrefix), + InboundAddresses: req.InboundAddresses, + OutboundAddress: req.OutboundAddress, + OutboundNumber: req.OutboundNumber, + InboundNumbersRegex: req.InboundNumbersRegex, + Username: req.Username, + Password: req.Password, + } + + if err := s.store.StoreSIPTrunk(ctx, info); err != nil { + return nil, err + } + return info, nil +} + +func (s *SIPService) ListSIPTrunk(ctx context.Context, req *livekit.ListSIPTrunkRequest) (*livekit.ListSIPTrunkResponse, error) { + if s.store == nil { + return nil, ErrSIPNotConnected + } + + trunks, err := s.store.ListSIPTrunk(ctx) + if err != nil { + return nil, err + } + + return &livekit.ListSIPTrunkResponse{Items: trunks}, nil +} + +func (s *SIPService) DeleteSIPTrunk(ctx context.Context, req *livekit.DeleteSIPTrunkRequest) (*livekit.SIPTrunkInfo, error) { + if s.store == nil { + return nil, ErrSIPNotConnected + } + + info, err := s.store.LoadSIPTrunk(ctx, req.SipTrunkId) + if err != nil { + return nil, err + } + + if err = s.store.DeleteSIPTrunk(ctx, info); err != nil { + return nil, err + } + + return info, nil +} + +func (s *SIPService) CreateSIPDispatchRule(ctx context.Context, req *livekit.CreateSIPDispatchRuleRequest) (*livekit.SIPDispatchRuleInfo, error) { + if s.store == nil { + return nil, ErrSIPNotConnected + } + + info := &livekit.SIPDispatchRuleInfo{ + SipDispatchRuleId: utils.NewGuid(utils.SIPDispatchRulePrefix), + Rule: req.Rule, + TrunkIds: req.TrunkIds, + HidePhoneNumber: req.HidePhoneNumber, + } + + if err := s.store.StoreSIPDispatchRule(ctx, info); err != nil { + return nil, err + } + return info, nil +} + +func (s *SIPService) ListSIPDispatchRule(ctx context.Context, req *livekit.ListSIPDispatchRuleRequest) (*livekit.ListSIPDispatchRuleResponse, error) { + if s.store == nil { + return nil, ErrSIPNotConnected + } + + rules, err := s.store.ListSIPDispatchRule(ctx) + if err != nil { + return nil, err + } + + return &livekit.ListSIPDispatchRuleResponse{Items: rules}, nil +} + +func (s *SIPService) DeleteSIPDispatchRule(ctx context.Context, req *livekit.DeleteSIPDispatchRuleRequest) (*livekit.SIPDispatchRuleInfo, error) { + if s.store == nil { + return nil, ErrSIPNotConnected + } + + info, err := s.store.LoadSIPDispatchRule(ctx, req.SipDispatchRuleId) + if err != nil { + return nil, err + } + + if err = s.store.DeleteSIPDispatchRule(ctx, info); err != nil { + return nil, err + } + + return info, nil +} + +func (s *SIPService) CreateSIPParticipant(ctx context.Context, req *livekit.CreateSIPParticipantRequest) (*livekit.SIPParticipantInfo, error) { + if s.store == nil { + return nil, ErrSIPNotConnected + } + + info := &livekit.SIPParticipantInfo{ + SipParticipantId: utils.NewGuid(utils.SIPParticipantPrefix), + } + + if err := s.store.StoreSIPParticipant(ctx, info); err != nil { + return nil, err + } + return info, nil +} + +func (s *SIPService) ListSIPParticipant(ctx context.Context, req *livekit.ListSIPParticipantRequest) (*livekit.ListSIPParticipantResponse, error) { + if s.store == nil { + return nil, ErrSIPNotConnected + } + + participants, err := s.store.ListSIPParticipant(ctx) + if err != nil { + return nil, err + } + + return &livekit.ListSIPParticipantResponse{Items: participants}, nil +} + +func (s *SIPService) DeleteSIPParticipant(ctx context.Context, req *livekit.DeleteSIPParticipantRequest) (*livekit.SIPParticipantInfo, error) { + if s.store == nil { + return nil, ErrSIPNotConnected + } + + info, err := s.store.LoadSIPParticipant(ctx, req.SipParticipantId) + if err != nil { + return nil, err + } + + if err = s.store.DeleteSIPParticipant(ctx, info); err != nil { + return nil, err + } + + return info, nil +} + +func (s *SIPService) SendSIPParticipantDTMF(ctx context.Context, req *livekit.SendSIPParticipantDTMFRequest) (*livekit.SIPParticipantDTMFInfo, error) { + if s.store == nil { + return nil, ErrSIPNotConnected + } + + return nil, fmt.Errorf("TODO") +} diff --git a/pkg/service/wire.go b/pkg/service/wire.go index afca2631e..93e3784b0 100644 --- a/pkg/service/wire.go +++ b/pkg/service/wire.go @@ -70,6 +70,10 @@ func InitializeServer(conf *config.Config, currentNode routing.LocalNode) (*Live getIngressStore, getIngressConfig, NewIngressService, + rpc.NewSIPClient, + getSIPStore, + getSIPConfig, + NewSIPService, NewRoomAllocator, NewRoomService, NewRTCService, @@ -195,6 +199,19 @@ func getIngressConfig(conf *config.Config) *config.IngressConfig { return &conf.Ingress } +func getSIPStore(s ObjectStore) SIPStore { + switch store := s.(type) { + case *RedisStore: + return store + default: + return nil + } +} + +func getSIPConfig(conf *config.Config) *config.SIPConfig { + return &conf.SIP +} + func createClientConfiguration() clientconfiguration.ClientConfigurationManager { return clientconfiguration.NewStaticClientConfigurationManager(clientconfiguration.StaticConfigurations) } diff --git a/pkg/service/wire_gen.go b/pkg/service/wire_gen.go index 3a6470b88..0f0eb6e87 100644 --- a/pkg/service/wire_gen.go +++ b/pkg/service/wire_gen.go @@ -66,6 +66,7 @@ func InitializeServer(conf *config.Config, currentNode routing.LocalNode) (*Live } egressStore := getEgressStore(objectStore) ingressStore := getIngressStore(objectStore) + sipStore := getSIPStore(objectStore) keyProvider, err := createKeyProvider(conf) if err != nil { return nil, err @@ -76,7 +77,7 @@ func InitializeServer(conf *config.Config, currentNode routing.LocalNode) (*Live } analyticsService := telemetry.NewAnalyticsService(conf, currentNode) telemetryService := telemetry.NewTelemetryService(queuedNotifier, analyticsService) - ioInfoService, err := NewIOInfoService(messageBus, egressStore, ingressStore, telemetryService) + ioInfoService, err := NewIOInfoService(messageBus, egressStore, ingressStore, sipStore, telemetryService) if err != nil { return nil, err } @@ -102,6 +103,12 @@ func InitializeServer(conf *config.Config, currentNode routing.LocalNode) (*Live return nil, err } ingressService := NewIngressService(ingressConfig, nodeID, messageBus, ingressClient, ingressStore, roomService, telemetryService) + sipConfig := getSIPConfig(conf) + sipClient, err := rpc.NewSIPClient(messageBus) + if err != nil { + return nil, err + } + sipService := NewSIPService(sipConfig, nodeID, messageBus, sipClient, sipStore, roomService, telemetryService) rtcService := NewRTCService(conf, roomAllocator, objectStore, router, currentNode, agentClient, telemetryService) agentService, err := NewAgentService(messageBus) if err != nil { @@ -123,7 +130,7 @@ func InitializeServer(conf *config.Config, currentNode routing.LocalNode) (*Live if err != nil { return nil, err } - livekitServer, err := NewLivekitServer(conf, roomService, egressService, ingressService, ioInfoService, rtcService, agentService, keyProvider, router, roomManager, signalServer, server, currentNode) + livekitServer, err := NewLivekitServer(conf, roomService, egressService, ingressService, sipService, ioInfoService, rtcService, agentService, keyProvider, router, roomManager, signalServer, server, currentNode) if err != nil { return nil, err } @@ -237,6 +244,19 @@ func getIngressConfig(conf *config.Config) *config.IngressConfig { return &conf.Ingress } +func getSIPStore(s ObjectStore) SIPStore { + switch store := s.(type) { + case *RedisStore: + return store + default: + return nil + } +} + +func getSIPConfig(conf *config.Config) *config.SIPConfig { + return &conf.SIP +} + func createClientConfiguration() clientconfiguration.ClientConfigurationManager { return clientconfiguration.NewStaticClientConfigurationManager(clientconfiguration.StaticConfigurations) } From 49ca1066cd82575039609ed34a2c030a2ee3beec Mon Sep 17 00:00:00 2001 From: Raja Subramanian Date: Wed, 15 Nov 2023 12:57:03 +0530 Subject: [PATCH 11/18] Reduce logging (#2243) * Reduce logging 1. Do not print rtp stats if nil. Means that some subscribed tracks may not have any logs (very short subscriptions which end before any packet is sent). 2. Log ICE candidates only at the end, not when ICE connects. That logs the selected ICE candidate pair. 3. Log ICE candidates only if not empty. * Update some deps --- go.mod | 4 ++-- go.sum | 7 ++++--- pkg/rtc/transport.go | 20 +++++++++++--------- pkg/sfu/downtrack.go | 5 ++++- 4 files changed, 21 insertions(+), 15 deletions(-) diff --git a/go.mod b/go.mod index 79543f407..2654fba97 100644 --- a/go.mod +++ b/go.mod @@ -25,7 +25,7 @@ require ( github.com/maxbrunsfeld/counterfeiter/v6 v6.7.0 github.com/mitchellh/go-homedir v1.1.0 github.com/olekukonko/tablewriter v0.0.5 - github.com/pion/dtls/v2 v2.2.7 + github.com/pion/dtls/v2 v2.2.8 github.com/pion/ice/v2 v2.3.11 github.com/pion/interceptor v0.1.25 github.com/pion/rtcp v1.2.12 @@ -71,7 +71,7 @@ require ( github.com/hashicorp/golang-lru v0.5.4 // indirect github.com/josharian/native v1.1.0 // indirect github.com/klauspost/compress v1.17.2 // indirect - github.com/klauspost/cpuid/v2 v2.2.5 // indirect + github.com/klauspost/cpuid/v2 v2.2.6 // indirect github.com/lithammer/shortuuid/v4 v4.0.0 // indirect github.com/mattn/go-runewidth v0.0.9 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect diff --git a/go.sum b/go.sum index 3715a2edf..2eb764325 100644 --- a/go.sum +++ b/go.sum @@ -109,8 +109,8 @@ github.com/jxskiss/base62 v1.1.0 h1:A5zbF8v8WXx2xixnAKD2w+abC+sIzYJX+nxmhA6HWFw= github.com/jxskiss/base62 v1.1.0/go.mod h1:HhWAlUXvxKThfOlZbcuFzsqwtF5TcqS9ru3y5GfjWAc= github.com/klauspost/compress v1.17.2 h1:RlWWUY/Dr4fL8qk9YG7DTZ7PDgME2V4csBXA8L/ixi4= github.com/klauspost/compress v1.17.2/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= -github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= -github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc= +github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= @@ -180,8 +180,9 @@ github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAl github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= github.com/pion/datachannel v1.5.5 h1:10ef4kwdjije+M9d7Xm9im2Y3O6A6ccQb0zcqZcJew8= github.com/pion/datachannel v1.5.5/go.mod h1:iMz+lECmfdCMqFRhXhcA/219B0SQlbpoR2V118yimL0= -github.com/pion/dtls/v2 v2.2.7 h1:cSUBsETxepsCSFSxC3mc/aDo14qQLMSL+O6IjG28yV8= github.com/pion/dtls/v2 v2.2.7/go.mod h1:8WiMkebSHFD0T+dIU+UeBaoV7kDhOW5oDCzZ7WZ/F9s= +github.com/pion/dtls/v2 v2.2.8 h1:BUroldfiIbV9jSnC6cKOMnyiORRWrWWpV11JUyEu5OA= +github.com/pion/dtls/v2 v2.2.8/go.mod h1:8WiMkebSHFD0T+dIU+UeBaoV7kDhOW5oDCzZ7WZ/F9s= github.com/pion/ice/v2 v2.3.11 h1:rZjVmUwyT55cmN8ySMpL7rsS8KYsJERsrxJLLxpKhdw= github.com/pion/ice/v2 v2.3.11/go.mod h1:hPcLC3kxMa+JGRzMHqQzjoSj3xtE9F+eoncmXLlCL4E= github.com/pion/interceptor v0.1.25 h1:pwY9r7P6ToQ3+IF0bajN0xmk/fNw/suTgaTdlwTDmhc= diff --git a/pkg/rtc/transport.go b/pkg/rtc/transport.go index ca22ffeec..16e454e78 100644 --- a/pkg/rtc/transport.go +++ b/pkg/rtc/transport.go @@ -657,12 +657,10 @@ func (t *PCTransport) onPeerConnectionStateChange(state webrtc.PeerConnectionSta } t.maybeNotifyFullyEstablished() - t.logICECandidates() } case webrtc.PeerConnectionStateFailed: t.params.Logger.Infow("peer connection failed") t.clearConnTimer() - t.logICECandidates() t.handleConnectionFailed(false) } } @@ -1606,13 +1604,17 @@ func (t *PCTransport) handleRemoteICECandidate(e *event) error { } func (t *PCTransport) handleLogICECandidates(e *event) error { - t.params.Logger.Infow( - "ice candidates", - "lc", t.allowedLocalCandidates.Get(), - "rc", t.allowedRemoteCandidates.Get(), - "lc (filtered)", t.filteredLocalCandidates.Get(), - "rc (filtered)", t.filteredRemoteCandidates.Get(), - ) + lc := t.allowedLocalCandidates.Get() + rc := t.allowedRemoteCandidates.Get() + if len(lc) != 0 || len(rc) != 0 { + t.params.Logger.Infow( + "ice candidates", + "lc", lc, + "rc", rc, + "lc (filtered)", t.filteredLocalCandidates.Get(), + "rc (filtered)", t.filteredRemoteCandidates.Get(), + ) + } return nil } diff --git a/pkg/sfu/downtrack.go b/pkg/sfu/downtrack.go index 07d7f45fa..c15e68dde 100644 --- a/pkg/sfu/downtrack.go +++ b/pkg/sfu/downtrack.go @@ -984,7 +984,10 @@ func (d *DownTrack) CloseWithFlush(flush bool) { d.bindLock.Unlock() d.connectionStats.Close() d.rtpStats.Stop() - d.params.Logger.Infow("rtp stats", "direction", "downstream", "mime", d.mime, "ssrc", d.ssrc, "stats", d.rtpStats.ToString()) + rtpStats := d.rtpStats.ToString() + if rtpStats != "" { + d.params.Logger.Infow("rtp stats", "direction", "downstream", "mime", d.mime, "ssrc", d.ssrc, "stats", rtpStats) + } d.maxLayerNotifierChMu.Lock() d.maxLayerNotifierChClosed = true From c6e467e903c3d8dffad506c2e4c6bf220d78fcc0 Mon Sep 17 00:00:00 2001 From: Denys Smirnov Date: Wed, 15 Nov 2023 16:35:05 +0200 Subject: [PATCH 12/18] Initial SIP Dispatch Rules implementation (#2241) --- pkg/service/ioservice.go | 25 +- pkg/service/ioservice_sip.go | 329 +++++++++++++++++++++++++ pkg/service/ioservice_sip_test.go | 394 ++++++++++++++++++++++++++++++ 3 files changed, 725 insertions(+), 23 deletions(-) create mode 100644 pkg/service/ioservice_sip.go create mode 100644 pkg/service/ioservice_sip_test.go diff --git a/pkg/service/ioservice.go b/pkg/service/ioservice.go index 2d9070cca..0149e7af6 100644 --- a/pkg/service/ioservice.go +++ b/pkg/service/ioservice.go @@ -17,15 +17,15 @@ package service import ( "context" "errors" - "fmt" "google.golang.org/protobuf/types/known/emptypb" - "github.com/livekit/livekit-server/pkg/telemetry" "github.com/livekit/protocol/livekit" "github.com/livekit/protocol/logger" "github.com/livekit/protocol/rpc" "github.com/livekit/psrpc" + + "github.com/livekit/livekit-server/pkg/telemetry" ) type IOInfoService struct { @@ -215,27 +215,6 @@ func (s *IOInfoService) UpdateIngressState(ctx context.Context, req *rpc.UpdateI return &emptypb.Empty{}, nil } -func (s *IOInfoService) EvaluateSIPDispatchRules(ctx context.Context, req *rpc.EvaluateSIPDispatchRulesRequest) (*rpc.EvaluateSIPDispatchRulesResponse, error) { - dispatchRules, err := s.ss.ListSIPDispatchRule(ctx) - if err != nil { - return nil, err - } - - if len(dispatchRules) == 0 { - return nil, fmt.Errorf("No SIP Dispatch Rule Found") - } - - directDispatchRule := dispatchRules[0].Rule.GetDispatchRuleDirect() - if directDispatchRule == nil { - return nil, fmt.Errorf("No SIP Direct Dispatch Rule Found") - } - - return &rpc.EvaluateSIPDispatchRulesResponse{ - RoomName: directDispatchRule.RoomName, - ParticipantIdentity: req.CallingNumber, - }, nil -} - func (s *IOInfoService) GetSIPTrunkAuthentication(ctx context.Context, req *rpc.GetSIPTrunkAuthenticationRequest) (*rpc.GetSIPTrunkAuthenticationResponse, error) { return nil, nil } diff --git a/pkg/service/ioservice_sip.go b/pkg/service/ioservice_sip.go new file mode 100644 index 000000000..715dea823 --- /dev/null +++ b/pkg/service/ioservice_sip.go @@ -0,0 +1,329 @@ +package service + +import ( + "context" + "fmt" + "math" + "regexp" + "sort" + + "github.com/livekit/protocol/livekit" + "github.com/livekit/protocol/logger" + "github.com/livekit/protocol/rpc" +) + +// sipRulePriority returns sorting priority for dispatch rules. Lower value means higher priority. +func sipRulePriority(info *livekit.SIPDispatchRuleInfo) int32 { + // In all these cases, prefer pin-protected rules. + // Thus, the order will be the following: + // - 0: Direct or Pin (both pin-protected) + // - 1: Individual (pin-protected) + // - 100: Direct (open) + // - 101: Individual (open) + const ( + last = math.MaxInt32 + ) + // TODO: Maybe allow setting specific priorities for dispatch rules? + switch rule := info.GetRule().GetRule().(type) { + default: + return last + case *livekit.SIPDispatchRule_DispatchRuleDirect: + if rule.DispatchRuleDirect.GetPin() != "" { + return 0 + } + return 100 + case *livekit.SIPDispatchRule_DispatchRulePin: + // TODO: If we assume that Pin is optional, this rule type is very similar to Direct. Could remove it? + return 0 + case *livekit.SIPDispatchRule_DispatchRuleIndividual: + if rule.DispatchRuleIndividual.GetPin() != "" { + return 1 + } + return 101 + } +} + +// sipSortRules predictably sorts dispatch rules by priority (first one is highest). +func sipSortRules(rules []*livekit.SIPDispatchRuleInfo) { + sort.Slice(rules, func(i, j int) bool { + p1, p2 := sipRulePriority(rules[i]), sipRulePriority(rules[j]) + if p1 < p2 { + return true + } else if p1 > p2 { + return false + } + // For predictable sorting order. + room1, _, _ := sipGetPinAndRoom(rules[i]) + room2, _, _ := sipGetPinAndRoom(rules[j]) + return room1 < room2 + }) +} + +// sipSelectDispatch takes a list of dispatch rules, and takes the decision which one should be selected. +// It returns an error if there are conflicting rules. Returns nil if no rules match. +func sipSelectDispatch(rules []*livekit.SIPDispatchRuleInfo, req *rpc.EvaluateSIPDispatchRulesRequest) (*livekit.SIPDispatchRuleInfo, error) { + if len(rules) == 0 { + return nil, nil + } + // Sorting will do the selection for us. We already filtered out irrelevant ones in matchSIPDispatchRule. + sipSortRules(rules) + byPin := make(map[string]*livekit.SIPDispatchRuleInfo) + var ( + pinRule *livekit.SIPDispatchRuleInfo + openRule *livekit.SIPDispatchRuleInfo + ) + openCnt := 0 + for _, r := range rules { + _, pin, err := sipGetPinAndRoom(r) + if err != nil { + return nil, err + } + if pin == "" { + openRule = r // last one + openCnt++ + } else if r2 := byPin[pin]; r2 != nil { + return nil, fmt.Errorf("Conflicting SIP Dispatch Rules: Same PIN for %q and %q", + r.SipDispatchRuleId, r2.SipDispatchRuleId) + } else { + byPin[pin] = r + // Pick the first one with a Pin. If Pin was provided in the request, we already filtered the right rules. + // If not, this rule will just be used to send RequestPin=true flag. + if pinRule == nil { + pinRule = r + } + } + } + if req.GetPin() != "" { + // If it's still nil that's fine. We will report "no rules matched" later. + return pinRule, nil + } + if pinRule != nil { + return pinRule, nil + } + if openCnt > 1 { + return nil, fmt.Errorf("Conflicting SIP Dispatch Rules: Matched %d open rules for %q", openCnt, req.CallingNumber) + } + return openRule, nil +} + +// sipGetPinAndRoom returns a room name/prefix and the pin for a dispatch rule. Just a convenience wrapper. +func sipGetPinAndRoom(info *livekit.SIPDispatchRuleInfo) (room, pin string, err error) { + // TODO: Could probably add methods on SIPDispatchRuleInfo struct instead. + switch rule := info.GetRule().GetRule().(type) { + default: + return "", "", fmt.Errorf("Unsupported SIP Dispatch Rule: %T", rule) + case *livekit.SIPDispatchRule_DispatchRuleDirect: + pin = rule.DispatchRuleDirect.GetPin() + room = rule.DispatchRuleDirect.GetRoomName() + case *livekit.SIPDispatchRule_DispatchRulePin: + pin = rule.DispatchRulePin.GetPin() + room = rule.DispatchRulePin.GetRoomName() + case *livekit.SIPDispatchRule_DispatchRuleIndividual: + pin = rule.DispatchRuleIndividual.GetPin() + room = rule.DispatchRuleIndividual.GetRoomPrefix() + } + return room, pin, nil +} + +// sipMatchTrunk finds a SIP Trunk definition matching the request. +// Returns nil if no rules matched or an error if there are conflicting definitions. +func sipMatchTrunk(trunks []*livekit.SIPTrunkInfo, req *rpc.EvaluateSIPDispatchRulesRequest) (*livekit.SIPTrunkInfo, error) { + var ( + selectedTrunk *livekit.SIPTrunkInfo + defaultTrunk *livekit.SIPTrunkInfo + defaultTrunkCnt int // to error in case there are multiple ones + ) + for _, tr := range trunks { + // Do not consider it if regexp doesn't match. + matches := len(tr.InboundNumbersRegex) == 0 + for _, reStr := range tr.InboundNumbersRegex { + // TODO: we should cache it + re, err := regexp.Compile(reStr) + if err != nil { + logger.Errorw("cannot parse SIP trunk regexp", err, "trunkID", tr.SipTrunkId) + continue + } + if re.MatchString(req.CallingNumber) { + matches = true + break + } + } + if !matches { + continue + } + if tr.OutboundNumber == "" { + // Default/wildcard trunk. + defaultTrunk = tr + defaultTrunkCnt++ + } else if tr.OutboundNumber == req.CalledNumber { + // Trunk specific to the number. + if selectedTrunk != nil { + return nil, fmt.Errorf("Multiple SIP Trunks matched for %q", req.CalledNumber) + } + selectedTrunk = tr + // Keep searching! We want to know if there are any conflicting Trunk definitions. + } + } + if selectedTrunk != nil { + return selectedTrunk, nil + } + if defaultTrunkCnt > 1 { + return nil, fmt.Errorf("Multiple default SIP Trunks matched for %q", req.CalledNumber) + } + // Could still be nil here. + return defaultTrunk, nil +} + +// sipMatchDispatchRule finds the best dispatch rule matching the request parameters. Returns an error if no rule matched. +// Trunk parameter can be nil, in which case only wildcard dispatch rules will be effective (ones without Trunk IDs). +func sipMatchDispatchRule(trunk *livekit.SIPTrunkInfo, rules []*livekit.SIPDispatchRuleInfo, req *rpc.EvaluateSIPDispatchRulesRequest) (*livekit.SIPDispatchRuleInfo, error) { + // Trunk can still be nil here in case none matched or were defined. + // This is still fine, but only in case we'll match exactly one wildcard dispatch rule. + if len(rules) == 0 { + return nil, fmt.Errorf("No SIP Dispatch Rules defined") + } + // We split the matched dispatch rules into two sets: specific and default (aka wildcard). + // First, attempt to match any of the specific rules, where we did match the Trunk ID. + // If nothing matches there - fallback to default/wildcard rules, where no Trunk IDs were mentioned. + var ( + specificRules []*livekit.SIPDispatchRuleInfo + defaultRules []*livekit.SIPDispatchRuleInfo + ) + // TODO: Apart from Pin, it would be nice to have a NoPin flag. + // The way it would work is that we will first list the rules and figure out if at least one has a Pin required. + // If it does, we will immediately respond with RequestPin=true. Now, on the SIP bridge side, we will run + // audio prompt asking for a Pin. The user will have an options to skip the pin (e.g. press #) and only try + // to match no-ping rooms. This will be very useful if only 1 number is available and has to route to both + // private and public rooms. + noPin := false + sentPin := req.GetPin() + for _, info := range rules { + _, rulePin, err := sipGetPinAndRoom(info) + if err != nil { + logger.Errorw("Invalid SIP Dispatch Rule", err, "dispatchRuleID", info.SipDispatchRuleId) + continue + } + // Filter heavily on the Pin, so that only relevant rules remain. + if noPin { + if rulePin != "" { + // Skip pin-protected rules if no pin mode requested. + continue + } + } else if sentPin != "" { + if rulePin == "" { + // Pin already sent, skip non-pin-protected rules. + continue + } + if sentPin != rulePin { + // Pin doesn't match. Don't return an error here, just wait for other rule to match (or none at all). + // Note that we will NOT match non-pin-protected rules, thus it will not fallback to open rules. + continue + } + } + if len(info.TrunkIds) == 0 { + // Default/wildcard dispatch rule. + defaultRules = append(defaultRules, info) + continue + } + // Specific dispatch rules. Require a Trunk associated with the number. + if trunk == nil { + continue + } + matches := false + for _, id := range info.TrunkIds { + if id == trunk.SipTrunkId { + matches = true + break + } + } + if !matches { + continue + } + specificRules = append(specificRules, info) + } + best, err := sipSelectDispatch(specificRules, req) + if err != nil { + return nil, err + } else if best != nil { + return best, nil + } + best, err = sipSelectDispatch(defaultRules, req) + if err != nil { + return nil, err + } else if best != nil { + return best, nil + } + if trunk == nil { + return nil, fmt.Errorf("No SIP Trunk or Dispatch Rules matched for %q", req.CalledNumber) + } + return nil, fmt.Errorf("No SIP Dispatch Rules matched for %q", req.CalledNumber) +} + +// matchSIPTrunk finds a SIP Trunk definition matching the request. +// Returns nil if no rules matched or an error if there are conflicting definitions. +func (s *IOInfoService) matchSIPTrunk(ctx context.Context, req *rpc.EvaluateSIPDispatchRulesRequest) (*livekit.SIPTrunkInfo, error) { + trunks, err := s.ss.ListSIPTrunk(ctx) + if err != nil { + return nil, err + } + return sipMatchTrunk(trunks, req) +} + +// matchSIPDispatchRule finds the best dispatch rule matching the request parameters. Returns an error if no rule matched. +// Trunk parameter can be nil, in which case only wildcard dispatch rules will be effective (ones without Trunk IDs). +func (s *IOInfoService) matchSIPDispatchRule(ctx context.Context, trunk *livekit.SIPTrunkInfo, req *rpc.EvaluateSIPDispatchRulesRequest) (*livekit.SIPDispatchRuleInfo, error) { + // Trunk can still be nil here in case none matched or were defined. + // This is still fine, but only in case we'll match exactly one wildcard dispatch rule. + rules, err := s.ss.ListSIPDispatchRule(ctx) + if err != nil { + return nil, err + } + return sipMatchDispatchRule(trunk, rules, req) +} + +func (s *IOInfoService) EvaluateSIPDispatchRules(ctx context.Context, req *rpc.EvaluateSIPDispatchRulesRequest) (*rpc.EvaluateSIPDispatchRulesResponse, error) { + trunk, err := s.matchSIPTrunk(ctx, req) + if err != nil { + return nil, err + } + best, err := s.matchSIPDispatchRule(ctx, trunk, req) + if err != nil { + return nil, err + } + sentPin := req.GetPin() + + from := req.CallingNumber + if best.HidePhoneNumber { + // TODO: Decide on the phone masking format. + // Maybe keep regional code, but mask all but 4 last digits? + from = from[len(from)-4:] + } + fromName := "Phone " + from + + room, rulePin, err := sipGetPinAndRoom(best) + if err != nil { + return nil, err + } + if rulePin != "" { + if sentPin == "" { + return &rpc.EvaluateSIPDispatchRulesResponse{ + RequestPin: true, + }, nil + } + if rulePin != sentPin { + // This should never happen in practice, because matchSIPDispatchRule should remove rules with the wrong pin. + return nil, fmt.Errorf("Incorrect PIN for SIP room") + } + } else { + // Pin was sent, but room doesn't require one. Assume user accidentally pressed phone button. + } + switch rule := best.GetRule().GetRule().(type) { + case *livekit.SIPDispatchRule_DispatchRuleIndividual: + // TODO: Decide on the suffix. Do we need to escape specific characters? + room = rule.DispatchRuleIndividual.GetRoomPrefix() + from + } + return &rpc.EvaluateSIPDispatchRulesResponse{ + RoomName: room, + ParticipantIdentity: fromName, + }, nil +} diff --git a/pkg/service/ioservice_sip_test.go b/pkg/service/ioservice_sip_test.go new file mode 100644 index 000000000..4b5ada20f --- /dev/null +++ b/pkg/service/ioservice_sip_test.go @@ -0,0 +1,394 @@ +package service + +import ( + "fmt" + "testing" + + "github.com/livekit/protocol/livekit" + "github.com/livekit/protocol/rpc" + "github.com/stretchr/testify/require" +) + +const ( + sipNumber1 = "1111 1111" + sipNumber2 = "2222 2222" + sipNumber3 = "3333 3333" + sipTrunkID1 = "aaa" + sipTrunkID2 = "bbb" +) + +func TestSIPMatchTrunk(t *testing.T) { + cases := []struct { + name string + trunks []*livekit.SIPTrunkInfo + exp int + expErr bool + }{ + { + name: "empty", + trunks: nil, + exp: -1, // no error; nil result + }, + { + name: "one wildcard", + trunks: []*livekit.SIPTrunkInfo{ + {SipTrunkId: "aaa"}, + }, + exp: 0, + }, + { + name: "matching", + trunks: []*livekit.SIPTrunkInfo{ + {SipTrunkId: "aaa", OutboundNumber: sipNumber2}, + }, + exp: 0, + }, + { + name: "matching regexp", + trunks: []*livekit.SIPTrunkInfo{ + {SipTrunkId: "aaa", OutboundNumber: sipNumber2, InboundNumbersRegex: []string{`^\d+ \d+$`}}, + }, + exp: 0, + }, + { + name: "not matching", + trunks: []*livekit.SIPTrunkInfo{ + {SipTrunkId: "aaa", OutboundNumber: sipNumber3}, + }, + exp: -1, + }, + { + name: "not matching regexp", + trunks: []*livekit.SIPTrunkInfo{ + {SipTrunkId: "aaa", OutboundNumber: sipNumber2, InboundNumbersRegex: []string{`^\d+$`}}, + }, + exp: -1, + }, + { + name: "one match", + trunks: []*livekit.SIPTrunkInfo{ + {SipTrunkId: "aaa", OutboundNumber: sipNumber3}, + {SipTrunkId: "bbb", OutboundNumber: sipNumber2}, + }, + exp: 1, + }, + { + name: "many matches", + trunks: []*livekit.SIPTrunkInfo{ + {SipTrunkId: "aaa", OutboundNumber: sipNumber3}, + {SipTrunkId: "bbb", OutboundNumber: sipNumber2}, + {SipTrunkId: "ccc", OutboundNumber: sipNumber2}, + }, + expErr: true, + }, + { + name: "many matches default", + trunks: []*livekit.SIPTrunkInfo{ + {SipTrunkId: "aaa", OutboundNumber: sipNumber3}, + {SipTrunkId: "bbb"}, + {SipTrunkId: "ccc", OutboundNumber: sipNumber2}, + {SipTrunkId: "ddd"}, + }, + exp: 2, + }, + { + name: "regexp", + trunks: []*livekit.SIPTrunkInfo{ + {SipTrunkId: "aaa", OutboundNumber: sipNumber3}, + {SipTrunkId: "bbb", OutboundNumber: sipNumber2}, + {SipTrunkId: "ccc", OutboundNumber: sipNumber2, InboundNumbersRegex: []string{`^\d+$`}}, + }, + exp: 1, + }, + { + name: "multiple defaults", + trunks: []*livekit.SIPTrunkInfo{ + {SipTrunkId: "aaa", OutboundNumber: sipNumber3}, + {SipTrunkId: "bbb"}, + {SipTrunkId: "ccc"}, + }, + expErr: true, + }, + } + for _, c := range cases { + c := c + t.Run(c.name, func(t *testing.T) { + req := &rpc.EvaluateSIPDispatchRulesRequest{ + CallingNumber: sipNumber1, + CalledNumber: sipNumber2, + } + got, err := sipMatchTrunk(c.trunks, req) + if c.expErr { + require.Error(t, err) + require.Nil(t, got) + t.Log(err) + } else { + var exp *livekit.SIPTrunkInfo + if c.exp >= 0 { + exp = c.trunks[c.exp] + } + require.NoError(t, err) + require.Equal(t, exp, got) + } + }) + } +} + +func newSIPTrunkDispatch() *livekit.SIPTrunkInfo { + return &livekit.SIPTrunkInfo{ + SipTrunkId: sipTrunkID1, + OutboundNumber: sipNumber2, + } +} + +func newSIPReqDispatch(pin string, noPin bool) *rpc.EvaluateSIPDispatchRulesRequest { + return &rpc.EvaluateSIPDispatchRulesRequest{ + CallingNumber: sipNumber1, + CalledNumber: sipNumber2, + Pin: pin, + //NoPin: noPin, // TODO + } +} + +func newDirectDispatch(room, pin string) *livekit.SIPDispatchRule { + return &livekit.SIPDispatchRule{ + Rule: &livekit.SIPDispatchRule_DispatchRuleDirect{ + DispatchRuleDirect: &livekit.SIPDispatchRuleDirect{ + RoomName: room, Pin: pin, + }, + }, + } +} + +func newIndividualDispatch(roomPref, pin string) *livekit.SIPDispatchRule { + return &livekit.SIPDispatchRule{ + Rule: &livekit.SIPDispatchRule_DispatchRuleIndividual{ + DispatchRuleIndividual: &livekit.SIPDispatchRuleIndividual{ + RoomPrefix: roomPref, Pin: pin, + }, + }, + } +} + +func TestSIPMatchDispatchRule(t *testing.T) { + cases := []struct { + name string + trunk *livekit.SIPTrunkInfo + rules []*livekit.SIPDispatchRuleInfo + reqPin string + noPin bool + exp int + expErr bool + }{ + // These cases just validate that no rules produce an error. + { + name: "empty", + trunk: nil, + rules: nil, + expErr: true, + }, + { + name: "only trunk", + trunk: newSIPTrunkDispatch(), + rules: nil, + expErr: true, + }, + // Default rules should work even if no trunk is defined. + { + name: "one rule/no trunk", + trunk: nil, + rules: []*livekit.SIPDispatchRuleInfo{ + {TrunkIds: nil, Rule: newDirectDispatch("sip", "")}, + }, + exp: 0, + }, + // Default rule should work with a trunk too. + { + name: "one rule/default trunk", + trunk: newSIPTrunkDispatch(), + rules: []*livekit.SIPDispatchRuleInfo{ + {TrunkIds: nil, Rule: newDirectDispatch("sip", "")}, + }, + exp: 0, + }, + // Rule matching the trunk should be selected. + { + name: "one rule/specific trunk", + trunk: newSIPTrunkDispatch(), + rules: []*livekit.SIPDispatchRuleInfo{ + {TrunkIds: []string{sipTrunkID1, sipTrunkID2}, Rule: newDirectDispatch("sip", "")}, + }, + exp: 0, + }, + // Rule NOT matching the trunk should NOT be selected. + { + name: "one rule/wrong trunk", + trunk: newSIPTrunkDispatch(), + rules: []*livekit.SIPDispatchRuleInfo{ + {TrunkIds: []string{"zzz"}, Rule: newDirectDispatch("sip", "")}, + }, + expErr: true, + }, + // Direct rule with a pin should be selected, even if no pin is provided. + { + name: "direct pin/correct", + trunk: newSIPTrunkDispatch(), + rules: []*livekit.SIPDispatchRuleInfo{ + {TrunkIds: []string{sipTrunkID1}, Rule: newDirectDispatch("sip", "123")}, + {TrunkIds: []string{sipTrunkID2}, Rule: newDirectDispatch("sip", "456")}, + }, + reqPin: "123", + exp: 0, + }, + // Direct rule with a pin should reject wrong pin. + { + name: "direct pin/wrong", + trunk: newSIPTrunkDispatch(), + rules: []*livekit.SIPDispatchRuleInfo{ + {TrunkIds: []string{sipTrunkID1}, Rule: newDirectDispatch("sip", "123")}, + {TrunkIds: []string{sipTrunkID2}, Rule: newDirectDispatch("sip", "456")}, + }, + reqPin: "zzz", + expErr: true, + }, + // Multiple direct rules with the same pin should result in an error. + { + name: "direct pin/conflict", + trunk: newSIPTrunkDispatch(), + rules: []*livekit.SIPDispatchRuleInfo{ + {TrunkIds: []string{sipTrunkID1}, Rule: newDirectDispatch("sip1", "123")}, + {TrunkIds: []string{sipTrunkID1, sipTrunkID2}, Rule: newDirectDispatch("sip2", "123")}, + }, + reqPin: "123", + expErr: true, + }, + // Multiple direct rules with the same pin on different trunks are ok. + { + name: "direct pin/no conflict on different trunk", + trunk: newSIPTrunkDispatch(), + rules: []*livekit.SIPDispatchRuleInfo{ + {TrunkIds: []string{sipTrunkID1}, Rule: newDirectDispatch("sip1", "123")}, + {TrunkIds: []string{sipTrunkID2}, Rule: newDirectDispatch("sip2", "123")}, + }, + reqPin: "123", + exp: 0, + }, + // Specific direct rules should take priority over default direct rules. + { + name: "direct pin/default and specific", + trunk: newSIPTrunkDispatch(), + rules: []*livekit.SIPDispatchRuleInfo{ + {TrunkIds: nil, Rule: newDirectDispatch("sip1", "123")}, + {TrunkIds: []string{sipTrunkID1}, Rule: newDirectDispatch("sip2", "123")}, + }, + reqPin: "123", + exp: 1, + }, + // Specific direct rules should take priority over default direct rules. No pin. + { + name: "direct/default and specific", + trunk: newSIPTrunkDispatch(), + rules: []*livekit.SIPDispatchRuleInfo{ + {TrunkIds: nil, Rule: newDirectDispatch("sip1", "")}, + {TrunkIds: []string{sipTrunkID1}, Rule: newDirectDispatch("sip2", "")}, + }, + exp: 1, + }, + // Specific direct rules should take priority over default direct rules. One with pin, other without. + { + name: "direct/default and specific/mixed 1", + trunk: newSIPTrunkDispatch(), + rules: []*livekit.SIPDispatchRuleInfo{ + {TrunkIds: nil, Rule: newDirectDispatch("sip1", "123")}, + {TrunkIds: []string{sipTrunkID1}, Rule: newDirectDispatch("sip2", "")}, + }, + exp: 1, + }, + { + name: "direct/default and specific/mixed 2", + trunk: newSIPTrunkDispatch(), + rules: []*livekit.SIPDispatchRuleInfo{ + {TrunkIds: nil, Rule: newDirectDispatch("sip1", "")}, + {TrunkIds: []string{sipTrunkID1}, Rule: newDirectDispatch("sip2", "123")}, + }, + exp: 1, + }, + // Multiple default direct rules are not allowed. + { + name: "direct/multiple defaults", + trunk: newSIPTrunkDispatch(), + rules: []*livekit.SIPDispatchRuleInfo{ + {TrunkIds: nil, Rule: newDirectDispatch("sip1", "")}, + {TrunkIds: nil, Rule: newDirectDispatch("sip2", "")}, + }, + expErr: true, + }, + // Cannot use both direct and individual rules with the same pin setup. + { + name: "direct vs individual/private", + trunk: newSIPTrunkDispatch(), + rules: []*livekit.SIPDispatchRuleInfo{ + {TrunkIds: nil, Rule: newIndividualDispatch("pref_", "123")}, + {TrunkIds: nil, Rule: newDirectDispatch("sip", "123")}, + }, + expErr: true, + }, + { + name: "direct vs individual/open", + trunk: newSIPTrunkDispatch(), + rules: []*livekit.SIPDispatchRuleInfo{ + {TrunkIds: nil, Rule: newIndividualDispatch("pref_", "")}, + {TrunkIds: nil, Rule: newDirectDispatch("sip", "")}, + }, + expErr: true, + }, + // Direct rules take priority over individual rules. + { + name: "direct vs individual/priority", + trunk: newSIPTrunkDispatch(), + rules: []*livekit.SIPDispatchRuleInfo{ + {TrunkIds: nil, Rule: newIndividualDispatch("pref_", "123")}, + {TrunkIds: nil, Rule: newDirectDispatch("sip", "456")}, + }, + reqPin: "456", + exp: 1, + }, + } + for _, c := range cases { + c := c + t.Run(c.name, func(t *testing.T) { + pins := []string{c.reqPin} + if !c.expErr && c.reqPin != "" { + // Should match the same rule, even if no pin is set (so that it can be requested). + pins = append(pins, "") + } + for i, r := range c.rules { + if r.SipDispatchRuleId == "" { + r.SipDispatchRuleId = fmt.Sprintf("rule_%d", i) + } + } + for _, pin := range pins { + pin := pin + name := pin + if name == "" { + name = "no pin" + } + t.Run(name, func(t *testing.T) { + got, err := sipMatchDispatchRule(c.trunk, c.rules, newSIPReqDispatch(pin, c.noPin)) + if c.expErr { + require.Error(t, err) + require.Nil(t, got) + t.Log(err) + } else { + var exp *livekit.SIPDispatchRuleInfo + if c.exp >= 0 { + exp = c.rules[c.exp] + } + require.NoError(t, err) + require.Equal(t, exp, got) + } + }) + } + }) + } +} From 7bcd6d4df0b3ea47fbd808c38afe16e7f5b82688 Mon Sep 17 00:00:00 2001 From: Denys Smirnov Date: Wed, 15 Nov 2023 22:29:36 +0200 Subject: [PATCH 13/18] SIP: Implement GetSIPTrunkAuthentication. (#2244) --- pkg/service/ioservice.go | 4 ---- pkg/service/ioservice_sip.go | 27 +++++++++++++++++++-------- pkg/service/ioservice_sip_test.go | 6 +----- 3 files changed, 20 insertions(+), 17 deletions(-) diff --git a/pkg/service/ioservice.go b/pkg/service/ioservice.go index 0149e7af6..70158d5cc 100644 --- a/pkg/service/ioservice.go +++ b/pkg/service/ioservice.go @@ -215,10 +215,6 @@ func (s *IOInfoService) UpdateIngressState(ctx context.Context, req *rpc.UpdateI return &emptypb.Empty{}, nil } -func (s *IOInfoService) GetSIPTrunkAuthentication(ctx context.Context, req *rpc.GetSIPTrunkAuthenticationRequest) (*rpc.GetSIPTrunkAuthenticationResponse, error) { - return nil, nil -} - func (s *IOInfoService) Stop() { close(s.shutdown) diff --git a/pkg/service/ioservice_sip.go b/pkg/service/ioservice_sip.go index 715dea823..62b1b89d9 100644 --- a/pkg/service/ioservice_sip.go +++ b/pkg/service/ioservice_sip.go @@ -127,7 +127,7 @@ func sipGetPinAndRoom(info *livekit.SIPDispatchRuleInfo) (room, pin string, err // sipMatchTrunk finds a SIP Trunk definition matching the request. // Returns nil if no rules matched or an error if there are conflicting definitions. -func sipMatchTrunk(trunks []*livekit.SIPTrunkInfo, req *rpc.EvaluateSIPDispatchRulesRequest) (*livekit.SIPTrunkInfo, error) { +func sipMatchTrunk(trunks []*livekit.SIPTrunkInfo, calling, called string) (*livekit.SIPTrunkInfo, error) { var ( selectedTrunk *livekit.SIPTrunkInfo defaultTrunk *livekit.SIPTrunkInfo @@ -143,7 +143,7 @@ func sipMatchTrunk(trunks []*livekit.SIPTrunkInfo, req *rpc.EvaluateSIPDispatchR logger.Errorw("cannot parse SIP trunk regexp", err, "trunkID", tr.SipTrunkId) continue } - if re.MatchString(req.CallingNumber) { + if re.MatchString(calling) { matches = true break } @@ -155,10 +155,10 @@ func sipMatchTrunk(trunks []*livekit.SIPTrunkInfo, req *rpc.EvaluateSIPDispatchR // Default/wildcard trunk. defaultTrunk = tr defaultTrunkCnt++ - } else if tr.OutboundNumber == req.CalledNumber { + } else if tr.OutboundNumber == called { // Trunk specific to the number. if selectedTrunk != nil { - return nil, fmt.Errorf("Multiple SIP Trunks matched for %q", req.CalledNumber) + return nil, fmt.Errorf("Multiple SIP Trunks matched for %q", called) } selectedTrunk = tr // Keep searching! We want to know if there are any conflicting Trunk definitions. @@ -168,7 +168,7 @@ func sipMatchTrunk(trunks []*livekit.SIPTrunkInfo, req *rpc.EvaluateSIPDispatchR return selectedTrunk, nil } if defaultTrunkCnt > 1 { - return nil, fmt.Errorf("Multiple default SIP Trunks matched for %q", req.CalledNumber) + return nil, fmt.Errorf("Multiple default SIP Trunks matched for %q", called) } // Could still be nil here. return defaultTrunk, nil @@ -261,12 +261,12 @@ func sipMatchDispatchRule(trunk *livekit.SIPTrunkInfo, rules []*livekit.SIPDispa // matchSIPTrunk finds a SIP Trunk definition matching the request. // Returns nil if no rules matched or an error if there are conflicting definitions. -func (s *IOInfoService) matchSIPTrunk(ctx context.Context, req *rpc.EvaluateSIPDispatchRulesRequest) (*livekit.SIPTrunkInfo, error) { +func (s *IOInfoService) matchSIPTrunk(ctx context.Context, calling, called string) (*livekit.SIPTrunkInfo, error) { trunks, err := s.ss.ListSIPTrunk(ctx) if err != nil { return nil, err } - return sipMatchTrunk(trunks, req) + return sipMatchTrunk(trunks, calling, called) } // matchSIPDispatchRule finds the best dispatch rule matching the request parameters. Returns an error if no rule matched. @@ -282,7 +282,7 @@ func (s *IOInfoService) matchSIPDispatchRule(ctx context.Context, trunk *livekit } func (s *IOInfoService) EvaluateSIPDispatchRules(ctx context.Context, req *rpc.EvaluateSIPDispatchRulesRequest) (*rpc.EvaluateSIPDispatchRulesResponse, error) { - trunk, err := s.matchSIPTrunk(ctx, req) + trunk, err := s.matchSIPTrunk(ctx, req.CallingNumber, req.CalledNumber) if err != nil { return nil, err } @@ -327,3 +327,14 @@ func (s *IOInfoService) EvaluateSIPDispatchRules(ctx context.Context, req *rpc.E ParticipantIdentity: fromName, }, nil } + +func (s *IOInfoService) GetSIPTrunkAuthentication(ctx context.Context, req *rpc.GetSIPTrunkAuthenticationRequest) (*rpc.GetSIPTrunkAuthenticationResponse, error) { + trunk, err := s.matchSIPTrunk(ctx, req.From, req.To) + if err != nil { + return nil, err + } + return &rpc.GetSIPTrunkAuthenticationResponse{ + Username: trunk.Username, + Password: trunk.Password, + }, nil +} diff --git a/pkg/service/ioservice_sip_test.go b/pkg/service/ioservice_sip_test.go index 4b5ada20f..b8b57fd07 100644 --- a/pkg/service/ioservice_sip_test.go +++ b/pkg/service/ioservice_sip_test.go @@ -113,11 +113,7 @@ func TestSIPMatchTrunk(t *testing.T) { for _, c := range cases { c := c t.Run(c.name, func(t *testing.T) { - req := &rpc.EvaluateSIPDispatchRulesRequest{ - CallingNumber: sipNumber1, - CalledNumber: sipNumber2, - } - got, err := sipMatchTrunk(c.trunks, req) + got, err := sipMatchTrunk(c.trunks, sipNumber1, sipNumber2) if c.expErr { require.Error(t, err) require.Nil(t, got) From c62382c76e26a9f8c4782bd06637b0aedbf8cdd0 Mon Sep 17 00:00:00 2001 From: Raja Subramanian Date: Fri, 17 Nov 2023 00:40:00 +0530 Subject: [PATCH 14/18] Clean up restart a bit. (#2247) --- pkg/sfu/buffer/dependencydescriptorparser.go | 4 +- pkg/sfu/buffer/rtpstats_receiver.go | 62 +------- pkg/sfu/utils/wraparound.go | 37 +++-- pkg/sfu/utils/wraparound_test.go | 143 ++++++++++++++++++- 4 files changed, 171 insertions(+), 75 deletions(-) diff --git a/pkg/sfu/buffer/dependencydescriptorparser.go b/pkg/sfu/buffer/dependencydescriptorparser.go index 6c91af260..8cc648b7f 100644 --- a/pkg/sfu/buffer/dependencydescriptorparser.go +++ b/pkg/sfu/buffer/dependencydescriptorparser.go @@ -52,8 +52,8 @@ func NewDependencyDescriptorParser(ddExtID uint8, logger logger.Logger, onMaxLay ddExtID: ddExtID, logger: logger, onMaxLayerChanged: onMaxLayerChanged, - seqWrapAround: utils.NewWrapAround[uint16, uint64](), - frameWrapAround: utils.NewWrapAround[uint16, uint64](), + seqWrapAround: utils.NewWrapAround[uint16, uint64](utils.WrapAroundParams{IsRestartAllowed: false}), + frameWrapAround: utils.NewWrapAround[uint16, uint64](utils.WrapAroundParams{IsRestartAllowed: false}), frameChecker: NewFrameIntegrityChecker(180, 1024), // 2seconds for L3T3 30fps video } } diff --git a/pkg/sfu/buffer/rtpstats_receiver.go b/pkg/sfu/buffer/rtpstats_receiver.go index dad7c66ef..b621f42b1 100644 --- a/pkg/sfu/buffer/rtpstats_receiver.go +++ b/pkg/sfu/buffer/rtpstats_receiver.go @@ -60,8 +60,8 @@ type RTPStatsReceiver struct { func NewRTPStatsReceiver(params RTPStatsParams) *RTPStatsReceiver { return &RTPStatsReceiver{ rtpStatsBase: newRTPStatsBase(params), - sequenceNumber: utils.NewWrapAround[uint16, uint64](), - timestamp: utils.NewWrapAround[uint32, uint64](), + sequenceNumber: utils.NewWrapAround[uint16, uint64](utils.WrapAroundParams{IsRestartAllowed: false}), + timestamp: utils.NewWrapAround[uint32, uint64](utils.WrapAroundParams{IsRestartAllowed: false}), history: protoutils.NewBitmap[uint64](cHistorySize), } } @@ -123,36 +123,16 @@ func (r *RTPStatsReceiver) Update( ) } else { resSN = r.sequenceNumber.Update(sequenceNumber) + if resSN.IsUnhandled { + flowState.IsNotHandled = true + return + } resTS = r.timestamp.Update(timestamp) } pktSize := uint64(hdrSize + payloadSize + paddingSize) gapSN := int64(resSN.ExtendedVal - resSN.PreExtendedHighest) if gapSN <= 0 { // duplicate OR out-of-order - // before start, don't restart - if resTS.IsRestart { - r.logger.Infow( - "rolling back timestamp restart", - "tsBefore", resTS.PreExtendedStart, - "tsAfter", r.timestamp.GetExtendedStart(), - "snBefore", resSN.PreExtendedStart, - "snAfter", r.sequenceNumber.GetExtendedStart(), - ) - r.timestamp.RollbackRestart(resTS.PreExtendedStart) - } - if resSN.IsRestart { - r.logger.Infow( - "rolling back sequence number restart", - "snBefore", resSN.PreExtendedStart, - "snAfter", r.sequenceNumber.GetExtendedStart(), - "tsBefore", resTS.PreExtendedStart, - "tsAfter", r.timestamp.GetExtendedStart(), - ) - r.sequenceNumber.RollbackRestart(resSN.PreExtendedStart) - flowState.IsNotHandled = true - return - } - if -gapSN >= cNumSequenceNumbers/2 { r.logger.Warnw( "large sequence number gap negative", nil, @@ -179,36 +159,6 @@ func (r *RTPStatsReceiver) Update( r.packetsOutOfOrder++ } - if resSN.IsRestart { - r.packetsLost += resSN.PreExtendedStart - resSN.ExtendedVal - - extStartSN := r.sequenceNumber.GetExtendedStart() - for i := uint32(0); i < r.nextSnapshotID-cFirstSnapshotID; i++ { - s := &r.snapshots[i] - if s.extStartSN == resSN.PreExtendedStart { - s.extStartSN = extStartSN - } - } - - r.logger.Infow( - "adjusting start sequence number", - "snBefore", resSN.PreExtendedStart, - "snAfter", resSN.ExtendedVal, - "tsBefore", resTS.PreExtendedStart, - "tsAfter", resTS.ExtendedVal, - ) - } - - if resTS.IsRestart { - r.logger.Infow( - "adjusting start timestamp", - "tsBefore", resTS.PreExtendedStart, - "tsAfter", resTS.ExtendedVal, - "snBefore", resSN.PreExtendedStart, - "snAfter", resSN.ExtendedVal, - ) - } - if r.isInRange(resSN.ExtendedVal, resSN.PreExtendedHighest) { if r.history.IsSet(resSN.ExtendedVal) { r.bytesDuplicate += pktSize diff --git a/pkg/sfu/utils/wraparound.go b/pkg/sfu/utils/wraparound.go index b04b331c2..e37d5ca27 100644 --- a/pkg/sfu/utils/wraparound.go +++ b/pkg/sfu/utils/wraparound.go @@ -26,7 +26,12 @@ type extendedNumber interface { uint32 | uint64 } +type WrapAroundParams struct { + IsRestartAllowed bool +} + type WrapAround[T number, ET extendedNumber] struct { + params WrapAroundParams fullRange ET initialized bool @@ -36,9 +41,10 @@ type WrapAround[T number, ET extendedNumber] struct { extendedHighest ET } -func NewWrapAround[T number, ET extendedNumber]() *WrapAround[T, ET] { +func NewWrapAround[T number, ET extendedNumber](params WrapAroundParams) *WrapAround[T, ET] { var t T return &WrapAround[T, ET]{ + params: params, fullRange: 1 << (unsafe.Sizeof(t) * 8), } } @@ -52,6 +58,7 @@ func (w *WrapAround[T, ET]) Seed(from *WrapAround[T, ET]) { } type WrapAroundUpdateResult[ET extendedNumber] struct { + IsUnhandled bool // when set, other fields are invalid IsRestart bool PreExtendedStart ET // valid only if IsRestart = true PreExtendedHighest ET @@ -140,20 +147,24 @@ func (w *WrapAround[T, ET]) maybeAdjustStart(val T) (result WrapAroundUpdateResu } if val-w.start > T(w.fullRange>>1) { - // out-of-order with existing start => a new start - result.IsRestart = true - if val > w.start { - result.PreExtendedStart = w.fullRange + ET(w.start) - } else { - result.PreExtendedStart = ET(w.start) - } + if w.params.IsRestartAllowed { + // out-of-order with existing start => a new start + result.IsRestart = true + if val > w.start { + result.PreExtendedStart = w.fullRange + ET(w.start) + } else { + result.PreExtendedStart = ET(w.start) + } - if w.isWrapBack(val, w.highest) { - w.cycles = w.fullRange - w.updateExtendedHighest() - cycles = 0 + if w.isWrapBack(val, w.highest) { + w.cycles = w.fullRange + w.updateExtendedHighest() + cycles = 0 + } + w.start = val + } else { + result.IsUnhandled = true } - w.start = val } else { if w.isWrapBack(val, w.highest) { cycles -= w.fullRange diff --git a/pkg/sfu/utils/wraparound_test.go b/pkg/sfu/utils/wraparound_test.go index 55d700650..018e227fc 100644 --- a/pkg/sfu/utils/wraparound_test.go +++ b/pkg/sfu/utils/wraparound_test.go @@ -21,7 +21,7 @@ import ( ) func TestWrapAroundUint16(t *testing.T) { - w := NewWrapAround[uint16, uint32]() + w := NewWrapAround[uint16, uint32](WrapAroundParams{IsRestartAllowed: true}) testCases := []struct { name string input uint16 @@ -194,8 +194,143 @@ func TestWrapAroundUint16(t *testing.T) { } } +func TestWrapAroundUint16NoRestart(t *testing.T) { + w := NewWrapAround[uint16, uint32](WrapAroundParams{IsRestartAllowed: false}) + testCases := []struct { + name string + input uint16 + updated WrapAroundUpdateResult[uint32] + start uint16 + extendedStart uint32 + highest uint16 + extendedHighest uint32 + }{ + // initialize + { + name: "initialize", + input: 10, + updated: WrapAroundUpdateResult[uint32]{ + IsRestart: false, + PreExtendedStart: 0, + PreExtendedHighest: 9, + ExtendedVal: 10, + }, + start: 10, + extendedStart: 10, + highest: 10, + extendedHighest: 10, + }, + // an older number without wrap around should not reset start point + { + name: "no reset start no wrap around", + input: 8, + updated: WrapAroundUpdateResult[uint32]{ + IsUnhandled: true, + // the following fields are not valid when `IsUnhandled = true`, but code fills it in + // and they are filled in here for testing purposes + PreExtendedHighest: 10, + ExtendedVal: 8, + }, + start: 10, + extendedStart: 10, + highest: 10, + extendedHighest: 10, + }, + // an older number with wrap around should not reset start point + { + name: "no reset start wrap around", + input: (1 << 16) - 6, + updated: WrapAroundUpdateResult[uint32]{ + IsUnhandled: true, + PreExtendedHighest: 10, + ExtendedVal: (1 << 16) - 6, + }, + start: 10, + extendedStart: 10, + highest: 10, + extendedHighest: 10, + }, + // yet another older number with wrap around should not reset start point + { + name: "no reset start again", + input: (1 << 16) - 12, + updated: WrapAroundUpdateResult[uint32]{ + IsUnhandled: true, + PreExtendedHighest: 10, + ExtendedVal: (1 << 16) - 12, + }, + start: 10, + extendedStart: 10, + highest: 10, + extendedHighest: 10, + }, + // duplicate should return same as highest + { + name: "duplicate", + input: 10, + updated: WrapAroundUpdateResult[uint32]{ + PreExtendedHighest: 10, + ExtendedVal: 10, + }, + start: 10, + extendedStart: 10, + highest: 10, + extendedHighest: 10, + }, + // a significant jump in order should move highest to that + { + name: "big in-order jump", + input: (1 << 15) - 10, + updated: WrapAroundUpdateResult[uint32]{ + PreExtendedHighest: 10, + ExtendedVal: (1 << 15) - 10, + }, + start: 10, + extendedStart: 10, + highest: (1 << 15) - 10, + extendedHighest: (1 << 15) - 10, + }, + // in-order, should update highest + { + name: "in-order", + input: (1 << 15) + 13, + updated: WrapAroundUpdateResult[uint32]{ + PreExtendedHighest: (1 << 15) - 10, + ExtendedVal: (1 << 15) + 13, + }, + start: 10, + extendedStart: 10, + highest: (1 << 15) + 13, + extendedHighest: (1 << 15) + 13, + }, + // now out-of-order should not reset start as half the range has been seen + { + name: "out-of-order after half range", + input: (1 << 15) - 11, + updated: WrapAroundUpdateResult[uint32]{ + PreExtendedHighest: (1 << 15) + 13, + ExtendedVal: (1 << 15) - 11, + }, + start: 10, + extendedStart: 10, + highest: (1 << 15) + 13, + extendedHighest: (1 << 15) + 13, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + require.Equal(t, tc.updated, w.Update(tc.input)) + require.Equal(t, tc.start, w.GetStart()) + require.Equal(t, tc.extendedStart, w.GetExtendedStart()) + require.Equal(t, tc.highest, w.GetHighest()) + require.Equal(t, tc.extendedHighest, w.GetExtendedHighest()) + }) + } +} + func TestWrapAroundUint16RollbackRestartAndResetHighest(t *testing.T) { - w := NewWrapAround[uint16, uint64]() + w := NewWrapAround[uint16, uint64](WrapAroundParams{IsRestartAllowed: true}) // initialize w.Update(23) @@ -268,7 +403,7 @@ func TestWrapAroundUint16RollbackRestartAndResetHighest(t *testing.T) { } func TestWrapAroundUint16WrapAroundRestartDuplicate(t *testing.T) { - w := NewWrapAround[uint16, uint64]() + w := NewWrapAround[uint16, uint64](WrapAroundParams{IsRestartAllowed: true}) // initialize w.Update(65534) @@ -314,7 +449,7 @@ func TestWrapAroundUint16WrapAroundRestartDuplicate(t *testing.T) { } func TestWrapAroundUint32(t *testing.T) { - w := NewWrapAround[uint32, uint64]() + w := NewWrapAround[uint32, uint64](WrapAroundParams{IsRestartAllowed: true}) testCases := []struct { name string input uint32 From 8c3ec742e6b5ba96c8f100afa7717d65d70177d6 Mon Sep 17 00:00:00 2001 From: Raja Subramanian Date: Fri, 17 Nov 2023 12:00:47 +0530 Subject: [PATCH 15/18] Use now for end time (#2248) * Use now for end time * less arithmetic --- pkg/sfu/buffer/datastats.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/sfu/buffer/datastats.go b/pkg/sfu/buffer/datastats.go index 515341cc0..795f8a0f2 100644 --- a/pkg/sfu/buffer/datastats.go +++ b/pkg/sfu/buffer/datastats.go @@ -71,7 +71,7 @@ func (s *DataStats) ToProtoActive() *livekit.RTPStats { return &livekit.RTPStats{ StartTime: timestamppb.New(time.Unix(s.windowStart/1e9, s.windowStart%1e9)), - EndTime: timestamppb.New(time.Now()), + EndTime: timestamppb.New(time.Unix(0, now)), Duration: float64(duration / 1e9), Bytes: uint64(s.windowBytes), Bitrate: float64(s.windowBytes) * 8 / float64(duration) / 1e9, From 0c66727161d5807b495e731a31e7d886b6a04b99 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 19 Nov 2023 18:05:04 -0800 Subject: [PATCH 16/18] Update module github.com/livekit/psrpc to v0.5.2 (#2151) Generated by renovateBot Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 2654fba97..2f501b5ad 100644 --- a/go.mod +++ b/go.mod @@ -19,7 +19,7 @@ require ( github.com/livekit/mageutil v0.0.0-20230125210925-54e8a70427c1 github.com/livekit/mediatransportutil v0.0.0-20231017082622-43f077b4e60e github.com/livekit/protocol v1.9.1 - github.com/livekit/psrpc v0.5.1 + github.com/livekit/psrpc v0.5.2 github.com/mackerelio/go-osstat v0.2.4 github.com/magefile/mage v1.15.0 github.com/maxbrunsfeld/counterfeiter/v6 v6.7.0 diff --git a/go.sum b/go.sum index 2eb764325..42548fb04 100644 --- a/go.sum +++ b/go.sum @@ -127,8 +127,8 @@ github.com/livekit/mediatransportutil v0.0.0-20231017082622-43f077b4e60e h1:yNeI github.com/livekit/mediatransportutil v0.0.0-20231017082622-43f077b4e60e/go.mod h1:+WIOYwiBMive5T81V8B2wdAc2zQNRjNQiJIcPxMTILY= github.com/livekit/protocol v1.9.1 h1:RTycWwtGUoYLb+7WYosJ3IP8f0e8j4lLxC/osy/dQi0= github.com/livekit/protocol v1.9.1/go.mod h1:kqDGmx+WZ6WMZ5V/T9lF8DgnuThAjetwjHq1nd7moSE= -github.com/livekit/psrpc v0.5.1 h1:ihN5uKIvbU69UsFS4HdYmou5GuK0Dt4hix4eOmRS7o8= -github.com/livekit/psrpc v0.5.1/go.mod h1:cQjxg1oCxYHhxxv6KJH1gSvdtCHQoRZCHgPdm5N8v2g= +github.com/livekit/psrpc v0.5.2 h1:+MvG8Otm/J6MTg2MP/uuMbrkxOWsrj2hDhu/I1VIU1U= +github.com/livekit/psrpc v0.5.2/go.mod h1:cQjxg1oCxYHhxxv6KJH1gSvdtCHQoRZCHgPdm5N8v2g= github.com/mackerelio/go-osstat v0.2.4 h1:qxGbdPkFo65PXOb/F/nhDKpF2nGmGaCFDLXoZjJTtUs= github.com/mackerelio/go-osstat v0.2.4/go.mod h1:Zy+qzGdZs3A9cuIqmgbJvwbmLQH9dJvtio5ZjJTbdlQ= github.com/magefile/mage v1.15.0 h1:BvGheCMAsG3bWUDbZ8AyXXpCNwU9u5CB6sM+HNb9HYg= From f18d6356e8e49abd3fabdae5595a09668e34d801 Mon Sep 17 00:00:00 2001 From: Denys Smirnov Date: Mon, 20 Nov 2023 17:18:59 +0200 Subject: [PATCH 17/18] SIP: Update protocol, use NoPin flag. (#2250) --- go.mod | 4 ++-- go.sum | 8 ++++---- pkg/service/ioservice_sip.go | 14 +------------- 3 files changed, 7 insertions(+), 19 deletions(-) diff --git a/go.mod b/go.mod index 2f501b5ad..c93bc5d8c 100644 --- a/go.mod +++ b/go.mod @@ -18,7 +18,7 @@ require ( github.com/jxskiss/base62 v1.1.0 github.com/livekit/mageutil v0.0.0-20230125210925-54e8a70427c1 github.com/livekit/mediatransportutil v0.0.0-20231017082622-43f077b4e60e - github.com/livekit/protocol v1.9.1 + github.com/livekit/protocol v1.9.2-0.20231116141704-aa43aa7482d7 github.com/livekit/psrpc v0.5.2 github.com/mackerelio/go-osstat v0.2.4 github.com/magefile/mage v1.15.0 @@ -34,7 +34,7 @@ require ( github.com/pion/sdp/v3 v3.0.6 github.com/pion/transport/v2 v2.2.4 github.com/pion/turn/v2 v2.1.4 - github.com/pion/webrtc/v3 v3.2.22 + github.com/pion/webrtc/v3 v3.2.23 github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.17.0 github.com/redis/go-redis/v9 v9.3.0 diff --git a/go.sum b/go.sum index 42548fb04..909a503d1 100644 --- a/go.sum +++ b/go.sum @@ -125,8 +125,8 @@ github.com/livekit/mageutil v0.0.0-20230125210925-54e8a70427c1 h1:jm09419p0lqTkD github.com/livekit/mageutil v0.0.0-20230125210925-54e8a70427c1/go.mod h1:Rs3MhFwutWhGwmY1VQsygw28z5bWcnEYmS1OG9OxjOQ= github.com/livekit/mediatransportutil v0.0.0-20231017082622-43f077b4e60e h1:yNeIo7MSMUWgoLu7LkNKnBYnJBFPFH9Wq4S6h1kS44M= github.com/livekit/mediatransportutil v0.0.0-20231017082622-43f077b4e60e/go.mod h1:+WIOYwiBMive5T81V8B2wdAc2zQNRjNQiJIcPxMTILY= -github.com/livekit/protocol v1.9.1 h1:RTycWwtGUoYLb+7WYosJ3IP8f0e8j4lLxC/osy/dQi0= -github.com/livekit/protocol v1.9.1/go.mod h1:kqDGmx+WZ6WMZ5V/T9lF8DgnuThAjetwjHq1nd7moSE= +github.com/livekit/protocol v1.9.2-0.20231116141704-aa43aa7482d7 h1:M/ljEz6MCH5lovoTT0t6hyaaZJEn4hvXs9J9OtQ+gS4= +github.com/livekit/protocol v1.9.2-0.20231116141704-aa43aa7482d7/go.mod h1:JgFHHd99wgEp4smATlJupOdA7iJHFoj2g3RFeM/Hk8M= github.com/livekit/psrpc v0.5.2 h1:+MvG8Otm/J6MTg2MP/uuMbrkxOWsrj2hDhu/I1VIU1U= github.com/livekit/psrpc v0.5.2/go.mod h1:cQjxg1oCxYHhxxv6KJH1gSvdtCHQoRZCHgPdm5N8v2g= github.com/mackerelio/go-osstat v0.2.4 h1:qxGbdPkFo65PXOb/F/nhDKpF2nGmGaCFDLXoZjJTtUs= @@ -221,8 +221,8 @@ github.com/pion/transport/v3 v3.0.1/go.mod h1:UY7kiITrlMv7/IKgd5eTUcaahZx5oUN3l9 github.com/pion/turn/v2 v2.1.3/go.mod h1:huEpByKKHix2/b9kmTAM3YoX6MKP+/D//0ClgUYR2fY= github.com/pion/turn/v2 v2.1.4 h1:2xn8rduI5W6sCZQkEnIUDAkrBQNl2eYIBCHMZ3QMmP8= github.com/pion/turn/v2 v2.1.4/go.mod h1:huEpByKKHix2/b9kmTAM3YoX6MKP+/D//0ClgUYR2fY= -github.com/pion/webrtc/v3 v3.2.22 h1:Hno262T7+V56MgUO30O0ZirZmVSvbXtnau31SB0WSpc= -github.com/pion/webrtc/v3 v3.2.22/go.mod h1:1CaT2fcZzZ6VZA+O1i9yK2DU4EOcXVvSbWG9pr5jefs= +github.com/pion/webrtc/v3 v3.2.23 h1:GbqEuxBbVLFhXk0GwxKAoaIJYiEa9TyoZPEZC+2HZxM= +github.com/pion/webrtc/v3 v3.2.23/go.mod h1:1CaT2fcZzZ6VZA+O1i9yK2DU4EOcXVvSbWG9pr5jefs= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= diff --git a/pkg/service/ioservice_sip.go b/pkg/service/ioservice_sip.go index 62b1b89d9..d427c5e2b 100644 --- a/pkg/service/ioservice_sip.go +++ b/pkg/service/ioservice_sip.go @@ -32,9 +32,6 @@ func sipRulePriority(info *livekit.SIPDispatchRuleInfo) int32 { return 0 } return 100 - case *livekit.SIPDispatchRule_DispatchRulePin: - // TODO: If we assume that Pin is optional, this rule type is very similar to Direct. Could remove it? - return 0 case *livekit.SIPDispatchRule_DispatchRuleIndividual: if rule.DispatchRuleIndividual.GetPin() != "" { return 1 @@ -115,9 +112,6 @@ func sipGetPinAndRoom(info *livekit.SIPDispatchRuleInfo) (room, pin string, err case *livekit.SIPDispatchRule_DispatchRuleDirect: pin = rule.DispatchRuleDirect.GetPin() room = rule.DispatchRuleDirect.GetRoomName() - case *livekit.SIPDispatchRule_DispatchRulePin: - pin = rule.DispatchRulePin.GetPin() - room = rule.DispatchRulePin.GetRoomName() case *livekit.SIPDispatchRule_DispatchRuleIndividual: pin = rule.DispatchRuleIndividual.GetPin() room = rule.DispatchRuleIndividual.GetRoomPrefix() @@ -189,13 +183,7 @@ func sipMatchDispatchRule(trunk *livekit.SIPTrunkInfo, rules []*livekit.SIPDispa specificRules []*livekit.SIPDispatchRuleInfo defaultRules []*livekit.SIPDispatchRuleInfo ) - // TODO: Apart from Pin, it would be nice to have a NoPin flag. - // The way it would work is that we will first list the rules and figure out if at least one has a Pin required. - // If it does, we will immediately respond with RequestPin=true. Now, on the SIP bridge side, we will run - // audio prompt asking for a Pin. The user will have an options to skip the pin (e.g. press #) and only try - // to match no-ping rooms. This will be very useful if only 1 number is available and has to route to both - // private and public rooms. - noPin := false + noPin := req.NoPin sentPin := req.GetPin() for _, info := range rules { _, rulePin, err := sipGetPinAndRoom(info) From 56dd39968408f0973374e5b336a28606a1da79d2 Mon Sep 17 00:00:00 2001 From: Raja Subramanian Date: Wed, 22 Nov 2023 11:47:15 +0530 Subject: [PATCH 18/18] Use a worker to report signal/data stats. (#2260) * Use a worker to report signal/data stats. Was checking if reporting is needed on every update. The check is wasted work if volume of signal/data messages is high as reporting happens only once in 10 seconds. Changing to a worker based on a timer. And also aligning with telemetry reporting interval which defaults to 30 seconds. * Remove unused constant --- pkg/rtc/participant.go | 2 +- pkg/service/rtcservice.go | 2 +- pkg/telemetry/signalanddatastats.go | 52 +++++++++++++---------------- pkg/telemetry/statsworker.go | 4 +-- 4 files changed, 28 insertions(+), 32 deletions(-) diff --git a/pkg/rtc/participant.go b/pkg/rtc/participant.go index 404cbf8a2..09f3c77b9 100644 --- a/pkg/rtc/participant.go +++ b/pkg/rtc/participant.go @@ -786,7 +786,7 @@ func (p *ParticipantImpl) Close(sendLeave bool, reason types.ParticipantCloseRea p.TransportManager.Close() }() - p.dataChannelStats.Report() + p.dataChannelStats.Stop() return nil } diff --git a/pkg/service/rtcservice.go b/pkg/service/rtcservice.go index 884a89656..86ff5f630 100644 --- a/pkg/service/rtcservice.go +++ b/pkg/service/rtcservice.go @@ -269,7 +269,7 @@ func (s *RTCService) ServeHTTP(w http.ResponseWriter, r *http.Request) { close(done) if signalStats != nil { - signalStats.Report() + signalStats.Stop() } }() diff --git a/pkg/telemetry/signalanddatastats.go b/pkg/telemetry/signalanddatastats.go index 840f126c3..fb17e61e7 100644 --- a/pkg/telemetry/signalanddatastats.go +++ b/pkg/telemetry/signalanddatastats.go @@ -20,12 +20,11 @@ import ( "go.uber.org/atomic" + "github.com/livekit/livekit-server/pkg/config" "github.com/livekit/protocol/livekit" "github.com/livekit/protocol/utils" ) -const statsReportInterval = 10 * time.Second - type BytesTrackType string const ( @@ -35,11 +34,11 @@ const ( // stats for signal and data channel type BytesTrackStats struct { - trackID livekit.TrackID - pID livekit.ParticipantID - send, recv atomic.Uint64 - lastStatsReport atomic.Value // *time.Time - telemetry TelemetryService + trackID livekit.TrackID + pID livekit.ParticipantID + send, recv atomic.Uint64 + telemetry TelemetryService + isStopped atomic.Bool } func NewBytesTrackStats(trackID livekit.TrackID, pID livekit.ParticipantID, telemetry TelemetryService) *BytesTrackStats { @@ -48,8 +47,7 @@ func NewBytesTrackStats(trackID livekit.TrackID, pID livekit.ParticipantID, tele pID: pID, telemetry: telemetry, } - now := time.Now() - s.lastStatsReport.Store(&now) + go s.reporter() return s } @@ -59,29 +57,13 @@ func (s *BytesTrackStats) AddBytes(bytes uint64, isSend bool) { } else { s.recv.Add(bytes) } - - s.report(false) } -func (s *BytesTrackStats) Report() { - s.report(true) +func (s *BytesTrackStats) Stop() { + s.isStopped.Store(true) } -func (s *BytesTrackStats) report(force bool) { - now := time.Now() - if !force { - lr := s.lastStatsReport.Load().(*time.Time) - if time.Since(*lr) < statsReportInterval { - return - } - - if !s.lastStatsReport.CompareAndSwap(lr, &now) { - return - } - } else { - s.lastStatsReport.Store(&now) - } - +func (s *BytesTrackStats) report() { if recv := s.recv.Swap(0); recv > 0 { s.telemetry.TrackStats(StatsKeyForData(livekit.StreamType_UPSTREAM, s.pID, s.trackID), &livekit.AnalyticsStat{ Streams: []*livekit.AnalyticsStream{ @@ -99,6 +81,20 @@ func (s *BytesTrackStats) report(force bool) { } } +func (s *BytesTrackStats) reporter() { + ticker := time.NewTicker(config.TelemetryStatsUpdateInterval) + defer ticker.Stop() + + for !s.isStopped.Load() { + <-ticker.C + s.report() + } + + s.report() +} + +// ----------------------------------------------------------------------- + func BytesTrackIDForParticipantID(typ BytesTrackType, participantID livekit.ParticipantID) livekit.TrackID { return livekit.TrackID(fmt.Sprintf("%s_%s%s", utils.TrackPrefix, string(typ), participantID)) } diff --git a/pkg/telemetry/statsworker.go b/pkg/telemetry/statsworker.go index b86175368..d48a6d6d5 100644 --- a/pkg/telemetry/statsworker.go +++ b/pkg/telemetry/statsworker.go @@ -126,8 +126,6 @@ func (s *StatsWorker) ClosedAt() time.Time { return s.closedAt } -// ------------------------------------------------------------------------- - func (s *StatsWorker) collectStats( ts *timestamppb.Timestamp, streamType livekit.StreamType, @@ -151,6 +149,8 @@ func (s *StatsWorker) collectStats( return stats } +// ------------------------------------------------------------------------- + // create a single stream and single video layer post aggregation func coalesce(stats []*livekit.AnalyticsStat) *livekit.AnalyticsStat { if len(stats) == 0 {