snapshot of https://github.com/pion/webrtc.git (main, 2026-09-15; blobs > 131072B omitted)
Some checks failed
REUSE Compliance Check / lint (push) Failing after 0s
Browser E2E / Test (push) Failing after 3s
Test / test (1.24) (push) Failing after 0s
Test / test-i386 (1.25) (push) Failing after 0s
Test / test-i386 (1.24) (push) Failing after 0s
Test / test-windows (1.25) (push) Failing after 0s
Test / test-windows (1.24) (push) Failing after 0s
Test / test (1.25) (push) Failing after 0s
Examples Tests / Test (push) Failing after 3s
Test / test-macos (1.25) (push) Failing after 0s
Test / test-macos (1.24) (push) Failing after 0s
Go mod tidy / tidy (push) Failing after 0s
Test / test-wasm (push) Failing after 0s
CodeQL / analyze (push) Failing after 1s
Fuzz / fuzz (push) Failing after 0s

This commit is contained in:
upstream-mirror
2026-09-15 20:06:07 +05:00
commit 8acb1a3a29
394 changed files with 78746 additions and 0 deletions

42
internal/fmtp/av1.go Normal file
View File

@@ -0,0 +1,42 @@
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
package fmtp
type av1FMTP struct {
parameters map[string]string
}
func (h *av1FMTP) MimeType() string {
return "video/av1"
}
func (h *av1FMTP) Match(b FMTP) bool {
c, ok := b.(*av1FMTP)
if !ok {
return false
}
// RTP Payload Format For AV1 (v1.0)
// https://aomediacodec.github.io/av1-rtp-spec/
// If the profile parameter is not present, it MUST be inferred to be 0 (“Main” profile).
hProfile, ok := h.parameters["profile"]
if !ok {
hProfile = "0"
}
cProfile, ok := c.parameters["profile"]
if !ok {
cProfile = "0"
}
if hProfile != cProfile {
return false
}
return true
}
func (h *av1FMTP) Parameter(key string) (string, bool) {
v, ok := h.parameters[key]
return v, ok
}

190
internal/fmtp/fmtp.go Normal file
View File

@@ -0,0 +1,190 @@
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
// Package fmtp implements per codec parsing of fmtp lines
package fmtp
import (
"strings"
)
func defaultClockRate(mimeType string) uint32 {
defaults := map[string]uint32{
"audio/opus": 48000,
"audio/pcmu": 8000,
"audio/pcma": 8000,
}
if def, ok := defaults[strings.ToLower(mimeType)]; ok {
return def
}
return 90000
}
func defaultChannels(mimeType string) uint16 {
defaults := map[string]uint16{
"audio/opus": 2,
}
if def, ok := defaults[strings.ToLower(mimeType)]; ok {
return def
}
return 0
}
func parseParameters(line string) map[string]string {
parameters := make(map[string]string)
for p := range strings.SplitSeq(line, ";") {
pp := strings.SplitN(strings.TrimSpace(p), "=", 2)
key := strings.ToLower(pp[0])
var value string
if len(pp) > 1 {
value = pp[1]
}
parameters[key] = value
}
return parameters
}
// ClockRateEqual checks whether two clock rates are equal.
func ClockRateEqual(mimeType string, valA, valB uint32) bool {
// Lots of users use formats without setting clock rate or channels.
// In this case, use default values.
// It would be better to remove this exception in a future major release.
if valA == 0 {
valA = defaultClockRate(mimeType)
}
if valB == 0 {
valB = defaultClockRate(mimeType)
}
return valA == valB
}
// ChannelsEqual checks whether two channels are equal.
func ChannelsEqual(mimeType string, valA, valB uint16) bool {
// Lots of users use formats without setting clock rate or channels.
// In this case, use default values.
// It would be better to remove this exception in a future major release.
if valA == 0 {
valA = defaultChannels(mimeType)
}
if valB == 0 {
valB = defaultChannels(mimeType)
}
// RFC8866: channel count "is OPTIONAL and may be omitted
// if the number of channels is one".
if valA == 0 {
valA = 1
}
if valB == 0 {
valB = 1
}
return valA == valB
}
func paramsEqual(valA, valB map[string]string) bool {
for k, v := range valA {
if vb, ok := valB[k]; ok && !strings.EqualFold(vb, v) {
return false
}
}
for k, v := range valB {
if va, ok := valA[k]; ok && !strings.EqualFold(va, v) {
return false
}
}
return true
}
// FMTP interface for implementing custom
// FMTP parsers based on MimeType.
type FMTP interface {
// MimeType returns the MimeType associated with
// the fmtp
MimeType() string
// Match compares two fmtp descriptions for
// compatibility based on the MimeType
Match(f FMTP) bool
// Parameter returns a value for the associated key
// if contained in the parsed fmtp string
Parameter(key string) (string, bool)
}
// Parse parses an fmtp string based on the MimeType.
func Parse(mimeType string, clockRate uint32, channels uint16, line string) FMTP {
var fmtp FMTP
parameters := parseParameters(line)
switch {
case strings.EqualFold(mimeType, "video/h264"):
fmtp = &h264FMTP{
parameters: parameters,
}
case strings.EqualFold(mimeType, "video/h265"):
fmtp = &h265FMTP{
parameters: parameters,
}
case strings.EqualFold(mimeType, "video/vp9"):
fmtp = &vp9FMTP{
parameters: parameters,
}
case strings.EqualFold(mimeType, "video/av1"):
fmtp = &av1FMTP{
parameters: parameters,
}
default:
fmtp = &genericFMTP{
mimeType: mimeType,
clockRate: clockRate,
channels: channels,
parameters: parameters,
}
}
return fmtp
}
type genericFMTP struct {
mimeType string
clockRate uint32
channels uint16
parameters map[string]string
}
func (g *genericFMTP) MimeType() string {
return g.mimeType
}
// Match returns true if g and b are compatible fmtp descriptions
// The generic implementation is used for MimeTypes that are not defined.
func (g *genericFMTP) Match(b FMTP) bool {
fmtp, ok := b.(*genericFMTP)
if !ok {
return false
}
return strings.EqualFold(g.mimeType, fmtp.MimeType()) &&
ClockRateEqual(g.mimeType, g.clockRate, fmtp.clockRate) &&
ChannelsEqual(g.mimeType, g.channels, fmtp.channels) &&
paramsEqual(g.parameters, fmtp.parameters)
}
func (g *genericFMTP) Parameter(key string) (string, bool) {
v, ok := g.parameters[key]
return v, ok
}

