Browse Source

chore: update the demo page (#1184)

Using bootstrap styles and tabs, update the demo page and bring in some stuff from the stats page.

The two main things that are missing right now are the bitrate switching and the timed metadata graphs, though, I think those aren't used as much. I figured we can bring what I have so far, and we can work on the others later on. For example, I want to migrate the representations dropdown to be a bit closer to what's in the stats page instead.

I kept the old index page as old-index.html and have the main page redirect there if we're on IE11, because the bootstrap version used doesn't support IE11. Hopefully, we won't be supporting IE11 for long, and we can delete this once we drop support.
pull/1185/head
Gary Katsevman 4 years ago
committed by GitHub
parent
commit
55f0bde503
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 328
      index.html
  2. 150
      old-index.html
  3. 110
      package-lock.json
  4. 1
      package.json
  5. 707
      scripts/index.js
  6. 7
      scripts/netlify.js
  7. 4
      scripts/old-index.js

328
index.html

@ -4,142 +4,228 @@
<meta charset="utf-8">
<title>videojs-http-streaming Demo</title>
<link rel="icon" href="logo.svg">
<link href="node_modules/bootstrap/dist/css/bootstrap.css" rel="stylesheet">
<link href="node_modules/video.js/dist/video-js.css" rel="stylesheet">
<link href="node_modules/videojs-http-source-selector/dist/videojs-http-source-selector.css" rel="stylesheet">
<style>
body {
font-family: Arial, sans-serif;
margin: 20px;
}
.info {
background-color: #eee;
border: thin solid #333;
border-radius: 3px;
padding: 0 5px;
margin: 20px 0;
}
label {
display: block;
.form-check {
background-color: hsl(0, 0%, 90%);
margin-block: 0.5rem;
padding: 0.25em 0.25em 0.25em 1.75em;
width: 700px;
width: fit-content;
margin-top: 4px;
}
.options label {
background-color: hsl(0, 0%, 90%);
padding: 0.25em;
margin: 0.25em;
#player-fixture {
min-height: 250px;
}
input[type=url], select {
min-width: 600px;
#segment-metadata {
list-style: none;
}
#preload {
min-width: auto;
#segment-metadata pre {
overflow: scroll;
}
h3 {
margin-bottom: 5px;
</style>
</head>
<body class="m-4">
<script>
// if we're on IE, load up the load index page
var result = (/MSIE\s(\d+)\.\d/).exec(navigator.userAgent);
var version = result && parseFloat(result[1]);
if (!version && (/Trident\/7.0/i).test(navigator.userAgent) && (/rv:11.0/).test(navigator.userAgent)) {
// IE 11 has a different user agent string than other IE versions
version = 11.0;
}
#keysystems {
display: block;
if (version) {
window.location.href = './old-index.html';
}
</style>
</script>
</head>
<body>
<div id="player-fixture">
</div>
<label>Representations</label>
<select id='representations'></select>
<h3>Options</h3>
<div class="options">
<label>
<input id=minified type="checkbox">
Minified VHS (reloads player)
</label>
<label>
<input id=sync-workers type="checkbox">
Synchronous Web Workers (reloads player)
</label>
<label>
<input id=liveui type="checkbox">
Enable the live UI (reloads player)
</label>
<label>
<input id=debug type="checkbox">
Debug Logging
</label>
<label>
<input id=muted type="checkbox">
Muted
</label>
<label>
<input id=autoplay type="checkbox">
Autoplay
</label>
<label>
<input id=llhls type="checkbox">
[EXPERIMENTAL] Enables support for ll-hls (reloads player)
</label>
<label>
<input id=buffer-water type="checkbox">
[EXPERIMENTAL] Use Buffer Level for ABR (reloads player)
</label>
<label>
<input id=exact-manifest-timings type="checkbox">
[EXPERIMENTAL] Use exact manifest timings for segment choices (reloads player)
</label>
<label>
<input id=pixel-diff-selector type="checkbox">
[EXPERIMENTAL] Use the Pixel difference resolution selector (reloads player)
</label>
<label>
<input id=override-native type="checkbox" checked>
Override Native (reloads player)
</label>
<label>
<input id=mirror-source type="checkbox" checked>
Mirror sources from player.src (reloads player, uses EXPERIMENTAL sourceset option)
</label>
<label>
Preload (reloads player)
<select id=preload>
<option selected>auto</option>
<option>none</option>
<option>metadata</option>
</select>
</div>
<header class="container-fluid">
<a href="https://github.com/videojs/http-streaming" class="d-flex align-items-center pb-3 mb-5 border-bottom" style="height: 4em">
<img src="./logo.svg" alt="VHS logo showcasing a VHS tape with the Video.js logo on the label" class="rounded mh-100">
<span class="fs-1 ps-2">VHS: videojs-http-streaming</span>
</a>
</header>
<div id="player-fixture" class="container-fluid pb-3 mb-3"></div>
<h3>Load a URL</h3>
<label>Url:</label>
<input id=url type=url>
<label>Type: (uses url extension if blank, usually application/x-mpegURL or application/dash+xml)</label>
<input id=type type=text>
<label>Optional Keystems JSON:</label>
<textarea id=keysystems cols=100 rows=5></textarea>
<button id=load-url type=button>Load</button>
<h3>Load a Source</h3>
<select id=load-source>
<optgroup label="hls">
</optgroup>
<optgroup label="dash">
</optgroup>
<optgroup label="drm">
</optgroup>
<optgroup label="live">
</optgroup>
<optgroup label="low latency live">
</optgroup>
<optgroup label="json manifest object">
</optgroup>
</select>
<h3>Navigation</h3>
<ul>
<li><a href="test/debug.html">Run unit tests in browser.</a></li>
<li><a href="docs/api/">Read generated docs.</a></li>
<li><a href="utils/stats/">Stats</a></li>
<ul class="nav nav-tabs container-fluid mb-3" id="myTab" role="tablist">
<li class="nav-item" role="presentation">
<button class="nav-link active" id="home-tab" data-bs-toggle="tab" data-bs-target="#sources" type="button" role="tab" aria-selected="true">Sources</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="contact-tab" data-bs-toggle="tab" data-bs-target="#options" type="button" role="tab" aria-selected="false">Options</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="profile-tab" data-bs-toggle="tab" data-bs-target="#levels" type="button" role="tab" aria-selected="false">Representations</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="profile-tab" data-bs-toggle="tab" data-bs-target="#player-stats" type="button" role="tab" aria-selected="false">Player Stats</button>
</li>
</ul>
<script src="scripts/index-demo-page.js"></script>
<div class="tab-content container-fluid">
<div class="tab-pane active" id="sources" role="tabpanel">
<label for=url class="form-label">Source URL</label>
<div class="input-group">
<span class="input-group-text"><label for=url>Url</label></span>
<input id=url type=url class="form-control">
</div>
<label for=type class="form-label">Source Type (uses url extension if blank, usually application/x-mpegURL or application/dash+xml)</label>
<div class="input-group">
<span class="input-group-text"><label for=type>Type</label></span>
<input id=type type=text class="form-control">
</div>
<label for="keysystems" class="form-label">Optional Keystems JSON:</label>
<div class="input-group">
<span class="input-group-text"><label for=keysystems>keySystems JSON</label></span>
<textarea id=keysystems cols=100 rows=5 class="form-control"></textarea>
</div>
<button id=load-url type=button class="btn btn-primary my-2">Load</button>
<div class="input-group">
<span class="input-group-text"><label for=load-source>Preloaded Sources</label></span>
<select id=load-source class="form-select">
<optgroup label="hls">
</optgroup>
<optgroup label="dash">
</optgroup>
<optgroup label="drm">
</optgroup>
<optgroup label="live">
</optgroup>
<optgroup label="low latency live">
</optgroup>
<optgroup label="json manifest object">
</optgroup>
</select>
</div>
</div>
<div class="tab-pane" id="options" role="tabpanel">
<div class="options">
<div class="form-check">
<input id=minified type="checkbox" class="form-check-input">
<label class="form-check-label" for="minified">Minified VHS (reloads player)</label>
</div>
<div class="form-check">
<input id=sync-workers type="checkbox" class="form-check-input">
<label class="form-check-label" for="sync-workers">Synchronous Web Workers (reloads player)</label>
</div>
<div class="form-check">
<input id=liveui type="checkbox" class="form-check-input">
<label class="form-check-label" for="liveui">Enable the live UI (reloads player)</label>
</div>
<div class="form-check">
<input id=fluid type="checkbox" class="form-check-input">
<label class="form-check-label" for="fluid">Fluid mode</label>
</div>
<div class="form-check">
<input id=debug type="checkbox" class="form-check-input">
<label class="form-check-label" for="debug">Debug Logging</label>
</div>
<div class="form-check">
<input id=muted type="checkbox" class="form-check-input">
<label class="form-check-label" for="muted">Muted</label>
</div>
<div class="form-check">
<input id=autoplay type="checkbox" class="form-check-input">
<label class="form-check-label" for="autoplay">Autoplay</label>
</div>
<div class="form-check">
<input id=llhls type="checkbox" class="form-check-input">
<label class="form-check-label" for="llhls">[EXPERIMENTAL] Enables support for ll-hls (reloads player)</label>
</div>
<div class="form-check">
<input id=buffer-water type="checkbox" class="form-check-input">
<label class="form-check-label" for="buffer-water">[EXPERIMENTAL] Use Buffer Level for ABR (reloads player)</label>
</div>
<div class="form-check">
<input id=exact-manifest-timings type="checkbox" class="form-check-input">
<label class="form-check-label" for="exact-manifest-timings">[EXPERIMENTAL] Use exact manifest timings for segment choices (reloads player)</label>
</div>
<div class="form-check">
<input id=pixel-diff-selector type="checkbox" class="form-check-input">
<label class="form-check-label" for="pixel-diff-selector">[EXPERIMENTAL] Use the Pixel difference resolution selector (reloads player)</label>
</div>
<div class="form-check">
<input id=override-native type="checkbox" class="form-check-input" checked>
<label class="form-check-label" for="override-native">Override Native (reloads player)</label>
</div>
<div class="form-check">
<input id=mirror-source type="checkbox" class="form-check-input" checked>
<label class="form-check-label" for="mirror-source">Mirror sources from player.src (reloads player, uses EXPERIMENTAL sourceset option)</label>
</div>
<div class="input-group">
<span class="input-group-text"><label for=preload>Preload (reloads player)</label></span>
<select id=preload class="form-select">
<option selected>auto</option>
<option>none</option>
<option>metadata</option>
</select>
</div>
</div>
</div>
<div class="tab-pane" id="levels" role="tabpanel">
<div class="input-group">
<span class="input-group-text"><label for=representations>Representations</label></span>
<select id='representations' class="form-select"></select>
</div>
</div>
<div class="tab-pane" id="player-stats" role="tabpanel">
<div class="row">
<div class="player-stats col-4">
<dl>
<dt>Current Time:</dt>
<dd class="current-time-stat">0</dd>
<dt>Buffered:</dt>
<dd class="buffered-stat">-</dd>
<dt>Video Buffered:</dt>
<dd class="video-buffered-stat">-</dd>
<dt>Audio Buffered:</dt>
<dd class="audio-buffered-stat">-</dd>
<dt>Seekable:</dt>
<dd><span class="seekable-start-stat">-</span> - <span class="seekable-end-stat">-</span></dd>
<dt>Video Bitrate:</dt>
<dd class="video-bitrate-stat">0 kbps</dd>
<dt>Measured Bitrate:</dt>
<dd class="measured-bitrate-stat">0 kbps</dd>
<dt>Video Timestamp Offset</dt>
<dd class="video-timestampoffset">0</dd>
<dt>Audio Timestamp Offset</dt>
<dd class="audio-timestampoffset">0</dd>
</dl>
</div>
<ul id="segment-metadata" class="col-8"></ul>
</div>
</div>
</div>
<script src="node_modules/bootstrap/dist/js/bootstrap.js"></script>
<script src="scripts/index.js"></script>
<script>
window.startDemo(function(player) {
// do something with setup player

150
old-index.html

@ -0,0 +1,150 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>videojs-http-streaming Demo</title>
<link rel="icon" href="logo.svg">
<link href="node_modules/video.js/dist/video-js.css" rel="stylesheet">
<link href="node_modules/videojs-http-source-selector/dist/videojs-http-source-selector.css" rel="stylesheet">
<style>
body {
font-family: Arial, sans-serif;
margin: 20px;
}
.info {
background-color: #eee;
border: thin solid #333;
border-radius: 3px;
padding: 0 5px;
margin: 20px 0;
}
label {
display: block;
width: 700px;
width: fit-content;
margin-top: 4px;
}
.options label {
background-color: hsl(0, 0%, 90%);
padding: 0.25em;
margin: 0.25em;
}
input[type=url], select {
min-width: 600px;
}
#preload {
min-width: auto;
}
h3 {
margin-bottom: 5px;
}
#keysystems {
display: block;
}
</style>
</head>
<body>
<div id="player-fixture">
</div>
<label>Representations</label>
<select id='representations'></select>
<h3>Options</h3>
<div class="options">
<label>
<input id=minified type="checkbox">
Minified VHS (reloads player)
</label>
<label>
<input id=sync-workers type="checkbox">
Synchronous Web Workers (reloads player)
</label>
<label>
<input id=liveui type="checkbox">
Enable the live UI (reloads player)
</label>
<label>
<input id=debug type="checkbox">
Debug Logging
</label>
<label>
<input id=muted type="checkbox">
Muted
</label>
<label>
<input id=autoplay type="checkbox">
Autoplay
</label>
<label>
<input id=llhls type="checkbox">
[EXPERIMENTAL] Enables support for ll-hls (reloads player)
</label>
<label>
<input id=buffer-water type="checkbox">
[EXPERIMENTAL] Use Buffer Level for ABR (reloads player)
</label>
<label>
<input id=exact-manifest-timings type="checkbox">
[EXPERIMENTAL] Use exact manifest timings for segment choices (reloads player)
</label>
<label>
<input id=pixel-diff-selector type="checkbox">
[EXPERIMENTAL] Use the Pixel difference resolution selector (reloads player)
</label>
<label>
<input id=override-native type="checkbox" checked>
Override Native (reloads player)
</label>
<label>
<input id=mirror-source type="checkbox" checked>
Mirror sources from player.src (reloads player, uses EXPERIMENTAL sourceset option)
</label>
<label>
Preload (reloads player)
<select id=preload>
<option selected>auto</option>
<option>none</option>
<option>metadata</option>
</select>
</label>
</div>
<h3>Load a URL</h3>
<label>Url:</label>
<input id=url type=url>
<label>Type: (uses url extension if blank, usually application/x-mpegURL or application/dash+xml)</label>
<input id=type type=text>
<label>Optional Keystems JSON:</label>
<textarea id=keysystems cols=100 rows=5></textarea>
<button id=load-url type=button>Load</button>
<h3>Load a Source</h3>
<select id=load-source>
<optgroup label="hls">
</optgroup>
<optgroup label="dash">
</optgroup>
<optgroup label="drm">
</optgroup>
<optgroup label="live">
</optgroup>
<optgroup label="low latency live">
</optgroup>
<optgroup label="json manifest object">
</optgroup>
</select>
<h3>Navigation</h3>
<ul>
<li><a href="test/debug.html">Run unit tests in browser.</a></li>
<li><a href="docs/api/">Read generated docs.</a></li>
<li><a href="utils/stats/">Stats</a></li>
</ul>
<script src="scripts/old-index.js"></script>
<script>
window.startDemo(function(player) {
// do something with setup player
});
</script>
</body>
</html>

110
package-lock.json

@ -1336,49 +1336,18 @@
}
},
"@videojs/http-streaming": {
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/@videojs/http-streaming/-/http-streaming-2.7.1.tgz",
"integrity": "sha512-e7I5zHtTklNlBXhWnl2Nla+8hqjXzKXauAVK8cmcN0b6keqwW3WQDfAAnAzzAGf3CvxDUVudRcWGQqtNrXYjmQ==",
"version": "2.10.0",
"resolved": "https://registry.npmjs.org/@videojs/http-streaming/-/http-streaming-2.10.0.tgz",
"integrity": "sha512-MoAp5BbexEqvCdyyazWH3thXpuOOUt82qjKB37VumXQiSZfNrIFgJRgdXOwHqsO4iEksWHSQRhJNxlTDMSP4dg==",
"requires": {
"@babel/runtime": "^7.12.5",
"@videojs/vhs-utils": "^3.0.0",
"@videojs/vhs-utils": "3.0.3",
"aes-decrypter": "3.1.2",
"global": "^4.4.0",
"m3u8-parser": "4.6.0",
"mpd-parser": "0.16.0",
"mux.js": "5.11.0",
"m3u8-parser": "4.7.0",
"mpd-parser": "0.18.0",
"mux.js": "5.12.2",
"video.js": "^6 || ^7"
},
"dependencies": {
"m3u8-parser": {
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/m3u8-parser/-/m3u8-parser-4.6.0.tgz",
"integrity": "sha512-dKhhpMcPqDM/KzULVrNyDZ/z766peQjwUghDTcl6TE7DQKAt/vm74/IMUAxpO34f6LDpM+OH/dYGQwW1eM4yWw==",
"requires": {
"@babel/runtime": "^7.12.5",
"@videojs/vhs-utils": "^3.0.0",
"global": "^4.4.0"
}
},
"mpd-parser": {
"version": "0.16.0",
"resolved": "https://registry.npmjs.org/mpd-parser/-/mpd-parser-0.16.0.tgz",
"integrity": "sha512-/pOFsDbOxXFAla47rYMdIypBZVtsQ9q3OHNuKtW2CJMaCGtNDtUcLS+B2TToYmB20rgi3XIgkyc2EsIvIAS4NA==",
"requires": {
"@babel/runtime": "^7.12.5",
"@videojs/vhs-utils": "^3.0.0",
"global": "^4.4.0",
"xmldom": "^0.5.0"
}
},
"mux.js": {
"version": "5.11.0",
"resolved": "https://registry.npmjs.org/mux.js/-/mux.js-5.11.0.tgz",
"integrity": "sha512-Q/iLfohHh5Pp6lW7EFtcxNuaCNJ3Ruywfy46pWLsY+yIxR1kXXImYY1wOhg8jLdBMs1kRaZqsiB4Zncsiw0a2Q==",
"requires": {
"@babel/runtime": "^7.11.2"
}
}
}
},
"@videojs/vhs-utils": {
@ -1392,9 +1361,9 @@
}
},
"@videojs/xhr": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@videojs/xhr/-/xhr-2.5.1.tgz",
"integrity": "sha512-wV9nGESHseSK+S9ePEru2+OJZ1jq/ZbbzniGQ4weAmTIepuBMSYPx5zrxxQA0E786T5ykpO8ts+LayV+3/oI2w==",
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/@videojs/xhr/-/xhr-2.6.0.tgz",
"integrity": "sha512-7J361GiN1tXpm+gd0xz2QWr3xNWBE+rytvo8J3KuggFaLg+U37gZQ2BuPLcnkfGffy2e+ozY70RHC8jt7zjA6Q==",
"requires": {
"@babel/runtime": "^7.5.5",
"global": "~4.4.0",
@ -1963,6 +1932,12 @@
"type-is": "~1.6.17"
}
},
"bootstrap": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.1.0.tgz",
"integrity": "sha512-bs74WNI9BgBo3cEovmdMHikSKoXnDgA6VQjJ7TyTotU6L7d41ZyCEEelPwkYEzsG/Zjv3ie9IE3EMAje0W9Xew==",
"dev": true
},
"boundary": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/boundary/-/boundary-1.0.1.tgz",
@ -6274,6 +6249,7 @@
"version": "0.25.7",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz",
"integrity": "sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==",
"dev": true,
"requires": {
"sourcemap-codec": "^1.4.4"
}
@ -7693,15 +7669,6 @@
"rollup-pluginutils": "^2.0.1"
}
},
"rollup-plugin-replace": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/rollup-plugin-replace/-/rollup-plugin-replace-2.2.0.tgz",
"integrity": "sha512-/5bxtUPkDHyBJAKketb4NfaeZjL5yLZdeUihSfbF2PQMz+rSTEb8ARKoOl3UBT4m7/X+QOXJo3sLTcq+yMMYTA==",
"requires": {
"magic-string": "^0.25.2",
"rollup-pluginutils": "^2.6.0"
}
},
"rollup-plugin-terser": {
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz",
@ -7727,6 +7694,7 @@
"version": "2.8.2",
"resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz",
"integrity": "sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ==",
"dev": true,
"requires": {
"estree-walker": "^0.6.1"
},
@ -7734,7 +7702,8 @@
"estree-walker": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.6.1.tgz",
"integrity": "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w=="
"integrity": "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==",
"dev": true
}
}
},
@ -8241,7 +8210,8 @@
"sourcemap-codec": {
"version": "1.4.8",
"resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz",
"integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA=="
"integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==",
"dev": true
},
"spawn-sync": {
"version": "1.0.15",
@ -9086,35 +9056,23 @@
}
},
"video.js": {
"version": "7.12.1",
"resolved": "https://registry.npmjs.org/video.js/-/video.js-7.12.1.tgz",
"integrity": "sha512-0Owl7q4Zbm6YHX94P9WVqQ2vnpfeNyOtTNwuTEEoKovZogoqV2McOUmsQGM4Edtg4vGTiP74Fv6HVa1V6FeRfg==",
"version": "7.15.0",
"resolved": "https://registry.npmjs.org/video.js/-/video.js-7.15.0.tgz",
"integrity": "sha512-ynUcjmbcfHfCqwqwAXAMGCcBeRgWBuqIW1PxomK6jfVhS8Vii+hckSBCVETpDPvVH6UmlsFLgJxADskyKdXj8A==",
"requires": {
"@babel/runtime": "^7.9.2",
"@videojs/http-streaming": "2.7.1",
"@videojs/xhr": "2.5.1",
"global": "4.3.2",
"@babel/runtime": "^7.12.5",
"@videojs/http-streaming": "2.10.0",
"@videojs/vhs-utils": "^3.0.3",
"@videojs/xhr": "2.6.0",
"aes-decrypter": "3.1.2",
"global": "^4.4.0",
"keycode": "^2.2.0",
"rollup-plugin-replace": "^2.2.0",
"m3u8-parser": "4.7.0",
"mpd-parser": "0.18.0",
"mux.js": "5.12.2",
"safe-json-parse": "4.0.0",
"videojs-font": "3.2.0",
"videojs-vtt.js": "^0.15.3"
},
"dependencies": {
"global": {
"version": "4.3.2",
"resolved": "https://registry.npmjs.org/global/-/global-4.3.2.tgz",
"integrity": "sha1-52mJJopsdMOJCLEwWxD8DjlOnQ8=",
"requires": {
"min-document": "^2.19.0",
"process": "~0.5.1"
}
},
"process": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/process/-/process-0.5.2.tgz",
"integrity": "sha1-FjjYqONML0QKkduVq5rrZ3/Bhc8="
}
}
},
"videojs-contrib-eme": {

1
package.json

@ -72,6 +72,7 @@
"@rollup/plugin-replace": "^2.3.4",
"@rollup/plugin-strip": "^2.0.1",
"@videojs/generator-helpers": "~2.0.1",
"bootstrap": "^5.1.0",
"d3": "^3.4.8",
"es5-shim": "^4.5.13",
"es6-shim": "^0.35.5",

707
scripts/index.js

@ -0,0 +1,707 @@
/* global window document */
/* eslint-disable vars-on-top, no-var, object-shorthand, no-console */
(function(window) {
var representationsEl = document.getElementById('representations');
representationsEl.addEventListener('change', function() {
var selectedIndex = representationsEl.selectedIndex;
if (!selectedIndex || selectedIndex < 1 || !window.vhs) {
return;
}
var selectedOption = representationsEl.options[representationsEl.selectedIndex];
if (!selectedOption) {
return;
}
var id = selectedOption.value;
window.vhs.representations().forEach(function(rep) {
rep.playlist.disabled = rep.id !== id;
});
window.mpc.fastQualityChange_();
});
var isManifestObjectType = function(url) {
return (/application\/vnd\.videojs\.vhs\+json/).test(url);
};
var hlsOptGroup = document.querySelector('[label="hls"]');
var dashOptGroup = document.querySelector('[label="dash"]');
var drmOptGroup = document.querySelector('[label="drm"]');
var liveOptGroup = document.querySelector('[label="live"]');
var llliveOptGroup = document.querySelector('[label="low latency live"]');
var manifestOptGroup = document.querySelector('[label="json manifest object"]');
// get the sources list squared away
var xhr = new window.XMLHttpRequest();
xhr.addEventListener('load', function() {
var sources = JSON.parse(xhr.responseText);
sources.forEach(function(source) {
var option = document.createElement('option');
option.innerText = source.name;
option.value = source.uri;
if (source.keySystems) {
option.setAttribute('data-key-systems', JSON.stringify(source.keySystems, null, 2));
}
if (source.mimetype) {
option.setAttribute('data-mimetype', source.mimetype);
}
if (source.features.indexOf('low-latency') !== -1) {
llliveOptGroup.appendChild(option);
} else if (source.features.indexOf('live') !== -1) {
liveOptGroup.appendChild(option);
} else if (source.keySystems) {
drmOptGroup.appendChild(option);
} else if (source.mimetype === 'application/x-mpegurl') {
hlsOptGroup.appendChild(option);
} else if (source.mimetype === 'application/dash+xml') {
dashOptGroup.appendChild(option);
}
});
});
xhr.open('GET', './scripts/sources.json');
xhr.send();
var hlsManifestXhr = new window.XMLHttpRequest();
hlsManifestXhr.addEventListener('load', function() {
var hlsManifest = hlsManifestXhr.responseText;
var option = document.createElement('option');
option.innerText = 'HLS Manifest Object Test, does not survive page reload';
option.value = `data:application/vnd.videojs.vhs+json,${hlsManifest}`;
manifestOptGroup.appendChild(option);
});
hlsManifestXhr.open('GET', './scripts/hls-manifest-object.json');
hlsManifestXhr.send();
var dashManifestXhr = new window.XMLHttpRequest();
dashManifestXhr.addEventListener('load', function() {
var dashManifest = dashManifestXhr.responseText;
var option = document.createElement('option');
option.innerText = 'Dash Manifest Object Test, does not survive page reload';
option.value = `data:application/vnd.videojs.vhs+json,${dashManifest}`;
manifestOptGroup.appendChild(option);
});
dashManifestXhr.open('GET', './scripts/dash-manifest-object.json');
dashManifestXhr.send();
// all relevant elements
var urlButton = document.getElementById('load-url');
var sources = document.getElementById('load-source');
var stateEls = {};
var getInputValue = function(el) {
if (el.type === 'url' || el.type === 'text' || el.nodeName.toLowerCase() === 'textarea') {
if (isManifestObjectType(el.value)) {
return '';
}
return encodeURIComponent(el.value);
} else if (el.type === 'select-one') {
return el.options[el.selectedIndex].value;
} else if (el.type === 'checkbox') {
return el.checked;
}
console.warn('unhandled input type ' + el.type);
return '';
};
var setInputValue = function(el, value) {
if (el.type === 'url' || el.type === 'text' || el.nodeName.toLowerCase() === 'textarea') {
el.value = decodeURIComponent(value);
} else if (el.type === 'select-one') {
for (var i = 0; i < el.options.length; i++) {
if (el.options[i].value === value) {
el.options[i].selected = true;
}
}
} else {
// get the `value` into a Boolean.
el.checked = JSON.parse(value);
}
};
var newEvent = function(name) {
var event;
if (typeof window.Event === 'function') {
event = new window.Event(name);
} else {
event = document.createEvent('Event');
event.initEvent(name, true, true);
}
return event;
};
// taken from video.js
var getFileExtension = function(path) {
var splitPathRe;
var pathParts;
if (typeof path === 'string') {
splitPathRe = /^(\/?)([\s\S]*?)((?:\.{1,2}|[^\/]*?)(\.([^\.\/\?]+)))(?:[\/]*|[\?].*)$/i;
pathParts = splitPathRe.exec(path);
if (pathParts) {
return pathParts.pop().toLowerCase();
}
}
return '';
};
var saveState = function() {
var query = '';
if (!window.history.replaceState) {
return;
}
Object.keys(stateEls).forEach(function(elName) {
var symbol = query.length ? '&' : '?';
query += symbol + elName + '=' + getInputValue(stateEls[elName]);
});
window.history.replaceState({}, 'vhs demo', query);
};
window.URLSearchParams = window.URLSearchParams || function(locationSearch) {
this.get = function(name) {
var results = new RegExp('[\?&]' + name + '=([^&#]*)').exec(locationSearch);
return results ? decodeURIComponent(results[1]) : null;
};
};
// eslint-disable-next-line
var loadState = function() {
var params = new window.URLSearchParams(window.location.search);
return Object.keys(stateEls).reduce(function(acc, elName) {
acc[elName] = typeof params.get(elName) !== 'object' ? params.get(elName) : getInputValue(stateEls[elName]);
return acc;
}, {});
};
// eslint-disable-next-line
var reloadScripts = function(urls, cb) {
var el = document.getElementById('reload-scripts');
if (!el) {
el = document.createElement('div');
el.id = 'reload-scripts';
document.body.appendChild(el);
}
while (el.firstChild) {
el.removeChild(el.firstChild);
}
var loaded = [];
var checkDone = function() {
if (loaded.length === urls.length) {
cb();
}
};
urls.forEach(function(url) {
var script = document.createElement('script');
// scripts marked as defer will be loaded asynchronously but will be executed in the order they are in the DOM
script.defer = true;
// dynamically created scripts are async by default unless otherwise specified
// async scripts are loaded asynchronously but also executed as soon as they are loaded
// we want to load them in the order they are added therefore we want to turn off async
script.async = false;
script.src = url;
script.onload = function() {
loaded.push(url);
checkDone();
};
el.appendChild(script);
});
};
var regenerateRepresentations = function() {
while (representationsEl.firstChild) {
representationsEl.removeChild(representationsEl.firstChild);
}
var selectedIndex;
window.vhs.representations().forEach(function(rep, i) {
var option = document.createElement('option');
option.value = rep.id;
option.innerText = JSON.stringify({
id: rep.id,
videoCodec: rep.codecs.video,
audioCodec: rep.codecs.audio,
bandwidth: rep.bandwidth,
heigth: rep.heigth,
width: rep.width
});
if (window.mpc.media().id === rep.id) {
selectedIndex = i;
}
representationsEl.appendChild(option);
});
representationsEl.selectedIndex = selectedIndex;
};
function getBuffered(buffered) {
var bufferedText = '';
if (!buffered) {
return bufferedText;
}
if (buffered.length) {
bufferedText += buffered.start(0) + ' - ' + buffered.end(0);
}
for (var i = 1; i < buffered.length; i++) {
bufferedText += ', ' + buffered.start(i) + ' - ' + buffered.end(i);
}
return bufferedText;
}
var setupSegmentMetadata = function(player) {
// setup segment metadata
var segmentMetadata = document.querySelector('#segment-metadata');
player.one('loadedmetadata', function() {
var tracks = player.textTracks();
var segmentMetadataTrack;
for (var i = 0; i < tracks.length; i++) {
if (tracks[i].label === 'segment-metadata') {
segmentMetadataTrack = tracks[i];
}
}
while (segmentMetadata.children.length) {
segmentMetadata.removeChild(segmentMetadata.firstChild);
}
if (segmentMetadataTrack) {
segmentMetadataTrack.addEventListener('cuechange', function() {
var cues = segmentMetadataTrack.activeCues || [];
while (segmentMetadata.children.length) {
segmentMetadata.removeChild(segmentMetadata.firstChild);
}
for (var j = 0; j < cues.length; j++) {
var text = JSON.stringify(JSON.parse(cues[j].text), null, 2);
var li = document.createElement('li');
var pre = document.createElement('pre');
pre.classList.add('border', 'rounded', 'p-2');
pre.textContent = text;
li.appendChild(pre);
segmentMetadata.appendChild(li);
}
});
}
});
};
var setupPlayerStats = function(player) {
var currentTimeStat = document.querySelector('.current-time-stat');
var bufferedStat = document.querySelector('.buffered-stat');
var videoBufferedStat = document.querySelector('.video-buffered-stat');
var audioBufferedStat = document.querySelector('.audio-buffered-stat');
var seekableStartStat = document.querySelector('.seekable-start-stat');
var seekableEndStat = document.querySelector('.seekable-end-stat');
var videoBitrateState = document.querySelector('.video-bitrate-stat');
var measuredBitrateStat = document.querySelector('.measured-bitrate-stat');
var videoTimestampOffset = document.querySelector('.video-timestampoffset');
var audioTimestampOffset = document.querySelector('.audio-timestampoffset');
player.on('timeupdate', function() {
currentTimeStat.textContent = player.currentTime().toFixed(1);
});
window.statsTimer = window.setInterval(function() {
var oldStart;
var oldEnd;
var seekable = player.seekable();
if (seekable && seekable.length) {
oldStart = seekableStartStat.textContent;
if (seekable.start(0).toFixed(1) !== oldStart) {
seekableStartStat.textContent = seekable.start(0).toFixed(1);
}
oldEnd = seekableEndStat.textContent;
if (seekable.end(0).toFixed(1) !== oldEnd) {
seekableEndStat.textContent = seekable.end(0).toFixed(1);
}
}
// buffered
bufferedStat.textContent = getBuffered(player.buffered());
// exit early if no VHS
if (!player.tech(true).vhs) {
videoBufferedStat.textContent = '';
audioBufferedStat.textContent = '';
videoBitrateState.textContent = '';
measuredBitrateStat.textContent = '';
videoTimestampOffset.textContent = '';
audioTimestampOffset.textContent = '';
return;
}
videoBufferedStat.textContent = getBuffered(player.tech(true).vhs.masterPlaylistController_.mainSegmentLoader_.sourceUpdater_.videoBuffer &&
player.tech(true).vhs.masterPlaylistController_.mainSegmentLoader_.sourceUpdater_.videoBuffer.buffered);
// demuxed audio
var audioBuffer = getBuffered(player.tech(true).vhs.masterPlaylistController_.audioSegmentLoader_.sourceUpdater_.audioBuffer &&
player.tech(true).vhs.masterPlaylistController_.audioSegmentLoader_.sourceUpdater_.audioBuffer.buffered);
// muxed audio
if (!audioBuffer) {
audioBuffer = getBuffered(player.tech(true).vhs.masterPlaylistController_.mainSegmentLoader_.sourceUpdater_.audioBuffer &&
player.tech(true).vhs.masterPlaylistController_.mainSegmentLoader_.sourceUpdater_.audioBuffer.buffered);
}
audioBufferedStat.textContent = audioBuffer;
if (player.tech(true).vhs.masterPlaylistController_.audioSegmentLoader_.sourceUpdater_.audioBuffer) {
audioTimestampOffset.textContent = player.tech(true).vhs.masterPlaylistController_.audioSegmentLoader_.sourceUpdater_.audioBuffer.timestampOffset;
} else if (player.tech(true).vhs.masterPlaylistController_.mainSegmentLoader_.sourceUpdater_.audioBuffer) {
audioTimestampOffset.textContent = player.tech(true).vhs.masterPlaylistController_.mainSegmentLoader_.sourceUpdater_.audioBuffer.timestampOffset;
}
if (player.tech(true).vhs.masterPlaylistController_.mainSegmentLoader_.sourceUpdater_.videoBuffer) {
videoTimestampOffset.textContent = player.tech(true).vhs.masterPlaylistController_.mainSegmentLoader_.sourceUpdater_.videoBuffer.timestampOffset;
}
// bitrates
var playlist = player.tech_.vhs.playlists.media();
if (playlist && playlist.attributes && playlist.attributes.BANDWIDTH) {
videoBitrateState.textContent = (playlist.attributes.BANDWIDTH / 1024).toLocaleString(undefined, {
maximumFractionDigits: 1
}) + ' kbps';
}
if (player.tech_.vhs.bandwidth) {
measuredBitrateStat.textContent = (player.tech_.vhs.bandwidth / 1024).toLocaleString(undefined, {
maximumFractionDigits: 1
}) + ' kbps';
}
}, 100);
};
[
'debug',
'autoplay',
'muted',
'fluid',
'minified',
'sync-workers',
'liveui',
'llhls',
'url',
'type',
'keysystems',
'buffer-water',
'exact-manifest-timings',
'pixel-diff-selector',
'override-native',
'preload',
'mirror-source'
].forEach(function(name) {
stateEls[name] = document.getElementById(name);
});
window.startDemo = function(cb) {
var state = loadState();
Object.keys(state).forEach(function(elName) {
setInputValue(stateEls[elName], state[elName]);
});
Array.prototype.forEach.call(sources.options, function(s, i) {
if (s.value === state.url) {
sources.selectedIndex = i;
}
});
stateEls.fluid.addEventListener('change', function(event) {
saveState();
if (event.target.checked) {
window['player-fixture'].style.aspectRatio = '16/9';
window['player-fixture'].style.minHeight = 'initial';
} else {
window['player-fixture'].style.aspectRatio = '';
window['player-fixture'].style.minHeight = '250px';
}
window.player.fluid(event.target.checked);
});
stateEls.muted.addEventListener('change', function(event) {
saveState();
window.player.muted(event.target.checked);
});
stateEls.autoplay.addEventListener('change', function(event) {
saveState();
window.player.autoplay(event.target.checked);
});
// stateEls that reload the player and scripts
[
'mirror-source',
'sync-workers',
'preload',
'llhls',
'buffer-water',
'override-native',
'liveui',
'pixel-diff-selector',
'exact-manifest-timings'
].forEach(function(name) {
stateEls[name].addEventListener('change', function(event) {
saveState();
stateEls.minified.dispatchEvent(newEvent('change'));
});
});
stateEls.debug.addEventListener('change', function(event) {
saveState();
window.videojs.log.level(event.target.checked ? 'debug' : 'info');
});
stateEls.minified.addEventListener('change', function(event) {
var urls = [
'node_modules/video.js/dist/alt/video.core',
'node_modules/videojs-contrib-eme/dist/videojs-contrib-eme',
'node_modules/videojs-contrib-quality-levels/dist/videojs-contrib-quality-levels',
'node_modules/videojs-http-source-selector/dist/videojs-http-source-selector'
].map(function(url) {
return url + (event.target.checked ? '.min' : '') + '.js';
});
if (stateEls['sync-workers'].checked) {
urls.push('dist/videojs-http-streaming-sync-workers.js');
} else {
urls.push('dist/videojs-http-streaming' + (event.target.checked ? '.min' : '') + '.js');
}
saveState();
if (window.player) {
window.player.dispose();
delete window.player;
}
if (window.videojs) {
delete window.videojs;
}
reloadScripts(urls, function() {
var player;
var fixture = document.getElementById('player-fixture');
var videoEl = document.createElement('video-js');
videoEl.setAttribute('controls', '');
videoEl.setAttribute('preload', stateEls.preload.options[stateEls.preload.selectedIndex].value || 'auto');
videoEl.className = 'vjs-default-skin';
fixture.appendChild(videoEl);
var mirrorSource = getInputValue(stateEls['mirror-source']);
if (window.statsTimer) {
window.statsTimer = null;
clearInterval(window.statsTimer);
}
player = window.player = window.videojs(videoEl, {
plugins: {
httpSourceSelector: {
default: 'auto'
}
},
liveui: stateEls.liveui.checked,
enableSourceset: mirrorSource,
html5: {
vhs: {
overrideNative: getInputValue(stateEls['override-native']),
experimentalBufferBasedABR: getInputValue(stateEls['buffer-water']),
experimentalLLHLS: getInputValue(stateEls.llhls),
experimentalExactManifestTimings: getInputValue(stateEls['exact-manifest-timings']),
experimentalLeastPixelDiffSelector: getInputValue(stateEls['pixel-diff-selector'])
}
}
});
setupPlayerStats(player);
setupSegmentMetadata(player);
player.on('sourceset', function() {
var source = player.currentSource();
if (source.keySystems) {
var copy = JSON.parse(JSON.stringify(source.keySystems));
// have to delete pssh as it will often make keySystems too big
// for a uri
Object.keys(copy).forEach(function(key) {
if (copy[key].hasOwnProperty('pssh')) {
delete copy[key].pssh;
}
});
stateEls.keysystems.value = JSON.stringify(copy, null, 2);
}
if (source.src) {
stateEls.url.value = encodeURI(source.src);
}
if (source.type) {
stateEls.type.value = source.type;
}
saveState();
});
player.width(640);
player.height(264);
// configure videojs-contrib-eme
player.eme();
stateEls.debug.dispatchEvent(newEvent('change'));
stateEls.muted.dispatchEvent(newEvent('change'));
stateEls.fluid.dispatchEvent(newEvent('change'));
stateEls.autoplay.dispatchEvent(newEvent('change'));
// run the load url handler for the intial source
if (stateEls.url.value) {
urlButton.dispatchEvent(newEvent('click'));
} else {
sources.dispatchEvent(newEvent('change'));
}
player.on('loadedmetadata', function() {
if (player.tech_.vhs) {
window.vhs = player.tech_.vhs;
window.mpc = player.tech_.vhs.masterPlaylistController_;
window.mpc.masterPlaylistLoader_.on('mediachange', regenerateRepresentations);
regenerateRepresentations();
} else {
window.vhs = null;
window.mpc = null;
}
});
cb(player);
});
});
var urlButtonClick = function(event) {
var ext;
var type = stateEls.type.value;
// reset type if it's a manifest object's type
if (type === 'application/vnd.videojs.vhs+json') {
type = '';
}
if (isManifestObjectType(stateEls.url.value)) {
type = 'application/vnd.videojs.vhs+json';
}
if (!type.trim()) {
ext = getFileExtension(stateEls.url.value);
if (ext === 'mpd') {
type = 'application/dash+xml';
} else if (ext === 'm3u8') {
type = 'application/x-mpegURL';
}
}
saveState();
var source = {
src: stateEls.url.value,
type: type
};
if (stateEls.keysystems.value) {
source.keySystems = JSON.parse(stateEls.keysystems.value);
}
sources.selectedIndex = -1;
Array.prototype.forEach.call(sources.options, function(s, i) {
if (s.value === stateEls.url.value) {
sources.selectedIndex = i;
}
});
window.player.src(source);
};
urlButton.addEventListener('click', urlButtonClick);
urlButton.addEventListener('tap', urlButtonClick);
sources.addEventListener('change', function(event) {
var selectedOption = sources.options[sources.selectedIndex];
if (!selectedOption) {
return;
}
var src = selectedOption.value;
stateEls.url.value = src;
stateEls.type.value = selectedOption.getAttribute('data-mimetype');
stateEls.keysystems.value = selectedOption.getAttribute('data-key-systems');
urlButton.dispatchEvent(newEvent('click'));
});
stateEls.url.addEventListener('keyup', function(event) {
if (event.key === 'Enter') {
urlButton.click();
}
});
stateEls.url.addEventListener('input', function(event) {
if (stateEls.type.value.length) {
stateEls.type.value = '';
}
});
stateEls.type.addEventListener('keyup', function(event) {
if (event.key === 'Enter') {
urlButton.click();
}
});
// run the change handler for the first time
stateEls.minified.dispatchEvent(newEvent('change'));
};
}(window));

7
scripts/netlify.js

@ -13,10 +13,13 @@ const files = [
'node_modules/videojs-http-source-selector/dist/videojs-http-source-selector.css',
'node_modules/videojs-http-source-selector/dist/videojs-http-source-selector.js',
'node_modules/videojs-http-source-selector/dist/videojs-http-source-selector.min.js',
'node_modules/bootstrap/dist/js/bootstrap.js',
'node_modules/bootstrap/dist/css/bootstrap.css',
'node_modules/d3/d3.min.js',
'logo.svg',
'scripts/sources.json',
'scripts/index-demo-page.js',
'scripts/index.js',
'scripts/old-index.js',
'scripts/dash-manifest-object.json',
'scripts/hls-manifest-object.json'
];
@ -33,5 +36,5 @@ files
// copy files/folders to deploy dir
files
.concat('dist', 'index.html', 'utils')
.concat('dist', 'index.html', 'old-index.html', 'utils')
.forEach((file) => sh.cp('-r', file, path.join(deployDir, file)));

4
scripts/index-demo-page.js → scripts/old-index.js

@ -77,7 +77,7 @@
var option = document.createElement('option');
option.innerText = 'HLS Manifest Object Test, does not survive page reload';
option.value = `data:application/vnd.videojs.vhs+json,${hlsManifest}`;
option.value = 'data:application/vnd.videojs.vhs+json,' + hlsManifest;
manifestOptGroup.appendChild(option);
});
@ -91,7 +91,7 @@
var option = document.createElement('option');
option.innerText = 'Dash Manifest Object Test, does not survive page reload';
option.value = `data:application/vnd.videojs.vhs+json,${dashManifest}`;
option.value = 'data:application/vnd.videojs.vhs+json,' + dashManifest;
manifestOptGroup.appendChild(option);
});
Loading…
Cancel
Save