github.com/pion/webrtc/v4@v4.0.1/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 "bufio" 11 "encoding/base64" 12 "encoding/json" 13 "errors" 14 "fmt" 15 "io" 16 "os" 17 "strings" 18 "syscall/js" 19 "time" 20 21 "github.com/pion/randutil" 22 "github.com/pion/webrtc/v4" 23 ) 24 25 const messageSize = 15 26 27 func main() { 28 // Since this behavior diverges from the WebRTC API it has to be 29 // enabled using a settings engine. Mixing both detached and the 30 // OnMessage DataChannel API is not supported. 31 32 // Create a SettingEngine and enable Detach 33 s := webrtc.SettingEngine{} 34 s.DetachDataChannels() 35 36 // Create an API object with the engine 37 api := webrtc.NewAPI(webrtc.WithSettingEngine(s)) 38 39 // Everything below is the Pion WebRTC API! Thanks for using it ❤️. 40 41 // Prepare the configuration 42 config := webrtc.Configuration{ 43 ICEServers: []webrtc.ICEServer{ 44 { 45 URLs: []string{"stun:stun.l.google.com:19302"}, 46 }, 47 }, 48 } 49 50 // Create a new RTCPeerConnection using the API object 51 peerConnection, err := api.NewPeerConnection(config) 52 if err != nil { 53 handleError(err) 54 } 55 56 // Create a datachannel with label 'data' 57 dataChannel, err := peerConnection.CreateDataChannel("data", nil) 58 if err != nil { 59 handleError(err) 60 } 61 62 // Set the handler for ICE connection state 63 // This will notify you when the peer has connected/disconnected 64 peerConnection.OnICEConnectionStateChange(func(connectionState webrtc.ICEConnectionState) { 65 log(fmt.Sprintf("ICE Connection State has changed: %s\n", connectionState.String())) 66 }) 67 68 // Register channel opening handling 69 dataChannel.OnOpen(func() { 70 log(fmt.Sprintf("Data channel '%s'-'%d' open.\n", dataChannel.Label(), dataChannel.ID())) 71 72 // Detach the data channel 73 raw, dErr := dataChannel.Detach() 74 if dErr != nil { 75 handleError(dErr) 76 } 77 78 // Handle reading from the data channel 79 go ReadLoop(raw) 80 81 // Handle writing to the data channel 82 go WriteLoop(raw) 83 }) 84 85 // Create an offer to send to the browser 86 offer, err := peerConnection.CreateOffer(nil) 87 if err != nil { 88 handleError(err) 89 } 90 91 // Sets the LocalDescription, and starts our UDP listeners 92 err = peerConnection.SetLocalDescription(offer) 93 if err != nil { 94 handleError(err) 95 } 96 97 // Add handlers for setting up the connection. 98 peerConnection.OnICEConnectionStateChange(func(state webrtc.ICEConnectionState) { 99 log(fmt.Sprint(state)) 100 }) 101 peerConnection.OnICECandidate(func(candidate *webrtc.ICECandidate) { 102 if candidate != nil { 103 encodedDescr := encode(peerConnection.LocalDescription()) 104 el := getElementByID("localSessionDescription") 105 el.Set("value", encodedDescr) 106 } 107 }) 108 109 // Set up global callbacks which will be triggered on button clicks. 110 /*js.Global().Set("sendMessage", js.FuncOf(func(_ js.Value, _ []js.Value) interface{} { 111 go func() { 112 el := getElementByID("message") 113 message := el.Get("value").String() 114 if message == "" { 115 js.Global().Call("alert", "Message must not be empty") 116 return 117 } 118 if err := sendChannel.SendText(message); err != nil { 119 handleError(err) 120 } 121 }() 122 return js.Undefined() 123 }))*/ 124 js.Global().Set("startSession", js.FuncOf(func(_ js.Value, _ []js.Value) interface{} { 125 go func() { 126 el := getElementByID("remoteSessionDescription") 127 sd := el.Get("value").String() 128 if sd == "" { 129 js.Global().Call("alert", "Session Description must not be empty") 130 return 131 } 132 133 descr := webrtc.SessionDescription{} 134 decode(sd, &descr) 135 if err := peerConnection.SetRemoteDescription(descr); err != nil { 136 handleError(err) 137 } 138 }() 139 return js.Undefined() 140 })) 141 142 // Block forever 143 select {} 144 } 145 146 // ReadLoop shows how to read from the datachannel directly 147 func ReadLoop(d io.Reader) { 148 for { 149 buffer := make([]byte, messageSize) 150 n, err := d.Read(buffer) 151 if err != nil { 152 log(fmt.Sprintf("Datachannel closed; Exit the readloop: %v", err)) 153 return 154 } 155 156 log(fmt.Sprintf("Message from DataChannel: %s\n", string(buffer[:n]))) 157 } 158 } 159 160 // WriteLoop shows how to write to the datachannel directly 161 func WriteLoop(d io.Writer) { 162 ticker := time.NewTicker(5 * time.Second) 163 defer ticker.Stop() 164 for range ticker.C { 165 message, err := randutil.GenerateCryptoRandomString(messageSize, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") 166 if err != nil { 167 handleError(err) 168 } 169 170 log(fmt.Sprintf("Sending %s \n", message)) 171 if _, err := d.Write([]byte(message)); err != nil { 172 handleError(err) 173 } 174 } 175 } 176 177 func log(msg string) { 178 el := getElementByID("logs") 179 el.Set("innerHTML", el.Get("innerHTML").String()+msg+"<br>") 180 } 181 182 func handleError(err error) { 183 log("Unexpected error. Check console.") 184 panic(err) 185 } 186 187 func getElementByID(id string) js.Value { 188 return js.Global().Get("document").Call("getElementById", id) 189 } 190 191 // Read from stdin until we get a newline 192 func readUntilNewline() (in string) { 193 var err error 194 195 r := bufio.NewReader(os.Stdin) 196 for { 197 in, err = r.ReadString('\n') 198 if err != nil && !errors.Is(err, io.EOF) { 199 panic(err) 200 } 201 202 if in = strings.TrimSpace(in); len(in) > 0 { 203 break 204 } 205 } 206 207 fmt.Println("") 208 return 209 } 210 211 // JSON encode + base64 a SessionDescription 212 func encode(obj *webrtc.SessionDescription) string { 213 b, err := json.Marshal(obj) 214 if err != nil { 215 panic(err) 216 } 217 218 return base64.StdEncoding.EncodeToString(b) 219 } 220 221 // Decode a base64 and unmarshal JSON into a SessionDescription 222 func decode(in string, obj *webrtc.SessionDescription) { 223 b, err := base64.StdEncoding.DecodeString(in) 224 if err != nil { 225 panic(err) 226 } 227 228 if err = json.Unmarshal(b, obj); err != nil { 229 panic(err) 230 } 231 }