b0fe8348 by jrivera

Merge remote-tracking branch 'origin/master'

2 parents 9ee2a7db e45497bc
...@@ -170,6 +170,40 @@ export default class MasterPlaylistController extends videojs.EventTarget { ...@@ -170,6 +170,40 @@ export default class MasterPlaylistController extends videojs.EventTarget {
170 } 170 }
171 171
172 /** 172 /**
173 * get the total number of media requests from the `audiosegmentloader_`
174 * and the `mainSegmentLoader_`
175 *
176 * @private
177 */
178 mediaRequests_() {
179 return this.audioSegmentLoader_.mediaRequests +
180 this.mainSegmentLoader_.mediaRequests;
181 }
182
183 /**
184 * get the total time that media requests have spent trnasfering
185 * from the `audiosegmentloader_` and the `mainSegmentLoader_`
186 *
187 * @private
188 */
189 mediaTransferDuration_() {
190 return this.audioSegmentLoader_.mediaTransferDuration +
191 this.mainSegmentLoader_.mediaTransferDuration;
192
193 }
194
195 /**
196 * get the total number of bytes transfered during media requests
197 * from the `audiosegmentloader_` and the `mainSegmentLoader_`
198 *
199 * @private
200 */
201 mediaBytesTransferred_() {
202 return this.audioSegmentLoader_.mediaBytesTransferred +
203 this.mainSegmentLoader_.mediaBytesTransferred;
204 }
205
206 /**
173 * fill our internal list of HlsAudioTracks with data from 207 * fill our internal list of HlsAudioTracks with data from
174 * the master playlist or use a default 208 * the master playlist or use a default
175 * 209 *
...@@ -350,7 +384,8 @@ export default class MasterPlaylistController extends videojs.EventTarget { ...@@ -350,7 +384,8 @@ export default class MasterPlaylistController extends videojs.EventTarget {
350 384
351 if (media !== this.masterPlaylistLoader_.media()) { 385 if (media !== this.masterPlaylistLoader_.media()) {
352 this.masterPlaylistLoader_.media(media); 386 this.masterPlaylistLoader_.media(media);
353 this.mainSegmentLoader_.sourceUpdater_.remove(this.tech_.currentTime() + 5, Infinity); 387 this.mainSegmentLoader_.sourceUpdater_.remove(this.tech_.currentTime() + 5,
388 Infinity);
354 } 389 }
355 } 390 }
356 391
......
...@@ -198,27 +198,58 @@ const bufferIntersection = function(bufferA, bufferB) { ...@@ -198,27 +198,58 @@ const bufferIntersection = function(bufferA, bufferB) {
198 * covers adjusted according to currentTime 198 * covers adjusted according to currentTime
199 * @param {TimeRanges} referenceRange - the original time range that the 199 * @param {TimeRanges} referenceRange - the original time range that the
200 * segment covers 200 * segment covers
201 * @param {Number} currentTime - time in seconds where the current playback
202 * is at
201 * @param {TimeRanges} buffered - the currently buffered time ranges 203 * @param {TimeRanges} buffered - the currently buffered time ranges
202 * @returns {Number} percent of the segment currently buffered 204 * @returns {Number} percent of the segment currently buffered
203 */ 205 */
204 const calculateBufferedPercent = function(segmentRange, referenceRange, buffered) { 206 const calculateBufferedPercent = function(adjustedRange,
207 referenceRange,
208 currentTime,
209 buffered) {
205 let referenceDuration = referenceRange.end(0) - referenceRange.start(0); 210 let referenceDuration = referenceRange.end(0) - referenceRange.start(0);
206 let segmentDuration = segmentRange.end(0) - segmentRange.start(0); 211 let adjustedDuration = adjustedRange.end(0) - adjustedRange.start(0);
207 let intersection = bufferIntersection(segmentRange, buffered); 212 let bufferMissingFromAdjusted = referenceDuration - adjustedDuration;
208 let count = intersection.length; 213 let adjustedIntersection = bufferIntersection(adjustedRange, buffered);
214 let referenceIntersection = bufferIntersection(referenceRange, buffered);
215 let adjustedOverlap = 0;
216 let referenceOverlap = 0;
217
218 let count = adjustedIntersection.length;
209 219
210 while (count--) { 220 while (count--) {
211 segmentDuration -= intersection.end(count) - intersection.start(count); 221 adjustedOverlap += adjustedIntersection.end(count) -
222 adjustedIntersection.start(count);
223
224 // If the current overlap segment starts at currentTime, then increase the
225 // overlap duration so that it actually starts at the beginning of referenceRange
226 // by including the difference between the two Range's durations
227 // This is a work around for the way Flash has no buffer before currentTime
228 if (adjustedIntersection.start(count) === currentTime) {
229 adjustedOverlap += bufferMissingFromAdjusted;
230 }
212 } 231 }
213 return (referenceDuration - segmentDuration) / referenceDuration * 100; 232
233 count = referenceIntersection.length;
234
235 while (count--) {
236 referenceOverlap += referenceIntersection.end(count) -
237 referenceIntersection.start(count);
238 }
239
240 // Use whichever value is larger for the percentage-buffered since that value
241 // is likely more accurate because the only way
242 return Math.max(adjustedOverlap, referenceOverlap) / referenceDuration * 100;
214 }; 243 };
215 244
216 /** 245 /**
217 * Return the amount of a segment specified by the mediaIndex overlaps 246 * Return the amount of a range specified by the startOfSegment and segmentDuration
218 * the current buffered content. 247 * overlaps the current buffered content.
219 * 248 *
220 * @param {Number} startOfSegment - the time where the segment begins 249 * @param {Number} startOfSegment - the time where the segment begins
221 * @param {Number} segmentDuration - the duration of the segment in seconds 250 * @param {Number} segmentDuration - the duration of the segment in seconds
251 * @param {Number} currentTime - time in seconds where the current playback
252 * is at
222 * @param {TimeRanges} buffered - the state of the buffer 253 * @param {TimeRanges} buffered - the state of the buffer
223 * @returns {Number} percentage of the segment's time range that is 254 * @returns {Number} percentage of the segment's time range that is
224 * already in `buffered` 255 * already in `buffered`
...@@ -254,6 +285,7 @@ const getSegmentBufferedPercent = function(startOfSegment, ...@@ -254,6 +285,7 @@ const getSegmentBufferedPercent = function(startOfSegment,
254 285
255 let percent = calculateBufferedPercent(adjustedSegmentRange, 286 let percent = calculateBufferedPercent(adjustedSegmentRange,
256 originalSegmentRange, 287 originalSegmentRange,
288 currentTime,
257 buffered); 289 buffered);
258 290
259 // If the segment is reported as having a zero duration, return 0% 291 // If the segment is reported as having a zero duration, return 0%
......
...@@ -131,7 +131,7 @@ export default class SegmentLoader extends videojs.EventTarget { ...@@ -131,7 +131,7 @@ export default class SegmentLoader extends videojs.EventTarget {
131 this.state = 'INIT'; 131 this.state = 'INIT';
132 this.bandwidth = settings.bandwidth; 132 this.bandwidth = settings.bandwidth;
133 this.roundTrip = NaN; 133 this.roundTrip = NaN;
134 this.bytesReceived = 0; 134 this.resetStats_();
135 135
136 // private properties 136 // private properties
137 this.hasPlayed_ = settings.hasPlayed; 137 this.hasPlayed_ = settings.hasPlayed;
...@@ -153,6 +153,17 @@ export default class SegmentLoader extends videojs.EventTarget { ...@@ -153,6 +153,17 @@ export default class SegmentLoader extends videojs.EventTarget {
153 } 153 }
154 154
155 /** 155 /**
156 * reset all of our media stats
157 *
158 * @private
159 */
160 resetStats_() {
161 this.mediaBytesTransferred = 0;
162 this.mediaRequests = 0;
163 this.mediaTransferDuration = 0;
164 }
165
166 /**
156 * dispose of the SegmentLoader and reset to the default state 167 * dispose of the SegmentLoader and reset to the default state
157 */ 168 */
158 dispose() { 169 dispose() {
...@@ -161,6 +172,7 @@ export default class SegmentLoader extends videojs.EventTarget { ...@@ -161,6 +172,7 @@ export default class SegmentLoader extends videojs.EventTarget {
161 if (this.sourceUpdater_) { 172 if (this.sourceUpdater_) {
162 this.sourceUpdater_.dispose(); 173 this.sourceUpdater_.dispose();
163 } 174 }
175 this.resetStats_();
164 } 176 }
165 177
166 /** 178 /**
...@@ -625,7 +637,9 @@ export default class SegmentLoader extends videojs.EventTarget { ...@@ -625,7 +637,9 @@ export default class SegmentLoader extends videojs.EventTarget {
625 // calculate the download bandwidth based on segment request 637 // calculate the download bandwidth based on segment request
626 this.roundTrip = request.roundTripTime; 638 this.roundTrip = request.roundTripTime;
627 this.bandwidth = request.bandwidth; 639 this.bandwidth = request.bandwidth;
628 this.bytesReceived += request.bytesReceived || 0; 640 this.mediaBytesTransferred += request.bytesReceived || 0;
641 this.mediaRequests += 1;
642 this.mediaTransferDuration += request.roundTripTime || 0;
629 643
630 if (segment.key) { 644 if (segment.key) {
631 segmentInfo.encryptedBytes = new Uint8Array(request.response); 645 segmentInfo.encryptedBytes = new Uint8Array(request.response);
......
...@@ -301,12 +301,16 @@ class HlsHandler extends Component { ...@@ -301,12 +301,16 @@ class HlsHandler extends Component {
301 301
302 this.tech_ = tech; 302 this.tech_ = tech;
303 this.source_ = source; 303 this.source_ = source;
304 this.stats = {};
304 305
305 // handle global & Source Handler level options 306 // handle global & Source Handler level options
306 this.options_ = videojs.mergeOptions(videojs.options.hls || {}, options.hls); 307 this.options_ = videojs.mergeOptions(videojs.options.hls || {}, options.hls);
307 this.setOptions_(); 308 this.setOptions_();
308 309
309 this.bytesReceived = 0; 310 // start playlist selection at a reasonable bandwidth for
311 // broadband internet
312 // 0.5 Mbps
313 this.bandwidth = this.options_.bandwidth || 4194304;
310 314
311 // listen for fullscreenchange events for this player so that we 315 // listen for fullscreenchange events for this player so that we
312 // can adjust our quality selection quickly 316 // can adjust our quality selection quickly
...@@ -406,6 +410,19 @@ class HlsHandler extends Component { ...@@ -406,6 +410,19 @@ class HlsHandler extends Component {
406 } 410 }
407 }); 411 });
408 412
413 Object.defineProperty(this.stats, 'bandwidth', {
414 get: () => this.bandwidth || 0
415 });
416 Object.defineProperty(this.stats, 'mediaRequests', {
417 get: () => this.masterPlaylistController_.mediaRequests_() || 0
418 });
419 Object.defineProperty(this.stats, 'mediaTransferDuration', {
420 get: () => this.masterPlaylistController_.mediaTransferDuration_() || 0
421 });
422 Object.defineProperty(this.stats, 'mediaBytesTransferred', {
423 get: () => this.masterPlaylistController_.mediaBytesTransferred_() || 0
424 });
425
409 this.tech_.one('canplay', 426 this.tech_.one('canplay',
410 this.masterPlaylistController_.setupFirstPlay.bind(this.masterPlaylistController_)); 427 this.masterPlaylistController_.setupFirstPlay.bind(this.masterPlaylistController_));
411 428
...@@ -527,7 +544,6 @@ class HlsHandler extends Component { ...@@ -527,7 +544,6 @@ class HlsHandler extends Component {
527 this.masterPlaylistController_.dispose(); 544 this.masterPlaylistController_.dispose();
528 } 545 }
529 this.tech_.audioTracks().removeEventListener('change', this.audioTrackChange_); 546 this.tech_.audioTracks().removeEventListener('change', this.audioTrackChange_);
530
531 super.dispose(); 547 super.dispose();
532 } 548 }
533 } 549 }
......
...@@ -64,6 +64,9 @@ QUnit.test('obeys none preload option', function() { ...@@ -64,6 +64,9 @@ QUnit.test('obeys none preload option', function() {
64 openMediaSource(this.player, this.clock); 64 openMediaSource(this.player, this.clock);
65 65
66 QUnit.equal(this.requests.length, 0, 'no segment requests'); 66 QUnit.equal(this.requests.length, 0, 'no segment requests');
67
68 // verify stats
69 QUnit.equal(this.player.tech_.hls.stats.bandwidth, 4194304, 'default bandwidth');
67 }); 70 });
68 71
69 QUnit.test('obeys auto preload option', function() { 72 QUnit.test('obeys auto preload option', function() {
...@@ -76,6 +79,9 @@ QUnit.test('obeys auto preload option', function() { ...@@ -76,6 +79,9 @@ QUnit.test('obeys auto preload option', function() {
76 openMediaSource(this.player, this.clock); 79 openMediaSource(this.player, this.clock);
77 80
78 QUnit.equal(this.requests.length, 1, '1 segment request'); 81 QUnit.equal(this.requests.length, 1, '1 segment request');
82
83 // verify stats
84 QUnit.equal(this.player.tech_.hls.stats.bandwidth, 4194304, 'default bandwidth');
79 }); 85 });
80 86
81 QUnit.test('obeys metadata preload option', function() { 87 QUnit.test('obeys metadata preload option', function() {
...@@ -88,6 +94,9 @@ QUnit.test('obeys metadata preload option', function() { ...@@ -88,6 +94,9 @@ QUnit.test('obeys metadata preload option', function() {
88 openMediaSource(this.player, this.clock); 94 openMediaSource(this.player, this.clock);
89 95
90 QUnit.equal(this.requests.length, 1, '1 segment request'); 96 QUnit.equal(this.requests.length, 1, '1 segment request');
97
98 // verify stats
99 QUnit.equal(this.player.tech_.hls.stats.bandwidth, 4194304, 'default bandwidth');
91 }); 100 });
92 101
93 QUnit.test('clears some of the buffer for a fast quality change', function() { 102 QUnit.test('clears some of the buffer for a fast quality change', function() {
...@@ -114,6 +123,9 @@ QUnit.test('clears some of the buffer for a fast quality change', function() { ...@@ -114,6 +123,9 @@ QUnit.test('clears some of the buffer for a fast quality change', function() {
114 QUnit.equal(removes.length, 1, 'removed buffered content'); 123 QUnit.equal(removes.length, 1, 'removed buffered content');
115 QUnit.equal(removes[0].start, 7 + 5, 'removed from a bit after current time'); 124 QUnit.equal(removes[0].start, 7 + 5, 'removed from a bit after current time');
116 QUnit.equal(removes[0].end, Infinity, 'removed to the end'); 125 QUnit.equal(removes[0].end, Infinity, 'removed to the end');
126
127 // verify stats
128 QUnit.equal(this.player.tech_.hls.stats.bandwidth, 4194304, 'default bandwidth');
117 }); 129 });
118 130
119 QUnit.test('does not clear the buffer when no fast quality change occurs', function() { 131 QUnit.test('does not clear the buffer when no fast quality change occurs', function() {
...@@ -134,6 +146,8 @@ QUnit.test('does not clear the buffer when no fast quality change occurs', funct ...@@ -134,6 +146,8 @@ QUnit.test('does not clear the buffer when no fast quality change occurs', funct
134 this.masterPlaylistController.fastQualityChange_(); 146 this.masterPlaylistController.fastQualityChange_();
135 147
136 QUnit.equal(removes.length, 0, 'did not remove content'); 148 QUnit.equal(removes.length, 0, 'did not remove content');
149 // verify stats
150 QUnit.equal(this.player.tech_.hls.stats.bandwidth, 4194304, 'default bandwidth');
137 }); 151 });
138 152
139 QUnit.test('if buffered, will request second segment byte range', function() { 153 QUnit.test('if buffered, will request second segment byte range', function() {
...@@ -163,6 +177,13 @@ QUnit.test('if buffered, will request second segment byte range', function() { ...@@ -163,6 +177,13 @@ QUnit.test('if buffered, will request second segment byte range', function() {
163 this.masterPlaylistController.mediaSource.sourceBuffers[0].trigger('updateend'); 177 this.masterPlaylistController.mediaSource.sourceBuffers[0].trigger('updateend');
164 this.clock.tick(10 * 1000); 178 this.clock.tick(10 * 1000);
165 QUnit.equal(this.requests[2].headers.Range, 'bytes=1823412-2299991'); 179 QUnit.equal(this.requests[2].headers.Range, 'bytes=1823412-2299991');
180
181 // verify stats
182 QUnit.equal(this.player.tech_.hls.stats.bandwidth, Infinity, 'Live stream');
183 QUnit.equal(this.player.tech_.hls.stats.mediaRequests, 1, '1 segment request');
184 QUnit.equal(this.player.tech_.hls.stats.mediaBytesTransferred,
185 16,
186 '16 bytes downloaded');
166 }); 187 });
167 188
168 QUnit.test('re-initializes the combined playlist loader when switching sources', 189 QUnit.test('re-initializes the combined playlist loader when switching sources',
...@@ -218,6 +239,8 @@ QUnit.test('updates the combined segment loader on live playlist refreshes', fun ...@@ -218,6 +239,8 @@ QUnit.test('updates the combined segment loader on live playlist refreshes', fun
218 239
219 this.masterPlaylistController.masterPlaylistLoader_.trigger('loadedplaylist'); 240 this.masterPlaylistController.masterPlaylistLoader_.trigger('loadedplaylist');
220 QUnit.equal(updates.length, 1, 'updated the segment list'); 241 QUnit.equal(updates.length, 1, 'updated the segment list');
242 // verify stats
243 QUnit.equal(this.player.tech_.hls.stats.bandwidth, 4194304, 'default bandwidth');
221 }); 244 });
222 245
223 QUnit.test( 246 QUnit.test(
...@@ -240,6 +263,13 @@ function() { ...@@ -240,6 +263,13 @@ function() {
240 standardXHRResponse(this.requests.shift()); 263 standardXHRResponse(this.requests.shift());
241 this.masterPlaylistController.mainSegmentLoader_.trigger('progress'); 264 this.masterPlaylistController.mainSegmentLoader_.trigger('progress');
242 QUnit.equal(progressCount, 1, 'fired a progress event'); 265 QUnit.equal(progressCount, 1, 'fired a progress event');
266
267 // verify stats
268 QUnit.equal(this.player.tech_.hls.stats.bandwidth, Infinity, 'Live stream');
269 QUnit.equal(this.player.tech_.hls.stats.mediaRequests, 1, '1 segment request');
270 QUnit.equal(this.player.tech_.hls.stats.mediaBytesTransferred,
271 16,
272 '16 bytes downloaded');
243 }); 273 });
244 274
245 QUnit.test('blacklists switching from video+audio playlists to audio only', function() { 275 QUnit.test('blacklists switching from video+audio playlists to audio only', function() {
...@@ -264,6 +294,9 @@ QUnit.test('blacklists switching from video+audio playlists to audio only', func ...@@ -264,6 +294,9 @@ QUnit.test('blacklists switching from video+audio playlists to audio only', func
264 'selected video+audio'); 294 'selected video+audio');
265 audioPlaylist = this.masterPlaylistController.masterPlaylistLoader_.master.playlists[0]; 295 audioPlaylist = this.masterPlaylistController.masterPlaylistLoader_.master.playlists[0];
266 QUnit.equal(audioPlaylist.excludeUntil, Infinity, 'excluded incompatible playlist'); 296 QUnit.equal(audioPlaylist.excludeUntil, Infinity, 'excluded incompatible playlist');
297
298 // verify stats
299 QUnit.equal(this.player.tech_.hls.stats.bandwidth, 1e10, 'bandwidth we set above');
267 }); 300 });
268 301
269 QUnit.test('blacklists switching from audio-only playlists to video+audio', function() { 302 QUnit.test('blacklists switching from audio-only playlists to video+audio', function() {
...@@ -290,6 +323,9 @@ QUnit.test('blacklists switching from audio-only playlists to video+audio', func ...@@ -290,6 +323,9 @@ QUnit.test('blacklists switching from audio-only playlists to video+audio', func
290 QUnit.equal(videoAudioPlaylist.excludeUntil, 323 QUnit.equal(videoAudioPlaylist.excludeUntil,
291 Infinity, 324 Infinity,
292 'excluded incompatible playlist'); 325 'excluded incompatible playlist');
326
327 // verify stats
328 QUnit.equal(this.player.tech_.hls.stats.bandwidth, 1, 'bandwidth we set above');
293 }); 329 });
294 330
295 QUnit.test('blacklists switching from video-only playlists to video+audio', function() { 331 QUnit.test('blacklists switching from video-only playlists to video+audio', function() {
...@@ -317,6 +353,9 @@ QUnit.test('blacklists switching from video-only playlists to video+audio', func ...@@ -317,6 +353,9 @@ QUnit.test('blacklists switching from video-only playlists to video+audio', func
317 QUnit.equal(videoAudioPlaylist.excludeUntil, 353 QUnit.equal(videoAudioPlaylist.excludeUntil,
318 Infinity, 354 Infinity,
319 'excluded incompatible playlist'); 355 'excluded incompatible playlist');
356
357 // verify stats
358 QUnit.equal(this.player.tech_.hls.stats.bandwidth, 1, 'bandwidth we set above');
320 }); 359 });
321 360
322 QUnit.test('blacklists switching between playlists with incompatible audio codecs', 361 QUnit.test('blacklists switching between playlists with incompatible audio codecs',
...@@ -343,6 +382,8 @@ function() { ...@@ -343,6 +382,8 @@ function() {
343 alternatePlaylist = 382 alternatePlaylist =
344 this.masterPlaylistController.masterPlaylistLoader_.master.playlists[1]; 383 this.masterPlaylistController.masterPlaylistLoader_.master.playlists[1];
345 QUnit.equal(alternatePlaylist.excludeUntil, Infinity, 'excluded incompatible playlist'); 384 QUnit.equal(alternatePlaylist.excludeUntil, Infinity, 'excluded incompatible playlist');
385 // verify stats
386 QUnit.equal(this.player.tech_.hls.stats.bandwidth, 1, 'bandwidth we set above');
346 }); 387 });
347 388
348 QUnit.test('updates the combined segment loader on media changes', function() { 389 QUnit.test('updates the combined segment loader on media changes', function() {
...@@ -369,6 +410,14 @@ QUnit.test('updates the combined segment loader on media changes', function() { ...@@ -369,6 +410,14 @@ QUnit.test('updates the combined segment loader on media changes', function() {
369 // media 410 // media
370 standardXHRResponse(this.requests.shift()); 411 standardXHRResponse(this.requests.shift());
371 QUnit.equal(updates.length, 1, 'updated the segment list'); 412 QUnit.equal(updates.length, 1, 'updated the segment list');
413
414 // verify stats
415 QUnit.equal(this.player.tech_.hls.stats.bandwidth, Infinity, 'Live stream');
416 QUnit.equal(this.player.tech_.hls.stats.mediaRequests, 1, '1 segment request');
417 QUnit.equal(
418 this.player.tech_.hls.stats.mediaBytesTransferred,
419 16,
420 '16 bytes downloaded');
372 }); 421 });
373 422
374 QUnit.test('selects a playlist after main/combined segment downloads', function() { 423 QUnit.test('selects a playlist after main/combined segment downloads', function() {
...@@ -392,6 +441,8 @@ QUnit.test('selects a playlist after main/combined segment downloads', function( ...@@ -392,6 +441,8 @@ QUnit.test('selects a playlist after main/combined segment downloads', function(
392 // and another 441 // and another
393 this.masterPlaylistController.mainSegmentLoader_.trigger('progress'); 442 this.masterPlaylistController.mainSegmentLoader_.trigger('progress');
394 QUnit.strictEqual(calls, 3, 'selects after additional segments'); 443 QUnit.strictEqual(calls, 3, 'selects after additional segments');
444 // verify stats
445 QUnit.equal(this.player.tech_.hls.stats.bandwidth, 4194304, 'default bandwidth');
395 }); 446 });
396 447
397 QUnit.test('updates the duration after switching playlists', function() { 448 QUnit.test('updates the duration after switching playlists', function() {
...@@ -424,6 +475,13 @@ QUnit.test('updates the duration after switching playlists', function() { ...@@ -424,6 +475,13 @@ QUnit.test('updates the duration after switching playlists', function() {
424 QUnit.ok(selectedPlaylist, 'selected playlist'); 475 QUnit.ok(selectedPlaylist, 'selected playlist');
425 QUnit.ok(this.masterPlaylistController.mediaSource.duration !== 0, 476 QUnit.ok(this.masterPlaylistController.mediaSource.duration !== 0,
426 'updates the duration'); 477 'updates the duration');
478
479 // verify stats
480 QUnit.equal(this.player.tech_.hls.stats.bandwidth, Infinity, 'Live stream');
481 QUnit.equal(this.player.tech_.hls.stats.mediaRequests, 1, '1 segment request');
482 QUnit.equal(this.player.tech_.hls.stats.mediaBytesTransferred,
483 16,
484 '16 bytes downloaded');
427 }); 485 });
428 486
429 QUnit.test('seekable uses the intersection of alternate audio and combined tracks', 487 QUnit.test('seekable uses the intersection of alternate audio and combined tracks',
......
...@@ -94,7 +94,7 @@ QUnit.test('detects time range end-point changed by updates', function() { ...@@ -94,7 +94,7 @@ QUnit.test('detects time range end-point changed by updates', function() {
94 94
95 QUnit.module('Segment Percent Buffered Calculations'); 95 QUnit.module('Segment Percent Buffered Calculations');
96 96
97 QUnit.test('calculates the percent buffered for segments', function() { 97 QUnit.test('calculates the percent buffered for segments in the simple case', function() {
98 let segmentStart = 10; 98 let segmentStart = 10;
99 let segmentDuration = 10; 99 let segmentDuration = 10;
100 let currentTime = 0; 100 let currentTime = 0;
...@@ -108,8 +108,8 @@ QUnit.test('calculates the percent buffered for segments', function() { ...@@ -108,8 +108,8 @@ QUnit.test('calculates the percent buffered for segments', function() {
108 QUnit.equal(percentBuffered, 40, 'calculated the buffered amount correctly'); 108 QUnit.equal(percentBuffered, 40, 'calculated the buffered amount correctly');
109 }); 109 });
110 110
111 QUnit.test('calculates the percent buffered for segments taking into account ' + 111 QUnit.test('consider the buffer before currentTime to be filled if the segement begins at ' +
112 'currentTime', function() { 112 'or before the currentTime', function() {
113 let segmentStart = 10; 113 let segmentStart = 10;
114 let segmentDuration = 10; 114 let segmentDuration = 10;
115 let currentTime = 15; 115 let currentTime = 15;
...@@ -123,6 +123,21 @@ QUnit.test('calculates the percent buffered for segments taking into account ' + ...@@ -123,6 +123,21 @@ QUnit.test('calculates the percent buffered for segments taking into account ' +
123 QUnit.equal(percentBuffered, 90, 'calculated the buffered amount correctly'); 123 QUnit.equal(percentBuffered, 90, 'calculated the buffered amount correctly');
124 }); 124 });
125 125
126 QUnit.test('does not consider the buffer before currentTime as filled if the segment ' +
127 'begins after the currentTime', function() {
128 let segmentStart = 10;
129 let segmentDuration = 10;
130 let currentTime = 18;
131 let buffered = createTimeRanges([[19, 30]]);
132 let percentBuffered = Ranges.getSegmentBufferedPercent(
133 segmentStart,
134 segmentDuration,
135 currentTime,
136 buffered);
137
138 QUnit.equal(percentBuffered, 10, 'calculated the buffered amount correctly');
139 });
140
126 QUnit.test('calculates the percent buffered for segments with multiple buffered ' + 141 QUnit.test('calculates the percent buffered for segments with multiple buffered ' +
127 'regions', function() { 142 'regions', function() {
128 let segmentStart = 10; 143 let segmentStart = 10;
...@@ -166,3 +181,32 @@ QUnit.test('calculates the percent buffered as 0 for zero-length segments', func ...@@ -166,3 +181,32 @@ QUnit.test('calculates the percent buffered as 0 for zero-length segments', func
166 181
167 QUnit.equal(percentBuffered, 0, 'calculated the buffered amount correctly'); 182 QUnit.equal(percentBuffered, 0, 'calculated the buffered amount correctly');
168 }); 183 });
184
185 QUnit.test('calculates the percent buffered as 0 for segments that do not overlap ' +
186 'buffered regions taking into account currentTime', function() {
187 let segmentStart = 10;
188 let segmentDuration = 10;
189 let currentTime = 19;
190 let buffered = createTimeRanges([[20, 30]]);
191 let percentBuffered = Ranges.getSegmentBufferedPercent(
192 segmentStart,
193 segmentDuration,
194 currentTime,
195 buffered);
196
197 QUnit.equal(percentBuffered, 0, 'calculated the buffered amount correctly');
198 });
199
200 QUnit.test('calculates the percent buffered for segments that end before currentTime', function() {
201 let segmentStart = 10;
202 let segmentDuration = 10;
203 let currentTime = 19.6;
204 let buffered = createTimeRanges([[0, 19.5]]);
205 let percentBuffered = Ranges.getSegmentBufferedPercent(
206 segmentStart,
207 segmentDuration,
208 currentTime,
209 buffered);
210
211 QUnit.equal(percentBuffered, 95, 'calculated the buffered amount correctly');
212 });
......
...@@ -105,6 +105,11 @@ QUnit.test('calling load is idempotent', function() { ...@@ -105,6 +105,11 @@ QUnit.test('calling load is idempotent', function() {
105 this.requests.shift().respond(200, null, ''); 105 this.requests.shift().respond(200, null, '');
106 loader.load(); 106 loader.load();
107 QUnit.equal(this.requests.length, 0, 'load has no effect'); 107 QUnit.equal(this.requests.length, 0, 'load has no effect');
108
109 // verify stats
110 QUnit.equal(loader.mediaBytesTransferred, 10, '10 bytes');
111 QUnit.equal(loader.mediaTransferDuration, 100, '100 ms (clock above)');
112 QUnit.equal(loader.mediaRequests, 1, '1 request');
108 }); 113 });
109 114
110 QUnit.test('calling load should unpause', function() { 115 QUnit.test('calling load should unpause', function() {
...@@ -135,6 +140,11 @@ QUnit.test('calling load should unpause', function() { ...@@ -135,6 +140,11 @@ QUnit.test('calling load should unpause', function() {
135 140
136 loader.load(); 141 loader.load();
137 QUnit.equal(loader.paused(), false, 'unpaused'); 142 QUnit.equal(loader.paused(), false, 'unpaused');
143
144 // verify stats
145 QUnit.equal(loader.mediaBytesTransferred, 10, '10 bytes');
146 QUnit.equal(loader.mediaTransferDuration, 1, '1 ms (clock above)');
147 QUnit.equal(loader.mediaRequests, 1, '1 request');
138 }); 148 });
139 149
140 QUnit.test('regularly checks the buffer while unpaused', function() { 150 QUnit.test('regularly checks the buffer while unpaused', function() {
...@@ -159,6 +169,11 @@ QUnit.test('regularly checks the buffer while unpaused', function() { ...@@ -159,6 +169,11 @@ QUnit.test('regularly checks the buffer while unpaused', function() {
159 currentTime = Config.GOAL_BUFFER_LENGTH; 169 currentTime = Config.GOAL_BUFFER_LENGTH;
160 this.clock.tick(10 * 1000); 170 this.clock.tick(10 * 1000);
161 QUnit.equal(this.requests.length, 1, 'requested another segment'); 171 QUnit.equal(this.requests.length, 1, 'requested another segment');
172
173 // verify stats
174 QUnit.equal(loader.mediaBytesTransferred, 10, '10 bytes');
175 QUnit.equal(loader.mediaTransferDuration, 1, '1 ms (clock above)');
176 QUnit.equal(loader.mediaRequests, 1, '1 request');
162 }); 177 });
163 178
164 QUnit.test('does not check the buffer while paused', function() { 179 QUnit.test('does not check the buffer while paused', function() {
...@@ -177,6 +192,11 @@ QUnit.test('does not check the buffer while paused', function() { ...@@ -177,6 +192,11 @@ QUnit.test('does not check the buffer while paused', function() {
177 192
178 this.clock.tick(10 * 1000); 193 this.clock.tick(10 * 1000);
179 QUnit.equal(this.requests.length, 0, 'did not make a request'); 194 QUnit.equal(this.requests.length, 0, 'did not make a request');
195
196 // verify stats
197 QUnit.equal(loader.mediaBytesTransferred, 10, '10 bytes');
198 QUnit.equal(loader.mediaTransferDuration, 1, '1 ms (clock above)');
199 QUnit.equal(loader.mediaRequests, 1, '1 request');
180 }); 200 });
181 201
182 QUnit.test('calculates bandwidth after downloading a segment', function() { 202 QUnit.test('calculates bandwidth after downloading a segment', function() {
...@@ -191,7 +211,12 @@ QUnit.test('calculates bandwidth after downloading a segment', function() { ...@@ -191,7 +211,12 @@ QUnit.test('calculates bandwidth after downloading a segment', function() {
191 211
192 QUnit.equal(loader.bandwidth, (10 / 100) * 8 * 1000, 'calculated bandwidth'); 212 QUnit.equal(loader.bandwidth, (10 / 100) * 8 * 1000, 'calculated bandwidth');
193 QUnit.equal(loader.roundTrip, 100, 'saves request round trip time'); 213 QUnit.equal(loader.roundTrip, 100, 'saves request round trip time');
194 QUnit.equal(loader.bytesReceived, 10, 'saves bytes received'); 214
215 // TODO: Bandwidth Stat will be stale??
216 // verify stats
217 QUnit.equal(loader.mediaBytesTransferred, 10, '10 bytes');
218 QUnit.equal(loader.mediaTransferDuration, 100, '100 ms (clock above)');
219 QUnit.equal(loader.mediaRequests, 1, '1 request');
195 }); 220 });
196 221
197 QUnit.test('segment request timeouts reset bandwidth', function() { 222 QUnit.test('segment request timeouts reset bandwidth', function() {
...@@ -223,6 +248,10 @@ QUnit.test('appending a segment triggers progress', function() { ...@@ -223,6 +248,10 @@ QUnit.test('appending a segment triggers progress', function() {
223 mediaSource.sourceBuffers[0].trigger('updateend'); 248 mediaSource.sourceBuffers[0].trigger('updateend');
224 249
225 QUnit.equal(progresses, 1, 'fired progress'); 250 QUnit.equal(progresses, 1, 'fired progress');
251
252 // verify stats
253 QUnit.equal(loader.mediaBytesTransferred, 10, '10 bytes');
254 QUnit.equal(loader.mediaRequests, 1, '1 request');
226 }); 255 });
227 256
228 QUnit.test('only requests one segment at a time', function() { 257 QUnit.test('only requests one segment at a time', function() {
...@@ -251,6 +280,11 @@ QUnit.test('only appends one segment at a time', function() { ...@@ -251,6 +280,11 @@ QUnit.test('only appends one segment at a time', function() {
251 QUnit.equal(mediaSource.sourceBuffers[0].updates_.filter( 280 QUnit.equal(mediaSource.sourceBuffers[0].updates_.filter(
252 update => update.append).length, 1, 'only one append'); 281 update => update.append).length, 1, 'only one append');
253 QUnit.equal(this.requests.length, 0, 'only made one request'); 282 QUnit.equal(this.requests.length, 0, 'only made one request');
283
284 // verify stats
285 QUnit.equal(loader.mediaBytesTransferred, 10, '10 bytes');
286 QUnit.equal(loader.mediaTransferDuration, 100, '100 ms (clock above)');
287 QUnit.equal(loader.mediaRequests, 1, '1 request');
254 }); 288 });
255 289
256 QUnit.test('adjusts the playlist offset if no buffering progress is made', function() { 290 QUnit.test('adjusts the playlist offset if no buffering progress is made', function() {
...@@ -288,6 +322,11 @@ QUnit.test('adjusts the playlist offset if no buffering progress is made', funct ...@@ -288,6 +322,11 @@ QUnit.test('adjusts the playlist offset if no buffering progress is made', funct
288 322
289 // so the loader should try the next segment 323 // so the loader should try the next segment
290 QUnit.equal(this.requests[0].url, '1.ts', 'moved ahead a segment'); 324 QUnit.equal(this.requests[0].url, '1.ts', 'moved ahead a segment');
325
326 // verify stats
327 QUnit.equal(loader.mediaBytesTransferred, 20, '20 bytes');
328 QUnit.equal(loader.mediaTransferDuration, 2, '2 ms (clocks above)');
329 QUnit.equal(loader.mediaRequests, 2, '2 requests');
291 }); 330 });
292 331
293 QUnit.test('never attempt to load a segment that ' + 332 QUnit.test('never attempt to load a segment that ' +
...@@ -319,6 +358,11 @@ QUnit.test('never attempt to load a segment that ' + ...@@ -319,6 +358,11 @@ QUnit.test('never attempt to load a segment that ' +
319 358
320 // the loader should move on to the next segment 359 // the loader should move on to the next segment
321 QUnit.equal(this.requests[0].url, '1.ts', 'moved ahead a segment'); 360 QUnit.equal(this.requests[0].url, '1.ts', 'moved ahead a segment');
361
362 // verify stats
363 QUnit.equal(loader.mediaBytesTransferred, 10, '10 bytes');
364 QUnit.equal(loader.mediaTransferDuration, 1, '1 ms (clocks above)');
365 QUnit.equal(loader.mediaRequests, 1, '1 requests');
322 }); 366 });
323 367
324 QUnit.test('adjusts the playlist offset if no buffering progress is made', function() { 368 QUnit.test('adjusts the playlist offset if no buffering progress is made', function() {
...@@ -356,6 +400,11 @@ QUnit.test('adjusts the playlist offset if no buffering progress is made', funct ...@@ -356,6 +400,11 @@ QUnit.test('adjusts the playlist offset if no buffering progress is made', funct
356 400
357 // so the loader should try the next segment 401 // so the loader should try the next segment
358 QUnit.equal(this.requests[0].url, '1.ts', 'moved ahead a segment'); 402 QUnit.equal(this.requests[0].url, '1.ts', 'moved ahead a segment');
403
404 // verify stats
405 QUnit.equal(loader.mediaBytesTransferred, 20, '20 bytes');
406 QUnit.equal(loader.mediaTransferDuration, 2, '2 ms (clocks above)');
407 QUnit.equal(loader.mediaRequests, 2, '2 requests');
359 }); 408 });
360 409
361 QUnit.test('adjusts the playlist offset even when segment.end is set if no' + 410 QUnit.test('adjusts the playlist offset even when segment.end is set if no' +
...@@ -453,6 +502,10 @@ QUnit.test('abort does not cancel segment processing in progress', function() { ...@@ -453,6 +502,10 @@ QUnit.test('abort does not cancel segment processing in progress', function() {
453 502
454 loader.abort(); 503 loader.abort();
455 QUnit.equal(loader.state, 'APPENDING', 'still appending'); 504 QUnit.equal(loader.state, 'APPENDING', 'still appending');
505
506 // verify stats
507 QUnit.equal(loader.mediaBytesTransferred, 10, '10 bytes');
508 QUnit.equal(loader.mediaRequests, 1, '1 request');
456 }); 509 });
457 510
458 QUnit.test('sets the timestampOffset on timeline change', function() { 511 QUnit.test('sets the timestampOffset on timeline change', function() {
...@@ -474,6 +527,10 @@ QUnit.test('sets the timestampOffset on timeline change', function() { ...@@ -474,6 +527,10 @@ QUnit.test('sets the timestampOffset on timeline change', function() {
474 this.requests[0].response = new Uint8Array(10).buffer; 527 this.requests[0].response = new Uint8Array(10).buffer;
475 this.requests.shift().respond(200, null, ''); 528 this.requests.shift().respond(200, null, '');
476 QUnit.equal(mediaSource.sourceBuffers[0].timestampOffset, 10, 'set timestampOffset'); 529 QUnit.equal(mediaSource.sourceBuffers[0].timestampOffset, 10, 'set timestampOffset');
530
531 // verify stats
532 QUnit.equal(loader.mediaBytesTransferred, 20, '20 bytes');
533 QUnit.equal(loader.mediaRequests, 2, '2 requests');
477 }); 534 });
478 535
479 QUnit.test('tracks segment end times as they are buffered', function() { 536 QUnit.test('tracks segment end times as they are buffered', function() {
...@@ -491,6 +548,10 @@ QUnit.test('tracks segment end times as they are buffered', function() { ...@@ -491,6 +548,10 @@ QUnit.test('tracks segment end times as they are buffered', function() {
491 ]); 548 ]);
492 mediaSource.sourceBuffers[0].trigger('updateend'); 549 mediaSource.sourceBuffers[0].trigger('updateend');
493 QUnit.equal(playlist.segments[0].end, 9.5, 'updated duration'); 550 QUnit.equal(playlist.segments[0].end, 9.5, 'updated duration');
551
552 // verify stats
553 QUnit.equal(loader.mediaBytesTransferred, 10, '10 bytes');
554 QUnit.equal(loader.mediaRequests, 1, '1 request');
494 }); 555 });
495 556
496 QUnit.test('segment 404s should trigger an error', function() { 557 QUnit.test('segment 404s should trigger an error', function() {
...@@ -548,6 +609,10 @@ QUnit.test('fires ended at the end of a playlist', function() { ...@@ -548,6 +609,10 @@ QUnit.test('fires ended at the end of a playlist', function() {
548 mediaSource.sourceBuffers[0].buffered = videojs.createTimeRanges([[0, 10]]); 609 mediaSource.sourceBuffers[0].buffered = videojs.createTimeRanges([[0, 10]]);
549 mediaSource.sourceBuffers[0].trigger('updateend'); 610 mediaSource.sourceBuffers[0].trigger('updateend');
550 QUnit.equal(endOfStreams, 1, 'triggered ended'); 611 QUnit.equal(endOfStreams, 1, 'triggered ended');
612
613 // verify stats
614 QUnit.equal(loader.mediaBytesTransferred, 10, '10 bytes');
615 QUnit.equal(loader.mediaRequests, 1, '1 request');
551 }); 616 });
552 617
553 QUnit.test('live playlists do not trigger ended', function() { 618 QUnit.test('live playlists do not trigger ended', function() {
...@@ -572,6 +637,10 @@ QUnit.test('live playlists do not trigger ended', function() { ...@@ -572,6 +637,10 @@ QUnit.test('live playlists do not trigger ended', function() {
572 mediaSource.sourceBuffers[0].buffered = videojs.createTimeRanges([[0, 10]]); 637 mediaSource.sourceBuffers[0].buffered = videojs.createTimeRanges([[0, 10]]);
573 mediaSource.sourceBuffers[0].trigger('updateend'); 638 mediaSource.sourceBuffers[0].trigger('updateend');
574 QUnit.equal(endOfStreams, 0, 'did not trigger ended'); 639 QUnit.equal(endOfStreams, 0, 'did not trigger ended');
640
641 // verify stats
642 QUnit.equal(loader.mediaBytesTransferred, 10, '10 bytes');
643 QUnit.equal(loader.mediaRequests, 1, '1 request');
575 }); 644 });
576 645
577 QUnit.test('respects the global withCredentials option', function() { 646 QUnit.test('respects the global withCredentials option', function() {
...@@ -781,6 +850,10 @@ QUnit.test('the key is saved to the segment in the correct format', function() { ...@@ -781,6 +850,10 @@ QUnit.test('the key is saved to the segment in the correct format', function() {
781 QUnit.deepEqual(segment.key.bytes, 850 QUnit.deepEqual(segment.key.bytes,
782 new Uint32Array([0, 0x01000000, 0x02000000, 0x03000000]), 851 new Uint32Array([0, 0x01000000, 0x02000000, 0x03000000]),
783 'passed the specified segment key'); 852 'passed the specified segment key');
853
854 // verify stats
855 QUnit.equal(loader.mediaBytesTransferred, 10, '10 bytes');
856 QUnit.equal(loader.mediaRequests, 1, '1 request was completed');
784 }); 857 });
785 858
786 QUnit.test('supplies media sequence of current segment as the IV by default, if no IV ' + 859 QUnit.test('supplies media sequence of current segment as the IV by default, if no IV ' +
...@@ -811,6 +884,10 @@ function() { ...@@ -811,6 +884,10 @@ function() {
811 884
812 QUnit.deepEqual(segment.key.iv, new Uint32Array([0, 0, 0, 5]), 885 QUnit.deepEqual(segment.key.iv, new Uint32Array([0, 0, 0, 5]),
813 'the IV for the segment is the media sequence'); 886 'the IV for the segment is the media sequence');
887
888 // verify stats
889 QUnit.equal(loader.mediaBytesTransferred, 10, '10 bytes');
890 QUnit.equal(loader.mediaRequests, 1, '1 request');
814 }); 891 });
815 892
816 QUnit.test('segment with key has decrypted bytes appended during processing', function() { 893 QUnit.test('segment with key has decrypted bytes appended during processing', function() {
...@@ -839,6 +916,10 @@ QUnit.test('segment with key has decrypted bytes appended during processing', fu ...@@ -839,6 +916,10 @@ QUnit.test('segment with key has decrypted bytes appended during processing', fu
839 // Allow the decrypter's async stream to run the callback 916 // Allow the decrypter's async stream to run the callback
840 this.clock.tick(1); 917 this.clock.tick(1);
841 QUnit.ok(loader.pendingSegment_.bytes, 'decrypted bytes in segment'); 918 QUnit.ok(loader.pendingSegment_.bytes, 'decrypted bytes in segment');
919
920 // verify stats
921 QUnit.equal(loader.mediaBytesTransferred, 8, '8 bytes');
922 QUnit.equal(loader.mediaRequests, 1, '1 request');
842 }); 923 });
843 924
844 QUnit.test('calling load with an encrypted segment waits for both key and segment ' + 925 QUnit.test('calling load with an encrypted segment waits for both key and segment ' +
...@@ -864,6 +945,10 @@ QUnit.test('calling load with an encrypted segment waits for both key and segmen ...@@ -864,6 +945,10 @@ QUnit.test('calling load with an encrypted segment waits for both key and segmen
864 keyRequest.response = new Uint32Array([0, 0, 0, 0]).buffer; 945 keyRequest.response = new Uint32Array([0, 0, 0, 0]).buffer;
865 keyRequest.respond(200, null, ''); 946 keyRequest.respond(200, null, '');
866 QUnit.equal(loader.state, 'DECRYPTING', 'moves to decrypting state'); 947 QUnit.equal(loader.state, 'DECRYPTING', 'moves to decrypting state');
948
949 // verify stats
950 QUnit.equal(loader.mediaBytesTransferred, 10, '10 bytes');
951 QUnit.equal(loader.mediaRequests, 1, '1 request');
867 }); 952 });
868 953
869 QUnit.test('key request timeouts reset bandwidth', function() { 954 QUnit.test('key request timeouts reset bandwidth', function() {
......
...@@ -139,6 +139,25 @@ QUnit.test('starts playing if autoplay is specified', function() { ...@@ -139,6 +139,25 @@ QUnit.test('starts playing if autoplay is specified', function() {
139 QUnit.ok(!this.player.paused(), 'not paused'); 139 QUnit.ok(!this.player.paused(), 'not paused');
140 }); 140 });
141 141
142 QUnit.test('stats are reset on each new source', function() {
143 this.player.src({
144 src: 'manifest/playlist.m3u8',
145 type: 'application/vnd.apple.mpegurl'
146 });
147
148 // make sure play() is called *after* the media source opens
149 openMediaSource(this.player, this.clock);
150 standardXHRResponse(this.requests.shift());
151 standardXHRResponse(this.requests.shift());
152
153 QUnit.equal(this.player.tech_.hls.stats.mediaBytesTransferred, 16, 'stat is set');
154 this.player.src({
155 src: 'manifest/master.m3u8',
156 type: 'application/vnd.apple.mpegurl'
157 });
158 QUnit.equal(this.player.tech_.hls.stats.mediaBytesTransferred, 0, 'stat is reset');
159 });
160
142 QUnit.test('XHR requests first byte range on play', function() { 161 QUnit.test('XHR requests first byte range on play', function() {
143 this.player.src({ 162 this.player.src({
144 src: 'manifest/playlist.m3u8', 163 src: 'manifest/playlist.m3u8',
...@@ -359,6 +378,10 @@ QUnit.test('starts downloading a segment on loadedmetadata', function() { ...@@ -359,6 +378,10 @@ QUnit.test('starts downloading a segment on loadedmetadata', function() {
359 QUnit.strictEqual(this.requests[1].url, 378 QUnit.strictEqual(this.requests[1].url,
360 absoluteUrl('manifest/media-00001.ts'), 379 absoluteUrl('manifest/media-00001.ts'),
361 'the first segment is requested'); 380 'the first segment is requested');
381
382 // verify stats
383 QUnit.equal(this.player.tech_.hls.stats.mediaBytesTransferred, 16, '16 bytes');
384 QUnit.equal(this.player.tech_.hls.stats.mediaRequests, 1, '1 request');
362 }); 385 });
363 386
364 QUnit.test('re-initializes the handler for each source', function() { 387 QUnit.test('re-initializes the handler for each source', function() {
...@@ -433,6 +456,10 @@ QUnit.test('downloads media playlists after loading the master', function() { ...@@ -433,6 +456,10 @@ QUnit.test('downloads media playlists after loading the master', function() {
433 QUnit.strictEqual(this.requests[2].url, 456 QUnit.strictEqual(this.requests[2].url,
434 absoluteUrl('manifest/media2-00001.ts'), 457 absoluteUrl('manifest/media2-00001.ts'),
435 'first segment requested'); 458 'first segment requested');
459
460 // verify stats
461 QUnit.equal(this.player.tech_.hls.stats.mediaBytesTransferred, 16, '16 bytes');
462 QUnit.equal(this.player.tech_.hls.stats.mediaRequests, 1, '1 request');
436 }); 463 });
437 464
438 QUnit.test('upshifts if the initial bandwidth hint is high', function() { 465 QUnit.test('upshifts if the initial bandwidth hint is high', function() {
...@@ -462,6 +489,10 @@ QUnit.test('upshifts if the initial bandwidth hint is high', function() { ...@@ -462,6 +489,10 @@ QUnit.test('upshifts if the initial bandwidth hint is high', function() {
462 absoluteUrl('manifest/media2-00001.ts'), 489 absoluteUrl('manifest/media2-00001.ts'),
463 'first segment requested' 490 'first segment requested'
464 ); 491 );
492
493 // verify stats
494 QUnit.equal(this.player.tech_.hls.stats.mediaBytesTransferred, 16, '16 bytes');
495 QUnit.equal(this.player.tech_.hls.stats.mediaRequests, 1, '1 request');
465 }); 496 });
466 497
467 QUnit.test('downshifts if the initial bandwidth hint is low', function() { 498 QUnit.test('downshifts if the initial bandwidth hint is low', function() {
...@@ -485,6 +516,10 @@ QUnit.test('downshifts if the initial bandwidth hint is low', function() { ...@@ -485,6 +516,10 @@ QUnit.test('downshifts if the initial bandwidth hint is low', function() {
485 QUnit.strictEqual(this.requests[2].url, 516 QUnit.strictEqual(this.requests[2].url,
486 absoluteUrl('manifest/media1-00001.ts'), 517 absoluteUrl('manifest/media1-00001.ts'),
487 'first segment requested'); 518 'first segment requested');
519
520 // verify stats
521 QUnit.equal(this.player.tech_.hls.stats.mediaBytesTransferred, 16, '16 bytes');
522 QUnit.equal(this.player.tech_.hls.stats.mediaRequests, 1, '1 request');
488 }); 523 });
489 524
490 QUnit.test('buffer checks are noops until a media playlist is ready', function() { 525 QUnit.test('buffer checks are noops until a media playlist is ready', function() {
...@@ -499,6 +534,7 @@ QUnit.test('buffer checks are noops until a media playlist is ready', function() ...@@ -499,6 +534,7 @@ QUnit.test('buffer checks are noops until a media playlist is ready', function()
499 QUnit.strictEqual(this.requests[0].url, 534 QUnit.strictEqual(this.requests[0].url,
500 'manifest/media.m3u8', 535 'manifest/media.m3u8',
501 'media playlist requested'); 536 'media playlist requested');
537
502 }); 538 });
503 539
504 QUnit.test('buffer checks are noops when only the master is ready', function() { 540 QUnit.test('buffer checks are noops when only the master is ready', function() {
...@@ -533,6 +569,9 @@ QUnit.test('buffer checks are noops when only the master is ready', function() { ...@@ -533,6 +569,9 @@ QUnit.test('buffer checks are noops when only the master is ready', function() {
533 QUnit.strictEqual(this.requests[0].url, 569 QUnit.strictEqual(this.requests[0].url,
534 absoluteUrl('manifest/media1.m3u8'), 570 absoluteUrl('manifest/media1.m3u8'),
535 'media playlist requested'); 571 'media playlist requested');
572
573 // verify stats
574 QUnit.equal(this.player.tech_.hls.stats.bandwidth, 1, 'bandwidth set above');
536 }); 575 });
537 576
538 QUnit.test('selects a playlist below the current bandwidth', function() { 577 QUnit.test('selects a playlist below the current bandwidth', function() {
...@@ -556,6 +595,9 @@ QUnit.test('selects a playlist below the current bandwidth', function() { ...@@ -556,6 +595,9 @@ QUnit.test('selects a playlist below the current bandwidth', function() {
556 QUnit.strictEqual(playlist, 595 QUnit.strictEqual(playlist,
557 this.player.tech_.hls.playlists.master.playlists[1], 596 this.player.tech_.hls.playlists.master.playlists[1],
558 'the low bitrate stream is selected'); 597 'the low bitrate stream is selected');
598
599 // verify stats
600 QUnit.equal(this.player.tech_.hls.stats.bandwidth, 10, 'bandwidth set above');
559 }); 601 });
560 602
561 QUnit.test('allows initial bandwidth to be provided', function() { 603 QUnit.test('allows initial bandwidth to be provided', function() {
...@@ -574,6 +616,9 @@ QUnit.test('allows initial bandwidth to be provided', function() { ...@@ -574,6 +616,9 @@ QUnit.test('allows initial bandwidth to be provided', function() {
574 QUnit.equal(this.player.tech_.hls.bandwidth, 616 QUnit.equal(this.player.tech_.hls.bandwidth,
575 500, 617 500,
576 'prefers user-specified initial bandwidth'); 618 'prefers user-specified initial bandwidth');
619
620 // verify stats
621 QUnit.equal(this.player.tech_.hls.stats.bandwidth, 500, 'bandwidth set above');
577 }); 622 });
578 623
579 QUnit.test('raises the minimum bitrate for a stream proportionially', function() { 624 QUnit.test('raises the minimum bitrate for a stream proportionially', function() {
...@@ -598,6 +643,9 @@ QUnit.test('raises the minimum bitrate for a stream proportionially', function() ...@@ -598,6 +643,9 @@ QUnit.test('raises the minimum bitrate for a stream proportionially', function()
598 QUnit.strictEqual(playlist, 643 QUnit.strictEqual(playlist,
599 this.player.tech_.hls.playlists.master.playlists[1], 644 this.player.tech_.hls.playlists.master.playlists[1],
600 'a lower bitrate stream is selected'); 645 'a lower bitrate stream is selected');
646
647 // verify stats
648 QUnit.equal(this.player.tech_.hls.stats.bandwidth, 11, 'bandwidth set above');
601 }); 649 });
602 650
603 QUnit.test('uses the lowest bitrate if no other is suitable', function() { 651 QUnit.test('uses the lowest bitrate if no other is suitable', function() {
...@@ -619,6 +667,9 @@ QUnit.test('uses the lowest bitrate if no other is suitable', function() { ...@@ -619,6 +667,9 @@ QUnit.test('uses the lowest bitrate if no other is suitable', function() {
619 QUnit.strictEqual(playlist, 667 QUnit.strictEqual(playlist,
620 this.player.tech_.hls.playlists.master.playlists[1], 668 this.player.tech_.hls.playlists.master.playlists[1],
621 'the lowest bitrate stream is selected'); 669 'the lowest bitrate stream is selected');
670
671 // verify stats
672 QUnit.equal(this.player.tech_.hls.stats.bandwidth, 1, 'bandwidth set above');
622 }); 673 });
623 674
624 QUnit.test('selects the correct rendition by tech dimensions', function() { 675 QUnit.test('selects the correct rendition by tech dimensions', function() {
...@@ -683,6 +734,9 @@ QUnit.test('selects the correct rendition by tech dimensions', function() { ...@@ -683,6 +734,9 @@ QUnit.test('selects the correct rendition by tech dimensions', function() {
683 QUnit.equal(playlist.attributes.BANDWIDTH, 734 QUnit.equal(playlist.attributes.BANDWIDTH,
684 440000, 735 440000,
685 'should have the expected bandwidth in case of multiple, if exact match'); 736 'should have the expected bandwidth in case of multiple, if exact match');
737
738 // verify stats
739 QUnit.equal(this.player.tech_.hls.stats.bandwidth, 3000000, 'bandwidth set above');
686 }); 740 });
687 741
688 QUnit.test('selects the highest bitrate playlist when the player dimensions are ' + 742 QUnit.test('selects the highest bitrate playlist when the player dimensions are ' +
...@@ -713,6 +767,9 @@ QUnit.test('selects the highest bitrate playlist when the player dimensions are ...@@ -713,6 +767,9 @@ QUnit.test('selects the highest bitrate playlist when the player dimensions are
713 QUnit.equal(playlist.attributes.BANDWIDTH, 767 QUnit.equal(playlist.attributes.BANDWIDTH,
714 1000, 768 1000,
715 'selected the highest bandwidth variant'); 769 'selected the highest bandwidth variant');
770
771 // verify stats
772 QUnit.equal(this.player.tech_.hls.stats.bandwidth, 1e10, 'bandwidth set above');
716 }); 773 });
717 774
718 QUnit.test('filters playlists that are currently excluded', function() { 775 QUnit.test('filters playlists that are currently excluded', function() {
...@@ -748,6 +805,9 @@ QUnit.test('filters playlists that are currently excluded', function() { ...@@ -748,6 +805,9 @@ QUnit.test('filters playlists that are currently excluded', function() {
748 QUnit.equal(playlist, 805 QUnit.equal(playlist,
749 this.player.tech_.hls.playlists.master.playlists[0], 806 this.player.tech_.hls.playlists.master.playlists[0],
750 'expired the exclusion'); 807 'expired the exclusion');
808
809 // verify stats
810 QUnit.equal(this.player.tech_.hls.stats.bandwidth, 1e10, 'bandwidth set above');
751 }); 811 });
752 812
753 QUnit.test('does not blacklist compatible H.264 codec strings', function() { 813 QUnit.test('does not blacklist compatible H.264 codec strings', function() {
...@@ -778,6 +838,9 @@ QUnit.test('does not blacklist compatible H.264 codec strings', function() { ...@@ -778,6 +838,9 @@ QUnit.test('does not blacklist compatible H.264 codec strings', function() {
778 QUnit.strictEqual(typeof master.playlists[1].excludeUntil, 838 QUnit.strictEqual(typeof master.playlists[1].excludeUntil,
779 'undefined', 839 'undefined',
780 'did not blacklist'); 840 'did not blacklist');
841
842 // verify stats
843 QUnit.equal(this.player.tech_.hls.stats.bandwidth, 1, 'bandwidth set above');
781 }); 844 });
782 845
783 QUnit.test('does not blacklist compatible AAC codec strings', function() { 846 QUnit.test('does not blacklist compatible AAC codec strings', function() {
...@@ -808,6 +871,9 @@ QUnit.test('does not blacklist compatible AAC codec strings', function() { ...@@ -808,6 +871,9 @@ QUnit.test('does not blacklist compatible AAC codec strings', function() {
808 QUnit.strictEqual(typeof master.playlists[1].excludeUntil, 871 QUnit.strictEqual(typeof master.playlists[1].excludeUntil,
809 'undefined', 872 'undefined',
810 'did not blacklist'); 873 'did not blacklist');
874
875 // verify stats
876 QUnit.equal(this.player.tech_.hls.stats.bandwidth, 1, 'bandwidth set above');
811 }); 877 });
812 878
813 QUnit.test('cancels outstanding XHRs when seeking', function() { 879 QUnit.test('cancels outstanding XHRs when seeking', function() {
...@@ -883,6 +949,9 @@ QUnit.test('segment 404 should trigger blacklisting of media', function() { ...@@ -883,6 +949,9 @@ QUnit.test('segment 404 should trigger blacklisting of media', function() {
883 this.requests[2].respond(400); 949 this.requests[2].respond(400);
884 QUnit.ok(media.excludeUntil > 0, 'original media blacklisted for some time'); 950 QUnit.ok(media.excludeUntil > 0, 'original media blacklisted for some time');
885 QUnit.equal(this.env.log.warn.calls, 1, 'warning logged for blacklist'); 951 QUnit.equal(this.env.log.warn.calls, 1, 'warning logged for blacklist');
952
953 // verify stats
954 QUnit.equal(this.player.tech_.hls.stats.bandwidth, 20000, 'bandwidth set above');
886 }); 955 });
887 956
888 QUnit.test('playlist 404 should blacklist media', function() { 957 QUnit.test('playlist 404 should blacklist media', function() {
...@@ -917,6 +986,8 @@ QUnit.test('playlist 404 should blacklist media', function() { ...@@ -917,6 +986,8 @@ QUnit.test('playlist 404 should blacklist media', function() {
917 QUnit.ok(media.excludeUntil > 0, 'original media blacklisted for some time'); 986 QUnit.ok(media.excludeUntil > 0, 'original media blacklisted for some time');
918 QUnit.equal(this.env.log.warn.calls, 1, 'warning logged for blacklist'); 987 QUnit.equal(this.env.log.warn.calls, 1, 'warning logged for blacklist');
919 988
989 // verify stats
990 QUnit.equal(this.player.tech_.hls.stats.bandwidth, 1e10, 'bandwidth set above');
920 }); 991 });
921 992
922 QUnit.test('seeking in an empty playlist is a non-erroring noop', function() { 993 QUnit.test('seeking in an empty playlist is a non-erroring noop', function() {
...@@ -965,6 +1036,9 @@ QUnit.test('fire loadedmetadata once we successfully load a playlist', function( ...@@ -965,6 +1036,9 @@ QUnit.test('fire loadedmetadata once we successfully load a playlist', function(
965 standardXHRResponse(this.requests.shift()); 1036 standardXHRResponse(this.requests.shift());
966 QUnit.equal(count, 1, 1037 QUnit.equal(count, 1,
967 'loadedMedia triggered after successful recovery from 404'); 1038 'loadedMedia triggered after successful recovery from 404');
1039
1040 // verify stats
1041 QUnit.equal(this.player.tech_.hls.stats.bandwidth, 20000, 'bandwidth set above');
968 }); 1042 });
969 1043
970 QUnit.test('sets seekable and duration for live playlists', function() { 1044 QUnit.test('sets seekable and duration for live playlists', function() {
...@@ -1221,6 +1295,9 @@ QUnit.test('resets the switching algorithm if a request times out', function() { ...@@ -1221,6 +1295,9 @@ QUnit.test('resets the switching algorithm if a request times out', function() {
1221 QUnit.strictEqual(this.player.tech_.hls.playlists.media(), 1295 QUnit.strictEqual(this.player.tech_.hls.playlists.media(),
1222 this.player.tech_.hls.playlists.master.playlists[1], 1296 this.player.tech_.hls.playlists.master.playlists[1],
1223 'reset to the lowest bitrate playlist'); 1297 'reset to the lowest bitrate playlist');
1298
1299 // verify stats
1300 QUnit.equal(this.player.tech_.hls.stats.bandwidth, 1, 'bandwidth is reset too');
1224 }); 1301 });
1225 1302
1226 QUnit.test('disposes the playlist loader', function() { 1303 QUnit.test('disposes the playlist loader', function() {
...@@ -1411,6 +1488,10 @@ QUnit.test('calling play() at the end of a video replays', function() { ...@@ -1411,6 +1488,10 @@ QUnit.test('calling play() at the end of a video replays', function() {
1411 1488
1412 this.player.tech_.trigger('play'); 1489 this.player.tech_.trigger('play');
1413 QUnit.equal(seekTime, 0, 'seeked to the beginning'); 1490 QUnit.equal(seekTime, 0, 'seeked to the beginning');
1491
1492 // verify stats
1493 QUnit.equal(this.player.tech_.hls.stats.mediaBytesTransferred, 16, '16 bytes');
1494 QUnit.equal(this.player.tech_.hls.stats.mediaRequests, 1, '1 request');
1414 }); 1495 });
1415 1496
1416 QUnit.test('keys are resolved relative to the master playlist', function() { 1497 QUnit.test('keys are resolved relative to the master playlist', function() {
...@@ -1435,6 +1516,9 @@ QUnit.test('keys are resolved relative to the master playlist', function() { ...@@ -1435,6 +1516,9 @@ QUnit.test('keys are resolved relative to the master playlist', function() {
1435 QUnit.equal(this.requests[0].url, 1516 QUnit.equal(this.requests[0].url,
1436 absoluteUrl('video/playlist/keys/key.php'), 1517 absoluteUrl('video/playlist/keys/key.php'),
1437 'resolves multiple relative paths'); 1518 'resolves multiple relative paths');
1519
1520 // verify stats
1521 QUnit.equal(this.player.tech_.hls.stats.bandwidth, 4194304, 'default');
1438 }); 1522 });
1439 1523
1440 QUnit.test('keys are resolved relative to their containing playlist', function() { 1524 QUnit.test('keys are resolved relative to their containing playlist', function() {
...@@ -1487,6 +1571,10 @@ QUnit.test('seeking should abort an outstanding key request and create a new one ...@@ -1487,6 +1571,10 @@ QUnit.test('seeking should abort an outstanding key request and create a new one
1487 'https://example.com/' + 1571 'https://example.com/' +
1488 this.player.tech_.hls.playlists.media().segments[1].key.uri, 1572 this.player.tech_.hls.playlists.media().segments[1].key.uri,
1489 'urls should match'); 1573 'urls should match');
1574
1575 // verify stats
1576 QUnit.equal(this.player.tech_.hls.stats.mediaBytesTransferred, 16, '16 bytes');
1577 QUnit.equal(this.player.tech_.hls.stats.mediaRequests, 1, '1 request');
1490 }); 1578 });
1491 1579
1492 QUnit.test('switching playlists with an outstanding key request aborts request and ' + 1580 QUnit.test('switching playlists with an outstanding key request aborts request and ' +
...@@ -1529,6 +1617,9 @@ QUnit.test('switching playlists with an outstanding key request aborts request a ...@@ -1529,6 +1617,9 @@ QUnit.test('switching playlists with an outstanding key request aborts request a
1529 QUnit.equal(this.requests[1].url, 1617 QUnit.equal(this.requests[1].url,
1530 'http://media.example.com/fileSequence52-A.ts', 1618 'http://media.example.com/fileSequence52-A.ts',
1531 'requested the segment'); 1619 'requested the segment');
1620 // verify stats
1621 QUnit.equal(this.player.tech_.hls.stats.mediaBytesTransferred, 16, '16 bytes');
1622 QUnit.equal(this.player.tech_.hls.stats.mediaRequests, 1, '1 request');
1532 }); 1623 });
1533 1624
1534 QUnit.test('does not download segments if preload option set to none', function() { 1625 QUnit.test('does not download segments if preload option set to none', function() {
...@@ -1549,6 +1640,9 @@ QUnit.test('does not download segments if preload option set to none', function( ...@@ -1549,6 +1640,9 @@ QUnit.test('does not download segments if preload option set to none', function(
1549 return !(/m3u8$/).test(request.uri); 1640 return !(/m3u8$/).test(request.uri);
1550 }); 1641 });
1551 QUnit.equal(this.requests.length, 0, 'did not download any segments'); 1642 QUnit.equal(this.requests.length, 0, 'did not download any segments');
1643
1644 // verify stats
1645 QUnit.equal(this.player.tech_.hls.stats.bandwidth, 4194304, 'default');
1552 }); 1646 });
1553 1647
1554 // workaround https://bugzilla.mozilla.org/show_bug.cgi?id=548397 1648 // workaround https://bugzilla.mozilla.org/show_bug.cgi?id=548397
...@@ -1572,6 +1666,9 @@ QUnit.test('selectPlaylist does not fail if getComputedStyle returns null', func ...@@ -1572,6 +1666,9 @@ QUnit.test('selectPlaylist does not fail if getComputedStyle returns null', func
1572 this.player.tech_.hls.selectPlaylist(); 1666 this.player.tech_.hls.selectPlaylist();
1573 QUnit.ok(true, 'should not throw'); 1667 QUnit.ok(true, 'should not throw');
1574 window.getComputedStyle = oldGetComputedStyle; 1668 window.getComputedStyle = oldGetComputedStyle;
1669
1670 // verify stats
1671 QUnit.equal(this.player.tech_.hls.stats.bandwidth, 4194304, 'default');
1575 }); 1672 });
1576 1673
1577 QUnit.test('resolves relative key URLs against the playlist', function() { 1674 QUnit.test('resolves relative key URLs against the playlist', function() {
...@@ -1608,6 +1705,9 @@ QUnit.test('adds 1 default audio track if we have not parsed any, and the playli ...@@ -1608,6 +1705,9 @@ QUnit.test('adds 1 default audio track if we have not parsed any, and the playli
1608 1705
1609 QUnit.equal(this.player.audioTracks().length, 1, 'one audio track after load'); 1706 QUnit.equal(this.player.audioTracks().length, 1, 'one audio track after load');
1610 QUnit.ok(this.player.audioTracks()[0] instanceof HlsAudioTrack, 'audio track is an hls audio track'); 1707 QUnit.ok(this.player.audioTracks()[0] instanceof HlsAudioTrack, 'audio track is an hls audio track');
1708
1709 // verify stats
1710 QUnit.equal(this.player.tech_.hls.stats.bandwidth, 4194304, 'default');
1611 }); 1711 });
1612 1712
1613 QUnit.test('adds 1 default audio track if in flash mode', function() { 1713 QUnit.test('adds 1 default audio track if in flash mode', function() {
...@@ -1673,6 +1773,9 @@ QUnit.test('adds audio tracks if we have parsed some from a playlist', function( ...@@ -1673,6 +1773,9 @@ QUnit.test('adds audio tracks if we have parsed some from a playlist', function(
1673 1773
1674 QUnit.equal(vjsAudioTracks[1].enabled, false, 'main track is disabled'); 1774 QUnit.equal(vjsAudioTracks[1].enabled, false, 'main track is disabled');
1675 QUnit.equal(hlsAudioTracks[1].enabled, false, 'main track is disabled'); 1775 QUnit.equal(hlsAudioTracks[1].enabled, false, 'main track is disabled');
1776
1777 // verify stats
1778 QUnit.equal(this.player.tech_.hls.stats.bandwidth, 4194304, 'default');
1676 }); 1779 });
1677 1780
1678 QUnit.test('audio info from audioinfo event is stored on hls', function() { 1781 QUnit.test('audio info from audioinfo event is stored on hls', function() {
...@@ -1754,6 +1857,9 @@ QUnit.test('audioinfo changes with three tracks, enabled track is blacklisted an ...@@ -1754,6 +1857,9 @@ QUnit.test('audioinfo changes with three tracks, enabled track is blacklisted an
1754 QUnit.equal(blacklistPlaylistCalls, 0, 'blacklist was not called on playlist'); 1857 QUnit.equal(blacklistPlaylistCalls, 0, 'blacklist was not called on playlist');
1755 QUnit.equal(this.env.log.warn.calls, 1, 'firefox issue warning logged'); 1858 QUnit.equal(this.env.log.warn.calls, 1, 'firefox issue warning logged');
1756 videojs.browser.IS_FIREFOX = oldIsFirefox; 1859 videojs.browser.IS_FIREFOX = oldIsFirefox;
1860
1861 // verify stats
1862 QUnit.equal(this.player.tech_.hls.stats.bandwidth, 4194304, 'default');
1757 }); 1863 });
1758 1864
1759 QUnit.test('audioinfo changes with one track, blacklist playlist', function() { 1865 QUnit.test('audioinfo changes with one track, blacklist playlist', function() {
...@@ -1787,6 +1893,9 @@ QUnit.test('audioinfo changes with one track, blacklist playlist', function() { ...@@ -1787,6 +1893,9 @@ QUnit.test('audioinfo changes with one track, blacklist playlist', function() {
1787 QUnit.equal(blacklistPlaylistCalls, 1, 'blacklist was called on playlist'); 1893 QUnit.equal(blacklistPlaylistCalls, 1, 'blacklist was called on playlist');
1788 QUnit.equal(this.env.log.warn.calls, 1, 'firefox issue warning logged'); 1894 QUnit.equal(this.env.log.warn.calls, 1, 'firefox issue warning logged');
1789 videojs.browser.IS_FIREFOX = oldIsFirefox; 1895 videojs.browser.IS_FIREFOX = oldIsFirefox;
1896
1897 // verify stats
1898 QUnit.equal(this.player.tech_.hls.stats.bandwidth, 4194304, 'default');
1790 }); 1899 });
1791 1900
1792 QUnit.test('audioinfo changes with three tracks, default is enabled, blacklisted playlist', function() { 1901 QUnit.test('audioinfo changes with three tracks, default is enabled, blacklisted playlist', function() {
...@@ -1835,6 +1944,9 @@ QUnit.test('audioinfo changes with three tracks, default is enabled, blacklisted ...@@ -1835,6 +1944,9 @@ QUnit.test('audioinfo changes with three tracks, default is enabled, blacklisted
1835 QUnit.equal(blacklistPlaylistCalls, 1, 'blacklist was called on playlist'); 1944 QUnit.equal(blacklistPlaylistCalls, 1, 'blacklist was called on playlist');
1836 QUnit.equal(this.env.log.warn.calls, 1, 'firefox issue warning logged'); 1945 QUnit.equal(this.env.log.warn.calls, 1, 'firefox issue warning logged');
1837 videojs.browser.IS_FIREFOX = oldIsFirefox; 1946 videojs.browser.IS_FIREFOX = oldIsFirefox;
1947
1948 // verify stats
1949 QUnit.equal(this.player.tech_.hls.stats.bandwidth, 4194304, 'default');
1838 }); 1950 });
1839 1951
1840 QUnit.test('cleans up the buffer when loading live segments', function() { 1952 QUnit.test('cleans up the buffer when loading live segments', function() {
...@@ -1884,6 +1996,10 @@ QUnit.test('cleans up the buffer when loading live segments', function() { ...@@ -1884,6 +1996,10 @@ QUnit.test('cleans up the buffer when loading live segments', function() {
1884 QUnit.equal(removes.length, 1, 'remove called'); 1996 QUnit.equal(removes.length, 1, 'remove called');
1885 QUnit.deepEqual(removes[0], [0, seekable.start(0)], 1997 QUnit.deepEqual(removes[0], [0, seekable.start(0)],
1886 'remove called with the right range'); 1998 'remove called with the right range');
1999
2000 // verify stats
2001 QUnit.equal(this.player.tech_.hls.stats.mediaBytesTransferred, 16, '16 bytes');
2002 QUnit.equal(this.player.tech_.hls.stats.mediaRequests, 1, '1 request');
1887 }); 2003 });
1888 2004
1889 QUnit.test('cleans up the buffer based on currentTime when loading a live segment ' + 2005 QUnit.test('cleans up the buffer based on currentTime when loading a live segment ' +
...@@ -1934,6 +2050,10 @@ QUnit.test('cleans up the buffer based on currentTime when loading a live segmen ...@@ -1934,6 +2050,10 @@ QUnit.test('cleans up the buffer based on currentTime when loading a live segmen
1934 QUnit.strictEqual(this.requests[0].url, 'liveStart30sBefore.m3u8', 'master playlist requested'); 2050 QUnit.strictEqual(this.requests[0].url, 'liveStart30sBefore.m3u8', 'master playlist requested');
1935 QUnit.equal(removes.length, 1, 'remove called'); 2051 QUnit.equal(removes.length, 1, 'remove called');
1936 QUnit.deepEqual(removes[0], [0, 80 - 60], 'remove called with the right range'); 2052 QUnit.deepEqual(removes[0], [0, 80 - 60], 'remove called with the right range');
2053
2054 // verify stats
2055 QUnit.equal(this.player.tech_.hls.stats.mediaBytesTransferred, 16, '16 bytes');
2056 QUnit.equal(this.player.tech_.hls.stats.mediaRequests, 1, '1 request');
1937 }); 2057 });
1938 2058
1939 QUnit.test('cleans up the buffer when loading VOD segments', function() { 2059 QUnit.test('cleans up the buffer when loading VOD segments', function() {
...@@ -1969,6 +2089,10 @@ QUnit.test('cleans up the buffer when loading VOD segments', function() { ...@@ -1969,6 +2089,10 @@ QUnit.test('cleans up the buffer when loading VOD segments', function() {
1969 'media playlist requested'); 2089 'media playlist requested');
1970 QUnit.equal(removes.length, 1, 'remove called'); 2090 QUnit.equal(removes.length, 1, 'remove called');
1971 QUnit.deepEqual(removes[0], [0, 120 - 60], 'remove called with the right range'); 2091 QUnit.deepEqual(removes[0], [0, 120 - 60], 'remove called with the right range');
2092
2093 // verify stats
2094 QUnit.equal(this.player.tech_.hls.stats.mediaBytesTransferred, 16, '16 bytes');
2095 QUnit.equal(this.player.tech_.hls.stats.mediaRequests, 1, '1 request');
1972 }); 2096 });
1973 2097
1974 QUnit.test('when mediaGroup changes enabled track should not change', function() { 2098 QUnit.test('when mediaGroup changes enabled track should not change', function() {
...@@ -2042,6 +2166,9 @@ QUnit.test('when mediaGroup changes enabled track should not change', function() ...@@ -2042,6 +2166,9 @@ QUnit.test('when mediaGroup changes enabled track should not change', function()
2042 QUnit.equal(trackOne.enabled, false, 'track 1 - still disabled'); 2166 QUnit.equal(trackOne.enabled, false, 'track 1 - still disabled');
2043 QUnit.equal(trackTwo.enabled, true, 'track 2 - still enabled'); 2167 QUnit.equal(trackTwo.enabled, true, 'track 2 - still enabled');
2044 QUnit.equal(trackThree.enabled, false, 'track 3 - disabled'); 2168 QUnit.equal(trackThree.enabled, false, 'track 3 - disabled');
2169
2170 // verify stats
2171 QUnit.equal(this.player.tech_.hls.stats.bandwidth, 4194304, 'default');
2045 }); 2172 });
2046 2173
2047 QUnit.test('Allows specifying the beforeRequest function on the player', function() { 2174 QUnit.test('Allows specifying the beforeRequest function on the player', function() {
...@@ -2062,6 +2189,9 @@ QUnit.test('Allows specifying the beforeRequest function on the player', functio ...@@ -2062,6 +2189,9 @@ QUnit.test('Allows specifying the beforeRequest function on the player', functio
2062 standardXHRResponse(this.requests.shift()); 2189 standardXHRResponse(this.requests.shift());
2063 2190
2064 QUnit.ok(beforeRequestCalled, 'beforeRequest was called'); 2191 QUnit.ok(beforeRequestCalled, 'beforeRequest was called');
2192
2193 // verify stats
2194 QUnit.equal(this.player.tech_.hls.stats.bandwidth, 4194304, 'default');
2065 }); 2195 });
2066 2196
2067 QUnit.test('Allows specifying the beforeRequest function globally', function() { 2197 QUnit.test('Allows specifying the beforeRequest function globally', function() {
...@@ -2082,6 +2212,9 @@ QUnit.test('Allows specifying the beforeRequest function globally', function() { ...@@ -2082,6 +2212,9 @@ QUnit.test('Allows specifying the beforeRequest function globally', function() {
2082 QUnit.ok(beforeRequestCalled, 'beforeRequest was called'); 2212 QUnit.ok(beforeRequestCalled, 'beforeRequest was called');
2083 2213
2084 delete videojs.Hls.xhr.beforeRequest; 2214 delete videojs.Hls.xhr.beforeRequest;
2215
2216 // verify stats
2217 QUnit.equal(this.player.tech_.hls.stats.bandwidth, 4194304, 'default');
2085 }); 2218 });
2086 2219
2087 QUnit.test('Allows overriding the global beforeRequest function', function() { 2220 QUnit.test('Allows overriding the global beforeRequest function', function() {
...@@ -2114,6 +2247,10 @@ QUnit.test('Allows overriding the global beforeRequest function', function() { ...@@ -2114,6 +2247,10 @@ QUnit.test('Allows overriding the global beforeRequest function', function() {
2114 'for the master playlist'); 2247 'for the master playlist');
2115 2248
2116 delete videojs.Hls.xhr.beforeRequest; 2249 delete videojs.Hls.xhr.beforeRequest;
2250
2251 // verify stats
2252 QUnit.equal(this.player.tech_.hls.stats.mediaBytesTransferred, 16, 'seen above');
2253 QUnit.equal(this.player.tech_.hls.stats.mediaRequests, 1, 'one segment request');
2117 }); 2254 });
2118 2255
2119 QUnit.module('HLS Integration', { 2256 QUnit.module('HLS Integration', {
...@@ -2151,6 +2288,26 @@ QUnit.test('aborts all in-flight work when disposed', function() { ...@@ -2151,6 +2288,26 @@ QUnit.test('aborts all in-flight work when disposed', function() {
2151 }); 2288 });
2152 }); 2289 });
2153 2290
2291 QUnit.test('stats are reset on dispose', function() {
2292 let hls = HlsSourceHandler('html5').handleSource({
2293 src: 'manifest/master.m3u8',
2294 type: 'application/vnd.apple.mpegurl'
2295 }, this.tech);
2296
2297 hls.mediaSource.trigger('sourceopen');
2298 // master
2299 standardXHRResponse(this.requests.shift());
2300 // media
2301 standardXHRResponse(this.requests.shift());
2302
2303 // media
2304 standardXHRResponse(this.requests.shift());
2305
2306 QUnit.equal(hls.stats.mediaBytesTransferred, 16, 'stat is set');
2307 hls.dispose();
2308 QUnit.equal(hls.stats.mediaBytesTransferred, 0, 'stat is reset');
2309 });
2310
2154 QUnit.test('detects fullscreen and triggers a quality change', function() { 2311 QUnit.test('detects fullscreen and triggers a quality change', function() {
2155 let qualityChanges = 0; 2312 let qualityChanges = 0;
2156 let hls = HlsSourceHandler('html5').handleSource({ 2313 let hls = HlsSourceHandler('html5').handleSource({
...@@ -2215,6 +2372,11 @@ QUnit.test('downloads additional playlists if required', function() { ...@@ -2215,6 +2372,11 @@ QUnit.test('downloads additional playlists if required', function() {
2215 hls.playlists.media().resolvedUri, 2372 hls.playlists.media().resolvedUri,
2216 'a new playlists was selected'); 2373 'a new playlists was selected');
2217 QUnit.ok(hls.playlists.media().segments, 'segments are now available'); 2374 QUnit.ok(hls.playlists.media().segments, 'segments are now available');
2375
2376 // verify stats
2377 QUnit.equal(hls.stats.bandwidth, 3000000, 'default');
2378 QUnit.equal(hls.stats.mediaBytesTransferred, 16, '16 bytes');
2379 QUnit.equal(hls.stats.mediaRequests, 1, '1 request');
2218 }); 2380 });
2219 2381
2220 QUnit.test('waits to download new segments until the media playlist is stable', function() { 2382 QUnit.test('waits to download new segments until the media playlist is stable', function() {
...@@ -2251,6 +2413,11 @@ QUnit.test('waits to download new segments until the media playlist is stable', ...@@ -2251,6 +2413,11 @@ QUnit.test('waits to download new segments until the media playlist is stable',
2251 standardXHRResponse(this.requests.shift()); 2413 standardXHRResponse(this.requests.shift());
2252 this.clock.tick(10 * 1000); 2414 this.clock.tick(10 * 1000);
2253 QUnit.equal(this.requests.length, 1, 'resumes segment fetching'); 2415 QUnit.equal(this.requests.length, 1, 'resumes segment fetching');
2416
2417 // verify stats
2418 QUnit.equal(hls.stats.bandwidth, Infinity, 'default');
2419 QUnit.equal(hls.stats.mediaBytesTransferred, 16, '16 bytes');
2420 QUnit.equal(hls.stats.mediaRequests, 1, '1 request');
2254 }); 2421 });
2255 2422
2256 QUnit.test('live playlist starts three target durations before live', function() { 2423 QUnit.test('live playlist starts three target durations before live', function() {
...@@ -2286,6 +2453,7 @@ QUnit.test('live playlist starts three target durations before live', function() ...@@ -2286,6 +2453,7 @@ QUnit.test('live playlist starts three target durations before live', function()
2286 'seeked to the seekable end'); 2453 'seeked to the seekable end');
2287 2454
2288 QUnit.equal(this.requests.length, 1, 'begins buffering'); 2455 QUnit.equal(this.requests.length, 1, 'begins buffering');
2456
2289 }); 2457 });
2290 2458
2291 QUnit.module('HLS - Encryption', { 2459 QUnit.module('HLS - Encryption', {
...@@ -2361,4 +2529,8 @@ QUnit.test('treats invalid keys as a key request failure and blacklists playlist ...@@ -2361,4 +2529,8 @@ QUnit.test('treats invalid keys as a key request failure and blacklists playlist
2361 QUnit.ok(hls.playlists.media().excludeUntil > 0, 2529 QUnit.ok(hls.playlists.media().excludeUntil > 0,
2362 'blacklisted playlist'); 2530 'blacklisted playlist');
2363 QUnit.equal(this.env.log.warn.calls, 1, 'logged warning for blacklist'); 2531 QUnit.equal(this.env.log.warn.calls, 1, 'logged warning for blacklist');
2532
2533 // verify stats
2534 QUnit.equal(hls.stats.mediaBytesTransferred, 16, '16 bytes');
2535 QUnit.equal(hls.stats.mediaRequests, 1, '1 request');
2364 }); 2536 });
......