Graph bandwidth on the stats page
Fix the stats page to work with video.js 5. Get the measured bitrate graph updating in real time.
Showing
3 changed files
with
82 additions
and
32 deletions
... | @@ -23,7 +23,9 @@ | ... | @@ -23,7 +23,9 @@ |
23 | 23 | ||
24 | <!-- Media Sources plugin --> | 24 | <!-- Media Sources plugin --> |
25 | <script src="../../node_modules/videojs-contrib-media-sources/src/videojs-media-sources.js"></script> | 25 | <script src="../../node_modules/videojs-contrib-media-sources/src/videojs-media-sources.js"></script> |
26 | 26 | <script> | |
27 | videojs.MediaSource.webWorkerURI = '../../node_modules/videojs-contrib-media-sources/src/transmuxer_worker.js'; | ||
28 | </script> | ||
27 | <!-- HLS plugin --> | 29 | <!-- HLS plugin --> |
28 | <script src="../../src/videojs-hls.js"></script> | 30 | <script src="../../src/videojs-hls.js"></script> |
29 | 31 | ||
... | @@ -77,12 +79,11 @@ | ... | @@ -77,12 +79,11 @@ |
77 | </video> | 79 | </video> |
78 | <section class="stats"> | 80 | <section class="stats"> |
79 | <h2>Player Stats</h2> | 81 | <h2>Player Stats</h2> |
80 | <div class="segment-timeline"></div> | ||
81 | <dl> | 82 | <dl> |
82 | <dt>Current Time:</dt> | 83 | <dt>Current Time:</dt> |
83 | <dd class="current-time-stat">0</dd> | 84 | <dd class="current-time-stat">0</dd> |
84 | <dt>Buffered:</dt> | 85 | <dt>Buffered:</dt> |
85 | <dd><span class="buffered-start-stat">-</span> - <span class="buffered-end-stat">-</span></dd> | 86 | <dd class="buffered-stat">-</dd> |
86 | <dt>Seekable:</dt> | 87 | <dt>Seekable:</dt> |
87 | <dd><span class="seekable-start-stat">-</span> - <span class="seekable-end-stat">-</span></dd> | 88 | <dd><span class="seekable-start-stat">-</span> - <span class="seekable-end-stat">-</span></dd> |
88 | <dt>Video Bitrate:</dt> | 89 | <dt>Video Bitrate:</dt> |
... | @@ -90,10 +91,13 @@ | ... | @@ -90,10 +91,13 @@ |
90 | <dt>Measured Bitrate:</dt> | 91 | <dt>Measured Bitrate:</dt> |
91 | <dd class="measured-bitrate-stat">0 kbps</dd> | 92 | <dd class="measured-bitrate-stat">0 kbps</dd> |
92 | </dl> | 93 | </dl> |
94 | <h3>Bitrate Switching</h3> | ||
93 | <div class="switching-stats"> | 95 | <div class="switching-stats"> |
94 | Once the player begins loading, you'll see information about the | 96 | Once the player begins loading, you'll see information about the |
95 | operation of the adaptive quality switching here. | 97 | operation of the adaptive quality switching here. |
96 | </div> | 98 | </div> |
99 | <h3>Timed Metadata</h3> | ||
100 | <div class="segment-timeline"></div> | ||
97 | </section> | 101 | </section> |
98 | 102 | ||
99 | <script src="stats.js"></script> | 103 | <script src="stats.js"></script> |
... | @@ -107,8 +111,7 @@ | ... | @@ -107,8 +111,7 @@ |
107 | // ------------ | 111 | // ------------ |
108 | 112 | ||
109 | var currentTimeStat = document.querySelector('.current-time-stat'); | 113 | var currentTimeStat = document.querySelector('.current-time-stat'); |
110 | var bufferedStartStat = document.querySelector('.buffered-start-stat'); | 114 | var bufferedStat = document.querySelector('.buffered-stat'); |
111 | var bufferedEndStat = document.querySelector('.buffered-end-stat'); | ||
112 | var seekableStartStat = document.querySelector('.seekable-start-stat'); | 115 | var seekableStartStat = document.querySelector('.seekable-start-stat'); |
113 | var seekableEndStat = document.querySelector('.seekable-end-stat'); | 116 | var seekableEndStat = document.querySelector('.seekable-end-stat'); |
114 | var videoBitrateState = document.querySelector('.video-bitrate-stat'); | 117 | var videoBitrateState = document.querySelector('.video-bitrate-stat'); |
... | @@ -119,20 +122,17 @@ | ... | @@ -119,20 +122,17 @@ |
119 | }); | 122 | }); |
120 | 123 | ||
121 | player.on('progress', function() { | 124 | player.on('progress', function() { |
122 | var oldStart, oldEnd; | 125 | var bufferedText = '', oldStart, oldEnd, i; |
126 | |||
123 | // buffered | 127 | // buffered |
124 | var buffered = player.buffered(); | 128 | var buffered = player.buffered(); |
125 | if (buffered.length) { | 129 | if (buffered.length) { |
126 | 130 | bufferedText += buffered.start(0) + ' - ' + buffered.end(0); | |
127 | oldStart = bufferedStartStat.textContent; | 131 | } |
128 | if (buffered.start(0).toFixed(1) !== oldStart) { | 132 | for (i = 1; i < buffered.length; i++) { |
129 | bufferedStartStat.textContent = buffered.start(0).toFixed(1); | 133 | bufferedText += ', ' + buffered.start(i) + ' - ' + buffered.end(i); |
130 | } | ||
131 | oldEnd = bufferedEndStat.textContent; | ||
132 | if (buffered.end(0).toFixed(1) !== oldEnd) { | ||
133 | bufferedEndStat.textContent = buffered.end(0).toFixed(1); | ||
134 | } | ||
135 | } | 134 | } |
135 | bufferedStat.textContent = bufferedText; | ||
136 | 136 | ||
137 | // seekable | 137 | // seekable |
138 | var seekable = player.seekable(); | 138 | var seekable = player.seekable(); |
... | @@ -149,14 +149,14 @@ | ... | @@ -149,14 +149,14 @@ |
149 | } | 149 | } |
150 | 150 | ||
151 | // bitrates | 151 | // bitrates |
152 | var playlist = player.tech.hls.playlists.media(); | 152 | var playlist = player.tech_.hls.playlists.media(); |
153 | if (playlist && playlist.attributes && playlist.attributes.BANDWIDTH) { | 153 | if (playlist && playlist.attributes && playlist.attributes.BANDWIDTH) { |
154 | videoBitrateState.textContent = (playlist.attributes.BANDWIDTH / 1024).toLocaleString(undefined, { | 154 | videoBitrateState.textContent = (playlist.attributes.BANDWIDTH / 1024).toLocaleString(undefined, { |
155 | maximumFractionDigits: 1 | 155 | maximumFractionDigits: 1 |
156 | }) + ' kbps'; | 156 | }) + ' kbps'; |
157 | } | 157 | } |
158 | if (player.tech.hls.bandwidth) { | 158 | if (player.tech_.hls.bandwidth) { |
159 | measuredBitrateStat.textContent = (player.tech.hls.bandwidth / 1024).toLocaleString(undefined, { | 159 | measuredBitrateStat.textContent = (player.tech_.hls.bandwidth / 1024).toLocaleString(undefined, { |
160 | maximumFractionDigits: 1 | 160 | maximumFractionDigits: 1 |
161 | }) + ' kbps'; | 161 | }) + ' kbps'; |
162 | } | 162 | } | ... | ... |
... | @@ -4,10 +4,15 @@ | ... | @@ -4,10 +4,15 @@ |
4 | } | 4 | } |
5 | 5 | ||
6 | .axis line, | 6 | .axis line, |
7 | .axis path, | 7 | .axis path { |
8 | .intersect { | 8 | fill: none; |
9 | stroke: #111; | ||
10 | } | ||
11 | |||
12 | .bitrates { | ||
9 | fill: none; | 13 | fill: none; |
10 | stroke: #000; | 14 | stroke: steelblue; |
15 | stroke-width: 3px; | ||
11 | } | 16 | } |
12 | 17 | ||
13 | .cue { | 18 | .cue { |
... | @@ -23,6 +28,6 @@ | ... | @@ -23,6 +28,6 @@ |
23 | 28 | ||
24 | .intersect { | 29 | .intersect { |
25 | fill: none; | 30 | fill: none; |
26 | stroke: #000; | 31 | stroke: #111; |
27 | stroke-dasharray: 2,2; | 32 | stroke-dasharray: 2,2; |
28 | } | 33 | } | ... | ... |
... | @@ -7,9 +7,35 @@ | ... | @@ -7,9 +7,35 @@ |
7 | 7 | ||
8 | var d3 = window.d3; | 8 | var d3 = window.d3; |
9 | 9 | ||
10 | var setupGraph = function(element) { | 10 | var bitrateTickFormatter = d3.format(',.0f'); |
11 | element.innerHTML = ''; | ||
12 | 11 | ||
12 | var updateBitrateAxes = function(svg, xScale, yScale) { | ||
13 | var xAxis = d3.svg.axis().scale(xScale).orient('bottom'); | ||
14 | svg.select('.axis.x') | ||
15 | .transition().duration(500) | ||
16 | .call(xAxis); | ||
17 | |||
18 | var yAxis = d3.svg.axis().scale(yScale) | ||
19 | .tickFormat(function(value) { | ||
20 | return bitrateTickFormatter(value / 1024); | ||
21 | }).orient('left'); | ||
22 | svg.select('.axis.y') | ||
23 | .transition().duration(500) | ||
24 | .call(yAxis); | ||
25 | }; | ||
26 | |||
27 | var updateBitrates = function(svg, x, y, measuredBitrateKbps) { | ||
28 | var bitrates, line; | ||
29 | |||
30 | bitrates = svg.selectAll('.bitrates').datum(measuredBitrateKbps); | ||
31 | line = d3.svg.line() | ||
32 | .x(function(bitrate) { return x(bitrate.time); }) | ||
33 | .y(function(bitrate) { return y(bitrate.value); }); | ||
34 | |||
35 | bitrates.transition().duration(500).attr('d', line); | ||
36 | }; | ||
37 | |||
38 | var setupGraph = function(element, player) { | ||
13 | // setup the display | 39 | // setup the display |
14 | var margin = { | 40 | var margin = { |
15 | top: 20, | 41 | top: 20, |
... | @@ -30,15 +56,14 @@ | ... | @@ -30,15 +56,14 @@ |
30 | var x = d3.time.scale().range([0, width]); // d3.scale.linear().range([0, width]); | 56 | var x = d3.time.scale().range([0, width]); // d3.scale.linear().range([0, width]); |
31 | var y = d3.scale.linear().range([height, 0]); | 57 | var y = d3.scale.linear().range([height, 0]); |
32 | 58 | ||
33 | x.domain([new Date(), new Date(Date.now() + (5 * 60 * 1000))]); | 59 | x.domain([new Date(), new Date(Date.now() + (1 * 60 * 1000))]); |
34 | y.domain([0, 5 * 1024 * 1024 * 8]); | 60 | y.domain([0, 5 * 1024 * 1024 * 8]); |
35 | 61 | ||
36 | var timeAxis = d3.svg.axis().scale(x).orient('bottom'); | 62 | var timeAxis = d3.svg.axis().scale(x).orient('bottom'); |
37 | var tickFormatter = d3.format(',.0f'); | ||
38 | var bitrateAxis = d3.svg.axis() | 63 | var bitrateAxis = d3.svg.axis() |
39 | .scale(y) | 64 | .scale(y) |
40 | .tickFormat(function(value) { | 65 | .tickFormat(function(value) { |
41 | return tickFormatter(value / 1024); | 66 | return bitrateTickFormatter(value / 1024); |
42 | }) | 67 | }) |
43 | .orient('left'); | 68 | .orient('left'); |
44 | 69 | ||
... | @@ -60,6 +85,26 @@ | ... | @@ -60,6 +85,26 @@ |
60 | .style('text-anchor', 'end') | 85 | .style('text-anchor', 'end') |
61 | .text('Bitrate (kb/s)'); | 86 | .text('Bitrate (kb/s)'); |
62 | 87 | ||
88 | svg.append('path') | ||
89 | .attr('class', 'bitrates'); | ||
90 | |||
91 | var measuredBitrateKbps = [{ | ||
92 | time: new Date(), | ||
93 | value: player.tech_.hls.bandwidth || 0 | ||
94 | }]; | ||
95 | |||
96 | player.on('progress', function() { | ||
97 | measuredBitrateKbps.push({ | ||
98 | time: new Date(), | ||
99 | value: player.tech_.hls.bandwidth || 0 | ||
100 | }); | ||
101 | x.domain([x.domain()[0], new Date()]); | ||
102 | y.domain([0, d3.max(measuredBitrateKbps, function(bitrate) { | ||
103 | return bitrate.value; | ||
104 | })]); | ||
105 | updateBitrateAxes(svg, x, y); | ||
106 | updateBitrates(svg, x, y, measuredBitrateKbps); | ||
107 | }); | ||
63 | }; | 108 | }; |
64 | 109 | ||
65 | // --------------- | 110 | // --------------- |
... | @@ -86,8 +131,8 @@ | ... | @@ -86,8 +131,8 @@ |
86 | 131 | ||
87 | var mediaDomain = function(media, player) { | 132 | var mediaDomain = function(media, player) { |
88 | var segments = media.segments; | 133 | var segments = media.segments; |
89 | var end = player.tech.hls.playlists.expiredPreDiscontinuity_; | 134 | var end = player.tech_.hls.playlists.expiredPreDiscontinuity_; |
90 | end += player.tech.hls.playlists.expiredPostDiscontinuity_; | 135 | end += player.tech_.hls.playlists.expiredPostDiscontinuity_; |
91 | end += Playlist.duration(media, | 136 | end += Playlist.duration(media, |
92 | media.mediaSequence, | 137 | media.mediaSequence, |
93 | media.mediaSequence + segments.length); | 138 | media.mediaSequence + segments.length); |
... | @@ -160,7 +205,7 @@ | ... | @@ -160,7 +205,7 @@ |
160 | .call(ptsAxis); | 205 | .call(ptsAxis); |
161 | }; | 206 | }; |
162 | var svgRenderSegmentTimeline = function(container, player) { | 207 | var svgRenderSegmentTimeline = function(container, player) { |
163 | var media = player.tech.hls.playlists.media(); | 208 | var media = player.tech_.hls.playlists.media(); |
164 | var segments = media.segments; // media.segments.slice(0, count); | 209 | var segments = media.segments; // media.segments.slice(0, count); |
165 | 210 | ||
166 | // setup the display | 211 | // setup the display |
... | @@ -196,7 +241,7 @@ | ... | @@ -196,7 +241,7 @@ |
196 | 241 | ||
197 | // update everything on progress | 242 | // update everything on progress |
198 | player.on('progress', function() { | 243 | player.on('progress', function() { |
199 | var updatedMedia = player.tech.hls.playlists.media(); | 244 | var updatedMedia = player.tech_.hls.playlists.media(); |
200 | var segments = updatedMedia.segments; // updatedMedia.segments.slice(currentIndex, currentIndex + count); | 245 | var segments = updatedMedia.segments; // updatedMedia.segments.slice(currentIndex, currentIndex + count); |
201 | 246 | ||
202 | if (updatedMedia.mediaSequence !== media.mediaSequence) { | 247 | if (updatedMedia.mediaSequence !== media.mediaSequence) { |
... | @@ -220,7 +265,7 @@ | ... | @@ -220,7 +265,7 @@ |
220 | }; | 265 | }; |
221 | 266 | ||
222 | var displayCues = function(container, player) { | 267 | var displayCues = function(container, player) { |
223 | var media = player.tech.hls.playlists.media(); | 268 | var media = player.tech_.hls.playlists.media(); |
224 | if (media && media.segments) { | 269 | if (media && media.segments) { |
225 | svgRenderSegmentTimeline(container, player); | 270 | svgRenderSegmentTimeline(container, player); |
226 | } else { | 271 | } else { | ... | ... |
-
Please register or sign in to post a comment