hls.js 1.5.11 → 1.5.12-0.canary.10338

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 (88) hide show
  1. package/README.md +4 -3
  2. package/dist/hls-demo.js +41 -38
  3. package/dist/hls-demo.js.map +1 -1
  4. package/dist/hls.js +3477 -2195
  5. package/dist/hls.js.d.ts +108 -85
  6. package/dist/hls.js.map +1 -1
  7. package/dist/hls.light.js +2401 -1754
  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 +2148 -1476
  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 +2863 -1558
  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 +36 -36
  20. package/src/config.ts +3 -2
  21. package/src/controller/abr-controller.ts +24 -20
  22. package/src/controller/audio-stream-controller.ts +68 -74
  23. package/src/controller/audio-track-controller.ts +1 -1
  24. package/src/controller/base-playlist-controller.ts +27 -10
  25. package/src/controller/base-stream-controller.ts +160 -38
  26. package/src/controller/buffer-controller.ts +230 -92
  27. package/src/controller/buffer-operation-queue.ts +16 -19
  28. package/src/controller/cap-level-controller.ts +3 -2
  29. package/src/controller/cmcd-controller.ts +51 -14
  30. package/src/controller/content-steering-controller.ts +29 -15
  31. package/src/controller/eme-controller.ts +10 -23
  32. package/src/controller/error-controller.ts +6 -8
  33. package/src/controller/fps-controller.ts +8 -3
  34. package/src/controller/fragment-tracker.ts +15 -11
  35. package/src/controller/gap-controller.ts +43 -16
  36. package/src/controller/id3-track-controller.ts +7 -7
  37. package/src/controller/latency-controller.ts +9 -11
  38. package/src/controller/level-controller.ts +37 -19
  39. package/src/controller/stream-controller.ts +37 -32
  40. package/src/controller/subtitle-stream-controller.ts +28 -40
  41. package/src/controller/subtitle-track-controller.ts +5 -3
  42. package/src/controller/timeline-controller.ts +19 -21
  43. package/src/crypt/aes-crypto.ts +21 -2
  44. package/src/crypt/decrypter-aes-mode.ts +4 -0
  45. package/src/crypt/decrypter.ts +32 -16
  46. package/src/crypt/fast-aes-key.ts +28 -5
  47. package/src/demux/audio/aacdemuxer.ts +2 -2
  48. package/src/demux/audio/ac3-demuxer.ts +4 -3
  49. package/src/demux/audio/adts.ts +9 -4
  50. package/src/demux/audio/base-audio-demuxer.ts +16 -14
  51. package/src/demux/audio/mp3demuxer.ts +4 -3
  52. package/src/demux/audio/mpegaudio.ts +1 -1
  53. package/src/demux/mp4demuxer.ts +7 -7
  54. package/src/demux/sample-aes.ts +2 -0
  55. package/src/demux/transmuxer-interface.ts +4 -12
  56. package/src/demux/transmuxer-worker.ts +4 -4
  57. package/src/demux/transmuxer.ts +16 -3
  58. package/src/demux/tsdemuxer.ts +75 -38
  59. package/src/demux/video/avc-video-parser.ts +208 -119
  60. package/src/demux/video/base-video-parser.ts +147 -18
  61. package/src/demux/video/exp-golomb.ts +0 -208
  62. package/src/demux/video/hevc-video-parser.ts +749 -0
  63. package/src/events.ts +8 -1
  64. package/src/exports-named.ts +1 -1
  65. package/src/hls.ts +61 -38
  66. package/src/loader/fragment-loader.ts +10 -3
  67. package/src/loader/key-loader.ts +3 -1
  68. package/src/loader/level-key.ts +10 -9
  69. package/src/loader/playlist-loader.ts +4 -5
  70. package/src/remux/mp4-generator.ts +196 -1
  71. package/src/remux/mp4-remuxer.ts +24 -8
  72. package/src/task-loop.ts +5 -2
  73. package/src/types/component-api.ts +3 -1
  74. package/src/types/demuxer.ts +4 -0
  75. package/src/types/events.ts +4 -0
  76. package/src/types/remuxer.ts +1 -1
  77. package/src/utils/buffer-helper.ts +12 -31
  78. package/src/utils/cea-608-parser.ts +1 -3
  79. package/src/utils/codecs.ts +34 -5
  80. package/src/utils/encryption-methods-util.ts +21 -0
  81. package/src/utils/fetch-loader.ts +1 -1
  82. package/src/utils/imsc1-ttml-parser.ts +1 -1
  83. package/src/utils/keysystem-util.ts +1 -6
  84. package/src/utils/logger.ts +58 -23
  85. package/src/utils/mp4-tools.ts +5 -3
  86. package/src/utils/utf8-utils.ts +18 -0
  87. package/src/utils/webvtt-parser.ts +1 -1
  88. package/src/demux/id3.ts +0 -411
