hls.js 1.6.0-beta.2.0.canary.10876 → 1.6.0-beta.2.0.canary.10878
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.js +127 -105
- package/dist/hls.js.map +1 -1
- package/dist/hls.light.js +3 -3
- 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 -3
- 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 +110 -92
- package/dist/hls.mjs.map +1 -1
- package/dist/hls.worker.js +1 -1
- package/package.json +1 -1
- package/src/controller/eme-controller.ts +154 -116
- package/src/loader/key-loader.ts +6 -1
@@ -535,147 +535,185 @@ class EMEController extends Logger implements ComponentAPI {
|
|
535
535
|
return;
|
536
536
|
}
|
537
537
|
|
538
|
-
|
539
|
-
let keySystemDomain: KeySystems | undefined;
|
540
|
-
|
541
|
-
if (
|
542
|
-
initDataType === 'sinf' &&
|
543
|
-
this.getLicenseServerUrl(KeySystems.FAIRPLAY)
|
544
|
-
) {
|
545
|
-
// Match sinf keyId to playlist skd://keyId=
|
546
|
-
const json = bin2str(new Uint8Array(initData));
|
547
|
-
try {
|
548
|
-
const sinf = base64Decode(JSON.parse(json).sinf);
|
549
|
-
const tenc = parseSinf(sinf);
|
550
|
-
if (!tenc) {
|
551
|
-
throw new Error(
|
552
|
-
`'schm' box missing or not cbcs/cenc with schi > tenc`,
|
553
|
-
);
|
554
|
-
}
|
555
|
-
keyId = tenc.subarray(8, 24);
|
556
|
-
keySystemDomain = KeySystems.FAIRPLAY;
|
557
|
-
} catch (error) {
|
558
|
-
this.warn(`${logMessage} Failed to parse sinf: ${error}`);
|
559
|
-
return;
|
560
|
-
}
|
561
|
-
} else if (this.getLicenseServerUrl(KeySystems.WIDEVINE)) {
|
562
|
-
// Support Widevine clear-lead key-session creation (otherwise depend on playlist keys)
|
563
|
-
const psshResults = parseMultiPssh(initData);
|
564
|
-
|
565
|
-
// TODO: If using keySystemAccessPromises we might want to wait until one is resolved
|
538
|
+
if (!this.keyFormatPromise) {
|
566
539
|
let keySystems = Object.keys(
|
567
540
|
this.keySystemAccessPromises,
|
568
541
|
) as KeySystems[];
|
569
542
|
if (!keySystems.length) {
|
570
543
|
keySystems = getKeySystemsForConfig(this.config);
|
571
544
|
}
|
545
|
+
const keyFormats = keySystems
|
546
|
+
.map(keySystemToKeySystemFormat)
|
547
|
+
.filter((k) => !!k) as KeySystemFormats[];
|
548
|
+
this.keyFormatPromise = this.getKeyFormatPromise(keyFormats);
|
549
|
+
}
|
572
550
|
|
573
|
-
|
574
|
-
|
575
|
-
? keySystemIdToKeySystemDomain(pssh.systemId)
|
576
|
-
: null;
|
577
|
-
return keySystem ? keySystems.indexOf(keySystem) > -1 : false;
|
578
|
-
})[0];
|
551
|
+
this.keyFormatPromise.then((keySystemFormat) => {
|
552
|
+
const keySystem = keySystemFormatToKeySystemDomain(keySystemFormat);
|
579
553
|
|
580
|
-
|
554
|
+
let keyId: Uint8Array | null | undefined;
|
555
|
+
let keySystemDomain: KeySystems | undefined;
|
556
|
+
|
557
|
+
if (initDataType === 'sinf') {
|
558
|
+
if (keySystem !== KeySystems.FAIRPLAY) {
|
559
|
+
this.warn(
|
560
|
+
`Ignoring unexpected "${event.type}" event with init data type: "${initDataType}" for selected key-system ${keySystem}`,
|
561
|
+
);
|
562
|
+
return;
|
563
|
+
}
|
564
|
+
// Match sinf keyId to playlist skd://keyId=
|
565
|
+
const json = bin2str(new Uint8Array(initData));
|
566
|
+
try {
|
567
|
+
const sinf = base64Decode(JSON.parse(json).sinf);
|
568
|
+
const tenc = parseSinf(sinf);
|
569
|
+
if (!tenc) {
|
570
|
+
throw new Error(
|
571
|
+
`'schm' box missing or not cbcs/cenc with schi > tenc`,
|
572
|
+
);
|
573
|
+
}
|
574
|
+
keyId = tenc.subarray(8, 24);
|
575
|
+
keySystemDomain = KeySystems.FAIRPLAY;
|
576
|
+
} catch (error) {
|
577
|
+
this.warn(`${logMessage} Failed to parse sinf: ${error}`);
|
578
|
+
return;
|
579
|
+
}
|
580
|
+
} else {
|
581
581
|
if (
|
582
|
-
|
583
|
-
|
582
|
+
keySystem !== KeySystems.WIDEVINE &&
|
583
|
+
keySystem !== KeySystems.PLAYREADY
|
584
584
|
) {
|
585
|
-
this.warn(
|
586
|
-
|
587
|
-
this.log(
|
588
|
-
`ignoring ${logMessage} for ${(psshResults as PsshData[])
|
589
|
-
.map((pssh) => keySystemIdToKeySystemDomain(pssh.systemId))
|
590
|
-
.join(',')} pssh data in favor of playlist keys`,
|
585
|
+
this.warn(
|
586
|
+
`Ignoring unexpected "${event.type}" event with init data type: "${initDataType}" for selected key-system ${keySystem}`,
|
591
587
|
);
|
588
|
+
return;
|
592
589
|
}
|
593
|
-
|
594
|
-
|
590
|
+
// Support Widevine/PlayReady clear-lead key-session creation (otherwise depend on playlist keys)
|
591
|
+
const psshResults = parseMultiPssh(initData);
|
595
592
|
|
596
|
-
|
597
|
-
|
598
|
-
|
599
|
-
|
600
|
-
|
601
|
-
|
602
|
-
|
593
|
+
const psshInfos = psshResults.filter(
|
594
|
+
(pssh): pssh is PsshData =>
|
595
|
+
!!pssh.systemId &&
|
596
|
+
keySystemIdToKeySystemDomain(pssh.systemId) === keySystem,
|
597
|
+
);
|
598
|
+
|
599
|
+
if (psshInfos.length > 1) {
|
600
|
+
this.warn(
|
601
|
+
`${logMessage} Using first of ${psshInfos.length} pssh found for selected key-system ${keySystem}`,
|
602
|
+
);
|
603
603
|
}
|
604
|
-
}
|
605
|
-
}
|
606
604
|
|
607
|
-
|
608
|
-
return;
|
609
|
-
}
|
605
|
+
const psshInfo = psshInfos[0];
|
610
606
|
|
611
|
-
|
612
|
-
|
607
|
+
if (!psshInfo) {
|
608
|
+
if (
|
609
|
+
psshResults.length === 0 ||
|
610
|
+
psshResults.some(
|
611
|
+
(pssh): pssh is PsshInvalidResult => !pssh.systemId,
|
612
|
+
)
|
613
|
+
) {
|
614
|
+
this.warn(`${logMessage} contains incomplete or invalid pssh data`);
|
615
|
+
} else {
|
616
|
+
this.log(
|
617
|
+
`ignoring ${logMessage} for ${(psshResults as PsshData[])
|
618
|
+
.map((pssh) => keySystemIdToKeySystemDomain(pssh.systemId))
|
619
|
+
.join(',')} pssh data in favor of playlist keys`,
|
620
|
+
);
|
621
|
+
}
|
622
|
+
return;
|
623
|
+
}
|
613
624
|
|
614
|
-
|
615
|
-
|
616
|
-
|
617
|
-
|
618
|
-
|
619
|
-
|
620
|
-
|
621
|
-
|
622
|
-
const oldKeyIdHex = Hex.hexDump(decryptdata.keyId);
|
623
|
-
if (
|
624
|
-
keyIdHex === oldKeyIdHex ||
|
625
|
-
decryptdata.uri.replace(/-/g, '').indexOf(keyIdHex) !== -1
|
626
|
-
) {
|
627
|
-
keySessionContextPromise = keyIdToKeySessionPromise[oldKeyIdHex];
|
628
|
-
if (decryptdata.pssh) {
|
629
|
-
break;
|
625
|
+
keySystemDomain = keySystemIdToKeySystemDomain(psshInfo.systemId);
|
626
|
+
if (psshInfo.version === 0 && psshInfo.data) {
|
627
|
+
if (keySystemDomain === KeySystems.WIDEVINE) {
|
628
|
+
const offset = psshInfo.data.length - 22;
|
629
|
+
keyId = psshInfo.data.subarray(offset, offset + 16);
|
630
|
+
} else if (keySystemDomain === KeySystems.PLAYREADY) {
|
631
|
+
keyId = parsePlayReadyWRM(psshInfo.data);
|
632
|
+
}
|
630
633
|
}
|
631
|
-
delete keyIdToKeySessionPromise[oldKeyIdHex];
|
632
|
-
decryptdata.pssh = new Uint8Array(initData);
|
633
|
-
decryptdata.keyId = keyId;
|
634
|
-
keySessionContextPromise = keyIdToKeySessionPromise[keyIdHex] =
|
635
|
-
keySessionContextPromise.then(() => {
|
636
|
-
return this.generateRequestWithPreferredKeySession(
|
637
|
-
keyContext,
|
638
|
-
initDataType,
|
639
|
-
initData,
|
640
|
-
'encrypted-event-key-match',
|
641
|
-
);
|
642
|
-
});
|
643
|
-
keySessionContextPromise.catch((error) => this.handleError(error));
|
644
|
-
break;
|
645
634
|
}
|
646
|
-
}
|
647
635
|
|
648
|
-
|
649
|
-
|
650
|
-
|
651
|
-
|
652
|
-
|
653
|
-
|
654
|
-
|
655
|
-
|
656
|
-
|
657
|
-
|
658
|
-
|
659
|
-
|
660
|
-
|
661
|
-
|
662
|
-
|
663
|
-
|
664
|
-
|
665
|
-
|
666
|
-
|
667
|
-
|
636
|
+
if (!keySystemDomain || !keyId) {
|
637
|
+
return;
|
638
|
+
}
|
639
|
+
|
640
|
+
const keyIdHex = Hex.hexDump(keyId);
|
641
|
+
const { keyIdToKeySessionPromise, mediaKeySessions } = this;
|
642
|
+
|
643
|
+
let keySessionContextPromise = keyIdToKeySessionPromise[keyIdHex];
|
644
|
+
for (let i = 0; i < mediaKeySessions.length; i++) {
|
645
|
+
// Match playlist key
|
646
|
+
const keyContext = mediaKeySessions[i];
|
647
|
+
const decryptdata = keyContext.decryptdata;
|
648
|
+
if (!decryptdata.keyId) {
|
649
|
+
continue;
|
650
|
+
}
|
651
|
+
const oldKeyIdHex = Hex.hexDump(decryptdata.keyId);
|
652
|
+
if (
|
653
|
+
keyIdHex === oldKeyIdHex ||
|
654
|
+
decryptdata.uri.replace(/-/g, '').indexOf(keyIdHex) !== -1
|
655
|
+
) {
|
656
|
+
keySessionContextPromise = keyIdToKeySessionPromise[oldKeyIdHex];
|
657
|
+
if (decryptdata.pssh) {
|
658
|
+
break;
|
659
|
+
}
|
660
|
+
delete keyIdToKeySessionPromise[oldKeyIdHex];
|
661
|
+
decryptdata.pssh = new Uint8Array(initData);
|
662
|
+
decryptdata.keyId = keyId;
|
663
|
+
keySessionContextPromise = keyIdToKeySessionPromise[keyIdHex] =
|
664
|
+
keySessionContextPromise.then(() => {
|
668
665
|
return this.generateRequestWithPreferredKeySession(
|
669
|
-
|
666
|
+
keyContext,
|
670
667
|
initDataType,
|
671
668
|
initData,
|
672
|
-
'encrypted-event-
|
669
|
+
'encrypted-event-key-match',
|
673
670
|
);
|
674
671
|
});
|
675
|
-
|
676
|
-
|
677
|
-
|
678
|
-
|
672
|
+
keySessionContextPromise.catch((error) => this.handleError(error));
|
673
|
+
break;
|
674
|
+
}
|
675
|
+
}
|
676
|
+
|
677
|
+
if (!keySessionContextPromise) {
|
678
|
+
if (keySystemDomain !== keySystem) {
|
679
|
+
this.log(
|
680
|
+
`Ignoring "${event.type}" event with ${keySystemDomain} init data for selected key-system ${keySystem}`,
|
681
|
+
);
|
682
|
+
return;
|
683
|
+
}
|
684
|
+
// "Clear-lead" (misc key not encountered in playlist)
|
685
|
+
keySessionContextPromise = keyIdToKeySessionPromise[keyIdHex] =
|
686
|
+
this.getKeySystemSelectionPromise([keySystemDomain]).then(
|
687
|
+
({ keySystem, mediaKeys }) => {
|
688
|
+
this.throwIfDestroyed();
|
689
|
+
|
690
|
+
const decryptdata = new LevelKey(
|
691
|
+
'ISO-23001-7',
|
692
|
+
keyIdHex,
|
693
|
+
keySystemToKeySystemFormat(keySystem) ?? '',
|
694
|
+
);
|
695
|
+
decryptdata.pssh = new Uint8Array(initData);
|
696
|
+
decryptdata.keyId = keyId as Uint8Array;
|
697
|
+
return this.attemptSetMediaKeys(keySystem, mediaKeys).then(() => {
|
698
|
+
this.throwIfDestroyed();
|
699
|
+
const keySessionContext = this.createMediaKeySessionContext({
|
700
|
+
decryptdata,
|
701
|
+
keySystem,
|
702
|
+
mediaKeys,
|
703
|
+
});
|
704
|
+
return this.generateRequestWithPreferredKeySession(
|
705
|
+
keySessionContext,
|
706
|
+
initDataType,
|
707
|
+
initData,
|
708
|
+
'encrypted-event-no-match',
|
709
|
+
);
|
710
|
+
});
|
711
|
+
},
|
712
|
+
);
|
713
|
+
|
714
|
+
keySessionContextPromise.catch((error) => this.handleError(error));
|
715
|
+
}
|
716
|
+
});
|
679
717
|
};
|
680
718
|
|
681
719
|
private onWaitingForKey = (event: Event) => {
|
package/src/loader/key-loader.ts
CHANGED
@@ -112,7 +112,12 @@ export default class KeyLoader implements ComponentAPI {
|
|
112
112
|
}
|
113
113
|
|
114
114
|
load(frag: Fragment): Promise<KeyLoadedData> {
|
115
|
-
if (
|
115
|
+
if (
|
116
|
+
!frag.decryptdata &&
|
117
|
+
frag.encrypted &&
|
118
|
+
this.emeController &&
|
119
|
+
this.config.emeEnabled
|
120
|
+
) {
|
116
121
|
// Multiple keys, but none selected, resolve in eme-controller
|
117
122
|
return this.emeController
|
118
123
|
.selectKeySystemFormat(frag)
|