From d0128b19cdf119af0243fd0570547553e09c86da Mon Sep 17 00:00:00 2001 From: Raja Subramanian Date: Fri, 2 Feb 2024 21:52:43 +0530 Subject: [PATCH 01/44] Reset sender reports before measuring clock skew. (#2437) --- pkg/sfu/buffer/rtpstats_receiver.go | 37 +++++++++++++++-------------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/pkg/sfu/buffer/rtpstats_receiver.go b/pkg/sfu/buffer/rtpstats_receiver.go index 53a963fcb..093084dd8 100644 --- a/pkg/sfu/buffer/rtpstats_receiver.go +++ b/pkg/sfu/buffer/rtpstats_receiver.go @@ -271,6 +271,25 @@ func (r *RTPStatsReceiver) SetRtcpSenderReportData(srData *RTCPSenderReportData) r.maybeAdjustFirstPacketTime(srDataCopy.RTPTimestamp, r.timestamp.GetStart()) + if r.srNewest != nil && srDataCopy.RTPTimestampExt < r.srNewest.RTPTimestampExt { + // This can happen when a track is replaced with a null and then restored - + // i. e. muting replacing with null and unmute restoring the original track. + // Under such a condition reset the sender reports to start from this point. + // Resetting will ensure sample rate calculations do not go haywire due to negative time. + if r.outOfOrderSsenderReportCount%10 == 0 { + r.logger.Infow( + "received sender report, out-of-order, resetting", + "last", r.srNewest.ToString(), + "current", srDataCopy.ToString(), + "count", r.outOfOrderSsenderReportCount, + ) + } + r.outOfOrderSsenderReportCount++ + + r.srFirst = nil + r.srNewest = nil + } + if r.srNewest != nil { timeSinceLast := srData.NTPTimestamp.Time().Sub(r.srNewest.NTPTimestamp.Time()).Seconds() rtpDiffSinceLast := srDataCopy.RTPTimestampExt - r.srNewest.RTPTimestampExt @@ -297,24 +316,6 @@ func (r *RTPStatsReceiver) SetRtcpSenderReportData(srData *RTCPSenderReportData) } } - if r.srNewest != nil && srDataCopy.RTPTimestampExt < r.srNewest.RTPTimestampExt { - // This can happen when a track is replaced with a null and then restored - - // i. e. muting replacing with null and unmute restoring the original track. - // Under such a condition reset the sender reports to start from this point. - // Resetting will ensure sample rate calculations do not go haywire due to negative time. - if r.outOfOrderSsenderReportCount%10 == 0 { - r.logger.Infow( - "received sender report, out-of-order, resetting", - "last", r.srNewest.ToString(), - "current", srDataCopy.ToString(), - "count", r.outOfOrderSsenderReportCount, - ) - } - r.outOfOrderSsenderReportCount++ - - r.srFirst = nil - } - r.srNewest = &srDataCopy if r.srFirst == nil { r.srFirst = &srDataCopy From f26fbceaba05e0440cbc79e475552ddc68c8bc39 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 3 Feb 2024 02:15:21 -0800 Subject: [PATCH 02/44] Update pion deps (#2438) Generated by renovateBot Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- go.mod | 4 ++-- go.sum | 9 +++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 1d6f8666e..9fccd20ca 100644 --- a/go.mod +++ b/go.mod @@ -25,12 +25,12 @@ require ( github.com/maxbrunsfeld/counterfeiter/v6 v6.8.1 github.com/mitchellh/go-homedir v1.1.0 github.com/olekukonko/tablewriter v0.0.5 - github.com/pion/dtls/v2 v2.2.9 + github.com/pion/dtls/v2 v2.2.10 github.com/pion/ice/v2 v2.3.12 github.com/pion/interceptor v0.1.25 github.com/pion/rtcp v1.2.13 github.com/pion/rtp v1.8.3 - github.com/pion/sctp v1.8.9 + github.com/pion/sctp v1.8.10 github.com/pion/sdp/v3 v3.0.6 github.com/pion/transport/v2 v2.2.4 github.com/pion/turn/v2 v2.1.4 diff --git a/go.sum b/go.sum index 231b870b9..066754465 100644 --- a/go.sum +++ b/go.sum @@ -182,8 +182,8 @@ github.com/onsi/gomega v1.30.0 h1:hvMK7xYz4D3HapigLTeGdId/NcfQx1VHMJc60ew99+8= 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.9 h1:K+D/aVf9/REahQvqk6G5JavdrD8W1PWDKC11UlwN7ts= -github.com/pion/dtls/v2 v2.2.9/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.11/go.mod h1:hPcLC3kxMa+JGRzMHqQzjoSj3xtE9F+eoncmXLlCL4E= github.com/pion/ice/v2 v2.3.12 h1:NWKW2b3+oSZS3klbQMIEWQ0i52Kuo0KBg505a5kQv4s= github.com/pion/ice/v2 v2.3.12/go.mod h1:hPcLC3kxMa+JGRzMHqQzjoSj3xtE9F+eoncmXLlCL4E= @@ -205,8 +205,8 @@ 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= -github.com/pion/sctp v1.8.9/go.mod h1:cMLT45jqw3+jiJCrtHVwfQLnfR0MGZ4rgOJwUOIqLkI= +github.com/pion/sctp v1.8.10 h1:FDPlkojWQ2hIjnvgFs+frHR33TZCxoRhV2HztZ07NnU= +github.com/pion/sctp v1.8.10/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.18 h1:vKpAXfawO9RtTRKZJbG4y0v1b11NZxQnxRl85kGuUlo= @@ -396,6 +396,7 @@ golang.org/x/term v0.9.0/go.mod h1:M6DEAAIenWoTxdKrOltXcmDY3rSplQUkrvaDU5FcQyo= golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o= golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= +golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= 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= From 4bce0e7ed40d2ac2312c6e58a5d490dd122c675e Mon Sep 17 00:00:00 2001 From: Paul Wells Date: Sat, 3 Feb 2024 14:57:07 -0800 Subject: [PATCH 03/44] fix startup with -dev and -config (#2442) --- cmd/server/main.go | 13 ++----------- pkg/config/config.go | 8 ++++---- 2 files changed, 6 insertions(+), 15 deletions(-) diff --git a/cmd/server/main.go b/cmd/server/main.go index b9bfea418..3e8e3344d 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -29,7 +29,6 @@ import ( "github.com/livekit/livekit-server/pkg/rtc" "github.com/livekit/livekit-server/pkg/telemetry/prometheus" - "github.com/livekit/mediatransportutil/pkg/rtcconfig" "github.com/livekit/protocol/logger" "github.com/livekit/livekit-server/pkg/config" @@ -205,11 +204,7 @@ func getConfig(c *cli.Context) (*config.Config, error) { } config.InitLoggerFromConfig(&conf.Logging) - if c.String("config") == "" && c.String("config-body") == "" && conf.Development { - // use single port UDP when no config is provided - conf.RTC.UDPPort = rtcconfig.PortRange{Start: 7882} - conf.RTC.ICEPortRangeStart = 0 - conf.RTC.ICEPortRangeEnd = 0 + if conf.Development { logger.Infow("starting in development mode") if len(conf.Keys) == 0 { @@ -248,10 +243,6 @@ func getConfig(c *cli.Context) (*config.Config, error) { } func startServer(c *cli.Context) error { - rand.Seed(time.Now().UnixNano()) - - memProfile := c.String("memprofile") - conf, err := getConfig(c) if err != nil { return err @@ -263,7 +254,7 @@ func startServer(c *cli.Context) error { return err } - if memProfile != "" { + if memProfile := c.String("memprofile"); memProfile != "" { if f, err := os.Create(memProfile); err != nil { return err } else { diff --git a/pkg/config/config.go b/pkg/config/config.go index 1a515f86f..0d18022c7 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -516,16 +516,16 @@ func NewConfig(confString string, strictMode bool, c *cli.Context, baseFlags []c } } - if err := conf.RTC.Validate(conf.Development); err != nil { - return nil, fmt.Errorf("could not validate RTC config: %v", err) - } - if c != nil { if err := conf.updateFromCLI(c, baseFlags); err != nil { return nil, err } } + if err := conf.RTC.Validate(conf.Development); err != nil { + return nil, fmt.Errorf("could not validate RTC config: %v", err) + } + // expand env vars in filenames file, err := homedir.Expand(os.ExpandEnv(conf.KeyFile)) if err != nil { From 7c16ca6a0cdfe9172eaa8ffbdd445aa54e480997 Mon Sep 17 00:00:00 2001 From: Raja Subramanian Date: Sun, 4 Feb 2024 11:12:22 +0530 Subject: [PATCH 04/44] Log feed Sender Report to better understand forwarded sender report (#2443) anomalies. --- pkg/sfu/buffer/rtpstats_sender.go | 26 +++++++++++++++++++++++++- pkg/sfu/downtrack.go | 2 +- pkg/sfu/streamtrackermanager.go | 9 +++++++++ 3 files changed, 35 insertions(+), 2 deletions(-) diff --git a/pkg/sfu/buffer/rtpstats_sender.go b/pkg/sfu/buffer/rtpstats_sender.go index 618341e75..98b25744a 100644 --- a/pkg/sfu/buffer/rtpstats_sender.go +++ b/pkg/sfu/buffer/rtpstats_sender.go @@ -160,6 +160,9 @@ type RTPStatsSender struct { clockSkewCount int outOfOrderSenderReportCount int metadataCacheOverflowCount int + + srFeedFirst *RTCPSenderReportData + srFeedNewest *RTCPSenderReportData } func NewRTPStatsSender(params RTPStatsParams) *RTPStatsSender { @@ -198,6 +201,15 @@ func (r *RTPStatsSender) Seed(from *RTPStatsSender) { r.nextSenderSnapshotID = from.nextSenderSnapshotID r.senderSnapshots = make([]senderSnapshot, cap(from.senderSnapshots)) copy(r.senderSnapshots, from.senderSnapshots) + + if from.srFeedFirst != nil { + srFeedFirst := *from.srFeedFirst + r.srFeedFirst = &srFeedFirst + } + if from.srFeedNewest != nil { + srFeedNewest := *from.srFeedNewest + r.srFeedNewest = &srFeedNewest + } } func (r *RTPStatsSender) NewSnapshotId() uint32 { @@ -571,10 +583,16 @@ func (r *RTPStatsSender) LastReceiverReportTime() time.Time { return r.lastRRTime } -func (r *RTPStatsSender) MaybeAdjustFirstPacketTime(ts uint32) { +func (r *RTPStatsSender) MaybeAdjustFirstPacketTime(srData *RTCPSenderReportData, ts uint32) { r.lock.Lock() defer r.lock.Unlock() + srDataCopy := *srData + if r.srFeedFirst == nil { + r.srFeedFirst = &srDataCopy + } + r.srFeedNewest = &srDataCopy + r.maybeAdjustFirstPacketTime(ts, uint32(r.extStartTS)) } @@ -637,8 +655,11 @@ func (r *RTPStatsSender) GetRtcpSenderReport(ssrc uint32, calculatedClockRate ui if r.clockSkewCount%10 == 0 { r.logger.Infow( "sending sender report, clock skew", + "first", r.srFirst.ToString(), "last", r.srNewest.ToString(), "curr", srData.ToString(), + "firstFeed", r.srFeedFirst.ToString(), + "lastFeed", r.srFeedNewest.ToString(), "timeNow", time.Now().String(), "extStartTS", r.extStartTS, "extHighestTS", r.extHighestTS, @@ -674,8 +695,11 @@ func (r *RTPStatsSender) GetRtcpSenderReport(ssrc uint32, calculatedClockRate ui if r.outOfOrderSenderReportCount%10 == 0 { r.logger.Infow( "sending sender report, out-of-order, repairing", + "first", r.srFirst.ToString(), "last", r.srNewest.ToString(), "curr", srData.ToString(), + "firstFeed", r.srFeedFirst.ToString(), + "lastFeed", r.srFeedNewest.ToString(), "timeNow", time.Now().String(), "extStartTS", r.extStartTS, "extHighestTS", r.extHighestTS, diff --git a/pkg/sfu/downtrack.go b/pkg/sfu/downtrack.go index f12859142..470e11eed 100644 --- a/pkg/sfu/downtrack.go +++ b/pkg/sfu/downtrack.go @@ -1914,7 +1914,7 @@ func (d *DownTrack) sendSilentFrameOnMuteForOpus() { func (d *DownTrack) HandleRTCPSenderReportData(_payloadType webrtc.PayloadType, isSVC bool, layer int32, srData *buffer.RTCPSenderReportData) error { if (layer == d.forwarder.GetReferenceLayerSpatial() || (layer == 0 && isSVC)) && srData != nil { - d.rtpStats.MaybeAdjustFirstPacketTime(srData.RTPTimestamp + uint32(d.forwarder.GetReferenceTimestampOffset())) + d.rtpStats.MaybeAdjustFirstPacketTime(srData, srData.RTPTimestamp+uint32(d.forwarder.GetReferenceTimestampOffset())) } return nil } diff --git a/pkg/sfu/streamtrackermanager.go b/pkg/sfu/streamtrackermanager.go index a934b9e93..2817ef31f 100644 --- a/pkg/sfu/streamtrackermanager.go +++ b/pkg/sfu/streamtrackermanager.go @@ -572,6 +572,15 @@ func (s *StreamTrackerManager) updateLayerOffsetLocked(ref, other int32) { // Rationale: higher layers could be paused for extended periods of time // due to adaptive stream/dynacast or publisher constraints like CPU/bandwidth. // The check is to avoid using very old reports. + s.logger.Infow( + "skipping offset update", + "ref", ref, + "refNTP", srRef.NTPTimestamp.Time().String(), + "refRTP", srRef.RTPTimestamp, + "other", other, + "otherNTP", srOther.NTPTimestamp.Time().String(), + "otherRTP", srOther.RTPTimestamp, + ) return } rtpDiff := ntpDiff.Nanoseconds() * int64(s.clockRate) / 1e9 From b7147efb87e21183b5758f7bb1bf9ebf2783d80a Mon Sep 17 00:00:00 2001 From: Raja Subramanian Date: Mon, 5 Feb 2024 13:41:41 +0530 Subject: [PATCH 05/44] Close published tracks on participant close (#2446) --- pkg/rtc/mediatrack.go | 23 +++++++++---------- pkg/rtc/participant.go | 42 +++++++++------------------------- pkg/rtc/uptrackmanager.go | 47 +++++++++++++++++++++------------------ pkg/sfu/buffer/buffer.go | 2 +- pkg/sfu/receiver.go | 14 +++++------- 5 files changed, 52 insertions(+), 76 deletions(-) diff --git a/pkg/rtc/mediatrack.go b/pkg/rtc/mediatrack.go index ba714228d..e2978d82d 100644 --- a/pkg/rtc/mediatrack.go +++ b/pkg/rtc/mediatrack.go @@ -56,17 +56,16 @@ type MediaTrackParams struct { ParticipantID livekit.ParticipantID ParticipantIdentity livekit.ParticipantIdentity ParticipantVersion uint32 - // channel to send RTCP packets to the source - RTCPChan chan []rtcp.Packet - BufferFactory *buffer.Factory - ReceiverConfig ReceiverConfig - SubscriberConfig DirectionConfig - PLIThrottleConfig config.PLIThrottleConfig - AudioConfig config.AudioConfig - VideoConfig config.VideoConfig - Telemetry telemetry.TelemetryService - Logger logger.Logger - SimTracks map[uint32]SimulcastTrackInfo + BufferFactory *buffer.Factory + ReceiverConfig ReceiverConfig + SubscriberConfig DirectionConfig + PLIThrottleConfig config.PLIThrottleConfig + AudioConfig config.AudioConfig + VideoConfig config.VideoConfig + Telemetry telemetry.TelemetryService + Logger logger.Logger + SimTracks map[uint32]SimulcastTrackInfo + OnRTCP func([]rtcp.Packet) } func NewMediaTrack(params MediaTrackParams, ti *livekit.TrackInfo) *MediaTrack { @@ -252,13 +251,13 @@ func (t *MediaTrack) AddReceiver(receiver *webrtc.RTPReceiver, track *webrtc.Tra ti, LoggerWithCodecMime(t.params.Logger, mime), twcc, + t.params.OnRTCP, t.params.VideoConfig.StreamTracker, sfu.WithPliThrottleConfig(t.params.PLIThrottleConfig), sfu.WithAudioConfig(t.params.AudioConfig), sfu.WithLoadBalanceThreshold(20), sfu.WithStreamTrackers(), ) - newWR.SetRTCPCh(t.params.RTCPChan) newWR.OnCloseHandler(func() { t.MediaTrackReceiver.SetClosing() t.MediaTrackReceiver.ClearReceiver(mime, false) diff --git a/pkg/rtc/participant.go b/pkg/rtc/participant.go index 8c476d5e7..96f251d07 100644 --- a/pkg/rtc/participant.go +++ b/pkg/rtc/participant.go @@ -158,7 +158,7 @@ type ParticipantImpl struct { disconnectTimer *time.Timer migrationTimer *time.Timer - rtcpCh chan []rtcp.Packet + pubRTCPQueue *sutils.OpsQueue // hold reference for MediaTrack twcc *twcc.Responder @@ -240,7 +240,7 @@ func NewParticipant(params ParticipantParams) (*ParticipantImpl, error) { } p := &ParticipantImpl{ params: params, - rtcpCh: make(chan []rtcp.Packet, 100), + pubRTCPQueue: sutils.NewOpsQueue("pub-rtcp", 64, false), pendingTracks: make(map[string]*pendingTrackInfo), pendingPublishingTracks: make(map[livekit.TrackID]*pendingTrackInfo), connectedAt: time.Now(), @@ -1467,7 +1467,7 @@ func (p *ParticipantImpl) onPublisherInitialConnected() { if p.supervisor != nil { p.supervisor.SetPublisherPeerConnectionConnected(true) } - go p.publisherRTCPWorker() + p.pubRTCPQueue.Start() } func (p *ParticipantImpl) onSubscriberInitialConnected() { @@ -2000,7 +2000,6 @@ func (p *ParticipantImpl) addMediaTrack(signalCid string, sdpCid string, ti *liv ParticipantID: p.params.SID, ParticipantIdentity: p.params.Identity, ParticipantVersion: p.version.Load(), - RTCPChan: p.rtcpCh, BufferFactory: p.params.Config.BufferFactory, ReceiverConfig: p.params.Config.Receiver, AudioConfig: p.params.AudioConfig, @@ -2010,6 +2009,7 @@ func (p *ParticipantImpl) addMediaTrack(signalCid string, sdpCid string, ti *liv SubscriberConfig: p.params.Config.Subscriber, PLIThrottleConfig: p.params.PLIThrottleConfig, SimTracks: p.params.SimTracks, + OnRTCP: p.postRtcp, }, ti) mt.OnSubscribedMaxQualityChange(p.onSubscribedMaxQualityChange) @@ -2117,7 +2117,7 @@ func (p *ParticipantImpl) hasPendingMigratedTrack() bool { } func (p *ParticipantImpl) onUpTrackManagerClose() { - p.postRtcp(nil) + p.pubRTCPQueue.Stop() } func (p *ParticipantImpl) getPendingTrack(clientId string, kind livekit.TrackType) (string, *livekit.TrackInfo, bool) { @@ -2241,28 +2241,6 @@ func (p *ParticipantImpl) getPublishedTrackBySdpCid(clientId string) types.Media return nil } -func (p *ParticipantImpl) publisherRTCPWorker() { - defer func() { - if r := Recover(p.GetLogger()); r != nil { - os.Exit(1) - } - }() - - // read from rtcpChan - for pkts := range p.rtcpCh { - if pkts == nil { - p.pubLogger.Debugw("exiting publisher RTCP worker") - return - } - - if err := p.TransportManager.WritePublisherRTCP(pkts); err != nil { - if !IsEOF(err) { - p.pubLogger.Errorw("could not write RTCP to participant", err) - } - } - } -} - func (p *ParticipantImpl) DebugInfo() map[string]interface{} { info := map[string]interface{}{ "ID": p.params.SID, @@ -2291,11 +2269,11 @@ func (p *ParticipantImpl) DebugInfo() map[string]interface{} { } func (p *ParticipantImpl) postRtcp(pkts []rtcp.Packet) { - select { - case p.rtcpCh <- pkts: - default: - p.params.Logger.Warnw("rtcp channel full", nil) - } + p.pubRTCPQueue.Enqueue(func() { + if err := p.TransportManager.WritePublisherRTCP(pkts); err != nil && !IsEOF(err) { + p.pubLogger.Errorw("could not write RTCP to participant", err) + } + }) } func (p *ParticipantImpl) setDowntracksConnected() { diff --git a/pkg/rtc/uptrackmanager.go b/pkg/rtc/uptrackmanager.go index ed01e1f79..5cadfc267 100644 --- a/pkg/rtc/uptrackmanager.go +++ b/pkg/rtc/uptrackmanager.go @@ -64,22 +64,37 @@ func NewUpTrackManager(params UpTrackManagerParams) *UpTrackManager { func (u *UpTrackManager) Close(willBeResumed bool) { u.lock.Lock() - u.closed = true - notify := len(u.publishedTracks) == 0 - u.lock.Unlock() - - // remove all subscribers - for _, t := range u.GetPublishedTracks() { - t.ClearAllReceivers(willBeResumed) + if u.closed { + u.lock.Unlock() + return } - if notify && u.onClose != nil { - u.onClose() + u.closed = true + + publishedTracks := u.publishedTracks + u.publishedTracks = make(map[livekit.TrackID]types.MediaTrack) + u.lock.Unlock() + + for _, t := range publishedTracks { + t.Close(willBeResumed) + } + + if onClose := u.getOnUpTrackManagerClose(); onClose != nil { + onClose() } } func (u *UpTrackManager) OnUpTrackManagerClose(f func()) { + u.lock.Lock() u.onClose = f + u.lock.Unlock() +} + +func (u *UpTrackManager) getOnUpTrackManagerClose() func() { + u.lock.RLock() + defer u.lock.RUnlock() + + return u.onClose } func (u *UpTrackManager) ToProto() []*livekit.TrackInfo { @@ -247,22 +262,10 @@ func (u *UpTrackManager) AddPublishedTrack(track types.MediaTrack) { u.params.Logger.Debugw("added published track", "trackID", track.ID(), "trackInfo", logger.Proto(track.ToProto())) track.AddOnClose(func() { - notifyClose := false - - // cleanup u.lock.Lock() - trackID := track.ID() - delete(u.publishedTracks, trackID) + delete(u.publishedTracks, track.ID()) // not modifying subscription permissions, will get reset on next update from participant - - if u.closed && len(u.publishedTracks) == 0 { - notifyClose = true - } u.lock.Unlock() - - if notifyClose && u.onClose != nil { - u.onClose() - } }) } diff --git a/pkg/sfu/buffer/buffer.go b/pkg/sfu/buffer/buffer.go index f27946064..7e250c23a 100644 --- a/pkg/sfu/buffer/buffer.go +++ b/pkg/sfu/buffer/buffer.go @@ -264,7 +264,7 @@ func (b *Buffer) Bind(params webrtc.RTPParameters, codec webrtc.RTPCodecCapabili b.bound = true } -// Write adds an RTP Packet, out of order, new packet may be arrived later +// Write adds an RTP Packet, ordering is not guaranteed, newer packets may arrive later func (b *Buffer) Write(pkt []byte) (n int, err error) { b.Lock() defer b.Unlock() diff --git a/pkg/sfu/receiver.go b/pkg/sfu/receiver.go index ae368b42f..b05b2b23c 100644 --- a/pkg/sfu/receiver.go +++ b/pkg/sfu/receiver.go @@ -108,7 +108,7 @@ type WebRTCReceiver struct { useTrackers bool trackInfo atomic.Pointer[livekit.TrackInfo] - rtcpCh chan []rtcp.Packet + onRTCP func([]rtcp.Packet) twcc *twcc.Responder @@ -198,6 +198,7 @@ func NewWebRTCReceiver( trackInfo *livekit.TrackInfo, logger logger.Logger, twcc *twcc.Responder, + onRTCP func([]rtcp.Packet), trackersConfig config.StreamTrackersConfig, opts ...ReceiverOpts, ) *WebRTCReceiver { @@ -209,6 +210,7 @@ func NewWebRTCReceiver( codec: track.Codec(), kind: track.Kind(), twcc: twcc, + onRTCP: onRTCP, isSVC: IsSvcCodec(track.Codec().MimeType), isRED: IsRedCodec(track.Codec().MimeType), } @@ -514,10 +516,8 @@ func (w *WebRTCReceiver) sendRTCP(packets []rtcp.Packet) { return } - select { - case w.rtcpCh <- packets: - default: - w.logger.Warnw("sendRTCP failed, rtcp channel full", nil) + if w.onRTCP != nil { + w.onRTCP(packets) } } @@ -531,10 +531,6 @@ func (w *WebRTCReceiver) SendPLI(layer int32, force bool) { buff.SendPLI(force) } -func (w *WebRTCReceiver) SetRTCPCh(ch chan []rtcp.Packet) { - w.rtcpCh = ch -} - func (w *WebRTCReceiver) getBuffer(layer int32) *buffer.Buffer { w.bufferMu.RLock() defer w.bufferMu.RUnlock() From e9cff525f400aadef26684d5fbb7740186720ea5 Mon Sep 17 00:00:00 2001 From: Denys Smirnov Date: Tue, 6 Feb 2024 00:13:44 +0200 Subject: [PATCH 06/44] Add method for creating SIP participants with a custom token. (#2448) --- go.mod | 2 +- go.sum | 4 ++-- pkg/service/sip.go | 7 ++++++- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index 9fccd20ca..08fb3f734 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-20231213075826-cccbf2b93d3f - github.com/livekit/protocol v1.9.8-0.20240130003842-54f76fc6865e + github.com/livekit/protocol v1.9.8 github.com/livekit/psrpc v0.5.3-0.20240129223932-473b29cda289 github.com/mackerelio/go-osstat v0.2.4 github.com/magefile/mage v1.15.0 diff --git a/go.sum b/go.sum index 066754465..a333ca31b 100644 --- a/go.sum +++ b/go.sum @@ -126,8 +126,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-20231213075826-cccbf2b93d3f h1:XHrwGwLNGQB3ZqolH1YdMH/22hgXKr4vm+2M7JKMMGg= github.com/livekit/mediatransportutil v0.0.0-20231213075826-cccbf2b93d3f/go.mod h1:GBzn9xL+mivI1pW+tyExcKgbc0VOc29I9yJsNcAVaAc= -github.com/livekit/protocol v1.9.8-0.20240130003842-54f76fc6865e h1:aQGmivssjKlvca8Mmmm4XzHQrFV+vwrGIhAxQE8tnZk= -github.com/livekit/protocol v1.9.8-0.20240130003842-54f76fc6865e/go.mod h1:lSJlMeTJfQBEv8/D2p3zdCo+i+jTmTtn24ysL4ePK28= +github.com/livekit/protocol v1.9.8 h1:CyK+oQ2TgSkRGgkJfDlJd5FRP42cahEFCOyxg2Sa/ts= +github.com/livekit/protocol v1.9.8/go.mod h1:lSJlMeTJfQBEv8/D2p3zdCo+i+jTmTtn24ysL4ePK28= github.com/livekit/psrpc v0.5.3-0.20240129223932-473b29cda289 h1:oTgNH7v9TXsBgoltKk5mnWjv4qqcPF2iV+WtEVQ6ROM= github.com/livekit/psrpc v0.5.3-0.20240129223932-473b29cda289/go.mod h1:cQjxg1oCxYHhxxv6KJH1gSvdtCHQoRZCHgPdm5N8v2g= github.com/mackerelio/go-osstat v0.2.4 h1:qxGbdPkFo65PXOb/F/nhDKpF2nGmGaCFDLXoZjJTtUs= diff --git a/pkg/service/sip.go b/pkg/service/sip.go index a95dbad09..0ad76b207 100644 --- a/pkg/service/sip.go +++ b/pkg/service/sip.go @@ -157,7 +157,7 @@ func (s *SIPService) DeleteSIPDispatchRule(ctx context.Context, req *livekit.Del return info, nil } -func (s *SIPService) CreateSIPParticipant(ctx context.Context, req *livekit.CreateSIPParticipantRequest) (*livekit.SIPParticipantInfo, error) { +func (s *SIPService) CreateSIPParticipantWithToken(ctx context.Context, req *livekit.CreateSIPParticipantRequest, wsUrl, token string) (*livekit.SIPParticipantInfo, error) { if s.store == nil { return nil, ErrSIPNotConnected } @@ -167,6 +167,8 @@ func (s *SIPService) CreateSIPParticipant(ctx context.Context, req *livekit.Crea CallTo: req.SipCallTo, RoomName: req.RoomName, ParticipantIdentity: req.ParticipantIdentity, + WsUrl: wsUrl, + Token: token, } if req.SipTrunkId != "" { trunk, err := s.store.LoadSIPTrunk(ctx, req.SipTrunkId) @@ -202,3 +204,6 @@ func (s *SIPService) CreateSIPParticipant(ctx context.Context, req *livekit.Crea RoomName: req.RoomName, }, nil } +func (s *SIPService) CreateSIPParticipant(ctx context.Context, req *livekit.CreateSIPParticipantRequest) (*livekit.SIPParticipantInfo, error) { + return s.CreateSIPParticipantWithToken(ctx, req, "", "") +} From 716844c383fa120e928af0c0966d056b209b5e56 Mon Sep 17 00:00:00 2001 From: Raja Subramanian Date: Tue, 6 Feb 2024 10:39:57 +0530 Subject: [PATCH 07/44] Log unpublish for debug. (#2451) Also, call unpublish callback irrespective of state and let callback handle do the needed checks. --- pkg/rtc/participant.go | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/pkg/rtc/participant.go b/pkg/rtc/participant.go index 96f251d07..22bc2ac99 100644 --- a/pkg/rtc/participant.go +++ b/pkg/rtc/participant.go @@ -2062,12 +2062,9 @@ func (p *ParticipantImpl) addMediaTrack(signalCid string, sdpCid string, ti *liv p.dirty.Store(true) - if !p.IsClosed() { - // unpublished events aren't necessary when participant is closed - p.pubLogger.Debugw("track unpublished", "trackID", ti.Sid, "track", logger.Proto(ti)) - if onTrackUnpublished := p.getOnTrackUnpublished(); onTrackUnpublished != nil { - onTrackUnpublished(p, mt) - } + p.pubLogger.Infow("track unpublished", "trackID", ti.Sid, "track", logger.Proto(ti)) + if onTrackUnpublished := p.getOnTrackUnpublished(); onTrackUnpublished != nil { + onTrackUnpublished(p, mt) } }) From be87a1b6f0d0257b613b5356eff53c9bd0f5fc3d Mon Sep 17 00:00:00 2001 From: cnderrauber Date: Tue, 6 Feb 2024 21:30:37 +0800 Subject: [PATCH 08/44] Support rtx for publisher (#2452) * Support rtx for publisher * remote log * solve comment --- go.mod | 8 +- go.sum | 21 ++--- pkg/rtc/config.go | 4 +- pkg/rtc/mediaengine.go | 23 +++++ pkg/rtc/mediatrack.go | 4 +- pkg/rtc/participant.go | 14 ++- pkg/rtc/transport.go | 90 ++++++++++++++++-- pkg/rtc/transportmanager.go | 3 + pkg/rtc/unhandlesimulcast.go | 15 +-- pkg/sfu/buffer/buffer.go | 138 ++++++++++++++++++++------- pkg/sfu/buffer/factory.go | 30 ++++++ pkg/sfu/downtrack.go | 3 +- pkg/sfu/interceptor/rtx.go | 168 +++++++++++++++++++++++++++++++++ pkg/sfu/receiver.go | 3 - pkg/sfu/{ => utils}/helpers.go | 15 ++- 15 files changed, 453 insertions(+), 86 deletions(-) create mode 100644 pkg/sfu/interceptor/rtx.go rename pkg/sfu/{ => utils}/helpers.go (76%) diff --git a/go.mod b/go.mod index 08fb3f734..b75305fc9 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,7 @@ require ( github.com/hashicorp/golang-lru/v2 v2.0.7 github.com/jxskiss/base62 v1.1.0 github.com/livekit/mageutil v0.0.0-20230125210925-54e8a70427c1 - github.com/livekit/mediatransportutil v0.0.0-20231213075826-cccbf2b93d3f + github.com/livekit/mediatransportutil v0.0.0-20240206022904-8e203f9db6af github.com/livekit/protocol v1.9.8 github.com/livekit/psrpc v0.5.3-0.20240129223932-473b29cda289 github.com/mackerelio/go-osstat v0.2.4 @@ -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.24 + github.com/pion/webrtc/v3 v3.2.25 github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.18.0 github.com/redis/go-redis/v9 v9.4.0 @@ -65,7 +65,7 @@ require ( github.com/golang/protobuf v1.5.3 // indirect github.com/google/go-cmp v0.6.0 // indirect github.com/google/subcommands v1.2.0 // indirect - github.com/google/uuid v1.5.0 // indirect + github.com/google/uuid v1.6.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-retryablehttp v0.7.5 // indirect github.com/hashicorp/golang-lru v0.5.4 // indirect @@ -82,7 +82,7 @@ require ( github.com/nats-io/nuid v1.0.1 // indirect github.com/pion/datachannel v1.5.5 // indirect github.com/pion/logging v0.2.2 // indirect - github.com/pion/mdns v0.0.9 // indirect + github.com/pion/mdns v0.0.10 // indirect github.com/pion/randutil v0.1.0 // indirect github.com/pion/srtp/v2 v2.0.18 // indirect github.com/pion/stun v0.6.1 // indirect diff --git a/go.sum b/go.sum index a333ca31b..917cf90d7 100644 --- a/go.sum +++ b/go.sum @@ -73,8 +73,8 @@ github.com/google/subcommands v1.2.0 h1:vWQspBTo2nEqTUFita5/KeEWlUL8kQObDFbub/EN github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU= -github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +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/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.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= @@ -124,8 +124,8 @@ github.com/lithammer/shortuuid/v4 v4.0.0 h1:QRbbVkfgNippHOS8PXDkti4NaWeyYfcBTHtw github.com/lithammer/shortuuid/v4 v4.0.0/go.mod h1:Zs8puNcrvf2rV9rTH51ZLLcj7ZXqQI3lv67aw4KiB1Y= github.com/livekit/mageutil v0.0.0-20230125210925-54e8a70427c1 h1:jm09419p0lqTkDaKb5iXdynYrzB84ErPPO4LbRASk58= github.com/livekit/mageutil v0.0.0-20230125210925-54e8a70427c1/go.mod h1:Rs3MhFwutWhGwmY1VQsygw28z5bWcnEYmS1OG9OxjOQ= -github.com/livekit/mediatransportutil v0.0.0-20231213075826-cccbf2b93d3f h1:XHrwGwLNGQB3ZqolH1YdMH/22hgXKr4vm+2M7JKMMGg= -github.com/livekit/mediatransportutil v0.0.0-20231213075826-cccbf2b93d3f/go.mod h1:GBzn9xL+mivI1pW+tyExcKgbc0VOc29I9yJsNcAVaAc= +github.com/livekit/mediatransportutil v0.0.0-20240206022904-8e203f9db6af h1:7VBmJFAqCRCCJxHG7c8z5LGEbwUGqzzw1weZgAewmLA= +github.com/livekit/mediatransportutil v0.0.0-20240206022904-8e203f9db6af/go.mod h1:GBzn9xL+mivI1pW+tyExcKgbc0VOc29I9yJsNcAVaAc= github.com/livekit/protocol v1.9.8 h1:CyK+oQ2TgSkRGgkJfDlJd5FRP42cahEFCOyxg2Sa/ts= github.com/livekit/protocol v1.9.8/go.mod h1:lSJlMeTJfQBEv8/D2p3zdCo+i+jTmTtn24ysL4ePK28= github.com/livekit/psrpc v0.5.3-0.20240129223932-473b29cda289 h1:oTgNH7v9TXsBgoltKk5mnWjv4qqcPF2iV+WtEVQ6ROM= @@ -192,8 +192,8 @@ github.com/pion/interceptor v0.1.25/go.mod h1:wkbPYAak5zKsfpVDYMtEfWEy8D4zL+rpxC 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.8/go.mod h1:hYE72WX8WDveIhg7fmXgMKivD3Puklk0Ymzog0lSyaI= -github.com/pion/mdns v0.0.9 h1:7Ue5KZsqq8EuqStnpPWV33vYYEH0+skdDN5L7EiEsI4= -github.com/pion/mdns v0.0.9/go.mod h1:2JA5exfxwzXiCihmxpTKgFUpiQws2MnipoPK09vecIc= +github.com/pion/mdns v0.0.10 h1:u9/12WL2NNgtGT2nNPXT6+A+xeOF0PkawM/S/wPMWQA= +github.com/pion/mdns v0.0.10/go.mod h1:Y1scL/8TT8KQ172UfxrE4j0c04NOY71bJS1aE1zvyGY= 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= @@ -225,8 +225,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.24 h1:MiFL5DMo2bDaaIFWr0DDpwiV/L4EGbLZb+xoRvfEo1Y= -github.com/pion/webrtc/v3 v3.2.24/go.mod h1:1CaT2fcZzZ6VZA+O1i9yK2DU4EOcXVvSbWG9pr5jefs= +github.com/pion/webrtc/v3 v3.2.25 h1:hmewKZtY60TOMOnAfEfHI+D78VpDe3OdnwcRpE8kLEc= +github.com/pion/webrtc/v3 v3.2.25/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= @@ -295,7 +295,6 @@ 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.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= golang.org/x/exp v0.0.0-20240119083558-1b970713d09a h1:Q8/wZp0KX97QFTc2ywcOE0YRjZPVIx+MXInMzdvQqcA= @@ -332,7 +331,6 @@ 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.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -383,7 +381,6 @@ 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.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -395,7 +392,6 @@ golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.9.0/go.mod h1:M6DEAAIenWoTxdKrOltXcmDY3rSplQUkrvaDU5FcQyo= golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o= golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= -golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= 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= @@ -407,7 +403,6 @@ 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/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= diff --git a/pkg/rtc/config.go b/pkg/rtc/config.go index 820d41857..239b08608 100644 --- a/pkg/rtc/config.go +++ b/pkg/rtc/config.go @@ -25,7 +25,8 @@ import ( ) const ( - frameMarking = "urn:ietf:params:rtp-hdrext:framemarking" + frameMarking = "urn:ietf:params:rtp-hdrext:framemarking" + repairedRTPStreamID = "urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id" ) type WebRTCConfig struct { @@ -87,6 +88,7 @@ func NewWebRTCConfig(conf *config.Config) (*WebRTCConfig, error) { sdp.TransportCCURI, frameMarking, dd.ExtensionURI, + repairedRTPStreamID, }, }, RTCPFeedback: RTCPFeedbackConfig{ diff --git a/pkg/rtc/mediaengine.go b/pkg/rtc/mediaengine.go index 8bbb3db20..55836b472 100644 --- a/pkg/rtc/mediaengine.go +++ b/pkg/rtc/mediaengine.go @@ -15,6 +15,7 @@ package rtc import ( + "fmt" "strings" "github.com/pion/webrtc/v3" @@ -24,8 +25,13 @@ import ( "github.com/livekit/protocol/livekit" ) +const ( + videoRTXMimeType = "video/rtx" +) + var opusCodecCapability = webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeOpus, ClockRate: 48000, Channels: 2, SDPFmtpLine: "minptime=10;useinbandfec=1"} var redCodecCapability = webrtc.RTPCodecCapability{MimeType: sfu.MimeTypeAudioRed, ClockRate: 48000, Channels: 2, SDPFmtpLine: "111/111"} +var videoRTX = webrtc.RTPCodecCapability{MimeType: videoRTXMimeType, ClockRate: 90000} func registerCodecs(me *webrtc.MediaEngine, codecs []*livekit.Codec, rtcpFeedback RTCPFeedbackConfig, filterOutH264HighProfile bool) error { opusCodec := opusCodecCapability @@ -50,6 +56,8 @@ func registerCodecs(me *webrtc.MediaEngine, codecs []*livekit.Codec, rtcpFeedbac } } + rtxEnabled := IsCodecEnabled(codecs, videoRTX) + h264HighProfileFmtp := "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=640032" for _, codec := range []webrtc.RTPCodecParameters{ { @@ -84,10 +92,25 @@ func registerCodecs(me *webrtc.MediaEngine, codecs []*livekit.Codec, rtcpFeedbac if filterOutH264HighProfile && codec.RTPCodecCapability.SDPFmtpLine == h264HighProfileFmtp { continue } + if codec.MimeType == videoRTXMimeType { + continue + } if IsCodecEnabled(codecs, codec.RTPCodecCapability) { if err := me.RegisterCodec(codec, webrtc.RTPCodecTypeVideo); err != nil { return err } + if rtxEnabled { + if err := me.RegisterCodec(webrtc.RTPCodecParameters{ + RTPCodecCapability: webrtc.RTPCodecCapability{ + MimeType: videoRTXMimeType, + ClockRate: 90000, + SDPFmtpLine: fmt.Sprintf("apt=%d", codec.PayloadType), + }, + PayloadType: codec.PayloadType + 1, + }, webrtc.RTPCodecTypeVideo); err != nil { + return err + } + } } } return nil diff --git a/pkg/rtc/mediatrack.go b/pkg/rtc/mediatrack.go index e2978d82d..73aa631a5 100644 --- a/pkg/rtc/mediatrack.go +++ b/pkg/rtc/mediatrack.go @@ -23,7 +23,6 @@ import ( "github.com/pion/webrtc/v3" "go.uber.org/atomic" - "github.com/livekit/mediatransportutil/pkg/twcc" "github.com/livekit/protocol/livekit" "github.com/livekit/protocol/logger" @@ -182,7 +181,7 @@ func (t *MediaTrack) UpdateCodecCid(codecs []*livekit.SimulcastCodec) { } // AddReceiver adds a new RTP receiver to the track, returns true when receiver represents a new codec -func (t *MediaTrack) AddReceiver(receiver *webrtc.RTPReceiver, track *webrtc.TrackRemote, twcc *twcc.Responder, mid string) bool { +func (t *MediaTrack) AddReceiver(receiver *webrtc.RTPReceiver, track *webrtc.TrackRemote, mid string) bool { var newCodec bool buff, rtcpReader := t.params.BufferFactory.GetBufferPair(uint32(track.SSRC())) if buff == nil || rtcpReader == nil { @@ -250,7 +249,6 @@ func (t *MediaTrack) AddReceiver(receiver *webrtc.RTPReceiver, track *webrtc.Tra track, ti, LoggerWithCodecMime(t.params.Logger, mime), - twcc, t.params.OnRTCP, t.params.VideoConfig.StreamTracker, sfu.WithPliThrottleConfig(t.params.PLIThrottleConfig), diff --git a/pkg/rtc/participant.go b/pkg/rtc/participant.go index 22bc2ac99..0a64d74c1 100644 --- a/pkg/rtc/participant.go +++ b/pkg/rtc/participant.go @@ -1205,6 +1205,10 @@ func (h PrimaryTransportHandler) OnFullyEstablished() { } func (p *ParticipantImpl) setupTransportManager() error { + p.twcc = twcc.NewTransportWideCCResponder() + p.twcc.OnFeedback(func(pkts []rtcp.Packet) { + p.postRtcp(pkts) + }) ath := AnyTransportHandler{p: p} var pth transport.Handler = PublisherTransportHandler{ath} var sth transport.Handler = SubscriberTransportHandler{ath} @@ -1223,6 +1227,7 @@ func (p *ParticipantImpl) setupTransportManager() error { // after the participant has joined SubscriberAsPrimary: subscriberAsPrimary, Config: p.params.Config, + Twcc: p.twcc, ProtocolVersion: p.params.ProtocolVersion, CongestionControlConfig: p.params.CongestionControlConfig, EnabledPublishCodecs: p.enabledPublishCodecs, @@ -1913,16 +1918,9 @@ func (p *ParticipantImpl) mediaTrackReceived(track *webrtc.TrackRemote, rtpRecei p.dirty.Store(true) } - ssrc := uint32(track.SSRC()) - if p.twcc == nil { - p.twcc = twcc.NewTransportWideCCResponder(ssrc) - p.twcc.OnFeedback(func(pkts []rtcp.Packet) { - p.postRtcp(pkts) - }) - } p.pendingTracksLock.Unlock() - if mt.AddReceiver(rtpReceiver, track, p.twcc, mid) { + if mt.AddReceiver(rtpReceiver, track, mid) { p.removePendingMigratedTrack(mt) } diff --git a/pkg/rtc/transport.go b/pkg/rtc/transport.go index c0e804d65..164d47926 100644 --- a/pkg/rtc/transport.go +++ b/pkg/rtc/transport.go @@ -17,6 +17,7 @@ package rtc import ( "fmt" "net" + "strconv" "strings" "sync" "time" @@ -37,11 +38,14 @@ import ( "github.com/livekit/livekit-server/pkg/config" "github.com/livekit/livekit-server/pkg/rtc/transport" "github.com/livekit/livekit-server/pkg/rtc/types" + lkinterceptor "github.com/livekit/livekit-server/pkg/sfu/interceptor" "github.com/livekit/livekit-server/pkg/sfu/pacer" "github.com/livekit/livekit-server/pkg/sfu/rtpextension" "github.com/livekit/livekit-server/pkg/sfu/streamallocator" + sfuutils "github.com/livekit/livekit-server/pkg/sfu/utils" "github.com/livekit/livekit-server/pkg/telemetry/prometheus" sutils "github.com/livekit/livekit-server/pkg/utils" + lktwcc "github.com/livekit/mediatransportutil/pkg/twcc" "github.com/livekit/protocol/livekit" "github.com/livekit/protocol/logger" "github.com/livekit/protocol/logger/pionlogger" @@ -199,6 +203,7 @@ type TransportParams struct { ParticipantIdentity livekit.ParticipantIdentity ProtocolVersion types.ProtocolVersion Config *WebRTCConfig + Twcc *lktwcc.Responder DirectionConfig DirectionConfig CongestionControlConfig config.CongestionControlConfig EnabledCodecs []*livekit.Codec @@ -326,6 +331,40 @@ func newPeerConnection(params TransportParams, onBandwidthEstimator func(estimat ir.Add(f) } } + + setTWCCForVideo := func(info *interceptor.StreamInfo) { + if !strings.HasPrefix(info.MimeType, "video") { + return + } + // rtx stream don't have rtcp feedback, always set twcc for rtx stream + twccFb := strings.HasSuffix(info.MimeType, "rtx") + if !twccFb { + for _, fb := range info.RTCPFeedback { + if fb.Type == webrtc.TypeRTCPFBTransportCC { + twccFb = true + break + } + } + } + if !twccFb { + return + } + + twccExtID := sfuutils.GetHeaderExtensionID(info.RTPHeaderExtensions, webrtc.RTPHeaderExtensionCapability{URI: sdp.TransportCCURI}) + if twccExtID != 0 { + if buffer := params.Config.BufferFactory.GetBuffer(info.SSRC); buffer != nil { + params.Logger.Debugw("set rtx twcc and ext id", "ssrc", info.SSRC, "twccExtID", twccExtID) + buffer.SetTWCCAndExtID(params.Twcc, uint8(twccExtID)) + } else { + params.Logger.Errorw("failed to get buffer for rtx stream", nil, "ssrc", info.SSRC) + } + } + } + // put rtx interceptor behind unhandle simulcast interceptor so it can get the correct mid & rid + ir.Add(lkinterceptor.NewRTXInfoExtractorFactory(setTWCCForVideo, func(repair, base uint32) { + params.Logger.Debugw("rtx pair found from extension", "repair", repair, "base", base) + params.Config.BufferFactory.SetRTXPair(repair, base) + }, params.Logger)) api := webrtc.NewAPI( webrtc.WithMediaEngine(me), webrtc.WithSettingEngine(se), @@ -1532,11 +1571,7 @@ func (t *PCTransport) handleRemoteDescriptionReceived(e *event) error { } } -func (t *PCTransport) isRemoteOfferRestartICE(sd *webrtc.SessionDescription) (string, bool, error) { - parsed, err := sd.Unmarshal() - if err != nil { - return "", false, err - } +func (t *PCTransport) isRemoteOfferRestartICE(parsed *sdp.SessionDescription) (string, bool, error) { user, pwd, err := lksdp.ExtractICECredential(parsed) if err != nil { return "", false, err @@ -1633,7 +1668,11 @@ func (t *PCTransport) createAndSendAnswer() error { } func (t *PCTransport) handleRemoteOfferReceived(sd *webrtc.SessionDescription) error { - iceCredential, offerRestartICE, err := t.isRemoteOfferRestartICE(sd) + parsed, err := sd.Unmarshal() + if err != nil { + return nil + } + iceCredential, offerRestartICE, err := t.isRemoteOfferRestartICE(parsed) if err != nil { return errors.Wrap(err, "check remote offer restart ice failed") } @@ -1655,6 +1694,13 @@ func (t *PCTransport) handleRemoteOfferReceived(sd *webrtc.SessionDescription) e if err := t.setRemoteDescription(*sd); err != nil { return err } + rtxRepairs := rtxRepairsFromSDP(parsed, t.params.Logger) + if len(rtxRepairs) > 0 { + t.params.Logger.Debugw("rtx pairs found from sdp", "ssrcs", rtxRepairs) + for repair, base := range rtxRepairs { + t.params.Config.BufferFactory.SetRTXPair(repair, base) + } + } if t.currentOfferIceCredential == "" || offerRestartICE { t.currentOfferIceCredential = iceCredential @@ -1778,3 +1824,35 @@ func configureAudioTransceiver(tr *webrtc.RTPTransceiver, stereo bool, nack bool tr.SetCodecPreferences(configCodecs) } + +func rtxRepairsFromSDP(s *sdp.SessionDescription, logger logger.Logger) map[uint32]uint32 { + rtxRepairFlows := map[uint32]uint32{} + for _, media := range s.MediaDescriptions { + for _, attr := range media.Attributes { + switch attr.Key { + case sdp.AttrKeySSRCGroup: + split := strings.Split(attr.Value, " ") + if split[0] == sdp.SemanticTokenFlowIdentification { + // Essentially lines like `a=ssrc-group:FID 2231627014 632943048` are processed by this section + // as this declares that the second SSRC (632943048) is a rtx repair flow (RFC4588) for the first + // (2231627014) as specified in RFC5576 + if len(split) == 3 { + baseSsrc, err := strconv.ParseUint(split[1], 10, 32) + if err != nil { + logger.Warnw("Failed to parse SSRC", err, "ssrc", split[1]) + continue + } + rtxRepairFlow, err := strconv.ParseUint(split[2], 10, 32) + if err != nil { + logger.Warnw("Failed to parse SSRC", err, "ssrc", split[2]) + continue + } + rtxRepairFlows[uint32(rtxRepairFlow)] = uint32(baseSsrc) + } + } + } + } + } + + return rtxRepairFlows +} diff --git a/pkg/rtc/transportmanager.go b/pkg/rtc/transportmanager.go index 401fea8e0..9dfbcce5f 100644 --- a/pkg/rtc/transportmanager.go +++ b/pkg/rtc/transportmanager.go @@ -33,6 +33,7 @@ import ( "github.com/livekit/livekit-server/pkg/rtc/types" "github.com/livekit/livekit-server/pkg/sfu" "github.com/livekit/livekit-server/pkg/sfu/pacer" + "github.com/livekit/mediatransportutil/pkg/twcc" "github.com/livekit/protocol/livekit" "github.com/livekit/protocol/logger" ) @@ -71,6 +72,7 @@ type TransportManagerParams struct { SID livekit.ParticipantID SubscriberAsPrimary bool Config *WebRTCConfig + Twcc *twcc.Responder ProtocolVersion types.ProtocolVersion CongestionControlConfig config.CongestionControlConfig EnabledSubscribeCodecs []*livekit.Codec @@ -131,6 +133,7 @@ func NewTransportManager(params TransportManagerParams) (*TransportManager, erro ParticipantIdentity: params.Identity, ProtocolVersion: params.ProtocolVersion, Config: params.Config, + Twcc: params.Twcc, DirectionConfig: params.Config.Publisher, CongestionControlConfig: params.CongestionControlConfig, EnabledCodecs: params.EnabledPublishCodecs, diff --git a/pkg/rtc/unhandlesimulcast.go b/pkg/rtc/unhandlesimulcast.go index 568c7dc1b..0c4292633 100644 --- a/pkg/rtc/unhandlesimulcast.go +++ b/pkg/rtc/unhandlesimulcast.go @@ -19,6 +19,8 @@ import ( "github.com/pion/rtp" "github.com/pion/sdp/v3" "github.com/pion/webrtc/v3" + + "github.com/livekit/livekit-server/pkg/sfu/utils" ) const ( @@ -109,20 +111,11 @@ type UnhandleSimulcastInterceptor struct { simTracks map[uint32]SimulcastTrackInfo } -func getHeaderExtensionID(extensions []interceptor.RTPHeaderExtension, extension webrtc.RTPHeaderExtensionCapability) int { - for _, h := range extensions { - if extension.URI == h.URI { - return h.ID - } - } - return 0 -} - func (u *UnhandleSimulcastInterceptor) BindRemoteStream(info *interceptor.StreamInfo, reader interceptor.RTPReader) interceptor.RTPReader { if t, ok := u.simTracks[info.SSRC]; ok { // if we support fec for simulcast streams at future, should get rsid extensions - midExtensionID := getHeaderExtensionID(info.RTPHeaderExtensions, webrtc.RTPHeaderExtensionCapability{URI: sdp.SDESMidURI}) - streamIDExtensionID := getHeaderExtensionID(info.RTPHeaderExtensions, webrtc.RTPHeaderExtensionCapability{URI: sdp.SDESRTPStreamIDURI}) + midExtensionID := utils.GetHeaderExtensionID(info.RTPHeaderExtensions, webrtc.RTPHeaderExtensionCapability{URI: sdp.SDESMidURI}) + streamIDExtensionID := utils.GetHeaderExtensionID(info.RTPHeaderExtensions, webrtc.RTPHeaderExtensionCapability{URI: sdp.SDESRTPStreamIDURI}) if midExtensionID == 0 || streamIDExtensionID == 0 { return reader } diff --git a/pkg/sfu/buffer/buffer.go b/pkg/sfu/buffer/buffer.go index 7e250c23a..bf73cf27a 100644 --- a/pkg/sfu/buffer/buffer.go +++ b/pkg/sfu/buffer/buffer.go @@ -71,6 +71,7 @@ type Buffer struct { videoPool *sync.Pool audioPool *sync.Pool codecType webrtc.RTPCodecType + payloadType uint8 extPackets deque.Deque[*ExtPacket] pPackets []pendingPacket closeOnce sync.Once @@ -122,6 +123,8 @@ type Buffer struct { packetNotFoundCount atomic.Uint32 packetTooOldCount atomic.Uint32 + + primaryBufferForRTX *Buffer } // NewBuffer constructs a new Buffer @@ -156,11 +159,12 @@ func (b *Buffer) SetPaused(paused bool) { b.paused = paused } -func (b *Buffer) SetTWCC(twcc *twcc.Responder) { +func (b *Buffer) SetTWCCAndExtID(twcc *twcc.Responder, extID uint8) { b.Lock() defer b.Unlock() b.twcc = twcc + b.twccExt = extID } func (b *Buffer) SetAudioLevelParams(audioLevelParams audio.AudioLevelParams) { @@ -187,6 +191,17 @@ func (b *Buffer) Bind(params webrtc.RTPParameters, codec webrtc.RTPCodecCapabili b.clockRate = codec.ClockRate b.lastReport = time.Now() b.mime = strings.ToLower(codec.MimeType) + for _, codecParameter := range params.Codecs { + if strings.EqualFold(codecParameter.MimeType, codec.MimeType) { + b.payloadType = uint8(codecParameter.PayloadType) + break + } + } + + if b.payloadType == 0 { + b.logger.Warnw("could not find payload type for codec", nil, "codec", codec.MimeType, "parameters", params) + b.payloadType = uint8(params.Codecs[0].PayloadType) + } for _, ext := range params.HeaderExtensions { switch ext.URI { @@ -235,16 +250,6 @@ func (b *Buffer) Bind(params webrtc.RTPParameters, codec webrtc.RTPCodecCapabili case webrtc.TypeRTCPFBGoogREMB: b.logger.Debugw("Setting feedback", "type", webrtc.TypeRTCPFBGoogREMB) b.logger.Debugw("REMB not supported, RTCP feedback will not be generated") - case webrtc.TypeRTCPFBTransportCC: - if b.codecType == webrtc.RTPCodecTypeVideo { - b.logger.Debugw("Setting feedback", "type", webrtc.TypeRTCPFBTransportCC) - for _, ext := range params.HeaderExtensions { - if ext.URI == sdp.TransportCCURI { - b.twccExt = uint8(ext.ID) - break - } - } - } case webrtc.TypeRTCPFBNACK: // pion use a single mediaengine to manage negotiated codecs of peerconnection, that means we can't have different // codec settings at track level for same codec type, so enable nack for all audio receivers but don't create nack queue @@ -258,7 +263,7 @@ func (b *Buffer) Bind(params webrtc.RTPParameters, codec webrtc.RTPCodecCapabili } for _, pp := range b.pPackets { - b.calc(pp.packet, pp.arrivalTime) + b.calc(pp.packet, nil, pp.arrivalTime, false) } b.pPackets = nil b.bound = true @@ -266,14 +271,38 @@ func (b *Buffer) Bind(params webrtc.RTPParameters, codec webrtc.RTPCodecCapabili // Write adds an RTP Packet, ordering is not guaranteed, newer packets may arrive later func (b *Buffer) Write(pkt []byte) (n int, err error) { - b.Lock() - defer b.Unlock() + var rtpPacket rtp.Packet + err = rtpPacket.Unmarshal(pkt) + if err != nil { + return + } + b.Lock() if b.closed.Load() { + b.Unlock() err = io.EOF return } + if b.twcc != nil && b.twccExt != 0 && !b.closed.Load() { + if ext := rtpPacket.GetExtension(b.twccExt); ext != nil { + b.twcc.Push(rtpPacket.SSRC, binary.BigEndian.Uint16(ext[0:2]), time.Now().UnixNano(), rtpPacket.Marker) + } + } + + // handle RTX packet + if pb := b.primaryBufferForRTX; pb != nil { + b.Unlock() + + // skip padding only packets + if rtpPacket.Padding && len(rtpPacket.Payload) == 0 { + return + } + + pb.writeRTX(&rtpPacket) + return + } + if !b.bound { packet := make([]byte, len(pkt)) copy(packet, pkt) @@ -281,10 +310,58 @@ func (b *Buffer) Write(pkt []byte) (n int, err error) { packet: packet, arrivalTime: time.Now(), }) + b.Unlock() return } - b.calc(pkt, time.Now()) + b.payloadType = rtpPacket.PayloadType + b.calc(pkt, &rtpPacket, time.Now(), false) + b.Unlock() + return +} + +func (b *Buffer) SetPrimaryBufferForRTX(primaryBuffer *Buffer) { + b.Lock() + b.primaryBufferForRTX = primaryBuffer + pkts := b.pPackets + b.pPackets = nil + b.Unlock() + for _, pp := range pkts { + var rtpPacket rtp.Packet + err := rtpPacket.Unmarshal(pp.packet) + if err != nil { + continue + } + if rtpPacket.Padding && len(rtpPacket.Payload) == 0 { + continue + } + primaryBuffer.writeRTX(&rtpPacket) + } +} + +func (b *Buffer) writeRTX(rtxPkt *rtp.Packet) (n int, err error) { + b.Lock() + defer b.Unlock() + if !b.bound { + return + } + + videoPktPtr := b.videoPool.Get().(*[]byte) + defer b.videoPool.Put(videoPktPtr) + + videoPkt := *rtxPkt + videoPkt.PayloadType = b.payloadType + videoPkt.SequenceNumber = binary.BigEndian.Uint16(rtxPkt.Payload[:2]) + videoPkt.SSRC = b.mediaSSRC + videoPkt.Payload = rtxPkt.Payload[2:] + n, err = videoPkt.MarshalTo((*videoPktPtr)) + + if err != nil { + b.logger.Errorw("could not marshal repaired packet", err, "ssrc", b.mediaSSRC, "sn", videoPkt.SequenceNumber) + return + } + + b.calc((*videoPktPtr)[:n], &videoPkt, time.Now(), true) return } @@ -414,23 +491,25 @@ func (b *Buffer) SetRTT(rtt uint32) { } } -func (b *Buffer) calc(pkt []byte, arrivalTime time.Time) { +func (b *Buffer) calc(rawPkt []byte, rtpPacket *rtp.Packet, arrivalTime time.Time, isRTX bool) { defer func() { b.doNACKs() b.doReports(arrivalTime) }() - var rtpPacket rtp.Packet - if err := rtpPacket.Unmarshal(pkt); err != nil { - b.logger.Errorw("could not unmarshal RTP packet", err) - return + if rtpPacket == nil { + rtpPacket = &rtp.Packet{} + if err := rtpPacket.Unmarshal(rawPkt); err != nil { + b.logger.Errorw("could not unmarshal RTP packet", err) + return + } } // process header extensions always as padding packets could be used for probing - b.processHeaderExtensions(&rtpPacket, arrivalTime) + b.processHeaderExtensions(rtpPacket, arrivalTime, isRTX) - flowState := b.updateStreamState(&rtpPacket, arrivalTime) + flowState := b.updateStreamState(rtpPacket, arrivalTime) if flowState.IsNotHandled { return } @@ -472,7 +551,7 @@ func (b *Buffer) calc(pkt []byte, arrivalTime time.Time) { } flowState.ExtSequenceNumber -= snAdjustment rtpPacket.Header.SequenceNumber = uint16(flowState.ExtSequenceNumber) - _, err = b.bucket.AddPacketWithSequenceNumber(pkt, rtpPacket.Header.SequenceNumber) + _, err = b.bucket.AddPacketWithSequenceNumber(rawPkt, rtpPacket.Header.SequenceNumber) if err != nil { if errors.Is(err, bucket.ErrPacketTooOld) { packetTooOldCount := b.packetTooOldCount.Inc() @@ -485,7 +564,7 @@ func (b *Buffer) calc(pkt []byte, arrivalTime time.Time) { return } - ep := b.getExtPacket(&rtpPacket, arrivalTime, flowState) + ep := b.getExtPacket(rtpPacket, arrivalTime, flowState) if ep == nil { return } @@ -570,16 +649,9 @@ func (b *Buffer) updateStreamState(p *rtp.Packet, arrivalTime time.Time) RTPFlow return flowState } -func (b *Buffer) processHeaderExtensions(p *rtp.Packet, arrivalTime time.Time) { - // submit to TWCC even if it is a padding only packet. Clients use padding only packets as probes - // for bandwidth estimation - if b.twcc != nil && b.twccExt != 0 { - if ext := p.GetExtension(b.twccExt); ext != nil { - b.twcc.Push(binary.BigEndian.Uint16(ext[0:2]), arrivalTime.UnixNano(), p.Marker) - } - } +func (b *Buffer) processHeaderExtensions(p *rtp.Packet, arrivalTime time.Time, isRTX bool) { - if b.audioLevelExt != 0 { + if b.audioLevelExt != 0 && !isRTX { if !b.latestTSForAudioLevelInitialized { b.latestTSForAudioLevelInitialized = true b.latestTSForAudioLevel = p.Timestamp diff --git a/pkg/sfu/buffer/factory.go b/pkg/sfu/buffer/factory.go index d0a9979f8..d59038d28 100644 --- a/pkg/sfu/buffer/factory.go +++ b/pkg/sfu/buffer/factory.go @@ -51,6 +51,7 @@ func (f *FactoryOfBufferFactory) CreateBufferFactory() *Factory { audioPool: f.audioPool, rtpBuffers: make(map[uint32]*Buffer), rtcpReaders: make(map[uint32]*RTCPReader), + rtxPair: make(map[uint32]uint32), } } @@ -60,6 +61,7 @@ type Factory struct { audioPool *sync.Pool rtpBuffers map[uint32]*Buffer rtcpReaders map[uint32]*RTCPReader + rtxPair map[uint32]uint32 // repair -> base } func (f *Factory) GetOrNew(packetType packetio.BufferPacketType, ssrc uint32) io.ReadWriteCloser { @@ -84,9 +86,25 @@ func (f *Factory) GetOrNew(packetType packetio.BufferPacketType, ssrc uint32) io } buffer := NewBuffer(ssrc, f.videoPool, f.audioPool) f.rtpBuffers[ssrc] = buffer + for repair, base := range f.rtxPair { + if repair == ssrc { + baseBuffer, ok := f.rtpBuffers[base] + if ok { + buffer.SetPrimaryBufferForRTX(baseBuffer) + } + break + } else if base == ssrc { + repairBuffer, ok := f.rtpBuffers[repair] + if ok { + repairBuffer.SetPrimaryBufferForRTX(buffer) + } + break + } + } buffer.OnClose(func() { f.Lock() delete(f.rtpBuffers, ssrc) + delete(f.rtxPair, ssrc) f.Unlock() }) return buffer @@ -111,3 +129,15 @@ func (f *Factory) GetRTCPReader(ssrc uint32) *RTCPReader { defer f.RUnlock() return f.rtcpReaders[ssrc] } + +func (f *Factory) SetRTXPair(repair, base uint32) { + f.Lock() + repairBuffer, baseBuffer := f.rtpBuffers[repair], f.rtpBuffers[base] + if repairBuffer == nil || baseBuffer == nil { + f.rtxPair[repair] = base + } + f.Unlock() + if repairBuffer != nil && baseBuffer != nil { + repairBuffer.SetPrimaryBufferForRTX(baseBuffer) + } +} diff --git a/pkg/sfu/downtrack.go b/pkg/sfu/downtrack.go index 470e11eed..54a948f7a 100644 --- a/pkg/sfu/downtrack.go +++ b/pkg/sfu/downtrack.go @@ -38,6 +38,7 @@ import ( dd "github.com/livekit/livekit-server/pkg/sfu/dependencydescriptor" "github.com/livekit/livekit-server/pkg/sfu/pacer" "github.com/livekit/livekit-server/pkg/sfu/rtpextension" + "github.com/livekit/livekit-server/pkg/sfu/utils" ) // TrackSender defines an interface send media to remote peer @@ -357,7 +358,7 @@ func (d *DownTrack) Bind(t webrtc.TrackLocalContext) (webrtc.RTPCodecParameters, } var codec webrtc.RTPCodecParameters for _, c := range d.upstreamCodecs { - matchCodec, err := codecParametersFuzzySearch(c, t.CodecParameters()) + matchCodec, err := utils.CodecParametersFuzzySearch(c, t.CodecParameters()) if err == nil { codec = matchCodec break diff --git a/pkg/sfu/interceptor/rtx.go b/pkg/sfu/interceptor/rtx.go new file mode 100644 index 000000000..624ede827 --- /dev/null +++ b/pkg/sfu/interceptor/rtx.go @@ -0,0 +1,168 @@ +package interceptor + +import ( + "sync" + + "github.com/pion/interceptor" + "github.com/pion/sdp/v3" + "github.com/pion/webrtc/v3" + + "github.com/livekit/livekit-server/pkg/sfu/utils" + "github.com/livekit/protocol/logger" +) + +const ( + SDESRepairRTPStreamIDURI = "urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id" + + rtxProbeCount = 10 +) + +type streamInfo struct { + mid string + rid string + rsid string +} + +type RTXInfoExtractorFactory struct { + onStreamFound func(*interceptor.StreamInfo) + onRTXPairFound func(repair, base uint32) + lock sync.Mutex + streams map[uint32]streamInfo + logger logger.Logger +} + +func NewRTXInfoExtractorFactory(onStreamFound func(*interceptor.StreamInfo), onRTXPairFound func(repair, base uint32), logger logger.Logger) *RTXInfoExtractorFactory { + return &RTXInfoExtractorFactory{ + onStreamFound: onStreamFound, + onRTXPairFound: onRTXPairFound, + streams: make(map[uint32]streamInfo), + logger: logger, + } +} + +func (f *RTXInfoExtractorFactory) NewInterceptor(id string) (interceptor.Interceptor, error) { + return &RTXInfoExtractor{ + factory: f, + logger: f.logger, + }, nil +} + +func (f *RTXInfoExtractorFactory) setStreamInfo(ssrc uint32, mid, rid, rsid string) { + var repairSsrc, baseSsrc uint32 + f.lock.Lock() + + if rsid != "" { + // repair stream found, find base stream + for base, info := range f.streams { + if info.mid == mid && info.rid == rsid { + repairSsrc = ssrc + baseSsrc = base + delete(f.streams, base) + break + } + } + } else { + // base stream found, find repair stream + for repair, info := range f.streams { + if info.mid == mid && info.rsid == rid { + repairSsrc = repair + baseSsrc = ssrc + delete(f.streams, repair) + break + } + } + } + + // no rtx pair found, save it for later + if repairSsrc == 0 || baseSsrc == 0 { + f.streams[ssrc] = streamInfo{ + mid: mid, + rid: rid, + rsid: rsid, + } + } + + f.lock.Unlock() + + if repairSsrc != 0 && baseSsrc != 0 { + f.onRTXPairFound(repairSsrc, baseSsrc) + } +} + +type RTXInfoExtractor struct { + interceptor.NoOp + + factory *RTXInfoExtractorFactory + logger logger.Logger +} + +func (u *RTXInfoExtractor) BindRemoteStream(info *interceptor.StreamInfo, reader interceptor.RTPReader) interceptor.RTPReader { + u.factory.onStreamFound(info) + + midExtensionID := utils.GetHeaderExtensionID(info.RTPHeaderExtensions, webrtc.RTPHeaderExtensionCapability{URI: sdp.SDESMidURI}) + streamIDExtensionID := utils.GetHeaderExtensionID(info.RTPHeaderExtensions, webrtc.RTPHeaderExtensionCapability{URI: sdp.SDESRTPStreamIDURI}) + repairStreamIDExtensionID := utils.GetHeaderExtensionID(info.RTPHeaderExtensions, webrtc.RTPHeaderExtensionCapability{URI: SDESRepairRTPStreamIDURI}) + if midExtensionID == 0 || streamIDExtensionID == 0 || repairStreamIDExtensionID == 0 { + return reader + } + + return &rtxInfoReader{ + tryTimes: rtxProbeCount, + reader: reader, + midExtID: uint8(midExtensionID), + ridExtID: uint8(streamIDExtensionID), + rsidExtID: uint8(repairStreamIDExtensionID), + factory: u.factory, + logger: u.logger, + } +} + +type rtxInfoReader struct { + tryTimes int + reader interceptor.RTPReader + midExtID uint8 + ridExtID uint8 + rsidExtID uint8 + factory *RTXInfoExtractorFactory + logger logger.Logger +} + +func (r *rtxInfoReader) Read(b []byte, a interceptor.Attributes) (int, interceptor.Attributes, error) { + n, a, err := r.reader.Read(b, a) + if r.tryTimes < 0 || err != nil { + return n, a, err + } + + if a == nil { + a = make(interceptor.Attributes) + } + header, err := a.GetRTPHeader(b[:n]) + if err != nil { + return n, a, nil + } + + var mid, rid, rsid string + if payload := header.GetExtension(r.midExtID); payload != nil { + mid = string(payload) + } + + if payload := header.GetExtension(r.ridExtID); payload != nil { + rid = string(payload) + } + + if payload := header.GetExtension(r.rsidExtID); payload != nil { + rsid = string(payload) + } + + if mid != "" && (rid != "" || rsid != "") { + r.logger.Debugw("stream found", "mid", mid, "rid", rid, "rsid", rsid, "ssrc", header.SSRC) + r.tryTimes = -1 + go r.factory.setStreamInfo(header.SSRC, mid, rid, rsid) + } else { + // ignore padding only packet for probe count + if !(header.Padding && n-header.MarshalSize()-int(b[n-1]) == 0) { + r.tryTimes-- + } + } + return n, a, nil +} diff --git a/pkg/sfu/receiver.go b/pkg/sfu/receiver.go index b05b2b23c..bdb8853ea 100644 --- a/pkg/sfu/receiver.go +++ b/pkg/sfu/receiver.go @@ -197,7 +197,6 @@ func NewWebRTCReceiver( track *webrtc.TrackRemote, trackInfo *livekit.TrackInfo, logger logger.Logger, - twcc *twcc.Responder, onRTCP func([]rtcp.Packet), trackersConfig config.StreamTrackersConfig, opts ...ReceiverOpts, @@ -209,7 +208,6 @@ func NewWebRTCReceiver( streamID: track.StreamID(), codec: track.Codec(), kind: track.Kind(), - twcc: twcc, onRTCP: onRTCP, isSVC: IsSvcCodec(track.Codec().MimeType), isRED: IsRedCodec(track.Codec().MimeType), @@ -340,7 +338,6 @@ func (w *WebRTCReceiver) AddUpTrack(track *webrtc.TrackRemote, buff *buffer.Buff layer = buffer.RidToSpatialLayer(track.RID(), w.trackInfo.Load()) } buff.SetLogger(w.logger.WithValues("layer", layer)) - buff.SetTWCC(w.twcc) buff.SetAudioLevelParams(audio.AudioLevelParams{ ActiveLevel: w.audioConfig.ActiveLevel, MinPercentile: w.audioConfig.MinPercentile, diff --git a/pkg/sfu/helpers.go b/pkg/sfu/utils/helpers.go similarity index 76% rename from pkg/sfu/helpers.go rename to pkg/sfu/utils/helpers.go index 1f4101910..476050416 100644 --- a/pkg/sfu/helpers.go +++ b/pkg/sfu/utils/helpers.go @@ -12,17 +12,18 @@ // See the License for the specific language governing permissions and // limitations under the License. -package sfu +package utils import ( "strings" + "github.com/pion/interceptor" "github.com/pion/webrtc/v3" ) // Do a fuzzy find for a codec in the list of codecs // Used for lookup up a codec in an existing list to find a match -func codecParametersFuzzySearch(needle webrtc.RTPCodecParameters, haystack []webrtc.RTPCodecParameters) (webrtc.RTPCodecParameters, error) { +func CodecParametersFuzzySearch(needle webrtc.RTPCodecParameters, haystack []webrtc.RTPCodecParameters) (webrtc.RTPCodecParameters, error) { // First attempt to match on MimeType + SDPFmtpLine for _, c := range haystack { if strings.EqualFold(c.RTPCodecCapability.MimeType, needle.RTPCodecCapability.MimeType) && @@ -41,4 +42,12 @@ func codecParametersFuzzySearch(needle webrtc.RTPCodecParameters, haystack []web return webrtc.RTPCodecParameters{}, webrtc.ErrCodecNotFound } -// ----------------------------------------------- +// GetHeaderExtensionID returns the ID of a header extension, or 0 if not found +func GetHeaderExtensionID(extensions []interceptor.RTPHeaderExtension, extension webrtc.RTPHeaderExtensionCapability) int { + for _, h := range extensions { + if extension.URI == h.URI { + return h.ID + } + } + return 0 +} From 2f9ec2117f7579fb34e90e2644e602dca2240ad5 Mon Sep 17 00:00:00 2001 From: Raja Subramanian Date: Tue, 6 Feb 2024 19:01:24 +0530 Subject: [PATCH 09/44] Do not enqueue after stop. (#2457) Else, that could leak as the process routine may have exited and the entries are not processed anywhere. --- pkg/utils/opsqueue.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pkg/utils/opsqueue.go b/pkg/utils/opsqueue.go index 01f9a12ff..30dbbe1ec 100644 --- a/pkg/utils/opsqueue.go +++ b/pkg/utils/opsqueue.go @@ -75,8 +75,12 @@ func (oq *OpsQueue) Enqueue(op func()) { oq.lock.Lock() defer oq.lock.Unlock() + if oq.isStopped { + return + } + oq.ops.PushBack(op) - if oq.ops.Len() == 1 && !oq.isStopped { + if oq.ops.Len() == 1 { select { case oq.wake <- struct{}{}: default: From af0a8fbbbce3d4d60874d6c605e010757773cd18 Mon Sep 17 00:00:00 2001 From: cnderrauber Date: Tue, 6 Feb 2024 21:38:36 +0800 Subject: [PATCH 10/44] add log for extpacket accumulated (#2454) --- go.mod | 2 +- go.sum | 4 ++-- pkg/sfu/buffer/buffer.go | 11 +++++++++-- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/go.mod b/go.mod index b75305fc9..16555caf0 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,7 @@ require ( github.com/hashicorp/golang-lru/v2 v2.0.7 github.com/jxskiss/base62 v1.1.0 github.com/livekit/mageutil v0.0.0-20230125210925-54e8a70427c1 - github.com/livekit/mediatransportutil v0.0.0-20240206022904-8e203f9db6af + github.com/livekit/mediatransportutil v0.0.0-20240206082112-9bf41dcbce76 github.com/livekit/protocol v1.9.8 github.com/livekit/psrpc v0.5.3-0.20240129223932-473b29cda289 github.com/mackerelio/go-osstat v0.2.4 diff --git a/go.sum b/go.sum index 917cf90d7..5e5f0e402 100644 --- a/go.sum +++ b/go.sum @@ -124,8 +124,8 @@ github.com/lithammer/shortuuid/v4 v4.0.0 h1:QRbbVkfgNippHOS8PXDkti4NaWeyYfcBTHtw github.com/lithammer/shortuuid/v4 v4.0.0/go.mod h1:Zs8puNcrvf2rV9rTH51ZLLcj7ZXqQI3lv67aw4KiB1Y= github.com/livekit/mageutil v0.0.0-20230125210925-54e8a70427c1 h1:jm09419p0lqTkDaKb5iXdynYrzB84ErPPO4LbRASk58= github.com/livekit/mageutil v0.0.0-20230125210925-54e8a70427c1/go.mod h1:Rs3MhFwutWhGwmY1VQsygw28z5bWcnEYmS1OG9OxjOQ= -github.com/livekit/mediatransportutil v0.0.0-20240206022904-8e203f9db6af h1:7VBmJFAqCRCCJxHG7c8z5LGEbwUGqzzw1weZgAewmLA= -github.com/livekit/mediatransportutil v0.0.0-20240206022904-8e203f9db6af/go.mod h1:GBzn9xL+mivI1pW+tyExcKgbc0VOc29I9yJsNcAVaAc= +github.com/livekit/mediatransportutil v0.0.0-20240206082112-9bf41dcbce76 h1:Zw88krOHni51OzDUlrduYb3m7VcsaKw06TnnDhsQpjg= +github.com/livekit/mediatransportutil v0.0.0-20240206082112-9bf41dcbce76/go.mod h1:GBzn9xL+mivI1pW+tyExcKgbc0VOc29I9yJsNcAVaAc= github.com/livekit/protocol v1.9.8 h1:CyK+oQ2TgSkRGgkJfDlJd5FRP42cahEFCOyxg2Sa/ts= github.com/livekit/protocol v1.9.8/go.mod h1:lSJlMeTJfQBEv8/D2p3zdCo+i+jTmTtn24ysL4ePK28= github.com/livekit/psrpc v0.5.3-0.20240129223932-473b29cda289 h1:oTgNH7v9TXsBgoltKk5mnWjv4qqcPF2iV+WtEVQ6ROM= diff --git a/pkg/sfu/buffer/buffer.go b/pkg/sfu/buffer/buffer.go index bf73cf27a..d657b6f8a 100644 --- a/pkg/sfu/buffer/buffer.go +++ b/pkg/sfu/buffer/buffer.go @@ -121,8 +121,9 @@ type Buffer struct { frameRateCalculator [DefaultMaxLayerSpatial + 1]FrameRateCalculator frameRateCalculated bool - packetNotFoundCount atomic.Uint32 - packetTooOldCount atomic.Uint32 + packetNotFoundCount atomic.Uint32 + packetTooOldCount atomic.Uint32 + extPacketTooMuchCount atomic.Uint32 primaryBufferForRTX *Buffer } @@ -570,6 +571,12 @@ func (b *Buffer) calc(rawPkt []byte, rtpPacket *rtp.Packet, arrivalTime time.Tim } b.extPackets.PushBack(ep) + if b.extPackets.Len() > b.bucket.Capacity() { + if (b.extPacketTooMuchCount.Inc()-1)%100 == 0 { + b.logger.Warnw("too much ext packets", nil, "count", b.extPackets.Len()) + } + } + b.doFpsCalc(ep) } From 5a310f961cfdcdbb0c96adb520353dfe72cb4190 Mon Sep 17 00:00:00 2001 From: Raja Subramanian Date: Tue, 6 Feb 2024 23:33:58 +0530 Subject: [PATCH 11/44] Log receiver close. (#2456) * Log receiver close. This is going to increase log volume, but want to check if peer connection close trickles back into receiver close. * log final close --- pkg/rtc/mediatrack.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pkg/rtc/mediatrack.go b/pkg/rtc/mediatrack.go index 73aa631a5..2dfbfb9bd 100644 --- a/pkg/rtc/mediatrack.go +++ b/pkg/rtc/mediatrack.go @@ -257,9 +257,11 @@ func (t *MediaTrack) AddReceiver(receiver *webrtc.RTPReceiver, track *webrtc.Tra sfu.WithStreamTrackers(), ) newWR.OnCloseHandler(func() { + t.params.Logger.Infow("webrtc receiver closed") t.MediaTrackReceiver.SetClosing() t.MediaTrackReceiver.ClearReceiver(mime, false) if t.MediaTrackReceiver.TryClose() { + t.params.Logger.Infow("mediaTrack closed") if t.dynacastManager != nil { t.dynacastManager.Close() } From e1fb69b634a980a0089014f6b37bb38ee3d8145c Mon Sep 17 00:00:00 2001 From: Raja Subramanian Date: Wed, 7 Feb 2024 09:32:56 +0530 Subject: [PATCH 12/44] Synthesize a track setting on sync state. (#2455) * Synthesize a track setting on sync state. * Add setting before subscription * clean up * Skip tracks that are not subscribed * protocol deps --- go.mod | 8 ++++---- go.sum | 16 ++++++++-------- pkg/rtc/room.go | 28 ++++++++++++++++++++++++++++ 3 files changed, 40 insertions(+), 12 deletions(-) diff --git a/go.mod b/go.mod index 16555caf0..4ac1d1f67 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-20240206082112-9bf41dcbce76 - github.com/livekit/protocol v1.9.8 + github.com/livekit/protocol v1.9.9-0.20240206170043-39eb9f38a0a6 github.com/livekit/psrpc v0.5.3-0.20240129223932-473b29cda289 github.com/mackerelio/go-osstat v0.2.4 github.com/magefile/mage v1.15.0 @@ -46,7 +46,7 @@ require ( github.com/urfave/cli/v2 v2.27.1 github.com/urfave/negroni/v3 v3.0.0 go.uber.org/atomic v1.11.0 - golang.org/x/exp v0.0.0-20240119083558-1b970713d09a + golang.org/x/exp v0.0.0-20240205201215-2c58cdc269a3 golang.org/x/sync v0.6.0 google.golang.org/protobuf v1.32.0 gopkg.in/yaml.v3 v3.0.1 @@ -70,7 +70,7 @@ require ( 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.5 // indirect + github.com/klauspost/compress v1.17.6 // 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 @@ -101,7 +101,7 @@ require ( golang.org/x/sys v0.16.0 // indirect golang.org/x/text v0.14.0 // indirect golang.org/x/tools v0.17.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240125205218-1f4bbc51befe // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240205150955-31a09d347014 // indirect google.golang.org/grpc v1.61.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect ) diff --git a/go.sum b/go.sum index 5e5f0e402..c1ebbe41d 100644 --- a/go.sum +++ b/go.sum @@ -108,8 +108,8 @@ github.com/jsimonetti/rtnetlink v0.0.0-20211022192332-93da33804786 h1:N527AHMa79 github.com/jsimonetti/rtnetlink v0.0.0-20211022192332-93da33804786/go.mod h1:v4hqbTdfQngbVSZJVWUhGE/lbTFf9jb+ygmNUDQMuOs= 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.5 h1:d4vBd+7CHydUqpFBgUEKkSdtSugf9YFmSkvUYPquI5E= -github.com/klauspost/compress v1.17.5/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= +github.com/klauspost/compress v1.17.6 h1:60eq2E/jlfwQXtvZEeBUYADs+BwKBWURIY+Gj2eRGjI= +github.com/klauspost/compress v1.17.6/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= 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= @@ -126,8 +126,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-20240206082112-9bf41dcbce76 h1:Zw88krOHni51OzDUlrduYb3m7VcsaKw06TnnDhsQpjg= github.com/livekit/mediatransportutil v0.0.0-20240206082112-9bf41dcbce76/go.mod h1:GBzn9xL+mivI1pW+tyExcKgbc0VOc29I9yJsNcAVaAc= -github.com/livekit/protocol v1.9.8 h1:CyK+oQ2TgSkRGgkJfDlJd5FRP42cahEFCOyxg2Sa/ts= -github.com/livekit/protocol v1.9.8/go.mod h1:lSJlMeTJfQBEv8/D2p3zdCo+i+jTmTtn24ysL4ePK28= +github.com/livekit/protocol v1.9.9-0.20240206170043-39eb9f38a0a6 h1:85NwqH78bbR1r9XG7jOPfUj/VlcvoMF7tXQqh3fkgUk= +github.com/livekit/protocol v1.9.9-0.20240206170043-39eb9f38a0a6/go.mod h1:lSJlMeTJfQBEv8/D2p3zdCo+i+jTmTtn24ysL4ePK28= github.com/livekit/psrpc v0.5.3-0.20240129223932-473b29cda289 h1:oTgNH7v9TXsBgoltKk5mnWjv4qqcPF2iV+WtEVQ6ROM= github.com/livekit/psrpc v0.5.3-0.20240129223932-473b29cda289/go.mod h1:cQjxg1oCxYHhxxv6KJH1gSvdtCHQoRZCHgPdm5N8v2g= github.com/mackerelio/go-osstat v0.2.4 h1:qxGbdPkFo65PXOb/F/nhDKpF2nGmGaCFDLXoZjJTtUs= @@ -297,8 +297,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.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= -golang.org/x/exp v0.0.0-20240119083558-1b970713d09a h1:Q8/wZp0KX97QFTc2ywcOE0YRjZPVIx+MXInMzdvQqcA= -golang.org/x/exp v0.0.0-20240119083558-1b970713d09a/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08= +golang.org/x/exp v0.0.0-20240205201215-2c58cdc269a3 h1:/RIbNt/Zr7rVhIkQhooTxCxFcdWLGIKnZA4IXNFSrvo= +golang.org/x/exp v0.0.0-20240205201215-2c58cdc269a3/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08= 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= @@ -417,8 +417,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-20240125205218-1f4bbc51befe h1:bQnxqljG/wqi4NTXu2+DJ3n7APcEA882QZ1JvhQAq9o= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240125205218-1f4bbc51befe/go.mod h1:PAREbraiVEVGVdTZsVWjSbbTtSyGbAgIIvni8a8CD5s= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240205150955-31a09d347014 h1:FSL3lRCkhaPFxqi0s9o+V4UI2WTzAVOvkgbd4kVV4Wg= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240205150955-31a09d347014/go.mod h1:SaPjaZGWb0lPqs6Ittu0spdfrOArqji4ZdeP5IC/9N4= google.golang.org/grpc v1.61.0 h1:TOvOcuXn30kRao+gfcvsebNEa5iZIiLkisYEkf7R7o0= google.golang.org/grpc v1.61.0/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= diff --git a/pkg/rtc/room.go b/pkg/rtc/room.go index 4e848a7c5..fe850c578 100644 --- a/pkg/rtc/room.go +++ b/pkg/rtc/room.go @@ -686,6 +686,34 @@ func (r *Room) SyncState(participant types.LocalParticipant, state *livekit.Sync return nil } + // synthesize a track setting for each disabled track, + // can be set before addding subscriptions, + // in fact it is done before so that setting can be updated immediately upon subscription. + for _, trackSid := range state.TrackSidsDisabled { + // skip if it is not present in the subscribe list + found := false + for _, sid := range state.Subscription.TrackSids { + if sid == trackSid { + found = true + break + } + } + if !found { + participant_tracks: + for _, pt := range state.Subscription.ParticipantTracks { + for _, sid := range pt.TrackSids { + if sid == trackSid { + found = true + break participant_tracks + } + } + } + } + if found { + participant.UpdateSubscribedTrackSettings(livekit.TrackID(trackSid), &livekit.UpdateTrackSettings{Disabled: true}) + } + } + r.UpdateSubscriptions( participant, livekit.StringsAsIDs[livekit.TrackID](state.Subscription.TrackSids), From f4f0b3336bf52319adc4d7a108c2f0eec23cbe84 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 6 Feb 2024 21:31:15 -0800 Subject: [PATCH 13/44] Update module github.com/pion/turn/v2 to v2.1.5 (#2450) Generated by renovateBot Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 4ac1d1f67..b35c08e29 100644 --- a/go.mod +++ b/go.mod @@ -33,7 +33,7 @@ require ( github.com/pion/sctp v1.8.10 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/turn/v2 v2.1.5 github.com/pion/webrtc/v3 v3.2.25 github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.18.0 From f95194c833b1c88ac4cebe03626a07bc20bb6d0b Mon Sep 17 00:00:00 2001 From: Raja Subramanian Date: Wed, 7 Feb 2024 13:52:57 +0530 Subject: [PATCH 14/44] Fixes to sync state disabled tracks. (#2459) * Fixes to sync state disabled tracks. * test --- pkg/rtc/room.go | 25 ++----------------- pkg/rtc/subscribedtrack.go | 12 ++++++--- pkg/rtc/subscriptionmanager.go | 6 ++--- pkg/rtc/subscriptionmanager_test.go | 2 +- pkg/rtc/types/interfaces.go | 2 +- .../types/typesfakes/fake_subscribed_track.go | 18 +++++++------ 6 files changed, 25 insertions(+), 40 deletions(-) diff --git a/pkg/rtc/room.go b/pkg/rtc/room.go index fe850c578..622e5e62b 100644 --- a/pkg/rtc/room.go +++ b/pkg/rtc/room.go @@ -649,7 +649,7 @@ func (r *Room) UpdateSubscriptions( func (r *Room) SyncState(participant types.LocalParticipant, state *livekit.SyncState) error { pLogger := participant.GetLogger() - pLogger.Infow("setting sync state", "state", state) + pLogger.Infow("setting sync state", "state", logger.Proto(state)) shouldReconnect := false pubTracks := state.GetPublishTracks() @@ -690,28 +690,7 @@ func (r *Room) SyncState(participant types.LocalParticipant, state *livekit.Sync // can be set before addding subscriptions, // in fact it is done before so that setting can be updated immediately upon subscription. for _, trackSid := range state.TrackSidsDisabled { - // skip if it is not present in the subscribe list - found := false - for _, sid := range state.Subscription.TrackSids { - if sid == trackSid { - found = true - break - } - } - if !found { - participant_tracks: - for _, pt := range state.Subscription.ParticipantTracks { - for _, sid := range pt.TrackSids { - if sid == trackSid { - found = true - break participant_tracks - } - } - } - } - if found { - participant.UpdateSubscribedTrackSettings(livekit.TrackID(trackSid), &livekit.UpdateTrackSettings{Disabled: true}) - } + participant.UpdateSubscribedTrackSettings(livekit.TrackID(trackSid), &livekit.UpdateTrackSettings{Disabled: true}) } r.UpdateSubscriptions( diff --git a/pkg/rtc/subscribedtrack.go b/pkg/rtc/subscribedtrack.go index fd1eb7f94..d873c25f2 100644 --- a/pkg/rtc/subscribedtrack.go +++ b/pkg/rtc/subscribedtrack.go @@ -189,7 +189,7 @@ func (t *SubscribedTrack) SetPublisherMuted(muted bool) { t.updateDownTrackMute() } -func (t *SubscribedTrack) UpdateSubscriberSettings(settings *livekit.UpdateTrackSettings) { +func (t *SubscribedTrack) UpdateSubscriberSettings(settings *livekit.UpdateTrackSettings, isImmediate bool) { prevDisabled := t.subMuted.Swap(settings.Disabled) t.settings.Store(settings) @@ -197,11 +197,15 @@ func (t *SubscribedTrack) UpdateSubscriberSettings(settings *livekit.UpdateTrack t.logger.Debugw("updated subscribed track enabled", "enabled", !settings.Disabled) } - // avoid frequent changes to mute & video layers, unless it became visible - if prevDisabled != settings.Disabled && !settings.Disabled { + if isImmediate { t.UpdateVideoLayer() } else { - t.debouncer(t.UpdateVideoLayer) + // avoid frequent changes to mute & video layers, unless it became visible + if prevDisabled != settings.Disabled && !settings.Disabled { + t.UpdateVideoLayer() + } else { + t.debouncer(t.UpdateVideoLayer) + } } } diff --git a/pkg/rtc/subscriptionmanager.go b/pkg/rtc/subscriptionmanager.go index c53791aa5..73e5acbe0 100644 --- a/pkg/rtc/subscriptionmanager.go +++ b/pkg/rtc/subscriptionmanager.go @@ -823,8 +823,8 @@ func (s *trackSubscription) setSubscribedTrack(track types.SubscribedTrack) { s.lock.Unlock() if settings != nil && track != nil { - s.logger.Debugw("restoring subscriber settings", "settings", settings) - track.UpdateSubscriberSettings(settings) + s.logger.Debugw("restoring subscriber settings", "settings", logger.Proto(settings)) + track.UpdateSubscriberSettings(settings, true) } if oldTrack != nil { oldTrack.OnClose(nil) @@ -895,7 +895,7 @@ func (s *trackSubscription) setSettings(settings *livekit.UpdateTrackSettings) { subTrack := s.subscribedTrack s.lock.Unlock() if subTrack != nil { - subTrack.UpdateSubscriberSettings(settings) + subTrack.UpdateSubscriberSettings(settings, false) } } diff --git a/pkg/rtc/subscriptionmanager_test.go b/pkg/rtc/subscriptionmanager_test.go index 9ad6f20ea..a666f47f6 100644 --- a/pkg/rtc/subscriptionmanager_test.go +++ b/pkg/rtc/subscriptionmanager_test.go @@ -344,7 +344,7 @@ func TestUpdateSettingsBeforeSubscription(t *testing.T) { return st.UpdateSubscriberSettingsCallCount() == 1 }, subSettleTimeout, subCheckInterval, "UpdateSubscriberSettings should be called once") - applied := st.UpdateSubscriberSettingsArgsForCall(0) + applied, _ := st.UpdateSubscriberSettingsArgsForCall(0) require.Equal(t, settings.Disabled, applied.Disabled) require.Equal(t, settings.Width, applied.Width) require.Equal(t, settings.Height, applied.Height) diff --git a/pkg/rtc/types/interfaces.go b/pkg/rtc/types/interfaces.go index 32eb1e35c..a08443d7a 100644 --- a/pkg/rtc/types/interfaces.go +++ b/pkg/rtc/types/interfaces.go @@ -523,7 +523,7 @@ type SubscribedTrack interface { RTPSender() *webrtc.RTPSender IsMuted() bool SetPublisherMuted(muted bool) - UpdateSubscriberSettings(settings *livekit.UpdateTrackSettings) + UpdateSubscriberSettings(settings *livekit.UpdateTrackSettings, isImmediate bool) // selects appropriate video layer according to subscriber preferences UpdateVideoLayer() NeedsNegotiation() bool diff --git a/pkg/rtc/types/typesfakes/fake_subscribed_track.go b/pkg/rtc/types/typesfakes/fake_subscribed_track.go index 833b8e4b3..375e2cb44 100644 --- a/pkg/rtc/types/typesfakes/fake_subscribed_track.go +++ b/pkg/rtc/types/typesfakes/fake_subscribed_track.go @@ -161,10 +161,11 @@ type FakeSubscribedTrack struct { subscriberIdentityReturnsOnCall map[int]struct { result1 livekit.ParticipantIdentity } - UpdateSubscriberSettingsStub func(*livekit.UpdateTrackSettings) + UpdateSubscriberSettingsStub func(*livekit.UpdateTrackSettings, bool) updateSubscriberSettingsMutex sync.RWMutex updateSubscriberSettingsArgsForCall []struct { arg1 *livekit.UpdateTrackSettings + arg2 bool } UpdateVideoLayerStub func() updateVideoLayerMutex sync.RWMutex @@ -991,16 +992,17 @@ func (fake *FakeSubscribedTrack) SubscriberIdentityReturnsOnCall(i int, result1 }{result1} } -func (fake *FakeSubscribedTrack) UpdateSubscriberSettings(arg1 *livekit.UpdateTrackSettings) { +func (fake *FakeSubscribedTrack) UpdateSubscriberSettings(arg1 *livekit.UpdateTrackSettings, arg2 bool) { fake.updateSubscriberSettingsMutex.Lock() fake.updateSubscriberSettingsArgsForCall = append(fake.updateSubscriberSettingsArgsForCall, struct { arg1 *livekit.UpdateTrackSettings - }{arg1}) + arg2 bool + }{arg1, arg2}) stub := fake.UpdateSubscriberSettingsStub - fake.recordInvocation("UpdateSubscriberSettings", []interface{}{arg1}) + fake.recordInvocation("UpdateSubscriberSettings", []interface{}{arg1, arg2}) fake.updateSubscriberSettingsMutex.Unlock() if stub != nil { - fake.UpdateSubscriberSettingsStub(arg1) + fake.UpdateSubscriberSettingsStub(arg1, arg2) } } @@ -1010,17 +1012,17 @@ func (fake *FakeSubscribedTrack) UpdateSubscriberSettingsCallCount() int { return len(fake.updateSubscriberSettingsArgsForCall) } -func (fake *FakeSubscribedTrack) UpdateSubscriberSettingsCalls(stub func(*livekit.UpdateTrackSettings)) { +func (fake *FakeSubscribedTrack) UpdateSubscriberSettingsCalls(stub func(*livekit.UpdateTrackSettings, bool)) { fake.updateSubscriberSettingsMutex.Lock() defer fake.updateSubscriberSettingsMutex.Unlock() fake.UpdateSubscriberSettingsStub = stub } -func (fake *FakeSubscribedTrack) UpdateSubscriberSettingsArgsForCall(i int) *livekit.UpdateTrackSettings { +func (fake *FakeSubscribedTrack) UpdateSubscriberSettingsArgsForCall(i int) (*livekit.UpdateTrackSettings, bool) { fake.updateSubscriberSettingsMutex.RLock() defer fake.updateSubscriberSettingsMutex.RUnlock() argsForCall := fake.updateSubscriberSettingsArgsForCall[i] - return argsForCall.arg1 + return argsForCall.arg1, argsForCall.arg2 } func (fake *FakeSubscribedTrack) UpdateVideoLayer() { From 89e58b150e126d18d7b4928de9ed76a5c6a8f212 Mon Sep 17 00:00:00 2001 From: Raja Subramanian Date: Wed, 7 Feb 2024 14:52:39 +0530 Subject: [PATCH 15/44] Update TURN (#2460) --- go.sum | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/go.sum b/go.sum index c1ebbe41d..839e3654f 100644 --- a/go.sum +++ b/go.sum @@ -223,8 +223,8 @@ github.com/pion/transport/v2 v2.2.4/go.mod h1:q2U/tf9FEfnSBGSW6w5Qp5PFWRLRj3NjLh 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/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/turn/v2 v2.1.5 h1:tTyy7TM3DCoX9IxTt/yHc/bThiRLyXK3T1YbNcgx9k4= +github.com/pion/turn/v2 v2.1.5/go.mod h1:huEpByKKHix2/b9kmTAM3YoX6MKP+/D//0ClgUYR2fY= github.com/pion/webrtc/v3 v3.2.25 h1:hmewKZtY60TOMOnAfEfHI+D78VpDe3OdnwcRpE8kLEc= github.com/pion/webrtc/v3 v3.2.25/go.mod h1:1CaT2fcZzZ6VZA+O1i9yK2DU4EOcXVvSbWG9pr5jefs= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= From fd35c9edfc13721b269eb5798eeca985fb0bb3c7 Mon Sep 17 00:00:00 2001 From: Paul Wells Date: Wed, 7 Feb 2024 19:35:58 -0800 Subject: [PATCH 16/44] add exponential backoff to room service check retries (#2462) --- go.mod | 1 + go.sum | 2 ++ pkg/config/config.go | 11 +++++++---- pkg/service/roomservice.go | 31 +++++++++++++------------------ 4 files changed, 23 insertions(+), 22 deletions(-) diff --git a/go.mod b/go.mod index b35c08e29..98306c775 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/livekit/livekit-server go 1.20 require ( + github.com/avast/retry-go/v4 v4.5.1 github.com/bep/debounce v1.2.1 github.com/d5/tengo/v2 v2.16.1 github.com/dustin/go-humanize v1.0.1 diff --git a/go.sum b/go.sum index 839e3654f..cf28d015c 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +github.com/avast/retry-go/v4 v4.5.1 h1:AxIx0HGi4VZ3I02jr78j5lZ3M6x1E0Ivxa6b0pUUh7o= +github.com/avast/retry-go/v4 v4.5.1/go.mod h1:/sipNsvNB3RRuT5iNcb6h73nw3IBmXJ/H3XrCQYSOpc= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bep/debounce v1.2.1 h1:v67fRdBA9UQu2NhLFXrSg0Brw7CexQekrBwDMM8bzeY= diff --git a/pkg/config/config.go b/pkg/config/config.go index 0d18022c7..7fef6b5fc 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -297,19 +297,22 @@ type IngressConfig struct { type SIPConfig struct { } -// not exposed to YAML type APIConfig struct { // amount of time to wait for API to execute, default 2s - ExecutionTimeout time.Duration + ExecutionTimeout time.Duration `yaml:"execution_timeout,omitempty"` - // amount of time to wait before checking for operation complete - CheckInterval time.Duration + // min amount of time to wait before checking for operation complete + CheckInterval time.Duration `yaml:"check_interval,omitempty"` + + // max amount of time to wait before checking for operation complete + MaxCheckInterval time.Duration `yaml:"max_check_interval,omitempty"` } func DefaultAPIConfig() APIConfig { return APIConfig{ ExecutionTimeout: 2 * time.Second, CheckInterval: 100 * time.Millisecond, + MaxCheckInterval: 300 * time.Second, } } diff --git a/pkg/service/roomservice.go b/pkg/service/roomservice.go index d9bac7065..9ea7ebf0e 100644 --- a/pkg/service/roomservice.go +++ b/pkg/service/roomservice.go @@ -17,8 +17,8 @@ package service import ( "context" "strconv" - "time" + "github.com/avast/retry-go/v4" "github.com/pkg/errors" "github.com/twitchtv/twirp" @@ -101,7 +101,7 @@ func (s *RoomService) CreateRoom(ctx context.Context, req *livekit.CreateRoomReq defer res.ResponseSource.Close() // ensure it's created correctly - err = s.confirmExecution(func() error { + err = s.confirmExecution(ctx, func() error { _, _, err := s.roomStore.LoadRoom(ctx, livekit.RoomName(req.Name), false) if err != nil { return ErrOperationFailed @@ -299,7 +299,7 @@ func (s *RoomService) UpdateRoomMetadata(ctx context.Context, req *livekit.Updat return nil, err } - err = s.confirmExecution(func() error { + err = s.confirmExecution(ctx, func() error { room, _, err = s.roomStore.LoadRoom(ctx, livekit.RoomName(req.Room), false) if err != nil { return err @@ -326,19 +326,14 @@ func (s *RoomService) UpdateRoomMetadata(ctx context.Context, req *livekit.Updat return room, nil } -func (s *RoomService) confirmExecution(f func() error) error { - expired := time.After(s.apiConf.ExecutionTimeout) - var err error - for { - select { - case <-expired: - return err - default: - err = f() - if err == nil { - return nil - } - time.Sleep(s.apiConf.CheckInterval) - } - } +func (s *RoomService) confirmExecution(ctx context.Context, f func() error) error { + ctx, cancel := context.WithTimeout(ctx, s.apiConf.ExecutionTimeout) + defer cancel() + return retry.Do( + f, + retry.Context(ctx), + retry.Delay(s.apiConf.CheckInterval), + retry.MaxDelay(s.apiConf.MaxCheckInterval), + retry.DelayType(retry.BackOffDelay), + ) } From d20811d1c2f3f0d4abae551d1396fb18220d3fa9 Mon Sep 17 00:00:00 2001 From: Raja Subramanian Date: Thu, 8 Feb 2024 18:58:48 +0530 Subject: [PATCH 17/44] Ignore `disabled` when adpative stream is enabled. (#2463) * Ignore `disabled` when adpative stream is enabled. Due to interplay of adaptive stream/visibility/dynacast, when adaptive stream is enabled, subscribed track forces visibility and starts streaming at low quality. This would trigger a render on client and trigger a visibility update. So, even if a migration disables a track, upon migration complete and subscription bind, ignore disable and stream. * don't hold lock during callback * don't need to store pubMuted * don't need to hold settings lock for pub muted --- pkg/rtc/mediatracksubscriptions.go | 5 +- pkg/rtc/subscribedtrack.go | 155 +++++++++++++++++++---------- 2 files changed, 101 insertions(+), 59 deletions(-) diff --git a/pkg/rtc/mediatracksubscriptions.go b/pkg/rtc/mediatracksubscriptions.go index e074c2577..d960f8de2 100644 --- a/pkg/rtc/mediatracksubscriptions.go +++ b/pkg/rtc/mediatracksubscriptions.go @@ -404,10 +404,7 @@ func (t *MediaTrackSubscriptions) DebugInfo() []map[string]interface{} { subscribedTrackInfo := make([]map[string]interface{}, 0) for _, val := range t.getAllSubscribedTracks() { if st, ok := val.(*SubscribedTrack); ok { - dt := st.DownTrack().DebugInfo() - dt["PubMuted"] = st.pubMuted.Load() - dt["SubMuted"] = st.subMuted.Load() - subscribedTrackInfo = append(subscribedTrackInfo, dt) + subscribedTrackInfo = append(subscribedTrackInfo, st.DownTrack().DebugInfo()) } } diff --git a/pkg/rtc/subscribedtrack.go b/pkg/rtc/subscribedtrack.go index d873c25f2..3cdf12d4e 100644 --- a/pkg/rtc/subscribedtrack.go +++ b/pkg/rtc/subscribedtrack.go @@ -21,10 +21,12 @@ import ( "github.com/bep/debounce" "github.com/pion/webrtc/v3" "go.uber.org/atomic" + "google.golang.org/protobuf/proto" sutils "github.com/livekit/livekit-server/pkg/utils" "github.com/livekit/protocol/livekit" "github.com/livekit/protocol/logger" + "github.com/livekit/protocol/utils" "github.com/livekit/livekit-server/pkg/rtc/types" "github.com/livekit/livekit-server/pkg/sfu" @@ -47,17 +49,20 @@ type SubscribedTrackParams struct { type SubscribedTrack struct { params SubscribedTrackParams - subMuted atomic.Bool - pubMuted atomic.Bool - settings atomic.Pointer[livekit.UpdateTrackSettings] logger logger.Logger sender atomic.Pointer[webrtc.RTPSender] needsNegotiation atomic.Bool + versionGenerator utils.TimedVersionGenerator + settingsLock sync.Mutex + settings *livekit.UpdateTrackSettings + settingsVersion *utils.TimedVersion + bindLock sync.Mutex + bound bool onBindCallbacks []func(error) - onClose atomic.Value // func(bool) - bound atomic.Bool + + onClose atomic.Value // func(bool) debouncer func(func()) } @@ -70,7 +75,8 @@ func NewSubscribedTrack(params SubscribedTrackParams) *SubscribedTrack { "publisherID", params.PublisherID, "publisher", params.PublisherIdentity, ), - debouncer: debounce.New(subscriptionDebounceInterval), + versionGenerator: utils.NewDefaultTimedVersionGenerator(), + debouncer: debounce.New(subscriptionDebounceInterval), } return s @@ -78,7 +84,7 @@ func NewSubscribedTrack(params SubscribedTrackParams) *SubscribedTrack { func (t *SubscribedTrack) AddOnBind(f func(error)) { t.bindLock.Lock() - bound := t.bound.Load() + bound := t.bound if !bound { t.onBindCallbacks = append(t.onBindCallbacks, f) } @@ -94,7 +100,7 @@ func (t *SubscribedTrack) AddOnBind(f func(error)) { func (t *SubscribedTrack) Bound(err error) { t.bindLock.Lock() if err == nil { - t.bound.Store(true) + t.bound = true } callbacks := t.onBindCallbacks t.onBindCallbacks = nil @@ -110,17 +116,21 @@ func (t *SubscribedTrack) Bound(err error) { // time of subscription, we might not be able to trigger adaptive stream updates on the client side // (since there isn't any video frames coming through). this will leave the stream "stuck" on off, without // a trigger to re-enable it - var desiredLayer int32 - if t.params.AdaptiveStream { - desiredLayer = buffer.VideoQualityToSpatialLayer(livekit.VideoQuality_LOW, t.params.MediaTrack.ToProto()) + t.settingsLock.Lock() + if t.settings != nil { + if t.params.AdaptiveStream { + // remove `disabled` flag to force a visibility update + t.settings.Disabled = false + } } else { - desiredLayer = buffer.VideoQualityToSpatialLayer(livekit.VideoQuality_HIGH, t.params.MediaTrack.ToProto()) + if t.params.AdaptiveStream { + t.settings = &livekit.UpdateTrackSettings{Quality: livekit.VideoQuality_LOW} + } else { + t.settings = &livekit.UpdateTrackSettings{Quality: livekit.VideoQuality_HIGH} + } } - settings := t.settings.Load() - if settings != nil { - desiredLayer = t.spatialLayerFromSettings(settings) - } - t.DownTrack().SetMaxSpatialLayer(desiredLayer) + t.settingsLock.Unlock() + t.applySettings() } for _, cb := range callbacks { @@ -140,7 +150,10 @@ func (t *SubscribedTrack) OnClose(f func(bool)) { } func (t *SubscribedTrack) IsBound() bool { - return t.bound.Load() + t.bindLock.Lock() + defer t.bindLock.Unlock() + + return t.bound } func (t *SubscribedTrack) ID() livekit.TrackID { @@ -181,51 +194,97 @@ func (t *SubscribedTrack) MediaTrack() types.MediaTrack { // has subscriber indicated it wants to mute this track func (t *SubscribedTrack) IsMuted() bool { - return t.subMuted.Load() + t.settingsLock.Lock() + defer t.settingsLock.Unlock() + + return t.isMutedLocked() +} + +func (t *SubscribedTrack) isMutedLocked() bool { + if t.settings == nil { + return false + } + + return t.settings.Disabled } func (t *SubscribedTrack) SetPublisherMuted(muted bool) { - t.pubMuted.Store(muted) - t.updateDownTrackMute() + t.DownTrack().PubMute(muted) } func (t *SubscribedTrack) UpdateSubscriberSettings(settings *livekit.UpdateTrackSettings, isImmediate bool) { - prevDisabled := t.subMuted.Swap(settings.Disabled) - t.settings.Store(settings) - - if prevDisabled != settings.Disabled { - t.logger.Debugw("updated subscribed track enabled", "enabled", !settings.Disabled) + t.settingsLock.Lock() + if proto.Equal(t.settings, settings) { + t.settingsLock.Unlock() + return } + isImmediate = isImmediate || (!settings.Disabled && settings.Disabled != t.isMutedLocked()) + t.settings = proto.Clone(settings).(*livekit.UpdateTrackSettings) + t.settingsLock.Unlock() + if isImmediate { - t.UpdateVideoLayer() + t.applySettings() } else { // avoid frequent changes to mute & video layers, unless it became visible - if prevDisabled != settings.Disabled && !settings.Disabled { - t.UpdateVideoLayer() - } else { - t.debouncer(t.UpdateVideoLayer) - } + t.debouncer(t.applySettings) } } func (t *SubscribedTrack) UpdateVideoLayer() { - t.updateDownTrackMute() - if t.DownTrack().Kind() != webrtc.RTPCodecTypeVideo { + t.applySettings() +} + +func (t *SubscribedTrack) applySettings() { + t.settingsLock.Lock() + if t.settings == nil { + t.settingsLock.Unlock() return } - settings := t.settings.Load() - if settings == nil || settings.Disabled { + t.logger.Debugw("updating subscriber track settings", "settings", logger.Proto(t.settings)) + t.settingsVersion = t.versionGenerator.New() + settingsVersion := t.settingsVersion + t.settingsLock.Unlock() + + dt := t.DownTrack() + spatial := buffer.InvalidLayerSpatial + temporal := buffer.InvalidLayerTemporal + if dt.Kind() == webrtc.RTPCodecTypeVideo { + mt := t.MediaTrack() + quality := t.settings.Quality + if t.settings.Width > 0 { + quality = mt.GetQualityForDimension(t.settings.Width, t.settings.Height) + } + + spatial = buffer.VideoQualityToSpatialLayer(quality, mt.ToProto()) + if t.settings.Fps > 0 { + temporal = mt.GetTemporalLayerForSpatialFps(spatial, t.settings.Fps, dt.Codec().MimeType) + } + } + + t.settingsLock.Lock() + if settingsVersion.Compare(t.settingsVersion) != 0 { + // a newer settings has superceded this one + t.settingsLock.Unlock() return } - t.logger.Debugw("updating video layer", "settings", settings) - spatial := t.spatialLayerFromSettings(settings) - t.DownTrack().SetMaxSpatialLayer(spatial) - if settings.Fps > 0 { - t.DownTrack().SetMaxTemporalLayer(t.MediaTrack().GetTemporalLayerForSpatialFps(spatial, settings.Fps, t.DownTrack().Codec().MimeType)) + if t.settings.Disabled { + dt.Mute(true) + t.settingsLock.Unlock() + return + } else { + dt.Mute(false) } + + if dt.Kind() == webrtc.RTPCodecTypeVideo { + dt.SetMaxSpatialLayer(spatial) + if temporal != buffer.InvalidLayerTemporal { + dt.SetMaxTemporalLayer(temporal) + } + } + t.settingsLock.Unlock() } func (t *SubscribedTrack) NeedsNegotiation() bool { @@ -243,17 +302,3 @@ func (t *SubscribedTrack) RTPSender() *webrtc.RTPSender { func (t *SubscribedTrack) SetRTPSender(sender *webrtc.RTPSender) { t.sender.Store(sender) } - -func (t *SubscribedTrack) updateDownTrackMute() { - t.DownTrack().Mute(t.subMuted.Load()) - t.DownTrack().PubMute(t.pubMuted.Load()) -} - -func (t *SubscribedTrack) spatialLayerFromSettings(settings *livekit.UpdateTrackSettings) int32 { - quality := settings.Quality - if settings.Width > 0 { - quality = t.MediaTrack().GetQualityForDimension(settings.Width, settings.Height) - } - - return buffer.VideoQualityToSpatialLayer(quality, t.params.MediaTrack.ToProto()) -} From b659fef8ed5bdf8373dcbe06344c5af3497bb768 Mon Sep 17 00:00:00 2001 From: Benjamin Pracht Date: Thu, 8 Feb 2024 13:59:26 -0800 Subject: [PATCH 18/44] Add support for ingress ParticipantMetadata (#2461) --- go.mod | 2 +- go.sum | 4 ++-- pkg/config/config.go | 16 ++++++++++------ pkg/service/ingress.go | 4 ++++ 4 files changed, 17 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index 98306c775..d10a87f64 100644 --- a/go.mod +++ b/go.mod @@ -19,7 +19,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-20240206082112-9bf41dcbce76 - github.com/livekit/protocol v1.9.9-0.20240206170043-39eb9f38a0a6 + github.com/livekit/protocol v1.9.9-0.20240207222207-9c3dabb016f6 github.com/livekit/psrpc v0.5.3-0.20240129223932-473b29cda289 github.com/mackerelio/go-osstat v0.2.4 github.com/magefile/mage v1.15.0 diff --git a/go.sum b/go.sum index cf28d015c..b56bc4b87 100644 --- a/go.sum +++ b/go.sum @@ -128,8 +128,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-20240206082112-9bf41dcbce76 h1:Zw88krOHni51OzDUlrduYb3m7VcsaKw06TnnDhsQpjg= github.com/livekit/mediatransportutil v0.0.0-20240206082112-9bf41dcbce76/go.mod h1:GBzn9xL+mivI1pW+tyExcKgbc0VOc29I9yJsNcAVaAc= -github.com/livekit/protocol v1.9.9-0.20240206170043-39eb9f38a0a6 h1:85NwqH78bbR1r9XG7jOPfUj/VlcvoMF7tXQqh3fkgUk= -github.com/livekit/protocol v1.9.9-0.20240206170043-39eb9f38a0a6/go.mod h1:lSJlMeTJfQBEv8/D2p3zdCo+i+jTmTtn24ysL4ePK28= +github.com/livekit/protocol v1.9.9-0.20240207222207-9c3dabb016f6 h1:hYsKwQCb3Q89flJAz8r74bcGOWH+3EGaCXBMFNtlQpU= +github.com/livekit/protocol v1.9.9-0.20240207222207-9c3dabb016f6/go.mod h1:lSJlMeTJfQBEv8/D2p3zdCo+i+jTmTtn24ysL4ePK28= github.com/livekit/psrpc v0.5.3-0.20240129223932-473b29cda289 h1:oTgNH7v9TXsBgoltKk5mnWjv4qqcPF2iV+WtEVQ6ROM= github.com/livekit/psrpc v0.5.3-0.20240129223932-473b29cda289/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 7fef6b5fc..f6f0891a3 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -33,8 +33,10 @@ import ( "github.com/livekit/protocol/rpc" ) -type CongestionControlProbeMode string -type StreamTrackerType string +type ( + CongestionControlProbeMode string + StreamTrackerType string +) const ( generatedCLIFlagUsage = "generated" @@ -294,8 +296,7 @@ type IngressConfig struct { WHIPBaseURL string `yaml:"whip_base_url,omitempty"` } -type SIPConfig struct { -} +type SIPConfig struct{} type APIConfig struct { // amount of time to wait for API to execute, default 2s @@ -639,10 +640,10 @@ func (conf *Config) ToCLIFlagNames(existingFlags []cli.Flag) map[string]reflect. func (conf *Config) ValidateKeys() error { // prefer keyfile if set if conf.KeyFile != "" { - var otherFilter os.FileMode = 0007 + var otherFilter os.FileMode = 0o007 if st, err := os.Stat(conf.KeyFile); err != nil { return err - } else if st.Mode().Perm()&otherFilter != 0000 { + } else if st.Mode().Perm()&otherFilter != 0o000 { return ErrKeyFileIncorrectPermission } f, err := os.Open(conf.KeyFile) @@ -747,6 +748,9 @@ func GenerateCLIFlags(existingFlags []cli.Flag, hidden bool) ([]cli.Flag, error) case reflect.Map: // TODO continue + case reflect.Struct: + // TODO + continue default: return flags, fmt.Errorf("cli flag generation unsupported for config type: %s is a %s", name, kind.String()) } diff --git a/pkg/service/ingress.go b/pkg/service/ingress.go index 2acda0064..75d5ffc91 100644 --- a/pkg/service/ingress.go +++ b/pkg/service/ingress.go @@ -156,6 +156,7 @@ func (s *IngressService) CreateIngressWithUrl(ctx context.Context, urlStr string RoomName: req.RoomName, ParticipantIdentity: req.ParticipantIdentity, ParticipantName: req.ParticipantName, + ParticipantMetadata: req.ParticipantMetadata, State: &livekit.IngressState{}, } @@ -224,6 +225,9 @@ func updateInfoUsingRequest(req *livekit.UpdateIngressRequest, info *livekit.Ing if req.BypassTranscoding != nil { info.BypassTranscoding = *req.BypassTranscoding } + if req.ParticipantMetadata != "" { + info.ParticipantMetadata = req.ParticipantMetadata + } if req.Audio != nil { info.Audio = req.Audio } From 213b46dca9013639f7ed212a2f5bd889ad765a1c Mon Sep 17 00:00:00 2001 From: Paul Wells Date: Thu, 8 Feb 2024 16:54:14 -0800 Subject: [PATCH 19/44] skip confirming room persistence (#2466) --- pkg/service/roomservice.go | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/pkg/service/roomservice.go b/pkg/service/roomservice.go index 9ea7ebf0e..3da38851c 100644 --- a/pkg/service/roomservice.go +++ b/pkg/service/roomservice.go @@ -100,19 +100,6 @@ func (s *RoomService) CreateRoom(ctx context.Context, req *livekit.CreateRoomReq defer res.RequestSink.Close() defer res.ResponseSource.Close() - // ensure it's created correctly - err = s.confirmExecution(ctx, func() error { - _, _, err := s.roomStore.LoadRoom(ctx, livekit.RoomName(req.Name), false) - if err != nil { - return ErrOperationFailed - } else { - return nil - } - }) - if err != nil { - return nil, err - } - if created { go func() { s.agentClient.JobRequest(ctx, &livekit.Job{ From 7508560fde549a2900514b084191ba197dd35bee Mon Sep 17 00:00:00 2001 From: Mathew Kamkar <578302+matkam@users.noreply.github.com> Date: Fri, 9 Feb 2024 12:09:51 -0800 Subject: [PATCH 20/44] larger buckets for jitter prometheus histogram (#2468) --- pkg/telemetry/prometheus/packets.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pkg/telemetry/prometheus/packets.go b/pkg/telemetry/prometheus/packets.go index ea8284295..6b643e903 100644 --- a/pkg/telemetry/prometheus/packets.go +++ b/pkg/telemetry/prometheus/packets.go @@ -116,7 +116,9 @@ func initPacketStats(nodeID string, nodeType livekit.NodeType, env string) { Subsystem: "jitter", Name: "us", ConstLabels: prometheus.Labels{"node_id": nodeID, "node_type": nodeType.String(), "env": env}, - Buckets: []float64{100, 500, 1500, 3000, 6000, 12000, 24000, 48000, 96000, 192000}, + + // 1ms, 10ms, 30ms, 50ms, 70ms, 100ms, 300ms, 600ms, 1s + Buckets: []float64{1000, 10000, 30000, 50000, 70000, 100000, 300000, 600000, 1000000}, }, promStreamLabels) promRTT = prometheus.NewHistogramVec(prometheus.HistogramOpts{ Namespace: livekitNamespace, From 0f830ffd1b3b5e6fa8a2c1fa36a2c803cfe454cd Mon Sep 17 00:00:00 2001 From: Russ d'Sa Date: Sat, 10 Feb 2024 08:15:28 -0800 Subject: [PATCH 21/44] Update README.md (#2441) Based on user feedback, clarifying that LiveKit CLI is a separate repo and we recommend someone installs. Making it more clear that the commands below are to install LiveKit server. --- README.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 6040880cf..9e9ff0648 100644 --- a/README.md +++ b/README.md @@ -191,8 +191,11 @@ enabling you to build automations that behave like end-users. ## Install -We recommend installing [livekit-cli](https://github.com/livekit/livekit-cli) along with the server. It lets you access -server APIs, create tokens, and generate test traffic. +> [!TIP] +> We recommend installing [LiveKit CLI](https://github.com/livekit/livekit-cli) along with the server. It lets you access +> server APIs, create tokens, and generate test traffic. + +The following will install LiveKit's media server: ### MacOS From 927d8fc0efc0fed7e71cc7c278ab089978a358bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20Monnom?= Date: Sun, 11 Feb 2024 03:30:51 +0100 Subject: [PATCH 22/44] UserPacket sid should be empty for hidden participants (#2469) --- pkg/rtc/participant.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/pkg/rtc/participant.go b/pkg/rtc/participant.go index 0a64d74c1..7fb7b3c8c 100644 --- a/pkg/rtc/participant.go +++ b/pkg/rtc/participant.go @@ -1445,8 +1445,13 @@ func (p *ParticipantImpl) onDataMessage(kind livekit.DataPacket_Kind, data []byt onDataPacket := p.onDataPacket p.lock.RUnlock() if onDataPacket != nil { - payload.User.ParticipantSid = string(p.params.SID) - payload.User.ParticipantIdentity = string(p.params.Identity) + if p.Hidden() { + payload.User.ParticipantSid = "" + payload.User.ParticipantIdentity = "" + } else { + payload.User.ParticipantSid = string(p.params.SID) + payload.User.ParticipantIdentity = string(p.params.Identity) + } onDataPacket(p, &dp) } default: From 89a312d259fac53b91e13f0fe0de637874e9cd97 Mon Sep 17 00:00:00 2001 From: Raja Subramanian Date: Mon, 12 Feb 2024 11:49:14 +0530 Subject: [PATCH 23/44] Ignore duplicate RID. (#2471) Firefox on Windows 10 seems to be producing simulcast tracks with duplicate RID. That causes a leak as only one buffer is processed. Ignore duplicate rid. NOTE: This is not perfect as the actual layer -> rid is indeterminable at addition time. It would require looking at packets to determine the video dimensions and match to rid/layer to figure out which one is correct and which one is duplicate. To simplify though, taking the first one and dropping later ones. This could mean the correct resolution is not streamed, but that should be okay. The leak is far more destructive. --- pkg/rtc/mediatrack.go | 12 +++++++++- pkg/rtc/participant.go | 10 +++++++++ pkg/sfu/buffer/buffer.go | 4 ++-- pkg/sfu/receiver.go | 48 +++++++++++++++++++++------------------- 4 files changed, 48 insertions(+), 26 deletions(-) diff --git a/pkg/rtc/mediatrack.go b/pkg/rtc/mediatrack.go index 2dfbfb9bd..16f6c24f3 100644 --- a/pkg/rtc/mediatrack.go +++ b/pkg/rtc/mediatrack.go @@ -309,7 +309,17 @@ func (t *MediaTrack) AddReceiver(receiver *webrtc.RTPReceiver, track *webrtc.Tra } t.lock.Unlock() - wr.(*sfu.WebRTCReceiver).AddUpTrack(track, buff) + if err := wr.(*sfu.WebRTCReceiver).AddUpTrack(track, buff); err != nil { + t.params.Logger.Warnw( + "adding up track failed", err, + "rid", track.RID(), + "layer", layer, + "ssrc", track.SSRC(), + "newCodec", newCodec, + ) + buff.Close() + return false + } // LK-TODO: can remove this completely when VideoLayers protocol becomes the default as it has info from client or if we decide to use TrackInfo.Simulcast if t.numUpTracks.Inc() > 1 || track.RID() != "" { diff --git a/pkg/rtc/participant.go b/pkg/rtc/participant.go index 7fb7b3c8c..3a29f321a 100644 --- a/pkg/rtc/participant.go +++ b/pkg/rtc/participant.go @@ -1137,6 +1137,8 @@ func (p *ParticipantImpl) UpdateMediaRTT(rtt uint32) { } } +// ---------------------------------------------------------- + type AnyTransportHandler struct { transport.UnimplementedHandler p *ParticipantImpl @@ -1154,6 +1156,8 @@ func (h AnyTransportHandler) OnICECandidate(c *webrtc.ICECandidate, target livek return h.p.onICECandidate(c, target) } +// ---------------------------------------------------------- + type PublisherTransportHandler struct { AnyTransportHandler } @@ -1174,6 +1178,8 @@ func (h PublisherTransportHandler) OnDataPacket(kind livekit.DataPacket_Kind, da h.p.onDataMessage(kind, data) } +// ---------------------------------------------------------- + type SubscriberTransportHandler struct { AnyTransportHandler } @@ -1190,6 +1196,8 @@ func (h SubscriberTransportHandler) OnInitialConnected() { h.p.onSubscriberInitialConnected() } +// ---------------------------------------------------------- + type PrimaryTransportHandler struct { transport.Handler p *ParticipantImpl @@ -1204,6 +1212,8 @@ func (h PrimaryTransportHandler) OnFullyEstablished() { h.p.onPrimaryTransportFullyEstablished() } +// ---------------------------------------------------------- + func (p *ParticipantImpl) setupTransportManager() error { p.twcc = twcc.NewTransportWideCCResponder() p.twcc.OnFeedback(func(pkts []rtcp.Packet) { diff --git a/pkg/sfu/buffer/buffer.go b/pkg/sfu/buffer/buffer.go index d657b6f8a..1e607eaf0 100644 --- a/pkg/sfu/buffer/buffer.go +++ b/pkg/sfu/buffer/buffer.go @@ -147,7 +147,7 @@ func (b *Buffer) SetLogger(logger logger.Logger) { b.Lock() defer b.Unlock() - b.logger = logger.WithComponent(sutils.ComponentSFU) + b.logger = logger.WithComponent(sutils.ComponentSFU).WithValues("ssrc", b.mediaSSRC) if b.rtpStats != nil { b.rtpStats.SetLogger(b.logger) } @@ -584,7 +584,7 @@ func (b *Buffer) patchExtPacket(ep *ExtPacket, buf []byte) *ExtPacket { n, err := b.getPacket(buf, ep.Packet.SequenceNumber) if err != nil { packetNotFoundCount := b.packetNotFoundCount.Inc() - if packetNotFoundCount%20 == 0 { + if (packetNotFoundCount-1)%20 == 0 { b.logger.Warnw("could not get packet from bucket", err, "sn", ep.Packet.SequenceNumber, "headSN", b.bucket.HeadSequenceNumber(), "count", packetNotFoundCount) } return nil diff --git a/pkg/sfu/receiver.go b/pkg/sfu/receiver.go index bdb8853ea..0eba7f48d 100644 --- a/pkg/sfu/receiver.go +++ b/pkg/sfu/receiver.go @@ -42,6 +42,7 @@ var ( ErrReceiverClosed = errors.New("receiver closed") ErrDownTrackAlreadyExist = errors.New("DownTrack already exist") ErrBufferNotFound = errors.New("buffer not found") + ErrDuplicateLayer = errors.New("duplicate layer") ) type AudioLevelHandle func(level uint8, duration uint32) @@ -114,11 +115,9 @@ type WebRTCReceiver struct { bufferMu sync.RWMutex buffers [buffer.DefaultMaxLayerSpatial + 1]*buffer.Buffer + upTracks [buffer.DefaultMaxLayerSpatial + 1]*webrtc.TrackRemote rtt uint32 - upTrackMu sync.RWMutex - upTracks [buffer.DefaultMaxLayerSpatial + 1]*webrtc.TrackRemote - lbThreshold int streamTrackerManager *StreamTrackerManager @@ -265,9 +264,16 @@ func (w *WebRTCReceiver) OnStatsUpdate(fn func(w *WebRTCReceiver, stat *livekit. } func (w *WebRTCReceiver) OnMaxLayerChange(fn func(maxLayer int32)) { - w.upTrackMu.Lock() + w.bufferMu.Lock() w.onMaxLayerChange = fn - w.upTrackMu.Unlock() + w.bufferMu.Unlock() +} + +func (w *WebRTCReceiver) getOnMaxLayerChange() func(maxLayer int32) { + w.bufferMu.RLock() + defer w.bufferMu.RUnlock() + + return w.onMaxLayerChange } func (w *WebRTCReceiver) GetConnectionScoreAndQuality() (float32, livekit.ConnectionQuality) { @@ -306,10 +312,7 @@ func (w *WebRTCReceiver) TrackID() livekit.TrackID { return w.trackID } -func (w *WebRTCReceiver) SSRC(layer int) uint32 { - w.upTrackMu.RLock() - defer w.upTrackMu.RUnlock() - +func (w *WebRTCReceiver) ssrc(layer int) uint32 { if track := w.upTracks[layer]; track != nil { return uint32(track.SSRC()) } @@ -328,9 +331,9 @@ func (w *WebRTCReceiver) Kind() webrtc.RTPCodecType { return w.kind } -func (w *WebRTCReceiver) AddUpTrack(track *webrtc.TrackRemote, buff *buffer.Buffer) { +func (w *WebRTCReceiver) AddUpTrack(track *webrtc.TrackRemote, buff *buffer.Buffer) error { if w.closed.Load() { - return + return ErrReceiverClosed } layer := int32(0) @@ -369,14 +372,16 @@ func (w *WebRTCReceiver) AddUpTrack(track *webrtc.TrackRemote, buff *buffer.Buff buff.SetPLIThrottle(duration.Nanoseconds()) } - w.upTrackMu.Lock() - w.upTracks[layer] = track - w.upTrackMu.Unlock() - w.bufferMu.Lock() + if w.upTracks[layer] != nil { + w.bufferMu.Unlock() + return ErrDuplicateLayer + } + w.upTracks[layer] = track w.buffers[layer] = buff rtt := w.rtt w.bufferMu.Unlock() + buff.SetRTT(rtt) buff.SetPaused(w.streamTrackerManager.IsPaused()) @@ -385,6 +390,7 @@ func (w *WebRTCReceiver) AddUpTrack(track *webrtc.TrackRemote, buff *buffer.Buff } go w.forwardRTP(layer) + return nil } // SetUpTrackPaused indicates upstream will not be sending any data. @@ -471,11 +477,7 @@ func (w *WebRTCReceiver) OnMaxTemporalLayerSeenChanged(maxTemporalLayerSeen int3 // StreamTrackerManagerListener.OnMaxAvailableLayerChanged func (w *WebRTCReceiver) OnMaxAvailableLayerChanged(maxAvailableLayer int32) { - w.upTrackMu.RLock() - onMaxLayerChange := w.onMaxLayerChange - w.upTrackMu.RUnlock() - - if onMaxLayerChange != nil { + if onMaxLayerChange := w.getOnMaxLayerChange(); onMaxLayerChange != nil { onMaxLayerChange(maxAvailableLayer) } } @@ -619,7 +621,7 @@ func (w *WebRTCReceiver) GetDeltaStats() map[uint32]*buffer.StreamStatsWithLayer patched[int32(layer)] = sswl.Layers[0] sswl.Layers = patched - deltaStats[w.SSRC(layer)] = sswl + deltaStats[w.ssrc(layer)] = sswl } return deltaStats @@ -711,7 +713,7 @@ func (w *WebRTCReceiver) DebugInfo() map[string]interface{} { "Simulcast": isSimulcast, } - w.upTrackMu.RLock() + w.bufferMu.RLock() upTrackInfo := make([]map[string]interface{}, 0, len(w.upTracks)) for layer, ut := range w.upTracks { if ut != nil { @@ -723,7 +725,7 @@ func (w *WebRTCReceiver) DebugInfo() map[string]interface{} { }) } } - w.upTrackMu.RUnlock() + w.bufferMu.RUnlock() info["UpTracks"] = upTrackInfo return info From 49fd332e91208555d2d1cfe47725353975cf7775 Mon Sep 17 00:00:00 2001 From: Raja Subramanian Date: Mon, 12 Feb 2024 12:14:25 +0530 Subject: [PATCH 24/44] Store first SR also as it can get reset (#2472) --- pkg/sfu/buffer/rtpstats_sender.go | 12 ++++++------ pkg/sfu/downtrack.go | 24 ++++++++++++++++++++---- pkg/sfu/receiver.go | 2 +- 3 files changed, 27 insertions(+), 11 deletions(-) diff --git a/pkg/sfu/buffer/rtpstats_sender.go b/pkg/sfu/buffer/rtpstats_sender.go index 98b25744a..ca4415140 100644 --- a/pkg/sfu/buffer/rtpstats_sender.go +++ b/pkg/sfu/buffer/rtpstats_sender.go @@ -583,15 +583,15 @@ func (r *RTPStatsSender) LastReceiverReportTime() time.Time { return r.lastRRTime } -func (r *RTPStatsSender) MaybeAdjustFirstPacketTime(srData *RTCPSenderReportData, ts uint32) { +func (r *RTPStatsSender) MaybeAdjustFirstPacketTime(srFirst *RTCPSenderReportData, srNewest *RTCPSenderReportData, ts uint32) { r.lock.Lock() defer r.lock.Unlock() - srDataCopy := *srData - if r.srFeedFirst == nil { - r.srFeedFirst = &srDataCopy - } - r.srFeedNewest = &srDataCopy + srFirstCopy := *srFirst + r.srFeedFirst = &srFirstCopy + + srNewestCopy := *srNewest + r.srFeedNewest = &srNewestCopy r.maybeAdjustFirstPacketTime(ts, uint32(r.extStartTS)) } diff --git a/pkg/sfu/downtrack.go b/pkg/sfu/downtrack.go index 54a948f7a..c4deac566 100644 --- a/pkg/sfu/downtrack.go +++ b/pkg/sfu/downtrack.go @@ -55,7 +55,13 @@ type TrackSender interface { ID() string SubscriberID() livekit.ParticipantID TrackInfoAvailable() - HandleRTCPSenderReportData(payloadType webrtc.PayloadType, isSVC bool, layer int32, srData *buffer.RTCPSenderReportData) error + HandleRTCPSenderReportData( + payloadType webrtc.PayloadType, + isSVC bool, + layer int32, + srFirst *buffer.RTCPSenderReportData, + srNewest *buffer.RTCPSenderReportData, + ) error } // ------------------------------------------------------------------- @@ -1913,9 +1919,19 @@ func (d *DownTrack) sendSilentFrameOnMuteForOpus() { } } -func (d *DownTrack) HandleRTCPSenderReportData(_payloadType webrtc.PayloadType, isSVC bool, layer int32, srData *buffer.RTCPSenderReportData) error { - if (layer == d.forwarder.GetReferenceLayerSpatial() || (layer == 0 && isSVC)) && srData != nil { - d.rtpStats.MaybeAdjustFirstPacketTime(srData, srData.RTPTimestamp+uint32(d.forwarder.GetReferenceTimestampOffset())) +func (d *DownTrack) HandleRTCPSenderReportData( + _payloadType webrtc.PayloadType, + isSVC bool, + layer int32, + srFirst *buffer.RTCPSenderReportData, + srNewest *buffer.RTCPSenderReportData, +) error { + if (layer == d.forwarder.GetReferenceLayerSpatial() || (layer == 0 && isSVC)) && srNewest != nil { + d.rtpStats.MaybeAdjustFirstPacketTime( + srFirst, + srNewest, + srNewest.RTPTimestamp+uint32(d.forwarder.GetReferenceTimestampOffset()), + ) } return nil } diff --git a/pkg/sfu/receiver.go b/pkg/sfu/receiver.go index 0eba7f48d..e33f06eb4 100644 --- a/pkg/sfu/receiver.go +++ b/pkg/sfu/receiver.go @@ -353,7 +353,7 @@ func (w *WebRTCReceiver) AddUpTrack(track *webrtc.TrackRemote, buff *buffer.Buff w.streamTrackerManager.SetRTCPSenderReportData(layer, srFirst, srNewest) w.downTrackSpreader.Broadcast(func(dt TrackSender) { - _ = dt.HandleRTCPSenderReportData(w.codec.PayloadType, w.isSVC, layer, srNewest) + _ = dt.HandleRTCPSenderReportData(w.codec.PayloadType, w.isSVC, layer, srFirst, srNewest) }) }) From 3674217d64bfd81fb82350279a0e3e41060d11bd Mon Sep 17 00:00:00 2001 From: Denys Smirnov Date: Mon, 12 Feb 2024 18:52:21 +0200 Subject: [PATCH 25/44] Support new SIP protocol. (#2474) --- go.mod | 10 +++++----- go.sum | 26 ++++++++++---------------- pkg/service/ioservice_sip.go | 4 ++++ 3 files changed, 19 insertions(+), 21 deletions(-) diff --git a/go.mod b/go.mod index d10a87f64..2570179d4 100644 --- a/go.mod +++ b/go.mod @@ -19,7 +19,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-20240206082112-9bf41dcbce76 - github.com/livekit/protocol v1.9.9-0.20240207222207-9c3dabb016f6 + github.com/livekit/protocol v1.9.9 github.com/livekit/psrpc v0.5.3-0.20240129223932-473b29cda289 github.com/mackerelio/go-osstat v0.2.4 github.com/magefile/mage v1.15.0 @@ -27,7 +27,7 @@ require ( github.com/mitchellh/go-homedir v1.1.0 github.com/olekukonko/tablewriter v0.0.5 github.com/pion/dtls/v2 v2.2.10 - github.com/pion/ice/v2 v2.3.12 + github.com/pion/ice/v2 v2.3.13 github.com/pion/interceptor v0.1.25 github.com/pion/rtcp v1.2.13 github.com/pion/rtp v1.8.3 @@ -35,7 +35,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.5 - github.com/pion/webrtc/v3 v3.2.25 + github.com/pion/webrtc/v3 v3.2.26 github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.18.0 github.com/redis/go-redis/v9 v9.4.0 @@ -83,7 +83,7 @@ require ( github.com/nats-io/nuid v1.0.1 // indirect github.com/pion/datachannel v1.5.5 // indirect github.com/pion/logging v0.2.2 // indirect - github.com/pion/mdns v0.0.10 // indirect + github.com/pion/mdns v0.0.12 // indirect github.com/pion/randutil v0.1.0 // indirect github.com/pion/srtp/v2 v2.0.18 // indirect github.com/pion/stun v0.6.1 // indirect @@ -97,7 +97,7 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.26.0 // indirect golang.org/x/crypto v0.18.0 // indirect - golang.org/x/mod v0.14.0 // indirect + golang.org/x/mod v0.15.0 // indirect golang.org/x/net v0.20.0 // indirect golang.org/x/sys v0.16.0 // indirect golang.org/x/text v0.14.0 // indirect diff --git a/go.sum b/go.sum index b56bc4b87..97c201835 100644 --- a/go.sum +++ b/go.sum @@ -128,8 +128,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-20240206082112-9bf41dcbce76 h1:Zw88krOHni51OzDUlrduYb3m7VcsaKw06TnnDhsQpjg= github.com/livekit/mediatransportutil v0.0.0-20240206082112-9bf41dcbce76/go.mod h1:GBzn9xL+mivI1pW+tyExcKgbc0VOc29I9yJsNcAVaAc= -github.com/livekit/protocol v1.9.9-0.20240207222207-9c3dabb016f6 h1:hYsKwQCb3Q89flJAz8r74bcGOWH+3EGaCXBMFNtlQpU= -github.com/livekit/protocol v1.9.9-0.20240207222207-9c3dabb016f6/go.mod h1:lSJlMeTJfQBEv8/D2p3zdCo+i+jTmTtn24ysL4ePK28= +github.com/livekit/protocol v1.9.9 h1:VNfeJeoVhHrU6fiHqKJyIISFKSLMWYldCDVuHYZwgyk= +github.com/livekit/protocol v1.9.9/go.mod h1:j5ctajx0GDRA1GugIsDBGyMGMXhMP6rCNl+43mR+5h8= github.com/livekit/psrpc v0.5.3-0.20240129223932-473b29cda289 h1:oTgNH7v9TXsBgoltKk5mnWjv4qqcPF2iV+WtEVQ6ROM= github.com/livekit/psrpc v0.5.3-0.20240129223932-473b29cda289/go.mod h1:cQjxg1oCxYHhxxv6KJH1gSvdtCHQoRZCHgPdm5N8v2g= github.com/mackerelio/go-osstat v0.2.4 h1:qxGbdPkFo65PXOb/F/nhDKpF2nGmGaCFDLXoZjJTtUs= @@ -186,16 +186,14 @@ github.com/pion/datachannel v1.5.5/go.mod h1:iMz+lECmfdCMqFRhXhcA/219B0SQlbpoR2V 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.11/go.mod h1:hPcLC3kxMa+JGRzMHqQzjoSj3xtE9F+eoncmXLlCL4E= -github.com/pion/ice/v2 v2.3.12 h1:NWKW2b3+oSZS3klbQMIEWQ0i52Kuo0KBg505a5kQv4s= -github.com/pion/ice/v2 v2.3.12/go.mod h1:hPcLC3kxMa+JGRzMHqQzjoSj3xtE9F+eoncmXLlCL4E= +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/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.8/go.mod h1:hYE72WX8WDveIhg7fmXgMKivD3Puklk0Ymzog0lSyaI= -github.com/pion/mdns v0.0.10 h1:u9/12WL2NNgtGT2nNPXT6+A+xeOF0PkawM/S/wPMWQA= -github.com/pion/mdns v0.0.10/go.mod h1:Y1scL/8TT8KQ172UfxrE4j0c04NOY71bJS1aE1zvyGY= +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= @@ -227,8 +225,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.5 h1:tTyy7TM3DCoX9IxTt/yHc/bThiRLyXK3T1YbNcgx9k4= github.com/pion/turn/v2 v2.1.5/go.mod h1:huEpByKKHix2/b9kmTAM3YoX6MKP+/D//0ClgUYR2fY= -github.com/pion/webrtc/v3 v3.2.25 h1:hmewKZtY60TOMOnAfEfHI+D78VpDe3OdnwcRpE8kLEc= -github.com/pion/webrtc/v3 v3.2.25/go.mod h1:1CaT2fcZzZ6VZA+O1i9yK2DU4EOcXVvSbWG9pr5jefs= +github.com/pion/webrtc/v3 v3.2.26 h1:Lj1ygouOokm9nJ/O7xl4y+IOOu5IjU4NNaoafSz6+mA= +github.com/pion/webrtc/v3 v3.2.26/go.mod h1:wTDNUio622Yfyoj7lzRuIPclJKZn0dU0i2RUPfG6Jfk= 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= @@ -294,7 +292,6 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U 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.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.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= @@ -304,8 +301,8 @@ golang.org/x/exp v0.0.0-20240205201215-2c58cdc269a3/go.mod h1:idGWGoKP1toJGkd5/i 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.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= -golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8= +golang.org/x/mod v0.15.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= @@ -330,7 +327,6 @@ 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= golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= 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.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= @@ -391,7 +387,6 @@ golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= -golang.org/x/term v0.9.0/go.mod h1:M6DEAAIenWoTxdKrOltXcmDY3rSplQUkrvaDU5FcQyo= golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o= golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= @@ -402,7 +397,6 @@ 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= 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.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= diff --git a/pkg/service/ioservice_sip.go b/pkg/service/ioservice_sip.go index 540b906b5..133044af9 100644 --- a/pkg/service/ioservice_sip.go +++ b/pkg/service/ioservice_sip.go @@ -16,6 +16,7 @@ package service import ( "context" + "errors" "github.com/livekit/protocol/livekit" "github.com/livekit/protocol/logger" @@ -57,6 +58,9 @@ func (s *IOInfoService) EvaluateSIPDispatchRules(ctx context.Context, req *rpc.E } best, err := s.matchSIPDispatchRule(ctx, trunk, req) if err != nil { + if e := (*sip.ErrNoDispatchMatched)(nil); errors.As(err, &e) { + return &rpc.EvaluateSIPDispatchRulesResponse{Result: rpc.SIPDispatchResult_DROP}, nil + } return nil, err } logger.Debugw("SIP dispatch rule matched", "dispatchRule", best.SipDispatchRuleId, "called", req.CalledNumber, "calling", req.CallingNumber) From 33755e7dfd49de914825a0797c83e2e78e523135 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 12 Feb 2024 09:13:08 -0800 Subject: [PATCH 26/44] Update module github.com/google/wire to v0.6.0 (#2449) Generated by renovateBot Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 15 +++++++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/go.mod b/go.mod index 2570179d4..1397c9171 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/frostbyte73/core v0.0.9 github.com/gammazero/deque v0.2.1 github.com/gammazero/workerpool v1.1.3 - github.com/google/wire v0.5.0 + github.com/google/wire v0.6.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 diff --git a/go.sum b/go.sum index 97c201835..969325061 100644 --- a/go.sum +++ b/go.sum @@ -70,15 +70,14 @@ github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/subcommands v1.0.1/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= github.com/google/subcommands v1.2.0 h1:vWQspBTo2nEqTUFita5/KeEWlUL8kQObDFbub/EN9oE= github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 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/google/wire v0.5.0 h1:I7ELFeVBr3yfPIcc8+MWvrjk+3VjbcSzoXm3JVa+jD8= -github.com/google/wire v0.5.0/go.mod h1:ngWDr9Qvq3yZA10YrxfyGELY/AFWGVpy9c1LTRi1EoU= +github.com/google/wire v0.6.0 h1:HBkoIh4BdSxoyo9PveV8giw7ZsaBOvzWKfcg/6MrVwI= +github.com/google/wire v0.6.0/go.mod h1:F4QhpQ9EDIdJ1Mbop/NZBRB+5yrR6qg3BnctaoUk6NA= 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= @@ -294,6 +293,7 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y 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.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= golang.org/x/exp v0.0.0-20240205201215-2c58cdc269a3 h1:/RIbNt/Zr7rVhIkQhooTxCxFcdWLGIKnZA4IXNFSrvo= @@ -301,6 +301,8 @@ golang.org/x/exp v0.0.0-20240205201215-2c58cdc269a3/go.mod h1:idGWGoKP1toJGkd5/i 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.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8= golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -329,6 +331,7 @@ golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= 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.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -337,6 +340,7 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/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.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= 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= @@ -379,6 +383,7 @@ 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.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -389,6 +394,7 @@ golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o= golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= +golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= 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= @@ -399,14 +405,15 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= 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/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.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc= golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= From dbda3936103b91ea0e9aa3bcb768768c30190a60 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 12 Feb 2024 10:06:31 -0800 Subject: [PATCH 27/44] Update pion deps (#2465) Generated by renovateBot Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- go.mod | 4 ++-- go.sum | 9 ++++----- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/go.mod b/go.mod index 1397c9171..503d41461 100644 --- a/go.mod +++ b/go.mod @@ -31,11 +31,11 @@ require ( github.com/pion/interceptor v0.1.25 github.com/pion/rtcp v1.2.13 github.com/pion/rtp v1.8.3 - github.com/pion/sctp v1.8.10 + github.com/pion/sctp v1.8.12 github.com/pion/sdp/v3 v3.0.6 github.com/pion/transport/v2 v2.2.4 github.com/pion/turn/v2 v2.1.5 - github.com/pion/webrtc/v3 v3.2.26 + github.com/pion/webrtc/v3 v3.2.28 github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.18.0 github.com/redis/go-redis/v9 v9.4.0 diff --git a/go.sum b/go.sum index 969325061..5b4560660 100644 --- a/go.sum +++ b/go.sum @@ -203,9 +203,8 @@ 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.10 h1:FDPlkojWQ2hIjnvgFs+frHR33TZCxoRhV2HztZ07NnU= -github.com/pion/sctp v1.8.10/go.mod h1:cMLT45jqw3+jiJCrtHVwfQLnfR0MGZ4rgOJwUOIqLkI= +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/srtp/v2 v2.0.18 h1:vKpAXfawO9RtTRKZJbG4y0v1b11NZxQnxRl85kGuUlo= @@ -224,8 +223,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.5 h1:tTyy7TM3DCoX9IxTt/yHc/bThiRLyXK3T1YbNcgx9k4= github.com/pion/turn/v2 v2.1.5/go.mod h1:huEpByKKHix2/b9kmTAM3YoX6MKP+/D//0ClgUYR2fY= -github.com/pion/webrtc/v3 v3.2.26 h1:Lj1ygouOokm9nJ/O7xl4y+IOOu5IjU4NNaoafSz6+mA= -github.com/pion/webrtc/v3 v3.2.26/go.mod h1:wTDNUio622Yfyoj7lzRuIPclJKZn0dU0i2RUPfG6Jfk= +github.com/pion/webrtc/v3 v3.2.28 h1:ienStxZ6HcjtH2UlmnFpMM0loENiYjaX437uIUpQSKo= +github.com/pion/webrtc/v3 v3.2.28/go.mod h1:PNRCEuQlibrmuBhOTnol9j6KkIbUG11aHLEfNpUYey0= 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 07f64251b2646e15861d5401dea3bc1240cc72f2 Mon Sep 17 00:00:00 2001 From: Raja Subramanian Date: Tue, 13 Feb 2024 00:06:57 +0530 Subject: [PATCH 28/44] Delete spammy log (#2476) * Move spammy log to Debugw * Actually, delete as log is not useful --- pkg/sfu/streamtrackermanager.go | 9 --------- 1 file changed, 9 deletions(-) diff --git a/pkg/sfu/streamtrackermanager.go b/pkg/sfu/streamtrackermanager.go index 2817ef31f..a934b9e93 100644 --- a/pkg/sfu/streamtrackermanager.go +++ b/pkg/sfu/streamtrackermanager.go @@ -572,15 +572,6 @@ func (s *StreamTrackerManager) updateLayerOffsetLocked(ref, other int32) { // Rationale: higher layers could be paused for extended periods of time // due to adaptive stream/dynacast or publisher constraints like CPU/bandwidth. // The check is to avoid using very old reports. - s.logger.Infow( - "skipping offset update", - "ref", ref, - "refNTP", srRef.NTPTimestamp.Time().String(), - "refRTP", srRef.RTPTimestamp, - "other", other, - "otherNTP", srOther.NTPTimestamp.Time().String(), - "otherRTP", srOther.RTPTimestamp, - ) return } rtpDiff := ntpDiff.Nanoseconds() * int64(s.clockRate) / 1e9 From 0bcd9a2f8b2e3e2f170b0a3ead54e25333fbc92c Mon Sep 17 00:00:00 2001 From: Raja Subramanian Date: Tue, 13 Feb 2024 12:01:20 +0530 Subject: [PATCH 29/44] Remove some noisy logs (#2477) --- pkg/sfu/buffer/buffer.go | 5 ----- pkg/sfu/forwarder.go | 12 ------------ 2 files changed, 17 deletions(-) diff --git a/pkg/sfu/buffer/buffer.go b/pkg/sfu/buffer/buffer.go index 1e607eaf0..00172573e 100644 --- a/pkg/sfu/buffer/buffer.go +++ b/pkg/sfu/buffer/buffer.go @@ -537,11 +537,6 @@ func (b *Buffer) calc(rawPkt []byte, rtpPacket *rtp.Packet, arrivalTime time.Tim b.logger.Errorw("could not exclude range", err, "sn", rtpPacket.SequenceNumber, "esn", flowState.ExtSequenceNumber) } } - // TODO-VP9-DEBUG-REMOVE-START - snAdjustment, err := b.snRangeMap.GetValue(flowState.ExtSequenceNumber) - b.logger.Debugw("dropping padding packet", "sn", rtpPacket.SequenceNumber, "osn", flowState.ExtSequenceNumber, "msn", flowState.ExtSequenceNumber-snAdjustment, "error", err) - // TODO-VP9-DEBUG-REMOVE-END - return } // add to RTX buffer using sequence number after accounting for dropped padding only packets diff --git a/pkg/sfu/forwarder.go b/pkg/sfu/forwarder.go index 44de9ba71..86fe39967 100644 --- a/pkg/sfu/forwarder.go +++ b/pkg/sfu/forwarder.go @@ -1697,18 +1697,6 @@ func (f *Forwarder) getTranslationParamsVideo(extPkt *buffer.ExtPacket, layer in // call to update highest incoming sequence number and other internal structures if tpRTP, err := f.rtpMunger.UpdateAndGetSnTs(extPkt, result.RTPMarker); err == nil { if tpRTP.snOrdering == SequenceNumberOrderingContiguous { - // TODO-VP9-DEBUG-REMOVE-START - f.logger.Debugw( - "dropping packet", - "isn", extPkt.ExtSequenceNumber, - "its", extPkt.ExtTimestamp, - "osn", tpRTP.extSequenceNumber, - "ots", tpRTP.extTimestamp, - "payloadLen", len(extPkt.Packet.Payload), - "sid", extPkt.Spatial, - "tid", extPkt.Temporal, - ) - // TODO-VP9-DEBUG-REMOVE-END f.rtpMunger.PacketDropped(extPkt) } else { // TODO-VP9-DEBUG-REMOVE-START From 57d424542e25d3696ff1ae7682e3859ba4eaf1d9 Mon Sep 17 00:00:00 2001 From: Raja Subramanian Date: Tue, 13 Feb 2024 13:19:58 +0530 Subject: [PATCH 30/44] Update protocol to get protoProxy close await channel (#2478) --- go.mod | 8 ++++---- go.sum | 14 ++++++++------ 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/go.mod b/go.mod index 503d41461..9894dcb7d 100644 --- a/go.mod +++ b/go.mod @@ -19,8 +19,8 @@ require ( github.com/jxskiss/base62 v1.1.0 github.com/livekit/mageutil v0.0.0-20230125210925-54e8a70427c1 github.com/livekit/mediatransportutil v0.0.0-20240206082112-9bf41dcbce76 - github.com/livekit/protocol v1.9.9 - github.com/livekit/psrpc v0.5.3-0.20240129223932-473b29cda289 + github.com/livekit/protocol v1.9.10-0.20240213074347-cee767e2d909 + github.com/livekit/psrpc v0.5.3-0.20240209001357-380f59f00c58 github.com/mackerelio/go-osstat v0.2.4 github.com/magefile/mage v1.15.0 github.com/maxbrunsfeld/counterfeiter/v6 v6.8.1 @@ -96,10 +96,10 @@ 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.18.0 // indirect + golang.org/x/crypto v0.19.0 // indirect golang.org/x/mod v0.15.0 // indirect golang.org/x/net v0.20.0 // indirect - golang.org/x/sys v0.16.0 // indirect + golang.org/x/sys v0.17.0 // indirect golang.org/x/text v0.14.0 // indirect golang.org/x/tools v0.17.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240205150955-31a09d347014 // indirect diff --git a/go.sum b/go.sum index 5b4560660..605b241d6 100644 --- a/go.sum +++ b/go.sum @@ -127,10 +127,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-20240206082112-9bf41dcbce76 h1:Zw88krOHni51OzDUlrduYb3m7VcsaKw06TnnDhsQpjg= github.com/livekit/mediatransportutil v0.0.0-20240206082112-9bf41dcbce76/go.mod h1:GBzn9xL+mivI1pW+tyExcKgbc0VOc29I9yJsNcAVaAc= -github.com/livekit/protocol v1.9.9 h1:VNfeJeoVhHrU6fiHqKJyIISFKSLMWYldCDVuHYZwgyk= -github.com/livekit/protocol v1.9.9/go.mod h1:j5ctajx0GDRA1GugIsDBGyMGMXhMP6rCNl+43mR+5h8= -github.com/livekit/psrpc v0.5.3-0.20240129223932-473b29cda289 h1:oTgNH7v9TXsBgoltKk5mnWjv4qqcPF2iV+WtEVQ6ROM= -github.com/livekit/psrpc v0.5.3-0.20240129223932-473b29cda289/go.mod h1:cQjxg1oCxYHhxxv6KJH1gSvdtCHQoRZCHgPdm5N8v2g= +github.com/livekit/protocol v1.9.10-0.20240213074347-cee767e2d909 h1:36Jehw3Z0CHV3QDbDWUndky02lGtdpl0XTDs7u8UwPk= +github.com/livekit/protocol v1.9.10-0.20240213074347-cee767e2d909/go.mod h1:042ukZY29snaLjTVxVO+X89zuqqr9HDcpkq45c9Y27A= +github.com/livekit/psrpc v0.5.3-0.20240209001357-380f59f00c58 h1:yH55rBGLRO+ict2mu6bKZ5iPwTIrIwU1i0ydgThi4+k= +github.com/livekit/psrpc v0.5.3-0.20240209001357-380f59f00c58/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= @@ -293,8 +293,9 @@ 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.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= -golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= 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-20240205201215-2c58cdc269a3 h1:/RIbNt/Zr7rVhIkQhooTxCxFcdWLGIKnZA4IXNFSrvo= golang.org/x/exp v0.0.0-20240205201215-2c58cdc269a3/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= @@ -383,8 +384,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.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= 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/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= From f7b6e915cbd3c93e33c74c8a49548b2d89b5577b Mon Sep 17 00:00:00 2001 From: Raja Subramanian Date: Tue, 13 Feb 2024 14:24:31 +0530 Subject: [PATCH 31/44] Fix return on dropping a padding packet. (#2479) Had deleted an extra line while cleaning up. --- pkg/sfu/buffer/buffer.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/sfu/buffer/buffer.go b/pkg/sfu/buffer/buffer.go index 00172573e..85f0b2ae9 100644 --- a/pkg/sfu/buffer/buffer.go +++ b/pkg/sfu/buffer/buffer.go @@ -537,6 +537,7 @@ func (b *Buffer) calc(rawPkt []byte, rtpPacket *rtp.Packet, arrivalTime time.Tim b.logger.Errorw("could not exclude range", err, "sn", rtpPacket.SequenceNumber, "esn", flowState.ExtSequenceNumber) } } + return } // add to RTX buffer using sequence number after accounting for dropped padding only packets From e4c112929ced02e1b738193a16da6794a424b77d Mon Sep 17 00:00:00 2001 From: Raja Subramanian Date: Tue, 13 Feb 2024 22:56:28 +0530 Subject: [PATCH 32/44] Declare migration complete on publisher PC connected. (#2481) Unless there are no published tracks, declare connected on primary PC connected. Streamlining this a bit. A bit of history - With original migration, migration complete was declared on all tracks published. - When muted tracks has to be migrated, a publish is synthesised for muted tracks, but migration complete did not wait till publisher peer connection connected. - A few weeks back, those paths were merged and all cases were changed to use synthesised publish. - Previously the completion point was different between muted and unmuted tracks. And with the change to treat everything like a muted track, completion point changed. Change it so that if publisher PC is expected to be active, wait for it to be connected before declaring migration complete. --- pkg/rtc/participant.go | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/pkg/rtc/participant.go b/pkg/rtc/participant.go index 3a29f321a..5cf2dc299 100644 --- a/pkg/rtc/participant.go +++ b/pkg/rtc/participant.go @@ -902,17 +902,12 @@ func (p *ParticipantImpl) SetMigrateState(s types.MigrateState) { p.migrateState.Store(s) p.dirty.Store(true) - processPendingOffer := false - if s == types.MigrateStateSync { - processPendingOffer = true - } - - if s == types.MigrateStateComplete { - p.TransportManager.ProcessPendingPublisherDataChannels() - } - - if processPendingOffer { + switch s { + case types.MigrateStateSync: p.TransportManager.ProcessPendingPublisherOffer() + + case types.MigrateStateComplete: + p.TransportManager.ProcessPendingPublisherDataChannels() } if onMigrateStateChange := p.getOnMigrateStateChange(); onMigrateStateChange != nil { @@ -1484,9 +1479,14 @@ func (p *ParticipantImpl) onICECandidate(c *webrtc.ICECandidate, target livekit. } func (p *ParticipantImpl) onPublisherInitialConnected() { + if !p.hasPendingMigratedTrack() { + p.SetMigrateState(types.MigrateStateComplete) + } + if p.supervisor != nil { p.supervisor.SetPublisherPeerConnectionConnected(true) } + p.pubRTCPQueue.Start() } @@ -1497,7 +1497,9 @@ func (p *ParticipantImpl) onSubscriberInitialConnected() { } func (p *ParticipantImpl) onPrimaryTransportInitialConnected() { - if !p.hasPendingMigratedTrack() && p.MigrateState() == types.MigrateStateSync { + if !p.hasPendingMigratedTrack() && len(p.GetPublishedTracks()) == 0 { + // if there are no published tracks, declare migration complete on primary transport initial connect, + // else, wait for all tracks to be published and publisher peer connection established p.SetMigrateState(types.MigrateStateComplete) } } @@ -2102,7 +2104,7 @@ func (p *ParticipantImpl) handleTrackPublished(track types.MediaTrack) { delete(p.pendingPublishingTracks, track.ID()) p.pendingTracksLock.Unlock() - if !p.hasPendingMigratedTrack() { + if !p.hasPendingMigratedTrack() && p.TransportManager.HasPublisherEverConnected() { p.SetMigrateState(types.MigrateStateComplete) } } From d216f94ac159ca54fc93a96ff87d934bcedebd96 Mon Sep 17 00:00:00 2001 From: Raja Subramanian Date: Thu, 15 Feb 2024 18:05:50 +0530 Subject: [PATCH 33/44] Remove some logs. (#2484) * Remove some logs. Also, changing Errorw -> Warnw in a bunch of places. Going to move towards using `Errorw` for cases where a functionally unexpected condition happens, i.e by design a condition should not happen yet it triggered kind of scenarios. * log error --- pkg/rtc/subscriptionmanager.go | 6 +++--- pkg/rtc/transport.go | 30 ++++++++++++------------------ pkg/sfu/downtrack.go | 1 + 3 files changed, 16 insertions(+), 21 deletions(-) diff --git a/pkg/rtc/subscriptionmanager.go b/pkg/rtc/subscriptionmanager.go index 73e5acbe0..353936fd1 100644 --- a/pkg/rtc/subscriptionmanager.go +++ b/pkg/rtc/subscriptionmanager.go @@ -344,7 +344,7 @@ func (m *SubscriptionManager) reconcileSubscription(s *trackSubscription) { default: // all other errors if s.durationSinceStart() > subscriptionTimeout { - s.logger.Errorw("failed to subscribe, triggering error handler", err, + s.logger.Warnw("failed to subscribe, triggering error handler", err, "attempt", numAttempts, ) s.maybeRecordError(m.params.Telemetry, m.params.Participant.ID(), err, false) @@ -365,7 +365,7 @@ func (m *SubscriptionManager) reconcileSubscription(s *trackSubscription) { if s.needsUnsubscribe() { if err := m.unsubscribe(s); err != nil { - s.logger.Errorw("failed to unsubscribe", err) + s.logger.Warnw("failed to unsubscribe", err) } else { // successfully unsubscribed, remove from map m.lock.Lock() @@ -383,7 +383,7 @@ func (m *SubscriptionManager) reconcileSubscription(s *trackSubscription) { // if a publisher leaves or closes the source track, SubscribedTrack will be closed as well and it will go // back to needsSubscribe state if s.durationSinceStart() > subscriptionTimeout { - s.logger.Errorw("track not bound after timeout", nil) + s.logger.Warnw("track not bound after timeout", nil) s.maybeRecordError(m.params.Telemetry, m.params.Participant.ID(), ErrTrackNotBound, false) m.params.OnSubscriptionError(s.trackID, true, ErrTrackNotBound) } diff --git a/pkg/rtc/transport.go b/pkg/rtc/transport.go index 164d47926..22983adf4 100644 --- a/pkg/rtc/transport.go +++ b/pkg/rtc/transport.go @@ -326,7 +326,7 @@ func newPeerConnection(params TransportParams, onBandwidthEstimator func(estimat if len(params.SimTracks) > 0 { f, err := NewUnhandleSimulcastInterceptorFactory(UnhandleSimulcastTracks(params.SimTracks)) if err != nil { - params.Logger.Errorw("NewUnhandleSimulcastInterceptorFactory failed", err) + params.Logger.Warnw("NewUnhandleSimulcastInterceptorFactory failed", err) } else { ir.Add(f) } @@ -356,7 +356,7 @@ func newPeerConnection(params TransportParams, onBandwidthEstimator func(estimat params.Logger.Debugw("set rtx twcc and ext id", "ssrc", info.SSRC, "twccExtID", twccExtID) buffer.SetTWCCAndExtID(params.Twcc, uint8(twccExtID)) } else { - params.Logger.Errorw("failed to get buffer for rtx stream", nil, "ssrc", info.SSRC) + params.Logger.Warnw("failed to get buffer for rtx stream", nil, "ssrc", info.SSRC) } } } @@ -604,7 +604,7 @@ func (t *PCTransport) handleConnectionFailed(forceShortConn bool) { if isShort { pair, err := t.getSelectedPair() if err != nil { - t.params.Logger.Errorw("short ICE connection", err, "duration", duration) + t.params.Logger.Warnw("short ICE connection", err, "duration", duration) } else { t.params.Logger.Infow("short ICE connection", "pair", pair, "duration", duration) } @@ -632,11 +632,6 @@ func (t *PCTransport) onICEConnectionStateChange(state webrtc.ICEConnectionState case webrtc.ICEConnectionStateChecking: t.setICEStartedAt(time.Now()) - - case webrtc.ICEConnectionStateDisconnected: - t.params.Logger.Infow("ice connection state change unexpected", "state", state.String()) - case webrtc.ICEConnectionStateFailed: - t.params.Logger.Debugw("ice connection state change unexpected", "state", state.String()) } } @@ -652,7 +647,6 @@ func (t *PCTransport) onPeerConnectionStateChange(state webrtc.PeerConnectionSta t.maybeNotifyFullyEstablished() } case webrtc.PeerConnectionStateFailed: - t.params.Logger.Infow("peer connection failed") t.clearConnTimer() t.handleConnectionFailed(false) } @@ -823,7 +817,7 @@ func (t *PCTransport) CreateDataChannel(label string, dci *webrtc.DataChannelIni dcErrorHandler := func(err error) { if !errors.Is(err, sctp.ErrResetPacketInStateNotExist) && !errors.Is(err, sctp.ErrChunk) { - t.params.Logger.Errorw(dc.Label()+" data channel error", err) + t.params.Logger.Warnw(dc.Label()+" data channel error", err) } } @@ -848,7 +842,7 @@ func (t *PCTransport) CreateDataChannel(label string, dci *webrtc.DataChannelIni t.lossyDC.OnClose(dcCloseHandler) t.lossyDC.OnError(dcErrorHandler) default: - t.params.Logger.Errorw("unknown data channel label", nil, "label", dc.Label()) + t.params.Logger.Warnw("unknown data channel label", nil, "label", dc.Label()) } t.lock.Unlock() @@ -864,7 +858,7 @@ func (t *PCTransport) CreateDataChannelIfEmpty(dcLabel string, dci *webrtc.DataC case LossyDataChannel: dc = t.lossyDC default: - t.params.Logger.Errorw("unknown data channel label", nil, "label", label) + t.params.Logger.Warnw("unknown data channel label", nil, "label", label) err = errors.New("unknown data channel label") } t.lock.RUnlock() @@ -1164,7 +1158,7 @@ func (t *PCTransport) initPCWithPreviousAnswer(previousAnswer webrtc.SessionDesc // because sdp can negotiate multi times before migration.(it will sticky to the last m-line atfirst negotiate) // so use a dummy pc to negotiate sdp to fixed the datachannel's mid at same position with previous answer if err := t.preparePC(previousAnswer); err != nil { - t.params.Logger.Errorw("prepare pc for migration failed", err) + t.params.Logger.Warnw("prepare pc for migration failed", err) return senders, err } continue @@ -1202,7 +1196,7 @@ func (t *PCTransport) SetPreviousSdp(offer, answer *webrtc.SessionDescription) { if t.pc.RemoteDescription() == nil && t.previousAnswer == nil { t.previousAnswer = answer if senders, err := t.initPCWithPreviousAnswer(*t.previousAnswer); err != nil { - t.params.Logger.Errorw("initPCWithPreviousAnswer failed", err) + t.params.Logger.Warnw("initPCWithPreviousAnswer failed", err) t.lock.Unlock() t.params.Handler.OnNegotiationFailed() @@ -1211,7 +1205,7 @@ func (t *PCTransport) SetPreviousSdp(offer, answer *webrtc.SessionDescription) { // in migration case, can't reuse transceiver before negotiated except track subscribed at previous node t.canReuseTransceiver = false if err := t.parseTrackMid(*offer, senders); err != nil { - t.params.Logger.Errorw("parse previous offer failed", err, "offer", offer.SDP) + t.params.Logger.Warnw("parse previous offer failed", err, "offer", offer.SDP) } } } @@ -1251,7 +1245,7 @@ func (t *PCTransport) postEvent(event event) { err := t.handleEvent(&event) if err != nil { if !t.isClosed.Load() { - t.params.Logger.Errorw("error handling event", err, "event", event.String()) + t.params.Logger.Warnw("error handling event", err, "event", event.String()) t.params.Handler.OnNegotiationFailed() } } @@ -1398,7 +1392,7 @@ func (t *PCTransport) setNegotiationState(state transport.NegotiationState) { func (t *PCTransport) filterCandidates(sd webrtc.SessionDescription, preferTCP bool) webrtc.SessionDescription { parsed, err := sd.Unmarshal() if err != nil { - t.params.Logger.Errorw("could not unmarshal SDP to filter candidates", err) + t.params.Logger.Warnw("could not unmarshal SDP to filter candidates", err) return sd } @@ -1428,7 +1422,7 @@ func (t *PCTransport) filterCandidates(sd webrtc.SessionDescription, preferTCP b bytes, err := parsed.Marshal() if err != nil { - t.params.Logger.Errorw("could not marshal SDP to filter candidates", err) + t.params.Logger.Warnw("could not marshal SDP to filter candidates", err) return sd } sd.SDP = string(bytes) diff --git a/pkg/sfu/downtrack.go b/pkg/sfu/downtrack.go index c4deac566..6d42e755b 100644 --- a/pkg/sfu/downtrack.go +++ b/pkg/sfu/downtrack.go @@ -1641,6 +1641,7 @@ func (d *DownTrack) retransmitPackets(nacks []uint16) { "eosn", epm.extSequenceNumber, "eots", epm.extTimestamp, "sid", epm.layer, + "error", err, ) // TODO-VP9-DEBUG-REMOVE-END nackMisses++ From 8d7257dd19467726c045b080f88c241dc4d5a83d Mon Sep 17 00:00:00 2001 From: Benjamin Pracht Date: Thu, 15 Feb 2024 14:00:25 -0800 Subject: [PATCH 34/44] Move CreateIngress to IOInfoService. Adopt UpdateIngressStare from IOInfoService instead of IngressService (#2485) When paired with an updated ingress service, this will cause CreateIngress to be called twice for URL pull ingress, with the 2nd call failing. --- go.mod | 10 +- go.sum | 18 +-- pkg/service/clients.go | 5 + pkg/service/ingress.go | 25 +++- pkg/service/ioservice.go | 85 ++---------- pkg/service/ioservice_ingress.go | 90 ++++++++++++ pkg/service/servicefakes/fake_ioclient.go | 162 ++++++++++++++++++++++ pkg/service/wire_gen.go | 2 +- 8 files changed, 301 insertions(+), 96 deletions(-) create mode 100644 pkg/service/ioservice_ingress.go diff --git a/go.mod b/go.mod index 9894dcb7d..42cd30c94 100644 --- a/go.mod +++ b/go.mod @@ -19,7 +19,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-20240206082112-9bf41dcbce76 - github.com/livekit/protocol v1.9.10-0.20240213074347-cee767e2d909 + github.com/livekit/protocol v1.9.10-0.20240214205753-2e75b8fb463f github.com/livekit/psrpc v0.5.3-0.20240209001357-380f59f00c58 github.com/mackerelio/go-osstat v0.2.4 github.com/magefile/mage v1.15.0 @@ -47,7 +47,7 @@ require ( github.com/urfave/cli/v2 v2.27.1 github.com/urfave/negroni/v3 v3.0.0 go.uber.org/atomic v1.11.0 - golang.org/x/exp v0.0.0-20240205201215-2c58cdc269a3 + golang.org/x/exp v0.0.0-20240213143201-ec583247a57a golang.org/x/sync v0.6.0 google.golang.org/protobuf v1.32.0 gopkg.in/yaml.v3 v3.0.1 @@ -98,11 +98,11 @@ require ( go.uber.org/zap v1.26.0 // indirect golang.org/x/crypto v0.19.0 // indirect golang.org/x/mod v0.15.0 // indirect - golang.org/x/net v0.20.0 // indirect + golang.org/x/net v0.21.0 // indirect golang.org/x/sys v0.17.0 // indirect golang.org/x/text v0.14.0 // indirect - golang.org/x/tools v0.17.0 // indirect + golang.org/x/tools v0.18.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240205150955-31a09d347014 // indirect - google.golang.org/grpc v1.61.0 // indirect + google.golang.org/grpc v1.61.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect ) diff --git a/go.sum b/go.sum index 605b241d6..210a2b9ad 100644 --- a/go.sum +++ b/go.sum @@ -127,8 +127,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-20240206082112-9bf41dcbce76 h1:Zw88krOHni51OzDUlrduYb3m7VcsaKw06TnnDhsQpjg= github.com/livekit/mediatransportutil v0.0.0-20240206082112-9bf41dcbce76/go.mod h1:GBzn9xL+mivI1pW+tyExcKgbc0VOc29I9yJsNcAVaAc= -github.com/livekit/protocol v1.9.10-0.20240213074347-cee767e2d909 h1:36Jehw3Z0CHV3QDbDWUndky02lGtdpl0XTDs7u8UwPk= -github.com/livekit/protocol v1.9.10-0.20240213074347-cee767e2d909/go.mod h1:042ukZY29snaLjTVxVO+X89zuqqr9HDcpkq45c9Y27A= +github.com/livekit/protocol v1.9.10-0.20240214205753-2e75b8fb463f h1:f/KuQMFbWxKp43I2mftTLCWTwGnwtTE9FF7Btr78Uzc= +github.com/livekit/protocol v1.9.10-0.20240214205753-2e75b8fb463f/go.mod h1:/kviHT6yTNqHdZ9QsvRuxAHf7LaBROa7qe5naT1oVrU= github.com/livekit/psrpc v0.5.3-0.20240209001357-380f59f00c58 h1:yH55rBGLRO+ict2mu6bKZ5iPwTIrIwU1i0ydgThi4+k= github.com/livekit/psrpc v0.5.3-0.20240209001357-380f59f00c58/go.mod h1:cQjxg1oCxYHhxxv6KJH1gSvdtCHQoRZCHgPdm5N8v2g= github.com/mackerelio/go-osstat v0.2.4 h1:qxGbdPkFo65PXOb/F/nhDKpF2nGmGaCFDLXoZjJTtUs= @@ -296,8 +296,8 @@ golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliY 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-20240205201215-2c58cdc269a3 h1:/RIbNt/Zr7rVhIkQhooTxCxFcdWLGIKnZA4IXNFSrvo= -golang.org/x/exp v0.0.0-20240205201215-2c58cdc269a3/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08= +golang.org/x/exp v0.0.0-20240213143201-ec583247a57a h1:HinSgX1tJRX3KsL//Gxynpw5CTOAIPhgL4W8PNiIpVE= +golang.org/x/exp v0.0.0-20240213143201-ec583247a57a/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc= 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= @@ -332,8 +332,9 @@ 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.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= -golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= 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/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= @@ -415,16 +416,17 @@ golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4f 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.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= -golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc= golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps= +golang.org/x/tools v0.18.0 h1:k8NLag8AGHnn+PHbl7g43CtqZAwG60vZkLqgyZgIHgQ= +golang.org/x/tools v0.18.0/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg= 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-20240205150955-31a09d347014 h1:FSL3lRCkhaPFxqi0s9o+V4UI2WTzAVOvkgbd4kVV4Wg= google.golang.org/genproto/googleapis/rpc v0.0.0-20240205150955-31a09d347014/go.mod h1:SaPjaZGWb0lPqs6Ittu0spdfrOArqji4ZdeP5IC/9N4= -google.golang.org/grpc v1.61.0 h1:TOvOcuXn30kRao+gfcvsebNEa5iZIiLkisYEkf7R7o0= -google.golang.org/grpc v1.61.0/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs= +google.golang.org/grpc v1.61.1 h1:kLAiWrZs7YeDM6MumDe7m3y4aM6wacLzM1Y/wiLP9XY= +google.golang.org/grpc v1.61.1/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs= 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= diff --git a/pkg/service/clients.go b/pkg/service/clients.go index 53c0e415b..89faa691a 100644 --- a/pkg/service/clients.go +++ b/pkg/service/clients.go @@ -26,10 +26,15 @@ import ( "github.com/livekit/protocol/utils" ) +//go:generate go run github.com/maxbrunsfeld/counterfeiter/v6 -generate + +//counterfeiter:generate . IOClient type IOClient interface { CreateEgress(ctx context.Context, info *livekit.EgressInfo) (*emptypb.Empty, error) GetEgress(ctx context.Context, req *rpc.GetEgressRequest) (*livekit.EgressInfo, error) ListEgress(ctx context.Context, req *livekit.ListEgressRequest) (*livekit.ListEgressResponse, error) + CreateIngress(ctx context.Context, req *livekit.IngressInfo) (*emptypb.Empty, error) + UpdateIngressState(ctx context.Context, req *rpc.UpdateIngressStateRequest) (*emptypb.Empty, error) } type egressLauncher struct { diff --git a/pkg/service/ingress.go b/pkg/service/ingress.go index 75d5ffc91..40b09aaf3 100644 --- a/pkg/service/ingress.go +++ b/pkg/service/ingress.go @@ -39,6 +39,7 @@ type IngressService struct { bus psrpc.MessageBus psrpcClient rpc.IngressClient store IngressStore + io IOClient roomService livekit.RoomService telemetry telemetry.TelemetryService launcher IngressLauncher @@ -50,6 +51,7 @@ func NewIngressServiceWithIngressLauncher( bus psrpc.MessageBus, psrpcClient rpc.IngressClient, store IngressStore, + io IOClient, rs livekit.RoomService, ts telemetry.TelemetryService, launcher IngressLauncher, @@ -61,6 +63,7 @@ func NewIngressServiceWithIngressLauncher( bus: bus, psrpcClient: psrpcClient, store: store, + io: io, roomService: rs, telemetry: ts, launcher: launcher, @@ -73,10 +76,11 @@ func NewIngressService( bus psrpc.MessageBus, psrpcClient rpc.IngressClient, store IngressStore, + io IOClient, rs livekit.RoomService, ts telemetry.TelemetryService, ) *IngressService { - s := NewIngressServiceWithIngressLauncher(conf, nodeID, bus, psrpcClient, store, rs, ts, nil) + s := NewIngressServiceWithIngressLauncher(conf, nodeID, bus, psrpcClient, store, io, rs, ts, nil) s.launcher = s @@ -192,11 +196,19 @@ func (s *IngressService) CreateIngressWithUrl(ctx context.Context, urlStr string } } - if err = s.store.StoreIngress(ctx, info); err != nil { - logger.Errorw("could not write ingress info", err) + // TODO Remove this store Ingress call for URL pull as it is redundant since + // the ingress service sends a CreateIngress RPC + _, err = s.io.CreateIngress(ctx, info) + switch err { + case nil: + break + case ingress.ErrIngressOutOfDate: + // Error returned if the ingress was already created by the ingress service + err = nil + default: + logger.Errorw("could not create ingress object", err) return nil, err } - s.telemetry.IngressCreated(ctx, info) return info, nil } @@ -274,7 +286,10 @@ func (s *IngressService) UpdateIngress(ctx context.Context, req *livekit.UpdateI switch info.State.Status { case livekit.IngressState_ENDPOINT_ERROR: info.State.Status = livekit.IngressState_ENDPOINT_INACTIVE - err = s.store.UpdateIngressState(ctx, req.IngressId, info.State) + _, err = s.io.UpdateIngressState(ctx, &rpc.UpdateIngressStateRequest{ + IngressId: req.IngressId, + State: info.State, + }) if err != nil { logger.Warnw("could not store ingress state", err) } diff --git a/pkg/service/ioservice.go b/pkg/service/ioservice.go index 67f995033..f3f2848c9 100644 --- a/pkg/service/ioservice.go +++ b/pkg/service/ioservice.go @@ -16,7 +16,6 @@ package service import ( "context" - "errors" "google.golang.org/protobuf/types/known/emptypb" @@ -78,6 +77,14 @@ func (s *IOInfoService) Start() error { return nil } +func (s *IOInfoService) Stop() { + close(s.shutdown) + + if s.ioServer != nil { + s.ioServer.Shutdown() + } +} + func (s *IOInfoService) CreateEgress(ctx context.Context, info *livekit.EgressInfo) (*emptypb.Empty, error) { // check if egress already exists to avoid duplicate EgressStarted event if _, err := s.es.LoadEgress(ctx, info.EgressId); err == nil { @@ -156,79 +163,3 @@ func (s *IOInfoService) UpdateMetrics(ctx context.Context, req *rpc.UpdateMetric ) return &emptypb.Empty{}, nil } - -func (s *IOInfoService) GetIngressInfo(ctx context.Context, req *rpc.GetIngressInfoRequest) (*rpc.GetIngressInfoResponse, error) { - info, err := s.loadIngressFromInfoRequest(req) - if err != nil { - return nil, err - } - - return &rpc.GetIngressInfoResponse{Info: info}, nil -} - -func (s *IOInfoService) loadIngressFromInfoRequest(req *rpc.GetIngressInfoRequest) (info *livekit.IngressInfo, err error) { - if req.IngressId != "" { - info, err = s.is.LoadIngress(context.Background(), req.IngressId) - } else if req.StreamKey != "" { - info, err = s.is.LoadIngressFromStreamKey(context.Background(), req.StreamKey) - } else { - err = errors.New("request needs to specify either IngressId or StreamKey") - } - return info, err -} - -func (s *IOInfoService) UpdateIngressState(ctx context.Context, req *rpc.UpdateIngressStateRequest) (*emptypb.Empty, error) { - info, err := s.is.LoadIngress(ctx, req.IngressId) - if err != nil { - return nil, err - } - - if err = s.is.UpdateIngressState(ctx, req.IngressId, req.State); err != nil { - logger.Errorw("could not update ingress", err) - return nil, err - } - - if info.State.Status != req.State.Status { - info.State = req.State - - switch req.State.Status { - case livekit.IngressState_ENDPOINT_ERROR, - livekit.IngressState_ENDPOINT_INACTIVE, - livekit.IngressState_ENDPOINT_COMPLETE: - s.telemetry.IngressEnded(ctx, info) - - if req.State.Error != "" { - logger.Infow("ingress failed", "error", req.State.Error, "ingressID", req.IngressId) - } else { - logger.Infow("ingress ended", "ingressID", req.IngressId) - } - - case livekit.IngressState_ENDPOINT_PUBLISHING: - s.telemetry.IngressStarted(ctx, info) - - logger.Infow("ingress started", "ingressID", req.IngressId) - - case livekit.IngressState_ENDPOINT_BUFFERING: - s.telemetry.IngressUpdated(ctx, info) - - logger.Infow("ingress buffering", "ingressID", req.IngressId) - } - } else { - // Status didn't change, send Updated event - info.State = req.State - - s.telemetry.IngressUpdated(ctx, info) - - logger.Infow("ingress updated", "ingressID", req.IngressId, "status", info.State.Status) - } - - return &emptypb.Empty{}, nil -} - -func (s *IOInfoService) Stop() { - close(s.shutdown) - - if s.ioServer != nil { - s.ioServer.Shutdown() - } -} diff --git a/pkg/service/ioservice_ingress.go b/pkg/service/ioservice_ingress.go new file mode 100644 index 000000000..cba5ab390 --- /dev/null +++ b/pkg/service/ioservice_ingress.go @@ -0,0 +1,90 @@ +package service + +import ( + "context" + "errors" + + "github.com/livekit/protocol/livekit" + "github.com/livekit/protocol/logger" + "github.com/livekit/protocol/rpc" + "google.golang.org/protobuf/types/known/emptypb" +) + +func (s *IOInfoService) CreateIngress(ctx context.Context, info *livekit.IngressInfo) (*emptypb.Empty, error) { + err := s.is.StoreIngress(ctx, info) + if err != nil { + return nil, err + } + + s.telemetry.IngressCreated(ctx, info) + + return &emptypb.Empty{}, nil +} + +func (s *IOInfoService) GetIngressInfo(ctx context.Context, req *rpc.GetIngressInfoRequest) (*rpc.GetIngressInfoResponse, error) { + info, err := s.loadIngressFromInfoRequest(req) + if err != nil { + return nil, err + } + + return &rpc.GetIngressInfoResponse{Info: info}, nil +} + +func (s *IOInfoService) loadIngressFromInfoRequest(req *rpc.GetIngressInfoRequest) (info *livekit.IngressInfo, err error) { + if req.IngressId != "" { + info, err = s.is.LoadIngress(context.Background(), req.IngressId) + } else if req.StreamKey != "" { + info, err = s.is.LoadIngressFromStreamKey(context.Background(), req.StreamKey) + } else { + err = errors.New("request needs to specify either IngressId or StreamKey") + } + return info, err +} + +func (s *IOInfoService) UpdateIngressState(ctx context.Context, req *rpc.UpdateIngressStateRequest) (*emptypb.Empty, error) { + info, err := s.is.LoadIngress(ctx, req.IngressId) + if err != nil { + return nil, err + } + + if err = s.is.UpdateIngressState(ctx, req.IngressId, req.State); err != nil { + logger.Errorw("could not update ingress", err) + return nil, err + } + + if info.State.Status != req.State.Status { + info.State = req.State + + switch req.State.Status { + case livekit.IngressState_ENDPOINT_ERROR, + livekit.IngressState_ENDPOINT_INACTIVE, + livekit.IngressState_ENDPOINT_COMPLETE: + s.telemetry.IngressEnded(ctx, info) + + if req.State.Error != "" { + logger.Infow("ingress failed", "error", req.State.Error, "ingressID", req.IngressId) + } else { + logger.Infow("ingress ended", "ingressID", req.IngressId) + } + + case livekit.IngressState_ENDPOINT_PUBLISHING: + s.telemetry.IngressStarted(ctx, info) + + logger.Infow("ingress started", "ingressID", req.IngressId) + + case livekit.IngressState_ENDPOINT_BUFFERING: + s.telemetry.IngressUpdated(ctx, info) + + logger.Infow("ingress buffering", "ingressID", req.IngressId) + } + } else { + // Status didn't change, send Updated event + info.State = req.State + + s.telemetry.IngressUpdated(ctx, info) + + logger.Infow("ingress state updated", "ingressID", req.IngressId, "status", info.State.Status) + } + + return &emptypb.Empty{}, nil +} diff --git a/pkg/service/servicefakes/fake_ioclient.go b/pkg/service/servicefakes/fake_ioclient.go index 5d440cbb9..0e1e22ac5 100644 --- a/pkg/service/servicefakes/fake_ioclient.go +++ b/pkg/service/servicefakes/fake_ioclient.go @@ -26,6 +26,20 @@ type FakeIOClient struct { result1 *emptypb.Empty result2 error } + CreateIngressStub func(context.Context, *livekit.IngressInfo) (*emptypb.Empty, error) + createIngressMutex sync.RWMutex + createIngressArgsForCall []struct { + arg1 context.Context + arg2 *livekit.IngressInfo + } + createIngressReturns struct { + result1 *emptypb.Empty + result2 error + } + createIngressReturnsOnCall map[int]struct { + result1 *emptypb.Empty + result2 error + } GetEgressStub func(context.Context, *rpc.GetEgressRequest) (*livekit.EgressInfo, error) getEgressMutex sync.RWMutex getEgressArgsForCall []struct { @@ -54,6 +68,20 @@ type FakeIOClient struct { result1 *livekit.ListEgressResponse result2 error } + UpdateIngressStateStub func(context.Context, *rpc.UpdateIngressStateRequest) (*emptypb.Empty, error) + updateIngressStateMutex sync.RWMutex + updateIngressStateArgsForCall []struct { + arg1 context.Context + arg2 *rpc.UpdateIngressStateRequest + } + updateIngressStateReturns struct { + result1 *emptypb.Empty + result2 error + } + updateIngressStateReturnsOnCall map[int]struct { + result1 *emptypb.Empty + result2 error + } invocations map[string][][]interface{} invocationsMutex sync.RWMutex } @@ -123,6 +151,71 @@ func (fake *FakeIOClient) CreateEgressReturnsOnCall(i int, result1 *emptypb.Empt }{result1, result2} } +func (fake *FakeIOClient) CreateIngress(arg1 context.Context, arg2 *livekit.IngressInfo) (*emptypb.Empty, error) { + fake.createIngressMutex.Lock() + ret, specificReturn := fake.createIngressReturnsOnCall[len(fake.createIngressArgsForCall)] + fake.createIngressArgsForCall = append(fake.createIngressArgsForCall, struct { + arg1 context.Context + arg2 *livekit.IngressInfo + }{arg1, arg2}) + stub := fake.CreateIngressStub + fakeReturns := fake.createIngressReturns + fake.recordInvocation("CreateIngress", []interface{}{arg1, arg2}) + fake.createIngressMutex.Unlock() + if stub != nil { + return stub(arg1, arg2) + } + if specificReturn { + return ret.result1, ret.result2 + } + return fakeReturns.result1, fakeReturns.result2 +} + +func (fake *FakeIOClient) CreateIngressCallCount() int { + fake.createIngressMutex.RLock() + defer fake.createIngressMutex.RUnlock() + return len(fake.createIngressArgsForCall) +} + +func (fake *FakeIOClient) CreateIngressCalls(stub func(context.Context, *livekit.IngressInfo) (*emptypb.Empty, error)) { + fake.createIngressMutex.Lock() + defer fake.createIngressMutex.Unlock() + fake.CreateIngressStub = stub +} + +func (fake *FakeIOClient) CreateIngressArgsForCall(i int) (context.Context, *livekit.IngressInfo) { + fake.createIngressMutex.RLock() + defer fake.createIngressMutex.RUnlock() + argsForCall := fake.createIngressArgsForCall[i] + return argsForCall.arg1, argsForCall.arg2 +} + +func (fake *FakeIOClient) CreateIngressReturns(result1 *emptypb.Empty, result2 error) { + fake.createIngressMutex.Lock() + defer fake.createIngressMutex.Unlock() + fake.CreateIngressStub = nil + fake.createIngressReturns = struct { + result1 *emptypb.Empty + result2 error + }{result1, result2} +} + +func (fake *FakeIOClient) CreateIngressReturnsOnCall(i int, result1 *emptypb.Empty, result2 error) { + fake.createIngressMutex.Lock() + defer fake.createIngressMutex.Unlock() + fake.CreateIngressStub = nil + if fake.createIngressReturnsOnCall == nil { + fake.createIngressReturnsOnCall = make(map[int]struct { + result1 *emptypb.Empty + result2 error + }) + } + fake.createIngressReturnsOnCall[i] = struct { + result1 *emptypb.Empty + result2 error + }{result1, result2} +} + func (fake *FakeIOClient) GetEgress(arg1 context.Context, arg2 *rpc.GetEgressRequest) (*livekit.EgressInfo, error) { fake.getEgressMutex.Lock() ret, specificReturn := fake.getEgressReturnsOnCall[len(fake.getEgressArgsForCall)] @@ -253,15 +346,84 @@ func (fake *FakeIOClient) ListEgressReturnsOnCall(i int, result1 *livekit.ListEg }{result1, result2} } +func (fake *FakeIOClient) UpdateIngressState(arg1 context.Context, arg2 *rpc.UpdateIngressStateRequest) (*emptypb.Empty, error) { + fake.updateIngressStateMutex.Lock() + ret, specificReturn := fake.updateIngressStateReturnsOnCall[len(fake.updateIngressStateArgsForCall)] + fake.updateIngressStateArgsForCall = append(fake.updateIngressStateArgsForCall, struct { + arg1 context.Context + arg2 *rpc.UpdateIngressStateRequest + }{arg1, arg2}) + stub := fake.UpdateIngressStateStub + fakeReturns := fake.updateIngressStateReturns + fake.recordInvocation("UpdateIngressState", []interface{}{arg1, arg2}) + fake.updateIngressStateMutex.Unlock() + if stub != nil { + return stub(arg1, arg2) + } + if specificReturn { + return ret.result1, ret.result2 + } + return fakeReturns.result1, fakeReturns.result2 +} + +func (fake *FakeIOClient) UpdateIngressStateCallCount() int { + fake.updateIngressStateMutex.RLock() + defer fake.updateIngressStateMutex.RUnlock() + return len(fake.updateIngressStateArgsForCall) +} + +func (fake *FakeIOClient) UpdateIngressStateCalls(stub func(context.Context, *rpc.UpdateIngressStateRequest) (*emptypb.Empty, error)) { + fake.updateIngressStateMutex.Lock() + defer fake.updateIngressStateMutex.Unlock() + fake.UpdateIngressStateStub = stub +} + +func (fake *FakeIOClient) UpdateIngressStateArgsForCall(i int) (context.Context, *rpc.UpdateIngressStateRequest) { + fake.updateIngressStateMutex.RLock() + defer fake.updateIngressStateMutex.RUnlock() + argsForCall := fake.updateIngressStateArgsForCall[i] + return argsForCall.arg1, argsForCall.arg2 +} + +func (fake *FakeIOClient) UpdateIngressStateReturns(result1 *emptypb.Empty, result2 error) { + fake.updateIngressStateMutex.Lock() + defer fake.updateIngressStateMutex.Unlock() + fake.UpdateIngressStateStub = nil + fake.updateIngressStateReturns = struct { + result1 *emptypb.Empty + result2 error + }{result1, result2} +} + +func (fake *FakeIOClient) UpdateIngressStateReturnsOnCall(i int, result1 *emptypb.Empty, result2 error) { + fake.updateIngressStateMutex.Lock() + defer fake.updateIngressStateMutex.Unlock() + fake.UpdateIngressStateStub = nil + if fake.updateIngressStateReturnsOnCall == nil { + fake.updateIngressStateReturnsOnCall = make(map[int]struct { + result1 *emptypb.Empty + result2 error + }) + } + fake.updateIngressStateReturnsOnCall[i] = struct { + result1 *emptypb.Empty + result2 error + }{result1, result2} +} + func (fake *FakeIOClient) Invocations() map[string][][]interface{} { fake.invocationsMutex.RLock() defer fake.invocationsMutex.RUnlock() fake.createEgressMutex.RLock() defer fake.createEgressMutex.RUnlock() + fake.createIngressMutex.RLock() + defer fake.createIngressMutex.RUnlock() fake.getEgressMutex.RLock() defer fake.getEgressMutex.RUnlock() fake.listEgressMutex.RLock() defer fake.listEgressMutex.RUnlock() + fake.updateIngressStateMutex.RLock() + defer fake.updateIngressStateMutex.RUnlock() copiedInvocations := map[string][][]interface{}{} for key, value := range fake.invocations { copiedInvocations[key] = value diff --git a/pkg/service/wire_gen.go b/pkg/service/wire_gen.go index e76fa56c7..c8307a3bf 100644 --- a/pkg/service/wire_gen.go +++ b/pkg/service/wire_gen.go @@ -106,7 +106,7 @@ func InitializeServer(conf *config.Config, currentNode routing.LocalNode) (*Live if err != nil { return nil, err } - ingressService := NewIngressService(ingressConfig, nodeID, messageBus, ingressClient, ingressStore, roomService, telemetryService) + ingressService := NewIngressService(ingressConfig, nodeID, messageBus, ingressClient, ingressStore, ioInfoService, roomService, telemetryService) sipConfig := getSIPConfig(conf) sipClient, err := rpc.NewSIPClient(messageBus) if err != nil { From 5ac5bd236a903a1a5d720a81abec3669db5edf72 Mon Sep 17 00:00:00 2001 From: Raja Subramanian Date: Sat, 17 Feb 2024 13:40:07 +0530 Subject: [PATCH 35/44] Let track events go through after participant close. (#2487) * Let track events go through after participant close. Also, reducing lock scope in telemetry service. * use shadow --- pkg/rtc/participant.go | 2 +- pkg/rtc/subscriptionmanager.go | 2 +- pkg/telemetry/telemetryservice.go | 39 +++++++++++++++++++++++-------- 3 files changed, 31 insertions(+), 12 deletions(-) diff --git a/pkg/rtc/participant.go b/pkg/rtc/participant.go index 5cf2dc299..962b90b1f 100644 --- a/pkg/rtc/participant.go +++ b/pkg/rtc/participant.go @@ -2063,7 +2063,7 @@ func (p *ParticipantImpl) addMediaTrack(signalCid string, sdpCid string, ti *liv p.ID(), p.Identity(), mt.ToProto(), - !p.IsClosed(), + true, ) // re-use Track sid diff --git a/pkg/rtc/subscriptionmanager.go b/pkg/rtc/subscriptionmanager.go index 353936fd1..eb879d1a6 100644 --- a/pkg/rtc/subscriptionmanager.go +++ b/pkg/rtc/subscriptionmanager.go @@ -662,7 +662,7 @@ func (m *SubscriptionManager) handleSubscribedTrackClose(s *trackSubscription, w context.Background(), m.params.Participant.ID(), &livekit.TrackInfo{Sid: string(s.trackID), Type: subTrack.MediaTrack().Kind()}, - !willBeResumed && !m.params.Participant.IsClosed(), + !willBeResumed, ) dt := subTrack.DownTrack() diff --git a/pkg/telemetry/telemetryservice.go b/pkg/telemetry/telemetryservice.go index fe5e329c2..c684d5842 100644 --- a/pkg/telemetry/telemetryservice.go +++ b/pkg/telemetry/telemetryservice.go @@ -24,6 +24,7 @@ import ( "github.com/livekit/protocol/livekit" "github.com/livekit/protocol/logger" "github.com/livekit/protocol/webhook" + "golang.org/x/exp/maps" ) //go:generate go run github.com/maxbrunsfeld/counterfeiter/v6 . TelemetryService @@ -93,8 +94,9 @@ type telemetryService struct { notifier webhook.QueuedNotifier jobsQueue *utils.OpsQueue - lock sync.RWMutex - workers map[livekit.ParticipantID]*StatsWorker + lock sync.RWMutex + workers map[livekit.ParticipantID]*StatsWorker + workersShadow []*StatsWorker } func NewTelemetryService(notifier webhook.QueuedNotifier, analytics AnalyticsService) TelemetryService { @@ -113,10 +115,11 @@ func NewTelemetryService(notifier webhook.QueuedNotifier, analytics AnalyticsSer } func (t *telemetryService) FlushStats() { - t.lock.Lock() - defer t.lock.Unlock() + t.lock.RLock() + workersShadow := t.workersShadow + t.lock.RUnlock() - for _, worker := range t.workers { + for _, worker := range workersShadow { worker.Flush() } } @@ -167,21 +170,37 @@ func (t *telemetryService) createWorker(ctx context.Context, t.lock.Lock() t.workers[participantID] = worker + t.workersShadow = maps.Values(t.workers) t.lock.Unlock() return worker } func (t *telemetryService) cleanupWorkers() { - t.lock.Lock() - defer t.lock.Unlock() + t.lock.RLock() + workersShadow := t.workersShadow + t.lock.RUnlock() - for participantID, worker := range t.workers { + toReap := make([]livekit.ParticipantID, 0, len(workersShadow)) + for _, worker := range workersShadow { closedAt := worker.ClosedAt() if !closedAt.IsZero() && time.Since(closedAt) > workerCleanupWait { - logger.Debugw("reaping analytics worker for participant", "pID", participantID) - delete(t.workers, participantID) + worker.Flush() + + toReap = append(toReap, worker.ParticipantID()) } } + + if len(toReap) == 0 { + return + } + + t.lock.Lock() + logger.Debugw("reaping analytics worker for participants", "pID", toReap) + for _, pID := range toReap { + delete(t.workers, pID) + } + t.workersShadow = maps.Values(t.workers) + t.lock.Unlock() } func (t *telemetryService) LocalRoomState(ctx context.Context, info *livekit.AnalyticsNodeRooms) { From e7dd03181c2acc5fc51c102fda91164df17f9651 Mon Sep 17 00:00:00 2001 From: Raja Subramanian Date: Sat, 17 Feb 2024 21:30:19 +0530 Subject: [PATCH 36/44] Use smaller default deque size. (#2488) --- pkg/rtc/dynacastmanager.go | 2 +- pkg/utils/opsqueue.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/rtc/dynacastmanager.go b/pkg/rtc/dynacastmanager.go index 1163cb09a..c9395e60a 100644 --- a/pkg/rtc/dynacastmanager.go +++ b/pkg/rtc/dynacastmanager.go @@ -59,7 +59,7 @@ func NewDynacastManager(params DynacastManagerParams) *DynacastManager { maxSubscribedQuality: make(map[string]livekit.VideoQuality), committedMaxSubscribedQuality: make(map[string]livekit.VideoQuality), maxSubscribedQualityDebounce: debounce.New(params.DynacastPauseDelay), - qualityNotifyOpQueue: utils.NewOpsQueue("quality-notify", 0, true), + qualityNotifyOpQueue: utils.NewOpsQueue("quality-notify", 64, true), } d.qualityNotifyOpQueue.Start() return d diff --git a/pkg/utils/opsqueue.go b/pkg/utils/opsqueue.go index 30dbbe1ec..19f8d7ecc 100644 --- a/pkg/utils/opsqueue.go +++ b/pkg/utils/opsqueue.go @@ -41,7 +41,7 @@ func NewOpsQueue(name string, minSize uint, flushOnStop bool) *OpsQueue { wake: make(chan struct{}, 1), doneChan: make(chan struct{}), } - oq.ops.SetMinCapacity(uint(utils.Min(bits.Len64(uint64(minSize-1)), 16))) + oq.ops.SetMinCapacity(uint(utils.Min(bits.Len64(uint64(minSize-1)), 7))) return oq } From 69ad8a28e4c99bf5b8a1840c962ea1f6e8e7f918 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 17 Feb 2024 11:26:20 -0800 Subject: [PATCH 37/44] Update golang Docker tag to v1.22 (#2458) Generated by renovateBot Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 3258a00fa..ebdf80cac 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -FROM golang:1.21-alpine as builder +FROM golang:1.22-alpine as builder ARG TARGETPLATFORM ARG TARGETARCH From 110cc83508146ed27048557c58ac5d78ad3a1952 Mon Sep 17 00:00:00 2001 From: zesun96 <52589692+zesun96@users.noreply.github.com> Date: Sun, 18 Feb 2024 03:32:23 +0800 Subject: [PATCH 38/44] fix participant refreshing the room information after room closed (#2445) --- pkg/service/roommanager.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/service/roommanager.go b/pkg/service/roommanager.go index b09ec48c2..be65c314f 100644 --- a/pkg/service/roommanager.go +++ b/pkg/service/roommanager.go @@ -469,7 +469,7 @@ func (r *RoomManager) StartSession( } persistRoomForParticipantCount := func(proto *livekit.Room) { - if !participant.Hidden() { + if !participant.Hidden() && !room.IsClosed() { err = r.roomStore.StoreRoom(ctx, proto, room.Internal()) if err != nil { logger.Errorw("could not store room", err) From 7658e1b60db047a1cae6b05cd297acca174b48b5 Mon Sep 17 00:00:00 2001 From: akarl10 Date: Sat, 17 Feb 2024 20:44:55 +0100 Subject: [PATCH 39/44] make 64bit atomics also align on 32bit (#2386) do this by simply putting them at the front of each struct see https://pkg.go.dev/sync/atomic#pkg-note-BUG fixes #2383 Co-authored-by: akarl10 --- pkg/rtc/participant.go | 5 ++++- pkg/rtc/room.go | 14 +++++++++----- pkg/rtc/uptrackmanager.go | 5 ++++- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/pkg/rtc/participant.go b/pkg/rtc/participant.go index 962b90b1f..acf326311 100644 --- a/pkg/rtc/participant.go +++ b/pkg/rtc/participant.go @@ -137,6 +137,10 @@ type ParticipantParams struct { } type ParticipantImpl struct { + // utils.TimedVersion is a atomic. To be correctly aligned also on 32bit archs + // 64it atomics need to be at the front of a struct + timedVersion utils.TimedVersion + params ParticipantParams isClosed atomic.Bool @@ -200,7 +204,6 @@ type ParticipantImpl struct { dirty atomic.Bool version atomic.Uint32 - timedVersion utils.TimedVersion // callbacks & handlers onTrackPublished func(types.LocalParticipant, types.MediaTrack) diff --git a/pkg/rtc/room.go b/pkg/rtc/room.go index 622e5e62b..3b94a0cf9 100644 --- a/pkg/rtc/room.go +++ b/pkg/rtc/room.go @@ -80,6 +80,15 @@ type disconnectSignalOnResumeNoMessages struct { } type Room struct { + // atomics always need to be 64bit/8byte aligned + // on 32bit arch only the beginning of the struct + // starts at such a boundary. + // time the first participant joined the room + joinedAt atomic.Int64 + // time that the last participant left the room + leftAt atomic.Int64 + holds atomic.Int32 + lock sync.RWMutex protoRoom *livekit.Room @@ -109,11 +118,6 @@ type Room struct { batchedUpdates map[livekit.ParticipantIdentity]*participantUpdate batchedUpdatesMu sync.Mutex - // time the first participant joined the room - joinedAt atomic.Int64 - holds atomic.Int32 - // time that the last participant left the room - leftAt atomic.Int64 closed chan struct{} trailer []byte diff --git a/pkg/rtc/uptrackmanager.go b/pkg/rtc/uptrackmanager.go index 5cadfc267..2e81324ab 100644 --- a/pkg/rtc/uptrackmanager.go +++ b/pkg/rtc/uptrackmanager.go @@ -37,6 +37,10 @@ type UpTrackManagerParams struct { // UpTrackManager manages all uptracks from a participant type UpTrackManager struct { + // utils.TimedVersion is a atomic. To be correctly aligned also on 32bit archs + // 64it atomics need to be at the front of a struct + subscriptionPermissionVersion utils.TimedVersion + params UpTrackManagerParams closed bool @@ -44,7 +48,6 @@ type UpTrackManager struct { // publishedTracks that participant is publishing publishedTracks map[livekit.TrackID]types.MediaTrack subscriptionPermission *livekit.SubscriptionPermission - subscriptionPermissionVersion utils.TimedVersion // subscriber permission for published tracks subscriberPermissions map[livekit.ParticipantIdentity]*livekit.TrackPermission // subscriberIdentity => *livekit.TrackPermission From 8371848747101bbdfdd58a7d494a5502f0944986 Mon Sep 17 00:00:00 2001 From: David Zhao Date: Sat, 17 Feb 2024 12:37:15 -0800 Subject: [PATCH 40/44] Version 1.5.3 (#2489) * Version 1.5.3 * add missing copyright notices * update protocol for redis.tls YAML keys --- CHANGELOG | 38 +++++++++++++++++++ config-sample.yaml | 27 ++++++++++++- go.mod | 2 +- go.sum | 4 +- pkg/rtc/transport/handler.go | 14 +++++++ pkg/rtc/transport/negotiationstate.go | 14 +++++++ pkg/service/ioservice_ingress.go | 14 +++++++ pkg/sfu/buffer/frameintegrity.go | 14 +++++++ pkg/sfu/buffer/frameintegrity_test.go | 14 +++++++ pkg/sfu/interceptor/rtx.go | 14 +++++++ pkg/sfu/playoutdelay.go | 14 +++++++ pkg/sfu/playoutdelay_test.go | 14 +++++++ pkg/sfu/rtpextension/playoutdelay.go | 14 +++++++ pkg/sfu/rtpextension/playoutdelay_test.go | 14 +++++++ .../videolayerselector/framenumberwrapper.go | 14 +++++++ .../framenumberwrapper_test.go | 14 +++++++ test/agent.go | 14 +++++++ version/version.go | 2 +- 18 files changed, 249 insertions(+), 6 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index bb789bceb..d8408a4c5 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -2,6 +2,44 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [1.5.3] - 2024-02-17 + +### Added +- Added dynamic playout delay if PlayoutDelay enabled in the room (#2403) +- Allow creating SRT URL pull ingress (requires Ingress service release) (#2416) +- Use default max playout delay as chrome (#2411) +- RTX support on publisher transport (#2452) +- Add exponential backoff to room service check retries (#2462) +- Add support for ingress ParticipantMetadata (#2461) + +### Fixed +- Prevent race of new track and new receiver. (#2345) +- Fixed race condition when applying metadata update. (#2363 #2478) +- Fixed race condition in DownTrack.Bind. (#2388) +- Improved PSRPC over redis reliability with keepalive (#2398) +- Fix race condition on Participant.updateState (#2401) +- Replace /bin/bash with env call for FreeBSD compatibility (#2409) +- Fix startup with -dev and -config (#2442) +- Fix published track leaks: close published tracks on participant close (#2446) +- Enforce empty SID for UserPacket from hidden participants (#2469) +- Ignore duplicate RID. (Fix for spec breakage by Firefox on Windows 10) (#2471) + +### Changed +- Logging improvements (various PRs) +- Server shuts down after a second SIGINT to simplify development lifecycle (#2364) +- A/V sync improvements (#2369 #2437 #2472) +- Prometheus: larger max session start time bin size (#2380) +- Updated SIP protocol for creating participants. (requires latest SIP release) (#2404 #2474) +- Improved reliability of signal stream starts with retries (#2414) +- Use Deque instead of channels in internal communications to reduce memory usage. (#2418 #2419) +- Do not synthesise DISCONNECT on session change. (#2412) +- Prometheus: larger buckets for jitter histogram (#2468) +- Support for improved Ingress internal RPC (#2485) +- Let track events go through after participant close. (#2487) + +### Removed +- Removed code related to legacy (pre 1.5.x) RPC protocol (#2384 #2385) + ## [1.5.2] - 2023-12-21 Support for LiveKit SIP Bridge diff --git a/config-sample.yaml b/config-sample.yaml index 0d001bd10..ae34d759f 100644 --- a/config-sample.yaml +++ b/config-sample.yaml @@ -1,3 +1,17 @@ +# Copyright 2024 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. + # main TCP port for RoomService and RTC endpoint # for production setups, this port should be placed behind a load balancer with TLS port: 7880 @@ -6,8 +20,6 @@ port: 7880 # clients could connect to any node and be routed to the same room redis: address: redis.host:6379 - # To require TLS transport - # use_tls: true # db: 0 # username: myuser # password: mypassword @@ -20,6 +32,17 @@ redis: # sentinel_username: user # sentinel_password: pass # + # To use TLS with redis + # tls: + # enabled: true + # # when set to true, LiveKit will not verify the server's certificate, defaults to true + # insecure: false + # server_name: myserver.com + # # file containing trusted root certificates for verification + # ca_cert_file: /path/to/ca.crt + # client_cert_file: /path/to/client.crt + # client_key_file: /path/to/client.key + # # To use cluster remove the address key above and add the following # cluster_addresses: # - livekit-redis-node-0.livekit-redis-headless:6379 diff --git a/go.mod b/go.mod index 42cd30c94..a26ffd656 100644 --- a/go.mod +++ b/go.mod @@ -19,7 +19,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-20240206082112-9bf41dcbce76 - github.com/livekit/protocol v1.9.10-0.20240214205753-2e75b8fb463f + github.com/livekit/protocol v1.9.10-0.20240217202122-51aba73c0582 github.com/livekit/psrpc v0.5.3-0.20240209001357-380f59f00c58 github.com/mackerelio/go-osstat v0.2.4 github.com/magefile/mage v1.15.0 diff --git a/go.sum b/go.sum index 210a2b9ad..3b4e5741e 100644 --- a/go.sum +++ b/go.sum @@ -127,8 +127,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-20240206082112-9bf41dcbce76 h1:Zw88krOHni51OzDUlrduYb3m7VcsaKw06TnnDhsQpjg= github.com/livekit/mediatransportutil v0.0.0-20240206082112-9bf41dcbce76/go.mod h1:GBzn9xL+mivI1pW+tyExcKgbc0VOc29I9yJsNcAVaAc= -github.com/livekit/protocol v1.9.10-0.20240214205753-2e75b8fb463f h1:f/KuQMFbWxKp43I2mftTLCWTwGnwtTE9FF7Btr78Uzc= -github.com/livekit/protocol v1.9.10-0.20240214205753-2e75b8fb463f/go.mod h1:/kviHT6yTNqHdZ9QsvRuxAHf7LaBROa7qe5naT1oVrU= +github.com/livekit/protocol v1.9.10-0.20240217202122-51aba73c0582 h1:hSOSQs2pKF6TD9CEwu7+LatqfvF/LiyIbeCoUPCGRho= +github.com/livekit/protocol v1.9.10-0.20240217202122-51aba73c0582/go.mod h1:/kviHT6yTNqHdZ9QsvRuxAHf7LaBROa7qe5naT1oVrU= github.com/livekit/psrpc v0.5.3-0.20240209001357-380f59f00c58 h1:yH55rBGLRO+ict2mu6bKZ5iPwTIrIwU1i0ydgThi4+k= github.com/livekit/psrpc v0.5.3-0.20240209001357-380f59f00c58/go.mod h1:cQjxg1oCxYHhxxv6KJH1gSvdtCHQoRZCHgPdm5N8v2g= github.com/mackerelio/go-osstat v0.2.4 h1:qxGbdPkFo65PXOb/F/nhDKpF2nGmGaCFDLXoZjJTtUs= diff --git a/pkg/rtc/transport/handler.go b/pkg/rtc/transport/handler.go index eab3de349..dbad2f5b6 100644 --- a/pkg/rtc/transport/handler.go +++ b/pkg/rtc/transport/handler.go @@ -1,3 +1,17 @@ +// Copyright 2024 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 transport import ( diff --git a/pkg/rtc/transport/negotiationstate.go b/pkg/rtc/transport/negotiationstate.go index 8f074f83f..26d6d1a2b 100644 --- a/pkg/rtc/transport/negotiationstate.go +++ b/pkg/rtc/transport/negotiationstate.go @@ -1,3 +1,17 @@ +// Copyright 2024 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 transport import "fmt" diff --git a/pkg/service/ioservice_ingress.go b/pkg/service/ioservice_ingress.go index cba5ab390..9e84e7e7c 100644 --- a/pkg/service/ioservice_ingress.go +++ b/pkg/service/ioservice_ingress.go @@ -1,3 +1,17 @@ +// Copyright 2024 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 ( diff --git a/pkg/sfu/buffer/frameintegrity.go b/pkg/sfu/buffer/frameintegrity.go index 1b712652e..bc6fe30a7 100644 --- a/pkg/sfu/buffer/frameintegrity.go +++ b/pkg/sfu/buffer/frameintegrity.go @@ -1,3 +1,17 @@ +// Copyright 2024 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 buffer import ( diff --git a/pkg/sfu/buffer/frameintegrity_test.go b/pkg/sfu/buffer/frameintegrity_test.go index 3e505efdd..144a9f0f2 100644 --- a/pkg/sfu/buffer/frameintegrity_test.go +++ b/pkg/sfu/buffer/frameintegrity_test.go @@ -1,3 +1,17 @@ +// Copyright 2024 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 buffer import ( diff --git a/pkg/sfu/interceptor/rtx.go b/pkg/sfu/interceptor/rtx.go index 624ede827..1b7f2c4e1 100644 --- a/pkg/sfu/interceptor/rtx.go +++ b/pkg/sfu/interceptor/rtx.go @@ -1,3 +1,17 @@ +// Copyright 2024 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 interceptor import ( diff --git a/pkg/sfu/playoutdelay.go b/pkg/sfu/playoutdelay.go index 2ac26a85b..cf95e3b5b 100644 --- a/pkg/sfu/playoutdelay.go +++ b/pkg/sfu/playoutdelay.go @@ -1,3 +1,17 @@ +// Copyright 2024 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 sfu import ( diff --git a/pkg/sfu/playoutdelay_test.go b/pkg/sfu/playoutdelay_test.go index 190891034..05a88f5d0 100644 --- a/pkg/sfu/playoutdelay_test.go +++ b/pkg/sfu/playoutdelay_test.go @@ -1,3 +1,17 @@ +// Copyright 2024 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 sfu import ( diff --git a/pkg/sfu/rtpextension/playoutdelay.go b/pkg/sfu/rtpextension/playoutdelay.go index 500bb67ea..2e311a621 100644 --- a/pkg/sfu/rtpextension/playoutdelay.go +++ b/pkg/sfu/rtpextension/playoutdelay.go @@ -1,3 +1,17 @@ +// Copyright 2024 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 rtpextension import ( diff --git a/pkg/sfu/rtpextension/playoutdelay_test.go b/pkg/sfu/rtpextension/playoutdelay_test.go index 7262a6475..7c07ed26c 100644 --- a/pkg/sfu/rtpextension/playoutdelay_test.go +++ b/pkg/sfu/rtpextension/playoutdelay_test.go @@ -1,3 +1,17 @@ +// Copyright 2024 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 rtpextension import ( diff --git a/pkg/sfu/videolayerselector/framenumberwrapper.go b/pkg/sfu/videolayerselector/framenumberwrapper.go index d2821db80..1820d6c41 100644 --- a/pkg/sfu/videolayerselector/framenumberwrapper.go +++ b/pkg/sfu/videolayerselector/framenumberwrapper.go @@ -1,3 +1,17 @@ +// Copyright 2024 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 videolayerselector import "github.com/livekit/protocol/logger" diff --git a/pkg/sfu/videolayerselector/framenumberwrapper_test.go b/pkg/sfu/videolayerselector/framenumberwrapper_test.go index 361a7de4c..6fa3a724c 100644 --- a/pkg/sfu/videolayerselector/framenumberwrapper_test.go +++ b/pkg/sfu/videolayerselector/framenumberwrapper_test.go @@ -1,3 +1,17 @@ +// Copyright 2024 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 videolayerselector import ( diff --git a/test/agent.go b/test/agent.go index 65b4952b6..cc04cad84 100644 --- a/test/agent.go +++ b/test/agent.go @@ -1,3 +1,17 @@ +// Copyright 2024 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 test import ( diff --git a/version/version.go b/version/version.go index e821eae6b..17d23954c 100644 --- a/version/version.go +++ b/version/version.go @@ -14,4 +14,4 @@ package version -const Version = "1.5.2" +const Version = "1.5.3" From 262b160464dde929918e00947870103fc07dcb55 Mon Sep 17 00:00:00 2001 From: Raja Subramanian Date: Mon, 19 Feb 2024 09:02:01 +0530 Subject: [PATCH 41/44] Simplify migrate complete a bit more. (#2491) Moving handling of migrated tracks to when the migration state moves to completed. Pending data channel were already happening only on complete. Move tracks also to that point. Handling it earlier meant that track published callback happened and ownership of track moved to new node before the new node could finish peer connection. So, in cases where migration did not go through, this caused confusion of track ownership. --- pkg/rtc/participant.go | 27 +++++++-------------------- pkg/rtc/participant_signal.go | 8 +++++++- 2 files changed, 14 insertions(+), 21 deletions(-) diff --git a/pkg/rtc/participant.go b/pkg/rtc/participant.go index acf326311..87201780a 100644 --- a/pkg/rtc/participant.go +++ b/pkg/rtc/participant.go @@ -202,8 +202,8 @@ type ParticipantImpl struct { lock utils.RWMutex - dirty atomic.Bool - version atomic.Uint32 + dirty atomic.Bool + version atomic.Uint32 // callbacks & handlers onTrackPublished func(types.LocalParticipant, types.MediaTrack) @@ -654,18 +654,11 @@ func (p *ParticipantImpl) onPublisherAnswer(answer webrtc.SessionDescription) er p.pubLogger.Debugw("sending answer", "transport", livekit.SignalTarget_PUBLISHER) answer = p.configurePublisherAnswer(answer) - if err := p.writeMessage(&livekit.SignalResponse{ + return p.writeMessage(&livekit.SignalResponse{ Message: &livekit.SignalResponse_Answer{ Answer: ToProtoSessionDescription(answer), }, - }); err != nil { - return err - } - - if p.MigrateState() == types.MigrateStateSync { - go p.handleMigrateTracks() - } - return nil + }) } func (p *ParticipantImpl) handleMigrateTracks() { @@ -910,6 +903,7 @@ func (p *ParticipantImpl) SetMigrateState(s types.MigrateState) { p.TransportManager.ProcessPendingPublisherOffer() case types.MigrateStateComplete: + p.handleMigrateTracks() p.TransportManager.ProcessPendingPublisherDataChannels() } @@ -1482,9 +1476,7 @@ func (p *ParticipantImpl) onICECandidate(c *webrtc.ICECandidate, target livekit. } func (p *ParticipantImpl) onPublisherInitialConnected() { - if !p.hasPendingMigratedTrack() { - p.SetMigrateState(types.MigrateStateComplete) - } + p.SetMigrateState(types.MigrateStateComplete) if p.supervisor != nil { p.supervisor.SetPublisherPeerConnectionConnected(true) @@ -1533,8 +1525,7 @@ func (p *ParticipantImpl) setupDisconnectTimer() { if p.IsClosed() || p.IsDisconnected() { return } - reason := types.ParticipantCloseReasonPeerConnectionDisconnected - _ = p.Close(true, reason, false) + _ = p.Close(true, types.ParticipantCloseReasonPeerConnectionDisconnected, false) }) p.lock.Unlock() } @@ -2106,10 +2097,6 @@ func (p *ParticipantImpl) handleTrackPublished(track types.MediaTrack) { p.pendingTracksLock.Lock() delete(p.pendingPublishingTracks, track.ID()) p.pendingTracksLock.Unlock() - - if !p.hasPendingMigratedTrack() && p.TransportManager.HasPublisherEverConnected() { - p.SetMigrateState(types.MigrateStateComplete) - } } func (p *ParticipantImpl) hasPendingMigratedTrack() bool { diff --git a/pkg/rtc/participant_signal.go b/pkg/rtc/participant_signal.go index 63e944e41..d4dbafa35 100644 --- a/pkg/rtc/participant_signal.go +++ b/pkg/rtc/participant_signal.go @@ -100,7 +100,13 @@ func (p *ParticipantImpl) SendParticipantUpdate(participantsToUpdate []*livekit. // this is a message delivered out of order, a more recent version of the message had already been // sent. if pi.Version < lastVersion.version { - p.params.Logger.Debugw("skipping outdated participant update", "otherParticipant", pi.Identity, "otherPID", pi.Sid, "version", pi.Version, "lastVersion", lastVersion) + p.params.Logger.Debugw( + "skipping outdated participant update", + "otherParticipant", pi.Identity, + "otherPID", pi.Sid, + "version", pi.Version, + "lastVersion", lastVersion, + ) isValid = false } } From 1e2da70122e26204423880aab05531ccf5ed7618 Mon Sep 17 00:00:00 2001 From: Raja Subramanian Date: Mon, 19 Feb 2024 10:19:55 +0530 Subject: [PATCH 42/44] Filter out context canceled errors from logging (#2492) * Filter out context canceled errors from logging * move a few more warns to debug --- pkg/rtc/participant.go | 6 +++--- pkg/service/utils.go | 6 +++++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/pkg/rtc/participant.go b/pkg/rtc/participant.go index 87201780a..247fd454d 100644 --- a/pkg/rtc/participant.go +++ b/pkg/rtc/participant.go @@ -1864,7 +1864,7 @@ func (p *ParticipantImpl) setTrackMuted(trackID livekit.TrackID, muted bool) *li } if !isPending && track == nil { - p.pubLogger.Warnw("could not locate track", nil, "trackID", trackID) + p.pubLogger.Debugw("could not locate track", "trackID", trackID) } return trackInfo @@ -2379,7 +2379,7 @@ func (p *ParticipantImpl) onAnyTransportNegotiationFailed() { func (p *ParticipantImpl) UpdateSubscribedQuality(nodeID livekit.NodeID, trackID livekit.TrackID, maxQualities []types.SubscribedCodecQuality) error { track := p.GetPublishedTrack(trackID) if track == nil { - p.pubLogger.Warnw("could not find track", nil, "trackID", trackID) + p.pubLogger.Debugw("could not find track", "trackID", trackID) return errors.New("could not find published track") } @@ -2390,7 +2390,7 @@ func (p *ParticipantImpl) UpdateSubscribedQuality(nodeID livekit.NodeID, trackID func (p *ParticipantImpl) UpdateMediaLoss(nodeID livekit.NodeID, trackID livekit.TrackID, fractionalLoss uint32) error { track := p.GetPublishedTrack(trackID) if track == nil { - p.pubLogger.Warnw("could not find track", nil, "trackID", trackID) + p.pubLogger.Debugw("could not find track", "trackID", trackID) return errors.New("could not find published track") } diff --git a/pkg/service/utils.go b/pkg/service/utils.go index 2d1bafd4e..7ce0e7bc4 100644 --- a/pkg/service/utils.go +++ b/pkg/service/utils.go @@ -15,6 +15,8 @@ package service import ( + "context" + "errors" "net" "net/http" "regexp" @@ -27,7 +29,9 @@ func handleError(w http.ResponseWriter, r *http.Request, status int, err error, if r != nil && r.URL != nil { keysAndValues = append(keysAndValues, "method", r.Method, "path", r.URL.Path) } - logger.GetLogger().WithCallDepth(1).Warnw("error handling request", err, keysAndValues...) + if !errors.Is(err, context.Canceled) { + logger.GetLogger().WithCallDepth(1).Warnw("error handling request", err, keysAndValues...) + } w.WriteHeader(status) _, _ = w.Write([]byte(err.Error())) } From e4acf028532e30b241e27b801ec0144ec4d9e5f3 Mon Sep 17 00:00:00 2001 From: Raja Subramanian Date: Mon, 19 Feb 2024 13:30:54 +0530 Subject: [PATCH 43/44] Include pending migrate tracks in ParticipantInfo. (#2493) It is possible that participant state and migration state updates are racing. And a participant update could end up with no tracks when migration is being processed. --- pkg/rtc/participant.go | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/pkg/rtc/participant.go b/pkg/rtc/participant.go index 247fd454d..74e27dc2f 100644 --- a/pkg/rtc/participant.go +++ b/pkg/rtc/participant.go @@ -514,8 +514,37 @@ func (p *ParticipantImpl) ToProtoWithVersion() (*livekit.ParticipantInfo, utils. IsPublisher: p.IsPublisher(), } p.lock.RUnlock() + + p.pendingTracksLock.RLock() pi.Tracks = p.UpTrackManager.ToProto() + // add any pending migrating tracks, else an update could delete/unsubscribe from yet to be published, migrating tracks + maybeAdd := func(pti *pendingTrackInfo) { + if !pti.migrated { + return + } + + found := false + for _, ti := range pi.Tracks { + if ti.Sid == pti.trackInfos[0].Sid { + found = true + break + } + } + + if !found { + pi.Tracks = append(pi.Tracks, pti.trackInfos[0]) + } + } + + for _, pt := range p.pendingTracks { + maybeAdd(pt) + } + for _, ppt := range p.pendingPublishingTracks { + maybeAdd(ppt) + } + p.pendingTracksLock.RUnlock() + return pi, piv } @@ -748,6 +777,10 @@ func (p *ParticipantImpl) SetMigrateInfo( } p.pendingTracksLock.Unlock() + if len(mediaTracks) != 0 { + p.setIsPublisher(true) + } + p.TransportManager.SetMigrateInfo(previousOffer, previousAnswer, dataChannels) } From 91c320770bfdca0d2c240b2df9955241613913d7 Mon Sep 17 00:00:00 2001 From: cnderrauber Date: Tue, 20 Feb 2024 11:57:38 +0800 Subject: [PATCH 44/44] Replace reflect.Equal with generic sliceEqual (#2494) * Replace reflect.Equal with generic sliceEqual * Use library function --- .../dependencydescriptorwriter.go | 22 ++++--------------- 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/pkg/sfu/dependencydescriptor/dependencydescriptorwriter.go b/pkg/sfu/dependencydescriptor/dependencydescriptorwriter.go index bd7da49e3..d392c90a3 100644 --- a/pkg/sfu/dependencydescriptor/dependencydescriptorwriter.go +++ b/pkg/sfu/dependencydescriptor/dependencydescriptorwriter.go @@ -16,7 +16,8 @@ package dependencydescriptor import ( "fmt" - "reflect" + + "golang.org/x/exp/slices" ) type TemplateMatch struct { @@ -126,26 +127,11 @@ func (w *DependencyDescriptorWriter) findBestTemplate() error { return nil } -// TODO: uncomment this when go 1.18 enabled -// func sliceEqual[T comparable](a, b []T) bool { -// if len(a) != len(b) { -// return false -// } - -// for i, v := range a { -// if v != b[i] { -// return false -// } -// } - -// return true -// } - func (w *DependencyDescriptorWriter) calculateMatch(idx int, template *FrameDependencyTemplate) TemplateMatch { var result TemplateMatch result.TemplateIdx = idx - result.NeedCustomFdiffs = w.descriptor.FrameDependencies.FrameDiffs != nil && !reflect.DeepEqual(w.descriptor.FrameDependencies.FrameDiffs, template.FrameDiffs) - result.NeedCustomDtis = w.descriptor.FrameDependencies.DecodeTargetIndications != nil && !reflect.DeepEqual(w.descriptor.FrameDependencies.DecodeTargetIndications, template.DecodeTargetIndications) + result.NeedCustomFdiffs = w.descriptor.FrameDependencies.FrameDiffs != nil && !slices.Equal(w.descriptor.FrameDependencies.FrameDiffs, template.FrameDiffs) + result.NeedCustomDtis = w.descriptor.FrameDependencies.DecodeTargetIndications != nil && !slices.Equal(w.descriptor.FrameDependencies.DecodeTargetIndications, template.DecodeTargetIndications) for i := 0; i < w.structure.NumChains; i++ { if w.activeChains&(1<