Skip to content

Commit

Permalink
Add support for rtcp-fb:*
Browse files Browse the repository at this point in the history
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:<int> 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.
  • Loading branch information
thebongy authored and stv0g committed May 5, 2023
1 parent b8d6fcc commit 38eca8b
Show file tree
Hide file tree
Showing 2 changed files with 80 additions and 27 deletions.
76 changes: 62 additions & 14 deletions util.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
)
Expand Down Expand Up @@ -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:<payload type> <RTCP feedback type> [<RTCP feedback parameter>]
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) {
Expand Down Expand Up @@ -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
}
}
}
Expand Down
31 changes: 18 additions & 13 deletions util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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", ""),
},
},
},
Expand Down Expand Up @@ -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"},
},
},
{
Expand All @@ -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"},
},
},
} {
Expand Down

0 comments on commit 38eca8b

Please sign in to comment.