UNPKG

22.5 kBJavaScriptView Raw
1import videojs from 'video.js';
2import QUnit from 'qunit';
3import {
4 useFakeEnvironment,
5 useFakeMediaSource,
6 createPlayer,
7 openMediaSource,
8 standardXHRResponse
9} from './test-helpers.js';
10import PlaybackWatcher from '../src/playback-watcher';
11
12let monitorCurrentTime_;
13
14QUnit.module('PlaybackWatcher', {
15 beforeEach(assert) {
16 this.env = useFakeEnvironment(assert);
17 this.requests = this.env.requests;
18 this.mse = useFakeMediaSource();
19 this.clock = this.env.clock;
20 this.old = {};
21
22 // setup a player
23 this.player = createPlayer();
24 this.player.autoplay(true);
25 },
26
27 afterEach() {
28 this.env.restore();
29 this.mse.restore();
30 this.player.dispose();
31 }
32});
33
34QUnit.test('skips over gap in firefox with waiting event', function(assert) {
35 let hlsGapSkipEvents = 0;
36
37 this.player.autoplay(true);
38
39 this.player.tech_.on('usage', (event) => {
40 if (event.name === 'hls-gap-skip') {
41 hlsGapSkipEvents++;
42 }
43 });
44
45 // create a buffer with a gap between 10 & 20 seconds
46 this.player.tech_.buffered = function() {
47 return videojs.createTimeRanges([[0, 10], [20, 30]]);
48 };
49
50 // set an arbitrary source
51 this.player.src({
52 src: 'master.m3u8',
53 type: 'application/vnd.apple.mpegurl'
54 });
55
56 // start playback normally
57 this.player.tech_.triggerReady();
58 this.clock.tick(1);
59 standardXHRResponse(this.requests.shift());
60 openMediaSource(this.player, this.clock);
61 this.player.tech_.trigger('canplay');
62 this.player.tech_.trigger('play');
63 this.player.tech_.trigger('playing');
64 this.clock.tick(1);
65
66 assert.equal(hlsGapSkipEvents, 0, 'there is no skipped gap');
67 // seek to 10 seconds and wait 12 seconds
68 this.player.currentTime(10);
69 this.player.tech_.trigger('waiting');
70 this.clock.tick(12000);
71
72 // check that player jumped the gap
73 assert.equal(Math.round(this.player.currentTime()),
74 20, 'Player seeked over gap after timer');
75 assert.equal(hlsGapSkipEvents, 1, 'there is one skipped gap');
76});
77
78QUnit.test('skips over gap in chrome without waiting event', function(assert) {
79 let hlsGapSkipEvents = 0;
80
81 this.player.autoplay(true);
82
83 this.player.tech_.on('usage', (event) => {
84 if (event.name === 'hls-gap-skip') {
85 hlsGapSkipEvents++;
86 }
87 });
88
89 // create a buffer with a gap between 10 & 20 seconds
90 this.player.tech_.buffered = function() {
91 return videojs.createTimeRanges([[0, 10], [20, 30]]);
92 };
93
94 // set an arbitrary source
95 this.player.src({
96 src: 'master.m3u8',
97 type: 'application/vnd.apple.mpegurl'
98 });
99
100 // start playback normally
101 this.player.tech_.triggerReady();
102 this.clock.tick(1);
103 standardXHRResponse(this.requests.shift());
104 openMediaSource(this.player, this.clock);
105 this.player.tech_.trigger('canplay');
106 this.player.tech_.trigger('play');
107 this.player.tech_.trigger('playing');
108 this.clock.tick(1);
109
110 assert.equal(hlsGapSkipEvents, 0, 'there is no skipped gap');
111
112 // seek to 10 seconds & simulate chrome waiting event
113 this.player.currentTime(10);
114
115 this.clock.tick(4000);
116
117 // checks that player doesn't seek before timer expires
118 assert.equal(this.player.currentTime(), 10, 'Player doesnt seek over gap pre-timer');
119 this.clock.tick(10000);
120
121 // check that player jumped the gap
122 assert.equal(Math.round(this.player.currentTime()),
123 20, 'Player seeked over gap after timer');
124 assert.equal(hlsGapSkipEvents, 1, 'there is one skipped gap');
125});
126
127QUnit.test('skips over gap in Chrome due to video underflow', function(assert) {
128 let hlsVideoUnderflowEvents = 0;
129
130 this.player.autoplay(true);
131
132 this.player.tech_.on('usage', (event) => {
133 if (event.name === 'hls-video-underflow') {
134 hlsVideoUnderflowEvents++;
135 }
136 });
137
138 this.player.tech_.buffered = () => {
139 return videojs.createTimeRanges([[0, 10], [10.1, 20]]);
140 };
141
142 // set an arbitrary source
143 this.player.src({
144 src: 'master.m3u8',
145 type: 'application/vnd.apple.mpegurl'
146 });
147
148 // start playback normally
149 this.player.tech_.triggerReady();
150 this.clock.tick(1);
151 standardXHRResponse(this.requests.shift());
152 openMediaSource(this.player, this.clock);
153 this.player.tech_.trigger('play');
154 this.player.tech_.trigger('playing');
155 this.clock.tick(1);
156
157 assert.equal(hlsVideoUnderflowEvents, 0, 'no video underflow event got triggered');
158
159 this.player.currentTime(13);
160
161 let seeks = [];
162
163 this.player.tech_.setCurrentTime = (time) => {
164 seeks.push(time);
165 };
166
167 this.player.tech_.trigger('waiting');
168
169 assert.equal(seeks.length, 1, 'one seek');
170 assert.equal(seeks[0], 13, 'player seeked to current time');
171 assert.equal(hlsVideoUnderflowEvents, 1, 'triggered a video underflow event');
172});
173
174QUnit.test('seek to live point if we fall off the end of a live playlist',
175function(assert) {
176 // set an arbitrary live source
177 this.player.src({
178 src: 'liveStart30sBefore.m3u8',
179 type: 'application/vnd.apple.mpegurl'
180 });
181
182 // start playback normally
183 this.player.tech_.triggerReady();
184 this.clock.tick(1);
185 standardXHRResponse(this.requests.shift());
186 openMediaSource(this.player, this.clock);
187 this.player.tech_.trigger('play');
188 this.player.tech_.trigger('playing');
189 this.clock.tick(1);
190
191 this.player.currentTime(0);
192
193 let seeks = [];
194
195 this.player.tech_.setCurrentTime = (time) => {
196 seeks.push(time);
197 };
198
199 this.player.tech_.hls.playbackWatcher_.seekable = () => {
200 return videojs.createTimeRanges([[1, 45]]);
201 };
202
203 this.player.tech_.trigger('waiting');
204
205 assert.equal(seeks.length, 1, 'one seek');
206 assert.equal(seeks[0], 45, 'player seeked to live point');
207});
208
209QUnit.test('seeks to current time when stuck inside buffered region', function(assert) {
210
211 // set an arbitrary live source
212 this.player.src({
213 src: 'liveStart30sBefore.m3u8',
214 type: 'application/vnd.apple.mpegurl'
215 });
216
217 // start playback normally
218 this.player.tech_.triggerReady();
219 this.clock.tick(1);
220 standardXHRResponse(this.requests.shift());
221 openMediaSource(this.player, this.clock);
222 this.player.tech_.trigger('canplay');
223 this.player.tech_.trigger('play');
224 this.player.tech_.trigger('playing');
225 this.clock.tick(1);
226
227 this.player.currentTime(10);
228
229 let seeks = [];
230
231 this.player.tech_.setCurrentTime = (time) => {
232 seeks.push(time);
233 };
234
235 this.player.tech_.seeking = () => false;
236 this.player.tech_.buffered = () => videojs.createTimeRanges([[0, 30]]);
237 this.player.tech_.seekable = () => videojs.createTimeRanges([[0, 30]]);
238 this.player.tech_.paused = () => false;
239
240 // Playback watcher loop runs on a 250ms clock
241 this.clock.tick(250);
242
243 // Loop has run through once, `lastRecordedTime` should have been recorded
244 // and `consecutiveUpdates` set to 0 to begin count
245 assert.equal(this.player.tech_.hls.playbackWatcher_.lastRecordedTime, 10,
246 'Playback Watcher stored current time');
247 assert.equal(this.player.tech_.hls.playbackWatcher_.consecutiveUpdates, 0,
248 'consecutiveUpdates set to 0');
249
250 // Playback watcher loop runs on a 250ms clock
251 this.clock.tick(250);
252
253 // Loop should increment consecutive updates until it is >= 5
254 assert.equal(this.player.tech_.hls.playbackWatcher_.consecutiveUpdates, 1,
255 'consecutiveUpdates incremented');
256
257 // Playback watcher loop runs on a 250ms clock
258 this.clock.tick(250);
259
260 // Loop should increment consecutive updates until it is >= 5
261 assert.equal(this.player.tech_.hls.playbackWatcher_.consecutiveUpdates, 2,
262 'consecutiveUpdates incremented');
263
264 // Playback watcher loop runs on a 250ms clock
265 this.clock.tick(250);
266
267 // Loop should increment consecutive updates until it is >= 5
268 assert.equal(this.player.tech_.hls.playbackWatcher_.consecutiveUpdates, 3,
269 'consecutiveUpdates incremented');
270
271 // Playback watcher loop runs on a 250ms clock
272 this.clock.tick(250);
273
274 // Loop should increment consecutive updates until it is >= 5
275 assert.equal(this.player.tech_.hls.playbackWatcher_.consecutiveUpdates, 4,
276 'consecutiveUpdates incremented');
277
278 // Playback watcher loop runs on a 250ms clock
279 this.clock.tick(250);
280
281 // Loop should increment consecutive updates until it is >= 5
282 assert.equal(this.player.tech_.hls.playbackWatcher_.consecutiveUpdates, 5,
283 'consecutiveUpdates incremented');
284
285 // Playback watcher loop runs on a 250ms clock
286 this.clock.tick(250);
287
288 // Loop should see consecutive updates >= 5, call `waiting_`
289 assert.equal(this.player.tech_.hls.playbackWatcher_.consecutiveUpdates, 0,
290 'consecutiveUpdates reset');
291
292 // Playback watcher seeked to currentTime in `waiting_` to correct the `unknownwaiting`
293 assert.equal(seeks.length, 1, 'one seek');
294 assert.equal(seeks[0], 10, 'player seeked to currentTime');
295});
296
297QUnit.test('does not seek to current time when stuck near edge of buffered region',
298 function(assert) {
299 // set an arbitrary live source
300 this.player.src({
301 src: 'liveStart30sBefore.m3u8',
302 type: 'application/vnd.apple.mpegurl'
303 });
304
305 // start playback normally
306 this.player.tech_.triggerReady();
307 this.clock.tick(1);
308 standardXHRResponse(this.requests.shift());
309 openMediaSource(this.player, this.clock);
310 this.player.tech_.trigger('canplay');
311 this.player.tech_.trigger('play');
312 this.player.tech_.trigger('playing');
313 this.clock.tick(1);
314
315 this.player.currentTime(29.98);
316
317 let seeks = [];
318
319 this.player.tech_.setCurrentTime = (time) => {
320 seeks.push(time);
321 };
322
323 this.player.tech_.seeking = () => false;
324 this.player.tech_.buffered = () => videojs.createTimeRanges([[0, 30]]);
325 this.player.tech_.seekable = () => videojs.createTimeRanges([[0, 30]]);
326 this.player.tech_.paused = () => false;
327
328 // Playback watcher loop runs on a 250ms clock
329 this.clock.tick(250);
330
331 // Loop has run through once, `lastRecordedTime` should have been recorded
332 // and `consecutiveUpdates` set to 0 to begin count
333 assert.equal(this.player.tech_.hls.playbackWatcher_.lastRecordedTime, 29.98,
334 'Playback Watcher stored current time');
335 assert.equal(this.player.tech_.hls.playbackWatcher_.consecutiveUpdates, 0,
336 'consecutiveUpdates set to 0');
337
338 // Playback watcher loop runs on a 250ms clock
339 this.clock.tick(250);
340
341 // Loop has run through a second time, should detect that currentTime hasn't made
342 // progress while at the end of the buffer. Since the currentTime is at the end of the
343 // buffer, `consecutiveUpdates` should not be incremented
344 assert.equal(this.player.tech_.hls.playbackWatcher_.lastRecordedTime, 29.98,
345 'Playback Watcher stored current time');
346 assert.equal(this.player.tech_.hls.playbackWatcher_.consecutiveUpdates, 0,
347 'consecutiveUpdates should still be 0');
348
349 // no corrective seek
350 assert.equal(seeks.length, 0, 'no seek');
351 });
352
353QUnit.test('fires notifications when activated', function(assert) {
354 let buffered = [[]];
355 let seekable = [[]];
356 let currentTime = 0;
357 let hlsLiveResyncEvents = 0;
358 let hlsVideoUnderflowEvents = 0;
359 let playbackWatcher;
360
361 this.player.src({
362 src: 'liveStart30sBefore.m3u8',
363 type: 'application/vnd.apple.mpegurl'
364 });
365 this.player.tech_.triggerReady();
366 this.clock.tick(1);
367 this.player.tech_.currentTime = function() {
368 return currentTime;
369 };
370 this.player.tech_.buffered = function() {
371 return {
372 length: buffered.length,
373 start(i) {
374 return buffered[i][0];
375 },
376 end(i) {
377 return buffered[i][1];
378 }
379 };
380 };
381 playbackWatcher = this.player.tech_.hls.playbackWatcher_;
382 playbackWatcher.seekable = function() {
383 return {
384 length: seekable.length,
385 start(i) {
386 return seekable[i][0];
387 },
388 end(i) {
389 return seekable[i][1];
390 }
391 };
392 };
393 this.player.tech_.on('usage', (event) => {
394 if (event.name === 'hls-live-resync') {
395 hlsLiveResyncEvents++;
396 }
397 if (event.name === 'hls-video-underflow') {
398 hlsVideoUnderflowEvents++;
399 }
400 });
401
402 currentTime = 19;
403 seekable[0] = [20, 30];
404 playbackWatcher.waiting_();
405 assert.equal(hlsLiveResyncEvents, 1, 'triggered a liveresync event');
406
407 currentTime = 12;
408 seekable[0] = [0, 100];
409 buffered = [[0, 9], [10, 20]];
410 playbackWatcher.waiting_();
411 assert.equal(hlsVideoUnderflowEvents, 1, 'triggered a videounderflow event');
412 assert.equal(hlsLiveResyncEvents, 1, 'did not trigger an additional liveresync event');
413});
414
415QUnit.test('fixes bad seeks', function(assert) {
416 // set an arbitrary live source
417 this.player.src({
418 src: 'liveStart30sBefore.m3u8',
419 type: 'application/vnd.apple.mpegurl'
420 });
421
422 // start playback normally
423 this.player.tech_.triggerReady();
424 this.clock.tick(1);
425 standardXHRResponse(this.requests.shift());
426 openMediaSource(this.player, this.clock);
427 this.player.tech_.trigger('play');
428 this.player.tech_.trigger('playing');
429 this.clock.tick(1);
430
431 let playbackWatcher = this.player.tech_.hls.playbackWatcher_;
432 let seeks = [];
433 let seekable;
434 let seeking;
435 let currentTime;
436
437 playbackWatcher.seekable = () => seekable;
438 playbackWatcher.tech_ = {
439 off: () => {},
440 seeking: () => seeking,
441 setCurrentTime: (time) => {
442 seeks.push(time);
443 },
444 currentTime: () => currentTime
445 };
446
447 currentTime = 50;
448 seekable = videojs.createTimeRanges([[1, 45]]);
449 seeking = false;
450 assert.ok(!playbackWatcher.fixesBadSeeks_(), 'does nothing when not seeking');
451 assert.equal(seeks.length, 0, 'did not seek');
452
453 seeking = true;
454 assert.ok(playbackWatcher.fixesBadSeeks_(), 'acts when seek past seekable range');
455 assert.equal(seeks.length, 1, 'seeked');
456 assert.equal(seeks[0], 45, 'player seeked to live point');
457
458 currentTime = 0;
459 assert.ok(playbackWatcher.fixesBadSeeks_(), 'acts when seek before seekable range');
460 assert.equal(seeks.length, 2, 'seeked');
461 assert.equal(seeks[1], 1.1, 'player seeked to start of the live window');
462
463 currentTime = 30;
464 assert.ok(!playbackWatcher.fixesBadSeeks_(), 'does nothing when time within range');
465 assert.equal(seeks.length, 2, 'did not seek');
466});
467
468QUnit.test('corrects seek outside of seekable', function(assert) {
469 // set an arbitrary live source
470 this.player.src({
471 src: 'liveStart30sBefore.m3u8',
472 type: 'application/vnd.apple.mpegurl'
473 });
474
475 // start playback normally
476 this.player.tech_.triggerReady();
477 this.clock.tick(1);
478 standardXHRResponse(this.requests.shift());
479 openMediaSource(this.player, this.clock);
480 this.player.tech_.trigger('play');
481 this.player.tech_.trigger('playing');
482 this.clock.tick(1);
483
484 let playbackWatcher = this.player.tech_.hls.playbackWatcher_;
485 let seeks = [];
486 let seekable;
487 let seeking;
488 let currentTime;
489
490 playbackWatcher.seekable = () => seekable;
491 playbackWatcher.tech_ = {
492 off: () => {},
493 seeking: () => seeking,
494 setCurrentTime: (time) => {
495 seeks.push(time);
496 },
497 currentTime: () => currentTime,
498 // mocked out
499 paused: () => false,
500 buffered: () => videojs.createTimeRanges()
501 };
502
503 // waiting
504
505 currentTime = 50;
506 seekable = videojs.createTimeRanges([[1, 45]]);
507 seeking = true;
508 this.player.tech_.trigger('waiting');
509 assert.equal(seeks.length, 1, 'seeked');
510 assert.equal(seeks[0], 45, 'player seeked to live point');
511
512 currentTime = 0;
513 this.player.tech_.trigger('waiting');
514 assert.equal(seeks.length, 2, 'seeked');
515 assert.equal(seeks[1], 1.1, 'player seeked to start of the live window');
516
517 // inside of seekable range
518 currentTime = 10;
519 this.player.tech_.trigger('waiting');
520 assert.equal(seeks.length, 2, 'did not seek');
521
522 currentTime = 50;
523 // if we're not seeking, the case shouldn't be handled here
524 seeking = false;
525 this.player.tech_.trigger('waiting');
526 assert.equal(seeks.length, 2, 'did not seek');
527
528 // no check for 0 with seeking false because that should be handled by live falloff
529
530 // checkCurrentTime
531
532 seeking = true;
533 currentTime = 50;
534 playbackWatcher.checkCurrentTime_();
535 assert.equal(seeks.length, 3, 'seeked');
536 assert.equal(seeks[2], 45, 'player seeked to live point');
537
538 currentTime = 0;
539 playbackWatcher.checkCurrentTime_();
540 assert.equal(seeks.length, 4, 'seeked');
541 assert.equal(seeks[3], 1.1, 'player seeked to live point');
542
543 currentTime = 10;
544 playbackWatcher.checkCurrentTime_();
545 assert.equal(seeks.length, 4, 'did not seek');
546
547 seeking = false;
548 currentTime = 50;
549 playbackWatcher.checkCurrentTime_();
550 assert.equal(seeks.length, 4, 'did not seek');
551
552 currentTime = 0;
553 playbackWatcher.checkCurrentTime_();
554 assert.equal(seeks.length, 4, 'did not seek');
555});
556
557QUnit.test('calls fixesBadSeeks_ on seekablechanged', function(assert) {
558 // set an arbitrary live source
559 this.player.src({
560 src: 'liveStart30sBefore.m3u8',
561 type: 'application/vnd.apple.mpegurl'
562 });
563
564 // start playback normally
565 this.player.tech_.triggerReady();
566 this.clock.tick(1);
567 standardXHRResponse(this.requests.shift());
568 openMediaSource(this.player, this.clock);
569 this.player.tech_.trigger('play');
570 this.player.tech_.trigger('playing');
571 this.clock.tick(1);
572
573 let playbackWatcher = this.player.tech_.hls.playbackWatcher_;
574 let fixesBadSeeks_ = 0;
575
576 playbackWatcher.fixesBadSeeks_ = () => fixesBadSeeks_++;
577
578 this.player.tech_.trigger('seekablechanged');
579
580 assert.equal(fixesBadSeeks_, 1, 'fixesBadSeeks_ was called');
581});
582
583QUnit.module('PlaybackWatcher isolated functions', {
584 beforeEach() {
585 monitorCurrentTime_ = PlaybackWatcher.prototype.monitorCurrentTime_;
586 PlaybackWatcher.prototype.monitorCurrentTime_ = () => {};
587 this.playbackWatcher = new PlaybackWatcher({
588 tech: {
589 on: () => {},
590 off: () => {}
591 }
592 });
593 },
594 afterEach() {
595 this.playbackWatcher.dispose();
596 PlaybackWatcher.prototype.monitorCurrentTime_ = monitorCurrentTime_;
597 }
598});
599
600QUnit.test('skips gap from video underflow', function(assert) {
601 assert.equal(
602 this.playbackWatcher.gapFromVideoUnderflow_(videojs.createTimeRanges(), 0),
603 null,
604 'returns null when buffer is empty');
605 assert.equal(
606 this.playbackWatcher.gapFromVideoUnderflow_(videojs.createTimeRanges([[0, 10]]), 13),
607 null,
608 'returns null when there is only a previous buffer');
609 assert.equal(
610 this.playbackWatcher.gapFromVideoUnderflow_(
611 videojs.createTimeRanges([[0, 10], [10.1, 20]]), 15),
612 null,
613 'returns null when gap is too far from current time');
614 assert.equal(
615 this.playbackWatcher.gapFromVideoUnderflow_(
616 videojs.createTimeRanges([[0, 10], [10.1, 20]]), 9.9),
617 null,
618 'returns null when gap is after current time');
619 assert.equal(
620 this.playbackWatcher.gapFromVideoUnderflow_(
621 videojs.createTimeRanges([[0, 10.1], [10.2, 20]]), 12.1),
622 null,
623 'returns null when time is less than or equal to 2 seconds ahead');
624 assert.equal(
625 this.playbackWatcher.gapFromVideoUnderflow_(
626 videojs.createTimeRanges([[0, 10], [10.1, 20]]), 14.1),
627 null,
628 'returns null when time is greater than or equal to 4 seconds ahead');
629 assert.deepEqual(
630 this.playbackWatcher.gapFromVideoUnderflow_(
631 videojs.createTimeRanges([[0, 10], [10.1, 20]]), 12.2),
632 {start: 10, end: 10.1},
633 'returns gap when gap is small and time is greater than 2 seconds ahead in a buffer');
634 assert.deepEqual(
635 this.playbackWatcher.gapFromVideoUnderflow_(
636 videojs.createTimeRanges([[0, 10], [10.1, 20]]), 13),
637 {start: 10, end: 10.1},
638 'returns gap when gap is small and time is 3 seconds ahead in a buffer');
639 assert.deepEqual(
640 this.playbackWatcher.gapFromVideoUnderflow_(
641 videojs.createTimeRanges([[0, 10], [10.1, 20]]), 13.9),
642 {start: 10, end: 10.1},
643 'returns gap when gap is small and time is less than 4 seconds ahead in a buffer');
644 // In a case where current time is outside of the buffered range, something odd must've
645 // happened, but we should still allow the player to try to continue from that spot.
646 assert.deepEqual(
647 this.playbackWatcher.gapFromVideoUnderflow_(
648 videojs.createTimeRanges([[0, 10], [10.1, 12.9]]), 13),
649 {start: 10, end: 10.1},
650 'returns gap even when current time is not in buffered range');
651});
652
653QUnit.test('detects live window falloff', function(assert) {
654 let beforeSeekableWindow_ =
655 this.playbackWatcher.beforeSeekableWindow_.bind(this.playbackWatcher);
656
657 assert.ok(
658 beforeSeekableWindow_(videojs.createTimeRanges([[11, 20]]), 10),
659 'true if playlist live and current time before seekable');
660
661 assert.ok(
662 !beforeSeekableWindow_(videojs.createTimeRanges([]), 10),
663 'false if no seekable range');
664 assert.ok(
665 !beforeSeekableWindow_(videojs.createTimeRanges([[0, 10]]), -1),
666 'false if seekable range starts at 0');
667 assert.ok(
668 !beforeSeekableWindow_(videojs.createTimeRanges([[11, 20]]), 11),
669 'false if current time at seekable start');
670 assert.ok(
671 !beforeSeekableWindow_(videojs.createTimeRanges([[11, 20]]), 20),
672 'false if current time at seekable end');
673 assert.ok(
674 !beforeSeekableWindow_(videojs.createTimeRanges([[11, 20]]), 15),
675 'false if current time within seekable range');
676 assert.ok(
677 !beforeSeekableWindow_(videojs.createTimeRanges([[11, 20]]), 21),
678 'false if current time past seekable range');
679 assert.ok(
680 beforeSeekableWindow_(videojs.createTimeRanges([[11, 20]]), 0),
681 'true if current time is 0 and earlier than seekable range');
682});
683
684QUnit.test('detects beyond seekable window', function(assert) {
685 let afterSeekableWindow_ =
686 this.playbackWatcher.afterSeekableWindow_.bind(this.playbackWatcher);
687
688 assert.ok(
689 !afterSeekableWindow_(videojs.createTimeRanges([[11, 20]]), 10.8),
690 'false if before seekable range');
691 assert.ok(
692 afterSeekableWindow_(videojs.createTimeRanges([[11, 20]]), 20.2),
693 'true if after seekable range');
694 assert.ok(
695 !afterSeekableWindow_(videojs.createTimeRanges([[11, 20]]), 10.9),
696 'false if within starting seekable range buffer');
697 assert.ok(
698 !afterSeekableWindow_(videojs.createTimeRanges([[11, 20]]), 20.1),
699 'false if within ending seekable range buffer');
700
701 assert.ok(
702 !afterSeekableWindow_(videojs.createTimeRanges(), 10),
703 'false if no seekable range');
704 assert.ok(
705 !afterSeekableWindow_(videojs.createTimeRanges([[0, 10]]), -0.2),
706 'false if current time is negative');
707 assert.ok(
708 !afterSeekableWindow_(videojs.createTimeRanges([[0, 10]]), 5),
709 'false if within seekable range');
710 assert.ok(
711 !afterSeekableWindow_(videojs.createTimeRanges([[0, 10]]), 0),
712 'false if within seekable range');
713 assert.ok(
714 !afterSeekableWindow_(videojs.createTimeRanges([[0, 10]]), 10),
715 'false if within seekable range');
716});