hls.js 1.5.14-0.canary.10555 → 1.5.14-0.canary.10559

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/package.json CHANGED
@@ -111,7 +111,7 @@
111
111
  "karma-rollup-preprocessor": "github:jlmakes/karma-rollup-preprocessor#7a7268d91149307b3cf2888ee4e65ccd079955a3",
112
112
  "karma-sinon-chai": "2.0.2",
113
113
  "karma-sourcemap-loader": "0.4.0",
114
- "lint-staged": "15.2.8",
114
+ "lint-staged": "15.2.9",
115
115
  "markdown-styles": "3.2.0",
116
116
  "micromatch": "4.0.7",
117
117
  "mocha": "10.7.3",
@@ -130,5 +130,5 @@
130
130
  "url-toolkit": "2.2.5",
131
131
  "wrangler": "3.72.0"
132
132
  },
133
- "version": "1.5.14-0.canary.10555"
133
+ "version": "1.5.14-0.canary.10559"
134
134
  }
@@ -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/utf8-utils';
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 { bin2str, parsePssh, parseSinf } from '../utils/mp4-tools';
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';
@@ -512,7 +516,8 @@ class EMEController extends Logger implements ComponentAPI {
512
516
 
513
517
  private onMediaEncrypted = (event: MediaEncryptedEvent) => {
514
518
  const { initDataType, initData } = event;
515
- this.debug(`"${event.type}" event: init data type: "${initDataType}"`);
519
+ const logMessage = `"${event.type}" event: init data type: "${initDataType}"`;
520
+ this.debug(logMessage);
516
521
 
517
522
  // Ignore event when initData is null
518
523
  if (initData === null) {
@@ -532,30 +537,42 @@ class EMEController extends Logger implements ComponentAPI {
532
537
  const sinf = base64Decode(JSON.parse(json).sinf);
533
538
  const tenc = parseSinf(new Uint8Array(sinf));
534
539
  if (!tenc) {
535
- return;
540
+ throw new Error(
541
+ `'schm' box missing or not cbcs/cenc with schi > tenc`,
542
+ );
536
543
  }
537
544
  keyId = tenc.subarray(8, 24);
538
545
  keySystemDomain = KeySystems.FAIRPLAY;
539
546
  } catch (error) {
540
- this.warn('Failed to parse sinf "encrypted" event message initData');
547
+ this.warn(`${logMessage} Failed to parse sinf: ${error}`);
541
548
  return;
542
549
  }
543
550
  } else {
544
- // Support clear-lead key-session creation (otherwise depend on playlist keys)
545
- const psshInfo = parsePssh(initData);
546
- if (psshInfo === null) {
551
+ // Support Widevine clear-lead key-session creation (otherwise depend on playlist keys)
552
+ const psshResults = parseMultiPssh(initData);
553
+ const psshInfo = psshResults.filter(
554
+ (pssh): pssh is PsshData => pssh.systemId === KeySystemIds.WIDEVINE,
555
+ )[0];
556
+ if (!psshInfo) {
557
+ if (
558
+ psshResults.length === 0 ||
559
+ psshResults.some((pssh): pssh is PsshInvalidResult => !pssh.systemId)
560
+ ) {
561
+ this.warn(`${logMessage} contains incomplete or invalid pssh data`);
562
+ } else {
563
+ this.log(
564
+ `ignoring ${logMessage} for ${(psshResults as PsshData[])
565
+ .map((pssh) => keySystemIdToKeySystemDomain(pssh.systemId))
566
+ .join(',')} pssh data in favor of playlist keys`,
567
+ );
568
+ }
547
569
  return;
548
570
  }
549
- if (
550
- psshInfo.version === 0 &&
551
- psshInfo.systemId === KeySystemIds.WIDEVINE &&
552
- psshInfo.data
553
- ) {
554
- keyId = psshInfo.data.subarray(8, 24);
571
+ keySystemDomain = keySystemIdToKeySystemDomain(psshInfo.systemId);
572
+ if (psshInfo.version === 0 && psshInfo.data) {
573
+ const offset = psshInfo.data.length - 22;
574
+ keyId = psshInfo.data.subarray(offset, offset + 16);
555
575
  }
556
- keySystemDomain = keySystemIdToKeySystemDomain(
557
- psshInfo.systemId as KeySystemIds,
558
- );
559
576
  }
560
577
 
561
578
  if (!keySystemDomain || !keyId) {
@@ -570,7 +587,7 @@ class EMEController extends Logger implements ComponentAPI {
570
587
  // Match playlist key
571
588
  const keyContext = mediaKeySessions[i];
572
589
  const decryptdata = keyContext.decryptdata;
573
- if (decryptdata.pssh || !decryptdata.keyId) {
590
+ if (!decryptdata.keyId) {
574
591
  continue;
575
592
  }
576
593
  const oldKeyIdHex = Hex.hexDump(decryptdata.keyId);
@@ -579,6 +596,9 @@ class EMEController extends Logger implements ComponentAPI {
579
596
  decryptdata.uri.replace(/-/g, '').indexOf(keyIdHex) !== -1
580
597
  ) {
581
598
  keySessionContextPromise = keyIdToKeySessionPromise[oldKeyIdHex];
599
+ if (decryptdata.pssh) {
600
+ break;
601
+ }
582
602
  delete keyIdToKeySessionPromise[oldKeyIdHex];
583
603
  decryptdata.pssh = new Uint8Array(initData);
584
604
  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
- // CENC = '1077efecc0b24d02ace33c1e52e2fb4b'
40
- // CLEARKEY = 'e2719d58a985b3c9781ab030af78d30e',
41
- // FAIRPLAY = '94ce86fb07ff4f43adb893d2fa968ca2',
42
- // PLAYREADY = '9a04f07998404286ab92e65be0885f95',
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
- // } else if (systemId === KeySystemIds.PLAYREADY) {
52
- // return KeySystems.PLAYREADY;
53
- // } else if (systemId === KeySystemIds.CENC || systemId === KeySystemIds.CLEARKEY) {
54
- // return KeySystems.CLEARKEY;
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
 
@@ -3,6 +3,7 @@ import { sliceUint8 } from './typed-array';
3
3
  import { utf8ArrayToStr } from '@svta/common-media-library/utils/utf8ArrayToStr';
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
 
@@ -547,7 +548,6 @@ export function parseSinf(sinf: Uint8Array): Uint8Array | null {
547
548
  return findBox(sinf, ['schi', 'tenc'])[0];
548
549
  }
549
550
  }
550
- logger.error(`[eme] missing 'schm' box`);
551
551
  return null;
552
552
  }
553
553
 
@@ -1351,41 +1351,86 @@ export function mp4pssh(
1351
1351
  );
1352
1352
  }
1353
1353
 
1354
- export function parsePssh(initData: ArrayBuffer) {
1355
- if (!(initData instanceof ArrayBuffer) || initData.byteLength < 32) {
1356
- return null;
1354
+ export type PsshData = {
1355
+ version: 0 | 1;
1356
+ systemId: KeySystemIds;
1357
+ kids: null | Uint8Array[];
1358
+ data: null | Uint8Array;
1359
+ offset: number;
1360
+ size: number;
1361
+ };
1362
+
1363
+ export type PsshInvalidResult = {
1364
+ systemId?: undefined;
1365
+ offset: number;
1366
+ size: number;
1367
+ };
1368
+
1369
+ export function parseMultiPssh(
1370
+ initData: ArrayBuffer,
1371
+ ): (PsshData | PsshInvalidResult)[] {
1372
+ const results: (PsshData | PsshInvalidResult)[] = [];
1373
+ if (initData instanceof ArrayBuffer) {
1374
+ const length = initData.byteLength;
1375
+ let offset = 0;
1376
+ while (offset + 32 < length) {
1377
+ const view = new DataView(initData, offset);
1378
+ const pssh = parsePssh(view);
1379
+ results.push(pssh);
1380
+ offset += pssh.size;
1381
+ }
1357
1382
  }
1358
- const result = {
1359
- version: 0,
1360
- systemId: '',
1361
- kids: null as null | Uint8Array[],
1362
- data: null as null | Uint8Array,
1363
- };
1364
- const view = new DataView(initData);
1365
- const boxSize = view.getUint32(0);
1366
- if (initData.byteLength !== boxSize && boxSize > 44) {
1367
- return null;
1383
+ return results;
1384
+ }
1385
+
1386
+ function parsePssh(view: DataView): PsshData | PsshInvalidResult {
1387
+ const size = view.getUint32(0);
1388
+ const offset = view.byteOffset;
1389
+ const length = view.byteLength;
1390
+ if (length < size) {
1391
+ return {
1392
+ offset,
1393
+ size: length,
1394
+ };
1368
1395
  }
1369
1396
  const type = view.getUint32(4);
1370
1397
  if (type !== 0x70737368) {
1371
- return null;
1398
+ return { offset, size };
1372
1399
  }
1373
- result.version = view.getUint32(8) >>> 24;
1374
- if (result.version > 1) {
1375
- return null;
1400
+ const version = view.getUint32(8) >>> 24;
1401
+ if (version !== 0 && version !== 1) {
1402
+ return { offset, size };
1376
1403
  }
1377
- result.systemId = Hex.hexDump(new Uint8Array(initData, 12, 16));
1404
+ const buffer = view.buffer;
1405
+ const systemId = Hex.hexDump(
1406
+ new Uint8Array(buffer, offset + 12, 16),
1407
+ ) as KeySystemIds;
1378
1408
  const dataSizeOrKidCount = view.getUint32(28);
1379
- if (result.version === 0) {
1380
- if (boxSize - 32 < dataSizeOrKidCount) {
1381
- return null;
1409
+ let kids: null | Uint8Array[] = null;
1410
+ let data: null | Uint8Array = null;
1411
+ if (version === 0) {
1412
+ if (size - 32 < dataSizeOrKidCount || dataSizeOrKidCount < 22) {
1413
+ return { offset, size };
1414
+ }
1415
+ data = new Uint8Array(buffer, offset + 32, dataSizeOrKidCount);
1416
+ } else if (version === 1) {
1417
+ if (
1418
+ !dataSizeOrKidCount ||
1419
+ length < offset + 32 + dataSizeOrKidCount * 16 + 16
1420
+ ) {
1421
+ return { offset, size };
1382
1422
  }
1383
- result.data = new Uint8Array(initData, 32, dataSizeOrKidCount);
1384
- } else if (result.version === 1) {
1385
- result.kids = [];
1423
+ kids = [];
1386
1424
  for (let i = 0; i < dataSizeOrKidCount; i++) {
1387
- result.kids.push(new Uint8Array(initData, 32 + i * 16, 16));
1425
+ kids.push(new Uint8Array(buffer, offset + 32 + i * 16, 16));
1388
1426
  }
1389
1427
  }
1390
- return result;
1428
+ return {
1429
+ version,
1430
+ systemId,
1431
+ kids,
1432
+ data,
1433
+ offset,
1434
+ size,
1435
+ };
1391
1436
  }