golanggohlsrtmpwebrtcmedia-serverobs-studiortcprtmp-proxyrtmp-serverrtprtsprtsp-proxyrtsp-relayrtsp-serversrtstreamingwebrtc-proxy
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
144 lines
3.1 KiB
144 lines
3.1 KiB
<!DOCTYPE html> |
|
<html> |
|
<head> |
|
<meta charset="utf-8"> |
|
<meta name="viewport" content="width=device-width"> |
|
<style> |
|
html, body { |
|
margin: 0; |
|
padding: 0; |
|
height: 100%; |
|
font-family: 'Arial', sans-serif; |
|
} |
|
#video { |
|
position: absolute; |
|
top: 0; |
|
left: 0; |
|
width: 100%; |
|
height: 100%; |
|
background: rgb(30, 30, 30); |
|
} |
|
#message { |
|
position: absolute; |
|
left: 0; |
|
top: 0; |
|
width: 100%; |
|
height: 100%; |
|
display: flex; |
|
align-items: center; |
|
text-align: center; |
|
justify-content: center; |
|
font-size: 16px; |
|
font-weight: bold; |
|
color: white; |
|
pointer-events: none; |
|
padding: 20px; |
|
box-sizing: border-box; |
|
text-shadow: 0 0 5px black; |
|
} |
|
</style> |
|
</head> |
|
<body> |
|
|
|
<video id="video"></video> |
|
<div id="message"></div> |
|
|
|
<script src="hls.min.js"></script> |
|
|
|
<script> |
|
|
|
const retryPause = 2000; |
|
|
|
const video = document.getElementById('video'); |
|
const message = document.getElementById('message'); |
|
|
|
let defaultControls = false; |
|
|
|
const setMessage = (str) => { |
|
if (str !== '') { |
|
video.controls = false; |
|
} else { |
|
video.controls = defaultControls; |
|
} |
|
message.innerText = str; |
|
}; |
|
|
|
const loadStream = () => { |
|
// always prefer hls.js over native HLS. |
|
// this is because some Android versions support native HLS |
|
// but don't support fMP4s. |
|
if (Hls.isSupported()) { |
|
const hls = new Hls({ |
|
maxLiveSyncPlaybackRate: 1.5, |
|
}); |
|
|
|
hls.on(Hls.Events.ERROR, (evt, data) => { |
|
if (data.fatal) { |
|
hls.destroy(); |
|
|
|
if (data.details === 'manifestIncompatibleCodecsError') { |
|
setMessage('stream makes use of codecs which are incompatible with this browser or operative system'); |
|
} else if (data.response && data.response.code === 404) { |
|
setMessage('stream not found, retrying in some seconds'); |
|
} else { |
|
setMessage(data.error + ', retrying in some seconds'); |
|
} |
|
|
|
setTimeout(() => loadStream(video), retryPause); |
|
} |
|
}); |
|
|
|
hls.on(Hls.Events.MEDIA_ATTACHED, () => { |
|
hls.loadSource('index.m3u8' + window.location.search); |
|
}); |
|
|
|
hls.on(Hls.Events.MANIFEST_PARSED, () => { |
|
setMessage(''); |
|
video.play(); |
|
}); |
|
|
|
hls.attachMedia(video); |
|
|
|
} else if (video.canPlayType('application/vnd.apple.mpegurl')) { |
|
// since it's not possible to detect timeout errors in iOS, |
|
// wait for the playlist to be available before starting the stream |
|
fetch('index.m3u8') |
|
.then(() => { |
|
video.src = 'index.m3u8'; |
|
video.play(); |
|
}); |
|
} |
|
}; |
|
|
|
const parseBoolString = (str, defaultVal) => { |
|
str = (str || ''); |
|
|
|
if (['1', 'yes', 'true'].includes(str.toLowerCase())) { |
|
return true; |
|
} |
|
if (['0', 'no', 'false'].includes(str.toLowerCase())) { |
|
return false; |
|
} |
|
return defaultVal; |
|
}; |
|
|
|
const loadAttributesFromQuery = () => { |
|
const params = new URLSearchParams(window.location.search); |
|
video.controls = parseBoolString(params.get('controls'), true); |
|
video.muted = parseBoolString(params.get('muted'), true); |
|
video.autoplay = parseBoolString(params.get('autoplay'), true); |
|
video.playsInline = parseBoolString(params.get('playsinline'), true); |
|
defaultControls = video.controls; |
|
}; |
|
|
|
const init = () => { |
|
loadAttributesFromQuery(); |
|
loadStream(); |
|
}; |
|
|
|
window.addEventListener('DOMContentLoaded', init); |
|
|
|
</script> |
|
|
|
</body> |
|
</html>
|
|
|