diff --git a/pkg/sfu/buffer/buffer.go b/pkg/sfu/buffer/buffer.go index 0fd76a420..bc106d13e 100644 --- a/pkg/sfu/buffer/buffer.go +++ b/pkg/sfu/buffer/buffer.go @@ -785,8 +785,9 @@ func (b *Buffer) getExtPacket(rtpPacket *rtp.Packet, arrivalTime int64, flowStat // DD-TODO : notify active decode target change if changed. } } - switch b.mime { - case "video/vp8": + + switch utils.MatchMimeType(b.mime) { + case utils.MimeTypeVP8: vp8Packet := VP8{} if err := vp8Packet.Unmarshal(rtpPacket.Payload); err != nil { b.logger.Warnw("could not unmarshal VP8 packet", err) @@ -802,7 +803,7 @@ func (b *Buffer) getExtPacket(rtpPacket *rtp.Packet, arrivalTime int64, flowStat ep.Payload = vp8Packet ep.Spatial = InvalidLayerSpatial // vp8 don't have spatial scalability, reset to invalid - case "video/vp9": + case utils.MimeTypeVP9: if ep.DependencyDescriptor == nil { var vp9Packet codecs.VP9Packet _, err := vp9Packet.Unmarshal(rtpPacket.Payload) @@ -818,11 +819,11 @@ func (b *Buffer) getExtPacket(rtpPacket *rtp.Packet, arrivalTime int64, flowStat } ep.KeyFrame = IsVP9KeyFrame(rtpPacket.Payload) - case "video/h264": + case utils.MimeTypeH264: ep.KeyFrame = IsH264KeyFrame(rtpPacket.Payload) ep.Spatial = InvalidLayerSpatial // h.264 don't have spatial scalability, reset to invalid - case "video/av1": + case utils.MimeTypeAV1: ep.KeyFrame = IsAV1KeyFrame(rtpPacket.Payload) } @@ -1122,10 +1123,8 @@ func (b *Buffer) GetTemporalLayerFpsForSpatial(layer int32) []float32 { // SVC-TODO: May only need to differentiate between simulcast and non-simulcast // SVC-TODO: i. e. may be possible to treat single layer as SVC to get proper/intended functionality. func IsSvcCodec(mime string) bool { - switch strings.ToLower(mime) { - case "video/av1": - fallthrough - case "video/vp9": + switch utils.MatchMimeType(mime) { + case utils.MimeTypeAV1, utils.MimeTypeVP9: return true } return false diff --git a/pkg/sfu/utils/mimetype.go b/pkg/sfu/utils/mimetype.go new file mode 100644 index 000000000..0908e1eec --- /dev/null +++ b/pkg/sfu/utils/mimetype.go @@ -0,0 +1,102 @@ +// 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 utils + +type MimeType int + +const ( + MimeTypeUnknown MimeType = iota + MimeTypeVP8 + MimeTypeVP9 + MimeTypeH264 + MimeTypeAV1 +) + +func MatchMimeType(mimeType string) MimeType { + switch len(mimeType) { + case 9: + switch mimeType[0] { + case 'v', 'V': + switch mimeType[1] { + case 'i', 'I': + switch mimeType[2] { + case 'd', 'D': + switch mimeType[3] { + case 'e', 'E': + switch mimeType[4] { + case 'o', 'O': + switch mimeType[5] { + case '/': + switch mimeType[6] { + case 'v', 'V': + switch mimeType[7] { + case 'p', 'P': + switch mimeType[8] { + case '8': + return MimeTypeVP8 + case '9': + return MimeTypeVP9 + } + } + case 'a', 'A': + switch mimeType[7] { + case 'v', 'V': + switch mimeType[8] { + case '1': + return MimeTypeAV1 + } + } + } + } + } + } + } + } + } + case 10: + switch mimeType[0] { + case 'v', 'V': + switch mimeType[1] { + case 'i', 'I': + switch mimeType[2] { + case 'd', 'D': + switch mimeType[3] { + case 'e', 'E': + switch mimeType[4] { + case 'o', 'O': + switch mimeType[5] { + case '/': + switch mimeType[6] { + case 'h', 'H': + switch mimeType[7] { + case '2': + switch mimeType[8] { + case '6': + switch mimeType[9] { + case '4': + return MimeTypeH264 + } + } + } + } + } + } + } + } + } + } + } + return MimeTypeUnknown +} diff --git a/pkg/sfu/utils/mimetype_test.go b/pkg/sfu/utils/mimetype_test.go new file mode 100644 index 000000000..6cfcc7111 --- /dev/null +++ b/pkg/sfu/utils/mimetype_test.go @@ -0,0 +1,65 @@ +// 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 utils + +import ( + "strings" + "testing" + + "github.com/stretchr/testify/require" +) + +func toLowerSwitch(mimeType string) MimeType { + switch strings.ToLower(mimeType) { + case "video/vp8": + return MimeTypeVP8 + case "video/vp9": + return MimeTypeVP9 + case "video/h264": + return MimeTypeH264 + case "video/av1": + return MimeTypeAV1 + default: + return MimeTypeUnknown + } +} + +func TestMimeTypeMatch(t *testing.T) { + require.Equal(t, MimeTypeVP8, MatchMimeType("VIDEO/VP8"), "VIDEO/VP8") + require.Equal(t, MimeTypeVP9, MatchMimeType("VIDEO/VP9"), "VIDEO/VP9") + require.Equal(t, MimeTypeH264, MatchMimeType("VIDEO/H264"), "VIDEO/H264") + require.Equal(t, MimeTypeAV1, MatchMimeType("VIDEO/AV1"), "VIDEO/AV1") +} + +func BenchmarkMimeTypeMatch(b *testing.B) { + mimeTypes := []string{ + "video/VP8", + "video/VP9", + "video/H264", + "video/AV1", + } + + b.Run("ToLower/switch", func(b *testing.B) { + for i := range b.N { + _ = toLowerSwitch(mimeTypes[i%len(mimeTypes)]) + } + }) + + b.Run("MatchMimeType", func(b *testing.B) { + for i := range b.N { + _ = MatchMimeType(mimeTypes[i%len(mimeTypes)]) + } + }) +}