Files
webrtc/internal/fmtp/fmtp_test.go
aler9 31c2c0dfc1 Fix AV1 and VP9 codec matching
Currently, AV1 or VP9 formats are matched regardless of the profile
parameter. This was not noticeable until browsers started advertising
multiple VP9 formats at the same time, each with a different profile
ID, in order to allow the counterpart to send different streams on the
basis of supported profiles.

This causes two issues: first, the library includes in the SDP all
formats passed by the browser, regardless of the fact that the profile
ID is registered in the API or not. Then, the library is unable to
choose the correct format for streaming, causing an intermittent
failure.

This patch fixes the matching algorithm and also covers the case in
which the profile ID is missing, by using values dictated by
specifications.

Tests were refactored since previous ones covered the same lines
multiple times.
2024-06-25 22:10:39 +02:00

521 lines
9.3 KiB
Go

// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
package fmtp
import (
"reflect"
"testing"
)
func TestParseParameters(t *testing.T) {
for _, ca := range []struct {
name string
line string
parameters map[string]string
}{
{
"one param",
"key-name=value",
map[string]string{
"key-name": "value",
},
},
{
"one param with white spaces",
"\tkey-name=value ",
map[string]string{
"key-name": "value",
},
},
{
"two params",
"key-name=value;key2=value2",
map[string]string{
"key-name": "value",
"key2": "value2",
},
},
{
"two params with white spaces",
"key-name=value; \n\tkey2=value2 ",
map[string]string{
"key-name": "value",
"key2": "value2",
},
},
} {
t.Run(ca.name, func(t *testing.T) {
parameters := parseParameters(ca.line)
if !reflect.DeepEqual(parameters, ca.parameters) {
t.Errorf("expected '%v', got '%v'", ca.parameters, parameters)
}
})
}
}
func TestParse(t *testing.T) {
for _, ca := range []struct {
name string
mimeType string
line string
expected FMTP
}{
{
"generic",
"generic",
"key-name=value",
&genericFMTP{
mimeType: "generic",
parameters: map[string]string{
"key-name": "value",
},
},
},
{
"generic case normalization",
"generic",
"Key=value",
&genericFMTP{
mimeType: "generic",
parameters: map[string]string{
"key": "value",
},
},
},
{
"h264",
"video/h264",
"key-name=value",
&h264FMTP{
parameters: map[string]string{
"key-name": "value",
},
},
},
{
"vp9",
"video/vp9",
"key-name=value",
&vp9FMTP{
parameters: map[string]string{
"key-name": "value",
},
},
},
{
"av1",
"video/av1",
"key-name=value",
&av1FMTP{
parameters: map[string]string{
"key-name": "value",
},
},
},
} {
t.Run(ca.name, func(t *testing.T) {
f := Parse(ca.mimeType, ca.line)
if !reflect.DeepEqual(ca.expected, f) {
t.Errorf("expected '%v', got '%v'", ca.expected, f)
}
if f.MimeType() != ca.mimeType {
t.Errorf("Expected '%v', got '%s'", ca.mimeType, f.MimeType())
}
})
}
}
func TestMatch(t *testing.T) {
consistString := map[bool]string{true: "consist", false: "inconsist"}
for _, ca := range []struct {
name string
a FMTP
b FMTP
consist bool
}{
{
"generic equal",
&genericFMTP{
mimeType: "generic",
parameters: map[string]string{
"key1": "value1",
"key2": "value2",
"key3": "value3",
},
},
&genericFMTP{
mimeType: "generic",
parameters: map[string]string{
"key1": "value1",
"key2": "value2",
"key3": "value3",
},
},
true,
},
{
"generic one extra param",
&genericFMTP{
mimeType: "generic",
parameters: map[string]string{
"key1": "value1",
"key2": "value2",
"key3": "value3",
},
},
&genericFMTP{
mimeType: "generic",
parameters: map[string]string{
"key1": "value1",
"key2": "value2",
"key3": "value3",
"key4": "value4",
},
},
true,
},
{
"generic inconsistent different kind",
&genericFMTP{
mimeType: "generic",
parameters: map[string]string{
"key1": "value1",
"key2": "value2",
"key3": "value3",
},
},
&h264FMTP{},
false,
},
{
"generic inconsistent different mime type",
&genericFMTP{
mimeType: "generic1",
parameters: map[string]string{
"key1": "value1",
"key2": "value2",
"key3": "value3",
},
},
&genericFMTP{
mimeType: "generic2",
parameters: map[string]string{
"key1": "value1",
"key2": "value2",
"key3": "value3",
},
},
false,
},
{
"generic inconsistent different parameters",
&genericFMTP{
mimeType: "generic",
parameters: map[string]string{
"key1": "value1",
"key2": "value2",
"key3": "value3",
},
},
&genericFMTP{
mimeType: "generic",
parameters: map[string]string{
"key1": "value1",
"key2": "different_value",
"key3": "value3",
},
},
false,
},
{
"h264 equal",
&h264FMTP{
parameters: map[string]string{
"level-asymmetry-allowed": "1",
"packetization-mode": "1",
"profile-level-id": "42e01f",
},
},
&h264FMTP{
parameters: map[string]string{
"level-asymmetry-allowed": "1",
"packetization-mode": "1",
"profile-level-id": "42e01f",
},
},
true,
},
{
"h264 one extra param",
&h264FMTP{
parameters: map[string]string{
"level-asymmetry-allowed": "1",
"packetization-mode": "1",
"profile-level-id": "42e01f",
},
},
&h264FMTP{
parameters: map[string]string{
"packetization-mode": "1",
"profile-level-id": "42e01f",
},
},
true,
},
{
"h264 different profile level ids version",
&h264FMTP{
parameters: map[string]string{
"packetization-mode": "1",
"profile-level-id": "42e01f",
},
},
&h264FMTP{
parameters: map[string]string{
"packetization-mode": "1",
"profile-level-id": "42e029",
},
},
true,
},
{
"h264 inconsistent different kind",
&h264FMTP{
parameters: map[string]string{
"packetization-mode": "0",
"profile-level-id": "42e01f",
},
},
&genericFMTP{},
false,
},
{
"h264 inconsistent different parameters",
&h264FMTP{
parameters: map[string]string{
"packetization-mode": "0",
"profile-level-id": "42e01f",
},
},
&h264FMTP{
parameters: map[string]string{
"packetization-mode": "1",
"profile-level-id": "42e01f",
},
},
false,
},
{
"h264 inconsistent missing packetization mode",
&h264FMTP{
parameters: map[string]string{
"packetization-mode": "0",
"profile-level-id": "42e01f",
},
},
&h264FMTP{
parameters: map[string]string{
"profile-level-id": "42e01f",
},
},
false,
},
{
"h264 inconsistent missing profile level id",
&h264FMTP{
parameters: map[string]string{
"packetization-mode": "1",
"profile-level-id": "42e01f",
},
},
&h264FMTP{
parameters: map[string]string{
"packetization-mode": "1",
},
},
false,
},
{
"h264 inconsistent invalid profile level id",
&h264FMTP{
parameters: map[string]string{
"packetization-mode": "1",
"profile-level-id": "42e029",
},
},
&h264FMTP{
parameters: map[string]string{
"packetization-mode": "1",
"profile-level-id": "41e029",
},
},
false,
},
{
"vp9 equal",
&vp9FMTP{
parameters: map[string]string{
"profile-id": "1",
},
},
&vp9FMTP{
parameters: map[string]string{
"profile-id": "1",
},
},
true,
},
{
"vp9 missing profile",
&vp9FMTP{
parameters: map[string]string{},
},
&vp9FMTP{
parameters: map[string]string{},
},
true,
},
{
"vp9 inferred profile",
&vp9FMTP{
parameters: map[string]string{
"profile-id": "0",
},
},
&vp9FMTP{
parameters: map[string]string{},
},
true,
},
{
"vp9 inconsistent different kind",
&vp9FMTP{
parameters: map[string]string{
"profile-id": "0",
},
},
&genericFMTP{},
false,
},
{
"vp9 inconsistent different profile",
&vp9FMTP{
parameters: map[string]string{
"profile-id": "0",
},
},
&vp9FMTP{
parameters: map[string]string{
"profile-id": "1",
},
},
false,
},
{
"vp9 inconsistent different inferred profile",
&vp9FMTP{
parameters: map[string]string{},
},
&vp9FMTP{
parameters: map[string]string{
"profile-id": "1",
},
},
false,
},
{
"av1 equal",
&av1FMTP{
parameters: map[string]string{
"profile": "1",
},
},
&av1FMTP{
parameters: map[string]string{
"profile": "1",
},
},
true,
},
{
"av1 missing profile",
&av1FMTP{
parameters: map[string]string{},
},
&av1FMTP{
parameters: map[string]string{},
},
true,
},
{
"av1 inferred profile",
&av1FMTP{
parameters: map[string]string{
"profile": "0",
},
},
&av1FMTP{
parameters: map[string]string{},
},
true,
},
{
"av1 inconsistent different kind",
&av1FMTP{
parameters: map[string]string{
"profile": "0",
},
},
&genericFMTP{},
false,
},
{
"av1 inconsistent different profile",
&av1FMTP{
parameters: map[string]string{
"profile": "0",
},
},
&av1FMTP{
parameters: map[string]string{
"profile": "1",
},
},
false,
},
{
"av1 inconsistent different inferred profile",
&av1FMTP{
parameters: map[string]string{},
},
&av1FMTP{
parameters: map[string]string{
"profile": "1",
},
},
false,
},
} {
t.Run(ca.name, func(t *testing.T) {
c := ca.a.Match(ca.b)
if c != ca.consist {
t.Errorf(
"'%s' and '%s' are expected to be %s, but treated as %s",
ca.a, ca.b, consistString[ca.consist], consistString[c],
)
}
c = ca.b.Match(ca.a)
if c != ca.consist {
t.Errorf(
"'%s' and '%s' are expected to be %s, but treated as %s",
ca.a, ca.b, consistString[ca.consist], consistString[c],
)
}
})
}
}