Skip to content

Commit cbe660b

Browse files
committed
Use intersection of codecs to generate rtcp-fb
Update MediaEngine codec creation to take into account remote and local rtcp-fb. Before we would incorrectly always take the remote rtcp-fb and ignore local. Resolves #2943 Resolves #2944 Resolves #1968
1 parent 1c4bc79 commit cbe660b

File tree

4 files changed

+98
-11
lines changed

4 files changed

+98
-11
lines changed

mediaengine.go

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -406,7 +406,7 @@ func (m *MediaEngine) collectStats(collector *statsReportCollector) {
406406
}
407407

408408
// Look up a codec and enable if it exists
409-
func (m *MediaEngine) matchRemoteCodec(remoteCodec RTPCodecParameters, typ RTPCodecType, exactMatches, partialMatches []RTPCodecParameters) (codecMatchType, error) {
409+
func (m *MediaEngine) matchRemoteCodec(remoteCodec RTPCodecParameters, typ RTPCodecType, exactMatches, partialMatches []RTPCodecParameters) (RTPCodecParameters, codecMatchType, error) {
410410
codecs := m.videoCodecs
411411
if typ == RTPCodecTypeAudio {
412412
codecs = m.audioCodecs
@@ -416,7 +416,7 @@ func (m *MediaEngine) matchRemoteCodec(remoteCodec RTPCodecParameters, typ RTPCo
416416
if apt, hasApt := remoteFmtp.Parameter("apt"); hasApt {
417417
payloadType, err := strconv.ParseUint(apt, 10, 8)
418418
if err != nil {
419-
return codecMatchNone, err
419+
return RTPCodecParameters{}, codecMatchNone, err
420420
}
421421

422422
aptMatch := codecMatchNone
@@ -440,7 +440,7 @@ func (m *MediaEngine) matchRemoteCodec(remoteCodec RTPCodecParameters, typ RTPCo
440440
}
441441

442442
if aptMatch == codecMatchNone {
443-
return codecMatchNone, nil // not an error, we just ignore this codec we don't support
443+
return RTPCodecParameters{}, codecMatchNone, nil // not an error, we just ignore this codec we don't support
444444
}
445445

446446
// replace the apt value with the original codec's payload type
@@ -450,15 +450,15 @@ func (m *MediaEngine) matchRemoteCodec(remoteCodec RTPCodecParameters, typ RTPCo
450450
}
451451

452452
// if apt's media codec is partial match, then apt codec must be partial match too
453-
_, matchType := codecParametersFuzzySearch(toMatchCodec, codecs)
453+
localCodec, matchType := codecParametersFuzzySearch(toMatchCodec, codecs)
454454
if matchType == codecMatchExact && aptMatch == codecMatchPartial {
455455
matchType = codecMatchPartial
456456
}
457-
return matchType, nil
457+
return localCodec, matchType, nil
458458
}
459459

460-
_, matchType := codecParametersFuzzySearch(remoteCodec, codecs)
461-
return matchType, nil
460+
localCodec, matchType := codecParametersFuzzySearch(remoteCodec, codecs)
461+
return localCodec, matchType, nil
462462
}
463463

464464
// Update header extensions from a remote media section
@@ -560,16 +560,18 @@ func (m *MediaEngine) updateFromRemoteDescription(desc sdp.SessionDescription) e
560560
exactMatches := make([]RTPCodecParameters, 0, len(codecs))
561561
partialMatches := make([]RTPCodecParameters, 0, len(codecs))
562562

563-
for _, codec := range codecs {
564-
matchType, mErr := m.matchRemoteCodec(codec, typ, exactMatches, partialMatches)
563+
for _, remoteCodec := range codecs {
564+
localCodec, matchType, mErr := m.matchRemoteCodec(remoteCodec, typ, exactMatches, partialMatches)
565565
if mErr != nil {
566566
return mErr
567567
}
568568

569+
remoteCodec.RTCPFeedback = rtcpFeedbackIntersection(localCodec.RTCPFeedback, remoteCodec.RTCPFeedback)
570+
569571
if matchType == codecMatchExact {
570-
exactMatches = append(exactMatches, codec)
572+
exactMatches = append(exactMatches, remoteCodec)
571573
} else if matchType == codecMatchPartial {
572-
partialMatches = append(partialMatches, codec)
574+
partialMatches = append(partialMatches, remoteCodec)
573575
}
574576
}
575577

mediaengine_test.go

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -722,3 +722,74 @@ a=fmtp:127 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001
722722
})
723723
}
724724
}
725+
726+
// rtcp-fb should be an intersection of local and remote
727+
func TestRTCPFeedbackHandling(t *testing.T) {
728+
const offerSdp = `
729+
v=0
730+
o=- 8448668841136641781 4 IN IP4 127.0.0.1
731+
s=-
732+
t=0 0
733+
a=group:BUNDLE 0
734+
a=extmap-allow-mixed
735+
a=msid-semantic: WMS 4beea6b0-cf95-449c-a1ec-78e16b247426
736+
m=video 9 UDP/TLS/RTP/SAVPF 96
737+
c=IN IP4 0.0.0.0
738+
a=rtcp:9 IN IP4 0.0.0.0
739+
a=ice-ufrag:1/MvHwjAyVf27aLu
740+
a=ice-pwd:3dBU7cFOBl120v33cynDvN1E
741+
a=ice-options:google-ice
742+
a=fingerprint:sha-256 75:74:5A:A6:A4:E5:52:F4:A7:67:4C:01:C7:EE:91:3F:21:3D:A2:E3:53:7B:6F:30:86:F2:30:AA:65:FB:04:24
743+
a=setup:actpass
744+
a=mid:0
745+
a=sendrecv
746+
a=rtpmap:96 VP8/90000
747+
a=rtcp-fb:96 goog-remb
748+
a=rtcp-fb:96 nack
749+
`
750+
751+
runTest := func(createTransceiver bool, t *testing.T) {
752+
m := &MediaEngine{}
753+
assert.NoError(t, m.RegisterCodec(RTPCodecParameters{
754+
RTPCodecCapability: RTPCodecCapability{MimeType: MimeTypeVP8, ClockRate: 90000, RTCPFeedback: []RTCPFeedback{
755+
{Type: TypeRTCPFBTransportCC},
756+
{Type: TypeRTCPFBNACK},
757+
}},
758+
PayloadType: 96,
759+
}, RTPCodecTypeVideo))
760+
761+
peerConnection, err := NewAPI(WithMediaEngine(m)).NewPeerConnection(Configuration{})
762+
assert.NoError(t, err)
763+
764+
if createTransceiver {
765+
_, err = peerConnection.AddTransceiverFromKind(RTPCodecTypeVideo)
766+
assert.NoError(t, err)
767+
}
768+
769+
assert.NoError(t, peerConnection.SetRemoteDescription(SessionDescription{
770+
Type: SDPTypeOffer,
771+
SDP: offerSdp,
772+
},
773+
))
774+
775+
answer, err := peerConnection.CreateAnswer(nil)
776+
assert.NoError(t, err)
777+
778+
// Both clients support
779+
assert.True(t, strings.Contains(answer.SDP, "a=rtcp-fb:96 nack"))
780+
781+
// Only one client supports
782+
assert.False(t, strings.Contains(answer.SDP, "a=rtcp-fb:96 goog-remb"))
783+
assert.False(t, strings.Contains(answer.SDP, "a=rtcp-fb:96 transport-cc"))
784+
785+
assert.NoError(t, peerConnection.Close())
786+
}
787+
788+
t.Run("recvonly", func(t *testing.T) {
789+
runTest(false, t)
790+
})
791+
792+
t.Run("sendrecv", func(t *testing.T) {
793+
runTest(true, t)
794+
})
795+
}

rtpcodec.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,3 +136,16 @@ func findRTXPayloadType(needle PayloadType, haystack []RTPCodecParameters) Paylo
136136

137137
return PayloadType(0)
138138
}
139+
140+
func rtcpFeedbackIntersection(a, b []RTCPFeedback) (out []RTCPFeedback) {
141+
for _, aFeedback := range a {
142+
for _, bFeeback := range b {
143+
if aFeedback.Type == bFeeback.Type && aFeedback.Parameter == bFeeback.Parameter {
144+
out = append(out, aFeedback)
145+
break
146+
}
147+
}
148+
}
149+
150+
return
151+
}

rtptransceiver.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ func (t *RTPTransceiver) getCodecs() []RTPCodecParameters {
7878
if codec.PayloadType == 0 {
7979
codec.PayloadType = c.PayloadType
8080
}
81+
codec.RTCPFeedback = rtcpFeedbackIntersection(codec.RTCPFeedback, c.RTCPFeedback)
8182
filteredCodecs = append(filteredCodecs, codec)
8283
}
8384
}

0 commit comments

Comments
 (0)