diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..976d8fa Binary files /dev/null and b/.DS_Store differ diff --git a/README.md b/README.md index cf749ac..762349d 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ Version 2 of the SDK contains a small set of breaking changes. Read t ## Installation ```shell -go get github.com/livekit/server-sdk-go/v2 +go get github.com/tryiris-ai/livekit-server-sdk-go/v2 ``` Note: since v1.0 release, this package requires Go 1.18+ in order to build. @@ -32,7 +32,7 @@ Note: since v1.0 release, this package requires Go 1.18+ in order to build. import ( "time" - lksdk "github.com/livekit/server-sdk-go/v2" + lksdk "github.com/tryiris-ai/livekit-server-sdk-go/v2" "github.com/livekit/protocol/auth" ) @@ -56,7 +56,7 @@ RoomService gives you complete control over rooms and participants within them. ```go import ( - lksdk "github.com/livekit/server-sdk-go/v2" + lksdk "github.com/tryiris-ai/livekit-server-sdk-go/v2" livekit "github.com/livekit/protocol/livekit" ) @@ -110,7 +110,7 @@ The Real-time SDK gives you access programmatic access as a client enabling you ```go import ( - lksdk "github.com/livekit/server-sdk-go/v2" + lksdk "github.com/tryiris-ai/livekit-server-sdk-go/v2" ) func main() { @@ -210,7 +210,8 @@ if _, err = room.LocalParticipant.PublishTrack(track, &lksdk.TrackPublicationOpt } ``` -For a full working example, refer to [join.go](https://github.com/livekit/livekit-cli/blob/main/cmd/livekit-cli/join.go) in livekit-cli. +For a full working example, refer to [filesender](https://github.com/livekit/server-sdk-go/blob/main/examples/filesender/main.go). This +example sends all audio/video files in the current directory. ### Publish from other sources @@ -247,6 +248,14 @@ room, err := lksdk.ConnectToRoom(hostURL, lksdk.ConnectInfo{ }, lksdk.WithPacer(pf)) ``` +## Receiving tracks from Room + +With the Go SDK, you can accept media from the room. + +For a full working example, refer to [filesaver](https://github.com/livekit/server-sdk-go/blob/main/examples/filesaver/main.go). This +example saves the audio/video in the LiveKit room to the local disk. + + ## Receiving webhooks The Go SDK helps you to verify and decode webhook callbacks to ensure their authenticity. diff --git a/bootstrap.sh b/bootstrap.sh old mode 100755 new mode 100644 diff --git a/callback.go b/callback.go index fffb2eb..a2234d3 100644 --- a/callback.go +++ b/callback.go @@ -94,8 +94,17 @@ func (cb *ParticipantCallback) Merge(other *ParticipantCallback) { } } +type DisconnectionReason string + +const ( + LeaveRequested DisconnectionReason = "leave requested by room" + Failed DisconnectionReason = "connection to room failed" + NegotiationFailed DisconnectionReason = "negotiation failed" +) + type RoomCallback struct { OnDisconnected func() + OnDisconnectedWithReason func(reason DisconnectionReason) OnParticipantConnected func(*RemoteParticipant) OnParticipantDisconnected func(*RemoteParticipant) OnActiveSpeakersChanged func([]Participant) @@ -113,6 +122,7 @@ func NewRoomCallback() *RoomCallback { ParticipantCallback: *pc, OnDisconnected: func() {}, + OnDisconnectedWithReason: func(reason DisconnectionReason) {}, OnParticipantConnected: func(participant *RemoteParticipant) {}, OnParticipantDisconnected: func(participant *RemoteParticipant) {}, OnActiveSpeakersChanged: func(participants []Participant) {}, @@ -130,6 +140,9 @@ func (cb *RoomCallback) Merge(other *RoomCallback) { if other.OnDisconnected != nil { cb.OnDisconnected = other.OnDisconnected } + if other.OnDisconnectedWithReason != nil { + cb.OnDisconnectedWithReason = other.OnDisconnectedWithReason + } if other.OnParticipantConnected != nil { cb.OnParticipantConnected = other.OnParticipantConnected } diff --git a/engine.go b/engine.go index 74a38c5..d1ce9b9 100644 --- a/engine.go +++ b/engine.go @@ -18,6 +18,7 @@ import ( "sync" "time" + "github.com/pion/dtls/v2" "github.com/pion/webrtc/v3" "go.uber.org/atomic" "google.golang.org/protobuf/encoding/protojson" @@ -36,22 +37,23 @@ const ( ) type RTCEngine struct { - pclock sync.Mutex - publisher *PCTransport - subscriber *PCTransport - client *SignalClient - dclock sync.RWMutex - reliableDC *webrtc.DataChannel - lossyDC *webrtc.DataChannel - reliableDCSub *webrtc.DataChannel - lossyDCSub *webrtc.DataChannel - trackPublishedChan chan *livekit.TrackPublishedResponse - subscriberPrimary bool - hasConnected atomic.Bool - hasPublish atomic.Bool - closed atomic.Bool - reconnecting atomic.Bool - requiresFullReconnect atomic.Bool + pclock sync.Mutex + publisher *PCTransport + subscriber *PCTransport + client *SignalClient + dclock sync.RWMutex + reliableDC *webrtc.DataChannel + lossyDC *webrtc.DataChannel + reliableDCSub *webrtc.DataChannel + lossyDCSub *webrtc.DataChannel + trackPublishedChan chan *livekit.TrackPublishedResponse + subscriberPrimary bool + hasConnected atomic.Bool + hasPublish atomic.Bool + closed atomic.Bool + reconnecting atomic.Bool + requiresFullReconnect atomic.Bool + srtpProtectionProfiles []dtls.SRTPProtectionProfile url string token atomic.String @@ -60,7 +62,7 @@ type RTCEngine struct { JoinTimeout time.Duration // callbacks - OnDisconnected func() + OnDisconnected func(reason DisconnectionReason) OnMediaTrack func(track *webrtc.TrackRemote, receiver *webrtc.RTPReceiver) OnParticipantUpdate func([]*livekit.ParticipantInfo) OnSpeakersChanged func([]*livekit.SpeakerInfo) @@ -120,6 +122,7 @@ func (e *RTCEngine) Join(url string, token string, params *connectParams) (*live e.url = url e.token.Store(token) e.connParams = params + e.srtpProtectionProfiles = params.SRTPProtectionProfiles err = e.configure(res.IceServers, res.ClientConfiguration, proto.Bool(res.SubscriberPrimary)) if err != nil { @@ -205,7 +208,10 @@ func (e *RTCEngine) configure( clientConfig *livekit.ClientConfiguration, subscriberPrimary *bool) error { rtcICEServers := FromProtoIceServers(iceServers) - configuration := webrtc.Configuration{ICEServers: rtcICEServers} + configuration := webrtc.Configuration{ + ICEServers: rtcICEServers, + ICETransportPolicy: e.connParams.ICETransportPolicy, + } if clientConfig != nil && clientConfig.GetForceRelay() == livekit.ClientConfigSetting_ENABLED { configuration.ICETransportPolicy = webrtc.ICETransportPolicyRelay @@ -226,17 +232,21 @@ func (e *RTCEngine) configure( var err error if e.publisher, err = NewPCTransport(PCTransportParams{ - Configuration: configuration, - RetransmitBufferSize: e.connParams.RetransmitBufferSize, - Pacer: e.connParams.Pacer, - OnRTTUpdate: e.setRTT, - IsSender: true, + Configuration: configuration, + RetransmitBufferSize: e.connParams.RetransmitBufferSize, + Pacer: e.connParams.Pacer, + Interceptors: e.connParams.Interceptors, + OnRTTUpdate: e.setRTT, + IsSender: true, + OnNegotiationError: e.handleNegiationError, + SRTPProtectionProfiles: e.srtpProtectionProfiles, }); err != nil { return err } if e.subscriber, err = NewPCTransport(PCTransportParams{ Configuration: configuration, RetransmitBufferSize: e.connParams.RetransmitBufferSize, + OnNegotiationError: e.handleNegiationError, }); err != nil { return err } @@ -559,7 +569,7 @@ func (e *RTCEngine) handleDisconnect(fullReconnect bool) { } if e.OnDisconnected != nil { - e.OnDisconnected() + e.OnDisconnected(Failed) } }() } @@ -584,9 +594,11 @@ func (e *RTCEngine) resumeConnection() error { publisher := e.publisher e.pclock.Unlock() if sendOffer { - publisher.createAndSendOffer(&webrtc.OfferOptions{ + if err := publisher.createAndSendOffer(&webrtc.OfferOptions{ ICERestart: true, - }) + }); err != nil { + return err + } } if err = e.waitUntilConnected(); err != nil { @@ -642,7 +654,15 @@ func (e *RTCEngine) handleLeave(leave *livekit.LeaveRequest) { "canReconnect", leave.GetCanReconnect(), ) if e.OnDisconnected != nil { - e.OnDisconnected() + e.OnDisconnected(LeaveRequested) } } } + +func (e *RTCEngine) handleNegiationError(err error) { + logger.Errorw("negotiation error", err) + + if e.OnDisconnected != nil { + e.OnDisconnected(NegotiationFailed) + } +} diff --git a/examples/filesaver/main.go b/examples/filesaver/main.go index c9cc11a..f40e156 100644 --- a/examples/filesaver/main.go +++ b/examples/filesaver/main.go @@ -31,8 +31,8 @@ import ( "github.com/pion/webrtc/v3/pkg/media/oggwriter" "github.com/livekit/protocol/logger" - lksdk "github.com/livekit/server-sdk-go/v2" - "github.com/livekit/server-sdk-go/v2/pkg/samplebuilder" + lksdk "github.com/tryiris-ai/livekit-server-sdk-go/v2" + "github.com/tryiris-ai/livekit-server-sdk-go/v2/pkg/samplebuilder" ) var ( diff --git a/examples/filesender/main.go b/examples/filesender/main.go new file mode 100644 index 0000000..c6d5aa1 --- /dev/null +++ b/examples/filesender/main.go @@ -0,0 +1,97 @@ +// 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 main + +import ( + "flag" + "fmt" + "os" + "os/signal" + "strings" + "syscall" + "time" + + lksdk "github.com/tryiris-ai/livekit-server-sdk-go/v2" +) + +var ( + host, apiKey, apiSecret, roomName, identity string +) + +func init() { + flag.StringVar(&host, "host", "", "livekit server host") + flag.StringVar(&apiKey, "api-key", "", "livekit api key") + flag.StringVar(&apiSecret, "api-secret", "", "livekit api secret") + flag.StringVar(&roomName, "room-name", "", "room name") + flag.StringVar(&identity, "identity", "", "participant identity") +} + +func main() { + flag.Parse() + if host == "" || apiKey == "" || apiSecret == "" || roomName == "" || identity == "" { + fmt.Println("invalid arguments.") + return + } + room, err := lksdk.ConnectToRoom(host, lksdk.ConnectInfo{ + APIKey: apiKey, + APISecret: apiSecret, + RoomName: roomName, + ParticipantIdentity: identity, + }, &lksdk.RoomCallback{}, lksdk.WithAutoSubscribe(false)) + if err != nil { + panic(err) + } + + sigChan := make(chan os.Signal, 1) + signal.Notify(sigChan, syscall.SIGINT) + + files, err := os.ReadDir(".") + if err != nil { + panic(err) + } + + for _, file := range files { + if file.IsDir() { + continue + } else if !strings.HasSuffix(file.Name(), ".h264") && !strings.HasSuffix(file.Name(), ".ivf") && !strings.HasSuffix(file.Name(), ".ogg") { + continue + } + + frameDuration := 33 * time.Millisecond + if strings.HasSuffix(file.Name(), ".ogg") { + frameDuration = 20 * time.Millisecond + } + + track, err := lksdk.NewLocalFileTrack(file.Name(), + lksdk.ReaderTrackWithFrameDuration(frameDuration), + lksdk.ReaderTrackWithOnWriteComplete(func() { fmt.Println("track finished") }), + ) + if err != nil { + panic(err) + } + + if _, err = room.LocalParticipant.PublishTrack(track, &lksdk.TrackPublicationOptions{ + VideoWidth: 640, + VideoHeight: 480, + Name: file.Name(), + }); err != nil { + panic(err) + } + + } + + <-sigChan + room.Disconnect() +} diff --git a/go.mod b/go.mod index 6908cad..d3ce216 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module github.com/livekit/server-sdk-go/v2 +module github.com/tryiris-ai/livekit-server-sdk-go/v2 go 1.21 @@ -8,19 +8,19 @@ require ( github.com/go-logr/stdr v1.2.2 github.com/gorilla/websocket v1.5.1 github.com/livekit/mediatransportutil v0.0.0-20240302142739-1c3dd691a1b8 - github.com/livekit/protocol v1.11.0 + github.com/livekit/protocol v1.12.0 github.com/magefile/mage v1.15.0 github.com/pion/dtls/v2 v2.2.10 - github.com/pion/interceptor v0.1.25 + github.com/pion/interceptor v0.1.27 github.com/pion/rtcp v1.2.14 - github.com/pion/rtp v1.8.3 - github.com/pion/sdp/v3 v3.0.6 - github.com/pion/webrtc/v3 v3.2.28 + github.com/pion/rtp v1.8.5 + github.com/pion/sdp/v3 v3.0.9 + github.com/pion/webrtc/v3 v3.2.38 github.com/stretchr/testify v1.9.0 github.com/twitchtv/twirp v8.1.3+incompatible go.uber.org/atomic v1.11.0 - golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 - google.golang.org/protobuf v1.32.0 + golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 + google.golang.org/protobuf v1.33.0 ) require ( @@ -31,9 +31,9 @@ require ( github.com/eapache/channels v1.1.0 // indirect github.com/eapache/queue v1.1.0 // indirect github.com/frostbyte73/core v0.0.10 // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/gammazero/deque v0.2.1 // indirect - github.com/go-jose/go-jose/v3 v3.0.2 // indirect - github.com/golang/protobuf v1.5.3 // indirect + github.com/go-jose/go-jose/v3 v3.0.3 // indirect github.com/google/uuid v1.6.0 // indirect github.com/jxskiss/base62 v1.1.0 // indirect github.com/klauspost/compress v1.17.6 // indirect @@ -41,16 +41,15 @@ require ( github.com/lithammer/shortuuid/v4 v4.0.0 // indirect github.com/livekit/mageutil v0.0.0-20230125210925-54e8a70427c1 // indirect github.com/livekit/psrpc v0.5.3-0.20240228172457-3724cb4adbc4 // indirect - github.com/mackerelio/go-osstat v0.2.4 // indirect github.com/nats-io/nats.go v1.31.0 // indirect github.com/nats-io/nkeys v0.4.6 // indirect github.com/nats-io/nuid v1.0.1 // indirect github.com/pion/datachannel v1.5.5 // indirect - github.com/pion/ice/v2 v2.3.13 // indirect + github.com/pion/ice/v2 v2.3.15 // indirect github.com/pion/logging v0.2.2 // indirect github.com/pion/mdns v0.0.12 // indirect github.com/pion/randutil v0.1.0 // indirect - github.com/pion/sctp v1.8.12 // indirect + github.com/pion/sctp v1.8.16 // indirect github.com/pion/srtp/v2 v2.0.18 // indirect github.com/pion/stun v0.6.1 // indirect github.com/pion/transport/v2 v2.2.4 // indirect @@ -60,17 +59,17 @@ require ( github.com/prometheus/client_model v0.5.0 // indirect github.com/prometheus/common v0.48.0 // indirect github.com/prometheus/procfs v0.12.0 // indirect - github.com/puzpuzpuz/xsync v1.5.2 // indirect + github.com/puzpuzpuz/xsync/v3 v3.1.0 // indirect github.com/redis/go-redis/v9 v9.5.1 // indirect github.com/zeebo/xxh3 v1.0.2 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect - golang.org/x/crypto v0.19.0 // indirect - golang.org/x/net v0.21.0 // indirect - golang.org/x/sync v0.6.0 // indirect - golang.org/x/sys v0.17.0 // indirect + golang.org/x/crypto v0.22.0 // indirect + golang.org/x/net v0.24.0 // indirect + golang.org/x/sync v0.7.0 // indirect + golang.org/x/sys v0.19.0 // indirect golang.org/x/text v0.14.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240221002015-b0ce06bbee7c // indirect - google.golang.org/grpc v1.62.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de // indirect + google.golang.org/grpc v1.63.2 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 152e6a3..d411d0c 100644 --- a/go.sum +++ b/go.sum @@ -19,33 +19,17 @@ github.com/eapache/queue v1.1.0 h1:YOEu7KNc61ntiQlcEeUIoDTJ2o8mQznoNvUhiigpIqc= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= github.com/frostbyte73/core v0.0.10 h1:D4DQXdPb8ICayz0n75rs4UYTXrUSdxzUfeleuNJORsU= github.com/frostbyte73/core v0.0.10/go.mod h1:XsOGqrqe/VEV7+8vJ+3a8qnCIXNbKsoEiu/czs7nrcU= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= 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/go-jose/go-jose/v3 v3.0.2 h1:2Edjn8Nrb44UvTdp84KU0bBPs1cO7noRCybtS3eJEUQ= -github.com/go-jose/go-jose/v3 v3.0.2/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ= +github.com/go-jose/go-jose/v3 v3.0.3 h1:fFKWeig/irsp7XD2zBxvnmA/XaRWp5V3CBsZXJF7G7k= +github.com/go-jose/go-jose/v3 v3.0.3/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= @@ -55,7 +39,6 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= -github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= 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.6 h1:60eq2E/jlfwQXtvZEeBUYADs+BwKBWURIY+Gj2eRGjI= @@ -75,12 +58,10 @@ 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-20240302142739-1c3dd691a1b8 h1:xawydPEACNO5Ncs2LgioTjWghXQ0eUN1q1RnVUUyVnI= github.com/livekit/mediatransportutil v0.0.0-20240302142739-1c3dd691a1b8/go.mod h1:jwKUCmObuiEDH0iiuJHaGMXwRs3RjrB4G6qqgkr/5oE= -github.com/livekit/protocol v1.11.0 h1:3V1j0EGfh5T8A/rb/H7kB+ak9TINA8a/2jXpH+emLsg= -github.com/livekit/protocol v1.11.0/go.mod h1:XpJ2t2wFnnQghPpkxXAzMZhYMDnm8wWxdxYJK4fP9gM= +github.com/livekit/protocol v1.12.0 h1:B7qsqq5xf9MmyG9WEk9/gMsfMVXuyLNxX5cO6TQil6s= +github.com/livekit/protocol v1.12.0/go.mod h1:G7Pa985GhZv2MCC3UnUocBhZfi3DsWA6WmlSkkpQYTM= github.com/livekit/psrpc v0.5.3-0.20240228172457-3724cb4adbc4 h1:253WtQ2VGVHzIIzW9MUZj7vUDDILESU3zsEbiRdxYF0= github.com/livekit/psrpc v0.5.3-0.20240228172457-3724cb4adbc4/go.mod h1:CQUBSPfYYAaevg1TNCc6/aYsa8DJH4jSRFdCeSZk5u0= -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= github.com/magefile/mage v1.15.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A= github.com/nats-io/nats.go v1.31.0 h1:/WFBHEc/dOKBF6qf1TZhrdEfTmOZ5JzdJ+Y3m6Y/p7E= @@ -89,42 +70,33 @@ github.com/nats-io/nkeys v0.4.6 h1:IzVe95ru2CT6ta874rt9saQRkWfe2nFj1NtvYSLqMzY= github.com/nats-io/nkeys v0.4.6/go.mod h1:4DxZNzenSVd1cYQoAa8948QY3QDjrHfcfVADymtkpts= github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= -github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= -github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= -github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= -github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= -github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= -github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= 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/go.mod h1:8WiMkebSHFD0T+dIU+UeBaoV7kDhOW5oDCzZ7WZ/F9s= github.com/pion/dtls/v2 v2.2.10 h1:u2Axk+FyIR1VFTPurktB+1zoEPGIW3bmyj3LEFrXjAA= github.com/pion/dtls/v2 v2.2.10/go.mod h1:d9SYc9fch0CqK90mRk1dC7AkzzpwJj6u2GU3u+9pqFE= -github.com/pion/ice/v2 v2.3.13 h1:xOxP+4V9nSDlUaGFRf/LvAuGHDXRcjIdsbbXPK/w7c8= -github.com/pion/ice/v2 v2.3.13/go.mod h1:KXJJcZK7E8WzrBEYnV4UtqEZsGeWfHxsNqhVcVvgjxw= -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/ice/v2 v2.3.15 h1:oCGVqnd6OWmJr4I6eQwSWn8VJDF45wIXFTjV3tyyris= +github.com/pion/ice/v2 v2.3.15/go.mod h1:KXJJcZK7E8WzrBEYnV4UtqEZsGeWfHxsNqhVcVvgjxw= +github.com/pion/interceptor v0.1.27 h1:mZ01OiGiukwRxezmDGzYjjokCVlDOk4T6BfaL5qrtGo= +github.com/pion/interceptor v0.1.27/go.mod h1:/vVaqLwDjGv4GRbgmChIKZIT5EXFDijwmj4WmIYy9bI= github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY= github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms= github.com/pion/mdns v0.0.12 h1:CiMYlY+O0azojWDmxdNr7ADGrnZ+V6Ilfner+6mSVK8= github.com/pion/mdns v0.0.12/go.mod h1:VExJjv8to/6Wqm1FXK+Ii/Z9tsVk/F5sD/N70cnYFbk= 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/go.mod h1:ztfEwXZNLGyF1oQDttz/ZKIBaeeg/oWbRYqzBM9TL1I= github.com/pion/rtcp v1.2.12/go.mod h1:sn6qjxvnwyAkkPzPULIbVqSKI5Dv54Rv7VG0kNxh9L4= github.com/pion/rtcp v1.2.14 h1:KCkGV3vJ+4DAJmvP0vaQShsb0xkRfWkO540Gy102KyE= github.com/pion/rtcp v1.2.14/go.mod h1:sn6qjxvnwyAkkPzPULIbVqSKI5Dv54Rv7VG0kNxh9L4= -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/rtp v1.8.4/go.mod h1:pBGHaFt/yW7bf1jjWAoUjpSNoDnw98KTMg+jWWvziqU= +github.com/pion/rtp v1.8.5 h1:uYzINfaK+9yWs7r537z/Rc1SvT8ILjBcmDOpJcTB+OU= +github.com/pion/rtp v1.8.5/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.12 h1:2VX50pedElH+is6FI+OKyRTeN5oy4mrk2HjnGa3UCmY= -github.com/pion/sctp v1.8.12/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/sctp v1.8.16 h1:PKrMs+o9EMLRvFfXq59WFsC+V8mN1wnKzqrv+3D/gYY= +github.com/pion/sctp v1.8.16/go.mod h1:P6PbDVA++OJMrVNg2AL3XtYHV4uD6dvfyOovCgMs0PE= +github.com/pion/sdp/v3 v3.0.9 h1:pX++dCHoHUwq43kuwf3PyJfHlwIj4hXA7Vrifiq0IJY= +github.com/pion/sdp/v3 v3.0.9/go.mod h1:B5xmvENq5IXJimIO4zfp6LAe1fD9N+kFv+V/1lOdz8M= 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= @@ -136,12 +108,13 @@ github.com/pion/transport/v2 v2.2.2/go.mod h1:OJg3ojoBJopjEeECq2yJdXH9YVrUJ1uQ++ github.com/pion/transport/v2 v2.2.3/go.mod h1:q2U/tf9FEfnSBGSW6w5Qp5PFWRLRj3NjLhCCgpRK4p0= github.com/pion/transport/v2 v2.2.4 h1:41JJK6DZQYSeVLxILA2+F4ZkKb4Xd/tFJZRFZQ9QAlo= github.com/pion/transport/v2 v2.2.4/go.mod h1:q2U/tf9FEfnSBGSW6w5Qp5PFWRLRj3NjLhCCgpRK4p0= -github.com/pion/transport/v3 v3.0.1 h1:gDTlPJwROfSfz6QfSi0ZmeCSkFcnWWiiR9ES0ouANiM= github.com/pion/transport/v3 v3.0.1/go.mod h1:UY7kiITrlMv7/IKgd5eTUcaahZx5oUN3l9SzK5f5xE0= +github.com/pion/transport/v3 v3.0.2 h1:r+40RJR25S9w3jbA6/5uEPTzcdn7ncyU44RWCbHkLg4= +github.com/pion/transport/v3 v3.0.2/go.mod h1:nIToODoOlb5If2jF9y2Igfx3PFYWfuXi37m0IlWa/D0= github.com/pion/turn/v2 v2.1.3 h1:pYxTVWG2gpC97opdRc5IGsQ1lJ9O/IlNhkzj7MMrGAA= github.com/pion/turn/v2 v2.1.3/go.mod h1:huEpByKKHix2/b9kmTAM3YoX6MKP+/D//0ClgUYR2fY= -github.com/pion/webrtc/v3 v3.2.28 h1:ienStxZ6HcjtH2UlmnFpMM0loENiYjaX437uIUpQSKo= -github.com/pion/webrtc/v3 v3.2.28/go.mod h1:PNRCEuQlibrmuBhOTnol9j6KkIbUG11aHLEfNpUYey0= +github.com/pion/webrtc/v3 v3.2.38 h1:oA52VJAJhOjSi1JpKjf0CM+cCiZ3b7jBxvsoOiajeDU= +github.com/pion/webrtc/v3 v3.2.38/go.mod h1:AQ8p56OLbm3MjhYovYdgPuyX6oc+JcKx/HFoCGFcYzA= 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= @@ -154,17 +127,16 @@ github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSz github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc= github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= -github.com/puzpuzpuz/xsync v1.5.2 h1:yRAP4wqSOZG+/4pxJ08fPTwrfL0IzE/LKQ/cw509qGY= -github.com/puzpuzpuz/xsync v1.5.2/go.mod h1:K98BYhX3k1dQ2M63t1YNVDanbwUPmBCAhNmVrrxfiGg= +github.com/puzpuzpuz/xsync/v3 v3.1.0 h1:EewKT7/LNac5SLiEblJeUu8z5eERHrmRLnMQL2d7qX4= +github.com/puzpuzpuz/xsync/v3 v3.1.0/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA= github.com/redis/go-redis/v9 v9.5.1 h1:H1X4D3yHPaYrkL5X06Wh6xNVM/pX0Ft4RV0vMGvLBh8= github.com/redis/go-redis/v9 v9.5.1/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M= github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= -github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= @@ -175,7 +147,6 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/twitchtv/twirp v8.1.3+incompatible h1:+F4TdErPgSUbMZMwp13Q/KgDVuI7HJXP61mNV3/7iuU= github.com/twitchtv/twirp v8.1.3+incompatible/go.mod h1:RRJoFSAmTEh2weEqWtpPE3vFK5YBhA6bqp2l1kfCC5A= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ= github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= @@ -190,27 +161,20 @@ go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN8 go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= 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.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= -golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= -golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 h1:LfspQV/FYTatPTr/3HzIcmiUFH7PGP+OQ6mgDYo3yuQ= -golang.org/x/exp v0.0.0-20240222234643-814bf88cf225/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= +golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= +golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 h1:aAcj0Da7eBAtrTp03QXWvm88pSyOt+UgdZw2BFZ+lEw= +golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8/go.mod h1:CQ1k9gNrJ50XIzaKCRR2hssIjF07kZFEiieALBM/ARQ= 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/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= @@ -219,26 +183,15 @@ golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= 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.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= -golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= -golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= +golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= -golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -251,8 +204,9 @@ 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.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= +golang.org/x/sys v0.19.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= @@ -265,7 +219,6 @@ golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= @@ -276,37 +229,19 @@ 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-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/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= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240221002015-b0ce06bbee7c h1:NUsgEN92SQQqzfA+YtqYNqYmB3DMMYLlIwUZAQFVFbo= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240221002015-b0ce06bbee7c/go.mod h1:H4O17MA/PE9BsGx3w+a+W2VOLLD1Qf7oJneAoU6WktY= -google.golang.org/grpc v1.62.0 h1:HQKZ/fa1bXkX1oFOvSjmZEUL8wLSaZTjCcLAlmZRtdk= -google.golang.org/grpc v1.62.0/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= -google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de h1:cZGRis4/ot9uVm639a+rHCUaG0JJHEsdyzSQTMX+suY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:H4O17MA/PE9BsGx3w+a+W2VOLLD1Qf7oJneAoU6WktY= +google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM= +google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/integration_test.go b/integration_test.go index 01e256f..50326a4 100644 --- a/integration_test.go +++ b/integration_test.go @@ -32,7 +32,7 @@ import ( "github.com/livekit/protocol/livekit" - "github.com/livekit/server-sdk-go/v2/pkg/interceptor" + "github.com/tryiris-ai/livekit-server-sdk-go/v2/pkg/interceptor" ) // The integration test of the SDK. can't run this test standalone, should be run with `mage test` @@ -129,8 +129,8 @@ func TestJoin(t *testing.T) { require.NotNil(t, serverInfo) require.Equal(t, serverInfo.Edition, livekit.ServerInfo_Standard) - pub.LocalParticipant.PublishDataPacket(UserData([]byte("test"))) - pub.LocalParticipant.PublishDataPacket(&livekit.SipDTMF{Digit: "#"}) + pub.LocalParticipant.PublishDataPacket(UserData([]byte("test")), WithDataPublishReliable(true)) + pub.LocalParticipant.PublishDataPacket(&livekit.SipDTMF{Digit: "#"}, WithDataPublishReliable(true)) localPub := pubNullTrack(t, pub, audioTrackName) require.Equal(t, localPub.Name(), audioTrackName) @@ -210,7 +210,7 @@ func TestForceTLS(t *testing.T) { require.NoError(t, err) // ensure publisher connected - pub.LocalParticipant.PublishDataPacket(UserData([]byte("test"))) + pub.LocalParticipant.PublishDataPacket(UserData([]byte("test")), WithDataPublishReliable(true)) pub.Simulate(SimulateForceTLS) require.Eventually(t, func() bool { return reconnected.Load() && pub.engine.ensurePublisherConnected(true) == nil }, 15*time.Second, 100*time.Millisecond) @@ -256,7 +256,7 @@ func TestSubscribeMutedTrack(t *testing.T) { var trackReceived atomic.Int32 var pubTrackMuted sync.WaitGroup - require.NoError(t, pub.LocalParticipant.PublishDataPacket(UserData([]byte("test")))) + require.NoError(t, pub.LocalParticipant.PublishDataPacket(UserData([]byte("test"))), WithDataPublishReliable(true)) pubMuteTrack := func(t *testing.T, room *Room, name string, codec webrtc.RTPCodecCapability) *LocalTrackPublication { pubTrackMuted.Add(1) diff --git a/localparticipant.go b/localparticipant.go index 699fe8e..a7ec412 100644 --- a/localparticipant.go +++ b/localparticipant.go @@ -119,7 +119,7 @@ func (p *LocalParticipant) PublishTrack(track webrtc.TrackLocal, opts *TrackPubl publisher.Negotiate() - logger.Infow("published track", "name", opts.Name, "source", opts.Source.String()) + logger.Infow("published track", "name", opts.Name, "source", opts.Source.String(), "trackID", pubRes.Track.Sid) return pub, nil } @@ -139,9 +139,12 @@ func (p *LocalParticipant) PublishSimulcastTrack(tracks []*LocalTrack, opts *Tra } } + tracksCopy := make([]*LocalTrack, len(tracks)) + copy(tracksCopy, tracks) + // tracks should be low to high - sort.Slice(tracks, func(i, j int) bool { - return tracks[i].videoLayer.Width < tracks[j].videoLayer.Width + sort.Slice(tracksCopy, func(i, j int) bool { + return tracksCopy[i].videoLayer.Width < tracksCopy[j].videoLayer.Width }) if opts == nil { @@ -152,13 +155,13 @@ func (p *LocalParticipant) PublishSimulcastTrack(tracks []*LocalTrack, opts *Tra opts.Source = livekit.TrackSource_CAMERA } - mainTrack := tracks[len(tracks)-1] + mainTrack := tracksCopy[len(tracksCopy)-1] pub := NewLocalTrackPublication(KindFromRTPType(mainTrack.Kind()), nil, *opts, p.engine.client) pub.onMuteChanged = p.onTrackMuted var layers []*livekit.VideoLayer - for _, st := range tracks { + for _, st := range tracksCopy { layers = append(layers, st.videoLayer) } err := p.engine.client.SendRequest(&livekit.SignalRequest{ @@ -197,7 +200,7 @@ func (p *LocalParticipant) PublishSimulcastTrack(tracks []*LocalTrack, opts *Tra publishPC := publisher.PeerConnection() var transceiver *webrtc.RTPTransceiver var sender *webrtc.RTPSender - for idx, st := range tracks { + for idx, st := range tracksCopy { if idx == 0 { transceiver, err = publishPC.AddTransceiverFromTrack(st, webrtc.RTPTransceiverInit{ Direction: webrtc.RTPTransceiverDirectionSendonly, @@ -221,7 +224,7 @@ func (p *LocalParticipant) PublishSimulcastTrack(tracks []*LocalTrack, opts *Tra publisher.Negotiate() - logger.Infow("published simulcast track", "name", opts.Name, "source", opts.Source.String()) + logger.Infow("published simulcast track", "name", opts.Name, "source", opts.Source.String(), "trackID", pubRes.Track.Sid) return pub, nil } @@ -289,16 +292,12 @@ func (p *LocalParticipant) publishData(kind livekit.DataPacket_Kind, dataPacket // // Messages are sent via a LOSSY channel by default, see WithDataPublishReliable for sending reliable data. // -// Deprecated: Use PublishDataPacket with UserData instead. Note that it sends reliable packets by default. +// Deprecated: Use PublishDataPacket with UserData instead. func (p *LocalParticipant) PublishData(payload []byte, opts ...DataPublishOption) error { options := &dataPublishOptions{} for _, opt := range opts { opt(options) } - if options.Reliable == nil { - // Old logic sends packets as lossy by default. - opts = append(opts, WithDataPublishReliable(false)) - } return p.PublishDataPacket(UserData(payload), opts...) } @@ -342,7 +341,7 @@ func (p *UserDataPacket) ToProto() *livekit.DataPacket { // By default, the message can be received by all participants in a room, // see WithDataPublishDestination for choosing specific participants. // -// Messages are sent via a RELIABLE channel, see WithDataPublishReliable for sending lossy data. +// Messages are sent via UDP and offer no delivery guarantees, see WithDataPublishReliable for sending data reliably (with retries). func (p *LocalParticipant) PublishDataPacket(pck DataPacket, opts ...DataPublishOption) error { options := &dataPublishOptions{} for _, opt := range opts { @@ -354,11 +353,11 @@ func (p *LocalParticipant) PublishDataPacket(pck DataPacket, opts ...DataPublish u.User.Topic = proto.String(options.Topic) } } - // New logic sends packets as reliable by default. + // This matches the default value of Kind on protobuf level. - kind := livekit.DataPacket_RELIABLE - if options.Reliable != nil && !*options.Reliable { - kind = livekit.DataPacket_LOSSY + kind := livekit.DataPacket_LOSSY + if options.Reliable != nil && *options.Reliable { + kind = livekit.DataPacket_RELIABLE } //lint:ignore SA1019 backward compatibility dataPacket.Kind = kind @@ -402,6 +401,8 @@ func (p *LocalParticipant) UnpublishTrack(sid string) error { pub.CloseTrack() + logger.Infow("unpublished track", "name", pub.Name(), "sid", sid) + return err } diff --git a/localtrack.go b/localtrack.go index 8d5a1d7..3631151 100644 --- a/localtrack.go +++ b/localtrack.go @@ -64,6 +64,7 @@ type LocalTrack struct { clockRate float64 bound atomic.Bool lock sync.RWMutex + writeStartupLock sync.Mutex audioLevelID uint8 sdesMidID uint8 sdesRtpStreamID uint8 @@ -75,6 +76,7 @@ type LocalTrack struct { muted atomic.Bool cancelWrite func() + writeClosed chan struct{} provider SampleProvider onBind func() onUnbind func() @@ -482,6 +484,13 @@ func (s *LocalTrack) Close() error { return nil } +func (s *LocalTrack) SSRC() webrtc.SSRC { + s.lock.Lock() + defer s.lock.Unlock() + + return s.ssrc +} + func (s *LocalTrack) setMuted(muted bool) { s.muted.Store(muted) } @@ -526,16 +535,30 @@ func (s *LocalTrack) rtcpWorker(rtcpReader interceptor.RTCPReader) { } func (s *LocalTrack) writeWorker(provider SampleProvider, onComplete func()) { - if s.cancelWrite != nil { - s.cancelWrite() + s.writeStartupLock.Lock() + + s.lock.RLock() + previousCancel := s.cancelWrite + previousWriteClosed := s.writeClosed + s.lock.RUnlock() + + if previousCancel != nil { + previousCancel() + // wait for previous write to finish to prevent multi-threaded provider reading + <-previousWriteClosed } - var ctx context.Context + s.lock.Lock() + var ctx context.Context ctx, s.cancelWrite = context.WithCancel(context.Background()) + writeClosed := make(chan struct{}) + s.writeClosed = writeClosed s.lock.Unlock() + s.writeStartupLock.Unlock() if onComplete != nil { defer onComplete() } + defer close(writeClosed) audioProvider, isAudioProvider := provider.(AudioSampleProvider) @@ -544,6 +567,7 @@ func (s *LocalTrack) writeWorker(provider SampleProvider, onComplete func()) { defer ticker.Stop() for { + // Be mindful that NextSample is not thread-safe sample, err := provider.NextSample(ctx) if err == io.EOF { return @@ -598,6 +622,8 @@ func payloaderForCodec(codec webrtc.RTPCodecCapability) (rtp.Payloader, error) { }, nil case strings.ToLower(webrtc.MimeTypeVP9): return &codecs.VP9Payloader{}, nil + case strings.ToLower(webrtc.MimeTypeAV1): + return &codecs.AV1Payloader{}, nil case strings.ToLower(webrtc.MimeTypeG722): return &codecs.G722Payloader{}, nil case strings.ToLower(webrtc.MimeTypePCMU), strings.ToLower(webrtc.MimeTypePCMA): diff --git a/magefile.go b/magefile.go index 3087a08..e96ccf5 100644 --- a/magefile.go +++ b/magefile.go @@ -29,6 +29,8 @@ import ( var Default = Build +const livekitServerVersion = "master" + func Build() error { fmt.Println("building...") cmd := exec.Command("go", "build", ".") @@ -122,7 +124,7 @@ logging: for p := 30000; p <= 30020; p++ { parameters = append(parameters, fmt.Sprintf("-p%d:%d/udp", p, p)) } - parameters = append(parameters, []string{`--name`, `livekit-server`, `livekit/livekit-server`}...) + parameters = append(parameters, []string{`--name`, `livekit-server`, `livekit/livekit-server:` + livekitServerVersion}...) if addresses, _ := getLocalIPAddresses(); len(addresses) > 0 { fmt.Println("set node ip", addresses[0]) parameters = append(parameters, `--node-ip`, addresses[0]) diff --git a/pkg/jitter/buffer.go b/pkg/jitter/buffer.go index 299eb86..930f5e1 100644 --- a/pkg/jitter/buffer.go +++ b/pkg/jitter/buffer.go @@ -403,10 +403,13 @@ func (b *Buffer) drop() { for b.head != nil && !b.head.start && before32(b.head.packet.Timestamp-b.maxSampleSize, b.minTS) { dropped = true b.packetsDropped++ + b.prevSN = b.head.packet.SequenceNumber - 1 b.dropHead() } - b.prevSN = b.head.packet.SequenceNumber - 1 + if b.head != nil { + b.prevSN = b.head.packet.SequenceNumber - 1 + } } for c := b.head; c != nil; { diff --git a/room.go b/room.go index 84f302b..206fb23 100644 --- a/room.go +++ b/room.go @@ -20,6 +20,8 @@ import ( "strings" "sync" + "github.com/pion/dtls/v2" + "github.com/pion/interceptor" "github.com/pion/rtcp" "github.com/pion/webrtc/v3" "golang.org/x/exp/maps" @@ -81,6 +83,12 @@ type connectParams struct { RetransmitBufferSize uint16 Pacer pacer.Factory + + Interceptors []interceptor.Factory + + ICETransportPolicy webrtc.ICETransportPolicy + + SRTPProtectionProfiles []dtls.SRTPProtectionProfile } type ConnectOption func(*connectParams) @@ -105,6 +113,24 @@ func WithPacer(pacer pacer.Factory) ConnectOption { } } +func WithInterceptors(interceptors []interceptor.Factory) ConnectOption { + return func(p *connectParams) { + p.Interceptors = interceptors + } +} + +func WithICETransportPolicy(iceTransportPolicy webrtc.ICETransportPolicy) ConnectOption { + return func(p *connectParams) { + p.ICETransportPolicy = iceTransportPolicy + } +} + +func WithSRTPProtectionProfiles(profiles []dtls.SRTPProtectionProfile) ConnectOption { + return func(p *connectParams) { + p.SRTPProtectionProfiles = profiles + } +} + type PLIWriter func(webrtc.SSRC) type Room struct { @@ -117,6 +143,7 @@ type Room struct { remoteParticipants map[livekit.ParticipantIdentity]*RemoteParticipant sidToIdentity map[livekit.ParticipantID]livekit.ParticipantIdentity + sidDefers map[livekit.ParticipantID][]func(p *RemoteParticipant) metadata string activeSpeakers []Participant serverInfo *livekit.ServerInfo @@ -131,6 +158,7 @@ func NewRoom(callback *RoomCallback) *Room { engine: engine, remoteParticipants: make(map[livekit.ParticipantIdentity]*RemoteParticipant), sidToIdentity: make(map[livekit.ParticipantID]livekit.ParticipantIdentity), + sidDefers: make(map[livekit.ParticipantID][]func(*RemoteParticipant)), callback: NewRoomCallback(), sidReady: make(chan struct{}), } @@ -251,6 +279,32 @@ func (r *Room) Disconnect() { r.cleanup() } +func (r *Room) deferParticipantUpdate(sid livekit.ParticipantID, fnc func(p *RemoteParticipant)) { + r.lock.Lock() + defer r.lock.Unlock() + r.sidDefers[sid] = append(r.sidDefers[sid], fnc) +} + +func (r *Room) runParticipantDefers(sid livekit.ParticipantID, p *RemoteParticipant) { + r.lock.RLock() + has := len(r.sidDefers[sid]) != 0 + r.lock.RUnlock() + if !has { + return + } + r.lock.Lock() + fncs := r.sidDefers[sid] + delete(r.sidDefers, sid) + r.lock.Unlock() + if len(fncs) == 0 { + return + } + logger.Infow("running deferred updates for participant", "participantID", sid, "updates", len(fncs)) + for _, fnc := range fncs { + fnc(p) + } +} + func (r *Room) GetParticipantByIdentity(identity string) *RemoteParticipant { r.lock.RLock() defer r.lock.RUnlock() @@ -299,13 +353,14 @@ func (r *Room) ServerInfo() *livekit.ServerInfo { func (r *Room) addRemoteParticipant(pi *livekit.ParticipantInfo, updateExisting bool) *RemoteParticipant { r.lock.Lock() + defer r.lock.Unlock() rp, ok := r.remoteParticipants[livekit.ParticipantIdentity(pi.Identity)] if ok { if updateExisting { rp.updateInfo(pi) r.sidToIdentity[livekit.ParticipantID(pi.Sid)] = livekit.ParticipantIdentity(pi.Identity) } - r.lock.Unlock() + return rp } @@ -319,8 +374,6 @@ func (r *Room) addRemoteParticipant(pi *livekit.ParticipantInfo, updateExisting }) r.remoteParticipants[livekit.ParticipantIdentity(pi.Identity)] = rp r.sidToIdentity[livekit.ParticipantID(pi.Sid)] = livekit.ParticipantIdentity(pi.Identity) - r.lock.Unlock() - return rp } @@ -333,17 +386,22 @@ func (r *Room) handleMediaTrack(track *webrtc.TrackRemote, receiver *webrtc.RTPR // backwards compatibility trackID = streamID } + update := func(p *RemoteParticipant) { + p.addSubscribedMediaTrack(track, trackID, receiver) + } rp := r.GetParticipantBySID(participantID) if rp == nil { - logger.Errorw("could not find participant", nil, "participantID", participantID) + logger.Infow("could not find participant, deferring track update", "participantID", participantID) + r.deferParticipantUpdate(livekit.ParticipantID(participantID), update) return } - rp.addSubscribedMediaTrack(track, trackID, receiver) + update(rp) } -func (r *Room) handleDisconnect() { +func (r *Room) handleDisconnect(reason DisconnectionReason) { r.callback.OnDisconnected() + r.callback.OnDisconnectedWithReason(reason) r.cleanup() } @@ -420,7 +478,17 @@ func (r *Room) handleParticipantUpdate(participants []*livekit.ParticipantInfo) rp = r.addRemoteParticipant(pi, true) go r.callback.OnParticipantConnected(rp) } else { + oldSid := livekit.ParticipantID(rp.SID()) rp.updateInfo(pi) + newSid := livekit.ParticipantID(rp.SID()) + if oldSid != newSid { + logger.Infow("participant sid update", "sid-old", oldSid, "sid-new", newSid, "identity", rp.Identity()) + r.lock.Lock() + delete(r.sidToIdentity, oldSid) + r.sidToIdentity[newSid] = livekit.ParticipantIdentity(rp.Identity()) + r.lock.Unlock() + r.runParticipantDefers(newSid, rp) + } } } } @@ -429,6 +497,7 @@ func (r *Room) handleParticipantDisconnect(p *RemoteParticipant) { r.lock.Lock() delete(r.remoteParticipants, livekit.ParticipantIdentity(p.Identity())) delete(r.sidToIdentity, livekit.ParticipantID(p.SID())) + delete(r.sidDefers, livekit.ParticipantID(p.SID())) r.lock.Unlock() p.unpublishAllTracks() diff --git a/transport.go b/transport.go index 11d6470..0bbc2a1 100644 --- a/transport.go +++ b/transport.go @@ -25,13 +25,14 @@ import ( "github.com/pion/dtls/v2" "github.com/pion/interceptor" "github.com/pion/interceptor/pkg/nack" + "github.com/pion/interceptor/pkg/twcc" "github.com/pion/sdp/v3" "github.com/pion/webrtc/v3" lkinterceptor "github.com/livekit/mediatransportutil/pkg/interceptor" "github.com/livekit/mediatransportutil/pkg/pacer" lksdp "github.com/livekit/protocol/sdp" - sdkinterceptor "github.com/livekit/server-sdk-go/v2/pkg/interceptor" + sdkinterceptor "github.com/tryiris-ai/livekit-server-sdk-go/v2/pkg/interceptor" ) const ( @@ -58,6 +59,7 @@ type PCTransport struct { rttFromXR atomic.Bool onRemoteDescriptionSettled func() error + onNegotiationError func(err error) onRTTUpdate func(rtt uint32) OnOffer func(description webrtc.SessionDescription) @@ -68,8 +70,65 @@ type PCTransportParams struct { RetransmitBufferSize uint16 Pacer pacer.Factory + Interceptors []interceptor.Factory + OnNegotiationError func(err error) OnRTTUpdate func(rtt uint32) IsSender bool + + SRTPProtectionProfiles []dtls.SRTPProtectionProfile +} + +func (t *PCTransport) registerDefaultInterceptors(params PCTransportParams, i *interceptor.Registry) error { + if params.Pacer != nil { + i.Add(sdkinterceptor.NewPacerInterceptorFactory(params.Pacer)) + } + + // nack interceptor + generator := &sdkinterceptor.NackGeneratorInterceptorFactory{} + var generatorOption []nack.ResponderOption + if params.RetransmitBufferSize > 0 { + generatorOption = append(generatorOption, nack.ResponderSize(params.RetransmitBufferSize)) + } + t.nackGenerator = generator + + responder, err := nack.NewResponderInterceptor(generatorOption...) + if err != nil { + return err + } + i.Add(generator) + i.Add(responder) + + // rtcp report interceptor + if err := webrtc.ConfigureRTCPReports(i); err != nil { + return err + } + + // twcc interceptor + twccGenerator, err := twcc.NewSenderInterceptor() + if err != nil { + return err + } + i.Add(twccGenerator) + + i.Add(sdkinterceptor.NewLimitSizeInterceptorFactory()) + + if params.OnRTTUpdate != nil { + i.Add(sdkinterceptor.NewRTTInterceptorFactory(t.handleRTTUpdate)) + } + + var onXRRtt func(rtt uint32) + if params.IsSender { + // publisher only responds to XR request for sfu to measure RTT + onXRRtt = func(rtt uint32) {} + } else { + onXRRtt = func(rtt uint32) { + t.rttFromXR.Store(true) + t.setRTT(rtt) + } + } + i.Add(lkinterceptor.NewRTTFromXRFactory(onXRRtt)) + + return nil } func NewPCTransport(params PCTransportParams) (*PCTransport, error) { @@ -94,60 +153,42 @@ func NewPCTransport(params PCTransportParams) (*PCTransport, error) { t := &PCTransport{ debouncedNegotiate: debounce.New(negotiationFrequency), + onNegotiationError: params.OnNegotiationError, onRTTUpdate: params.OnRTTUpdate, } - // nack interceptor - generator := &sdkinterceptor.NackGeneratorInterceptorFactory{} - var generatorOption []nack.ResponderOption - if params.RetransmitBufferSize > 0 { - generatorOption = append(generatorOption, nack.ResponderSize(params.RetransmitBufferSize)) - } - responder, err := nack.NewResponderInterceptor(generatorOption...) - if err != nil { - return nil, err + if params.Interceptors != nil { + for _, c := range params.Interceptors { + i.Add(c) + } + } else { + err := t.registerDefaultInterceptors(params, i) + if err != nil { + return nil, err + } } m.RegisterFeedback(webrtc.RTCPFeedback{Type: "nack"}, webrtc.RTPCodecTypeVideo) m.RegisterFeedback(webrtc.RTCPFeedback{Type: "nack", Parameter: "pli"}, webrtc.RTPCodecTypeVideo) - if params.Pacer != nil { - i.Add(sdkinterceptor.NewPacerInterceptorFactory(params.Pacer)) - } - - i.Add(responder) - i.Add(generator) - - // rtcp report interceptor - if err := webrtc.ConfigureRTCPReports(i); err != nil { + m.RegisterFeedback(webrtc.RTCPFeedback{Type: webrtc.TypeRTCPFBTransportCC}, webrtc.RTPCodecTypeVideo) + if err := m.RegisterHeaderExtension(webrtc.RTPHeaderExtensionCapability{URI: sdp.TransportCCURI}, webrtc.RTPCodecTypeVideo); err != nil { return nil, err } - // twcc interceptor - if err := webrtc.ConfigureTWCCSender(m, i); err != nil { + m.RegisterFeedback(webrtc.RTCPFeedback{Type: webrtc.TypeRTCPFBTransportCC}, webrtc.RTPCodecTypeAudio) + if err := m.RegisterHeaderExtension(webrtc.RTPHeaderExtensionCapability{URI: sdp.TransportCCURI}, webrtc.RTPCodecTypeAudio); err != nil { return nil, err } - i.Add(sdkinterceptor.NewLimitSizeInterceptorFactory()) - - if params.OnRTTUpdate != nil { - i.Add(sdkinterceptor.NewRTTInterceptorFactory(t.handleRTTUpdate)) - } + se := webrtc.SettingEngine{} - var onXRRtt func(rtt uint32) - if params.IsSender { - // publisher only responds to XR request for sfu to measure RTT - onXRRtt = func(rtt uint32) {} + if len(params.SRTPProtectionProfiles) == 0 { + se.SetSRTPProtectionProfiles(dtls.SRTP_AEAD_AES_128_GCM, dtls.SRTP_AES128_CM_HMAC_SHA1_80) } else { - onXRRtt = func(rtt uint32) { - t.rttFromXR.Store(true) - t.setRTT(rtt) - } + se.SetSRTPProtectionProfiles(params.SRTPProtectionProfiles...) } - i.Add(lkinterceptor.NewRTTFromXRFactory(onXRRtt)) - se := webrtc.SettingEngine{} - se.SetSRTPProtectionProfiles(dtls.SRTP_AEAD_AES_128_GCM, dtls.SRTP_AES128_CM_HMAC_SHA1_80) se.SetDTLSRetransmissionInterval(dtlsRetransmissionInterval) se.SetICETimeouts(iceDisconnectedTimeout, iceFailedTimeout, iceKeepaliveInterval) @@ -158,7 +199,6 @@ func NewPCTransport(params PCTransportParams) (*PCTransport, error) { } t.pc = pc - t.nackGenerator = generator pc.OnICEGatheringStateChange(t.onICEGatheringStateChange) @@ -371,10 +411,12 @@ func (t *PCTransport) createAndSendOffer(options *webrtc.OfferOptions) error { logger.Debugw("create offer", "offer", offer.SDP) if err != nil { logger.Errorw("could not negotiate", err) + t.onNegotiationError(err) return err } if err := t.pc.SetLocalDescription(offer); err != nil { logger.Errorw("could not set local description", err) + t.onNegotiationError(err) return err } t.restartAfterGathering = false diff --git a/version.go b/version.go index 57b8444..652bfc1 100644 --- a/version.go +++ b/version.go @@ -14,4 +14,4 @@ package lksdk -const Version = "2.0.6" +const Version = "2.1.1"