shaka-player 3.2.2 → 3.3.2
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/feature_request.md +1 -1
- package/.github/ISSUE_TEMPLATE/question.md +1 -1
- package/.github/workflows/build_and_test.yaml +73 -0
- package/.github/workflows/release.yaml +53 -0
- package/AUTHORS +6 -5
- package/CHANGELOG.md +314 -1
- package/CONTRIBUTORS +4 -2
- package/build/checkversion.py +1 -1
- package/build/conformance.textproto +183 -169
- package/build/test.py +6 -0
- package/build/types/core +3 -0
- package/build/types/offline +1 -0
- package/build/types/polyfill +1 -0
- package/build/types/ui +2 -0
- package/demo/common/assets.js +18 -0
- package/demo/common/message_ids.js +13 -1
- package/demo/config.js +31 -1
- package/demo/index.html +1 -1
- package/demo/locales/en.json +12 -0
- package/demo/locales/source.json +48 -0
- package/dist/controls.css +26 -2
- package/dist/controls.css.map +1 -1
- package/dist/demo.compiled.debug.js +19 -16
- package/dist/demo.compiled.debug.map +3 -3
- package/dist/demo.compiled.js +19 -16
- package/dist/demo.compiled.map +3 -3
- package/dist/deps.js +21 -14
- package/dist/locales.js +31 -29
- package/dist/receiver.compiled.debug.js +11 -9
- package/dist/receiver.compiled.debug.map +3 -3
- package/dist/receiver.compiled.js +11 -9
- package/dist/receiver.compiled.map +3 -3
- package/dist/shaka-player.compiled.d.ts +232 -18
- package/dist/shaka-player.compiled.debug.d.ts +232 -18
- package/dist/shaka-player.compiled.debug.externs.js +293 -17
- package/dist/shaka-player.compiled.debug.js +481 -422
- package/dist/shaka-player.compiled.debug.map +4 -4
- package/dist/shaka-player.compiled.externs.js +293 -17
- package/dist/shaka-player.compiled.js +846 -817
- package/dist/shaka-player.compiled.map +4 -4
- package/dist/shaka-player.ui.d.ts +250 -30
- package/dist/shaka-player.ui.debug.d.ts +250 -30
- package/dist/shaka-player.ui.debug.externs.js +322 -28
- package/dist/shaka-player.ui.debug.js +630 -553
- package/dist/shaka-player.ui.debug.map +4 -4
- package/dist/shaka-player.ui.externs.js +322 -28
- package/dist/shaka-player.ui.js +1048 -1006
- package/dist/shaka-player.ui.map +4 -4
- package/docs/design/bg-fetch-after.gv +29 -0
- package/docs/design/bg-fetch-after.gv.png +0 -0
- package/docs/design/bg-fetch-before.gv +18 -0
- package/docs/design/bg-fetch-before.gv.png +0 -0
- package/docs/design/bg-fetch.md +134 -0
- package/docs/tutorials/blob-url.md +15 -0
- package/docs/tutorials/index.json +1 -0
- package/docs/tutorials/ui-customization.md +66 -0
- package/docs/tutorials/ui.md +5 -5
- package/externs/cmcd.js +186 -0
- package/externs/ima.js +3 -0
- package/externs/shaka/ads.js +5 -0
- package/externs/shaka/manifest.js +12 -1
- package/externs/shaka/manifest_parser.js +10 -1
- package/externs/shaka/net.js +20 -4
- package/externs/shaka/offline.js +31 -2
- package/externs/shaka/player.js +138 -5
- package/externs/shaka/text.js +24 -0
- package/externs/webcrypto.js +18 -0
- package/karma.conf.js +19 -15
- package/lib/abr/ewma.js +14 -0
- package/lib/abr/ewma_bandwidth_estimator.js +16 -2
- package/lib/abr/simple_abr_manager.js +6 -0
- package/lib/ads/ad_manager.js +1 -0
- package/lib/ads/client_side_ad.js +8 -0
- package/lib/ads/client_side_ad_manager.js +8 -4
- package/lib/ads/server_side_ad.js +7 -0
- package/lib/cast/cast_proxy.js +3 -0
- package/lib/cast/cast_receiver.js +3 -0
- package/lib/cast/cast_utils.js +1 -0
- package/lib/dash/dash_parser.js +79 -33
- package/lib/dash/segment_base.js +25 -1
- package/lib/dash/segment_list.js +18 -9
- package/lib/dash/segment_template.js +23 -10
- package/lib/hls/hls_parser.js +116 -23
- package/lib/hls/manifest_text_parser.js +1 -0
- package/lib/media/quality_observer.js +274 -0
- package/lib/media/region_observer.js +39 -33
- package/lib/media/region_timeline.js +17 -19
- package/lib/media/segment_index.js +8 -1
- package/lib/media/segment_reference.js +37 -4
- package/lib/media/streaming_engine.js +47 -6
- package/lib/net/http_fetch_plugin.js +31 -11
- package/lib/net/http_plugin_utils.js +1 -0
- package/lib/net/http_xhr_plugin.js +40 -14
- package/lib/net/networking_engine.js +85 -18
- package/lib/offline/download_info.js +68 -0
- package/lib/offline/download_manager.js +13 -5
- package/lib/offline/indexeddb/base_storage_cell.js +35 -0
- package/lib/offline/indexeddb/storage_mechanism.js +1 -1
- package/lib/offline/indexeddb/v5_storage_cell.js +5 -0
- package/lib/offline/storage.js +401 -293
- package/lib/offline/stored_content_utils.js +2 -0
- package/lib/player.js +254 -27
- package/lib/polyfill/media_capabilities.js +30 -15
- package/lib/polyfill/random_uuid.js +52 -0
- package/lib/text/cue.js +18 -0
- package/lib/text/ttml_text_parser.js +109 -60
- package/lib/text/ui_text_displayer.js +19 -13
- package/lib/util/cmcd_manager.js +672 -0
- package/lib/util/error.js +6 -0
- package/lib/util/fake_event_target.js +21 -1
- package/lib/util/language_utils.js +12 -0
- package/lib/util/mutex.js +52 -0
- package/lib/util/periods.js +98 -64
- package/lib/util/player_configuration.js +18 -0
- package/lib/util/stream_utils.js +86 -10
- package/lib/util/xml_utils.js +19 -0
- package/package.json +3 -6
- package/shaka-player.uncompiled.js +3 -0
- package/test/dash/dash_parser_content_protection_unit.js +2 -0
- package/test/dash/dash_parser_live_unit.js +144 -1
- package/test/dash/dash_parser_manifest_unit.js +37 -2
- package/test/dash/dash_parser_segment_base_unit.js +2 -0
- package/test/dash/dash_parser_segment_list_unit.js +2 -1
- package/test/dash/dash_parser_segment_template_unit.js +2 -1
- package/test/hls/hls_live_unit.js +2 -0
- package/test/hls/hls_parser_unit.js +76 -0
- package/test/media/drm_engine_unit.js +3 -3
- package/test/media/quality_observer_unit.js +142 -0
- package/test/media/region_observer_unit.js +40 -5
- package/test/media/region_timeline_unit.js +40 -22
- package/test/media/segment_reference_unit.js +16 -1
- package/test/media/streaming_engine_integration.js +4 -2
- package/test/media/streaming_engine_unit.js +147 -0
- package/test/net/http_plugin_unit.js +20 -15
- package/test/offline/storage_integration.js +108 -4
- package/test/player_integration.js +140 -8
- package/test/player_src_equals_integration.js +41 -8
- package/test/player_unit.js +19 -9
- package/test/test/assets/chapters.vtt +2 -2
- package/test/test/assets/chapters2.vtt +10 -0
- package/test/test/util/dash_parser_util.js +4 -0
- package/test/test/util/fake_ad.js +7 -0
- package/test/test/util/fake_drm_engine.js +9 -2
- package/test/test/util/manifest_generator.js +3 -2
- package/test/test/util/test_scheme.js +14 -1
- package/test/test/util/ttml_utils.js +81 -0
- package/test/test/util/util.js +10 -7
- package/test/text/mp4_ttml_parser_unit.js +11 -18
- package/test/text/ttml_text_parser_unit.js +544 -228
- package/test/ui/text_displayer_layout_unit.js +8 -7
- package/test/ui/ui_unit.js +250 -0
- package/test/util/cmcd_manager_unit.js +291 -0
- package/test/util/stream_utils_unit.js +36 -30
- package/third_party/closure-uri/uri.js +21 -0
- package/ui/airplay_button.js +1 -0
- package/ui/audio_language_selection.js +7 -0
- package/ui/cast_button.js +1 -0
- package/ui/context_menu.js +109 -0
- package/ui/controls.js +44 -7
- package/ui/controls.less +1 -0
- package/ui/enums.js +4 -0
- package/ui/externs/ui.js +28 -1
- package/ui/fast_forward_button.js +14 -5
- package/ui/fullscreen_button.js +1 -0
- package/ui/less/buttons.less +0 -4
- package/ui/less/containers.less +74 -0
- package/ui/less/overflow_menu.less +0 -10
- package/ui/less/tooltip.less +97 -0
- package/ui/locales/en.json +1 -0
- package/ui/locales/source.json +4 -0
- package/ui/localization.js +12 -36
- package/ui/loop_button.js +5 -0
- package/ui/mute_button.js +2 -1
- package/ui/overflow_menu.js +5 -25
- package/ui/pip_button.js +5 -0
- package/ui/play_button.js +2 -2
- package/ui/playback_rate_selection.js +8 -9
- package/ui/resolution_selection.js +7 -0
- package/ui/rewind_button.js +15 -6
- package/ui/seek_bar.js +4 -2
- package/ui/settings_menu.js +36 -4
- package/ui/small_play_button.js +1 -0
- package/ui/statistics_button.js +253 -0
- package/ui/text_selection.js +12 -4
- package/ui/ui.js +30 -0
- package/ui/volume_bar.js +1 -1
- package/externs/media_capabilities.js +0 -16
|
@@ -15,6 +15,9 @@ describe('RegionObserver', () => {
|
|
|
15
15
|
/** @type {!shaka.media.RegionObserver} */
|
|
16
16
|
let observer;
|
|
17
17
|
|
|
18
|
+
/** @type {!jasmine.Spy} */
|
|
19
|
+
let onSeekRange;
|
|
20
|
+
|
|
18
21
|
/** @type {!jasmine.Spy} */
|
|
19
22
|
let onEnterRegion;
|
|
20
23
|
/** @type {!jasmine.Spy} */
|
|
@@ -23,18 +26,26 @@ describe('RegionObserver', () => {
|
|
|
23
26
|
let onSkipRegion;
|
|
24
27
|
|
|
25
28
|
beforeEach(() => {
|
|
29
|
+
onSeekRange = jasmine.createSpy('onSeekRange');
|
|
30
|
+
onSeekRange.and.returnValue({start: 0, end: 100});
|
|
31
|
+
|
|
26
32
|
onEnterRegion = jasmine.createSpy('onEnterRegion');
|
|
27
33
|
onExitRegion = jasmine.createSpy('onExitRegion');
|
|
28
34
|
onSkipRegion = jasmine.createSpy('onSkipRegion');
|
|
29
35
|
|
|
30
36
|
timeline = new shaka.media.RegionTimeline(
|
|
31
|
-
()
|
|
37
|
+
shaka.test.Util.spyFunc(onSeekRange));
|
|
32
38
|
|
|
33
39
|
observer = new shaka.media.RegionObserver(timeline);
|
|
34
|
-
observer.
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
40
|
+
observer.addEventListener('enter', (event) => {
|
|
41
|
+
shaka.test.Util.spyFunc(onEnterRegion)(event['region'], event['seeking']);
|
|
42
|
+
});
|
|
43
|
+
observer.addEventListener('exit', (event) => {
|
|
44
|
+
shaka.test.Util.spyFunc(onExitRegion)(event['region'], event['seeking']);
|
|
45
|
+
});
|
|
46
|
+
observer.addEventListener('skip', (event) => {
|
|
47
|
+
shaka.test.Util.spyFunc(onSkipRegion)(event['region'], event['seeking']);
|
|
48
|
+
});
|
|
38
49
|
});
|
|
39
50
|
|
|
40
51
|
it('fires enter event when adding a region the playhead is in', () => {
|
|
@@ -313,6 +324,30 @@ describe('RegionObserver', () => {
|
|
|
313
324
|
expect(onSkipRegion).not.toHaveBeenCalled();
|
|
314
325
|
});
|
|
315
326
|
|
|
327
|
+
// See https://github.com/google/shaka-player/issues/3949
|
|
328
|
+
it('cleans up references to regions', async () => {
|
|
329
|
+
const region = createRegion('my-region', 0, 10);
|
|
330
|
+
timeline.addRegion(region);
|
|
331
|
+
|
|
332
|
+
// Poll so that RegionObserver will store regions.
|
|
333
|
+
poll(observer,
|
|
334
|
+
/* timeInSeconds= */ 5,
|
|
335
|
+
/* seeking= */ false);
|
|
336
|
+
|
|
337
|
+
// Hack to get a private member without the compiler complaining:
|
|
338
|
+
/** @type {Map} */
|
|
339
|
+
const regionMap = (/** @type {?} */(observer))['oldPosition_'];
|
|
340
|
+
expect(regionMap.size).not.toBe(0);
|
|
341
|
+
|
|
342
|
+
// Move the seek range so that the region will be removed.
|
|
343
|
+
onSeekRange.and.returnValue({start: 20, end: 100});
|
|
344
|
+
// Give the timeline time to filter regions
|
|
345
|
+
await shaka.test.Util.delay(
|
|
346
|
+
shaka.media.RegionTimeline.REGION_FILTER_INTERVAL * 2);
|
|
347
|
+
|
|
348
|
+
expect(regionMap.size).toBe(0);
|
|
349
|
+
});
|
|
350
|
+
|
|
316
351
|
/**
|
|
317
352
|
* @param {string} id
|
|
318
353
|
* @param {number} startTimeSeconds
|
|
@@ -14,18 +14,27 @@ describe('RegionTimeline', () => {
|
|
|
14
14
|
/** @type {!jasmine.Spy} */
|
|
15
15
|
let onNewRegion;
|
|
16
16
|
|
|
17
|
+
/** @type {!jasmine.Spy} */
|
|
18
|
+
let onRemoveRegion;
|
|
19
|
+
|
|
17
20
|
/** @type {!jasmine.Spy} */
|
|
18
21
|
let onSeekRange;
|
|
19
22
|
|
|
20
23
|
beforeEach(() => {
|
|
21
|
-
onNewRegion = jasmine.createSpy('onNewRegion');
|
|
22
|
-
|
|
23
24
|
onSeekRange = jasmine.createSpy('onSeekRange');
|
|
24
25
|
onSeekRange.and.returnValue({start: 0, end: 100});
|
|
25
|
-
|
|
26
26
|
timeline = new shaka.media.RegionTimeline(
|
|
27
27
|
shaka.test.Util.spyFunc(onSeekRange));
|
|
28
|
-
|
|
28
|
+
|
|
29
|
+
onNewRegion = jasmine.createSpy('onNewRegion');
|
|
30
|
+
timeline.addEventListener('regionadd', (event) => {
|
|
31
|
+
shaka.test.Util.spyFunc(onNewRegion)(event['region']);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
onRemoveRegion = jasmine.createSpy('onRemoveRegion');
|
|
35
|
+
timeline.addEventListener('regionremove', (event) => {
|
|
36
|
+
shaka.test.Util.spyFunc(onRemoveRegion)(event['region']);
|
|
37
|
+
});
|
|
29
38
|
});
|
|
30
39
|
|
|
31
40
|
afterEach(() => {
|
|
@@ -85,24 +94,33 @@ describe('RegionTimeline', () => {
|
|
|
85
94
|
]);
|
|
86
95
|
});
|
|
87
96
|
|
|
88
|
-
it('filters
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
97
|
+
it('filters regions that end before the seek range', async () => {
|
|
98
|
+
onSeekRange.and.returnValue({start: 5, end: 100});
|
|
99
|
+
|
|
100
|
+
const region1 = createRegion('urn:foo', 'my-region', 0, 3);
|
|
101
|
+
const region2 = createRegion('urn:foo', 'my-region', 3, 10);
|
|
102
|
+
const region3 = createRegion('urn:foo', 'my-region', 5, 10);
|
|
103
|
+
|
|
104
|
+
timeline.addRegion(region1);
|
|
105
|
+
timeline.addRegion(region2);
|
|
106
|
+
timeline.addRegion(region3);
|
|
107
|
+
expect(onNewRegion).toHaveBeenCalledTimes(3);
|
|
108
|
+
expect(onNewRegion).toHaveBeenCalledWith(region1);
|
|
109
|
+
expect(onNewRegion).toHaveBeenCalledWith(region2);
|
|
110
|
+
expect(onNewRegion).toHaveBeenCalledWith(region3);
|
|
111
|
+
|
|
112
|
+
let regions = Array.from(timeline.regions());
|
|
113
|
+
expect(regions.length).toBe(3);
|
|
114
|
+
|
|
115
|
+
// Give the timeline time to filter regions
|
|
116
|
+
await shaka.test.Util.delay(
|
|
117
|
+
shaka.media.RegionTimeline.REGION_FILTER_INTERVAL * 2);
|
|
118
|
+
|
|
119
|
+
regions = Array.from(timeline.regions());
|
|
120
|
+
expect(regions).toEqual([region2, region3]);
|
|
121
|
+
|
|
122
|
+
expect(onRemoveRegion).toHaveBeenCalledWith(region1);
|
|
123
|
+
});
|
|
106
124
|
|
|
107
125
|
/**
|
|
108
126
|
* @param {string} schemeIdUri
|
|
@@ -38,14 +38,29 @@ describe('SegmentReference', () => {
|
|
|
38
38
|
});
|
|
39
39
|
|
|
40
40
|
describe('InitSegmentReference', () => {
|
|
41
|
+
const mediaQuality = {
|
|
42
|
+
bandwidth: 1,
|
|
43
|
+
audioSamplingRate: 444000,
|
|
44
|
+
codecs: 'my codec',
|
|
45
|
+
contentType: 'video',
|
|
46
|
+
frameRate: 30,
|
|
47
|
+
height: 720,
|
|
48
|
+
mimeType: 'mime type',
|
|
49
|
+
channelsCount: 2,
|
|
50
|
+
pixelAspectRatio: '1:1',
|
|
51
|
+
width: 1280,
|
|
52
|
+
};
|
|
53
|
+
|
|
41
54
|
it('returns in getters values from constructor parameters', () => {
|
|
42
55
|
const reference = new shaka.media.InitSegmentReference(
|
|
43
56
|
/* getUris= */ () => ['x', 'y'],
|
|
44
57
|
/* startByte= */ 4,
|
|
45
|
-
/* endByte= */ 5
|
|
58
|
+
/* endByte= */ 5,
|
|
59
|
+
mediaQuality);
|
|
46
60
|
|
|
47
61
|
expect(reference.getUris()).toEqual(['x', 'y']);
|
|
48
62
|
expect(reference.getStartByte()).toBe(4);
|
|
49
63
|
expect(reference.getEndByte()).toBe(5);
|
|
64
|
+
expect(reference.getMediaQuality()).toBe(mediaQuality);
|
|
50
65
|
});
|
|
51
66
|
});
|
|
@@ -60,7 +60,6 @@ describe('StreamingEngine', () => {
|
|
|
60
60
|
/** @type {!shaka.media.StreamingEngine} */
|
|
61
61
|
let streamingEngine;
|
|
62
62
|
|
|
63
|
-
|
|
64
63
|
/** @type {shaka.extern.Variant} */
|
|
65
64
|
let variant;
|
|
66
65
|
|
|
@@ -262,6 +261,7 @@ describe('StreamingEngine', () => {
|
|
|
262
261
|
|
|
263
262
|
function createStreamingEngine() {
|
|
264
263
|
const playerInterface = {
|
|
264
|
+
modifySegmentRequest: (request, segmentInfo) => {},
|
|
265
265
|
getPresentationTime: () => playhead.getTime(),
|
|
266
266
|
getBandwidthEstimate: () => 1e6,
|
|
267
267
|
mediaSourceEngine: mediaSourceEngine,
|
|
@@ -270,6 +270,7 @@ describe('StreamingEngine', () => {
|
|
|
270
270
|
onEvent: Util.spyFunc(onEvent),
|
|
271
271
|
onManifestUpdate: () => {},
|
|
272
272
|
onSegmentAppended: () => playhead.notifyOfBufferingChange(),
|
|
273
|
+
onInitSegmentAppended: () => {},
|
|
273
274
|
};
|
|
274
275
|
streamingEngine = new shaka.media.StreamingEngine(
|
|
275
276
|
/** @type {shaka.extern.Manifest} */(manifest), playerInterface);
|
|
@@ -419,8 +420,9 @@ describe('StreamingEngine', () => {
|
|
|
419
420
|
// 3. Playhead seeks to force us back inside the window
|
|
420
421
|
// 4. (maybe) seek if there is a gap at the period boundary
|
|
421
422
|
// 5. (maybe) seek to flush a pipeline stall
|
|
423
|
+
// 6. (maybe) on slower platforms (e.g. GitHub actions)
|
|
422
424
|
expect(seekCount).toBeGreaterThan(2);
|
|
423
|
-
expect(seekCount).toBeLessThan(
|
|
425
|
+
expect(seekCount).toBeLessThan(7);
|
|
424
426
|
});
|
|
425
427
|
});
|
|
426
428
|
|
|
@@ -97,6 +97,9 @@ describe('StreamingEngine', () => {
|
|
|
97
97
|
/** @type {!shaka.media.StreamingEngine} */
|
|
98
98
|
let streamingEngine;
|
|
99
99
|
|
|
100
|
+
/** @type {function(function(), number)} */
|
|
101
|
+
let realSetTimeout;
|
|
102
|
+
|
|
100
103
|
/**
|
|
101
104
|
* Runs the fake event loop.
|
|
102
105
|
* @param {function()=} callback An optional callback that is executed
|
|
@@ -116,6 +119,7 @@ describe('StreamingEngine', () => {
|
|
|
116
119
|
}
|
|
117
120
|
|
|
118
121
|
beforeAll(() => {
|
|
122
|
+
realSetTimeout = window.setTimeout;
|
|
119
123
|
jasmine.clock().install();
|
|
120
124
|
jasmine.clock().mockDate();
|
|
121
125
|
});
|
|
@@ -451,6 +455,7 @@ describe('StreamingEngine', () => {
|
|
|
451
455
|
presentationTimeInSeconds != undefined,
|
|
452
456
|
'All tests should have defined an initial presentation time by now!');
|
|
453
457
|
const playerInterface = {
|
|
458
|
+
modifySegmentRequest: (request, segmentInfo) => {},
|
|
454
459
|
getPresentationTime: () => presentationTimeInSeconds,
|
|
455
460
|
getBandwidthEstimate: Util.spyFunc(getBandwidthEstimate),
|
|
456
461
|
mediaSourceEngine: mediaSourceEngine,
|
|
@@ -459,6 +464,7 @@ describe('StreamingEngine', () => {
|
|
|
459
464
|
onEvent: Util.spyFunc(onEvent),
|
|
460
465
|
onManifestUpdate: Util.spyFunc(onManifestUpdate),
|
|
461
466
|
onSegmentAppended: Util.spyFunc(onSegmentAppended),
|
|
467
|
+
onInitSegmentAppended: () => {},
|
|
462
468
|
};
|
|
463
469
|
streamingEngine = new shaka.media.StreamingEngine(
|
|
464
470
|
/** @type {shaka.extern.Manifest} */(manifest), playerInterface);
|
|
@@ -822,6 +828,53 @@ describe('StreamingEngine', () => {
|
|
|
822
828
|
.toHaveBeenCalledWith('video', 0, lt20, gt40);
|
|
823
829
|
});
|
|
824
830
|
|
|
831
|
+
// Regression test for https://github.com/google/shaka-player/issues/3717
|
|
832
|
+
it('applies fudge factors for the duration', async () => {
|
|
833
|
+
setupVod();
|
|
834
|
+
|
|
835
|
+
// In #3717, the duration was just barely large enough to encompass an
|
|
836
|
+
// additional segment, but that segment didn't exist, so playback never
|
|
837
|
+
// completed. Here, we set the duration to just beyond the 3rd segment, and
|
|
838
|
+
// we make the 4th segment fail when requested.
|
|
839
|
+
const duration = 30.000000005;
|
|
840
|
+
timeline.getDuration.and.returnValue(duration);
|
|
841
|
+
|
|
842
|
+
const targetUri = '1_video_3'; // The URI of the 4th video segment.
|
|
843
|
+
failRequestsForTarget(netEngine, targetUri);
|
|
844
|
+
|
|
845
|
+
mediaSourceEngine = new shaka.test.FakeMediaSourceEngine(segmentData);
|
|
846
|
+
createStreamingEngine();
|
|
847
|
+
|
|
848
|
+
// Here we go!
|
|
849
|
+
streamingEngine.switchVariant(variant);
|
|
850
|
+
streamingEngine.switchTextStream(textStream);
|
|
851
|
+
await streamingEngine.start();
|
|
852
|
+
playing = true;
|
|
853
|
+
await runTest();
|
|
854
|
+
|
|
855
|
+
// The end of the stream should have been reached, and the 4th segment from
|
|
856
|
+
// each type should never have been requested.
|
|
857
|
+
expect(mediaSourceEngine.endOfStream).toHaveBeenCalled();
|
|
858
|
+
|
|
859
|
+
const segmentType = shaka.net.NetworkingEngine.RequestType.SEGMENT;
|
|
860
|
+
|
|
861
|
+
netEngine.expectRequest('0_audio_0', segmentType);
|
|
862
|
+
netEngine.expectRequest('0_video_0', segmentType);
|
|
863
|
+
netEngine.expectRequest('0_text_0', segmentType);
|
|
864
|
+
|
|
865
|
+
netEngine.expectRequest('0_audio_1', segmentType);
|
|
866
|
+
netEngine.expectRequest('0_video_1', segmentType);
|
|
867
|
+
netEngine.expectRequest('0_text_1', segmentType);
|
|
868
|
+
|
|
869
|
+
netEngine.expectRequest('1_audio_2', segmentType);
|
|
870
|
+
netEngine.expectRequest('1_video_2', segmentType);
|
|
871
|
+
netEngine.expectRequest('1_text_2', segmentType);
|
|
872
|
+
|
|
873
|
+
netEngine.expectNoRequest('1_audio_3', segmentType);
|
|
874
|
+
netEngine.expectNoRequest('1_video_3', segmentType);
|
|
875
|
+
netEngine.expectNoRequest('1_text_3', segmentType);
|
|
876
|
+
});
|
|
877
|
+
|
|
825
878
|
it('does not buffer one media type ahead of another', async () => {
|
|
826
879
|
setupVod();
|
|
827
880
|
mediaSourceEngine = new shaka.test.FakeMediaSourceEngine(segmentData);
|
|
@@ -1389,6 +1442,66 @@ describe('StreamingEngine', () => {
|
|
|
1389
1442
|
});
|
|
1390
1443
|
});
|
|
1391
1444
|
|
|
1445
|
+
it('into unbuffered regions when nothing is buffered ' +
|
|
1446
|
+
'and mediaState is performing an update', async () => {
|
|
1447
|
+
// Here we go!
|
|
1448
|
+
streamingEngine.switchVariant(variant);
|
|
1449
|
+
streamingEngine.switchTextStream(textStream);
|
|
1450
|
+
// ensure init source buffer promise does not resolve before seeked()
|
|
1451
|
+
// so mediaState remains in "performingUpdate" state
|
|
1452
|
+
const initSourceBufferPromise = new shaka.util.PublicPromise();
|
|
1453
|
+
mediaSourceEngine.setStreamProperties.and
|
|
1454
|
+
.returnValue(initSourceBufferPromise);
|
|
1455
|
+
await streamingEngine.start();
|
|
1456
|
+
playing = true;
|
|
1457
|
+
|
|
1458
|
+
// tick to trigger mediaState updates
|
|
1459
|
+
jasmine.clock().tick(1);
|
|
1460
|
+
// give a chance for fetchAndAppend_ to be invoked
|
|
1461
|
+
// by async onUpdate_ callback
|
|
1462
|
+
await Util.shortDelay(realSetTimeout);
|
|
1463
|
+
|
|
1464
|
+
// Nothing is buffered yet.
|
|
1465
|
+
expect(mediaSourceEngine.segments).toEqual({
|
|
1466
|
+
audio: [false, false, false, false],
|
|
1467
|
+
video: [false, false, false, false],
|
|
1468
|
+
text: [false, false, false, false],
|
|
1469
|
+
});
|
|
1470
|
+
|
|
1471
|
+
// Seek forward to an unbuffered region in the first Period.
|
|
1472
|
+
presentationTimeInSeconds = 15;
|
|
1473
|
+
streamingEngine.seeked();
|
|
1474
|
+
|
|
1475
|
+
// resolve initSourceBufferPromise after seeked(), waitingToClearBuffer
|
|
1476
|
+
// should have been set, so this will now trigger the actual flush of
|
|
1477
|
+
// the buffer
|
|
1478
|
+
initSourceBufferPromise.resolve();
|
|
1479
|
+
mediaSourceEngine.setStreamProperties.and
|
|
1480
|
+
.returnValue(Promise.resolve());
|
|
1481
|
+
|
|
1482
|
+
// allow mediaState update to resolve
|
|
1483
|
+
await Util.shortDelay(realSetTimeout);
|
|
1484
|
+
|
|
1485
|
+
onTick.and.callFake(() => {
|
|
1486
|
+
expect(mediaSourceEngine.clear).toHaveBeenCalled();
|
|
1487
|
+
onTick.and.stub();
|
|
1488
|
+
});
|
|
1489
|
+
|
|
1490
|
+
await runTest(Util.spyFunc(onTick));
|
|
1491
|
+
|
|
1492
|
+
// Verify buffers.
|
|
1493
|
+
expect(mediaSourceEngine.initSegments).toEqual({
|
|
1494
|
+
audio: [false, true],
|
|
1495
|
+
video: [false, true],
|
|
1496
|
+
text: [],
|
|
1497
|
+
});
|
|
1498
|
+
expect(mediaSourceEngine.segments).toEqual({
|
|
1499
|
+
audio: [false, true, true, true],
|
|
1500
|
+
video: [false, true, true, true],
|
|
1501
|
+
text: [false, true, true, true],
|
|
1502
|
+
});
|
|
1503
|
+
});
|
|
1504
|
+
|
|
1392
1505
|
it('into unbuffered regions near segment start', async () => {
|
|
1393
1506
|
// Here we go!
|
|
1394
1507
|
streamingEngine.switchVariant(variant);
|
|
@@ -3388,6 +3501,40 @@ describe('StreamingEngine', () => {
|
|
|
3388
3501
|
segmentAvailability.end++;
|
|
3389
3502
|
}
|
|
3390
3503
|
|
|
3504
|
+
/**
|
|
3505
|
+
* @param {!shaka.test.FakeNetworkingEngine} netEngine A NetworkingEngine
|
|
3506
|
+
* look-alike.
|
|
3507
|
+
* @param {string} targetUri
|
|
3508
|
+
* @param {shaka.util.Error.Code=} errorCode
|
|
3509
|
+
*/
|
|
3510
|
+
function failRequestsForTarget(
|
|
3511
|
+
netEngine, targetUri, errorCode=shaka.util.Error.Code.BAD_HTTP_STATUS) {
|
|
3512
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
3513
|
+
const originalNetEngineRequest = netEngine.request.bind(netEngine);
|
|
3514
|
+
|
|
3515
|
+
netEngine.request = jasmine.createSpy('request').and.callFake(
|
|
3516
|
+
(requestType, request) => {
|
|
3517
|
+
if (request.uris[0] == targetUri) {
|
|
3518
|
+
const data = [targetUri];
|
|
3519
|
+
|
|
3520
|
+
if (errorCode == shaka.util.Error.Code.BAD_HTTP_STATUS) {
|
|
3521
|
+
data.push(404);
|
|
3522
|
+
data.push('');
|
|
3523
|
+
}
|
|
3524
|
+
|
|
3525
|
+
// The compiler still sees the error code parameter as potentially
|
|
3526
|
+
// undefined, even though we gave it a default value.
|
|
3527
|
+
goog.asserts.assert(errorCode != undefined, 'Undefined error code');
|
|
3528
|
+
|
|
3529
|
+
return shaka.util.AbortableOperation.failed(new shaka.util.Error(
|
|
3530
|
+
shaka.util.Error.Severity.CRITICAL,
|
|
3531
|
+
shaka.util.Error.Category.NETWORK,
|
|
3532
|
+
errorCode, data));
|
|
3533
|
+
}
|
|
3534
|
+
return originalNetEngineRequest(requestType, request);
|
|
3535
|
+
});
|
|
3536
|
+
}
|
|
3537
|
+
|
|
3391
3538
|
/**
|
|
3392
3539
|
* @param {!shaka.test.FakeNetworkingEngine} netEngine A NetworkingEngine
|
|
3393
3540
|
* look-alike.
|
|
@@ -26,6 +26,9 @@ function httpPluginTests(usingFetch) {
|
|
|
26
26
|
// A dummy progress callback.
|
|
27
27
|
const progressUpdated = (elapsedMs, bytes, bytesRemaining) => {};
|
|
28
28
|
|
|
29
|
+
// A dummy headers callback.
|
|
30
|
+
const headersReceived = (headers) => {};
|
|
31
|
+
|
|
29
32
|
/** @type {shaka.extern.RetryParameters} */
|
|
30
33
|
let retryParameters;
|
|
31
34
|
|
|
@@ -168,7 +171,8 @@ function httpPluginTests(usingFetch) {
|
|
|
168
171
|
request.method = 'POST';
|
|
169
172
|
request.headers['BAZ'] = '123';
|
|
170
173
|
|
|
171
|
-
await plugin(
|
|
174
|
+
await plugin(
|
|
175
|
+
request.uris[0], request, requestType, progressUpdated, headersReceived)
|
|
172
176
|
.promise;
|
|
173
177
|
|
|
174
178
|
const actual = mostRecentRequest();
|
|
@@ -189,8 +193,8 @@ function httpPluginTests(usingFetch) {
|
|
|
189
193
|
request.body = null;
|
|
190
194
|
request.method = 'GET';
|
|
191
195
|
|
|
192
|
-
await plugin(request.uris[0], request, requestType, progressUpdated
|
|
193
|
-
.promise;
|
|
196
|
+
await plugin(request.uris[0], request, requestType, progressUpdated,
|
|
197
|
+
headersReceived).promise;
|
|
194
198
|
|
|
195
199
|
const actual = jasmine.Fetch.requests.mostRecent();
|
|
196
200
|
expect(actual).toBeTruthy();
|
|
@@ -205,8 +209,8 @@ function httpPluginTests(usingFetch) {
|
|
|
205
209
|
|
|
206
210
|
const request = shaka.net.NetworkingEngine.makeRequest(
|
|
207
211
|
[uri], retryParameters, Util.spyFunc(streamDataCallback));
|
|
208
|
-
const response =
|
|
209
|
-
|
|
212
|
+
const response = await plugin(
|
|
213
|
+
uri, request, requestType, progressUpdated, headersReceived).promise;
|
|
210
214
|
|
|
211
215
|
expect(mostRecentRequest().url).toBe(uri);
|
|
212
216
|
expect(response).toBeTruthy();
|
|
@@ -297,9 +301,9 @@ function httpPluginTests(usingFetch) {
|
|
|
297
301
|
const request = shaka.net.NetworkingEngine.makeRequest(
|
|
298
302
|
['https://foo.bar/cache'], retryParameters);
|
|
299
303
|
|
|
300
|
-
const response =
|
|
301
|
-
|
|
302
|
-
|
|
304
|
+
const response = await plugin(
|
|
305
|
+
request.uris[0], request, requestType, progressUpdated, headersReceived)
|
|
306
|
+
.promise;
|
|
303
307
|
expect(response).toBeTruthy();
|
|
304
308
|
expect(response.fromCache).toBe(true);
|
|
305
309
|
});
|
|
@@ -312,8 +316,8 @@ function httpPluginTests(usingFetch) {
|
|
|
312
316
|
uri = 'https://foo.bar/timeout';
|
|
313
317
|
const request = shaka.net.NetworkingEngine.makeRequest(
|
|
314
318
|
[uri], retryParameters);
|
|
315
|
-
const operation = plugin(
|
|
316
|
-
|
|
319
|
+
const operation = plugin(request.uris[0], request, requestType,
|
|
320
|
+
progressUpdated, headersReceived);
|
|
317
321
|
|
|
318
322
|
/** @type {jasmine.Fetch.RequestStub} */
|
|
319
323
|
const actual = jasmine.Fetch.requests.mostRecent();
|
|
@@ -354,8 +358,8 @@ function httpPluginTests(usingFetch) {
|
|
|
354
358
|
uri = 'https://foo.bar/';
|
|
355
359
|
const request = shaka.net.NetworkingEngine.makeRequest(
|
|
356
360
|
[uri], retryParameters);
|
|
357
|
-
operation = plugin(
|
|
358
|
-
|
|
361
|
+
operation = plugin(request.uris[0], request, requestType, progressUpdated,
|
|
362
|
+
headersReceived);
|
|
359
363
|
requestPromise = operation.promise;
|
|
360
364
|
}
|
|
361
365
|
|
|
@@ -388,8 +392,8 @@ function httpPluginTests(usingFetch) {
|
|
|
388
392
|
async function testSucceeds(uri, overrideUri) {
|
|
389
393
|
const request = shaka.net.NetworkingEngine.makeRequest(
|
|
390
394
|
[uri], retryParameters);
|
|
391
|
-
const response =
|
|
392
|
-
|
|
395
|
+
const response = await plugin(
|
|
396
|
+
uri, request, requestType, progressUpdated, headersReceived).promise;
|
|
393
397
|
|
|
394
398
|
expect(mostRecentRequest().url).toBe(uri);
|
|
395
399
|
expect(response).toBeTruthy();
|
|
@@ -410,7 +414,8 @@ function httpPluginTests(usingFetch) {
|
|
|
410
414
|
const request = shaka.net.NetworkingEngine.makeRequest(
|
|
411
415
|
[uri], retryParameters);
|
|
412
416
|
|
|
413
|
-
const p = plugin(
|
|
417
|
+
const p = plugin(
|
|
418
|
+
uri, request, requestType, progressUpdated, headersReceived).promise;
|
|
414
419
|
if (expected.code == shaka.util.Error.Code.TIMEOUT) {
|
|
415
420
|
jasmine.clock().tick(5000);
|
|
416
421
|
}
|
|
@@ -22,10 +22,12 @@ goog.require('shaka.test.ManifestGenerator');
|
|
|
22
22
|
goog.require('shaka.test.TestScheme');
|
|
23
23
|
goog.require('shaka.test.Util');
|
|
24
24
|
goog.require('shaka.util.AbortableOperation');
|
|
25
|
+
goog.require('shaka.util.BufferUtils');
|
|
25
26
|
goog.require('shaka.util.Error');
|
|
26
27
|
goog.require('shaka.util.EventManager');
|
|
27
28
|
goog.require('shaka.util.PlayerConfiguration');
|
|
28
29
|
goog.require('shaka.util.PublicPromise');
|
|
30
|
+
goog.require('shaka.util.Uint8ArrayUtils');
|
|
29
31
|
goog.requireType('shaka.media.SegmentReference');
|
|
30
32
|
|
|
31
33
|
/** @return {boolean} */
|
|
@@ -58,12 +60,16 @@ filterDescribe('Storage', storageSupport, () => {
|
|
|
58
60
|
const manifestWithNonZeroStartUri = 'fake:manifest-with-non-zero-start';
|
|
59
61
|
const manifestWithLiveTimelineUri = 'fake:manifest-with-live-timeline';
|
|
60
62
|
const manifestWithAlternateSegmentsUri = 'fake:manifest-with-alt-segments';
|
|
63
|
+
const manifestWithVideoInitSegmentsUri =
|
|
64
|
+
'fake:manifest-with-video-init-segments';
|
|
61
65
|
|
|
66
|
+
const initSegmentUri = 'fake:init-segment';
|
|
62
67
|
const segment1Uri = 'fake:segment-1';
|
|
63
68
|
const segment2Uri = 'fake:segment-2';
|
|
64
69
|
const segment3Uri = 'fake:segment-3';
|
|
65
70
|
const segment4Uri = 'fake:segment-4';
|
|
66
71
|
|
|
72
|
+
const alternateInitSegmentUri = 'fake:alt-init-segment';
|
|
67
73
|
const alternateSegment1Uri = 'fake:alt-segment-1';
|
|
68
74
|
const alternateSegment2Uri = 'fake:alt-segment-2';
|
|
69
75
|
const alternateSegment3Uri = 'fake:alt-segment-3';
|
|
@@ -945,6 +951,46 @@ filterDescribe('Storage', storageSupport, () => {
|
|
|
945
951
|
}
|
|
946
952
|
});
|
|
947
953
|
|
|
954
|
+
it('can extract DRM info from segments', async () => {
|
|
955
|
+
const pssh1 =
|
|
956
|
+
'00000028' + // atom size
|
|
957
|
+
'70737368' + // atom type='pssh'
|
|
958
|
+
'00000000' + // v0, flags=0
|
|
959
|
+
'edef8ba979d64acea3c827dcd51d21ed' + // system id (Widevine)
|
|
960
|
+
'00000008' + // data size
|
|
961
|
+
'0102030405060708'; // data
|
|
962
|
+
const psshData1 = shaka.util.Uint8ArrayUtils.fromHex(pssh1);
|
|
963
|
+
const pssh2 =
|
|
964
|
+
'00000028' + // atom size
|
|
965
|
+
'70737368' + // atom type='pssh'
|
|
966
|
+
'00000000' + // v0, flags=0
|
|
967
|
+
'edef8ba979d64acea3c827dcd51d21ed' + // system id (Widevine)
|
|
968
|
+
'00000008' + // data size
|
|
969
|
+
'1337420123456789'; // data
|
|
970
|
+
const psshData2 = shaka.util.Uint8ArrayUtils.fromHex(pssh2);
|
|
971
|
+
netEngine.setResponseValue(initSegmentUri,
|
|
972
|
+
shaka.util.BufferUtils.toArrayBuffer(psshData1));
|
|
973
|
+
netEngine.setResponseValue(alternateInitSegmentUri,
|
|
974
|
+
shaka.util.BufferUtils.toArrayBuffer(psshData2));
|
|
975
|
+
|
|
976
|
+
const drm = new shaka.test.FakeDrmEngine();
|
|
977
|
+
const drmInfo = makeDrmInfo();
|
|
978
|
+
drmInfo.keySystem = 'com.widevine.alpha';
|
|
979
|
+
drm.setDrmInfo(drmInfo);
|
|
980
|
+
overrideDrmAndManifest(
|
|
981
|
+
storage,
|
|
982
|
+
drm,
|
|
983
|
+
makeManifestWithVideoInitSegments());
|
|
984
|
+
|
|
985
|
+
const stored = await storage.store(
|
|
986
|
+
manifestWithVideoInitSegmentsUri, noMetadata, fakeMimeType).promise;
|
|
987
|
+
goog.asserts.assert(stored.offlineUri != null, 'URI should not be null!');
|
|
988
|
+
|
|
989
|
+
// The manifest chooses the alternate stream, so expect only the alt init
|
|
990
|
+
// segment.
|
|
991
|
+
expect(drm.newInitData).toHaveBeenCalledWith('cenc', psshData2);
|
|
992
|
+
});
|
|
993
|
+
|
|
948
994
|
it('can store multiple assets at once', async () => {
|
|
949
995
|
// Block the network so that we won't finish the first store command.
|
|
950
996
|
/** @type {!shaka.util.PublicPromise} */
|
|
@@ -1234,11 +1280,11 @@ filterDescribe('Storage', storageSupport, () => {
|
|
|
1234
1280
|
goog.asserts.assert(
|
|
1235
1281
|
content.offlineUri != null, 'URI should not be null!');
|
|
1236
1282
|
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1283
|
+
// We expect 5 progress events because there are 4 unique segments, plus
|
|
1284
|
+
// the manifest.
|
|
1285
|
+
/** @type {!Array.<number>}*/
|
|
1240
1286
|
const progressSteps = [
|
|
1241
|
-
0.
|
|
1287
|
+
0.2, 0.4, 0.6, 0.8, 1,
|
|
1242
1288
|
];
|
|
1243
1289
|
|
|
1244
1290
|
const progressCallback = (content, progress) => {
|
|
@@ -1399,6 +1445,62 @@ filterDescribe('Storage', storageSupport, () => {
|
|
|
1399
1445
|
};
|
|
1400
1446
|
}
|
|
1401
1447
|
|
|
1448
|
+
/** @return {shaka.extern.Manifest} */
|
|
1449
|
+
function makeManifestWithVideoInitSegments() {
|
|
1450
|
+
const manifest = shaka.test.ManifestGenerator.generate((manifest) => {
|
|
1451
|
+
manifest.presentationTimeline.setDuration(20);
|
|
1452
|
+
|
|
1453
|
+
manifest.addVariant(0, (variant) => {
|
|
1454
|
+
variant.bandwidth = kbps(13);
|
|
1455
|
+
variant.addVideo(1, (stream) => {
|
|
1456
|
+
stream.bandwidth = kbps(13);
|
|
1457
|
+
stream.size(100, 200);
|
|
1458
|
+
});
|
|
1459
|
+
});
|
|
1460
|
+
manifest.addVariant(2, (variant) => {
|
|
1461
|
+
variant.bandwidth = kbps(20);
|
|
1462
|
+
variant.addVideo(3, (stream) => {
|
|
1463
|
+
stream.bandwidth = kbps(20);
|
|
1464
|
+
stream.size(200, 400);
|
|
1465
|
+
});
|
|
1466
|
+
});
|
|
1467
|
+
});
|
|
1468
|
+
|
|
1469
|
+
const stream = manifest.variants[0].video;
|
|
1470
|
+
goog.asserts.assert(stream, 'The first stream should exist');
|
|
1471
|
+
stream.encrypted = true;
|
|
1472
|
+
const init = new shaka.media.InitSegmentReference(
|
|
1473
|
+
() => [initSegmentUri], 0, null);
|
|
1474
|
+
const refs = [
|
|
1475
|
+
makeReference(segment1Uri, 0, 1),
|
|
1476
|
+
makeReference(segment2Uri, 1, 2),
|
|
1477
|
+
makeReference(segment3Uri, 2, 3),
|
|
1478
|
+
makeReference(segment4Uri, 3, 4),
|
|
1479
|
+
];
|
|
1480
|
+
for (const ref of refs) {
|
|
1481
|
+
ref.initSegmentReference = init;
|
|
1482
|
+
}
|
|
1483
|
+
overrideSegmentIndex(stream, refs);
|
|
1484
|
+
|
|
1485
|
+
const streamAlt = manifest.variants[1].video;
|
|
1486
|
+
goog.asserts.assert(streamAlt, 'The second stream should exist');
|
|
1487
|
+
streamAlt.encrypted = true;
|
|
1488
|
+
const initAlt = new shaka.media.InitSegmentReference(
|
|
1489
|
+
() => [alternateInitSegmentUri], 0, null);
|
|
1490
|
+
const refsAlt = [
|
|
1491
|
+
makeReference(alternateSegment1Uri, 0, 1),
|
|
1492
|
+
makeReference(alternateSegment2Uri, 1, 2),
|
|
1493
|
+
makeReference(alternateSegment3Uri, 2, 3),
|
|
1494
|
+
makeReference(alternateSegment4Uri, 3, 4),
|
|
1495
|
+
];
|
|
1496
|
+
for (const ref of refsAlt) {
|
|
1497
|
+
ref.initSegmentReference = initAlt;
|
|
1498
|
+
}
|
|
1499
|
+
overrideSegmentIndex(streamAlt, refsAlt);
|
|
1500
|
+
|
|
1501
|
+
return manifest;
|
|
1502
|
+
}
|
|
1503
|
+
|
|
1402
1504
|
/** @return {shaka.extern.Manifest} */
|
|
1403
1505
|
function makeManifestWithPerStreamBandwidth() {
|
|
1404
1506
|
const manifest = shaka.test.ManifestGenerator.generate((manifest) => {
|
|
@@ -1612,6 +1714,8 @@ filterDescribe('Storage', storageSupport, () => {
|
|
|
1612
1714
|
makeManifestWithLiveTimeline();
|
|
1613
1715
|
this.map_[manifestWithAlternateSegmentsUri] =
|
|
1614
1716
|
makeManifestWithAlternateSegments();
|
|
1717
|
+
this.map_[manifestWithVideoInitSegmentsUri] =
|
|
1718
|
+
makeManifestWithVideoInitSegments();
|
|
1615
1719
|
}
|
|
1616
1720
|
|
|
1617
1721
|
/** @override */
|