Disable MediaEngine Copy by Default

If an API is shared between PeerConnections they would use the same
MediaEngine. A MediaEngine contains negotiated PayloadTypes so if the
PeerConnections were answering you would end up in invalid states.

Add DisableMediaEngineCopy to SettingEngine in case user needs old
behavior.

Resolves #1662
This commit is contained in:
Sean DuBois
2021-02-09 21:23:31 -08:00
parent b5fa979c08
commit c8b7aa386a
4 changed files with 65 additions and 0 deletions

View File

@@ -271,6 +271,16 @@ func (m *MediaEngine) getHeaderExtensionID(extension RTPHeaderExtensionCapabilit
return return
} }
// copy copies any user modifiable state of the MediaEngine
// all internal state is reset
func (m *MediaEngine) copy() *MediaEngine {
return &MediaEngine{
videoCodecs: append([]RTPCodecParameters{}, m.videoCodecs...),
audioCodecs: append([]RTPCodecParameters{}, m.audioCodecs...),
headerExtensions: append([]mediaEngineHeaderExtension{}, m.headerExtensions...),
}
}
func (m *MediaEngine) getCodecByPayload(payloadType PayloadType) (RTPCodecParameters, RTPCodecType, error) { func (m *MediaEngine) getCodecByPayload(payloadType PayloadType) (RTPCodecParameters, RTPCodecType, error) {
for _, codec := range m.negotiatedVideoCodecs { for _, codec := range m.negotiatedVideoCodecs {
if codec.PayloadType == payloadType { if codec.PayloadType == payloadType {

View File

@@ -105,6 +105,10 @@ func NewPeerConnection(configuration Configuration) (*PeerConnection, error) {
// NewPeerConnection creates a new PeerConnection with the provided configuration against the received API object // NewPeerConnection creates a new PeerConnection with the provided configuration against the received API object
func (api *API) NewPeerConnection(configuration Configuration) (*PeerConnection, error) { func (api *API) NewPeerConnection(configuration Configuration) (*PeerConnection, error) {
if !api.settingEngine.disableMediaEngineCopy {
api.mediaEngine = api.mediaEngine.copy()
}
// https://w3c.github.io/webrtc-pc/#constructor (Step #2) // https://w3c.github.io/webrtc-pc/#constructor (Step #2)
// Some variables defined explicitly despite their implicit zero values to // Some variables defined explicitly despite their implicit zero values to
// allow better readability to understand what is happening. // allow better readability to understand what is happening.

View File

@@ -59,6 +59,7 @@ type SettingEngine struct {
LoggerFactory logging.LoggerFactory LoggerFactory logging.LoggerFactory
iceTCPMux ice.TCPMux iceTCPMux ice.TCPMux
iceProxyDialer proxy.Dialer iceProxyDialer proxy.Dialer
disableMediaEngineCopy bool
} }
// DetachDataChannels enables detaching data channels. When enabled // DetachDataChannels enables detaching data channels. When enabled
@@ -255,3 +256,10 @@ func (e *SettingEngine) SetICETCPMux(tcpMux ice.TCPMux) {
func (e *SettingEngine) SetICEProxyDialer(d proxy.Dialer) { func (e *SettingEngine) SetICEProxyDialer(d proxy.Dialer) {
e.iceProxyDialer = d e.iceProxyDialer = d
} }
// DisableMediaEngineCopy stops the MediaEngine from being copied. This allows a user to modify
// the MediaEngine after the PeerConnection has been constructed. This is useful if you wish to
// modify codecs after signaling. Make sure not to share MediaEngines between PeerConnections.
func (e *SettingEngine) DisableMediaEngineCopy(isDisabled bool) {
e.disableMediaEngineCopy = isDisabled
}

View File

@@ -139,3 +139,46 @@ func TestSettingEngine_SetICETCP(t *testing.T) {
assert.Equal(t, tcpMux, settingEngine.iceTCPMux) assert.Equal(t, tcpMux, settingEngine.iceTCPMux)
} }
func TestSettingEngine_SetDisableMediaEngineCopy(t *testing.T) {
t.Run("Copy", func(t *testing.T) {
m := &MediaEngine{}
assert.NoError(t, m.RegisterDefaultCodecs())
offerer, answerer, err := NewAPI(WithMediaEngine(m)).newPair(Configuration{})
assert.NoError(t, err)
_, err = offerer.AddTransceiverFromKind(RTPCodecTypeVideo)
assert.NoError(t, err)
assert.NoError(t, signalPair(offerer, answerer))
assert.False(t, m.negotiatedVideo)
assert.Empty(t, m.negotiatedVideoCodecs)
assert.NoError(t, offerer.Close())
assert.NoError(t, answerer.Close())
})
t.Run("No Copy", func(t *testing.T) {
m := &MediaEngine{}
assert.NoError(t, m.RegisterDefaultCodecs())
s := SettingEngine{}
s.DisableMediaEngineCopy(true)
offerer, answerer, err := NewAPI(WithMediaEngine(m), WithSettingEngine(s)).newPair(Configuration{})
assert.NoError(t, err)
_, err = offerer.AddTransceiverFromKind(RTPCodecTypeVideo)
assert.NoError(t, err)
assert.NoError(t, signalPair(offerer, answerer))
assert.True(t, m.negotiatedVideo)
assert.NotEmpty(t, m.negotiatedVideoCodecs)
assert.NoError(t, offerer.Close())
assert.NoError(t, answerer.Close())
})
}