github.com/pion/webrtc/v4@v4.0.1/rtpsender_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  	"context"
    11  	"errors"
    12  	"io"
    13  	"sync/atomic"
    14  	"testing"
    15  	"time"
    16  
    17  	"github.com/pion/interceptor"
    18  	"github.com/pion/transport/v3/test"
    19  	"github.com/pion/webrtc/v4/pkg/media"
    20  	"github.com/stretchr/testify/assert"
    21  )
    22  
    23  func Test_RTPSender_ReplaceTrack(t *testing.T) {
    24  	lim := test.TimeOut(time.Second * 10)
    25  	defer lim.Stop()
    26  
    27  	report := test.CheckRoutines(t)
    28  	defer report()
    29  
    30  	s := SettingEngine{}
    31  	s.DisableSRTPReplayProtection(true)
    32  
    33  	sender, receiver, err := NewAPI(WithSettingEngine(s)).newPair(Configuration{})
    34  	assert.NoError(t, err)
    35  
    36  	trackA, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion")
    37  	assert.NoError(t, err)
    38  
    39  	trackB, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeH264}, "video", "pion")
    40  	assert.NoError(t, err)
    41  
    42  	rtpSender, err := sender.AddTrack(trackA)
    43  	assert.NoError(t, err)
    44  
    45  	seenPacketA, seenPacketACancel := context.WithCancel(context.Background())
    46  	seenPacketB, seenPacketBCancel := context.WithCancel(context.Background())
    47  
    48  	var onTrackCount uint64
    49  	receiver.OnTrack(func(track *TrackRemote, _ *RTPReceiver) {
    50  		assert.Equal(t, uint64(1), atomic.AddUint64(&onTrackCount, 1))
    51  
    52  		for {
    53  			pkt, _, err := track.ReadRTP()
    54  			if err != nil {
    55  				assert.True(t, errors.Is(err, io.EOF))
    56  				return
    57  			}
    58  
    59  			switch {
    60  			case pkt.Payload[len(pkt.Payload)-1] == 0xAA:
    61  				assert.Equal(t, track.Codec().MimeType, MimeTypeVP8)
    62  				seenPacketACancel()
    63  			case pkt.Payload[len(pkt.Payload)-1] == 0xBB:
    64  				assert.Equal(t, track.Codec().MimeType, MimeTypeH264)
    65  				seenPacketBCancel()
    66  			default:
    67  				t.Fatalf("Unexpected RTP Data % 02x", pkt.Payload[len(pkt.Payload)-1])
    68  			}
    69  		}
    70  	})
    71  
    72  	assert.NoError(t, signalPair(sender, receiver))
    73  
    74  	// Block Until packet with 0xAA has been seen
    75  	func() {
    76  		for range time.Tick(time.Millisecond * 20) {
    77  			select {
    78  			case <-seenPacketA.Done():
    79  				return
    80  			default:
    81  				assert.NoError(t, trackA.WriteSample(media.Sample{Data: []byte{0xAA}, Duration: time.Second}))
    82  			}
    83  		}
    84  	}()
    85  
    86  	assert.NoError(t, rtpSender.ReplaceTrack(trackB))
    87  
    88  	// Block Until packet with 0xBB has been seen
    89  	func() {
    90  		for range time.Tick(time.Millisecond * 20) {
    91  			select {
    92  			case <-seenPacketB.Done():
    93  				return
    94  			default:
    95  				assert.NoError(t, trackB.WriteSample(media.Sample{Data: []byte{0xBB}, Duration: time.Second}))
    96  			}
    97  		}
    98  	}()
    99  
   100  	closePairNow(t, sender, receiver)
   101  }
   102  
   103  func Test_RTPSender_GetParameters(t *testing.T) {
   104  	lim := test.TimeOut(time.Second * 10)
   105  	defer lim.Stop()
   106  
   107  	report := test.CheckRoutines(t)
   108  	defer report()
   109  
   110  	offerer, answerer, err := newPair()
   111  	assert.NoError(t, err)
   112  
   113  	rtpTransceiver, err := offerer.AddTransceiverFromKind(RTPCodecTypeVideo)
   114  	assert.NoError(t, err)
   115  
   116  	assert.NoError(t, signalPair(offerer, answerer))
   117  
   118  	parameters := rtpTransceiver.Sender().GetParameters()
   119  	assert.NotEqual(t, 0, len(parameters.Codecs))
   120  	assert.Equal(t, 1, len(parameters.Encodings))
   121  	assert.Equal(t, rtpTransceiver.Sender().trackEncodings[0].ssrc, parameters.Encodings[0].SSRC)
   122  	assert.Equal(t, "", parameters.Encodings[0].RID)
   123  
   124  	closePairNow(t, offerer, answerer)
   125  }
   126  
   127  func Test_RTPSender_GetParameters_WithRID(t *testing.T) {
   128  	lim := test.TimeOut(time.Second * 10)
   129  	defer lim.Stop()
   130  
   131  	report := test.CheckRoutines(t)
   132  	defer report()
   133  
   134  	offerer, answerer, err := newPair()
   135  	assert.NoError(t, err)
   136  
   137  	rtpTransceiver, err := offerer.AddTransceiverFromKind(RTPCodecTypeVideo)
   138  	assert.NoError(t, err)
   139  
   140  	assert.NoError(t, signalPair(offerer, answerer))
   141  
   142  	track, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion", WithRTPStreamID("moo"))
   143  	assert.NoError(t, err)
   144  
   145  	err = rtpTransceiver.setSendingTrack(track)
   146  	assert.NoError(t, err)
   147  
   148  	parameters := rtpTransceiver.Sender().GetParameters()
   149  	assert.Equal(t, track.RID(), parameters.Encodings[0].RID)
   150  
   151  	closePairNow(t, offerer, answerer)
   152  }
   153  
   154  func Test_RTPSender_SetReadDeadline(t *testing.T) {
   155  	lim := test.TimeOut(time.Second * 30)
   156  	defer lim.Stop()
   157  
   158  	report := test.CheckRoutines(t)
   159  	defer report()
   160  
   161  	sender, receiver, wan := createVNetPair(t, &interceptor.Registry{})
   162  
   163  	track, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion")
   164  	assert.NoError(t, err)
   165  
   166  	rtpSender, err := sender.AddTrack(track)
   167  	assert.NoError(t, err)
   168  
   169  	peerConnectionsConnected := untilConnectionState(PeerConnectionStateConnected, sender, receiver)
   170  
   171  	assert.NoError(t, signalPair(sender, receiver))
   172  
   173  	peerConnectionsConnected.Wait()
   174  
   175  	assert.NoError(t, rtpSender.SetReadDeadline(time.Now().Add(1*time.Second)))
   176  	_, _, err = rtpSender.ReadRTCP()
   177  	assert.Error(t, err)
   178  
   179  	assert.NoError(t, wan.Stop())
   180  	closePairNow(t, sender, receiver)
   181  }
   182  
   183  func Test_RTPSender_ReplaceTrack_InvalidTrackKindChange(t *testing.T) {
   184  	lim := test.TimeOut(time.Second * 10)
   185  	defer lim.Stop()
   186  
   187  	report := test.CheckRoutines(t)
   188  	defer report()
   189  
   190  	sender, receiver, err := newPair()
   191  	assert.NoError(t, err)
   192  
   193  	trackA, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion")
   194  	assert.NoError(t, err)
   195  
   196  	trackB, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeOpus}, "audio", "pion")
   197  	assert.NoError(t, err)
   198  
   199  	rtpSender, err := sender.AddTrack(trackA)
   200  	assert.NoError(t, err)
   201  
   202  	assert.NoError(t, signalPair(sender, receiver))
   203  
   204  	seenPacket, seenPacketCancel := context.WithCancel(context.Background())
   205  	receiver.OnTrack(func(_ *TrackRemote, _ *RTPReceiver) {
   206  		seenPacketCancel()
   207  	})
   208  
   209  	func() {
   210  		for range time.Tick(time.Millisecond * 20) {
   211  			select {
   212  			case <-seenPacket.Done():
   213  				return
   214  			default:
   215  				assert.NoError(t, trackA.WriteSample(media.Sample{Data: []byte{0xAA}, Duration: time.Second}))
   216  			}
   217  		}
   218  	}()
   219  
   220  	assert.True(t, errors.Is(rtpSender.ReplaceTrack(trackB), ErrRTPSenderNewTrackHasIncorrectKind))
   221  
   222  	closePairNow(t, sender, receiver)
   223  }
   224  
   225  func Test_RTPSender_ReplaceTrack_InvalidCodecChange(t *testing.T) {
   226  	lim := test.TimeOut(time.Second * 10)
   227  	defer lim.Stop()
   228  
   229  	report := test.CheckRoutines(t)
   230  	defer report()
   231  
   232  	sender, receiver, err := newPair()
   233  	assert.NoError(t, err)
   234  
   235  	trackA, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion")
   236  	assert.NoError(t, err)
   237  
   238  	trackB, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP9}, "video", "pion")
   239  	assert.NoError(t, err)
   240  
   241  	rtpSender, err := sender.AddTrack(trackA)
   242  	assert.NoError(t, err)
   243  
   244  	err = rtpSender.rtpTransceiver.SetCodecPreferences([]RTPCodecParameters{{
   245  		RTPCodecCapability: RTPCodecCapability{MimeType: MimeTypeVP8},
   246  		PayloadType:        96,
   247  	}})
   248  	assert.NoError(t, err)
   249  
   250  	assert.NoError(t, signalPair(sender, receiver))
   251  
   252  	seenPacket, seenPacketCancel := context.WithCancel(context.Background())
   253  	receiver.OnTrack(func(_ *TrackRemote, _ *RTPReceiver) {
   254  		seenPacketCancel()
   255  	})
   256  
   257  	func() {
   258  		for range time.Tick(time.Millisecond * 20) {
   259  			select {
   260  			case <-seenPacket.Done():
   261  				return
   262  			default:
   263  				assert.NoError(t, trackA.WriteSample(media.Sample{Data: []byte{0xAA}, Duration: time.Second}))
   264  			}
   265  		}
   266  	}()
   267  
   268  	assert.True(t, errors.Is(rtpSender.ReplaceTrack(trackB), ErrUnsupportedCodec))
   269  
   270  	closePairNow(t, sender, receiver)
   271  }
   272  
   273  func Test_RTPSender_GetParameters_NilTrack(t *testing.T) {
   274  	track, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion")
   275  	assert.NoError(t, err)
   276  
   277  	peerConnection, err := NewPeerConnection(Configuration{})
   278  	assert.NoError(t, err)
   279  
   280  	rtpSender, err := peerConnection.AddTrack(track)
   281  	assert.NoError(t, err)
   282  
   283  	assert.NoError(t, rtpSender.ReplaceTrack(nil))
   284  	rtpSender.GetParameters()
   285  
   286  	assert.NoError(t, peerConnection.Close())
   287  }
   288  
   289  func Test_RTPSender_Send(t *testing.T) {
   290  	track, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion")
   291  	assert.NoError(t, err)
   292  
   293  	peerConnection, err := NewPeerConnection(Configuration{})
   294  	assert.NoError(t, err)
   295  
   296  	rtpSender, err := peerConnection.AddTrack(track)
   297  	assert.NoError(t, err)
   298  
   299  	parameter := rtpSender.GetParameters()
   300  	err = rtpSender.Send(parameter)
   301  	<-rtpSender.sendCalled
   302  	assert.NoError(t, err)
   303  
   304  	assert.NoError(t, peerConnection.Close())
   305  }
   306  
   307  func Test_RTPSender_Send_Called_Once(t *testing.T) {
   308  	track, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion")
   309  	assert.NoError(t, err)
   310  
   311  	peerConnection, err := NewPeerConnection(Configuration{})
   312  	assert.NoError(t, err)
   313  
   314  	rtpSender, err := peerConnection.AddTrack(track)
   315  	assert.NoError(t, err)
   316  
   317  	parameter := rtpSender.GetParameters()
   318  	err = rtpSender.Send(parameter)
   319  	<-rtpSender.sendCalled
   320  	assert.NoError(t, err)
   321  
   322  	err = rtpSender.Send(parameter)
   323  	assert.Equal(t, errRTPSenderSendAlreadyCalled, err)
   324  
   325  	assert.NoError(t, peerConnection.Close())
   326  }
   327  
   328  func Test_RTPSender_Send_Track_Removed(t *testing.T) {
   329  	track, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion")
   330  	assert.NoError(t, err)
   331  
   332  	peerConnection, err := NewPeerConnection(Configuration{})
   333  	assert.NoError(t, err)
   334  
   335  	rtpSender, err := peerConnection.AddTrack(track)
   336  	assert.NoError(t, err)
   337  
   338  	parameter := rtpSender.GetParameters()
   339  	assert.NoError(t, peerConnection.RemoveTrack(rtpSender))
   340  	assert.Equal(t, errRTPSenderTrackRemoved, rtpSender.Send(parameter))
   341  
   342  	assert.NoError(t, peerConnection.Close())
   343  }
   344  
   345  func Test_RTPSender_Add_Encoding(t *testing.T) {
   346  	track, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion")
   347  	assert.NoError(t, err)
   348  
   349  	peerConnection, err := NewPeerConnection(Configuration{})
   350  	assert.NoError(t, err)
   351  
   352  	rtpSender, err := peerConnection.AddTrack(track)
   353  	assert.NoError(t, err)
   354  
   355  	assert.Equal(t, errRTPSenderTrackNil, rtpSender.AddEncoding(nil))
   356  
   357  	track1, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion")
   358  	assert.NoError(t, err)
   359  	assert.Equal(t, errRTPSenderRidNil, rtpSender.AddEncoding(track1))
   360  
   361  	track1, err = NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion", WithRTPStreamID("h"))
   362  	assert.NoError(t, err)
   363  	assert.Equal(t, errRTPSenderNoBaseEncoding, rtpSender.AddEncoding(track1))
   364  
   365  	track, err = NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion", WithRTPStreamID("q"))
   366  	assert.NoError(t, err)
   367  
   368  	rtpSender, err = peerConnection.AddTrack(track)
   369  	assert.NoError(t, err)
   370  
   371  	track1, err = NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "video1", "pion", WithRTPStreamID("h"))
   372  	assert.NoError(t, err)
   373  	assert.Equal(t, errRTPSenderBaseEncodingMismatch, rtpSender.AddEncoding(track1))
   374  
   375  	track1, err = NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion1", WithRTPStreamID("h"))
   376  	assert.NoError(t, err)
   377  	assert.Equal(t, errRTPSenderBaseEncodingMismatch, rtpSender.AddEncoding(track1))
   378  
   379  	track1, err = NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeOpus}, "video", "pion", WithRTPStreamID("h"))
   380  	assert.NoError(t, err)
   381  	assert.Equal(t, errRTPSenderBaseEncodingMismatch, rtpSender.AddEncoding(track1))
   382  
   383  	track1, err = NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion", WithRTPStreamID("q"))
   384  	assert.NoError(t, err)
   385  	assert.Equal(t, errRTPSenderRIDCollision, rtpSender.AddEncoding(track1))
   386  
   387  	track1, err = NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion", WithRTPStreamID("h"))
   388  	assert.NoError(t, err)
   389  	assert.NoError(t, rtpSender.AddEncoding(track1))
   390  
   391  	err = rtpSender.Send(rtpSender.GetParameters())
   392  	assert.NoError(t, err)
   393  
   394  	track1, err = NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion", WithRTPStreamID("f"))
   395  	assert.NoError(t, err)
   396  	assert.Equal(t, errRTPSenderSendAlreadyCalled, rtpSender.AddEncoding(track1))
   397  
   398  	err = rtpSender.Stop()
   399  	assert.NoError(t, err)
   400  
   401  	assert.Equal(t, errRTPSenderStopped, rtpSender.AddEncoding(track1))
   402  
   403  	assert.NoError(t, peerConnection.Close())
   404  }
   405  
   406  // nolint: dupl
   407  func Test_RTPSender_FEC_Support(t *testing.T) {
   408  	t.Run("FEC disabled by default", func(t *testing.T) {
   409  		track, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion")
   410  		assert.NoError(t, err)
   411  
   412  		peerConnection, err := NewPeerConnection(Configuration{})
   413  		assert.NoError(t, err)
   414  
   415  		rtpSender, err := peerConnection.AddTrack(track)
   416  		assert.NoError(t, err)
   417  
   418  		assert.Zero(t, rtpSender.GetParameters().Encodings[0].FEC.SSRC)
   419  		assert.NoError(t, peerConnection.Close())
   420  	})
   421  
   422  	t.Run("FEC can be enabled", func(t *testing.T) {
   423  		m := MediaEngine{}
   424  		assert.NoError(t, m.RegisterCodec(RTPCodecParameters{
   425  			RTPCodecCapability: RTPCodecCapability{MimeTypeVP8, 90000, 0, "", nil},
   426  			PayloadType:        94,
   427  		}, RTPCodecTypeVideo))
   428  		assert.NoError(t, m.RegisterCodec(RTPCodecParameters{
   429  			RTPCodecCapability: RTPCodecCapability{MimeTypeFlexFEC, 90000, 0, "", nil},
   430  			PayloadType:        95,
   431  		}, RTPCodecTypeVideo))
   432  
   433  		api := NewAPI(WithMediaEngine(&m))
   434  
   435  		track, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion")
   436  		assert.NoError(t, err)
   437  
   438  		peerConnection, err := api.NewPeerConnection(Configuration{})
   439  		assert.NoError(t, err)
   440  
   441  		rtpSender, err := peerConnection.AddTrack(track)
   442  		assert.NoError(t, err)
   443  
   444  		assert.NotZero(t, rtpSender.GetParameters().Encodings[0].FEC.SSRC)
   445  		assert.NoError(t, peerConnection.Close())
   446  	})
   447  }
   448  
   449  // nolint: dupl
   450  func Test_RTPSender_RTX_Support(t *testing.T) {
   451  	t.Run("RTX SSRC by Default", func(t *testing.T) {
   452  		track, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion")
   453  		assert.NoError(t, err)
   454  
   455  		peerConnection, err := NewPeerConnection(Configuration{})
   456  		assert.NoError(t, err)
   457  
   458  		rtpSender, err := peerConnection.AddTrack(track)
   459  		assert.NoError(t, err)
   460  
   461  		assert.NotZero(t, rtpSender.GetParameters().Encodings[0].RTX.SSRC)
   462  		assert.NoError(t, peerConnection.Close())
   463  	})
   464  
   465  	t.Run("RTX can be disabled", func(t *testing.T) {
   466  		m := MediaEngine{}
   467  		assert.NoError(t, m.RegisterCodec(RTPCodecParameters{
   468  			RTPCodecCapability: RTPCodecCapability{MimeTypeVP8, 90000, 0, "", nil},
   469  			PayloadType:        94,
   470  		}, RTPCodecTypeVideo))
   471  		api := NewAPI(WithMediaEngine(&m))
   472  
   473  		track, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion")
   474  		assert.NoError(t, err)
   475  
   476  		peerConnection, err := api.NewPeerConnection(Configuration{})
   477  		assert.NoError(t, err)
   478  
   479  		rtpSender, err := peerConnection.AddTrack(track)
   480  		assert.NoError(t, err)
   481  
   482  		assert.Zero(t, rtpSender.GetParameters().Encodings[0].RTX.SSRC)
   483  
   484  		assert.NoError(t, peerConnection.Close())
   485  	})
   486  }