hls.js 1.5.3 → 1.5.5-0.canary.9977

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.
Files changed (68) hide show
  1. package/README.md +1 -0
  2. package/dist/hls-demo.js +10 -0
  3. package/dist/hls-demo.js.map +1 -1
  4. package/dist/hls.js +1954 -1103
  5. package/dist/hls.js.d.ts +63 -50
  6. package/dist/hls.js.map +1 -1
  7. package/dist/hls.light.js +1631 -784
  8. package/dist/hls.light.js.map +1 -1
  9. package/dist/hls.light.min.js +1 -1
  10. package/dist/hls.light.min.js.map +1 -1
  11. package/dist/hls.light.mjs +1428 -590
  12. package/dist/hls.light.mjs.map +1 -1
  13. package/dist/hls.min.js +1 -1
  14. package/dist/hls.min.js.map +1 -1
  15. package/dist/hls.mjs +1703 -866
  16. package/dist/hls.mjs.map +1 -1
  17. package/dist/hls.worker.js +1 -1
  18. package/dist/hls.worker.js.map +1 -1
  19. package/package.json +18 -18
  20. package/src/config.ts +3 -2
  21. package/src/controller/abr-controller.ts +24 -22
  22. package/src/controller/audio-stream-controller.ts +16 -17
  23. package/src/controller/audio-track-controller.ts +1 -1
  24. package/src/controller/base-playlist-controller.ts +7 -7
  25. package/src/controller/base-stream-controller.ts +56 -29
  26. package/src/controller/buffer-controller.ts +11 -11
  27. package/src/controller/cap-level-controller.ts +1 -2
  28. package/src/controller/cmcd-controller.ts +25 -3
  29. package/src/controller/content-steering-controller.ts +8 -6
  30. package/src/controller/eme-controller.ts +9 -22
  31. package/src/controller/error-controller.ts +6 -8
  32. package/src/controller/fps-controller.ts +2 -3
  33. package/src/controller/gap-controller.ts +43 -16
  34. package/src/controller/latency-controller.ts +9 -11
  35. package/src/controller/level-controller.ts +5 -17
  36. package/src/controller/stream-controller.ts +27 -33
  37. package/src/controller/subtitle-stream-controller.ts +14 -15
  38. package/src/controller/subtitle-track-controller.ts +5 -3
  39. package/src/controller/timeline-controller.ts +23 -30
  40. package/src/crypt/aes-crypto.ts +21 -2
  41. package/src/crypt/decrypter-aes-mode.ts +4 -0
  42. package/src/crypt/decrypter.ts +32 -18
  43. package/src/crypt/fast-aes-key.ts +24 -5
  44. package/src/demux/audio/adts.ts +9 -4
  45. package/src/demux/sample-aes.ts +2 -0
  46. package/src/demux/transmuxer-interface.ts +4 -12
  47. package/src/demux/transmuxer-worker.ts +4 -4
  48. package/src/demux/transmuxer.ts +16 -3
  49. package/src/demux/tsdemuxer.ts +63 -37
  50. package/src/demux/video/avc-video-parser.ts +208 -119
  51. package/src/demux/video/base-video-parser.ts +134 -2
  52. package/src/demux/video/exp-golomb.ts +0 -208
  53. package/src/demux/video/hevc-video-parser.ts +746 -0
  54. package/src/events.ts +7 -0
  55. package/src/hls.ts +42 -34
  56. package/src/loader/fragment-loader.ts +9 -2
  57. package/src/loader/key-loader.ts +2 -0
  58. package/src/loader/level-key.ts +10 -9
  59. package/src/remux/mp4-generator.ts +196 -1
  60. package/src/remux/mp4-remuxer.ts +23 -7
  61. package/src/task-loop.ts +5 -2
  62. package/src/types/component-api.ts +2 -0
  63. package/src/types/demuxer.ts +3 -0
  64. package/src/types/events.ts +4 -0
  65. package/src/utils/codecs.ts +33 -4
  66. package/src/utils/encryption-methods-util.ts +21 -0
  67. package/src/utils/logger.ts +53 -24
  68. package/src/utils/mp4-tools.ts +28 -9
@@ -64,6 +64,7 @@ export interface DemuxedAudioTrack extends DemuxedTrack {
64
64
  segmentCodec?: string;
65
65
  channelCount?: number;
66
66
  manifestCodec?: string;
67
+ parsedCodec?: string;
67
68
  samples: AudioSample[];
68
69
  }
