twitchtv / acm-mmsys-2020-grand-challenge

Assets for Twitch's ACM MMSys 2020 Grand Challenge
Apache License 2.0
42 stars 21 forks source link

Loading own ABR rule #7

Open ThAlKa opened 4 years ago

ThAlKa commented 4 years ago

Hello. I am trying to import my custom ABR rule in 'index.html', in order to direct the player to use my 'CustomRule' instead of the Default ABR settings ('useDefaultABRRules': false).

Although all include directories seem to be set correctly, I get the following error:

Uncaught ReferenceError: CustomRule is not defined.

Via a search online, I have found that this is typically related to jQuery not being referenced (which in my case it is). Has anyone experienced a similar problem, or has any suggestions on how to resolve it?

Here is my index.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8"/>
    <title>Low latency live stream instantiation example</title>   
    <script src="../../dist/dash.all.debug.js"></script>
    <script src="http://code.jquery.com/jquery-3.4.1.min.js"></script>
    <script type='module' src="./CustomRule.js"></script>
    <!--dash.all.min.js should be used in production over dash.all.debug.js
        Debug files are not compressed or obfuscated making the file size much larger compared with dash.all.min.js-->
    <!--<script src="../../dist/dash.all.min.js"></script>-->

    <script class="code">
        var player, targetLatency, minDrift, catchupPlaybackRate;

        function init() {
            var video,
                url  = 'http://localhost:9001/live/live.mpd';

            video = document.querySelector("video");
            player = dashjs.MediaPlayer().create();
            player.initialize(video, url, true);
            player.updateSettings({ 'streaming': { 'lowLatencyEnabled': true }});
            player.updateSettings({'debug': {'logLevel': dashjs.Debug.LOG_LEVEL_WARNING }});
            player.updateSettings({
            'streaming': {
                'abr': {
                    'useDefaultABRRules': false //in case of own abr algo development
                }
            }
        });
            // add my custom quality switch rule.
            player.addABRCustomRule('qualitySwitchRules', 'CustomRule', CustomRule);
            applyParameters();

            return player;
        }

        function applyParameters() {
            targetLatency = parseFloat(document.getElementById("target-latency").value, 10);
            minDrift = parseFloat(document.getElementById("min-drift").value, 10);
            catchupPlaybackRate = parseFloat(document.getElementById("catchup-playback-rate").value, 10);

            player.updateSettings({
                'streaming': {
                    'liveDelay': targetLatency,
                    'liveCatchUpMinDrift': minDrift,
                    'liveCatchUpPlaybackRate': catchupPlaybackRate
                }
            });
        }
    </script>

    <style>
        .video-wrapper {
            display: flex;
            flex-flow: row wrap;
        }

        .video-wrapper > div:nth-child(2) {
            margin-left: 25px;
        }

        video {
            width: 640px;
            height: 360px;
        }

        ul {
            margin: 0;
        }

        input {
            width: 5em;
            border: 1px solid gray;
            padding: 0 4px 0 8px;
        }

        .help-container {
            display: flex;
            flex-flow: row wrap;
            margin-top: 1em;
            align-content: center;
            background: white;
            border: solid 1px #ddd;
            padding: 0.5em;
        }

        .help-container > div {
            width: 33.3%;
            padding: 1em;
            box-sizing: border-box;
        }

        .help-container h3 {
            margin-top: 0;
        }
    </style>
</head>
<body>
<div>
    <div class="video-wrapper">
        <video controls="true" autoplay muted></video>
        <div>
            <div>
                <form action="javascript:applyParameters()">
                    <fieldset>
                        <legend>Configurable parameters</legend>
                        <p>Target Latency (secs): <input type="number" id="target-latency" value="1" min="0" step="0.1"></p>
                        <p>Min. drift (secs): <input type="number" id="min-drift" value="0.05" min="0.0" max="0.5" step="0.01"></p>
                        <p>Catch-up playback rate (%): <input type="number" id="catchup-playback-rate" value="0.5" min="0.0" max="0.5" step="0.01"></p>
                        <button type="submit">Apply</button>
                    </fieldset>
                </form>
            </div>
            <br>
            <fieldset>
                <legend>Current values</legend>
                <ul>
                    <li>Latency: <span id="latency-tag"></span></li>
                    <li>Min. drift: <span id="mindrift-tag"></span></li>
                    <li>Playback rate: <span id="playbackrate-tag"></span></li>
                    <li>Buffer: <b><span id="buffer-tag"></span></b></li>
                    <li>Quality: <b><span id="quality-tag"></span></b></li>
                </ul>
                <div id="stats"></div>
            </fieldset>
        </div>
    </div>

    <p style="font-family:Arial,sans-serif; font-weight: bold; font-size: 1.1em">Concepts definition</p>
    <div class="help-container">
        <div id="latency-help">
            <h3>Latency</h3>
            <p>Lowering this value will lower latency but may decrease the player's ability to build a stable buffer.</p>
            <p><a href="http://cdn.dashjs.org/latest/jsdoc/module-MediaPlayer.html#setLiveDelay__anchor" target="_blank">setLiveDelay() doc</a></p>
        </div>

        <div id="min-drift-help">
            <h3>Min. drift</h3>
            <p>Minimum latency deviation allowed before activating catch-up mechanism.</p>
            <p><a href="http://cdn.dashjs.org/latest/jsdoc/module-MediaPlayer.html#setLowLatencyMinDrift__anchor" target="_blank">setLowLatencyMinDrift() doc</a></p>
        </div>

        <div id="catch-up-playback-rate-help">
            <h3>Catch-up playback rate</h3>
            <p>Maximum catch-up rate, as a percentage, for low latency live streams.</p>
            <p><a href="http://cdn.dashjs.org/latest/jsdoc/module-MediaPlayer.html#setCatchUpPlaybackRate__anchor" target="_blank">setCatchUpPlaybackRate() doc</a></p>
        </div>
    </div>
