@@ -22,6 +22,8 @@ import {
22
22
KeySystems ,
23
23
requestMediaKeySystemAccess ,
24
24
} from '../utils/mediakeys-helper' ;
25
+ import { bin2str , parseSinf } from '../utils/mp4-tools' ;
26
+ import { base64Decode } from '../utils/numeric-encoding-utils' ;
25
27
import { stringify } from '../utils/safe-json-stringify' ;
26
28
import { strToUtf8array } from '../utils/utf8-utils' ;
27
29
import type { EMEControllerConfig , HlsConfig , LoadPolicy } from '../config' ;
@@ -552,6 +554,105 @@ class EMEController extends Logger implements ComponentAPI {
552
554
return this . attemptKeySystemAccess ( keySystemsToAttempt ) ;
553
555
}
554
556
557
+ private onMediaEncrypted = ( event : MediaEncryptedEvent ) => {
558
+ const { initDataType, initData } = event ;
559
+ const logMessage = `"${ event . type } " event: init data type: "${ initDataType } "` ;
560
+ this . debug ( logMessage ) ;
561
+
562
+ // Ignore event when initData is null
563
+ if ( initData === null ) {
564
+ return ;
565
+ }
566
+
567
+ if ( ! this . keyFormatPromise ) {
568
+ let keySystems = Object . keys (
569
+ this . keySystemAccessPromises ,
570
+ ) as KeySystems [ ] ;
571
+ if ( ! keySystems . length ) {
572
+ keySystems = getKeySystemsForConfig ( this . config ) ;
573
+ }
574
+ const keyFormats = keySystems
575
+ . map ( keySystemDomainToKeySystemFormat )
576
+ . filter ( ( k ) => ! ! k ) as KeySystemFormats [ ] ;
577
+ this . keyFormatPromise = this . getKeyFormatPromise ( keyFormats ) ;
578
+ }
579
+
580
+ this . keyFormatPromise . then ( ( keySystemFormat ) => {
581
+ const keySystem = keySystemFormatToKeySystemDomain ( keySystemFormat ) ;
582
+ if ( initDataType !== 'sinf' || keySystem !== KeySystems . FAIRPLAY ) {
583
+ this . log (
584
+ `Ignoring "${ event . type } " event with init data type: "${ initDataType } " for selected key-system ${ keySystem } ` ,
585
+ ) ;
586
+ return ;
587
+ }
588
+
589
+ // Match sinf keyId to playlist skd://keyId=
590
+ let keyId : Uint8Array < ArrayBuffer > | undefined ;
591
+ try {
592
+ const json = bin2str ( new Uint8Array ( initData ) ) ;
593
+ const sinf = base64Decode ( JSON . parse ( json ) . sinf ) ;
594
+ const tenc = parseSinf ( sinf ) ;
595
+ if ( ! tenc ) {
596
+ throw new Error (
597
+ `'schm' box missing or not cbcs/cenc with schi > tenc` ,
598
+ ) ;
599
+ }
600
+ keyId = new Uint8Array ( tenc . subarray ( 8 , 24 ) ) ;
601
+ } catch ( error ) {
602
+ this . warn ( `${ logMessage } Failed to parse sinf: ${ error } ` ) ;
603
+ return ;
604
+ }
605
+
606
+ const keyIdHex = Hex . hexDump ( keyId ) ;
607
+ const { keyIdToKeySessionPromise, mediaKeySessions } = this ;
608
+ let keySessionContextPromise = keyIdToKeySessionPromise [ keyIdHex ] ;
609
+
610
+ for ( let i = 0 ; i < mediaKeySessions . length ; i ++ ) {
611
+ // Match playlist key
612
+ const keyContext = mediaKeySessions [ i ] ;
613
+ const decryptdata = keyContext . decryptdata ;
614
+ if ( ! decryptdata . keyId ) {
615
+ continue ;
616
+ }
617
+ const oldKeyIdHex = Hex . hexDump ( decryptdata . keyId ) ;
618
+ if (
619
+ keyIdHex === oldKeyIdHex ||
620
+ decryptdata . uri . replace ( / - / g, '' ) . indexOf ( keyIdHex ) !== - 1
621
+ ) {
622
+ keySessionContextPromise = keyIdToKeySessionPromise [ oldKeyIdHex ] ;
623
+ if ( ! keySessionContextPromise ) {
624
+ continue ;
625
+ }
626
+ if ( decryptdata . pssh ) {
627
+ break ;
628
+ }
629
+ delete keyIdToKeySessionPromise [ oldKeyIdHex ] ;
630
+ decryptdata . pssh = new Uint8Array ( initData ) ;
631
+ decryptdata . keyId = keyId ;
632
+ keySessionContextPromise = keyIdToKeySessionPromise [ keyIdHex ] =
633
+ keySessionContextPromise . then ( ( ) => {
634
+ return this . generateRequestWithPreferredKeySession (
635
+ keyContext ,
636
+ initDataType ,
637
+ initData ,
638
+ 'encrypted-event-key-match' ,
639
+ ) ;
640
+ } ) ;
641
+ keySessionContextPromise . catch ( ( error ) => this . handleError ( error ) ) ;
642
+ break ;
643
+ }
644
+ }
645
+
646
+ if ( ! keySessionContextPromise ) {
647
+ this . handleError (
648
+ new Error (
649
+ `Key ID ${ keyIdHex } not encountered in playlist. Key-system sessions ${ mediaKeySessions . length } .` ,
650
+ ) ,
651
+ ) ;
652
+ }
653
+ } ) ;
654
+ } ;
655
+
555
656
private onWaitingForKey = ( event : Event ) => {
556
657
this . log ( `"${ event . type } " event` ) ;
557
658
} ;
@@ -1130,13 +1231,15 @@ class EMEController extends Logger implements ComponentAPI {
1130
1231
// keep reference of media
1131
1232
this . media = media ;
1132
1233
1234
+ addEventListener ( media , 'encrypted' , this . onMediaEncrypted ) ;
1133
1235
addEventListener ( media , 'waitingforkey' , this . onWaitingForKey ) ;
1134
1236
}
1135
1237
1136
1238
private onMediaDetached ( ) {
1137
1239
const media = this . media ;
1138
1240
1139
1241
if ( media ) {
1242
+ removeEventListener ( media , 'encrypted' , this . onMediaEncrypted ) ;
1140
1243
removeEventListener ( media , 'waitingforkey' , this . onWaitingForKey ) ;
1141
1244
this . media = null ;
1142
1245
this . mediaKeys = null ;
0 commit comments