Versions Compared

Key

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

Table of Contents

...

Overview

The player example shows how to convert stream published on WCS server to HLS and play it in browser using VideoJS library. HLS stream cut starts automatically when strea is requested by HLS URL, for example http`https://localhost:8082test1.flashphoner.com:8445/test/test.m3u8 on m3u8` on the screenshot belowImage Removed.

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

Image Added

Image Added

The code of the example

...

hls-player.css - player page styles file
video-js.css - HLS player 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)
videojs-hlsvideo.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:88888444/client2/examples/demo/streaming/hls-player/hls-player.html

Where host is WCS server address

Analyzing the code

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

1. A server HLS URL detectiongetHLSUrl() 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;
                    break;
            }
            option.text = videojsFolder;
            option.value = videojsFolder;
            videojsInput.appendChild(option);
        }

        setHandler("videojsBtn", "click", onVideojsBtnClick);
    }
}

2. Loading the VideoJS chosen version

code

Code Block
languagejs
themeRDark
function initPage(const loadVideoJS = function (version) {
    if (version) {
    $    let playerPage = document.getElementById("#headerplayerPage");
        loadFile(version + "/video.js", "text/javascript").then( data  => {
            console.log("HLS VideoJSlibrary Playerloaded Minimalsuccessfully", data);
    $("#urlServer").val(getHLSUrl());
    ...
}

2. Player initialization

videojs() code

...

        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. The player HTML page initializing

code

Code Block
languagejs
themeRDark
functionconst initPage = function() {
    setText("header", "HLS VideoJS Player Minimal");
    setValue("urlServer", getHLSUrl());
    enableItem("applyBtn");
    setText("applyBtn", "Play");
    setHandler("applyBtn", "click", playBtnClick);
    setHandler("backBtn10", "click", backBtnClick);
    setHandler("backBtn30", "click", backBtnClick);
    setHandler("backBtnMax",  ..."click", backBtnClick);
    setHandler("liveBtn", "click", liveBtnClick);
    varlet remoteVideo = document.getElementById('remoteVideo');
    remoteVideo.className = "video-js vjs-default-skin";
    player = initVideoJsPlayer(remoteVideo);
    playbackStats = PlaybackStats(STATS_INTERVAL);
}

4. Player initializing

videojs

...

3() code

The following parameters are passed to the player:

  • video - div tag to play the stream on the page
  • playsinline: true - play a video on the page without automatic full screen switching (ignored in iOS Safari)
  • playbackRates - playback rates list
  • liveui: true - enables rewind (DVR) interface
  • liveTracker - live playback thresholds setup
  • fill: true - scale the player to div tag dimensions
Code Block
languagejs
themeRDark
const initVideoJsPlayer = function(video) {
    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
    });
    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;
}

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

encodeURIComponent() code

Code Block
languagejs
themeRDark
function playBtnClick() {
    if (validateForm()) {
          let var streamName = $getValue('#playStreamplayStream').val();
           streamName = encodeURIComponent(streamName);
        ...
    }
}

46. HLS stream URL forming and player launching

player.play() code

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

Code Block
languagejs
themeRDark
functionconst playBtnClick = function() {
    if (validateForm()) {
        ...
        varlet videoSrc = $getValue("#urlServerurlServer").val() + '/' + streamName + '/' + streamName + '.m3u8';
        varlet key = $getValue('#keykey').val();
        varlet token = $getValue("#tokentoken").val();
        if (key.length > 0 && token.length > 0) {
            videoSrc += "?" + key + "=" + token;
        }
        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() {
    if (validateForm()) {
        ...
        player.on('playing', function() {
            console.log("Play with VideoJs");
"playing event fired");
            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();
                }
            }
        });
        ...
    }
}

8. Enable rewind (DVR) interface for the first subscriber

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

9. Rewind button click action

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) {
         player.play   backTime = seekable ? seekable.start(0) : player.currentTime();
        }
        player.currentTime(backTime);
    }
}

10. Live button click action

player.liveTracker.seekToLiveEdge() code

Code Block
languagejs
themeRDark
const liveBtnClick = function() {
    if (player != null && player.liveTracker) {
        player.liveTracker.seekToLiveEdge();
        onStartedtoggleBackButtons(true);
    }
}

511. Playback stopping

player.dispose() code

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

Code Block
languagejs
themeRDark
functionconst stopBtnClick = function() {
    if (player != null) {
        console.log("Stop VideoJS player");
        stopLiveUITimer();
        player.pausedispose();
    }
    onStopped();
}

612. New div contaioner container tag creation after previous player was removed

code

Code Block
languagejs
themeRDark
functionconst createRemoteVideo = function(parent) {
    let remoteVideo = document.createElement("video");
    remoteVideo.id = "remoteVideo";
    remoteVideo.width=852;
    remoteVideo.height=480;
    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 = videojs(remoteVideo)initVideoJsPlayer(remoteVideo);
}

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