hls.js 1.5.2-0.canary.9923 → 1.5.2

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 (44) hide show
  1. package/dist/hls-demo.js +0 -5
  2. package/dist/hls-demo.js.map +1 -1
  3. package/dist/hls.js +379 -431
  4. package/dist/hls.js.d.ts +14 -14
  5. package/dist/hls.js.map +1 -1
  6. package/dist/hls.light.js +217 -275
  7. package/dist/hls.light.js.map +1 -1
  8. package/dist/hls.light.min.js +1 -1
  9. package/dist/hls.light.min.js.map +1 -1
  10. package/dist/hls.light.mjs +219 -278
  11. package/dist/hls.light.mjs.map +1 -1
  12. package/dist/hls.min.js +1 -1
  13. package/dist/hls.min.js.map +1 -1
  14. package/dist/hls.mjs +350 -401
  15. package/dist/hls.mjs.map +1 -1
  16. package/dist/hls.worker.js +1 -1
  17. package/dist/hls.worker.js.map +1 -1
  18. package/package.json +9 -9
  19. package/src/controller/abr-controller.ts +2 -2
  20. package/src/controller/base-stream-controller.ts +16 -16
  21. package/src/controller/buffer-controller.ts +0 -6
  22. package/src/controller/eme-controller.ts +12 -6
  23. package/src/controller/latency-controller.ts +8 -7
  24. package/src/controller/level-controller.ts +18 -7
  25. package/src/controller/stream-controller.ts +14 -11
  26. package/src/controller/subtitle-stream-controller.ts +1 -6
  27. package/src/controller/subtitle-track-controller.ts +2 -4
  28. package/src/controller/timeline-controller.ts +26 -20
  29. package/src/crypt/aes-crypto.ts +2 -21
  30. package/src/crypt/decrypter.ts +18 -32
  31. package/src/crypt/fast-aes-key.ts +5 -24
  32. package/src/demux/audio/adts.ts +4 -9
  33. package/src/demux/sample-aes.ts +0 -2
  34. package/src/demux/transmuxer-interface.ts +12 -4
  35. package/src/demux/transmuxer.ts +3 -16
  36. package/src/demux/tsdemuxer.ts +17 -12
  37. package/src/loader/fragment-loader.ts +2 -9
  38. package/src/loader/key-loader.ts +0 -2
  39. package/src/loader/level-key.ts +9 -10
  40. package/src/remux/mp4-remuxer.ts +3 -4
  41. package/src/types/demuxer.ts +0 -1
  42. package/src/utils/codecs.ts +4 -33
  43. package/src/crypt/decrypter-aes-mode.ts +0 -4
  44. package/src/utils/encryption-methods-util.ts +0 -21
@@ -4,7 +4,6 @@ import AESDecryptor, { removePadding } from './aes-decryptor';
4
4
  import { logger } from '../utils/logger';
5
5
  import { appendUint8Array } from '../utils/mp4-tools';
6
6
  import { sliceUint8 } from '../utils/typed-array';
7
- import { DecrypterAesMode } from './decrypter-aes-mode';
8
7
  import type { HlsConfig } from '../config';
9
8
 
10
9
  const CHUNK_SIZE = 16; // 16 bytes, 128 bits
