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 }