-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
JS/WASM shim for Detach API
- Loading branch information
Showing
6 changed files
with
303 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
// +build js,wasm | ||
|
||
package webrtc | ||
|
||
import ( | ||
"errors" | ||
) | ||
|
||
type detachedDataChannel struct { | ||
dc *DataChannel | ||
|
||
read chan DataChannelMessage | ||
done chan struct{} | ||
} | ||
|
||
func newDetachedDataChannel(dc *DataChannel) *detachedDataChannel { | ||
read := make(chan DataChannelMessage) | ||
done := make(chan struct{}) | ||
|
||
// Wire up callbacks | ||
dc.OnMessage(func(msg DataChannelMessage) { | ||
read <- msg // TODO: Potential leak? | ||
}) | ||
|
||
// TODO: OnClose? | ||
|
||
return &detachedDataChannel{ | ||
dc: dc, | ||
read: read, | ||
done: done, | ||
} | ||
} | ||
|
||
func (c *detachedDataChannel) Read(p []byte) (int, error) { | ||
n, _, err := c.ReadDataChannel(p) | ||
return n, err | ||
} | ||
|
||
func (c *detachedDataChannel) ReadDataChannel(p []byte) (int, bool, error) { | ||
select { | ||
case <-c.done: | ||
return 0, false, errors.New("Reader closed") | ||
case msg := <-c.read: | ||
n := copy(p, msg.Data) | ||
if n < len(msg.Data) { | ||
return n, msg.IsString, errors.New("Read buffer to small") | ||
} | ||
return n, msg.IsString, nil | ||
} | ||
} | ||
|
||
func (c *detachedDataChannel) Write(p []byte) (n int, err error) { | ||
return c.WriteDataChannel(p, false) | ||
} | ||
|
||
func (c *detachedDataChannel) WriteDataChannel(p []byte, isString bool) (n int, err error) { | ||
if isString { | ||
err = c.dc.SendText(string(p)) | ||
return len(p), err | ||
} | ||
|
||
err = c.dc.Send(p) | ||
|
||
return len(p), err | ||
} | ||
|
||
func (c *detachedDataChannel) Close() error { | ||
close(c.done) | ||
|
||
return c.dc.Close() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
textarea { | ||
width: 500px; | ||
min-height: 75px; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
Browser base64 Session Description<br /> | ||
<textarea id="localSessionDescription" readonly="true"></textarea> <br /> | ||
|
||
Golang base64 Session Description<br /> | ||
<textarea id="remoteSessionDescription"></textarea><br/> | ||
<button onclick="window.startSession()">Start Session</button><br /> | ||
|
||
<br /> | ||
|
||
<!--Message<br /> | ||
<textarea id="message">This is my DataChannel message!</textarea> <br/> | ||
<button onclick="window.sendMessage()">Send Message</button> <br />--> | ||
|
||
<br /> | ||
Logs<br /> | ||
<div id="logs"></div> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,176 @@ | ||
// +build js,wasm | ||
|
||
package main | ||
|
||
import ( | ||
"fmt" | ||
"io" | ||
"syscall/js" | ||
"time" | ||
|
||
"github.com/pions/webrtc" | ||
|
||
"github.com/pions/webrtc/examples/internal/signal" | ||
) | ||
|
||
const messageSize = 15 | ||
|
||
func main() { | ||
// Since this behavior diverges from the WebRTC API it has to be | ||
// enabled using a settings engine. Mixing both detached and the | ||
// OnMessage DataChannel API is not supported. | ||
|
||
// Create a SettingEngine and enable Detach | ||
s := webrtc.SettingEngine{} | ||
s.DetachDataChannels() | ||
|
||
// Create an API object with the engine | ||
api := webrtc.NewAPI(webrtc.WithSettingEngine(s)) | ||
|
||
// Everything below is the pion-WebRTC API! Thanks for using it ❤️. | ||
|
||
// Prepare the configuration | ||
config := webrtc.Configuration{ | ||
ICEServers: []webrtc.ICEServer{ | ||
{ | ||
URLs: []string{"stun:stun.l.google.com:19302"}, | ||
}, | ||
}, | ||
} | ||
|
||
// Create a new RTCPeerConnection using the API object | ||
peerConnection, err := api.NewPeerConnection(config) | ||
if err != nil { | ||
handleError(err) | ||
} | ||
|
||
// Create a datachannel with label 'data' | ||
dataChannel, err := peerConnection.CreateDataChannel("data", nil) | ||
if err != nil { | ||
handleError(err) | ||
} | ||
|
||
// Set the handler for ICE connection state | ||
// This will notify you when the peer has connected/disconnected | ||
peerConnection.OnICEConnectionStateChange(func(connectionState webrtc.ICEConnectionState) { | ||
log(fmt.Sprintf("ICE Connection State has changed: %s\n", connectionState.String())) | ||
}) | ||
|
||
// Register channel opening handling | ||
dataChannel.OnOpen(func() { | ||
log(fmt.Sprintf("Data channel '%s'-'%d' open.\n", dataChannel.Label(), dataChannel.ID())) | ||
|
||
// Detach the data channel | ||
raw, dErr := dataChannel.Detach() | ||
if dErr != nil { | ||
handleError(dErr) | ||
} | ||
|
||
// Handle reading from the data channel | ||
go ReadLoop(raw) | ||
|
||
// Handle writing to the data channel | ||
go WriteLoop(raw) | ||
}) | ||
|
||
// Create an offer to send to the browser | ||
offer, err := peerConnection.CreateOffer(nil) | ||
if err != nil { | ||
handleError(err) | ||
} | ||
|
||
// Sets the LocalDescription, and starts our UDP listeners | ||
err = peerConnection.SetLocalDescription(offer) | ||
if err != nil { | ||
handleError(err) | ||
} | ||
|
||
// Add handlers for setting up the connection. | ||
peerConnection.OnICEConnectionStateChange(func(state webrtc.ICEConnectionState) { | ||
log(fmt.Sprint(state)) | ||
}) | ||
peerConnection.OnICECandidate(func(candidate *webrtc.ICECandidate) { | ||
if candidate != nil { | ||
encodedDescr := signal.Encode(peerConnection.LocalDescription()) | ||
el := getElementByID("localSessionDescription") | ||
el.Set("value", encodedDescr) | ||
} | ||
}) | ||
|
||
// Set up global callbacks which will be triggered on button clicks. | ||
/*js.Global().Set("sendMessage", js.FuncOf(func(_ js.Value, _ []js.Value) interface{} { | ||
go func() { | ||
el := getElementByID("message") | ||
message := el.Get("value").String() | ||
if message == "" { | ||
js.Global().Call("alert", "Message must not be empty") | ||
return | ||
} | ||
if err := sendChannel.SendText(message); err != nil { | ||
handleError(err) | ||
} | ||
}() | ||
return js.Undefined() | ||
}))*/ | ||
js.Global().Set("startSession", js.FuncOf(func(_ js.Value, _ []js.Value) interface{} { | ||
go func() { | ||
el := getElementByID("remoteSessionDescription") | ||
sd := el.Get("value").String() | ||
if sd == "" { | ||
js.Global().Call("alert", "Session Description must not be empty") | ||
return | ||
} | ||
|
||
descr := webrtc.SessionDescription{} | ||
signal.Decode(sd, &descr) | ||
if err := peerConnection.SetRemoteDescription(descr); err != nil { | ||
handleError(err) | ||
} | ||
}() | ||
return js.Undefined() | ||
})) | ||
|
||
// Block forever | ||
select {} | ||
} | ||
|
||
// ReadLoop shows how to read from the datachannel directly | ||
func ReadLoop(d io.Reader) { | ||
for { | ||
buffer := make([]byte, messageSize) | ||
n, err := d.Read(buffer) | ||
if err != nil { | ||
log(fmt.Sprintf("Datachannel closed; Exit the readloop: %v", err)) | ||
return | ||
} | ||
|
||
log(fmt.Sprintf("Message from DataChannel: %s\n", string(buffer[:n]))) | ||
} | ||
} | ||
|
||
// WriteLoop shows how to write to the datachannel directly | ||
func WriteLoop(d io.Writer) { | ||
for range time.NewTicker(5 * time.Second).C { | ||
message := signal.RandSeq(messageSize) | ||
log(fmt.Sprintf("Sending %s \n", message)) | ||
|
||
_, err := d.Write([]byte(message)) | ||
if err != nil { | ||
handleError(err) | ||
} | ||
} | ||
} | ||
|
||
func log(msg string) { | ||
el := getElementByID("logs") | ||
el.Set("innerHTML", el.Get("innerHTML").String()+msg+"<br>") | ||
} | ||
|
||
func handleError(err error) { | ||
log("Unexpected error. Check console.") | ||
panic(err) | ||
} | ||
|
||
func getElementByID(id string) js.Value { | ||
return js.Global().Get("document").Call("getElementById", id) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters