github.com/pion/webrtc/v4@v4.0.1/examples/data-channels/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 20 "github.com/pion/webrtc/v4" 21 ) 22 23 func main() { 24 // Configure and create a new PeerConnection. 25 config := webrtc.Configuration{ 26 ICEServers: []webrtc.ICEServer{ 27 { 28 URLs: []string{"stun:stun.l.google.com:19302"}, 29 }, 30 }, 31 } 32 pc, err := webrtc.NewPeerConnection(config) 33 if err != nil { 34 handleError(err) 35 } 36 37 // Create DataChannel. 38 sendChannel, err := pc.CreateDataChannel("foo", nil) 39 if err != nil { 40 handleError(err) 41 } 42 sendChannel.OnClose(func() { 43 fmt.Println("sendChannel has closed") 44 }) 45 sendChannel.OnOpen(func() { 46 fmt.Println("sendChannel has opened") 47 48 candidatePair, err := pc.SCTP().Transport().ICETransport().GetSelectedCandidatePair() 49 50 fmt.Println(candidatePair) 51 fmt.Println(err) 52 }) 53 sendChannel.OnMessage(func(msg webrtc.DataChannelMessage) { 54 log(fmt.Sprintf("Message from DataChannel %s payload %s", sendChannel.Label(), string(msg.Data))) 55 }) 56 57 // Create offer 58 offer, err := pc.CreateOffer(nil) 59 if err != nil { 60 handleError(err) 61 } 62 if err := pc.SetLocalDescription(offer); err != nil { 63 handleError(err) 64 } 65 66 // Add handlers for setting up the connection. 67 pc.OnICEConnectionStateChange(func(state webrtc.ICEConnectionState) { 68 log(fmt.Sprint(state)) 69 }) 70 pc.OnICECandidate(func(candidate *webrtc.ICECandidate) { 71 if candidate != nil { 72 encodedDescr := encode(pc.LocalDescription()) 73 el := getElementByID("localSessionDescription") 74 el.Set("value", encodedDescr) 75 } 76 }) 77 78 // Set up global callbacks which will be triggered on button clicks. 79 js.Global().Set("sendMessage", js.FuncOf(func(_ js.Value, _ []js.Value) interface{} { 80 go func() { 81 el := getElementByID("message") 82 message := el.Get("value").String() 83 if message == "" { 84 js.Global().Call("alert", "Message must not be empty") 85 return 86 } 87 if err := sendChannel.SendText(message); err != nil { 88 handleError(err) 89 } 90 }() 91 return js.Undefined() 92 })) 93 js.Global().Set("startSession", js.FuncOf(func(_ js.Value, _ []js.Value) interface{} { 94 go func() { 95 el := getElementByID("remoteSessionDescription") 96 sd := el.Get("value").String() 97 if sd == "" { 98 js.Global().Call("alert", "Session Description must not be empty") 99 return 100 } 101 102 descr := webrtc.SessionDescription{} 103 decode(sd, &descr) 104 if err := pc.SetRemoteDescription(descr); err != nil { 105 handleError(err) 106 } 107 }() 108 return js.Undefined() 109 })) 110 js.Global().Set("copySDP", js.FuncOf(func(_ js.Value, _ []js.Value) interface{} { 111 go func() { 112 defer func() { 113 if e := recover(); e != nil { 114 switch e := e.(type) { 115 case error: 116 handleError(e) 117 default: 118 handleError(fmt.Errorf("recovered with non-error value: (%T) %s", e, e)) 119 } 120 } 121 }() 122 123 browserSDP := getElementByID("localSessionDescription") 124 125 browserSDP.Call("focus") 126 browserSDP.Call("select") 127 128 copyStatus := js.Global().Get("document").Call("execCommand", "copy") 129 if copyStatus.Bool() { 130 log("Copying SDP was successful") 131 } else { 132 log("Copying SDP was unsuccessful") 133 } 134 }() 135 return js.Undefined() 136 })) 137 138 // Stay alive 139 select {} 140 } 141 142 func log(msg string) { 143 el := getElementByID("logs") 144 el.Set("innerHTML", el.Get("innerHTML").String()+msg+"<br>") 145 } 146 147 func handleError(err error) { 148 log("Unexpected error. Check console.") 149 panic(err) 150 } 151 152 func getElementByID(id string) js.Value { 153 return js.Global().Get("document").Call("getElementById", id) 154 } 155 156 // Read from stdin until we get a newline 157 func readUntilNewline() (in string) { 158 var err error 159 160 r := bufio.NewReader(os.Stdin) 161 for { 162 in, err = r.ReadString('\n') 163 if err != nil && !errors.Is(err, io.EOF) { 164 panic(err) 165 } 166 167 if in = strings.TrimSpace(in); len(in) > 0 { 168 break 169 } 170 } 171 172 fmt.Println("") 173 return 174 } 175 176 // JSON encode + base64 a SessionDescription 177 func encode(obj *webrtc.SessionDescription) string { 178 b, err := json.Marshal(obj) 179 if err != nil { 180 panic(err) 181 } 182 183 return base64.StdEncoding.EncodeToString(b) 184 } 185 186 // Decode a base64 and unmarshal JSON into a SessionDescription 187 func decode(in string, obj *webrtc.SessionDescription) { 188 b, err := base64.StdEncoding.DecodeString(in) 189 if err != nil { 190 panic(err) 191 } 192 193 if err = json.Unmarshal(b, obj); err != nil { 194 panic(err) 195 } 196 }