feat: videojs for chunked HLS
This commit is contained in:
72
index.html
72
index.html
@@ -2,6 +2,7 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Okiiparty</title>
|
||||
<link href="https://vjs.zencdn.net/8.23.3/video-js.css" rel="stylesheet" />
|
||||
|
||||
<style>
|
||||
* {padding:0; margin: 0}
|
||||
@@ -32,6 +33,25 @@
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
.video-js {
|
||||
position: revert;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.hide {
|
||||
animation: fade-out 0.5s forwards;
|
||||
}
|
||||
|
||||
@keyframes fade-out {
|
||||
100% {
|
||||
opacity: 0;
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
@@ -42,62 +62,78 @@
|
||||
</abbr>
|
||||
</div>
|
||||
|
||||
<video muted controls>
|
||||
<source src="/stream" type="video/mp4">
|
||||
<video
|
||||
id="video"
|
||||
class="video-js"
|
||||
controls
|
||||
preload="auto"
|
||||
muted
|
||||
data-setup="{}"
|
||||
>
|
||||
<source src="/stream/stream.m3u8" />
|
||||
<p class="vjs-no-js">
|
||||
To view this video please enable JavaScript, and consider upgrading to a
|
||||
web browser that
|
||||
<a href="https://videojs.com/html5-video-support/" target="_blank">supports HTML5 video</a>
|
||||
</p>
|
||||
</video>
|
||||
|
||||
<script src="https://vjs.zencdn.net/8.23.3/video.min.js"></script>
|
||||
|
||||
<script>
|
||||
const loc = window.location;
|
||||
let proto = loc.protocol == "https:" ? "wss:" : "ws:";
|
||||
let host = loc.host;
|
||||
const video = videojs("video");
|
||||
let wsUri = `${proto}//${host}/broker`;
|
||||
const socket = new WebSocket(wsUri);
|
||||
const video = document.querySelector("video");
|
||||
const banner = document.querySelector(".banner");
|
||||
let requestedWait = false
|
||||
|
||||
const broadcast = message => socket.send(JSON.stringify(message));
|
||||
const broadcastSeek = () => broadcast({ action: "seek", time: video.currentTime })
|
||||
const broadcastSeek = () => broadcast({ action: "seek", time: video.currentTime() })
|
||||
const broadcastPlay = () => broadcast({action: "play"});
|
||||
const broadcastPause = () => broadcast({action: "pause"});
|
||||
|
||||
// Oneshot block for event propagatation
|
||||
function stepOverEvent(eventType, listener, stepFunction) {
|
||||
video.removeEventListener(eventType, listener)
|
||||
video.off(eventType, listener)
|
||||
stepFunction(video)
|
||||
video.addEventListener(eventType, () => { video.addEventListener(eventType, listener) })
|
||||
video.on(eventType, () => { video.on(eventType, listener) })
|
||||
}
|
||||
|
||||
socket.addEventListener("message", (event) => {
|
||||
let message = JSON.parse(event.data)
|
||||
if (message.action == "play") { stepOverEvent("play", broadcastPlay, video => { video.play() }) }
|
||||
if (message.action == "pause") { stepOverEvent("pause", broadcastPause, video => { video.pause() }) }
|
||||
if (message.action == "seek") { stepOverEvent("seeked", broadcastSeek, video => { video.currentTime = message.time }) }
|
||||
if (message.action == "seek") { stepOverEvent("seeked", broadcastSeek, video => { video.currentTime(message.time) }) }
|
||||
|
||||
if (message.action == "wait") {
|
||||
if (!video.paused) { video.pause() }
|
||||
video.controls = false
|
||||
if (!video.paused()) { video.pause() }
|
||||
video.controls(false)
|
||||
}
|
||||
if (message.action == "canplay") { video.controls = true }
|
||||
if (message.action == "canplay") { video.controls(true) }
|
||||
|
||||
})
|
||||
|
||||
video.addEventListener("play", broadcastPlay)
|
||||
video.addEventListener("pause", broadcastPause)
|
||||
video.addEventListener("seeked", broadcastSeek)
|
||||
video.addEventListener("waiting", () => {
|
||||
video.on("play", broadcastPlay)
|
||||
video.on("pause", broadcastPause)
|
||||
video.on("seeked", broadcastSeek)
|
||||
video.on("waiting", () => {
|
||||
requestedWait = true
|
||||
broadcast({action: "wait"})
|
||||
video.controls = false
|
||||
video.controls(false)
|
||||
})
|
||||
|
||||
video.addEventListener("canplay", () => {
|
||||
video.on("canplay", () => {
|
||||
if (!requestedWait) { return }
|
||||
broadcast({action: "canplay"})
|
||||
video.controls = true
|
||||
video.controls(true)
|
||||
requestedWait = false
|
||||
})
|
||||
video.addEventListener("volumechange", () => { banner.style.display = "none" })
|
||||
video.on("volumechange", () => {
|
||||
banner.classList.add('hide');
|
||||
})
|
||||
|
||||
</script>
|
||||
</body>
|
||||
|
||||
16
main.go
16
main.go
@@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
@@ -12,7 +13,7 @@ import (
|
||||
|
||||
type Broker struct {
|
||||
sockets map[*websocket.Conn]struct{}
|
||||
mutex sync.Mutex
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
var upgrader = websocket.Upgrader{
|
||||
@@ -22,6 +23,9 @@ var upgrader = websocket.Upgrader{
|
||||
|
||||
var broker Broker
|
||||
|
||||
//go:embed index.html
|
||||
var static embed.FS
|
||||
|
||||
func actionBroker(w http.ResponseWriter, r *http.Request) {
|
||||
conn, err := upgrader.Upgrade(w, r, nil)
|
||||
if err != nil {
|
||||
@@ -29,10 +33,10 @@ func actionBroker(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
broker.mutex.Lock()
|
||||
broker.Lock()
|
||||
broker.sockets[conn] = struct{}{}
|
||||
log.Printf("broker currently handles %d clients", len(broker.sockets))
|
||||
broker.mutex.Unlock()
|
||||
broker.Unlock()
|
||||
|
||||
for {
|
||||
messageType, p, err := conn.ReadMessage()
|
||||
@@ -67,12 +71,10 @@ func main() {
|
||||
log.Fatal("stream file is undefined")
|
||||
}
|
||||
broker.sockets = make(map[*websocket.Conn]struct{})
|
||||
http.HandleFunc("/stream", func(w http.ResponseWriter, r *http.Request) {
|
||||
http.ServeFile(w, r, streamFile)
|
||||
})
|
||||
http.Handle("/stream/", http.StripPrefix("/stream/", http.FileServer(http.Dir(streamFile))))
|
||||
http.HandleFunc("/broker", actionBroker)
|
||||
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
http.ServeFile(w, r, "index.html")
|
||||
http.ServeFileFS(w, r, static, "index.html")
|
||||
})
|
||||
|
||||
panic(http.ListenAndServe(fmt.Sprintf(":%d", *listenPort), nil))
|
||||
|
||||
Reference in New Issue
Block a user