598e5e89 by David LaPalomento

Merge pull request #389 from videojs/discontinuity

Discontinuity
2 parents 9e8618df 322b2318
...@@ -44,11 +44,11 @@ ...@@ -44,11 +44,11 @@
44 "karma-sauce-launcher": "~0.1.8", 44 "karma-sauce-launcher": "~0.1.8",
45 "qunitjs": "^1.18.0", 45 "qunitjs": "^1.18.0",
46 "sinon": "1.10.2", 46 "sinon": "1.10.2",
47 "video.js": "^5.0.0-rc.51" 47 "video.js": "^5.0.0-rc.96"
48 }, 48 },
49 "dependencies": { 49 "dependencies": {
50 "pkcs7": "^0.2.2", 50 "pkcs7": "^0.2.2",
51 "videojs-contrib-media-sources": "videojs/videojs-contrib-media-sources.git#mse-mp2t-polyfill", 51 "videojs-contrib-media-sources": "videojs/videojs-contrib-media-sources.git#mse-mp2t-polyfill",
52 "videojs-swf": "5.0.0-rc0" 52 "videojs-swf": "5.0.0-rc1"
53 } 53 }
54 } 54 }
......
...@@ -24,8 +24,8 @@ keyFailed = function(key) { ...@@ -24,8 +24,8 @@ keyFailed = function(key) {
24 return key.retries && key.retries >= 2; 24 return key.retries && key.retries >= 2;
25 }; 25 };
26 26
27 videojs.Hls = videojs.extends(Component, { 27 videojs.Hls = videojs.extend(Component, {
28 constructor: function(tech, source) { 28 constructor: function(tech, options) {
29 var self = this, _player; 29 var self = this, _player;
30 30
31 Component.call(this, tech); 31 Component.call(this, tech);
...@@ -44,7 +44,8 @@ videojs.Hls = videojs.extends(Component, { ...@@ -44,7 +44,8 @@ videojs.Hls = videojs.extends(Component, {
44 } 44 }
45 } 45 }
46 this.tech_ = tech; 46 this.tech_ = tech;
47 this.source_ = source; 47 this.source_ = options.source;
48 this.mode_ = options.mode;
48 this.bytesReceived = 0; 49 this.bytesReceived = 0;
49 50
50 // loadingState_ tracks how far along the buffering process we 51 // loadingState_ tracks how far along the buffering process we
...@@ -87,7 +88,8 @@ videojs.Hls.canPlaySource = function() { ...@@ -87,7 +88,8 @@ videojs.Hls.canPlaySource = function() {
87 * the browser it is running in. It is not necessary to use or modify 88 * the browser it is running in. It is not necessary to use or modify
88 * this object in normal usage. 89 * this object in normal usage.
89 */ 90 */
90 videojs.HlsSourceHandler = { 91 videojs.HlsSourceHandler = function(mode) {
92 return {
91 canHandleSource: function(srcObj) { 93 canHandleSource: function(srcObj) {
92 var mpegurlRE = /^application\/(?:x-|vnd\.apple\.)mpegurl/i; 94 var mpegurlRE = /^application\/(?:x-|vnd\.apple\.)mpegurl/i;
93 95
...@@ -98,17 +100,21 @@ videojs.HlsSourceHandler = { ...@@ -98,17 +100,21 @@ videojs.HlsSourceHandler = {
98 return mpegurlRE.test(srcObj.type); 100 return mpegurlRE.test(srcObj.type);
99 }, 101 },
100 handleSource: function(source, tech) { 102 handleSource: function(source, tech) {
101 tech.hls = new videojs.Hls(tech, source); 103 tech.hls = new videojs.Hls(tech, {
104 source: source,
105 mode: mode
106 });
102 tech.hls.src(source.src); 107 tech.hls.src(source.src);
103 return tech.hls; 108 return tech.hls;
104 } 109 }
110 };
105 }; 111 };
106 // register with the appropriate tech 112
113 // register source handlers with the appropriate techs
107 if (videojs.MediaSource.supportsNativeMediaSources()) { 114 if (videojs.MediaSource.supportsNativeMediaSources()) {
108 videojs.getComponent('Html5').registerSourceHandler(videojs.HlsSourceHandler); 115 videojs.getComponent('Html5').registerSourceHandler(videojs.HlsSourceHandler('html5'));
109 } else {
110 videojs.getComponent('Flash').registerSourceHandler(videojs.HlsSourceHandler);
111 } 116 }
117 videojs.getComponent('Flash').registerSourceHandler(videojs.HlsSourceHandler('flash'));
112 118
113 // the desired length of video to maintain in the buffer, in seconds 119 // the desired length of video to maintain in the buffer, in seconds
114 videojs.Hls.GOAL_BUFFER_LENGTH = 30; 120 videojs.Hls.GOAL_BUFFER_LENGTH = 30;
...@@ -121,7 +127,7 @@ videojs.Hls.prototype.src = function(src) { ...@@ -121,7 +127,7 @@ videojs.Hls.prototype.src = function(src) {
121 return; 127 return;
122 } 128 }
123 129
124 this.mediaSource = new videojs.MediaSource(); 130 this.mediaSource = new videojs.MediaSource({ mode: this.mode_ });
125 this.segmentBuffer_ = []; 131 this.segmentBuffer_ = [];
126 132
127 // if the stream contains ID3 metadata, expose that as a metadata 133 // if the stream contains ID3 metadata, expose that as a metadata
...@@ -739,6 +745,31 @@ videojs.Hls.prototype.stopCheckingBuffer_ = function() { ...@@ -739,6 +745,31 @@ videojs.Hls.prototype.stopCheckingBuffer_ = function() {
739 }; 745 };
740 746
741 /** 747 /**
748 * Attempts to find the buffered TimeRange where playback is currently
749 * happening. Returns a new TimeRange with one or zero ranges.
750 */
751 videojs.Hls.prototype.findCurrentBuffered_ = function() {
752 var
753 tech = this.tech_,
754 currentTime = tech.currentTime(),
755 buffered = this.tech_.buffered(),
756 i;
757
758 if (buffered && buffered.length) {
759 // Search for a range containing the play-head
760 for (i = 0;i < buffered.length; i++) {
761 if (buffered.start(i) <= currentTime &&
762 buffered.end(i) >= currentTime) {
763 return videojs.createTimeRange(buffered.start(i), buffered.end(i));
764 }
765 }
766 }
767
768 // Return an empty range if no ranges exist
769 return videojs.createTimeRange();
770 };
771
772 /**
742 * Determines whether there is enough video data currently in the buffer 773 * Determines whether there is enough video data currently in the buffer
743 * and downloads a new segment if the buffered time is less than the goal. 774 * and downloads a new segment if the buffered time is less than the goal.
744 * @param offset (optional) {number} the offset into the downloaded segment 775 * @param offset (optional) {number} the offset into the downloaded segment
...@@ -747,7 +778,8 @@ videojs.Hls.prototype.stopCheckingBuffer_ = function() { ...@@ -747,7 +778,8 @@ videojs.Hls.prototype.stopCheckingBuffer_ = function() {
747 videojs.Hls.prototype.fillBuffer = function(offset) { 778 videojs.Hls.prototype.fillBuffer = function(offset) {
748 var 779 var
749 tech = this.tech_, 780 tech = this.tech_,
750 buffered = this.tech_.buffered(), 781 currentTime = tech.currentTime(),
782 buffered = this.findCurrentBuffered_(),
751 bufferedTime = 0, 783 bufferedTime = 0,
752 segment, 784 segment,
753 segmentUri; 785 segmentUri;
...@@ -785,9 +817,10 @@ videojs.Hls.prototype.fillBuffer = function(offset) { ...@@ -785,9 +817,10 @@ videojs.Hls.prototype.fillBuffer = function(offset) {
785 return; 817 return;
786 } 818 }
787 819
820 // To determine how much is buffered, we need to find the buffered region we
821 // are currently playing in and measure it's length
788 if (buffered && buffered.length) { 822 if (buffered && buffered.length) {
789 // assuming a single, contiguous buffer region 823 bufferedTime = Math.max(0, buffered.end(0) - currentTime);
790 bufferedTime = tech.buffered().end(0) - tech.currentTime();
791 } 824 }
792 825
793 // if there is plenty of content in the buffer and we're not 826 // if there is plenty of content in the buffer and we're not
...@@ -844,12 +877,13 @@ videojs.Hls.prototype.loadSegment = function(segmentUri, offset) { ...@@ -844,12 +877,13 @@ videojs.Hls.prototype.loadSegment = function(segmentUri, offset) {
844 // the segment request is no longer outstanding 877 // the segment request is no longer outstanding
845 self.segmentXhr_ = null; 878 self.segmentXhr_ = null;
846 879
847 if (error) {
848 // if a segment request times out, we may have better luck with another playlist 880 // if a segment request times out, we may have better luck with another playlist
849 if (request.timedout) { 881 if (request.timedout) {
850 self.bandwidth = 1; 882 self.bandwidth = 1;
851 return self.playlists.media(self.selectPlaylist()); 883 return self.playlists.media(self.selectPlaylist());
852 } 884 }
885
886 if (!request.aborted && error) {
853 // otherwise, try jumping ahead to the next segment 887 // otherwise, try jumping ahead to the next segment
854 self.error = { 888 self.error = {
855 status: request.status, 889 status: request.status,
...@@ -914,7 +948,6 @@ videojs.Hls.prototype.drainBuffer = function(event) { ...@@ -914,7 +948,6 @@ videojs.Hls.prototype.drainBuffer = function(event) {
914 segment, 948 segment,
915 decrypter, 949 decrypter,
916 segIv, 950 segIv,
917 segmentOffset = 0,
918 // ptsTime, 951 // ptsTime,
919 segmentBuffer = this.segmentBuffer_; 952 segmentBuffer = this.segmentBuffer_;
920 953
...@@ -999,11 +1032,9 @@ videojs.Hls.prototype.drainBuffer = function(event) { ...@@ -999,11 +1032,9 @@ videojs.Hls.prototype.drainBuffer = function(event) {
999 // this.tech_.el().vjs_discontinuity(); 1032 // this.tech_.el().vjs_discontinuity();
1000 // } 1033 // }
1001 1034
1002 // determine the timestamp offset for the start of this segment 1035 if (segment.discontinuity) {
1003 segmentOffset = this.playlists.expiredPostDiscontinuity_ + this.playlists.expiredPreDiscontinuity_; 1036 this.sourceBuffer.timestampOffset = this.findCurrentBuffered_().end(0);
1004 segmentOffset += videojs.Hls.Playlist.duration(playlist, 1037 }
1005 playlist.mediaSequence,
1006 playlist.mediaSequence + mediaIndex);
1007 1038
1008 this.sourceBuffer.appendBuffer(bytes); 1039 this.sourceBuffer.appendBuffer(bytes);
1009 1040
......
...@@ -5,13 +5,35 @@ ...@@ -5,13 +5,35 @@
5 * A wrapper for videojs.xhr that tracks bandwidth. 5 * A wrapper for videojs.xhr that tracks bandwidth.
6 */ 6 */
7 videojs.Hls.xhr = function(options, callback) { 7 videojs.Hls.xhr = function(options, callback) {
8 var request = videojs.xhr(options, function(error, request) { 8 // Add a default timeout for all hls requests
9 if (request.response) { 9 options = videojs.mergeOptions({
10 timeout: 45e3
11 }, options);
12
13 var request = videojs.xhr(options, function(error, response) {
14 if (!error && request.response) {
10 request.responseTime = (new Date()).getTime(); 15 request.responseTime = (new Date()).getTime();
11 request.roundTripTime = request.responseTime - request.requestTime; 16 request.roundTripTime = request.responseTime - request.requestTime;
12 request.bytesReceived = request.response.byteLength || request.response.length; 17 request.bytesReceived = request.response.byteLength || request.response.length;
18 if (!request.bandwidth) {
13 request.bandwidth = Math.floor((request.bytesReceived / request.roundTripTime) * 8 * 1000); 19 request.bandwidth = Math.floor((request.bytesReceived / request.roundTripTime) * 8 * 1000);
14 } 20 }
21 }
22
23 // videojs.xhr now uses a specific code on the error object to signal that a request has
24 // timed out errors of setting a boolean on the request object
25 if (error || request.timedout) {
26 request.timedout = request.timedout || (error.code === 'ETIMEDOUT');
27 } else {
28 request.timedout = false;
29 }
30
31 // videojs.xhr no longer consider status codes outside of 200 and 0 (for file uris) to be
32 // errors but the old XHR did so emulate that behavior
33 if (!error && response.statusCode !== 200 && response.statusCode !== 0) {
34 error = new Error('XHR Failed with a response of: ' +
35 (request && (request.response || request.responseText)));
36 }
15 37
16 callback(error, request); 38 callback(error, request);
17 }); 39 });
......
...@@ -20,6 +20,8 @@ ...@@ -20,6 +20,8 @@
20 setup: function() { 20 setup: function() {
21 // fake XHRs 21 // fake XHRs
22 sinonXhr = sinon.useFakeXMLHttpRequest(); 22 sinonXhr = sinon.useFakeXMLHttpRequest();
23 videojs.xhr.XMLHttpRequest = sinonXhr;
24
23 requests = []; 25 requests = [];
24 sinonXhr.onCreate = function(xhr) { 26 sinonXhr.onCreate = function(xhr) {
25 // force the XHR2 timeout polyfill 27 // force the XHR2 timeout polyfill
...@@ -32,6 +34,7 @@ ...@@ -32,6 +34,7 @@
32 }, 34 },
33 teardown: function() { 35 teardown: function() {
34 sinonXhr.restore(); 36 sinonXhr.restore();
37 videojs.xhr.XMLHttpRequest = window.XMLHttpRequest;
35 clock.restore(); 38 clock.restore();
36 } 39 }
37 }); 40 });
......
...@@ -87,24 +87,24 @@ var ...@@ -87,24 +87,24 @@ var
87 player.buffered = function() { 87 player.buffered = function() {
88 return videojs.createTimeRange(0, 0); 88 return videojs.createTimeRange(0, 0);
89 }; 89 };
90 mockTech(player.tech); 90 mockTech(player.tech_);
91 91
92 return player; 92 return player;
93 }, 93 },
94 openMediaSource = function(player) { 94 openMediaSource = function(player) {
95 // ensure the Flash tech is ready 95 // ensure the Flash tech is ready
96 player.tech.triggerReady(); 96 player.tech_.triggerReady();
97 clock.tick(1); 97 clock.tick(1);
98 98
99 // simulate the sourceopen event 99 // simulate the sourceopen event
100 player.tech.hls.mediaSource.readyState = 'open'; 100 player.tech_.hls.mediaSource.readyState = 'open';
101 player.tech.hls.mediaSource.dispatchEvent({ 101 player.tech_.hls.mediaSource.dispatchEvent({
102 type: 'sourceopen', 102 type: 'sourceopen',
103 swfId: player.tech.el().id 103 swfId: player.tech_.el().id
104 }); 104 });
105 105
106 // endOfStream triggers an exception if flash isn't available 106 // endOfStream triggers an exception if flash isn't available
107 player.tech.hls.mediaSource.endOfStream = function(error) { 107 player.tech_.hls.mediaSource.endOfStream = function(error) {
108 this.error_ = error; 108 this.error_ = error;
109 }; 109 };
110 }, 110 },
...@@ -190,10 +190,10 @@ var ...@@ -190,10 +190,10 @@ var
190 }, 190 },
191 191
192 // a no-op MediaSource implementation to allow synchronous testing 192 // a no-op MediaSource implementation to allow synchronous testing
193 MockMediaSource = videojs.extends(videojs.EventTarget, { 193 MockMediaSource = videojs.extend(videojs.EventTarget, {
194 constructor: function() {}, 194 constructor: function() {},
195 addSourceBuffer: function() { 195 addSourceBuffer: function() {
196 return new (videojs.extends(videojs.EventTarget, { 196 return new (videojs.extend(videojs.EventTarget, {
197 constructor: function() {}, 197 constructor: function() {},
198 abort: function() {}, 198 abort: function() {},
199 buffered: videojs.createTimeRange(), 199 buffered: videojs.createTimeRange(),
...@@ -287,6 +287,7 @@ module('HLS', { ...@@ -287,6 +287,7 @@ module('HLS', {
287 287
288 // fake XHRs 288 // fake XHRs
289 xhr = sinon.useFakeXMLHttpRequest(); 289 xhr = sinon.useFakeXMLHttpRequest();
290 videojs.xhr.XMLHttpRequest = xhr;
290 requests = []; 291 requests = [];
291 xhr.onCreate = function(xhr) { 292 xhr.onCreate = function(xhr) {
292 requests.push(xhr); 293 requests.push(xhr);
...@@ -314,6 +315,7 @@ module('HLS', { ...@@ -314,6 +315,7 @@ module('HLS', {
314 315
315 player.dispose(); 316 player.dispose();
316 xhr.restore(); 317 xhr.restore();
318 videojs.xhr.XMLHttpRequest = window.XMLHttpRequest;
317 clock.restore(); 319 clock.restore();
318 } 320 }
319 }); 321 });
...@@ -326,10 +328,10 @@ test('starts playing if autoplay is specified', function() { ...@@ -326,10 +328,10 @@ test('starts playing if autoplay is specified', function() {
326 type: 'application/vnd.apple.mpegurl' 328 type: 'application/vnd.apple.mpegurl'
327 }); 329 });
328 // REMOVEME workaround https://github.com/videojs/video.js/issues/2326 330 // REMOVEME workaround https://github.com/videojs/video.js/issues/2326
329 player.tech.triggerReady(); 331 player.tech_.triggerReady();
330 clock.tick(1); 332 clock.tick(1);
331 // make sure play() is called *after* the media source opens 333 // make sure play() is called *after* the media source opens
332 player.tech.hls.play = function() { 334 player.tech_.hls.play = function() {
333 plays++; 335 plays++;
334 }; 336 };
335 openMediaSource(player); 337 openMediaSource(player);
...@@ -365,7 +367,7 @@ test('autoplay seeks to the live point after media source open', function() { ...@@ -365,7 +367,7 @@ test('autoplay seeks to the live point after media source open', function() {
365 src: 'liveStart30sBefore.m3u8', 367 src: 'liveStart30sBefore.m3u8',
366 type: 'application/vnd.apple.mpegurl' 368 type: 'application/vnd.apple.mpegurl'
367 }); 369 });
368 player.tech.triggerReady(); 370 player.tech_.triggerReady();
369 clock.tick(1); 371 clock.tick(1);
370 standardXHRResponse(requests.shift()); 372 standardXHRResponse(requests.shift());
371 openMediaSource(player); 373 openMediaSource(player);
...@@ -379,12 +381,12 @@ test('duration is set when the source opens after the playlist is loaded', funct ...@@ -379,12 +381,12 @@ test('duration is set when the source opens after the playlist is loaded', funct
379 src: 'media.m3u8', 381 src: 'media.m3u8',
380 type: 'application/vnd.apple.mpegurl' 382 type: 'application/vnd.apple.mpegurl'
381 }); 383 });
382 player.tech.triggerReady(); 384 player.tech_.triggerReady();
383 clock.tick(1); 385 clock.tick(1);
384 standardXHRResponse(requests.shift()); 386 standardXHRResponse(requests.shift());
385 openMediaSource(player); 387 openMediaSource(player);
386 388
387 equal(player.tech.hls.mediaSource.duration , 40, 'set the duration'); 389 equal(player.tech_.hls.mediaSource.duration , 40, 'set the duration');
388 }); 390 });
389 391
390 test('codecs are passed to the source buffer', function() { 392 test('codecs are passed to the source buffer', function() {
...@@ -394,7 +396,7 @@ test('codecs are passed to the source buffer', function() { ...@@ -394,7 +396,7 @@ test('codecs are passed to the source buffer', function() {
394 type: 'application/vnd.apple.mpegurl' 396 type: 'application/vnd.apple.mpegurl'
395 }); 397 });
396 openMediaSource(player); 398 openMediaSource(player);
397 player.tech.hls.mediaSource.addSourceBuffer = function(codec) { 399 player.tech_.hls.mediaSource.addSourceBuffer = function(codec) {
398 codecs.push(codec); 400 codecs.push(codec);
399 }; 401 };
400 402
...@@ -420,7 +422,7 @@ test('creates a PlaylistLoader on init', function() { ...@@ -420,7 +422,7 @@ test('creates a PlaylistLoader on init', function() {
420 src: 'manifest/playlist.m3u8', 422 src: 'manifest/playlist.m3u8',
421 type: 'application/vnd.apple.mpegurl' 423 type: 'application/vnd.apple.mpegurl'
422 }); 424 });
423 425 openMediaSource(player);
424 player.src({ 426 player.src({
425 src:'manifest/playlist.m3u8', 427 src:'manifest/playlist.m3u8',
426 type: 'application/vnd.apple.mpegurl' 428 type: 'application/vnd.apple.mpegurl'
...@@ -429,11 +431,11 @@ test('creates a PlaylistLoader on init', function() { ...@@ -429,11 +431,11 @@ test('creates a PlaylistLoader on init', function() {
429 431
430 equal(requests[0].aborted, true, 'aborted previous src'); 432 equal(requests[0].aborted, true, 'aborted previous src');
431 standardXHRResponse(requests[1]); 433 standardXHRResponse(requests[1]);
432 ok(player.tech.hls.playlists.master, 'set the master playlist'); 434 ok(player.tech_.hls.playlists.master, 'set the master playlist');
433 ok(player.tech.hls.playlists.media(), 'set the media playlist'); 435 ok(player.tech_.hls.playlists.media(), 'set the media playlist');
434 ok(player.tech.hls.playlists.media().segments, 'the segment entries are parsed'); 436 ok(player.tech_.hls.playlists.media().segments, 'the segment entries are parsed');
435 strictEqual(player.tech.hls.playlists.master.playlists[0], 437 strictEqual(player.tech_.hls.playlists.master.playlists[0],
436 player.tech.hls.playlists.media(), 438 player.tech_.hls.playlists.media(),
437 'the playlist is selected'); 439 'the playlist is selected');
438 }); 440 });
439 441
...@@ -455,14 +457,14 @@ test('re-initializes the playlist loader when switching sources', function() { ...@@ -455,14 +457,14 @@ test('re-initializes the playlist loader when switching sources', function() {
455 }); 457 });
456 // maybe not needed if https://github.com/videojs/video.js/issues/2326 gets fixed 458 // maybe not needed if https://github.com/videojs/video.js/issues/2326 gets fixed
457 clock.tick(1); 459 clock.tick(1);
458 ok(!player.tech.hls.playlists.media(), 'no media playlist'); 460 ok(!player.tech_.hls.playlists.media(), 'no media playlist');
459 equal(player.tech.hls.playlists.state, 461 equal(player.tech_.hls.playlists.state,
460 'HAVE_NOTHING', 462 'HAVE_NOTHING',
461 'reset the playlist loader state'); 463 'reset the playlist loader state');
462 equal(requests.length, 1, 'requested the new src'); 464 equal(requests.length, 1, 'requested the new src');
463 465
464 // buffer check 466 // buffer check
465 player.tech.hls.checkBuffer_(); 467 player.tech_.hls.checkBuffer_();
466 equal(requests.length, 1, 'did not request a stale segment'); 468 equal(requests.length, 1, 'did not request a stale segment');
467 469
468 // sourceopen 470 // sourceopen
...@@ -479,12 +481,12 @@ test('sets the duration if one is available on the playlist', function() { ...@@ -479,12 +481,12 @@ test('sets the duration if one is available on the playlist', function() {
479 type: 'application/vnd.apple.mpegurl' 481 type: 'application/vnd.apple.mpegurl'
480 }); 482 });
481 openMediaSource(player); 483 openMediaSource(player);
482 player.tech.on('durationchange', function() { 484 player.tech_.on('durationchange', function() {
483 events++; 485 events++;
484 }); 486 });
485 487
486 standardXHRResponse(requests[0]); 488 standardXHRResponse(requests[0]);
487 equal(player.tech.hls.mediaSource.duration, 40, 'set the duration'); 489 equal(player.tech_.hls.mediaSource.duration, 40, 'set the duration');
488 equal(events, 1, 'durationchange is fired'); 490 equal(events, 1, 'durationchange is fired');
489 }); 491 });
490 492
...@@ -495,14 +497,14 @@ QUnit.skip('calculates the duration if needed', function() { ...@@ -495,14 +497,14 @@ QUnit.skip('calculates the duration if needed', function() {
495 type: 'application/vnd.apple.mpegurl' 497 type: 'application/vnd.apple.mpegurl'
496 }); 498 });
497 openMediaSource(player); 499 openMediaSource(player);
498 player.tech.hls.mediaSource.duration = NaN; 500 player.tech_.hls.mediaSource.duration = NaN;
499 player.on('durationchange', function() { 501 player.on('durationchange', function() {
500 changes++; 502 changes++;
501 }); 503 });
502 504
503 standardXHRResponse(requests[0]); 505 standardXHRResponse(requests[0]);
504 strictEqual(player.tech.hls.mediaSource.duration, 506 strictEqual(player.tech_.hls.mediaSource.duration,
505 player.tech.hls.playlists.media().segments.length * 10, 507 player.tech_.hls.playlists.media().segments.length * 10,
506 'duration is updated'); 508 'duration is updated');
507 strictEqual(changes, 1, 'one durationchange fired'); 509 strictEqual(changes, 1, 'one durationchange fired');
508 }); 510 });
...@@ -549,6 +551,28 @@ test('starts downloading a segment on loadedmetadata', function() { ...@@ -549,6 +551,28 @@ test('starts downloading a segment on loadedmetadata', function() {
549 'the first segment is requested'); 551 'the first segment is requested');
550 }); 552 });
551 553
554 test('finds the correct buffered region based on currentTime', function() {
555 player.src({
556 src: 'manifest/media.m3u8',
557 type: 'application/vnd.apple.mpegurl'
558 });
559 player.tech_.buffered = function() {
560 return videojs.createTimeRanges([[0, 5], [6, 12]]);
561 };
562 openMediaSource(player);
563
564 standardXHRResponse(requests[0]);
565 standardXHRResponse(requests[1]);
566 player.currentTime(3);
567 clock.tick(1);
568 equal(player.tech_.hls.findCurrentBuffered_().end(0),
569 5, 'inside the first buffered region');
570 player.currentTime(6);
571 clock.tick(1);
572 equal(player.tech_.hls.findCurrentBuffered_().end(0),
573 12, 'inside the second buffered region');
574 });
575
552 test('recognizes absolute URIs and requests them unmodified', function() { 576 test('recognizes absolute URIs and requests them unmodified', function() {
553 player.src({ 577 player.src({
554 src: 'manifest/absoluteUris.m3u8', 578 src: 'manifest/absoluteUris.m3u8',
...@@ -588,11 +612,11 @@ test('re-initializes the handler for each source', function() { ...@@ -588,11 +612,11 @@ test('re-initializes the handler for each source', function() {
588 type: 'application/vnd.apple.mpegurl' 612 type: 'application/vnd.apple.mpegurl'
589 }); 613 });
590 openMediaSource(player); 614 openMediaSource(player);
591 firstPlaylists = player.tech.hls.playlists; 615 firstPlaylists = player.tech_.hls.playlists;
592 firstMSE = player.tech.hls.mediaSource; 616 firstMSE = player.tech_.hls.mediaSource;
593 standardXHRResponse(requests.shift()); 617 standardXHRResponse(requests.shift());
594 standardXHRResponse(requests.shift()); 618 standardXHRResponse(requests.shift());
595 player.tech.hls.sourceBuffer.abort = function() { 619 player.tech_.hls.sourceBuffer.abort = function() {
596 aborts++; 620 aborts++;
597 }; 621 };
598 622
...@@ -601,8 +625,8 @@ test('re-initializes the handler for each source', function() { ...@@ -601,8 +625,8 @@ test('re-initializes the handler for each source', function() {
601 type: 'application/vnd.apple.mpegurl' 625 type: 'application/vnd.apple.mpegurl'
602 }); 626 });
603 openMediaSource(player); 627 openMediaSource(player);
604 secondPlaylists = player.tech.hls.playlists; 628 secondPlaylists = player.tech_.hls.playlists;
605 secondMSE = player.tech.hls.mediaSource; 629 secondMSE = player.tech_.hls.mediaSource;
606 630
607 equal(1, aborts, 'aborted the old source buffer'); 631 equal(1, aborts, 'aborted the old source buffer');
608 ok(requests[0].aborted, 'aborted the old segment request'); 632 ok(requests[0].aborted, 'aborted the old segment request');
...@@ -618,7 +642,7 @@ test('triggers an error when a master playlist request errors', function() { ...@@ -618,7 +642,7 @@ test('triggers an error when a master playlist request errors', function() {
618 openMediaSource(player); 642 openMediaSource(player);
619 requests.pop().respond(500); 643 requests.pop().respond(500);
620 644
621 equal(player.tech.hls.mediaSource.error_, 'network', 'a network error is triggered'); 645 equal(player.tech_.hls.mediaSource.error_, 'network', 'a network error is triggered');
622 }); 646 });
623 647
624 test('downloads media playlists after loading the master', function() { 648 test('downloads media playlists after loading the master', function() {
...@@ -629,7 +653,7 @@ test('downloads media playlists after loading the master', function() { ...@@ -629,7 +653,7 @@ test('downloads media playlists after loading the master', function() {
629 openMediaSource(player); 653 openMediaSource(player);
630 654
631 // set bandwidth to an appropriate number so we don't switch 655 // set bandwidth to an appropriate number so we don't switch
632 player.tech.hls.bandwidth = 200000; 656 player.tech_.hls.bandwidth = 200000;
633 standardXHRResponse(requests[0]); 657 standardXHRResponse(requests[0]);
634 standardXHRResponse(requests[1]); 658 standardXHRResponse(requests[1]);
635 standardXHRResponse(requests[2]); 659 standardXHRResponse(requests[2]);
...@@ -652,8 +676,8 @@ test('upshift if initial bandwidth is high', function() { ...@@ -652,8 +676,8 @@ test('upshift if initial bandwidth is high', function() {
652 676
653 standardXHRResponse(requests[0]); 677 standardXHRResponse(requests[0]);
654 678
655 player.tech.hls.playlists.setBandwidth = function() { 679 player.tech_.hls.playlists.setBandwidth = function() {
656 player.tech.hls.playlists.bandwidth = 1000000000; 680 player.tech_.hls.playlists.bandwidth = 1000000000;
657 }; 681 };
658 682
659 standardXHRResponse(requests[1]); 683 standardXHRResponse(requests[1]);
...@@ -682,8 +706,8 @@ test('dont downshift if bandwidth is low', function() { ...@@ -682,8 +706,8 @@ test('dont downshift if bandwidth is low', function() {
682 706
683 standardXHRResponse(requests[0]); 707 standardXHRResponse(requests[0]);
684 708
685 player.tech.hls.playlists.setBandwidth = function() { 709 player.tech_.hls.playlists.setBandwidth = function() {
686 player.tech.hls.playlists.bandwidth = 100; 710 player.tech_.hls.playlists.bandwidth = 100;
687 }; 711 };
688 712
689 standardXHRResponse(requests[1]); 713 standardXHRResponse(requests[1]);
...@@ -710,10 +734,10 @@ test('starts checking the buffer on init', function() { ...@@ -710,10 +734,10 @@ test('starts checking the buffer on init', function() {
710 734
711 // wait long enough for the buffer check interval to expire and 735 // wait long enough for the buffer check interval to expire and
712 // trigger fill/drainBuffer 736 // trigger fill/drainBuffer
713 player.tech.hls.fillBuffer = function() { 737 player.tech_.hls.fillBuffer = function() {
714 fills++; 738 fills++;
715 }; 739 };
716 player.tech.hls.drainBuffer = function() { 740 player.tech_.hls.drainBuffer = function() {
717 drains++; 741 drains++;
718 }; 742 };
719 clock.tick(500); 743 clock.tick(500);
...@@ -732,7 +756,7 @@ test('buffer checks are noops until a media playlist is ready', function() { ...@@ -732,7 +756,7 @@ test('buffer checks are noops until a media playlist is ready', function() {
732 type: 'application/vnd.apple.mpegurl' 756 type: 'application/vnd.apple.mpegurl'
733 }); 757 });
734 openMediaSource(player); 758 openMediaSource(player);
735 player.tech.hls.checkBuffer_(); 759 player.tech_.hls.checkBuffer_();
736 760
737 strictEqual(1, requests.length, 'one request was made'); 761 strictEqual(1, requests.length, 'one request was made');
738 strictEqual(requests[0].url, 'manifest/media.m3u8', 'media playlist requested'); 762 strictEqual(requests[0].url, 'manifest/media.m3u8', 'media playlist requested');
...@@ -760,7 +784,7 @@ test('buffer checks are noops when only the master is ready', function() { ...@@ -760,7 +784,7 @@ test('buffer checks are noops when only the master is ready', function() {
760 // respond with the master playlist but don't send the media playlist yet 784 // respond with the master playlist but don't send the media playlist yet
761 standardXHRResponse(requests.shift()); 785 standardXHRResponse(requests.shift());
762 // trigger fillBuffer() 786 // trigger fillBuffer()
763 player.tech.hls.checkBuffer_(); 787 player.tech_.hls.checkBuffer_();
764 788
765 strictEqual(1, requests.length, 'one request was made'); 789 strictEqual(1, requests.length, 'one request was made');
766 strictEqual(requests[0].url, 790 strictEqual(requests[0].url,
...@@ -782,11 +806,11 @@ test('calculates the bandwidth after downloading a segment', function() { ...@@ -782,11 +806,11 @@ test('calculates the bandwidth after downloading a segment', function() {
782 806
783 standardXHRResponse(requests[1]); 807 standardXHRResponse(requests[1]);
784 808
785 ok(player.tech.hls.bandwidth, 'bandwidth is calculated'); 809 ok(player.tech_.hls.bandwidth, 'bandwidth is calculated');
786 ok(player.tech.hls.bandwidth > 0, 810 ok(player.tech_.hls.bandwidth > 0,
787 'bandwidth is positive: ' + player.tech.hls.bandwidth); 811 'bandwidth is positive: ' + player.tech_.hls.bandwidth);
788 ok(player.tech.hls.segmentXhrTime >= 0, 812 ok(player.tech_.hls.segmentXhrTime >= 0,
789 'saves segment request time: ' + player.tech.hls.segmentXhrTime + 's'); 813 'saves segment request time: ' + player.tech_.hls.segmentXhrTime + 's');
790 }); 814 });
791 815
792 test('fires a progress event after downloading a segment', function() { 816 test('fires a progress event after downloading a segment', function() {
...@@ -813,14 +837,14 @@ test('selects a playlist after segment downloads', function() { ...@@ -813,14 +837,14 @@ test('selects a playlist after segment downloads', function() {
813 type: 'application/vnd.apple.mpegurl' 837 type: 'application/vnd.apple.mpegurl'
814 }); 838 });
815 openMediaSource(player); 839 openMediaSource(player);
816 player.tech.hls.selectPlaylist = function() { 840 player.tech_.hls.selectPlaylist = function() {
817 calls++; 841 calls++;
818 return player.tech.hls.playlists.master.playlists[0]; 842 return player.tech_.hls.playlists.master.playlists[0];
819 }; 843 };
820 844
821 standardXHRResponse(requests[0]); 845 standardXHRResponse(requests[0]);
822 846
823 player.tech.hls.bandwidth = 3000000; 847 player.tech_.hls.bandwidth = 3000000;
824 standardXHRResponse(requests[1]); 848 standardXHRResponse(requests[1]);
825 standardXHRResponse(requests[2]); 849 standardXHRResponse(requests[2]);
826 850
...@@ -831,7 +855,7 @@ test('selects a playlist after segment downloads', function() { ...@@ -831,7 +855,7 @@ test('selects a playlist after segment downloads', function() {
831 player.buffered = function() { 855 player.buffered = function() {
832 return videojs.createTimeRange(0, 2); 856 return videojs.createTimeRange(0, 2);
833 }; 857 };
834 player.tech.hls.checkBuffer_(); 858 player.tech_.hls.checkBuffer_();
835 859
836 standardXHRResponse(requests[3]); 860 standardXHRResponse(requests[3]);
837 861
...@@ -847,15 +871,15 @@ test('moves to the next segment if there is a network error', function() { ...@@ -847,15 +871,15 @@ test('moves to the next segment if there is a network error', function() {
847 }); 871 });
848 openMediaSource(player); 872 openMediaSource(player);
849 873
850 player.tech.hls.bandwidth = 20000; 874 player.tech_.hls.bandwidth = 20000;
851 standardXHRResponse(requests[0]); 875 standardXHRResponse(requests[0]);
852 standardXHRResponse(requests[1]); 876 standardXHRResponse(requests[1]);
853 877
854 mediaIndex = player.tech.hls.mediaIndex; 878 mediaIndex = player.tech_.hls.mediaIndex;
855 player.trigger('timeupdate'); 879 player.trigger('timeupdate');
856 880
857 requests[2].respond(400); 881 requests[2].respond(400);
858 strictEqual(mediaIndex + 1, player.tech.hls.mediaIndex, 'media index is incremented'); 882 strictEqual(mediaIndex + 1, player.tech_.hls.mediaIndex, 'media index is incremented');
859 }); 883 });
860 884
861 test('updates the duration after switching playlists', function() { 885 test('updates the duration after switching playlists', function() {
...@@ -865,13 +889,13 @@ test('updates the duration after switching playlists', function() { ...@@ -865,13 +889,13 @@ test('updates the duration after switching playlists', function() {
865 type: 'application/vnd.apple.mpegurl' 889 type: 'application/vnd.apple.mpegurl'
866 }); 890 });
867 openMediaSource(player); 891 openMediaSource(player);
868 player.tech.hls.selectPlaylist = function() { 892 player.tech_.hls.selectPlaylist = function() {
869 selectedPlaylist = true; 893 selectedPlaylist = true;
870 894
871 // this duraiton should be overwritten by the playlist change 895 // this duraiton should be overwritten by the playlist change
872 player.tech.hls.mediaSource.duration = -Infinity; 896 player.tech_.hls.mediaSource.duration = -Infinity;
873 897
874 return player.tech.hls.playlists.master.playlists[1]; 898 return player.tech_.hls.playlists.master.playlists[1];
875 }; 899 };
876 900
877 standardXHRResponse(requests[0]); 901 standardXHRResponse(requests[0]);
...@@ -879,7 +903,7 @@ test('updates the duration after switching playlists', function() { ...@@ -879,7 +903,7 @@ test('updates the duration after switching playlists', function() {
879 standardXHRResponse(requests[2]); 903 standardXHRResponse(requests[2]);
880 standardXHRResponse(requests[3]); 904 standardXHRResponse(requests[3]);
881 ok(selectedPlaylist, 'selected playlist'); 905 ok(selectedPlaylist, 'selected playlist');
882 ok(player.tech.hls.mediaSource.duration !== -Infinity, 'updates the duration'); 906 ok(player.tech_.hls.mediaSource.duration !== -Infinity, 'updates the duration');
883 }); 907 });
884 908
885 test('downloads additional playlists if required', function() { 909 test('downloads additional playlists if required', function() {
...@@ -894,12 +918,12 @@ test('downloads additional playlists if required', function() { ...@@ -894,12 +918,12 @@ test('downloads additional playlists if required', function() {
894 }); 918 });
895 openMediaSource(player); 919 openMediaSource(player);
896 920
897 player.tech.hls.bandwidth = 20000; 921 player.tech_.hls.bandwidth = 20000;
898 standardXHRResponse(requests[0]); 922 standardXHRResponse(requests[0]);
899 923
900 standardXHRResponse(requests[1]); 924 standardXHRResponse(requests[1]);
901 // before an m3u8 is downloaded, no segments are available 925 // before an m3u8 is downloaded, no segments are available
902 player.tech.hls.selectPlaylist = function() { 926 player.tech_.hls.selectPlaylist = function() {
903 if (!called) { 927 if (!called) {
904 called = true; 928 called = true;
905 return playlist; 929 return playlist;
...@@ -921,9 +945,9 @@ test('downloads additional playlists if required', function() { ...@@ -921,9 +945,9 @@ test('downloads additional playlists if required', function() {
921 absoluteUrl('manifest/' + playlist.uri), 945 absoluteUrl('manifest/' + playlist.uri),
922 'made playlist request'); 946 'made playlist request');
923 strictEqual(playlist.uri, 947 strictEqual(playlist.uri,
924 player.tech.hls.playlists.media().uri, 948 player.tech_.hls.playlists.media().uri,
925 'a new playlists was selected'); 949 'a new playlists was selected');
926 ok(player.tech.hls.playlists.media().segments, 'segments are now available'); 950 ok(player.tech_.hls.playlists.media().segments, 'segments are now available');
927 }); 951 });
928 952
929 test('selects a playlist below the current bandwidth', function() { 953 test('selects a playlist below the current bandwidth', function() {
...@@ -937,15 +961,15 @@ test('selects a playlist below the current bandwidth', function() { ...@@ -937,15 +961,15 @@ test('selects a playlist below the current bandwidth', function() {
937 standardXHRResponse(requests[0]); 961 standardXHRResponse(requests[0]);
938 962
939 // the default playlist has a really high bitrate 963 // the default playlist has a really high bitrate
940 player.tech.hls.playlists.master.playlists[0].attributes.BANDWIDTH = 9e10; 964 player.tech_.hls.playlists.master.playlists[0].attributes.BANDWIDTH = 9e10;
941 // playlist 1 has a very low bitrate 965 // playlist 1 has a very low bitrate
942 player.tech.hls.playlists.master.playlists[1].attributes.BANDWIDTH = 1; 966 player.tech_.hls.playlists.master.playlists[1].attributes.BANDWIDTH = 1;
943 // but the detected client bandwidth is really low 967 // but the detected client bandwidth is really low
944 player.tech.hls.bandwidth = 10; 968 player.tech_.hls.bandwidth = 10;
945 969
946 playlist = player.tech.hls.selectPlaylist(); 970 playlist = player.tech_.hls.selectPlaylist();
947 strictEqual(playlist, 971 strictEqual(playlist,
948 player.tech.hls.playlists.master.playlists[1], 972 player.tech_.hls.playlists.master.playlists[1],
949 'the low bitrate stream is selected'); 973 'the low bitrate stream is selected');
950 }); 974 });
951 975
...@@ -961,7 +985,7 @@ test('scales the bandwidth estimate for the first segment', function() { ...@@ -961,7 +985,7 @@ test('scales the bandwidth estimate for the first segment', function() {
961 '#EXTM3U\n' + 985 '#EXTM3U\n' +
962 '#EXT-X-PLAYLIST-TYPE:VOD\n' + 986 '#EXT-X-PLAYLIST-TYPE:VOD\n' +
963 '#EXT-X-TARGETDURATION:10\n'); 987 '#EXT-X-TARGETDURATION:10\n');
964 equal(player.tech.hls.bandwidth, 500 * 5, 'scaled the bandwidth estimate by 5'); 988 equal(player.tech_.hls.bandwidth, 500 * 5, 'scaled the bandwidth estimate by 5');
965 }); 989 });
966 990
967 test('allows initial bandwidth to be provided', function() { 991 test('allows initial bandwidth to be provided', function() {
...@@ -970,14 +994,14 @@ test('allows initial bandwidth to be provided', function() { ...@@ -970,14 +994,14 @@ test('allows initial bandwidth to be provided', function() {
970 type: 'application/vnd.apple.mpegurl' 994 type: 'application/vnd.apple.mpegurl'
971 }); 995 });
972 openMediaSource(player); 996 openMediaSource(player);
973 player.tech.hls.bandwidth = 500; 997 player.tech_.hls.bandwidth = 500;
974 998
975 requests[0].bandwidth = 1; 999 requests[0].bandwidth = 1;
976 requests.shift().respond(200, null, 1000 requests.shift().respond(200, null,
977 '#EXTM3U\n' + 1001 '#EXTM3U\n' +
978 '#EXT-X-PLAYLIST-TYPE:VOD\n' + 1002 '#EXT-X-PLAYLIST-TYPE:VOD\n' +
979 '#EXT-X-TARGETDURATION:10\n'); 1003 '#EXT-X-TARGETDURATION:10\n');
980 equal(player.tech.hls.bandwidth, 500, 'prefers user-specified intial bandwidth'); 1004 equal(player.tech_.hls.bandwidth, 500, 'prefers user-specified intial bandwidth');
981 }); 1005 });
982 1006
983 test('raises the minimum bitrate for a stream proportionially', function() { 1007 test('raises the minimum bitrate for a stream proportionially', function() {
...@@ -991,15 +1015,15 @@ test('raises the minimum bitrate for a stream proportionially', function() { ...@@ -991,15 +1015,15 @@ test('raises the minimum bitrate for a stream proportionially', function() {
991 standardXHRResponse(requests[0]); 1015 standardXHRResponse(requests[0]);
992 1016
993 // the default playlist's bandwidth + 10% is equal to the current bandwidth 1017 // the default playlist's bandwidth + 10% is equal to the current bandwidth
994 player.tech.hls.playlists.master.playlists[0].attributes.BANDWIDTH = 10; 1018 player.tech_.hls.playlists.master.playlists[0].attributes.BANDWIDTH = 10;
995 player.tech.hls.bandwidth = 11; 1019 player.tech_.hls.bandwidth = 11;
996 1020
997 // 9.9 * 1.1 < 11 1021 // 9.9 * 1.1 < 11
998 player.tech.hls.playlists.master.playlists[1].attributes.BANDWIDTH = 9.9; 1022 player.tech_.hls.playlists.master.playlists[1].attributes.BANDWIDTH = 9.9;
999 playlist = player.tech.hls.selectPlaylist(); 1023 playlist = player.tech_.hls.selectPlaylist();
1000 1024
1001 strictEqual(playlist, 1025 strictEqual(playlist,
1002 player.tech.hls.playlists.master.playlists[1], 1026 player.tech_.hls.playlists.master.playlists[1],
1003 'a lower bitrate stream is selected'); 1027 'a lower bitrate stream is selected');
1004 }); 1028 });
1005 1029
...@@ -1014,12 +1038,12 @@ test('uses the lowest bitrate if no other is suitable', function() { ...@@ -1014,12 +1038,12 @@ test('uses the lowest bitrate if no other is suitable', function() {
1014 standardXHRResponse(requests[0]); 1038 standardXHRResponse(requests[0]);
1015 1039
1016 // the lowest bitrate playlist is much greater than 1b/s 1040 // the lowest bitrate playlist is much greater than 1b/s
1017 player.tech.hls.bandwidth = 1; 1041 player.tech_.hls.bandwidth = 1;
1018 playlist = player.tech.hls.selectPlaylist(); 1042 playlist = player.tech_.hls.selectPlaylist();
1019 1043
1020 // playlist 1 has the lowest advertised bitrate 1044 // playlist 1 has the lowest advertised bitrate
1021 strictEqual(playlist, 1045 strictEqual(playlist,
1022 player.tech.hls.playlists.master.playlists[1], 1046 player.tech_.hls.playlists.master.playlists[1],
1023 'the lowest bitrate stream is selected'); 1047 'the lowest bitrate stream is selected');
1024 }); 1048 });
1025 1049
...@@ -1037,18 +1061,18 @@ test('uses the lowest bitrate if no other is suitable', function() { ...@@ -1037,18 +1061,18 @@ test('uses the lowest bitrate if no other is suitable', function() {
1037 1061
1038 player.width(640); 1062 player.width(640);
1039 player.height(360); 1063 player.height(360);
1040 player.tech.hls.bandwidth = 3000000; 1064 player.tech_.hls.bandwidth = 3000000;
1041 1065
1042 playlist = player.tech.hls.selectPlaylist(); 1066 playlist = player.tech_.hls.selectPlaylist();
1043 1067
1044 deepEqual(playlist.attributes.RESOLUTION, {width:960,height:540},'should return the correct resolution by player dimensions'); 1068 deepEqual(playlist.attributes.RESOLUTION, {width:960,height:540},'should return the correct resolution by player dimensions');
1045 equal(playlist.attributes.BANDWIDTH, 1928000, 'should have the expected bandwidth in case of multiple'); 1069 equal(playlist.attributes.BANDWIDTH, 1928000, 'should have the expected bandwidth in case of multiple');
1046 1070
1047 player.width(1920); 1071 player.width(1920);
1048 player.height(1080); 1072 player.height(1080);
1049 player.tech.hls.bandwidth = 3000000; 1073 player.tech_.hls.bandwidth = 3000000;
1050 1074
1051 playlist = player.tech.hls.selectPlaylist(); 1075 playlist = player.tech_.hls.selectPlaylist();
1052 1076
1053 deepEqual(playlist.attributes.RESOLUTION, { 1077 deepEqual(playlist.attributes.RESOLUTION, {
1054 width:960, 1078 width:960,
...@@ -1058,7 +1082,7 @@ test('uses the lowest bitrate if no other is suitable', function() { ...@@ -1058,7 +1082,7 @@ test('uses the lowest bitrate if no other is suitable', function() {
1058 1082
1059 player.width(396); 1083 player.width(396);
1060 player.height(224); 1084 player.height(224);
1061 playlist = player.tech.hls.selectPlaylist(); 1085 playlist = player.tech_.hls.selectPlaylist();
1062 1086
1063 deepEqual(playlist.attributes.RESOLUTION, { 1087 deepEqual(playlist.attributes.RESOLUTION, {
1064 width:396, 1088 width:396,
...@@ -1083,12 +1107,12 @@ test('selects the highest bitrate playlist when the player dimensions are ' + ...@@ -1083,12 +1107,12 @@ test('selects the highest bitrate playlist when the player dimensions are ' +
1083 '#EXT-X-STREAM-INF:BANDWIDTH=1,RESOLUTION=1x1\n' + 1107 '#EXT-X-STREAM-INF:BANDWIDTH=1,RESOLUTION=1x1\n' +
1084 'media1.m3u8\n'); // master 1108 'media1.m3u8\n'); // master
1085 standardXHRResponse(requests.shift()); // media 1109 standardXHRResponse(requests.shift()); // media
1086 player.tech.hls.bandwidth = 1e10; 1110 player.tech_.hls.bandwidth = 1e10;
1087 1111
1088 player.width(1024); 1112 player.width(1024);
1089 player.height(768); 1113 player.height(768);
1090 1114
1091 playlist = player.tech.hls.selectPlaylist(); 1115 playlist = player.tech_.hls.selectPlaylist();
1092 1116
1093 equal(playlist.attributes.BANDWIDTH, 1117 equal(playlist.attributes.BANDWIDTH,
1094 1000, 1118 1000,
...@@ -1101,10 +1125,10 @@ test('does not download the next segment if the buffer is full', function() { ...@@ -1101,10 +1125,10 @@ test('does not download the next segment if the buffer is full', function() {
1101 src: 'manifest/media.m3u8', 1125 src: 'manifest/media.m3u8',
1102 type: 'application/vnd.apple.mpegurl' 1126 type: 'application/vnd.apple.mpegurl'
1103 }); 1127 });
1104 player.tech.currentTime = function() { 1128 player.tech_.currentTime = function() {
1105 return currentTime; 1129 return currentTime;
1106 }; 1130 };
1107 player.tech.buffered = function() { 1131 player.tech_.buffered = function() {
1108 return videojs.createTimeRange(0, currentTime + videojs.Hls.GOAL_BUFFER_LENGTH); 1132 return videojs.createTimeRange(0, currentTime + videojs.Hls.GOAL_BUFFER_LENGTH);
1109 }; 1133 };
1110 openMediaSource(player); 1134 openMediaSource(player);
...@@ -1126,14 +1150,14 @@ test('downloads the next segment if the buffer is getting low', function() { ...@@ -1126,14 +1150,14 @@ test('downloads the next segment if the buffer is getting low', function() {
1126 standardXHRResponse(requests[0]); 1150 standardXHRResponse(requests[0]);
1127 standardXHRResponse(requests[1]); 1151 standardXHRResponse(requests[1]);
1128 1152
1129 strictEqual(requests.length, 2, 'did not make a request'); 1153 strictEqual(requests.length, 2, 'made two requests');
1130 player.currentTime = function() { 1154 player.tech_.currentTime = function() {
1131 return 15; 1155 return 15;
1132 }; 1156 };
1133 player.buffered = function() { 1157 player.tech_.buffered = function() {
1134 return videojs.createTimeRange(0, 19.999); 1158 return videojs.createTimeRange(0, 19.999);
1135 }; 1159 };
1136 player.tech.hls.checkBuffer_(); 1160 player.tech_.hls.checkBuffer_();
1137 1161
1138 standardXHRResponse(requests[2]); 1162 standardXHRResponse(requests[2]);
1139 1163
...@@ -1143,6 +1167,69 @@ test('downloads the next segment if the buffer is getting low', function() { ...@@ -1143,6 +1167,69 @@ test('downloads the next segment if the buffer is getting low', function() {
1143 'made segment request'); 1167 'made segment request');
1144 }); 1168 });
1145 1169
1170 test('buffers based on the correct TimeRange if multiple ranges exist', function() {
1171 player.tech_.currentTime = function() {
1172 return 8;
1173 };
1174
1175 player.tech_.buffered = function() {
1176 return {
1177 start: function(num) {
1178 switch (num) {
1179 case 0:
1180 return 0;
1181 case 1:
1182 return 50;
1183 }
1184 },
1185 end: function(num) {
1186 switch (num) {
1187 case 0:
1188 return 10;
1189 case 1:
1190 return 160;
1191 }
1192 },
1193 length: 2
1194 };
1195 };
1196
1197 player.src({
1198 src: 'manifest/media.m3u8',
1199 type: 'application/vnd.apple.mpegurl'
1200 });
1201 openMediaSource(player);
1202
1203 standardXHRResponse(requests[0]);
1204 standardXHRResponse(requests[1]);
1205
1206 strictEqual(requests.length, 2, 'made two requests');
1207 strictEqual(requests[1].url,
1208 absoluteUrl('manifest/media-00001.ts'),
1209 'made segment request');
1210
1211 player.tech_.currentTime = function() {
1212 return 55;
1213 };
1214
1215 player.tech_.hls.checkBuffer_();
1216
1217 strictEqual(requests.length, 2, 'made no additional requests');
1218
1219 player.tech_.currentTime = function() {
1220 return 134;
1221 };
1222
1223 player.tech_.hls.checkBuffer_();
1224 standardXHRResponse(requests[2]);
1225
1226 strictEqual(requests.length, 3, 'made three requests');
1227
1228 strictEqual(requests[2].url,
1229 absoluteUrl('manifest/media-00002.ts'),
1230 'made segment request');
1231 });
1232
1146 test('stops downloading segments at the end of the playlist', function() { 1233 test('stops downloading segments at the end of the playlist', function() {
1147 player.src({ 1234 player.src({
1148 src: 'manifest/media.m3u8', 1235 src: 'manifest/media.m3u8',
...@@ -1151,7 +1238,7 @@ test('stops downloading segments at the end of the playlist', function() { ...@@ -1151,7 +1238,7 @@ test('stops downloading segments at the end of the playlist', function() {
1151 openMediaSource(player); 1238 openMediaSource(player);
1152 standardXHRResponse(requests[0]); 1239 standardXHRResponse(requests[0]);
1153 requests = []; 1240 requests = [];
1154 player.tech.hls.mediaIndex = 4; 1241 player.tech_.hls.mediaIndex = 4;
1155 player.trigger('timeupdate'); 1242 player.trigger('timeupdate');
1156 1243
1157 strictEqual(requests.length, 0, 'no request is made'); 1244 strictEqual(requests.length, 0, 'no request is made');
...@@ -1181,14 +1268,14 @@ test('only appends one segment at a time', function() { ...@@ -1181,14 +1268,14 @@ test('only appends one segment at a time', function() {
1181 standardXHRResponse(requests.pop()); // media.m3u8 1268 standardXHRResponse(requests.pop()); // media.m3u8
1182 standardXHRResponse(requests.pop()); // segment 0 1269 standardXHRResponse(requests.pop()); // segment 0
1183 1270
1184 player.tech.hls.sourceBuffer.updating = true; 1271 player.tech_.hls.sourceBuffer.updating = true;
1185 player.tech.hls.sourceBuffer.appendBuffer = function() { 1272 player.tech_.hls.sourceBuffer.appendBuffer = function() {
1186 appends++; 1273 appends++;
1187 }; 1274 };
1188 1275
1189 player.tech.hls.checkBuffer_(); 1276 player.tech_.hls.checkBuffer_();
1190 standardXHRResponse(requests.pop()); // segment 1 1277 standardXHRResponse(requests.pop()); // segment 1
1191 player.tech.hls.checkBuffer_(); // should be a no-op 1278 player.tech_.hls.checkBuffer_(); // should be a no-op
1192 equal(appends, 0, 'did not append while updating'); 1279 equal(appends, 0, 'did not append while updating');
1193 }); 1280 });
1194 1281
...@@ -1206,10 +1293,10 @@ QUnit.skip('records the min and max PTS values for a segment', function() { ...@@ -1206,10 +1293,10 @@ QUnit.skip('records the min and max PTS values for a segment', function() {
1206 tags.push({ pts: 10, bytes: new Uint8Array(1) }); 1293 tags.push({ pts: 10, bytes: new Uint8Array(1) });
1207 standardXHRResponse(requests.pop()); // segment 0 1294 standardXHRResponse(requests.pop()); // segment 0
1208 1295
1209 equal(player.tech.hls.playlists.media().segments[0].minVideoPts, 0, 'recorded min video pts'); 1296 equal(player.tech_.hls.playlists.media().segments[0].minVideoPts, 0, 'recorded min video pts');
1210 equal(player.tech.hls.playlists.media().segments[0].maxVideoPts, 10, 'recorded max video pts'); 1297 equal(player.tech_.hls.playlists.media().segments[0].maxVideoPts, 10, 'recorded max video pts');
1211 equal(player.tech.hls.playlists.media().segments[0].minAudioPts, 0, 'recorded min audio pts'); 1298 equal(player.tech_.hls.playlists.media().segments[0].minAudioPts, 0, 'recorded min audio pts');
1212 equal(player.tech.hls.playlists.media().segments[0].maxAudioPts, 10, 'recorded max audio pts'); 1299 equal(player.tech_.hls.playlists.media().segments[0].maxAudioPts, 10, 'recorded max audio pts');
1213 }); 1300 });
1214 1301
1215 QUnit.skip('records PTS values for video-only segments', function() { 1302 QUnit.skip('records PTS values for video-only segments', function() {
...@@ -1222,23 +1309,23 @@ QUnit.skip('records PTS values for video-only segments', function() { ...@@ -1222,23 +1309,23 @@ QUnit.skip('records PTS values for video-only segments', function() {
1222 openMediaSource(player); 1309 openMediaSource(player);
1223 standardXHRResponse(requests.pop()); // media.m3u8 1310 standardXHRResponse(requests.pop()); // media.m3u8
1224 1311
1225 player.tech.hls.segmentParser_.stats.aacTags = function() { 1312 player.tech_.hls.segmentParser_.stats.aacTags = function() {
1226 return 0; 1313 return 0;
1227 }; 1314 };
1228 player.tech.hls.segmentParser_.stats.minAudioPts = function() { 1315 player.tech_.hls.segmentParser_.stats.minAudioPts = function() {
1229 throw new Error('No audio tags'); 1316 throw new Error('No audio tags');
1230 }; 1317 };
1231 player.tech.hls.segmentParser_.stats.maxAudioPts = function() { 1318 player.tech_.hls.segmentParser_.stats.maxAudioPts = function() {
1232 throw new Error('No audio tags'); 1319 throw new Error('No audio tags');
1233 }; 1320 };
1234 tags.push({ pts: 0, bytes: new Uint8Array(1) }); 1321 tags.push({ pts: 0, bytes: new Uint8Array(1) });
1235 tags.push({ pts: 10, bytes: new Uint8Array(1) }); 1322 tags.push({ pts: 10, bytes: new Uint8Array(1) });
1236 standardXHRResponse(requests.pop()); // segment 0 1323 standardXHRResponse(requests.pop()); // segment 0
1237 1324
1238 equal(player.tech.hls.playlists.media().segments[0].minVideoPts, 0, 'recorded min video pts'); 1325 equal(player.tech_.hls.playlists.media().segments[0].minVideoPts, 0, 'recorded min video pts');
1239 equal(player.tech.hls.playlists.media().segments[0].maxVideoPts, 10, 'recorded max video pts'); 1326 equal(player.tech_.hls.playlists.media().segments[0].maxVideoPts, 10, 'recorded max video pts');
1240 strictEqual(player.tech.hls.playlists.media().segments[0].minAudioPts, undefined, 'min audio pts is undefined'); 1327 strictEqual(player.tech_.hls.playlists.media().segments[0].minAudioPts, undefined, 'min audio pts is undefined');
1241 strictEqual(player.tech.hls.playlists.media().segments[0].maxAudioPts, undefined, 'max audio pts is undefined'); 1328 strictEqual(player.tech_.hls.playlists.media().segments[0].maxAudioPts, undefined, 'max audio pts is undefined');
1242 }); 1329 });
1243 1330
1244 QUnit.skip('records PTS values for audio-only segments', function() { 1331 QUnit.skip('records PTS values for audio-only segments', function() {
...@@ -1251,23 +1338,23 @@ QUnit.skip('records PTS values for audio-only segments', function() { ...@@ -1251,23 +1338,23 @@ QUnit.skip('records PTS values for audio-only segments', function() {
1251 openMediaSource(player); 1338 openMediaSource(player);
1252 standardXHRResponse(requests.pop()); // media.m3u8 1339 standardXHRResponse(requests.pop()); // media.m3u8
1253 1340
1254 player.tech.hls.segmentParser_.stats.h264Tags = function() { 1341 player.tech_.hls.segmentParser_.stats.h264Tags = function() {
1255 return 0; 1342 return 0;
1256 }; 1343 };
1257 player.tech.hls.segmentParser_.stats.minVideoPts = function() { 1344 player.tech_.hls.segmentParser_.stats.minVideoPts = function() {
1258 throw new Error('No video tags'); 1345 throw new Error('No video tags');
1259 }; 1346 };
1260 player.tech.hls.segmentParser_.stats.maxVideoPts = function() { 1347 player.tech_.hls.segmentParser_.stats.maxVideoPts = function() {
1261 throw new Error('No video tags'); 1348 throw new Error('No video tags');
1262 }; 1349 };
1263 tags.push({ pts: 0, bytes: new Uint8Array(1) }); 1350 tags.push({ pts: 0, bytes: new Uint8Array(1) });
1264 tags.push({ pts: 10, bytes: new Uint8Array(1) }); 1351 tags.push({ pts: 10, bytes: new Uint8Array(1) });
1265 standardXHRResponse(requests.pop()); // segment 0 1352 standardXHRResponse(requests.pop()); // segment 0
1266 1353
1267 equal(player.tech.hls.playlists.media().segments[0].minAudioPts, 0, 'recorded min audio pts'); 1354 equal(player.tech_.hls.playlists.media().segments[0].minAudioPts, 0, 'recorded min audio pts');
1268 equal(player.tech.hls.playlists.media().segments[0].maxAudioPts, 10, 'recorded max audio pts'); 1355 equal(player.tech_.hls.playlists.media().segments[0].maxAudioPts, 10, 'recorded max audio pts');
1269 strictEqual(player.tech.hls.playlists.media().segments[0].minVideoPts, undefined, 'min video pts is undefined'); 1356 strictEqual(player.tech_.hls.playlists.media().segments[0].minVideoPts, undefined, 'min video pts is undefined');
1270 strictEqual(player.tech.hls.playlists.media().segments[0].maxVideoPts, undefined, 'max video pts is undefined'); 1357 strictEqual(player.tech_.hls.playlists.media().segments[0].maxVideoPts, undefined, 'max video pts is undefined');
1271 }); 1358 });
1272 1359
1273 test('waits to download new segments until the media playlist is stable', function() { 1360 test('waits to download new segments until the media playlist is stable', function() {
...@@ -1278,24 +1365,24 @@ test('waits to download new segments until the media playlist is stable', functi ...@@ -1278,24 +1365,24 @@ test('waits to download new segments until the media playlist is stable', functi
1278 }); 1365 });
1279 openMediaSource(player); 1366 openMediaSource(player);
1280 standardXHRResponse(requests.shift()); // master 1367 standardXHRResponse(requests.shift()); // master
1281 player.tech.hls.bandwidth = 1; // make sure we stay on the lowest variant 1368 player.tech_.hls.bandwidth = 1; // make sure we stay on the lowest variant
1282 standardXHRResponse(requests.shift()); // media 1369 standardXHRResponse(requests.shift()); // media
1283 1370
1284 // mock a playlist switch 1371 // mock a playlist switch
1285 media = player.tech.hls.playlists.media(); 1372 media = player.tech_.hls.playlists.media();
1286 player.tech.hls.playlists.media = function() { 1373 player.tech_.hls.playlists.media = function() {
1287 return media; 1374 return media;
1288 }; 1375 };
1289 player.tech.hls.playlists.state = 'SWITCHING_MEDIA'; 1376 player.tech_.hls.playlists.state = 'SWITCHING_MEDIA';
1290 1377
1291 standardXHRResponse(requests.shift()); // segment 0 1378 standardXHRResponse(requests.shift()); // segment 0
1292 1379
1293 equal(requests.length, 0, 'no requests outstanding'); 1380 equal(requests.length, 0, 'no requests outstanding');
1294 player.tech.hls.checkBuffer_(); 1381 player.tech_.hls.checkBuffer_();
1295 equal(requests.length, 0, 'delays segment fetching'); 1382 equal(requests.length, 0, 'delays segment fetching');
1296 1383
1297 player.tech.hls.playlists.state = 'LOADED_METADATA'; 1384 player.tech_.hls.playlists.state = 'LOADED_METADATA';
1298 player.tech.hls.checkBuffer_(); 1385 player.tech_.hls.checkBuffer_();
1299 equal(requests.length, 1, 'resumes segment fetching'); 1386 equal(requests.length, 1, 'resumes segment fetching');
1300 }); 1387 });
1301 1388
...@@ -1306,7 +1393,7 @@ test('cancels outstanding XHRs when seeking', function() { ...@@ -1306,7 +1393,7 @@ test('cancels outstanding XHRs when seeking', function() {
1306 }); 1393 });
1307 openMediaSource(player); 1394 openMediaSource(player);
1308 standardXHRResponse(requests[0]); 1395 standardXHRResponse(requests[0]);
1309 player.tech.hls.media = { 1396 player.tech_.hls.media = {
1310 segments: [{ 1397 segments: [{
1311 uri: '0.ts', 1398 uri: '0.ts',
1312 duration: 10 1399 duration: 10
...@@ -1337,7 +1424,7 @@ test('when outstanding XHRs are cancelled, they get aborted properly', function( ...@@ -1337,7 +1424,7 @@ test('when outstanding XHRs are cancelled, they get aborted properly', function(
1337 // trigger a segment download request 1424 // trigger a segment download request
1338 player.trigger('timeupdate'); 1425 player.trigger('timeupdate');
1339 1426
1340 player.tech.hls.segmentXhr_.onreadystatechange = function() { 1427 player.tech_.hls.segmentXhr_.onreadystatechange = function() {
1341 readystatechanges++; 1428 readystatechanges++;
1342 }; 1429 };
1343 1430
...@@ -1347,7 +1434,7 @@ test('when outstanding XHRs are cancelled, they get aborted properly', function( ...@@ -1347,7 +1434,7 @@ test('when outstanding XHRs are cancelled, they get aborted properly', function(
1347 1434
1348 ok(requests[1].aborted, 'XHR aborted'); 1435 ok(requests[1].aborted, 'XHR aborted');
1349 strictEqual(requests.length, 3, 'opened new XHR'); 1436 strictEqual(requests.length, 3, 'opened new XHR');
1350 notEqual(player.tech.hls.segmentXhr_.url, requests[1].url, 'a new segment is request that is not the aborted one'); 1437 notEqual(player.tech_.hls.segmentXhr_.url, requests[1].url, 'a new segment is request that is not the aborted one');
1351 strictEqual(readystatechanges, 0, 'onreadystatechange was not called'); 1438 strictEqual(readystatechanges, 0, 'onreadystatechange was not called');
1352 }); 1439 });
1353 1440
...@@ -1369,15 +1456,15 @@ test('segmentXhr is properly nulled out when dispose is called', function() { ...@@ -1369,15 +1456,15 @@ test('segmentXhr is properly nulled out when dispose is called', function() {
1369 // trigger a segment download request 1456 // trigger a segment download request
1370 player.trigger('timeupdate'); 1457 player.trigger('timeupdate');
1371 1458
1372 player.tech.hls.segmentXhr_.onreadystatechange = function() { 1459 player.tech_.hls.segmentXhr_.onreadystatechange = function() {
1373 readystatechanges++; 1460 readystatechanges++;
1374 }; 1461 };
1375 1462
1376 player.tech.hls.dispose(); 1463 player.tech_.hls.dispose();
1377 1464
1378 ok(requests[1].aborted, 'XHR aborted'); 1465 ok(requests[1].aborted, 'XHR aborted');
1379 strictEqual(requests.length, 2, 'did not open a new XHR'); 1466 strictEqual(requests.length, 2, 'did not open a new XHR');
1380 equal(player.tech.hls.segmentXhr_, null, 'the segment xhr is nulled out'); 1467 equal(player.tech_.hls.segmentXhr_, null, 'the segment xhr is nulled out');
1381 strictEqual(readystatechanges, 0, 'onreadystatechange was not called'); 1468 strictEqual(readystatechanges, 0, 'onreadystatechange was not called');
1382 1469
1383 Flash.prototype.dispose = oldDispose; 1470 Flash.prototype.dispose = oldDispose;
...@@ -1392,9 +1479,9 @@ QUnit.skip('exposes in-band metadata events as cues', function() { ...@@ -1392,9 +1479,9 @@ QUnit.skip('exposes in-band metadata events as cues', function() {
1392 }); 1479 });
1393 openMediaSource(player); 1480 openMediaSource(player);
1394 1481
1395 player.tech.hls.segmentParser_.parseSegmentBinaryData = function() { 1482 player.tech_.hls.segmentParser_.parseSegmentBinaryData = function() {
1396 // trigger a metadata event 1483 // trigger a metadata event
1397 player.tech.hls.segmentParser_.metadataStream.trigger('data', { 1484 player.tech_.hls.segmentParser_.metadataStream.trigger('data', {
1398 pts: 2000, 1485 pts: 2000,
1399 data: new Uint8Array([]), 1486 data: new Uint8Array([]),
1400 frames: [{ 1487 frames: [{
...@@ -1448,9 +1535,9 @@ QUnit.skip('only adds in-band cues the first time they are encountered', functio ...@@ -1448,9 +1535,9 @@ QUnit.skip('only adds in-band cues the first time they are encountered', functio
1448 }); 1535 });
1449 openMediaSource(player); 1536 openMediaSource(player);
1450 1537
1451 player.tech.hls.segmentParser_.parseSegmentBinaryData = function() { 1538 player.tech_.hls.segmentParser_.parseSegmentBinaryData = function() {
1452 // trigger a metadata event 1539 // trigger a metadata event
1453 player.tech.hls.segmentParser_.metadataStream.trigger('data', { 1540 player.tech_.hls.segmentParser_.metadataStream.trigger('data', {
1454 pts: 2000, 1541 pts: 2000,
1455 data: new Uint8Array([]), 1542 data: new Uint8Array([]),
1456 frames: [{ 1543 frames: [{
...@@ -1463,7 +1550,7 @@ QUnit.skip('only adds in-band cues the first time they are encountered', functio ...@@ -1463,7 +1550,7 @@ QUnit.skip('only adds in-band cues the first time they are encountered', functio
1463 standardXHRResponse(requests.shift()); 1550 standardXHRResponse(requests.shift());
1464 // seek back to the first segment 1551 // seek back to the first segment
1465 player.currentTime(0); 1552 player.currentTime(0);
1466 player.tech.hls.trigger('seeking'); 1553 player.tech_.hls.trigger('seeking');
1467 tags.push({ pts: 0, bytes: new Uint8Array(1) }); 1554 tags.push({ pts: 0, bytes: new Uint8Array(1) });
1468 standardXHRResponse(requests.shift()); 1555 standardXHRResponse(requests.shift());
1469 1556
...@@ -1483,10 +1570,10 @@ QUnit.skip('clears in-band cues ahead of current time on seek', function() { ...@@ -1483,10 +1570,10 @@ QUnit.skip('clears in-band cues ahead of current time on seek', function() {
1483 }); 1570 });
1484 openMediaSource(player); 1571 openMediaSource(player);
1485 1572
1486 player.tech.hls.segmentParser_.parseSegmentBinaryData = function() { 1573 player.tech_.hls.segmentParser_.parseSegmentBinaryData = function() {
1487 // trigger a metadata event 1574 // trigger a metadata event
1488 while (events.length) { 1575 while (events.length) {
1489 player.tech.hls.segmentParser_.metadataStream.trigger('data', events.shift()); 1576 player.tech_.hls.segmentParser_.metadataStream.trigger('data', events.shift());
1490 } 1577 }
1491 }; 1578 };
1492 standardXHRResponse(requests.shift()); // media 1579 standardXHRResponse(requests.shift()); // media
...@@ -1519,7 +1606,7 @@ QUnit.skip('clears in-band cues ahead of current time on seek', function() { ...@@ -1519,7 +1606,7 @@ QUnit.skip('clears in-band cues ahead of current time on seek', function() {
1519 value: 'cue 2' 1606 value: 'cue 2'
1520 }] 1607 }]
1521 }); 1608 });
1522 player.tech.hls.checkBuffer_(); 1609 player.tech_.hls.checkBuffer_();
1523 standardXHRResponse(requests.shift()); // segment 1 1610 standardXHRResponse(requests.shift()); // segment 1
1524 1611
1525 track = player.textTracks()[0]; 1612 track = player.textTracks()[0];
...@@ -1541,9 +1628,9 @@ QUnit.skip('translates ID3 PTS values to cue media timeline positions', function ...@@ -1541,9 +1628,9 @@ QUnit.skip('translates ID3 PTS values to cue media timeline positions', function
1541 }); 1628 });
1542 openMediaSource(player); 1629 openMediaSource(player);
1543 1630
1544 player.tech.hls.segmentParser_.parseSegmentBinaryData = function() { 1631 player.tech_.hls.segmentParser_.parseSegmentBinaryData = function() {
1545 // trigger a metadata event 1632 // trigger a metadata event
1546 player.tech.hls.segmentParser_.metadataStream.trigger('data', { 1633 player.tech_.hls.segmentParser_.metadataStream.trigger('data', {
1547 pts: 5 * 1000, 1634 pts: 5 * 1000,
1548 data: new Uint8Array([]), 1635 data: new Uint8Array([]),
1549 frames: [{ 1636 frames: [{
...@@ -1636,10 +1723,10 @@ QUnit.skip('translates ID3 PTS values across discontinuities', function() { ...@@ -1636,10 +1723,10 @@ QUnit.skip('translates ID3 PTS values across discontinuities', function() {
1636 }); 1723 });
1637 openMediaSource(player); 1724 openMediaSource(player);
1638 1725
1639 player.tech.hls.segmentParser_.parseSegmentBinaryData = function() { 1726 player.tech_.hls.segmentParser_.parseSegmentBinaryData = function() {
1640 // trigger a metadata event 1727 // trigger a metadata event
1641 if (events.length) { 1728 if (events.length) {
1642 player.tech.hls.segmentParser_.metadataStream.trigger('data', events.shift()); 1729 player.tech_.hls.segmentParser_.metadataStream.trigger('data', events.shift());
1643 } 1730 }
1644 }; 1731 };
1645 1732
...@@ -1677,7 +1764,7 @@ QUnit.skip('translates ID3 PTS values across discontinuities', function() { ...@@ -1677,7 +1764,7 @@ QUnit.skip('translates ID3 PTS values across discontinuities', function() {
1677 value: 'cue 1' 1764 value: 'cue 1'
1678 }] 1765 }]
1679 }); 1766 });
1680 player.tech.hls.checkBuffer_(); 1767 player.tech_.hls.checkBuffer_();
1681 standardXHRResponse(requests.shift()); 1768 standardXHRResponse(requests.shift());
1682 1769
1683 track = player.textTracks()[0]; 1770 track = player.textTracks()[0];
...@@ -1734,16 +1821,16 @@ test('adjusts the segment offsets for out-of-buffer seeking', function() { ...@@ -1734,16 +1821,16 @@ test('adjusts the segment offsets for out-of-buffer seeking', function() {
1734 }); 1821 });
1735 openMediaSource(player); 1822 openMediaSource(player);
1736 standardXHRResponse(requests.shift()); // media 1823 standardXHRResponse(requests.shift()); // media
1737 player.tech.hls.sourceBuffer.buffered = function() { 1824 player.tech_.hls.sourceBuffer.buffered = function() {
1738 return videojs.createTimeRange(0, 20); 1825 return videojs.createTimeRange(0, 20);
1739 }; 1826 };
1740 equal(player.tech.hls.mediaIndex, 0, 'starts at zero'); 1827 equal(player.tech_.hls.mediaIndex, 0, 'starts at zero');
1741 1828
1742 player.tech.setCurrentTime(35); 1829 player.tech_.setCurrentTime(35);
1743 clock.tick(1); 1830 clock.tick(1);
1744 // drop the aborted segment 1831 // drop the aborted segment
1745 requests.shift(); 1832 requests.shift();
1746 equal(player.tech.hls.mediaIndex, 3, 'moved the mediaIndex'); 1833 equal(player.tech_.hls.mediaIndex, 3, 'moved the mediaIndex');
1747 standardXHRResponse(requests.shift()); 1834 standardXHRResponse(requests.shift());
1748 }); 1835 });
1749 1836
...@@ -1754,7 +1841,7 @@ test('seeks between buffered time ranges', function() { ...@@ -1754,7 +1841,7 @@ test('seeks between buffered time ranges', function() {
1754 }); 1841 });
1755 openMediaSource(player); 1842 openMediaSource(player);
1756 standardXHRResponse(requests.shift()); // media 1843 standardXHRResponse(requests.shift()); // media
1757 player.tech.buffered = function() { 1844 player.tech_.buffered = function() {
1758 return { 1845 return {
1759 length: 2, 1846 length: 2,
1760 ranges_: [[0, 10], [20, 30]], 1847 ranges_: [[0, 10], [20, 30]],
...@@ -1767,11 +1854,11 @@ test('seeks between buffered time ranges', function() { ...@@ -1767,11 +1854,11 @@ test('seeks between buffered time ranges', function() {
1767 }; 1854 };
1768 }; 1855 };
1769 1856
1770 player.tech.setCurrentTime(15); 1857 player.tech_.setCurrentTime(15);
1771 clock.tick(1); 1858 clock.tick(1);
1772 // drop the aborted segment 1859 // drop the aborted segment
1773 requests.shift(); 1860 requests.shift();
1774 equal(player.tech.hls.mediaIndex, 1, 'updated the mediaIndex'); 1861 equal(player.tech_.hls.mediaIndex, 1, 'updated the mediaIndex');
1775 standardXHRResponse(requests.shift()); 1862 standardXHRResponse(requests.shift());
1776 }); 1863 });
1777 1864
...@@ -1783,14 +1870,14 @@ test('does not modify the media index for in-buffer seeking', function() { ...@@ -1783,14 +1870,14 @@ test('does not modify the media index for in-buffer seeking', function() {
1783 }); 1870 });
1784 openMediaSource(player); 1871 openMediaSource(player);
1785 standardXHRResponse(requests.shift()); 1872 standardXHRResponse(requests.shift());
1786 player.tech.buffered = function() { 1873 player.tech_.buffered = function() {
1787 return videojs.createTimeRange(0, 20); 1874 return videojs.createTimeRange(0, 20);
1788 }; 1875 };
1789 mediaIndex = player.tech.hls.mediaIndex; 1876 mediaIndex = player.tech_.hls.mediaIndex;
1790 1877
1791 player.tech.setCurrentTime(11); 1878 player.tech_.setCurrentTime(11);
1792 clock.tick(1); 1879 clock.tick(1);
1793 equal(player.tech.hls.mediaIndex, mediaIndex, 'did not interrupt buffering'); 1880 equal(player.tech_.hls.mediaIndex, mediaIndex, 'did not interrupt buffering');
1794 equal(requests.length, 1, 'did not abort the outstanding request'); 1881 equal(requests.length, 1, 'did not abort the outstanding request');
1795 }); 1882 });
1796 1883
...@@ -1802,7 +1889,7 @@ test('playlist 404 should end stream with a network error', function() { ...@@ -1802,7 +1889,7 @@ test('playlist 404 should end stream with a network error', function() {
1802 openMediaSource(player); 1889 openMediaSource(player);
1803 requests.pop().respond(404); 1890 requests.pop().respond(404);
1804 1891
1805 equal(player.tech.hls.mediaSource.error_, 'network', 'set a network error'); 1892 equal(player.tech_.hls.mediaSource.error_, 'network', 'set a network error');
1806 }); 1893 });
1807 1894
1808 test('segment 404 should trigger MEDIA_ERR_NETWORK', function () { 1895 test('segment 404 should trigger MEDIA_ERR_NETWORK', function () {
...@@ -1815,8 +1902,8 @@ test('segment 404 should trigger MEDIA_ERR_NETWORK', function () { ...@@ -1815,8 +1902,8 @@ test('segment 404 should trigger MEDIA_ERR_NETWORK', function () {
1815 1902
1816 standardXHRResponse(requests[0]); 1903 standardXHRResponse(requests[0]);
1817 requests[1].respond(404); 1904 requests[1].respond(404);
1818 ok(player.tech.hls.error.message, 'an error message is available'); 1905 ok(player.tech_.hls.error.message, 'an error message is available');
1819 equal(2, player.tech.hls.error.code, 'Player error code should be set to MediaError.MEDIA_ERR_NETWORK'); 1906 equal(2, player.tech_.hls.error.code, 'Player error code should be set to MediaError.MEDIA_ERR_NETWORK');
1820 }); 1907 });
1821 1908
1822 test('segment 500 should trigger MEDIA_ERR_ABORTED', function () { 1909 test('segment 500 should trigger MEDIA_ERR_ABORTED', function () {
...@@ -1829,8 +1916,8 @@ test('segment 500 should trigger MEDIA_ERR_ABORTED', function () { ...@@ -1829,8 +1916,8 @@ test('segment 500 should trigger MEDIA_ERR_ABORTED', function () {
1829 1916
1830 standardXHRResponse(requests[0]); 1917 standardXHRResponse(requests[0]);
1831 requests[1].respond(500); 1918 requests[1].respond(500);
1832 ok(player.tech.hls.error.message, 'an error message is available'); 1919 ok(player.tech_.hls.error.message, 'an error message is available');
1833 equal(4, player.tech.hls.error.code, 'Player error code should be set to MediaError.MEDIA_ERR_ABORTED'); 1920 equal(4, player.tech_.hls.error.code, 'Player error code should be set to MediaError.MEDIA_ERR_ABORTED');
1834 }); 1921 });
1835 1922
1836 test('seeking in an empty playlist is a non-erroring noop', function() { 1923 test('seeking in an empty playlist is a non-erroring noop', function() {
...@@ -1845,7 +1932,7 @@ test('seeking in an empty playlist is a non-erroring noop', function() { ...@@ -1845,7 +1932,7 @@ test('seeking in an empty playlist is a non-erroring noop', function() {
1845 requests.shift().respond(200, null, '#EXTM3U\n'); 1932 requests.shift().respond(200, null, '#EXTM3U\n');
1846 1933
1847 requestsLength = requests.length; 1934 requestsLength = requests.length;
1848 player.tech.setCurrentTime(183); 1935 player.tech_.setCurrentTime(183);
1849 clock.tick(1); 1936 clock.tick(1);
1850 1937
1851 equal(requests.length, requestsLength, 'made no additional requests'); 1938 equal(requests.length, requestsLength, 'made no additional requests');
...@@ -1860,7 +1947,7 @@ test('duration is Infinity for live playlists', function() { ...@@ -1860,7 +1947,7 @@ test('duration is Infinity for live playlists', function() {
1860 1947
1861 standardXHRResponse(requests[0]); 1948 standardXHRResponse(requests[0]);
1862 1949
1863 strictEqual(player.tech.hls.mediaSource.duration, 1950 strictEqual(player.tech_.hls.mediaSource.duration,
1864 Infinity, 1951 Infinity,
1865 'duration is infinity'); 1952 'duration is infinity');
1866 }); 1953 });
...@@ -1871,7 +1958,7 @@ test('updates the media index when a playlist reloads', function() { ...@@ -1871,7 +1958,7 @@ test('updates the media index when a playlist reloads', function() {
1871 type: 'application/vnd.apple.mpegurl' 1958 type: 'application/vnd.apple.mpegurl'
1872 }); 1959 });
1873 openMediaSource(player); 1960 openMediaSource(player);
1874 player.tech.trigger('play'); 1961 player.tech_.trigger('play');
1875 1962
1876 requests[0].respond(200, null, 1963 requests[0].respond(200, null,
1877 '#EXTM3U\n' + 1964 '#EXTM3U\n' +
...@@ -1883,9 +1970,9 @@ test('updates the media index when a playlist reloads', function() { ...@@ -1883,9 +1970,9 @@ test('updates the media index when a playlist reloads', function() {
1883 '2.ts\n'); 1970 '2.ts\n');
1884 standardXHRResponse(requests[1]); 1971 standardXHRResponse(requests[1]);
1885 // play the stream until 2.ts is playing 1972 // play the stream until 2.ts is playing
1886 player.tech.hls.mediaIndex = 3; 1973 player.tech_.hls.mediaIndex = 3;
1887 // trigger a playlist refresh 1974 // trigger a playlist refresh
1888 player.tech.hls.playlists.trigger('mediaupdatetimeout'); 1975 player.tech_.hls.playlists.trigger('mediaupdatetimeout');
1889 requests[2].respond(200, null, 1976 requests[2].respond(200, null,
1890 '#EXTM3U\n' + 1977 '#EXTM3U\n' +
1891 '#EXT-X-MEDIA-SEQUENCE:1\n' + 1978 '#EXT-X-MEDIA-SEQUENCE:1\n' +
...@@ -1896,7 +1983,7 @@ test('updates the media index when a playlist reloads', function() { ...@@ -1896,7 +1983,7 @@ test('updates the media index when a playlist reloads', function() {
1896 '#EXTINF:10,\n' + 1983 '#EXTINF:10,\n' +
1897 '3.ts\n'); 1984 '3.ts\n');
1898 1985
1899 strictEqual(player.tech.hls.mediaIndex, 2, 'mediaIndex is updated after the reload'); 1986 strictEqual(player.tech_.hls.mediaIndex, 2, 'mediaIndex is updated after the reload');
1900 }); 1987 });
1901 1988
1902 test('live playlist starts three target durations before live', function() { 1989 test('live playlist starts three target durations before live', function() {
...@@ -1922,12 +2009,12 @@ test('live playlist starts three target durations before live', function() { ...@@ -1922,12 +2009,12 @@ test('live playlist starts three target durations before live', function() {
1922 2009
1923 equal(requests.length, 0, 'no outstanding segment request'); 2010 equal(requests.length, 0, 'no outstanding segment request');
1924 2011
1925 player.tech.paused = function() { return false; }; 2012 player.tech_.paused = function() { return false; };
1926 player.tech.trigger('play'); 2013 player.tech_.trigger('play');
1927 clock.tick(1); 2014 clock.tick(1);
1928 mediaPlaylist = player.tech.hls.playlists.media(); 2015 mediaPlaylist = player.tech_.hls.playlists.media();
1929 equal(player.tech.hls.mediaIndex, 1, 'mediaIndex is updated at play'); 2016 equal(player.tech_.hls.mediaIndex, 1, 'mediaIndex is updated at play');
1930 equal(player.currentTime(), player.tech.hls.seekable().end(0), 'seeked to the seekable end'); 2017 equal(player.currentTime(), player.tech_.hls.seekable().end(0), 'seeked to the seekable end');
1931 2018
1932 equal(requests.length, 1, 'begins buffering'); 2019 equal(requests.length, 1, 'begins buffering');
1933 }); 2020 });
...@@ -1959,14 +2046,14 @@ test('live playlist starts with correct currentTime value', function() { ...@@ -1959,14 +2046,14 @@ test('live playlist starts with correct currentTime value', function() {
1959 2046
1960 standardXHRResponse(requests[0]); 2047 standardXHRResponse(requests[0]);
1961 2048
1962 player.tech.hls.playlists.trigger('loadedmetadata'); 2049 player.tech_.hls.playlists.trigger('loadedmetadata');
1963 2050
1964 player.tech.paused = function() { return false; }; 2051 player.tech_.paused = function() { return false; };
1965 player.tech.trigger('play'); 2052 player.tech_.trigger('play');
1966 clock.tick(1); 2053 clock.tick(1);
1967 2054
1968 strictEqual(player.currentTime(), 2055 strictEqual(player.currentTime(),
1969 videojs.Hls.Playlist.seekable(player.tech.hls.playlists.media()).end(0), 2056 videojs.Hls.Playlist.seekable(player.tech_.hls.playlists.media()).end(0),
1970 'currentTime is updated at playback'); 2057 'currentTime is updated at playback');
1971 }); 2058 });
1972 2059
...@@ -1985,22 +2072,22 @@ test('resets the time to a seekable position when resuming a live stream ' + ...@@ -1985,22 +2072,22 @@ test('resets the time to a seekable position when resuming a live stream ' +
1985 '16.ts\n'); 2072 '16.ts\n');
1986 // mock out the player to simulate a live stream that has been 2073 // mock out the player to simulate a live stream that has been
1987 // playing for awhile 2074 // playing for awhile
1988 player.tech.hls.seekable = function() { 2075 player.tech_.hls.seekable = function() {
1989 return videojs.createTimeRange(160, 170); 2076 return videojs.createTimeRange(160, 170);
1990 }; 2077 };
1991 player.tech.setCurrentTime = function(time) { 2078 player.tech_.setCurrentTime = function(time) {
1992 if (time !== undefined) { 2079 if (time !== undefined) {
1993 seekTarget = time; 2080 seekTarget = time;
1994 } 2081 }
1995 }; 2082 };
1996 player.tech.played = function() { 2083 player.tech_.played = function() {
1997 return videojs.createTimeRange(120, 170); 2084 return videojs.createTimeRange(120, 170);
1998 }; 2085 };
1999 player.tech.trigger('playing'); 2086 player.tech_.trigger('playing');
2000 2087
2001 player.tech.trigger('play'); 2088 player.tech_.trigger('play');
2002 equal(seekTarget, player.seekable().start(0), 'seeked to the start of seekable'); 2089 equal(seekTarget, player.seekable().start(0), 'seeked to the start of seekable');
2003 player.tech.trigger('seeked'); 2090 player.tech_.trigger('seeked');
2004 }); 2091 });
2005 2092
2006 test('mediaIndex is zero before the first segment loads', function() { 2093 test('mediaIndex is zero before the first segment loads', function() {
...@@ -2014,7 +2101,7 @@ test('mediaIndex is zero before the first segment loads', function() { ...@@ -2014,7 +2101,7 @@ test('mediaIndex is zero before the first segment loads', function() {
2014 }); 2101 });
2015 openMediaSource(player); 2102 openMediaSource(player);
2016 2103
2017 strictEqual(player.tech.hls.mediaIndex, 0, 'mediaIndex is zero'); 2104 strictEqual(player.tech_.hls.mediaIndex, 0, 'mediaIndex is zero');
2018 }); 2105 });
2019 2106
2020 test('mediaIndex returns correctly at playlist boundaries', function() { 2107 test('mediaIndex returns correctly at playlist boundaries', function() {
...@@ -2027,13 +2114,13 @@ test('mediaIndex returns correctly at playlist boundaries', function() { ...@@ -2027,13 +2114,13 @@ test('mediaIndex returns correctly at playlist boundaries', function() {
2027 standardXHRResponse(requests.shift()); // master 2114 standardXHRResponse(requests.shift()); // master
2028 standardXHRResponse(requests.shift()); // media 2115 standardXHRResponse(requests.shift()); // media
2029 2116
2030 strictEqual(player.tech.hls.mediaIndex, 0, 'mediaIndex is zero at first segment'); 2117 strictEqual(player.tech_.hls.mediaIndex, 0, 'mediaIndex is zero at first segment');
2031 2118
2032 // seek to end 2119 // seek to end
2033 player.tech.setCurrentTime(40); 2120 player.tech_.setCurrentTime(40);
2034 clock.tick(1); 2121 clock.tick(1);
2035 2122
2036 strictEqual(player.tech.hls.mediaIndex, 3, 'mediaIndex is 3 at last segment'); 2123 strictEqual(player.tech_.hls.mediaIndex, 3, 'mediaIndex is 3 at last segment');
2037 }); 2124 });
2038 2125
2039 test('reloads out-of-date live playlists when switching variants', function() { 2126 test('reloads out-of-date live playlists when switching variants', function() {
...@@ -2043,7 +2130,7 @@ test('reloads out-of-date live playlists when switching variants', function() { ...@@ -2043,7 +2130,7 @@ test('reloads out-of-date live playlists when switching variants', function() {
2043 }); 2130 });
2044 openMediaSource(player); 2131 openMediaSource(player);
2045 2132
2046 player.tech.hls.master = { 2133 player.tech_.hls.master = {
2047 playlists: [{ 2134 playlists: [{
2048 mediaSequence: 15, 2135 mediaSequence: 15,
2049 segments: [1, 1, 1] 2136 segments: [1, 1, 1]
...@@ -2054,7 +2141,7 @@ test('reloads out-of-date live playlists when switching variants', function() { ...@@ -2054,7 +2141,7 @@ test('reloads out-of-date live playlists when switching variants', function() {
2054 }] 2141 }]
2055 }; 2142 };
2056 // playing segment 15 on playlist zero 2143 // playing segment 15 on playlist zero
2057 player.tech.hls.media = player.tech.hls.master.playlists[0]; 2144 player.tech_.hls.media = player.tech_.hls.master.playlists[0];
2058 player.mediaIndex = 1; 2145 player.mediaIndex = 1;
2059 window.manifests['variant-update'] = '#EXTM3U\n' + 2146 window.manifests['variant-update'] = '#EXTM3U\n' +
2060 '#EXT-X-MEDIA-SEQUENCE:16\n' + 2147 '#EXT-X-MEDIA-SEQUENCE:16\n' +
...@@ -2064,8 +2151,8 @@ test('reloads out-of-date live playlists when switching variants', function() { ...@@ -2064,8 +2151,8 @@ test('reloads out-of-date live playlists when switching variants', function() {
2064 '17.ts\n'; 2151 '17.ts\n';
2065 2152
2066 // switch playlists 2153 // switch playlists
2067 player.tech.hls.selectPlaylist = function() { 2154 player.tech_.hls.selectPlaylist = function() {
2068 return player.tech.hls.master.playlists[1]; 2155 return player.tech_.hls.master.playlists[1];
2069 }; 2156 };
2070 // timeupdate downloads segment 16 then switches playlists 2157 // timeupdate downloads segment 16 then switches playlists
2071 player.trigger('timeupdate'); 2158 player.trigger('timeupdate');
...@@ -2149,7 +2236,7 @@ test('clears the segment buffer on seek', function() { ...@@ -2149,7 +2236,7 @@ test('clears the segment buffer on seek', function() {
2149 } 2236 }
2150 return currentTime; 2237 return currentTime;
2151 }; 2238 };
2152 player.tech.buffered = function() { 2239 player.tech_.buffered = function() {
2153 return videojs.createTimeRange(); 2240 return videojs.createTimeRange();
2154 }; 2241 };
2155 2242
...@@ -2169,20 +2256,52 @@ test('clears the segment buffer on seek', function() { ...@@ -2169,20 +2256,52 @@ test('clears the segment buffer on seek', function() {
2169 clock.tick(6000); 2256 clock.tick(6000);
2170 2257
2171 standardXHRResponse(requests.pop()); // 2.ts 2258 standardXHRResponse(requests.pop()); // 2.ts
2172 equal(player.tech.hls.segmentBuffer_.length, 2, 'started fetching segments'); 2259 equal(player.tech_.hls.segmentBuffer_.length, 2, 'started fetching segments');
2173 2260
2174 // seek back to the beginning 2261 // seek back to the beginning
2175 player.currentTime(0); 2262 player.currentTime(0);
2176 clock.tick(1); 2263 clock.tick(1);
2177 equal(player.tech.hls.segmentBuffer_.length, 0, 'cleared the segment buffer'); 2264 equal(player.tech_.hls.segmentBuffer_.length, 0, 'cleared the segment buffer');
2265 });
2266
2267 test('calls mediaSource\'s timestampOffset on discontinuity', function() {
2268 player.src({
2269 src: 'discontinuity.m3u8',
2270 type: 'application/vnd.apple.mpegurl'
2271 });
2272 openMediaSource(player);
2273 player.play();
2274 player.tech_.buffered = function() {
2275 return videojs.createTimeRange(0, 10);
2276 };
2277
2278 requests.pop().respond(200, null,
2279 '#EXTM3U\n' +
2280 '#EXTINF:10,0\n' +
2281 '1.ts\n' +
2282 '#EXT-X-DISCONTINUITY\n' +
2283 '#EXTINF:10,0\n' +
2284 '2.ts\n' +
2285 '#EXT-X-ENDLIST\n');
2286 standardXHRResponse(requests.pop()); // 1.ts
2287 player.tech_.hls.sourceBuffer.timestampOffset = 0;
2288
2289 equal(player.tech_.hls.sourceBuffer.timestampOffset, 0, 'timestampOffset starts at zero');
2290
2291 // play to 6s to trigger the next segment request
2292 clock.tick(6000);
2293
2294 standardXHRResponse(requests.pop()); // 2.ts
2295 equal(player.tech_.hls.sourceBuffer.timestampOffset, 10, 'timestampOffset set after discontinuity');
2178 }); 2296 });
2179 2297
2298
2180 test('can seek before the source buffer opens', function() { 2299 test('can seek before the source buffer opens', function() {
2181 player.src({ 2300 player.src({
2182 src: 'media.m3u8', 2301 src: 'media.m3u8',
2183 type: 'application/vnd.apple.mpegurl' 2302 type: 'application/vnd.apple.mpegurl'
2184 }); 2303 });
2185 player.tech.triggerReady(); 2304 player.tech_.triggerReady();
2186 clock.tick(1); 2305 clock.tick(1);
2187 standardXHRResponse(requests.shift()); 2306 standardXHRResponse(requests.shift());
2188 player.triggerReady(); 2307 player.triggerReady();
...@@ -2191,7 +2310,6 @@ test('can seek before the source buffer opens', function() { ...@@ -2191,7 +2310,6 @@ test('can seek before the source buffer opens', function() {
2191 equal(player.currentTime(), 1, 'seeked'); 2310 equal(player.currentTime(), 1, 'seeked');
2192 }); 2311 });
2193 2312
2194 // TODO: Decide on proper discontinuity behavior
2195 QUnit.skip('sets the timestampOffset after seeking to discontinuity', function() { 2313 QUnit.skip('sets the timestampOffset after seeking to discontinuity', function() {
2196 var bufferEnd; 2314 var bufferEnd;
2197 player.src({ 2315 player.src({
...@@ -2199,7 +2317,7 @@ QUnit.skip('sets the timestampOffset after seeking to discontinuity', function() ...@@ -2199,7 +2317,7 @@ QUnit.skip('sets the timestampOffset after seeking to discontinuity', function()
2199 type: 'application/vnd.apple.mpegurl' 2317 type: 'application/vnd.apple.mpegurl'
2200 }); 2318 });
2201 openMediaSource(player); 2319 openMediaSource(player);
2202 player.tech.buffered = function() { 2320 player.tech_.buffered = function() {
2203 return videojs.createTimeRange(0, bufferEnd); 2321 return videojs.createTimeRange(0, bufferEnd);
2204 }; 2322 };
2205 2323
...@@ -2214,14 +2332,14 @@ QUnit.skip('sets the timestampOffset after seeking to discontinuity', function() ...@@ -2214,14 +2332,14 @@ QUnit.skip('sets the timestampOffset after seeking to discontinuity', function()
2214 standardXHRResponse(requests.pop()); // 1.ts 2332 standardXHRResponse(requests.pop()); // 1.ts
2215 2333
2216 // seek to a discontinuity 2334 // seek to a discontinuity
2217 player.tech.setCurrentTime(10); 2335 player.tech_.setCurrentTime(10);
2218 bufferEnd = 9.9; 2336 bufferEnd = 9.9;
2219 clock.tick(1); 2337 clock.tick(1);
2220 standardXHRResponse(requests.pop()); // 1.ts 2338 standardXHRResponse(requests.pop()); // 1.ts, again
2221 player.tech.hls.checkBuffer_(); 2339 player.tech_.hls.checkBuffer_();
2222 standardXHRResponse(requests.pop()); // 2.ts, again 2340 standardXHRResponse(requests.pop()); // 2.ts
2223 equal(player.tech.hls.sourceBuffer.timestampOffset, 2341 equal(player.tech_.hls.sourceBuffer.timestampOffset,
2224 10, 2342 9.9,
2225 'set the timestamp offset'); 2343 'set the timestamp offset');
2226 }); 2344 });
2227 2345
...@@ -2234,7 +2352,7 @@ QUnit.skip('tracks segment end times as they are buffered', function() { ...@@ -2234,7 +2352,7 @@ QUnit.skip('tracks segment end times as they are buffered', function() {
2234 openMediaSource(player); 2352 openMediaSource(player);
2235 2353
2236 // as new segments are downloaded, the buffer end is updated 2354 // as new segments are downloaded, the buffer end is updated
2237 player.tech.buffered = function() { 2355 player.tech_.buffered = function() {
2238 return videojs.createTimeRange(0, bufferEnd); 2356 return videojs.createTimeRange(0, bufferEnd);
2239 }; 2357 };
2240 requests.shift().respond(200, null, 2358 requests.shift().respond(200, null,
...@@ -2247,13 +2365,13 @@ QUnit.skip('tracks segment end times as they are buffered', function() { ...@@ -2247,13 +2365,13 @@ QUnit.skip('tracks segment end times as they are buffered', function() {
2247 2365
2248 // 0.ts is shorter than advertised 2366 // 0.ts is shorter than advertised
2249 standardXHRResponse(requests.shift()); 2367 standardXHRResponse(requests.shift());
2250 equal(player.tech.hls.mediaSource.duration, 20, 'original duration is from the m3u8'); 2368 equal(player.tech_.hls.mediaSource.duration, 20, 'original duration is from the m3u8');
2251 2369
2252 bufferEnd = 9.5; 2370 bufferEnd = 9.5;
2253 player.tech.hls.sourceBuffer.trigger('update'); 2371 player.tech_.hls.sourceBuffer.trigger('update');
2254 player.tech.hls.sourceBuffer.trigger('updateend'); 2372 player.tech_.hls.sourceBuffer.trigger('updateend');
2255 equal(player.tech.duration(), 10 + 9.5, 'updated duration'); 2373 equal(player.tech_.duration(), 10 + 9.5, 'updated duration');
2256 equal(player.tech.hls.appendingSegmentInfo_, null, 'cleared the appending segment'); 2374 equal(player.tech_.hls.appendingSegmentInfo_, null, 'cleared the appending segment');
2257 }); 2375 });
2258 2376
2259 QUnit.skip('seeking does not fail when targeted between segments', function() { 2377 QUnit.skip('seeking does not fail when targeted between segments', function() {
...@@ -2265,12 +2383,12 @@ QUnit.skip('seeking does not fail when targeted between segments', function() { ...@@ -2265,12 +2383,12 @@ QUnit.skip('seeking does not fail when targeted between segments', function() {
2265 openMediaSource(player); 2383 openMediaSource(player);
2266 2384
2267 // mock out the currentTime callbacks 2385 // mock out the currentTime callbacks
2268 player.tech.el().vjs_setProperty = function(property, value) { 2386 player.tech_.el().vjs_setProperty = function(property, value) {
2269 if (property === 'currentTime') { 2387 if (property === 'currentTime') {
2270 currentTime = value; 2388 currentTime = value;
2271 } 2389 }
2272 }; 2390 };
2273 player.tech.el().vjs_getProperty = function(property) { 2391 player.tech_.el().vjs_getProperty = function(property) {
2274 if (property === 'currentTime') { 2392 if (property === 'currentTime') {
2275 return currentTime; 2393 return currentTime;
2276 } 2394 }
...@@ -2278,7 +2396,7 @@ QUnit.skip('seeking does not fail when targeted between segments', function() { ...@@ -2278,7 +2396,7 @@ QUnit.skip('seeking does not fail when targeted between segments', function() {
2278 2396
2279 standardXHRResponse(requests.shift()); // media 2397 standardXHRResponse(requests.shift()); // media
2280 standardXHRResponse(requests.shift()); // segment 0 2398 standardXHRResponse(requests.shift()); // segment 0
2281 player.tech.hls.checkBuffer_(); 2399 player.tech_.hls.checkBuffer_();
2282 segmentUrl = requests[0].url; 2400 segmentUrl = requests[0].url;
2283 standardXHRResponse(requests.shift()); // segment 1 2401 standardXHRResponse(requests.shift()); // segment 1
2284 2402
...@@ -2286,12 +2404,12 @@ QUnit.skip('seeking does not fail when targeted between segments', function() { ...@@ -2286,12 +2404,12 @@ QUnit.skip('seeking does not fail when targeted between segments', function() {
2286 // less than the first in segment 1 2404 // less than the first in segment 1
2287 // FIXME: it's not possible to seek here without timestamp-based 2405 // FIXME: it's not possible to seek here without timestamp-based
2288 // segment durations 2406 // segment durations
2289 player.tech.setCurrentTime(9.4); 2407 player.tech_.setCurrentTime(9.4);
2290 clock.tick(1); 2408 clock.tick(1);
2291 equal(requests[0].url, segmentUrl, 'requested the later segment'); 2409 equal(requests[0].url, segmentUrl, 'requested the later segment');
2292 2410
2293 standardXHRResponse(requests.shift()); // segment 1 2411 standardXHRResponse(requests.shift()); // segment 1
2294 player.tech.trigger('seeked'); 2412 player.tech_.trigger('seeked');
2295 equal(player.currentTime(), 9.5, 'seeked to the later time'); 2413 equal(player.currentTime(), 9.5, 'seeked to the later time');
2296 }); 2414 });
2297 2415
...@@ -2301,7 +2419,7 @@ test('resets the switching algorithm if a request times out', function() { ...@@ -2301,7 +2419,7 @@ test('resets the switching algorithm if a request times out', function() {
2301 type: 'application/vnd.apple.mpegurl' 2419 type: 'application/vnd.apple.mpegurl'
2302 }); 2420 });
2303 openMediaSource(player); 2421 openMediaSource(player);
2304 player.tech.hls.bandwidth = 20000; 2422 player.tech_.hls.bandwidth = 20000;
2305 2423
2306 standardXHRResponse(requests.shift()); // master 2424 standardXHRResponse(requests.shift()); // master
2307 standardXHRResponse(requests.shift()); // media.m3u8 2425 standardXHRResponse(requests.shift()); // media.m3u8
...@@ -2311,8 +2429,8 @@ test('resets the switching algorithm if a request times out', function() { ...@@ -2311,8 +2429,8 @@ test('resets the switching algorithm if a request times out', function() {
2311 2429
2312 standardXHRResponse(requests.shift()); 2430 standardXHRResponse(requests.shift());
2313 2431
2314 strictEqual(player.tech.hls.playlists.media(), 2432 strictEqual(player.tech_.hls.playlists.media(),
2315 player.tech.hls.playlists.master.playlists[1], 2433 player.tech_.hls.playlists.master.playlists[1],
2316 'reset to the lowest bitrate playlist'); 2434 'reset to the lowest bitrate playlist');
2317 }); 2435 });
2318 2436
...@@ -2324,10 +2442,10 @@ test('disposes the playlist loader', function() { ...@@ -2324,10 +2442,10 @@ test('disposes the playlist loader', function() {
2324 type: 'application/vnd.apple.mpegurl' 2442 type: 'application/vnd.apple.mpegurl'
2325 }); 2443 });
2326 openMediaSource(player); 2444 openMediaSource(player);
2327 loaderDispose = player.tech.hls.playlists.dispose; 2445 loaderDispose = player.tech_.hls.playlists.dispose;
2328 player.tech.hls.playlists.dispose = function() { 2446 player.tech_.hls.playlists.dispose = function() {
2329 disposes++; 2447 disposes++;
2330 loaderDispose.call(player.tech.hls.playlists); 2448 loaderDispose.call(player.tech_.hls.playlists);
2331 }; 2449 };
2332 2450
2333 player.dispose(); 2451 player.dispose();
...@@ -2383,7 +2501,7 @@ test('aborts the source buffer on disposal', function() { ...@@ -2383,7 +2501,7 @@ test('aborts the source buffer on disposal', function() {
2383 }); 2501 });
2384 openMediaSource(player); 2502 openMediaSource(player);
2385 standardXHRResponse(requests.shift()); 2503 standardXHRResponse(requests.shift());
2386 player.tech.hls.sourceBuffer.abort = function() { 2504 player.tech_.hls.sourceBuffer.abort = function() {
2387 aborts++; 2505 aborts++;
2388 }; 2506 };
2389 2507
...@@ -2392,19 +2510,21 @@ test('aborts the source buffer on disposal', function() { ...@@ -2392,19 +2510,21 @@ test('aborts the source buffer on disposal', function() {
2392 }); 2510 });
2393 2511
2394 test('the source handler supports HLS mime types', function() { 2512 test('the source handler supports HLS mime types', function() {
2395 ok(videojs.HlsSourceHandler.canHandleSource({ 2513 ['html5', 'flash'].forEach(function(techName) {
2514 ok(videojs.HlsSourceHandler(techName).canHandleSource({
2396 type: 'aPplicatiOn/x-MPegUrl' 2515 type: 'aPplicatiOn/x-MPegUrl'
2397 }), 'supports x-mpegurl'); 2516 }), 'supports x-mpegurl');
2398 ok(videojs.HlsSourceHandler.canHandleSource({ 2517 ok(videojs.HlsSourceHandler(techName).canHandleSource({
2399 type: 'aPplicatiOn/VnD.aPPle.MpEgUrL' 2518 type: 'aPplicatiOn/VnD.aPPle.MpEgUrL'
2400 }), 'supports vnd.apple.mpegurl'); 2519 }), 'supports vnd.apple.mpegurl');
2401 2520
2402 ok(!(videojs.HlsSourceHandler.canHandleSource({ 2521 ok(!(videojs.HlsSourceHandler(techName).canHandleSource({
2403 type: 'video/mp4' 2522 type: 'video/mp4'
2404 }) instanceof videojs.Hls), 'does not support mp4'); 2523 }) instanceof videojs.Hls), 'does not support mp4');
2405 ok(!(videojs.HlsSourceHandler.canHandleSource({ 2524 ok(!(videojs.HlsSourceHandler(techName).canHandleSource({
2406 type: 'video/x-flv' 2525 type: 'video/x-flv'
2407 }) instanceof videojs.Hls), 'does not support flv'); 2526 }) instanceof videojs.Hls), 'does not support flv');
2527 });
2408 }); 2528 });
2409 2529
2410 test('has no effect if native HLS is available', function() { 2530 test('has no effect if native HLS is available', function() {
...@@ -2416,7 +2536,7 @@ test('has no effect if native HLS is available', function() { ...@@ -2416,7 +2536,7 @@ test('has no effect if native HLS is available', function() {
2416 type: 'application/x-mpegURL' 2536 type: 'application/x-mpegURL'
2417 }); 2537 });
2418 2538
2419 ok(!player.tech.hls, 'did not load hls tech'); 2539 ok(!player.tech_.hls, 'did not load hls tech');
2420 player.dispose(); 2540 player.dispose();
2421 }); 2541 });
2422 2542
...@@ -2436,7 +2556,7 @@ test('tracks the bytes downloaded', function() { ...@@ -2436,7 +2556,7 @@ test('tracks the bytes downloaded', function() {
2436 }); 2556 });
2437 openMediaSource(player); 2557 openMediaSource(player);
2438 2558
2439 strictEqual(player.tech.hls.bytesReceived, 0, 'no bytes received'); 2559 strictEqual(player.tech_.hls.bytesReceived, 0, 'no bytes received');
2440 2560
2441 requests.shift().respond(200, null, 2561 requests.shift().respond(200, null,
2442 '#EXTM3U\n' + 2562 '#EXTM3U\n' +
...@@ -2449,15 +2569,15 @@ test('tracks the bytes downloaded', function() { ...@@ -2449,15 +2569,15 @@ test('tracks the bytes downloaded', function() {
2449 requests[0].response = new ArrayBuffer(17); 2569 requests[0].response = new ArrayBuffer(17);
2450 requests.shift().respond(200, null, ''); 2570 requests.shift().respond(200, null, '');
2451 2571
2452 strictEqual(player.tech.hls.bytesReceived, 17, 'tracked bytes received'); 2572 strictEqual(player.tech_.hls.bytesReceived, 17, 'tracked bytes received');
2453 2573
2454 player.tech.hls.checkBuffer_(); 2574 player.tech_.hls.checkBuffer_();
2455 2575
2456 // transmit some more 2576 // transmit some more
2457 requests[0].response = new ArrayBuffer(5); 2577 requests[0].response = new ArrayBuffer(5);
2458 requests.shift().respond(200, null, ''); 2578 requests.shift().respond(200, null, '');
2459 2579
2460 strictEqual(player.tech.hls.bytesReceived, 22, 'tracked more bytes'); 2580 strictEqual(player.tech_.hls.bytesReceived, 22, 'tracked more bytes');
2461 }); 2581 });
2462 2582
2463 test('re-emits mediachange events', function() { 2583 test('re-emits mediachange events', function() {
...@@ -2472,12 +2592,12 @@ test('re-emits mediachange events', function() { ...@@ -2472,12 +2592,12 @@ test('re-emits mediachange events', function() {
2472 }); 2592 });
2473 openMediaSource(player); 2593 openMediaSource(player);
2474 2594
2475 player.tech.hls.playlists.trigger('mediachange'); 2595 player.tech_.hls.playlists.trigger('mediachange');
2476 strictEqual(mediaChanges, 1, 'fired mediachange'); 2596 strictEqual(mediaChanges, 1, 'fired mediachange');
2477 }); 2597 });
2478 2598
2479 test('can be disposed before finishing initialization', function() { 2599 test('can be disposed before finishing initialization', function() {
2480 var player = createPlayer(), readyHandlers = []; 2600 var readyHandlers = [];
2481 player.ready = function(callback) { 2601 player.ready = function(callback) {
2482 readyHandlers.push(callback); 2602 readyHandlers.push(callback);
2483 }; 2603 };
...@@ -2493,6 +2613,7 @@ test('can be disposed before finishing initialization', function() { ...@@ -2493,6 +2613,7 @@ test('can be disposed before finishing initialization', function() {
2493 try { 2613 try {
2494 while (readyHandlers.length) { 2614 while (readyHandlers.length) {
2495 readyHandlers.shift().call(player); 2615 readyHandlers.shift().call(player);
2616 openMediaSource(player);
2496 } 2617 }
2497 ok(true, 'did not throw an exception'); 2618 ok(true, 'did not throw an exception');
2498 } catch (e) { 2619 } catch (e) {
...@@ -2507,7 +2628,7 @@ test('calls ended() on the media source at the end of a playlist', function() { ...@@ -2507,7 +2628,7 @@ test('calls ended() on the media source at the end of a playlist', function() {
2507 type: 'application/vnd.apple.mpegurl' 2628 type: 'application/vnd.apple.mpegurl'
2508 }); 2629 });
2509 openMediaSource(player); 2630 openMediaSource(player);
2510 player.tech.hls.mediaSource.endOfStream = function() { 2631 player.tech_.hls.mediaSource.endOfStream = function() {
2511 endOfStreams++; 2632 endOfStreams++;
2512 }; 2633 };
2513 // playlist response 2634 // playlist response
...@@ -2521,7 +2642,7 @@ test('calls ended() on the media source at the end of a playlist', function() { ...@@ -2521,7 +2642,7 @@ test('calls ended() on the media source at the end of a playlist', function() {
2521 requests.shift().respond(200, null, ''); 2642 requests.shift().respond(200, null, '');
2522 strictEqual(endOfStreams, 0, 'waits for the buffer update to finish'); 2643 strictEqual(endOfStreams, 0, 'waits for the buffer update to finish');
2523 2644
2524 player.tech.hls.sourceBuffer.trigger('updateend'); 2645 player.tech_.hls.sourceBuffer.trigger('updateend');
2525 strictEqual(endOfStreams, 1, 'ended media source'); 2646 strictEqual(endOfStreams, 1, 'ended media source');
2526 }); 2647 });
2527 2648
...@@ -2538,13 +2659,13 @@ test('calling play() at the end of a video resets the media index', function() { ...@@ -2538,13 +2659,13 @@ test('calling play() at the end of a video resets the media index', function() {
2538 '#EXT-X-ENDLIST\n'); 2659 '#EXT-X-ENDLIST\n');
2539 standardXHRResponse(requests.shift()); 2660 standardXHRResponse(requests.shift());
2540 2661
2541 strictEqual(player.tech.hls.mediaIndex, 1, 'index is 1 after the first segment'); 2662 strictEqual(player.tech_.hls.mediaIndex, 1, 'index is 1 after the first segment');
2542 player.tech.ended = function() { 2663 player.tech_.ended = function() {
2543 return true; 2664 return true;
2544 }; 2665 };
2545 2666
2546 player.tech.trigger('play'); 2667 player.tech_.trigger('play');
2547 strictEqual(player.tech.hls.mediaIndex, 0, 'index is 0 after the first segment'); 2668 strictEqual(player.tech_.hls.mediaIndex, 0, 'index is 0 after the first segment');
2548 }); 2669 });
2549 2670
2550 test('drainBuffer will not proceed with empty source buffer', function() { 2671 test('drainBuffer will not proceed with empty source buffer', function() {
...@@ -2555,7 +2676,7 @@ test('drainBuffer will not proceed with empty source buffer', function() { ...@@ -2555,7 +2676,7 @@ test('drainBuffer will not proceed with empty source buffer', function() {
2555 }); 2676 });
2556 openMediaSource(player); 2677 openMediaSource(player);
2557 2678
2558 oldMedia = player.tech.hls.playlists.media; 2679 oldMedia = player.tech_.hls.playlists.media;
2559 newMedia = {segments: [{ 2680 newMedia = {segments: [{
2560 key: { 2681 key: {
2561 'retries': 5 2682 'retries': 5
...@@ -2568,23 +2689,23 @@ test('drainBuffer will not proceed with empty source buffer', function() { ...@@ -2568,23 +2689,23 @@ test('drainBuffer will not proceed with empty source buffer', function() {
2568 }, 2689 },
2569 uri: 'http://media.example.com/fileSequence53-B.ts' 2690 uri: 'http://media.example.com/fileSequence53-B.ts'
2570 }]}; 2691 }]};
2571 player.tech.hls.playlists.media = function() { 2692 player.tech_.hls.playlists.media = function() {
2572 return newMedia; 2693 return newMedia;
2573 }; 2694 };
2574 2695
2575 player.tech.hls.sourceBuffer = undefined; 2696 player.tech_.hls.sourceBuffer = undefined;
2576 compareBuffer = [{mediaIndex: 0, playlist: newMedia, offset: 0, bytes: new Uint8Array(3)}]; 2697 compareBuffer = [{mediaIndex: 0, playlist: newMedia, offset: 0, bytes: new Uint8Array(3)}];
2577 player.tech.hls.segmentBuffer_ = [{mediaIndex: 0, playlist: newMedia, offset: 0, bytes: new Uint8Array(3)}]; 2698 player.tech_.hls.segmentBuffer_ = [{mediaIndex: 0, playlist: newMedia, offset: 0, bytes: new Uint8Array(3)}];
2578 2699
2579 player.tech.hls.drainBuffer(); 2700 player.tech_.hls.drainBuffer();
2580 2701
2581 /* Normally, drainBuffer() calls segmentBuffer.shift(), removing a segment from the stack. 2702 /* Normally, drainBuffer() calls segmentBuffer.shift(), removing a segment from the stack.
2582 * Comparing two buffers to ensure no segment was popped verifies that we returned early 2703 * Comparing two buffers to ensure no segment was popped verifies that we returned early
2583 * from drainBuffer() because sourceBuffer was empty. 2704 * from drainBuffer() because sourceBuffer was empty.
2584 */ 2705 */
2585 deepEqual(player.tech.hls.segmentBuffer_, compareBuffer, 'playlist remains unchanged'); 2706 deepEqual(player.tech_.hls.segmentBuffer_, compareBuffer, 'playlist remains unchanged');
2586 2707
2587 player.tech.hls.playlists.media = oldMedia; 2708 player.tech_.hls.playlists.media = oldMedia;
2588 }); 2709 });
2589 2710
2590 test('keys are requested when an encrypted segment is loaded', function() { 2711 test('keys are requested when an encrypted segment is loaded', function() {
...@@ -2593,13 +2714,13 @@ test('keys are requested when an encrypted segment is loaded', function() { ...@@ -2593,13 +2714,13 @@ test('keys are requested when an encrypted segment is loaded', function() {
2593 type: 'application/vnd.apple.mpegurl' 2714 type: 'application/vnd.apple.mpegurl'
2594 }); 2715 });
2595 openMediaSource(player); 2716 openMediaSource(player);
2596 player.tech.trigger('play'); 2717 player.tech_.trigger('play');
2597 standardXHRResponse(requests.shift()); // playlist 2718 standardXHRResponse(requests.shift()); // playlist
2598 standardXHRResponse(requests.shift()); // first segment 2719 standardXHRResponse(requests.shift()); // first segment
2599 2720
2600 strictEqual(requests.length, 1, 'a key XHR is created'); 2721 strictEqual(requests.length, 1, 'a key XHR is created');
2601 strictEqual(requests[0].url, 2722 strictEqual(requests[0].url,
2602 player.tech.hls.playlists.media().segments[0].key.uri, 2723 player.tech_.hls.playlists.media().segments[0].key.uri,
2603 'a key XHR is created with correct uri'); 2724 'a key XHR is created with correct uri');
2604 }); 2725 });
2605 2726
...@@ -2667,15 +2788,15 @@ test('a new key XHR is created when a the segment is received', function() { ...@@ -2667,15 +2788,15 @@ test('a new key XHR is created when a the segment is received', function() {
2667 standardXHRResponse(requests.shift()); // segment 1 2788 standardXHRResponse(requests.shift()); // segment 1
2668 standardXHRResponse(requests.shift()); // key 1 2789 standardXHRResponse(requests.shift()); // key 1
2669 // "finish" decrypting segment 1 2790 // "finish" decrypting segment 1
2670 player.tech.hls.segmentBuffer_[0].bytes = new Uint8Array(16); 2791 player.tech_.hls.segmentBuffer_[0].bytes = new Uint8Array(16);
2671 player.tech.hls.checkBuffer_(); 2792 player.tech_.hls.checkBuffer_();
2672 2793
2673 standardXHRResponse(requests.shift()); // segment 2 2794 standardXHRResponse(requests.shift()); // segment 2
2674 2795
2675 strictEqual(requests.length, 1, 'a key XHR is created'); 2796 strictEqual(requests.length, 1, 'a key XHR is created');
2676 strictEqual(requests[0].url, 2797 strictEqual(requests[0].url,
2677 'https://example.com/' + 2798 'https://example.com/' +
2678 player.tech.hls.playlists.media().segments[1].key.uri, 2799 player.tech_.hls.playlists.media().segments[1].key.uri,
2679 'a key XHR is created with the correct uri'); 2800 'a key XHR is created with the correct uri');
2680 }); 2801 });
2681 2802
...@@ -2708,7 +2829,7 @@ test('seeking should abort an outstanding key request and create a new one', fun ...@@ -2708,7 +2829,7 @@ test('seeking should abort an outstanding key request and create a new one', fun
2708 equal(requests.length, 1, 'requested the new key'); 2829 equal(requests.length, 1, 'requested the new key');
2709 equal(requests[0].url, 2830 equal(requests[0].url,
2710 'https://example.com/' + 2831 'https://example.com/' +
2711 player.tech.hls.playlists.media().segments[1].key.uri, 2832 player.tech_.hls.playlists.media().segments[1].key.uri,
2712 'urls should match'); 2833 'urls should match');
2713 }); 2834 });
2714 2835
...@@ -2718,7 +2839,7 @@ test('retries key requests once upon failure', function() { ...@@ -2718,7 +2839,7 @@ test('retries key requests once upon failure', function() {
2718 type: 'application/vnd.apple.mpegurl' 2839 type: 'application/vnd.apple.mpegurl'
2719 }); 2840 });
2720 openMediaSource(player); 2841 openMediaSource(player);
2721 player.tech.trigger('play'); 2842 player.tech_.trigger('play');
2722 2843
2723 requests.shift().respond(200, null, 2844 requests.shift().respond(200, null,
2724 '#EXTM3U\n' + 2845 '#EXTM3U\n' +
...@@ -2745,7 +2866,7 @@ test('skip segments if key requests fail more than once', function() { ...@@ -2745,7 +2866,7 @@ test('skip segments if key requests fail more than once', function() {
2745 type: 'application/vnd.apple.mpegurl' 2866 type: 'application/vnd.apple.mpegurl'
2746 }); 2867 });
2747 openMediaSource(player); 2868 openMediaSource(player);
2748 player.tech.trigger('play'); 2869 player.tech_.trigger('play');
2749 2870
2750 requests.shift().respond(200, null, 2871 requests.shift().respond(200, null,
2751 '#EXTM3U\n' + 2872 '#EXTM3U\n' +
...@@ -2755,14 +2876,14 @@ test('skip segments if key requests fail more than once', function() { ...@@ -2755,14 +2876,14 @@ test('skip segments if key requests fail more than once', function() {
2755 '#EXT-X-KEY:METHOD=AES-128,URI="htts://priv.example.com/key.php?r=53"\n' + 2876 '#EXT-X-KEY:METHOD=AES-128,URI="htts://priv.example.com/key.php?r=53"\n' +
2756 '#EXTINF:15.0,\n' + 2877 '#EXTINF:15.0,\n' +
2757 'http://media.example.com/fileSequence53-A.ts\n'); 2878 'http://media.example.com/fileSequence53-A.ts\n');
2758 player.tech.hls.sourceBuffer.appendBuffer = function(chunk) { 2879 player.tech_.hls.sourceBuffer.appendBuffer = function(chunk) {
2759 bytes.push(chunk); 2880 bytes.push(chunk);
2760 }; 2881 };
2761 standardXHRResponse(requests.shift()); // segment 1 2882 standardXHRResponse(requests.shift()); // segment 1
2762 requests.shift().respond(404); // fail key 2883 requests.shift().respond(404); // fail key
2763 requests.shift().respond(404); // fail key, again 2884 requests.shift().respond(404); // fail key, again
2764 2885
2765 player.tech.hls.checkBuffer_(); 2886 player.tech_.hls.checkBuffer_();
2766 standardXHRResponse(requests.shift()); // segment 2 2887 standardXHRResponse(requests.shift()); // segment 2
2767 equal(bytes.length, 0, 'did not append encrypted bytes'); 2888 equal(bytes.length, 0, 'did not append encrypted bytes');
2768 2889
...@@ -2770,8 +2891,8 @@ test('skip segments if key requests fail more than once', function() { ...@@ -2770,8 +2891,8 @@ test('skip segments if key requests fail more than once', function() {
2770 requests[0].response = new Uint32Array([0,0,0,0]).buffer; 2891 requests[0].response = new Uint32Array([0,0,0,0]).buffer;
2771 requests.shift().respond(200, null, ''); 2892 requests.shift().respond(200, null, '');
2772 // "finish" decryption 2893 // "finish" decryption
2773 player.tech.hls.segmentBuffer_[0].bytes = new Uint8Array(16); 2894 player.tech_.hls.segmentBuffer_[0].bytes = new Uint8Array(16);
2774 player.tech.hls.checkBuffer_(); 2895 player.tech_.hls.checkBuffer_();
2775 2896
2776 equal(bytes.length, 1, 'appended cleartext bytes from the second segment'); 2897 equal(bytes.length, 1, 'appended cleartext bytes from the second segment');
2777 deepEqual(bytes[0], new Uint8Array(16), 'appended bytes from the second segment, not the first'); 2898 deepEqual(bytes[0], new Uint8Array(16), 'appended bytes from the second segment, not the first');
...@@ -2785,7 +2906,7 @@ test('the key is supplied to the decrypter in the correct format', function() { ...@@ -2785,7 +2906,7 @@ test('the key is supplied to the decrypter in the correct format', function() {
2785 type: 'application/vnd.apple.mpegurl' 2906 type: 'application/vnd.apple.mpegurl'
2786 }); 2907 });
2787 openMediaSource(player); 2908 openMediaSource(player);
2788 player.tech.trigger('play'); 2909 player.tech_.trigger('play');
2789 2910
2790 requests.pop().respond(200, null, 2911 requests.pop().respond(200, null,
2791 '#EXTM3U\n' + 2912 '#EXTM3U\n' +
...@@ -2820,7 +2941,7 @@ test('supplies the media sequence of current segment as the IV by default, if no ...@@ -2820,7 +2941,7 @@ test('supplies the media sequence of current segment as the IV by default, if no
2820 type: 'application/vnd.apple.mpegurl' 2941 type: 'application/vnd.apple.mpegurl'
2821 }); 2942 });
2822 openMediaSource(player); 2943 openMediaSource(player);
2823 player.tech.trigger('play'); 2944 player.tech_.trigger('play');
2824 2945
2825 requests.pop().respond(200, null, 2946 requests.pop().respond(200, null,
2826 '#EXTM3U\n' + 2947 '#EXTM3U\n' +
...@@ -2860,15 +2981,15 @@ test('switching playlists with an outstanding key request does not stall playbac ...@@ -2860,15 +2981,15 @@ test('switching playlists with an outstanding key request does not stall playbac
2860 type: 'application/vnd.apple.mpegurl' 2981 type: 'application/vnd.apple.mpegurl'
2861 }); 2982 });
2862 openMediaSource(player); 2983 openMediaSource(player);
2863 player.tech.trigger('play'); 2984 player.tech_.trigger('play');
2864 2985
2865 // master playlist 2986 // master playlist
2866 standardXHRResponse(requests.shift()); 2987 standardXHRResponse(requests.shift());
2867 // media playlist 2988 // media playlist
2868 requests.shift().respond(200, null, media); 2989 requests.shift().respond(200, null, media);
2869 // mock out media switching from this point on 2990 // mock out media switching from this point on
2870 player.tech.hls.playlists.media = function() { 2991 player.tech_.hls.playlists.media = function() {
2871 return player.tech.hls.playlists.master.playlists[0]; 2992 return player.tech_.hls.playlists.master.playlists[0];
2872 }; 2993 };
2873 // first segment of the original media playlist 2994 // first segment of the original media playlist
2874 standardXHRResponse(requests.shift()); 2995 standardXHRResponse(requests.shift());
...@@ -2876,9 +2997,9 @@ test('switching playlists with an outstanding key request does not stall playbac ...@@ -2876,9 +2997,9 @@ test('switching playlists with an outstanding key request does not stall playbac
2876 requests.shift(); 2997 requests.shift();
2877 2998
2878 // "switch" media 2999 // "switch" media
2879 player.tech.hls.playlists.trigger('mediachange'); 3000 player.tech_.hls.playlists.trigger('mediachange');
2880 3001
2881 player.tech.hls.checkBuffer_(); 3002 player.tech_.hls.checkBuffer_();
2882 3003
2883 ok(requests.length, 'made a request'); 3004 ok(requests.length, 'made a request');
2884 equal(requests[0].url, 3005 equal(requests[0].url,
...@@ -2915,7 +3036,7 @@ test('treats invalid keys as a key request failure', function() { ...@@ -2915,7 +3036,7 @@ test('treats invalid keys as a key request failure', function() {
2915 type: 'application/vnd.apple.mpegurl' 3036 type: 'application/vnd.apple.mpegurl'
2916 }); 3037 });
2917 openMediaSource(player); 3038 openMediaSource(player);
2918 player.tech.trigger('play'); 3039 player.tech_.trigger('play');
2919 requests.shift().respond(200, null, 3040 requests.shift().respond(200, null,
2920 '#EXTM3U\n' + 3041 '#EXTM3U\n' +
2921 '#EXT-X-MEDIA-SEQUENCE:5\n' + 3042 '#EXT-X-MEDIA-SEQUENCE:5\n' +
...@@ -2925,7 +3046,7 @@ test('treats invalid keys as a key request failure', function() { ...@@ -2925,7 +3046,7 @@ test('treats invalid keys as a key request failure', function() {
2925 '#EXT-X-KEY:METHOD=NONE\n' + 3046 '#EXT-X-KEY:METHOD=NONE\n' +
2926 '#EXTINF:15.0,\n' + 3047 '#EXTINF:15.0,\n' +
2927 'http://media.example.com/fileSequence52-B.ts\n'); 3048 'http://media.example.com/fileSequence52-B.ts\n');
2928 player.tech.hls.sourceBuffer.appendBuffer = function(chunk) { 3049 player.tech_.hls.sourceBuffer.appendBuffer = function(chunk) {
2929 bytes.push(chunk); 3050 bytes.push(chunk);
2930 }; 3051 };
2931 // segment request 3052 // segment request
...@@ -2941,7 +3062,7 @@ test('treats invalid keys as a key request failure', function() { ...@@ -2941,7 +3062,7 @@ test('treats invalid keys as a key request failure', function() {
2941 requests.shift().respond(200, null, ''); 3062 requests.shift().respond(200, null, '');
2942 3063
2943 // the first segment should be dropped and playback moves on 3064 // the first segment should be dropped and playback moves on
2944 player.tech.hls.checkBuffer_(); 3065 player.tech_.hls.checkBuffer_();
2945 equal(bytes.length, 0, 'did not append bytes'); 3066 equal(bytes.length, 0, 'did not append bytes');
2946 3067
2947 // second segment request 3068 // second segment request
...@@ -2958,7 +3079,7 @@ test('live stream should not call endOfStream', function(){ ...@@ -2958,7 +3079,7 @@ test('live stream should not call endOfStream', function(){
2958 type: 'application/vnd.apple.mpegurl' 3079 type: 'application/vnd.apple.mpegurl'
2959 }); 3080 });
2960 openMediaSource(player); 3081 openMediaSource(player);
2961 player.tech.trigger('play'); 3082 player.tech_.trigger('play');
2962 requests[0].respond(200, null, 3083 requests[0].respond(200, null,
2963 '#EXTM3U\n' + 3084 '#EXTM3U\n' +
2964 '#EXT-X-MEDIA-SEQUENCE:0\n' + 3085 '#EXT-X-MEDIA-SEQUENCE:0\n' +
...@@ -2967,7 +3088,7 @@ test('live stream should not call endOfStream', function(){ ...@@ -2967,7 +3088,7 @@ test('live stream should not call endOfStream', function(){
2967 ); 3088 );
2968 requests[1].response = window.bcSegment; 3089 requests[1].response = window.bcSegment;
2969 requests[1].respond(200, null, ""); 3090 requests[1].respond(200, null, "");
2970 equal("open", player.tech.hls.mediaSource.readyState, 3091 equal("open", player.tech_.hls.mediaSource.readyState,
2971 "media source should be in open state, not ended state for live stream after the last segment in m3u8 downloaded"); 3092 "media source should be in open state, not ended state for live stream after the last segment in m3u8 downloaded");
2972 }); 3093 });
2973 3094
...@@ -2981,7 +3102,7 @@ test('does not download segments if preload option set to none', function() { ...@@ -2981,7 +3102,7 @@ test('does not download segments if preload option set to none', function() {
2981 openMediaSource(player); 3102 openMediaSource(player);
2982 standardXHRResponse(requests.shift()); // master 3103 standardXHRResponse(requests.shift()); // master
2983 standardXHRResponse(requests.shift()); // media 3104 standardXHRResponse(requests.shift()); // media
2984 player.tech.hls.checkBuffer_(); 3105 player.tech_.hls.checkBuffer_();
2985 3106
2986 requests = requests.filter(function(request) { 3107 requests = requests.filter(function(request) {
2987 return !/m3u8$/.test(request.uri); 3108 return !/m3u8$/.test(request.uri);
......