github.com/pion/webrtc/v4@v4.0.1/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  	"math/rand"
    17  	"regexp"
    18  	"strings"
    19  	"sync"
    20  	"sync/atomic"
    21  	"testing"
    22  	"time"
    23  
    24  	"github.com/pion/logging"
    25  	"github.com/pion/rtcp"
    26  	"github.com/pion/rtp"
    27  	"github.com/pion/sdp/v3"
    28  	"github.com/pion/transport/v3/test"
    29  	"github.com/pion/transport/v3/vnet"
    30  	"github.com/pion/webrtc/v4/internal/util"
    31  	"github.com/pion/webrtc/v4/pkg/media"
    32  	"github.com/stretchr/testify/assert"
    33  	"github.com/stretchr/testify/require"
    34  )
    35  
    36  var (
    37  	errIncomingTrackIDInvalid    = errors.New("incoming Track ID is invalid")
    38  	errIncomingTrackLabelInvalid = errors.New("incoming Track Label is invalid")
    39  	errNoTransceiverwithMid      = errors.New("no transceiver with mid")
    40  )
    41  
    42  /*
    43  Integration test for bi-directional peers
    44  
    45  This asserts we can send RTP and RTCP both ways, and blocks until
    46  each side gets something (and asserts payload contents)
    47  */
    48  // nolint: gocyclo
    49  func TestPeerConnection_Media_Sample(t *testing.T) {
    50  	const (
    51  		expectedTrackID  = "video"
    52  		expectedStreamID = "pion"
    53  	)
    54  
    55  	lim := test.TimeOut(time.Second * 30)
    56  	defer lim.Stop()
    57  
    58  	report := test.CheckRoutines(t)
    59  	defer report()
    60  
    61  	pcOffer, pcAnswer, err := newPair()
    62  	if err != nil {
    63  		t.Fatal(err)
    64  	}
    65  
    66  	awaitRTPRecv := make(chan bool)
    67  	awaitRTPRecvClosed := make(chan bool)
    68  	awaitRTPSend := make(chan bool)
    69  
    70  	awaitRTCPSenderRecv := make(chan bool)
    71  	awaitRTCPSenderSend := make(chan error)
    72  
    73  	awaitRTCPReceiverRecv := make(chan error)
    74  	awaitRTCPReceiverSend := make(chan error)
    75  
    76  	trackMetadataValid := make(chan error)
    77  
    78  	pcAnswer.OnTrack(func(track *TrackRemote, receiver *RTPReceiver) {
    79  		if track.ID() != expectedTrackID {
    80  			trackMetadataValid <- fmt.Errorf("%w: expected(%s) actual(%s)", errIncomingTrackIDInvalid, expectedTrackID, track.ID())
    81  			return
    82  		}
    83  
    84  		if track.StreamID() != expectedStreamID {
    85  			trackMetadataValid <- fmt.Errorf("%w: expected(%s) actual(%s)", errIncomingTrackLabelInvalid, expectedStreamID, track.StreamID())
    86  			return
    87  		}
    88  		close(trackMetadataValid)
    89  
    90  		go func() {
    91  			for {
    92  				time.Sleep(time.Millisecond * 100)
    93  				if routineErr := pcAnswer.WriteRTCP([]rtcp.Packet{&rtcp.RapidResynchronizationRequest{SenderSSRC: uint32(track.SSRC()), MediaSSRC: uint32(track.SSRC())}}); routineErr != nil {
    94  					awaitRTCPReceiverSend <- routineErr
    95  					return
    96  				}
    97  
    98  				select {
    99  				case <-awaitRTCPSenderRecv:
   100  					close(awaitRTCPReceiverSend)
   101  					return
   102  				default:
   103  				}
   104  			}
   105  		}()
   106  
   107  		go func() {
   108  			_, _, routineErr := receiver.Read(make([]byte, 1400))
   109  			if routineErr != nil {
   110  				awaitRTCPReceiverRecv <- routineErr
   111  			} else {
   112  				close(awaitRTCPReceiverRecv)
   113  			}
   114  		}()
   115  
   116  		haveClosedAwaitRTPRecv := false
   117  		for {
   118  			p, _, routineErr := track.ReadRTP()
   119  			if routineErr != nil {
   120  				close(awaitRTPRecvClosed)
   121  				return
   122  			} else if bytes.Equal(p.Payload, []byte{0x10, 0x00}) && !haveClosedAwaitRTPRecv {
   123  				haveClosedAwaitRTPRecv = true
   124  				close(awaitRTPRecv)
   125  			}
   126  		}
   127  	})
   128  
   129  	vp8Track, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, expectedTrackID, expectedStreamID)
   130  	if err != nil {
   131  		t.Fatal(err)
   132  	}
   133  	sender, err := pcOffer.AddTrack(vp8Track)
   134  	if err != nil {
   135  		t.Fatal(err)
   136  	}
   137  
   138  	go func() {
   139  		for {
   140  			time.Sleep(time.Millisecond * 100)
   141  			if pcOffer.ICEConnectionState() != ICEConnectionStateConnected {
   142  				continue
   143  			}
   144  			if routineErr := vp8Track.WriteSample(media.Sample{Data: []byte{0x00}, Duration: time.Second}); routineErr != nil {
   145  				fmt.Println(routineErr)
   146  			}
   147  
   148  			select {
   149  			case <-awaitRTPRecv:
   150  				close(awaitRTPSend)
   151  				return
   152  			default:
   153  			}
   154  		}
   155  	}()
   156  
   157  	go func() {
   158  		parameters := sender.GetParameters()
   159  
   160  		for {
   161  			time.Sleep(time.Millisecond * 100)
   162  			if routineErr := pcOffer.WriteRTCP([]rtcp.Packet{&rtcp.PictureLossIndication{SenderSSRC: uint32(parameters.Encodings[0].SSRC), MediaSSRC: uint32(parameters.Encodings[0].SSRC)}}); routineErr != nil {
   163  				awaitRTCPSenderSend <- routineErr
   164  			}
   165  
   166  			select {
   167  			case <-awaitRTCPReceiverRecv:
   168  				close(awaitRTCPSenderSend)
   169  				return
   170  			default:
   171  			}
   172  		}
   173  	}()
   174  
   175  	go func() {
   176  		if _, _, routineErr := sender.Read(make([]byte, 1400)); routineErr == nil {
   177  			close(awaitRTCPSenderRecv)
   178  		}
   179  	}()
   180  
   181  	assert.NoError(t, signalPair(pcOffer, pcAnswer))
   182  
   183  	err, ok := <-trackMetadataValid
   184  	if ok {
   185  		t.Fatal(err)
   186  	}
   187  
   188  	<-awaitRTPRecv
   189  	<-awaitRTPSend
   190  
   191  	<-awaitRTCPSenderRecv
   192  	if err, ok = <-awaitRTCPSenderSend; ok {
   193  		t.Fatal(err)
   194  	}
   195  
   196  	<-awaitRTCPReceiverRecv
   197  	if err, ok = <-awaitRTCPReceiverSend; ok {
   198  		t.Fatal(err)
   199  	}
   200  
   201  	closePairNow(t, pcOffer, pcAnswer)
   202  	<-awaitRTPRecvClosed
   203  }
   204  
   205  /*
   206  PeerConnection should be able to be torn down at anytime
   207  This test adds an input track and asserts
   208  
   209  * OnTrack doesn't fire since no video packets will arrive
   210  * No goroutine leaks
   211  * No deadlocks on shutdown
   212  */
   213  func TestPeerConnection_Media_Shutdown(t *testing.T) {
   214  	iceCompleteAnswer := make(chan struct{})
   215  	iceCompleteOffer := make(chan struct{})
   216  
   217  	lim := test.TimeOut(time.Second * 30)
   218  	defer lim.Stop()
   219  
   220  	report := test.CheckRoutines(t)
   221  	defer report()
   222  
   223  	pcOffer, pcAnswer, err := newPair()
   224  	if err != nil {
   225  		t.Fatal(err)
   226  	}
   227  
   228  	_, err = pcOffer.AddTransceiverFromKind(RTPCodecTypeVideo, RTPTransceiverInit{Direction: RTPTransceiverDirectionRecvonly})
   229  	if err != nil {
   230  		t.Fatal(err)
   231  	}
   232  
   233  	_, err = pcAnswer.AddTransceiverFromKind(RTPCodecTypeAudio, RTPTransceiverInit{Direction: RTPTransceiverDirectionRecvonly})
   234  	if err != nil {
   235  		t.Fatal(err)
   236  	}
   237  
   238  	opusTrack, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeOpus}, "audio", "pion1")
   239  	if err != nil {
   240  		t.Fatal(err)
   241  	}
   242  
   243  	vp8Track, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion2")
   244  	if err != nil {
   245  		t.Fatal(err)
   246  	}
   247  
   248  	if _, err = pcOffer.AddTrack(opusTrack); err != nil {
   249  		t.Fatal(err)
   250  	} else if _, err = pcAnswer.AddTrack(vp8Track); err != nil {
   251  		t.Fatal(err)
   252  	}
   253  
   254  	var onTrackFiredLock sync.Mutex
   255  	onTrackFired := false
   256  
   257  	pcAnswer.OnTrack(func(*TrackRemote, *RTPReceiver) {
   258  		onTrackFiredLock.Lock()
   259  		defer onTrackFiredLock.Unlock()
   260  		onTrackFired = true
   261  	})
   262  
   263  	pcAnswer.OnICEConnectionStateChange(func(iceState ICEConnectionState) {
   264  		if iceState == ICEConnectionStateConnected {
   265  			close(iceCompleteAnswer)
   266  		}
   267  	})
   268  	pcOffer.OnICEConnectionStateChange(func(iceState ICEConnectionState) {
   269  		if iceState == ICEConnectionStateConnected {
   270  			close(iceCompleteOffer)
   271  		}
   272  	})
   273  
   274  	err = signalPair(pcOffer, pcAnswer)
   275  	if err != nil {
   276  		t.Fatal(err)
   277  	}
   278  	<-iceCompleteAnswer
   279  	<-iceCompleteOffer
   280  
   281  	// Each PeerConnection should have one sender, one receiver and one transceiver
   282  	for _, pc := range []*PeerConnection{pcOffer, pcAnswer} {
   283  		senders := pc.GetSenders()
   284  		if len(senders) != 1 {
   285  			t.Errorf("Each PeerConnection should have one RTPSender, we have %d", len(senders))
   286  		}
   287  
   288  		receivers := pc.GetReceivers()
   289  		if len(receivers) != 2 {
   290  			t.Errorf("Each PeerConnection should have two RTPReceivers, we have %d", len(receivers))
   291  		}
   292  
   293  		transceivers := pc.GetTransceivers()
   294  		if len(transceivers) != 2 {
   295  			t.Errorf("Each PeerConnection should have two RTPTransceivers, we have %d", len(transceivers))
   296  		}
   297  	}
   298  
   299  	closePairNow(t, pcOffer, pcAnswer)
   300  
   301  	onTrackFiredLock.Lock()
   302  	if onTrackFired {
   303  		t.Fatalf("PeerConnection OnTrack fired even though we got no packets")
   304  	}
   305  	onTrackFiredLock.Unlock()
   306  }
   307  
   308  /*
   309  Integration test for behavior around media and disconnected peers
   310  
   311  * Sending RTP and RTCP to a disconnected Peer shouldn't return an error
   312  */
   313  func TestPeerConnection_Media_Disconnected(t *testing.T) {
   314  	lim := test.TimeOut(time.Second * 30)
   315  	defer lim.Stop()
   316  
   317  	report := test.CheckRoutines(t)
   318  	defer report()
   319  
   320  	s := SettingEngine{}
   321  	s.SetICETimeouts(time.Second/2, time.Second/2, time.Second/8)
   322  
   323  	m := &MediaEngine{}
   324  	assert.NoError(t, m.RegisterDefaultCodecs())
   325  
   326  	pcOffer, pcAnswer, wan := createVNetPair(t, nil)
   327  
   328  	keepPackets := &atomicBool{}
   329  	keepPackets.set(true)
   330  
   331  	// Add a filter that monitors the traffic on the router
   332  	wan.AddChunkFilter(func(vnet.Chunk) bool {
   333  		return keepPackets.get()
   334  	})
   335  
   336  	vp8Track, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion2")
   337  	if err != nil {
   338  		t.Fatal(err)
   339  	}
   340  
   341  	vp8Sender, err := pcOffer.AddTrack(vp8Track)
   342  	if err != nil {
   343  		t.Fatal(err)
   344  	}
   345  
   346  	haveDisconnected := make(chan error)
   347  	pcOffer.OnICEConnectionStateChange(func(iceState ICEConnectionState) {
   348  		if iceState == ICEConnectionStateDisconnected {
   349  			close(haveDisconnected)
   350  		} else if iceState == ICEConnectionStateConnected {
   351  			// Assert that DTLS is done by pull remote certificate, don't tear down the PC early
   352  			for {
   353  				if len(vp8Sender.Transport().GetRemoteCertificate()) != 0 {
   354  					if pcAnswer.sctpTransport.association() != nil {
   355  						break
   356  					}
   357  				}
   358  
   359  				time.Sleep(time.Second)
   360  			}
   361  
   362  			keepPackets.set(false)
   363  		}
   364  	})
   365  
   366  	if err = signalPair(pcOffer, pcAnswer); err != nil {
   367  		t.Fatal(err)
   368  	}
   369  
   370  	err, ok := <-haveDisconnected
   371  	if ok {
   372  		t.Fatal(err)
   373  	}
   374  	for i := 0; i <= 5; i++ {
   375  		if rtpErr := vp8Track.WriteSample(media.Sample{Data: []byte{0x00}, Duration: time.Second}); rtpErr != nil {
   376  			t.Fatal(rtpErr)
   377  		} else if rtcpErr := pcOffer.WriteRTCP([]rtcp.Packet{&rtcp.PictureLossIndication{MediaSSRC: 0}}); rtcpErr != nil {
   378  			t.Fatal(rtcpErr)
   379  		}
   380  	}
   381  
   382  	assert.NoError(t, wan.Stop())
   383  	closePairNow(t, pcOffer, pcAnswer)
   384  }
   385  
   386  type undeclaredSsrcLogger struct{ unhandledSimulcastError chan struct{} }
   387  
   388  func (u *undeclaredSsrcLogger) Trace(string)                  {}
   389  func (u *undeclaredSsrcLogger) Tracef(string, ...interface{}) {}
   390  func (u *undeclaredSsrcLogger) Debug(string)                  {}
   391  func (u *undeclaredSsrcLogger) Debugf(string, ...interface{}) {}
   392  func (u *undeclaredSsrcLogger) Info(string)                   {}
   393  func (u *undeclaredSsrcLogger) Infof(string, ...interface{})  {}
   394  func (u *undeclaredSsrcLogger) Warn(string)                   {}
   395  func (u *undeclaredSsrcLogger) Warnf(string, ...interface{})  {}
   396  func (u *undeclaredSsrcLogger) Error(string)                  {}
   397  func (u *undeclaredSsrcLogger) Errorf(format string, _ ...interface{}) {
   398  	if format == incomingUnhandledRTPSsrc {
   399  		close(u.unhandledSimulcastError)
   400  	}
   401  }
   402  
   403  type undeclaredSsrcLoggerFactory struct{ unhandledSimulcastError chan struct{} }
   404  
   405  func (u *undeclaredSsrcLoggerFactory) NewLogger(string) logging.LeveledLogger {
   406  	return &undeclaredSsrcLogger{u.unhandledSimulcastError}
   407  }
   408  
   409  // Filter SSRC lines
   410  func filterSsrc(offer string) (filteredSDP string) {
   411  	scanner := bufio.NewScanner(strings.NewReader(offer))
   412  	for scanner.Scan() {
   413  		l := scanner.Text()
   414  		if strings.HasPrefix(l, "a=ssrc") {
   415  			continue
   416  		}
   417  
   418  		filteredSDP += l + "\n"
   419  	}
   420  	return
   421  }
   422  
   423  // If a SessionDescription has a single media section and no SSRC
   424  // assume that it is meant to handle all RTP packets
   425  func TestUndeclaredSSRC(t *testing.T) {
   426  	lim := test.TimeOut(time.Second * 30)
   427  	defer lim.Stop()
   428  
   429  	report := test.CheckRoutines(t)
   430  	defer report()
   431  
   432  	t.Run("No SSRC", func(t *testing.T) {
   433  		pcOffer, pcAnswer, err := newPair()
   434  		assert.NoError(t, err)
   435  
   436  		vp8Writer, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion2")
   437  		assert.NoError(t, err)
   438  
   439  		_, err = pcOffer.AddTrack(vp8Writer)
   440  		assert.NoError(t, err)
   441  
   442  		onTrackFired := make(chan struct{})
   443  		pcAnswer.OnTrack(func(trackRemote *TrackRemote, _ *RTPReceiver) {
   444  			assert.Equal(t, trackRemote.StreamID(), vp8Writer.StreamID())
   445  			assert.Equal(t, trackRemote.ID(), vp8Writer.ID())
   446  			close(onTrackFired)
   447  		})
   448  
   449  		offer, err := pcOffer.CreateOffer(nil)
   450  		assert.NoError(t, err)
   451  
   452  		offerGatheringComplete := GatheringCompletePromise(pcOffer)
   453  		assert.NoError(t, pcOffer.SetLocalDescription(offer))
   454  		<-offerGatheringComplete
   455  
   456  		offer.SDP = filterSsrc(pcOffer.LocalDescription().SDP)
   457  		assert.NoError(t, pcAnswer.SetRemoteDescription(offer))
   458  
   459  		answer, err := pcAnswer.CreateAnswer(nil)
   460  		assert.NoError(t, err)
   461  
   462  		answerGatheringComplete := GatheringCompletePromise(pcAnswer)
   463  		assert.NoError(t, pcAnswer.SetLocalDescription(answer))
   464  		<-answerGatheringComplete
   465  
   466  		assert.NoError(t, pcOffer.SetRemoteDescription(*pcAnswer.LocalDescription()))
   467  
   468  		sendVideoUntilDone(onTrackFired, t, []*TrackLocalStaticSample{vp8Writer})
   469  		closePairNow(t, pcOffer, pcAnswer)
   470  	})
   471  
   472  	t.Run("Has RID", func(t *testing.T) {
   473  		unhandledSimulcastError := make(chan struct{})
   474  
   475  		m := &MediaEngine{}
   476  		assert.NoError(t, m.RegisterDefaultCodecs())
   477  
   478  		pcOffer, pcAnswer, err := NewAPI(WithSettingEngine(SettingEngine{
   479  			LoggerFactory: &undeclaredSsrcLoggerFactory{unhandledSimulcastError},
   480  		}), WithMediaEngine(m)).newPair(Configuration{})
   481  		assert.NoError(t, err)
   482  
   483  		vp8Writer, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion2")
   484  		assert.NoError(t, err)
   485  
   486  		_, err = pcOffer.AddTrack(vp8Writer)
   487  		assert.NoError(t, err)
   488  
   489  		offer, err := pcOffer.CreateOffer(nil)
   490  		assert.NoError(t, err)
   491  
   492  		offerGatheringComplete := GatheringCompletePromise(pcOffer)
   493  		assert.NoError(t, pcOffer.SetLocalDescription(offer))
   494  		<-offerGatheringComplete
   495  
   496  		// Append RID to end of SessionDescription. Will not be considered unhandled anymore
   497  		offer.SDP = filterSsrc(pcOffer.LocalDescription().SDP) + "a=" + sdpAttributeRid + "\r\n"
   498  		assert.NoError(t, pcAnswer.SetRemoteDescription(offer))
   499  
   500  		answer, err := pcAnswer.CreateAnswer(nil)
   501  		assert.NoError(t, err)
   502  
   503  		answerGatheringComplete := GatheringCompletePromise(pcAnswer)
   504  		assert.NoError(t, pcAnswer.SetLocalDescription(answer))
   505  		<-answerGatheringComplete
   506  
   507  		assert.NoError(t, pcOffer.SetRemoteDescription(*pcAnswer.LocalDescription()))
   508  
   509  		sendVideoUntilDone(unhandledSimulcastError, t, []*TrackLocalStaticSample{vp8Writer})
   510  		closePairNow(t, pcOffer, pcAnswer)
   511  	})
   512  }
   513  
   514  func TestAddTransceiverFromTrackSendOnly(t *testing.T) {
   515  	lim := test.TimeOut(time.Second * 30)
   516  	defer lim.Stop()
   517  
   518  	report := test.CheckRoutines(t)
   519  	defer report()
   520  
   521  	pc, err := NewPeerConnection(Configuration{})
   522  	if err != nil {
   523  		t.Error(err.Error())
   524  	}
   525  
   526  	track, err := NewTrackLocalStaticSample(
   527  		RTPCodecCapability{MimeType: "audio/Opus"},
   528  		"track-id",
   529  		"stream-id",
   530  	)
   531  	if err != nil {
   532  		t.Error(err.Error())
   533  	}
   534  
   535  	transceiver, err := pc.AddTransceiverFromTrack(track, RTPTransceiverInit{
   536  		Direction: RTPTransceiverDirectionSendonly,
   537  	})
   538  	if err != nil {
   539  		t.Error(err.Error())
   540  	}
   541  
   542  	if transceiver.Receiver() != nil {
   543  		t.Errorf("Transceiver shouldn't have a receiver")
   544  	}
   545  
   546  	if transceiver.Sender() == nil {
   547  		t.Errorf("Transceiver should have a sender")
   548  	}
   549  
   550  	if len(pc.GetTransceivers()) != 1 {
   551  		t.Errorf("PeerConnection should have one transceiver but has %d", len(pc.GetTransceivers()))
   552  	}
   553  
   554  	if len(pc.GetSenders()) != 1 {
   555  		t.Errorf("PeerConnection should have one sender but has %d", len(pc.GetSenders()))
   556  	}
   557  
   558  	offer, err := pc.CreateOffer(nil)
   559  	if err != nil {
   560  		t.Error(err.Error())
   561  	}
   562  
   563  	if !offerMediaHasDirection(offer, RTPCodecTypeAudio, RTPTransceiverDirectionSendonly) {
   564  		t.Errorf("Direction on SDP is not %s", RTPTransceiverDirectionSendonly)
   565  	}
   566  
   567  	assert.NoError(t, pc.Close())
   568  }
   569  
   570  func TestAddTransceiverFromTrackSendRecv(t *testing.T) {
   571  	lim := test.TimeOut(time.Second * 30)
   572  	defer lim.Stop()
   573  
   574  	report := test.CheckRoutines(t)
   575  	defer report()
   576  
   577  	pc, err := NewPeerConnection(Configuration{})
   578  	if err != nil {
   579  		t.Error(err.Error())
   580  	}
   581  
   582  	track, err := NewTrackLocalStaticSample(
   583  		RTPCodecCapability{MimeType: "audio/Opus"},
   584  		"track-id",
   585  		"stream-id",
   586  	)
   587  	if err != nil {
   588  		t.Error(err.Error())
   589  	}
   590  
   591  	transceiver, err := pc.AddTransceiverFromTrack(track, RTPTransceiverInit{
   592  		Direction: RTPTransceiverDirectionSendrecv,
   593  	})
   594  	if err != nil {
   595  		t.Error(err.Error())
   596  	}
   597  
   598  	if transceiver.Receiver() == nil {
   599  		t.Errorf("Transceiver should have a receiver")
   600  	}
   601  
   602  	if transceiver.Sender() == nil {
   603  		t.Errorf("Transceiver should have a sender")
   604  	}
   605  
   606  	if len(pc.GetTransceivers()) != 1 {
   607  		t.Errorf("PeerConnection should have one transceiver but has %d", len(pc.GetTransceivers()))
   608  	}
   609  
   610  	offer, err := pc.CreateOffer(nil)
   611  	if err != nil {
   612  		t.Error(err.Error())
   613  	}
   614  
   615  	if !offerMediaHasDirection(offer, RTPCodecTypeAudio, RTPTransceiverDirectionSendrecv) {
   616  		t.Errorf("Direction on SDP is not %s", RTPTransceiverDirectionSendrecv)
   617  	}
   618  	assert.NoError(t, pc.Close())
   619  }
   620  
   621  func TestAddTransceiverAddTrack_Reuse(t *testing.T) {
   622  	pc, err := NewPeerConnection(Configuration{})
   623  	assert.NoError(t, err)
   624  
   625  	tr, err := pc.AddTransceiverFromKind(
   626  		RTPCodecTypeVideo,
   627  		RTPTransceiverInit{Direction: RTPTransceiverDirectionRecvonly},
   628  	)
   629  	assert.NoError(t, err)
   630  
   631  	assert.Equal(t, []*RTPTransceiver{tr}, pc.GetTransceivers())
   632  
   633  	addTrack := func() (TrackLocal, *RTPSender) {
   634  		track, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "foo", "bar")
   635  		assert.NoError(t, err)
   636  
   637  		sender, err := pc.AddTrack(track)
   638  		assert.NoError(t, err)
   639  
   640  		return track, sender
   641  	}
   642  
   643  	track1, sender1 := addTrack()
   644  	assert.Equal(t, 1, len(pc.GetTransceivers()))
   645  	assert.Equal(t, sender1, tr.Sender())
   646  	assert.Equal(t, track1, tr.Sender().Track())
   647  	require.NoError(t, pc.RemoveTrack(sender1))
   648  
   649  	track2, _ := addTrack()
   650  	assert.Equal(t, 1, len(pc.GetTransceivers()))
   651  	assert.Equal(t, track2, tr.Sender().Track())
   652  
   653  	addTrack()
   654  	assert.Equal(t, 2, len(pc.GetTransceivers()))
   655  
   656  	assert.NoError(t, pc.Close())
   657  }
   658  
   659  func TestAddTransceiverAddTrack_NewRTPSender_Error(t *testing.T) {
   660  	pc, err := NewPeerConnection(Configuration{})
   661  	assert.NoError(t, err)
   662  
   663  	_, err = pc.AddTransceiverFromKind(
   664  		RTPCodecTypeVideo,
   665  		RTPTransceiverInit{Direction: RTPTransceiverDirectionRecvonly},
   666  	)
   667  	assert.NoError(t, err)
   668  
   669  	dtlsTransport := pc.dtlsTransport
   670  	pc.dtlsTransport = nil
   671  
   672  	track, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "foo", "bar")
   673  	assert.NoError(t, err)
   674  
   675  	_, err = pc.AddTrack(track)
   676  	assert.Error(t, err, "DTLSTransport must not be nil")
   677  
   678  	assert.Equal(t, 1, len(pc.GetTransceivers()))
   679  
   680  	pc.dtlsTransport = dtlsTransport
   681  	assert.NoError(t, pc.Close())
   682  }
   683  
   684  func TestRtpSenderReceiver_ReadClose_Error(t *testing.T) {
   685  	pc, err := NewPeerConnection(Configuration{})
   686  	assert.NoError(t, err)
   687  
   688  	tr, err := pc.AddTransceiverFromKind(
   689  		RTPCodecTypeVideo,
   690  		RTPTransceiverInit{Direction: RTPTransceiverDirectionSendrecv},
   691  	)
   692  	assert.NoError(t, err)
   693  
   694  	sender, receiver := tr.Sender(), tr.Receiver()
   695  	assert.NoError(t, sender.Stop())
   696  	_, _, err = sender.Read(make([]byte, 0, 1400))
   697  	assert.ErrorIs(t, err, io.ErrClosedPipe)
   698  
   699  	assert.NoError(t, receiver.Stop())
   700  	_, _, err = receiver.Read(make([]byte, 0, 1400))
   701  	assert.ErrorIs(t, err, io.ErrClosedPipe)
   702  
   703  	assert.NoError(t, pc.Close())
   704  }
   705  
   706  // nolint: dupl
   707  func TestAddTransceiverFromKind(t *testing.T) {
   708  	lim := test.TimeOut(time.Second * 30)
   709  	defer lim.Stop()
   710  
   711  	report := test.CheckRoutines(t)
   712  	defer report()
   713  
   714  	pc, err := NewPeerConnection(Configuration{})
   715  	if err != nil {
   716  		t.Error(err.Error())
   717  	}
   718  
   719  	transceiver, err := pc.AddTransceiverFromKind(RTPCodecTypeVideo, RTPTransceiverInit{
   720  		Direction: RTPTransceiverDirectionRecvonly,
   721  	})
   722  	if err != nil {
   723  		t.Error(err.Error())
   724  	}
   725  
   726  	if transceiver.Receiver() == nil {
   727  		t.Errorf("Transceiver should have a receiver")
   728  	}
   729  
   730  	if transceiver.Sender() != nil {
   731  		t.Errorf("Transceiver shouldn't have a sender")
   732  	}
   733  
   734  	offer, err := pc.CreateOffer(nil)
   735  	if err != nil {
   736  		t.Error(err.Error())
   737  	}
   738  
   739  	if !offerMediaHasDirection(offer, RTPCodecTypeVideo, RTPTransceiverDirectionRecvonly) {
   740  		t.Errorf("Direction on SDP is not %s", RTPTransceiverDirectionRecvonly)
   741  	}
   742  	assert.NoError(t, pc.Close())
   743  }
   744  
   745  func TestAddTransceiverFromTrackFailsRecvOnly(t *testing.T) {
   746  	lim := test.TimeOut(time.Second * 30)
   747  	defer lim.Stop()
   748  
   749  	report := test.CheckRoutines(t)
   750  	defer report()
   751  
   752  	pc, err := NewPeerConnection(Configuration{})
   753  	if err != nil {
   754  		t.Error(err.Error())
   755  	}
   756  
   757  	track, err := NewTrackLocalStaticSample(
   758  		RTPCodecCapability{MimeType: MimeTypeH264, SDPFmtpLine: "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f"},
   759  		"track-id",
   760  		"track-label",
   761  	)
   762  	if err != nil {
   763  		t.Error(err.Error())
   764  	}
   765  
   766  	transceiver, err := pc.AddTransceiverFromTrack(track, RTPTransceiverInit{
   767  		Direction: RTPTransceiverDirectionRecvonly,
   768  	})
   769  
   770  	if transceiver != nil {
   771  		t.Error("AddTransceiverFromTrack shouldn't succeed with Direction RTPTransceiverDirectionRecvonly")
   772  	}
   773  
   774  	assert.NotNil(t, err)
   775  	assert.NoError(t, pc.Close())
   776  }
   777  
   778  func TestPlanBMediaExchange(t *testing.T) {
   779  	runTest := func(trackCount int, t *testing.T) {
   780  		addSingleTrack := func(p *PeerConnection) *TrackLocalStaticSample {
   781  			track, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, fmt.Sprintf("video-%d", util.RandUint32()), fmt.Sprintf("video-%d", util.RandUint32()))
   782  			assert.NoError(t, err)
   783  
   784  			_, err = p.AddTrack(track)
   785  			assert.NoError(t, err)
   786  
   787  			return track
   788  		}
   789  
   790  		pcOffer, err := NewPeerConnection(Configuration{SDPSemantics: SDPSemanticsPlanB})
   791  		assert.NoError(t, err)
   792  
   793  		pcAnswer, err := NewPeerConnection(Configuration{SDPSemantics: SDPSemanticsPlanB})
   794  		assert.NoError(t, err)
   795  
   796  		var onTrackWaitGroup sync.WaitGroup
   797  		onTrackWaitGroup.Add(trackCount)
   798  		pcAnswer.OnTrack(func(*TrackRemote, *RTPReceiver) {
   799  			onTrackWaitGroup.Done()
   800  		})
   801  
   802  		done := make(chan struct{})
   803  		go func() {
   804  			onTrackWaitGroup.Wait()
   805  			close(done)
   806  		}()
   807  
   808  		_, err = pcAnswer.AddTransceiverFromKind(RTPCodecTypeVideo)
   809  		assert.NoError(t, err)
   810  
   811  		outboundTracks := []*TrackLocalStaticSample{}
   812  		for i := 0; i < trackCount; i++ {
   813  			outboundTracks = append(outboundTracks, addSingleTrack(pcOffer))
   814  		}
   815  
   816  		assert.NoError(t, signalPair(pcOffer, pcAnswer))
   817  
   818  		func() {
   819  			for {
   820  				select {
   821  				case <-time.After(20 * time.Millisecond):
   822  					for _, track := range outboundTracks {
   823  						assert.NoError(t, track.WriteSample(media.Sample{Data: []byte{0x00}, Duration: time.Second}))
   824  					}
   825  				case <-done:
   826  					return
   827  				}
   828  			}
   829  		}()
   830  
   831  		closePairNow(t, pcOffer, pcAnswer)
   832  	}
   833  
   834  	lim := test.TimeOut(time.Second * 30)
   835  	defer lim.Stop()
   836  
   837  	report := test.CheckRoutines(t)
   838  	defer report()
   839  
   840  	t.Run("Single Track", func(t *testing.T) {
   841  		runTest(1, t)
   842  	})
   843  	t.Run("Multi Track", func(t *testing.T) {
   844  		runTest(2, t)
   845  	})
   846  }
   847  
   848  // TestPeerConnection_Start_Only_Negotiated_Senders tests that only
   849  // the current negotiated transceivers senders provided in an
   850  // offer/answer are started
   851  func TestPeerConnection_Start_Only_Negotiated_Senders(t *testing.T) {
   852  	lim := test.TimeOut(time.Second * 30)
   853  	defer lim.Stop()
   854  
   855  	report := test.CheckRoutines(t)
   856  	defer report()
   857  
   858  	pcOffer, err := NewPeerConnection(Configuration{})
   859  	assert.NoError(t, err)
   860  	defer func() { assert.NoError(t, pcOffer.Close()) }()
   861  
   862  	pcAnswer, err := NewPeerConnection(Configuration{})
   863  	assert.NoError(t, err)
   864  	defer func() { assert.NoError(t, pcAnswer.Close()) }()
   865  
   866  	track1, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion1")
   867  	require.NoError(t, err)
   868  
   869  	sender1, err := pcOffer.AddTrack(track1)
   870  	require.NoError(t, err)
   871  
   872  	offer, err := pcOffer.CreateOffer(nil)
   873  	assert.NoError(t, err)
   874  
   875  	offerGatheringComplete := GatheringCompletePromise(pcOffer)
   876  	assert.NoError(t, pcOffer.SetLocalDescription(offer))
   877  	<-offerGatheringComplete
   878  	assert.NoError(t, pcAnswer.SetRemoteDescription(*pcOffer.LocalDescription()))
   879  	answer, err := pcAnswer.CreateAnswer(nil)
   880  	assert.NoError(t, err)
   881  	answerGatheringComplete := GatheringCompletePromise(pcAnswer)
   882  	assert.NoError(t, pcAnswer.SetLocalDescription(answer))
   883  	<-answerGatheringComplete
   884  
   885  	// Add a new track between providing the offer and applying the answer
   886  
   887  	track2, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion2")
   888  	require.NoError(t, err)
   889  
   890  	sender2, err := pcOffer.AddTrack(track2)
   891  	require.NoError(t, err)
   892  
   893  	// apply answer so we'll test generateMatchedSDP
   894  	assert.NoError(t, pcOffer.SetRemoteDescription(*pcAnswer.LocalDescription()))
   895  
   896  	// Wait for senders to be started by startTransports spawned goroutine
   897  	pcOffer.ops.Done()
   898  
   899  	// sender1 should be started but sender2 should not be started
   900  	assert.True(t, sender1.hasSent(), "sender1 is not started but should be started")
   901  	assert.False(t, sender2.hasSent(), "sender2 is started but should not be started")
   902  }
   903  
   904  // TestPeerConnection_Start_Right_Receiver tests that the right
   905  // receiver (the receiver which transceiver has the same media section as the track)
   906  // is started for the specified track
   907  func TestPeerConnection_Start_Right_Receiver(t *testing.T) {
   908  	isTransceiverReceiverStarted := func(pc *PeerConnection, mid string) (bool, error) {
   909  		for _, transceiver := range pc.GetTransceivers() {
   910  			if transceiver.Mid() != mid {
   911  				continue
   912  			}
   913  			return transceiver.Receiver() != nil && transceiver.Receiver().haveReceived(), nil
   914  		}
   915  		return false, fmt.Errorf("%w: %q", errNoTransceiverwithMid, mid)
   916  	}
   917  
   918  	lim := test.TimeOut(time.Second * 30)
   919  	defer lim.Stop()
   920  
   921  	report := test.CheckRoutines(t)
   922  	defer report()
   923  
   924  	pcOffer, pcAnswer, err := newPair()
   925  	require.NoError(t, err)
   926  
   927  	_, err = pcAnswer.AddTransceiverFromKind(RTPCodecTypeVideo, RTPTransceiverInit{Direction: RTPTransceiverDirectionRecvonly})
   928  	assert.NoError(t, err)
   929  
   930  	track1, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion1")
   931  	require.NoError(t, err)
   932  
   933  	sender1, err := pcOffer.AddTrack(track1)
   934  	require.NoError(t, err)
   935  
   936  	assert.NoError(t, signalPair(pcOffer, pcAnswer))
   937  
   938  	pcOffer.ops.Done()
   939  	pcAnswer.ops.Done()
   940  
   941  	// transceiver with mid 0 should be started
   942  	started, err := isTransceiverReceiverStarted(pcAnswer, "0")
   943  	assert.NoError(t, err)
   944  	assert.True(t, started, "transceiver with mid 0 should be started")
   945  
   946  	// Remove track
   947  	assert.NoError(t, pcOffer.RemoveTrack(sender1))
   948  
   949  	assert.NoError(t, signalPair(pcOffer, pcAnswer))
   950  
   951  	pcOffer.ops.Done()
   952  	pcAnswer.ops.Done()
   953  
   954  	// transceiver with mid 0 should not be started
   955  	started, err = isTransceiverReceiverStarted(pcAnswer, "0")
   956  	assert.NoError(t, err)
   957  	assert.False(t, started, "transceiver with mid 0 should not be started")
   958  
   959  	// Add a new transceiver (we're not using AddTrack since it'll reuse the transceiver with mid 0)
   960  	_, err = pcOffer.AddTransceiverFromTrack(track1)
   961  	assert.NoError(t, err)
   962  
   963  	_, err = pcAnswer.AddTransceiverFromKind(RTPCodecTypeVideo, RTPTransceiverInit{Direction: RTPTransceiverDirectionRecvonly})
   964  	assert.NoError(t, err)
   965  
   966  	assert.NoError(t, signalPair(pcOffer, pcAnswer))
   967  
   968  	pcOffer.ops.Done()
   969  	pcAnswer.ops.Done()
   970  
   971  	// transceiver with mid 0 should not be started
   972  	started, err = isTransceiverReceiverStarted(pcAnswer, "0")
   973  	assert.NoError(t, err)
   974  	assert.False(t, started, "transceiver with mid 0 should not be started")
   975  	// transceiver with mid 2 should be started
   976  	started, err = isTransceiverReceiverStarted(pcAnswer, "2")
   977  	assert.NoError(t, err)
   978  	assert.True(t, started, "transceiver with mid 2 should be started")
   979  
   980  	closePairNow(t, pcOffer, pcAnswer)
   981  }
   982  
   983  func TestPeerConnection_Simulcast_Probe(t *testing.T) {
   984  	lim := test.TimeOut(time.Second * 30) //nolint
   985  	defer lim.Stop()
   986  
   987  	report := test.CheckRoutines(t)
   988  	defer report()
   989  
   990  	// Assert that failed Simulcast probing doesn't cause
   991  	// the handleUndeclaredSSRC to be leaked
   992  	t.Run("Leak", func(t *testing.T) {
   993  		track, err := NewTrackLocalStaticRTP(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion")
   994  		assert.NoError(t, err)
   995  
   996  		offerer, answerer, err := newPair()
   997  		assert.NoError(t, err)
   998  
   999  		_, err = offerer.AddTrack(track)
  1000  		assert.NoError(t, err)
  1001  
  1002  		ticker := time.NewTicker(time.Millisecond * 20)
  1003  		defer ticker.Stop()
  1004  		testFinished := make(chan struct{})
  1005  		seenFiveStreams, seenFiveStreamsCancel := context.WithCancel(context.Background())
  1006  
  1007  		go func() {
  1008  			for {
  1009  				select {
  1010  				case <-testFinished:
  1011  					return
  1012  				case <-ticker.C:
  1013  					answerer.dtlsTransport.lock.Lock()
  1014  					if len(answerer.dtlsTransport.simulcastStreams) >= 5 {
  1015  						seenFiveStreamsCancel()
  1016  					}
  1017  					answerer.dtlsTransport.lock.Unlock()
  1018  
  1019  					track.mu.Lock()
  1020  					if len(track.bindings) == 1 {
  1021  						_, err = track.bindings[0].writeStream.WriteRTP(&rtp.Header{
  1022  							Version: 2,
  1023  							SSRC:    util.RandUint32(),
  1024  						}, []byte{0, 1, 2, 3, 4, 5})
  1025  						assert.NoError(t, err)
  1026  					}
  1027  					track.mu.Unlock()
  1028  				}
  1029  			}
  1030  		}()
  1031  
  1032  		assert.NoError(t, signalPair(offerer, answerer))
  1033  
  1034  		peerConnectionConnected := untilConnectionState(PeerConnectionStateConnected, offerer, answerer)
  1035  		peerConnectionConnected.Wait()
  1036  
  1037  		<-seenFiveStreams.Done()
  1038  
  1039  		closePairNow(t, offerer, answerer)
  1040  		close(testFinished)
  1041  	})
  1042  
  1043  	// Assert that NonSimulcast Traffic isn't incorrectly broken by the probe
  1044  	t.Run("Break NonSimulcast", func(t *testing.T) {
  1045  		unhandledSimulcastError := make(chan struct{})
  1046  
  1047  		m := &MediaEngine{}
  1048  		assert.NoError(t, m.RegisterDefaultCodecs())
  1049  		assert.NoError(t, ConfigureSimulcastExtensionHeaders(m))
  1050  
  1051  		pcOffer, pcAnswer, err := NewAPI(WithSettingEngine(SettingEngine{
  1052  			LoggerFactory: &undeclaredSsrcLoggerFactory{unhandledSimulcastError},
  1053  		}), WithMediaEngine(m)).newPair(Configuration{})
  1054  		assert.NoError(t, err)
  1055  
  1056  		firstTrack, err := NewTrackLocalStaticRTP(RTPCodecCapability{MimeType: MimeTypeVP8}, "firstTrack", "firstTrack")
  1057  		assert.NoError(t, err)
  1058  
  1059  		_, err = pcOffer.AddTrack(firstTrack)
  1060  		assert.NoError(t, err)
  1061  
  1062  		secondTrack, err := NewTrackLocalStaticRTP(RTPCodecCapability{MimeType: MimeTypeVP8}, "secondTrack", "secondTrack")
  1063  		assert.NoError(t, err)
  1064  
  1065  		_, err = pcOffer.AddTrack(secondTrack)
  1066  		assert.NoError(t, err)
  1067  
  1068  		assert.NoError(t, signalPairWithModification(pcOffer, pcAnswer, func(sessionDescription string) (filtered string) {
  1069  			shouldDiscard := false
  1070  
  1071  			scanner := bufio.NewScanner(strings.NewReader(sessionDescription))
  1072  			for scanner.Scan() {
  1073  				if strings.HasPrefix(scanner.Text(), "m=video") {
  1074  					shouldDiscard = !shouldDiscard
  1075  				}
  1076  
  1077  				if !shouldDiscard {
  1078  					filtered += scanner.Text() + "\r\n"
  1079  				}
  1080  			}
  1081  			return
  1082  		}))
  1083  
  1084  		peerConnectionConnected := untilConnectionState(PeerConnectionStateConnected, pcOffer, pcAnswer)
  1085  		peerConnectionConnected.Wait()
  1086  
  1087  		sequenceNumber := uint16(0)
  1088  		sendRTPPacket := func() {
  1089  			sequenceNumber++
  1090  			assert.NoError(t, firstTrack.WriteRTP(&rtp.Packet{
  1091  				Header: rtp.Header{
  1092  					Version:        2,
  1093  					SequenceNumber: sequenceNumber,
  1094  				},
  1095  				Payload: []byte{0x00},
  1096  			}))
  1097  			time.Sleep(20 * time.Millisecond)
  1098  		}
  1099  
  1100  		for ; sequenceNumber <= 5; sequenceNumber++ {
  1101  			sendRTPPacket()
  1102  		}
  1103  
  1104  		trackRemoteChan := make(chan *TrackRemote, 1)
  1105  		pcAnswer.OnTrack(func(trackRemote *TrackRemote, _ *RTPReceiver) {
  1106  			trackRemoteChan <- trackRemote
  1107  		})
  1108  
  1109  		assert.NoError(t, signalPair(pcOffer, pcAnswer))
  1110  
  1111  		trackRemote := func() *TrackRemote {
  1112  			for {
  1113  				select {
  1114  				case t := <-trackRemoteChan:
  1115  					return t
  1116  				default:
  1117  					sendRTPPacket()
  1118  				}
  1119  			}
  1120  		}()
  1121  
  1122  		func() {
  1123  			for {
  1124  				select {
  1125  				case <-unhandledSimulcastError:
  1126  					return
  1127  				default:
  1128  					sendRTPPacket()
  1129  				}
  1130  			}
  1131  		}()
  1132  
  1133  		_, _, err = trackRemote.Read(make([]byte, 1500))
  1134  		assert.NoError(t, err)
  1135  
  1136  		closePairNow(t, pcOffer, pcAnswer)
  1137  	})
  1138  }
  1139  
  1140  // Assert that CreateOffer returns an error for a RTPSender with no codecs
  1141  // pion/webrtc#1702
  1142  func TestPeerConnection_CreateOffer_NoCodecs(t *testing.T) {
  1143  	lim := test.TimeOut(time.Second * 30)
  1144  	defer lim.Stop()
  1145  
  1146  	report := test.CheckRoutines(t)
  1147  	defer report()
  1148  
  1149  	m := &MediaEngine{}
  1150  
  1151  	pc, err := NewAPI(WithMediaEngine(m)).NewPeerConnection(Configuration{})
  1152  	assert.NoError(t, err)
  1153  
  1154  	track, err := NewTrackLocalStaticRTP(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion")
  1155  	assert.NoError(t, err)
  1156  
  1157  	_, err = pc.AddTrack(track)
  1158  	assert.NoError(t, err)
  1159  
  1160  	_, err = pc.CreateOffer(nil)
  1161  	assert.Equal(t, err, ErrSenderWithNoCodecs)
  1162  
  1163  	assert.NoError(t, pc.Close())
  1164  }
  1165  
  1166  // Assert that AddTrack is thread-safe
  1167  func TestPeerConnection_RaceReplaceTrack(t *testing.T) {
  1168  	pc, err := NewPeerConnection(Configuration{})
  1169  	assert.NoError(t, err)
  1170  
  1171  	addTrack := func() *TrackLocalStaticSample {
  1172  		track, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "foo", "bar")
  1173  		assert.NoError(t, err)
  1174  		_, err = pc.AddTrack(track)
  1175  		assert.NoError(t, err)
  1176  		return track
  1177  	}
  1178  
  1179  	for i := 0; i < 10; i++ {
  1180  		addTrack()
  1181  	}
  1182  	for _, tr := range pc.GetTransceivers() {
  1183  		assert.NoError(t, pc.RemoveTrack(tr.Sender()))
  1184  	}
  1185  
  1186  	var wg sync.WaitGroup
  1187  	tracks := make([]*TrackLocalStaticSample, 10)
  1188  	wg.Add(10)
  1189  	for i := 0; i < 10; i++ {
  1190  		go func(j int) {
  1191  			tracks[j] = addTrack()
  1192  			wg.Done()
  1193  		}(i)
  1194  	}
  1195  
  1196  	wg.Wait()
  1197  
  1198  	for _, track := range tracks {
  1199  		have := false
  1200  		for _, t := range pc.GetTransceivers() {
  1201  			if t.Sender() != nil && t.Sender().Track() == track {
  1202  				have = true
  1203  				break
  1204  			}
  1205  		}
  1206  		if !have {
  1207  			t.Errorf("track was added but not found on senders")
  1208  		}
  1209  	}
  1210  
  1211  	assert.NoError(t, pc.Close())
  1212  }
  1213  
  1214  func TestPeerConnection_Simulcast(t *testing.T) {
  1215  	lim := test.TimeOut(time.Second * 30)
  1216  	defer lim.Stop()
  1217  
  1218  	report := test.CheckRoutines(t)
  1219  	defer report()
  1220  
  1221  	rids := []string{"a", "b", "c"}
  1222  
  1223  	t.Run("E2E", func(t *testing.T) {
  1224  		pcOffer, pcAnswer, err := newPair()
  1225  		assert.NoError(t, err)
  1226  
  1227  		vp8WriterA, err := NewTrackLocalStaticRTP(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion2", WithRTPStreamID(rids[0]))
  1228  		assert.NoError(t, err)
  1229  
  1230  		vp8WriterB, err := NewTrackLocalStaticRTP(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion2", WithRTPStreamID(rids[1]))
  1231  		assert.NoError(t, err)
  1232  
  1233  		vp8WriterC, err := NewTrackLocalStaticRTP(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion2", WithRTPStreamID(rids[2]))
  1234  		assert.NoError(t, err)
  1235  
  1236  		sender, err := pcOffer.AddTrack(vp8WriterA)
  1237  		assert.NoError(t, err)
  1238  		assert.NotNil(t, sender)
  1239  
  1240  		assert.NoError(t, sender.AddEncoding(vp8WriterB))
  1241  		assert.NoError(t, sender.AddEncoding(vp8WriterC))
  1242  
  1243  		var ridMapLock sync.RWMutex
  1244  		ridMap := map[string]int{}
  1245  
  1246  		assertRidCorrect := func(t *testing.T) {
  1247  			ridMapLock.Lock()
  1248  			defer ridMapLock.Unlock()
  1249  
  1250  			for _, rid := range rids {
  1251  				assert.Equal(t, ridMap[rid], 1)
  1252  			}
  1253  			assert.Equal(t, len(ridMap), 3)
  1254  		}
  1255  
  1256  		ridsFullfilled := func() bool {
  1257  			ridMapLock.Lock()
  1258  			defer ridMapLock.Unlock()
  1259  
  1260  			ridCount := len(ridMap)
  1261  			return ridCount == 3
  1262  		}
  1263  
  1264  		pcAnswer.OnTrack(func(trackRemote *TrackRemote, _ *RTPReceiver) {
  1265  			ridMapLock.Lock()
  1266  			defer ridMapLock.Unlock()
  1267  			ridMap[trackRemote.RID()] = ridMap[trackRemote.RID()] + 1
  1268  		})
  1269  
  1270  		parameters := sender.GetParameters()
  1271  		assert.Equal(t, "a", parameters.Encodings[0].RID)
  1272  		assert.Equal(t, "b", parameters.Encodings[1].RID)
  1273  		assert.Equal(t, "c", parameters.Encodings[2].RID)
  1274  
  1275  		var midID, ridID uint8
  1276  		for _, extension := range parameters.HeaderExtensions {
  1277  			switch extension.URI {
  1278  			case sdp.SDESMidURI:
  1279  				midID = uint8(extension.ID)
  1280  			case sdp.SDESRTPStreamIDURI:
  1281  				ridID = uint8(extension.ID)
  1282  			}
  1283  		}
  1284  		assert.NotZero(t, midID)
  1285  		assert.NotZero(t, ridID)
  1286  
  1287  		assert.NoError(t, signalPair(pcOffer, pcAnswer))
  1288  
  1289  		// padding only packets should not affect simulcast probe
  1290  		var sequenceNumber uint16
  1291  		for sequenceNumber = 0; sequenceNumber < simulcastProbeCount+10; sequenceNumber++ {
  1292  			time.Sleep(20 * time.Millisecond)
  1293  
  1294  			for _, track := range []*TrackLocalStaticRTP{vp8WriterA, vp8WriterB, vp8WriterC} {
  1295  				pkt := &rtp.Packet{
  1296  					Header: rtp.Header{
  1297  						Version:        2,
  1298  						SequenceNumber: sequenceNumber,
  1299  						PayloadType:    96,
  1300  						Padding:        true,
  1301  					},
  1302  					Payload: []byte{0x00, 0x02},
  1303  				}
  1304  
  1305  				assert.NoError(t, track.WriteRTP(pkt))
  1306  			}
  1307  		}
  1308  		assert.False(t, ridsFullfilled(), "Simulcast probe should not be fulfilled by padding only packets")
  1309  
  1310  		for ; !ridsFullfilled(); sequenceNumber++ {
  1311  			time.Sleep(20 * time.Millisecond)
  1312  
  1313  			for _, track := range []*TrackLocalStaticRTP{vp8WriterA, vp8WriterB, vp8WriterC} {
  1314  				pkt := &rtp.Packet{
  1315  					Header: rtp.Header{
  1316  						Version:        2,
  1317  						SequenceNumber: sequenceNumber,
  1318  						PayloadType:    96,
  1319  					},
  1320  					Payload: []byte{0x00},
  1321  				}
  1322  				assert.NoError(t, pkt.Header.SetExtension(midID, []byte("0")))
  1323  				assert.NoError(t, pkt.Header.SetExtension(ridID, []byte(track.RID())))
  1324  
  1325  				assert.NoError(t, track.WriteRTP(pkt))
  1326  			}
  1327  		}
  1328  
  1329  		assertRidCorrect(t)
  1330  		closePairNow(t, pcOffer, pcAnswer)
  1331  	})
  1332  
  1333  	t.Run("RTCP", func(t *testing.T) {
  1334  		pcOffer, pcAnswer, err := newPair()
  1335  		assert.NoError(t, err)
  1336  
  1337  		vp8WriterA, err := NewTrackLocalStaticRTP(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion2", WithRTPStreamID(rids[0]))
  1338  		assert.NoError(t, err)
  1339  
  1340  		vp8WriterB, err := NewTrackLocalStaticRTP(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion2", WithRTPStreamID(rids[1]))
  1341  		assert.NoError(t, err)
  1342  
  1343  		vp8WriterC, err := NewTrackLocalStaticRTP(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion2", WithRTPStreamID(rids[2]))
  1344  		assert.NoError(t, err)
  1345  
  1346  		sender, err := pcOffer.AddTrack(vp8WriterA)
  1347  		assert.NoError(t, err)
  1348  		assert.NotNil(t, sender)
  1349  
  1350  		assert.NoError(t, sender.AddEncoding(vp8WriterB))
  1351  		assert.NoError(t, sender.AddEncoding(vp8WriterC))
  1352  
  1353  		rtcpCounter := uint64(0)
  1354  		pcAnswer.OnTrack(func(trackRemote *TrackRemote, receiver *RTPReceiver) {
  1355  			_, _, simulcastReadErr := receiver.ReadSimulcastRTCP(trackRemote.RID())
  1356  			assert.NoError(t, simulcastReadErr)
  1357  			atomic.AddUint64(&rtcpCounter, 1)
  1358  		})
  1359  
  1360  		var midID, ridID uint8
  1361  		for _, extension := range sender.GetParameters().HeaderExtensions {
  1362  			switch extension.URI {
  1363  			case sdp.SDESMidURI:
  1364  				midID = uint8(extension.ID)
  1365  			case sdp.SDESRTPStreamIDURI:
  1366  				ridID = uint8(extension.ID)
  1367  			}
  1368  		}
  1369  		assert.NotZero(t, midID)
  1370  		assert.NotZero(t, ridID)
  1371  
  1372  		assert.NoError(t, signalPair(pcOffer, pcAnswer))
  1373  
  1374  		for sequenceNumber := uint16(0); atomic.LoadUint64(&rtcpCounter) < 3; sequenceNumber++ {
  1375  			time.Sleep(20 * time.Millisecond)
  1376  
  1377  			for _, track := range []*TrackLocalStaticRTP{vp8WriterA, vp8WriterB, vp8WriterC} {
  1378  				pkt := &rtp.Packet{
  1379  					Header: rtp.Header{
  1380  						Version:        2,
  1381  						SequenceNumber: sequenceNumber,
  1382  						PayloadType:    96,
  1383  					},
  1384  					Payload: []byte{0x00},
  1385  				}
  1386  				assert.NoError(t, pkt.Header.SetExtension(midID, []byte("0")))
  1387  				assert.NoError(t, pkt.Header.SetExtension(ridID, []byte(track.RID())))
  1388  
  1389  				assert.NoError(t, track.WriteRTP(pkt))
  1390  			}
  1391  		}
  1392  
  1393  		closePairNow(t, pcOffer, pcAnswer)
  1394  	})
  1395  }
  1396  
  1397  type simulcastTestTrackLocal struct {
  1398  	*TrackLocalStaticRTP
  1399  }
  1400  
  1401  // don't use ssrc&payload in bindings to let the test write different stream packets.
  1402  func (s *simulcastTestTrackLocal) WriteRTP(pkt *rtp.Packet) error {
  1403  	packet := getPacketAllocationFromPool()
  1404  
  1405  	defer resetPacketPoolAllocation(packet)
  1406  
  1407  	*packet = *pkt
  1408  
  1409  	s.mu.RLock()
  1410  	defer s.mu.RUnlock()
  1411  
  1412  	writeErrs := []error{}
  1413  
  1414  	for _, b := range s.bindings {
  1415  		if _, err := b.writeStream.WriteRTP(&packet.Header, packet.Payload); err != nil {
  1416  			writeErrs = append(writeErrs, err)
  1417  		}
  1418  	}
  1419  
  1420  	return util.FlattenErrs(writeErrs)
  1421  }
  1422  
  1423  func TestPeerConnection_Simulcast_RTX(t *testing.T) {
  1424  	lim := test.TimeOut(time.Second * 30)
  1425  	defer lim.Stop()
  1426  
  1427  	report := test.CheckRoutines(t)
  1428  	defer report()
  1429  
  1430  	rids := []string{"a", "b"}
  1431  	pcOffer, pcAnswer, err := newPair()
  1432  	assert.NoError(t, err)
  1433  
  1434  	vp8WriterAStatic, err := NewTrackLocalStaticRTP(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion2", WithRTPStreamID(rids[0]))
  1435  	assert.NoError(t, err)
  1436  
  1437  	vp8WriterBStatic, err := NewTrackLocalStaticRTP(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion2", WithRTPStreamID(rids[1]))
  1438  	assert.NoError(t, err)
  1439  
  1440  	vp8WriterA, vp8WriterB := &simulcastTestTrackLocal{vp8WriterAStatic}, &simulcastTestTrackLocal{vp8WriterBStatic}
  1441  
  1442  	sender, err := pcOffer.AddTrack(vp8WriterA)
  1443  	assert.NoError(t, err)
  1444  	assert.NotNil(t, sender)
  1445  
  1446  	assert.NoError(t, sender.AddEncoding(vp8WriterB))
  1447  
  1448  	var ridMapLock sync.RWMutex
  1449  	ridMap := map[string]int{}
  1450  
  1451  	assertRidCorrect := func(t *testing.T) {
  1452  		ridMapLock.Lock()
  1453  		defer ridMapLock.Unlock()
  1454  
  1455  		for _, rid := range rids {
  1456  			assert.Equal(t, ridMap[rid], 1)
  1457  		}
  1458  		assert.Equal(t, len(ridMap), 2)
  1459  	}
  1460  
  1461  	ridsFullfilled := func() bool {
  1462  		ridMapLock.Lock()
  1463  		defer ridMapLock.Unlock()
  1464  
  1465  		ridCount := len(ridMap)
  1466  		return ridCount == 2
  1467  	}
  1468  
  1469  	var rtxPacketRead atomic.Int32
  1470  	var wg sync.WaitGroup
  1471  	wg.Add(2)
  1472  
  1473  	pcAnswer.OnTrack(func(trackRemote *TrackRemote, _ *RTPReceiver) {
  1474  		ridMapLock.Lock()
  1475  		ridMap[trackRemote.RID()] = ridMap[trackRemote.RID()] + 1
  1476  		ridMapLock.Unlock()
  1477  
  1478  		defer wg.Done()
  1479  
  1480  		for {
  1481  			_, attr, rerr := trackRemote.ReadRTP()
  1482  			if rerr != nil {
  1483  				break
  1484  			}
  1485  			if pt, ok := attr.Get(AttributeRtxPayloadType).(byte); ok {
  1486  				if pt == 97 {
  1487  					rtxPacketRead.Add(1)
  1488  				}
  1489  			}
  1490  		}
  1491  	})
  1492  
  1493  	parameters := sender.GetParameters()
  1494  	assert.Equal(t, "a", parameters.Encodings[0].RID)
  1495  	assert.Equal(t, "b", parameters.Encodings[1].RID)
  1496  
  1497  	var midID, ridID, rsid uint8
  1498  	for _, extension := range parameters.HeaderExtensions {
  1499  		switch extension.URI {
  1500  		case sdp.SDESMidURI:
  1501  			midID = uint8(extension.ID)
  1502  		case sdp.SDESRTPStreamIDURI:
  1503  			ridID = uint8(extension.ID)
  1504  		case sdesRepairRTPStreamIDURI:
  1505  			rsid = uint8(extension.ID)
  1506  		}
  1507  	}
  1508  	assert.NotZero(t, midID)
  1509  	assert.NotZero(t, ridID)
  1510  	assert.NotZero(t, rsid)
  1511  
  1512  	err = signalPairWithModification(pcOffer, pcAnswer, func(sdp string) string {
  1513  		// Original chrome sdp contains no ssrc info https://pastebin.com/raw/JTjX6zg6
  1514  		re := regexp.MustCompile("(?m)[\r\n]+^.*a=ssrc.*$")
  1515  		res := re.ReplaceAllString(sdp, "")
  1516  		return res
  1517  	})
  1518  	assert.NoError(t, err)
  1519  
  1520  	// padding only packets should not affect simulcast probe
  1521  	var sequenceNumber uint16
  1522  	for sequenceNumber = 0; sequenceNumber < simulcastProbeCount+10; sequenceNumber++ {
  1523  		time.Sleep(20 * time.Millisecond)
  1524  
  1525  		for i, track := range []*simulcastTestTrackLocal{vp8WriterA, vp8WriterB} {
  1526  			pkt := &rtp.Packet{
  1527  				Header: rtp.Header{
  1528  					Version:        2,
  1529  					SequenceNumber: sequenceNumber,
  1530  					PayloadType:    96,
  1531  					Padding:        true,
  1532  					SSRC:           uint32(i + 1),
  1533  				},
  1534  				Payload: []byte{0x00, 0x02},
  1535  			}
  1536  
  1537  			assert.NoError(t, track.WriteRTP(pkt))
  1538  		}
  1539  	}
  1540  	assert.False(t, ridsFullfilled(), "Simulcast probe should not be fulfilled by padding only packets")
  1541  
  1542  	for ; !ridsFullfilled(); sequenceNumber++ {
  1543  		time.Sleep(20 * time.Millisecond)
  1544  
  1545  		for i, track := range []*simulcastTestTrackLocal{vp8WriterA, vp8WriterB} {
  1546  			pkt := &rtp.Packet{
  1547  				Header: rtp.Header{
  1548  					Version:        2,
  1549  					SequenceNumber: sequenceNumber,
  1550  					PayloadType:    96,
  1551  					SSRC:           uint32(i + 1),
  1552  				},
  1553  				Payload: []byte{0x00},
  1554  			}
  1555  			assert.NoError(t, pkt.Header.SetExtension(midID, []byte("0")))
  1556  			assert.NoError(t, pkt.Header.SetExtension(ridID, []byte(track.RID())))
  1557  
  1558  			assert.NoError(t, track.WriteRTP(pkt))
  1559  		}
  1560  	}
  1561  
  1562  	assertRidCorrect(t)
  1563  
  1564  	for i := 0; i < simulcastProbeCount+10; i++ {
  1565  		sequenceNumber++
  1566  		time.Sleep(10 * time.Millisecond)
  1567  
  1568  		for j, track := range []*simulcastTestTrackLocal{vp8WriterA, vp8WriterB} {
  1569  			pkt := &rtp.Packet{
  1570  				Header: rtp.Header{
  1571  					Version:        2,
  1572  					SequenceNumber: sequenceNumber,
  1573  					PayloadType:    97,
  1574  					SSRC:           uint32(100 + j),
  1575  				},
  1576  				Payload: []byte{0x00, 0x00, 0x00, 0x00, 0x00},
  1577  			}
  1578  			assert.NoError(t, pkt.Header.SetExtension(midID, []byte("0")))
  1579  			assert.NoError(t, pkt.Header.SetExtension(ridID, []byte(track.RID())))
  1580  			assert.NoError(t, pkt.Header.SetExtension(rsid, []byte(track.RID())))
  1581  
  1582  			assert.NoError(t, track.WriteRTP(pkt))
  1583  		}
  1584  	}
  1585  
  1586  	for ; rtxPacketRead.Load() == 0; sequenceNumber++ {
  1587  		time.Sleep(20 * time.Millisecond)
  1588  
  1589  		for i, track := range []*simulcastTestTrackLocal{vp8WriterA, vp8WriterB} {
  1590  			pkt := &rtp.Packet{
  1591  				Header: rtp.Header{
  1592  					Version:        2,
  1593  					SequenceNumber: sequenceNumber,
  1594  					PayloadType:    96,
  1595  					SSRC:           uint32(i + 1),
  1596  				},
  1597  				Payload: []byte{0x00},
  1598  			}
  1599  			assert.NoError(t, pkt.Header.SetExtension(midID, []byte("0")))
  1600  			assert.NoError(t, pkt.Header.SetExtension(ridID, []byte(track.RID())))
  1601  
  1602  			assert.NoError(t, track.WriteRTP(pkt))
  1603  		}
  1604  	}
  1605  
  1606  	closePairNow(t, pcOffer, pcAnswer)
  1607  
  1608  	wg.Wait()
  1609  
  1610  	assert.Greater(t, rtxPacketRead.Load(), int32(0), "no rtx packet read")
  1611  }
  1612  
  1613  // Everytime we receive a new SSRC we probe it and try to determine the proper way to handle it.
  1614  // In most cases a Track explicitly declares a SSRC and a OnTrack is fired. In two cases we don't
  1615  // know the SSRC ahead of time
  1616  // * Undeclared SSRC in a single media section (https://github.com/pion/webrtc/issues/880)
  1617  // * Simulcast
  1618  //
  1619  // The Undeclared SSRC processing code would run before Simulcast. If a Simulcast Offer/Answer only
  1620  // contained one Media Section we would never fire the OnTrack. We would assume it was a failed
  1621  // Undeclared SSRC processing. This test asserts that we properly handled this.
  1622  func TestPeerConnection_Simulcast_NoDataChannel(t *testing.T) {
  1623  	lim := test.TimeOut(time.Second * 30)
  1624  	defer lim.Stop()
  1625  
  1626  	report := test.CheckRoutines(t)
  1627  	defer report()
  1628  
  1629  	pcSender, pcReceiver, err := newPair()
  1630  	assert.NoError(t, err)
  1631  
  1632  	var wg sync.WaitGroup
  1633  	wg.Add(4)
  1634  
  1635  	var connectionWg sync.WaitGroup
  1636  	connectionWg.Add(2)
  1637  
  1638  	connectionStateChangeHandler := func(state PeerConnectionState) {
  1639  		if state == PeerConnectionStateConnected {
  1640  			connectionWg.Done()
  1641  		}
  1642  	}
  1643  
  1644  	pcSender.OnConnectionStateChange(connectionStateChangeHandler)
  1645  	pcReceiver.OnConnectionStateChange(connectionStateChangeHandler)
  1646  
  1647  	pcReceiver.OnTrack(func(*TrackRemote, *RTPReceiver) {
  1648  		defer wg.Done()
  1649  	})
  1650  
  1651  	go func() {
  1652  		defer wg.Done()
  1653  		vp8WriterA, err := NewTrackLocalStaticRTP(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion", WithRTPStreamID("a"))
  1654  		assert.NoError(t, err)
  1655  
  1656  		sender, err := pcSender.AddTrack(vp8WriterA)
  1657  		assert.NoError(t, err)
  1658  		assert.NotNil(t, sender)
  1659  
  1660  		vp8WriterB, err := NewTrackLocalStaticRTP(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion", WithRTPStreamID("b"))
  1661  		assert.NoError(t, err)
  1662  		err = sender.AddEncoding(vp8WriterB)
  1663  		assert.NoError(t, err)
  1664  
  1665  		vp8WriterC, err := NewTrackLocalStaticRTP(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion", WithRTPStreamID("c"))
  1666  		assert.NoError(t, err)
  1667  		err = sender.AddEncoding(vp8WriterC)
  1668  		assert.NoError(t, err)
  1669  
  1670  		parameters := sender.GetParameters()
  1671  		var midID, ridID, rsidID uint8
  1672  		for _, extension := range parameters.HeaderExtensions {
  1673  			switch extension.URI {
  1674  			case sdp.SDESMidURI:
  1675  				midID = uint8(extension.ID)
  1676  			case sdp.SDESRTPStreamIDURI:
  1677  				ridID = uint8(extension.ID)
  1678  			case sdesRepairRTPStreamIDURI:
  1679  				rsidID = uint8(extension.ID)
  1680  			}
  1681  		}
  1682  		assert.NotZero(t, midID)
  1683  		assert.NotZero(t, ridID)
  1684  		assert.NotZero(t, rsidID)
  1685  
  1686  		// signaling
  1687  		offerSDP, err := pcSender.CreateOffer(nil)
  1688  		assert.NoError(t, err)
  1689  		err = pcSender.SetLocalDescription(offerSDP)
  1690  		assert.NoError(t, err)
  1691  
  1692  		err = pcReceiver.SetRemoteDescription(offerSDP)
  1693  		assert.NoError(t, err)
  1694  		answerSDP, err := pcReceiver.CreateAnswer(nil)
  1695  		assert.NoError(t, err)
  1696  
  1697  		answerGatheringComplete := GatheringCompletePromise(pcReceiver)
  1698  		err = pcReceiver.SetLocalDescription(answerSDP)
  1699  		assert.NoError(t, err)
  1700  		<-answerGatheringComplete
  1701  
  1702  		assert.NoError(t, pcSender.SetRemoteDescription(*pcReceiver.LocalDescription()))
  1703  
  1704  		connectionWg.Wait()
  1705  
  1706  		var seqNo uint16
  1707  		for i := 0; i < 100; i++ {
  1708  			pkt := &rtp.Packet{
  1709  				Header: rtp.Header{
  1710  					Version:        2,
  1711  					SequenceNumber: seqNo,
  1712  					PayloadType:    96,
  1713  				},
  1714  				Payload: []byte{0x00, 0x00},
  1715  			}
  1716  
  1717  			assert.NoError(t, pkt.SetExtension(ridID, []byte("a")))
  1718  			assert.NoError(t, pkt.SetExtension(midID, []byte(sender.rtpTransceiver.Mid())))
  1719  			assert.NoError(t, vp8WriterA.WriteRTP(pkt))
  1720  
  1721  			assert.NoError(t, pkt.SetExtension(ridID, []byte("b")))
  1722  			assert.NoError(t, pkt.SetExtension(midID, []byte(sender.rtpTransceiver.Mid())))
  1723  			assert.NoError(t, vp8WriterB.WriteRTP(pkt))
  1724  
  1725  			assert.NoError(t, pkt.SetExtension(ridID, []byte("c")))
  1726  			assert.NoError(t, pkt.SetExtension(midID, []byte(sender.rtpTransceiver.Mid())))
  1727  			assert.NoError(t, vp8WriterC.WriteRTP(pkt))
  1728  
  1729  			seqNo++
  1730  		}
  1731  	}()
  1732  
  1733  	wg.Wait()
  1734  
  1735  	closePairNow(t, pcSender, pcReceiver)
  1736  }
  1737  
  1738  // Check that PayloadType of 0 is handled correctly. At one point
  1739  // we incorrectly assumed 0 meant an invalid stream and wouldn't update things
  1740  // properly
  1741  func TestPeerConnection_Zero_PayloadType(t *testing.T) {
  1742  	lim := test.TimeOut(time.Second * 5)
  1743  	defer lim.Stop()
  1744  
  1745  	report := test.CheckRoutines(t)
  1746  	defer report()
  1747  
  1748  	pcOffer, pcAnswer, err := newPair()
  1749  	require.NoError(t, err)
  1750  
  1751  	audioTrack, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypePCMU}, "audio", "audio")
  1752  	require.NoError(t, err)
  1753  
  1754  	_, err = pcOffer.AddTrack(audioTrack)
  1755  	require.NoError(t, err)
  1756  
  1757  	assert.NoError(t, signalPair(pcOffer, pcAnswer))
  1758  
  1759  	trackFired := make(chan struct{})
  1760  
  1761  	pcAnswer.OnTrack(func(track *TrackRemote, _ *RTPReceiver) {
  1762  		require.Equal(t, track.Codec().MimeType, MimeTypePCMU)
  1763  		close(trackFired)
  1764  	})
  1765  
  1766  	func() {
  1767  		ticker := time.NewTicker(20 * time.Millisecond)
  1768  		defer ticker.Stop()
  1769  
  1770  		for {
  1771  			select {
  1772  			case <-trackFired:
  1773  				return
  1774  			case <-ticker.C:
  1775  				if routineErr := audioTrack.WriteSample(media.Sample{Data: []byte{0x00}, Duration: time.Second}); routineErr != nil {
  1776  					fmt.Println(routineErr)
  1777  				}
  1778  			}
  1779  		}
  1780  	}()
  1781  
  1782  	closePairNow(t, pcOffer, pcAnswer)
  1783  }
  1784  
  1785  // Assert that NACKs work E2E with no extra configuration. If media is sent over a lossy connection
  1786  // the user gets retransmitted RTP packets with no extra configuration
  1787  func Test_PeerConnection_RTX_E2E(t *testing.T) {
  1788  	defer test.TimeOut(time.Second * 30).Stop()
  1789  
  1790  	pcOffer, pcAnswer, wan := createVNetPair(t, nil)
  1791  
  1792  	wan.AddChunkFilter(func(vnet.Chunk) bool {
  1793  		return rand.Intn(5) != 4 //nolint: gosec
  1794  	})
  1795  
  1796  	track, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "track-id", "stream-id")
  1797  	assert.NoError(t, err)
  1798  
  1799  	rtpSender, err := pcOffer.AddTrack(track)
  1800  	assert.NoError(t, err)
  1801  
  1802  	go func() {
  1803  		rtcpBuf := make([]byte, 1500)
  1804  		for {
  1805  			if _, _, rtcpErr := rtpSender.Read(rtcpBuf); rtcpErr != nil {
  1806  				return
  1807  			}
  1808  		}
  1809  	}()
  1810  
  1811  	rtxSsrc := rtpSender.GetParameters().Encodings[0].RTX.SSRC
  1812  	ssrc := rtpSender.GetParameters().Encodings[0].SSRC
  1813  
  1814  	rtxRead, rtxReadCancel := context.WithCancel(context.Background())
  1815  	pcAnswer.OnTrack(func(track *TrackRemote, _ *RTPReceiver) {
  1816  		for {
  1817  			pkt, attributes, readRTPErr := track.ReadRTP()
  1818  			if errors.Is(readRTPErr, io.EOF) {
  1819  				return
  1820  			} else if pkt.PayloadType == 0 {
  1821  				continue
  1822  			}
  1823  
  1824  			assert.NotNil(t, pkt)
  1825  			assert.Equal(t, pkt.SSRC, uint32(ssrc))
  1826  			assert.Equal(t, pkt.PayloadType, uint8(96))
  1827  
  1828  			rtxPayloadType := attributes.Get(AttributeRtxPayloadType)
  1829  			rtxSequenceNumber := attributes.Get(AttributeRtxSequenceNumber)
  1830  			rtxSSRC := attributes.Get(AttributeRtxSsrc)
  1831  			if rtxPayloadType != nil && rtxSequenceNumber != nil && rtxSSRC != nil {
  1832  				assert.Equal(t, rtxPayloadType, uint8(97))
  1833  				assert.Equal(t, rtxSSRC, uint32(rtxSsrc))
  1834  
  1835  				rtxReadCancel()
  1836  			}
  1837  		}
  1838  	})
  1839  
  1840  	assert.NoError(t, signalPair(pcOffer, pcAnswer))
  1841  
  1842  	func() {
  1843  		for {
  1844  			select {
  1845  			case <-time.After(20 * time.Millisecond):
  1846  				writeErr := track.WriteSample(media.Sample{Data: []byte{0x00}, Duration: time.Second})
  1847  				assert.NoError(t, writeErr)
  1848  			case <-rtxRead.Done():
  1849  				return
  1850  			}
  1851  		}
  1852  	}()
  1853  
  1854  	assert.NoError(t, wan.Stop())
  1855  	closePairNow(t, pcOffer, pcAnswer)
  1856  }