Don't reuse transceiver in one round negotiation

Should not reuse transceiver (remove & add track)
in one round negotiation, it cause the transceiver
changes ssrc/id without transit to inactive and the
remote peer connection can't fire track close and
OnTrack event.
This commit is contained in:
cnderrauber
2024-09-06 22:25:01 +08:00
committed by cnderrauber
parent 9a71f6981f
commit e6a86e74b9
2 changed files with 58 additions and 8 deletions

View File

@@ -1118,10 +1118,18 @@ func (pc *PeerConnection) SetRemoteDescription(desc SessionDescription) error {
case direction == RTPTransceiverDirectionRecvonly:
if t.Direction() == RTPTransceiverDirectionSendrecv {
t.setDirection(RTPTransceiverDirectionSendonly)
} else if t.Direction() == RTPTransceiverDirectionRecvonly {
t.setDirection(RTPTransceiverDirectionInactive)
}
case direction == RTPTransceiverDirectionSendrecv:
if t.Direction() == RTPTransceiverDirectionSendonly {
t.setDirection(RTPTransceiverDirectionSendrecv)
} else if t.Direction() == RTPTransceiverDirectionInactive {
t.setDirection(RTPTransceiverDirectionRecvonly)
}
case direction == RTPTransceiverDirectionSendonly:
if t.Direction() == RTPTransceiverDirectionInactive {
t.setDirection(RTPTransceiverDirectionRecvonly)
}
}
@@ -1288,7 +1296,7 @@ func setRTPTransceiverCurrentDirection(answer *SessionDescription, currentTransc
// If a transceiver is created by applying a remote description that has recvonly transceiver,
// it will have no sender. In this case, the transceiver's current direction is set to inactive so
// that the transceiver can be reused by next AddTrack.
if direction == RTPTransceiverDirectionSendonly && t.Sender() == nil {
if !weOffer && direction == RTPTransceiverDirectionSendonly && t.Sender() == nil {
direction = RTPTransceiverDirectionInactive
}
@@ -1340,8 +1348,8 @@ func (pc *PeerConnection) configureRTPReceivers(isRenegotiation bool, remoteDesc
mid := t.Mid()
receiverNeedsStopped := false
func() {
for _, t := range tracks {
for _, track := range tracks {
func(t *TrackRemote) {
t.mu.Lock()
defer t.mu.Unlock()
@@ -1349,19 +1357,19 @@ func (pc *PeerConnection) configureRTPReceivers(isRenegotiation bool, remoteDesc
if details := trackDetailsForRID(incomingTracks, mid, t.rid); details != nil {
t.id = details.id
t.streamID = details.streamID
continue
return
}
} else if t.ssrc != 0 {
if details := trackDetailsForSSRC(incomingTracks, t.ssrc); details != nil {
t.id = details.id
t.streamID = details.streamID
continue
return
}
}
receiverNeedsStopped = true
}
}()
}(track)
}
if !receiverNeedsStopped {
continue

View File

@@ -1160,12 +1160,15 @@ func TestPeerConnection_Regegotiation_ReuseTransceiver(t *testing.T) {
t.Fatal(err)
}
vp8Track, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "foo", "bar")
vp8Track, err := NewTrackLocalStaticRTP(RTPCodecCapability{MimeType: MimeTypeVP8}, "foo", "bar")
assert.NoError(t, err)
sender, err := pcOffer.AddTrack(vp8Track)
assert.NoError(t, err)
assert.NoError(t, signalPair(pcOffer, pcAnswer))
peerConnectionConnected := untilConnectionState(PeerConnectionStateConnected, pcOffer, pcAnswer)
peerConnectionConnected.Wait()
assert.Equal(t, len(pcOffer.GetTransceivers()), 1)
assert.Equal(t, pcOffer.GetTransceivers()[0].getCurrentDirection(), RTPTransceiverDirectionSendonly)
assert.NoError(t, pcOffer.RemoveTrack(sender))
@@ -1185,6 +1188,45 @@ func TestPeerConnection_Regegotiation_ReuseTransceiver(t *testing.T) {
assert.NoError(t, err)
assert.Equal(t, len(pcOffer.GetTransceivers()), 2)
assert.True(t, sender.rtpTransceiver == pcOffer.GetTransceivers()[0])
assert.NoError(t, signalPair(pcOffer, pcAnswer))
tracksCh := make(chan *TrackRemote, 2)
pcAnswer.OnTrack(func(tr *TrackRemote, _ *RTPReceiver) {
tracksCh <- tr
})
ssrcReuse := sender.GetParameters().Encodings[0].SSRC
for i := 0; i < 10; i++ {
assert.NoError(t, vp8Track.WriteRTP(&rtp.Packet{Header: rtp.Header{Version: 2}, Payload: []byte{0, 1, 2, 3, 4, 5}}))
time.Sleep(20 * time.Millisecond)
}
// shold not reuse tranceiver between two CreateOffer
offer, err := pcOffer.CreateOffer(nil)
assert.NoError(t, err)
assert.NoError(t, pcOffer.RemoveTrack(sender))
assert.NoError(t, pcOffer.SetLocalDescription(offer))
assert.NoError(t, pcAnswer.SetRemoteDescription(offer))
answer, err := pcAnswer.CreateAnswer(nil)
assert.NoError(t, pcAnswer.SetLocalDescription(answer))
assert.NoError(t, err)
assert.NoError(t, pcOffer.SetRemoteDescription(answer))
sender3, err := pcOffer.AddTrack(vp8Track)
ssrcNotReuse := sender3.GetParameters().Encodings[0].SSRC
assert.NoError(t, err)
assert.Equal(t, len(pcOffer.GetTransceivers()), 3)
assert.NoError(t, signalPair(pcOffer, pcAnswer))
assert.True(t, sender3.rtpTransceiver == pcOffer.GetTransceivers()[2])
for i := 0; i < 10; i++ {
assert.NoError(t, vp8Track.WriteRTP(&rtp.Packet{Header: rtp.Header{Version: 2}, Payload: []byte{0, 1, 2, 3, 4, 5}}))
time.Sleep(20 * time.Millisecond)
}
tr1 := <-tracksCh
tr2 := <-tracksCh
assert.Equal(t, tr1.SSRC(), ssrcReuse)
assert.Equal(t, tr2.SSRC(), ssrcNotReuse)
closePairNow(t, pcOffer, pcAnswer)
}