mirror of
https://github.com/pion/webrtc.git
synced 2025-09-27 03:25:58 +08:00
Add opt control transceiver re-use in recvonly
SetDisableTransceiverReuseInRecvonly controls if a transceiver is re-used when its current direction is `recvonly`. This is useful for the following scenario - Remote side sends `offer` with `sendonly` media section. - Local side creates transceiver in `SetRemoteDescription` and sets direction to `recvonly. - Local side calls `AddTrack`. - As the current direction is `recvonly`, the transceiver added above will be re-used. That will set the direction to `sendrecv` and the generated `answer` will have `sendrecv` for that media section. - That answer becomes incompatible as the offerer is using `sendonly`. Note that local transceiver will be in `recvonly` for both `sendrecv` and `sendonly` directions in the media section. If the `offer` did use `sendrecv`, it is possible to re-use that transceiver for sending. So, disabling re-use will prohibit re-use in the `sendrecv` case also and hence is slightly wasteful.
This commit is contained in:

committed by
Raja Subramanian

parent
3e84081c87
commit
2299a71701
@@ -2105,7 +2105,8 @@ func (pc *PeerConnection) AddTrack(track TrackLocal) (*RTPSender, error) {
|
|||||||
// But that will cause sdp inflate. So we only check currentDirection's current value,
|
// But that will cause sdp inflate. So we only check currentDirection's current value,
|
||||||
// that's worked for all browsers.
|
// that's worked for all browsers.
|
||||||
if transceiver.kind == track.Kind() && transceiver.Sender() == nil &&
|
if transceiver.kind == track.Kind() && transceiver.Sender() == nil &&
|
||||||
currentDirection != RTPTransceiverDirectionSendrecv && currentDirection != RTPTransceiverDirectionSendonly {
|
currentDirection != RTPTransceiverDirectionSendrecv && currentDirection != RTPTransceiverDirectionSendonly &&
|
||||||
|
(!pc.api.settingEngine.disableTransceiverReuseInRecvonly || currentDirection != RTPTransceiverDirectionRecvonly) {
|
||||||
sender, err := pc.api.NewRTPSender(track, pc.dtlsTransport)
|
sender, err := pc.api.NewRTPSender(track, pc.dtlsTransport)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
err = transceiver.SetSender(sender, track)
|
err = transceiver.SetSender(sender, track)
|
||||||
|
@@ -707,41 +707,95 @@ func TestAddTransceiverFromTrackSendRecv(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestAddTransceiverAddTrack_Reuse(t *testing.T) {
|
func TestAddTransceiverAddTrack_Reuse(t *testing.T) {
|
||||||
pc, err := NewPeerConnection(Configuration{})
|
t.Run("reuse test", func(t *testing.T) {
|
||||||
assert.NoError(t, err)
|
pc, err := NewPeerConnection(Configuration{})
|
||||||
|
|
||||||
tr, err := pc.AddTransceiverFromKind(
|
|
||||||
RTPCodecTypeVideo,
|
|
||||||
RTPTransceiverInit{Direction: RTPTransceiverDirectionRecvonly},
|
|
||||||
)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
assert.Equal(t, []*RTPTransceiver{tr}, pc.GetTransceivers())
|
|
||||||
|
|
||||||
addTrack := func() (TrackLocal, *RTPSender) {
|
|
||||||
track, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "foo", "bar")
|
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
sender, err := pc.AddTrack(track)
|
tr, err := pc.AddTransceiverFromKind(
|
||||||
|
RTPCodecTypeVideo,
|
||||||
|
RTPTransceiverInit{Direction: RTPTransceiverDirectionRecvonly},
|
||||||
|
)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
return track, sender
|
assert.Equal(t, []*RTPTransceiver{tr}, pc.GetTransceivers())
|
||||||
}
|
|
||||||
|
|
||||||
track1, sender1 := addTrack()
|
addTrack := func() (TrackLocal, *RTPSender) {
|
||||||
assert.Equal(t, 1, len(pc.GetTransceivers()))
|
track, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "foo", "bar")
|
||||||
assert.Equal(t, sender1, tr.Sender())
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, track1, tr.Sender().Track())
|
|
||||||
require.NoError(t, pc.RemoveTrack(sender1))
|
|
||||||
|
|
||||||
track2, _ := addTrack()
|
sender, err := pc.AddTrack(track)
|
||||||
assert.Equal(t, 1, len(pc.GetTransceivers()))
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, track2, tr.Sender().Track())
|
|
||||||
|
|
||||||
addTrack()
|
return track, sender
|
||||||
assert.Equal(t, 2, len(pc.GetTransceivers()))
|
}
|
||||||
|
|
||||||
assert.NoError(t, pc.Close())
|
track1, sender1 := addTrack()
|
||||||
|
assert.Equal(t, 1, len(pc.GetTransceivers()))
|
||||||
|
assert.Equal(t, sender1, tr.Sender())
|
||||||
|
assert.Equal(t, track1, tr.Sender().Track())
|
||||||
|
require.NoError(t, pc.RemoveTrack(sender1))
|
||||||
|
|
||||||
|
track2, _ := addTrack()
|
||||||
|
assert.Equal(t, 1, len(pc.GetTransceivers()))
|
||||||
|
assert.Equal(t, track2, tr.Sender().Track())
|
||||||
|
|
||||||
|
addTrack()
|
||||||
|
assert.Equal(t, 2, len(pc.GetTransceivers()))
|
||||||
|
|
||||||
|
assert.NoError(t, pc.Close())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("reuse disable test", func(t *testing.T) {
|
||||||
|
se := SettingEngine{}
|
||||||
|
se.SetDisableTransceiverReuseInRecvonly(true)
|
||||||
|
pc, err := NewAPI(WithSettingEngine(se)).NewPeerConnection(Configuration{})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
tr, err := pc.AddTransceiverFromKind(
|
||||||
|
RTPCodecTypeVideo,
|
||||||
|
RTPTransceiverInit{Direction: RTPTransceiverDirectionRecvonly},
|
||||||
|
)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
assert.Equal(t, []*RTPTransceiver{tr}, pc.GetTransceivers())
|
||||||
|
|
||||||
|
addTrack := func() (TrackLocal, *RTPSender) {
|
||||||
|
track, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "foo", "bar")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
sender, err := pc.AddTrack(track)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
return track, sender
|
||||||
|
}
|
||||||
|
|
||||||
|
// force direction to `recvonly` and ensure SettingEngine setting disables re-use
|
||||||
|
tr.setCurrentDirection(RTPTransceiverDirectionRecvonly)
|
||||||
|
addTrack()
|
||||||
|
assert.Equal(t, 2, len(pc.GetTransceivers()))
|
||||||
|
|
||||||
|
// the newly added transceiver above will have a sender, so not re-usable
|
||||||
|
_, sender := addTrack()
|
||||||
|
assert.Equal(t, 3, len(pc.GetTransceivers()))
|
||||||
|
|
||||||
|
// remove last added track to make that transceiver re-usable
|
||||||
|
require.NoError(t, pc.RemoveTrack(sender))
|
||||||
|
|
||||||
|
track, sender := addTrack()
|
||||||
|
assert.Equal(t, 3, len(pc.GetTransceivers()))
|
||||||
|
var matchedTransceiver *RTPTransceiver
|
||||||
|
for _, tr := range pc.GetTransceivers() {
|
||||||
|
if tr.Sender() == sender {
|
||||||
|
matchedTransceiver = tr
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert.NotNil(t, matchedTransceiver)
|
||||||
|
assert.Equal(t, track, matchedTransceiver.Sender().Track())
|
||||||
|
|
||||||
|
assert.NoError(t, pc.Close())
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAddTransceiverAddTrack_NewRTPSender_Error(t *testing.T) {
|
func TestAddTransceiverAddTrack_NewRTPSender_Error(t *testing.T) {
|
||||||
|
@@ -110,6 +110,7 @@ type SettingEngine struct {
|
|||||||
disableCloseByDTLS bool
|
disableCloseByDTLS bool
|
||||||
dataChannelBlockWrite bool
|
dataChannelBlockWrite bool
|
||||||
handleUndeclaredSSRCWithoutAnswer bool
|
handleUndeclaredSSRCWithoutAnswer bool
|
||||||
|
disableTransceiverReuseInRecvonly bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *SettingEngine) getSCTPMaxMessageSize() uint32 {
|
func (e *SettingEngine) getSCTPMaxMessageSize() uint32 {
|
||||||
@@ -577,3 +578,23 @@ func (e *SettingEngine) DisableCloseByDTLS(isEnabled bool) {
|
|||||||
func (e *SettingEngine) SetHandleUndeclaredSSRCWithoutAnswer(handleUndeclaredSSRCWithoutAnswer bool) {
|
func (e *SettingEngine) SetHandleUndeclaredSSRCWithoutAnswer(handleUndeclaredSSRCWithoutAnswer bool) {
|
||||||
e.handleUndeclaredSSRCWithoutAnswer = handleUndeclaredSSRCWithoutAnswer
|
e.handleUndeclaredSSRCWithoutAnswer = handleUndeclaredSSRCWithoutAnswer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetDisableTransceiverReuseInRecvonly controls if a transceiver is re-used
|
||||||
|
// when its current direction is `recvonly`.
|
||||||
|
//
|
||||||
|
// This is useful for the following scenario
|
||||||
|
// - Remote side sends `offer` with `sendonly` media section.
|
||||||
|
// - Local side creates transceiver in `SetRemoteDescription` and sets direction to `recvonly`.
|
||||||
|
// - Local side calls `AddTrack`.
|
||||||
|
// - As the current direction is `recvonly`, the transceiver added above will be re-used.
|
||||||
|
// That will set the direction to `sendrecv` and the generated `answer` will have `sendrecv`
|
||||||
|
// for that media section.
|
||||||
|
// - That answer becomes incompatible as the offerer is using `sendonly`.
|
||||||
|
//
|
||||||
|
// Note that local transceiver will be in `recvonly` for both `sendrecv` and `sendonly` directions
|
||||||
|
// in the media section. If the `offer` did use `sendrecv`, it is possible to re-use that transceiver
|
||||||
|
// for sending. So, disabling re-use will prohibit re-use in the `sendrecv` case also and hence is
|
||||||
|
// slightly wasteful.
|
||||||
|
func (e *SettingEngine) SetDisableTransceiverReuseInRecvonly(disableTransceiverReuseInRecvonly bool) {
|
||||||
|
e.disableTransceiverReuseInRecvonly = disableTransceiverReuseInRecvonly
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user