shaka-player 4.3.13 → 4.3.14
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/ISSUE_TEMPLATE/bug_report.md +1 -1
- package/.github/ISSUE_TEMPLATE/question.md +7 -0
- package/.github/workflows/release-please.yaml +2 -27
- package/AUTHORS +5 -0
- package/CHANGELOG.md +87 -0
- package/CODE_OF_CONDUCT.md +1 -1
- package/CONTRIBUTORS +6 -0
- package/README.md +2 -0
- package/demo/common/asset.js +0 -1
- package/demo/common/assets.js +0 -1
- package/demo/custom.js +2 -0
- package/dist/demo.compiled.debug.js +1 -1
- package/dist/demo.compiled.debug.map +1 -1
- package/dist/demo.compiled.js +1 -1
- package/dist/demo.compiled.map +1 -1
- package/dist/deps.js +6 -6
- package/dist/locales.js +29 -29
- package/dist/receiver.compiled.debug.map +1 -1
- package/dist/receiver.compiled.map +1 -1
- package/dist/shaka-player.compiled.d.ts +25 -5
- package/dist/shaka-player.compiled.debug.d.ts +25 -5
- package/dist/shaka-player.compiled.debug.externs.js +73 -45
- package/dist/shaka-player.compiled.debug.js +414 -396
- package/dist/shaka-player.compiled.debug.map +4 -4
- package/dist/shaka-player.compiled.externs.js +73 -45
- package/dist/shaka-player.compiled.js +912 -903
- package/dist/shaka-player.compiled.map +4 -4
- package/dist/shaka-player.ui.d.ts +37 -6
- package/dist/shaka-player.ui.debug.d.ts +37 -6
- package/dist/shaka-player.ui.debug.externs.js +74 -45
- package/dist/shaka-player.ui.debug.js +438 -418
- package/dist/shaka-player.ui.debug.map +4 -4
- package/dist/shaka-player.ui.externs.js +74 -45
- package/dist/shaka-player.ui.js +1060 -1051
- package/dist/shaka-player.ui.map +4 -4
- package/docs/tutorials/ad_monetization.md +54 -0
- package/docs/tutorials/fairplay.md +2 -2
- package/docs/tutorials/index.json +1 -0
- package/docs/tutorials/plugins.md +4 -0
- package/docs/tutorials/text-displayer.md +15 -0
- package/docs/tutorials/ui-customization.md +29 -0
- package/docs/tutorials/ui.md +4 -1
- package/externs/lcevc.js +6 -0
- package/externs/shaka/manifest_parser.js +8 -0
- package/externs/shaka/player.js +1 -1
- package/externs/shaka/text.js +3 -1
- package/lib/abr/simple_abr_manager.js +1 -1
- package/lib/ads/client_side_ad.js +9 -0
- package/lib/ads/client_side_ad_manager.js +7 -2
- package/lib/ads/server_side_ad_manager.js +4 -0
- package/lib/cast/cast_proxy.js +18 -0
- package/lib/cea/cea608_memory.js +1 -1
- package/lib/cea/cea_decoder.js +11 -1
- package/lib/dash/dash_parser.js +75 -41
- package/lib/dash/mpd_utils.js +39 -27
- package/lib/hls/hls_parser.js +90 -27
- package/lib/hls/hls_utils.js +5 -5
- package/lib/media/content_workarounds.js +30 -0
- package/lib/media/drm_engine.js +16 -12
- package/lib/media/media_source_engine.js +4 -2
- package/lib/media/play_rate_controller.js +1 -0
- package/lib/media/presentation_timeline.js +11 -0
- package/lib/media/quality_observer.js +1 -1
- package/lib/media/segment_reference.js +5 -2
- package/lib/media/streaming_engine.js +50 -30
- package/lib/media/transmuxer.js +1 -1
- package/lib/media/video_wrapper.js +4 -3
- package/lib/offline/offline_manifest_parser.js +5 -0
- package/lib/offline/session_deleter.js +0 -8
- package/lib/offline/storage.js +4 -1
- package/lib/player.js +79 -18
- package/lib/polyfill/media_capabilities.js +23 -3
- package/lib/text/mp4_ttml_parser.js +2 -2
- package/lib/text/simple_text_displayer.js +7 -6
- package/lib/text/srt_text_parser.js +2 -2
- package/lib/text/text_engine.js +3 -2
- package/lib/text/ttml_text_parser.js +33 -5
- package/lib/text/ui_text_displayer.js +81 -15
- package/lib/text/vtt_text_parser.js +15 -2
- package/lib/util/buffer_utils.js +26 -4
- package/lib/util/cmcd_manager.js +10 -0
- package/lib/util/fairplay_utils.js +1 -1
- package/lib/util/manifest_parser_utils.js +14 -8
- package/lib/util/object_utils.js +1 -1
- package/lib/util/periods.js +91 -36
- package/lib/util/platform.js +39 -2
- package/lib/util/player_configuration.js +5 -3
- package/lib/util/stream_utils.js +36 -28
- package/lib/util/xml_utils.js +5 -0
- package/package.json +2 -2
- package/test/cea/cea_decoder_unit.js +40 -0
- package/test/dash/dash_parser_live_unit.js +5 -8
- package/test/hls/hls_parser_unit.js +50 -3
- package/test/media/buffering_observer_unit.js +13 -0
- package/test/media/content_workarounds_unit.js +1 -1
- package/test/media/drm_engine_unit.js +30 -3
- package/test/media/media_source_engine_unit.js +1 -1
- package/test/media/transmuxer_integration.js +2 -2
- package/test/offline/storage_integration.js +3 -0
- package/test/player_integration.js +40 -40
- package/test/player_unit.js +31 -0
- package/test/polyfill/media_capabilities_unit.js +12 -6
- package/test/test/util/fake_drm_engine.js +7 -0
- package/test/test/util/layout_tests.js +2 -1
- package/test/test/util/simple_fakes.js +2 -2
- package/test/test/util/test_scheme.js +3 -0
- package/test/test/util/waiter.js +5 -0
- package/test/text/mp4_ttml_parser_unit.js +4 -4
- package/test/text/simple_text_displayer_unit.js +1 -1
- package/test/text/srt_text_parser_unit.js +1 -1
- package/test/text/text_engine_unit.js +6 -0
- package/test/text/ttml_text_parser_unit.js +49 -18
- package/test/text/ui_text_displayer_unit.js +9 -0
- package/test/util/buffer_utils_unit.js +1 -1
- package/test/util/periods_unit.js +113 -0
- package/third_party/closure-uri/uri.js +1 -1
- package/ui/externs/ui.js +24 -0
- package/ui/language_utils.js +11 -5
- package/ui/play_button.js +20 -0
- package/ui/playback_rate_selection.js +4 -0
- package/ui/presentation_time.js +5 -5
- package/ui/small_play_button.js +2 -2
- package/ui/volume_bar.js +10 -6
- /package/.github/{config.yml → ISSUE_TEMPLATE/config.yml} +0 -0
|
@@ -607,12 +607,12 @@ describe('DashParser Live', () => {
|
|
|
607
607
|
expect(onError).toHaveBeenCalledTimes(1);
|
|
608
608
|
});
|
|
609
609
|
|
|
610
|
-
it('
|
|
610
|
+
it('still updates when @minimumUpdatePeriod is zero', async () => {
|
|
611
611
|
const lines = [
|
|
612
612
|
'<SegmentTemplate startNumber="1" media="s$Number$.mp4" duration="2" />',
|
|
613
613
|
];
|
|
614
614
|
// updateTime parameter sets @minimumUpdatePeriod in the manifest.
|
|
615
|
-
const manifestText = makeSimpleLiveManifestText(lines, updateTime);
|
|
615
|
+
const manifestText = makeSimpleLiveManifestText(lines, /* updateTime= */ 0);
|
|
616
616
|
|
|
617
617
|
/** @type {!jasmine.Spy} */
|
|
618
618
|
const tickAfter = updateTickSpy();
|
|
@@ -622,16 +622,14 @@ describe('DashParser Live', () => {
|
|
|
622
622
|
await parser.start('dummy://foo', playerInterface);
|
|
623
623
|
|
|
624
624
|
expect(tickAfter).toHaveBeenCalledTimes(1);
|
|
625
|
-
const delay = tickAfter.calls.mostRecent().args[0];
|
|
626
|
-
expect(delay).toBe(updateTime);
|
|
627
625
|
});
|
|
628
626
|
|
|
629
|
-
it('
|
|
627
|
+
it('uses @minimumUpdatePeriod', async () => {
|
|
630
628
|
const lines = [
|
|
631
629
|
'<SegmentTemplate startNumber="1" media="s$Number$.mp4" duration="2" />',
|
|
632
630
|
];
|
|
633
631
|
// updateTime parameter sets @minimumUpdatePeriod in the manifest.
|
|
634
|
-
const manifestText = makeSimpleLiveManifestText(lines,
|
|
632
|
+
const manifestText = makeSimpleLiveManifestText(lines, updateTime);
|
|
635
633
|
|
|
636
634
|
/** @type {!jasmine.Spy} */
|
|
637
635
|
const tickAfter = updateTickSpy();
|
|
@@ -640,10 +638,9 @@ describe('DashParser Live', () => {
|
|
|
640
638
|
fakeNetEngine.setResponseText('dummy://foo', manifestText);
|
|
641
639
|
await parser.start('dummy://foo', playerInterface);
|
|
642
640
|
|
|
643
|
-
const waitTime = shaka.dash.DashParser['MIN_UPDATE_PERIOD_'];
|
|
644
641
|
expect(tickAfter).toHaveBeenCalledTimes(1);
|
|
645
642
|
const delay = tickAfter.calls.mostRecent().args[0];
|
|
646
|
-
expect(delay).toBe(
|
|
643
|
+
expect(delay).toBe(updateTime);
|
|
647
644
|
});
|
|
648
645
|
|
|
649
646
|
it('does not update when @minimumUpdatePeriod is missing', async () => {
|
|
@@ -739,6 +739,45 @@ describe('HlsParser', () => {
|
|
|
739
739
|
await testHlsParser(master, media, manifest);
|
|
740
740
|
});
|
|
741
741
|
|
|
742
|
+
it('parses audio+video variant with global closed captions', async () => {
|
|
743
|
+
const master = [
|
|
744
|
+
'#EXTM3U\n',
|
|
745
|
+
'#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aud1",LANGUAGE="eng",CHANNELS="2",',
|
|
746
|
+
'URI="audio"\n',
|
|
747
|
+
'#EXT-X-MEDIA:TYPE=CLOSED-CAPTIONS,GROUP-ID="cap1",LANGUAGE="eng",',
|
|
748
|
+
'INSTREAM-ID="CC1"\n',
|
|
749
|
+
'#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="avc1,mp4a",',
|
|
750
|
+
'RESOLUTION=960x540,FRAME-RATE=60,AUDIO="aud1"\n',
|
|
751
|
+
'video\n',
|
|
752
|
+
].join('');
|
|
753
|
+
|
|
754
|
+
const media = [
|
|
755
|
+
'#EXTM3U\n',
|
|
756
|
+
'#EXT-X-PLAYLIST-TYPE:VOD\n',
|
|
757
|
+
'#EXT-X-MAP:URI="init.mp4",BYTERANGE="616@0"\n',
|
|
758
|
+
'#EXTINF:5,\n',
|
|
759
|
+
'#EXT-X-BYTERANGE:121090@616\n',
|
|
760
|
+
'main.mp4',
|
|
761
|
+
].join('');
|
|
762
|
+
|
|
763
|
+
const closedCaptions = new Map([['CC1', 'en']]);
|
|
764
|
+
const manifest = shaka.test.ManifestGenerator.generate((manifest) => {
|
|
765
|
+
manifest.anyTimeline();
|
|
766
|
+
manifest.addPartialVariant((variant) => {
|
|
767
|
+
variant.addPartialStream(ContentType.VIDEO, (stream) => {
|
|
768
|
+
stream.closedCaptions = closedCaptions;
|
|
769
|
+
stream.mime('video/mp4', 'avc1');
|
|
770
|
+
});
|
|
771
|
+
variant.addPartialStream(ContentType.AUDIO, (stream) => {
|
|
772
|
+
stream.mime('audio/mp4', 'mp4a');
|
|
773
|
+
});
|
|
774
|
+
});
|
|
775
|
+
manifest.sequenceMode = true;
|
|
776
|
+
});
|
|
777
|
+
|
|
778
|
+
await testHlsParser(master, media, manifest);
|
|
779
|
+
});
|
|
780
|
+
|
|
742
781
|
it('parses audio+video variant with no closed captions', async () => {
|
|
743
782
|
const master = [
|
|
744
783
|
'#EXTM3U\n',
|
|
@@ -2926,7 +2965,8 @@ describe('HlsParser', () => {
|
|
|
2926
2965
|
const media = [
|
|
2927
2966
|
'#EXTM3U\n',
|
|
2928
2967
|
'#EXT-X-PLAYLIST-TYPE:VOD\n',
|
|
2929
|
-
'#EXT-X-
|
|
2968
|
+
'#EXT-X-BYTERANGE:616@0\n',
|
|
2969
|
+
'#EXT-X-MAP:URI="init.mp4"\n',
|
|
2930
2970
|
'#EXTINF:5,\n',
|
|
2931
2971
|
'main.mp4\n',
|
|
2932
2972
|
'#EXT-X-MAP:URI="init2.mp4",BYTERANGE="616@0"\n',
|
|
@@ -2954,8 +2994,15 @@ describe('HlsParser', () => {
|
|
|
2954
2994
|
// uri.
|
|
2955
2995
|
const initSegments = Array.from(actualVideo.segmentIndex).map(
|
|
2956
2996
|
(seg) => seg.initSegmentReference);
|
|
2957
|
-
expect(initSegments
|
|
2958
|
-
|
|
2997
|
+
expect(initSegments.length).toBe(2);
|
|
2998
|
+
const firstInitSegment = initSegments[0];
|
|
2999
|
+
expect(firstInitSegment.getUris()[0]).toBe('test:/init.mp4');
|
|
3000
|
+
expect(firstInitSegment.startByte).toBe(0);
|
|
3001
|
+
expect(firstInitSegment.endByte).toBe(615);
|
|
3002
|
+
const secondInitSegment = initSegments[1];
|
|
3003
|
+
expect(secondInitSegment.getUris()[0]).toBe('test:/init2.mp4');
|
|
3004
|
+
expect(secondInitSegment.startByte).toBe(0);
|
|
3005
|
+
expect(secondInitSegment.endByte).toBe(615);
|
|
2959
3006
|
});
|
|
2960
3007
|
|
|
2961
3008
|
it('parses variants encrypted with AES-128', async () => {
|
|
@@ -11,15 +11,28 @@ describe('BufferingObserver', () => {
|
|
|
11
11
|
const thresholdAfterStarving = 5;
|
|
12
12
|
const thresholdAfterSatisfied = 2;
|
|
13
13
|
|
|
14
|
+
const originalDateNow = Date.now;
|
|
15
|
+
|
|
16
|
+
/** @type {!Date} */
|
|
17
|
+
let baseTime;
|
|
18
|
+
|
|
14
19
|
/** @type {!shaka.media.BufferingObserver} */
|
|
15
20
|
let controller;
|
|
16
21
|
|
|
17
22
|
beforeEach(() => {
|
|
23
|
+
// Any test using Date needs to have Date mocked to avoid flake.
|
|
24
|
+
baseTime = new Date(2015, 11, 30);
|
|
25
|
+
Date.now = () => baseTime.getTime();
|
|
26
|
+
|
|
18
27
|
controller = new BufferingObserver(
|
|
19
28
|
thresholdAfterStarving,
|
|
20
29
|
thresholdAfterSatisfied);
|
|
21
30
|
});
|
|
22
31
|
|
|
32
|
+
afterEach(() => {
|
|
33
|
+
Date.now = originalDateNow;
|
|
34
|
+
});
|
|
35
|
+
|
|
23
36
|
describe('when satisfied', () => {
|
|
24
37
|
beforeEach(() => {
|
|
25
38
|
controller.setState(State.SATISFIED);
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
describe('ContentWorkarounds', () => {
|
|
8
8
|
const encryptionBoxes = {
|
|
9
9
|
'encv': ['hev1', 'hvc1', 'avc1', 'avc3'],
|
|
10
|
-
'enca': ['ac-3', 'ec-3', 'mp4a'],
|
|
10
|
+
'enca': ['ac-3', 'ec-3', 'ac-4', 'mp4a'],
|
|
11
11
|
};
|
|
12
12
|
for (const encryptionBox of Object.keys(encryptionBoxes)) {
|
|
13
13
|
for (const box of encryptionBoxes[encryptionBox]) {
|
|
@@ -2100,7 +2100,7 @@ describe('DrmEngine', () => {
|
|
|
2100
2100
|
videoRobustness: 'really_really_ridiculously_good',
|
|
2101
2101
|
serverCertificate: serverCert,
|
|
2102
2102
|
serverCertificateUri: '',
|
|
2103
|
-
initData: ['
|
|
2103
|
+
initData: [{keyId: 'a'}],
|
|
2104
2104
|
keyIds: new Set(['deadbeefdeadbeefdeadbeefdeadbeef']),
|
|
2105
2105
|
};
|
|
2106
2106
|
const drmInfoAudio = {
|
|
@@ -2111,7 +2111,7 @@ describe('DrmEngine', () => {
|
|
|
2111
2111
|
audioRobustness: 'good',
|
|
2112
2112
|
serverCertificate: undefined,
|
|
2113
2113
|
serverCertificateUri: '',
|
|
2114
|
-
initData: [
|
|
2114
|
+
initData: [{keyId: 'b'}],
|
|
2115
2115
|
keyIds: new Set(['eadbeefdeadbeefdeadbeefdeadbeefd']),
|
|
2116
2116
|
};
|
|
2117
2117
|
const drmInfoDesired = {
|
|
@@ -2123,7 +2123,7 @@ describe('DrmEngine', () => {
|
|
|
2123
2123
|
videoRobustness: 'really_really_ridiculously_good',
|
|
2124
2124
|
serverCertificate: serverCert,
|
|
2125
2125
|
serverCertificateUri: '',
|
|
2126
|
-
initData: ['
|
|
2126
|
+
initData: [{keyId: 'a'}, {keyId: 'b'}],
|
|
2127
2127
|
keyIds: new Set([
|
|
2128
2128
|
'deadbeefdeadbeefdeadbeefdeadbeef',
|
|
2129
2129
|
'eadbeefdeadbeefdeadbeefdeadbeefd',
|
|
@@ -2177,6 +2177,33 @@ describe('DrmEngine', () => {
|
|
|
2177
2177
|
[drmInfoAudio]);
|
|
2178
2178
|
expect(returned).toEqual([drmInfoDesired]);
|
|
2179
2179
|
});
|
|
2180
|
+
|
|
2181
|
+
it('does not match incompatible drmInfos', () => {
|
|
2182
|
+
// Different key systems do not match.
|
|
2183
|
+
const drmInfo1 = {
|
|
2184
|
+
keySystem: 'drm.abc',
|
|
2185
|
+
licenseServerUri: undefined,
|
|
2186
|
+
distinctiveIdentifierRequired: false,
|
|
2187
|
+
persistentStateRequired: false,
|
|
2188
|
+
serverCertificate: undefined,
|
|
2189
|
+
serverCertificateUri: '',
|
|
2190
|
+
initData: [],
|
|
2191
|
+
keyIds: new Set(),
|
|
2192
|
+
};
|
|
2193
|
+
const drmInfo2 = {
|
|
2194
|
+
keySystem: 'drm.foobar',
|
|
2195
|
+
licenseServerUri: undefined,
|
|
2196
|
+
distinctiveIdentifierRequired: false,
|
|
2197
|
+
persistentStateRequired: false,
|
|
2198
|
+
serverCertificate: undefined,
|
|
2199
|
+
serverCertificateUri: '',
|
|
2200
|
+
initData: [],
|
|
2201
|
+
keyIds: new Set(),
|
|
2202
|
+
};
|
|
2203
|
+
const returned1 = shaka.media.DrmEngine.getCommonDrmInfos(
|
|
2204
|
+
[drmInfo1], [drmInfo2]);
|
|
2205
|
+
expect(returned1).toEqual([]);
|
|
2206
|
+
});
|
|
2180
2207
|
}); // describe('getCommonDrmInfos')
|
|
2181
2208
|
|
|
2182
2209
|
describe('configure', () => {
|
|
@@ -560,7 +560,7 @@ describe('MediaSourceEngine', () => {
|
|
|
560
560
|
await mediaSourceEngine.appendBuffer(
|
|
561
561
|
ContentType.TEXT, data, reference, /* hasClosedCaptions= */ false);
|
|
562
562
|
expect(mockTextEngine.appendBuffer).toHaveBeenCalledWith(
|
|
563
|
-
data, 0, 10);
|
|
563
|
+
data, 0, 10, 'foo://bar');
|
|
564
564
|
});
|
|
565
565
|
|
|
566
566
|
it('appends transmuxed data and captions', async () => {
|
|
@@ -102,8 +102,8 @@ describe('Transmuxer', () => {
|
|
|
102
102
|
});
|
|
103
103
|
});
|
|
104
104
|
|
|
105
|
-
it('
|
|
106
|
-
expect(transmuxer.
|
|
105
|
+
it('getOriginalMimeType returns the correct mimeType', () => {
|
|
106
|
+
expect(transmuxer.getOriginalMimeType()).toBe(transmuxerMimeType);
|
|
107
107
|
});
|
|
108
108
|
|
|
109
109
|
describe('transmuxing', () => {
|
|
@@ -59,6 +59,46 @@ describe('Player', () => {
|
|
|
59
59
|
document.body.removeChild(video);
|
|
60
60
|
});
|
|
61
61
|
|
|
62
|
+
describe('seek range', () => {
|
|
63
|
+
// Regression test for Issue #3675.
|
|
64
|
+
it('has a reasonable seek range after going from live to VOD', async () => {
|
|
65
|
+
const netEngine = player.getNetworkingEngine();
|
|
66
|
+
const startTime = Date.now();
|
|
67
|
+
netEngine.registerRequestFilter((type, request) => {
|
|
68
|
+
if (type != shaka.net.NetworkingEngine.RequestType.MANIFEST) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
// Simulate a live stream by providing different manifests over time.
|
|
72
|
+
const time = (Date.now() - startTime) / 1000;
|
|
73
|
+
const manifestNumber = Math.min(5, Math.floor(0.5 + time / 2));
|
|
74
|
+
request.uris = [
|
|
75
|
+
'/base/test/test/assets/3675/dash_' + manifestNumber + '.mpd',
|
|
76
|
+
];
|
|
77
|
+
console.log('getting manifest', request.uris);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// Play the stream.
|
|
81
|
+
await player.load('/base/test/test/assets/3675/dash_0.mpd');
|
|
82
|
+
await video.play();
|
|
83
|
+
|
|
84
|
+
// Wait for the stream to be over.
|
|
85
|
+
eventManager.listen(player, 'error', Util.spyFunc(onErrorSpy));
|
|
86
|
+
/** @type {shaka.test.Waiter} */
|
|
87
|
+
const waiter = new shaka.test.Waiter(eventManager)
|
|
88
|
+
.setPlayer(player)
|
|
89
|
+
.timeoutAfter(40)
|
|
90
|
+
.failOnTimeout(true);
|
|
91
|
+
await waiter.waitForEnd(video);
|
|
92
|
+
|
|
93
|
+
// The stream should have transitioned to VOD by now.
|
|
94
|
+
expect(player.isLive()).toBe(false);
|
|
95
|
+
|
|
96
|
+
// Check that the final seek range is as expected.
|
|
97
|
+
const seekRange = player.seekRange();
|
|
98
|
+
expect(seekRange.end).toBeCloseTo(14);
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
|
|
62
102
|
describe('attach', () => {
|
|
63
103
|
beforeEach(async () => {
|
|
64
104
|
// To test attach, we want to construct a player without a video element
|
|
@@ -903,46 +943,6 @@ describe('Player', () => {
|
|
|
903
943
|
});
|
|
904
944
|
});
|
|
905
945
|
|
|
906
|
-
describe('seek range', () => {
|
|
907
|
-
// Regression test for Issue #3675.
|
|
908
|
-
it('has a reasonable seek range after going from live to VOD', async () => {
|
|
909
|
-
const netEngine = player.getNetworkingEngine();
|
|
910
|
-
const startTime = Date.now();
|
|
911
|
-
netEngine.registerRequestFilter((type, request) => {
|
|
912
|
-
if (type != shaka.net.NetworkingEngine.RequestType.MANIFEST) {
|
|
913
|
-
return;
|
|
914
|
-
}
|
|
915
|
-
// Simulate a live stream by providing different manifests over time.
|
|
916
|
-
const time = (Date.now() - startTime) / 1000;
|
|
917
|
-
const manifestNumber = Math.min(5, Math.floor(0.5 + time / 2));
|
|
918
|
-
request.uris = [
|
|
919
|
-
'/base/test/test/assets/3675/dash_' + manifestNumber + '.mpd',
|
|
920
|
-
];
|
|
921
|
-
console.log('getting manifest', request.uris);
|
|
922
|
-
});
|
|
923
|
-
|
|
924
|
-
// Play the stream.
|
|
925
|
-
await player.load('/base/test/test/assets/3675/dash_0.mpd');
|
|
926
|
-
await video.play();
|
|
927
|
-
|
|
928
|
-
// Wait for the stream to be over.
|
|
929
|
-
eventManager.listen(player, 'error', Util.spyFunc(onErrorSpy));
|
|
930
|
-
/** @type {shaka.test.Waiter} */
|
|
931
|
-
const waiter = new shaka.test.Waiter(eventManager)
|
|
932
|
-
.setPlayer(player)
|
|
933
|
-
.timeoutAfter(40)
|
|
934
|
-
.failOnTimeout(true);
|
|
935
|
-
await waiter.waitForEnd(video);
|
|
936
|
-
|
|
937
|
-
// The stream should have transitioned to VOD by now.
|
|
938
|
-
expect(player.isLive()).toBe(false);
|
|
939
|
-
|
|
940
|
-
// Check that the final seek range is as expected.
|
|
941
|
-
const seekRange = player.seekRange();
|
|
942
|
-
expect(seekRange.end).toBeCloseTo(14);
|
|
943
|
-
});
|
|
944
|
-
});
|
|
945
|
-
|
|
946
946
|
describe('buffering', () => {
|
|
947
947
|
const startBuffering = jasmine.objectContaining({buffering: true});
|
|
948
948
|
const endBuffering = jasmine.objectContaining({buffering: false});
|
package/test/player_unit.js
CHANGED
|
@@ -3291,6 +3291,37 @@ describe('Player', () => {
|
|
|
3291
3291
|
expect(tracks[0].id).toBe(2);
|
|
3292
3292
|
});
|
|
3293
3293
|
|
|
3294
|
+
it('removes based on frameRate', async () => {
|
|
3295
|
+
manifest = shaka.test.ManifestGenerator.generate((manifest) => {
|
|
3296
|
+
manifest.addVariant(0, (variant) => {
|
|
3297
|
+
variant.addVideo(1, (stream) => {
|
|
3298
|
+
stream.frameRate = 15;
|
|
3299
|
+
});
|
|
3300
|
+
});
|
|
3301
|
+
|
|
3302
|
+
manifest.addVariant(1, (variant) => {
|
|
3303
|
+
variant.addVideo(2, (stream) => {
|
|
3304
|
+
stream.frameRate = 30;
|
|
3305
|
+
});
|
|
3306
|
+
});
|
|
3307
|
+
|
|
3308
|
+
manifest.addVariant(2, (variant) => {
|
|
3309
|
+
variant.addVideo(3, (stream) => {
|
|
3310
|
+
stream.frameRate = 60;
|
|
3311
|
+
});
|
|
3312
|
+
});
|
|
3313
|
+
});
|
|
3314
|
+
|
|
3315
|
+
await player.load(fakeManifestUri, 0, fakeMimeType);
|
|
3316
|
+
expect(player.getVariantTracks().length).toBe(3);
|
|
3317
|
+
|
|
3318
|
+
player.configure({restrictions: {minFrameRate: 20, maxFrameRate: 40}});
|
|
3319
|
+
|
|
3320
|
+
const tracks = player.getVariantTracks();
|
|
3321
|
+
expect(tracks.length).toBe(1);
|
|
3322
|
+
expect(tracks[0].id).toBe(1);
|
|
3323
|
+
});
|
|
3324
|
+
|
|
3294
3325
|
it('removes the whole variant if one stream is restricted', async () => {
|
|
3295
3326
|
manifest = shaka.test.ManifestGenerator.generate((manifest) => {
|
|
3296
3327
|
manifest.addVariant(0, (variant) => {
|
|
@@ -178,8 +178,10 @@ describe('MediaCapabilities', () => {
|
|
|
178
178
|
await navigator.mediaCapabilities.decodingInfo(mockDecodingConfig);
|
|
179
179
|
|
|
180
180
|
expect(mockCanDisplayType).not.toHaveBeenCalled();
|
|
181
|
-
// 1 (during install()) +
|
|
182
|
-
|
|
181
|
+
// 1 (during install()) +
|
|
182
|
+
// 1 (for video config check) +
|
|
183
|
+
// 1 (for audio config check).
|
|
184
|
+
expect(isChromecastSpy).toHaveBeenCalledTimes(3);
|
|
183
185
|
// 1 (fallback in canCastDisplayType()) +
|
|
184
186
|
// 1 (mockDecodingConfig.audio).
|
|
185
187
|
expect(supportMap.has(mockDecodingConfig.video.contentType))
|
|
@@ -201,8 +203,10 @@ describe('MediaCapabilities', () => {
|
|
|
201
203
|
await navigator.mediaCapabilities.decodingInfo(mockDecodingConfig);
|
|
202
204
|
|
|
203
205
|
expect(mockCanDisplayType).not.toHaveBeenCalled();
|
|
204
|
-
// 1 (during install()) +
|
|
205
|
-
|
|
206
|
+
// 1 (during install()) +
|
|
207
|
+
// 1 (for video config check) +
|
|
208
|
+
// 1 (for audio config check).
|
|
209
|
+
expect(isChromecastSpy).toHaveBeenCalledTimes(3);
|
|
206
210
|
// 1 (fallback in canCastDisplayType()) +
|
|
207
211
|
// 1 (mockDecodingConfig.audio).
|
|
208
212
|
expect(supportMap.has(mockDecodingConfig.video.contentType))
|
|
@@ -248,8 +252,10 @@ describe('MediaCapabilities', () => {
|
|
|
248
252
|
shaka.polyfill.MediaCapabilities.install();
|
|
249
253
|
await navigator.mediaCapabilities.decodingInfo(mockDecodingConfig);
|
|
250
254
|
|
|
251
|
-
// 1 (during install()) +
|
|
252
|
-
|
|
255
|
+
// 1 (during install()) +
|
|
256
|
+
// 1 (for video config check) +
|
|
257
|
+
// 1 (for audio config check).
|
|
258
|
+
expect(isChromecastSpy).toHaveBeenCalledTimes(3);
|
|
253
259
|
// 1 (mockDecodingConfig.audio).
|
|
254
260
|
expect(supportMap.has(chromecastType)).toBe(true);
|
|
255
261
|
// Called once in canCastDisplayType.
|
|
@@ -100,4 +100,11 @@ shaka.test.FakeDrmEngine = class {
|
|
|
100
100
|
// Copy the values to break the reference to the input value.
|
|
101
101
|
this.offlineSessions_ = sessions.map((s) => s);
|
|
102
102
|
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* @override
|
|
106
|
+
*/
|
|
107
|
+
hasManifestInitData() {
|
|
108
|
+
return true;
|
|
109
|
+
}
|
|
103
110
|
};
|
|
@@ -377,7 +377,8 @@ shaka.test.NativeTextLayoutTests = class extends shaka.test.TextLayoutTests {
|
|
|
377
377
|
|
|
378
378
|
/** @override */
|
|
379
379
|
recreateTextDisplayer() {
|
|
380
|
-
this.textDisplayer = new shaka.text.SimpleTextDisplayer(
|
|
380
|
+
this.textDisplayer = new shaka.text.SimpleTextDisplayer(
|
|
381
|
+
this.video, shaka.Player.TextTrackLabel);
|
|
381
382
|
this.textDisplayer.setTextVisibility(true);
|
|
382
383
|
}
|
|
383
384
|
|
|
@@ -520,7 +520,7 @@ shaka.test.FakeTransmuxer = class {
|
|
|
520
520
|
jasmine.createSpy('transmux').and.returnValue(Promise.resolve(output));
|
|
521
521
|
|
|
522
522
|
/** @type {!jasmine.Spy} */
|
|
523
|
-
this.
|
|
524
|
-
jasmine.createSpy('
|
|
523
|
+
this.getOriginalMimeType =
|
|
524
|
+
jasmine.createSpy('getOriginalMimeType').and.returnValue('mimeType');
|
|
525
525
|
}
|
|
526
526
|
};
|
package/test/test/util/waiter.js
CHANGED
|
@@ -184,6 +184,7 @@ shaka.test.Waiter = class {
|
|
|
184
184
|
}
|
|
185
185
|
this.eventManager_.unlisten(mediaElement, 'timeupdate');
|
|
186
186
|
this.eventManager_.unlisten(mediaElement, 'ended');
|
|
187
|
+
this.eventManager_.unlisten(mediaElement, 'durationchange');
|
|
187
188
|
};
|
|
188
189
|
|
|
189
190
|
const p = new Promise((resolve) => {
|
|
@@ -202,6 +203,10 @@ shaka.test.Waiter = class {
|
|
|
202
203
|
timer.tickEvery(/* seconds= */ 1);
|
|
203
204
|
this.eventManager_.listen(mediaElement, 'timeupdate', check);
|
|
204
205
|
this.eventManager_.listen(mediaElement, 'ended', check);
|
|
206
|
+
// Some tests on Safari expose a condition where the ended flag can be
|
|
207
|
+
// found in the desired state in a brief window during a durationchange
|
|
208
|
+
// event.
|
|
209
|
+
this.eventManager_.listen(mediaElement, 'durationchange', check);
|
|
205
210
|
});
|
|
206
211
|
|
|
207
212
|
return this.waitUntilGeneric_(goalName, p, cleanup, mediaElement);
|
|
@@ -43,7 +43,7 @@ describe('Mp4TtmlParser', () => {
|
|
|
43
43
|
parser.parseInit(ttmlInitSegment);
|
|
44
44
|
const time =
|
|
45
45
|
{periodStart: 0, segmentStart: 0, segmentEnd: 60, vttOffset: 0};
|
|
46
|
-
const ret = parser.parseMedia(ttmlSegmentMultipleMDAT, time);
|
|
46
|
+
const ret = parser.parseMedia(ttmlSegmentMultipleMDAT, time, null);
|
|
47
47
|
// Bodies.
|
|
48
48
|
expect(ret.length).toBe(2);
|
|
49
49
|
// Divs.
|
|
@@ -63,10 +63,10 @@ describe('Mp4TtmlParser', () => {
|
|
|
63
63
|
const parser = new shaka.text.Mp4TtmlParser();
|
|
64
64
|
parser.parseInit(ttmlInitSegment);
|
|
65
65
|
|
|
66
|
-
const ret1 = parser.parseMedia(ttmlSegment, time1);
|
|
66
|
+
const ret1 = parser.parseMedia(ttmlSegment, time1, null);
|
|
67
67
|
expect(ret1.length).toBeGreaterThan(0);
|
|
68
68
|
|
|
69
|
-
const ret2 = parser.parseMedia(ttmlSegment, time2);
|
|
69
|
+
const ret2 = parser.parseMedia(ttmlSegment, time2, null);
|
|
70
70
|
expect(ret2.length).toBeGreaterThan(0);
|
|
71
71
|
|
|
72
72
|
expect(ret2[0].startTime).toBe(ret1[0].startTime + 7);
|
|
@@ -164,7 +164,7 @@ describe('Mp4TtmlParser', () => {
|
|
|
164
164
|
parser.parseInit(ttmlInitSegment);
|
|
165
165
|
const time =
|
|
166
166
|
{periodStart: 0, segmentStart: 0, segmentEnd: 60, vttOffset: 0};
|
|
167
|
-
const result = parser.parseMedia(ttmlSegment, time);
|
|
167
|
+
const result = parser.parseMedia(ttmlSegment, time, null);
|
|
168
168
|
shaka.test.TtmlUtils.verifyHelper(
|
|
169
169
|
cues, result, {startTime: 23, endTime: 53.5});
|
|
170
170
|
});
|
|
@@ -18,7 +18,7 @@ describe('SimpleTextDisplayer', () => {
|
|
|
18
18
|
|
|
19
19
|
beforeEach(() => {
|
|
20
20
|
video = new shaka.test.FakeVideo();
|
|
21
|
-
displayer = new SimpleTextDisplayer(video);
|
|
21
|
+
displayer = new SimpleTextDisplayer(video, shaka.Player.TextTrackLabel);
|
|
22
22
|
|
|
23
23
|
expect(video.textTracks.length).toBe(1);
|
|
24
24
|
mockTrack = /** @type {!shaka.test.FakeTextTrack} */ (video.textTracks[0]);
|
|
@@ -71,7 +71,7 @@ describe('SrtTextParser', () => {
|
|
|
71
71
|
const data = BufferUtils.toUint8(StringUtils.toUTF8(text));
|
|
72
72
|
|
|
73
73
|
const parser = new shaka.text.SrtTextParser();
|
|
74
|
-
const result = parser.parseMedia(data, time);
|
|
74
|
+
const result = parser.parseMedia(data, time, null);
|
|
75
75
|
|
|
76
76
|
const expected = cues.map((cue) => {
|
|
77
77
|
if (cue.nestedCues) {
|
|
@@ -95,6 +95,7 @@ describe('TextEngine', () => {
|
|
|
95
95
|
expect(mockParseMedia).toHaveBeenCalledOnceMoreWith([
|
|
96
96
|
dummyData,
|
|
97
97
|
{periodStart: 0, segmentStart: 0, segmentEnd: 3, vttOffset: 0},
|
|
98
|
+
undefined,
|
|
98
99
|
]);
|
|
99
100
|
|
|
100
101
|
expect(mockDisplayer.appendSpy).toHaveBeenCalledOnceMoreWith([
|
|
@@ -110,6 +111,7 @@ describe('TextEngine', () => {
|
|
|
110
111
|
expect(mockParseMedia).toHaveBeenCalledOnceMoreWith([
|
|
111
112
|
dummyData,
|
|
112
113
|
{periodStart: 0, segmentStart: 3, segmentEnd: 5, vttOffset: 0},
|
|
114
|
+
undefined,
|
|
113
115
|
]);
|
|
114
116
|
|
|
115
117
|
expect(mockDisplayer.appendSpy).toHaveBeenCalledOnceMoreWith([
|
|
@@ -262,6 +264,7 @@ describe('TextEngine', () => {
|
|
|
262
264
|
expect(mockParseMedia).toHaveBeenCalledOnceMoreWith([
|
|
263
265
|
dummyData,
|
|
264
266
|
{periodStart: 0, segmentStart: 0, segmentEnd: 3, vttOffset: 0},
|
|
267
|
+
undefined,
|
|
265
268
|
]);
|
|
266
269
|
expect(mockDisplayer.appendSpy).toHaveBeenCalledOnceMoreWith([
|
|
267
270
|
[
|
|
@@ -276,6 +279,7 @@ describe('TextEngine', () => {
|
|
|
276
279
|
expect(mockParseMedia).toHaveBeenCalledOnceMoreWith([
|
|
277
280
|
dummyData,
|
|
278
281
|
{periodStart: 4, segmentStart: 4, segmentEnd: 7, vttOffset: 4},
|
|
282
|
+
undefined,
|
|
279
283
|
]);
|
|
280
284
|
expect(mockDisplayer.appendSpy).toHaveBeenCalledOnceMoreWith([
|
|
281
285
|
[
|
|
@@ -305,6 +309,7 @@ describe('TextEngine', () => {
|
|
|
305
309
|
expect(mockParseMedia).toHaveBeenCalledOnceMoreWith([
|
|
306
310
|
dummyData,
|
|
307
311
|
{periodStart: 0, segmentStart: 0, segmentEnd: 3, vttOffset: 0},
|
|
312
|
+
undefined,
|
|
308
313
|
]);
|
|
309
314
|
|
|
310
315
|
textEngine.setTimestampOffset(8);
|
|
@@ -314,6 +319,7 @@ describe('TextEngine', () => {
|
|
|
314
319
|
expect(mockParseMedia).toHaveBeenCalledOnceMoreWith([
|
|
315
320
|
dummyData,
|
|
316
321
|
{periodStart: 8, segmentStart: 4, segmentEnd: 7, vttOffset: 4},
|
|
322
|
+
undefined,
|
|
317
323
|
]);
|
|
318
324
|
});
|
|
319
325
|
});
|