...
Пример также позволяет выбрать версию VideoJS для проигрывания. Версия 8, в отличие от версии 7, поддерживает Low Latency HLS, если сервер отдает поток в этом формате
Код примера
Код данного примера находится на сервере по следующему пути:
/usr/local/FlashphonerWebCallServer/client2/examples/demo/streaming/hls-player
hls-player.css - файл стилей страницы с плеером
hls-player.html - страница с плеером
hls-player.js - скрипт, обеспечивающий запуск плеера
player-page.html - общие элементы страницы плеера для трех примеров воспроизведения HLS
В подкаталогах videojs7 и videojs8 находятся, соответственно, две версии VideoJS:
video.js - скрипт, обеспечивающий работу плеера (http://videojs.com/, Apache License Version 2.0)
video.min.js - скрипт, обеспечивающий работу плеера (минимизированная версия)
video-js.css - файл стилей HLS-плеера
Тестировать данный пример можно по следующему адресу:
https://hostНачиная со сборки 2.0.244, пример поддерживает следующие параметры:
- version - используемая версия VideoJS:
videojs7
илиvideojs8
- src - полный HLS URL потока для проигрывания, должен быть закодирован при помощи URI encode, например
https%3A%2F%2Ftest1.flashphoner.com%3A8445%2Ftest%2Ftest.m3u8
- autoplay - автоматически запустить проигрывание указанного HLS URL, при этом все поля ввода и кнопки скрываются:
false
(по умолчанию) илиtrue
Пример открытия плеера с параметрами, как на скриншоте выше (ссылка в поле Permalink
)
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%2Ftest.m3u8 |
Пример вызова плеера с автозапуском
Code Block | ||
---|---|---|
| ||
https://test1.flashphoner.com:8444/client2/examples/demo/streaming/hls-player/hls-player.html |
...
Здесь host - адрес вашего WCS-сервера.
Работа с кодом примера
Для разбора кода возьмем версию файла hls-player.js с хешем 32144d9, которая находится здесь и доступна для скачивания в соответствующей сборке 2.0.243.
1. Загрузка страницы плеера
Code Block | ||||
---|---|---|---|---|
| ||||
const loadPlayerPage = function() { if (videojsVersion) { hideItem("videojsInputForm"); loadVideoJS("videojs" + videojsVersion); } else { let videojsInput = document.getElementById("videojsInput"); ?version=videojs7&src=https%3A%2F%2Ftest1.flashphoner.com%3A8445%2Ftest%2Ftest.m3u8&autoplay=true |
При этом воспроизведение потока будет запущено автоматически, с отключенным звуком. Для включения звука зритель должен использовать кнопку регулятора громкости в интерфейсе плеера.
Код примера
Код данного примера находится на сервере по следующему пути:
/usr/local/FlashphonerWebCallServer/client2/examples/demo/streaming/hls-player
hls-player.css - файл стилей страницы с плеером
hls-player.html - страница с плеером
hls-player.js - скрипт, обеспечивающий запуск плеера
player-page.html - общие элементы страницы плеера для трех примеров воспроизведения HLS
В подкаталогах videojs7 и videojs8 находятся, соответственно, две версии VideoJS:
video.js - скрипт, обеспечивающий работу плеера (http://videojs.com/, Apache License Version 2.0)
video.min.js - скрипт, обеспечивающий работу плеера (минимизированная версия)
video-js.css - файл стилей HLS-плеера
Тестировать данный пример можно по следующему адресу:
https://host:8444/client2/examples/demo/streaming/hls-player/hls-player.html
Здесь host - адрес вашего WCS-сервера.
Работа с кодом примера
Для разбора кода возьмем версию файла hls-player.js с хешем 1703e13, которая находится здесь и доступна для скачивания в соответствующей сборке 2.0.244.
1. Загрузка страницы плеера
Code Block | ||||
---|---|---|---|---|
| ||||
const loadPlayerPage = function() {
if (videojsVersion) {
hideItem("videojsInputForm");
loadVideoJS("videojs" + videojsVersion);
} else {
let videojsInput = document.getElementById("videojsInput");
for (videojsType in VIDEOJS_VERSION_TYPE) {
let option = document.createElement("option");
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) {
let playerPage = document.getElementById("playerPage");
loadFile(version + "/video.js", "text/javascript").then( data => {
console.log("HLS library loaded successfully", data);
loadFile(version + "/video-js.css", "stylesheet").then ( data => {
console.log("HLS library stylesheet loaded successfully", data);
hideItem("videojsInputForm");
loadPage("player-page.html", "playerPage", initPage );
}).catch( err => {
playerPage.innerHTML = "Can't load VideoJS library stylesheet";
playerPage.setAttribute("class", "text-danger");
console.error(err);
})
}).catch( err => {
setText("videojsError", "Can't load VideoJS library");
console.error(err);
});
}
} |
3. Инициализация HTML-страницы плеера
Code Block | ||||
---|---|---|---|---|
| ||||
const initPage = function() {
if (playSrc) {
setValue("fullLink", decodeURIComponent(playSrc));
} else if (autoplay) {
console.warn("No HLS URL set, autoplay disabled");
autoplay = false;
}
let remoteVideo = document.getElementById('remoteVideo');
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
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);
}
} |
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, { for (videojsType in VIDEOJS_VERSION_TYPE) { playsinline: true, playbackRates: let option = document.createElement("option"); [0.1, 0.25, 0.5, 1, 1.5, 2], let videojsFolder = "";liveui: true, switch (videojsType) liveTracker: { case 'VIDEOJS7':trackingThreshold: LIVE_THRESHOLD, videojsFolder = VIDEOJS_VERSION_TYPE.VIDEOJS7;liveTolerance: LIVE_TOLERANCE break;}, case 'VIDEOJS8':fill: true, muted: muted videojsFolder = VIDEOJS_VERSION_TYPE.VIDEOJS8 }); console.log("Using VideoJs " + videojs.VERSION); break; if (Browser.isSafariWebRTC() && Browser.isiOS()) { } // iOS hack when using standard controls to option.textleave =fullscreen videojsFolder;mode option.valuelet videoTag = videojsFoldergetActualVideoTag(); videojsInput.appendChildif(optionvideoTag); { } setHandlersetWebkitFullscreenHandlers("videojsBtn"videoTag, "click", onVideojsBtnClickfalse); } } |
2. Загрузка выбранной версии VideoJS
Code Block | ||||
---|---|---|---|---|
| ||||
const loadVideoJS = function (version) { if (version) {} let} playerPage = document.getElementById("playerPage"); } return videoJsPlayer; } |
5. Формирование URL HLS-потока
encodeURIComponent() code
Если указаны ключ и токен авторизации, они будут включены в URL потока
Code Block | ||||
---|---|---|---|---|
| ||||
const getVideoSrc = loadFile(version + "/video.js", "text/javascript").then( data => function(src) { let videoSrc = src; if console.log("HLS library loaded successfully", data); (validateForm()) { let streamName = getValue('playStream'); loadFile(version + "/video-js.css", "stylesheet").then ( data => { streamName = encodeURIComponent(streamName); videoSrc = getValue("urlServer") + '/' + streamName console.log("HLS library stylesheet loaded successfully", data)+ '/' + streamName + '.m3u8'; let key hideItem("videojsInputForm"= getValue('key'); let token loadPage("player-page.html", "playerPage", initPage = getValue("token"); if (key.length > 0 && })token.catch(length err> =>0) { playerPage.innerHTML videoSrc += "Can't load VideoJS library stylesheet"?" + key + "=" + token; } } playerPage.setAttributesetValue("classfullLink", "text-danger"videoSrc); return videoSrc; } |
6. Запуск плеера
player.on(), player.play() code
Code Block | ||||
---|---|---|---|---|
| ||||
const playBtnClick = function() { let videoSrc console.error(err= getVideoSrc(getValue("fullLink")); if })(videoSrc) { })player.catchon( err => 'loadedmetadata', function() { setText("videojsError", "Can't load VideoJS libraryconsole.log("Play with VideoJs"); consoleplayer.errorplay(err); }); } } |
3. Инициализация HTML-страницы плеера
Code Block | ||||
---|---|---|---|---|
| ||||
const initPage = function() ... player.src({ setText("header", "HLS VideoJS Player Minimal"); src: videoSrc, setValue("urlServer", getHLSUrl()); enableItem("applyBtn"); setText("applyBtn", "Play"); type: "application/vnd.apple.mpegurl" setHandler("applyBtn", "click", playBtnClick}); setHandler("backBtn10", "click", backBtnClick onStarted(); setHandler("backBtn30", "click", backBtnClick); setHandler("backBtnMax", "click", backBtnClick); setHandler("liveBtn", "click", liveBtnClick);} } |
7. Обработка события playing
player.on() code
Code Block | ||||
---|---|---|---|---|
| ||||
const playBtnClick = function() { let videoSrc = getVideoSrc(getValue("fullLink")); if (videoSrc) { ... let remoteVideo = documentplayer.getElementByIdon('remoteVideoplaying', function(); remoteVideo.className = "video-js vjs-default-skin"; { player = initVideoJsPlayer(remoteVideoconsole.log("playing event fired"); playbackStats = PlaybackStats displayPermalink(STATS_INTERVAL); } |
4. Инициализация плеера
videojs() code
Плееру передаются следующие параметры:
- video - div-элемент, в котором должен быть проигран поток
- playsinline: true - проигрывать видео на странице, не переключаясь в полноэкранный режим (игнорируется в iOS Safari)
- playbackRates - список скоростей воспроизведения
- liveui: true - включает интерфейс для перемотки (DVR)
- liveTracker - настройка границы проигрывания живого потока
Code Block | ||||
---|---|---|---|---|
| ||||
const initVideoJsPlayer = function(video) { let videoJsPlayer = videojs(video, { videoSrc); if (player.liveTracker) { if (!player.liveTracker.isLive()) { // A cratch to display live UI for the first subscriber playsinline: true, liveUIDisplay(); playbackRates: [0.1, 0.25, 0.5, 1, 1.5, 2], } liveui: true, if liveTracker:(player.liveTracker.atLiveEdge()) { trackingThreshold: LIVE_THRESHOLD, // Unlock backward buttons when seeked to liveTolerance: LIVE_TOLERANCElive edge }, fill: true }toggleBackButtons(true); console.log("Using VideoJs " + videojs.VERSION); if (Browser.isSafariWebRTC() && Browser.isiOS()) { // iOS hack when using standard controls to leave fullscreen mode Stop live UI startup timer let videoTag = getActualVideoTagstopLiveUITimer(); if(videoTag) {} setWebkitFullscreenHandlers(videoTag, false);} }); } ... return videoJsPlayer;} } |
5. Определение имени потока (должен быть опубликован на сервере)
encodeURIComponent8. Включение интерфейса перемотки (DVR) для первого подписчика
player.liveTracker.seekToLiveEdge() code
Code Block | ||||
---|---|---|---|---|
| ||||
function playBtnClickconst liveUIDisplay = function() { if (validateFormstopLiveUITimer()) { letif streamName(player = getValue('playStream');&& player.liveTracker) { streamNameliveUITimer = encodeURIComponentsetInterval(function(streamName); { ... } } |
6. Формирование URL HLS-потока и запуск плеера
player.play() code
Если указаны ключ и токен авторизации, они будут включены в URL потока
Code Block | ||||
---|---|---|---|---|
| ||||
const playBtnClick = function() { if (validateForm()) { ... let videoSrc = getValue("urlServer") + '/' + streamName + '/' + streamName + '.m3u8'if (!player.liveTracker.isLive() && player.liveTracker.liveWindow() > LIVE_THRESHOLD) { // Live UI is not displayed yet, seek to live edge to display player.liveTracker.seekToLiveEdge(); let key = getValue('key'); } let token = getValue("token"); if (key.length > 0 && token.length > 0}, LIVE_UI_INTERVAL) } } |
9. Действия по нажатию на кнопку перемотки назад
player.seekable(), player.currentTime() code
Code Block | ||||
---|---|---|---|---|
| ||||
const backBtnClick = function(event) { if videoSrc += "?" + key + "=" + token; }(player != null && player.liveTracker) { player.on('loadedmetadata', function() {toggleBackButtons(false); let seekable = consoleplayer.log("Play with VideoJs"seekable(); let backTime player.play()= -1; });if (event.target.id.indexOf("10") !== -1) { ... backTime = player.src({currentTime() - 10; } else if (event.target.id.indexOf("30") src: videoSrc,!== -1) { type: "application/vnd.apple.mpegurl"backTime = player.currentTime() - 30; }); onStarted(); } } |
7. Обработка события playing
player.on() code
Code Block | ||||
---|---|---|---|---|
| ||||
const playBtnClick = function(if (backTime < 0) { if (validateForm()) { backTime = ... seekable player.on('playing', function() { ? seekable.start(0) : player.currentTime(); } consoleplayer.log("playing event fired"currentTime(backTime); if (player.liveTracker} } |
10. Действия по нажатию на кнопку Live
player.liveTracker.seekToLiveEdge() code
Code Block | ||||
---|---|---|---|---|
| ||||
const liveBtnClick = function() { if (player != null if (!&& player.liveTracker.isLive()) { // A cratch to display live UI for the first subscriber player.liveTracker.seekToLiveEdge(); toggleBackButtons(true); } } |
11. Остановка воспроизведения
player.dispose() code
Этот метод удаляет со страницы div элемент, в котором был ранее инициализирован плеер
Code Block | ||||
---|---|---|---|---|
| ||||
const stopBtnClick = liveUIDisplayfunction(); { if (player != null) { console.log("Stop VideoJS }player"); stopLiveUITimer(); if (player.liveTracker.atLiveEdgedispose()) {; } onStopped(); } |
12. Создание нового div элемента и плеера в нем после остановки предыдущего плеера
Code Block | ||||
---|---|---|---|---|
| ||||
const createRemoteVideo = function(parent) { let remoteVideo // Unlock backward buttons when seeked to live edge= document.createElement("video"); remoteVideo.id = "remoteVideo"; remoteVideo.controls="controls"; toggleBackButtons(true)remoteVideo.autoplay="autoplay"; remoteVideo.type="application/vnd.apple.mpegurl"; remoteVideo.className = "video-js vjs-default-skin"; remoteVideo.setAttribute("playsinline",""); // Stop live UI startup timerremoteVideo.setAttribute("webkit-playsinline",""); parent.appendChild(remoteVideo); player = initVideoJsPlayer(remoteVideo, autoplay); } |
13. Получение доступной статистики воспроизведения из HTML5 video элемента
HTML5Stats code
Code Block | ||||
---|---|---|---|---|
| ||||
const PlaybackStats = function(interval) { const playbackStats = stopLiveUITimer(); { interval: interval || STATS_INTERVAL, } timer: null, stats: }null, }); start: function() { ... } } |
8. Включение интерфейса перемотки (DVR) для первого подписчика
player.liveTracker.seekToLiveEdge() code
Code Block | ||||
---|---|---|---|---|
| ||||
const liveUIDisplay let video = functiongetActualVideoTag(); { stopLiveUITimer() if (player && playerplaybackStats.liveTracker) { stop(); liveUITimerstats = setIntervalHTML5Stats(function(video); { playbackStats.timer if= setInterval(!player.liveTracker.isLive() && player.liveTracker.liveWindow() > LIVE_THRESHOLD) { playbackStats.displayStats, playbackStats.interval); setText("videoWidth", "N/A"); setText("videoHeight", // Live UI is not displayed yet, seek to live edge to display"N/A"); setText("videoRate", "N/A"); player.liveTracker.seekToLiveEdge(setText("videoFps", "N/A"); } }, LIVE_UI_INTERVAL) } } |
9. Действия по нажатию на кнопку перемотки назад
player.seekable(), player.currentTime() code
Code Block | ||||
---|---|---|---|---|
| ||||
const backBtnClick = function(eventshowItem("stats"); }, stop: function() { if (player != null && player.liveTracker if (playbackStats.timer) { toggleBackButtons(false clearInterval(playbackStats.timer); let seekable playbackStats.timer = player.seekable()null; let backTime = -1;} if (event.target.id.indexOf("10") !== -1) { playbackStats.stats = null; backTime = player.currentTime() - 10hideItem("stats"); }, else if (event.target.id.indexOf("30") !== -1 displayStats: function() { backTime = player.currentTime() - 30;if (stats.collect()) { } iflet (backTimewidth < 0) { = stats.getWidth(); backTime = seekable ? seekable.start(0) : player.currentTimelet height = stats.getHeight(); } let bitrate = playerstats.currentTimegetBitrate(backTime); } } |
11. Действия по нажатию на кнопку Live
player.liveTracker.seekToLiveEdge() code
Code Block | ||||
---|---|---|---|---|
| ||||
const liveBtnClick = function() { if (player != null && player.liveTracker) { let fps = stats.getFps(); player.liveTracker.seekToLiveEdge(setText("videoWidth", width); toggleBackButtons(true); } } |
12. Остановка воспроизведения
player.dispose() code
Этот метод удаляет со страницы div элемент, в котором был ранее инициализирован плеер
Code Block | ||||
---|---|---|---|---|
| ||||
const stopBtnClick = function() { setText("videoHeight", height); if (playerbitrate !== nullundefined) { setText("videoRate", Math.round(bitrate)); console.log("Stop VideoJS player"); stopLiveUITimer();} player.dispose(); } if onStopped(); } |
13. Создание нового div элемента и плеера в нем после остановки предыдущего плеера
Code Block | ||||
---|---|---|---|---|
| ||||
const createRemoteVideo = function(parent(fps !== undefined) { let remoteVideo = document.createElement("video"); remoteVideo.id = setText("remoteVideo"videoFps", fps.toFixed(1)); 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)return playbackStats; } |