github.com/pion/webrtc/v3@v3.2.24/examples/data-channels-detach/jsfiddle/main.go (about) 1 // SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly> 2 // SPDX-License-Identifier: MIT 3 4 //go:build js && wasm 5 // +build js,wasm 6 7 package main 8 9 import ( 10 "fmt" 11 "io" 12 "syscall/js" 13 "time" 14 15 "github.com/pion/webrtc/v3" 16 17 "github.com/pion/webrtc/v3/examples/internal/signal" 18 ) 19 20 const messageSize = 15 21 22 func main() { 23 // Since this behavior diverges from the WebRTC API it has to be 24 // enabled using a settings engine. Mixing both detached and the 25 // OnMessage DataChannel API is not supported. 26 27 // Create a SettingEngine and enable Detach 28 s := webrtc.SettingEngine{} 29 s.DetachDataChannels() 30 31 // Create an API object with the engine 32 api := webrtc.NewAPI(webrtc.WithSettingEngine(s)) 33 34 // Everything below is the Pion WebRTC API! Thanks for using it ❤️. 35 36 // Prepare the configuration 37 config := webrtc.Configuration{ 38 ICEServers: []webrtc.ICEServer{ 39 { 40 URLs: []string{"stun:stun.l.google.com:19302"}, 41 }, 42 }, 43 } 44 45 // Create a new RTCPeerConnection using the API object 46 peerConnection, err := api.NewPeerConnection(config) 47 if err != nil { 48 handleError(err) 49 } 50 51 // Create a datachannel with label 'data' 52 dataChannel, err := peerConnection.CreateDataChannel("data", nil) 53 if err != nil { 54 handleError(err) 55 } 56 57 // Set the handler for ICE connection state 58 // This will notify you when the peer has connected/disconnected 59 peerConnection.OnICEConnectionStateChange(func(connectionState webrtc.ICEConnectionState) { 60 log(fmt.Sprintf("ICE Connection State has changed: %s\n", connectionState.String())) 61 }) 62 63 // Register channel opening handling 64 dataChannel.OnOpen(func() { 65 log(fmt.Sprintf("Data channel '%s'-'%d' open.\n", dataChannel.Label(), dataChannel.ID())) 66 67 // Detach the data channel 68 raw, dErr := dataChannel.Detach() 69 if dErr != nil { 70 handleError(dErr) 71 } 72 73 // Handle reading from the data channel 74 go ReadLoop(raw) 75 76 // Handle writing to the data channel 77 go WriteLoop(raw) 78 }) 79 80 // Create an offer to send to the browser 81 offer, err := peerConnection.CreateOffer(nil) 82 if err != nil { 83 handleError(err) 84 } 85 86 // Sets the LocalDescription, and starts our UDP listeners 87 err = peerConnection.SetLocalDescription(offer) 88 if err != nil { 89 handleError(err) 90 } 91 92 // Add handlers for setting up the connection. 93 peerConnection.OnICEConnectionStateChange(func(state webrtc.ICEConnectionState) { 94 log(fmt.Sprint(state)) 95 }) 96 peerConnection.OnICECandidate(func(candidate *webrtc.ICECandidate) { 97 if candidate != nil { 98 encodedDescr := signal.Encode(peerConnection.LocalDescription()) 99 el := getElementByID("localSessionDescription") 100 el.Set("value", encodedDescr) 101 } 102 }) 103 104 // Set up global callbacks which will be triggered on button clicks. 105 /*js.Global().Set("sendMessage", js.FuncOf(func(_ js.Value, _ []js.Value) interface{} { 106 go func() { 107 el := getElementByID("message") 108 message := el.Get("value").String() 109 if message == "" { 110 js.Global().Call("alert", "Message must not be empty") 111 return 112 } 113 if err := sendChannel.SendText(message); err != nil { 114 handleError(err) 115 } 116 }() 117 return js.Undefined() 118 }))*/ 119 js.Global().Set("startSession", js.FuncOf(func(_ js.Value, _ []js.Value) interface{} { 120 go func() { 121 el := getElementByID("remoteSessionDescription") 122 sd := el.Get("value").String() 123 if sd == "" { 124 js.Global().Call("alert", "Session Description must not be empty") 125 return 126 } 127 128 descr := webrtc.SessionDescription{} 129 signal.Decode(sd, &descr) 130 if err := peerConnection.SetRemoteDescription(descr); err != nil { 131 handleError(err) 132 } 133 }() 134 return js.Undefined() 135 })) 136 137 // Block forever 138 select {} 139 } 140 141 // ReadLoop shows how to read from the datachannel directly 142 func ReadLoop(d io.Reader) { 143 for { 144 buffer := make([]byte, messageSize) 145 n, err := d.Read(buffer) 146 if err != nil { 147 log(fmt.Sprintf("Datachannel closed; Exit the readloop: %v", err)) 148 return 149 } 150 151 log(fmt.Sprintf("Message from DataChannel: %s\n", string(buffer[:n]))) 152 } 153 } 154 155 // WriteLoop shows how to write to the datachannel directly 156 func WriteLoop(d io.Writer) { 157 for range time.NewTicker(5 * time.Second).C { 158 message := signal.RandSeq(messageSize) 159 log(fmt.Sprintf("Sending %s \n", message)) 160 161 _, err := d.Write([]byte(message)) 162 if err != nil { 163 handleError(err) 164 } 165 } 166 } 167 168 func log(msg string) { 169 el := getElementByID("logs") 170 el.Set("innerHTML", el.Get("innerHTML").String()+msg+"<br>") 171 } 172 173 func handleError(err error) { 174 log("Unexpected error. Check console.") 175 panic(err) 176 } 177 178 func getElementByID(id string) js.Value { 179 return js.Global().Get("document").Call("getElementById", id) 180 }