From 38eca8b92d89d22c7a88fa4cc5ad7ee1804c6042 Mon Sep 17 00:00:00 2001 From: thebongy Date: Mon, 3 Apr 2023 22:04:27 +0530 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. --- util.go | 76 ++++++++++++++++++++++++++++++++++++++++++---------- util_test.go | 31 ++++++++++++--------- 2 files changed, 80 insertions(+), 27 deletions(-) diff --git a/util.go b/util.go index 86aacbf..575b156 100644 --- a/util.go +++ b/util.go @@ -78,6 +78,18 @@ type Codec struct { RTCPFeedback []string } +// RTCPFeedback represents a single a=rtcp-fb line +type RTCPFeedback struct { + // If not wildcard, payload type specifies the specific payload type + PayloadType uint8 + + // If wildcard, PayloadType must be ignored, all payload types match + Wildcard bool + + // The RTCPFeedback value for this a=rtcp-fb line + Val string +} + const ( unknown = iota ) @@ -153,30 +165,43 @@ func parseFmtp(fmtp string) (Codec, error) { return codec, nil } -func parseRtcpFb(rtcpFb string) (Codec, error) { - var codec Codec +func parseRtcpFb(rtcpFb string) (RTCPFeedback, error) { + var rtcpFeedback RTCPFeedback parsingFailed := errExtractCodecRtcpFb // a=ftcp-fb: [] split := strings.SplitN(rtcpFb, " ", 2) if len(split) != 2 { - return codec, parsingFailed + return rtcpFeedback, parsingFailed } ptSplit := strings.Split(split[0], ":") if len(ptSplit) != 2 { - return codec, parsingFailed + return rtcpFeedback, parsingFailed } - ptInt, err := strconv.ParseUint(ptSplit[1], 10, 8) - if err != nil { - return codec, parsingFailed + if ptSplit[1] == "*" { + rtcpFeedback.Wildcard = true + } else { + ptInt, err := strconv.ParseUint(ptSplit[1], 10, 8) + if err != nil { + return rtcpFeedback, parsingFailed + } + rtcpFeedback.PayloadType = uint8(ptInt) } - codec.PayloadType = uint8(ptInt) - codec.RTCPFeedback = append(codec.RTCPFeedback, split[1]) + rtcpFeedback.Val = split[1] - return codec, nil + return rtcpFeedback, nil +} + +func appendIfMissing(slice []string, i string) []string { + for _, ele := range slice { + if ele == i { + return slice + } + } + return append(slice, i) } func mergeCodecs(codec Codec, codecs map[uint8]Codec) { @@ -219,10 +244,33 @@ func (s *SessionDescription) buildCodecMap() map[uint8]Codec { if err == nil { mergeCodecs(codec, codecs) } - case strings.HasPrefix(attr, "rtcp-fb:"): - codec, err := parseRtcpFb(attr) - if err == nil { - mergeCodecs(codec, codecs) + } + } + } + + for _, m := range s.MediaDescriptions { + for _, a := range m.Attributes { + attr := a.String() + if strings.HasPrefix(attr, "rtcp-fb:") { + feedback, err := parseRtcpFb(attr) + if err != nil { + continue + } + + if feedback.Wildcard { + for pt, codec := range codecs { + codec.RTCPFeedback = appendIfMissing(codec.RTCPFeedback, feedback.Val) + codecs[pt] = codec + } + } else { + codec, ok := codecs[feedback.PayloadType] + + if !ok { + continue + } + + codec.RTCPFeedback = appendIfMissing(codec.RTCPFeedback, feedback.Val) + codecs[feedback.PayloadType] = codec } } } diff --git a/util_test.go b/util_test.go index 85c3020..733138a 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", ""), }, }, }, @@ -99,28 +101,31 @@ func TestGetCodecForPayloadType(t *testing.T) { { PayloadType: 120, Expected: 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"}, }, }, { PayloadType: 121, Expected: 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"}, }, }, { PayloadType: 126, Expected: 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"}, }, }, { @@ -130,7 +135,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"}, }, }, } {