Skip to content

Commit 46942ae

Browse files
committed
Support static/required codec params. Fix codec order.
1 parent 966281e commit 46942ae

5 files changed

Lines changed: 223 additions & 38 deletions

File tree

amrwb/amrwb.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ func init() {
4646
Priority: -4,
4747
FileExt: "amrwb",
4848
Disabled: true,
49+
ReqParams: []media.CodecParam{
50+
{Key: "octet-align", Val: "0"},
51+
},
4952
}, func(w media.PCM16Writer) media.WriteCloser[Sample] {
5053
return Decode(w, RTPBandwidthEfficient)
5154
}, func(w media.WriteCloser[Sample]) media.PCM16Writer {

codecs.go

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,72 @@ import (
1919
"strings"
2020
)
2121

22+
type CodecParams []CodecParam
23+
24+
func (arr CodecParams) String() string {
25+
params := make([]string, 0, len(arr))
26+
for _, p := range arr {
27+
params = append(params, p.String())
28+
}
29+
return strings.Join(params, ";")
30+
}
31+
32+
func (arr CodecParams) Get(key string) (string, bool) {
33+
for _, p := range arr {
34+
if p.Key == key {
35+
return p.Val, true
36+
}
37+
}
38+
return "", false
39+
}
40+
func (arr CodecParams) Has(key string) bool {
41+
for _, p := range arr {
42+
if p.Key == key {
43+
return true
44+
}
45+
}
46+
return false
47+
}
48+
func (arr CodecParams) HasValue(key, val string) bool {
49+
for _, p := range arr {
50+
if p.Key == key {
51+
return p.Val == val
52+
}
53+
}
54+
return false
55+
}
56+
func (arr CodecParams) HasParam(p2 CodecParam) bool {
57+
for _, p := range arr {
58+
if p.Key == p2.Key {
59+
return p.Val == p2.Val
60+
}
61+
}
62+
return false
63+
}
64+
65+
type CodecParam struct {
66+
Key string
67+
Val string
68+
}
69+
70+
func (p CodecParam) String() string {
71+
if p.Val == "" {
72+
return p.Key
73+
}
74+
return p.Key + "=" + p.Val
75+
}
76+
2277
type CodecInfo struct {
2378
SDPName string
2479
SampleRate int
2580
RTPClockRate int
2681
RTPDefType byte
2782
RTPIsStatic bool
28-
Priority int
83+
Priority int // higher is preferable
2984
Disabled bool // codec is disabled in GlobalCodecs by default
3085
Hidden bool // codec should not appear in SDP offer, but can be used in the answer
3186
FileExt string
87+
ReqParams CodecParams // a list of required codec params (fmtp)
3288
}
3389

3490
type Codec interface {

sdp/codecs.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,20 +39,25 @@ func init() {
3939

4040
// CodecByNameWith finds the codec with a given SDP name.
4141
// If the codec is not found or disabled in the codec set, it returns nil.
42-
func CodecByNameWith(s *media.CodecSet, name string) media.Codec {
42+
func CodecByNameWith(s *media.CodecSet, name string, params media.CodecParams) media.Codec {
4343
if s == nil {
4444
s = media.GlobalCodecs()
4545
}
4646
c := codecByName[strings.ToLower(name)]
4747
if !s.IsEnabled(c) {
4848
return nil
4949
}
50+
for _, p := range c.Info().ReqParams {
51+
if val, ok := params.Get(p.Key); ok && val != p.Val {
52+
return nil
53+
}
54+
}
5055
return c
5156
}
5257

5358
// CodecByName finds the codec with a given SDP name.
5459
//
5560
// Deprecated: use CodecByNameWith
5661
func CodecByName(name string) media.Codec {
57-
return CodecByNameWith(media.GlobalCodecs(), name)
62+
return CodecByNameWith(media.GlobalCodecs(), name, nil)
5863
}

sdp/offer.go

Lines changed: 73 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -61,12 +61,11 @@ func OfferCodecsWith(s *media.CodecSet) []CodecInfo {
6161
codecs := s.ListEnabled()
6262
slices.SortFunc(codecs, func(a, b media.Codec) int {
6363
ai, bi := a.Info(), b.Info()
64-
if ai.RTPIsStatic != bi.RTPIsStatic {
65-
if ai.RTPIsStatic {
66-
return -1
67-
} else if bi.RTPIsStatic {
68-
return 1
64+
if ai.Priority == bi.Priority {
65+
if ai.SampleRate != bi.SampleRate {
66+
return bi.SampleRate - ai.SampleRate
6967
}
68+
return strings.Compare(ai.SDPName, bi.SDPName)
7069
}
7170
return bi.Priority - ai.Priority
7271
})
@@ -128,15 +127,22 @@ func OfferMediaWith(s *media.CodecSet, rtpListenerPort int, encrypted Encryption
128127
formats := make([]string, 0, len(codecs))
129128
dtmfType := byte(0)
130129
for _, codec := range codecs {
131-
if codec.Codec.Info().SDPName == dtmf.SDPNameAndRate {
130+
ci := codec.Codec.Info()
131+
if ci.SDPName == dtmf.SDPNameAndRate {
132132
dtmfType = codec.Type
133133
}
134134
styp := strconv.Itoa(int(codec.Type))
135135
formats = append(formats, styp)
136136
attrs = append(attrs, sdp.Attribute{
137137
Key: "rtpmap",
138-
Value: styp + " " + codec.Codec.Info().SDPName,
138+
Value: styp + " " + ci.SDPName,
139139
})
140+
if len(ci.ReqParams) != 0 {
141+
attrs = append(attrs, sdp.Attribute{
142+
Key: "fmtp",
143+
Value: styp + " " + ci.ReqParams.String(),
144+
})
145+
}
140146
}
141147
if dtmfType > 0 {
142148
attrs = append(attrs, sdp.Attribute{
@@ -190,10 +196,16 @@ func AnswerMedia(rtpListenerPort int, audio *AudioConfig, crypt *srtp.Profile) *
190196
// Static compiler check for frame duration hardcoded below.
191197
var _ = [1]struct{}{}[20*time.Millisecond-rtp.DefFrameDur]
192198

193-
attrs := make([]sdp.Attribute, 0, 6)
199+
attrs := make([]sdp.Attribute, 0, 7)
200+
ac := audio.Codec.Info()
194201
attrs = append(attrs, sdp.Attribute{
195-
Key: "rtpmap", Value: fmt.Sprintf("%d %s", audio.Type, audio.Codec.Info().SDPName),
202+
Key: "rtpmap", Value: fmt.Sprintf("%d %s", audio.Type, ac.SDPName),
196203
})
204+
if len(ac.ReqParams) != 0 {
205+
attrs = append(attrs, sdp.Attribute{
206+
Key: "fmtp", Value: fmt.Sprintf("%d %s", audio.Type, ac.ReqParams.String()),
207+
})
208+
}
197209
formats := make([]string, 0, 2)
198210
formats = append(formats, strconv.Itoa(int(audio.Type)))
199211
if audio.DTMFType != 0 {
@@ -642,7 +654,25 @@ func parseSRTPProfile(val string) (*srtp.Profile, error) {
642654

643655
// ParseMediaWith parses SDP media description based on the given codec set.
644656
func ParseMediaWith(s *media.CodecSet, d *sdp.MediaDescription) (*MediaDesc, error) {
645-
var out MediaDesc
657+
type codecInfo struct {
658+
Type int
659+
Name string
660+
Params media.CodecParams
661+
}
662+
var (
663+
out MediaDesc
664+
codecs []*codecInfo
665+
)
666+
getCodec := func(typ int) *codecInfo {
667+
for _, c := range codecs {
668+
if c.Type == typ {
669+
return c
670+
}
671+
}
672+
c := &codecInfo{Type: typ}
673+
codecs = append(codecs, c)
674+
return c
675+
}
646676
for _, m := range d.Attributes {
647677
switch m.Key {
648678
case "rtpmap":
@@ -659,11 +689,26 @@ func ParseMediaWith(s *media.CodecSet, d *sdp.MediaDescription) (*MediaDesc, err
659689
out.DTMFType = byte(typ)
660690
continue
661691
}
662-
codec, _ := CodecByNameWith(s, name).(media.AudioCodec)
663-
out.Codecs = append(out.Codecs, CodecInfo{
664-
Type: byte(typ),
665-
Codec: codec,
666-
})
692+
c := getCodec(typ)
693+
c.Name = name
694+
case "fmtp":
695+
sub := strings.SplitN(m.Value, " ", 2)
696+
if len(sub) != 2 {
697+
continue
698+
}
699+
typ, err := strconv.Atoi(sub[0])
700+
if err != nil {
701+
continue
702+
}
703+
c := getCodec(typ)
704+
for _, par := range strings.Split(sub[1], ";") {
705+
p := media.CodecParam{Key: par}
706+
if i := strings.IndexByte(par, '='); i >= 0 {
707+
p.Key = par[:i]
708+
p.Val = par[i+1:]
709+
}
710+
c.Params = append(c.Params, p)
711+
}
667712
case "crypto":
668713
p, err := parseSRTPProfile(m.Value)
669714
if err != nil {
@@ -685,12 +730,21 @@ func ParseMediaWith(s *media.CodecSet, d *sdp.MediaDescription) (*MediaDesc, err
685730
if err != nil {
686731
continue
687732
}
688-
codec, _ := rtp.CodecByPayloadType(byte(typ)).(media.AudioCodec)
689-
if !s.IsEnabled(codec) {
690-
codec = nil
733+
c := getCodec(typ)
734+
_ = c // just add
735+
}
736+
for _, ci := range codecs {
737+
var codec media.AudioCodec
738+
if ci.Name != "" {
739+
codec, _ = CodecByNameWith(s, ci.Name, ci.Params).(media.AudioCodec)
740+
} else {
741+
codec, _ = rtp.CodecByPayloadType(byte(ci.Type)).(media.AudioCodec)
742+
if !s.IsEnabled(codec) {
743+
codec = nil
744+
}
691745
}
692746
out.Codecs = append(out.Codecs, CodecInfo{
693-
Type: byte(typ),
747+
Type: byte(ci.Type),
694748
Codec: codec,
695749
})
696750
}

0 commit comments

Comments
 (0)