Skip to content

Commit c7c57e2

Browse files
authored
Error on conflicting codec values when parsing
Resolves #198
1 parent c128a97 commit c7c57e2

File tree

2 files changed

+101
-23
lines changed

2 files changed

+101
-23
lines changed

util.go

Lines changed: 57 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,16 @@ const (
1919
)
2020

2121
var (
22-
errExtractCodecRtpmap = errors.New("could not extract codec from rtpmap")
23-
errExtractCodecFmtp = errors.New("could not extract codec from fmtp")
24-
errExtractCodecRtcpFb = errors.New("could not extract codec from rtcp-fb")
25-
errPayloadTypeNotFound = errors.New("payload type not found")
26-
errCodecNotFound = errors.New("codec not found")
27-
errSyntaxError = errors.New("SyntaxError")
22+
errExtractCodecRtpmap = errors.New("could not extract codec from rtpmap")
23+
errExtractCodecFmtp = errors.New("could not extract codec from fmtp")
24+
errExtractCodecRtcpFb = errors.New("could not extract codec from rtcp-fb")
25+
errMultipleName = errors.New("codec has multiple names defined")
26+
errMultipleClockRate = errors.New("codec has multiple clock rates")
27+
errMultipleEncodingParameters = errors.New("codec has multiple encoding parameters")
28+
errMultipleFmtp = errors.New("codec has multiple fmtp values")
29+
errPayloadTypeNotFound = errors.New("payload type not found")
30+
errCodecNotFound = errors.New("codec not found")
31+
errSyntaxError = errors.New("SyntaxError")
2832
)
2933

3034
// ConnectionRole indicates which of the end points should initiate the connection establishment.
@@ -203,30 +207,49 @@ func parseRtcpFb(rtcpFb string) (codec Codec, isWildcard bool, err error) {
203207
return codec, isWildcard, nil
204208
}
205209

