Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Code Block
themeRDark
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
themeRDark
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

Code Block
languagejs
themeRDark
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

Code Block
languagejs
themeRDark
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

Code Block
languagejs
themeRDark
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

Code Block
languagejs
themeRDark
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

Code Block
languagejs
themeRDark
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

Code Block
languagejs
themeRDark
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
languagejs
themeRDark
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
languagejs
themeRDark
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
languagejs
themeRDark
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
languagejs
themeRDark
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
languagejs
themeRDark
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 потока

const getVideoSrc = function(src) { let videoSrc = src; if (validateForm())
Code Block
languagejs
themeRDark
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
languagejs
themeRDark
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
languagejs
themeRDark
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
languagejs
themeRDark
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
languagejs
themeRDark
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
languagejs
themeRDark
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
languagejs
themeRDark
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
languagejs
themeRDark
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

Code Block
languagejs
themeRDark
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

Code Block
languagejs
themeRDark
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
languagejs
themeRDark
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;
}