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  }