@@ -23,13 +23,14 @@ import (
2323// trackDetails represents any media source that can be represented in a SDP
2424// This isn't keyed by SSRC because it also needs to support rid based sources.
2525type trackDetails struct {
26- mid string
27- kind RTPCodecType
28- streamID string
29- id string
30- ssrcs []SSRC
31- repairSsrc * SSRC
32- rids []string
26+ mid string
27+ kind RTPCodecType
28+ streamID string
29+ id string
30+ ssrcs []SSRC
31+ rtxSsrc * SSRC
32+ fecSsrc * SSRC
33+ rids []string
3334}
3435
3536func trackDetailsForSSRC (trackDetails []trackDetails , ssrc SSRC ) * trackDetails {
@@ -91,6 +92,7 @@ func trackDetailsFromSDP(
9192 for _ , media := range s .MediaDescriptions {
9293 tracksInMediaSection := []trackDetails {}
9394 rtxRepairFlows := map [uint64 ]uint64 {}
95+ fecRepairFlows := map [uint64 ]uint64 {}
9496
9597 // Plan B can have multiple tracks in a single media section
9698 streamID := ""
@@ -143,7 +145,35 @@ func trackDetailsFromSDP(
143145 for i := range tracksInMediaSection {
144146 if tracksInMediaSection [i ].ssrcs [0 ] == SSRC (baseSsrc ) {
145147 repairSsrc := SSRC (rtxRepairFlow )
146- tracksInMediaSection [i ].repairSsrc = & repairSsrc
148+ tracksInMediaSection [i ].rtxSsrc = & repairSsrc
149+ }
150+ }
151+ }
152+ } else if split [0 ] == sdp .SemanticTokenForwardErrorCorrectionFramework {
153+ // Similar to above, lines like `a=ssrc-group:FEC-FR aaaaa bbbbb`
154+ // means for video ssrc aaaaa, there's a FEC track bbbbb
155+ if len (split ) == 3 {
156+ baseSsrc , err := strconv .ParseUint (split [1 ], 10 , 32 )
157+ if err != nil {
158+ log .Warnf ("Failed to parse SSRC: %v" , err )
159+
160+ continue
161+ }
162+ fecRepairFlow , err := strconv .ParseUint (split [2 ], 10 , 32 )
163+ if err != nil {
164+ log .Warnf ("Failed to parse SSRC: %v" , err )
165+
166+ continue
167+ }
168+ fecRepairFlows [fecRepairFlow ] = baseSsrc
169+ tracksInMediaSection = filterTrackWithSSRC (
170+ tracksInMediaSection ,
171+ SSRC (fecRepairFlow ),
172+ ) // Remove if fec was added as track before
173+ for i := range tracksInMediaSection {
174+ if tracksInMediaSection [i ].ssrcs [0 ] == SSRC (baseSsrc ) {
175+ repairSsrc := SSRC (fecRepairFlow )
176+ tracksInMediaSection [i ].fecSsrc = & repairSsrc
147177 }
148178 }
149179 }
@@ -171,6 +201,9 @@ func trackDetailsFromSDP(
171201 if _ , ok := rtxRepairFlows [ssrc ]; ok {
172202 continue // This ssrc is a RTX repair flow, ignore
173203 }
204+ if _ , ok := fecRepairFlows [ssrc ]; ok {
205+ continue // This ssrc is a FEC repair flow, ignore
206+ }
174207
175208 if len (split ) == 3 && strings .HasPrefix (split [1 ], "msid:" ) {
176209 streamID = split [1 ][len ("msid:" ):]
@@ -197,7 +230,13 @@ func trackDetailsFromSDP(
197230 for r , baseSsrc := range rtxRepairFlows {
198231 if baseSsrc == ssrc {
199232 repairSsrc := SSRC (r ) //nolint:gosec // G115
200- trackDetails .repairSsrc = & repairSsrc
233+ trackDetails .rtxSsrc = & repairSsrc
234+ }
235+ }
236+ for r , baseSsrc := range fecRepairFlows {
237+ if baseSsrc == ssrc {
238+ fecSsrc := SSRC (r ) //nolint:gosec // G115
239+ trackDetails .fecSsrc = & fecSsrc
201240 }
202241 }
203242
@@ -243,8 +282,12 @@ func trackDetailsToRTPReceiveParameters(trackDetails *trackDetails) RTPReceivePa
243282 encodings [i ].SSRC = trackDetails .ssrcs [i ]
244283 }
245284
246- if trackDetails .repairSsrc != nil {
247- encodings [i ].RTX .SSRC = * trackDetails .repairSsrc
285+ if trackDetails .rtxSsrc != nil {
286+ encodings [i ].RTX .SSRC = * trackDetails .rtxSsrc
287+ }
288+
289+ if trackDetails .fecSsrc != nil {
290+ encodings [i ].FEC .SSRC = * trackDetails .fecSsrc
248291 }
249292 }
250293
@@ -430,10 +473,26 @@ func addSenderSDP(
430473 sendParameters := sender .GetParameters ()
431474 for _ , encoding := range sendParameters .Encodings {
432475 if encoding .RTX .SSRC != 0 {
433- media = media .WithValueAttribute ("ssrc-group" , fmt .Sprintf ("FID %d %d" , encoding .SSRC , encoding .RTX .SSRC ))
476+ media = media .WithValueAttribute (
477+ "ssrc-group" ,
478+ fmt .Sprintf (
479+ "%s %d %d" ,
480+ sdp .SemanticTokenFlowIdentification ,
481+ encoding .SSRC ,
482+ encoding .RTX .SSRC ,
483+ ),
484+ )
434485 }
435486 if encoding .FEC .SSRC != 0 {
436- media = media .WithValueAttribute ("ssrc-group" , fmt .Sprintf ("FEC-FR %d %d" , encoding .SSRC , encoding .FEC .SSRC ))
487+ media = media .WithValueAttribute (
488+ "ssrc-group" ,
489+ fmt .Sprintf (
490+ "%s %d %d" ,
491+ sdp .SemanticTokenForwardErrorCorrectionFramework ,
492+ encoding .SSRC ,
493+ encoding .FEC .SSRC ,
494+ ),
495+ )
437496 }
438497
439498 media = media .WithMediaSource (
0 commit comments