@@ -4,7 +4,7 @@ import type { HlsEventEmitter } from '../events';
4
4
  import { Events } from '../events';
5
5
  import { ErrorTypes, ErrorDetails } from '../errors';
6
6
  import { logger } from '../utils/logger';
7
- import {
7
+ import type {
8
8
  InitSegmentData,
9
9
  Remuxer,
10
10
  RemuxerResult,
@@ -29,6 +29,7 @@ import type { TrackSet } from '../types/track';
29
29
  import type { SourceBufferName } from '../types/buffer';
30
30
  import type { Fragment } from '../loader/fragment';
31
31
  import type { HlsConfig } from '../config';
32
+ import type { TypeSupported } from '../utils/codecs';
32
33
 
33
34
  const MAX_SILENT_FRAME_DURATION = 10 * 1000; // 10 seconds
34
35
  const AAC_SAMPLES_PER_FRAME = 1024;
@@ -41,7 +42,7 @@ let safariWebkitVersion: number | null = null;
41
42
  export default class MP4Remuxer implements Remuxer {
42
43
  private observer: HlsEventEmitter;
43
44
  private config: HlsConfig;
44
- private typeSupported: any;
45
+ private typeSupported: TypeSupported;
45
46
  private ISGenerated: boolean = false;
46
47
  private _initPTS: RationalTimestamp | null = null;
47
48
  private _initDTS: RationalTimestamp | null = null;
@@ -515,7 +516,7 @@ export default class MP4Remuxer implements Remuxer {
515
516
  if (foundHole || foundOverlap) {
516
517
  if (foundHole) {
517
518
  logger.warn(
518
- `AVC: ${toMsFromMpegTsClock(
519
+ `${(track.segmentCodec || '').toUpperCase()}: ${toMsFromMpegTsClock(
519
520
  delta,
520
521
  true,
521
522
  )} ms (${delta}dts) hole between fragments detected at ${timeOffset.toFixed(
@@ -524,7 +525,7 @@ export default class MP4Remuxer implements Remuxer {
524
525
  );
525
526
  } else {
526
527
  logger.warn(
527
- `AVC: ${toMsFromMpegTsClock(
528
+ `${(track.segmentCodec || '').toUpperCase()}: ${toMsFromMpegTsClock(
528
529
  -delta,
529
530
  true,
530
531
  )} ms (${delta}dts) overlapping between fragments detected at ${timeOffset.toFixed(
@@ -543,12 +544,27 @@ export default class MP4Remuxer implements Remuxer {
543
544
  inputSamples[0].dts = firstDTS;
544
545
  inputSamples[0].pts = firstPTS;
545
546
  } else {
547
+ let isPTSOrderRetained = true;
546
548
  for (let i = 0; i < inputSamples.length; i++) {
547
- if (inputSamples[i].dts > firstPTS) {
549
+ if (inputSamples[i].dts > firstPTS && isPTSOrderRetained) {
548
550
  break;
549
551
  }
552
+
553
+ const prevPTS = inputSamples[i].pts;
550
554
  inputSamples[i].dts -= delta;
551
555
  inputSamples[i].pts -= delta;
556
+
557
+ // check to see if this sample's PTS order has changed
558
+ // relative to the next one
559
+ if (i < inputSamples.length - 1) {
560
+ const nextSamplePTS = inputSamples[i + 1].pts;
561
+ const currentSamplePTS = inputSamples[i].pts;
562
+
563
+ const currentOrder = nextSamplePTS <= currentSamplePTS;
564
+ const prevOrder = nextSamplePTS <= prevPTS;
565
+
566
+ isPTSOrderRetained = currentOrder == prevOrder;
567
+ }
552
568
  }
553
569
  }
554
570
  logger.log(
@@ -743,7 +759,7 @@ export default class MP4Remuxer implements Remuxer {
743
759
  }
744
760
  }
745
761
  }
746
- // next AVC sample DTS should be equal to last sample DTS + last sample duration (in PES timescale)
762
+ // next AVC/HEVC sample DTS should be equal to last sample DTS + last sample duration (in PES timescale)
747
763
  mp4SampleDuration =
748
764
  stretchedLastFrame || !mp4SampleDuration
749
765
  ? averageSampleDuration
@@ -927,7 +943,7 @@ export default class MP4Remuxer implements Remuxer {
927
943
  for (let j = 0; j < missing; j++) {
928
944
  const newStamp = Math.max(nextPts as number, 0);
929
945
  let fillFrame = AAC.getSilentFrame(
930
- track.manifestCodec || track.codec,
946
+ track.parsedCodec || track.manifestCodec || track.codec,
931
947
  track.channelCount,
932
948
  );
933
949
  if (!fillFrame) {
@@ -1077,7 +1093,7 @@ export default class MP4Remuxer implements Remuxer {
1077
1093
  const nbSamples: number = Math.ceil((endDTS - startDTS) / frameDuration);
1078
1094
  // silent frame
1079
1095
  const silentFrame: Uint8Array | undefined = AAC.getSilentFrame(
1080
- track.manifestCodec || track.codec,
1096
+ track.parsedCodec || track.manifestCodec || track.codec,
1081
1097
  track.channelCount,
1082
1098
  );
1083
1099
 
package/src/task-loop.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import { type ILogger, Logger } from './utils/logger';
2
+
1
3
  /**
2
4
  * @ignore
3
5
  * Sub-class specialization of EventHandler base class.
@@ -27,13 +29,14 @@
27
29
  * we are limiting the task execution per call stack to exactly one, but scheduling/post-poning further
28
30
  * task processing on the next main loop iteration (also known as "next tick" in the Node/JS runtime lingo).
29
31
  */
30
- export default class TaskLoop {
32
+ export default class TaskLoop extends Logger {
31
33
  private readonly _boundTick: () => void;
32
34
  private _tickTimer: number | null = null;
33
35
  private _tickInterval: number | null = null;
34
36
  private _tickCallCount = 0;
35
37
 
36
- constructor() {
38
+ constructor(label: string, logger: ILogger) {
39
+ super(label, logger);
37
40
  this._boundTick = this.tick.bind(this);
38
41
  }
39
42
 
@@ -1,4 +1,4 @@
1
- import EwmaBandWidthEstimator from '../utils/ewma-bandwidth-estimator';
1
+ import type EwmaBandWidthEstimator from '../utils/ewma-bandwidth-estimator';
2
2
 
3
3
  export interface ComponentAPI {
4
4
  destroy(): void;
@@ -15,4 +15,6 @@ export interface AbrComponentAPI extends ComponentAPI {
15
15
  export interface NetworkComponentAPI extends ComponentAPI {
16
16
  startLoad(startPosition: number): void;
17
17
  stopLoad(): void;
18
+ pauseBuffering?(): void;
19
+ resumeBuffering?(): void;
18
20
  }
@@ -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,15 @@ 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;
80
+ lastNalu?: VideoSampleUnit | null;
78
81
  segmentCodec?: string;
79
82
  manifestCodec?: string;
80
83
  samples: VideoSample[] | Uint8Array;
84
+ params?: object;
81
85
  }
82
86
 
83
87
  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;
@@ -1,5 +1,5 @@
1
1
  import type { TrackSet } from './track';
2
- import {
2
+ import type {
3
3
  DemuxedAudioTrack,
4
4
  DemuxedMetadataTrack,
5
5
  DemuxedUserdataTrack,
@@ -35,19 +35,13 @@ export class BufferHelper {
35
35
  * Return true if `media`'s buffered include `position`
36
36
  */
37
37
  static isBuffered(media: Bufferable, position: number): boolean {
38
- try {
39
- if (media) {
40
- const buffered = BufferHelper.getBuffered(media);
41
- for (let i = 0; i < buffered.length; i++) {
42
- if (position >= buffered.start(i) && position <= buffered.end(i)) {
43
- return true;
44
- }
38
+ if (media) {
39
+ const buffered = BufferHelper.getBuffered(media);
40
+ for (let i = buffered.length; i--; ) {
41
+ if (position >= buffered.start(i) && position <= buffered.end(i)) {
42
+ return true;
45
43
  }
46
44
  }
47
- } catch (error) {
48
- // this is to catch
49
- // InvalidStateError: Failed to read the 'buffered' property from 'SourceBuffer':
50
- // This SourceBuffer has been removed from the parent media source
51
45
  }
52
46
  return false;
53
47
  }
@@ -57,21 +51,15 @@ export class BufferHelper {
57
51
  pos: number,
58
52
  maxHoleDuration: number,
59
53
  ): BufferInfo {
60
- try {
61
- if (media) {
62
- const vbuffered = BufferHelper.getBuffered(media);
54
+ if (media) {
55
+ const vbuffered = BufferHelper.getBuffered(media);
56
+ if (vbuffered.length) {
63
57
  const buffered: BufferTimeRange[] = [];
64
- let i: number;
65
- for (i = 0; i < vbuffered.length; i++) {
58
+ for (let i = 0; i < vbuffered.length; i++) {
66
59
  buffered.push({ start: vbuffered.start(i), end: vbuffered.end(i) });
67
60
  }
68
-
69
- return this.bufferedInfo(buffered, pos, maxHoleDuration);
61
+ return BufferHelper.bufferedInfo(buffered, pos, maxHoleDuration);
70
62
  }
71
- } catch (error) {
72
- // this is to catch
73
- // InvalidStateError: Failed to read the 'buffered' property from 'SourceBuffer':
74
- // This SourceBuffer has been removed from the parent media source
75
63
  }
76
64
  return { len: 0, start: pos, end: pos, nextStart: undefined };
77
65
  }
@@ -88,14 +76,7 @@ export class BufferHelper {
88
76
  } {
89
77
  pos = Math.max(0, pos);
90
78
  // sort on buffer.start/smaller end (IE does not always return sorted buffered range)
91
- buffered.sort(function (a, b) {
92
- const diff = a.start - b.start;
93
- if (diff) {
94
- return diff;
95
- } else {
96
- return b.end - a.end;
97
- }
98
- });
79
+ buffered.sort((a, b) => a.start - b.start || b.end - a.end);
99
80
 
100
81
  let buffered2: BufferTimeRange[] = [];
101
82
  if (maxHoleDuration) {
@@ -164,7 +145,7 @@ export class BufferHelper {
164
145
  */
165
146
  static getBuffered(media: Bufferable): TimeRanges {
166
147
  try {
167
- return media.buffered;
148
+ return media.buffered || noopBuffered;
168
149
  } catch (e) {
169
150
  logger.log('failed to get media.buffered', e);
170
151
  return noopBuffered;
@@ -1331,9 +1331,7 @@ class Cea608Parser {
1331
1331
  if (charCodes) {
1332
1332
  this.logger.log(
1333
1333
  VerboseLevel.DEBUG,
1334
- () =>
1335
- 'Char codes = ' +
1336
- numArrayToHexArray(charCodes as number[]).join(','),
1334
+ () => 'Char codes = ' + numArrayToHexArray(charCodes).join(','),
1337
1335
  );
1338
1336
  }
1339
1337
  return charCodes;
@@ -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,
@@ -185,7 +193,7 @@ export function getCodecCompatibleName(
185
193
  }
186
194
 
187
195
  export function pickMostCompleteCodecName(
188
- parsedCodec: string,
196
+ parsedCodec: string | undefined,
189
197
  levelCodec: string | undefined,
190
198
  ): string | undefined {
191
199
  // Parsing of mp4a codecs strings in mp4-tools from media is incomplete as of d8c6c7a
@@ -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
+ }
@@ -1,4 +1,4 @@
1
- import {
1
+ import type {
2
2
  LoaderCallbacks,
3
3
  LoaderContext,
4
4
  Loader,
@@ -1,7 +1,7 @@
1
1
  import { findBox } from './mp4-tools';
2
2
  import { parseTimeStamp } from './vttparser';
3
3
  import VTTCue from './vttcue';
4
- import { utf8ArrayToStr } from '../demux/id3';
4
+ import { utf8ArrayToStr } from '@svta/common-media-library/utils/utf8ArrayToStr';
5
5
  import {
6
6
  RationalTimestamp,
7
7
  toTimescaleFromScale,
@@ -1,4 +1,5 @@
1
1
  import { base64Decode } from './numeric-encoding-utils';
2
+ import { strToUtf8array } from './utf8-utils';
2
3
 
3
4
  function getKeyIdBytes(str: string): Uint8Array {
4
5
  const keyIdbytes = strToUtf8array(str).subarray(0, 16);
@@ -40,9 +41,3 @@ export function convertDataUriToArrayBytes(uri: string): Uint8Array | null {
40
41
  }
41
42
  return keydata;
42
43
  }
43
-
44
- export function strToUtf8array(str: string): Uint8Array {
45
- return Uint8Array.from(unescape(encodeURIComponent(str)), (c) =>
46
- c.charCodeAt(0),
47
- );
48
- }
@@ -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,
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
+ const 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,29 @@ 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
  }
109
+ // global exported logger uses the same functions as new logger without `id`
110
+ keys.forEach((key) => {
111
+ exportedLogger[key] = getLoggerFn(key, debugConfig);
112
+ });
80
113
  } else {
81
- exportedLogger = fakeLogger;
114
+ // Reset global exported logger
115
+ Object.assign(exportedLogger, newLogger);
82
116
  }
117
+ return newLogger;
83
118
  }
84
119
 
85
120
  export const logger: ILogger = exportedLogger;
@@ -1,6 +1,6 @@
1
1
  import { ElementaryStreamTypes } from '../loader/fragment';
2
2
  import { sliceUint8 } from './typed-array';
3
- import { utf8ArrayToStr } from '../demux/id3';
3
+ import { utf8ArrayToStr } from '@svta/common-media-library/utils/utf8ArrayToStr';
4
4
  import { logger } from '../utils/logger';
5
5
  import Hex from './hex';
6
6
  import type { PassthroughTrack, UserdataSample } from '../types/demuxer';
@@ -327,7 +327,7 @@ function parseStsd(stsd: Uint8Array): { codec: string; encrypted: boolean } {
327
327
  case 'mp4a': {
328
328
  const codecBox = findBox(sampleEntries, [fourCC])[0];
329
329
  const esdsBox = findBox(codecBox.subarray(28), ['esds'])[0];
330
- if (esdsBox && esdsBox.length > 12) {
330
+ if (esdsBox && esdsBox.length > 7) {
331
331
  let i = 4;
332
332
  // ES Descriptor tag
333
333
  if (esdsBox[i++] !== 0x03) {
@@ -478,7 +478,9 @@ function parseStsd(stsd: Uint8Array): { codec: string; encrypted: boolean } {
478
478
 
479
479
  function skipBERInteger(bytes: Uint8Array, i: number): number {
480
480
  const limit = i + 5;
481
- while (bytes[i++] & 0x80 && i < limit) {}
481
+ while (bytes[i++] & 0x80 && i < limit) {
482
+ /* do nothing */
483
+ }
482
484
  return i;
483
485
  }
484
486
 
@@ -0,0 +1,18 @@
1
+ // breaking up those two types in order to clarify what is happening in the decoding path.
2
+ type DecodedFrame<T> = { key: string; data: T; info?: any };
3
+ export type Frame = DecodedFrame<ArrayBuffer | string>;
4
+ // http://stackoverflow.com/questions/8936984/uint8array-to-string-in-javascript/22373197
5
+ // http://www.onicos.com/staff/iz/amuse/javascript/expert/utf.txt
6
+ /* utf.js - UTF-8 <=> UTF-16 convertion
7
+ *
8
+ * Copyright (C) 1999 Masanao Izumo <iz@onicos.co.jp>
9
+ * Version: 1.0
10
+ * LastModified: Dec 25 1999
11
+ * This library is free. You can redistribute it and/or modify it.
12
+ */
13
+
14
+ export function strToUtf8array(str: string): Uint8Array {
15
+ return Uint8Array.from(unescape(encodeURIComponent(str)), (c) =>
16
+ c.charCodeAt(0),
17
+ );
18
+ }
@@ -1,5 +1,5 @@
1
1
  import { VTTParser } from './vttparser';
2
- import { utf8ArrayToStr } from '../demux/id3';
2
+ import { utf8ArrayToStr } from '@svta/common-media-library/utils/utf8ArrayToStr';
3
3
  import {
4
4
  RationalTimestamp,
5
5
  toMpegTsClockFromTimescale,