@@ -20,10 +19,9 @@ export default class Decrypter {
20
19
  private currentIV: ArrayBuffer | null = null;
21
20
  private currentResult: ArrayBuffer | null = null;
22
21
  private useSoftware: boolean;
23
- private enableSoftwareAES: boolean;
24
22
 
25
23
  constructor(config: HlsConfig, { removePKCS7Padding = true } = {}) {
26
- this.enableSoftwareAES = config.enableSoftwareAES;
24
+ this.useSoftware = config.enableSoftwareAES;
27
25
  this.removePKCS7Padding = removePKCS7Padding;
28
26
  // built in decryptor expects PKCS7 padding
29
27
  if (removePKCS7Padding) {
@@ -38,7 +36,9 @@ export default class Decrypter {
38
36
  /* no-op */
39
37
  }
40
38
  }
41
- this.useSoftware = this.subtle === null;
39
+ if (this.subtle === null) {
40
+ this.useSoftware = true;
41
+ }
42
42
  }
43
43
 
44
44
  destroy() {
@@ -82,11 +82,10 @@ export default class Decrypter {
82
82
  data: Uint8Array | ArrayBuffer,
83
83
  key: ArrayBuffer,
84
84
  iv: ArrayBuffer,
85
- aesMode: DecrypterAesMode,
86
85
  ): Promise<ArrayBuffer> {
87
86
  if (this.useSoftware) {
88
87
  return new Promise((resolve, reject) => {
89
- this.softwareDecrypt(new Uint8Array(data), key, iv, aesMode);
88
+ this.softwareDecrypt(new Uint8Array(data), key, iv);
90
89
  const decryptResult = this.flush();
91
90
  if (decryptResult) {
92
91
  resolve(decryptResult.buffer);
@@ -95,7 +94,7 @@ export default class Decrypter {
95
94
  }
96
95
  });
97
96
  }
98
- return this.webCryptoDecrypt(new Uint8Array(data), key, iv, aesMode);
97
+ return this.webCryptoDecrypt(new Uint8Array(data), key, iv);
99
98
  }
100
99
 
101
100
  // Software decryption is progressive. Progressive decryption may not return a result on each call. Any cached
@@ -104,13 +103,8 @@ export default class Decrypter {
104
103
  data: Uint8Array,
105
104
  key: ArrayBuffer,
106
105
  iv: ArrayBuffer,
107
- aesMode: DecrypterAesMode,
108
106
  ): ArrayBuffer | null {
109
107
  const { currentIV, currentResult, remainderData } = this;
110
- if (aesMode !== DecrypterAesMode.cbc || key.byteLength !== 16) {
111
- logger.warn('SoftwareDecrypt: can only handle AES-128-CBC');
112
- return null;
113
- }
114
108
  this.logOnce('JS AES decrypt');
115
109
  // The output is staggered during progressive parsing - the current result is cached, and emitted on the next call
116
110
  // This is done in order to strip PKCS7 padding, which is found at the end of each segment. We only know we've reached
@@ -153,22 +147,21 @@ export default class Decrypter {
153
147
  data: Uint8Array,
154
148
  key: ArrayBuffer,
155
149
  iv: ArrayBuffer,
156
- aesMode: DecrypterAesMode,
157
150
  ): Promise<ArrayBuffer> {
158
151
  const subtle = this.subtle;
159
152
  if (this.key !== key || !this.fastAesKey) {
160
153
  this.key = key;
161
- this.fastAesKey = new FastAESKey(subtle, key, aesMode);
154
+ this.fastAesKey = new FastAESKey(subtle, key);
162
155
  }
163
156
  return this.fastAesKey
164
157
  .expandKey()
165
- .then((aesKey: CryptoKey) => {
158
+ .then((aesKey) => {
166
159
  // decrypt using web crypto
167
160
  if (!subtle) {
168
161
  return Promise.reject(new Error('web crypto not initialized'));
169
162
  }
170
163
  this.logOnce('WebCrypto AES decrypt');
171
- const crypto = new AESCrypto(subtle, new Uint8Array(iv), aesMode);
164
+ const crypto = new AESCrypto(subtle, new Uint8Array(iv));
172
165
  return crypto.decrypt(data.buffer, aesKey);
173
166
  })
174
167
  .catch((err) => {
@@ -176,26 +169,19 @@ export default class Decrypter {
176
169
  `[decrypter]: WebCrypto Error, disable WebCrypto API, ${err.name}: ${err.message}`,
177
170
  );
178
171
 
179
- return this.onWebCryptoError(data, key, iv, aesMode);
172
+ return this.onWebCryptoError(data, key, iv);
180
173
  });
181
174
  }
182
175
 
183
- private onWebCryptoError(data, key, iv, aesMode): ArrayBuffer | never {
184
- const enableSoftwareAES = this.enableSoftwareAES;
185
- if (enableSoftwareAES) {
186
- this.useSoftware = true;
187
- this.logEnabled = true;
188
- this.softwareDecrypt(data, key, iv, aesMode);
189
- const decryptResult = this.flush();
190
- if (decryptResult) {
191
- return decryptResult.buffer;
192
- }
176
+ private onWebCryptoError(data, key, iv): ArrayBuffer | never {
177
+ this.useSoftware = true;
178
+ this.logEnabled = true;
179
+ this.softwareDecrypt(data, key, iv);
180
+ const decryptResult = this.flush();
181
+ if (decryptResult) {
182
+ return decryptResult.buffer;
193
183
  }
194
- throw new Error(
195
- 'WebCrypto' +
196
- (enableSoftwareAES ? ' and softwareDecrypt' : '') +
197
- ': failed to decrypt data',
198
- );
184
+ throw new Error('WebCrypto and softwareDecrypt: failed to decrypt data');
199
185
  }
200
186
 
201
187
  private getValidChunk(data: Uint8Array): Uint8Array {
@@ -1,35 +1,16 @@
1
- import { DecrypterAesMode } from './decrypter-aes-mode';
2
-
3
1
  export default class FastAESKey {
4
2
  private subtle: any;
5
3
  private key: ArrayBuffer;
6
- private aesMode: DecrypterAesMode;
7
4
 
8
- constructor(subtle, key, aesMode: DecrypterAesMode) {
5
+ constructor(subtle, key) {
9
6
  this.subtle = subtle;
10
7
  this.key = key;
11
- this.aesMode = aesMode;
12
8
  }
13
9
 
14
10
  expandKey() {
15
- const subtleAlgoName = getSubtleAlgoName(this.aesMode);
16
- return this.subtle.importKey(
17
- 'raw',
18
- this.key,
19
- { name: subtleAlgoName },
20
- false,
21
- ['encrypt', 'decrypt'],
22
- );
23
- }
24
- }
25
-
26
- function getSubtleAlgoName(aesMode: DecrypterAesMode) {
27
- switch (aesMode) {
28
- case DecrypterAesMode.cbc:
29
- return 'AES-CBC';
30
- case DecrypterAesMode.ctr:
31
- return 'AES-CTR';
32
- default:
33
- throw new Error(`[FastAESKey] invalid aes mode ${aesMode}`);
11
+ return this.subtle.importKey('raw', this.key, { name: 'AES-CBC' }, false, [
12
+ 'encrypt',
13
+ 'decrypt',
14
+ ]);
34
15
  }
35
16
  }
@@ -17,7 +17,6 @@ type AudioConfig = {
17
17
  samplerate: number;
18
18
  channelCount: number;
19
19
  codec: string;
20
- parsedCodec: string;
21
20
  manifestCodec: string;
22
21
  };
23
22
 
@@ -33,7 +32,6 @@ export function getAudioConfig(
33
32
  audioCodec: string,
34
33
  ): AudioConfig | void {
35
34
  let adtsObjectType: number;
36
- let originalAdtsObjectType: number;
37
35
  let adtsExtensionSamplingIndex: number;
38
36
  let adtsChannelConfig: number;
39
37
  let config: number[];
@@ -44,8 +42,7 @@ export function getAudioConfig(
44
42
  8000, 7350,
45
43
  ];
46
44
  // byte 2
47
- adtsObjectType = originalAdtsObjectType =
48
- ((data[offset + 2] & 0xc0) >>> 6) + 1;
45
+ adtsObjectType = ((data[offset + 2] & 0xc0) >>> 6) + 1;
49
46
  const adtsSamplingIndex = (data[offset + 2] & 0x3c) >>> 2;
50
47
  if (adtsSamplingIndex > adtsSamplingRates.length - 1) {
51
48
  const error = new Error(`invalid ADTS sampling index:${adtsSamplingIndex}`);
@@ -64,8 +61,8 @@ export function getAudioConfig(
64
61
  logger.log(
65
62
  `manifest codec:${audioCodec}, ADTS type:${adtsObjectType}, samplingIndex:${adtsSamplingIndex}`,
66
63
  );
67
- // Firefox and Pale Moon: freq less than 24kHz = AAC SBR (HE-AAC)
68
- if (/firefox|palemoon/i.test(userAgent)) {
64
+ // firefox: freq less than 24kHz = AAC SBR (HE-AAC)
65
+ if (/firefox/i.test(userAgent)) {
69
66
  if (adtsSamplingIndex >= 6) {
70
67
  adtsObjectType = 5;
71
68
  config = new Array(4);
@@ -170,7 +167,6 @@ export function getAudioConfig(
170
167
  samplerate: adtsSamplingRates[adtsSamplingIndex],
171
168
  channelCount: adtsChannelConfig,
172
169
  codec: 'mp4a.40.' + adtsObjectType,
173
- parsedCodec: 'mp4a.40.' + originalAdtsObjectType,
174
170
  manifestCodec,
175
171
  };
176
172
  }
@@ -248,9 +244,8 @@ export function initTrackConfig(
248
244
  track.channelCount = config.channelCount;
249
245
  track.codec = config.codec;
250
246
  track.manifestCodec = config.manifestCodec;
251
- track.parsedCodec = config.parsedCodec;
252
247
  logger.log(
253
- `parsed codec:${track.parsedCodec}, codec:${track.codec}, rate:${config.samplerate}, channels:${config.channelCount}`,
248
+ `parsed codec:${track.codec}, rate:${config.samplerate}, channels:${config.channelCount}`,
254
249
  );
255
250
  }
256
251
  }
@@ -4,7 +4,6 @@
4
4
 
5
5
  import { HlsConfig } from '../config';
6
6
  import Decrypter from '../crypt/decrypter';
7
- import { DecrypterAesMode } from '../crypt/decrypter-aes-mode';
8
7
  import { HlsEventEmitter } from '../events';
9
8
  import type {
10
9
  AudioSample,
@@ -31,7 +30,6 @@ class SampleAesDecrypter {
31
30
  encryptedData,
32
31
  this.keyData.key.buffer,
33
32
  this.keyData.iv.buffer,
34
- DecrypterAesMode.cbc,
35
33
  );
36
34
  }
37
35
 
@@ -12,13 +12,14 @@ import Transmuxer, {
12
12
  } from '../demux/transmuxer';
13
13
  import { logger } from '../utils/logger';
14
14
  import { ErrorTypes, ErrorDetails } from '../errors';
15
+ import { getMediaSource } from '../utils/mediasource-helper';
15
16
  import { EventEmitter } from 'eventemitter3';
16
17
  import { Fragment, Part } from '../loader/fragment';
17
- import { getM2TSSupportedAudioTypes } from '../utils/codecs';
18
18
  import type { ChunkMetadata, TransmuxerResult } from '../types/transmuxer';
19
19
  import type Hls from '../hls';
20
20
  import type { HlsEventEmitter } from '../events';
21
21
  import type { PlaylistLevelType } from '../types/loader';
22
+ import type { TypeSupported } from './tsdemuxer';
22
23
  import type { RationalTimestamp } from '../utils/timescale-conversion';
23
24
 
24
25
  export default class TransmuxerInterface {
@@ -63,9 +64,16 @@ export default class TransmuxerInterface {
63
64
  this.observer.on(Events.FRAG_DECRYPTED, forwardMessage);
64
65
  this.observer.on(Events.ERROR, forwardMessage);
65
66
 
66
- const m2tsTypeSupported = getM2TSSupportedAudioTypes(
67
- config.preferManagedMediaSource,
68
- );
67
+ const MediaSource = getMediaSource(config.preferManagedMediaSource) || {
68
+ isTypeSupported: () => false,
69
+ };
70
+ const m2tsTypeSupported: TypeSupported = {
71
+ mpeg: MediaSource.isTypeSupported('audio/mpeg'),
72
+ mp3: MediaSource.isTypeSupported('audio/mp4; codecs="mp3"'),
73
+ ac3: __USE_M2TS_ADVANCED_CODECS__
74
+ ? MediaSource.isTypeSupported('audio/mp4; codecs="ac-3"')
75
+ : false,
76
+ };
69
77
 
70
78
  // navigator.vendor is not always available in Web Worker
71
79
  // refer to https://developer.mozilla.org/en-US/docs/Web/API/WorkerGlobalScope/navigator
@@ -4,23 +4,18 @@ import { ErrorTypes, ErrorDetails } from '../errors';
4
4
  import Decrypter from '../crypt/decrypter';
5
5
  import AACDemuxer from './audio/aacdemuxer';
6
6
  import MP4Demuxer from '../demux/mp4demuxer';
7
- import TSDemuxer from '../demux/tsdemuxer';
7
+ import TSDemuxer, { TypeSupported } from '../demux/tsdemuxer';
8
8
  import MP3Demuxer from './audio/mp3demuxer';
9
9
  import { AC3Demuxer } from './audio/ac3-demuxer';
10
10
  import MP4Remuxer from '../remux/mp4-remuxer';
11
11
  import PassThroughRemuxer from '../remux/passthrough-remuxer';
12
12
  import { logger } from '../utils/logger';
13
- import {
14
- isFullSegmentEncryption,
15
- getAesModeFromFullSegmentMethod,
16
- } from '../utils/encryption-methods-util';
17
13
  import type { Demuxer, DemuxerResult, KeyData } from '../types/demuxer';
18
14
  import type { Remuxer } from '../types/remuxer';
19
15
  import type { TransmuxerResult, ChunkMetadata } from '../types/transmuxer';
20
16
  import type { HlsConfig } from '../config';
21
17
  import type { DecryptData } from '../loader/level-key';
22
18
  import type { PlaylistLevelType } from '../types/loader';
23
- import type { TypeSupported } from '../utils/codecs';
24
19
  import type { RationalTimestamp } from '../utils/timescale-conversion';
25
20
  import { optionalSelf } from '../utils/global';
26
21
 
@@ -119,10 +114,8 @@ export default class Transmuxer {
119
114
  } = transmuxConfig;
120
115
 
121
116
  const keyData = getEncryptionType(uintData, decryptdata);
122
- if (keyData && isFullSegmentEncryption(keyData.method)) {
117
+ if (keyData && keyData.method === 'AES-128') {
123
118
  const decrypter = this.getDecrypter();
124
- const aesMode = getAesModeFromFullSegmentMethod(keyData.method);
125
-
126
119
  // Software decryption is synchronous; webCrypto is not
127
120
  if (decrypter.isSync()) {
128
121
  // Software decryption is progressive. Progressive decryption may not return a result on each call. Any cached
@@ -131,7 +124,6 @@ export default class Transmuxer {
131
124
  uintData,
132
125
  keyData.key.buffer,
133
126
  keyData.iv.buffer,
134
- aesMode,
135
127
  );
136
128
  // For Low-Latency HLS Parts, decrypt in place, since part parsing is expected on push progress
137
129
  const loadingParts = chunkMeta.part > -1;
@@ -145,12 +137,7 @@ export default class Transmuxer {
145
137
  uintData = new Uint8Array(decryptedData);
146
138
  } else {
147
139
  this.decryptionPromise = decrypter
148
- .webCryptoDecrypt(
149
- uintData,
150
- keyData.key.buffer,
151
- keyData.iv.buffer,
152
- aesMode,
153
- )
140
+ .webCryptoDecrypt(uintData, keyData.key.buffer, keyData.iv.buffer)
154
141
  .then((decryptedData): TransmuxerResult => {
155
142
  // Calling push here is important; if flush() is called while this is still resolving, this ensures that
156
143
  // the decrypted data has been transmuxed
@@ -20,21 +20,20 @@ import { logger } from '../utils/logger';
20
20
  import { ErrorTypes, ErrorDetails } from '../errors';
21
21
  import type { HlsConfig } from '../config';
22
22
  import type { HlsEventEmitter } from '../events';
23
- import type { TypeSupported } from '../utils/codecs';
24
23
  import {
24
+ DemuxedVideoTrack,
25
+ DemuxedAudioTrack,
26
+ DemuxedTrack,
27
+ Demuxer,
28
+ DemuxerResult,
29
+ VideoSample,
30
+ DemuxedMetadataTrack,
31
+ DemuxedUserdataTrack,
32
+ ElementaryStreamData,
33
+ KeyData,
25
34
  MetadataSchema,
26
- type DemuxedVideoTrack,
27
- type DemuxedAudioTrack,
28
- type DemuxedTrack,
29
- type Demuxer,
30
- type DemuxerResult,
31
- type VideoSample,
32
- type DemuxedMetadataTrack,
33
- type DemuxedUserdataTrack,
34
- type ElementaryStreamData,
35
- type KeyData,
36
35
  } from '../types/demuxer';
37
- import type { AudioFrame } from '../types/demuxer';
36
+ import { AudioFrame } from '../types/demuxer';
38
37
 
39
38
  export type ParsedTimestamp = {
40
39
  pts?: number;
@@ -49,6 +48,12 @@ export type PES = ParsedTimestamp & {
49
48
  export type ParsedVideoSample = ParsedTimestamp &
50
49
  Omit<VideoSample, 'pts' | 'dts'>;
51
50
 
51
+ export interface TypeSupported {
52
+ mpeg: boolean;
53
+ mp3: boolean;
54
+ ac3: boolean;
55
+ }
56
+
52
57
  const PACKET_LENGTH = 188;
53
58
 
54
59
  class TSDemuxer implements Demuxer {
@@ -336,11 +336,8 @@ function createLoaderContext(
336
336
  if (Number.isFinite(start) && Number.isFinite(end)) {
337
337
  let byteRangeStart = start;
338
338
  let byteRangeEnd = end;
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,
339
+ if (frag.sn === 'initSegment' && frag.decryptdata?.method === 'AES-128') {
340
+ // MAP segment encrypted with method 'AES-128', when served with HTTP Range,
344
341
  // has the unencrypted size specified in the range.
345
342
  // Ref: https://tools.ietf.org/html/draft-pantos-hls-rfc8216bis-08#section-6.3.6
346
343
  const fragmentLen = end - start;
@@ -375,10 +372,6 @@ function createGapLoadError(frag: Fragment, part?: Part): LoadError {
375
372
  return new LoadError(errorData);
376
373
  }
377
374
 
378
- function isMethodFullSegmentAesCbc(method) {
379
- return method === 'AES-128' || method === 'AES-256';
380
- }
381
-
382
375
  export class LoadError extends Error {
383
376
  public readonly data: FragLoadFailResult;
384
377
  constructor(data: FragLoadFailResult) {
@@ -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(
@@ -2,7 +2,6 @@ import {
2
2
  changeEndianness,
3
3
  convertDataUriToArrayBytes,
4
4
  } from '../utils/keysystem-util';
5
- import { isFullSegmentEncryption } from '../utils/encryption-methods-util';
6
5
  import { KeySystemFormats } from '../utils/mediakeys-helper';
7
6
  import { mp4pssh } from '../utils/mp4-tools';
8
7
  import { logger } from '../utils/logger';
@@ -52,14 +51,13 @@ export class LevelKey implements DecryptData {
52
51
  this.keyFormatVersions = formatversions;
53
52
  this.iv = iv;
54
53
  this.encrypted = method ? method !== 'NONE' : false;
55
- this.isCommonEncryption =
56
- this.encrypted && !isFullSegmentEncryption(method);
54
+ this.isCommonEncryption = this.encrypted && method !== 'AES-128';
57
55
  }
58
56
 
59
57
  public isSupported(): boolean {
60
58
  // If it's Segment encryption or No encryption, just select that key system
61
59
  if (this.method) {
62
- if (isFullSegmentEncryption(this.method) || this.method === 'NONE') {
60
+ if (this.method === 'AES-128' || this.method === 'NONE') {
63
61
  return true;
64
62
  }
65
63
  if (this.keyFormat === 'identity') {
@@ -90,15 +88,16 @@ export class LevelKey implements DecryptData {
90
88
  return null;
91
89
  }
92
90
 
93
- if (isFullSegmentEncryption(this.method) && this.uri && !this.iv) {
91
+ if (this.method === 'AES-128' && this.uri && !this.iv) {
94
92
  if (typeof sn !== 'number') {
95
93
  // We are fetching decryption data for a initialization segment
96
- // If the segment was encrypted with AES-128/256
94
+ // If the segment was encrypted with AES-128
97
95
  // It must have an IV defined. We cannot substitute the Segment Number in.
98
- logger.warn(
99
- `missing IV for initialization segment with method="${this.method}" - compliance issue`,
100
- );
101
-
96
+ if (this.method === 'AES-128' && !this.iv) {
97
+ logger.warn(
98
+ `missing IV for initialization segment with method="${this.method}" - compliance issue`,
99
+ );
100
+ }
102
101
  // Explicitly set sn to resulting value from implicit conversions 'initSegment' values for IV generation.
103
102
  sn = 0;
104
103
  }
@@ -29,7 +29,6 @@ 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';
33
32
 
34
33
  const MAX_SILENT_FRAME_DURATION = 10 * 1000; // 10 seconds
35
34
  const AAC_SAMPLES_PER_FRAME = 1024;
@@ -42,7 +41,7 @@ let safariWebkitVersion: number | null = null;
42
41
  export default class MP4Remuxer implements Remuxer {
43
42
  private observer: HlsEventEmitter;
44
43
  private config: HlsConfig;
45
- private typeSupported: TypeSupported;
44
+ private typeSupported: any;
46
45
  private ISGenerated: boolean = false;
47
46
  private _initPTS: RationalTimestamp | null = null;
48
47
  private _initDTS: RationalTimestamp | null = null;
@@ -928,7 +927,7 @@ export default class MP4Remuxer implements Remuxer {
928
927
  for (let j = 0; j < missing; j++) {
929
928
  const newStamp = Math.max(nextPts as number, 0);
930
929
  let fillFrame = AAC.getSilentFrame(
931
- track.parsedCodec || track.manifestCodec || track.codec,
930
+ track.manifestCodec || track.codec,
932
931
  track.channelCount,
933
932
  );
934
933
  if (!fillFrame) {
@@ -1078,7 +1077,7 @@ export default class MP4Remuxer implements Remuxer {
1078
1077
  const nbSamples: number = Math.ceil((endDTS - startDTS) / frameDuration);
1079
1078
  // silent frame
1080
1079
  const silentFrame: Uint8Array | undefined = AAC.getSilentFrame(
1081
- track.parsedCodec || track.manifestCodec || track.codec,
1080
+ track.manifestCodec || track.codec,
1082
1081
  track.channelCount,
1083
1082
  );
1084
1083
 
@@ -64,7 +64,6 @@ export interface DemuxedAudioTrack extends DemuxedTrack {
64
64
  segmentCodec?: string;
65
65
  channelCount?: number;
66
66
  manifestCodec?: string;
67
- parsedCodec?: string;
68
67
  samples: AudioSample[];
69
68
  }
70
69
 
@@ -147,15 +147,12 @@ 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
150
153
  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'],
159
156
  }[lowerCaseCodec];
160
157
 
161
158
  for (let i = 0; i < codecsToCheck.length; i++) {
@@ -168,18 +165,13 @@ function getCodecCompatibleNameLower(
168
165
  ) {
169
166
  CODEC_COMPATIBLE_NAMES[lowerCaseCodec] = codecsToCheck[i];
170
167
  return codecsToCheck[i];
171
- } else if (
172
- codecsToCheck[i] === 'mp3' &&
173
- getMediaSource(preferManagedMediaSource)?.isTypeSupported('audio/mpeg')
174
- ) {
175
- return '';
176
168
  }
177
169
  }
178
170
 
179
171
  return lowerCaseCodec;
180
172
  }
181
173
 
182
- const AUDIO_CODEC_REGEXP = /flac|opus|mp4a\.40\.34/i;
174
+ const AUDIO_CODEC_REGEXP = /flac|opus/i;
183
175
  export function getCodecCompatibleName(
184
176
  codec: string,
185
177
  preferManagedMediaSource = true,
@@ -217,24 +209,3 @@ export function convertAVC1ToAVCOTI(codec: string) {
217
209
  }
218
210
  return codec;
219
211
  }
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
- }
@@ -1,4 +0,0 @@
1
- export const enum DecrypterAesMode {
2
- cbc = 0,
3
- ctr = 1,
4
- }
@@ -1,21 +0,0 @@
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
- }