github.com/pion/webrtc/v4@v4.0.1/peerconnection_renegotiation_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 "context" 12 "errors" 13 "io" 14 "strconv" 15 "strings" 16 "sync" 17 "sync/atomic" 18 "testing" 19 "time" 20 21 "github.com/pion/rtp" 22 "github.com/pion/transport/v3/test" 23 "github.com/pion/webrtc/v4/internal/util" 24 "github.com/pion/webrtc/v4/pkg/media" 25 "github.com/pion/webrtc/v4/pkg/rtcerr" 26 "github.com/stretchr/testify/assert" 27 "github.com/stretchr/testify/require" 28 ) 29 30 func sendVideoUntilDone(done <-chan struct{}, t *testing.T, tracks []*TrackLocalStaticSample) { 31 for { 32 select { 33 case <-time.After(20 * time.Millisecond): 34 for _, track := range tracks { 35 assert.NoError(t, track.WriteSample(media.Sample{Data: []byte{0x00}, Duration: time.Second})) 36 } 37 case <-done: 38 return 39 } 40 } 41 } 42 43 func sdpMidHasSsrc(offer SessionDescription, mid string, ssrc SSRC) bool { 44 for _, media := range offer.parsed.MediaDescriptions { 45 cmid, ok := media.Attribute("mid") 46 if !ok { 47 continue 48 } 49 if cmid != mid { 50 continue 51 } 52 cssrc, ok := media.Attribute("ssrc") 53 if !ok { 54 continue 55 } 56 parts := strings.Split(cssrc, " ") 57 58 ssrcInt64, err := strconv.ParseUint(parts[0], 10, 32) 59 if err != nil { 60 continue 61 } 62 63 if uint32(ssrcInt64) == uint32(ssrc) { 64 return true 65 } 66 } 67 return false 68 } 69 70 func TestPeerConnection_Renegotiation_AddRecvonlyTransceiver(t *testing.T) { 71 type testCase struct { 72 name string 73 answererSends bool 74 } 75 76 testCases := []testCase{ 77 // Assert the following behaviors: 78 // - Offerer can add a recvonly transceiver 79 // - During negotiation, answerer peer adds an inactive (or sendonly) transceiver 80 // - Offerer can add a track 81 // - Answerer can receive the RTP packets. 82 {"add recvonly, then receive from answerer", false}, 83 // Assert the following behaviors: 84 // - Offerer can add a recvonly transceiver 85 // - During negotiation, answerer peer adds an inactive (or sendonly) transceiver 86 // - Answerer can add a track to the existing sendonly transceiver 87 // - Offerer can receive the RTP packets. 88 {"add recvonly, then send to answerer", true}, 89 } 90 91 for _, tc := range testCases { 92 tc := tc 93 t.Run(tc.name, func(t *testing.T) { 94 lim := test.TimeOut(time.Second * 30) 95 defer lim.Stop() 96 97 report := test.CheckRoutines(t) 98 defer report() 99 100 pcOffer, pcAnswer, err := newPair() 101 if err != nil { 102 t.Fatal(err) 103 } 104 105 _, err = pcOffer.AddTransceiverFromKind( 106 RTPCodecTypeVideo, 107 RTPTransceiverInit{ 108 Direction: RTPTransceiverDirectionRecvonly, 109 }, 110 ) 111 assert.NoError(t, err) 112 113 assert.NoError(t, signalPair(pcOffer, pcAnswer)) 114 115 localTrack, err := NewTrackLocalStaticSample( 116 RTPCodecCapability{MimeType: "video/VP8"}, "track-one", "stream-one", 117 ) 118 require.NoError(t, err) 119 120 if tc.answererSends { 121 _, err = pcAnswer.AddTrack(localTrack) 122 } else { 123 _, err = pcOffer.AddTrack(localTrack) 124 } 125 126 require.NoError(t, err) 127 128 onTrackFired, onTrackFiredFunc := context.WithCancel(context.Background()) 129 130 if tc.answererSends { 131 pcOffer.OnTrack(func(*TrackRemote, *RTPReceiver) { 132 onTrackFiredFunc() 133 }) 134 assert.NoError(t, signalPair(pcAnswer, pcOffer)) 135 } else { 136 pcAnswer.OnTrack(func(*TrackRemote, *RTPReceiver) { 137 onTrackFiredFunc() 138 }) 139 assert.NoError(t, signalPair(pcOffer, pcAnswer)) 140 } 141 142 sendVideoUntilDone(onTrackFired.Done(), t, []*TrackLocalStaticSample{localTrack}) 143 144 closePairNow(t, pcOffer, pcAnswer) 145 }) 146 } 147 } 148 149 /* 150 * Assert the following behaviors 151 * - We are able to call AddTrack after signaling 152 * - OnTrack is NOT called on the other side until after SetRemoteDescription 153 * - We are able to re-negotiate and AddTrack is properly called 154 */ 155 func TestPeerConnection_Renegotiation_AddTrack(t *testing.T) { 156 lim := test.TimeOut(time.Second * 30) 157 defer lim.Stop() 158 159 report := test.CheckRoutines(t) 160 defer report() 161 162 pcOffer, pcAnswer, err := newPair() 163 if err != nil { 164 t.Fatal(err) 165 } 166 167 haveRenegotiated := &atomicBool{} 168 onTrackFired, onTrackFiredFunc := context.WithCancel(context.Background()) 169 pcAnswer.OnTrack(func(*TrackRemote, *RTPReceiver) { 170 if !haveRenegotiated.get() { 171 t.Fatal("OnTrack was called before renegotiation") 172 } 173 onTrackFiredFunc() 174 }) 175 176 assert.NoError(t, signalPair(pcOffer, pcAnswer)) 177 178 _, err = pcAnswer.AddTransceiverFromKind(RTPCodecTypeVideo, RTPTransceiverInit{Direction: RTPTransceiverDirectionRecvonly}) 179 assert.NoError(t, err) 180 181 vp8Track, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "foo", "bar") 182 assert.NoError(t, err) 183 184 sender, err := pcOffer.AddTrack(vp8Track) 185 assert.NoError(t, err) 186 187 // Send 10 packets, OnTrack MUST not be fired 188 for i := 0; i <= 10; i++ { 189 assert.NoError(t, vp8Track.WriteSample(media.Sample{Data: []byte{0x00}, Duration: time.Second})) 190 time.Sleep(20 * time.Millisecond) 191 } 192 193 haveRenegotiated.set(true) 194 assert.False(t, sender.isNegotiated()) 195 offer, err := pcOffer.CreateOffer(nil) 196 assert.True(t, sender.isNegotiated()) 197 assert.NoError(t, err) 198 assert.NoError(t, pcOffer.SetLocalDescription(offer)) 199 assert.NoError(t, pcAnswer.SetRemoteDescription(offer)) 200 answer, err := pcAnswer.CreateAnswer(nil) 201 assert.NoError(t, err) 202 203 assert.NoError(t, pcAnswer.SetLocalDescription(answer)) 204 205 pcOffer.ops.Done() 206 assert.Equal(t, 0, len(vp8Track.rtpTrack.bindings)) 207 208 assert.NoError(t, pcOffer.SetRemoteDescription(answer)) 209 210 pcOffer.ops.Done() 211 assert.Equal(t, 1, len(vp8Track.rtpTrack.bindings)) 212 213 sendVideoUntilDone(onTrackFired.Done(), t, []*TrackLocalStaticSample{vp8Track}) 214 215 closePairNow(t, pcOffer, pcAnswer) 216 } 217 218 // Assert that adding tracks across multiple renegotiations performs as expected 219 func TestPeerConnection_Renegotiation_AddTrack_Multiple(t *testing.T) { 220 addTrackWithLabel := func(trackID string, pcOffer, pcAnswer *PeerConnection) *TrackLocalStaticSample { 221 _, err := pcAnswer.AddTransceiverFromKind(RTPCodecTypeVideo, RTPTransceiverInit{Direction: RTPTransceiverDirectionRecvonly}) 222 assert.NoError(t, err) 223 224 track, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, trackID, trackID) 225 assert.NoError(t, err) 226 227 _, err = pcOffer.AddTrack(track) 228 assert.NoError(t, err) 229 230 return track 231 } 232 233 trackIDs := []string{util.MathRandAlpha(16), util.MathRandAlpha(16), util.MathRandAlpha(16)} 234 outboundTracks := []*TrackLocalStaticSample{} 235 onTrackCount := map[string]int{} 236 onTrackChan := make(chan struct{}, 1) 237 238 lim := test.TimeOut(time.Second * 30) 239 defer lim.Stop() 240 241 report := test.CheckRoutines(t) 242 defer report() 243 244 pcOffer, pcAnswer, err := newPair() 245 if err != nil { 246 t.Fatal(err) 247 } 248 249 pcAnswer.OnTrack(func(track *TrackRemote, _ *RTPReceiver) { 250 onTrackCount[track.ID()]++ 251 onTrackChan <- struct{}{} 252 }) 253 254 assert.NoError(t, signalPair(pcOffer, pcAnswer)) 255 256 for i := range trackIDs { 257 outboundTracks = append(outboundTracks, addTrackWithLabel(trackIDs[i], pcOffer, pcAnswer)) 258 assert.NoError(t, signalPair(pcOffer, pcAnswer)) 259 sendVideoUntilDone(onTrackChan, t, outboundTracks) 260 } 261 262 closePairNow(t, pcOffer, pcAnswer) 263 264 assert.Equal(t, onTrackCount[trackIDs[0]], 1) 265 assert.Equal(t, onTrackCount[trackIDs[1]], 1) 266 assert.Equal(t, onTrackCount[trackIDs[2]], 1) 267 } 268 269 // Assert that renegotiation triggers OnTrack() with correct ID and label from 270 // remote side, even when a transceiver was added before the actual track data 271 // was received. This happens when we add a transceiver on the server, create 272 // an offer on the server and the browser's answer contains the same SSRC, but 273 // a track hasn't been added on the browser side yet. The browser can add a 274 // track later and renegotiate, and track ID and label will be set by the time 275 // first packets are received. 276 func TestPeerConnection_Renegotiation_AddTrack_Rename(t *testing.T) { 277 lim := test.TimeOut(time.Second * 30) 278 defer lim.Stop() 279 280 report := test.CheckRoutines(t) 281 defer report() 282 283 pcOffer, pcAnswer, err := newPair() 284 if err != nil { 285 t.Fatal(err) 286 } 287 288 haveRenegotiated := &atomicBool{} 289 onTrackFired, onTrackFiredFunc := context.WithCancel(context.Background()) 290 var atomicRemoteTrack atomic.Value 291 pcOffer.OnTrack(func(track *TrackRemote, _ *RTPReceiver) { 292 if !haveRenegotiated.get() { 293 t.Fatal("OnTrack was called before renegotiation") 294 } 295 onTrackFiredFunc() 296 atomicRemoteTrack.Store(track) 297 }) 298 299 _, err = pcOffer.AddTransceiverFromKind(RTPCodecTypeVideo, RTPTransceiverInit{Direction: RTPTransceiverDirectionRecvonly}) 300 assert.NoError(t, err) 301 vp8Track, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "foo1", "bar1") 302 assert.NoError(t, err) 303 _, err = pcAnswer.AddTrack(vp8Track) 304 assert.NoError(t, err) 305 306 assert.NoError(t, signalPair(pcOffer, pcAnswer)) 307 308 vp8Track.rtpTrack.id = "foo2" 309 vp8Track.rtpTrack.streamID = "bar2" 310 311 haveRenegotiated.set(true) 312 assert.NoError(t, signalPair(pcOffer, pcAnswer)) 313 314 sendVideoUntilDone(onTrackFired.Done(), t, []*TrackLocalStaticSample{vp8Track}) 315 316 closePairNow(t, pcOffer, pcAnswer) 317 318 remoteTrack, ok := atomicRemoteTrack.Load().(*TrackRemote) 319 require.True(t, ok) 320 require.NotNil(t, remoteTrack) 321 assert.Equal(t, "foo2", remoteTrack.ID()) 322 assert.Equal(t, "bar2", remoteTrack.StreamID()) 323 } 324 325 // TestPeerConnection_Transceiver_Mid tests that we'll provide the same 326 // transceiver for a media id on successive offer/answer 327 func TestPeerConnection_Transceiver_Mid(t *testing.T) { 328 lim := test.TimeOut(time.Second * 30) 329 defer lim.Stop() 330 331 report := test.CheckRoutines(t) 332 defer report() 333 334 pcOffer, err := NewPeerConnection(Configuration{}) 335 assert.NoError(t, err) 336 337 pcAnswer, err := NewPeerConnection(Configuration{}) 338 assert.NoError(t, err) 339 340 track1, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion1") 341 require.NoError(t, err) 342 343 sender1, err := pcOffer.AddTrack(track1) 344 require.NoError(t, err) 345 346 track2, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion2") 347 require.NoError(t, err) 348 349 sender2, err := pcOffer.AddTrack(track2) 350 require.NoError(t, err) 351 352 // this will create the initial offer using generateUnmatchedSDP 353 offer, err := pcOffer.CreateOffer(nil) 354 assert.NoError(t, err) 355 356 offerGatheringComplete := GatheringCompletePromise(pcOffer) 357 assert.NoError(t, pcOffer.SetLocalDescription(offer)) 358 <-offerGatheringComplete 359 360 assert.NoError(t, pcAnswer.SetRemoteDescription(*pcOffer.LocalDescription())) 361 362 answer, err := pcAnswer.CreateAnswer(nil) 363 assert.NoError(t, err) 364 365 answerGatheringComplete := GatheringCompletePromise(pcAnswer) 366 assert.NoError(t, pcAnswer.SetLocalDescription(answer)) 367 <-answerGatheringComplete 368 369 // apply answer so we'll test generateMatchedSDP 370 assert.NoError(t, pcOffer.SetRemoteDescription(*pcAnswer.LocalDescription())) 371 372 pcOffer.ops.Done() 373 pcAnswer.ops.Done() 374 375 // Must have 3 media descriptions (2 video channels) 376 assert.Equal(t, len(offer.parsed.MediaDescriptions), 2) 377 378 assert.True(t, sdpMidHasSsrc(offer, "0", sender1.trackEncodings[0].ssrc), "Expected mid %q with ssrc %d, offer.SDP: %s", "0", sender1.trackEncodings[0].ssrc, offer.SDP) 379 380 // Remove first track, must keep same number of media 381 // descriptions and same track ssrc for mid 1 as previous 382 assert.NoError(t, pcOffer.RemoveTrack(sender1)) 383 384 offer, err = pcOffer.CreateOffer(nil) 385 assert.NoError(t, err) 386 assert.NoError(t, pcOffer.SetLocalDescription(offer)) 387 388 assert.Equal(t, len(offer.parsed.MediaDescriptions), 2) 389 390 assert.True(t, sdpMidHasSsrc(offer, "1", sender2.trackEncodings[0].ssrc), "Expected mid %q with ssrc %d, offer.SDP: %s", "1", sender2.trackEncodings[0].ssrc, offer.SDP) 391 392 _, err = pcAnswer.CreateAnswer(nil) 393 assert.Equal(t, err, &rtcerr.InvalidStateError{Err: ErrIncorrectSignalingState}) 394 395 pcOffer.ops.Done() 396 pcAnswer.ops.Done() 397 398 assert.NoError(t, pcAnswer.SetRemoteDescription(offer)) 399 answer, err = pcAnswer.CreateAnswer(nil) 400 assert.NoError(t, err) 401 assert.NoError(t, pcOffer.SetRemoteDescription(answer)) 402 403 track3, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion3") 404 require.NoError(t, err) 405 406 sender3, err := pcOffer.AddTrack(track3) 407 require.NoError(t, err) 408 409 offer, err = pcOffer.CreateOffer(nil) 410 assert.NoError(t, err) 411 412 // We reuse the existing non-sending transceiver 413 assert.Equal(t, len(offer.parsed.MediaDescriptions), 2) 414 415 assert.True(t, sdpMidHasSsrc(offer, "0", sender3.trackEncodings[0].ssrc), "Expected mid %q with ssrc %d, offer.sdp: %s", "0", sender3.trackEncodings[0].ssrc, offer.SDP) 416 assert.True(t, sdpMidHasSsrc(offer, "1", sender2.trackEncodings[0].ssrc), "Expected mid %q with ssrc %d, offer.sdp: %s", "1", sender2.trackEncodings[0].ssrc, offer.SDP) 417 418 closePairNow(t, pcOffer, pcAnswer) 419 } 420 421 func TestPeerConnection_Renegotiation_CodecChange(t *testing.T) { 422 lim := test.TimeOut(time.Second * 30) 423 defer lim.Stop() 424 425 report := test.CheckRoutines(t) 426 defer report() 427 428 pcOffer, err := NewPeerConnection(Configuration{}) 429 assert.NoError(t, err) 430 431 pcAnswer, err := NewPeerConnection(Configuration{}) 432 assert.NoError(t, err) 433 434 track1, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "video1", "pion1") 435 require.NoError(t, err) 436 437 track2, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "video2", "pion2") 438 require.NoError(t, err) 439 440 sender1, err := pcOffer.AddTrack(track1) 441 require.NoError(t, err) 442 443 _, err = pcAnswer.AddTransceiverFromKind(RTPCodecTypeVideo, RTPTransceiverInit{Direction: RTPTransceiverDirectionRecvonly}) 444 require.NoError(t, err) 445 446 tracksCh := make(chan *TrackRemote) 447 tracksClosed := make(chan struct{}) 448 pcAnswer.OnTrack(func(track *TrackRemote, _ *RTPReceiver) { 449 tracksCh <- track 450 for { 451 if _, _, readErr := track.ReadRTP(); errors.Is(readErr, io.EOF) { 452 tracksClosed <- struct{}{} 453 return 454 } 455 } 456 }) 457 458 err = signalPair(pcOffer, pcAnswer) 459 require.NoError(t, err) 460 461 transceivers := pcOffer.GetTransceivers() 462 require.Equal(t, 1, len(transceivers)) 463 require.Equal(t, "0", transceivers[0].Mid()) 464 465 transceivers = pcAnswer.GetTransceivers() 466 require.Equal(t, 1, len(transceivers)) 467 require.Equal(t, "0", transceivers[0].Mid()) 468 469 ctx, cancel := context.WithCancel(context.Background()) 470 go sendVideoUntilDone(ctx.Done(), t, []*TrackLocalStaticSample{track1}) 471 472 remoteTrack1 := <-tracksCh 473 cancel() 474 475 assert.Equal(t, "video1", remoteTrack1.ID()) 476 assert.Equal(t, "pion1", remoteTrack1.StreamID()) 477 478 require.NoError(t, pcOffer.RemoveTrack(sender1)) 479 480 require.NoError(t, signalPair(pcOffer, pcAnswer)) 481 <-tracksClosed 482 483 sender2, err := pcOffer.AddTrack(track2) 484 require.NoError(t, err) 485 require.NoError(t, signalPair(pcOffer, pcAnswer)) 486 transceivers = pcOffer.GetTransceivers() 487 require.Equal(t, 1, len(transceivers)) 488 require.Equal(t, "0", transceivers[0].Mid()) 489 490 transceivers = pcAnswer.GetTransceivers() 491 require.Equal(t, 1, len(transceivers)) 492 require.Equal(t, "0", transceivers[0].Mid()) 493 494 ctx, cancel = context.WithCancel(context.Background()) 495 go sendVideoUntilDone(ctx.Done(), t, []*TrackLocalStaticSample{track2}) 496 497 remoteTrack2 := <-tracksCh 498 cancel() 499 500 require.NoError(t, pcOffer.RemoveTrack(sender2)) 501 502 err = signalPair(pcOffer, pcAnswer) 503 require.NoError(t, err) 504 <-tracksClosed 505 506 assert.Equal(t, "video2", remoteTrack2.ID()) 507 assert.Equal(t, "pion2", remoteTrack2.StreamID()) 508 509 closePairNow(t, pcOffer, pcAnswer) 510 } 511 512 func TestPeerConnection_Renegotiation_RemoveTrack(t *testing.T) { 513 lim := test.TimeOut(time.Second * 30) 514 defer lim.Stop() 515 516 report := test.CheckRoutines(t) 517 defer report() 518 519 pcOffer, pcAnswer, err := newPair() 520 if err != nil { 521 t.Fatal(err) 522 } 523 524 _, err = pcAnswer.AddTransceiverFromKind(RTPCodecTypeVideo, RTPTransceiverInit{Direction: RTPTransceiverDirectionRecvonly}) 525 assert.NoError(t, err) 526 527 vp8Track, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "foo", "bar") 528 assert.NoError(t, err) 529 530 sender, err := pcOffer.AddTrack(vp8Track) 531 assert.NoError(t, err) 532 533 onTrackFired, onTrackFiredFunc := context.WithCancel(context.Background()) 534 trackClosed, trackClosedFunc := context.WithCancel(context.Background()) 535 536 pcAnswer.OnTrack(func(track *TrackRemote, _ *RTPReceiver) { 537 onTrackFiredFunc() 538 539 for { 540 if _, _, err := track.ReadRTP(); errors.Is(err, io.EOF) { 541 trackClosedFunc() 542 return 543 } 544 } 545 }) 546 547 assert.NoError(t, signalPair(pcOffer, pcAnswer)) 548 sendVideoUntilDone(onTrackFired.Done(), t, []*TrackLocalStaticSample{vp8Track}) 549 550 assert.NoError(t, pcOffer.RemoveTrack(sender)) 551 assert.NoError(t, signalPair(pcOffer, pcAnswer)) 552 553 <-trackClosed.Done() 554 closePairNow(t, pcOffer, pcAnswer) 555 } 556 557 func TestPeerConnection_RoleSwitch(t *testing.T) { 558 lim := test.TimeOut(time.Second * 30) 559 defer lim.Stop() 560 561 report := test.CheckRoutines(t) 562 defer report() 563 564 pcFirstOfferer, pcSecondOfferer, err := newPair() 565 if err != nil { 566 t.Fatal(err) 567 } 568 569 onTrackFired, onTrackFiredFunc := context.WithCancel(context.Background()) 570 pcFirstOfferer.OnTrack(func(*TrackRemote, *RTPReceiver) { 571 onTrackFiredFunc() 572 }) 573 574 assert.NoError(t, signalPair(pcFirstOfferer, pcSecondOfferer)) 575 576 // Add a new Track to the second offerer 577 // This asserts that it will match the ordering of the last RemoteDescription, but then also add new Transceivers to the end 578 _, err = pcFirstOfferer.AddTransceiverFromKind(RTPCodecTypeVideo, RTPTransceiverInit{Direction: RTPTransceiverDirectionRecvonly}) 579 assert.NoError(t, err) 580 581 vp8Track, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "foo", "bar") 582 assert.NoError(t, err) 583 584 _, err = pcSecondOfferer.AddTrack(vp8Track) 585 assert.NoError(t, err) 586 587 assert.NoError(t, signalPair(pcSecondOfferer, pcFirstOfferer)) 588 sendVideoUntilDone(onTrackFired.Done(), t, []*TrackLocalStaticSample{vp8Track}) 589 590 closePairNow(t, pcFirstOfferer, pcSecondOfferer) 591 } 592 593 // Assert that renegotiation doesn't attempt to gather ICE twice 594 // Before we would attempt to gather multiple times and would put 595 // the PeerConnection into a broken state 596 func TestPeerConnection_Renegotiation_Trickle(t *testing.T) { 597 lim := test.TimeOut(time.Second * 30) 598 defer lim.Stop() 599 600 report := test.CheckRoutines(t) 601 defer report() 602 603 settingEngine := SettingEngine{} 604 605 api := NewAPI(WithSettingEngine(settingEngine)) 606 607 // Invalid STUN server on purpose, will stop ICE Gathering from completing in time 608 pcOffer, pcAnswer, err := api.newPair(Configuration{ 609 ICEServers: []ICEServer{ 610 { 611 URLs: []string{"stun:127.0.0.1:5000"}, 612 }, 613 }, 614 }) 615 if err != nil { 616 t.Fatal(err) 617 } 618 619 _, err = pcOffer.CreateDataChannel("test-channel", nil) 620 assert.NoError(t, err) 621 622 var wg sync.WaitGroup 623 wg.Add(2) 624 pcOffer.OnICECandidate(func(c *ICECandidate) { 625 if c != nil { 626 assert.NoError(t, pcAnswer.AddICECandidate(c.ToJSON())) 627 } else { 628 wg.Done() 629 } 630 }) 631 pcAnswer.OnICECandidate(func(c *ICECandidate) { 632 if c != nil { 633 assert.NoError(t, pcOffer.AddICECandidate(c.ToJSON())) 634 } else { 635 wg.Done() 636 } 637 }) 638 639 negotiate := func() { 640 offer, err := pcOffer.CreateOffer(nil) 641 assert.NoError(t, err) 642 643 assert.NoError(t, pcAnswer.SetRemoteDescription(offer)) 644 assert.NoError(t, pcOffer.SetLocalDescription(offer)) 645 646 answer, err := pcAnswer.CreateAnswer(nil) 647 assert.NoError(t, err) 648 649 assert.NoError(t, pcOffer.SetRemoteDescription(answer)) 650 assert.NoError(t, pcAnswer.SetLocalDescription(answer)) 651 } 652 negotiate() 653 negotiate() 654 655 pcOffer.ops.Done() 656 pcAnswer.ops.Done() 657 wg.Wait() 658 659 closePairNow(t, pcOffer, pcAnswer) 660 } 661 662 func TestPeerConnection_Renegotiation_SetLocalDescription(t *testing.T) { 663 lim := test.TimeOut(time.Second * 30) 664 defer lim.Stop() 665 666 report := test.CheckRoutines(t) 667 defer report() 668 669 pcOffer, pcAnswer, err := newPair() 670 if err != nil { 671 t.Fatal(err) 672 } 673 674 onTrackFired, onTrackFiredFunc := context.WithCancel(context.Background()) 675 pcOffer.OnTrack(func(*TrackRemote, *RTPReceiver) { 676 onTrackFiredFunc() 677 }) 678 679 assert.NoError(t, signalPair(pcOffer, pcAnswer)) 680 681 pcOffer.ops.Done() 682 pcAnswer.ops.Done() 683 684 _, err = pcOffer.AddTransceiverFromKind(RTPCodecTypeVideo, RTPTransceiverInit{Direction: RTPTransceiverDirectionRecvonly}) 685 assert.NoError(t, err) 686 687 localTrack, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "foo", "bar") 688 assert.NoError(t, err) 689 690 sender, err := pcAnswer.AddTrack(localTrack) 691 assert.NoError(t, err) 692 693 offer, err := pcOffer.CreateOffer(nil) 694 assert.NoError(t, err) 695 assert.NoError(t, pcOffer.SetLocalDescription(offer)) 696 assert.NoError(t, pcAnswer.SetRemoteDescription(offer)) 697 assert.False(t, sender.isNegotiated()) 698 answer, err := pcAnswer.CreateAnswer(nil) 699 assert.NoError(t, err) 700 assert.True(t, sender.isNegotiated()) 701 702 pcAnswer.ops.Done() 703 assert.Equal(t, 0, len(localTrack.rtpTrack.bindings)) 704 705 assert.NoError(t, pcAnswer.SetLocalDescription(answer)) 706 707 pcAnswer.ops.Done() 708 assert.Equal(t, 1, len(localTrack.rtpTrack.bindings)) 709 710 assert.NoError(t, pcOffer.SetRemoteDescription(answer)) 711 712 sendVideoUntilDone(onTrackFired.Done(), t, []*TrackLocalStaticSample{localTrack}) 713 714 closePairNow(t, pcOffer, pcAnswer) 715 } 716 717 // Issue #346, don't start the SCTP Subsystem if the RemoteDescription doesn't contain one 718 // Before we would always start it, and re-negotiations would fail because SCTP was in flight 719 func TestPeerConnection_Renegotiation_NoApplication(t *testing.T) { 720 signalPairExcludeDataChannel := func(pcOffer, pcAnswer *PeerConnection) { 721 offer, err := pcOffer.CreateOffer(nil) 722 assert.NoError(t, err) 723 offerGatheringComplete := GatheringCompletePromise(pcOffer) 724 assert.NoError(t, pcOffer.SetLocalDescription(offer)) 725 <-offerGatheringComplete 726 727 assert.NoError(t, pcAnswer.SetRemoteDescription(*pcOffer.LocalDescription())) 728 729 answer, err := pcAnswer.CreateAnswer(nil) 730 assert.NoError(t, err) 731 732 answerGatheringComplete := GatheringCompletePromise(pcAnswer) 733 assert.NoError(t, pcAnswer.SetLocalDescription(answer)) 734 <-answerGatheringComplete 735 736 assert.NoError(t, pcOffer.SetRemoteDescription(*pcAnswer.LocalDescription())) 737 } 738 739 lim := test.TimeOut(time.Second * 30) 740 defer lim.Stop() 741 742 report := test.CheckRoutines(t) 743 defer report() 744 745 pcOffer, pcAnswer, err := newPair() 746 if err != nil { 747 t.Fatal(err) 748 } 749 750 pcOfferConnected, pcOfferConnectedCancel := context.WithCancel(context.Background()) 751 pcOffer.OnICEConnectionStateChange(func(i ICEConnectionState) { 752 if i == ICEConnectionStateConnected { 753 pcOfferConnectedCancel() 754 } 755 }) 756 757 pcAnswerConnected, pcAnswerConnectedCancel := context.WithCancel(context.Background()) 758 pcAnswer.OnICEConnectionStateChange(func(i ICEConnectionState) { 759 if i == ICEConnectionStateConnected { 760 pcAnswerConnectedCancel() 761 } 762 }) 763 764 _, err = pcOffer.AddTransceiverFromKind(RTPCodecTypeVideo, RTPTransceiverInit{Direction: RTPTransceiverDirectionSendrecv}) 765 assert.NoError(t, err) 766 767 _, err = pcAnswer.AddTransceiverFromKind(RTPCodecTypeVideo, RTPTransceiverInit{Direction: RTPTransceiverDirectionSendrecv}) 768 assert.NoError(t, err) 769 770 signalPairExcludeDataChannel(pcOffer, pcAnswer) 771 pcOffer.ops.Done() 772 pcAnswer.ops.Done() 773 774 signalPairExcludeDataChannel(pcOffer, pcAnswer) 775 pcOffer.ops.Done() 776 pcAnswer.ops.Done() 777 778 <-pcAnswerConnected.Done() 779 <-pcOfferConnected.Done() 780 781 assert.Equal(t, pcOffer.SCTP().State(), SCTPTransportStateConnecting) 782 assert.Equal(t, pcAnswer.SCTP().State(), SCTPTransportStateConnecting) 783 784 closePairNow(t, pcOffer, pcAnswer) 785 } 786 787 func TestAddDataChannelDuringRenegotiation(t *testing.T) { 788 lim := test.TimeOut(time.Second * 10) 789 defer lim.Stop() 790 791 report := test.CheckRoutines(t) 792 defer report() 793 794 pcOffer, err := NewPeerConnection(Configuration{}) 795 assert.NoError(t, err) 796 797 pcAnswer, err := NewPeerConnection(Configuration{}) 798 assert.NoError(t, err) 799 800 track, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion") 801 assert.NoError(t, err) 802 803 _, err = pcOffer.AddTrack(track) 804 assert.NoError(t, err) 805 806 offer, err := pcOffer.CreateOffer(nil) 807 assert.NoError(t, err) 808 809 offerGatheringComplete := GatheringCompletePromise(pcOffer) 810 assert.NoError(t, pcOffer.SetLocalDescription(offer)) 811 <-offerGatheringComplete 812 813 assert.NoError(t, pcAnswer.SetRemoteDescription(*pcOffer.LocalDescription())) 814 815 answer, err := pcAnswer.CreateAnswer(nil) 816 assert.NoError(t, err) 817 818 answerGatheringComplete := GatheringCompletePromise(pcAnswer) 819 assert.NoError(t, pcAnswer.SetLocalDescription(answer)) 820 <-answerGatheringComplete 821 822 assert.NoError(t, pcOffer.SetRemoteDescription(*pcAnswer.LocalDescription())) 823 824 _, err = pcOffer.CreateDataChannel("data-channel", nil) 825 assert.NoError(t, err) 826 827 // Assert that DataChannel is in offer now 828 offer, err = pcOffer.CreateOffer(nil) 829 assert.NoError(t, err) 830 831 applicationMediaSectionCount := 0 832 for _, d := range offer.parsed.MediaDescriptions { 833 if d.MediaName.Media == mediaSectionApplication { 834 applicationMediaSectionCount++ 835 } 836 } 837 assert.Equal(t, applicationMediaSectionCount, 1) 838 839 onDataChannelFired, onDataChannelFiredFunc := context.WithCancel(context.Background()) 840 pcAnswer.OnDataChannel(func(*DataChannel) { 841 onDataChannelFiredFunc() 842 }) 843 844 assert.NoError(t, signalPair(pcOffer, pcAnswer)) 845 846 <-onDataChannelFired.Done() 847 closePairNow(t, pcOffer, pcAnswer) 848 } 849 850 // Assert that CreateDataChannel fires OnNegotiationNeeded 851 func TestNegotiationCreateDataChannel(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 pc, err := NewPeerConnection(Configuration{}) 859 assert.NoError(t, err) 860 861 var wg sync.WaitGroup 862 wg.Add(1) 863 864 pc.OnNegotiationNeeded(func() { 865 defer func() { 866 wg.Done() 867 }() 868 }) 869 870 // Create DataChannel, wait until OnNegotiationNeeded is fired 871 if _, err = pc.CreateDataChannel("testChannel", nil); err != nil { 872 t.Error(err.Error()) 873 } 874 875 // Wait until OnNegotiationNeeded is fired 876 wg.Wait() 877 assert.NoError(t, pc.Close()) 878 } 879 880 func TestNegotiationNeededRemoveTrack(t *testing.T) { 881 var wg sync.WaitGroup 882 wg.Add(1) 883 884 report := test.CheckRoutines(t) 885 defer report() 886 887 pcOffer, err := NewPeerConnection(Configuration{}) 888 assert.NoError(t, err) 889 pcAnswer, err := NewPeerConnection(Configuration{}) 890 assert.NoError(t, err) 891 892 track, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion") 893 assert.NoError(t, err) 894 895 pcOffer.OnNegotiationNeeded(func() { 896 wg.Add(1) 897 offer, createOfferErr := pcOffer.CreateOffer(nil) 898 assert.NoError(t, createOfferErr) 899 900 offerGatheringComplete := GatheringCompletePromise(pcOffer) 901 assert.NoError(t, pcOffer.SetLocalDescription(offer)) 902 903 <-offerGatheringComplete 904 assert.NoError(t, pcAnswer.SetRemoteDescription(*pcOffer.LocalDescription())) 905 906 answer, createAnswerErr := pcAnswer.CreateAnswer(nil) 907 assert.NoError(t, createAnswerErr) 908 909 answerGatheringComplete := GatheringCompletePromise(pcAnswer) 910 assert.NoError(t, pcAnswer.SetLocalDescription(answer)) 911 912 <-answerGatheringComplete 913 assert.NoError(t, pcOffer.SetRemoteDescription(*pcAnswer.LocalDescription())) 914 wg.Done() 915 wg.Done() 916 }) 917 918 sender, err := pcOffer.AddTrack(track) 919 assert.NoError(t, err) 920 921 assert.NoError(t, track.WriteSample(media.Sample{Data: []byte{0x00}, Duration: time.Second})) 922 923 wg.Wait() 924 925 wg.Add(1) 926 assert.NoError(t, pcOffer.RemoveTrack(sender)) 927 928 wg.Wait() 929 930 closePairNow(t, pcOffer, pcAnswer) 931 } 932 933 func TestNegotiationNeededStressOneSided(t *testing.T) { 934 lim := test.TimeOut(time.Second * 30) 935 defer lim.Stop() 936 937 report := test.CheckRoutines(t) 938 defer report() 939 940 pcA, pcB, err := newPair() 941 assert.NoError(t, err) 942 943 const expectedTrackCount = 500 944 ctx, done := context.WithCancel(context.Background()) 945 pcA.OnNegotiationNeeded(func() { 946 count := len(pcA.GetTransceivers()) 947 assert.NoError(t, signalPair(pcA, pcB)) 948 if count == expectedTrackCount { 949 done() 950 } 951 }) 952 953 for i := 0; i < expectedTrackCount; i++ { 954 track, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion") 955 assert.NoError(t, err) 956 957 _, err = pcA.AddTrack(track) 958 assert.NoError(t, err) 959 } 960 <-ctx.Done() 961 assert.Equal(t, expectedTrackCount, len(pcB.GetTransceivers())) 962 closePairNow(t, pcA, pcB) 963 } 964 965 // TestPeerConnection_Renegotiation_DisableTrack asserts that if a remote track is set inactive 966 // that locally it goes inactive as well 967 func TestPeerConnection_Renegotiation_DisableTrack(t *testing.T) { 968 lim := test.TimeOut(time.Second * 30) 969 defer lim.Stop() 970 971 report := test.CheckRoutines(t) 972 defer report() 973 974 pcOffer, pcAnswer, err := newPair() 975 assert.NoError(t, err) 976 977 // Create two transceivers 978 _, err = pcOffer.AddTransceiverFromKind(RTPCodecTypeVideo) 979 assert.NoError(t, err) 980 981 transceiver, err := pcOffer.AddTransceiverFromKind(RTPCodecTypeVideo) 982 assert.NoError(t, err) 983 984 assert.NoError(t, signalPair(pcOffer, pcAnswer)) 985 986 // Assert we have three active transceivers 987 offer, err := pcOffer.CreateOffer(nil) 988 assert.NoError(t, err) 989 assert.Equal(t, strings.Count(offer.SDP, "a=sendrecv"), 3) 990 991 // Assert we have two active transceivers, one inactive 992 assert.NoError(t, transceiver.Stop()) 993 offer, err = pcOffer.CreateOffer(nil) 994 assert.NoError(t, err) 995 assert.Equal(t, strings.Count(offer.SDP, "a=sendrecv"), 2) 996 assert.Equal(t, strings.Count(offer.SDP, "a=inactive"), 1) 997 998 // Assert that the offer disabled one of our transceivers 999 assert.NoError(t, pcAnswer.SetRemoteDescription(offer)) 1000 answer, err := pcAnswer.CreateAnswer(nil) 1001 assert.NoError(t, err) 1002 assert.Equal(t, strings.Count(answer.SDP, "a=sendrecv"), 1) // DataChannel 1003 assert.Equal(t, strings.Count(answer.SDP, "a=recvonly"), 1) 1004 assert.Equal(t, strings.Count(answer.SDP, "a=inactive"), 1) 1005 1006 closePairNow(t, pcOffer, pcAnswer) 1007 } 1008 1009 func TestPeerConnection_Renegotiation_Simulcast(t *testing.T) { 1010 lim := test.TimeOut(time.Second * 30) 1011 defer lim.Stop() 1012 1013 report := test.CheckRoutines(t) 1014 defer report() 1015 1016 originalRids := []string{"a", "b", "c"} 1017 signalWithRids := func(sessionDescription string, rids []string) string { 1018 sessionDescription = strings.SplitAfter(sessionDescription, "a=end-of-candidates\r\n")[0] 1019 sessionDescription = filterSsrc(sessionDescription) 1020 for _, rid := range rids { 1021 sessionDescription += "a=" + sdpAttributeRid + ":" + rid + " send\r\n" 1022 } 1023 return sessionDescription + "a=simulcast:send " + strings.Join(rids, ";") + "\r\n" 1024 } 1025 1026 var trackMapLock sync.RWMutex 1027 trackMap := map[string]*TrackRemote{} 1028 1029 onTrackHandler := func(track *TrackRemote, _ *RTPReceiver) { 1030 trackMapLock.Lock() 1031 defer trackMapLock.Unlock() 1032 trackMap[track.RID()] = track 1033 } 1034 1035 sendUntilAllTracksFired := func(vp8Writer *TrackLocalStaticRTP, rids []string) { 1036 allTracksFired := func() bool { 1037 trackMapLock.Lock() 1038 defer trackMapLock.Unlock() 1039 1040 return len(trackMap) == len(rids) 1041 } 1042 1043 for sequenceNumber := uint16(0); !allTracksFired(); sequenceNumber++ { 1044 time.Sleep(20 * time.Millisecond) 1045 1046 for ssrc, rid := range rids { 1047 header := &rtp.Header{ 1048 Version: 2, 1049 SSRC: uint32(ssrc + 1), 1050 SequenceNumber: sequenceNumber, 1051 PayloadType: 96, 1052 } 1053 assert.NoError(t, header.SetExtension(1, []byte("0"))) 1054 assert.NoError(t, header.SetExtension(2, []byte(rid))) 1055 1056 _, err := vp8Writer.bindings[0].writeStream.WriteRTP(header, []byte{0x00}) 1057 assert.NoError(t, err) 1058 } 1059 } 1060 } 1061 1062 assertTracksClosed := func(t *testing.T) { 1063 trackMapLock.Lock() 1064 defer trackMapLock.Unlock() 1065 1066 for _, track := range trackMap { 1067 _, _, err := track.ReadRTP() // Ignore first Read, this is our peeked data 1068 assert.Nil(t, err) 1069 1070 _, _, err = track.ReadRTP() 1071 assert.Equal(t, err, io.EOF) 1072 } 1073 } 1074 1075 t.Run("Disable Transceiver", func(t *testing.T) { 1076 trackMap = map[string]*TrackRemote{} 1077 pcOffer, pcAnswer, err := newPair() 1078 assert.NoError(t, err) 1079 1080 vp8Writer, err := NewTrackLocalStaticRTP(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion2") 1081 assert.NoError(t, err) 1082 1083 rtpTransceiver, err := pcOffer.AddTransceiverFromTrack( 1084 vp8Writer, 1085 RTPTransceiverInit{ 1086 Direction: RTPTransceiverDirectionSendonly, 1087 }, 1088 ) 1089 assert.NoError(t, err) 1090 1091 assert.NoError(t, signalPairWithModification(pcOffer, pcAnswer, func(sessionDescription string) string { 1092 return signalWithRids(sessionDescription, originalRids) 1093 })) 1094 1095 pcAnswer.OnTrack(onTrackHandler) 1096 sendUntilAllTracksFired(vp8Writer, originalRids) 1097 1098 assert.NoError(t, pcOffer.RemoveTrack(rtpTransceiver.Sender())) 1099 assert.NoError(t, signalPairWithModification(pcOffer, pcAnswer, func(sessionDescription string) string { 1100 sessionDescription = strings.SplitAfter(sessionDescription, "a=end-of-candidates\r\n")[0] 1101 return sessionDescription 1102 })) 1103 1104 assertTracksClosed(t) 1105 closePairNow(t, pcOffer, pcAnswer) 1106 }) 1107 1108 t.Run("Change RID", func(t *testing.T) { 1109 trackMap = map[string]*TrackRemote{} 1110 pcOffer, pcAnswer, err := newPair() 1111 assert.NoError(t, err) 1112 1113 vp8Writer, err := NewTrackLocalStaticRTP(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion2") 1114 assert.NoError(t, err) 1115 1116 _, err = pcOffer.AddTransceiverFromTrack( 1117 vp8Writer, 1118 RTPTransceiverInit{ 1119 Direction: RTPTransceiverDirectionSendonly, 1120 }, 1121 ) 1122 assert.NoError(t, err) 1123 1124 assert.NoError(t, signalPairWithModification(pcOffer, pcAnswer, func(sessionDescription string) string { 1125 return signalWithRids(sessionDescription, originalRids) 1126 })) 1127 1128 pcAnswer.OnTrack(onTrackHandler) 1129 sendUntilAllTracksFired(vp8Writer, originalRids) 1130 1131 newRids := []string{"d", "e", "f"} 1132 assert.NoError(t, signalPairWithModification(pcOffer, pcAnswer, func(sessionDescription string) string { 1133 scanner := bufio.NewScanner(strings.NewReader(sessionDescription)) 1134 sessionDescription = "" 1135 for scanner.Scan() { 1136 l := scanner.Text() 1137 if strings.HasPrefix(l, "a=rid") || strings.HasPrefix(l, "a=simulcast") { 1138 continue 1139 } 1140 1141 sessionDescription += l + "\n" 1142 } 1143 return signalWithRids(sessionDescription, newRids) 1144 })) 1145 1146 assertTracksClosed(t) 1147 closePairNow(t, pcOffer, pcAnswer) 1148 }) 1149 } 1150 1151 func TestPeerConnection_Regegotiation_ReuseTransceiver(t *testing.T) { 1152 lim := test.TimeOut(time.Second * 30) 1153 defer lim.Stop() 1154 1155 report := test.CheckRoutines(t) 1156 defer report() 1157 1158 pcOffer, pcAnswer, err := newPair() 1159 if err != nil { 1160 t.Fatal(err) 1161 } 1162 1163 vp8Track, err := NewTrackLocalStaticRTP(RTPCodecCapability{MimeType: MimeTypeVP8}, "foo", "bar") 1164 assert.NoError(t, err) 1165 sender, err := pcOffer.AddTrack(vp8Track) 1166 assert.NoError(t, err) 1167 assert.NoError(t, signalPair(pcOffer, pcAnswer)) 1168 1169 peerConnectionConnected := untilConnectionState(PeerConnectionStateConnected, pcOffer, pcAnswer) 1170 peerConnectionConnected.Wait() 1171 1172 assert.Equal(t, len(pcOffer.GetTransceivers()), 1) 1173 assert.Equal(t, pcOffer.GetTransceivers()[0].getCurrentDirection(), RTPTransceiverDirectionSendonly) 1174 assert.NoError(t, pcOffer.RemoveTrack(sender)) 1175 assert.Equal(t, pcOffer.GetTransceivers()[0].getCurrentDirection(), RTPTransceiverDirectionSendonly) 1176 1177 // should not reuse tranceiver 1178 vp8Track2, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "foo", "bar") 1179 assert.NoError(t, err) 1180 sender2, err := pcOffer.AddTrack(vp8Track2) 1181 assert.NoError(t, err) 1182 assert.Equal(t, len(pcOffer.GetTransceivers()), 2) 1183 assert.NoError(t, signalPair(pcOffer, pcAnswer)) 1184 assert.True(t, sender2.rtpTransceiver == pcOffer.GetTransceivers()[1]) 1185 1186 // should reuse first transceiver 1187 sender, err = pcOffer.AddTrack(vp8Track) 1188 assert.NoError(t, err) 1189 assert.Equal(t, len(pcOffer.GetTransceivers()), 2) 1190 assert.True(t, sender.rtpTransceiver == pcOffer.GetTransceivers()[0]) 1191 assert.NoError(t, signalPair(pcOffer, pcAnswer)) 1192 1193 tracksCh := make(chan *TrackRemote, 2) 1194 pcAnswer.OnTrack(func(tr *TrackRemote, _ *RTPReceiver) { 1195 tracksCh <- tr 1196 }) 1197 1198 ssrcReuse := sender.GetParameters().Encodings[0].SSRC 1199 for i := 0; i < 10; i++ { 1200 assert.NoError(t, vp8Track.WriteRTP(&rtp.Packet{Header: rtp.Header{Version: 2}, Payload: []byte{0, 1, 2, 3, 4, 5}})) 1201 time.Sleep(20 * time.Millisecond) 1202 } 1203 1204 // shold not reuse tranceiver between two CreateOffer 1205 offer, err := pcOffer.CreateOffer(nil) 1206 assert.NoError(t, err) 1207 assert.NoError(t, pcOffer.RemoveTrack(sender)) 1208 assert.NoError(t, pcOffer.SetLocalDescription(offer)) 1209 assert.NoError(t, pcAnswer.SetRemoteDescription(offer)) 1210 answer, err := pcAnswer.CreateAnswer(nil) 1211 assert.NoError(t, pcAnswer.SetLocalDescription(answer)) 1212 assert.NoError(t, err) 1213 assert.NoError(t, pcOffer.SetRemoteDescription(answer)) 1214 sender3, err := pcOffer.AddTrack(vp8Track) 1215 ssrcNotReuse := sender3.GetParameters().Encodings[0].SSRC 1216 assert.NoError(t, err) 1217 assert.Equal(t, len(pcOffer.GetTransceivers()), 3) 1218 assert.NoError(t, signalPair(pcOffer, pcAnswer)) 1219 assert.True(t, sender3.rtpTransceiver == pcOffer.GetTransceivers()[2]) 1220 1221 for i := 0; i < 10; i++ { 1222 assert.NoError(t, vp8Track.WriteRTP(&rtp.Packet{Header: rtp.Header{Version: 2}, Payload: []byte{0, 1, 2, 3, 4, 5}})) 1223 time.Sleep(20 * time.Millisecond) 1224 } 1225 1226 tr1 := <-tracksCh 1227 tr2 := <-tracksCh 1228 assert.Equal(t, tr1.SSRC(), ssrcReuse) 1229 assert.Equal(t, tr2.SSRC(), ssrcNotReuse) 1230 1231 closePairNow(t, pcOffer, pcAnswer) 1232 } 1233 1234 func TestPeerConnection_Renegotiation_MidConflict(t *testing.T) { 1235 lim := test.TimeOut(time.Second * 30) 1236 defer lim.Stop() 1237 1238 report := test.CheckRoutines(t) 1239 defer report() 1240 1241 offerPC, err := NewPeerConnection(Configuration{}) 1242 assert.NoError(t, err) 1243 answerPC, err := NewPeerConnection(Configuration{}) 1244 assert.NoError(t, err) 1245 _, err = offerPC.CreateDataChannel("test", nil) 1246 assert.NoError(t, err) 1247 1248 _, err = offerPC.AddTransceiverFromKind(RTPCodecTypeVideo, RTPTransceiverInit{Direction: RTPTransceiverDirectionSendonly}) 1249 assert.NoError(t, err) 1250 _, err = offerPC.AddTransceiverFromKind(RTPCodecTypeAudio, RTPTransceiverInit{Direction: RTPTransceiverDirectionSendonly}) 1251 assert.NoError(t, err) 1252 1253 offer, err := offerPC.CreateOffer(nil) 1254 assert.NoError(t, err) 1255 assert.NoError(t, offerPC.SetLocalDescription(offer)) 1256 assert.NoError(t, answerPC.SetRemoteDescription(offer), offer.SDP) 1257 answer, err := answerPC.CreateAnswer(nil) 1258 assert.NoError(t, err) 1259 assert.NoError(t, answerPC.SetLocalDescription(answer)) 1260 assert.NoError(t, offerPC.SetRemoteDescription(answer)) 1261 assert.Equal(t, SignalingStateStable, offerPC.SignalingState()) 1262 1263 tr, err := offerPC.AddTransceiverFromKind(RTPCodecTypeVideo, RTPTransceiverInit{Direction: RTPTransceiverDirectionSendonly}) 1264 assert.NoError(t, err) 1265 assert.NoError(t, tr.SetMid("3")) 1266 _, err = offerPC.AddTransceiverFromKind(RTPCodecTypeVideo, RTPTransceiverInit{Direction: RTPTransceiverDirectionSendrecv}) 1267 assert.NoError(t, err) 1268 _, err = offerPC.CreateOffer(nil) 1269 assert.NoError(t, err) 1270 1271 assert.NoError(t, offerPC.Close()) 1272 assert.NoError(t, answerPC.Close()) 1273 } 1274 1275 func TestPeerConnection_Regegotiation_AnswerAddsTrack(t *testing.T) { 1276 lim := test.TimeOut(time.Second * 30) 1277 defer lim.Stop() 1278 1279 report := test.CheckRoutines(t) 1280 defer report() 1281 1282 pcOffer, pcAnswer, err := newPair() 1283 if err != nil { 1284 t.Fatal(err) 1285 } 1286 1287 tracksCh := make(chan *TrackRemote) 1288 pcOffer.OnTrack(func(track *TrackRemote, _ *RTPReceiver) { 1289 tracksCh <- track 1290 for { 1291 if _, _, readErr := track.ReadRTP(); errors.Is(readErr, io.EOF) { 1292 return 1293 } 1294 } 1295 }) 1296 1297 vp8Track, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "foo", "bar") 1298 assert.NoError(t, err) 1299 assert.NoError(t, signalPair(pcOffer, pcAnswer)) 1300 1301 _, err = pcOffer.AddTransceiverFromKind(RTPCodecTypeVideo, RTPTransceiverInit{ 1302 Direction: RTPTransceiverDirectionRecvonly, 1303 }) 1304 assert.NoError(t, err) 1305 assert.NoError(t, signalPair(pcOffer, pcAnswer)) 1306 1307 _, err = pcAnswer.AddTransceiverFromKind(RTPCodecTypeVideo, RTPTransceiverInit{ 1308 Direction: RTPTransceiverDirectionSendonly, 1309 }) 1310 assert.NoError(t, err) 1311 1312 assert.NoError(t, err) 1313 _, err = pcAnswer.AddTrack(vp8Track) 1314 assert.NoError(t, err) 1315 assert.NoError(t, signalPair(pcOffer, pcAnswer)) 1316 1317 ctx, cancel := context.WithCancel(context.Background()) 1318 1319 go sendVideoUntilDone(ctx.Done(), t, []*TrackLocalStaticSample{vp8Track}) 1320 1321 <-tracksCh 1322 cancel() 1323 1324 closePairNow(t, pcOffer, pcAnswer) 1325 } 1326 1327 func TestNegotiationNeededWithRecvonlyTrack(t *testing.T) { 1328 lim := test.TimeOut(time.Second * 30) 1329 defer lim.Stop() 1330 1331 report := test.CheckRoutines(t) 1332 defer report() 1333 1334 pcOffer, pcAnswer, err := newPair() 1335 if err != nil { 1336 t.Fatal(err) 1337 } 1338 1339 var wg sync.WaitGroup 1340 wg.Add(1) 1341 pcAnswer.OnNegotiationNeeded(wg.Done) 1342 1343 _, err = pcOffer.AddTransceiverFromKind(RTPCodecTypeVideo, RTPTransceiverInit{Direction: RTPTransceiverDirectionRecvonly}) 1344 if err != nil { 1345 t.Fatal(err) 1346 } 1347 1348 if err := signalPair(pcOffer, pcAnswer); err != nil { 1349 t.Fatal(err) 1350 } 1351 1352 onDataChannel, onDataChannelCancel := context.WithCancel(context.Background()) 1353 pcAnswer.OnDataChannel(func(*DataChannel) { 1354 onDataChannelCancel() 1355 }) 1356 <-onDataChannel.Done() 1357 wg.Wait() 1358 1359 closePairNow(t, pcOffer, pcAnswer) 1360 } 1361 1362 func TestNegotiationNotNeededAfterReplaceTrackNil(t *testing.T) { 1363 lim := test.TimeOut(time.Second * 30) 1364 defer lim.Stop() 1365 1366 report := test.CheckRoutines(t) 1367 defer report() 1368 1369 pcOffer, err := NewPeerConnection(Configuration{}) 1370 assert.NoError(t, err) 1371 1372 pcAnswer, err := NewPeerConnection(Configuration{}) 1373 assert.NoError(t, err) 1374 1375 tr, err := pcOffer.AddTransceiverFromKind(RTPCodecTypeAudio) 1376 assert.NoError(t, err) 1377 1378 assert.NoError(t, signalPair(pcOffer, pcAnswer)) 1379 1380 assert.NoError(t, tr.Sender().ReplaceTrack(nil)) 1381 1382 assert.False(t, pcOffer.checkNegotiationNeeded()) 1383 1384 assert.NoError(t, pcOffer.Close()) 1385 assert.NoError(t, pcAnswer.Close()) 1386 }