github.com/pion/webrtc/v4@v4.0.1/peerconnection_renegotiation_test.go (about)

     1  // SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
     2  // SPDX-License-Identifier: MIT
     3  
     4  //go:build !js
     5  // +build !js
     6  
     7  package webrtc
     8  
     9  import (
    10  	"bufio"
    11  	"context"
    12  	"errors"
    13  	"io"
    14  	"strconv"
    15  	"strings"
    16  	"sync"
    17  	"sync/atomic"
    18  	"testing"
    19  	"time"
    20  
    21  	"github.com/pion/rtp"
    22  	"github.com/pion/transport/v3/test"
    23  	"github.com/pion/webrtc/v4/internal/util"
    24  	"github.com/pion/webrtc/v4/pkg/media"
    25  	"github.com/pion/webrtc/v4/pkg/rtcerr"
    26  	"github.com/stretchr/testify/assert"
    27  	"github.com/stretchr/testify/require"
    28  )
    29  
    30  func sendVideoUntilDone(done <-chan struct{}, t *testing.T, tracks []*TrackLocalStaticSample) {
    31  	for {
    32  		select {
    33  		case <-time.After(20 * time.Millisecond):
    34  			for _, track := range tracks {
    35  				assert.NoError(t, track.WriteSample(media.Sample{Data: []byte{0x00}, Duration: time.Second}))
    36  			}
    37  		case <-done:
    38  			return
    39  		}
    40  	}
    41  }
    42  
    43  func sdpMidHasSsrc(offer SessionDescription, mid string, ssrc SSRC) bool {
    44  	for _, media := range offer.parsed.MediaDescriptions {
    45  		cmid, ok := media.Attribute("mid")
    46  		if !ok {
    47  			continue
    48  		}
    49  		if cmid != mid {
    50  			continue
    51  		}
    52  		cssrc, ok := media.Attribute("ssrc")
    53  		if !ok {
    54  			continue
    55  		}
    56  		parts := strings.Split(cssrc, " ")
    57  
    58  		ssrcInt64, err := strconv.ParseUint(parts[0], 10, 32)
    59  		if err != nil {
    60  			continue
    61  		}
    62  
    63  		if uint32(ssrcInt64) == uint32(ssrc) {
    64  			return true
    65  		}
    66  	}
    67  	return false
    68  }
    69  
    70  func TestPeerConnection_Renegotiation_AddRecvonlyTransceiver(t *testing.T) {
    71  	type testCase struct {
    72  		name          string
    73  		answererSends bool
    74  	}
    75  
    76  	testCases := []testCase{
    77  		// Assert the following behaviors:
    78  		// - Offerer can add a recvonly transceiver
    79  		// - During negotiation, answerer peer adds an inactive (or sendonly) transceiver
    80  		// - Offerer can add a track
    81  		// - Answerer can receive the RTP packets.
    82  		{"add recvonly, then receive from answerer", false},
    83  		// Assert the following behaviors:
    84  		// - Offerer can add a recvonly transceiver
    85  		// - During negotiation, answerer peer adds an inactive (or sendonly) transceiver
    86  		// - Answerer can add a track to the existing sendonly transceiver
    87  		// - Offerer can receive the RTP packets.
    88  		{"add recvonly, then send to answerer", true},
    89  	}
    90  
    91  	for _, tc := range testCases {
    92  		tc := tc
    93  		t.Run(tc.name, func(t *testing.T) {
    94  			lim := test.TimeOut(time.Second * 30)
    95  			defer lim.Stop()
    96  
    97  			report := test.CheckRoutines(t)
    98  			defer report()
    99  
   100  			pcOffer, pcAnswer, err := newPair()
   101  			if err != nil {
   102  				t.Fatal(err)
   103  			}
   104  
   105  			_, err = pcOffer.AddTransceiverFromKind(
   106  				RTPCodecTypeVideo,
   107  				RTPTransceiverInit{
   108  					Direction: RTPTransceiverDirectionRecvonly,
   109  				},
   110  			)
   111  			assert.NoError(t, err)
   112  
   113  			assert.NoError(t, signalPair(pcOffer, pcAnswer))
   114  
   115  			localTrack, err := NewTrackLocalStaticSample(
   116  				RTPCodecCapability{MimeType: "video/VP8"}, "track-one", "stream-one",
   117  			)
   118  			require.NoError(t, err)
   119  
   120  			if tc.answererSends {
   121  				_, err = pcAnswer.AddTrack(localTrack)
   122  			} else {
   123  				_, err = pcOffer.AddTrack(localTrack)
   124  			}
   125  
   126  			require.NoError(t, err)
   127  
   128  			onTrackFired, onTrackFiredFunc := context.WithCancel(context.Background())
   129  
   130  			if tc.answererSends {
   131  				pcOffer.OnTrack(func(*TrackRemote, *RTPReceiver) {
   132  					onTrackFiredFunc()
   133  				})
   134  				assert.NoError(t, signalPair(pcAnswer, pcOffer))
   135  			} else {
   136  				pcAnswer.OnTrack(func(*TrackRemote, *RTPReceiver) {
   137  					onTrackFiredFunc()
   138  				})
   139  				assert.NoError(t, signalPair(pcOffer, pcAnswer))
   140  			}
   141  
   142  			sendVideoUntilDone(onTrackFired.Done(), t, []*TrackLocalStaticSample{localTrack})
   143  
   144  			closePairNow(t, pcOffer, pcAnswer)
   145  		})
   146  	}
   147  }
   148  
   149  /*
   150  *  Assert the following behaviors
   151  * - We are able to call AddTrack after signaling
   152  * - OnTrack is NOT called on the other side until after SetRemoteDescription
   153  * - We are able to re-negotiate and AddTrack is properly called
   154   */
   155  func TestPeerConnection_Renegotiation_AddTrack(t *testing.T) {
   156  	lim := test.TimeOut(time.Second * 30)
   157  	defer lim.Stop()
   158  
   159  	report := test.CheckRoutines(t)
   160  	defer report()
   161  
   162  	pcOffer, pcAnswer, err := newPair()
   163  	if err != nil {
   164  		t.Fatal(err)
   165  	}
   166  
   167  	haveRenegotiated := &atomicBool{}
   168  	onTrackFired, onTrackFiredFunc := context.WithCancel(context.Background())
   169  	pcAnswer.OnTrack(func(*TrackRemote, *RTPReceiver) {
   170  		if !haveRenegotiated.get() {
   171  			t.Fatal("OnTrack was called before renegotiation")
   172  		}
   173  		onTrackFiredFunc()
   174  	})
   175  
   176  	assert.NoError(t, signalPair(pcOffer, pcAnswer))
   177  
   178  	_, err = pcAnswer.AddTransceiverFromKind(RTPCodecTypeVideo, RTPTransceiverInit{Direction: RTPTransceiverDirectionRecvonly})
   179  	assert.NoError(t, err)
   180  
   181  	vp8Track, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "foo", "bar")
   182  	assert.NoError(t, err)
   183  
   184  	sender, err := pcOffer.AddTrack(vp8Track)
   185  	assert.NoError(t, err)
   186  
   187  	// Send 10 packets, OnTrack MUST not be fired
   188  	for i := 0; i <= 10; i++ {
   189  		assert.NoError(t, vp8Track.WriteSample(media.Sample{Data: []byte{0x00}, Duration: time.Second}))
   190  		time.Sleep(20 * time.Millisecond)
   191  	}
   192  
   193  	haveRenegotiated.set(true)
   194  	assert.False(t, sender.isNegotiated())
   195  	offer, err := pcOffer.CreateOffer(nil)
   196  	assert.True(t, sender.isNegotiated())
   197  	assert.NoError(t, err)
   198  	assert.NoError(t, pcOffer.SetLocalDescription(offer))
   199  	assert.NoError(t, pcAnswer.SetRemoteDescription(offer))
   200  	answer, err := pcAnswer.CreateAnswer(nil)
   201  	assert.NoError(t, err)
   202  
   203  	assert.NoError(t, pcAnswer.SetLocalDescription(answer))
   204  
   205  	pcOffer.ops.Done()
   206  	assert.Equal(t, 0, len(vp8Track.rtpTrack.bindings))
   207  
   208  	assert.NoError(t, pcOffer.SetRemoteDescription(answer))
   209  
   210  	pcOffer.ops.Done()
   211  	assert.Equal(t, 1, len(vp8Track.rtpTrack.bindings))
   212  
   213  	sendVideoUntilDone(onTrackFired.Done(), t, []*TrackLocalStaticSample{vp8Track})
   214  
   215  	closePairNow(t, pcOffer, pcAnswer)
   216  }
   217  
   218  // Assert that adding tracks across multiple renegotiations performs as expected
   219  func TestPeerConnection_Renegotiation_AddTrack_Multiple(t *testing.T) {
   220  	addTrackWithLabel := func(trackID string, pcOffer, pcAnswer *PeerConnection) *TrackLocalStaticSample {
   221  		_, err := pcAnswer.AddTransceiverFromKind(RTPCodecTypeVideo, RTPTransceiverInit{Direction: RTPTransceiverDirectionRecvonly})
   222  		assert.NoError(t, err)
   223  
   224  		track, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, trackID, trackID)
   225  		assert.NoError(t, err)
   226  
   227  		_, err = pcOffer.AddTrack(track)
   228  		assert.NoError(t, err)
   229  
   230  		return track
   231  	}
   232  
   233  	trackIDs := []string{util.MathRandAlpha(16), util.MathRandAlpha(16), util.MathRandAlpha(16)}
   234  	outboundTracks := []*TrackLocalStaticSample{}
   235  	onTrackCount := map[string]int{}
   236  	onTrackChan := make(chan struct{}, 1)
   237  
   238  	lim := test.TimeOut(time.Second * 30)
   239  	defer lim.Stop()
   240  
   241  	report := test.CheckRoutines(t)
   242  	defer report()
   243  
   244  	pcOffer, pcAnswer, err := newPair()
   245  	if err != nil {
   246  		t.Fatal(err)
   247  	}
   248  
   249  	pcAnswer.OnTrack(func(track *TrackRemote, _ *RTPReceiver) {
   250  		onTrackCount[track.ID()]++
   251  		onTrackChan <- struct{}{}
   252  	})
   253  
   254  	assert.NoError(t, signalPair(pcOffer, pcAnswer))
   255  
   256  	for i := range trackIDs {
   257  		outboundTracks = append(outboundTracks, addTrackWithLabel(trackIDs[i], pcOffer, pcAnswer))
   258  		assert.NoError(t, signalPair(pcOffer, pcAnswer))
   259  		sendVideoUntilDone(onTrackChan, t, outboundTracks)
   260  	}
   261  
   262  	closePairNow(t, pcOffer, pcAnswer)
   263  
   264  	assert.Equal(t, onTrackCount[trackIDs[0]], 1)
   265  	assert.Equal(t, onTrackCount[trackIDs[1]], 1)
   266  	assert.Equal(t, onTrackCount[trackIDs[2]], 1)
   267  }
   268  
   269  // Assert that renegotiation triggers OnTrack() with correct ID and label from
   270  // remote side, even when a transceiver was added before the actual track data
   271  // was received. This happens when we add a transceiver on the server, create
   272  // an offer on the server and the browser's answer contains the same SSRC, but
   273  // a track hasn't been added on the browser side yet. The browser can add a
   274  // track later and renegotiate, and track ID and label will be set by the time
   275  // first packets are received.
   276  func TestPeerConnection_Renegotiation_AddTrack_Rename(t *testing.T) {
   277  	lim := test.TimeOut(time.Second * 30)
   278  	defer lim.Stop()
   279  
   280  	report := test.CheckRoutines(t)
   281  	defer report()
   282  
   283  	pcOffer, pcAnswer, err := newPair()
   284  	if err != nil {
   285  		t.Fatal(err)
   286  	}
   287  
   288  	haveRenegotiated := &atomicBool{}
   289  	onTrackFired, onTrackFiredFunc := context.WithCancel(context.Background())
   290  	var atomicRemoteTrack atomic.Value
   291  	pcOffer.OnTrack(func(track *TrackRemote, _ *RTPReceiver) {
   292  		if !haveRenegotiated.get() {
   293  			t.Fatal("OnTrack was called before renegotiation")
   294  		}
   295  		onTrackFiredFunc()
   296  		atomicRemoteTrack.Store(track)
   297  	})
   298  
   299  	_, err = pcOffer.AddTransceiverFromKind(RTPCodecTypeVideo, RTPTransceiverInit{Direction: RTPTransceiverDirectionRecvonly})
   300  	assert.NoError(t, err)
   301  	vp8Track, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "foo1", "bar1")
   302  	assert.NoError(t, err)
   303  	_, err = pcAnswer.AddTrack(vp8Track)
   304  	assert.NoError(t, err)
   305  
   306  	assert.NoError(t, signalPair(pcOffer, pcAnswer))
   307  
   308  	vp8Track.rtpTrack.id = "foo2"
   309  	vp8Track.rtpTrack.streamID = "bar2"
   310  
   311  	haveRenegotiated.set(true)
   312  	assert.NoError(t, signalPair(pcOffer, pcAnswer))
   313  
   314  	sendVideoUntilDone(onTrackFired.Done(), t, []*TrackLocalStaticSample{vp8Track})
   315  
   316  	closePairNow(t, pcOffer, pcAnswer)
   317  
   318  	remoteTrack, ok := atomicRemoteTrack.Load().(*TrackRemote)
   319  	require.True(t, ok)
   320  	require.NotNil(t, remoteTrack)
   321  	assert.Equal(t, "foo2", remoteTrack.ID())
   322  	assert.Equal(t, "bar2", remoteTrack.StreamID())
   323  }
   324  
   325  // TestPeerConnection_Transceiver_Mid tests that we'll provide the same
   326  // transceiver for a media id on successive offer/answer
   327  func TestPeerConnection_Transceiver_Mid(t *testing.T) {
   328  	lim := test.TimeOut(time.Second * 30)
   329  	defer lim.Stop()
   330  
   331  	report := test.CheckRoutines(t)
   332  	defer report()
   333  
   334  	pcOffer, err := NewPeerConnection(Configuration{})
   335  	assert.NoError(t, err)
   336  
   337  	pcAnswer, err := NewPeerConnection(Configuration{})
   338  	assert.NoError(t, err)
   339  
   340  	track1, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion1")
   341  	require.NoError(t, err)
   342  
   343  	sender1, err := pcOffer.AddTrack(track1)
   344  	require.NoError(t, err)
   345  
   346  	track2, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion2")
   347  	require.NoError(t, err)
   348  
   349  	sender2, err := pcOffer.AddTrack(track2)
   350  	require.NoError(t, err)
   351  
   352  	// this will create the initial offer using generateUnmatchedSDP
   353  	offer, err := pcOffer.CreateOffer(nil)
   354  	assert.NoError(t, err)
   355  
   356  	offerGatheringComplete := GatheringCompletePromise(pcOffer)
   357  	assert.NoError(t, pcOffer.SetLocalDescription(offer))
   358  	<-offerGatheringComplete
   359  
   360  	assert.NoError(t, pcAnswer.SetRemoteDescription(*pcOffer.LocalDescription()))
   361  
   362  	answer, err := pcAnswer.CreateAnswer(nil)
   363  	assert.NoError(t, err)
   364  
   365  	answerGatheringComplete := GatheringCompletePromise(pcAnswer)
   366  	assert.NoError(t, pcAnswer.SetLocalDescription(answer))
   367  	<-answerGatheringComplete
   368  
   369  	// apply answer so we'll test generateMatchedSDP
   370  	assert.NoError(t, pcOffer.SetRemoteDescription(*pcAnswer.LocalDescription()))
   371  
   372  	pcOffer.ops.Done()
   373  	pcAnswer.ops.Done()
   374  
   375  	// Must have 3 media descriptions (2 video channels)
   376  	assert.Equal(t, len(offer.parsed.MediaDescriptions), 2)
   377  
   378  	assert.True(t, sdpMidHasSsrc(offer, "0", sender1.trackEncodings[0].ssrc), "Expected mid %q with ssrc %d, offer.SDP: %s", "0", sender1.trackEncodings[0].ssrc, offer.SDP)
   379  
   380  	// Remove first track, must keep same number of media
   381  	// descriptions and same track ssrc for mid 1 as previous
   382  	assert.NoError(t, pcOffer.RemoveTrack(sender1))
   383  
   384  	offer, err = pcOffer.CreateOffer(nil)
   385  	assert.NoError(t, err)
   386  	assert.NoError(t, pcOffer.SetLocalDescription(offer))
   387  
   388  	assert.Equal(t, len(offer.parsed.MediaDescriptions), 2)
   389  
   390  	assert.True(t, sdpMidHasSsrc(offer, "1", sender2.trackEncodings[0].ssrc), "Expected mid %q with ssrc %d, offer.SDP: %s", "1", sender2.trackEncodings[0].ssrc, offer.SDP)
   391  
   392  	_, err = pcAnswer.CreateAnswer(nil)
   393  	assert.Equal(t, err, &rtcerr.InvalidStateError{Err: ErrIncorrectSignalingState})
   394  
   395  	pcOffer.ops.Done()
   396  	pcAnswer.ops.Done()
   397  
   398  	assert.NoError(t, pcAnswer.SetRemoteDescription(offer))
   399  	answer, err = pcAnswer.CreateAnswer(nil)
   400  	assert.NoError(t, err)
   401  	assert.NoError(t, pcOffer.SetRemoteDescription(answer))
   402  
   403  	track3, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion3")
   404  	require.NoError(t, err)
   405  
   406  	sender3, err := pcOffer.AddTrack(track3)
   407  	require.NoError(t, err)
   408  
   409  	offer, err = pcOffer.CreateOffer(nil)
   410  	assert.NoError(t, err)
   411  
   412  	// We reuse the existing non-sending transceiver
   413  	assert.Equal(t, len(offer.parsed.MediaDescriptions), 2)
   414  
   415  	assert.True(t, sdpMidHasSsrc(offer, "0", sender3.trackEncodings[0].ssrc), "Expected mid %q with ssrc %d, offer.sdp: %s", "0", sender3.trackEncodings[0].ssrc, offer.SDP)
   416  	assert.True(t, sdpMidHasSsrc(offer, "1", sender2.trackEncodings[0].ssrc), "Expected mid %q with ssrc %d, offer.sdp: %s", "1", sender2.trackEncodings[0].ssrc, offer.SDP)
   417  
   418  	closePairNow(t, pcOffer, pcAnswer)
   419  }
   420  
   421  func TestPeerConnection_Renegotiation_CodecChange(t *testing.T) {
   422  	lim := test.TimeOut(time.Second * 30)
   423  	defer lim.Stop()
   424  
   425  	report := test.CheckRoutines(t)
   426  	defer report()
   427  
   428  	pcOffer, err := NewPeerConnection(Configuration{})
   429  	assert.NoError(t, err)
   430  
   431  	pcAnswer, err := NewPeerConnection(Configuration{})
   432  	assert.NoError(t, err)
   433  
   434  	track1, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "video1", "pion1")
   435  	require.NoError(t, err)
   436  
   437  	track2, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "video2", "pion2")
   438  	require.NoError(t, err)
   439  
   440  	sender1, err := pcOffer.AddTrack(track1)
   441  	require.NoError(t, err)
   442  
   443  	_, err = pcAnswer.AddTransceiverFromKind(RTPCodecTypeVideo, RTPTransceiverInit{Direction: RTPTransceiverDirectionRecvonly})
   444  	require.NoError(t, err)
   445  
   446  	tracksCh := make(chan *TrackRemote)
   447  	tracksClosed := make(chan struct{})
   448  	pcAnswer.OnTrack(func(track *TrackRemote, _ *RTPReceiver) {
   449  		tracksCh <- track
   450  		for {
   451  			if _, _, readErr := track.ReadRTP(); errors.Is(readErr, io.EOF) {
   452  				tracksClosed <- struct{}{}
   453  				return
   454  			}
   455  		}
   456  	})
   457  
   458  	err = signalPair(pcOffer, pcAnswer)
   459  	require.NoError(t, err)
   460  
   461  	transceivers := pcOffer.GetTransceivers()
   462  	require.Equal(t, 1, len(transceivers))
   463  	require.Equal(t, "0", transceivers[0].Mid())
   464  
   465  	transceivers = pcAnswer.GetTransceivers()
   466  	require.Equal(t, 1, len(transceivers))
   467  	require.Equal(t, "0", transceivers[0].Mid())
   468  
   469  	ctx, cancel := context.WithCancel(context.Background())
   470  	go sendVideoUntilDone(ctx.Done(), t, []*TrackLocalStaticSample{track1})
   471  
   472  	remoteTrack1 := <-tracksCh
   473  	cancel()
   474  
   475  	assert.Equal(t, "video1", remoteTrack1.ID())
   476  	assert.Equal(t, "pion1", remoteTrack1.StreamID())
   477  
   478  	require.NoError(t, pcOffer.RemoveTrack(sender1))
   479  
   480  	require.NoError(t, signalPair(pcOffer, pcAnswer))
   481  	<-tracksClosed
   482  
   483  	sender2, err := pcOffer.AddTrack(track2)
   484  	require.NoError(t, err)
   485  	require.NoError(t, signalPair(pcOffer, pcAnswer))
   486  	transceivers = pcOffer.GetTransceivers()
   487  	require.Equal(t, 1, len(transceivers))
   488  	require.Equal(t, "0", transceivers[0].Mid())
   489  
   490  	transceivers = pcAnswer.GetTransceivers()
   491  	require.Equal(t, 1, len(transceivers))
   492  	require.Equal(t, "0", transceivers[0].Mid())
   493  
   494  	ctx, cancel = context.WithCancel(context.Background())
   495  	go sendVideoUntilDone(ctx.Done(), t, []*TrackLocalStaticSample{track2})
   496  
   497  	remoteTrack2 := <-tracksCh
   498  	cancel()
   499  
   500  	require.NoError(t, pcOffer.RemoveTrack(sender2))
   501  
   502  	err = signalPair(pcOffer, pcAnswer)
   503  	require.NoError(t, err)
   504  	<-tracksClosed
   505  
   506  	assert.Equal(t, "video2", remoteTrack2.ID())
   507  	assert.Equal(t, "pion2", remoteTrack2.StreamID())
   508  
   509  	closePairNow(t, pcOffer, pcAnswer)
   510  }
   511  
   512  func TestPeerConnection_Renegotiation_RemoveTrack(t *testing.T) {
   513  	lim := test.TimeOut(time.Second * 30)
   514  	defer lim.Stop()
   515  
   516  	report := test.CheckRoutines(t)
   517  	defer report()
   518  
   519  	pcOffer, pcAnswer, err := newPair()
   520  	if err != nil {
   521  		t.Fatal(err)
   522  	}
   523  
   524  	_, err = pcAnswer.AddTransceiverFromKind(RTPCodecTypeVideo, RTPTransceiverInit{Direction: RTPTransceiverDirectionRecvonly})
   525  	assert.NoError(t, err)
   526  
   527  	vp8Track, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "foo", "bar")
   528  	assert.NoError(t, err)
   529  
   530  	sender, err := pcOffer.AddTrack(vp8Track)
   531  	assert.NoError(t, err)
   532  
   533  	onTrackFired, onTrackFiredFunc := context.WithCancel(context.Background())
   534  	trackClosed, trackClosedFunc := context.WithCancel(context.Background())
   535  
   536  	pcAnswer.OnTrack(func(track *TrackRemote, _ *RTPReceiver) {
   537  		onTrackFiredFunc()
   538  
   539  		for {
   540  			if _, _, err := track.ReadRTP(); errors.Is(err, io.EOF) {
   541  				trackClosedFunc()
   542  				return
   543  			}
   544  		}
   545  	})
   546  
   547  	assert.NoError(t, signalPair(pcOffer, pcAnswer))
   548  	sendVideoUntilDone(onTrackFired.Done(), t, []*TrackLocalStaticSample{vp8Track})
   549  
   550  	assert.NoError(t, pcOffer.RemoveTrack(sender))
   551  	assert.NoError(t, signalPair(pcOffer, pcAnswer))
   552  
   553  	<-trackClosed.Done()
   554  	closePairNow(t, pcOffer, pcAnswer)
   555  }
   556  
   557  func TestPeerConnection_RoleSwitch(t *testing.T) {
   558  	lim := test.TimeOut(time.Second * 30)
   559  	defer lim.Stop()
   560  
   561  	report := test.CheckRoutines(t)
   562  	defer report()
   563  
   564  	pcFirstOfferer, pcSecondOfferer, err := newPair()
   565  	if err != nil {
   566  		t.Fatal(err)
   567  	}
   568  
   569  	onTrackFired, onTrackFiredFunc := context.WithCancel(context.Background())
   570  	pcFirstOfferer.OnTrack(func(*TrackRemote, *RTPReceiver) {
   571  		onTrackFiredFunc()
   572  	})
   573  
   574  	assert.NoError(t, signalPair(pcFirstOfferer, pcSecondOfferer))
   575  
   576  	// Add a new Track to the second offerer
   577  	// This asserts that it will match the ordering of the last RemoteDescription, but then also add new Transceivers to the end
   578  	_, err = pcFirstOfferer.AddTransceiverFromKind(RTPCodecTypeVideo, RTPTransceiverInit{Direction: RTPTransceiverDirectionRecvonly})
   579  	assert.NoError(t, err)
   580  
   581  	vp8Track, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "foo", "bar")
   582  	assert.NoError(t, err)
   583  
   584  	_, err = pcSecondOfferer.AddTrack(vp8Track)
   585  	assert.NoError(t, err)
   586  
   587  	assert.NoError(t, signalPair(pcSecondOfferer, pcFirstOfferer))
   588  	sendVideoUntilDone(onTrackFired.Done(), t, []*TrackLocalStaticSample{vp8Track})
   589  
   590  	closePairNow(t, pcFirstOfferer, pcSecondOfferer)
   591  }
   592  
   593  // Assert that renegotiation doesn't attempt to gather ICE twice
   594  // Before we would attempt to gather multiple times and would put
   595  // the PeerConnection into a broken state
   596  func TestPeerConnection_Renegotiation_Trickle(t *testing.T) {
   597  	lim := test.TimeOut(time.Second * 30)
   598  	defer lim.Stop()
   599  
   600  	report := test.CheckRoutines(t)
   601  	defer report()
   602  
   603  	settingEngine := SettingEngine{}
   604  
   605  	api := NewAPI(WithSettingEngine(settingEngine))
   606  
   607  	// Invalid STUN server on purpose, will stop ICE Gathering from completing in time
   608  	pcOffer, pcAnswer, err := api.newPair(Configuration{
   609  		ICEServers: []ICEServer{
   610  			{
   611  				URLs: []string{"stun:127.0.0.1:5000"},
   612  			},
   613  		},
   614  	})
   615  	if err != nil {
   616  		t.Fatal(err)
   617  	}
   618  
   619  	_, err = pcOffer.CreateDataChannel("test-channel", nil)
   620  	assert.NoError(t, err)
   621  
   622  	var wg sync.WaitGroup
   623  	wg.Add(2)
   624  	pcOffer.OnICECandidate(func(c *ICECandidate) {
   625  		if c != nil {
   626  			assert.NoError(t, pcAnswer.AddICECandidate(c.ToJSON()))
   627  		} else {
   628  			wg.Done()
   629  		}
   630  	})
   631  	pcAnswer.OnICECandidate(func(c *ICECandidate) {
   632  		if c != nil {
   633  			assert.NoError(t, pcOffer.AddICECandidate(c.ToJSON()))
   634  		} else {
   635  			wg.Done()
   636  		}
   637  	})
   638  
   639  	negotiate := func() {
   640  		offer, err := pcOffer.CreateOffer(nil)
   641  		assert.NoError(t, err)
   642  
   643  		assert.NoError(t, pcAnswer.SetRemoteDescription(offer))
   644  		assert.NoError(t, pcOffer.SetLocalDescription(offer))
   645  
   646  		answer, err := pcAnswer.CreateAnswer(nil)
   647  		assert.NoError(t, err)
   648  
   649  		assert.NoError(t, pcOffer.SetRemoteDescription(answer))
   650  		assert.NoError(t, pcAnswer.SetLocalDescription(answer))
   651  	}
   652  	negotiate()
   653  	negotiate()
   654  
   655  	pcOffer.ops.Done()
   656  	pcAnswer.ops.Done()
   657  	wg.Wait()
   658  
   659  	closePairNow(t, pcOffer, pcAnswer)
   660  }
   661  
   662  func TestPeerConnection_Renegotiation_SetLocalDescription(t *testing.T) {
   663  	lim := test.TimeOut(time.Second * 30)
   664  	defer lim.Stop()
   665  
   666  	report := test.CheckRoutines(t)
   667  	defer report()
   668  
   669  	pcOffer, pcAnswer, err := newPair()
   670  	if err != nil {
   671  		t.Fatal(err)
   672  	}
   673  
   674  	onTrackFired, onTrackFiredFunc := context.WithCancel(context.Background())
   675  	pcOffer.OnTrack(func(*TrackRemote, *RTPReceiver) {
   676  		onTrackFiredFunc()
   677  	})
   678  
   679  	assert.NoError(t, signalPair(pcOffer, pcAnswer))
   680  
   681  	pcOffer.ops.Done()
   682  	pcAnswer.ops.Done()
   683  
   684  	_, err = pcOffer.AddTransceiverFromKind(RTPCodecTypeVideo, RTPTransceiverInit{Direction: RTPTransceiverDirectionRecvonly})
   685  	assert.NoError(t, err)
   686  
   687  	localTrack, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "foo", "bar")
   688  	assert.NoError(t, err)
   689  
   690  	sender, err := pcAnswer.AddTrack(localTrack)
   691  	assert.NoError(t, err)
   692  
   693  	offer, err := pcOffer.CreateOffer(nil)
   694  	assert.NoError(t, err)
   695  	assert.NoError(t, pcOffer.SetLocalDescription(offer))
   696  	assert.NoError(t, pcAnswer.SetRemoteDescription(offer))
   697  	assert.False(t, sender.isNegotiated())
   698  	answer, err := pcAnswer.CreateAnswer(nil)
   699  	assert.NoError(t, err)
   700  	assert.True(t, sender.isNegotiated())
   701  
   702  	pcAnswer.ops.Done()
   703  	assert.Equal(t, 0, len(localTrack.rtpTrack.bindings))
   704  
   705  	assert.NoError(t, pcAnswer.SetLocalDescription(answer))
   706  
   707  	pcAnswer.ops.Done()
   708  	assert.Equal(t, 1, len(localTrack.rtpTrack.bindings))
   709  
   710  	assert.NoError(t, pcOffer.SetRemoteDescription(answer))
   711  
   712  	sendVideoUntilDone(onTrackFired.Done(), t, []*TrackLocalStaticSample{localTrack})
   713  
   714  	closePairNow(t, pcOffer, pcAnswer)
   715  }
   716  
   717  // Issue #346, don't start the SCTP Subsystem if the RemoteDescription doesn't contain one
   718  // Before we would always start it, and re-negotiations would fail because SCTP was in flight
   719  func TestPeerConnection_Renegotiation_NoApplication(t *testing.T) {
   720  	signalPairExcludeDataChannel := func(pcOffer, pcAnswer *PeerConnection) {
   721  		offer, err := pcOffer.CreateOffer(nil)
   722  		assert.NoError(t, err)
   723  		offerGatheringComplete := GatheringCompletePromise(pcOffer)
   724  		assert.NoError(t, pcOffer.SetLocalDescription(offer))
   725  		<-offerGatheringComplete
   726  
   727  		assert.NoError(t, pcAnswer.SetRemoteDescription(*pcOffer.LocalDescription()))
   728  
   729  		answer, err := pcAnswer.CreateAnswer(nil)
   730  		assert.NoError(t, err)
   731  
   732  		answerGatheringComplete := GatheringCompletePromise(pcAnswer)
   733  		assert.NoError(t, pcAnswer.SetLocalDescription(answer))
   734  		<-answerGatheringComplete
   735  
   736  		assert.NoError(t, pcOffer.SetRemoteDescription(*pcAnswer.LocalDescription()))
   737  	}
   738  
   739  	lim := test.TimeOut(time.Second * 30)
   740  	defer lim.Stop()
   741  
   742  	report := test.CheckRoutines(t)
   743  	defer report()
   744  
   745  	pcOffer, pcAnswer, err := newPair()
   746  	if err != nil {
   747  		t.Fatal(err)
   748  	}
   749  
   750  	pcOfferConnected, pcOfferConnectedCancel := context.WithCancel(context.Background())
   751  	pcOffer.OnICEConnectionStateChange(func(i ICEConnectionState) {
   752  		if i == ICEConnectionStateConnected {
   753  			pcOfferConnectedCancel()
   754  		}
   755  	})
   756  
   757  	pcAnswerConnected, pcAnswerConnectedCancel := context.WithCancel(context.Background())
   758  	pcAnswer.OnICEConnectionStateChange(func(i ICEConnectionState) {
   759  		if i == ICEConnectionStateConnected {
   760  			pcAnswerConnectedCancel()
   761  		}
   762  	})
   763  
   764  	_, err = pcOffer.AddTransceiverFromKind(RTPCodecTypeVideo, RTPTransceiverInit{Direction: RTPTransceiverDirectionSendrecv})
   765  	assert.NoError(t, err)
   766  
   767  	_, err = pcAnswer.AddTransceiverFromKind(RTPCodecTypeVideo, RTPTransceiverInit{Direction: RTPTransceiverDirectionSendrecv})
   768  	assert.NoError(t, err)
   769  
   770  	signalPairExcludeDataChannel(pcOffer, pcAnswer)
   771  	pcOffer.ops.Done()
   772  	pcAnswer.ops.Done()
   773  
   774  	signalPairExcludeDataChannel(pcOffer, pcAnswer)
   775  	pcOffer.ops.Done()
   776  	pcAnswer.ops.Done()
   777  
   778  	<-pcAnswerConnected.Done()
   779  	<-pcOfferConnected.Done()
   780  
   781  	assert.Equal(t, pcOffer.SCTP().State(), SCTPTransportStateConnecting)
   782  	assert.Equal(t, pcAnswer.SCTP().State(), SCTPTransportStateConnecting)
   783  
   784  	closePairNow(t, pcOffer, pcAnswer)
   785  }
   786  
   787  func TestAddDataChannelDuringRenegotiation(t *testing.T) {
   788  	lim := test.TimeOut(time.Second * 10)
   789  	defer lim.Stop()
   790  
   791  	report := test.CheckRoutines(t)
   792  	defer report()
   793  
   794  	pcOffer, err := NewPeerConnection(Configuration{})
   795  	assert.NoError(t, err)
   796  
   797  	pcAnswer, err := NewPeerConnection(Configuration{})
   798  	assert.NoError(t, err)
   799  
   800  	track, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion")
   801  	assert.NoError(t, err)
   802  
   803  	_, err = pcOffer.AddTrack(track)
   804  	assert.NoError(t, err)
   805  
   806  	offer, err := pcOffer.CreateOffer(nil)
   807  	assert.NoError(t, err)
   808  
   809  	offerGatheringComplete := GatheringCompletePromise(pcOffer)
   810  	assert.NoError(t, pcOffer.SetLocalDescription(offer))
   811  	<-offerGatheringComplete
   812  
   813  	assert.NoError(t, pcAnswer.SetRemoteDescription(*pcOffer.LocalDescription()))
   814  
   815  	answer, err := pcAnswer.CreateAnswer(nil)
   816  	assert.NoError(t, err)
   817  
   818  	answerGatheringComplete := GatheringCompletePromise(pcAnswer)
   819  	assert.NoError(t, pcAnswer.SetLocalDescription(answer))
   820  	<-answerGatheringComplete
   821  
   822  	assert.NoError(t, pcOffer.SetRemoteDescription(*pcAnswer.LocalDescription()))
   823  
   824  	_, err = pcOffer.CreateDataChannel("data-channel", nil)
   825  	assert.NoError(t, err)
   826  
   827  	// Assert that DataChannel is in offer now
   828  	offer, err = pcOffer.CreateOffer(nil)
   829  	assert.NoError(t, err)
   830  
   831  	applicationMediaSectionCount := 0
   832  	for _, d := range offer.parsed.MediaDescriptions {
   833  		if d.MediaName.Media == mediaSectionApplication {
   834  			applicationMediaSectionCount++
   835  		}
   836  	}
   837  	assert.Equal(t, applicationMediaSectionCount, 1)
   838  
   839  	onDataChannelFired, onDataChannelFiredFunc := context.WithCancel(context.Background())
   840  	pcAnswer.OnDataChannel(func(*DataChannel) {
   841  		onDataChannelFiredFunc()
   842  	})
   843  
   844  	assert.NoError(t, signalPair(pcOffer, pcAnswer))
   845  
   846  	<-onDataChannelFired.Done()
   847  	closePairNow(t, pcOffer, pcAnswer)
   848  }
   849  
   850  // Assert that CreateDataChannel fires OnNegotiationNeeded
   851  func TestNegotiationCreateDataChannel(t *testing.T) {
   852  	lim := test.TimeOut(time.Second * 30)
   853  	defer lim.Stop()
   854  
   855  	report := test.CheckRoutines(t)
   856  	defer report()
   857  
   858  	pc, err := NewPeerConnection(Configuration{})
   859  	assert.NoError(t, err)
   860  
   861  	var wg sync.WaitGroup
   862  	wg.Add(1)
   863  
   864  	pc.OnNegotiationNeeded(func() {
   865  		defer func() {
   866  			wg.Done()
   867  		}()
   868  	})
   869  
   870  	// Create DataChannel, wait until OnNegotiationNeeded is fired
   871  	if _, err = pc.CreateDataChannel("testChannel", nil); err != nil {
   872  		t.Error(err.Error())
   873  	}
   874  
   875  	// Wait until OnNegotiationNeeded is fired
   876  	wg.Wait()
   877  	assert.NoError(t, pc.Close())
   878  }
   879  
   880  func TestNegotiationNeededRemoveTrack(t *testing.T) {
   881  	var wg sync.WaitGroup
   882  	wg.Add(1)
   883  
   884  	report := test.CheckRoutines(t)
   885  	defer report()
   886  
   887  	pcOffer, err := NewPeerConnection(Configuration{})
   888  	assert.NoError(t, err)
   889  	pcAnswer, err := NewPeerConnection(Configuration{})
   890  	assert.NoError(t, err)
   891  
   892  	track, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion")
   893  	assert.NoError(t, err)
   894  
   895  	pcOffer.OnNegotiationNeeded(func() {
   896  		wg.Add(1)
   897  		offer, createOfferErr := pcOffer.CreateOffer(nil)
   898  		assert.NoError(t, createOfferErr)
   899  
   900  		offerGatheringComplete := GatheringCompletePromise(pcOffer)
   901  		assert.NoError(t, pcOffer.SetLocalDescription(offer))
   902  
   903  		<-offerGatheringComplete
   904  		assert.NoError(t, pcAnswer.SetRemoteDescription(*pcOffer.LocalDescription()))
   905  
   906  		answer, createAnswerErr := pcAnswer.CreateAnswer(nil)
   907  		assert.NoError(t, createAnswerErr)
   908  
   909  		answerGatheringComplete := GatheringCompletePromise(pcAnswer)
   910  		assert.NoError(t, pcAnswer.SetLocalDescription(answer))
   911  
   912  		<-answerGatheringComplete
   913  		assert.NoError(t, pcOffer.SetRemoteDescription(*pcAnswer.LocalDescription()))
   914  		wg.Done()
   915  		wg.Done()
   916  	})
   917  
   918  	sender, err := pcOffer.AddTrack(track)
   919  	assert.NoError(t, err)
   920  
   921  	assert.NoError(t, track.WriteSample(media.Sample{Data: []byte{0x00}, Duration: time.Second}))
   922  
   923  	wg.Wait()
   924  
   925  	wg.Add(1)
   926  	assert.NoError(t, pcOffer.RemoveTrack(sender))
   927  
   928  	wg.Wait()
   929  
   930  	closePairNow(t, pcOffer, pcAnswer)
   931  }
   932  
   933  func TestNegotiationNeededStressOneSided(t *testing.T) {
   934  	lim := test.TimeOut(time.Second * 30)
   935  	defer lim.Stop()
   936  
   937  	report := test.CheckRoutines(t)
   938  	defer report()
   939  
   940  	pcA, pcB, err := newPair()
   941  	assert.NoError(t, err)
   942  
   943  	const expectedTrackCount = 500
   944  	ctx, done := context.WithCancel(context.Background())
   945  	pcA.OnNegotiationNeeded(func() {
   946  		count := len(pcA.GetTransceivers())
   947  		assert.NoError(t, signalPair(pcA, pcB))
   948  		if count == expectedTrackCount {
   949  			done()
   950  		}
   951  	})
   952  
   953  	for i := 0; i < expectedTrackCount; i++ {
   954  		track, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion")
   955  		assert.NoError(t, err)
   956  
   957  		_, err = pcA.AddTrack(track)
   958  		assert.NoError(t, err)
   959  	}
   960  	<-ctx.Done()
   961  	assert.Equal(t, expectedTrackCount, len(pcB.GetTransceivers()))
   962  	closePairNow(t, pcA, pcB)
   963  }
   964  
   965  // TestPeerConnection_Renegotiation_DisableTrack asserts that if a remote track is set inactive
   966  // that locally it goes inactive as well
   967  func TestPeerConnection_Renegotiation_DisableTrack(t *testing.T) {
   968  	lim := test.TimeOut(time.Second * 30)
   969  	defer lim.Stop()
   970  
   971  	report := test.CheckRoutines(t)
   972  	defer report()
   973  
   974  	pcOffer, pcAnswer, err := newPair()
   975  	assert.NoError(t, err)
   976  
   977  	// Create two transceivers
   978  	_, err = pcOffer.AddTransceiverFromKind(RTPCodecTypeVideo)
   979  	assert.NoError(t, err)
   980  
   981  	transceiver, err := pcOffer.AddTransceiverFromKind(RTPCodecTypeVideo)
   982  	assert.NoError(t, err)
   983  
   984  	assert.NoError(t, signalPair(pcOffer, pcAnswer))
   985  
   986  	// Assert we have three active transceivers
   987  	offer, err := pcOffer.CreateOffer(nil)
   988  	assert.NoError(t, err)
   989  	assert.Equal(t, strings.Count(offer.SDP, "a=sendrecv"), 3)
   990  
   991  	// Assert we have two active transceivers, one inactive
   992  	assert.NoError(t, transceiver.Stop())
   993  	offer, err = pcOffer.CreateOffer(nil)
   994  	assert.NoError(t, err)
   995  	assert.Equal(t, strings.Count(offer.SDP, "a=sendrecv"), 2)
   996  	assert.Equal(t, strings.Count(offer.SDP, "a=inactive"), 1)
   997  
   998  	// Assert that the offer disabled one of our transceivers
   999  	assert.NoError(t, pcAnswer.SetRemoteDescription(offer))
  1000  	answer, err := pcAnswer.CreateAnswer(nil)
  1001  	assert.NoError(t, err)
  1002  	assert.Equal(t, strings.Count(answer.SDP, "a=sendrecv"), 1) // DataChannel
  1003  	assert.Equal(t, strings.Count(answer.SDP, "a=recvonly"), 1)
  1004  	assert.Equal(t, strings.Count(answer.SDP, "a=inactive"), 1)
  1005  
  1006  	closePairNow(t, pcOffer, pcAnswer)
  1007  }
  1008  
  1009  func TestPeerConnection_Renegotiation_Simulcast(t *testing.T) {
  1010  	lim := test.TimeOut(time.Second * 30)
  1011  	defer lim.Stop()
  1012  
  1013  	report := test.CheckRoutines(t)
  1014  	defer report()
  1015  
  1016  	originalRids := []string{"a", "b", "c"}
  1017  	signalWithRids := func(sessionDescription string, rids []string) string {
  1018  		sessionDescription = strings.SplitAfter(sessionDescription, "a=end-of-candidates\r\n")[0]
  1019  		sessionDescription = filterSsrc(sessionDescription)
  1020  		for _, rid := range rids {
  1021  			sessionDescription += "a=" + sdpAttributeRid + ":" + rid + " send\r\n"
  1022  		}
  1023  		return sessionDescription + "a=simulcast:send " + strings.Join(rids, ";") + "\r\n"
  1024  	}
  1025  
  1026  	var trackMapLock sync.RWMutex
  1027  	trackMap := map[string]*TrackRemote{}
  1028  
  1029  	onTrackHandler := func(track *TrackRemote, _ *RTPReceiver) {
  1030  		trackMapLock.Lock()
  1031  		defer trackMapLock.Unlock()
  1032  		trackMap[track.RID()] = track
  1033  	}
  1034  
  1035  	sendUntilAllTracksFired := func(vp8Writer *TrackLocalStaticRTP, rids []string) {
  1036  		allTracksFired := func() bool {
  1037  			trackMapLock.Lock()
  1038  			defer trackMapLock.Unlock()
  1039  
  1040  			return len(trackMap) == len(rids)
  1041  		}
  1042  
  1043  		for sequenceNumber := uint16(0); !allTracksFired(); sequenceNumber++ {
  1044  			time.Sleep(20 * time.Millisecond)
  1045  
  1046  			for ssrc, rid := range rids {
  1047  				header := &rtp.Header{
  1048  					Version:        2,
  1049  					SSRC:           uint32(ssrc + 1),
  1050  					SequenceNumber: sequenceNumber,
  1051  					PayloadType:    96,
  1052  				}
  1053  				assert.NoError(t, header.SetExtension(1, []byte("0")))
  1054  				assert.NoError(t, header.SetExtension(2, []byte(rid)))
  1055  
  1056  				_, err := vp8Writer.bindings[0].writeStream.WriteRTP(header, []byte{0x00})
  1057  				assert.NoError(t, err)
  1058  			}
  1059  		}
  1060  	}
  1061  
  1062  	assertTracksClosed := func(t *testing.T) {
  1063  		trackMapLock.Lock()
  1064  		defer trackMapLock.Unlock()
  1065  
  1066  		for _, track := range trackMap {
  1067  			_, _, err := track.ReadRTP() // Ignore first Read, this is our peeked data
  1068  			assert.Nil(t, err)
  1069  
  1070  			_, _, err = track.ReadRTP()
  1071  			assert.Equal(t, err, io.EOF)
  1072  		}
  1073  	}
  1074  
  1075  	t.Run("Disable Transceiver", func(t *testing.T) {
  1076  		trackMap = map[string]*TrackRemote{}
  1077  		pcOffer, pcAnswer, err := newPair()
  1078  		assert.NoError(t, err)
  1079  
  1080  		vp8Writer, err := NewTrackLocalStaticRTP(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion2")
  1081  		assert.NoError(t, err)
  1082  
  1083  		rtpTransceiver, err := pcOffer.AddTransceiverFromTrack(
  1084  			vp8Writer,
  1085  			RTPTransceiverInit{
  1086  				Direction: RTPTransceiverDirectionSendonly,
  1087  			},
  1088  		)
  1089  		assert.NoError(t, err)
  1090  
  1091  		assert.NoError(t, signalPairWithModification(pcOffer, pcAnswer, func(sessionDescription string) string {
  1092  			return signalWithRids(sessionDescription, originalRids)
  1093  		}))
  1094  
  1095  		pcAnswer.OnTrack(onTrackHandler)
  1096  		sendUntilAllTracksFired(vp8Writer, originalRids)
  1097  
  1098  		assert.NoError(t, pcOffer.RemoveTrack(rtpTransceiver.Sender()))
  1099  		assert.NoError(t, signalPairWithModification(pcOffer, pcAnswer, func(sessionDescription string) string {
  1100  			sessionDescription = strings.SplitAfter(sessionDescription, "a=end-of-candidates\r\n")[0]
  1101  			return sessionDescription
  1102  		}))
  1103  
  1104  		assertTracksClosed(t)
  1105  		closePairNow(t, pcOffer, pcAnswer)
  1106  	})
  1107  
  1108  	t.Run("Change RID", func(t *testing.T) {
  1109  		trackMap = map[string]*TrackRemote{}
  1110  		pcOffer, pcAnswer, err := newPair()
  1111  		assert.NoError(t, err)
  1112  
  1113  		vp8Writer, err := NewTrackLocalStaticRTP(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion2")
  1114  		assert.NoError(t, err)
  1115  
  1116  		_, err = pcOffer.AddTransceiverFromTrack(
  1117  			vp8Writer,
  1118  			RTPTransceiverInit{
  1119  				Direction: RTPTransceiverDirectionSendonly,
  1120  			},
  1121  		)
  1122  		assert.NoError(t, err)
  1123  
  1124  		assert.NoError(t, signalPairWithModification(pcOffer, pcAnswer, func(sessionDescription string) string {
  1125  			return signalWithRids(sessionDescription, originalRids)
  1126  		}))
  1127  
  1128  		pcAnswer.OnTrack(onTrackHandler)
  1129  		sendUntilAllTracksFired(vp8Writer, originalRids)
  1130  
  1131  		newRids := []string{"d", "e", "f"}
  1132  		assert.NoError(t, signalPairWithModification(pcOffer, pcAnswer, func(sessionDescription string) string {
  1133  			scanner := bufio.NewScanner(strings.NewReader(sessionDescription))
  1134  			sessionDescription = ""
  1135  			for scanner.Scan() {
  1136  				l := scanner.Text()
  1137  				if strings.HasPrefix(l, "a=rid") || strings.HasPrefix(l, "a=simulcast") {
  1138  					continue
  1139  				}
  1140  
  1141  				sessionDescription += l + "\n"
  1142  			}
  1143  			return signalWithRids(sessionDescription, newRids)
  1144  		}))
  1145  
  1146  		assertTracksClosed(t)
  1147  		closePairNow(t, pcOffer, pcAnswer)
  1148  	})
  1149  }
  1150  
  1151  func TestPeerConnection_Regegotiation_ReuseTransceiver(t *testing.T) {
  1152  	lim := test.TimeOut(time.Second * 30)
  1153  	defer lim.Stop()
  1154  
  1155  	report := test.CheckRoutines(t)
  1156  	defer report()
  1157  
  1158  	pcOffer, pcAnswer, err := newPair()
  1159  	if err != nil {
  1160  		t.Fatal(err)
  1161  	}
  1162  
  1163  	vp8Track, err := NewTrackLocalStaticRTP(RTPCodecCapability{MimeType: MimeTypeVP8}, "foo", "bar")
  1164  	assert.NoError(t, err)
  1165  	sender, err := pcOffer.AddTrack(vp8Track)
  1166  	assert.NoError(t, err)
  1167  	assert.NoError(t, signalPair(pcOffer, pcAnswer))
  1168  
  1169  	peerConnectionConnected := untilConnectionState(PeerConnectionStateConnected, pcOffer, pcAnswer)
  1170  	peerConnectionConnected.Wait()
  1171  
  1172  	assert.Equal(t, len(pcOffer.GetTransceivers()), 1)
  1173  	assert.Equal(t, pcOffer.GetTransceivers()[0].getCurrentDirection(), RTPTransceiverDirectionSendonly)
  1174  	assert.NoError(t, pcOffer.RemoveTrack(sender))
  1175  	assert.Equal(t, pcOffer.GetTransceivers()[0].getCurrentDirection(), RTPTransceiverDirectionSendonly)
  1176  
  1177  	// should not reuse tranceiver
  1178  	vp8Track2, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "foo", "bar")
  1179  	assert.NoError(t, err)
  1180  	sender2, err := pcOffer.AddTrack(vp8Track2)
  1181  	assert.NoError(t, err)
  1182  	assert.Equal(t, len(pcOffer.GetTransceivers()), 2)
  1183  	assert.NoError(t, signalPair(pcOffer, pcAnswer))
  1184  	assert.True(t, sender2.rtpTransceiver == pcOffer.GetTransceivers()[1])
  1185  
  1186  	// should reuse first transceiver
  1187  	sender, err = pcOffer.AddTrack(vp8Track)
  1188  	assert.NoError(t, err)
  1189  	assert.Equal(t, len(pcOffer.GetTransceivers()), 2)
  1190  	assert.True(t, sender.rtpTransceiver == pcOffer.GetTransceivers()[0])
  1191  	assert.NoError(t, signalPair(pcOffer, pcAnswer))
  1192  
  1193  	tracksCh := make(chan *TrackRemote, 2)
  1194  	pcAnswer.OnTrack(func(tr *TrackRemote, _ *RTPReceiver) {
  1195  		tracksCh <- tr
  1196  	})
  1197  
  1198  	ssrcReuse := sender.GetParameters().Encodings[0].SSRC
  1199  	for i := 0; i < 10; i++ {
  1200  		assert.NoError(t, vp8Track.WriteRTP(&rtp.Packet{Header: rtp.Header{Version: 2}, Payload: []byte{0, 1, 2, 3, 4, 5}}))
  1201  		time.Sleep(20 * time.Millisecond)
  1202  	}
  1203  
  1204  	// shold not reuse tranceiver between two CreateOffer
  1205  	offer, err := pcOffer.CreateOffer(nil)
  1206  	assert.NoError(t, err)
  1207  	assert.NoError(t, pcOffer.RemoveTrack(sender))
  1208  	assert.NoError(t, pcOffer.SetLocalDescription(offer))
  1209  	assert.NoError(t, pcAnswer.SetRemoteDescription(offer))
  1210  	answer, err := pcAnswer.CreateAnswer(nil)
  1211  	assert.NoError(t, pcAnswer.SetLocalDescription(answer))
  1212  	assert.NoError(t, err)
  1213  	assert.NoError(t, pcOffer.SetRemoteDescription(answer))
  1214  	sender3, err := pcOffer.AddTrack(vp8Track)
  1215  	ssrcNotReuse := sender3.GetParameters().Encodings[0].SSRC
  1216  	assert.NoError(t, err)
  1217  	assert.Equal(t, len(pcOffer.GetTransceivers()), 3)
  1218  	assert.NoError(t, signalPair(pcOffer, pcAnswer))
  1219  	assert.True(t, sender3.rtpTransceiver == pcOffer.GetTransceivers()[2])
  1220  
  1221  	for i := 0; i < 10; i++ {
  1222  		assert.NoError(t, vp8Track.WriteRTP(&rtp.Packet{Header: rtp.Header{Version: 2}, Payload: []byte{0, 1, 2, 3, 4, 5}}))
  1223  		time.Sleep(20 * time.Millisecond)
  1224  	}
  1225  
  1226  	tr1 := <-tracksCh
  1227  	tr2 := <-tracksCh
  1228  	assert.Equal(t, tr1.SSRC(), ssrcReuse)
  1229  	assert.Equal(t, tr2.SSRC(), ssrcNotReuse)
  1230  
  1231  	closePairNow(t, pcOffer, pcAnswer)
  1232  }
  1233  
  1234  func TestPeerConnection_Renegotiation_MidConflict(t *testing.T) {
  1235  	lim := test.TimeOut(time.Second * 30)
  1236  	defer lim.Stop()
  1237  
  1238  	report := test.CheckRoutines(t)
  1239  	defer report()
  1240  
  1241  	offerPC, err := NewPeerConnection(Configuration{})
  1242  	assert.NoError(t, err)
  1243  	answerPC, err := NewPeerConnection(Configuration{})
  1244  	assert.NoError(t, err)
  1245  	_, err = offerPC.CreateDataChannel("test", nil)
  1246  	assert.NoError(t, err)
  1247  
  1248  	_, err = offerPC.AddTransceiverFromKind(RTPCodecTypeVideo, RTPTransceiverInit{Direction: RTPTransceiverDirectionSendonly})
  1249  	assert.NoError(t, err)
  1250  	_, err = offerPC.AddTransceiverFromKind(RTPCodecTypeAudio, RTPTransceiverInit{Direction: RTPTransceiverDirectionSendonly})
  1251  	assert.NoError(t, err)
  1252  
  1253  	offer, err := offerPC.CreateOffer(nil)
  1254  	assert.NoError(t, err)
  1255  	assert.NoError(t, offerPC.SetLocalDescription(offer))
  1256  	assert.NoError(t, answerPC.SetRemoteDescription(offer), offer.SDP)
  1257  	answer, err := answerPC.CreateAnswer(nil)
  1258  	assert.NoError(t, err)
  1259  	assert.NoError(t, answerPC.SetLocalDescription(answer))
  1260  	assert.NoError(t, offerPC.SetRemoteDescription(answer))
  1261  	assert.Equal(t, SignalingStateStable, offerPC.SignalingState())
  1262  
  1263  	tr, err := offerPC.AddTransceiverFromKind(RTPCodecTypeVideo, RTPTransceiverInit{Direction: RTPTransceiverDirectionSendonly})
  1264  	assert.NoError(t, err)
  1265  	assert.NoError(t, tr.SetMid("3"))
  1266  	_, err = offerPC.AddTransceiverFromKind(RTPCodecTypeVideo, RTPTransceiverInit{Direction: RTPTransceiverDirectionSendrecv})
  1267  	assert.NoError(t, err)
  1268  	_, err = offerPC.CreateOffer(nil)
  1269  	assert.NoError(t, err)
  1270  
  1271  	assert.NoError(t, offerPC.Close())
  1272  	assert.NoError(t, answerPC.Close())
  1273  }
  1274  
  1275  func TestPeerConnection_Regegotiation_AnswerAddsTrack(t *testing.T) {
  1276  	lim := test.TimeOut(time.Second * 30)
  1277  	defer lim.Stop()
  1278  
  1279  	report := test.CheckRoutines(t)
  1280  	defer report()
  1281  
  1282  	pcOffer, pcAnswer, err := newPair()
  1283  	if err != nil {
  1284  		t.Fatal(err)
  1285  	}
  1286  
  1287  	tracksCh := make(chan *TrackRemote)
  1288  	pcOffer.OnTrack(func(track *TrackRemote, _ *RTPReceiver) {
  1289  		tracksCh <- track
  1290  		for {
  1291  			if _, _, readErr := track.ReadRTP(); errors.Is(readErr, io.EOF) {
  1292  				return
  1293  			}
  1294  		}
  1295  	})
  1296  
  1297  	vp8Track, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "foo", "bar")
  1298  	assert.NoError(t, err)
  1299  	assert.NoError(t, signalPair(pcOffer, pcAnswer))
  1300  
  1301  	_, err = pcOffer.AddTransceiverFromKind(RTPCodecTypeVideo, RTPTransceiverInit{
  1302  		Direction: RTPTransceiverDirectionRecvonly,
  1303  	})
  1304  	assert.NoError(t, err)
  1305  	assert.NoError(t, signalPair(pcOffer, pcAnswer))
  1306  
  1307  	_, err = pcAnswer.AddTransceiverFromKind(RTPCodecTypeVideo, RTPTransceiverInit{
  1308  		Direction: RTPTransceiverDirectionSendonly,
  1309  	})
  1310  	assert.NoError(t, err)
  1311  
  1312  	assert.NoError(t, err)
  1313  	_, err = pcAnswer.AddTrack(vp8Track)
  1314  	assert.NoError(t, err)
  1315  	assert.NoError(t, signalPair(pcOffer, pcAnswer))
  1316  
  1317  	ctx, cancel := context.WithCancel(context.Background())
  1318  
  1319  	go sendVideoUntilDone(ctx.Done(), t, []*TrackLocalStaticSample{vp8Track})
  1320  
  1321  	<-tracksCh
  1322  	cancel()
  1323  
  1324  	closePairNow(t, pcOffer, pcAnswer)
  1325  }
  1326  
  1327  func TestNegotiationNeededWithRecvonlyTrack(t *testing.T) {
  1328  	lim := test.TimeOut(time.Second * 30)
  1329  	defer lim.Stop()
  1330  
  1331  	report := test.CheckRoutines(t)
  1332  	defer report()
  1333  
  1334  	pcOffer, pcAnswer, err := newPair()
  1335  	if err != nil {
  1336  		t.Fatal(err)
  1337  	}
  1338  
  1339  	var wg sync.WaitGroup
  1340  	wg.Add(1)
  1341  	pcAnswer.OnNegotiationNeeded(wg.Done)
  1342  
  1343  	_, err = pcOffer.AddTransceiverFromKind(RTPCodecTypeVideo, RTPTransceiverInit{Direction: RTPTransceiverDirectionRecvonly})
  1344  	if err != nil {
  1345  		t.Fatal(err)
  1346  	}
  1347  
  1348  	if err := signalPair(pcOffer, pcAnswer); err != nil {
  1349  		t.Fatal(err)
  1350  	}
  1351  
  1352  	onDataChannel, onDataChannelCancel := context.WithCancel(context.Background())
  1353  	pcAnswer.OnDataChannel(func(*DataChannel) {
  1354  		onDataChannelCancel()
  1355  	})
  1356  	<-onDataChannel.Done()
  1357  	wg.Wait()
  1358  
  1359  	closePairNow(t, pcOffer, pcAnswer)
  1360  }
  1361  
  1362  func TestNegotiationNotNeededAfterReplaceTrackNil(t *testing.T) {
  1363  	lim := test.TimeOut(time.Second * 30)
  1364  	defer lim.Stop()
  1365  
  1366  	report := test.CheckRoutines(t)
  1367  	defer report()
  1368  
  1369  	pcOffer, err := NewPeerConnection(Configuration{})
  1370  	assert.NoError(t, err)
  1371  
  1372  	pcAnswer, err := NewPeerConnection(Configuration{})
  1373  	assert.NoError(t, err)
  1374  
  1375  	tr, err := pcOffer.AddTransceiverFromKind(RTPCodecTypeAudio)
  1376  	assert.NoError(t, err)
  1377  
  1378  	assert.NoError(t, signalPair(pcOffer, pcAnswer))
  1379  
  1380  	assert.NoError(t, tr.Sender().ReplaceTrack(nil))
  1381  
  1382  	assert.False(t, pcOffer.checkNegotiationNeeded())
  1383  
  1384  	assert.NoError(t, pcOffer.Close())
  1385  	assert.NoError(t, pcAnswer.Close())
  1386  }