shaka-player 4.0.0 → 4.1.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/config.yml +1 -0
- package/.github/workflows/release-please.yaml +6 -11
- package/CHANGELOG.md +33 -0
- package/CONTRIBUTORS +1 -0
- package/README.md +4 -4
- package/build/misspellings.txt +1 -0
- package/demo/common/message_ids.js +1 -0
- package/demo/config.js +3 -1
- package/demo/demo.less +0 -1
- package/demo/locales/en.json +1 -0
- package/demo/locales/source.json +4 -0
- package/dist/controls.css +3 -3
- package/dist/controls.css.map +1 -1
- package/dist/demo.compiled.debug.js +4 -4
- package/dist/demo.compiled.debug.map +2 -2
- package/dist/demo.compiled.js +4 -4
- package/dist/demo.compiled.map +2 -2
- package/dist/demo.css +1 -1
- package/dist/demo.css.map +1 -1
- package/dist/deps.js +3 -3
- package/dist/locales.js +32 -32
- package/dist/receiver.compiled.debug.js +2 -2
- package/dist/receiver.compiled.debug.map +2 -2
- package/dist/receiver.compiled.js +2 -2
- package/dist/receiver.compiled.map +2 -2
- package/dist/shaka-player.compiled.d.ts +15 -8
- package/dist/shaka-player.compiled.debug.d.ts +15 -8
- package/dist/shaka-player.compiled.debug.externs.js +11 -2
- package/dist/shaka-player.compiled.debug.js +266 -253
- package/dist/shaka-player.compiled.debug.map +3 -3
- package/dist/shaka-player.compiled.externs.js +11 -2
- package/dist/shaka-player.compiled.js +792 -784
- package/dist/shaka-player.compiled.map +3 -3
- package/dist/shaka-player.ui.d.ts +16 -9
- package/dist/shaka-player.ui.debug.d.ts +16 -9
- package/dist/shaka-player.ui.debug.externs.js +11 -2
- package/dist/shaka-player.ui.debug.js +299 -282
- package/dist/shaka-player.ui.debug.map +3 -3
- package/dist/shaka-player.ui.externs.js +11 -2
- package/dist/shaka-player.ui.js +948 -941
- package/dist/shaka-player.ui.map +3 -3
- package/docs/design/chromecast.md +3 -1
- package/docs/tutorials/drm-config.md +16 -0
- package/docs/tutorials/ui.md +26 -0
- package/docs/tutorials/upgrade.md +4 -2
- package/externs/chromecast.js +9 -2
- package/externs/shaka/manifest.js +7 -0
- package/externs/shaka/player.js +32 -2
- package/externs/shaka/text.js +7 -0
- package/karma.conf.js +4 -0
- package/lib/abr/simple_abr_manager.js +2 -1
- package/lib/cast/cast_proxy.js +19 -8
- package/lib/cast/cast_receiver.js +3 -3
- package/lib/cast/cast_sender.js +12 -2
- package/lib/dash/dash_parser.js +2 -2
- package/lib/hls/hls_parser.js +46 -15
- package/lib/media/drm_engine.js +36 -8
- package/lib/media/gap_jumping_controller.js +23 -1
- package/lib/media/media_source_engine.js +91 -37
- package/lib/media/playhead.js +47 -4
- package/lib/media/stall_detector.js +19 -2
- package/lib/media/streaming_engine.js +9 -3
- package/lib/offline/indexeddb/storage_mechanism.js +4 -2
- package/lib/offline/manifest_converter.js +1 -0
- package/lib/player.js +339 -175
- package/lib/text/cue.js +6 -0
- package/lib/text/ui_text_displayer.js +1 -0
- package/lib/text/vtt_text_parser.js +11 -7
- package/lib/util/config_utils.js +25 -0
- package/lib/util/error.js +13 -0
- package/lib/util/fake_event.js +39 -0
- package/lib/util/manifest_parser_utils.js +1 -1
- package/lib/util/mime_utils.js +62 -6
- package/lib/util/periods.js +5 -4
- package/lib/util/player_configuration.js +16 -14
- package/lib/util/stats.js +28 -1
- package/lib/util/stream_utils.js +11 -2
- package/package.json +5 -2
- package/test/cast/cast_proxy_unit.js +3 -1
- package/test/cast/cast_sender_unit.js +21 -6
- package/test/demo/demo_unit.js +2 -1
- package/test/hls/hls_live_unit.js +79 -7
- package/test/hls/manifest_text_parser_unit.js +19 -0
- package/test/media/adaptation_set_unit.js +1 -0
- package/test/media/playhead_unit.js +114 -25
- package/test/media/stall_detector_unit.js +20 -2
- package/test/media/streaming_engine_integration.js +2 -1
- package/test/media/streaming_engine_unit.js +1 -0
- package/test/offline/indexeddb_storage_unit.js +1 -1
- package/test/offline/storage_compatibility_unit.js +1 -1
- package/test/player_external.js +7 -1
- package/test/player_integration.js +9 -10
- package/test/player_src_equals_integration.js +81 -83
- package/test/player_unit.js +240 -2
- package/test/test/boot.js +9 -0
- package/test/test/externs/filters.js +4 -0
- package/test/test/util/manifest_generator.js +2 -0
- package/test/test/util/simple_fakes.js +45 -2
- package/test/test/util/streaming_engine_util.js +1 -0
- package/test/text/vtt_text_parser_unit.js +68 -5
- package/test/ui/ui_unit.js +18 -5
- package/test/util/mime_utils_unit.js +54 -0
- package/test/util/periods_unit.js +79 -0
- package/test/util/stream_utils_unit.js +108 -0
- package/ui/controls.js +12 -8
- package/ui/externs/ui.js +10 -1
- package/ui/less/containers.less +1 -1
- package/ui/less/general.less +1 -1
- package/ui/less/range_elements.less +1 -0
- package/ui/resolution_selection.js +1 -1
- package/ui/statistics_button.js +10 -0
- package/ui/ui.js +12 -1
- package/.github/pull_request_template.md +0 -10
- package/docs/tutorials/gsoc-ideas-list.md +0 -3
|
@@ -539,11 +539,13 @@ describe('HlsParser live', () => {
|
|
|
539
539
|
ManifestParser.verifySegmentIndex(video, [ref1, ref2]);
|
|
540
540
|
});
|
|
541
541
|
|
|
542
|
+
// Test for https://github.com/shaka-project/shaka-player/issues/4223
|
|
542
543
|
it('parses streams with partial and preload hinted segments', async () => {
|
|
543
544
|
playerInterface.isLowLatencyMode = () => true;
|
|
544
545
|
const mediaWithPartialSegments = [
|
|
545
546
|
'#EXTM3U\n',
|
|
546
547
|
'#EXT-X-TARGETDURATION:5\n',
|
|
548
|
+
'#EXT-X-PART-INF:PART-TARGET=1.5\n',
|
|
547
549
|
'#EXT-X-MAP:URI="init.mp4",BYTERANGE="616@0"\n',
|
|
548
550
|
'#EXT-X-MEDIA-SEQUENCE:0\n',
|
|
549
551
|
// ref includes partialRef, partialRef2
|
|
@@ -576,26 +578,73 @@ describe('HlsParser live', () => {
|
|
|
576
578
|
'test:/partial2.mp4', 2, 4, /* syncTime= */ null,
|
|
577
579
|
/* baseUri= */ '', /* startByte= */ 200, /* endByte= */ 429);
|
|
578
580
|
|
|
581
|
+
const ref = makeReference(
|
|
582
|
+
'test:/main.mp4', 0, 4, /* syncTime= */ null,
|
|
583
|
+
/* baseUri= */ '', /* startByte= */ 0, /* endByte= */ 429,
|
|
584
|
+
/* timestampOffset= */ 0, [partialRef, partialRef2]);
|
|
585
|
+
|
|
579
586
|
const partialRef3 = makeReference(
|
|
580
587
|
'test:/partial.mp4', 4, 6, /* syncTime= */ null,
|
|
581
588
|
/* baseUri= */ '', /* startByte= */ 0, /* endByte= */ 209);
|
|
582
589
|
|
|
583
|
-
// A preload hinted partial segment doesn't have duration information,
|
|
584
|
-
// so its startTime and endTime are the same.
|
|
585
590
|
const preloadRef = makeReference(
|
|
586
|
-
'test:/partial.mp4', 6,
|
|
591
|
+
'test:/partial.mp4', 6, 7.5, /* syncTime= */ null,
|
|
587
592
|
/* baseUri= */ '', /* startByte= */ 210, /* endByte= */ null);
|
|
588
593
|
|
|
594
|
+
// ref2 is not fully published yet, so it doesn't have a segment uri.
|
|
595
|
+
const ref2 = makeReference(
|
|
596
|
+
'', 4, 7.5, /* syncTime= */ null,
|
|
597
|
+
/* baseUri= */ '', /* startByte= */ 0, /* endByte= */ null,
|
|
598
|
+
/* timestampOffset= */ 0, [partialRef3, preloadRef]);
|
|
599
|
+
|
|
600
|
+
const manifest = await parser.start('test:/master', playerInterface);
|
|
601
|
+
const video = manifest.variants[0].video;
|
|
602
|
+
await video.createSegmentIndex();
|
|
603
|
+
ManifestParser.verifySegmentIndex(video, [ref, ref2]);
|
|
604
|
+
});
|
|
605
|
+
|
|
606
|
+
// Test for https://github.com/shaka-project/shaka-player/issues/4223
|
|
607
|
+
it('ignores preload hinted segments without target duration', async () => {
|
|
608
|
+
playerInterface.isLowLatencyMode = () => true;
|
|
609
|
+
|
|
610
|
+
// Missing PART-TARGET, so preload hints are skipped.
|
|
611
|
+
const mediaWithPartialSegments = [
|
|
612
|
+
'#EXTM3U\n',
|
|
613
|
+
'#EXT-X-TARGETDURATION:5\n',
|
|
614
|
+
'#EXT-X-MAP:URI="init.mp4",BYTERANGE="616@0"\n',
|
|
615
|
+
'#EXT-X-MEDIA-SEQUENCE:0\n',
|
|
616
|
+
'#EXTINF:4,\n',
|
|
617
|
+
// ref1
|
|
618
|
+
'main.mp4\n',
|
|
619
|
+
// ref2 includes partialRef, but not preloadRef
|
|
620
|
+
// partialRef
|
|
621
|
+
'#EXT-X-PART:DURATION=2,URI="partial.mp4",BYTERANGE=210@0\n',
|
|
622
|
+
// preloadRef
|
|
623
|
+
'#EXT-X-PRELOAD-HINT:TYPE=PART,URI="partial.mp4",BYTERANGE-START=210\n',
|
|
624
|
+
].join('');
|
|
625
|
+
|
|
626
|
+
fakeNetEngine
|
|
627
|
+
.setResponseText('test:/master', master)
|
|
628
|
+
.setResponseText('test:/video', mediaWithPartialSegments)
|
|
629
|
+
.setResponseValue('test:/init.mp4', initSegmentData)
|
|
630
|
+
.setResponseValue('test:/main.mp4', segmentData)
|
|
631
|
+
.setResponseValue('test:/partial.mp4', segmentData)
|
|
632
|
+
.setResponseValue('test:/partial2.mp4', segmentData);
|
|
633
|
+
|
|
589
634
|
const ref = makeReference(
|
|
590
635
|
'test:/main.mp4', 0, 4, /* syncTime= */ null,
|
|
591
|
-
/* baseUri= */ '', /* startByte= */ 0, /* endByte= */
|
|
592
|
-
/* timestampOffset= */ 0, [
|
|
636
|
+
/* baseUri= */ '', /* startByte= */ 0, /* endByte= */ null,
|
|
637
|
+
/* timestampOffset= */ 0, []);
|
|
638
|
+
|
|
639
|
+
const partialRef = makeReference(
|
|
640
|
+
'test:/partial.mp4', 4, 6, /* syncTime= */ null,
|
|
641
|
+
/* baseUri= */ '', /* startByte= */ 0, /* endByte= */ 209);
|
|
593
642
|
|
|
594
643
|
// ref2 is not fully published yet, so it doesn't have a segment uri.
|
|
595
644
|
const ref2 = makeReference(
|
|
596
645
|
'', 4, 6, /* syncTime= */ null,
|
|
597
|
-
/* baseUri= */ '', /* startByte= */ 0, /* endByte= */
|
|
598
|
-
/* timestampOffset= */ 0, [
|
|
646
|
+
/* baseUri= */ '', /* startByte= */ 0, /* endByte= */ 209,
|
|
647
|
+
/* timestampOffset= */ 0, [partialRef]);
|
|
599
648
|
|
|
600
649
|
const manifest = await parser.start('test:/master', playerInterface);
|
|
601
650
|
const video = manifest.variants[0].video;
|
|
@@ -603,6 +652,29 @@ describe('HlsParser live', () => {
|
|
|
603
652
|
ManifestParser.verifySegmentIndex(video, [ref, ref2]);
|
|
604
653
|
});
|
|
605
654
|
|
|
655
|
+
// Test for https://github.com/shaka-project/shaka-player/issues/4185
|
|
656
|
+
it('does not fail on preload hints with LL mode off', async () => {
|
|
657
|
+
// LL mode must be off for this test!
|
|
658
|
+
playerInterface.isLowLatencyMode = () => false;
|
|
659
|
+
|
|
660
|
+
const mediaWithPartialSegments = [
|
|
661
|
+
'#EXTM3U\n',
|
|
662
|
+
'#EXT-X-TARGETDURATION:5\n',
|
|
663
|
+
'#EXT-X-PART-INF:PART-TARGET=1.5\n',
|
|
664
|
+
'#EXTINF:4,\n',
|
|
665
|
+
'main.mp4\n',
|
|
666
|
+
'#EXT-X-PART:DURATION=2,URI="partial.mp4",BYTERANGE=210@0\n',
|
|
667
|
+
'#EXT-X-PRELOAD-HINT:TYPE=PART,URI="partial.mp4",BYTERANGE-START=210\n',
|
|
668
|
+
].join('');
|
|
669
|
+
|
|
670
|
+
fakeNetEngine
|
|
671
|
+
.setResponseText('test:/master', master)
|
|
672
|
+
.setResponseText('test:/video', mediaWithPartialSegments);
|
|
673
|
+
|
|
674
|
+
// If this throws, the test fails. Otherwise, it passes.
|
|
675
|
+
await parser.start('test:/master', playerInterface);
|
|
676
|
+
});
|
|
677
|
+
|
|
606
678
|
describe('update', () => {
|
|
607
679
|
it('adds new segments when they appear', async () => {
|
|
608
680
|
const ref1 = makeReference(
|
|
@@ -238,6 +238,25 @@ describe('ManifestTextParser', () => {
|
|
|
238
238
|
'#EXTM3U\n' +
|
|
239
239
|
'#EXT-X-MEDIA:CODECS="avc1.64002a,mp4a.40.2",AUDIO="a1,a2"',
|
|
240
240
|
|
|
241
|
+
// manifest URI:
|
|
242
|
+
'https://test/manifest.m3u8');
|
|
243
|
+
|
|
244
|
+
verifyPlaylist(
|
|
245
|
+
{
|
|
246
|
+
type: shaka.hls.PlaylistType.MASTER,
|
|
247
|
+
tags: [
|
|
248
|
+
new shaka.hls.Tag(/* id= */ 3, 'EXT-X-MEDIA',
|
|
249
|
+
[
|
|
250
|
+
new shaka.hls.Attribute('CODECS',
|
|
251
|
+
'av01.0.08M.08,mp4a.40.2'),
|
|
252
|
+
]),
|
|
253
|
+
],
|
|
254
|
+
},
|
|
255
|
+
|
|
256
|
+
// playlist text:
|
|
257
|
+
'#EXTM3U\n' +
|
|
258
|
+
'#EXT-X-MEDIA:CODECS="av01.0.08M.08,mp4a.40.2"',
|
|
259
|
+
|
|
241
260
|
// manifest URI:
|
|
242
261
|
'https://test/manifest.m3u8');
|
|
243
262
|
});
|
|
@@ -21,12 +21,15 @@ let TimeRange;
|
|
|
21
21
|
* start: number,
|
|
22
22
|
* waitingAt: number,
|
|
23
23
|
* expectedEndTime: number,
|
|
24
|
+
* expectEvent: boolean,
|
|
24
25
|
* }}
|
|
25
26
|
*
|
|
26
27
|
* @description
|
|
27
28
|
* Parameters for a test where we start playing inside a buffered range and play
|
|
28
29
|
* until the end of the buffer. Then, if we expect it, Playhead should jump
|
|
29
|
-
* to the expected time.
|
|
30
|
+
* to the expected time. We should get a 'stalldetected' event when the Playhead
|
|
31
|
+
* detects a stall through the StallDetector, and a 'gapjumped' event when the
|
|
32
|
+
* Playhead jumps over a gap in the buffered range(s).
|
|
30
33
|
*
|
|
31
34
|
* @property {!Array.<TimeRange>} buffered
|
|
32
35
|
* The buffered ranges for the test.
|
|
@@ -36,6 +39,9 @@ let TimeRange;
|
|
|
36
39
|
* The time to pause at and fire a 'waiting' event.
|
|
37
40
|
* @property {number} expectedEndTime
|
|
38
41
|
* The expected time at the end of the test.
|
|
42
|
+
* @property {boolean} expectEvent
|
|
43
|
+
* If true, expect either the 'stalldetected' or 'gapjumped' event to be
|
|
44
|
+
* fired.
|
|
39
45
|
*/
|
|
40
46
|
let PlayingTestInfo;
|
|
41
47
|
|
|
@@ -47,6 +53,7 @@ let PlayingTestInfo;
|
|
|
47
53
|
* start: number,
|
|
48
54
|
* seekTo: number,
|
|
49
55
|
* expectedEndTime: number,
|
|
56
|
+
* expectEvent: boolean,
|
|
50
57
|
* }}
|
|
51
58
|
*
|
|
52
59
|
* @description
|
|
@@ -65,6 +72,9 @@ let PlayingTestInfo;
|
|
|
65
72
|
* The time to seek to.
|
|
66
73
|
* @property {number} expectedEndTime
|
|
67
74
|
* The expected time at the end of the test.
|
|
75
|
+
* @property {boolean} expectEvent
|
|
76
|
+
* If true, expect either the 'stalldetected' or 'gapjumped' event to be
|
|
77
|
+
* fired.
|
|
68
78
|
*/
|
|
69
79
|
let SeekTestInfo;
|
|
70
80
|
|
|
@@ -87,6 +97,10 @@ describe('Playhead', () => {
|
|
|
87
97
|
/** @type {!jasmine.Spy} */
|
|
88
98
|
let onSeek;
|
|
89
99
|
|
|
100
|
+
// Callback to us from Playhead when an event should be sent to the app.
|
|
101
|
+
/** @type {!jasmine.Spy} */
|
|
102
|
+
let onEvent;
|
|
103
|
+
|
|
90
104
|
beforeAll(() => {
|
|
91
105
|
jasmine.clock().install();
|
|
92
106
|
});
|
|
@@ -100,6 +114,7 @@ describe('Playhead', () => {
|
|
|
100
114
|
timeline = new shaka.test.FakePresentationTimeline();
|
|
101
115
|
|
|
102
116
|
onSeek = jasmine.createSpy('onSeek');
|
|
117
|
+
onEvent = jasmine.createSpy('onEvent');
|
|
103
118
|
|
|
104
119
|
timeline.isLive.and.returnValue(false);
|
|
105
120
|
timeline.getSeekRangeStart.and.returnValue(5);
|
|
@@ -143,7 +158,8 @@ describe('Playhead', () => {
|
|
|
143
158
|
manifest,
|
|
144
159
|
config,
|
|
145
160
|
/* startTime= */ 5,
|
|
146
|
-
Util.spyFunc(onSeek)
|
|
161
|
+
Util.spyFunc(onSeek),
|
|
162
|
+
Util.spyFunc(onEvent));
|
|
147
163
|
|
|
148
164
|
expect(video.currentTime).toBe(5);
|
|
149
165
|
expect(playhead.getTime()).toBe(5);
|
|
@@ -163,7 +179,8 @@ describe('Playhead', () => {
|
|
|
163
179
|
manifest,
|
|
164
180
|
config,
|
|
165
181
|
/* startTime= */ 5,
|
|
166
|
-
Util.spyFunc(onSeek)
|
|
182
|
+
Util.spyFunc(onSeek),
|
|
183
|
+
Util.spyFunc(onEvent));
|
|
167
184
|
|
|
168
185
|
expect(video.addEventListener).toHaveBeenCalledWith(
|
|
169
186
|
'loadedmetadata', jasmine.any(Function), jasmine.anything());
|
|
@@ -203,7 +220,8 @@ describe('Playhead', () => {
|
|
|
203
220
|
manifest,
|
|
204
221
|
config,
|
|
205
222
|
/* startTime= */ 5,
|
|
206
|
-
Util.spyFunc(onSeek)
|
|
223
|
+
Util.spyFunc(onSeek),
|
|
224
|
+
Util.spyFunc(onEvent));
|
|
207
225
|
|
|
208
226
|
video.on['seeking']();
|
|
209
227
|
expect(playhead.getTime()).toBe(5);
|
|
@@ -221,7 +239,12 @@ describe('Playhead', () => {
|
|
|
221
239
|
timeline.getSeekRangeEnd.and.returnValue(60);
|
|
222
240
|
|
|
223
241
|
playhead = new shaka.media.MediaSourcePlayhead(
|
|
224
|
-
video,
|
|
242
|
+
video,
|
|
243
|
+
manifest,
|
|
244
|
+
config,
|
|
245
|
+
/* startTime= */ 0,
|
|
246
|
+
Util.spyFunc(onSeek),
|
|
247
|
+
Util.spyFunc(onEvent));
|
|
225
248
|
|
|
226
249
|
expect(playhead.getTime()).toBe(0);
|
|
227
250
|
});
|
|
@@ -234,7 +257,12 @@ describe('Playhead', () => {
|
|
|
234
257
|
timeline.getDuration.and.returnValue(60);
|
|
235
258
|
|
|
236
259
|
playhead = new shaka.media.MediaSourcePlayhead(
|
|
237
|
-
video,
|
|
260
|
+
video,
|
|
261
|
+
manifest,
|
|
262
|
+
config,
|
|
263
|
+
/* startTime= */ 60,
|
|
264
|
+
Util.spyFunc(onSeek),
|
|
265
|
+
Util.spyFunc(onEvent));
|
|
238
266
|
|
|
239
267
|
expect(playhead.getTime()).toBe(59); // duration - durationBackoff
|
|
240
268
|
expect(video.currentTime).toBe(59); // duration - durationBackoff
|
|
@@ -248,7 +276,12 @@ describe('Playhead', () => {
|
|
|
248
276
|
timeline.getSeekRangeEnd.and.returnValue(60);
|
|
249
277
|
|
|
250
278
|
playhead = new shaka.media.MediaSourcePlayhead(
|
|
251
|
-
video,
|
|
279
|
+
video,
|
|
280
|
+
manifest,
|
|
281
|
+
config,
|
|
282
|
+
/* startTime= */ -15,
|
|
283
|
+
Util.spyFunc(onSeek),
|
|
284
|
+
Util.spyFunc(onEvent));
|
|
252
285
|
|
|
253
286
|
expect(playhead.getTime()).toBe(45);
|
|
254
287
|
});
|
|
@@ -262,7 +295,12 @@ describe('Playhead', () => {
|
|
|
262
295
|
// If the live stream's playback offset time is not available, start
|
|
263
296
|
// playing from the seek range start time.
|
|
264
297
|
playhead = new shaka.media.MediaSourcePlayhead(
|
|
265
|
-
video,
|
|
298
|
+
video,
|
|
299
|
+
manifest,
|
|
300
|
+
config,
|
|
301
|
+
/* startTime= */ -40,
|
|
302
|
+
Util.spyFunc(onSeek),
|
|
303
|
+
Util.spyFunc(onEvent));
|
|
266
304
|
|
|
267
305
|
expect(playhead.getTime()).toBe(30);
|
|
268
306
|
});
|
|
@@ -273,7 +311,8 @@ describe('Playhead', () => {
|
|
|
273
311
|
manifest,
|
|
274
312
|
config,
|
|
275
313
|
/* startTime= */ 5,
|
|
276
|
-
Util.spyFunc(onSeek)
|
|
314
|
+
Util.spyFunc(onSeek),
|
|
315
|
+
Util.spyFunc(onEvent));
|
|
277
316
|
|
|
278
317
|
expect(video.addEventListener).toHaveBeenCalledWith(
|
|
279
318
|
'loadedmetadata', jasmine.any(Function), jasmine.anything());
|
|
@@ -305,7 +344,8 @@ describe('Playhead', () => {
|
|
|
305
344
|
manifest,
|
|
306
345
|
config,
|
|
307
346
|
/* startTime= */ null,
|
|
308
|
-
Util.spyFunc(onSeek)
|
|
347
|
+
Util.spyFunc(onSeek),
|
|
348
|
+
Util.spyFunc(onEvent));
|
|
309
349
|
|
|
310
350
|
expect(video.addEventListener).toHaveBeenCalledWith(
|
|
311
351
|
'loadedmetadata', jasmine.any(Function), jasmine.anything());
|
|
@@ -337,7 +377,8 @@ describe('Playhead', () => {
|
|
|
337
377
|
manifest,
|
|
338
378
|
config,
|
|
339
379
|
/* startTime= */ 5,
|
|
340
|
-
Util.spyFunc(onSeek)
|
|
380
|
+
Util.spyFunc(onSeek),
|
|
381
|
+
Util.spyFunc(onEvent));
|
|
341
382
|
|
|
342
383
|
// This has to periodically increment the mock date to allow the onSeeking_
|
|
343
384
|
// handler to seek, if appropriate.
|
|
@@ -496,7 +537,8 @@ describe('Playhead', () => {
|
|
|
496
537
|
manifest,
|
|
497
538
|
config,
|
|
498
539
|
/* startTime= */ 5,
|
|
499
|
-
Util.spyFunc(onSeek)
|
|
540
|
+
Util.spyFunc(onSeek),
|
|
541
|
+
Util.spyFunc(onEvent));
|
|
500
542
|
|
|
501
543
|
setMockDate(0);
|
|
502
544
|
video.on['seeking']();
|
|
@@ -544,7 +586,8 @@ describe('Playhead', () => {
|
|
|
544
586
|
manifest,
|
|
545
587
|
config,
|
|
546
588
|
/* startTime= */ 5,
|
|
547
|
-
Util.spyFunc(onSeek)
|
|
589
|
+
Util.spyFunc(onSeek),
|
|
590
|
+
Util.spyFunc(onEvent));
|
|
548
591
|
|
|
549
592
|
// First, seek to start time.
|
|
550
593
|
video.currentTime = 0;
|
|
@@ -604,7 +647,8 @@ describe('Playhead', () => {
|
|
|
604
647
|
manifest,
|
|
605
648
|
config,
|
|
606
649
|
/* startTime= */ 5,
|
|
607
|
-
Util.spyFunc(onSeek)
|
|
650
|
+
Util.spyFunc(onSeek),
|
|
651
|
+
Util.spyFunc(onEvent));
|
|
608
652
|
expect(currentTime).toBe(1000);
|
|
609
653
|
seekCount = 0;
|
|
610
654
|
|
|
@@ -649,7 +693,8 @@ describe('Playhead', () => {
|
|
|
649
693
|
manifest,
|
|
650
694
|
config,
|
|
651
695
|
/* startTime= */ 5,
|
|
652
|
-
Util.spyFunc(onSeek)
|
|
696
|
+
Util.spyFunc(onSeek),
|
|
697
|
+
Util.spyFunc(onEvent));
|
|
653
698
|
|
|
654
699
|
video.on['seeking']();
|
|
655
700
|
expect(video.currentTime).toBe(5);
|
|
@@ -680,7 +725,8 @@ describe('Playhead', () => {
|
|
|
680
725
|
manifest,
|
|
681
726
|
config,
|
|
682
727
|
/* startTime= */ 5,
|
|
683
|
-
Util.spyFunc(onSeek)
|
|
728
|
+
Util.spyFunc(onSeek),
|
|
729
|
+
Util.spyFunc(onEvent));
|
|
684
730
|
|
|
685
731
|
video.on['seeking']();
|
|
686
732
|
expect(video.currentTime).toBe(5);
|
|
@@ -714,7 +760,8 @@ describe('Playhead', () => {
|
|
|
714
760
|
manifest,
|
|
715
761
|
config,
|
|
716
762
|
/* startTime= */ 30,
|
|
717
|
-
Util.spyFunc(onSeek)
|
|
763
|
+
Util.spyFunc(onSeek),
|
|
764
|
+
Util.spyFunc(onEvent));
|
|
718
765
|
|
|
719
766
|
video.currentTime = 0;
|
|
720
767
|
video.seeking = true;
|
|
@@ -759,7 +806,8 @@ describe('Playhead', () => {
|
|
|
759
806
|
manifest,
|
|
760
807
|
config,
|
|
761
808
|
/* startTime= */ 30,
|
|
762
|
-
Util.spyFunc(onSeek)
|
|
809
|
+
Util.spyFunc(onSeek),
|
|
810
|
+
Util.spyFunc(onEvent));
|
|
763
811
|
|
|
764
812
|
/**
|
|
765
813
|
* Prevent retries on the initial start time seek. This will ensure that
|
|
@@ -798,6 +846,7 @@ describe('Playhead', () => {
|
|
|
798
846
|
buffered: [{start: 0, end: 10}],
|
|
799
847
|
start: 3,
|
|
800
848
|
waitingAt: 10,
|
|
849
|
+
expectEvent: false,
|
|
801
850
|
expectedEndTime: 10,
|
|
802
851
|
});
|
|
803
852
|
|
|
@@ -805,6 +854,7 @@ describe('Playhead', () => {
|
|
|
805
854
|
buffered: [{start: 0, end: 10}, {start: 20, end: 30}],
|
|
806
855
|
start: 24,
|
|
807
856
|
waitingAt: 30,
|
|
857
|
+
expectEvent: false,
|
|
808
858
|
expectedEndTime: 30,
|
|
809
859
|
});
|
|
810
860
|
|
|
@@ -812,6 +862,7 @@ describe('Playhead', () => {
|
|
|
812
862
|
buffered: [{start: 0, end: 10}, {start: 11, end: 20}],
|
|
813
863
|
start: 5,
|
|
814
864
|
waitingAt: 10,
|
|
865
|
+
expectEvent: true,
|
|
815
866
|
expectedEndTime: 11,
|
|
816
867
|
});
|
|
817
868
|
|
|
@@ -820,6 +871,7 @@ describe('Playhead', () => {
|
|
|
820
871
|
[{start: 0, end: 10}, {start: 11, end: 20}, {start: 21, end: 30}],
|
|
821
872
|
start: 5,
|
|
822
873
|
waitingAt: 10,
|
|
874
|
+
expectEvent: true,
|
|
823
875
|
expectedEndTime: 11,
|
|
824
876
|
});
|
|
825
877
|
|
|
@@ -828,6 +880,7 @@ describe('Playhead', () => {
|
|
|
828
880
|
[{start: 0, end: 10}, {start: 11, end: 20}, {start: 21, end: 30}],
|
|
829
881
|
start: 15,
|
|
830
882
|
waitingAt: 20,
|
|
883
|
+
expectEvent: true,
|
|
831
884
|
expectedEndTime: 21,
|
|
832
885
|
});
|
|
833
886
|
}); // with small gaps
|
|
@@ -837,6 +890,7 @@ describe('Playhead', () => {
|
|
|
837
890
|
buffered: [{start: 0, end: 10}, {start: 30, end: 40}],
|
|
838
891
|
start: 5,
|
|
839
892
|
waitingAt: 10,
|
|
893
|
+
expectEvent: true,
|
|
840
894
|
expectedEndTime: 30,
|
|
841
895
|
});
|
|
842
896
|
|
|
@@ -845,6 +899,7 @@ describe('Playhead', () => {
|
|
|
845
899
|
[{start: 0, end: 10}, {start: 30, end: 40}, {start: 50, end: 60}],
|
|
846
900
|
start: 5,
|
|
847
901
|
waitingAt: 10,
|
|
902
|
+
expectEvent: true,
|
|
848
903
|
expectedEndTime: 30,
|
|
849
904
|
});
|
|
850
905
|
|
|
@@ -853,6 +908,7 @@ describe('Playhead', () => {
|
|
|
853
908
|
[{start: 0, end: 10}, {start: 20, end: 30}, {start: 50, end: 60}],
|
|
854
909
|
start: 24,
|
|
855
910
|
waitingAt: 30,
|
|
911
|
+
expectEvent: true,
|
|
856
912
|
expectedEndTime: 50,
|
|
857
913
|
});
|
|
858
914
|
}); // with large gaps
|
|
@@ -867,12 +923,15 @@ describe('Playhead', () => {
|
|
|
867
923
|
video.currentTime = data.start;
|
|
868
924
|
video.readyState = HTMLMediaElement.HAVE_ENOUGH_DATA;
|
|
869
925
|
|
|
926
|
+
onEvent.and.callFake((event) => {});
|
|
927
|
+
|
|
870
928
|
playhead = new shaka.media.MediaSourcePlayhead(
|
|
871
929
|
video,
|
|
872
930
|
manifest,
|
|
873
931
|
config,
|
|
874
932
|
/* startTime= */ data.start,
|
|
875
|
-
Util.spyFunc(onSeek)
|
|
933
|
+
Util.spyFunc(onSeek),
|
|
934
|
+
Util.spyFunc(onEvent));
|
|
876
935
|
|
|
877
936
|
jasmine.clock().tick(500);
|
|
878
937
|
for (let time = data.start; time < data.waitingAt; time++) {
|
|
@@ -893,11 +952,14 @@ describe('Playhead', () => {
|
|
|
893
952
|
expect(video.currentTime).toBe(time + 0.4);
|
|
894
953
|
}
|
|
895
954
|
|
|
955
|
+
expect(onEvent).not.toHaveBeenCalled();
|
|
956
|
+
|
|
896
957
|
video.currentTime = data.waitingAt;
|
|
897
958
|
video.readyState = HTMLMediaElement.HAVE_CURRENT_DATA;
|
|
898
959
|
video.on['waiting']();
|
|
899
960
|
jasmine.clock().tick(500);
|
|
900
961
|
|
|
962
|
+
expect(onEvent).toHaveBeenCalledTimes(data.expectEvent ? 1 : 0);
|
|
901
963
|
expect(video.currentTime).toBe(data.expectedEndTime);
|
|
902
964
|
});
|
|
903
965
|
}
|
|
@@ -909,6 +971,7 @@ describe('Playhead', () => {
|
|
|
909
971
|
buffered: [{start: 0, end: 10}],
|
|
910
972
|
start: 4,
|
|
911
973
|
seekTo: 14,
|
|
974
|
+
expectEvent: false,
|
|
912
975
|
expectedEndTime: 14,
|
|
913
976
|
});
|
|
914
977
|
|
|
@@ -916,6 +979,7 @@ describe('Playhead', () => {
|
|
|
916
979
|
buffered: [{start: 0, end: 10}, {start: 11, end: 20}],
|
|
917
980
|
start: 3,
|
|
918
981
|
seekTo: 10.4,
|
|
982
|
+
expectEvent: true,
|
|
919
983
|
expectedEndTime: 11,
|
|
920
984
|
});
|
|
921
985
|
|
|
@@ -924,6 +988,7 @@ describe('Playhead', () => {
|
|
|
924
988
|
[{start: 0, end: 10}, {start: 11, end: 20}, {start: 21, end: 30}],
|
|
925
989
|
start: 3,
|
|
926
990
|
seekTo: 10.4,
|
|
991
|
+
expectEvent: true,
|
|
927
992
|
expectedEndTime: 11,
|
|
928
993
|
});
|
|
929
994
|
|
|
@@ -932,6 +997,7 @@ describe('Playhead', () => {
|
|
|
932
997
|
[{start: 0, end: 10}, {start: 11, end: 20}, {start: 21, end: 30}],
|
|
933
998
|
start: 3,
|
|
934
999
|
seekTo: 20.5,
|
|
1000
|
+
expectEvent: true,
|
|
935
1001
|
expectedEndTime: 21,
|
|
936
1002
|
});
|
|
937
1003
|
|
|
@@ -939,6 +1005,7 @@ describe('Playhead', () => {
|
|
|
939
1005
|
buffered: [{start: 0, end: 10}, {start: 30, end: 40}],
|
|
940
1006
|
start: 3,
|
|
941
1007
|
seekTo: 29.2,
|
|
1008
|
+
expectEvent: true,
|
|
942
1009
|
expectedEndTime: 30,
|
|
943
1010
|
});
|
|
944
1011
|
}); // with small gaps
|
|
@@ -948,6 +1015,7 @@ describe('Playhead', () => {
|
|
|
948
1015
|
buffered: [{start: 0, end: 10}, {start: 30, end: 40}],
|
|
949
1016
|
start: 5,
|
|
950
1017
|
seekTo: 12,
|
|
1018
|
+
expectEvent: true,
|
|
951
1019
|
expectedEndTime: 30,
|
|
952
1020
|
});
|
|
953
1021
|
}); // with large gaps
|
|
@@ -961,6 +1029,7 @@ describe('Playhead', () => {
|
|
|
961
1029
|
newBuffered: [{start: 20, end: 30}, {start: 31, end: 40}],
|
|
962
1030
|
start: 3,
|
|
963
1031
|
seekTo: 22,
|
|
1032
|
+
expectEvent: false,
|
|
964
1033
|
expectedEndTime: 22,
|
|
965
1034
|
});
|
|
966
1035
|
|
|
@@ -971,6 +1040,7 @@ describe('Playhead', () => {
|
|
|
971
1040
|
newBuffered: [{start: 0.2, end: 10}],
|
|
972
1041
|
start: 4,
|
|
973
1042
|
seekTo: 0,
|
|
1043
|
+
expectEvent: true,
|
|
974
1044
|
expectedEndTime: 0.2,
|
|
975
1045
|
});
|
|
976
1046
|
|
|
@@ -980,6 +1050,7 @@ describe('Playhead', () => {
|
|
|
980
1050
|
newBuffered: [{start: 20, end: 30}, {start: 31, end: 40}],
|
|
981
1051
|
start: 3,
|
|
982
1052
|
seekTo: 30.2,
|
|
1053
|
+
expectEvent: true,
|
|
983
1054
|
expectedEndTime: 31,
|
|
984
1055
|
});
|
|
985
1056
|
|
|
@@ -989,6 +1060,7 @@ describe('Playhead', () => {
|
|
|
989
1060
|
newBuffered: [{start: 20, end: 30}, {start: 31, end: 40}],
|
|
990
1061
|
start: 3,
|
|
991
1062
|
seekTo: 30,
|
|
1063
|
+
expectEvent: true,
|
|
992
1064
|
expectedEndTime: 31,
|
|
993
1065
|
});
|
|
994
1066
|
|
|
@@ -998,6 +1070,7 @@ describe('Playhead', () => {
|
|
|
998
1070
|
newBuffered: [{start: 20, end: 30}],
|
|
999
1071
|
start: 3,
|
|
1000
1072
|
seekTo: 34,
|
|
1073
|
+
expectEvent: false,
|
|
1001
1074
|
expectedEndTime: 34,
|
|
1002
1075
|
});
|
|
1003
1076
|
|
|
@@ -1007,6 +1080,7 @@ describe('Playhead', () => {
|
|
|
1007
1080
|
newBuffered: [{start: 0, end: 10}],
|
|
1008
1081
|
start: 24,
|
|
1009
1082
|
seekTo: 4,
|
|
1083
|
+
expectEvent: false,
|
|
1010
1084
|
expectedEndTime: 4,
|
|
1011
1085
|
});
|
|
1012
1086
|
|
|
@@ -1017,6 +1091,7 @@ describe('Playhead', () => {
|
|
|
1017
1091
|
// should still be waiting.
|
|
1018
1092
|
start: 24,
|
|
1019
1093
|
seekTo: 4,
|
|
1094
|
+
expectEvent: false,
|
|
1020
1095
|
expectedEndTime: 4,
|
|
1021
1096
|
});
|
|
1022
1097
|
|
|
@@ -1026,6 +1101,7 @@ describe('Playhead', () => {
|
|
|
1026
1101
|
newBuffered: [{start: 2, end: 10}],
|
|
1027
1102
|
start: 24,
|
|
1028
1103
|
seekTo: 1.6,
|
|
1104
|
+
expectEvent: true,
|
|
1029
1105
|
expectedEndTime: 2,
|
|
1030
1106
|
});
|
|
1031
1107
|
}); // with small gaps
|
|
@@ -1036,6 +1112,7 @@ describe('Playhead', () => {
|
|
|
1036
1112
|
newBuffered: [{start: 20, end: 30}],
|
|
1037
1113
|
start: 25,
|
|
1038
1114
|
seekTo: 0,
|
|
1115
|
+
expectEvent: true,
|
|
1039
1116
|
expectedEndTime: 20,
|
|
1040
1117
|
});
|
|
1041
1118
|
|
|
@@ -1045,6 +1122,7 @@ describe('Playhead', () => {
|
|
|
1045
1122
|
newBuffered: [{start: 20, end: 30}, {start: 40, end: 50}],
|
|
1046
1123
|
start: 3,
|
|
1047
1124
|
seekTo: 32,
|
|
1125
|
+
expectEvent: true,
|
|
1048
1126
|
expectedEndTime: 40,
|
|
1049
1127
|
});
|
|
1050
1128
|
}); // with large gaps
|
|
@@ -1061,9 +1139,11 @@ describe('Playhead', () => {
|
|
|
1061
1139
|
manifest,
|
|
1062
1140
|
config,
|
|
1063
1141
|
/* startTime= */ 12,
|
|
1064
|
-
Util.spyFunc(onSeek)
|
|
1142
|
+
Util.spyFunc(onSeek),
|
|
1143
|
+
Util.spyFunc(onEvent));
|
|
1065
1144
|
|
|
1066
1145
|
jasmine.clock().tick(500);
|
|
1146
|
+
expect(onEvent).not.toHaveBeenCalled();
|
|
1067
1147
|
|
|
1068
1148
|
// Append a segment before seeking.
|
|
1069
1149
|
playhead.notifyOfBufferingChange();
|
|
@@ -1107,7 +1187,8 @@ describe('Playhead', () => {
|
|
|
1107
1187
|
manifest,
|
|
1108
1188
|
config,
|
|
1109
1189
|
/* startTime= */ 0,
|
|
1110
|
-
Util.spyFunc(onSeek)
|
|
1190
|
+
Util.spyFunc(onSeek),
|
|
1191
|
+
Util.spyFunc(onEvent));
|
|
1111
1192
|
|
|
1112
1193
|
playhead.notifyOfBufferingChange();
|
|
1113
1194
|
jasmine.clock().tick(500);
|
|
@@ -1128,7 +1209,8 @@ describe('Playhead', () => {
|
|
|
1128
1209
|
manifest,
|
|
1129
1210
|
config,
|
|
1130
1211
|
/* startTime= */ 5,
|
|
1131
|
-
Util.spyFunc(onSeek)
|
|
1212
|
+
Util.spyFunc(onSeek),
|
|
1213
|
+
Util.spyFunc(onEvent));
|
|
1132
1214
|
|
|
1133
1215
|
playhead.notifyOfBufferingChange();
|
|
1134
1216
|
jasmine.clock().tick(500);
|
|
@@ -1151,7 +1233,8 @@ describe('Playhead', () => {
|
|
|
1151
1233
|
manifest,
|
|
1152
1234
|
config,
|
|
1153
1235
|
/* startTime= */ 0,
|
|
1154
|
-
Util.spyFunc(onSeek)
|
|
1236
|
+
Util.spyFunc(onSeek),
|
|
1237
|
+
Util.spyFunc(onEvent));
|
|
1155
1238
|
|
|
1156
1239
|
playhead.notifyOfBufferingChange();
|
|
1157
1240
|
jasmine.clock().tick(500);
|
|
@@ -1174,7 +1257,8 @@ describe('Playhead', () => {
|
|
|
1174
1257
|
manifest,
|
|
1175
1258
|
config,
|
|
1176
1259
|
/* startTime= */ 0,
|
|
1177
|
-
Util.spyFunc(onSeek)
|
|
1260
|
+
Util.spyFunc(onSeek),
|
|
1261
|
+
Util.spyFunc(onEvent));
|
|
1178
1262
|
|
|
1179
1263
|
playhead.notifyOfBufferingChange();
|
|
1180
1264
|
jasmine.clock().tick(500);
|
|
@@ -1193,14 +1277,18 @@ describe('Playhead', () => {
|
|
|
1193
1277
|
video.currentTime = data.start;
|
|
1194
1278
|
video.readyState = HTMLMediaElement.HAVE_ENOUGH_DATA;
|
|
1195
1279
|
|
|
1280
|
+
onEvent.and.callFake((event) => {});
|
|
1281
|
+
|
|
1196
1282
|
playhead = new shaka.media.MediaSourcePlayhead(
|
|
1197
1283
|
video,
|
|
1198
1284
|
manifest,
|
|
1199
1285
|
config,
|
|
1200
1286
|
/* startTime= */ data.start,
|
|
1201
|
-
Util.spyFunc(onSeek)
|
|
1287
|
+
Util.spyFunc(onSeek),
|
|
1288
|
+
Util.spyFunc(onEvent));
|
|
1202
1289
|
|
|
1203
1290
|
jasmine.clock().tick(500);
|
|
1291
|
+
expect(onEvent).not.toHaveBeenCalled();
|
|
1204
1292
|
|
|
1205
1293
|
// Seek to the given position and update ready state.
|
|
1206
1294
|
video.currentTime = data.seekTo;
|
|
@@ -1232,6 +1320,7 @@ describe('Playhead', () => {
|
|
|
1232
1320
|
jasmine.clock().tick(250);
|
|
1233
1321
|
}
|
|
1234
1322
|
|
|
1323
|
+
expect(onEvent).toHaveBeenCalledTimes(data.expectEvent ? 1 : 0);
|
|
1235
1324
|
expect(video.currentTime).toBe(data.expectedEndTime);
|
|
1236
1325
|
});
|
|
1237
1326
|
}
|