768
internal/fmtp/fmtp_test.go Normal file
View File

@@ -0,0 +1,768 @@
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
package fmtp
import (
"testing"
"github.com/stretchr/testify/assert"
)
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)
assert.Equal(t, ca.parameters, parameters)
})
}
}
func TestParse(t *testing.T) {
for _, ca := range []struct {
name string
mimeType string
clockRate uint32
channels uint16
line string
expected FMTP
}{
{
"generic",
"generic",
90000,
2,
"key-name=value",
&genericFMTP{
mimeType: "generic",
clockRate: 90000,
channels: 2,
parameters: map[string]string{
"key-name": "value",
},
},
},
{
"generic case normalization",
"generic",
90000,
2,
"Key=value",
&genericFMTP{
mimeType: "generic",
clockRate: 90000,
channels: 2,
parameters: map[string]string{
"key": "value",
},
},
},
{
"h264",
"video/h264",
90000,
0,
"key-name=value",
&h264FMTP{
parameters: map[string]string{
"key-name": "value",
},
},
},
{
"h265",
"video/h265",
90000,
0,
"key-name=value",
&h265FMTP{
parameters: map[string]string{
"key-name": "value",
},
},
},
{
"vp9",
"video/vp9",
90000,
0,
"key-name=value",
&vp9FMTP{
parameters: map[string]string{
"key-name": "value",
},
},
},
{
"av1",
"video/av1",
90000,
0,
"key-name=value",
&av1FMTP{
parameters: map[string]string{
"key-name": "value",
},
},
},
} {
t.Run(ca.name, func(t *testing.T) {
f := Parse(ca.mimeType, ca.clockRate, ca.channels, ca.line)
assert.Equal(t, ca.expected, f)
assert.Equal(t, ca.mimeType, f.MimeType())
})
}
}
func TestMatch(t *testing.T) { //nolint:maintidx
consistString := map[bool]string{true: "consist", false: "inconsist"}
parseH265 := func(line string) FMTP {
return Parse("video/h265", 90000, 0, line)
}
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 inferred channels",
&genericFMTP{
mimeType: "generic",
channels: 1,
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 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 clock rate",
&genericFMTP{
mimeType: "generic",
clockRate: 90000,
parameters: map[string]string{
"key1": "value1",
"key2": "value2",
"key3": "value3",
},
},
&genericFMTP{
mimeType: "generic",
clockRate: 48000,
parameters: map[string]string{
"key1": "value1",
"key2": "value2",
"key3": "value3",
},
},
false,
},
{
"generic inconsistent different channels",
&genericFMTP{
mimeType: "generic",
clockRate: 90000,
channels: 2,
parameters: map[string]string{
"key1": "value1",
"key2": "value2",
"key3": "value3",
},
},
&genericFMTP{
mimeType: "generic",
clockRate: 90000,
channels: 1,
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,
},
{
"h265 downgraded level",
parseH265("level-id=186;profile-id=1;tier-flag=0;tx-mode=SRST"),
parseH265("level-id=93;profile-id=1;tier-flag=0;tx-mode=SRST"),
true,
},
{
"h265 inferred defaults and ignored sprop parameter",
parseH265(""),
parseH265("profile-id=1;tier-flag=0;tx-mode=srst;sprop-vps=ignored"),
true,
},
{
"h265 inconsistent different kind",
parseH265(""),
&genericFMTP{},
false,
},
{
"h265 inconsistent different profile",
parseH265("profile-id=1"),
parseH265("profile-id=2"),
false,
},
{
"h265 inconsistent different tier",
parseH265("tier-flag=0"),
parseH265("tier-flag=1"),
false,
},
{
"h265 inconsistent different transmission mode",
parseH265("tx-mode=SRST"),
parseH265("tx-mode=MRST"),
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,
},
{
"pcmu channels",
&genericFMTP{
mimeType: "audio/pcmu",
clockRate: 8000,
channels: 0,
parameters: map[string]string{
"key1": "value1",
"key2": "value2",
"key3": "value3",
},
},
&genericFMTP{
mimeType: "audio/pcmu",
clockRate: 8000,
channels: 1,
parameters: map[string]string{
"key1": "value1",
"key2": "value2",
"key3": "value3",
},
},
true,
},
{
"pcmu inconsistent channels",
&genericFMTP{
mimeType: "audio/pcmu",
clockRate: 8000,
channels: 0,
parameters: map[string]string{
"key1": "value1",
"key2": "value2",
"key3": "value3",
},
},
&genericFMTP{
mimeType: "audio/pcmu",
clockRate: 8000,
channels: 2,
parameters: map[string]string{
"key1": "value1",
"key2": "value2",
"key3": "value3",
},
},
false,
},
{
"pcmu clockrate",
&genericFMTP{
mimeType: "audio/pcmu",
clockRate: 0,
channels: 0,
parameters: map[string]string{
"key1": "value1",
"key2": "value2",
"key3": "value3",
},
},
&genericFMTP{
mimeType: "audio/pcmu",
clockRate: 8000,
channels: 0,
parameters: map[string]string{
"key1": "value1",
"key2": "value2",
"key3": "value3",
},
},
true,
},
{
"pcmu inconsistent clockrate",
&genericFMTP{
mimeType: "audio/pcmu",
clockRate: 0,
channels: 0,
parameters: map[string]string{
"key1": "value1",
"key2": "value2",
"key3": "value3",
},
},
&genericFMTP{
mimeType: "audio/pcmu",
clockRate: 16000,
channels: 0,
parameters: map[string]string{
"key1": "value1",
"key2": "value2",
"key3": "value3",
},
},
false,
},
{
"opus clockrate",
&genericFMTP{
mimeType: "audio/opus",
clockRate: 0,
channels: 0,
parameters: map[string]string{
"key1": "value1",
"key2": "value2",
"key3": "value3",
},
},
&genericFMTP{
mimeType: "audio/opus",
clockRate: 48000,
channels: 2,
parameters: map[string]string{
"key1": "value1",
"key2": "value2",
"key3": "value3",
},
},
true,
},
} {
t.Run(ca.name, func(t *testing.T) {
c := ca.a.Match(ca.b)
assert.Equal(t, ca.consist, c)
assert.Equal(
t, ca.consist, c,
"'%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)
assert.Equalf(
t, ca.consist, c,
"'%s' and '%s' are expected to be %s, but treated as %s",
ca.b, ca.a, consistString[ca.consist], consistString[c],
)
})
}
}

86
internal/fmtp/h264.go Normal file
View File

@@ -0,0 +1,86 @@
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
package fmtp
import (
"encoding/hex"
)
func profileLevelIDMatches(a, b string) bool {
aa, err := hex.DecodeString(a)
if err != nil || len(aa) < 2 {
return false
}
bb, err := hex.DecodeString(b)
if err != nil || len(bb) < 2 {
return false
}
return aa[0] == bb[0] && aa[1] == bb[1]
}
type h264FMTP struct {
parameters map[string]string
}
func (h *h264FMTP) MimeType() string {
return "video/h264"
}
// Match returns true if h and b are compatible fmtp descriptions
// Based on RFC6184 Section 8.2.2:
//
// The parameters identifying a media format configuration for H.264
// are profile-level-id and packetization-mode. These media format
// configuration parameters (except for the level part of profile-
// level-id) MUST be used symmetrically; that is, the answerer MUST
// either maintain all configuration parameters or remove the media
// format (payload type) completely if one or more of the parameter
// values are not supported.
// Informative note: The requirement for symmetric use does not
// apply for the level part of profile-level-id and does not apply
// for the other stream properties and capability parameters.
func (h *h264FMTP) Match(b FMTP) bool {
fmtp, ok := b.(*h264FMTP)
if !ok {
return false
}
// test packetization-mode
hpmode, hok := h.parameters["packetization-mode"]
if !hok {
return false
}
cpmode, cok := fmtp.parameters["packetization-mode"]
if !cok {
return false
}
if hpmode != cpmode {
return false
}
// test profile-level-id
hplid, hok := h.parameters["profile-level-id"]
if !hok {
return false
}
cplid, cok := fmtp.parameters["profile-level-id"]
if !cok {
return false
}
if !profileLevelIDMatches(hplid, cplid) {
return false
}
return true
}
func (h *h264FMTP) Parameter(key string) (string, bool) {
v, ok := h.parameters[key]
return v, ok
}

57
internal/fmtp/h265.go Normal file
View File

@@ -0,0 +1,57 @@
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
package fmtp
import "strings"
type h265FMTP struct {
parameters map[string]string
}
func (h *h265FMTP) MimeType() string {
return "video/h265"
}
// Match returns true if h and b describe compatible H.265 media formats.
// draft-ietf-avtcore-hevc-webrtc-09 Section 2.1 defines profile-id,
// tier-flag, and tx-mode as the H.265 media format parameters for WebRTC:
// https://datatracker.ietf.org/doc/html/draft-ietf-avtcore-hevc-webrtc-09#section-2.1
//
// level-id is intentionally not compared, as required by W3C WebRTC candidate
// correction 52:
// https://www.w3.org/TR/webrtc/#codec-match-without-level-id
// RFC 7798 Section 7.2.2 permits an answer to lower it, so treating it as a
// symmetric parameter would reject a valid offer/answer exchange:
// https://www.rfc-editor.org/rfc/rfc7798.html#section-7.2.2
func (h *h265FMTP) Match(b FMTP) bool {
fmtp, ok := b.(*h265FMTP)
if !ok {
return false
}
return strings.EqualFold(
h.parameterOrDefault("profile-id", "1"),
fmtp.parameterOrDefault("profile-id", "1"),
) && strings.EqualFold(
h.parameterOrDefault("tier-flag", "0"),
fmtp.parameterOrDefault("tier-flag", "0"),
) && strings.EqualFold(
h.parameterOrDefault("tx-mode", "SRST"),
fmtp.parameterOrDefault("tx-mode", "SRST"),
)
}
func (h *h265FMTP) Parameter(key string) (string, bool) {
v, ok := h.parameters[key]
return v, ok
}
func (h *h265FMTP) parameterOrDefault(key, defaultValue string) string {
if value, ok := h.Parameter(key); ok {
return value
}
return defaultValue
}

42
internal/fmtp/vp9.go Normal file
View File

@@ -0,0 +1,42 @@
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
package fmtp
type vp9FMTP struct {
parameters map[string]string
}
func (h *vp9FMTP) MimeType() string {
return "video/vp9"
}
func (h *vp9FMTP) Match(b FMTP) bool {
c, ok := b.(*vp9FMTP)
if !ok {
return false
}
// RTP Payload Format for VP9 Video - draft-ietf-payload-vp9-16
// https://datatracker.ietf.org/doc/html/draft-ietf-payload-vp9-16
// If no profile-id is present, Profile 0 MUST be inferred
hProfileID, ok := h.parameters["profile-id"]
if !ok {
hProfileID = "0"
}
cProfileID, ok := c.parameters["profile-id"]
if !ok {
cProfileID = "0"
}
if hProfileID != cProfileID {
return false
}
return true
}
func (h *vp9FMTP) Parameter(key string) (string, bool) {
v, ok := h.parameters[key]
return v, ok
}