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
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:
190
internal/fmtp/fmtp.go
Normal file
190
internal/fmtp/fmtp.go
Normal 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
|
||||
}
|
||||
Reference in New Issue
Block a user