hls.js 1.5.13-0.canary.10410 → 1.5.13
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 +3 -4
- package/dist/hls-demo.js +38 -41
- package/dist/hls-demo.js.map +1 -1
- package/dist/hls.js +2704 -4236
- package/dist/hls.js.d.ts +110 -179
- package/dist/hls.js.map +1 -1
- package/dist/hls.light.js +1994 -2914
- 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 +3458 -4388
- 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 +4506 -6048
- 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 +2 -5
- package/src/controller/abr-controller.ts +25 -39
- package/src/controller/audio-stream-controller.ts +137 -141
- package/src/controller/audio-track-controller.ts +1 -1
- package/src/controller/base-playlist-controller.ts +10 -27
- package/src/controller/base-stream-controller.ts +91 -235
- package/src/controller/buffer-controller.ts +97 -250
- package/src/controller/buffer-operation-queue.ts +19 -16
- package/src/controller/cap-level-controller.ts +2 -3
- package/src/controller/cmcd-controller.ts +14 -51
- package/src/controller/content-steering-controller.ts +15 -29
- package/src/controller/eme-controller.ts +23 -10
- package/src/controller/error-controller.ts +22 -28
- package/src/controller/fps-controller.ts +3 -8
- package/src/controller/fragment-finders.ts +16 -44
- package/src/controller/fragment-tracker.ts +25 -58
- package/src/controller/gap-controller.ts +16 -43
- package/src/controller/id3-track-controller.ts +35 -45
- package/src/controller/latency-controller.ts +13 -18
- package/src/controller/level-controller.ts +19 -37
- package/src/controller/stream-controller.ts +83 -100
- package/src/controller/subtitle-stream-controller.ts +47 -35
- package/src/controller/subtitle-track-controller.ts +3 -5
- package/src/controller/timeline-controller.ts +22 -20
- package/src/crypt/aes-crypto.ts +2 -21
- package/src/crypt/decrypter.ts +16 -32
- package/src/crypt/fast-aes-key.ts +5 -28
- package/src/demux/audio/aacdemuxer.ts +2 -2
- package/src/demux/audio/ac3-demuxer.ts +3 -4
- package/src/demux/audio/adts.ts +4 -9
- package/src/demux/audio/base-audio-demuxer.ts +14 -16
- package/src/demux/audio/mp3demuxer.ts +3 -4
- package/src/demux/audio/mpegaudio.ts +1 -1
- package/src/demux/id3.ts +411 -0
- package/src/demux/mp4demuxer.ts +7 -7
- package/src/demux/sample-aes.ts +0 -2
- package/src/demux/transmuxer-interface.ts +16 -8
- package/src/demux/transmuxer-worker.ts +4 -4
- package/src/demux/transmuxer.ts +3 -16
- package/src/demux/tsdemuxer.ts +38 -75
- package/src/demux/video/avc-video-parser.ts +121 -210
- package/src/demux/video/base-video-parser.ts +2 -135
- package/src/demux/video/exp-golomb.ts +208 -0
- package/src/events.ts +1 -8
- package/src/exports-named.ts +1 -1
- package/src/hls.ts +47 -84
- package/src/loader/date-range.ts +5 -71
- package/src/loader/fragment-loader.ts +21 -23
- package/src/loader/fragment.ts +4 -8
- package/src/loader/key-loader.ts +1 -3
- package/src/loader/level-details.ts +6 -6
- package/src/loader/level-key.ts +9 -10
- package/src/loader/m3u8-parser.ts +144 -138
- package/src/loader/playlist-loader.ts +7 -5
- package/src/remux/mp4-generator.ts +1 -196
- package/src/remux/mp4-remuxer.ts +62 -32
- package/src/remux/passthrough-remuxer.ts +1 -1
- package/src/task-loop.ts +2 -5
- package/src/types/component-api.ts +1 -3
- package/src/types/demuxer.ts +0 -3
- package/src/types/events.ts +6 -19
- package/src/types/fragment-tracker.ts +2 -2
- package/src/types/general.ts +6 -0
- package/src/types/media-playlist.ts +1 -9
- package/src/types/remuxer.ts +1 -1
- package/src/utils/attr-list.ts +9 -96
- package/src/utils/buffer-helper.ts +31 -12
- package/src/utils/cea-608-parser.ts +3 -1
- package/src/utils/codecs.ts +5 -34
- package/src/utils/fetch-loader.ts +1 -1
- package/src/utils/hdr.ts +7 -4
- package/src/utils/imsc1-ttml-parser.ts +1 -1
- package/src/utils/keysystem-util.ts +6 -1
- package/src/utils/level-helper.ts +44 -71
- package/src/utils/logger.ts +23 -58
- package/src/utils/mp4-tools.ts +3 -5
- package/src/utils/rendition-helper.ts +74 -100
- package/src/utils/variable-substitution.ts +19 -0
- package/src/utils/webvtt-parser.ts +12 -2
- package/src/crypt/decrypter-aes-mode.ts +0 -4
- package/src/demux/video/hevc-video-parser.ts +0 -749
- package/src/utils/encryption-methods-util.ts +0 -21
- package/src/utils/hash.ts +0 -10
- package/src/utils/utf8-utils.ts +0 -18
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 { logger, enableLogs } from './utils/logger';
|
12
12
|
import { enableStreamingMode, hlsDefaultConfig, mergeConfig } from './config';
|
13
13
|
import { EventEmitter } from 'eventemitter3';
|
14
14
|
import { Events } from './events';
|
@@ -59,13 +59,9 @@ 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
|
-
|
67
62
|
private coreComponents: ComponentAPI[];
|
68
63
|
private networkControllers: NetworkComponentAPI[];
|
64
|
+
private started: boolean = false;
|
69
65
|
private _emitter: HlsEventEmitter = new EventEmitter();
|
70
66
|
private _autoLevelCapping: number = -1;
|
71
67
|
private _maxHdcpLevel: HdcpLevel = null;
|
@@ -80,7 +76,7 @@ export default class Hls implements HlsEventEmitter {
|
|
80
76
|
private emeController: EMEController;
|
81
77
|
private cmcdController: CMCDController;
|
82
78
|
private _media: HTMLMediaElement | null = null;
|
83
|
-
private
|
79
|
+
private url: string | null = null;
|
84
80
|
private triggeringException?: boolean;
|
85
81
|
|
86
82
|
/**
|
@@ -146,19 +142,12 @@ export default class Hls implements HlsEventEmitter {
|
|
146
142
|
* @param userConfig - Configuration options applied over `Hls.DefaultConfig`
|
147
143
|
*/
|
148
144
|
constructor(userConfig: Partial<HlsConfig> = {}) {
|
149
|
-
|
150
|
-
|
151
|
-
'Hls instance',
|
152
|
-
));
|
153
|
-
const config = (this.config = mergeConfig(
|
154
|
-
Hls.DefaultConfig,
|
155
|
-
userConfig,
|
156
|
-
logger,
|
157
|
-
));
|
145
|
+
enableLogs(userConfig.debug || false, 'Hls instance');
|
146
|
+
const config = (this.config = mergeConfig(Hls.DefaultConfig, userConfig));
|
158
147
|
this.userConfig = userConfig;
|
159
148
|
|
160
149
|
if (config.progressive) {
|
161
|
-
enableStreamingMode(config
|
150
|
+
enableStreamingMode(config);
|
162
151
|
}
|
163
152
|
|
164
153
|
// core controllers and network loaders
|
@@ -171,10 +160,8 @@ export default class Hls implements HlsEventEmitter {
|
|
171
160
|
} = config;
|
172
161
|
const errorController = new ConfigErrorController(this);
|
173
162
|
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);
|
176
163
|
const bufferController = (this.bufferController =
|
177
|
-
new ConfigBufferController(this
|
164
|
+
new ConfigBufferController(this));
|
178
165
|
const capLevelController = (this.capLevelController =
|
179
166
|
new ConfigCapLevelController(this));
|
180
167
|
|
@@ -183,7 +170,7 @@ export default class Hls implements HlsEventEmitter {
|
|
183
170
|
const id3TrackController = new ID3TrackController(this);
|
184
171
|
|
185
172
|
const ConfigContentSteeringController = config.contentSteeringController;
|
186
|
-
//
|
173
|
+
// ConentSteeringController is defined before LevelController to receive Multivariant Playlist events first
|
187
174
|
const contentSteering = ConfigContentSteeringController
|
188
175
|
? new ConfigContentSteeringController(this)
|
189
176
|
: null;
|
@@ -191,6 +178,8 @@ export default class Hls implements HlsEventEmitter {
|
|
191
178
|
this,
|
192
179
|
contentSteering,
|
193
180
|
));
|
181
|
+
// FragmentTracker must be defined before StreamController because the order of event handling is important
|
182
|
+
const fragmentTracker = new FragmentTracker(this);
|
194
183
|
const keyLoader = new KeyLoader(this.config);
|
195
184
|
const streamController = (this.streamController = new StreamController(
|
196
185
|
this,
|
@@ -232,7 +221,7 @@ export default class Hls implements HlsEventEmitter {
|
|
232
221
|
new AudioStreamControllerClass(this, fragmentTracker, keyLoader),
|
233
222
|
);
|
234
223
|
}
|
235
|
-
//
|
224
|
+
// subtitleTrackController must be defined before subtitleStreamController because the order of event handling is important
|
236
225
|
this.subtitleTrackController = this.createController(
|
237
226
|
config.subtitleTrackController,
|
238
227
|
networkControllers,
|
@@ -331,7 +320,7 @@ export default class Hls implements HlsEventEmitter {
|
|
331
320
|
try {
|
332
321
|
return this.emit(event, event, eventObject);
|
333
322
|
} catch (error) {
|
334
|
-
|
323
|
+
logger.error(
|
335
324
|
'An internal error happened while handling event ' +
|
336
325
|
event +
|
337
326
|
'. Error message: "' +
|
@@ -365,12 +354,12 @@ export default class Hls implements HlsEventEmitter {
|
|
365
354
|
* Dispose of the instance
|
366
355
|
*/
|
367
356
|
destroy() {
|
368
|
-
|
357
|
+
logger.log('destroy');
|
369
358
|
this.trigger(Events.DESTROYING, undefined);
|
370
359
|
this.detachMedia();
|
371
360
|
this.removeAllListeners();
|
372
361
|
this._autoLevelCapping = -1;
|
373
|
-
this.
|
362
|
+
this.url = null;
|
374
363
|
|
375
364
|
this.networkControllers.forEach((component) => component.destroy());
|
376
365
|
this.networkControllers.length = 0;
|
@@ -388,7 +377,7 @@ export default class Hls implements HlsEventEmitter {
|
|
388
377
|
* Attaches Hls.js to a media element
|
389
378
|
*/
|
390
379
|
attachMedia(media: HTMLMediaElement) {
|
391
|
-
|
380
|
+
logger.log('attachMedia');
|
392
381
|
this._media = media;
|
393
382
|
this.trigger(Events.MEDIA_ATTACHING, { media: media });
|
394
383
|
}
|
@@ -397,7 +386,7 @@ export default class Hls implements HlsEventEmitter {
|
|
397
386
|
* Detach Hls.js from the media
|
398
387
|
*/
|
399
388
|
detachMedia() {
|
400
|
-
|
389
|
+
logger.log('detachMedia');
|
401
390
|
this.trigger(Events.MEDIA_DETACHING, undefined);
|
402
391
|
this._media = null;
|
403
392
|
}
|
@@ -408,8 +397,8 @@ export default class Hls implements HlsEventEmitter {
|
|
408
397
|
loadSource(url: string) {
|
409
398
|
this.stopLoad();
|
410
399
|
const media = this.media;
|
411
|
-
const loadedSource = this.
|
412
|
-
const loadingSource = (this.
|
400
|
+
const loadedSource = this.url;
|
401
|
+
const loadingSource = (this.url = buildAbsoluteURL(
|
413
402
|
self.location.href,
|
414
403
|
url,
|
415
404
|
{
|
@@ -418,7 +407,7 @@ export default class Hls implements HlsEventEmitter {
|
|
418
407
|
));
|
419
408
|
this._autoLevelCapping = -1;
|
420
409
|
this._maxHdcpLevel = null;
|
421
|
-
|
410
|
+
logger.log(`loadSource:${loadingSource}`);
|
422
411
|
if (
|
423
412
|
media &&
|
424
413
|
loadedSource &&
|
@@ -431,13 +420,6 @@ export default class Hls implements HlsEventEmitter {
|
|
431
420
|
this.trigger(Events.MANIFEST_LOADING, { url: url });
|
432
421
|
}
|
433
422
|
|
434
|
-
/**
|
435
|
-
* Gets the currently loaded URL
|
436
|
-
*/
|
437
|
-
public get url(): string | null {
|
438
|
-
return this._url;
|
439
|
-
}
|
440
|
-
|
441
423
|
/**
|
442
424
|
* Start loading data from the stream source.
|
443
425
|
* Depending on default config, client starts loading automatically when a source is set.
|
@@ -446,7 +428,8 @@ export default class Hls implements HlsEventEmitter {
|
|
446
428
|
* Defaults to -1 (None: starts from earliest point)
|
447
429
|
*/
|
448
430
|
startLoad(startPosition: number = -1) {
|
449
|
-
|
431
|
+
logger.log(`startLoad(${startPosition})`);
|
432
|
+
this.started = true;
|
450
433
|
this.networkControllers.forEach((controller) => {
|
451
434
|
controller.startLoad(startPosition);
|
452
435
|
});
|
@@ -456,31 +439,34 @@ export default class Hls implements HlsEventEmitter {
|
|
456
439
|
* Stop loading of any stream data.
|
457
440
|
*/
|
458
441
|
stopLoad() {
|
459
|
-
|
442
|
+
logger.log('stopLoad');
|
443
|
+
this.started = false;
|
460
444
|
this.networkControllers.forEach((controller) => {
|
461
445
|
controller.stopLoad();
|
462
446
|
});
|
463
447
|
}
|
464
448
|
|
465
449
|
/**
|
466
|
-
* Resumes stream controller segment loading
|
450
|
+
* Resumes stream controller segment loading if previously started.
|
467
451
|
*/
|
468
452
|
resumeBuffering() {
|
469
|
-
this.
|
470
|
-
|
471
|
-
controller
|
472
|
-
|
473
|
-
|
453
|
+
if (this.started) {
|
454
|
+
this.networkControllers.forEach((controller) => {
|
455
|
+
if ('fragmentLoader' in controller) {
|
456
|
+
controller.startLoad(-1);
|
457
|
+
}
|
458
|
+
});
|
459
|
+
}
|
474
460
|
}
|
475
461
|
|
476
462
|
/**
|
477
|
-
*
|
463
|
+
* Stops stream controller segment loading without changing 'started' state like stopLoad().
|
478
464
|
* This allows for media buffering to be paused without interupting playlist loading.
|
479
465
|
*/
|
480
466
|
pauseBuffering() {
|
481
467
|
this.networkControllers.forEach((controller) => {
|
482
|
-
if (controller
|
483
|
-
controller.
|
468
|
+
if ('fragmentLoader' in controller) {
|
469
|
+
controller.stopLoad();
|
484
470
|
}
|
485
471
|
});
|
486
472
|
}
|
@@ -489,7 +475,7 @@ export default class Hls implements HlsEventEmitter {
|
|
489
475
|
* Swap through possible audio codecs in the stream (for example to switch from stereo to 5.1)
|
490
476
|
*/
|
491
477
|
swapAudioCodec() {
|
492
|
-
|
478
|
+
logger.log('swapAudioCodec');
|
493
479
|
this.streamController.swapAudioCodec();
|
494
480
|
}
|
495
481
|
|
@@ -500,7 +486,7 @@ export default class Hls implements HlsEventEmitter {
|
|
500
486
|
* Automatic recovery of media-errors by this process is configurable.
|
501
487
|
*/
|
502
488
|
recoverMediaError() {
|
503
|
-
|
489
|
+
logger.log('recoverMediaError');
|
504
490
|
const media = this._media;
|
505
491
|
this.detachMedia();
|
506
492
|
if (media) {
|
@@ -531,7 +517,7 @@ export default class Hls implements HlsEventEmitter {
|
|
531
517
|
* 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.
|
532
518
|
*/
|
533
519
|
set currentLevel(newLevel: number) {
|
534
|
-
|
520
|
+
logger.log(`set currentLevel:${newLevel}`);
|
535
521
|
this.levelController.manualLevel = newLevel;
|
536
522
|
this.streamController.immediateLevelSwitch();
|
537
523
|
}
|
@@ -550,7 +536,7 @@ export default class Hls implements HlsEventEmitter {
|
|
550
536
|
* @param newLevel - Pass -1 for automatic level selection
|
551
537
|
*/
|
552
538
|
set nextLevel(newLevel: number) {
|
553
|
-
|
539
|
+
logger.log(`set nextLevel:${newLevel}`);
|
554
540
|
this.levelController.manualLevel = newLevel;
|
555
541
|
this.streamController.nextLevelSwitch();
|
556
542
|
}
|
@@ -569,7 +555,7 @@ export default class Hls implements HlsEventEmitter {
|
|
569
555
|
* @param newLevel - Pass -1 for automatic level selection
|
570
556
|
*/
|
571
557
|
set loadLevel(newLevel: number) {
|
572
|
-
|
558
|
+
logger.log(`set loadLevel:${newLevel}`);
|
573
559
|
this.levelController.manualLevel = newLevel;
|
574
560
|
}
|
575
561
|
|
@@ -600,7 +586,7 @@ export default class Hls implements HlsEventEmitter {
|
|
600
586
|
* Sets "first-level", see getter.
|
601
587
|
*/
|
602
588
|
set firstLevel(newLevel: number) {
|
603
|
-
|
589
|
+
logger.log(`set firstLevel:${newLevel}`);
|
604
590
|
this.levelController.firstLevel = newLevel;
|
605
591
|
}
|
606
592
|
|
@@ -625,7 +611,7 @@ export default class Hls implements HlsEventEmitter {
|
|
625
611
|
* (determined from download of first segment)
|
626
612
|
*/
|
627
613
|
set startLevel(newLevel: number) {
|
628
|
-
|
614
|
+
logger.log(`set startLevel:${newLevel}`);
|
629
615
|
// if not in automatic start level detection, ensure startLevel is greater than minAutoLevel
|
630
616
|
if (newLevel !== -1) {
|
631
617
|
newLevel = Math.max(newLevel, this.minAutoLevel);
|
@@ -700,7 +686,7 @@ export default class Hls implements HlsEventEmitter {
|
|
700
686
|
*/
|
701
687
|
set autoLevelCapping(newLevel: number) {
|
702
688
|
if (this._autoLevelCapping !== newLevel) {
|
703
|
-
|
689
|
+
logger.log(`set autoLevelCapping:${newLevel}`);
|
704
690
|
this._autoLevelCapping = newLevel;
|
705
691
|
this.levelController.checkMaxAutoUpdated();
|
706
692
|
}
|
@@ -809,10 +795,6 @@ export default class Hls implements HlsEventEmitter {
|
|
809
795
|
return this.streamController.getMainFwdBufferInfo();
|
810
796
|
}
|
811
797
|
|
812
|
-
public get maxBufferLength(): number {
|
813
|
-
return this.streamController.maxBufferLength;
|
814
|
-
}
|
815
|
-
|
816
798
|
/**
|
817
799
|
* Find and select the best matching audio track, making a level switch when a Group change is necessary.
|
818
800
|
* Updates `hls.config.audioPreference`. Returns the selected track, or null when no matching track is found.
|
@@ -820,7 +802,7 @@ export default class Hls implements HlsEventEmitter {
|
|
820
802
|
public setAudioOption(
|
821
803
|
audioOption: MediaPlaylist | AudioSelectionOption | undefined,
|
822
804
|
): MediaPlaylist | null {
|
823
|
-
return this.audioTrackController?.setAudioOption(audioOption)
|
805
|
+
return this.audioTrackController?.setAudioOption(audioOption);
|
824
806
|
}
|
825
807
|
/**
|
826
808
|
* Find and select the best matching subtitle track, making a level switch when a Group change is necessary.
|
@@ -829,9 +811,8 @@ export default class Hls implements HlsEventEmitter {
|
|
829
811
|
public setSubtitleOption(
|
830
812
|
subtitleOption: MediaPlaylist | SubtitleSelectionOption | undefined,
|
831
813
|
): MediaPlaylist | null {
|
832
|
-
|
833
|
-
|
834
|
-
);
|
814
|
+
this.subtitleTrackController?.setSubtitleOption(subtitleOption);
|
815
|
+
return null;
|
835
816
|
}
|
836
817
|
|
837
818
|
/**
|
@@ -976,10 +957,6 @@ export default class Hls implements HlsEventEmitter {
|
|
976
957
|
return this.latencyController.targetLatency;
|
977
958
|
}
|
978
959
|
|
979
|
-
set targetLatency(latency: number) {
|
980
|
-
this.latencyController.targetLatency = latency;
|
981
|
-
}
|
982
|
-
|
983
960
|
/**
|
984
961
|
* the rate at which the edge of the current live playlist is advancing or 1 if there is none
|
985
962
|
*/
|
@@ -993,17 +970,6 @@ export default class Hls implements HlsEventEmitter {
|
|
993
970
|
get forceStartLoad(): boolean {
|
994
971
|
return this.streamController.forceStartLoad;
|
995
972
|
}
|
996
|
-
|
997
|
-
/**
|
998
|
-
* ContentSteering pathwayPriority getter/setter
|
999
|
-
*/
|
1000
|
-
get pathwayPriority(): string[] | null {
|
1001
|
-
return this.levelController.pathwayPriority;
|
1002
|
-
}
|
1003
|
-
|
1004
|
-
set pathwayPriority(pathwayPriority: string[]) {
|
1005
|
-
this.levelController.pathwayPriority = pathwayPriority;
|
1006
|
-
}
|
1007
973
|
}
|
1008
974
|
|
1009
975
|
export type {
|
@@ -1066,7 +1032,7 @@ export type {
|
|
1066
1032
|
TSDemuxerConfig,
|
1067
1033
|
} from './config';
|
1068
1034
|
export type { MediaKeySessionContext } from './controller/eme-controller';
|
1069
|
-
export type { ILogger
|
1035
|
+
export type { ILogger } from './utils/logger';
|
1070
1036
|
export type {
|
1071
1037
|
PathwayClone,
|
1072
1038
|
SteeringManifest,
|
@@ -1080,7 +1046,7 @@ export type {
|
|
1080
1046
|
KeySystems,
|
1081
1047
|
KeySystemFormats,
|
1082
1048
|
} from './utils/mediakeys-helper';
|
1083
|
-
export type { DateRange
|
1049
|
+
export type { DateRange } from './loader/date-range';
|
1084
1050
|
export type { LoadStats } from './loader/load-stats';
|
1085
1051
|
export type { LevelKey } from './loader/level-key';
|
1086
1052
|
export type { LevelDetails } from './loader/level-details';
|
@@ -1130,7 +1096,6 @@ export type { ChunkMetadata } from './types/transmuxer';
|
|
1130
1096
|
export type {
|
1131
1097
|
BaseSegment,
|
1132
1098
|
Fragment,
|
1133
|
-
MediaFragment,
|
1134
1099
|
Part,
|
1135
1100
|
ElementaryStreams,
|
1136
1101
|
ElementaryStreamTypes,
|
@@ -1182,7 +1147,6 @@ export type {
|
|
1182
1147
|
ManifestParsedData,
|
1183
1148
|
MediaAttachedData,
|
1184
1149
|
MediaAttachingData,
|
1185
|
-
MediaEndedData,
|
1186
1150
|
NonNativeTextTrack,
|
1187
1151
|
NonNativeTextTracksData,
|
1188
1152
|
SteeringManifestLoadedData,
|
@@ -1197,4 +1161,3 @@ export type {
|
|
1197
1161
|
IErrorAction,
|
1198
1162
|
} from './controller/error-controller';
|
1199
1163
|
export type { AttrList } from './utils/attr-list';
|
1200
|
-
export type { ParsedMultivariantPlaylist } from './loader/m3u8-parser';
|
package/src/loader/date-range.ts
CHANGED
@@ -1,12 +1,10 @@
|
|
1
1
|
import { AttrList } from '../utils/attr-list';
|
2
2
|
import { logger } from '../utils/logger';
|
3
|
-
import type { Fragment } from './fragment';
|
4
3
|
|
5
4
|
// Avoid exporting const enum so that these values can be inlined
|
6
5
|
const enum DateRangeAttribute {
|
7
6
|
ID = 'ID',
|
8
7
|
CLASS = 'CLASS',
|
9
|
-
CUE = 'CUE',
|
10
8
|
START_DATE = 'START-DATE',
|
11
9
|
DURATION = 'DURATION',
|
12
10
|
END_DATE = 'END-DATE',
|
@@ -14,22 +12,12 @@ const enum DateRangeAttribute {
|
|
14
12
|
PLANNED_DURATION = 'PLANNED-DURATION',
|
15
13
|
SCTE35_OUT = 'SCTE35-OUT',
|
16
14
|
SCTE35_IN = 'SCTE35-IN',
|
17
|
-
SCTE35_CMD = 'SCTE35-CMD',
|
18
15
|
}
|
19
16
|
|
20
|
-
export type DateRangeCue = {
|
21
|
-
pre: boolean;
|
22
|
-
post: boolean;
|
23
|
-
once: boolean;
|
24
|
-
};
|
25
|
-
|
26
|
-
const CLASS_INTERSTITIAL = 'com.apple.hls.interstitial';
|
27
|
-
|
28
17
|
export function isDateRangeCueAttribute(attrName: string): boolean {
|
29
18
|
return (
|
30
19
|
attrName !== DateRangeAttribute.ID &&
|
31
20
|
attrName !== DateRangeAttribute.CLASS &&
|
32
|
-
attrName !== DateRangeAttribute.CUE &&
|
33
21
|
attrName !== DateRangeAttribute.START_DATE &&
|
34
22
|
attrName !== DateRangeAttribute.DURATION &&
|
35
23
|
attrName !== DateRangeAttribute.END_DATE &&
|
@@ -40,27 +28,17 @@ export function isDateRangeCueAttribute(attrName: string): boolean {
|
|
40
28
|
export function isSCTE35Attribute(attrName: string): boolean {
|
41
29
|
return (
|
42
30
|
attrName === DateRangeAttribute.SCTE35_OUT ||
|
43
|
-
attrName === DateRangeAttribute.SCTE35_IN
|
44
|
-
attrName === DateRangeAttribute.SCTE35_CMD
|
31
|
+
attrName === DateRangeAttribute.SCTE35_IN
|
45
32
|
);
|
46
33
|
}
|
47
34
|
|
48
35
|
export class DateRange {
|
49
36
|
public attr: AttrList;
|
50
|
-
public tagAnchor: Fragment | null;
|
51
|
-
public tagOrder: number;
|
52
37
|
private _startDate: Date;
|
53
38
|
private _endDate?: Date;
|
54
|
-
private _cue?: DateRangeCue;
|
55
39
|
private _badValueForSameId?: string;
|
56
40
|
|
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;
|
41
|
+
constructor(dateRangeAttr: AttrList, dateRangeWithSameId?: DateRange) {
|
64
42
|
if (dateRangeWithSameId) {
|
65
43
|
const previousAttr = dateRangeWithSameId.attr;
|
66
44
|
for (const key in previousAttr) {
|
@@ -83,13 +61,9 @@ export class DateRange {
|
|
83
61
|
);
|
84
62
|
}
|
85
63
|
this.attr = dateRangeAttr;
|
86
|
-
this._startDate =
|
87
|
-
? dateRangeWithSameId.startDate
|
88
|
-
: new Date(dateRangeAttr[DateRangeAttribute.START_DATE]);
|
64
|
+
this._startDate = new Date(dateRangeAttr[DateRangeAttribute.START_DATE]);
|
89
65
|
if (DateRangeAttribute.END_DATE in this.attr) {
|
90
|
-
const endDate =
|
91
|
-
dateRangeWithSameId?.endDate ||
|
92
|
-
new Date(this.attr[DateRangeAttribute.END_DATE]);
|
66
|
+
const endDate = new Date(this.attr[DateRangeAttribute.END_DATE]);
|
93
67
|
if (Number.isFinite(endDate.getTime())) {
|
94
68
|
this._endDate = endDate;
|
95
69
|
}
|
@@ -104,36 +78,6 @@ export class DateRange {
|
|
104
78
|
return this.attr.CLASS;
|
105
79
|
}
|
106
80
|
|
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
|
-
|
137
81
|
get startDate(): Date {
|
138
82
|
return this._startDate;
|
139
83
|
}
|
@@ -176,23 +120,13 @@ export class DateRange {
|
|
176
120
|
return this.attr.bool(DateRangeAttribute.END_ON_NEXT);
|
177
121
|
}
|
178
122
|
|
179
|
-
get isInterstitial(): boolean {
|
180
|
-
return this.class === CLASS_INTERSTITIAL;
|
181
|
-
}
|
182
|
-
|
183
123
|
get isValid(): boolean {
|
184
124
|
return (
|
185
125
|
!!this.id &&
|
186
126
|
!this._badValueForSameId &&
|
187
127
|
Number.isFinite(this.startDate.getTime()) &&
|
188
128
|
(this.duration === null || this.duration >= 0) &&
|
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)
|
129
|
+
(!this.endOnNext || !!this.class)
|
196
130
|
);
|
197
131
|
}
|
198
132
|
}
|
@@ -1,17 +1,18 @@
|
|
1
1
|
import { ErrorTypes, ErrorDetails } from '../errors';
|
2
|
+
import { Fragment } from './fragment';
|
3
|
+
import {
|
4
|
+
Loader,
|
5
|
+
LoaderConfiguration,
|
6
|
+
FragmentLoaderContext,
|
7
|
+
} from '../types/loader';
|
2
8
|
import { getLoaderConfigWithoutReties } from '../utils/error-helper';
|
3
9
|
import type { HlsConfig } from '../config';
|
4
|
-
import type { BaseSegment,
|
10
|
+
import type { BaseSegment, Part } from './fragment';
|
5
11
|
import type {
|
6
12
|
ErrorData,
|
7
13
|
FragLoadedData,
|
8
14
|
PartsLoadedData,
|
9
15
|
} from '../types/events';
|
10
|
-
import type {
|
11
|
-
Loader,
|
12
|
-
LoaderConfiguration,
|
13
|
-
FragmentLoaderContext,
|
14
|
-
} from '../types/loader';
|
15
16
|
|
16
17
|
const MIN_CHUNK_SIZE = Math.pow(2, 17); // 128kb
|
17
18
|
|
@@ -76,11 +77,13 @@ export default class FragmentLoader {
|
|
76
77
|
frag.gap = false;
|
77
78
|
}
|
78
79
|
}
|
79
|
-
const loader =
|
80
|
-
|
81
|
-
|
80
|
+
const loader =
|
81
|
+
(this.loader =
|
82
|
+
frag.loader =
|
83
|
+
FragmentILoader
|
84
|
+
? new FragmentILoader(config)
|
85
|
+
: (new DefaultILoader(config) as Loader<FragmentLoaderContext>));
|
82
86
|
const loaderContext = createLoaderContext(frag);
|
83
|
-
frag.loader = loader;
|
84
87
|
const loadPolicy = getLoaderConfigWithoutReties(
|
85
88
|
config.fragLoadPolicy.default,
|
86
89
|
);
|
@@ -185,11 +188,13 @@ export default class FragmentLoader {
|
|
185
188
|
reject(createGapLoadError(frag, part));
|
186
189
|
return;
|
187
190
|
}
|
188
|
-
const loader =
|
189
|
-
|
190
|
-
|
191
|
+
const loader =
|
192
|
+
(this.loader =
|
193
|
+
frag.loader =
|
194
|
+
FragmentILoader
|
195
|
+
? new FragmentILoader(config)
|
196
|
+
: (new DefaultILoader(config) as Loader<FragmentLoaderContext>));
|
191
197
|
const loaderContext = createLoaderContext(frag, part);
|
192
|
-
frag.loader = loader;
|
193
198
|
// Should we define another load policy for parts?
|
194
199
|
const loadPolicy = getLoaderConfigWithoutReties(
|
195
200
|
config.fragLoadPolicy.default,
|
@@ -331,11 +336,8 @@ function createLoaderContext(
|
|
331
336
|
if (Number.isFinite(start) && Number.isFinite(end)) {
|
332
337
|
let byteRangeStart = start;
|
333
338
|
let byteRangeEnd = end;
|
334
|
-
if (
|
335
|
-
|
336
|
-
isMethodFullSegmentAesCbc(frag.decryptdata?.method)
|
337
|
-
) {
|
338
|
-
// MAP segment encrypted with method 'AES-128' or 'AES-256' (cbc), when served with HTTP Range,
|
339
|
+
if (frag.sn === 'initSegment' && frag.decryptdata?.method === 'AES-128') {
|
340
|
+
// MAP segment encrypted with method 'AES-128', when served with HTTP Range,
|
339
341
|
// has the unencrypted size specified in the range.
|
340
342
|
// Ref: https://tools.ietf.org/html/draft-pantos-hls-rfc8216bis-08#section-6.3.6
|
341
343
|
const fragmentLen = end - start;
|
@@ -370,10 +372,6 @@ function createGapLoadError(frag: Fragment, part?: Part): LoadError {
|
|
370
372
|
return new LoadError(errorData);
|
371
373
|
}
|
372
374
|
|
373
|
-
function isMethodFullSegmentAesCbc(method) {
|
374
|
-
return method === 'AES-128' || method === 'AES-256';
|
375
|
-
}
|
376
|
-
|
377
375
|
export class LoadError extends Error {
|
378
376
|
public readonly data: FragLoadFailResult;
|
379
377
|
constructor(data: FragLoadFailResult) {
|
package/src/loader/fragment.ts
CHANGED
@@ -90,10 +90,6 @@ export class BaseSegment {
|
|
90
90
|
}
|
91
91
|
}
|
92
92
|
|
93
|
-
export interface MediaFragment extends Fragment {
|
94
|
-
sn: number;
|
95
|
-
}
|
96
|
-
|
97
93
|
/**
|
98
94
|
* Object representing parsed data from an HLS Segment. Found in {@link hls.js#LevelDetails.fragments}.
|
99
95
|
*/
|
@@ -127,9 +123,9 @@ export class Fragment extends BaseSegment {
|
|
127
123
|
// The ending Presentation Time Stamp (PTS) of the fragment. Set after transmux complete.
|
128
124
|
public endPTS?: number;
|
129
125
|
// The starting Decode Time Stamp (DTS) of the fragment. Set after transmux complete.
|
130
|
-
public startDTS
|
126
|
+
public startDTS!: number;
|
131
127
|
// The ending Decode Time Stamp (DTS) of the fragment. Set after transmux complete.
|
132
|
-
public endDTS
|
128
|
+
public endDTS!: number;
|
133
129
|
// The start time of the fragment, as listed in the manifest. Updated after transmux complete.
|
134
130
|
public start: number = 0;
|
135
131
|
// Set by `updateFragPTSDTS` in level-helper
|
@@ -278,13 +274,13 @@ export class Part extends BaseSegment {
|
|
278
274
|
public readonly gap: boolean = false;
|
279
275
|
public readonly independent: boolean = false;
|
280
276
|
public readonly relurl: string;
|
281
|
-
public readonly fragment:
|
277
|
+
public readonly fragment: Fragment;
|
282
278
|
public readonly index: number;
|
283
279
|
public stats: LoadStats = new LoadStats();
|
284
280
|
|
285
281
|
constructor(
|
286
282
|
partAttrs: AttrList,
|
287
|
-
frag:
|
283
|
+
frag: Fragment,
|
288
284
|
baseurl: string,
|
289
285
|
index: number,
|
290
286
|
previous?: Part,
|
package/src/loader/key-loader.ts
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
import { ErrorTypes, ErrorDetails } from '../errors';
|
2
|
-
import
|
2
|
+
import {
|
3
3
|
LoaderStats,
|
4
4
|
LoaderResponse,
|
5
5
|
LoaderConfiguration,
|
@@ -194,8 +194,6 @@ 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':
|
199
197
|
return this.loadKeyHTTP(keyInfo, frag);
|
200
198
|
default:
|
201
199
|
return Promise.reject(
|