Move stats out to a dedicated page
Keep the example page simple. Add a live graph of cue point PTS position versus media timeline position. Update the display so it works with live content.
Showing
5 changed files
with
432 additions
and
76 deletions
... | @@ -71,86 +71,11 @@ | ... | @@ -71,86 +71,11 @@ |
71 | src="http://solutions.brightcove.com/jwhisenant/hls/apple/bipbop/bipbopall.m3u8" | 71 | src="http://solutions.brightcove.com/jwhisenant/hls/apple/bipbop/bipbopall.m3u8" |
72 | type="application/x-mpegURL"> | 72 | type="application/x-mpegURL"> |
73 | </video> | 73 | </video> |
74 | <section class="stats"> | ||
75 | <h2>Player Stats</h2> | ||
76 | <dl> | ||
77 | <dt>Current Time:</dt> | ||
78 | <dd class="current-time-stat">0</dd> | ||
79 | <dt>Buffered:</dt> | ||
80 | <dd><span class="buffered-start-stat">-</span> - <span class="buffered-end-stat">-</span></dd> | ||
81 | <dt>Seekable:</dt> | ||
82 | <dd><span class="seekable-start-stat">-</span> - <span class="seekable-end-stat">-</span></dd> | ||
83 | <dt>Video Bitrate:</dt> | ||
84 | <dd class="video-bitrate-stat">0 kbps</dd> | ||
85 | <dt>Measured Bitrate:</dt> | ||
86 | <dd class="measured-bitrate-stat">0 kbps</dd> | ||
87 | </dl> | ||
88 | </section> | ||
89 | <script> | 74 | <script> |
90 | videojs.options.flash.swf = 'node_modules/videojs-swf/dist/video-js.swf'; | 75 | videojs.options.flash.swf = 'node_modules/videojs-swf/dist/video-js.swf'; |
76 | |||
91 | // initialize the player | 77 | // initialize the player |
92 | var player = videojs('video'); | 78 | var player = videojs('video'); |
93 | |||
94 | // ------------ | ||
95 | // Player Stats | ||
96 | // ------------ | ||
97 | |||
98 | var currentTimeStat = document.querySelector('.current-time-stat'); | ||
99 | var bufferedStartStat = document.querySelector('.buffered-start-stat'); | ||
100 | var bufferedEndStat = document.querySelector('.buffered-end-stat'); | ||
101 | var seekableStartStat = document.querySelector('.seekable-start-stat'); | ||
102 | var seekableEndStat = document.querySelector('.seekable-end-stat'); | ||
103 | var videoBitrateState = document.querySelector('.video-bitrate-stat'); | ||
104 | var measuredBitrateStat = document.querySelector('.measured-bitrate-stat'); | ||
105 | |||
106 | |||
107 | player.on('timeupdate', function() { | ||
108 | currentTimeStat.textContent = player.currentTime().toFixed(1); | ||
109 | }); | ||
110 | |||
111 | player.on('progress', function() { | ||
112 | var oldStart, oldEnd; | ||
113 | // buffered | ||
114 | var buffered = player.buffered(); | ||
115 | if (buffered.length) { | ||
116 | |||
117 | oldStart = bufferedStartStat.textContent; | ||
118 | if (buffered.start(0).toFixed(1) !== oldStart) { | ||
119 | bufferedStartStat.textContent = buffered.start(0).toFixed(1); | ||
120 | } | ||
121 | oldEnd = bufferedEndStat.textContent; | ||
122 | if (buffered.end(0).toFixed(1) !== oldEnd) { | ||
123 | bufferedEndStat.textContent = buffered.end(0).toFixed(1); | ||
124 | } | ||
125 | } | ||
126 | |||
127 | // seekable | ||
128 | var seekable = player.seekable(); | ||
129 | if (seekable && seekable.length) { | ||
130 | |||
131 | oldStart = seekableStartStat.textContent; | ||
132 | if (seekable.start(0).toFixed(1) !== oldStart) { | ||
133 | seekableStartStat.textContent = seekable.start(0).toFixed(1); | ||
134 | } | ||
135 | oldEnd = seekableEndStat.textContent; | ||
136 | if (seekable.end(0).toFixed(1) !== oldEnd) { | ||
137 | seekableEndStat.textContent = seekable.end(0).toFixed(1); | ||
138 | } | ||
139 | } | ||
140 | |||
141 | // bitrates | ||
142 | var playlist = player.hls.playlists.media(); | ||
143 | if (playlist && playlist.attributes.BANDWIDTH) { | ||
144 | videoBitrateState.textContent = (playlist.attributes.BANDWIDTH / 1024).toLocaleString(undefined, { | ||
145 | maximumFractionDigits: 1 | ||
146 | }) + ' kbps'; | ||
147 | } | ||
148 | if (player.hls.bandwidth) { | ||
149 | measuredBitrateStat.textContent = (player.hls.bandwidth / 1024).toLocaleString(undefined, { | ||
150 | maximumFractionDigits: 1 | ||
151 | }) + ' kbps'; | ||
152 | } | ||
153 | }); | ||
154 | </script> | 79 | </script> |
155 | </body> | 80 | </body> |
156 | </html> | 81 | </html> | ... | ... |
... | @@ -329,6 +329,7 @@ videojs.Hls.prototype.addCuesForMetadata_ = function(segmentInfo) { | ... | @@ -329,6 +329,7 @@ videojs.Hls.prototype.addCuesForMetadata_ = function(segmentInfo) { |
329 | time = segmentOffset + ((metadata.pts - minPts) * 0.001); | 329 | time = segmentOffset + ((metadata.pts - minPts) * 0.001); |
330 | cue = new window.VTTCue(time, time, frame.value || frame.url || ''); | 330 | cue = new window.VTTCue(time, time, frame.value || frame.url || ''); |
331 | cue.frame = frame; | 331 | cue.frame = frame; |
332 | cue.pts_ = metadata.pts; | ||
332 | textTrack.addCue(cue); | 333 | textTrack.addCue(cue); |
333 | } | 334 | } |
334 | segmentInfo.pendingMetadata.shift(); | 335 | segmentInfo.pendingMetadata.shift(); | ... | ... |
test/stats/index.html
0 → 100644
1 | <!DOCTYPE html> | ||
2 | <html> | ||
3 | <head> | ||
4 | <meta charset="utf-8"> | ||
5 | <title>video.js HLS Stats</title> | ||
6 | |||
7 | <link href="../../node_modules/video.js/dist/video-js/video-js.css" rel="stylesheet"> | ||
8 | |||
9 | <!-- video.js --> | ||
10 | <script src="../../node_modules/video.js/dist/video-js/video.dev.js"></script> | ||
11 | |||
12 | <!-- Media Sources plugin --> | ||
13 | <script src="../../node_modules/videojs-contrib-media-sources/src/videojs-media-sources.js"></script> | ||
14 | |||
15 | <!-- HLS plugin --> | ||
16 | <script src="../../src/videojs-hls.js"></script> | ||
17 | |||
18 | <!-- segment handling --> | ||
19 | <script src="../../src/xhr.js"></script> | ||
20 | <script src="../../src/flv-tag.js"></script> | ||
21 | <script src="../../src/stream.js"></script> | ||
22 | <script src="../../src/exp-golomb.js"></script> | ||
23 | <script src="../../src/h264-extradata.js"></script> | ||
24 | <script src="../../src/h264-stream.js"></script> | ||
25 | <script src="../../src/aac-stream.js"></script> | ||
26 | <script src="../../src/metadata-stream.js"></script> | ||
27 | <script src="../../src/segment-parser.js"></script> | ||
28 | |||
29 | <!-- m3u8 handling --> | ||
30 | <script src="../../src/m3u8/m3u8-parser.js"></script> | ||
31 | <script src="../../src/playlist.js"></script> | ||
32 | <script src="../../src/playlist-loader.js"></script> | ||
33 | |||
34 | <script src="../../node_modules/pkcs7/dist/pkcs7.unpad.js"></script> | ||
35 | <script src="../../src/decrypter.js"></script> | ||
36 | |||
37 | |||
38 | <!-- player stats visualization --> | ||
39 | <link href="stats.css" rel="stylesheet"> | ||
40 | <script src="../switcher/js/vendor/d3.min.js"></script> | ||
41 | |||
42 | <!-- debugging --> | ||
43 | <script src="../../src/bin-utils.js"></script> | ||
44 | <style> | ||
45 | body { | ||
46 | font-family: Arial, sans-serif; | ||
47 | margin: 20px; | ||
48 | } | ||
49 | .info { | ||
50 | background-color: #eee; | ||
51 | border: thin solid #333; | ||
52 | border-radius: 3px; | ||
53 | padding: 0 5px; | ||
54 | margin: 20px 0; | ||
55 | } | ||
56 | </style> | ||
57 | |||
58 | </head> | ||
59 | <body> | ||
60 | <div class="info"> | ||
61 | <p>The video below is an <a href="https://developer.apple.com/library/ios/documentation/networkinginternet/conceptual/streamingmediaguide/Introduction/Introduction.html#//apple_ref/doc/uid/TP40008332-CH1-SW1">HTTP Live Stream</a>. On desktop browsers other than Safari, the HLS plugin will polyfill support for the format on top of the video.js Flash tech.</p> | ||
62 | <p>Due to security restrictions in Flash, you will have to load this page over HTTP(S) to see the example in action.</p> | ||
63 | </div> | ||
64 | <video id="video" | ||
65 | class="video-js vjs-default-skin" | ||
66 | height="300" | ||
67 | width="600" | ||
68 | controls> | ||
69 | <source | ||
70 | src="http://s3.amazonaws.com/_bc_dml/example-content/bipbop-id3/index.m3u8" | ||
71 | type="application/x-mpegURL"> | ||
72 | </video> | ||
73 | <section class="stats"> | ||
74 | <h2>Player Stats</h2> | ||
75 | <div class="segment-timeline"></div> | ||
76 | <dl> | ||
77 | <dt>Current Time:</dt> | ||
78 | <dd class="current-time-stat">0</dd> | ||
79 | <dt>Buffered:</dt> | ||
80 | <dd><span class="buffered-start-stat">-</span> - <span class="buffered-end-stat">-</span></dd> | ||
81 | <dt>Seekable:</dt> | ||
82 | <dd><span class="seekable-start-stat">-</span> - <span class="seekable-end-stat">-</span></dd> | ||
83 | <dt>Video Bitrate:</dt> | ||
84 | <dd class="video-bitrate-stat">0 kbps</dd> | ||
85 | <dt>Measured Bitrate:</dt> | ||
86 | <dd class="measured-bitrate-stat">0 kbps</dd> | ||
87 | </dl> | ||
88 | <div class="switching-stats"> | ||
89 | Once the player begins loading, you'll see information about the | ||
90 | operation of the adaptive quality switching here. | ||
91 | </div> | ||
92 | </section> | ||
93 | |||
94 | <script src="stats.js"></script> | ||
95 | <script> | ||
96 | videojs.options.flash.swf = '../../node_modules/videojs-swf/dist/video-js.swf'; | ||
97 | // initialize the player | ||
98 | var player = videojs('video'); | ||
99 | |||
100 | // ------------ | ||
101 | // Player Stats | ||
102 | // ------------ | ||
103 | |||
104 | var currentTimeStat = document.querySelector('.current-time-stat'); | ||
105 | var bufferedStartStat = document.querySelector('.buffered-start-stat'); | ||
106 | var bufferedEndStat = document.querySelector('.buffered-end-stat'); | ||
107 | var seekableStartStat = document.querySelector('.seekable-start-stat'); | ||
108 | var seekableEndStat = document.querySelector('.seekable-end-stat'); | ||
109 | var videoBitrateState = document.querySelector('.video-bitrate-stat'); | ||
110 | var measuredBitrateStat = document.querySelector('.measured-bitrate-stat'); | ||
111 | |||
112 | player.on('timeupdate', function() { | ||
113 | currentTimeStat.textContent = player.currentTime().toFixed(1); | ||
114 | }); | ||
115 | |||
116 | player.on('progress', function() { | ||
117 | var oldStart, oldEnd; | ||
118 | // buffered | ||
119 | var buffered = player.buffered(); | ||
120 | if (buffered.length) { | ||
121 | |||
122 | oldStart = bufferedStartStat.textContent; | ||
123 | if (buffered.start(0).toFixed(1) !== oldStart) { | ||
124 | bufferedStartStat.textContent = buffered.start(0).toFixed(1); | ||
125 | } | ||
126 | oldEnd = bufferedEndStat.textContent; | ||
127 | if (buffered.end(0).toFixed(1) !== oldEnd) { | ||
128 | bufferedEndStat.textContent = buffered.end(0).toFixed(1); | ||
129 | } | ||
130 | } | ||
131 | |||
132 | // seekable | ||
133 | var seekable = player.seekable(); | ||
134 | if (seekable && seekable.length) { | ||
135 | |||
136 | oldStart = seekableStartStat.textContent; | ||
137 | if (seekable.start(0).toFixed(1) !== oldStart) { | ||
138 | seekableStartStat.textContent = seekable.start(0).toFixed(1); | ||
139 | } | ||
140 | oldEnd = seekableEndStat.textContent; | ||
141 | if (seekable.end(0).toFixed(1) !== oldEnd) { | ||
142 | seekableEndStat.textContent = seekable.end(0).toFixed(1); | ||
143 | } | ||
144 | } | ||
145 | |||
146 | // bitrates | ||
147 | var playlist = player.hls.playlists.media(); | ||
148 | if (playlist && playlist.attributes && playlist.attributes.BANDWIDTH) { | ||
149 | videoBitrateState.textContent = (playlist.attributes.BANDWIDTH / 1024).toLocaleString(undefined, { | ||
150 | maximumFractionDigits: 1 | ||
151 | }) + ' kbps'; | ||
152 | } | ||
153 | if (player.hls.bandwidth) { | ||
154 | measuredBitrateStat.textContent = (player.hls.bandwidth / 1024).toLocaleString(undefined, { | ||
155 | maximumFractionDigits: 1 | ||
156 | }) + ' kbps'; | ||
157 | } | ||
158 | }); | ||
159 | |||
160 | videojs.Hls.displayStats(document.querySelector('.switching-stats'), player); | ||
161 | videojs.Hls.displayCues(document.querySelector('.segment-timeline'), player); | ||
162 | </script> | ||
163 | </body> | ||
164 | </html> |
test/stats/stats.css
0 → 100644
1 | .axis text, | ||
2 | .cue text { | ||
3 | font: 12px sans-serif; | ||
4 | } | ||
5 | |||
6 | .axis line, | ||
7 | .axis path, | ||
8 | .intersect { | ||
9 | fill: none; | ||
10 | stroke: #000; | ||
11 | } | ||
12 | |||
13 | .cue { | ||
14 | width: 20px; | ||
15 | height: 20px; | ||
16 | } | ||
17 | .cue text { | ||
18 | display: none; | ||
19 | } | ||
20 | .cue:hover text { | ||
21 | display: block; | ||
22 | } | ||
23 | |||
24 | .intersect { | ||
25 | fill: none; | ||
26 | stroke: #000; | ||
27 | stroke-dasharray: 2,2; | ||
28 | } |
test/stats/stats.js
0 → 100644
1 | (function(window, videojs, undefined) { | ||
2 | 'use strict'; | ||
3 | |||
4 | // ------------- | ||
5 | // Initial Setup | ||
6 | // ------------- | ||
7 | |||
8 | var d3 = window.d3; | ||
9 | |||
10 | var setupGraph = function(element) { | ||
11 | element.innerHTML = ''; | ||
12 | |||
13 | // setup the display | ||
14 | var margin = { | ||
15 | top: 20, | ||
16 | right: 80, | ||
17 | bottom: 30, | ||
18 | left: 50 | ||
19 | }; | ||
20 | var width = 600 - margin.left - margin.right; | ||
21 | var height = 300 - margin.top - margin.bottom; | ||
22 | var svg = d3.select(element) | ||
23 | .append('svg') | ||
24 | .attr('width', width + margin.left + margin.right) | ||
25 | .attr('height', height + margin.top + margin.bottom) | ||
26 | .append('g') | ||
27 | .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); | ||
28 | |||
29 | // setup the timeline | ||
30 | var x = d3.time.scale().range([0, width]); // d3.scale.linear().range([0, width]); | ||
31 | var y = d3.scale.linear().range([height, 0]); | ||
32 | |||
33 | x.domain([new Date(), new Date(Date.now() + (5 * 60 * 1000))]); | ||
34 | y.domain([0, 5 * 1024 * 1024 * 8]); | ||
35 | |||
36 | var timeAxis = d3.svg.axis().scale(x).orient('bottom'); | ||
37 | var tickFormatter = d3.format(',.0f'); | ||
38 | var bitrateAxis = d3.svg.axis() | ||
39 | .scale(y) | ||
40 | .tickFormat(function(value) { | ||
41 | return tickFormatter(value / 1024); | ||
42 | }) | ||
43 | .orient('left'); | ||
44 | |||
45 | // time axis | ||
46 | svg.selectAll('.axis').remove(); | ||
47 | svg.append('g') | ||
48 | .attr('class', 'x axis') | ||
49 | .attr('transform', 'translate(0,' + height + ')') | ||
50 | .call(timeAxis); | ||
51 | |||
52 | // bitrate axis | ||
53 | svg.append('g') | ||
54 | .attr('class', 'y axis') | ||
55 | .call(bitrateAxis) | ||
56 | .append('text') | ||
57 | .attr('transform', 'rotate(-90)') | ||
58 | .attr('y', 6) | ||
59 | .attr('dy', '.71em') | ||
60 | .style('text-anchor', 'end') | ||
61 | .text('Bitrate (kb/s)'); | ||
62 | |||
63 | }; | ||
64 | |||
65 | // --------------- | ||
66 | // Dynamic Updates | ||
67 | // --------------- | ||
68 | |||
69 | var displayStats = function(element, player) { | ||
70 | setupGraph(element, player); | ||
71 | }; | ||
72 | |||
73 | // ----------------- | ||
74 | // Cue Visualization | ||
75 | // ----------------- | ||
76 | |||
77 | var Playlist = videojs.Hls.Playlist; | ||
78 | var margin = { | ||
79 | top: 8, | ||
80 | right: 8, | ||
81 | bottom: 20, | ||
82 | left: 80 | ||
83 | }; | ||
84 | var width = 600 - margin.left - margin.right; | ||
85 | var height = 600 - margin.top - margin.bottom; | ||
86 | |||
87 | var mediaDomain = function(media, player) { | ||
88 | var segments = media.segments; | ||
89 | var end = player.hls.playlists.expiredPreDiscontinuity_; | ||
90 | end += player.hls.playlists.expiredPostDiscontinuity_; | ||
91 | end += Playlist.duration(media, | ||
92 | media.mediaSequence, | ||
93 | media.mediaSequence + segments.length); | ||
94 | return [0, end]; | ||
95 | }; | ||
96 | var ptsDomain = function(segments, mediaScale, mediaOffset) { | ||
97 | mediaOffset = mediaOffset * 1000 || 0; | ||
98 | var start = mediaScale.domain()[0] * 1000; | ||
99 | var segment = segments[0]; | ||
100 | |||
101 | if (segment && | ||
102 | segment.minAudioPts !== undefined || | ||
103 | segment.minVideoPts !== undefined) { | ||
104 | start = Math.min(segment.minAudioPts || Infinity, | ||
105 | segment.minVideoPts || Infinity); | ||
106 | } | ||
107 | start -= mediaOffset; | ||
108 | return [ | ||
109 | start, | ||
110 | (mediaScale.domain()[1] - mediaScale.domain()[0]) * 1000 + start | ||
111 | ]; | ||
112 | }; | ||
113 | var svgUpdateCues = function(svg, mediaScale, ptsScale, y, cues) { | ||
114 | cues = Array.prototype.slice.call(cues).filter(function(cue) { | ||
115 | return cue.startTime > mediaScale.domain()[0] && | ||
116 | cue.startTime < mediaScale.domain()[1]; | ||
117 | }); | ||
118 | var points = svg.selectAll('.cue').data(cues, function(cue) { | ||
119 | return cue.pts_ + ' -> ' + cue.startTime; | ||
120 | }); | ||
121 | points.attr('transform', function(cue) { | ||
122 | return 'translate(' + mediaScale(cue.startTime) + ',' + ptsScale(cue.pts_) + ')'; | ||
123 | }); | ||
124 | var enter = points.enter().append('g') | ||
125 | .attr('class', 'cue'); | ||
126 | enter.append('circle') | ||
127 | .attr('r', 5) | ||
128 | .attr('data-time', function(cue) { | ||
129 | return cue.startTime; | ||
130 | }) | ||
131 | .attr('data-pts', function(cue) { | ||
132 | return cue.pts_; | ||
133 | }); | ||
134 | enter.append('text') | ||
135 | .attr('transform', 'translate(8,0)') | ||
136 | .text(function(cue) { | ||
137 | return 'time: ' + videojs.formatTime(cue.startTime); | ||
138 | }); | ||
139 | enter.append('text') | ||
140 | .attr('transform', 'translate(8,16)') | ||
141 | .text(function(cue) { | ||
142 | return 'pts: ' + cue.pts_; | ||
143 | }); | ||
144 | points.exit().remove(); | ||
145 | }; | ||
146 | var svgUpdateAxes = function(svg, mediaScale, ptsScale) { | ||
147 | // media timeline axis | ||
148 | var mediaAxis = d3.svg.axis().scale(mediaScale).orient('bottom'); | ||
149 | svg.select('.axis.media') | ||
150 | .transition().duration(500) | ||
151 | .call(mediaAxis); | ||
152 | |||
153 | // presentation timeline axis | ||
154 | if (!isFinite(ptsScale.domain()[0]) || !isFinite(ptsScale.domain()[1])) { | ||
155 | return; | ||
156 | } | ||
157 | var ptsAxis = d3.svg.axis().scale(ptsScale).orient('left'); | ||
158 | svg.select('.axis.presentation') | ||
159 | .transition().duration(500) | ||
160 | .call(ptsAxis); | ||
161 | }; | ||
162 | var svgRenderSegmentTimeline = function(container, player) { | ||
163 | var media = player.hls.playlists.media(); | ||
164 | var segments = media.segments; // media.segments.slice(0, count); | ||
165 | |||
166 | // setup the display | ||
167 | var svg = d3.select(container) | ||
168 | .append('svg') | ||
169 | .attr('width', width + margin.left + margin.right) | ||
170 | .attr('height', height + margin.top + margin.bottom) | ||
171 | .append('g') | ||
172 | .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); | ||
173 | |||
174 | // setup the scales | ||
175 | var mediaScale = d3.scale.linear().range([0, width]); | ||
176 | mediaScale.domain(mediaDomain(media, player)); | ||
177 | var ptsScale = d3.scale.linear().range([height, 0]); | ||
178 | ptsScale.domain(ptsDomain(segments, mediaScale)); | ||
179 | |||
180 | // render | ||
181 | var mediaAxis = d3.svg.axis().scale(mediaScale).orient('bottom'); | ||
182 | svg.append('g') | ||
183 | .attr('class', 'x axis media') | ||
184 | .attr('transform', 'translate(0,' + height + ')') | ||
185 | .call(mediaAxis); | ||
186 | var ptsAxis = d3.svg.axis().scale(ptsScale).orient('left'); | ||
187 | svg.append('g') | ||
188 | .attr('class', 'y axis presentation') | ||
189 | .call(ptsAxis); | ||
190 | |||
191 | svg.append('path') | ||
192 | .attr('class', 'intersect') | ||
193 | .attr('d', 'M0,' + height + 'L' + width +',0'); | ||
194 | |||
195 | var mediaOffset = 0; | ||
196 | |||
197 | // update everything on progress | ||
198 | player.on('progress', function() { | ||
199 | var updatedMedia = player.hls.playlists.media(); | ||
200 | var segments = updatedMedia.segments; // updatedMedia.segments.slice(currentIndex, currentIndex + count); | ||
201 | |||
202 | if (updatedMedia.mediaSequence !== media.mediaSequence) { | ||
203 | mediaOffset += Playlist.duration(media, | ||
204 | media.mediaSequence, | ||
205 | updatedMedia.mediaSequence); | ||
206 | media = updatedMedia; | ||
207 | } | ||
208 | |||
209 | mediaScale.domain(mediaDomain(updatedMedia, player)); | ||
210 | ptsScale.domain(ptsDomain(segments, mediaScale, mediaOffset)); | ||
211 | svgUpdateAxes(svg, mediaScale, ptsScale, updatedMedia, segments); | ||
212 | if (!isFinite(ptsScale.domain()[0]) || !isFinite(ptsScale.domain()[1])) { | ||
213 | return; | ||
214 | } | ||
215 | for (var i = 0; i < player.textTracks().length; i++) { | ||
216 | var track = player.textTracks()[i]; | ||
217 | svgUpdateCues(svg, mediaScale, ptsScale, ptsScale, track.cues); | ||
218 | } | ||
219 | }); | ||
220 | }; | ||
221 | |||
222 | var displayCues = function(container, player) { | ||
223 | var media = player.hls.playlists.media(); | ||
224 | if (media && media.segments) { | ||
225 | svgRenderSegmentTimeline(container, player); | ||
226 | } else { | ||
227 | player.one('loadedmetadata', function() { | ||
228 | svgRenderSegmentTimeline(container, player); | ||
229 | }); | ||
230 | } | ||
231 | }; | ||
232 | |||
233 | |||
234 | // export | ||
235 | videojs.Hls.displayStats = displayStats; | ||
236 | videojs.Hls.displayCues = displayCues; | ||
237 | |||
238 | })(window, window.videojs); |
-
Please register or sign in to post a comment