hls.js 1.5.14-0.canary.10567 → 1.5.14-0.canary.10569
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.d.mts +1 -0
- package/dist/hls.d.ts +1 -0
- package/dist/hls.js +92 -126
- package/dist/hls.js.d.ts +1 -0
- package/dist/hls.js.map +1 -1
- package/dist/hls.light.js +35 -95
- 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 +37 -95
- 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 +90 -120
- 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/eme-controller.ts +40 -16
- package/src/demux/audio/aacdemuxer.ts +2 -1
- package/src/demux/audio/adts.ts +31 -102
- package/src/demux/tsdemuxer.ts +1 -7
- package/src/loader/level-key.ts +8 -36
- package/src/remux/mp4-generator.ts +37 -34
- package/src/utils/mediakeys-helper.ts +33 -0
package/package.json
CHANGED
@@ -12,10 +12,10 @@ import {
|
|
12
12
|
keySystemDomainToKeySystemFormat as keySystemToKeySystemFormat,
|
13
13
|
KeySystemFormats,
|
14
14
|
keySystemFormatToKeySystemDomain,
|
15
|
-
KeySystemIds,
|
16
15
|
keySystemIdToKeySystemDomain,
|
17
16
|
KeySystems,
|
18
17
|
requestMediaKeySystemAccess,
|
18
|
+
parsePlayReadyWRM,
|
19
19
|
} from '../utils/mediakeys-helper';
|
20
20
|
import { strToUtf8array } from '../utils/utf8-utils';
|
21
21
|
import { base64Decode } from '../utils/numeric-encoding-utils';
|
@@ -25,8 +25,8 @@ import {
|
|
25
25
|
bin2str,
|
26
26
|
parseMultiPssh,
|
27
27
|
parseSinf,
|
28
|
-
PsshData,
|
29
|
-
PsshInvalidResult,
|
28
|
+
type PsshData,
|
29
|
+
type PsshInvalidResult,
|
30
30
|
} from '../utils/mp4-tools';
|
31
31
|
import { EventEmitter } from 'eventemitter3';
|
32
32
|
import type Hls from '../hls';
|
@@ -127,7 +127,7 @@ class EMEController extends Logger implements ComponentAPI {
|
|
127
127
|
this.hls.off(Events.MANIFEST_LOADED, this.onManifestLoaded, this);
|
128
128
|
}
|
129
129
|
|
130
|
-
private getLicenseServerUrl(keySystem: KeySystems): string |
|
130
|
+
private getLicenseServerUrl(keySystem: KeySystems): string | undefined {
|
131
131
|
const { drmSystems, widevineLicenseUrl } = this.config;
|
132
132
|
const keySystemConfiguration = drmSystems[keySystem];
|
133
133
|
|
@@ -139,10 +139,16 @@ class EMEController extends Logger implements ComponentAPI {
|
|
139
139
|
if (keySystem === KeySystems.WIDEVINE && widevineLicenseUrl) {
|
140
140
|
return widevineLicenseUrl;
|
141
141
|
}
|
142
|
+
}
|
142
143
|
|
143
|
-
|
144
|
-
|
145
|
-
)
|
144
|
+
private getLicenseServerUrlOrThrow(keySystem: KeySystems): string | never {
|
145
|
+
const url = this.getLicenseServerUrl(keySystem);
|
146
|
+
if (url === undefined) {
|
147
|
+
throw new Error(
|
148
|
+
`no license server URL configured for key-system "${keySystem}"`,
|
149
|
+
);
|
150
|
+
}
|
151
|
+
return url;
|
146
152
|
}
|
147
153
|
|
148
154
|
private getServerCertificateUrl(keySystem: KeySystems): string | void {
|
@@ -524,12 +530,12 @@ class EMEController extends Logger implements ComponentAPI {
|
|
524
530
|
return;
|
525
531
|
}
|
526
532
|
|
527
|
-
let keyId: Uint8Array | undefined;
|
533
|
+
let keyId: Uint8Array | null | undefined;
|
528
534
|
let keySystemDomain: KeySystems | undefined;
|
529
535
|
|
530
536
|
if (
|
531
537
|
initDataType === 'sinf' &&
|
532
|
-
this.
|
538
|
+
this.getLicenseServerUrl(KeySystems.FAIRPLAY)
|
533
539
|
) {
|
534
540
|
// Match sinf keyId to playlist skd://keyId=
|
535
541
|
const json = bin2str(new Uint8Array(initData));
|
@@ -547,12 +553,25 @@ class EMEController extends Logger implements ComponentAPI {
|
|
547
553
|
this.warn(`${logMessage} Failed to parse sinf: ${error}`);
|
548
554
|
return;
|
549
555
|
}
|
550
|
-
} else {
|
556
|
+
} else if (this.getLicenseServerUrl(KeySystems.WIDEVINE)) {
|
551
557
|
// Support Widevine clear-lead key-session creation (otherwise depend on playlist keys)
|
552
558
|
const psshResults = parseMultiPssh(initData);
|
553
|
-
|
554
|
-
|
555
|
-
|
559
|
+
|
560
|
+
// TODO: If using keySystemAccessPromises we might want to wait until one is resolved
|
561
|
+
let keySystems = Object.keys(
|
562
|
+
this.keySystemAccessPromises,
|
563
|
+
) as KeySystems[];
|
564
|
+
if (!keySystems.length) {
|
565
|
+
keySystems = getKeySystemsForConfig(this.config);
|
566
|
+
}
|
567
|
+
|
568
|
+
const psshInfo = psshResults.filter((pssh): pssh is PsshData => {
|
569
|
+
const keySystem = pssh.systemId
|
570
|
+
? keySystemIdToKeySystemDomain(pssh.systemId)
|
571
|
+
: null;
|
572
|
+
return keySystem ? keySystems.indexOf(keySystem) > -1 : false;
|
573
|
+
})[0];
|
574
|
+
|
556
575
|
if (!psshInfo) {
|
557
576
|
if (
|
558
577
|
psshResults.length === 0 ||
|
@@ -568,10 +587,15 @@ class EMEController extends Logger implements ComponentAPI {
|
|
568
587
|
}
|
569
588
|
return;
|
570
589
|
}
|
590
|
+
|
571
591
|
keySystemDomain = keySystemIdToKeySystemDomain(psshInfo.systemId);
|
572
592
|
if (psshInfo.version === 0 && psshInfo.data) {
|
573
|
-
|
574
|
-
|
593
|
+
if (keySystemDomain === KeySystems.WIDEVINE) {
|
594
|
+
const offset = psshInfo.data.length - 22;
|
595
|
+
keyId = psshInfo.data.subarray(offset, offset + 16);
|
596
|
+
} else if (keySystemDomain === KeySystems.PLAYREADY) {
|
597
|
+
keyId = parsePlayReadyWRM(psshInfo.data);
|
598
|
+
}
|
575
599
|
}
|
576
600
|
}
|
577
601
|
|
@@ -1098,7 +1122,7 @@ class EMEController extends Logger implements ComponentAPI {
|
|
1098
1122
|
): Promise<ArrayBuffer> {
|
1099
1123
|
const keyLoadPolicy = this.config.keyLoadPolicy.default;
|
1100
1124
|
return new Promise((resolve, reject) => {
|
1101
|
-
const url = this.
|
1125
|
+
const url = this.getLicenseServerUrlOrThrow(keySessionContext.keySystem);
|
1102
1126
|
this.log(`Sending license request to URL: ${url}`);
|
1103
1127
|
const xhr = new XMLHttpRequest();
|
1104
1128
|
xhr.responseType = 'arraybuffer';
|
@@ -7,6 +7,7 @@ import * as MpegAudio from './mpegaudio';
|
|
7
7
|
import { getId3Data } from '@svta/common-media-library/id3/getId3Data';
|
8
8
|
import type { HlsEventEmitter } from '../../events';
|
9
9
|
import type { HlsConfig } from '../../config';
|
10
|
+
import type { DemuxedAudioTrack } from '../../types/demuxer';
|
10
11
|
import type { ILogger } from '../../utils/logger';
|
11
12
|
|
12
13
|
class AACDemuxer extends BaseAudioDemuxer {
|
@@ -71,7 +72,7 @@ class AACDemuxer extends BaseAudioDemuxer {
|
|
71
72
|
return ADTS.canParse(data, offset);
|
72
73
|
}
|
73
74
|
|
74
|
-
appendFrame(track, data, offset) {
|
75
|
+
appendFrame(track: DemuxedAudioTrack, data: Uint8Array, offset: number) {
|
75
76
|
ADTS.initTrackConfig(
|
76
77
|
track,
|
77
78
|
this.observer,
|
package/src/demux/audio/adts.ts
CHANGED
@@ -13,12 +13,12 @@ import type {
|
|
13
13
|
} from '../../types/demuxer';
|
14
14
|
|
15
15
|
type AudioConfig = {
|
16
|
-
config: number
|
16
|
+
config: [number, number];
|
17
17
|
samplerate: number;
|
18
18
|
channelCount: number;
|
19
19
|
codec: string;
|
20
20
|
parsedCodec: string;
|
21
|
-
manifestCodec: string;
|
21
|
+
manifestCodec: string | undefined;
|
22
22
|
};
|
23
23
|
|
24
24
|
type FrameHeader = {
|
@@ -30,24 +30,15 @@ export function getAudioConfig(
|
|
30
30
|
observer: HlsEventEmitter,
|
31
31
|
data: Uint8Array,
|
32
32
|
offset: number,
|
33
|
-
|
33
|
+
manifestCodec: string | undefined,
|
34
34
|
): AudioConfig | void {
|
35
|
-
let adtsObjectType: number;
|
36
|
-
let originalAdtsObjectType: number;
|
37
|
-
let adtsExtensionSamplingIndex: number;
|
38
|
-
let adtsChannelConfig: number;
|
39
|
-
let config: number[];
|
40
|
-
const userAgent = navigator.userAgent.toLowerCase();
|
41
|
-
const manifestCodec = audioCodec;
|
42
35
|
const adtsSamplingRates = [
|
43
36
|
96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025,
|
44
37
|
8000, 7350,
|
45
38
|
];
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
const adtsSamplingIndex = (data[offset + 2] & 0x3c) >>> 2;
|
50
|
-
if (adtsSamplingIndex > adtsSamplingRates.length - 1) {
|
39
|
+
const byte2 = data[offset + 2];
|
40
|
+
const adtsSamplingIndex = (byte2 >> 2) & 0xf;
|
41
|
+
if (adtsSamplingIndex > 12) {
|
51
42
|
const error = new Error(`invalid ADTS sampling index:${adtsSamplingIndex}`);
|
52
43
|
observer.emit(Events.ERROR, Events.ERROR, {
|
53
44
|
type: ErrorTypes.MEDIA_ERROR,
|
@@ -58,66 +49,12 @@ export function getAudioConfig(
|
|
58
49
|
});
|
59
50
|
return;
|
60
51
|
}
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
`manifest codec:${audioCodec}, ADTS type:${adtsObjectType}, samplingIndex:${adtsSamplingIndex}`,
|
66
|
-
);
|
67
|
-
// Firefox and Pale Moon: freq less than 24kHz = AAC SBR (HE-AAC)
|
68
|
-
if (/firefox|palemoon/i.test(userAgent)) {
|
69
|
-
if (adtsSamplingIndex >= 6) {
|
70
|
-
adtsObjectType = 5;
|
71
|
-
config = new Array(4);
|
72
|
-
// HE-AAC uses SBR (Spectral Band Replication) , high frequencies are constructed from low frequencies
|
73
|
-
// there is a factor 2 between frame sample rate and output sample rate
|
74
|
-
// multiply frequency by 2 (see table below, equivalent to substract 3)
|
75
|
-
adtsExtensionSamplingIndex = adtsSamplingIndex - 3;
|
76
|
-
} else {
|
77
|
-
adtsObjectType = 2;
|
78
|
-
config = new Array(2);
|
79
|
-
adtsExtensionSamplingIndex = adtsSamplingIndex;
|
80
|
-
}
|
81
|
-
// Android : always use AAC
|
82
|
-
} else if (userAgent.indexOf('android') !== -1) {
|
83
|
-
adtsObjectType = 2;
|
84
|
-
config = new Array(2);
|
85
|
-
adtsExtensionSamplingIndex = adtsSamplingIndex;
|
86
|
-
} else {
|
87
|
-
/* for other browsers (Chrome/Vivaldi/Opera ...)
|
88
|
-
always force audio type to be HE-AAC SBR, as some browsers do not support audio codec switch properly (like Chrome ...)
|
89
|
-
*/
|
90
|
-
adtsObjectType = 5;
|
91
|
-
config = new Array(4);
|
92
|
-
// if (manifest codec is HE-AAC or HE-AACv2) OR (manifest codec not specified AND frequency less than 24kHz)
|
93
|
-
if (
|
94
|
-
(audioCodec &&
|
95
|
-
(audioCodec.indexOf('mp4a.40.29') !== -1 ||
|
96
|
-
audioCodec.indexOf('mp4a.40.5') !== -1)) ||
|
97
|
-
(!audioCodec && adtsSamplingIndex >= 6)
|
98
|
-
) {
|
99
|
-
// HE-AAC uses SBR (Spectral Band Replication) , high frequencies are constructed from low frequencies
|
100
|
-
// there is a factor 2 between frame sample rate and output sample rate
|
101
|
-
// multiply frequency by 2 (see table below, equivalent to substract 3)
|
102
|
-
adtsExtensionSamplingIndex = adtsSamplingIndex - 3;
|
103
|
-
} else {
|
104
|
-
// if (manifest codec is AAC) AND (frequency less than 24kHz AND nb channel is 1) OR (manifest codec not specified and mono audio)
|
105
|
-
// Chrome fails to play back with low frequency AAC LC mono when initialized with HE-AAC. This is not a problem with stereo.
|
106
|
-
if (
|
107
|
-
(audioCodec &&
|
108
|
-
audioCodec.indexOf('mp4a.40.2') !== -1 &&
|
109
|
-
((adtsSamplingIndex >= 6 && adtsChannelConfig === 1) ||
|
110
|
-
/vivaldi/i.test(userAgent))) ||
|
111
|
-
(!audioCodec && adtsChannelConfig === 1)
|
112
|
-
) {
|
113
|
-
adtsObjectType = 2;
|
114
|
-
config = new Array(2);
|
115
|
-
}
|
116
|
-
adtsExtensionSamplingIndex = adtsSamplingIndex;
|
117
|
-
}
|
118
|
-
}
|
52
|
+
// MPEG-4 Audio Object Type (profile_ObjectType+1)
|
53
|
+
const adtsObjectType = ((byte2 >> 6) & 0x3) + 1;
|
54
|
+
const channelCount = ((data[offset + 3] >> 6) & 0x3) | ((byte2 & 1) << 2);
|
55
|
+
const codec = 'mp4a.40.' + adtsObjectType;
|
119
56
|
/* refer to http://wiki.multimedia.cx/index.php?title=MPEG-4_Audio#Audio_Specific_Config
|
120
|
-
ISO 14496-3
|
57
|
+
ISO/IEC 14496-3 - Table 1.13 — Syntax of AudioSpecificConfig()
|
121
58
|
Audio Profile / Audio Object Type
|
122
59
|
0: Null
|
123
60
|
1: AAC Main
|
@@ -150,27 +87,27 @@ export function getAudioConfig(
|
|
150
87
|
2: 2 channels: front-left, front-right
|
151
88
|
*/
|
152
89
|
// audioObjectType = profile => profile, the MPEG-4 Audio Object Type minus 1
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
// adtsExtensionSamplingIndex
|
161
|
-
config[1] |= (adtsExtensionSamplingIndex & 0x0e) >> 1;
|
162
|
-
config[2] = (adtsExtensionSamplingIndex & 0x01) << 7;
|
163
|
-
// adtsObjectType (force to 2, chrome is checking that object type is less than 5 ???
|
164
|
-
// https://chromium.googlesource.com/chromium/src.git/+/master/media/formats/mp4/aac.cc
|
165
|
-
config[2] |= 2 << 2;
|
166
|
-
config[3] = 0;
|
90
|
+
const samplerate = adtsSamplingRates[adtsSamplingIndex];
|
91
|
+
let aacSampleIndex = adtsSamplingIndex;
|
92
|
+
if (adtsObjectType === 5 || adtsObjectType === 29) {
|
93
|
+
// HE-AAC uses SBR (Spectral Band Replication) , high frequencies are constructed from low frequencies
|
94
|
+
// there is a factor 2 between frame sample rate and output sample rate
|
95
|
+
// multiply frequency by 2 (see table above, equivalent to substract 3)
|
96
|
+
aacSampleIndex -= 3;
|
167
97
|
}
|
98
|
+
const config: [number, number] = [
|
99
|
+
(adtsObjectType << 3) | ((aacSampleIndex & 0x0e) >> 1),
|
100
|
+
((aacSampleIndex & 0x01) << 7) | (channelCount << 3),
|
101
|
+
];
|
102
|
+
logger.log(
|
103
|
+
`manifest codec:${manifestCodec}, parsed codec:${codec}, channels:${channelCount}, rate:${samplerate} (ADTS object type:${adtsObjectType} sampling index:${adtsSamplingIndex})`,
|
104
|
+
);
|
168
105
|
return {
|
169
106
|
config,
|
170
|
-
samplerate
|
171
|
-
channelCount
|
172
|
-
codec
|
173
|
-
parsedCodec:
|
107
|
+
samplerate,
|
108
|
+
channelCount,
|
109
|
+
codec,
|
110
|
+
parsedCodec: codec,
|
174
111
|
manifestCodec,
|
175
112
|
};
|
176
113
|
}
|
@@ -236,22 +173,14 @@ export function initTrackConfig(
|
|
236
173
|
observer: HlsEventEmitter,
|
237
174
|
data: Uint8Array,
|
238
175
|
offset: number,
|
239
|
-
audioCodec: string,
|
176
|
+
audioCodec: string | undefined,
|
240
177
|
) {
|
241
178
|
if (!track.samplerate) {
|
242
179
|
const config = getAudioConfig(observer, data, offset, audioCodec);
|
243
180
|
if (!config) {
|
244
181
|
return;
|
245
182
|
}
|
246
|
-
track
|
247
|
-
track.samplerate = config.samplerate;
|
248
|
-
track.channelCount = config.channelCount;
|
249
|
-
track.codec = config.codec;
|
250
|
-
track.manifestCodec = config.manifestCodec;
|
251
|
-
track.parsedCodec = config.parsedCodec;
|
252
|
-
logger.log(
|
253
|
-
`parsed codec:${track.parsedCodec}, codec:${track.codec}, rate:${config.samplerate}, channels:${config.channelCount}`,
|
254
|
-
);
|
183
|
+
Object.assign(track, config);
|
255
184
|
}
|
256
185
|
}
|
257
186
|
|
package/src/demux/tsdemuxer.ts
CHANGED
@@ -648,13 +648,7 @@ class TSDemuxer implements Demuxer {
|
|
648
648
|
}
|
649
649
|
}
|
650
650
|
|
651
|
-
ADTS.initTrackConfig(
|
652
|
-
track,
|
653
|
-
this.observer,
|
654
|
-
data,
|
655
|
-
offset,
|
656
|
-
this.audioCodec as string,
|
657
|
-
);
|
651
|
+
ADTS.initTrackConfig(track, this.observer, data, offset, this.audioCodec);
|
658
652
|
|
659
653
|
let pts: number;
|
660
654
|
if (pes.pts !== undefined) {
|
package/src/loader/level-key.ts
CHANGED
@@ -1,12 +1,8 @@
|
|
1
|
-
import {
|
2
|
-
changeEndianness,
|
3
|
-
convertDataUriToArrayBytes,
|
4
|
-
} from '../utils/keysystem-util';
|
1
|
+
import { convertDataUriToArrayBytes } from '../utils/keysystem-util';
|
5
2
|
import { isFullSegmentEncryption } from '../utils/encryption-methods-util';
|
6
|
-
import { KeySystemFormats } from '../utils/mediakeys-helper';
|
3
|
+
import { KeySystemFormats, parsePlayReadyWRM } from '../utils/mediakeys-helper';
|
7
4
|
import { mp4pssh } from '../utils/mp4-tools';
|
8
5
|
import { logger } from '../utils/logger';
|
9
|
-
import { base64Decode } from '../utils/numeric-encoding-utils';
|
10
6
|
|
11
7
|
let keyUriToKeyIdMap: { [uri: string]: Uint8Array } = {};
|
12
8
|
|
@@ -122,6 +118,8 @@ export class LevelKey implements DecryptData {
|
|
122
118
|
if (keyBytes) {
|
123
119
|
switch (this.keyFormat) {
|
124
120
|
case KeySystemFormats.WIDEVINE:
|
121
|
+
// Setting `pssh` on this LevelKey/DecryptData allows HLS.js to generate a session using
|
122
|
+
// the playlist-key before the "encrypted" event. (Comment out to only use "encrypted" path.)
|
125
123
|
this.pssh = keyBytes;
|
126
124
|
// In case of widevine keyID is embedded in PSSH box. Read Key ID.
|
127
125
|
if (keyBytes.length >= 22) {
|
@@ -137,38 +135,12 @@ export class LevelKey implements DecryptData {
|
|
137
135
|
0x5b, 0xe0, 0x88, 0x5f, 0x95,
|
138
136
|
]);
|
139
137
|
|
138
|
+
// Setting `pssh` on this LevelKey/DecryptData allows HLS.js to generate a session using
|
139
|
+
// the playlist-key before the "encrypted" event. (Comment out to only use "encrypted" path.)
|
140
140
|
this.pssh = mp4pssh(PlayReadyKeySystemUUID, null, keyBytes);
|
141
141
|
|
142
|
-
|
143
|
-
|
144
|
-
keyBytes.byteOffset,
|
145
|
-
keyBytes.byteLength / 2,
|
146
|
-
);
|
147
|
-
const keyByteStr = String.fromCharCode.apply(
|
148
|
-
null,
|
149
|
-
Array.from(keyBytesUtf16),
|
150
|
-
);
|
151
|
-
|
152
|
-
// Parse Playready WRMHeader XML
|
153
|
-
const xmlKeyBytes = keyByteStr.substring(
|
154
|
-
keyByteStr.indexOf('<'),
|
155
|
-
keyByteStr.length,
|
156
|
-
);
|
157
|
-
const parser = new DOMParser();
|
158
|
-
const xmlDoc = parser.parseFromString(xmlKeyBytes, 'text/xml');
|
159
|
-
const keyData = xmlDoc.getElementsByTagName('KID')[0];
|
160
|
-
if (keyData) {
|
161
|
-
const keyId = keyData.childNodes[0]
|
162
|
-
? keyData.childNodes[0].nodeValue
|
163
|
-
: keyData.getAttribute('VALUE');
|
164
|
-
if (keyId) {
|
165
|
-
const keyIdArray = base64Decode(keyId).subarray(0, 16);
|
166
|
-
// KID value in PRO is a base64-encoded little endian GUID interpretation of UUID
|
167
|
-
// KID value in ‘tenc’ is a big endian UUID GUID interpretation of UUID
|
168
|
-
changeEndianness(keyIdArray);
|
169
|
-
this.keyId = keyIdArray;
|
170
|
-
}
|
171
|
-
}
|
142
|
+
this.keyId = parsePlayReadyWRM(keyBytes);
|
143
|
+
|
172
144
|
break;
|
173
145
|
}
|
174
146
|
default: {
|
@@ -2,6 +2,7 @@
|
|
2
2
|
* Generate MP4 Box
|
3
3
|
*/
|
4
4
|
|
5
|
+
import type { DemuxedAudioTrack } from '../types/demuxer';
|
5
6
|
import { appendUint8Array } from '../utils/mp4-tools';
|
6
7
|
|
7
8
|
type HdlrTypes = {
|
@@ -739,43 +740,45 @@ class MP4 {
|
|
739
740
|
);
|
740
741
|
}
|
741
742
|
|
742
|
-
static esds(track) {
|
743
|
-
const
|
744
|
-
return new Uint8Array(
|
745
|
-
|
746
|
-
|
747
|
-
|
748
|
-
|
749
|
-
0x00, // flags
|
743
|
+
static esds(track: DemuxedAudioTrack) {
|
744
|
+
const config = track.config as [number, number];
|
745
|
+
return new Uint8Array([
|
746
|
+
0x00, // version 0
|
747
|
+
0x00,
|
748
|
+
0x00,
|
749
|
+
0x00, // flags
|
750
750
|
|
751
|
-
|
752
|
-
|
753
|
-
0x00,
|
754
|
-
0x01, // es_id
|
755
|
-
0x00, // stream_priority
|
751
|
+
0x03, // descriptor_type
|
752
|
+
0x19, // length
|
756
753
|
|
757
|
-
|
758
|
-
|
759
|
-
|
760
|
-
|
761
|
-
|
762
|
-
|
763
|
-
|
764
|
-
|
765
|
-
|
766
|
-
|
767
|
-
|
768
|
-
|
769
|
-
|
770
|
-
|
771
|
-
|
754
|
+
0x00,
|
755
|
+
0x01, // es_id
|
756
|
+
|
757
|
+
0x00, // stream_priority
|
758
|
+
|
759
|
+
0x04, // descriptor_type
|
760
|
+
0x11, // length
|
761
|
+
0x40, // codec : mpeg4_audio
|
762
|
+
0x15, // stream_type
|
763
|
+
0x00,
|
764
|
+
0x00,
|
765
|
+
0x00, // buffer_size
|
766
|
+
0x00,
|
767
|
+
0x00,
|
768
|
+
0x00,
|
769
|
+
0x00, // maxBitrate
|
770
|
+
0x00,
|
771
|
+
0x00,
|
772
|
+
0x00,
|
773
|
+
0x00, // avgBitrate
|
772
774
|
|
773
|
-
|
774
|
-
|
775
|
-
|
776
|
-
|
777
|
-
|
778
|
-
|
775
|
+
0x05, // descriptor_type
|
776
|
+
0x02, // length
|
777
|
+
...config,
|
778
|
+
0x06,
|
779
|
+
0x01,
|
780
|
+
0x02, // GASpecificConfig)); // length + audio config descriptor
|
781
|
+
]);
|
779
782
|
}
|
780
783
|
|
781
784
|
static audioStsd(track) {
|
@@ -1,5 +1,7 @@
|
|
1
1
|
import type { DRMSystemOptions, EMEControllerConfig } from '../config';
|
2
2
|
import { optionalSelf } from './global';
|
3
|
+
import { changeEndianness } from './keysystem-util';
|
4
|
+
import { base64Decode } from './numeric-encoding-utils';
|
3
5
|
|
4
6
|
/**
|
5
7
|
* @see https://developer.mozilla.org/en-US/docs/Web/API/Navigator/requestMediaKeySystemAccess
|
@@ -163,3 +165,34 @@ function createMediaKeySystemConfigurations(
|
|
163
165
|
|
164
166
|
return [baseConfig];
|
165
167
|
}
|
168
|
+
|
169
|
+
export function parsePlayReadyWRM(keyBytes: Uint8Array): Uint8Array | null {
|
170
|
+
const keyBytesUtf16 = new Uint16Array(
|
171
|
+
keyBytes.buffer,
|
172
|
+
keyBytes.byteOffset,
|
173
|
+
keyBytes.byteLength / 2,
|
174
|
+
);
|
175
|
+
const keyByteStr = String.fromCharCode.apply(null, Array.from(keyBytesUtf16));
|
176
|
+
|
177
|
+
// Parse Playready WRMHeader XML
|
178
|
+
const xmlKeyBytes = keyByteStr.substring(
|
179
|
+
keyByteStr.indexOf('<'),
|
180
|
+
keyByteStr.length,
|
181
|
+
);
|
182
|
+
const parser = new DOMParser();
|
183
|
+
const xmlDoc = parser.parseFromString(xmlKeyBytes, 'text/xml');
|
184
|
+
const keyData = xmlDoc.getElementsByTagName('KID')[0];
|
185
|
+
if (keyData) {
|
186
|
+
const keyId = keyData.childNodes[0]
|
187
|
+
? keyData.childNodes[0].nodeValue
|
188
|
+
: keyData.getAttribute('VALUE');
|
189
|
+
if (keyId) {
|
190
|
+
const keyIdArray = base64Decode(keyId).subarray(0, 16);
|
191
|
+
// KID value in PRO is a base64-encoded little endian GUID interpretation of UUID
|
192
|
+
// KID value in ‘tenc’ is a big endian UUID GUID interpretation of UUID
|
193
|
+
changeEndianness(keyIdArray);
|
194
|
+
return keyIdArray;
|
195
|
+
}
|
196
|
+
}
|
197
|
+
return null;
|
198
|
+
}
|