hls.js 1.5.14 → 1.5.16
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/dist/hls-demo.js +3 -2
- package/dist/hls-demo.js.map +1 -1
- package/dist/hls.js +98 -45
- package/dist/hls.js.map +1 -1
- package/dist/hls.light.js +3 -5
- 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 +3 -5
- 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 +92 -45
- 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 +1 -1
- package/src/controller/content-steering-controller.ts +1 -2
- package/src/controller/eme-controller.ts +39 -19
- package/src/utils/mediakeys-helper.ts +11 -8
- package/src/utils/mp4-tools.ts +72 -27
package/package.json
CHANGED
@@ -232,9 +232,8 @@ export default class ContentSteeringController implements NetworkComponentAPI {
|
|
232
232
|
this.log(
|
233
233
|
`Found ${pathwayLevels.length}/${levels.length} levels in Pathway "${this.pathwayId}"`,
|
234
234
|
);
|
235
|
-
return pathwayLevels;
|
236
235
|
}
|
237
|
-
return
|
236
|
+
return pathwayLevels;
|
238
237
|
}
|
239
238
|
|
240
239
|
private getLevelsForPathway(pathwayId: string): Level[] {
|
@@ -14,8 +14,6 @@ import {
|
|
14
14
|
keySystemFormatToKeySystemDomain,
|
15
15
|
KeySystemIds,
|
16
16
|
keySystemIdToKeySystemDomain,
|
17
|
-
} from '../utils/mediakeys-helper';
|
18
|
-
import {
|
19
17
|
KeySystems,
|
20
18
|
requestMediaKeySystemAccess,
|
21
19
|
} from '../utils/mediakeys-helper';
|
@@ -23,7 +21,13 @@ import { strToUtf8array } from '../utils/keysystem-util';
|
|
23
21
|
import { base64Decode } from '../utils/numeric-encoding-utils';
|
24
22
|
import { DecryptData, LevelKey } from '../loader/level-key';
|
25
23
|
import Hex from '../utils/hex';
|
26
|
-
import {
|
24
|
+
import {
|
25
|
+
bin2str,
|
26
|
+
parseMultiPssh,
|
27
|
+
parseSinf,
|
28
|
+
PsshData,
|
29
|
+
PsshInvalidResult,
|
30
|
+
} from '../utils/mp4-tools';
|
27
31
|
import { EventEmitter } from 'eventemitter3';
|
28
32
|
import type Hls from '../hls';
|
29
33
|
import type { ComponentAPI } from '../types/component-api';
|
@@ -525,7 +529,8 @@ class EMEController implements ComponentAPI {
|
|
525
529
|
|
526
530
|
private _onMediaEncrypted(event: MediaEncryptedEvent) {
|
527
531
|
const { initDataType, initData } = event;
|
528
|
-
|
532
|
+
const logMessage = `"${event.type}" event: init data type: "${initDataType}"`;
|
533
|
+
this.debug(logMessage);
|
529
534
|
|
530
535
|
// Ignore event when initData is null
|
531
536
|
if (initData === null) {
|
@@ -545,30 +550,42 @@ class EMEController implements ComponentAPI {
|
|
545
550
|
const sinf = base64Decode(JSON.parse(json).sinf);
|
546
551
|
const tenc = parseSinf(new Uint8Array(sinf));
|
547
552
|
if (!tenc) {
|
548
|
-
|
553
|
+
throw new Error(
|
554
|
+
`'schm' box missing or not cbcs/cenc with schi > tenc`,
|
555
|
+
);
|
549
556
|
}
|
550
557
|
keyId = tenc.subarray(8, 24);
|
551
558
|
keySystemDomain = KeySystems.FAIRPLAY;
|
552
559
|
} catch (error) {
|
553
|
-
this.warn(
|
560
|
+
this.warn(`${logMessage} Failed to parse sinf: ${error}`);
|
554
561
|
return;
|
555
562
|
}
|
556
563
|
} else {
|
557
|
-
// Support clear-lead key-session creation (otherwise depend on playlist keys)
|
558
|
-
const
|
559
|
-
|
564
|
+
// Support Widevine clear-lead key-session creation (otherwise depend on playlist keys)
|
565
|
+
const psshResults = parseMultiPssh(initData);
|
566
|
+
const psshInfo = psshResults.filter(
|
567
|
+
(pssh): pssh is PsshData => pssh.systemId === KeySystemIds.WIDEVINE,
|
568
|
+
)[0];
|
569
|
+
if (!psshInfo) {
|
570
|
+
if (
|
571
|
+
psshResults.length === 0 ||
|
572
|
+
psshResults.some((pssh): pssh is PsshInvalidResult => !pssh.systemId)
|
573
|
+
) {
|
574
|
+
this.warn(`${logMessage} contains incomplete or invalid pssh data`);
|
575
|
+
} else {
|
576
|
+
this.log(
|
577
|
+
`ignoring ${logMessage} for ${(psshResults as PsshData[])
|
578
|
+
.map((pssh) => keySystemIdToKeySystemDomain(pssh.systemId))
|
579
|
+
.join(',')} pssh data in favor of playlist keys`,
|
580
|
+
);
|
581
|
+
}
|
560
582
|
return;
|
561
583
|
}
|
562
|
-
|
563
|
-
|
564
|
-
psshInfo.
|
565
|
-
psshInfo.data
|
566
|
-
) {
|
567
|
-
keyId = psshInfo.data.subarray(8, 24);
|
584
|
+
keySystemDomain = keySystemIdToKeySystemDomain(psshInfo.systemId);
|
585
|
+
if (psshInfo.version === 0 && psshInfo.data) {
|
586
|
+
const offset = psshInfo.data.length - 22;
|
587
|
+
keyId = psshInfo.data.subarray(offset, offset + 16);
|
568
588
|
}
|
569
|
-
keySystemDomain = keySystemIdToKeySystemDomain(
|
570
|
-
psshInfo.systemId as KeySystemIds,
|
571
|
-
);
|
572
589
|
}
|
573
590
|
|
574
591
|
if (!keySystemDomain || !keyId) {
|
@@ -583,7 +600,7 @@ class EMEController implements ComponentAPI {
|
|
583
600
|
// Match playlist key
|
584
601
|
const keyContext = mediaKeySessions[i];
|
585
602
|
const decryptdata = keyContext.decryptdata;
|
586
|
-
if (
|
603
|
+
if (!decryptdata.keyId) {
|
587
604
|
continue;
|
588
605
|
}
|
589
606
|
const oldKeyIdHex = Hex.hexDump(decryptdata.keyId);
|
@@ -592,6 +609,9 @@ class EMEController implements ComponentAPI {
|
|
592
609
|
decryptdata.uri.replace(/-/g, '').indexOf(keyIdHex) !== -1
|
593
610
|
) {
|
594
611
|
keySessionContextPromise = keyIdToKeySessionPromise[oldKeyIdHex];
|
612
|
+
if (decryptdata.pssh) {
|
613
|
+
break;
|
614
|
+
}
|
595
615
|
delete keyIdToKeySessionPromise[oldKeyIdHex];
|
596
616
|
decryptdata.pssh = new Uint8Array(initData);
|
597
617
|
decryptdata.keyId = keyId;
|
@@ -36,10 +36,10 @@ export function keySystemFormatToKeySystemDomain(
|
|
36
36
|
|
37
37
|
// System IDs for which we can extract a key ID from "encrypted" event PSSH
|
38
38
|
export const enum KeySystemIds {
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
39
|
+
CENC = '1077efecc0b24d02ace33c1e52e2fb4b',
|
40
|
+
CLEARKEY = 'e2719d58a985b3c9781ab030af78d30e',
|
41
|
+
FAIRPLAY = '94ce86fb07ff4f43adb893d2fa968ca2',
|
42
|
+
PLAYREADY = '9a04f07998404286ab92e65be0885f95',
|
43
43
|
WIDEVINE = 'edef8ba979d64acea3c827dcd51d21ed',
|
44
44
|
}
|
45
45
|
|
@@ -48,10 +48,13 @@ export function keySystemIdToKeySystemDomain(
|
|
48
48
|
): KeySystems | undefined {
|
49
49
|
if (systemId === KeySystemIds.WIDEVINE) {
|
50
50
|
return KeySystems.WIDEVINE;
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
51
|
+
} else if (systemId === KeySystemIds.PLAYREADY) {
|
52
|
+
return KeySystems.PLAYREADY;
|
53
|
+
} else if (
|
54
|
+
systemId === KeySystemIds.CENC ||
|
55
|
+
systemId === KeySystemIds.CLEARKEY
|
56
|
+
) {
|
57
|
+
return KeySystems.CLEARKEY;
|
55
58
|
}
|
56
59
|
}
|
57
60
|
|
package/src/utils/mp4-tools.ts
CHANGED
@@ -3,6 +3,7 @@ import { sliceUint8 } from './typed-array';
|
|
3
3
|
import { utf8ArrayToStr } from '../demux/id3';
|
4
4
|
import { logger } from '../utils/logger';
|
5
5
|
import Hex from './hex';
|
6
|
+
import type { KeySystemIds } from './mediakeys-helper';
|
6
7
|
import type { PassthroughTrack, UserdataSample } from '../types/demuxer';
|
7
8
|
import type { DecryptData } from '../loader/level-key';
|
8
9
|
|
@@ -545,7 +546,6 @@ export function parseSinf(sinf: Uint8Array): Uint8Array | null {
|
|
545
546
|
return findBox(sinf, ['schi', 'tenc'])[0];
|
546
547
|
}
|
547
548
|
}
|
548
|
-
logger.error(`[eme] missing 'schm' box`);
|
549
549
|
return null;
|
550
550
|
}
|
551
551
|
|
@@ -1349,41 +1349,86 @@ export function mp4pssh(
|
|
1349
1349
|
);
|
1350
1350
|
}
|
1351
1351
|
|
1352
|
-
export
|
1353
|
-
|
1354
|
-
|
1352
|
+
export type PsshData = {
|
1353
|
+
version: 0 | 1;
|
1354
|
+
systemId: KeySystemIds;
|
1355
|
+
kids: null | Uint8Array[];
|
1356
|
+
data: null | Uint8Array;
|
1357
|
+
offset: number;
|
1358
|
+
size: number;
|
1359
|
+
};
|
1360
|
+
|
1361
|
+
export type PsshInvalidResult = {
|
1362
|
+
systemId?: undefined;
|
1363
|
+
offset: number;
|
1364
|
+
size: number;
|
1365
|
+
};
|
1366
|
+
|
1367
|
+
export function parseMultiPssh(
|
1368
|
+
initData: ArrayBuffer,
|
1369
|
+
): (PsshData | PsshInvalidResult)[] {
|
1370
|
+
const results: (PsshData | PsshInvalidResult)[] = [];
|
1371
|
+
if (initData instanceof ArrayBuffer) {
|
1372
|
+
const length = initData.byteLength;
|
1373
|
+
let offset = 0;
|
1374
|
+
while (offset + 32 < length) {
|
1375
|
+
const view = new DataView(initData, offset);
|
1376
|
+
const pssh = parsePssh(view);
|
1377
|
+
results.push(pssh);
|
1378
|
+
offset += pssh.size;
|
1379
|
+
}
|
1355
1380
|
}
|
1356
|
-
|
1357
|
-
|
1358
|
-
|
1359
|
-
|
1360
|
-
|
1361
|
-
|
1362
|
-
const
|
1363
|
-
|
1364
|
-
|
1365
|
-
|
1381
|
+
return results;
|
1382
|
+
}
|
1383
|
+
|
1384
|
+
function parsePssh(view: DataView): PsshData | PsshInvalidResult {
|
1385
|
+
const size = view.getUint32(0);
|
1386
|
+
const offset = view.byteOffset;
|
1387
|
+
const length = view.byteLength;
|
1388
|
+
if (length < size) {
|
1389
|
+
return {
|
1390
|
+
offset,
|
1391
|
+
size: length,
|
1392
|
+
};
|
1366
1393
|
}
|
1367
1394
|
const type = view.getUint32(4);
|
1368
1395
|
if (type !== 0x70737368) {
|
1369
|
-
return
|
1396
|
+
return { offset, size };
|
1370
1397
|
}
|
1371
|
-
|
1372
|
-
if (
|
1373
|
-
return
|
1398
|
+
const version = view.getUint32(8) >>> 24;
|
1399
|
+
if (version !== 0 && version !== 1) {
|
1400
|
+
return { offset, size };
|
1374
1401
|
}
|
1375
|
-
|
1402
|
+
const buffer = view.buffer;
|
1403
|
+
const systemId = Hex.hexDump(
|
1404
|
+
new Uint8Array(buffer, offset + 12, 16),
|
1405
|
+
) as KeySystemIds;
|
1376
1406
|
const dataSizeOrKidCount = view.getUint32(28);
|
1377
|
-
|
1378
|
-
|
1379
|
-
|
1407
|
+
let kids: null | Uint8Array[] = null;
|
1408
|
+
let data: null | Uint8Array = null;
|
1409
|
+
if (version === 0) {
|
1410
|
+
if (size - 32 < dataSizeOrKidCount || dataSizeOrKidCount < 22) {
|
1411
|
+
return { offset, size };
|
1412
|
+
}
|
1413
|
+
data = new Uint8Array(buffer, offset + 32, dataSizeOrKidCount);
|
1414
|
+
} else if (version === 1) {
|
1415
|
+
if (
|
1416
|
+
!dataSizeOrKidCount ||
|
1417
|
+
length < offset + 32 + dataSizeOrKidCount * 16 + 16
|
1418
|
+
) {
|
1419
|
+
return { offset, size };
|
1380
1420
|
}
|
1381
|
-
|
1382
|
-
} else if (result.version === 1) {
|
1383
|
-
result.kids = [];
|
1421
|
+
kids = [];
|
1384
1422
|
for (let i = 0; i < dataSizeOrKidCount; i++) {
|
1385
|
-
|
1423
|
+
kids.push(new Uint8Array(buffer, offset + 32 + i * 16, 16));
|
1386
1424
|
}
|
1387
1425
|
}
|
1388
|
-
return
|
1426
|
+
return {
|
1427
|
+
version,
|
1428
|
+
systemId,
|
1429
|
+
kids,
|
1430
|
+
data,
|
1431
|
+
offset,
|
1432
|
+
size,
|
1433
|
+
};
|
1389
1434
|
}
|