github.com/pion/webrtc/v3@v3.2.24/peerconnection_media_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  	"bytes"
    12  	"context"
    13  	"errors"
    14  	"fmt"
    15  	"io"
    16  	"strings"
    17  	"sync"
    18  	"testing"
    19  	"time"
    20  
    21  	"github.com/pion/logging"
    22  	"github.com/pion/randutil"
    23  	"github.com/pion/rtcp"
    24  	"github.com/pion/rtp"
    25  	"github.com/pion/sdp/v3"
    26  	"github.com/pion/transport/v2/test"
    27  	"github.com/pion/webrtc/v3/pkg/media"
    28  	"github.com/stretchr/testify/assert"
    29  	"github.com/stretchr/testify/require"
    30  )
    31  
    32  var (
    33  	errIncomingTrackIDInvalid    = errors.New("incoming Track ID is invalid")
    34  	errIncomingTrackLabelInvalid = errors.New("incoming Track Label is invalid")
    35  	errNoTransceiverwithMid      = errors.New("no transceiver with mid")
    36  )
    37  
    38  func registerSimulcastHeaderExtensions(m *MediaEngine, codecType RTPCodecType) {
    39  	for _, extension := range []string{
    40  		sdp.SDESMidURI,
    41  		sdp.SDESRTPStreamIDURI,
    42  		sdesRepairRTPStreamIDURI,
    43  	} {
    44  		if err := m.RegisterHeaderExtension(RTPHeaderExtensionCapability{URI: extension}, codecType); err != nil {
    45  			panic(err)
    46  		}
    47  	}
    48  }
    49  
    50  /*
    51  Integration test for bi-directional peers
    52  
    53  This asserts we can send RTP and RTCP both ways, and blocks until
    54  each side gets something (and asserts payload contents)
    55  */
    56  // nolint: gocyclo
    57  func TestPeerConnection_Media_Sample(t *testing.T) {
    58  	const (
    59  		expectedTrackID  = "video"
    60  		expectedStreamID = "pion"
    61  	)
    62  
    63  	lim := test.TimeOut(time.Second * 30)
    64  	defer lim.Stop()
    65  
    66  	report := test.CheckRoutines(t)
    67  	defer report()
    68  
    69  	pcOffer, pcAnswer, err := newPair()
    70  	if err != nil {
    71  		t.Fatal(err)
    72  	}
    73  
    74  	awaitRTPRecv := make(chan bool)
    75  	awaitRTPRecvClosed := make(chan bool)
    76  	awaitRTPSend := make(chan bool)
    77  
    78  	awaitRTCPSenderRecv := make(chan bool)
    79  	awaitRTCPSenderSend := make(chan error)
    80  
    81  	awaitRTCPReceiverRecv := make(chan error)
    82  	awaitRTCPReceiverSend := make(chan error)
    83  
    84  	trackMetadataValid := make(chan error)
    85  
    86  	pcAnswer.OnTrack(func(track *TrackRemote, receiver *RTPReceiver) {
    87  		if track.ID() != expectedTrackID {
    88  			trackMetadataValid <- fmt.Errorf("%w: expected(%s) actual(%s)", errIncomingTrackIDInvalid, expectedTrackID, track.ID())
    89  			return
    90  		}
    91  
    92  		if track.StreamID() != expectedStreamID {
    93  			trackMetadataValid <- fmt.Errorf("%w: expected(%s) actual(%s)", errIncomingTrackLabelInvalid, expectedStreamID, track.StreamID())
    94  			return
    95  		}
    96  		close(trackMetadataValid)
    97  
    98  		go func() {
    99  			for {
   100  				time.Sleep(time.Millisecond * 100)
   101  				if routineErr := pcAnswer.WriteRTCP([]rtcp.Packet{&rtcp.RapidResynchronizationRequest{SenderSSRC: uint32(track.SSRC()), MediaSSRC: uint32(track.SSRC())}}); routineErr != nil {
   102  					awaitRTCPReceiverSend <- routineErr
   103  					return
   104  				}
   105  
   106  				select {
   107  				case <-awaitRTCPSenderRecv:
   108  					close(awaitRTCPReceiverSend)
   109  					return
   110  				default:
   111  				}
   112  			}
   113  		}()
   114  
   115  		go func() {
   116  			_, _, routineErr := receiver.Read(make([]byte, 1400))
   117  			if routineErr != nil {
   118  				awaitRTCPReceiverRecv <- routineErr
   119  			} else {
   120  				close(awaitRTCPReceiverRecv)
   121  			}
   122  		}()
   123  
   124  		haveClosedAwaitRTPRecv := false
   125  		for {
   126  			p, _, routineErr := track.ReadRTP()
   127  			if routineErr != nil {
   128  				close(awaitRTPRecvClosed)
   129  				return
   130  			} else if bytes.Equal(p.Payload, []byte{0x10, 0x00}) && !haveClosedAwaitRTPRecv {
   131  				haveClosedAwaitRTPRecv = true
   132  				close(awaitRTPRecv)
   133  			}
   134  		}
   135  	})
   136  
   137  	vp8Track, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, expectedTrackID, expectedStreamID)
   138  	if err != nil {
   139  		t.Fatal(err)
   140  	}
   141  	sender, err := pcOffer.AddTrack(vp8Track)
   142  	if err != nil {
   143  		t.Fatal(err)
   144  	}
   145  
   146  	go func() {
   147  		for {
   148  			time.Sleep(time.Millisecond * 100)
   149  			if pcOffer.ICEConnectionState() != ICEConnectionStateConnected {
   150  				continue
   151  			}
   152  			if routineErr := vp8Track.WriteSample(media.Sample{Data: []byte{0x00}, Duration: time.Second}); routineErr != nil {
   153  				fmt.Println(routineErr)
   154  			}
   155  
   156  			select {
   157  			case <-awaitRTPRecv:
   158  				close(awaitRTPSend)
   159  				return
   160  			default:
   161  			}
   162  		}
   163  	}()
   164  
   165  	go func() {
   166  		for {
   167  			time.Sleep(time.Millisecond * 100)
   168  			if routineErr := pcOffer.WriteRTCP([]rtcp.Packet{&rtcp.PictureLossIndication{SenderSSRC: uint32(sender.trackEncodings[0].ssrc), MediaSSRC: uint32(sender.trackEncodings[0].ssrc)}}); routineErr != nil {
   169  				awaitRTCPSenderSend <- routineErr
   170  			}
   171  
   172  			select {
   173  			case <-awaitRTCPReceiverRecv:
   174  				close(awaitRTCPSenderSend)
   175  				return
   176  			default:
   177  			}
   178  		}
   179  	}()
   180  
   181  	go func() {
   182  		if _, _, routineErr := sender.Read(make([]byte, 1400)); routineErr == nil {
   183  			close(awaitRTCPSenderRecv)
   184  		}
   185  	}()
   186  
   187  	assert.NoError(t, signalPair(pcOffer, pcAnswer))
   188  
   189  	err, ok := <-trackMetadataValid
   190  	if ok {
   191  		t.Fatal(err)
   192  	}
   193  
   194  	<-awaitRTPRecv
   195  	<-awaitRTPSend
   196  
   197  	<-awaitRTCPSenderRecv
   198  	if err, ok = <-awaitRTCPSenderSend; ok {
   199  		t.Fatal(err)
   200  	}
   201  
   202  	<-awaitRTCPReceiverRecv
   203  	if err, ok = <-awaitRTCPReceiverSend; ok {
   204  		t.Fatal(err)
   205  	}
   206  
   207  	closePairNow(t, pcOffer, pcAnswer)
   208  	<-awaitRTPRecvClosed
   209  }
   210  
   211  /*
   212  PeerConnection should be able to be torn down at anytime
   213  This test adds an input track and asserts
   214  
   215  * OnTrack doesn't fire since no video packets will arrive
   216  * No goroutine leaks
   217  * No deadlocks on shutdown
   218  */
   219  func TestPeerConnection_Media_Shutdown(t *testing.T) {
   220  	iceCompleteAnswer := make(chan struct{})
   221  	iceCompleteOffer := make(chan struct{})
   222  
   223  	lim := test.TimeOut(time.Second * 30)
   224  	defer lim.Stop()
   225  
   226  	report := test.CheckRoutines(t)
   227  	defer report()
   228  
   229  	pcOffer, pcAnswer, err := newPair()
   230  	if err != nil {
   231  		t.Fatal(err)
   232  	}
   233  
   234  	_, err = pcOffer.AddTransceiverFromKind(RTPCodecTypeVideo, RTPTransceiverInit{Direction: RTPTransceiverDirectionRecvonly})
   235  	if err != nil {
   236  		t.Fatal(err)
   237  	}
   238  
   239  	_, err = pcAnswer.AddTransceiverFromKind(RTPCodecTypeAudio, RTPTransceiverInit{Direction: RTPTransceiverDirectionRecvonly})
   240  	if err != nil {
   241  		t.Fatal(err)
   242  	}
   243  
   244  	opusTrack, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeOpus}, "audio", "pion1")
   245  	if err != nil {
   246  		t.Fatal(err)
   247  	}
   248  
   249  	vp8Track, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion2")
   250  	if err != nil {
   251  		t.Fatal(err)
   252  	}
   253  
   254  	if _, err = pcOffer.AddTrack(opusTrack); err != nil {
   255  		t.Fatal(err)
   256  	} else if _, err = pcAnswer.AddTrack(vp8Track); err != nil {
   257  		t.Fatal(err)
   258  	}
   259  
   260  	var onTrackFiredLock sync.Mutex
   261  	onTrackFired := false
   262  
   263  	pcAnswer.OnTrack(func(track *TrackRemote, receiver *RTPReceiver) {
   264  		onTrackFiredLock.Lock()
   265  		defer onTrackFiredLock.Unlock()
   266  		onTrackFired = true
   267  	})
   268  
   269  	pcAnswer.OnICEConnectionStateChange(func(iceState ICEConnectionState) {
   270  		if iceState == ICEConnectionStateConnected {
   271  			close(iceCompleteAnswer)
   272  		}
   273  	})
   274  	pcOffer.OnICEConnectionStateChange(func(iceState ICEConnectionState) {
   275  		if iceState == ICEConnectionStateConnected {
   276  			close(iceCompleteOffer)
   277  		}
   278  	})
   279  
   280  	err = signalPair(pcOffer, pcAnswer)
   281  	if err != nil {
   282  		t.Fatal(err)
   283  	}
   284  	<-iceCompleteAnswer
   285  	<-iceCompleteOffer
   286  
   287  	// Each PeerConnection should have one sender, one receiver and one transceiver
   288  	for _, pc := range []*PeerConnection{pcOffer, pcAnswer} {
   289  		senders := pc.GetSenders()
   290  		if len(senders) != 1 {
   291  			t.Errorf("Each PeerConnection should have one RTPSender, we have %d", len(senders))
   292  		}
   293  
   294  		receivers := pc.GetReceivers()
   295  		if len(receivers) != 2 {
   296  			t.Errorf("Each PeerConnection should have two RTPReceivers, we have %d", len(receivers))
   297  		}
   298  
   299  		transceivers := pc.GetTransceivers()
   300  		if len(transceivers) != 2 {
   301  			t.Errorf("Each PeerConnection should have two RTPTransceivers, we have %d", len(transceivers))
   302  		}
   303  	}
   304  
   305  	closePairNow(t, pcOffer, pcAnswer)
   306  
   307  	onTrackFiredLock.Lock()
   308  	if onTrackFired {
   309  		t.Fatalf("PeerConnection OnTrack fired even though we got no packets")
   310  	}
   311  	onTrackFiredLock.Unlock()
   312  }
   313  
   314  /*
   315  Integration test for behavior around media and disconnected peers
   316  
   317  * Sending RTP and RTCP to a disconnected Peer shouldn't return an error
   318  */
   319  func TestPeerConnection_Media_Disconnected(t *testing.T) {
   320  	lim := test.TimeOut(time.Second * 30)
   321  	defer lim.Stop()
   322  
   323  	report := test.CheckRoutines(t)
   324  	defer report()
   325  
   326  	s := SettingEngine{}
   327  	s.SetICETimeouts(time.Second/2, time.Second/2, time.Second/8)
   328  
   329  	m := &MediaEngine{}
   330  	assert.NoError(t, m.RegisterDefaultCodecs())
   331  
   332  	pcOffer, pcAnswer, err := NewAPI(WithSettingEngine(s), WithMediaEngine(m)).newPair(Configuration{})
   333  	if err != nil {
   334  		t.Fatal(err)
   335  	}
   336  
   337  	vp8Track, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion2")
   338  	if err != nil {
   339  		t.Fatal(err)
   340  	}
   341  
   342  	vp8Sender, err := pcOffer.AddTrack(vp8Track)
   343  	if err != nil {
   344  		t.Fatal(err)
   345  	}
   346  
   347  	haveDisconnected := make(chan error)
   348  	pcOffer.OnICEConnectionStateChange(func(iceState ICEConnectionState) {
   349  		if iceState == ICEConnectionStateDisconnected {
   350  			close(haveDisconnected)
   351  		} else if iceState == ICEConnectionStateConnected {
   352  			// Assert that DTLS is done by pull remote certificate, don't tear down the PC early
   353  			for {
   354  				if len(vp8Sender.Transport().GetRemoteCertificate()) != 0 {
   355  					if pcAnswer.sctpTransport.association() != nil {
   356  						break
   357  					}
   358  				}
   359  
   360  				time.Sleep(time.Second)
   361  			}
   362  
   363  			if pcCloseErr := pcAnswer.Close(); pcCloseErr != nil {
   364  				haveDisconnected <- pcCloseErr
   365  			}
   366  		}
   367  	})
   368  
   369  	err = signalPair(pcOffer, pcAnswer)
   370  	if err != nil {
   371  		t.Fatal(err)
   372  	}
   373  
   374  	err, ok := <-haveDisconnected
   375  	if ok {
   376  		t.Fatal(err)
   377  	}
   378  	for i := 0; i <= 5; i++ {
   379  		if rtpErr := vp8Track.WriteSample(media.Sample{Data: []byte{0x00}, Duration: time.Second}); rtpErr != nil {
   380  			t.Fatal(rtpErr)
   381  		} else if rtcpErr := pcOffer.WriteRTCP([]rtcp.Packet{&rtcp.PictureLossIndication{MediaSSRC: 0}}); rtcpErr != nil {
   382  			t.Fatal(rtcpErr)
   383  		}
   384  	}
   385  
   386  	assert.NoError(t, pcOffer.Close())
   387  }
   388  
   389  type undeclaredSsrcLogger struct{ unhandledSimulcastError chan struct{} }
   390  
   391  func (u *undeclaredSsrcLogger) Trace(string)                  {}
   392  func (u *undeclaredSsrcLogger) Tracef(string, ...interface{}) {}
   393  func (u *undeclaredSsrcLogger) Debug(string)                  {}
   394  func (u *undeclaredSsrcLogger) Debugf(string, ...interface{}) {}
   395  func (u *undeclaredSsrcLogger) Info(string)                   {}
   396  func (u *undeclaredSsrcLogger) Infof(string, ...interface{})  {}
   397  func (u *undeclaredSsrcLogger) Warn(string)                   {}
   398  func (u *undeclaredSsrcLogger) Warnf(string, ...interface{})  {}
   399  func (u *undeclaredSsrcLogger) Error(string)                  {}
   400  func (u *undeclaredSsrcLogger) Errorf(format string, _ ...interface{}) {
   401  	if format == incomingUnhandledRTPSsrc {
   402  		close(u.unhandledSimulcastError)
   403  	}
   404  }
   405  
   406  type undeclaredSsrcLoggerFactory struct{ unhandledSimulcastError chan struct{} }
   407  
   408  func (u *undeclaredSsrcLoggerFactory) NewLogger(string) logging.LeveledLogger {
   409  	return &undeclaredSsrcLogger{u.unhandledSimulcastError}
   410  }
   411  
   412  // Filter SSRC lines
   413  func filterSsrc(offer string) (filteredSDP string) {
   414  	scanner := bufio.NewScanner(strings.NewReader(offer))
   415  	for scanner.Scan() {
   416  		l := scanner.Text()
   417  		if strings.HasPrefix(l, "a=ssrc") {
   418  			continue
   419  		}
   420  
   421  		filteredSDP += l + "\n"
   422  	}
   423  	return
   424  }
   425  
   426  // If a SessionDescription has a single media section and no SSRC
   427  // assume that it is meant to handle all RTP packets
   428  func TestUndeclaredSSRC(t *testing.T) {
   429  	lim := test.TimeOut(time.Second * 30)
   430  	defer lim.Stop()
   431  
   432  	report := test.CheckRoutines(t)
   433  	defer report()
   434  
   435  	t.Run("No SSRC", func(t *testing.T) {
   436  		pcOffer, pcAnswer, err := newPair()
   437  		assert.NoError(t, err)
   438  
   439  		vp8Writer, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion2")
   440  		assert.NoError(t, err)
   441  
   442  		_, err = pcOffer.AddTrack(vp8Writer)
   443  		assert.NoError(t, err)
   444  
   445  		onTrackFired := make(chan struct{})
   446  		pcAnswer.OnTrack(func(trackRemote *TrackRemote, r *RTPReceiver) {
   447  			assert.Equal(t, trackRemote.StreamID(), vp8Writer.StreamID())
   448  			assert.Equal(t, trackRemote.ID(), vp8Writer.ID())
   449  			close(onTrackFired)
   450  		})
   451  
   452  		offer, err := pcOffer.CreateOffer(nil)
   453  		assert.NoError(t, err)
   454  
   455  		offerGatheringComplete := GatheringCompletePromise(pcOffer)
   456  		assert.NoError(t, pcOffer.SetLocalDescription(offer))
   457  		<-offerGatheringComplete
   458  
   459  		offer.SDP = filterSsrc(pcOffer.LocalDescription().SDP)
   460  		assert.NoError(t, pcAnswer.SetRemoteDescription(offer))
   461  
   462  		answer, err := pcAnswer.CreateAnswer(nil)
   463  		assert.NoError(t, err)
   464  
   465  		answerGatheringComplete := GatheringCompletePromise(pcAnswer)
   466  		assert.NoError(t, pcAnswer.SetLocalDescription(answer))
   467  		<-answerGatheringComplete
   468  
   469  		assert.NoError(t, pcOffer.SetRemoteDescription(*pcAnswer.LocalDescription()))
   470  
   471  		sendVideoUntilDone(onTrackFired, t, []*TrackLocalStaticSample{vp8Writer})
   472  		closePairNow(t, pcOffer, pcAnswer)
   473  	})
   474  
   475  	t.Run("Has RID", func(t *testing.T) {
   476  		unhandledSimulcastError := make(chan struct{})
   477  
   478  		m := &MediaEngine{}
   479  		assert.NoError(t, m.RegisterDefaultCodecs())
   480  
   481  		pcOffer, pcAnswer, err := NewAPI(WithSettingEngine(SettingEngine{
   482  			LoggerFactory: &undeclaredSsrcLoggerFactory{unhandledSimulcastError},
   483  		}), WithMediaEngine(m)).newPair(Configuration{})
   484  		assert.NoError(t, err)
   485  
   486  		vp8Writer, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion2")
   487  		assert.NoError(t, err)
   488  
   489  		_, err = pcOffer.AddTrack(vp8Writer)
   490  		assert.NoError(t, err)
   491  
   492  		offer, err := pcOffer.CreateOffer(nil)
   493  		assert.NoError(t, err)
   494  
   495  		offerGatheringComplete := GatheringCompletePromise(pcOffer)
   496  		assert.NoError(t, pcOffer.SetLocalDescription(offer))
   497  		<-offerGatheringComplete
   498  
   499  		// Append RID to end of SessionDescription. Will not be considered unhandled anymore
   500  		offer.SDP = filterSsrc(pcOffer.LocalDescription().SDP) + "a=" + sdpAttributeRid + "\r\n"
   501  		assert.NoError(t, pcAnswer.SetRemoteDescription(offer))
   502  
   503  		answer, err := pcAnswer.CreateAnswer(nil)
   504  		assert.NoError(t, err)
   505  
   506  		answerGatheringComplete := GatheringCompletePromise(pcAnswer)
   507  		assert.NoError(t, pcAnswer.SetLocalDescription(answer))
   508  		<-answerGatheringComplete
   509  
   510  		assert.NoError(t, pcOffer.SetRemoteDescription(*pcAnswer.LocalDescription()))
   511  
   512  		sendVideoUntilDone(unhandledSimulcastError, t, []*TrackLocalStaticSample{vp8Writer})
   513  		closePairNow(t, pcOffer, pcAnswer)
   514  	})
   515  }
   516  
   517  func TestAddTransceiverFromTrackSendOnly(t *testing.T) {
   518  	lim := test.TimeOut(time.Second * 30)
   519  	defer lim.Stop()
   520  
   521  	report := test.CheckRoutines(t)
   522  	defer report()
   523  
   524  	pc, err := NewPeerConnection(Configuration{})
   525  	if err != nil {
   526  		t.Error(err.Error())
   527  	}
   528  
   529  	track, err := NewTrackLocalStaticSample(
   530  		RTPCodecCapability{MimeType: "audio/Opus"},
   531  		"track-id",
   532  		"stream-id",
   533  	)
   534  	if err != nil {
   535  		t.Error(err.Error())
   536  	}
   537  
   538  	transceiver, err := pc.AddTransceiverFromTrack(track, RTPTransceiverInit{
   539  		Direction: RTPTransceiverDirectionSendonly,
   540  	})
   541  	if err != nil {
   542  		t.Error(err.Error())
   543  	}
   544  
   545  	if transceiver.Receiver() != nil {
   546  		t.Errorf("Transceiver shouldn't have a receiver")
   547  	}
   548  
   549  	if transceiver.Sender() == nil {
   550  		t.Errorf("Transceiver should have a sender")
   551  	}
   552  
   553  	if len(pc.GetTransceivers()) != 1 {
   554  		t.Errorf("PeerConnection should have one transceiver but has %d", len(pc.GetTransceivers()))
   555  	}
   556  
   557  	if len(pc.GetSenders()) != 1 {
   558  		t.Errorf("PeerConnection should have one sender but has %d", len(pc.GetSenders()))
   559  	}
   560  
   561  	offer, err := pc.CreateOffer(nil)
   562  	if err != nil {
   563  		t.Error(err.Error())
   564  	}
   565  
   566  	if !offerMediaHasDirection(offer, RTPCodecTypeAudio, RTPTransceiverDirectionSendonly) {
   567  		t.Errorf("Direction on SDP is not %s", RTPTransceiverDirectionSendonly)
   568  	}
   569  
   570  	assert.NoError(t, pc.Close())
   571  }
   572  
   573  func TestAddTransceiverFromTrackSendRecv(t *testing.T) {
   574  	lim := test.TimeOut(time.Second * 30)
   575  	defer lim.Stop()
   576  
   577  	report := test.CheckRoutines(t)
   578  	defer report()
   579  
   580  	pc, err := NewPeerConnection(Configuration{})
   581  	if err != nil {
   582  		t.Error(err.Error())
   583  	}
   584  
   585  	track, err := NewTrackLocalStaticSample(
   586  		RTPCodecCapability{MimeType: "audio/Opus"},
   587  		"track-id",
   588  		"stream-id",
   589  	)
   590  	if err != nil {
   591  		t.Error(err.Error())
   592  	}
   593  
   594  	transceiver, err := pc.AddTransceiverFromTrack(track, RTPTransceiverInit{
   595  		Direction: RTPTransceiverDirectionSendrecv,
   596  	})
   597  	if err != nil {
   598  		t.Error(err.Error())
   599  	}
   600  
   601  	if transceiver.Receiver() == nil {
   602  		t.Errorf("Transceiver should have a receiver")
   603  	}
   604  
   605  	if transceiver.Sender() == nil {
   606  		t.Errorf("Transceiver should have a sender")
   607  	}
   608  
   609  	if len(pc.GetTransceivers()) != 1 {
   610  		t.Errorf("PeerConnection should have one transceiver but has %d", len(pc.GetTransceivers()))
   611  	}
   612  
   613  	offer, err := pc.CreateOffer(nil)
   614  	if err != nil {
   615  		t.Error(err.Error())
   616  	}
   617  
   618  	if !offerMediaHasDirection(offer, RTPCodecTypeAudio, RTPTransceiverDirectionSendrecv) {
   619  		t.Errorf("Direction on SDP is not %s", RTPTransceiverDirectionSendrecv)
   620  	}
   621  	assert.NoError(t, pc.Close())
   622  }
   623  
   624  func TestAddTransceiverAddTrack_Reuse(t *testing.T) {
   625  	pc, err := NewPeerConnection(Configuration{})
   626  	assert.NoError(t, err)
   627  
   628  	tr, err := pc.AddTransceiverFromKind(
   629  		RTPCodecTypeVideo,
   630  		RTPTransceiverInit{Direction: RTPTransceiverDirectionRecvonly},
   631  	)
   632  	assert.NoError(t, err)
   633  
   634  	assert.Equal(t, []*RTPTransceiver{tr}, pc.GetTransceivers())
   635  
   636  	addTrack := func() (TrackLocal, *RTPSender) {
   637  		track, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "foo", "bar")
   638  		assert.NoError(t, err)
   639  
   640  		sender, err := pc.AddTrack(track)
   641  		assert.NoError(t, err)
   642  
   643  		return track, sender
   644  	}
   645  
   646  	track1, sender1 := addTrack()
   647  	assert.Equal(t, 1, len(pc.GetTransceivers()))
   648  	assert.Equal(t, sender1, tr.Sender())
   649  	assert.Equal(t, track1, tr.Sender().Track())
   650  	require.NoError(t, pc.RemoveTrack(sender1))
   651  
   652  	track2, _ := addTrack()
   653  	assert.Equal(t, 1, len(pc.GetTransceivers()))
   654  	assert.Equal(t, track2, tr.Sender().Track())
   655  
   656  	addTrack()
   657  	assert.Equal(t, 2, len(pc.GetTransceivers()))
   658  
   659  	assert.NoError(t, pc.Close())
   660  }
   661  
   662  func TestAddTransceiverAddTrack_NewRTPSender_Error(t *testing.T) {
   663  	pc, err := NewPeerConnection(Configuration{})
   664  	assert.NoError(t, err)
   665  
   666  	_, err = pc.AddTransceiverFromKind(
   667  		RTPCodecTypeVideo,
   668  		RTPTransceiverInit{Direction: RTPTransceiverDirectionRecvonly},
   669  	)
   670  	assert.NoError(t, err)
   671  
   672  	dtlsTransport := pc.dtlsTransport
   673  	pc.dtlsTransport = nil
   674  
   675  	track, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "foo", "bar")
   676  	assert.NoError(t, err)
   677  
   678  	_, err = pc.AddTrack(track)
   679  	assert.Error(t, err, "DTLSTransport must not be nil")
   680  
   681  	assert.Equal(t, 1, len(pc.GetTransceivers()))
   682  
   683  	pc.dtlsTransport = dtlsTransport
   684  	assert.NoError(t, pc.Close())
   685  }
   686  
   687  func TestRtpSenderReceiver_ReadClose_Error(t *testing.T) {
   688  	pc, err := NewPeerConnection(Configuration{})
   689  	assert.NoError(t, err)
   690  
   691  	tr, err := pc.AddTransceiverFromKind(
   692  		RTPCodecTypeVideo,
   693  		RTPTransceiverInit{Direction: RTPTransceiverDirectionSendrecv},
   694  	)
   695  	assert.NoError(t, err)
   696  
   697  	sender, receiver := tr.Sender(), tr.Receiver()
   698  	assert.NoError(t, sender.Stop())
   699  	_, _, err = sender.Read(make([]byte, 0, 1400))
   700  	assert.ErrorIs(t, err, io.ErrClosedPipe)
   701  
   702  	assert.NoError(t, receiver.Stop())
   703  	_, _, err = receiver.Read(make([]byte, 0, 1400))
   704  	assert.ErrorIs(t, err, io.ErrClosedPipe)
   705  
   706  	assert.NoError(t, pc.Close())
   707  }
   708  
   709  // nolint: dupl
   710  func TestAddTransceiverFromKind(t *testing.T) {
   711  	lim := test.TimeOut(time.Second * 30)
   712  	defer lim.Stop()
   713  
   714  	report := test.CheckRoutines(t)
   715  	defer report()
   716  
   717  	pc, err := NewPeerConnection(Configuration{})
   718  	if err != nil {
   719  		t.Error(err.Error())
   720  	}
   721  
   722  	transceiver, err := pc.AddTransceiverFromKind(RTPCodecTypeVideo, RTPTransceiverInit{
   723  		Direction: RTPTransceiverDirectionRecvonly,
   724  	})
   725  	if err != nil {
   726  		t.Error(err.Error())
   727  	}
   728  
   729  	if transceiver.Receiver() == nil {
   730  		t.Errorf("Transceiver should have a receiver")
   731  	}
   732  
   733  	if transceiver.Sender() != nil {
   734  		t.Errorf("Transceiver shouldn't have a sender")
   735  	}
   736  
   737  	offer, err := pc.CreateOffer(nil)
   738  	if err != nil {
   739  		t.Error(err.Error())
   740  	}
   741  
   742  	if !offerMediaHasDirection(offer, RTPCodecTypeVideo, RTPTransceiverDirectionRecvonly) {
   743  		t.Errorf("Direction on SDP is not %s", RTPTransceiverDirectionRecvonly)
   744  	}
   745  	assert.NoError(t, pc.Close())
   746  }
   747  
   748  func TestAddTransceiverFromTrackFailsRecvOnly(t *testing.T) {
   749  	lim := test.TimeOut(time.Second * 30)
   750  	defer lim.Stop()
   751  
   752  	report := test.CheckRoutines(t)
   753  	defer report()
   754  
   755  	pc, err := NewPeerConnection(Configuration{})
   756  	if err != nil {
   757  		t.Error(err.Error())
   758  	}
   759  
   760  	track, err := NewTrackLocalStaticSample(
   761  		RTPCodecCapability{MimeType: MimeTypeH264, SDPFmtpLine: "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f"},
   762  		"track-id",
   763  		"track-label",
   764  	)
   765  	if err != nil {
   766  		t.Error(err.Error())
   767  	}
   768  
   769  	transceiver, err := pc.AddTransceiverFromTrack(track, RTPTransceiverInit{
   770  		Direction: RTPTransceiverDirectionRecvonly,
   771  	})
   772  
   773  	if transceiver != nil {
   774  		t.Error("AddTransceiverFromTrack shouldn't succeed with Direction RTPTransceiverDirectionRecvonly")
   775  	}
   776  
   777  	assert.NotNil(t, err)
   778  	assert.NoError(t, pc.Close())
   779  }
   780  
   781  func TestPlanBMediaExchange(t *testing.T) {
   782  	runTest := func(trackCount int, t *testing.T) {
   783  		addSingleTrack := func(p *PeerConnection) *TrackLocalStaticSample {
   784  			track, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, fmt.Sprintf("video-%d", randutil.NewMathRandomGenerator().Uint32()), fmt.Sprintf("video-%d", randutil.NewMathRandomGenerator().Uint32()))
   785  			assert.NoError(t, err)
   786  
   787  			_, err = p.AddTrack(track)
   788  			assert.NoError(t, err)
   789  
   790  			return track
   791  		}
   792  
   793  		pcOffer, err := NewPeerConnection(Configuration{SDPSemantics: SDPSemanticsPlanB})
   794  		assert.NoError(t, err)
   795  
   796  		pcAnswer, err := NewPeerConnection(Configuration{SDPSemantics: SDPSemanticsPlanB})
   797  		assert.NoError(t, err)
   798  
   799  		var onTrackWaitGroup sync.WaitGroup
   800  		onTrackWaitGroup.Add(trackCount)
   801  		pcAnswer.OnTrack(func(track *TrackRemote, r *RTPReceiver) {
   802  			onTrackWaitGroup.Done()
   803  		})
   804  
   805  		done := make(chan struct{})
   806  		go func() {
   807  			onTrackWaitGroup.Wait()
   808  			close(done)
   809  		}()
   810  
   811  		_, err = pcAnswer.AddTransceiverFromKind(RTPCodecTypeVideo)
   812  		assert.NoError(t, err)
   813  
   814  		outboundTracks := []*TrackLocalStaticSample{}
   815  		for i := 0; i < trackCount; i++ {
   816  			outboundTracks = append(outboundTracks, addSingleTrack(pcOffer))
   817  		}
   818  
   819  		assert.NoError(t, signalPair(pcOffer, pcAnswer))
   820  
   821  		func() {
   822  			for {
   823  				select {
   824  				case <-time.After(20 * time.Millisecond):
   825  					for _, track := range outboundTracks {
   826  						assert.NoError(t, track.WriteSample(media.Sample{Data: []byte{0x00}, Duration: time.Second}))
   827  					}
   828  				case <-done:
   829  					return
   830  				}
   831  			}
   832  		}()
   833  
   834  		closePairNow(t, pcOffer, pcAnswer)
   835  	}
   836  
   837  	lim := test.TimeOut(time.Second * 30)
   838  	defer lim.Stop()
   839  
   840  	report := test.CheckRoutines(t)
   841  	defer report()
   842  
   843  	t.Run("Single Track", func(t *testing.T) {
   844  		runTest(1, t)
   845  	})
   846  	t.Run("Multi Track", func(t *testing.T) {
   847  		runTest(2, t)
   848  	})
   849  }
   850  
   851  // TestPeerConnection_Start_Only_Negotiated_Senders tests that only
   852  // the current negotiated transceivers senders provided in an
   853  // offer/answer are started
   854  func TestPeerConnection_Start_Only_Negotiated_Senders(t *testing.T) {
   855  	lim := test.TimeOut(time.Second * 30)
   856  	defer lim.Stop()
   857  
   858  	report := test.CheckRoutines(t)
   859  	defer report()
   860  
   861  	pcOffer, err := NewPeerConnection(Configuration{})
   862  	assert.NoError(t, err)
   863  	defer func() { assert.NoError(t, pcOffer.Close()) }()
   864  
   865  	pcAnswer, err := NewPeerConnection(Configuration{})
   866  	assert.NoError(t, err)
   867  	defer func() { assert.NoError(t, pcAnswer.Close()) }()
   868  
   869  	track1, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion1")
   870  	require.NoError(t, err)
   871  
   872  	sender1, err := pcOffer.AddTrack(track1)
   873  	require.NoError(t, err)
   874  
   875  	offer, err := pcOffer.CreateOffer(nil)
   876  	assert.NoError(t, err)
   877  
   878  	offerGatheringComplete := GatheringCompletePromise(pcOffer)
   879  	assert.NoError(t, pcOffer.SetLocalDescription(offer))
   880  	<-offerGatheringComplete
   881  	assert.NoError(t, pcAnswer.SetRemoteDescription(*pcOffer.LocalDescription()))
   882  	answer, err := pcAnswer.CreateAnswer(nil)
   883  	assert.NoError(t, err)
   884  	answerGatheringComplete := GatheringCompletePromise(pcAnswer)
   885  	assert.NoError(t, pcAnswer.SetLocalDescription(answer))
   886  	<-answerGatheringComplete
   887  
   888  	// Add a new track between providing the offer and applying the answer
   889  
   890  	track2, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion2")
   891  	require.NoError(t, err)
   892  
   893  	sender2, err := pcOffer.AddTrack(track2)
   894  	require.NoError(t, err)
   895  
   896  	// apply answer so we'll test generateMatchedSDP
   897  	assert.NoError(t, pcOffer.SetRemoteDescription(*pcAnswer.LocalDescription()))
   898  
   899  	// Wait for senders to be started by startTransports spawned goroutine
   900  	pcOffer.ops.Done()
   901  
   902  	// sender1 should be started but sender2 should not be started
   903  	assert.True(t, sender1.hasSent(), "sender1 is not started but should be started")
   904  	assert.False(t, sender2.hasSent(), "sender2 is started but should not be started")
   905  }
   906  
   907  // TestPeerConnection_Start_Right_Receiver tests that the right
   908  // receiver (the receiver which transceiver has the same media section as the track)
   909  // is started for the specified track
   910  func TestPeerConnection_Start_Right_Receiver(t *testing.T) {
   911  	isTransceiverReceiverStarted := func(pc *PeerConnection, mid string) (bool, error) {
   912  		for _, transceiver := range pc.GetTransceivers() {
   913  			if transceiver.Mid() != mid {
   914  				continue
   915  			}
   916  			return transceiver.Receiver() != nil && transceiver.Receiver().haveReceived(), nil
   917  		}
   918  		return false, fmt.Errorf("%w: %q", errNoTransceiverwithMid, mid)
   919  	}
   920  
   921  	lim := test.TimeOut(time.Second * 30)
   922  	defer lim.Stop()
   923  
   924  	report := test.CheckRoutines(t)
   925  	defer report()
   926  
   927  	pcOffer, pcAnswer, err := newPair()
   928  	require.NoError(t, err)
   929  
   930  	_, err = pcAnswer.AddTransceiverFromKind(RTPCodecTypeVideo, RTPTransceiverInit{Direction: RTPTransceiverDirectionRecvonly})
   931  	assert.NoError(t, err)
   932  
   933  	track1, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion1")
   934  	require.NoError(t, err)
   935  
   936  	sender1, err := pcOffer.AddTrack(track1)
   937  	require.NoError(t, err)
   938  
   939  	assert.NoError(t, signalPair(pcOffer, pcAnswer))
   940  
   941  	pcOffer.ops.Done()
   942  	pcAnswer.ops.Done()
   943  
   944  	// transceiver with mid 0 should be started
   945  	started, err := isTransceiverReceiverStarted(pcAnswer, "0")
   946  	assert.NoError(t, err)
   947  	assert.True(t, started, "transceiver with mid 0 should be started")
   948  
   949  	// Remove track
   950  	assert.NoError(t, pcOffer.RemoveTrack(sender1))
   951  
   952  	assert.NoError(t, signalPair(pcOffer, pcAnswer))
   953  
   954  	pcOffer.ops.Done()
   955  	pcAnswer.ops.Done()
   956  
   957  	// transceiver with mid 0 should not be started
   958  	started, err = isTransceiverReceiverStarted(pcAnswer, "0")
   959  	assert.NoError(t, err)
   960  	assert.False(t, started, "transceiver with mid 0 should not be started")
   961  
   962  	// Add a new transceiver (we're not using AddTrack since it'll reuse the transceiver with mid 0)
   963  	_, err = pcOffer.AddTransceiverFromTrack(track1)
   964  	assert.NoError(t, err)
   965  
   966  	_, err = pcAnswer.AddTransceiverFromKind(RTPCodecTypeVideo, RTPTransceiverInit{Direction: RTPTransceiverDirectionRecvonly})
   967  	assert.NoError(t, err)
   968  
   969  	assert.NoError(t, signalPair(pcOffer, pcAnswer))
   970  
   971  	pcOffer.ops.Done()
   972  	pcAnswer.ops.Done()
   973  
   974  	// transceiver with mid 0 should not be started
   975  	started, err = isTransceiverReceiverStarted(pcAnswer, "0")
   976  	assert.NoError(t, err)
   977  	assert.False(t, started, "transceiver with mid 0 should not be started")
   978  	// transceiver with mid 2 should be started
   979  	started, err = isTransceiverReceiverStarted(pcAnswer, "2")
   980  	assert.NoError(t, err)
   981  	assert.True(t, started, "transceiver with mid 2 should be started")
   982  
   983  	closePairNow(t, pcOffer, pcAnswer)
   984  }
   985  
   986  func TestPeerConnection_Simulcast_Probe(t *testing.T) {
   987  	lim := test.TimeOut(time.Second * 30) //nolint
   988  	defer lim.Stop()
   989  
   990  	report := test.CheckRoutines(t)
   991  	defer report()
   992  
   993  	// Assert that failed Simulcast probing doesn't cause
   994  	// the handleUndeclaredSSRC to be leaked
   995  	t.Run("Leak", func(t *testing.T) {
   996  		track, err := NewTrackLocalStaticRTP(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion")
   997  		assert.NoError(t, err)
   998  
   999  		offerer, answerer, err := newPair()
  1000  		assert.NoError(t, err)
  1001  
  1002  		_, err = offerer.AddTrack(track)
  1003  		assert.NoError(t, err)
  1004  
  1005  		ticker := time.NewTicker(time.Millisecond * 20)
  1006  		testFinished := make(chan struct{})
  1007  		seenFiveStreams, seenFiveStreamsCancel := context.WithCancel(context.Background())
  1008  
  1009  		go func() {
  1010  			for {
  1011  				select {
  1012  				case <-testFinished:
  1013  					return
  1014  				case <-ticker.C:
  1015  					answerer.dtlsTransport.lock.Lock()
  1016  					if len(answerer.dtlsTransport.simulcastStreams) >= 5 {
  1017  						seenFiveStreamsCancel()
  1018  					}
  1019  					answerer.dtlsTransport.lock.Unlock()
  1020  
  1021  					track.mu.Lock()
  1022  					if len(track.bindings) == 1 {
  1023  						_, err = track.bindings[0].writeStream.WriteRTP(&rtp.Header{
  1024  							Version: 2,
  1025  							SSRC:    randutil.NewMathRandomGenerator().Uint32(),
  1026  						}, []byte{0, 1, 2, 3, 4, 5})
  1027  						assert.NoError(t, err)
  1028  					}
  1029  					track.mu.Unlock()
  1030  				}
  1031  			}
  1032  		}()
  1033  
  1034  		assert.NoError(t, signalPair(offerer, answerer))
  1035  
  1036  		peerConnectionConnected := untilConnectionState(PeerConnectionStateConnected, offerer, answerer)
  1037  		peerConnectionConnected.Wait()
  1038  
  1039  		<-seenFiveStreams.Done()
  1040  
  1041  		closePairNow(t, offerer, answerer)
  1042  		close(testFinished)
  1043  	})
  1044  
  1045  	// Assert that NonSimulcast Traffic isn't incorrectly broken by the probe
  1046  	t.Run("Break NonSimulcast", func(t *testing.T) {
  1047  		unhandledSimulcastError := make(chan struct{})
  1048  
  1049  		m := &MediaEngine{}
  1050  		if err := m.RegisterDefaultCodecs(); err != nil {
  1051  			panic(err)
  1052  		}
  1053  		registerSimulcastHeaderExtensions(m, RTPCodecTypeVideo)
  1054  
  1055  		pcOffer, pcAnswer, err := NewAPI(WithSettingEngine(SettingEngine{
  1056  			LoggerFactory: &undeclaredSsrcLoggerFactory{unhandledSimulcastError},
  1057  		}), WithMediaEngine(m)).newPair(Configuration{})
  1058  		assert.NoError(t, err)
  1059  
  1060  		firstTrack, err := NewTrackLocalStaticRTP(RTPCodecCapability{MimeType: MimeTypeVP8}, "firstTrack", "firstTrack")
  1061  		assert.NoError(t, err)
  1062  
  1063  		_, err = pcOffer.AddTrack(firstTrack)
  1064  		assert.NoError(t, err)
  1065  
  1066  		secondTrack, err := NewTrackLocalStaticRTP(RTPCodecCapability{MimeType: MimeTypeVP8}, "secondTrack", "secondTrack")
  1067  		assert.NoError(t, err)
  1068  
  1069  		_, err = pcOffer.AddTrack(secondTrack)
  1070  		assert.NoError(t, err)
  1071  
  1072  		assert.NoError(t, signalPairWithModification(pcOffer, pcAnswer, func(sessionDescription string) (filtered string) {
  1073  			shouldDiscard := false
  1074  
  1075  			scanner := bufio.NewScanner(strings.NewReader(sessionDescription))
  1076  			for scanner.Scan() {
  1077  				if strings.HasPrefix(scanner.Text(), "m=video") {
  1078  					shouldDiscard = !shouldDiscard
  1079  				}
  1080  
  1081  				if !shouldDiscard {
  1082  					filtered += scanner.Text() + "\r\n"
  1083  				}
  1084  			}
  1085  
  1086  			return
  1087  		}))
  1088  
  1089  		sequenceNumber := uint16(0)
  1090  		sendRTPPacket := func() {
  1091  			sequenceNumber++
  1092  			assert.NoError(t, firstTrack.WriteRTP(&rtp.Packet{
  1093  				Header: rtp.Header{
  1094  					Version:        2,
  1095  					SequenceNumber: sequenceNumber,
  1096  				},
  1097  				Payload: []byte{0x00},
  1098  			}))
  1099  			time.Sleep(20 * time.Millisecond)
  1100  		}
  1101  
  1102  		for ; sequenceNumber <= 5; sequenceNumber++ {
  1103  			sendRTPPacket()
  1104  		}
  1105  
  1106  		assert.NoError(t, signalPair(pcOffer, pcAnswer))
  1107  
  1108  		trackRemoteChan := make(chan *TrackRemote, 1)
  1109  		pcAnswer.OnTrack(func(trackRemote *TrackRemote, _ *RTPReceiver) {
  1110  			trackRemoteChan <- trackRemote
  1111  		})
  1112  
  1113  		trackRemote := func() *TrackRemote {
  1114  			for {
  1115  				select {
  1116  				case t := <-trackRemoteChan:
  1117  					return t
  1118  				default:
  1119  					sendRTPPacket()
  1120  				}
  1121  			}
  1122  		}()
  1123  
  1124  		func() {
  1125  			for {
  1126  				select {
  1127  				case <-unhandledSimulcastError:
  1128  					return
  1129  				default:
  1130  					sendRTPPacket()
  1131  				}
  1132  			}
  1133  		}()
  1134  
  1135  		_, _, err = trackRemote.Read(make([]byte, 1500))
  1136  		assert.NoError(t, err)
  1137  
  1138  		closePairNow(t, pcOffer, pcAnswer)
  1139  	})
  1140  }
  1141  
  1142  // Assert that CreateOffer returns an error for a RTPSender with no codecs
  1143  // pion/webrtc#1702
  1144  func TestPeerConnection_CreateOffer_NoCodecs(t *testing.T) {
  1145  	lim := test.TimeOut(time.Second * 30)
  1146  	defer lim.Stop()
  1147  
  1148  	report := test.CheckRoutines(t)
  1149  	defer report()
  1150  
  1151  	m := &MediaEngine{}
  1152  
  1153  	pc, err := NewAPI(WithMediaEngine(m)).NewPeerConnection(Configuration{})
  1154  	assert.NoError(t, err)
  1155  
  1156  	track, err := NewTrackLocalStaticRTP(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion")
  1157  	assert.NoError(t, err)
  1158  
  1159  	_, err = pc.AddTrack(track)
  1160  	assert.NoError(t, err)
  1161  
  1162  	_, err = pc.CreateOffer(nil)
  1163  	assert.Equal(t, err, ErrSenderWithNoCodecs)
  1164  
  1165  	assert.NoError(t, pc.Close())
  1166  }
  1167  
  1168  // Assert that AddTrack is thread-safe
  1169  func TestPeerConnection_RaceReplaceTrack(t *testing.T) {
  1170  	pc, err := NewPeerConnection(Configuration{})
  1171  	assert.NoError(t, err)
  1172  
  1173  	addTrack := func() *TrackLocalStaticSample {
  1174  		track, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "foo", "bar")
  1175  		assert.NoError(t, err)
  1176  		_, err = pc.AddTrack(track)
  1177  		assert.NoError(t, err)
  1178  		return track
  1179  	}
  1180  
  1181  	for i := 0; i < 10; i++ {
  1182  		addTrack()
  1183  	}
  1184  	for _, tr := range pc.GetTransceivers() {
  1185  		assert.NoError(t, pc.RemoveTrack(tr.Sender()))
  1186  	}
  1187  
  1188  	var wg sync.WaitGroup
  1189  	tracks := make([]*TrackLocalStaticSample, 10)
  1190  	wg.Add(10)
  1191  	for i := 0; i < 10; i++ {
  1192  		go func(j int) {
  1193  			tracks[j] = addTrack()
  1194  			wg.Done()
  1195  		}(i)
  1196  	}
  1197  
  1198  	wg.Wait()
  1199  
  1200  	for _, track := range tracks {
  1201  		have := false
  1202  		for _, t := range pc.GetTransceivers() {
  1203  			if t.Sender() != nil && t.Sender().Track() == track {
  1204  				have = true
  1205  				break
  1206  			}
  1207  		}
  1208  		if !have {
  1209  			t.Errorf("track was added but not found on senders")
  1210  		}
  1211  	}
  1212  
  1213  	assert.NoError(t, pc.Close())
  1214  }
  1215  
  1216  func TestPeerConnection_Simulcast(t *testing.T) {
  1217  	lim := test.TimeOut(time.Second * 30)
  1218  	defer lim.Stop()
  1219  
  1220  	report := test.CheckRoutines(t)
  1221  	defer report()
  1222  
  1223  	rids := []string{"a", "b", "c"}
  1224  	var ridMapLock sync.RWMutex
  1225  	ridMap := map[string]int{}
  1226  
  1227  	// Enable Extension Headers needed for Simulcast
  1228  	m := &MediaEngine{}
  1229  	if err := m.RegisterDefaultCodecs(); err != nil {
  1230  		panic(err)
  1231  	}
  1232  	registerSimulcastHeaderExtensions(m, RTPCodecTypeVideo)
  1233  
  1234  	assertRidCorrect := func(t *testing.T) {
  1235  		ridMapLock.Lock()
  1236  		defer ridMapLock.Unlock()
  1237  
  1238  		for _, rid := range rids {
  1239  			assert.Equal(t, ridMap[rid], 1)
  1240  		}
  1241  		assert.Equal(t, len(ridMap), 3)
  1242  	}
  1243  
  1244  	ridsFullfilled := func() bool {
  1245  		ridMapLock.Lock()
  1246  		defer ridMapLock.Unlock()
  1247  
  1248  		ridCount := len(ridMap)
  1249  		return ridCount == 3
  1250  	}
  1251  
  1252  	onTrackHandler := func(trackRemote *TrackRemote, _ *RTPReceiver) {
  1253  		ridMapLock.Lock()
  1254  		defer ridMapLock.Unlock()
  1255  		ridMap[trackRemote.RID()] = ridMap[trackRemote.RID()] + 1
  1256  	}
  1257  
  1258  	t.Run("RTP Extension Based", func(t *testing.T) {
  1259  		pcOffer, pcAnswer, err := NewAPI(WithMediaEngine(m)).newPair(Configuration{})
  1260  		assert.NoError(t, err)
  1261  
  1262  		vp8WriterA, err := NewTrackLocalStaticRTP(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion2", WithRTPStreamID("a"))
  1263  		assert.NoError(t, err)
  1264  
  1265  		sender, err := pcOffer.AddTrack(vp8WriterA)
  1266  		assert.NoError(t, err)
  1267  		assert.NotNil(t, sender)
  1268  
  1269  		vp8WriterB, err := NewTrackLocalStaticRTP(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion2", WithRTPStreamID("b"))
  1270  		assert.NoError(t, err)
  1271  		err = sender.AddEncoding(vp8WriterB)
  1272  		assert.NoError(t, err)
  1273  
  1274  		vp8WriterC, err := NewTrackLocalStaticRTP(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion2", WithRTPStreamID("c"))
  1275  		assert.NoError(t, err)
  1276  		err = sender.AddEncoding(vp8WriterC)
  1277  		assert.NoError(t, err)
  1278  
  1279  		ridMap = map[string]int{}
  1280  		pcAnswer.OnTrack(onTrackHandler)
  1281  
  1282  		parameters := sender.GetParameters()
  1283  		assert.Equal(t, "a", parameters.Encodings[0].RID)
  1284  		assert.Equal(t, "b", parameters.Encodings[1].RID)
  1285  		assert.Equal(t, "c", parameters.Encodings[2].RID)
  1286  
  1287  		var midID, ridID, rsidID uint8
  1288  		for _, extension := range parameters.HeaderExtensions {
  1289  			switch extension.URI {
  1290  			case sdp.SDESMidURI:
  1291  				midID = uint8(extension.ID)
  1292  			case sdp.SDESRTPStreamIDURI:
  1293  				ridID = uint8(extension.ID)
  1294  			case sdesRepairRTPStreamIDURI:
  1295  				rsidID = uint8(extension.ID)
  1296  			}
  1297  		}
  1298  		assert.NotZero(t, midID)
  1299  		assert.NotZero(t, ridID)
  1300  		assert.NotZero(t, rsidID)
  1301  
  1302  		assert.NoError(t, signalPair(pcOffer, pcAnswer))
  1303  
  1304  		for sequenceNumber := uint16(0); !ridsFullfilled(); sequenceNumber++ {
  1305  			time.Sleep(20 * time.Millisecond)
  1306  
  1307  			for ssrc, rid := range rids {
  1308  				header := &rtp.Header{
  1309  					Version:        2,
  1310  					SSRC:           uint32(ssrc),
  1311  					SequenceNumber: sequenceNumber,
  1312  					PayloadType:    96,
  1313  				}
  1314  				assert.NoError(t, header.SetExtension(midID, []byte("0")))
  1315  
  1316  				// Send RSID for first 10 packets
  1317  				if sequenceNumber >= 10 {
  1318  					assert.NoError(t, header.SetExtension(ridID, []byte(rid)))
  1319  				} else {
  1320  					assert.NoError(t, header.SetExtension(rsidID, []byte(rid)))
  1321  					header.SSRC += 10
  1322  				}
  1323  
  1324  				var writer *TrackLocalStaticRTP
  1325  				switch rid {
  1326  				case "a":
  1327  					writer = vp8WriterA
  1328  				case "b":
  1329  					writer = vp8WriterB
  1330  				case "c":
  1331  					writer = vp8WriterC
  1332  				}
  1333  				_, err = writer.bindings[0].writeStream.WriteRTP(header, []byte{0x00})
  1334  				assert.NoError(t, err)
  1335  			}
  1336  		}
  1337  
  1338  		assertRidCorrect(t)
  1339  		closePairNow(t, pcOffer, pcAnswer)
  1340  	})
  1341  }
  1342  
  1343  // Everytime we receieve a new SSRC we probe it and try to determine the proper way to handle it.
  1344  // In most cases a Track explicitly declares a SSRC and a OnTrack is fired. In two cases we don't
  1345  // know the SSRC ahead of time
  1346  // * Undeclared SSRC in a single media section (https://github.com/pion/webrtc/issues/880)
  1347  // * Simulcast
  1348  //
  1349  // The Undeclared SSRC processing code would run before Simulcast. If a Simulcast Offer/Answer only
  1350  // contained one Media Section we would never fire the OnTrack. We would assume it was a failed
  1351  // Undeclared SSRC processing. This test asserts that we properly handled this.
  1352  func TestPeerConnection_Simulcast_NoDataChannel(t *testing.T) {
  1353  	lim := test.TimeOut(time.Second * 30)
  1354  	defer lim.Stop()
  1355  
  1356  	report := test.CheckRoutines(t)
  1357  	defer report()
  1358  
  1359  	// Enable Extension Headers needed for Simulcast
  1360  	m := &MediaEngine{}
  1361  	if err := m.RegisterDefaultCodecs(); err != nil {
  1362  		panic(err)
  1363  	}
  1364  	registerSimulcastHeaderExtensions(m, RTPCodecTypeVideo)
  1365  
  1366  	pcSender, pcReceiver, err := NewAPI(WithMediaEngine(m)).newPair(Configuration{})
  1367  	assert.NoError(t, err)
  1368  
  1369  	var wg sync.WaitGroup
  1370  	wg.Add(4)
  1371  
  1372  	var connectionWg sync.WaitGroup
  1373  	connectionWg.Add(2)
  1374  
  1375  	connectionStateChangeHandler := func(state PeerConnectionState) {
  1376  		if state == PeerConnectionStateConnected {
  1377  			connectionWg.Done()
  1378  		}
  1379  	}
  1380  
  1381  	pcSender.OnConnectionStateChange(connectionStateChangeHandler)
  1382  	pcReceiver.OnConnectionStateChange(connectionStateChangeHandler)
  1383  
  1384  	pcReceiver.OnTrack(func(track *TrackRemote, _ *RTPReceiver) {
  1385  		defer wg.Done()
  1386  	})
  1387  
  1388  	go func() {
  1389  		defer wg.Done()
  1390  		vp8WriterA, err := NewTrackLocalStaticRTP(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion", WithRTPStreamID("a"))
  1391  		assert.NoError(t, err)
  1392  
  1393  		sender, err := pcSender.AddTrack(vp8WriterA)
  1394  		assert.NoError(t, err)
  1395  		assert.NotNil(t, sender)
  1396  
  1397  		vp8WriterB, err := NewTrackLocalStaticRTP(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion", WithRTPStreamID("b"))
  1398  		assert.NoError(t, err)
  1399  		err = sender.AddEncoding(vp8WriterB)
  1400  		assert.NoError(t, err)
  1401  
  1402  		vp8WriterC, err := NewTrackLocalStaticRTP(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion", WithRTPStreamID("c"))
  1403  		assert.NoError(t, err)
  1404  		err = sender.AddEncoding(vp8WriterC)
  1405  		assert.NoError(t, err)
  1406  
  1407  		parameters := sender.GetParameters()
  1408  		var midID, ridID, rsidID uint8
  1409  		for _, extension := range parameters.HeaderExtensions {
  1410  			switch extension.URI {
  1411  			case sdp.SDESMidURI:
  1412  				midID = uint8(extension.ID)
  1413  			case sdp.SDESRTPStreamIDURI:
  1414  				ridID = uint8(extension.ID)
  1415  			case sdesRepairRTPStreamIDURI:
  1416  				rsidID = uint8(extension.ID)
  1417  			}
  1418  		}
  1419  		assert.NotZero(t, midID)
  1420  		assert.NotZero(t, ridID)
  1421  		assert.NotZero(t, rsidID)
  1422  
  1423  		// signaling
  1424  		offerSDP, err := pcSender.CreateOffer(nil)
  1425  		assert.NoError(t, err)
  1426  		err = pcSender.SetLocalDescription(offerSDP)
  1427  		assert.NoError(t, err)
  1428  
  1429  		err = pcReceiver.SetRemoteDescription(offerSDP)
  1430  		assert.NoError(t, err)
  1431  		answerSDP, err := pcReceiver.CreateAnswer(nil)
  1432  		assert.NoError(t, err)
  1433  
  1434  		answerGatheringComplete := GatheringCompletePromise(pcReceiver)
  1435  		err = pcReceiver.SetLocalDescription(answerSDP)
  1436  		assert.NoError(t, err)
  1437  		<-answerGatheringComplete
  1438  
  1439  		assert.NoError(t, pcSender.SetRemoteDescription(*pcReceiver.LocalDescription()))
  1440  
  1441  		connectionWg.Wait()
  1442  
  1443  		var seqNo uint16
  1444  		for i := 0; i < 100; i++ {
  1445  			pkt := &rtp.Packet{
  1446  				Header: rtp.Header{
  1447  					Version:        2,
  1448  					SequenceNumber: seqNo,
  1449  					PayloadType:    96,
  1450  				},
  1451  				Payload: []byte{0x00, 0x00},
  1452  			}
  1453  
  1454  			assert.NoError(t, pkt.SetExtension(ridID, []byte("a")))
  1455  			assert.NoError(t, pkt.SetExtension(midID, []byte(sender.rtpTransceiver.Mid())))
  1456  			assert.NoError(t, vp8WriterA.WriteRTP(pkt))
  1457  
  1458  			assert.NoError(t, pkt.SetExtension(ridID, []byte("b")))
  1459  			assert.NoError(t, pkt.SetExtension(midID, []byte(sender.rtpTransceiver.Mid())))
  1460  			assert.NoError(t, vp8WriterB.WriteRTP(pkt))
  1461  
  1462  			assert.NoError(t, pkt.SetExtension(ridID, []byte("c")))
  1463  			assert.NoError(t, pkt.SetExtension(midID, []byte(sender.rtpTransceiver.Mid())))
  1464  			assert.NoError(t, vp8WriterC.WriteRTP(pkt))
  1465  
  1466  			seqNo++
  1467  		}
  1468  	}()
  1469  
  1470  	wg.Wait()
  1471  
  1472  	closePairNow(t, pcSender, pcReceiver)
  1473  }