Skip to content

Commit 4541b73

Browse files
committed
Add Retransmission and FEC to TrackLocal
If the MediaEngine contains support for them a SSRC will be generated appropriately Co-authored-by: aggresss <[email protected]> Co-authored-by: Kevin Wang <[email protected]> Resolves #1989 Resolves #1675
1 parent bd2309f commit 4541b73

21 files changed

+467
-119
lines changed

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ require (
66
github.com/pion/datachannel v1.5.9
77
github.com/pion/dtls/v3 v3.0.2
88
github.com/pion/ice/v4 v4.0.1
9-
github.com/pion/interceptor v0.1.31
9+
github.com/pion/interceptor v0.1.32
1010
github.com/pion/logging v0.2.2
1111
github.com/pion/randutil v0.1.0
1212
github.com/pion/rtcp v1.2.14

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@ github.com/pion/dtls/v3 v3.0.2 h1:425DEeJ/jfuTTghhUDW0GtYZYIwwMtnKKJNMcWccTX0=
4141
github.com/pion/dtls/v3 v3.0.2/go.mod h1:dfIXcFkKoujDQ+jtd8M6RgqKK3DuaUilm3YatAbGp5k=
4242
github.com/pion/ice/v4 v4.0.1 h1:2d3tPoTR90F3TcGYeXUwucGlXI3hds96cwv4kjZmb9s=
4343
github.com/pion/ice/v4 v4.0.1/go.mod h1:2dpakjpd7+74L5j3TAe6gvkbI5UIzOgAnkimm9SuHvA=
44-
github.com/pion/interceptor v0.1.31 h1:9enhHjP1fDfMI8sqvpO5c/9QuTQnCf2dzPHwwIH4x5w=
45-
github.com/pion/interceptor v0.1.31/go.mod h1:JzxbJ4umVTlZAf+/utHzNesY8tmRkM2lVmkS82TTj8Y=
44+
github.com/pion/interceptor v0.1.32 h1:DYbusOBhWKjPMiA5ifyczW03Tnh12gCaYn4VOvLMGk4=
45+
github.com/pion/interceptor v0.1.32/go.mod h1:JzxbJ4umVTlZAf+/utHzNesY8tmRkM2lVmkS82TTj8Y=
4646
github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY=
4747
github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms=
4848
github.com/pion/mdns/v2 v2.0.7 h1:c9kM8ewCgjslaAmicYMFQIde2H9/lrZpjBkN8VwoVtM=

interceptor.go

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ func (i *interceptorToTrackLocalWriter) Write(b []byte) (int, error) {
159159
return i.WriteRTP(&packet.Header, packet.Payload)
160160
}
161161

162-
func createStreamInfo(id string, ssrc SSRC, payloadType PayloadType, codec RTPCodecCapability, webrtcHeaderExtensions []RTPHeaderExtensionParameter) *interceptor.StreamInfo {
162+
func createStreamInfo(id string, ssrc, ssrcFEC, ssrcRTX SSRC, payloadType PayloadType, codec RTPCodecCapability, webrtcHeaderExtensions []RTPHeaderExtensionParameter) *interceptor.StreamInfo {
163163
headerExtensions := make([]interceptor.RTPHeaderExtension, 0, len(webrtcHeaderExtensions))
164164
for _, h := range webrtcHeaderExtensions {
165165
headerExtensions = append(headerExtensions, interceptor.RTPHeaderExtension{ID: h.ID, URI: h.URI})
@@ -171,15 +171,17 @@ func createStreamInfo(id string, ssrc SSRC, payloadType PayloadType, codec RTPCo
171171
}
172172

173173
return &interceptor.StreamInfo{
174-
ID: id,
175-
Attributes: interceptor.Attributes{},
176-
SSRC: uint32(ssrc),
177-
PayloadType: uint8(payloadType),
178-
RTPHeaderExtensions: headerExtensions,
179-
MimeType: codec.MimeType,
180-
ClockRate: codec.ClockRate,
181-
Channels: codec.Channels,
182-
SDPFmtpLine: codec.SDPFmtpLine,
183-
RTCPFeedback: feedbacks,
174+
ID: id,
175+
Attributes: interceptor.Attributes{},
176+
SSRC: uint32(ssrc),
177+
SSRCRetransmission: uint32(ssrcRTX),
178+
SSRCForwardErrorCorrection: uint32(ssrcFEC),
179+
PayloadType: uint8(payloadType),
180+
RTPHeaderExtensions: headerExtensions,
181+
MimeType: codec.MimeType,
182+
ClockRate: codec.ClockRate,
183+
Channels: codec.Channels,
184+
SDPFmtpLine: codec.SDPFmtpLine,
185+
RTCPFeedback: feedbacks,
184186
}
185187
}

interceptor_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -196,18 +196,18 @@ func Test_Interceptor_BindUnbind(t *testing.T) {
196196
if cnt := atomic.LoadUint32(&cntUnbindLocalStream); cnt != 1 {
197197
t.Errorf("UnbindLocalStreamFn is expected to be called once, but called %d times", cnt)
198198
}
199-
if cnt := atomic.LoadUint32(&cntBindRemoteStream); cnt != 1 {
199+
if cnt := atomic.LoadUint32(&cntBindRemoteStream); cnt != 2 {
200200
t.Errorf("BindRemoteStreamFn is expected to be called once, but called %d times", cnt)
201201
}
202-
if cnt := atomic.LoadUint32(&cntUnbindRemoteStream); cnt != 1 {
202+
if cnt := atomic.LoadUint32(&cntUnbindRemoteStream); cnt != 2 {
203203
t.Errorf("UnbindRemoteStreamFn is expected to be called once, but called %d times", cnt)
204204
}
205205

206206
// BindRTCPWriter/Reader and Close should be called from both side.
207207
if cnt := atomic.LoadUint32(&cntBindRTCPWriter); cnt != 2 {
208208
t.Errorf("BindRTCPWriterFn is expected to be called twice, but called %d times", cnt)
209209
}
210-
if cnt := atomic.LoadUint32(&cntBindRTCPReader); cnt != 2 {
210+
if cnt := atomic.LoadUint32(&cntBindRTCPReader); cnt != 3 {
211211
t.Errorf("BindRTCPReaderFn is expected to be called twice, but called %d times", cnt)
212212
}
213213
if cnt := atomic.LoadUint32(&cntClose); cnt != 2 {

mediaengine.go

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,12 @@ const (
4747
// MimeTypePCMA PCMA MIME type
4848
// Note: Matching should be case insensitive.
4949
MimeTypePCMA = "audio/PCMA"
50+
// MimeTypeRTX RTX MIME type
51+
// Note: Matching should be case insensitive.
52+
MimeTypeRTX = "video/rtx"
53+
// MimeTypeFlexFEC FEC MIME Type
54+
// Note: Matching should be case insensitive.
55+
MimeTypeFlexFEC = "video/flexfec"
5056
)
5157

5258
type mediaEngineHeaderExtension struct {
@@ -106,7 +112,7 @@ func (m *MediaEngine) RegisterDefaultCodecs() error {
106112
PayloadType: 96,
107113
},
108114
{
109-
RTPCodecCapability: RTPCodecCapability{"video/rtx", 90000, 0, "apt=96", nil},
115+
RTPCodecCapability: RTPCodecCapability{MimeTypeRTX, 90000, 0, "apt=96", nil},
110116
PayloadType: 97,
111117
},
112118

@@ -115,7 +121,7 @@ func (m *MediaEngine) RegisterDefaultCodecs() error {
115121
PayloadType: 102,
116122
},
117123
{
118-
RTPCodecCapability: RTPCodecCapability{"video/rtx", 90000, 0, "apt=102", nil},
124+
RTPCodecCapability: RTPCodecCapability{MimeTypeRTX, 90000, 0, "apt=102", nil},
119125
PayloadType: 103,
120126
},
121127

@@ -124,7 +130,7 @@ func (m *MediaEngine) RegisterDefaultCodecs() error {
124130
PayloadType: 104,
125131
},
126132
{
127-
RTPCodecCapability: RTPCodecCapability{"video/rtx", 90000, 0, "apt=104", nil},
133+
RTPCodecCapability: RTPCodecCapability{MimeTypeRTX, 90000, 0, "apt=104", nil},
128134
PayloadType: 105,
129135
},
130136

@@ -133,7 +139,7 @@ func (m *MediaEngine) RegisterDefaultCodecs() error {
133139
PayloadType: 106,
134140
},
135141
{
136-
RTPCodecCapability: RTPCodecCapability{"video/rtx", 90000, 0, "apt=106", nil},
142+
RTPCodecCapability: RTPCodecCapability{MimeTypeRTX, 90000, 0, "apt=106", nil},
137143
PayloadType: 107,
138144
},
139145

@@ -142,7 +148,7 @@ func (m *MediaEngine) RegisterDefaultCodecs() error {
142148
PayloadType: 108,
143149
},
144150
{
145-
RTPCodecCapability: RTPCodecCapability{"video/rtx", 90000, 0, "apt=108", nil},
151+
RTPCodecCapability: RTPCodecCapability{MimeTypeRTX, 90000, 0, "apt=108", nil},
146152
PayloadType: 109,
147153
},
148154

@@ -151,7 +157,7 @@ func (m *MediaEngine) RegisterDefaultCodecs() error {
151157
PayloadType: 127,
152158
},
153159
{
154-
RTPCodecCapability: RTPCodecCapability{"video/rtx", 90000, 0, "apt=127", nil},
160+
RTPCodecCapability: RTPCodecCapability{MimeTypeRTX, 90000, 0, "apt=127", nil},
155161
PayloadType: 125,
156162
},
157163

@@ -160,7 +166,7 @@ func (m *MediaEngine) RegisterDefaultCodecs() error {
160166
PayloadType: 39,
161167
},
162168
{
163-
RTPCodecCapability: RTPCodecCapability{"video/rtx", 90000, 0, "apt=39", nil},
169+
RTPCodecCapability: RTPCodecCapability{MimeTypeRTX, 90000, 0, "apt=39", nil},
164170
PayloadType: 40,
165171
},
166172

@@ -169,7 +175,7 @@ func (m *MediaEngine) RegisterDefaultCodecs() error {
169175
PayloadType: 45,
170176
},
171177
{
172-
RTPCodecCapability: RTPCodecCapability{"video/rtx", 90000, 0, "apt=45", nil},
178+
RTPCodecCapability: RTPCodecCapability{MimeTypeRTX, 90000, 0, "apt=45", nil},
173179
PayloadType: 46,
174180
},
175181

@@ -178,7 +184,7 @@ func (m *MediaEngine) RegisterDefaultCodecs() error {
178184
PayloadType: 98,
179185
},
180186
{
181-
RTPCodecCapability: RTPCodecCapability{"video/rtx", 90000, 0, "apt=98", nil},
187+
RTPCodecCapability: RTPCodecCapability{MimeTypeRTX, 90000, 0, "apt=98", nil},
182188
PayloadType: 99,
183189
},
184190

@@ -187,7 +193,7 @@ func (m *MediaEngine) RegisterDefaultCodecs() error {
187193
PayloadType: 100,
188194
},
189195
{
190-
RTPCodecCapability: RTPCodecCapability{"video/rtx", 90000, 0, "apt=100", nil},
196+
RTPCodecCapability: RTPCodecCapability{MimeTypeRTX, 90000, 0, "apt=100", nil},
191197
PayloadType: 101,
192198
},
193199

@@ -196,7 +202,7 @@ func (m *MediaEngine) RegisterDefaultCodecs() error {
196202
PayloadType: 112,
197203
},
198204
{
199-
RTPCodecCapability: RTPCodecCapability{"video/rtx", 90000, 0, "apt=112", nil},
205+
RTPCodecCapability: RTPCodecCapability{MimeTypeRTX, 90000, 0, "apt=112", nil},
200206
PayloadType: 113,
201207
},
202208
} {
@@ -702,3 +708,23 @@ func payloaderForCodec(codec RTPCodecCapability) (rtp.Payloader, error) {
702708
return nil, ErrNoPayloaderForCodec
703709
}
704710
}
711+
712+
func (m *MediaEngine) isRTXEnabled(typ RTPCodecType, directions []RTPTransceiverDirection) bool {
713+
for _, p := range m.getRTPParametersByKind(typ, directions).Codecs {
714+
if p.MimeType == MimeTypeRTX {
715+
return true
716+
}
717+
}
718+
719+
return false
720+
}
721+
722+
func (m *MediaEngine) isFECEnabled(typ RTPCodecType, directions []RTPTransceiverDirection) bool {
723+
for _, p := range m.getRTPParametersByKind(typ, directions).Codecs {
724+
if strings.Contains(p.MimeType, MimeTypeFlexFEC) {
725+
return true
726+
}
727+
}
728+
729+
return false
730+
}

mediaengine_test.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -364,31 +364,31 @@ a=fmtp:97 apt=96
364364
PayloadType: 96,
365365
}, RTPCodecTypeVideo))
366366
assert.NoError(t, m.RegisterCodec(RTPCodecParameters{
367-
RTPCodecCapability: RTPCodecCapability{"video/rtx", 90000, 0, "apt=96", nil},
367+
RTPCodecCapability: RTPCodecCapability{MimeTypeRTX, 90000, 0, "apt=96", nil},
368368
PayloadType: 97,
369369
}, RTPCodecTypeVideo))
370370
assert.NoError(t, m.RegisterCodec(RTPCodecParameters{
371371
RTPCodecCapability: RTPCodecCapability{MimeTypeH264, 90000, 0, "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f", nil},
372372
PayloadType: 102,
373373
}, RTPCodecTypeVideo))
374374
assert.NoError(t, m.RegisterCodec(RTPCodecParameters{
375-
RTPCodecCapability: RTPCodecCapability{"video/rtx", 90000, 0, "apt=102", nil},
375+
RTPCodecCapability: RTPCodecCapability{MimeTypeRTX, 90000, 0, "apt=102", nil},
376376
PayloadType: 103,
377377
}, RTPCodecTypeVideo))
378378
assert.NoError(t, m.RegisterCodec(RTPCodecParameters{
379379
RTPCodecCapability: RTPCodecCapability{MimeTypeH264, 90000, 0, "level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42001f", nil},
380380
PayloadType: 104,
381381
}, RTPCodecTypeVideo))
382382
assert.NoError(t, m.RegisterCodec(RTPCodecParameters{
383-
RTPCodecCapability: RTPCodecCapability{"video/rtx", 90000, 0, "apt=104", nil},
383+
RTPCodecCapability: RTPCodecCapability{MimeTypeRTX, 90000, 0, "apt=104", nil},
384384
PayloadType: 105,
385385
}, RTPCodecTypeVideo))
386386
assert.NoError(t, m.RegisterCodec(RTPCodecParameters{
387387
RTPCodecCapability: RTPCodecCapability{MimeTypeVP9, 90000, 0, "profile-id=2", nil},
388388
PayloadType: 98,
389389
}, RTPCodecTypeVideo))
390390
assert.NoError(t, m.RegisterCodec(RTPCodecParameters{
391-
RTPCodecCapability: RTPCodecCapability{"video/rtx", 90000, 0, "apt=98", nil},
391+
RTPCodecCapability: RTPCodecCapability{MimeTypeRTX, 90000, 0, "apt=98", nil},
392392
PayloadType: 99,
393393
}, RTPCodecTypeVideo))
394394
assert.NoError(t, m.updateFromRemoteDescription(mustParse(profileLevels)))
@@ -400,15 +400,15 @@ a=fmtp:97 apt=96
400400
assert.Equal(t, vp9Codec.MimeType, MimeTypeVP9)
401401
vp9RTX, _, err := m.getCodecByPayload(97)
402402
assert.NoError(t, err)
403-
assert.Equal(t, vp9RTX.MimeType, "video/rtx")
403+
assert.Equal(t, vp9RTX.MimeType, MimeTypeRTX)
404404

405405
h264P1Codec, _, err := m.getCodecByPayload(106)
406406
assert.NoError(t, err)
407407
assert.Equal(t, h264P1Codec.MimeType, MimeTypeH264)
408408
assert.Equal(t, h264P1Codec.SDPFmtpLine, "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f")
409409
h264P1RTX, _, err := m.getCodecByPayload(107)
410410
assert.NoError(t, err)
411-
assert.Equal(t, h264P1RTX.MimeType, "video/rtx")
411+
assert.Equal(t, h264P1RTX.MimeType, MimeTypeRTX)
412412
assert.Equal(t, h264P1RTX.SDPFmtpLine, "apt=106")
413413

414414
h264P0Codec, _, err := m.getCodecByPayload(108)
@@ -417,7 +417,7 @@ a=fmtp:97 apt=96
417417
assert.Equal(t, h264P0Codec.SDPFmtpLine, "level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42001f")
418418
h264P0RTX, _, err := m.getCodecByPayload(109)
419419
assert.NoError(t, err)
420-
assert.Equal(t, h264P0RTX.MimeType, "video/rtx")
420+
assert.Equal(t, h264P0RTX.MimeType, MimeTypeRTX)
421421
assert.Equal(t, h264P0RTX.SDPFmtpLine, "apt=108")
422422
})
423423

@@ -443,7 +443,7 @@ a=fmtp:97 apt=96
443443
PayloadType: 96,
444444
}, RTPCodecTypeVideo))
445445
assert.NoError(t, m.RegisterCodec(RTPCodecParameters{
446-
RTPCodecCapability: RTPCodecCapability{"video/rtx", 90000, 0, "apt=96", nil},
446+
RTPCodecCapability: RTPCodecCapability{MimeTypeRTX, 90000, 0, "apt=96", nil},
447447
PayloadType: 97,
448448
}, RTPCodecTypeVideo))
449449
assert.NoError(t, m.updateFromRemoteDescription(mustParse(profileLevels)))

