Source: lib/util/fairplay_utils.js

  1. /*! @license
  2. * Shaka Player
  3. * Copyright 2016 Google LLC
  4. * SPDX-License-Identifier: Apache-2.0
  5. */
  6. goog.provide('shaka.util.FairPlayUtils');
  7. goog.require('goog.Uri');
  8. goog.require('goog.asserts');
  9. goog.require('shaka.util.BufferUtils');
  10. goog.require('shaka.util.Error');
  11. goog.require('shaka.util.StringUtils');
  12. /**
  13. * @summary A set of FairPlay utility functions.
  14. * @export
  15. */
  16. shaka.util.FairPlayUtils = class {
  17. /**
  18. * Using the default method, extract a content ID from the init data. This is
  19. * based on the FairPlay example documentation.
  20. *
  21. * @param {!BufferSource} initData
  22. * @return {string}
  23. * @export
  24. */
  25. static defaultGetContentId(initData) {
  26. const uriString = shaka.util.StringUtils.fromBytesAutoDetect(initData);
  27. // The domain of that URI is the content ID according to Apple's FPS
  28. // sample.
  29. const uri = new goog.Uri(uriString);
  30. return uri.getDomain();
  31. }
  32. /**
  33. * Transforms the init data buffer using the given data. The format is:
  34. *
  35. * <pre>
  36. * [4 bytes] initDataSize
  37. * [initDataSize bytes] initData
  38. * [4 bytes] contentIdSize
  39. * [contentIdSize bytes] contentId
  40. * [4 bytes] certSize
  41. * [certSize bytes] cert
  42. * </pre>
  43. *
  44. * @param {!BufferSource} initData
  45. * @param {!BufferSource|string} contentId
  46. * @param {?BufferSource} cert The server certificate; this will throw if not
  47. * provided.
  48. * @return {!Uint8Array}
  49. * @export
  50. */
  51. static initDataTransform(initData, contentId, cert) {
  52. if (!cert || !cert.byteLength) {
  53. throw new shaka.util.Error(
  54. shaka.util.Error.Severity.CRITICAL,
  55. shaka.util.Error.Category.DRM,
  56. shaka.util.Error.Code.SERVER_CERTIFICATE_REQUIRED);
  57. }
  58. // From that, we build a new init data to use in the session. This is
  59. // composed of several parts. First, the init data as a UTF-16 sdk:// URL.
  60. // Second, a 4-byte LE length followed by the content ID in UTF-16-LE.
  61. // Third, a 4-byte LE length followed by the certificate.
  62. /** @type {BufferSource} */
  63. let contentIdArray;
  64. if (typeof contentId == 'string') {
  65. contentIdArray =
  66. shaka.util.StringUtils.toUTF16(contentId, /* littleEndian= */ true);
  67. } else {
  68. contentIdArray = contentId;
  69. }
  70. // The init data we get is a UTF-8 string; convert that to a UTF-16 string.
  71. const sdkUri = shaka.util.StringUtils.fromBytesAutoDetect(initData);
  72. const utf16 =
  73. shaka.util.StringUtils.toUTF16(sdkUri, /* littleEndian= */ true);
  74. const rebuiltInitData = new Uint8Array(
  75. 12 + utf16.byteLength + contentIdArray.byteLength + cert.byteLength);
  76. let offset = 0;
  77. /** @param {BufferSource} array */
  78. const append = (array) => {
  79. rebuiltInitData.set(shaka.util.BufferUtils.toUint8(array), offset);
  80. offset += array.byteLength;
  81. };
  82. /** @param {BufferSource} array */
  83. const appendWithLength = (array) => {
  84. const view = shaka.util.BufferUtils.toDataView(rebuiltInitData);
  85. const value = array.byteLength;
  86. view.setUint32(offset, value, /* littleEndian= */ true);
  87. offset += 4;
  88. append(array);
  89. };
  90. appendWithLength(utf16);
  91. appendWithLength(contentIdArray);
  92. appendWithLength(cert);
  93. goog.asserts.assert(
  94. offset == rebuiltInitData.length, 'Inconsistent init data length');
  95. return rebuiltInitData;
  96. }
  97. };