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  }