Files
livekit/pkg/sfu/codecmunger/vp8_test.go
David Zhao 981fb7cac7 Adding license notices (#1913)
* Adding license notices

* remove from config
2023-07-27 16:43:19 -07:00

525 lines
13 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 codecmunger
import (
"reflect"
"testing"
"github.com/stretchr/testify/require"
"github.com/livekit/protocol/logger"
"github.com/livekit/livekit-server/pkg/sfu/buffer"
"github.com/livekit/livekit-server/pkg/sfu/testutils"
)
func compare(expected *VP8, actual *VP8) bool {
return reflect.DeepEqual(expected.pictureIdWrapHandler, actual.pictureIdWrapHandler) &&
expected.extLastPictureId == actual.extLastPictureId &&
expected.pictureIdOffset == actual.pictureIdOffset &&
expected.pictureIdUsed == actual.pictureIdUsed &&
expected.lastTl0PicIdx == actual.lastTl0PicIdx &&
expected.tl0PicIdxOffset == actual.tl0PicIdxOffset &&
expected.tl0PicIdxUsed == actual.tl0PicIdxUsed &&
expected.tidUsed == actual.tidUsed &&
expected.lastKeyIdx == actual.lastKeyIdx &&
expected.keyIdxOffset == actual.keyIdxOffset &&
expected.keyIdxUsed == actual.keyIdxUsed
}
func newVP8() *VP8 {
return NewVP8(logger.GetLogger())
}
func TestSetLast(t *testing.T) {
v := newVP8()
params := &testutils.TestExtPacketParams{
SequenceNumber: 23333,
Timestamp: 0xabcdef,
SSRC: 0x12345678,
}
vp8 := &buffer.VP8{
FirstByte: 25,
I: true,
M: true,
PictureID: 13467,
L: true,
TL0PICIDX: 233,
T: true,
TID: 13,
Y: true,
K: true,
KEYIDX: 23,
HeaderSize: 6,
IsKeyFrame: true,
}
extPkt, err := testutils.GetTestExtPacketVP8(params, vp8)
require.NoError(t, err)
require.NotNil(t, extPkt)
expectedVP8 := VP8{
pictureIdWrapHandler: VP8PictureIdWrapHandler{
maxPictureId: 13466,
maxMBit: true,
totalWrap: 0,
lastWrap: 0,
},
extLastPictureId: 13467,
pictureIdOffset: 0,
pictureIdUsed: true,
lastTl0PicIdx: 233,
tl0PicIdxOffset: 0,
tl0PicIdxUsed: true,
tidUsed: true,
lastKeyIdx: 23,
keyIdxOffset: 0,
keyIdxUsed: true,
}
v.SetLast(extPkt)
require.True(t, compare(&expectedVP8, v))
}
func TestUpdateOffsets(t *testing.T) {
v := newVP8()
params := &testutils.TestExtPacketParams{
SequenceNumber: 23333,
Timestamp: 0xabcdef,
SSRC: 0x12345678,
}
vp8 := &buffer.VP8{
FirstByte: 25,
I: true,
M: true,
PictureID: 13467,
L: true,
TL0PICIDX: 233,
T: true,
TID: 13,
Y: true,
K: true,
KEYIDX: 23,
HeaderSize: 6,
IsKeyFrame: true,
}
extPkt, _ := testutils.GetTestExtPacketVP8(params, vp8)
v.SetLast(extPkt)
params = &testutils.TestExtPacketParams{
SequenceNumber: 56789,
Timestamp: 0xabcdef,
SSRC: 0x87654321,
}
vp8 = &buffer.VP8{
FirstByte: 25,
I: true,
M: true,
PictureID: 345,
L: true,
TL0PICIDX: 12,
T: true,
TID: 13,
Y: true,
K: true,
KEYIDX: 4,
HeaderSize: 6,
IsKeyFrame: true,
}
extPkt, _ = testutils.GetTestExtPacketVP8(params, vp8)
v.UpdateOffsets(extPkt)
expectedVP8 := VP8{
pictureIdWrapHandler: VP8PictureIdWrapHandler{
maxPictureId: 344,
maxMBit: true,
totalWrap: 0,
lastWrap: 0,
},
extLastPictureId: 13467,
pictureIdOffset: 345 - 13467 - 1,
pictureIdUsed: true,
lastTl0PicIdx: 233,
tl0PicIdxOffset: (12 - 233 - 1) & 0xff,
tl0PicIdxUsed: true,
tidUsed: true,
lastKeyIdx: 23,
keyIdxOffset: (4 - 23 - 1) & 0x1f,
keyIdxUsed: true,
}
require.True(t, compare(&expectedVP8, v))
}
func TestOutOfOrderPictureId(t *testing.T) {
v := newVP8()
params := &testutils.TestExtPacketParams{
SequenceNumber: 23333,
Timestamp: 0xabcdef,
SSRC: 0x12345678,
}
vp8 := &buffer.VP8{
FirstByte: 25,
I: true,
M: true,
PictureID: 13467,
L: true,
TL0PICIDX: 233,
T: true,
TID: 1,
Y: true,
K: true,
KEYIDX: 23,
HeaderSize: 6,
IsKeyFrame: true,
}
extPkt, _ := testutils.GetTestExtPacketVP8(params, vp8)
v.SetLast(extPkt)
v.UpdateAndGet(extPkt, false, false, 2)
// out-of-order sequence number not in the missing picture id cache
vp8.PictureID = 13466
extPkt, _ = testutils.GetTestExtPacketVP8(params, vp8)
codecBytes, err := v.UpdateAndGet(extPkt, true, false, 2)
require.Error(t, err)
require.ErrorIs(t, err, ErrOutOfOrderVP8PictureIdCacheMiss)
require.Nil(t, codecBytes)
// create a hole in picture id
vp8.PictureID = 13469
extPkt, _ = testutils.GetTestExtPacketVP8(params, vp8)
expectedVP8 := &buffer.VP8{
FirstByte: 25,
I: true,
M: true,
PictureID: 13469,
L: true,
TL0PICIDX: 233,
T: true,
TID: 1,
Y: true,
K: true,
KEYIDX: 23,
HeaderSize: 6,
IsKeyFrame: true,
}
marshalledVP8, err := expectedVP8.Marshal()
require.NoError(t, err)
codecBytes, err = v.UpdateAndGet(extPkt, false, true, 2)
require.NoError(t, err)
require.Equal(t, marshalledVP8, codecBytes)
// all three, the last, the current and the in-between should have been added to missing picture id cache
value, ok := v.PictureIdOffset(13467)
require.True(t, ok)
require.EqualValues(t, 0, value)
value, ok = v.PictureIdOffset(13468)
require.True(t, ok)
require.EqualValues(t, 0, value)
value, ok = v.PictureIdOffset(13469)
require.True(t, ok)
require.EqualValues(t, 0, value)
// out-of-order sequence number should be in the missing picture id cache
vp8.PictureID = 13468
extPkt, _ = testutils.GetTestExtPacketVP8(params, vp8)
expectedVP8 = &buffer.VP8{
FirstByte: 25,
I: true,
M: true,
PictureID: 13468,
L: true,
TL0PICIDX: 233,
T: true,
TID: 1,
Y: true,
K: true,
KEYIDX: 23,
HeaderSize: 6,
IsKeyFrame: true,
}
marshalledVP8, err = expectedVP8.Marshal()
require.NoError(t, err)
codecBytes, err = v.UpdateAndGet(extPkt, true, false, 2)
require.NoError(t, err)
require.Equal(t, marshalledVP8, codecBytes)
}
func TestTemporalLayerFiltering(t *testing.T) {
v := newVP8()
params := &testutils.TestExtPacketParams{
SequenceNumber: 23333,
Timestamp: 0xabcdef,
SSRC: 0x12345678,
}
vp8 := &buffer.VP8{
FirstByte: 25,
I: true,
M: true,
PictureID: 13467,
L: true,
TL0PICIDX: 233,
T: true,
TID: 1,
Y: true,
K: true,
KEYIDX: 23,
HeaderSize: 6,
IsKeyFrame: true,
}
extPkt, _ := testutils.GetTestExtPacketVP8(params, vp8)
v.SetLast(extPkt)
// translate
tp, err := v.UpdateAndGet(extPkt, false, false, 0)
require.Error(t, err)
require.ErrorIs(t, err, ErrFilteredVP8TemporalLayer)
require.Nil(t, tp)
dropped, _ := v.droppedPictureIds.Get(13467)
require.True(t, dropped)
require.EqualValues(t, 1, v.pictureIdOffset)
// another packet with the same picture id.
// It should be dropped, but offset should not be updated.
params.SequenceNumber = 23334
extPkt, _ = testutils.GetTestExtPacketVP8(params, vp8)
tp, err = v.UpdateAndGet(extPkt, false, false, 0)
require.Error(t, err)
require.ErrorIs(t, err, ErrFilteredVP8TemporalLayer)
require.Nil(t, tp)
dropped, _ = v.droppedPictureIds.Get(13467)
require.True(t, dropped)
require.EqualValues(t, 1, v.pictureIdOffset)
// another packet with the same picture id, but a gap in sequence number.
// It should be dropped, but offset should not be updated.
params.SequenceNumber = 23337
extPkt, _ = testutils.GetTestExtPacketVP8(params, vp8)
tp, err = v.UpdateAndGet(extPkt, false, false, 0)
require.Error(t, err)
require.ErrorIs(t, err, ErrFilteredVP8TemporalLayer)
require.Nil(t, tp)
dropped, _ = v.droppedPictureIds.Get(13467)
require.True(t, dropped)
require.EqualValues(t, 1, v.pictureIdOffset)
}
func TestGapInSequenceNumberSamePicture(t *testing.T) {
v := newVP8()
params := &testutils.TestExtPacketParams{
SequenceNumber: 65533,
Timestamp: 0xabcdef,
SSRC: 0x12345678,
PayloadSize: 33,
}
vp8 := &buffer.VP8{
FirstByte: 25,
I: true,
M: true,
PictureID: 13467,
L: true,
TL0PICIDX: 233,
T: true,
TID: 1,
Y: true,
K: true,
KEYIDX: 23,
HeaderSize: 6,
IsKeyFrame: true,
}
extPkt, _ := testutils.GetTestExtPacketVP8(params, vp8)
v.SetLast(extPkt)
expectedVP8 := &buffer.VP8{
FirstByte: 25,
I: true,
M: true,
PictureID: 13467,
L: true,
TL0PICIDX: 233,
T: true,
TID: 1,
Y: true,
K: true,
KEYIDX: 23,
HeaderSize: 6,
IsKeyFrame: true,
}
marshalledVP8, err := expectedVP8.Marshal()
require.NoError(t, err)
codecBytes, err := v.UpdateAndGet(extPkt, false, false, 2)
require.NoError(t, err)
require.Equal(t, marshalledVP8, codecBytes)
// telling there is a gap in sequence number will add pictures to missing picture cache
expectedVP8 = &buffer.VP8{
FirstByte: 25,
I: true,
M: true,
PictureID: 13467,
L: true,
TL0PICIDX: 233,
T: true,
TID: 1,
Y: true,
K: true,
KEYIDX: 23,
HeaderSize: 6,
IsKeyFrame: true,
}
marshalledVP8, err = expectedVP8.Marshal()
require.NoError(t, err)
codecBytes, err = v.UpdateAndGet(extPkt, false, true, 2)
require.NoError(t, err)
require.Equal(t, marshalledVP8, codecBytes)
value, ok := v.PictureIdOffset(13467)
require.True(t, ok)
require.EqualValues(t, 0, value)
}
func TestUpdateAndGetPadding(t *testing.T) {
v := newVP8()
params := &testutils.TestExtPacketParams{
SequenceNumber: 23333,
Timestamp: 0xabcdef,
SSRC: 0x12345678,
PayloadSize: 20,
}
vp8 := &buffer.VP8{
FirstByte: 25,
I: true,
M: true,
PictureID: 13467,
L: true,
TL0PICIDX: 233,
T: true,
TID: 13,
Y: true,
K: true,
KEYIDX: 23,
HeaderSize: 6,
IsKeyFrame: true,
}
extPkt, _ := testutils.GetTestExtPacketVP8(params, vp8)
v.SetLast(extPkt)
// getting padding with repeat of last picture
blankBytes, err := v.UpdateAndGetPadding(false)
require.NoError(t, err)
expectedVP8 := buffer.VP8{
FirstByte: 16,
I: true,
M: true,
PictureID: 13467,
L: true,
TL0PICIDX: 233,
T: true,
TID: 0,
Y: true,
K: true,
KEYIDX: 23,
HeaderSize: 6,
IsKeyFrame: true,
}
marshalledVP8, err := expectedVP8.Marshal()
require.NoError(t, err)
require.Equal(t, marshalledVP8, blankBytes)
// getting padding with new picture
blankBytes, err = v.UpdateAndGetPadding(true)
require.NoError(t, err)
expectedVP8 = buffer.VP8{
FirstByte: 16,
I: true,
M: true,
PictureID: 13468,
L: true,
TL0PICIDX: 234,
T: true,
TID: 0,
Y: true,
K: true,
KEYIDX: 24,
HeaderSize: 6,
IsKeyFrame: true,
}
marshalledVP8, err = expectedVP8.Marshal()
require.NoError(t, err)
require.Equal(t, marshalledVP8, blankBytes)
}
func TestVP8PictureIdWrapHandler(t *testing.T) {
v := &VP8PictureIdWrapHandler{}
v.Init(109, false)
require.Equal(t, int32(109), v.MaxPictureId())
require.False(t, v.maxMBit)
v.UpdateMaxPictureId(109350, true)
require.Equal(t, int32(109350), v.MaxPictureId())
require.True(t, v.maxMBit)
// start with something close to the 15-bit wrap around point
v.Init(32766, true)
// out-of-order, do not wrap
extPictureId := v.Unwrap(32750, true)
require.Equal(t, int32(32750), extPictureId)
require.Equal(t, int32(0), v.totalWrap)
require.Equal(t, int32(0), v.lastWrap)
// wrap at 15-bits
extPictureId = v.Unwrap(5, false)
require.Equal(t, int32(32773), extPictureId) // 15-bit wrap at 32768 + 5 = 32773
require.Equal(t, int32(32768), v.totalWrap)
require.Equal(t, int32(32768), v.lastWrap)
// set things near 7-bit wrap point
v.UpdateMaxPictureId(32893, false) // 32768 + 125
// wrap at 7-bits
extPictureId = v.Unwrap(5, true)
require.Equal(t, int32(32901), extPictureId) // 15-bit wrap at 32768 + 7-bit wrap at 128 + 5 = 32901
require.Equal(t, int32(32896), v.totalWrap) // one 15-bit wrap + one 7-bit wrap
require.Equal(t, int32(128), v.lastWrap)
// a new picture in 7-bit mode much with a gap in between.
// A big enough gap which would have been treated as out-of-order in 7-bit mode.
v.UpdateMaxPictureId(32901, false)
extPictureId = v.Unwrap(73, false)
require.Equal(t, int32(32841), extPictureId) // 15-bit wrap at 32768 + 73 = 32841
// a new picture in 15-bit mode much with a gap in between.
// A big enough gap which would have been treated as out-of-order in 7-bit mode.
v.UpdateMaxPictureId(32901, true)
v.lastWrap = int32(32768)
extPictureId = v.Unwrap(73, false)
require.Equal(t, int32(32969), extPictureId) // 15-bit wrap at 32768 + 7-bit wrap at 128 + 73 = 32969
}