Created
May 23, 2021 09:20
-
-
Save mmitou/c8afd108ba195b8b3acceb7ea4301b94 to your computer and use it in GitHub Desktop.
This file contains hidden or 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
<!DOCTYPE html> | |
<html> | |
<head> | |
<title>signaling</title> | |
</head> | |
<body> | |
<div class="localVideo"> | |
<video width="320" height="240" style="border: 1px solid black;" autoplay></video> | |
<button >open camera</button> | |
</div> | |
<div class="remoteVideo"> | |
<video width="320" height="240" style="border: 1px solid black;" autoplay></video> | |
</div> | |
<div class="websocketControl"> | |
<button class="websocketControl_openCloseButton">open</button> | |
</div> | |
<div class="input"> | |
<input type="text"> | |
<button class="input_sendButton">send</button> | |
</div> | |
<div class="output"> | |
</div> | |
<script src="main.js"></script> | |
</body> | |
</html> |
This file contains hidden or 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
package main | |
import ( | |
"context" | |
"fmt" | |
"time" | |
"github.com/gorilla/websocket" | |
"github.com/labstack/echo/v4" | |
"github.com/rs/zerolog" | |
"github.com/rs/zerolog/log" | |
"github.com/rs/zerolog/pkgerrors" | |
) | |
var upgrader = websocket.Upgrader{} | |
type wsClient struct { | |
id string | |
conn *websocket.Conn | |
done <-chan struct{} | |
} | |
type websocketMessage struct { | |
MessageType int | |
Payload []byte | |
} | |
type Message struct { | |
id string | |
websocketMessage | |
err error | |
} | |
func (c wsClient) close() { | |
c.conn.Close() | |
} | |
func (c wsClient) runReciever(srvrCh chan<- Message) { | |
go func() { | |
for { | |
select { | |
case <-c.done: | |
return | |
default: | |
mt, p, err := c.conn.ReadMessage() | |
if err != nil { | |
srvrCh <- Message{id: c.id, err: err} | |
} else { | |
srvrCh <- Message{c.id, websocketMessage{MessageType: mt, Payload: p}, nil} | |
} | |
} | |
} | |
}() | |
} | |
func (c wsClient) runSender(srvrCh chan<- Message) chan<- websocketMessage { | |
ch := make(chan websocketMessage, 10) | |
go func() { | |
for { | |
select { | |
case <-c.done: | |
return | |
case msg := <-ch: | |
err := c.conn.WriteMessage(msg.MessageType, msg.Payload) | |
if err != nil { | |
srvrCh <- Message{id: c.id, err: err} | |
} | |
} | |
} | |
}() | |
return ch | |
} | |
type connection struct { | |
wsClient wsClient | |
sndrCh chan<- websocketMessage | |
done chan<- struct{} | |
} | |
func (c wsClient) connect(msgCh chan<- Message) connection { | |
done := make(chan struct{}) | |
c.done = done | |
c.runReciever(msgCh) | |
sndrCh := c.runSender(msgCh) | |
return connection{wsClient: c, sndrCh: sndrCh, done: done} | |
} | |
func (c connection) close() { | |
close(c.done) | |
c.wsClient.close() | |
} | |
func runEchoServer(ctx context.Context) chan<- wsClient { | |
registrar := make(chan wsClient) | |
go func() { | |
clients := make(map[string]connection) | |
msgCh := make(chan Message) | |
defer func() { | |
for _, c := range clients { | |
c.close() | |
} | |
close(msgCh) | |
}() | |
for { | |
select { | |
case <-ctx.Done(): | |
return | |
case c := <-registrar: | |
clients[c.id] = c.connect(msgCh) | |
case msg := <-msgCh: | |
if msg.err != nil { | |
log.Error().Err(msg.err).Msg("") | |
} else { | |
for _, connection := range clients { | |
connection.sndrCh <- msg.websocketMessage | |
} | |
} | |
} | |
} | |
}() | |
return registrar | |
} | |
func hub(registrar chan<- wsClient) func(c echo.Context) error { | |
i := 0 | |
return func(c echo.Context) error { | |
i++ | |
w := c.Response() | |
r := c.Request() | |
conn, err := upgrader.Upgrade(w, r, nil) | |
if err != nil { | |
return err | |
} | |
registrar <- wsClient{id: fmt.Sprintf("hello%d", i), conn: conn} | |
return nil | |
} | |
} | |
func main() { | |
zerolog.TimeFieldFormat = time.RFC3339Nano | |
zerolog.ErrorStackMarshaler = pkgerrors.MarshalStack | |
zerolog.SetGlobalLevel(zerolog.DebugLevel) | |
e := echo.New() | |
ctx, cancel := context.WithCancel(context.Background()) | |
defer cancel() | |
registrar := runEchoServer(ctx) | |
e.HTTPErrorHandler = func(err error, c echo.Context) { | |
log.Debug().Err(err).Msg(fmt.Sprintf("%+v", err)) | |
e.DefaultHTTPErrorHandler(err, c) | |
} | |
e.GET("/ws", hub(registrar)) | |
e.Static("/", "./web") | |
e.Logger.Fatal(e.Start(":8080")) | |
} |
This file contains hidden or 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
"use strict"; | |
let ws = null; | |
const print = ((output) => { | |
return (msg) => { | |
const d = document.createElement("div"); | |
d.textContent = msg; | |
output.appendChild(d); | |
output.scroll(0, output.scrollHeight); | |
}; | |
})(document.querySelector("div.output")); | |
const initWebsocket = (websocket) => { | |
websocket.addEventListener("open", () => print("open")); | |
websocket.addEventListener("close", () => { | |
print("close"); | |
ws = null; | |
}); | |
websocket.addEventListener("message", (event) => { | |
print("response: " + event.data); | |
}); | |
websocket.addEventListener("error", (event) => { | |
console.log("error:", event.data); | |
}); | |
return websocket; | |
}; | |
document | |
.querySelector(".websocketControl_openCloseButton") | |
.addEventListener("click", (event) => { | |
console.log("openClose button clicked", ws == null); | |
if (ws == null) { | |
// open websocket | |
ws = new WebSocket("ws://localhost:8080/ws"); | |
initWebsocket(ws); | |
// change button text for close | |
event.target.innerText = "close"; | |
} else { | |
ws.close(); | |
// change button text for open | |
event.target.innerText = "open"; | |
} | |
}); | |
document | |
.querySelector(".input_sendButton") | |
.addEventListener("click", (event) => { | |
if (ws == null) { | |
print("ws is null"); | |
return; | |
} | |
const input = document.querySelector("input"); | |
print("send: " + input.value); | |
ws.send(input.value); | |
return; | |
}); | |
// video control | |
const showVideoButton = document.querySelector(".localVideo > button"); | |
showVideoButton.addEventListener("click", async (event) => { | |
try { | |
const stream = await navigator.mediaDevices.getUserMedia({ | |
audio: false, | |
video: true, | |
}); | |
const video = document.querySelector(".localVideo > video"); | |
// const videoTracks = stream.getVideoTracks(); | |
// window.stream = stream; | |
video.srcObject = stream; | |
event.target.disabled = true; | |
} catch (err) { | |
console.log(err); | |
} | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment