hls.js 1.5.2-0.canary.9923 → 1.5.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/dist/hls-demo.js +0 -5
- package/dist/hls-demo.js.map +1 -1
- package/dist/hls.js +379 -431
- package/dist/hls.js.d.ts +14 -14
- package/dist/hls.js.map +1 -1
- package/dist/hls.light.js +217 -275
- package/dist/hls.light.js.map +1 -1
- package/dist/hls.light.min.js +1 -1
- package/dist/hls.light.min.js.map +1 -1
- package/dist/hls.light.mjs +219 -278
- package/dist/hls.light.mjs.map +1 -1
- package/dist/hls.min.js +1 -1
- package/dist/hls.min.js.map +1 -1
- package/dist/hls.mjs +350 -401
- package/dist/hls.mjs.map +1 -1
- package/dist/hls.worker.js +1 -1
- package/dist/hls.worker.js.map +1 -1
- package/package.json +9 -9
- package/src/controller/abr-controller.ts +2 -2
- package/src/controller/base-stream-controller.ts +16 -16
- package/src/controller/buffer-controller.ts +0 -6
- package/src/controller/eme-controller.ts +12 -6
- package/src/controller/latency-controller.ts +8 -7
- package/src/controller/level-controller.ts +18 -7
- package/src/controller/stream-controller.ts +14 -11
- package/src/controller/subtitle-stream-controller.ts +1 -6
- package/src/controller/subtitle-track-controller.ts +2 -4
- package/src/controller/timeline-controller.ts +26 -20
- package/src/crypt/aes-crypto.ts +2 -21
- package/src/crypt/decrypter.ts +18 -32
- package/src/crypt/fast-aes-key.ts +5 -24
- package/src/demux/audio/adts.ts +4 -9
- package/src/demux/sample-aes.ts +0 -2
- package/src/demux/transmuxer-interface.ts +12 -4
- package/src/demux/transmuxer.ts +3 -16
- package/src/demux/tsdemuxer.ts +17 -12
- package/src/loader/fragment-loader.ts +2 -9
- package/src/loader/key-loader.ts +0 -2
- package/src/loader/level-key.ts +9 -10
- package/src/remux/mp4-remuxer.ts +3 -4
- package/src/types/demuxer.ts +0 -1
- package/src/utils/codecs.ts +4 -33
- package/src/crypt/decrypter-aes-mode.ts +0 -4
- package/src/utils/encryption-methods-util.ts +0 -21
package/package.json
CHANGED
@@ -67,10 +67,10 @@
|
|
67
67
|
"@babel/plugin-proposal-object-rest-spread": "7.20.7",
|
68
68
|
"@babel/plugin-proposal-optional-chaining": "7.21.0",
|
69
69
|
"@babel/plugin-transform-object-assign": "7.23.3",
|
70
|
-
"@babel/preset-env": "7.23.
|
70
|
+
"@babel/preset-env": "7.23.7",
|
71
71
|
"@babel/preset-typescript": "7.23.3",
|
72
72
|
"@babel/register": "7.23.7",
|
73
|
-
"@microsoft/api-documenter": "7.23.
|
73
|
+
"@microsoft/api-documenter": "7.23.16",
|
74
74
|
"@microsoft/api-extractor": "7.39.1",
|
75
75
|
"@rollup/plugin-alias": "5.1.0",
|
76
76
|
"@rollup/plugin-babel": "6.0.4",
|
@@ -78,17 +78,17 @@
|
|
78
78
|
"@rollup/plugin-node-resolve": "15.2.3",
|
79
79
|
"@rollup/plugin-replace": "5.0.5",
|
80
80
|
"@rollup/plugin-terser": "0.4.4",
|
81
|
-
"@rollup/plugin-typescript": "11.1.
|
81
|
+
"@rollup/plugin-typescript": "11.1.5",
|
82
82
|
"@svta/common-media-library": "0.6.1",
|
83
83
|
"@types/chai": "4.3.11",
|
84
84
|
"@types/chart.js": "2.9.41",
|
85
85
|
"@types/mocha": "10.0.6",
|
86
86
|
"@types/sinon-chai": "3.2.12",
|
87
|
-
"@typescript-eslint/eslint-plugin": "6.
|
88
|
-
"@typescript-eslint/parser": "6.
|
87
|
+
"@typescript-eslint/eslint-plugin": "6.17.0",
|
88
|
+
"@typescript-eslint/parser": "6.17.0",
|
89
89
|
"babel-loader": "9.1.3",
|
90
90
|
"babel-plugin-transform-remove-console": "6.9.4",
|
91
|
-
"chai": "4.
|
91
|
+
"chai": "4.3.10",
|
92
92
|
"chart.js": "2.9.4",
|
93
93
|
"chromedriver": "120.0.1",
|
94
94
|
"doctoc": "2.2.1",
|
@@ -119,7 +119,7 @@
|
|
119
119
|
"npm-run-all": "4.1.5",
|
120
120
|
"prettier": "3.1.1",
|
121
121
|
"promise-polyfill": "8.3.0",
|
122
|
-
"rollup": "4.9.
|
122
|
+
"rollup": "4.9.4",
|
123
123
|
"rollup-plugin-istanbul": "5.0.0",
|
124
124
|
"sauce-connect-launcher": "1.3.2",
|
125
125
|
"selenium-webdriver": "4.16.0",
|
@@ -128,7 +128,7 @@
|
|
128
128
|
"sinon-chai": "3.7.0",
|
129
129
|
"typescript": "5.3.3",
|
130
130
|
"url-toolkit": "2.2.5",
|
131
|
-
"wrangler": "3.
|
131
|
+
"wrangler": "3.22.4"
|
132
132
|
},
|
133
|
-
"version": "1.5.2
|
133
|
+
"version": "1.5.2"
|
134
134
|
}
|
@@ -286,7 +286,7 @@ class AbrController implements AbrComponentAPI {
|
|
286
286
|
const level = levels[frag.level];
|
287
287
|
const expectedLen =
|
288
288
|
stats.total ||
|
289
|
-
Math.max(stats.loaded, Math.round((duration * level.
|
289
|
+
Math.max(stats.loaded, Math.round((duration * level.averageBitrate) / 8));
|
290
290
|
let timeStreaming = loadedFirstByte ? timeLoading - ttfb : timeLoading;
|
291
291
|
if (timeStreaming < 1 && loadedFirstByte) {
|
292
292
|
timeStreaming = Math.min(timeLoading, (stats.loaded * 8) / bwEstimate);
|
@@ -346,7 +346,7 @@ class AbrController implements AbrComponentAPI {
|
|
346
346
|
// If there has been no loading progress, sample TTFB
|
347
347
|
this.bwEstimator.sampleTTFB(timeLoading);
|
348
348
|
}
|
349
|
-
const nextLoadLevelBitrate = levels[nextLoadLevel].
|
349
|
+
const nextLoadLevelBitrate = levels[nextLoadLevel].maxBitrate;
|
350
350
|
if (
|
351
351
|
this.getBwEstimate() * this.hls.config.abrBandWidthUpFactor >
|
352
352
|
nextLoadLevelBitrate
|
@@ -7,10 +7,6 @@ import { ErrorDetails, ErrorTypes } from '../errors';
|
|
7
7
|
import { ChunkMetadata } from '../types/transmuxer';
|
8
8
|
import { appendUint8Array } from '../utils/mp4-tools';
|
9
9
|
import { alignStream } from '../utils/discontinuities';
|
10
|
-
import {
|
11
|
-
isFullSegmentEncryption,
|
12
|
-
getAesModeFromFullSegmentMethod,
|
13
|
-
} from '../utils/encryption-methods-util';
|
14
10
|
import {
|
15
11
|
findFragmentByPDT,
|
16
12
|
findFragmentByPTS,
|
@@ -101,6 +97,8 @@ export default class BaseStreamController
|
|
101
97
|
protected startFragRequested: boolean = false;
|
102
98
|
protected decrypter: Decrypter;
|
103
99
|
protected initPTS: RationalTimestamp[] = [];
|
100
|
+
protected onvseeking: EventListener | null = null;
|
101
|
+
protected onvended: EventListener | null = null;
|
104
102
|
|
105
103
|
private readonly logPrefix: string = '';
|
106
104
|
protected log: (msg: any) => void;
|
@@ -199,8 +197,10 @@ export default class BaseStreamController
|
|
199
197
|
data: MediaAttachedData,
|
200
198
|
) {
|
201
199
|
const media = (this.media = this.mediaBuffer = data.media);
|
202
|
-
|
203
|
-
|
200
|
+
this.onvseeking = this.onMediaSeeking.bind(this) as EventListener;
|
201
|
+
this.onvended = this.onMediaEnded.bind(this) as EventListener;
|
202
|
+
media.addEventListener('seeking', this.onvseeking);
|
203
|
+
media.addEventListener('ended', this.onvended);
|
204
204
|
const config = this.config;
|
205
205
|
if (this.levels && config.autoStartLoad && this.state === State.STOPPED) {
|
206
206
|
this.startLoad(config.startPosition);
|
@@ -215,9 +215,10 @@ export default class BaseStreamController
|
|
215
215
|
}
|
216
216
|
|
217
217
|
// remove video listeners
|
218
|
-
if (media) {
|
219
|
-
media.removeEventListener('seeking', this.
|
220
|
-
media.removeEventListener('ended', this.
|
218
|
+
if (media && this.onvseeking && this.onvended) {
|
219
|
+
media.removeEventListener('seeking', this.onvseeking);
|
220
|
+
media.removeEventListener('ended', this.onvended);
|
221
|
+
this.onvseeking = this.onvended = null;
|
221
222
|
}
|
222
223
|
if (this.keyLoader) {
|
223
224
|
this.keyLoader.detach();
|
@@ -228,7 +229,7 @@ export default class BaseStreamController
|
|
228
229
|
this.stopLoad();
|
229
230
|
}
|
230
231
|
|
231
|
-
protected onMediaSeeking
|
232
|
+
protected onMediaSeeking() {
|
232
233
|
const { config, fragCurrent, media, mediaBuffer, state } = this;
|
233
234
|
const currentTime: number = media ? media.currentTime : 0;
|
234
235
|
const bufferInfo = BufferHelper.bufferInfo(
|
@@ -291,12 +292,12 @@ export default class BaseStreamController
|
|
291
292
|
|
292
293
|
// Async tick to speed up processing
|
293
294
|
this.tickImmediate();
|
294
|
-
}
|
295
|
+
}
|
295
296
|
|
296
|
-
protected onMediaEnded
|
297
|
+
protected onMediaEnded() {
|
297
298
|
// reset startPosition and lastCurrentTime to restart playback @ stream beginning
|
298
299
|
this.startPosition = this.lastCurrentTime = 0;
|
299
|
-
}
|
300
|
+
}
|
300
301
|
|
301
302
|
protected onManifestLoaded(
|
302
303
|
event: Events.MANIFEST_LOADED,
|
@@ -311,7 +312,7 @@ export default class BaseStreamController
|
|
311
312
|
this.stopLoad();
|
312
313
|
super.onHandlerDestroying();
|
313
314
|
// @ts-ignore
|
314
|
-
this.hls =
|
315
|
+
this.hls = null;
|
315
316
|
}
|
316
317
|
|
317
318
|
protected onHandlerDestroyed() {
|
@@ -485,7 +486,7 @@ export default class BaseStreamController
|
|
485
486
|
payload.byteLength > 0 &&
|
486
487
|
decryptData?.key &&
|
487
488
|
decryptData.iv &&
|
488
|
-
|
489
|
+
decryptData.method === 'AES-128'
|
489
490
|
) {
|
490
491
|
const startTime = self.performance.now();
|
491
492
|
// decrypt init segment data
|
@@ -494,7 +495,6 @@ export default class BaseStreamController
|
|
494
495
|
new Uint8Array(payload),
|
495
496
|
decryptData.key.buffer,
|
496
497
|
decryptData.iv.buffer,
|
497
|
-
getAesModeFromFullSegmentMethod(decryptData.method),
|
498
498
|
)
|
499
499
|
.catch((err) => {
|
500
500
|
hls.trigger(Events.ERROR, {
|
@@ -110,12 +110,6 @@ export default class BufferController implements ComponentAPI {
|
|
110
110
|
this.lastMpegAudioChunk = null;
|
111
111
|
// @ts-ignore
|
112
112
|
this.hls = null;
|
113
|
-
// @ts-ignore
|
114
|
-
this._onMediaSourceOpen = this._onMediaSourceClose = null;
|
115
|
-
// @ts-ignore
|
116
|
-
this._onMediaSourceEnded = null;
|
117
|
-
// @ts-ignore
|
118
|
-
this._onStartStreaming = this._onEndStreaming = null;
|
119
113
|
}
|
120
114
|
|
121
115
|
protected registerListeners() {
|
@@ -90,6 +90,8 @@ class EMEController implements ComponentAPI {
|
|
90
90
|
private setMediaKeysQueue: Promise<void>[] = EMEController.CDMCleanupPromise
|
91
91
|
? [EMEController.CDMCleanupPromise]
|
92
92
|
: [];
|
93
|
+
private onMediaEncrypted = this._onMediaEncrypted.bind(this);
|
94
|
+
private onWaitingForKey = this._onWaitingForKey.bind(this);
|
93
95
|
|
94
96
|
private debug: (msg: any) => void = logger.debug.bind(logger, LOGGER_PREFIX);
|
95
97
|
private log: (msg: any) => void = logger.log.bind(logger, LOGGER_PREFIX);
|
@@ -111,9 +113,13 @@ class EMEController implements ComponentAPI {
|
|
111
113
|
config.licenseXhrSetup = config.licenseResponseCallback = undefined;
|
112
114
|
config.drmSystems = config.drmSystemOptions = {};
|
113
115
|
// @ts-ignore
|
114
|
-
this.hls =
|
116
|
+
this.hls =
|
117
|
+
this.onMediaEncrypted =
|
118
|
+
this.onWaitingForKey =
|
119
|
+
this.keyIdToKeySessionPromise =
|
120
|
+
null as any;
|
115
121
|
// @ts-ignore
|
116
|
-
this.
|
122
|
+
this.config = null;
|
117
123
|
}
|
118
124
|
|
119
125
|
private registerListeners() {
|
@@ -517,7 +523,7 @@ class EMEController implements ComponentAPI {
|
|
517
523
|
return this.attemptKeySystemAccess(keySystemsToAttempt);
|
518
524
|
}
|
519
525
|
|
520
|
-
private
|
526
|
+
private _onMediaEncrypted(event: MediaEncryptedEvent) {
|
521
527
|
const { initDataType, initData } = event;
|
522
528
|
this.debug(`"${event.type}" event: init data type: "${initDataType}"`);
|
523
529
|
|
@@ -633,11 +639,11 @@ class EMEController implements ComponentAPI {
|
|
633
639
|
);
|
634
640
|
}
|
635
641
|
keySessionContextPromise.catch((error) => this.handleError(error));
|
636
|
-
}
|
642
|
+
}
|
637
643
|
|
638
|
-
private
|
644
|
+
private _onWaitingForKey(event: Event) {
|
639
645
|
this.log(`"${event.type}" event`);
|
640
|
-
}
|
646
|
+
}
|
641
647
|
|
642
648
|
private attemptSetMediaKeys(
|
643
649
|
keySystem: KeySystems,
|
@@ -19,6 +19,7 @@ export default class LatencyController implements ComponentAPI {
|
|
19
19
|
private currentTime: number = 0;
|
20
20
|
private stallCount: number = 0;
|
21
21
|
private _latency: number | null = null;
|
22
|
+
private timeupdateHandler = () => this.timeupdate();
|
22
23
|
|
23
24
|
constructor(hls: Hls) {
|
24
25
|
this.hls = hls;
|
@@ -125,7 +126,7 @@ export default class LatencyController implements ComponentAPI {
|
|
125
126
|
this.onMediaDetaching();
|
126
127
|
this.levelDetails = null;
|
127
128
|
// @ts-ignore
|
128
|
-
this.hls = this.
|
129
|
+
this.hls = this.timeupdateHandler = null;
|
129
130
|
}
|
130
131
|
|
131
132
|
private registerListeners() {
|
@@ -149,12 +150,12 @@ export default class LatencyController implements ComponentAPI {
|
|
149
150
|
data: MediaAttachingData,
|
150
151
|
) {
|
151
152
|
this.media = data.media;
|
152
|
-
this.media.addEventListener('timeupdate', this.
|
153
|
+
this.media.addEventListener('timeupdate', this.timeupdateHandler);
|
153
154
|
}
|
154
155
|
|
155
156
|
private onMediaDetaching() {
|
156
157
|
if (this.media) {
|
157
|
-
this.media.removeEventListener('timeupdate', this.
|
158
|
+
this.media.removeEventListener('timeupdate', this.timeupdateHandler);
|
158
159
|
this.media = null;
|
159
160
|
}
|
160
161
|
}
|
@@ -171,10 +172,10 @@ export default class LatencyController implements ComponentAPI {
|
|
171
172
|
) {
|
172
173
|
this.levelDetails = details;
|
173
174
|
if (details.advanced) {
|
174
|
-
this.
|
175
|
+
this.timeupdate();
|
175
176
|
}
|
176
177
|
if (!details.live && this.media) {
|
177
|
-
this.media.removeEventListener('timeupdate', this.
|
178
|
+
this.media.removeEventListener('timeupdate', this.timeupdateHandler);
|
178
179
|
}
|
179
180
|
}
|
180
181
|
|
@@ -190,7 +191,7 @@ export default class LatencyController implements ComponentAPI {
|
|
190
191
|
}
|
191
192
|
}
|
192
193
|
|
193
|
-
private
|
194
|
+
private timeupdate() {
|
194
195
|
const { media, levelDetails } = this;
|
195
196
|
if (!media || !levelDetails) {
|
196
197
|
return;
|
@@ -241,7 +242,7 @@ export default class LatencyController implements ComponentAPI {
|
|
241
242
|
} else if (media.playbackRate !== 1 && media.playbackRate !== 0) {
|
242
243
|
media.playbackRate = 1;
|
243
244
|
}
|
244
|
-
}
|
245
|
+
}
|
245
246
|
|
246
247
|
private estimateLiveEdge(): number | null {
|
247
248
|
const { levelDetails } = this;
|
@@ -16,7 +16,6 @@ import {
|
|
16
16
|
codecsSetSelectionPreferenceValue,
|
17
17
|
convertAVC1ToAVCOTI,
|
18
18
|
getCodecCompatibleName,
|
19
|
-
getM2TSSupportedAudioTypes,
|
20
19
|
videoCodecPreferenceValue,
|
21
20
|
} from '../utils/codecs';
|
22
21
|
import BasePlaylistController from './base-playlist-controller';
|
@@ -28,6 +27,8 @@ import type Hls from '../hls';
|
|
28
27
|
import type { HlsUrlParameters, LevelParsed } from '../types/level';
|
29
28
|
import type { MediaPlaylist } from '../types/media-playlist';
|
30
29
|
|
30
|
+
let chromeOrFirefox: boolean;
|
31
|
+
|
31
32
|
export default class LevelController extends BasePlaylistController {
|
32
33
|
private _levels: Level[] = [];
|
33
34
|
private _firstLevel: number = -1;
|
@@ -118,12 +119,22 @@ export default class LevelController extends BasePlaylistController {
|
|
118
119
|
|
119
120
|
data.levels.forEach((levelParsed: LevelParsed) => {
|
120
121
|
const attributes = levelParsed.attrs;
|
122
|
+
|
123
|
+
// erase audio codec info if browser does not support mp4a.40.34.
|
124
|
+
// demuxer will autodetect codec and fallback to mpeg/audio
|
121
125
|
let { audioCodec, videoCodec } = levelParsed;
|
126
|
+
if (audioCodec?.indexOf('mp4a.40.34') !== -1) {
|
127
|
+
chromeOrFirefox ||= /chrome|firefox/i.test(navigator.userAgent);
|
128
|
+
if (chromeOrFirefox) {
|
129
|
+
levelParsed.audioCodec = audioCodec = undefined;
|
130
|
+
}
|
131
|
+
}
|
132
|
+
|
122
133
|
if (audioCodec) {
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
134
|
+
levelParsed.audioCodec = audioCodec = getCodecCompatibleName(
|
135
|
+
audioCodec,
|
136
|
+
preferManagedMediaSource,
|
137
|
+
);
|
127
138
|
}
|
128
139
|
|
129
140
|
if (videoCodec?.indexOf('avc1') === 0) {
|
@@ -296,8 +307,8 @@ export default class LevelController extends BasePlaylistController {
|
|
296
307
|
return valueB - valueA;
|
297
308
|
}
|
298
309
|
}
|
299
|
-
if (a.
|
300
|
-
return a.
|
310
|
+
if (a.averageBitrate !== b.averageBitrate) {
|
311
|
+
return a.averageBitrate - b.averageBitrate;
|
301
312
|
}
|
302
313
|
return 0;
|
303
314
|
});
|
@@ -49,6 +49,8 @@ export default class StreamController
|
|
49
49
|
private altAudio: boolean = false;
|
50
50
|
private audioOnly: boolean = false;
|
51
51
|
private fragPlaying: Fragment | null = null;
|
52
|
+
private onvplaying: EventListener | null = null;
|
53
|
+
private onvseeked: EventListener | null = null;
|
52
54
|
private fragLastKbps: number = 0;
|
53
55
|
private couldBacktrack: boolean = false;
|
54
56
|
private backtrackFragment: Fragment | null = null;
|
@@ -115,8 +117,6 @@ export default class StreamController
|
|
115
117
|
|
116
118
|
protected onHandlerDestroying() {
|
117
119
|
this._unregisterListeners();
|
118
|
-
// @ts-ignore
|
119
|
-
this.onMediaPlaying = this.onMediaSeeked = null;
|
120
120
|
super.onHandlerDestroying();
|
121
121
|
}
|
122
122
|
|
@@ -515,8 +515,10 @@ export default class StreamController
|
|
515
515
|
) {
|
516
516
|
super.onMediaAttached(event, data);
|
517
517
|
const media = data.media;
|
518
|
-
|
519
|
-
|
518
|
+
this.onvplaying = this.onMediaPlaying.bind(this);
|
519
|
+
this.onvseeked = this.onMediaSeeked.bind(this);
|
520
|
+
media.addEventListener('playing', this.onvplaying as EventListener);
|
521
|
+
media.addEventListener('seeked', this.onvseeked as EventListener);
|
520
522
|
this.gapController = new GapController(
|
521
523
|
this.config,
|
522
524
|
media,
|
@@ -527,9 +529,10 @@ export default class StreamController
|
|
527
529
|
|
528
530
|
protected onMediaDetaching() {
|
529
531
|
const { media } = this;
|
530
|
-
if (media) {
|
531
|
-
media.removeEventListener('playing', this.
|
532
|
-
media.removeEventListener('seeked', this.
|
532
|
+
if (media && this.onvplaying && this.onvseeked) {
|
533
|
+
media.removeEventListener('playing', this.onvplaying);
|
534
|
+
media.removeEventListener('seeked', this.onvseeked);
|
535
|
+
this.onvplaying = this.onvseeked = null;
|
533
536
|
this.videoBuffer = null;
|
534
537
|
}
|
535
538
|
this.fragPlaying = null;
|
@@ -540,12 +543,12 @@ export default class StreamController
|
|
540
543
|
super.onMediaDetaching();
|
541
544
|
}
|
542
545
|
|
543
|
-
private onMediaPlaying
|
546
|
+
private onMediaPlaying() {
|
544
547
|
// tick to speed up FRAG_CHANGED triggering
|
545
548
|
this.tick();
|
546
|
-
}
|
549
|
+
}
|
547
550
|
|
548
|
-
private onMediaSeeked
|
551
|
+
private onMediaSeeked() {
|
549
552
|
const media = this.media;
|
550
553
|
const currentTime = media ? media.currentTime : null;
|
551
554
|
if (Number.isFinite(currentTime)) {
|
@@ -565,7 +568,7 @@ export default class StreamController
|
|
565
568
|
|
566
569
|
// tick to speed up FRAG_CHANGED triggering
|
567
570
|
this.tick();
|
568
|
-
}
|
571
|
+
}
|
569
572
|
|
570
573
|
private onManifestLoading() {
|
571
574
|
// reset buffer on manifest loading
|
@@ -9,10 +9,6 @@ import { PlaylistLevelType } from '../types/loader';
|
|
9
9
|
import { Level } from '../types/level';
|
10
10
|
import { subtitleOptionsIdentical } from '../utils/media-option-attributes';
|
11
11
|
import { ErrorDetails, ErrorTypes } from '../errors';
|
12
|
-
import {
|
13
|
-
isFullSegmentEncryption,
|
14
|
-
getAesModeFromFullSegmentMethod,
|
15
|
-
} from '../utils/encryption-methods-util';
|
16
12
|
import type { NetworkComponentAPI } from '../types/component-api';
|
17
13
|
import type Hls from '../hls';
|
18
14
|
import type { FragmentTracker } from './fragment-tracker';
|
@@ -364,7 +360,7 @@ export class SubtitleStreamController
|
|
364
360
|
payload.byteLength > 0 &&
|
365
361
|
decryptData?.key &&
|
366
362
|
decryptData.iv &&
|
367
|
-
|
363
|
+
decryptData.method === 'AES-128'
|
368
364
|
) {
|
369
365
|
const startTime = performance.now();
|
370
366
|
// decrypt the subtitles
|
@@ -373,7 +369,6 @@ export class SubtitleStreamController
|
|
373
369
|
new Uint8Array(payload),
|
374
370
|
decryptData.key.buffer,
|
375
371
|
decryptData.iv.buffer,
|
376
|
-
getAesModeFromFullSegmentMethod(decryptData.method),
|
377
372
|
)
|
378
373
|
.catch((err) => {
|
379
374
|
hls.trigger(Events.ERROR, {
|
@@ -35,12 +35,11 @@ class SubtitleTrackController extends BasePlaylistController {
|
|
35
35
|
private currentTrack: MediaPlaylist | null = null;
|
36
36
|
private selectDefaultTrack: boolean = true;
|
37
37
|
private queuedDefaultTrack: number = -1;
|
38
|
+
private asyncPollTrackChange: () => void = () => this.pollTrackChange(0);
|
38
39
|
private useTextTrackPolling: boolean = false;
|
39
40
|
private subtitlePollingInterval: number = -1;
|
40
41
|
private _subtitleDisplay: boolean = true;
|
41
42
|
|
42
|
-
private asyncPollTrackChange = () => this.pollTrackChange(0);
|
43
|
-
|
44
43
|
constructor(hls: Hls) {
|
45
44
|
super(hls, '[subtitle-track-controller]');
|
46
45
|
this.registerListeners();
|
@@ -51,8 +50,7 @@ class SubtitleTrackController extends BasePlaylistController {
|
|
51
50
|
this.tracks.length = 0;
|
52
51
|
this.tracksInGroup.length = 0;
|
53
52
|
this.currentTrack = null;
|
54
|
-
|
55
|
-
this.onTextTracksChanged = this.asyncPollTrackChange = null;
|
53
|
+
this.onTextTracksChanged = this.asyncPollTrackChange = null as any;
|
56
54
|
super.destroy();
|
57
55
|
}
|
58
56
|
|
@@ -136,12 +136,17 @@ export class TimelineController implements ComponentAPI {
|
|
136
136
|
}
|
137
137
|
|
138
138
|
private initCea608Parsers() {
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
139
|
+
if (
|
140
|
+
this.config.enableCEA708Captions &&
|
141
|
+
(!this.cea608Parser1 || !this.cea608Parser2)
|
142
|
+
) {
|
143
|
+
const channel1 = new OutputFilter(this, 'textTrack1');
|
144
|
+
const channel2 = new OutputFilter(this, 'textTrack2');
|
145
|
+
const channel3 = new OutputFilter(this, 'textTrack3');
|
146
|
+
const channel4 = new OutputFilter(this, 'textTrack4');
|
147
|
+
this.cea608Parser1 = new Cea608Parser(1, channel1, channel2);
|
148
|
+
this.cea608Parser2 = new Cea608Parser(3, channel3, channel4);
|
149
|
+
}
|
145
150
|
}
|
146
151
|
|
147
152
|
public addCues(
|
@@ -463,19 +468,21 @@ export class TimelineController implements ComponentAPI {
|
|
463
468
|
}
|
464
469
|
|
465
470
|
private onFragLoading(event: Events.FRAG_LOADING, data: FragLoadingData) {
|
471
|
+
this.initCea608Parsers();
|
472
|
+
const { cea608Parser1, cea608Parser2, lastCc, lastSn, lastPartIndex } =
|
473
|
+
this;
|
474
|
+
if (!this.enabled || !cea608Parser1 || !cea608Parser2) {
|
475
|
+
return;
|
476
|
+
}
|
466
477
|
// if this frag isn't contiguous, clear the parser so cues with bad start/end times aren't added to the textTrack
|
467
|
-
if (
|
468
|
-
const { cea608Parser1, cea608Parser2, lastSn } = this;
|
469
|
-
if (!cea608Parser1 || !cea608Parser2) {
|
470
|
-
return;
|
471
|
-
}
|
478
|
+
if (data.frag.type === PlaylistLevelType.MAIN) {
|
472
479
|
const { cc, sn } = data.frag;
|
473
|
-
const partIndex = data
|
480
|
+
const partIndex = data?.part?.index ?? -1;
|
474
481
|
if (
|
475
482
|
!(
|
476
483
|
sn === lastSn + 1 ||
|
477
|
-
(sn === lastSn && partIndex ===
|
478
|
-
cc ===
|
484
|
+
(sn === lastSn && partIndex === lastPartIndex + 1) ||
|
485
|
+
cc === lastCc
|
479
486
|
)
|
480
487
|
) {
|
481
488
|
cea608Parser1.reset();
|
@@ -662,7 +669,9 @@ export class TimelineController implements ComponentAPI {
|
|
662
669
|
event: Events.FRAG_PARSING_USERDATA,
|
663
670
|
data: FragParsingUserdataData,
|
664
671
|
) {
|
665
|
-
|
672
|
+
this.initCea608Parsers();
|
673
|
+
const { cea608Parser1, cea608Parser2 } = this;
|
674
|
+
if (!this.enabled || !cea608Parser1 || !cea608Parser2) {
|
666
675
|
return;
|
667
676
|
}
|
668
677
|
const { frag, samples } = data;
|
@@ -677,12 +686,9 @@ export class TimelineController implements ComponentAPI {
|
|
677
686
|
for (let i = 0; i < samples.length; i++) {
|
678
687
|
const ccBytes = samples[i].bytes;
|
679
688
|
if (ccBytes) {
|
680
|
-
if (!this.cea608Parser1) {
|
681
|
-
this.initCea608Parsers();
|
682
|
-
}
|
683
689
|
const ccdatas = this.extractCea608Data(ccBytes);
|
684
|
-
|
685
|
-
|
690
|
+
cea608Parser1.addData(samples[i].pts, ccdatas[0]);
|
691
|
+
cea608Parser2.addData(samples[i].pts, ccdatas[1]);
|
686
692
|
}
|
687
693
|
}
|
688
694
|
}
|
package/src/crypt/aes-crypto.ts
CHANGED
@@ -1,32 +1,13 @@
|
|
1
|
-
import { DecrypterAesMode } from './decrypter-aes-mode';
|
2
|
-
|
3
1
|
export default class AESCrypto {
|
4
2
|
private subtle: SubtleCrypto;
|
5
3
|
private aesIV: Uint8Array;
|
6
|
-
private aesMode: DecrypterAesMode;
|
7
4
|
|
8
|
-
constructor(subtle: SubtleCrypto, iv: Uint8Array
|
5
|
+
constructor(subtle: SubtleCrypto, iv: Uint8Array) {
|
9
6
|
this.subtle = subtle;
|
10
7
|
this.aesIV = iv;
|
11
|
-
this.aesMode = aesMode;
|
12
8
|
}
|
13
9
|
|
14
10
|
decrypt(data: ArrayBuffer, key: CryptoKey) {
|
15
|
-
|
16
|
-
case DecrypterAesMode.cbc:
|
17
|
-
return this.subtle.decrypt(
|
18
|
-
{ name: 'AES-CBC', iv: this.aesIV },
|
19
|
-
key,
|
20
|
-
data,
|
21
|
-
);
|
22
|
-
case DecrypterAesMode.ctr:
|
23
|
-
return this.subtle.decrypt(
|
24
|
-
{ name: 'AES-CTR', counter: this.aesIV, length: 64 }, //64 : NIST SP800-38A standard suggests that the counter should occupy half of the counter block
|
25
|
-
key,
|
26
|
-
data,
|
27
|
-
);
|
28
|
-
default:
|
29
|
-
throw new Error(`[AESCrypto] invalid aes mode ${this.aesMode}`);
|
30
|
-
}
|
11
|
+
return this.subtle.decrypt({ name: 'AES-CBC', iv: this.aesIV }, key, data);
|
31
12
|
}
|
32
13
|
}
|