...
Пример также позволяет выбрать версию 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) { ?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; hideItem("videojsInputForm") break; loadVideoJS("videojs" + videojsVersion); } else {case 'VIDEOJS8': let videojsInput = document.getElementById("videojsInput"); for (videojsTypevideojsFolder in= VIDEOJS_VERSION_TYPE) { .VIDEOJS8; let option = document.createElement("option"); break; let videojsFolder = "";} switch (videojsType) {option.text = videojsFolder; option.value case 'VIDEOJS7':= videojsFolder; videojsInput.appendChild(option); videojsFolder} = VIDEOJS_VERSION_TYPE.VIDEOJS7; setHandler("videojsBtn", "click", onVideojsBtnClick); break;} } |
2. Загрузка выбранной версии VideoJS
Code Block | ||||
---|---|---|---|---|
| ||||
const loadVideoJS = function (version) { if (version) { let playerPage case 'VIDEOJS8':= document.getElementById("playerPage"); loadFile(version + "/video.js", videojsFolder = VIDEOJS_VERSION_TYPE.VIDEOJS8;"text/javascript").then( data => { console.log("HLS library loaded successfully", data); break; loadFile(version + "/video-js.css", }"stylesheet").then ( data => { option.text = videojsFolder; console.log("HLS library stylesheet loaded successfully", data); option.value = videojsFolder; videojsInput.appendChild(optionhideItem("videojsInputForm"); } setHandlerloadPage("videojsBtnplayer-page.html", "clickplayerPage", onVideojsBtnClickinitPage ); } } |
2. Загрузка выбранной версии VideoJS
Code Block | ||||
---|---|---|---|---|
| ||||
const loadVideoJS = function (version) { }).catch( err => { if (version) { let playerPage.innerHTML = document.getElementById("playerPage")"Can't load VideoJS library stylesheet"; loadFile(version + "/video.js playerPage.setAttribute("class", "text/javascript-danger").then( data => {; console.log("HLS library loaded successfully", data console.error(err); loadFile(version + "/video-js.css", "stylesheet").then ( data}) }).catch( err => { setText("videojsError", "Can't load VideoJS console.log("HLS library stylesheet loaded successfully", datalibrary"); console.error(err); }); } } |
3. Инициализация HTML-страницы плеера
Code Block | ||||
---|---|---|---|---|
| ||||
const initPage = hideItemfunction("videojsInputForm"); { if (playSrc) { loadPagesetValue("player-page.htmlfullLink", "playerPage", initPage decodeURIComponent(playSrc)); } else if (autoplay) { }).catch( err => { console.warn("No HLS URL set, autoplay disabled"); autoplay = false; } playerPage.innerHTML = "Can't load VideoJS library stylesheet"let remoteVideo = document.getElementById('remoteVideo'); if (autoplay) { // There should playerPage.setAttribute("class", "text-danger"); not be any visible item on the page unless player console.errorhideAllToAutoplay(err); // The player should use }) all available page width }).catch( err => { setUpPlayerItem(true); // The player should setText("videojsError", "Can't load VideoJS library");be muted to automatically start playback player = console.error(errinitVideoJsPlayer(remoteVideo, true); }playBtnClick(); } } |
3. Инициализация HTML-страницы плеера
Code Block | ||||
---|---|---|---|---|
| ||||
const initPage = function() { setText("header", "HLS VideoJS Player Minimal"); setValue("urlServer", getHLSUrl()); enableItem("applyBtn"); else { // No autoplay, all the forms and buttons should be visible setText("applyBtnheader", "Play"); HLS VideoJS setHandler("applyBtn", "click", playBtnClickPlayer Minimal"); setHandler("backBtn10", "click", backBtnClick displayCommonItems(); setHandler("backBtn30", "click", backBtnClick setUpButtons(); setHandler("backBtnMax", "click", backBtnClick enablePlaybackStats(); // The player setHandler("liveBtn", "click", liveBtnClick);should have a maximum fixed size let remoteVideo = document.getElementById('remoteVideo'setUpPlayerItem(false); remoteVideo.className = "video-js vjs-default-skin"; player = initVideoJsPlayer(remoteVideo); // The player can be unmuted because user should click Play button playbackStatsplayer = PlaybackStats(STATS_INTERVAL);initVideoJsPlayer(remoteVideo, false); } } |
4. Инициализация плеера
videojs() code
Плееру передаются следующие параметры:
...
Code Block | ||||
---|---|---|---|---|
| ||||
const initVideoJsPlayer = function(video) { const initVideoJsPlayer = function(video, muted) { let videoJsPlayer = null; if (video) { video.className = "video-js vjs-default-skin"; let 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 }, liveTolerancefill: LIVE_TOLERANCEtrue, }, muted: muted fill: true }); 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); } let videoTag = getActualVideoTag(); } return videoJsPlayer; } |
5. Определение имени потока (должен быть опубликован на сервере)
encodeURIComponent() code
Code Block | ||||
---|---|---|---|---|
| ||||
function playBtnClick() { if(videoTag) { if (validateForm()) { let streamName = getValue('playStream'); streamName = encodeURIComponent(streamName);setWebkitFullscreenHandlers(videoTag, false); } ...} } return videoJsPlayer; } |
65. Формирование URL HLS-потока и запуск плеераplayer.play
encodeURIComponent() code
Если указаны ключ и токен авторизации, они будут включены в URL потока
Code Block | ||||
---|---|---|---|---|
| ||||
const playBtnClickgetVideoSrc = function(src) { let videoSrc = src; if (validateForm()) { let streamName = getValue(validateForm()) { 'playStream'); streamName ...= encodeURIComponent(streamName); let videoSrc = getValue("urlServer") + '/' + streamName + '/' + streamName + '.m3u8'; let key = getValue('key'); let token = getValue("token"); if (key.length > 0 && token.length > 0) {length > 0) { videoSrc += "?" + key + "=" + token; } } setValue("fullLink", videoSrc); return videoSrc += "?" + key + "=" + token; } |
6. Запуск плеера
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(); } } |
7. Обработка события playing
player.on() code
Code Block | ||||
---|---|---|---|---|
| ||||
const playBtnClick = function() { let videoSrc if= getVideoSrc(validateFormgetValue("fullLink")) { ; if (videoSrc) { ... player.on('playing', function() { console.log("playing event fired"); displayPermalink(videoSrc); if (player.liveTracker) { if (!player.liveTracker.isLive()) { // A cratch to display live UI for the first subscriber liveUIDisplay(); } if (player.liveTracker.atLiveEdge()) { // Unlock backward buttons when seeked to live edge toggleBackButtons(true); // Stop live UI startup timer stopLiveUITimer(); } } }); ... } } |
...
player.liveTracker.seekToLiveEdge() code
Code Block | ||||
---|---|---|---|---|
| ||||
const liveUIDisplay = function() { stopLiveUITimer() if (player && player.liveTracker) { liveUITimer = setInterval(function() { if (!player.liveTracker.isLive() && player.liveTracker.liveWindow() > LIVE_THRESHOLD) { // Live UI is not displayed yet, seek to live edge to display player.liveTracker.seekToLiveEdge(); } }, LIVE_UI_INTERVAL) } } |
...
player.seekable(), player.currentTime() code
Code Block | ||||
---|---|---|---|---|
| ||||
const backBtnClick = function(event) { if (player != null && player.liveTracker) { toggleBackButtons(false); let seekable = player.seekable(); let backTime = -1; if (event.target.id.indexOf("10") !== -1) { backTime = player.currentTime() - 10; } else if (event.target.id.indexOf("30") !== -1) { backTime = player.currentTime() - 30; } if (backTime < 0) { backTime = seekable ? seekable.start(0) : player.currentTime(); } player.currentTime(backTime); } } |
...
player.liveTracker.seekToLiveEdge() code
Code Block | ||||
---|---|---|---|---|
| ||||
const liveBtnClick = function() { if (player != null && player.liveTracker) { player.liveTracker.seekToLiveEdge(); toggleBackButtons(true); } } |
11. Остановка воспроизведения
player.dispose() code
Этот метод удаляет со страницы div элемент, в котором был ранее инициализирован плеер
...
12. Создание нового 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);
} |
13. Получение доступной статистики воспроизведения из 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; } |