UNPKG

28 kBJavaScriptView Raw
1import Playlist from '../src/playlist';
2import PlaylistLoader from '../src/playlist-loader';
3import QUnit from 'qunit';
4import xhrFactory from '../src/xhr';
5import { useFakeEnvironment } from './test-helpers';
6
7QUnit.module('Playlist Duration');
8
9QUnit.test('total duration for live playlists is Infinity', function(assert) {
10 let duration = Playlist.duration({
11 segments: [{
12 duration: 4,
13 uri: '0.ts'
14 }]
15 });
16
17 assert.equal(duration, Infinity, 'duration is infinity');
18});
19
20QUnit.module('Playlist Interval Duration');
21
22QUnit.test('accounts for non-zero starting VOD media sequences', function(assert) {
23 let duration = Playlist.duration({
24 mediaSequence: 10,
25 endList: true,
26 segments: [{
27 duration: 10,
28 uri: '0.ts'
29 }, {
30 duration: 10,
31 uri: '1.ts'
32 }, {
33 duration: 10,
34 uri: '2.ts'
35 }, {
36 duration: 10,
37 uri: '3.ts'
38 }]
39 });
40
41 assert.equal(duration, 4 * 10, 'includes only listed segments');
42});
43
44QUnit.test('uses timeline values when available', function(assert) {
45 let duration = Playlist.duration({
46 mediaSequence: 0,
47 endList: true,
48 segments: [{
49 start: 0,
50 uri: '0.ts'
51 }, {
52 duration: 10,
53 end: 2 * 10 + 2,
54 uri: '1.ts'
55 }, {
56 duration: 10,
57 end: 3 * 10 + 2,
58 uri: '2.ts'
59 }, {
60 duration: 10,
61 end: 4 * 10 + 2,
62 uri: '3.ts'
63 }]
64 }, 4);
65
66 assert.equal(duration, 4 * 10 + 2, 'used timeline values');
67});
68
69QUnit.test('works when partial timeline information is available', function(assert) {
70 let duration = Playlist.duration({
71 mediaSequence: 0,
72 endList: true,
73 segments: [{
74 start: 0,
75 uri: '0.ts'
76 }, {
77 duration: 9,
78 uri: '1.ts'
79 }, {
80 duration: 10,
81 uri: '2.ts'
82 }, {
83 duration: 10,
84 start: 30.007,
85 end: 40.002,
86 uri: '3.ts'
87 }, {
88 duration: 10,
89 end: 50.0002,
90 uri: '4.ts'
91 }]
92 }, 5);
93
94 assert.equal(duration, 50.0002, 'calculated with mixed intervals');
95});
96
97QUnit.test('uses timeline values for the expired duration of live playlists',
98function(assert) {
99 let playlist = {
100 mediaSequence: 12,
101 segments: [{
102 duration: 10,
103 end: 120.5,
104 uri: '0.ts'
105 }, {
106 duration: 9,
107 uri: '1.ts'
108 }]
109 };
110 let duration;
111
112 duration = Playlist.duration(playlist, playlist.mediaSequence);
113 assert.equal(duration, 110.5, 'used segment end time');
114 duration = Playlist.duration(playlist, playlist.mediaSequence + 1);
115 assert.equal(duration, 120.5, 'used segment end time');
116 duration = Playlist.duration(playlist, playlist.mediaSequence + 2);
117 assert.equal(duration, 120.5 + 9, 'used segment end time');
118});
119
120QUnit.test('looks outside the queried interval for live playlist timeline values',
121function(assert) {
122 let playlist = {
123 mediaSequence: 12,
124 segments: [{
125 duration: 10,
126 uri: '0.ts'
127 }, {
128 duration: 9,
129 end: 120.5,
130 uri: '1.ts'
131 }]
132 };
133 let duration;
134
135 duration = Playlist.duration(playlist, playlist.mediaSequence);
136 assert.equal(duration, 120.5 - 9 - 10, 'used segment end time');
137});
138
139QUnit.test('ignores discontinuity sequences later than the end', function(assert) {
140 let duration = Playlist.duration({
141 mediaSequence: 0,
142 discontinuityStarts: [1, 3],
143 segments: [{
144 duration: 10,
145 uri: '0.ts'
146 }, {
147 discontinuity: true,
148 duration: 9,
149 uri: '1.ts'
150 }, {
151 duration: 10,
152 uri: '2.ts'
153 }, {
154 discontinuity: true,
155 duration: 10,
156 uri: '3.ts'
157 }]
158 }, 2);
159
160 assert.equal(duration, 19, 'excluded the later segments');
161});
162
163QUnit.test('handles trailing segments without timeline information', function(assert) {
164 let duration;
165 let playlist = {
166 mediaSequence: 0,
167 endList: true,
168 segments: [{
169 start: 0,
170 end: 10.5,
171 uri: '0.ts'
172 }, {
173 duration: 9,
174 uri: '1.ts'
175 }, {
176 duration: 10,
177 uri: '2.ts'
178 }, {
179 start: 29.45,
180 end: 39.5,
181 uri: '3.ts'
182 }]
183 };
184
185 duration = Playlist.duration(playlist, 3);
186 assert.equal(duration, 29.45, 'calculated duration');
187
188 duration = Playlist.duration(playlist, 2);
189 assert.equal(duration, 19.5, 'calculated duration');
190});
191
192QUnit.test('uses timeline intervals when segments have them', function(assert) {
193 let duration;
194 let playlist = {
195 mediaSequence: 0,
196 segments: [{
197 start: 0,
198 end: 10,
199 uri: '0.ts'
200 }, {
201 duration: 9,
202 uri: '1.ts'
203 }, {
204 start: 20.1,
205 end: 30.1,
206 duration: 10,
207 uri: '2.ts'
208 }]
209 };
210
211 duration = Playlist.duration(playlist, 2);
212 assert.equal(duration, 20.1, 'used the timeline-based interval');
213
214 duration = Playlist.duration(playlist, 3);
215 assert.equal(duration, 30.1, 'used the timeline-based interval');
216});
217
218QUnit.test('counts the time between segments as part of the earlier segment\'s duration',
219function(assert) {
220 let duration = Playlist.duration({
221 mediaSequence: 0,
222 endList: true,
223 segments: [{
224 start: 0,
225 end: 10,
226 uri: '0.ts'
227 }, {
228 start: 10.1,
229 end: 20.1,
230 duration: 10,
231 uri: '1.ts'
232 }]
233 }, 1);
234
235 assert.equal(duration, 10.1, 'included the segment gap');
236});
237
238QUnit.test('accounts for discontinuities', function(assert) {
239 let duration = Playlist.duration({
240 mediaSequence: 0,
241 endList: true,
242 discontinuityStarts: [1],
243 segments: [{
244 duration: 10,
245 uri: '0.ts'
246 }, {
247 discontinuity: true,
248 duration: 10,
249 uri: '1.ts'
250 }]
251 }, 2);
252
253 assert.equal(duration, 10 + 10, 'handles discontinuities');
254});
255
256QUnit.test('a non-positive length interval has zero duration', function(assert) {
257 let playlist = {
258 mediaSequence: 0,
259 discontinuityStarts: [1],
260 segments: [{
261 duration: 10,
262 uri: '0.ts'
263 }, {
264 discontinuity: true,
265 duration: 10,
266 uri: '1.ts'
267 }]
268 };
269
270 assert.equal(Playlist.duration(playlist, 0), 0, 'zero-length duration is zero');
271 assert.equal(Playlist.duration(playlist, 0, false), 0, 'zero-length duration is zero');
272 assert.equal(Playlist.duration(playlist, -1), 0, 'negative length duration is zero');
273});
274
275QUnit.module('Playlist Seekable');
276
277QUnit.test('calculates seekable time ranges from available segments', function(assert) {
278 let playlist = {
279 mediaSequence: 0,
280 segments: [{
281 duration: 10,
282 uri: '0.ts'
283 }, {
284 duration: 10,
285 uri: '1.ts'
286 }],
287 endList: true
288 };
289 let seekable = Playlist.seekable(playlist);
290
291 assert.equal(seekable.length, 1, 'there are seekable ranges');
292 assert.equal(seekable.start(0), 0, 'starts at zero');
293 assert.equal(seekable.end(0), Playlist.duration(playlist), 'ends at the duration');
294});
295
296QUnit.test('calculates playlist end time from the available segments', function(assert) {
297 let playlistEnd = Playlist.playlistEnd({
298 mediaSequence: 0,
299 segments: [{
300 duration: 10,
301 uri: '0.ts'
302 }, {
303 duration: 10,
304 uri: '1.ts'
305 }],
306 endList: true
307 });
308
309 assert.equal(playlistEnd, 20, 'paylist end at the duration');
310});
311
312QUnit.test('master playlists have empty seekable ranges and no playlist end',
313function(assert) {
314 let playlist = {
315 playlists: [{
316 uri: 'low.m3u8'
317 }, {
318 uri: 'high.m3u8'
319 }]
320 };
321 let seekable = Playlist.seekable(playlist);
322 let playlistEnd = Playlist.playlistEnd(playlist);
323
324 assert.equal(seekable.length, 0, 'no seekable ranges from a master playlist');
325 assert.equal(playlistEnd, null, 'no playlist end from a master playlist');
326});
327
328QUnit.test('seekable end is three target durations from the actual end of live playlists',
329function(assert) {
330 let seekable = Playlist.seekable({
331 mediaSequence: 0,
332 syncInfo: {
333 time: 0,
334 mediaSequence: 0
335 },
336 targetDuration: 10,
337 segments: [{
338 duration: 7,
339 uri: '0.ts'
340 }, {
341 duration: 10,
342 uri: '1.ts'
343 }, {
344 duration: 10,
345 uri: '2.ts'
346 }, {
347 duration: 10,
348 uri: '3.ts'
349 }]
350 });
351
352 assert.equal(seekable.length, 1, 'there are seekable ranges');
353 assert.equal(seekable.start(0), 0, 'starts at zero');
354 assert.equal(seekable.end(0), 7, 'ends three target durations from the last segment');
355});
356
357QUnit.test('seekable end and playlist end account for non-standard target durations',
358function(assert) {
359 const playlist = {
360 targetDuration: 2,
361 mediaSequence: 0,
362 syncInfo: {
363 time: 0,
364 mediaSequence: 0
365 },
366 segments: [{
367 duration: 2,
368 uri: '0.ts'
369 }, {
370 duration: 2,
371 uri: '1.ts'
372 }, {
373 duration: 1,
374 uri: '2.ts'
375 }, {
376 duration: 2,
377 uri: '3.ts'
378 }, {
379 duration: 2,
380 uri: '4.ts'
381 }]
382 };
383 let seekable = Playlist.seekable(playlist);
384 let playlistEnd = Playlist.playlistEnd(playlist);
385
386 assert.equal(seekable.start(0), 0, 'starts at the earliest available segment');
387 assert.equal(seekable.end(0),
388 // Playlist duration is 9s. Target duration 2s. Seekable end should be at
389 // least 6s from end. Adding segment durations starting from the end to get
390 // that 6s target
391 9 - (2 + 2 + 1 + 2),
392 'allows seeking no further than the start of the segment 2 target' +
393 'durations back from the beginning of the last segment');
394 assert.equal(playlistEnd, 9, 'playlist end at the last segment');
395});
396
397QUnit.test('safeLiveIndex is correct for standard segment durations', function(assert) {
398 const playlist = {
399 targetDuration: 6,
400 mediaSequence: 10,
401 syncInfo: {
402 time: 0,
403 mediaSequence: 10
404 },
405 segments: [
406 {
407 duration: 6
408 },
409 {
410 duration: 6
411 },
412 {
413 duration: 6
414 },
415 {
416 duration: 6
417 },
418 {
419 duration: 6
420 },
421 {
422 duration: 6
423 }
424 ]
425 };
426
427 assert.equal(Playlist.safeLiveIndex(playlist), 3,
428 'correct media index for standard durations');
429});
430
431QUnit.test('safeLiveIndex is correct for variable segment durations', function(assert) {
432 const playlist = {
433 targetDuration: 6,
434 mediaSequence: 10,
435 syncInfo: {
436 time: 0,
437 mediaSequence: 10
438 },
439 segments: [
440 {
441 duration: 6
442 },
443 {
444 duration: 4
445 },
446 {
447 duration: 5
448 },
449 {
450 // this segment is 16 seconds from the end of playlist, the safe live point
451 duration: 6
452 },
453 {
454 duration: 3
455 },
456 {
457 duration: 4
458 },
459 {
460 duration: 3
461 }
462 ]
463 };
464
465 // safe live point is no less than 15 seconds (3s + 2 * 6s) from the end of the playlist
466 assert.equal(Playlist.safeLiveIndex(playlist), 3,
467 'correct media index for variable segment durations');
468});
469
470QUnit.test('safeLiveIndex is 0 when no safe live point', function(assert) {
471 const playlist = {
472 targetDuration: 6,
473 mediaSequence: 10,
474 syncInfo: {
475 time: 0,
476 mediaSequence: 10
477 },
478 segments: [
479 {
480 duration: 6
481 },
482 {
483 duration: 3
484 },
485 {
486 duration: 3
487 }
488 ]
489 };
490
491 assert.equal(Playlist.safeLiveIndex(playlist), 0,
492 'returns media index 0 when playlist has no safe live point');
493});
494
495QUnit.test(
496 'seekable end and playlist end account for non-zero starting VOD media sequence',
497function(assert) {
498 let playlist = {
499 targetDuration: 2,
500 mediaSequence: 5,
501 endList: true,
502 segments: [{
503 duration: 2,
504 uri: '0.ts'
505 }, {
506 duration: 2,
507 uri: '1.ts'
508 }, {
509 duration: 1,
510 uri: '2.ts'
511 }, {
512 duration: 2,
513 uri: '3.ts'
514 }, {
515 duration: 2,
516 uri: '4.ts'
517 }]
518 };
519 let seekable = Playlist.seekable(playlist);
520 let playlistEnd = Playlist.playlistEnd(playlist);
521
522 assert.equal(seekable.start(0), 0, 'starts at the earliest available segment');
523 assert.equal(seekable.end(0), 9, 'seekable end is same as duration');
524 assert.equal(playlistEnd, 9, 'playlist end at the last segment');
525});
526
527QUnit.test('playlist with no sync points has empty seekable range and empty playlist end',
528function(assert) {
529 let playlist = {
530 targetDuration: 10,
531 mediaSequence: 0,
532 segments: [{
533 duration: 7,
534 uri: '0.ts'
535 }, {
536 duration: 10,
537 uri: '1.ts'
538 }, {
539 duration: 10,
540 uri: '2.ts'
541 }, {
542 duration: 10,
543 uri: '3.ts'
544 }]
545 };
546
547 // seekable and playlistEnd take an optional expired parameter that is from
548 // SyncController.getExpiredTime which returns null when there is no sync point, so
549 // this test passes in null to simulate no sync points
550 let seekable = Playlist.seekable(playlist, null);
551 let playlistEnd = Playlist.playlistEnd(playlist, null);
552
553 assert.equal(seekable.length, 0, 'no seekable range for playlist with no sync points');
554 assert.equal(playlistEnd, null, 'no playlist end for playlist with no sync points');
555});
556
557QUnit.test('seekable and playlistEnd use available sync points for calculating',
558 function(assert) {
559 let playlist = {
560 targetDuration: 10,
561 mediaSequence: 100,
562 syncInfo: {
563 time: 50,
564 mediaSequence: 95
565 },
566 segments: [
567 {
568 duration: 10,
569 uri: '0.ts'
570 },
571 {
572 duration: 10,
573 uri: '1.ts'
574 },
575 {
576 duration: 10,
577 uri: '2.ts'
578 },
579 {
580 duration: 10,
581 uri: '3.ts'
582 },
583 {
584 duration: 10,
585 uri: '4.ts'
586 }
587 ]
588 };
589
590 // getExpiredTime would return 100 for this playlist
591 let seekable = Playlist.seekable(playlist, 100);
592 let playlistEnd = Playlist.playlistEnd(playlist, 100);
593
594 assert.ok(seekable.length, 'seekable range calculated');
595 assert.equal(seekable.start(0),
596 100,
597 'estimated start time based on expired sync point');
598 assert.equal(seekable.end(0),
599 120,
600 'allows seeking no further than three segments from the end');
601 assert.equal(playlistEnd, 150, 'playlist end at the last segment end');
602
603 playlist = {
604 targetDuration: 10,
605 mediaSequence: 100,
606 segments: [
607 {
608 duration: 10,
609 uri: '0.ts'
610 },
611 {
612 duration: 10,
613 uri: '1.ts',
614 start: 108.5,
615 end: 118.4
616 },
617 {
618 duration: 10,
619 uri: '2.ts'
620 },
621 {
622 duration: 10,
623 uri: '3.ts'
624 },
625 {
626 duration: 10,
627 uri: '4.ts'
628 }
629 ]
630 };
631
632 // getExpiredTime would return 98.5
633 seekable = Playlist.seekable(playlist, 98.5);
634 playlistEnd = Playlist.playlistEnd(playlist, 98.5);
635
636 assert.ok(seekable.length, 'seekable range calculated');
637 assert.equal(seekable.start(0), 98.5, 'estimated start time using segmentSync');
638 assert.equal(seekable.end(0),
639 118.4,
640 'allows seeking no further than three segments from the end');
641 assert.equal(playlistEnd, 148.4, 'playlist end at the last segment end');
642
643 playlist = {
644 targetDuration: 10,
645 mediaSequence: 100,
646 syncInfo: {
647 time: 50,
648 mediaSequence: 95
649 },
650 segments: [
651 {
652 duration: 10,
653 uri: '0.ts'
654 },
655 {
656 duration: 10,
657 uri: '1.ts',
658 start: 108.5,
659 end: 118.5
660 },
661 {
662 duration: 10,
663 uri: '2.ts'
664 },
665 {
666 duration: 10,
667 uri: '3.ts'
668 },
669 {
670 duration: 10,
671 uri: '4.ts'
672 }
673 ]
674 };
675
676 // getExpiredTime would return 98.5
677 seekable = Playlist.seekable(playlist, 98.5);
678 playlistEnd = Playlist.playlistEnd(playlist, 98.5);
679
680 assert.ok(seekable.length, 'seekable range calculated');
681 assert.equal(
682 seekable.start(0),
683 98.5,
684 'estimated start time using nearest sync point (segmentSync in this case)');
685 assert.equal(seekable.end(0),
686 118.5,
687 'allows seeking no further than three segments from the end');
688 assert.equal(playlistEnd, 148.5, 'playlist end at the last segment end');
689
690 playlist = {
691 targetDuration: 10,
692 mediaSequence: 100,
693 syncInfo: {
694 time: 90.8,
695 mediaSequence: 99
696 },
697 segments: [
698 {
699 duration: 10,
700 uri: '0.ts'
701 },
702 {
703 duration: 10,
704 uri: '1.ts'
705 },
706 {
707 duration: 10,
708 uri: '2.ts',
709 start: 118.5,
710 end: 128.5
711 },
712 {
713 duration: 10,
714 uri: '3.ts'
715 },
716 {
717 duration: 10,
718 uri: '4.ts'
719 }
720 ]
721 };
722
723 // getExpiredTime would return 100.8
724 seekable = Playlist.seekable(playlist, 100.8);
725 playlistEnd = Playlist.playlistEnd(playlist, 100.8);
726
727 assert.ok(seekable.length, 'seekable range calculated');
728 assert.equal(
729 seekable.start(0),
730 100.8,
731 'estimated start time using nearest sync point (expiredSync in this case)');
732 assert.equal(seekable.end(0),
733 118.5,
734 'allows seeking no further than three segments from the end');
735 assert.equal(playlistEnd, 148.5, 'playlist end at the last segment end');
736 });
737
738QUnit.module('Playlist hasAttribute');
739
740QUnit.test('correctly checks for existence of playlist attribute', function(assert) {
741 const playlist = {};
742
743 assert.notOk(Playlist.hasAttribute('BANDWIDTH', playlist),
744 'false for playlist with no attributes property');
745
746 playlist.attributes = {};
747
748 assert.notOk(Playlist.hasAttribute('BANDWIDTH', playlist),
749 'false for playlist with without specified attribute');
750
751 playlist.attributes.BANDWIDTH = 100;
752
753 assert.ok(Playlist.hasAttribute('BANDWIDTH', playlist),
754 'true for playlist with specified attribute');
755});
756
757QUnit.module('Playlist estimateSegmentRequestTime');
758
759QUnit.test('estimates segment request time based on bandwidth', function(assert) {
760 let segmentDuration = 10;
761 let bandwidth = 100;
762 let playlist = { attributes: { } };
763 let bytesReceived = 0;
764
765 let estimate = Playlist.estimateSegmentRequestTime(segmentDuration,
766 bandwidth,
767 playlist,
768 bytesReceived);
769
770 assert.ok(isNaN(estimate), 'returns NaN when no BANDWIDTH information on playlist');
771
772 playlist.attributes.BANDWIDTH = 100;
773
774 estimate = Playlist.estimateSegmentRequestTime(segmentDuration,
775 bandwidth,
776 playlist,
777 bytesReceived);
778
779 assert.equal(estimate, 10, 'calculated estimated download time');
780
781 bytesReceived = 25;
782
783 estimate = Playlist.estimateSegmentRequestTime(segmentDuration,
784 bandwidth,
785 playlist,
786 bytesReceived);
787
788 assert.equal(estimate, 8, 'takes into account bytes already received from download');
789});
790
791QUnit.module('Playlist enabled states', {
792 beforeEach(assert) {
793 this.env = useFakeEnvironment(assert);
794 this.clock = this.env.clock;
795 },
796 afterEach() {
797 this.env.restore();
798 }
799});
800
801QUnit.test('determines if a playlist is incompatible', function(assert) {
802 // incompatible means that the playlist was blacklisted due to incompatible
803 // configuration e.g. audio only stream when trying to playback audio and video.
804 // incompaatibility is denoted by a blacklist of Infinity.
805 assert.notOk(Playlist.isIncompatible({}),
806 'playlist not incompatible if no excludeUntil');
807
808 assert.notOk(Playlist.isIncompatible({ excludeUntil: 1 }),
809 'playlist not incompatible if expired blacklist');
810
811 assert.notOk(Playlist.isIncompatible({ excludeUntil: Date.now() + 9999 }),
812 'playlist not incompatible if temporarily blacklisted');
813
814 assert.ok(Playlist.isIncompatible({ excludeUntil: Infinity }),
815 'playlist is incompatible if excludeUntil is Infinity');
816});
817
818QUnit.test('determines if a playlist is blacklisted', function(assert) {
819 assert.notOk(Playlist.isBlacklisted({}),
820 'playlist not blacklisted if no excludeUntil');
821
822 assert.notOk(Playlist.isBlacklisted({ excludeUntil: Date.now() - 1 }),
823 'playlist not blacklisted if expired excludeUntil');
824
825 assert.ok(Playlist.isBlacklisted({ excludeUntil: Date.now() + 9999 }),
826 'playlist is blacklisted');
827
828 assert.ok(Playlist.isBlacklisted({ excludeUntil: Infinity }),
829 'playlist is blacklisted if excludeUntil is Infinity');
830});
831
832QUnit.test('determines if a playlist is disabled', function(assert) {
833 assert.notOk(Playlist.isDisabled({}), 'playlist not disabled');
834
835 assert.ok(Playlist.isDisabled({ disabled: true }), 'playlist is disabled');
836});
837
838QUnit.test('playlists with no or expired blacklist are enabled', function(assert) {
839 // enabled means not blacklisted and not disabled
840 assert.ok(Playlist.isEnabled({}), 'playlist with no blacklist is enabled');
841 assert.ok(Playlist.isEnabled({ excludeUntil: Date.now() - 1 }),
842 'playlist with expired blacklist is enabled');
843});
844
845QUnit.test('blacklisted playlists are not enabled', function(assert) {
846 // enabled means not blacklisted and not disabled
847 assert.notOk(Playlist.isEnabled({ excludeUntil: Date.now() + 9999 }),
848 'playlist with temporary blacklist is not enabled');
849 assert.notOk(Playlist.isEnabled({ excludeUntil: Infinity }),
850 'playlist with permanent is not enabled');
851});
852
853QUnit.test('manually disabled playlists are not enabled regardless of blacklist state',
854function(assert) {
855 // enabled means not blacklisted and not disabled
856 assert.notOk(Playlist.isEnabled({ disabled: true }),
857 'disabled playlist with no blacklist is not enabled');
858 assert.notOk(Playlist.isEnabled({ disabled: true, excludeUntil: Date.now() - 1 }),
859 'disabled playlist with expired blacklist is not enabled');
860 assert.notOk(Playlist.isEnabled({ disabled: true, excludeUntil: Date.now() + 9999 }),
861 'disabled playlist with temporary blacklist is not enabled');
862 assert.notOk(Playlist.isEnabled({ disabled: true, excludeUntil: Infinity }),
863 'disabled playlist with permanent blacklist is not enabled');
864});
865
866QUnit.module('Playlist isAes and isFmp4', {
867 beforeEach(assert) {
868 this.env = useFakeEnvironment(assert);
869 this.clock = this.env.clock;
870 this.requests = this.env.requests;
871 this.fakeHls = {
872 xhr: xhrFactory()
873 };
874 },
875 afterEach() {
876 this.env.restore();
877 }
878});
879
880QUnit.test('determine if playlist is an AES encrypted HLS stream', function(assert) {
881 let media;
882 let loader = new PlaylistLoader('media.m3u8', this.fakeHls);
883
884 loader.load();
885 this.requests.shift().respond(
886 200,
887 null,
888 '#EXTM3U\n' +
889 '#EXT-X-TARGETDURATION:15\n' +
890 '#EXT-X-KEY:METHOD=AES-128,URI="http://example.com/keys/key.php"\n' +
891 '#EXTINF:2.833,\n' +
892 'http://example.com/000001.ts\n' +
893 '#EXT-X-ENDLIST\n'
894 );
895
896 media = loader.media();
897
898 assert.ok(Playlist.isAes(media), 'media is an AES encrypted HLS stream');
899});
900
901QUnit.test('determine if playlist contains an fmp4 segment', function(assert) {
902 let media;
903 let loader = new PlaylistLoader('video/fmp4.m3u8', this.fakeHls);
904
905 loader.load();
906 this.requests.shift().respond(200, null,
907 '#EXTM3U\n' +
908 '#EXT-X-MAP:URI="main.mp4",BYTERANGE="720@0"\n' +
909 '#EXTINF:10,\n' +
910 '0.mp4\n' +
911 '#EXT-X-ENDLIST\n');
912
913 media = loader.media();
914
915 assert.ok(Playlist.isFmp4(media), 'media contains fmp4 segment');
916});
917
918QUnit.module('Playlist Media Index For Time', {
919 beforeEach(assert) {
920 this.env = useFakeEnvironment(assert);
921 this.clock = this.env.clock;
922 this.requests = this.env.requests;
923 this.fakeHls = {
924 xhr: xhrFactory()
925 };
926 },
927 afterEach() {
928 this.env.restore();
929 }
930});
931
932QUnit.test('can get media index by playback position for non-live videos',
933function(assert) {
934 let media;
935 let loader = new PlaylistLoader('media.m3u8', this.fakeHls);
936
937 loader.load();
938
939 this.requests.shift().respond(200, null,
940 '#EXTM3U\n' +
941 '#EXT-X-MEDIA-SEQUENCE:0\n' +
942 '#EXTINF:4,\n' +
943 '0.ts\n' +
944 '#EXTINF:5,\n' +
945 '1.ts\n' +
946 '#EXTINF:6,\n' +
947 '2.ts\n' +
948 '#EXT-X-ENDLIST\n'
949 );
950
951 media = loader.media();
952
953 assert.equal(Playlist.getMediaInfoForTime(media, -1, 0, 0).mediaIndex, 0,
954 'the index is never less than zero');
955 assert.equal(Playlist.getMediaInfoForTime(media, 0, 0, 0).mediaIndex, 0,
956 'time zero is index zero');
957 assert.equal(Playlist.getMediaInfoForTime(media, 3, 0, 0).mediaIndex, 0,
958 'time three is index zero');
959 assert.equal(Playlist.getMediaInfoForTime(media, 10, 0, 0).mediaIndex, 2,
960 'time 10 is index 2');
961 assert.equal(Playlist.getMediaInfoForTime(media, 22, 0, 0).mediaIndex, 2,
962 'time greater than the length is index 2');
963});
964
965QUnit.test('returns the lower index when calculating for a segment boundary',
966function(assert) {
967 let media;
968 let loader = new PlaylistLoader('media.m3u8', this.fakeHls);
969
970 loader.load();
971
972 this.requests.shift().respond(200, null,
973 '#EXTM3U\n' +
974 '#EXT-X-MEDIA-SEQUENCE:0\n' +
975 '#EXTINF:4,\n' +
976 '0.ts\n' +
977 '#EXTINF:5,\n' +
978 '1.ts\n' +
979 '#EXT-X-ENDLIST\n'
980 );
981
982 media = loader.media();
983
984 assert.equal(Playlist.getMediaInfoForTime(media, 4, 0, 0).mediaIndex, 0,
985 'rounds down exact matches');
986 assert.equal(Playlist.getMediaInfoForTime(media, 3.7, 0, 0).mediaIndex, 0,
987 'rounds down');
988 assert.equal(Playlist.getMediaInfoForTime(media, 4.5, 0, 0).mediaIndex, 1,
989 'rounds up at 0.5');
990});
991
992QUnit.test(
993'accounts for non-zero starting segment time when calculating media index',
994function(assert) {
995 let media;
996 let loader = new PlaylistLoader('media.m3u8', this.fakeHls);
997
998 loader.load();
999
1000 this.requests.shift().respond(200, null,
1001 '#EXTM3U\n' +
1002 '#EXT-X-MEDIA-SEQUENCE:1001\n' +
1003 '#EXTINF:4,\n' +
1004 '1001.ts\n' +
1005 '#EXTINF:5,\n' +
1006 '1002.ts\n'
1007 );
1008
1009 media = loader.media();
1010
1011 assert.equal(
1012 Playlist.getMediaInfoForTime(media, 45, 0, 150).mediaIndex,
1013 0,
1014 'expired content returns 0 for earliest segment available'
1015 );
1016 assert.equal(
1017 Playlist.getMediaInfoForTime(media, 75, 0, 150).mediaIndex,
1018 0,
1019 'expired content returns 0 for earliest segment available'
1020 );
1021 assert.equal(
1022 Playlist.getMediaInfoForTime(media, 0, 0, 150).mediaIndex,
1023 0,
1024 'time of 0 with no expired time returns first segment'
1025 );
1026 assert.equal(
1027 Playlist.getMediaInfoForTime(media, 50 + 100, 0, 150).mediaIndex,
1028 0,
1029 'calculates the earliest available position'
1030 );
1031 assert.equal(
1032 Playlist.getMediaInfoForTime(media, 50 + 100 + 2, 0, 150).mediaIndex,
1033 0,
1034 'calculates within the first segment'
1035 );
1036 assert.equal(
1037 Playlist.getMediaInfoForTime(media, 50 + 100 + 2, 0, 150).mediaIndex,
1038 0,
1039 'calculates within the first segment'
1040 );
1041 assert.equal(
1042 Playlist.getMediaInfoForTime(media, 50 + 100 + 4, 0, 150).mediaIndex,
1043 0,
1044 'calculates earlier segment on exact boundary match'
1045 );
1046 assert.equal(
1047 Playlist.getMediaInfoForTime(media, 50 + 100 + 4.5, 0, 150).mediaIndex,
1048 1,
1049 'calculates within the second segment'
1050 );
1051 assert.equal(
1052 Playlist.getMediaInfoForTime(media, 50 + 100 + 6, 0, 150).mediaIndex,
1053 1,
1054 'calculates within the second segment'
1055 );
1056
1057 assert.equal(
1058 Playlist.getMediaInfoForTime(media, 159, 0, 150).mediaIndex,
1059 1,
1060 'returns last segment when time is equal to end of last segment'
1061 );
1062 assert.equal(
1063 Playlist.getMediaInfoForTime(media, 160, 0, 150).mediaIndex,
1064 1,
1065 'returns last segment when time is past end of last segment'
1066 );
1067});