...
| Code Block | ||
|---|---|---|
| ||
https://test1.flashphoner.com:8444/client2/examples/demo/streaming/hls-player/hls-player.html?version=videojs7&src=https%3A%2F%2Ftest1.flashphoner.com%3A8445%2Ftest%2Ftestcom%3A8445%2Ftest-HLS-ABR-STREAM%2Ftest-HLS-ABR-STREAM.m3u8 |
Пример вызова плеера с автозапуском
| Code Block | ||
|---|---|---|
| ||
https://test1.flashphoner.com:8444/client2/examples/demo/streaming/hls-player/hls-player.html?version=videojs7&src=https%3A%2F%2Ftest1.flashphoner.com%3A8445%2Ftest%2Ftestcom%3A8445%2Ftest-HLS-ABR-STREAM%2Ftest-HLS-ABR-STREAM.m3u8&autoplay=true |
При этом воспроизведение потока будет запущено автоматически, с отключенным звуком. Для включения звука зритель должен использовать кнопку регулятора громкости в интерфейсе плеера.
...
video.js - скрипт, обеспечивающий работу плеера (http://videojs.com/, Apache License Version 2.0)
video.min.js - скрипт, обеспечивающий работу плеера (минимизированная версия)
videojs-contrib-quality-levels.js - плагин для переключения качества (только VideoJS 7)
video-js.css - файл стилей HLS-плеера
...
Для разбора кода возьмем версию файла hls-player.js с хешем 1703e13b19f637, которая находится здесь и доступна для скачивания в соответствующей сборке 2.0.244248.
1. Загрузка страницы плеера
| Code Block | ||||
|---|---|---|---|---|
| ||||
const loadPlayerPage = function() {
if (videojsVersion) {
hideItem("videojsInputForm");
loadVideoJS("videojs" + videojsVersion);
} else {
let videojsInput = document.getElementById("videojsInput");if (autoplay) {
for (videojsType in VIDEOJS_VERSION_TYPE) { console.warn("No VideoJS version set, autoplay disabled");
let optionautoplay = document.createElement("option")false;
}
let videojsFoldervideojsInput = document.getElementById("videojsInput");
for (videojsType switch (videojsTypein VIDEOJS_VERSION_TYPE) {
let option = document.createElement("option");
case ' let videojsFolder = "";
switch (videojsType) {
case 'VIDEOJS7':
videojsFolder = VIDEOJS_VERSION_TYPE.VIDEOJS7;
break;
case 'VIDEOJS8':
videojsFolder = VIDEOJS_VERSION_TYPE.VIDEOJS8;
break;
}
option.text = videojsFolder;
option.value = videojsFolder;
videojsInput.appendChild(option);
}
setHandler("videojsBtn", "click", onVideojsBtnClick);
}
} |
2. Загрузка выбранной версии VideoJS
| Code Block | ||||
|---|---|---|---|---|
| ||||
const loadVideoJS = function (version) { if (version) { videojsVersion = version; let playerPage = document.getElementById("playerPage"); loadFile(version + "/video.js", "text/javascript").then( data => { console.log("HLS library loaded successfully", data); loadFileloadStyles(version + "/video-js.css", "stylesheet").then ( data, playerPage); }).catch( err => { console.log("HLS library stylesheet loaded successfully", datasetText("videojsError", "Can't load VideoJS library"); hideItem("videojsInputForm"console.error(err); }); loadPage("player-page.html", "playerPage", initPage );} } |
3. Загрузка стилей для выбранной версии VideoJS
| Code Block | ||||
|---|---|---|---|---|
| ||||
const loadStyles = function(version, playerPage) { if (version) { loadFile(version + }).catch( err"/video-js.css", "stylesheet").then ( data => { console.log("HLS library stylesheet playerPage.innerHTML = "Can't load VideoJS library stylesheet"loaded successfully", data); if (version playerPage.setAttribute("class", "text-danger");=== VIDEOJS_VERSION_TYPE.VIDEOJS7) { console.error(errloadQualityPlugin(version, playerPage); }) else { }).catch( err => { setText("videojsError", "Can't load VideoJS library"); hideItem("videojsInputForm"); console.error(errloadPage("player-page.html", "playerPage", initPage); }); } } |
3. Инициализация HTML-страницы плеера
| Code Block | ||||
|---|---|---|---|---|
| ||||
const initPage = function() { if (playSrc) }).catch( err => { setValue("fullLink", decodeURIComponent(playSrc)); } else if (autoplay) { playerPage.innerHTML = "Can't load VideoJS library stylesheet"; consoleplayerPage.warnsetAttribute("No HLS URL set, autoplay disabled"class", "text-danger"); autoplay = false console.error(err); } let remoteVideo = document.getElementById('remoteVideo'}); if (autoplay} } |
4. Загрузка плагина переключения качества для VideoJS 7
| Code Block | ||||
|---|---|---|---|---|
| ||||
const loadQualityPlugin = function(version, playerPage) { if (version) { // There should not be any visibleloadFile(version item on the page unless player+ "/videojs-contrib-quality-levels.js", "text/javascript").then( data => { hideAllToAutoplay( console.log("HLS quality levels plugin loaded successfully", data); // The player should use all available page width hideItem("videojsInputForm"); setUpPlayerItem(trueloadPage("player-page.html", "playerPage", initPage); // The player should be muted to automatically start playback }).catch( err => { player = initVideoJsPlayer(remoteVideo, true); playBtnClick(setText("videojsError", "Can't load VideoJS quality levels plugin"); } else { console.error(err); // No autoplay, all the forms and}); buttons should be visible setText("header", "HLS VideoJS Player Minimal");} } |
5. Инициализация HTML-страницы плеера
| Code Block | ||||
|---|---|---|---|---|
| ||||
const initPage = function() { if displayCommonItems(playSrc); { setUpButtons(setValue("fullLink", decodeURIComponent(playSrc)); } else if enablePlaybackStats(autoplay); { // The player should have a maximum fixed size console.warn("No HLS URL set, autoplay disabled"); autoplay = setUpPlayerItem(false); } //let TheremoteVideo player can be unmuted because user should click Play button = document.getElementById('remoteVideo'); if (autoplay) { // There playershould = initVideoJsPlayer(remoteVideo, false); } } |
4. Инициализация плеера
videojs() code
Плееру передаются следующие параметры:
- video - div-элемент, в котором должен быть проигран поток
- playsinline: true - проигрывать видео на странице, не переключаясь в полноэкранный режим (игнорируется в iOS Safari)
- playbackRates - список скоростей воспроизведения
- liveui: true - включает интерфейс для перемотки (DVR)
- liveTracker - настройка границы проигрывания живого потока
- fill: true - масштабировать плеер под размеры div элемента
| Code Block | ||||
|---|---|---|---|---|
| ||||
const initVideoJsPlayer = function(video, muted) { let videoJsPlayer = null; if (video) { video.className = "video-js vjs-default-skin"; videoJsPlayer = videojs(video, { 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 player = initVideoJsPlayer(remoteVideo, true); playBtnClick(); } else { // No autoplay, all the forms and buttons should be visible setText("header", "HLS VideoJS Player Minimal"); displayCommonItems(); setUpButtons(); enablePlaybackStats(); // The player should have a maximum fixed size setUpPlayerItem(false); // The player can be unmuted because user should click Play button player = initVideoJsPlayer(remoteVideo, false); } } |
6. Инициализация плеера
videojs() code
Плееру передаются следующие параметры:
- video - div-элемент, в котором должен быть проигран поток
- playsinline: true - проигрывать видео на странице, не переключаясь в полноэкранный режим (игнорируется в iOS Safari)
- playbackRates - список скоростей воспроизведения
- liveui: true - включает интерфейс для перемотки (DVR)
- liveTracker - настройка границы проигрывания живого потока
- fill: true - масштабировать плеер под размеры div элемента
- muted - включить или выключить (для autoplay) звук
- html5.vhs.limitRenditionByPlayerDimensions: false - не ограничивать загружаемое качество размерами div элемента
| Code Block | ||||
|---|---|---|---|---|
| ||||
const initVideoJsPlayer = function(video, muted) {
let videoJsPlayer = null;
if (video) {
video.className = "video-js vjs-default-skin";
videoJsPlayer = videojs(video, {
playsinline: true,
playbackRates: [0.1, 0.25, 0.5, 1, 1.5, 2],
liveui: true,
liveTracker: {
trackingThreshold: LIVE_THRESHOLD,
liveTolerance: LIVE_TOLERANCE
},
fill: true,
muted: muted,
html5: {
vhs: {
limitRenditionByPlayerDimensions: false
}
}
});
console.log("Using VideoJs " + videojs.VERSION);
if (Browser.isSafariWebRTC() && Browser.isiOS()) {
// iOS hack when using standard controls to leave fullscreen mode
let videoTag = getActualVideoTag();
if(videoTag) {
setWebkitFullscreenHandlers(videoTag, false);
}
}
}
return videoJsPlayer;
} |
7. Формирование URL HLS-потока
encodeURIComponent() code
Если указаны ключ и токен авторизации, они будут включены в URL потока
| Code Block | ||||
|---|---|---|---|---|
| ||||
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;
} |
8. Запуск плеера
player.on(), player.play() code
| Code Block | ||||
|---|---|---|---|---|
| ||||
const playBtnClick = function() {
let videoSrc = getVideoSrc(getValue("fullLink"));
if (videoSrc) {
player.on('loadedmetadata', function() {
console.log("Play with VideoJs");
player.play();
});
...
player.src({
src: videoSrc,
type: "application/vnd.apple.mpegurl"
});
onStarted();
}
} |
9. Обработка события playing
player.on() code
| Code Block | ||||
|---|---|---|---|---|
| ||||
const playBtnClick = function() { let videoSrc = getVideoSrc(getValue("fullLink")); if (videoSrc) { ... player.on('playing', function() { console.log("playing event fired"); displayPermalink(videoSrc); if (player.liveTracker) { playsinline: true, if (!player.liveTracker.isLive()) { playbackRates: [0.1, 0.25, 0.5, 1, 1.5, 2], // A cratch to display live UI for the liveui:first true,subscriber liveTracker: { liveUIDisplay(); trackingThreshold: LIVE_THRESHOLD, } liveTolerance: LIVE_TOLERANCE if (player.liveTracker.atLiveEdge()) { }, fill: true, // Unlock backward buttons when seeked to muted: mutedlive edge }); console.log("Using VideoJs " + videojs.VERSION toggleBackButtons(true); if (Browser.isSafariWebRTC() && Browser.isiOS()) { // iOSStop hacklive whenUI usingstartup standardtimer controls to leave fullscreen mode let videoTag = getActualVideoTagstopLiveUITimer(); if(videoTag) { } } setWebkitFullscreenHandlers(videoTag, falseinitQualityLevels(player); } displayQualitySwitch(); }); } ... return videoJsPlayer;} } |
5. Формирование URL HLS-потока
encodeURIComponent10. Включение интерфейса перемотки (DVR) для первого подписчика
player.liveTracker.seekToLiveEdge() code
Если указаны ключ и токен авторизации, они будут включены в URL потока
| Code Block | ||||||
|---|---|---|---|---|---|---|
| const getVideoSrc = function(src) {
let videoSrc = src;
if (validateForm())||||||
const liveUIDisplay = function() { let streamName = getValue('playStream');stopLiveUITimer() if (player && streamName = encodeURIComponent(streamName);player.liveTracker) { videoSrcliveUITimer = getValue("urlServer") + '/' + streamName + '/' + streamName + '.m3u8'; setInterval(function() { let key = getValue('key'); if (!player.liveTracker.isLive() && player.liveTracker.liveWindow() > LIVE_THRESHOLD) { let token = getValue("token"); if (key.length > 0 && token.length > 0) { // Live UI is not displayed yet, seek to live edge to display videoSrc += "?" + key + "=" + token player.liveTracker.seekToLiveEdge(); } } setValue("fullLink"}, videoSrcLIVE_UI_INTERVAL); return videoSrc;} } |
6. Запуск плеера11. Получение списка доступных качеств
player.on(), player.playqualityLevels() code
| Code Block | ||||
|---|---|---|---|---|
| ||||
const playBtnClickinitQualityLevels = function(player) { let videoSrc = getVideoSrc(getValue("fullLink")); if (videoSrcplayer && !qualityLevels.length) { let playerQualityLevels = player.on('loadedmetadata', function() {qualityLevels(); if (playerQualityLevels) { console.log("Play with VideoJs"); let qualityDiv = playerdocument.playgetElementById("qualityBtns"); let })qualityLevel; ... for (let i player.src({ = 0; i < playerQualityLevels.length; i++) { src: videoSrc, qualityLevel = QualityLevel(playerQualityLevels, type: "application/vnd.apple.mpegurl"playerQualityLevels[i].height, i, qualityDiv); }); onStartedqualityLevels.push(qualityLevel); } } |
7. Обработка события playing
player.on() code
| Code Block | ||||
|---|---|---|---|---|
| ||||
const playBtnClick = function() { let videoSrc = getVideoSrc(getValue("fullLink")); if (videoSrc } if (qualityLevels.length) { ... qualityLevel = player.on('playing', function() { QualityLevel(playerQualityLevels, QUALITY_AUTO, -1, qualityDiv); consolequalityLevels.log("playing event fired"push(qualityLevel); displayPermalink(videoSrc); } if (player.liveTracker) { } } } |
12. Действия по нажатию на кнопку перемотки назад
player.seekable(), player.currentTime() code
| Code Block | ||||
|---|---|---|---|---|
| ||||
const backBtnClick = function(event) { if (player != null && player.liveTracker.isLive()) { toggleBackButtons(false); let seekable // A cratch to display live UI for the first subscriber= player.seekable(); let backTime liveUIDisplay(); = -1; if (event.target.id.indexOf("10") !== -1) }{ backTime = if (player.liveTracker.atLiveEdgecurrentTime()) - {10; } else if (event.target.id.indexOf("30") !== -1) { // Unlock backward buttonsbackTime when seeked to live edge= player.currentTime() - 30; } if (backTime < toggleBackButtons(true);0) { backTime = seekable ? seekable.start(0) : player.currentTime(); // Stop live UI startup timer} player.currentTime(backTime); } } |
13. Действия по нажатию на кнопку Live
player.liveTracker.seekToLiveEdge() code
| Code Block | ||||
|---|---|---|---|---|
| ||||
const liveBtnClick stopLiveUITimer= function(); } { if (player != null && }player.liveTracker) { }player.liveTracker.seekToLiveEdge(); ...toggleBackButtons(true); } } |
8. Включение интерфейса перемотки (DVR) для первого подписчика
...
14. Действия по нажатию на кнопку выбора качества
player.qualityLevels().selectedIndex_, player.qualityLevels().trigger() code
| Code Block | ||||
|---|---|---|---|---|
| ||||
const liveUIDisplayqualityBtnClick = function(button, playerQualityLevels, index) { stopLiveUITimer()if (playerQualityLevels && playerQualityLevels.length) { if (player && player.liveTracker) { liveUITimer = setInterval(function( let currentIndex = playerQualityLevels.selectedIndex_; for (let i = 0; i < playerQualityLevels.length; i++) { if (!player.liveTracker.isLive() && player.liveTracker.liveWindow() > LIVE_THRESHOLD) { let qualityLevel = playerQualityLevels[i]; if (index === -1 || i === index) //{ Live UI is not displayed yet, seek to live edge to display qualityLevel.enabled = true; player.liveTracker.seekToLiveEdge(); } else if (i === index) { } }, LIVE_UI_INTERVAL) } } |
9. Действия по нажатию на кнопку перемотки назад
player.seekable(), player.currentTime() code
| Code Block | ||||
|---|---|---|---|---|
| ||||
const backBtnClick = function(event) {qualityLevel.enabled = true; if (player != null && player.liveTracker) { currentIndex = toggleBackButtons(false)index; let seekable} = player.seekable();else { let backTime qualityLevel.enabled = -1false; if (event.target.id.indexOf("10") !== -1) { } } backTime = player.currentTime() - 10playerQualityLevels.selectedIndex_ = currentIndex; } else if (event.target.id.indexOf("30") !== -1) {playerQualityLevels.trigger({ type: 'change', selectedIndex: currentIndex }); } backTimebutton.style.color = player.currentTime() - 30QUALITY_COLORS.SELECTED; qualityLevels.forEach(item => }{ if (backTime < 0item.button.id !== button.id) { backTimeitem.button.style.color = seekable ? seekable.start(0) : player.currentTime();QUALITY_COLORS.AVAILABLE } player.currentTime(backTime}); } } |
10. Действия по нажатию на кнопку Live15. Остановка воспроизведения
player.liveTracker.seekToLiveEdgedispose() code
Этот метод удаляет со страницы div элемент, в котором был ранее инициализирован плеер
| Code Block | ||||
|---|---|---|---|---|
| ||||
const liveBtnClickstopBtnClick = function() { if (player != null && player.liveTracker) {) { console.log("Stop VideoJS player"); player.liveTracker.seekToLiveEdgestopLiveUITimer(); toggleBackButtonsplayer.dispose(true); } onStopped(); } |
11. Остановка воспроизведения
player.dispose() code
Этот метод удаляет со страницы div элемент, в котором был ранее инициализирован плеер
16. Очистка списка качеств после остановки плеера
| Code Block | ||||
|---|---|---|---|---|
| ||||
const stopBtnClickdisposeQualityLevels = function() { if (player != null)qualityLevels.forEach(level => { if console.log("Stop VideoJS player"); (level.button) { stopLiveUITimerlevel.button.remove(); player.dispose();} }); qualityLevels = onStopped()[]; } |
1217. Создание нового div элемента и плеера в нем после остановки предыдущего плеера
| Code Block | ||||
|---|---|---|---|---|
| ||||
const createRemoteVideo = function(parent) {
let remoteVideo = document.createElement("video");
remoteVideo.id = "remoteVideo";
remoteVideo.controls="controls";
remoteVideo.autoplay="autoplay";
remoteVideo.type="application/vnd.apple.mpegurl";
remoteVideo.className = "video-js vjs-default-skin";
remoteVideo.setAttribute("playsinline","");
remoteVideo.setAttribute("webkit-playsinline","");
parent.appendChild(remoteVideo);
player = initVideoJsPlayer(remoteVideo, autoplay);
} |
1318. Получение доступной статистики воспроизведения из HTML5 video элемента
HTML5Stats code
| Code Block | ||||
|---|---|---|---|---|
| ||||
const PlaybackStats = function(interval) {
const playbackStats = {
interval: interval || STATS_INTERVAL,
timer: null,
stats: null,
start: function() {
let video = getActualVideoTag();
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;
} |