69
70
 
@@ -72,12 +73,14 @@ export interface DemuxedVideoTrackBase extends DemuxedTrack {
72
73
  height?: number;
73
74
  pixelRatio?: [number, number];
74
75
  audFound?: boolean;
76
+ vps?: Uint8Array[];
75
77
  pps?: Uint8Array[];
76
78
  sps?: Uint8Array[];
77
79
  naluState?: number;
78
80
  segmentCodec?: string;
79
81
  manifestCodec?: string;
80
82
  samples: VideoSample[] | Uint8Array;
83
+ params?: object;
81
84
  }
82
85
 
83
86
  export interface DemuxedVideoTrack extends DemuxedVideoTrackBase {
@@ -42,6 +42,10 @@ export interface MediaAttachedData {
42
42
  mediaSource?: MediaSource;
43
43
  }
44
44
 
45
+ export interface MediaEndedData {
46
+ stalled: boolean;
47
+ }
48
+
45
49
  export interface BufferCodecsData {
46
50
  video?: Track;
47
51
  audio?: Track;
@@ -147,12 +147,15 @@ function getCodecCompatibleNameLower(
147
147
  return CODEC_COMPATIBLE_NAMES[lowerCaseCodec]!;
148
148
  }
149
149
 
150
- // Idealy fLaC and Opus would be first (spec-compliant) but
151
- // some browsers will report that fLaC is supported then fail.
152
- // see: https://bugs.chromium.org/p/chromium/issues/detail?id=1422728
153
150
  const codecsToCheck = {
151
+ // Idealy fLaC and Opus would be first (spec-compliant) but
152
+ // some browsers will report that fLaC is supported then fail.
153
+ // see: https://bugs.chromium.org/p/chromium/issues/detail?id=1422728
154
154
  flac: ['flac', 'fLaC', 'FLAC'],
155
155
  opus: ['opus', 'Opus'],
156
+ // Replace audio codec info if browser does not support mp4a.40.34,
157
+ // and demuxer can fallback to 'audio/mpeg' or 'audio/mp4;codecs="mp3"'
158
+ 'mp4a.40.34': ['mp3'],
156
159
  }[lowerCaseCodec];
157
160
 
158
161
  for (let i = 0; i < codecsToCheck.length; i++) {
@@ -165,13 +168,18 @@ function getCodecCompatibleNameLower(
165
168
  ) {
166
169
  CODEC_COMPATIBLE_NAMES[lowerCaseCodec] = codecsToCheck[i];
167
170
  return codecsToCheck[i];
171
+ } else if (
172
+ codecsToCheck[i] === 'mp3' &&
173
+ getMediaSource(preferManagedMediaSource)?.isTypeSupported('audio/mpeg')
174
+ ) {
175
+ return '';
168
176
  }
169
177
  }
170
178
 
171
179
  return lowerCaseCodec;
172
180
  }
173
181
 
174
- const AUDIO_CODEC_REGEXP = /flac|opus/i;
182
+ const AUDIO_CODEC_REGEXP = /flac|opus|mp4a\.40\.34/i;
175
183
  export function getCodecCompatibleName(
176
184
  codec: string,
177
185
  preferManagedMediaSource = true,
@@ -209,3 +217,24 @@ export function convertAVC1ToAVCOTI(codec: string) {
209
217
  }
210
218
  return codec;
211
219
  }
220
+
221
+ export interface TypeSupported {
222
+ mpeg: boolean;
223
+ mp3: boolean;
224
+ ac3: boolean;
225
+ }
226
+
227
+ export function getM2TSSupportedAudioTypes(
228
+ preferManagedMediaSource: boolean,
229
+ ): TypeSupported {
230
+ const MediaSource = getMediaSource(preferManagedMediaSource) || {
231
+ isTypeSupported: () => false,
232
+ };
233
+ return {
234
+ mpeg: MediaSource.isTypeSupported('audio/mpeg'),
235
+ mp3: MediaSource.isTypeSupported('audio/mp4; codecs="mp3"'),
236
+ ac3: __USE_M2TS_ADVANCED_CODECS__
237
+ ? MediaSource.isTypeSupported('audio/mp4; codecs="ac-3"')
238
+ : false,
239
+ };
240
+ }
@@ -0,0 +1,21 @@
1
+ import { DecrypterAesMode } from '../crypt/decrypter-aes-mode';
2
+
3
+ export function isFullSegmentEncryption(method: string): boolean {
4
+ return (
5
+ method === 'AES-128' || method === 'AES-256' || method === 'AES-256-CTR'
6
+ );
7
+ }
8
+
9
+ export function getAesModeFromFullSegmentMethod(
10
+ method: string,
11
+ ): DecrypterAesMode {
12
+ switch (method) {
13
+ case 'AES-128':
14
+ case 'AES-256':
15
+ return DecrypterAesMode.cbc;
16
+ case 'AES-256-CTR':
17
+ return DecrypterAesMode.ctr;
18
+ default:
19
+ throw new Error(`invalid full segment method ${method}`);
20
+ }
21
+ }
@@ -11,6 +11,25 @@ export interface ILogger {
11
11
  error: ILogFunction;
12
12
  }
13
13
 
14
+ export class Logger implements ILogger {
15
+ trace: ILogFunction;
16
+ debug: ILogFunction;
17
+ log: ILogFunction;
18
+ warn: ILogFunction;
19
+ info: ILogFunction;
20
+ error: ILogFunction;
21
+
22
+ constructor(label: string, logger: ILogger) {
23
+ const lb = `[${label}]:`;
24
+ this.trace = noop;
25
+ this.debug = logger.debug.bind(null, lb);
26
+ this.log = logger.log.bind(null, lb);
27
+ this.warn = logger.warn.bind(null, lb);
28
+ this.info = logger.info.bind(null, lb);
29
+ this.error = logger.error.bind(null, lb);
30
+ }
31
+ }
32
+
14
33
  const noop: ILogFunction = function () {};
15
34
 
16
35
  const fakeLogger: ILogger = {
@@ -22,7 +41,9 @@ const fakeLogger: ILogger = {
22
41
  error: noop,
23
42
  };
24
43
 
25
- let exportedLogger: ILogger = fakeLogger;
44
+ function createLogger() {
45
+ return Object.assign({}, fakeLogger);
46
+ }
26
47
 
27
48
  // let lastCallTime;
28
49
  // function formatMsgWithTimeInfo(type, msg) {
@@ -33,33 +54,37 @@ let exportedLogger: ILogger = fakeLogger;
33
54
  // return msg;
34
55
  // }
35
56
 
36
- function consolePrintFn(type: string): ILogFunction {
57
+ function consolePrintFn(type: string, id: string | undefined): ILogFunction {
37
58
  const func: ILogFunction = self.console[type];
38
- if (func) {
39
- return func.bind(self.console, `[${type}] >`);
40
- }
41
- return noop;
59
+ return func
60
+ ? func.bind(self.console, `${id ? '[' + id + '] ' : ''}[${type}] >`)
61
+ : noop;
42
62
  }
43
63
 
44
- function exportLoggerFunctions(
45
- debugConfig: boolean | ILogger,
46
- ...functions: string[]
47
- ): void {
48
- functions.forEach(function (type) {
49
- exportedLogger[type] = debugConfig[type]
50
- ? debugConfig[type].bind(debugConfig)
51
- : consolePrintFn(type);
52
- });
64
+ function getLoggerFn(
65
+ key: string,
66
+ debugConfig: boolean | Partial<ILogger>,
67
+ id: string | undefined,
68
+ ): ILogFunction {
69
+ return debugConfig[key]
70
+ ? debugConfig[key].bind(debugConfig)
71
+ : consolePrintFn(key, id);
53
72
  }
54
73
 
55
- export function enableLogs(debugConfig: boolean | ILogger, id: string): void {
74
+ let exportedLogger: ILogger = createLogger();
75
+
76
+ export function enableLogs(
77
+ debugConfig: boolean | ILogger,
78
+ context: string,
79
+ id?: string | undefined,
80
+ ): ILogger {
56
81
  // check that console is available
82
+ const newLogger = createLogger();
57
83
  if (
58
84
  (typeof console === 'object' && debugConfig === true) ||
59
85
  typeof debugConfig === 'object'
60
86
  ) {
61
- exportLoggerFunctions(
62
- debugConfig,
87
+ const keys: (keyof ILogger)[] = [
63
88
  // Remove out from list here to hard-disable a log-level
64
89
  // 'trace',
65
90
  'debug',
@@ -67,19 +92,23 @@ export function enableLogs(debugConfig: boolean | ILogger, id: string): void {
67
92
  'info',
68
93
  'warn',
69
94
  'error',
70
- );
95
+ ];
96
+ keys.forEach((key) => {
97
+ newLogger[key] = getLoggerFn(key, debugConfig, id);
98
+ });
71
99
  // Some browsers don't allow to use bind on console object anyway
72
100
  // fallback to default if needed
73
101
  try {
74
- exportedLogger.log(
75
- `Debug logs enabled for "${id}" in hls.js version ${__VERSION__}`,
102
+ newLogger.log(
103
+ `Debug logs enabled for "${context}" in hls.js version ${__VERSION__}`,
76
104
  );
77
105
  } catch (e) {
78
- exportedLogger = fakeLogger;
106
+ /* log fn threw an exception. All logger methods are no-ops. */
107
+ return createLogger();
79
108
  }
80
- } else {
81
- exportedLogger = fakeLogger;
82
109
  }
110
+ exportedLogger = newLogger;
111
+ return newLogger;
83
112
  }
84
113
 
85
114
  export const logger: ILogger = exportedLogger;
@@ -38,6 +38,13 @@ export function readUint32(buffer: Uint8Array, offset: number): number {
38
38
  return val < 0 ? 4294967296 + val : val;
39
39
  }
40
40
 
41
+ export function readUint64(buffer: Uint8Array, offset: number) {
42
+ let result = readUint32(buffer, offset);
43
+ result *= Math.pow(2, 32);
44
+ result += readUint32(buffer, offset + 4);
45
+ return result;
46
+ }
47
+
41
48
  export function readSint32(buffer: Uint8Array, offset: number): number {
42
49
  return (
43
50
  (buffer[offset] << 24) |
@@ -125,15 +132,15 @@ export function parseSegmentIndex(sidx: Uint8Array): SidxInfo | null {
125
132
  const timescale = readUint32(sidx, index);
126
133
  index += 4;
127
134
 
128
- // TODO: parse earliestPresentationTime and firstOffset
129
- // usually zero in our case
130
- const earliestPresentationTime = 0;
131
- const firstOffset = 0;
135
+ let earliestPresentationTime = 0;
136
+ let firstOffset = 0;
132
137
 
133
138
  if (version === 0) {
134
- index += 8;
139
+ earliestPresentationTime = readUint32(sidx, (index += 4));
140
+ firstOffset = readUint32(sidx, (index += 4));
135
141
  } else {
136
- index += 16;
142
+ earliestPresentationTime = readUint64(sidx, (index += 8));
143
+ firstOffset = readUint64(sidx, (index += 8));
137
144
  }
138
145
 
139
146
  // skip reserved
@@ -677,19 +684,31 @@ export function getDuration(data: Uint8Array, initData: InitData) {
677
684
  }
678
685
  if (videoDuration === 0 && audioDuration === 0) {
679
686
  // If duration samples are not available in the traf use sidx subsegment_duration
687
+ let sidxMinStart = Infinity;
688
+ let sidxMaxEnd = 0;
680
689
  let sidxDuration = 0;
681
690
  const sidxs = findBox(data, ['sidx']);
682
691
  for (let i = 0; i < sidxs.length; i++) {
683
692
  const sidx = parseSegmentIndex(sidxs[i]);
684
693
  if (sidx?.references) {
685
- sidxDuration += sidx.references.reduce(
694
+ sidxMinStart = Math.min(
695
+ sidxMinStart,
696
+ sidx.earliestPresentationTime / sidx.timescale,
697
+ );
698
+ const subSegmentDuration = sidx.references.reduce(
686
699
  (dur, ref) => dur + ref.info.duration || 0,
687
700
  0,
688
701
  );
702
+ sidxMaxEnd = Math.max(
703
+ sidxMaxEnd,
704
+ subSegmentDuration + sidx.earliestPresentationTime / sidx.timescale,
705
+ );
706
+ sidxDuration = sidxMaxEnd - sidxMinStart;
689
707
  }
690
708
  }
691
-
692
- return sidxDuration;
709
+ if (sidxDuration && Number.isFinite(sidxDuration)) {
710
+ return sidxDuration;
711
+ }
693
712
  }
694
713
  if (videoDuration) {
695
714
  return videoDuration;