github.com/pion/webrtc/v4@v4.0.1/track_local_static_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  	"testing"
    13  	"time"
    14  
    15  	"github.com/pion/rtp"
    16  	"github.com/pion/transport/v3/test"
    17  	"github.com/stretchr/testify/assert"
    18  )
    19  
    20  // If a remote doesn't support a Codec used by a `TrackLocalStatic`
    21  // an error should be returned to the user
    22  func Test_TrackLocalStatic_NoCodecIntersection(t *testing.T) {
    23  	lim := test.TimeOut(time.Second * 30)
    24  	defer lim.Stop()
    25  
    26  	report := test.CheckRoutines(t)
    27  	defer report()
    28  
    29  	track, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion")
    30  	assert.NoError(t, err)
    31  
    32  	t.Run("Offerer", func(t *testing.T) {
    33  		pc, err := NewPeerConnection(Configuration{})
    34  		assert.NoError(t, err)
    35  
    36  		noCodecPC, err := NewAPI(WithMediaEngine(&MediaEngine{})).NewPeerConnection(Configuration{})
    37  		assert.NoError(t, err)
    38  
    39  		_, err = pc.AddTrack(track)
    40  		assert.NoError(t, err)
    41  
    42  		assert.ErrorIs(t, signalPair(pc, noCodecPC), ErrUnsupportedCodec)
    43  
    44  		closePairNow(t, noCodecPC, pc)
    45  	})
    46  
    47  	t.Run("Answerer", func(t *testing.T) {
    48  		pc, err := NewPeerConnection(Configuration{})
    49  		assert.NoError(t, err)
    50  
    51  		m := &MediaEngine{}
    52  		assert.NoError(t, m.RegisterCodec(RTPCodecParameters{
    53  			RTPCodecCapability: RTPCodecCapability{MimeType: "video/VP9", ClockRate: 90000, Channels: 0, SDPFmtpLine: "", RTCPFeedback: nil},
    54  			PayloadType:        96,
    55  		}, RTPCodecTypeVideo))
    56  
    57  		vp9OnlyPC, err := NewAPI(WithMediaEngine(m)).NewPeerConnection(Configuration{})
    58  		assert.NoError(t, err)
    59  
    60  		_, err = vp9OnlyPC.AddTransceiverFromKind(RTPCodecTypeVideo)
    61  		assert.NoError(t, err)
    62  
    63  		_, err = pc.AddTrack(track)
    64  		assert.NoError(t, err)
    65  
    66  		assert.True(t, errors.Is(signalPair(vp9OnlyPC, pc), ErrUnsupportedCodec))
    67  
    68  		closePairNow(t, vp9OnlyPC, pc)
    69  	})
    70  
    71  	t.Run("Local", func(t *testing.T) {
    72  		offerer, answerer, err := newPair()
    73  		assert.NoError(t, err)
    74  
    75  		invalidCodecTrack, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: "video/invalid-codec"}, "video", "pion")
    76  		assert.NoError(t, err)
    77  
    78  		_, err = offerer.AddTrack(invalidCodecTrack)
    79  		assert.NoError(t, err)
    80  
    81  		assert.True(t, errors.Is(signalPair(offerer, answerer), ErrUnsupportedCodec))
    82  		closePairNow(t, offerer, answerer)
    83  	})
    84  }
    85  
    86  // Assert that Bind/Unbind happens when expected
    87  func Test_TrackLocalStatic_Closed(t *testing.T) {
    88  	lim := test.TimeOut(time.Second * 30)
    89  	defer lim.Stop()
    90  
    91  	report := test.CheckRoutines(t)
    92  	defer report()
    93  
    94  	pcOffer, pcAnswer, err := newPair()
    95  	assert.NoError(t, err)
    96  
    97  	_, err = pcAnswer.AddTransceiverFromKind(RTPCodecTypeVideo)
    98  	assert.NoError(t, err)
    99  
   100  	vp8Writer, err := NewTrackLocalStaticRTP(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion")
   101  	assert.NoError(t, err)
   102  
   103  	_, err = pcOffer.AddTrack(vp8Writer)
   104  	assert.NoError(t, err)
   105  
   106  	assert.Equal(t, len(vp8Writer.bindings), 0, "No binding should exist before signaling")
   107  
   108  	assert.NoError(t, signalPair(pcOffer, pcAnswer))
   109  
   110  	assert.Equal(t, len(vp8Writer.bindings), 1, "binding should exist after signaling")
   111  
   112  	closePairNow(t, pcOffer, pcAnswer)
   113  
   114  	assert.Equal(t, len(vp8Writer.bindings), 0, "No binding should exist after close")
   115  }
   116  
   117  func Test_TrackLocalStatic_PayloadType(t *testing.T) {
   118  	lim := test.TimeOut(time.Second * 30)
   119  	defer lim.Stop()
   120  
   121  	report := test.CheckRoutines(t)
   122  	defer report()
   123  
   124  	mediaEngineOne := &MediaEngine{}
   125  	assert.NoError(t, mediaEngineOne.RegisterCodec(RTPCodecParameters{
   126  		RTPCodecCapability: RTPCodecCapability{MimeType: "video/VP8", ClockRate: 90000, Channels: 0, SDPFmtpLine: "", RTCPFeedback: nil},
   127  		PayloadType:        100,
   128  	}, RTPCodecTypeVideo))
   129  
   130  	mediaEngineTwo := &MediaEngine{}
   131  	assert.NoError(t, mediaEngineTwo.RegisterCodec(RTPCodecParameters{
   132  		RTPCodecCapability: RTPCodecCapability{MimeType: "video/VP8", ClockRate: 90000, Channels: 0, SDPFmtpLine: "", RTCPFeedback: nil},
   133  		PayloadType:        200,
   134  	}, RTPCodecTypeVideo))
   135  
   136  	offerer, err := NewAPI(WithMediaEngine(mediaEngineOne)).NewPeerConnection(Configuration{})
   137  	assert.NoError(t, err)
   138  
   139  	answerer, err := NewAPI(WithMediaEngine(mediaEngineTwo)).NewPeerConnection(Configuration{})
   140  	assert.NoError(t, err)
   141  
   142  	track, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion")
   143  	assert.NoError(t, err)
   144  
   145  	_, err = offerer.AddTransceiverFromKind(RTPCodecTypeVideo)
   146  	assert.NoError(t, err)
   147  
   148  	_, err = answerer.AddTrack(track)
   149  	assert.NoError(t, err)
   150  
   151  	onTrackFired, onTrackFiredFunc := context.WithCancel(context.Background())
   152  	offerer.OnTrack(func(track *TrackRemote, _ *RTPReceiver) {
   153  		assert.Equal(t, track.PayloadType(), PayloadType(100))
   154  		assert.Equal(t, track.Codec().RTPCodecCapability.MimeType, "video/VP8")
   155  
   156  		onTrackFiredFunc()
   157  	})
   158  
   159  	assert.NoError(t, signalPair(offerer, answerer))
   160  
   161  	sendVideoUntilDone(onTrackFired.Done(), t, []*TrackLocalStaticSample{track})
   162  
   163  	closePairNow(t, offerer, answerer)
   164  }
   165  
   166  // Assert that writing to a Track doesn't modify the input
   167  // Even though we can pass a pointer we shouldn't modify the incoming value
   168  func Test_TrackLocalStatic_Mutate_Input(t *testing.T) {
   169  	lim := test.TimeOut(time.Second * 30)
   170  	defer lim.Stop()
   171  
   172  	report := test.CheckRoutines(t)
   173  	defer report()
   174  
   175  	pcOffer, pcAnswer, err := newPair()
   176  	assert.NoError(t, err)
   177  
   178  	vp8Writer, err := NewTrackLocalStaticRTP(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion")
   179  	assert.NoError(t, err)
   180  
   181  	_, err = pcOffer.AddTrack(vp8Writer)
   182  	assert.NoError(t, err)
   183  
   184  	assert.NoError(t, signalPair(pcOffer, pcAnswer))
   185  
   186  	pkt := &rtp.Packet{Header: rtp.Header{SSRC: 1, PayloadType: 1}}
   187  	assert.NoError(t, vp8Writer.WriteRTP(pkt))
   188  
   189  	assert.Equal(t, pkt.Header.SSRC, uint32(1))
   190  	assert.Equal(t, pkt.Header.PayloadType, uint8(1))
   191  
   192  	closePairNow(t, pcOffer, pcAnswer)
   193  }
   194  
   195  // Assert that writing to a Track that has Binded (but not connected)
   196  // does not block
   197  func Test_TrackLocalStatic_Binding_NonBlocking(t *testing.T) {
   198  	lim := test.TimeOut(time.Second * 5)
   199  	defer lim.Stop()
   200  
   201  	report := test.CheckRoutines(t)
   202  	defer report()
   203  
   204  	pcOffer, pcAnswer, err := newPair()
   205  	assert.NoError(t, err)
   206  
   207  	_, err = pcOffer.AddTransceiverFromKind(RTPCodecTypeVideo)
   208  	assert.NoError(t, err)
   209  
   210  	vp8Writer, err := NewTrackLocalStaticRTP(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion")
   211  	assert.NoError(t, err)
   212  
   213  	_, err = pcAnswer.AddTrack(vp8Writer)
   214  	assert.NoError(t, err)
   215  
   216  	offer, err := pcOffer.CreateOffer(nil)
   217  	assert.NoError(t, err)
   218  
   219  	assert.NoError(t, pcAnswer.SetRemoteDescription(offer))
   220  
   221  	answer, err := pcAnswer.CreateAnswer(nil)
   222  	assert.NoError(t, err)
   223  	assert.NoError(t, pcAnswer.SetLocalDescription(answer))
   224  
   225  	_, err = vp8Writer.Write(make([]byte, 20))
   226  	assert.NoError(t, err)
   227  
   228  	closePairNow(t, pcOffer, pcAnswer)
   229  }
   230  
   231  func BenchmarkTrackLocalWrite(b *testing.B) {
   232  	offerPC, answerPC, err := newPair()
   233  	defer closePairNow(b, offerPC, answerPC)
   234  	if err != nil {
   235  		b.Fatalf("Failed to create a PC pair for testing")
   236  	}
   237  
   238  	track, err := NewTrackLocalStaticRTP(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion")
   239  	assert.NoError(b, err)
   240  
   241  	_, err = offerPC.AddTrack(track)
   242  	assert.NoError(b, err)
   243  
   244  	_, err = answerPC.AddTransceiverFromKind(RTPCodecTypeVideo)
   245  	assert.NoError(b, err)
   246  
   247  	b.SetBytes(1024)
   248  
   249  	buf := make([]byte, 1024)
   250  	for i := 0; i < b.N; i++ {
   251  		_, err := track.Write(buf)
   252  		assert.NoError(b, err)
   253  	}
   254  }
   255  
   256  func Test_TrackLocalStatic_Padding(t *testing.T) {
   257  	mediaEngineOne := &MediaEngine{}
   258  	assert.NoError(t, mediaEngineOne.RegisterCodec(RTPCodecParameters{
   259  		RTPCodecCapability: RTPCodecCapability{MimeType: "video/VP8", ClockRate: 90000, Channels: 0, SDPFmtpLine: "", RTCPFeedback: nil},
   260  		PayloadType:        100,
   261  	}, RTPCodecTypeVideo))
   262  
   263  	mediaEngineTwo := &MediaEngine{}
   264  	assert.NoError(t, mediaEngineTwo.RegisterCodec(RTPCodecParameters{
   265  		RTPCodecCapability: RTPCodecCapability{MimeType: "video/VP8", ClockRate: 90000, Channels: 0, SDPFmtpLine: "", RTCPFeedback: nil},
   266  		PayloadType:        200,
   267  	}, RTPCodecTypeVideo))
   268  
   269  	offerer, err := NewAPI(WithMediaEngine(mediaEngineOne)).NewPeerConnection(Configuration{})
   270  	assert.NoError(t, err)
   271  
   272  	answerer, err := NewAPI(WithMediaEngine(mediaEngineTwo)).NewPeerConnection(Configuration{})
   273  	assert.NoError(t, err)
   274  
   275  	track, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion")
   276  	assert.NoError(t, err)
   277  
   278  	_, err = offerer.AddTransceiverFromKind(RTPCodecTypeVideo)
   279  	assert.NoError(t, err)
   280  
   281  	_, err = answerer.AddTrack(track)
   282  	assert.NoError(t, err)
   283  
   284  	onTrackFired, onTrackFiredFunc := context.WithCancel(context.Background())
   285  
   286  	offerer.OnTrack(func(track *TrackRemote, _ *RTPReceiver) {
   287  		assert.Equal(t, track.PayloadType(), PayloadType(100))
   288  		assert.Equal(t, track.Codec().RTPCodecCapability.MimeType, "video/VP8")
   289  
   290  		for i := 0; i < 20; i++ {
   291  			// Padding payload
   292  			p, _, e := track.ReadRTP()
   293  			assert.NoError(t, e)
   294  			assert.True(t, p.Padding)
   295  			assert.Equal(t, p.PaddingSize, byte(255))
   296  		}
   297  
   298  		onTrackFiredFunc()
   299  	})
   300  
   301  	assert.NoError(t, signalPair(offerer, answerer))
   302  
   303  	exit := false
   304  
   305  	for !exit {
   306  		select {
   307  		case <-time.After(1 * time.Millisecond):
   308  			assert.NoError(t, track.GeneratePadding(1))
   309  		case <-onTrackFired.Done():
   310  			exit = true
   311  		}
   312  	}
   313  
   314  	closePairNow(t, offerer, answerer)
   315  }
   316  
   317  func Test_TrackLocalStatic_RTX(t *testing.T) {
   318  	defer test.TimeOut(time.Second * 30).Stop()
   319  	defer test.CheckRoutines(t)()
   320  
   321  	offerer, answerer, err := newPair()
   322  	assert.NoError(t, err)
   323  
   324  	track, err := NewTrackLocalStaticRTP(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion")
   325  	assert.NoError(t, err)
   326  
   327  	_, err = offerer.AddTrack(track)
   328  	assert.NoError(t, err)
   329  
   330  	assert.NoError(t, signalPair(offerer, answerer))
   331  
   332  	track.mu.Lock()
   333  	assert.NotZero(t, track.bindings[0].ssrcRTX)
   334  	assert.NotZero(t, track.bindings[0].payloadTypeRTX)
   335  	track.mu.Unlock()
   336  
   337  	closePairNow(t, offerer, answerer)
   338  }