mirror of
https://github.com/shaka-project/shaka-player.git
synced 2026-06-26 17:46:26 +03:00
7ae6fc7d93
The 'webkitkeyneeded' and 'encrypted' events send similar data, but they were incompatible with each other and our transform handling. This makes our polyfill produce the same format as the browser for cases where the browser may only fire the old event. This also makes our utilities work with the new format. The 'webkitkeyneeded' event was a length-prefixed UTF-16 string while the 'encrypted' event was just a UTF-8 string. This also makes a breaking change in the transform callback to pass the init data type. This shouldn't break anyone that only uses the first argument; the second argument was mainly added so we could have the default transform work without knowing anything. This change could also break people who use custom transform functions. The init data format is changing, which could break people who read it directly. If they follow the tutorial and use our utilities, it shouldn't break. This also updates the tutorial to match the new format and be more clear about the format. Fixes #2214 Change-Id: I006382028e828e31e20e085114fd7fd85c0e1eaa
107 lines
3.2 KiB
JavaScript
107 lines
3.2 KiB
JavaScript
/** @license
|
|
* Copyright 2016 Google LLC
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
goog.provide('shaka.util.FairPlayUtils');
|
|
|
|
goog.require('goog.Uri');
|
|
goog.require('goog.asserts');
|
|
goog.require('shaka.util.BufferUtils');
|
|
|
|
|
|
/**
|
|
* @summary A set of FairPlay utility functions.
|
|
* @exportInterface
|
|
*/
|
|
shaka.util.FairPlayUtils = class {
|
|
/**
|
|
* Using the default method, extract a content ID from the init data. This is
|
|
* based on the FairPlay example documentation.
|
|
*
|
|
* @param {!BufferSource} initData
|
|
* @return {string}
|
|
* @export
|
|
*/
|
|
static defaultGetContentId(initData) {
|
|
const uriString = shaka.util.StringUtils.fromBytesAutoDetect(initData);
|
|
|
|
// The domain of that URI is the content ID according to Apple's FPS
|
|
// sample.
|
|
const uri = new goog.Uri(uriString);
|
|
return uri.getDomain();
|
|
}
|
|
|
|
/**
|
|
* Transforms the init data buffer using the given data. The format is:
|
|
*
|
|
* <pre>
|
|
* [4 bytes] initDataSize
|
|
* [initDataSize bytes] initData
|
|
* [4 bytes] contentIdSize
|
|
* [contentIdSize bytes] contentId
|
|
* [4 bytes] certSize
|
|
* [certSize bytes] cert
|
|
* </pre>
|
|
*
|
|
* @param {!BufferSource} initData
|
|
* @param {!BufferSource|string} contentId
|
|
* @param {?BufferSource} cert The server certificate; this will throw if not
|
|
* provided.
|
|
* @return {!Uint8Array}
|
|
* @export
|
|
*/
|
|
static initDataTransform(initData, contentId, cert) {
|
|
if (!cert || !cert.byteLength) {
|
|
throw new shaka.util.Error(
|
|
shaka.util.Error.Severity.CRITICAL,
|
|
shaka.util.Error.Category.DRM,
|
|
shaka.util.Error.Code.SERVER_CERTIFICATE_REQUIRED);
|
|
}
|
|
|
|
// From that, we build a new init data to use in the session. This is
|
|
// composed of several parts. First, the init data as a UTF-16 sdk:// URL.
|
|
// Second, a 4-byte LE length followed by the content ID in UTF-16-LE.
|
|
// Third, a 4-byte LE length followed by the certificate.
|
|
/** @type {BufferSource} */
|
|
let contentIdArray;
|
|
if (typeof contentId == 'string') {
|
|
contentIdArray =
|
|
shaka.util.StringUtils.toUTF16(contentId, /* littleEndian= */ true);
|
|
} else {
|
|
contentIdArray = contentId;
|
|
}
|
|
|
|
// The init data we get is a UTF-8 string; convert that to a UTF-16 string.
|
|
const sdkUri = shaka.util.StringUtils.fromBytesAutoDetect(initData);
|
|
const utf16 =
|
|
shaka.util.StringUtils.toUTF16(sdkUri, /* littleEndian= */ true);
|
|
|
|
const rebuiltInitData = new Uint8Array(
|
|
12 + utf16.byteLength + contentIdArray.byteLength + cert.byteLength);
|
|
|
|
let offset = 0;
|
|
/** @param {BufferSource} array */
|
|
const append = (array) => {
|
|
rebuiltInitData.set(shaka.util.BufferUtils.toUint8(array), offset);
|
|
offset += array.byteLength;
|
|
};
|
|
/** @param {BufferSource} array */
|
|
const appendWithLength = (array) => {
|
|
const view = shaka.util.BufferUtils.toDataView(rebuiltInitData);
|
|
const value = array.byteLength;
|
|
view.setUint32(offset, value, /* littleEndian= */ true);
|
|
offset += 4;
|
|
append(array);
|
|
};
|
|
|
|
appendWithLength(utf16);
|
|
appendWithLength(contentIdArray);
|
|
appendWithLength(cert);
|
|
|
|
goog.asserts.assert(
|
|
offset == rebuiltInitData.length, 'Inconsistent init data length');
|
|
return rebuiltInitData;
|
|
}
|
|
};
|