hls.js 1.5.11 → 1.5.12-0.canary.10340
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 +3477 -2195
- package/dist/hls.js.d.ts +108 -85
- package/dist/hls.js.map +1 -1
- package/dist/hls.light.js +2401 -1754
- 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 +2148 -1476
- 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 +2863 -1558
- 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 +36 -36
- package/src/config.ts +3 -2
- package/src/controller/abr-controller.ts +24 -20
- package/src/controller/audio-stream-controller.ts +68 -74
- 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 +160 -38
- package/src/controller/buffer-controller.ts +230 -92
- 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 +6 -8
- package/src/controller/fps-controller.ts +8 -3
- package/src/controller/fragment-tracker.ts +15 -11
- package/src/controller/gap-controller.ts +43 -16
- package/src/controller/id3-track-controller.ts +7 -7
- package/src/controller/latency-controller.ts +9 -11
- package/src/controller/level-controller.ts +37 -19
- package/src/controller/stream-controller.ts +37 -32
- package/src/controller/subtitle-stream-controller.ts +28 -40
- package/src/controller/subtitle-track-controller.ts +5 -3
- package/src/controller/timeline-controller.ts +19 -21
- 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 +4 -12
- 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 +208 -119
- package/src/demux/video/base-video-parser.ts +147 -18
- 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 +61 -38
- package/src/loader/fragment-loader.ts +10 -3
- package/src/loader/key-loader.ts +3 -1
- package/src/loader/level-key.ts +10 -9
- package/src/loader/playlist-loader.ts +4 -5
- package/src/remux/mp4-generator.ts +196 -1
- package/src/remux/mp4-remuxer.ts +24 -8
- package/src/task-loop.ts +5 -2
- package/src/types/component-api.ts +3 -1
- package/src/types/demuxer.ts +4 -0
- package/src/types/events.ts +4 -0
- package/src/types/remuxer.ts +1 -1
- 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/imsc1-ttml-parser.ts +1 -1
- package/src/utils/keysystem-util.ts +1 -6
- package/src/utils/logger.ts +58 -23
- package/src/utils/mp4-tools.ts +5 -3
- package/src/utils/utf8-utils.ts +18 -0
- package/src/utils/webvtt-parser.ts +1 -1
- package/src/demux/id3.ts +0 -411
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,
|
@@ -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.
|
@@ -970,6 +981,17 @@ export default class Hls implements HlsEventEmitter {
|
|
970
981
|
get forceStartLoad(): boolean {
|
971
982
|
return this.streamController.forceStartLoad;
|
972
983
|
}
|
984
|
+
|
985
|
+
/**
|
986
|
+
* ContentSteering pathwayPriority getter/setter
|
987
|
+
*/
|
988
|
+
get pathwayPriority(): string[] | null {
|
989
|
+
return this.levelController.pathwayPriority;
|
990
|
+
}
|
991
|
+
|
992
|
+
set pathwayPriority(pathwayPriority: string[]) {
|
993
|
+
this.levelController.pathwayPriority = pathwayPriority;
|
994
|
+
}
|
973
995
|
}
|
974
996
|
|
975
997
|
export type {
|
@@ -1032,7 +1054,7 @@ export type {
|
|
1032
1054
|
TSDemuxerConfig,
|
1033
1055
|
} from './config';
|
1034
1056
|
export type { MediaKeySessionContext } from './controller/eme-controller';
|
1035
|
-
export type { ILogger } from './utils/logger';
|
1057
|
+
export type { ILogger, Logger } from './utils/logger';
|
1036
1058
|
export type {
|
1037
1059
|
PathwayClone,
|
1038
1060
|
SteeringManifest,
|
@@ -1147,6 +1169,7 @@ export type {
|
|
1147
1169
|
ManifestParsedData,
|
1148
1170
|
MediaAttachedData,
|
1149
1171
|
MediaAttachingData,
|
1172
|
+
MediaEndedData,
|
1150
1173
|
NonNativeTextTrack,
|
1151
1174
|
NonNativeTextTracksData,
|
1152
1175
|
SteeringManifestLoadedData,
|
@@ -1,6 +1,6 @@
|
|
1
1
|
import { ErrorTypes, ErrorDetails } from '../errors';
|
2
2
|
import { Fragment } from './fragment';
|
3
|
-
import {
|
3
|
+
import type {
|
4
4
|
Loader,
|
5
5
|
LoaderConfiguration,
|
6
6
|
FragmentLoaderContext,
|
@@ -336,8 +336,11 @@ function createLoaderContext(
|
|
336
336
|
if (Number.isFinite(start) && Number.isFinite(end)) {
|
337
337
|
let byteRangeStart = start;
|
338
338
|
let byteRangeEnd = end;
|
339
|
-
if (
|
340
|
-
|
339
|
+
if (
|
340
|
+
frag.sn === 'initSegment' &&
|
341
|
+
isMethodFullSegmentAesCbc(frag.decryptdata?.method)
|
342
|
+
) {
|
343
|
+
// MAP segment encrypted with method 'AES-128' or 'AES-256' (cbc), when served with HTTP Range,
|
341
344
|
// has the unencrypted size specified in the range.
|
342
345
|
// Ref: https://tools.ietf.org/html/draft-pantos-hls-rfc8216bis-08#section-6.3.6
|
343
346
|
const fragmentLen = end - start;
|
@@ -372,6 +375,10 @@ function createGapLoadError(frag: Fragment, part?: Part): LoadError {
|
|
372
375
|
return new LoadError(errorData);
|
373
376
|
}
|
374
377
|
|
378
|
+
function isMethodFullSegmentAesCbc(method) {
|
379
|
+
return method === 'AES-128' || method === 'AES-256';
|
380
|
+
}
|
381
|
+
|
375
382
|
export class LoadError extends Error {
|
376
383
|
public readonly data: FragLoadFailResult;
|
377
384
|
constructor(data: FragLoadFailResult) {
|
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(
|
package/src/loader/level-key.ts
CHANGED
@@ -2,6 +2,7 @@ import {
|
|
2
2
|
changeEndianness,
|
3
3
|
convertDataUriToArrayBytes,
|
4
4
|
} from '../utils/keysystem-util';
|
5
|
+
import { isFullSegmentEncryption } from '../utils/encryption-methods-util';
|
5
6
|
import { KeySystemFormats } from '../utils/mediakeys-helper';
|
6
7
|
import { mp4pssh } from '../utils/mp4-tools';
|
7
8
|
import { logger } from '../utils/logger';
|
@@ -51,13 +52,14 @@ export class LevelKey implements DecryptData {
|
|
51
52
|
this.keyFormatVersions = formatversions;
|
52
53
|
this.iv = iv;
|
53
54
|
this.encrypted = method ? method !== 'NONE' : false;
|
54
|
-
this.isCommonEncryption =
|
55
|
+
this.isCommonEncryption =
|
56
|
+
this.encrypted && !isFullSegmentEncryption(method);
|
55
57
|
}
|
56
58
|
|
57
59
|
public isSupported(): boolean {
|
58
60
|
// If it's Segment encryption or No encryption, just select that key system
|
59
61
|
if (this.method) {
|
60
|
-
if (this.method
|
62
|
+
if (isFullSegmentEncryption(this.method) || this.method === 'NONE') {
|
61
63
|
return true;
|
62
64
|
}
|
63
65
|
if (this.keyFormat === 'identity') {
|
@@ -88,16 +90,15 @@ export class LevelKey implements DecryptData {
|
|
88
90
|
return null;
|
89
91
|
}
|
90
92
|
|
91
|
-
if (this.method
|
93
|
+
if (isFullSegmentEncryption(this.method) && this.uri && !this.iv) {
|
92
94
|
if (typeof sn !== 'number') {
|
93
95
|
// We are fetching decryption data for a initialization segment
|
94
|
-
// If the segment was encrypted with AES-128
|
96
|
+
// If the segment was encrypted with AES-128/256
|
95
97
|
// It must have an IV defined. We cannot substitute the Segment Number in.
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
}
|
98
|
+
logger.warn(
|
99
|
+
`missing IV for initialization segment with method="${this.method}" - compliance issue`,
|
100
|
+
);
|
101
|
+
|
101
102
|
// Explicitly set sn to resulting value from implicit conversions 'initSegment' values for IV generation.
|
102
103
|
sn = 0;
|
103
104
|
}
|
@@ -8,7 +8,6 @@
|
|
8
8
|
|
9
9
|
import { Events } from '../events';
|
10
10
|
import { ErrorDetails, ErrorTypes } from '../errors';
|
11
|
-
import { logger } from '../utils/logger';
|
12
11
|
import M3U8Parser from './m3u8-parser';
|
13
12
|
import type { LevelParsed, VariableMap } from '../types/level';
|
14
13
|
import type {
|
@@ -221,10 +220,10 @@ class PlaylistLoader implements NetworkComponentAPI {
|
|
221
220
|
loaderContext.level === context.level
|
222
221
|
) {
|
223
222
|
// same URL can't overlap
|
224
|
-
logger.trace('[playlist-loader]: playlist request ongoing');
|
223
|
+
this.hls.logger.trace('[playlist-loader]: playlist request ongoing');
|
225
224
|
return;
|
226
225
|
}
|
227
|
-
logger.log(
|
226
|
+
this.hls.logger.log(
|
228
227
|
`[playlist-loader]: aborting previous loader for type: ${context.type}`,
|
229
228
|
);
|
230
229
|
loader.abort();
|
@@ -408,7 +407,7 @@ class PlaylistLoader implements NetworkComponentAPI {
|
|
408
407
|
levels[0].audioCodec &&
|
409
408
|
!levels[0].attrs.AUDIO
|
410
409
|
) {
|
411
|
-
logger.log(
|
410
|
+
this.hls.logger.log(
|
412
411
|
'[playlist-loader]: audio codec signaled in quality level, but no embedded audio track signaled, create one',
|
413
412
|
);
|
414
413
|
audioTracks.unshift({
|
@@ -555,7 +554,7 @@ class PlaylistLoader implements NetworkComponentAPI {
|
|
555
554
|
message += ` id: ${context.id} group-id: "${context.groupId}"`;
|
556
555
|
}
|
557
556
|
const error = new Error(message);
|
558
|
-
logger.warn(`[playlist-loader]: ${message}`);
|
557
|
+
this.hls.logger.warn(`[playlist-loader]: ${message}`);
|
559
558
|
let details = ErrorDetails.UNKNOWN;
|
560
559
|
let fatal = false;
|
561
560
|
|
@@ -28,6 +28,8 @@ class MP4 {
|
|
28
28
|
MP4.types = {
|
29
29
|
avc1: [], // codingname
|
30
30
|
avcC: [],
|
31
|
+
hvc1: [],
|
32
|
+
hvcC: [],
|
31
33
|
btrt: [],
|
32
34
|
dinf: [],
|
33
35
|
dref: [],
|
@@ -839,8 +841,10 @@ class MP4 {
|
|
839
841
|
return MP4.box(MP4.types.stsd, MP4.STSD, MP4.ac3(track));
|
840
842
|
}
|
841
843
|
return MP4.box(MP4.types.stsd, MP4.STSD, MP4.mp4a(track));
|
842
|
-
} else {
|
844
|
+
} else if (track.segmentCodec === 'avc') {
|
843
845
|
return MP4.box(MP4.types.stsd, MP4.STSD, MP4.avc1(track));
|
846
|
+
} else {
|
847
|
+
return MP4.box(MP4.types.stsd, MP4.STSD, MP4.hvc1(track));
|
844
848
|
}
|
845
849
|
}
|
846
850
|
|
@@ -1123,6 +1127,197 @@ class MP4 {
|
|
1123
1127
|
const result = appendUint8Array(MP4.FTYP, movie);
|
1124
1128
|
return result;
|
1125
1129
|
}
|
1130
|
+
|
1131
|
+
static hvc1(track) {
|
1132
|
+
const ps = track.params;
|
1133
|
+
const units = [track.vps, track.sps, track.pps];
|
1134
|
+
const NALuLengthSize = 4;
|
1135
|
+
const config = new Uint8Array([
|
1136
|
+
0x01,
|
1137
|
+
(ps.general_profile_space << 6) |
|
1138
|
+
(ps.general_tier_flag ? 32 : 0) |
|
1139
|
+
ps.general_profile_idc,
|
1140
|
+
ps.general_profile_compatibility_flags[0],
|
1141
|
+
ps.general_profile_compatibility_flags[1],
|
1142
|
+
ps.general_profile_compatibility_flags[2],
|
1143
|
+
ps.general_profile_compatibility_flags[3],
|
1144
|
+
ps.general_constraint_indicator_flags[0],
|
1145
|
+
ps.general_constraint_indicator_flags[1],
|
1146
|
+
ps.general_constraint_indicator_flags[2],
|
1147
|
+
ps.general_constraint_indicator_flags[3],
|
1148
|
+
ps.general_constraint_indicator_flags[4],
|
1149
|
+
ps.general_constraint_indicator_flags[5],
|
1150
|
+
ps.general_level_idc,
|
1151
|
+
240 | (ps.min_spatial_segmentation_idc >> 8),
|
1152
|
+
255 & ps.min_spatial_segmentation_idc,
|
1153
|
+
252 | ps.parallelismType,
|
1154
|
+
252 | ps.chroma_format_idc,
|
1155
|
+
248 | ps.bit_depth_luma_minus8,
|
1156
|
+
248 | ps.bit_depth_chroma_minus8,
|
1157
|
+
0x00,
|
1158
|
+
parseInt(ps.frame_rate.fps),
|
1159
|
+
(NALuLengthSize - 1) |
|
1160
|
+
(ps.temporal_id_nested << 2) |
|
1161
|
+
(ps.num_temporal_layers << 3) |
|
1162
|
+
(ps.frame_rate.fixed ? 64 : 0),
|
1163
|
+
units.length,
|
1164
|
+
]);
|
1165
|
+
|
1166
|
+
// compute hvcC size in bytes
|
1167
|
+
let length = config.length;
|
1168
|
+
for (let i = 0; i < units.length; i += 1) {
|
1169
|
+
length += 3;
|
1170
|
+
for (let j = 0; j < units[i].length; j += 1) {
|
1171
|
+
length += 2 + units[i][j].length;
|
1172
|
+
}
|
1173
|
+
}
|
1174
|
+
|
1175
|
+
const hvcC = new Uint8Array(length);
|
1176
|
+
hvcC.set(config, 0);
|
1177
|
+
length = config.length;
|
1178
|
+
// append parameter set units: one vps, one or more sps and pps
|
1179
|
+
const iMax = units.length - 1;
|
1180
|
+
for (let i = 0; i < units.length; i += 1) {
|
1181
|
+
hvcC.set(
|
1182
|
+
new Uint8Array([
|
1183
|
+
(32 + i) | (i === iMax ? 128 : 0),
|
1184
|
+
0x00,
|
1185
|
+
units[i].length,
|
1186
|
+
]),
|
1187
|
+
length,
|
1188
|
+
);
|
1189
|
+
length += 3;
|
1190
|
+
for (let j = 0; j < units[i].length; j += 1) {
|
1191
|
+
hvcC.set(
|
1192
|
+
new Uint8Array([units[i][j].length >> 8, units[i][j].length & 255]),
|
1193
|
+
length,
|
1194
|
+
);
|
1195
|
+
length += 2;
|
1196
|
+
hvcC.set(units[i][j], length);
|
1197
|
+
length += units[i][j].length;
|
1198
|
+
}
|
1199
|
+
}
|
1200
|
+
const hvcc = MP4.box(MP4.types.hvcC, hvcC);
|
1201
|
+
const width = track.width;
|
1202
|
+
const height = track.height;
|
1203
|
+
const hSpacing = track.pixelRatio[0];
|
1204
|
+
const vSpacing = track.pixelRatio[1];
|
1205
|
+
|
1206
|
+
return MP4.box(
|
1207
|
+
MP4.types.hvc1,
|
1208
|
+
new Uint8Array([
|
1209
|
+
0x00,
|
1210
|
+
0x00,
|
1211
|
+
0x00, // reserved
|
1212
|
+
0x00,
|
1213
|
+
0x00,
|
1214
|
+
0x00, // reserved
|
1215
|
+
0x00,
|
1216
|
+
0x01, // data_reference_index
|
1217
|
+
0x00,
|
1218
|
+
0x00, // pre_defined
|
1219
|
+
0x00,
|
1220
|
+
0x00, // reserved
|
1221
|
+
0x00,
|
1222
|
+
0x00,
|
1223
|
+
0x00,
|
1224
|
+
0x00,
|
1225
|
+
0x00,
|
1226
|
+
0x00,
|
1227
|
+
0x00,
|
1228
|
+
0x00,
|
1229
|
+
0x00,
|
1230
|
+
0x00,
|
1231
|
+
0x00,
|
1232
|
+
0x00, // pre_defined
|
1233
|
+
(width >> 8) & 0xff,
|
1234
|
+
width & 0xff, // width
|
1235
|
+
(height >> 8) & 0xff,
|
1236
|
+
height & 0xff, // height
|
1237
|
+
0x00,
|
1238
|
+
0x48,
|
1239
|
+
0x00,
|
1240
|
+
0x00, // horizresolution
|
1241
|
+
0x00,
|
1242
|
+
0x48,
|
1243
|
+
0x00,
|
1244
|
+
0x00, // vertresolution
|
1245
|
+
0x00,
|
1246
|
+
0x00,
|
1247
|
+
0x00,
|
1248
|
+
0x00, // reserved
|
1249
|
+
0x00,
|
1250
|
+
0x01, // frame_count
|
1251
|
+
0x12,
|
1252
|
+
0x64,
|
1253
|
+
0x61,
|
1254
|
+
0x69,
|
1255
|
+
0x6c, // dailymotion/hls.js
|
1256
|
+
0x79,
|
1257
|
+
0x6d,
|
1258
|
+
0x6f,
|
1259
|
+
0x74,
|
1260
|
+
0x69,
|
1261
|
+
0x6f,
|
1262
|
+
0x6e,
|
1263
|
+
0x2f,
|
1264
|
+
0x68,
|
1265
|
+
0x6c,
|
1266
|
+
0x73,
|
1267
|
+
0x2e,
|
1268
|
+
0x6a,
|
1269
|
+
0x73,
|
1270
|
+
0x00,
|
1271
|
+
0x00,
|
1272
|
+
0x00,
|
1273
|
+
0x00,
|
1274
|
+
0x00,
|
1275
|
+
0x00,
|
1276
|
+
0x00,
|
1277
|
+
0x00,
|
1278
|
+
0x00,
|
1279
|
+
0x00,
|
1280
|
+
0x00,
|
1281
|
+
0x00,
|
1282
|
+
0x00, // compressorname
|
1283
|
+
0x00,
|
1284
|
+
0x18, // depth = 24
|
1285
|
+
0x11,
|
1286
|
+
0x11,
|
1287
|
+
]), // pre_defined = -1
|
1288
|
+
hvcc,
|
1289
|
+
MP4.box(
|
1290
|
+
MP4.types.btrt,
|
1291
|
+
new Uint8Array([
|
1292
|
+
0x00,
|
1293
|
+
0x1c,
|
1294
|
+
0x9c,
|
1295
|
+
0x80, // bufferSizeDB
|
1296
|
+
0x00,
|
1297
|
+
0x2d,
|
1298
|
+
0xc6,
|
1299
|
+
0xc0, // maxBitrate
|
1300
|
+
0x00,
|
1301
|
+
0x2d,
|
1302
|
+
0xc6,
|
1303
|
+
0xc0,
|
1304
|
+
]),
|
1305
|
+
), // avgBitrate
|
1306
|
+
MP4.box(
|
1307
|
+
MP4.types.pasp,
|
1308
|
+
new Uint8Array([
|
1309
|
+
hSpacing >> 24, // hSpacing
|
1310
|
+
(hSpacing >> 16) & 0xff,
|
1311
|
+
(hSpacing >> 8) & 0xff,
|
1312
|
+
hSpacing & 0xff,
|
1313
|
+
vSpacing >> 24, // vSpacing
|
1314
|
+
(vSpacing >> 16) & 0xff,
|
1315
|
+
(vSpacing >> 8) & 0xff,
|
1316
|
+
vSpacing & 0xff,
|
1317
|
+
]),
|
1318
|
+
),
|
1319
|
+
);
|
1320
|
+
}
|
1126
1321
|
}
|
1127
1322
|
|
1128
1323
|
export default MP4;
|