Skip to content

Commit 4aceefe

Browse files
committed
Automatically configure RTX codecs
1 parent 3e43ae9 commit 4aceefe

File tree

4 files changed

+208
-2
lines changed

4 files changed

+208
-2
lines changed

mediaengine.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,44 @@ func (m *MediaEngine) RegisterCodec(codec RTPCodecParameters, typ RTPCodecType)
245245
return err
246246
}
247247

248+
func (m *MediaEngine) autoConfigRTXCodecs() error {
249+
additionalRTXCodecs := []RTPCodecParameters{}
250+
for _, codec := range m.videoCodecs {
251+
// ignore FEC & RTX
252+
if strings.Contains(codec.MimeType, MimeTypeFlexFEC) || codec.MimeType == MimeTypeRTX {
253+
continue
254+
}
255+
haveNACK := false
256+
for _, fb := range codec.RTCPFeedback {
257+
if fb.Type == "nack" {
258+
haveNACK = true
259+
260+
break
261+
}
262+
}
263+
if haveNACK {
264+
additionalRTXCodecs = append(additionalRTXCodecs, RTPCodecParameters{
265+
RTPCodecCapability: RTPCodecCapability{
266+
MimeType: MimeTypeRTX,
267+
ClockRate: 90000,
268+
Channels: 0,
269+
SDPFmtpLine: fmt.Sprintf("apt=%d", codec.PayloadType),
270+
RTCPFeedback: nil,
271+
},
272+
PayloadType: codec.PayloadType + 1,
273+
})
274+
}
275+
}
276+
for i := range additionalRTXCodecs {
277+
err := m.RegisterCodec(additionalRTXCodecs[i], RTPCodecTypeVideo)
278+
if err != nil {
279+
return err
280+
}
281+
}
282+
283+
return nil
284+
}
285+
248286
// RegisterHeaderExtension adds a header extension to the MediaEngine
249287
// To determine the negotiated value use `GetHeaderExtensionID` after signaling is complete.
250288
//

mediaengine_test.go

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -896,3 +896,158 @@ a=rtcp-fb:96 nack
896896
runTest(t, true)
897897
})
898898
}
899+
900+
func TestAutoConfigRTXCodecs(t *testing.T) {
901+
for _, test := range []struct {
902+
Original []RTPCodecParameters
903+
ExpectedResult []RTPCodecParameters
904+
ExpectedError error
905+
}{
906+
{
907+
// no video codec
908+
Original: []RTPCodecParameters{
909+
{
910+
PayloadType: 1,
911+
RTPCodecCapability: RTPCodecCapability{
912+
MimeType: MimeTypeFlexFEC03,
913+
ClockRate: 90000,
914+
Channels: 0,
915+
SDPFmtpLine: "repair-window=10000000",
916+
RTCPFeedback: nil,
917+
},
918+
},
919+
},
920+
ExpectedResult: []RTPCodecParameters{
921+
{
922+
PayloadType: 1,
923+
RTPCodecCapability: RTPCodecCapability{
924+
MimeType: MimeTypeFlexFEC03,
925+
ClockRate: 90000,
926+
Channels: 0,
927+
SDPFmtpLine: "repair-window=10000000",
928+
RTCPFeedback: nil,
929+
},
930+
},
931+
},
932+
ExpectedError: nil,
933+
},
934+
{
935+
// one video codec with no nack rtcp feedback
936+
Original: []RTPCodecParameters{
937+
{
938+
PayloadType: 1,
939+
RTPCodecCapability: RTPCodecCapability{
940+
MimeType: MimeTypeH265,
941+
ClockRate: 90000,
942+
Channels: 0,
943+
SDPFmtpLine: "",
944+
RTCPFeedback: nil,
945+
},
946+
},
947+
},
948+
ExpectedResult: []RTPCodecParameters{
949+
{
950+
PayloadType: 1,
951+
RTPCodecCapability: RTPCodecCapability{
952+
MimeType: MimeTypeH265,
953+
ClockRate: 90000,
954+
Channels: 0,
955+
SDPFmtpLine: "",
956+
RTCPFeedback: nil,
957+
},
958+
},
959+
},
960+
ExpectedError: nil,
961+
},
962+
{
963+
// one video codec with nack and pli rtcp feedback
964+
Original: []RTPCodecParameters{
965+
{
966+
PayloadType: 1,
967+
RTPCodecCapability: RTPCodecCapability{
968+
MimeType: MimeTypeH265,
969+
ClockRate: 90000,
970+
Channels: 0,
971+
SDPFmtpLine: "",
972+
RTCPFeedback: []RTCPFeedback{
973+
{Type: "nack", Parameter: ""},
974+
{Type: "nack", Parameter: "pli"},
975+
},
976+
},
977+
},
978+
},
979+
ExpectedResult: []RTPCodecParameters{
980+
{
981+
PayloadType: 1,
982+
RTPCodecCapability: RTPCodecCapability{
983+
MimeType: MimeTypeH265,
984+
ClockRate: 90000,
985+
Channels: 0,
986+
SDPFmtpLine: "",
987+
RTCPFeedback: []RTCPFeedback{
988+
{Type: "nack", Parameter: ""},
989+
{Type: "nack", Parameter: "pli"},
990+
},
991+
},
992+
},
993+
{
994+
PayloadType: 2,
995+
RTPCodecCapability: RTPCodecCapability{
996+
MimeType: MimeTypeRTX,
997+
ClockRate: 90000,
998+
Channels: 0,
999+
SDPFmtpLine: "apt=1",
1000+
RTCPFeedback: nil,
1001+
},
1002+
},
1003+
},
1004+
ExpectedError: nil,
1005+
},
1006+
{
1007+
// multiple video codec, expect error because of PayloadType collision
1008+
Original: []RTPCodecParameters{
1009+
{
1010+
PayloadType: 1,
1011+
RTPCodecCapability: RTPCodecCapability{
1012+
MimeType: MimeTypeH265,
1013+
ClockRate: 90000,
1014+
Channels: 0,
1015+
SDPFmtpLine: "",
1016+
RTCPFeedback: []RTCPFeedback{
1017+
{Type: "nack", Parameter: ""},
1018+
{Type: "nack", Parameter: "pli"},
1019+
},
1020+
},
1021+
},
1022+
{
1023+
PayloadType: 2,
1024+
RTPCodecCapability: RTPCodecCapability{
1025+
MimeType: MimeTypeVP8,
1026+
ClockRate: 90000,
1027+
Channels: 0,
1028+
SDPFmtpLine: "",
1029+
RTCPFeedback: []RTCPFeedback{
1030+
{Type: "nack", Parameter: ""},
1031+
{Type: "nack", Parameter: "pli"},
1032+
},
1033+
},
1034+
},
1035+
},
1036+
ExpectedResult: nil,
1037+
ExpectedError: ErrCodecAlreadyRegistered,
1038+
},
1039+
} {
1040+
m := &MediaEngine{
1041+
videoCodecs: test.Original,
1042+
}
1043+
err := m.autoConfigRTXCodecs()
1044+
assert.Equal(t, err, test.ExpectedError)
1045+
if err == nil {
1046+
for i := range m.videoCodecs {
1047+
// ignore for following assert
1048+
m.videoCodecs[i].statsID = ""
1049+
}
1050+
assert.Equal(t, m.videoCodecs, test.ExpectedResult)
1051+
}
1052+
}
1053+
}

