github.com/pion/webrtc/v3@v3.2.24/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/v2/test"
    23  	"github.com/pion/webrtc/v3/internal/util"
    24  	"github.com/pion/webrtc/v3/pkg/media"
    25  	"github.com/pion/webrtc/v3/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(track *TrackRemote, r *RTPReceiver) {
   132  					onTrackFiredFunc()
   133  				})
   134  				assert.NoError(t, signalPair(pcAnswer, pcOffer))
   135  			} else {
   136  				pcAnswer.OnTrack(func(track *TrackRemote, r *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(track *TrackRemote, r *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, r *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, r *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, r *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, r *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(track *TrackRemote, r *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  	assert.NoError(t, api.mediaEngine.RegisterDefaultCodecs())
   607  
   608  	// Invalid STUN server on purpose, will stop ICE Gathering from completing in time
   609  	pcOffer, pcAnswer, err := api.newPair(Configuration{
   610  		ICEServers: []ICEServer{
   611  			{
   612  				URLs: []string{"stun:127.0.0.1:5000"},
   613  			},
   614  		},
   615  	})
   616  	if err != nil {
   617  		t.Fatal(err)
   618  	}
   619  
   620  	_, err = pcOffer.CreateDataChannel("test-channel", nil)
   621  	assert.NoError(t, err)
   622  
   623  	var wg sync.WaitGroup
   624  	wg.Add(2)
   625  	pcOffer.OnICECandidate(func(c *ICECandidate) {
   626  		if c != nil {
   627  			assert.NoError(t, pcAnswer.AddICECandidate(c.ToJSON()))
   628  		} else {
   629  			wg.Done()
   630  		}
   631  	})
   632  	pcAnswer.OnICECandidate(func(c *ICECandidate) {
   633  		if c != nil {
   634  			assert.NoError(t, pcOffer.AddICECandidate(c.ToJSON()))
   635  		} else {
   636  			wg.Done()
   637  		}
   638  	})
   639  
   640  	negotiate := func() {
   641  		offer, err := pcOffer.CreateOffer(nil)
   642  		assert.NoError(t, err)
   643  
   644  		assert.NoError(t, pcAnswer.SetRemoteDescription(offer))
   645  		assert.NoError(t, pcOffer.SetLocalDescription(offer))
   646  
   647  		answer, err := pcAnswer.CreateAnswer(nil)
   648  		assert.NoError(t, err)
   649  
   650  		assert.NoError(t, pcOffer.SetRemoteDescription(answer))
   651  		assert.NoError(t, pcAnswer.SetLocalDescription(answer))
   652  	}
   653  	negotiate()
   654  	negotiate()
   655  
   656  	pcOffer.ops.Done()
   657  	pcAnswer.ops.Done()
   658  	wg.Wait()
   659  
   660  	closePairNow(t, pcOffer, pcAnswer)
   661  }
   662  
   663  func TestPeerConnection_Renegotiation_SetLocalDescription(t *testing.T) {
   664  	lim := test.TimeOut(time.Second * 30)
   665  	defer lim.Stop()
   666  
   667  	report := test.CheckRoutines(t)
   668  	defer report()
   669  
   670  	pcOffer, pcAnswer, err := newPair()
   671  	if err != nil {
   672  		t.Fatal(err)
   673  	}
   674  
   675  	onTrackFired, onTrackFiredFunc := context.WithCancel(context.Background())
   676  	pcOffer.OnTrack(func(track *TrackRemote, r *RTPReceiver) {
   677  		onTrackFiredFunc()
   678  	})
   679  
   680  	assert.NoError(t, signalPair(pcOffer, pcAnswer))
   681  
   682  	pcOffer.ops.Done()
   683  	pcAnswer.ops.Done()
   684  
   685  	_, err = pcOffer.AddTransceiverFromKind(RTPCodecTypeVideo, RTPTransceiverInit{Direction: RTPTransceiverDirectionRecvonly})
   686  	assert.NoError(t, err)
   687  
   688  	localTrack, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "foo", "bar")
   689  	assert.NoError(t, err)
   690  
   691  	sender, err := pcAnswer.AddTrack(localTrack)
   692  	assert.NoError(t, err)
   693  
   694  	offer, err := pcOffer.CreateOffer(nil)
   695  	assert.NoError(t, err)
   696  	assert.NoError(t, pcOffer.SetLocalDescription(offer))
   697  	assert.NoError(t, pcAnswer.SetRemoteDescription(offer))
   698  	assert.False(t, sender.isNegotiated())
   699  	answer, err := pcAnswer.CreateAnswer(nil)
   700  	assert.NoError(t, err)
   701  	assert.True(t, sender.isNegotiated())
   702  
   703  	pcAnswer.ops.Done()
   704  	assert.Equal(t, 0, len(localTrack.rtpTrack.bindings))
   705  
   706  	assert.NoError(t, pcAnswer.SetLocalDescription(answer))
   707  
   708  	pcAnswer.ops.Done()
   709  	assert.Equal(t, 1, len(localTrack.rtpTrack.bindings))
   710  
   711  	assert.NoError(t, pcOffer.SetRemoteDescription(answer))
   712  
   713  	sendVideoUntilDone(onTrackFired.Done(), t, []*TrackLocalStaticSample{localTrack})
   714  
   715  	closePairNow(t, pcOffer, pcAnswer)
   716  }
   717  
   718  // Issue #346, don't start the SCTP Subsystem if the RemoteDescription doesn't contain one
   719  // Before we would always start it, and re-negotiations would fail because SCTP was in flight
   720  func TestPeerConnection_Renegotiation_NoApplication(t *testing.T) {
   721  	signalPairExcludeDataChannel := func(pcOffer, pcAnswer *PeerConnection) {
   722  		offer, err := pcOffer.CreateOffer(nil)
   723  		assert.NoError(t, err)
   724  		offerGatheringComplete := GatheringCompletePromise(pcOffer)
   725  		assert.NoError(t, pcOffer.SetLocalDescription(offer))
   726  		<-offerGatheringComplete
   727  
   728  		assert.NoError(t, pcAnswer.SetRemoteDescription(*pcOffer.LocalDescription()))
   729  
   730  		answer, err := pcAnswer.CreateAnswer(nil)
   731  		assert.NoError(t, err)
   732  
   733  		answerGatheringComplete := GatheringCompletePromise(pcAnswer)
   734  		assert.NoError(t, pcAnswer.SetLocalDescription(answer))
   735  		<-answerGatheringComplete
   736  
   737  		assert.NoError(t, pcOffer.SetRemoteDescription(*pcAnswer.LocalDescription()))
   738  	}
   739  
   740  	lim := test.TimeOut(time.Second * 30)
   741  	defer lim.Stop()
   742  
   743  	report := test.CheckRoutines(t)
   744  	defer report()
   745  
   746  	pcOffer, pcAnswer, err := newPair()
   747  	if err != nil {
   748  		t.Fatal(err)
   749  	}
   750  
   751  	pcOfferConnected, pcOfferConnectedCancel := context.WithCancel(context.Background())
   752  	pcOffer.OnICEConnectionStateChange(func(i ICEConnectionState) {
   753  		if i == ICEConnectionStateConnected {
   754  			pcOfferConnectedCancel()
   755  		}
   756  	})
   757  
   758  	pcAnswerConnected, pcAnswerConnectedCancel := context.WithCancel(context.Background())
   759  	pcAnswer.OnICEConnectionStateChange(func(i ICEConnectionState) {
   760  		if i == ICEConnectionStateConnected {
   761  			pcAnswerConnectedCancel()
   762  		}
   763  	})
   764  
   765  	_, err = pcOffer.AddTransceiverFromKind(RTPCodecTypeVideo, RTPTransceiverInit{Direction: RTPTransceiverDirectionSendrecv})
   766  	assert.NoError(t, err)
   767  
   768  	_, err = pcAnswer.AddTransceiverFromKind(RTPCodecTypeVideo, RTPTransceiverInit{Direction: RTPTransceiverDirectionSendrecv})
   769  	assert.NoError(t, err)
   770  
   771  	signalPairExcludeDataChannel(pcOffer, pcAnswer)
   772  	pcOffer.ops.Done()
   773  	pcAnswer.ops.Done()
   774  
   775  	signalPairExcludeDataChannel(pcOffer, pcAnswer)
   776  	pcOffer.ops.Done()
   777  	pcAnswer.ops.Done()
   778  
   779  	<-pcAnswerConnected.Done()
   780  	<-pcOfferConnected.Done()
   781  
   782  	assert.Equal(t, pcOffer.SCTP().State(), SCTPTransportStateConnecting)
   783  	assert.Equal(t, pcAnswer.SCTP().State(), SCTPTransportStateConnecting)
   784  
   785  	closePairNow(t, pcOffer, pcAnswer)
   786  }
   787  
   788  func TestAddDataChannelDuringRenegotiation(t *testing.T) {
   789  	lim := test.TimeOut(time.Second * 10)
   790  	defer lim.Stop()
   791  
   792  	report := test.CheckRoutines(t)
   793  	defer report()
   794  
   795  	pcOffer, err := NewPeerConnection(Configuration{})
   796  	assert.NoError(t, err)
   797  
   798  	pcAnswer, err := NewPeerConnection(Configuration{})
   799  	assert.NoError(t, err)
   800  
   801  	track, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion")
   802  	assert.NoError(t, err)
   803  
   804  	_, err = pcOffer.AddTrack(track)
   805  	assert.NoError(t, err)
   806  
   807  	offer, err := pcOffer.CreateOffer(nil)
   808  	assert.NoError(t, err)
   809  
   810  	offerGatheringComplete := GatheringCompletePromise(pcOffer)
   811  	assert.NoError(t, pcOffer.SetLocalDescription(offer))
   812  	<-offerGatheringComplete
   813  
   814  	assert.NoError(t, pcAnswer.SetRemoteDescription(*pcOffer.LocalDescription()))
   815  
   816  	answer, err := pcAnswer.CreateAnswer(nil)
   817  	assert.NoError(t, err)
   818  
   819  	answerGatheringComplete := GatheringCompletePromise(pcAnswer)
   820  	assert.NoError(t, pcAnswer.SetLocalDescription(answer))
   821  	<-answerGatheringComplete
   822  
   823  	assert.NoError(t, pcOffer.SetRemoteDescription(*pcAnswer.LocalDescription()))
   824  
   825  	_, err = pcOffer.CreateDataChannel("data-channel", nil)
   826  	assert.NoError(t, err)
   827  
   828  	// Assert that DataChannel is in offer now
   829  	offer, err = pcOffer.CreateOffer(nil)
   830  	assert.NoError(t, err)
   831  
   832  	applicationMediaSectionCount := 0
   833  	for _, d := range offer.parsed.MediaDescriptions {
   834  		if d.MediaName.Media == mediaSectionApplication {
   835  			applicationMediaSectionCount++
   836  		}
   837  	}
   838  	assert.Equal(t, applicationMediaSectionCount, 1)
   839  
   840  	onDataChannelFired, onDataChannelFiredFunc := context.WithCancel(context.Background())
   841  	pcAnswer.OnDataChannel(func(*DataChannel) {
   842  		onDataChannelFiredFunc()
   843  	})
   844  
   845  	assert.NoError(t, signalPair(pcOffer, pcAnswer))
   846  
   847  	<-onDataChannelFired.Done()
   848  	closePairNow(t, pcOffer, pcAnswer)
   849  }
   850  
   851  // Assert that CreateDataChannel fires OnNegotiationNeeded
   852  func TestNegotiationCreateDataChannel(t *testing.T) {
   853  	lim := test.TimeOut(time.Second * 30)
   854  	defer lim.Stop()
   855  
   856  	report := test.CheckRoutines(t)
   857  	defer report()
   858  
   859  	pc, err := NewPeerConnection(Configuration{})
   860  	assert.NoError(t, err)
   861  
   862  	var wg sync.WaitGroup
   863  	wg.Add(1)
   864  
   865  	pc.OnNegotiationNeeded(func() {
   866  		defer func() {
   867  			wg.Done()
   868  		}()
   869  	})
   870  
   871  	// Create DataChannel, wait until OnNegotiationNeeded is fired
   872  	if _, err = pc.CreateDataChannel("testChannel", nil); err != nil {
   873  		t.Error(err.Error())
   874  	}
   875  
   876  	// Wait until OnNegotiationNeeded is fired
   877  	wg.Wait()
   878  	assert.NoError(t, pc.Close())
   879  }
   880  
   881  func TestNegotiationNeededRemoveTrack(t *testing.T) {
   882  	var wg sync.WaitGroup
   883  	wg.Add(1)
   884  
   885  	report := test.CheckRoutines(t)
   886  	defer report()
   887  
   888  	pcOffer, err := NewPeerConnection(Configuration{})
   889  	assert.NoError(t, err)
   890  	pcAnswer, err := NewPeerConnection(Configuration{})
   891  	assert.NoError(t, err)
   892  
   893  	track, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion")
   894  	assert.NoError(t, err)
   895  
   896  	pcOffer.OnNegotiationNeeded(func() {
   897  		wg.Add(1)
   898  		offer, createOfferErr := pcOffer.CreateOffer(nil)
   899  		assert.NoError(t, createOfferErr)
   900  
   901  		offerGatheringComplete := GatheringCompletePromise(pcOffer)
   902  		assert.NoError(t, pcOffer.SetLocalDescription(offer))
   903  
   904  		<-offerGatheringComplete
   905  		assert.NoError(t, pcAnswer.SetRemoteDescription(*pcOffer.LocalDescription()))
   906  
   907  		answer, createAnswerErr := pcAnswer.CreateAnswer(nil)
   908  		assert.NoError(t, createAnswerErr)
   909  
   910  		answerGatheringComplete := GatheringCompletePromise(pcAnswer)
   911  		assert.NoError(t, pcAnswer.SetLocalDescription(answer))
   912  
   913  		<-answerGatheringComplete
   914  		assert.NoError(t, pcOffer.SetRemoteDescription(*pcAnswer.LocalDescription()))
   915  		wg.Done()
   916  		wg.Done()
   917  	})
   918  
   919  	sender, err := pcOffer.AddTrack(track)
   920  	assert.NoError(t, err)
   921  
   922  	assert.NoError(t, track.WriteSample(media.Sample{Data: []byte{0x00}, Duration: time.Second}))
   923  
   924  	wg.Wait()
   925  
   926  	wg.Add(1)
   927  	assert.NoError(t, pcOffer.RemoveTrack(sender))
   928  
   929  	wg.Wait()
   930  
   931  	closePairNow(t, pcOffer, pcAnswer)
   932  }
   933  
   934  func TestNegotiationNeededStressOneSided(t *testing.T) {
   935  	lim := test.TimeOut(time.Second * 30)
   936  	defer lim.Stop()
   937  
   938  	report := test.CheckRoutines(t)
   939  	defer report()
   940  
   941  	pcA, pcB, err := newPair()
   942  	assert.NoError(t, err)
   943  
   944  	const expectedTrackCount = 500
   945  	ctx, done := context.WithCancel(context.Background())
   946  	pcA.OnNegotiationNeeded(func() {
   947  		count := len(pcA.GetTransceivers())
   948  		assert.NoError(t, signalPair(pcA, pcB))
   949  		if count == expectedTrackCount {
   950  			done()
   951  		}
   952  	})
   953  
   954  	for i := 0; i < expectedTrackCount; i++ {
   955  		track, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion")
   956  		assert.NoError(t, err)
   957  
   958  		_, err = pcA.AddTrack(track)
   959  		assert.NoError(t, err)
   960  	}
   961  	<-ctx.Done()
   962  	assert.Equal(t, expectedTrackCount, len(pcB.GetTransceivers()))
   963  	closePairNow(t, pcA, pcB)
   964  }
   965  
   966  // TestPeerConnection_Renegotiation_DisableTrack asserts that if a remote track is set inactive
   967  // that locally it goes inactive as well
   968  func TestPeerConnection_Renegotiation_DisableTrack(t *testing.T) {
   969  	lim := test.TimeOut(time.Second * 30)
   970  	defer lim.Stop()
   971  
   972  	report := test.CheckRoutines(t)
   973  	defer report()
   974  
   975  	pcOffer, pcAnswer, err := newPair()
   976  	assert.NoError(t, err)
   977  
   978  	// Create two transceivers
   979  	_, err = pcOffer.AddTransceiverFromKind(RTPCodecTypeVideo)
   980  	assert.NoError(t, err)
   981  
   982  	transceiver, err := pcOffer.AddTransceiverFromKind(RTPCodecTypeVideo)
   983  	assert.NoError(t, err)
   984  
   985  	assert.NoError(t, signalPair(pcOffer, pcAnswer))
   986  
   987  	// Assert we have three active transceivers
   988  	offer, err := pcOffer.CreateOffer(nil)
   989  	assert.NoError(t, err)
   990  	assert.Equal(t, strings.Count(offer.SDP, "a=sendrecv"), 3)
   991  
   992  	// Assert we have two active transceivers, one inactive
   993  	assert.NoError(t, transceiver.Stop())
   994  	offer, err = pcOffer.CreateOffer(nil)
   995  	assert.NoError(t, err)
   996  	assert.Equal(t, strings.Count(offer.SDP, "a=sendrecv"), 2)
   997  	assert.Equal(t, strings.Count(offer.SDP, "a=inactive"), 1)
   998  
   999  	// Assert that the offer disabled one of our transceivers
  1000  	assert.NoError(t, pcAnswer.SetRemoteDescription(offer))
  1001  	answer, err := pcAnswer.CreateAnswer(nil)
  1002  	assert.NoError(t, err)
  1003  	assert.Equal(t, strings.Count(answer.SDP, "a=sendrecv"), 1) // DataChannel
  1004  	assert.Equal(t, strings.Count(answer.SDP, "a=recvonly"), 1)
  1005  	assert.Equal(t, strings.Count(answer.SDP, "a=inactive"), 1)
  1006  
  1007  	closePairNow(t, pcOffer, pcAnswer)
  1008  }
  1009  
  1010  func TestPeerConnection_Renegotiation_Simulcast(t *testing.T) {
  1011  	lim := test.TimeOut(time.Second * 30)
  1012  	defer lim.Stop()
  1013  
  1014  	report := test.CheckRoutines(t)
  1015  	defer report()
  1016  
  1017  	m := &MediaEngine{}
  1018  	if err := m.RegisterDefaultCodecs(); err != nil {
  1019  		panic(err)
  1020  	}
  1021  	registerSimulcastHeaderExtensions(m, RTPCodecTypeVideo)
  1022  
  1023  	originalRids := []string{"a", "b", "c"}
  1024  	signalWithRids := func(sessionDescription string, rids []string) string {
  1025  		sessionDescription = strings.SplitAfter(sessionDescription, "a=end-of-candidates\r\n")[0]
  1026  		sessionDescription = filterSsrc(sessionDescription)
  1027  		for _, rid := range rids {
  1028  			sessionDescription += "a=" + sdpAttributeRid + ":" + rid + " send\r\n"
  1029  		}
  1030  		return sessionDescription + "a=simulcast:send " + strings.Join(rids, ";") + "\r\n"
  1031  	}
  1032  
  1033  	var trackMapLock sync.RWMutex
  1034  	trackMap := map[string]*TrackRemote{}
  1035  
  1036  	onTrackHandler := func(track *TrackRemote, _ *RTPReceiver) {
  1037  		trackMapLock.Lock()
  1038  		defer trackMapLock.Unlock()
  1039  		trackMap[track.RID()] = track
  1040  	}
  1041  
  1042  	sendUntilAllTracksFired := func(vp8Writer *TrackLocalStaticRTP, rids []string) {
  1043  		allTracksFired := func() bool {
  1044  			trackMapLock.Lock()
  1045  			defer trackMapLock.Unlock()
  1046  
  1047  			return len(trackMap) == len(rids)
  1048  		}
  1049  
  1050  		for sequenceNumber := uint16(0); !allTracksFired(); sequenceNumber++ {
  1051  			time.Sleep(20 * time.Millisecond)
  1052  
  1053  			for ssrc, rid := range rids {
  1054  				header := &rtp.Header{
  1055  					Version:        2,
  1056  					SSRC:           uint32(ssrc),
  1057  					SequenceNumber: sequenceNumber,
  1058  					PayloadType:    96,
  1059  				}
  1060  				assert.NoError(t, header.SetExtension(1, []byte("0")))
  1061  				assert.NoError(t, header.SetExtension(2, []byte(rid)))
  1062  
  1063  				_, err := vp8Writer.bindings[0].writeStream.WriteRTP(header, []byte{0x00})
  1064  				assert.NoError(t, err)
  1065  			}
  1066  		}
  1067  	}
  1068  
  1069  	assertTracksClosed := func(t *testing.T) {
  1070  		trackMapLock.Lock()
  1071  		defer trackMapLock.Unlock()
  1072  
  1073  		for _, track := range trackMap {
  1074  			_, _, err := track.ReadRTP() // Ignore first Read, this is our peeked data
  1075  			assert.Nil(t, err)
  1076  
  1077  			_, _, err = track.ReadRTP()
  1078  			assert.Equal(t, err, io.EOF)
  1079  		}
  1080  	}
  1081  
  1082  	t.Run("Disable Transceiver", func(t *testing.T) {
  1083  		trackMap = map[string]*TrackRemote{}
  1084  		pcOffer, pcAnswer, err := NewAPI(WithMediaEngine(m)).newPair(Configuration{})
  1085  		assert.NoError(t, err)
  1086  
  1087  		vp8Writer, err := NewTrackLocalStaticRTP(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion2")
  1088  		assert.NoError(t, err)
  1089  
  1090  		rtpTransceiver, err := pcOffer.AddTransceiverFromTrack(
  1091  			vp8Writer,
  1092  			RTPTransceiverInit{
  1093  				Direction: RTPTransceiverDirectionSendonly,
  1094  			},
  1095  		)
  1096  		assert.NoError(t, err)
  1097  
  1098  		assert.NoError(t, signalPairWithModification(pcOffer, pcAnswer, func(sessionDescription string) string {
  1099  			return signalWithRids(sessionDescription, originalRids)
  1100  		}))
  1101  
  1102  		pcAnswer.OnTrack(onTrackHandler)
  1103  		sendUntilAllTracksFired(vp8Writer, originalRids)
  1104  
  1105  		assert.NoError(t, pcOffer.RemoveTrack(rtpTransceiver.Sender()))
  1106  		assert.NoError(t, signalPairWithModification(pcOffer, pcAnswer, func(sessionDescription string) string {
  1107  			sessionDescription = strings.SplitAfter(sessionDescription, "a=end-of-candidates\r\n")[0]
  1108  			return sessionDescription
  1109  		}))
  1110  
  1111  		assertTracksClosed(t)
  1112  		closePairNow(t, pcOffer, pcAnswer)
  1113  	})
  1114  
  1115  	t.Run("Change RID", func(t *testing.T) {
  1116  		trackMap = map[string]*TrackRemote{}
  1117  		pcOffer, pcAnswer, err := NewAPI(WithMediaEngine(m)).newPair(Configuration{})
  1118  		assert.NoError(t, err)
  1119  
  1120  		vp8Writer, err := NewTrackLocalStaticRTP(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion2")
  1121  		assert.NoError(t, err)
  1122  
  1123  		_, err = pcOffer.AddTransceiverFromTrack(
  1124  			vp8Writer,
  1125  			RTPTransceiverInit{
  1126  				Direction: RTPTransceiverDirectionSendonly,
  1127  			},
  1128  		)
  1129  		assert.NoError(t, err)
  1130  
  1131  		assert.NoError(t, signalPairWithModification(pcOffer, pcAnswer, func(sessionDescription string) string {
  1132  			return signalWithRids(sessionDescription, originalRids)
  1133  		}))
  1134  
  1135  		pcAnswer.OnTrack(onTrackHandler)
  1136  		sendUntilAllTracksFired(vp8Writer, originalRids)
  1137  
  1138  		newRids := []string{"d", "e", "f"}
  1139  		assert.NoError(t, signalPairWithModification(pcOffer, pcAnswer, func(sessionDescription string) string {
  1140  			scanner := bufio.NewScanner(strings.NewReader(sessionDescription))
  1141  			sessionDescription = ""
  1142  			for scanner.Scan() {
  1143  				l := scanner.Text()
  1144  				if strings.HasPrefix(l, "a=rid") || strings.HasPrefix(l, "a=simulcast") {
  1145  					continue
  1146  				}
  1147  
  1148  				sessionDescription += l + "\n"
  1149  			}
  1150  			return signalWithRids(sessionDescription, newRids)
  1151  		}))
  1152  
  1153  		assertTracksClosed(t)
  1154  		closePairNow(t, pcOffer, pcAnswer)
  1155  	})
  1156  }
  1157  
  1158  func TestPeerConnection_Regegotiation_ReuseTransceiver(t *testing.T) {
  1159  	lim := test.TimeOut(time.Second * 30)
  1160  	defer lim.Stop()
  1161  
  1162  	report := test.CheckRoutines(t)
  1163  	defer report()
  1164  
  1165  	pcOffer, pcAnswer, err := newPair()
  1166  	if err != nil {
  1167  		t.Fatal(err)
  1168  	}
  1169  
  1170  	vp8Track, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "foo", "bar")
  1171  	assert.NoError(t, err)
  1172  	sender, err := pcOffer.AddTrack(vp8Track)
  1173  	assert.NoError(t, err)
  1174  	assert.NoError(t, signalPair(pcOffer, pcAnswer))
  1175  
  1176  	assert.Equal(t, len(pcOffer.GetTransceivers()), 1)
  1177  	assert.Equal(t, pcOffer.GetTransceivers()[0].getCurrentDirection(), RTPTransceiverDirectionSendonly)
  1178  	assert.NoError(t, pcOffer.RemoveTrack(sender))
  1179  	assert.Equal(t, pcOffer.GetTransceivers()[0].getCurrentDirection(), RTPTransceiverDirectionSendonly)
  1180  
  1181  	// should not reuse tranceiver
  1182  	vp8Track2, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "foo", "bar")
  1183  	assert.NoError(t, err)
  1184  	sender2, err := pcOffer.AddTrack(vp8Track2)
  1185  	assert.NoError(t, err)
  1186  	assert.Equal(t, len(pcOffer.GetTransceivers()), 2)
  1187  	assert.NoError(t, signalPair(pcOffer, pcAnswer))
  1188  	assert.True(t, sender2.rtpTransceiver == pcOffer.GetTransceivers()[1])
  1189  
  1190  	// should reuse first transceiver
  1191  	sender, err = pcOffer.AddTrack(vp8Track)
  1192  	assert.NoError(t, err)
  1193  	assert.Equal(t, len(pcOffer.GetTransceivers()), 2)
  1194  	assert.True(t, sender.rtpTransceiver == pcOffer.GetTransceivers()[0])
  1195  
  1196  	closePairNow(t, pcOffer, pcAnswer)
  1197  }
  1198  
  1199  func TestPeerConnection_Renegotiation_MidConflict(t *testing.T) {
  1200  	lim := test.TimeOut(time.Second * 30)
  1201  	defer lim.Stop()
  1202  
  1203  	report := test.CheckRoutines(t)
  1204  	defer report()
  1205  
  1206  	offerPC, err := NewPeerConnection(Configuration{})
  1207  	assert.NoError(t, err)
  1208  	answerPC, err := NewPeerConnection(Configuration{})
  1209  	assert.NoError(t, err)
  1210  	_, err = offerPC.CreateDataChannel("test", nil)
  1211  	assert.NoError(t, err)
  1212  
  1213  	_, err = offerPC.AddTransceiverFromKind(RTPCodecTypeVideo, RtpTransceiverInit{Direction: RTPTransceiverDirectionSendonly})
  1214  	assert.NoError(t, err)
  1215  	_, err = offerPC.AddTransceiverFromKind(RTPCodecTypeAudio, RtpTransceiverInit{Direction: RTPTransceiverDirectionSendonly})
  1216  	assert.NoError(t, err)
  1217  
  1218  	offer, err := offerPC.CreateOffer(nil)
  1219  	assert.NoError(t, err)
  1220  	assert.NoError(t, offerPC.SetLocalDescription(offer))
  1221  	assert.NoError(t, answerPC.SetRemoteDescription(offer), offer.SDP)
  1222  	answer, err := answerPC.CreateAnswer(nil)
  1223  	assert.NoError(t, err)
  1224  	assert.NoError(t, answerPC.SetLocalDescription(answer))
  1225  	assert.NoError(t, offerPC.SetRemoteDescription(answer))
  1226  	assert.Equal(t, SignalingStateStable, offerPC.SignalingState())
  1227  
  1228  	tr, err := offerPC.AddTransceiverFromKind(RTPCodecTypeVideo, RtpTransceiverInit{Direction: RTPTransceiverDirectionSendonly})
  1229  	assert.NoError(t, err)
  1230  	assert.NoError(t, tr.SetMid("3"))
  1231  	_, err = offerPC.AddTransceiverFromKind(RTPCodecTypeVideo, RtpTransceiverInit{Direction: RTPTransceiverDirectionSendrecv})
  1232  	assert.NoError(t, err)
  1233  	_, err = offerPC.CreateOffer(nil)
  1234  	assert.NoError(t, err)
  1235  
  1236  	assert.NoError(t, offerPC.Close())
  1237  	assert.NoError(t, answerPC.Close())
  1238  }
  1239  
  1240  func TestPeerConnection_Regegotiation_AnswerAddsTrack(t *testing.T) {
  1241  	lim := test.TimeOut(time.Second * 30)
  1242  	defer lim.Stop()
  1243  
  1244  	report := test.CheckRoutines(t)
  1245  	defer report()
  1246  
  1247  	pcOffer, pcAnswer, err := newPair()
  1248  	if err != nil {
  1249  		t.Fatal(err)
  1250  	}
  1251  
  1252  	tracksCh := make(chan *TrackRemote)
  1253  	pcOffer.OnTrack(func(track *TrackRemote, r *RTPReceiver) {
  1254  		tracksCh <- track
  1255  		for {
  1256  			if _, _, readErr := track.ReadRTP(); errors.Is(readErr, io.EOF) {
  1257  				return
  1258  			}
  1259  		}
  1260  	})
  1261  
  1262  	vp8Track, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "foo", "bar")
  1263  	assert.NoError(t, err)
  1264  	assert.NoError(t, signalPair(pcOffer, pcAnswer))
  1265  
  1266  	_, err = pcOffer.AddTransceiverFromKind(RTPCodecTypeVideo, RtpTransceiverInit{
  1267  		Direction: RTPTransceiverDirectionRecvonly,
  1268  	})
  1269  	assert.NoError(t, err)
  1270  	assert.NoError(t, signalPair(pcOffer, pcAnswer))
  1271  
  1272  	_, err = pcAnswer.AddTransceiverFromKind(RTPCodecTypeVideo, RtpTransceiverInit{
  1273  		Direction: RTPTransceiverDirectionSendonly,
  1274  	})
  1275  	assert.NoError(t, err)
  1276  
  1277  	assert.NoError(t, err)
  1278  	_, err = pcAnswer.AddTrack(vp8Track)
  1279  	assert.NoError(t, err)
  1280  	assert.NoError(t, signalPair(pcOffer, pcAnswer))
  1281  
  1282  	ctx, cancel := context.WithCancel(context.Background())
  1283  
  1284  	go sendVideoUntilDone(ctx.Done(), t, []*TrackLocalStaticSample{vp8Track})
  1285  
  1286  	<-tracksCh
  1287  	cancel()
  1288  
  1289  	closePairNow(t, pcOffer, pcAnswer)
  1290  }
  1291  
  1292  func TestNegotiationNeededWithRecvonlyTrack(t *testing.T) {
  1293  	lim := test.TimeOut(time.Second * 30)
  1294  	defer lim.Stop()
  1295  
  1296  	report := test.CheckRoutines(t)
  1297  	defer report()
  1298  
  1299  	pcOffer, pcAnswer, err := newPair()
  1300  	if err != nil {
  1301  		t.Fatal(err)
  1302  	}
  1303  
  1304  	var wg sync.WaitGroup
  1305  	wg.Add(1)
  1306  	pcAnswer.OnNegotiationNeeded(wg.Done)
  1307  
  1308  	_, err = pcOffer.AddTransceiverFromKind(RTPCodecTypeVideo, RTPTransceiverInit{Direction: RTPTransceiverDirectionRecvonly})
  1309  	if err != nil {
  1310  		t.Fatal(err)
  1311  	}
  1312  
  1313  	if err := signalPair(pcOffer, pcAnswer); err != nil {
  1314  		t.Fatal(err)
  1315  	}
  1316  
  1317  	onDataChannel, onDataChannelCancel := context.WithCancel(context.Background())
  1318  	pcAnswer.OnDataChannel(func(d *DataChannel) {
  1319  		onDataChannelCancel()
  1320  	})
  1321  	<-onDataChannel.Done()
  1322  	wg.Wait()
  1323  
  1324  	closePairNow(t, pcOffer, pcAnswer)
  1325  }