mirror of
https://github.com/pion/webrtc.git
synced 2025-10-04 14:53:05 +08:00
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
This commit is contained in:

committed by
Juliusz Chroboczek

parent
90222f6620
commit
ed84fadb26
@@ -401,7 +401,7 @@ func (m *MediaEngine) collectStats(collector *statsReportCollector) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Look up a codec and enable if it exists
|
// Look up a codec and enable if it exists
|
||||||
func (m *MediaEngine) matchRemoteCodec(remoteCodec RTPCodecParameters, typ RTPCodecType, exactMatches, partialMatches []RTPCodecParameters) (codecMatchType, error) {
|
func (m *MediaEngine) matchRemoteCodec(remoteCodec RTPCodecParameters, typ RTPCodecType, exactMatches, partialMatches []RTPCodecParameters) (RTPCodecParameters, codecMatchType, error) {
|
||||||
codecs := m.videoCodecs
|
codecs := m.videoCodecs
|
||||||
if typ == RTPCodecTypeAudio {
|
if typ == RTPCodecTypeAudio {
|
||||||
codecs = m.audioCodecs
|
codecs = m.audioCodecs
|
||||||
@@ -411,7 +411,7 @@ func (m *MediaEngine) matchRemoteCodec(remoteCodec RTPCodecParameters, typ RTPCo
|
|||||||
if apt, hasApt := remoteFmtp.Parameter("apt"); hasApt {
|
if apt, hasApt := remoteFmtp.Parameter("apt"); hasApt {
|
||||||
payloadType, err := strconv.ParseUint(apt, 10, 8)
|
payloadType, err := strconv.ParseUint(apt, 10, 8)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return codecMatchNone, err
|
return RTPCodecParameters{}, codecMatchNone, err
|
||||||
}
|
}
|
||||||
|
|
||||||
aptMatch := codecMatchNone
|
aptMatch := codecMatchNone
|
||||||
@@ -435,7 +435,7 @@ func (m *MediaEngine) matchRemoteCodec(remoteCodec RTPCodecParameters, typ RTPCo
|
|||||||
}
|
}
|
||||||
|
|
||||||
if aptMatch == codecMatchNone {
|
if aptMatch == codecMatchNone {
|
||||||
return codecMatchNone, nil // not an error, we just ignore this codec we don't support
|
return RTPCodecParameters{}, codecMatchNone, nil // not an error, we just ignore this codec we don't support
|
||||||
}
|
}
|
||||||
|
|
||||||
// replace the apt value with the original codec's payload type
|
// replace the apt value with the original codec's payload type
|
||||||
@@ -445,15 +445,15 @@ func (m *MediaEngine) matchRemoteCodec(remoteCodec RTPCodecParameters, typ RTPCo
|
|||||||
}
|
}
|
||||||
|
|
||||||
// if apt's media codec is partial match, then apt codec must be partial match too
|
// if apt's media codec is partial match, then apt codec must be partial match too
|
||||||
_, matchType := codecParametersFuzzySearch(toMatchCodec, codecs)
|
localCodec, matchType := codecParametersFuzzySearch(toMatchCodec, codecs)
|
||||||
if matchType == codecMatchExact && aptMatch == codecMatchPartial {
|
if matchType == codecMatchExact && aptMatch == codecMatchPartial {
|
||||||
matchType = codecMatchPartial
|
matchType = codecMatchPartial
|
||||||
}
|
}
|
||||||
return matchType, nil
|
return localCodec, matchType, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
_, matchType := codecParametersFuzzySearch(remoteCodec, codecs)
|
localCodec, matchType := codecParametersFuzzySearch(remoteCodec, codecs)
|
||||||
return matchType, nil
|
return localCodec, matchType, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update header extensions from a remote media section
|
// Update header extensions from a remote media section
|
||||||
@@ -555,16 +555,18 @@ func (m *MediaEngine) updateFromRemoteDescription(desc sdp.SessionDescription) e
|
|||||||
exactMatches := make([]RTPCodecParameters, 0, len(codecs))
|
exactMatches := make([]RTPCodecParameters, 0, len(codecs))
|
||||||
partialMatches := make([]RTPCodecParameters, 0, len(codecs))
|
partialMatches := make([]RTPCodecParameters, 0, len(codecs))
|
||||||
|
|
||||||
for _, codec := range codecs {
|
for _, remoteCodec := range codecs {
|
||||||
matchType, mErr := m.matchRemoteCodec(codec, typ, exactMatches, partialMatches)
|
localCodec, matchType, mErr := m.matchRemoteCodec(remoteCodec, typ, exactMatches, partialMatches)
|
||||||
if mErr != nil {
|
if mErr != nil {
|
||||||
return mErr
|
return mErr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
remoteCodec.RTCPFeedback = rtcpFeedbackIntersection(localCodec.RTCPFeedback, remoteCodec.RTCPFeedback)
|
||||||
|
|
||||||
if matchType == codecMatchExact {
|
if matchType == codecMatchExact {
|
||||||
exactMatches = append(exactMatches, codec)
|
exactMatches = append(exactMatches, remoteCodec)
|
||||||
} else if matchType == codecMatchPartial {
|
} else if matchType == codecMatchPartial {
|
||||||
partialMatches = append(partialMatches, codec)
|
partialMatches = append(partialMatches, remoteCodec)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -722,3 +722,74 @@ a=fmtp:127 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// rtcp-fb should be an intersection of local and remote
|
||||||
|
func TestRTCPFeedbackHandling(t *testing.T) {
|
||||||
|
const offerSdp = `
|
||||||
|
v=0
|
||||||
|
o=- 8448668841136641781 4 IN IP4 127.0.0.1
|
||||||
|
s=-
|
||||||
|
t=0 0
|
||||||
|
a=group:BUNDLE 0
|
||||||
|
a=extmap-allow-mixed
|
||||||
|
a=msid-semantic: WMS 4beea6b0-cf95-449c-a1ec-78e16b247426
|
||||||
|
m=video 9 UDP/TLS/RTP/SAVPF 96
|
||||||
|
c=IN IP4 0.0.0.0
|
||||||
|
a=rtcp:9 IN IP4 0.0.0.0
|
||||||
|
a=ice-ufrag:1/MvHwjAyVf27aLu
|
||||||
|
a=ice-pwd:3dBU7cFOBl120v33cynDvN1E
|
||||||
|
a=ice-options:google-ice
|
||||||
|
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
|
||||||
|
a=setup:actpass
|
||||||
|
a=mid:0
|
||||||
|
a=sendrecv
|
||||||
|
a=rtpmap:96 VP8/90000
|
||||||
|
a=rtcp-fb:96 goog-remb
|
||||||
|
a=rtcp-fb:96 nack
|
||||||
|
`
|
||||||
|
|
||||||
|
runTest := func(createTransceiver bool, t *testing.T) {
|
||||||
|
m := &MediaEngine{}
|
||||||
|
assert.NoError(t, m.RegisterCodec(RTPCodecParameters{
|
||||||
|
RTPCodecCapability: RTPCodecCapability{MimeType: MimeTypeVP8, ClockRate: 90000, RTCPFeedback: []RTCPFeedback{
|
||||||
|
{Type: TypeRTCPFBTransportCC},
|
||||||
|
{Type: TypeRTCPFBNACK},
|
||||||
|
}},
|
||||||
|
PayloadType: 96,
|
||||||
|
}, RTPCodecTypeVideo))
|
||||||
|
|
||||||
|
peerConnection, err := NewAPI(WithMediaEngine(m)).NewPeerConnection(Configuration{})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
if createTransceiver {
|
||||||
|
_, err = peerConnection.AddTransceiverFromKind(RTPCodecTypeVideo)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.NoError(t, peerConnection.SetRemoteDescription(SessionDescription{
|
||||||
|
Type: SDPTypeOffer,
|
||||||
|
SDP: offerSdp,
|
||||||
|
},
|
||||||
|
))
|
||||||
|
|
||||||
|
answer, err := peerConnection.CreateAnswer(nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// Both clients support
|
||||||
|
assert.True(t, strings.Contains(answer.SDP, "a=rtcp-fb:96 nack"))
|
||||||
|
|
||||||
|
// Only one client supports
|
||||||
|
assert.False(t, strings.Contains(answer.SDP, "a=rtcp-fb:96 goog-remb"))
|
||||||
|
assert.False(t, strings.Contains(answer.SDP, "a=rtcp-fb:96 transport-cc"))
|
||||||
|
|
||||||
|
assert.NoError(t, peerConnection.Close())
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("recvonly", func(t *testing.T) {
|
||||||
|
runTest(false, t)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("sendrecv", func(t *testing.T) {
|
||||||
|
runTest(true, t)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
13
rtpcodec.go
13
rtpcodec.go
@@ -121,3 +121,16 @@ func codecParametersFuzzySearch(needle RTPCodecParameters, haystack []RTPCodecPa
|
|||||||
|
|
||||||
return RTPCodecParameters{}, codecMatchNone
|
return RTPCodecParameters{}, codecMatchNone
|
||||||
}
|
}
|
||||||
|
func rtcpFeedbackIntersection(a, b []RTCPFeedback) (out []RTCPFeedback) {
|
||||||
|
for _, aFeedback := range a {
|
||||||
|
for _, bFeeback := range b {
|
||||||
|
if aFeedback.Type == bFeeback.Type && aFeedback.Parameter == bFeeback.Parameter {
|
||||||
|
out = append(out, aFeedback)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -78,6 +78,7 @@ func (t *RTPTransceiver) getCodecs() []RTPCodecParameters {
|
|||||||
if codec.PayloadType == 0 {
|
if codec.PayloadType == 0 {
|
||||||
codec.PayloadType = c.PayloadType
|
codec.PayloadType = c.PayloadType
|
||||||
}
|
}
|
||||||
|
codec.RTCPFeedback = rtcpFeedbackIntersection(codec.RTCPFeedback, c.RTCPFeedback)
|
||||||
filteredCodecs = append(filteredCodecs, codec)
|
filteredCodecs = append(filteredCodecs, codec)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user