UNPKG

@stormstreaming/stormlibrary

Version:

A JavaScript library containing core web video player functionality for embedding live-video streams on a website. Part of StormStreaming Suite.

504 lines (410 loc) 21 kB
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Storm Library DEVELOPER PAGE</title> <script src="../dist/iife/index.js"></script> </head> <link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link href="https://fonts.googleapis.com/css2?family=Courier+Prime:ital,wght@0,400;0,700;1,400;1,700&display=swap" rel="stylesheet"> <style> body {font-family: "Courier Prime", monospace;} span {width:100%;float:left; font-size:10px} button {margin:5px;padding:3px 6px; font-family: "Courier Prime", monospace;} label {margin:-20px 0 -20px 0;position:absolute;display: block;background:white;padding-left:10px;padding-right:10px; font-family: "Courier Prime", monospace;font-style: italic;} .selectWrapper {display: flex} .selectWrapper label {position: relative;margin: 0;padding-top: 4px;float:left;font-size: 12px;font-style:normal;} .selectWrapper select {float:left;position: relative} .toolbox {padding:10px;margin-left:10px;position: relative;margin-top:20px;border:2px dashed grey} .toolbox-wrapper {display:flex} ul {margin-right:10px;display: block;padding-inline-start:10px} li { display: flex; flex-wrap: nowrap; align-items: center;font-size: 14px;font-style: normal;padding-bottom: 10px;} li span { display: inline;font-size: 14px; font-weight: 600;} .value {margin-left:7px} </style> <body> <div id="storm_player_0"> <h1>Storm Library - DEVELOPER PAGE</h1> <div id="main" style="background:grey"> <div id="vContainer"></div> </div> </div> <div id="vContainer2" style="position:fixed;left:0;top:0;width:30%;z-index:2;background:red"></div> <div class="toolbox-wrapper"> <div class="toolbox"> <label>Playback</label> <ul> <li>StreamKey: <span id="streamKeyValue" class="value"></span></li> <li>PlaybackState: <span id="playbackStateValue" class="value"></span></li> <li>StreamState: <span id="streamStateValue" class="value"></span></li> <li>Volume: <span id="volumeValue" class="value"></span></li> <li>Muted: <span id="mutedValue" class="value"></span></li> <li>Muted by: <span id="mutedByValue" class="value"></span></li> </ul> </div> <div class="toolbox"> <label>Quality</label> <ul> <li>Quality label: <span id="selectedQualityLabel" class="value"></span></li> <li>Quality monogram: <span id="selectedQualityMonogram" class="value"></span></li> <li><button onclick="console.log(storm.getSourceItemList())">getSourceItemList()</button></li> <li><button onclick="console.log(storm.getCurrentSourceItem())">getCurrentSourceItem()</button></li> <li><button onclick="console.log(storm.getQualityItemList())">getQualityItemList()</button></li> <div class="selectWrapper"> <label for="qualityList">Quality Switch</label> <select id="qualityList"></select> </div> </ul> </div> <div class="toolbox"> <label>LIBRARY</label> <ul> <li>BW Graph: <div id="graph2" style="width:100px;height:20px"></div></li> <li>Curr. Bandwidth: <span id="currBandwidthValue" class="value"></span></li> <li>Min. Bandwidth: <span id="minBandwidthValue" class="value"></span></li> <li>Max. Bandwidth: <span id="maxBandwidthValue" class="value"></span></li> <li>BW Stability Trend: <span id="bwStabilityTrend" class="value"></span></li> </ul> </div> <div class="toolbox"> <label>QUALITY</label> <ul> <li>Mode: <span id="qualityMode" class="value"></span></li> <li> <div class="selectWrapper"> <label for="modeList">Mode Switch</label> <select id="modeList"> <option value="passive">passive</option> <option value="highestQuality">highestQuality</option> <option value="lowestQuality">lowestQuality</option> <option value="resolutionAware" selected>resolutionAware</option> </select> </div></li> <li>Degrade Charges: <span id="degradeCountValue" class="value"></span><button onclick="increaseDegrade()">[+]</button></li> <li>Fake Cap: <input type="number" id="fakeCap" value="1000" style="width:60px" /><button onclick="setFakeCap()">Set</button></li> <li>Next action: <span id="nextActionTime" class="value"></span></li> <li>BW Cap: <span id="bwCapValue" class="value"></span></li> <li><button onclick="removeTime(10)">[-] 10</button><button onclick="removeTime(60)">[-] 60</button><button onclick="removeTime(900)">[-] 900</button></li> </ul> </div> <div class="toolbox"> <label>BUFFER</label> <ul> <li>Buffer Graph: <div id="graph1" style="width:100px;height:20px"></div></li> <li>Buffer Size: <span id="bufferSizeValue" class="value"></span></li> <li>Buffer Stability Graph: <div id="graph3" style="width:100px;height:20px"></div></li> <li>Buffer Stability Trend: <span id="bufferStabilityTrend" class="value"></span></li> <li>Buffer Stability Dev: <span id="bufferStabilityDev" class="value"></span></li> </ul> </div> <div class="toolbox"> <label>PLAYBACK</label> <ul> <li>Playback Rate: <span id="playbackRateValue" class="value"></span></li> <li>Playback duration: <span id="playbackDurationValue" class="value"></span></li> <li>Playback starTime: <span id="playbackStartTimeValue" class="value"></span></li> <li>Stream duration: <span id="streamDurationValue"class="value"></span></li> <li>Stream starTime: <span id="streamStartTimeValue" class="value"></span></li> </ul> </div> </div> <div class="toolbox-wrapper"> <div class="toolbox"> <label>Playback</label> <button onclick="storm.play()">play()</button> <button onclick="storm.pause()">pause()</button> <button onclick="storm.stop()">stop()</button> <button onclick="storm.togglePlay()">togglePlay()</button> <button onclick="storm.subscribe('test', true)">subscribe('test', true)</button> <button onclick="storm.subscribe('test', false)">subscribe('test', false)</button> <button onclick="storm.unsubscribe()">unsubscribe()</button> <button onclick="console.log(storm.isPlaying())">isPlaying()</button> <button onclick="console.log(storm.getPlaybackState())">getPlaybackState()</button> <button onclick="console.log(storm.getStreamState())">getStreamState()</button> <button onclick="console.log(storm.getAbsoluteStreamTime())">getAbsoluteStreamTime()</button> </div> <div class="toolbox"> <label>Sound</label> <button onclick="storm.mute()">mute()</button> <button onclick="storm.unmute()">unmute()</button> <button onclick="storm.setVolume(0)">setVolume(0)</button> <button onclick="storm.setVolume(50)">setVolume(50)</button> <button onclick="storm.setVolume(100)">setVolume(100)</button> <button onclick="console.log(storm.getVolume())">getVolume()</button> <button onclick="console.log(storm.isMute())">isMuted()</button> </div> <div class="toolbox"> <label>Resize</label> <button onclick="storm.setSize(640, 360)">resize(640, 360)</button> <button onclick="storm.setSize(854, 480)">resize(854, 480)</button> <button onclick="storm.setSize(1280, 720)">resize(1280, 720)</button> <button onclick="storm.setSize(1920, 1080)">resize(1920, 1080)</button> <button onclick="storm.setWidth(360)">setWidth(360)</button> <button onclick="storm.setWidth('50%')">setWidth('50%')</button> <button onclick="storm.setWidth('100%')">setWidth('100%')</button> <button onclick="storm.setHeight(180)">setHeight(180)</button> <button onclick="storm.setHeight('50%')">setHeight('50%')</button> <button onclick="storm.setHeight('100%')">setHeight('100%')</button> <button onclick="console.log(storm.getWidth())">getWidth()</button> <button onclick="console.log(storm.getHeight())">getHeight()</button> <button onclick="storm.attachToContainer('vContainer')">attachToContainer('vContainer')</button> <button onclick="storm.attachToContainer('vContainer2')">attachToContainer('vContainer2')</button> <button onclick="storm.attachToContainer('vContainer3')">attachToContainer('vContainer3')</button> <button onclick="storm.detachFromContainer()">detachFromContainer()</button> </div> <div class="toolbox"> <label>Quality</label> <button onclick="console.log(storm.getSourceList())">getSourceList()</button> <button onclick="console.log(storm.getCurrentSourceItem())">getCurrentSourceItem()</button> <button onclick="console.log(storm.getQualityItemList())">getQualityItemList()</button> </div> <div class="toolbox"> <label>Other</label> <button onclick="console.log(storm.getLibraryID())">getLibraryID()</button> <button onclick="console.log(storm.getVersion())">getVersion()</button> <button onclick="storm.destroy()">destroy()</button> <button onclick="storm.enterFullScreen()">enterFullScreen()</button> </div> </div> <div class="toolbox-wrapper"> <div class="toolbox" style="width:50%"> <label>Player Log</label> <div id="logContainer"></div> </div> <div class="toolbox" style="width:50%"> <label>Events recording</label> <textarea id="events" rows="30" style="padding:10px;width: calc(100% - 20px)">Events:</textarea> </div> </div> <script> let config = { stream: { serverList: [ // list of streaming server, 2nd, 3rd etc. will be used as backup { host: "127.0.0.1", // host or ip to the streaming server application:"live", port: 8080, // server port ssl: false // whenever SSL connection should be used or not }, ], }, settings: { autoStart: true, video: { scalingMode: "letterbox", containerID: "vContainer", width:"60%", aspectRatio:"16:9", resizeDebounce:0, }, quality: { controlMode: "RESOLUTION_AWARE" // resolutionAware, passive, highestQuality }, audio: { startVolume:50, }, debug: { console: { enabled: false, logTypes: ["INFO","ERROR", "TRACE", "WARNING", "SUCCESS"], monoColor: false }, container: { enabled: true, containerID: "logContainer", logTypes: ["ERROR", "INFO", "TRACE", "WARNING","SUCCESS"], monoColor: false }, playbackController: true, playerUnit: true, qualityController: true, stageController: true } } } let storm = stormLibrary(config); storm.addEventListener("streamEnd", function(event){ functionAppend(`Library ID: ${event.ref.getLibraryID()} - Stream ended`); }) storm.addEventListener("optionalStreamData", function(event){ functionAppend(`Library ID: ${event.ref.getLibraryID()} - Optional stream data`); }) storm.addEventListener("awaitingStream", function(event){ functionAppend(`Library ID: ${event.ref.getLibraryID()} - Awaiting for stream`); }) storm.addEventListener("streamNotFound", function(event){ functionAppend(`Library ID: ${event.ref.getLibraryID()} - Stream not found`); }) storm.addEventListener("playerReady", function(event){ functionAppend(`Library ID: ${event.ref.getLibraryID()} - Core Player is ready for interaction!`); }) storm.addEventListener("serverConnectionInitiate", function(event){ functionAppend(`Library ID: ${event.ref.getLibraryID()} - (serverConnectionInitiate) Starting connection to: ${event.serverURL}`); }) storm.addEventListener("serverConnect", function(event){ functionAppend(`Library ID: ${event.ref.getLibraryID()} - (serverConnect) Successfully connected to: ${event.serverURL} | seqNum: ${event.sequenceNum} `); }) storm.addEventListener("serverDisconnect", function(event){ functionAppend(`Library ID: ${event.ref.getLibraryID()} - serverDisconnect: ${event.serverURL} | seqNum: ${event.sequenceNum} ` ); }) storm.addEventListener("streamStateChange", function(event){ functionAppend(`Library ID: ${event.ref.getLibraryID()} - (streamStateChange): ${event.state}`); document.getElementById("streamStateValue").innerHTML = event.state; }) storm.addEventListener("qualityListUpdate", function(event){ updateQualityList(storm.getQualityItemList()); }) storm.addEventListener("playbackStateChange", function(event){ functionAppend(`Library ID: ${event.ref.getLibraryID()} - (playbackStateChange): ${event.state}`); document.getElementById("streamKeyValue").innerHTML = event.streamKey; document.getElementById("playbackStateValue").innerHTML = event.state; }) storm.addEventListener("serverConnectionError", function(event){ functionAppend(`Library ID: ${event.ref.getLibraryID()} - (serverConnectionError) Could not connect to server: ${event.serverURL}`); }) storm.addEventListener("allConnectionsFailed", function(event){ functionAppend(`Library ID: ${event.ref.getLibraryID()} - All servers failed!`); }) storm.addEventListener("videoElementCreate", function(event){ functionAppend(`Library ID: ${event.ref.getLibraryID()} - videoElementCreate!`); }) storm.addEventListener("volumeChange", function(event){ document.getElementById("volumeValue").innerHTML = event.volume; document.getElementById("mutedValue").innerHTML = event.muted; document.getElementById("mutedByValue").innerHTML = event.invokedBy; functionAppend(`Library ID: ${event.ref.getLibraryID()} - volumeChanged! ${event.volume} | ${event.muted} | ${event.invokedBy}`); }) storm.addEventListener("playbackInitiate", function(event){ functionAppend(`Library ID: ${event.ref.getLibraryID()} - (playbackInitiate) Playback initiated!`); updateQualityList(storm.getQualityItemList()); }) storm.addEventListener("bufferingStart", function(event){ functionAppend(`Library ID: ${event.ref.getLibraryID()} - (bufferingStart) Buffering has started!`); }) storm.addEventListener("playbackStop", function(event){ functionAppend(`Library ID: ${event.ref.getLibraryID()} - (playbackStop) Playback Stop!`); }) storm.addEventListener("bufferingComplete", function(event){ functionAppend(`Library ID: ${event.ref.getLibraryID()} - (bufferingComplete) Buffering is complete!`); }) storm.addEventListener("playbackStart", function(event){ functionAppend(`Library ID: ${event.ref.getLibraryID()} - (playbackStart) Playback Start!`); }) storm.addEventListener("playbackPause", function(event){ functionAppend(`Library ID: ${event.ref.getLibraryID()} - (playbackPause) Playback Pause!`); }) storm.addEventListener("metadataReceived", function(event){ functionAppend(`Library ID: ${event.ref.getLibraryID()} - Metadata!`); }) storm.addEventListener("fullScreenEnter", function(event){ functionAppend(`Library ID: ${event.ref.getLibraryID()} - fullScreenEnter!`); }) storm.addEventListener("fullScreenExit", function(event){ functionAppend(`Library ID: ${event.ref.getLibraryID()} - fullScreenExit!`); }) storm.addEventListener("playbackProgress", function(event){ document.getElementById("playbackStartTimeValue").innerHTML = formatUnixTime(event.playbackStartTime); document.getElementById("playbackDurationValue").innerHTML = formatSecondsWithMilliseconds(event.playbackDuration); document.getElementById("streamDurationValue").innerHTML = formatSecondsWithMilliseconds(event.streamDuration); document.getElementById("streamStartTimeValue").innerHTML = formatUnixTime(event.streamStartTime); }) storm.initialize(); const graph1 = storm.createBufferGraph("graph1", 50).start(); const graph2 = storm.createBandwidthGraph("graph2", 50).start(); const graph3 = storm.createBufferStabilityGraph("graph3", 50).start(); storm.subscribe("test", true); function updateQualityList(qualities) { const qualityList = document.getElementById('qualityList'); qualityList.innerHTML = ''; qualities.forEach(quality => { const option = document.createElement('option'); option.value = quality.id; option.textContent = quality.label; qualityList.appendChild(option); if(quality.isSelected){ document.getElementById("selectedQualityLabel").innerHTML = quality.label; document.getElementById("selectedQualityMonogram").innerHTML = quality.monogram; qualityList.value = quality.id; } }); } document.getElementById('qualityList').addEventListener('change', () => { storm.playQualityItem(document.getElementById('qualityList').value); }); document.getElementById('modeList').addEventListener('change', () => { storm.setQualityControlMode(document.getElementById('modeList').value); }); function formatUnixTime(unixTime) { const date = new Date(unixTime); const pad = (num) => num.toString().padStart(2, '0'); const day = pad(date.getDate()); const month = pad(date.getMonth() + 1); const year = date.getFullYear(); const hours = pad(date.getHours()); const minutes = pad(date.getMinutes()); const seconds = pad(date.getSeconds()); return `${day}.${month}.${year} ${hours}:${minutes}:${seconds}`; } function formatSecondsWithMilliseconds(milliseconds) { const hours = Math.floor(milliseconds / 3600000); milliseconds %= 3600000; const minutes = Math.floor(milliseconds / 60000); milliseconds %= 60000; const seconds = Math.floor(milliseconds / 1000); const milisecondsLeft = milliseconds % 1000; const formattedHours = hours < 10 ? '0' + hours : hours; const formattedMinutes = minutes < 10 ? '0' + minutes : minutes; const formattedSeconds = seconds < 10 ? '0' + seconds : seconds; const formattedMilliseconds = milisecondsLeft.toString().padStart(3, '0'); return `${formattedHours}:${formattedMinutes}:${formattedSeconds}.${formattedMilliseconds}`; } function removeTime(value){ storm?.getQualityController()?.reduceUpgradeTimeout(value); } function functionAppend(appender){ let allText = document.getElementById("events").value; document.getElementById("events").value = appender+ "\r\n\r\n" +allText } function increaseDegrade(){ storm?.getPlaybackController()?.getPlayer()?._degradeChargeMeter.addEntry(); } function setFakeCap(){ const value = document.getElementById("fakeCap").value; storm.getStorageManager().saveField("fakeBandwidthCap",value.toString()); document.getElementById("fakeCap").value = 0; } setInterval(() => { let playbackRatio = storm?.getPlaybackRate() ?? "Unknown"; document.getElementById("playbackRateValue").innerHTML = playbackRatio; let bwCapValue = storm?.getStorageManager().getField("bandwidthCapValue"); document.getElementById("bwCapValue").innerHTML = Number(bwCapValue).toFixed(2)+" kbps"; let qualityMode = storm?.getQualityControlMode() ?? "unknown"; document.getElementById("qualityMode").innerHTML = qualityMode; let nextActionTime = storm?.getQualityController()?.getTimeToNextUpgrade() ?? 0; document.getElementById("nextActionTime").innerHTML = nextActionTime+" s"; let overloadCount = storm?.getPlaybackController()?.getPlayer()?._degradeChargeMeter.currentCount ?? 0; document.getElementById("degradeCountValue").innerHTML = overloadCount; // bandwidth stability: let bwStabilityTrendLabel = storm?.getBandwidthAnalyser()?.currentTrend ?? "unknown"; let bwStabilityTrendDuration = storm?.getBandwidthAnalyser()?.trendDuration ?? 0; document.getElementById("bwStabilityTrend").innerHTML = bwStabilityTrendLabel+" ("+bwStabilityTrendDuration.toFixed(3) +" s)"; // current bandwidth let currBandwidth = storm?.getBandwidthMeter()?.currentBandwidth ?? "0"; document.getElementById("currBandwidthValue").innerHTML = (currBandwidth/1000).toFixed(2)+" kbps" // minimal bandwidth let minBandwidth = storm?.getBandwidthMeter()?.minBandwidth ?? "0"; document.getElementById("minBandwidthValue").innerHTML = (minBandwidth/1000).toFixed(2)+" kbps" // max bandwidth let maxBandwidth = storm?.getBandwidthMeter()?.maxBandwidth ?? "0"; document.getElementById("maxBandwidthValue").innerHTML = (maxBandwidth/1000).toFixed(2)+" kbps" // buffer size let bufferSize = storm?.getBufferAnalyser()?.bufferSize ?? "0"; document.getElementById("bufferSizeValue").innerHTML = bufferSize.toFixed(2)+" s"; let bufferStabilityLabel = storm?.getBufferAnalyser()?.stability ?? "unknown"; let bufferStabilityDev = storm?.getBufferAnalyser()?.bufferDeviation ?? 0; document.getElementById("bufferStabilityTrend").innerHTML = bufferStabilityLabel; document.getElementById("bufferStabilityDev").innerHTML = bufferStabilityDev.toFixed(3); },100) </script> </body> </html>