/**
* @license
* Copyright 2016 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
goog.provide('shaka.util.FairPlayUtils');
goog.require('goog.Uri');
goog.require('goog.asserts');
/**
* @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 {!Uint8Array} */
let contentIdArray;
if (typeof contentId == 'string') {
contentIdArray = new Uint8Array(
shaka.util.StringUtils.toUTF16(contentId, /* littleEndian= */ true));
} else {
contentIdArray = new Uint8Array(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 {!Uint8Array} array */
const append = (array) => {
rebuiltInitData.set(array, offset);
offset += array.byteLength;
};
/** @param {!Uint8Array} array */
const appendWithLength = (array) => {
const view = new DataView(rebuiltInitData.buffer);
const value = array.byteLength;
view.setUint32(offset, value, /* littleEndian= */ true);
offset += 4;
append(array);
};
appendWithLength(new Uint8Array(utf16));
appendWithLength(contentIdArray);
appendWithLength(new Uint8Array(cert));
goog.asserts.assert(
offset == rebuiltInitData.length, 'Inconsistent init data length');
return rebuiltInitData;
}
};