mirror of
https://github.com/pion/webrtc.git
synced 2025-10-26 16:50:34 +08:00
RTCRtpReceiver would leak routines and block if the ReadStream hadn't given us a packet yet Closes #387 Co-authored-by: rob-deutsch <robzyb+altgithub@gmail.com>
232 lines
4.5 KiB
Go
232 lines
4.5 KiB
Go
package webrtc
|
|
|
|
import (
|
|
"bytes"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/pions/rtcp"
|
|
"github.com/pions/transport/test"
|
|
"github.com/pions/webrtc/pkg/ice"
|
|
"github.com/pions/webrtc/pkg/media"
|
|
)
|
|
|
|
func TestRTCPeerConnection_Media_Sample(t *testing.T) {
|
|
api := NewAPI()
|
|
lim := test.TimeOut(time.Second * 30)
|
|
defer lim.Stop()
|
|
|
|
report := test.CheckRoutines(t)
|
|
defer report()
|
|
|
|
api.mediaEngine.RegisterDefaultCodecs()
|
|
pcOffer, pcAnswer, err := api.newPair()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
awaitRTPRecv := make(chan bool)
|
|
awaitRTPRecvClosed := make(chan bool)
|
|
awaitRTPSend := make(chan bool)
|
|
|
|
awaitRTCPSenderRecv := make(chan bool)
|
|
awaitRTCPSenderSend := make(chan error)
|
|
|
|
awaitRTCPRecieverRecv := make(chan bool)
|
|
awaitRTCPRecieverSend := make(chan error)
|
|
|
|
pcAnswer.OnTrack(func(track *RTCTrack) {
|
|
go func() {
|
|
for {
|
|
time.Sleep(time.Millisecond * 100)
|
|
if routineErr := pcAnswer.SendRTCP(&rtcp.RapidResynchronizationRequest{SenderSSRC: track.Ssrc, MediaSSRC: track.Ssrc}); routineErr != nil {
|
|
awaitRTCPRecieverSend <- routineErr
|
|
return
|
|
}
|
|
|
|
select {
|
|
case <-awaitRTCPSenderRecv:
|
|
close(awaitRTCPRecieverSend)
|
|
return
|
|
default:
|
|
}
|
|
}
|
|
}()
|
|
|
|
go func() {
|
|
<-track.RTCPPackets
|
|
close(awaitRTCPRecieverRecv)
|
|
}()
|
|
|
|
haveClosedAwaitRTPRecv := false
|
|
for {
|
|
p, ok := <-track.Packets
|
|
if !ok {
|
|
close(awaitRTPRecvClosed)
|
|
return
|
|
} else if bytes.Equal(p.Payload, []byte{0x10, 0x00}) && !haveClosedAwaitRTPRecv {
|
|
haveClosedAwaitRTPRecv = true
|
|
close(awaitRTPRecv)
|
|
}
|
|
}
|
|
})
|
|
|
|
vp8Track, err := pcOffer.NewRTCSampleTrack(DefaultPayloadTypeVP8, "video", "pion")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err = pcOffer.AddTrack(vp8Track); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
go func() {
|
|
for {
|
|
time.Sleep(time.Millisecond * 100)
|
|
vp8Track.Samples <- media.RTCSample{Data: []byte{0x00}, Samples: 1}
|
|
|
|
select {
|
|
case <-awaitRTPRecv:
|
|
close(awaitRTPSend)
|
|
return
|
|
default:
|
|
}
|
|
}
|
|
}()
|
|
|
|
go func() {
|
|
for {
|
|
time.Sleep(time.Millisecond * 100)
|
|
if routineErr := pcOffer.SendRTCP(&rtcp.PictureLossIndication{SenderSSRC: vp8Track.Ssrc, MediaSSRC: vp8Track.Ssrc}); routineErr != nil {
|
|
awaitRTCPSenderSend <- routineErr
|
|
}
|
|
|
|
select {
|
|
case <-awaitRTCPRecieverRecv:
|
|
close(awaitRTCPSenderSend)
|
|
return
|
|
default:
|
|
}
|
|
}
|
|
}()
|
|
|
|
go func() {
|
|
<-vp8Track.RTCPPackets
|
|
close(awaitRTCPSenderRecv)
|
|
}()
|
|
|
|
err = signalPair(pcOffer, pcAnswer)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
<-awaitRTPRecv
|
|
<-awaitRTPSend
|
|
|
|
<-awaitRTCPSenderRecv
|
|
err, ok := <-awaitRTCPSenderSend
|
|
if ok {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
<-awaitRTCPRecieverRecv
|
|
err, ok = <-awaitRTCPRecieverSend
|
|
if ok {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
err = pcOffer.Close()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
err = pcAnswer.Close()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
<-awaitRTPRecvClosed
|
|
}
|
|
|
|
/*
|
|
RTCPeerConnection should be able to be torn down at anytime
|
|
This test adds an input track and asserts
|
|
|
|
* OnTrack doesn't fire since no video packets will arrive
|
|
* No goroutine leaks
|
|
* No deadlocks on shutdown
|
|
*/
|
|
func TestRTCPeerConnection_Media_Shutdown(t *testing.T) {
|
|
iceComplete := make(chan bool)
|
|
|
|
api := NewAPI()
|
|
lim := test.TimeOut(time.Second * 30)
|
|
defer lim.Stop()
|
|
|
|
report := test.CheckRoutines(t)
|
|
defer report()
|
|
|
|
api.mediaEngine.RegisterDefaultCodecs()
|
|
pcOffer, pcAnswer, err := api.newPair()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
opusTrack, err := pcOffer.NewRTCSampleTrack(DefaultPayloadTypeOpus, "audio", "pion1")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
vp8Track, err := pcOffer.NewRTCSampleTrack(DefaultPayloadTypeVP8, "video", "pion2")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if _, err = pcOffer.AddTrack(opusTrack); err != nil {
|
|
t.Fatal(err)
|
|
} else if _, err = pcOffer.AddTrack(vp8Track); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
var onTrackFiredLock sync.RWMutex
|
|
onTrackFired := false
|
|
|
|
pcAnswer.OnTrack(func(track *RTCTrack) {
|
|
onTrackFiredLock.Lock()
|
|
defer onTrackFiredLock.Unlock()
|
|
onTrackFired = true
|
|
})
|
|
|
|
pcAnswer.OnICEConnectionStateChange(func(iceState ice.ConnectionState) {
|
|
if iceState == ice.ConnectionStateConnected {
|
|
go func() {
|
|
time.Sleep(3 * time.Second) // TODO RTCPeerConnection.Close() doesn't block for all subsystems
|
|
close(iceComplete)
|
|
}()
|
|
}
|
|
})
|
|
|
|
err = signalPair(pcOffer, pcAnswer)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
<-iceComplete
|
|
|
|
err = pcOffer.Close()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
err = pcAnswer.Close()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
onTrackFiredLock.Lock()
|
|
if onTrackFired {
|
|
t.Fatalf("RTCPeerConnection OnTrack fired even though we got no packets")
|
|
}
|
|
onTrackFiredLock.Unlock()
|
|
|
|
}
|