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
|
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
|
// RegisterHeaderExtension adds a header extension to the MediaEngine
|
||||||
// To determine the negotiated value use `GetHeaderExtensionID` after signaling is complete.
|
// 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)
|
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.iceConnectionState.Store(ICEConnectionStateNew)
|
||||||
pc.connectionState.Store(PeerConnectionStateNew)
|
pc.connectionState.Store(PeerConnectionStateNew)
|
||||||
|
|
||||||
i, err := api.interceptorRegistry.Build("")
|
var i interceptor.Interceptor
|
||||||
if err != nil {
|
var err error
|
||||||
|
if i, err = api.interceptorRegistry.Build(""); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -152,6 +153,12 @@ func (api *API) NewPeerConnection(configuration Configuration) (*PeerConnection,
|
|||||||
interceptor: i,
|
interceptor: i,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if api.settingEngine.autoConfigRTXCodec {
|
||||||
|
if err = api.mediaEngine.autoConfigRTXCodecs(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if api.settingEngine.disableMediaEngineCopy {
|
if api.settingEngine.disableMediaEngineCopy {
|
||||||
pc.api.mediaEngine = api.mediaEngine
|
pc.api.mediaEngine = api.mediaEngine
|
||||||
} else {
|
} else {
|
||||||
|
@@ -106,6 +106,7 @@ type SettingEngine struct {
|
|||||||
fireOnTrackBeforeFirstRTP bool
|
fireOnTrackBeforeFirstRTP bool
|
||||||
disableCloseByDTLS bool
|
disableCloseByDTLS bool
|
||||||
dataChannelBlockWrite bool
|
dataChannelBlockWrite bool
|
||||||
|
autoConfigRTXCodec bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *SettingEngine) getSCTPMaxMessageSize() uint32 {
|
func (e *SettingEngine) getSCTPMaxMessageSize() uint32 {
|
||||||
@@ -551,3 +552,8 @@ func (e *SettingEngine) SetFireOnTrackBeforeFirstRTP(fireOnTrackBeforeFirstRTP b
|
|||||||
func (e *SettingEngine) DisableCloseByDTLS(isEnabled bool) {
|
func (e *SettingEngine) DisableCloseByDTLS(isEnabled bool) {
|
||||||
e.disableCloseByDTLS = isEnabled
|
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)
|
assert.ErrorIs(t, err, context.DeadlineExceeded)
|
||||||
closePairNow(t, offer, answer)
|
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