</div>
<script>
    document.addEventListener("DOMContentLoaded", function () {
        const player = init();
        const video = document.querySelector("video")
        let stallingAt = null;
        const CMA = () => {
            let average = 0;
            let count = 0;

            return {
                average(val) {
                    if (isNaN(val)) {
                        return 0;
                    }
                    average = average + ((val - average) / ++count);
                    return average;
                },
            }
        }

        setInterval(function() {
                var dashMetrics = player.getDashMetrics();
                var settings = player.getSettings();

                var currentLatency = parseFloat(player.getCurrentLiveLatency(), 10);
                document.getElementById("latency-tag").innerHTML = currentLatency + " secs";

                document.getElementById("mindrift-tag").innerHTML = settings.streaming.liveCatchUpMinDrift + " secs";

                var currentPlaybackRate = player.getPlaybackRate();
                document.getElementById("playbackrate-tag").innerHTML = Math.round(currentPlaybackRate * 100) / 100;

                var currentBuffer = dashMetrics.getCurrentBufferLevel("video");
                document.getElementById("buffer-tag").innerHTML = currentBuffer + " secs";
            }, 200);

            player.on(dashjs.MediaPlayer.events.QUALITY_CHANGE_REQUESTED, (e) => {
                console.warn('Quality changed requested', e);
            });

            player.on(dashjs.MediaPlayer.events.QUALITY_CHANGE_RENDERED, (e) => {
                console.warn('Quality changed', e);
                const quality = player.getBitrateInfoListFor('video')[e.newQuality];
                if (!quality) {
                    return;
                }
                document.querySelector('#quality-tag').innerText = `${quality.width}x${quality.height}, ${quality.bitrate / 1000}Kbps`;
            });

        window.startRecording = () => {
            console.info('Begin recording');

            const latencyCMA = CMA();
            const bufferCMA = CMA();
            const history = window.abrHistory = {
                switchHistory: [],
                stallDuration: 0,
                averageLatency: 0,
                averageBufferLength: 0,
            };

            // Record the initial quality
            recordSwitch(player.getBitrateInfoListFor('video')[player.getQualityFor('video')]);

            let pollInterval = -1;
            window.stopRecording = () => {
                clearInterval(pollInterval);
                checkStallResolution();
                const lastQuality = history.switchHistory[history.switchHistory.length - 1];
                if (lastQuality.end === null) {
                    lastQuality.end = video.currentTime;
                }
                console.warn('Run ended. Please navigate back to node for results.');
            }

            pollInterval = setInterval(function() {
                const currentLatency = parseFloat(player.getCurrentLiveLatency(), 10);
                const currentBuffer = player.getDashMetrics().getCurrentBufferLevel("video");
                history.averageLatency = latencyCMA.average(currentLatency);
                history.averageBufferLength = bufferCMA.average(currentBuffer);
                console.log(history);
            }, 200);

            player.on(dashjs.MediaPlayer.events.QUALITY_CHANGE_RENDERED, (e) => {
                recordSwitch(player.getBitrateInfoListFor('video')[e.newQuality]);
            });

            video.addEventListener('waiting', (e) => {
                stallingAt = performance.now();
            });

            video.addEventListener('timeupdate', () => {
                checkStallResolution();
            });

            function recordSwitch(quality) {
                if (!quality) {
                    return;
                }
                const switchHistory = history.switchHistory;
                const prev = switchHistory[switchHistory.length - 1];
                const videoNow = video.currentTime;
                if (prev) {
                    prev.end = videoNow;
                }
                switchHistory.push({ start: videoNow, end: null, quality });
            }

            function checkStallResolution() {
                if (stallingAt !== null) {
                    history.stallDuration += (performance.now() - stallingAt);
                    stallingAt = null;
                }
            }
        }
    });
</script>
<script src="../highlighter.js"></script>
</body>
</html>
johnBartos commented 4 years ago

Hi @ThAlKa, it sounds like either your script isn't being loaded, or you're not creating a reference to your rule that the demo script can access. You can check to see if your script is being loaded in the "Network" tab of the devtools (you should see a status code of 200 for the request to CustomRule.js).

For the reference, make sure that your CustomRule.js script is adding the CustomRule object to the global scope. You may want to do something like window.CustomRule = myAbrRule() at the top of your CustomRule.js script.

Let me know if the above suggestions don't solve your problem.