hls.js 1.5.12 → 1.5.13-0.canary.10401
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 +4 -3
- package/dist/hls-demo.js +41 -38
- package/dist/hls-demo.js.map +1 -1
- package/dist/hls.js +4174 -2625
- package/dist/hls.js.d.ts +173 -108
- package/dist/hls.js.map +1 -1
- package/dist/hls.light.js +2851 -1914
- 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 +2560 -1608
- 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 +3546 -1982
- 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 +38 -38
- package/src/config.ts +5 -2
- package/src/controller/abr-controller.ts +39 -25
- package/src/controller/audio-stream-controller.ts +141 -137
- package/src/controller/audio-track-controller.ts +1 -1
- package/src/controller/base-playlist-controller.ts +27 -10
- package/src/controller/base-stream-controller.ts +215 -82
- package/src/controller/buffer-controller.ts +250 -97
- package/src/controller/buffer-operation-queue.ts +16 -19
- package/src/controller/cap-level-controller.ts +3 -2
- package/src/controller/cmcd-controller.ts +51 -14
- package/src/controller/content-steering-controller.ts +29 -15
- package/src/controller/eme-controller.ts +10 -23
- package/src/controller/error-controller.ts +28 -22
- package/src/controller/fps-controller.ts +8 -3
- package/src/controller/fragment-finders.ts +44 -16
- package/src/controller/fragment-tracker.ts +58 -25
- package/src/controller/gap-controller.ts +43 -16
- package/src/controller/id3-track-controller.ts +45 -35
- package/src/controller/latency-controller.ts +18 -13
- package/src/controller/level-controller.ts +37 -19
- package/src/controller/stream-controller.ts +100 -83
- package/src/controller/subtitle-stream-controller.ts +35 -47
- package/src/controller/subtitle-track-controller.ts +5 -3
- package/src/controller/timeline-controller.ts +20 -22
- package/src/crypt/aes-crypto.ts +21 -2
- package/src/crypt/decrypter-aes-mode.ts +4 -0
- package/src/crypt/decrypter.ts +32 -16
- package/src/crypt/fast-aes-key.ts +28 -5
- package/src/demux/audio/aacdemuxer.ts +2 -2
- package/src/demux/audio/ac3-demuxer.ts +4 -3
- package/src/demux/audio/adts.ts +9 -4
- package/src/demux/audio/base-audio-demuxer.ts +16 -14
- package/src/demux/audio/mp3demuxer.ts +4 -3
- package/src/demux/audio/mpegaudio.ts +1 -1
- package/src/demux/mp4demuxer.ts +7 -7
- package/src/demux/sample-aes.ts +2 -0
- package/src/demux/transmuxer-interface.ts +8 -16
- package/src/demux/transmuxer-worker.ts +4 -4
- package/src/demux/transmuxer.ts +16 -3
- package/src/demux/tsdemuxer.ts +75 -38
- package/src/demux/video/avc-video-parser.ts +210 -121
- package/src/demux/video/base-video-parser.ts +135 -2
- package/src/demux/video/exp-golomb.ts +0 -208
- package/src/demux/video/hevc-video-parser.ts +749 -0
- package/src/events.ts +8 -1
- package/src/exports-named.ts +1 -1
- package/src/hls.ts +73 -43
- package/src/loader/date-range.ts +71 -5
- package/src/loader/fragment-loader.ts +23 -21
- package/src/loader/fragment.ts +8 -4
- package/src/loader/key-loader.ts +3 -1
- package/src/loader/level-details.ts +6 -6
- package/src/loader/level-key.ts +10 -9
- package/src/loader/m3u8-parser.ts +138 -144
- package/src/loader/playlist-loader.ts +5 -7
- package/src/remux/mp4-generator.ts +196 -1
- package/src/remux/mp4-remuxer.ts +36 -16
- package/src/remux/passthrough-remuxer.ts +1 -1
- package/src/task-loop.ts +5 -2
- package/src/types/component-api.ts +3 -1
- package/src/types/demuxer.ts +3 -0
- package/src/types/events.ts +19 -6
- package/src/types/fragment-tracker.ts +2 -2
- package/src/types/media-playlist.ts +9 -1
- package/src/types/remuxer.ts +1 -1
- package/src/utils/attr-list.ts +96 -9
- package/src/utils/buffer-helper.ts +12 -31
- package/src/utils/cea-608-parser.ts +1 -3
- package/src/utils/codecs.ts +34 -5
- package/src/utils/encryption-methods-util.ts +21 -0
- package/src/utils/fetch-loader.ts +1 -1
- package/src/utils/hash.ts +10 -0
- package/src/utils/hdr.ts +4 -7
- package/src/utils/imsc1-ttml-parser.ts +1 -1
- package/src/utils/keysystem-util.ts +1 -6
- package/src/utils/level-helper.ts +71 -44
- package/src/utils/logger.ts +58 -23
- package/src/utils/mp4-tools.ts +5 -3
- package/src/utils/rendition-helper.ts +100 -74
- package/src/utils/utf8-utils.ts +18 -0
- package/src/utils/variable-substitution.ts +0 -19
- package/src/utils/webvtt-parser.ts +2 -12
- package/src/demux/id3.ts +0 -411
- package/src/types/general.ts +0 -6
package/src/events.ts
CHANGED
@@ -1,8 +1,9 @@
|
|
1
|
-
import {
|
1
|
+
import type {
|
2
2
|
ManifestLoadedData,
|
3
3
|
ManifestLoadingData,
|
4
4
|
MediaAttachedData,
|
5
5
|
MediaAttachingData,
|
6
|
+
MediaEndedData,
|
6
7
|
LevelLoadingData,
|
7
8
|
LevelLoadedData,
|
8
9
|
ManifestParsedData,
|
@@ -60,6 +61,8 @@ export enum Events {
|
|
60
61
|
MEDIA_DETACHING = 'hlsMediaDetaching',
|
61
62
|
// Fired when MediaSource has been detached from media element
|
62
63
|
MEDIA_DETACHED = 'hlsMediaDetached',
|
64
|
+
// Fired when HTMLMediaElement dispatches "ended" event, or stalls at end of VOD program
|
65
|
+
MEDIA_ENDED = 'hlsMediaEnded',
|
63
66
|
// Fired when the buffer is going to be reset
|
64
67
|
BUFFER_RESET = 'hlsBufferReset',
|
65
68
|
// Fired when we know about the codecs that we need buffers for to push into - data: {tracks : { container, codec, levelCodec, initSegment, metadata }}
|
@@ -184,6 +187,10 @@ export interface HlsListeners {
|
|
184
187
|
) => void;
|
185
188
|
[Events.MEDIA_DETACHING]: (event: Events.MEDIA_DETACHING) => void;
|
186
189
|
[Events.MEDIA_DETACHED]: (event: Events.MEDIA_DETACHED) => void;
|
190
|
+
[Events.MEDIA_ENDED]: (
|
191
|
+
event: Events.MEDIA_ENDED,
|
192
|
+
data: MediaEndedData,
|
193
|
+
) => void;
|
187
194
|
[Events.BUFFER_RESET]: (event: Events.BUFFER_RESET) => void;
|
188
195
|
[Events.BUFFER_CODECS]: (
|
189
196
|
event: Events.BUFFER_CODECS,
|
package/src/exports-named.ts
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
import Hls from './hls';
|
2
2
|
import { Events } from './events';
|
3
3
|
import { ErrorTypes, ErrorDetails } from './errors';
|
4
|
-
import { Level } from './types/level';
|
4
|
+
import type { Level } from './types/level';
|
5
5
|
import AbrController from './controller/abr-controller';
|
6
6
|
import AudioTrackController from './controller/audio-track-controller';
|
7
7
|
import AudioStreamController from './controller/audio-stream-controller';
|
package/src/hls.ts
CHANGED
@@ -8,7 +8,7 @@ import KeyLoader from './loader/key-loader';
|
|
8
8
|
import StreamController from './controller/stream-controller';
|
9
9
|
import { isMSESupported, isSupported } from './is-supported';
|
10
10
|
import { getMediaSource } from './utils/mediasource-helper';
|
11
|
-
import {
|
11
|
+
import { enableLogs, type ILogger } from './utils/logger';
|
12
12
|
import { enableStreamingMode, hlsDefaultConfig, mergeConfig } from './config';
|
13
13
|
import { EventEmitter } from 'eventemitter3';
|
14
14
|
import { Events } from './events';
|
@@ -59,9 +59,13 @@ export default class Hls implements HlsEventEmitter {
|
|
59
59
|
*/
|
60
60
|
public readonly userConfig: Partial<HlsConfig>;
|
61
61
|
|
62
|
+
/**
|
63
|
+
* The logger functions used by this player instance, configured on player instantiation.
|
64
|
+
*/
|
65
|
+
public readonly logger: ILogger;
|
66
|
+
|
62
67
|
private coreComponents: ComponentAPI[];
|
63
68
|
private networkControllers: NetworkComponentAPI[];
|
64
|
-
private started: boolean = false;
|
65
69
|
private _emitter: HlsEventEmitter = new EventEmitter();
|
66
70
|
private _autoLevelCapping: number = -1;
|
67
71
|
private _maxHdcpLevel: HdcpLevel = null;
|
@@ -142,12 +146,19 @@ export default class Hls implements HlsEventEmitter {
|
|
142
146
|
* @param userConfig - Configuration options applied over `Hls.DefaultConfig`
|
143
147
|
*/
|
144
148
|
constructor(userConfig: Partial<HlsConfig> = {}) {
|
145
|
-
|
146
|
-
|
149
|
+
const logger = (this.logger = enableLogs(
|
150
|
+
userConfig.debug || false,
|
151
|
+
'Hls instance',
|
152
|
+
));
|
153
|
+
const config = (this.config = mergeConfig(
|
154
|
+
Hls.DefaultConfig,
|
155
|
+
userConfig,
|
156
|
+
logger,
|
157
|
+
));
|
147
158
|
this.userConfig = userConfig;
|
148
159
|
|
149
160
|
if (config.progressive) {
|
150
|
-
enableStreamingMode(config);
|
161
|
+
enableStreamingMode(config, logger);
|
151
162
|
}
|
152
163
|
|
153
164
|
// core controllers and network loaders
|
@@ -160,8 +171,10 @@ export default class Hls implements HlsEventEmitter {
|
|
160
171
|
} = config;
|
161
172
|
const errorController = new ConfigErrorController(this);
|
162
173
|
const abrController = (this.abrController = new ConfigAbrController(this));
|
174
|
+
// FragmentTracker must be defined before StreamController because the order of event handling is important
|
175
|
+
const fragmentTracker = new FragmentTracker(this);
|
163
176
|
const bufferController = (this.bufferController =
|
164
|
-
new ConfigBufferController(this));
|
177
|
+
new ConfigBufferController(this, fragmentTracker));
|
165
178
|
const capLevelController = (this.capLevelController =
|
166
179
|
new ConfigCapLevelController(this));
|
167
180
|
|
@@ -170,7 +183,7 @@ export default class Hls implements HlsEventEmitter {
|
|
170
183
|
const id3TrackController = new ID3TrackController(this);
|
171
184
|
|
172
185
|
const ConfigContentSteeringController = config.contentSteeringController;
|
173
|
-
//
|
186
|
+
// ContentSteeringController is defined before LevelController to receive Multivariant Playlist events first
|
174
187
|
const contentSteering = ConfigContentSteeringController
|
175
188
|
? new ConfigContentSteeringController(this)
|
176
189
|
: null;
|
@@ -178,8 +191,6 @@ export default class Hls implements HlsEventEmitter {
|
|
178
191
|
this,
|
179
192
|
contentSteering,
|
180
193
|
));
|
181
|
-
// FragmentTracker must be defined before StreamController because the order of event handling is important
|
182
|
-
const fragmentTracker = new FragmentTracker(this);
|
183
194
|
const keyLoader = new KeyLoader(this.config);
|
184
195
|
const streamController = (this.streamController = new StreamController(
|
185
196
|
this,
|
@@ -221,7 +232,7 @@ export default class Hls implements HlsEventEmitter {
|
|
221
232
|
new AudioStreamControllerClass(this, fragmentTracker, keyLoader),
|
222
233
|
);
|
223
234
|
}
|
224
|
-
// subtitleTrackController
|
235
|
+
// Instantiate subtitleTrackController before SubtitleStreamController to receive level events first
|
225
236
|
this.subtitleTrackController = this.createController(
|
226
237
|
config.subtitleTrackController,
|
227
238
|
networkControllers,
|
@@ -320,7 +331,7 @@ export default class Hls implements HlsEventEmitter {
|
|
320
331
|
try {
|
321
332
|
return this.emit(event, event, eventObject);
|
322
333
|
} catch (error) {
|
323
|
-
logger.error(
|
334
|
+
this.logger.error(
|
324
335
|
'An internal error happened while handling event ' +
|
325
336
|
event +
|
326
337
|
'. Error message: "' +
|
@@ -354,7 +365,7 @@ export default class Hls implements HlsEventEmitter {
|
|
354
365
|
* Dispose of the instance
|
355
366
|
*/
|
356
367
|
destroy() {
|
357
|
-
logger.log('destroy');
|
368
|
+
this.logger.log('destroy');
|
358
369
|
this.trigger(Events.DESTROYING, undefined);
|
359
370
|
this.detachMedia();
|
360
371
|
this.removeAllListeners();
|
@@ -377,7 +388,7 @@ export default class Hls implements HlsEventEmitter {
|
|
377
388
|
* Attaches Hls.js to a media element
|
378
389
|
*/
|
379
390
|
attachMedia(media: HTMLMediaElement) {
|
380
|
-
logger.log('attachMedia');
|
391
|
+
this.logger.log('attachMedia');
|
381
392
|
this._media = media;
|
382
393
|
this.trigger(Events.MEDIA_ATTACHING, { media: media });
|
383
394
|
}
|
@@ -386,7 +397,7 @@ export default class Hls implements HlsEventEmitter {
|
|
386
397
|
* Detach Hls.js from the media
|
387
398
|
*/
|
388
399
|
detachMedia() {
|
389
|
-
logger.log('detachMedia');
|
400
|
+
this.logger.log('detachMedia');
|
390
401
|
this.trigger(Events.MEDIA_DETACHING, undefined);
|
391
402
|
this._media = null;
|
392
403
|
}
|
@@ -407,7 +418,7 @@ export default class Hls implements HlsEventEmitter {
|
|
407
418
|
));
|
408
419
|
this._autoLevelCapping = -1;
|
409
420
|
this._maxHdcpLevel = null;
|
410
|
-
logger.log(`loadSource:${loadingSource}`);
|
421
|
+
this.logger.log(`loadSource:${loadingSource}`);
|
411
422
|
if (
|
412
423
|
media &&
|
413
424
|
loadedSource &&
|
@@ -428,8 +439,7 @@ export default class Hls implements HlsEventEmitter {
|
|
428
439
|
* Defaults to -1 (None: starts from earliest point)
|
429
440
|
*/
|
430
441
|
startLoad(startPosition: number = -1) {
|
431
|
-
logger.log(`startLoad(${startPosition})`);
|
432
|
-
this.started = true;
|
442
|
+
this.logger.log(`startLoad(${startPosition})`);
|
433
443
|
this.networkControllers.forEach((controller) => {
|
434
444
|
controller.startLoad(startPosition);
|
435
445
|
});
|
@@ -439,34 +449,31 @@ export default class Hls implements HlsEventEmitter {
|
|
439
449
|
* Stop loading of any stream data.
|
440
450
|
*/
|
441
451
|
stopLoad() {
|
442
|
-
logger.log('stopLoad');
|
443
|
-
this.started = false;
|
452
|
+
this.logger.log('stopLoad');
|
444
453
|
this.networkControllers.forEach((controller) => {
|
445
454
|
controller.stopLoad();
|
446
455
|
});
|
447
456
|
}
|
448
457
|
|
449
458
|
/**
|
450
|
-
* Resumes stream controller segment loading
|
459
|
+
* Resumes stream controller segment loading after `pauseBuffering` has been called.
|
451
460
|
*/
|
452
461
|
resumeBuffering() {
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
});
|
459
|
-
}
|
462
|
+
this.networkControllers.forEach((controller) => {
|
463
|
+
if (controller.resumeBuffering) {
|
464
|
+
controller.resumeBuffering();
|
465
|
+
}
|
466
|
+
});
|
460
467
|
}
|
461
468
|
|
462
469
|
/**
|
463
|
-
*
|
470
|
+
* Prevents stream controller from loading new segments until `resumeBuffering` is called.
|
464
471
|
* This allows for media buffering to be paused without interupting playlist loading.
|
465
472
|
*/
|
466
473
|
pauseBuffering() {
|
467
474
|
this.networkControllers.forEach((controller) => {
|
468
|
-
if (
|
469
|
-
controller.
|
475
|
+
if (controller.pauseBuffering) {
|
476
|
+
controller.pauseBuffering();
|
470
477
|
}
|
471
478
|
});
|
472
479
|
}
|
@@ -475,7 +482,7 @@ export default class Hls implements HlsEventEmitter {
|
|
475
482
|
* Swap through possible audio codecs in the stream (for example to switch from stereo to 5.1)
|
476
483
|
*/
|
477
484
|
swapAudioCodec() {
|
478
|
-
logger.log('swapAudioCodec');
|
485
|
+
this.logger.log('swapAudioCodec');
|
479
486
|
this.streamController.swapAudioCodec();
|
480
487
|
}
|
481
488
|
|
@@ -486,7 +493,7 @@ export default class Hls implements HlsEventEmitter {
|
|
486
493
|
* Automatic recovery of media-errors by this process is configurable.
|
487
494
|
*/
|
488
495
|
recoverMediaError() {
|
489
|
-
logger.log('recoverMediaError');
|
496
|
+
this.logger.log('recoverMediaError');
|
490
497
|
const media = this._media;
|
491
498
|
this.detachMedia();
|
492
499
|
if (media) {
|
@@ -517,7 +524,7 @@ export default class Hls implements HlsEventEmitter {
|
|
517
524
|
* Set quality level index immediately. This will flush the current buffer to replace the quality asap. That means playback will interrupt at least shortly to re-buffer and re-sync eventually. Set to -1 for automatic level selection.
|
518
525
|
*/
|
519
526
|
set currentLevel(newLevel: number) {
|
520
|
-
logger.log(`set currentLevel:${newLevel}`);
|
527
|
+
this.logger.log(`set currentLevel:${newLevel}`);
|
521
528
|
this.levelController.manualLevel = newLevel;
|
522
529
|
this.streamController.immediateLevelSwitch();
|
523
530
|
}
|
@@ -536,7 +543,7 @@ export default class Hls implements HlsEventEmitter {
|
|
536
543
|
* @param newLevel - Pass -1 for automatic level selection
|
537
544
|
*/
|
538
545
|
set nextLevel(newLevel: number) {
|
539
|
-
logger.log(`set nextLevel:${newLevel}`);
|
546
|
+
this.logger.log(`set nextLevel:${newLevel}`);
|
540
547
|
this.levelController.manualLevel = newLevel;
|
541
548
|
this.streamController.nextLevelSwitch();
|
542
549
|
}
|
@@ -555,7 +562,7 @@ export default class Hls implements HlsEventEmitter {
|
|
555
562
|
* @param newLevel - Pass -1 for automatic level selection
|
556
563
|
*/
|
557
564
|
set loadLevel(newLevel: number) {
|
558
|
-
logger.log(`set loadLevel:${newLevel}`);
|
565
|
+
this.logger.log(`set loadLevel:${newLevel}`);
|
559
566
|
this.levelController.manualLevel = newLevel;
|
560
567
|
}
|
561
568
|
|
@@ -586,7 +593,7 @@ export default class Hls implements HlsEventEmitter {
|
|
586
593
|
* Sets "first-level", see getter.
|
587
594
|
*/
|
588
595
|
set firstLevel(newLevel: number) {
|
589
|
-
logger.log(`set firstLevel:${newLevel}`);
|
596
|
+
this.logger.log(`set firstLevel:${newLevel}`);
|
590
597
|
this.levelController.firstLevel = newLevel;
|
591
598
|
}
|
592
599
|
|
@@ -611,7 +618,7 @@ export default class Hls implements HlsEventEmitter {
|
|
611
618
|
* (determined from download of first segment)
|
612
619
|
*/
|
613
620
|
set startLevel(newLevel: number) {
|
614
|
-
logger.log(`set startLevel:${newLevel}`);
|
621
|
+
this.logger.log(`set startLevel:${newLevel}`);
|
615
622
|
// if not in automatic start level detection, ensure startLevel is greater than minAutoLevel
|
616
623
|
if (newLevel !== -1) {
|
617
624
|
newLevel = Math.max(newLevel, this.minAutoLevel);
|
@@ -686,7 +693,7 @@ export default class Hls implements HlsEventEmitter {
|
|
686
693
|
*/
|
687
694
|
set autoLevelCapping(newLevel: number) {
|
688
695
|
if (this._autoLevelCapping !== newLevel) {
|
689
|
-
logger.log(`set autoLevelCapping:${newLevel}`);
|
696
|
+
this.logger.log(`set autoLevelCapping:${newLevel}`);
|
690
697
|
this._autoLevelCapping = newLevel;
|
691
698
|
this.levelController.checkMaxAutoUpdated();
|
692
699
|
}
|
@@ -795,6 +802,10 @@ export default class Hls implements HlsEventEmitter {
|
|
795
802
|
return this.streamController.getMainFwdBufferInfo();
|
796
803
|
}
|
797
804
|
|
805
|
+
public get maxBufferLength(): number {
|
806
|
+
return this.streamController.maxBufferLength;
|
807
|
+
}
|
808
|
+
|
798
809
|
/**
|
799
810
|
* Find and select the best matching audio track, making a level switch when a Group change is necessary.
|
800
811
|
* Updates `hls.config.audioPreference`. Returns the selected track, or null when no matching track is found.
|
@@ -802,7 +813,7 @@ export default class Hls implements HlsEventEmitter {
|
|
802
813
|
public setAudioOption(
|
803
814
|
audioOption: MediaPlaylist | AudioSelectionOption | undefined,
|
804
815
|
): MediaPlaylist | null {
|
805
|
-
return this.audioTrackController?.setAudioOption(audioOption);
|
816
|
+
return this.audioTrackController?.setAudioOption(audioOption) || null;
|
806
817
|
}
|
807
818
|
/**
|
808
819
|
* Find and select the best matching subtitle track, making a level switch when a Group change is necessary.
|
@@ -811,8 +822,9 @@ export default class Hls implements HlsEventEmitter {
|
|
811
822
|
public setSubtitleOption(
|
812
823
|
subtitleOption: MediaPlaylist | SubtitleSelectionOption | undefined,
|
813
824
|
): MediaPlaylist | null {
|
814
|
-
|
815
|
-
|
825
|
+
return (
|
826
|
+
this.subtitleTrackController?.setSubtitleOption(subtitleOption) || null
|
827
|
+
);
|
816
828
|
}
|
817
829
|
|
818
830
|
/**
|
@@ -957,6 +969,10 @@ export default class Hls implements HlsEventEmitter {
|
|
957
969
|
return this.latencyController.targetLatency;
|
958
970
|
}
|
959
971
|
|
972
|
+
set targetLatency(latency: number) {
|
973
|
+
this.latencyController.targetLatency = latency;
|
974
|
+
}
|
975
|
+
|
960
976
|
/**
|
961
977
|
* the rate at which the edge of the current live playlist is advancing or 1 if there is none
|
962
978
|
*/
|
@@ -970,6 +986,17 @@ export default class Hls implements HlsEventEmitter {
|
|
970
986
|
get forceStartLoad(): boolean {
|
971
987
|
return this.streamController.forceStartLoad;
|
972
988
|
}
|
989
|
+
|
990
|
+
/**
|
991
|
+
* ContentSteering pathwayPriority getter/setter
|
992
|
+
*/
|
993
|
+
get pathwayPriority(): string[] | null {
|
994
|
+
return this.levelController.pathwayPriority;
|
995
|
+
}
|
996
|
+
|
997
|
+
set pathwayPriority(pathwayPriority: string[]) {
|
998
|
+
this.levelController.pathwayPriority = pathwayPriority;
|
999
|
+
}
|
973
1000
|
}
|
974
1001
|
|
975
1002
|
export type {
|
@@ -1032,7 +1059,7 @@ export type {
|
|
1032
1059
|
TSDemuxerConfig,
|
1033
1060
|
} from './config';
|
1034
1061
|
export type { MediaKeySessionContext } from './controller/eme-controller';
|
1035
|
-
export type { ILogger } from './utils/logger';
|
1062
|
+
export type { ILogger, Logger } from './utils/logger';
|
1036
1063
|
export type {
|
1037
1064
|
PathwayClone,
|
1038
1065
|
SteeringManifest,
|
@@ -1046,7 +1073,7 @@ export type {
|
|
1046
1073
|
KeySystems,
|
1047
1074
|
KeySystemFormats,
|
1048
1075
|
} from './utils/mediakeys-helper';
|
1049
|
-
export type { DateRange } from './loader/date-range';
|
1076
|
+
export type { DateRange, DateRangeCue } from './loader/date-range';
|
1050
1077
|
export type { LoadStats } from './loader/load-stats';
|
1051
1078
|
export type { LevelKey } from './loader/level-key';
|
1052
1079
|
export type { LevelDetails } from './loader/level-details';
|
@@ -1096,6 +1123,7 @@ export type { ChunkMetadata } from './types/transmuxer';
|
|
1096
1123
|
export type {
|
1097
1124
|
BaseSegment,
|
1098
1125
|
Fragment,
|
1126
|
+
MediaFragment,
|
1099
1127
|
Part,
|
1100
1128
|
ElementaryStreams,
|
1101
1129
|
ElementaryStreamTypes,
|
@@ -1147,6 +1175,7 @@ export type {
|
|
1147
1175
|
ManifestParsedData,
|
1148
1176
|
MediaAttachedData,
|
1149
1177
|
MediaAttachingData,
|
1178
|
+
MediaEndedData,
|
1150
1179
|
NonNativeTextTrack,
|
1151
1180
|
NonNativeTextTracksData,
|
1152
1181
|
SteeringManifestLoadedData,
|
@@ -1161,3 +1190,4 @@ export type {
|
|
1161
1190
|
IErrorAction,
|
1162
1191
|
} from './controller/error-controller';
|
1163
1192
|
export type { AttrList } from './utils/attr-list';
|
1193
|
+
export type { ParsedMultivariantPlaylist } from './loader/m3u8-parser';
|
package/src/loader/date-range.ts
CHANGED
@@ -1,10 +1,12 @@
|
|
1
1
|
import { AttrList } from '../utils/attr-list';
|
2
2
|
import { logger } from '../utils/logger';
|
3
|
+
import type { Fragment } from './fragment';
|
3
4
|
|
4
5
|
// Avoid exporting const enum so that these values can be inlined
|
5
6
|
const enum DateRangeAttribute {
|
6
7
|
ID = 'ID',
|
7
8
|
CLASS = 'CLASS',
|
9
|
+
CUE = 'CUE',
|
8
10
|
START_DATE = 'START-DATE',
|
9
11
|
DURATION = 'DURATION',
|
10
12
|
END_DATE = 'END-DATE',
|
@@ -12,12 +14,22 @@ const enum DateRangeAttribute {
|
|
12
14
|
PLANNED_DURATION = 'PLANNED-DURATION',
|
13
15
|
SCTE35_OUT = 'SCTE35-OUT',
|
14
16
|
SCTE35_IN = 'SCTE35-IN',
|
17
|
+
SCTE35_CMD = 'SCTE35-CMD',
|
15
18
|
}
|
16
19
|
|
20
|
+
export type DateRangeCue = {
|
21
|
+
pre: boolean;
|
22
|
+
post: boolean;
|
23
|
+
once: boolean;
|
24
|
+
};
|
25
|
+
|
26
|
+
const CLASS_INTERSTITIAL = 'com.apple.hls.interstitial';
|
27
|
+
|
17
28
|
export function isDateRangeCueAttribute(attrName: string): boolean {
|
18
29
|
return (
|
19
30
|
attrName !== DateRangeAttribute.ID &&
|
20
31
|
attrName !== DateRangeAttribute.CLASS &&
|
32
|
+
attrName !== DateRangeAttribute.CUE &&
|
21
33
|
attrName !== DateRangeAttribute.START_DATE &&
|
22
34
|
attrName !== DateRangeAttribute.DURATION &&
|
23
35
|
attrName !== DateRangeAttribute.END_DATE &&
|
@@ -28,17 +40,27 @@ export function isDateRangeCueAttribute(attrName: string): boolean {
|
|
28
40
|
export function isSCTE35Attribute(attrName: string): boolean {
|
29
41
|
return (
|
30
42
|
attrName === DateRangeAttribute.SCTE35_OUT ||
|
31
|
-
attrName === DateRangeAttribute.SCTE35_IN
|
43
|
+
attrName === DateRangeAttribute.SCTE35_IN ||
|
44
|
+
attrName === DateRangeAttribute.SCTE35_CMD
|
32
45
|
);
|
33
46
|
}
|
34
47
|
|
35
48
|
export class DateRange {
|
36
49
|
public attr: AttrList;
|
50
|
+
public tagAnchor: Fragment | null;
|
51
|
+
public tagOrder: number;
|
37
52
|
private _startDate: Date;
|
38
53
|
private _endDate?: Date;
|
54
|
+
private _cue?: DateRangeCue;
|
39
55
|
private _badValueForSameId?: string;
|
40
56
|
|
41
|
-
constructor(
|
57
|
+
constructor(
|
58
|
+
dateRangeAttr: AttrList,
|
59
|
+
dateRangeWithSameId?: DateRange | undefined,
|
60
|
+
tagCount: number = 0,
|
61
|
+
) {
|
62
|
+
this.tagAnchor = dateRangeWithSameId?.tagAnchor || null;
|
63
|
+
this.tagOrder = dateRangeWithSameId?.tagOrder ?? tagCount;
|
42
64
|
if (dateRangeWithSameId) {
|
43
65
|
const previousAttr = dateRangeWithSameId.attr;
|
44
66
|
for (const key in previousAttr) {
|
@@ -61,9 +83,13 @@ export class DateRange {
|
|
61
83
|
);
|
62
84
|
}
|
63
85
|
this.attr = dateRangeAttr;
|
64
|
-
this._startDate =
|
86
|
+
this._startDate = dateRangeWithSameId
|
87
|
+
? dateRangeWithSameId.startDate
|
88
|
+
: new Date(dateRangeAttr[DateRangeAttribute.START_DATE]);
|
65
89
|
if (DateRangeAttribute.END_DATE in this.attr) {
|
66
|
-
const endDate =
|
90
|
+
const endDate =
|
91
|
+
dateRangeWithSameId?.endDate ||
|
92
|
+
new Date(this.attr[DateRangeAttribute.END_DATE]);
|
67
93
|
if (Number.isFinite(endDate.getTime())) {
|
68
94
|
this._endDate = endDate;
|
69
95
|
}
|
@@ -78,6 +104,36 @@ export class DateRange {
|
|
78
104
|
return this.attr.CLASS;
|
79
105
|
}
|
80
106
|
|
107
|
+
get cue(): DateRangeCue {
|
108
|
+
const _cue = this._cue;
|
109
|
+
if (_cue === undefined) {
|
110
|
+
return (this._cue = this.attr.enumeratedStringList(
|
111
|
+
this.attr.CUE ? 'CUE' : 'X-CUE',
|
112
|
+
{
|
113
|
+
pre: false,
|
114
|
+
post: false,
|
115
|
+
once: false,
|
116
|
+
},
|
117
|
+
));
|
118
|
+
}
|
119
|
+
return _cue;
|
120
|
+
}
|
121
|
+
|
122
|
+
get startTime(): number {
|
123
|
+
const { tagAnchor } = this;
|
124
|
+
// eslint-disable-next-line @typescript-eslint/prefer-optional-chain
|
125
|
+
if (tagAnchor === null || tagAnchor.programDateTime === null) {
|
126
|
+
logger.warn(
|
127
|
+
`Expected tagAnchor Fragment with PDT set for DateRange "${this.id}": ${tagAnchor}`,
|
128
|
+
);
|
129
|
+
return NaN;
|
130
|
+
}
|
131
|
+
return (
|
132
|
+
tagAnchor.start +
|
133
|
+
(this.startDate.getTime() - tagAnchor.programDateTime) / 1000
|
134
|
+
);
|
135
|
+
}
|
136
|
+
|
81
137
|
get startDate(): Date {
|
82
138
|
return this._startDate;
|
83
139
|
}
|
@@ -120,13 +176,23 @@ export class DateRange {
|
|
120
176
|
return this.attr.bool(DateRangeAttribute.END_ON_NEXT);
|
121
177
|
}
|
122
178
|
|
179
|
+
get isInterstitial(): boolean {
|
180
|
+
return this.class === CLASS_INTERSTITIAL;
|
181
|
+
}
|
182
|
+
|
123
183
|
get isValid(): boolean {
|
124
184
|
return (
|
125
185
|
!!this.id &&
|
126
186
|
!this._badValueForSameId &&
|
127
187
|
Number.isFinite(this.startDate.getTime()) &&
|
128
188
|
(this.duration === null || this.duration >= 0) &&
|
129
|
-
(!this.endOnNext || !!this.class)
|
189
|
+
(!this.endOnNext || !!this.class) &&
|
190
|
+
(!this.attr.CUE ||
|
191
|
+
(!this.cue.pre && !this.cue.post) ||
|
192
|
+
this.cue.pre !== this.cue.post) &&
|
193
|
+
(!this.isInterstitial ||
|
194
|
+
'X-ASSET-URI' in this.attr ||
|
195
|
+
'X-ASSET-LIST' in this.attr)
|
130
196
|
);
|
131
197
|
}
|
132
198
|
}
|
@@ -1,18 +1,17 @@
|
|
1
1
|
import { ErrorTypes, ErrorDetails } from '../errors';
|
2
|
-
import { Fragment } from './fragment';
|
3
|
-
import {
|
4
|
-
Loader,
|
5
|
-
LoaderConfiguration,
|
6
|
-
FragmentLoaderContext,
|
7
|
-
} from '../types/loader';
|
8
2
|
import { getLoaderConfigWithoutReties } from '../utils/error-helper';
|
9
3
|
import type { HlsConfig } from '../config';
|
10
|
-
import type { BaseSegment, Part } from './fragment';
|
4
|
+
import type { BaseSegment, Fragment, Part } from './fragment';
|
11
5
|
import type {
|
12
6
|
ErrorData,
|
13
7
|
FragLoadedData,
|
14
8
|
PartsLoadedData,
|
15
9
|
} from '../types/events';
|
10
|
+
import type {
|
11
|
+
Loader,
|
12
|
+
LoaderConfiguration,
|
13
|
+
FragmentLoaderContext,
|
14
|
+
} from '../types/loader';
|
16
15
|
|
17
16
|
const MIN_CHUNK_SIZE = Math.pow(2, 17); // 128kb
|
18
17
|
|
@@ -77,13 +76,11 @@ export default class FragmentLoader {
|
|
77
76
|
frag.gap = false;
|
78
77
|
}
|
79
78
|
}
|
80
|
-
const loader =
|
81
|
-
(
|
82
|
-
|
83
|
-
FragmentILoader
|
84
|
-
? new FragmentILoader(config)
|
85
|
-
: (new DefaultILoader(config) as Loader<FragmentLoaderContext>));
|
79
|
+
const loader = (this.loader = FragmentILoader
|
80
|
+
? new FragmentILoader(config)
|
81
|
+
: (new DefaultILoader(config) as Loader<FragmentLoaderContext>));
|
86
82
|
const loaderContext = createLoaderContext(frag);
|
83
|
+
frag.loader = loader;
|
87
84
|
const loadPolicy = getLoaderConfigWithoutReties(
|
88
85
|
config.fragLoadPolicy.default,
|
89
86
|
);
|
@@ -188,13 +185,11 @@ export default class FragmentLoader {
|
|
188
185
|
reject(createGapLoadError(frag, part));
|
189
186
|
return;
|
190
187
|
}
|
191
|
-
const loader =
|
192
|
-
(
|
193
|
-
|
194
|
-
FragmentILoader
|
195
|
-
? new FragmentILoader(config)
|
196
|
-
: (new DefaultILoader(config) as Loader<FragmentLoaderContext>));
|
188
|
+
const loader = (this.loader = FragmentILoader
|
189
|
+
? new FragmentILoader(config)
|
190
|
+
: (new DefaultILoader(config) as Loader<FragmentLoaderContext>));
|
197
191
|
const loaderContext = createLoaderContext(frag, part);
|
192
|
+
frag.loader = loader;
|
198
193
|
// Should we define another load policy for parts?
|
199
194
|
const loadPolicy = getLoaderConfigWithoutReties(
|
200
195
|
config.fragLoadPolicy.default,
|
@@ -336,8 +331,11 @@ function createLoaderContext(
|
|
336
331
|
if (Number.isFinite(start) && Number.isFinite(end)) {
|
337
332
|
let byteRangeStart = start;
|
338
333
|
let byteRangeEnd = end;
|
339
|
-
if (
|
340
|
-
|
334
|
+
if (
|
335
|
+
frag.sn === 'initSegment' &&
|
336
|
+
isMethodFullSegmentAesCbc(frag.decryptdata?.method)
|
337
|
+
) {
|
338
|
+
// MAP segment encrypted with method 'AES-128' or 'AES-256' (cbc), when served with HTTP Range,
|
341
339
|
// has the unencrypted size specified in the range.
|
342
340
|
// Ref: https://tools.ietf.org/html/draft-pantos-hls-rfc8216bis-08#section-6.3.6
|
343
341
|
const fragmentLen = end - start;
|
@@ -372,6 +370,10 @@ function createGapLoadError(frag: Fragment, part?: Part): LoadError {
|
|
372
370
|
return new LoadError(errorData);
|
373
371
|
}
|
374
372
|
|
373
|
+
function isMethodFullSegmentAesCbc(method) {
|
374
|
+
return method === 'AES-128' || method === 'AES-256';
|
375
|
+
}
|
376
|
+
|
375
377
|
export class LoadError extends Error {
|
376
378
|
public readonly data: FragLoadFailResult;
|
377
379
|
constructor(data: FragLoadFailResult) {
|
package/src/loader/fragment.ts
CHANGED
@@ -90,6 +90,10 @@ export class BaseSegment {
|
|
90
90
|
}
|
91
91
|
}
|
92
92
|
|
93
|
+
export interface MediaFragment extends Fragment {
|
94
|
+
sn: number;
|
95
|
+
}
|
96
|
+
|
93
97
|
/**
|
94
98
|
* Object representing parsed data from an HLS Segment. Found in {@link hls.js#LevelDetails.fragments}.
|
95
99
|
*/
|
@@ -123,9 +127,9 @@ export class Fragment extends BaseSegment {
|
|
123
127
|
// The ending Presentation Time Stamp (PTS) of the fragment. Set after transmux complete.
|
124
128
|
public endPTS?: number;
|
125
129
|
// The starting Decode Time Stamp (DTS) of the fragment. Set after transmux complete.
|
126
|
-
public startDTS
|
130
|
+
public startDTS?: number;
|
127
131
|
// The ending Decode Time Stamp (DTS) of the fragment. Set after transmux complete.
|
128
|
-
public endDTS
|
132
|
+
public endDTS?: number;
|
129
133
|
// The start time of the fragment, as listed in the manifest. Updated after transmux complete.
|
130
134
|
public start: number = 0;
|
131
135
|
// Set by `updateFragPTSDTS` in level-helper
|
@@ -274,13 +278,13 @@ export class Part extends BaseSegment {
|
|
274
278
|
public readonly gap: boolean = false;
|
275
279
|
public readonly independent: boolean = false;
|
276
280
|
public readonly relurl: string;
|
277
|
-
public readonly fragment:
|
281
|
+
public readonly fragment: MediaFragment;
|
278
282
|
public readonly index: number;
|
279
283
|
public stats: LoadStats = new LoadStats();
|
280
284
|
|
281
285
|
constructor(
|
282
286
|
partAttrs: AttrList,
|
283
|
-
frag:
|
287
|
+
frag: MediaFragment,
|
284
288
|
baseurl: string,
|
285
289
|
index: number,
|
286
290
|
previous?: Part,
|
package/src/loader/key-loader.ts
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
import { ErrorTypes, ErrorDetails } from '../errors';
|
2
|
-
import {
|
2
|
+
import type {
|
3
3
|
LoaderStats,
|
4
4
|
LoaderResponse,
|
5
5
|
LoaderConfiguration,
|
@@ -194,6 +194,8 @@ export default class KeyLoader implements ComponentAPI {
|
|
194
194
|
}
|
195
195
|
return this.loadKeyEME(keyInfo, frag);
|
196
196
|
case 'AES-128':
|
197
|
+
case 'AES-256':
|
198
|
+
case 'AES-256-CTR':
|
197
199
|
return this.loadKeyHTTP(keyInfo, frag);
|
198
200
|
default:
|
199
201
|
return Promise.reject(
|