mirror of
https://github.com/shaka-project/shaka-player.git
synced 2026-06-15 16:06:41 +03:00
6d76a135e5
Add support for HLS com.apple.streamingkeydelivery through MSE/EME implementation. Close #3346 ## Tests Tested on: - Mac 11.6 Safari 15.2 - iOS 15.2 Safari 15.2 - Mac 11.6 Chrome 96 (for potential regressions on Widevine keySystem) | Mode | DRM API | TS | CMAF (mono-key and multi-keys) |---|---|---|---| | file | EME | ✅ | ✅ | | file | Legacy-prefixed | ✅ | ✅ | | media-source | EME | **mux-js**: `encrypted` never fired<br />**real MSE**: `encrypted` event received, but with incorrect `sinf` initData (*1) | ✅ | | media-source | Legacy-prefixed | **mux-js**: `webkitneedkey` never fired<br/>**real MSE**: TBD | 🔴 fails to append media segment to SourceBuffer (init segment ok) `(video:4) – "failed fetch and append: code=3015"` | ## Support table | Mode | DRM API | TS | CMAF (mono-key and multi-keys) |---|---|---|---| | file | EME | ✅ | ✅ | | file | Legacy-prefixed | ✅ | ✅ | | media-source | EME | 🚫 `4040: HLS_MSE_ENCRYPTED_MP2T_NOT_SUPPORTED` | ✅ | | media-source | Legacy-prefixed | 🚫 `4041: HLS_MSE_ENCRYPTED_LEGACY_APPLE_MEDIA_KEYS_NOT_SUPPORTED` |🚫 `4041: HLS_MSE_ENCRYPTED_LEGACY_APPLE_MEDIA_KEYS_NOT_SUPPORTED` | ⚠️ Use EME APIs with multi-keys CMAF makes the video stalling with the audio continuing alone after a short time (~3 minutes in the stream, could be shorter, could be longer). Didn't find an explanation to that yet. I've observed the same behaviour with hls.js code so I don't think this is a player issue.
85 lines
2.8 KiB
Markdown
85 lines
2.8 KiB
Markdown
# FairPlay Support
|
|
|
|
We support FairPlay with EME on compatible environments or native `src=`.
|
|
Adding FairPlay support involves a bit more work than other key systems.
|
|
|
|
## Server certificate
|
|
|
|
All FairPlay content requires setting a server certificate. You can either
|
|
provide it directly or set a serverCertificateUri for Shaka to fetch it for
|
|
you.
|
|
|
|
```js
|
|
const req = await fetch('https://example.com/cert.der');
|
|
const cert = await req.arrayBuffer();
|
|
|
|
player.configure('drm.advanced.com\\.apple\\.fps\\.serverCertificate',
|
|
new Uint8Array(cert));
|
|
```
|
|
|
|
```js
|
|
player.configure('drm.advanced.com\\.apple\\.fps\\.serverCertificateUri',
|
|
'https://example.com/cert.der');
|
|
```
|
|
|
|
## Content ID
|
|
|
|
Note: This only applies when legacy Apple Media Keys is used.
|
|
|
|
Some FairPlay content use custom signaling for the content ID. The content ID
|
|
is used by the browser to generate the license request. If you don't use the
|
|
default content ID derivation, you need to specify a custom init data transform:
|
|
|
|
```js
|
|
player.configure('drm.initDataTransform', (initData, initDataType) => {
|
|
if (initDataType != 'skd')
|
|
return initData;
|
|
|
|
// 'initData' is a buffer containing an 'skd://' URL as a UTF-8 string.
|
|
const skdUri = shaka.util.StringUtils.fromBytesAutoDetect(initData);
|
|
const contentId = getMyContentId(skdUri);
|
|
const cert = player.drmInfo().serverCertificate;
|
|
return shaka.util.FairPlayUtils.initDataTransform(initData, contentId, cert);
|
|
});
|
|
```
|
|
|
|
## License wrapping
|
|
|
|
Some FairPlay servers need to accept the license request in a different format
|
|
or give the response in a different format. For more info, see the general
|
|
{@tutorial license-wrapping} tutorial:
|
|
|
|
```js
|
|
player.getNetworkingEngine().registerRequestFilter((type, request) => {
|
|
if (type != shaka.net.NetworkingEngine.RequestType.LICENSE) {
|
|
return;
|
|
}
|
|
|
|
const originalPayload = new Uint8Array(request.body);
|
|
const base64Payload =
|
|
shaka.util.Uint8ArrayUtils.toStandardBase64(originalPayload);
|
|
const params = 'spc=' + base64Payload;
|
|
request.headers['Content-Type'] = 'application/x-www-form-urlencoded';
|
|
request.body = shaka.util.StringUtils.toUTF8(encodeURIComponent(params));
|
|
});
|
|
|
|
player.getNetworkingEngine().registerResponseFilter((type, response) => {
|
|
if (type != shaka.net.NetworkingEngine.RequestType.LICENSE) {
|
|
return;
|
|
}
|
|
|
|
let responseText = shaka.util.StringUtils.fromUTF8(response.data);
|
|
// Trim whitespace.
|
|
responseText = responseText.trim();
|
|
|
|
// Look for <ckc> wrapper and remove it.
|
|
if (responseText.substr(0, 5) === '<ckc>' &&
|
|
responseText.substr(-6) === '</ckc>') {
|
|
responseText = responseText.slice(5, -6);
|
|
}
|
|
|
|
// Decode the base64-encoded data into the format the browser expects.
|
|
response.data = shaka.util.Uint8ArrayUtils.fromBase64(responseText).buffer;
|
|
});
|
|
```
|