@transmitlive/m3u8-parser 4.7.2-beta.6
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/CONTRIBUTING.md +30 -0
- package/LICENSE +13 -0
- package/README.md +388 -0
- package/dist/m3u8-parser.cjs.js +1563 -0
- package/dist/m3u8-parser.es.js +1549 -0
- package/dist/m3u8-parser.js +1748 -0
- package/dist/m3u8-parser.min.js +2 -0
- package/index.html +15 -0
- package/package.json +100 -0
- package/scripts/karma.conf.js +12 -0
- package/scripts/rollup.config.js +47 -0
- package/src/index.js +19 -0
- package/src/line-stream.js +35 -0
- package/src/parse-stream.js +619 -0
- package/src/parser.js +748 -0
- package/test/fixtures/integration/absoluteUris.js +31 -0
- package/test/fixtures/integration/absoluteUris.m3u8 +12 -0
- package/test/fixtures/integration/allowCache.js +165 -0
- package/test/fixtures/integration/allowCache.m3u8 +58 -0
- package/test/fixtures/integration/allowCacheInvalid.js +21 -0
- package/test/fixtures/integration/allowCacheInvalid.m3u8 +10 -0
- package/test/fixtures/integration/alternateAudio.js +56 -0
- package/test/fixtures/integration/alternateAudio.m3u8 +9 -0
- package/test/fixtures/integration/alternateVideo.js +48 -0
- package/test/fixtures/integration/alternateVideo.m3u8 +8 -0
- package/test/fixtures/integration/brightcove.js +57 -0
- package/test/fixtures/integration/brightcove.m3u8 +9 -0
- package/test/fixtures/integration/byteRange.js +161 -0
- package/test/fixtures/integration/byteRange.m3u8 +56 -0
- package/test/fixtures/integration/dateTime.js +27 -0
- package/test/fixtures/integration/dateTime.m3u8 +12 -0
- package/test/fixtures/integration/diff-init-key.js +164 -0
- package/test/fixtures/integration/diff-init-key.m3u8 +57 -0
- package/test/fixtures/integration/disallowCache.js +21 -0
- package/test/fixtures/integration/disallowCache.m3u8 +10 -0
- package/test/fixtures/integration/disc-sequence.js +32 -0
- package/test/fixtures/integration/disc-sequence.m3u8 +15 -0
- package/test/fixtures/integration/discontinuity.js +59 -0
- package/test/fixtures/integration/discontinuity.m3u8 +26 -0
- package/test/fixtures/integration/domainUris.js +31 -0
- package/test/fixtures/integration/domainUris.m3u8 +12 -0
- package/test/fixtures/integration/empty.js +5 -0
- package/test/fixtures/integration/empty.m3u8 +0 -0
- package/test/fixtures/integration/emptyAllowCache.js +21 -0
- package/test/fixtures/integration/emptyAllowCache.m3u8 +10 -0
- package/test/fixtures/integration/emptyMediaSequence.js +31 -0
- package/test/fixtures/integration/emptyMediaSequence.m3u8 +14 -0
- package/test/fixtures/integration/emptyPlaylistType.js +40 -0
- package/test/fixtures/integration/emptyPlaylistType.m3u8 +16 -0
- package/test/fixtures/integration/emptyTargetDuration.js +57 -0
- package/test/fixtures/integration/emptyTargetDuration.m3u8 +10 -0
- package/test/fixtures/integration/encrypted.js +61 -0
- package/test/fixtures/integration/encrypted.m3u8 +28 -0
- package/test/fixtures/integration/event.js +41 -0
- package/test/fixtures/integration/event.m3u8 +16 -0
- package/test/fixtures/integration/extXPlaylistTypeInvalidPlaylist.js +15 -0
- package/test/fixtures/integration/extXPlaylistTypeInvalidPlaylist.m3u8 +8 -0
- package/test/fixtures/integration/extinf.js +165 -0
- package/test/fixtures/integration/extinf.m3u8 +57 -0
- package/test/fixtures/integration/fmp4.js +44 -0
- package/test/fixtures/integration/fmp4.m3u8 +14 -0
- package/test/fixtures/integration/headerOnly.js +5 -0
- package/test/fixtures/integration/headerOnly.m3u8 +1 -0
- package/test/fixtures/integration/invalidAllowCache.js +21 -0
- package/test/fixtures/integration/invalidAllowCache.m3u8 +10 -0
- package/test/fixtures/integration/invalidMediaSequence.js +31 -0
- package/test/fixtures/integration/invalidMediaSequence.m3u8 +14 -0
- package/test/fixtures/integration/invalidPlaylistType.js +40 -0
- package/test/fixtures/integration/invalidPlaylistType.m3u8 +16 -0
- package/test/fixtures/integration/invalidTargetDuration.js +164 -0
- package/test/fixtures/integration/invalidTargetDuration.m3u8 +57 -0
- package/test/fixtures/integration/liveMissingSegmentDuration.js +25 -0
- package/test/fixtures/integration/liveMissingSegmentDuration.m3u8 +9 -0
- package/test/fixtures/integration/liveStart30sBefore.js +54 -0
- package/test/fixtures/integration/liveStart30sBefore.m3u8 +22 -0
- package/test/fixtures/integration/llhls-byte-range.js +253 -0
- package/test/fixtures/integration/llhls-byte-range.m3u8 +66 -0
- package/test/fixtures/integration/llhls-delta-byte-range.js +149 -0
- package/test/fixtures/integration/llhls-delta-byte-range.m3u8 +30 -0
- package/test/fixtures/integration/llhls.js +214 -0
- package/test/fixtures/integration/llhls.m3u8 +56 -0
- package/test/fixtures/integration/llhlsDelta.js +186 -0
- package/test/fixtures/integration/llhlsDelta.m3u8 +50 -0
- package/test/fixtures/integration/manifestExtTTargetdurationNegative.js +14 -0
- package/test/fixtures/integration/manifestExtTTargetdurationNegative.m3u8 +5 -0
- package/test/fixtures/integration/manifestExtXEndlistEarly.js +35 -0
- package/test/fixtures/integration/manifestExtXEndlistEarly.m3u8 +14 -0
- package/test/fixtures/integration/manifestNoExtM3u.js +15 -0
- package/test/fixtures/integration/manifestNoExtM3u.m3u8 +4 -0
- package/test/fixtures/integration/master-fmp4.js +465 -0
- package/test/fixtures/integration/master-fmp4.m3u8 +76 -0
- package/test/fixtures/integration/master.js +57 -0
- package/test/fixtures/integration/master.m3u8 +10 -0
- package/test/fixtures/integration/media.js +31 -0
- package/test/fixtures/integration/media.m3u8 +12 -0
- package/test/fixtures/integration/mediaSequence.js +31 -0
- package/test/fixtures/integration/mediaSequence.m3u8 +14 -0
- package/test/fixtures/integration/missingEndlist.js +19 -0
- package/test/fixtures/integration/missingEndlist.m3u8 +6 -0
- package/test/fixtures/integration/missingExtinf.js +27 -0
- package/test/fixtures/integration/missingExtinf.m3u8 +11 -0
- package/test/fixtures/integration/missingMediaSequence.js +31 -0
- package/test/fixtures/integration/missingMediaSequence.m3u8 +13 -0
- package/test/fixtures/integration/missingSegmentDuration.js +31 -0
- package/test/fixtures/integration/missingSegmentDuration.m3u8 +11 -0
- package/test/fixtures/integration/multipleAudioGroups.js +89 -0
- package/test/fixtures/integration/multipleAudioGroups.m3u8 +17 -0
- package/test/fixtures/integration/multipleAudioGroupsCombinedMain.js +88 -0
- package/test/fixtures/integration/multipleAudioGroupsCombinedMain.m3u8 +17 -0
- package/test/fixtures/integration/multipleTargetDurations.js +28 -0
- package/test/fixtures/integration/multipleTargetDurations.m3u8 +8 -0
- package/test/fixtures/integration/multipleVideo.js +74 -0
- package/test/fixtures/integration/multipleVideo.m3u8 +16 -0
- package/test/fixtures/integration/negativeMediaSequence.js +31 -0
- package/test/fixtures/integration/negativeMediaSequence.m3u8 +14 -0
- package/test/fixtures/integration/playlist.js +165 -0
- package/test/fixtures/integration/playlist.m3u8 +57 -0
- package/test/fixtures/integration/playlistMediaSequenceHigher.js +16 -0
- package/test/fixtures/integration/playlistMediaSequenceHigher.m3u8 +8 -0
- package/test/fixtures/integration/start.js +36 -0
- package/test/fixtures/integration/start.m3u8 +13 -0
- package/test/fixtures/integration/streamInfInvalid.js +24 -0
- package/test/fixtures/integration/streamInfInvalid.m3u8 +6 -0
- package/test/fixtures/integration/twoMediaSequences.js +31 -0
- package/test/fixtures/integration/twoMediaSequences.m3u8 +15 -0
- package/test/fixtures/integration/versionInvalid.js +16 -0
- package/test/fixtures/integration/versionInvalid.m3u8 +8 -0
- package/test/fixtures/integration/whiteSpace.js +31 -0
- package/test/fixtures/integration/whiteSpace.m3u8 +13 -0
- package/test/fixtures/integration/zeroDuration.js +16 -0
- package/test/fixtures/integration/zeroDuration.m3u8 +7 -0
- package/test/line-stream.test.js +80 -0
- package/test/parse-stream.test.js +903 -0
- package/test/parser.test.js +884 -0
|
@@ -0,0 +1,884 @@
|
|
|
1
|
+
import QUnit from 'qunit';
|
|
2
|
+
import testDataExpected from 'data-files!expecteds';
|
|
3
|
+
import testDataManifests from 'data-files!manifests';
|
|
4
|
+
import {Parser} from '../src';
|
|
5
|
+
|
|
6
|
+
QUnit.module('m3u8s', function(hooks) {
|
|
7
|
+
hooks.beforeEach(function() {
|
|
8
|
+
this.parser = new Parser();
|
|
9
|
+
QUnit.dump.maxDepth = 8;
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
QUnit.module('general');
|
|
13
|
+
|
|
14
|
+
QUnit.test('can be constructed', function(assert) {
|
|
15
|
+
assert.notStrictEqual(this.parser, 'undefined', 'parser is defined');
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
QUnit.test('can set custom parsers', function(assert) {
|
|
19
|
+
const manifest = [
|
|
20
|
+
'#EXTM3U',
|
|
21
|
+
'#EXT-X-VERSION:3',
|
|
22
|
+
'#EXT-X-TARGETDURATION:10',
|
|
23
|
+
'#EXT-X-MEDIA-SEQUENCE:0',
|
|
24
|
+
'#EXT-X-PROGRAM-DATE-TIME:2017-07-31T20:35:35.053+00:00',
|
|
25
|
+
'#VOD-STARTTIMESTAMP:1501533337573',
|
|
26
|
+
'#VOD-TOTALDELETEDDURATION:0.0',
|
|
27
|
+
'#VOD-FRAMERATE:29.97',
|
|
28
|
+
''
|
|
29
|
+
].join('\n');
|
|
30
|
+
|
|
31
|
+
this.parser.addParser({
|
|
32
|
+
expression: /^#VOD-STARTTIMESTAMP/,
|
|
33
|
+
customType: 'startTimestamp'
|
|
34
|
+
});
|
|
35
|
+
this.parser.addParser({
|
|
36
|
+
expression: /^#VOD-TOTALDELETEDDURATION/,
|
|
37
|
+
customType: 'totalDeleteDuration'
|
|
38
|
+
});
|
|
39
|
+
this.parser.addParser({
|
|
40
|
+
expression: /^#VOD-FRAMERATE/,
|
|
41
|
+
customType: 'framerate',
|
|
42
|
+
dataParser: (line) => (line.split(':')[1])
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
this.parser.push(manifest);
|
|
46
|
+
this.parser.end();
|
|
47
|
+
assert.strictEqual(
|
|
48
|
+
this.parser.manifest.custom.startTimestamp,
|
|
49
|
+
'#VOD-STARTTIMESTAMP:1501533337573',
|
|
50
|
+
'sets custom timestamp line'
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
assert.strictEqual(
|
|
54
|
+
this.parser.manifest.custom.totalDeleteDuration,
|
|
55
|
+
'#VOD-TOTALDELETEDDURATION:0.0',
|
|
56
|
+
'sets custom delete duration'
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
assert.strictEqual(this.parser.manifest.custom.framerate, '29.97', 'sets framerate');
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
QUnit.test('segment level custom data', function(assert) {
|
|
63
|
+
const manifest = [
|
|
64
|
+
'#EXTM3U',
|
|
65
|
+
'#VOD-TIMING:1511816599485',
|
|
66
|
+
'#COMMENT',
|
|
67
|
+
'#EXTINF:8.0,',
|
|
68
|
+
'ex1.ts',
|
|
69
|
+
'#VOD-TIMING',
|
|
70
|
+
'#EXTINF:8.0,',
|
|
71
|
+
'ex2.ts',
|
|
72
|
+
'#VOD-TIMING:1511816615485',
|
|
73
|
+
'#EXT-UNKNOWN',
|
|
74
|
+
'#EXTINF:8.0,',
|
|
75
|
+
'ex3.ts',
|
|
76
|
+
'#VOD-TIMING:1511816623485',
|
|
77
|
+
'#EXTINF:8.0,',
|
|
78
|
+
'ex3.ts',
|
|
79
|
+
'#EXT-X-ENDLIST'
|
|
80
|
+
].join('\n');
|
|
81
|
+
|
|
82
|
+
this.parser.addParser({
|
|
83
|
+
expression: /^#VOD-TIMING/,
|
|
84
|
+
customType: 'vodTiming',
|
|
85
|
+
segment: true
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
this.parser.push(manifest);
|
|
89
|
+
this.parser.end();
|
|
90
|
+
assert.equal(
|
|
91
|
+
this.parser.manifest.segments[0].custom.vodTiming,
|
|
92
|
+
'#VOD-TIMING:1511816599485',
|
|
93
|
+
'parser attached segment level custom data'
|
|
94
|
+
);
|
|
95
|
+
assert.equal(
|
|
96
|
+
this.parser.manifest.segments[1].custom.vodTiming,
|
|
97
|
+
'#VOD-TIMING',
|
|
98
|
+
'parser got segment level custom data without :'
|
|
99
|
+
);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
QUnit.test('attaches cue-out data to segment', function(assert) {
|
|
103
|
+
const manifest = [
|
|
104
|
+
'#EXTM3U',
|
|
105
|
+
'#EXTINF:5,',
|
|
106
|
+
'#COMMENT',
|
|
107
|
+
'ex1.ts',
|
|
108
|
+
'#EXT-X-CUE-OUT:10',
|
|
109
|
+
'#EXTINF:5,',
|
|
110
|
+
'ex2.ts',
|
|
111
|
+
'#EXT-X-CUE-OUT15',
|
|
112
|
+
'#EXT-UKNOWN-TAG',
|
|
113
|
+
'#EXTINF:5,',
|
|
114
|
+
'ex3.ts',
|
|
115
|
+
'#EXT-X-CUE-OUT',
|
|
116
|
+
'#EXTINF:5,',
|
|
117
|
+
'ex3.ts',
|
|
118
|
+
'#EXT-X-ENDLIST'
|
|
119
|
+
].join('\n');
|
|
120
|
+
|
|
121
|
+
this.parser.push(manifest);
|
|
122
|
+
this.parser.end();
|
|
123
|
+
|
|
124
|
+
assert.equal(this.parser.manifest.segments[1].cueOut, '10', 'parser attached cue out tag');
|
|
125
|
+
assert.equal(this.parser.manifest.segments[2].cueOut, '15', 'cue out without : seperator');
|
|
126
|
+
assert.equal(this.parser.manifest.segments[3].cueOut, '', 'cue out without data');
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
QUnit.test('attaches cue-out-cont data to segment', function(assert) {
|
|
130
|
+
const manifest = [
|
|
131
|
+
'#EXTM3U',
|
|
132
|
+
'#EXTINF:5,',
|
|
133
|
+
'#COMMENT',
|
|
134
|
+
'ex1.ts',
|
|
135
|
+
'#EXT-X-CUE-OUT-CONT:10/60',
|
|
136
|
+
'#EXTINF:5,',
|
|
137
|
+
'ex2.ts',
|
|
138
|
+
'#EXT-X-CUE-OUT-CONT15/30',
|
|
139
|
+
'#EXT-UKNOWN-TAG',
|
|
140
|
+
'#EXTINF:5,',
|
|
141
|
+
'ex3.ts',
|
|
142
|
+
'#EXT-X-CUE-OUT-CONT',
|
|
143
|
+
'#EXTINF:5,',
|
|
144
|
+
'ex3.ts',
|
|
145
|
+
'#EXT-X-ENDLIST'
|
|
146
|
+
].join('\n');
|
|
147
|
+
|
|
148
|
+
this.parser.push(manifest);
|
|
149
|
+
this.parser.end();
|
|
150
|
+
|
|
151
|
+
assert.equal(
|
|
152
|
+
this.parser.manifest.segments[1].cueOutCont, '10/60',
|
|
153
|
+
'parser attached cue out cont tag'
|
|
154
|
+
);
|
|
155
|
+
assert.equal(
|
|
156
|
+
this.parser.manifest.segments[2].cueOutCont, '15/30',
|
|
157
|
+
'cue out cont without : seperator'
|
|
158
|
+
);
|
|
159
|
+
assert.equal(this.parser.manifest.segments[3].cueOutCont, '', 'cue out cont without data');
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
QUnit.test('attaches cue-in data to segment', function(assert) {
|
|
163
|
+
const manifest = [
|
|
164
|
+
'#EXTM3U',
|
|
165
|
+
'#EXTINF:5,',
|
|
166
|
+
'#COMMENT',
|
|
167
|
+
'ex1.ts',
|
|
168
|
+
'#EXT-X-CUE-IN',
|
|
169
|
+
'#EXTINF:5,',
|
|
170
|
+
'ex2.ts',
|
|
171
|
+
'#EXT-X-CUE-IN:15',
|
|
172
|
+
'#EXT-UKNOWN-TAG',
|
|
173
|
+
'#EXTINF:5,',
|
|
174
|
+
'ex3.ts',
|
|
175
|
+
'#EXT-X-CUE-IN=abc',
|
|
176
|
+
'#EXTINF:5,',
|
|
177
|
+
'ex3.ts',
|
|
178
|
+
'#EXT-X-ENDLIST'
|
|
179
|
+
].join('\n');
|
|
180
|
+
|
|
181
|
+
this.parser.push(manifest);
|
|
182
|
+
this.parser.end();
|
|
183
|
+
|
|
184
|
+
assert.equal(this.parser.manifest.segments[1].cueIn, '', 'parser attached cue in tag');
|
|
185
|
+
assert.equal(this.parser.manifest.segments[2].cueIn, '15', 'cue in with data');
|
|
186
|
+
assert.equal(
|
|
187
|
+
this.parser.manifest.segments[3].cueIn, '=abc',
|
|
188
|
+
'cue in without colon seperator'
|
|
189
|
+
);
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
QUnit.test('parses characteristics attribute', function(assert) {
|
|
193
|
+
const manifest = [
|
|
194
|
+
'#EXTM3U',
|
|
195
|
+
'#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="subs",CHARACTERISTICS="char",NAME="test"',
|
|
196
|
+
'#EXT-X-STREAM-INF:BANDWIDTH=1,CODECS="mp4a.40.2, avc1.4d400d",SUBTITLES="subs"',
|
|
197
|
+
'index.m3u8'
|
|
198
|
+
].join('\n');
|
|
199
|
+
|
|
200
|
+
this.parser.push(manifest);
|
|
201
|
+
this.parser.end();
|
|
202
|
+
|
|
203
|
+
assert.equal(
|
|
204
|
+
this.parser.manifest.mediaGroups.SUBTITLES.subs.test.characteristics,
|
|
205
|
+
'char',
|
|
206
|
+
'parsed CHARACTERISTICS attribute'
|
|
207
|
+
);
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
QUnit.test('parses FORCED attribute', function(assert) {
|
|
211
|
+
const manifest = [
|
|
212
|
+
'#EXTM3U',
|
|
213
|
+
'#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="subs",CHARACTERISTICS="char",NAME="test",FORCED=YES',
|
|
214
|
+
'#EXT-X-STREAM-INF:BANDWIDTH=1,CODECS="mp4a.40.2, avc1.4d400d",SUBTITLES="subs"',
|
|
215
|
+
'index.m3u8'
|
|
216
|
+
].join('\n');
|
|
217
|
+
|
|
218
|
+
this.parser.push(manifest);
|
|
219
|
+
this.parser.end();
|
|
220
|
+
|
|
221
|
+
assert.ok(
|
|
222
|
+
this.parser.manifest.mediaGroups.SUBTITLES.subs.test.forced,
|
|
223
|
+
'parsed FORCED attribute'
|
|
224
|
+
);
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
QUnit.test('parses Widevine #EXT-X-KEY attributes and attaches to manifest', function(assert) {
|
|
228
|
+
const manifest = [
|
|
229
|
+
'#EXTM3U',
|
|
230
|
+
'#EXT-X-KEY:METHOD=SAMPLE-AES-CTR,' +
|
|
231
|
+
'URI="data:text/plain;base64,AAAAPnBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAAB4iFnN' +
|
|
232
|
+
'oYWthX2NlYzJmNjRhYTc4OTBhMTFI49yVmwY=",KEYID=0x800AACAA522958AE888062B5695DB6BF,' +
|
|
233
|
+
'KEYFORMATVERSIONS="1",KEYFORMAT="urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed"',
|
|
234
|
+
'#EXTINF:5,',
|
|
235
|
+
'ex1.ts',
|
|
236
|
+
'#EXT-X-ENDLIST'
|
|
237
|
+
].join('\n');
|
|
238
|
+
|
|
239
|
+
this.parser.push(manifest);
|
|
240
|
+
this.parser.end();
|
|
241
|
+
|
|
242
|
+
assert.ok(this.parser.manifest.contentProtection, 'contentProtection property added');
|
|
243
|
+
assert.equal(
|
|
244
|
+
this.parser.manifest.contentProtection['com.widevine.alpha'].attributes.schemeIdUri,
|
|
245
|
+
'urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed',
|
|
246
|
+
'schemeIdUri set correctly'
|
|
247
|
+
);
|
|
248
|
+
assert.equal(
|
|
249
|
+
this.parser.manifest.contentProtection['com.widevine.alpha'].attributes.keyId,
|
|
250
|
+
'800AACAA522958AE888062B5695DB6BF',
|
|
251
|
+
'keyId set correctly'
|
|
252
|
+
);
|
|
253
|
+
assert.equal(
|
|
254
|
+
this.parser.manifest.contentProtection['com.widevine.alpha'].pssh.byteLength,
|
|
255
|
+
62,
|
|
256
|
+
'base64 URI decoded to TypedArray'
|
|
257
|
+
);
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
QUnit.test('Widevine #EXT-X-KEY attributes not attached to manifest if METHOD is invalid', function(assert) {
|
|
261
|
+
const manifest = [
|
|
262
|
+
'#EXTM3U',
|
|
263
|
+
'#EXT-X-KEY:METHOD=NONE,' +
|
|
264
|
+
'URI="data:text/plain;base64,AAAAPnBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAAB4iFnN' +
|
|
265
|
+
'oYWthX2NlYzJmNjRhYTc4OTBhMTFI49yVmwY=",KEYID=0x800AACAA522958AE888062B5695DB6BF,' +
|
|
266
|
+
'KEYFORMATVERSIONS="1",KEYFORMAT="urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed"',
|
|
267
|
+
'#EXTINF:5,',
|
|
268
|
+
'ex1.ts',
|
|
269
|
+
'#EXT-X-ENDLIST'
|
|
270
|
+
].join('\n');
|
|
271
|
+
|
|
272
|
+
this.parser.push(manifest);
|
|
273
|
+
this.parser.end();
|
|
274
|
+
|
|
275
|
+
assert.notOk(this.parser.manifest.contentProtection, 'contentProtection not added');
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
QUnit.test('Widevine #EXT-X-KEY attributes not attached to manifest if URI is invalid', function(assert) {
|
|
279
|
+
const manifest = [
|
|
280
|
+
'#EXTM3U',
|
|
281
|
+
'#EXT-X-KEY:METHOD=SAMPLE-AES-CTR,' +
|
|
282
|
+
'URI="AAAAPnBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAAB4iFnN' +
|
|
283
|
+
'oYWthX2NlYzJmNjRhYTc4OTBhMTFI49yVmwY=",KEYID=0x800AACAA522958AE888062B5695DB6BF,' +
|
|
284
|
+
'KEYFORMATVERSIONS="1",KEYFORMAT="urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed"',
|
|
285
|
+
'#EXTINF:5,',
|
|
286
|
+
'ex1.ts',
|
|
287
|
+
'#EXT-X-ENDLIST'
|
|
288
|
+
].join('\n');
|
|
289
|
+
|
|
290
|
+
this.parser.push(manifest);
|
|
291
|
+
this.parser.end();
|
|
292
|
+
|
|
293
|
+
assert.notOk(this.parser.manifest.contentProtection, 'contentProtection not added');
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
QUnit.test('Widevine #EXT-X-KEY attributes not attached to manifest if KEYID is invalid', function(assert) {
|
|
297
|
+
const manifest = [
|
|
298
|
+
'#EXTM3U',
|
|
299
|
+
'#EXT-X-KEY:METHOD=SAMPLE-AES-CTR,' +
|
|
300
|
+
'URI="data:text/plain;base64,AAAAPnBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAAB4iFnN' +
|
|
301
|
+
'oYWthX2NlYzJmNjRhYTc4OTBhMTFI49yVmwY=",KEYID=800AACAA522958AE888062B5695DB6BF,' +
|
|
302
|
+
'KEYFORMATVERSIONS="1",KEYFORMAT="urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed"',
|
|
303
|
+
'#EXTINF:5,',
|
|
304
|
+
'ex1.ts',
|
|
305
|
+
'#EXT-X-ENDLIST'
|
|
306
|
+
].join('\n');
|
|
307
|
+
|
|
308
|
+
this.parser.push(manifest);
|
|
309
|
+
this.parser.end();
|
|
310
|
+
|
|
311
|
+
assert.notOk(this.parser.manifest.contentProtection, 'contentProtection not added');
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
QUnit.test('Widevine #EXT-X-KEY attributes not attached to manifest if KEYFORMAT is not Widevine UUID', function(assert) {
|
|
315
|
+
const manifest = [
|
|
316
|
+
'#EXTM3U',
|
|
317
|
+
'#EXT-X-KEY:METHOD=SAMPLE-AES-CTR,' +
|
|
318
|
+
'URI="data:text/plain;base64,AAAAPnBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAAB4iFnN' +
|
|
319
|
+
'oYWthX2NlYzJmNjRhYTc4OTBhMTFI49yVmwY=",KEYID=0x800AACAA522958AE888062B5695DB6BF,' +
|
|
320
|
+
'KEYFORMATVERSIONS="1",KEYFORMAT="invalid-keyformat"',
|
|
321
|
+
'#EXTINF:5,',
|
|
322
|
+
'ex1.ts',
|
|
323
|
+
'#EXT-X-ENDLIST'
|
|
324
|
+
].join('\n');
|
|
325
|
+
|
|
326
|
+
this.parser.push(manifest);
|
|
327
|
+
this.parser.end();
|
|
328
|
+
|
|
329
|
+
assert.notOk(this.parser.manifest.contentProtection, 'contentProtection not added');
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
QUnit.test('byterange offset defaults to next byte', function(assert) {
|
|
333
|
+
const manifest = [
|
|
334
|
+
'#EXTM3U',
|
|
335
|
+
'#EXTINF:5,',
|
|
336
|
+
'#EXT-X-BYTERANGE:10@5',
|
|
337
|
+
'segment.ts',
|
|
338
|
+
'#EXTINF:5,',
|
|
339
|
+
'#EXT-X-BYTERANGE:20',
|
|
340
|
+
'segment.ts',
|
|
341
|
+
'#EXTINF:5,',
|
|
342
|
+
'#EXT-X-BYTERANGE:30',
|
|
343
|
+
'segment.ts',
|
|
344
|
+
'#EXTINF:5,',
|
|
345
|
+
'segment2.ts',
|
|
346
|
+
'#EXT-X-BYTERANGE:15@100',
|
|
347
|
+
'segment.ts',
|
|
348
|
+
'#EXT-X-BYTERANGE:17',
|
|
349
|
+
'segment.ts',
|
|
350
|
+
'#EXT-X-ENDLIST'
|
|
351
|
+
].join('\n');
|
|
352
|
+
|
|
353
|
+
this.parser.push(manifest);
|
|
354
|
+
this.parser.end();
|
|
355
|
+
|
|
356
|
+
assert.deepEqual(
|
|
357
|
+
this.parser.manifest.segments[0].byterange,
|
|
358
|
+
{ length: 10, offset: 5 },
|
|
359
|
+
'first segment has correct byterange'
|
|
360
|
+
);
|
|
361
|
+
assert.deepEqual(
|
|
362
|
+
this.parser.manifest.segments[1].byterange,
|
|
363
|
+
{ length: 20, offset: 15 },
|
|
364
|
+
'second segment has correct byterange'
|
|
365
|
+
);
|
|
366
|
+
assert.deepEqual(
|
|
367
|
+
this.parser.manifest.segments[2].byterange,
|
|
368
|
+
{ length: 30, offset: 35 },
|
|
369
|
+
'third segment has correct byterange'
|
|
370
|
+
);
|
|
371
|
+
assert.notOk(this.parser.manifest.segments[3].byterange, 'fourth segment has no byterange');
|
|
372
|
+
assert.deepEqual(
|
|
373
|
+
this.parser.manifest.segments[4].byterange,
|
|
374
|
+
{ length: 15, offset: 100 },
|
|
375
|
+
'fifth segment has correct byterange'
|
|
376
|
+
);
|
|
377
|
+
// not tested is a segment with no offset coming after a segment that isn't a sub range,
|
|
378
|
+
// as the spec requires that a byterange without an offset must follow a segment that
|
|
379
|
+
// is a sub range of the same media resource
|
|
380
|
+
assert.deepEqual(
|
|
381
|
+
this.parser.manifest.segments[5].byterange,
|
|
382
|
+
{ length: 17, offset: 115 },
|
|
383
|
+
'sixth segment has correct byterange'
|
|
384
|
+
);
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
QUnit.module('warn/info', {
|
|
388
|
+
beforeEach() {
|
|
389
|
+
this.warnings = [];
|
|
390
|
+
this.infos = [];
|
|
391
|
+
|
|
392
|
+
this.parser.on('warn', (warn) => this.warnings.push(warn.message));
|
|
393
|
+
this.parser.on('info', (info) => this.infos.push(info.message));
|
|
394
|
+
|
|
395
|
+
}
|
|
396
|
+
});
|
|
397
|
+
QUnit.test('warn when #EXT-X-TARGETDURATION is invalid', function(assert) {
|
|
398
|
+
this.parser.push([
|
|
399
|
+
'#EXT-X-VERSION:3',
|
|
400
|
+
'#EXT-X-MEDIA-SEQUENCE:0',
|
|
401
|
+
'#EXT-X-DISCONTINUITY-SEQUENCE:0',
|
|
402
|
+
'#EXT-X-TARGETDURATION:foo',
|
|
403
|
+
'#EXTINF:10,',
|
|
404
|
+
'media-00001.ts',
|
|
405
|
+
'#EXT-X-ENDLIST'
|
|
406
|
+
].join('\n'));
|
|
407
|
+
this.parser.end();
|
|
408
|
+
|
|
409
|
+
const warnings = [
|
|
410
|
+
'ignoring invalid target duration: undefined'
|
|
411
|
+
];
|
|
412
|
+
|
|
413
|
+
assert.deepEqual(
|
|
414
|
+
this.warnings,
|
|
415
|
+
warnings,
|
|
416
|
+
'warnings as expected'
|
|
417
|
+
);
|
|
418
|
+
|
|
419
|
+
assert.deepEqual(
|
|
420
|
+
this.infos,
|
|
421
|
+
[],
|
|
422
|
+
'info as expected'
|
|
423
|
+
);
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
QUnit.test('warns when #EXT-X-START missing TIME-OFFSET attribute', function(assert) {
|
|
427
|
+
this.parser.push([
|
|
428
|
+
'#EXT-X-VERSION:3',
|
|
429
|
+
'#EXT-X-MEDIA-SEQUENCE:0',
|
|
430
|
+
'#EXT-X-DISCONTINUITY-SEQUENCE:0',
|
|
431
|
+
'#EXT-X-TARGETDURATION:10',
|
|
432
|
+
'#EXT-X-START:PRECISE=YES',
|
|
433
|
+
'#EXTINF:10,',
|
|
434
|
+
'media-00001.ts',
|
|
435
|
+
'#EXT-X-ENDLIST'
|
|
436
|
+
].join('\n'));
|
|
437
|
+
this.parser.end();
|
|
438
|
+
|
|
439
|
+
assert.deepEqual(
|
|
440
|
+
this.warnings,
|
|
441
|
+
['ignoring start declaration without appropriate attribute list'],
|
|
442
|
+
'warnings as expected'
|
|
443
|
+
);
|
|
444
|
+
|
|
445
|
+
assert.deepEqual(
|
|
446
|
+
this.infos,
|
|
447
|
+
[],
|
|
448
|
+
'info as expected'
|
|
449
|
+
);
|
|
450
|
+
|
|
451
|
+
assert.strictEqual(typeof this.parser.manifest.start, 'undefined', 'does not parse start');
|
|
452
|
+
});
|
|
453
|
+
|
|
454
|
+
QUnit.test('warning when #EXT-X-SKIP missing SKIPPED-SEGMENTS attribute', function(assert) {
|
|
455
|
+
this.parser.push([
|
|
456
|
+
'#EXT-X-VERSION:3',
|
|
457
|
+
'#EXT-X-MEDIA-SEQUENCE:0',
|
|
458
|
+
'#EXT-X-DISCONTINUITY-SEQUENCE:0',
|
|
459
|
+
'#EXT-X-TARGETDURATION:10',
|
|
460
|
+
'#EXT-X-SKIP:foo=bar',
|
|
461
|
+
'#EXTINF:10,',
|
|
462
|
+
'media-00001.ts',
|
|
463
|
+
'#EXT-X-ENDLIST'
|
|
464
|
+
].join('\n'));
|
|
465
|
+
this.parser.end();
|
|
466
|
+
|
|
467
|
+
assert.deepEqual(
|
|
468
|
+
this.warnings,
|
|
469
|
+
['#EXT-X-SKIP lacks required attribute(s): SKIPPED-SEGMENTS'],
|
|
470
|
+
'warnings as expected'
|
|
471
|
+
);
|
|
472
|
+
|
|
473
|
+
assert.deepEqual(
|
|
474
|
+
this.infos,
|
|
475
|
+
[],
|
|
476
|
+
'info as expected'
|
|
477
|
+
);
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
QUnit.test('warns when #EXT-X-PART missing URI/DURATION attributes', function(assert) {
|
|
481
|
+
this.parser.push([
|
|
482
|
+
'#EXT-X-VERSION:3',
|
|
483
|
+
'#EXT-X-MEDIA-SEQUENCE:0',
|
|
484
|
+
'#EXT-X-DISCONTINUITY-SEQUENCE:0',
|
|
485
|
+
'#EXT-X-TARGETDURATION:10',
|
|
486
|
+
'#EXT-X-PART:DURATION=1',
|
|
487
|
+
'#EXT-X-PART:URI=2',
|
|
488
|
+
'#EXT-X-PART:foo=bar',
|
|
489
|
+
'#EXTINF:10,',
|
|
490
|
+
'media-00001.ts',
|
|
491
|
+
'#EXT-X-ENDLIST'
|
|
492
|
+
].join('\n'));
|
|
493
|
+
this.parser.end();
|
|
494
|
+
|
|
495
|
+
const warnings = [
|
|
496
|
+
'#EXT-X-PART #0 for segment #0 lacks required attribute(s): URI',
|
|
497
|
+
'#EXT-X-PART #1 for segment #0 lacks required attribute(s): DURATION',
|
|
498
|
+
'#EXT-X-PART #2 for segment #0 lacks required attribute(s): URI, DURATION'
|
|
499
|
+
];
|
|
500
|
+
|
|
501
|
+
assert.deepEqual(
|
|
502
|
+
this.warnings,
|
|
503
|
+
warnings,
|
|
504
|
+
'warnings as expected'
|
|
505
|
+
);
|
|
506
|
+
|
|
507
|
+
assert.deepEqual(
|
|
508
|
+
this.infos,
|
|
509
|
+
[],
|
|
510
|
+
'info as expected'
|
|
511
|
+
);
|
|
512
|
+
});
|
|
513
|
+
|
|
514
|
+
QUnit.test('warns when #EXT-X-PRELOAD-HINT missing TYPE/URI attribute', function(assert) {
|
|
515
|
+
this.parser.push([
|
|
516
|
+
'#EXT-X-VERSION:3',
|
|
517
|
+
'#EXT-X-MEDIA-SEQUENCE:0',
|
|
518
|
+
'#EXT-X-DISCONTINUITY-SEQUENCE:0',
|
|
519
|
+
'#EXT-X-TARGETDURATION:10',
|
|
520
|
+
'#EXT-X-PRELOAD-HINT:TYPE=foo',
|
|
521
|
+
'#EXT-X-PRELOAD-HINT:URI=foo',
|
|
522
|
+
'#EXT-X-PRELOAD-HINT:foo=bar',
|
|
523
|
+
'#EXTINF:10,',
|
|
524
|
+
'media-00001.ts',
|
|
525
|
+
'#EXT-X-ENDLIST'
|
|
526
|
+
].join('\n'));
|
|
527
|
+
this.parser.end();
|
|
528
|
+
|
|
529
|
+
const warnings = [
|
|
530
|
+
'#EXT-X-PRELOAD-HINT #0 for segment #0 lacks required attribute(s): URI',
|
|
531
|
+
'#EXT-X-PRELOAD-HINT #1 for segment #0 lacks required attribute(s): TYPE',
|
|
532
|
+
'#EXT-X-PRELOAD-HINT #2 for segment #0 lacks required attribute(s): TYPE, URI'
|
|
533
|
+
];
|
|
534
|
+
|
|
535
|
+
assert.deepEqual(
|
|
536
|
+
this.warnings,
|
|
537
|
+
warnings,
|
|
538
|
+
'warnings as expected'
|
|
539
|
+
);
|
|
540
|
+
|
|
541
|
+
assert.deepEqual(
|
|
542
|
+
this.infos,
|
|
543
|
+
[],
|
|
544
|
+
'info as expected'
|
|
545
|
+
);
|
|
546
|
+
});
|
|
547
|
+
|
|
548
|
+
QUnit.test('warns when we get #EXT-X-PRELOAD-HINT with the same TYPE', function(assert) {
|
|
549
|
+
this.parser.push([
|
|
550
|
+
'#EXT-X-VERSION:3',
|
|
551
|
+
'#EXT-X-MEDIA-SEQUENCE:0',
|
|
552
|
+
'#EXT-X-DISCONTINUITY-SEQUENCE:0',
|
|
553
|
+
'#EXT-X-TARGETDURATION:10',
|
|
554
|
+
'#EXT-X-PRELOAD-HINT:TYPE=foo,URI=foo1',
|
|
555
|
+
'#EXT-X-PRELOAD-HINT:TYPE=foo,URI=foo2',
|
|
556
|
+
'#EXTINF:10,',
|
|
557
|
+
'media-00001.ts',
|
|
558
|
+
'#EXT-X-ENDLIST'
|
|
559
|
+
].join('\n'));
|
|
560
|
+
this.parser.end();
|
|
561
|
+
|
|
562
|
+
const warnings = [
|
|
563
|
+
'#EXT-X-PRELOAD-HINT #1 for segment #0 has the same TYPE foo as preload hint #0'
|
|
564
|
+
];
|
|
565
|
+
|
|
566
|
+
assert.deepEqual(
|
|
567
|
+
this.warnings,
|
|
568
|
+
warnings,
|
|
569
|
+
'warnings as expected'
|
|
570
|
+
);
|
|
571
|
+
|
|
572
|
+
assert.deepEqual(
|
|
573
|
+
this.infos,
|
|
574
|
+
[],
|
|
575
|
+
'info as expected'
|
|
576
|
+
);
|
|
577
|
+
});
|
|
578
|
+
|
|
579
|
+
QUnit.test('warn when #EXT-X-RENDITION-REPORT missing LAST-MSN/URI attribute', function(assert) {
|
|
580
|
+
this.parser.push([
|
|
581
|
+
'#EXT-X-VERSION:3',
|
|
582
|
+
'#EXT-X-MEDIA-SEQUENCE:0',
|
|
583
|
+
'#EXT-X-DISCONTINUITY-SEQUENCE:0',
|
|
584
|
+
'#EXT-X-TARGETDURATION:10',
|
|
585
|
+
'#EXT-X-RENDITION-REPORT:URI=foo',
|
|
586
|
+
'#EXT-X-RENDITION-REPORT:LAST-MSN=2',
|
|
587
|
+
'#EXT-X-RENDITION-REPORT:foo=bar',
|
|
588
|
+
'#EXTINF:10,',
|
|
589
|
+
'media-00001.ts',
|
|
590
|
+
'#EXT-X-ENDLIST'
|
|
591
|
+
].join('\n'));
|
|
592
|
+
this.parser.end();
|
|
593
|
+
|
|
594
|
+
const warnings = [
|
|
595
|
+
'#EXT-X-RENDITION-REPORT #0 lacks required attribute(s): LAST-MSN',
|
|
596
|
+
'#EXT-X-RENDITION-REPORT #1 lacks required attribute(s): URI',
|
|
597
|
+
'#EXT-X-RENDITION-REPORT #2 lacks required attribute(s): LAST-MSN, URI'
|
|
598
|
+
];
|
|
599
|
+
|
|
600
|
+
assert.deepEqual(
|
|
601
|
+
this.warnings,
|
|
602
|
+
warnings,
|
|
603
|
+
'warnings as expected'
|
|
604
|
+
);
|
|
605
|
+
|
|
606
|
+
assert.deepEqual(
|
|
607
|
+
this.infos,
|
|
608
|
+
[],
|
|
609
|
+
'info as expected'
|
|
610
|
+
);
|
|
611
|
+
});
|
|
612
|
+
|
|
613
|
+
QUnit.test('warns when #EXT-X-RENDITION-REPORT missing LAST-PART attribute with parts', function(assert) {
|
|
614
|
+
this.parser.push([
|
|
615
|
+
'#EXT-X-VERSION:3',
|
|
616
|
+
'#EXT-X-MEDIA-SEQUENCE:0',
|
|
617
|
+
'#EXT-X-DISCONTINUITY-SEQUENCE:0',
|
|
618
|
+
'#EXT-X-TARGETDURATION:10',
|
|
619
|
+
'#EXT-X-RENDITION-REPORT:URI=foo,LAST-MSN=4',
|
|
620
|
+
'#EXT-X-PART:URI=foo,DURATION=10',
|
|
621
|
+
'#EXT-X-RENDITION-REPORT:URI=foo,LAST-MSN=4',
|
|
622
|
+
'#EXTINF:10,',
|
|
623
|
+
'media-00001.ts',
|
|
624
|
+
'#EXT-X-ENDLIST'
|
|
625
|
+
].join('\n'));
|
|
626
|
+
this.parser.end();
|
|
627
|
+
|
|
628
|
+
const warnings = [
|
|
629
|
+
'#EXT-X-RENDITION-REPORT #0 lacks required attribute(s): LAST-PART',
|
|
630
|
+
'#EXT-X-RENDITION-REPORT #1 lacks required attribute(s): LAST-PART'
|
|
631
|
+
];
|
|
632
|
+
|
|
633
|
+
assert.deepEqual(
|
|
634
|
+
this.warnings,
|
|
635
|
+
warnings,
|
|
636
|
+
'warnings as expected'
|
|
637
|
+
);
|
|
638
|
+
|
|
639
|
+
assert.deepEqual(
|
|
640
|
+
this.infos,
|
|
641
|
+
[],
|
|
642
|
+
'info as expected'
|
|
643
|
+
);
|
|
644
|
+
});
|
|
645
|
+
|
|
646
|
+
QUnit.test('warns when #EXT-X-PART-INF missing PART-TARGET attribute', function(assert) {
|
|
647
|
+
this.parser.push([
|
|
648
|
+
'#EXT-X-VERSION:3',
|
|
649
|
+
'#EXT-X-MEDIA-SEQUENCE:0',
|
|
650
|
+
'#EXT-X-DISCONTINUITY-SEQUENCE:0',
|
|
651
|
+
'#EXT-X-TARGETDURATION:10',
|
|
652
|
+
'#EXT-X-PART-INF:URI=foo',
|
|
653
|
+
'#EXTINF:10,',
|
|
654
|
+
'media-00001.ts',
|
|
655
|
+
'#EXT-X-ENDLIST'
|
|
656
|
+
].join('\n'));
|
|
657
|
+
this.parser.end();
|
|
658
|
+
|
|
659
|
+
const warnings = [
|
|
660
|
+
'#EXT-X-PART-INF lacks required attribute(s): PART-TARGET'
|
|
661
|
+
];
|
|
662
|
+
|
|
663
|
+
assert.deepEqual(
|
|
664
|
+
this.warnings,
|
|
665
|
+
warnings,
|
|
666
|
+
'warnings as expected'
|
|
667
|
+
);
|
|
668
|
+
|
|
669
|
+
assert.deepEqual(
|
|
670
|
+
this.infos,
|
|
671
|
+
[],
|
|
672
|
+
'info as expected'
|
|
673
|
+
);
|
|
674
|
+
});
|
|
675
|
+
|
|
676
|
+
QUnit.test('warns when #EXT-X-SERVER-CONTROL missing CAN-SKIP-UNTIL with CAN-SKIP-DATERANGES attribute', function(assert) {
|
|
677
|
+
this.parser.push([
|
|
678
|
+
'#EXT-X-VERSION:3',
|
|
679
|
+
'#EXT-X-MEDIA-SEQUENCE:0',
|
|
680
|
+
'#EXT-X-DISCONTINUITY-SEQUENCE:0',
|
|
681
|
+
'#EXT-X-TARGETDURATION:10',
|
|
682
|
+
'#EXT-X-SERVER-CONTROL:CAN-BLOCK-RELOAD=NO,HOLD-BACK=30,CAN-SKIP-DATERANGES=YES',
|
|
683
|
+
'#EXTINF:10,',
|
|
684
|
+
'media-00001.ts',
|
|
685
|
+
'#EXT-X-ENDLIST'
|
|
686
|
+
].join('\n'));
|
|
687
|
+
this.parser.end();
|
|
688
|
+
|
|
689
|
+
const warnings = [
|
|
690
|
+
'#EXT-X-SERVER-CONTROL lacks required attribute CAN-SKIP-UNTIL which is required when CAN-SKIP-DATERANGES is set'
|
|
691
|
+
];
|
|
692
|
+
|
|
693
|
+
assert.deepEqual(
|
|
694
|
+
this.warnings,
|
|
695
|
+
warnings,
|
|
696
|
+
'warnings as expected'
|
|
697
|
+
);
|
|
698
|
+
|
|
699
|
+
assert.deepEqual(
|
|
700
|
+
this.infos,
|
|
701
|
+
[],
|
|
702
|
+
'info as expected'
|
|
703
|
+
);
|
|
704
|
+
});
|
|
705
|
+
|
|
706
|
+
QUnit.test('warn when #EXT-X-SERVER-CONTROL HOLD-BACK and PART-HOLD-BACK too low', function(assert) {
|
|
707
|
+
this.parser.push([
|
|
708
|
+
'#EXT-X-VERSION:3',
|
|
709
|
+
'#EXT-X-MEDIA-SEQUENCE:0',
|
|
710
|
+
'#EXT-X-DISCONTINUITY-SEQUENCE:0',
|
|
711
|
+
'#EXT-X-TARGETDURATION:10',
|
|
712
|
+
'#EXT-X-PART-INF:PART-TARGET=1',
|
|
713
|
+
'#EXT-X-SERVER-CONTROL:CAN-BLOCK-RELOAD=YES,HOLD-BACK=1,PART-HOLD-BACK=0.5',
|
|
714
|
+
'#EXTINF:10,',
|
|
715
|
+
'media-00001.ts',
|
|
716
|
+
'#EXT-X-ENDLIST'
|
|
717
|
+
].join('\n'));
|
|
718
|
+
this.parser.end();
|
|
719
|
+
|
|
720
|
+
const warnings = [
|
|
721
|
+
'#EXT-X-SERVER-CONTROL clamping HOLD-BACK (1) to targetDuration * 3 (30)',
|
|
722
|
+
'#EXT-X-SERVER-CONTROL clamping PART-HOLD-BACK (0.5) to partTargetDuration * 2 (2).'
|
|
723
|
+
];
|
|
724
|
+
|
|
725
|
+
assert.deepEqual(
|
|
726
|
+
this.warnings,
|
|
727
|
+
warnings,
|
|
728
|
+
'warnings as expected'
|
|
729
|
+
);
|
|
730
|
+
|
|
731
|
+
assert.deepEqual(
|
|
732
|
+
this.infos,
|
|
733
|
+
[],
|
|
734
|
+
'info as expected'
|
|
735
|
+
);
|
|
736
|
+
});
|
|
737
|
+
|
|
738
|
+
QUnit.test('warn when #EXT-X-SERVER-CONTROL before target durations HOLD-BACK/PART-HOLD-BACK too low', function(assert) {
|
|
739
|
+
this.parser.push([
|
|
740
|
+
'#EXT-X-VERSION:3',
|
|
741
|
+
'#EXT-X-MEDIA-SEQUENCE:0',
|
|
742
|
+
'#EXT-X-SERVER-CONTROL:CAN-BLOCK-RELOAD=YES,HOLD-BACK=1,PART-HOLD-BACK=0.5',
|
|
743
|
+
'#EXT-X-DISCONTINUITY-SEQUENCE:0',
|
|
744
|
+
'#EXT-X-TARGETDURATION:10',
|
|
745
|
+
'#EXT-X-PART-INF:PART-TARGET=1',
|
|
746
|
+
'#EXTINF:10,',
|
|
747
|
+
'media-00001.ts',
|
|
748
|
+
'#EXT-X-ENDLIST'
|
|
749
|
+
].join('\n'));
|
|
750
|
+
this.parser.end();
|
|
751
|
+
|
|
752
|
+
const warnings = [
|
|
753
|
+
'#EXT-X-SERVER-CONTROL clamping HOLD-BACK (1) to targetDuration * 3 (30)',
|
|
754
|
+
'#EXT-X-SERVER-CONTROL clamping PART-HOLD-BACK (0.5) to partTargetDuration * 2 (2).'
|
|
755
|
+
];
|
|
756
|
+
|
|
757
|
+
assert.deepEqual(
|
|
758
|
+
this.warnings,
|
|
759
|
+
warnings,
|
|
760
|
+
'warnings as expected'
|
|
761
|
+
);
|
|
762
|
+
|
|
763
|
+
assert.deepEqual(
|
|
764
|
+
this.infos,
|
|
765
|
+
[],
|
|
766
|
+
'info as expected'
|
|
767
|
+
);
|
|
768
|
+
});
|
|
769
|
+
|
|
770
|
+
QUnit.test('info when #EXT-X-SERVER-CONTROL sets defaults', function(assert) {
|
|
771
|
+
this.parser.push([
|
|
772
|
+
'#EXT-X-VERSION:3',
|
|
773
|
+
'#EXT-X-MEDIA-SEQUENCE:0',
|
|
774
|
+
'#EXT-X-DISCONTINUITY-SEQUENCE:0',
|
|
775
|
+
'#EXT-X-TARGETDURATION:10',
|
|
776
|
+
'#EXT-X-PART-INF:PART-TARGET=1',
|
|
777
|
+
'#EXT-X-SERVER-CONTROL:foo=bar',
|
|
778
|
+
'#EXTINF:10,',
|
|
779
|
+
'media-00001.ts',
|
|
780
|
+
'#EXT-X-ENDLIST'
|
|
781
|
+
].join('\n'));
|
|
782
|
+
this.parser.end();
|
|
783
|
+
|
|
784
|
+
const infos = [
|
|
785
|
+
'#EXT-X-SERVER-CONTROL defaulting CAN-BLOCK-RELOAD to false',
|
|
786
|
+
'#EXT-X-SERVER-CONTROL defaulting HOLD-BACK to targetDuration * 3 (30).',
|
|
787
|
+
'#EXT-X-SERVER-CONTROL defaulting PART-HOLD-BACK to partTargetDuration * 3 (3).'
|
|
788
|
+
];
|
|
789
|
+
|
|
790
|
+
assert.deepEqual(
|
|
791
|
+
this.warnings,
|
|
792
|
+
[],
|
|
793
|
+
'warnings as expected'
|
|
794
|
+
);
|
|
795
|
+
|
|
796
|
+
assert.deepEqual(
|
|
797
|
+
this.infos,
|
|
798
|
+
infos,
|
|
799
|
+
'info as expected'
|
|
800
|
+
);
|
|
801
|
+
});
|
|
802
|
+
|
|
803
|
+
QUnit.test('info when #EXT-X-SERVER-CONTROL before target durations and sets defaults', function(assert) {
|
|
804
|
+
this.parser.push([
|
|
805
|
+
'#EXT-X-VERSION:3',
|
|
806
|
+
'#EXT-X-MEDIA-SEQUENCE:0',
|
|
807
|
+
'#EXT-X-DISCONTINUITY-SEQUENCE:0',
|
|
808
|
+
'#EXT-X-SERVER-CONTROL:foo=bar',
|
|
809
|
+
'#EXT-X-TARGETDURATION:10',
|
|
810
|
+
'#EXT-X-PART-INF:PART-TARGET=1',
|
|
811
|
+
'#EXTINF:10,',
|
|
812
|
+
'media-00001.ts',
|
|
813
|
+
'#EXT-X-ENDLIST'
|
|
814
|
+
].join('\n'));
|
|
815
|
+
this.parser.end();
|
|
816
|
+
|
|
817
|
+
const infos = [
|
|
818
|
+
'#EXT-X-SERVER-CONTROL defaulting CAN-BLOCK-RELOAD to false',
|
|
819
|
+
'#EXT-X-SERVER-CONTROL defaulting HOLD-BACK to targetDuration * 3 (30).',
|
|
820
|
+
'#EXT-X-SERVER-CONTROL defaulting PART-HOLD-BACK to partTargetDuration * 3 (3).'
|
|
821
|
+
];
|
|
822
|
+
|
|
823
|
+
assert.deepEqual(
|
|
824
|
+
this.warnings,
|
|
825
|
+
[],
|
|
826
|
+
'warnings as expected'
|
|
827
|
+
);
|
|
828
|
+
|
|
829
|
+
assert.deepEqual(
|
|
830
|
+
this.infos,
|
|
831
|
+
infos,
|
|
832
|
+
'info as expected'
|
|
833
|
+
);
|
|
834
|
+
});
|
|
835
|
+
|
|
836
|
+
QUnit.test('Can understand widevine/fairplay/playready drm ext-x-key', function(assert) {
|
|
837
|
+
this.parser.push([
|
|
838
|
+
'#EXT-X-VERSION:3',
|
|
839
|
+
'#EXT-X-MEDIA-SEQUENCE:0',
|
|
840
|
+
'#EXT-X-DISCONTINUITY-SEQUENCE:0',
|
|
841
|
+
'#EXT-X-TARGETDURATION:10',
|
|
842
|
+
'#EXT-X-PART-INF:PART-TARGET=1',
|
|
843
|
+
'#EXT-X-SERVER-CONTROL:foo=bar',
|
|
844
|
+
'#EXT-X-KEY:METHOD=SAMPLE-AES,URI="data:text/plain;base64,foo",KEYID=0x555777,IV=1234567890abcdef1234567890abcdef,KEYFORMATVERSIONS="1",KEYFORMAT="urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed"',
|
|
845
|
+
'#EXT-X-KEY:METHOD=SAMPLE-AES,URI="skd://foo",KEYFORMATVERSIONS="1",KEYFORMAT="com.apple.streamingkeydelivery"',
|
|
846
|
+
'#EXT-X-KEY:METHOD=SAMPLE-AES,URI="http://example.com",KEYFORMATVERSIONS="1",KEYFORMAT="com.microsoft.playready"',
|
|
847
|
+
'#EXTINF:10,',
|
|
848
|
+
'media-00001.ts',
|
|
849
|
+
'#EXT-X-ENDLIST'
|
|
850
|
+
].join('\n'));
|
|
851
|
+
this.parser.end();
|
|
852
|
+
|
|
853
|
+
assert.deepEqual(
|
|
854
|
+
Object.keys(this.parser.manifest.contentProtection),
|
|
855
|
+
['com.widevine.alpha', 'com.apple.fps.1_0', 'com.microsoft.playready'],
|
|
856
|
+
'info as expected'
|
|
857
|
+
);
|
|
858
|
+
});
|
|
859
|
+
|
|
860
|
+
QUnit.module('integration');
|
|
861
|
+
|
|
862
|
+
for (const key in testDataExpected) {
|
|
863
|
+
if (!testDataManifests[key]) {
|
|
864
|
+
throw new Error(`${key}.js does not have an equivelent m3u8 manifest to test against`);
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
for (const key in testDataManifests) {
|
|
869
|
+
if (!testDataExpected[key]) {
|
|
870
|
+
throw new Error(`${key}.m3u8 does not have an equivelent expected js file to test against`);
|
|
871
|
+
}
|
|
872
|
+
QUnit.test(`parses ${key}.m3u8 as expected in ${key}.js`, function(assert) {
|
|
873
|
+
this.parser.push(testDataManifests[key]());
|
|
874
|
+
this.parser.end();
|
|
875
|
+
|
|
876
|
+
assert.deepEqual(
|
|
877
|
+
this.parser.manifest,
|
|
878
|
+
testDataExpected[key](),
|
|
879
|
+
key + '.m3u8 was parsed correctly'
|
|
880
|
+
);
|
|
881
|
+
});
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
});
|