mirror of
https://github.com/pion/webrtc.git
synced 2025-09-26 19:21:12 +08:00
Automatically configure RTX codecs
This commit is contained in:
@@ -273,6 +273,44 @@ func (m *MediaEngine) RegisterCodec(codec RTPCodecParameters, typ RTPCodecType)
|
||||
return err
|
||||
}
|
||||
|
||||
func (m *MediaEngine) autoConfigRTXCodecs() error {
|
||||
additionalRTXCodecs := []RTPCodecParameters{}
|
||||
for _, codec := range m.videoCodecs {
|
||||
// ignore FEC & RTX
|
||||
if strings.Contains(codec.MimeType, MimeTypeFlexFEC) || codec.MimeType == MimeTypeRTX {
|
||||
continue
|
||||
}
|
||||
haveNACK := false
|
||||
for _, fb := range codec.RTCPFeedback {
|
||||
if fb.Type == "nack" {
|
||||
haveNACK = true
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
if haveNACK {
|
||||
additionalRTXCodecs = append(additionalRTXCodecs, RTPCodecParameters{
|
||||
RTPCodecCapability: RTPCodecCapability{
|
||||
MimeType: MimeTypeRTX,
|
||||
ClockRate: 90000,
|
||||
Channels: 0,
|
||||
SDPFmtpLine: fmt.Sprintf("apt=%d", codec.PayloadType),
|
||||
RTCPFeedback: nil,
|
||||
},
|
||||
PayloadType: codec.PayloadType + 1,
|
||||
})
|
||||
}
|
||||
}
|
||||
for i := range additionalRTXCodecs {
|
||||
err := m.RegisterCodec(additionalRTXCodecs[i], RTPCodecTypeVideo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RegisterHeaderExtension adds a header extension to the MediaEngine
|
||||
// To determine the negotiated value use `GetHeaderExtensionID` after signaling is complete.
|
||||
//
|
||||
|
@@ -961,3 +961,158 @@ a=ssrc:4281768245 msid:6ff05509-be96-4ef1-a74f-425e14720983 16d5d7fe-d076-4718-9
|
||||
assert.Len(t, mediaEngine.negotiatedVideoCodecs, 2)
|
||||
})
|
||||
}
|
||||
|
||||
func TestAutoConfigRTXCodecs(t *testing.T) {
|
||||
for _, test := range []struct {
|
||||
Original []RTPCodecParameters
|
||||
ExpectedResult []RTPCodecParameters
|
||||
ExpectedError error
|
||||
}{
|
||||
{
|
||||
// no video codec
|
||||
Original: []RTPCodecParameters{
|
||||
{
|
||||
PayloadType: 1,
|
||||
RTPCodecCapability: RTPCodecCapability{
|
||||
MimeType: MimeTypeFlexFEC03,
|
||||
ClockRate: 90000,
|
||||
Channels: 0,
|
||||
SDPFmtpLine: "repair-window=10000000",
|
||||
RTCPFeedback: nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
ExpectedResult: []RTPCodecParameters{
|
||||
{
|
||||
PayloadType: 1,
|
||||
RTPCodecCapability: RTPCodecCapability{
|
||||
MimeType: MimeTypeFlexFEC03,
|
||||
ClockRate: 90000,
|
||||
Channels: 0,
|
||||
SDPFmtpLine: "repair-window=10000000",
|
||||
RTCPFeedback: nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
ExpectedError: nil,
|
||||
},
|
||||
{
|
||||
// one video codec with no nack rtcp feedback
|
||||
Original: []RTPCodecParameters{
|
||||
{
|
||||
PayloadType: 1,
|
||||
RTPCodecCapability: RTPCodecCapability{
|
||||
MimeType: MimeTypeH265,
|
||||
ClockRate: 90000,
|
||||
Channels: 0,
|
||||
SDPFmtpLine: "",
|
||||
RTCPFeedback: nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
ExpectedResult: []RTPCodecParameters{
|
||||
{
|
||||
PayloadType: 1,
|
||||
RTPCodecCapability: RTPCodecCapability{
|
||||
MimeType: MimeTypeH265,
|
||||
ClockRate: 90000,
|
||||
Channels: 0,
|
||||
SDPFmtpLine: "",
|
||||
RTCPFeedback: nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
ExpectedError: nil,
|
||||
},
|
||||
{
|
||||
// one video codec with nack and pli rtcp feedback
|
||||
Original: []RTPCodecParameters{
|
||||
{
|
||||
PayloadType: 1,
|
||||
RTPCodecCapability: RTPCodecCapability{
|
||||
MimeType: MimeTypeH265,
|
||||
ClockRate: 90000,
|
||||
Channels: 0,
|
||||
SDPFmtpLine: "",
|
||||
RTCPFeedback: []RTCPFeedback{
|
||||
{Type: "nack", Parameter: ""},
|
||||
{Type: "nack", Parameter: "pli"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
ExpectedResult: []RTPCodecParameters{
|
||||
{
|
||||
PayloadType: 1,
|
||||
RTPCodecCapability: RTPCodecCapability{
|
||||
MimeType: MimeTypeH265,
|
||||
ClockRate: 90000,
|
||||
Channels: 0,
|
||||
SDPFmtpLine: "",
|
||||
RTCPFeedback: []RTCPFeedback{
|
||||
{Type: "nack", Parameter: ""},
|
||||
{Type: "nack", Parameter: "pli"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
PayloadType: 2,
|
||||
RTPCodecCapability: RTPCodecCapability{
|
||||
MimeType: MimeTypeRTX,
|
||||
ClockRate: 90000,
|
||||
Channels: 0,
|
||||
SDPFmtpLine: "apt=1",
|
||||
RTCPFeedback: nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
ExpectedError: nil,
|
||||
},
|
||||
{
|
||||
// multiple video codec, expect error because of PayloadType collision
|
||||
Original: []RTPCodecParameters{
|
||||
{
|
||||
PayloadType: 1,
|
||||
RTPCodecCapability: RTPCodecCapability{
|
||||
MimeType: MimeTypeH265,
|
||||
ClockRate: 90000,
|
||||
Channels: 0,
|
||||
SDPFmtpLine: "",
|
||||
RTCPFeedback: []RTCPFeedback{
|
||||
{Type: "nack", Parameter: ""},
|
||||
{Type: "nack", Parameter: "pli"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
PayloadType: 2,
|
||||
RTPCodecCapability: RTPCodecCapability{
|
||||
MimeType: MimeTypeVP8,
|
||||
ClockRate: 90000,
|
||||
Channels: 0,
|
||||
SDPFmtpLine: "",
|
||||
RTCPFeedback: []RTCPFeedback{
|
||||
{Type: "nack", Parameter: ""},
|
||||
{Type: "nack", Parameter: "pli"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
ExpectedResult: nil,
|
||||
ExpectedError: ErrCodecAlreadyRegistered,
|
||||
},
|
||||
} {
|
||||
m := &MediaEngine{
|
||||
videoCodecs: test.Original,
|
||||
}
|
||||
err := m.autoConfigRTXCodecs()
|
||||
assert.Equal(t, err, test.ExpectedError)
|
||||
if err == nil {
|
||||
for i := range m.videoCodecs {
|
||||
// ignore for following assert
|
||||
m.videoCodecs[i].statsID = ""
|
||||
}
|
||||
assert.Equal(t, m.videoCodecs, test.ExpectedResult)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -142,8 +142,9 @@ func (api *API) NewPeerConnection(configuration Configuration) (*PeerConnection,
|
||||
pc.iceConnectionState.Store(ICEConnectionStateNew)
|
||||
pc.connectionState.Store(PeerConnectionStateNew)
|
||||
|
||||
i, err := api.interceptorRegistry.Build("")
|
||||
if err != nil {
|
||||
var i interceptor.Interceptor
|
||||
var err error
|
||||
if i, err = api.interceptorRegistry.Build(""); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -152,6 +153,12 @@ func (api *API) NewPeerConnection(configuration Configuration) (*PeerConnection,
|
||||
interceptor: i,
|
||||
}
|
||||
|
||||
if api.settingEngine.autoConfigRTXCodec {
|
||||
if err = api.mediaEngine.autoConfigRTXCodecs(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if api.settingEngine.disableMediaEngineCopy {
|
||||
pc.api.mediaEngine = api.mediaEngine
|
||||
} else {
|
||||
|
@@ -106,6 +106,7 @@ type SettingEngine struct {
|
||||
fireOnTrackBeforeFirstRTP bool
|
||||
disableCloseByDTLS bool
|
||||
dataChannelBlockWrite bool
|
||||
autoConfigRTXCodec bool
|
||||
}
|
||||
|
||||
func (e *SettingEngine) getSCTPMaxMessageSize() uint32 {
|
||||
@@ -551,3 +552,8 @@ func (e *SettingEngine) SetFireOnTrackBeforeFirstRTP(fireOnTrackBeforeFirstRTP b
|
||||
func (e *SettingEngine) DisableCloseByDTLS(isEnabled bool) {
|
||||
e.disableCloseByDTLS = isEnabled
|
||||
}
|
||||
|
||||
// AutoConfigRTXCodec sets if the RTX codec should be automatically configured.
|
||||
func (e *SettingEngine) AutoConfigRTXCodec(autoConfigRTXCodec bool) {
|
||||
e.autoConfigRTXCodec = autoConfigRTXCodec
|
||||
}
|
||||
|
@@ -464,3 +464,58 @@ func TestEnableDataChannelBlockWrite(t *testing.T) {
|
||||
assert.ErrorIs(t, err, context.DeadlineExceeded)
|
||||
closePairNow(t, offer, answer)
|
||||
}
|
||||
|
||||
func TestAutoConfigRTXCodec(t *testing.T) {
|
||||
lim := test.TimeOut(time.Second * 30)
|
||||
defer lim.Stop()
|
||||
|
||||
report := test.CheckRoutines(t)
|
||||
defer report()
|
||||
|
||||
settingEngine := SettingEngine{}
|
||||
settingEngine.DisableMediaEngineCopy(true)
|
||||
settingEngine.AutoConfigRTXCodec(true)
|
||||
mediaEngine := &MediaEngine{}
|
||||
err := mediaEngine.RegisterCodec(
|
||||
RTPCodecParameters{
|
||||
PayloadType: 96,
|
||||
RTPCodecCapability: RTPCodecCapability{
|
||||
MimeType: MimeTypeVP8,
|
||||
ClockRate: 90000,
|
||||
},
|
||||
},
|
||||
RTPCodecTypeVideo,
|
||||
)
|
||||
assert.Equal(t, err, nil)
|
||||
api := NewAPI(
|
||||
WithMediaEngine(mediaEngine),
|
||||
WithSettingEngine(settingEngine),
|
||||
)
|
||||
config := Configuration{
|
||||
ICEServers: []ICEServer{
|
||||
{
|
||||
URLs: []string{"stun:stun.l.google.com:19302"},
|
||||
},
|
||||
},
|
||||
}
|
||||
var pc *PeerConnection
|
||||
pc, err = api.NewPeerConnection(config)
|
||||
assert.Equal(t, err, nil)
|
||||
for i := range mediaEngine.videoCodecs {
|
||||
mediaEngine.videoCodecs[i].statsID = ""
|
||||
}
|
||||
assert.Equal(t, len(mediaEngine.videoCodecs), 2)
|
||||
assert.Equal(t, mediaEngine.videoCodecs[1],
|
||||
RTPCodecParameters{
|
||||
PayloadType: 97,
|
||||
RTPCodecCapability: RTPCodecCapability{
|
||||
MimeType: MimeTypeRTX,
|
||||
ClockRate: 90000,
|
||||
Channels: 0,
|
||||
SDPFmtpLine: "apt=96",
|
||||
RTCPFeedback: nil,
|
||||
},
|
||||
},
|
||||
)
|
||||
assert.NoError(t, pc.close(true))
|
||||
}
|
||||
|
Reference in New Issue
Block a user