diff --git a/pkg/rtc/mediatrack.go b/pkg/rtc/mediatrack.go index d65935564..7ce11f3ba 100644 --- a/pkg/rtc/mediatrack.go +++ b/pkg/rtc/mediatrack.go @@ -188,6 +188,8 @@ func (t *MediaTrack) AddReceiver(receiver *webrtc.RTPReceiver, track *webrtc.Tra ) } + newWR.OnMaxLayerChange(t.OnMaxLayerChange) + t.buffer = buff t.MediaTrackReceiver.SetupReceiver(newWR, priority, mid) @@ -234,3 +236,17 @@ func (t *MediaTrack) SetRTT(rtt uint32) { func (t *MediaTrack) HasPendingCodec() bool { return len(t.params.TrackInfo.Codecs) > len(t.Receivers()) } + +func (t *MediaTrack) OnMaxLayerChange(maxLayer int32) { + ti := &livekit.TrackInfo{ + Sid: t.trackInfo.Sid, + Type: t.trackInfo.Type, + } + + if layer, ok := t.MediaTrackReceiver.layerDimensions[livekit.VideoQuality(maxLayer)]; ok { + ti.Layers = []*livekit.VideoLayer{{Quality: livekit.VideoQuality(maxLayer), Width: layer.Width, Height: layer.Height}} + } else if maxLayer == -1 { + ti.Layers = []*livekit.VideoLayer{{Quality: livekit.VideoQuality_OFF}} + } + t.params.Telemetry.TrackPublishedUpdate(context.Background(), t.PublisherID(), ti) +} diff --git a/pkg/sfu/receiver.go b/pkg/sfu/receiver.go index 9a4129678..601a407e3 100644 --- a/pkg/sfu/receiver.go +++ b/pkg/sfu/receiver.go @@ -97,6 +97,9 @@ type WebRTCReceiver struct { // update stats onStatsUpdate func(w *WebRTCReceiver, stat *livekit.AnalyticsStat) + + // update layer info + onMaxLayerChange func(maxLayer int32) } func RidToLayer(rid string) int32 { @@ -182,6 +185,7 @@ func NewWebRTCReceiver( isSVC: IsSvcCodec(track.Codec().MimeType), } + w.streamTrackerManager.OnMaxLayerChanged(w.onMaxLayerChange) w.streamTrackerManager.OnAvailableLayersChanged(w.downTrackLayerChange) w.streamTrackerManager.OnBitrateAvailabilityChanged(w.downTrackBitrateAvailabilityChange) @@ -259,6 +263,10 @@ func (w *WebRTCReceiver) OnStatsUpdate(fn func(w *WebRTCReceiver, stat *livekit. w.onStatsUpdate = fn } +func (w *WebRTCReceiver) OnMaxLayerChange(fn func(maxLayer int32)) { + w.streamTrackerManager.OnMaxLayerChanged(fn) +} + func (w *WebRTCReceiver) GetConnectionScore() float32 { return w.connectionStats.GetScore() } diff --git a/pkg/sfu/streamtrackermanager.go b/pkg/sfu/streamtrackermanager.go index 0a83ec818..d1ee3d45a 100644 --- a/pkg/sfu/streamtrackermanager.go +++ b/pkg/sfu/streamtrackermanager.go @@ -73,6 +73,7 @@ type StreamTrackerManager struct { onAvailableLayersChanged func(availableLayers []int32) onBitrateAvailabilityChanged func() + onMaxLayerChanged func(maxLayer int32) } func NewStreamTrackerManager(logger logger.Logger, source livekit.TrackSource) *StreamTrackerManager { @@ -91,6 +92,10 @@ func (s *StreamTrackerManager) OnBitrateAvailabilityChanged(f func()) { s.onBitrateAvailabilityChanged = f } +func (s *StreamTrackerManager) OnMaxLayerChanged(f func(maxLayer int32)) { + s.onMaxLayerChanged = f +} + func (s *StreamTrackerManager) AddTracker(layer int32) *StreamTracker { var params StreamTrackerParams if s.source == livekit.TrackSource_SCREEN_SHARE { @@ -318,10 +323,20 @@ func (s *StreamTrackerManager) addAvailableLayer(layer int32) { if s.onAvailableLayersChanged != nil { s.onAvailableLayersChanged(layers) } + + // check if new layer was the max layer + if layers[len(layers)-1] == layer && s.onMaxLayerChanged != nil { + s.onMaxLayerChanged(layer) + } } func (s *StreamTrackerManager) removeAvailableLayer(layer int32) { s.lock.Lock() + prevMaxLayer := InvalidLayerSpatial + if len(s.availableLayers) > 0 { + prevMaxLayer = s.availableLayers[len(s.availableLayers)-1] + } + newLayers := make([]int32, 0, DefaultMaxLayerSpatial+1) for _, l := range s.availableLayers { if l != layer { @@ -330,6 +345,11 @@ func (s *StreamTrackerManager) removeAvailableLayer(layer int32) { } sort.Slice(newLayers, func(i, j int) bool { return newLayers[i] < newLayers[j] }) s.availableLayers = newLayers + + curMaxLayer := InvalidLayerSpatial + if len(s.availableLayers) > 0 { + curMaxLayer = s.availableLayers[len(s.availableLayers)-1] + } s.lock.Unlock() s.logger.Debugw("available layers changed - layer gone", "removed", layer, "layers", newLayers) @@ -338,4 +358,9 @@ func (s *StreamTrackerManager) removeAvailableLayer(layer int32) { if s.onAvailableLayersChanged != nil { s.onAvailableLayersChanged(newLayers) } + + // if maxLayer was removed, send the new maxLayer + if curMaxLayer != prevMaxLayer && s.onMaxLayerChanged != nil { + s.onMaxLayerChanged(curMaxLayer) + } }