shaka-player 3.3.4 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/build-and-test.yaml +0 -1
- package/.github/workflows/demo-version-index.yaml +8 -0
- package/.github/workflows/release-please.yaml +23 -2
- package/.github/workflows/selenium-lab-tests.yaml +0 -1
- package/.github/workflows/sync-labels.yaml +33 -0
- package/.github/workflows/update-issues.yaml +29 -0
- package/.github/workflows/validate-pr-title.yaml +35 -0
- package/AUTHORS +1 -0
- package/CHANGELOG.md +198 -32
- package/CONTRIBUTORS +1 -0
- package/README.md +10 -7
- package/app-engine/demo-version-index/README.md +3 -3
- package/app-engine/demo-version-index/app.yaml +27 -1
- package/app-engine/demo-version-index/generate.py +164 -0
- package/app-engine/demo-version-index/main.py +4 -155
- package/app-engine/demo-version-index/requirements.txt +0 -1
- package/build/README.md +2 -2
- package/build/compiler.py +1 -1
- package/build/generateTsDefs.py +0 -2
- package/build/shaka-lab.yaml +0 -2
- package/build/test.py +7 -1
- package/build/types/core +0 -1
- package/build/types/polyfill +0 -3
- package/demo/common/asset.js +17 -1
- package/demo/common/assets.js +29 -0
- package/demo/common/message_ids.js +6 -3
- package/demo/config.js +10 -11
- package/demo/custom.js +39 -0
- package/demo/demo_utils.js +0 -1
- package/demo/locales/en.json +6 -3
- package/demo/locales/source.json +22 -10
- package/demo/main.js +9 -6
- package/demo/search.js +2 -1
- package/dist/controls.css +1 -1
- package/dist/demo.compiled.debug.js +71 -64
- package/dist/demo.compiled.debug.map +3 -3
- package/dist/demo.compiled.js +71 -64
- package/dist/demo.compiled.map +3 -3
- package/dist/demo.css +1 -1
- package/dist/demo.css.map +1 -1
- package/dist/deps.js +16 -20
- package/dist/locales.js +31 -29
- package/dist/receiver.compiled.debug.js +32 -28
- package/dist/receiver.compiled.debug.map +3 -3
- package/dist/receiver.compiled.js +32 -28
- package/dist/receiver.compiled.map +3 -3
- package/dist/shaka-player.compiled.d.ts +78 -83
- package/dist/shaka-player.compiled.debug.d.ts +78 -83
- package/dist/shaka-player.compiled.debug.externs.js +126 -126
- package/dist/shaka-player.compiled.debug.js +426 -483
- package/dist/shaka-player.compiled.debug.map +4 -4
- package/dist/shaka-player.compiled.externs.js +126 -126
- package/dist/shaka-player.compiled.js +838 -856
- package/dist/shaka-player.compiled.map +4 -4
- package/dist/shaka-player.ui.d.ts +80 -83
- package/dist/shaka-player.ui.debug.d.ts +80 -83
- package/dist/shaka-player.ui.debug.externs.js +134 -126
- package/dist/shaka-player.ui.debug.js +488 -543
- package/dist/shaka-player.ui.debug.map +4 -4
- package/dist/shaka-player.ui.externs.js +134 -126
- package/dist/shaka-player.ui.js +980 -994
- package/dist/shaka-player.ui.map +4 -4
- package/docs/tutorials/config.md +0 -2
- package/docs/tutorials/drm-config.md +5 -0
- package/docs/tutorials/fairplay.md +7 -22
- package/docs/tutorials/faq.md +0 -11
- package/docs/tutorials/index.json +2 -8
- package/docs/tutorials/plugins.md +1 -3
- package/docs/tutorials/upgrade.md +83 -11
- package/externs/polyfill.js +15 -0
- package/externs/shaka/manifest.js +5 -1
- package/externs/shaka/net.js +12 -0
- package/externs/shaka/offline.js +5 -1
- package/externs/shaka/player.js +39 -32
- package/externs/shaka/text.js +12 -12
- package/externs/sourcebuffer.js +15 -0
- package/lib/ads/client_side_ad.js +13 -2
- package/lib/ads/client_side_ad_manager.js +22 -4
- package/lib/cast/cast_utils.js +0 -1
- package/lib/dash/content_protection.js +85 -5
- package/lib/dash/dash_parser.js +1 -1
- package/lib/hls/hls_parser.js +378 -743
- package/lib/hls/manifest_text_parser.js +2 -3
- package/lib/media/drm_engine.js +102 -53
- package/lib/media/gap_jumping_controller.js +10 -45
- package/lib/media/media_source_engine.js +87 -15
- package/lib/media/playhead.js +2 -5
- package/lib/media/presentation_timeline.js +1 -2
- package/lib/media/segment_index.js +13 -46
- package/lib/media/segment_reference.js +47 -1
- package/lib/media/streaming_engine.js +29 -11
- package/lib/media/time_ranges_utils.js +4 -9
- package/lib/net/networking_engine.js +3 -0
- package/lib/offline/indexeddb/v1_storage_cell.js +1 -0
- package/lib/offline/indexeddb/v2_storage_cell.js +1 -0
- package/lib/offline/manifest_converter.js +1 -0
- package/lib/offline/storage.js +100 -132
- package/lib/player.js +17 -201
- package/lib/polyfill/media_capabilities.js +12 -6
- package/lib/polyfill/mediasource.js +0 -28
- package/lib/polyfill/patchedmediakeys_nop.js +2 -0
- package/lib/polyfill/patchedmediakeys_webkit.js +2 -0
- package/lib/text/cue.js +0 -6
- package/lib/text/lrc_text_parser.js +8 -0
- package/lib/text/mp4_ttml_parser.js +8 -0
- package/lib/text/mp4_vtt_parser.js +8 -0
- package/lib/text/sbv_text_parser.js +8 -0
- package/lib/text/simple_text_displayer.js +1 -7
- package/lib/text/srt_text_parser.js +8 -0
- package/lib/text/ssa_text_parser.js +8 -0
- package/lib/text/text_engine.js +19 -3
- package/lib/text/ttml_text_parser.js +8 -0
- package/lib/text/ui_text_displayer.js +1 -7
- package/lib/text/vtt_text_parser.js +24 -2
- package/lib/text/web_vtt_generator.js +1 -7
- package/lib/util/error.js +9 -13
- package/lib/util/fairplay_utils.js +109 -0
- package/lib/util/functional.js +0 -42
- package/lib/util/mime_utils.js +2 -0
- package/lib/util/platform.js +45 -0
- package/lib/util/player_configuration.js +12 -6
- package/lib/util/pssh.js +23 -4
- package/lib/util/stream_utils.js +92 -22
- package/lib/util/uint8array_utils.js +0 -16
- package/lib/util/xml_utils.js +1 -1
- package/package.json +1 -1
- package/roadmap.md +19 -19
- package/shaka-player.uncompiled.js +0 -3
- package/test/cast/cast_utils_unit.js +1 -0
- package/test/dash/dash_parser_content_protection_unit.js +44 -0
- package/test/dash/dash_parser_manifest_unit.js +0 -24
- package/test/dash/dash_parser_segment_template_unit.js +3 -4
- package/test/demo/demo_unit.js +2 -1
- package/test/hls/hls_live_unit.js +99 -220
- package/test/hls/hls_parser_unit.js +515 -432
- package/test/media/drm_engine_unit.js +86 -120
- package/test/media/media_source_engine_integration.js +26 -3
- package/test/media/media_source_engine_unit.js +7 -6
- package/test/media/playhead_unit.js +27 -191
- package/test/media/segment_index_unit.js +15 -25
- package/test/media/streaming_engine_integration.js +4 -39
- package/test/media/streaming_engine_unit.js +3 -2
- package/test/media/time_ranges_utils_unit.js +0 -5
- package/test/offline/manifest_convert_unit.js +34 -4
- package/test/offline/offline_manifest_parser_unit.js +1 -0
- package/test/offline/storage_integration.js +5 -4
- package/test/player_external.js +0 -6
- package/test/player_integration.js +16 -0
- package/test/player_unit.js +26 -0
- package/test/test/util/fake_media_source_engine.js +11 -4
- package/test/test/util/manifest_generator.js +3 -1
- package/test/test/util/manifest_parser_util.js +4 -1
- package/test/test/util/offline_utils.js +1 -0
- package/test/test/util/streaming_engine_util.js +1 -0
- package/test/test/util/test_scheme.js +24 -1
- package/test/test/util/util.js +4 -4
- package/test/text/cue_integration.js +2 -2
- package/test/text/lrc_text_parser_unit.js +6 -7
- package/test/text/mp4_ttml_parser_unit.js +6 -4
- package/test/text/mp4_vtt_parser_unit.js +6 -5
- package/test/text/sbv_text_parser_unit.js +5 -6
- package/test/text/srt_text_parser_unit.js +5 -6
- package/test/text/ssa_text_parser_unit.js +17 -29
- package/test/text/text_engine_unit.js +45 -5
- package/test/text/ttml_text_parser_unit.js +77 -77
- package/test/text/vtt_text_parser_unit.js +55 -57
- package/test/ui/ui_integration.js +0 -16
- package/test/ui/ui_unit.js +27 -0
- package/test/util/cmcd_manager_unit.js +3 -0
- package/test/util/stream_utils_unit.js +23 -7
- package/ui/controls.js +65 -40
- package/ui/fullscreen_button.js +26 -6
- package/ui/less/containers.less +3 -3
- package/ui/locales/en.json +1 -0
- package/ui/locales/source.json +5 -0
- package/ui/resolution_selection.js +31 -33
- package/ui/ui.js +0 -17
- package/docs/tutorials/upgrade-v2-5.md +0 -441
- package/docs/tutorials/upgrade-v3.md +0 -38
- package/lib/polyfill/mathround.js +0 -49
- package/lib/polyfill/patchedmediakeys_apple.js +0 -744
- package/lib/polyfill/patchedmediakeys_ms.js +0 -698
- package/lib/util/mutex.js +0 -52
- package/test/util/functional_unit.js +0 -82
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
|
|
7
7
|
describe('HlsParser', () => {
|
|
8
8
|
const ContentType = shaka.util.ManifestParserUtils.ContentType;
|
|
9
|
-
const ManifestParser = shaka.test.ManifestParser;
|
|
10
9
|
const TextStreamKind = shaka.util.ManifestParserUtils.TextStreamKind;
|
|
11
10
|
const Util = shaka.test.Util;
|
|
12
11
|
const originalAlwaysWarn = shaka.log.alwaysWarn;
|
|
@@ -156,6 +155,7 @@ describe('HlsParser', () => {
|
|
|
156
155
|
].join('');
|
|
157
156
|
|
|
158
157
|
const manifest = shaka.test.ManifestGenerator.generate((manifest) => {
|
|
158
|
+
manifest.sequenceMode = true;
|
|
159
159
|
manifest.anyTimeline();
|
|
160
160
|
manifest.addPartialVariant((variant) => {
|
|
161
161
|
variant.language = 'en';
|
|
@@ -230,6 +230,7 @@ describe('HlsParser', () => {
|
|
|
230
230
|
].join('');
|
|
231
231
|
|
|
232
232
|
const manifest = shaka.test.ManifestGenerator.generate((manifest) => {
|
|
233
|
+
manifest.sequenceMode = true;
|
|
233
234
|
manifest.anyTimeline();
|
|
234
235
|
manifest.addPartialVariant((variant) => {
|
|
235
236
|
variant.language = 'en';
|
|
@@ -300,6 +301,7 @@ describe('HlsParser', () => {
|
|
|
300
301
|
stream.size(960, 540);
|
|
301
302
|
});
|
|
302
303
|
});
|
|
304
|
+
manifest.sequenceMode = true;
|
|
303
305
|
});
|
|
304
306
|
|
|
305
307
|
fakeNetEngine
|
|
@@ -336,6 +338,7 @@ describe('HlsParser', () => {
|
|
|
336
338
|
stream.mime('video/mp4', 'avc1.4d001e');
|
|
337
339
|
});
|
|
338
340
|
});
|
|
341
|
+
manifest.sequenceMode = true;
|
|
339
342
|
});
|
|
340
343
|
|
|
341
344
|
await testHlsParser(master, media, manifest);
|
|
@@ -365,6 +368,7 @@ describe('HlsParser', () => {
|
|
|
365
368
|
stream.mime('video/mp4', 'avc1');
|
|
366
369
|
});
|
|
367
370
|
});
|
|
371
|
+
manifest.sequenceMode = true;
|
|
368
372
|
});
|
|
369
373
|
|
|
370
374
|
await testHlsParser(master, media, manifest);
|
|
@@ -393,6 +397,7 @@ describe('HlsParser', () => {
|
|
|
393
397
|
stream.mime('video/mp4', 'avc1');
|
|
394
398
|
});
|
|
395
399
|
});
|
|
400
|
+
manifest.sequenceMode = true;
|
|
396
401
|
});
|
|
397
402
|
|
|
398
403
|
await testHlsParser(master, media, manifest);
|
|
@@ -424,6 +429,7 @@ describe('HlsParser', () => {
|
|
|
424
429
|
stream.mime('video/mp4', 'avc1');
|
|
425
430
|
});
|
|
426
431
|
});
|
|
432
|
+
manifest.sequenceMode = true;
|
|
427
433
|
});
|
|
428
434
|
|
|
429
435
|
await testHlsParser(master, media, manifest);
|
|
@@ -452,6 +458,7 @@ describe('HlsParser', () => {
|
|
|
452
458
|
stream.mime('audio/mp4', 'mp4a');
|
|
453
459
|
});
|
|
454
460
|
});
|
|
461
|
+
manifest.sequenceMode = true;
|
|
455
462
|
});
|
|
456
463
|
|
|
457
464
|
await testHlsParser(master, media, manifest);
|
|
@@ -486,6 +493,7 @@ describe('HlsParser', () => {
|
|
|
486
493
|
stream.mime('audio/mp4', 'mp4a');
|
|
487
494
|
});
|
|
488
495
|
});
|
|
496
|
+
manifest.sequenceMode = true;
|
|
489
497
|
});
|
|
490
498
|
|
|
491
499
|
await testHlsParser(master, media, manifest);
|
|
@@ -520,6 +528,35 @@ describe('HlsParser', () => {
|
|
|
520
528
|
stream.mime('audio/mp4', '');
|
|
521
529
|
});
|
|
522
530
|
});
|
|
531
|
+
manifest.sequenceMode = true;
|
|
532
|
+
});
|
|
533
|
+
|
|
534
|
+
await testHlsParser(master, media, manifest);
|
|
535
|
+
});
|
|
536
|
+
|
|
537
|
+
it('accepts containerless streams', async () => {
|
|
538
|
+
const master = [
|
|
539
|
+
'#EXTM3U\n',
|
|
540
|
+
'#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=41457,CODECS="mp4a.40.2"\n',
|
|
541
|
+
'audio\n',
|
|
542
|
+
].join('');
|
|
543
|
+
|
|
544
|
+
const media = [
|
|
545
|
+
'#EXTM3U\n',
|
|
546
|
+
'#EXT-X-PLAYLIST-TYPE:VOD\n',
|
|
547
|
+
'#EXTINF:5,\n',
|
|
548
|
+
'#EXT-X-BYTERANGE:121090@616\n',
|
|
549
|
+
'main.aac',
|
|
550
|
+
].join('');
|
|
551
|
+
|
|
552
|
+
const manifest = shaka.test.ManifestGenerator.generate((manifest) => {
|
|
553
|
+
manifest.anyTimeline();
|
|
554
|
+
manifest.addPartialVariant((variant) => {
|
|
555
|
+
variant.addPartialStream(ContentType.AUDIO, (stream) => {
|
|
556
|
+
stream.mime('audio/aac', '');
|
|
557
|
+
});
|
|
558
|
+
});
|
|
559
|
+
manifest.sequenceMode = true;
|
|
523
560
|
});
|
|
524
561
|
|
|
525
562
|
await testHlsParser(master, media, manifest);
|
|
@@ -558,6 +595,7 @@ describe('HlsParser', () => {
|
|
|
558
595
|
stream.mime('audio/mp4', 'mp4a');
|
|
559
596
|
});
|
|
560
597
|
});
|
|
598
|
+
manifest.sequenceMode = true;
|
|
561
599
|
});
|
|
562
600
|
|
|
563
601
|
await testHlsParser(master, media, manifest);
|
|
@@ -594,6 +632,7 @@ describe('HlsParser', () => {
|
|
|
594
632
|
stream.mime('audio/mp4', 'mp4a');
|
|
595
633
|
});
|
|
596
634
|
});
|
|
635
|
+
manifest.sequenceMode = true;
|
|
597
636
|
});
|
|
598
637
|
|
|
599
638
|
await testHlsParser(master, media, manifest);
|
|
@@ -624,66 +663,12 @@ describe('HlsParser', () => {
|
|
|
624
663
|
stream.mime('audio/mp4', 'mp4a');
|
|
625
664
|
});
|
|
626
665
|
});
|
|
666
|
+
manifest.sequenceMode = true;
|
|
627
667
|
});
|
|
628
668
|
|
|
629
669
|
await testHlsParser(master, media, manifest);
|
|
630
670
|
});
|
|
631
671
|
|
|
632
|
-
it('sets seek range correctly for non-zero start', async () => {
|
|
633
|
-
const master = [
|
|
634
|
-
'#EXTM3U\n',
|
|
635
|
-
'#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="avc1",',
|
|
636
|
-
'RESOLUTION=960x540,FRAME-RATE=60\n',
|
|
637
|
-
'video',
|
|
638
|
-
].join('');
|
|
639
|
-
|
|
640
|
-
const media = [
|
|
641
|
-
'#EXTM3U\n',
|
|
642
|
-
'#EXT-X-PLAYLIST-TYPE:VOD\n',
|
|
643
|
-
'#EXT-X-MEDIA-SEQUENCE:131\n',
|
|
644
|
-
'#EXT-X-MAP:URI="init.mp4",BYTERANGE="616@0"\n',
|
|
645
|
-
'#EXTINF:5,\n',
|
|
646
|
-
'#EXT-X-BYTERANGE:121090@616\n',
|
|
647
|
-
'main.mp4',
|
|
648
|
-
].join('');
|
|
649
|
-
|
|
650
|
-
segmentData = new Uint8Array([
|
|
651
|
-
0x00, 0x00, 0x00, 0x24, // size (36)
|
|
652
|
-
0x6D, 0x6F, 0x6F, 0x66, // type (moof)
|
|
653
|
-
0x00, 0x00, 0x00, 0x1C, // traf size (28)
|
|
654
|
-
0x74, 0x72, 0x61, 0x66, // type (traf)
|
|
655
|
-
|
|
656
|
-
0x00, 0x00, 0x00, 0x14, // tfdt size (20)
|
|
657
|
-
0x74, 0x66, 0x64, 0x74, // type (tfdt)
|
|
658
|
-
0x01, 0x00, 0x00, 0x00, // version and flags
|
|
659
|
-
|
|
660
|
-
0x00, 0x00, 0x00, 0x00, // baseMediaDecodeTime first 4 bytes (0)
|
|
661
|
-
0x00, 0x0A, 0x00, 0x00, // baseMediaDecodeTime last 4 bytes (655360)
|
|
662
|
-
]);
|
|
663
|
-
|
|
664
|
-
fakeNetEngine
|
|
665
|
-
.setResponseText('test:/master', master)
|
|
666
|
-
.setResponseText('test:/video', media)
|
|
667
|
-
.setResponseValue('test:/init.mp4', initSegmentData)
|
|
668
|
-
.setResponseValue('test:/main.mp4', segmentData);
|
|
669
|
-
|
|
670
|
-
const manifest = await parser.start('test:/master', playerInterface);
|
|
671
|
-
const presentationTimeline = manifest.presentationTimeline;
|
|
672
|
-
const stream = manifest.variants[0].video;
|
|
673
|
-
await stream.createSegmentIndex();
|
|
674
|
-
goog.asserts.assert(stream.segmentIndex != null, 'Null segmentIndex!');
|
|
675
|
-
|
|
676
|
-
const ref = Array.from(stream.segmentIndex)[0];
|
|
677
|
-
expect(ref).not.toBe(null);
|
|
678
|
-
if (ref) {
|
|
679
|
-
expect(ref.startTime).toBe(0);
|
|
680
|
-
// baseMediaDecodeTime (655360) / timescale (1000)
|
|
681
|
-
expect(ref.timestampOffset).toBe(-655.36);
|
|
682
|
-
}
|
|
683
|
-
expect(presentationTimeline.getSeekRangeStart()).toBe(0);
|
|
684
|
-
expect(presentationTimeline.getSeekRangeEnd()).toBe(5);
|
|
685
|
-
});
|
|
686
|
-
|
|
687
672
|
it('parses multiplexed variant', async () => {
|
|
688
673
|
const master = [
|
|
689
674
|
'#EXTM3U\n',
|
|
@@ -708,6 +693,7 @@ describe('HlsParser', () => {
|
|
|
708
693
|
stream.mime('video/mp4', 'avc1,mp4a');
|
|
709
694
|
});
|
|
710
695
|
});
|
|
696
|
+
manifest.sequenceMode = true;
|
|
711
697
|
});
|
|
712
698
|
|
|
713
699
|
await testHlsParser(master, media, manifest);
|
|
@@ -737,6 +723,7 @@ describe('HlsParser', () => {
|
|
|
737
723
|
stream.mime('video/mp4', /** @type {?} */ (jasmine.any(String)));
|
|
738
724
|
});
|
|
739
725
|
});
|
|
726
|
+
manifest.sequenceMode = true;
|
|
740
727
|
});
|
|
741
728
|
|
|
742
729
|
await testHlsParser(master, media, manifest);
|
|
@@ -771,6 +758,7 @@ describe('HlsParser', () => {
|
|
|
771
758
|
stream.mime('audio/mp4', /** @type {?} */ (jasmine.any(String)));
|
|
772
759
|
});
|
|
773
760
|
});
|
|
761
|
+
manifest.sequenceMode = true;
|
|
774
762
|
});
|
|
775
763
|
|
|
776
764
|
await testHlsParser(master, media, manifest);
|
|
@@ -801,6 +789,7 @@ describe('HlsParser', () => {
|
|
|
801
789
|
stream.mime('video/mp4', /** @type {?} */ (jasmine.any(String)));
|
|
802
790
|
});
|
|
803
791
|
});
|
|
792
|
+
manifest.sequenceMode = true;
|
|
804
793
|
});
|
|
805
794
|
|
|
806
795
|
await testHlsParser(master, media, manifest);
|
|
@@ -831,6 +820,7 @@ describe('HlsParser', () => {
|
|
|
831
820
|
stream.mime('audio/mp4', /** @type {?} */ (jasmine.any(String)));
|
|
832
821
|
});
|
|
833
822
|
});
|
|
823
|
+
manifest.sequenceMode = true;
|
|
834
824
|
});
|
|
835
825
|
|
|
836
826
|
await testHlsParser(master, media, manifest);
|
|
@@ -882,6 +872,7 @@ describe('HlsParser', () => {
|
|
|
882
872
|
stream.language = 'fr';
|
|
883
873
|
});
|
|
884
874
|
});
|
|
875
|
+
manifest.sequenceMode = true;
|
|
885
876
|
});
|
|
886
877
|
|
|
887
878
|
await testHlsParser(master, media, manifest);
|
|
@@ -924,6 +915,7 @@ describe('HlsParser', () => {
|
|
|
924
915
|
stream.language = 'fr';
|
|
925
916
|
});
|
|
926
917
|
});
|
|
918
|
+
manifest.sequenceMode = true;
|
|
927
919
|
});
|
|
928
920
|
|
|
929
921
|
await testHlsParser(master, media, manifest);
|
|
@@ -971,63 +963,12 @@ describe('HlsParser', () => {
|
|
|
971
963
|
];
|
|
972
964
|
});
|
|
973
965
|
});
|
|
966
|
+
manifest.sequenceMode = true;
|
|
974
967
|
});
|
|
975
968
|
|
|
976
969
|
await testHlsParser(master, media, manifest);
|
|
977
970
|
});
|
|
978
971
|
|
|
979
|
-
it('fetch the start time for one audio/video stream and reuse for the others',
|
|
980
|
-
async () => {
|
|
981
|
-
const SEGMENT = shaka.net.NetworkingEngine.RequestType.SEGMENT;
|
|
982
|
-
const master = [
|
|
983
|
-
'#EXTM3U\n',
|
|
984
|
-
'#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aud1",LANGUAGE="eng",',
|
|
985
|
-
'CHANNELS="2",URI="audio"\n',
|
|
986
|
-
'#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="sub1",LANGUAGE="eng",',
|
|
987
|
-
'URI="text"\n',
|
|
988
|
-
'#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="avc1,mp4a",',
|
|
989
|
-
'RESOLUTION=960x540,FRAME-RATE=60,AUDIO="aud1"\n',
|
|
990
|
-
'video\n',
|
|
991
|
-
].join('');
|
|
992
|
-
|
|
993
|
-
const media = [
|
|
994
|
-
'#EXTM3U\n',
|
|
995
|
-
'#EXT-X-PLAYLIST-TYPE:VOD\n',
|
|
996
|
-
'#EXT-X-MAP:URI="init.mp4",BYTERANGE="616@0"\n',
|
|
997
|
-
'#EXTINF:5,\n',
|
|
998
|
-
'#EXT-X-BYTERANGE:121090@616\n',
|
|
999
|
-
'main.mp4',
|
|
1000
|
-
].join('');
|
|
1001
|
-
|
|
1002
|
-
const textMedia = [
|
|
1003
|
-
'#EXTM3U\n',
|
|
1004
|
-
'#EXT-X-PLAYLIST-TYPE:VOD\n',
|
|
1005
|
-
'#EXTINF:5,\n',
|
|
1006
|
-
'#EXT-X-BYTERANGE:121090@616\n',
|
|
1007
|
-
'main.vtt',
|
|
1008
|
-
].join('');
|
|
1009
|
-
|
|
1010
|
-
fakeNetEngine
|
|
1011
|
-
.setResponseText('test:/master', master)
|
|
1012
|
-
.setResponseText('test:/audio', media)
|
|
1013
|
-
.setResponseText('test:/video', media)
|
|
1014
|
-
.setResponseText('test:/text', textMedia)
|
|
1015
|
-
.setResponseText('test:/main.vtt', vttText)
|
|
1016
|
-
.setResponseValue('test:/init.mp4', initSegmentData)
|
|
1017
|
-
.setResponseValue('test:/main.mp4', segmentData);
|
|
1018
|
-
|
|
1019
|
-
await parser.start('test:/master', playerInterface);
|
|
1020
|
-
// The start time of audio should be fetched first, and then video and
|
|
1021
|
-
// text streams should reuse the start time from audio.
|
|
1022
|
-
// Thus, there should be 2 segment requests, for fetching audio init
|
|
1023
|
-
// and main segments, and not for video and text segments.
|
|
1024
|
-
expect(fakeNetEngine.request.calls.allArgs().filter((args) => {
|
|
1025
|
-
return args[0] == SEGMENT;
|
|
1026
|
-
}).length).toBe(2);
|
|
1027
|
-
fakeNetEngine.expectRequest('test:/init.mp4', SEGMENT);
|
|
1028
|
-
fakeNetEngine.expectRequest('test:/main.mp4', SEGMENT);
|
|
1029
|
-
});
|
|
1030
|
-
|
|
1031
972
|
it('gets mime type from header request', async () => {
|
|
1032
973
|
const master = [
|
|
1033
974
|
'#EXTM3U\n',
|
|
@@ -1052,6 +993,7 @@ describe('HlsParser', () => {
|
|
|
1052
993
|
stream.mime('video/mp4', 'avc1');
|
|
1053
994
|
});
|
|
1054
995
|
});
|
|
996
|
+
manifest.sequenceMode = true;
|
|
1055
997
|
});
|
|
1056
998
|
|
|
1057
999
|
// The extra parameters should be stripped by the parser.
|
|
@@ -1109,6 +1051,7 @@ describe('HlsParser', () => {
|
|
|
1109
1051
|
stream.kind = TextStreamKind.SUBTITLE;
|
|
1110
1052
|
stream.mime('text/vtt', '');
|
|
1111
1053
|
});
|
|
1054
|
+
manifest.sequenceMode = true;
|
|
1112
1055
|
});
|
|
1113
1056
|
|
|
1114
1057
|
fakeNetEngine
|
|
@@ -1178,6 +1121,7 @@ describe('HlsParser', () => {
|
|
|
1178
1121
|
stream.kind = TextStreamKind.SUBTITLE;
|
|
1179
1122
|
stream.mime('text/vtt', '');
|
|
1180
1123
|
});
|
|
1124
|
+
manifest.sequenceMode = true;
|
|
1181
1125
|
});
|
|
1182
1126
|
|
|
1183
1127
|
fakeNetEngine
|
|
@@ -1194,6 +1138,69 @@ describe('HlsParser', () => {
|
|
|
1194
1138
|
expect(actual).toEqual(manifest);
|
|
1195
1139
|
});
|
|
1196
1140
|
|
|
1141
|
+
it('gets mime type of SUBTITLES from header request', async () => {
|
|
1142
|
+
const master = [
|
|
1143
|
+
'#EXTM3U\n',
|
|
1144
|
+
'#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aud1",LANGUAGE="eng",',
|
|
1145
|
+
'URI="audio"\n',
|
|
1146
|
+
'#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="sub1",LANGUAGE="eng",',
|
|
1147
|
+
'URI="text"\n',
|
|
1148
|
+
'#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="avc1,mp4a",',
|
|
1149
|
+
'RESOLUTION=960x540,FRAME-RATE=60,AUDIO="aud1",SUBTITLES="sub1"\n',
|
|
1150
|
+
'video\n',
|
|
1151
|
+
].join('');
|
|
1152
|
+
|
|
1153
|
+
const media = [
|
|
1154
|
+
'#EXTM3U\n',
|
|
1155
|
+
'#EXT-X-PLAYLIST-TYPE:VOD\n',
|
|
1156
|
+
'#EXT-X-MAP:URI="init.mp4",BYTERANGE="616@0"\n',
|
|
1157
|
+
'#EXTINF:5,\n',
|
|
1158
|
+
'#EXT-X-BYTERANGE:121090@616\n',
|
|
1159
|
+
'main.mp4',
|
|
1160
|
+
].join('');
|
|
1161
|
+
|
|
1162
|
+
const textMedia = [
|
|
1163
|
+
'#EXTM3U\n',
|
|
1164
|
+
'#EXT-X-PLAYLIST-TYPE:VOD\n',
|
|
1165
|
+
'#EXTINF:5,\n',
|
|
1166
|
+
'#EXT-X-BYTERANGE:121090@616\n',
|
|
1167
|
+
'main.subs',
|
|
1168
|
+
].join('');
|
|
1169
|
+
|
|
1170
|
+
const manifest = shaka.test.ManifestGenerator.generate((manifest) => {
|
|
1171
|
+
manifest.anyTimeline();
|
|
1172
|
+
manifest.addPartialVariant((variant) => {
|
|
1173
|
+
variant.addPartialStream(ContentType.VIDEO, (stream) => {
|
|
1174
|
+
stream.mime('video/mp4', 'avc1');
|
|
1175
|
+
});
|
|
1176
|
+
variant.addPartialStream(ContentType.AUDIO, (stream) => {
|
|
1177
|
+
stream.mime('audio/mp4', 'mp4a');
|
|
1178
|
+
});
|
|
1179
|
+
});
|
|
1180
|
+
manifest.addPartialTextStream((stream) => {
|
|
1181
|
+
stream.language = 'en';
|
|
1182
|
+
stream.kind = TextStreamKind.SUBTITLE;
|
|
1183
|
+
stream.mime('application/mp4', '');
|
|
1184
|
+
});
|
|
1185
|
+
manifest.sequenceMode = true;
|
|
1186
|
+
});
|
|
1187
|
+
|
|
1188
|
+
fakeNetEngine
|
|
1189
|
+
.setResponseText('test:/master', master)
|
|
1190
|
+
.setResponseText('test:/audio', media)
|
|
1191
|
+
.setResponseText('test:/video', media)
|
|
1192
|
+
.setResponseText('test:/text', textMedia)
|
|
1193
|
+
.setResponseText('test:/main.subs', vttText)
|
|
1194
|
+
.setHeaders('test:/main.subs', {
|
|
1195
|
+
'content-type': 'application/mp4; foo=bar',
|
|
1196
|
+
})
|
|
1197
|
+
.setResponseValue('test:/init.mp4', initSegmentData)
|
|
1198
|
+
.setResponseValue('test:/main.mp4', segmentData);
|
|
1199
|
+
|
|
1200
|
+
const actual = await parser.start('test:/master', playerInterface);
|
|
1201
|
+
expect(actual).toEqual(manifest);
|
|
1202
|
+
});
|
|
1203
|
+
|
|
1197
1204
|
it('parses manifest with FORCED SUBTITLES', async () => {
|
|
1198
1205
|
const master = [
|
|
1199
1206
|
'#EXTM3U\n',
|
|
@@ -1239,6 +1246,7 @@ describe('HlsParser', () => {
|
|
|
1239
1246
|
stream.kind = TextStreamKind.SUBTITLE;
|
|
1240
1247
|
stream.mime('text/vtt', '');
|
|
1241
1248
|
});
|
|
1249
|
+
manifest.sequenceMode = true;
|
|
1242
1250
|
});
|
|
1243
1251
|
|
|
1244
1252
|
fakeNetEngine
|
|
@@ -1300,6 +1308,7 @@ describe('HlsParser', () => {
|
|
|
1300
1308
|
stream.kind = TextStreamKind.SUBTITLE;
|
|
1301
1309
|
stream.mime('text/vtt', '');
|
|
1302
1310
|
});
|
|
1311
|
+
manifest.sequenceMode = true;
|
|
1303
1312
|
});
|
|
1304
1313
|
|
|
1305
1314
|
fakeNetEngine
|
|
@@ -1478,6 +1487,105 @@ describe('HlsParser', () => {
|
|
|
1478
1487
|
}
|
|
1479
1488
|
});
|
|
1480
1489
|
|
|
1490
|
+
it('parse EXT-X-GAP', async () => {
|
|
1491
|
+
const master = [
|
|
1492
|
+
'#EXTM3U\n',
|
|
1493
|
+
'#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aud1",LANGUAGE="eng",',
|
|
1494
|
+
'CHANNELS="2",URI="audio"\n',
|
|
1495
|
+
'#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="avc1,mp4a",',
|
|
1496
|
+
'RESOLUTION=960x540,FRAME-RATE=60,AUDIO="aud1"\n',
|
|
1497
|
+
'video\n',
|
|
1498
|
+
].join('');
|
|
1499
|
+
|
|
1500
|
+
const video = [
|
|
1501
|
+
'#EXTM3U\n',
|
|
1502
|
+
'#EXT-X-PLAYLIST-TYPE:VOD\n',
|
|
1503
|
+
'#EXT-X-MAP:URI="init.mp4",BYTERANGE="616@0"\n',
|
|
1504
|
+
'#EXTINF:5,\n',
|
|
1505
|
+
'main.mp4\n',
|
|
1506
|
+
'#EXTINF:5,\n',
|
|
1507
|
+
'#EXT-X-GAP\n',
|
|
1508
|
+
'main.mp4\n',
|
|
1509
|
+
'#EXTINF:5,\n',
|
|
1510
|
+
'main.mp4\n',
|
|
1511
|
+
].join('');
|
|
1512
|
+
|
|
1513
|
+
const audio = [
|
|
1514
|
+
'#EXTM3U\n',
|
|
1515
|
+
'#EXT-X-PLAYLIST-TYPE:VOD\n',
|
|
1516
|
+
'#EXT-X-MAP:URI="init.mp4",BYTERANGE="616@0"\n',
|
|
1517
|
+
'#EXTINF:5,\n',
|
|
1518
|
+
'main.mp4\n',
|
|
1519
|
+
'#EXTINF:5,\n',
|
|
1520
|
+
'#EXT-X-GAP\n',
|
|
1521
|
+
'main.mp4\n',
|
|
1522
|
+
'#EXTINF:5,\n',
|
|
1523
|
+
'main.mp4\n',
|
|
1524
|
+
].join('');
|
|
1525
|
+
|
|
1526
|
+
fakeNetEngine
|
|
1527
|
+
.setResponseText('test:/master', master)
|
|
1528
|
+
.setResponseText('test:/audio', audio)
|
|
1529
|
+
.setResponseText('test:/video', video)
|
|
1530
|
+
.setResponseValue('test:/init.mp4', initSegmentData)
|
|
1531
|
+
.setResponseValue('test:/main.mp4', segmentData);
|
|
1532
|
+
|
|
1533
|
+
const actual = await parser.start('test:/master', playerInterface);
|
|
1534
|
+
|
|
1535
|
+
expect(actual.variants.length).toBe(1);
|
|
1536
|
+
|
|
1537
|
+
const variant = actual.variants[0];
|
|
1538
|
+
expect(variant.video).toBeTruthy();
|
|
1539
|
+
expect(variant.audio).toBeTruthy();
|
|
1540
|
+
|
|
1541
|
+
const available = shaka.media.SegmentReference.Status.AVAILABLE;
|
|
1542
|
+
const missing = shaka.media.SegmentReference.Status.MISSING;
|
|
1543
|
+
|
|
1544
|
+
await variant.video.createSegmentIndex();
|
|
1545
|
+
goog.asserts.assert(variant.video.segmentIndex != null,
|
|
1546
|
+
'Null segmentIndex!');
|
|
1547
|
+
|
|
1548
|
+
const firstVideoReference = variant.video.segmentIndex.get(0);
|
|
1549
|
+
const secondVideoReference = variant.video.segmentIndex.get(1);
|
|
1550
|
+
const thirdVideoReference = variant.video.segmentIndex.get(2);
|
|
1551
|
+
|
|
1552
|
+
expect(firstVideoReference).not.toBe(null);
|
|
1553
|
+
expect(secondVideoReference).not.toBe(null);
|
|
1554
|
+
expect(thirdVideoReference).not.toBe(null);
|
|
1555
|
+
|
|
1556
|
+
if (firstVideoReference) {
|
|
1557
|
+
expect(firstVideoReference.getStatus()).toBe(available);
|
|
1558
|
+
}
|
|
1559
|
+
if (secondVideoReference) {
|
|
1560
|
+
expect(secondVideoReference.getStatus()).toBe(missing);
|
|
1561
|
+
}
|
|
1562
|
+
if (thirdVideoReference) {
|
|
1563
|
+
expect(thirdVideoReference.getStatus()).toBe(available);
|
|
1564
|
+
}
|
|
1565
|
+
|
|
1566
|
+
await variant.audio.createSegmentIndex();
|
|
1567
|
+
goog.asserts.assert(variant.audio.segmentIndex != null,
|
|
1568
|
+
'Null segmentIndex!');
|
|
1569
|
+
|
|
1570
|
+
const firstAudioReference = variant.audio.segmentIndex.get(0);
|
|
1571
|
+
const secondAudioReference = variant.audio.segmentIndex.get(1);
|
|
1572
|
+
const thirdAudioReference = variant.audio.segmentIndex.get(2);
|
|
1573
|
+
|
|
1574
|
+
expect(firstAudioReference).not.toBe(null);
|
|
1575
|
+
expect(secondAudioReference).not.toBe(null);
|
|
1576
|
+
expect(thirdAudioReference).not.toBe(null);
|
|
1577
|
+
|
|
1578
|
+
if (firstAudioReference) {
|
|
1579
|
+
expect(firstAudioReference.getStatus()).toBe(available);
|
|
1580
|
+
}
|
|
1581
|
+
if (secondAudioReference) {
|
|
1582
|
+
expect(secondAudioReference.getStatus()).toBe(missing);
|
|
1583
|
+
}
|
|
1584
|
+
if (thirdAudioReference) {
|
|
1585
|
+
expect(thirdAudioReference.getStatus()).toBe(available);
|
|
1586
|
+
}
|
|
1587
|
+
});
|
|
1588
|
+
|
|
1481
1589
|
it('Disable audio does not create audio streams', async () => {
|
|
1482
1590
|
const master = [
|
|
1483
1591
|
'#EXTM3U\n',
|
|
@@ -1804,6 +1912,7 @@ describe('HlsParser', () => {
|
|
|
1804
1912
|
stream.language = 'en';
|
|
1805
1913
|
stream.mime('application/mp4', 'stpp.ttml.im1t');
|
|
1806
1914
|
});
|
|
1915
|
+
manifest.sequenceMode = true;
|
|
1807
1916
|
});
|
|
1808
1917
|
|
|
1809
1918
|
fakeNetEngine
|
|
@@ -1853,6 +1962,7 @@ describe('HlsParser', () => {
|
|
|
1853
1962
|
manifest.addPartialTextStream((stream) => {
|
|
1854
1963
|
stream.mime('text/vtt', 'vtt');
|
|
1855
1964
|
});
|
|
1965
|
+
manifest.sequenceMode = true;
|
|
1856
1966
|
});
|
|
1857
1967
|
|
|
1858
1968
|
fakeNetEngine
|
|
@@ -1895,6 +2005,7 @@ describe('HlsParser', () => {
|
|
|
1895
2005
|
manifest.addPartialTextStream((stream) => {
|
|
1896
2006
|
stream.kind = TextStreamKind.SUBTITLE;
|
|
1897
2007
|
});
|
|
2008
|
+
manifest.sequenceMode = true;
|
|
1898
2009
|
});
|
|
1899
2010
|
|
|
1900
2011
|
fakeNetEngine
|
|
@@ -1909,6 +2020,173 @@ describe('HlsParser', () => {
|
|
|
1909
2020
|
expect(actual).toEqual(manifest);
|
|
1910
2021
|
});
|
|
1911
2022
|
|
|
2023
|
+
describe('produces syncTime', () => {
|
|
2024
|
+
/**
|
|
2025
|
+
* @param {number} startTime
|
|
2026
|
+
* @param {number} endTime
|
|
2027
|
+
* @param {number} syncTime
|
|
2028
|
+
* @return {!shaka.media.SegmentReference}
|
|
2029
|
+
*/
|
|
2030
|
+
function makeReference(startTime, endTime, syncTime) {
|
|
2031
|
+
const initUris = () => ['test:/init.mp4'];
|
|
2032
|
+
const init = new shaka.media.InitSegmentReference(initUris, 0, 615);
|
|
2033
|
+
const uris = () => ['test:/main.mp4'];
|
|
2034
|
+
return new shaka.media.SegmentReference(
|
|
2035
|
+
startTime, endTime, uris, 0, null, init, 0, 0, Infinity,
|
|
2036
|
+
[], undefined, undefined, syncTime);
|
|
2037
|
+
}
|
|
2038
|
+
|
|
2039
|
+
/**
|
|
2040
|
+
* @param {string} media
|
|
2041
|
+
* @param {!Array.<number>} startTimes
|
|
2042
|
+
* @param {(function(!shaka.media.SegmentReference))=} modifyFn
|
|
2043
|
+
*/
|
|
2044
|
+
async function test(media, startTimes, modifyFn) {
|
|
2045
|
+
const master = [
|
|
2046
|
+
'#EXTM3U\n',
|
|
2047
|
+
'#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="avc1,vtt",',
|
|
2048
|
+
'RESOLUTION=960x540,FRAME-RATE=60\n',
|
|
2049
|
+
'video\n',
|
|
2050
|
+
].join('');
|
|
2051
|
+
|
|
2052
|
+
const segments = [];
|
|
2053
|
+
for (let i = 0; i < startTimes.length - 1; i++) {
|
|
2054
|
+
const startTime = startTimes[i];
|
|
2055
|
+
const endTime = startTimes[i + 1];
|
|
2056
|
+
const reference = makeReference(startTime, endTime, startTime);
|
|
2057
|
+
if (modifyFn) {
|
|
2058
|
+
modifyFn(reference);
|
|
2059
|
+
}
|
|
2060
|
+
segments.push(reference);
|
|
2061
|
+
}
|
|
2062
|
+
|
|
2063
|
+
const manifest = shaka.test.ManifestGenerator.generate((manifest) => {
|
|
2064
|
+
manifest.anyTimeline();
|
|
2065
|
+
manifest.addPartialVariant((variant) => {
|
|
2066
|
+
variant.addPartialStream(ContentType.VIDEO, (stream) => {
|
|
2067
|
+
stream.segmentIndex = new shaka.media.SegmentIndex(segments);
|
|
2068
|
+
});
|
|
2069
|
+
});
|
|
2070
|
+
manifest.sequenceMode = true;
|
|
2071
|
+
});
|
|
2072
|
+
|
|
2073
|
+
fakeNetEngine
|
|
2074
|
+
.setResponseText('test:/master', master)
|
|
2075
|
+
.setResponseText('test:/video', media)
|
|
2076
|
+
.setResponseValue('test:/init.mp4', initSegmentData)
|
|
2077
|
+
.setResponseValue('test:/main.mp4', segmentData);
|
|
2078
|
+
|
|
2079
|
+
const actual = await parser.start('test:/master', playerInterface);
|
|
2080
|
+
expect(actual).toEqual(manifest);
|
|
2081
|
+
}
|
|
2082
|
+
|
|
2083
|
+
it('from EXT-X-PROGRAM-DATE-TIME', async () => {
|
|
2084
|
+
await test([
|
|
2085
|
+
'#EXTM3U\n',
|
|
2086
|
+
'#EXT-X-PLAYLIST-TYPE:VOD\n',
|
|
2087
|
+
'#EXT-X-MAP:URI="init.mp4",BYTERANGE="616@0"\n',
|
|
2088
|
+
'#EXT-X-PROGRAM-DATE-TIME:2000-01-01T00:00:05.00Z\n',
|
|
2089
|
+
'#EXTINF:5,\n',
|
|
2090
|
+
'main.mp4\n',
|
|
2091
|
+
'#EXT-X-PROGRAM-DATE-TIME:2000-01-01T00:00:10.00Z\n',
|
|
2092
|
+
'#EXTINF:5,\n',
|
|
2093
|
+
'main.mp4\n',
|
|
2094
|
+
'#EXT-X-PROGRAM-DATE-TIME:2000-01-01T00:00:15.00Z\n',
|
|
2095
|
+
'#EXTINF:5,\n',
|
|
2096
|
+
'main.mp4\n',
|
|
2097
|
+
'#EXT-X-PROGRAM-DATE-TIME:2000-01-01T00:00:20.00Z\n',
|
|
2098
|
+
'#EXTINF:5,\n',
|
|
2099
|
+
'main.mp4\n',
|
|
2100
|
+
'#EXT-X-PROGRAM-DATE-TIME:2000-01-01T00:00:25.00Z\n',
|
|
2101
|
+
'#EXTINF:5,\n',
|
|
2102
|
+
'main.mp4',
|
|
2103
|
+
].join(''), [0, 5, 10, 15, 20, 25]);
|
|
2104
|
+
});
|
|
2105
|
+
|
|
2106
|
+
it('when some EXT-X-PROGRAM-DATE-TIME values are missing', async () => {
|
|
2107
|
+
await test([
|
|
2108
|
+
'#EXTM3U\n',
|
|
2109
|
+
'#EXT-X-PLAYLIST-TYPE:VOD\n',
|
|
2110
|
+
'#EXT-X-MAP:URI="init.mp4",BYTERANGE="616@0"\n',
|
|
2111
|
+
'#EXTINF:2,\n',
|
|
2112
|
+
'main.mp4\n',
|
|
2113
|
+
'#EXT-X-PROGRAM-DATE-TIME:2000-01-01T00:00:10.00Z\n',
|
|
2114
|
+
'#EXTINF:5,\n',
|
|
2115
|
+
'main.mp4\n',
|
|
2116
|
+
'#EXTINF:5,\n',
|
|
2117
|
+
'main.mp4\n',
|
|
2118
|
+
'#EXTINF:5,\n',
|
|
2119
|
+
'main.mp4\n',
|
|
2120
|
+
'#EXT-X-PROGRAM-DATE-TIME:2000-01-01T00:00:25.00Z\n',
|
|
2121
|
+
'#EXTINF:2,\n',
|
|
2122
|
+
'main.mp4\n',
|
|
2123
|
+
'#EXTINF:4,\n',
|
|
2124
|
+
'main.mp4',
|
|
2125
|
+
].join(''), [0, 2, 7, 12, 17, 19, 23]);
|
|
2126
|
+
});
|
|
2127
|
+
|
|
2128
|
+
it('except when ignoreManifestProgramDateTime is set', async () => {
|
|
2129
|
+
const config = shaka.util.PlayerConfiguration.createDefault().manifest;
|
|
2130
|
+
config.hls.ignoreManifestProgramDateTime = true;
|
|
2131
|
+
parser.configure(config);
|
|
2132
|
+
await test([
|
|
2133
|
+
'#EXTM3U\n',
|
|
2134
|
+
'#EXT-X-PLAYLIST-TYPE:VOD\n',
|
|
2135
|
+
'#EXT-X-MAP:URI="init.mp4",BYTERANGE="616@0"\n',
|
|
2136
|
+
'#EXT-X-PROGRAM-DATE-TIME:2000-01-01T00:00:05.00Z\n',
|
|
2137
|
+
'#EXTINF:5,\n',
|
|
2138
|
+
'main.mp4\n',
|
|
2139
|
+
'#EXT-X-PROGRAM-DATE-TIME:2000-01-01T00:00:10.00Z\n',
|
|
2140
|
+
'#EXTINF:5,\n',
|
|
2141
|
+
'main.mp4\n',
|
|
2142
|
+
'#EXT-X-PROGRAM-DATE-TIME:2000-01-01T00:00:15.00Z\n',
|
|
2143
|
+
'#EXTINF:5,\n',
|
|
2144
|
+
'main.mp4\n',
|
|
2145
|
+
'#EXT-X-PROGRAM-DATE-TIME:2000-01-01T00:00:20.00Z\n',
|
|
2146
|
+
'#EXTINF:5,\n',
|
|
2147
|
+
'main.mp4\n',
|
|
2148
|
+
'#EXT-X-PROGRAM-DATE-TIME:2000-01-01T00:00:25.00Z\n',
|
|
2149
|
+
'#EXTINF:5,\n',
|
|
2150
|
+
'main.mp4',
|
|
2151
|
+
].join(''), [0, 5, 10, 15, 20, 25], (reference) => {
|
|
2152
|
+
reference.syncTime = null;
|
|
2153
|
+
});
|
|
2154
|
+
});
|
|
2155
|
+
|
|
2156
|
+
it('when there are partial segments', async () => {
|
|
2157
|
+
playerInterface.isLowLatencyMode = () => true;
|
|
2158
|
+
await test([
|
|
2159
|
+
'#EXTM3U\n',
|
|
2160
|
+
'#EXT-X-PLAYLIST-TYPE:VOD\n',
|
|
2161
|
+
'#EXT-X-MAP:URI="init.mp4",BYTERANGE="616@0"\n',
|
|
2162
|
+
'#EXT-X-PROGRAM-DATE-TIME:2000-01-01T00:00:05.00Z\n',
|
|
2163
|
+
'#EXTINF:5,\n',
|
|
2164
|
+
'main.mp4\n',
|
|
2165
|
+
'#EXT-X-PROGRAM-DATE-TIME:2000-01-01T00:00:10.00Z\n',
|
|
2166
|
+
'#EXTINF:5,\n',
|
|
2167
|
+
'main.mp4\n',
|
|
2168
|
+
'#EXT-X-PROGRAM-DATE-TIME:2000-01-01T00:00:15.00Z\n',
|
|
2169
|
+
'#EXT-X-PART:DURATION=2.5,URI="main.mp4"\n',
|
|
2170
|
+
'#EXT-X-PART:DURATION=2.5,URI="main.mp4"\n',
|
|
2171
|
+
'#EXTINF:5,\n',
|
|
2172
|
+
'main.mp4\n',
|
|
2173
|
+
'#EXT-X-PROGRAM-DATE-TIME:2000-01-01T00:00:20.00Z\n',
|
|
2174
|
+
'#EXTINF:5,\n',
|
|
2175
|
+
'main.mp4\n',
|
|
2176
|
+
'#EXT-X-PROGRAM-DATE-TIME:2000-01-01T00:00:25.00Z\n',
|
|
2177
|
+
'#EXTINF:5,\n',
|
|
2178
|
+
'main.mp4',
|
|
2179
|
+
].join(''), [0, 5, 10, 15, 20, 25], (reference) => {
|
|
2180
|
+
if (reference.startTime == 10) {
|
|
2181
|
+
reference.partialReferences = [
|
|
2182
|
+
makeReference(10, 12.5, 10),
|
|
2183
|
+
makeReference(12.5, 15, 12.5),
|
|
2184
|
+
];
|
|
2185
|
+
}
|
|
2186
|
+
});
|
|
2187
|
+
});
|
|
2188
|
+
});
|
|
2189
|
+
|
|
1912
2190
|
it('drops failed text streams when configured to', async () => {
|
|
1913
2191
|
const master = [
|
|
1914
2192
|
'#EXTM3U\n',
|
|
@@ -1932,6 +2210,7 @@ describe('HlsParser', () => {
|
|
|
1932
2210
|
manifest.addPartialVariant((variant) => {
|
|
1933
2211
|
variant.addPartialStream(ContentType.VIDEO);
|
|
1934
2212
|
});
|
|
2213
|
+
manifest.sequenceMode = true;
|
|
1935
2214
|
});
|
|
1936
2215
|
|
|
1937
2216
|
fakeNetEngine
|
|
@@ -1969,6 +2248,7 @@ describe('HlsParser', () => {
|
|
|
1969
2248
|
manifest.addPartialVariant((variant) => {
|
|
1970
2249
|
variant.addPartialStream(ContentType.VIDEO);
|
|
1971
2250
|
});
|
|
2251
|
+
manifest.sequenceMode = true;
|
|
1972
2252
|
});
|
|
1973
2253
|
|
|
1974
2254
|
fakeNetEngine
|
|
@@ -2011,6 +2291,7 @@ describe('HlsParser', () => {
|
|
|
2011
2291
|
});
|
|
2012
2292
|
variant.addPartialStream(ContentType.AUDIO);
|
|
2013
2293
|
});
|
|
2294
|
+
manifest.sequenceMode = true;
|
|
2014
2295
|
});
|
|
2015
2296
|
|
|
2016
2297
|
await testHlsParser(master, media, manifest);
|
|
@@ -2095,6 +2376,7 @@ describe('HlsParser', () => {
|
|
|
2095
2376
|
stream.mime('audio/mp4', 'mp4a');
|
|
2096
2377
|
});
|
|
2097
2378
|
});
|
|
2379
|
+
manifest.sequenceMode = true;
|
|
2098
2380
|
});
|
|
2099
2381
|
|
|
2100
2382
|
await testHlsParser(master, media, manifest);
|
|
@@ -2190,6 +2472,7 @@ describe('HlsParser', () => {
|
|
|
2190
2472
|
stream.language = 'en';
|
|
2191
2473
|
});
|
|
2192
2474
|
});
|
|
2475
|
+
manifest.sequenceMode = true;
|
|
2193
2476
|
});
|
|
2194
2477
|
|
|
2195
2478
|
fakeNetEngine
|
|
@@ -2248,6 +2531,7 @@ describe('HlsParser', () => {
|
|
|
2248
2531
|
});
|
|
2249
2532
|
});
|
|
2250
2533
|
});
|
|
2534
|
+
manifest.sequenceMode = true;
|
|
2251
2535
|
});
|
|
2252
2536
|
|
|
2253
2537
|
await testHlsParser(master, media, manifest);
|
|
@@ -2287,6 +2571,44 @@ describe('HlsParser', () => {
|
|
|
2287
2571
|
});
|
|
2288
2572
|
});
|
|
2289
2573
|
});
|
|
2574
|
+
manifest.sequenceMode = true;
|
|
2575
|
+
});
|
|
2576
|
+
|
|
2577
|
+
await testHlsParser(master, media, manifest);
|
|
2578
|
+
});
|
|
2579
|
+
|
|
2580
|
+
it('constructs DrmInfo for FairPlay', async () => {
|
|
2581
|
+
const master = [
|
|
2582
|
+
'#EXTM3U\n',
|
|
2583
|
+
'#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="avc1",',
|
|
2584
|
+
'RESOLUTION=960x540,FRAME-RATE=60\n',
|
|
2585
|
+
'video\n',
|
|
2586
|
+
].join('');
|
|
2587
|
+
|
|
2588
|
+
const media = [
|
|
2589
|
+
'#EXTM3U\n',
|
|
2590
|
+
'#EXT-X-TARGETDURATION:6\n',
|
|
2591
|
+
'#EXT-X-PLAYLIST-TYPE:VOD\n',
|
|
2592
|
+
'#EXT-X-KEY:METHOD=SAMPLE-AES-CTR,',
|
|
2593
|
+
'KEYFORMAT="com.apple.streamingkeydelivery",',
|
|
2594
|
+
'URI="skd://f93d4e700d7ddde90529a27735d9e7cb",\n',
|
|
2595
|
+
'#EXT-X-MAP:URI="init.mp4"\n',
|
|
2596
|
+
'#EXTINF:5,\n',
|
|
2597
|
+
'#EXT-X-BYTERANGE:121090@616\n',
|
|
2598
|
+
'main.mp4',
|
|
2599
|
+
].join('');
|
|
2600
|
+
|
|
2601
|
+
const manifest = shaka.test.ManifestGenerator.generate((manifest) => {
|
|
2602
|
+
manifest.anyTimeline();
|
|
2603
|
+
manifest.addPartialVariant((variant) => {
|
|
2604
|
+
variant.addPartialStream(ContentType.VIDEO, (stream) => {
|
|
2605
|
+
stream.encrypted = true;
|
|
2606
|
+
stream.addDrmInfo('com.apple.fps', (drmInfo) => {
|
|
2607
|
+
drmInfo.addInitData('sinf', new Uint8Array(0));
|
|
2608
|
+
});
|
|
2609
|
+
});
|
|
2610
|
+
});
|
|
2611
|
+
manifest.sequenceMode = true;
|
|
2290
2612
|
});
|
|
2291
2613
|
|
|
2292
2614
|
await testHlsParser(master, media, manifest);
|
|
@@ -2320,6 +2642,7 @@ describe('HlsParser', () => {
|
|
|
2320
2642
|
stream.mime('audio/mp4', 'mp4a');
|
|
2321
2643
|
});
|
|
2322
2644
|
});
|
|
2645
|
+
manifest.sequenceMode = true;
|
|
2323
2646
|
});
|
|
2324
2647
|
|
|
2325
2648
|
fakeNetEngine.setHeaders(
|
|
@@ -2488,6 +2811,34 @@ describe('HlsParser', () => {
|
|
|
2488
2811
|
});
|
|
2489
2812
|
});
|
|
2490
2813
|
|
|
2814
|
+
it('if FairPlay encryption with MSE and mp2t content', async () => {
|
|
2815
|
+
const master = [
|
|
2816
|
+
'#EXTM3U\n',
|
|
2817
|
+
'#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="avc1",',
|
|
2818
|
+
'RESOLUTION=960x540,FRAME-RATE=60\n',
|
|
2819
|
+
'video\n',
|
|
2820
|
+
].join('');
|
|
2821
|
+
|
|
2822
|
+
const media = [
|
|
2823
|
+
'#EXTM3U\n',
|
|
2824
|
+
'#EXT-X-TARGETDURATION:6\n',
|
|
2825
|
+
'#EXT-X-PLAYLIST-TYPE:VOD\n',
|
|
2826
|
+
'#EXT-X-KEY:METHOD=SAMPLE-AES-CTR,',
|
|
2827
|
+
'KEYFORMAT="com.apple.streamingkeydelivery",',
|
|
2828
|
+
'URI="skd://f93d4e700d7ddde90529a27735d9e7cb",\n',
|
|
2829
|
+
'#EXTINF:5,\n',
|
|
2830
|
+
'#EXT-X-BYTERANGE:121090@616\n',
|
|
2831
|
+
'main.ts',
|
|
2832
|
+
].join('');
|
|
2833
|
+
|
|
2834
|
+
const error = new shaka.util.Error(
|
|
2835
|
+
shaka.util.Error.Severity.CRITICAL,
|
|
2836
|
+
shaka.util.Error.Category.MANIFEST,
|
|
2837
|
+
Code.HLS_MSE_ENCRYPTED_MP2T_NOT_SUPPORTED);
|
|
2838
|
+
|
|
2839
|
+
await verifyError(master, media, error);
|
|
2840
|
+
});
|
|
2841
|
+
|
|
2491
2842
|
describe('if required tags are missing', () => {
|
|
2492
2843
|
/**
|
|
2493
2844
|
* @param {string} master
|
|
@@ -2526,261 +2877,6 @@ describe('HlsParser', () => {
|
|
|
2526
2877
|
});
|
|
2527
2878
|
}); // Errors out
|
|
2528
2879
|
|
|
2529
|
-
describe('getStartTime_', () => {
|
|
2530
|
-
/** @type {number} */
|
|
2531
|
-
let segmentDataStartTime;
|
|
2532
|
-
/** @type {!Uint8Array} */
|
|
2533
|
-
let tsSegmentData;
|
|
2534
|
-
/** @type {!Uint8Array} */
|
|
2535
|
-
let nullTsPacketData;
|
|
2536
|
-
|
|
2537
|
-
const master = [
|
|
2538
|
-
'#EXTM3U\n',
|
|
2539
|
-
'#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="avc1",',
|
|
2540
|
-
'RESOLUTION=960x540,FRAME-RATE=60\n',
|
|
2541
|
-
'video',
|
|
2542
|
-
].join('');
|
|
2543
|
-
|
|
2544
|
-
const media = [
|
|
2545
|
-
'#EXTM3U\n',
|
|
2546
|
-
'#EXT-X-PLAYLIST-TYPE:VOD\n',
|
|
2547
|
-
'#EXT-X-MAP:URI="init.mp4"\n',
|
|
2548
|
-
'#EXTINF:5,\n',
|
|
2549
|
-
'#EXT-X-BYTERANGE:121090@616\n',
|
|
2550
|
-
'main.mp4',
|
|
2551
|
-
].join('');
|
|
2552
|
-
|
|
2553
|
-
// TODO: Add separate tests to cover correct handling of BYTERANGE in
|
|
2554
|
-
// constructing references. Here it is covered incidentally.
|
|
2555
|
-
const expectedStartByte = 616;
|
|
2556
|
-
const expectedEndByte = 121705;
|
|
2557
|
-
// Nit: this value is an implementation detail of the fix for #1106
|
|
2558
|
-
const partialEndByte = expectedStartByte + 2048 - 1;
|
|
2559
|
-
|
|
2560
|
-
beforeEach(() => {
|
|
2561
|
-
// TODO: use StreamGenerator?
|
|
2562
|
-
segmentData = new Uint8Array([
|
|
2563
|
-
0x00, 0x00, 0x00, 0x24, // size (36)
|
|
2564
|
-
0x6D, 0x6F, 0x6F, 0x66, // type (moof)
|
|
2565
|
-
0x00, 0x00, 0x00, 0x1C, // traf size (28)
|
|
2566
|
-
0x74, 0x72, 0x61, 0x66, // type (traf)
|
|
2567
|
-
0x00, 0x00, 0x00, 0x14, // tfdt size (20)
|
|
2568
|
-
0x74, 0x66, 0x64, 0x74, // type (tfdt)
|
|
2569
|
-
0x01, 0x00, 0x00, 0x00, // version and flags
|
|
2570
|
-
|
|
2571
|
-
0x00, 0x00, 0x00, 0x00, // baseMediaDecodeTime first 4 bytes
|
|
2572
|
-
0x00, 0x00, 0x07, 0xd0, // baseMediaDecodeTime last 4 bytes (2000)
|
|
2573
|
-
]);
|
|
2574
|
-
tsSegmentData = new Uint8Array([
|
|
2575
|
-
0x47, // TS sync byte (fixed value)
|
|
2576
|
-
0x41, 0x01, // not corrupt, payload follows, packet ID 257
|
|
2577
|
-
0x10, // not scrambled, no adaptation field, payload only, seq #0
|
|
2578
|
-
0x00, 0x00, 0x01, // PES start code (fixed value)
|
|
2579
|
-
0xe0, // stream ID (video stream 0)
|
|
2580
|
-
0x00, 0x00, // PES packet length (doesn't matter)
|
|
2581
|
-
0x80, // marker bits (fixed value), not scrambled, not priority
|
|
2582
|
-
0x80, // PTS only, no DTS, other flags 0 (don't matter)
|
|
2583
|
-
0x05, // remaining PES header length == 5 (one timestamp)
|
|
2584
|
-
0x21, 0x00, 0x0b, 0x7e, 0x41, // PTS = 180000, encoded into 5 bytes
|
|
2585
|
-
]);
|
|
2586
|
-
// 180000 (TS PTS) divided by fixed TS timescale (90000) = 2s.
|
|
2587
|
-
// 2000 (MP4 PTS) divided by parsed MP4 timescale (1000) = 2s.
|
|
2588
|
-
segmentDataStartTime = 2;
|
|
2589
|
-
nullTsPacketData = new Uint8Array([
|
|
2590
|
-
0x47, // TS sync byte (fixed value)
|
|
2591
|
-
0x1f, 0xff, // null packet (packet ID 8191)
|
|
2592
|
-
]);
|
|
2593
|
-
});
|
|
2594
|
-
|
|
2595
|
-
it('parses start time from mp4 segment', async () => {
|
|
2596
|
-
fakeNetEngine
|
|
2597
|
-
.setResponseText('test:/master', master)
|
|
2598
|
-
.setResponseText('test:/video', media)
|
|
2599
|
-
.setResponseValue('test:/init.mp4', initSegmentData)
|
|
2600
|
-
.setResponseValue('test:/main.mp4', segmentData);
|
|
2601
|
-
|
|
2602
|
-
const expectedRef = ManifestParser.makeReference(
|
|
2603
|
-
/* uri= */ 'test:/main.mp4',
|
|
2604
|
-
/* startTime= */ 0,
|
|
2605
|
-
/* endTime= */ 5,
|
|
2606
|
-
/* baseUri= */ '',
|
|
2607
|
-
expectedStartByte,
|
|
2608
|
-
expectedEndByte);
|
|
2609
|
-
// In VOD content, we set the timestampOffset to align the
|
|
2610
|
-
// content to presentation time 0.
|
|
2611
|
-
expectedRef.timestampOffset = -segmentDataStartTime;
|
|
2612
|
-
|
|
2613
|
-
const manifest = await parser.start('test:/master', playerInterface);
|
|
2614
|
-
const video = manifest.variants[0].video;
|
|
2615
|
-
await video.createSegmentIndex();
|
|
2616
|
-
ManifestParser.verifySegmentIndex(video, [expectedRef]);
|
|
2617
|
-
|
|
2618
|
-
// Make sure the segment data was fetched with the correct byte
|
|
2619
|
-
// range.
|
|
2620
|
-
fakeNetEngine.expectRangeRequest(
|
|
2621
|
-
'test:/main.mp4',
|
|
2622
|
-
expectedStartByte,
|
|
2623
|
-
partialEndByte);
|
|
2624
|
-
});
|
|
2625
|
-
|
|
2626
|
-
it('parses start time from ts segments', async () => {
|
|
2627
|
-
const tsMediaPlaylist = media.replace(/\.mp4/g, '.ts');
|
|
2628
|
-
|
|
2629
|
-
fakeNetEngine
|
|
2630
|
-
.setResponseText('test:/master', master)
|
|
2631
|
-
.setResponseText('test:/video', tsMediaPlaylist)
|
|
2632
|
-
.setResponseValue('test:/main.ts', tsSegmentData);
|
|
2633
|
-
|
|
2634
|
-
const expectedRef = ManifestParser.makeReference(
|
|
2635
|
-
/* uri= */ 'test:/main.ts',
|
|
2636
|
-
/* startTime= */ 0,
|
|
2637
|
-
/* endTime= */ 5,
|
|
2638
|
-
/* baseUri= */ '',
|
|
2639
|
-
expectedStartByte,
|
|
2640
|
-
expectedEndByte);
|
|
2641
|
-
// In VOD content, we set the timestampOffset to align the
|
|
2642
|
-
// content to presentation time 0.
|
|
2643
|
-
expectedRef.timestampOffset = -segmentDataStartTime;
|
|
2644
|
-
|
|
2645
|
-
const manifest = await parser.start('test:/master', playerInterface);
|
|
2646
|
-
const video = manifest.variants[0].video;
|
|
2647
|
-
await video.createSegmentIndex();
|
|
2648
|
-
ManifestParser.verifySegmentIndex(video, [expectedRef]);
|
|
2649
|
-
|
|
2650
|
-
// Make sure the segment data was fetched with the correct byte
|
|
2651
|
-
// range.
|
|
2652
|
-
fakeNetEngine.expectRangeRequest(
|
|
2653
|
-
'test:/main.ts',
|
|
2654
|
-
expectedStartByte,
|
|
2655
|
-
partialEndByte);
|
|
2656
|
-
});
|
|
2657
|
-
|
|
2658
|
-
it('parses start time from ts segments with null packets', async () => {
|
|
2659
|
-
const tsMediaPlaylist = media.replace(/\.mp4/g, '.ts');
|
|
2660
|
-
|
|
2661
|
-
// Each packet is 188 bytes, so allocate space for 3.
|
|
2662
|
-
const tsSegmentWithNullPackets = new Uint8Array(188 * 3);
|
|
2663
|
-
// The first two are "null" packets.
|
|
2664
|
-
tsSegmentWithNullPackets.set(nullTsPacketData, /* offset= */ 0);
|
|
2665
|
-
tsSegmentWithNullPackets.set(nullTsPacketData, /* offset= */ 188);
|
|
2666
|
-
// The third has a timestamp.
|
|
2667
|
-
tsSegmentWithNullPackets.set(tsSegmentData, /* offset= */ 188 * 2);
|
|
2668
|
-
|
|
2669
|
-
fakeNetEngine
|
|
2670
|
-
.setResponseText('test:/master', master)
|
|
2671
|
-
.setResponseText('test:/video', tsMediaPlaylist)
|
|
2672
|
-
.setResponseValue('test:/main.ts', tsSegmentWithNullPackets);
|
|
2673
|
-
|
|
2674
|
-
const expectedRef = ManifestParser.makeReference(
|
|
2675
|
-
/* uri= */ 'test:/main.ts',
|
|
2676
|
-
/* startTime= */ 0,
|
|
2677
|
-
/* endTime= */ 5,
|
|
2678
|
-
/* baseUri= */ '',
|
|
2679
|
-
expectedStartByte,
|
|
2680
|
-
expectedEndByte);
|
|
2681
|
-
// In VOD content, we set the timestampOffset to align the
|
|
2682
|
-
// content to presentation time 0.
|
|
2683
|
-
expectedRef.timestampOffset = -segmentDataStartTime;
|
|
2684
|
-
|
|
2685
|
-
const manifest = await parser.start('test:/master', playerInterface);
|
|
2686
|
-
const video = manifest.variants[0].video;
|
|
2687
|
-
await video.createSegmentIndex();
|
|
2688
|
-
ManifestParser.verifySegmentIndex(video, [expectedRef]);
|
|
2689
|
-
|
|
2690
|
-
// Make sure the segment data was fetched with the correct byte
|
|
2691
|
-
// range.
|
|
2692
|
-
fakeNetEngine.expectRangeRequest(
|
|
2693
|
-
'test:/main.ts',
|
|
2694
|
-
expectedStartByte,
|
|
2695
|
-
partialEndByte);
|
|
2696
|
-
});
|
|
2697
|
-
|
|
2698
|
-
// We want to make sure that we can interrupt the parser while it is getting
|
|
2699
|
-
// the start time. This is a regression test for Issue #1788 where
|
|
2700
|
-
// interrupting the partial network request would be misinterpreted as the
|
|
2701
|
-
// server not supporting range requests.
|
|
2702
|
-
it('can be interrupted', async () => {
|
|
2703
|
-
fakeNetEngine
|
|
2704
|
-
.setResponseText('test:/master', master)
|
|
2705
|
-
.setResponseText('test:/video', media)
|
|
2706
|
-
.setResponseValue('test:/init.mp4', initSegmentData);
|
|
2707
|
-
|
|
2708
|
-
// We are assuming that the time will be pulled out of the main mp4
|
|
2709
|
-
// segment, so if we see a request that has a range header, we will stop
|
|
2710
|
-
// the parser.
|
|
2711
|
-
/** @type {!Map.<string, !BufferSource>} */
|
|
2712
|
-
const responses = new Map();
|
|
2713
|
-
responses.set('test:/main.mp4', segmentData);
|
|
2714
|
-
responses.set('test:/init.mp4', initSegmentData);
|
|
2715
|
-
|
|
2716
|
-
responses.forEach((data, uri) => {
|
|
2717
|
-
fakeNetEngine.setResponse(uri, () => {
|
|
2718
|
-
// Now that we are stopping the parser, we don't want to see any more
|
|
2719
|
-
// requests. So if there is another request, fail the test.
|
|
2720
|
-
responses.forEach((data, uri) => {
|
|
2721
|
-
fakeNetEngine.setResponse(uri, fail);
|
|
2722
|
-
});
|
|
2723
|
-
|
|
2724
|
-
// Stop the parser, but don't wait on it or else we will hit deadlock.
|
|
2725
|
-
parser.stop();
|
|
2726
|
-
|
|
2727
|
-
return Promise.resolve(data);
|
|
2728
|
-
});
|
|
2729
|
-
});
|
|
2730
|
-
|
|
2731
|
-
const expected = Util.jasmineError(new shaka.util.Error(
|
|
2732
|
-
shaka.util.Error.Severity.CRITICAL,
|
|
2733
|
-
shaka.util.Error.Category.PLAYER,
|
|
2734
|
-
shaka.util.Error.Code.OPERATION_ABORTED));
|
|
2735
|
-
await expectAsync(parser.start('test:/master', playerInterface))
|
|
2736
|
-
.toBeRejectedWith(expected);
|
|
2737
|
-
});
|
|
2738
|
-
|
|
2739
|
-
it('sets duration with respect to presentation offset', async () => {
|
|
2740
|
-
fakeNetEngine
|
|
2741
|
-
.setResponseText('test:/master', master)
|
|
2742
|
-
.setResponseText('test:/video', media)
|
|
2743
|
-
.setResponseValue('test:/init.mp4', initSegmentData)
|
|
2744
|
-
.setResponseValue('test:/main.mp4', segmentData);
|
|
2745
|
-
|
|
2746
|
-
const manifest = await parser.start('test:/master', playerInterface);
|
|
2747
|
-
const presentationTimeline = manifest.presentationTimeline;
|
|
2748
|
-
|
|
2749
|
-
const video = manifest.variants[0].video;
|
|
2750
|
-
await video.createSegmentIndex();
|
|
2751
|
-
goog.asserts.assert(video.segmentIndex != null, 'Null segmentIndex!');
|
|
2752
|
-
|
|
2753
|
-
const refs = Array.from(video.segmentIndex);
|
|
2754
|
-
expect(refs.length).toBe(1);
|
|
2755
|
-
|
|
2756
|
-
expect(refs[0].timestampOffset).toBe(-segmentDataStartTime);
|
|
2757
|
-
// The duration should be set to the sum of the segment durations (5),
|
|
2758
|
-
// even though the endTime of the segment is larger.
|
|
2759
|
-
expect(refs[0].endTime - refs[0].startTime).toBe(5);
|
|
2760
|
-
expect(presentationTimeline.getDuration()).toBe(5);
|
|
2761
|
-
});
|
|
2762
|
-
|
|
2763
|
-
it('forces full segment request', async () => {
|
|
2764
|
-
fakeNetEngine
|
|
2765
|
-
.setResponseText('test:/master', master)
|
|
2766
|
-
.setResponseText('test:/video', media)
|
|
2767
|
-
.setResponseValue('test:/init.mp4', initSegmentData)
|
|
2768
|
-
.setResponseValue('test:/main.mp4', segmentData);
|
|
2769
|
-
|
|
2770
|
-
const config = shaka.util.PlayerConfiguration.createDefault().manifest;
|
|
2771
|
-
config.hls.useFullSegmentsForStartTime = true;
|
|
2772
|
-
parser.configure(config);
|
|
2773
|
-
await parser.start('test:/master', playerInterface);
|
|
2774
|
-
|
|
2775
|
-
// Make sure the segment data was fetched with the correct byte
|
|
2776
|
-
// range.
|
|
2777
|
-
fakeNetEngine.expectRangeRequest(
|
|
2778
|
-
'test:/main.mp4',
|
|
2779
|
-
expectedStartByte,
|
|
2780
|
-
expectedEndByte);
|
|
2781
|
-
});
|
|
2782
|
-
});
|
|
2783
|
-
|
|
2784
2880
|
it('correctly detects VOD streams as non-live', async () => {
|
|
2785
2881
|
const master = [
|
|
2786
2882
|
'#EXTM3U\n',
|
|
@@ -3037,80 +3133,12 @@ describe('HlsParser', () => {
|
|
|
3037
3133
|
stream.language = 'fr';
|
|
3038
3134
|
});
|
|
3039
3135
|
});
|
|
3136
|
+
manifest.sequenceMode = true;
|
|
3040
3137
|
});
|
|
3041
3138
|
|
|
3042
3139
|
await testHlsParser(master, media, manifest);
|
|
3043
3140
|
});
|
|
3044
3141
|
|
|
3045
|
-
it('skips raw audio formats', async () => {
|
|
3046
|
-
const master = [
|
|
3047
|
-
'#EXTM3U\n',
|
|
3048
|
-
'#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="audio",URI="audio1"\n',
|
|
3049
|
-
'#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="audio",URI="audio2"\n',
|
|
3050
|
-
'#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="audio",URI="audio3"\n',
|
|
3051
|
-
'#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="audio",URI="audio4"\n',
|
|
3052
|
-
'#EXT-X-STREAM-INF:BANDWIDTH=400,CODECS="avc1,mp4a",',
|
|
3053
|
-
'RESOLUTION=1280x720,AUDIO="audio"\n',
|
|
3054
|
-
'video\n',
|
|
3055
|
-
].join('');
|
|
3056
|
-
|
|
3057
|
-
const videoMedia = [
|
|
3058
|
-
'#EXTM3U\n',
|
|
3059
|
-
'#EXT-X-PLAYLIST-TYPE:VOD\n',
|
|
3060
|
-
'#EXT-X-MAP:URI="v-init.mp4"\n',
|
|
3061
|
-
'#EXTINF:5,\n',
|
|
3062
|
-
'v1.mp4',
|
|
3063
|
-
].join('');
|
|
3064
|
-
|
|
3065
|
-
const audioMedia1 = [
|
|
3066
|
-
'#EXTM3U\n',
|
|
3067
|
-
'#EXT-X-PLAYLIST-TYPE:VOD\n',
|
|
3068
|
-
'#EXTINF:5,\n',
|
|
3069
|
-
'a1.mp3',
|
|
3070
|
-
].join('');
|
|
3071
|
-
|
|
3072
|
-
const audioMedia2 = [
|
|
3073
|
-
'#EXTM3U\n',
|
|
3074
|
-
'#EXT-X-PLAYLIST-TYPE:VOD\n',
|
|
3075
|
-
'#EXTINF:5,\n',
|
|
3076
|
-
'a1.aac',
|
|
3077
|
-
].join('');
|
|
3078
|
-
|
|
3079
|
-
const audioMedia3 = [
|
|
3080
|
-
'#EXTM3U\n',
|
|
3081
|
-
'#EXT-X-PLAYLIST-TYPE:VOD\n',
|
|
3082
|
-
'#EXTINF:5,\n',
|
|
3083
|
-
'a1.ac3',
|
|
3084
|
-
].join('');
|
|
3085
|
-
|
|
3086
|
-
const audioMedia4 = [
|
|
3087
|
-
'#EXTM3U\n',
|
|
3088
|
-
'#EXT-X-PLAYLIST-TYPE:VOD\n',
|
|
3089
|
-
'#EXTINF:5,\n',
|
|
3090
|
-
'a1.ec3',
|
|
3091
|
-
].join('');
|
|
3092
|
-
|
|
3093
|
-
fakeNetEngine
|
|
3094
|
-
.setResponseText('test:/master', master)
|
|
3095
|
-
.setResponseText('test:/video', videoMedia)
|
|
3096
|
-
.setResponseText('test:/audio1', audioMedia1)
|
|
3097
|
-
.setResponseText('test:/audio2', audioMedia2)
|
|
3098
|
-
.setResponseText('test:/audio3', audioMedia3)
|
|
3099
|
-
.setResponseText('test:/audio4', audioMedia4)
|
|
3100
|
-
.setResponseValue('test:/v-init.mp4', initSegmentData)
|
|
3101
|
-
.setResponseValue('test:/v1.mp4', segmentData);
|
|
3102
|
-
|
|
3103
|
-
const alwaysWarnSpy = jasmine.createSpy('shaka.log.alwaysWarn');
|
|
3104
|
-
shaka.log.alwaysWarn = shaka.test.Util.spyFunc(alwaysWarnSpy);
|
|
3105
|
-
|
|
3106
|
-
const manifest = await parser.start('test:/master', playerInterface);
|
|
3107
|
-
expect(manifest.variants.length).toBe(1);
|
|
3108
|
-
expect(manifest.variants[0].audio).toBe(null);
|
|
3109
|
-
|
|
3110
|
-
// We should log a warning when this happens.
|
|
3111
|
-
expect(alwaysWarnSpy).toHaveBeenCalled();
|
|
3112
|
-
});
|
|
3113
|
-
|
|
3114
3142
|
// Issue #1875
|
|
3115
3143
|
it('ignores audio groups on audio-only content', async () => {
|
|
3116
3144
|
// NOTE: To reproduce the original issue accurately, the two audio playlist
|
|
@@ -3146,6 +3174,7 @@ describe('HlsParser', () => {
|
|
|
3146
3174
|
stream.mime('audio/mp4', 'mp4a');
|
|
3147
3175
|
});
|
|
3148
3176
|
});
|
|
3177
|
+
manifest.sequenceMode = true;
|
|
3149
3178
|
});
|
|
3150
3179
|
|
|
3151
3180
|
fakeNetEngine
|
|
@@ -3328,6 +3357,7 @@ describe('HlsParser', () => {
|
|
|
3328
3357
|
stream.mime('audio/mp4', 'mp4a');
|
|
3329
3358
|
});
|
|
3330
3359
|
});
|
|
3360
|
+
manifest.sequenceMode = true;
|
|
3331
3361
|
});
|
|
3332
3362
|
|
|
3333
3363
|
await testHlsParser(master, media, manifest);
|
|
@@ -3365,6 +3395,7 @@ describe('HlsParser', () => {
|
|
|
3365
3395
|
stream.mime('audio/mp4', 'mp4a');
|
|
3366
3396
|
});
|
|
3367
3397
|
});
|
|
3398
|
+
manifest.sequenceMode = true;
|
|
3368
3399
|
});
|
|
3369
3400
|
|
|
3370
3401
|
await testHlsParser(master, media, manifest);
|
|
@@ -3403,6 +3434,7 @@ describe('HlsParser', () => {
|
|
|
3403
3434
|
stream.mime('audio/mp4', 'mp4a');
|
|
3404
3435
|
});
|
|
3405
3436
|
});
|
|
3437
|
+
manifest.sequenceMode = true;
|
|
3406
3438
|
});
|
|
3407
3439
|
|
|
3408
3440
|
await testHlsParser(master, media, manifest);
|
|
@@ -3442,6 +3474,7 @@ describe('HlsParser', () => {
|
|
|
3442
3474
|
stream.mime('audio/mp4', 'mp4a');
|
|
3443
3475
|
});
|
|
3444
3476
|
});
|
|
3477
|
+
manifest.sequenceMode = true;
|
|
3445
3478
|
});
|
|
3446
3479
|
|
|
3447
3480
|
await testHlsParser(master, media, manifest);
|
|
@@ -3472,4 +3505,54 @@ describe('HlsParser', () => {
|
|
|
3472
3505
|
jasmine.objectContaining(eventValue3));
|
|
3473
3506
|
});
|
|
3474
3507
|
});
|
|
3508
|
+
|
|
3509
|
+
it('parses media playlists directly', async () => {
|
|
3510
|
+
const media = [
|
|
3511
|
+
'#EXTM3U\n',
|
|
3512
|
+
'#EXT-X-PLAYLIST-TYPE:VOD\n',
|
|
3513
|
+
'#EXT-X-MAP:URI="init.mp4",BYTERANGE="616@0"\n',
|
|
3514
|
+
'#EXTINF:5,\n',
|
|
3515
|
+
'#EXT-X-BYTERANGE:121090@616\n',
|
|
3516
|
+
'main.mp4',
|
|
3517
|
+
].join('');
|
|
3518
|
+
|
|
3519
|
+
const manifest = shaka.test.ManifestGenerator.generate((manifest) => {
|
|
3520
|
+
manifest.sequenceMode = true;
|
|
3521
|
+
manifest.anyTimeline();
|
|
3522
|
+
manifest.addPartialVariant((variant) => {
|
|
3523
|
+
variant.addPartialStream(ContentType.VIDEO, (stream) => {
|
|
3524
|
+
stream.mime('video/mp2t', 'avc1.42E01E, mp4a.40.2');
|
|
3525
|
+
});
|
|
3526
|
+
});
|
|
3527
|
+
});
|
|
3528
|
+
|
|
3529
|
+
await testHlsParser(media, '', manifest);
|
|
3530
|
+
});
|
|
3531
|
+
|
|
3532
|
+
it('honors hls.mediaPlaylistFullMimeType', async () => {
|
|
3533
|
+
const media = [
|
|
3534
|
+
'#EXTM3U\n',
|
|
3535
|
+
'#EXT-X-PLAYLIST-TYPE:VOD\n',
|
|
3536
|
+
'#EXT-X-MAP:URI="init.mp4",BYTERANGE="616@0"\n',
|
|
3537
|
+
'#EXTINF:5,\n',
|
|
3538
|
+
'#EXT-X-BYTERANGE:121090@616\n',
|
|
3539
|
+
'main.mp4',
|
|
3540
|
+
].join('');
|
|
3541
|
+
|
|
3542
|
+
const config = shaka.util.PlayerConfiguration.createDefault().manifest;
|
|
3543
|
+
config.hls.mediaPlaylistFullMimeType = 'audio/webm; codecs="vorbis"';
|
|
3544
|
+
parser.configure(config);
|
|
3545
|
+
|
|
3546
|
+
const manifest = shaka.test.ManifestGenerator.generate((manifest) => {
|
|
3547
|
+
manifest.sequenceMode = true;
|
|
3548
|
+
manifest.anyTimeline();
|
|
3549
|
+
manifest.addPartialVariant((variant) => {
|
|
3550
|
+
variant.addPartialStream(ContentType.AUDIO, (stream) => {
|
|
3551
|
+
stream.mime('audio/webm', 'vorbis');
|
|
3552
|
+
});
|
|
3553
|
+
});
|
|
3554
|
+
});
|
|
3555
|
+
|
|
3556
|
+
await testHlsParser(media, '', manifest);
|
|
3557
|
+
});
|
|
3475
3558
|
});
|