Skip to content

Commit

Permalink
Match codecs with different rate or channels
Browse files Browse the repository at this point in the history
Consider clock rate and channels when matching codecs. This allows to
support codecs with the same MIME type but sample rate or channel count
that might be different from the default ones, like PCMU, PCMA, LPCM
and multiopus.
  • Loading branch information
aler9 committed Feb 10, 2025
1 parent 59c7270 commit aa27913
Show file tree
Hide file tree
Showing 4 changed files with 330 additions and 32 deletions.
108 changes: 90 additions & 18 deletions internal/fmtp/fmtp.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,32 @@ 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)

Expand All @@ -24,6 +50,61 @@ func parseParameters(line string) map[string]string {
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
}

Check warning on line 102 in internal/fmtp/fmtp.go

View check run for this annotation

Codecov / codecov/patch

internal/fmtp/fmtp.go#L101-L102

Added lines #L101 - L102 were not covered by tests
}

return true
}

// FMTP interface for implementing custom
// FMTP parsers based on MimeType.
type FMTP interface {
Expand All @@ -39,7 +120,7 @@ type FMTP interface {
}

// Parse parses an fmtp string based on the MimeType.
func Parse(mimeType, line string) FMTP {
func Parse(mimeType string, clockRate uint32, channels uint16, line string) FMTP {
var fmtp FMTP

parameters := parseParameters(line)
Expand All @@ -63,6 +144,8 @@ func Parse(mimeType, line string) FMTP {
default:
fmtp = &genericFMTP{
mimeType: mimeType,
clockRate: clockRate,
channels: channels,
parameters: parameters,
}
}
Expand All @@ -72,6 +155,8 @@ func Parse(mimeType, line string) FMTP {

type genericFMTP struct {
mimeType string
clockRate uint32
channels uint16
parameters map[string]string
}

Expand All @@ -87,23 +172,10 @@ func (g *genericFMTP) Match(b FMTP) bool {
return false
}

if !strings.EqualFold(g.mimeType, fmtp.MimeType()) {
return false
}

for k, v := range g.parameters {
if vb, ok := fmtp.parameters[k]; ok && !strings.EqualFold(vb, v) {
return false
}
}

for k, v := range fmtp.parameters {
if va, ok := g.parameters[k]; ok && !strings.EqualFold(va, v) {
return false
}
}

return true
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) {
Expand Down
Loading

0 comments on commit aa27913

Please sign in to comment.