peerconnection.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,8 +142,9 @@ func (api *API) NewPeerConnection(configuration Configuration) (*PeerConnection,
142142
pc.iceConnectionState.Store(ICEConnectionStateNew)
143143
pc.connectionState.Store(PeerConnectionStateNew)
144144

145-
i, err := api.interceptorRegistry.Build("")
146-
if err != nil {
145+
var i interceptor.Interceptor
146+
var err error
147+
if i, err = api.interceptorRegistry.Build(""); err != nil {
147148
return nil, err
148149
}
149150

@@ -152,6 +153,12 @@ func (api *API) NewPeerConnection(configuration Configuration) (*PeerConnection,
152153
interceptor: i,
153154
}
154155

156+
if api.settingEngine.autoConfigRTXCodec {
157+
if err = api.mediaEngine.autoConfigRTXCodecs(); err != nil {
158+
return nil, err
159+
}
160+
}
161+
155162
if api.settingEngine.disableMediaEngineCopy {
156163
pc.api.mediaEngine = api.mediaEngine
157164
} else {

settingengine.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ type SettingEngine struct {
105105
fireOnTrackBeforeFirstRTP bool
106106
disableCloseByDTLS bool
107107
dataChannelBlockWrite bool
108+
autoConfigRTXCodec bool
108109
}
109110

110111
func (e *SettingEngine) getSCTPMaxMessageSize() uint32 {
@@ -540,3 +541,8 @@ func (e *SettingEngine) SetFireOnTrackBeforeFirstRTP(fireOnTrackBeforeFirstRTP b
540541
func (e *SettingEngine) DisableCloseByDTLS(isEnabled bool) {
541542
e.disableCloseByDTLS = isEnabled
542543
}
544+
545+
// AutoConfigRTXCodec sets if the RTX codec should be automatically configured.
546+
func (e *SettingEngine) AutoConfigRTXCodec(autoConfigRTXCodec bool) {
547+
e.autoConfigRTXCodec = autoConfigRTXCodec
548+
}

0 commit comments

Comments
 (0)