9 "github.com/anacrolix/missinggo/v2/pproffd"
10 "github.com/pion/datachannel"
12 "github.com/pion/webrtc/v2"
16 metrics = expvar.NewMap("webtorrent")
17 api = func() *webrtc.API {
18 // Enable the detach API (since it's non-standard but more idiomatic).
19 s := webrtc.SettingEngine{}
20 s.DetachDataChannels()
21 return webrtc.NewAPI(webrtc.WithSettingEngine(s))
23 config = webrtc.Configuration{ICEServers: []webrtc.ICEServer{{URLs: []string{"stun:stun.l.google.com:19302"}}}}
24 newPeerConnectionMu sync.Mutex
27 type wrappedPeerConnection struct {
28 *webrtc.PeerConnection
32 func (me wrappedPeerConnection) Close() error {
33 return me.CloseWrapper.Close()
36 func newPeerConnection() (wrappedPeerConnection, error) {
37 newPeerConnectionMu.Lock()
38 defer newPeerConnectionMu.Unlock()
39 pc, err := api.NewPeerConnection(config)
41 return wrappedPeerConnection{}, err
43 return wrappedPeerConnection{
45 pproffd.NewCloseWrapper(pc),
49 // newOffer creates a transport and returns a WebRTC offer to be announced
51 peerConnection wrappedPeerConnection,
52 dataChannel *webrtc.DataChannel,
53 offer webrtc.SessionDescription,
56 peerConnection, err = newPeerConnection()
60 dataChannel, err = peerConnection.CreateDataChannel("webrtc-datachannel", nil)
62 peerConnection.Close()
65 offer, err = peerConnection.CreateOffer(nil)
67 peerConnection.Close()
70 err = peerConnection.SetLocalDescription(offer)
72 peerConnection.Close()
78 func initAnsweringPeerConnection(
79 peerConnection wrappedPeerConnection,
80 offer webrtc.SessionDescription,
81 ) (answer webrtc.SessionDescription, err error) {
82 err = peerConnection.SetRemoteDescription(offer)
86 answer, err = peerConnection.CreateAnswer(nil)
90 err = peerConnection.SetLocalDescription(answer)
97 // newAnsweringPeerConnection creates a transport from a WebRTC offer and and returns a WebRTC answer to be
99 func newAnsweringPeerConnection(offer webrtc.SessionDescription) (
100 peerConn wrappedPeerConnection, answer webrtc.SessionDescription, err error,
102 peerConn, err = newPeerConnection()
104 err = fmt.Errorf("failed to create new connection: %w", err)
107 answer, err = initAnsweringPeerConnection(peerConn, offer)
114 func (t *outboundOffer) setAnswer(answer webrtc.SessionDescription, onOpen func(datachannel.ReadWriteCloser)) error {
115 setDataChannelOnOpen(t.dataChannel, t.peerConnection, onOpen)
116 err := t.peerConnection.SetRemoteDescription(answer)
120 type datachannelReadWriter interface {
127 type ioCloserFunc func() error
129 func (me ioCloserFunc) Close() error {
133 func setDataChannelOnOpen(
134 dc *webrtc.DataChannel,
135 pc wrappedPeerConnection,
136 onOpen func(closer datachannel.ReadWriteCloser),
139 raw, err := dc.Detach()
141 // This shouldn't happen if the API is configured correctly, and we call from OnOpen.
144 onOpen(hookDataChannelCloser(raw, pc))
148 func hookDataChannelCloser(dcrwc datachannel.ReadWriteCloser, pc wrappedPeerConnection) datachannel.ReadWriteCloser {
150 datachannelReadWriter
154 ioCloserFunc(pc.Close),