hls.js 1.5.6-0.canary.10011 → 1.5.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/README.md +0 -1
- package/dist/hls-demo.js +0 -10
- package/dist/hls-demo.js.map +1 -1
- package/dist/hls.js +1134 -2043
- package/dist/hls.js.d.ts +50 -65
- package/dist/hls.js.map +1 -1
- package/dist/hls.light.js +852 -1141
- 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 +686 -974
- 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 +847 -1741
- 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 +21 -21
- package/src/config.ts +2 -3
- package/src/controller/abr-controller.ts +20 -21
- package/src/controller/audio-stream-controller.ts +16 -15
- package/src/controller/audio-track-controller.ts +1 -1
- package/src/controller/base-playlist-controller.ts +8 -20
- package/src/controller/base-stream-controller.ts +33 -149
- package/src/controller/buffer-controller.ts +11 -11
- package/src/controller/cap-level-controller.ts +2 -1
- package/src/controller/cmcd-controller.ts +6 -27
- package/src/controller/content-steering-controller.ts +6 -8
- package/src/controller/eme-controller.ts +22 -9
- package/src/controller/error-controller.ts +8 -6
- package/src/controller/fps-controller.ts +3 -2
- package/src/controller/gap-controller.ts +16 -43
- package/src/controller/latency-controller.ts +11 -9
- package/src/controller/level-controller.ts +18 -12
- package/src/controller/stream-controller.ts +32 -25
- package/src/controller/subtitle-stream-controller.ts +14 -13
- package/src/controller/subtitle-track-controller.ts +3 -5
- package/src/controller/timeline-controller.ts +30 -23
- 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-worker.ts +4 -4
- package/src/demux/transmuxer.ts +3 -16
- package/src/demux/tsdemuxer.ts +37 -71
- package/src/demux/video/avc-video-parser.ts +119 -208
- package/src/demux/video/base-video-parser.ts +2 -134
- package/src/demux/video/exp-golomb.ts +208 -0
- package/src/events.ts +0 -7
- package/src/hls.ts +34 -42
- 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/loader/playlist-loader.ts +5 -4
- package/src/remux/mp4-generator.ts +1 -196
- package/src/remux/mp4-remuxer.ts +7 -23
- package/src/task-loop.ts +2 -5
- package/src/types/component-api.ts +0 -2
- package/src/types/demuxer.ts +0 -3
- package/src/types/events.ts +0 -4
- package/src/utils/codecs.ts +4 -33
- package/src/utils/logger.ts +24 -54
- package/src/crypt/decrypter-aes-mode.ts +0 -4
- package/src/demux/video/hevc-video-parser.ts +0 -746
- package/src/utils/encryption-methods-util.ts +0 -21
@@ -1,4 +1,5 @@
|
|
1
1
|
import { Events } from '../events';
|
2
|
+
import { logger } from '../utils/logger';
|
2
3
|
import type { ComponentAPI } from '../types/component-api';
|
3
4
|
import type Hls from '../hls';
|
4
5
|
import type { MediaAttachingData } from '../types/events';
|
@@ -83,13 +84,13 @@ class FPSController implements ComponentAPI {
|
|
83
84
|
totalDroppedFrames: droppedFrames,
|
84
85
|
});
|
85
86
|
if (droppedFPS > 0) {
|
86
|
-
//
|
87
|
+
// logger.log('checkFPS : droppedFPS/decodedFPS:' + droppedFPS/(1000 * currentDecoded / currentPeriod));
|
87
88
|
if (
|
88
89
|
currentDropped >
|
89
90
|
hls.config.fpsDroppedMonitoringThreshold * currentDecoded
|
90
91
|
) {
|
91
92
|
let currentLevel = hls.currentLevel;
|
92
|
-
|
93
|
+
logger.warn(
|
93
94
|
'drop FPS ratio greater than max allowed value for currentLevel: ' +
|
94
95
|
currentLevel,
|
95
96
|
);
|
@@ -1,22 +1,20 @@
|
|
1
|
-
import {
|
1
|
+
import type { BufferInfo } from '../utils/buffer-helper';
|
2
2
|
import { BufferHelper } from '../utils/buffer-helper';
|
3
3
|
import { ErrorTypes, ErrorDetails } from '../errors';
|
4
4
|
import { PlaylistLevelType } from '../types/loader';
|
5
5
|
import { Events } from '../events';
|
6
|
-
import {
|
6
|
+
import { logger } from '../utils/logger';
|
7
7
|
import type Hls from '../hls';
|
8
|
-
import type { BufferInfo } from '../utils/buffer-helper';
|
9
8
|
import type { HlsConfig } from '../config';
|
10
9
|
import type { Fragment } from '../loader/fragment';
|
11
10
|
import type { FragmentTracker } from './fragment-tracker';
|
12
|
-
import type { LevelDetails } from '../loader/level-details';
|
13
11
|
|
14
12
|
export const STALL_MINIMUM_DURATION_MS = 250;
|
15
13
|
export const MAX_START_GAP_JUMP = 2.0;
|
16
14
|
export const SKIP_BUFFER_HOLE_STEP_SECONDS = 0.1;
|
17
15
|
export const SKIP_BUFFER_RANGE_START = 0.05;
|
18
16
|
|
19
|
-
export default class GapController
|
17
|
+
export default class GapController {
|
20
18
|
private config: HlsConfig;
|
21
19
|
private media: HTMLMediaElement | null = null;
|
22
20
|
private fragmentTracker: FragmentTracker;
|
@@ -26,15 +24,8 @@ export default class GapController extends Logger {
|
|
26
24
|
private stalled: number | null = null;
|
27
25
|
private moved: boolean = false;
|
28
26
|
private seeking: boolean = false;
|
29
|
-
private ended: number = 0;
|
30
27
|
|
31
|
-
constructor(
|
32
|
-
config: HlsConfig,
|
33
|
-
media: HTMLMediaElement,
|
34
|
-
fragmentTracker: FragmentTracker,
|
35
|
-
hls: Hls,
|
36
|
-
) {
|
37
|
-
super('gap-controller', hls.logger);
|
28
|
+
constructor(config, media, fragmentTracker, hls) {
|
38
29
|
this.config = config;
|
39
30
|
this.media = media;
|
40
31
|
this.fragmentTracker = fragmentTracker;
|
@@ -53,12 +44,7 @@ export default class GapController extends Logger {
|
|
53
44
|
*
|
54
45
|
* @param lastCurrentTime - Previously read playhead position
|
55
46
|
*/
|
56
|
-
public poll(
|
57
|
-
lastCurrentTime: number,
|
58
|
-
activeFrag: Fragment | null,
|
59
|
-
levelDetails: LevelDetails | undefined,
|
60
|
-
state: string,
|
61
|
-
) {
|
47
|
+
public poll(lastCurrentTime: number, activeFrag: Fragment | null) {
|
62
48
|
const { config, media, stalled } = this;
|
63
49
|
if (media === null) {
|
64
50
|
return;
|
@@ -71,7 +57,6 @@ export default class GapController extends Logger {
|
|
71
57
|
|
72
58
|
// The playhead is moving, no-op
|
73
59
|
if (currentTime !== lastCurrentTime) {
|
74
|
-
this.ended = 0;
|
75
60
|
this.moved = true;
|
76
61
|
if (!seeking) {
|
77
62
|
this.nudgeRetry = 0;
|
@@ -80,7 +65,7 @@ export default class GapController extends Logger {
|
|
80
65
|
// The playhead is now moving, but was previously stalled
|
81
66
|
if (this.stallReported) {
|
82
67
|
const stalledDuration = self.performance.now() - stalled;
|
83
|
-
|
68
|
+
logger.warn(
|
84
69
|
`playback not stuck anymore @${currentTime}, after ${Math.round(
|
85
70
|
stalledDuration,
|
86
71
|
)}ms`,
|
@@ -143,9 +128,12 @@ export default class GapController extends Logger {
|
|
143
128
|
// When joining a live stream with audio tracks, account for live playlist window sliding by allowing
|
144
129
|
// a larger jump over start gaps caused by the audio-stream-controller buffering a start fragment
|
145
130
|
// that begins over 1 target duration after the video start position.
|
146
|
-
const
|
131
|
+
const level = this.hls.levels
|
132
|
+
? this.hls.levels[this.hls.currentLevel]
|
133
|
+
: null;
|
134
|
+
const isLive = level?.details?.live;
|
147
135
|
const maxStartGapJump = isLive
|
148
|
-
?
|
136
|
+
? level!.details!.targetduration * 2
|
149
137
|
: MAX_START_GAP_JUMP;
|
150
138
|
const partialOrGap = this.fragmentTracker.getPartialFragment(currentTime);
|
151
139
|
if (startJump > 0 && (startJump <= maxStartGapJump || partialOrGap)) {
|
@@ -165,21 +153,6 @@ export default class GapController extends Logger {
|
|
165
153
|
|
166
154
|
const stalledDuration = tnow - stalled;
|
167
155
|
if (!seeking && stalledDuration >= STALL_MINIMUM_DURATION_MS) {
|
168
|
-
// Dispatch MEDIA_ENDED when media.ended/ended event is not signalled at end of stream
|
169
|
-
if (
|
170
|
-
state === State.ENDED &&
|
171
|
-
!(levelDetails && levelDetails.live) &&
|
172
|
-
Math.abs(currentTime - (levelDetails?.edge || 0)) < 1
|
173
|
-
) {
|
174
|
-
if (stalledDuration < 1000 || this.ended) {
|
175
|
-
return;
|
176
|
-
}
|
177
|
-
this.ended = currentTime;
|
178
|
-
this.hls.trigger(Events.MEDIA_ENDED, {
|
179
|
-
stalled: true,
|
180
|
-
});
|
181
|
-
return;
|
182
|
-
}
|
183
156
|
// Report stalling after trying to fix
|
184
157
|
this._reportStall(bufferInfo);
|
185
158
|
if (!this.media) {
|
@@ -233,7 +206,7 @@ export default class GapController extends Logger {
|
|
233
206
|
bufferInfo.nextStart - currentTime < config.maxBufferHole)) &&
|
234
207
|
stalledDurationMs > config.highBufferWatchdogPeriod * 1000
|
235
208
|
) {
|
236
|
-
|
209
|
+
logger.warn('Trying to nudge playhead over buffer-hole');
|
237
210
|
// Try to nudge currentTime over a buffer hole if we've been stalling for the configured amount of seconds
|
238
211
|
// We only try to jump the hole if it's under the configured size
|
239
212
|
// Reset stalled so to rearm watchdog timer
|
@@ -257,7 +230,7 @@ export default class GapController extends Logger {
|
|
257
230
|
media.currentTime
|
258
231
|
} due to low buffer (${JSON.stringify(bufferInfo)})`,
|
259
232
|
);
|
260
|
-
|
233
|
+
logger.warn(error.message);
|
261
234
|
hls.trigger(Events.ERROR, {
|
262
235
|
type: ErrorTypes.MEDIA_ERROR,
|
263
236
|
details: ErrorDetails.BUFFER_STALLED_ERROR,
|
@@ -332,7 +305,7 @@ export default class GapController extends Logger {
|
|
332
305
|
startTime + SKIP_BUFFER_RANGE_START,
|
333
306
|
currentTime + SKIP_BUFFER_HOLE_STEP_SECONDS,
|
334
307
|
);
|
335
|
-
|
308
|
+
logger.warn(
|
336
309
|
`skipping hole, adjusting currentTime from ${currentTime} to ${targetTime}`,
|
337
310
|
);
|
338
311
|
this.moved = true;
|
@@ -375,7 +348,7 @@ export default class GapController extends Logger {
|
|
375
348
|
const error = new Error(
|
376
349
|
`Nudging 'currentTime' from ${currentTime} to ${targetTime}`,
|
377
350
|
);
|
378
|
-
|
351
|
+
logger.warn(error.message);
|
379
352
|
media.currentTime = targetTime;
|
380
353
|
hls.trigger(Events.ERROR, {
|
381
354
|
type: ErrorTypes.MEDIA_ERROR,
|
@@ -387,7 +360,7 @@ export default class GapController extends Logger {
|
|
387
360
|
const error = new Error(
|
388
361
|
`Playhead still not moving while enough data buffered @${currentTime} after ${config.nudgeMaxRetry} nudges`,
|
389
362
|
);
|
390
|
-
|
363
|
+
logger.error(error.message);
|
391
364
|
hls.trigger(Events.ERROR, {
|
392
365
|
type: ErrorTypes.MEDIA_ERROR,
|
393
366
|
details: ErrorDetails.BUFFER_STALLED_ERROR,
|
@@ -6,6 +6,7 @@ import type {
|
|
6
6
|
LevelUpdatedData,
|
7
7
|
MediaAttachingData,
|
8
8
|
} from '../types/events';
|
9
|
+
import { logger } from '../utils/logger';
|
9
10
|
import type { ComponentAPI } from '../types/component-api';
|
10
11
|
import type Hls from '../hls';
|
11
12
|
import type { HlsConfig } from '../config';
|
@@ -18,6 +19,7 @@ export default class LatencyController implements ComponentAPI {
|
|
18
19
|
private currentTime: number = 0;
|
19
20
|
private stallCount: number = 0;
|
20
21
|
private _latency: number | null = null;
|
22
|
+
private timeupdateHandler = () => this.timeupdate();
|
21
23
|
|
22
24
|
constructor(hls: Hls) {
|
23
25
|
this.hls = hls;
|
@@ -124,7 +126,7 @@ export default class LatencyController implements ComponentAPI {
|
|
124
126
|
this.onMediaDetaching();
|
125
127
|
this.levelDetails = null;
|
126
128
|
// @ts-ignore
|
127
|
-
this.hls = null;
|
129
|
+
this.hls = this.timeupdateHandler = null;
|
128
130
|
}
|
129
131
|
|
130
132
|
private registerListeners() {
|
@@ -148,12 +150,12 @@ export default class LatencyController implements ComponentAPI {
|
|
148
150
|
data: MediaAttachingData,
|
149
151
|
) {
|
150
152
|
this.media = data.media;
|
151
|
-
this.media.addEventListener('timeupdate', this.
|
153
|
+
this.media.addEventListener('timeupdate', this.timeupdateHandler);
|
152
154
|
}
|
153
155
|
|
154
156
|
private onMediaDetaching() {
|
155
157
|
if (this.media) {
|
156
|
-
this.media.removeEventListener('timeupdate', this.
|
158
|
+
this.media.removeEventListener('timeupdate', this.timeupdateHandler);
|
157
159
|
this.media = null;
|
158
160
|
}
|
159
161
|
}
|
@@ -170,10 +172,10 @@ export default class LatencyController implements ComponentAPI {
|
|
170
172
|
) {
|
171
173
|
this.levelDetails = details;
|
172
174
|
if (details.advanced) {
|
173
|
-
this.
|
175
|
+
this.timeupdate();
|
174
176
|
}
|
175
177
|
if (!details.live && this.media) {
|
176
|
-
this.media.removeEventListener('timeupdate', this.
|
178
|
+
this.media.removeEventListener('timeupdate', this.timeupdateHandler);
|
177
179
|
}
|
178
180
|
}
|
179
181
|
|
@@ -183,13 +185,13 @@ export default class LatencyController implements ComponentAPI {
|
|
183
185
|
}
|
184
186
|
this.stallCount++;
|
185
187
|
if (this.levelDetails?.live) {
|
186
|
-
|
187
|
-
'[
|
188
|
+
logger.warn(
|
189
|
+
'[playback-rate-controller]: Stall detected, adjusting target latency',
|
188
190
|
);
|
189
191
|
}
|
190
192
|
}
|
191
193
|
|
192
|
-
private
|
194
|
+
private timeupdate() {
|
193
195
|
const { media, levelDetails } = this;
|
194
196
|
if (!media || !levelDetails) {
|
195
197
|
return;
|
@@ -240,7 +242,7 @@ export default class LatencyController implements ComponentAPI {
|
|
240
242
|
} else if (media.playbackRate !== 1 && media.playbackRate !== 0) {
|
241
243
|
media.playbackRate = 1;
|
242
244
|
}
|
243
|
-
}
|
245
|
+
}
|
244
246
|
|
245
247
|
private estimateLiveEdge(): number | null {
|
246
248
|
const { levelDetails } = this;
|
@@ -27,6 +27,8 @@ import type Hls from '../hls';
|
|
27
27
|
import type { HlsUrlParameters, LevelParsed } from '../types/level';
|
28
28
|
import type { MediaPlaylist } from '../types/media-playlist';
|
29
29
|
|
30
|
+
let chromeOrFirefox: boolean;
|
31
|
+
|
30
32
|
export default class LevelController extends BasePlaylistController {
|
31
33
|
private _levels: Level[] = [];
|
32
34
|
private _firstLevel: number = -1;
|
@@ -43,7 +45,7 @@ export default class LevelController extends BasePlaylistController {
|
|
43
45
|
hls: Hls,
|
44
46
|
contentSteeringController: ContentSteeringController | null,
|
45
47
|
) {
|
46
|
-
super(hls, 'level-controller');
|
48
|
+
super(hls, '[level-controller]');
|
47
49
|
this.steering = contentSteeringController;
|
48
50
|
this._registerListeners();
|
49
51
|
}
|
@@ -117,12 +119,22 @@ export default class LevelController extends BasePlaylistController {
|
|
117
119
|
|
118
120
|
data.levels.forEach((levelParsed: LevelParsed) => {
|
119
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
|
120
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
|
+
|
121
133
|
if (audioCodec) {
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
134
|
+
levelParsed.audioCodec = audioCodec = getCodecCompatibleName(
|
135
|
+
audioCodec,
|
136
|
+
preferManagedMediaSource,
|
137
|
+
);
|
126
138
|
}
|
127
139
|
|
128
140
|
if (videoCodec?.indexOf('avc1') === 0) {
|
@@ -556,13 +568,7 @@ export default class LevelController extends BasePlaylistController {
|
|
556
568
|
if (curLevel.fragmentError === 0) {
|
557
569
|
curLevel.loadError = 0;
|
558
570
|
}
|
559
|
-
|
560
|
-
let previousDetails = curLevel.details;
|
561
|
-
if (previousDetails === data.details && previousDetails.advanced) {
|
562
|
-
previousDetails = undefined;
|
563
|
-
}
|
564
|
-
|
565
|
-
this.playlistLoaded(level, data, previousDetails);
|
571
|
+
this.playlistLoaded(level, data, curLevel.details);
|
566
572
|
} else if (data.deliveryDirectives?.skip) {
|
567
573
|
// received a delta playlist update that cannot be merged
|
568
574
|
details.deltaUpdateFailed = true;
|
@@ -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;
|
@@ -64,15 +66,17 @@ export default class StreamController
|
|
64
66
|
hls,
|
65
67
|
fragmentTracker,
|
66
68
|
keyLoader,
|
67
|
-
'stream-controller',
|
69
|
+
'[stream-controller]',
|
68
70
|
PlaylistLevelType.MAIN,
|
69
71
|
);
|
70
|
-
this.
|
72
|
+
this._registerListeners();
|
71
73
|
}
|
72
74
|
|
73
|
-
|
74
|
-
super.registerListeners();
|
75
|
+
private _registerListeners() {
|
75
76
|
const { hls } = this;
|
77
|
+
hls.on(Events.MEDIA_ATTACHED, this.onMediaAttached, this);
|
78
|
+
hls.on(Events.MEDIA_DETACHING, this.onMediaDetaching, this);
|
79
|
+
hls.on(Events.MANIFEST_LOADING, this.onManifestLoading, this);
|
76
80
|
hls.on(Events.MANIFEST_PARSED, this.onManifestParsed, this);
|
77
81
|
hls.on(Events.LEVEL_LOADING, this.onLevelLoading, this);
|
78
82
|
hls.on(Events.LEVEL_LOADED, this.onLevelLoaded, this);
|
@@ -81,6 +85,7 @@ export default class StreamController
|
|
81
85
|
this.onFragLoadEmergencyAborted,
|
82
86
|
this,
|
83
87
|
);
|
88
|
+
hls.on(Events.ERROR, this.onError, this);
|
84
89
|
hls.on(Events.AUDIO_TRACK_SWITCHING, this.onAudioTrackSwitching, this);
|
85
90
|
hls.on(Events.AUDIO_TRACK_SWITCHED, this.onAudioTrackSwitched, this);
|
86
91
|
hls.on(Events.BUFFER_CREATED, this.onBufferCreated, this);
|
@@ -89,9 +94,11 @@ export default class StreamController
|
|
89
94
|
hls.on(Events.FRAG_BUFFERED, this.onFragBuffered, this);
|
90
95
|
}
|
91
96
|
|
92
|
-
protected
|
93
|
-
super.unregisterListeners();
|
97
|
+
protected _unregisterListeners() {
|
94
98
|
const { hls } = this;
|
99
|
+
hls.off(Events.MEDIA_ATTACHED, this.onMediaAttached, this);
|
100
|
+
hls.off(Events.MEDIA_DETACHING, this.onMediaDetaching, this);
|
101
|
+
hls.off(Events.MANIFEST_LOADING, this.onManifestLoading, this);
|
95
102
|
hls.off(Events.MANIFEST_PARSED, this.onManifestParsed, this);
|
96
103
|
hls.off(Events.LEVEL_LOADED, this.onLevelLoaded, this);
|
97
104
|
hls.off(
|
@@ -99,6 +106,7 @@ export default class StreamController
|
|
99
106
|
this.onFragLoadEmergencyAborted,
|
100
107
|
this,
|
101
108
|
);
|
109
|
+
hls.off(Events.ERROR, this.onError, this);
|
102
110
|
hls.off(Events.AUDIO_TRACK_SWITCHING, this.onAudioTrackSwitching, this);
|
103
111
|
hls.off(Events.AUDIO_TRACK_SWITCHED, this.onAudioTrackSwitched, this);
|
104
112
|
hls.off(Events.BUFFER_CREATED, this.onBufferCreated, this);
|
@@ -108,9 +116,7 @@ export default class StreamController
|
|
108
116
|
}
|
109
117
|
|
110
118
|
protected onHandlerDestroying() {
|
111
|
-
|
112
|
-
this.onMediaPlaying = this.onMediaSeeked = null;
|
113
|
-
this.unregisterListeners();
|
119
|
+
this._unregisterListeners();
|
114
120
|
super.onHandlerDestroying();
|
115
121
|
}
|
116
122
|
|
@@ -232,7 +238,7 @@ export default class StreamController
|
|
232
238
|
}
|
233
239
|
|
234
240
|
const level = hls.nextLoadLevel;
|
235
|
-
if (!
|
241
|
+
if (!levels?.[level]) {
|
236
242
|
return;
|
237
243
|
}
|
238
244
|
|
@@ -510,8 +516,10 @@ export default class StreamController
|
|
510
516
|
) {
|
511
517
|
super.onMediaAttached(event, data);
|
512
518
|
const media = data.media;
|
513
|
-
|
514
|
-
|
519
|
+
this.onvplaying = this.onMediaPlaying.bind(this);
|
520
|
+
this.onvseeked = this.onMediaSeeked.bind(this);
|
521
|
+
media.addEventListener('playing', this.onvplaying as EventListener);
|
522
|
+
media.addEventListener('seeked', this.onvseeked as EventListener);
|
515
523
|
this.gapController = new GapController(
|
516
524
|
this.config,
|
517
525
|
media,
|
@@ -522,9 +530,10 @@ export default class StreamController
|
|
522
530
|
|
523
531
|
protected onMediaDetaching() {
|
524
532
|
const { media } = this;
|
525
|
-
if (media) {
|
526
|
-
media.removeEventListener('playing', this.
|
527
|
-
media.removeEventListener('seeked', this.
|
533
|
+
if (media && this.onvplaying && this.onvseeked) {
|
534
|
+
media.removeEventListener('playing', this.onvplaying);
|
535
|
+
media.removeEventListener('seeked', this.onvseeked);
|
536
|
+
this.onvplaying = this.onvseeked = null;
|
528
537
|
this.videoBuffer = null;
|
529
538
|
}
|
530
539
|
this.fragPlaying = null;
|
@@ -535,12 +544,12 @@ export default class StreamController
|
|
535
544
|
super.onMediaDetaching();
|
536
545
|
}
|
537
546
|
|
538
|
-
private onMediaPlaying
|
547
|
+
private onMediaPlaying() {
|
539
548
|
// tick to speed up FRAG_CHANGED triggering
|
540
549
|
this.tick();
|
541
|
-
}
|
550
|
+
}
|
542
551
|
|
543
|
-
private onMediaSeeked
|
552
|
+
private onMediaSeeked() {
|
544
553
|
const media = this.media;
|
545
554
|
const currentTime = media ? media.currentTime : null;
|
546
555
|
if (Number.isFinite(currentTime)) {
|
@@ -560,9 +569,9 @@ export default class StreamController
|
|
560
569
|
|
561
570
|
// tick to speed up FRAG_CHANGED triggering
|
562
571
|
this.tick();
|
563
|
-
}
|
572
|
+
}
|
564
573
|
|
565
|
-
|
574
|
+
private onManifestLoading() {
|
566
575
|
// reset buffer on manifest loading
|
567
576
|
this.log('Trigger BUFFER_RESET');
|
568
577
|
this.hls.trigger(Events.BUFFER_RESET, undefined);
|
@@ -875,7 +884,7 @@ export default class StreamController
|
|
875
884
|
this.fragBufferedComplete(frag, part);
|
876
885
|
}
|
877
886
|
|
878
|
-
|
887
|
+
private onError(event: Events.ERROR, data: ErrorData) {
|
879
888
|
if (data.fatal) {
|
880
889
|
this.state = State.ERROR;
|
881
890
|
return;
|
@@ -933,10 +942,8 @@ export default class StreamController
|
|
933
942
|
|
934
943
|
if (this.loadedmetadata || !BufferHelper.getBuffered(media).length) {
|
935
944
|
// Resolve gaps using the main buffer, whose ranges are the intersections of the A/V sourcebuffers
|
936
|
-
const
|
937
|
-
|
938
|
-
const levelDetails = this.getLevelDetails();
|
939
|
-
gapController.poll(this.lastCurrentTime, activeFrag, levelDetails, state);
|
945
|
+
const activeFrag = this.state !== State.IDLE ? this.fragCurrent : null;
|
946
|
+
gapController.poll(this.lastCurrentTime, activeFrag);
|
940
947
|
}
|
941
948
|
|
942
949
|
this.lastCurrentTime = media.currentTime;
|
@@ -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';
|
@@ -55,22 +51,25 @@ export class SubtitleStreamController
|
|
55
51
|
hls,
|
56
52
|
fragmentTracker,
|
57
53
|
keyLoader,
|
58
|
-
'subtitle-stream-controller',
|
54
|
+
'[subtitle-stream-controller]',
|
59
55
|
PlaylistLevelType.SUBTITLE,
|
60
56
|
);
|
61
|
-
this.
|
57
|
+
this._registerListeners();
|
62
58
|
}
|
63
59
|
|
64
60
|
protected onHandlerDestroying() {
|
65
|
-
this.
|
61
|
+
this._unregisterListeners();
|
66
62
|
super.onHandlerDestroying();
|
67
63
|
this.mainDetails = null;
|
68
64
|
}
|
69
65
|
|
70
|
-
|
71
|
-
super.registerListeners();
|
66
|
+
private _registerListeners() {
|
72
67
|
const { hls } = this;
|
68
|
+
hls.on(Events.MEDIA_ATTACHED, this.onMediaAttached, this);
|
69
|
+
hls.on(Events.MEDIA_DETACHING, this.onMediaDetaching, this);
|
70
|
+
hls.on(Events.MANIFEST_LOADING, this.onManifestLoading, this);
|
73
71
|
hls.on(Events.LEVEL_LOADED, this.onLevelLoaded, this);
|
72
|
+
hls.on(Events.ERROR, this.onError, this);
|
74
73
|
hls.on(Events.SUBTITLE_TRACKS_UPDATED, this.onSubtitleTracksUpdated, this);
|
75
74
|
hls.on(Events.SUBTITLE_TRACK_SWITCH, this.onSubtitleTrackSwitch, this);
|
76
75
|
hls.on(Events.SUBTITLE_TRACK_LOADED, this.onSubtitleTrackLoaded, this);
|
@@ -79,10 +78,13 @@ export class SubtitleStreamController
|
|
79
78
|
hls.on(Events.FRAG_BUFFERED, this.onFragBuffered, this);
|
80
79
|
}
|
81
80
|
|
82
|
-
|
83
|
-
super.unregisterListeners();
|
81
|
+
private _unregisterListeners() {
|
84
82
|
const { hls } = this;
|
83
|
+
hls.off(Events.MEDIA_ATTACHED, this.onMediaAttached, this);
|
84
|
+
hls.off(Events.MEDIA_DETACHING, this.onMediaDetaching, this);
|
85
|
+
hls.off(Events.MANIFEST_LOADING, this.onManifestLoading, this);
|
85
86
|
hls.off(Events.LEVEL_LOADED, this.onLevelLoaded, this);
|
87
|
+
hls.off(Events.ERROR, this.onError, this);
|
86
88
|
hls.off(Events.SUBTITLE_TRACKS_UPDATED, this.onSubtitleTracksUpdated, this);
|
87
89
|
hls.off(Events.SUBTITLE_TRACK_SWITCH, this.onSubtitleTrackSwitch, this);
|
88
90
|
hls.off(Events.SUBTITLE_TRACK_LOADED, this.onSubtitleTrackLoaded, this);
|
@@ -358,7 +360,7 @@ export class SubtitleStreamController
|
|
358
360
|
payload.byteLength > 0 &&
|
359
361
|
decryptData?.key &&
|
360
362
|
decryptData.iv &&
|
361
|
-
|
363
|
+
decryptData.method === 'AES-128'
|
362
364
|
) {
|
363
365
|
const startTime = performance.now();
|
364
366
|
// decrypt the subtitles
|
@@ -367,7 +369,6 @@ export class SubtitleStreamController
|
|
367
369
|
new Uint8Array(payload),
|
368
370
|
decryptData.key.buffer,
|
369
371
|
decryptData.iv.buffer,
|
370
|
-
getAesModeFromFullSegmentMethod(decryptData.method),
|
371
372
|
)
|
372
373
|
.catch((err) => {
|
373
374
|
hls.trigger(Events.ERROR, {
|
@@ -35,14 +35,13 @@ 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
|
-
super(hls, 'subtitle-track-controller');
|
44
|
+
super(hls, '[subtitle-track-controller]');
|
46
45
|
this.registerListeners();
|
47
46
|
}
|
48
47
|
|
@@ -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
|
|