206-
func mergeCodecs(codec Codec, codecs map[uint8]Codec) {
210+
func mergeCodecs(codec Codec, codecs map[uint8]Codec) error { // nolint: cyclop
207211
savedCodec := codecs[codec.PayloadType]
212+
savedCodec.PayloadType = codec.PayloadType
213+
214+
if codec.Name != "" {
215+
if savedCodec.Name != "" && savedCodec.Name != codec.Name {
216+
return errMultipleName
217+
}
208218

209-
if savedCodec.PayloadType == 0 {
210-
savedCodec.PayloadType = codec.PayloadType
211-
}
212-
if savedCodec.Name == "" {
213219
savedCodec.Name = codec.Name
214220
}
215-
if savedCodec.ClockRate == 0 {
221+
222+
if codec.ClockRate != 0 {
223+
if savedCodec.ClockRate != 0 && savedCodec.ClockRate != codec.ClockRate {
224+
return errMultipleClockRate
225+
}
226+
216227
savedCodec.ClockRate = codec.ClockRate
217228
}
218-
if savedCodec.EncodingParameters == "" {
229+
230+
if codec.EncodingParameters != "" {
231+
if savedCodec.EncodingParameters != "" && savedCodec.EncodingParameters != codec.EncodingParameters {
232+
return errMultipleEncodingParameters
233+
}
234+
219235
savedCodec.EncodingParameters = codec.EncodingParameters
220236
}
221-
if savedCodec.Fmtp == "" {
237+
238+
if codec.Fmtp != "" {
239+
if savedCodec.Fmtp != "" && savedCodec.Fmtp != codec.Fmtp {
240+
return errMultipleFmtp
241+
}
242+
222243
savedCodec.Fmtp = codec.Fmtp
223244
}
224-
savedCodec.RTCPFeedback = append(savedCodec.RTCPFeedback, codec.RTCPFeedback...)
225245

246+
savedCodec.RTCPFeedback = append(savedCodec.RTCPFeedback, codec.RTCPFeedback...)
226247
codecs[savedCodec.PayloadType] = savedCodec
248+
249+
return nil
227250
}
228251

229-
func (s *SessionDescription) buildCodecMap() map[uint8]Codec { //nolint:cyclop
252+
func (s *SessionDescription) buildCodecMap() (map[uint8]Codec, error) { //nolint:cyclop, gocognit
230253
codecs := map[uint8]Codec{
231254
// static codecs that do not require a rtpmap
232255
0: {
@@ -249,12 +272,16 @@ func (s *SessionDescription) buildCodecMap() map[uint8]Codec { //nolint:cyclop
249272
case strings.HasPrefix(attr, "rtpmap:"):
250273
codec, err := parseRtpmap(attr)
251274
if err == nil {
252-
mergeCodecs(codec, codecs)
275+
if err = mergeCodecs(codec, codecs); err != nil {
276+
return nil, err
277+
}
253278
}
254279
case strings.HasPrefix(attr, "fmtp:"):
255280
codec, err := parseFmtp(attr)
256281
if err == nil {
257-
mergeCodecs(codec, codecs)
282+
if err = mergeCodecs(codec, codecs); err != nil {
283+
return nil, err
284+
}
258285
}
259286
case strings.HasPrefix(attr, "rtcp-fb:"):
260287
codec, isWildcard, err := parseRtcpFb(attr)
@@ -263,7 +290,9 @@ func (s *SessionDescription) buildCodecMap() map[uint8]Codec { //nolint:cyclop
263290
case isWildcard:
264291
wildcardRTCPFeedback = append(wildcardRTCPFeedback, codec.RTCPFeedback...)
265292
default:
266-
mergeCodecs(codec, codecs)
293+
if err = mergeCodecs(codec, codecs); err != nil {
294+
return nil, err
295+
}
267296
}
268297
}
269298
}
@@ -277,7 +306,7 @@ func (s *SessionDescription) buildCodecMap() map[uint8]Codec { //nolint:cyclop
277306
codecs[i] = codec
278307
}
279308

280-
return codecs
309+
return codecs, nil
281310
}
282311

283312
func equivalentFmtp(want, got string) bool {
@@ -321,7 +350,10 @@ func codecsMatch(wanted, got Codec) bool {
321350

322351
// GetCodecForPayloadType scans the SessionDescription for the given payload type and returns the codec.
323352
func (s *SessionDescription) GetCodecForPayloadType(payloadType uint8) (Codec, error) {
324-
codecs := s.buildCodecMap()
353+
codecs, err := s.buildCodecMap()
354+
if err != nil {
355+
return Codec{}, err
356+
}
325357

326358
codec, ok := codecs[payloadType]
327359
if ok {
@@ -334,7 +366,10 @@ func (s *SessionDescription) GetCodecForPayloadType(payloadType uint8) (Codec, e
334366
// GetPayloadTypeForCodec scans the SessionDescription for a codec that matches the provided codec
335367
// as closely as possible and returns its payload type.
336368
func (s *SessionDescription) GetPayloadTypeForCodec(wanted Codec) (uint8, error) {
337-
codecs := s.buildCodecMap()
369+
codecs, err := s.buildCodecMap()
370+
if err != nil {
371+
return 0, err
372+
}
338373

339374
for payloadType, codec := range codecs {
340375
if codecsMatch(wanted, codec) {

util_test.go

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import (
77
"errors"
88
"reflect"
99
"testing"
10+
11+
"github.com/stretchr/testify/require"
1012
)
1113

1214
func getTestSessionDescription() SessionDescription {
@@ -19,7 +21,7 @@ func getTestSessionDescription() SessionDescription {
1921
Value: 51372,
2022
},
2123
Protos: []string{"RTP", "AVP"},
22-
Formats: []string{"120", "121", "126", "97", "98"},
24+
Formats: []string{"120", "121", "126", "97", "98", "111"},
2325
},
2426
Attributes: []Attribute{
2527
NewAttribute("fmtp:126 profile-level-id=42e01f;level-asymmetry-allowed=1;packetization-mode=1", ""),
@@ -32,6 +34,7 @@ func getTestSessionDescription() SessionDescription {
3234
NewAttribute("rtpmap:126 H264/90000", ""),
3335
NewAttribute("rtpmap:97 H264/90000", ""),
3436
NewAttribute("rtpmap:98 H264/90000", ""),
37+
NewAttribute("rtpmap:111 opus/48000/2", ""),
3538
NewAttribute("rtcp-fb:97 ccm fir", ""),
3639
NewAttribute("rtcp-fb:97 nack", ""),
3740
NewAttribute("rtcp-fb:97 nack pli", ""),
@@ -241,3 +244,43 @@ func TestNewSessionID(t *testing.T) {
241244
t.Error("Value around upper boundary was not generated")
242245
}
243246
}
247+
248+
func TestCodecMultipleValues(t *testing.T) {
249+
for _, test := range []struct {
250+
name string
251+
attributeToAdd string
252+
expectedError error
253+
}{
254+
{
255+
"multiple name",
256+
"rtpmap:120 VP9/90000",
257+
errMultipleName,
258+
},
259+
{
260+
"multiple clockrate",
261+
"rtpmap:120 VP8/80000",
262+
errMultipleClockRate,
263+
},
264+
{
265+
"multiple encoding parameters",
266+
"rtpmap:111 opus/48000/3",
267+
errMultipleEncodingParameters,
268+
},
269+
{
270+
"multiple fmtp",
271+
"fmtp:126 multiple-fmtp",
272+
errMultipleFmtp,
273+
},
274+
} {
275+
t.Run(test.name, func(t *testing.T) {
276+
sd := getTestSessionDescription()
277+
sd.MediaDescriptions[0].Attributes = append(
278+
sd.MediaDescriptions[0].Attributes,
279+
NewPropertyAttribute(test.attributeToAdd),
280+
)
281+
282+
_, err := sd.GetPayloadTypeForCodec(Codec{Name: "VP8/90000"})
283+
require.ErrorIs(t, test.expectedError, err)
284+
})
285+
}
286+
}

0 commit comments

Comments
 (0)