Smoother dynacast transition (#390)

This commit is contained in:
Raja Subramanian
2022-01-31 07:47:24 +05:30
committed by GitHub
parent a1f88faed1
commit 745939ec24
10 changed files with 159 additions and 144 deletions
+23 -10
View File
@@ -126,7 +126,7 @@ func TestSubscribedMaxQuality(t *testing.T) {
},
}})
mt.NotifySubscriberMaxQuality("s1", livekit.VideoQuality_HIGH)
mt.notifySubscriberMaxQuality("s1", livekit.VideoQuality_HIGH)
actualTrackID := livekit.TrackID("")
actualSubscribedQualities := make([]*livekit.SubscribedQuality, 0)
@@ -137,7 +137,7 @@ func TestSubscribedMaxQuality(t *testing.T) {
})
// mute all subscribers
mt.NotifySubscriberMaxQuality("s1", livekit.VideoQuality_OFF)
mt.notifySubscriberMaxQuality("s1", livekit.VideoQuality_OFF)
expectedSubscribedQualities := []*livekit.SubscribedQuality{
{Quality: livekit.VideoQuality_LOW, Enabled: false},
@@ -181,8 +181,8 @@ func TestSubscribedMaxQuality(t *testing.T) {
return nil
})
mt.NotifySubscriberMaxQuality("s1", livekit.VideoQuality_HIGH)
mt.NotifySubscriberMaxQuality("s2", livekit.VideoQuality_MEDIUM)
mt.notifySubscriberMaxQuality("s1", livekit.VideoQuality_HIGH)
mt.notifySubscriberMaxQuality("s2", livekit.VideoQuality_MEDIUM)
expectedSubscribedQualities := []*livekit.SubscribedQuality{
{Quality: livekit.VideoQuality_LOW, Enabled: true},
@@ -193,7 +193,7 @@ func TestSubscribedMaxQuality(t *testing.T) {
require.EqualValues(t, expectedSubscribedQualities, actualSubscribedQualities)
// "s1" dropping to MEDIUM should disable HIGH layer
mt.NotifySubscriberMaxQuality("s1", livekit.VideoQuality_MEDIUM)
mt.notifySubscriberMaxQuality("s1", livekit.VideoQuality_MEDIUM)
expectedSubscribedQualities = []*livekit.SubscribedQuality{
{Quality: livekit.VideoQuality_LOW, Enabled: true},
@@ -204,8 +204,8 @@ func TestSubscribedMaxQuality(t *testing.T) {
require.EqualValues(t, expectedSubscribedQualities, actualSubscribedQualities)
// "s1" and "s1" dropping to LOW should disable HIGH & MEDIUM
mt.NotifySubscriberMaxQuality("s1", livekit.VideoQuality_LOW)
mt.NotifySubscriberMaxQuality("s2", livekit.VideoQuality_LOW)
mt.notifySubscriberMaxQuality("s1", livekit.VideoQuality_LOW)
mt.notifySubscriberMaxQuality("s2", livekit.VideoQuality_LOW)
expectedSubscribedQualities = []*livekit.SubscribedQuality{
{Quality: livekit.VideoQuality_LOW, Enabled: true},
@@ -216,7 +216,7 @@ func TestSubscribedMaxQuality(t *testing.T) {
require.EqualValues(t, expectedSubscribedQualities, actualSubscribedQualities)
// muting "s2" only should not disable all qualities
mt.NotifySubscriberMaxQuality("s2", livekit.VideoQuality_OFF)
mt.notifySubscriberMaxQuality("s2", livekit.VideoQuality_OFF)
expectedSubscribedQualities = []*livekit.SubscribedQuality{
{Quality: livekit.VideoQuality_LOW, Enabled: true},
@@ -227,7 +227,7 @@ func TestSubscribedMaxQuality(t *testing.T) {
require.EqualValues(t, expectedSubscribedQualities, actualSubscribedQualities)
// muting "s1" also should disable all qualities
mt.NotifySubscriberMaxQuality("s1", livekit.VideoQuality_OFF)
mt.notifySubscriberMaxQuality("s1", livekit.VideoQuality_OFF)
expectedSubscribedQualities = []*livekit.SubscribedQuality{
{Quality: livekit.VideoQuality_LOW, Enabled: false},
@@ -238,12 +238,25 @@ func TestSubscribedMaxQuality(t *testing.T) {
require.EqualValues(t, expectedSubscribedQualities, actualSubscribedQualities)
// unmuting "s1" should enable previously set max quality
mt.NotifySubscriberMaxQuality("s1", livekit.VideoQuality_LOW)
mt.notifySubscriberMaxQuality("s1", livekit.VideoQuality_LOW)
expectedSubscribedQualities = []*livekit.SubscribedQuality{
{Quality: livekit.VideoQuality_LOW, Enabled: true},
{Quality: livekit.VideoQuality_MEDIUM, Enabled: false},
{Quality: livekit.VideoQuality_HIGH, Enabled: false},
}
require.Equal(t, livekit.TrackID("v1"), actualTrackID)
require.EqualValues(t, expectedSubscribedQualities, actualSubscribedQualities)
// a higher quality from a different node should trigger that quality
mt.NotifySubscriberNodeMaxQuality("n1", livekit.VideoQuality_HIGH)
expectedSubscribedQualities = []*livekit.SubscribedQuality{
{Quality: livekit.VideoQuality_LOW, Enabled: true},
{Quality: livekit.VideoQuality_MEDIUM, Enabled: true},
{Quality: livekit.VideoQuality_HIGH, Enabled: true},
}
require.Equal(t, livekit.TrackID("v1"), actualTrackID)
require.EqualValues(t, expectedSubscribedQualities, actualSubscribedQualities)
})
}
+15
View File
@@ -396,6 +396,21 @@ func SpatialLayerForQuality(quality livekit.VideoQuality) int32 {
}
}
func QualityForSpatialLayer(layer int32) livekit.VideoQuality {
switch layer {
case 0:
return livekit.VideoQuality_LOW
case 1:
return livekit.VideoQuality_MEDIUM
case 2:
return livekit.VideoQuality_HIGH
case sfu.InvalidLayerSpatial:
return livekit.VideoQuality_OFF
default:
return livekit.VideoQuality_OFF
}
}
func VideoQualityToRID(q livekit.VideoQuality) string {
switch q {
case livekit.VideoQuality_HIGH:
+8 -4
View File
@@ -131,6 +131,7 @@ func (t *MediaTrackSubscriptions) AddSubscriber(sub types.LocalParticipant, code
if err != nil {
return nil, err
}
subTrack := NewSubscribedTrack(SubscribedTrackParams{
PublisherID: t.params.MediaTrack.PublisherID(),
PublisherIdentity: t.params.MediaTrack.PublisherIdentity(),
@@ -182,17 +183,22 @@ func (t *MediaTrackSubscriptions) AddSubscriber(sub types.LocalParticipant, code
downTrack.SetRTPHeaderExtensions(sendParameters.HeaderExtensions)
downTrack.SetTransceiver(transceiver)
// when outtrack is bound, start loop to send reports
downTrack.OnBind(func() {
go subTrack.Bound()
go t.sendDownTrackBindingReports(sub)
})
trackID := t.params.MediaTrack.ID()
trackID := t.params.MediaTrack.ID()
downTrack.OnStatsUpdate(func(_ *sfu.DownTrack, stat *livekit.AnalyticsStat) {
t.params.Telemetry.TrackStats(livekit.StreamType_DOWNSTREAM, subscriberID, trackID, stat)
})
downTrack.OnMaxLayerChanged(func(dt *sfu.DownTrack, layer int32) {
t.notifySubscriberMaxQuality(subscriberID, QualityForSpatialLayer(layer))
})
downTrack.OnCloseHandler(func() {
t.subscribedTracksMu.Lock()
delete(t.subscribedTracks, subscriberID)
@@ -233,7 +239,6 @@ func (t *MediaTrackSubscriptions) AddSubscriber(sub types.LocalParticipant, code
}
}
t.NotifySubscriberMaxQuality(subscriberID, livekit.VideoQuality_OFF)
sub.RemoveSubscribedTrack(subTrack)
sub.Negotiate()
})
@@ -243,7 +248,6 @@ func (t *MediaTrackSubscriptions) AddSubscriber(sub types.LocalParticipant, code
// since sub will lock, run it in a goroutine to avoid deadlocks
go func() {
t.NotifySubscriberMaxQuality(subscriberID, livekit.VideoQuality_HIGH) // start with HIGH, let subscription change it later
sub.AddSubscribedTrack(subTrack)
sub.Negotiate()
}()
@@ -393,7 +397,7 @@ func (t *MediaTrackSubscriptions) OnSubscribedMaxQualityChange(f func(subscribed
t.onSubscribedMaxQualityChange = f
}
func (t *MediaTrackSubscriptions) NotifySubscriberMaxQuality(subscriberID livekit.ParticipantID, quality livekit.VideoQuality) {
func (t *MediaTrackSubscriptions) notifySubscriberMaxQuality(subscriberID livekit.ParticipantID, quality livekit.VideoQuality) {
if t.params.MediaTrack.Kind() != livekit.TrackType_VIDEO {
return
}
+1 -6
View File
@@ -102,10 +102,7 @@ func (t *SubscribedTrack) UpdateVideoLayer() {
if t.DownTrack().Kind() != webrtc.RTPCodecTypeVideo {
return
}
if t.subMuted.Get() {
t.MediaTrack().NotifySubscriberMaxQuality(t.params.SubscriberID, livekit.VideoQuality_OFF)
return
}
settings, ok := t.settings.Load().(*livekit.UpdateTrackSettings)
if !ok {
return
@@ -116,8 +113,6 @@ func (t *SubscribedTrack) UpdateVideoLayer() {
quality = t.MediaTrack().GetQualityForDimension(settings.Width, settings.Height)
}
t.DownTrack().SetMaxSpatialLayer(SpatialLayerForQuality(quality))
t.MediaTrack().NotifySubscriberMaxQuality(t.params.SubscriberID, quality)
}
func (t *SubscribedTrack) updateDownTrackMute() {
-2
View File
@@ -199,9 +199,7 @@ type MediaTrack interface {
// returns quality information that's appropriate for width & height
GetQualityForDimension(width, height uint32) livekit.VideoQuality
NotifySubscriberMaxQuality(subscriberID livekit.ParticipantID, quality livekit.VideoQuality)
NotifySubscriberNodeMaxQuality(nodeID string, quality livekit.VideoQuality)
NotifySubscriberNodeMediaLoss(nodeID string, fractionalLoss uint8)
}
@@ -121,12 +121,6 @@ type FakeLocalMediaTrack struct {
nameReturnsOnCall map[int]struct {
result1 string
}
NotifySubscriberMaxQualityStub func(livekit.ParticipantID, livekit.VideoQuality)
notifySubscriberMaxQualityMutex sync.RWMutex
notifySubscriberMaxQualityArgsForCall []struct {
arg1 livekit.ParticipantID
arg2 livekit.VideoQuality
}
NotifySubscriberNodeMaxQualityStub func(string, livekit.VideoQuality)
notifySubscriberNodeMaxQualityMutex sync.RWMutex
notifySubscriberNodeMaxQualityArgsForCall []struct {
@@ -834,39 +828,6 @@ func (fake *FakeLocalMediaTrack) NameReturnsOnCall(i int, result1 string) {
}{result1}
}
func (fake *FakeLocalMediaTrack) NotifySubscriberMaxQuality(arg1 livekit.ParticipantID, arg2 livekit.VideoQuality) {
fake.notifySubscriberMaxQualityMutex.Lock()
fake.notifySubscriberMaxQualityArgsForCall = append(fake.notifySubscriberMaxQualityArgsForCall, struct {
arg1 livekit.ParticipantID
arg2 livekit.VideoQuality
}{arg1, arg2})
stub := fake.NotifySubscriberMaxQualityStub
fake.recordInvocation("NotifySubscriberMaxQuality", []interface{}{arg1, arg2})
fake.notifySubscriberMaxQualityMutex.Unlock()
if stub != nil {
fake.NotifySubscriberMaxQualityStub(arg1, arg2)
}
}
func (fake *FakeLocalMediaTrack) NotifySubscriberMaxQualityCallCount() int {
fake.notifySubscriberMaxQualityMutex.RLock()
defer fake.notifySubscriberMaxQualityMutex.RUnlock()
return len(fake.notifySubscriberMaxQualityArgsForCall)
}
func (fake *FakeLocalMediaTrack) NotifySubscriberMaxQualityCalls(stub func(livekit.ParticipantID, livekit.VideoQuality)) {
fake.notifySubscriberMaxQualityMutex.Lock()
defer fake.notifySubscriberMaxQualityMutex.Unlock()
fake.NotifySubscriberMaxQualityStub = stub
}
func (fake *FakeLocalMediaTrack) NotifySubscriberMaxQualityArgsForCall(i int) (livekit.ParticipantID, livekit.VideoQuality) {
fake.notifySubscriberMaxQualityMutex.RLock()
defer fake.notifySubscriberMaxQualityMutex.RUnlock()
argsForCall := fake.notifySubscriberMaxQualityArgsForCall[i]
return argsForCall.arg1, argsForCall.arg2
}
func (fake *FakeLocalMediaTrack) NotifySubscriberNodeMaxQuality(arg1 string, arg2 livekit.VideoQuality) {
fake.notifySubscriberNodeMaxQualityMutex.Lock()
fake.notifySubscriberNodeMaxQualityArgsForCall = append(fake.notifySubscriberNodeMaxQualityArgsForCall, struct {
@@ -1521,8 +1482,6 @@ func (fake *FakeLocalMediaTrack) Invocations() map[string][][]interface{} {
defer fake.kindMutex.RUnlock()
fake.nameMutex.RLock()
defer fake.nameMutex.RUnlock()
fake.notifySubscriberMaxQualityMutex.RLock()
defer fake.notifySubscriberMaxQualityMutex.RUnlock()
fake.notifySubscriberNodeMaxQualityMutex.RLock()
defer fake.notifySubscriberNodeMaxQualityMutex.RUnlock()
fake.notifySubscriberNodeMediaLossMutex.RLock()
@@ -99,12 +99,6 @@ type FakeMediaTrack struct {
nameReturnsOnCall map[int]struct {
result1 string
}
NotifySubscriberMaxQualityStub func(livekit.ParticipantID, livekit.VideoQuality)
notifySubscriberMaxQualityMutex sync.RWMutex
notifySubscriberMaxQualityArgsForCall []struct {
arg1 livekit.ParticipantID
arg2 livekit.VideoQuality
}
NotifySubscriberNodeMaxQualityStub func(string, livekit.VideoQuality)
notifySubscriberNodeMaxQualityMutex sync.RWMutex
notifySubscriberNodeMaxQualityArgsForCall []struct {
@@ -683,39 +677,6 @@ func (fake *FakeMediaTrack) NameReturnsOnCall(i int, result1 string) {
}{result1}
}
func (fake *FakeMediaTrack) NotifySubscriberMaxQuality(arg1 livekit.ParticipantID, arg2 livekit.VideoQuality) {
fake.notifySubscriberMaxQualityMutex.Lock()
fake.notifySubscriberMaxQualityArgsForCall = append(fake.notifySubscriberMaxQualityArgsForCall, struct {
arg1 livekit.ParticipantID
arg2 livekit.VideoQuality
}{arg1, arg2})
stub := fake.NotifySubscriberMaxQualityStub
fake.recordInvocation("NotifySubscriberMaxQuality", []interface{}{arg1, arg2})
fake.notifySubscriberMaxQualityMutex.Unlock()
if stub != nil {
fake.NotifySubscriberMaxQualityStub(arg1, arg2)
}
}
func (fake *FakeMediaTrack) NotifySubscriberMaxQualityCallCount() int {
fake.notifySubscriberMaxQualityMutex.RLock()
defer fake.notifySubscriberMaxQualityMutex.RUnlock()
return len(fake.notifySubscriberMaxQualityArgsForCall)
}
func (fake *FakeMediaTrack) NotifySubscriberMaxQualityCalls(stub func(livekit.ParticipantID, livekit.VideoQuality)) {
fake.notifySubscriberMaxQualityMutex.Lock()
defer fake.notifySubscriberMaxQualityMutex.Unlock()
fake.NotifySubscriberMaxQualityStub = stub
}
func (fake *FakeMediaTrack) NotifySubscriberMaxQualityArgsForCall(i int) (livekit.ParticipantID, livekit.VideoQuality) {
fake.notifySubscriberMaxQualityMutex.RLock()
defer fake.notifySubscriberMaxQualityMutex.RUnlock()
argsForCall := fake.notifySubscriberMaxQualityArgsForCall[i]
return argsForCall.arg1, argsForCall.arg2
}
func (fake *FakeMediaTrack) NotifySubscriberNodeMaxQuality(arg1 string, arg2 livekit.VideoQuality) {
fake.notifySubscriberNodeMaxQualityMutex.Lock()
fake.notifySubscriberNodeMaxQualityArgsForCall = append(fake.notifySubscriberNodeMaxQualityArgsForCall, struct {
@@ -1260,8 +1221,6 @@ func (fake *FakeMediaTrack) Invocations() map[string][][]interface{} {
defer fake.kindMutex.RUnlock()
fake.nameMutex.RLock()
defer fake.nameMutex.RUnlock()
fake.notifySubscriberMaxQualityMutex.RLock()
defer fake.notifySubscriberMaxQualityMutex.RUnlock()
fake.notifySubscriberNodeMaxQualityMutex.RLock()
defer fake.notifySubscriberNodeMaxQualityMutex.RUnlock()
fake.notifySubscriberNodeMediaLossMutex.RLock()
+50 -6
View File
@@ -134,6 +134,9 @@ type DownTrack struct {
// update stats
onStatsUpdate func(dt *DownTrack, stat *livekit.AnalyticsStat)
// when max subscribed layer changes
onMaxLayerChanged func(dt *DownTrack, layer int32)
}
// NewDownTrack returns a DownTrack.
@@ -336,6 +339,10 @@ func (d *DownTrack) WriteRTP(extPkt *buffer.ExtPacket, layer int32) error {
f(d, pktSize)
}
if tp.isSwitchingToMaxLayer && d.onMaxLayerChanged != nil && d.kind == webrtc.RTPCodecTypeVideo {
d.onMaxLayerChanged(d, layer)
}
d.UpdatePrimaryStats(uint32(pktSize))
} else {
d.logger.Errorw("writing rtp packet err", err)
@@ -369,7 +376,7 @@ func (d *DownTrack) WritePaddingRTP(bytesToSend int) int {
// can be sent only on frame boundaries, writing on disabled tracks
// will give more options.
// LK-TODO-END
if d.forwarder.Muted() {
if d.forwarder.IsMuted() {
return 0
}
@@ -440,12 +447,25 @@ func (d *DownTrack) WritePaddingRTP(bytesToSend int) int {
}
// Mute enables or disables media forwarding
func (d *DownTrack) Mute(val bool) {
changed := d.forwarder.Mute(val)
func (d *DownTrack) Mute(muted bool) {
changed, maxLayers := d.forwarder.Mute(muted)
if !changed {
return
}
if d.onMaxLayerChanged != nil && d.kind == webrtc.RTPCodecTypeVideo {
if muted {
d.onMaxLayerChanged(d, InvalidLayerSpatial)
} else {
//
// When unmuting, don't wait for layer lock as
// client might need to be notified to start layers
// before locking can happen in the forwarder.
//
d.onMaxLayerChanged(d, maxLayers.spatial)
}
}
if d.onSubscriptionChanged != nil {
d.onSubscriptionChanged(d)
}
@@ -478,6 +498,10 @@ func (d *DownTrack) CloseWithFlush(flush bool) {
d.connectionStats.Close()
if d.onMaxLayerChanged != nil && d.kind == webrtc.RTPCodecTypeVideo {
d.onMaxLayerChanged(d, InvalidLayerSpatial)
}
if d.onCloseHandler != nil {
d.onCloseHandler()
}
@@ -485,18 +509,29 @@ func (d *DownTrack) CloseWithFlush(flush bool) {
}
func (d *DownTrack) SetMaxSpatialLayer(spatialLayer int32) {
changed, maxLayers := d.forwarder.SetMaxSpatialLayer(spatialLayer)
changed, maxLayers, currentLayers := d.forwarder.SetMaxSpatialLayer(spatialLayer)
if !changed {
return
}
if d.onMaxLayerChanged != nil && d.kind == webrtc.RTPCodecTypeVideo && maxLayers.SpatialGreaterThanOrEqual(currentLayers) {
//
// Notify when new max is
// 1. Equal to current -> already locked to the new max
// 2. Greater than current -> two scenarios
// a. is higher than previous max -> client may need to start higher layer before forwarder can lock
// b. is lower than previous max -> client can stop higher layer(s)
//
d.onMaxLayerChanged(d, maxLayers.spatial)
}
if d.onSubscribedLayersChanged != nil {
d.onSubscribedLayersChanged(d, maxLayers)
}
}
func (d *DownTrack) SetMaxTemporalLayer(temporalLayer int32) {
changed, maxLayers := d.forwarder.SetMaxTemporalLayer(temporalLayer)
changed, maxLayers, _ := d.forwarder.SetMaxTemporalLayer(temporalLayer)
if !changed {
return
}
@@ -570,6 +605,15 @@ func (d *DownTrack) OnStatsUpdate(fn func(dt *DownTrack, stat *livekit.Analytics
d.onStatsUpdate = fn
}
func (d *DownTrack) OnMaxLayerChanged(fn func(dt *DownTrack, layer int32)) {
d.onMaxLayerChanged = fn
// have to send this immediately to set initial values
if fn != nil && d.kind == webrtc.RTPCodecTypeVideo {
go fn(d, d.forwarder.MaxLayers().spatial)
}
}
func (d *DownTrack) IsDeficient() bool {
return d.forwarder.IsDeficient()
}
@@ -1041,7 +1085,7 @@ func (d *DownTrack) DebugInfo() map[string]interface{} {
"SSRC": d.ssrc,
"MimeType": d.codec.MimeType,
"Bound": d.bound.get(),
"Muted": d.forwarder.Muted(),
"Muted": d.forwarder.IsMuted(),
"CurrentSpatialLayer": d.forwarder.CurrentLayers().spatial,
"Stats": stats,
}
+23 -15
View File
@@ -117,11 +117,12 @@ const (
)
type TranslationParams struct {
shouldDrop bool
isDroppingRelevant bool
shouldSendPLI bool
rtp *TranslationParamsRTP
vp8 *TranslationParamsVP8
shouldDrop bool
isDroppingRelevant bool
shouldSendPLI bool
isSwitchingToMaxLayer bool
rtp *TranslationParamsRTP
vp8 *TranslationParamsVP8
}
type VideoLayers struct {
@@ -137,6 +138,10 @@ func (v VideoLayers) GreaterThan(v2 VideoLayers) bool {
return v.spatial > v2.spatial || (v.spatial == v2.spatial && v.temporal > v2.temporal)
}
func (v VideoLayers) SpatialGreaterThanOrEqual(v2 VideoLayers) bool {
return v.spatial >= v2.spatial
}
const (
InvalidLayerSpatial = int32(-1)
InvalidLayerTemporal = int32(-1)
@@ -211,49 +216,49 @@ func NewForwarder(codec webrtc.RTPCodecCapability, kind webrtc.RTPCodecType, log
return f
}
func (f *Forwarder) Mute(val bool) bool {
func (f *Forwarder) Mute(val bool) (bool, VideoLayers) {
f.lock.Lock()
defer f.lock.Unlock()
if f.muted == val {
return false
return false, f.maxLayers
}
f.muted = val
return true
return true, f.maxLayers
}
func (f *Forwarder) Muted() bool {
func (f *Forwarder) IsMuted() bool {
f.lock.RLock()
defer f.lock.RUnlock()
return f.muted
}
func (f *Forwarder) SetMaxSpatialLayer(spatialLayer int32) (bool, VideoLayers) {
func (f *Forwarder) SetMaxSpatialLayer(spatialLayer int32) (bool, VideoLayers, VideoLayers) {
f.lock.Lock()
defer f.lock.Unlock()
if f.kind == webrtc.RTPCodecTypeAudio || spatialLayer == f.maxLayers.spatial {
return false, InvalidLayers
return false, f.maxLayers, f.currentLayers
}
f.maxLayers.spatial = spatialLayer
return true, f.maxLayers
return true, f.maxLayers, f.currentLayers
}
func (f *Forwarder) SetMaxTemporalLayer(temporalLayer int32) (bool, VideoLayers) {
func (f *Forwarder) SetMaxTemporalLayer(temporalLayer int32) (bool, VideoLayers, VideoLayers) {
f.lock.Lock()
defer f.lock.Unlock()
if f.kind == webrtc.RTPCodecTypeAudio || temporalLayer == f.maxLayers.temporal {
return false, InvalidLayers
return false, f.maxLayers, f.currentLayers
}
f.maxLayers.temporal = temporalLayer
return true, f.maxLayers
return true, f.maxLayers, f.currentLayers
}
func (f *Forwarder) MaxLayers() VideoLayers {
@@ -1118,6 +1123,9 @@ func (f *Forwarder) getTranslationParamsVideo(extPkt *buffer.ExtPacket, layer in
if extPkt.KeyFrame {
// lock to target layer
f.currentLayers.spatial = f.targetLayers.spatial
if f.currentLayers.spatial == f.maxLayers.spatial {
tp.isSwitchingToMaxLayer = true
}
} else {
tp.shouldSendPLI = true
}
+39 -19
View File
@@ -24,13 +24,16 @@ func newForwarder(codec webrtc.RTPCodecCapability, kind webrtc.RTPCodecType) *Fo
func TestForwarderMute(t *testing.T) {
f := newForwarder(testutils.TestOpusCodec, webrtc.RTPCodecTypeAudio)
require.False(t, f.Muted())
require.False(t, f.Mute(false)) // no change in mute state
require.False(t, f.Muted())
require.True(t, f.Mute(true))
require.True(t, f.Muted())
require.True(t, f.Mute(false))
require.False(t, f.Muted())
require.False(t, f.IsMuted())
muted, _ := f.Mute(false)
require.False(t, muted) // no change in mute state
require.False(t, f.IsMuted())
muted, _ = f.Mute(true)
require.True(t, muted)
require.True(t, f.IsMuted())
muted, _ = f.Mute(false)
require.True(t, muted)
require.False(t, f.IsMuted())
}
func TestForwarderLayersAudio(t *testing.T) {
@@ -41,13 +44,15 @@ func TestForwarderLayersAudio(t *testing.T) {
require.Equal(t, InvalidLayers, f.CurrentLayers())
require.Equal(t, InvalidLayers, f.TargetLayers())
changed, layers := f.SetMaxSpatialLayer(1)
changed, maxLayers, currentLayers := f.SetMaxSpatialLayer(1)
require.False(t, changed)
require.Equal(t, InvalidLayers, layers)
require.Equal(t, InvalidLayers, maxLayers)
require.Equal(t, InvalidLayers, currentLayers)
changed, layers = f.SetMaxTemporalLayer(1)
changed, maxLayers, currentLayers = f.SetMaxTemporalLayer(1)
require.False(t, changed)
require.Equal(t, InvalidLayers, layers)
require.Equal(t, InvalidLayers, maxLayers)
require.Equal(t, InvalidLayers, currentLayers)
require.Equal(t, InvalidLayers, f.MaxLayers())
}
@@ -65,31 +70,46 @@ func TestForwarderLayersVideo(t *testing.T) {
require.Equal(t, InvalidLayers, f.CurrentLayers())
require.Equal(t, InvalidLayers, f.TargetLayers())
changed, layers := f.SetMaxSpatialLayer(DefaultMaxLayerSpatial)
changed, maxLayers, currentLayers := f.SetMaxSpatialLayer(DefaultMaxLayerSpatial)
require.False(t, changed)
require.Equal(t, InvalidLayers, layers)
require.Equal(t, expectedLayers, maxLayers)
require.Equal(t, InvalidLayers, currentLayers)
changed, layers = f.SetMaxSpatialLayer(DefaultMaxLayerSpatial - 1)
changed, maxLayers, currentLayers = f.SetMaxSpatialLayer(DefaultMaxLayerSpatial - 1)
require.True(t, changed)
expectedLayers = VideoLayers{
spatial: DefaultMaxLayerSpatial - 1,
temporal: DefaultMaxLayerTemporal,
}
require.Equal(t, expectedLayers, layers)
require.Equal(t, expectedLayers, maxLayers)
require.Equal(t, expectedLayers, f.MaxLayers())
require.Equal(t, InvalidLayers, currentLayers)
changed, layers = f.SetMaxTemporalLayer(DefaultMaxLayerTemporal)
f.currentLayers = VideoLayers{spatial: 0, temporal: 1}
changed, maxLayers, currentLayers = f.SetMaxSpatialLayer(DefaultMaxLayerSpatial - 1)
require.False(t, changed)
require.Equal(t, InvalidLayers, layers)
expectedLayers = VideoLayers{
spatial: DefaultMaxLayerSpatial - 1,
temporal: DefaultMaxLayerTemporal,
}
require.Equal(t, expectedLayers, maxLayers)
require.Equal(t, expectedLayers, f.MaxLayers())
require.Equal(t, VideoLayers{spatial: 0, temporal: 1}, currentLayers)
changed, layers = f.SetMaxTemporalLayer(DefaultMaxLayerTemporal - 1)
changed, maxLayers, currentLayers = f.SetMaxTemporalLayer(DefaultMaxLayerTemporal)
require.False(t, changed)
require.Equal(t, expectedLayers, maxLayers)
require.Equal(t, VideoLayers{spatial: 0, temporal: 1}, currentLayers)
changed, maxLayers, currentLayers = f.SetMaxTemporalLayer(DefaultMaxLayerTemporal - 1)
require.True(t, changed)
expectedLayers = VideoLayers{
spatial: DefaultMaxLayerSpatial - 1,
temporal: DefaultMaxLayerTemporal - 1,
}
require.Equal(t, expectedLayers, layers)
require.Equal(t, expectedLayers, maxLayers)
require.Equal(t, expectedLayers, f.MaxLayers())
require.Equal(t, VideoLayers{spatial: 0, temporal: 1}, currentLayers)
}
func TestForwarderGetForwardingStatus(t *testing.T) {