shaka-player 3.3.4 → 4.0.1
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/build-and-test.yaml +0 -1
- package/.github/workflows/demo-version-index.yaml +8 -0
- package/.github/workflows/release-please.yaml +19 -3
- package/.github/workflows/selenium-lab-tests.yaml +0 -1
- package/.github/workflows/sync-labels.yaml +33 -0
- package/.github/workflows/update-issues.yaml +29 -0
- package/.github/workflows/validate-pr-title.yaml +35 -0
- package/AUTHORS +1 -0
- package/CHANGELOG.md +209 -28
- package/CONTRIBUTORS +1 -0
- package/README.md +10 -7
- package/app-engine/demo-version-index/README.md +3 -3
- package/app-engine/demo-version-index/app.yaml +27 -1
- package/app-engine/demo-version-index/generate.py +164 -0
- package/app-engine/demo-version-index/main.py +4 -155
- package/app-engine/demo-version-index/requirements.txt +0 -1
- package/build/README.md +2 -2
- package/build/compiler.py +1 -1
- package/build/generateTsDefs.py +0 -2
- package/build/misspellings.txt +1 -0
- package/build/shaka-lab.yaml +0 -2
- package/build/test.py +7 -1
- package/build/types/core +0 -1
- package/build/types/polyfill +0 -3
- package/demo/common/asset.js +17 -1
- package/demo/common/assets.js +29 -0
- package/demo/common/message_ids.js +6 -3
- package/demo/config.js +10 -11
- package/demo/custom.js +39 -0
- package/demo/demo_utils.js +0 -1
- package/demo/locales/en.json +6 -3
- package/demo/locales/source.json +22 -10
- package/demo/main.js +9 -6
- package/demo/search.js +2 -1
- package/dist/controls.css +1 -1
- package/dist/demo.compiled.debug.js +71 -64
- package/dist/demo.compiled.debug.map +3 -3
- package/dist/demo.compiled.js +71 -64
- package/dist/demo.compiled.map +3 -3
- package/dist/demo.css +1 -1
- package/dist/demo.css.map +1 -1
- package/dist/deps.js +16 -20
- package/dist/locales.js +33 -31
- package/dist/receiver.compiled.debug.js +32 -28
- package/dist/receiver.compiled.debug.map +3 -3
- package/dist/receiver.compiled.js +32 -28
- package/dist/receiver.compiled.map +3 -3
- package/dist/shaka-player.compiled.d.ts +78 -82
- package/dist/shaka-player.compiled.debug.d.ts +78 -82
- package/dist/shaka-player.compiled.debug.externs.js +127 -126
- package/dist/shaka-player.compiled.debug.js +471 -523
- package/dist/shaka-player.compiled.debug.map +4 -4
- package/dist/shaka-player.compiled.externs.js +127 -126
- package/dist/shaka-player.compiled.js +844 -858
- package/dist/shaka-player.compiled.map +4 -4
- package/dist/shaka-player.ui.d.ts +80 -82
- package/dist/shaka-player.ui.debug.d.ts +80 -82
- package/dist/shaka-player.ui.debug.externs.js +135 -126
- package/dist/shaka-player.ui.debug.js +531 -581
- package/dist/shaka-player.ui.debug.map +4 -4
- package/dist/shaka-player.ui.externs.js +135 -126
- package/dist/shaka-player.ui.js +991 -1001
- package/dist/shaka-player.ui.map +4 -4
- package/docs/tutorials/config.md +0 -2
- package/docs/tutorials/drm-config.md +5 -0
- package/docs/tutorials/fairplay.md +7 -22
- package/docs/tutorials/faq.md +0 -11
- package/docs/tutorials/index.json +2 -8
- package/docs/tutorials/plugins.md +1 -3
- package/docs/tutorials/upgrade.md +85 -11
- package/externs/polyfill.js +15 -0
- package/externs/shaka/manifest.js +5 -1
- package/externs/shaka/net.js +12 -0
- package/externs/shaka/offline.js +5 -1
- package/externs/shaka/player.js +39 -21
- package/externs/shaka/text.js +12 -12
- package/externs/sourcebuffer.js +15 -0
- package/karma.conf.js +4 -0
- package/lib/ads/client_side_ad.js +13 -2
- package/lib/ads/client_side_ad_manager.js +22 -4
- package/lib/cast/cast_utils.js +0 -1
- package/lib/dash/content_protection.js +85 -5
- package/lib/dash/dash_parser.js +3 -3
- package/lib/hls/hls_parser.js +391 -747
- package/lib/hls/manifest_text_parser.js +2 -3
- package/lib/media/drm_engine.js +102 -37
- package/lib/media/gap_jumping_controller.js +10 -45
- package/lib/media/media_source_engine.js +157 -31
- package/lib/media/playhead.js +2 -5
- package/lib/media/presentation_timeline.js +1 -2
- package/lib/media/segment_index.js +13 -46
- package/lib/media/segment_reference.js +47 -1
- package/lib/media/streaming_engine.js +28 -11
- package/lib/media/time_ranges_utils.js +4 -9
- package/lib/net/networking_engine.js +3 -0
- package/lib/offline/indexeddb/storage_mechanism.js +4 -2
- package/lib/offline/indexeddb/v1_storage_cell.js +1 -0
- package/lib/offline/indexeddb/v2_storage_cell.js +1 -0
- package/lib/offline/manifest_converter.js +1 -0
- package/lib/offline/storage.js +100 -132
- package/lib/player.js +181 -307
- package/lib/polyfill/media_capabilities.js +12 -6
- package/lib/polyfill/mediasource.js +0 -28
- package/lib/polyfill/patchedmediakeys_nop.js +2 -0
- package/lib/polyfill/patchedmediakeys_webkit.js +2 -0
- package/lib/text/cue.js +0 -6
- package/lib/text/lrc_text_parser.js +8 -0
- package/lib/text/mp4_ttml_parser.js +8 -0
- package/lib/text/mp4_vtt_parser.js +8 -0
- package/lib/text/sbv_text_parser.js +8 -0
- package/lib/text/simple_text_displayer.js +1 -7
- package/lib/text/srt_text_parser.js +8 -0
- package/lib/text/ssa_text_parser.js +8 -0
- package/lib/text/text_engine.js +19 -3
- package/lib/text/ttml_text_parser.js +8 -0
- package/lib/text/ui_text_displayer.js +1 -7
- package/lib/text/vtt_text_parser.js +27 -5
- package/lib/text/web_vtt_generator.js +1 -7
- package/lib/util/config_utils.js +25 -0
- package/lib/util/error.js +16 -13
- package/lib/util/fairplay_utils.js +109 -0
- package/lib/util/functional.js +0 -42
- package/lib/util/manifest_parser_utils.js +1 -1
- package/lib/util/mime_utils.js +50 -1
- package/lib/util/periods.js +5 -4
- package/lib/util/platform.js +45 -0
- package/lib/util/player_configuration.js +26 -20
- package/lib/util/pssh.js +23 -4
- package/lib/util/stream_utils.js +96 -24
- package/lib/util/uint8array_utils.js +0 -16
- package/lib/util/xml_utils.js +1 -1
- package/package.json +4 -1
- package/roadmap.md +19 -19
- package/shaka-player.uncompiled.js +0 -3
- package/test/cast/cast_utils_unit.js +1 -0
- package/test/dash/dash_parser_content_protection_unit.js +44 -0
- package/test/dash/dash_parser_manifest_unit.js +0 -24
- package/test/dash/dash_parser_segment_template_unit.js +3 -4
- package/test/demo/demo_unit.js +2 -1
- package/test/hls/hls_live_unit.js +121 -220
- package/test/hls/hls_parser_unit.js +515 -432
- package/test/hls/manifest_text_parser_unit.js +19 -0
- package/test/media/drm_engine_unit.js +86 -120
- package/test/media/media_source_engine_integration.js +26 -3
- package/test/media/media_source_engine_unit.js +7 -6
- package/test/media/playhead_unit.js +27 -191
- package/test/media/segment_index_unit.js +15 -25
- package/test/media/streaming_engine_integration.js +4 -39
- package/test/media/streaming_engine_unit.js +3 -2
- package/test/media/time_ranges_utils_unit.js +0 -5
- package/test/offline/indexeddb_storage_unit.js +1 -1
- package/test/offline/manifest_convert_unit.js +34 -4
- package/test/offline/offline_manifest_parser_unit.js +1 -0
- package/test/offline/storage_compatibility_unit.js +1 -1
- package/test/offline/storage_integration.js +5 -4
- package/test/player_external.js +7 -7
- package/test/player_integration.js +22 -10
- package/test/player_src_equals_integration.js +81 -83
- package/test/player_unit.js +124 -1
- package/test/test/boot.js +9 -0
- package/test/test/externs/filters.js +4 -0
- package/test/test/util/fake_media_source_engine.js +11 -4
- package/test/test/util/manifest_generator.js +3 -1
- package/test/test/util/manifest_parser_util.js +4 -1
- package/test/test/util/offline_utils.js +1 -0
- package/test/test/util/simple_fakes.js +31 -2
- package/test/test/util/streaming_engine_util.js +1 -0
- package/test/test/util/test_scheme.js +24 -1
- package/test/test/util/util.js +4 -4
- package/test/text/cue_integration.js +2 -2
- package/test/text/lrc_text_parser_unit.js +6 -7
- package/test/text/mp4_ttml_parser_unit.js +6 -4
- package/test/text/mp4_vtt_parser_unit.js +6 -5
- package/test/text/sbv_text_parser_unit.js +5 -6
- package/test/text/srt_text_parser_unit.js +5 -6
- package/test/text/ssa_text_parser_unit.js +17 -29
- package/test/text/text_engine_unit.js +45 -5
- package/test/text/ttml_text_parser_unit.js +77 -77
- package/test/text/vtt_text_parser_unit.js +116 -59
- package/test/ui/ui_integration.js +0 -16
- package/test/ui/ui_unit.js +27 -0
- package/test/util/cmcd_manager_unit.js +3 -0
- package/test/util/mime_utils_unit.js +54 -0
- package/test/util/periods_unit.js +79 -0
- package/test/util/stream_utils_unit.js +23 -7
- package/ui/controls.js +65 -40
- package/ui/fullscreen_button.js +26 -6
- package/ui/less/containers.less +3 -3
- package/ui/locales/en.json +1 -0
- package/ui/locales/source.json +5 -0
- package/ui/resolution_selection.js +31 -33
- package/ui/ui.js +0 -17
- package/docs/tutorials/gsoc-ideas-list.md +0 -3
- package/docs/tutorials/upgrade-v2-5.md +0 -441
- package/docs/tutorials/upgrade-v3.md +0 -38
- package/lib/polyfill/mathround.js +0 -49
- package/lib/polyfill/patchedmediakeys_apple.js +0 -744
- package/lib/polyfill/patchedmediakeys_ms.js +0 -698
- package/lib/util/mutex.js +0 -52
- package/test/util/functional_unit.js +0 -82
|
@@ -35,12 +35,9 @@ goog.require('shaka.offline.indexeddb.StorageMechanism');
|
|
|
35
35
|
goog.require('shaka.polyfill.Aria');
|
|
36
36
|
goog.require('shaka.polyfill.EncryptionScheme');
|
|
37
37
|
goog.require('shaka.polyfill.Fullscreen');
|
|
38
|
-
goog.require('shaka.polyfill.MathRound');
|
|
39
38
|
goog.require('shaka.polyfill.MediaSource');
|
|
40
39
|
goog.require('shaka.polyfill.MediaCapabilities');
|
|
41
40
|
goog.require('shaka.polyfill.Orientation');
|
|
42
|
-
goog.require('shaka.polyfill.PatchedMediaKeysApple');
|
|
43
|
-
goog.require('shaka.polyfill.PatchedMediaKeysMs');
|
|
44
41
|
goog.require('shaka.polyfill.PatchedMediaKeysNop');
|
|
45
42
|
goog.require('shaka.polyfill.PatchedMediaKeysWebkit');
|
|
46
43
|
goog.require('shaka.polyfill.PiPWebkit');
|
|
@@ -805,6 +805,50 @@ describe('DashParser ContentProtection', () => {
|
|
|
805
805
|
});
|
|
806
806
|
});
|
|
807
807
|
|
|
808
|
+
describe('getClearKeyLicenseUrl', () => {
|
|
809
|
+
it('valid clearkey:Laurl node', () => {
|
|
810
|
+
const input = {
|
|
811
|
+
init: null,
|
|
812
|
+
keyId: null,
|
|
813
|
+
schemeUri: '',
|
|
814
|
+
node: strToXml([
|
|
815
|
+
'<test xmlns:clearkey="http://dashif.org/guidelines/clearKey">',
|
|
816
|
+
' <clearkey:Laurl ',
|
|
817
|
+
' Lic_type="EME-1.0">www.example.com</clearkey:Laurl>',
|
|
818
|
+
'</test>',
|
|
819
|
+
].join('\n')),
|
|
820
|
+
};
|
|
821
|
+
const actual = ContentProtection.getClearKeyLicenseUrl(input);
|
|
822
|
+
expect(actual).toBe('www.example.com');
|
|
823
|
+
});
|
|
824
|
+
|
|
825
|
+
it('clearkey:Laurl without license url', () => {
|
|
826
|
+
const input = {
|
|
827
|
+
init: null,
|
|
828
|
+
keyId: null,
|
|
829
|
+
schemeUri: '',
|
|
830
|
+
node: strToXml([
|
|
831
|
+
'<test xmlns:clearkey="http://dashif.org/guidelines/clearKey">',
|
|
832
|
+
' <clearkey:Laurl Lic_type="EME-1.0"></clearkey:Laurl>',
|
|
833
|
+
'</test>',
|
|
834
|
+
].join('\n')),
|
|
835
|
+
};
|
|
836
|
+
const actual = ContentProtection.getClearKeyLicenseUrl(input);
|
|
837
|
+
expect(actual).toBe('');
|
|
838
|
+
});
|
|
839
|
+
|
|
840
|
+
it('no clearkey:Laurl node', () => {
|
|
841
|
+
const input = {
|
|
842
|
+
init: null,
|
|
843
|
+
keyId: null,
|
|
844
|
+
schemeUri: '',
|
|
845
|
+
node: strToXml('<test></test>'),
|
|
846
|
+
};
|
|
847
|
+
const actual = ContentProtection.getClearKeyLicenseUrl(input);
|
|
848
|
+
expect(actual).toBe('');
|
|
849
|
+
});
|
|
850
|
+
});
|
|
851
|
+
|
|
808
852
|
describe('getPlayReadyLicenseURL', () => {
|
|
809
853
|
it('mspro', () => {
|
|
810
854
|
const laurl = [
|
|
@@ -447,30 +447,6 @@ describe('DashParser Manifest', () => {
|
|
|
447
447
|
expect(stream.closedCaptions).toEqual(expectedClosedCaptions);
|
|
448
448
|
});
|
|
449
449
|
|
|
450
|
-
it('Detects E-AC3 JOC content by SupplementalProperty', async () => {
|
|
451
|
-
const idUri = 'tag:dolby.com,2018:dash:EC3_ExtensionType:2018';
|
|
452
|
-
const source = [
|
|
453
|
-
'<MPD>',
|
|
454
|
-
' <Period duration="PT30M">',
|
|
455
|
-
' <AdaptationSet mimeType="audio/mp4" lang="\u2603">',
|
|
456
|
-
' <Representation bandwidth="500">',
|
|
457
|
-
' <SupplementalProperty schemeIdUri="' + idUri + '" value="JOC"/>',
|
|
458
|
-
' <BaseURL>http://example.com</BaseURL>',
|
|
459
|
-
' <SegmentTemplate media="2.mp4" duration="1" />',
|
|
460
|
-
' </Representation>',
|
|
461
|
-
' </AdaptationSet>',
|
|
462
|
-
' </Period>',
|
|
463
|
-
'</MPD>',
|
|
464
|
-
].join('\n');
|
|
465
|
-
|
|
466
|
-
fakeNetEngine.setResponseText('dummy://foo', source);
|
|
467
|
-
|
|
468
|
-
/** @type {shaka.extern.Manifest} */
|
|
469
|
-
const manifest = await parser.start('dummy://foo', playerInterface);
|
|
470
|
-
const stream = manifest.variants[0].audio;
|
|
471
|
-
expect(stream.mimeType).toBe('audio/eac3-joc');
|
|
472
|
-
});
|
|
473
|
-
|
|
474
450
|
it('Detects spatial audio', async () => {
|
|
475
451
|
const idUri = 'tag:dolby.com,2018:dash:EC3_ExtensionType:2018';
|
|
476
452
|
const source = [
|
|
@@ -106,9 +106,8 @@ describe('DashParser SegmentTemplate', () => {
|
|
|
106
106
|
's2.mp4', 50, 60, baseUri);
|
|
107
107
|
expectedRef2.timestampOffset = -10;
|
|
108
108
|
|
|
109
|
-
const
|
|
110
|
-
const
|
|
111
|
-
const ref2 = iterator.seek(55);
|
|
109
|
+
const ref1 = stream.segmentIndex.getIteratorForTime(45).next().value;
|
|
110
|
+
const ref2 = stream.segmentIndex.getIteratorForTime(55).next().value;
|
|
112
111
|
expect(ref1).toEqual(expectedRef1);
|
|
113
112
|
expect(ref2).toEqual(expectedRef2);
|
|
114
113
|
});
|
|
@@ -445,7 +444,7 @@ describe('DashParser SegmentTemplate', () => {
|
|
|
445
444
|
await variants[2].video.createSegmentIndex();
|
|
446
445
|
|
|
447
446
|
const getRefAt = (stream, time) => {
|
|
448
|
-
return stream.segmentIndex
|
|
447
|
+
return stream.segmentIndex.getIteratorForTime(time).next().value;
|
|
449
448
|
};
|
|
450
449
|
|
|
451
450
|
expect(getRefAt(variants[0].video, 0)).toEqual(
|
package/test/demo/demo_unit.js
CHANGED
|
@@ -93,7 +93,8 @@ describe('Demo', () => {
|
|
|
93
93
|
.add('preferredVariantRole')
|
|
94
94
|
.add('playRangeStart')
|
|
95
95
|
.add('playRangeEnd')
|
|
96
|
-
.add('manifest.dash.keySystemsByURI')
|
|
96
|
+
.add('manifest.dash.keySystemsByURI')
|
|
97
|
+
.add('manifest.hls.mediaPlaylistFullMimeType');
|
|
97
98
|
|
|
98
99
|
/**
|
|
99
100
|
* @param {!Object} section
|
|
@@ -28,14 +28,6 @@ describe('HlsParser live', () => {
|
|
|
28
28
|
let segmentData;
|
|
29
29
|
/** @type {!Uint8Array} */
|
|
30
30
|
let selfInitializingSegmentData;
|
|
31
|
-
/** @type {!Uint8Array} */
|
|
32
|
-
let tsSegmentData;
|
|
33
|
-
/** @type {!Uint8Array} */
|
|
34
|
-
let pastRolloverSegmentData;
|
|
35
|
-
/** @type {number} */
|
|
36
|
-
let rolloverOffset;
|
|
37
|
-
/** @type {number} */
|
|
38
|
-
let segmentDataStartTime;
|
|
39
31
|
|
|
40
32
|
beforeEach(() => {
|
|
41
33
|
// TODO: use StreamGenerator?
|
|
@@ -66,36 +58,6 @@ describe('HlsParser live', () => {
|
|
|
66
58
|
0x00, 0x00, 0x00, 0x00, // baseMediaDecodeTime first 4 bytes
|
|
67
59
|
0x00, 0x00, 0x07, 0xd0, // baseMediaDecodeTime last 4 bytes (2000)
|
|
68
60
|
]);
|
|
69
|
-
tsSegmentData = new Uint8Array([
|
|
70
|
-
0x47, // TS sync byte (fixed value)
|
|
71
|
-
0x41, 0x01, // not corrupt, payload follows, packet ID 257
|
|
72
|
-
0x10, // not scrambled, no adaptation field, payload only, seq #0
|
|
73
|
-
0x00, 0x00, 0x01, // PES start code (fixed value)
|
|
74
|
-
0xe0, // stream ID (video stream 0)
|
|
75
|
-
0x00, 0x00, // PES packet length (doesn't matter)
|
|
76
|
-
0x80, // marker bits (fixed value), not scrambled, not priority
|
|
77
|
-
0x80, // PTS only, no DTS, other flags 0 (don't matter)
|
|
78
|
-
0x05, // remaining PES header length == 5 (one timestamp)
|
|
79
|
-
0x21, 0x00, 0x0b, 0x7e, 0x41, // PTS = 180000, encoded into 5 bytes
|
|
80
|
-
]);
|
|
81
|
-
// 180000 divided by TS timescale (90000) = segment starts at 2s.
|
|
82
|
-
segmentDataStartTime = 2;
|
|
83
|
-
|
|
84
|
-
pastRolloverSegmentData = new Uint8Array([
|
|
85
|
-
0x00, 0x00, 0x00, 0x24, // size (36)
|
|
86
|
-
0x6D, 0x6F, 0x6F, 0x66, // type (moof)
|
|
87
|
-
0x00, 0x00, 0x00, 0x1C, // traf size (28)
|
|
88
|
-
0x74, 0x72, 0x61, 0x66, // type (traf)
|
|
89
|
-
0x00, 0x00, 0x00, 0x14, // tfdt size (20)
|
|
90
|
-
0x74, 0x66, 0x64, 0x74, // type (tfdt)
|
|
91
|
-
0x01, 0x00, 0x00, 0x00, // version and flags
|
|
92
|
-
0x00, 0x00, 0x00, 0x00, // baseMediaDecodeTime first 4 bytes
|
|
93
|
-
0x0b, 0x60, 0xbc, 0x28, // baseMediaDecodeTime last 4 bytes (190889000)
|
|
94
|
-
]);
|
|
95
|
-
|
|
96
|
-
// The timestamp above would roll over twice, so this rollover offset should
|
|
97
|
-
// be applied.
|
|
98
|
-
rolloverOffset = (0x200000000 * 2) / 90000;
|
|
99
61
|
|
|
100
62
|
selfInitializingSegmentData =
|
|
101
63
|
shaka.util.Uint8ArrayUtils.concat(initSegmentData, segmentData);
|
|
@@ -221,8 +183,10 @@ describe('HlsParser live', () => {
|
|
|
221
183
|
|
|
222
184
|
describe('update', () => {
|
|
223
185
|
it('adds new segments when they appear', async () => {
|
|
224
|
-
const ref1 =
|
|
225
|
-
|
|
186
|
+
const ref1 = makeReference(
|
|
187
|
+
'test:/main.mp4', 0, 2, /* syncTime= */ null);
|
|
188
|
+
const ref2 = makeReference(
|
|
189
|
+
'test:/main2.mp4', 2, 4, /* syncTime= */ null);
|
|
226
190
|
|
|
227
191
|
await testUpdate(
|
|
228
192
|
master, media, [ref1], mediaWithAdditionalSegment, [ref1, ref2]);
|
|
@@ -236,8 +200,10 @@ describe('HlsParser live', () => {
|
|
|
236
200
|
].join('');
|
|
237
201
|
|
|
238
202
|
const masterWithTwoVariants = master + secondVariant;
|
|
239
|
-
const ref1 =
|
|
240
|
-
|
|
203
|
+
const ref1 = makeReference(
|
|
204
|
+
'test:/main.mp4', 0, 2, /* syncTime= */ null);
|
|
205
|
+
const ref2 = makeReference(
|
|
206
|
+
'test:/main2.mp4', 2, 4, /* syncTime= */ null);
|
|
241
207
|
|
|
242
208
|
await testUpdate(
|
|
243
209
|
masterWithTwoVariants, media, [ref1], mediaWithAdditionalSegment,
|
|
@@ -257,8 +223,10 @@ describe('HlsParser live', () => {
|
|
|
257
223
|
].join('');
|
|
258
224
|
|
|
259
225
|
const masterWithAudio = masterlist + audio;
|
|
260
|
-
const ref1 =
|
|
261
|
-
|
|
226
|
+
const ref1 = makeReference(
|
|
227
|
+
'test:/main.mp4', 0, 2, /* syncTime= */ null);
|
|
228
|
+
const ref2 = makeReference(
|
|
229
|
+
'test:/main2.mp4', 2, 4, /* syncTime= */ null);
|
|
262
230
|
|
|
263
231
|
await testUpdate(
|
|
264
232
|
masterWithAudio, media, [ref1], mediaWithAdditionalSegment,
|
|
@@ -278,9 +246,12 @@ describe('HlsParser live', () => {
|
|
|
278
246
|
|
|
279
247
|
const updatedMedia1 = media + newSegment1;
|
|
280
248
|
const updatedMedia2 = updatedMedia1 + newSegment2;
|
|
281
|
-
const ref1 =
|
|
282
|
-
|
|
283
|
-
const
|
|
249
|
+
const ref1 = makeReference(
|
|
250
|
+
'test:/main.mp4', 0, 2, /* syncTime= */ null);
|
|
251
|
+
const ref2 = makeReference(
|
|
252
|
+
'test:/main2.mp4', 2, 4, /* syncTime= */ null);
|
|
253
|
+
const ref3 = makeReference(
|
|
254
|
+
'test:/main3.mp4', 4, 6, /* syncTime= */ null);
|
|
284
255
|
|
|
285
256
|
fakeNetEngine
|
|
286
257
|
.setResponseText('test:/master', master)
|
|
@@ -386,19 +357,6 @@ describe('HlsParser live', () => {
|
|
|
386
357
|
'main.mp4\n',
|
|
387
358
|
].join('');
|
|
388
359
|
|
|
389
|
-
const mediaWithByteRange = [
|
|
390
|
-
'#EXTM3U\n',
|
|
391
|
-
'#EXT-X-TARGETDURATION:5\n',
|
|
392
|
-
'#EXT-X-MAP:URI="init.mp4",BYTERANGE="616@0"\n',
|
|
393
|
-
'#EXT-X-MEDIA-SEQUENCE:0\n',
|
|
394
|
-
'#EXT-X-BYTERANGE:121090@616\n',
|
|
395
|
-
'#EXTINF:2,\n',
|
|
396
|
-
'main.mp4\n',
|
|
397
|
-
].join('');
|
|
398
|
-
|
|
399
|
-
const expectedStartByte = 616;
|
|
400
|
-
const expectedEndByte = 121705;
|
|
401
|
-
|
|
402
360
|
const mediaWithAdditionalSegment = [
|
|
403
361
|
'#EXTM3U\n',
|
|
404
362
|
'#EXT-X-TARGETDURATION:5\n',
|
|
@@ -563,17 +521,17 @@ describe('HlsParser live', () => {
|
|
|
563
521
|
.setResponseValue('test:/main.mp4', segmentData)
|
|
564
522
|
.setResponseValue('test:/main2.mp4', segmentData);
|
|
565
523
|
|
|
566
|
-
const ref1 =
|
|
567
|
-
'test:/main.mp4',
|
|
524
|
+
const ref1 = makeReference(
|
|
525
|
+
'test:/main.mp4', 0, 2, /* syncTime= */ null,
|
|
568
526
|
/* baseUri= */ '', /* startByte= */ 0, /* endByte= */ null,
|
|
569
527
|
/* timestampOffset= */ 0);
|
|
570
528
|
|
|
571
529
|
// Expect the timestamp offset to be set for the segment after the
|
|
572
530
|
// EXT-X-DISCONTINUITY tag.
|
|
573
|
-
const ref2 =
|
|
574
|
-
'test:/main2.mp4',
|
|
531
|
+
const ref2 = makeReference(
|
|
532
|
+
'test:/main2.mp4', 2, 4, /* syncTime= */ null,
|
|
575
533
|
/* baseUri= */ '', /* startByte= */ 0, /* endByte= */ null,
|
|
576
|
-
/* timestampOffset= */
|
|
534
|
+
/* timestampOffset= */ 0);
|
|
577
535
|
|
|
578
536
|
const manifest = await parser.start('test:/master', playerInterface);
|
|
579
537
|
const video = manifest.variants[0].video;
|
|
@@ -581,57 +539,6 @@ describe('HlsParser live', () => {
|
|
|
581
539
|
ManifestParser.verifySegmentIndex(video, [ref1, ref2]);
|
|
582
540
|
});
|
|
583
541
|
|
|
584
|
-
it('offsets VTT text with rolled over TS timestamps', async () => {
|
|
585
|
-
const masterWithVtt = [
|
|
586
|
-
'#EXTM3U\n',
|
|
587
|
-
'#EXT-X-MEDIA:TYPE=SUBTITLES,LANGUAGE="fra",URI="text",',
|
|
588
|
-
'GROUP-ID="sub1"\n',
|
|
589
|
-
'#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="avc1",',
|
|
590
|
-
'RESOLUTION=960x540,FRAME-RATE=60,SUBTITLES="sub1"\n',
|
|
591
|
-
'video\n',
|
|
592
|
-
].join('');
|
|
593
|
-
|
|
594
|
-
const textPlaylist = [
|
|
595
|
-
'#EXTM3U\n',
|
|
596
|
-
'#EXT-X-TARGETDURATION:5\n',
|
|
597
|
-
'#EXT-X-MEDIA-SEQUENCE:0\n',
|
|
598
|
-
'#EXTINF:2,\n',
|
|
599
|
-
'main.vtt\n',
|
|
600
|
-
].join('');
|
|
601
|
-
|
|
602
|
-
const vtt = [
|
|
603
|
-
'WEBVTT\n',
|
|
604
|
-
'\n',
|
|
605
|
-
'00:00.000 --> 00:01.000\n',
|
|
606
|
-
'Hello, world!\n',
|
|
607
|
-
].join('');
|
|
608
|
-
|
|
609
|
-
fakeNetEngine
|
|
610
|
-
.setResponseText('test:/master', masterWithVtt)
|
|
611
|
-
.setResponseText('test:/video', media)
|
|
612
|
-
.setResponseText('test:/text', textPlaylist)
|
|
613
|
-
.setResponseValue('test:/init.mp4', initSegmentData)
|
|
614
|
-
.setResponseValue('test:/main.mp4', pastRolloverSegmentData)
|
|
615
|
-
.setResponseText('test:/main.vtt', vtt);
|
|
616
|
-
|
|
617
|
-
const manifest = await parser.start('test:/master', playerInterface);
|
|
618
|
-
const textStream = manifest.textStreams[0];
|
|
619
|
-
await textStream.createSegmentIndex();
|
|
620
|
-
goog.asserts.assert(textStream.segmentIndex, 'Null segmentIndex!');
|
|
621
|
-
|
|
622
|
-
let ref = Array.from(textStream.segmentIndex)[0];
|
|
623
|
-
expect(ref).not.toBe(null);
|
|
624
|
-
expect(ref.startTime).not.toBeLessThan(rolloverOffset);
|
|
625
|
-
|
|
626
|
-
const videoStream = manifest.variants[0].video;
|
|
627
|
-
await videoStream.createSegmentIndex();
|
|
628
|
-
goog.asserts.assert(videoStream.segmentIndex, 'Null segmentIndex!');
|
|
629
|
-
|
|
630
|
-
ref = Array.from(videoStream.segmentIndex)[0];
|
|
631
|
-
expect(ref).not.toBe(null);
|
|
632
|
-
expect(ref.startTime).not.toBeLessThan(rolloverOffset);
|
|
633
|
-
});
|
|
634
|
-
|
|
635
542
|
it('parses streams with partial and preload hinted segments', async () => {
|
|
636
543
|
playerInterface.isLowLatencyMode = () => true;
|
|
637
544
|
const mediaWithPartialSegments = [
|
|
@@ -661,35 +568,32 @@ describe('HlsParser live', () => {
|
|
|
661
568
|
.setResponseValue('test:/partial.mp4', segmentData)
|
|
662
569
|
.setResponseValue('test:/partial2.mp4', segmentData);
|
|
663
570
|
|
|
664
|
-
const partialRef =
|
|
665
|
-
'test:/partial.mp4',
|
|
571
|
+
const partialRef = makeReference(
|
|
572
|
+
'test:/partial.mp4', 0, 2, /* syncTime= */ null,
|
|
666
573
|
/* baseUri= */ '', /* startByte= */ 0, /* endByte= */ 199);
|
|
667
574
|
|
|
668
|
-
const partialRef2 =
|
|
669
|
-
'test:/partial2.mp4',
|
|
670
|
-
|
|
671
|
-
/* endByte= */ 429);
|
|
575
|
+
const partialRef2 = makeReference(
|
|
576
|
+
'test:/partial2.mp4', 2, 4, /* syncTime= */ null,
|
|
577
|
+
/* baseUri= */ '', /* startByte= */ 200, /* endByte= */ 429);
|
|
672
578
|
|
|
673
|
-
const partialRef3 =
|
|
674
|
-
'test:/partial.mp4',
|
|
675
|
-
segmentDataStartTime + 6,
|
|
579
|
+
const partialRef3 = makeReference(
|
|
580
|
+
'test:/partial.mp4', 4, 6, /* syncTime= */ null,
|
|
676
581
|
/* baseUri= */ '', /* startByte= */ 0, /* endByte= */ 209);
|
|
677
582
|
|
|
678
583
|
// A preload hinted partial segment doesn't have duration information,
|
|
679
584
|
// so its startTime and endTime are the same.
|
|
680
|
-
const preloadRef =
|
|
681
|
-
'test:/partial.mp4',
|
|
682
|
-
segmentDataStartTime + 6,
|
|
585
|
+
const preloadRef = makeReference(
|
|
586
|
+
'test:/partial.mp4', 6, 6, /* syncTime= */ null,
|
|
683
587
|
/* baseUri= */ '', /* startByte= */ 210, /* endByte= */ null);
|
|
684
588
|
|
|
685
|
-
const ref =
|
|
686
|
-
'test:/main.mp4',
|
|
589
|
+
const ref = makeReference(
|
|
590
|
+
'test:/main.mp4', 0, 4, /* syncTime= */ null,
|
|
687
591
|
/* baseUri= */ '', /* startByte= */ 0, /* endByte= */ 429,
|
|
688
592
|
/* timestampOffset= */ 0, [partialRef, partialRef2]);
|
|
689
593
|
|
|
690
594
|
// ref2 is not fully published yet, so it doesn't have a segment uri.
|
|
691
|
-
const ref2 =
|
|
692
|
-
'',
|
|
595
|
+
const ref2 = makeReference(
|
|
596
|
+
'', 4, 6, /* syncTime= */ null,
|
|
693
597
|
/* baseUri= */ '', /* startByte= */ 0, /* endByte= */ null,
|
|
694
598
|
/* timestampOffset= */ 0, [partialRef3, preloadRef]);
|
|
695
599
|
|
|
@@ -699,18 +603,44 @@ describe('HlsParser live', () => {
|
|
|
699
603
|
ManifestParser.verifySegmentIndex(video, [ref, ref2]);
|
|
700
604
|
});
|
|
701
605
|
|
|
606
|
+
// Test for https://github.com/shaka-project/shaka-player/issues/4185
|
|
607
|
+
it('does not fail on preload hints with LL mode off', async () => {
|
|
608
|
+
// LL mode must be off for this test!
|
|
609
|
+
playerInterface.isLowLatencyMode = () => false;
|
|
610
|
+
|
|
611
|
+
const mediaWithPartialSegments = [
|
|
612
|
+
'#EXTM3U\n',
|
|
613
|
+
'#EXT-X-TARGETDURATION:5\n',
|
|
614
|
+
'#EXTINF:4,\n',
|
|
615
|
+
'main.mp4\n',
|
|
616
|
+
'#EXT-X-PART:DURATION=2,URI="partial.mp4",BYTERANGE=210@0\n',
|
|
617
|
+
'#EXT-X-PRELOAD-HINT:TYPE=PART,URI="partial.mp4",BYTERANGE-START=210\n',
|
|
618
|
+
].join('');
|
|
619
|
+
|
|
620
|
+
fakeNetEngine
|
|
621
|
+
.setResponseText('test:/master', master)
|
|
622
|
+
.setResponseText('test:/video', mediaWithPartialSegments);
|
|
623
|
+
|
|
624
|
+
// If this throws, the test fails. Otherwise, it passes.
|
|
625
|
+
await parser.start('test:/master', playerInterface);
|
|
626
|
+
});
|
|
627
|
+
|
|
702
628
|
describe('update', () => {
|
|
703
629
|
it('adds new segments when they appear', async () => {
|
|
704
|
-
const ref1 =
|
|
705
|
-
|
|
630
|
+
const ref1 = makeReference(
|
|
631
|
+
'test:/main.mp4', 0, 2, /* syncTime= */ null);
|
|
632
|
+
const ref2 = makeReference(
|
|
633
|
+
'test:/main2.mp4', 2, 4, /* syncTime= */ null);
|
|
706
634
|
|
|
707
635
|
await testUpdate(
|
|
708
636
|
master, media, [ref1], mediaWithAdditionalSegment, [ref1, ref2]);
|
|
709
637
|
});
|
|
710
638
|
|
|
711
639
|
it('evicts removed segments', async () => {
|
|
712
|
-
const ref1 =
|
|
713
|
-
|
|
640
|
+
const ref1 = makeReference(
|
|
641
|
+
'test:/main.mp4', 0, 2, /* syncTime= */ null);
|
|
642
|
+
const ref2 = makeReference(
|
|
643
|
+
'test:/main2.mp4', 2, 4, /* syncTime= */ null);
|
|
714
644
|
|
|
715
645
|
await testUpdate(
|
|
716
646
|
master, mediaWithAdditionalSegment, [ref1, ref2],
|
|
@@ -718,12 +648,13 @@ describe('HlsParser live', () => {
|
|
|
718
648
|
});
|
|
719
649
|
|
|
720
650
|
it('handles updates with redirects', async () => {
|
|
721
|
-
const oldRef1 =
|
|
651
|
+
const oldRef1 = makeReference(
|
|
652
|
+
'test:/main.mp4', 0, 2, /* syncTime= */ null);
|
|
722
653
|
|
|
723
|
-
const newRef1 =
|
|
724
|
-
|
|
725
|
-
const newRef2 =
|
|
726
|
-
|
|
654
|
+
const newRef1 = makeReference(
|
|
655
|
+
'test:/redirected/main.mp4', 0, 2, /* syncTime= */ null);
|
|
656
|
+
const newRef2 = makeReference(
|
|
657
|
+
'test:/redirected/main2.mp4', 2, 4, /* syncTime= */ null);
|
|
727
658
|
|
|
728
659
|
let playlistFetchCount = 0;
|
|
729
660
|
|
|
@@ -750,8 +681,8 @@ describe('HlsParser live', () => {
|
|
|
750
681
|
.setResponseValue('test:/init.mp4', initSegmentData)
|
|
751
682
|
.setResponseValue('test:/main.mp4', segmentData);
|
|
752
683
|
|
|
753
|
-
const expectedRef =
|
|
754
|
-
'test:/main.mp4',
|
|
684
|
+
const expectedRef = makeReference(
|
|
685
|
+
'test:/main.mp4', 0, 2, /* syncTime= */ null);
|
|
755
686
|
// In live content, we do not set timestampOffset.
|
|
756
687
|
expectedRef.timestampOffset = 0;
|
|
757
688
|
|
|
@@ -768,12 +699,11 @@ describe('HlsParser live', () => {
|
|
|
768
699
|
.setResponseValue('test:/init.mp4', initSegmentData)
|
|
769
700
|
.setResponseValue('test:/main.mp4', segmentData);
|
|
770
701
|
|
|
771
|
-
const ref1 =
|
|
772
|
-
'test:/main.mp4',
|
|
702
|
+
const ref1 = makeReference(
|
|
703
|
+
'test:/main.mp4', 0, 2, /* syncTime= */ null);
|
|
773
704
|
|
|
774
|
-
const ref2 =
|
|
775
|
-
'test:/main2.mp4',
|
|
776
|
-
segmentDataStartTime + 4);
|
|
705
|
+
const ref2 = makeReference(
|
|
706
|
+
'test:/main2.mp4', 2, 4, /* syncTime= */ null);
|
|
777
707
|
|
|
778
708
|
const manifest = await parser.start('test:/master', playerInterface);
|
|
779
709
|
const video = manifest.variants[0].video;
|
|
@@ -809,11 +739,11 @@ describe('HlsParser live', () => {
|
|
|
809
739
|
.setResponseValue('test:/main.mp4', segmentData)
|
|
810
740
|
.setResponseValue('test:/main2.mp4', segmentData);
|
|
811
741
|
|
|
812
|
-
const ref1 =
|
|
813
|
-
|
|
742
|
+
const ref1 = makeReference(
|
|
743
|
+
'test:/main.mp4', 0, 2, /* syncTime= */ null);
|
|
814
744
|
|
|
815
|
-
const ref2 =
|
|
816
|
-
|
|
745
|
+
const ref2 = makeReference(
|
|
746
|
+
'test:/main2.mp4', 2, 4, /* syncTime= */ null);
|
|
817
747
|
|
|
818
748
|
const manifest =
|
|
819
749
|
await parser.start('test:/master', playerInterface);
|
|
@@ -846,57 +776,6 @@ describe('HlsParser live', () => {
|
|
|
846
776
|
shaka.net.NetworkingEngine.RequestType.SEGMENT);
|
|
847
777
|
});
|
|
848
778
|
|
|
849
|
-
it('parses start time from ts segments', async () => {
|
|
850
|
-
const tsMediaPlaylist =
|
|
851
|
-
mediaWithRemovedSegment.replace(/\.mp4/g, '.ts');
|
|
852
|
-
|
|
853
|
-
fakeNetEngine
|
|
854
|
-
.setResponseText('test:/master', master)
|
|
855
|
-
.setResponseText('test:/video', tsMediaPlaylist)
|
|
856
|
-
.setResponseValue('test:/main2.ts', tsSegmentData);
|
|
857
|
-
|
|
858
|
-
const expectedRef = ManifestParser.makeReference(
|
|
859
|
-
'test:/main2.ts', segmentDataStartTime, segmentDataStartTime + 2);
|
|
860
|
-
// In live content, we do not set timestampOffset.
|
|
861
|
-
expectedRef.timestampOffset = 0;
|
|
862
|
-
|
|
863
|
-
const manifest = await parser.start('test:/master', playerInterface);
|
|
864
|
-
const video = manifest.variants[0].video;
|
|
865
|
-
await video.createSegmentIndex();
|
|
866
|
-
ManifestParser.verifySegmentIndex(video, [expectedRef]);
|
|
867
|
-
});
|
|
868
|
-
|
|
869
|
-
it('gets start time of segments with byte range', async () => {
|
|
870
|
-
// Nit: this value is an implementation detail of the fix for #1106
|
|
871
|
-
const partialEndByte = expectedStartByte + 2048 - 1;
|
|
872
|
-
|
|
873
|
-
fakeNetEngine
|
|
874
|
-
.setResponseText('test:/master', master)
|
|
875
|
-
.setResponseText('test:/video', mediaWithByteRange)
|
|
876
|
-
.setResponseValue('test:/init.mp4', initSegmentData)
|
|
877
|
-
.setResponseValue('test:/main.mp4', segmentData);
|
|
878
|
-
|
|
879
|
-
const expectedRef = ManifestParser.makeReference(
|
|
880
|
-
/* uri= */ 'test:/main.mp4',
|
|
881
|
-
/* start= */ segmentDataStartTime,
|
|
882
|
-
/* end= */ segmentDataStartTime + 2,
|
|
883
|
-
/* baseUri= */ '',
|
|
884
|
-
expectedStartByte,
|
|
885
|
-
expectedEndByte); // Complete segment reference
|
|
886
|
-
|
|
887
|
-
const manifest = await parser.start('test:/master', playerInterface);
|
|
888
|
-
const video = manifest.variants[0].video;
|
|
889
|
-
await video.createSegmentIndex();
|
|
890
|
-
ManifestParser.verifySegmentIndex(video, [expectedRef]);
|
|
891
|
-
|
|
892
|
-
// There should have been a range request for this segment to get the
|
|
893
|
-
// start time.
|
|
894
|
-
fakeNetEngine.expectRangeRequest(
|
|
895
|
-
'test:/main.mp4',
|
|
896
|
-
expectedStartByte,
|
|
897
|
-
partialEndByte); // partial segment request
|
|
898
|
-
});
|
|
899
|
-
|
|
900
779
|
it('request playlist delta updates to skip segments', async () => {
|
|
901
780
|
const mediaWithDeltaUpdates = [
|
|
902
781
|
'#EXTM3U\n',
|
|
@@ -961,9 +840,12 @@ describe('HlsParser live', () => {
|
|
|
961
840
|
].join('');
|
|
962
841
|
|
|
963
842
|
playerInterface.isLowLatencyMode = () => true;
|
|
964
|
-
const ref1 =
|
|
965
|
-
|
|
966
|
-
const
|
|
843
|
+
const ref1 = makeReference(
|
|
844
|
+
'test:/main.mp4', 0, 2, /* syncTime= */ null);
|
|
845
|
+
const ref2 = makeReference(
|
|
846
|
+
'test:/main2.mp4', 2, 4, /* syncTime= */ null);
|
|
847
|
+
const ref3 = makeReference(
|
|
848
|
+
'test:/main3.mp4', 4, 6, /* syncTime= */ null);
|
|
967
849
|
// With 'SKIPPED-SEGMENTS', ref1 is skipped from the playlist,
|
|
968
850
|
// and ref1 should be in the SegmentReferences list.
|
|
969
851
|
// ref3 should be appended to the SegmentReferences list.
|
|
@@ -1003,29 +885,29 @@ describe('HlsParser live', () => {
|
|
|
1003
885
|
|
|
1004
886
|
playerInterface.isLowLatencyMode = () => true;
|
|
1005
887
|
|
|
1006
|
-
const ref1 =
|
|
1007
|
-
'test:/main.mp4',
|
|
888
|
+
const ref1 = makeReference(
|
|
889
|
+
'test:/main.mp4', 0, 2, /* syncTime= */ null,
|
|
1008
890
|
/* baseUri= */ '', /* startByte= */ 0, /* endByte= */ null,
|
|
1009
891
|
/* timestampOffset= */ 0);
|
|
1010
892
|
|
|
1011
893
|
// Expect the timestamp offset to be set for the segment after the
|
|
1012
894
|
// EXT-X-DISCONTINUITY tag.
|
|
1013
|
-
const ref2 =
|
|
1014
|
-
'test:/main2.mp4',
|
|
1015
|
-
|
|
1016
|
-
/*
|
|
895
|
+
const ref2 = makeReference(
|
|
896
|
+
'test:/main2.mp4', 2, 4, /* syncTime= */ null,
|
|
897
|
+
/* baseUri= */ '', /* startByte= */ 0, /* endByte= */ null,
|
|
898
|
+
/* timestampOffset= */ 0);
|
|
1017
899
|
|
|
1018
900
|
// Expect the timestamp offset to be set for the segment, with the
|
|
1019
901
|
// EXT-X-DISCONTINUITY tag skipped in the playlist.
|
|
1020
|
-
const ref3 =
|
|
1021
|
-
'test:/main3.mp4',
|
|
1022
|
-
|
|
1023
|
-
/*
|
|
902
|
+
const ref3 = makeReference(
|
|
903
|
+
'test:/main3.mp4', 4, 6, /* syncTime= */ null,
|
|
904
|
+
/* baseUri= */ '', /* startByte= */ 0, /* endByte= */ null,
|
|
905
|
+
/* timestampOffset= */ 0);
|
|
1024
906
|
|
|
1025
|
-
const ref4 =
|
|
1026
|
-
'test:/main4.mp4',
|
|
1027
|
-
|
|
1028
|
-
/*
|
|
907
|
+
const ref4 = makeReference(
|
|
908
|
+
'test:/main4.mp4', 6, 8, /* syncTime= */ null,
|
|
909
|
+
/* baseUri= */ '', /* startByte= */ 0, /* endByte= */ null,
|
|
910
|
+
/* timestampOffset= */ 0);
|
|
1029
911
|
|
|
1030
912
|
// With 'SKIPPED-SEGMENTS', ref1, ref2 are skipped from the playlist,
|
|
1031
913
|
// and ref1,ref2 should be in the SegmentReferences list.
|
|
@@ -1036,4 +918,23 @@ describe('HlsParser live', () => {
|
|
|
1036
918
|
});
|
|
1037
919
|
}); // describe('update')
|
|
1038
920
|
}); // describe('playlist type LIVE')
|
|
921
|
+
|
|
922
|
+
/**
|
|
923
|
+
* @param {string} uri A relative URI to http://example.com
|
|
924
|
+
* @param {number} start
|
|
925
|
+
* @param {number} end
|
|
926
|
+
* @param {?number} syncTime
|
|
927
|
+
* @param {string=} baseUri
|
|
928
|
+
* @param {number=} startByte
|
|
929
|
+
* @param {?number=} endByte
|
|
930
|
+
* @param {number=} timestampOffset
|
|
931
|
+
* @param {!Array.<!shaka.media.SegmentReference>=} partialReferences
|
|
932
|
+
* @param {?string=} tilesLayout
|
|
933
|
+
* @return {!shaka.media.SegmentReference}
|
|
934
|
+
*/
|
|
935
|
+
function makeReference(uri, start, end, syncTime, baseUri, startByte, endByte,
|
|
936
|
+
timestampOffset, partialReferences, tilesLayout) {
|
|
937
|
+
return ManifestParser.makeReference(uri, start, end, baseUri, startByte,
|
|
938
|
+
endByte, timestampOffset, partialReferences, tilesLayout, syncTime);
|
|
939
|
+
}
|
|
1039
940
|
}); // describe('HlsParser live')
|