]> Sergey Matveev's repositories - btrtrc.git/blob - webtorrent/transport.go
Import code https://github.com/backkem/gowttest
[btrtrc.git] / webtorrent / transport.go
1 package main
2
3 import (
4         "fmt"
5         "log"
6         "sync"
7
8         "github.com/pion/datachannel"
9
10         "github.com/pion/webrtc/v2"
11 )
12
13 type Transport struct {
14         pc *webrtc.PeerConnection
15         dc *webrtc.DataChannel
16
17         lock *sync.Mutex
18 }
19
20 // NewTransport creates a transport and returns a WebRTC offer
21 // to be announced
22 func NewTransport() (*Transport, webrtc.SessionDescription, error) {
23         // Enable the detach API (since it's non-standard but more idiomatic)
24         // (This should be done once globally)
25         s := webrtc.SettingEngine{}
26         s.DetachDataChannels()
27         api := webrtc.NewAPI(webrtc.WithSettingEngine(s))
28
29         config := webrtc.Configuration{ICEServers: []webrtc.ICEServer{{URLs: []string{"stun:stun.l.google.com:19302"}}}}
30         peerConnection, err := api.NewPeerConnection(config)
31         if err != nil {
32                 return nil, webrtc.SessionDescription{}, fmt.Errorf("failed to peer connection: %v\n", err)
33         }
34         dataChannel, err := peerConnection.CreateDataChannel("webrtc-datachannel", nil)
35         if err != nil {
36                 return nil, webrtc.SessionDescription{}, fmt.Errorf("failed to data channel: %v\n", err)
37         }
38         peerConnection.OnICEConnectionStateChange(func(connectionState webrtc.ICEConnectionState) {
39                 fmt.Printf("ICE Connection State has changed: %s\n", connectionState.String())
40         })
41
42         dataChannel.OnMessage(func(msg webrtc.DataChannelMessage) {
43                 fmt.Printf("Message from DataChannel '%s': '%s'\n", dataChannel.Label(), string(msg.Data))
44         })
45         offer, err := peerConnection.CreateOffer(nil)
46         if err != nil {
47                 return nil, webrtc.SessionDescription{}, fmt.Errorf("failed to create offer: %v\n", err)
48         }
49         err = peerConnection.SetLocalDescription(offer)
50         if err != nil {
51                 return nil, webrtc.SessionDescription{}, fmt.Errorf("failed to set local description: %v\n", err)
52         }
53
54         t := &Transport{pc: peerConnection, dc: dataChannel, lock: &sync.Mutex{}}
55         return t, offer, nil
56 }
57
58 // NewTransportFromOffer creates a transport from a WebRTC offer and
59 // and returns a WebRTC answer to be announced
60 func NewTransportFromOffer(offer webrtc.SessionDescription, onOpen func(datachannel.ReadWriteCloser)) (*Transport, webrtc.SessionDescription, error) {
61         // Enable the detach API (since it's non-standard but more idiomatic)
62         // (This should be done once globally)
63         s := webrtc.SettingEngine{}
64         s.DetachDataChannels()
65         api := webrtc.NewAPI(webrtc.WithSettingEngine(s))
66
67         config := webrtc.Configuration{ICEServers: []webrtc.ICEServer{{URLs: []string{"stun:stun.l.google.com:19302"}}}}
68         peerConnection, err := api.NewPeerConnection(config)
69         if err != nil {
70                 return nil, webrtc.SessionDescription{}, fmt.Errorf("failed to peer connection: %v", err)
71         }
72         peerConnection.OnICEConnectionStateChange(func(connectionState webrtc.ICEConnectionState) {
73                 fmt.Printf("ICE Connection State has changed: %s\n", connectionState.String())
74         })
75
76         t := &Transport{pc: peerConnection, lock: &sync.Mutex{}}
77         peerConnection.OnDataChannel(func(d *webrtc.DataChannel) {
78                 fmt.Printf("New DataChannel %s %d\n", d.Label(), d.ID())
79                 t.lock.Lock()
80                 t.dc = d
81                 t.lock.Unlock()
82                 t.handleOpen(onOpen)
83         })
84
85         err = peerConnection.SetRemoteDescription(offer)
86         if err != nil {
87                 return nil, webrtc.SessionDescription{}, fmt.Errorf("%v", err)
88         }
89         answer, err := peerConnection.CreateAnswer(nil)
90         if err != nil {
91                 return nil, webrtc.SessionDescription{}, fmt.Errorf("%v", err)
92         }
93         err = peerConnection.SetLocalDescription(answer)
94         if err != nil {
95                 return nil, webrtc.SessionDescription{}, fmt.Errorf("%v", err)
96         }
97
98         return t, answer, nil
99 }
100
101 // SetAnswer sets the WebRTC answer
102 func (t *Transport) SetAnswer(answer webrtc.SessionDescription, onOpen func(datachannel.ReadWriteCloser)) error {
103         t.handleOpen(onOpen)
104
105         err := t.pc.SetRemoteDescription(answer)
106         if err != nil {
107                 return err
108         }
109         return nil
110 }
111
112 func (t *Transport) handleOpen(onOpen func(datachannel.ReadWriteCloser)) {
113         t.lock.Lock()
114         dc := t.dc
115         t.lock.Unlock()
116         dc.OnOpen(func() {
117                 fmt.Printf("Data channel '%s'-'%d' open.\n", dc.Label(), dc.ID())
118
119                 // Detach the data channel
120                 raw, err := dc.Detach()
121                 if err != nil {
122                         log.Fatalf("failed to detach: %v", err) // TODO: Error handling
123                 }
124
125                 onOpen(raw)
126         })
127 }