HLS.js Player¶
Overview¶
The example shows how to convert stream published on WCS server to HLS and play it in browser using HLS.js. HLS segments cut starts automatically when stream is requested by HLS URL, for example https://test1.flashphoner.com:8445/test/test.m3u8
on the screenshot below
Since build 2.0.244, the example supports the following parameters:
llhls
- enable Low Latency HLS support:false
ortrue
src
- stream full HLS URL to play, should be encoded with URI, for examplehttps%3A%2F%2Ftest1.flashphoner.com%3A8445%2Ftest%2Ftest.m3u8
autoplay
- automatically play the HLS URL, in this case all the input fields and buttons are hidden:false
(by default) ortrue
The player URL example with parameters as displayed on the screenshot above (the link is available in Permalink
field)
https://test1.flashphoner.com:8444/client2/examples/demo/streaming/hls-js-player/hls-js-player.html?llhls=false&src=https%3A%2F%2Ftest1.flashphoner.com%3A8445%2Ftest-HLS-ABR-STREAM%2Ftest-HLS-ABR-STREAM.m3u8
The player URL example with autoplay enabled
https://test1.flashphoner.com:8444/client2/examples/demo/streaming/hls-js-player/hls-js-player.html?llhls=false&src=https%3A%2F%2Ftest1.flashphoner.com%3A8445%2Ftest-HLS-ABR-STREAM%2Ftest-HLS-ABR-STREAM.m3u8&autoplay=true
In this case the stream should be played automatically with audio muted. A viewer should use a loud slider in the player interface to unmute audio.
The code of the example¶
The source code can be accessed on server by the following path:
hls-js-player.css
- player page styles filehls-js-player.html
- player pagehls.js
- player script (https://github.com/video-dev/hls.js/, Apache License Version 2.0)hls-js-player.js
- player launch scripthls.min.js
- player script (minimized)../hls-player/player-page.html
- common player page elements for three HLS playback examples
The example can be tested using the following URL:
Where host
is WCS server address
Analyzing the code¶
To analyze the code get hls-js-player.js
file version with hash b19f637
which is available here and can be downloaded in build 2.0.248.
1. Loading the player page¶
const loadPlayerPage = function() {
loadPage("../hls-player/player-page.html", "playerPage", initPage );
}
2. The player HTML page initializing¶
If browser does not support MSE, player will not be initialized and a warning will be displayed
const initPage = function() {
if (playSrc) {
setValue("fullLink", decodeURIComponent(playSrc));
} else if (autoplay) {
console.warn("No HLS URL set, autoplay disabled");
autoplay = false;
}
if (llHlsEnabled) {
setCheckbox("llHlsEnabled", llHlsEnabled);
}
remoteVideo = document.getElementById('remoteVideo');
if (Hls.isSupported()) {
console.log("Using HLS.JS " + Hls.version);
if (autoplay) {
// There should not be any visible item on the page unless player
hideAllToAutoplay();
// The player should use all available page width
setUpPlayerItem(true);
// The player should be muted to automatically start playback
initVideoPlayer(remoteVideo, true);
playBtnClick();
} else {
setText("header", "HLS.JS Player Minimal");
showItem("llHlsMode");
displayCommonItems();
setUpButtons();
enablePlaybackStats();
// The player should have a maximum fixed size
setUpPlayerItem(false);
// The player can be unmuted because user should click Play button
initVideoPlayer(remoteVideo, false);
}
} else {
setText("notifyFlash", "Your browser doesn't support MSE technology required to play video");
disableItem("applyBtn");
toggleInputs(false);
}
}
3. Video tag initializing to play¶
const initVideoPlayer = function(video, muted) {
if (video) {
video.style.backgroundColor = "black";
video.muted = muted;
}
}
4. HLS stream URL forming¶
If authentication key and token are set, they will be included to stream URL
const getVideoSrc = function(src) {
let videoSrc = src;
if (validateForm()) {
let streamName = getValue('playStream');
streamName = encodeURIComponent(streamName);
videoSrc = getValue("urlServer") + '/' + streamName + '/' + streamName + '.m3u8';
let key = getValue('key');
let token = getValue("token");
if (key.length > 0 && token.length > 0) {
videoSrc += "?" + key + "=" + token;
}
}
setValue("fullLink", videoSrc);
return videoSrc;
}
5. HLS.js player configuration¶
const getHlsConfig = function(llHlsEnabled) {
let config = {
lowLatencyMode: false,
enableWorker: true,
backBufferLength: 90,
manifestLoadingTimeOut: 15000
};
console.log("Low Latency HLS: "+llHlsEnabled)
if(llHlsEnabled) {
// Here we configure HLS.JS for lower latency
config = {
lowLatencyMode: llHlsEnabled,
enableWorker: true,
backBufferLength: 90,
liveBackBufferLength: 0,
liveSyncDuration: 0.5,
liveMaxLatencyDuration: 5,
liveDurationInfinity: true,
highBufferWatchdogPeriod: 1,
manifestLoadingTimeOut: 15000
};
}
return config;
}
6. Player launching¶
Hls.on()
, Hls.loadSource()
, Hls.attachMedia()
, HTMLVideoElement.onplaying()
code
const playBtnClick = function() {
let videoSrc = getVideoSrc(getValue("fullLink"));
if (videoSrc) {
llHlsEnabled = getCheckbox("llHlsEnabled");
hlsPlayer = new Hls(getHlsConfig(llHlsEnabled));
hlsPlayer.on(Hls.Events.MANIFEST_PARSED, function() {
console.log("Play with HLS.js");
remoteVideo.play();
initQualityLevels(hlsPlayer);
});
remoteVideo.onplaying = () => {
console.log("playing event fired");
displayPermalink(videoSrc);
displayQualitySwitch();
}
hlsPlayer.loadSource(videoSrc);
hlsPlayer.attachMedia(remoteVideo);
onStarted();
}
}
7. Get available qualities list¶
Hls.levels
code
const initQualityLevels = function(player) {
if (player) {
let qualityDiv = document.getElementById("qualityBtns");
let qualityLevel;
for (let i = 0; i < player.levels.length; i++) {
qualityLevel = QualityLevel(player, player.levels[i].height, i, qualityDiv);
qualityLevels.push(qualityLevel);
}
if (qualityLevels.length) {
qualityLevel = QualityLevel(player, QUALITY_AUTO, -1, qualityDiv);
qualityLevels.push(qualityLevel);
}
}
}
8. Quality button click action¶
Hls.currentLevel
code
const qualityBtnClick = function(button, player, index) {
if (player) {
player.currentLevel = index;
}
button.style.color = QUALITY_COLORS.SELECTED;
qualityLevels.forEach(item => {
if (item.button.id !== button.id) {
item.button.style.color = QUALITY_COLORS.AVAILABLE
}
});
}
9. Playback stopping¶
const stopBtnClick = function() {
if (hlsPlayer != null) {
console.log("Stop HLS segments loading");
hlsPlayer.stopLoad();
hlsPlayer = null;
}
if (remoteVideo != null) {
console.log("Stop HTML5 player");
remoteVideo.pause();
remoteVideo.currentTime = 0;
remoteVideo.removeAttribute('src');
remoteVideo.load();
}
onStopped();
}
10. Dispose qualities list when player is stopped¶
const disposeQualityLevels = function() {
qualityLevels.forEach(level => {
if (level.button) {
level.button.remove();
}
});
qualityLevels = [];
}
11. Getting an available playback statistics from HTML5 video tag¶
HTML5Stats
code
const PlaybackStats = function(interval) {
const playbackStats = {
interval: interval || STATS_INTERVAL,
timer: null,
stats: null,
start: function() {
let video = remoteVideo;
playbackStats.stop();
stats = HTML5Stats(video);
playbackStats.timer = setInterval(playbackStats.displayStats, playbackStats.interval);
setText("videoWidth", "N/A");
setText("videoHeight", "N/A");
setText("videoRate", "N/A");
setText("videoFps", "N/A");
showItem("stats");
},
stop: function() {
if (playbackStats.timer) {
clearInterval(playbackStats.timer);
playbackStats.timer = null;
}
playbackStats.stats = null;
hideItem("stats");
},
displayStats: function() {
if (stats.collect()) {
let width = stats.getWidth();
let height = stats.getHeight();
let bitrate = stats.getBitrate();
let fps = stats.getFps();
setText("videoWidth", width);
setText("videoHeight", height);
if (bitrate !== undefined) {
setText("videoRate", Math.round(bitrate));
}
if (fps !== undefined) {
setText("videoFps", fps.toFixed(1));
}
}
}
};
return playbackStats;
}