Versions Compared

Key

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

...

The example allows to choose VideoJS version to play. VideoJS 8 supports Low Latency HLS if server allows to play this format

Image Modified

The code of the example

The source code can be accessed on server by the following path:

/usr/local/FlashphonerWebCallServer/client2/examples/demo/streaming/hls-player

hls-player.css - player page styles file
hls-player.html - player page
hls-player.js - player launch script
player-page.html - common player page elements for three HLS playback examples

The videojs7 and videojs8 subfolders contain two VideoJS versions respectively:

video.js - the player library script (http://videojs.com/, Apache License Version 2.0)
video.min.js - the player library script (minimized)
video-js.css - the player library styles file

The example can be tested using the fiollowing URL:

https://hostSince build 2.0.244, the example supports the following parameters:

  • version - VideoJS version to use: videojs7 or videojs8 
  • src - stream full HLS URL to play, should be encoded with URI, for example https%3A%2F%2Ftest1.flashphoner.com%3A8445%2Ftest%2Ftest.m3u8 
  • autoplay - automatically play the HLS URL, in this case all the input fields and buttons are hidden: false (by default) or true 

The player URL example with parameters as displayed on the screenshot above (the link is available in Permalink field)

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

The player URL example with autoplay enabled

Code Block
themeRDark
https://test1.flashphoner.com:8444/client2/examples/demo/streaming/hls-player/hls-player.html

...

Where host is WCS server address

Analyzing the code

To analyze the code get hls-player.js file version with hash 32144d9, which is available here and can be downloaded in build 2.0.243.

1. Loading the player page

code

Code Block
languagejs
themeRDark
const loadPlayerPage = function() {
    if (videojsVersion) {
?version=videojs7&src=https%3A%2F%2Ftest1.flashphoner.com%3A8445%2Ftest%2Ftest.m3u8&autoplay=true

In this case the stream should be played automatically with audio muted. A viewer should use a loud slider in the player interface to unmute audio.

The code of the example

The source code can be accessed on server by the following path:

/usr/local/FlashphonerWebCallServer/client2/examples/demo/streaming/hls-player

hls-player.css - player page styles file
hls-player.html - player page
hls-player.js - player launch script
player-page.html - common player page elements for three HLS playback examples

The videojs7 and videojs8 subfolders contain two VideoJS versions respectively:

video.js - the player library script (http://videojs.com/, Apache License Version 2.0)
video.min.js - the player library script (minimized)
video-js.css - the player library styles file

The example can be tested using the fiollowing URL:

https://host:8444/client2/examples/demo/streaming/hls-player/hls-player.html

Where host is WCS server address

Analyzing the code

To analyze the code get hls-player.js file version with hash 1703e13, which is available here and can be downloaded in build 2.0.244.

1. Loading the player page

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;
                    hideItem("videojsInputForm")break;
         loadVideoJS("videojs"  + videojsVersion);
}
           } else { option.text = videojsFolder;
        let videojsInput    option.value = document.getElementById("videojsInput")videojsFolder;
        for  (videojsType in VIDEOJS_VERSION_TYPE) {
 videojsInput.appendChild(option);
        }

     let option = document.createElementsetHandler("option"videojsBtn", "click", onVideojsBtnClick);
            let videojsFolder = "";
  }
}

2. Loading the VideoJS chosen version

code

Code Block
languagejs
themeRDark
const loadVideoJS = function (version) {
    if      switch (videojsType(version) {
        let playerPage       case 'VIDEOJS7':= document.getElementById("playerPage");
        loadFile(version +           videojsFolder = VIDEOJS_VERSION_TYPE.VIDEOJS7;"/video.js", "text/javascript").then( data  => {
            console.log("HLS library loaded successfully", data);
    break;
        loadFile(version + "/video-js.css", "stylesheet").then ( data => {
 case 'VIDEOJS8':
              console.log("HLS library stylesheet loaded successfully", data);
 videojsFolder = VIDEOJS_VERSION_TYPE.VIDEOJS8;
             hideItem("videojsInputForm");
       break;
         loadPage("player-page.html", "playerPage", initPage });
            option.text}).catch( err => videojsFolder;{
            option.value = videojsFolder;
  playerPage.innerHTML          videojsInput.appendChild(option)= "Can't load VideoJS library stylesheet";
        }

        setHandlerplayerPage.setAttribute("videojsBtnclass", "click", onVideojsBtnClicktext-danger");
    }
}

2. Loading the VideoJS chosen version

code

Code Block
languagejs
themeRDark
const loadVideoJS = function (version) {
           if console.error(versionerr) {;
         let playerPage = document.getElementById("playerPage"); })
        loadFile(version + "/video.js", "text/javascript"}).thencatch( dataerr  => {
            console.log("HLS library loaded successfully", data);
setText("videojsError", "Can't load VideoJS library");
            console.error(err);
     loadFile(version + "/video-js.css", "stylesheet").then ( data => {
     });
    }
}

3. The player HTML page initializing

code

Code Block
languagejs
themeRDark
const initPage = function() {
    if (playSrc)  {
     console.log("HLS library stylesheet loaded successfullysetValue("fullLink", datadecodeURIComponent(playSrc));
    } else if (autoplay) {
        hideItemconsole.warn("videojsInputFormNo HLS URL set, autoplay disabled");
        autoplay = false;
      loadPage("player-page.html", "playerPage", initPage );
    }
    let remoteVideo = document.getElementById('remoteVideo');
        }).catch( err => if (autoplay) {
        // There should not be any visible item playerPage.innerHTMLon = "Can't load VideoJS library stylesheet";the page unless player
        hideAllToAutoplay();
        playerPage.setAttribute("class", "text-danger");
        // The player should use all available page width
        console.errorsetUpPlayerItem(errtrue);
        // The player  })
    should be muted to automatically start playback
    }).catch( err => {
 player = initVideoJsPlayer(remoteVideo, true);
        setText("videojsError", "Can't load VideoJS library"playBtnClick();
    } else {
       console.error(err);
        });
    }
}

3. The player HTML page initializing

code

Code Block
languagejs
themeRDark
const initPage = function() {
 // No autoplay, all the forms and buttons should be visible
        setText("header", "HLS VideoJS Player Minimal");
    setValue("urlServer", getHLSUrl());
        enableItemdisplayCommonItems("applyBtn");
    setText("applyBtn", "Play"    setUpButtons();
    setHandler("applyBtn", "click", playBtnClick    enablePlaybackStats();
    setHandler("backBtn10", "click", backBtnClick);
    setHandler("backBtn30", "click", backBtnClick);    // The player should have a maximum fixed size
    setHandler("backBtnMax", "click", backBtnClick    setUpPlayerItem(false);
    setHandler("liveBtn", "click", liveBtnClick);
  // The letplayer remoteVideocan = document.getElementById('remoteVideo');
    remoteVideo.className = "video-js vjs-default-skin";be unmuted because user should click Play button
        player = initVideoJsPlayer(remoteVideo, false);
    playbackStats = PlaybackStats(STATS_INTERVAL);}
}

4. Player initializing

videojs() code

The following parameters are passed to the player:

...

const initVideoJsPlayer = function(video) {
Code Block
languagejs
themeRDark
RDark
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
            },
            fill: }true,
          fill  muted: truemuted
        });
        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, falselet videoTag = getActualVideoTag();
        }
    }
    return videoJsPlayer;
}

5. Stream name detection (the stream should be published to server)

encodeURIComponent() code

Code Block
languagejs
themeRDark
function playBtnClick( if(videoTag) {
      if (validateForm()) {
        let streamName = getValue('playStream');
        streamName = encodeURIComponent(streamName);setWebkitFullscreenHandlers(videoTag, false);
            }
        ...}
    }
    return videoJsPlayer;
}

65. HLS stream URL forming and player launchingplayer.play

encodeURIComponent() code

If authentication key and token are set, they will be included to stream URL

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

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();
    }
}

7. playing event handler

player.on() code

Code Block
languagejs
themeRDark
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
languagejs
themeRDark
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
languagejs
themeRDark
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
languagejs
themeRDark
const liveBtnClick = function() {
    if (player != null && player.liveTracker) {
        player.liveTracker.seekToLiveEdge();
        toggleBackButtons(true);
    }
}

11. Playback stopping

player.dispose() code

This method removes the div container tag where player was initialized from the page

...

12. New div container tag creation after previous player was removed

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);
}

13. Getting an available playback statistics from HTML5 video tag

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;
}