diff --git a/pkg/config/config.go b/pkg/config/config.go index cea2c4488..c6b4d6fb4 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -140,8 +140,23 @@ type AudioConfig struct { SmoothIntervals uint32 `yaml:"smooth_intervals"` } +type StreamTrackerConfig struct { + SamplesRequired uint32 `yaml:"samples_required"` + CyclesRequired uint32 `yaml:"cycles_required"` + CycleDuration time.Duration `yaml:"cycle_duration"` + BitrateReportInterval time.Duration `yaml:"bitrate_report_interval"` +} + +type StreamTrackersConfig struct { + Video []StreamTrackerConfig `yaml:"video"` + Screenshare []StreamTrackerConfig `yaml:"screenshare"` + ExemptedLayersVideo []int32 `yaml:"exempted_layers_video"` + ExemptedLayersScreenshare []int32 `yaml:"exempted_layers_screenshare"` +} + type VideoConfig struct { - DynacastPauseDelay time.Duration `yaml:"dynacast_pause_delay,omitempty"` + DynacastPauseDelay time.Duration `yaml:"dynacast_pause_delay,omitempty"` + StreamTracker StreamTrackersConfig `yaml:"stream_tracker,omitempty"` } type RoomConfig struct { @@ -254,6 +269,50 @@ func NewConfig(confString string, strictMode bool, c *cli.Context, baseFlags []c }, Video: VideoConfig{ DynacastPauseDelay: 5 * time.Second, + StreamTracker: StreamTrackersConfig{ + ExemptedLayersScreenshare: []int32{0}, + ExemptedLayersVideo: []int32{}, + Screenshare: []StreamTrackerConfig{ + { + SamplesRequired: 1, + CyclesRequired: 1, + CycleDuration: 2 * time.Second, + BitrateReportInterval: 4 * time.Second, + }, + { + SamplesRequired: 1, + CyclesRequired: 1, + CycleDuration: 2 * time.Second, + BitrateReportInterval: 4 * time.Second, + }, + { + SamplesRequired: 1, + CyclesRequired: 1, + CycleDuration: 2 * time.Second, + BitrateReportInterval: 4 * time.Second, + }, + }, + Video: []StreamTrackerConfig{ + { + SamplesRequired: 1, + CyclesRequired: 4, + CycleDuration: 500 * time.Millisecond, + BitrateReportInterval: 1 * time.Second, + }, + { + SamplesRequired: 5, + CyclesRequired: 20, + CycleDuration: 500 * time.Millisecond, + BitrateReportInterval: 1 * time.Second, + }, + { + SamplesRequired: 5, + CyclesRequired: 20, + CycleDuration: 500 * time.Millisecond, + BitrateReportInterval: 1 * time.Second, + }, + }, + }, }, Redis: redisLiveKit.RedisConfig{}, Room: RoomConfig{ diff --git a/pkg/rtc/mediatrack.go b/pkg/rtc/mediatrack.go index e2a0b6229..eedfa6184 100644 --- a/pkg/rtc/mediatrack.go +++ b/pkg/rtc/mediatrack.go @@ -234,6 +234,7 @@ func (t *MediaTrack) AddReceiver(receiver *webrtc.RTPReceiver, track *webrtc.Tra t.params.TrackInfo, LoggerWithCodecMime(t.params.Logger, mime), twcc, + t.params.VideoConfig.StreamTracker, sfu.WithPliThrottleConfig(t.params.PLIThrottleConfig), sfu.WithAudioConfig(t.params.AudioConfig), sfu.WithLoadBalanceThreshold(20), diff --git a/pkg/sfu/receiver.go b/pkg/sfu/receiver.go index 5118506f6..f8aec6a67 100644 --- a/pkg/sfu/receiver.go +++ b/pkg/sfu/receiver.go @@ -171,6 +171,7 @@ func NewWebRTCReceiver( trackInfo *livekit.TrackInfo, logger logger.Logger, twcc *twcc.Responder, + trackerConfig config.StreamTrackersConfig, opts ...ReceiverOpts, ) *WebRTCReceiver { w := &WebRTCReceiver{ @@ -188,7 +189,7 @@ func NewWebRTCReceiver( isRED: IsRedCodec(track.Codec().MimeType), } - w.streamTrackerManager = NewStreamTrackerManager(logger, trackInfo, w.isSVC) + w.streamTrackerManager = NewStreamTrackerManager(logger, trackInfo, w.isSVC, trackerConfig) w.streamTrackerManager.OnAvailableLayersChanged(w.downTrackLayerChange) w.streamTrackerManager.OnBitrateAvailabilityChanged(w.downTrackBitrateAvailabilityChange) diff --git a/pkg/sfu/streamtrackermanager.go b/pkg/sfu/streamtrackermanager.go index 1c70ce830..28cd5f295 100644 --- a/pkg/sfu/streamtrackermanager.go +++ b/pkg/sfu/streamtrackermanager.go @@ -3,69 +3,24 @@ package sfu import ( "sort" "sync" - "time" + "github.com/livekit/livekit-server/pkg/config" "github.com/livekit/livekit-server/pkg/sfu/buffer" "github.com/livekit/protocol/livekit" "github.com/livekit/protocol/logger" ) -var ( - ExemptedLayersScreenshare = []int32{0} - ExemptedLayersVideo = []int32{} -) - -var ( - ConfigVideo = []StreamTrackerParams{ - { - SamplesRequired: 1, - CyclesRequired: 4, - CycleDuration: 500 * time.Millisecond, - BitrateReportInterval: 1 * time.Second, - }, - { - SamplesRequired: 5, - CyclesRequired: 20, - CycleDuration: 500 * time.Millisecond, - BitrateReportInterval: 1 * time.Second, - }, - { - SamplesRequired: 5, - CyclesRequired: 20, - CycleDuration: 500 * time.Millisecond, - BitrateReportInterval: 1 * time.Second, - }, - } - - // be very forgiving for screen share to account for cases like static screen where there could be only one packet per second - ConfigScreenshare = []StreamTrackerParams{ - { - SamplesRequired: 1, - CyclesRequired: 1, - CycleDuration: 2 * time.Second, - BitrateReportInterval: 4 * time.Second, - }, - { - SamplesRequired: 1, - CyclesRequired: 1, - CycleDuration: 2 * time.Second, - BitrateReportInterval: 4 * time.Second, - }, - { - SamplesRequired: 1, - CyclesRequired: 1, - CycleDuration: 2 * time.Second, - BitrateReportInterval: 4 * time.Second, - }, - } -) - type StreamTrackerManager struct { logger logger.Logger trackInfo *livekit.TrackInfo isSVC bool maxPublishedLayer int32 + configVideo []StreamTrackerParams + configScreenshare []StreamTrackerParams + exemptedLayersVideo []int32 + exemptedLayersScreenshare []int32 + lock sync.RWMutex trackers [DefaultMaxLayerSpatial + 1]*StreamTracker @@ -80,12 +35,32 @@ type StreamTrackerManager struct { onMaxLayerChanged func(maxLayer int32) } -func NewStreamTrackerManager(logger logger.Logger, trackInfo *livekit.TrackInfo, isSVC bool) *StreamTrackerManager { +func NewStreamTrackerManager(logger logger.Logger, trackInfo *livekit.TrackInfo, isSVC bool, trackerConfig config.StreamTrackersConfig) *StreamTrackerManager { s := &StreamTrackerManager{ - logger: logger, - trackInfo: trackInfo, - isSVC: isSVC, - maxPublishedLayer: 0, + logger: logger, + trackInfo: trackInfo, + isSVC: isSVC, + maxPublishedLayer: 0, + exemptedLayersVideo: trackerConfig.ExemptedLayersVideo, + exemptedLayersScreenshare: trackerConfig.ExemptedLayersScreenshare, + } + + for _, layer := range trackerConfig.Video { + s.configVideo = append(s.configVideo, StreamTrackerParams{ + SamplesRequired: layer.SamplesRequired, + CyclesRequired: layer.CyclesRequired, + CycleDuration: layer.CycleDuration, + BitrateReportInterval: layer.BitrateReportInterval, + }) + } + + for _, layer := range trackerConfig.Screenshare { + s.configScreenshare = append(s.configScreenshare, StreamTrackerParams{ + SamplesRequired: layer.SamplesRequired, + CyclesRequired: layer.CyclesRequired, + CycleDuration: layer.CycleDuration, + BitrateReportInterval: layer.BitrateReportInterval, + }) } for _, layer := range s.trackInfo.Layers { @@ -114,17 +89,17 @@ func (s *StreamTrackerManager) OnMaxLayerChanged(f func(maxLayer int32)) { func (s *StreamTrackerManager) AddTracker(layer int32) *StreamTracker { var params StreamTrackerParams if s.trackInfo.Source == livekit.TrackSource_SCREEN_SHARE { - if int(layer) >= len(ConfigScreenshare) { + if int(layer) >= len(s.configScreenshare) { return nil } - params = ConfigScreenshare[layer] + params = s.configScreenshare[layer] } else { - if int(layer) >= len(ConfigVideo) { + if int(layer) >= len(s.configVideo) { return nil } - params = ConfigVideo[layer] + params = s.configVideo[layer] } params.Logger = s.logger.WithValues("layer", layer) tracker := NewStreamTracker(params) @@ -420,9 +395,9 @@ func (s *StreamTrackerManager) removeAvailableLayer(layer int32) { exempt := false var sourceExemptedLayers []int32 if s.trackInfo.Source == livekit.TrackSource_SCREEN_SHARE { - sourceExemptedLayers = ExemptedLayersScreenshare + sourceExemptedLayers = s.exemptedLayersScreenshare } else { - sourceExemptedLayers = ExemptedLayersVideo + sourceExemptedLayers = s.exemptedLayersVideo } for _, l := range sourceExemptedLayers { if layer == l {