8 "github.com/pion/datachannel"
10 "github.com/pion/webrtc/v2"
14 api = func() *webrtc.API {
15 // Enable the detach API (since it's non-standard but more idiomatic)
16 // (This should be done once globally)
17 s := webrtc.SettingEngine{}
18 s.DetachDataChannels()
19 return webrtc.NewAPI(webrtc.WithSettingEngine(s))
21 config = webrtc.Configuration{ICEServers: []webrtc.ICEServer{{URLs: []string{"stun:stun.l.google.com:19302"}}}}
22 newPeerConnectionMu sync.Mutex
25 func newPeerConnection() (*webrtc.PeerConnection, error) {
26 newPeerConnectionMu.Lock()
27 defer newPeerConnectionMu.Unlock()
28 return api.NewPeerConnection(config)
31 type Transport struct {
32 pc *webrtc.PeerConnection
33 dc *webrtc.DataChannel
38 // NewTransport creates a transport and returns a WebRTC offer to be announced
39 func NewTransport() (*Transport, webrtc.SessionDescription, error) {
40 peerConnection, err := newPeerConnection()
42 return nil, webrtc.SessionDescription{}, fmt.Errorf("failed to peer connection: %v\n", err)
44 dataChannel, err := peerConnection.CreateDataChannel("webrtc-datachannel", nil)
46 return nil, webrtc.SessionDescription{}, fmt.Errorf("failed to data channel: %v\n", err)
48 peerConnection.OnICEConnectionStateChange(func(connectionState webrtc.ICEConnectionState) {
49 fmt.Printf("ICE Connection State has changed: %s\n", connectionState.String())
52 dataChannel.OnMessage(func(msg webrtc.DataChannelMessage) {
53 fmt.Printf("Message from DataChannel '%s': '%s'\n", dataChannel.Label(), string(msg.Data))
55 offer, err := peerConnection.CreateOffer(nil)
57 return nil, webrtc.SessionDescription{}, fmt.Errorf("failed to create offer: %v\n", err)
59 err = peerConnection.SetLocalDescription(offer)
61 return nil, webrtc.SessionDescription{}, fmt.Errorf("failed to set local description: %v\n", err)
64 t := &Transport{pc: peerConnection, dc: dataChannel}
68 // NewTransportFromOffer creates a transport from a WebRTC offer and and returns a WebRTC answer to
70 func NewTransportFromOffer(offer webrtc.SessionDescription, onOpen onDataChannelOpen) (*Transport, webrtc.SessionDescription, error) {
71 peerConnection, err := newPeerConnection()
73 return nil, webrtc.SessionDescription{}, fmt.Errorf("failed to peer connection: %v", err)
75 peerConnection.OnICEConnectionStateChange(func(connectionState webrtc.ICEConnectionState) {
76 fmt.Printf("ICE Connection State has changed: %s\n", connectionState.String())
79 t := &Transport{pc: peerConnection}
81 err = peerConnection.SetRemoteDescription(offer)
83 return nil, webrtc.SessionDescription{}, fmt.Errorf("%v", err)
85 answer, err := peerConnection.CreateAnswer(nil)
87 return nil, webrtc.SessionDescription{}, fmt.Errorf("%v", err)
89 peerConnection.OnDataChannel(func(d *webrtc.DataChannel) {
90 fmt.Printf("New DataChannel %s %d\n", d.Label(), d.ID())
94 t.handleOpen(func(dc datachannel.ReadWriteCloser) {
95 onOpen(dc, DataChannelContext{answer, offer, false})
98 err = peerConnection.SetLocalDescription(answer)
100 return nil, webrtc.SessionDescription{}, fmt.Errorf("%v", err)
103 return t, answer, nil
106 // SetAnswer sets the WebRTC answer
107 func (t *Transport) SetAnswer(answer webrtc.SessionDescription, onOpen func(datachannel.ReadWriteCloser)) error {
110 err := t.pc.SetRemoteDescription(answer)
117 func (t *Transport) handleOpen(onOpen func(datachannel.ReadWriteCloser)) {
122 fmt.Printf("Data channel '%s'-'%d' open.\n", dc.Label(), dc.ID())
124 // Detach the data channel
125 raw, err := dc.Detach()
127 log.Fatalf("failed to detach: %v", err) // TODO: Error handling