peerconnection.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1048,6 +1048,11 @@ func (pc *PeerConnection) SetRemoteDescription(desc SessionDescription) error {
10481048
return err
10491049
}
10501050

1051+
// Disable RTX/FEC on RTPSenders if the remote didn't support it
1052+
for _, sender := range pc.GetSenders() {
1053+
sender.configureRTXAndFEC()
1054+
}
1055+
10511056
var t *RTPTransceiver
10521057
localTransceivers := append([]*RTPTransceiver{}, pc.GetTransceivers()...)
10531058
detectedPlanB := descriptionIsPlanB(pc.RemoteDescription(), pc.log)
@@ -1616,7 +1621,7 @@ func (pc *PeerConnection) handleIncomingSSRC(rtpStream io.Reader, ssrc SSRC) err
16161621
return err
16171622
}
16181623

1619-
streamInfo := createStreamInfo("", ssrc, params.Codecs[0].PayloadType, params.Codecs[0].RTPCodecCapability, params.HeaderExtensions)
1624+
streamInfo := createStreamInfo("", ssrc, 0, 0, params.Codecs[0].PayloadType, params.Codecs[0].RTPCodecCapability, params.HeaderExtensions)
16201625
readStream, interceptor, rtcpReadStream, rtcpInterceptor, err := pc.dtlsTransport.streamsForSSRC(ssrc, *streamInfo)
16211626
if err != nil {
16221627
return err

peerconnection_media_test.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import (
2121
"time"
2222

2323
"github.com/pion/logging"
24-
"github.com/pion/randutil"
2524
"github.com/pion/rtcp"
2625
"github.com/pion/rtp"
2726
"github.com/pion/sdp/v3"
@@ -778,7 +777,7 @@ func TestAddTransceiverFromTrackFailsRecvOnly(t *testing.T) {
778777
func TestPlanBMediaExchange(t *testing.T) {
779778
runTest := func(trackCount int, t *testing.T) {
780779
addSingleTrack := func(p *PeerConnection) *TrackLocalStaticSample {
781-
track, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, fmt.Sprintf("video-%d", randutil.NewMathRandomGenerator().Uint32()), fmt.Sprintf("video-%d", randutil.NewMathRandomGenerator().Uint32()))
780+
track, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, fmt.Sprintf("video-%d", util.RandUint32()), fmt.Sprintf("video-%d", util.RandUint32()))
782781
assert.NoError(t, err)
783782

784783
_, err = p.AddTrack(track)
@@ -1020,7 +1019,7 @@ func TestPeerConnection_Simulcast_Probe(t *testing.T) {
10201019
if len(track.bindings) == 1 {
10211020
_, err = track.bindings[0].writeStream.WriteRTP(&rtp.Header{
10221021
Version: 2,
1023-
SSRC: randutil.NewMathRandomGenerator().Uint32(),
1022+
SSRC: util.RandUint32(),
10241023
}, []byte{0, 1, 2, 3, 4, 5})
10251024
assert.NoError(t, err)
10261025
}

pkg/media/oggwriter/oggwriter.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ import (
1010
"io"
1111
"os"
1212

13-
"github.com/pion/randutil"
1413
"github.com/pion/rtp"
1514
"github.com/pion/rtp/codecs"
15+
"github.com/pion/webrtc/v4/internal/util"
1616
)
1717

1818
const (
@@ -68,7 +68,7 @@ func NewWith(out io.Writer, sampleRate uint32, channelCount uint16) (*OggWriter,
6868
stream: out,
6969
sampleRate: sampleRate,
7070
channelCount: channelCount,
71-
serial: randutil.NewMathRandomGenerator().Uint32(),
71+
serial: util.RandUint32(),
7272
checksumTable: generateChecksumTable(),
7373

7474
// Timestamp and Granule MUST start from 1

rtpcodec.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
package webrtc
55

66
import (
7+
"fmt"
78
"strings"
89

910
"github.com/pion/webrtc/v4/internal/fmtp"
@@ -123,3 +124,15 @@ func codecParametersFuzzySearch(needle RTPCodecParameters, haystack []RTPCodecPa
123124

124125
return RTPCodecParameters{}, codecMatchNone
125126
}
127+
128+
// Given a CodecParameters find the RTX CodecParameters if one exists
129+
func findRTXCodecParameters(needle PayloadType, haystack []RTPCodecParameters) (RTPCodecParameters, bool) {
130+
aptStr := fmt.Sprintf("apt=%d", needle)
131+
for _, c := range haystack {
132+
if aptStr == c.SDPFmtpLine {
133+
return c, true
134+
}
135+
}
136+
137+
return RTPCodecParameters{}, false
138+
}

0 commit comments

Comments
 (0)