From d9a59a594982f950c56cc49a870054f165aa2560 Mon Sep 17 00:00:00 2001 From: Sean DuBois Date: Sun, 21 Apr 2024 22:55:01 -0400 Subject: [PATCH 01/39] Add test for TrackRemote and RTX Packets Relates to #2752 --- rtpreceiver_go_test.go | 125 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) diff --git a/rtpreceiver_go_test.go b/rtpreceiver_go_test.go index 911f0c83822..6a8b2b23c2d 100644 --- a/rtpreceiver_go_test.go +++ b/rtpreceiver_go_test.go @@ -7,11 +7,21 @@ package webrtc import ( + "bufio" "context" + "encoding/binary" + "errors" + "fmt" + "io" + "strconv" + "strings" "testing" "time" + "github.com/pion/randutil" + "github.com/pion/rtp" "github.com/pion/sdp/v3" + "github.com/pion/transport/v3/test" "github.com/pion/webrtc/v4/pkg/media" "github.com/stretchr/testify/assert" ) @@ -70,3 +80,118 @@ func TestSetRTPParameters(t *testing.T) { assert.NoError(t, wan.Stop()) closePairNow(t, sender, receiver) } + +// Assert the behavior of reading a RTX with a distinct SSRC +// All the attributes should be populated and the packet unpacked +func Test_RTX_Read(t *testing.T) { + defer test.TimeOut(time.Second * 30).Stop() + + var ssrc *uint32 + ssrcLines := "" + rtxSsrc := randutil.NewMathRandomGenerator().Uint32() + + pcOffer, pcAnswer, err := newPair() + assert.NoError(t, err) + + track, err := NewTrackLocalStaticRTP(RTPCodecCapability{MimeType: MimeTypeVP8}, "track-id", "stream-id") + assert.NoError(t, err) + + _, err = pcOffer.AddTrack(track) + assert.NoError(t, err) + + rtxRead, rtxReadCancel := context.WithCancel(context.Background()) + pcAnswer.OnTrack(func(track *TrackRemote, _ *RTPReceiver) { + for { + pkt, attributes, readRTPErr := track.ReadRTP() + if errors.Is(readRTPErr, io.EOF) { + return + } else if pkt.PayloadType == 0 { + continue + } + + assert.NoError(t, readRTPErr) + assert.NotNil(t, pkt) + assert.Equal(t, pkt.SSRC, *ssrc) + assert.Equal(t, pkt.PayloadType, uint8(96)) + assert.Equal(t, pkt.Payload, []byte{0xB, 0xA, 0xD}) + + rtxPayloadType := attributes.Get(AttributeRtxPayloadType) + rtxSequenceNumber := attributes.Get(AttributeRtxSequenceNumber) + rtxSSRC := attributes.Get(AttributeRtxSsrc) + if rtxPayloadType != nil && rtxSequenceNumber != nil && rtxSSRC != nil { + assert.Equal(t, rtxPayloadType, uint8(97)) + assert.Equal(t, rtxSSRC, rtxSsrc) + assert.Equal(t, rtxSequenceNumber, pkt.SequenceNumber+500) + + rtxReadCancel() + } + } + }) + + assert.NoError(t, signalPairWithModification(pcOffer, pcAnswer, func(offer string) (modified string) { + scanner := bufio.NewScanner(strings.NewReader(offer)) + for scanner.Scan() { + l := scanner.Text() + + if strings.HasPrefix(l, "a=ssrc") { + if ssrc == nil { + lineSplit := strings.Split(l, " ")[0] + parsed, atoiErr := strconv.ParseUint(strings.TrimPrefix(lineSplit, "a=ssrc:"), 10, 32) + assert.NoError(t, atoiErr) + + parsedSsrc := uint32(parsed) + ssrc = &parsedSsrc + + modified += fmt.Sprintf("a=ssrc-group:FID %d %d\r\n", *ssrc, rtxSsrc) + } + + ssrcLines += l + "\n" + } else if ssrcLines != "" { + ssrcLines = strings.ReplaceAll(ssrcLines, fmt.Sprintf("%d", *ssrc), fmt.Sprintf("%d", rtxSsrc)) + modified += ssrcLines + ssrcLines = "" + } + + modified += l + "\n" + } + + return modified + })) + + func() { + for i := uint16(0); ; i++ { + pkt := rtp.Packet{ + Header: rtp.Header{ + Version: 2, + SSRC: *ssrc, + PayloadType: 96, + SequenceNumber: i, + }, + Payload: []byte{0xB, 0xA, 0xD}, + } + + select { + case <-time.After(20 * time.Millisecond): + // Send the original packet + err = track.WriteRTP(&pkt) + assert.NoError(t, err) + + rtxPayload := []byte{0x0, 0x0, 0xB, 0xA, 0xD} + binary.BigEndian.PutUint16(rtxPayload[0:2], pkt.Header.SequenceNumber) + + // Send the RTX + _, err = track.bindings[0].writeStream.WriteRTP(&rtp.Header{ + Version: 2, + SSRC: rtxSsrc, + PayloadType: 97, + SequenceNumber: i + 500, + }, rtxPayload) + assert.NoError(t, err) + case <-rtxRead.Done(): + return + } + } + }() + + closePairNow(t, pcOffer, pcAnswer) +} From a9e88d25312d8038ceedae9b8f59a17646725de0 Mon Sep 17 00:00:00 2001 From: cnderrauber Date: Mon, 22 Apr 2024 12:53:36 +0800 Subject: [PATCH 02/39] Add recvonly transceiver for simulcast video Fix one of the layers can't send back video because the transceiver is used for receiving. --- examples/simulcast/main.go | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/examples/simulcast/main.go b/examples/simulcast/main.go index ded6bdfe07d..6054bf6a96f 100644 --- a/examples/simulcast/main.go +++ b/examples/simulcast/main.go @@ -64,14 +64,18 @@ func main() { } outputTracks["f"] = outputTrack - // Add this newly created track to the PeerConnection - if _, err = peerConnection.AddTrack(outputTracks["q"]); err != nil { + if _, err = peerConnection.AddTransceiverFromKind(webrtc.RTPCodecTypeVideo, webrtc.RTPTransceiverInit{Direction: webrtc.RTPTransceiverDirectionRecvonly}); err != nil { panic(err) } - if _, err = peerConnection.AddTrack(outputTracks["h"]); err != nil { + + // Add this newly created track to the PeerConnection to send back video + if _, err = peerConnection.AddTransceiverFromTrack(outputTracks["q"], webrtc.RTPTransceiverInit{Direction: webrtc.RTPTransceiverDirectionSendonly}); err != nil { + panic(err) + } + if _, err = peerConnection.AddTransceiverFromTrack(outputTracks["h"], webrtc.RTPTransceiverInit{Direction: webrtc.RTPTransceiverDirectionSendonly}); err != nil { panic(err) } - if _, err = peerConnection.AddTrack(outputTracks["f"]); err != nil { + if _, err = peerConnection.AddTransceiverFromTrack(outputTracks["f"], webrtc.RTPTransceiverInit{Direction: webrtc.RTPTransceiverDirectionSendonly}); err != nil { panic(err) } From d851a44e180f51480a4a556015a5d36000179cad Mon Sep 17 00:00:00 2001 From: cnderrauber Date: Wed, 24 Apr 2024 16:50:23 +0800 Subject: [PATCH 03/39] Replace pool with bytes in readLoop Replace pool with bytes in readLoop --- datachannel.go | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/datachannel.go b/datachannel.go index a846eb58e3d..b3dfdad4e10 100644 --- a/datachannel.go +++ b/datachannel.go @@ -349,18 +349,11 @@ func (d *DataChannel) onError(err error) { } } -// See https://github.com/pion/webrtc/issues/1516 -// nolint:gochecknoglobals -var rlBufPool = sync.Pool{New: func() interface{} { - return make([]byte, dataChannelBufferSize) -}} - func (d *DataChannel) readLoop() { + buffer := make([]byte, dataChannelBufferSize) for { - buffer := rlBufPool.Get().([]byte) //nolint:forcetypeassert n, isString, err := d.dataChannel.ReadDataChannel(buffer) if err != nil { - rlBufPool.Put(buffer) // nolint:staticcheck d.setReadyState(DataChannelStateClosed) if !errors.Is(err, io.EOF) { d.onError(err) @@ -371,8 +364,6 @@ func (d *DataChannel) readLoop() { m := DataChannelMessage{Data: make([]byte, n), IsString: isString} copy(m.Data, buffer[:n]) - // The 'staticcheck' pragma is a false positive on the part of the CI linter. - rlBufPool.Put(buffer) // nolint:staticcheck // NB: Why was DataChannelMessage not passed as a pointer value? d.onMessage(m) // nolint:staticcheck From 4ca42e7cc1cde7aa32c3dc67dbca689ae4492b7a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 24 Apr 2024 09:03:11 +0000 Subject: [PATCH 04/39] Update module github.com/pion/rtp to v1.8.6 Generated by renovateBot --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index ca23337eccb..7222f3de945 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/pion/logging v0.2.2 github.com/pion/randutil v0.1.0 github.com/pion/rtcp v1.2.14 - github.com/pion/rtp v1.8.5 + github.com/pion/rtp v1.8.6 github.com/pion/sctp v1.8.16 github.com/pion/sdp/v3 v3.0.9 github.com/pion/srtp/v3 v3.0.1 diff --git a/go.sum b/go.sum index fc25c58f65a..2ec773b3d6f 100644 --- a/go.sum +++ b/go.sum @@ -57,8 +57,8 @@ github.com/pion/rtcp v1.2.12/go.mod h1:sn6qjxvnwyAkkPzPULIbVqSKI5Dv54Rv7VG0kNxh9 github.com/pion/rtcp v1.2.14 h1:KCkGV3vJ+4DAJmvP0vaQShsb0xkRfWkO540Gy102KyE= github.com/pion/rtcp v1.2.14/go.mod h1:sn6qjxvnwyAkkPzPULIbVqSKI5Dv54Rv7VG0kNxh9L4= github.com/pion/rtp v1.8.3/go.mod h1:pBGHaFt/yW7bf1jjWAoUjpSNoDnw98KTMg+jWWvziqU= -github.com/pion/rtp v1.8.5 h1:uYzINfaK+9yWs7r537z/Rc1SvT8ILjBcmDOpJcTB+OU= -github.com/pion/rtp v1.8.5/go.mod h1:pBGHaFt/yW7bf1jjWAoUjpSNoDnw98KTMg+jWWvziqU= +github.com/pion/rtp v1.8.6 h1:MTmn/b0aWWsAzux2AmP8WGllusBVw4NPYPVFFd7jUPw= +github.com/pion/rtp v1.8.6/go.mod h1:pBGHaFt/yW7bf1jjWAoUjpSNoDnw98KTMg+jWWvziqU= github.com/pion/sctp v1.8.13/go.mod h1:YKSgO/bO/6aOMP9LCie1DuD7m+GamiK2yIiPM6vH+GA= github.com/pion/sctp v1.8.16 h1:PKrMs+o9EMLRvFfXq59WFsC+V8mN1wnKzqrv+3D/gYY= github.com/pion/sctp v1.8.16/go.mod h1:P6PbDVA++OJMrVNg2AL3XtYHV4uD6dvfyOovCgMs0PE= From 83cfeea397d4fa356ace296147732bbf8eac854f Mon Sep 17 00:00:00 2001 From: cnderrauber Date: Thu, 25 Apr 2024 17:20:27 +0800 Subject: [PATCH 05/39] Update RtxSSRC for simulcast track remote Fix #2751, updates remote track's rtx ssrc for simulcast track doesn't contain rtx ssrc in sdp since readRTX relies on rtx ssrc to determine if it has a rtx stream. --- peerconnection_media_test.go | 218 +++++++++++++++++++++++++++++++++++ rtpreceiver.go | 4 + track_remote.go | 6 + 3 files changed, 228 insertions(+) diff --git a/peerconnection_media_test.go b/peerconnection_media_test.go index 228cfed4bc2..c196818a2e5 100644 --- a/peerconnection_media_test.go +++ b/peerconnection_media_test.go @@ -13,6 +13,7 @@ import ( "errors" "fmt" "io" + "regexp" "strings" "sync" "sync/atomic" @@ -26,6 +27,7 @@ import ( "github.com/pion/sdp/v3" "github.com/pion/transport/v3/test" "github.com/pion/transport/v3/vnet" + "github.com/pion/webrtc/v4/internal/util" "github.com/pion/webrtc/v4/pkg/media" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -1388,6 +1390,222 @@ func TestPeerConnection_Simulcast(t *testing.T) { }) } +type simulcastTestTrackLocal struct { + *TrackLocalStaticRTP +} + +// don't use ssrc&payload in bindings to let the test write different stream packets. +func (s *simulcastTestTrackLocal) WriteRTP(pkt *rtp.Packet) error { + packet := getPacketAllocationFromPool() + + defer resetPacketPoolAllocation(packet) + + *packet = *pkt + + s.mu.RLock() + defer s.mu.RUnlock() + + writeErrs := []error{} + + for _, b := range s.bindings { + if _, err := b.writeStream.WriteRTP(&packet.Header, packet.Payload); err != nil { + writeErrs = append(writeErrs, err) + } + } + + return util.FlattenErrs(writeErrs) +} + +func TestPeerConnection_Simulcast_RTX(t *testing.T) { + lim := test.TimeOut(time.Second * 30) + defer lim.Stop() + + report := test.CheckRoutines(t) + defer report() + + rids := []string{"a", "b"} + pcOffer, pcAnswer, err := newPair() + assert.NoError(t, err) + + vp8WriterAStatic, err := NewTrackLocalStaticRTP(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion2", WithRTPStreamID(rids[0])) + assert.NoError(t, err) + + vp8WriterBStatic, err := NewTrackLocalStaticRTP(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion2", WithRTPStreamID(rids[1])) + assert.NoError(t, err) + + vp8WriterA, vp8WriterB := &simulcastTestTrackLocal{vp8WriterAStatic}, &simulcastTestTrackLocal{vp8WriterBStatic} + + sender, err := pcOffer.AddTrack(vp8WriterA) + assert.NoError(t, err) + assert.NotNil(t, sender) + + assert.NoError(t, sender.AddEncoding(vp8WriterB)) + + var ridMapLock sync.RWMutex + ridMap := map[string]int{} + + assertRidCorrect := func(t *testing.T) { + ridMapLock.Lock() + defer ridMapLock.Unlock() + + for _, rid := range rids { + assert.Equal(t, ridMap[rid], 1) + } + assert.Equal(t, len(ridMap), 2) + } + + ridsFullfilled := func() bool { + ridMapLock.Lock() + defer ridMapLock.Unlock() + + ridCount := len(ridMap) + return ridCount == 2 + } + + var rtxPacketRead atomic.Int32 + var wg sync.WaitGroup + wg.Add(2) + + pcAnswer.OnTrack(func(trackRemote *TrackRemote, _ *RTPReceiver) { + ridMapLock.Lock() + ridMap[trackRemote.RID()] = ridMap[trackRemote.RID()] + 1 + ridMapLock.Unlock() + + defer wg.Done() + + for { + _, attr, rerr := trackRemote.ReadRTP() + if rerr != nil { + break + } + if pt, ok := attr.Get(AttributeRtxPayloadType).(byte); ok { + if pt == 97 { + rtxPacketRead.Add(1) + } + } + } + }) + + parameters := sender.GetParameters() + assert.Equal(t, "a", parameters.Encodings[0].RID) + assert.Equal(t, "b", parameters.Encodings[1].RID) + + var midID, ridID, rsid uint8 + for _, extension := range parameters.HeaderExtensions { + switch extension.URI { + case sdp.SDESMidURI: + midID = uint8(extension.ID) + case sdp.SDESRTPStreamIDURI: + ridID = uint8(extension.ID) + case sdesRepairRTPStreamIDURI: + rsid = uint8(extension.ID) + } + } + assert.NotZero(t, midID) + assert.NotZero(t, ridID) + assert.NotZero(t, rsid) + + err = signalPairWithModification(pcOffer, pcAnswer, func(sdp string) string { + // Original chrome sdp contains no ssrc info https://pastebin.com/raw/JTjX6zg6 + re := regexp.MustCompile("(?m)[\r\n]+^.*a=ssrc.*$") + res := re.ReplaceAllString(sdp, "") + return res + }) + assert.NoError(t, err) + + // padding only packets should not affect simulcast probe + var sequenceNumber uint16 + for sequenceNumber = 0; sequenceNumber < simulcastProbeCount+10; sequenceNumber++ { + time.Sleep(20 * time.Millisecond) + + for i, track := range []*simulcastTestTrackLocal{vp8WriterA, vp8WriterB} { + pkt := &rtp.Packet{ + Header: rtp.Header{ + Version: 2, + SequenceNumber: sequenceNumber, + PayloadType: 96, + Padding: true, + SSRC: uint32(i), + }, + Payload: []byte{0x00, 0x02}, + } + + assert.NoError(t, track.WriteRTP(pkt)) + } + } + assert.False(t, ridsFullfilled(), "Simulcast probe should not be fulfilled by padding only packets") + + for ; !ridsFullfilled(); sequenceNumber++ { + time.Sleep(20 * time.Millisecond) + + for i, track := range []*simulcastTestTrackLocal{vp8WriterA, vp8WriterB} { + pkt := &rtp.Packet{ + Header: rtp.Header{ + Version: 2, + SequenceNumber: sequenceNumber, + PayloadType: 96, + SSRC: uint32(i), + }, + Payload: []byte{0x00}, + } + assert.NoError(t, pkt.Header.SetExtension(midID, []byte("0"))) + assert.NoError(t, pkt.Header.SetExtension(ridID, []byte(track.RID()))) + + assert.NoError(t, track.WriteRTP(pkt)) + } + } + + assertRidCorrect(t) + + for i := 0; i < simulcastProbeCount+10; i++ { + sequenceNumber++ + time.Sleep(10 * time.Millisecond) + + for j, track := range []*simulcastTestTrackLocal{vp8WriterA, vp8WriterB} { + pkt := &rtp.Packet{ + Header: rtp.Header{ + Version: 2, + SequenceNumber: sequenceNumber, + PayloadType: 97, + SSRC: uint32(100 + j), + }, + Payload: []byte{0x00, 0x00, 0x00, 0x00, 0x00}, + } + assert.NoError(t, pkt.Header.SetExtension(midID, []byte("0"))) + assert.NoError(t, pkt.Header.SetExtension(ridID, []byte(track.RID()))) + assert.NoError(t, pkt.Header.SetExtension(rsid, []byte(track.RID()))) + + assert.NoError(t, track.WriteRTP(pkt)) + } + } + + for ; rtxPacketRead.Load() == 0; sequenceNumber++ { + time.Sleep(20 * time.Millisecond) + + for i, track := range []*simulcastTestTrackLocal{vp8WriterA, vp8WriterB} { + pkt := &rtp.Packet{ + Header: rtp.Header{ + Version: 2, + SequenceNumber: sequenceNumber, + PayloadType: 96, + SSRC: uint32(i), + }, + Payload: []byte{0x00}, + } + assert.NoError(t, pkt.Header.SetExtension(midID, []byte("0"))) + assert.NoError(t, pkt.Header.SetExtension(ridID, []byte(track.RID()))) + + assert.NoError(t, track.WriteRTP(pkt)) + } + } + + closePairNow(t, pcOffer, pcAnswer) + + wg.Wait() + + assert.Greater(t, rtxPacketRead.Load(), int32(0), "no rtx packet read") +} + // Everytime we receieve a new SSRC we probe it and try to determine the proper way to handle it. // In most cases a Track explicitly declares a SSRC and a OnTrack is fired. In two cases we don't // know the SSRC ahead of time diff --git a/rtpreceiver.go b/rtpreceiver.go index 2457ba080a8..3dd4d83b1aa 100644 --- a/rtpreceiver.go +++ b/rtpreceiver.go @@ -418,6 +418,10 @@ func (r *RTPReceiver) receiveForRtx(ssrc SSRC, rsid string, streamInfo *intercep for i := range r.tracks { if r.tracks[i].track.RID() == rsid { track = &r.tracks[i] + if track.track.RtxSSRC() == 0 { + track.track.setRtxSSRC(SSRC(streamInfo.SSRC)) + } + break } } } diff --git a/track_remote.go b/track_remote.go index 691b488d83e..7e448dd9895 100644 --- a/track_remote.go +++ b/track_remote.go @@ -224,3 +224,9 @@ func (t *TrackRemote) HasRTX() bool { defer t.mu.RUnlock() return t.rtxSsrc != 0 } + +func (t *TrackRemote) setRtxSSRC(ssrc SSRC) { + t.mu.Lock() + defer t.mu.Unlock() + t.rtxSsrc = ssrc +} From a97c420d0c3780b1a3914ce60f80c49561e9f1ab Mon Sep 17 00:00:00 2001 From: knowmost Date: Sun, 28 Apr 2024 11:13:23 +0800 Subject: [PATCH 06/39] Fix typos in multiple comments --- api_js.go | 2 +- datachannel.go | 2 +- datachannel_js.go | 2 +- examples/insertable-streams/README.md | 2 +- interceptor.go | 2 +- internal/util/util.go | 4 ++-- peerconnection.go | 2 +- peerconnection_media_test.go | 2 +- sdp.go | 2 +- 9 files changed, 10 insertions(+), 10 deletions(-) diff --git a/api_js.go b/api_js.go index fe94bff1fb1..1f0fcb74c2a 100644 --- a/api_js.go +++ b/api_js.go @@ -6,7 +6,7 @@ package webrtc -// API bundles the global funcions of the WebRTC and ORTC API. +// API bundles the global functions of the WebRTC and ORTC API. type API struct { settingEngine *SettingEngine } diff --git a/datachannel.go b/datachannel.go index b3dfdad4e10..e975a04f7b5 100644 --- a/datachannel.go +++ b/datachannel.go @@ -236,7 +236,7 @@ func (d *DataChannel) onOpen() { } // OnDial sets an event handler which is invoked when the -// peer has been dialed, but before said peer has responsed +// peer has been dialed, but before said peer has responded func (d *DataChannel) OnDial(f func()) { d.mu.Lock() d.dialHandlerOnce = sync.Once{} diff --git a/datachannel_js.go b/datachannel_js.go index a6d1ae5e8aa..5e5fc4b0cda 100644 --- a/datachannel_js.go +++ b/datachannel_js.go @@ -115,7 +115,7 @@ func (d *DataChannel) SendText(s string) (err error) { // Before calling Detach you have to enable this behavior by calling // webrtc.DetachDataChannels(). Combining detached and normal data channels // is not supported. -// Please reffer to the data-channels-detach example and the +// Please refer to the data-channels-detach example and the // pion/datachannel documentation for the correct way to handle the // resulting DataChannel object. func (d *DataChannel) Detach() (datachannel.ReadWriteCloser, error) { diff --git a/examples/insertable-streams/README.md b/examples/insertable-streams/README.md index 73a77bc9761..09d23d3b928 100644 --- a/examples/insertable-streams/README.md +++ b/examples/insertable-streams/README.md @@ -4,7 +4,7 @@ This example modifies the video with a single-byte XOR cipher before sending, an decrypts in Javascript. insertable-streams allows the browser to process encoded video. You could implement -E2E encyption, add metadata or insert a completely different video feed! +E2E encryption, add metadata or insert a completely different video feed! ## Instructions ### Create IVF named `output.ivf` that contains a VP8 track diff --git a/interceptor.go b/interceptor.go index fec45ab7e0f..3ec09f7ac48 100644 --- a/interceptor.go +++ b/interceptor.go @@ -127,7 +127,7 @@ func ConfigureCongestionControlFeedback(mediaEngine *MediaEngine, interceptorReg return nil } -// ConfigureSimulcastExtensionHeaders enables the RTP Extenison Headers needed for Simulcast +// ConfigureSimulcastExtensionHeaders enables the RTP Extension Headers needed for Simulcast func ConfigureSimulcastExtensionHeaders(mediaEngine *MediaEngine) error { if err := mediaEngine.RegisterHeaderExtension(RTPHeaderExtensionCapability{URI: sdp.SDESMidURI}, RTPCodecTypeVideo); err != nil { return err diff --git a/internal/util/util.go b/internal/util/util.go index 3f43c123e53..966a6230a68 100644 --- a/internal/util/util.go +++ b/internal/util/util.go @@ -18,12 +18,12 @@ const ( // Use global random generator to properly seed by crypto grade random. var globalMathRandomGenerator = randutil.NewMathRandomGenerator() // nolint:gochecknoglobals -// MathRandAlpha generates a mathmatical random alphabet sequence of the requested length. +// MathRandAlpha generates a mathematical random alphabet sequence of the requested length. func MathRandAlpha(n int) string { return globalMathRandomGenerator.GenerateString(n, runesAlpha) } -// RandUint32 generates a mathmatical random uint32. +// RandUint32 generates a mathematical random uint32. func RandUint32() uint32 { return globalMathRandomGenerator.Uint32() } diff --git a/peerconnection.go b/peerconnection.go index 5e2fba24cf1..69489058861 100644 --- a/peerconnection.go +++ b/peerconnection.go @@ -1326,7 +1326,7 @@ func runIfNewReceiver( return false } -// configurepRTPReceivers opens knows inbound SRTP streams from the RemoteDescription +// configureRTPReceivers opens knows inbound SRTP streams from the RemoteDescription func (pc *PeerConnection) configureRTPReceivers(isRenegotiation bool, remoteDesc *SessionDescription, currentTransceivers []*RTPTransceiver) { //nolint:gocognit incomingTracks := trackDetailsFromSDP(pc.log, remoteDesc.parsed) diff --git a/peerconnection_media_test.go b/peerconnection_media_test.go index c196818a2e5..43bdd7ed429 100644 --- a/peerconnection_media_test.go +++ b/peerconnection_media_test.go @@ -1606,7 +1606,7 @@ func TestPeerConnection_Simulcast_RTX(t *testing.T) { assert.Greater(t, rtxPacketRead.Load(), int32(0), "no rtx packet read") } -// Everytime we receieve a new SSRC we probe it and try to determine the proper way to handle it. +// Everytime we receive a new SSRC we probe it and try to determine the proper way to handle it. // In most cases a Track explicitly declares a SSRC and a OnTrack is fired. In two cases we don't // know the SSRC ahead of time // * Undeclared SSRC in a single media section (https://github.com/pion/webrtc/issues/880) diff --git a/sdp.go b/sdp.go index 980f9850779..7ab0fd89807 100644 --- a/sdp.go +++ b/sdp.go @@ -85,7 +85,7 @@ func trackDetailsFromSDP(log logging.LeveledLogger, s *sdp.SessionDescription) ( tracksInMediaSection := []trackDetails{} rtxRepairFlows := map[uint64]uint64{} - // Plan B can have multiple tracks in a signle media section + // Plan B can have multiple tracks in a single media section streamID := "" trackID := "" From 480be18a3464a077963aba36f2cd890d5669e503 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 2 May 2024 07:36:18 +0000 Subject: [PATCH 07/39] Update module github.com/pion/ice/v3 to v3.0.7 Generated by renovateBot --- go.mod | 10 +++++----- go.sum | 17 ++++++++++------- icegatherer.go | 1 + settingengine.go | 11 +++++++++++ settingengine_test.go | 31 +++++++++++++++++++++++++++++++ 5 files changed, 58 insertions(+), 12 deletions(-) diff --git a/go.mod b/go.mod index 7222f3de945..d5f729df8f2 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.19 require ( github.com/pion/datachannel v1.5.6 github.com/pion/dtls/v2 v2.2.10 - github.com/pion/ice/v3 v3.0.6 + github.com/pion/ice/v3 v3.0.7 github.com/pion/interceptor v0.1.29 github.com/pion/logging v0.2.2 github.com/pion/randutil v0.1.0 @@ -18,7 +18,7 @@ require ( github.com/pion/transport/v3 v3.0.2 github.com/sclevine/agouti v3.0.0+incompatible github.com/stretchr/testify v1.9.0 - golang.org/x/net v0.22.0 + golang.org/x/net v0.24.0 ) require ( @@ -28,9 +28,9 @@ require ( github.com/onsi/gomega v1.17.0 // indirect github.com/pion/mdns/v2 v2.0.7 // indirect github.com/pion/transport/v2 v2.2.4 // indirect - github.com/pion/turn/v3 v3.0.2 // indirect + github.com/pion/turn/v3 v3.0.3 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - golang.org/x/crypto v0.21.0 // indirect - golang.org/x/sys v0.18.0 // indirect + golang.org/x/crypto v0.22.0 // indirect + golang.org/x/sys v0.19.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 2ec773b3d6f..dc386726f26 100644 --- a/go.sum +++ b/go.sum @@ -43,8 +43,8 @@ github.com/pion/datachannel v1.5.6/go.mod h1:1eKT6Q85pRnr2mHiWHxJwO50SfZRtWHTsNI github.com/pion/dtls/v2 v2.2.7/go.mod h1:8WiMkebSHFD0T+dIU+UeBaoV7kDhOW5oDCzZ7WZ/F9s= github.com/pion/dtls/v2 v2.2.10 h1:u2Axk+FyIR1VFTPurktB+1zoEPGIW3bmyj3LEFrXjAA= github.com/pion/dtls/v2 v2.2.10/go.mod h1:d9SYc9fch0CqK90mRk1dC7AkzzpwJj6u2GU3u+9pqFE= -github.com/pion/ice/v3 v3.0.6 h1:UC5vZCMhmve7yv+Y6E5eTnRTl+t9LLtmeBYQ9038Zm8= -github.com/pion/ice/v3 v3.0.6/go.mod h1:4eMTUKQEjC1fGQGB6qUzy2ux9Pc1v9EsO3hNaii+kXI= +github.com/pion/ice/v3 v3.0.7 h1:dfMViRKblENqzorR2cQiiRKWqQfqKZ9+nT/sREX3ra8= +github.com/pion/ice/v3 v3.0.7/go.mod h1:pBRcCoJRC0vwvFsemfRIqRLYukV4bPboGb0B4b8AhrQ= github.com/pion/interceptor v0.1.29 h1:39fsnlP1U8gw2JzOFWdfCU82vHvhW9o0rZnZF56wF+M= github.com/pion/interceptor v0.1.29/go.mod h1:ri+LGNjRUc5xUNtDEPzfdkmSqISixVTBF/z/Zms/6T4= github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY= @@ -74,8 +74,8 @@ github.com/pion/transport/v2 v2.2.4/go.mod h1:q2U/tf9FEfnSBGSW6w5Qp5PFWRLRj3NjLh github.com/pion/transport/v3 v3.0.1/go.mod h1:UY7kiITrlMv7/IKgd5eTUcaahZx5oUN3l9SzK5f5xE0= github.com/pion/transport/v3 v3.0.2 h1:r+40RJR25S9w3jbA6/5uEPTzcdn7ncyU44RWCbHkLg4= github.com/pion/transport/v3 v3.0.2/go.mod h1:nIToODoOlb5If2jF9y2Igfx3PFYWfuXi37m0IlWa/D0= -github.com/pion/turn/v3 v3.0.2 h1:iBonAIIKRwkVUJBFiFd/kSjytP7FlX0HwCyBDJPRDdU= -github.com/pion/turn/v3 v3.0.2/go.mod h1:vw0Dz420q7VYAF3J4wJKzReLHIo2LGp4ev8nXQexYsc= +github.com/pion/turn/v3 v3.0.3 h1:1e3GVk8gHZLPBA5LqadWYV60lmaKUaHCkm9DX9CkGcE= +github.com/pion/turn/v3 v3.0.3/go.mod h1:vw0Dz420q7VYAF3J4wJKzReLHIo2LGp4ev8nXQexYsc= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/sclevine/agouti v3.0.0+incompatible h1:8IBJS6PWz3uTlMP3YBIR5f+KAldcGuOeFkFbUWfBgK4= @@ -101,8 +101,9 @@ golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= -golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= +golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= +golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= @@ -120,8 +121,9 @@ golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= -golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc= golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= +golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -147,8 +149,9 @@ golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= diff --git a/icegatherer.go b/icegatherer.go index 51ea1aff79c..232145a974d 100644 --- a/icegatherer.go +++ b/icegatherer.go @@ -124,6 +124,7 @@ func (g *ICEGatherer) createAgent() error { ProxyDialer: g.api.settingEngine.iceProxyDialer, DisableActiveTCP: g.api.settingEngine.iceDisableActiveTCP, MaxBindingRequests: g.api.settingEngine.iceMaxBindingRequests, + BindingRequestHandler: g.api.settingEngine.iceBindingRequestHandler, } requestedNetworkTypes := g.api.settingEngine.candidates.ICENetworkTypes diff --git a/settingengine.go b/settingengine.go index ef0d9bea04e..a0d5e3be1ea 100644 --- a/settingengine.go +++ b/settingengine.go @@ -17,6 +17,7 @@ import ( dtlsElliptic "github.com/pion/dtls/v2/pkg/crypto/elliptic" "github.com/pion/ice/v3" "github.com/pion/logging" + "github.com/pion/stun/v2" "github.com/pion/transport/v3" "github.com/pion/transport/v3/packetio" "golang.org/x/net/proxy" @@ -91,6 +92,7 @@ type SettingEngine struct { iceUDPMux ice.UDPMux iceProxyDialer proxy.Dialer iceDisableActiveTCP bool + iceBindingRequestHandler func(m *stun.Message, local, remote ice.Candidate, pair *ice.CandidatePair) bool disableMediaEngineCopy bool srtpProtectionProfiles []dtls.SRTPProtectionProfile receiveMTU uint @@ -458,3 +460,12 @@ func (e *SettingEngine) SetDTLSCustomerCipherSuites(customCipherSuites func() [] func (e *SettingEngine) SetSCTPRTOMax(rtoMax time.Duration) { e.sctp.rtoMax = rtoMax } + +// SetICEBindingRequestHandler sets a callback that is fired on a STUN BindingRequest +// This allows users to do things like +// - Log incoming Binding Requests for debugging +// - Implement draft-thatcher-ice-renomination +// - Implement custom CandidatePair switching logic +func (e *SettingEngine) SetICEBindingRequestHandler(bindingRequestHandler func(m *stun.Message, local, remote ice.Candidate, pair *ice.CandidatePair) bool) { + e.iceBindingRequestHandler = bindingRequestHandler +} diff --git a/settingengine_test.go b/settingengine_test.go index df88243bbb4..787604b70a5 100644 --- a/settingengine_test.go +++ b/settingengine_test.go @@ -13,6 +13,8 @@ import ( "time" "github.com/pion/dtls/v2/pkg/crypto/elliptic" + "github.com/pion/ice/v3" + "github.com/pion/stun/v2" "github.com/pion/transport/v3/test" "github.com/stretchr/testify/assert" ) @@ -278,3 +280,32 @@ func TestSetSCTPRTOMax(t *testing.T) { s.SetSCTPRTOMax(expSize) assert.Equal(t, expSize, s.sctp.rtoMax) } + +func TestSetICEBindingRequestHandler(t *testing.T) { + seenICEControlled, seenICEControlledCancel := context.WithCancel(context.Background()) + seenICEControlling, seenICEControllingCancel := context.WithCancel(context.Background()) + + s := SettingEngine{} + s.SetICEBindingRequestHandler(func(m *stun.Message, _, _ ice.Candidate, _ *ice.CandidatePair) bool { + for _, a := range m.Attributes { + switch a.Type { + case stun.AttrICEControlled: + seenICEControlledCancel() + case stun.AttrICEControlling: + seenICEControllingCancel() + default: + } + } + + return false + }) + + pcOffer, pcAnswer, err := NewAPI(WithSettingEngine(s)).newPair(Configuration{}) + assert.NoError(t, err) + + assert.NoError(t, signalPair(pcOffer, pcAnswer)) + + <-seenICEControlled.Done() + <-seenICEControlling.Done() + closePairNow(t, pcOffer, pcAnswer) +} From 34c72157be8bf6404822863d112c2f496f8ffad6 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 6 May 2024 23:10:46 +0000 Subject: [PATCH 08/39] Update module golang.org/x/net to v0.25.0 Generated by renovateBot --- go.mod | 6 +++--- go.sum | 14 +++++++------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/go.mod b/go.mod index d5f729df8f2..9e3a24bc9e4 100644 --- a/go.mod +++ b/go.mod @@ -18,7 +18,7 @@ require ( github.com/pion/transport/v3 v3.0.2 github.com/sclevine/agouti v3.0.0+incompatible github.com/stretchr/testify v1.9.0 - golang.org/x/net v0.24.0 + golang.org/x/net v0.25.0 ) require ( @@ -30,7 +30,7 @@ require ( github.com/pion/transport/v2 v2.2.4 // indirect github.com/pion/turn/v3 v3.0.3 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - golang.org/x/crypto v0.22.0 // indirect - golang.org/x/sys v0.19.0 // indirect + golang.org/x/crypto v0.23.0 // indirect + golang.org/x/sys v0.20.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index dc386726f26..6966b250612 100644 --- a/go.sum +++ b/go.sum @@ -102,8 +102,8 @@ golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98y golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= -golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= -golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= +golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= @@ -122,8 +122,8 @@ golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= -golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= -golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= +golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -150,8 +150,8 @@ golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= -golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= @@ -168,8 +168,8 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= From aa9c623ae1849dbe56b7c781e32f367b3ca0bcff Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 7 May 2024 01:35:15 +0000 Subject: [PATCH 09/39] Update module github.com/pion/dtls/v2 to v2.2.11 Generated by renovateBot --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 9e3a24bc9e4..41928678388 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.19 require ( github.com/pion/datachannel v1.5.6 - github.com/pion/dtls/v2 v2.2.10 + github.com/pion/dtls/v2 v2.2.11 github.com/pion/ice/v3 v3.0.7 github.com/pion/interceptor v0.1.29 github.com/pion/logging v0.2.2 diff --git a/go.sum b/go.sum index 6966b250612..b1129ee88ec 100644 --- a/go.sum +++ b/go.sum @@ -41,8 +41,8 @@ github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAl github.com/pion/datachannel v1.5.6 h1:1IxKJntfSlYkpUj8LlYRSWpYiTTC02nUrOE8T3DqGeg= github.com/pion/datachannel v1.5.6/go.mod h1:1eKT6Q85pRnr2mHiWHxJwO50SfZRtWHTsNIVb/NfGW4= github.com/pion/dtls/v2 v2.2.7/go.mod h1:8WiMkebSHFD0T+dIU+UeBaoV7kDhOW5oDCzZ7WZ/F9s= -github.com/pion/dtls/v2 v2.2.10 h1:u2Axk+FyIR1VFTPurktB+1zoEPGIW3bmyj3LEFrXjAA= -github.com/pion/dtls/v2 v2.2.10/go.mod h1:d9SYc9fch0CqK90mRk1dC7AkzzpwJj6u2GU3u+9pqFE= +github.com/pion/dtls/v2 v2.2.11 h1:9U/dpCYl1ySttROPWJgqWKEylUdT0fXp/xst6JwY5Ks= +github.com/pion/dtls/v2 v2.2.11/go.mod h1:d9SYc9fch0CqK90mRk1dC7AkzzpwJj6u2GU3u+9pqFE= github.com/pion/ice/v3 v3.0.7 h1:dfMViRKblENqzorR2cQiiRKWqQfqKZ9+nT/sREX3ra8= github.com/pion/ice/v3 v3.0.7/go.mod h1:pBRcCoJRC0vwvFsemfRIqRLYukV4bPboGb0B4b8AhrQ= github.com/pion/interceptor v0.1.29 h1:39fsnlP1U8gw2JzOFWdfCU82vHvhW9o0rZnZF56wF+M= From 09461d55a6b42de7ccf9b55bd666bc08054c1bbe Mon Sep 17 00:00:00 2001 From: Sean DuBois Date: Sun, 19 May 2024 00:08:36 -0400 Subject: [PATCH 10/39] Remove examples/internal Users find it frustrating that example code doesn't work out of tree. This makes copying the examples out of the repo easier. Relates to #1981 --- .../bandwidth-estimation-from-disk/main.go | 51 +++++++- examples/broadcast/main.go | 54 ++++++++- .../data-channels-detach/jsfiddle/main.go | 65 ++++++++-- examples/data-channels-detach/main.go | 61 +++++++++- examples/data-channels/jsfiddle/main.go | 54 ++++++++- examples/data-channels/main.go | 64 ++++++++-- examples/insertable-streams/main.go | 51 +++++++- examples/internal/signal/http.go | 31 ----- examples/internal/signal/rand.go | 19 --- examples/internal/signal/signal.go | 113 ------------------ examples/ortc-media/main.go | 74 +++++++++++- examples/ortc/main.go | 85 +++++++++++-- examples/pion-to-pion/answer/main.go | 58 ++++++++- examples/pion-to-pion/offer/main.go | 12 +- examples/play-from-disk/main.go | 51 +++++++- examples/reflect/main.go | 53 +++++++- examples/rtcp-processing/main.go | 54 ++++++++- examples/rtp-forwarder/main.go | 52 +++++++- examples/rtp-to-webrtc/main.go | 52 +++++++- examples/save-to-disk-av1/main.go | 52 +++++++- examples/save-to-disk/main.go | 52 +++++++- examples/simulcast/main.go | 51 +++++++- examples/stats/main.go | 54 ++++++++- examples/swap-tracks/main.go | 52 +++++++- 24 files changed, 1064 insertions(+), 251 deletions(-) delete mode 100644 examples/internal/signal/http.go delete mode 100644 examples/internal/signal/rand.go delete mode 100644 examples/internal/signal/signal.go diff --git a/examples/bandwidth-estimation-from-disk/main.go b/examples/bandwidth-estimation-from-disk/main.go index c1cf585f87c..54e73d1a00c 100644 --- a/examples/bandwidth-estimation-from-disk/main.go +++ b/examples/bandwidth-estimation-from-disk/main.go @@ -8,17 +8,20 @@ package main import ( + "bufio" + "encoding/base64" + "encoding/json" "errors" "fmt" "io" "os" + "strings" "time" "github.com/pion/interceptor" "github.com/pion/interceptor/pkg/cc" "github.com/pion/interceptor/pkg/gcc" "github.com/pion/webrtc/v4" - "github.com/pion/webrtc/v4/examples/internal/signal" "github.com/pion/webrtc/v4/pkg/media" "github.com/pion/webrtc/v4/pkg/media/ivfreader" ) @@ -144,7 +147,7 @@ func main() { // Wait for the offer to be pasted offer := webrtc.SessionDescription{} - signal.Decode(signal.MustReadStdin(), &offer) + decode(readUntilNewline(), &offer) // Set the remote SessionDescription if err = peerConnection.SetRemoteDescription(offer); err != nil { @@ -171,7 +174,7 @@ func main() { <-gatherComplete // Output the answer in base64 so we can paste it in browser - fmt.Println(signal.Encode(*peerConnection.LocalDescription())) + fmt.Println(encode(peerConnection.LocalDescription())) // Open a IVF file and start reading using our IVFReader file, err := os.Open(qualityLevels[currentQuality].fileName) @@ -254,3 +257,45 @@ func setReaderFile(filename string) func(_ int64) io.Reader { return file } } + +// Read from stdin until we get a newline +func readUntilNewline() (in string) { + var err error + + r := bufio.NewReader(os.Stdin) + for { + in, err = r.ReadString('\n') + if err != nil && !errors.Is(err, io.EOF) { + panic(err) + } + + if in = strings.TrimSpace(in); len(in) > 0 { + break + } + } + + fmt.Println("") + return +} + +// JSON encode + base64 a SessionDescription +func encode(obj *webrtc.SessionDescription) string { + b, err := json.Marshal(obj) + if err != nil { + panic(err) + } + + return base64.StdEncoding.EncodeToString(b) +} + +// Decode a base64 and unmarshal JSON into a SessionDescription +func decode(in string, obj *webrtc.SessionDescription) { + b, err := base64.StdEncoding.DecodeString(in) + if err != nil { + panic(err) + } + + if err = json.Unmarshal(b, obj); err != nil { + panic(err) + } +} diff --git a/examples/broadcast/main.go b/examples/broadcast/main.go index 22a3d914ddc..c92971c5975 100644 --- a/examples/broadcast/main.go +++ b/examples/broadcast/main.go @@ -8,26 +8,29 @@ package main import ( + "encoding/base64" + "encoding/json" "errors" "flag" "fmt" "io" + "net/http" + "strconv" "github.com/pion/interceptor" "github.com/pion/interceptor/pkg/intervalpli" "github.com/pion/webrtc/v4" - "github.com/pion/webrtc/v4/examples/internal/signal" ) func main() { // nolint:gocognit port := flag.Int("port", 8080, "http server port") flag.Parse() - sdpChan := signal.HTTPSDPServer(*port) + sdpChan := httpSDPServer(*port) // Everything below is the Pion WebRTC API, thanks for using it ❤️. offer := webrtc.SessionDescription{} - signal.Decode(<-sdpChan, &offer) + decode(<-sdpChan, &offer) fmt.Println("") peerConnectionConfig := webrtc.Configuration{ @@ -132,7 +135,7 @@ func main() { // nolint:gocognit <-gatherComplete // Get the LocalDescription and take it to base64 so we can paste in browser - fmt.Println(signal.Encode(*peerConnection.LocalDescription())) + fmt.Println(encode(peerConnection.LocalDescription())) localTrack := <-localTrackChan for { @@ -140,7 +143,7 @@ func main() { // nolint:gocognit fmt.Println("Curl an base64 SDP to start sendonly peer connection") recvOnlyOffer := webrtc.SessionDescription{} - signal.Decode(<-sdpChan, &recvOnlyOffer) + decode(<-sdpChan, &recvOnlyOffer) // Create a new PeerConnection peerConnection, err := webrtc.NewPeerConnection(peerConnectionConfig) @@ -192,6 +195,45 @@ func main() { // nolint:gocognit <-gatherComplete // Get the LocalDescription and take it to base64 so we can paste in browser - fmt.Println(signal.Encode(*peerConnection.LocalDescription())) + fmt.Println(encode(peerConnection.LocalDescription())) } } + +// JSON encode + base64 a SessionDescription +func encode(obj *webrtc.SessionDescription) string { + b, err := json.Marshal(obj) + if err != nil { + panic(err) + } + + return base64.StdEncoding.EncodeToString(b) +} + +// Decode a base64 and unmarshal JSON into a SessionDescription +func decode(in string, obj *webrtc.SessionDescription) { + b, err := base64.StdEncoding.DecodeString(in) + if err != nil { + panic(err) + } + + if err = json.Unmarshal(b, obj); err != nil { + panic(err) + } +} + +// httpSDPServer starts a HTTP Server that consumes SDPs +func httpSDPServer(port int) chan string { + sdpChan := make(chan string) + http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + body, _ := io.ReadAll(r.Body) + fmt.Fprintf(w, "done") + sdpChan <- string(body) + }) + + go func() { + // nolint: gosec + panic(http.ListenAndServe(":"+strconv.Itoa(port), nil)) + }() + + return sdpChan +} diff --git a/examples/data-channels-detach/jsfiddle/main.go b/examples/data-channels-detach/jsfiddle/main.go index bb093cf925b..6ed1626a899 100644 --- a/examples/data-channels-detach/jsfiddle/main.go +++ b/examples/data-channels-detach/jsfiddle/main.go @@ -7,14 +7,19 @@ package main import ( + "bufio" + "encoding/base64" + "encoding/json" + "errors" "fmt" "io" + "os" + "strings" "syscall/js" "time" + "github.com/pion/randutil" "github.com/pion/webrtc/v4" - - "github.com/pion/webrtc/v4/examples/internal/signal" ) const messageSize = 15 @@ -95,7 +100,7 @@ func main() { }) peerConnection.OnICECandidate(func(candidate *webrtc.ICECandidate) { if candidate != nil { - encodedDescr := signal.Encode(peerConnection.LocalDescription()) + encodedDescr := encode(peerConnection.LocalDescription()) el := getElementByID("localSessionDescription") el.Set("value", encodedDescr) } @@ -126,7 +131,7 @@ func main() { } descr := webrtc.SessionDescription{} - signal.Decode(sd, &descr) + decode(sd, &descr) if err := peerConnection.SetRemoteDescription(descr); err != nil { handleError(err) } @@ -155,13 +160,15 @@ func ReadLoop(d io.Reader) { // WriteLoop shows how to write to the datachannel directly func WriteLoop(d io.Writer) { for range time.NewTicker(5 * time.Second).C { - message := signal.RandSeq(messageSize) - log(fmt.Sprintf("Sending %s \n", message)) - - _, err := d.Write([]byte(message)) + message, err := randutil.GenerateCryptoRandomString(messageSize, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") if err != nil { handleError(err) } + + log(fmt.Sprintf("Sending %s \n", message)) + if _, err := d.Write([]byte(message)); err != nil { + handleError(err) + } } } @@ -178,3 +185,45 @@ func handleError(err error) { func getElementByID(id string) js.Value { return js.Global().Get("document").Call("getElementById", id) } + +// Read from stdin until we get a newline +func readUntilNewline() (in string) { + var err error + + r := bufio.NewReader(os.Stdin) + for { + in, err = r.ReadString('\n') + if err != nil && !errors.Is(err, io.EOF) { + panic(err) + } + + if in = strings.TrimSpace(in); len(in) > 0 { + break + } + } + + fmt.Println("") + return +} + +// JSON encode + base64 a SessionDescription +func encode(obj *webrtc.SessionDescription) string { + b, err := json.Marshal(obj) + if err != nil { + panic(err) + } + + return base64.StdEncoding.EncodeToString(b) +} + +// Decode a base64 and unmarshal JSON into a SessionDescription +func decode(in string, obj *webrtc.SessionDescription) { + b, err := base64.StdEncoding.DecodeString(in) + if err != nil { + panic(err) + } + + if err = json.Unmarshal(b, obj); err != nil { + panic(err) + } +} diff --git a/examples/data-channels-detach/main.go b/examples/data-channels-detach/main.go index 8e16245119e..4a4ef2b3b7e 100644 --- a/examples/data-channels-detach/main.go +++ b/examples/data-channels-detach/main.go @@ -5,13 +5,18 @@ package main import ( + "bufio" + "encoding/base64" + "encoding/json" + "errors" "fmt" "io" "os" + "strings" "time" + "github.com/pion/randutil" "github.com/pion/webrtc/v4" - "github.com/pion/webrtc/v4/examples/internal/signal" ) const messageSize = 15 @@ -94,7 +99,7 @@ func main() { // Wait for the offer to be pasted offer := webrtc.SessionDescription{} - signal.Decode(signal.MustReadStdin(), &offer) + decode(readUntilNewline(), &offer) // Set the remote SessionDescription err = peerConnection.SetRemoteDescription(offer) @@ -123,7 +128,7 @@ func main() { <-gatherComplete // Output the answer in base64 so we can paste it in browser - fmt.Println(signal.Encode(*peerConnection.LocalDescription())) + fmt.Println(encode(peerConnection.LocalDescription())) // Block forever select {} @@ -146,12 +151,56 @@ func ReadLoop(d io.Reader) { // WriteLoop shows how to write to the datachannel directly func WriteLoop(d io.Writer) { for range time.NewTicker(5 * time.Second).C { - message := signal.RandSeq(messageSize) + message, err := randutil.GenerateCryptoRandomString(messageSize, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") + if err != nil { + panic(err) + } + fmt.Printf("Sending %s \n", message) + if _, err := d.Write([]byte(message)); err != nil { + panic(err) + } + } +} - _, err := d.Write([]byte(message)) - if err != nil { +// Read from stdin until we get a newline +func readUntilNewline() (in string) { + var err error + + r := bufio.NewReader(os.Stdin) + for { + in, err = r.ReadString('\n') + if err != nil && !errors.Is(err, io.EOF) { panic(err) } + + if in = strings.TrimSpace(in); len(in) > 0 { + break + } + } + + fmt.Println("") + return +} + +// JSON encode + base64 a SessionDescription +func encode(obj *webrtc.SessionDescription) string { + b, err := json.Marshal(obj) + if err != nil { + panic(err) + } + + return base64.StdEncoding.EncodeToString(b) +} + +// Decode a base64 and unmarshal JSON into a SessionDescription +func decode(in string, obj *webrtc.SessionDescription) { + b, err := base64.StdEncoding.DecodeString(in) + if err != nil { + panic(err) + } + + if err = json.Unmarshal(b, obj); err != nil { + panic(err) } } diff --git a/examples/data-channels/jsfiddle/main.go b/examples/data-channels/jsfiddle/main.go index 001b3f2226f..44faed1a593 100644 --- a/examples/data-channels/jsfiddle/main.go +++ b/examples/data-channels/jsfiddle/main.go @@ -7,11 +7,17 @@ package main import ( + "bufio" + "encoding/base64" + "encoding/json" + "errors" "fmt" + "io" + "os" + "strings" "syscall/js" "github.com/pion/webrtc/v4" - "github.com/pion/webrtc/v4/examples/internal/signal" ) func main() { @@ -63,7 +69,7 @@ func main() { }) pc.OnICECandidate(func(candidate *webrtc.ICECandidate) { if candidate != nil { - encodedDescr := signal.Encode(pc.LocalDescription()) + encodedDescr := encode(pc.LocalDescription()) el := getElementByID("localSessionDescription") el.Set("value", encodedDescr) } @@ -94,7 +100,7 @@ func main() { } descr := webrtc.SessionDescription{} - signal.Decode(sd, &descr) + decode(sd, &descr) if err := pc.SetRemoteDescription(descr); err != nil { handleError(err) } @@ -146,3 +152,45 @@ func handleError(err error) { func getElementByID(id string) js.Value { return js.Global().Get("document").Call("getElementById", id) } + +// Read from stdin until we get a newline +func readUntilNewline() (in string) { + var err error + + r := bufio.NewReader(os.Stdin) + for { + in, err = r.ReadString('\n') + if err != nil && !errors.Is(err, io.EOF) { + panic(err) + } + + if in = strings.TrimSpace(in); len(in) > 0 { + break + } + } + + fmt.Println("") + return +} + +// JSON encode + base64 a SessionDescription +func encode(obj *webrtc.SessionDescription) string { + b, err := json.Marshal(obj) + if err != nil { + panic(err) + } + + return base64.StdEncoding.EncodeToString(b) +} + +// Decode a base64 and unmarshal JSON into a SessionDescription +func decode(in string, obj *webrtc.SessionDescription) { + b, err := base64.StdEncoding.DecodeString(in) + if err != nil { + panic(err) + } + + if err = json.Unmarshal(b, obj); err != nil { + panic(err) + } +} diff --git a/examples/data-channels/main.go b/examples/data-channels/main.go index 012413f241b..217f59f9dbc 100644 --- a/examples/data-channels/main.go +++ b/examples/data-channels/main.go @@ -5,12 +5,18 @@ package main import ( + "bufio" + "encoding/base64" + "encoding/json" + "errors" "fmt" + "io" "os" + "strings" "time" + "github.com/pion/randutil" "github.com/pion/webrtc/v4" - "github.com/pion/webrtc/v4/examples/internal/signal" ) func main() { @@ -65,12 +71,14 @@ func main() { fmt.Printf("Data channel '%s'-'%d' open. Random messages will now be sent to any connected DataChannels every 5 seconds\n", d.Label(), d.ID()) for range time.NewTicker(5 * time.Second).C { - message := signal.RandSeq(15) - fmt.Printf("Sending '%s'\n", message) + message, sendErr := randutil.GenerateCryptoRandomString(15, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") + if sendErr != nil { + panic(sendErr) + } // Send the message as text - sendErr := d.SendText(message) - if sendErr != nil { + fmt.Printf("Sending '%s'\n", message) + if sendErr = d.SendText(message); sendErr != nil { panic(sendErr) } } @@ -84,7 +92,7 @@ func main() { // Wait for the offer to be pasted offer := webrtc.SessionDescription{} - signal.Decode(signal.MustReadStdin(), &offer) + decode(readUntilNewline(), &offer) // Set the remote SessionDescription err = peerConnection.SetRemoteDescription(offer) @@ -113,8 +121,50 @@ func main() { <-gatherComplete // Output the answer in base64 so we can paste it in browser - fmt.Println(signal.Encode(*peerConnection.LocalDescription())) + fmt.Println(encode(peerConnection.LocalDescription())) // Block forever select {} } + +// Read from stdin until we get a newline +func readUntilNewline() (in string) { + var err error + + r := bufio.NewReader(os.Stdin) + for { + in, err = r.ReadString('\n') + if err != nil && !errors.Is(err, io.EOF) { + panic(err) + } + + if in = strings.TrimSpace(in); len(in) > 0 { + break + } + } + + fmt.Println("") + return +} + +// JSON encode + base64 a SessionDescription +func encode(obj *webrtc.SessionDescription) string { + b, err := json.Marshal(obj) + if err != nil { + panic(err) + } + + return base64.StdEncoding.EncodeToString(b) +} + +// Decode a base64 and unmarshal JSON into a SessionDescription +func decode(in string, obj *webrtc.SessionDescription) { + b, err := base64.StdEncoding.DecodeString(in) + if err != nil { + panic(err) + } + + if err = json.Unmarshal(b, obj); err != nil { + panic(err) + } +} diff --git a/examples/insertable-streams/main.go b/examples/insertable-streams/main.go index b80229b2645..97d3eb2f17f 100644 --- a/examples/insertable-streams/main.go +++ b/examples/insertable-streams/main.go @@ -8,15 +8,18 @@ package main import ( + "bufio" "context" + "encoding/base64" + "encoding/json" "errors" "fmt" "io" "os" + "strings" "time" "github.com/pion/webrtc/v4" - "github.com/pion/webrtc/v4/examples/internal/signal" "github.com/pion/webrtc/v4/pkg/media" "github.com/pion/webrtc/v4/pkg/media/ivfreader" ) @@ -136,7 +139,7 @@ func main() { // Wait for the offer to be pasted offer := webrtc.SessionDescription{} - signal.Decode(signal.MustReadStdin(), &offer) + decode(readUntilNewline(), &offer) // Set the remote SessionDescription if err = peerConnection.SetRemoteDescription(offer); err != nil { @@ -163,8 +166,50 @@ func main() { <-gatherComplete // Output the answer in base64 so we can paste it in browser - fmt.Println(signal.Encode(*peerConnection.LocalDescription())) + fmt.Println(encode(peerConnection.LocalDescription())) // Block forever select {} } + +// Read from stdin until we get a newline +func readUntilNewline() (in string) { + var err error + + r := bufio.NewReader(os.Stdin) + for { + in, err = r.ReadString('\n') + if err != nil && !errors.Is(err, io.EOF) { + panic(err) + } + + if in = strings.TrimSpace(in); len(in) > 0 { + break + } + } + + fmt.Println("") + return +} + +// JSON encode + base64 a SessionDescription +func encode(obj *webrtc.SessionDescription) string { + b, err := json.Marshal(obj) + if err != nil { + panic(err) + } + + return base64.StdEncoding.EncodeToString(b) +} + +// Decode a base64 and unmarshal JSON into a SessionDescription +func decode(in string, obj *webrtc.SessionDescription) { + b, err := base64.StdEncoding.DecodeString(in) + if err != nil { + panic(err) + } + + if err = json.Unmarshal(b, obj); err != nil { + panic(err) + } +} diff --git a/examples/internal/signal/http.go b/examples/internal/signal/http.go deleted file mode 100644 index d705e6428f2..00000000000 --- a/examples/internal/signal/http.go +++ /dev/null @@ -1,31 +0,0 @@ -// SPDX-FileCopyrightText: 2023 The Pion community -// SPDX-License-Identifier: MIT - -package signal - -import ( - "fmt" - "io" - "net/http" - "strconv" -) - -// HTTPSDPServer starts a HTTP Server that consumes SDPs -func HTTPSDPServer(port int) chan string { - sdpChan := make(chan string) - http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { - body, _ := io.ReadAll(r.Body) - fmt.Fprintf(w, "done") - sdpChan <- string(body) - }) - - go func() { - // nolint: gosec - err := http.ListenAndServe(":"+strconv.Itoa(port), nil) - if err != nil { - panic(err) - } - }() - - return sdpChan -} diff --git a/examples/internal/signal/rand.go b/examples/internal/signal/rand.go deleted file mode 100644 index c55fc6cf54f..00000000000 --- a/examples/internal/signal/rand.go +++ /dev/null @@ -1,19 +0,0 @@ -// SPDX-FileCopyrightText: 2023 The Pion community -// SPDX-License-Identifier: MIT - -package signal - -import "github.com/pion/randutil" - -// RandSeq generates a random string to serve as dummy data -// -// It returns a deterministic sequence of values each time a program is run. -// Use rand.Seed() function in your real applications. -func RandSeq(n int) string { - val, err := randutil.GenerateCryptoRandomString(n, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") - if err != nil { - panic(err) - } - - return val -} diff --git a/examples/internal/signal/signal.go b/examples/internal/signal/signal.go deleted file mode 100644 index 14547a33c0d..00000000000 --- a/examples/internal/signal/signal.go +++ /dev/null @@ -1,113 +0,0 @@ -// SPDX-FileCopyrightText: 2023 The Pion community -// SPDX-License-Identifier: MIT - -// Package signal contains helpers to exchange the SDP session -// description between examples. -package signal - -import ( - "bufio" - "bytes" - "compress/gzip" - "encoding/base64" - "encoding/json" - "fmt" - "io" - "os" - "strings" -) - -// Allows compressing offer/answer to bypass terminal input limits. -const compress = false - -// MustReadStdin blocks until input is received from stdin -func MustReadStdin() string { - r := bufio.NewReader(os.Stdin) - - var in string - for { - var err error - in, err = r.ReadString('\n') - if err != io.EOF { - if err != nil { - panic(err) - } - } - in = strings.TrimSpace(in) - if len(in) > 0 { - break - } - } - - fmt.Println("") - - return in -} - -// Encode encodes the input in base64 -// It can optionally zip the input before encoding -func Encode(obj interface{}) string { - b, err := json.Marshal(obj) - if err != nil { - panic(err) - } - - if compress { - b = zip(b) - } - - return base64.StdEncoding.EncodeToString(b) -} - -// Decode decodes the input from base64 -// It can optionally unzip the input after decoding -func Decode(in string, obj interface{}) { - b, err := base64.StdEncoding.DecodeString(in) - if err != nil { - panic(err) - } - - if compress { - b = unzip(b) - } - - err = json.Unmarshal(b, obj) - if err != nil { - panic(err) - } -} - -func zip(in []byte) []byte { - var b bytes.Buffer - gz := gzip.NewWriter(&b) - _, err := gz.Write(in) - if err != nil { - panic(err) - } - err = gz.Flush() - if err != nil { - panic(err) - } - err = gz.Close() - if err != nil { - panic(err) - } - return b.Bytes() -} - -func unzip(in []byte) []byte { - var b bytes.Buffer - _, err := b.Write(in) - if err != nil { - panic(err) - } - r, err := gzip.NewReader(&b) - if err != nil { - panic(err) - } - res, err := io.ReadAll(r) - if err != nil { - panic(err) - } - return res -} diff --git a/examples/ortc-media/main.go b/examples/ortc-media/main.go index 39155024210..2029554e60e 100644 --- a/examples/ortc-media/main.go +++ b/examples/ortc-media/main.go @@ -8,15 +8,20 @@ package main import ( + "bufio" + "encoding/base64" + "encoding/json" "errors" "flag" "fmt" "io" + "net/http" "os" + "strconv" + "strings" "time" "github.com/pion/webrtc/v4" - "github.com/pion/webrtc/v4/examples/internal/signal" "github.com/pion/webrtc/v4/pkg/media" "github.com/pion/webrtc/v4/pkg/media/ivfreader" ) @@ -142,16 +147,16 @@ func main() { iceRole := webrtc.ICERoleControlled // Exchange the information - fmt.Println(signal.Encode(s)) + fmt.Println(encode(&s)) remoteSignal := Signal{} if *isOffer { - signalingChan := signal.HTTPSDPServer(*port) - signal.Decode(<-signalingChan, &remoteSignal) + signalingChan := httpSDPServer(*port) + decode(<-signalingChan, &remoteSignal) iceRole = webrtc.ICERoleControlling } else { - signal.Decode(signal.MustReadStdin(), &remoteSignal) + decode(readUntilNewline(), &remoteSignal) } if err = ice.SetRemoteCandidates(remoteSignal.ICECandidates); err != nil { @@ -244,3 +249,62 @@ type Signal struct { DTLSParameters webrtc.DTLSParameters `json:"dtlsParameters"` RTPSendParameters webrtc.RTPSendParameters `json:"rtpSendParameters"` } + +// Read from stdin until we get a newline +func readUntilNewline() (in string) { + var err error + + r := bufio.NewReader(os.Stdin) + for { + in, err = r.ReadString('\n') + if err != nil && !errors.Is(err, io.EOF) { + panic(err) + } + + if in = strings.TrimSpace(in); len(in) > 0 { + break + } + } + + fmt.Println("") + return +} + +// JSON encode + base64 a SessionDescription +func encode(obj *Signal) string { + b, err := json.Marshal(obj) + if err != nil { + panic(err) + } + + return base64.StdEncoding.EncodeToString(b) +} + +// Decode a base64 and unmarshal JSON into a SessionDescription +func decode(in string, obj *Signal) { + b, err := base64.StdEncoding.DecodeString(in) + if err != nil { + panic(err) + } + + if err = json.Unmarshal(b, obj); err != nil { + panic(err) + } +} + +// httpSDPServer starts a HTTP Server that consumes SDPs +func httpSDPServer(port int) chan string { + sdpChan := make(chan string) + http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + body, _ := io.ReadAll(r.Body) + fmt.Fprintf(w, "done") + sdpChan <- string(body) + }) + + go func() { + // nolint: gosec + panic(http.ListenAndServe(":"+strconv.Itoa(port), nil)) + }() + + return sdpChan +} diff --git a/examples/ortc/main.go b/examples/ortc/main.go index 60ad6c83f3c..d9981896b00 100644 --- a/examples/ortc/main.go +++ b/examples/ortc/main.go @@ -8,12 +8,21 @@ package main import ( + "bufio" + "encoding/base64" + "encoding/json" + "errors" "flag" "fmt" + "io" + "net/http" + "os" + "strconv" + "strings" "time" + "github.com/pion/randutil" "github.com/pion/webrtc/v4" - "github.com/pion/webrtc/v4/examples/internal/signal" ) func main() { @@ -103,16 +112,16 @@ func main() { iceRole := webrtc.ICERoleControlled // Exchange the information - fmt.Println(signal.Encode(s)) + fmt.Println(encode(s)) remoteSignal := Signal{} if *isOffer { - signalingChan := signal.HTTPSDPServer(*port) - signal.Decode(<-signalingChan, &remoteSignal) + signalingChan := httpSDPServer(*port) + decode(<-signalingChan, &remoteSignal) iceRole = webrtc.ICERoleControlling } else { - signal.Decode(signal.MustReadStdin(), &remoteSignal) + decode(readUntilNewline(), &remoteSignal) } if err = ice.SetRemoteCandidates(remoteSignal.ICECandidates); err != nil { @@ -175,12 +184,74 @@ func handleOnOpen(channel *webrtc.DataChannel) func() { fmt.Printf("Data channel '%s'-'%d' open. Random messages will now be sent to any connected DataChannels every 5 seconds\n", channel.Label(), channel.ID()) for range time.NewTicker(5 * time.Second).C { - message := signal.RandSeq(15) - fmt.Printf("Sending '%s' \n", message) + message, err := randutil.GenerateCryptoRandomString(15, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") + if err != nil { + panic(err) + } + fmt.Printf("Sending %s \n", message) if err := channel.SendText(message); err != nil { panic(err) } } } } + +// Read from stdin until we get a newline +func readUntilNewline() (in string) { + var err error + + r := bufio.NewReader(os.Stdin) + for { + in, err = r.ReadString('\n') + if err != nil && !errors.Is(err, io.EOF) { + panic(err) + } + + if in = strings.TrimSpace(in); len(in) > 0 { + break + } + } + + fmt.Println("") + return +} + +// JSON encode + base64 a SessionDescription +func encode(obj Signal) string { + b, err := json.Marshal(obj) + if err != nil { + panic(err) + } + + return base64.StdEncoding.EncodeToString(b) +} + +// Decode a base64 and unmarshal JSON into a SessionDescription +func decode(in string, obj *Signal) { + b, err := base64.StdEncoding.DecodeString(in) + if err != nil { + panic(err) + } + + if err = json.Unmarshal(b, obj); err != nil { + panic(err) + } +} + +// httpSDPServer starts a HTTP Server that consumes SDPs +func httpSDPServer(port int) chan string { + sdpChan := make(chan string) + http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + body, _ := io.ReadAll(r.Body) + fmt.Fprintf(w, "done") + sdpChan <- string(body) + }) + + go func() { + // nolint: gosec + panic(http.ListenAndServe(":"+strconv.Itoa(port), nil)) + }() + + return sdpChan +} diff --git a/examples/pion-to-pion/answer/main.go b/examples/pion-to-pion/answer/main.go index 97a53f58268..01dfbaa14ed 100644 --- a/examples/pion-to-pion/answer/main.go +++ b/examples/pion-to-pion/answer/main.go @@ -5,18 +5,22 @@ package main import ( + "bufio" "bytes" + "encoding/base64" "encoding/json" + "errors" "flag" "fmt" "io" "net/http" "os" + "strings" "sync" "time" + "github.com/pion/randutil" "github.com/pion/webrtc/v4" - "github.com/pion/webrtc/v4/examples/internal/signal" ) func signalCandidate(addr string, c *webrtc.ICECandidate) error { @@ -164,12 +168,14 @@ func main() { // nolint:gocognit fmt.Printf("Data channel '%s'-'%d' open. Random messages will now be sent to any connected DataChannels every 5 seconds\n", d.Label(), d.ID()) for range time.NewTicker(5 * time.Second).C { - message := signal.RandSeq(15) - fmt.Printf("Sending '%s'\n", message) + message, sendTextErr := randutil.GenerateCryptoRandomString(15, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") + if sendTextErr != nil { + panic(sendTextErr) + } // Send the message as text - sendTextErr := d.SendText(message) - if sendTextErr != nil { + fmt.Printf("Sending '%s'\n", message) + if sendTextErr = d.SendText(message); sendTextErr != nil { panic(sendTextErr) } } @@ -185,3 +191,45 @@ func main() { // nolint:gocognit // nolint: gosec panic(http.ListenAndServe(*answerAddr, nil)) } + +// Read from stdin until we get a newline +func readUntilNewline() (in string) { + var err error + + r := bufio.NewReader(os.Stdin) + for { + in, err = r.ReadString('\n') + if err != nil && !errors.Is(err, io.EOF) { + panic(err) + } + + if in = strings.TrimSpace(in); len(in) > 0 { + break + } + } + + fmt.Println("") + return +} + +// JSON encode + base64 a SessionDescription +func encode(obj *webrtc.SessionDescription) string { + b, err := json.Marshal(obj) + if err != nil { + panic(err) + } + + return base64.StdEncoding.EncodeToString(b) +} + +// Decode a base64 and unmarshal JSON into a SessionDescription +func decode(in string, obj *webrtc.SessionDescription) { + b, err := base64.StdEncoding.DecodeString(in) + if err != nil { + panic(err) + } + + if err = json.Unmarshal(b, obj); err != nil { + panic(err) + } +} diff --git a/examples/pion-to-pion/offer/main.go b/examples/pion-to-pion/offer/main.go index 236212a74a9..16332f0d235 100644 --- a/examples/pion-to-pion/offer/main.go +++ b/examples/pion-to-pion/offer/main.go @@ -15,8 +15,8 @@ import ( "sync" "time" + "github.com/pion/randutil" "github.com/pion/webrtc/v4" - "github.com/pion/webrtc/v4/examples/internal/signal" ) func signalCandidate(addr string, c *webrtc.ICECandidate) error { @@ -145,12 +145,14 @@ func main() { //nolint:gocognit fmt.Printf("Data channel '%s'-'%d' open. Random messages will now be sent to any connected DataChannels every 5 seconds\n", dataChannel.Label(), dataChannel.ID()) for range time.NewTicker(5 * time.Second).C { - message := signal.RandSeq(15) - fmt.Printf("Sending '%s'\n", message) + message, sendTextErr := randutil.GenerateCryptoRandomString(15, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") + if sendTextErr != nil { + panic(sendTextErr) + } // Send the message as text - sendTextErr := dataChannel.SendText(message) - if sendTextErr != nil { + fmt.Printf("Sending '%s'\n", message) + if sendTextErr = dataChannel.SendText(message); sendTextErr != nil { panic(sendTextErr) } } diff --git a/examples/play-from-disk/main.go b/examples/play-from-disk/main.go index 13b7f707cfa..c6bb33632ff 100644 --- a/examples/play-from-disk/main.go +++ b/examples/play-from-disk/main.go @@ -8,15 +8,18 @@ package main import ( + "bufio" "context" + "encoding/base64" + "encoding/json" "errors" "fmt" "io" "os" + "strings" "time" "github.com/pion/webrtc/v4" - "github.com/pion/webrtc/v4/examples/internal/signal" "github.com/pion/webrtc/v4/pkg/media" "github.com/pion/webrtc/v4/pkg/media/ivfreader" "github.com/pion/webrtc/v4/pkg/media/oggreader" @@ -248,7 +251,7 @@ func main() { // Wait for the offer to be pasted offer := webrtc.SessionDescription{} - signal.Decode(signal.MustReadStdin(), &offer) + decode(readUntilNewline(), &offer) // Set the remote SessionDescription if err = peerConnection.SetRemoteDescription(offer); err != nil { @@ -275,8 +278,50 @@ func main() { <-gatherComplete // Output the answer in base64 so we can paste it in browser - fmt.Println(signal.Encode(*peerConnection.LocalDescription())) + fmt.Println(encode(peerConnection.LocalDescription())) // Block forever select {} } + +// Read from stdin until we get a newline +func readUntilNewline() (in string) { + var err error + + r := bufio.NewReader(os.Stdin) + for { + in, err = r.ReadString('\n') + if err != nil && !errors.Is(err, io.EOF) { + panic(err) + } + + if in = strings.TrimSpace(in); len(in) > 0 { + break + } + } + + fmt.Println("") + return +} + +// JSON encode + base64 a SessionDescription +func encode(obj *webrtc.SessionDescription) string { + b, err := json.Marshal(obj) + if err != nil { + panic(err) + } + + return base64.StdEncoding.EncodeToString(b) +} + +// Decode a base64 and unmarshal JSON into a SessionDescription +func decode(in string, obj *webrtc.SessionDescription) { + b, err := base64.StdEncoding.DecodeString(in) + if err != nil { + panic(err) + } + + if err = json.Unmarshal(b, obj); err != nil { + panic(err) + } +} diff --git a/examples/reflect/main.go b/examples/reflect/main.go index ff859432f90..f666cf17e37 100644 --- a/examples/reflect/main.go +++ b/examples/reflect/main.go @@ -8,13 +8,18 @@ package main import ( + "bufio" + "encoding/base64" + "encoding/json" + "errors" "fmt" + "io" "os" + "strings" "github.com/pion/interceptor" "github.com/pion/interceptor/pkg/intervalpli" "github.com/pion/webrtc/v4" - "github.com/pion/webrtc/v4/examples/internal/signal" ) // nolint:gocognit @@ -102,7 +107,7 @@ func main() { // Wait for the offer to be pasted offer := webrtc.SessionDescription{} - signal.Decode(signal.MustReadStdin(), &offer) + decode(readUntilNewline(), &offer) // Set the remote SessionDescription err = peerConnection.SetRemoteDescription(offer) @@ -167,8 +172,50 @@ func main() { <-gatherComplete // Output the answer in base64 so we can paste it in browser - fmt.Println(signal.Encode(*peerConnection.LocalDescription())) + fmt.Println(encode(peerConnection.LocalDescription())) // Block forever select {} } + +// Read from stdin until we get a newline +func readUntilNewline() (in string) { + var err error + + r := bufio.NewReader(os.Stdin) + for { + in, err = r.ReadString('\n') + if err != nil && !errors.Is(err, io.EOF) { + panic(err) + } + + if in = strings.TrimSpace(in); len(in) > 0 { + break + } + } + + fmt.Println("") + return +} + +// JSON encode + base64 a SessionDescription +func encode(obj *webrtc.SessionDescription) string { + b, err := json.Marshal(obj) + if err != nil { + panic(err) + } + + return base64.StdEncoding.EncodeToString(b) +} + +// Decode a base64 and unmarshal JSON into a SessionDescription +func decode(in string, obj *webrtc.SessionDescription) { + b, err := base64.StdEncoding.DecodeString(in) + if err != nil { + panic(err) + } + + if err = json.Unmarshal(b, obj); err != nil { + panic(err) + } +} diff --git a/examples/rtcp-processing/main.go b/examples/rtcp-processing/main.go index afd01870d1e..5b5e61b2f62 100644 --- a/examples/rtcp-processing/main.go +++ b/examples/rtcp-processing/main.go @@ -8,10 +8,16 @@ package main import ( + "bufio" + "encoding/base64" + "encoding/json" + "errors" "fmt" + "io" + "os" + "strings" "github.com/pion/webrtc/v4" - "github.com/pion/webrtc/v4/examples/internal/signal" ) func main() { @@ -60,7 +66,7 @@ func main() { // Wait for the offer to be pasted offer := webrtc.SessionDescription{} - signal.Decode(signal.MustReadStdin(), &offer) + decode(readUntilNewline(), &offer) // Set the remote SessionDescription err = peerConnection.SetRemoteDescription(offer) @@ -89,8 +95,50 @@ func main() { <-gatherComplete // Output the answer in base64 so we can paste it in browser - fmt.Println(signal.Encode(*peerConnection.LocalDescription())) + fmt.Println(encode(peerConnection.LocalDescription())) // Block forever select {} } + +// Read from stdin until we get a newline +func readUntilNewline() (in string) { + var err error + + r := bufio.NewReader(os.Stdin) + for { + in, err = r.ReadString('\n') + if err != nil && !errors.Is(err, io.EOF) { + panic(err) + } + + if in = strings.TrimSpace(in); len(in) > 0 { + break + } + } + + fmt.Println("") + return +} + +// JSON encode + base64 a SessionDescription +func encode(obj *webrtc.SessionDescription) string { + b, err := json.Marshal(obj) + if err != nil { + panic(err) + } + + return base64.StdEncoding.EncodeToString(b) +} + +// Decode a base64 and unmarshal JSON into a SessionDescription +func decode(in string, obj *webrtc.SessionDescription) { + b, err := base64.StdEncoding.DecodeString(in) + if err != nil { + panic(err) + } + + if err = json.Unmarshal(b, obj); err != nil { + panic(err) + } +} diff --git a/examples/rtp-forwarder/main.go b/examples/rtp-forwarder/main.go index 57d02816f5c..12cd65d1f91 100644 --- a/examples/rtp-forwarder/main.go +++ b/examples/rtp-forwarder/main.go @@ -8,16 +8,20 @@ package main import ( + "bufio" + "encoding/base64" + "encoding/json" "errors" "fmt" + "io" "net" "os" + "strings" "github.com/pion/interceptor" "github.com/pion/interceptor/pkg/intervalpli" "github.com/pion/rtp" "github.com/pion/webrtc/v4" - "github.com/pion/webrtc/v4/examples/internal/signal" ) type udpConn struct { @@ -207,7 +211,7 @@ func main() { // Wait for the offer to be pasted offer := webrtc.SessionDescription{} - signal.Decode(signal.MustReadStdin(), &offer) + decode(readUntilNewline(), &offer) // Set the remote SessionDescription if err = peerConnection.SetRemoteDescription(offer); err != nil { @@ -234,8 +238,50 @@ func main() { <-gatherComplete // Output the answer in base64 so we can paste it in browser - fmt.Println(signal.Encode(*peerConnection.LocalDescription())) + fmt.Println(encode(peerConnection.LocalDescription())) // Block forever select {} } + +// Read from stdin until we get a newline +func readUntilNewline() (in string) { + var err error + + r := bufio.NewReader(os.Stdin) + for { + in, err = r.ReadString('\n') + if err != nil && !errors.Is(err, io.EOF) { + panic(err) + } + + if in = strings.TrimSpace(in); len(in) > 0 { + break + } + } + + fmt.Println("") + return +} + +// JSON encode + base64 a SessionDescription +func encode(obj *webrtc.SessionDescription) string { + b, err := json.Marshal(obj) + if err != nil { + panic(err) + } + + return base64.StdEncoding.EncodeToString(b) +} + +// Decode a base64 and unmarshal JSON into a SessionDescription +func decode(in string, obj *webrtc.SessionDescription) { + b, err := base64.StdEncoding.DecodeString(in) + if err != nil { + panic(err) + } + + if err = json.Unmarshal(b, obj); err != nil { + panic(err) + } +} diff --git a/examples/rtp-to-webrtc/main.go b/examples/rtp-to-webrtc/main.go index 81acf82c811..5b86f9acadb 100644 --- a/examples/rtp-to-webrtc/main.go +++ b/examples/rtp-to-webrtc/main.go @@ -8,13 +8,17 @@ package main import ( + "bufio" + "encoding/base64" + "encoding/json" "errors" "fmt" "io" "net" + "os" + "strings" "github.com/pion/webrtc/v4" - "github.com/pion/webrtc/v4/examples/internal/signal" ) func main() { @@ -85,7 +89,7 @@ func main() { // Wait for the offer to be pasted offer := webrtc.SessionDescription{} - signal.Decode(signal.MustReadStdin(), &offer) + decode(readUntilNewline(), &offer) // Set the remote SessionDescription if err = peerConnection.SetRemoteDescription(offer); err != nil { @@ -112,7 +116,7 @@ func main() { <-gatherComplete // Output the answer in base64 so we can paste it in browser - fmt.Println(signal.Encode(*peerConnection.LocalDescription())) + fmt.Println(encode(peerConnection.LocalDescription())) // Read RTP packets forever and send them to the WebRTC Client inboundRTPPacket := make([]byte, 1600) // UDP MTU @@ -132,3 +136,45 @@ func main() { } } } + +// Read from stdin until we get a newline +func readUntilNewline() (in string) { + var err error + + r := bufio.NewReader(os.Stdin) + for { + in, err = r.ReadString('\n') + if err != nil && !errors.Is(err, io.EOF) { + panic(err) + } + + if in = strings.TrimSpace(in); len(in) > 0 { + break + } + } + + fmt.Println("") + return +} + +// JSON encode + base64 a SessionDescription +func encode(obj *webrtc.SessionDescription) string { + b, err := json.Marshal(obj) + if err != nil { + panic(err) + } + + return base64.StdEncoding.EncodeToString(b) +} + +// Decode a base64 and unmarshal JSON into a SessionDescription +func decode(in string, obj *webrtc.SessionDescription) { + b, err := base64.StdEncoding.DecodeString(in) + if err != nil { + panic(err) + } + + if err = json.Unmarshal(b, obj); err != nil { + panic(err) + } +} diff --git a/examples/save-to-disk-av1/main.go b/examples/save-to-disk-av1/main.go index 29f03e59d7c..a985f637801 100644 --- a/examples/save-to-disk-av1/main.go +++ b/examples/save-to-disk-av1/main.go @@ -8,14 +8,18 @@ package main import ( + "bufio" + "encoding/base64" + "encoding/json" + "errors" "fmt" + "io" "os" "strings" "github.com/pion/interceptor" "github.com/pion/interceptor/pkg/intervalpli" "github.com/pion/webrtc/v4" - "github.com/pion/webrtc/v4/examples/internal/signal" "github.com/pion/webrtc/v4/pkg/media" "github.com/pion/webrtc/v4/pkg/media/ivfwriter" ) @@ -133,7 +137,7 @@ func main() { // Wait for the offer to be pasted offer := webrtc.SessionDescription{} - signal.Decode(signal.MustReadStdin(), &offer) + decode(readUntilNewline(), &offer) // Set the remote SessionDescription err = peerConnection.SetRemoteDescription(offer) @@ -162,8 +166,50 @@ func main() { <-gatherComplete // Output the answer in base64 so we can paste it in browser - fmt.Println(signal.Encode(*peerConnection.LocalDescription())) + fmt.Println(encode(peerConnection.LocalDescription())) // Block forever select {} } + +// Read from stdin until we get a newline +func readUntilNewline() (in string) { + var err error + + r := bufio.NewReader(os.Stdin) + for { + in, err = r.ReadString('\n') + if err != nil && !errors.Is(err, io.EOF) { + panic(err) + } + + if in = strings.TrimSpace(in); len(in) > 0 { + break + } + } + + fmt.Println("") + return +} + +// JSON encode + base64 a SessionDescription +func encode(obj *webrtc.SessionDescription) string { + b, err := json.Marshal(obj) + if err != nil { + panic(err) + } + + return base64.StdEncoding.EncodeToString(b) +} + +// Decode a base64 and unmarshal JSON into a SessionDescription +func decode(in string, obj *webrtc.SessionDescription) { + b, err := base64.StdEncoding.DecodeString(in) + if err != nil { + panic(err) + } + + if err = json.Unmarshal(b, obj); err != nil { + panic(err) + } +} diff --git a/examples/save-to-disk/main.go b/examples/save-to-disk/main.go index 1113f422081..a519aed2a28 100644 --- a/examples/save-to-disk/main.go +++ b/examples/save-to-disk/main.go @@ -8,14 +8,18 @@ package main import ( + "bufio" + "encoding/base64" + "encoding/json" + "errors" "fmt" + "io" "os" "strings" "github.com/pion/interceptor" "github.com/pion/interceptor/pkg/intervalpli" "github.com/pion/webrtc/v4" - "github.com/pion/webrtc/v4/examples/internal/signal" "github.com/pion/webrtc/v4/pkg/media" "github.com/pion/webrtc/v4/pkg/media/ivfwriter" "github.com/pion/webrtc/v4/pkg/media/oggwriter" @@ -161,7 +165,7 @@ func main() { // Wait for the offer to be pasted offer := webrtc.SessionDescription{} - signal.Decode(signal.MustReadStdin(), &offer) + decode(readUntilNewline(), &offer) // Set the remote SessionDescription err = peerConnection.SetRemoteDescription(offer) @@ -190,8 +194,50 @@ func main() { <-gatherComplete // Output the answer in base64 so we can paste it in browser - fmt.Println(signal.Encode(*peerConnection.LocalDescription())) + fmt.Println(encode(peerConnection.LocalDescription())) // Block forever select {} } + +// Read from stdin until we get a newline +func readUntilNewline() (in string) { + var err error + + r := bufio.NewReader(os.Stdin) + for { + in, err = r.ReadString('\n') + if err != nil && !errors.Is(err, io.EOF) { + panic(err) + } + + if in = strings.TrimSpace(in); len(in) > 0 { + break + } + } + + fmt.Println("") + return +} + +// JSON encode + base64 a SessionDescription +func encode(obj *webrtc.SessionDescription) string { + b, err := json.Marshal(obj) + if err != nil { + panic(err) + } + + return base64.StdEncoding.EncodeToString(b) +} + +// Decode a base64 and unmarshal JSON into a SessionDescription +func decode(in string, obj *webrtc.SessionDescription) { + b, err := base64.StdEncoding.DecodeString(in) + if err != nil { + panic(err) + } + + if err = json.Unmarshal(b, obj); err != nil { + panic(err) + } +} diff --git a/examples/simulcast/main.go b/examples/simulcast/main.go index 6054bf6a96f..25b06928906 100644 --- a/examples/simulcast/main.go +++ b/examples/simulcast/main.go @@ -8,15 +8,18 @@ package main import ( + "bufio" + "encoding/base64" + "encoding/json" "errors" "fmt" "io" "os" + "strings" "time" "github.com/pion/rtcp" "github.com/pion/webrtc/v4" - "github.com/pion/webrtc/v4/examples/internal/signal" ) // nolint:gocognit @@ -96,7 +99,7 @@ func main() { // Wait for the offer to be pasted offer := webrtc.SessionDescription{} - signal.Decode(signal.MustReadStdin(), &offer) + decode(readUntilNewline(), &offer) if err = peerConnection.SetRemoteDescription(offer); err != nil { panic(err) @@ -171,8 +174,50 @@ func main() { <-gatherComplete // Output the answer in base64 so we can paste it in browser - fmt.Println(signal.Encode(*peerConnection.LocalDescription())) + fmt.Println(encode(peerConnection.LocalDescription())) // Block forever select {} } + +// Read from stdin until we get a newline +func readUntilNewline() (in string) { + var err error + + r := bufio.NewReader(os.Stdin) + for { + in, err = r.ReadString('\n') + if err != nil && !errors.Is(err, io.EOF) { + panic(err) + } + + if in = strings.TrimSpace(in); len(in) > 0 { + break + } + } + + fmt.Println("") + return +} + +// JSON encode + base64 a SessionDescription +func encode(obj *webrtc.SessionDescription) string { + b, err := json.Marshal(obj) + if err != nil { + panic(err) + } + + return base64.StdEncoding.EncodeToString(b) +} + +// Decode a base64 and unmarshal JSON into a SessionDescription +func decode(in string, obj *webrtc.SessionDescription) { + b, err := base64.StdEncoding.DecodeString(in) + if err != nil { + panic(err) + } + + if err = json.Unmarshal(b, obj); err != nil { + panic(err) + } +} diff --git a/examples/stats/main.go b/examples/stats/main.go index 9d3a9574f8e..1cf7082eba3 100644 --- a/examples/stats/main.go +++ b/examples/stats/main.go @@ -8,13 +8,19 @@ package main import ( + "bufio" + "encoding/base64" + "encoding/json" + "errors" "fmt" + "io" + "os" + "strings" "time" "github.com/pion/interceptor" "github.com/pion/interceptor/pkg/stats" "github.com/pion/webrtc/v4" - "github.com/pion/webrtc/v4/examples/internal/signal" ) // nolint:gocognit @@ -108,7 +114,7 @@ func main() { // Wait for the offer to be pasted offer := webrtc.SessionDescription{} - signal.Decode(signal.MustReadStdin(), &offer) + decode(readUntilNewline(), &offer) // Set the remote SessionDescription err = peerConnection.SetRemoteDescription(offer) @@ -137,8 +143,50 @@ func main() { <-gatherComplete // Output the answer in base64 so we can paste it in browser - fmt.Println(signal.Encode(*peerConnection.LocalDescription())) + fmt.Println(encode(peerConnection.LocalDescription())) // Block forever select {} } + +// Read from stdin until we get a newline +func readUntilNewline() (in string) { + var err error + + r := bufio.NewReader(os.Stdin) + for { + in, err = r.ReadString('\n') + if err != nil && !errors.Is(err, io.EOF) { + panic(err) + } + + if in = strings.TrimSpace(in); len(in) > 0 { + break + } + } + + fmt.Println("") + return +} + +// JSON encode + base64 a SessionDescription +func encode(obj *webrtc.SessionDescription) string { + b, err := json.Marshal(obj) + if err != nil { + panic(err) + } + + return base64.StdEncoding.EncodeToString(b) +} + +// Decode a base64 and unmarshal JSON into a SessionDescription +func decode(in string, obj *webrtc.SessionDescription) { + b, err := base64.StdEncoding.DecodeString(in) + if err != nil { + panic(err) + } + + if err = json.Unmarshal(b, obj); err != nil { + panic(err) + } +} diff --git a/examples/swap-tracks/main.go b/examples/swap-tracks/main.go index 85935baa8fb..8b36b6120ce 100644 --- a/examples/swap-tracks/main.go +++ b/examples/swap-tracks/main.go @@ -8,16 +8,20 @@ package main import ( + "bufio" "context" + "encoding/base64" + "encoding/json" "errors" "fmt" "io" + "os" + "strings" "time" "github.com/pion/rtcp" "github.com/pion/rtp" "github.com/pion/webrtc/v4" - "github.com/pion/webrtc/v4/examples/internal/signal" ) func main() { // nolint:gocognit @@ -68,7 +72,7 @@ func main() { // nolint:gocognit // Wait for the offer to be pasted offer := webrtc.SessionDescription{} - signal.Decode(signal.MustReadStdin(), &offer) + decode(readUntilNewline(), &offer) // Set the remote SessionDescription err = peerConnection.SetRemoteDescription(offer) @@ -166,7 +170,7 @@ func main() { // nolint:gocognit // in a production application you should exchange ICE Candidates via OnICECandidate <-gatherComplete - fmt.Println(signal.Encode(*peerConnection.LocalDescription())) + fmt.Println(encode(peerConnection.LocalDescription())) // Asynchronously take all packets in the channel and write them out to our // track @@ -215,3 +219,45 @@ func main() { // nolint:gocognit fmt.Printf("Switched to track #%v\n", currTrack+1) } } + +// Read from stdin until we get a newline +func readUntilNewline() (in string) { + var err error + + r := bufio.NewReader(os.Stdin) + for { + in, err = r.ReadString('\n') + if err != nil && !errors.Is(err, io.EOF) { + panic(err) + } + + if in = strings.TrimSpace(in); len(in) > 0 { + break + } + } + + fmt.Println("") + return +} + +// JSON encode + base64 a SessionDescription +func encode(obj *webrtc.SessionDescription) string { + b, err := json.Marshal(obj) + if err != nil { + panic(err) + } + + return base64.StdEncoding.EncodeToString(b) +} + +// Decode a base64 and unmarshal JSON into a SessionDescription +func decode(in string, obj *webrtc.SessionDescription) { + b, err := base64.StdEncoding.DecodeString(in) + if err != nil { + panic(err) + } + + if err = json.Unmarshal(b, obj); err != nil { + panic(err) + } +} From ca05618c7f2fa0777dfbad03a1cb745b666716fd Mon Sep 17 00:00:00 2001 From: Eric Daniels Date: Tue, 11 Jun 2024 11:38:41 -0400 Subject: [PATCH 11/39] Reset state machine after negotiationNeededOp - Fixes #2774 --- peerconnection.go | 20 ++++++++++---------- peerconnection_go_test.go | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 10 deletions(-) diff --git a/peerconnection.go b/peerconnection.go index 69489058861..ef370664594 100644 --- a/peerconnection.go +++ b/peerconnection.go @@ -291,6 +291,16 @@ func (pc *PeerConnection) onNegotiationNeeded() { } func (pc *PeerConnection) negotiationNeededOp() { + // non-canon, reset needed state machine and run again if there was a request + defer func() { + pc.mu.Lock() + defer pc.mu.Unlock() + if pc.negotiationNeededState == negotiationNeededStateQueue { + defer pc.onNegotiationNeeded() + } + pc.negotiationNeededState = negotiationNeededStateEmpty + }() + // Don't run NegotiatedNeeded checks if OnNegotiationNeeded is not set if handler, ok := pc.onNegotiationNeededHandler.Load().(func()); !ok || handler == nil { return @@ -307,16 +317,6 @@ func (pc *PeerConnection) negotiationNeededOp() { return } - // non-canon, run again if there was a request - defer func() { - pc.mu.Lock() - defer pc.mu.Unlock() - if pc.negotiationNeededState == negotiationNeededStateQueue { - defer pc.onNegotiationNeeded() - } - pc.negotiationNeededState = negotiationNeededStateEmpty - }() - // Step 2.3 if pc.SignalingState() != SignalingStateStable { return diff --git a/peerconnection_go_test.go b/peerconnection_go_test.go index e36c62f75b4..2dde30694a9 100644 --- a/peerconnection_go_test.go +++ b/peerconnection_go_test.go @@ -1606,3 +1606,41 @@ func TestPeerConnectionState(t *testing.T) { assert.NoError(t, pc.Close()) assert.Equal(t, PeerConnectionStateClosed, pc.ConnectionState()) } + +// See https://github.com/pion/webrtc/issues/2774 +func TestNegotiationNeededAddedAfterOpQueueDone(t *testing.T) { + lim := test.TimeOut(time.Second * 30) + defer lim.Stop() + + report := test.CheckRoutines(t) + defer report() + + pc, err := NewPeerConnection(Configuration{}) + if err != nil { + t.Error(err.Error()) + } + + var wg sync.WaitGroup + wg.Add(1) + + _, err = pc.CreateDataChannel("initial_data_channel", nil) + assert.NoError(t, err) + + // after there are no ops left in the queue, a previously faulty version + // of negotiationNeededOp would keep the negotiation needed state in + // negotiationNeededStateQueue which will cause all subsequent + // onNegotiationNeeded calls to never queue again, only if + // OnNegotiationNeeded has not been set yet. + for !pc.ops.IsEmpty() { + time.Sleep(time.Millisecond) + } + + pc.OnNegotiationNeeded(wg.Done) + + _, err = pc.CreateDataChannel("another_data_channel", nil) + assert.NoError(t, err) + + wg.Wait() + + assert.NoError(t, pc.Close()) +} From d56bead6a6b6341348cb29b3ceeb9736020df46d Mon Sep 17 00:00:00 2001 From: aler9 <46489434+aler9@users.noreply.github.com> Date: Mon, 10 Jun 2024 12:24:29 +0200 Subject: [PATCH 12/39] Fix race condition in test In TestPeerConnection_Zero_PayloadType, WriteSample might be called after closePairNow, resulting in failure. This patch fixes the issue. --- peerconnection_media_test.go | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/peerconnection_media_test.go b/peerconnection_media_test.go index 43bdd7ed429..1a56dfb4bb7 100644 --- a/peerconnection_media_test.go +++ b/peerconnection_media_test.go @@ -1752,26 +1752,28 @@ func TestPeerConnection_Zero_PayloadType(t *testing.T) { assert.NoError(t, signalPair(pcOffer, pcAnswer)) - onTrackFired, onTrackFiredCancel := context.WithCancel(context.Background()) + trackFired := make(chan struct{}) + pcAnswer.OnTrack(func(track *TrackRemote, _ *RTPReceiver) { require.Equal(t, track.Codec().MimeType, MimeTypePCMU) - onTrackFiredCancel() + close(trackFired) }) - go func() { + func() { ticker := time.NewTicker(20 * time.Millisecond) defer ticker.Stop() - select { - case <-onTrackFired.Done(): - return - case <-ticker.C: - if routineErr := audioTrack.WriteSample(media.Sample{Data: []byte{0x00}, Duration: time.Second}); routineErr != nil { - fmt.Println(routineErr) + for { + select { + case <-trackFired: + return + case <-ticker.C: + if routineErr := audioTrack.WriteSample(media.Sample{Data: []byte{0x00}, Duration: time.Second}); routineErr != nil { + fmt.Println(routineErr) + } } } }() - <-onTrackFired.Done() closePairNow(t, pcOffer, pcAnswer) } From 1bb9fa90205f3968ca3028d36d3fae3c17f670cc Mon Sep 17 00:00:00 2001 From: Eric Daniels Date: Mon, 24 Jun 2024 10:54:41 -0400 Subject: [PATCH 13/39] Make onNegotiationNeeded conform to spec - Removes non-canon logic --- operations.go | 17 +++++++-- operations_test.go | 22 ++++++++++-- peerconnection.go | 74 +++++++++++++++++---------------------- peerconnection_go_test.go | 38 -------------------- peerconnectionstate.go | 11 ------ 5 files changed, 66 insertions(+), 96 deletions(-) diff --git a/operations.go b/operations.go index d9dca4a8e48..bc366ac34db 100644 --- a/operations.go +++ b/operations.go @@ -16,11 +16,19 @@ type operations struct { mu sync.Mutex busy bool ops *list.List + + updateNegotiationNeededFlagOnEmptyChain *atomicBool + onNegotiationNeeded func() } -func newOperations() *operations { +func newOperations( + updateNegotiationNeededFlagOnEmptyChain *atomicBool, + onNegotiationNeeded func(), +) *operations { return &operations{ - ops: list.New(), + ops: list.New(), + updateNegotiationNeededFlagOnEmptyChain: updateNegotiationNeededFlagOnEmptyChain, + onNegotiationNeeded: onNegotiationNeeded, } } @@ -93,4 +101,9 @@ func (o *operations) start() { fn() fn = o.pop() } + if !o.updateNegotiationNeededFlagOnEmptyChain.get() { + return + } + o.updateNegotiationNeededFlagOnEmptyChain.set(false) + o.onNegotiationNeeded() } diff --git a/operations_test.go b/operations_test.go index f426549f6c5..428c2b4df97 100644 --- a/operations_test.go +++ b/operations_test.go @@ -4,19 +4,31 @@ package webrtc import ( + "sync" "testing" "github.com/stretchr/testify/assert" ) func TestOperations_Enqueue(t *testing.T) { - ops := newOperations() - for i := 0; i < 100; i++ { + updateNegotiationNeededFlagOnEmptyChain := &atomicBool{} + onNegotiationNeededCalledCount := 0 + var onNegotiationNeededCalledCountMu sync.Mutex + ops := newOperations(updateNegotiationNeededFlagOnEmptyChain, func() { + onNegotiationNeededCalledCountMu.Lock() + onNegotiationNeededCalledCount++ + onNegotiationNeededCalledCountMu.Unlock() + }) + for resultSet := 0; resultSet < 100; resultSet++ { results := make([]int, 16) + resultSetCopy := resultSet for i := range results { func(j int) { ops.Enqueue(func() { results[j] = j * j + if resultSetCopy > 50 { + updateNegotiationNeededFlagOnEmptyChain.set(true) + } }) }(i) } @@ -26,9 +38,13 @@ func TestOperations_Enqueue(t *testing.T) { assert.Equal(t, len(expected), len(results)) assert.Equal(t, expected, results) } + onNegotiationNeededCalledCountMu.Lock() + defer onNegotiationNeededCalledCountMu.Unlock() + assert.NotEqual(t, onNegotiationNeededCalledCount, 0) } func TestOperations_Done(*testing.T) { - ops := newOperations() + ops := newOperations(&atomicBool{}, func() { + }) ops.Done() } diff --git a/peerconnection.go b/peerconnection.go index ef370664594..3ae8b732c66 100644 --- a/peerconnection.go +++ b/peerconnection.go @@ -55,9 +55,9 @@ type PeerConnection struct { idpLoginURL *string - isClosed *atomicBool - isNegotiationNeeded *atomicBool - negotiationNeededState negotiationNeededState + isClosed *atomicBool + isNegotiationNeeded *atomicBool + updateNegotiationNeededFlagOnEmptyChain *atomicBool lastOffer string lastAnswer string @@ -104,6 +104,7 @@ func (api *API) NewPeerConnection(configuration Configuration) (*PeerConnection, // https://w3c.github.io/webrtc-pc/#constructor (Step #2) // Some variables defined explicitly despite their implicit zero values to // allow better readability to understand what is happening. + pc := &PeerConnection{ statsID: fmt.Sprintf("PeerConnection-%d", time.Now().UnixNano()), configuration: Configuration{ @@ -114,18 +115,19 @@ func (api *API) NewPeerConnection(configuration Configuration) (*PeerConnection, Certificates: []Certificate{}, ICECandidatePoolSize: 0, }, - ops: newOperations(), - isClosed: &atomicBool{}, - isNegotiationNeeded: &atomicBool{}, - negotiationNeededState: negotiationNeededStateEmpty, - lastOffer: "", - lastAnswer: "", - greaterMid: -1, - signalingState: SignalingStateStable, + isClosed: &atomicBool{}, + isNegotiationNeeded: &atomicBool{}, + updateNegotiationNeededFlagOnEmptyChain: &atomicBool{}, + lastOffer: "", + lastAnswer: "", + greaterMid: -1, + signalingState: SignalingStateStable, api: api, log: api.settingEngine.LoggerFactory.NewLogger("pc"), } + pc.ops = newOperations(pc.updateNegotiationNeededFlagOnEmptyChain, pc.onNegotiationNeeded) + pc.iceConnectionState.Store(ICEConnectionStateNew) pc.connectionState.Store(PeerConnectionStateNew) @@ -277,66 +279,54 @@ func (pc *PeerConnection) OnNegotiationNeeded(f func()) { // onNegotiationNeeded enqueues negotiationNeededOp if necessary // caller of this method should hold `pc.mu` lock +// https://www.w3.org/TR/webrtc/#dfn-update-the-negotiation-needed-flag func (pc *PeerConnection) onNegotiationNeeded() { - // https://w3c.github.io/webrtc-pc/#updating-the-negotiation-needed-flag - // non-canon step 1 - if pc.negotiationNeededState == negotiationNeededStateRun { - pc.negotiationNeededState = negotiationNeededStateQueue - return - } else if pc.negotiationNeededState == negotiationNeededStateQueue { + // 4.7.3.1 If the length of connection.[[Operations]] is not 0, then set + // connection.[[UpdateNegotiationNeededFlagOnEmptyChain]] to true, and abort these steps. + if !pc.ops.IsEmpty() { + pc.updateNegotiationNeededFlagOnEmptyChain.set(true) return } - pc.negotiationNeededState = negotiationNeededStateRun pc.ops.Enqueue(pc.negotiationNeededOp) } +// https://www.w3.org/TR/webrtc/#dfn-update-the-negotiation-needed-flag func (pc *PeerConnection) negotiationNeededOp() { - // non-canon, reset needed state machine and run again if there was a request - defer func() { - pc.mu.Lock() - defer pc.mu.Unlock() - if pc.negotiationNeededState == negotiationNeededStateQueue { - defer pc.onNegotiationNeeded() - } - pc.negotiationNeededState = negotiationNeededStateEmpty - }() - - // Don't run NegotiatedNeeded checks if OnNegotiationNeeded is not set - if handler, ok := pc.onNegotiationNeededHandler.Load().(func()); !ok || handler == nil { - return - } - - // https://www.w3.org/TR/webrtc/#updating-the-negotiation-needed-flag - // Step 2.1 + // 4.7.3.2.1 If connection.[[IsClosed]] is true, abort these steps. if pc.isClosed.get() { return } - // non-canon step 2.2 + + // 4.7.3.2.2 If the length of connection.[[Operations]] is not 0, + // then set connection.[[UpdateNegotiationNeededFlagOnEmptyChain]] to + // true, and abort these steps. if !pc.ops.IsEmpty() { - pc.ops.Enqueue(pc.negotiationNeededOp) + pc.updateNegotiationNeededFlagOnEmptyChain.set(true) return } - // Step 2.3 + // 4.7.3.2.3 If connection's signaling state is not "stable", abort these steps. if pc.SignalingState() != SignalingStateStable { return } - // Step 2.4 + // 4.7.3.2.4 If the result of checking if negotiation is needed is false, + // clear the negotiation-needed flag by setting connection.[[NegotiationNeeded]] + // to false, and abort these steps. if !pc.checkNegotiationNeeded() { pc.isNegotiationNeeded.set(false) return } - // Step 2.5 + // 4.7.3.2.5 If connection.[[NegotiationNeeded]] is already true, abort these steps. if pc.isNegotiationNeeded.get() { return } - // Step 2.6 + // 4.7.3.2.6 Set connection.[[NegotiationNeeded]] to true. pc.isNegotiationNeeded.set(true) - // Step 2.7 + // 4.7.3.2.7 Fire an event named negotiationneeded at connection. if handler, ok := pc.onNegotiationNeededHandler.Load().(func()); ok && handler != nil { handler() } diff --git a/peerconnection_go_test.go b/peerconnection_go_test.go index 2dde30694a9..e36c62f75b4 100644 --- a/peerconnection_go_test.go +++ b/peerconnection_go_test.go @@ -1606,41 +1606,3 @@ func TestPeerConnectionState(t *testing.T) { assert.NoError(t, pc.Close()) assert.Equal(t, PeerConnectionStateClosed, pc.ConnectionState()) } - -// See https://github.com/pion/webrtc/issues/2774 -func TestNegotiationNeededAddedAfterOpQueueDone(t *testing.T) { - lim := test.TimeOut(time.Second * 30) - defer lim.Stop() - - report := test.CheckRoutines(t) - defer report() - - pc, err := NewPeerConnection(Configuration{}) - if err != nil { - t.Error(err.Error()) - } - - var wg sync.WaitGroup - wg.Add(1) - - _, err = pc.CreateDataChannel("initial_data_channel", nil) - assert.NoError(t, err) - - // after there are no ops left in the queue, a previously faulty version - // of negotiationNeededOp would keep the negotiation needed state in - // negotiationNeededStateQueue which will cause all subsequent - // onNegotiationNeeded calls to never queue again, only if - // OnNegotiationNeeded has not been set yet. - for !pc.ops.IsEmpty() { - time.Sleep(time.Millisecond) - } - - pc.OnNegotiationNeeded(wg.Done) - - _, err = pc.CreateDataChannel("another_data_channel", nil) - assert.NoError(t, err) - - wg.Wait() - - assert.NoError(t, pc.Close()) -} diff --git a/peerconnectionstate.go b/peerconnectionstate.go index 9ff712112a7..0ff24afca04 100644 --- a/peerconnectionstate.go +++ b/peerconnectionstate.go @@ -87,14 +87,3 @@ func (t PeerConnectionState) String() string { return ErrUnknownType.Error() } } - -type negotiationNeededState int - -const ( - // NegotiationNeededStateEmpty not running and queue is empty - negotiationNeededStateEmpty = iota - // NegotiationNeededStateEmpty running and queue is empty - negotiationNeededStateRun - // NegotiationNeededStateEmpty running and queue - negotiationNeededStateQueue -) From fc3521e85c997a6f04f59e49470b52eb7afc06d4 Mon Sep 17 00:00:00 2001 From: Konstantin Date: Mon, 24 Jun 2024 15:44:47 +0200 Subject: [PATCH 14/39] Track bytesReadSuccesfully in oggreader --- pkg/media/oggreader/oggreader.go | 2 ++ pkg/media/oggreader/oggreader_test.go | 2 ++ 2 files changed, 4 insertions(+) diff --git a/pkg/media/oggreader/oggreader.go b/pkg/media/oggreader/oggreader.go index 50d1a7cd87b..5aaeb8b94eb 100644 --- a/pkg/media/oggreader/oggreader.go +++ b/pkg/media/oggreader/oggreader.go @@ -189,6 +189,8 @@ func (o *OggReader) ParseNextPage() ([]byte, *OggPageHeader, error) { } } + o.bytesReadSuccesfully += int64(len(h) + len(sizeBuffer) + len(payload)) + return payload, pageHeader, nil } diff --git a/pkg/media/oggreader/oggreader_test.go b/pkg/media/oggreader/oggreader_test.go index 787f5573b4e..cbb151e9e5f 100644 --- a/pkg/media/oggreader/oggreader_test.go +++ b/pkg/media/oggreader/oggreader_test.go @@ -46,10 +46,12 @@ func TestOggReader_ParseNextPage(t *testing.T) { reader, _, err := NewWith(ogg) assert.NoError(t, err) assert.NotNil(t, reader) + assert.Equal(t, int64(47), reader.bytesReadSuccesfully) payload, _, err := reader.ParseNextPage() assert.Equal(t, []byte{0x98, 0x36, 0xbe, 0x88, 0x9e}, payload) assert.NoError(t, err) + assert.Equal(t, int64(80), reader.bytesReadSuccesfully) _, _, err = reader.ParseNextPage() assert.Equal(t, err, io.EOF) From 31c2c0dfc1c50fc86ee10f61f665e69e4be1c5e1 Mon Sep 17 00:00:00 2001 From: aler9 <46489434+aler9@users.noreply.github.com> Date: Mon, 27 May 2024 22:43:33 +0200 Subject: [PATCH 15/39] Fix AV1 and VP9 codec matching Currently, AV1 or VP9 formats are matched regardless of the profile parameter. This was not noticeable until browsers started advertising multiple VP9 formats at the same time, each with a different profile ID, in order to allow the counterpart to send different streams on the basis of supported profiles. This causes two issues: first, the library includes in the SDP all formats passed by the browser, regardless of the fact that the profile ID is registered in the API or not. Then, the library is unable to choose the correct format for streaming, causing an intermittent failure. This patch fixes the matching algorithm and also covers the case in which the profile ID is missing, by using values dictated by specifications. Tests were refactored since previous ones covered the same lines multiple times. --- internal/fmtp/av1.go | 40 +++ internal/fmtp/fmtp.go | 45 +++- internal/fmtp/fmtp_test.go | 538 +++++++++++++++++++++++++++++++------ internal/fmtp/h264_test.go | 145 ---------- internal/fmtp/vp9.go | 40 +++ 5 files changed, 573 insertions(+), 235 deletions(-) create mode 100644 internal/fmtp/av1.go delete mode 100644 internal/fmtp/h264_test.go create mode 100644 internal/fmtp/vp9.go diff --git a/internal/fmtp/av1.go b/internal/fmtp/av1.go new file mode 100644 index 00000000000..59d8b31a8d1 --- /dev/null +++ b/internal/fmtp/av1.go @@ -0,0 +1,40 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + +package fmtp + +type av1FMTP struct { + parameters map[string]string +} + +func (h *av1FMTP) MimeType() string { + return "video/av1" +} + +func (h *av1FMTP) Match(b FMTP) bool { + c, ok := b.(*av1FMTP) + if !ok { + return false + } + + // AV1 RTP specification: + // If the profile parameter is not present, it MUST be inferred to be 0 (“Main” profile). + hProfile, ok := h.parameters["profile"] + if !ok { + hProfile = "0" + } + cProfile, ok := c.parameters["profile"] + if !ok { + cProfile = "0" + } + if hProfile != cProfile { + return false + } + + return true +} + +func (h *av1FMTP) Parameter(key string) (string, bool) { + v, ok := h.parameters[key] + return v, ok +} diff --git a/internal/fmtp/fmtp.go b/internal/fmtp/fmtp.go index 5461c019bd2..f515a648d01 100644 --- a/internal/fmtp/fmtp.go +++ b/internal/fmtp/fmtp.go @@ -8,6 +8,22 @@ import ( "strings" ) +func parseParameters(line string) map[string]string { + parameters := make(map[string]string) + + for _, p := range strings.Split(line, ";") { + pp := strings.SplitN(strings.TrimSpace(p), "=", 2) + key := strings.ToLower(pp[0]) + var value string + if len(pp) > 1 { + value = pp[1] + } + parameters[key] = value + } + + return parameters +} + // FMTP interface for implementing custom // FMTP parsers based on MimeType type FMTP interface { @@ -23,29 +39,30 @@ type FMTP interface { } // Parse parses an fmtp string based on the MimeType -func Parse(mimetype, line string) FMTP { +func Parse(mimeType, line string) FMTP { var f FMTP - parameters := make(map[string]string) - - for _, p := range strings.Split(line, ";") { - pp := strings.SplitN(strings.TrimSpace(p), "=", 2) - key := strings.ToLower(pp[0]) - var value string - if len(pp) > 1 { - value = pp[1] - } - parameters[key] = value - } + parameters := parseParameters(line) switch { - case strings.EqualFold(mimetype, "video/h264"): + case strings.EqualFold(mimeType, "video/h264"): f = &h264FMTP{ parameters: parameters, } + + case strings.EqualFold(mimeType, "video/vp9"): + f = &vp9FMTP{ + parameters: parameters, + } + + case strings.EqualFold(mimeType, "video/av1"): + f = &av1FMTP{ + parameters: parameters, + } + default: f = &genericFMTP{ - mimeType: mimetype, + mimeType: mimeType, parameters: parameters, } } diff --git a/internal/fmtp/fmtp_test.go b/internal/fmtp/fmtp_test.go index 2bc2bc873e7..96c1a8e7434 100644 --- a/internal/fmtp/fmtp_test.go +++ b/internal/fmtp/fmtp_test.go @@ -8,127 +8,513 @@ import ( "testing" ) -func TestGenericParseFmtp(t *testing.T) { - testCases := map[string]struct { - input string +func TestParseParameters(t *testing.T) { + for _, ca := range []struct { + name string + line string + parameters map[string]string + }{ + { + "one param", + "key-name=value", + map[string]string{ + "key-name": "value", + }, + }, + { + "one param with white spaces", + "\tkey-name=value ", + map[string]string{ + "key-name": "value", + }, + }, + { + "two params", + "key-name=value;key2=value2", + map[string]string{ + "key-name": "value", + "key2": "value2", + }, + }, + { + "two params with white spaces", + "key-name=value; \n\tkey2=value2 ", + map[string]string{ + "key-name": "value", + "key2": "value2", + }, + }, + } { + t.Run(ca.name, func(t *testing.T) { + parameters := parseParameters(ca.line) + if !reflect.DeepEqual(parameters, ca.parameters) { + t.Errorf("expected '%v', got '%v'", ca.parameters, parameters) + } + }) + } +} + +func TestParse(t *testing.T) { + for _, ca := range []struct { + name string + mimeType string + line string expected FMTP }{ - "OneParam": { - input: "key-name=value", - expected: &genericFMTP{ + { + "generic", + "generic", + "key-name=value", + &genericFMTP{ mimeType: "generic", parameters: map[string]string{ "key-name": "value", }, }, }, - "OneParamWithWhiteSpeces": { - input: "\tkey-name=value ", - expected: &genericFMTP{ + { + "generic case normalization", + "generic", + "Key=value", + &genericFMTP{ mimeType: "generic", + parameters: map[string]string{ + "key": "value", + }, + }, + }, + { + "h264", + "video/h264", + "key-name=value", + &h264FMTP{ parameters: map[string]string{ "key-name": "value", }, }, }, - "TwoParams": { - input: "key-name=value;key2=value2", - expected: &genericFMTP{ - mimeType: "generic", + { + "vp9", + "video/vp9", + "key-name=value", + &vp9FMTP{ parameters: map[string]string{ "key-name": "value", - "key2": "value2", }, }, }, - "TwoParamsWithWhiteSpeces": { - input: "key-name=value; \n\tkey2=value2 ", - expected: &genericFMTP{ - mimeType: "generic", + { + "av1", + "video/av1", + "key-name=value", + &av1FMTP{ parameters: map[string]string{ "key-name": "value", - "key2": "value2", }, }, }, - } - for name, testCase := range testCases { - testCase := testCase - t.Run(name, func(t *testing.T) { - f := Parse("generic", testCase.input) - if !reflect.DeepEqual(testCase.expected, f) { - t.Errorf("Expected Fmtp params: %v, got: %v", testCase.expected, f) + } { + t.Run(ca.name, func(t *testing.T) { + f := Parse(ca.mimeType, ca.line) + if !reflect.DeepEqual(ca.expected, f) { + t.Errorf("expected '%v', got '%v'", ca.expected, f) } - if f.MimeType() != "generic" { - t.Errorf("Expected MimeType of generic, got: %s", f.MimeType()) + if f.MimeType() != ca.mimeType { + t.Errorf("Expected '%v', got '%s'", ca.mimeType, f.MimeType()) } }) } } -func TestGenericFmtpCompare(t *testing.T) { +func TestMatch(t *testing.T) { consistString := map[bool]string{true: "consist", false: "inconsist"} - testCases := map[string]struct { - a, b string + for _, ca := range []struct { + name string + a FMTP + b FMTP consist bool }{ - "Equal": { - a: "key1=value1;key2=value2;key3=value3", - b: "key1=value1;key2=value2;key3=value3", - consist: true, - }, - "EqualWithWhitespaceVariants": { - a: "key1=value1;key2=value2;key3=value3", - b: " key1=value1; \nkey2=value2;\t\nkey3=value3", - consist: true, - }, - "EqualWithCase": { - a: "key1=value1;key2=value2;key3=value3", - b: "key1=value1;key2=Value2;Key3=value3", - consist: true, - }, - "OneHasExtraParam": { - a: "key1=value1;key2=value2;key3=value3", - b: "key1=value1;key2=value2;key3=value3;key4=value4", - consist: true, - }, - "Inconsistent": { - a: "key1=value1;key2=value2;key3=value3", - b: "key1=value1;key2=different_value;key3=value3", - consist: false, - }, - "Inconsistent_OneHasExtraParam": { - a: "key1=value1;key2=value2;key3=value3;key4=value4", - b: "key1=value1;key2=different_value;key3=value3", - consist: false, + { + "generic equal", + &genericFMTP{ + mimeType: "generic", + parameters: map[string]string{ + "key1": "value1", + "key2": "value2", + "key3": "value3", + }, + }, + &genericFMTP{ + mimeType: "generic", + parameters: map[string]string{ + "key1": "value1", + "key2": "value2", + "key3": "value3", + }, + }, + true, }, - } - for name, testCase := range testCases { - testCase := testCase - check := func(t *testing.T, a, b string) { - aa := Parse("", a) - bb := Parse("", b) - c := aa.Match(bb) - if c != testCase.consist { + { + "generic one extra param", + &genericFMTP{ + mimeType: "generic", + parameters: map[string]string{ + "key1": "value1", + "key2": "value2", + "key3": "value3", + }, + }, + &genericFMTP{ + mimeType: "generic", + parameters: map[string]string{ + "key1": "value1", + "key2": "value2", + "key3": "value3", + "key4": "value4", + }, + }, + true, + }, + { + "generic inconsistent different kind", + &genericFMTP{ + mimeType: "generic", + parameters: map[string]string{ + "key1": "value1", + "key2": "value2", + "key3": "value3", + }, + }, + &h264FMTP{}, + false, + }, + { + "generic inconsistent different mime type", + &genericFMTP{ + mimeType: "generic1", + parameters: map[string]string{ + "key1": "value1", + "key2": "value2", + "key3": "value3", + }, + }, + &genericFMTP{ + mimeType: "generic2", + parameters: map[string]string{ + "key1": "value1", + "key2": "value2", + "key3": "value3", + }, + }, + false, + }, + { + "generic inconsistent different parameters", + &genericFMTP{ + mimeType: "generic", + parameters: map[string]string{ + "key1": "value1", + "key2": "value2", + "key3": "value3", + }, + }, + &genericFMTP{ + mimeType: "generic", + parameters: map[string]string{ + "key1": "value1", + "key2": "different_value", + "key3": "value3", + }, + }, + false, + }, + { + "h264 equal", + &h264FMTP{ + parameters: map[string]string{ + "level-asymmetry-allowed": "1", + "packetization-mode": "1", + "profile-level-id": "42e01f", + }, + }, + &h264FMTP{ + parameters: map[string]string{ + "level-asymmetry-allowed": "1", + "packetization-mode": "1", + "profile-level-id": "42e01f", + }, + }, + true, + }, + { + "h264 one extra param", + &h264FMTP{ + parameters: map[string]string{ + "level-asymmetry-allowed": "1", + "packetization-mode": "1", + "profile-level-id": "42e01f", + }, + }, + &h264FMTP{ + parameters: map[string]string{ + "packetization-mode": "1", + "profile-level-id": "42e01f", + }, + }, + true, + }, + { + "h264 different profile level ids version", + &h264FMTP{ + parameters: map[string]string{ + "packetization-mode": "1", + "profile-level-id": "42e01f", + }, + }, + &h264FMTP{ + parameters: map[string]string{ + "packetization-mode": "1", + "profile-level-id": "42e029", + }, + }, + true, + }, + { + "h264 inconsistent different kind", + &h264FMTP{ + parameters: map[string]string{ + "packetization-mode": "0", + "profile-level-id": "42e01f", + }, + }, + &genericFMTP{}, + false, + }, + { + "h264 inconsistent different parameters", + &h264FMTP{ + parameters: map[string]string{ + "packetization-mode": "0", + "profile-level-id": "42e01f", + }, + }, + &h264FMTP{ + parameters: map[string]string{ + "packetization-mode": "1", + "profile-level-id": "42e01f", + }, + }, + false, + }, + { + "h264 inconsistent missing packetization mode", + &h264FMTP{ + parameters: map[string]string{ + "packetization-mode": "0", + "profile-level-id": "42e01f", + }, + }, + &h264FMTP{ + parameters: map[string]string{ + "profile-level-id": "42e01f", + }, + }, + false, + }, + { + "h264 inconsistent missing profile level id", + &h264FMTP{ + parameters: map[string]string{ + "packetization-mode": "1", + "profile-level-id": "42e01f", + }, + }, + &h264FMTP{ + parameters: map[string]string{ + "packetization-mode": "1", + }, + }, + false, + }, + { + "h264 inconsistent invalid profile level id", + &h264FMTP{ + parameters: map[string]string{ + "packetization-mode": "1", + "profile-level-id": "42e029", + }, + }, + &h264FMTP{ + parameters: map[string]string{ + "packetization-mode": "1", + "profile-level-id": "41e029", + }, + }, + false, + }, + { + "vp9 equal", + &vp9FMTP{ + parameters: map[string]string{ + "profile-id": "1", + }, + }, + &vp9FMTP{ + parameters: map[string]string{ + "profile-id": "1", + }, + }, + true, + }, + { + "vp9 missing profile", + &vp9FMTP{ + parameters: map[string]string{}, + }, + &vp9FMTP{ + parameters: map[string]string{}, + }, + true, + }, + { + "vp9 inferred profile", + &vp9FMTP{ + parameters: map[string]string{ + "profile-id": "0", + }, + }, + &vp9FMTP{ + parameters: map[string]string{}, + }, + true, + }, + { + "vp9 inconsistent different kind", + &vp9FMTP{ + parameters: map[string]string{ + "profile-id": "0", + }, + }, + &genericFMTP{}, + false, + }, + { + "vp9 inconsistent different profile", + &vp9FMTP{ + parameters: map[string]string{ + "profile-id": "0", + }, + }, + &vp9FMTP{ + parameters: map[string]string{ + "profile-id": "1", + }, + }, + false, + }, + { + "vp9 inconsistent different inferred profile", + &vp9FMTP{ + parameters: map[string]string{}, + }, + &vp9FMTP{ + parameters: map[string]string{ + "profile-id": "1", + }, + }, + false, + }, + { + "av1 equal", + &av1FMTP{ + parameters: map[string]string{ + "profile": "1", + }, + }, + &av1FMTP{ + parameters: map[string]string{ + "profile": "1", + }, + }, + true, + }, + { + "av1 missing profile", + &av1FMTP{ + parameters: map[string]string{}, + }, + &av1FMTP{ + parameters: map[string]string{}, + }, + true, + }, + { + "av1 inferred profile", + &av1FMTP{ + parameters: map[string]string{ + "profile": "0", + }, + }, + &av1FMTP{ + parameters: map[string]string{}, + }, + true, + }, + { + "av1 inconsistent different kind", + &av1FMTP{ + parameters: map[string]string{ + "profile": "0", + }, + }, + &genericFMTP{}, + false, + }, + { + "av1 inconsistent different profile", + &av1FMTP{ + parameters: map[string]string{ + "profile": "0", + }, + }, + &av1FMTP{ + parameters: map[string]string{ + "profile": "1", + }, + }, + false, + }, + { + "av1 inconsistent different inferred profile", + &av1FMTP{ + parameters: map[string]string{}, + }, + &av1FMTP{ + parameters: map[string]string{ + "profile": "1", + }, + }, + false, + }, + } { + t.Run(ca.name, func(t *testing.T) { + c := ca.a.Match(ca.b) + if c != ca.consist { t.Errorf( "'%s' and '%s' are expected to be %s, but treated as %s", - a, b, consistString[testCase.consist], consistString[c], + ca.a, ca.b, consistString[ca.consist], consistString[c], ) } - // test reverse case here - c = bb.Match(aa) - if c != testCase.consist { + c = ca.b.Match(ca.a) + if c != ca.consist { t.Errorf( "'%s' and '%s' are expected to be %s, but treated as %s", - a, b, consistString[testCase.consist], consistString[c], + ca.a, ca.b, consistString[ca.consist], consistString[c], ) } - } - t.Run(name, func(t *testing.T) { - check(t, testCase.a, testCase.b) }) } } diff --git a/internal/fmtp/h264_test.go b/internal/fmtp/h264_test.go deleted file mode 100644 index 93da80d5e71..00000000000 --- a/internal/fmtp/h264_test.go +++ /dev/null @@ -1,145 +0,0 @@ -// SPDX-FileCopyrightText: 2023 The Pion community -// SPDX-License-Identifier: MIT - -package fmtp - -import ( - "reflect" - "testing" -) - -func TestH264FMTPParse(t *testing.T) { - testCases := map[string]struct { - input string - expected FMTP - }{ - "OneParam": { - input: "key-name=value", - expected: &h264FMTP{ - parameters: map[string]string{ - "key-name": "value", - }, - }, - }, - "OneParamWithWhiteSpeces": { - input: "\tkey-name=value ", - expected: &h264FMTP{ - parameters: map[string]string{ - "key-name": "value", - }, - }, - }, - "TwoParams": { - input: "key-name=value;key2=value2", - expected: &h264FMTP{ - parameters: map[string]string{ - "key-name": "value", - "key2": "value2", - }, - }, - }, - "TwoParamsWithWhiteSpeces": { - input: "key-name=value; \n\tkey2=value2 ", - expected: &h264FMTP{ - parameters: map[string]string{ - "key-name": "value", - "key2": "value2", - }, - }, - }, - } - for name, testCase := range testCases { - testCase := testCase - t.Run(name, func(t *testing.T) { - f := Parse("video/h264", testCase.input) - if !reflect.DeepEqual(testCase.expected, f) { - t.Errorf("Expected Fmtp params: %v, got: %v", testCase.expected, f) - } - - if f.MimeType() != "video/h264" { - t.Errorf("Expected MimeType of video/h264, got: %s", f.MimeType()) - } - }) - } -} - -func TestH264FMTPCompare(t *testing.T) { - consistString := map[bool]string{true: "consist", false: "inconsist"} - - testCases := map[string]struct { - a, b string - consist bool - }{ - "Equal": { - a: "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f", - b: "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f", - consist: true, - }, - "EqualWithWhitespaceVariants": { - a: "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f", - b: " level-asymmetry-allowed=1; \npacketization-mode=1;\t\nprofile-level-id=42e01f", - consist: true, - }, - "EqualWithCase": { - a: "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f", - b: "level-asymmetry-allowed=1;packetization-mode=1;PROFILE-LEVEL-ID=42e01f", - consist: true, - }, - "OneHasExtraParam": { - a: "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f", - b: "packetization-mode=1;profile-level-id=42e01f", - consist: true, - }, - "DifferentProfileLevelIDVersions": { - a: "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f", - b: "packetization-mode=1;profile-level-id=42e029", - consist: true, - }, - "Inconsistent": { - a: "packetization-mode=1;profile-level-id=42e029", - b: "packetization-mode=0;profile-level-id=42e029", - consist: false, - }, - "Inconsistent_MissingPacketizationMode": { - a: "packetization-mode=1;profile-level-id=42e029", - b: "profile-level-id=42e029", - consist: false, - }, - "Inconsistent_MissingProfileLevelID": { - a: "packetization-mode=1;profile-level-id=42e029", - b: "packetization-mode=1", - consist: false, - }, - "Inconsistent_InvalidProfileLevelID": { - a: "packetization-mode=1;profile-level-id=42e029", - b: "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=41e029", - consist: false, - }, - } - for name, testCase := range testCases { - testCase := testCase - check := func(t *testing.T, a, b string) { - aa := Parse("video/h264", a) - bb := Parse("video/h264", b) - c := aa.Match(bb) - if c != testCase.consist { - t.Errorf( - "'%s' and '%s' are expected to be %s, but treated as %s", - a, b, consistString[testCase.consist], consistString[c], - ) - } - - // test reverse case here - c = bb.Match(aa) - if c != testCase.consist { - t.Errorf( - "'%s' and '%s' are expected to be %s, but treated as %s", - a, b, consistString[testCase.consist], consistString[c], - ) - } - } - t.Run(name, func(t *testing.T) { - check(t, testCase.a, testCase.b) - }) - } -} diff --git a/internal/fmtp/vp9.go b/internal/fmtp/vp9.go new file mode 100644 index 00000000000..9400fe02b6f --- /dev/null +++ b/internal/fmtp/vp9.go @@ -0,0 +1,40 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + +package fmtp + +type vp9FMTP struct { + parameters map[string]string +} + +func (h *vp9FMTP) MimeType() string { + return "video/vp9" +} + +func (h *vp9FMTP) Match(b FMTP) bool { + c, ok := b.(*vp9FMTP) + if !ok { + return false + } + + // draft-ietf-payload-vp9-16: + // If no profile-id is present, Profile 0 MUST be inferred + hProfileID, ok := h.parameters["profile-id"] + if !ok { + hProfileID = "0" + } + cProfileID, ok := c.parameters["profile-id"] + if !ok { + cProfileID = "0" + } + if hProfileID != cProfileID { + return false + } + + return true +} + +func (h *vp9FMTP) Parameter(key string) (string, bool) { + v, ok := h.parameters[key] + return v, ok +} From a868a14ec8196a58bf064e33ba00dd3e259c36e8 Mon Sep 17 00:00:00 2001 From: aler9 <46489434+aler9@users.noreply.github.com> Date: Tue, 25 Jun 2024 22:30:07 +0200 Subject: [PATCH 16/39] Add links to RTP payload format specifications --- internal/fmtp/av1.go | 3 ++- internal/fmtp/vp9.go | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/internal/fmtp/av1.go b/internal/fmtp/av1.go index 59d8b31a8d1..29eccd114d0 100644 --- a/internal/fmtp/av1.go +++ b/internal/fmtp/av1.go @@ -17,7 +17,8 @@ func (h *av1FMTP) Match(b FMTP) bool { return false } - // AV1 RTP specification: + // RTP Payload Format For AV1 (v1.0) + // https://aomediacodec.github.io/av1-rtp-spec/ // If the profile parameter is not present, it MUST be inferred to be 0 (“Main” profile). hProfile, ok := h.parameters["profile"] if !ok { diff --git a/internal/fmtp/vp9.go b/internal/fmtp/vp9.go index 9400fe02b6f..7fc618bccd8 100644 --- a/internal/fmtp/vp9.go +++ b/internal/fmtp/vp9.go @@ -17,7 +17,8 @@ func (h *vp9FMTP) Match(b FMTP) bool { return false } - // draft-ietf-payload-vp9-16: + // RTP Payload Format for VP9 Video - draft-ietf-payload-vp9-16 + // https://datatracker.ietf.org/doc/html/draft-ietf-payload-vp9-16 // If no profile-id is present, Profile 0 MUST be inferred hProfileID, ok := h.parameters["profile-id"] if !ok { From 0a97ff62b3fbd63df284a3f523ad2e80e93a6792 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 1 Jul 2024 01:27:28 +0000 Subject: [PATCH 17/39] Update module golang.org/x/net to v0.26.0 Generated by renovateBot --- go.mod | 6 +++--- go.sum | 14 +++++++------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/go.mod b/go.mod index 41928678388..91780fb29fe 100644 --- a/go.mod +++ b/go.mod @@ -18,7 +18,7 @@ require ( github.com/pion/transport/v3 v3.0.2 github.com/sclevine/agouti v3.0.0+incompatible github.com/stretchr/testify v1.9.0 - golang.org/x/net v0.25.0 + golang.org/x/net v0.26.0 ) require ( @@ -30,7 +30,7 @@ require ( github.com/pion/transport/v2 v2.2.4 // indirect github.com/pion/turn/v3 v3.0.3 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - golang.org/x/crypto v0.23.0 // indirect - golang.org/x/sys v0.20.0 // indirect + golang.org/x/crypto v0.24.0 // indirect + golang.org/x/sys v0.21.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index b1129ee88ec..765e2af11b7 100644 --- a/go.sum +++ b/go.sum @@ -102,8 +102,8 @@ golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98y golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= -golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= -golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= +golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= @@ -122,8 +122,8 @@ golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= -golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= -golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= +golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -150,8 +150,8 @@ golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= -golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= +golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= @@ -169,7 +169,7 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= +golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= From f22966136c1a22c25d330303b7ea47d7e2d4771e Mon Sep 17 00:00:00 2001 From: Eric Daniels Date: Tue, 25 Jun 2024 12:12:23 -0400 Subject: [PATCH 18/39] Support wasm tests on apple silicon --- package.json | 5 +- test-wasm/node_shim.js | 2 +- yarn.lock | 516 ++++------------------------------------- 3 files changed, 52 insertions(+), 471 deletions(-) diff --git a/package.json b/package.json index 429f3efb342..f58d23972b6 100644 --- a/package.json +++ b/package.json @@ -3,9 +3,10 @@ "repository": "git@github.com:pion/webrtc.git", "private": true, "devDependencies": { - "wrtc": "0.4.7" + "@roamhq/wrtc": "^0.8.0" }, "dependencies": { "request": "2.88.2" - } + }, + "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e" } diff --git a/test-wasm/node_shim.js b/test-wasm/node_shim.js index 9dcd8016b69..d45ca19bb5a 100644 --- a/test-wasm/node_shim.js +++ b/test-wasm/node_shim.js @@ -4,7 +4,7 @@ // This file adds RTCPeerConnection to the global context, making Node.js more // closely match the browser API for WebRTC. -const wrtc = require('wrtc') +const wrtc = require('@roamhq/wrtc') global.window = { RTCPeerConnection: wrtc.RTCPeerConnection diff --git a/yarn.lock b/yarn.lock index 01bf7823dcc..809d55604ca 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4,10 +4,42 @@ # SPDX-FileCopyrightText: 2023 The Pion community # SPDX-License-Identifier: MIT -abbrev@1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" - integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== +"@roamhq/wrtc-darwin-arm64@0.8.0": + version "0.8.0" + resolved "https://registry.yarnpkg.com/@roamhq/wrtc-darwin-arm64/-/wrtc-darwin-arm64-0.8.0.tgz#15057e6b8f57e4d1b7008a9d848b6f2036adbb24" + integrity sha512-OtV2KWO7zOG3L8TF3KCt9aucynVCD/ww2xeXXgg+FLkya3ca0uzehN8EQJ3BL4tkInksbFJ2ssyu9cehfJ3ZuA== + +"@roamhq/wrtc-darwin-x64@0.8.0": + version "0.8.0" + resolved "https://registry.yarnpkg.com/@roamhq/wrtc-darwin-x64/-/wrtc-darwin-x64-0.8.0.tgz#e09137b5a7edf2c2412bc63f1da1893dcaa211fd" + integrity sha512-VY7Vzt/SDDDCpW//h8GW9bOZrOr8gWXPZVD9473ypl4jyBIoO57yyLbHzd1G0vBUkS6szsHlQCz1WwpI30YL+g== + +"@roamhq/wrtc-linux-arm64@0.8.1": + version "0.8.1" + resolved "https://registry.yarnpkg.com/@roamhq/wrtc-linux-arm64/-/wrtc-linux-arm64-0.8.1.tgz#9a5f3297de44fcec86713d0baefa0594658ab71e" + integrity sha512-FBJLLazlWkGQUXaokC/rTbrUQbb0CNFYry52fZGstufrGLTWu+g4HcwXdVvxh1tnVtVMvkQGk+mlOL52sCxw0A== + +"@roamhq/wrtc-linux-x64@0.8.1": + version "0.8.1" + resolved "https://registry.yarnpkg.com/@roamhq/wrtc-linux-x64/-/wrtc-linux-x64-0.8.1.tgz#3c5b60ca6cc6ebf5c2389d852f4a101135031da2" + integrity sha512-I9oWG7b4uvWO1IOR/aF34n+ID6TKVuSs0jd19h5KdhfRtw7FFh9xxuwN9rONPxLVa6fS0q+MCZgAf8Scz89L8Q== + +"@roamhq/wrtc-win32-x64@0.8.0": + version "0.8.0" + resolved "https://registry.yarnpkg.com/@roamhq/wrtc-win32-x64/-/wrtc-win32-x64-0.8.0.tgz#582e2478df48201d5757b60dcd4b4dcc40a054b7" + integrity sha512-R2fxl41BLWPiP4eaTHGLzbbVvRjx1mV/OsgINCvawO7Hwz5Zx9I45+Fhrw3hd4n5amIeSG9VIF7Kz8eeTFXTGQ== + +"@roamhq/wrtc@^0.8.0": + version "0.8.0" + resolved "https://registry.yarnpkg.com/@roamhq/wrtc/-/wrtc-0.8.0.tgz#03c8c64c3b6a1e6e8965ec6496fa7e97571ae04b" + integrity sha512-C0V/nqc4/2xzORI5qa4mIeN/8UO3ywN1kInrJ9u6GljFx0D18JMUJEqe8yYHa61RrEeoWN3PKdW++k8TocSx/A== + optionalDependencies: + "@roamhq/wrtc-darwin-arm64" "0.8.0" + "@roamhq/wrtc-darwin-x64" "0.8.0" + "@roamhq/wrtc-linux-arm64" "0.8.1" + "@roamhq/wrtc-linux-x64" "0.8.1" + "@roamhq/wrtc-win32-x64" "0.8.0" + domexception "^4.0.0" ajv@^6.5.5: version "6.12.2" @@ -19,29 +51,6 @@ ajv@^6.5.5: json-schema-traverse "^0.4.1" uri-js "^4.2.2" -ansi-regex@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" - integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= - -ansi-regex@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" - integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= - -aproba@^1.0.3: - version "1.2.0" - resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" - integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== - -are-we-there-yet@~1.1.2: - version "1.1.5" - resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21" - integrity sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w== - dependencies: - delegates "^1.0.0" - readable-stream "^2.0.6" - asn1@~0.2.3: version "0.2.4" resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" @@ -69,11 +78,6 @@ aws4@^1.8.0: resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.10.0.tgz#a17b3a8ea811060e74d47d306122400ad4497ae2" integrity sha512-3YDiu347mtVtjpyV3u5kVqQLP242c06zwDOgpeRnybmXlYYsLbtTrUBUm8i8srONt+FWobl5aibnU1030PeeuA== -balanced-match@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" - integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= - bcrypt-pbkdf@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" @@ -81,29 +85,11 @@ bcrypt-pbkdf@^1.0.0: dependencies: tweetnacl "^0.14.3" -brace-expansion@^1.1.7: - version "1.1.11" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" - integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== - dependencies: - balanced-match "^1.0.0" - concat-map "0.0.1" - caseless@~0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= -chownr@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.1.tgz#54726b8b8fff4df053c42187e801fb4412df1494" - integrity sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g== - -code-point-at@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" - integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= - combined-stream@^1.0.6, combined-stream@~1.0.6: version "1.0.8" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" @@ -111,17 +97,7 @@ combined-stream@^1.0.6, combined-stream@~1.0.6: dependencies: delayed-stream "~1.0.0" -concat-map@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" - integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= - -console-control-strings@^1.0.0, console-control-strings@~1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" - integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= - -core-util-is@1.0.2, core-util-is@~1.0.0: +core-util-is@1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= @@ -133,39 +109,17 @@ dashdash@^1.12.0: dependencies: assert-plus "^1.0.0" -debug@^2.1.2: - version "2.6.9" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" - integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== - dependencies: - ms "2.0.0" - -deep-extend@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" - integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== - delayed-stream@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= -delegates@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" - integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= - -detect-libc@^1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" - integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups= - -domexception@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/domexception/-/domexception-1.0.1.tgz#937442644ca6a31261ef36e3ec677fe805582c90" - integrity sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug== +domexception@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/domexception/-/domexception-4.0.0.tgz#4ad1be56ccadc86fc76d033353999a8037d03673" + integrity sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw== dependencies: - webidl-conversions "^4.0.2" + webidl-conversions "^7.0.0" ecc-jsbn@~0.1.1: version "0.1.2" @@ -214,32 +168,6 @@ form-data@~2.3.2: combined-stream "^1.0.6" mime-types "^2.1.12" -fs-minipass@^1.2.5: - version "1.2.5" - resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.5.tgz#06c277218454ec288df77ada54a03b8702aacb9d" - integrity sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ== - dependencies: - minipass "^2.2.1" - -fs.realpath@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" - integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= - -gauge@~2.7.3: - version "2.7.4" - resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" - integrity sha1-LANAXHU4w51+s3sxcCLjJfsBi/c= - dependencies: - aproba "^1.0.3" - console-control-strings "^1.0.0" - has-unicode "^2.0.0" - object-assign "^4.1.0" - signal-exit "^3.0.0" - string-width "^1.0.1" - strip-ansi "^3.0.1" - wide-align "^1.1.0" - getpass@^0.1.1: version "0.1.7" resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" @@ -247,18 +175,6 @@ getpass@^0.1.1: dependencies: assert-plus "^1.0.0" -glob@^7.1.3: - version "7.1.3" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1" - integrity sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.0.4" - once "^1.3.0" - path-is-absolute "^1.0.0" - har-schema@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" @@ -272,11 +188,6 @@ har-validator@~5.1.3: ajv "^6.5.5" har-schema "^2.0.0" -has-unicode@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" - integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk= - http-signature@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" @@ -286,60 +197,11 @@ http-signature@~1.2.0: jsprim "^1.2.2" sshpk "^1.7.0" -iconv-lite@^0.4.4: - version "0.4.24" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" - integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== - dependencies: - safer-buffer ">= 2.1.2 < 3" - -ignore-walk@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.1.tgz#a83e62e7d272ac0e3b551aaa82831a19b69f82f8" - integrity sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ== - dependencies: - minimatch "^3.0.4" - -inflight@^1.0.4: - version "1.0.6" - resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" - integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= - dependencies: - once "^1.3.0" - wrappy "1" - -inherits@2, inherits@~2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" - integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= - -ini@~1.3.0: - version "1.3.5" - resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" - integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw== - -is-fullwidth-code-point@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" - integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs= - dependencies: - number-is-nan "^1.0.0" - -is-fullwidth-code-point@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" - integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= - is-typedarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= -isarray@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" - integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= - isstream@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" @@ -387,161 +249,16 @@ mime-types@^2.1.12, mime-types@~2.1.19: dependencies: mime-db "1.44.0" -minimatch@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" - integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== - dependencies: - brace-expansion "^1.1.7" - -minimist@0.0.8: - version "0.0.8" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" - integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0= - -minimist@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" - integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ= - -minipass@^2.2.1, minipass@^2.3.4: - version "2.3.5" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.3.5.tgz#cacebe492022497f656b0f0f51e2682a9ed2d848" - integrity sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA== - dependencies: - safe-buffer "^5.1.2" - yallist "^3.0.0" - -minizlib@^1.1.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.2.1.tgz#dd27ea6136243c7c880684e8672bb3a45fd9b614" - integrity sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA== - dependencies: - minipass "^2.2.1" - -mkdirp@^0.5.0, mkdirp@^0.5.1: - version "0.5.1" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" - integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= - dependencies: - minimist "0.0.8" - -ms@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" - integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= - -needle@^2.2.1: - version "2.2.4" - resolved "https://registry.yarnpkg.com/needle/-/needle-2.2.4.tgz#51931bff82533b1928b7d1d69e01f1b00ffd2a4e" - integrity sha512-HyoqEb4wr/rsoaIDfTH2aVL9nWtQqba2/HvMv+++m8u0dz808MaagKILxtfeSN7QU7nvbQ79zk3vYOJp9zsNEA== - dependencies: - debug "^2.1.2" - iconv-lite "^0.4.4" - sax "^1.2.4" - -node-pre-gyp@^0.13.0: - version "0.13.0" - resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.13.0.tgz#df9ab7b68dd6498137717838e4f92a33fc9daa42" - integrity sha512-Md1D3xnEne8b/HGVQkZZwV27WUi1ZRuZBij24TNaZwUPU3ZAFtvT6xxJGaUVillfmMKnn5oD1HoGsp2Ftik7SQ== - dependencies: - detect-libc "^1.0.2" - mkdirp "^0.5.1" - needle "^2.2.1" - nopt "^4.0.1" - npm-packlist "^1.1.6" - npmlog "^4.0.2" - rc "^1.2.7" - rimraf "^2.6.1" - semver "^5.3.0" - tar "^4" - -nopt@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d" - integrity sha1-0NRoWv1UFRk8jHUFYC0NF81kR00= - dependencies: - abbrev "1" - osenv "^0.1.4" - -npm-bundled@^1.0.1: - version "1.0.6" - resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.0.6.tgz#e7ba9aadcef962bb61248f91721cd932b3fe6bdd" - integrity sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g== - -npm-packlist@^1.1.6: - version "1.4.1" - resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.4.1.tgz#19064cdf988da80ea3cee45533879d90192bbfbc" - integrity sha512-+TcdO7HJJ8peiiYhvPxsEDhF3PJFGUGRcFsGve3vxvxdcpO2Z4Z7rkosRM0kWj6LfbK/P0gu3dzk5RU1ffvFcw== - dependencies: - ignore-walk "^3.0.1" - npm-bundled "^1.0.1" - -npmlog@^4.0.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" - integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg== - dependencies: - are-we-there-yet "~1.1.2" - console-control-strings "~1.1.0" - gauge "~2.7.3" - set-blocking "~2.0.0" - -number-is-nan@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" - integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= - oauth-sign@~0.9.0: version "0.9.0" resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== -object-assign@^4.1.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" - integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= - -once@^1.3.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" - integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= - dependencies: - wrappy "1" - -os-homedir@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" - integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M= - -os-tmpdir@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" - integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= - -osenv@^0.1.4: - version "0.1.5" - resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410" - integrity sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g== - dependencies: - os-homedir "^1.0.0" - os-tmpdir "^1.0.0" - -path-is-absolute@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" - integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= - performance-now@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= -process-nextick-args@~2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa" - integrity sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw== - psl@^1.1.28: version "1.8.0" resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" @@ -557,29 +274,6 @@ qs@~6.5.2: resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== -rc@^1.2.7: - version "1.2.8" - resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" - integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== - dependencies: - deep-extend "^0.6.0" - ini "~1.3.0" - minimist "^1.2.0" - strip-json-comments "~2.0.1" - -readable-stream@^2.0.6: - version "2.3.6" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" - integrity sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw== - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.3" - isarray "~1.0.0" - process-nextick-args "~2.0.0" - safe-buffer "~5.1.1" - string_decoder "~1.1.1" - util-deprecate "~1.0.1" - request@2.88.2: version "2.88.2" resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" @@ -606,48 +300,21 @@ request@2.88.2: tunnel-agent "^0.6.0" uuid "^3.3.2" -rimraf@^2.6.1: - version "2.6.3" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" - integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== - dependencies: - glob "^7.1.3" - safe-buffer@^5.0.1: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== -safe-buffer@^5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: +safe-buffer@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== -"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: +safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== -sax@^1.2.4: - version "1.2.4" - resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" - integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== - -semver@^5.3.0: - version "5.6.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.6.0.tgz#7e74256fbaa49c75aa7c7a205cc22799cac80004" - integrity sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg== - -set-blocking@~2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" - integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= - -signal-exit@^3.0.0: - version "3.0.2" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" - integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0= - sshpk@^1.7.0: version "1.16.1" resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877" @@ -663,62 +330,6 @@ sshpk@^1.7.0: safer-buffer "^2.0.2" tweetnacl "~0.14.0" -string-width@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" - integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= - dependencies: - code-point-at "^1.0.0" - is-fullwidth-code-point "^1.0.0" - strip-ansi "^3.0.0" - -"string-width@^1.0.2 || 2": - version "2.1.1" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" - integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== - dependencies: - is-fullwidth-code-point "^2.0.0" - strip-ansi "^4.0.0" - -string_decoder@~1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" - integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== - dependencies: - safe-buffer "~5.1.0" - -strip-ansi@^3.0.0, strip-ansi@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" - integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= - dependencies: - ansi-regex "^2.0.0" - -strip-ansi@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" - integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= - dependencies: - ansi-regex "^3.0.0" - -strip-json-comments@~2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" - integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= - -tar@^4: - version "4.4.8" - resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.8.tgz#b19eec3fde2a96e64666df9fdb40c5ca1bc3747d" - integrity sha512-LzHF64s5chPQQS0IYBn9IN5h3i98c12bo4NCO7e0sGM2llXQ3p2FGC5sdENN4cTW48O915Sh+x+EXx7XW96xYQ== - dependencies: - chownr "^1.1.1" - fs-minipass "^1.2.5" - minipass "^2.3.4" - minizlib "^1.1.1" - mkdirp "^0.5.0" - safe-buffer "^5.1.2" - yallist "^3.0.2" - tough-cookie@~2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" @@ -746,11 +357,6 @@ uri-js@^4.2.2: dependencies: punycode "^2.1.0" -util-deprecate@~1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" - integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= - uuid@^3.3.2: version "3.4.0" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" @@ -765,33 +371,7 @@ verror@1.10.0: core-util-is "1.0.2" extsprintf "^1.2.0" -webidl-conversions@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad" - integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg== - -wide-align@^1.1.0: - version "1.1.3" - resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" - integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA== - dependencies: - string-width "^1.0.2 || 2" - -wrappy@1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" - integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= - -wrtc@0.4.7: - version "0.4.7" - resolved "https://registry.yarnpkg.com/wrtc/-/wrtc-0.4.7.tgz#c61530cd662713e50bffe64b7a78673ce070426c" - integrity sha512-P6Hn7VT4lfSH49HxLHcHhDq+aFf/jd9dPY7lDHeFhZ22N3858EKuwm2jmnlPzpsRGEPaoF6XwkcxY5SYnt4f/g== - dependencies: - node-pre-gyp "^0.13.0" - optionalDependencies: - domexception "^1.0.1" - -yallist@^3.0.0, yallist@^3.0.2: - version "3.0.3" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.0.3.tgz#b4b049e314be545e3ce802236d6cd22cd91c3de9" - integrity sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A== +webidl-conversions@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-7.0.0.tgz#256b4e1882be7debbf01d05f0aa2039778ea080a" + integrity sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g== From 7c8bfbd44af38f9b24b7bdfdb194320c2c737d32 Mon Sep 17 00:00:00 2001 From: Eric Daniels Date: Mon, 1 Jul 2024 16:35:26 -0400 Subject: [PATCH 19/39] Make pc.Close wait on spawned goroutines to close --- datachannel.go | 19 +++++++ peerconnection.go | 35 ++++++++++-- peerconnection_close_test.go | 102 +++++++++++++++++++++++++++++++++++ 3 files changed, 152 insertions(+), 4 deletions(-) diff --git a/datachannel.go b/datachannel.go index e975a04f7b5..11e3f3e0c18 100644 --- a/datachannel.go +++ b/datachannel.go @@ -40,6 +40,7 @@ type DataChannel struct { readyState atomic.Value // DataChannelState bufferedAmountLowThreshold uint64 detachCalled bool + readLoopActive chan struct{} // The binaryType represents attribute MUST, on getting, return the value to // which it was last set. On setting, if the new value is either the string @@ -327,6 +328,7 @@ func (d *DataChannel) handleOpen(dc *datachannel.DataChannel, isRemote, isAlread defer d.mu.Unlock() if !d.api.settingEngine.detach.DataChannels { + d.readLoopActive = make(chan struct{}) go d.readLoop() } } @@ -350,6 +352,7 @@ func (d *DataChannel) onError(err error) { } func (d *DataChannel) readLoop() { + defer close(d.readLoopActive) buffer := make([]byte, dataChannelBufferSize) for { n, isString, err := d.dataChannel.ReadDataChannel(buffer) @@ -449,6 +452,22 @@ func (d *DataChannel) Detach() (datachannel.ReadWriteCloser, error) { // Close Closes the DataChannel. It may be called regardless of whether // the DataChannel object was created by this peer or the remote peer. func (d *DataChannel) Close() error { + return d.close(false) +} + +// Normally, close only stops writes from happening, so waitForReadsDone=true +// will wait for reads to be finished based on underlying SCTP association +// closure or a SCTP reset stream from the other side. This is safe to call +// with waitForReadsDone=true after tearing down a PeerConnection but not +// necessarily before. For example, if you used a vnet and dropped all packets +// right before closing the DataChannel, you'd need never see a reset stream. +func (d *DataChannel) close(waitForReadsDone bool) error { + if waitForReadsDone && d.readLoopActive != nil { + defer func() { + <-d.readLoopActive + }() + } + d.mu.Lock() haveSctpTransport := d.dataChannel != nil d.mu.Unlock() diff --git a/peerconnection.go b/peerconnection.go index 3ae8b732c66..f3307c88bf7 100644 --- a/peerconnection.go +++ b/peerconnection.go @@ -56,6 +56,7 @@ type PeerConnection struct { idpLoginURL *string isClosed *atomicBool + isClosedDone chan struct{} isNegotiationNeeded *atomicBool updateNegotiationNeededFlagOnEmptyChain *atomicBool @@ -116,6 +117,7 @@ func (api *API) NewPeerConnection(configuration Configuration) (*PeerConnection, ICECandidatePoolSize: 0, }, isClosed: &atomicBool{}, + isClosedDone: make(chan struct{}), isNegotiationNeeded: &atomicBool{}, updateNegotiationNeededFlagOnEmptyChain: &atomicBool{}, lastOffer: "", @@ -2044,14 +2046,31 @@ func (pc *PeerConnection) writeRTCP(pkts []rtcp.Packet, _ interceptor.Attributes return pc.dtlsTransport.WriteRTCP(pkts) } -// Close ends the PeerConnection +// Close ends the PeerConnection. +// It will make a best effort to wait for all underlying goroutines it spawned to finish, +// except for cases that would cause deadlocks with itself. func (pc *PeerConnection) Close() error { // https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-close (step #1) // https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-close (step #2) if pc.isClosed.swap(true) { + // someone else got here first but may still be closing (e.g. via DTLS close_notify) + <-pc.isClosedDone return nil } + defer close(pc.isClosedDone) + // Try closing everything and collect the errors + // Shutdown strategy: + // 1. Close all data channels. + // 2. All Conn close by closing their underlying Conn. + // 3. A Mux stops this chain. It won't close the underlying + // Conn if one of the endpoints is closed down. To + // continue the chain the Mux has to be closed. + pc.sctpTransport.lock.Lock() + closeErrs := make([]error, 0, 4+len(pc.sctpTransport.dataChannels)) + pc.sctpTransport.lock.Unlock() + + // canon steps // https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-close (step #3) pc.signalingState.Set(SignalingStateClosed) @@ -2061,7 +2080,6 @@ func (pc *PeerConnection) Close() error { // 2. A Mux stops this chain. It won't close the underlying // Conn if one of the endpoints is closed down. To // continue the chain the Mux has to be closed. - closeErrs := make([]error, 4) closeErrs = append(closeErrs, pc.api.interceptor.Close()) @@ -2088,7 +2106,6 @@ func (pc *PeerConnection) Close() error { // https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-close (step #7) closeErrs = append(closeErrs, pc.dtlsTransport.Stop()) - // https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-close (step #8, #9, #10) if pc.iceTransport != nil { closeErrs = append(closeErrs, pc.iceTransport.Stop()) @@ -2097,6 +2114,13 @@ func (pc *PeerConnection) Close() error { // https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-close (step #11) pc.updateConnectionState(pc.ICEConnectionState(), pc.dtlsTransport.State()) + // non-canon steps + pc.sctpTransport.lock.Lock() + for _, d := range pc.sctpTransport.dataChannels { + closeErrs = append(closeErrs, d.close(true)) + } + pc.sctpTransport.lock.Unlock() + return util.FlattenErrs(closeErrs) } @@ -2268,8 +2292,11 @@ func (pc *PeerConnection) startTransports(iceRole ICERole, dtlsRole DTLSRole, re } pc.dtlsTransport.internalOnCloseHandler = func() { - pc.log.Info("Closing PeerConnection from DTLS CloseNotify") + if pc.isClosed.get() { + return + } + pc.log.Info("Closing PeerConnection from DTLS CloseNotify") go func() { if pcClosErr := pc.Close(); pcClosErr != nil { pc.log.Warnf("Failed to close PeerConnection from DTLS CloseNotify: %s", pcClosErr) diff --git a/peerconnection_close_test.go b/peerconnection_close_test.go index 5360d701fc2..ac3b23f704d 100644 --- a/peerconnection_close_test.go +++ b/peerconnection_close_test.go @@ -7,6 +7,8 @@ package webrtc import ( + "runtime" + "strings" "testing" "time" @@ -179,3 +181,103 @@ func TestPeerConnection_Close_DuringICE(t *testing.T) { t.Error("pcOffer.Close() Timeout") } } + +func TestPeerConnection_CloseWithIncomingMessages(t *testing.T) { + // Limit runtime in case of deadlocks + lim := test.TimeOut(time.Second * 20) + defer lim.Stop() + + report := CheckRoutinesIntolerant(t) + defer report() + + pcOffer, pcAnswer, err := newPair() + if err != nil { + t.Fatal(err) + } + + var dcAnswer *DataChannel + answerDataChannelOpened := make(chan struct{}) + pcAnswer.OnDataChannel(func(d *DataChannel) { + // Make sure this is the data channel we were looking for. (Not the one + // created in signalPair). + if d.Label() != "data" { + return + } + dcAnswer = d + close(answerDataChannelOpened) + }) + + dcOffer, err := pcOffer.CreateDataChannel("data", nil) + if err != nil { + t.Fatal(err) + } + + offerDataChannelOpened := make(chan struct{}) + dcOffer.OnOpen(func() { + close(offerDataChannelOpened) + }) + + err = signalPair(pcOffer, pcAnswer) + if err != nil { + t.Fatal(err) + } + + <-offerDataChannelOpened + <-answerDataChannelOpened + + msgNum := 0 + dcOffer.OnMessage(func(_ DataChannelMessage) { + t.Log("msg", msgNum) + msgNum++ + }) + + // send 50 messages, then close pcOffer, and then send another 50 + for i := 0; i < 100; i++ { + if i == 50 { + err = pcOffer.Close() + if err != nil { + t.Fatal(err) + } + } + _ = dcAnswer.Send([]byte("hello!")) + } + + err = pcAnswer.Close() + if err != nil { + t.Fatal(err) + } +} + +// CheckRoutinesIntolerant is used to check for leaked go-routines. +// It differs from test.CheckRoutines in that it won't wait at all +// for lingering goroutines. This is helpful for tests that need +// to ensure clean closure of resources. +func CheckRoutinesIntolerant(t *testing.T) func() { + return func() { + routines := getRoutines() + if len(routines) == 0 { + return + } + t.Fatalf("%s: \n%s", "Unexpected routines on test end", strings.Join(routines, "\n\n")) // nolint + } +} + +func getRoutines() []string { + buf := make([]byte, 2<<20) + buf = buf[:runtime.Stack(buf, true)] + return filterRoutines(strings.Split(string(buf), "\n\n")) +} + +func filterRoutines(routines []string) []string { + result := []string{} + for _, stack := range routines { + if stack == "" || // Empty + strings.Contains(stack, "testing.Main(") || // Tests + strings.Contains(stack, "testing.(*T).Run(") || // Test run + strings.Contains(stack, "getRoutines(") { // This routine + continue + } + result = append(result, stack) + } + return result +} From d89278747795a02a4a36a75669a7f7d012ab3987 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 2 Jul 2024 05:19:06 +0000 Subject: [PATCH 20/39] Update module github.com/pion/sctp to v1.8.17 Generated by renovateBot --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 91780fb29fe..ff81d418439 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/pion/randutil v0.1.0 github.com/pion/rtcp v1.2.14 github.com/pion/rtp v1.8.6 - github.com/pion/sctp v1.8.16 + github.com/pion/sctp v1.8.17 github.com/pion/sdp/v3 v3.0.9 github.com/pion/srtp/v3 v3.0.1 github.com/pion/stun/v2 v2.0.0 diff --git a/go.sum b/go.sum index 765e2af11b7..d64438bc69d 100644 --- a/go.sum +++ b/go.sum @@ -60,8 +60,8 @@ github.com/pion/rtp v1.8.3/go.mod h1:pBGHaFt/yW7bf1jjWAoUjpSNoDnw98KTMg+jWWvziqU github.com/pion/rtp v1.8.6 h1:MTmn/b0aWWsAzux2AmP8WGllusBVw4NPYPVFFd7jUPw= github.com/pion/rtp v1.8.6/go.mod h1:pBGHaFt/yW7bf1jjWAoUjpSNoDnw98KTMg+jWWvziqU= github.com/pion/sctp v1.8.13/go.mod h1:YKSgO/bO/6aOMP9LCie1DuD7m+GamiK2yIiPM6vH+GA= -github.com/pion/sctp v1.8.16 h1:PKrMs+o9EMLRvFfXq59WFsC+V8mN1wnKzqrv+3D/gYY= -github.com/pion/sctp v1.8.16/go.mod h1:P6PbDVA++OJMrVNg2AL3XtYHV4uD6dvfyOovCgMs0PE= +github.com/pion/sctp v1.8.17 h1:uK1VChU9C/J3Sj/Cq6cwEGtx2Gludgoi5zwZBZzWNT0= +github.com/pion/sctp v1.8.17/go.mod h1:P6PbDVA++OJMrVNg2AL3XtYHV4uD6dvfyOovCgMs0PE= github.com/pion/sdp/v3 v3.0.9 h1:pX++dCHoHUwq43kuwf3PyJfHlwIj4hXA7Vrifiq0IJY= github.com/pion/sdp/v3 v3.0.9/go.mod h1:B5xmvENq5IXJimIO4zfp6LAe1fD9N+kFv+V/1lOdz8M= github.com/pion/srtp/v3 v3.0.1 h1:AkIQRIZ+3tAOJMQ7G301xtrD1vekQbNeRO7eY1K8ZHk= From de5d997e51a515a0bc8356d98301492e7178548c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 2 Jul 2024 15:29:26 +0000 Subject: [PATCH 21/39] Update module github.com/pion/ice/v3 to v3.0.8 Generated by renovateBot --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index ff81d418439..cc0e728c19c 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.19 require ( github.com/pion/datachannel v1.5.6 github.com/pion/dtls/v2 v2.2.11 - github.com/pion/ice/v3 v3.0.7 + github.com/pion/ice/v3 v3.0.8 github.com/pion/interceptor v0.1.29 github.com/pion/logging v0.2.2 github.com/pion/randutil v0.1.0 diff --git a/go.sum b/go.sum index d64438bc69d..109758d9628 100644 --- a/go.sum +++ b/go.sum @@ -43,8 +43,8 @@ github.com/pion/datachannel v1.5.6/go.mod h1:1eKT6Q85pRnr2mHiWHxJwO50SfZRtWHTsNI github.com/pion/dtls/v2 v2.2.7/go.mod h1:8WiMkebSHFD0T+dIU+UeBaoV7kDhOW5oDCzZ7WZ/F9s= github.com/pion/dtls/v2 v2.2.11 h1:9U/dpCYl1ySttROPWJgqWKEylUdT0fXp/xst6JwY5Ks= github.com/pion/dtls/v2 v2.2.11/go.mod h1:d9SYc9fch0CqK90mRk1dC7AkzzpwJj6u2GU3u+9pqFE= -github.com/pion/ice/v3 v3.0.7 h1:dfMViRKblENqzorR2cQiiRKWqQfqKZ9+nT/sREX3ra8= -github.com/pion/ice/v3 v3.0.7/go.mod h1:pBRcCoJRC0vwvFsemfRIqRLYukV4bPboGb0B4b8AhrQ= +github.com/pion/ice/v3 v3.0.8 h1:GtoHjsSdcFmF+cCByHODNEMFBwWIngw851XgpsnlO88= +github.com/pion/ice/v3 v3.0.8/go.mod h1:aSHw7r4guKNumxc5dCWfzWga+qtz1ki/nnpHfJTSLTs= github.com/pion/interceptor v0.1.29 h1:39fsnlP1U8gw2JzOFWdfCU82vHvhW9o0rZnZF56wF+M= github.com/pion/interceptor v0.1.29/go.mod h1:ri+LGNjRUc5xUNtDEPzfdkmSqISixVTBF/z/Zms/6T4= github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY= From ee1efb7c4fe44a9eaf1fb34ecb5bc28a47695921 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 6 Jul 2024 17:20:06 +0000 Subject: [PATCH 22/39] Update module github.com/pion/ice/v3 to v3.0.9 Generated by renovateBot --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index cc0e728c19c..b5b5bf8498c 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.19 require ( github.com/pion/datachannel v1.5.6 github.com/pion/dtls/v2 v2.2.11 - github.com/pion/ice/v3 v3.0.8 + github.com/pion/ice/v3 v3.0.9 github.com/pion/interceptor v0.1.29 github.com/pion/logging v0.2.2 github.com/pion/randutil v0.1.0 diff --git a/go.sum b/go.sum index 109758d9628..777f1eb1d98 100644 --- a/go.sum +++ b/go.sum @@ -43,8 +43,8 @@ github.com/pion/datachannel v1.5.6/go.mod h1:1eKT6Q85pRnr2mHiWHxJwO50SfZRtWHTsNI github.com/pion/dtls/v2 v2.2.7/go.mod h1:8WiMkebSHFD0T+dIU+UeBaoV7kDhOW5oDCzZ7WZ/F9s= github.com/pion/dtls/v2 v2.2.11 h1:9U/dpCYl1ySttROPWJgqWKEylUdT0fXp/xst6JwY5Ks= github.com/pion/dtls/v2 v2.2.11/go.mod h1:d9SYc9fch0CqK90mRk1dC7AkzzpwJj6u2GU3u+9pqFE= -github.com/pion/ice/v3 v3.0.8 h1:GtoHjsSdcFmF+cCByHODNEMFBwWIngw851XgpsnlO88= -github.com/pion/ice/v3 v3.0.8/go.mod h1:aSHw7r4guKNumxc5dCWfzWga+qtz1ki/nnpHfJTSLTs= +github.com/pion/ice/v3 v3.0.9 h1:AAJQSCJa5qqDJ2pe9TOH4YTamP5m7PJeoQ58ey3McFg= +github.com/pion/ice/v3 v3.0.9/go.mod h1:aSHw7r4guKNumxc5dCWfzWga+qtz1ki/nnpHfJTSLTs= github.com/pion/interceptor v0.1.29 h1:39fsnlP1U8gw2JzOFWdfCU82vHvhW9o0rZnZF56wF+M= github.com/pion/interceptor v0.1.29/go.mod h1:ri+LGNjRUc5xUNtDEPzfdkmSqISixVTBF/z/Zms/6T4= github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY= From a365c79ecb3ca3b3371136701009314867111310 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 6 Jul 2024 19:16:46 +0000 Subject: [PATCH 23/39] Update module github.com/pion/datachannel to v1.5.7 Generated by renovateBot --- go.mod | 2 +- go.sum | 11 +++-------- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index b5b5bf8498c..c1938a2e267 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/pion/webrtc/v4 go 1.19 require ( - github.com/pion/datachannel v1.5.6 + github.com/pion/datachannel v1.5.7 github.com/pion/dtls/v2 v2.2.11 github.com/pion/ice/v3 v3.0.9 github.com/pion/interceptor v0.1.29 diff --git a/go.sum b/go.sum index 777f1eb1d98..738721c5af6 100644 --- a/go.sum +++ b/go.sum @@ -22,10 +22,7 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= @@ -38,8 +35,8 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.17.0 h1:9Luw4uT5HTjHTN8+aNcSThgH1vdXnmdJ8xIfZ4wyTRE= github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= -github.com/pion/datachannel v1.5.6 h1:1IxKJntfSlYkpUj8LlYRSWpYiTTC02nUrOE8T3DqGeg= -github.com/pion/datachannel v1.5.6/go.mod h1:1eKT6Q85pRnr2mHiWHxJwO50SfZRtWHTsNIVb/NfGW4= +github.com/pion/datachannel v1.5.7 h1:Z2NXdUnAwuHTrjS+CAiLdkfCYaD/Wa+Ly4FShIgICsA= +github.com/pion/datachannel v1.5.7/go.mod h1:5F8FSGAjVb63JcMmEBm2r6FDQGeijTuFLrE1QsYLVEo= github.com/pion/dtls/v2 v2.2.7/go.mod h1:8WiMkebSHFD0T+dIU+UeBaoV7kDhOW5oDCzZ7WZ/F9s= github.com/pion/dtls/v2 v2.2.11 h1:9U/dpCYl1ySttROPWJgqWKEylUdT0fXp/xst6JwY5Ks= github.com/pion/dtls/v2 v2.2.11/go.mod h1:d9SYc9fch0CqK90mRk1dC7AkzzpwJj6u2GU3u+9pqFE= @@ -59,7 +56,6 @@ github.com/pion/rtcp v1.2.14/go.mod h1:sn6qjxvnwyAkkPzPULIbVqSKI5Dv54Rv7VG0kNxh9 github.com/pion/rtp v1.8.3/go.mod h1:pBGHaFt/yW7bf1jjWAoUjpSNoDnw98KTMg+jWWvziqU= github.com/pion/rtp v1.8.6 h1:MTmn/b0aWWsAzux2AmP8WGllusBVw4NPYPVFFd7jUPw= github.com/pion/rtp v1.8.6/go.mod h1:pBGHaFt/yW7bf1jjWAoUjpSNoDnw98KTMg+jWWvziqU= -github.com/pion/sctp v1.8.13/go.mod h1:YKSgO/bO/6aOMP9LCie1DuD7m+GamiK2yIiPM6vH+GA= github.com/pion/sctp v1.8.17 h1:uK1VChU9C/J3Sj/Cq6cwEGtx2Gludgoi5zwZBZzWNT0= github.com/pion/sctp v1.8.17/go.mod h1:P6PbDVA++OJMrVNg2AL3XtYHV4uD6dvfyOovCgMs0PE= github.com/pion/sdp/v3 v3.0.9 h1:pX++dCHoHUwq43kuwf3PyJfHlwIj4hXA7Vrifiq0IJY= @@ -189,7 +185,6 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= From c2a0b3332829e444d2054e7613e4b5a24a78aab9 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 6 Jul 2024 22:40:05 +0000 Subject: [PATCH 24/39] Update module github.com/pion/sctp to v1.8.18 Generated by renovateBot --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index c1938a2e267..4760838c5e2 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/pion/randutil v0.1.0 github.com/pion/rtcp v1.2.14 github.com/pion/rtp v1.8.6 - github.com/pion/sctp v1.8.17 + github.com/pion/sctp v1.8.18 github.com/pion/sdp/v3 v3.0.9 github.com/pion/srtp/v3 v3.0.1 github.com/pion/stun/v2 v2.0.0 diff --git a/go.sum b/go.sum index 738721c5af6..023a5541b42 100644 --- a/go.sum +++ b/go.sum @@ -56,8 +56,8 @@ github.com/pion/rtcp v1.2.14/go.mod h1:sn6qjxvnwyAkkPzPULIbVqSKI5Dv54Rv7VG0kNxh9 github.com/pion/rtp v1.8.3/go.mod h1:pBGHaFt/yW7bf1jjWAoUjpSNoDnw98KTMg+jWWvziqU= github.com/pion/rtp v1.8.6 h1:MTmn/b0aWWsAzux2AmP8WGllusBVw4NPYPVFFd7jUPw= github.com/pion/rtp v1.8.6/go.mod h1:pBGHaFt/yW7bf1jjWAoUjpSNoDnw98KTMg+jWWvziqU= -github.com/pion/sctp v1.8.17 h1:uK1VChU9C/J3Sj/Cq6cwEGtx2Gludgoi5zwZBZzWNT0= -github.com/pion/sctp v1.8.17/go.mod h1:P6PbDVA++OJMrVNg2AL3XtYHV4uD6dvfyOovCgMs0PE= +github.com/pion/sctp v1.8.18 h1:zsqVBs7WF54QCEWnqve1acj9nc1dL25FllPF6C/bMaI= +github.com/pion/sctp v1.8.18/go.mod h1:P6PbDVA++OJMrVNg2AL3XtYHV4uD6dvfyOovCgMs0PE= github.com/pion/sdp/v3 v3.0.9 h1:pX++dCHoHUwq43kuwf3PyJfHlwIj4hXA7Vrifiq0IJY= github.com/pion/sdp/v3 v3.0.9/go.mod h1:B5xmvENq5IXJimIO4zfp6LAe1fD9N+kFv+V/1lOdz8M= github.com/pion/srtp/v3 v3.0.1 h1:AkIQRIZ+3tAOJMQ7G301xtrD1vekQbNeRO7eY1K8ZHk= From adf9a44ba56ac5b2620909d8ad05ecd7cf3c12a7 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 7 Jul 2024 15:52:22 +0000 Subject: [PATCH 25/39] Update module github.com/pion/datachannel to v1.5.8 Generated by renovateBot --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 4760838c5e2..cdb557db3b9 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/pion/webrtc/v4 go 1.19 require ( - github.com/pion/datachannel v1.5.7 + github.com/pion/datachannel v1.5.8 github.com/pion/dtls/v2 v2.2.11 github.com/pion/ice/v3 v3.0.9 github.com/pion/interceptor v0.1.29 diff --git a/go.sum b/go.sum index 023a5541b42..2901758807d 100644 --- a/go.sum +++ b/go.sum @@ -35,8 +35,8 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.17.0 h1:9Luw4uT5HTjHTN8+aNcSThgH1vdXnmdJ8xIfZ4wyTRE= github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= -github.com/pion/datachannel v1.5.7 h1:Z2NXdUnAwuHTrjS+CAiLdkfCYaD/Wa+Ly4FShIgICsA= -github.com/pion/datachannel v1.5.7/go.mod h1:5F8FSGAjVb63JcMmEBm2r6FDQGeijTuFLrE1QsYLVEo= +github.com/pion/datachannel v1.5.8 h1:ph1P1NsGkazkjrvyMfhRBUAWMxugJjq2HfQifaOoSNo= +github.com/pion/datachannel v1.5.8/go.mod h1:PgmdpoaNBLX9HNzNClmdki4DYW5JtI7Yibu8QzbL3tI= github.com/pion/dtls/v2 v2.2.7/go.mod h1:8WiMkebSHFD0T+dIU+UeBaoV7kDhOW5oDCzZ7WZ/F9s= github.com/pion/dtls/v2 v2.2.11 h1:9U/dpCYl1ySttROPWJgqWKEylUdT0fXp/xst6JwY5Ks= github.com/pion/dtls/v2 v2.2.11/go.mod h1:d9SYc9fch0CqK90mRk1dC7AkzzpwJj6u2GU3u+9pqFE= From 4dd839257daba73516cbab78f33592de4ee6ce02 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 7 Jul 2024 18:24:21 +0000 Subject: [PATCH 26/39] Update module github.com/pion/sctp to v1.8.19 Generated by renovateBot --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index cdb557db3b9..d43d00f2212 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/pion/randutil v0.1.0 github.com/pion/rtcp v1.2.14 github.com/pion/rtp v1.8.6 - github.com/pion/sctp v1.8.18 + github.com/pion/sctp v1.8.19 github.com/pion/sdp/v3 v3.0.9 github.com/pion/srtp/v3 v3.0.1 github.com/pion/stun/v2 v2.0.0 diff --git a/go.sum b/go.sum index 2901758807d..14560c21448 100644 --- a/go.sum +++ b/go.sum @@ -56,8 +56,8 @@ github.com/pion/rtcp v1.2.14/go.mod h1:sn6qjxvnwyAkkPzPULIbVqSKI5Dv54Rv7VG0kNxh9 github.com/pion/rtp v1.8.3/go.mod h1:pBGHaFt/yW7bf1jjWAoUjpSNoDnw98KTMg+jWWvziqU= github.com/pion/rtp v1.8.6 h1:MTmn/b0aWWsAzux2AmP8WGllusBVw4NPYPVFFd7jUPw= github.com/pion/rtp v1.8.6/go.mod h1:pBGHaFt/yW7bf1jjWAoUjpSNoDnw98KTMg+jWWvziqU= -github.com/pion/sctp v1.8.18 h1:zsqVBs7WF54QCEWnqve1acj9nc1dL25FllPF6C/bMaI= -github.com/pion/sctp v1.8.18/go.mod h1:P6PbDVA++OJMrVNg2AL3XtYHV4uD6dvfyOovCgMs0PE= +github.com/pion/sctp v1.8.19 h1:2CYuw+SQ5vkQ9t0HdOPccsCz1GQMDuVy5PglLgKVBW8= +github.com/pion/sctp v1.8.19/go.mod h1:P6PbDVA++OJMrVNg2AL3XtYHV4uD6dvfyOovCgMs0PE= github.com/pion/sdp/v3 v3.0.9 h1:pX++dCHoHUwq43kuwf3PyJfHlwIj4hXA7Vrifiq0IJY= github.com/pion/sdp/v3 v3.0.9/go.mod h1:B5xmvENq5IXJimIO4zfp6LAe1fD9N+kFv+V/1lOdz8M= github.com/pion/srtp/v3 v3.0.1 h1:AkIQRIZ+3tAOJMQ7G301xtrD1vekQbNeRO7eY1K8ZHk= From dc99b03894bf07f1a7522775f33244417a6df075 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 7 Jul 2024 22:03:00 +0000 Subject: [PATCH 27/39] Update module github.com/pion/srtp/v3 to v3.0.2 Generated by renovateBot --- go.mod | 2 +- go.sum | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/go.mod b/go.mod index d43d00f2212..1356aac2f19 100644 --- a/go.mod +++ b/go.mod @@ -13,7 +13,7 @@ require ( github.com/pion/rtp v1.8.6 github.com/pion/sctp v1.8.19 github.com/pion/sdp/v3 v3.0.9 - github.com/pion/srtp/v3 v3.0.1 + github.com/pion/srtp/v3 v3.0.2 github.com/pion/stun/v2 v2.0.0 github.com/pion/transport/v3 v3.0.2 github.com/sclevine/agouti v3.0.0+incompatible diff --git a/go.sum b/go.sum index 14560c21448..c21ed7caec7 100644 --- a/go.sum +++ b/go.sum @@ -50,18 +50,16 @@ github.com/pion/mdns/v2 v2.0.7 h1:c9kM8ewCgjslaAmicYMFQIde2H9/lrZpjBkN8VwoVtM= github.com/pion/mdns/v2 v2.0.7/go.mod h1:vAdSYNAT0Jy3Ru0zl2YiW3Rm/fJCwIeM0nToenfOJKA= github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA= github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8= -github.com/pion/rtcp v1.2.12/go.mod h1:sn6qjxvnwyAkkPzPULIbVqSKI5Dv54Rv7VG0kNxh9L4= github.com/pion/rtcp v1.2.14 h1:KCkGV3vJ+4DAJmvP0vaQShsb0xkRfWkO540Gy102KyE= github.com/pion/rtcp v1.2.14/go.mod h1:sn6qjxvnwyAkkPzPULIbVqSKI5Dv54Rv7VG0kNxh9L4= -github.com/pion/rtp v1.8.3/go.mod h1:pBGHaFt/yW7bf1jjWAoUjpSNoDnw98KTMg+jWWvziqU= github.com/pion/rtp v1.8.6 h1:MTmn/b0aWWsAzux2AmP8WGllusBVw4NPYPVFFd7jUPw= github.com/pion/rtp v1.8.6/go.mod h1:pBGHaFt/yW7bf1jjWAoUjpSNoDnw98KTMg+jWWvziqU= github.com/pion/sctp v1.8.19 h1:2CYuw+SQ5vkQ9t0HdOPccsCz1GQMDuVy5PglLgKVBW8= github.com/pion/sctp v1.8.19/go.mod h1:P6PbDVA++OJMrVNg2AL3XtYHV4uD6dvfyOovCgMs0PE= github.com/pion/sdp/v3 v3.0.9 h1:pX++dCHoHUwq43kuwf3PyJfHlwIj4hXA7Vrifiq0IJY= github.com/pion/sdp/v3 v3.0.9/go.mod h1:B5xmvENq5IXJimIO4zfp6LAe1fD9N+kFv+V/1lOdz8M= -github.com/pion/srtp/v3 v3.0.1 h1:AkIQRIZ+3tAOJMQ7G301xtrD1vekQbNeRO7eY1K8ZHk= -github.com/pion/srtp/v3 v3.0.1/go.mod h1:3R3a1qIOIxBkVTLGFjafKK6/fJoTdQDhcC67HOyMbJ8= +github.com/pion/srtp/v3 v3.0.2 h1:TFV1hGLjE6d6RoKbf+PpoyaH6Gp0uHQUonfD9tyw3Pc= +github.com/pion/srtp/v3 v3.0.2/go.mod h1:a3nDwSdwn7yzdqf1zGsBC6V89nOgfBzIkN2v3VojdIw= github.com/pion/stun/v2 v2.0.0 h1:A5+wXKLAypxQri59+tmQKVs7+l6mMM+3d+eER9ifRU0= github.com/pion/stun/v2 v2.0.0/go.mod h1:22qRSh08fSEttYUmJZGlriq9+03jtVmXNODgLccj8GQ= github.com/pion/transport/v2 v2.2.1/go.mod h1:cXXWavvCnFF6McHTft3DWS9iic2Mftcz1Aq29pGcU5g= From 6988aff3af3b2008a4778e6b163234c358674630 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 13 Jul 2024 20:14:42 +0000 Subject: [PATCH 28/39] Update module github.com/pion/rtp to v1.8.7 Generated by renovateBot --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 1356aac2f19..a5bb9555407 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/pion/logging v0.2.2 github.com/pion/randutil v0.1.0 github.com/pion/rtcp v1.2.14 - github.com/pion/rtp v1.8.6 + github.com/pion/rtp v1.8.7 github.com/pion/sctp v1.8.19 github.com/pion/sdp/v3 v3.0.9 github.com/pion/srtp/v3 v3.0.2 diff --git a/go.sum b/go.sum index c21ed7caec7..5f50af2139e 100644 --- a/go.sum +++ b/go.sum @@ -52,8 +52,8 @@ github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA= github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8= github.com/pion/rtcp v1.2.14 h1:KCkGV3vJ+4DAJmvP0vaQShsb0xkRfWkO540Gy102KyE= github.com/pion/rtcp v1.2.14/go.mod h1:sn6qjxvnwyAkkPzPULIbVqSKI5Dv54Rv7VG0kNxh9L4= -github.com/pion/rtp v1.8.6 h1:MTmn/b0aWWsAzux2AmP8WGllusBVw4NPYPVFFd7jUPw= -github.com/pion/rtp v1.8.6/go.mod h1:pBGHaFt/yW7bf1jjWAoUjpSNoDnw98KTMg+jWWvziqU= +github.com/pion/rtp v1.8.7 h1:qslKkG8qxvQ7hqaxkmL7Pl0XcUm+/Er7nMnu6Vq+ZxM= +github.com/pion/rtp v1.8.7/go.mod h1:pBGHaFt/yW7bf1jjWAoUjpSNoDnw98KTMg+jWWvziqU= github.com/pion/sctp v1.8.19 h1:2CYuw+SQ5vkQ9t0HdOPccsCz1GQMDuVy5PglLgKVBW8= github.com/pion/sctp v1.8.19/go.mod h1:P6PbDVA++OJMrVNg2AL3XtYHV4uD6dvfyOovCgMs0PE= github.com/pion/sdp/v3 v3.0.9 h1:pX++dCHoHUwq43kuwf3PyJfHlwIj4hXA7Vrifiq0IJY= From e17ce045898e5abea4eb154baefeca912761518d Mon Sep 17 00:00:00 2001 From: Sean DuBois Date: Mon, 15 Jul 2024 11:19:56 -0400 Subject: [PATCH 29/39] Revert 7c8bfbd44a and add test Don't block Close on spawned goroutines --- datachannel.go | 19 ------- peerconnection.go | 35 ++---------- peerconnection_close_test.go | 102 ----------------------------------- peerconnection_go_test.go | 34 ++++++++++++ 4 files changed, 38 insertions(+), 152 deletions(-) diff --git a/datachannel.go b/datachannel.go index 11e3f3e0c18..e975a04f7b5 100644 --- a/datachannel.go +++ b/datachannel.go @@ -40,7 +40,6 @@ type DataChannel struct { readyState atomic.Value // DataChannelState bufferedAmountLowThreshold uint64 detachCalled bool - readLoopActive chan struct{} // The binaryType represents attribute MUST, on getting, return the value to // which it was last set. On setting, if the new value is either the string @@ -328,7 +327,6 @@ func (d *DataChannel) handleOpen(dc *datachannel.DataChannel, isRemote, isAlread defer d.mu.Unlock() if !d.api.settingEngine.detach.DataChannels { - d.readLoopActive = make(chan struct{}) go d.readLoop() } } @@ -352,7 +350,6 @@ func (d *DataChannel) onError(err error) { } func (d *DataChannel) readLoop() { - defer close(d.readLoopActive) buffer := make([]byte, dataChannelBufferSize) for { n, isString, err := d.dataChannel.ReadDataChannel(buffer) @@ -452,22 +449,6 @@ func (d *DataChannel) Detach() (datachannel.ReadWriteCloser, error) { // Close Closes the DataChannel. It may be called regardless of whether // the DataChannel object was created by this peer or the remote peer. func (d *DataChannel) Close() error { - return d.close(false) -} - -// Normally, close only stops writes from happening, so waitForReadsDone=true -// will wait for reads to be finished based on underlying SCTP association -// closure or a SCTP reset stream from the other side. This is safe to call -// with waitForReadsDone=true after tearing down a PeerConnection but not -// necessarily before. For example, if you used a vnet and dropped all packets -// right before closing the DataChannel, you'd need never see a reset stream. -func (d *DataChannel) close(waitForReadsDone bool) error { - if waitForReadsDone && d.readLoopActive != nil { - defer func() { - <-d.readLoopActive - }() - } - d.mu.Lock() haveSctpTransport := d.dataChannel != nil d.mu.Unlock() diff --git a/peerconnection.go b/peerconnection.go index f3307c88bf7..3ae8b732c66 100644 --- a/peerconnection.go +++ b/peerconnection.go @@ -56,7 +56,6 @@ type PeerConnection struct { idpLoginURL *string isClosed *atomicBool - isClosedDone chan struct{} isNegotiationNeeded *atomicBool updateNegotiationNeededFlagOnEmptyChain *atomicBool @@ -117,7 +116,6 @@ func (api *API) NewPeerConnection(configuration Configuration) (*PeerConnection, ICECandidatePoolSize: 0, }, isClosed: &atomicBool{}, - isClosedDone: make(chan struct{}), isNegotiationNeeded: &atomicBool{}, updateNegotiationNeededFlagOnEmptyChain: &atomicBool{}, lastOffer: "", @@ -2046,31 +2044,14 @@ func (pc *PeerConnection) writeRTCP(pkts []rtcp.Packet, _ interceptor.Attributes return pc.dtlsTransport.WriteRTCP(pkts) } -// Close ends the PeerConnection. -// It will make a best effort to wait for all underlying goroutines it spawned to finish, -// except for cases that would cause deadlocks with itself. +// Close ends the PeerConnection func (pc *PeerConnection) Close() error { // https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-close (step #1) // https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-close (step #2) if pc.isClosed.swap(true) { - // someone else got here first but may still be closing (e.g. via DTLS close_notify) - <-pc.isClosedDone return nil } - defer close(pc.isClosedDone) - // Try closing everything and collect the errors - // Shutdown strategy: - // 1. Close all data channels. - // 2. All Conn close by closing their underlying Conn. - // 3. A Mux stops this chain. It won't close the underlying - // Conn if one of the endpoints is closed down. To - // continue the chain the Mux has to be closed. - pc.sctpTransport.lock.Lock() - closeErrs := make([]error, 0, 4+len(pc.sctpTransport.dataChannels)) - pc.sctpTransport.lock.Unlock() - - // canon steps // https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-close (step #3) pc.signalingState.Set(SignalingStateClosed) @@ -2080,6 +2061,7 @@ func (pc *PeerConnection) Close() error { // 2. A Mux stops this chain. It won't close the underlying // Conn if one of the endpoints is closed down. To // continue the chain the Mux has to be closed. + closeErrs := make([]error, 4) closeErrs = append(closeErrs, pc.api.interceptor.Close()) @@ -2106,6 +2088,7 @@ func (pc *PeerConnection) Close() error { // https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-close (step #7) closeErrs = append(closeErrs, pc.dtlsTransport.Stop()) + // https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-close (step #8, #9, #10) if pc.iceTransport != nil { closeErrs = append(closeErrs, pc.iceTransport.Stop()) @@ -2114,13 +2097,6 @@ func (pc *PeerConnection) Close() error { // https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-close (step #11) pc.updateConnectionState(pc.ICEConnectionState(), pc.dtlsTransport.State()) - // non-canon steps - pc.sctpTransport.lock.Lock() - for _, d := range pc.sctpTransport.dataChannels { - closeErrs = append(closeErrs, d.close(true)) - } - pc.sctpTransport.lock.Unlock() - return util.FlattenErrs(closeErrs) } @@ -2292,11 +2268,8 @@ func (pc *PeerConnection) startTransports(iceRole ICERole, dtlsRole DTLSRole, re } pc.dtlsTransport.internalOnCloseHandler = func() { - if pc.isClosed.get() { - return - } - pc.log.Info("Closing PeerConnection from DTLS CloseNotify") + go func() { if pcClosErr := pc.Close(); pcClosErr != nil { pc.log.Warnf("Failed to close PeerConnection from DTLS CloseNotify: %s", pcClosErr) diff --git a/peerconnection_close_test.go b/peerconnection_close_test.go index ac3b23f704d..5360d701fc2 100644 --- a/peerconnection_close_test.go +++ b/peerconnection_close_test.go @@ -7,8 +7,6 @@ package webrtc import ( - "runtime" - "strings" "testing" "time" @@ -181,103 +179,3 @@ func TestPeerConnection_Close_DuringICE(t *testing.T) { t.Error("pcOffer.Close() Timeout") } } - -func TestPeerConnection_CloseWithIncomingMessages(t *testing.T) { - // Limit runtime in case of deadlocks - lim := test.TimeOut(time.Second * 20) - defer lim.Stop() - - report := CheckRoutinesIntolerant(t) - defer report() - - pcOffer, pcAnswer, err := newPair() - if err != nil { - t.Fatal(err) - } - - var dcAnswer *DataChannel - answerDataChannelOpened := make(chan struct{}) - pcAnswer.OnDataChannel(func(d *DataChannel) { - // Make sure this is the data channel we were looking for. (Not the one - // created in signalPair). - if d.Label() != "data" { - return - } - dcAnswer = d - close(answerDataChannelOpened) - }) - - dcOffer, err := pcOffer.CreateDataChannel("data", nil) - if err != nil { - t.Fatal(err) - } - - offerDataChannelOpened := make(chan struct{}) - dcOffer.OnOpen(func() { - close(offerDataChannelOpened) - }) - - err = signalPair(pcOffer, pcAnswer) - if err != nil { - t.Fatal(err) - } - - <-offerDataChannelOpened - <-answerDataChannelOpened - - msgNum := 0 - dcOffer.OnMessage(func(_ DataChannelMessage) { - t.Log("msg", msgNum) - msgNum++ - }) - - // send 50 messages, then close pcOffer, and then send another 50 - for i := 0; i < 100; i++ { - if i == 50 { - err = pcOffer.Close() - if err != nil { - t.Fatal(err) - } - } - _ = dcAnswer.Send([]byte("hello!")) - } - - err = pcAnswer.Close() - if err != nil { - t.Fatal(err) - } -} - -// CheckRoutinesIntolerant is used to check for leaked go-routines. -// It differs from test.CheckRoutines in that it won't wait at all -// for lingering goroutines. This is helpful for tests that need -// to ensure clean closure of resources. -func CheckRoutinesIntolerant(t *testing.T) func() { - return func() { - routines := getRoutines() - if len(routines) == 0 { - return - } - t.Fatalf("%s: \n%s", "Unexpected routines on test end", strings.Join(routines, "\n\n")) // nolint - } -} - -func getRoutines() []string { - buf := make([]byte, 2<<20) - buf = buf[:runtime.Stack(buf, true)] - return filterRoutines(strings.Split(string(buf), "\n\n")) -} - -func filterRoutines(routines []string) []string { - result := []string{} - for _, stack := range routines { - if stack == "" || // Empty - strings.Contains(stack, "testing.Main(") || // Tests - strings.Contains(stack, "testing.(*T).Run(") || // Test run - strings.Contains(stack, "getRoutines(") { // This routine - continue - } - result = append(result, stack) - } - return result -} diff --git a/peerconnection_go_test.go b/peerconnection_go_test.go index e36c62f75b4..2cafdd68a07 100644 --- a/peerconnection_go_test.go +++ b/peerconnection_go_test.go @@ -1606,3 +1606,37 @@ func TestPeerConnectionState(t *testing.T) { assert.NoError(t, pc.Close()) assert.Equal(t, PeerConnectionStateClosed, pc.ConnectionState()) } + +func TestPeerConnectionDeadlock(t *testing.T) { + lim := test.TimeOut(time.Second * 5) + defer lim.Stop() + + report := test.CheckRoutines(t) + defer report() + + closeHdlr := func(peerConnection *PeerConnection) { + peerConnection.OnICEConnectionStateChange(func(i ICEConnectionState) { + if i == ICEConnectionStateFailed || i == ICEConnectionStateClosed { + if err := peerConnection.Close(); err != nil { + assert.NoError(t, err) + } + } + }) + } + + pcOffer, pcAnswer, err := NewAPI().newPair(Configuration{}) + assert.NoError(t, err) + + assert.NoError(t, signalPair(pcOffer, pcAnswer)) + + onDataChannel, onDataChannelCancel := context.WithCancel(context.Background()) + pcAnswer.OnDataChannel(func(*DataChannel) { + onDataChannelCancel() + }) + <-onDataChannel.Done() + + closeHdlr(pcOffer) + closeHdlr(pcAnswer) + + closePairNow(t, pcOffer, pcAnswer) +} From 166d82e1e82d5add391f4270a36a8ebd167bc5c2 Mon Sep 17 00:00:00 2001 From: Sean DuBois Date: Mon, 15 Jul 2024 12:23:41 -0400 Subject: [PATCH 30/39] Update module github.com/pion/ice/v3 to v3.0.10 --- go.mod | 10 +++++----- go.sum | 19 ++++++++++--------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/go.mod b/go.mod index a5bb9555407..2ddd6303a19 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.19 require ( github.com/pion/datachannel v1.5.8 github.com/pion/dtls/v2 v2.2.11 - github.com/pion/ice/v3 v3.0.9 + github.com/pion/ice/v3 v3.0.10 github.com/pion/interceptor v0.1.29 github.com/pion/logging v0.2.2 github.com/pion/randutil v0.1.0 @@ -18,7 +18,7 @@ require ( github.com/pion/transport/v3 v3.0.2 github.com/sclevine/agouti v3.0.0+incompatible github.com/stretchr/testify v1.9.0 - golang.org/x/net v0.26.0 + golang.org/x/net v0.27.0 ) require ( @@ -27,10 +27,10 @@ require ( github.com/onsi/ginkgo v1.16.5 // indirect github.com/onsi/gomega v1.17.0 // indirect github.com/pion/mdns/v2 v2.0.7 // indirect - github.com/pion/transport/v2 v2.2.4 // indirect + github.com/pion/transport/v2 v2.2.5 // indirect github.com/pion/turn/v3 v3.0.3 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - golang.org/x/crypto v0.24.0 // indirect - golang.org/x/sys v0.21.0 // indirect + golang.org/x/crypto v0.25.0 // indirect + golang.org/x/sys v0.22.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 5f50af2139e..a4463877a92 100644 --- a/go.sum +++ b/go.sum @@ -40,8 +40,8 @@ github.com/pion/datachannel v1.5.8/go.mod h1:PgmdpoaNBLX9HNzNClmdki4DYW5JtI7Yibu github.com/pion/dtls/v2 v2.2.7/go.mod h1:8WiMkebSHFD0T+dIU+UeBaoV7kDhOW5oDCzZ7WZ/F9s= github.com/pion/dtls/v2 v2.2.11 h1:9U/dpCYl1ySttROPWJgqWKEylUdT0fXp/xst6JwY5Ks= github.com/pion/dtls/v2 v2.2.11/go.mod h1:d9SYc9fch0CqK90mRk1dC7AkzzpwJj6u2GU3u+9pqFE= -github.com/pion/ice/v3 v3.0.9 h1:AAJQSCJa5qqDJ2pe9TOH4YTamP5m7PJeoQ58ey3McFg= -github.com/pion/ice/v3 v3.0.9/go.mod h1:aSHw7r4guKNumxc5dCWfzWga+qtz1ki/nnpHfJTSLTs= +github.com/pion/ice/v3 v3.0.10 h1:ZRNZkZW4C/CFniAfEH7Ix4hW+TVGENsNkzebYY72TYY= +github.com/pion/ice/v3 v3.0.10/go.mod h1:aSHw7r4guKNumxc5dCWfzWga+qtz1ki/nnpHfJTSLTs= github.com/pion/interceptor v0.1.29 h1:39fsnlP1U8gw2JzOFWdfCU82vHvhW9o0rZnZF56wF+M= github.com/pion/interceptor v0.1.29/go.mod h1:ri+LGNjRUc5xUNtDEPzfdkmSqISixVTBF/z/Zms/6T4= github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY= @@ -63,8 +63,9 @@ github.com/pion/srtp/v3 v3.0.2/go.mod h1:a3nDwSdwn7yzdqf1zGsBC6V89nOgfBzIkN2v3Vo github.com/pion/stun/v2 v2.0.0 h1:A5+wXKLAypxQri59+tmQKVs7+l6mMM+3d+eER9ifRU0= github.com/pion/stun/v2 v2.0.0/go.mod h1:22qRSh08fSEttYUmJZGlriq9+03jtVmXNODgLccj8GQ= github.com/pion/transport/v2 v2.2.1/go.mod h1:cXXWavvCnFF6McHTft3DWS9iic2Mftcz1Aq29pGcU5g= -github.com/pion/transport/v2 v2.2.4 h1:41JJK6DZQYSeVLxILA2+F4ZkKb4Xd/tFJZRFZQ9QAlo= github.com/pion/transport/v2 v2.2.4/go.mod h1:q2U/tf9FEfnSBGSW6w5Qp5PFWRLRj3NjLhCCgpRK4p0= +github.com/pion/transport/v2 v2.2.5 h1:iyi25i/21gQck4hfRhomF6SktmUQjRsRW4WJdhfc3Kc= +github.com/pion/transport/v2 v2.2.5/go.mod h1:q2U/tf9FEfnSBGSW6w5Qp5PFWRLRj3NjLhCCgpRK4p0= github.com/pion/transport/v3 v3.0.1/go.mod h1:UY7kiITrlMv7/IKgd5eTUcaahZx5oUN3l9SzK5f5xE0= github.com/pion/transport/v3 v3.0.2 h1:r+40RJR25S9w3jbA6/5uEPTzcdn7ncyU44RWCbHkLg4= github.com/pion/transport/v3 v3.0.2/go.mod h1:nIToODoOlb5If2jF9y2Igfx3PFYWfuXi37m0IlWa/D0= @@ -96,8 +97,8 @@ golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98y golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= -golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= -golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= +golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= +golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= @@ -116,8 +117,8 @@ golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= -golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= -golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= +golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys= +golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -144,8 +145,8 @@ golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= -golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= +golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= From 287d10638dbf8a682555de39d2a6814213e595fb Mon Sep 17 00:00:00 2001 From: cnderrauber Date: Thu, 18 Jul 2024 10:43:27 +0800 Subject: [PATCH 31/39] Match header extensions to remote media sections Firefox would send updated header extension in renegotiation, e.g. publish a track without simucalst then renegotiate second track with simucalst, the two media secontions will have different rtp header extensions in offer. Need to match remote header extentions for each media sections to avoid second track publish failed. --- mediaengine.go | 54 ++++++++++++++++++++++++++++-------- mediaengine_test.go | 33 ++++++++++++++++++++++ peerconnection.go | 4 ++- peerconnection_media_test.go | 7 +++-- sdp.go | 14 +++++++--- 5 files changed, 93 insertions(+), 19 deletions(-) diff --git a/mediaengine.go b/mediaengine.go index cbdcfb6d7d5..47a08d2f24c 100644 --- a/mediaengine.go +++ b/mediaengine.go @@ -455,6 +455,30 @@ func (m *MediaEngine) matchRemoteCodec(remoteCodec RTPCodecParameters, typ RTPCo return matchType, nil } +// Update header extensions from a remote media section +func (m *MediaEngine) updateHeaderExtensionFromMediaSection(media *sdp.MediaDescription) error { + var typ RTPCodecType + switch { + case strings.EqualFold(media.MediaName.Media, "audio"): + typ = RTPCodecTypeAudio + case strings.EqualFold(media.MediaName.Media, "video"): + typ = RTPCodecTypeVideo + default: + return nil + } + extensions, err := rtpExtensionsFromMediaDescription(media) + if err != nil { + return err + } + + for extension, id := range extensions { + if err = m.updateHeaderExtension(id, extension, typ); err != nil { + return err + } + } + return nil +} + // Look up a header extension and enable if it exists func (m *MediaEngine) updateHeaderExtension(id int, extension string, typ RTPCodecType) error { if m.negotiatedHeaderExtensions == nil { @@ -498,14 +522,27 @@ func (m *MediaEngine) updateFromRemoteDescription(desc sdp.SessionDescription) e for _, media := range desc.MediaDescriptions { var typ RTPCodecType + switch { - case !m.negotiatedAudio && strings.EqualFold(media.MediaName.Media, "audio"): - m.negotiatedAudio = true + case strings.EqualFold(media.MediaName.Media, "audio"): typ = RTPCodecTypeAudio - case !m.negotiatedVideo && strings.EqualFold(media.MediaName.Media, "video"): - m.negotiatedVideo = true + case strings.EqualFold(media.MediaName.Media, "video"): typ = RTPCodecTypeVideo + } + + switch { + case !m.negotiatedAudio && typ == RTPCodecTypeAudio: + m.negotiatedAudio = true + case !m.negotiatedVideo && typ == RTPCodecTypeVideo: + m.negotiatedVideo = true default: + // update header extesions from remote sdp if codec is negotiated, Firefox + // would send updated header extension in renegotiation. + // e.g. publish first track without simucalst ->negotiated-> publish second track with simucalst + // then the two media secontions have different rtp header extensions in offer + if err := m.updateHeaderExtensionFromMediaSection(media); err != nil { + return err + } continue } @@ -541,16 +578,9 @@ func (m *MediaEngine) updateFromRemoteDescription(desc sdp.SessionDescription) e continue } - extensions, err := rtpExtensionsFromMediaDescription(media) - if err != nil { + if err := m.updateHeaderExtensionFromMediaSection(media); err != nil { return err } - - for extension, id := range extensions { - if err = m.updateHeaderExtension(id, extension, typ); err != nil { - return err - } - } } return nil } diff --git a/mediaengine_test.go b/mediaengine_test.go index d9a9e7b1ff4..28f76069fbe 100644 --- a/mediaengine_test.go +++ b/mediaengine_test.go @@ -212,6 +212,39 @@ a=rtpmap:111 opus/48000/2 assert.False(t, midVideoEnabled) }) + t.Run("Different Header Extensions on same codec", func(t *testing.T) { + const headerExtensions = `v=0 +o=- 4596489990601351948 2 IN IP4 127.0.0.1 +s=- +t=0 0 +m=audio 9 UDP/TLS/RTP/SAVPF 111 +a=rtpmap:111 opus/48000/2 +m=audio 9 UDP/TLS/RTP/SAVPF 111 +a=extmap:7 urn:ietf:params:rtp-hdrext:sdes:mid +a=extmap:5 urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id +a=rtpmap:111 opus/48000/2 +` + + m := MediaEngine{} + assert.NoError(t, m.RegisterDefaultCodecs()) + assert.NoError(t, m.RegisterHeaderExtension(RTPHeaderExtensionCapability{URI: "urn:ietf:params:rtp-hdrext:sdes:mid"}, RTPCodecTypeAudio)) + assert.NoError(t, m.RegisterHeaderExtension(RTPHeaderExtensionCapability{URI: "urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id"}, RTPCodecTypeAudio)) + assert.NoError(t, m.updateFromRemoteDescription(mustParse(headerExtensions))) + + assert.False(t, m.negotiatedVideo) + assert.True(t, m.negotiatedAudio) + + absID, absAudioEnabled, absVideoEnabled := m.getHeaderExtensionID(RTPHeaderExtensionCapability{sdp.ABSSendTimeURI}) + assert.Equal(t, absID, 0) + assert.False(t, absAudioEnabled) + assert.False(t, absVideoEnabled) + + midID, midAudioEnabled, midVideoEnabled := m.getHeaderExtensionID(RTPHeaderExtensionCapability{sdp.SDESMidURI}) + assert.Equal(t, midID, 7) + assert.True(t, midAudioEnabled) + assert.False(t, midVideoEnabled) + }) + t.Run("Prefers exact codec matches", func(t *testing.T) { const profileLevels = `v=0 o=- 4596489990601351948 2 IN IP4 127.0.0.1 diff --git a/peerconnection.go b/peerconnection.go index 3ae8b732c66..7a7e4835fba 100644 --- a/peerconnection.go +++ b/peerconnection.go @@ -2465,7 +2465,9 @@ func (pc *PeerConnection) generateMatchedSDP(transceivers []*RTPTransceiver, use sender.setNegotiated() } mediaTransceivers := []*RTPTransceiver{t} - mediaSections = append(mediaSections, mediaSection{id: midValue, transceivers: mediaTransceivers, ridMap: getRids(media)}) + + extensions, _ := rtpExtensionsFromMediaDescription(media) + mediaSections = append(mediaSections, mediaSection{id: midValue, transceivers: mediaTransceivers, matchExtensions: extensions, ridMap: getRids(media)}) } } diff --git a/peerconnection_media_test.go b/peerconnection_media_test.go index 1a56dfb4bb7..0e29d90f400 100644 --- a/peerconnection_media_test.go +++ b/peerconnection_media_test.go @@ -1080,6 +1080,9 @@ func TestPeerConnection_Simulcast_Probe(t *testing.T) { return })) + peerConnectionConnected := untilConnectionState(PeerConnectionStateConnected, pcOffer, pcAnswer) + peerConnectionConnected.Wait() + sequenceNumber := uint16(0) sendRTPPacket := func() { sequenceNumber++ @@ -1097,13 +1100,13 @@ func TestPeerConnection_Simulcast_Probe(t *testing.T) { sendRTPPacket() } - assert.NoError(t, signalPair(pcOffer, pcAnswer)) - trackRemoteChan := make(chan *TrackRemote, 1) pcAnswer.OnTrack(func(trackRemote *TrackRemote, _ *RTPReceiver) { trackRemoteChan <- trackRemote }) + assert.NoError(t, signalPair(pcOffer, pcAnswer)) + trackRemote := func() *TrackRemote { for { select { diff --git a/sdp.go b/sdp.go index 7ab0fd89807..f1b59940b10 100644 --- a/sdp.go +++ b/sdp.go @@ -487,6 +487,11 @@ func addTransceiverSDP( parameters := mediaEngine.getRTPParametersByKind(t.kind, directions) for _, rtpExtension := range parameters.HeaderExtensions { + if mediaSection.matchExtensions != nil { + if _, enabled := mediaSection.matchExtensions[rtpExtension.URI]; !enabled { + continue + } + } extURL, err := url.Parse(rtpExtension.URI) if err != nil { return false, err @@ -533,10 +538,11 @@ type simulcastRid struct { } type mediaSection struct { - id string - transceivers []*RTPTransceiver - data bool - ridMap map[string]*simulcastRid + id string + transceivers []*RTPTransceiver + data bool + matchExtensions map[string]int + ridMap map[string]*simulcastRid } func bundleMatchFromRemote(matchBundleGroup *string) func(mid string) bool { From ce37669ddab8bf4805c7bdffb033a427f527cc56 Mon Sep 17 00:00:00 2001 From: Sean DuBois Date: Thu, 18 Jul 2024 15:21:46 -0400 Subject: [PATCH 32/39] Delete deadcode in pion-to-pion/answer Was accidentally copied, never needed --- examples/pion-to-pion/answer/main.go | 46 ---------------------------- 1 file changed, 46 deletions(-) diff --git a/examples/pion-to-pion/answer/main.go b/examples/pion-to-pion/answer/main.go index 01dfbaa14ed..dd4dd7ab11e 100644 --- a/examples/pion-to-pion/answer/main.go +++ b/examples/pion-to-pion/answer/main.go @@ -5,17 +5,13 @@ package main import ( - "bufio" "bytes" - "encoding/base64" "encoding/json" - "errors" "flag" "fmt" "io" "net/http" "os" - "strings" "sync" "time" @@ -191,45 +187,3 @@ func main() { // nolint:gocognit // nolint: gosec panic(http.ListenAndServe(*answerAddr, nil)) } - -// Read from stdin until we get a newline -func readUntilNewline() (in string) { - var err error - - r := bufio.NewReader(os.Stdin) - for { - in, err = r.ReadString('\n') - if err != nil && !errors.Is(err, io.EOF) { - panic(err) - } - - if in = strings.TrimSpace(in); len(in) > 0 { - break - } - } - - fmt.Println("") - return -} - -// JSON encode + base64 a SessionDescription -func encode(obj *webrtc.SessionDescription) string { - b, err := json.Marshal(obj) - if err != nil { - panic(err) - } - - return base64.StdEncoding.EncodeToString(b) -} - -// Decode a base64 and unmarshal JSON into a SessionDescription -func decode(in string, obj *webrtc.SessionDescription) { - b, err := base64.StdEncoding.DecodeString(in) - if err != nil { - panic(err) - } - - if err = json.Unmarshal(b, obj); err != nil { - panic(err) - } -} From 8780e68cb78b533bb0a617c7f4ec9c064527e632 Mon Sep 17 00:00:00 2001 From: Sean DuBois Date: Thu, 18 Jul 2024 15:40:11 -0400 Subject: [PATCH 33/39] Add nolint to examples Ignore err from Fprintf --- examples/broadcast/main.go | 2 +- examples/ortc-media/main.go | 2 +- examples/ortc/main.go | 2 +- examples/whip-whep/main.go | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/broadcast/main.go b/examples/broadcast/main.go index c92971c5975..47eb7f2df9f 100644 --- a/examples/broadcast/main.go +++ b/examples/broadcast/main.go @@ -226,7 +226,7 @@ func httpSDPServer(port int) chan string { sdpChan := make(chan string) http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { body, _ := io.ReadAll(r.Body) - fmt.Fprintf(w, "done") + fmt.Fprintf(w, "done") //nolint: errcheck sdpChan <- string(body) }) diff --git a/examples/ortc-media/main.go b/examples/ortc-media/main.go index 2029554e60e..f601280019e 100644 --- a/examples/ortc-media/main.go +++ b/examples/ortc-media/main.go @@ -297,7 +297,7 @@ func httpSDPServer(port int) chan string { sdpChan := make(chan string) http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { body, _ := io.ReadAll(r.Body) - fmt.Fprintf(w, "done") + fmt.Fprintf(w, "done") //nolint: errcheck sdpChan <- string(body) }) diff --git a/examples/ortc/main.go b/examples/ortc/main.go index d9981896b00..70098d0fad2 100644 --- a/examples/ortc/main.go +++ b/examples/ortc/main.go @@ -244,7 +244,7 @@ func httpSDPServer(port int) chan string { sdpChan := make(chan string) http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { body, _ := io.ReadAll(r.Body) - fmt.Fprintf(w, "done") + fmt.Fprintf(w, "done") //nolint: errcheck sdpChan <- string(body) }) diff --git a/examples/whip-whep/main.go b/examples/whip-whep/main.go index b2cc83a591f..f5676e61680 100644 --- a/examples/whip-whep/main.go +++ b/examples/whip-whep/main.go @@ -193,5 +193,5 @@ func writeAnswer(w http.ResponseWriter, peerConnection *webrtc.PeerConnection, o w.WriteHeader(http.StatusCreated) // Write Answer with Candidates as HTTP Response - fmt.Fprint(w, peerConnection.LocalDescription().SDP) + fmt.Fprint(w, peerConnection.LocalDescription().SDP) //nolint: errcheck } From 9c6604df0a46b1a32eb7f7180919a0afae9caf11 Mon Sep 17 00:00:00 2001 From: Sean DuBois Date: Thu, 18 Jul 2024 16:14:23 -0400 Subject: [PATCH 34/39] Drop calls to RegisterDefaultCodecs in tests Not needed anymore --- api_test.go | 3 --- interceptor_test.go | 10 ++-------- mediaengine_test.go | 2 +- ortc_media_test.go | 3 --- peerconnection_go_test.go | 4 +--- peerconnection_renegotiation_test.go | 1 - rtpsender_test.go | 5 +---- 7 files changed, 5 insertions(+), 23 deletions(-) diff --git a/api_test.go b/api_test.go index 3724fba9c32..06f27697fcc 100644 --- a/api_test.go +++ b/api_test.go @@ -31,12 +31,9 @@ func TestNewAPI(t *testing.T) { func TestNewAPI_Options(t *testing.T) { s := SettingEngine{} s.DetachDataChannels() - m := MediaEngine{} - assert.NoError(t, m.RegisterDefaultCodecs()) api := NewAPI( WithSettingEngine(s), - WithMediaEngine(&m), ) if !api.settingEngine.detach.DataChannels { diff --git a/interceptor_test.go b/interceptor_test.go index 31cfe058341..aad610bb35d 100644 --- a/interceptor_test.go +++ b/interceptor_test.go @@ -33,9 +33,6 @@ func TestPeerConnection_Interceptor(t *testing.T) { defer report() createPC := func() *PeerConnection { - m := &MediaEngine{} - assert.NoError(t, m.RegisterDefaultCodecs()) - ir := &interceptor.Registry{} ir.Add(&mock_interceptor.Factory{ NewInterceptorFn: func(_ string) (interceptor.Interceptor, error) { @@ -64,7 +61,7 @@ func TestPeerConnection_Interceptor(t *testing.T) { }, }) - pc, err := NewAPI(WithMediaEngine(m), WithInterceptorRegistry(ir)).NewPeerConnection(Configuration{}) + pc, err := NewAPI(WithInterceptorRegistry(ir)).NewPeerConnection(Configuration{}) assert.NoError(t, err) return pc @@ -115,9 +112,6 @@ func Test_Interceptor_BindUnbind(t *testing.T) { report := test.CheckRoutines(t) defer report() - m := &MediaEngine{} - assert.NoError(t, m.RegisterDefaultCodecs()) - var ( cntBindRTCPReader uint32 cntBindRTCPWriter uint32 @@ -160,7 +154,7 @@ func Test_Interceptor_BindUnbind(t *testing.T) { NewInterceptorFn: func(_ string) (interceptor.Interceptor, error) { return mockInterceptor, nil }, }) - sender, receiver, err := NewAPI(WithMediaEngine(m), WithInterceptorRegistry(ir)).newPair(Configuration{}) + sender, receiver, err := NewAPI(WithInterceptorRegistry(ir)).newPair(Configuration{}) assert.NoError(t, err) track, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion") diff --git a/mediaengine_test.go b/mediaengine_test.go index 28f76069fbe..786cbcac0af 100644 --- a/mediaengine_test.go +++ b/mediaengine_test.go @@ -341,7 +341,7 @@ a=rtpmap:96 VP8/90000 o=- 4596489990601351948 2 IN IP4 127.0.0.1 s=- t=0 0 -m=video 60323 UDP/TLS/RTP/SAVPF 94 95 106 107 108 109 96 97 +m=video 60323 UDP/TLS/RTP/SAVPF 94 95 106 107 108 109 96 97 a=rtpmap:94 VP8/90000 a=rtpmap:95 rtx/90000 a=fmtp:95 apt=94 diff --git a/ortc_media_test.go b/ortc_media_test.go index 5d9624166dd..7d81662e60e 100644 --- a/ortc_media_test.go +++ b/ortc_media_test.go @@ -26,9 +26,6 @@ func Test_ORTC_Media(t *testing.T) { stackA, stackB, err := newORTCPair() assert.NoError(t, err) - assert.NoError(t, stackA.api.mediaEngine.RegisterDefaultCodecs()) - assert.NoError(t, stackB.api.mediaEngine.RegisterDefaultCodecs()) - assert.NoError(t, signalORTCPair(stackA, stackB)) track, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion") diff --git a/peerconnection_go_test.go b/peerconnection_go_test.go index 2cafdd68a07..1123edf8fed 100644 --- a/peerconnection_go_test.go +++ b/peerconnection_go_test.go @@ -1030,7 +1030,6 @@ func (r *trackRecords) remains() int { // This test assure that all track events emits. func TestPeerConnection_MassiveTracks(t *testing.T) { var ( - api = NewAPI() tRecs = &trackRecords{ trackIDs: make(map[string]struct{}), receivedTrackIDs: make(map[string]struct{}), @@ -1059,8 +1058,7 @@ func TestPeerConnection_MassiveTracks(t *testing.T) { connected = make(chan struct{}) stopped = make(chan struct{}) ) - assert.NoError(t, api.mediaEngine.RegisterDefaultCodecs()) - offerPC, answerPC, err := api.newPair(Configuration{}) + offerPC, answerPC, err := newPair() assert.NoError(t, err) // Create massive tracks. for range make([]struct{}, trackCount) { diff --git a/peerconnection_renegotiation_test.go b/peerconnection_renegotiation_test.go index ac923d9dab9..33bc714f774 100644 --- a/peerconnection_renegotiation_test.go +++ b/peerconnection_renegotiation_test.go @@ -603,7 +603,6 @@ func TestPeerConnection_Renegotiation_Trickle(t *testing.T) { settingEngine := SettingEngine{} api := NewAPI(WithSettingEngine(settingEngine)) - assert.NoError(t, api.mediaEngine.RegisterDefaultCodecs()) // Invalid STUN server on purpose, will stop ICE Gathering from completing in time pcOffer, pcAnswer, err := api.newPair(Configuration{ diff --git a/rtpsender_test.go b/rtpsender_test.go index cd77976f96e..9b4863ca4aa 100644 --- a/rtpsender_test.go +++ b/rtpsender_test.go @@ -29,10 +29,7 @@ func Test_RTPSender_ReplaceTrack(t *testing.T) { s := SettingEngine{} s.DisableSRTPReplayProtection(true) - m := &MediaEngine{} - assert.NoError(t, m.RegisterDefaultCodecs()) - - sender, receiver, err := NewAPI(WithMediaEngine(m), WithSettingEngine(s)).newPair(Configuration{}) + sender, receiver, err := NewAPI(WithSettingEngine(s)).newPair(Configuration{}) assert.NoError(t, err) trackA, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion") From b874788a069b769d1a7fa53d2f45fe7de67b8755 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 18 Jul 2024 21:35:58 +0000 Subject: [PATCH 35/39] Update module github.com/pion/srtp/v3 to v3.0.3 Generated by renovateBot --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 2ddd6303a19..71bf5d2170b 100644 --- a/go.mod +++ b/go.mod @@ -13,7 +13,7 @@ require ( github.com/pion/rtp v1.8.7 github.com/pion/sctp v1.8.19 github.com/pion/sdp/v3 v3.0.9 - github.com/pion/srtp/v3 v3.0.2 + github.com/pion/srtp/v3 v3.0.3 github.com/pion/stun/v2 v2.0.0 github.com/pion/transport/v3 v3.0.2 github.com/sclevine/agouti v3.0.0+incompatible diff --git a/go.sum b/go.sum index a4463877a92..617d2843ef3 100644 --- a/go.sum +++ b/go.sum @@ -58,8 +58,8 @@ github.com/pion/sctp v1.8.19 h1:2CYuw+SQ5vkQ9t0HdOPccsCz1GQMDuVy5PglLgKVBW8= github.com/pion/sctp v1.8.19/go.mod h1:P6PbDVA++OJMrVNg2AL3XtYHV4uD6dvfyOovCgMs0PE= github.com/pion/sdp/v3 v3.0.9 h1:pX++dCHoHUwq43kuwf3PyJfHlwIj4hXA7Vrifiq0IJY= github.com/pion/sdp/v3 v3.0.9/go.mod h1:B5xmvENq5IXJimIO4zfp6LAe1fD9N+kFv+V/1lOdz8M= -github.com/pion/srtp/v3 v3.0.2 h1:TFV1hGLjE6d6RoKbf+PpoyaH6Gp0uHQUonfD9tyw3Pc= -github.com/pion/srtp/v3 v3.0.2/go.mod h1:a3nDwSdwn7yzdqf1zGsBC6V89nOgfBzIkN2v3VojdIw= +github.com/pion/srtp/v3 v3.0.3 h1:tRtEOpmR8NtsB/KndlKXFOj/AIIs6aPrCq4TlAatC4M= +github.com/pion/srtp/v3 v3.0.3/go.mod h1:Bp9ztzPCoE0ETca/R+bTVTO5kBgaQMiQkTmZWwazDTc= github.com/pion/stun/v2 v2.0.0 h1:A5+wXKLAypxQri59+tmQKVs7+l6mMM+3d+eER9ifRU0= github.com/pion/stun/v2 v2.0.0/go.mod h1:22qRSh08fSEttYUmJZGlriq9+03jtVmXNODgLccj8GQ= github.com/pion/transport/v2 v2.2.1/go.mod h1:cXXWavvCnFF6McHTft3DWS9iic2Mftcz1Aq29pGcU5g= From 17d3e97b59c3995937b020174a45fe02a1cee03c Mon Sep 17 00:00:00 2001 From: Eric Daniels Date: Fri, 19 Jul 2024 11:25:35 -0400 Subject: [PATCH 36/39] Hold pc.mu while populating local candidates --- peerconnection.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/peerconnection.go b/peerconnection.go index 7a7e4835fba..67af02bc639 100644 --- a/peerconnection.go +++ b/peerconnection.go @@ -2114,10 +2114,11 @@ func (pc *PeerConnection) addRTPTransceiver(t *RTPTransceiver) { // by the ICEAgent since the offer or answer was created. func (pc *PeerConnection) CurrentLocalDescription() *SessionDescription { pc.mu.Lock() + defer pc.mu.Unlock() + localDescription := pc.currentLocalDescription iceGather := pc.iceGatherer iceGatheringState := pc.ICEGatheringState() - pc.mu.Unlock() return populateLocalCandidates(localDescription, iceGather, iceGatheringState) } @@ -2127,10 +2128,11 @@ func (pc *PeerConnection) CurrentLocalDescription() *SessionDescription { // PeerConnection is in the stable state, the value is null. func (pc *PeerConnection) PendingLocalDescription() *SessionDescription { pc.mu.Lock() + defer pc.mu.Unlock() + localDescription := pc.pendingLocalDescription iceGather := pc.iceGatherer iceGatheringState := pc.ICEGatheringState() - pc.mu.Unlock() return populateLocalCandidates(localDescription, iceGather, iceGatheringState) } From 064006d6d335bfceda27b078d90e945bb5c26b6e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 20 Jul 2024 20:01:24 +0000 Subject: [PATCH 37/39] Update module github.com/pion/dtls/v2 to v2.2.12 Generated by renovateBot --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 71bf5d2170b..78f23a03edf 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.19 require ( github.com/pion/datachannel v1.5.8 - github.com/pion/dtls/v2 v2.2.11 + github.com/pion/dtls/v2 v2.2.12 github.com/pion/ice/v3 v3.0.10 github.com/pion/interceptor v0.1.29 github.com/pion/logging v0.2.2 diff --git a/go.sum b/go.sum index 617d2843ef3..cd49bd111d8 100644 --- a/go.sum +++ b/go.sum @@ -38,8 +38,8 @@ github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAl github.com/pion/datachannel v1.5.8 h1:ph1P1NsGkazkjrvyMfhRBUAWMxugJjq2HfQifaOoSNo= github.com/pion/datachannel v1.5.8/go.mod h1:PgmdpoaNBLX9HNzNClmdki4DYW5JtI7Yibu8QzbL3tI= github.com/pion/dtls/v2 v2.2.7/go.mod h1:8WiMkebSHFD0T+dIU+UeBaoV7kDhOW5oDCzZ7WZ/F9s= -github.com/pion/dtls/v2 v2.2.11 h1:9U/dpCYl1ySttROPWJgqWKEylUdT0fXp/xst6JwY5Ks= -github.com/pion/dtls/v2 v2.2.11/go.mod h1:d9SYc9fch0CqK90mRk1dC7AkzzpwJj6u2GU3u+9pqFE= +github.com/pion/dtls/v2 v2.2.12 h1:KP7H5/c1EiVAAKUmXyCzPiQe5+bCJrpOeKg/L05dunk= +github.com/pion/dtls/v2 v2.2.12/go.mod h1:d9SYc9fch0CqK90mRk1dC7AkzzpwJj6u2GU3u+9pqFE= github.com/pion/ice/v3 v3.0.10 h1:ZRNZkZW4C/CFniAfEH7Ix4hW+TVGENsNkzebYY72TYY= github.com/pion/ice/v3 v3.0.10/go.mod h1:aSHw7r4guKNumxc5dCWfzWga+qtz1ki/nnpHfJTSLTs= github.com/pion/interceptor v0.1.29 h1:39fsnlP1U8gw2JzOFWdfCU82vHvhW9o0rZnZF56wF+M= From 19d022423d7975b7dafce874c0e2c7bc957ee066 Mon Sep 17 00:00:00 2001 From: sirzooro Date: Sat, 20 Jul 2024 23:33:03 +0200 Subject: [PATCH 38/39] Added support for SRTP_NULL_HMAC_SHA1_80 cipher Added support for SRTP_NULL_HMAC_SHA1_80 protection profile (cipher). It is disabled by default. You need to use SettingEngine and set list of allowed SRTP protection profiles using its SetSRTPProtectionProfiles function called with dtls.SRTP_NULL_HMAC_SHA1_80 as a parameter. You need to do this for both pion peers. For non-pion ones you may need to enable it somewhere too, as NULL cipher is usually disabled for security reasons. --- dtlstransport.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dtlstransport.go b/dtlstransport.go index 410d1645831..ec08a0846d3 100644 --- a/dtlstransport.go +++ b/dtlstransport.go @@ -377,6 +377,8 @@ func (t *DTLSTransport) Start(remoteParameters DTLSParameters) error { t.srtpProtectionProfile = srtp.ProtectionProfileAeadAes256Gcm case dtls.SRTP_AES128_CM_HMAC_SHA1_80: t.srtpProtectionProfile = srtp.ProtectionProfileAes128CmHmacSha1_80 + case dtls.SRTP_NULL_HMAC_SHA1_80: + t.srtpProtectionProfile = srtp.ProtectionProfileNullHmacSha1_80 default: t.onStateChange(DTLSTransportStateFailed) return ErrNoSRTPProtectionProfile From c85269bee372fbb75a4ffad53613e92dcd14ce48 Mon Sep 17 00:00:00 2001 From: Sean DuBois Date: Fri, 19 Jul 2024 14:56:52 -0400 Subject: [PATCH 39/39] Properly handle non-media probes libwebrtc has started sending media probes on an unannounced SSRC(0). Currently Pion will ignore this as the SSRC hasn't been declared explicitly and no RID/MID RTP Headers. This adds a special case to accept SSRC 0 and Read the RTP packets. This allows the TWCC reports to properly be generated. --- interceptor_test.go | 55 ++++++++++++++++++++++++++-- peerconnection.go | 37 ++++++++++++++++++- peerconnection_media_test.go | 6 +-- peerconnection_renegotiation_test.go | 2 +- rtpreceiver.go | 12 +++--- 5 files changed, 96 insertions(+), 16 deletions(-) diff --git a/interceptor_test.go b/interceptor_test.go index aad610bb35d..2670627df74 100644 --- a/interceptor_test.go +++ b/interceptor_test.go @@ -225,12 +225,59 @@ func Test_InterceptorRegistry_Build(t *testing.T) { }, }) - peerConnectionA, err := NewAPI(WithInterceptorRegistry(ir)).NewPeerConnection(Configuration{}) - assert.NoError(t, err) - - peerConnectionB, err := NewAPI(WithInterceptorRegistry(ir)).NewPeerConnection(Configuration{}) + peerConnectionA, peerConnectionB, err := NewAPI(WithInterceptorRegistry(ir)).newPair(Configuration{}) assert.NoError(t, err) assert.Equal(t, 2, registryBuildCount) closePairNow(t, peerConnectionA, peerConnectionB) } + +func Test_Interceptor_ZeroSSRC(t *testing.T) { + to := test.TimeOut(time.Second * 20) + defer to.Stop() + + report := test.CheckRoutines(t) + defer report() + + track, err := NewTrackLocalStaticRTP(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion") + assert.NoError(t, err) + + offerer, answerer, err := newPair() + assert.NoError(t, err) + + _, err = offerer.AddTrack(track) + assert.NoError(t, err) + + probeReceiverCreated := make(chan struct{}) + + go func() { + sequenceNumber := uint16(0) + for range time.NewTicker(time.Millisecond * 20).C { + track.mu.Lock() + if len(track.bindings) == 1 { + _, err = track.bindings[0].writeStream.WriteRTP(&rtp.Header{ + Version: 2, + SSRC: 0, + SequenceNumber: sequenceNumber, + }, []byte{0, 1, 2, 3, 4, 5}) + assert.NoError(t, err) + } + sequenceNumber++ + track.mu.Unlock() + + if nonMediaBandwidthProbe, ok := answerer.nonMediaBandwidthProbe.Load().(*RTPReceiver); ok { + assert.Equal(t, len(nonMediaBandwidthProbe.Tracks()), 1) + close(probeReceiverCreated) + return + } + } + }() + + assert.NoError(t, signalPair(offerer, answerer)) + + peerConnectionConnected := untilConnectionState(PeerConnectionStateConnected, offerer, answerer) + peerConnectionConnected.Wait() + + <-probeReceiverCreated + closePairNow(t, offerer, answerer) +} diff --git a/peerconnection.go b/peerconnection.go index 67af02bc639..5876e5f76cd 100644 --- a/peerconnection.go +++ b/peerconnection.go @@ -68,7 +68,8 @@ type PeerConnection struct { // should be defined (see JSEP 3.4.1). greaterMid int - rtpTransceivers []*RTPTransceiver + rtpTransceivers []*RTPTransceiver + nonMediaBandwidthProbe atomic.Value // RTPReceiver onSignalingStateChangeHandler func(SignalingState) onICEConnectionStateChangeHandler atomic.Value // func(ICEConnectionState) @@ -1524,6 +1525,32 @@ func (pc *PeerConnection) handleUndeclaredSSRC(ssrc SSRC, remoteDescription *Ses return true, nil } +// Chrome sends probing traffic on SSRC 0. This reads the packets to ensure that we properly +// generate TWCC reports for it. Since this isn't actually media we don't pass this to the user +func (pc *PeerConnection) handleNonMediaBandwidthProbe() { + nonMediaBandwidthProbe, err := pc.api.NewRTPReceiver(RTPCodecTypeVideo, pc.dtlsTransport) + if err != nil { + pc.log.Errorf("handleNonMediaBandwidthProbe failed to create RTPReceiver: %v", err) + return + } + + if err = nonMediaBandwidthProbe.Receive(RTPReceiveParameters{ + Encodings: []RTPDecodingParameters{{RTPCodingParameters: RTPCodingParameters{}}}, + }); err != nil { + pc.log.Errorf("handleNonMediaBandwidthProbe failed to start RTPReceiver: %v", err) + return + } + + pc.nonMediaBandwidthProbe.Store(nonMediaBandwidthProbe) + b := make([]byte, pc.api.settingEngine.getReceiveMTU()) + for { + if _, _, err = nonMediaBandwidthProbe.readRTP(b, nonMediaBandwidthProbe.Track()); err != nil { + pc.log.Tracef("handleNonMediaBandwidthProbe read exiting: %v", err) + return + } + } +} + func (pc *PeerConnection) handleIncomingSSRC(rtpStream io.Reader, ssrc SSRC) error { //nolint:gocognit remoteDescription := pc.RemoteDescription() if remoteDescription == nil { @@ -1656,6 +1683,11 @@ func (pc *PeerConnection) undeclaredRTPMediaProcessor() { continue } + if ssrc == 0 { + go pc.handleNonMediaBandwidthProbe() + continue + } + pc.dtlsTransport.storeSimulcastStream(stream) if atomic.AddUint64(&simulcastRoutineCount, 1) >= simulcastMaxProbeRoutines { @@ -2072,6 +2104,9 @@ func (pc *PeerConnection) Close() error { closeErrs = append(closeErrs, t.Stop()) } } + if nonMediaBandwidthProbe, ok := pc.nonMediaBandwidthProbe.Load().(*RTPReceiver); ok { + closeErrs = append(closeErrs, nonMediaBandwidthProbe.Stop()) + } pc.mu.Unlock() // https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-close (step #5) diff --git a/peerconnection_media_test.go b/peerconnection_media_test.go index 0e29d90f400..121b41c68a1 100644 --- a/peerconnection_media_test.go +++ b/peerconnection_media_test.go @@ -1528,7 +1528,7 @@ func TestPeerConnection_Simulcast_RTX(t *testing.T) { SequenceNumber: sequenceNumber, PayloadType: 96, Padding: true, - SSRC: uint32(i), + SSRC: uint32(i + 1), }, Payload: []byte{0x00, 0x02}, } @@ -1547,7 +1547,7 @@ func TestPeerConnection_Simulcast_RTX(t *testing.T) { Version: 2, SequenceNumber: sequenceNumber, PayloadType: 96, - SSRC: uint32(i), + SSRC: uint32(i + 1), }, Payload: []byte{0x00}, } @@ -1591,7 +1591,7 @@ func TestPeerConnection_Simulcast_RTX(t *testing.T) { Version: 2, SequenceNumber: sequenceNumber, PayloadType: 96, - SSRC: uint32(i), + SSRC: uint32(i + 1), }, Payload: []byte{0x00}, } diff --git a/peerconnection_renegotiation_test.go b/peerconnection_renegotiation_test.go index 33bc714f774..6f4465475f2 100644 --- a/peerconnection_renegotiation_test.go +++ b/peerconnection_renegotiation_test.go @@ -1046,7 +1046,7 @@ func TestPeerConnection_Renegotiation_Simulcast(t *testing.T) { for ssrc, rid := range rids { header := &rtp.Header{ Version: 2, - SSRC: uint32(ssrc), + SSRC: uint32(ssrc + 1), SequenceNumber: sequenceNumber, PayloadType: 96, } diff --git a/rtpreceiver.go b/rtpreceiver.go index 3dd4d83b1aa..abd2dff969b 100644 --- a/rtpreceiver.go +++ b/rtpreceiver.go @@ -201,7 +201,7 @@ func (r *RTPReceiver) startReceive(parameters RTPReceiveParameters) error { var t *trackStreams for idx, ts := range r.tracks { - if ts.track != nil && parameters.Encodings[i].SSRC != 0 && ts.track.SSRC() == parameters.Encodings[i].SSRC { + if ts.track != nil && ts.track.SSRC() == parameters.Encodings[i].SSRC { t = &r.tracks[idx] break } @@ -210,12 +210,10 @@ func (r *RTPReceiver) startReceive(parameters RTPReceiveParameters) error { return fmt.Errorf("%w: %d", errRTPReceiverWithSSRCTrackStreamNotFound, parameters.Encodings[i].SSRC) } - if parameters.Encodings[i].SSRC != 0 { - t.streamInfo = createStreamInfo("", parameters.Encodings[i].SSRC, 0, codec, globalParams.HeaderExtensions) - var err error - if t.rtpReadStream, t.rtpInterceptor, t.rtcpReadStream, t.rtcpInterceptor, err = r.transport.streamsForSSRC(parameters.Encodings[i].SSRC, *t.streamInfo); err != nil { - return err - } + t.streamInfo = createStreamInfo("", parameters.Encodings[i].SSRC, 0, codec, globalParams.HeaderExtensions) + var err error + if t.rtpReadStream, t.rtpInterceptor, t.rtcpReadStream, t.rtcpInterceptor, err = r.transport.streamsForSSRC(parameters.Encodings[i].SSRC, *t.streamInfo); err != nil { + return err } if rtxSsrc := parameters.Encodings[i].RTX.SSRC; rtxSsrc != 0 {