// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT

// Package fmtp implements per codec parsing of fmtp lines
package fmtp import ( ) func defaultClockRate( string) uint32 { := map[string]uint32{ "audio/opus": 48000, "audio/pcmu": 8000, "audio/pcma": 8000, } if , := [strings.ToLower()]; { return } return 90000 } func defaultChannels( string) uint16 { := map[string]uint16{ "audio/opus": 2, } if , := [strings.ToLower()]; { return } return 0 } func parseParameters( string) map[string]string { := make(map[string]string) for , := range strings.Split(, ";") { := strings.SplitN(strings.TrimSpace(), "=", 2) := strings.ToLower([0]) var string if len() > 1 { = [1] } [] = } return } // ClockRateEqual checks whether two clock rates are equal. func ( string, , 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 == 0 { = defaultClockRate() } if == 0 { = defaultClockRate() } return == } // ChannelsEqual checks whether two channels are equal. func ( string, , 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 == 0 { = defaultChannels() } if == 0 { = defaultChannels() } // RFC8866: channel count "is OPTIONAL and may be omitted // if the number of channels is one". if == 0 { = 1 } if == 0 { = 1 } return == } func paramsEqual(, map[string]string) bool { for , := range { if , := []; && !strings.EqualFold(, ) { return false } } for , := range { if , := []; && !strings.EqualFold(, ) { 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 ( string, uint32, uint16, string) FMTP { var FMTP := parseParameters() switch { case strings.EqualFold(, "video/h264"): = &h264FMTP{ parameters: , } case strings.EqualFold(, "video/vp9"): = &vp9FMTP{ parameters: , } case strings.EqualFold(, "video/av1"): = &av1FMTP{ parameters: , } default: = &genericFMTP{ mimeType: , clockRate: , channels: , parameters: , } } return } type genericFMTP struct { mimeType string clockRate uint32 channels uint16 parameters map[string]string } func ( *genericFMTP) () string { return .mimeType } // Match returns true if g and b are compatible fmtp descriptions // The generic implementation is used for MimeTypes that are not defined. func ( *genericFMTP) ( FMTP) bool { , := .(*genericFMTP) if ! { return false } return strings.EqualFold(.mimeType, .MimeType()) && ClockRateEqual(.mimeType, .clockRate, .clockRate) && ChannelsEqual(.mimeType, .channels, .channels) && paramsEqual(.parameters, .parameters) } func ( *genericFMTP) ( string) (string, bool) { , := .parameters[] return , }