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 }