Versions Compared

Key

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

...

Пример также позволяет выбрать версию 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
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%2Ftest.m3u8

Пример вызова плеера с автозапуском

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

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

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

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

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

Code Block
languagejs
themeRDark
const loadVideoJS = function (version) {
    if (version) {}
        let}
  playerPage = document.getElementById("playerPage");
 }
    return videoJsPlayer;
}

5. Формирование URL HLS-потока

encodeURIComponent() code

Если указаны ключ и токен авторизации, они будут включены в URL потока

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

Code Block
languagejs
themeRDark
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
languagejs
themeRDark
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
languagejs
themeRDark
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
languagejs
themeRDark
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
languagejs
themeRDark
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
languagejs
themeRDark
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
languagejs
themeRDark
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
languagejs
themeRDark
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
languagejs
themeRDark
const stopBtnClick = liveUIDisplayfunction();
 {
    if (player != null) {
        console.log("Stop VideoJS }player");
        stopLiveUITimer();
        if (player.liveTracker.atLiveEdgedispose()) {;
    }
         onStopped();
}

12. Создание нового div элемента и плеера в нем после остановки предыдущего плеера

code

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

10. Действия по нажатию на кнопку Live

player.liveTracker.seekToLiveEdge() code

Code Block
languagejs
themeRDark
const liveBtnClick = function() {
    if (player != null && player.liveTracker) {
            let fps = stats.getFps();

                player.liveTracker.seekToLiveEdge(setText("videoWidth", width);
        toggleBackButtons(true);
    }
}

11. Остановка воспроизведения

player.dispose() code

Этот метод удаляет со страницы div элемент, в котором был ранее инициализирован плеер

Code Block
languagejs
themeRDark
const stopBtnClick = function() {
        setText("videoHeight", height);

                if (playerbitrate !== nullundefined) {
                    setText("videoRate", Math.round(bitrate));
      console.log("Stop VideoJS player");
        stopLiveUITimer();}
        player.dispose();
     }
   if onStopped();
}

12. Создание нового div элемента и плеера в нем после остановки предыдущего плеера

code

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