Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion examples/warp/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"fmt"
"net/http"

"github.com/pion/webrtc/v4"

Check failure on line 15 in examples/warp/main.go

View workflow job for this annotation

GitHub Actions / lint / Go

could not import github.com/pion/webrtc/v4 (-: # github.com/pion/webrtc/v4
)

func main() {
Expand All @@ -36,7 +36,7 @@
}
}

func setupOfferHandler(pc **webrtc.PeerConnection) {

Check failure on line 39 in examples/warp/main.go

View workflow job for this annotation

GitHub Actions / lint / Go

undefined: webrtc (typecheck)
http.HandleFunc("/offer", func(responseWriter http.ResponseWriter, r *http.Request) {
var offer webrtc.SessionDescription
if err := json.NewDecoder(r.Body).Decode(&offer); err != nil {
Expand All @@ -45,8 +45,13 @@
return
}

// Enable SNAP.
s := webrtc.SettingEngine{}
s.EnableSctpSnap(true)
api := webrtc.NewAPI(webrtc.WithSettingEngine(s))

var err error
*pc, err = webrtc.NewPeerConnection(webrtc.Configuration{
*pc, err = api.NewPeerConnection(webrtc.Configuration{
BundlePolicy: webrtc.BundlePolicyMaxBundle,
})
if err != nil {
Expand All @@ -66,7 +71,7 @@
})
}

func setupICECandidateHandler(pc *webrtc.PeerConnection) {

Check failure on line 74 in examples/warp/main.go

View workflow job for this annotation

GitHub Actions / lint / Go

undefined: webrtc (typecheck)
pc.OnICECandidate(func(c *webrtc.ICECandidate) {
if c != nil {
fmt.Printf("🌐 New ICE candidate: %s\n", c.Address)
Expand Down
27 changes: 21 additions & 6 deletions peerconnection.go
Original file line number Diff line number Diff line change
Expand Up @@ -1615,10 +1615,11 @@ func (pc *PeerConnection) startRTPSenders(currentTransceivers []*RTPTransceiver)
}

// Start SCTP subsystem.
func (pc *PeerConnection) startSCTP(maxMessageSize uint32) {
func (pc *PeerConnection) startSCTP(maxMessageSize uint32, remoteSctpInit []byte) {
// Start sctp
if err := pc.sctpTransport.Start(SCTPCapabilities{
MaxMessageSize: maxMessageSize,
SctpInit: remoteSctpInit,
}); err != nil {
pc.log.Warnf("Failed to start SCTP: %s", err)
if err = pc.sctpTransport.Stop(); err != nil {
Expand Down Expand Up @@ -2791,7 +2792,8 @@ func (pc *PeerConnection) startRTP(

pc.startRTPReceivers(remoteDesc, currentTransceivers)
if d := haveDataChannel(remoteDesc); d != nil {
pc.startSCTP(getMaxMessageSize(d))
remoteSctpInit, _ := getSctpInit(d)
pc.startSCTP(getMaxMessageSize(d), remoteSctpInit)
}
}

Expand Down Expand Up @@ -2824,6 +2826,11 @@ func (pc *PeerConnection) generateUnmatchedSDP(

// Needed for pc.sctpTransport.dataChannelsRequested
pc.sctpTransport.lock.Lock()

var localSctpInit []byte
if pc.sctpTransport.dataChannelsRequested != 0 && pc.api.settingEngine.sctp.enableSnap {
localSctpInit = pc.sctpTransport.GetSctpInit()
}
defer pc.sctpTransport.lock.Unlock()

if isPlanB { //nolint:nestif
Expand Down Expand Up @@ -2860,7 +2867,7 @@ func (pc *PeerConnection) generateUnmatchedSDP(
}

if pc.sctpTransport.dataChannelsRequested != 0 {
mediaSections = append(mediaSections, mediaSection{id: strconv.Itoa(len(mediaSections)), data: true})
mediaSections = append(mediaSections, mediaSection{id: strconv.Itoa(len(mediaSections)), data: true, sctpInit: localSctpInit})
}
}

Expand Down Expand Up @@ -2889,7 +2896,7 @@ func (pc *PeerConnection) generateUnmatchedSDP(
}

// generateMatchedSDP generates a SDP and takes the remote state into account
// this is used everytime we have a RemoteDescription
// This is used everytime we have a RemoteDescription
//
//nolint:gocognit,gocyclo,cyclop
func (pc *PeerConnection) generateMatchedSDP(
Expand Down Expand Up @@ -2929,14 +2936,22 @@ func (pc *PeerConnection) generateMatchedSDP(

mediaSections := []mediaSection{}
alreadyHaveApplicationMediaSection := false
var localSctpInit []byte
for _, media := range remoteDescription.parsed.MediaDescriptions {
midValue := getMidValue(media)
if midValue == "" {
return nil, errPeerConnRemoteDescriptionWithoutMidValue
}

if media.MediaName.Media == mediaSectionApplication {
mediaSections = append(mediaSections, mediaSection{id: midValue, data: true})
init, _ := getSctpInit(media)
if init != nil && pc.api.settingEngine.sctp.enableSnap {
pc.sctpTransport.lock.Lock()
localSctpInit = pc.sctpTransport.GetSctpInit()
pc.sctpTransport.lock.Unlock()
}

mediaSections = append(mediaSections, mediaSection{id: midValue, data: true, sctpInit: localSctpInit})
alreadyHaveApplicationMediaSection = true

continue
Expand Down Expand Up @@ -3023,7 +3038,7 @@ func (pc *PeerConnection) generateMatchedSDP(
if detectedPlanB {
mediaSections = append(mediaSections, mediaSection{id: "data", data: true})
} else {
mediaSections = append(mediaSections, mediaSection{id: strconv.Itoa(len(mediaSections)), data: true})
mediaSections = append(mediaSections, mediaSection{id: strconv.Itoa(len(mediaSections)), data: true, sctpInit: localSctpInit})
}
}
} else if remoteDescription != nil {
Expand Down
17 changes: 17 additions & 0 deletions peerconnection_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -807,6 +807,23 @@
closePairNow(t, pcOffer, pcAnswer)
}

func TestSctpSnap(t *testing.T) {
s := SettingEngine{}
s.EnableSnap(true)

Check failure on line 812 in peerconnection_test.go

View workflow job for this annotation

GitHub Actions / test (1.24) / Go 1.24

s.EnableSnap undefined (type SettingEngine has no field or method EnableSnap)

Check failure on line 812 in peerconnection_test.go

View workflow job for this annotation

GitHub Actions / test (1.25) / Go 1.25

s.EnableSnap undefined (type SettingEngine has no field or method EnableSnap)
api := NewAPI(WithSettingEngine(s))

offer, err := api.NewPeerConnection(Configuration{})
assert.NoError(t, err)
answer, err := api.NewPeerConnection(Configuration{})
assert.NoError(t, err)

peerConnectionsConnected := untilConnectionState(PeerConnectionStateConnected, offer, answer)
assert.NoError(t, signalPair(offer, answer))
peerConnectionsConnected.Wait()

closePairNow(t, offer, answer)
}

func TestICETrickleCapabilityString(t *testing.T) {
tests := []struct {
value ICETrickleCapability
Expand Down
1 change: 1 addition & 0 deletions sctpcapabilities.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ package webrtc
// SCTPCapabilities indicates the capabilities of the SCTPTransport.
type SCTPCapabilities struct {
MaxMessageSize uint32 `json:"maxMessageSize"`
SctpInit []byte `json:"sctpInit"`
}
20 changes: 19 additions & 1 deletion sctptransport.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@
dataChannelsRequested uint32
dataChannelsAccepted uint32

localSctpInit []byte

api *API
log logging.LeveledLogger
}
Expand Down Expand Up @@ -107,6 +109,7 @@
if maxMessageSize == 0 {
maxMessageSize = sctpMaxMessageSizeUnsetValue
}
remoteSctpInit := capabilities.SctpInit

dtlsTransport := r.Transport()
if dtlsTransport == nil || dtlsTransport.conn == nil {
Expand All @@ -119,11 +122,14 @@
LoggerFactory: r.api.settingEngine.LoggerFactory,
RTOMax: float64(r.api.settingEngine.sctp.rtoMax) / float64(time.Millisecond),
BlockWrite: r.api.settingEngine.detach.DataChannels && r.api.settingEngine.dataChannelBlockWrite,
MaxMessageSize: maxMessageSize,
MTU: outboundMTU,
MinCwnd: r.api.settingEngine.sctp.minCwnd,
FastRtxWnd: r.api.settingEngine.sctp.fastRtxWnd,
CwndCAStep: r.api.settingEngine.sctp.cwndCAStep,
}, sctp.SctpParameters{

Check failure on line 129 in sctptransport.go

View workflow job for this annotation

GitHub Actions / test-macos (1.24) / Go macOS 1.24

undefined: sctp.SctpParameters

Check failure on line 129 in sctptransport.go

View workflow job for this annotation

GitHub Actions / test-macos (1.24) / Go macOS 1.24

too many arguments in call to sctp.Client

Check failure on line 129 in sctptransport.go

View workflow job for this annotation

GitHub Actions / test (1.24) / Go 1.24

undefined: sctp.SctpParameters

Check failure on line 129 in sctptransport.go

View workflow job for this annotation

GitHub Actions / test (1.24) / Go 1.24

too many arguments in call to sctp.Client

Check failure on line 129 in sctptransport.go

View workflow job for this annotation

GitHub Actions / test (1.24) / Go 1.24

undefined: sctp.SctpParameters

Check failure on line 129 in sctptransport.go

View workflow job for this annotation

GitHub Actions / test (1.24) / Go 1.24

too many arguments in call to sctp.Client

Check failure on line 129 in sctptransport.go

View workflow job for this annotation

GitHub Actions / lint / Go

undefined: sctp.SctpParameters

Check failure on line 129 in sctptransport.go

View workflow job for this annotation

GitHub Actions / lint / Go

too many arguments in call to sctp.Client

Check failure on line 129 in sctptransport.go

View workflow job for this annotation

GitHub Actions / lint / Go

undefined: sctp.SctpParameters

Check failure on line 129 in sctptransport.go

View workflow job for this annotation

GitHub Actions / lint / Go

too many arguments in call to sctp.Client

Check failure on line 129 in sctptransport.go

View workflow job for this annotation

GitHub Actions / test-macos (1.25) / Go macOS 1.25

undefined: sctp.SctpParameters

Check failure on line 129 in sctptransport.go

View workflow job for this annotation

GitHub Actions / test-macos (1.25) / Go macOS 1.25

too many arguments in call to sctp.Client

Check failure on line 129 in sctptransport.go

View workflow job for this annotation

GitHub Actions / test (1.25) / Go 1.25

undefined: sctp.SctpParameters

Check failure on line 129 in sctptransport.go

View workflow job for this annotation

GitHub Actions / test (1.25) / Go 1.25

too many arguments in call to sctp.Client

Check failure on line 129 in sctptransport.go

View workflow job for this annotation

GitHub Actions / test (1.25) / Go 1.25

undefined: sctp.SctpParameters

Check failure on line 129 in sctptransport.go

View workflow job for this annotation

GitHub Actions / test (1.25) / Go 1.25

too many arguments in call to sctp.Client
MaxMessageSize: maxMessageSize,
LocalSctpInit: r.localSctpInit,
RemoteSctpInit: remoteSctpInit,
})
if err != nil {
return err
Expand Down Expand Up @@ -456,3 +462,15 @@

return r.sctpAssociation.BufferedAmount()
}

// The caller should hold the lock.
func (r *SCTPTransport) GetSctpInit() []byte {
if len(r.localSctpInit) == 0 {
r.localSctpInit, _ = sctp.GenerateOutOfBandToken(sctp.Config{

Check failure on line 469 in sctptransport.go

View workflow job for this annotation

GitHub Actions / test-macos (1.24) / Go macOS 1.24

undefined: sctp.GenerateOutOfBandToken

Check failure on line 469 in sctptransport.go

View workflow job for this annotation

GitHub Actions / test (1.24) / Go 1.24

undefined: sctp.GenerateOutOfBandToken

Check failure on line 469 in sctptransport.go

View workflow job for this annotation

GitHub Actions / test (1.24) / Go 1.24

undefined: sctp.GenerateOutOfBandToken

Check failure on line 469 in sctptransport.go

View workflow job for this annotation

GitHub Actions / lint / Go

undefined: sctp.GenerateOutOfBandToken) (typecheck)

Check failure on line 469 in sctptransport.go

View workflow job for this annotation

GitHub Actions / lint / Go

undefined: sctp.GenerateOutOfBandToken) (typecheck)

Check failure on line 469 in sctptransport.go

View workflow job for this annotation

GitHub Actions / test-macos (1.25) / Go macOS 1.25

undefined: sctp.GenerateOutOfBandToken

Check failure on line 469 in sctptransport.go

View workflow job for this annotation

GitHub Actions / test (1.25) / Go 1.25

undefined: sctp.GenerateOutOfBandToken

Check failure on line 469 in sctptransport.go

View workflow job for this annotation

GitHub Actions / test (1.25) / Go 1.25

undefined: sctp.GenerateOutOfBandToken
MaxReceiveBufferSize: r.api.settingEngine.sctp.maxReceiveBufferSize,
EnableZeroChecksum: r.api.settingEngine.sctp.enableZeroChecksum,
})
}

return r.localSctpInit
}
25 changes: 25 additions & 0 deletions sdp.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
package webrtc

import (
"encoding/base64"
"errors"
"fmt"
"net/url"
Expand Down Expand Up @@ -369,6 +370,7 @@ func addDataMediaSection(
dtlsRole sdp.ConnectionRole,
iceGatheringState ICEGatheringState,
sctpMaxMessageSize uint32,
sctpInit []byte,
) error {
media := (&sdp.MediaDescription{
MediaName: sdp.MediaName{
Expand All @@ -388,10 +390,14 @@ func addDataMediaSection(
WithValueAttribute(sdp.AttrKeyConnectionSetup, dtlsRole.String()).
WithValueAttribute(sdp.AttrKeyMID, midValue).
WithPropertyAttribute(RTPTransceiverDirectionSendrecv.String()).
// TODO: do not hardcode this.
WithPropertyAttribute("sctp-port:5000").
WithValueAttribute("max-message-size", fmt.Sprintf("%d", sctpMaxMessageSize)).
WithICECredentials(iceParams.UsernameFragment, iceParams.Password)

if len(sctpInit) != 0 {
media = media.WithValueAttribute("sctp-init", base64.StdEncoding.EncodeToString(sctpInit))
}
for _, f := range dtlsFingerprints {
media = media.WithFingerprint(f.Algorithm, strings.ToUpper(f.Value))
}
Expand Down Expand Up @@ -669,6 +675,7 @@ type mediaSection struct {
id string
transceivers []*RTPTransceiver
data bool
sctpInit []byte
matchExtensions map[string]int
rids []*simulcastRid
}
Expand Down Expand Up @@ -742,6 +749,7 @@ func populateSDP(
connectionRole,
iceGatheringState,
sctpMaxMessageSize,
section.sctpInit,
); err != nil {
return nil, err
}
Expand Down Expand Up @@ -1210,3 +1218,20 @@ func getMaxMessageSize(desc *sdp.MediaDescription) uint32 {

return 0
}

func getSctpInit(desc *sdp.MediaDescription) ([]byte, error) {
var err error
var decoded []byte
for _, a := range desc.Attributes {
if strings.TrimSpace(a.Key) == "sctp-init" {
decoded, err = base64.StdEncoding.DecodeString(a.Value)
if err == nil {
return decoded, nil
}

return nil, err
}
}

return nil, nil
}
6 changes: 6 additions & 0 deletions settingengine.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ type SettingEngine struct {
minCwnd uint32
fastRtxWnd uint32
cwndCAStep uint32
enableSnap bool
}
sdpMediaLevelFingerprints bool
answeringDTLSRole DTLSRole
Expand Down Expand Up @@ -590,6 +591,11 @@ func (e *SettingEngine) EnableSCTPZeroChecksum(isEnabled bool) {
e.sctp.enableZeroChecksum = isEnabled
}

// EnableSctpSnap enables the use of the SCTP SNAP connect optimization.
func (e *SettingEngine) EnableSctpSnap(isEnabled bool) {
e.sctp.enableSnap = isEnabled
}

// SetSCTPMaxMessageSize sets the largest message we are willing to accept.
// Leave this 0 for the default max message size.
func (e *SettingEngine) SetSCTPMaxMessageSize(maxMessageSize uint32) {
Expand Down
Loading