Files
livekit/pkg/sfu/buffer/videolayerutils.go
Raja Subramanian faff67162b Consolidate TrackInfo. (#2331)
* Consolidate TrackInfo.

TrackInfo was spread across a bit. Consolidating it.

* TODO comments

* test

* update TrackInfo on SSRC change

* further consolidation

* log mimes only

* update receivers on SSRC set

* clone proto on return

* feedback: break loop on mime match

* prevent data race
2023-12-21 09:56:54 +05:30

327 lines
9.7 KiB
Go

// Copyright 2023 LiveKit, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package buffer
import (
"github.com/livekit/protocol/livekit"
"github.com/livekit/protocol/logger"
)
const (
QuarterResolution = "q"
HalfResolution = "h"
FullResolution = "f"
)
// SIMULCAST-CODEC-TODO: these need to be codec mime aware if and when each codec suppports different layers
func LayerPresenceFromTrackInfo(trackInfo *livekit.TrackInfo) *[livekit.VideoQuality_HIGH + 1]bool {
if trackInfo == nil || len(trackInfo.Layers) == 0 {
return nil
}
var layerPresence [livekit.VideoQuality_HIGH + 1]bool
for _, layer := range trackInfo.Layers {
// WARNING: comparing protobuf enum
if layer.Quality <= livekit.VideoQuality_HIGH {
layerPresence[layer.Quality] = true
} else {
logger.Warnw("unexpected quality in track info", nil, "trackID", trackInfo.Sid, "trackInfo", logger.Proto(trackInfo))
}
}
return &layerPresence
}
func RidToSpatialLayer(rid string, trackInfo *livekit.TrackInfo) int32 {
lp := LayerPresenceFromTrackInfo(trackInfo)
if lp == nil {
switch rid {
case QuarterResolution:
return 0
case HalfResolution:
return 1
case FullResolution:
return 2
default:
return 0
}
}
switch rid {
case QuarterResolution:
switch {
case lp[livekit.VideoQuality_LOW] && lp[livekit.VideoQuality_MEDIUM] && lp[livekit.VideoQuality_HIGH]:
fallthrough
case lp[livekit.VideoQuality_LOW] && lp[livekit.VideoQuality_MEDIUM]:
fallthrough
case lp[livekit.VideoQuality_LOW] && lp[livekit.VideoQuality_HIGH]:
fallthrough
case lp[livekit.VideoQuality_MEDIUM] && lp[livekit.VideoQuality_HIGH]:
return 0
default:
// only one quality published, could be any
return 0
}
case HalfResolution:
switch {
case lp[livekit.VideoQuality_LOW] && lp[livekit.VideoQuality_MEDIUM] && lp[livekit.VideoQuality_HIGH]:
fallthrough
case lp[livekit.VideoQuality_LOW] && lp[livekit.VideoQuality_MEDIUM]:
fallthrough
case lp[livekit.VideoQuality_LOW] && lp[livekit.VideoQuality_HIGH]:
fallthrough
case lp[livekit.VideoQuality_MEDIUM] && lp[livekit.VideoQuality_HIGH]:
return 1
default:
// only one quality published, could be any
return 0
}
case FullResolution:
switch {
case lp[livekit.VideoQuality_LOW] && lp[livekit.VideoQuality_MEDIUM] && lp[livekit.VideoQuality_HIGH]:
return 2
case lp[livekit.VideoQuality_LOW] && lp[livekit.VideoQuality_MEDIUM]:
logger.Warnw("unexpected rid f with only two qualities, low and medium", nil, "trackID", trackInfo.Sid, "trackInfo", logger.Proto(trackInfo))
return 1
case lp[livekit.VideoQuality_LOW] && lp[livekit.VideoQuality_HIGH]:
logger.Warnw("unexpected rid f with only two qualities, low and high", nil, "trackID", trackInfo.Sid, "trackInfo", logger.Proto(trackInfo))
return 1
case lp[livekit.VideoQuality_MEDIUM] && lp[livekit.VideoQuality_HIGH]:
logger.Warnw("unexpected rid f with only two qualities, medium and high", nil, "trackID", trackInfo.Sid, "trackInfo", logger.Proto(trackInfo))
return 1
default:
// only one quality published, could be any
return 0
}
default:
// no rid, should be single layer
return 0
}
}
func SpatialLayerToRid(layer int32, trackInfo *livekit.TrackInfo) string {
lp := LayerPresenceFromTrackInfo(trackInfo)
if lp == nil {
switch layer {
case 0:
return QuarterResolution
case 1:
return HalfResolution
case 2:
return FullResolution
default:
return QuarterResolution
}
}
switch layer {
case 0:
switch {
case lp[livekit.VideoQuality_LOW] && lp[livekit.VideoQuality_MEDIUM] && lp[livekit.VideoQuality_HIGH]:
fallthrough
case lp[livekit.VideoQuality_LOW] && lp[livekit.VideoQuality_MEDIUM]:
fallthrough
case lp[livekit.VideoQuality_LOW] && lp[livekit.VideoQuality_HIGH]:
fallthrough
case lp[livekit.VideoQuality_MEDIUM] && lp[livekit.VideoQuality_HIGH]:
return QuarterResolution
default:
return QuarterResolution
}
case 1:
switch {
case lp[livekit.VideoQuality_LOW] && lp[livekit.VideoQuality_MEDIUM] && lp[livekit.VideoQuality_HIGH]:
fallthrough
case lp[livekit.VideoQuality_LOW] && lp[livekit.VideoQuality_MEDIUM]:
fallthrough
case lp[livekit.VideoQuality_LOW] && lp[livekit.VideoQuality_HIGH]:
fallthrough
case lp[livekit.VideoQuality_MEDIUM] && lp[livekit.VideoQuality_HIGH]:
return HalfResolution
default:
return QuarterResolution
}
case 2:
switch {
case lp[livekit.VideoQuality_LOW] && lp[livekit.VideoQuality_MEDIUM] && lp[livekit.VideoQuality_HIGH]:
return FullResolution
case lp[livekit.VideoQuality_LOW] && lp[livekit.VideoQuality_MEDIUM]:
logger.Warnw("unexpected layer 2 with only two qualities, low and medium", nil, "trackID", trackInfo.Sid, "trackInfo", logger.Proto(trackInfo))
return HalfResolution
case lp[livekit.VideoQuality_LOW] && lp[livekit.VideoQuality_HIGH]:
logger.Warnw("unexpected layer 2 with only two qualities, low and high", nil, "trackID", trackInfo.Sid, "trackInfo", logger.Proto(trackInfo))
return HalfResolution
case lp[livekit.VideoQuality_MEDIUM] && lp[livekit.VideoQuality_HIGH]:
logger.Warnw("unexpected layer 2 with only two qualities, medium and high", nil, "trackID", trackInfo.Sid, "trackInfo", logger.Proto(trackInfo))
return HalfResolution
default:
return QuarterResolution
}
default:
return QuarterResolution
}
}
func VideoQualityToRid(quality livekit.VideoQuality, trackInfo *livekit.TrackInfo) string {
return SpatialLayerToRid(VideoQualityToSpatialLayer(quality, trackInfo), trackInfo)
}
func SpatialLayerToVideoQuality(layer int32, trackInfo *livekit.TrackInfo) livekit.VideoQuality {
lp := LayerPresenceFromTrackInfo(trackInfo)
if lp == nil {
switch layer {
case 0:
return livekit.VideoQuality_LOW
case 1:
return livekit.VideoQuality_MEDIUM
case 2:
return livekit.VideoQuality_HIGH
default:
return livekit.VideoQuality_OFF
}
}
switch layer {
case 0:
switch {
case lp[livekit.VideoQuality_LOW] && lp[livekit.VideoQuality_MEDIUM] && lp[livekit.VideoQuality_HIGH]:
fallthrough
case lp[livekit.VideoQuality_LOW] && lp[livekit.VideoQuality_MEDIUM]:
fallthrough
case lp[livekit.VideoQuality_LOW] && lp[livekit.VideoQuality_HIGH]:
fallthrough
case lp[livekit.VideoQuality_LOW]:
return livekit.VideoQuality_LOW
case lp[livekit.VideoQuality_MEDIUM] && lp[livekit.VideoQuality_HIGH]:
fallthrough
case lp[livekit.VideoQuality_MEDIUM]:
return livekit.VideoQuality_MEDIUM
default:
return livekit.VideoQuality_HIGH
}
case 1:
switch {
case lp[livekit.VideoQuality_LOW] && lp[livekit.VideoQuality_MEDIUM] && lp[livekit.VideoQuality_HIGH]:
fallthrough
case lp[livekit.VideoQuality_LOW] && lp[livekit.VideoQuality_MEDIUM]:
return livekit.VideoQuality_MEDIUM
case lp[livekit.VideoQuality_LOW] && lp[livekit.VideoQuality_HIGH]:
fallthrough
case lp[livekit.VideoQuality_MEDIUM] && lp[livekit.VideoQuality_HIGH]:
return livekit.VideoQuality_HIGH
default:
logger.Errorw("invalid layer", nil, "trackID", trackInfo.Sid, "layer", layer, "trackInfo", logger.Proto(trackInfo))
return livekit.VideoQuality_HIGH
}
case 2:
switch {
case lp[livekit.VideoQuality_LOW] && lp[livekit.VideoQuality_MEDIUM] && lp[livekit.VideoQuality_HIGH]:
return livekit.VideoQuality_HIGH
default:
logger.Errorw("invalid layer", nil, "trackID", trackInfo.Sid, "layer", layer, "trackInfo", logger.Proto(trackInfo))
return livekit.VideoQuality_HIGH
}
}
return livekit.VideoQuality_OFF
}
func VideoQualityToSpatialLayer(quality livekit.VideoQuality, trackInfo *livekit.TrackInfo) int32 {
lp := LayerPresenceFromTrackInfo(trackInfo)
if lp == nil {
switch quality {
case livekit.VideoQuality_LOW:
return 0
case livekit.VideoQuality_MEDIUM:
return 1
case livekit.VideoQuality_HIGH:
return 2
default:
return InvalidLayerSpatial
}
}
switch quality {
case livekit.VideoQuality_LOW:
switch {
case lp[livekit.VideoQuality_LOW] && lp[livekit.VideoQuality_MEDIUM] && lp[livekit.VideoQuality_HIGH]:
fallthrough
case lp[livekit.VideoQuality_LOW] && lp[livekit.VideoQuality_MEDIUM]:
fallthrough
case lp[livekit.VideoQuality_LOW] && lp[livekit.VideoQuality_HIGH]:
fallthrough
case lp[livekit.VideoQuality_MEDIUM] && lp[livekit.VideoQuality_HIGH]:
fallthrough
default: // only one quality published, could be any
return 0
}
case livekit.VideoQuality_MEDIUM:
switch {
case lp[livekit.VideoQuality_LOW] && lp[livekit.VideoQuality_MEDIUM] && lp[livekit.VideoQuality_HIGH]:
fallthrough
case lp[livekit.VideoQuality_LOW] && lp[livekit.VideoQuality_MEDIUM]:
fallthrough
case lp[livekit.VideoQuality_LOW] && lp[livekit.VideoQuality_HIGH]:
return 1
case lp[livekit.VideoQuality_MEDIUM] && lp[livekit.VideoQuality_HIGH]:
return 0
default: // only one quality published, could be any
return 0
}
case livekit.VideoQuality_HIGH:
switch {
case lp[livekit.VideoQuality_LOW] && lp[livekit.VideoQuality_MEDIUM] && lp[livekit.VideoQuality_HIGH]:
return 2
case lp[livekit.VideoQuality_LOW] && lp[livekit.VideoQuality_MEDIUM]:
fallthrough
case lp[livekit.VideoQuality_LOW] && lp[livekit.VideoQuality_HIGH]:
fallthrough
case lp[livekit.VideoQuality_MEDIUM] && lp[livekit.VideoQuality_HIGH]:
return 1
default: // only one quality published, could be any
return 0
}
}
return InvalidLayerSpatial
}