From 84d5ab0d57c88f326287aac55e1e0f052f5ce368 Mon Sep 17 00:00:00 2001 From: thebongy Date: Thu, 28 Mar 2024 21:44:35 -0400 Subject: [PATCH] Add support for rtcp-fb:* According to Chrome PSA: https://groups.google.com/g/discuss-webrtc/c/Y_h2B-NOzW0 rtcp-fb:* might start getting sent by chrome in M112. This was rolled back but may land again in M114 according to maintainers. Also, according to https://webrtc.googlesource.com/src.git/+/8155227/#F0 Chrome may plan on sending both rtcp-fb:* and rtcp-fb: variants for some time to allow migration in downstream projects. This change correctly parses the wildcard case to add the feedback to all payload types found in the SDP, to maintain compatibility with Pion. Resolves #172 --- util.go | 52 +++++++++++++++++++++++++++++++++++++++------------- util_test.go | 31 ++++++++++++++++++------------- 2 files changed, 57 insertions(+), 26 deletions(-) diff --git a/util.go b/util.go index f78824b..0f14f15 100644 --- a/util.go +++ b/util.go @@ -86,6 +86,16 @@ func (c Codec) String() string { return fmt.Sprintf("%d %s/%d/%s (%s) [%s]", c.PayloadType, c.Name, c.ClockRate, c.EncodingParameters, c.Fmtp, strings.Join(c.RTCPFeedback, ", ")) } +func (c *Codec) appendRTCPFeedback(rtcpFeedback string) { + for _, existingRTCPFeedback := range c.RTCPFeedback { + if existingRTCPFeedback == rtcpFeedback { + return + } + } + + c.RTCPFeedback = append(c.RTCPFeedback, rtcpFeedback) +} + func parseRtpmap(rtpmap string) (Codec, error) { var codec Codec parsingFailed := errExtractCodecRtpmap @@ -153,30 +163,33 @@ func parseFmtp(fmtp string) (Codec, error) { return codec, nil } -func parseRtcpFb(rtcpFb string) (Codec, error) { - var codec Codec - parsingFailed := errExtractCodecRtcpFb +func parseRtcpFb(rtcpFb string) (codec Codec, isWildcard bool, err error) { + var ptInt uint64 + err = errExtractCodecRtcpFb // a=ftcp-fb: [] split := strings.SplitN(rtcpFb, " ", 2) if len(split) != 2 { - return codec, parsingFailed + return } ptSplit := strings.Split(split[0], ":") if len(ptSplit) != 2 { - return codec, parsingFailed + return } - ptInt, err := strconv.ParseUint(ptSplit[1], 10, 8) - if err != nil { - return codec, parsingFailed + isWildcard = ptSplit[1] == "*" + if !isWildcard { + ptInt, err = strconv.ParseUint(ptSplit[1], 10, 8) + if err != nil { + return + } + + codec.PayloadType = uint8(ptInt) } - codec.PayloadType = uint8(ptInt) codec.RTCPFeedback = append(codec.RTCPFeedback, split[1]) - - return codec, nil + return codec, isWildcard, nil } func mergeCodecs(codec Codec, codecs map[uint8]Codec) { @@ -217,6 +230,7 @@ func (s *SessionDescription) buildCodecMap() map[uint8]Codec { }, } + wildcardRTCPFeedback := []string{} for _, m := range s.MediaDescriptions { for _, a := range m.Attributes { attr := a.String() @@ -232,14 +246,26 @@ func (s *SessionDescription) buildCodecMap() map[uint8]Codec { mergeCodecs(codec, codecs) } case strings.HasPrefix(attr, "rtcp-fb:"): - codec, err := parseRtcpFb(attr) - if err == nil { + codec, isWildcard, err := parseRtcpFb(attr) + switch { + case err != nil: + case isWildcard: + wildcardRTCPFeedback = append(wildcardRTCPFeedback, codec.RTCPFeedback...) + default: mergeCodecs(codec, codecs) } } } } + for i, codec := range codecs { + for _, newRTCPFeedback := range wildcardRTCPFeedback { + codec.appendRTCPFeedback(newRTCPFeedback) + } + + codecs[i] = codec + } + return codecs } diff --git a/util_test.go b/util_test.go index c910a7b..d1348d1 100644 --- a/util_test.go +++ b/util_test.go @@ -33,6 +33,8 @@ func getTestSessionDescription() SessionDescription { NewAttribute("rtcp-fb:97 ccm fir", ""), NewAttribute("rtcp-fb:97 nack", ""), NewAttribute("rtcp-fb:97 nack pli", ""), + NewAttribute("rtcp-fb:* transport-cc", ""), + NewAttribute("rtcp-fb:* nack", ""), }, }, }, @@ -103,10 +105,11 @@ func TestGetCodecForPayloadType(t *testing.T) { getTestSessionDescription(), 120, Codec{ - PayloadType: 120, - Name: "VP8", - ClockRate: 90000, - Fmtp: "max-fs=12288;max-fr=60", + PayloadType: 120, + Name: "VP8", + ClockRate: 90000, + Fmtp: "max-fs=12288;max-fr=60", + RTCPFeedback: []string{"transport-cc", "nack"}, }, }, { @@ -114,10 +117,11 @@ func TestGetCodecForPayloadType(t *testing.T) { getTestSessionDescription(), 121, Codec{ - PayloadType: 121, - Name: "VP9", - ClockRate: 90000, - Fmtp: "max-fs=12288;max-fr=60", + PayloadType: 121, + Name: "VP9", + ClockRate: 90000, + Fmtp: "max-fs=12288;max-fr=60", + RTCPFeedback: []string{"transport-cc", "nack"}, }, }, { @@ -125,10 +129,11 @@ func TestGetCodecForPayloadType(t *testing.T) { getTestSessionDescription(), 126, Codec{ - PayloadType: 126, - Name: "H264", - ClockRate: 90000, - Fmtp: "profile-level-id=42e01f;level-asymmetry-allowed=1;packetization-mode=1", + PayloadType: 126, + Name: "H264", + ClockRate: 90000, + Fmtp: "profile-level-id=42e01f;level-asymmetry-allowed=1;packetization-mode=1", + RTCPFeedback: []string{"transport-cc", "nack"}, }, }, { @@ -140,7 +145,7 @@ func TestGetCodecForPayloadType(t *testing.T) { Name: "H264", ClockRate: 90000, Fmtp: "profile-level-id=42e01f;level-asymmetry-allowed=1", - RTCPFeedback: []string{"ccm fir", "nack", "nack pli"}, + RTCPFeedback: []string{"ccm fir", "nack", "nack pli", "transport-cc"}, }, }, {